Re: [Qemu-devel] [PATCH v4 2/8] ahci: MSI capability should be at 0x80, not 0x50.
On 09/17/2014 12:42 PM, Michael S. Tsirkin wrote: On Wed, Sep 17, 2014 at 05:54:28PM +0200, Jan Kiszka wrote: On 2014-08-21 19:44, John Snow wrote: In the Intel ICH9 data sheet, the MSI capability offset in the PCI configuration space for ICH9 AHCI devices is specified to be 0x80. Further, the PCI capability pointer should always point to 0x80 in ICH9 devices, despite the fact that AHCI 1.3 specifies that it should be pointing to PMCAP (Which in this instance would be 0x70) to maintain adherence to the Intel data sheet specifications and real observed behavior. Signed-off-by: John Snow --- hw/ide/ich.c | 7 ++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/ide/ich.c b/hw/ide/ich.c index a2f1639..8eb77a1 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -71,6 +71,7 @@ #include #include +#define ICH9_MSI_CAP_OFFSET 0x80 #define ICH9_SATA_CAP_OFFSET0xA8 #define ICH9_IDP_BAR4 @@ -115,7 +116,6 @@ static int pci_ich9_ahci_init(PCIDevice *dev) /* XXX Software should program this register */ dev->config[0x90] = 1 << 6; /* Address Map Register - AHCI mode */ -msi_init(dev, 0x50, 1, true, false); d->ahci.irq = pci_allocate_irq(dev); pci_register_bar(dev, ICH9_IDP_BAR, PCI_BASE_ADDRESS_SPACE_IO, @@ -135,6 +135,11 @@ static int pci_ich9_ahci_init(PCIDevice *dev) (ICH9_IDP_BAR + 0x4) | (ICH9_IDP_INDEX_LOG2 << 4)); d->ahci.idp_offset = ICH9_IDP_INDEX; +/* Although the AHCI 1.3 specification states that the first capability + * should be PMCAP, the Intel ICH9 data sheet specifies that the ICH9 + * AHCI device puts the MSI capability first, pointing to 0x80. */ +msi_init(dev, ICH9_MSI_CAP_OFFSET, 1, true, false); + return 0; } I did this for HDA recently, and it became clear that this requires a compat switch to expose the old, broken layout to older guests. Otherwise you can't migrate from them. Jan Absolutely but ahci still disables migration :) I was halfway to preparing a v5, but you're right ... We've disabled AHCI migration until we iron out the most egregious kinks. -- Siemens AG, Corporate Technology, CT RTC ITP SES-DE Corporate Competence Center Embedded Linux -- —js
[Qemu-devel] [RFC v2 0/3] Q35/AHCI -cdrom/-hda desugaring
This is an extremely rough/quick sketch of a -cdrom/-hda desugaring fix for Q35/AHCI. Before I spent any time on it, I wanted feedback from Markus or anyone else who had concerns about how this problem would get fixed. This is, then, rough approach #2. Highlights: (1) Add a board property (instead of a HBA property, sigh) that defines how we should map (index, (bus,unit)). (2) Modify drive_new to accept the MachineClass instead of the default interface type. This does not affect how default drives get added, because any over-rides to the "default type" get handled in options, so while it appears we have removed the type of default drives, we have not. (3) Create helpers for AHCI to assist the Q35 board in populating the AHCI device with the IDE drives. (4) Create a helper to whine at us for oversights and help bug reporters give us more meaningful information. John Snow (3): blockdev: Add function to search for orphaned drives Add units-per-idebus property ahci: implement -cdrom and -hd[a-d] blockdev.c| 29 +++-- device-hotplug.c | 2 +- hw/i386/pc_q35.c | 6 +- hw/ide/ahci.c | 31 +++ hw/ide/ahci.h | 3 +++ include/hw/boards.h | 3 ++- include/sysemu/blockdev.h | 3 ++- vl.c | 24 8 files changed, 87 insertions(+), 14 deletions(-) -- 1.9.3
[Qemu-devel] [RFC v2 2/3] Add units-per-idebus property
Signed-off-by: John Snow --- blockdev.c| 10 -- device-hotplug.c | 2 +- hw/i386/pc_q35.c | 3 ++- include/hw/boards.h | 3 ++- include/sysemu/blockdev.h | 2 +- vl.c | 19 +++ 6 files changed, 25 insertions(+), 14 deletions(-) diff --git a/blockdev.c b/blockdev.c index 5e7c93a..6c524b7 100644 --- a/blockdev.c +++ b/blockdev.c @@ -45,6 +45,7 @@ #include "qmp-commands.h" #include "trace.h" #include "sysemu/arch_init.h" +#include "hw/boards.h" static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives); @@ -643,7 +644,7 @@ QemuOptsList qemu_legacy_drive_opts = { }, }; -DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) +DriveInfo *drive_new(QemuOpts *all_opts, MachineClass *mc) { const char *value; DriveInfo *dinfo = NULL; @@ -651,6 +652,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) QemuOpts *legacy_opts; DriveMediaType media = MEDIA_DISK; BlockInterfaceType type; +BlockInterfaceType block_default_type = mc->block_default_type; int cyls, heads, secs, translation; int max_devs, bus_id, unit_id, index; const char *devaddr; @@ -828,7 +830,11 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) unit_id = qemu_opt_get_number(legacy_opts, "unit", -1); index = qemu_opt_get_number(legacy_opts, "index", -1); -max_devs = if_max_devs[type]; +if (type == IF_IDE && mc->units_per_idebus) { +max_devs = mc->units_per_idebus; +} else { +max_devs = if_max_devs[type]; +} if (index != -1) { if (bus_id != 0 || unit_id != -1) { diff --git a/device-hotplug.c b/device-hotplug.c index e6a1ffb..857ac53 100644 --- a/device-hotplug.c +++ b/device-hotplug.c @@ -40,7 +40,7 @@ DriveInfo *add_init_drive(const char *optstr) return NULL; mc = MACHINE_GET_CLASS(current_machine); -dinfo = drive_new(opts, mc->block_default_type); +dinfo = drive_new(opts, mc); if (!dinfo) { qemu_opts_del(opts); return NULL; diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index d4a907c..fd26fe1 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -348,7 +348,8 @@ static void pc_q35_init_1_4(MachineState *machine) #define PC_Q35_2_2_MACHINE_OPTIONS \ PC_Q35_MACHINE_OPTIONS, \ -.default_machine_opts = "firmware=bios-256k.bin" +.default_machine_opts = "firmware=bios-256k.bin", \ +.units_per_idebus = 1 static QEMUMachine pc_q35_machine_v2_2 = { PC_Q35_2_2_MACHINE_OPTIONS, diff --git a/include/hw/boards.h b/include/hw/boards.h index dfb6718..73e656f 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -37,6 +37,7 @@ struct QEMUMachine { no_cdrom:1, no_sdcard:1; int is_default; +unsigned short units_per_idebus; const char *default_machine_opts; const char *default_boot_order; GlobalProperty *compat_props; @@ -95,11 +96,11 @@ struct MachineClass { no_cdrom:1, no_sdcard:1; int is_default; +unsigned short units_per_idebus; const char *default_machine_opts; const char *default_boot_order; GlobalProperty *compat_props; const char *hw_version; - HotplugHandler *(*get_hotplug_handler)(MachineState *machine, DeviceState *dev); }; diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 25d52d2..f7de0a0 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -55,7 +55,7 @@ DriveInfo *drive_get_by_blockdev(BlockDriverState *bs); QemuOpts *drive_def(const char *optstr); QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, const char *optstr); -DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type); +DriveInfo *drive_new(QemuOpts *arg, MachineClass *mc); void drive_del(DriveInfo *dinfo); /* device-hotplug */ diff --git a/vl.c b/vl.c index e095bcd..8359469 100644 --- a/vl.c +++ b/vl.c @@ -1151,9 +1151,9 @@ static int cleanup_add_fd(QemuOpts *opts, void *opaque) static int drive_init_func(QemuOpts *opts, void *opaque) { -BlockInterfaceType *block_default_type = opaque; +MachineClass *mc = opaque; -return drive_new(opts, *block_default_type) == NULL; +return drive_new(opts, mc) == NULL; } static int drive_enable_snapshot(QemuOpts *opts, void *opaque) @@ -1165,7 +1165,7 @@ static int drive_enable_snapshot(QemuOpts *opts, void *opaque) } static void default_drive(int enable, int snapshot, BlockInterfaceType type, - int index, const char *optstr) + int index, const char *optstr, MachineClass *mc
[Qemu-devel] [RFC v2 3/3] ahci: implement -cdrom and -hd[a-d]
Signed-off-by: John Snow --- hw/i386/pc_q35.c | 3 +++ hw/ide/ahci.c| 31 +++ hw/ide/ahci.h| 3 +++ 3 files changed, 37 insertions(+) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index fd26fe1..0f33696 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -86,6 +86,7 @@ static void pc_q35_init(MachineState *machine) DeviceState *icc_bridge; PcGuestInfo *guest_info; ram_addr_t lowmem; +DriveInfo *hd[MAX_SATA_PORTS]; /* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory * and 256 Mbytes for PCI Express Enhanced Configuration Access Mapping @@ -253,6 +254,8 @@ static void pc_q35_init(MachineState *machine) true, "ich9-ahci"); idebus[0] = qdev_get_child_bus(&ahci->qdev, "ide.0"); idebus[1] = qdev_get_child_bus(&ahci->qdev, "ide.1"); +ahci_drive_get(ahci, hd); +ahci_ide_create_devs(ahci, hd); if (usb_enabled(false)) { /* Should we create 6 UHCI according to ich9 spec? */ diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index ba69de3..ae28de4 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1402,3 +1402,34 @@ static void sysbus_ahci_register_types(void) } type_init(sysbus_ahci_register_types) + +void ahci_drive_get(PCIDevice *dev, DriveInfo **tab) +{ +AHCIPCIState *d = ICH_AHCI(dev); +AHCIState *ahci = &d->ahci; +unsigned i; + +if ((i = drive_get_max_bus(IF_IDE)) >= ahci->ports) { +fprintf(stderr, "AHCI: Too many IDE buses defined for AHCI (%d > %d)\n", +i, ahci->ports - 1); +} + +for (i = 0; i < ahci->ports; ++i) { +tab[i] = drive_get_by_index(IF_IDE, i); +} +} + +void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **tab) +{ +AHCIPCIState *d = ICH_AHCI(dev); +AHCIState *ahci = &d->ahci; +unsigned i; + +for (i = 0; i < ahci->ports; i++) { +if (tab[i] == NULL) { +continue; +} +ide_create_drive(&ahci->dev[i].port, 0, tab[i]); +} + +} diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h index 1543df7..06a18de 100644 --- a/hw/ide/ahci.h +++ b/hw/ide/ahci.h @@ -332,4 +332,7 @@ void ahci_uninit(AHCIState *s); void ahci_reset(AHCIState *s); +void ahci_drive_get(PCIDevice *dev, DriveInfo **tab); +void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **tab); + #endif /* HW_IDE_AHCI_H */ -- 1.9.3
[Qemu-devel] [RFC v2 1/3] blockdev: Add function to search for orphaned drives
Signed-off-by: John Snow --- blockdev.c| 19 +++ include/sysemu/blockdev.h | 1 + vl.c | 5 + 3 files changed, 25 insertions(+) diff --git a/blockdev.c b/blockdev.c index b361fbb..5e7c93a 100644 --- a/blockdev.c +++ b/blockdev.c @@ -166,6 +166,25 @@ DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit) return NULL; } +DriveInfo *drive_check_orphaned(void) +{ +DriveInfo *dinfo; +DriveInfo *ret = NULL; + +QTAILQ_FOREACH(dinfo, &drives, next) { +/* If dev is NULL, it has no device attached. + * If drv is non-NULL, it has a file attached. + * If both conditions are true, it is possibly an oversight. */ +if ((dinfo->bdrv->dev == NULL) && (dinfo->bdrv->drv != NULL)) { +fprintf(stderr, "Orphaned drive: id=%s,if=%s,file=%s\n", +dinfo->id, if_name[dinfo->type], dinfo->bdrv->filename); +ret = dinfo; +} +} + +return ret; +} + DriveInfo *drive_get_by_index(BlockInterfaceType type, int index) { return drive_get(type, diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 23a5d10..25d52d2 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -46,6 +46,7 @@ struct DriveInfo { }; DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); +DriveInfo *drive_check_orphaned(void); DriveInfo *drive_get_by_index(BlockInterfaceType type, int index); int drive_get_max_bus(BlockInterfaceType type); DriveInfo *drive_get_next(BlockInterfaceType type); diff --git a/vl.c b/vl.c index 5db0d08..e095bcd 100644 --- a/vl.c +++ b/vl.c @@ -4457,6 +4457,11 @@ int main(int argc, char **argv, char **envp) if (qemu_opts_foreach(qemu_find_opts("device"), device_init_func, NULL, 1) != 0) exit(1); +/* anybody left over? */ +if (drive_check_orphaned()) { +fprintf(stderr, "Warning: found drives without a backing device.\n"); +} + net_check_clients(); ds = init_displaystate(); -- 1.9.3
[Qemu-devel] [PATCH 02/15] qtest/ahci: Add port_select helper
This helper identifies which port of the AHCI HBA has a device we may run tests on. Signed-off-by: John Snow --- tests/ahci-test.c | 46 +- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index d845635..5739063 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -1235,6 +1235,33 @@ static void ahci_test_port_spec(AHCIState *ahci, uint8_t port) } /** + * Pick the first implemented and running port + */ +static unsigned port_select(AHCIState *ahci) +{ +uint32_t ports, reg; +unsigned i; + +ports = AHCI_RREG(AHCI_PI); +for (i = 0; i < 32; ports >>= 1, ++i) { +if (ports == 0) { +i = 32; +} + +if (!(ports & 0x01)) { +continue; +} + +reg = PX_RREG(i, AHCI_PX_CMD); +if (BITSET(reg, AHCI_PX_CMD_ST)) { +break; +} +} +g_assert(i < 32); +return i; +} + +/** * Utilizing an initialized AHCI HBA, issue an IDENTIFY command to the first * device we see, then read and check the response. */ @@ -1245,7 +1272,7 @@ static void ahci_test_identify(AHCIState *ahci) RegH2DFIS fis; AHCICommand cmd; PRD prd; -uint32_t ports, reg, table, data_ptr; +uint32_t reg, table, data_ptr; uint16_t buff[256]; unsigned i; int rc; @@ -1267,22 +1294,7 @@ static void ahci_test_identify(AHCIState *ahci) */ /* Pick the first implemented and running port */ -ports = AHCI_RREG(AHCI_PI); -for (i = 0; i < 32; ports >>= 1, ++i) { -if (ports == 0) { -i = 32; -} - -if (!(ports & 0x01)) { -continue; -} - -reg = PX_RREG(i, AHCI_PX_CMD); -if (BITSET(reg, AHCI_PX_CMD_ST)) { -break; -} -} -g_assert_cmphex(i, <, 32); +i = port_select(ahci); g_test_message("Selected port %u for test", i); /* Clear out this port's interrupts (ignore the init register d2h fis) */ -- 1.9.3
[Qemu-devel] [PATCH 05/15] qtest/ahci: Add build cmd table helper
build_cmd_table is a helper routine to build the command table, which consists of the Command FIS and the Physical Region Descriptor Table. Signed-off-by: John Snow --- tests/ahci-test.c | 113 +++--- 1 file changed, 74 insertions(+), 39 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 7a62eef..f07495f 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -1350,6 +1350,67 @@ static unsigned pick_cmd(AHCIState *ahci, uint8_t px) g_assert_not_reached(); } +static inline unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd) +{ +/* Each PRD can describe up to 4MiB */ +g_assert_cmphex(bytes_per_prd, <=, 4096 * 1024); +g_assert_cmphex(bytes_per_prd & 0x01, ==, 0x00); +return (bytes + bytes_per_prd - 1) / bytes_per_prd; +} + +static uint64_t build_cmd_table(AHCIState *ahci, uint8_t px, size_t sz, +uint8_t cmd, uint64_t buffer) +{ +PRD prd; +RegH2DFIS fis; + +const unsigned bytes_per_prd = 4096; +unsigned prdtl = size_to_prdtl(sz, bytes_per_prd); +unsigned table_size = CMD_TBL_SIZ(prdtl); +uint64_t table_ptr; +unsigned remaining, i; + +/* Create a Command Table buffer. 0x80 for the bulk, + * plus enough room for `prdtl` entries. */ +table_ptr = guest_alloc(guest_malloc, table_size); +g_assert(table_ptr); +/* AHCI 1.3 spec states that it must be 0x80 aligned. */ +ASSERT_BIT_CLEAR(table_ptr, 0x7F); + +/* Construct our Command FIS */ +memset(&fis, 0x00, sizeof(fis)); +fis.fis_type = 0x27; /* Register Host-to-Device FIS */ +fis.flags = 0x80;/* Command FIS. ('C bit') */ +fis.command = cmd; /* e.g, 0xEC : Identify */ +fis.count = cpu_to_le16(sz / 512); + +/* Construct the PRDs. */ +remaining = sz; +for (i = 0; i < prdtl; ++i) { +prd.dba = cpu_to_le64(buffer + (bytes_per_prd * i)); +prd.res = 0; +if (remaining > bytes_per_prd) { +/* Note that byte count is 0-based. */ +prd.dbc = cpu_to_le32(bytes_per_prd - 1); +remaining -= bytes_per_prd; +} else { +/* Again, dbc is 0-based. */ +prd.dbc = cpu_to_le32(remaining - 1); +remaining = 0; +} +prd.dbc |= cpu_to_le32(0x8000); /* Request DPS Interrupt */ + +/* Commit the PRD entry to the Command Table */ +memwrite(table_ptr + 0x80 + (i * sizeof(PRD)), + &prd, sizeof(PRD)); +} + +/* Commit the Command FIS to the Command Table */ +memwrite(table_ptr, &fis, sizeof(fis)); + +return table_ptr; +} + /** * Utilizing an initialized AHCI HBA, issue an IDENTIFY command to the first * device we see, then read and check the response. @@ -1358,9 +1419,7 @@ static void ahci_test_identify(AHCIState *ahci) { RegD2HFIS *d2h = g_malloc0(0x20); RegD2HFIS *pio = g_malloc0(0x20); -RegH2DFIS fis; AHCICommand cmd; -PRD prd; uint32_t reg, data_ptr; uint16_t buff[256]; unsigned i; @@ -1371,17 +1430,18 @@ static void ahci_test_identify(AHCIState *ahci) g_assert(ahci != NULL); /* We need to: - * (1) Create a Command Table Buffer and update the Command List Slot #0 - * to point to this buffer. - * (2) Construct an FIS host-to-device command structure, and write it to - * the top of the command table buffer. - * (3) Create a data buffer for the IDENTIFY response to be sent to + * (1) Create a data buffer for the IDENTIFY response to be sent to, + * (2) Create a Command Table Buffer + * (3) Construct an FIS host-to-device command structure, and write it to + * the top of the Command Table Buffer. * (4) Create a Physical Region Descriptor that points to the data buffer, - * and write it to the bottom (offset 0x80) of the command table. - * (5) Now, PxCLB points to the command list, command 0 points to + * and write it to the bottom (offset 0x80) of the Command Table. + * (5) Obtain a Command List slot, and update this header to point to + * the Command Table we built above. + * (6) Now, PxCLB points to the command list, command 0 points to * our table, and our table contains an FIS instruction and a * PRD that points to our rx buffer. - * (6) We inform the HBA via PxCI that there is a command ready in slot #0. + * (7) We inform the HBA via PxCI that there is a command ready in slot #0. */ /* Pick the first implemented and running port */ @@ -1391,16 +1451,13 @@ static void ahci_test_identify(AHCIState *ahci) /* Clear out the FIS Receive area and any pending interrupts. */ port_clear(ahci, i); -/* Create a Command Table buffer. 0x80 is the smallest with a PRDTL of 0. */ -/* We need at least one PRD, so round up to the nearest 0x80 multiple. */ -table_ptr = gues
[Qemu-devel] [PATCH 04/15] qtest/ahci: Add command header helpers
Add command_header_set, command_header_get, command_destroy and cmd_pick to help quickly manage the command header information in the AHCI device. command_header_set and get will store or retrieve an AHCI command header, respectively. cmd_pick chooses the first available but least recently used command slot to allow us to cycle through the available command slots. command_destroy obliterates all information contained within a given slot's command header, and frees its associated command table, but not its DMA buffer! Signed-off-by: John Snow --- tests/ahci-test.c | 132 ++ 1 file changed, 104 insertions(+), 28 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 77e3963..7a62eef 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -257,6 +257,9 @@ typedef struct AHCIPortState { uint64_t fb; uint64_t clb; +uint64_t ctba[32]; +uint16_t prdtl[32]; +uint8_t next; /** Next Command Slot to Use **/ } AHCIPortState; typedef struct AHCIState { @@ -342,8 +345,7 @@ typedef struct AHCICommand { uint8_t b2; uint16_t prdtl; /* Phys Region Desc. Table Length */ uint32_t prdbc; /* Phys Region Desc. Byte Count */ -uint32_t ctba; /* Command Table Descriptor Base Address */ -uint32_t ctbau; /*'' Upper */ +uint64_t ctba; /* Command Table Descriptor Base Address */ uint32_t res[4]; } __attribute__((__packed__)) AHCICommand; @@ -352,11 +354,10 @@ typedef struct AHCICommand { * struct ahci_command. */ typedef struct PRD { -uint32_t dba; /* Data Base Address */ -uint32_t dbau; /* Data Base Address Upper */ +uint64_t dba; /* Data Base Address */ uint32_t res; /* Reserved */ uint32_t dbc; /* Data Byte Count (0-indexed) & Interrupt Flag (bit 2^31) */ -} PRD; +} __attribute__((__packed__)) PRD; /*** Globals ***/ static QGuestAllocator *guest_malloc; @@ -1277,6 +1278,78 @@ static void port_clear(AHCIState *ahci, uint8_t px) qmemset(ahci->port[px].fb, 0x00, 0x100); } +/* Get the #cx'th command of port #px. */ +static void get_command_header(AHCIState *ahci, uint8_t px, + uint8_t cx, AHCICommand *cmd) +{ +uint64_t ba = ahci->port[px].clb; +ba += cx * sizeof(AHCICommand); +memread(ba, cmd, sizeof(AHCICommand)); + +cmd->prdtl = le16_to_cpu(cmd->prdtl); +cmd->prdbc = le32_to_cpu(cmd->prdbc); +cmd->ctba = le64_to_cpu(cmd->ctba); +} + +/* Set the #cx'th command of port #px. */ +static void set_command_header(AHCIState *ahci, uint8_t px, + uint8_t cx, AHCICommand *cmd) +{ +uint64_t ba = ahci->port[px].clb; +ba += cx * sizeof(AHCICommand); + +cmd->prdtl = cpu_to_le16(cmd->prdtl); +cmd->prdbc = cpu_to_le32(cmd->prdbc); +cmd->ctba = cpu_to_le64(cmd->ctba); + +memwrite(ba, cmd, sizeof(AHCICommand)); +} + +static void destroy_command(AHCIState *ahci, uint8_t px, uint8_t cx) +{ +AHCICommand cmd; + +/* Obtain the Nth Command Header */ +get_command_header(ahci, px, cx, &cmd); +if (cmd.ctba == 0) { +/* No address in it, so just return -- it's empty. */ +goto tidy; +} + +/* Free the Table */ +guest_free(guest_malloc, cmd.ctba); + + tidy: +/* NULL the header. */ +memset(&cmd, 0x00, sizeof(cmd)); +set_command_header(ahci, px, cx, &cmd); +ahci->port[px].ctba[cx] = 0; +ahci->port[px].prdtl[cx] = 0; +} + +static unsigned pick_cmd(AHCIState *ahci, uint8_t px) +{ +unsigned i; +unsigned j; +uint32_t reg; + +reg = PX_RREG(px, AHCI_PX_CI); + +/* Pick the least recently used command slot that's available */ +for (i = 0; i < 32; ++i) { +j = ((ahci->port[px].next + i) % 32); +if (reg & (1 << j)) { +continue; +} +destroy_command(ahci, px, i); +ahci->port[px].next = (j + 1) % 32; +return j; +} + +g_test_message("All command slots were busy."); +g_assert_not_reached(); +} + /** * Utilizing an initialized AHCI HBA, issue an IDENTIFY command to the first * device we see, then read and check the response. @@ -1288,10 +1361,12 @@ static void ahci_test_identify(AHCIState *ahci) RegH2DFIS fis; AHCICommand cmd; PRD prd; -uint32_t reg, table, data_ptr; +uint32_t reg, data_ptr; uint16_t buff[256]; unsigned i; int rc; +uint8_t cx; +uint64_t table_ptr; g_assert(ahci != NULL); @@ -1318,27 +1393,27 @@ static void ahci_test_identify(AHCIState *ahci) /* Create a Command Table buffer. 0x80 is the smallest with a PRDTL of 0. */ /* We need at least one PRD, so round up to the nearest 0x80 multiple. */ -table = guest_alloc(guest_malloc, CMD_TBL_SIZ(1)); -g_assert(table); -ASSERT_
[Qemu-devel] [PATCH 12/15] qtest/ahci: Enforce zero-leaks for guest mem usage
The purpose of this patch is to aid in debugging the new malloc-pc interface for guest memory, by enabling the enforcing of no guest memory blocks in use at exit time. Signed-off-by: John Snow --- tests/ahci-test.c | 27 --- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index dcc0748..3f92618 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -436,6 +436,7 @@ static void ahci_test_msicap(AHCIState *ahci, uint8_t offset); static void ahci_test_pmcap(AHCIState *ahci, uint8_t offset); static void get_command_header(AHCIState *ahci, uint8_t px, uint8_t cx, AHCICommand *cmd); +static void destroy_command(AHCIState *ahci, uint8_t px, uint8_t cx); /*** Utilities ***/ @@ -506,7 +507,9 @@ static void qtest_boot(const char *cmdline_fmt, ...) qtest_start(cmdline); qtest_irq_intercept_in(global_qtest, "ioapic"); -guest_malloc = pc_alloc_init(); +/* Enforce 0 leaks. */ +guest_malloc = pc_alloc_init_flags(PC_ALLOC_LEAK_ASSERT | + PC_ALLOC_PARANOID); g_free(cmdline); } @@ -516,8 +519,10 @@ static void qtest_boot(const char *cmdline_fmt, ...) */ static void qtest_shutdown(void) { -g_free(guest_malloc); -guest_malloc = NULL; +if (guest_malloc) { +pc_alloc_uninit(guest_malloc); +guest_malloc = NULL; +} qtest_end(); } @@ -547,6 +552,20 @@ static AHCIState *ahci_boot(void) */ static void ahci_shutdown(AHCIState *ahci) { +uint8_t px, cx; + +for (px = 0; px < 32; ++px) { +if (ahci->port[px].fb) { +guest_free(guest_malloc, ahci->port[px].fb); +} +if (ahci->port[px].clb) { +for (cx = 0; cx < 32; cx++) { +destroy_command(ahci, px, cx); +} +guest_free(guest_malloc, ahci->port[px].clb); +} +} + free_ahci_device(ahci->dev); g_free(ahci); qtest_shutdown(); @@ -1676,6 +1695,8 @@ static void ahci_test_identify(AHCIState *ahci) string_bswap16(&buff[23], 8); rc = memcmp(&buff[23], "version ", 8); g_assert_cmphex(rc, ==, 0); + +guest_free(guest_malloc, data_ptr); } /**/ -- 1.9.3
[Qemu-devel] [PATCH 03/15] qtest/ahci: Add port_clear helper
Add a helper that assists in clearing out potentially old error and FIS information from an AHCI port's data structures. This ensures we always start with a blank slate for interrupt and FIS receipt information. Signed-off-by: John Snow --- tests/ahci-test.c | 26 ++ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 5739063..77e3963 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -1262,6 +1262,22 @@ static unsigned port_select(AHCIState *ahci) } /** + * Clear a port's interrupts and status information prior to a test. + */ +static void port_clear(AHCIState *ahci, uint8_t px) +{ +uint32_t reg; + +/* Clear out this port's interrupts (ignore the init register d2h fis) */ +reg = PX_RREG(px, AHCI_PX_IS); +PX_WREG(px, AHCI_PX_IS, reg); +g_assert_cmphex(PX_RREG(px, AHCI_PX_IS), ==, 0); + +/* Wipe the FIS-Recieve Buffer */ +qmemset(ahci->port[px].fb, 0x00, 0x100); +} + +/** * Utilizing an initialized AHCI HBA, issue an IDENTIFY command to the first * device we see, then read and check the response. */ @@ -1297,14 +1313,8 @@ static void ahci_test_identify(AHCIState *ahci) i = port_select(ahci); g_test_message("Selected port %u for test", i); -/* Clear out this port's interrupts (ignore the init register d2h fis) */ -reg = PX_RREG(i, AHCI_PX_IS); -PX_WREG(i, AHCI_PX_IS, reg); -g_assert_cmphex(PX_RREG(i, AHCI_PX_IS), ==, 0); - -/* Wipe the FIS-Recieve Buffer */ -g_assert_cmphex(ahci->port[i].fb, !=, 0); -qmemset(ahci->port[i].fb, 0x00, 0x100); +/* Clear out the FIS Receive area and any pending interrupts. */ +port_clear(ahci, i); /* Create a Command Table buffer. 0x80 is the smallest with a PRDTL of 0. */ /* We need at least one PRD, so round up to the nearest 0x80 multiple. */ -- 1.9.3
[Qemu-devel] [PATCH 06/15] qtest/ahci: Add link_cmd_slot helper
link_cmd_slot creates the command header, by setting a pointer to the command table as well as adjusting other metadata, like DMA direction and PRDT length. It effectively links a command, via its table, to the indicated command slot. Signed-off-by: John Snow --- tests/ahci-test.c | 72 +++ 1 file changed, 57 insertions(+), 15 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index f07495f..3e65435 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -252,11 +252,26 @@ #define AHCI_VERSION_1_2 (0x00010200) #define AHCI_VERSION_1_3 (0x00010300) +/* AHCI COMMAND HEADER FLAGS */ + +#define CMDH_CFL(0x1F) +#define CMDH_ATAPI (0x20) +#define CMDH_READ (0x00) /* Fake. */ +#define CMDH_WRITE (0x40) +#define CMDH_PREFETCH (0x80) +#define CMDH_RESET (0x100) +#define CMDH_BIST (0x200) +#define CMDH_CLR_BSY (0x400) +#define CMDH_RES (0x800) +#define CMDH_PMP (0xF000) + /*** Structures ***/ typedef struct AHCIPortState { uint64_t fb; uint64_t clb; +uint64_t _ctba; +uint16_t _prdtl; uint64_t ctba[32]; uint16_t prdtl[32]; uint8_t next; /** Next Command Slot to Use **/ @@ -341,8 +356,7 @@ typedef struct RegH2DFIS { * The command list contains between 1-32 of these structures. */ typedef struct AHCICommand { -uint8_t b1; -uint8_t b2; +uint16_t flags; /* Cmd-Fis-Len, PMP#, and flags. */ uint16_t prdtl; /* Phys Region Desc. Table Length */ uint32_t prdbc; /* Phys Region Desc. Byte Count */ uint64_t ctba; /* Command Table Descriptor Base Address */ @@ -1286,6 +1300,7 @@ static void get_command_header(AHCIState *ahci, uint8_t px, ba += cx * sizeof(AHCICommand); memread(ba, cmd, sizeof(AHCICommand)); +cmd->flags = le16_to_cpu(cmd->flags); cmd->prdtl = le16_to_cpu(cmd->prdtl); cmd->prdbc = le32_to_cpu(cmd->prdbc); cmd->ctba = le64_to_cpu(cmd->ctba); @@ -1298,6 +1313,7 @@ static void set_command_header(AHCIState *ahci, uint8_t px, uint64_t ba = ahci->port[px].clb; ba += cx * sizeof(AHCICommand); +cmd->flags = cpu_to_le16(cmd->flags); cmd->prdtl = cpu_to_le16(cmd->prdtl); cmd->prdbc = cpu_to_le32(cmd->prdbc); cmd->ctba = cpu_to_le64(cmd->ctba); @@ -1408,10 +1424,47 @@ static uint64_t build_cmd_table(AHCIState *ahci, uint8_t px, size_t sz, /* Commit the Command FIS to the Command Table */ memwrite(table_ptr, &fis, sizeof(fis)); +/* Bookmark these values. */ +ahci->port[px]._prdtl = prdtl; +ahci->port[px]._ctba = table_ptr; + return table_ptr; } /** + * Given a pointer to a Command Table, create a Command Header for it and write + * it to a free slot in the Command List buffer. + * @return The command slot number we picked. + */ +static unsigned link_cmd_slot(AHCIState *ahci, uint8_t px, uint64_t table_ptr, + uint8_t cmdh_flags) +{ +AHCICommand cmd = { .flags = 0 }; +uint8_t cx; + +cx = pick_cmd(ahci, px); + +/* Construct our Command Header */ +cmd.flags = 5; +cmd.flags |= CMDH_CLR_BSY; +cmd.flags |= cmdh_flags; +cmd.prdtl = (table_ptr == ahci->port[px]._ctba) ? ahci->port[px]._prdtl : 1; +cmd.prdbc = 0; +cmd.ctba = table_ptr; + +/* Commit Command #cx, pointing to the Table, to the Command List Buffer. */ +set_command_header(ahci, px, cx, &cmd); + +/* For convenience only. */ +ahci->port[px].ctba[cx] = table_ptr; +ahci->port[px].prdtl[cx] = cmd.prdtl; +ahci->port[px]._ctba = 0; +ahci->port[px]._prdtl = 0; + +return cx; +} + +/** * Utilizing an initialized AHCI HBA, issue an IDENTIFY command to the first * device we see, then read and check the response. */ @@ -1458,19 +1511,8 @@ static void ahci_test_identify(AHCIState *ahci) /* Construct the Command Table (FIS and PRDT) */ table_ptr = build_cmd_table(ahci, i, 512, 0xEC, data_ptr); -/* pick a command slot (should be 0!) */ -cx = pick_cmd(ahci, i); - -/* Construct our Command Header (set_command_header handles endianness.) */ -memset(&cmd, 0x00, sizeof(cmd)); -cmd.b1 = 5;/* reg_h2d_fis is 5 double-words long */ -cmd.b2 = 0x04; /* clear PxTFD.STS.BSY when done */ -cmd.prdtl = 1; /* One PRD table entry. */ -cmd.prdbc = 0; -cmd.ctba = table_ptr; - -/* Commit Command #cx, pointing to the Table, to the Command List Buffer. */ -set_command_header(ahci, i, cx, &cmd); +/* Pick a command slot and link it to the command table we've built */ +cx = link_cmd_slot(ahci, i, table_ptr, CMDH_READ); /* Everything is in place, but we haven't given the go-ahead yet, * so we should find that there are no pending interrupts yet. */ -- 1.9.3
[Qemu-devel] [PATCH 10/15] qtest/ahci: Add port_check_nonbusy helper
A simple helper that asserts a given port is not busy processing any commands via the TFD, Command Issue and SACT registers. Signed-off-by: John Snow --- tests/ahci-test.c | 19 +++ 1 file changed, 19 insertions(+) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 315e6e9..b7bfd86 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -1328,6 +1328,24 @@ static void port_check_interrupts(AHCIState *ahci, uint8_t px, g_assert_cmphex(PX_RREG(px, AHCI_PX_IS), ==, 0); } +static void port_check_nonbusy(AHCIState *ahci, uint8_t px, uint8_t cx) +{ +uint32_t reg; + +/* Assert that the command slot is no longer busy (NCQ) */ +reg = PX_RREG(px, AHCI_PX_SACT); +ASSERT_BIT_CLEAR(reg, (1 << cx)); + +/* Non-NCQ */ +reg = PX_RREG(px, AHCI_PX_CI); +ASSERT_BIT_CLEAR(reg, (1 << cx)); + +/* And assert that we are generally not busy. */ +reg = PX_RREG(px, AHCI_PX_TFD); +ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_BSY); +ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_DRQ); +} + /* Get the #cx'th command of port #px. */ static void get_command_header(AHCIState *ahci, uint8_t px, uint8_t cx, AHCICommand *cmd) @@ -1580,6 +1598,7 @@ static void ahci_test_identify(AHCIState *ahci) port_check_error(ahci, i); /* BUG: we expect AHCI_PX_IS_DPS to be set. */ port_check_interrupts(ahci, i, AHCI_PX_IS_DHRS | AHCI_PX_IS_PSS); +port_check_nonbusy(ahci, i, cx); /* Investigate the CMD, assert that we read 512 bytes */ get_command_header(ahci, i, cx, &cmd); -- 1.9.3
[Qemu-devel] [PATCH 15/15] qtest/ahci: Don't use a magic constant for buffer size
Fix to correct the use of "512" as a magic constant and define it as the buffer size for the operation instead. Signed-off-by: John Snow --- tests/ahci-test.c | 11 ++- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index e3091b9..9d16d0c 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -1651,6 +1651,7 @@ static void ahci_test_identify(AHCIState *ahci) int rc; uint8_t cx; uint64_t table_ptr; +const size_t buffsize = 512; g_assert(ahci != NULL); @@ -1677,11 +1678,11 @@ static void ahci_test_identify(AHCIState *ahci) port_clear(ahci, i); /* Create a data buffer where we will dump the IDENTIFY data to. */ -data_ptr = guest_alloc(guest_malloc, 512); +data_ptr = guest_alloc(guest_malloc, buffsize); g_assert(data_ptr); /* Construct the Command Table (FIS and PRDT) */ -table_ptr = build_cmd_table(ahci, i, 512, CMD_IDENTIFY, data_ptr); +table_ptr = build_cmd_table(ahci, i, buffsize, CMD_IDENTIFY, data_ptr); /* Pick a command slot and link it to the command table we've built */ cx = link_cmd_slot(ahci, i, table_ptr, CMDH_READ); @@ -1698,13 +1699,13 @@ static void ahci_test_identify(AHCIState *ahci) port_check_interrupts(ahci, i, AHCI_PX_IS_DHRS | AHCI_PX_IS_PSS); port_check_nonbusy(ahci, i, cx); /* Investigate the CMD, assert that we read 512 bytes */ -port_check_cmd_sanity(ahci, i, cx, 512); +port_check_cmd_sanity(ahci, i, cx, buffsize); /* Investigate FIS responses */ port_check_d2h_sanity(ahci, i, cx); -port_check_pio_sanity(ahci, i, cx, 512); +port_check_pio_sanity(ahci, i, cx, buffsize); /* Last, but not least: Investigate the IDENTIFY response data. */ -memread(data_ptr, &buff, 512); +memread(data_ptr, &buff, buffsize); /* Check serial number/version in the buffer */ /* NB: IDENTIFY strings are packed in 16bit little endian chunks. -- 1.9.3
[Qemu-devel] [PATCH 07/15] qtest/ahci: Add port_check_error helper
port_check-error checks a given port's error registers and asserts that everything from the port-level view is still OK. Signed-off-by: John Snow --- tests/ahci-test.c | 30 +++--- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 3e65435..fcf5ffb 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -1292,6 +1292,28 @@ static void port_clear(AHCIState *ahci, uint8_t px) qmemset(ahci->port[px].fb, 0x00, 0x100); } +/** + * Check a port for errors. + */ +static void port_check_error(AHCIState *ahci, uint8_t px) +{ +uint32_t reg; + +/* The upper 9 bits of the IS register all indicate errors. */ +reg = PX_RREG(px, AHCI_PX_IS); +reg >>= 23; +g_assert_cmphex(reg, ==, 0); + +/* The Sata Error Register should be empty. */ +reg = PX_RREG(px, AHCI_PX_SERR); +g_assert_cmphex(reg, ==, 0); + +/* The TFD also has two error sections. */ +reg = PX_RREG(px, AHCI_PX_TFD); +ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_ERR); +ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR); +} + /* Get the #cx'th command of port #px. */ static void get_command_header(AHCIState *ahci, uint8_t px, uint8_t cx, AHCICommand *cmd) @@ -1523,6 +1545,7 @@ static void ahci_test_identify(AHCIState *ahci) while (BITSET(PX_RREG(i, AHCI_PX_TFD), AHCI_PX_TFD_STS_BSY)) { usleep(50); } +port_check_error(ahci, i); /* Check for expected interrupts */ reg = PX_RREG(i, AHCI_PX_IS); @@ -1535,13 +1558,6 @@ static void ahci_test_identify(AHCIState *ahci) PX_WREG(i, AHCI_PX_IS, AHCI_PX_IS_DHRS | AHCI_PX_IS_PSS | AHCI_PX_IS_DPS); g_assert_cmphex(PX_RREG(i, AHCI_PX_IS), ==, 0); -/* Check for errors. */ -reg = PX_RREG(i, AHCI_PX_SERR); -g_assert_cmphex(reg, ==, 0); -reg = PX_RREG(i, AHCI_PX_TFD); -ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_ERR); -ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR); - /* Investigate the CMD, assert that we read 512 bytes */ get_command_header(ahci, i, cx, &cmd); g_assert_cmphex(512, ==, cmd.prdbc); -- 1.9.3
[Qemu-devel] [PATCH 13/15] qtest/ahci: Add a macro bootup routine
Add a routine that can be used to engage the AHCI device at a not-granular level so that bringing up the functionality of the HBA is easy in future tests that are not concerned with testing the bring-up process. Signed-off-by: John Snow --- tests/ahci-test.c | 19 --- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 3f92618..63fc309 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -739,6 +739,21 @@ static void ahci_hba_enable(AHCIState *ahci) * and clear the initial interrupts might be good. */ } +/** + * Boot and fully enable the HBA device. + * @see ahci_boot, ahci_pci_enable and ahci_hba_enable. + */ +static AHCIState *ahci_macro_bootup(void) +{ +AHCIState *ahci; +ahci = ahci_boot(); + +ahci_pci_enable(ahci); +ahci_hba_enable(ahci); + +return ahci; +} + /*** Specification Adherence Tests ***/ /** @@ -1773,9 +1788,7 @@ static void test_identify(void) { AHCIState *ahci; -ahci = ahci_boot(); -ahci_pci_enable(ahci); -ahci_hba_enable(ahci); +ahci = ahci_macro_bootup(); ahci_test_identify(ahci); ahci_shutdown(ahci); } -- 1.9.3
[Qemu-devel] [PATCH 09/15] qtest/ahci: Add port_check_interrupts helper
A helper that compares a given port's current interrupts and checks them against a supplied list of expected interrupt bits, and throws an error if they do not match. The helper then resets the requested interrupts on this port, and asserts that the interrupt register is now empty. Signed-off-by: John Snow --- tests/ahci-test.c | 26 -- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 4615681..315e6e9 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -1314,6 +1314,20 @@ static void port_check_error(AHCIState *ahci, uint8_t px) ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR); } +static void port_check_interrupts(AHCIState *ahci, uint8_t px, + uint32_t intr_mask) +{ +uint32_t reg; + +/* Check for expected interrupts */ +reg = PX_RREG(px, AHCI_PX_IS); +ASSERT_BIT_SET(reg, intr_mask); + +/* Clear expected interrupts and assert all interrupts now cleared. */ +PX_WREG(px, AHCI_PX_IS, intr_mask); +g_assert_cmphex(PX_RREG(px, AHCI_PX_IS), ==, 0); +} + /* Get the #cx'th command of port #px. */ static void get_command_header(AHCIState *ahci, uint8_t px, uint8_t cx, AHCICommand *cmd) @@ -1562,18 +1576,10 @@ static void ahci_test_identify(AHCIState *ahci) /* Issue Command #cx via PxCI */ issue_command(ahci, i, cx, 0); +/* Check registers for post-command consistency */ port_check_error(ahci, i); - -/* Check for expected interrupts */ -reg = PX_RREG(i, AHCI_PX_IS); -ASSERT_BIT_SET(reg, AHCI_PX_IS_DHRS); -ASSERT_BIT_SET(reg, AHCI_PX_IS_PSS); /* BUG: we expect AHCI_PX_IS_DPS to be set. */ -ASSERT_BIT_CLEAR(reg, AHCI_PX_IS_DPS); - -/* Clear expected interrupts and assert all interrupts now cleared. */ -PX_WREG(i, AHCI_PX_IS, AHCI_PX_IS_DHRS | AHCI_PX_IS_PSS | AHCI_PX_IS_DPS); -g_assert_cmphex(PX_RREG(i, AHCI_PX_IS), ==, 0); +port_check_interrupts(ahci, i, AHCI_PX_IS_DHRS | AHCI_PX_IS_PSS); /* Investigate the CMD, assert that we read 512 bytes */ get_command_header(ahci, i, cx, &cmd); -- 1.9.3
[Qemu-devel] [PATCH 08/15] qtest/ahci: Add issue_command helper
issue_command is a small routine that informs the AHCI device that we have finalized our command and we'd wish for the device to begin processing it. issue_command polls the AHCI device waiting for the device to finish, in order to achieve a synchronous effect. Signed-off-by: John Snow --- tests/ahci-test.c | 25 + 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index fcf5ffb..4615681 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -1343,6 +1343,26 @@ static void set_command_header(AHCIState *ahci, uint8_t px, memwrite(ba, cmd, sizeof(AHCICommand)); } +/** + * Issue Command #cx via PxCI and, if ncq is true, PxSACT + */ +static void issue_command(AHCIState *ahci, uint8_t px, + uint8_t cx, bool ncq) +{ +if (ncq) { +PX_WREG(px, AHCI_PX_SACT, (1 << cx)); +} + +PX_WREG(px, AHCI_PX_CI, (1 << cx)); +/* We can't rely on STS_BSY until the command has started processing. + * Therefore, we also use the Command Issue bit as indication of + * a command in-flight. */ +while (BITSET(PX_RREG(px, AHCI_PX_TFD), AHCI_PX_TFD_STS_BSY) || + BITSET(PX_RREG(px, AHCI_PX_CI), (1 << cx))) { +usleep(50); +} +} + static void destroy_command(AHCIState *ahci, uint8_t px, uint8_t cx) { AHCICommand cmd; @@ -1541,10 +1561,7 @@ static void ahci_test_identify(AHCIState *ahci) g_assert_cmphex(PX_RREG(i, AHCI_PX_IS), ==, 0); /* Issue Command #cx via PxCI */ -PX_WREG(i, AHCI_PX_CI, (1 << cx)); -while (BITSET(PX_RREG(i, AHCI_PX_TFD), AHCI_PX_TFD_STS_BSY)) { -usleep(50); -} +issue_command(ahci, i, cx, 0); port_check_error(ahci, i); /* Check for expected interrupts */ -- 1.9.3
[Qemu-devel] [PATCH 11/15] qtest/ahci: Add cmd response sanity check helpers
Adds a few helpers to help sanity-check the response of the AHCI device after a command. d2h_check_sanity inspects the D2H Register FIS, pio_check_sanity inspects the PIO Setup FIS, and cmd_check_sanity inspects the command header. Signed-off-by: John Snow --- tests/ahci-test.c | 123 +++--- 1 file changed, 80 insertions(+), 43 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index b7bfd86..dcc0748 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -306,25 +306,44 @@ typedef struct RegD2HFIS { uint8_t status; uint8_t error; /* DW1 */ -uint8_t lba_low; -uint8_t lba_mid; -uint8_t lba_high; +uint8_t lba_lo[3]; uint8_t device; /* DW2 */ -uint8_t lba3; -uint8_t lba4; -uint8_t lba5; -uint8_t res1; +uint8_t lba_hi[3]; +uint8_t res0; /* DW3 */ uint16_t count; -uint8_t res2; -uint8_t res3; +uint16_t res1; /* DW4 */ -uint16_t res4; -uint16_t res5; +uint32_t res2; } __attribute__((__packed__)) RegD2HFIS; /** + * Register device-to-host FIS structure; + * PIO Setup variety. + */ +typedef struct PIOSetupFIS { +/* DW0 */ +uint8_t fis_type; +uint8_t flags; +uint8_t status; +uint8_t error; +/* DW1 */ +uint8_t lba_lo[3]; +uint8_t device; +/* DW2 */ +uint8_t lba_hi[3]; +uint8_t res0; +/* DW3 */ +uint16_t count; +uint8_t res1; +uint8_t e_status; +/* DW4 */ +uint16_t tx_count; +uint16_t res2; +} __attribute__((__packed__)) PIOSetupFIS; + +/** * Register host-to-device FIS structure. */ typedef struct RegH2DFIS { @@ -334,14 +353,10 @@ typedef struct RegH2DFIS { uint8_t command; uint8_t feature_low; /* DW1 */ -uint8_t lba_low; -uint8_t lba_mid; -uint8_t lba_high; +uint8_t lba_lo[3]; uint8_t device; /* DW2 */ -uint8_t lba3; -uint8_t lba4; -uint8_t lba5; +uint8_t lba_hi[3]; uint8_t feature_high; /* DW3 */ uint16_t count; @@ -419,6 +434,8 @@ static void ahci_test_pci_caps(AHCIState *ahci, uint16_t header, static void ahci_test_satacap(AHCIState *ahci, uint8_t offset); static void ahci_test_msicap(AHCIState *ahci, uint8_t offset); static void ahci_test_pmcap(AHCIState *ahci, uint8_t offset); +static void get_command_header(AHCIState *ahci, uint8_t px, + uint8_t cx, AHCICommand *cmd); /*** Utilities ***/ @@ -1346,6 +1363,48 @@ static void port_check_nonbusy(AHCIState *ahci, uint8_t px, uint8_t cx) ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_DRQ); } +static void port_check_d2h_sanity(AHCIState *ahci, uint8_t px, uint8_t cx) +{ +RegD2HFIS *d2h = g_malloc0(0x20); +uint32_t reg; + +memread(ahci->port[px].fb + 0x40, d2h, 0x20); +g_assert_cmphex(d2h->fis_type, ==, 0x34); + +reg = PX_RREG(px, AHCI_PX_TFD); +g_assert_cmphex((reg & AHCI_PX_TFD_ERR), ==, d2h->error); +g_assert_cmphex((reg & AHCI_PX_TFD_STS), ==, d2h->status); + +g_free(d2h); +} + +static void port_check_pio_sanity(AHCIState *ahci, uint8_t px, + uint8_t cx, size_t buffsize) +{ +PIOSetupFIS *pio = g_malloc0(0x20); +uint32_t reg; + +memread(ahci->port[px].fb + 0x20, pio, 0x20); +g_assert_cmphex(pio->fis_type, ==, 0x5f); + +reg = PX_RREG(px, AHCI_PX_TFD); +g_assert_cmphex((reg & AHCI_PX_TFD_ERR), ==, pio->error); +g_assert_cmphex((reg & AHCI_PX_TFD_STS), ==, pio->status); + +g_assert_cmphex(le16_to_cpu(pio->tx_count), ==, buffsize); + +g_free(pio); +} + +static void port_check_cmd_sanity(AHCIState *ahci, uint8_t px, + uint8_t cx, size_t buffsize) +{ +AHCICommand cmd; + +get_command_header(ahci, px, cx, &cmd); +g_assert_cmphex(buffsize, ==, cmd.prdbc); +} + /* Get the #cx'th command of port #px. */ static void get_command_header(AHCIState *ahci, uint8_t px, uint8_t cx, AHCICommand *cmd) @@ -1544,10 +1603,7 @@ static unsigned link_cmd_slot(AHCIState *ahci, uint8_t px, uint64_t table_ptr, */ static void ahci_test_identify(AHCIState *ahci) { -RegD2HFIS *d2h = g_malloc0(0x20); -RegD2HFIS *pio = g_malloc0(0x20); -AHCICommand cmd; -uint32_t reg, data_ptr; +uint32_t data_ptr; uint16_t buff[256]; unsigned i; int rc; @@ -1599,27 +1655,11 @@ static void ahci_test_identify(AHCIState *ahci) /* BUG: we expect AHCI_PX_IS_DPS to be set. */ port_check_interrupts(ahci, i, AHCI_PX_IS_DHRS | AHCI_PX_IS_PSS); port_check_nonbusy(ahci, i, cx); - /* Investigate the CMD, assert that we read 512 bytes */ -get_command_header(ahci, i, cx, &cmd); -g_assert_cmphex(512, ==, cmd.prdbc); - +port_check_cmd_sanity(ahci, i, cx, 512); /* Investigate FIS responses */ -memread(ahci->port[i].fb + 0x20, pio, 0x20); -memread(ah
[Qemu-devel] [PATCH 14/15] qtest/ahci: Add human-readable command names
Instead of using direct hex values, use SATA command mnemonics. Signed-off-by: John Snow --- tests/ahci-test.c | 10 +- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 63fc309..e3091b9 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -265,6 +265,14 @@ #define CMDH_RES (0x800) #define CMDH_PMP (0xF000) +/* ATA Commands */ +enum { +CMD_READ_DMA= 0xC8, +CMD_WRITE_DMA = 0xCA, +CMD_FLUSH_CACHE = 0xE7, +CMD_IDENTIFY= 0xEC +}; + /*** Structures ***/ typedef struct AHCIPortState { @@ -1673,7 +1681,7 @@ static void ahci_test_identify(AHCIState *ahci) g_assert(data_ptr); /* Construct the Command Table (FIS and PRDT) */ -table_ptr = build_cmd_table(ahci, i, 512, 0xEC, data_ptr); +table_ptr = build_cmd_table(ahci, i, 512, CMD_IDENTIFY, data_ptr); /* Pick a command slot and link it to the command table we've built */ cx = link_cmd_slot(ahci, i, table_ptr, CMDH_READ); -- 1.9.3
[Qemu-devel] [PATCH 00/15] AHCI test helper refactors
The original version of the AHCI test base which is now staged for being merged, processes the ahci_identify test in a monolithic fashion. In authoring new tests, it became necessary and obvious as to how the operation of this device should be factored out to ease the writing of new AHCI tests. This patch set issues the necessary refactorings to support future test development for AHCI. This patch set DOES NOT account for any new fixes and requires no fixes from my "AHCI fixes" RFC in order to run successfully on 2014-09-18's origin/master. This patch set does not alter the operation of the existing test, or add new tests. It only offers refactorings for future patch submissions which depend on them, but are still under consideration. John Snow (15): qtest/ahci: Add AHCIState structure qtest/ahci: Add port_select helper qtest/ahci: Add port_clear helper qtest/ahci: Add command header helpers qtest/ahci: Add build cmd table helper qtest/ahci: Add link_cmd_slot helper qtest/ahci: Add port_check_error helper qtest/ahci: Add issue_command helper qtest/ahci: Add port_check_interrupts helper qtest/ahci: Add port_check_nonbusy helper qtest/ahci: Add cmd response sanity check helpers qtest/ahci: Enforce zero-leaks for guest mem usage qtest/ahci: Add a macro bootup routine qtest/ahci: Add human-readable command names qtest/ahci: Don't use a magic constant for buffer size tests/ahci-test.c | 860 -- 1 file changed, 583 insertions(+), 277 deletions(-) -- 1.9.3
[Qemu-devel] [PATCH 01/15] qtest/ahci: Add AHCIState structure
Introduce a state structure that can be used to conveniently bookmark key values of the AHCI Device instead of re-polling it all the time. This structure also helps ease of cleanup. Signed-off-by: John Snow --- tests/ahci-test.c | 283 ++ 1 file changed, 138 insertions(+), 145 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 7cc49f0..d845635 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -254,6 +254,21 @@ /*** Structures ***/ +typedef struct AHCIPortState { +uint64_t fb; +uint64_t clb; +} AHCIPortState; + +typedef struct AHCIState { +QPCIDevice *dev; +void *hba_base; +uint64_t barsize; +uint32_t fingerprint; +uint32_t cap; +uint32_t cap2; +AHCIPortState port[32]; +} AHCIState; + /** * Generic FIS structure. */ @@ -343,18 +358,11 @@ typedef struct PRD { uint32_t dbc; /* Data Byte Count (0-indexed) & Interrupt Flag (bit 2^31) */ } PRD; -typedef struct HBACap { -uint32_t cap; -uint32_t cap2; -} HBACap; - /*** Globals ***/ static QGuestAllocator *guest_malloc; static QPCIBus *pcibus; -static uint64_t barsize; static char tmp_path[] = "/tmp/qtest.XX"; static bool ahci_pedantic; -static uint32_t ahci_fingerprint; /*** Macro Utilities ***/ #define BITANY(data, mask) (((data) & (mask)) != 0) @@ -364,8 +372,9 @@ static uint32_t ahci_fingerprint; #define ASSERT_BIT_CLEAR(data, mask) g_assert_cmphex((data) & (mask), ==, 0) /*** IO macros for the AHCI memory registers. ***/ -#define AHCI_READ(OFST) qpci_io_readl(ahci, hba_base + (OFST)) -#define AHCI_WRITE(OFST, VAL) qpci_io_writel(ahci, hba_base + (OFST), (VAL)) +#define AHCI_READ(OFST) qpci_io_readl(ahci->dev, ahci->hba_base + (OFST)) +#define AHCI_WRITE(OFST, VAL) qpci_io_writel(ahci->dev, \ + ahci->hba_base + (OFST), (VAL)) #define AHCI_RREG(regno) AHCI_READ(4 * (regno)) #define AHCI_WREG(regno, val) AHCI_WRITE(4 * (regno), (val)) #define AHCI_SET(regno, mask) AHCI_WREG((regno), AHCI_RREG(regno) | (mask)) @@ -385,17 +394,16 @@ static uint32_t ahci_fingerprint; /*** Function Declarations ***/ -static QPCIDevice *get_ahci_device(void); -static QPCIDevice *start_ahci_device(QPCIDevice *dev, void **hba_base); +static QPCIDevice *get_ahci_device(uint32_t *fingerprint); +static void start_ahci_device(AHCIState *ahci); static void free_ahci_device(QPCIDevice *dev); -static void ahci_test_port_spec(QPCIDevice *ahci, void *hba_base, -HBACap *hcap, uint8_t port); -static void ahci_test_pci_spec(QPCIDevice *ahci); -static void ahci_test_pci_caps(QPCIDevice *ahci, uint16_t header, +static void ahci_test_port_spec(AHCIState *ahci, uint8_t port); +static void ahci_test_pci_spec(AHCIState *ahci); +static void ahci_test_pci_caps(AHCIState *ahci, uint16_t header, uint8_t offset); -static void ahci_test_satacap(QPCIDevice *ahci, uint8_t offset); -static void ahci_test_msicap(QPCIDevice *ahci, uint8_t offset); -static void ahci_test_pmcap(QPCIDevice *ahci, uint8_t offset); +static void ahci_test_satacap(AHCIState *ahci, uint8_t offset); +static void ahci_test_msicap(AHCIState *ahci, uint8_t offset); +static void ahci_test_pmcap(AHCIState *ahci, uint8_t offset); /*** Utilities ***/ @@ -413,9 +421,10 @@ static void string_bswap16(uint16_t *s, size_t bytes) /** * Locate, verify, and return a handle to the AHCI device. */ -static QPCIDevice *get_ahci_device(void) +static QPCIDevice *get_ahci_device(uint32_t *fingerprint) { QPCIDevice *ahci; +uint32_t ahci_fingerprint; pcibus = qpci_init_pc(); @@ -433,21 +442,19 @@ static QPCIDevice *get_ahci_device(void) g_assert_not_reached(); } +*fingerprint = ahci_fingerprint; return ahci; } -static void free_ahci_device(QPCIDevice *ahci) +static void free_ahci_device(QPCIDevice *dev) { /* libqos doesn't have a function for this, so free it manually */ -g_free(ahci); +g_free(dev); if (pcibus) { qpci_free_pc(pcibus); pcibus = NULL; } - -/* Clear our cached barsize information. */ -barsize = 0; } /*** Test Setup & Teardown ***/ @@ -485,8 +492,12 @@ static void qtest_shutdown(void) /** * Start a Q35 machine and bookmark a handle to the AHCI device. */ -static QPCIDevice *ahci_boot(void) +static AHCIState *ahci_boot(void) { +AHCIState *s; + +s = g_malloc0(sizeof(AHCIState)); + qtest_boot("-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s" " -M q35 " "-device ide-hd,drive=drive0 " @@ -494,15 +505,18 @@ static QPCIDevice *ahci_boot(void) tmp_path, "testdisk", "version"); /* Verify that we have an AHCI device present. */ -return get_ahci_device(); +s->dev
Re: [Qemu-devel] [PATCH 00/15] AHCI test helper refactors
On 09/19/2014 06:53 AM, Markus Armbruster wrote: John Snow writes: The original version of the AHCI test base which is now staged for being merged, processes the ahci_identify test in a monolithic fashion. In authoring new tests, it became necessary and obvious as to how the operation of this device should be factored out to ease the writing of new AHCI tests. This patch set issues the necessary refactorings to support future test development for AHCI. This patch set DOES NOT account for any new fixes and requires no fixes from my "AHCI fixes" RFC in order to run successfully on 2014-09-18's origin/master. This patch set does not alter the operation of the existing test, or add new tests. It only offers refactorings for future patch submissions which depend on them, but are still under consideration. [...] tests/ahci-test.c | 860 -- 1 file changed, 583 insertions(+), 277 deletions(-) Ignorant question: why should we commit the "monolithic" test only to refactor it extensively right away? Newbie problems, basically. I submitted the first AHCI test series 2014-07-07. The tests were staged last week. A lot happened on my downstream copy in that time, but I opted to keep the series "the same scope" from v1 through v4, because otherwise the series would have just kept growing ... I submitted v4 I do apologize at how absurd it is, on your end, to see an introduction followed immediately by a refactor. It seemed like the idea at the time was to get a basis that people agreed upon, then modify as needed. Sorry for the confusion. -- —js
Re: [Qemu-devel] [RFC v2 1/3] blockdev: Add function to search for orphaned drives
On 09/19/2014 04:28 AM, Markus Armbruster wrote: John Snow writes: Signed-off-by: John Snow --- blockdev.c| 19 +++ include/sysemu/blockdev.h | 1 + vl.c | 5 + 3 files changed, 25 insertions(+) diff --git a/blockdev.c b/blockdev.c index b361fbb..5e7c93a 100644 --- a/blockdev.c +++ b/blockdev.c @@ -166,6 +166,25 @@ DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit) return NULL; } +DriveInfo *drive_check_orphaned(void) +{ +DriveInfo *dinfo; +DriveInfo *ret = NULL; + +QTAILQ_FOREACH(dinfo, &drives, next) { +/* If dev is NULL, it has no device attached. + * If drv is non-NULL, it has a file attached. + * If both conditions are true, it is possibly an oversight. */ Suggest to spell out dinfo->bdrv->dev and dinfo->bdrv->drv. "File attached" is imprecise. BDS member drv is non-null betwen bdrv_open() and bdrv_close(). A BDS with null drv means "empty", in the sense of "no medium". +if ((dinfo->bdrv->dev == NULL) && (dinfo->bdrv->drv != NULL)) { +fprintf(stderr, "Orphaned drive: id=%s,if=%s,file=%s\n", +dinfo->id, if_name[dinfo->type], dinfo->bdrv->filename); +ret = dinfo; +} +} Please prefix "Warning:" to make the nature of this message more explicit. "Orphaned drive" might not be obvious to all users, but it's concise, and no worse than the "has no peer" we use for NICs. You warn when a non-empty drive is not used by a device model. This warns when you create one with -drive if=none for future use in the monitor. I guess that's fine. It doesn't warn for empty drives. I doubt "empty" should make a difference. I didn't want it to warn about things like the SD drive and the floppy drive, created by default, but you're right. Your suggestion below fixes this problem. I think the condition to check is "has the board failed to pick up a drive that is meant to be picked up by the board": dinfo->type != IF_NONE && !dinfo->bdrv->dev I guess this can warn about default drives, because we blindly add them whether the boards wants them or not. Stupidest solution that could possibly work: add a flag to DriveInfo to suppress the warning for them. Hm. Actually, the default drives are added by their specific interfaces. IF_IDE, IF_FLOPPY and IF_SD. This is a good improvement. (Well, cdrom is actually added via block_default_type which is usually unset, and happens to coincide with IF_IDE.) Better solution: don't add them unless the board wants them. I tried that before, but my solution[*] went nowhere. If you're interested in trying again, let me know, and I'll explain. + +return ret; +} + DriveInfo *drive_get_by_index(BlockInterfaceType type, int index) { return drive_get(type, diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 23a5d10..25d52d2 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -46,6 +46,7 @@ struct DriveInfo { }; DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); +DriveInfo *drive_check_orphaned(void); DriveInfo *drive_get_by_index(BlockInterfaceType type, int index); int drive_get_max_bus(BlockInterfaceType type); DriveInfo *drive_get_next(BlockInterfaceType type); diff --git a/vl.c b/vl.c index 5db0d08..e095bcd 100644 --- a/vl.c +++ b/vl.c @@ -4457,6 +4457,11 @@ int main(int argc, char **argv, char **envp) if (qemu_opts_foreach(qemu_find_opts("device"), device_init_func, NULL, 1) != 0) exit(1); +/* anybody left over? */ +if (drive_check_orphaned()) { +fprintf(stderr, "Warning: found drives without a backing device.\n"); +} + net_check_clients(); ds = init_displaystate(); [*] https://lists.nongnu.org/archive/html/qemu-devel/2012-08/msg02993.html
Re: [Qemu-devel] [RFC v2 0/3] Q35/AHCI -cdrom/-hda desugaring
On 09/19/2014 05:53 AM, Markus Armbruster wrote: John Snow writes: This is an extremely rough/quick sketch of a -cdrom/-hda desugaring fix for Q35/AHCI. Before I spent any time on it, I wanted feedback from Markus or anyone else who had concerns about how this problem would get fixed. This is, then, rough approach #2. Highlights: (1) Add a board property (instead of a HBA property, sigh) that defines how we should map (index, (bus,unit)). Imperfect, but it'll do for now. The place in the boards that sets it should point to the HBA in a comment. (2) Modify drive_new to accept the MachineClass instead of the default interface type. This does not affect how default drives get added, because any over-rides to the "default type" get handled in options, so while it appears we have removed the type of default drives, we have not. (3) Create helpers for AHCI to assist the Q35 board in populating the AHCI device with the IDE drives. (4) Create a helper to whine at us for oversights and help bug reporters give us more meaningful information. General approach looks good to me; I can see only coding bugs, not design flaws. I rewrote this series and was about to send it out, but it does fail the bios-tables-test because this test uses this command line: -net none -display none -machine q35,accel=tcg -drive file=tests/acpi-test-disk.raw,id=hd0 -device ide-hd,drive=hd0, Notice it doesn't say if=none for the drive, so after fixing Q35, this actually creates a new failure in this test because we will create the drive (and device), then fail when trying to create the second device attached to the same drive. I think this test is at fault, but I wanted to be duly diligent and ask the question: "Is it a big deal if I break backwards compatibility with broken scripts?" -- —js
[Qemu-devel] [PATCH 2/6] blockdev: Allow overriding if_max_dev property
The if_max_devs table as in the past been an immutable default that controls the mapping of index => (bus,unit) for all boards and all HBAs for each interface type. Since adding this mapping information to the HBA device itself is currently unwieldly from the perspective of retrieving this information at option parsing time (e.g, within drive_new), we consider the alternative of marking the if_max_devs table mutable so that later configuration and initialization can adjust the mapping at will, but only up until a drive is added, at which point the mapping is finalized. Signed-off-by: John Snow --- blockdev.c| 35 ++- include/sysemu/blockdev.h | 3 +++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/blockdev.c b/blockdev.c index 81398e7..94562e9 100644 --- a/blockdev.c +++ b/blockdev.c @@ -60,7 +60,7 @@ static const char *const if_name[IF_COUNT] = { [IF_XEN] = "xen", }; -static const int if_max_devs[IF_COUNT] = { +static int if_max_devs[IF_COUNT] = { /* * Do not change these numbers! They govern how drive option * index maps to unit and bus. That mapping is ABI. @@ -79,6 +79,30 @@ static const int if_max_devs[IF_COUNT] = { [IF_SCSI] = 7, }; +/** + * Boards may call this to offer board-by-board overrides + * of the default, global values. + */ +void override_max_devs(BlockInterfaceType type, int max_devs) +{ +DriveInfo *dinfo; + +if (max_devs <= 0) { +return; +} + +QTAILQ_FOREACH(dinfo, &drives, next) { +if (dinfo->type == type) { +fprintf(stderr, "Warning: Cannot override units-per-bus property of" +" the %s interface, because a drive of that type has" +" already been added.\n", if_name[type]); +return; +} +} + +if_max_devs[type] = max_devs; +} + /* * We automatically delete the drive when a device using it gets * unplugged. Questionable feature, but we can't just drop it. @@ -111,6 +135,15 @@ void blockdev_auto_del(BlockDriverState *bs) } } +int if_get_max_devs(BlockInterfaceType type) +{ +if (type >= IF_IDE && type < IF_COUNT) { +return if_max_devs[type]; +} + +return 0; +} + static int drive_index_to_bus_id(BlockInterfaceType type, int index) { int max_devs = if_max_devs[type]; diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 80f768d..10719d5 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -46,10 +46,13 @@ struct DriveInfo { QTAILQ_ENTRY(DriveInfo) next; }; +void override_max_devs(BlockInterfaceType type, int max_devs); + DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); bool drive_check_orphaned(void); DriveInfo *drive_get_by_index(BlockInterfaceType type, int index); int drive_get_max_bus(BlockInterfaceType type); +int if_get_max_devs(BlockInterfaceType type); DriveInfo *drive_get_next(BlockInterfaceType type); DriveInfo *drive_get_by_blockdev(BlockDriverState *bs); -- 1.9.3
[Qemu-devel] [PATCH 1/6] blockdev: Orphaned drive search
When users use command line options like -hda, -cdrom, or even -drive if=ide, it is up to the board initialization routines to pick up these drives and create backing devices for them. Some boards, like Q35, have not been doing this. However, there is no warning explaining why certain drive specifications are just silently ignored, so this function adds a check to print some warnings to assist users in debugging these sorts of issues in the future. This patch will warn about drives added with if_none, for which it is not possible to tell in advance if the omission of a backing device is an issue. A warning in these cases is considered appropriate. Signed-off-by: John Snow --- blockdev.c| 21 + include/sysemu/blockdev.h | 2 ++ vl.c | 12 +++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/blockdev.c b/blockdev.c index b361fbb..81398e7 100644 --- a/blockdev.c +++ b/blockdev.c @@ -166,6 +166,27 @@ DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit) return NULL; } +bool drive_check_orphaned(void) +{ +DriveInfo *dinfo; +bool rs = false; + +QTAILQ_FOREACH(dinfo, &drives, next) { +/* If dinfo->bdrv->dev is NULL, it has no device attached. */ +/* Unless this is a default drive, this may be an oversight. */ +if (!dinfo->bdrv->dev && !dinfo->is_default && +dinfo->type != IF_NONE) { +fprintf(stderr, "Warning: Orphaned drive without device: " +"id=%s,file=%s,if=%s,bus=%d,unit=%d\n", +dinfo->id, dinfo->bdrv->filename, if_name[dinfo->type], +dinfo->bus, dinfo->unit); +rs = true; +} +} + +return rs; +} + DriveInfo *drive_get_by_index(BlockInterfaceType type, int index) { return drive_get(type, diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 23a5d10..80f768d 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -38,6 +38,7 @@ struct DriveInfo { int unit; int auto_del; /* see blockdev_mark_auto_del() */ bool enable_auto_del; /* Only for legacy drive_new() */ +bool is_default;/* Added by default_drive() ? */ int media_cd; int cyls, heads, secs, trans; QemuOpts *opts; @@ -46,6 +47,7 @@ struct DriveInfo { }; DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); +bool drive_check_orphaned(void); DriveInfo *drive_get_by_index(BlockInterfaceType type, int index); int drive_get_max_bus(BlockInterfaceType type); DriveInfo *drive_get_next(BlockInterfaceType type); diff --git a/vl.c b/vl.c index dc792fe..eaef240 100644 --- a/vl.c +++ b/vl.c @@ -1168,6 +1168,7 @@ static void default_drive(int enable, int snapshot, BlockInterfaceType type, int index, const char *optstr) { QemuOpts *opts; +DriveInfo *dinfo; if (!enable || drive_get_by_index(type, index)) { return; @@ -1177,9 +1178,13 @@ static void default_drive(int enable, int snapshot, BlockInterfaceType type, if (snapshot) { drive_enable_snapshot(opts, NULL); } -if (!drive_new(opts, type)) { + +dinfo = drive_new(opts, type); +if (!dinfo) { exit(1); } +dinfo->is_default = true; + } void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque) @@ -4457,6 +4462,11 @@ int main(int argc, char **argv, char **envp) if (qemu_opts_foreach(qemu_find_opts("device"), device_init_func, NULL, 1) != 0) exit(1); +/* anybody left over? */ +if (drive_check_orphaned()) { +fprintf(stderr, "Warning: found drives without a backing device.\n"); +} + net_check_clients(); ds = init_displaystate(); -- 1.9.3
[Qemu-devel] [PATCH 0/6] Q35: Implement -cdrom/-hda sugar
The Q35 board initialization does not currently bother to look for any drives added by the various syntactical sugar shorthands to be added to the AHCI HBA. These include -hda through -hdd, -cdrom, and -drive if=ide shorthands. An obstacle to having implemented this sooner is debate over whether or not to add an additional interface type, and how to manage the different units-per-bus mappings of various HBA implementations. This patch series: (1) Does not add IF_AHCI, but reuses IF_IDE (2) Allows the if_max_devs table to be overridden (3) Adds this override to the Q35 board type. (4) Finally, adds implementation to Q35 initialization. Other changes from RFC2: - Re-uses ide_drive_get instead of ahci_drive_get - Adds units-per-bus property to all Q35 machines - Changes orphan scanning to exclude IF_NONE and automatically added drives - Renames 'units-per-idebus' to 'units-per-default-bus' And allows override of any one IF type (block_default) John Snow (6): blockdev: Orphaned drive search blockdev: Allow overriding if_max_dev property pc/vl: Add units-per-default-bus property ide: Update ide_drive_get to be HBA agnostic qtest/bios-tables: Correct Q35 command line q35/ahci: Pick up -cdrom and -hda options blockdev.c| 56 ++- hw/i386/pc.c | 1 + hw/i386/pc_q35.c | 6 - hw/ide/ahci.c | 15 + hw/ide/ahci.h | 2 ++ hw/ide/core.c | 12 +- include/hw/boards.h | 2 ++ include/sysemu/blockdev.h | 5 + tests/bios-tables-test.c | 10 - vl.c | 20 - 10 files changed, 115 insertions(+), 14 deletions(-) -- 1.9.3
[Qemu-devel] [PATCH 5/6] qtest/bios-tables: Correct Q35 command line
If the Q35 board types are to begin recognizing and decoding syntactic sugar for drive/device declarations, then workarounds found within the qtests suite need to be adjusted to prevent any test failures after the fix. bios-tables-test improperly uses this cli: -drive file=etc,id=hd -device ide-hd,drive=hd Which will create a drive and device due to the lack of specifying if=none. Then, it will attempt to create a second device and fail. This patch corrects this test to always use the full, non-sugared -device/-drive syntax for both PC and Q35. Signed-off-by: John Snow --- tests/bios-tables-test.c | 10 -- 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c index 602932b..9e4d205 100644 --- a/tests/bios-tables-test.c +++ b/tests/bios-tables-test.c @@ -714,14 +714,12 @@ static void test_acpi_one(const char *params, test_data *data) uint8_t signature_high; uint16_t signature; int i; -const char *device = ""; -if (!g_strcmp0(data->machine, MACHINE_Q35)) { -device = ",id=hd -device ide-hd,drive=hd"; -} +args = g_strdup_printf("-net none -display none %s " + "-drive id=hd0,if=none,file=%s " + "-device ide-hd,drive=hd0 ", + params ? params : "", disk); -args = g_strdup_printf("-net none -display none %s -drive file=%s%s,", - params ? params : "", disk, device); qtest_start(args); /* Wait at most 1 minute */ -- 1.9.3
[Qemu-devel] [PATCH 4/6] ide: Update ide_drive_get to be HBA agnostic
Instead of duplicating the logic for the if_ide (bus,unit) mappings, rely on the blockdev layer for managing those mappings for us, and use the drive_get_by_index call instead. This allows ide_drive_get to work for AHCI HBAs as well, and can be used in the Q35 initialization. Signed-off-by: John Snow --- hw/ide/core.c | 12 +++- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index 6fba056..1e43d50 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2551,13 +2551,15 @@ const VMStateDescription vmstate_ide_bus = { void ide_drive_get(DriveInfo **hd, int max_bus) { int i; +int max_devs = if_get_max_devs(IF_IDE) * max_bus; +int buses = drive_get_max_bus(IF_IDE) + 1; -if (drive_get_max_bus(IF_IDE) >= max_bus) { -fprintf(stderr, "qemu: too many IDE bus: %d\n", max_bus); -exit(1); +if (buses > max_bus) { +fprintf(stderr, "Warning: Too many IDE buses defined (%d > %d)\n", +buses, max_bus); } -for(i = 0; i < max_bus * MAX_IDE_DEVS; i++) { -hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); +for (i = 0; i < max_devs; i++) { +hd[i] = drive_get_by_index(IF_IDE, i); } } -- 1.9.3
[Qemu-devel] [PATCH 3/6] pc/vl: Add units-per-default-bus property
This patch adds the 'units_per_default_bus' property which allows individual boards to declare their desired index => (bus,unit) mapping for their default HBA, so that boards such as Q35 can specify that its default if_ide HBA, AHCI, only accepts one unit per bus. This property only overrides the mapping for drives matching the block_default_type interface. Signed-off-by: John Snow --- hw/i386/pc.c| 1 + hw/i386/pc_q35.c| 3 ++- include/hw/boards.h | 2 ++ vl.c| 8 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 2c2e9dc..ab578bf 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1524,6 +1524,7 @@ static void pc_generic_machine_class_init(ObjectClass *oc, void *data) mc->hot_add_cpu = qm->hot_add_cpu; mc->kvm_type = qm->kvm_type; mc->block_default_type = qm->block_default_type; +mc->units_per_default_bus = qm->units_per_default_bus; mc->max_cpus = qm->max_cpus; mc->no_serial = qm->no_serial; mc->no_parallel = qm->no_parallel; diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index d4a907c..b28ddbb 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -344,7 +344,8 @@ static void pc_q35_init_1_4(MachineState *machine) #define PC_Q35_MACHINE_OPTIONS \ PC_DEFAULT_MACHINE_OPTIONS, \ .desc = "Standard PC (Q35 + ICH9, 2009)", \ -.hot_add_cpu = pc_hot_add_cpu +.hot_add_cpu = pc_hot_add_cpu, \ +.units_per_default_bus = 1 #define PC_Q35_2_2_MACHINE_OPTIONS \ PC_Q35_MACHINE_OPTIONS, \ diff --git a/include/hw/boards.h b/include/hw/boards.h index dfb6718..663f16a 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -28,6 +28,7 @@ struct QEMUMachine { QEMUMachineHotAddCPUFunc *hot_add_cpu; QEMUMachineGetKvmtypeFunc *kvm_type; BlockInterfaceType block_default_type; +int units_per_default_bus; int max_cpus; unsigned int no_serial:1, no_parallel:1, @@ -86,6 +87,7 @@ struct MachineClass { int (*kvm_type)(const char *arg); BlockInterfaceType block_default_type; +int units_per_default_bus; int max_cpus; unsigned int no_serial:1, no_parallel:1, diff --git a/vl.c b/vl.c index eaef240..6fffa1f 100644 --- a/vl.c +++ b/vl.c @@ -1588,6 +1588,7 @@ static void machine_class_init(ObjectClass *oc, void *data) mc->hot_add_cpu = qm->hot_add_cpu; mc->kvm_type = qm->kvm_type; mc->block_default_type = qm->block_default_type; +mc->units_per_default_bus = qm->units_per_default_bus; mc->max_cpus = qm->max_cpus; mc->no_serial = qm->no_serial; mc->no_parallel = qm->no_parallel; @@ -4377,6 +4378,13 @@ int main(int argc, char **argv, char **envp) blk_mig_init(); ram_mig_init(); +/* If the currently selected machine wishes to override the units-per-bus + * property of its default HBA interface type, do so now. */ +if (machine_class->units_per_default_bus) { +override_max_devs(machine_class->block_default_type, + machine_class->units_per_default_bus); +} + /* open the virtual block devices */ if (snapshot) qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot, NULL, 0); -- 1.9.3
[Qemu-devel] [PATCH 6/6] q35/ahci: Pick up -cdrom and -hda options
This patch implements the backend for the Q35 board for us to be able to pick up and use drives defined by the -cdrom, -hda, or -drive if=ide shorthand options. A workaround for these command line options not previously working as intended in the bios-tables-test is also removed. Signed-off-by: John Snow --- hw/i386/pc_q35.c | 3 +++ hw/ide/ahci.c| 15 +++ hw/ide/ahci.h| 2 ++ 3 files changed, 20 insertions(+) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index b28ddbb..1664a2d 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -86,6 +86,7 @@ static void pc_q35_init(MachineState *machine) DeviceState *icc_bridge; PcGuestInfo *guest_info; ram_addr_t lowmem; +DriveInfo *hd[MAX_SATA_PORTS]; /* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory * and 256 Mbytes for PCI Express Enhanced Configuration Access Mapping @@ -253,6 +254,8 @@ static void pc_q35_init(MachineState *machine) true, "ich9-ahci"); idebus[0] = qdev_get_child_bus(&ahci->qdev, "ide.0"); idebus[1] = qdev_get_child_bus(&ahci->qdev, "ide.1"); +ide_drive_get(hd, ICH_AHCI(ahci)->ahci.ports); +ahci_ide_create_devs(ahci, hd); if (usb_enabled(false)) { /* Should we create 6 UHCI according to ich9 spec? */ diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index ba69de3..64f0d37 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1402,3 +1402,18 @@ static void sysbus_ahci_register_types(void) } type_init(sysbus_ahci_register_types) + +void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **tab) +{ +AHCIPCIState *d = ICH_AHCI(dev); +AHCIState *ahci = &d->ahci; +int i; + +for (i = 0; i < ahci->ports; i++) { +if (tab[i] == NULL) { +continue; +} +ide_create_drive(&ahci->dev[i].port, 0, tab[i]); +} + +} diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h index 1543df7..b6dc64e 100644 --- a/hw/ide/ahci.h +++ b/hw/ide/ahci.h @@ -332,4 +332,6 @@ void ahci_uninit(AHCIState *s); void ahci_reset(AHCIState *s); +void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **tab); + #endif /* HW_IDE_AHCI_H */ -- 1.9.3
Re: [Qemu-devel] issue: linking 64bit glib when building for cpu=i386
On 08/22/2014 05:05 AM, Stefan Hajnoczi wrote: On Fri, Aug 22, 2014 at 9:20 AM, Daniel P. Berrange wrote: On Thu, Aug 21, 2014 at 08:10:54PM -0400, John Snow wrote: I was running a series of tests on 32 and 64 bit hosts to test for endianness and variable width issues when I noticed that I couldn't properly perform a build of "make check" against a 32bit target from a 64bit host: ../../configure --cpu=i386 && make -j4 && make check This produces some warnings in tests-cutils about overflowing variables that are of type guint64. It's been mentioned on the mailing lists before, actually: http://lists.gnu.org/archive/html/qemu-devel/2014-05/msg00452.html The problem is that guint64 is being aliased against "unsigned long", which is only 4 bytes instead of the implied 8. This occurs because we link against the 64bit headers for glib instead of the 32bit ones when we're building for i386 from an x86_64 host. Our include flags wind up looking like: -I/usr/include/glib-2.0 but -I/usr/lib64/glib-2.0/include I was discussing the problem with Stefan: On 08/21/2014 05:03 AM, Stefan Hajnoczi wrote: The problem is that pkg-config uses libdir=/usr/lib64 by default on amd64 hosts. It doesn't know that gcc -m32 is being used. This results in glib's 64-bit headers being used where guint64 is just unsigned long. On 32-bit hosts this is incorrect. Two workarounds: 1. yum install pkgconfig.i686 and run it instead of pkgconfig.x86_64 2. Use the pkg-config --define-variable libdir=/usr/lib option You can set PKG_CONFIG=path/to/pkg-config.i686 on QEMU's ./configure command-line. This is all distro-specific :(. Any other solutions? Stefan I am not extremely well versed in configure or pkg-config ninjutsu, but I must imagine that the ARCH/cpu variables we are setting in configure could help us know to call the 32bit pkg-config instead of the native 64bit version and fix this issue. Does anyone have any good ideas? Surely other projects must have run into this elsewhere. Distros will install pkg-config .pc files for non-native architectures in a different location normally. The supported / recommended way to tell pkg-config to look in these alternative dirs is to set the env variable PKG_CONFIG_LIBDIR. This replaces the built-in default search directory that looks for native. So on a Fedora / RHELL system, to make pkg-config use 32-bit libs you want to set PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig which replaces the default location of /usr/lib64/pkgconfig. Nice, and Paolo sent me an automated way of doing that: On Fri, Aug 22, 2014 at 12:14:28AM +0200, Paolo Bonzini wrote: You need to set PKG_CONFIG_LIBDIR to /usr/lib/$MULTILIBDIR/pkgconfig where MULTILIBDIR is if $CC -print-multiarch >/dev/null 2>&1; then MULTILIBDIR=`$CC -print-multiarch $CFLAGS $CPPFLAGS` fi if test -z "$MULTILIBDIR"; then MULTILIBDIR=`$CC --print-multi-os-directory $CFLAGS $CPPFLAGS` fi This will point at /usr/lib/pkgconfig/glib-2.0.pc instead of /usr/lib64/pkgconfig/glib-2.0.pc I tested that it works. Stefan A barrier into introducing this into the configuration script is that clang, used on OSX hosts, does not support these print flags. Even worse, clang --print-multi-os-directory produces "x86_64" on Fedora 20, which will lead to an erroneous configuration. So we'll have to look into another way to support cross-compilation for i386 on x86_64 to avoid script breakage on other platforms. Any other ideas? It looks as if there have been proposals in the past to add some multi-arch awareness into pkg-config, but they haven't gone anywhere. So some tool needs to tell pkg-config where to look, but clang is apparently not up to the job. Where does gcc/g++ find out about the lib directories? Or are they baked-in per distro? Any ideas? --j
Re: [Qemu-devel] [PATCH v3] docs: add blkdebug block driver documentation
On 09/24/2014 12:24 PM, Max Reitz wrote: On 24.09.2014 11:44, Stefan Hajnoczi wrote: The blkdebug block driver is undocumented. Documenting it is worthwhile since it offers powerful error injection features that are used by qemu-iotests test cases. This document will make it easier for people to learn about and use blkdebug. Signed-off-by: Stefan Hajnoczi --- v3: * Fix tab space damage [Eric] * Rephrase event_names[] as full list of events [Eric] * Explain that blkdebug state is not observable from outside [Eric] * Clarify state 0 and state 1 [Eric] v2: * Added GPL v2 or later license and Red Hat copyright [Eric] * Expanded ini rules file explanation [Paolo] * Added note that errno values depend on the host [Eric] docs/blkdebug.txt | 161 ++ 1 file changed, 161 insertions(+) create mode 100644 docs/blkdebug.txt Reviewed-by: Max Reitz Maybe I'll add information about blkdebug's QMP interface sometime... If you have the cycles and the knowledge, you definitely should!
Re: [Qemu-devel] [PATCH 4/6] ide: Update ide_drive_get to be HBA agnostic
On 09/24/2014 10:35 AM, Markus Armbruster wrote: John Snow writes: Instead of duplicating the logic for the if_ide (bus,unit) mappings, rely on the blockdev layer for managing those mappings for us, and use the drive_get_by_index call instead. This allows ide_drive_get to work for AHCI HBAs as well, and can be used in the Q35 initialization. Signed-off-by: John Snow --- hw/ide/core.c | 12 +++- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index 6fba056..1e43d50 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2551,13 +2551,15 @@ const VMStateDescription vmstate_ide_bus = { void ide_drive_get(DriveInfo **hd, int max_bus) { int i; +int max_devs = if_get_max_devs(IF_IDE) * max_bus; Okay, here you need if_get_max_devs(). Suggest to move its introduction from PATCH 2 to this one. Hmm, I guess we better change its name to start with drive_. +int buses = drive_get_max_bus(IF_IDE) + 1; -if (drive_get_max_bus(IF_IDE) >= max_bus) { -fprintf(stderr, "qemu: too many IDE bus: %d\n", max_bus); -exit(1); +if (buses > max_bus) { +fprintf(stderr, "Warning: Too many IDE buses defined (%d > %d)\n", +buses, max_bus); New! Error message now English! Since you touch it, you could use error_report(). Not important, as doesn't make much of a difference in this case. } -for(i = 0; i < max_bus * MAX_IDE_DEVS; i++) { -hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); +for (i = 0; i < max_devs; i++) { +hd[i] = drive_get_by_index(IF_IDE, i); } } Maybe parameter max_bus should be replaced by the number of slots in hd[]. What do you think? This is your only recommendation I haven't implemented yet for V2. I think this makes sense from a mechanical perspective because it would be nice to have the hard guarantee of not running over the array boundary instead of relying on the numbers in different places to be consistent. The only reason I didn't do this is because I didn't want to touch calls to drive_get in 10 boards. On the other hand, if the numbers are out of alignment and we run over the end of the array here, it's a board property not aligning with how the board init function works -- Not really a runtime issue, and something we can avoid relatively easily. However, this could be a problem if we decide NOT to make the units-per-bus property constant across all Q35 types, for example, if we don't also then update how we generate this HD array. (If Q35 1.6 does not use this property, we'll have 12 max devices implied, which is clearly wrong and why I advocate instating this property backwards for all versions.) I think I am inclined to leave it as-is for now provided we agree that creating this property for all versions makes sense. If we wish to add the ability to have different numbers of units-per-bus properties per version, then the ide drive pickup code on all boards will have to get smarter. Thoughts?
Re: [Qemu-devel] [PATCH 1/6] blockdev: Orphaned drive search
On 09/24/2014 10:06 AM, Markus Armbruster wrote: John Snow writes: When users use command line options like -hda, -cdrom, or even -drive if=ide, it is up to the board initialization routines to pick up these drives and create backing devices for them. Some boards, like Q35, have not been doing this. However, there is no warning explaining why certain drive specifications are just silently ignored, so this function adds a check to print some warnings to assist users in debugging these sorts of issues in the future. This patch will warn about drives added with if_none, Judging from my testing, I suspect you mean "will not warn" ;) A fun story: I did actually mean what I wrote, but in my own testing, found that this produced warnings for the test suite, so I decided to change the behavior to not warn for IF_NONE. I figured it would make sense if it gave a little warning that amounted to "Don't forget to add a device for this drive!" but I didn't want to see any more output on the test suite, so I nixed it. I amended my commit message for V2. for which it is not possible to tell in advance if the omission of a backing device is an issue. A warning in these cases is considered appropriate. Signed-off-by: John Snow --- blockdev.c| 21 + include/sysemu/blockdev.h | 2 ++ vl.c | 12 +++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/blockdev.c b/blockdev.c index b361fbb..81398e7 100644 --- a/blockdev.c +++ b/blockdev.c @@ -166,6 +166,27 @@ DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit) return NULL; } +bool drive_check_orphaned(void) +{ +DriveInfo *dinfo; +bool rs = false; + +QTAILQ_FOREACH(dinfo, &drives, next) { +/* If dinfo->bdrv->dev is NULL, it has no device attached. */ +/* Unless this is a default drive, this may be an oversight. */ +if (!dinfo->bdrv->dev && !dinfo->is_default && +dinfo->type != IF_NONE) { +fprintf(stderr, "Warning: Orphaned drive without device: " +"id=%s,file=%s,if=%s,bus=%d,unit=%d\n", +dinfo->id, dinfo->bdrv->filename, if_name[dinfo->type], +dinfo->bus, dinfo->unit); +rs = true; +} +} + +return rs; +} + DriveInfo *drive_get_by_index(BlockInterfaceType type, int index) { return drive_get(type, diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 23a5d10..80f768d 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -38,6 +38,7 @@ struct DriveInfo { int unit; int auto_del; /* see blockdev_mark_auto_del() */ bool enable_auto_del; /* Only for legacy drive_new() */ +bool is_default;/* Added by default_drive() ? */ int media_cd; int cyls, heads, secs, trans; QemuOpts *opts; @@ -46,6 +47,7 @@ struct DriveInfo { }; DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); +bool drive_check_orphaned(void); DriveInfo *drive_get_by_index(BlockInterfaceType type, int index); int drive_get_max_bus(BlockInterfaceType type); DriveInfo *drive_get_next(BlockInterfaceType type); diff --git a/vl.c b/vl.c index dc792fe..eaef240 100644 --- a/vl.c +++ b/vl.c @@ -1168,6 +1168,7 @@ static void default_drive(int enable, int snapshot, BlockInterfaceType type, int index, const char *optstr) { QemuOpts *opts; +DriveInfo *dinfo; if (!enable || drive_get_by_index(type, index)) { return; @@ -1177,9 +1178,13 @@ static void default_drive(int enable, int snapshot, BlockInterfaceType type, if (snapshot) { drive_enable_snapshot(opts, NULL); } -if (!drive_new(opts, type)) { + +dinfo = drive_new(opts, type); +if (!dinfo) { exit(1); } +dinfo->is_default = true; + } void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque) @@ -4457,6 +4462,11 @@ int main(int argc, char **argv, char **envp) if (qemu_opts_foreach(qemu_find_opts("device"), device_init_func, NULL, 1) != 0) exit(1); +/* anybody left over? */ +if (drive_check_orphaned()) { +fprintf(stderr, "Warning: found drives without a backing device.\n"); +} + net_check_clients(); ds = init_displaystate(); Terminology: the device model is the front end, the thing created by -drive is the back end. I'd simply drop this message. If you want to keep it, rephrase it to avoid "backing device". -- —js
Re: [Qemu-devel] [PATCH 4/6] ide: Update ide_drive_get to be HBA agnostic
On 09/25/2014 02:13 AM, Markus Armbruster wrote: John Snow writes: On 09/24/2014 10:35 AM, Markus Armbruster wrote: John Snow writes: Instead of duplicating the logic for the if_ide (bus,unit) mappings, rely on the blockdev layer for managing those mappings for us, and use the drive_get_by_index call instead. This allows ide_drive_get to work for AHCI HBAs as well, and can be used in the Q35 initialization. Signed-off-by: John Snow --- hw/ide/core.c | 12 +++- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index 6fba056..1e43d50 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2551,13 +2551,15 @@ const VMStateDescription vmstate_ide_bus = { void ide_drive_get(DriveInfo **hd, int max_bus) { int i; +int max_devs = if_get_max_devs(IF_IDE) * max_bus; Okay, here you need if_get_max_devs(). Suggest to move its introduction from PATCH 2 to this one. Hmm, I guess we better change its name to start with drive_. +int buses = drive_get_max_bus(IF_IDE) + 1; -if (drive_get_max_bus(IF_IDE) >= max_bus) { -fprintf(stderr, "qemu: too many IDE bus: %d\n", max_bus); -exit(1); +if (buses > max_bus) { +fprintf(stderr, "Warning: Too many IDE buses defined (%d > %d)\n", +buses, max_bus); New! Error message now English! Since you touch it, you could use error_report(). Not important, as doesn't make much of a difference in this case. } -for(i = 0; i < max_bus * MAX_IDE_DEVS; i++) { -hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); +for (i = 0; i < max_devs; i++) { +hd[i] = drive_get_by_index(IF_IDE, i); } } Maybe parameter max_bus should be replaced by the number of slots in hd[]. What do you think? This is your only recommendation I haven't implemented yet for V2. I think this makes sense from a mechanical perspective because it would be nice to have the hard guarantee of not running over the array boundary instead of relying on the numbers in different places to be consistent. The only reason I didn't do this is because I didn't want to touch calls to drive_get in 10 boards. Leaving this idea to a followup patch is fine, even if we decide now we do want it. On the other hand, if the numbers are out of alignment and we run over the end of the array here, it's a board property not aligning with how the board init function works -- Not really a runtime issue, and something we can avoid relatively easily. An overrun is clearly a programming error. The intent of replacing the parameter is to make the code clearer, not to change its behavior. However, this could be a problem if we decide NOT to make the units-per-bus property constant across all Q35 types, for example, if we don't also then update how we generate this HD array. (If Q35 1.6 does not use this property, we'll have 12 max devices implied, which is clearly wrong and why I advocate instating this property backwards for all versions.) I think I am inclined to leave it as-is for now provided we agree that creating this property for all versions makes sense. If we wish to add the ability to have different numbers of units-per-bus properties per version, then the ide drive pickup code on all boards will have to get smarter. Okay. Let's figure out whether we want to keep the (bus, unit) mapping bug-compatibly broken for old machine types, and get this series wrapped. Then, if we still care about making ide_drive_get() clearer, try my idea to see how it works out. On the subject of compatibly broken: Is there any circumstance where the mapping has *any* effect on the current working behavior? Since we do not support the shorthand syntax at all currently, there is no code that USES this mapping to do anything. Even if you specify -drive file=...,index=42; the -device that you currently *must* add simply ignores any index/bus/unit mappings you've already given the drive -- it doesn't seem to touch or use any information within the DriveInfo structure at all. That said, why not make this property retroactive? It won't affect anything. Or rather, the property alone won't. What *will* break older command lines is the fact that we are now supporting the syntactic sugar at all, but I think this is clearly a bug, and we should not go out of our way to tolerate broken syntax. Based on the above, I think I will: (A) Implement the property for all versions (B) Change the behavior of ide_drive_get to take the number of elements in the array, and imply the number of buses instead. If we decide to roll back older versions to be "compatibly broken," this will in effect limit us to three drives instead of six (index 0, 2, and 4) being mapped to ports 0, 1, and 2. This requires me touching the board init to adjust the call to drive_get slightly.
[Qemu-devel] [PATCH v2 3/6] pc/vl: Add units-per-default-bus property
This patch adds the 'units_per_default_bus' property which allows individual boards to declare their desired index => (bus,unit) mapping for their default HBA, so that boards such as Q35 can specify that its default if_ide HBA, AHCI, only accepts one unit per bus. This property only overrides the mapping for drives matching the block_default_type interface. This patch also adds this property to *all* past and present Q35 machine types. This retroactive addition is justified because the previous erroneous index=>(bus,unit) mappings caused by lack of such a property were not utilized due to lack of initialization code in the Q35 init routine. Further, semantically, the Q35 board type has always had the property that its default HBA, AHCI, only accepts one unit per bus. The new code added to add devices to drives relies upon the accuracy of this mapping. Thus, the property is applied retroactively to reduce complexity of allowing IDE HBAs with different units per bus. Examples: Prior to this patch, all IDE HBAs were assumed to use 2 units per bus (Master, Slave). When using Q35 and AHCI, however, we only allow one unit per bus. -hdb foo.qcow2 would become index=1, or bus=0,unit=1. -hdd foo.qcow2 would become index=3, or bus=1,unit=1. -drive file=foo.qcow2,index=5 becomes bus=2,unit=1. These are invalid for AHCI. They now become, under Q35 only: -hdb foo.qcow2 --> index=1, bus=1, unit=0. -hdd foo.qcow2 --> index=3, bus=3, unit=0. -drive file=foo.qcow2,index=5 --> bus=5,unit=0. The mapping is adjusted based on the fact that the default IF for the Q35 machine type is IF_IDE, and units-per-default-bus overrides the IDE mapping from its default of 2 units per bus to just 1 unit per bus. Signed-off-by: John Snow --- hw/i386/pc.c| 1 + hw/i386/pc_q35.c| 3 ++- include/hw/boards.h | 2 ++ vl.c| 8 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 82a7daa..d045e8b 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1524,6 +1524,7 @@ static void pc_generic_machine_class_init(ObjectClass *oc, void *data) mc->hot_add_cpu = qm->hot_add_cpu; mc->kvm_type = qm->kvm_type; mc->block_default_type = qm->block_default_type; +mc->units_per_default_bus = qm->units_per_default_bus; mc->max_cpus = qm->max_cpus; mc->no_serial = qm->no_serial; mc->no_parallel = qm->no_parallel; diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index d4a907c..b28ddbb 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -344,7 +344,8 @@ static void pc_q35_init_1_4(MachineState *machine) #define PC_Q35_MACHINE_OPTIONS \ PC_DEFAULT_MACHINE_OPTIONS, \ .desc = "Standard PC (Q35 + ICH9, 2009)", \ -.hot_add_cpu = pc_hot_add_cpu +.hot_add_cpu = pc_hot_add_cpu, \ +.units_per_default_bus = 1 #define PC_Q35_2_2_MACHINE_OPTIONS \ PC_Q35_MACHINE_OPTIONS, \ diff --git a/include/hw/boards.h b/include/hw/boards.h index dfb6718..663f16a 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -28,6 +28,7 @@ struct QEMUMachine { QEMUMachineHotAddCPUFunc *hot_add_cpu; QEMUMachineGetKvmtypeFunc *kvm_type; BlockInterfaceType block_default_type; +int units_per_default_bus; int max_cpus; unsigned int no_serial:1, no_parallel:1, @@ -86,6 +87,7 @@ struct MachineClass { int (*kvm_type)(const char *arg); BlockInterfaceType block_default_type; +int units_per_default_bus; int max_cpus; unsigned int no_serial:1, no_parallel:1, diff --git a/vl.c b/vl.c index 6500472..940b149 100644 --- a/vl.c +++ b/vl.c @@ -1588,6 +1588,7 @@ static void machine_class_init(ObjectClass *oc, void *data) mc->hot_add_cpu = qm->hot_add_cpu; mc->kvm_type = qm->kvm_type; mc->block_default_type = qm->block_default_type; +mc->units_per_default_bus = qm->units_per_default_bus; mc->max_cpus = qm->max_cpus; mc->no_serial = qm->no_serial; mc->no_parallel = qm->no_parallel; @@ -4378,6 +4379,13 @@ int main(int argc, char **argv, char **envp) blk_mig_init(); ram_mig_init(); +/* If the currently selected machine wishes to override the units-per-bus + * property of its default HBA interface type, do so now. */ +if (machine_class->units_per_default_bus) { +override_max_devs(machine_class->block_default_type, + machine_class->units_per_default_bus); +} + /* open the virtual block devices */ if (snapshot) qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot, NULL, 0); -- 1.9.3
[Qemu-devel] [PATCH v2 5/6] qtest/bios-tables: Correct Q35 command line
If the Q35 board types are to begin recognizing and decoding syntactic sugar for drive/device declarations, then workarounds found within the qtests suite need to be adjusted to prevent any test failures after the fix. bios-tables-test improperly uses this cli: -drive file=etc,id=hd -device ide-hd,drive=hd Which will create a drive and device due to the lack of specifying if=none. Then, it will attempt to create a second device and fail. This patch corrects this test to always use the full, non-sugared -device/-drive syntax for both PC and Q35. Signed-off-by: John Snow --- tests/bios-tables-test.c | 10 -- 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c index 602932b..9e4d205 100644 --- a/tests/bios-tables-test.c +++ b/tests/bios-tables-test.c @@ -714,14 +714,12 @@ static void test_acpi_one(const char *params, test_data *data) uint8_t signature_high; uint16_t signature; int i; -const char *device = ""; -if (!g_strcmp0(data->machine, MACHINE_Q35)) { -device = ",id=hd -device ide-hd,drive=hd"; -} +args = g_strdup_printf("-net none -display none %s " + "-drive id=hd0,if=none,file=%s " + "-device ide-hd,drive=hd0 ", + params ? params : "", disk); -args = g_strdup_printf("-net none -display none %s -drive file=%s%s,", - params ? params : "", disk, device); qtest_start(args); /* Wait at most 1 minute */ -- 1.9.3
[Qemu-devel] [PATCH v2 1/6] blockdev: Orphaned drive search
When users use command line options like -hda, -cdrom, or even -drive if=ide, it is up to the board initialization routines to pick up these drives and create backing devices for them. Some boards, like Q35, have not been doing this. However, there is no warning explaining why certain drive specifications are just silently ignored, so this function adds a check to print some warnings to assist users in debugging these sorts of issues in the future. This patch will not warn about drives added with if_none, for which it is not possible to tell in advance if the omission of a backing device is an issue. A warning in these cases is considered appropriate. Signed-off-by: John Snow --- blockdev.c| 21 + include/sysemu/blockdev.h | 2 ++ vl.c | 10 +- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/blockdev.c b/blockdev.c index b361fbb..81398e7 100644 --- a/blockdev.c +++ b/blockdev.c @@ -166,6 +166,27 @@ DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit) return NULL; } +bool drive_check_orphaned(void) +{ +DriveInfo *dinfo; +bool rs = false; + +QTAILQ_FOREACH(dinfo, &drives, next) { +/* If dinfo->bdrv->dev is NULL, it has no device attached. */ +/* Unless this is a default drive, this may be an oversight. */ +if (!dinfo->bdrv->dev && !dinfo->is_default && +dinfo->type != IF_NONE) { +fprintf(stderr, "Warning: Orphaned drive without device: " +"id=%s,file=%s,if=%s,bus=%d,unit=%d\n", +dinfo->id, dinfo->bdrv->filename, if_name[dinfo->type], +dinfo->bus, dinfo->unit); +rs = true; +} +} + +return rs; +} + DriveInfo *drive_get_by_index(BlockInterfaceType type, int index) { return drive_get(type, diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 23a5d10..80f768d 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -38,6 +38,7 @@ struct DriveInfo { int unit; int auto_del; /* see blockdev_mark_auto_del() */ bool enable_auto_del; /* Only for legacy drive_new() */ +bool is_default;/* Added by default_drive() ? */ int media_cd; int cyls, heads, secs, trans; QemuOpts *opts; @@ -46,6 +47,7 @@ struct DriveInfo { }; DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); +bool drive_check_orphaned(void); DriveInfo *drive_get_by_index(BlockInterfaceType type, int index); int drive_get_max_bus(BlockInterfaceType type); DriveInfo *drive_get_next(BlockInterfaceType type); diff --git a/vl.c b/vl.c index dbdca59..6500472 100644 --- a/vl.c +++ b/vl.c @@ -1168,6 +1168,7 @@ static void default_drive(int enable, int snapshot, BlockInterfaceType type, int index, const char *optstr) { QemuOpts *opts; +DriveInfo *dinfo; if (!enable || drive_get_by_index(type, index)) { return; @@ -1177,9 +1178,13 @@ static void default_drive(int enable, int snapshot, BlockInterfaceType type, if (snapshot) { drive_enable_snapshot(opts, NULL); } -if (!drive_new(opts, type)) { + +dinfo = drive_new(opts, type); +if (!dinfo) { exit(1); } +dinfo->is_default = true; + } void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque) @@ -4458,6 +4463,9 @@ int main(int argc, char **argv, char **envp) if (qemu_opts_foreach(qemu_find_opts("device"), device_init_func, NULL, 1) != 0) exit(1); +/* Did we create any drives that we failed to create a device for? */ +drive_check_orphaned(); + net_check_clients(); ds = init_displaystate(); -- 1.9.3
[Qemu-devel] [PATCH v2 6/6] q35/ahci: Pick up -cdrom and -hda options
This patch implements the backend for the Q35 board for us to be able to pick up and use drives defined by the -cdrom, -hda, or -drive if=ide shorthand options. Signed-off-by: John Snow --- hw/i386/pc_q35.c | 4 hw/ide/ahci.c| 15 +++ hw/ide/ahci.h| 2 ++ 3 files changed, 21 insertions(+) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index b28ddbb..bb0dc8e 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -86,6 +86,7 @@ static void pc_q35_init(MachineState *machine) DeviceState *icc_bridge; PcGuestInfo *guest_info; ram_addr_t lowmem; +DriveInfo *hd[MAX_SATA_PORTS]; /* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory * and 256 Mbytes for PCI Express Enhanced Configuration Access Mapping @@ -253,6 +254,9 @@ static void pc_q35_init(MachineState *machine) true, "ich9-ahci"); idebus[0] = qdev_get_child_bus(&ahci->qdev, "ide.0"); idebus[1] = qdev_get_child_bus(&ahci->qdev, "ide.1"); +g_assert_cmpint(MAX_SATA_PORTS, ==, ICH_AHCI(ahci)->ahci.ports); +ide_drive_get(hd, ICH_AHCI(ahci)->ahci.ports); +ahci_ide_create_devs(ahci, hd); if (usb_enabled(false)) { /* Should we create 6 UHCI according to ich9 spec? */ diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 8978643..79abb6a 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1419,3 +1419,18 @@ static void sysbus_ahci_register_types(void) } type_init(sysbus_ahci_register_types) + +void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **tab) +{ +AHCIPCIState *d = ICH_AHCI(dev); +AHCIState *ahci = &d->ahci; +int i; + +for (i = 0; i < ahci->ports; i++) { +if (tab[i] == NULL) { +continue; +} +ide_create_drive(&ahci->dev[i].port, 0, tab[i]); +} + +} diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h index 1543df7..b6dc64e 100644 --- a/hw/ide/ahci.h +++ b/hw/ide/ahci.h @@ -332,4 +332,6 @@ void ahci_uninit(AHCIState *s); void ahci_reset(AHCIState *s); +void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **tab); + #endif /* HW_IDE_AHCI_H */ -- 1.9.3
[Qemu-devel] [PATCH v2 0/6] Q35: Implement -cdrom/-hda sugar
The Q35 board initialization does not currently bother to look for any drives added by the various syntactical sugar shorthands to be added to the AHCI HBA. These include -hda through -hdd, -cdrom, and -drive if=ide shorthands. An obstacle to having implemented this sooner is debate over whether or not to add an additional interface type, and how to manage the different units-per-bus mappings of various HBA implementations. This patch series: (1) Does not add IF_AHCI, but reuses IF_IDE (2) Allows the if_max_devs table to be overridden (3) Adds this override to the Q35 board type. (4) Finally, adds implementation to Q35 initialization. History: V2: - Adjusted language in patch #1's commit message. (drive if=none will NOT trigger warnings) - Removed superfluous warning with bad phrasing in patch #1 - Removed if_get_max_devs from patch #2 and added to patch #4 - Added an assertion to patch #2 - Added more detail to patch #3's commit message - Specified that Patch #3 will affect old Q35 machine types - Changed fprintf to error_report in patch #4 - Replaced max_bus parameter in ide_drive_get with 'n', size of array - Updated calls to ide_drive_get in other boards - Adjusted language in patch #6's commit message. (Removed reference to patch #5.) V1: - Re-uses ide_drive_get instead of ahci_drive_get - Adds units-per-bus property to all Q35 machines - Changes orphan scanning to exclude IF_NONE and automatically added drives - Renames 'units-per-idebus' to 'units-per-default-bus' And allows override of any one IF type (block_default) RFC2: - Rewrote series to avoid the creation of IF_AHCI. John Snow (6): blockdev: Orphaned drive search blockdev: Allow overriding if_max_dev property pc/vl: Add units-per-default-bus property ide: Update ide_drive_get to be HBA agnostic qtest/bios-tables: Correct Q35 command line q35/ahci: Pick up -cdrom and -hda options blockdev.c| 56 ++- hw/alpha/dp264.c | 2 +- hw/i386/pc.c | 1 + hw/i386/pc_piix.c | 2 +- hw/i386/pc_q35.c | 7 +- hw/ide/ahci.c | 15 + hw/ide/ahci.h | 2 ++ hw/ide/core.c | 21 +- hw/mips/mips_fulong2e.c | 2 +- hw/mips/mips_malta.c | 2 +- hw/mips/mips_r4k.c| 2 +- hw/ppc/mac_newworld.c | 2 +- hw/ppc/mac_oldworld.c | 2 +- hw/ppc/prep.c | 2 +- hw/sparc64/sun4u.c| 2 +- include/hw/boards.h | 2 ++ include/sysemu/blockdev.h | 5 + tests/bios-tables-test.c | 10 - vl.c | 18 ++- 19 files changed, 131 insertions(+), 24 deletions(-) -- 1.9.3
[Qemu-devel] [PATCH v2 2/6] blockdev: Allow overriding if_max_dev property
The if_max_devs table as in the past been an immutable default that controls the mapping of index => (bus,unit) for all boards and all HBAs for each interface type. Since adding this mapping information to the HBA device itself is currently unwieldly from the perspective of retrieving this information at option parsing time (e.g, within drive_new), we consider the alternative of marking the if_max_devs table mutable so that later configuration and initialization can adjust the mapping at will, but only up until a drive is added, at which point the mapping is finalized. Signed-off-by: John Snow --- blockdev.c| 26 +- include/sysemu/blockdev.h | 2 ++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/blockdev.c b/blockdev.c index 81398e7..9b05f1b 100644 --- a/blockdev.c +++ b/blockdev.c @@ -60,7 +60,7 @@ static const char *const if_name[IF_COUNT] = { [IF_XEN] = "xen", }; -static const int if_max_devs[IF_COUNT] = { +static int if_max_devs[IF_COUNT] = { /* * Do not change these numbers! They govern how drive option * index maps to unit and bus. That mapping is ABI. @@ -79,6 +79,30 @@ static const int if_max_devs[IF_COUNT] = { [IF_SCSI] = 7, }; +/** + * Boards may call this to offer board-by-board overrides + * of the default, global values. + */ +void override_max_devs(BlockInterfaceType type, int max_devs) +{ +DriveInfo *dinfo; + +if (max_devs <= 0) { +return; +} + +QTAILQ_FOREACH(dinfo, &drives, next) { +if (dinfo->type == type) { +fprintf(stderr, "Cannot override units-per-bus property of" +" the %s interface, because a drive of that type has" +" already been added.\n", if_name[type]); +g_assert_not_reached(); +} +} + +if_max_devs[type] = max_devs; +} + /* * We automatically delete the drive when a device using it gets * unplugged. Questionable feature, but we can't just drop it. diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 80f768d..f23d495 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -46,6 +46,8 @@ struct DriveInfo { QTAILQ_ENTRY(DriveInfo) next; }; +void override_max_devs(BlockInterfaceType type, int max_devs); + DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); bool drive_check_orphaned(void); DriveInfo *drive_get_by_index(BlockInterfaceType type, int index); -- 1.9.3
[Qemu-devel] [PATCH v2 4/6] ide: Update ide_drive_get to be HBA agnostic
Instead of duplicating the logic for the if_ide (bus,unit) mappings, rely on the blockdev layer for managing those mappings for us, and use the drive_get_by_index call instead. This allows ide_drive_get to work for AHCI HBAs as well, and can be used in the Q35 initialization. Lastly, change the nature of the argument to ide_drive_get so that represents the number of total drives we can support, and not the total number of buses. This will prevent array overflows if the units-per-default-bus property ever needs to be adjusted for compatibility reasons. Signed-off-by: John Snow --- blockdev.c| 9 + hw/alpha/dp264.c | 2 +- hw/i386/pc_piix.c | 2 +- hw/ide/core.c | 21 +++-- hw/mips/mips_fulong2e.c | 2 +- hw/mips/mips_malta.c | 2 +- hw/mips/mips_r4k.c| 2 +- hw/ppc/mac_newworld.c | 2 +- hw/ppc/mac_oldworld.c | 2 +- hw/ppc/prep.c | 2 +- hw/sparc64/sun4u.c| 2 +- include/sysemu/blockdev.h | 1 + 12 files changed, 34 insertions(+), 15 deletions(-) diff --git a/blockdev.c b/blockdev.c index 9b05f1b..ffaad39 100644 --- a/blockdev.c +++ b/blockdev.c @@ -135,6 +135,15 @@ void blockdev_auto_del(BlockDriverState *bs) } } +int drive_get_max_devs(BlockInterfaceType type) +{ +if (type >= IF_IDE && type < IF_COUNT) { +return if_max_devs[type]; +} + +return 0; +} + static int drive_index_to_bus_id(BlockInterfaceType type, int index) { int max_devs = if_max_devs[type]; diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index b178a03..ab61bb6 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -97,7 +97,7 @@ static void clipper_init(MachineState *machine) /* IDE disk setup. */ { DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; -ide_drive_get(hd, MAX_IDE_BUS); +ide_drive_get(hd, MAX_IDE_BUS * MAX_IDE_DEVS); pci_cmd646_ide_init(pci_bus, hd, 0); } diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 103d756..2760c81 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -239,7 +239,7 @@ static void pc_init1(MachineState *machine, pc_nic_init(isa_bus, pci_bus); -ide_drive_get(hd, MAX_IDE_BUS); +ide_drive_get(hd, MAX_IDE_BUS * MAX_IDE_DEVS); if (pci_enabled) { PCIDevice *dev; if (xen_enabled()) { diff --git a/hw/ide/core.c b/hw/ide/core.c index 190700a..e7c1050 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2558,16 +2558,25 @@ const VMStateDescription vmstate_ide_bus = { } }; -void ide_drive_get(DriveInfo **hd, int max_bus) +void ide_drive_get(DriveInfo **hd, int n) { int i; +int highest_bus = drive_get_max_bus(IF_IDE) + 1; +int n_buses = n / drive_get_max_devs(IF_IDE); -if (drive_get_max_bus(IF_IDE) >= max_bus) { -fprintf(stderr, "qemu: too many IDE bus: %d\n", max_bus); -exit(1); +/* Note: The number of actual buses available is not known. + * We compute this based on the size of the DriveInfo* array, n. + * If it is less than (drive_get_max_devs(IF_IDE) * num_real_buses), + * We will stop looking for drives prematurely instead of overfilling + * the array. */ + +if (highest_bus > n_buses) { +error_report("Warning: Too many IDE buses defined (%d > %d)", + highest_bus, n_buses); } -for(i = 0; i < max_bus * MAX_IDE_DEVS; i++) { -hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); +for (i = 0; i < n; i++) { +hd[i] = drive_get_by_index(IF_IDE, i); } + } diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index be286da..0239438 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -350,7 +350,7 @@ static void mips_fulong2e_init(MachineState *machine) pci_bus = bonito_init((qemu_irq *)&(env->irq[2])); /* South bridge */ -ide_drive_get(hd, MAX_IDE_BUS); +ide_drive_get(hd, MAX_IDE_BUS * MAX_IDE_DEVS); isa_bus = vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 0)); if (!isa_bus) { diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 2d87de9..e19a125 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -1147,7 +1147,7 @@ void mips_malta_init(MachineState *machine) pci_bus = gt64120_register(isa_irq); /* Southbridge */ -ide_drive_get(hd, MAX_IDE_BUS); +ide_drive_get(hd, MAX_IDE_BUS * MAX_IDE_DEVS); piix4_devfn = piix4_init(pci_bus, &isa_bus, 80); diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index e219766..0d6f7ed 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -294,7 +294,7 @@ void mips_r4k_init(MachineState *machine) if (nd_table[0].used) isa_ne2000_init(isa_bus, 0x300, 9, &nd_table[0]); -ide_drive_get(hd, MAX_IDE_BUS); +ide_drive_get(hd, MAX_IDE_BUS * MAX_IDE_DEVS); for(i = 0; i < MAX_IDE_BUS; i++)
Re: [Qemu-devel] [PATCH v2 6/6] q35/ahci: Pick up -cdrom and -hda options
On 09/30/2014 03:54 AM, Markus Armbruster wrote: John Snow writes: This patch implements the backend for the Q35 board for us to be able to pick up and use drives defined by the -cdrom, -hda, or -drive if=ide shorthand options. Signed-off-by: John Snow --- hw/i386/pc_q35.c | 4 hw/ide/ahci.c| 15 +++ hw/ide/ahci.h| 2 ++ 3 files changed, 21 insertions(+) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index b28ddbb..bb0dc8e 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -86,6 +86,7 @@ static void pc_q35_init(MachineState *machine) DeviceState *icc_bridge; PcGuestInfo *guest_info; ram_addr_t lowmem; +DriveInfo *hd[MAX_SATA_PORTS]; /* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory * and 256 Mbytes for PCI Express Enhanced Configuration Access Mapping @@ -253,6 +254,9 @@ static void pc_q35_init(MachineState *machine) true, "ich9-ahci"); idebus[0] = qdev_get_child_bus(&ahci->qdev, "ide.0"); idebus[1] = qdev_get_child_bus(&ahci->qdev, "ide.1"); +g_assert_cmpint(MAX_SATA_PORTS, ==, ICH_AHCI(ahci)->ahci.ports); +ide_drive_get(hd, ICH_AHCI(ahci)->ahci.ports); +ahci_ide_create_devs(ahci, hd); The assertion is new since v1, and a bit more interesting than it looks on first glance. It protects the two calls following it, by ensuring the array has space for the ports. MAX_SATA_PORTS is defined to 6 in this file. ICH_AHCI(ahci)->ahci.ports is initialized by ahci_init() to its ports argument. pci_ich9_ahci_init() passes literal 6. Oookay. The assertion is more restrictive than required for correctness: >= would do. I don't mind. It's more of a warning that these two values are, for now, considered equivalent. If that should change in the future for some unthinkable reason, this assertion will help remind whoever changes it. For now, Q35 uses ICH9. ICH9's AHCI controller has 6 ports. This should always be the case. I could, I suppose, make the HD array in the heap, and ask the ICH9 how many ports it has, etc... The assertion was a quick, equally authoritative way that is not likely to need changing. It's tempting to do ide_drive_get(hd, ARRAY_SIZE(hd)); for more obvious correctness, except that'll screw with the detection of extra drives if ahci.ports ever becomes < MAX_SATA_PORTS. Good. if (usb_enabled(false)) { /* Should we create 6 UHCI according to ich9 spec? */ diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 8978643..79abb6a 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1419,3 +1419,18 @@ static void sysbus_ahci_register_types(void) } type_init(sysbus_ahci_register_types) + +void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **tab) Elsewhere, we call it DriveInfo **hd. I'd stick to the common name. +{ +AHCIPCIState *d = ICH_AHCI(dev); +AHCIState *ahci = &d->ahci; +int i; + +for (i = 0; i < ahci->ports; i++) { +if (tab[i] == NULL) { +continue; +} +ide_create_drive(&ahci->dev[i].port, 0, tab[i]); +} + +} diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h index 1543df7..b6dc64e 100644 --- a/hw/ide/ahci.h +++ b/hw/ide/ahci.h @@ -332,4 +332,6 @@ void ahci_uninit(AHCIState *s); void ahci_reset(AHCIState *s); +void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **tab); + #endif /* HW_IDE_AHCI_H */
Re: [Qemu-devel] [PATCH v2 4/6] ide: Update ide_drive_get to be HBA agnostic
On 09/30/2014 03:38 AM, Markus Armbruster wrote: John Snow writes: Instead of duplicating the logic for the if_ide (bus,unit) mappings, rely on the blockdev layer for managing those mappings for us, and use the drive_get_by_index call instead. This allows ide_drive_get to work for AHCI HBAs as well, and can be used in the Q35 initialization. Lastly, change the nature of the argument to ide_drive_get so that represents the number of total drives we can support, and not the total number of buses. This will prevent array overflows if the units-per-default-bus property ever needs to be adjusted for compatibility reasons. Signed-off-by: John Snow --- blockdev.c| 9 + hw/alpha/dp264.c | 2 +- hw/i386/pc_piix.c | 2 +- hw/ide/core.c | 21 +++-- hw/mips/mips_fulong2e.c | 2 +- hw/mips/mips_malta.c | 2 +- hw/mips/mips_r4k.c| 2 +- hw/ppc/mac_newworld.c | 2 +- hw/ppc/mac_oldworld.c | 2 +- hw/ppc/prep.c | 2 +- hw/sparc64/sun4u.c| 2 +- include/sysemu/blockdev.h | 1 + 12 files changed, 34 insertions(+), 15 deletions(-) diff --git a/blockdev.c b/blockdev.c index 9b05f1b..ffaad39 100644 --- a/blockdev.c +++ b/blockdev.c @@ -135,6 +135,15 @@ void blockdev_auto_del(BlockDriverState *bs) } } +int drive_get_max_devs(BlockInterfaceType type) +{ +if (type >= IF_IDE && type < IF_COUNT) { +return if_max_devs[type]; +} + +return 0; +} + drive_get_max_bus() returns -1 for a type without drives. Includes invalid types. When it returns a non-negative number, a drive on that bus exists. Your drive_get_max_devs() has a similar name, but different semantics: it returns a positive number when the implied HBA supports multiple buses, else zero. The "else" includes invalid types. When it returns a positive number, then the HBA can take that many units per bus. No big deal, but functions comments would be nice. Should invalid type be treated as a programming error instead? static int drive_index_to_bus_id(BlockInterfaceType type, int index) { int max_devs = if_max_devs[type]; diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index b178a03..ab61bb6 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -97,7 +97,7 @@ static void clipper_init(MachineState *machine) /* IDE disk setup. */ { DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; -ide_drive_get(hd, MAX_IDE_BUS); +ide_drive_get(hd, MAX_IDE_BUS * MAX_IDE_DEVS); More obviously correct would be ide_drive_get(hd, ARRAY_SIZE(hd)); Less so here, because the declaration is right next to the use, more so elsewhere, where it isn't. pci_cmd646_ide_init(pci_bus, hd, 0); } diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 103d756..2760c81 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -239,7 +239,7 @@ static void pc_init1(MachineState *machine, pc_nic_init(isa_bus, pci_bus); -ide_drive_get(hd, MAX_IDE_BUS); +ide_drive_get(hd, MAX_IDE_BUS * MAX_IDE_DEVS); if (pci_enabled) { PCIDevice *dev; if (xen_enabled()) { diff --git a/hw/ide/core.c b/hw/ide/core.c index 190700a..e7c1050 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2558,16 +2558,25 @@ const VMStateDescription vmstate_ide_bus = { } }; -void ide_drive_get(DriveInfo **hd, int max_bus) +void ide_drive_get(DriveInfo **hd, int n) { int i; +int highest_bus = drive_get_max_bus(IF_IDE) + 1; Actually, this is the "highest bus" + 1 :) +int n_buses = n / drive_get_max_devs(IF_IDE); What if drive_get_max_devs(IF_IDE) returns 0? You could side-step the question by using drive_index_to_bus_id(n). -if (drive_get_max_bus(IF_IDE) >= max_bus) { -fprintf(stderr, "qemu: too many IDE bus: %d\n", max_bus); -exit(1); Before: fatal. +/* Note: The number of actual buses available is not known. + * We compute this based on the size of the DriveInfo* array, n. + * If it is less than (drive_get_max_devs(IF_IDE) * num_real_buses), + * We will stop looking for drives prematurely instead of overfilling + * the array. */ + You might want to consider winged comments: /* * Note: The number of actual buses available is not known. * We compute this based on the size of the DriveInfo* array, n. * If it is less than (drive_get_max_devs(IF_IDE) * num_real_buses), * We will stop looking for drives prematurely instead of overfilling * the array. */ +if (highest_bus > n_buses) { +error_report("Warning: Too many IDE buses defined (%d > %d)", + highest_bus, n_buses); After: warning. Why? I'll fix the divide by zero and address the other comments. I can adjust the semantics to match the other function -- sometimes I don't
[Qemu-devel] [PATCH v3 0/6] Q35: Implement -cdrom/-hda sugar
The Q35 board initialization does not currently bother to look for any drives added by the various syntactical sugar shorthands to be added to the AHCI HBA. These include -hda through -hdd, -cdrom, and -drive if=ide shorthands. An obstacle to having implemented this sooner is debate over whether or not to add an additional interface type, and how to manage the different units-per-bus mappings of various HBA implementations. This patch series: (1) Does not add IF_AHCI, but reuses IF_IDE (2) Allows the if_max_devs table to be overridden (3) Adds this override to the Q35 board type. (4) Finally, adds implementation to Q35 initialization. History: V3: - renamed a variable for consistency ("tab" to "hd") - Now uses ARRAY_SIZE() for calls to ide_drive_get where applicable - Re-added a call to exit() for the error pathway of ide_drive_get - Adjusted drive_get_max_devs semantics to be similar to drive_get_max_bus and added documentation - Removed any possibility of a divide-by-zero in ide_drive_get V2: - Adjusted language in patch #1's commit message. (drive if=none will NOT trigger warnings) - Removed superfluous warning with bad phrasing in patch #1 - Removed if_get_max_devs from patch #2 and added to patch #4 - Added an assertion to patch #2 - Added more detail to patch #3's commit message - Specified that Patch #3 will affect old Q35 machine types - Changed fprintf to error_report in patch #4 - Replaced max_bus parameter in ide_drive_get with 'n', size of array - Updated calls to ide_drive_get in other boards - Adjusted language in patch #6's commit message. (Removed reference to patch #5.) V1: - Re-uses ide_drive_get instead of ahci_drive_get - Adds units-per-bus property to all Q35 machines - Changes orphan scanning to exclude IF_NONE and automatically added drives - Renames 'units-per-idebus' to 'units-per-default-bus' And allows override of any one IF type (block_default) RFC2: - Rewrote series to avoid the creation of IF_AHCI. John Snow (6): blockdev: Orphaned drive search blockdev: Allow overriding if_max_dev property pc/vl: Add units-per-default-bus property ide: Update ide_drive_get to be HBA agnostic qtest/bios-tables: Correct Q35 command line q35/ahci: Pick up -cdrom and -hda options blockdev.c| 64 ++- hw/alpha/dp264.c | 2 +- hw/i386/pc.c | 1 + hw/i386/pc_piix.c | 2 +- hw/i386/pc_q35.c | 7 +- hw/ide/ahci.c | 15 +++ hw/ide/ahci.h | 2 ++ hw/ide/core.c | 22 hw/mips/mips_fulong2e.c | 2 +- hw/mips/mips_malta.c | 2 +- hw/mips/mips_r4k.c| 2 +- hw/ppc/mac_newworld.c | 2 +- hw/ppc/mac_oldworld.c | 2 +- hw/ppc/prep.c | 2 +- hw/sparc64/sun4u.c| 2 +- include/hw/boards.h | 2 ++ include/sysemu/blockdev.h | 5 tests/bios-tables-test.c | 10 +++- vl.c | 18 - 19 files changed, 141 insertions(+), 23 deletions(-) -- 1.9.3
[Qemu-devel] [PATCH v3 4/6] ide: Update ide_drive_get to be HBA agnostic
Instead of duplicating the logic for the if_ide (bus,unit) mappings, rely on the blockdev layer for managing those mappings for us, and use the drive_get_by_index call instead. This allows ide_drive_get to work for AHCI HBAs as well, and can be used in the Q35 initialization. Lastly, change the nature of the argument to ide_drive_get so that represents the number of total drives we can support, and not the total number of buses. This will prevent array overflows if the units-per-default-bus property ever needs to be adjusted for compatibility reasons. Signed-off-by: John Snow --- blockdev.c| 17 + hw/alpha/dp264.c | 2 +- hw/i386/pc_piix.c | 2 +- hw/ide/core.c | 22 +- hw/mips/mips_fulong2e.c | 2 +- hw/mips/mips_malta.c | 2 +- hw/mips/mips_r4k.c| 2 +- hw/ppc/mac_newworld.c | 2 +- hw/ppc/mac_oldworld.c | 2 +- hw/ppc/prep.c | 2 +- hw/sparc64/sun4u.c| 2 +- include/sysemu/blockdev.h | 1 + 12 files changed, 44 insertions(+), 14 deletions(-) diff --git a/blockdev.c b/blockdev.c index 1d9ab7f..4489090 100644 --- a/blockdev.c +++ b/blockdev.c @@ -135,6 +135,23 @@ void blockdev_auto_del(BlockDriverState *bs) } } +/** + * Returns the current mapping of how many units per bus + * a particular interface can support. + * + * A positive integer indicates n units per bus. + * 0 implies the mapping has not been established. + * -1 indicates an invalid BlockInterfaceType was given. + */ +int drive_get_max_devs(BlockInterfaceType type) +{ +if (type >= IF_IDE && type < IF_COUNT) { +return if_max_devs[type]; +} + +return -1; +} + static int drive_index_to_bus_id(BlockInterfaceType type, int index) { int max_devs = if_max_devs[type]; diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index b178a03..84a55e4 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -97,7 +97,7 @@ static void clipper_init(MachineState *machine) /* IDE disk setup. */ { DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; -ide_drive_get(hd, MAX_IDE_BUS); +ide_drive_get(hd, ARRAY_SIZE(hd)); pci_cmd646_ide_init(pci_bus, hd, 0); } diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 103d756..4384633 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -239,7 +239,7 @@ static void pc_init1(MachineState *machine, pc_nic_init(isa_bus, pci_bus); -ide_drive_get(hd, MAX_IDE_BUS); +ide_drive_get(hd, ARRAY_SIZE(hd)); if (pci_enabled) { PCIDevice *dev; if (xen_enabled()) { diff --git a/hw/ide/core.c b/hw/ide/core.c index 190700a..ae85428 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2558,16 +2558,28 @@ const VMStateDescription vmstate_ide_bus = { } }; -void ide_drive_get(DriveInfo **hd, int max_bus) +void ide_drive_get(DriveInfo **hd, int n) { int i; +int highest_bus = drive_get_max_bus(IF_IDE) + 1; +int max_devs = drive_get_max_devs(IF_IDE); +int n_buses = max_devs ? (n / max_devs) : n; -if (drive_get_max_bus(IF_IDE) >= max_bus) { -fprintf(stderr, "qemu: too many IDE bus: %d\n", max_bus); +/* + * Note: The number of actual buses available is not known. + * We compute this based on the size of the DriveInfo* array, n. + * If it is less than max_devs * , + * We will stop looking for drives prematurely instead of overfilling + * the array. + */ + +if (highest_bus > n_buses) { +error_report("Too many IDE buses defined (%d > %d)", + highest_bus, n_buses); exit(1); } -for(i = 0; i < max_bus * MAX_IDE_DEVS; i++) { -hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); +for (i = 0; i < n; i++) { +hd[i] = drive_get_by_index(IF_IDE, i); } } diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index be286da..29cd708 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -350,7 +350,7 @@ static void mips_fulong2e_init(MachineState *machine) pci_bus = bonito_init((qemu_irq *)&(env->irq[2])); /* South bridge */ -ide_drive_get(hd, MAX_IDE_BUS); +ide_drive_get(hd, ARRAY_SIZE(hd)); isa_bus = vt82c686b_init(pci_bus, PCI_DEVFN(FULONG2E_VIA_SLOT, 0)); if (!isa_bus) { diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 2d87de9..b20807c 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -1147,7 +1147,7 @@ void mips_malta_init(MachineState *machine) pci_bus = gt64120_register(isa_irq); /* Southbridge */ -ide_drive_get(hd, MAX_IDE_BUS); +ide_drive_get(hd, ARRAY_SIZE(hd)); piix4_devfn = piix4_init(pci_bus, &isa_bus, 80); diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index e219766..93606a4 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -294,7 +294,7 @@ void mi
[Qemu-devel] [PATCH v3 6/6] q35/ahci: Pick up -cdrom and -hda options
This patch implements the backend for the Q35 board for us to be able to pick up and use drives defined by the -cdrom, -hda, or -drive if=ide shorthand options. Signed-off-by: John Snow --- hw/i386/pc_q35.c | 4 hw/ide/ahci.c| 15 +++ hw/ide/ahci.h| 2 ++ 3 files changed, 21 insertions(+) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index b28ddbb..bb0dc8e 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -86,6 +86,7 @@ static void pc_q35_init(MachineState *machine) DeviceState *icc_bridge; PcGuestInfo *guest_info; ram_addr_t lowmem; +DriveInfo *hd[MAX_SATA_PORTS]; /* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory * and 256 Mbytes for PCI Express Enhanced Configuration Access Mapping @@ -253,6 +254,9 @@ static void pc_q35_init(MachineState *machine) true, "ich9-ahci"); idebus[0] = qdev_get_child_bus(&ahci->qdev, "ide.0"); idebus[1] = qdev_get_child_bus(&ahci->qdev, "ide.1"); +g_assert_cmpint(MAX_SATA_PORTS, ==, ICH_AHCI(ahci)->ahci.ports); +ide_drive_get(hd, ICH_AHCI(ahci)->ahci.ports); +ahci_ide_create_devs(ahci, hd); if (usb_enabled(false)) { /* Should we create 6 UHCI according to ich9 spec? */ diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 8978643..063730e 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1419,3 +1419,18 @@ static void sysbus_ahci_register_types(void) } type_init(sysbus_ahci_register_types) + +void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **hd) +{ +AHCIPCIState *d = ICH_AHCI(dev); +AHCIState *ahci = &d->ahci; +int i; + +for (i = 0; i < ahci->ports; i++) { +if (hd[i] == NULL) { +continue; +} +ide_create_drive(&ahci->dev[i].port, 0, hd[i]); +} + +} diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h index 1543df7..e223258 100644 --- a/hw/ide/ahci.h +++ b/hw/ide/ahci.h @@ -332,4 +332,6 @@ void ahci_uninit(AHCIState *s); void ahci_reset(AHCIState *s); +void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **hd); + #endif /* HW_IDE_AHCI_H */ -- 1.9.3
[Qemu-devel] [PATCH v3 3/6] pc/vl: Add units-per-default-bus property
This patch adds the 'units_per_default_bus' property which allows individual boards to declare their desired index => (bus,unit) mapping for their default HBA, so that boards such as Q35 can specify that its default if_ide HBA, AHCI, only accepts one unit per bus. This property only overrides the mapping for drives matching the block_default_type interface. This patch also adds this property to *all* past and present Q35 machine types. This retroactive addition is justified because the previous erroneous index=>(bus,unit) mappings caused by lack of such a property were not utilized due to lack of initialization code in the Q35 init routine. Further, semantically, the Q35 board type has always had the property that its default HBA, AHCI, only accepts one unit per bus. The new code added to add devices to drives relies upon the accuracy of this mapping. Thus, the property is applied retroactively to reduce complexity of allowing IDE HBAs with different units per bus. Examples: Prior to this patch, all IDE HBAs were assumed to use 2 units per bus (Master, Slave). When using Q35 and AHCI, however, we only allow one unit per bus. -hdb foo.qcow2 would become index=1, or bus=0,unit=1. -hdd foo.qcow2 would become index=3, or bus=1,unit=1. -drive file=foo.qcow2,index=5 becomes bus=2,unit=1. These are invalid for AHCI. They now become, under Q35 only: -hdb foo.qcow2 --> index=1, bus=1, unit=0. -hdd foo.qcow2 --> index=3, bus=3, unit=0. -drive file=foo.qcow2,index=5 --> bus=5,unit=0. The mapping is adjusted based on the fact that the default IF for the Q35 machine type is IF_IDE, and units-per-default-bus overrides the IDE mapping from its default of 2 units per bus to just 1 unit per bus. Signed-off-by: John Snow --- hw/i386/pc.c| 1 + hw/i386/pc_q35.c| 3 ++- include/hw/boards.h | 2 ++ vl.c| 8 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 82a7daa..d045e8b 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1524,6 +1524,7 @@ static void pc_generic_machine_class_init(ObjectClass *oc, void *data) mc->hot_add_cpu = qm->hot_add_cpu; mc->kvm_type = qm->kvm_type; mc->block_default_type = qm->block_default_type; +mc->units_per_default_bus = qm->units_per_default_bus; mc->max_cpus = qm->max_cpus; mc->no_serial = qm->no_serial; mc->no_parallel = qm->no_parallel; diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index d4a907c..b28ddbb 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -344,7 +344,8 @@ static void pc_q35_init_1_4(MachineState *machine) #define PC_Q35_MACHINE_OPTIONS \ PC_DEFAULT_MACHINE_OPTIONS, \ .desc = "Standard PC (Q35 + ICH9, 2009)", \ -.hot_add_cpu = pc_hot_add_cpu +.hot_add_cpu = pc_hot_add_cpu, \ +.units_per_default_bus = 1 #define PC_Q35_2_2_MACHINE_OPTIONS \ PC_Q35_MACHINE_OPTIONS, \ diff --git a/include/hw/boards.h b/include/hw/boards.h index dfb6718..663f16a 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -28,6 +28,7 @@ struct QEMUMachine { QEMUMachineHotAddCPUFunc *hot_add_cpu; QEMUMachineGetKvmtypeFunc *kvm_type; BlockInterfaceType block_default_type; +int units_per_default_bus; int max_cpus; unsigned int no_serial:1, no_parallel:1, @@ -86,6 +87,7 @@ struct MachineClass { int (*kvm_type)(const char *arg); BlockInterfaceType block_default_type; +int units_per_default_bus; int max_cpus; unsigned int no_serial:1, no_parallel:1, diff --git a/vl.c b/vl.c index 6500472..940b149 100644 --- a/vl.c +++ b/vl.c @@ -1588,6 +1588,7 @@ static void machine_class_init(ObjectClass *oc, void *data) mc->hot_add_cpu = qm->hot_add_cpu; mc->kvm_type = qm->kvm_type; mc->block_default_type = qm->block_default_type; +mc->units_per_default_bus = qm->units_per_default_bus; mc->max_cpus = qm->max_cpus; mc->no_serial = qm->no_serial; mc->no_parallel = qm->no_parallel; @@ -4378,6 +4379,13 @@ int main(int argc, char **argv, char **envp) blk_mig_init(); ram_mig_init(); +/* If the currently selected machine wishes to override the units-per-bus + * property of its default HBA interface type, do so now. */ +if (machine_class->units_per_default_bus) { +override_max_devs(machine_class->block_default_type, + machine_class->units_per_default_bus); +} + /* open the virtual block devices */ if (snapshot) qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot, NULL, 0); -- 1.9.3
[Qemu-devel] [PATCH v3 2/6] blockdev: Allow overriding if_max_dev property
The if_max_devs table as in the past been an immutable default that controls the mapping of index => (bus,unit) for all boards and all HBAs for each interface type. Since adding this mapping information to the HBA device itself is currently unwieldly from the perspective of retrieving this information at option parsing time (e.g, within drive_new), we consider the alternative of marking the if_max_devs table mutable so that later configuration and initialization can adjust the mapping at will, but only up until a drive is added, at which point the mapping is finalized. Signed-off-by: John Snow --- blockdev.c| 26 +- include/sysemu/blockdev.h | 2 ++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/blockdev.c b/blockdev.c index d2ad065..1d9ab7f 100644 --- a/blockdev.c +++ b/blockdev.c @@ -60,7 +60,7 @@ static const char *const if_name[IF_COUNT] = { [IF_XEN] = "xen", }; -static const int if_max_devs[IF_COUNT] = { +static int if_max_devs[IF_COUNT] = { /* * Do not change these numbers! They govern how drive option * index maps to unit and bus. That mapping is ABI. @@ -79,6 +79,30 @@ static const int if_max_devs[IF_COUNT] = { [IF_SCSI] = 7, }; +/** + * Boards may call this to offer board-by-board overrides + * of the default, global values. + */ +void override_max_devs(BlockInterfaceType type, int max_devs) +{ +DriveInfo *dinfo; + +if (max_devs <= 0) { +return; +} + +QTAILQ_FOREACH(dinfo, &drives, next) { +if (dinfo->type == type) { +fprintf(stderr, "Cannot override units-per-bus property of" +" the %s interface, because a drive of that type has" +" already been added.\n", if_name[type]); +g_assert_not_reached(); +} +} + +if_max_devs[type] = max_devs; +} + /* * We automatically delete the drive when a device using it gets * unplugged. Questionable feature, but we can't just drop it. diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 3040286..a4033d4 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -46,6 +46,8 @@ struct DriveInfo { QTAILQ_ENTRY(DriveInfo) next; }; +void override_max_devs(BlockInterfaceType type, int max_devs); + DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); bool drive_check_orphaned(void); DriveInfo *drive_get_by_index(BlockInterfaceType type, int index); -- 1.9.3
[Qemu-devel] [PATCH v3 5/6] qtest/bios-tables: Correct Q35 command line
If the Q35 board types are to begin recognizing and decoding syntactic sugar for drive/device declarations, then workarounds found within the qtests suite need to be adjusted to prevent any test failures after the fix. bios-tables-test improperly uses this cli: -drive file=etc,id=hd -device ide-hd,drive=hd Which will create a drive and device due to the lack of specifying if=none. Then, it will attempt to create a second device and fail. This patch corrects this test to always use the full, non-sugared -device/-drive syntax for both PC and Q35. Signed-off-by: John Snow --- tests/bios-tables-test.c | 10 -- 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c index 602932b..9e4d205 100644 --- a/tests/bios-tables-test.c +++ b/tests/bios-tables-test.c @@ -714,14 +714,12 @@ static void test_acpi_one(const char *params, test_data *data) uint8_t signature_high; uint16_t signature; int i; -const char *device = ""; -if (!g_strcmp0(data->machine, MACHINE_Q35)) { -device = ",id=hd -device ide-hd,drive=hd"; -} +args = g_strdup_printf("-net none -display none %s " + "-drive id=hd0,if=none,file=%s " + "-device ide-hd,drive=hd0 ", + params ? params : "", disk); -args = g_strdup_printf("-net none -display none %s -drive file=%s%s,", - params ? params : "", disk, device); qtest_start(args); /* Wait at most 1 minute */ -- 1.9.3
[Qemu-devel] [PATCH v3 1/6] blockdev: Orphaned drive search
When users use command line options like -hda, -cdrom, or even -drive if=ide, it is up to the board initialization routines to pick up these drives and create backing devices for them. Some boards, like Q35, have not been doing this. However, there is no warning explaining why certain drive specifications are just silently ignored, so this function adds a check to print some warnings to assist users in debugging these sorts of issues in the future. This patch will not warn about drives added with if_none, for which it is not possible to tell in advance if the omission of a backing device is an issue. A warning in these cases is considered appropriate. Signed-off-by: John Snow --- blockdev.c| 21 + include/sysemu/blockdev.h | 2 ++ vl.c | 10 +- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/blockdev.c b/blockdev.c index ad43648..d2ad065 100644 --- a/blockdev.c +++ b/blockdev.c @@ -166,6 +166,27 @@ DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit) return NULL; } +bool drive_check_orphaned(void) +{ +DriveInfo *dinfo; +bool rs = false; + +QTAILQ_FOREACH(dinfo, &drives, next) { +/* If dinfo->bdrv->dev is NULL, it has no device attached. */ +/* Unless this is a default drive, this may be an oversight. */ +if (!dinfo->bdrv->dev && !dinfo->is_default && +dinfo->type != IF_NONE) { +fprintf(stderr, "Warning: Orphaned drive without device: " +"id=%s,file=%s,if=%s,bus=%d,unit=%d\n", +dinfo->id, dinfo->bdrv->filename, if_name[dinfo->type], +dinfo->bus, dinfo->unit); +rs = true; +} +} + +return rs; +} + DriveInfo *drive_get_by_index(BlockInterfaceType type, int index) { return drive_get(type, diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index abec381..3040286 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -38,6 +38,7 @@ struct DriveInfo { int unit; int auto_del; /* see blockdev_mark_auto_del() */ bool enable_auto_del; /* Only for legacy drive_new() */ +bool is_default;/* Added by default_drive() ? */ int media_cd; int cyls, heads, secs, trans; QemuOpts *opts; @@ -46,6 +47,7 @@ struct DriveInfo { }; DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); +bool drive_check_orphaned(void); DriveInfo *drive_get_by_index(BlockInterfaceType type, int index); int drive_get_max_bus(BlockInterfaceType type); DriveInfo *drive_get_next(BlockInterfaceType type); diff --git a/vl.c b/vl.c index dbdca59..6500472 100644 --- a/vl.c +++ b/vl.c @@ -1168,6 +1168,7 @@ static void default_drive(int enable, int snapshot, BlockInterfaceType type, int index, const char *optstr) { QemuOpts *opts; +DriveInfo *dinfo; if (!enable || drive_get_by_index(type, index)) { return; @@ -1177,9 +1178,13 @@ static void default_drive(int enable, int snapshot, BlockInterfaceType type, if (snapshot) { drive_enable_snapshot(opts, NULL); } -if (!drive_new(opts, type)) { + +dinfo = drive_new(opts, type); +if (!dinfo) { exit(1); } +dinfo->is_default = true; + } void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque) @@ -4458,6 +4463,9 @@ int main(int argc, char **argv, char **envp) if (qemu_opts_foreach(qemu_find_opts("device"), device_init_func, NULL, 1) != 0) exit(1); +/* Did we create any drives that we failed to create a device for? */ +drive_check_orphaned(); + net_check_clients(); ds = init_displaystate(); -- 1.9.3
[Qemu-devel] [PATCH 0/6] AHCI Device Fixes
Based off of feedback from the RFC of the same name, this series batches together a group of fixes that improve the AHCI device to fix a number of bugs. A number of fixes included in the RFC that provide more radical changes are omitted for now in favor of a smaller, more easily reviewable set for QEMU 2.2. In summary: Patch #1 and #6 correct the format of FIS packet responses that are available to the guest operating system upon interrupt. Patch #2 corrects an oversight where we do not inform the guest how many bytes we've transferred. This is relied upon for non-NCQ modes and in some early bootup and shutdown code. Patch #5 corrects cases with malformed scatter-gather lists that may cause leaks, or cause QEMU to hang in an allocation loop. Patch #4 attempts to continue minimizing the divergence of the multiple pathways through the AHCI device by re-using existing callbacks. Taken together, these patches should allow non-ncq operation for Windows hosts, as well as enable hibernation for Windows 7. Hibernation for Windows 8 and AHCI remains non-functional. John Snow (6): ahci: Correct PIO/D2H FIS responses ahci: Update byte count after DMA completion ide: repair PIO transfers for cases where nsector > 1 ahci: unify sglist preparation ide: Correct handling of malformed/short PRDTs ahci: Fix SDB FIS Construction dma-helpers.c | 3 ++ hw/ide/ahci.c | 113 -- hw/ide/ahci.h | 8 hw/ide/core.c | 17 ++-- hw/ide/internal.h | 2 + hw/ide/pci.c | 5 ++- 6 files changed, 97 insertions(+), 51 deletions(-) -- 1.9.3
[Qemu-devel] [PATCH 3/6] ide: repair PIO transfers for cases where nsector > 1
Currently, for emulated PIO transfers through the AHCI device, any attempt made to request more than a single sector's worth of data will result in the same sector being transferred over and over. For example, if we request 8 sectors via PIO READ SECTORS, the AHCI device will give us the same sector eight times. This patch adds offset tracking into the PIO pathways so that we can fulfill these requests appropriately. Signed-off-by: John Snow --- hw/ide/ahci.c | 2 +- hw/ide/core.c | 5 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 6966a94..db1d226 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1091,7 +1091,7 @@ static void ahci_start_transfer(IDEDMA *dma) goto out; } -if (!ahci_populate_sglist(ad, &s->sg, 0)) { +if (!ahci_populate_sglist(ad, &s->sg, s->io_buffer_offset)) { has_sglist = 1; } diff --git a/hw/ide/core.c b/hw/ide/core.c index f15b174..7c1929e 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -589,6 +589,7 @@ static void ide_sector_read_cb(void *opaque, int ret) ide_set_sector(s, ide_get_sector(s) + n); s->nsector -= n; +s->io_buffer_offset += 512 * n; } void ide_sector_read(IDEState *s) @@ -829,6 +830,8 @@ static void ide_sector_write_cb(void *opaque, int ret) n = s->req_nb_sectors; } s->nsector -= n; +s->io_buffer_offset += 512 * n; + if (s->nsector == 0) { /* no more sectors to write */ ide_transfer_stop(s); @@ -1230,6 +1233,7 @@ static bool cmd_read_pio(IDEState *s, uint8_t cmd) ide_cmd_lba48_transform(s, lba48); s->req_nb_sectors = 1; +s->io_buffer_offset = 0; ide_sector_read(s); return false; @@ -1247,6 +1251,7 @@ static bool cmd_write_pio(IDEState *s, uint8_t cmd) ide_cmd_lba48_transform(s, lba48); s->req_nb_sectors = 1; +s->io_buffer_offset = 0; s->status = SEEK_STAT | READY_STAT; ide_transfer_start(s, s->io_buffer, 512, ide_sector_write); -- 1.9.3
[Qemu-devel] [PATCH 2/6] ahci: Update byte count after DMA completion
Currently, DMA read/write operations neglect to update the byte count after a successful transfer like ATAPI DMA read or PIO read/write operations do. We correct this oversight by adding another callback into the IDEDMAOps structure. The commit callback is called whenever we are cleaning up a scatter-gather list. AHCI can register this callback in order to update post- transfer information such as byte count updates. We use this callback in AHCI to consolidate where we delete the SGlist as generated from the PRDT, as well as update the byte count after the transfer is complete. The QEMUSGList structure has an init flag added to it in order to make qemu_sglist_destroy a nop if it is called when there is no sglist, which simplifies cleanup and error paths. This patch fixes several AHCI problems, notably Non-NCQ modes of operation for Windows 7 as well as Hibernate support for Windows 7. Signed-off-by: John Snow --- hw/ide/ahci.c | 41 +++-- hw/ide/core.c | 11 +++ hw/ide/internal.h | 2 ++ 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 0529d68..6966a94 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -48,6 +48,9 @@ static int handle_cmd(AHCIState *s,int port,int slot); static void ahci_reset_port(AHCIState *s, int port); static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis); static void ahci_init_d2h(AHCIDevice *ad); +static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write); +static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes); + static uint32_t ahci_port_read(AHCIState *s, int port, int offset) { @@ -1104,16 +1107,12 @@ static void ahci_start_transfer(IDEDMA *dma) } } -/* update number of transferred bytes */ -ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + size); - out: /* declare that we processed everything */ s->data_ptr = s->data_end; -if (has_sglist) { -qemu_sglist_destroy(&s->sg); -} +/* Update number of transferred bytes, destroy sglist */ +ahci_commit_buf(dma, size); s->end_transfer_func(s); @@ -1134,6 +1133,11 @@ static void ahci_start_dma(IDEDMA *dma, IDEState *s, dma_cb(s, 0); } +/** + * Called in DMA R/W chains to read the PRDT, utilizing ahci_populate_sglist. + * Not currently invoked by PIO R/W chains, + * which invoke ahci_populate_sglist via ahci_start_transfer. + */ static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); @@ -1146,6 +1150,24 @@ static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write) return s->io_buffer_size != 0; } +/** + * Destroys the scatter-gather list, + * and updates the command header with a bytes-read value. + * called explicitly via ahci_dma_rw_buf (ATAPI DMA), + * and ahci_start_transfer (PIO R/W), + * and called via callback from ide_dma_cb for DMA R/W paths. + */ +static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes) +{ +AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); +IDEState *s = &ad->port.ifs[0]; + +tx_bytes += le32_to_cpu(ad->cur_cmd->status); +ad->cur_cmd->status = cpu_to_le32(tx_bytes); + +qemu_sglist_destroy(&s->sg); +} + static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) { AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); @@ -1163,11 +1185,9 @@ static int ahci_dma_rw_buf(IDEDMA *dma, int is_write) dma_buf_write(p, l, &s->sg); } -/* free sglist that was created in ahci_populate_sglist() */ -qemu_sglist_destroy(&s->sg); +/* free sglist, update byte count */ +ahci_commit_buf(dma, l); -/* update number of transferred bytes */ -ad->cur_cmd->status = cpu_to_le32(le32_to_cpu(ad->cur_cmd->status) + l); s->io_buffer_index += l; s->io_buffer_offset += l; @@ -1210,6 +1230,7 @@ static const IDEDMAOps ahci_dma_ops = { .start_dma = ahci_start_dma, .start_transfer = ahci_start_transfer, .prepare_buf = ahci_dma_prepare_buf, +.commit_buf = ahci_commit_buf, .rw_buf = ahci_dma_rw_buf, .set_unit = ahci_dma_set_unit, .cmd_done = ahci_cmd_done, diff --git a/hw/ide/core.c b/hw/ide/core.c index 190700a..f15b174 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -631,8 +631,11 @@ void ide_sector_read(IDEState *s) ide_sector_read_cb, s); } -static void dma_buf_commit(IDEState *s) +static void dma_buf_commit(IDEState *s, uint32_t tx_bytes) { +if (s->bus->dma->ops->commit_buf) { +s->bus->dma->ops->commit_buf(s->bus->dma, tx_bytes); +} qemu_sglist_destroy(&s->sg); } @@ -647,6 +650,7 @@ void ide_set_inactive(IDEState *s, bool more) void ide_dma_error(IDEState *s) { +dma_buf_commit(s, 0); ide_abort_command(s); ide_set_inactive(s, false);
[Qemu-devel] [PATCH 6/6] ahci: Fix SDB FIS Construction
The SDB FIS creation was mangled; We were writing the error byte to byte 0, and omitting the SDB FIS magic byte. Though the SDB packet layout states that: byte 0: Must be 0xA1 to indicate SDB FIS. byte 1: Port multiplier select & other flags byte 2: status byte. byte 3: error byte. This patch adds an SDB FIS structure with human-readable names, and ensures that we are filling the structure appropriately. Signed-off-by: John Snow --- hw/ide/ahci.c | 18 +- hw/ide/ahci.h | 8 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 67c1e36..8002e9e 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -569,24 +569,24 @@ static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished) AHCIDevice *ad = &s->dev[port]; AHCIPortRegs *pr = &ad->port_regs; IDEState *ide_state; -uint8_t *sdb_fis; +SDBFIS *sdb_fis; if (!s->dev[port].res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) { return; } -sdb_fis = &ad->res_fis[RES_FIS_SDBFIS]; +sdb_fis = (SDBFIS *)&ad->res_fis[RES_FIS_SDBFIS]; ide_state = &ad->port.ifs[0]; -/* clear memory */ -*(uint32_t*)sdb_fis = 0; - -/* write values */ -sdb_fis[0] = ide_state->error; -sdb_fis[2] = ide_state->status & 0x77; +sdb_fis->type = 0xA1; +/* Interrupt pending & Notification bit */ +sdb_fis->flags = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0); +sdb_fis->status = ide_state->status & 0x77; +sdb_fis->error = ide_state->error; +/* update SAct field in SDB_FIS */ s->dev[port].finished |= finished; -*(uint32_t*)(sdb_fis + 4) = cpu_to_le32(ad->finished); +sdb_fis->payload = cpu_to_le32(ad->finished); /* Update shadow registers (except BSY 0x80 and DRQ 0x08) */ pr->tfdata = (ad->port.ifs[0].error << 8) | diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h index 1543df7..a58dd09 100644 --- a/hw/ide/ahci.h +++ b/hw/ide/ahci.h @@ -327,6 +327,14 @@ typedef struct NCQFrame { uint8_t reserved10; } QEMU_PACKED NCQFrame; +typedef struct SDBFIS { +uint8_t type; +uint8_t flags; +uint8_t status; +uint8_t error; +uint32_t payload; +} QEMU_PACKED SDBFIS; + void ahci_init(AHCIState *s, DeviceState *qdev, AddressSpace *as, int ports); void ahci_uninit(AHCIState *s); -- 1.9.3
[Qemu-devel] [PATCH 1/6] ahci: Correct PIO/D2H FIS responses
Currently, the D2H FIS packets AHCI generates simply parrot back the LBA that the guest sent to us in the cmd_fis. However, some commands (like READ NATIVE MAX) modify the LBA registers as a return value, through which the AHCI D2H FIS is the only response mechanism. Thus, the D2H response should use the current register values, not the initial ones. This patch adjusts the LBA and drive select register responses for PIO Setup and D2H FIS response packets. Additionally, the PIO and D2H FIS responses copy too many bytes from the command FIS that it is being generated from. Specifically, byte 11 which is the Features(15:8) field for Register Host to Device FIS packets, is instead reserved for the PIO Setup FIS and should always be 0. Signed-off-by: John Snow --- hw/ide/ahci.c | 48 +--- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 8978643..0529d68 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -599,6 +599,7 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) uint8_t *pio_fis, *cmd_fis; uint64_t tbl_addr; dma_addr_t cmd_len = 0x80; +IDEState *s = &ad->port.ifs[0]; if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) { return; @@ -628,21 +629,21 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) pio_fis[0] = 0x5f; pio_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0); -pio_fis[2] = ad->port.ifs[0].status; -pio_fis[3] = ad->port.ifs[0].error; - -pio_fis[4] = cmd_fis[4]; -pio_fis[5] = cmd_fis[5]; -pio_fis[6] = cmd_fis[6]; -pio_fis[7] = cmd_fis[7]; -pio_fis[8] = cmd_fis[8]; -pio_fis[9] = cmd_fis[9]; -pio_fis[10] = cmd_fis[10]; -pio_fis[11] = cmd_fis[11]; +pio_fis[2] = s->status; +pio_fis[3] = s->error; + +pio_fis[4] = s->sector; +pio_fis[5] = s->lcyl; +pio_fis[6] = s->hcyl; +pio_fis[7] = s->select; +pio_fis[8] = s->hob_sector; +pio_fis[9] = s->hob_lcyl; +pio_fis[10] = s->hob_hcyl; +pio_fis[11] = 0; pio_fis[12] = cmd_fis[12]; pio_fis[13] = cmd_fis[13]; pio_fis[14] = 0; -pio_fis[15] = ad->port.ifs[0].status; +pio_fis[15] = s->status; pio_fis[16] = len & 255; pio_fis[17] = len >> 8; pio_fis[18] = 0; @@ -669,6 +670,7 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis) int i; dma_addr_t cmd_len = 0x80; int cmd_mapped = 0; +IDEState *s = &ad->port.ifs[0]; if (!ad->res_fis || !(pr->cmd & PORT_CMD_FIS_RX)) { return; @@ -686,17 +688,17 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis) d2h_fis[0] = 0x34; d2h_fis[1] = (ad->hba->control_regs.irqstatus ? (1 << 6) : 0); -d2h_fis[2] = ad->port.ifs[0].status; -d2h_fis[3] = ad->port.ifs[0].error; - -d2h_fis[4] = cmd_fis[4]; -d2h_fis[5] = cmd_fis[5]; -d2h_fis[6] = cmd_fis[6]; -d2h_fis[7] = cmd_fis[7]; -d2h_fis[8] = cmd_fis[8]; -d2h_fis[9] = cmd_fis[9]; -d2h_fis[10] = cmd_fis[10]; -d2h_fis[11] = cmd_fis[11]; +d2h_fis[2] = s->status; +d2h_fis[3] = s->error; + +d2h_fis[4] = s->sector; +d2h_fis[5] = s->lcyl; +d2h_fis[6] = s->hcyl; +d2h_fis[7] = s->select; +d2h_fis[8] = s->hob_sector; +d2h_fis[9] = s->hob_lcyl; +d2h_fis[10] = s->hob_hcyl; +d2h_fis[11] = 0; d2h_fis[12] = cmd_fis[12]; d2h_fis[13] = cmd_fis[13]; for (i = 14; i < 20; i++) { -- 1.9.3
[Qemu-devel] [PATCH 5/6] ide: Correct handling of malformed/short PRDTs
This impacts both BMDMA and AHCI HBA interfaces for IDE. Currently, we confuse the difference between a PRD having "0 bytes" and a PRD having "0 complete sectors." This leads to, in the BMDMA case, leaked memory for short PRDTs, and infinite loops in the AHCI case. the "prepare_buf" callback is reworked to return 0 if it could not allocate a full sector's worth of buffer space, instead of returning non-zero if it allocated any number of bytes. ide_dma_cb adds a call to commit_buf in order to delete the short PRDT that it will not attempt to use to finish the DMA operation. This patch corrects both occurrences and adds an assertion to prevent future regression. This assertion is tested in the existing ide-test, and is covered in a forthcoming AHCI test. Signed-off-by: John Snow --- dma-helpers.c | 3 +++ hw/ide/ahci.c | 2 +- hw/ide/core.c | 1 + hw/ide/pci.c | 5 +++-- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/dma-helpers.c b/dma-helpers.c index 7f86e18..f51d6ee 100644 --- a/dma-helpers.c +++ b/dma-helpers.c @@ -38,6 +38,9 @@ int dma_memory_set(AddressSpace *as, dma_addr_t addr, uint8_t c, dma_addr_t len) void qemu_sglist_init(QEMUSGList *qsg, DeviceState *dev, int alloc_hint, AddressSpace *as) { +/* If this is true, you're leaking memory. */ +assert(qsg->sg == NULL); + qsg->sg = g_malloc(alloc_hint * sizeof(ScatterGatherEntry)); qsg->nsg = 0; qsg->nalloc = alloc_hint; diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 16cd248..67c1e36 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1147,7 +1147,7 @@ static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write) s->io_buffer_size = s->sg.size; DPRINTF(ad->port_no, "len=%#x\n", s->io_buffer_size); -return s->io_buffer_size != 0; +return s->io_buffer_size / 512 != 0; } /** diff --git a/hw/ide/core.c b/hw/ide/core.c index 7c1929e..82d01e8 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -732,6 +732,7 @@ void ide_dma_cb(void *opaque, int ret) /* The PRDs were too short. Reset the Active bit, but don't raise an * interrupt. */ s->status = READY_STAT | SEEK_STAT; +dma_buf_commit(s, false); goto eot; } diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 2397f35..3f643c2 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -74,8 +74,9 @@ static int bmdma_prepare_buf(IDEDMA *dma, int is_write) if (bm->cur_prd_len == 0) { /* end of table (with a fail safe of one page) */ if (bm->cur_prd_last || -(bm->cur_addr - bm->addr) >= BMDMA_PAGE_SIZE) -return s->io_buffer_size != 0; +(bm->cur_addr - bm->addr) >= BMDMA_PAGE_SIZE) { +return (s->io_buffer_size / 512) != 0; +} pci_dma_read(pci_dev, bm->cur_addr, &prd, 8); bm->cur_addr += 8; prd.addr = le32_to_cpu(prd.addr); -- 1.9.3
[Qemu-devel] [PATCH 4/6] ahci: unify sglist preparation
The intent of this patch is to further unify the creation and deletion of the sglist used for all AHCI transfers, including emulated PIO, ATAPI R/W, and native DMA R/W. By replacing ahci_start_transfer's call to ahci_populate_sglist with ahci_dma_prepare_buf, we reduce the number of direct calls where we manipulate the scatter-gather list in the AHCI code. To make this switch, the constant "0" passed as an offset in ahci_dma_prepare_buf is adjusted to use io_buffer_offset. For DMA pathways, this has no effect: io_buffer_offset is always updated to 0 at the beginning of a DMA transfer loop regardless. DMA pathways through ide_dma_cb() update the io_buffer_offset accordingly, and for circumstances where we might make several trips through this loop, this may actually correct a design flaw. For PIO pathways, the newly updated ahci_dma_prepare_buf will now prepare the sglist at the correct offset. It will also set io_buffer_size, but this is not used in the cmd_read_pio or cmd_write_pio pathways. Signed-off-by: John Snow --- hw/ide/ahci.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index db1d226..16cd248 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1091,7 +1091,7 @@ static void ahci_start_transfer(IDEDMA *dma) goto out; } -if (!ahci_populate_sglist(ad, &s->sg, s->io_buffer_offset)) { +if (ahci_dma_prepare_buf(dma, is_write)) { has_sglist = 1; } @@ -1143,7 +1143,7 @@ static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write) AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma); IDEState *s = &ad->port.ifs[0]; -ahci_populate_sglist(ad, &s->sg, 0); +ahci_populate_sglist(ad, &s->sg, s->io_buffer_offset); s->io_buffer_size = s->sg.size; DPRINTF(ad->port_no, "len=%#x\n", s->io_buffer_size); -- 1.9.3
[Qemu-devel] [PATCH] ide: Add resize callback to ide/core
Currently, if the block device backing the IDE drive is resized, the information about the device as cached inside of the IDEState structure is not updated, thus when a guest OS re-queries the drive, it is unable to see the expanded size. This patch adds a resize callback to correct this, and marks the identify buffer cache as being dirty to force ide_identify to regenerate this information. This callback also attempts to update the legacy CHS values, if only to maintain a sense of informational consistency. Lastly, a Linux guest as-is cannot resize a libata drive while in-use, but it can see the expanded size as part of a bus rescan event. This patch also allows guests such as Linux to see the new drive size after a soft reboot event, without having to exit the QEMU process. Signed-off-by: John Snow --- hw/ide/core.c | 29 + 1 file changed, 29 insertions(+) diff --git a/hw/ide/core.c b/hw/ide/core.c index db191a6..6c86e21 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2099,6 +2099,30 @@ static bool ide_cd_is_medium_locked(void *opaque) return ((IDEState *)opaque)->tray_locked; } +static void ide_resize_cb(void *opaque) +{ +IDEState *s = opaque; +IDEDevice *dev = s->unit ? s->bus->slave : s->bus->master; +uint64_t nb_sectors; + +/* Convince blkconf_geometry to re-determine geometry */ +dev->conf.cyls = 0; +dev->conf.heads = 0; +dev->conf.secs = 0; + +blkconf_geometry(&dev->conf, &dev->chs_trans, 65536, 16, 255); +bdrv_get_geometry(s->bs, &nb_sectors); + +s->nb_sectors = nb_sectors; +s->cylinders = dev->conf.cyls; +s->heads = dev->conf.heads; +s->sectors = dev->conf.secs; +s->chs_trans = dev->chs_trans; + +/* Let ide_identify() know it needs to regenerate the response. */ +s->identify_set = 0; +} + static const BlockDevOps ide_cd_block_ops = { .change_media_cb = ide_cd_change_cb, .eject_request_cb = ide_cd_eject_request_cb, @@ -2106,6 +2130,10 @@ static const BlockDevOps ide_cd_block_ops = { .is_medium_locked = ide_cd_is_medium_locked, }; +static const BlockDevOps ide_hd_block_ops = { +.resize_cb = ide_resize_cb, +}; + int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind, const char *version, const char *serial, const char *model, uint64_t wwn, @@ -2142,6 +2170,7 @@ int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind, error_report("Can't use a read-only drive"); return -1; } +bdrv_set_dev_ops(bs, &ide_hd_block_ops, s); } if (serial) { pstrcpy(s->drive_serial_str, sizeof(s->drive_serial_str), serial); -- 1.9.3
[Qemu-devel] [PATCH v2] ide: Add resize callback to ide/core
Currently, if the block device backing the IDE drive is resized, the information about the device as cached inside of the IDEState structure is not updated, thus when a guest OS re-queries the drive, it is unable to see the expanded size. This patch adds a resize callback that updates the IDENTIFY data buffer in order to correct this. Lastly, a Linux guest as-is cannot resize a libata drive while in-use, but it can see the expanded size as part of a bus rescan event. This patch also allows guests such as Linux to see the new drive size after a soft reboot event, without having to exit the QEMU process. Signed-off-by: John Snow --- V2: - Do not attempt to update geometry values, to avoid clobbering user-specified values, if they exist. - Do not regenerate the entire IDENTIFY buffer to avoid losing any settings that occurred during normal operation. hw/ide/core.c | 32 1 file changed, 32 insertions(+) diff --git a/hw/ide/core.c b/hw/ide/core.c index db191a6..ba5e3ad 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2099,6 +2099,33 @@ static bool ide_cd_is_medium_locked(void *opaque) return ((IDEState *)opaque)->tray_locked; } +static void ide_resize_cb(void *opaque) +{ +IDEState *s = opaque; +uint16_t *p = (uint16_t *)s->identify_data; +uint64_t nb_sectors; + +if (!s->identify_set) { +return; +} + +bdrv_get_geometry(s->bs, &nb_sectors); +s->nb_sectors = nb_sectors; + +/* Update the identify buffer. Note, this is not called for ATAPI. */ +if (s->drive_kind != IDE_CFATA) { +put_le16(p + 60, s->nb_sectors); +put_le16(p + 61, s->nb_sectors >> 16); +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); +} else { +put_le16(p + 7, s->nb_sectors >> 16); +put_le16(p + 8, s->nb_sectors); +} +} + static const BlockDevOps ide_cd_block_ops = { .change_media_cb = ide_cd_change_cb, .eject_request_cb = ide_cd_eject_request_cb, @@ -2106,6 +2133,10 @@ static const BlockDevOps ide_cd_block_ops = { .is_medium_locked = ide_cd_is_medium_locked, }; +static const BlockDevOps ide_hd_block_ops = { +.resize_cb = ide_resize_cb, +}; + int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind, const char *version, const char *serial, const char *model, uint64_t wwn, @@ -2142,6 +2173,7 @@ int ide_init_drive(IDEState *s, BlockDriverState *bs, IDEDriveKind kind, error_report("Can't use a read-only drive"); return -1; } +bdrv_set_dev_ops(bs, &ide_hd_block_ops, s); } if (serial) { pstrcpy(s->drive_serial_str, sizeof(s->drive_serial_str), serial); -- 1.9.3
[Qemu-devel] [PATCH v3 00/32] AHCI test suite framework v3
This submission does not re-send patches 1-24, which are identical to the v2 submission and have already been pulled onto stefanha's block staging branch. This patch series introduces a number of small fixes and tweaks to help support an AHCI test suite that in the future I hope to expand to a fuller regression suite to help guide the development of the AHCI device support under, in particular, the Q35 machine type in QEMU. Paolo Bonzini has contributed a number of cleanup and refactoring patches that support changes to the PIO setup FIS packet construction code, which is necessary for testing ths specification adherence of the IDENTIFY command, which issues its data exclusively via PIO mechanisms. The ahci-test code being checked in represents a minimum of functionality needed in order to issue and receive commands from the AHCI HBA under the libqos / qtest environment. In V3, there is one assertion for incorrect behavior that is expected to be fixed in a future patch set, where the Descriptor Processed interrupt is not set or posted within the identify test. V3: - All tests complete successfully. - Fixed the PCI capabilities ordering for AHCI. - Corrected the boot-time values of the PxTFD and PxSIG registers - Corrected PxTFD register being updated prior to PxCMD.FRE being set V2: "ide-test: add test for werror=stop" - changed filename variable name - altered the QMP event polling to avoid sleep - debug_path file cleanup on exit. "ide: stop PIO transfer on errors" - Modified logic to be unconditional. "ahci: construct PIO Setup FIS for PIO commands" - Added in dma_memory_map success checks and error pathways. "libqtest: Correct small memory leak." - Corrected raw usage of free() "ahci: Adding basic functionality qtest." - Removed HBA type. - Corrected cleanup order. - Corrected raw usage of free() - Removed needless conditional around g_free() - Altered ahci_boot to return by value instead of parameter. "ahci: Add test_pci_spec to ahci-test." - Removed all ifdef logic in favor of runtime tuning. (--pedantic) - Removed all warnings, all infractions are now hard assertions. - Replaced g_assert with g_assert_cmphex in many cases "ahci: add test_pci_enable to ahci-test." - Removed MSI codepaths, as it was incomplete and unused. "ahci: Add test_hba_spec to ahci-test." - Removed unneeded macros - Added in an optional bar_size return parameter to qpci_iomap "ahci: Add test_identify case to ahci-test." - Corrected raw usage of free() For convenience; https://github.com/jnsnow/qemu/tree/ahci-test-v3 John Snow (15): q35: Enable the ioapic device to be seen by qtest. qtest: Adding qtest_memset and qmemset. libqos: Correct memory leak libqtest: Correct small memory leak. libqos: Fixes a small memory leak. libqos: allow qpci_iomap to return BAR mapping size qtest/ide: Fix small memory leak ahci: Adding basic functionality qtest. ahci: MSI capability should be at 0x80, not 0x50. ahci: Add test_pci_spec to ahci-test. ahci: add test_pci_enable to ahci-test. ahci: properly shadow the TFD register ahci: Add test_hba_spec to ahci-test. ahci: Add test_hba_enable to ahci-test. ahci: Add test_identify case to ahci-test. Paolo Bonzini (17): blkdebug: report errors on flush too libqtest: add QTEST_LOG for debugging qtest testcases ide-test: add test for werror=stop ide: stash aiocb for flushes ide: simplify reset callbacks ide: simplify set_inactive callbacks ide: simplify async_cmd_done callbacks ide: simplify start_transfer callbacks ide: wrap start_dma callback ide: remove wrong setting of BM_STATUS_INT ide: fold add_status callback into set_inactive ide: move BM_STATUS bits to pci.[ch] ide: move retry constants out of BM_STATUS_* namespace ahci: remove duplicate PORT_IRQ_* constants ide: stop PIO transfer on errors ide: make all commands go through cmd_done ahci: construct PIO Setup FIS for PIO commands block/blkdebug.c | 20 + hw/i386/pc_q35.c |2 +- hw/ide/ahci.c | 155 +++-- hw/ide/ahci.h | 21 - hw/ide/atapi.c| 11 +- hw/ide/core.c | 94 ++- hw/ide/ich.c |5 +- hw/ide/internal.h | 38 +- hw/ide/macio.c|9 - hw/ide/pci.c | 45 +- hw/ide/pci.h |7 + tests/Makefile|2 + tests/ahci-test.c | 1554 + tests/ide-test.c | 85 ++- tests/libqos/malloc-pc.c |3 + tests/libqos/pci-pc.c | 12 +- tests/libqos/pci-pc.h |1 + tests/libqos/pci.c| 10 +- tests/libqos/pci.h|4 +- tests/libqtest.c | 20 +- tests/libqtest.h | 24 + tests/usb-hcd-ehci-test.c |2 +- 22 files changed, 1922 insertions(+), 202 deletions(-) create mode 100644 tests/ahci-test.c -- 1.9.3
[Qemu-devel] [PATCH v3 31/32] ahci: Add test_hba_enable to ahci-test.
This test engages the HBA functionality and initializes values to sane defaults to allow for minimal HBA functionality. Buffers are allocated and pointers are updated to allow minimal I/O commands to complete as expected. Error registers and responses are sanity checked for specification adherence. Signed-off-by: John Snow --- tests/ahci-test.c | 156 ++ 1 file changed, 156 insertions(+) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 85f8661..31880ed 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -277,11 +277,17 @@ static uint32_t ahci_fingerprint; #define ahci_write(OFST, VAL) qpci_io_writel(ahci, hba_base + (OFST), (VAL)) #define ahci_rreg(regno) ahci_read(4 * (regno)) #define ahci_wreg(regno, val) ahci_write(4 * (regno), (val)) +#define ahci_set(regno, mask) ahci_wreg((regno), ahci_rreg(regno) | (mask)) +#define ahci_clr(regno, mask) ahci_wreg((regno), ahci_rreg(regno) & ~(mask)) /*** IO macros for port-specific offsets inside of AHCI memory. ***/ #define px_ofst(port, regno) (HBA_PORT_NUM_REG * (port) + AHCI_PORTS + (regno)) #define px_rreg(port, regno) ahci_rreg(px_ofst((port), (regno))) #define px_wreg(port, regno, val) ahci_wreg(px_ofst((port), (regno)), (val)) +#define px_set(port, reg, mask) px_wreg((port), (reg),\ + px_rreg((port), (reg)) | (mask)); +#define px_clr(port, reg, mask) px_wreg((port), (reg),\ + px_rreg((port), (reg)) & ~(mask)); /*** Function Declarations ***/ static QPCIDevice *get_ahci_device(void); @@ -432,6 +438,140 @@ static QPCIDevice *start_ahci_device(QPCIDevice *ahci, void **hba_base) return ahci; } +/** + * Test and initialize the AHCI's HBA memory areas. + * Initialize and start any ports with devices attached. + * Bring the HBA into the idle state. + */ +static void ahci_hba_enable(QPCIDevice *ahci, void *hba_base) +{ +/* Bits of interest in this section: + * GHC.AE Global Host Control / AHCI Enable + * PxCMD.ST Port Command: Start + * PxCMD.SUD "Spin Up Device" + * PxCMD.POD "Power On Device" + * PxCMD.FRE "FIS Receive Enable" + * PxCMD.FR "FIS Receive Running" + * PxCMD.CR "Command List Running" + */ + +g_assert(ahci != NULL); +g_assert(hba_base != NULL); + +uint32_t reg, ports_impl, clb, fb; +uint16_t i; +uint8_t num_cmd_slots; + +g_assert(hba_base != 0); + +/* Set GHC.AE to 1 */ +ahci_set(AHCI_GHC, AHCI_GHC_AE); +reg = ahci_rreg(AHCI_GHC); +assert_bit_set(reg, AHCI_GHC_AE); + +/* Read CAP.NCS, how many command slots do we have? */ +reg = ahci_rreg(AHCI_CAP); +num_cmd_slots = ((reg & AHCI_CAP_NCS) >> ctzl(AHCI_CAP_NCS)) + 1; +g_test_message("Number of Command Slots: %u", num_cmd_slots); + +/* Determine which ports are implemented. */ +ports_impl = ahci_rreg(AHCI_PI); + +for (i = 0; ports_impl; ports_impl >>= 1, ++i) { +if (!(ports_impl & 0x01)) { +continue; +} + +g_test_message("Initializing port %u", i); + +reg = px_rreg(i, AHCI_PX_CMD); +if (bitclr(reg, AHCI_PX_CMD_ST | AHCI_PX_CMD_CR | + AHCI_PX_CMD_FRE | AHCI_PX_CMD_FR)) { +g_test_message("port is idle"); +} else { +g_test_message("port needs to be idled"); +px_clr(i, AHCI_PX_CMD, (AHCI_PX_CMD_ST | AHCI_PX_CMD_FRE)); +/* The port has 500ms to disengage. */ +usleep(50); +reg = px_rreg(i, AHCI_PX_CMD); +assert_bit_clear(reg, AHCI_PX_CMD_CR); +assert_bit_clear(reg, AHCI_PX_CMD_FR); +g_test_message("port is now idle"); +/* The spec does allow for possibly needing a PORT RESET + * or HBA reset if we fail to idle the port. */ +} + +/* Allocate Memory for the Command List Buffer & FIS Buffer */ +/* PxCLB space ... 0x20 per command, as in 4.2.2 p 36 */ +clb = guest_alloc(guest_malloc, num_cmd_slots * 0x20); +g_test_message("CLB: 0x%08x", clb); +px_wreg(i, AHCI_PX_CLB, clb); +g_assert_cmphex(clb, ==, px_rreg(i, AHCI_PX_CLB)); + +/* PxFB space ... 0x100, as in 4.2.1 p 35 */ +fb = guest_alloc(guest_malloc, 0x100); +g_test_message("FB: 0x%08x", fb); +px_wreg(i, AHCI_PX_FB, fb); +g_assert_cmphex(fb, ==, px_rreg(i, AHCI_PX_FB)); + +/* Clear PxSERR, PxIS, then IS.IPS[x] by writing '1's. */ +px_wreg(i, AHCI_PX_SERR, 0x); +px_wreg(i, AHCI_PX_IS, 0x); +ahci_wreg(AHCI_IS, (1 << i)); + +/* Verify Interrupts Cleared */ +reg = px_rreg(i, AHCI_PX_SERR); +
[Qemu-devel] [PATCH v3 29/32] ahci: properly shadow the TFD register
In a real AHCI device, several S/ATA registers are mirrored or shadowed within the AHCI register set. These registers are not updated synchronously for each read access, but rather after a Device-to-Host Register FIS packet is received, which contains the values from these registers on the device. In QEMU, by reaching directly into the device to grab these bits before they are "sent," we may introduce race conditions where unexpected values are present "before they are sent" which could cause issues for some guests, particularly if an attempt is made to read the PxTFD register prior to enabling the port, where incorrect values will be read. This patch also addresses the boot-time values for the PxTFD and PxSIG registers to bring them in line with the AHCI 1.3 specification. Signed-off-by: John Snow --- hw/ide/ahci.c | 42 -- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 4cda0d0..00d8a7a 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -78,8 +78,7 @@ static uint32_t ahci_port_read(AHCIState *s, int port, int offset) val = pr->cmd; break; case PORT_TFDATA: -val = ((uint16_t)s->dev[port].port.ifs[0].error << 8) | - s->dev[port].port.ifs[0].status; +val = pr->tfdata; break; case PORT_SIG: val = pr->sig; @@ -251,14 +250,13 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) check_cmd(s, port); break; case PORT_TFDATA: -s->dev[port].port.ifs[0].error = (val >> 8) & 0xff; -s->dev[port].port.ifs[0].status = val & 0xff; +/* Read Only. */ break; case PORT_SIG: -pr->sig = val; +/* Read Only */ break; case PORT_SCR_STAT: -pr->scr_stat = val; +/* Read Only */ break; case PORT_SCR_CTL: if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) && @@ -497,6 +495,8 @@ static void ahci_reset_port(AHCIState *s, int port) pr->scr_stat = 0; pr->scr_err = 0; pr->scr_act = 0; +pr->tfdata = 0x7F; +pr->sig = 0x; d->busy_slot = -1; d->init_d2h_sent = false; @@ -528,16 +528,16 @@ static void ahci_reset_port(AHCIState *s, int port) s->dev[port].port_state = STATE_RUN; if (!ide_state->bs) { -s->dev[port].port_regs.sig = 0; +pr->sig = 0; ide_state->status = SEEK_STAT | WRERR_STAT; } else if (ide_state->drive_kind == IDE_CD) { -s->dev[port].port_regs.sig = SATA_SIGNATURE_CDROM; +pr->sig = SATA_SIGNATURE_CDROM; ide_state->lcyl = 0x14; ide_state->hcyl = 0xeb; DPRINTF(port, "set lcyl = %d\n", ide_state->lcyl); ide_state->status = SEEK_STAT | WRERR_STAT | READY_STAT; } else { -s->dev[port].port_regs.sig = SATA_SIGNATURE_DISK; +pr->sig = SATA_SIGNATURE_DISK; ide_state->status = SEEK_STAT | WRERR_STAT; } @@ -563,7 +563,8 @@ static void debug_print_fis(uint8_t *fis, int cmd_len) static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished) { -AHCIPortRegs *pr = &s->dev[port].port_regs; +AHCIDevice *ad = &s->dev[port]; +AHCIPortRegs *pr = &ad->port_regs; IDEState *ide_state; uint8_t *sdb_fis; @@ -572,8 +573,8 @@ static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished) return; } -sdb_fis = &s->dev[port].res_fis[RES_FIS_SDBFIS]; -ide_state = &s->dev[port].port.ifs[0]; +sdb_fis = &ad->res_fis[RES_FIS_SDBFIS]; +ide_state = &ad->port.ifs[0]; /* clear memory */ *(uint32_t*)sdb_fis = 0; @@ -582,9 +583,14 @@ static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished) sdb_fis[0] = ide_state->error; sdb_fis[2] = ide_state->status & 0x77; s->dev[port].finished |= finished; -*(uint32_t*)(sdb_fis + 4) = cpu_to_le32(s->dev[port].finished); +*(uint32_t*)(sdb_fis + 4) = cpu_to_le32(ad->finished); -ahci_trigger_irq(s, &s->dev[port], PORT_IRQ_SDB_FIS); +/* Update shadow registers (except BSY 0x80 and DRQ 0x08) */ +pr->tfdata = (ad->port.ifs[0].error << 8) | +(ad->port.ifs[0].status & 0x77) | +(pr->tfdata & 0x88); + +ahci_trigger_irq(s, ad, PORT_IRQ_SDB_FIS); } static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) @@ -642,6 +648,10 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) pio_fis[18] = 0; pio_fis[19] = 0; +/* Update shadow registers: */ +pr->tfdata = (ad->port.ifs[0].error << 8) | +ad->port.ifs[0].status
[Qemu-devel] [PATCH v3 25/32] ahci: Adding basic functionality qtest.
Currently, there is no qtest to test the functionality of the AHCI functionality present within the Q35 machine type. This patch adds a skeleton for an AHCI test suite, and adds a simple sanity-check test case where we identify that the AHCI device is present, then disengage the virtual machine. Signed-off-by: John Snow --- tests/Makefile| 2 + tests/ahci-test.c | 196 ++ 2 files changed, 198 insertions(+) create mode 100644 tests/ahci-test.c diff --git a/tests/Makefile b/tests/Makefile index 4b2e1bb..0c61b22 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -132,6 +132,7 @@ check-qtest-i386-y = tests/endianness-test$(EXESUF) check-qtest-i386-y += tests/fdc-test$(EXESUF) gcov-files-i386-y = hw/block/fdc.c check-qtest-i386-y += tests/ide-test$(EXESUF) +check-qtest-i386-y += tests/ahci-test$(EXESUF) check-qtest-i386-y += tests/hd-geo-test$(EXESUF) gcov-files-i386-y += hw/block/hd-geometry.c check-qtest-i386-y += tests/boot-order-test$(EXESUF) @@ -301,6 +302,7 @@ tests/endianness-test$(EXESUF): tests/endianness-test.o tests/spapr-phb-test$(EXESUF): tests/spapr-phb-test.o $(libqos-obj-y) tests/fdc-test$(EXESUF): tests/fdc-test.o tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y) +tests/ahci-test$(EXESUF): tests/ahci-test.o $(libqos-pc-obj-y) tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y) tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o $(libqos-obj-y) diff --git a/tests/ahci-test.c b/tests/ahci-test.c new file mode 100644 index 000..cc49dba --- /dev/null +++ b/tests/ahci-test.c @@ -0,0 +1,196 @@ +/* + * AHCI test cases + * + * Copyright (c) 2014 John Snow + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include + +#include + +#include "libqtest.h" +#include "libqos/pci-pc.h" +#include "libqos/malloc-pc.h" + +#include "qemu-common.h" +#include "qemu/host-utils.h" + +#include "hw/pci/pci_ids.h" +#include "hw/pci/pci_regs.h" + +/* Test-specific defines. */ +#define TEST_IMAGE_SIZE(64 * 1024 * 1024) + +/*** Supplementary PCI Config Space IDs & Masks ***/ +#define PCI_DEVICE_ID_INTEL_Q35_AHCI (0x2922) + +/*** Globals ***/ +static QGuestAllocator *guest_malloc; +static QPCIBus *pcibus; +static char tmp_path[] = "/tmp/qtest.XX"; + +/*** Function Declarations ***/ +static QPCIDevice *get_ahci_device(void); +static void free_ahci_device(QPCIDevice *dev); + +/*** Utilities ***/ + +/** + * Locate, verify, and return a handle to the AHCI device. + */ +static QPCIDevice *get_ahci_device(void) +{ +QPCIDevice *ahci; +uint16_t vendor_id, device_id; + +pcibus = qpci_init_pc(); + +/* Find the AHCI PCI device and verify it's the right one. */ +ahci = qpci_device_find(pcibus, QPCI_DEVFN(0x1F, 0x02)); +g_assert(ahci != NULL); + +vendor_id = qpci_config_readw(ahci, PCI_VENDOR_ID); +device_id = qpci_config_readw(ahci, PCI_DEVICE_ID); + +g_assert_cmphex(vendor_id, ==, PCI_VENDOR_ID_INTEL); +g_assert_cmphex(device_id, ==, PCI_DEVICE_ID_INTEL_Q35_AHCI); + +return ahci; +} + +static void free_ahci_device(QPCIDevice *ahci) +{ +/* libqos doesn't have a function for this, so free it manually */ +g_free(ahci); + +if (pcibus) { +qpci_free_pc(pcibus); +pcibus = NULL; +} +} + +/*** Test Setup & Teardown ***/ + +/** + * Launch QEMU with the given command line, + * and then set up interrupts and our guest malloc interface. + */ +static void qtest_boot(const char *cmdline_fmt, ...) +{ +va_list ap; +char *cmdline; + +va_start(ap, cmdline_fmt); +cmdline = g_strdup_vprintf(cmdline_fmt, ap); +va_end(ap); + +qtest_start(cmdline); +qtest_irq_intercept_in(global_qtest, "ioapic&q
[Qemu-devel] [PATCH v3 26/32] ahci: MSI capability should be at 0x80, not 0x50.
In the Intel ICH9 data sheet, the MSI capability offset in the PCI configuration space for ICH9 AHCI devices is specified to be 0x80. Further, the PCI capability pointer should always point to 0x80 in ICH9 devices, despite the fact that AHCI 1.3 specifies that it should be pointing to PMCAP (Which in this instance would be 0x70) to maintain adherence to the Intel data sheet specifications and real observed behavior. Signed-off-by: John Snow --- hw/ide/ich.c | 5 - 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/ide/ich.c b/hw/ide/ich.c index a2f1639..d2a3ac2 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -71,6 +71,7 @@ #include #include +#define ICH9_MSI_CAP_OFFSET 0x80 #define ICH9_SATA_CAP_OFFSET0xA8 #define ICH9_IDP_BAR4 @@ -115,7 +116,6 @@ static int pci_ich9_ahci_init(PCIDevice *dev) /* XXX Software should program this register */ dev->config[0x90] = 1 << 6; /* Address Map Register - AHCI mode */ -msi_init(dev, 0x50, 1, true, false); d->ahci.irq = pci_allocate_irq(dev); pci_register_bar(dev, ICH9_IDP_BAR, PCI_BASE_ADDRESS_SPACE_IO, @@ -135,6 +135,9 @@ static int pci_ich9_ahci_init(PCIDevice *dev) (ICH9_IDP_BAR + 0x4) | (ICH9_IDP_INDEX_LOG2 << 4)); d->ahci.idp_offset = ICH9_IDP_INDEX; +/* MSI cap should be added last to be first. */ +msi_init(dev, ICH9_MSI_CAP_OFFSET, 1, true, false); + return 0; } -- 1.9.3
[Qemu-devel] [PATCH v3 28/32] ahci: add test_pci_enable to ahci-test.
This adds a test wherein we engage the PCI AHCI device and ensure that the memory region for the HBA functionality is now accessible. Under Q35 environments, additional PCI configuration is performed to ensure that the HBA functionality will become usable. Signed-off-by: John Snow --- tests/ahci-test.c | 57 ++ tests/libqos/pci.c | 6 ++ 2 files changed, 63 insertions(+) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 29ac0d0..0ca4a20 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -56,6 +56,7 @@ /*** Globals ***/ static QGuestAllocator *guest_malloc; static QPCIBus *pcibus; +static uint64_t barsize; static char tmp_path[] = "/tmp/qtest.XX"; static bool ahci_pedantic; static uint32_t ahci_fingerprint; @@ -66,6 +67,7 @@ static uint32_t ahci_fingerprint; /*** Function Declarations ***/ static QPCIDevice *get_ahci_device(void); +static QPCIDevice *start_ahci_device(QPCIDevice *dev, void **hba_base); static void free_ahci_device(QPCIDevice *dev); static void ahci_test_pci_spec(QPCIDevice *ahci); static void ahci_test_pci_caps(QPCIDevice *ahci, uint16_t header, @@ -111,6 +113,9 @@ static void free_ahci_device(QPCIDevice *ahci) qpci_free_pc(pcibus); pcibus = NULL; } + +/* Clear our cached barsize information. */ +barsize = 0; } /*** Test Setup & Teardown ***/ @@ -169,6 +174,44 @@ static void ahci_shutdown(QPCIDevice *ahci) qtest_shutdown(); } +/*** Logical Device Initialization ***/ + +/** + * Start the PCI device and sanity-check default operation. + */ +static void ahci_pci_enable(QPCIDevice *ahci, void **hba_base) +{ +uint8_t reg; + +start_ahci_device(ahci, hba_base); + +switch(ahci_fingerprint) { +case AHCI_INTEL_ICH9: +/* ICH9 has a register at PCI 0x92 that + * acts as a master port enabler mask. */ +reg = qpci_config_readb(ahci, 0x92); +reg |= 0x3F; +qpci_config_writeb(ahci, 0x92, reg); +assert_bit_set(qpci_config_readb(ahci, 0x92), 0x3F); +break; +} + +} + +/** + * Map BAR5/ABAR, and engage the PCI device. + */ +static QPCIDevice *start_ahci_device(QPCIDevice *ahci, void **hba_base) +{ +/* Map AHCI's ABAR (BAR5) */ +*hba_base = qpci_iomap(ahci, 5, &barsize); + +/* turns on pci.cmd.iose, pci.cmd.mse and pci.cmd.bme */ +qpci_device_enable(ahci); + +return ahci; +} + /*** Specification Adherence Tests ***/ /** @@ -428,6 +471,19 @@ static void test_pci_spec(void) ahci_shutdown(ahci); } +/** + * Engage the PCI AHCI device and sanity check the response. + * Perform additional PCI config space bringup for the HBA. + */ +static void test_pci_enable(void) +{ +QPCIDevice *ahci; +void *hba_base; +ahci = ahci_boot(); +ahci_pci_enable(ahci, &hba_base); +ahci_shutdown(ahci); +} + /**/ int main(int argc, char **argv) @@ -479,6 +535,7 @@ int main(int argc, char **argv) /* Run the tests */ qtest_add_func("/ahci/sanity", test_sanity); qtest_add_func("/ahci/pci_spec", test_pci_spec); +qtest_add_func("/ahci/pci_enable", test_pci_enable); ret = g_test_run(); diff --git a/tests/libqos/pci.c b/tests/libqos/pci.c index ce0b308..b244689 100644 --- a/tests/libqos/pci.c +++ b/tests/libqos/pci.c @@ -73,6 +73,12 @@ void qpci_device_enable(QPCIDevice *dev) cmd = qpci_config_readw(dev, PCI_COMMAND); cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; qpci_config_writew(dev, PCI_COMMAND, cmd); + +/* Verify the bits are now set. */ +cmd = qpci_config_readw(dev, PCI_COMMAND); +g_assert_cmphex(cmd & PCI_COMMAND_IO, ==, PCI_COMMAND_IO); +g_assert_cmphex(cmd & PCI_COMMAND_MEMORY, ==, PCI_COMMAND_MEMORY); +g_assert_cmphex(cmd & PCI_COMMAND_MASTER, ==, PCI_COMMAND_MASTER); } uint8_t qpci_config_readb(QPCIDevice *dev, uint8_t offset) -- 1.9.3
[Qemu-devel] [PATCH v3 32/32] ahci: Add test_identify case to ahci-test.
Utilizing all of the bring-up code in pci_enable and hba_enable, this test issues a simple IDENTIFY command via the HBA and retrieves the response via the PIO receive mechanisms of the HBA. Bugs: The DPS interrupt (Descriptor Processed Status) does not currently get set. This will need to be adjusted in a future patch series when the AHCI DMA pathways are reworked to allow the feature, which may be utilized by OSX guests. Signed-off-by: John Snow --- tests/ahci-test.c | 297 ++ 1 file changed, 297 insertions(+) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 31880ed..3c51d27 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -252,6 +252,97 @@ #define AHCI_VERSION_1_2 (0x00010200) #define AHCI_VERSION_1_3 (0x00010300) +/*** Structures ***/ + +/** + * Generic FIS structure. + */ +struct fis { +uint8_t fis_type; +uint8_t flags; +char data[0]; +} __attribute__((__packed__)); + +/** + * Register device-to-host FIS structure. + */ +struct reg_d2h_fis { +/* DW0 */ +uint8_t fis_type; +uint8_t flags; +uint8_t status; +uint8_t error; +/* DW1 */ +uint8_t lba_low; +uint8_t lba_mid; +uint8_t lba_high; +uint8_t device; +/* DW2 */ +uint8_t lba3; +uint8_t lba4; +uint8_t lba5; +uint8_t res1; +/* DW3 */ +uint16_t count; +uint8_t res2; +uint8_t res3; +/* DW4 */ +uint16_t res4; +uint16_t res5; +} __attribute__((__packed__)); + +/** + * Register host-to-device FIS structure. + */ +struct reg_h2d_fis { +/* DW0 */ +uint8_t fis_type; +uint8_t flags; +uint8_t command; +uint8_t feature_low; +/* DW1 */ +uint8_t lba_low; +uint8_t lba_mid; +uint8_t lba_high; +uint8_t device; +/* DW2 */ +uint8_t lba3; +uint8_t lba4; +uint8_t lba5; +uint8_t feature_high; +/* DW3 */ +uint16_t count; +uint8_t icc; +uint8_t control; +/* DW4 */ +uint32_t aux; +} __attribute__((__packed__)); + +/** + * Command List entry structure. + * The command list contains between 1-32 of these structures. + */ +struct ahci_command { +uint8_t b1; +uint8_t b2; +uint16_t prdtl; /* Phys Region Desc. Table Length */ +uint32_t prdbc; /* Phys Region Desc. Byte Count */ +uint32_t ctba; /* Command Table Descriptor Base Address */ +uint32_t ctbau; /*'' Upper */ +uint32_t res[4]; +} __attribute__((__packed__)); + +/** + * Physical Region Descriptor; pointed to by the Command List Header, + * struct ahci_command. + */ +struct prd { +uint32_t dba; /* Data Base Address */ +uint32_t dbau; /* Data Base Address Upper */ +uint32_t res; /* Reserved */ +uint32_t dbc; /* Data Byte Count (0-indexed) & Interrupt Flag (bit 2^31) */ +}; + typedef struct hba_cap { uint32_t cap; uint32_t cap2; @@ -289,6 +380,10 @@ static uint32_t ahci_fingerprint; #define px_clr(port, reg, mask) px_wreg((port), (reg),\ px_rreg((port), (reg)) & ~(mask)); +/* For calculating how big the PRD table needs to be: */ +#define cmd_tbl_siz(n) ((0x80 + ((n) * sizeof(struct prd)) + 0x7F) & ~0x7F) + + /*** Function Declarations ***/ static QPCIDevice *get_ahci_device(void); static QPCIDevice *start_ahci_device(QPCIDevice *dev, void **hba_base); @@ -304,6 +399,17 @@ static void ahci_test_pmcap(QPCIDevice *ahci, uint8_t offset); /*** Utilities ***/ +static void string_cpu_to_be16(uint16_t *s, size_t bytes) +{ +g_assert_cmphex((bytes & 1), ==, 0); +bytes /= 2; + +while (bytes--) { +*s = cpu_to_be16(*s); +s++; +} +} + /** * Locate, verify, and return a handle to the AHCI device. */ @@ -1124,6 +1230,180 @@ static void ahci_test_port_spec(QPCIDevice *ahci, void *hba_base, } } +/** + * Utilizing an initialized AHCI HBA, issue an IDENTIFY command to the first + * device we see, then read and check the response. + */ +static void ahci_test_identify(QPCIDevice *ahci, void *hba_base) +{ +struct reg_d2h_fis *d2h = g_malloc0(0x20); +struct reg_d2h_fis *pio = g_malloc0(0x20); +struct reg_h2d_fis fis; +struct ahci_command cmd; +struct prd prd; +uint32_t ports, reg, clb, table, fb, data_ptr; +uint16_t buff[256]; +unsigned i; +int rc; + +g_assert(ahci != NULL); +g_assert(hba_base != NULL); + +/* We need to: + * (1) Create a Command Table Buffer and update the Command List Slot #0 + * to point to this buffer. + * (2) Construct an FIS host-to-device command structure, and write it to + * the top of the command table buffer. + * (3) Create a data buffer for the IDENTIFY response to be sent to + * (4) Create a Physical Region Descriptor that points to the data buffer, + * and write it to the bottom (offset 0x80) of the command table. + * (5
[Qemu-devel] [PATCH v3 30/32] ahci: Add test_hba_spec to ahci-test.
Add a test routine that checks the boot-up values of the HBA configuration memory space against the AHCI 1.3 specification and Intel ICH9 data sheet (for Q35 machines) for adherence and sane values. The HBA is not yet engaged or put into the idle state. Signed-off-by: John Snow --- tests/ahci-test.c | 555 ++ 1 file changed, 555 insertions(+) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 0ca4a20..85f8661 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -53,6 +53,210 @@ #define AHCI_INTEL_ICH9 (PCI_DEVICE_ID_INTEL_Q35_AHCI << 16 | \ PCI_VENDOR_ID_INTEL) +/*** AHCI/HBA Register Offsets and Bitmasks ***/ +#define AHCI_CAP (0) +#define AHCI_CAP_NP(0x1F) +#define AHCI_CAP_SXS (0x20) +#define AHCI_CAP_EMS (0x40) +#define AHCI_CAP_CCCS (0x80) +#define AHCI_CAP_NCS (0x1F00) +#define AHCI_CAP_PSC (0x2000) +#define AHCI_CAP_SSC (0x4000) +#define AHCI_CAP_PMD (0x8000) +#define AHCI_CAP_FBSS (0x1) +#define AHCI_CAP_SPM(0x2) +#define AHCI_CAP_SAM(0x4) +#define AHCI_CAP_RESERVED (0x8) +#define AHCI_CAP_ISS (0xF0) +#define AHCI_CAP_SCLO (0x100) +#define AHCI_CAP_SAL (0x200) +#define AHCI_CAP_SALP (0x400) +#define AHCI_CAP_SSS (0x800) +#define AHCI_CAP_SMPS(0x1000) +#define AHCI_CAP_SSNTF (0x2000) +#define AHCI_CAP_SNCQ(0x4000) +#define AHCI_CAP_S64A(0x8000) + +#define AHCI_GHC (1) +#define AHCI_GHC_HR(0x01) +#define AHCI_GHC_IE(0x02) +#define AHCI_GHC_MRSM (0x04) +#define AHCI_GHC_RESERVED(0x7FF8) +#define AHCI_GHC_AE (0x8000) + +#define AHCI_IS (2) +#define AHCI_PI (3) +#define AHCI_VS (4) + +#define AHCI_TL (5) +#define AHCI_TL_EN (0x01) +#define AHCI_TL_RESERVED (0x06) +#define AHCI_TL_CC (0xFF00) +#define AHCI_TL_TV (0x) + +#define AHCI_CCCPORTS (6) +#define AHCI_EMLOC(7) + +#define AHCI_EMCTL(8) +#define AHCI_EMCTL_STSMR (0x01) +#define AHCI_EMCTL_CTLTM (0x100) +#define AHCI_EMCTL_CTLRST (0x200) +#define AHCI_EMCTL_RESERVED (0xF0F0FCFE) + +#define AHCI_CAP2 (9) +#define AHCI_CAP2_BOH (0x01) +#define AHCI_CAP2_NVMP (0x02) +#define AHCI_CAP2_APST (0x04) +#define AHCI_CAP2_RESERVED (0xFFF8) + +#define AHCI_BOHC(10) +#define AHCI_RESERVED(11) +#define AHCI_NVMHCI (24) +#define AHCI_VENDOR (40) +#define AHCI_PORTS (64) + +/*** Port Memory Offsets & Bitmasks ***/ +#define AHCI_PX_CLB (0) +#define AHCI_PX_CLB_RESERVED (0x1FF) + +#define AHCI_PX_CLBU (1) + +#define AHCI_PX_FB(2) +#define AHCI_PX_FB_RESERVED(0xFF) + +#define AHCI_PX_FBU (3) + +#define AHCI_PX_IS(4) +#define AHCI_PX_IS_DHRS (0x1) +#define AHCI_PX_IS_PSS (0x2) +#define AHCI_PX_IS_DSS (0x4) +#define AHCI_PX_IS_SDBS (0x8) +#define AHCI_PX_IS_UFS (0x10) +#define AHCI_PX_IS_DPS (0x20) +#define AHCI_PX_IS_PCS (0x40) +#define AHCI_PX_IS_DMPS(0x80) +#define AHCI_PX_IS_RESERVED (0x23FFF00) +#define AHCI_PX_IS_PRCS(0x40) +#define AHCI_PX_IS_IPMS(0x80) +#define AHCI_PX_IS_OFS(0x100) +#define AHCI_PX_IS_INFS (0x400) +#define AHCI_PX_IS_IFS(0x800) +#define AHCI_PX_IS_HBDS (0x1000) +#define AHCI_PX_IS_HBFS (0x2000) +#define AHCI_PX_IS_TFES (0x4000) +#define AHCI_PX_IS_CPDS (0x8000) + +#define AHCI_PX_IE(5) +#define AHCI_PX_IE_DHRE (0x1) +#define AHCI_PX_IE_PSE (0x2) +#define AHCI_PX_IE_DSE (0x4) +#define AHCI_PX_IE_SDBE (0x8) +#define AHCI_PX_IE_UFE (0x10) +#define AHCI_PX_IE_DPE (0x20) +#define AHCI_PX_IE_PCE (0x40) +#define AHCI_PX_IE_DMPE(0x80) +#define AHCI_PX_IE_RESERVED (0x23FFF00) +#define AHCI_PX_IE_PRCE(0x40) +#define AHCI_
[Qemu-devel] [PATCH v3 27/32] ahci: Add test_pci_spec to ahci-test.
Adds a specification adherence test for AHCI where the boot-up values for the PCI configuration space are compared against the AHCI 1.3 specification. This test does not itself attempt to engage the device. Signed-off-by: John Snow --- tests/ahci-test.c | 305 -- 1 file changed, 299 insertions(+), 6 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index cc49dba..29ac0d0 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -25,7 +25,7 @@ #include #include #include - +#include #include #include "libqtest.h" @@ -43,15 +43,36 @@ /*** Supplementary PCI Config Space IDs & Masks ***/ #define PCI_DEVICE_ID_INTEL_Q35_AHCI (0x2922) +#define PCI_MSI_FLAGS_RESERVED (0xFF00) +#define PCI_PM_CTRL_RESERVED (0xFC) +#define PCI_BCC(REG32) ((REG32) >> 24) +#define PCI_PI(REG32) (((REG32) >> 8) & 0xFF) +#define PCI_SCC(REG32) (((REG32) >> 16) & 0xFF) + +/*** Recognized AHCI Device Types ***/ +#define AHCI_INTEL_ICH9 (PCI_DEVICE_ID_INTEL_Q35_AHCI << 16 | \ + PCI_VENDOR_ID_INTEL) /*** Globals ***/ static QGuestAllocator *guest_malloc; static QPCIBus *pcibus; static char tmp_path[] = "/tmp/qtest.XX"; +static bool ahci_pedantic; +static uint32_t ahci_fingerprint; + +/*** Macro Utilities ***/ +#define assert_bit_set(data, mask) g_assert_cmphex((data) & (mask), ==, (mask)) +#define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0) /*** Function Declarations ***/ static QPCIDevice *get_ahci_device(void); static void free_ahci_device(QPCIDevice *dev); +static void ahci_test_pci_spec(QPCIDevice *ahci); +static void ahci_test_pci_caps(QPCIDevice *ahci, uint16_t header, + uint8_t offset); +static void ahci_test_satacap(QPCIDevice *ahci, uint8_t offset); +static void ahci_test_msicap(QPCIDevice *ahci, uint8_t offset); +static void ahci_test_pmcap(QPCIDevice *ahci, uint8_t offset); /*** Utilities ***/ @@ -61,7 +82,6 @@ static void free_ahci_device(QPCIDevice *dev); static QPCIDevice *get_ahci_device(void) { QPCIDevice *ahci; -uint16_t vendor_id, device_id; pcibus = qpci_init_pc(); @@ -69,11 +89,15 @@ static QPCIDevice *get_ahci_device(void) ahci = qpci_device_find(pcibus, QPCI_DEVFN(0x1F, 0x02)); g_assert(ahci != NULL); -vendor_id = qpci_config_readw(ahci, PCI_VENDOR_ID); -device_id = qpci_config_readw(ahci, PCI_DEVICE_ID); +ahci_fingerprint = qpci_config_readl(ahci, PCI_VENDOR_ID); -g_assert_cmphex(vendor_id, ==, PCI_VENDOR_ID_INTEL); -g_assert_cmphex(device_id, ==, PCI_DEVICE_ID_INTEL_Q35_AHCI); +switch (ahci_fingerprint) { +case AHCI_INTEL_ICH9: +break; +default: +/* Unknown device. */ +g_assert_not_reached(); +} return ahci; } @@ -145,6 +169,239 @@ static void ahci_shutdown(QPCIDevice *ahci) qtest_shutdown(); } +/*** Specification Adherence Tests ***/ + +/** + * Implementation for test_pci_spec. Ensures PCI configuration space is sane. + */ +static void ahci_test_pci_spec(QPCIDevice *ahci) +{ +uint8_t datab; +uint16_t data; +uint32_t datal; + +/* Most of these bits should start cleared until we turn them on. */ +data = qpci_config_readw(ahci, PCI_COMMAND); +assert_bit_clear(data, PCI_COMMAND_MEMORY); +assert_bit_clear(data, PCI_COMMAND_MASTER); +assert_bit_clear(data, PCI_COMMAND_SPECIAL); /* Reserved */ +assert_bit_clear(data, PCI_COMMAND_VGA_PALETTE); /* Reserved */ +assert_bit_clear(data, PCI_COMMAND_PARITY); +assert_bit_clear(data, PCI_COMMAND_WAIT);/* Reserved */ +assert_bit_clear(data, PCI_COMMAND_SERR); +assert_bit_clear(data, PCI_COMMAND_FAST_BACK); +assert_bit_clear(data, PCI_COMMAND_INTX_DISABLE); +assert_bit_clear(data, 0xF800); /* Reserved */ + +data = qpci_config_readw(ahci, PCI_STATUS); +assert_bit_clear(data, 0x01 | 0x02 | 0x04); /* Reserved */ +assert_bit_clear(data, PCI_STATUS_INTERRUPT); +assert_bit_set(data, PCI_STATUS_CAP_LIST); /* must be set */ +assert_bit_clear(data, PCI_STATUS_UDF); /* Reserved */ +assert_bit_clear(data, PCI_STATUS_PARITY); +assert_bit_clear(data, PCI_STATUS_SIG_TARGET_ABORT); +assert_bit_clear(data, PCI_STATUS_REC_TARGET_ABORT); +assert_bit_clear(data, PCI_STATUS_REC_MASTER_ABORT); +assert_bit_clear(data, PCI_STATUS_SIG_SYSTEM_ERROR); +assert_bit_clear(data, PCI_STATUS_DETECTED_PARITY); + +/* RID occupies the low byte, CCs occupy the high three. */ +datal = qpci_config_readl(ahci, PCI_CLASS_REVISION); +if (ahci_pedantic) { +/* AHCI 1.3 specifies that at-boot, the RID should reset to 0x00, + * Though in practice this is likely seldom true. */ +assert_bit_clear(datal, 0xFF); +} + +/* BCC *must* equal 0x01. */ +g
Re: [Qemu-devel] [PATCH v2] ide: Add resize callback to ide/core
On 08/14/2014 03:12 AM, Markus Armbruster wrote: I'd prefer if (s->drive_kind == IDE_CFATA) ... else ..., because it avoids the double negative. OK. This is how cmd_identify delegates. For matters of style I usually try to defer to nearby code. Your code duplicates the parts of the functions initializing s->identify_data dealing with nb_sectors. These are: * ide_identify() for IDE_HD Writes nb_sectors to slots 60..61 and 100..103. Matches your code. * ide_atapi_identify() for IDE_CD Doesn't use it at all. Your code clobbers slots 60..61 and 100.103. Oops. ide_resize_cb does not get called for ATAPI drives, so I think this is not relevant. I tried to note this in a comment. See ide_init_drive: if (kind == IDE_CD) { bdrv_set_dev_ops(bs, &ide_cd_block_ops, s); ... } else { ... bdrv_set_dev_ops(bs, &ide_hd_block_ops, s); } * ide_cfata_identify() for IDE_CFATA Writes nb_sectors to slots 7..8 and and 60..61. Your code only writes the former. ...Sorry. I appreciate you taking your time to review these patches. I will submit a V3. I guess code duplication is tolerable, because the format of s->identify_data is unlikely to change. Comments linking the copies together wouldn't hurt, though. If we don't want the duplication, we need to factor the length code out of the identify functions, so we can use it in both places. The length and complexity didn't feel like it needed to be factored out into two new functions, and I liked the idea of keeping the writes to the buffer sequential inside the initialization functions, because it made it easier for me to read along with the spec that way. Factoring it out seemed more of a fruitless hassle than anything. Thanks, --John
Re: [Qemu-devel] [PATCH v3 29/32] ahci: properly shadow the TFD register
On 08/14/2014 12:09 PM, Stefan Hajnoczi wrote: On Wed, Aug 13, 2014 at 05:56:12PM -0400, John Snow wrote: @@ -497,6 +495,8 @@ static void ahci_reset_port(AHCIState *s, int port) pr->scr_stat = 0; pr->scr_err = 0; pr->scr_act = 0; +pr->tfdata = 0x7F; Is it possible to avoid the magic number? I can name it as AHCI_TFD_BOOT_VALUE or AHCI_TFD_INIT_VALUE or so. Same for the PxSIG boot value too. I don't think I can give any greater meaning to the value because it's just a boot signature that means "We haven't received an FIS yet."
[Qemu-devel] IF_AHCI RFC (Was Re: Are -cdrom/-hda (or -drive if=ide) supposed to work in q35?)
On 06/12/2014 05:03 AM, Markus Armbruster wrote: I've always argued for SATA, because for me if=ide does *not* imply a specific kind of HBA any more than if=scsi does, and the "natural" HBA for Q35 is AHCI in SATA mode. Kevin (cc'ed) has argued for a way to make it connect in legacy PATA mode. I'd be fine with that, as long as it's off by default. Patches welcome. I have a design question for fixing this issue. I wired up the Q35 board to search for and add drives for bus=0 to bus=MAX_SATA_PORTS with unit=0 as seen in the blockdev.c dinfo list, as queried by using drive_get or drive_get_by_index. This part works just fine, and -hda and -cdrom work well. However, from the frontend, -hd[a-d] and -cdrom assume a particular index number which is later converted into a bus,unit pairing under drive_new via the functions drive_index_to_bus_id and drive_index_to_unit_id. The math performed here is a function of the IF type we use which is by default IF_IDE. IF_IDE specifies there are two units available per bus. The math for IF_IDE doesn't work for AHCI connected devices, though, which do not allow for master/slave configurations -- meaning that we actually may wind up only picking up -hda, -hdc and -cdrom, and -hdb and -hdd would go unnoticed because we don't have or look for slaves. I have two immediate thoughts for how to correct this: (1) The disgusting way that will make you hate me: Let drive_new do its bad math, but in the q35 initialization, look for drives with a unit != 0 and "fix" them in the drive info entries by adjusting their bus and unit numbers to be compliant with an AHCI setup. This has the least impact, but it looks gross, and could cause problems if anybody ever tries to do any other kind of initialization between initial option parsing and when we do our chipset initialization. (2) Add IF_SATA or IF_AHCI where we can specify that the number of units per bus is 1, and adjust the code accordingly so things get entered into the drive list properly from the start, and picking them up from the Q35 board initialization is trivial. The option I want is #2, but I am not familiar enough with the depth or breadth of the code to confidently say this is the right choice, so I am requesting some feedback on how people feel about this decision. As an added bonus for #2, this does give us another avenue to distinguish between legacy and AHCI interfaces on the Q35 board, and it would allow us to specify for Q35 that the default interface is IF_AHCI/IF_SATA to make the -hd[a-d], -cdrom shorthands more explicit about what they're going to try to do on our board.
[Qemu-devel] [PATCH v3] ide: Add resize callback to ide/core
Currently, if the block device backing the IDE drive is resized, the information about the device as cached inside of the IDEState structure is not updated, thus when a guest OS re-queries the drive, it is unable to see the expanded size. This patch adds a resize callback that updates the IDENTIFY data buffer in order to correct this. Lastly, a Linux guest as-is cannot resize a libata drive while in-use, but it can see the expanded size as part of a bus rescan event. This patch also allows guests such as Linux to see the new drive size after a soft reboot event, without having to exit the QEMU process. Signed-off-by: John Snow --- V3: - Factored out the size update into new functions. - Fixed the size update for CFATA. - Added assertion to clarify that ide_resize_cb is non-atapi. V2: - Do not attempt to update geometry values, to avoid clobbering user-specified values, if they exist. - Do not regenerate the entire IDENTIFY buffer to avoid losing any settings that occurred during normal operation. hw/ide/core.c | 69 ++- 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/hw/ide/core.c b/hw/ide/core.c index db191a6..1fde54e 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -75,6 +75,17 @@ static void put_le16(uint16_t *p, unsigned int v) *p = cpu_to_le16(v); } +static void ide_identify_size(IDEState *s) +{ +uint16_t *p = (uint16_t *)s->identify_data; +put_le16(p + 60, s->nb_sectors); +put_le16(p + 61, s->nb_sectors >> 16); +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_identify(IDEState *s) { uint16_t *p; @@ -116,8 +127,8 @@ static void ide_identify(IDEState *s) put_le16(p + 58, oldsize >> 16); if (s->mult_sectors) put_le16(p + 59, 0x100 | s->mult_sectors); -put_le16(p + 60, s->nb_sectors); -put_le16(p + 61, s->nb_sectors >> 16); +/* *(p + 60) := nb_sectors -- see ide_identify_size */ +/* *(p + 61) := nb_sectors >> 16 -- see ide_identify_size */ put_le16(p + 62, 0x07); /* single word dma0-2 supported */ put_le16(p + 63, 0x07); /* mdma0-2 supported */ put_le16(p + 64, 0x03); /* pio3-4 supported */ @@ -162,10 +173,10 @@ static void ide_identify(IDEState *s) } 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); +/* *(p + 100) := nb_sectors -- see ide_identify_size */ +/* *(p + 101) := nb_sectors >> 16 -- see ide_identify_size */ +/* *(p + 102) := nb_sectors >> 32 -- see ide_identify_size */ +/* *(p + 103) := nb_sectors >> 48 -- see ide_identify_size */ if (dev && dev->conf.physical_block_size) put_le16(p + 106, 0x6000 | get_physical_block_exp(&dev->conf)); @@ -181,6 +192,7 @@ static void ide_identify(IDEState *s) } memcpy(s->identify_data, p, sizeof(s->identify_data)); +ide_identify_size(s); s->identify_set = 1; } @@ -237,6 +249,15 @@ static void ide_atapi_identify(IDEState *s) s->identify_set = 1; } +static void ide_cfata_identify_size(IDEState *s) +{ +uint16_t *p = (uint16_t *)s->identify_data; +put_le16(p + 7, s->nb_sectors >> 16); /* Sectors per card */ +put_le16(p + 8, s->nb_sectors);/* Sectors per card */ +put_le16(p + 60, s->nb_sectors); /* Total LBA sectors */ +put_le16(p + 61, s->nb_sectors >> 16); /* Total LBA sectors */ +} + static void ide_cfata_identify(IDEState *s) { uint16_t *p; @@ -254,8 +275,8 @@ static void ide_cfata_identify(IDEState *s) put_le16(p + 1, s->cylinders); /* Default cylinders */ put_le16(p + 3, s->heads); /* Default heads */ put_le16(p + 6, s->sectors); /* Default sectors per track */ -put_le16(p + 7, s->nb_sectors >> 16); /* Sectors per card */ -put_le16(p + 8, s->nb_sectors);/* Sectors per card */ +/* *(p + 7) := nb_sectors >> 16 -- see ide_cfata_identify_size */ +/* *(p + 8) := nb_sectors -- see ide_cfata_identify_size */ padstr((char *)(p + 10), s->drive_serial_str, 20); /* serial number */ put_le16(p + 22, 0x0004); /* ECC bytes */ padstr((char *) (p + 23), s->version, 8); /* Firmware Revision */ @@ -276,8 +297,8 @@ static void ide_cfata_identify(IDEState *s) put_le16(p + 58, cur_sec >> 16); /* Current capacity */ if (s->mult_sectors
[Qemu-devel] [RFC 1/4] blockdev: add if_get_max_devs
Signed-off-by: John Snow --- blockdev.c| 9 + include/sysemu/blockdev.h | 1 + 2 files changed, 10 insertions(+) diff --git a/blockdev.c b/blockdev.c index 48bd9a3..58da77f 100644 --- a/blockdev.c +++ b/blockdev.c @@ -110,6 +110,15 @@ void blockdev_auto_del(BlockDriverState *bs) } } +int if_get_max_devs(BlockInterfaceType type) +{ +if (type >= IF_IDE && type < IF_COUNT) { +return if_max_devs[type]; +} + +return 0; +} + static int drive_index_to_bus_id(BlockInterfaceType type, int index) { int max_devs = if_max_devs[type]; diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 23a5d10..2420abd 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -48,6 +48,7 @@ struct DriveInfo { DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit); DriveInfo *drive_get_by_index(BlockInterfaceType type, int index); int drive_get_max_bus(BlockInterfaceType type); +int if_get_max_devs(BlockInterfaceType type); DriveInfo *drive_get_next(BlockInterfaceType type); DriveInfo *drive_get_by_blockdev(BlockDriverState *bs); -- 1.9.3
[Qemu-devel] [RFC 4/4] ahci: implement -cdrom and -hd[a-d]
Signed-off-by: John Snow --- hw/i386/pc_q35.c | 4 hw/ide/ahci.c| 17 + hw/ide/ahci.h| 2 ++ 3 files changed, 23 insertions(+) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 4b5a274..4613565 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -86,6 +86,7 @@ static void pc_q35_init(MachineState *machine) DeviceState *icc_bridge; PcGuestInfo *guest_info; ram_addr_t lowmem; +DriveInfo *hd[MAX_SATA_PORTS]; /* Check whether RAM fits below 4G (leaving 1/2 GByte for IO memory * and 256 Mbytes for PCI Express Enhanced Configuration Access Mapping @@ -253,6 +254,8 @@ static void pc_q35_init(MachineState *machine) true, "ich9-ahci"); idebus[0] = qdev_get_child_bus(&ahci->qdev, "ide.0"); idebus[1] = qdev_get_child_bus(&ahci->qdev, "ide.1"); +ide_drive_get(hd, IF_AHCI, MAX_SATA_PORTS); +ahci_ide_create_devs(ahci, hd); if (usb_enabled(false)) { /* Should we create 6 UHCI according to ich9 spec? */ @@ -354,6 +357,7 @@ static QEMUMachine pc_q35_machine_v2_2 = { .name = "pc-q35-2.2", .alias = "q35", .init = pc_q35_init, +.block_default_type = IF_AHCI, }; #define PC_Q35_2_1_MACHINE_OPTIONS PC_Q35_2_2_MACHINE_OPTIONS diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 4cda0d0..bc8b8e9 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1403,3 +1403,20 @@ static void sysbus_ahci_register_types(void) } type_init(sysbus_ahci_register_types) + +void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **hd_table) +{ +AHCIPCIState *d = ICH_AHCI(dev); +AHCIState *ahci = &d->ahci; +unsigned i; + +for (i = 0; i < ahci->ports; i++) { +if (hd_table[i] == NULL) { +continue; +} +fprintf(stderr, "ahci_ide_create_devs: ide_create_drive(bus:%p, 0, drive[index:%u])\n", +(void *)&ahci->dev[i].port, i); +ide_create_drive(&ahci->dev[i].port, 0, hd_table[i]); +} + +} diff --git a/hw/ide/ahci.h b/hw/ide/ahci.h index 1543df7..41daedb 100644 --- a/hw/ide/ahci.h +++ b/hw/ide/ahci.h @@ -332,4 +332,6 @@ void ahci_uninit(AHCIState *s); void ahci_reset(AHCIState *s); +void ahci_ide_create_devs(PCIDevice *dev, DriveInfo **hd_table); + #endif /* HW_IDE_AHCI_H */ -- 1.9.3
[Qemu-devel] [RFC 0/4] Adding -cdrom, -hd[abcd] and -drive file=... to Q35
Currently, the drive definitions created by drive_new() when using the -drive file=...[,if=ide] or -cdrom or -hd[abcd] options are not picked up by the Q35 initialization routine. To fix this, we have to add hooks to search for these drives using something like pc_piix's ide_drive_get and then add them using something like pci_ide_create_devs. Where it gets slightly wonky is the fact that if=ide is specified to use two devices per bus, whereas AHCI does not utilize that same master/slave mechanic. Therefore, many places inside of blockdev.c where we add and define new drives use incorrect math for AHCI devices and try to place them on impossible buses. Notably -hdb and -hdd would become inaccessible. To remedy this, I added a new interface type, IF_AHCI. Corresponding to this change, I modified the default machine properties for Q35 to use this interface as a default. The changes appear to work well, but where I'd like some feedback is what should happen if people do something like: qemu -M q35 -drive if=ide,file=fedora.qcow2 The code as presented here is not going to look for or attempt to connect IDE devices, because it is now looking for /AHCI/ devices. At worst, this may break a few existing scripts, but I actually think that since the if=ide,file=... shorthand never worked to begin with, the impact might actually be minimal. But since the legacy IDE interface of the ICH9 is as of yet unemulated, the if=ide drives don't have a reasonable place to go yet. I am also not sure what a reasonable way to handle people specifying BOTH if=ide and if=ahci drives would be. John Snow (4): blockdev: add if_get_max_devs blockdev: add IF_AHCI to support -cdrom and -hd[a-d] ide: update ide_drive_get to work with both PCI-IDE and AHCI interfaces ahci: implement -cdrom and -hd[a-d] blockdev.c| 18 +++--- hw/i386/pc_piix.c | 2 +- hw/i386/pc_q35.c | 4 hw/ide/ahci.c | 17 + hw/ide/ahci.h | 2 ++ hw/ide/core.c | 11 +++ include/hw/ide.h | 3 ++- include/sysemu/blockdev.h | 3 ++- 8 files changed, 50 insertions(+), 10 deletions(-) -- 1.9.3
[Qemu-devel] [RFC 2/4] blockdev: add IF_AHCI to support -cdrom and -hd[a-d]
Signed-off-by: John Snow --- blockdev.c| 9 ++--- include/sysemu/blockdev.h | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/blockdev.c b/blockdev.c index 58da77f..a9efe1f 100644 --- a/blockdev.c +++ b/blockdev.c @@ -57,6 +57,7 @@ static const char *const if_name[IF_COUNT] = { [IF_SD] = "sd", [IF_VIRTIO] = "virtio", [IF_XEN] = "xen", +[IF_AHCI] = "ahci", }; static const int if_max_devs[IF_COUNT] = { @@ -76,6 +77,7 @@ static const int if_max_devs[IF_COUNT] = { */ [IF_IDE] = 2, [IF_SCSI] = 7, +[IF_AHCI] = 1, }; /* @@ -876,7 +878,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) if (qemu_opts_id(all_opts) == NULL) { char *new_id; const char *mediastr = ""; -if (type == IF_IDE || type == IF_SCSI) { +if (type == IF_IDE || type == IF_SCSI || type == IF_AHCI) { mediastr = (media == MEDIA_CDROM) ? "-cd" : "-hd"; } if (max_devs) { @@ -918,7 +920,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) werror = qemu_opt_get(legacy_opts, "werror"); if (werror != NULL) { if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && -type != IF_NONE) { +type != IF_NONE && type != IF_AHCI) { error_report("werror is not supported by this bus type"); goto fail; } @@ -928,7 +930,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) rerror = qemu_opt_get(legacy_opts, "rerror"); if (rerror != NULL) { if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && -type != IF_NONE) { +type != IF_NONE && type != IF_AHCI) { error_report("rerror is not supported by this bus type"); goto fail; } @@ -966,6 +968,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) switch(type) { case IF_IDE: +case IF_AHCI: case IF_SCSI: case IF_XEN: case IF_NONE: diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 2420abd..6b02cf4 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -25,7 +25,7 @@ typedef enum { */ IF_IDE = 0, IF_NONE, -IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN, +IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN, IF_AHCI, IF_COUNT } BlockInterfaceType; -- 1.9.3
[Qemu-devel] [RFC 3/4] ide: update ide_drive_get to work with both PCI-IDE and AHCI interfaces
Signed-off-by: John Snow --- hw/i386/pc_piix.c | 2 +- hw/ide/core.c | 11 +++ include/hw/ide.h | 3 ++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 47ac1b5..9da6f0e 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -239,7 +239,7 @@ static void pc_init1(MachineState *machine, pc_nic_init(isa_bus, pci_bus); -ide_drive_get(hd, MAX_IDE_BUS); +ide_drive_get(hd, IF_IDE, MAX_IDE_BUS); if (pci_enabled) { PCIDevice *dev; if (xen_enabled()) { diff --git a/hw/ide/core.c b/hw/ide/core.c index b48127f..408d7c1 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -2481,16 +2481,19 @@ const VMStateDescription vmstate_ide_bus = { } }; -void ide_drive_get(DriveInfo **hd, int max_bus) +void ide_drive_get(DriveInfo **hd, BlockInterfaceType type, int max_bus) { int i; -if (drive_get_max_bus(IF_IDE) >= max_bus) { +if (drive_get_max_bus(type) >= max_bus) { fprintf(stderr, "qemu: too many IDE bus: %d\n", max_bus); exit(1); } -for(i = 0; i < max_bus * MAX_IDE_DEVS; i++) { -hd[i] = drive_get(IF_IDE, i / MAX_IDE_DEVS, i % MAX_IDE_DEVS); +for (i = 0; i < max_bus * if_get_max_devs(type); i++) { +fprintf(stderr, "hd[%u] := drive_get_by_index(%d, %u);\n", +i, type, i); +hd[i] = drive_get_by_index(type, i); +fprintf(stderr, "hd[%u] := %p\n", i, (void *)hd[i]); } } diff --git a/include/hw/ide.h b/include/hw/ide.h index bc8bd32..c4400ab 100644 --- a/include/hw/ide.h +++ b/include/hw/ide.h @@ -4,6 +4,7 @@ #include "hw/isa/isa.h" #include "hw/pci/pci.h" #include "exec/memory.h" +#include "sysemu/blockdev.h" #define MAX_IDE_DEVS 2 @@ -28,6 +29,6 @@ int ide_get_geometry(BusState *bus, int unit, int ide_get_bios_chs_trans(BusState *bus, int unit); /* ide/core.c */ -void ide_drive_get(DriveInfo **hd, int max_bus); +void ide_drive_get(DriveInfo **hd, BlockInterfaceType type, int max_bus); #endif /* HW_IDE_H */ -- 1.9.3
Re: [Qemu-devel] [RFC 0/4] Adding -cdrom, -hd[abcd] and -drive file=... to Q35
On 08/19/2014 04:05 AM, Markus Armbruster wrote: John Snow writes: Currently, the drive definitions created by drive_new() when using the -drive file=...[,if=ide] or -cdrom or -hd[abcd] options are not picked up by the Q35 initialization routine. To fix this, we have to add hooks to search for these drives using something like pc_piix's ide_drive_get and then add them using something like pci_ide_create_devs. ide_drive_get() isn't pc_piix's, it's a helper function in the IDE core which boards (not just pc_piix) use to find the if=ide drives. It fills in an array of DriveInfo. pci_ide_create_devs() is a helper function in the IDE PCI code which PCI IDE controllers (not just piix3-ide) use to create IDE devices for an array of DriveInfo. Yes. I meant to say pc_piix's /call to/ ide_drive_get. I would have to patch up the other boards if I changed this function! Only an RFC before I got too far down this path :] Where it gets slightly wonky is the fact that if=ide is specified to use two devices per bus, whereas AHCI does not utilize that same master/slave mechanic. Therefore, many places inside of blockdev.c where we add and define new drives use incorrect math for AHCI devices and try to place them on impossible buses. Notably -hdb and -hdd would become inaccessible. Yes. To remedy this, I added a new interface type, IF_AHCI. Corresponding to this change, I modified the default machine properties for Q35 to use this interface as a default. The changes appear to work well, but where I'd like some feedback is what should happen if people do something like: qemu -M q35 -drive if=ide,file=fedora.qcow2 The code as presented here is not going to look for or attempt to connect IDE devices, because it is now looking for /AHCI/ devices. At worst, this may break a few existing scripts, but I actually think that since the if=ide,file=... shorthand never worked to begin with, the impact might actually be minimal. But since the legacy IDE interface of the ICH9 is as of yet unemulated, the if=ide drives don't have a reasonable place to go yet. I am also not sure what a reasonable way to handle people specifying BOTH if=ide and if=ahci drives would be. We've been through IF_AHCI before, more than once, but that was before you got involved :) The problem at hand is that "-drive if=ide" and its sugared forms -hda, -hdb, -cdrom, ... don't work with Q35. You provide a solution for the sugared forms, you leave the problem unsolved for the unsugared form, and you add a new problem: -drive if=ahci doesn't work with i440FX. Progress, sort of. Adding a call to boards that support the AHCI device during their initialization should be easy enough, if we decide that "ide means ISA/PCI, ahci means the AHCI HBA." I could probably even write one generic routine between i440fx and q35 to do both IDE/AHCI. If we decide that IF_IDE and IF_AHCI mean different things, the problem of the unsugared form being unsolved depends on me (well, or someone) implementing the legacy IDE interface for Q35. Let's take a step back, and recap previous discussion. There are two defensible point of views, in my opinion. One is that IDE and AHCI should be separate interface types, just like IDE and SCSI are. Attempts to define an if=X drive with a board that doesn't provide a controller for X should fail[*]. Only onboard controllers matter, add-ons plugged in with -device don't. An i440FX board provides only IDE. A Q35 board provides only AHCI, not IDE. If we implement an ich9-ahci legacy mode, and switch it on, then it provides only IDE, not AHCI. Or maybe both, depending on how we do it. I think I am leaning towards this viewpoint, but it depends on what "interface" means in QEMU. Currently, the number of units per bus is tied to the "interface" and clearly the AHCI SATA interface only supports one per bus, so semantically this makes sense. I think the real ICH9 AHCI device supports only fully AHCI or fully legacy, but the AHCI spec itself appears to allow you to run a mixed-mode device. I am not sure we have a usage case for mixed-mode, so enforcing either/or for the AHCI device makes sense for now, I think. The other point of view is that IDE and AHCI are no more different than the different kinds of SCSI HBAs. This is certainly true from a qdev point of view: just like SCSI devices can connect to any SCSI qbus, regardless of the HBA providing it, so can IDE devices connect to any IDE qbus, regardless of the controller providing it. Yes... Really the only difference are some mapping semantics. I don't think there's any /other/ reason I needed IF_AHCI, of course, I wasn't around for the previous discussions, so maybe there are other reasons I am not aware of. There's a wrinkle: the mapping between index to (bus, unit). This mapping is ABI. The current map
Re: [Qemu-devel] [RFC 0/4] Adding -cdrom, -hd[abcd] and -drive file=... to Q35
On 08/19/2014 12:12 PM, Dr. David Alan Gilbert wrote: * John Snow (js...@redhat.com) wrote: The changes appear to work well, but where I'd like some feedback is what should happen if people do something like: qemu -M q35 -drive if=ide,file=fedora.qcow2 The code as presented here is not going to look for or attempt to connect IDE devices, because it is now looking for /AHCI/ devices. What happens if you try it - does it just silently ignore that -drive? The ideal would be for something to moan about unused drives so people realise why it's broken; but I seem to remember form a previous discussion it's hard to do on all platforms. With this patch, piix would continue to ignore AHCI and Q35 would ignore IDE. piix and Q35 both can be corrected to look for either/or IDE/AHCI and whine if you mix them. Q35 and piix both could be made to throw an error if you specify drives that the board doesn't pick up (AHCI in the case of piix if you have not specified that device, legacy IDE in the case of Q35 until we add legacy mode support and the user specifies that mode.) I suggested to Markus in a different reply that I could add a little flag to the drive list to help identify if any drives don't get picked up at board spin-up time... But it sounds like it might not be quite so simple. I will wait to add error handling modes until there is perhaps some consensus that the basic approach here is desirable. -- —js
Re: [Qemu-devel] [PATCH v3 0/4] libqos: add a simple first-fit memory allocator
On 08/05/2014 05:46 AM, Stefan Hajnoczi wrote: On Fri, Aug 01, 2014 at 11:38:55AM -0400, John Snow wrote: This set collects two patches by Marc Marí already on the mailing list, but goes further by adding a simple memory allocator that allows us to track and debug freed memory, and optionally keep track of any leaks. For convenience: https://github.com/jnsnow/qemu/tree/libqos-alloc v2: use QTAILQ as a basis for the linked list implementation instead. Correct an error in the size of the initial node. v3: remove mlist wrappers around QTAILQ interface for clarity. adjust the options controlling when to do allocation list debugging. John Snow (2): libqos: add a simple first-fit memory allocator qtest/ide: Uninitialize PC allocator Marc Marí (2): libqos: Correct mask to align size to PAGE_SIZE in malloc-pc libqos: Change free function called in malloc tests/ide-test.c | 2 + tests/libqos/malloc-pc.c | 280 +-- tests/libqos/malloc-pc.h | 9 ++ tests/libqos/malloc.h| 2 +- 4 files changed, 282 insertions(+), 11 deletions(-) -- 1.9.3 Reviewed-by: Stefan Hajnoczi Ping? (The first two patches in this series have been merged into upstream/master already.) -- —js
[Qemu-devel] [PATCH] ide: Add wwn support to IDE-ATAPI drive
Although it is possible to specify the wwn property for cdrom devices on the command line, the underlying driver fails to relay this information to the guest operating system via IDENTIFY. This is a simple patch to correct that. See ATA8-ACS, Table 22 parts 5, 6, and 9. Signed-off-by: John Snow --- hw/ide/core.c | 14 ++ 1 file changed, 14 insertions(+) diff --git a/hw/ide/core.c b/hw/ide/core.c index b48127f..de0e5e9 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -230,9 +230,23 @@ static void ide_atapi_identify(IDEState *s) } put_le16(p + 80, 0x1e); /* support up to ATA/ATAPI-4 */ +if (s->wwn) { +put_le16(p + 84, (1 << 8)); /* supports WWN for words 108-111 */ +put_le16(p + 87, (1 << 8)); /* WWN enabled */ +} + #ifdef USE_DMA_CDROM put_le16(p + 88, 0x3f | (1 << 13)); /* udma5 set and supported */ #endif + +if (s->wwn) { +/* LE 16-bit words 111-108 contain 64-bit World Wide Name */ +put_le16(p + 108, s->wwn >> 48); +put_le16(p + 109, s->wwn >> 32); +put_le16(p + 110, s->wwn >> 16); +put_le16(p + 111, s->wwn); +} + memcpy(s->identify_data, p, sizeof(s->identify_data)); s->identify_set = 1; } -- 1.9.3
Re: [Qemu-devel] [RFC 0/4] Adding -cdrom, -hd[abcd] and -drive file=... to Q35
On 08/19/2014 02:08 PM, Markus Armbruster wrote: John Snow writes: On 08/19/2014 04:05 AM, Markus Armbruster wrote: John Snow writes: Currently, the drive definitions created by drive_new() when using the -drive file=...[,if=ide] or -cdrom or -hd[abcd] options are not picked up by the Q35 initialization routine. To fix this, we have to add hooks to search for these drives using something like pc_piix's ide_drive_get and then add them using something like pci_ide_create_devs. ide_drive_get() isn't pc_piix's, it's a helper function in the IDE core which boards (not just pc_piix) use to find the if=ide drives. It fills in an array of DriveInfo. pci_ide_create_devs() is a helper function in the IDE PCI code which PCI IDE controllers (not just piix3-ide) use to create IDE devices for an array of DriveInfo. Yes. I meant to say pc_piix's /call to/ ide_drive_get. I would have to patch up the other boards if I changed this function! Only an RFC before I got too far down this path :] Where it gets slightly wonky is the fact that if=ide is specified to use two devices per bus, whereas AHCI does not utilize that same master/slave mechanic. Therefore, many places inside of blockdev.c where we add and define new drives use incorrect math for AHCI devices and try to place them on impossible buses. Notably -hdb and -hdd would become inaccessible. Yes. To remedy this, I added a new interface type, IF_AHCI. Corresponding to this change, I modified the default machine properties for Q35 to use this interface as a default. The changes appear to work well, but where I'd like some feedback is what should happen if people do something like: qemu -M q35 -drive if=ide,file=fedora.qcow2 The code as presented here is not going to look for or attempt to connect IDE devices, because it is now looking for /AHCI/ devices. At worst, this may break a few existing scripts, but I actually think that since the if=ide,file=... shorthand never worked to begin with, the impact might actually be minimal. But since the legacy IDE interface of the ICH9 is as of yet unemulated, the if=ide drives don't have a reasonable place to go yet. I am also not sure what a reasonable way to handle people specifying BOTH if=ide and if=ahci drives would be. We've been through IF_AHCI before, more than once, but that was before you got involved :) The problem at hand is that "-drive if=ide" and its sugared forms -hda, -hdb, -cdrom, ... don't work with Q35. You provide a solution for the sugared forms, you leave the problem unsolved for the unsugared form, and you add a new problem: -drive if=ahci doesn't work with i440FX. Progress, sort of. Adding a call to boards that support the AHCI device during their initialization should be easy enough, if we decide that "ide means ISA/PCI, ahci means the AHCI HBA." I could probably even write one generic routine between i440fx and q35 to do both IDE/AHCI. If we decide that IF_IDE and IF_AHCI mean different things, the problem of the unsugared form being unsolved depends on me (well, or someone) implementing the legacy IDE interface for Q35. Let me come back to this further down. Let's take a step back, and recap previous discussion. There are two defensible point of views, in my opinion. One is that IDE and AHCI should be separate interface types, just like IDE and SCSI are. Attempts to define an if=X drive with a board that doesn't provide a controller for X should fail[*]. Only onboard controllers matter, add-ons plugged in with -device don't. An i440FX board provides only IDE. A Q35 board provides only AHCI, not IDE. If we implement an ich9-ahci legacy mode, and switch it on, then it provides only IDE, not AHCI. Or maybe both, depending on how we do it. I think I am leaning towards this viewpoint, but it depends on what "interface" means in QEMU. Currently, the number of units per bus is tied to the "interface" and clearly the AHCI SATA interface only supports one per bus, so semantically this makes sense. An index <-> (bus, unit) mapping doesn't make an interface! Yes, it's tightly coupled to the interface, but that became wrong way back when we went beyond 8-bit SCSI HBAs, long before we added up AHCI HBAs. "Interface" seems nebulous in QEMU. In the physical world, there definitely is a difference between IDE/EIDE/PATA and SATA/AHCI. Different physical and link layers -- the verbs stayed the same. What I am getting at is that I am not sure what an "interface" is /supposed/ to encompass here in QEMU. Underlying device type? If that's the case, then IDE/AHCI are definitely identical. I think the real ICH9 AHCI device supports only fully AHCI or fully legacy, but the AHCI spec itself appears to allow you to run a mixed-mode device. I am not sure we have a usage case for mixed-mode, so enforcing either/or for th
Re: [Qemu-devel] [PATCH v3] ide: Add resize callback to ide/core
On 08/18/2014 09:12 AM, Stefan Hajnoczi wrote: On Thu, Aug 14, 2014 at 05:03:06PM -0400, John Snow wrote: @@ -162,10 +173,10 @@ static void ide_identify(IDEState *s) } 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); +/* *(p + 100) := nb_sectors -- see ide_identify_size */ +/* *(p + 101) := nb_sectors >> 16 -- see ide_identify_size */ +/* *(p + 102) := nb_sectors >> 32 -- see ide_identify_size */ +/* *(p + 103) := nb_sectors >> 48 -- see ide_identify_size */ These comments bitrot easily. I'd prefer not to have them, but not a reason to respin: Reviewed-by: Stefan Hajnoczi Ping? Respin w/o comments, or is this fine? -- —js
[Qemu-devel] [PATCH v4 5/8] ahci: properly shadow the TFD register
In a real AHCI device, several S/ATA registers are mirrored or shadowed within the AHCI register set. These registers are not updated synchronously for each read access, but are instead updated after a Device-to-Host Register FIS packet is received. The D2H FIS contains the values from these registers on the device. In QEMU, by reaching directly into the device to grab these bits before they are "sent," we may introduce race conditions where unexpected values are present "before they are sent" which could cause issues for some guests, particularly if an attempt is made to read the PxTFD register prior to enabling the port, where incorrect values will be read. This patch also addresses the boot-time values for the PxTFD and PxSIG registers to bring them in line with the AHCI 1.3 specification. Lastly, several fields (PxTFD, PxSIG and PxSACT) are read-only, and any attempts to write to them should be ignored. Signed-off-by: John Snow --- hw/ide/ahci.c | 42 -- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 932b0d5..5ff1442 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -78,8 +78,7 @@ static uint32_t ahci_port_read(AHCIState *s, int port, int offset) val = pr->cmd; break; case PORT_TFDATA: -val = ((uint16_t)s->dev[port].port.ifs[0].error << 8) | - s->dev[port].port.ifs[0].status; +val = pr->tfdata; break; case PORT_SIG: val = pr->sig; @@ -251,14 +250,13 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) check_cmd(s, port); break; case PORT_TFDATA: -s->dev[port].port.ifs[0].error = (val >> 8) & 0xff; -s->dev[port].port.ifs[0].status = val & 0xff; +/* Read Only. */ break; case PORT_SIG: -pr->sig = val; +/* Read Only */ break; case PORT_SCR_STAT: -pr->scr_stat = val; +/* Read Only */ break; case PORT_SCR_CTL: if (((pr->scr_ctl & AHCI_SCR_SCTL_DET) == 1) && @@ -497,6 +495,8 @@ static void ahci_reset_port(AHCIState *s, int port) pr->scr_stat = 0; pr->scr_err = 0; pr->scr_act = 0; +pr->tfdata = 0x7F; +pr->sig = 0x; d->busy_slot = -1; d->init_d2h_sent = false; @@ -528,16 +528,16 @@ static void ahci_reset_port(AHCIState *s, int port) s->dev[port].port_state = STATE_RUN; if (!ide_state->bs) { -s->dev[port].port_regs.sig = 0; +pr->sig = 0; ide_state->status = SEEK_STAT | WRERR_STAT; } else if (ide_state->drive_kind == IDE_CD) { -s->dev[port].port_regs.sig = SATA_SIGNATURE_CDROM; +pr->sig = SATA_SIGNATURE_CDROM; ide_state->lcyl = 0x14; ide_state->hcyl = 0xeb; DPRINTF(port, "set lcyl = %d\n", ide_state->lcyl); ide_state->status = SEEK_STAT | WRERR_STAT | READY_STAT; } else { -s->dev[port].port_regs.sig = SATA_SIGNATURE_DISK; +pr->sig = SATA_SIGNATURE_DISK; ide_state->status = SEEK_STAT | WRERR_STAT; } @@ -563,7 +563,8 @@ static void debug_print_fis(uint8_t *fis, int cmd_len) static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished) { -AHCIPortRegs *pr = &s->dev[port].port_regs; +AHCIDevice *ad = &s->dev[port]; +AHCIPortRegs *pr = &ad->port_regs; IDEState *ide_state; uint8_t *sdb_fis; @@ -572,8 +573,8 @@ static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished) return; } -sdb_fis = &s->dev[port].res_fis[RES_FIS_SDBFIS]; -ide_state = &s->dev[port].port.ifs[0]; +sdb_fis = &ad->res_fis[RES_FIS_SDBFIS]; +ide_state = &ad->port.ifs[0]; /* clear memory */ *(uint32_t*)sdb_fis = 0; @@ -582,9 +583,14 @@ static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished) sdb_fis[0] = ide_state->error; sdb_fis[2] = ide_state->status & 0x77; s->dev[port].finished |= finished; -*(uint32_t*)(sdb_fis + 4) = cpu_to_le32(s->dev[port].finished); +*(uint32_t*)(sdb_fis + 4) = cpu_to_le32(ad->finished); -ahci_trigger_irq(s, &s->dev[port], PORT_IRQ_SDB_FIS); +/* Update shadow registers (except BSY 0x80 and DRQ 0x08) */ +pr->tfdata = (ad->port.ifs[0].error << 8) | +(ad->port.ifs[0].status & 0x77) | +(pr->tfdata & 0x88); + +ahci_trigger_irq(s, ad, PORT_IRQ_SDB_FIS); } static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) @@ -642,6 +648,10 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len) pio_fis[18] = 0; pio_fis[19
[Qemu-devel] [PATCH v4 3/8] ahci: Add test_pci_spec to ahci-test.
Adds a specification adherence test for AHCI where the boot-up values for the PCI configuration space are compared against the AHCI 1.3 specification. This test does not itself attempt to engage the device. Signed-off-by: John Snow --- tests/ahci-test.c | 305 -- 1 file changed, 299 insertions(+), 6 deletions(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index cc49dba..0674d5e 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -25,7 +25,7 @@ #include #include #include - +#include #include #include "libqtest.h" @@ -43,15 +43,36 @@ /*** Supplementary PCI Config Space IDs & Masks ***/ #define PCI_DEVICE_ID_INTEL_Q35_AHCI (0x2922) +#define PCI_MSI_FLAGS_RESERVED (0xFF00) +#define PCI_PM_CTRL_RESERVED (0xFC) +#define PCI_BCC(REG32) ((REG32) >> 24) +#define PCI_PI(REG32) (((REG32) >> 8) & 0xFF) +#define PCI_SCC(REG32) (((REG32) >> 16) & 0xFF) + +/*** Recognized AHCI Device Types ***/ +#define AHCI_INTEL_ICH9 (PCI_DEVICE_ID_INTEL_Q35_AHCI << 16 | \ + PCI_VENDOR_ID_INTEL) /*** Globals ***/ static QGuestAllocator *guest_malloc; static QPCIBus *pcibus; static char tmp_path[] = "/tmp/qtest.XX"; +static bool ahci_pedantic; +static uint32_t ahci_fingerprint; + +/*** Macro Utilities ***/ +#define ASSERT_BIT_SET(data, mask) g_assert_cmphex((data) & (mask), ==, (mask)) +#define ASSERT_BIT_CLEAR(data, mask) g_assert_cmphex((data) & (mask), ==, 0) /*** Function Declarations ***/ static QPCIDevice *get_ahci_device(void); static void free_ahci_device(QPCIDevice *dev); +static void ahci_test_pci_spec(QPCIDevice *ahci); +static void ahci_test_pci_caps(QPCIDevice *ahci, uint16_t header, + uint8_t offset); +static void ahci_test_satacap(QPCIDevice *ahci, uint8_t offset); +static void ahci_test_msicap(QPCIDevice *ahci, uint8_t offset); +static void ahci_test_pmcap(QPCIDevice *ahci, uint8_t offset); /*** Utilities ***/ @@ -61,7 +82,6 @@ static void free_ahci_device(QPCIDevice *dev); static QPCIDevice *get_ahci_device(void) { QPCIDevice *ahci; -uint16_t vendor_id, device_id; pcibus = qpci_init_pc(); @@ -69,11 +89,15 @@ static QPCIDevice *get_ahci_device(void) ahci = qpci_device_find(pcibus, QPCI_DEVFN(0x1F, 0x02)); g_assert(ahci != NULL); -vendor_id = qpci_config_readw(ahci, PCI_VENDOR_ID); -device_id = qpci_config_readw(ahci, PCI_DEVICE_ID); +ahci_fingerprint = qpci_config_readl(ahci, PCI_VENDOR_ID); -g_assert_cmphex(vendor_id, ==, PCI_VENDOR_ID_INTEL); -g_assert_cmphex(device_id, ==, PCI_DEVICE_ID_INTEL_Q35_AHCI); +switch (ahci_fingerprint) { +case AHCI_INTEL_ICH9: +break; +default: +/* Unknown device. */ +g_assert_not_reached(); +} return ahci; } @@ -145,6 +169,239 @@ static void ahci_shutdown(QPCIDevice *ahci) qtest_shutdown(); } +/*** Specification Adherence Tests ***/ + +/** + * Implementation for test_pci_spec. Ensures PCI configuration space is sane. + */ +static void ahci_test_pci_spec(QPCIDevice *ahci) +{ +uint8_t datab; +uint16_t data; +uint32_t datal; + +/* Most of these bits should start cleared until we turn them on. */ +data = qpci_config_readw(ahci, PCI_COMMAND); +ASSERT_BIT_CLEAR(data, PCI_COMMAND_MEMORY); +ASSERT_BIT_CLEAR(data, PCI_COMMAND_MASTER); +ASSERT_BIT_CLEAR(data, PCI_COMMAND_SPECIAL); /* Reserved */ +ASSERT_BIT_CLEAR(data, PCI_COMMAND_VGA_PALETTE); /* Reserved */ +ASSERT_BIT_CLEAR(data, PCI_COMMAND_PARITY); +ASSERT_BIT_CLEAR(data, PCI_COMMAND_WAIT);/* Reserved */ +ASSERT_BIT_CLEAR(data, PCI_COMMAND_SERR); +ASSERT_BIT_CLEAR(data, PCI_COMMAND_FAST_BACK); +ASSERT_BIT_CLEAR(data, PCI_COMMAND_INTX_DISABLE); +ASSERT_BIT_CLEAR(data, 0xF800); /* Reserved */ + +data = qpci_config_readw(ahci, PCI_STATUS); +ASSERT_BIT_CLEAR(data, 0x01 | 0x02 | 0x04); /* Reserved */ +ASSERT_BIT_CLEAR(data, PCI_STATUS_INTERRUPT); +ASSERT_BIT_SET(data, PCI_STATUS_CAP_LIST); /* must be set */ +ASSERT_BIT_CLEAR(data, PCI_STATUS_UDF); /* Reserved */ +ASSERT_BIT_CLEAR(data, PCI_STATUS_PARITY); +ASSERT_BIT_CLEAR(data, PCI_STATUS_SIG_TARGET_ABORT); +ASSERT_BIT_CLEAR(data, PCI_STATUS_REC_TARGET_ABORT); +ASSERT_BIT_CLEAR(data, PCI_STATUS_REC_MASTER_ABORT); +ASSERT_BIT_CLEAR(data, PCI_STATUS_SIG_SYSTEM_ERROR); +ASSERT_BIT_CLEAR(data, PCI_STATUS_DETECTED_PARITY); + +/* RID occupies the low byte, CCs occupy the high three. */ +datal = qpci_config_readl(ahci, PCI_CLASS_REVISION); +if (ahci_pedantic) { +/* AHCI 1.3 specifies that at-boot, the RID should reset to 0x00, + * Though in practice this is likely seldom true. */ +ASSERT_BIT_CLEAR(datal, 0xFF); +} + +/* BCC *must* equal 0x01. */ +g
[Qemu-devel] [PATCH v4 1/8] ahci: Adding basic functionality qtest.
Currently, there is no qtest to test the functionality of the AHCI functionality present within the Q35 machine type. This patch adds a skeleton for an AHCI test suite, and adds a simple sanity-check test case where we identify that the AHCI device is present, then disengage the virtual machine. Signed-off-by: John Snow --- tests/Makefile| 2 + tests/ahci-test.c | 196 ++ 2 files changed, 198 insertions(+) create mode 100644 tests/ahci-test.c diff --git a/tests/Makefile b/tests/Makefile index 837e9c8..b65cec5 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -132,6 +132,7 @@ check-qtest-i386-y = tests/endianness-test$(EXESUF) check-qtest-i386-y += tests/fdc-test$(EXESUF) gcov-files-i386-y = hw/block/fdc.c check-qtest-i386-y += tests/ide-test$(EXESUF) +check-qtest-i386-y += tests/ahci-test$(EXESUF) check-qtest-i386-y += tests/hd-geo-test$(EXESUF) gcov-files-i386-y += hw/block/hd-geometry.c check-qtest-i386-y += tests/boot-order-test$(EXESUF) @@ -301,6 +302,7 @@ tests/endianness-test$(EXESUF): tests/endianness-test.o tests/spapr-phb-test$(EXESUF): tests/spapr-phb-test.o $(libqos-obj-y) tests/fdc-test$(EXESUF): tests/fdc-test.o tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y) +tests/ahci-test$(EXESUF): tests/ahci-test.o $(libqos-pc-obj-y) tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y) tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o $(libqos-obj-y) diff --git a/tests/ahci-test.c b/tests/ahci-test.c new file mode 100644 index 000..cc49dba --- /dev/null +++ b/tests/ahci-test.c @@ -0,0 +1,196 @@ +/* + * AHCI test cases + * + * Copyright (c) 2014 John Snow + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include +#include +#include + +#include + +#include "libqtest.h" +#include "libqos/pci-pc.h" +#include "libqos/malloc-pc.h" + +#include "qemu-common.h" +#include "qemu/host-utils.h" + +#include "hw/pci/pci_ids.h" +#include "hw/pci/pci_regs.h" + +/* Test-specific defines. */ +#define TEST_IMAGE_SIZE(64 * 1024 * 1024) + +/*** Supplementary PCI Config Space IDs & Masks ***/ +#define PCI_DEVICE_ID_INTEL_Q35_AHCI (0x2922) + +/*** Globals ***/ +static QGuestAllocator *guest_malloc; +static QPCIBus *pcibus; +static char tmp_path[] = "/tmp/qtest.XX"; + +/*** Function Declarations ***/ +static QPCIDevice *get_ahci_device(void); +static void free_ahci_device(QPCIDevice *dev); + +/*** Utilities ***/ + +/** + * Locate, verify, and return a handle to the AHCI device. + */ +static QPCIDevice *get_ahci_device(void) +{ +QPCIDevice *ahci; +uint16_t vendor_id, device_id; + +pcibus = qpci_init_pc(); + +/* Find the AHCI PCI device and verify it's the right one. */ +ahci = qpci_device_find(pcibus, QPCI_DEVFN(0x1F, 0x02)); +g_assert(ahci != NULL); + +vendor_id = qpci_config_readw(ahci, PCI_VENDOR_ID); +device_id = qpci_config_readw(ahci, PCI_DEVICE_ID); + +g_assert_cmphex(vendor_id, ==, PCI_VENDOR_ID_INTEL); +g_assert_cmphex(device_id, ==, PCI_DEVICE_ID_INTEL_Q35_AHCI); + +return ahci; +} + +static void free_ahci_device(QPCIDevice *ahci) +{ +/* libqos doesn't have a function for this, so free it manually */ +g_free(ahci); + +if (pcibus) { +qpci_free_pc(pcibus); +pcibus = NULL; +} +} + +/*** Test Setup & Teardown ***/ + +/** + * Launch QEMU with the given command line, + * and then set up interrupts and our guest malloc interface. + */ +static void qtest_boot(const char *cmdline_fmt, ...) +{ +va_list ap; +char *cmdline; + +va_start(ap, cmdline_fmt); +cmdline = g_strdup_vprintf(cmdline_fmt, ap); +va_end(ap); + +qtest_start(cmdline); +qtest_irq_intercept_in(global_qtest, "ioapic&q
[Qemu-devel] [PATCH v4 6/8] ahci: Add test_hba_spec to ahci-test.
Add a test routine that checks the boot-up values of the HBA configuration memory space against the AHCI 1.3 specification and Intel ICH9 data sheet (for Q35 machines) for adherence and sane values. The HBA is not yet engaged or put into the idle state. Signed-off-by: John Snow --- tests/ahci-test.c | 561 +- 1 file changed, 560 insertions(+), 1 deletion(-) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index ee41b41..11ef350 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -53,21 +53,242 @@ #define AHCI_INTEL_ICH9 (PCI_DEVICE_ID_INTEL_Q35_AHCI << 16 | \ PCI_VENDOR_ID_INTEL) +/*** AHCI/HBA Register Offsets and Bitmasks ***/ +#define AHCI_CAP (0) +#define AHCI_CAP_NP(0x1F) +#define AHCI_CAP_SXS (0x20) +#define AHCI_CAP_EMS (0x40) +#define AHCI_CAP_CCCS (0x80) +#define AHCI_CAP_NCS (0x1F00) +#define AHCI_CAP_PSC (0x2000) +#define AHCI_CAP_SSC (0x4000) +#define AHCI_CAP_PMD (0x8000) +#define AHCI_CAP_FBSS (0x1) +#define AHCI_CAP_SPM(0x2) +#define AHCI_CAP_SAM(0x4) +#define AHCI_CAP_RESERVED (0x8) +#define AHCI_CAP_ISS (0xF0) +#define AHCI_CAP_SCLO (0x100) +#define AHCI_CAP_SAL (0x200) +#define AHCI_CAP_SALP (0x400) +#define AHCI_CAP_SSS (0x800) +#define AHCI_CAP_SMPS(0x1000) +#define AHCI_CAP_SSNTF (0x2000) +#define AHCI_CAP_SNCQ(0x4000) +#define AHCI_CAP_S64A(0x8000) + +#define AHCI_GHC (1) +#define AHCI_GHC_HR(0x01) +#define AHCI_GHC_IE(0x02) +#define AHCI_GHC_MRSM (0x04) +#define AHCI_GHC_RESERVED(0x7FF8) +#define AHCI_GHC_AE (0x8000) + +#define AHCI_IS (2) +#define AHCI_PI (3) +#define AHCI_VS (4) + +#define AHCI_TL (5) +#define AHCI_TL_EN (0x01) +#define AHCI_TL_RESERVED (0x06) +#define AHCI_TL_CC (0xFF00) +#define AHCI_TL_TV (0x) + +#define AHCI_CCCPORTS (6) +#define AHCI_EMLOC(7) + +#define AHCI_EMCTL(8) +#define AHCI_EMCTL_STSMR (0x01) +#define AHCI_EMCTL_CTLTM (0x100) +#define AHCI_EMCTL_CTLRST (0x200) +#define AHCI_EMCTL_RESERVED (0xF0F0FCFE) + +#define AHCI_CAP2 (9) +#define AHCI_CAP2_BOH (0x01) +#define AHCI_CAP2_NVMP (0x02) +#define AHCI_CAP2_APST (0x04) +#define AHCI_CAP2_RESERVED (0xFFF8) + +#define AHCI_BOHC(10) +#define AHCI_RESERVED(11) +#define AHCI_NVMHCI (24) +#define AHCI_VENDOR (40) +#define AHCI_PORTS (64) + +/*** Port Memory Offsets & Bitmasks ***/ +#define AHCI_PX_CLB (0) +#define AHCI_PX_CLB_RESERVED (0x1FF) + +#define AHCI_PX_CLBU (1) + +#define AHCI_PX_FB(2) +#define AHCI_PX_FB_RESERVED(0xFF) + +#define AHCI_PX_FBU (3) + +#define AHCI_PX_IS(4) +#define AHCI_PX_IS_DHRS (0x1) +#define AHCI_PX_IS_PSS (0x2) +#define AHCI_PX_IS_DSS (0x4) +#define AHCI_PX_IS_SDBS (0x8) +#define AHCI_PX_IS_UFS (0x10) +#define AHCI_PX_IS_DPS (0x20) +#define AHCI_PX_IS_PCS (0x40) +#define AHCI_PX_IS_DMPS(0x80) +#define AHCI_PX_IS_RESERVED (0x23FFF00) +#define AHCI_PX_IS_PRCS(0x40) +#define AHCI_PX_IS_IPMS(0x80) +#define AHCI_PX_IS_OFS(0x100) +#define AHCI_PX_IS_INFS (0x400) +#define AHCI_PX_IS_IFS(0x800) +#define AHCI_PX_IS_HBDS (0x1000) +#define AHCI_PX_IS_HBFS (0x2000) +#define AHCI_PX_IS_TFES (0x4000) +#define AHCI_PX_IS_CPDS (0x8000) + +#define AHCI_PX_IE(5) +#define AHCI_PX_IE_DHRE (0x1) +#define AHCI_PX_IE_PSE (0x2) +#define AHCI_PX_IE_DSE (0x4) +#define AHCI_PX_IE_SDBE (0x8) +#define AHCI_PX_IE_UFE (0x10) +#define AHCI_PX_IE_DPE (0x20) +#define AHCI_PX_IE_PCE (0x40) +#define AHCI_PX_IE_DMPE(0x80) +#define AHCI_PX_IE_RESERVED (0x23FFF00) +#define AHCI_PX_IE_PRCE(0x40
[Qemu-devel] [PATCH v4 0/8] AHCI test suite framework
This submission does not re-send earlier patches in the series which have already been merged into QEMU, which were primarily staging fixes and small tweaks to support this smaller set of patches. This patch series introduces a number of small fixes and tweaks to help support an AHCI test suite that in the future I hope to expand to a fuller regression suite to help guide the development of the AHCI device support under, in particular, the Q35 machine type in QEMU. Paolo Bonzini has contributed a number of cleanup and refactoring patches that support changes to the PIO setup FIS packet construction code, which is necessary for testing ths specification adherence of the IDENTIFY command, which issues its data exclusively via PIO mechanisms. The ahci-test code being checked in represents a minimum of functionality needed in order to issue and receive commands from the AHCI HBA under the libqos / qtest environment. In V3+, there is one assertion for incorrect behavior that is expected to be fixed in a future patch set, where the Descriptor Processed interrupt is not set or posted within the identify test. V4: - Endianness fixes, primarily in ahci_test_identify, tested on ppc64 - Style issues (define and structure names) - Reshuffled barsize changes into later, more relevant patches - Some additional comments for clarification. V3: - All tests complete successfully. - Fixed the PCI capabilities ordering for AHCI. - Corrected the boot-time values of the PxTFD and PxSIG registers - Corrected PxTFD register being updated prior to PxCMD.FRE being set V2: "ide-test: add test for werror=stop" - changed filename variable name - altered the QMP event polling to avoid sleep - debug_path file cleanup on exit. "ide: stop PIO transfer on errors" - Modified logic to be unconditional. "ahci: construct PIO Setup FIS for PIO commands" - Added in dma_memory_map success checks and error pathways. "libqtest: Correct small memory leak." - Corrected raw usage of free() "ahci: Adding basic functionality qtest." - Removed HBA type. - Corrected cleanup order. - Corrected raw usage of free() - Removed needless conditional around g_free() - Altered ahci_boot to return by value instead of parameter. "ahci: Add test_pci_spec to ahci-test." - Removed all ifdef logic in favor of runtime tuning. (--pedantic) - Removed all warnings, all infractions are now hard assertions. - Replaced g_assert with g_assert_cmphex in many cases "ahci: add test_pci_enable to ahci-test." - Removed MSI codepaths, as it was incomplete and unused. "ahci: Add test_hba_spec to ahci-test." - Removed unneeded macros - Added in an optional bar_size return parameter to qpci_iomap "ahci: Add test_identify case to ahci-test." - Corrected raw usage of free() For convenience; https://github.com/jnsnow/qemu/tree/ahci-test-v4 John Snow (8): ahci: Adding basic functionality qtest. ahci: MSI capability should be at 0x80, not 0x50. ahci: Add test_pci_spec to ahci-test. ahci: add test_pci_enable to ahci-test. ahci: properly shadow the TFD register ahci: Add test_hba_spec to ahci-test. ahci: Add test_hba_enable to ahci-test. ahci: Add test_identify case to ahci-test. hw/ide/ahci.c | 42 +- hw/ide/ich.c |7 +- tests/Makefile |2 + tests/ahci-test.c | 1561 tests/libqos/pci.c |6 + 5 files changed, 1603 insertions(+), 15 deletions(-) create mode 100644 tests/ahci-test.c -- 1.9.3
[Qemu-devel] [PATCH v4 8/8] ahci: Add test_identify case to ahci-test.
Utilizing all of the bring-up code in pci_enable and hba_enable, this test issues a simple IDENTIFY command via the HBA and retrieves the response via the PIO receive mechanisms of the HBA. Bugs: The DPS interrupt (Descriptor Processed Status) does not currently get set. This will need to be adjusted in a future patch series when the AHCI DMA pathways are reworked to allow the feature, which may be utilized by OSX guests. Signed-off-by: John Snow --- tests/ahci-test.c | 304 ++ 1 file changed, 304 insertions(+) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index a1e7c8f..7cc49f0 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -252,6 +252,97 @@ #define AHCI_VERSION_1_2 (0x00010200) #define AHCI_VERSION_1_3 (0x00010300) +/*** Structures ***/ + +/** + * Generic FIS structure. + */ +typedef struct FIS { +uint8_t fis_type; +uint8_t flags; +char data[0]; +} __attribute__((__packed__)) FIS; + +/** + * Register device-to-host FIS structure. + */ +typedef struct RegD2HFIS { +/* DW0 */ +uint8_t fis_type; +uint8_t flags; +uint8_t status; +uint8_t error; +/* DW1 */ +uint8_t lba_low; +uint8_t lba_mid; +uint8_t lba_high; +uint8_t device; +/* DW2 */ +uint8_t lba3; +uint8_t lba4; +uint8_t lba5; +uint8_t res1; +/* DW3 */ +uint16_t count; +uint8_t res2; +uint8_t res3; +/* DW4 */ +uint16_t res4; +uint16_t res5; +} __attribute__((__packed__)) RegD2HFIS; + +/** + * Register host-to-device FIS structure. + */ +typedef struct RegH2DFIS { +/* DW0 */ +uint8_t fis_type; +uint8_t flags; +uint8_t command; +uint8_t feature_low; +/* DW1 */ +uint8_t lba_low; +uint8_t lba_mid; +uint8_t lba_high; +uint8_t device; +/* DW2 */ +uint8_t lba3; +uint8_t lba4; +uint8_t lba5; +uint8_t feature_high; +/* DW3 */ +uint16_t count; +uint8_t icc; +uint8_t control; +/* DW4 */ +uint32_t aux; +} __attribute__((__packed__)) RegH2DFIS; + +/** + * Command List entry structure. + * The command list contains between 1-32 of these structures. + */ +typedef struct AHCICommand { +uint8_t b1; +uint8_t b2; +uint16_t prdtl; /* Phys Region Desc. Table Length */ +uint32_t prdbc; /* Phys Region Desc. Byte Count */ +uint32_t ctba; /* Command Table Descriptor Base Address */ +uint32_t ctbau; /*'' Upper */ +uint32_t res[4]; +} __attribute__((__packed__)) AHCICommand; + +/** + * Physical Region Descriptor; pointed to by the Command List Header, + * struct ahci_command. + */ +typedef struct PRD { +uint32_t dba; /* Data Base Address */ +uint32_t dbau; /* Data Base Address Upper */ +uint32_t res; /* Reserved */ +uint32_t dbc; /* Data Byte Count (0-indexed) & Interrupt Flag (bit 2^31) */ +} PRD; + typedef struct HBACap { uint32_t cap; uint32_t cap2; @@ -289,6 +380,10 @@ static uint32_t ahci_fingerprint; #define PX_CLR(port, reg, mask) PX_WREG((port), (reg),\ PX_RREG((port), (reg)) & ~(mask)); +/* For calculating how big the PRD table needs to be: */ +#define CMD_TBL_SIZ(n) ((0x80 + ((n) * sizeof(PRD)) + 0x7F) & ~0x7F) + + /*** Function Declarations ***/ static QPCIDevice *get_ahci_device(void); static QPCIDevice *start_ahci_device(QPCIDevice *dev, void **hba_base); @@ -304,6 +399,17 @@ static void ahci_test_pmcap(QPCIDevice *ahci, uint8_t offset); /*** Utilities ***/ +static void string_bswap16(uint16_t *s, size_t bytes) +{ +g_assert_cmphex((bytes & 1), ==, 0); +bytes /= 2; + +while (bytes--) { +*s = bswap16(*s); +s++; +} +} + /** * Locate, verify, and return a handle to the AHCI device. */ @@ -418,6 +524,7 @@ static void ahci_pci_enable(QPCIDevice *ahci, void **hba_base) reg = qpci_config_readb(ahci, 0x92); reg |= 0x3F; qpci_config_writeb(ahci, 0x92, reg); +/* 0...011b -- bit significant, ports 0-5 enabled. */ ASSERT_BIT_SET(qpci_config_readb(ahci, 0x92), 0x3F); break; } @@ -1124,6 +1231,186 @@ static void ahci_test_port_spec(QPCIDevice *ahci, void *hba_base, } } +/** + * Utilizing an initialized AHCI HBA, issue an IDENTIFY command to the first + * device we see, then read and check the response. + */ +static void ahci_test_identify(QPCIDevice *ahci, void *hba_base) +{ +RegD2HFIS *d2h = g_malloc0(0x20); +RegD2HFIS *pio = g_malloc0(0x20); +RegH2DFIS fis; +AHCICommand cmd; +PRD prd; +uint32_t ports, reg, clb, table, fb, data_ptr; +uint16_t buff[256]; +unsigned i; +int rc; + +g_assert(ahci != NULL); +g_assert(hba_base != NULL); + +/* We need to: + * (1) Create a Command Table Buffer and update the Command List Slot #0 + * to point to this buffer. +
[Qemu-devel] [PATCH v4 2/8] ahci: MSI capability should be at 0x80, not 0x50.
In the Intel ICH9 data sheet, the MSI capability offset in the PCI configuration space for ICH9 AHCI devices is specified to be 0x80. Further, the PCI capability pointer should always point to 0x80 in ICH9 devices, despite the fact that AHCI 1.3 specifies that it should be pointing to PMCAP (Which in this instance would be 0x70) to maintain adherence to the Intel data sheet specifications and real observed behavior. Signed-off-by: John Snow --- hw/ide/ich.c | 7 ++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/ide/ich.c b/hw/ide/ich.c index a2f1639..8eb77a1 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -71,6 +71,7 @@ #include #include +#define ICH9_MSI_CAP_OFFSET 0x80 #define ICH9_SATA_CAP_OFFSET0xA8 #define ICH9_IDP_BAR4 @@ -115,7 +116,6 @@ static int pci_ich9_ahci_init(PCIDevice *dev) /* XXX Software should program this register */ dev->config[0x90] = 1 << 6; /* Address Map Register - AHCI mode */ -msi_init(dev, 0x50, 1, true, false); d->ahci.irq = pci_allocate_irq(dev); pci_register_bar(dev, ICH9_IDP_BAR, PCI_BASE_ADDRESS_SPACE_IO, @@ -135,6 +135,11 @@ static int pci_ich9_ahci_init(PCIDevice *dev) (ICH9_IDP_BAR + 0x4) | (ICH9_IDP_INDEX_LOG2 << 4)); d->ahci.idp_offset = ICH9_IDP_INDEX; +/* Although the AHCI 1.3 specification states that the first capability + * should be PMCAP, the Intel ICH9 data sheet specifies that the ICH9 + * AHCI device puts the MSI capability first, pointing to 0x80. */ +msi_init(dev, ICH9_MSI_CAP_OFFSET, 1, true, false); + return 0; } -- 1.9.3
[Qemu-devel] [PATCH v4 4/8] ahci: add test_pci_enable to ahci-test.
This adds a test wherein we engage the PCI AHCI device and ensure that the memory region for the HBA functionality is now accessible. Under Q35 environments, additional PCI configuration is performed to ensure that the HBA functionality will become usable. Signed-off-by: John Snow --- tests/ahci-test.c | 53 + tests/libqos/pci.c | 6 ++ 2 files changed, 59 insertions(+) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 0674d5e..ee41b41 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -66,6 +66,7 @@ static uint32_t ahci_fingerprint; /*** Function Declarations ***/ static QPCIDevice *get_ahci_device(void); +static QPCIDevice *start_ahci_device(QPCIDevice *dev, void **hba_base); static void free_ahci_device(QPCIDevice *dev); static void ahci_test_pci_spec(QPCIDevice *ahci); static void ahci_test_pci_caps(QPCIDevice *ahci, uint16_t header, @@ -169,6 +170,44 @@ static void ahci_shutdown(QPCIDevice *ahci) qtest_shutdown(); } +/*** Logical Device Initialization ***/ + +/** + * Start the PCI device and sanity-check default operation. + */ +static void ahci_pci_enable(QPCIDevice *ahci, void **hba_base) +{ +uint8_t reg; + +start_ahci_device(ahci, hba_base); + +switch (ahci_fingerprint) { +case AHCI_INTEL_ICH9: +/* ICH9 has a register at PCI 0x92 that + * acts as a master port enabler mask. */ +reg = qpci_config_readb(ahci, 0x92); +reg |= 0x3F; +qpci_config_writeb(ahci, 0x92, reg); +ASSERT_BIT_SET(qpci_config_readb(ahci, 0x92), 0x3F); +break; +} + +} + +/** + * Map BAR5/ABAR, and engage the PCI device. + */ +static QPCIDevice *start_ahci_device(QPCIDevice *ahci, void **hba_base) +{ +/* Map AHCI's ABAR (BAR5) */ +*hba_base = qpci_iomap(ahci, 5, NULL); + +/* turns on pci.cmd.iose, pci.cmd.mse and pci.cmd.bme */ +qpci_device_enable(ahci); + +return ahci; +} + /*** Specification Adherence Tests ***/ /** @@ -428,6 +467,19 @@ static void test_pci_spec(void) ahci_shutdown(ahci); } +/** + * Engage the PCI AHCI device and sanity check the response. + * Perform additional PCI config space bringup for the HBA. + */ +static void test_pci_enable(void) +{ +QPCIDevice *ahci; +void *hba_base; +ahci = ahci_boot(); +ahci_pci_enable(ahci, &hba_base); +ahci_shutdown(ahci); +} + /**/ int main(int argc, char **argv) @@ -479,6 +531,7 @@ int main(int argc, char **argv) /* Run the tests */ qtest_add_func("/ahci/sanity", test_sanity); qtest_add_func("/ahci/pci_spec", test_pci_spec); +qtest_add_func("/ahci/pci_enable", test_pci_enable); ret = g_test_run(); diff --git a/tests/libqos/pci.c b/tests/libqos/pci.c index ce0b308..b244689 100644 --- a/tests/libqos/pci.c +++ b/tests/libqos/pci.c @@ -73,6 +73,12 @@ void qpci_device_enable(QPCIDevice *dev) cmd = qpci_config_readw(dev, PCI_COMMAND); cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; qpci_config_writew(dev, PCI_COMMAND, cmd); + +/* Verify the bits are now set. */ +cmd = qpci_config_readw(dev, PCI_COMMAND); +g_assert_cmphex(cmd & PCI_COMMAND_IO, ==, PCI_COMMAND_IO); +g_assert_cmphex(cmd & PCI_COMMAND_MEMORY, ==, PCI_COMMAND_MEMORY); +g_assert_cmphex(cmd & PCI_COMMAND_MASTER, ==, PCI_COMMAND_MASTER); } uint8_t qpci_config_readb(QPCIDevice *dev, uint8_t offset) -- 1.9.3
[Qemu-devel] [PATCH v4 7/8] ahci: Add test_hba_enable to ahci-test.
This test engages the HBA functionality and initializes values to sane defaults to allow for minimal HBA functionality. Buffers are allocated and pointers are updated to allow minimal I/O commands to complete as expected. Error registers and responses are sanity checked for specification adherence. Signed-off-by: John Snow --- tests/ahci-test.c | 156 ++ 1 file changed, 156 insertions(+) diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 11ef350..a1e7c8f 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -277,11 +277,17 @@ static uint32_t ahci_fingerprint; #define AHCI_WRITE(OFST, VAL) qpci_io_writel(ahci, hba_base + (OFST), (VAL)) #define AHCI_RREG(regno) AHCI_READ(4 * (regno)) #define AHCI_WREG(regno, val) AHCI_WRITE(4 * (regno), (val)) +#define AHCI_SET(regno, mask) AHCI_WREG((regno), AHCI_RREG(regno) | (mask)) +#define AHCI_CLR(regno, mask) AHCI_WREG((regno), AHCI_RREG(regno) & ~(mask)) /*** IO macros for port-specific offsets inside of AHCI memory. ***/ #define PX_OFST(port, regno) (HBA_PORT_NUM_REG * (port) + AHCI_PORTS + (regno)) #define PX_RREG(port, regno) AHCI_RREG(PX_OFST((port), (regno))) #define PX_WREG(port, regno, val) AHCI_WREG(PX_OFST((port), (regno)), (val)) +#define PX_SET(port, reg, mask) PX_WREG((port), (reg),\ + PX_RREG((port), (reg)) | (mask)); +#define PX_CLR(port, reg, mask) PX_WREG((port), (reg),\ + PX_RREG((port), (reg)) & ~(mask)); /*** Function Declarations ***/ static QPCIDevice *get_ahci_device(void); @@ -432,6 +438,140 @@ static QPCIDevice *start_ahci_device(QPCIDevice *ahci, void **hba_base) return ahci; } +/** + * Test and initialize the AHCI's HBA memory areas. + * Initialize and start any ports with devices attached. + * Bring the HBA into the idle state. + */ +static void ahci_hba_enable(QPCIDevice *ahci, void *hba_base) +{ +/* Bits of interest in this section: + * GHC.AE Global Host Control / AHCI Enable + * PxCMD.ST Port Command: Start + * PxCMD.SUD "Spin Up Device" + * PxCMD.POD "Power On Device" + * PxCMD.FRE "FIS Receive Enable" + * PxCMD.FR "FIS Receive Running" + * PxCMD.CR "Command List Running" + */ + +g_assert(ahci != NULL); +g_assert(hba_base != NULL); + +uint32_t reg, ports_impl, clb, fb; +uint16_t i; +uint8_t num_cmd_slots; + +g_assert(hba_base != 0); + +/* Set GHC.AE to 1 */ +AHCI_SET(AHCI_GHC, AHCI_GHC_AE); +reg = AHCI_RREG(AHCI_GHC); +ASSERT_BIT_SET(reg, AHCI_GHC_AE); + +/* Read CAP.NCS, how many command slots do we have? */ +reg = AHCI_RREG(AHCI_CAP); +num_cmd_slots = ((reg & AHCI_CAP_NCS) >> ctzl(AHCI_CAP_NCS)) + 1; +g_test_message("Number of Command Slots: %u", num_cmd_slots); + +/* Determine which ports are implemented. */ +ports_impl = AHCI_RREG(AHCI_PI); + +for (i = 0; ports_impl; ports_impl >>= 1, ++i) { +if (!(ports_impl & 0x01)) { +continue; +} + +g_test_message("Initializing port %u", i); + +reg = PX_RREG(i, AHCI_PX_CMD); +if (BITCLR(reg, AHCI_PX_CMD_ST | AHCI_PX_CMD_CR | + AHCI_PX_CMD_FRE | AHCI_PX_CMD_FR)) { +g_test_message("port is idle"); +} else { +g_test_message("port needs to be idled"); +PX_CLR(i, AHCI_PX_CMD, (AHCI_PX_CMD_ST | AHCI_PX_CMD_FRE)); +/* The port has 500ms to disengage. */ +usleep(50); +reg = PX_RREG(i, AHCI_PX_CMD); +ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CR); +ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_FR); +g_test_message("port is now idle"); +/* The spec does allow for possibly needing a PORT RESET + * or HBA reset if we fail to idle the port. */ +} + +/* Allocate Memory for the Command List Buffer & FIS Buffer */ +/* PxCLB space ... 0x20 per command, as in 4.2.2 p 36 */ +clb = guest_alloc(guest_malloc, num_cmd_slots * 0x20); +g_test_message("CLB: 0x%08x", clb); +PX_WREG(i, AHCI_PX_CLB, clb); +g_assert_cmphex(clb, ==, PX_RREG(i, AHCI_PX_CLB)); + +/* PxFB space ... 0x100, as in 4.2.1 p 35 */ +fb = guest_alloc(guest_malloc, 0x100); +g_test_message("FB: 0x%08x", fb); +PX_WREG(i, AHCI_PX_FB, fb); +g_assert_cmphex(fb, ==, PX_RREG(i, AHCI_PX_FB)); + +/* Clear PxSERR, PxIS, then IS.IPS[x] by writing '1's. */ +PX_WREG(i, AHCI_PX_SERR, 0x); +PX_WREG(i, AHCI_PX_IS, 0x); +AHCI_WREG(AHCI_IS, (1 << i)); + +/* Verify Interrupts Cleared */ +reg = PX_RREG(i, AHCI_PX_SERR); +
[Qemu-devel] issue: linking 64bit glib when building for cpu=i386
I was running a series of tests on 32 and 64 bit hosts to test for endianness and variable width issues when I noticed that I couldn't properly perform a build of "make check" against a 32bit target from a 64bit host: ../../configure --cpu=i386 && make -j4 && make check This produces some warnings in tests-cutils about overflowing variables that are of type guint64. It's been mentioned on the mailing lists before, actually: http://lists.gnu.org/archive/html/qemu-devel/2014-05/msg00452.html The problem is that guint64 is being aliased against "unsigned long", which is only 4 bytes instead of the implied 8. This occurs because we link against the 64bit headers for glib instead of the 32bit ones when we're building for i386 from an x86_64 host. Our include flags wind up looking like: -I/usr/include/glib-2.0 but -I/usr/lib64/glib-2.0/include I was discussing the problem with Stefan: On 08/21/2014 05:03 AM, Stefan Hajnoczi wrote: The problem is that pkg-config uses libdir=/usr/lib64 by default on amd64 hosts. It doesn't know that gcc -m32 is being used. This results in glib's 64-bit headers being used where guint64 is just unsigned long. On 32-bit hosts this is incorrect. Two workarounds: 1. yum install pkgconfig.i686 and run it instead of pkgconfig.x86_64 2. Use the pkg-config --define-variable libdir=/usr/lib option You can set PKG_CONFIG=path/to/pkg-config.i686 on QEMU's ./configure command-line. This is all distro-specific :(. Any other solutions? Stefan I am not extremely well versed in configure or pkg-config ninjutsu, but I must imagine that the ARCH/cpu variables we are setting in configure could help us know to call the 32bit pkg-config instead of the native 64bit version and fix this issue. Does anyone have any good ideas? Surely other projects must have run into this elsewhere. -- —js
Re: [Qemu-devel] [PATCH 1/3] qemu-img: fix img_commit() error return value
On 08/26/2014 03:31 PM, Eric Blake wrote: On 08/26/2014 12:17 PM, Stefan Hajnoczi wrote: The img_commit() return value is a process exit code. Use 1 for failure instead of -1. The other failure paths in this function already use 1. Signed-off-by: Stefan Hajnoczi --- qemu-img.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qemu-img.c b/qemu-img.c index c843420..dc3adb5 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -771,7 +771,7 @@ static int img_commit(int argc, char **argv) ret = bdrv_parse_cache_flags(cache, &flags); if (ret < 0) { error_report("Invalid cache option: %s", cache); -return -1; +return 1; Nothing against this patch (you're consistent with the surrounding code, and most of qemu for that matter), but it highlights why I'm a fan of 'return EXIT_FAILURE' instead of 'return 1' in functions that return an exit status, because that makes it a lot more obvious _why_ I'm returning a non-negative number to represent failure. "Hey, good point." jsnow@local ~/s/qemu> git grep 'return 1;' | wc -l 842 "oh." This patch is probably fine -- is there some larger scheme we want to cook up within the style guide for advocating things like return code and error reporting standards? It seems to me like the preferred style for errors and returns has changed several times and there's not a good concrete rule (written) at the moment. It might be worth touching the CODING_GUIDE and/or HACKING files with recommendations, if we can agree on some consistent set of rules, like getting rid of error_set(...) and not using positive, un-named integers to represent errors. -- —js
Re: [Qemu-devel] [PATCH 0/3] qemu-img: error path fixes
On 08/26/2014 02:17 PM, Stefan Hajnoczi wrote: I noticed a few minor issues when looking at cache flag parsing. This series cleans them up. Stefan Hajnoczi (3): qemu-img: fix img_commit() error return value qemu-img: fix img_compare() flags error path qemu-img: always goto out in img_snapshot() error paths qemu-img.c | 23 ++- 1 file changed, 10 insertions(+), 13 deletions(-) Reviewed-by: John Snow -- —js
Re: [Qemu-devel] [PATCH 0/6] AHCI Device Fixes
Ping! At KVM Forum I had a discussion with (someone, sorry!) that having some pointers to which specifications to look at here might be helpful, since some of the fixes were just spec-adherence fixes. See below, in-line, for some additional notes on how to review these patches. On 10/02/2014 12:55 AM, John Snow wrote: Based off of feedback from the RFC of the same name, this series batches together a group of fixes that improve the AHCI device to fix a number of bugs. A number of fixes included in the RFC that provide more radical changes are omitted for now in favor of a smaller, more easily reviewable set for QEMU 2.2. In summary: Patch #1 and #6 correct the format of FIS packet responses that are available to the guest operating system upon interrupt. Patch #2 corrects an oversight where we do not inform the guest how many bytes we've transferred. This is relied upon for non-NCQ modes and in some early bootup and shutdown code. Patch #5 corrects cases with malformed scatter-gather lists that may cause leaks, or cause QEMU to hang in an allocation loop. Patch #4 attempts to continue minimizing the divergence of the multiple pathways through the AHCI device by re-using existing callbacks. Taken together, these patches should allow non-ncq operation for Windows hosts, as well as enable hibernation for Windows 7. Hibernation for Windows 8 and AHCI remains non-functional. John Snow (6): ahci: Correct PIO/D2H FIS responses == 1/6 == The PIO and D2H FIS responses are straightforward fixes and are based off of the SATA specification, using 3.2 as a reference. "sata 3.2" is a good google query. Section 10.5.11 covers the PIO FIS structure, and Section 10.5.6 covers the Register Device to Host FIS. This specification describes the fields of these structures and which ATA registers should be copied into them. The primary things here are: (1) The reserved bytes that we now respect, and (2) That these registers are the /post/ operation values and not the /pre/ operation values. Some commands, e.g. READ_NATIVE_MAX_ADDRESS return their information exclusively via the D2H FIS (See ATA8-ACS revision 6a) so it is improper to simply copy forward the user's values into the response. They should reflect the current state of the device. ahci: Update byte count after DMA completion == 2/6 == Byte count after DMA completion is covered under AHCI 1.3, which is freely available: http://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/serial-ata-ahci-spec-rev1_3.pdf The field is first mentioned in section 4.2.2 (Command List Structure) on page 37 as field "PRDBC." The rules on when this field is updated are described within section 5.4.1 on page 64. Notably, it is mandatory for non-NCQ commands but optional for NCQ ones. Our current AHCI implementation does not use the hw/ide/core callbacks for non-NCQ transfer modes: we define an ncq_cb instead, so the changes in this patch only change non-NCQ operation. This field *definitely* confuses windows in various ways if it is not set, including non-ncq operation and windows 7 hibernate/S4 operation. ide: repair PIO transfers for cases where nsector > 1 == 3/6 == The specification deficit here is that PIO transfers, while not actually PIO under AHCI, must still work! The commands are defined under ATA8-ACS revision 6a ("ata8 acs 6a" is a good google search term ...) and the relevant details are: Section 7.35 "READ SECTOR(S)" command 0x20 (PIO Data-In) This is the LBA28 command used by legacy devices to obtain (usually) a single sector at a time. Notably, it takes a count field, though, which can be 0x01 (one sector) up to 0xFF (255 sectors) or 0x00 (256 sectors.) "This 28-bit command is *mandatory* for all devices implementing the General and PACKET feature sets." (i.e., hard drives and cdroms.) Section 7.36 "READ SECTOR(S) EXT" command 0x24 (PIO Data-In) This is the LBA48 version of the command above. It also defines a count field that can range from 0x0001 to 0x -> 65536 sectors. This command is mandatory for any devices that implement the LBA48 feature set. This patch corrects our ignorance of the "count" field for PIO transfers, before which we'd only transfer the first sector N times instead of N sectors. I have not observed this command be used in this way "in the wild" but it was trivial to fix and made writing a test grid in qtests for AHCI easier. ahci: unify sglist preparation == 4/6 == This is more mechanical and less spec-based, but I am trying to reduce the number of pathways in which we fiddle with the scatter-gather list. ide: Correct handling of malformed/short PRDTs == 5/6 == If an ATA command asks for too many bytes, it may cause problems in QEMU. In short, the scatter-gather list length must be equal-to-or-greater-than the
Re: [Qemu-devel] Patch checking bot
On 10/20/2014 10:08 AM, Peter Maydell wrote: On 20 October 2014 11:25, Stefan Hajnoczi wrote: Hi, At KVM Forum 2014 we discussed a patch checking bot that automates patch format checking and smoke testing: 1. Did the patch submitter include Signed-off-by? 2. Does checkpatch.pl pass? 3. Does the patch apply to qemu.git/master? 4. Does each patch compile? 5. Does the series pass make check and qemu-iotests? Here are some thoughts on the patch checker: If a patch series passes successfully, no email is sent. If a patch series fails, an email with the errors is sent as a reply to the patch series email thread. The patch submitter can then respond in case there are false positive (e.g. from checkpatch.pl) - the bot doesn't care about replies but it tells the human reviewers and maintainers what the patch submitter intends to do. Probably also worth having a feature where the cover letter or patch can have a "patchchecker: no" line in it to tell the bot to ignore something, so people can avoid it sending lots of mail for patch series they know don't apply to mainline (eg ones which depend on a previous series). -- PMM Maybe it should still check what it can, but squelch the reply to list. Certain automatic checks may still be of value, even if it doesn't apply to master. If we have a website where we can check the bot and patch status, having some output might be nicer than allowing arbitrary skips. Further, maybe the bot could be trained to check the patch series target to see what branch it's supposed to apply to and go from there. If people are good about labeling their stable patches, the bot should be able to check those as well. This might help us tighten up and formalize our subject formatting rules, which would probably also help maintainer work-flow by allowing more robust automation. e.g., "Sorry, you submitted a patch, but I couldn't identify which branch/component you're trying to patch against!" I believe at KVM Forum it was also mentioned that it'd be nice to have the bot reply to patch series where the proper maintainers were missed with a "Hey, I added in who maintains touched>, please include it next time!" -- —js
Re: [Qemu-devel] [PATCH] libqos: Convert malloc-pc allocator to a generic allocator
MLIST_ENTNAME); -return &s->alloc; +return s; } inline QGuestAllocator *pc_alloc_init(void) { -return pc_alloc_init_flags(PC_ALLOC_NO_FLAGS); +return pc_alloc_init_flags(ALLOC_NO_FLAGS); } diff --git a/tests/libqos/malloc-pc.h b/tests/libqos/malloc-pc.h index 9f525e3..86ab9f0 100644 --- a/tests/libqos/malloc-pc.h +++ b/tests/libqos/malloc-pc.h @@ -15,15 +15,8 @@ #include "libqos/malloc.h" -typedef enum { -PC_ALLOC_NO_FLAGS= 0x00, -PC_ALLOC_LEAK_WARN = 0x01, -PC_ALLOC_LEAK_ASSERT = 0x02, -PC_ALLOC_PARANOID= 0x04 -} PCAllocOpts; - QGuestAllocator *pc_alloc_init(void); -QGuestAllocator *pc_alloc_init_flags(PCAllocOpts flags); -void pc_alloc_uninit(QGuestAllocator *allocator); +QGuestAllocator *pc_alloc_init_flags(QAllocOpts flags); +void pc_alloc_uninit(QGuestAllocator *allocator); #endif diff --git a/tests/libqos/malloc.c b/tests/libqos/malloc.c new file mode 100644 index 000..e145fd9 --- /dev/null +++ b/tests/libqos/malloc.c @@ -0,0 +1,268 @@ +/* + * libqos malloc support + * + * Copyright (c) 2014 + * + * Author: + * John Snow + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "libqos/malloc.h" +#include "qemu-common.h" +#include +#include +#include + +static void mlist_delete(MemList *list, MemBlock *node) +{ +g_assert(list && node); +QTAILQ_REMOVE(list, node, MLIST_ENTNAME); +g_free(node); +} + +static MemBlock *mlist_find_key(MemList *head, uint64_t addr) +{ +MemBlock *node; +QTAILQ_FOREACH(node, head, MLIST_ENTNAME) { +if (node->addr == addr) { +return node; +} +} +return NULL; +} + +static MemBlock *mlist_find_space(MemList *head, uint64_t size) +{ +MemBlock *node; + +QTAILQ_FOREACH(node, head, MLIST_ENTNAME) { +if (node->size >= size) { +return node; +} +} +return NULL; +} + +static MemBlock *mlist_sort_insert(MemList *head, MemBlock *insr) +{ +MemBlock *node; +g_assert(head && insr); + +QTAILQ_FOREACH(node, head, MLIST_ENTNAME) { +if (insr->addr < node->addr) { +QTAILQ_INSERT_BEFORE(node, insr, MLIST_ENTNAME); +return insr; +} +} + +QTAILQ_INSERT_TAIL(head, insr, MLIST_ENTNAME); +return insr; +} + +static inline uint64_t mlist_boundary(MemBlock *node) +{ +return node->size + node->addr; +} + +static MemBlock *mlist_join(MemList *head, MemBlock *left, MemBlock *right) +{ +g_assert(head && left && right); + +left->size += right->size; +mlist_delete(head, right); +return left; +} + +static void mlist_coalesce(MemList *head, MemBlock *node) +{ +g_assert(node); +MemBlock *left; +MemBlock *right; +char merge; + +do { +merge = 0; +left = QTAILQ_PREV(node, MemList, MLIST_ENTNAME); +right = QTAILQ_NEXT(node, MLIST_ENTNAME); + +/* clowns to the left of me */ +if (left && mlist_boundary(left) == node->addr) { +node = mlist_join(head, left, node); +merge = 1; +} + +/* jokers to the right */ +if (right && mlist_boundary(node) == right->addr) { +node = mlist_join(head, node, right); +merge = 1; +} + +} while (merge); +} + +static uint64_t mlist_fulfill(QGuestAllocator *s, MemBlock *freenode, +uint64_t size) +{ +uint64_t addr; +MemBlock *usednode; + +g_assert(freenode); +g_assert_cmpint(freenode->size, >=, size); + +addr = freenode->addr; +if (freenode->size == size) { +/* re-use this freenode as our used node */ +QTAILQ_REMOVE(&s->free, freenode, MLIST_ENTNAME); +usednode = freenode; +} else { +/* adjust the free node and create a new used node */ +freenode->addr += size; +freenode->size -= size; +usednode = mlist_new(addr, size); +} + +mlist_sort_insert(&s->used, usednode); +return addr; +} + +/* To assert the correctness of the list. + * Used only if ALLOC_PARANOID is set. */ +static void mlist_check(QGuestAllocator *s) +{ +MemBlock *node; +uint64_t addr = s->start > 0 ? s->start - 1 : 0; +uint64_t next = s->start; + +QTAILQ_FOREACH(node, &s->free, MLIST_ENTNAME) { +g_assert_cmpint(node->addr, >, addr); +g_assert_cmpint(node->addr, >=, next); +addr = node->addr; +next = node->addr + node->size; +} + +addr = s->start > 0 ? s->start - 1 : 0; +next = s->start; +QTAILQ_FOREACH(node, &s->used, MLIST_ENTNAME) { +g_assert_cmpint(node->addr, >, addr); +g_assert_cmpint(node->add