Skip to contentSkip to conversation

lba48 support

PATCH In review Jens Axboe 3 participants 5 messages Dec 29, 2005 – Jan 2, 2006

lore
Share & tools
lore

Jens Axboe

author
#0 lore raw

PATCH 1/1 lba48 support

Hi,

Saw the posts on this the other day and had a few spare hours to play with this. Works for me, with and without DMA (didn't test mult mode, but that should work fine too).

Test with caution though, it's changing the ide code so could eat your data if there's a bug there... Most clever OS's don't use lba48 even for lba48 capable drives, unless the device is > 2^28 sectors and the current request is past that (but they could be taking advantage of the larger transfer size possible, in which case lba48 will be used even for low sectors...).

Index: hw/ide.c
===================================================================
RCS file: /sources/qemu/qemu/hw/ide.c,v
retrieving revision 1.38

ide.c +123 -24
diff -u -r1.38 ide.c
@@ -305,14 +305,26 @@
     /* ide regs */
     uint8_t feature;
     uint8_t error;
-    uint16_t nsector; /* 0 is 256 to ease computations */
+    uint32_t nsector;
     uint8_t sector;
     uint8_t lcyl;
     uint8_t hcyl;
+    /* other part of tf for lba48 support */
+    uint8_t hob_feature;
+    uint8_t hob_nsector;
+    uint8_t hob_sector;
+    uint8_t hob_lcyl;
+    uint8_t hob_hcyl;
+
     uint8_t select;
     uint8_t status;
+
     /* 0x3f6 command, only meaningful for drive 0 */
     uint8_t cmd;
+    /* keeping track of registers written before command, for the hob part */
+    uint8_t regs_written;
+    /* set for lba48 access */
+    uint8_t lba48;
     /* depends on bit 4 in select, only meaningful for drive 0 */
     struct IDEState *cur_drive; 
     BlockDriverState *bs;
@@ -449,13 +461,17 @@
     put_le16(p + 61, s->nb_sectors >> 16);
     put_le16(p + 80, (1 << 1) | (1 << 2));
     put_le16(p + 82, (1 << 14));
-    put_le16(p + 83, (1 << 14));
+    put_le16(p + 83, (1 << 14) | (1 << 10)); /* lba48 supported */
     put_le16(p + 84, (1 << 14));
     put_le16(p + 85, (1 << 14));
-    put_le16(p + 86, 0);
+    put_le16(p + 86, (1 << 14) | (1 << 10)); /* lba48 supported */
     put_le16(p + 87, (1 << 14));
     put_le16(p + 88, 0x1f | (1 << 13));
     put_le16(p + 93, 1 | (1 << 14) | 0x2000 | 0x4000);
+    put_le16(p + 100, s->nb_sectors);
+    put_le16(p + 101, s->nb_sectors >> 16);
+    put_le16(p + 102, s->nb_sectors >> 32);
+    put_le16(p + 103, s->nb_sectors >> 48);
 }
 
 static void ide_atapi_identify(IDEState *s)
@@ -548,12 +564,18 @@
     int64_t sector_num;
     if (s->select & 0x40) {
         /* lba */
-        sector_num = ((s->select & 0x0f) << 24) | (s->hcyl << 16) | 
-            (s->lcyl << 8) | s->sector;
+	if (!s->lba48) {
+        	sector_num = ((s->select & 0x0f) << 24) | (s->hcyl << 16) |
+          	  (s->lcyl << 8) | s->sector;
+	} else {
+		sector_num = ((int64_t)s->hcyl << 40) |
+		  ((int64_t) s->lcyl << 32) |
+		  (s->sector << 24) | (s->hob_hcyl << 16) |
+		  (s->hob_lcyl << 8) | s->hob_sector;
+	}
     } else {
         sector_num = ((s->hcyl << 8) | s->lcyl) * s->heads * s->sectors +
-            (s->select & 0x0f) * s->sectors + 
-            (s->sector - 1);
+            (s->select & 0x0f) * s->sectors + (s->sector - 1);
     }
     return sector_num;
 }
@@ -562,10 +584,19 @@
 {
     unsigned int cyl, r;
     if (s->select & 0x40) {
-        s->select = (s->select & 0xf0) | (sector_num >> 24);
-        s->hcyl = (sector_num >> 16);
-        s->lcyl = (sector_num >> 8);
-        s->sector = (sector_num);
+	if (!s->lba48) {
+            s->select = (s->select & 0xf0) | (sector_num >> 24);
+            s->hcyl = (sector_num >> 16);
+            s->lcyl = (sector_num >> 8);
+            s->sector = (sector_num);
+	} else {
+	    s->hob_sector = sector_num;
+	    s->hob_lcyl = sector_num >> 8;
+	    s->hob_hcyl = sector_num >> 16;
+	    s->sector = sector_num >> 24;
+	    s->lcyl = sector_num >> 32;
+	    s->hcyl = sector_num >> 40;
+	}
     } else {
         cyl = sector_num / (s->heads * s->sectors);
         r = sector_num % (s->heads * s->sectors);
@@ -1455,37 +1486,64 @@
 {
     IDEState *ide_if = opaque;
     IDEState *s;
-    int unit, n;
+    int unit, n, hob = 0, lba48_cmd = 0;
 
 #ifdef DEBUG_IDE
     printf("IDE: write addr=0x%x val=0x%02x\n", addr, val);
 #endif
     addr &= 7;
+    if (addr >= 1 && addr <= 5) {
+	ide_if[0].regs_written |= (1 << addr);
+	hob = ((ide_if[0].regs_written & 0x3e) == 0x3e);
+    }
     switch(addr) {
     case 0:
         break;
     case 1:
         /* NOTE: data is written to the two drives */
-        ide_if[0].feature = val;
-        ide_if[1].feature = val;
+	if (!hob) {
+            ide_if[0].feature = val;
+            ide_if[1].feature = val;
+	} else {
+	    ide_if[0].hob_feature = val;
+	    ide_if[1].hob_feature = val;
+	}
         break;
     case 2:
-        if (val == 0)
-            val = 256;
-        ide_if[0].nsector = val;
-        ide_if[1].nsector = val;
+	if (!hob) {
+            ide_if[0].nsector = val;
+            ide_if[1].nsector = val;
+	} else {
+            ide_if[0].hob_nsector = val;
+            ide_if[1].hob_nsector = val;
+	}
         break;
     case 3:
-        ide_if[0].sector = val;
-        ide_if[1].sector = val;
+	if (!hob) {
+            ide_if[0].sector = val;
+            ide_if[1].sector = val;
+	} else {
+            ide_if[0].hob_sector = val;
+            ide_if[1].hob_sector = val;
+	}
         break;
     case 4:
-        ide_if[0].lcyl = val;
-        ide_if[1].lcyl = val;
+	if (!hob) {
+            ide_if[0].lcyl = val;
+            ide_if[1].lcyl = val;
+	} else {
+            ide_if[0].hob_lcyl = val;
+            ide_if[1].hob_lcyl = val;
+	}
         break;
     case 5:
-        ide_if[0].hcyl = val;
-        ide_if[1].hcyl = val;
+	if (!hob) {
+            ide_if[0].hcyl = val;
+            ide_if[1].hcyl = val;
+	} else {
+            ide_if[0].hob_hcyl = val;
+            ide_if[1].hob_hcyl = val;
+	}
         break;
     case 6:
         ide_if[0].select = (val & ~0x10) | 0xa0;
@@ -1501,10 +1559,34 @@
 #if defined(DEBUG_IDE)
         printf("ide: CMD=%02x\n", val);
 #endif
+	/* clear regs written when we see any command */
+	ide_if[0].regs_written = 0;
+
         s = ide_if->cur_drive;
         /* ignore commands to non existant slave */
         if (s != ide_if && !s->bs) 
             break;
+
+	s->lba48 = lba48_cmd;
+
+	/* handle the 'magic' 0 nsector count conversion here. to avoid
+	 * fiddling with the rest of the read logic, we just store the
+	 * full sector count in ->nsector and ignore ->hob_nsector from now
+	 */
+	if (!s->lba48) {
+		if (!s->nsector)
+			s->nsector = 256;
+	} else {
+		if (!s->nsector && !s->hob_nsector)
+			s->nsector = 65536;
+		else {
+			int lo = s->hob_nsector;
+			int hi = s->nsector;
+
+			s->nsector = (hi << 8) | lo;
+		}
+	}
+
         switch(val) {
         case WIN_IDENTIFY:
             if (s->bs && !s->is_cdrom) {
@@ -1536,12 +1618,16 @@
             }
             ide_set_irq(s);
             break;
+        case WIN_VERIFY_EXT:
+	    lba48_cmd = 1;
         case WIN_VERIFY:
         case WIN_VERIFY_ONCE:
             /* do sector number check ? */
             s->status = READY_STAT;
             ide_set_irq(s);
             break;
+	case WIN_READ_EXT:
+	    lba48_cmd = 1;
         case WIN_READ:
         case WIN_READ_ONCE:
             if (!s->bs) 
@@ -1549,6 +1635,8 @@
             s->req_nb_sectors = 1;
             ide_sector_read(s);
             break;
+	case WIN_WRITE_EXT:
+	    lba48_cmd = 1;
         case WIN_WRITE:
         case WIN_WRITE_ONCE:
             s->error = 0;
@@ -1556,12 +1644,16 @@
             s->req_nb_sectors = 1;
             ide_transfer_start(s, s->io_buffer, 512, ide_sector_write);
             break;
+	case WIN_MULTREAD_EXT:
+	    lba48_cmd = 1;
         case WIN_MULTREAD:
             if (!s->mult_sectors)
                 goto abort_cmd;
             s->req_nb_sectors = s->mult_sectors;
             ide_sector_read(s);
             break;
+        case WIN_MULTWRITE_EXT:
+	    lba48_cmd = 1;
         case WIN_MULTWRITE:
             if (!s->mult_sectors)
                 goto abort_cmd;
@@ -1573,18 +1665,24 @@
                 n = s->req_nb_sectors;
             ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_write);
             break;
+	case WIN_READDMA_EXT:
+	    lba48_cmd = 1;
         case WIN_READDMA:
         case WIN_READDMA_ONCE:
             if (!s->bs) 
                 goto abort_cmd;
             ide_sector_read_dma(s);
             break;
+	case WIN_WRITEDMA_EXT:
+	    lba48_cmd = 1;
         case WIN_WRITEDMA:
         case WIN_WRITEDMA_ONCE:
             if (!s->bs) 
                 goto abort_cmd;
             ide_sector_write_dma(s);
             break;
+        case WIN_READ_NATIVE_MAX_EXT:
+	    lba48_cmd = 1;
         case WIN_READ_NATIVE_MAX:
             ide_set_sector(s, s->nb_sectors - 1);
             s->status = READY_STAT;
@@ -1615,6 +1713,7 @@
 	case WIN_STANDBYNOW1:
         case WIN_IDLEIMMEDIATE:
         case WIN_FLUSH_CACHE:
+        case WIN_FLUSH_CACHE_EXT:
 	    s->status = READY_STAT;
             ide_set_irq(s);
             break;
Signature · 1 line
Jens Axboe
#1 lore raw
Quoted 10 lines · Jens Axboe

Saw the posts on this the other day and had a few spare hours to play with this. Works for me, with and without DMA (didn't test mult mode, but that should work fine too).

Test with caution though, it's changing the ide code so could eat your data if there's a bug there... Most clever OS's don't use lba48 even for lba48 capable drives, unless the device is > 2^28 sectors and the current request is past that (but they could be taking advantage of the larger transfer size possible, in which case lba48 will be used even for low sectors...).

Thank you for the patch ! At least two details should be corrected before I can apply it:

1) Each duplicated IDE register acts as a 2 byte FIFO, so the logic you added in the write function should be modified (the regs_written field is not needed).

2) The read back logic should be implemented (HOB bit in the device control register).

Fabrice.

#2 lore raw
Quoted 12 lines · Fabrice Bellard
Quoted 10 lines · Jens Axboe

Saw the posts on this the other day and had a few spare hours to play with this. Works for me, with and without DMA (didn't test mult mode, but that should work fine too).

Test with caution though, it's changing the ide code so could eat your data if there's a bug there... Most clever OS's don't use lba48 even for lba48 capable drives, unless the device is > 2^28 sectors and the current request is past that (but they could be taking advantage of the larger transfer size possible, in which case lba48 will be used even for low sectors...).

Quoted 6 lines

Thank you for the patch ! At least two details should be corrected before I can apply it:

1) Each duplicated IDE register acts as a 2 byte FIFO, so the logic you added in the write function should be modified (the regs_written field is not needed).

Perfect, I wasn't very fond of that approach either (it seemed fragile).

Quoted 2 lines

2) The read back logic should be implemented (HOB bit in the device control register).

Indeed. I'll get these things fixed up, wont be before monday though.

Signature · 1 line
Jens Axboe
#3 lore raw
In reply to Fabrice Bellard
Quoted 15 lines · Fabrice Bellard
Quoted 10 lines · Jens Axboe

Saw the posts on this the other day and had a few spare hours to play with this. Works for me, with and without DMA (didn't test mult mode, but that should work fine too).

Test with caution though, it's changing the ide code so could eat your data if there's a bug there... Most clever OS's don't use lba48 even for lba48 capable drives, unless the device is > 2^28 sectors and the current request is past that (but they could be taking advantage of the larger transfer size possible, in which case lba48 will be used even for low sectors...).

Thank you for the patch ! At least two details should be corrected before I can apply it:

Quoted 6 lines

1) Each duplicated IDE register acts as a 2 byte FIFO, so the logic you added in the write function should be modified (the regs_written field is not needed).

2) The read back logic should be implemented (HOB bit in the device control register).

Updated patch below. The read back logic doesn't work right now, since we always set bits 5-7 (the obsolete) bits in device select. But I've dropped the regs_written hack, the hob registers are now (as intended) always the previous value. That makes it LIFO, which I suppose is what you meant?

Index: hw/ide.c
===================================================================
RCS file: /sources/qemu/qemu/hw/ide.c,v
retrieving revision 1.38

ide.c +122 -20
diff -u -r1.38 ide.c
@@ -305,14 +305,24 @@
     /* ide regs */
     uint8_t feature;
     uint8_t error;
-    uint16_t nsector; /* 0 is 256 to ease computations */
+    uint32_t nsector;
     uint8_t sector;
     uint8_t lcyl;
     uint8_t hcyl;
+    /* other part of tf for lba48 support */
+    uint8_t hob_feature;
+    uint8_t hob_nsector;
+    uint8_t hob_sector;
+    uint8_t hob_lcyl;
+    uint8_t hob_hcyl;
+
     uint8_t select;
     uint8_t status;
+
     /* 0x3f6 command, only meaningful for drive 0 */
     uint8_t cmd;
+    /* set for lba48 access */
+    uint8_t lba48;
     /* depends on bit 4 in select, only meaningful for drive 0 */
     struct IDEState *cur_drive; 
     BlockDriverState *bs;
@@ -449,13 +459,17 @@
     put_le16(p + 61, s->nb_sectors >> 16);
     put_le16(p + 80, (1 << 1) | (1 << 2));
     put_le16(p + 82, (1 << 14));
-    put_le16(p + 83, (1 << 14));
+    put_le16(p + 83, (1 << 14) | (1 << 10)); /* lba48 supported */
     put_le16(p + 84, (1 << 14));
     put_le16(p + 85, (1 << 14));
-    put_le16(p + 86, 0);
+    put_le16(p + 86, (1 << 14) | (1 << 10)); /* lba48 supported */
     put_le16(p + 87, (1 << 14));
     put_le16(p + 88, 0x1f | (1 << 13));
     put_le16(p + 93, 1 | (1 << 14) | 0x2000 | 0x4000);
+    put_le16(p + 100, s->nb_sectors);
+    put_le16(p + 101, s->nb_sectors >> 16);
+    put_le16(p + 102, s->nb_sectors >> 32);
+    put_le16(p + 103, s->nb_sectors >> 48);
 }
 
 static void ide_atapi_identify(IDEState *s)
@@ -548,12 +562,18 @@
     int64_t sector_num;
     if (s->select & 0x40) {
         /* lba */
-        sector_num = ((s->select & 0x0f) << 24) | (s->hcyl << 16) | 
-            (s->lcyl << 8) | s->sector;
+	if (!s->lba48) {
+        	sector_num = ((s->select & 0x0f) << 24) | (s->hcyl << 16) |
+          	  (s->lcyl << 8) | s->sector;
+	} else {
+		sector_num = ((int64_t)s->hcyl << 40) |
+		  ((int64_t) s->lcyl << 32) |
+		  (s->sector << 24) | (s->hob_hcyl << 16) |
+		  (s->hob_lcyl << 8) | s->hob_sector;
+	}
     } else {
         sector_num = ((s->hcyl << 8) | s->lcyl) * s->heads * s->sectors +
-            (s->select & 0x0f) * s->sectors + 
-            (s->sector - 1);
+            (s->select & 0x0f) * s->sectors + (s->sector - 1);
     }
     return sector_num;
 }
@@ -562,10 +582,19 @@
 {
     unsigned int cyl, r;
     if (s->select & 0x40) {
-        s->select = (s->select & 0xf0) | (sector_num >> 24);
-        s->hcyl = (sector_num >> 16);
-        s->lcyl = (sector_num >> 8);
-        s->sector = (sector_num);
+	if (!s->lba48) {
+            s->select = (s->select & 0xf0) | (sector_num >> 24);
+            s->hcyl = (sector_num >> 16);
+            s->lcyl = (sector_num >> 8);
+            s->sector = (sector_num);
+	} else {
+	    s->hob_sector = sector_num;
+	    s->hob_lcyl = sector_num >> 8;
+	    s->hob_hcyl = sector_num >> 16;
+	    s->sector = sector_num >> 24;
+	    s->lcyl = sector_num >> 32;
+	    s->hcyl = sector_num >> 40;
+	}
     } else {
         cyl = sector_num / (s->heads * s->sectors);
         r = sector_num % (s->heads * s->sectors);
@@ -1451,43 +1480,65 @@
     s->nb_sectors = nb_sectors;
 }
 
+static void ide_clear_hob(IDEState *ide_if)
+{
+    /* any write clears HOB high bit of device control register */
+    ide_if[0].select &= ~(1 << 7);
+    ide_if[1].select &= ~(1 << 7);
+}
+
 static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
 {
     IDEState *ide_if = opaque;
     IDEState *s;
-    int unit, n;
+    int unit, n, lba48_cmd = 0;
 
 #ifdef DEBUG_IDE
     printf("IDE: write addr=0x%x val=0x%02x\n", addr, val);
 #endif
+
     addr &= 7;
     switch(addr) {
     case 0:
         break;
     case 1:
+	ide_clear_hob(ide_if);
         /* NOTE: data is written to the two drives */
+	ide_if[0].hob_feature = ide_if[0].feature;
+	ide_if[1].hob_feature = ide_if[1].feature;
         ide_if[0].feature = val;
         ide_if[1].feature = val;
         break;
     case 2:
-        if (val == 0)
-            val = 256;
+	ide_clear_hob(ide_if);
+	ide_if[0].hob_nsector = ide_if[0].nsector;
+	ide_if[1].hob_nsector = ide_if[1].nsector;
         ide_if[0].nsector = val;
         ide_if[1].nsector = val;
         break;
     case 3:
+	ide_clear_hob(ide_if);
+	ide_if[0].hob_sector = ide_if[0].sector;
+	ide_if[1].hob_sector = ide_if[1].sector;
         ide_if[0].sector = val;
         ide_if[1].sector = val;
         break;
     case 4:
+	ide_clear_hob(ide_if);
+	ide_if[0].hob_lcyl = ide_if[0].lcyl;
+	ide_if[1].hob_lcyl = ide_if[1].lcyl;
         ide_if[0].lcyl = val;
         ide_if[1].lcyl = val;
         break;
     case 5:
+	ide_clear_hob(ide_if);
+	ide_if[0].hob_hcyl = ide_if[0].hcyl;
+	ide_if[1].hob_hcyl = ide_if[1].hcyl;
         ide_if[0].hcyl = val;
         ide_if[1].hcyl = val;
         break;
     case 6:
+	/* FIXME: HOB readback uses bit 7 */
         ide_if[0].select = (val & ~0x10) | 0xa0;
         ide_if[1].select = (val | 0x10) | 0xa0;
         /* select drive */
@@ -1505,6 +1556,27 @@
         /* ignore commands to non existant slave */
         if (s != ide_if && !s->bs) 
             break;
+
+	s->lba48 = lba48_cmd;
+
+	/* handle the 'magic' 0 nsector count conversion here. to avoid
+	 * fiddling with the rest of the read logic, we just store the
+	 * full sector count in ->nsector and ignore ->hob_nsector from now
+	 */
+	if (!s->lba48) {
+		if (!s->nsector)
+			s->nsector = 256;
+	} else {
+		if (!s->nsector && !s->hob_nsector)
+			s->nsector = 65536;
+		else {
+			int lo = s->hob_nsector;
+			int hi = s->nsector;
+
+			s->nsector = (hi << 8) | lo;
+		}
+	}
+
         switch(val) {
         case WIN_IDENTIFY:
             if (s->bs && !s->is_cdrom) {
@@ -1536,12 +1608,16 @@
             }
             ide_set_irq(s);
             break;
+        case WIN_VERIFY_EXT:
+	    lba48_cmd = 1;
         case WIN_VERIFY:
         case WIN_VERIFY_ONCE:
             /* do sector number check ? */
             s->status = READY_STAT;
             ide_set_irq(s);
             break;
+	case WIN_READ_EXT:
+	    lba48_cmd = 1;
         case WIN_READ:
         case WIN_READ_ONCE:
             if (!s->bs) 
@@ -1549,6 +1625,8 @@
             s->req_nb_sectors = 1;
             ide_sector_read(s);
             break;
+	case WIN_WRITE_EXT:
+	    lba48_cmd = 1;
         case WIN_WRITE:
         case WIN_WRITE_ONCE:
             s->error = 0;
@@ -1556,12 +1634,16 @@
             s->req_nb_sectors = 1;
             ide_transfer_start(s, s->io_buffer, 512, ide_sector_write);
             break;
+	case WIN_MULTREAD_EXT:
+	    lba48_cmd = 1;
         case WIN_MULTREAD:
             if (!s->mult_sectors)
                 goto abort_cmd;
             s->req_nb_sectors = s->mult_sectors;
             ide_sector_read(s);
             break;
+        case WIN_MULTWRITE_EXT:
+	    lba48_cmd = 1;
         case WIN_MULTWRITE:
             if (!s->mult_sectors)
                 goto abort_cmd;
@@ -1573,18 +1655,24 @@
                 n = s->req_nb_sectors;
             ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_write);
             break;
+	case WIN_READDMA_EXT:
+	    lba48_cmd = 1;
         case WIN_READDMA:
         case WIN_READDMA_ONCE:
             if (!s->bs) 
                 goto abort_cmd;
             ide_sector_read_dma(s);
             break;
+	case WIN_WRITEDMA_EXT:
+	    lba48_cmd = 1;
         case WIN_WRITEDMA:
         case WIN_WRITEDMA_ONCE:
             if (!s->bs) 
                 goto abort_cmd;
             ide_sector_write_dma(s);
             break;
+        case WIN_READ_NATIVE_MAX_EXT:
+	    lba48_cmd = 1;
         case WIN_READ_NATIVE_MAX:
             ide_set_sector(s, s->nb_sectors - 1);
             s->status = READY_STAT;
@@ -1615,6 +1703,7 @@
 	case WIN_STANDBYNOW1:
         case WIN_IDLEIMMEDIATE:
         case WIN_FLUSH_CACHE:
+        case WIN_FLUSH_CACHE_EXT:
 	    s->status = READY_STAT;
             ide_set_irq(s);
             break;
@@ -1666,9 +1755,12 @@
     IDEState *ide_if = opaque;
     IDEState *s = ide_if->cur_drive;
     uint32_t addr;
-    int ret;
+    int ret, hob;
 
     addr = addr1 & 7;
+    /* FIXME: HOB readback uses bit 7, but it's always set right now */
+    /* hob = s->select & (1 << 7); */
+    hob = 0;
     switch(addr) {
     case 0:
         ret = 0xff;
@@ -1676,32 +1768,42 @@
     case 1:
         if (!ide_if[0].bs && !ide_if[1].bs)
             ret = 0;
-        else
+        else if (!hob)
             ret = s->error;
+	else
+	    ret = s->hob_feature;
         break;
     case 2:
         if (!ide_if[0].bs && !ide_if[1].bs)
             ret = 0;
-        else
+        else if (!hob)
             ret = s->nsector & 0xff;
+	else
+	    ret = s->hob_nsector;
         break;
     case 3:
         if (!ide_if[0].bs && !ide_if[1].bs)
             ret = 0;
-        else
+        else if (!hob)
             ret = s->sector;
+	else
+	    ret = s->hob_sector;
         break;
     case 4:
         if (!ide_if[0].bs && !ide_if[1].bs)
             ret = 0;
-        else
+        else if (!hob)
             ret = s->lcyl;
+	else
+	    ret = s->hob_lcyl;
         break;
     case 5:
         if (!ide_if[0].bs && !ide_if[1].bs)
             ret = 0;
-        else
+        else if (!hob)
             ret = s->hcyl;
+	else
+	    ret = s->hob_hcyl;
         break;
     case 6:
         if (!ide_if[0].bs && !ide_if[1].bs)
Signature · 1 line
Jens Axboe
#4 lore raw
Quoted 20 lines · Jens Axboe
Quoted 19 lines · Fabrice Bellard
Quoted 10 lines · Jens Axboe

Saw the posts on this the other day and had a few spare hours to play with this. Works for me, with and without DMA (didn't test mult mode, but that should work fine too).

Test with caution though, it's changing the ide code so could eat your data if there's a bug there... Most clever OS's don't use lba48 even for lba48 capable drives, unless the device is > 2^28 sectors and the current request is past that (but they could be taking advantage of the larger transfer size possible, in which case lba48 will be used even for low sectors...).

Thank you for the patch ! At least two details should be corrected before I can apply it:

1) Each duplicated IDE register acts as a 2 byte FIFO, so the logic you added in the write function should be modified (the regs_written field is not needed).

Quoted 8 lines
Quoted 2 lines

2) The read back logic should be implemented (HOB bit in the device control register).

Updated patch below. The read back logic doesn't work right now, since we always set bits 5-7 (the obsolete) bits in device select. But I've dropped the regs_written hack, the hob registers are now (as intended) always the previous value. That makes it LIFO, which I suppose is what you meant?

Scratch that, it was buggy. I should have tested it first, sorry about that. This one is tested and works. Also fixed some code to comply with the 4-space indentation that you use, I always tend to forget that as I always use tabs...

As a teaser, I'm attaching an installation screenshot from SUSE 10 with a 1.9TB disk attached.

Index: hw/ide.c
===================================================================
RCS file: /sources/qemu/qemu/hw/ide.c,v
retrieving revision 1.38

ide.c +124 -19
diff -u -r1.38 ide.c
@@ -305,14 +305,24 @@
     /* ide regs */
     uint8_t feature;
     uint8_t error;
-    uint16_t nsector; /* 0 is 256 to ease computations */
+    uint32_t nsector;
     uint8_t sector;
     uint8_t lcyl;
     uint8_t hcyl;
+    /* other part of tf for lba48 support */
+    uint8_t hob_feature;
+    uint8_t hob_nsector;
+    uint8_t hob_sector;
+    uint8_t hob_lcyl;
+    uint8_t hob_hcyl;
+
     uint8_t select;
     uint8_t status;
+
     /* 0x3f6 command, only meaningful for drive 0 */
     uint8_t cmd;
+    /* set for lba48 access */
+    uint8_t lba48;
     /* depends on bit 4 in select, only meaningful for drive 0 */
     struct IDEState *cur_drive; 
     BlockDriverState *bs;
@@ -449,13 +459,17 @@
     put_le16(p + 61, s->nb_sectors >> 16);
     put_le16(p + 80, (1 << 1) | (1 << 2));
     put_le16(p + 82, (1 << 14));
-    put_le16(p + 83, (1 << 14));
+    put_le16(p + 83, (1 << 14) | (1 << 10)); /* lba48 supported */
     put_le16(p + 84, (1 << 14));
     put_le16(p + 85, (1 << 14));
-    put_le16(p + 86, 0);
+    put_le16(p + 86, (1 << 14) | (1 << 10)); /* lba48 supported */
     put_le16(p + 87, (1 << 14));
     put_le16(p + 88, 0x1f | (1 << 13));
     put_le16(p + 93, 1 | (1 << 14) | 0x2000 | 0x4000);
+    put_le16(p + 100, s->nb_sectors);
+    put_le16(p + 101, s->nb_sectors >> 16);
+    put_le16(p + 102, s->nb_sectors >> 32);
+    put_le16(p + 103, s->nb_sectors >> 48);
 }
 
 static void ide_atapi_identify(IDEState *s)
@@ -548,12 +562,18 @@
     int64_t sector_num;
     if (s->select & 0x40) {
         /* lba */
-        sector_num = ((s->select & 0x0f) << 24) | (s->hcyl << 16) | 
-            (s->lcyl << 8) | s->sector;
+	if (!s->lba48) {
+        	sector_num = ((s->select & 0x0f) << 24) | (s->hcyl << 16) |
+          	  (s->lcyl << 8) | s->sector;
+	} else {
+		sector_num = ((int64_t)s->hob_hcyl << 40) |
+		  ((int64_t) s->hob_lcyl << 32) |
+		  (s->hob_sector << 24) | (s->hcyl << 16) |
+		  (s->lcyl << 8) | s->sector;
+	}
     } else {
         sector_num = ((s->hcyl << 8) | s->lcyl) * s->heads * s->sectors +
-            (s->select & 0x0f) * s->sectors + 
-            (s->sector - 1);
+            (s->select & 0x0f) * s->sectors + (s->sector - 1);
     }
     return sector_num;
 }
@@ -562,10 +582,19 @@
 {
     unsigned int cyl, r;
     if (s->select & 0x40) {
-        s->select = (s->select & 0xf0) | (sector_num >> 24);
-        s->hcyl = (sector_num >> 16);
-        s->lcyl = (sector_num >> 8);
-        s->sector = (sector_num);
+	if (!s->lba48) {
+            s->select = (s->select & 0xf0) | (sector_num >> 24);
+            s->hcyl = (sector_num >> 16);
+            s->lcyl = (sector_num >> 8);
+            s->sector = (sector_num);
+	} else {
+	    s->sector = sector_num;
+	    s->lcyl = sector_num >> 8;
+	    s->hcyl = sector_num >> 16;
+	    s->hob_sector = sector_num >> 24;
+	    s->hob_lcyl = sector_num >> 32;
+	    s->hob_hcyl = sector_num >> 40;
+	}
     } else {
         cyl = sector_num / (s->heads * s->sectors);
         r = sector_num % (s->heads * s->sectors);
@@ -1451,6 +1480,36 @@
     s->nb_sectors = nb_sectors;
 }
 
+static void ide_cmd_lba48_transform(IDEState *s)
+{
+    s->lba48 = 1;
+
+    /* handle the 'magic' 0 nsector count conversion here. to avoid
+     * fiddling with the rest of the read logic, we just store the
+     * full sector count in ->nsector and ignore ->hob_nsector from now
+     */
+    if (!s->lba48) {
+	if (!s->nsector)
+	    s->nsector = 256;
+    } else {
+	if (!s->nsector && !s->hob_nsector)
+	    s->nsector = 65536;
+	else {
+	    int lo = s->nsector;
+	    int hi = s->hob_nsector;
+
+	    s->nsector = (hi << 8) | lo;
+	}
+    }
+}
+
+static void ide_clear_hob(IDEState *ide_if)
+{
+    /* any write clears HOB high bit of device control register */
+    ide_if[0].select &= ~(1 << 7);
+    ide_if[1].select &= ~(1 << 7);
+}
+
 static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
 {
     IDEState *ide_if = opaque;
@@ -1460,34 +1519,49 @@
 #ifdef DEBUG_IDE
     printf("IDE: write addr=0x%x val=0x%02x\n", addr, val);
 #endif
+
     addr &= 7;
     switch(addr) {
     case 0:
         break;
     case 1:
+	ide_clear_hob(ide_if);
         /* NOTE: data is written to the two drives */
+	ide_if[0].hob_feature = ide_if[0].feature;
+	ide_if[1].hob_feature = ide_if[1].feature;
         ide_if[0].feature = val;
         ide_if[1].feature = val;
         break;
     case 2:
-        if (val == 0)
-            val = 256;
+	ide_clear_hob(ide_if);
+	ide_if[0].hob_nsector = ide_if[0].nsector;
+	ide_if[1].hob_nsector = ide_if[1].nsector;
         ide_if[0].nsector = val;
         ide_if[1].nsector = val;
         break;
     case 3:
+	ide_clear_hob(ide_if);
+	ide_if[0].hob_sector = ide_if[0].sector;
+	ide_if[1].hob_sector = ide_if[1].sector;
         ide_if[0].sector = val;
         ide_if[1].sector = val;
         break;
     case 4:
+	ide_clear_hob(ide_if);
+	ide_if[0].hob_lcyl = ide_if[0].lcyl;
+	ide_if[1].hob_lcyl = ide_if[1].lcyl;
         ide_if[0].lcyl = val;
         ide_if[1].lcyl = val;
         break;
     case 5:
+	ide_clear_hob(ide_if);
+	ide_if[0].hob_hcyl = ide_if[0].hcyl;
+	ide_if[1].hob_hcyl = ide_if[1].hcyl;
         ide_if[0].hcyl = val;
         ide_if[1].hcyl = val;
         break;
     case 6:
+	/* FIXME: HOB readback uses bit 7 */
         ide_if[0].select = (val & ~0x10) | 0xa0;
         ide_if[1].select = (val | 0x10) | 0xa0;
         /* select drive */
@@ -1505,6 +1579,7 @@
         /* ignore commands to non existant slave */
         if (s != ide_if && !s->bs) 
             break;
+
         switch(val) {
         case WIN_IDENTIFY:
             if (s->bs && !s->is_cdrom) {
@@ -1536,12 +1611,16 @@
             }
             ide_set_irq(s);
             break;
+        case WIN_VERIFY_EXT:
+	    ide_cmd_lba48_transform(s);
         case WIN_VERIFY:
         case WIN_VERIFY_ONCE:
             /* do sector number check ? */
             s->status = READY_STAT;
             ide_set_irq(s);
             break;
+	case WIN_READ_EXT:
+	    ide_cmd_lba48_transform(s);
         case WIN_READ:
         case WIN_READ_ONCE:
             if (!s->bs) 
@@ -1549,6 +1628,8 @@
             s->req_nb_sectors = 1;
             ide_sector_read(s);
             break;
+	case WIN_WRITE_EXT:
+	    ide_cmd_lba48_transform(s);
         case WIN_WRITE:
         case WIN_WRITE_ONCE:
             s->error = 0;
@@ -1556,12 +1637,16 @@
             s->req_nb_sectors = 1;
             ide_transfer_start(s, s->io_buffer, 512, ide_sector_write);
             break;
+	case WIN_MULTREAD_EXT:
+	    ide_cmd_lba48_transform(s);
         case WIN_MULTREAD:
             if (!s->mult_sectors)
                 goto abort_cmd;
             s->req_nb_sectors = s->mult_sectors;
             ide_sector_read(s);
             break;
+        case WIN_MULTWRITE_EXT:
+	    ide_cmd_lba48_transform(s);
         case WIN_MULTWRITE:
             if (!s->mult_sectors)
                 goto abort_cmd;
@@ -1573,18 +1658,24 @@
                 n = s->req_nb_sectors;
             ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_write);
             break;
+	case WIN_READDMA_EXT:
+	    ide_cmd_lba48_transform(s);
         case WIN_READDMA:
         case WIN_READDMA_ONCE:
             if (!s->bs) 
                 goto abort_cmd;
             ide_sector_read_dma(s);
             break;
+	case WIN_WRITEDMA_EXT:
+	    ide_cmd_lba48_transform(s);
         case WIN_WRITEDMA:
         case WIN_WRITEDMA_ONCE:
             if (!s->bs) 
                 goto abort_cmd;
             ide_sector_write_dma(s);
             break;
+        case WIN_READ_NATIVE_MAX_EXT:
+	    ide_cmd_lba48_transform(s);
         case WIN_READ_NATIVE_MAX:
             ide_set_sector(s, s->nb_sectors - 1);
             s->status = READY_STAT;
@@ -1615,6 +1706,7 @@
 	case WIN_STANDBYNOW1:
         case WIN_IDLEIMMEDIATE:
         case WIN_FLUSH_CACHE:
+        case WIN_FLUSH_CACHE_EXT:
 	    s->status = READY_STAT;
             ide_set_irq(s);
             break;
@@ -1666,9 +1758,12 @@
     IDEState *ide_if = opaque;
     IDEState *s = ide_if->cur_drive;
     uint32_t addr;
-    int ret;
+    int ret, hob;
 
     addr = addr1 & 7;
+    /* FIXME: HOB readback uses bit 7, but it's always set right now */
+    //hob = s->select & (1 << 7);
+    hob = 0;
     switch(addr) {
     case 0:
         ret = 0xff;
@@ -1676,32 +1771,42 @@
     case 1:
         if (!ide_if[0].bs && !ide_if[1].bs)
             ret = 0;
-        else
+        else if (!hob)
             ret = s->error;
+	else
+	    ret = s->hob_feature;
         break;
     case 2:
         if (!ide_if[0].bs && !ide_if[1].bs)
             ret = 0;
-        else
+        else if (!hob)
             ret = s->nsector & 0xff;
+	else
+	    ret = s->hob_nsector;
         break;
     case 3:
         if (!ide_if[0].bs && !ide_if[1].bs)
             ret = 0;
-        else
+        else if (!hob)
             ret = s->sector;
+	else
+	    ret = s->hob_sector;
         break;
     case 4:
         if (!ide_if[0].bs && !ide_if[1].bs)
             ret = 0;
-        else
+        else if (!hob)
             ret = s->lcyl;
+	else
+	    ret = s->hob_lcyl;
         break;
     case 5:
         if (!ide_if[0].bs && !ide_if[1].bs)
             ret = 0;
-        else
+        else if (!hob)
             ret = s->hcyl;
+	else
+	    ret = s->hob_hcyl;
         break;
     case 6:
         if (!ide_if[0].bs && !ide_if[1].bs)
Signature · 1 line
Jens Axboe