Index: /trunk/src/VBox/Devices/PC/BIOS-new/ahci.c
===================================================================
--- /trunk/src/VBox/Devices/PC/BIOS-new/ahci.c	(revision 38898)
+++ /trunk/src/VBox/Devices/PC/BIOS-new/ahci.c	(revision 38899)
@@ -27,7 +27,8 @@
 #include "inlines.h"
 #include "pciutil.h"
+#include "vds.h"
 
 #define VBOX_AHCI_DEBUG         0
-#define VBOX_AHCI_INT13_DEBUG   0 
+#define VBOX_AHCI_INT13_DEBUG   0
 
 #if VBOX_AHCI_DEBUG
@@ -51,4 +52,8 @@
  */
 #define DMA_WORKAROUND      1
+
+/* Number of S/G table entries in EDDS. */
+#define NUM_EDDS_SG         16
+
 
 /**
@@ -69,4 +74,15 @@
 
 /**
+ * AHCI PRDT structure.
+ */
+typedef struct
+{
+    uint32_t    phys_addr;
+    uint32_t    something;
+    uint32_t    reserved;
+    uint32_t    len;
+} ahci_prdt;
+
+/**
  * AHCI controller data.
  */
@@ -83,7 +99,9 @@
      *  Must be aligned on 128 byte boundary.
      */
-    uint8_t         abCmd[0x90];
-    /** Alignment */
-    uint8_t         abAlignment2[0xF0];
+    uint8_t         abCmd[0x80];
+    /** Physical Region Descriptor Table (PRDT) array. In other
+     *  words, a scatter/gather descriptor list.
+     */
+    ahci_prdt       aPrdt[16];
     /** Memory for the received command FIS area as specified by chapter 4.2.1
      *  of the Intel AHCI spec. This area is normally 256 bytes big but to save memory
@@ -106,8 +124,11 @@
     uint8_t         cCdDrives;
     uint8_t         aCdIdMap[AHCI_MAX_STORAGE_DEVICES];
+    /** Number of harddisks detected before the AHCI driver started detection. */
+    uint8_t         cHardDisksOld;
     /** int13 handler to call if given device is not from AHCI. */
     uint16_t        pfnInt13Old;
-    /** Number of harddisks detected before the AHCI driver started detection. */
-    uint8_t         cHardDisksOld;
+    /** VDS EDDS DMA buffer descriptor structure. */
+    vds_edds        edds;
+    vds_sg          edds_more_sg[NUM_EDDS_SG - 1];
 } ahci_t;
 
@@ -340,9 +361,16 @@
     ahci->abCmd[13] = u8SectCountExp;
 
-    /* Prepare PRDT. */
-    write_dword(ahci_seg, &AhciData->abCmd[0x80], ahci_addr_to_phys(buf));
-    write_dword(ahci_seg, &AhciData->abCmd[0x8c], (uint32_t)(cbData - 1));
+    /* Lock memory needed for DMA. */
+    ahci->edds.num_avail = NUM_EDDS_SG;
+    vds_build_sg_list( &ahci->edds, buf, cbData );
+
+    /* Set up the PRDT. */
+    ahci->aPrdt[0].phys_addr = ahci->edds.u.sg[0].phys_addr;
+    ahci->aPrdt[0].len       = ahci->edds.u.sg[0].size - 1;
 
     ahci_port_cmd_sync(ahci_seg, u16IoBase, fWrite, 0, 5, cbData);
+
+    /* Unlock the buffer again. */
+    vds_free_sg_list( &ahci->edds );
 }
 
@@ -374,7 +402,7 @@
          */
         //@todo: merge memsets?
-        _fmemset(&ahci->aCmdHdr[0], 0, 0x20);
-        _fmemset(&ahci->abCmd[0], 0, 0x84);
-        _fmemset(&ahci->abFisRecv[0], 0, 0x60);
+        _fmemset(&ahci->aCmdHdr[0], 0, sizeof(ahci->aCmdHdr));
+        _fmemset(&ahci->abCmd[0], 0, sizeof(ahci->abCmd));
+        _fmemset(&ahci->abFisRecv[0], 0, sizeof(ahci->abFisRecv));
 
         VBOXAHCI_PORT_WRITE_REG(u16IoBase, u8Port, AHCI_REG_PORT_FB, 0);
Index: /trunk/src/VBox/Devices/PC/BIOS-new/makefile
===================================================================
--- /trunk/src/VBox/Devices/PC/BIOS-new/makefile	(revision 38898)
+++ /trunk/src/VBox/Devices/PC/BIOS-new/makefile	(revision 38899)
@@ -31,5 +31,5 @@
 	keyboard.obj disk.obj serial.obj system.obj timepci.obj &
 	ps2mouse.obj parallel.obj logo.obj scsi.obj ahci.obj &
-	pciutil.obj pcibio32.obj orgs.obj
+	pciutil.obj vds.obj pcibio32.obj orgs.obj
 
 vbxbios.rom : vbxbios.bin
@@ -54,3 +54,3 @@
 clean : .symbolic
 	@rm -f *.obj *.err
-	@rm -f vbxbios.bin vbxbios.rom vbxbios.map
+	@rm -f vbxbios.bin vbxbios.rom vbxbios.map vbxbios.sym
Index: /trunk/src/VBox/Devices/PC/BIOS-new/notes.txt
===================================================================
--- /trunk/src/VBox/Devices/PC/BIOS-new/notes.txt	(revision 38898)
+++ /trunk/src/VBox/Devices/PC/BIOS-new/notes.txt	(revision 38899)
@@ -23,4 +23,10 @@
 - OS/2 is the only known guest which can run the 16-bit PCI BIOS in protected
   mode (but only if the 32-bit PCI BIOS is unavailable).
+
+- Any disk reads which use bus-master DMA (AHCI, IDE BM) must use VDS
+  (Virtual DMA Services) when present. Otherwise any reads/writes when the
+  real mode addresses don't map directly to physical addresses will fail
+  horribly. DOS 6.x with EMM386 is a good testcase (esp. loading drivers
+  into UMBs).
 
 
Index: /trunk/src/VBox/Devices/PC/BIOS-new/vds.c
===================================================================
--- /trunk/src/VBox/Devices/PC/BIOS-new/vds.c	(revision 38899)
+++ /trunk/src/VBox/Devices/PC/BIOS-new/vds.c	(revision 38899)
@@ -0,0 +1,117 @@
+/** @file
+ * Utility routines for calling the Virtual DMA Services.
+ */
+
+/*
+ * Copyright (C) 2011 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+#include <stdint.h>
+#include "biosint.h"
+#include "vds.h"
+
+typedef struct {
+    uint8_t     major;      /* VDS spec major version number. */
+    uint8_t     minor;      /* VDS spec minor version number. */
+    uint16_t    flags;      /* Capabilities/status flags. */
+    uint16_t    prod_no;    /* Product number. */
+    uint16_t    prod_rev;   /* Product revision number. */
+    uint32_t    max_buf;    /* Maximum buffer size supported. */
+} vds_ver;
+
+int vds_is_present( void )
+{
+    uint8_t __far   *vds_flags;
+
+    vds_flags = MK_FP( 0x40, VDS_FLAGS_OFS );
+    return( !!(*vds_flags & VDS_PRESENT) );
+}
+
+int vds_lock_sg( vds_edds __far *edds );
+#pragma aux vds_lock_sg =   \
+    "mov    ax, 8105h"      \
+    "mov    dx, 0"          \
+    "int    4Bh"            \
+    "jc     error"          \
+    "xor    al, al"         \
+    "error:"                \
+    "cbw"                   \
+    parm [es di] value [ax];
+
+int vds_unlock_sg( vds_edds __far *edds );
+#pragma aux vds_unlock_sg = \
+    "mov    ax, 8106h"      \
+    "mov    dx, 0"          \
+    "int    4Bh"            \
+    "jc     error"          \
+    "xor    al, al"         \
+    "error:"                \
+    "cbw"                   \
+    parm [es di] value [ax];
+
+
+/*
+ * Convert a real mode 16:16 segmented address to a simple 32-bit 
+ * linear address. 
+ */
+uint32_t vds_real_to_lin( void __far *ptr )
+{
+    return( ((uint32_t)FP_SEG( ptr ) << 4) + FP_OFF( ptr ) );
+}
+
+/*
+ * Build a VDS-style scatter/gather list, regardless of whether VDS is
+ * present or not. This routine either calls VDS to do the work or
+ * trivially creates the list if no remapping is needed.
+ */
+int vds_build_sg_list( vds_edds __far *edds, void __far *buf, uint16_t len )
+{
+    int     rc;
+
+    /* NB: The num_avail field in the EDDS must be set correctly! */
+    edds->region_size = len;
+    edds->offset = vds_real_to_lin( buf );
+    edds->seg_sel = 0;  /* Indicates a linear address. */
+    if( vds_is_present() ) {
+        /* VDS is present, use it. */
+        rc = vds_lock_sg( edds );
+    } else {
+        /* No VDS, do it ourselves with one S/G entry. */
+        edds->num_used = 1;
+        edds->u.sg[0].phys_addr = edds->offset;
+        edds->u.sg[0].size      = len;
+        rc = VDS_SUCCESS;
+    }
+    return( rc );
+}
+
+/*
+ * Free a VDS-style scatter/gather list, regardless of whether VDS
+ * is present or not.
+ */
+int vds_free_sg_list( vds_edds __far *edds )
+{
+    int     rc;
+
+    if( vds_is_present() ) {
+        /* VDS is present, use it. */
+        rc = vds_unlock_sg( edds );
+    } else {
+        /* No VDS, not much to do. */
+        /* We could check here if the EDDS had in fact been built by us.
+         * But if VDS really went away, what can we do about it anyway?
+         */
+        rc = VDS_SUCCESS;
+    }
+    edds->num_used = 0;
+    return( rc );
+}
Index: /trunk/src/VBox/Devices/PC/BIOS-new/vds.h
===================================================================
--- /trunk/src/VBox/Devices/PC/BIOS-new/vds.h	(revision 38899)
+++ /trunk/src/VBox/Devices/PC/BIOS-new/vds.h	(revision 38899)
@@ -0,0 +1,95 @@
+/* Virtual DMA Services (VDS) */
+
+#define VDS_FLAGS_OFS	0x7B	/* Offset of VDS flag byte in BDA. */
+#define VDS_PRESENT	    0x20	/* The VDS present bit. */
+
+/* The DMA descriptor data structure. */
+
+typedef struct {
+    uint32_t	region_size;	/* Region size in bytes. */
+    uint32_t	offset;		    /* Offset. */
+    uint16_t	seg_sel;	    /* Segment selector. */
+    uint16_t	buf_id;		    /* Buffer ID. */
+    uint32_t	phys_addr;	    /* Physical address. */
+} vds_dds;
+
+
+/* Scatter/gather descriptor entry. */
+
+typedef struct {
+    uint32_t	phys_addr;	/* Physical address. */
+    uint32_t	size;		/* Entry size. */
+} vds_sg;
+
+/* The extended DDS for scatter/gather. Note that the EDDS contains either
+ * S/G descriptors or x86-style PTEs.
+ */
+
+typedef struct {
+    uint32_t	region_size;	/* Region size in bytes. */
+    uint32_t	offset;		    /* Offset. */
+    uint16_t	seg_sel;	    /* Segment or selector. */
+    uint16_t	resvd;		    /* Reserved. */
+    uint16_t	num_avail;	    /* Number of entries available. */
+    uint16_t	num_used;	    /* Number of entries used. */
+    union {
+        vds_sg	    sg[1];	    /* S/G entry array. */
+        uint32_t    pte[1];	    /* Page table entry array. */
+    } u;
+} vds_edds;
+    
+
+/* VDS services */
+
+#define VDS_SERVICE		        0x81
+
+#define VDS_GET_VERSION		    0x02	/* Get version */
+#define VDS_LOCK_BUFFER		    0x03	/* Lock DMA buffer region */
+#define VDS_UNLOCK_BUFFER	    0x04	/* Unlock DMA buffer region */
+#define VDS_SG_LOCK		        0x05	/* Scatter/gather lock region */
+#define VDS_SG_UNLOCK		    0x06	/* Scatter/gather unlock region */
+#define VDS_REQUEST_BUFFER	    0x07	/* Request DMA buffer */
+#define VDS_RELEASE_BUFFER	    0x08	/* Release DMA buffer */
+#define VDS_BUFFER_COPYIN	    0x09	/* Copy into DMA buffer */
+#define VDS_BUFFER_COPYOUT	    0x0A	/* Copy out of DMA buffer */
+#define VDS_DISABLE_DMA_XLAT	0x0B	/* Disable DMA translation */
+#define VDS_ENABLE_DMA_XLAT	    0x0C	/* Enable DMA translation */
+
+/* VDS error codes */
+
+#define VDS_SUCCESS		        0x00	/* No error */
+#define VDS_ERR_NOT_CONTIG	    0x01	/* Region not contiguous */
+#define VDS_ERR_BOUNDRY_CROSS	0x02	/* Rgn crossed phys align boundary */
+#define VDS_ERR_CANT_LOCK	    0x03	/* Unable to lock pages */
+#define VDS_ERR_NO_BUF		    0x04	/* No buffer available */
+#define VDS_ERR_RGN_TOO_BIG	    0x05	/* Region too large for buffer */
+#define VDS_ERR_BUF_IN_USE	    0x06	/* Buffer currently in use */
+#define VDS_ERR_RGN_INVALID	    0x07	/* Invalid memory region */
+#define VDS_ERR_RGN_NOT_LOCKED	0x08	/* Region was not locked */
+#define VDS_ERR_TOO_MANY_PAGES	0x09	/* Num pages greater than table len */
+#define VDS_ERR_INVALID_ID	    0x0A	/* Invalid buffer ID */
+#define VDS_ERR_BNDRY_VIOL	    0x0B	/* Buffer boundary violated */
+#define VDS_ERR_INVAL_DMACHN	0x0C	/* Invalid DMA channel number */
+#define VDS_ERR_COUNT_OVRFLO	0x0D	/* Disable count overflow */
+#define VDS_ERR_COUNT_UNDRFLO	0x0E	/* Disable count underflow */
+#define VDS_ERR_UNSUPP_FUNC	    0x0F	/* Function not supported */
+#define VDS_ERR_BAD_FLAG	    0x10	/* Reserved flag bits set in DX */
+
+/* VDS option flags */
+
+#define VDSF_AUTOCOPY		    0x02	/* Automatic copy to/from buffer */
+#define VDSF_NOALLOC		    0x04	/* Disable auto buffer allocation */
+#define VDSF_NOREMAP		    0x08	/* Disable auto remap feature */
+#define VDSF_NO64K		        0x10	/* Region can't cross 64K boundary */
+#define VDSF_NO128K		        0x20	/* Region can't cross 128K boundary */
+#define VDSF_COPYTBL		    0x40	/* Copy page table for S/G remap */
+#define VDSF_NPOK		        0x80	/* Allow non-present pages for S/G */
+
+/* Higher level routines for utilizing VDS. */
+
+int vds_build_sg_list( vds_edds __far *edds, void __far *buf, uint16_t len );
+int vds_free_sg_list( vds_edds __far *edds );
+
+/* Helper for translating 16:16 real mode addresses to 32-bit linear. */
+
+uint32_t vds_real_to_lin( void __far *ptr );
