Index: /trunk/src/VBox/Devices/Storage/ATAController.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/ATAController.cpp	(revision 38893)
+++ /trunk/src/VBox/Devices/Storage/ATAController.cpp	(revision 38894)
@@ -106,4 +106,5 @@
 static bool ataWriteSectorsSS(AHCIATADevState *);
 static bool ataExecuteDeviceDiagnosticSS(AHCIATADevState *);
+static bool ataTrimSS(AHCIATADevState *);
 static bool ataPacketSS(AHCIATADevState *);
 static bool atapiGetConfigurationSS(AHCIATADevState *);
@@ -161,4 +162,5 @@
     ATAFN_SS_WRITE_SECTORS,
     ATAFN_SS_EXECUTE_DEVICE_DIAGNOSTIC,
+    ATAFN_SS_TRIM,
     ATAFN_SS_PACKET,
     ATAFN_SS_ATAPI_GET_CONFIGURATION,
@@ -193,4 +195,5 @@
     ataWriteSectorsSS,
     ataExecuteDeviceDiagnosticSS,
+    ataTrimSS,
     ataPacketSS,
     atapiGetConfigurationSS,
@@ -740,6 +743,14 @@
     p[67] = RT_H2LE_U16(120); /* minimum PIO cycle time without flow control */
     p[68] = RT_H2LE_U16(120); /* minimum PIO cycle time with IORDY flow control */
-    p[80] = RT_H2LE_U16(0x7e); /* support everything up to ATA/ATAPI-6 */
-    p[81] = RT_H2LE_U16(0x22); /* conforms to ATA/ATAPI-6 */
+    if (s->pDrvBlock->pfnDiscard)
+    {
+        p[80] = RT_H2LE_U16(0x1f0); /* support everything up to ATA/ATAPI-8 ACS */
+        p[81] = RT_H2LE_U16(0x28); /* conforms to ATA/ATAPI-8 ACS */
+    }
+    else
+    {
+        p[80] = RT_H2LE_U16(0x7e); /* support everything up to ATA/ATAPI-6 */
+        p[81] = RT_H2LE_U16(0x22); /* conforms to ATA/ATAPI-6 */
+    }
     p[82] = RT_H2LE_U16(1 << 3 | 1 << 5 | 1 << 6); /* supports power management,  write cache and look-ahead */
     if (s->cTotalSectors <= (1 << 28) - 1)
@@ -763,4 +774,6 @@
         p[103] = RT_H2LE_U16(s->cTotalSectors >> 48);
     }
+    if (s->pDrvBlock->pfnDiscard) /** @todo: Set bit 14 in word 69 too? (Deterministic read after TRIM). */
+        p[169] = RT_H2LE_U16(1); /* DATA SET MANAGEMENT command supported. */
     if (s->fNonRotational)
         p[217] = RT_H2LE_U16(1); /* Non-rotational medium */
@@ -2984,4 +2997,80 @@
 
 
+static int ataTrimSectors(AHCIATADevState *s, uint64_t u64Sector, uint32_t cSectors,
+                          bool *pfRedo)
+{
+    RTRANGE TrimRange;
+    PAHCIATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
+    int rc;
+
+    PDMCritSectLeave(&pCtl->lock);
+
+    TrimRange.offStart = u64Sector * 512;
+    TrimRange.cbRange  = cSectors * 512;
+
+    s->pLed->Asserted.s.fWriting = s->pLed->Actual.s.fWriting = 1;
+    rc = s->pDrvBlock->pfnDiscard(s->pDrvBlock, &TrimRange, 1);
+    s->pLed->Actual.s.fWriting = 0;
+
+    if (RT_SUCCESS(rc))
+        *pfRedo = false;
+    else
+        *pfRedo = ataIsRedoSetWarning(s, rc);
+
+    STAM_PROFILE_START(&pCtl->StatLockWait, a);
+    PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
+    STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
+    return rc;
+}
+
+
+static bool ataTrimSS(AHCIATADevState *s)
+{
+    int rc;
+    uint32_t cRangesMax;
+    uint64_t *pu64Range = (uint64_t *)s->CTX_SUFF(pbIOBuffer);
+    bool fRedo;
+
+    cRangesMax = s->cbElementaryTransfer / sizeof(uint64_t);
+    Assert(cRangesMax);
+
+    while (cRangesMax-- > 0)
+    {
+        if (ATA_RANGE_LENGTH_GET(*pu64Range) == 0)
+            break;
+
+        rc = ataTrimSectors(s, *pu64Range & ATA_RANGE_LBA_MASK,
+                            ATA_RANGE_LENGTH_GET(*pu64Range), &fRedo);
+        if (RT_FAILURE(rc))
+            break;
+
+        pu64Range++;
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        s->iSourceSink = ATAFN_SS_NULL;
+        ataCmdOK(s, ATA_STAT_SEEK);
+    }
+    else
+    {
+        if (fRedo)
+            return fRedo;
+        if (s->cErrors++ < MAX_LOG_REL_ERRORS)
+            LogRel(("PIIX3 ATA: LUN#%d: disk trim error (rc=%Rrc iSector=%#RX64 cSectors=%#RX32)\n",
+                    s->iLUN, rc, *pu64Range & ATA_RANGE_LBA_MASK, ATA_RANGE_LENGTH_GET(*pu64Range)));
+
+        /*
+         * Check if we got interrupted. We don't need to set status variables
+         * because the request was aborted.
+         */
+        if (rc != VERR_INTERRUPTED)
+            ataCmdError(s, ID_ERR);
+    }
+
+    return false;
+}
+
+
 static void ataParseCmd(AHCIATADevState *s, uint8_t cmd)
 {
@@ -3225,4 +3314,13 @@
             ataStartTransfer(s, ATAPI_PACKET_SIZE, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_PACKET, ATAFN_SS_PACKET, false);
             break;
+        case ATA_DATA_SET_MANAGEMENT:
+            if (!s->pDrvBlock || !s->pDrvBlock->pfnDiscard)
+                goto abort_cmd;
+            if (   !(s->uATARegFeature & UINT8_C(0x01))
+                || (s->uATARegFeature & ~UINT8_C(0x01)))
+                goto abort_cmd;
+            s->fDMA = true;
+            ataStartTransfer(s, (s->uATARegNSectorHOB << 8 | s->uATARegNSector) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_NULL, ATAFN_SS_TRIM, false);
+            break;
         default:
         abort_cmd:
Index: /trunk/src/VBox/Devices/Storage/DevATA.cpp
===================================================================
--- /trunk/src/VBox/Devices/Storage/DevATA.cpp	(revision 38893)
+++ /trunk/src/VBox/Devices/Storage/DevATA.cpp	(revision 38894)
@@ -572,4 +572,5 @@
 static bool ataWriteSectorsSS(ATADevState *);
 static bool ataExecuteDeviceDiagnosticSS(ATADevState *);
+static bool ataTrimSS(ATADevState *);
 static bool ataPacketSS(ATADevState *);
 static bool atapiGetConfigurationSS(ATADevState *);
@@ -628,4 +629,5 @@
     ATAFN_SS_WRITE_SECTORS,
     ATAFN_SS_EXECUTE_DEVICE_DIAGNOSTIC,
+    ATAFN_SS_TRIM,
     ATAFN_SS_PACKET,
     ATAFN_SS_ATAPI_GET_CONFIGURATION,
@@ -661,4 +663,5 @@
     ataWriteSectorsSS,
     ataExecuteDeviceDiagnosticSS,
+    ataTrimSS,
     ataPacketSS,
     atapiGetConfigurationSS,
@@ -1217,6 +1220,14 @@
     p[67] = RT_H2LE_U16(120); /* minimum PIO cycle time without flow control */
     p[68] = RT_H2LE_U16(120); /* minimum PIO cycle time with IORDY flow control */
-    p[80] = RT_H2LE_U16(0x7e); /* support everything up to ATA/ATAPI-6 */
-    p[81] = RT_H2LE_U16(0x22); /* conforms to ATA/ATAPI-6 */
+    if (s->pDrvBlock->pfnDiscard)
+    {
+        p[80] = RT_H2LE_U16(0x1f0); /* support everything up to ATA/ATAPI-8 ACS */
+        p[81] = RT_H2LE_U16(0x28); /* conforms to ATA/ATAPI-8 ACS */
+    }
+    else
+    {
+        p[80] = RT_H2LE_U16(0x7e); /* support everything up to ATA/ATAPI-6 */
+        p[81] = RT_H2LE_U16(0x22); /* conforms to ATA/ATAPI-6 */
+    }
     p[82] = RT_H2LE_U16(1 << 3 | 1 << 5 | 1 << 6); /* supports power management,  write cache and look-ahead */
     if (s->cTotalSectors <= (1 << 28) - 1)
@@ -1240,4 +1251,6 @@
         p[103] = RT_H2LE_U16(s->cTotalSectors >> 48);
     }
+    if (s->pDrvBlock->pfnDiscard) /** @todo: Set bit 14 in word 69 too? (Deterministic read after TRIM). */
+        p[169] = RT_H2LE_U16(1); /* DATA SET MANAGEMENT command supported. */
     if (s->fNonRotational)
         p[217] = RT_H2LE_U16(1); /* Non-rotational medium */
@@ -3735,4 +3748,80 @@
         ataSetStatusValue(s, ATA_STAT_READY | ATA_STAT_SEEK);
     s->uATARegError = 0x01;
+    return false;
+}
+
+
+static int ataTrimSectors(ATADevState *s, uint64_t u64Sector, uint32_t cSectors,
+                          bool *pfRedo)
+{
+    RTRANGE TrimRange;
+    PATACONTROLLER pCtl = ATADEVSTATE_2_CONTROLLER(s);
+    int rc;
+
+    PDMCritSectLeave(&pCtl->lock);
+
+    TrimRange.offStart = u64Sector * 512;
+    TrimRange.cbRange  = cSectors * 512;
+
+    s->Led.Asserted.s.fWriting = s->Led.Actual.s.fWriting = 1;
+    rc = s->pDrvBlock->pfnDiscard(s->pDrvBlock, &TrimRange, 1);
+    s->Led.Actual.s.fWriting = 0;
+
+    if (RT_SUCCESS(rc))
+        *pfRedo = false;
+    else
+        *pfRedo = ataIsRedoSetWarning(s, rc);
+
+    STAM_PROFILE_START(&pCtl->StatLockWait, a);
+    PDMCritSectEnter(&pCtl->lock, VINF_SUCCESS);
+    STAM_PROFILE_STOP(&pCtl->StatLockWait, a);
+    return rc;
+}
+
+
+static bool ataTrimSS(ATADevState *s)
+{
+    int rc;
+    uint32_t cRangesMax;
+    uint64_t *pu64Range = (uint64_t *)s->CTX_SUFF(pbIOBuffer);
+    bool fRedo;
+
+    cRangesMax = s->cbElementaryTransfer / sizeof(uint64_t);
+    Assert(cRangesMax);
+
+    while (cRangesMax-- > 0)
+    {
+        if (ATA_RANGE_LENGTH_GET(*pu64Range) == 0)
+            break;
+
+        rc = ataTrimSectors(s, *pu64Range & ATA_RANGE_LBA_MASK,
+                            ATA_RANGE_LENGTH_GET(*pu64Range), &fRedo);
+        if (RT_FAILURE(rc))
+            break;
+
+        pu64Range++;
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        s->iSourceSink = ATAFN_SS_NULL;
+        ataCmdOK(s, ATA_STAT_SEEK);
+    }
+    else
+    {
+        if (fRedo)
+            return fRedo;
+        if (s->cErrors++ < MAX_LOG_REL_ERRORS)
+            LogRel(("PIIX3 ATA: LUN#%d: disk trim error (rc=%Rrc iSector=%#RX64 cSectors=%#RX32)\n",
+                    s->iLUN, rc, *pu64Range & ATA_RANGE_LBA_MASK, ATA_RANGE_LENGTH_GET(*pu64Range)));
+
+        /*
+         * Check if we got interrupted. We don't need to set status variables
+         * because the request was aborted.
+         */
+        if (rc != VERR_INTERRUPTED)
+            ataCmdError(s, ID_ERR);
+    }
+
     return false;
 }
@@ -3992,4 +4081,13 @@
             ataStartTransfer(s, ATAPI_PACKET_SIZE, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_PACKET, ATAFN_SS_PACKET, false);
             break;
+        case ATA_DATA_SET_MANAGEMENT:
+            if (!s->pDrvBlock || !s->pDrvBlock->pfnDiscard)
+                goto abort_cmd;
+            if (   !(s->uATARegFeature & UINT8_C(0x01))
+                || (s->uATARegFeature & ~UINT8_C(0x01)))
+                goto abort_cmd;
+            s->fDMA = true;
+            ataStartTransfer(s, (s->uATARegNSectorHOB << 8 | s->uATARegNSector) * 512, PDMBLOCKTXDIR_TO_DEVICE, ATAFN_BT_NULL, ATAFN_SS_TRIM, false);
+            break;
         default:
         abort_cmd:
@@ -6135,4 +6233,7 @@
         }
         LogRel(("PIIX3 ATA: LUN#%d: disk, PCHS=%u/%u/%u, total number of sectors %Ld\n", pIf->iLUN, pIf->PCHSGeometry.cCylinders, pIf->PCHSGeometry.cHeads, pIf->PCHSGeometry.cSectors, pIf->cTotalSectors));
+
+        if (pIf->pDrvBlock->pfnDiscard)
+            LogRel(("PIIX3 ATA: LUN#%d: TRIM enabled\n", pIf->iLUN));
     }
     return rc;
Index: /trunk/src/VBox/Devices/Storage/ide.h
===================================================================
--- /trunk/src/VBox/Devices/Storage/ide.h	(revision 38893)
+++ /trunk/src/VBox/Devices/Storage/ide.h	(revision 38894)
@@ -176,4 +176,10 @@
 #define ATA_MODEL_NUMBER_LENGTH         40
 
+/** Mask to get the LBA value from a LBA range. */
+#define ATA_RANGE_LBA_MASK    UINT64_C(0xffffffffffff)
+/** Mas to get the length value from a LBA range. */
+#define ATA_RANGE_LENGTH_MASK UINT64_C(0xffff000000000000)
+/** Returns the length of the range in sectors. */
+#define ATA_RANGE_LENGTH_GET(val) (((val) & ATA_RANGE_LENGTH_MASK) >> 48)
 
 /* ATAPI defines */
