From: Sam Eiderman <shmuel.eiderman@oracle.com>
Add QTest tests to check the logical geometry override option.
The tests in hd-geo-test are out of date - they only test IDE and do not
test interesting MBRs.
I added a few helper functions which will make adding more tests easier.
QTest's fw_cfg helper functions support only legacy fw_cfg, so I had to
read the new fw_cfg layout on my own.
Creating qcow2 disks with specific size and MBR layout is currently
unused - we only use a default empty MBR.
Reviewed-by: Karl Heubaum <karl.heubaum@oracle.com>
Reviewed-by: Arbel Moshe <arbel.moshe@oracle.com>
Signed-off-by: Sam Eiderman <shmuel.eiderman@oracle.com>
---
tests/Makefile.include | 2 +-
tests/hd-geo-test.c | 589 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 590 insertions(+), 1 deletion(-)
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 479664f899..a5b92fea6a 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -780,7 +780,7 @@ tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y)
tests/ahci-test$(EXESUF): tests/ahci-test.o $(libqos-pc-obj-y) qemu-img$(EXESUF)
tests/ipmi-kcs-test$(EXESUF): tests/ipmi-kcs-test.o
tests/ipmi-bt-test$(EXESUF): tests/ipmi-bt-test.o
-tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o
+tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o $(libqos-obj-y)
tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y)
tests/boot-serial-test$(EXESUF): tests/boot-serial-test.o $(libqos-obj-y)
tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o \
diff --git a/tests/hd-geo-test.c b/tests/hd-geo-test.c
index 62eb624726..458de99c31 100644
--- a/tests/hd-geo-test.c
+++ b/tests/hd-geo-test.c
@@ -17,7 +17,12 @@
#include "qemu/osdep.h"
#include "qemu-common.h"
+#include "qemu/bswap.h"
+#include "qapi/qmp/qlist.h"
#include "libqtest.h"
+#include "libqos/fw_cfg.h"
+#include "libqos/libqos.h"
+#include "standard-headers/linux/qemu_fw_cfg.h"
#define ARGV_SIZE 256
@@ -388,6 +393,575 @@ static void test_ide_drive_cd_0(void)
qtest_quit(qts);
}
+typedef struct {
+ bool active;
+ uint32_t head;
+ uint32_t sector;
+ uint32_t cyl;
+ uint32_t end_head;
+ uint32_t end_sector;
+ uint32_t end_cyl;
+ uint32_t start_sect;
+ uint32_t nr_sects;
+} MBRpartitions[4];
+
+static MBRpartitions empty_mbr = { {false, 0, 0, 0, 0, 0, 0, 0, 0},
+ {false, 0, 0, 0, 0, 0, 0, 0, 0},
+ {false, 0, 0, 0, 0, 0, 0, 0, 0},
+ {false, 0, 0, 0, 0, 0, 0, 0, 0} };
+
+static char *create_qcow2_with_mbr(MBRpartitions mbr, uint64_t sectors)
+{
+ const char *template = "/tmp/qtest.XXXXXX";
+ char *raw_path = strdup(template);
+ char *qcow2_path = strdup(template);
+ char cmd[100 + 2 * PATH_MAX];
+ uint8_t buf[512];
+ int i, ret, fd, offset;
+ uint64_t qcow2_size = sectors * 512;
+ uint8_t status, parttype, head, sector, cyl;
+ char *qemu_img_path;
+ char *qemu_img_abs_path;
+
+ offset = 0xbe;
+
+ for (i = 0; i < 4; i++) {
+ status = mbr[i].active ? 0x80 : 0x00;
+ g_assert(mbr[i].head < 256);
+ g_assert(mbr[i].sector < 64);
+ g_assert(mbr[i].cyl < 1024);
+ head = mbr[i].head;
+ sector = mbr[i].sector + ((mbr[i].cyl & 0x300) >> 2);
+ cyl = mbr[i].cyl & 0xff;
+
+ buf[offset + 0x0] = status;
+ buf[offset + 0x1] = head;
+ buf[offset + 0x2] = sector;
+ buf[offset + 0x3] = cyl;
+
+ parttype = 0;
+ g_assert(mbr[i].end_head < 256);
+ g_assert(mbr[i].end_sector < 64);
+ g_assert(mbr[i].end_cyl < 1024);
+ head = mbr[i].end_head;
+ sector = mbr[i].end_sector + ((mbr[i].end_cyl & 0x300) >> 2);
+ cyl = mbr[i].end_cyl & 0xff;
+
+ buf[offset + 0x4] = parttype;
+ buf[offset + 0x5] = head;
+ buf[offset + 0x6] = sector;
+ buf[offset + 0x7] = cyl;
+
+ (*(uint32_t *)&buf[offset + 0x8]) = cpu_to_le32(mbr[i].start_sect);
+ (*(uint32_t *)&buf[offset + 0xc]) = cpu_to_le32(mbr[i].nr_sects);
+
+ offset += 0x10;
+ }
+
+ fd = mkstemp(raw_path);
+ g_assert(fd);
+ close(fd);
+
+ fd = open(raw_path, O_WRONLY);
+ g_assert(fd >= 0);
+ ret = write(fd, buf, sizeof(buf));
+ g_assert(ret == sizeof(buf));
+ close(fd);
+
+ fd = mkstemp(qcow2_path);
+ g_assert(fd);
+ close(fd);
+
+ qemu_img_path = getenv("QTEST_QEMU_IMG");
+ g_assert(qemu_img_path);
+ qemu_img_abs_path = realpath(qemu_img_path, NULL);
+ g_assert(qemu_img_abs_path);
+
+ ret = snprintf(cmd, sizeof(cmd),
+ "%s convert -f raw -O qcow2 %s %s > /dev/null",
+ qemu_img_abs_path,
+ raw_path, qcow2_path);
+ g_assert((0 < ret) && (ret <= sizeof(cmd)));
+ ret = system(cmd);
+ g_assert(ret == 0);
+
+ ret = snprintf(cmd, sizeof(cmd),
+ "%s resize %s %" PRIu64 " > /dev/null",
+ qemu_img_abs_path,
+ qcow2_path, qcow2_size);
+ g_assert((0 < ret) && (ret <= sizeof(cmd)));
+ ret = system(cmd);
+ g_assert(ret == 0);
+
+ free(qemu_img_abs_path);
+
+ unlink(raw_path);
+ free(raw_path);
+
+ return qcow2_path;
+}
+
+struct QemuCfgFile {
+ uint32_t size; /* file size */
+ uint16_t select; /* write this to 0x510 to read it */
+ uint16_t reserved;
+ char name[56];
+};
+
+static uint16_t find_fw_cfg_file(QFWCFG *fw_cfg,
+ const char *filename)
+{
+ struct QemuCfgFile qfile;
+ uint32_t count, e;
+ uint16_t select;
+
+ count = qfw_cfg_get_u32(fw_cfg, FW_CFG_FILE_DIR);
+ count = be32_to_cpu(count);
+ for (select = 0, e = 0; e < count; e++) {
+ qfw_cfg_read_data(fw_cfg, &qfile, sizeof(qfile));
+ if (!strcmp(filename, qfile.name)) {
+ select = be16_to_cpu(qfile.select);
+ }
+ }
+
+ return select;
+}
+
+static void read_fw_cfg_file(QFWCFG *fw_cfg,
+ const char *filename,
+ void *data,
+ size_t len)
+{
+ uint16_t select = find_fw_cfg_file(fw_cfg, filename);
+
+ g_assert(select);
+
+ qfw_cfg_get(fw_cfg, select, data, len);
+}
+
+#define BIOS_GEOMETRY_MAX_SIZE 10000
+
+typedef struct {
+ uint32_t c;
+ uint32_t h;
+ uint32_t s;
+} CHS;
+
+typedef struct {
+ const char *dev_path;
+ CHS chs;
+} CHSResult;
+
+static void read_bootdevices(QFWCFG *fw_cfg, CHSResult expected[])
+{
+ char *buf = g_malloc0(BIOS_GEOMETRY_MAX_SIZE);
+ char *cur;
+ GList *results = NULL, *cur_result;
+ CHSResult *r;
+ int i;
+ int res;
+ bool found;
+
+ read_fw_cfg_file(fw_cfg, "bios-geometry", buf, BIOS_GEOMETRY_MAX_SIZE);
+
+ for (cur = buf; *cur; cur++) {
+ if (*cur == '\n') {
+ *cur = '\0';
+ }
+ }
+ cur = buf;
+
+ while (strlen(cur)) {
+
+ r = g_malloc0(sizeof(*r));
+ r->dev_path = g_malloc0(strlen(cur) + 1);
+ res = sscanf(cur, "%s %" PRIu32 " %" PRIu32 " %" PRIu32,
+ (char *)r->dev_path,
+ &(r->chs.c), &(r->chs.h), &(r->chs.s));
+
+ g_assert(res == 4);
+
+ results = g_list_prepend(results, r);
+
+ cur += strlen(cur) + 1;
+ }
+
+ i = 0;
+
+ while (expected[i].dev_path) {
+ found = false;
+ cur_result = results;
+ while (cur_result) {
+ r = cur_result->data;
+ if (!strcmp(r->dev_path, expected[i].dev_path) &&
+ !memcmp(&(r->chs), &(expected[i].chs), sizeof(r->chs))) {
+ found = true;
+ break;
+ }
+ cur_result = g_list_next(cur_result);
+ }
+ g_assert(found);
+ g_free((char *)((CHSResult *)cur_result->data)->dev_path);
+ g_free(cur_result->data);
+ results = g_list_delete_link(results, cur_result);
+ i++;
+ }
+
+ g_assert(results == NULL);
+
+ g_free(buf);
+}
+
+#define MAX_DRIVES 30
+
+typedef struct {
+ char **argv;
+ int argc;
+ char **drives;
+ int n_drives;
+ int n_scsi_disks;
+ int n_scsi_controllers;
+ int n_virtio_disks;
+} TestArgs;
+
+static TestArgs *create_args(void)
+{
+ TestArgs *args = g_malloc0(sizeof(*args));
+ args->argv = g_new0(char *, ARGV_SIZE);
+ args->argc = append_arg(args->argc, args->argv,
+ ARGV_SIZE, g_strdup("-nodefaults"));
+ args->drives = g_new0(char *, MAX_DRIVES);
+ return args;
+}
+
+static void add_drive_with_mbr(TestArgs *args,
+ MBRpartitions mbr, uint64_t sectors)
+{
+ char *img_file_name;
+ char part[300];
+ int ret;
+
+ g_assert(args->n_drives < MAX_DRIVES);
+
+ img_file_name = create_qcow2_with_mbr(mbr, sectors);
+
+ args->drives[args->n_drives] = img_file_name;
+ ret = snprintf(part, sizeof(part),
+ "-drive file=%s,if=none,format=qcow2,id=disk%d",
+ img_file_name, args->n_drives);
+ g_assert((0 < ret) && (ret <= sizeof(part)));
+ args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part));
+ args->n_drives++;
+}
+
+static void add_ide_disk(TestArgs *args,
+ int drive_idx, int bus, int unit, int c, int h, int s)
+{
+ char part[300];
+ int ret;
+
+ ret = snprintf(part, sizeof(part),
+ "-device ide-hd,drive=disk%d,bus=ide.%d,unit=%d,"
+ "lcyls=%d,lheads=%d,lsecs=%d",
+ drive_idx, bus, unit, c, h, s);
+ g_assert((0 < ret) && (ret <= sizeof(part)));
+ args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part));
+}
+
+static void add_scsi_controller(TestArgs *args,
+ const char *type,
+ const char *bus,
+ int addr)
+{
+ char part[300];
+ int ret;
+
+ ret = snprintf(part, sizeof(part),
+ "-device %s,id=scsi%d,bus=%s,addr=%d",
+ type, args->n_scsi_controllers, bus, addr);
+ g_assert((0 < ret) && (ret <= sizeof(part)));
+ args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part));
+ args->n_scsi_controllers++;
+}
+
+static void add_scsi_disk(TestArgs *args,
+ int drive_idx, int bus,
+ int channel, int scsi_id, int lun,
+ int c, int h, int s)
+{
+ char part[300];
+ int ret;
+
+ ret = snprintf(part, sizeof(part),
+ "-device scsi-hd,id=scsi-disk%d,drive=disk%d,"
+ "bus=scsi%d.0,"
+ "channel=%d,scsi-id=%d,lun=%d,"
+ "lcyls=%d,lheads=%d,lsecs=%d",
+ args->n_scsi_disks, drive_idx, bus, channel, scsi_id, lun,
+ c, h, s);
+ g_assert((0 < ret) && (ret <= sizeof(part)));
+ args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part));
+ args->n_scsi_disks++;
+}
+
+static void add_virtio_disk(TestArgs *args,
+ int drive_idx, const char *bus, int addr,
+ int c, int h, int s)
+{
+ char part[300];
+ int ret;
+
+ ret = snprintf(part, sizeof(part),
+ "-device virtio-blk-pci,id=virtio-disk%d,"
+ "drive=disk%d,bus=%s,addr=%d,"
+ "lcyls=%d,lheads=%d,lsecs=%d",
+ args->n_virtio_disks, drive_idx, bus, addr, c, h, s);
+ g_assert((0 < ret) && (ret <= sizeof(part)));
+ args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part));
+ args->n_virtio_disks++;
+}
+
+static void test_override(TestArgs *args, CHSResult expected[])
+{
+ QTestState *qts;
+ char *joined_args;
+ QFWCFG *fw_cfg;
+ int i;
+
+ joined_args = g_strjoinv(" ", args->argv);
+
+ qts = qtest_init(joined_args);
+ fw_cfg = pc_fw_cfg_init(qts);
+
+ read_bootdevices(fw_cfg, expected);
+
+ g_free(joined_args);
+ qtest_quit(qts);
+
+ g_free(fw_cfg);
+
+ for (i = 0; i < args->n_drives; i++) {
+ unlink(args->drives[i]);
+ free(args->drives[i]);
+ }
+ g_free(args->drives);
+ g_strfreev(args->argv);
+ g_free(args);
+}
+
+static void test_override_ide(void)
+{
+ TestArgs *args = create_args();
+ CHSResult expected[] = {
+ {"/pci@i0cf8/ide@1,1/drive@0/disk@0", {10000, 120, 30} },
+ {"/pci@i0cf8/ide@1,1/drive@0/disk@1", {9000, 120, 30} },
+ {"/pci@i0cf8/ide@1,1/drive@1/disk@0", {0, 1, 1} },
+ {"/pci@i0cf8/ide@1,1/drive@1/disk@1", {1, 0, 0} },
+ {NULL, {0, 0, 0} }
+ };
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_ide_disk(args, 0, 0, 0, 10000, 120, 30);
+ add_ide_disk(args, 1, 0, 1, 9000, 120, 30);
+ add_ide_disk(args, 2, 1, 0, 0, 1, 1);
+ add_ide_disk(args, 3, 1, 1, 1, 0, 0);
+ test_override(args, expected);
+}
+
+static void test_override_scsi(void)
+{
+ TestArgs *args = create_args();
+ CHSResult expected[] = {
+ {"/pci@i0cf8/scsi@3/channel@0/disk@0,0", {10000, 120, 30} },
+ {"/pci@i0cf8/scsi@3/channel@0/disk@1,0", {9000, 120, 30} },
+ {"/pci@i0cf8/scsi@3/channel@0/disk@2,0", {1, 0, 0} },
+ {"/pci@i0cf8/scsi@3/channel@0/disk@3,0", {0, 1, 0} },
+ {NULL, {0, 0, 0} }
+ };
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_scsi_controller(args, "lsi53c895a", "pci.0", 3);
+ add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30);
+ add_scsi_disk(args, 1, 0, 0, 1, 0, 9000, 120, 30);
+ add_scsi_disk(args, 2, 0, 0, 2, 0, 1, 0, 0);
+ add_scsi_disk(args, 3, 0, 0, 3, 0, 0, 1, 0);
+ test_override(args, expected);
+}
+
+static void test_override_scsi_2_controllers(void)
+{
+ TestArgs *args = create_args();
+ CHSResult expected[] = {
+ {"/pci@i0cf8/scsi@3/channel@0/disk@0,0", {10000, 120, 30} },
+ {"/pci@i0cf8/scsi@3/channel@0/disk@1,0", {9000, 120, 30} },
+ {"/pci@i0cf8/scsi@4/channel@0/disk@0,1", {1, 0, 0} },
+ {"/pci@i0cf8/scsi@4/channel@0/disk@1,2", {0, 1, 0} },
+ {NULL, {0, 0, 0} }
+ };
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_scsi_controller(args, "lsi53c895a", "pci.0", 3);
+ add_scsi_controller(args, "virtio-scsi-pci", "pci.0", 4);
+ add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30);
+ add_scsi_disk(args, 1, 0, 0, 1, 0, 9000, 120, 30);
+ add_scsi_disk(args, 2, 1, 0, 0, 1, 1, 0, 0);
+ add_scsi_disk(args, 3, 1, 0, 1, 2, 0, 1, 0);
+ test_override(args, expected);
+}
+
+static void test_override_virtio_blk(void)
+{
+ TestArgs *args = create_args();
+ CHSResult expected[] = {
+ {"/pci@i0cf8/scsi@3/disk@0,0", {10000, 120, 30} },
+ {"/pci@i0cf8/scsi@4/disk@0,0", {9000, 120, 30} },
+ {NULL, {0, 0, 0} }
+ };
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_virtio_disk(args, 0, "pci.0", 3, 10000, 120, 30);
+ add_virtio_disk(args, 1, "pci.0", 4, 9000, 120, 30);
+ test_override(args, expected);
+}
+
+static void test_override_zero_chs(void)
+{
+ TestArgs *args = create_args();
+ CHSResult expected[] = {
+ {NULL, {0, 0, 0} }
+ };
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_ide_disk(args, 0, 1, 1, 0, 0, 0);
+ test_override(args, expected);
+}
+
+static void test_override_scsi_hot_unplug(void)
+{
+ QTestState *qts;
+ char *joined_args;
+ QFWCFG *fw_cfg;
+ QDict *response;
+ int i;
+ TestArgs *args = create_args();
+ CHSResult expected[] = {
+ {"/pci@i0cf8/scsi@2/channel@0/disk@0,0", {10000, 120, 30} },
+ {"/pci@i0cf8/scsi@2/channel@0/disk@1,0", {20, 20, 20} },
+ {NULL, {0, 0, 0} }
+ };
+ CHSResult expected2[] = {
+ {"/pci@i0cf8/scsi@2/channel@0/disk@1,0", {20, 20, 20} },
+ {NULL, {0, 0, 0} }
+ };
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_scsi_controller(args, "virtio-scsi-pci", "pci.0", 2);
+ add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30);
+ add_scsi_disk(args, 1, 0, 0, 1, 0, 20, 20, 20);
+
+ joined_args = g_strjoinv(" ", args->argv);
+
+ qts = qtest_init(joined_args);
+ fw_cfg = pc_fw_cfg_init(qts);
+
+ read_bootdevices(fw_cfg, expected);
+
+ /* unplug device an restart */
+ response = qtest_qmp(qts,
+ "{ 'execute': 'device_del',"
+ " 'arguments': {'id': 'scsi-disk0' }}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ qobject_unref(response);
+ response = qtest_qmp(qts,
+ "{ 'execute': 'system_reset', 'arguments': { }}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ qobject_unref(response);
+
+ qtest_qmp_eventwait(qts, "RESET");
+
+ read_bootdevices(fw_cfg, expected2);
+
+ g_free(joined_args);
+ qtest_quit(qts);
+
+ g_free(fw_cfg);
+
+ for (i = 0; i < args->n_drives; i++) {
+ unlink(args->drives[i]);
+ free(args->drives[i]);
+ }
+ g_free(args->drives);
+ g_strfreev(args->argv);
+ g_free(args);
+}
+
+static void test_override_virtio_hot_unplug(void)
+{
+ QTestState *qts;
+ char *joined_args;
+ QFWCFG *fw_cfg;
+ QDict *response;
+ int i;
+ TestArgs *args = create_args();
+ CHSResult expected[] = {
+ {"/pci@i0cf8/scsi@2/disk@0,0", {10000, 120, 30} },
+ {"/pci@i0cf8/scsi@3/disk@0,0", {20, 20, 20} },
+ {NULL, {0, 0, 0} }
+ };
+ CHSResult expected2[] = {
+ {"/pci@i0cf8/scsi@3/disk@0,0", {20, 20, 20} },
+ {NULL, {0, 0, 0} }
+ };
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_drive_with_mbr(args, empty_mbr, 1);
+ add_virtio_disk(args, 0, "pci.0", 2, 10000, 120, 30);
+ add_virtio_disk(args, 1, "pci.0", 3, 20, 20, 20);
+
+ joined_args = g_strjoinv(" ", args->argv);
+
+ qts = qtest_init(joined_args);
+ fw_cfg = pc_fw_cfg_init(qts);
+
+ read_bootdevices(fw_cfg, expected);
+
+ /* unplug device an restart */
+ response = qtest_qmp(qts,
+ "{ 'execute': 'device_del',"
+ " 'arguments': {'id': 'virtio-disk0' }}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ qobject_unref(response);
+ response = qtest_qmp(qts,
+ "{ 'execute': 'system_reset', 'arguments': { }}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ qobject_unref(response);
+
+ qtest_qmp_eventwait(qts, "RESET");
+
+ read_bootdevices(fw_cfg, expected2);
+
+ g_free(joined_args);
+ qtest_quit(qts);
+
+ g_free(fw_cfg);
+
+ for (i = 0; i < args->n_drives; i++) {
+ unlink(args->drives[i]);
+ free(args->drives[i]);
+ }
+ g_free(args->drives);
+ g_strfreev(args->argv);
+ g_free(args);
+}
+
int main(int argc, char **argv)
{
Backend i;
@@ -413,6 +987,21 @@ int main(int argc, char **argv)
qtest_add_func("hd-geo/ide/device/mbr/chs", test_ide_device_mbr_chs);
qtest_add_func("hd-geo/ide/device/user/chs", test_ide_device_user_chs);
qtest_add_func("hd-geo/ide/device/user/chst", test_ide_device_user_chst);
+ if (have_qemu_img()) {
+ qtest_add_func("hd-geo/override/ide", test_override_ide);
+ qtest_add_func("hd-geo/override/scsi", test_override_scsi);
+ qtest_add_func("hd-geo/override/scsi_2_controllers",
+ test_override_scsi_2_controllers);
+ qtest_add_func("hd-geo/override/virtio_blk", test_override_virtio_blk);
+ qtest_add_func("hd-geo/override/zero_chs", test_override_zero_chs);
+ qtest_add_func("hd-geo/override/scsi_hot_unplug",
+ test_override_scsi_hot_unplug);
+ qtest_add_func("hd-geo/override/virtio_hot_unplug",
+ test_override_virtio_hot_unplug);
+ } else {
+ g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; "
+ "skipping hd-geo/override/* tests");
+ }
ret = g_test_run();
--
2.23.0.351.gc4317032e6-goog
On 9/25/19 1:06 PM, Sam Eiderman wrote:
> From: Sam Eiderman <shmuel.eiderman@oracle.com>
>
> Add QTest tests to check the logical geometry override option.
>
> The tests in hd-geo-test are out of date - they only test IDE and do not
> test interesting MBRs.
>
> I added a few helper functions which will make adding more tests easier.
>
> QTest's fw_cfg helper functions support only legacy fw_cfg, so I had to
> read the new fw_cfg layout on my own.
>
> Creating qcow2 disks with specific size and MBR layout is currently
> unused - we only use a default empty MBR.
>
> Reviewed-by: Karl Heubaum <karl.heubaum@oracle.com>
> Reviewed-by: Arbel Moshe <arbel.moshe@oracle.com>
> Signed-off-by: Sam Eiderman <shmuel.eiderman@oracle.com>
> ---
> tests/Makefile.include | 2 +-
> tests/hd-geo-test.c | 589 +++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 590 insertions(+), 1 deletion(-)
>
> diff --git a/tests/Makefile.include b/tests/Makefile.include
> index 479664f899..a5b92fea6a 100644
> --- a/tests/Makefile.include
> +++ b/tests/Makefile.include
> @@ -780,7 +780,7 @@ tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y)
> tests/ahci-test$(EXESUF): tests/ahci-test.o $(libqos-pc-obj-y) qemu-img$(EXESUF)
> tests/ipmi-kcs-test$(EXESUF): tests/ipmi-kcs-test.o
> tests/ipmi-bt-test$(EXESUF): tests/ipmi-bt-test.o
> -tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o
> +tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o $(libqos-obj-y)
> tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y)
> tests/boot-serial-test$(EXESUF): tests/boot-serial-test.o $(libqos-obj-y)
> tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o \
> diff --git a/tests/hd-geo-test.c b/tests/hd-geo-test.c
> index 62eb624726..458de99c31 100644
> --- a/tests/hd-geo-test.c
> +++ b/tests/hd-geo-test.c
> @@ -17,7 +17,12 @@
>
> #include "qemu/osdep.h"
> #include "qemu-common.h"
> +#include "qemu/bswap.h"
> +#include "qapi/qmp/qlist.h"
> #include "libqtest.h"
> +#include "libqos/fw_cfg.h"
> +#include "libqos/libqos.h"
> +#include "standard-headers/linux/qemu_fw_cfg.h"
>
> #define ARGV_SIZE 256
>
> @@ -388,6 +393,575 @@ static void test_ide_drive_cd_0(void)
> qtest_quit(qts);
> }
>
> +typedef struct {
> + bool active;
> + uint32_t head;
> + uint32_t sector;
> + uint32_t cyl;
> + uint32_t end_head;
> + uint32_t end_sector;
> + uint32_t end_cyl;
> + uint32_t start_sect;
> + uint32_t nr_sects;
> +} MBRpartitions[4];
> +
> +static MBRpartitions empty_mbr = { {false, 0, 0, 0, 0, 0, 0, 0, 0},
> + {false, 0, 0, 0, 0, 0, 0, 0, 0},
> + {false, 0, 0, 0, 0, 0, 0, 0, 0},
> + {false, 0, 0, 0, 0, 0, 0, 0, 0} };
> +
> +static char *create_qcow2_with_mbr(MBRpartitions mbr, uint64_t sectors)
> +{
> + const char *template = "/tmp/qtest.XXXXXX";
> + char *raw_path = strdup(template);
> + char *qcow2_path = strdup(template);
> + char cmd[100 + 2 * PATH_MAX];
> + uint8_t buf[512];
> + int i, ret, fd, offset;
> + uint64_t qcow2_size = sectors * 512;
> + uint8_t status, parttype, head, sector, cyl;
> + char *qemu_img_path;
> + char *qemu_img_abs_path;
> +
> + offset = 0xbe;
> +
> + for (i = 0; i < 4; i++) {
> + status = mbr[i].active ? 0x80 : 0x00;
> + g_assert(mbr[i].head < 256);
> + g_assert(mbr[i].sector < 64);
> + g_assert(mbr[i].cyl < 1024);
> + head = mbr[i].head;
> + sector = mbr[i].sector + ((mbr[i].cyl & 0x300) >> 2);
> + cyl = mbr[i].cyl & 0xff;
> +
> + buf[offset + 0x0] = status;
> + buf[offset + 0x1] = head;
> + buf[offset + 0x2] = sector;
> + buf[offset + 0x3] = cyl;
> +
> + parttype = 0;
> + g_assert(mbr[i].end_head < 256);
> + g_assert(mbr[i].end_sector < 64);
> + g_assert(mbr[i].end_cyl < 1024);
> + head = mbr[i].end_head;
> + sector = mbr[i].end_sector + ((mbr[i].end_cyl & 0x300) >> 2);
> + cyl = mbr[i].end_cyl & 0xff;
> +
> + buf[offset + 0x4] = parttype;
> + buf[offset + 0x5] = head;
> + buf[offset + 0x6] = sector;
> + buf[offset + 0x7] = cyl;
> +
> + (*(uint32_t *)&buf[offset + 0x8]) = cpu_to_le32(mbr[i].start_sect);
> + (*(uint32_t *)&buf[offset + 0xc]) = cpu_to_le32(mbr[i].nr_sects);
> +
> + offset += 0x10;
> + }
> +
> + fd = mkstemp(raw_path);
> + g_assert(fd);
> + close(fd);
> +
> + fd = open(raw_path, O_WRONLY);
> + g_assert(fd >= 0);
> + ret = write(fd, buf, sizeof(buf));
> + g_assert(ret == sizeof(buf));
> + close(fd);
> +
> + fd = mkstemp(qcow2_path);
> + g_assert(fd);
> + close(fd);
> +
> + qemu_img_path = getenv("QTEST_QEMU_IMG");
> + g_assert(qemu_img_path);
> + qemu_img_abs_path = realpath(qemu_img_path, NULL);
> + g_assert(qemu_img_abs_path);
> +
> + ret = snprintf(cmd, sizeof(cmd),
> + "%s convert -f raw -O qcow2 %s %s > /dev/null",
> + qemu_img_abs_path,
> + raw_path, qcow2_path);
> + g_assert((0 < ret) && (ret <= sizeof(cmd)));
> + ret = system(cmd);
> + g_assert(ret == 0);
> +
> + ret = snprintf(cmd, sizeof(cmd),
> + "%s resize %s %" PRIu64 " > /dev/null",
> + qemu_img_abs_path,
> + qcow2_path, qcow2_size);
> + g_assert((0 < ret) && (ret <= sizeof(cmd)));
> + ret = system(cmd);
> + g_assert(ret == 0);
> +
> + free(qemu_img_abs_path);
> +
> + unlink(raw_path);
> + free(raw_path);
> +
> + return qcow2_path;
> +}
> +
> +struct QemuCfgFile {
> + uint32_t size; /* file size */
> + uint16_t select; /* write this to 0x510 to read it */
> + uint16_t reserved;
> + char name[56];
> +};
> +
> +static uint16_t find_fw_cfg_file(QFWCFG *fw_cfg,
> + const char *filename)
> +{
> + struct QemuCfgFile qfile;
> + uint32_t count, e;
> + uint16_t select;
> +
> + count = qfw_cfg_get_u32(fw_cfg, FW_CFG_FILE_DIR);
> + count = be32_to_cpu(count);
> + for (select = 0, e = 0; e < count; e++) {
> + qfw_cfg_read_data(fw_cfg, &qfile, sizeof(qfile));
> + if (!strcmp(filename, qfile.name)) {
> + select = be16_to_cpu(qfile.select);
> + }
> + }
> +
> + return select;
> +}
> +
> +static void read_fw_cfg_file(QFWCFG *fw_cfg,
> + const char *filename,
> + void *data,
> + size_t len)
> +{
> + uint16_t select = find_fw_cfg_file(fw_cfg, filename);
> +
> + g_assert(select);
> +
> + qfw_cfg_get(fw_cfg, select, data, len);
> +}
> +
> +#define BIOS_GEOMETRY_MAX_SIZE 10000
> +
> +typedef struct {
> + uint32_t c;
> + uint32_t h;
> + uint32_t s;
> +} CHS;
> +
> +typedef struct {
> + const char *dev_path;
> + CHS chs;
> +} CHSResult;
> +
> +static void read_bootdevices(QFWCFG *fw_cfg, CHSResult expected[])
> +{
> + char *buf = g_malloc0(BIOS_GEOMETRY_MAX_SIZE);
> + char *cur;
> + GList *results = NULL, *cur_result;
> + CHSResult *r;
> + int i;
> + int res;
> + bool found;
> +
> + read_fw_cfg_file(fw_cfg, "bios-geometry", buf, BIOS_GEOMETRY_MAX_SIZE);
Oh I'm glad to see the test I requested while reviewing the previous
patch! I'll have a look at it, but John 589 LoC I doubt I can do it for
Friday.
> +
> + for (cur = buf; *cur; cur++) {
> + if (*cur == '\n') {
> + *cur = '\0';
> + }
> + }
> + cur = buf;
> +
> + while (strlen(cur)) {
> +
> + r = g_malloc0(sizeof(*r));
> + r->dev_path = g_malloc0(strlen(cur) + 1);
> + res = sscanf(cur, "%s %" PRIu32 " %" PRIu32 " %" PRIu32,
> + (char *)r->dev_path,
> + &(r->chs.c), &(r->chs.h), &(r->chs.s));
> +
> + g_assert(res == 4);
> +
> + results = g_list_prepend(results, r);
> +
> + cur += strlen(cur) + 1;
> + }
> +
> + i = 0;
> +
> + while (expected[i].dev_path) {
> + found = false;
> + cur_result = results;
> + while (cur_result) {
> + r = cur_result->data;
> + if (!strcmp(r->dev_path, expected[i].dev_path) &&
> + !memcmp(&(r->chs), &(expected[i].chs), sizeof(r->chs))) {
> + found = true;
> + break;
> + }
> + cur_result = g_list_next(cur_result);
> + }
> + g_assert(found);
> + g_free((char *)((CHSResult *)cur_result->data)->dev_path);
> + g_free(cur_result->data);
> + results = g_list_delete_link(results, cur_result);
> + i++;
> + }
> +
> + g_assert(results == NULL);
> +
> + g_free(buf);
> +}
> +
> +#define MAX_DRIVES 30
> +
> +typedef struct {
> + char **argv;
> + int argc;
> + char **drives;
> + int n_drives;
> + int n_scsi_disks;
> + int n_scsi_controllers;
> + int n_virtio_disks;
> +} TestArgs;
> +
> +static TestArgs *create_args(void)
> +{
> + TestArgs *args = g_malloc0(sizeof(*args));
> + args->argv = g_new0(char *, ARGV_SIZE);
> + args->argc = append_arg(args->argc, args->argv,
> + ARGV_SIZE, g_strdup("-nodefaults"));
> + args->drives = g_new0(char *, MAX_DRIVES);
> + return args;
> +}
> +
> +static void add_drive_with_mbr(TestArgs *args,
> + MBRpartitions mbr, uint64_t sectors)
> +{
> + char *img_file_name;
> + char part[300];
> + int ret;
> +
> + g_assert(args->n_drives < MAX_DRIVES);
> +
> + img_file_name = create_qcow2_with_mbr(mbr, sectors);
> +
> + args->drives[args->n_drives] = img_file_name;
> + ret = snprintf(part, sizeof(part),
> + "-drive file=%s,if=none,format=qcow2,id=disk%d",
> + img_file_name, args->n_drives);
> + g_assert((0 < ret) && (ret <= sizeof(part)));
> + args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part));
> + args->n_drives++;
> +}
> +
> +static void add_ide_disk(TestArgs *args,
> + int drive_idx, int bus, int unit, int c, int h, int s)
> +{
> + char part[300];
> + int ret;
> +
> + ret = snprintf(part, sizeof(part),
> + "-device ide-hd,drive=disk%d,bus=ide.%d,unit=%d,"
> + "lcyls=%d,lheads=%d,lsecs=%d",
> + drive_idx, bus, unit, c, h, s);
> + g_assert((0 < ret) && (ret <= sizeof(part)));
> + args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part));
> +}
> +
> +static void add_scsi_controller(TestArgs *args,
> + const char *type,
> + const char *bus,
> + int addr)
> +{
> + char part[300];
> + int ret;
> +
> + ret = snprintf(part, sizeof(part),
> + "-device %s,id=scsi%d,bus=%s,addr=%d",
> + type, args->n_scsi_controllers, bus, addr);
> + g_assert((0 < ret) && (ret <= sizeof(part)));
> + args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part));
> + args->n_scsi_controllers++;
> +}
> +
> +static void add_scsi_disk(TestArgs *args,
> + int drive_idx, int bus,
> + int channel, int scsi_id, int lun,
> + int c, int h, int s)
> +{
> + char part[300];
> + int ret;
> +
> + ret = snprintf(part, sizeof(part),
> + "-device scsi-hd,id=scsi-disk%d,drive=disk%d,"
> + "bus=scsi%d.0,"
> + "channel=%d,scsi-id=%d,lun=%d,"
> + "lcyls=%d,lheads=%d,lsecs=%d",
> + args->n_scsi_disks, drive_idx, bus, channel, scsi_id, lun,
> + c, h, s);
> + g_assert((0 < ret) && (ret <= sizeof(part)));
> + args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part));
> + args->n_scsi_disks++;
> +}
> +
> +static void add_virtio_disk(TestArgs *args,
> + int drive_idx, const char *bus, int addr,
> + int c, int h, int s)
> +{
> + char part[300];
> + int ret;
> +
> + ret = snprintf(part, sizeof(part),
> + "-device virtio-blk-pci,id=virtio-disk%d,"
> + "drive=disk%d,bus=%s,addr=%d,"
> + "lcyls=%d,lheads=%d,lsecs=%d",
> + args->n_virtio_disks, drive_idx, bus, addr, c, h, s);
> + g_assert((0 < ret) && (ret <= sizeof(part)));
> + args->argc = append_arg(args->argc, args->argv, ARGV_SIZE, g_strdup(part));
> + args->n_virtio_disks++;
> +}
> +
> +static void test_override(TestArgs *args, CHSResult expected[])
> +{
> + QTestState *qts;
> + char *joined_args;
> + QFWCFG *fw_cfg;
> + int i;
> +
> + joined_args = g_strjoinv(" ", args->argv);
> +
> + qts = qtest_init(joined_args);
> + fw_cfg = pc_fw_cfg_init(qts);
> +
> + read_bootdevices(fw_cfg, expected);
> +
> + g_free(joined_args);
> + qtest_quit(qts);
> +
> + g_free(fw_cfg);
> +
> + for (i = 0; i < args->n_drives; i++) {
> + unlink(args->drives[i]);
> + free(args->drives[i]);
> + }
> + g_free(args->drives);
> + g_strfreev(args->argv);
> + g_free(args);
> +}
> +
> +static void test_override_ide(void)
> +{
> + TestArgs *args = create_args();
> + CHSResult expected[] = {
> + {"/pci@i0cf8/ide@1,1/drive@0/disk@0", {10000, 120, 30} },
> + {"/pci@i0cf8/ide@1,1/drive@0/disk@1", {9000, 120, 30} },
> + {"/pci@i0cf8/ide@1,1/drive@1/disk@0", {0, 1, 1} },
> + {"/pci@i0cf8/ide@1,1/drive@1/disk@1", {1, 0, 0} },
> + {NULL, {0, 0, 0} }
> + };
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_ide_disk(args, 0, 0, 0, 10000, 120, 30);
> + add_ide_disk(args, 1, 0, 1, 9000, 120, 30);
> + add_ide_disk(args, 2, 1, 0, 0, 1, 1);
> + add_ide_disk(args, 3, 1, 1, 1, 0, 0);
> + test_override(args, expected);
> +}
> +
> +static void test_override_scsi(void)
> +{
> + TestArgs *args = create_args();
> + CHSResult expected[] = {
> + {"/pci@i0cf8/scsi@3/channel@0/disk@0,0", {10000, 120, 30} },
> + {"/pci@i0cf8/scsi@3/channel@0/disk@1,0", {9000, 120, 30} },
> + {"/pci@i0cf8/scsi@3/channel@0/disk@2,0", {1, 0, 0} },
> + {"/pci@i0cf8/scsi@3/channel@0/disk@3,0", {0, 1, 0} },
> + {NULL, {0, 0, 0} }
> + };
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_scsi_controller(args, "lsi53c895a", "pci.0", 3);
> + add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30);
> + add_scsi_disk(args, 1, 0, 0, 1, 0, 9000, 120, 30);
> + add_scsi_disk(args, 2, 0, 0, 2, 0, 1, 0, 0);
> + add_scsi_disk(args, 3, 0, 0, 3, 0, 0, 1, 0);
> + test_override(args, expected);
> +}
> +
> +static void test_override_scsi_2_controllers(void)
> +{
> + TestArgs *args = create_args();
> + CHSResult expected[] = {
> + {"/pci@i0cf8/scsi@3/channel@0/disk@0,0", {10000, 120, 30} },
> + {"/pci@i0cf8/scsi@3/channel@0/disk@1,0", {9000, 120, 30} },
> + {"/pci@i0cf8/scsi@4/channel@0/disk@0,1", {1, 0, 0} },
> + {"/pci@i0cf8/scsi@4/channel@0/disk@1,2", {0, 1, 0} },
> + {NULL, {0, 0, 0} }
> + };
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_scsi_controller(args, "lsi53c895a", "pci.0", 3);
> + add_scsi_controller(args, "virtio-scsi-pci", "pci.0", 4);
> + add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30);
> + add_scsi_disk(args, 1, 0, 0, 1, 0, 9000, 120, 30);
> + add_scsi_disk(args, 2, 1, 0, 0, 1, 1, 0, 0);
> + add_scsi_disk(args, 3, 1, 0, 1, 2, 0, 1, 0);
> + test_override(args, expected);
> +}
> +
> +static void test_override_virtio_blk(void)
> +{
> + TestArgs *args = create_args();
> + CHSResult expected[] = {
> + {"/pci@i0cf8/scsi@3/disk@0,0", {10000, 120, 30} },
> + {"/pci@i0cf8/scsi@4/disk@0,0", {9000, 120, 30} },
> + {NULL, {0, 0, 0} }
> + };
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_virtio_disk(args, 0, "pci.0", 3, 10000, 120, 30);
> + add_virtio_disk(args, 1, "pci.0", 4, 9000, 120, 30);
> + test_override(args, expected);
> +}
> +
> +static void test_override_zero_chs(void)
> +{
> + TestArgs *args = create_args();
> + CHSResult expected[] = {
> + {NULL, {0, 0, 0} }
> + };
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_ide_disk(args, 0, 1, 1, 0, 0, 0);
> + test_override(args, expected);
> +}
> +
> +static void test_override_scsi_hot_unplug(void)
> +{
> + QTestState *qts;
> + char *joined_args;
> + QFWCFG *fw_cfg;
> + QDict *response;
> + int i;
> + TestArgs *args = create_args();
> + CHSResult expected[] = {
> + {"/pci@i0cf8/scsi@2/channel@0/disk@0,0", {10000, 120, 30} },
> + {"/pci@i0cf8/scsi@2/channel@0/disk@1,0", {20, 20, 20} },
> + {NULL, {0, 0, 0} }
> + };
> + CHSResult expected2[] = {
> + {"/pci@i0cf8/scsi@2/channel@0/disk@1,0", {20, 20, 20} },
> + {NULL, {0, 0, 0} }
> + };
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_scsi_controller(args, "virtio-scsi-pci", "pci.0", 2);
> + add_scsi_disk(args, 0, 0, 0, 0, 0, 10000, 120, 30);
> + add_scsi_disk(args, 1, 0, 0, 1, 0, 20, 20, 20);
> +
> + joined_args = g_strjoinv(" ", args->argv);
> +
> + qts = qtest_init(joined_args);
> + fw_cfg = pc_fw_cfg_init(qts);
> +
> + read_bootdevices(fw_cfg, expected);
> +
> + /* unplug device an restart */
> + response = qtest_qmp(qts,
> + "{ 'execute': 'device_del',"
> + " 'arguments': {'id': 'scsi-disk0' }}");
> + g_assert(response);
> + g_assert(!qdict_haskey(response, "error"));
> + qobject_unref(response);
> + response = qtest_qmp(qts,
> + "{ 'execute': 'system_reset', 'arguments': { }}");
> + g_assert(response);
> + g_assert(!qdict_haskey(response, "error"));
> + qobject_unref(response);
> +
> + qtest_qmp_eventwait(qts, "RESET");
> +
> + read_bootdevices(fw_cfg, expected2);
> +
> + g_free(joined_args);
> + qtest_quit(qts);
> +
> + g_free(fw_cfg);
> +
> + for (i = 0; i < args->n_drives; i++) {
> + unlink(args->drives[i]);
> + free(args->drives[i]);
> + }
> + g_free(args->drives);
> + g_strfreev(args->argv);
> + g_free(args);
> +}
> +
> +static void test_override_virtio_hot_unplug(void)
> +{
> + QTestState *qts;
> + char *joined_args;
> + QFWCFG *fw_cfg;
> + QDict *response;
> + int i;
> + TestArgs *args = create_args();
> + CHSResult expected[] = {
> + {"/pci@i0cf8/scsi@2/disk@0,0", {10000, 120, 30} },
> + {"/pci@i0cf8/scsi@3/disk@0,0", {20, 20, 20} },
> + {NULL, {0, 0, 0} }
> + };
> + CHSResult expected2[] = {
> + {"/pci@i0cf8/scsi@3/disk@0,0", {20, 20, 20} },
> + {NULL, {0, 0, 0} }
> + };
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_drive_with_mbr(args, empty_mbr, 1);
> + add_virtio_disk(args, 0, "pci.0", 2, 10000, 120, 30);
> + add_virtio_disk(args, 1, "pci.0", 3, 20, 20, 20);
> +
> + joined_args = g_strjoinv(" ", args->argv);
> +
> + qts = qtest_init(joined_args);
> + fw_cfg = pc_fw_cfg_init(qts);
> +
> + read_bootdevices(fw_cfg, expected);
> +
> + /* unplug device an restart */
> + response = qtest_qmp(qts,
> + "{ 'execute': 'device_del',"
> + " 'arguments': {'id': 'virtio-disk0' }}");
> + g_assert(response);
> + g_assert(!qdict_haskey(response, "error"));
> + qobject_unref(response);
> + response = qtest_qmp(qts,
> + "{ 'execute': 'system_reset', 'arguments': { }}");
> + g_assert(response);
> + g_assert(!qdict_haskey(response, "error"));
> + qobject_unref(response);
> +
> + qtest_qmp_eventwait(qts, "RESET");
> +
> + read_bootdevices(fw_cfg, expected2);
> +
> + g_free(joined_args);
> + qtest_quit(qts);
> +
> + g_free(fw_cfg);
> +
> + for (i = 0; i < args->n_drives; i++) {
> + unlink(args->drives[i]);
> + free(args->drives[i]);
> + }
> + g_free(args->drives);
> + g_strfreev(args->argv);
> + g_free(args);
> +}
> +
> int main(int argc, char **argv)
> {
> Backend i;
> @@ -413,6 +987,21 @@ int main(int argc, char **argv)
> qtest_add_func("hd-geo/ide/device/mbr/chs", test_ide_device_mbr_chs);
> qtest_add_func("hd-geo/ide/device/user/chs", test_ide_device_user_chs);
> qtest_add_func("hd-geo/ide/device/user/chst", test_ide_device_user_chst);
> + if (have_qemu_img()) {
> + qtest_add_func("hd-geo/override/ide", test_override_ide);
> + qtest_add_func("hd-geo/override/scsi", test_override_scsi);
> + qtest_add_func("hd-geo/override/scsi_2_controllers",
> + test_override_scsi_2_controllers);
> + qtest_add_func("hd-geo/override/virtio_blk", test_override_virtio_blk);
> + qtest_add_func("hd-geo/override/zero_chs", test_override_zero_chs);
> + qtest_add_func("hd-geo/override/scsi_hot_unplug",
> + test_override_scsi_hot_unplug);
> + qtest_add_func("hd-geo/override/virtio_hot_unplug",
> + test_override_virtio_hot_unplug);
> + } else {
> + g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; "
> + "skipping hd-geo/override/* tests");
> + }
>
> ret = g_test_run();
>
>
_______________________________________________
SeaBIOS mailing list -- seabios@seabios.org
To unsubscribe send an email to seabios-leave@seabios.org
On 9/26/19 6:00 AM, Philippe Mathieu-Daudé wrote:
> On 9/25/19 1:06 PM, Sam Eiderman wrote:
>> From: Sam Eiderman <shmuel.eiderman@oracle.com>
>>
>> Add QTest tests to check the logical geometry override option.
>>
>> The tests in hd-geo-test are out of date - they only test IDE and do not
>> test interesting MBRs.
>>
>> I added a few helper functions which will make adding more tests easier.
>>
>> QTest's fw_cfg helper functions support only legacy fw_cfg, so I had to
>> read the new fw_cfg layout on my own.
>>
>> Creating qcow2 disks with specific size and MBR layout is currently
>> unused - we only use a default empty MBR.
>>
>> Reviewed-by: Karl Heubaum <karl.heubaum@oracle.com>
>> Reviewed-by: Arbel Moshe <arbel.moshe@oracle.com>
>> Signed-off-by: Sam Eiderman <shmuel.eiderman@oracle.com>
>> ---
>> tests/Makefile.include | 2 +-
>> tests/hd-geo-test.c | 589 +++++++++++++++++++++++++++++++++++++++++
>> 2 files changed, 590 insertions(+), 1 deletion(-)
>>
>> diff --git a/tests/Makefile.include b/tests/Makefile.include
>> index 479664f899..a5b92fea6a 100644
>> --- a/tests/Makefile.include
>> +++ b/tests/Makefile.include
>> @@ -780,7 +780,7 @@ tests/ide-test$(EXESUF): tests/ide-test.o $(libqos-pc-obj-y)
>> tests/ahci-test$(EXESUF): tests/ahci-test.o $(libqos-pc-obj-y) qemu-img$(EXESUF)
>> tests/ipmi-kcs-test$(EXESUF): tests/ipmi-kcs-test.o
>> tests/ipmi-bt-test$(EXESUF): tests/ipmi-bt-test.o
>> -tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o
>> +tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o $(libqos-obj-y)
>> tests/boot-order-test$(EXESUF): tests/boot-order-test.o $(libqos-obj-y)
>> tests/boot-serial-test$(EXESUF): tests/boot-serial-test.o $(libqos-obj-y)
>> tests/bios-tables-test$(EXESUF): tests/bios-tables-test.o \
>> diff --git a/tests/hd-geo-test.c b/tests/hd-geo-test.c
>> index 62eb624726..458de99c31 100644
>> --- a/tests/hd-geo-test.c
>> +++ b/tests/hd-geo-test.c
>> @@ -17,7 +17,12 @@
>>
>> #include "qemu/osdep.h"
>> #include "qemu-common.h"
>> +#include "qemu/bswap.h"
>> +#include "qapi/qmp/qlist.h"
>> #include "libqtest.h"
>> +#include "libqos/fw_cfg.h"
>> +#include "libqos/libqos.h"
>> +#include "standard-headers/linux/qemu_fw_cfg.h"
>>
>> #define ARGV_SIZE 256
>>
>> @@ -388,6 +393,575 @@ static void test_ide_drive_cd_0(void)
>> qtest_quit(qts);
>> }
>>
>> +typedef struct {
>> + bool active;
>> + uint32_t head;
>> + uint32_t sector;
>> + uint32_t cyl;
>> + uint32_t end_head;
>> + uint32_t end_sector;
>> + uint32_t end_cyl;
>> + uint32_t start_sect;
>> + uint32_t nr_sects;
>> +} MBRpartitions[4];
>> +
>> +static MBRpartitions empty_mbr = { {false, 0, 0, 0, 0, 0, 0, 0, 0},
>> + {false, 0, 0, 0, 0, 0, 0, 0, 0},
>> + {false, 0, 0, 0, 0, 0, 0, 0, 0},
>> + {false, 0, 0, 0, 0, 0, 0, 0, 0} };
>> +
>> +static char *create_qcow2_with_mbr(MBRpartitions mbr, uint64_t sectors)
>> +{
>> + const char *template = "/tmp/qtest.XXXXXX";
>> + char *raw_path = strdup(template);
>> + char *qcow2_path = strdup(template);
>> + char cmd[100 + 2 * PATH_MAX];
>> + uint8_t buf[512];
>> + int i, ret, fd, offset;
>> + uint64_t qcow2_size = sectors * 512;
>> + uint8_t status, parttype, head, sector, cyl;
>> + char *qemu_img_path;
>> + char *qemu_img_abs_path;
>> +
>> + offset = 0xbe;
>> +
>> + for (i = 0; i < 4; i++) {
>> + status = mbr[i].active ? 0x80 : 0x00;
>> + g_assert(mbr[i].head < 256);
>> + g_assert(mbr[i].sector < 64);
>> + g_assert(mbr[i].cyl < 1024);
>> + head = mbr[i].head;
>> + sector = mbr[i].sector + ((mbr[i].cyl & 0x300) >> 2);
>> + cyl = mbr[i].cyl & 0xff;
>> +
>> + buf[offset + 0x0] = status;
>> + buf[offset + 0x1] = head;
>> + buf[offset + 0x2] = sector;
>> + buf[offset + 0x3] = cyl;
>> +
>> + parttype = 0;
>> + g_assert(mbr[i].end_head < 256);
>> + g_assert(mbr[i].end_sector < 64);
>> + g_assert(mbr[i].end_cyl < 1024);
>> + head = mbr[i].end_head;
>> + sector = mbr[i].end_sector + ((mbr[i].end_cyl & 0x300) >> 2);
>> + cyl = mbr[i].end_cyl & 0xff;
>> +
>> + buf[offset + 0x4] = parttype;
>> + buf[offset + 0x5] = head;
>> + buf[offset + 0x6] = sector;
>> + buf[offset + 0x7] = cyl;
>> +
>> + (*(uint32_t *)&buf[offset + 0x8]) = cpu_to_le32(mbr[i].start_sect);
>> + (*(uint32_t *)&buf[offset + 0xc]) = cpu_to_le32(mbr[i].nr_sects);
>> +
>> + offset += 0x10;
>> + }
>> +
>> + fd = mkstemp(raw_path);
>> + g_assert(fd);
>> + close(fd);
>> +
>> + fd = open(raw_path, O_WRONLY);
>> + g_assert(fd >= 0);
>> + ret = write(fd, buf, sizeof(buf));
>> + g_assert(ret == sizeof(buf));
>> + close(fd);
>> +
>> + fd = mkstemp(qcow2_path);
>> + g_assert(fd);
>> + close(fd);
>> +
>> + qemu_img_path = getenv("QTEST_QEMU_IMG");
>> + g_assert(qemu_img_path);
>> + qemu_img_abs_path = realpath(qemu_img_path, NULL);
>> + g_assert(qemu_img_abs_path);
>> +
>> + ret = snprintf(cmd, sizeof(cmd),
>> + "%s convert -f raw -O qcow2 %s %s > /dev/null",
>> + qemu_img_abs_path,
>> + raw_path, qcow2_path);
>> + g_assert((0 < ret) && (ret <= sizeof(cmd)));
>> + ret = system(cmd);
>> + g_assert(ret == 0);
>> +
>> + ret = snprintf(cmd, sizeof(cmd),
>> + "%s resize %s %" PRIu64 " > /dev/null",
>> + qemu_img_abs_path,
>> + qcow2_path, qcow2_size);
>> + g_assert((0 < ret) && (ret <= sizeof(cmd)));
>> + ret = system(cmd);
>> + g_assert(ret == 0);
>> +
>> + free(qemu_img_abs_path);
>> +
>> + unlink(raw_path);
>> + free(raw_path);
>> +
>> + return qcow2_path;
>> +}
>> +
>> +struct QemuCfgFile {
>> + uint32_t size; /* file size */
>> + uint16_t select; /* write this to 0x510 to read it */
>> + uint16_t reserved;
>> + char name[56];
>> +};
>> +
>> +static uint16_t find_fw_cfg_file(QFWCFG *fw_cfg,
>> + const char *filename)
>> +{
>> + struct QemuCfgFile qfile;
>> + uint32_t count, e;
>> + uint16_t select;
>> +
>> + count = qfw_cfg_get_u32(fw_cfg, FW_CFG_FILE_DIR);
>> + count = be32_to_cpu(count);
>> + for (select = 0, e = 0; e < count; e++) {
>> + qfw_cfg_read_data(fw_cfg, &qfile, sizeof(qfile));
>> + if (!strcmp(filename, qfile.name)) {
>> + select = be16_to_cpu(qfile.select);
>> + }
>> + }
>> +
>> + return select;
>> +}
>> +
>> +static void read_fw_cfg_file(QFWCFG *fw_cfg,
>> + const char *filename,
>> + void *data,
>> + size_t len)
>> +{
>> + uint16_t select = find_fw_cfg_file(fw_cfg, filename);
>> +
>> + g_assert(select);
>> +
>> + qfw_cfg_get(fw_cfg, select, data, len);
>> +}
>> +
>> +#define BIOS_GEOMETRY_MAX_SIZE 10000
>> +
>> +typedef struct {
>> + uint32_t c;
>> + uint32_t h;
>> + uint32_t s;
>> +} CHS;
>> +
>> +typedef struct {
>> + const char *dev_path;
>> + CHS chs;
>> +} CHSResult;
>> +
>> +static void read_bootdevices(QFWCFG *fw_cfg, CHSResult expected[])
>> +{
>> + char *buf = g_malloc0(BIOS_GEOMETRY_MAX_SIZE);
>> + char *cur;
>> + GList *results = NULL, *cur_result;
>> + CHSResult *r;
>> + int i;
>> + int res;
>> + bool found;
>> +
>> + read_fw_cfg_file(fw_cfg, "bios-geometry", buf, BIOS_GEOMETRY_MAX_SIZE);
>
> Oh I'm glad to see the test I requested while reviewing the previous
> patch! I'll have a look at it, but John 589 LoC I doubt I can do it for
> Friday.
>
It's just a test, even so -- we can amend it. There's no real hurry.
--js
_______________________________________________
SeaBIOS mailing list -- seabios@seabios.org
To unsubscribe send an email to seabios-leave@seabios.org
© 2016 - 2026 Red Hat, Inc.