* [Qemu-devel] [PATCH 1/3] ide id updates
2006-01-04 12:12 [Qemu-devel] [PATCH 0/3] qemu ide updates Jens Axboe
@ 2006-01-04 12:12 ` Jens Axboe
2006-01-04 12:13 ` [Qemu-devel] [PATCH 2/3] ide lba48 support Jens Axboe
2006-01-04 12:16 ` [Qemu-devel] [PATCH 3/3] proper support of FLUSH_CACHE and FLUSH_CACHE_EXT Jens Axboe
2 siblings, 0 replies; 12+ messages in thread
From: Jens Axboe @ 2006-01-04 12:12 UTC (permalink / raw)
To: qemu-devel
Hi,
Subject: [PATCH] ide id updates
From: Jens Axboe <axboe@suse.de>
Date: 1136375788 +0100
Some changes to the ata/atapi identify code and default values:
- Store the drive id in the IDEState, so we can reliably set and query
new values. Right now doing things like:
doesn't work, as the IDENTIFY command will re-fill default values
everytime.
- Fill in IORDY, dma timings, and mdma modes.
- Don't set both 1 << 14 and 0x4000 for word 93, it's the same thing.
- Fill in supported/set ata specs
- Implement real setting of transfer mode (sub feature 0x03 of
WIN_SETFEATURES) so we can reflect the transfer mode requested by the
OS.
With this patch, Linux correctly identifies and sets DMA mode in the
drive by default.
---
hw/ide.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
1 files changed, 54 insertions(+), 7 deletions(-)
eaa17552cf9891cbc8fc46b826fde10a4d5e07c7
diff --git a/hw/ide.c b/hw/ide.c
index 28ed5ab..8196ace 100644
--- a/hw/ide.c
+++ b/hw/ide.c
@@ -296,6 +296,8 @@ typedef struct IDEState {
int cylinders, heads, sectors;
int64_t nb_sectors;
int mult_sectors;
+ int identify_set;
+ uint16_t identify_data[256];
SetIRQFunc *set_irq;
void *irq_opaque;
int irq;
@@ -414,6 +416,11 @@ static void ide_identify(IDEState *s)
unsigned int oldsize;
char buf[20];
+ if (s->identify_set) {
+ memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data));
+ return;
+ }
+
memset(s->io_buffer, 0, 512);
p = (uint16_t *)s->io_buffer;
put_le16(p + 0, 0x0040);
@@ -433,10 +440,10 @@ static void ide_identify(IDEState *s)
put_le16(p + 47, 0x8000 | MAX_MULT_SECTORS);
#endif
put_le16(p + 48, 1); /* dword I/O */
- put_le16(p + 49, 1 << 9 | 1 << 8); /* DMA and LBA supported */
+ put_le16(p + 49, (1 << 11) | (1 << 9) | (1 << 8)); /* DMA and LBA supported */
put_le16(p + 51, 0x200); /* PIO transfer cycle */
put_le16(p + 52, 0x200); /* DMA transfer cycle */
- put_le16(p + 53, 1 | 1 << 2); /* words 54-58,88 are valid */
+ put_le16(p + 53, 1 | 1 << 1 | 1 << 2); /* words 54-58,64-70,88 are valid */
put_le16(p + 54, s->cylinders);
put_le16(p + 55, s->heads);
put_le16(p + 56, s->sectors);
@@ -447,15 +454,24 @@ static void ide_identify(IDEState *s)
put_le16(p + 59, 0x100 | s->mult_sectors);
put_le16(p + 60, s->nb_sectors);
put_le16(p + 61, s->nb_sectors >> 16);
- put_le16(p + 80, (1 << 1) | (1 << 2));
+ put_le16(p + 63, 0x07); /* mdma0-2 supported */
+ put_le16(p + 65, 120);
+ put_le16(p + 66, 120);
+ put_le16(p + 67, 120);
+ put_le16(p + 68, 120);
+ put_le16(p + 80, 0xf0); /* ata3 -> ata6 supported */
+ put_le16(p + 81, 0x16); /* conforms to ata5 */
put_le16(p + 82, (1 << 14));
put_le16(p + 83, (1 << 14));
put_le16(p + 84, (1 << 14));
put_le16(p + 85, (1 << 14));
put_le16(p + 86, 0);
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 + 88, 0x3f | (1 << 13)); /* udma5 set and supported */
+ put_le16(p + 93, 1 | (1 << 14) | 0x2000);
+
+ memcpy(s->identify_data, p, sizeof(s->identify_data));
+ s->identify_set = 1;
}
static void ide_atapi_identify(IDEState *s)
@@ -463,6 +479,11 @@ static void ide_atapi_identify(IDEState
uint16_t *p;
char buf[20];
+ if (s->identify_set) {
+ memcpy(s->io_buffer, s->identify_data, sizeof(s->identify_data));
+ return;
+ }
+
memset(s->io_buffer, 0, 512);
p = (uint16_t *)s->io_buffer;
/* Removable CDROM, 50us response, 12 byte packets */
@@ -483,11 +504,14 @@ static void ide_atapi_identify(IDEState
put_le16(p + 66, 0xb4); /* recommended DMA multiword tx cycle time */
put_le16(p + 67, 0x12c); /* minimum PIO cycle time without flow control */
put_le16(p + 68, 0xb4); /* minimum PIO cycle time with IORDY flow control */
-
+
put_le16(p + 71, 30); /* in ns */
put_le16(p + 72, 30); /* in ns */
put_le16(p + 80, 0x1e); /* support up to ATA/ATAPI-4 */
+
+ memcpy(s->identify_data, p, sizeof(s->identify_data));
+ s->identify_set = 1;
}
static void ide_set_signature(IDEState *s)
@@ -1601,13 +1625,36 @@ static void ide_ioport_write(void *opaqu
/* XXX: valid for CDROM ? */
switch(s->feature) {
case 0x02: /* write cache enable */
- case 0x03: /* set transfer mode */
case 0x82: /* write cache disable */
case 0xaa: /* read look-ahead enable */
case 0x55: /* read look-ahead disable */
s->status = READY_STAT | SEEK_STAT;
ide_set_irq(s);
break;
+ case 0x03: { /* set transfer mode */
+ uint8_t val = s->nsector & 0x07;
+
+ switch (s->nsector >> 3) {
+ case 0x00: /* pio default */
+ case 0x01: /* pio mode */
+ put_le16(s->identify_data + 63,0x07);
+ put_le16(s->identify_data + 88,0x3f);
+ break;
+ case 0x04: /* mdma mode */
+ put_le16(s->identify_data + 63,0x07 | (1 << (val + 8)));
+ put_le16(s->identify_data + 88,0x3f);
+ break;
+ case 0x08: /* udma mode */
+ put_le16(s->identify_data + 63,0x07);
+ put_le16(s->identify_data + 88,0x3f | (1 << (val + 8)));
+ break;
+ default:
+ goto abort_cmd;
+ }
+ s->status = READY_STAT | SEEK_STAT;
+ ide_set_irq(s);
+ break;
+ }
default:
goto abort_cmd;
}
--
1.0.GIT
--
Jens Axboe
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [Qemu-devel] [PATCH 2/3] ide lba48 support
2006-01-04 12:12 [Qemu-devel] [PATCH 0/3] qemu ide updates Jens Axboe
2006-01-04 12:12 ` [Qemu-devel] [PATCH 1/3] ide id updates Jens Axboe
@ 2006-01-04 12:13 ` Jens Axboe
2006-02-01 22:34 ` Fabrice Bellard
2006-01-04 12:16 ` [Qemu-devel] [PATCH 3/3] proper support of FLUSH_CACHE and FLUSH_CACHE_EXT Jens Axboe
2 siblings, 1 reply; 12+ messages in thread
From: Jens Axboe @ 2006-01-04 12:13 UTC (permalink / raw)
To: qemu-devel
Subject: [PATCH] Add lba48 support to ide
From: Jens Axboe <axboe@suse.de>
Date: 1136376117 +0100
Add lba48 support for the ide code. Read back of hob registers isn't
there yet, though.
---
hw/ide.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
1 files changed, 128 insertions(+), 20 deletions(-)
7f24e6a79f23c81385604cc18e9e7158b581bf18
diff --git a/hw/ide.c b/hw/ide.c
index 8196ace..6a52347 100644
--- a/hw/ide.c
+++ b/hw/ide.c
@@ -307,14 +307,24 @@ typedef struct IDEState {
/* 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;
@@ -462,13 +472,19 @@ static void ide_identify(IDEState *s)
put_le16(p + 80, 0xf0); /* ata3 -> ata6 supported */
put_le16(p + 81, 0x16); /* conforms to ata5 */
put_le16(p + 82, (1 << 14));
- put_le16(p + 83, (1 << 14));
+ /* 13=flush_cache_ext,12=flush_cache,10=lba48 */
+ put_le16(p + 83, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
put_le16(p + 84, (1 << 14));
put_le16(p + 85, (1 << 14));
- put_le16(p + 86, 0);
+ /* 13=flush_cache_ext,12=flush_cache,10=lba48 */
+ put_le16(p + 86, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
put_le16(p + 87, (1 << 14));
put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */
put_le16(p + 93, 1 | (1 << 14) | 0x2000);
+ 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);
memcpy(s->identify_data, p, sizeof(s->identify_data));
s->identify_set = 1;
@@ -572,12 +588,19 @@ static int64_t ide_get_sector(IDEState *
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) |
+ ((int64_t) s->hob_sector << 24) |
+ ((int64_t) s->hcyl << 16) |
+ ((int64_t) 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;
}
@@ -586,10 +609,19 @@ static void ide_set_sector(IDEState *s,
{
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);
@@ -1475,6 +1507,36 @@ static void cdrom_change_cb(void *opaque
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;
@@ -1484,34 +1546,49 @@ static void ide_ioport_write(void *opaqu
#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 */
@@ -1529,6 +1606,7 @@ static void ide_ioport_write(void *opaqu
/* ignore commands to non existant slave */
if (s != ide_if && !s->bs)
break;
+
switch(val) {
case WIN_IDENTIFY:
if (s->bs && !s->is_cdrom) {
@@ -1560,12 +1638,16 @@ static void ide_ioport_write(void *opaqu
}
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)
@@ -1573,6 +1655,8 @@ static void ide_ioport_write(void *opaqu
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;
@@ -1580,12 +1664,16 @@ static void ide_ioport_write(void *opaqu
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;
@@ -1597,18 +1685,24 @@ static void ide_ioport_write(void *opaqu
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;
@@ -1659,9 +1753,10 @@ static void ide_ioport_write(void *opaqu
goto abort_cmd;
}
break;
+ case WIN_FLUSH_CACHE:
+ case WIN_FLUSH_CACHE_EXT:
case WIN_STANDBYNOW1:
case WIN_IDLEIMMEDIATE:
- case WIN_FLUSH_CACHE:
s->status = READY_STAT;
ide_set_irq(s);
break;
@@ -1713,9 +1808,12 @@ static uint32_t ide_ioport_read(void *op
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;
@@ -1723,32 +1821,42 @@ static uint32_t ide_ioport_read(void *op
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)
--
1.0.GIT
--
Jens Axboe
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [Qemu-devel] [PATCH 2/3] ide lba48 support
2006-01-04 12:13 ` [Qemu-devel] [PATCH 2/3] ide lba48 support Jens Axboe
@ 2006-02-01 22:34 ` Fabrice Bellard
2006-02-02 9:53 ` Jens Axboe
0 siblings, 1 reply; 12+ messages in thread
From: Fabrice Bellard @ 2006-02-01 22:34 UTC (permalink / raw)
To: qemu-devel
Jens Axboe wrote:
> Subject: [PATCH] Add lba48 support to ide
> From: Jens Axboe <axboe@suse.de>
> Date: 1136376117 +0100
>
> Add lba48 support for the ide code. Read back of hob registers isn't
> there yet, though.
Do you have a more recent patch ? In your latest patch, the lba48 field
is never reset and the nsector may be broken.
Fabrice.
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Qemu-devel] [PATCH 2/3] ide lba48 support
2006-02-01 22:34 ` Fabrice Bellard
@ 2006-02-02 9:53 ` Jens Axboe
0 siblings, 0 replies; 12+ messages in thread
From: Jens Axboe @ 2006-02-02 9:53 UTC (permalink / raw)
To: qemu-devel
On Wed, Feb 01 2006, Fabrice Bellard wrote:
> Jens Axboe wrote:
> >Subject: [PATCH] Add lba48 support to ide
> >From: Jens Axboe <axboe@suse.de>
> >Date: 1136376117 +0100
> >
> >Add lba48 support for the ide code. Read back of hob registers isn't
> >there yet, though.
>
> Do you have a more recent patch ? In your latest patch, the lba48 field
> is never reset and the nsector may be broken.
The lba48 setting did look a little odd, should be corrected now. I
guess that is what would affect the nsector stuff, it looks correct to
me know.
>From nobody Mon Sep 17 00:00:00 2001
From: Jens Axboe <axboe@suse.de>
Date: Thu Feb 2 10:51:20 2006 +0100
Subject: [PATCH] Add lba48 support to ide
Enables qemu to support ide disk images > 2^28 * 512 bytes.
---
hw/ide.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
1 files changed, 137 insertions(+), 20 deletions(-)
b67eb122b5646ddcfd13d45563bbe6aa5309e9c0
diff --git a/hw/ide.c b/hw/ide.c
index 50b8e63..01b10e1 100644
--- a/hw/ide.c
+++ b/hw/ide.c
@@ -307,14 +307,24 @@ typedef struct IDEState {
/* 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;
@@ -462,13 +472,19 @@ static void ide_identify(IDEState *s)
put_le16(p + 80, 0xf0); /* ata3 -> ata6 supported */
put_le16(p + 81, 0x16); /* conforms to ata5 */
put_le16(p + 82, (1 << 14));
- put_le16(p + 83, (1 << 14));
+ /* 13=flush_cache_ext,12=flush_cache,10=lba48 */
+ put_le16(p + 83, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
put_le16(p + 84, (1 << 14));
put_le16(p + 85, (1 << 14));
- put_le16(p + 86, 0);
+ /* 13=flush_cache_ext,12=flush_cache,10=lba48 */
+ put_le16(p + 86, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
put_le16(p + 87, (1 << 14));
put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */
put_le16(p + 93, 1 | (1 << 14) | 0x2000);
+ 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);
memcpy(s->identify_data, p, sizeof(s->identify_data));
s->identify_set = 1;
@@ -572,12 +588,19 @@ static int64_t ide_get_sector(IDEState *
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) |
+ ((int64_t) s->hob_sector << 24) |
+ ((int64_t) s->hcyl << 16) |
+ ((int64_t) 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;
}
@@ -586,10 +609,19 @@ static void ide_set_sector(IDEState *s,
{
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);
@@ -1475,43 +1507,89 @@ static void cdrom_change_cb(void *opaque
s->nb_sectors = nb_sectors;
}
+static void ide_cmd_lba48_transform(IDEState *s, int lba48)
+{
+ s->lba48 = lba48;
+
+ /* 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;
IDEState *s;
int unit, n;
+ int lba48 = 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 */
@@ -1529,6 +1607,7 @@ static void ide_ioport_write(void *opaqu
/* ignore commands to non existant slave */
if (s != ide_if && !s->bs)
break;
+
switch(val) {
case WIN_IDENTIFY:
if (s->bs && !s->is_cdrom) {
@@ -1560,35 +1639,50 @@ static void ide_ioport_write(void *opaqu
}
ide_set_irq(s);
break;
+ case WIN_VERIFY_EXT:
+ lba48 = 1;
case WIN_VERIFY:
case WIN_VERIFY_ONCE:
/* do sector number check ? */
+ ide_cmd_lba48_transform(s, lba48);
s->status = READY_STAT;
ide_set_irq(s);
break;
+ case WIN_READ_EXT:
+ lba48 = 1;
case WIN_READ:
case WIN_READ_ONCE:
if (!s->bs)
goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
s->req_nb_sectors = 1;
ide_sector_read(s);
break;
+ case WIN_WRITE_EXT:
+ lba48 = 1;
case WIN_WRITE:
case WIN_WRITE_ONCE:
+ ide_cmd_lba48_transform(s, lba48);
s->error = 0;
s->status = SEEK_STAT | READY_STAT;
s->req_nb_sectors = 1;
ide_transfer_start(s, s->io_buffer, 512, ide_sector_write);
break;
+ case WIN_MULTREAD_EXT:
+ lba48 = 1;
case WIN_MULTREAD:
if (!s->mult_sectors)
goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
s->req_nb_sectors = s->mult_sectors;
ide_sector_read(s);
break;
+ case WIN_MULTWRITE_EXT:
+ lba48 = 1;
case WIN_MULTWRITE:
if (!s->mult_sectors)
goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
s->error = 0;
s->status = SEEK_STAT | READY_STAT;
s->req_nb_sectors = s->mult_sectors;
@@ -1597,19 +1691,28 @@ static void ide_ioport_write(void *opaqu
n = s->req_nb_sectors;
ide_transfer_start(s, s->io_buffer, 512 * n, ide_sector_write);
break;
+ case WIN_READDMA_EXT:
+ lba48 = 1;
case WIN_READDMA:
case WIN_READDMA_ONCE:
if (!s->bs)
goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
ide_sector_read_dma(s);
break;
+ case WIN_WRITEDMA_EXT:
+ lba48 = 1;
case WIN_WRITEDMA:
case WIN_WRITEDMA_ONCE:
if (!s->bs)
goto abort_cmd;
+ ide_cmd_lba48_transform(s, lba48);
ide_sector_write_dma(s);
break;
+ case WIN_READ_NATIVE_MAX_EXT:
+ lba48 = 1;
case WIN_READ_NATIVE_MAX:
+ ide_cmd_lba48_transform(s, lba48);
ide_set_sector(s, s->nb_sectors - 1);
s->status = READY_STAT;
ide_set_irq(s);
@@ -1659,9 +1762,10 @@ static void ide_ioport_write(void *opaqu
goto abort_cmd;
}
break;
+ case WIN_FLUSH_CACHE:
+ case WIN_FLUSH_CACHE_EXT:
case WIN_STANDBYNOW1:
case WIN_IDLEIMMEDIATE:
- case WIN_FLUSH_CACHE:
s->status = READY_STAT;
ide_set_irq(s);
break;
@@ -1713,9 +1817,12 @@ static uint32_t ide_ioport_read(void *op
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;
@@ -1723,32 +1830,42 @@ static uint32_t ide_ioport_read(void *op
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)
--
1.1.6.g8233
--
Jens Axboe
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [Qemu-devel] [PATCH 3/3] proper support of FLUSH_CACHE and FLUSH_CACHE_EXT
2006-01-04 12:12 [Qemu-devel] [PATCH 0/3] qemu ide updates Jens Axboe
2006-01-04 12:12 ` [Qemu-devel] [PATCH 1/3] ide id updates Jens Axboe
2006-01-04 12:13 ` [Qemu-devel] [PATCH 2/3] ide lba48 support Jens Axboe
@ 2006-01-04 12:16 ` Jens Axboe
2006-01-04 21:07 ` Johannes Schindelin
2 siblings, 1 reply; 12+ messages in thread
From: Jens Axboe @ 2006-01-04 12:16 UTC (permalink / raw)
To: qemu-devel
Subject: [PATCH] Properly support the ide flush cache commands
From: Jens Axboe <axboe@suse.de>
Date: 1136376567 +0100
Add a ->bdrv_sync() hook to the BlockDriver, as it should know how to
sync the cached state with what is on disk. I updated the raw and dmg
drivers, they just need to fsync() the file descriptor.
This is needed for correctness reasons, as the OS expects drive cached
data to be on platter when FLUSH_CACHE has completed successfully. At
least Linux uses this extensively for journalled file systems, if they
are mounted with the barrier= option.
---
block-dmg.c | 11 +++++++++++
block.c | 19 +++++++++++++++++++
block_int.h | 1 +
hw/ide.c | 18 +++++++++++++++---
vl.h | 1 +
5 files changed, 47 insertions(+), 3 deletions(-)
5a8639e3c23e7e312c5213c15dd40a24eee9b416
diff --git a/block-dmg.c b/block-dmg.c
index 5df7235..486a938 100644
--- a/block-dmg.c
+++ b/block-dmg.c
@@ -269,6 +269,12 @@ static int dmg_read(BlockDriverState *bs
return 0;
}
+static int dmg_sync(BlockDriverState *bs)
+{
+ BDRVDMGState *s = bs->opaque;
+ return fsync(s->fd);
+}
+
static void dmg_close(BlockDriverState *bs)
{
BDRVDMGState *s = bs->opaque;
@@ -293,5 +299,10 @@ BlockDriver bdrv_dmg = {
dmg_read,
NULL,
dmg_close,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ dmg_sync,
};
diff --git a/block.c b/block.c
index 6924cee..c37f29a 100644
--- a/block.c
+++ b/block.c
@@ -460,6 +460,14 @@ int bdrv_write(BlockDriverState *bs, int
return bs->drv->bdrv_write(bs, sector_num, buf, nb_sectors);
}
+int bdrv_sync(BlockDriverState *bs)
+{
+ if (bs->drv->bdrv_sync)
+ return bs->drv->bdrv_sync(bs);
+
+ return -EIO;
+}
+
void bdrv_get_geometry(BlockDriverState *bs, int64_t *nb_sectors_ptr)
{
*nb_sectors_ptr = bs->total_sectors;
@@ -752,6 +760,13 @@ static int raw_create(const char *filena
return 0;
}
+static int raw_sync(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ return fsync(s->fd);
+}
+
+
BlockDriver bdrv_raw = {
"raw",
sizeof(BDRVRawState),
@@ -761,6 +776,10 @@ BlockDriver bdrv_raw = {
raw_write,
raw_close,
raw_create,
+ NULL,
+ NULL,
+ NULL,
+ raw_sync,
};
void bdrv_init(void)
diff --git a/block_int.h b/block_int.h
index e303816..30e830a 100644
--- a/block_int.h
+++ b/block_int.h
@@ -40,6 +40,7 @@ struct BlockDriver {
int nb_sectors, int *pnum);
int (*bdrv_set_key)(BlockDriverState *bs, const char *key);
int (*bdrv_make_empty)(BlockDriverState *bs);
+ int (*bdrv_sync)(BlockDriverState *bs);
struct BlockDriver *next;
};
diff --git a/hw/ide.c b/hw/ide.c
index 6a52347..3aadd9e 100644
--- a/hw/ide.c
+++ b/hw/ide.c
@@ -22,6 +22,7 @@
* THE SOFTWARE.
*/
#include "vl.h"
+#include "block_int.h"
/* debug IDE devices */
//#define DEBUG_IDE
@@ -422,7 +423,7 @@ static void put_le16(uint16_t *p, unsign
static void ide_identify(IDEState *s)
{
- uint16_t *p;
+ uint16_t *p, flush_flags = 0;
unsigned int oldsize;
char buf[20];
@@ -431,6 +432,10 @@ static void ide_identify(IDEState *s)
return;
}
+ /* flush_cache_ext is bit 13, flush_cache is bit 12 */
+ if (s->bs->drv->bdrv_sync)
+ flush_flags = (1 << 13) | (1 << 12);
+
memset(s->io_buffer, 0, 512);
p = (uint16_t *)s->io_buffer;
put_le16(p + 0, 0x0040);
@@ -473,11 +478,11 @@ static void ide_identify(IDEState *s)
put_le16(p + 81, 0x16); /* conforms to ata5 */
put_le16(p + 82, (1 << 14));
/* 13=flush_cache_ext,12=flush_cache,10=lba48 */
- put_le16(p + 83, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
+ put_le16(p + 83, (1 << 14) | (1 << 10) | flush_flags);
put_le16(p + 84, (1 << 14));
put_le16(p + 85, (1 << 14));
/* 13=flush_cache_ext,12=flush_cache,10=lba48 */
- put_le16(p + 86, (1 << 14) | (1 << 13) | (1 <<12) | (1 << 10));
+ put_le16(p + 86, (1 << 14) | (1 << 10) | flush_flags);
put_le16(p + 87, (1 << 14));
put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */
put_le16(p + 93, 1 | (1 << 14) | 0x2000);
@@ -1755,6 +1760,13 @@ static void ide_ioport_write(void *opaqu
break;
case WIN_FLUSH_CACHE:
case WIN_FLUSH_CACHE_EXT:
+ if (s->is_cdrom)
+ goto abort_cmd;
+ if (bdrv_sync(s->bs))
+ goto abort_cmd;
+ s->status = READY_STAT;
+ ide_set_irq(s);
+ break;
case WIN_STANDBYNOW1:
case WIN_IDLEIMMEDIATE:
s->status = READY_STAT;
diff --git a/vl.h b/vl.h
index 7a10728..01a1985 100644
--- a/vl.h
+++ b/vl.h
@@ -460,6 +460,7 @@ int bdrv_write(BlockDriverState *bs, int
void bdrv_get_geometry(BlockDriverState *bs, int64_t *nb_sectors_ptr);
int bdrv_commit(BlockDriverState *bs);
void bdrv_set_boot_sector(BlockDriverState *bs, const uint8_t *data, int size);
+int bdrv_sync(BlockDriverState *bs);
#define BDRV_TYPE_HD 0
#define BDRV_TYPE_CDROM 1
--
1.0.GIT
--
Jens Axboe
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [Qemu-devel] [PATCH 3/3] proper support of FLUSH_CACHE and FLUSH_CACHE_EXT
2006-01-04 12:16 ` [Qemu-devel] [PATCH 3/3] proper support of FLUSH_CACHE and FLUSH_CACHE_EXT Jens Axboe
@ 2006-01-04 21:07 ` Johannes Schindelin
2006-01-05 8:14 ` Jens Axboe
0 siblings, 1 reply; 12+ messages in thread
From: Johannes Schindelin @ 2006-01-04 21:07 UTC (permalink / raw)
To: qemu-devel
Hi,
On Wed, 4 Jan 2006, Jens Axboe wrote:
> 1.0.GIT
Using git for QEmu development? Welcome to the club. ;-)
Regarding your patches: as far as I understand them, I like 'em.
Ciao,
Dscho
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Qemu-devel] [PATCH 3/3] proper support of FLUSH_CACHE and FLUSH_CACHE_EXT
2006-01-04 21:07 ` Johannes Schindelin
@ 2006-01-05 8:14 ` Jens Axboe
2006-01-05 9:18 ` Jens Axboe
2006-01-05 13:11 ` Johannes Schindelin
0 siblings, 2 replies; 12+ messages in thread
From: Jens Axboe @ 2006-01-05 8:14 UTC (permalink / raw)
To: qemu-devel
On Wed, Jan 04 2006, Johannes Schindelin wrote:
> Hi,
>
> On Wed, 4 Jan 2006, Jens Axboe wrote:
>
> > 1.0.GIT
>
> Using git for QEmu development? Welcome to the club. ;-)
Yes I just imported the repo into git, cvs isn't really my cup of tea
and it isn't very handy for patch series. git isn't very tailored for
that as well, but at least it allows me to just do a 'format-patch'
against the old master and get the patch series. And with a devel
branch, it's pretty easy to pull the new updates and rebase the devel
branch as needed.
Are you using a persistent git repo for qemu (ie continually importing
new changes)? I've considered setting one up :-)
> Regarding your patches: as far as I understand them, I like 'em.
Thanks!
--
Jens Axboe
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Qemu-devel] [PATCH 3/3] proper support of FLUSH_CACHE and FLUSH_CACHE_EXT
2006-01-05 8:14 ` Jens Axboe
@ 2006-01-05 9:18 ` Jens Axboe
2006-01-05 13:25 ` Johannes Schindelin
2006-01-05 13:11 ` Johannes Schindelin
1 sibling, 1 reply; 12+ messages in thread
From: Jens Axboe @ 2006-01-05 9:18 UTC (permalink / raw)
To: qemu-devel
On Thu, Jan 05 2006, Jens Axboe wrote:
> Are you using a persistent git repo for qemu (ie continually importing
> new changes)? I've considered setting one up :-)
I set up such a gateway, should be updated every night from Fabrices cvs
repository. The web interface is here:
http://brick.kernel.dk/git/?p=qemu.git;a=summary
and you can pull from the following git url:
git://brick.kernel.dk/data/git/cvsdata/qemu
I've added the 'ide' branch, with the patches posted here. If there's an
interest in this (the git repo, not the ide patches :), I can push it to
kernel.org as well.
--
Jens Axboe
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Qemu-devel] [PATCH 3/3] proper support of FLUSH_CACHE and FLUSH_CACHE_EXT
2006-01-05 9:18 ` Jens Axboe
@ 2006-01-05 13:25 ` Johannes Schindelin
2006-01-05 18:07 ` Jens Axboe
0 siblings, 1 reply; 12+ messages in thread
From: Johannes Schindelin @ 2006-01-05 13:25 UTC (permalink / raw)
To: qemu-devel
Hi,
On Thu, 5 Jan 2006, Jens Axboe wrote:
> On Thu, Jan 05 2006, Jens Axboe wrote:
> > Are you using a persistent git repo for qemu (ie continually importing
> > new changes)? I've considered setting one up :-)
>
> I set up such a gateway, should be updated every night from Fabrices cvs
> repository. The web interface is here:
>
> http://brick.kernel.dk/git/?p=qemu.git;a=summary
>
> and you can pull from the following git url:
>
> git://brick.kernel.dk/data/git/cvsdata/qemu
Nice!
> I've added the 'ide' branch, with the patches posted here. If there's an
> interest in this (the git repo, not the ide patches :), I can push it to
> kernel.org as well.
I don't think HPA would be that happy... ;-)
Ciao,
Dscho
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Qemu-devel] [PATCH 3/3] proper support of FLUSH_CACHE and FLUSH_CACHE_EXT
2006-01-05 13:25 ` Johannes Schindelin
@ 2006-01-05 18:07 ` Jens Axboe
0 siblings, 0 replies; 12+ messages in thread
From: Jens Axboe @ 2006-01-05 18:07 UTC (permalink / raw)
To: qemu-devel
On Thu, Jan 05 2006, Johannes Schindelin wrote:
> Hi,
>
> On Thu, 5 Jan 2006, Jens Axboe wrote:
>
> > On Thu, Jan 05 2006, Jens Axboe wrote:
> > > Are you using a persistent git repo for qemu (ie continually importing
> > > new changes)? I've considered setting one up :-)
> >
> > I set up such a gateway, should be updated every night from Fabrices cvs
> > repository. The web interface is here:
> >
> > http://brick.kernel.dk/git/?p=qemu.git;a=summary
> >
> > and you can pull from the following git url:
> >
> > git://brick.kernel.dk/data/git/cvsdata/qemu
>
> Nice!
>
> > I've added the 'ide' branch, with the patches posted here. If there's an
> > interest in this (the git repo, not the ide patches :), I can push it to
> > kernel.org as well.
>
> I don't think HPA would be that happy... ;-)
Well it is Linux related in some way, so I'm sure I could justify it :-)
--
Jens Axboe
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [Qemu-devel] [PATCH 3/3] proper support of FLUSH_CACHE and FLUSH_CACHE_EXT
2006-01-05 8:14 ` Jens Axboe
2006-01-05 9:18 ` Jens Axboe
@ 2006-01-05 13:11 ` Johannes Schindelin
1 sibling, 0 replies; 12+ messages in thread
From: Johannes Schindelin @ 2006-01-05 13:11 UTC (permalink / raw)
To: qemu-devel
Hi,
On Thu, 5 Jan 2006, Jens Axboe wrote:
> Are you using a persistent git repo for qemu (ie continually importing
> new changes)? I've considered setting one up :-)
Yes, I have a persistent repo. But I don't know where to put it (Takes up
about 18MB ATM).
I also have several branches where I track some side development, like the
busmouse support, and Paul's Qop system.
Ciao,
Dscho
^ permalink raw reply [flat|nested] 12+ messages in thread