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();