[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH] lba48 support
From: |
Jens Axboe |
Subject: |
Re: [Qemu-devel] [PATCH] lba48 support |
Date: |
Mon, 2 Jan 2006 16:20:01 +0100 |
On Mon, Jan 02 2006, Jens Axboe wrote:
> On Fri, Dec 30 2005, Fabrice Bellard wrote:
> > Jens Axboe wrote:
> > >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).
>
> 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
diff -u -r1.38 ide.c
--- hw/ide.c 6 Aug 2005 09:14:32 -0000 1.38
+++ hw/ide.c 2 Jan 2006 14:52:50 -0000
@@ -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)
--
Jens Axboe
qemu-bigdisk.png
Description: PNG image