* [Qemu-devel] [PATCH v2 0/4] i440fx-test: check firmware visibility
@ 2013-11-28 18:09 Laszlo Ersek
2013-11-28 18:09 ` [Qemu-devel] [PATCH v2 1/4] i440fx-test: qtest_start() should be paired with qtest_end() Laszlo Ersek
` (4 more replies)
0 siblings, 5 replies; 18+ messages in thread
From: Laszlo Ersek @ 2013-11-28 18:09 UTC (permalink / raw)
To: qemu-devel
v1 blurb:
> Marcel's commit
>
> commit a53ae8e934cd54686875b5bcfc2f434244ee55d6
> Author: Marcel Apfelbaum <marcel.a@redhat.com>
> Date: Mon Sep 16 11:21:16 2013 +0300
>
> hw/pci: partially handle pci master abort
>
> has exposed a conflict (an unintended, unordered overlap) between the
> memory ranges "pci-hole" and "system.flash". When the boot firmware is
> passed with -pflash, the "pci-hole" region hides it, and the guest
> cannot execute the firmware.
>
> The test cases added by this series should help avoid regressions in
> this area.
>
> On top of v1.7.0-rc0, the "/i440fx/firmware/bios" test passes even
> without fixing the memory region conflict (consistently with the fact
> that we never noticed the conflict in practice due to using -bios
> exclusively).
>
> The "/i440fx/firmware/pflash" test catches the problem, unless one of
> the discussed fixes are applied (ie. shrinking "pci-hole", or
> explicitly ordering it under "system.flash"):
>
> $ tests/i440fx-test --verbose
> [...]
> GTest: run: /i440fx/firmware/bios
> (MSG: qemu cmdline: -S -display none -bios /tmp/fw_blob_MA3Y5W)
> GTest: result: OK
> GTest: run: /i440fx/firmware/pflash
> (MSG: qemu cmdline: -S -display none -pflash /tmp/fw_blob_ELLU5W)
> **
> ERROR:tests/i440fx-test.c:368:test_i440fx_firmware: assertion failed
> (buf[i] == (char unsigned)i): (0x000000ff == 0x00000000)
> Aborted
The test passes with v1.7.0.
Changes in v2:
- Rebased ("-display none" is the default now).
- I looked into memory management prudence in qtest, and I can see that
we don't care about leaking small objects at all. (See for example
qpci_device_foreach(), which calls qpci_device_find() in a loop, and
leaks the retval every time.)
Laszlo Ersek (4):
i440fx-test: qtest_start() should be paired with qtest_end()
i440fx-test: give each GTest case its own qtest
i440fx-test: generate temporary firmware blob
i440fx-test: verify firmware under 4G and 1M, both -bios and -pflash
tests/i440fx-test.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 152 insertions(+), 17 deletions(-)
--
1.8.3.1
^ permalink raw reply [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH v2 1/4] i440fx-test: qtest_start() should be paired with qtest_end()
2013-11-28 18:09 [Qemu-devel] [PATCH v2 0/4] i440fx-test: check firmware visibility Laszlo Ersek
@ 2013-11-28 18:09 ` Laszlo Ersek
2013-11-29 13:23 ` Markus Armbruster
2013-11-28 18:09 ` [Qemu-devel] [PATCH v2 2/4] i440fx-test: give each GTest case its own qtest Laszlo Ersek
` (3 subsequent siblings)
4 siblings, 1 reply; 18+ messages in thread
From: Laszlo Ersek @ 2013-11-28 18:09 UTC (permalink / raw)
To: qemu-devel
Similarly to commit 1d9358e6
("libqtest: New qtest_end() to go with qtest_start()").
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
---
tests/i440fx-test.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c
index 65c786c..6ac46bf 100644
--- a/tests/i440fx-test.c
+++ b/tests/i440fx-test.c
@@ -2,9 +2,11 @@
* qtest I440FX test case
*
* Copyright IBM, Corp. 2012-2013
+ * Copyright Red Hat, Inc. 2013
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
+ * Laszlo Ersek <lersek@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
@@ -256,7 +258,6 @@ static void test_i440fx_pam(gconstpointer opaque)
int main(int argc, char **argv)
{
- QTestState *s;
TestData data;
char *cmdline;
int ret;
@@ -266,20 +267,17 @@ int main(int argc, char **argv)
data.num_cpus = 1;
cmdline = g_strdup_printf("-smp %d", data.num_cpus);
- s = qtest_start(cmdline);
+ qtest_start(cmdline);
g_free(cmdline);
data.bus = qpci_init_pc();
g_test_add_data_func("/i440fx/defaults", &data, test_i440fx_defaults);
g_test_add_data_func("/i440fx/pam", &data, test_i440fx_pam);
-
ret = g_test_run();
- if (s) {
- qtest_quit(s);
- }
+ qtest_end();
return ret;
}
--
1.8.3.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH v2 2/4] i440fx-test: give each GTest case its own qtest
2013-11-28 18:09 [Qemu-devel] [PATCH v2 0/4] i440fx-test: check firmware visibility Laszlo Ersek
2013-11-28 18:09 ` [Qemu-devel] [PATCH v2 1/4] i440fx-test: qtest_start() should be paired with qtest_end() Laszlo Ersek
@ 2013-11-28 18:09 ` Laszlo Ersek
2013-11-29 14:53 ` Eduardo Habkost
2013-11-28 18:09 ` [Qemu-devel] [PATCH v2 3/4] i440fx-test: generate temporary firmware blob Laszlo Ersek
` (2 subsequent siblings)
4 siblings, 1 reply; 18+ messages in thread
From: Laszlo Ersek @ 2013-11-28 18:09 UTC (permalink / raw)
To: qemu-devel
The current two GTest cases, /i440fx/defaults and /i440fx/pam can share a
qemu process, but the next two cases will need dedicated instances. It is
messy (and order-dependent) to dynamically configure GTest cases one by
one to start, stop, or keep the current qtest (*); let's just have each
GTest work with its own qtest. The performance difference should be
negligible.
(*) As g_test_run() can be invoked at most once per process startup, and
it runs GTest cases in sequence, we'd need clumsy data structures to
control each GTest case to start/stop/keep the qemu instance. Or, we'd
have to code the same information into the test methods themselves, which
would make them even more order-dependent.
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
---
tests/i440fx-test.c | 32 +++++++++++++++++++-------------
1 file changed, 19 insertions(+), 13 deletions(-)
diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c
index 6ac46bf..3962bca 100644
--- a/tests/i440fx-test.c
+++ b/tests/i440fx-test.c
@@ -28,16 +28,27 @@
typedef struct TestData
{
int num_cpus;
- QPCIBus *bus;
} TestData;
+static QPCIBus *test_start_get_bus(const TestData *s)
+{
+ char *cmdline;
+
+ cmdline = g_strdup_printf("-smp %d", s->num_cpus);
+ qtest_start(cmdline);
+ g_free(cmdline);
+ return qpci_init_pc();
+}
+
static void test_i440fx_defaults(gconstpointer opaque)
{
const TestData *s = opaque;
+ QPCIBus *bus;
QPCIDevice *dev;
uint32_t value;
- dev = qpci_device_find(s->bus, QPCI_DEVFN(0, 0));
+ bus = test_start_get_bus(s);
+ dev = qpci_device_find(bus, QPCI_DEVFN(0, 0));
g_assert(dev != NULL);
/* 3.2.2 */
@@ -121,6 +132,8 @@ static void test_i440fx_defaults(gconstpointer opaque)
g_assert_cmpint(qpci_config_readb(dev, 0x91), ==, 0x00); /* ERRSTS */
/* 3.2.26 */
g_assert_cmpint(qpci_config_readb(dev, 0x93), ==, 0x00); /* TRC */
+
+ qtest_end();
}
#define PAM_RE 1
@@ -179,6 +192,7 @@ static void write_area(uint32_t start, uint32_t end, uint8_t value)
static void test_i440fx_pam(gconstpointer opaque)
{
const TestData *s = opaque;
+ QPCIBus *bus;
QPCIDevice *dev;
int i;
static struct {
@@ -201,7 +215,8 @@ static void test_i440fx_pam(gconstpointer opaque)
{ 0xEC000, 0xEFFFF }, /* BIOS Extension */
};
- dev = qpci_device_find(s->bus, QPCI_DEVFN(0, 0));
+ bus = test_start_get_bus(s);
+ dev = qpci_device_find(bus, QPCI_DEVFN(0, 0));
g_assert(dev != NULL);
for (i = 0; i < ARRAY_SIZE(pam_area); i++) {
@@ -254,30 +269,21 @@ static void test_i440fx_pam(gconstpointer opaque)
/* Verify the area is not our new mask */
g_assert(!verify_area(pam_area[i].start, pam_area[i].end, 0x82));
}
+ qtest_end();
}
int main(int argc, char **argv)
{
TestData data;
- char *cmdline;
int ret;
g_test_init(&argc, &argv, NULL);
data.num_cpus = 1;
- cmdline = g_strdup_printf("-smp %d", data.num_cpus);
- qtest_start(cmdline);
- g_free(cmdline);
-
- data.bus = qpci_init_pc();
-
g_test_add_data_func("/i440fx/defaults", &data, test_i440fx_defaults);
g_test_add_data_func("/i440fx/pam", &data, test_i440fx_pam);
ret = g_test_run();
-
- qtest_end();
-
return ret;
}
--
1.8.3.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH v2 3/4] i440fx-test: generate temporary firmware blob
2013-11-28 18:09 [Qemu-devel] [PATCH v2 0/4] i440fx-test: check firmware visibility Laszlo Ersek
2013-11-28 18:09 ` [Qemu-devel] [PATCH v2 1/4] i440fx-test: qtest_start() should be paired with qtest_end() Laszlo Ersek
2013-11-28 18:09 ` [Qemu-devel] [PATCH v2 2/4] i440fx-test: give each GTest case its own qtest Laszlo Ersek
@ 2013-11-28 18:09 ` Laszlo Ersek
2013-11-29 13:57 ` Markus Armbruster
2013-11-28 18:09 ` [Qemu-devel] [PATCH v2 4/4] i440fx-test: verify firmware under 4G and 1M, both -bios and -pflash Laszlo Ersek
2013-11-28 18:18 ` [Qemu-devel] [PATCH v2 0/4] i440fx-test: check firmware visibility Laszlo Ersek
4 siblings, 1 reply; 18+ messages in thread
From: Laszlo Ersek @ 2013-11-28 18:09 UTC (permalink / raw)
To: qemu-devel
The blob is 64K in size and contains 0x00..0xFF repeatedly.
The client code added to main() wouldn't make much sense in the long term.
It helps with debugging and it silences gcc about create_firmware() being
unused, and we'll replace it in the next patch anyway.
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
---
tests/i440fx-test.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c
index 3962bca..5506421 100644
--- a/tests/i440fx-test.c
+++ b/tests/i440fx-test.c
@@ -20,6 +20,11 @@
#include <glib.h>
#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/mman.h>
+#include <stdlib.h>
#define BROKEN 1
@@ -272,13 +277,70 @@ static void test_i440fx_pam(gconstpointer opaque)
qtest_end();
}
+#define FW_SIZE ((size_t)65536)
+
+/* Create a temporary firmware blob, and return its absolute pathname as a
+ * dynamically allocated string.
+ * The file is closed before the function returns; it should be unlinked after
+ * use.
+ * In case of error, NULL is returned. The function prints the error message.
+ */
+static char *create_firmware(void)
+{
+ int ret, fd;
+ char *pathname;
+ GError *error = NULL;
+
+ ret = -1;
+ fd = g_file_open_tmp("fw_blob_XXXXXX", &pathname, &error);
+ if (fd == -1) {
+ fprintf(stderr, "unable to create temporary firmware blob: %s\n",
+ error->message);
+ g_error_free(error);
+ } else {
+ if (ftruncate(fd, FW_SIZE) == -1) {
+ fprintf(stderr, "ftruncate(\"%s\", %zu): %s\n", pathname, FW_SIZE,
+ strerror(errno));
+ } else {
+ void *buf;
+
+ buf = mmap(NULL, FW_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
+ if (buf == MAP_FAILED) {
+ fprintf(stderr, "mmap(\"%s\", %zu): %s\n", pathname, FW_SIZE,
+ strerror(errno));
+ } else {
+ size_t i;
+
+ for (i = 0; i < FW_SIZE; ++i) {
+ ((char unsigned *)buf)[i] = i;
+ }
+ munmap(buf, FW_SIZE);
+ ret = 0;
+ }
+ }
+ close(fd);
+ if (ret == -1) {
+ unlink(pathname);
+ g_free(pathname);
+ }
+ }
+
+ return ret == -1 ? NULL : pathname;
+}
+
int main(int argc, char **argv)
{
+ char *fw_pathname;
TestData data;
int ret;
g_test_init(&argc, &argv, NULL);
+ fw_pathname = create_firmware();
+ g_assert(fw_pathname != NULL);
+ unlink(fw_pathname);
+ g_free(fw_pathname);
+
data.num_cpus = 1;
g_test_add_data_func("/i440fx/defaults", &data, test_i440fx_defaults);
--
1.8.3.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [Qemu-devel] [PATCH v2 4/4] i440fx-test: verify firmware under 4G and 1M, both -bios and -pflash
2013-11-28 18:09 [Qemu-devel] [PATCH v2 0/4] i440fx-test: check firmware visibility Laszlo Ersek
` (2 preceding siblings ...)
2013-11-28 18:09 ` [Qemu-devel] [PATCH v2 3/4] i440fx-test: generate temporary firmware blob Laszlo Ersek
@ 2013-11-28 18:09 ` Laszlo Ersek
2013-11-29 14:09 ` Markus Armbruster
2013-11-28 18:18 ` [Qemu-devel] [PATCH v2 0/4] i440fx-test: check firmware visibility Laszlo Ersek
4 siblings, 1 reply; 18+ messages in thread
From: Laszlo Ersek @ 2013-11-28 18:09 UTC (permalink / raw)
To: qemu-devel
Check whether the firmware is not hidden by other memory regions.
Qemu is started in paused mode: it shouldn't try to interpret generated
garbage.
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
---
tests/i440fx-test.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 75 insertions(+), 6 deletions(-)
diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c
index 5506421..6de16e9 100644
--- a/tests/i440fx-test.c
+++ b/tests/i440fx-test.c
@@ -35,6 +35,11 @@ typedef struct TestData
int num_cpus;
} TestData;
+typedef struct FirmwareTestFixture {
+ /* decides whether we're testing -bios or -pflash */
+ bool is_bios;
+} FirmwareTestFixture;
+
static QPCIBus *test_start_get_bus(const TestData *s)
{
char *cmdline;
@@ -278,6 +283,7 @@ static void test_i440fx_pam(gconstpointer opaque)
}
#define FW_SIZE ((size_t)65536)
+#define ISA_BIOS_MAXSZ ((size_t)(128 * 1024))
/* Create a temporary firmware blob, and return its absolute pathname as a
* dynamically allocated string.
@@ -328,23 +334,86 @@ static char *create_firmware(void)
return ret == -1 ? NULL : pathname;
}
-int main(int argc, char **argv)
+static void test_i440fx_firmware(FirmwareTestFixture *fixture,
+ gconstpointer user_data)
{
- char *fw_pathname;
- TestData data;
- int ret;
-
- g_test_init(&argc, &argv, NULL);
+ char *fw_pathname, *cmdline;
+ char unsigned *buf;
+ size_t i, isa_bios_size;
fw_pathname = create_firmware();
g_assert(fw_pathname != NULL);
+
+ /* Better hope the user didn't put metacharacters in TMPDIR and co. */
+ cmdline = g_strdup_printf("-S %s %s",
+ fixture->is_bios ? "-bios" : "-pflash",
+ fw_pathname);
+ g_test_message("qemu cmdline: %s", cmdline);
+ qtest_start(cmdline);
+ g_free(cmdline);
+
+ /* Qemu has loaded the firmware (because qtest_start() only returns after
+ * the QMP handshake completes). We must unlink the firmware blob right
+ * here, because any assertion firing below would leak it in the
+ * filesystem. This is also the reason why we recreate the blob every time
+ * this function is invoked.
+ */
unlink(fw_pathname);
g_free(fw_pathname);
+ /* check below 4G */
+ buf = g_malloc0(FW_SIZE);
+ memread(0x100000000ULL - FW_SIZE, buf, FW_SIZE);
+ for (i = 0; i < FW_SIZE; ++i) {
+ g_assert_cmphex(buf[i], ==, (char unsigned)i);
+ }
+
+ /* check in ISA space too */
+ memset(buf, 0, FW_SIZE);
+ isa_bios_size = ISA_BIOS_MAXSZ < FW_SIZE ? ISA_BIOS_MAXSZ : FW_SIZE;
+ memread(0x100000 - isa_bios_size, buf, isa_bios_size);
+ for (i = 0; i < isa_bios_size; ++i) {
+ g_assert_cmphex(buf[i], ==,
+ (char unsigned)((FW_SIZE - isa_bios_size) + i));
+ }
+
+ g_free(buf);
+ qtest_end();
+}
+
+static void add_firmware_test(const char *testpath,
+ void (*setup_fixture)(FirmwareTestFixture *f,
+ gconstpointer test_data))
+{
+ g_test_add(testpath, FirmwareTestFixture, NULL, setup_fixture,
+ test_i440fx_firmware, NULL);
+}
+
+static void request_bios(FirmwareTestFixture *fixture,
+ gconstpointer user_data)
+{
+ fixture->is_bios = true;
+}
+
+static void request_pflash(FirmwareTestFixture *fixture,
+ gconstpointer user_data)
+{
+ fixture->is_bios = false;
+}
+
+int main(int argc, char **argv)
+{
+ TestData data;
+ int ret;
+
+ g_test_init(&argc, &argv, NULL);
+
data.num_cpus = 1;
g_test_add_data_func("/i440fx/defaults", &data, test_i440fx_defaults);
g_test_add_data_func("/i440fx/pam", &data, test_i440fx_pam);
+ add_firmware_test("/i440fx/firmware/bios", request_bios);
+ add_firmware_test("/i440fx/firmware/pflash", request_pflash);
ret = g_test_run();
return ret;
--
1.8.3.1
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH v2 0/4] i440fx-test: check firmware visibility
2013-11-28 18:09 [Qemu-devel] [PATCH v2 0/4] i440fx-test: check firmware visibility Laszlo Ersek
` (3 preceding siblings ...)
2013-11-28 18:09 ` [Qemu-devel] [PATCH v2 4/4] i440fx-test: verify firmware under 4G and 1M, both -bios and -pflash Laszlo Ersek
@ 2013-11-28 18:18 ` Laszlo Ersek
2013-11-29 17:12 ` Andreas Färber
4 siblings, 1 reply; 18+ messages in thread
From: Laszlo Ersek @ 2013-11-28 18:18 UTC (permalink / raw)
To: qemu-devel; +Cc: Andreas Färber
On 11/28/13 19:09, Laszlo Ersek wrote:
> Changes in v2:
> - Rebased ("-display none" is the default now).
>
> - I looked into memory management prudence in qtest, and I can see that
> we don't care about leaking small objects at all. (See for example
> qpci_device_foreach(), which calls qpci_device_find() in a loop, and
> leaks the retval every time.)
Sorry, last sentence missing:
"Which is why I didn't try to plug such leaks in my series."
NB: If I don't get feedback for this, I'm dropping it.
http://thread.gmane.org/gmane.comp.emulators.qemu/240173/focus=241207
Thanks
Laszlo
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH v2 1/4] i440fx-test: qtest_start() should be paired with qtest_end()
2013-11-28 18:09 ` [Qemu-devel] [PATCH v2 1/4] i440fx-test: qtest_start() should be paired with qtest_end() Laszlo Ersek
@ 2013-11-29 13:23 ` Markus Armbruster
0 siblings, 0 replies; 18+ messages in thread
From: Markus Armbruster @ 2013-11-29 13:23 UTC (permalink / raw)
To: Laszlo Ersek; +Cc: qemu-devel
Laszlo Ersek <lersek@redhat.com> writes:
> Similarly to commit 1d9358e6
> ("libqtest: New qtest_end() to go with qtest_start()").
>
> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> ---
> tests/i440fx-test.c | 10 ++++------
> 1 file changed, 4 insertions(+), 6 deletions(-)
>
> diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c
> index 65c786c..6ac46bf 100644
> --- a/tests/i440fx-test.c
> +++ b/tests/i440fx-test.c
> @@ -2,9 +2,11 @@
> * qtest I440FX test case
> *
> * Copyright IBM, Corp. 2012-2013
> + * Copyright Red Hat, Inc. 2013
> *
> * Authors:
> * Anthony Liguori <aliguori@us.ibm.com>
> + * Laszlo Ersek <lersek@redhat.com>
> *
> * This work is licensed under the terms of the GNU GPL, version 2 or later.
> * See the COPYING file in the top-level directory.
> @@ -256,7 +258,6 @@ static void test_i440fx_pam(gconstpointer opaque)
>
> int main(int argc, char **argv)
> {
> - QTestState *s;
> TestData data;
> char *cmdline;
> int ret;
> @@ -266,20 +267,17 @@ int main(int argc, char **argv)
> data.num_cpus = 1;
>
> cmdline = g_strdup_printf("-smp %d", data.num_cpus);
> - s = qtest_start(cmdline);
> + qtest_start(cmdline);
> g_free(cmdline);
>
> data.bus = qpci_init_pc();
>
> g_test_add_data_func("/i440fx/defaults", &data, test_i440fx_defaults);
> g_test_add_data_func("/i440fx/pam", &data, test_i440fx_pam);
> -
>
> ret = g_test_run();
>
> - if (s) {
> - qtest_quit(s);
> - }
> + qtest_end();
>
> return ret;
> }
There are a few more mismatched qtest_start()..qtest_quit() pairs
elsewhere. That's an observation, not a demand :)
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH v2 3/4] i440fx-test: generate temporary firmware blob
2013-11-28 18:09 ` [Qemu-devel] [PATCH v2 3/4] i440fx-test: generate temporary firmware blob Laszlo Ersek
@ 2013-11-29 13:57 ` Markus Armbruster
2013-11-29 15:07 ` Laszlo Ersek
0 siblings, 1 reply; 18+ messages in thread
From: Markus Armbruster @ 2013-11-29 13:57 UTC (permalink / raw)
To: Laszlo Ersek; +Cc: qemu-devel
Laszlo Ersek <lersek@redhat.com> writes:
> The blob is 64K in size and contains 0x00..0xFF repeatedly.
>
> The client code added to main() wouldn't make much sense in the long term.
> It helps with debugging and it silences gcc about create_firmware() being
> unused, and we'll replace it in the next patch anyway.
>
> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> ---
> tests/i440fx-test.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 62 insertions(+)
>
> diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c
> index 3962bca..5506421 100644
> --- a/tests/i440fx-test.c
> +++ b/tests/i440fx-test.c
> @@ -20,6 +20,11 @@
>
> #include <glib.h>
> #include <string.h>
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <errno.h>
> +#include <sys/mman.h>
> +#include <stdlib.h>
>
> #define BROKEN 1
>
> @@ -272,13 +277,70 @@ static void test_i440fx_pam(gconstpointer opaque)
> qtest_end();
> }
>
> +#define FW_SIZE ((size_t)65536)
Any particular reason for the cast?
> +
> +/* Create a temporary firmware blob, and return its absolute pathname as a
> + * dynamically allocated string.
> + * The file is closed before the function returns; it should be unlinked after
> + * use.
> + * In case of error, NULL is returned. The function prints the error message.
> + */
Actually, this creates a blob file. Its temporariness and firmware-ness
are all the caller's business. Rephrase accordingly?
Could this function be generally useful for tests?
> +static char *create_firmware(void)
> +{
> + int ret, fd;
> + char *pathname;
> + GError *error = NULL;
> +
> + ret = -1;
> + fd = g_file_open_tmp("fw_blob_XXXXXX", &pathname, &error);
> + if (fd == -1) {
> + fprintf(stderr, "unable to create temporary firmware blob: %s\n",
> + error->message);
> + g_error_free(error);
> + } else {
> + if (ftruncate(fd, FW_SIZE) == -1) {
> + fprintf(stderr, "ftruncate(\"%s\", %zu): %s\n", pathname, FW_SIZE,
> + strerror(errno));
I wonder whether glib wants us to use g_test_message() here.
> + } else {
> + void *buf;
> +
> + buf = mmap(NULL, FW_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
> + if (buf == MAP_FAILED) {
> + fprintf(stderr, "mmap(\"%s\", %zu): %s\n", pathname, FW_SIZE,
> + strerror(errno));
> + } else {
> + size_t i;
> +
> + for (i = 0; i < FW_SIZE; ++i) {
> + ((char unsigned *)buf)[i] = i;
(unsigned char *), please
Why not simply unsigned char *buf?
> + }
> + munmap(buf, FW_SIZE);
> + ret = 0;
> + }
> + }
Not sure I like this use of mmap(), but it's certainly covered by your
artistic license.
> + close(fd);
> + if (ret == -1) {
> + unlink(pathname);
> + g_free(pathname);
> + }
> + }
> +
> + return ret == -1 ? NULL : pathname;
> +}
Works. Stylistic nitpick: I'd do the error handling differently,
though. I prefer
if fail
report
bail out
continue normally
to
if fail
report
else
continue normally
because it keeps the normal workings flowing down "straight" rather than
increasingly indented.
static char *create_firmware(void)
{
int fd, i;
char *pathname;
GError *error = NULL;
unsigned char *buf;
fd = g_file_open_tmp("fw_blob_XXXXXX", &pathname, &error);
g_assert_no_error(error);
if (ftruncate(fd, FW_SIZE) < 0) {
fprintf(stderr, "ftruncate(\"%s\", %zu): %s\n", pathname, FW_SIZE,
strerror(errno));
goto fail;
}
buf = mmap(NULL, FW_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
if (buf == MAP_FAILED) {
fprintf(stderr, "mmap(\"%s\", %zu): %s\n", pathname, FW_SIZE,
strerror(errno));
goto fail;
}
for (i = 0; i < FW_SIZE; ++i) {
buf[i] = i;
}
munmap(buf, FW_SIZE);
close(fd);
return pathname;
err:
close(fd);
unlink(pathname);
g_free(pathname);
return NULL;
}
> +
> int main(int argc, char **argv)
> {
> + char *fw_pathname;
> TestData data;
> int ret;
>
> g_test_init(&argc, &argv, NULL);
>
> + fw_pathname = create_firmware();
> + g_assert(fw_pathname != NULL);
> + unlink(fw_pathname);
> + g_free(fw_pathname);
> +
> data.num_cpus = 1;
>
> g_test_add_data_func("/i440fx/defaults", &data, test_i440fx_defaults);
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH v2 4/4] i440fx-test: verify firmware under 4G and 1M, both -bios and -pflash
2013-11-28 18:09 ` [Qemu-devel] [PATCH v2 4/4] i440fx-test: verify firmware under 4G and 1M, both -bios and -pflash Laszlo Ersek
@ 2013-11-29 14:09 ` Markus Armbruster
2013-11-29 15:30 ` Laszlo Ersek
0 siblings, 1 reply; 18+ messages in thread
From: Markus Armbruster @ 2013-11-29 14:09 UTC (permalink / raw)
To: Laszlo Ersek; +Cc: qemu-devel
Laszlo Ersek <lersek@redhat.com> writes:
> Check whether the firmware is not hidden by other memory regions.
>
> Qemu is started in paused mode: it shouldn't try to interpret generated
> garbage.
>
> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> ---
> tests/i440fx-test.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++----
> 1 file changed, 75 insertions(+), 6 deletions(-)
>
> diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c
> index 5506421..6de16e9 100644
> --- a/tests/i440fx-test.c
> +++ b/tests/i440fx-test.c
> @@ -35,6 +35,11 @@ typedef struct TestData
> int num_cpus;
> } TestData;
>
> +typedef struct FirmwareTestFixture {
> + /* decides whether we're testing -bios or -pflash */
> + bool is_bios;
> +} FirmwareTestFixture;
> +
> static QPCIBus *test_start_get_bus(const TestData *s)
> {
> char *cmdline;
> @@ -278,6 +283,7 @@ static void test_i440fx_pam(gconstpointer opaque)
> }
>
> #define FW_SIZE ((size_t)65536)
> +#define ISA_BIOS_MAXSZ ((size_t)(128 * 1024))
>
> /* Create a temporary firmware blob, and return its absolute pathname as a
> * dynamically allocated string.
> @@ -328,23 +334,86 @@ static char *create_firmware(void)
> return ret == -1 ? NULL : pathname;
> }
>
> -int main(int argc, char **argv)
> +static void test_i440fx_firmware(FirmwareTestFixture *fixture,
> + gconstpointer user_data)
> {
> - char *fw_pathname;
> - TestData data;
> - int ret;
> -
> - g_test_init(&argc, &argv, NULL);
> + char *fw_pathname, *cmdline;
> + char unsigned *buf;
unsigned char *buf, please.
> + size_t i, isa_bios_size;
>
> fw_pathname = create_firmware();
> g_assert(fw_pathname != NULL);
> +
> + /* Better hope the user didn't put metacharacters in TMPDIR and co. */
Putting meta-characters in TMPDIR would be... adventurous.
You could add quotes. Then it breaks only when the user puts quotes in ;-P
> + cmdline = g_strdup_printf("-S %s %s",
> + fixture->is_bios ? "-bios" : "-pflash",
> + fw_pathname);
> + g_test_message("qemu cmdline: %s", cmdline);
> + qtest_start(cmdline);
> + g_free(cmdline);
> +
> + /* Qemu has loaded the firmware (because qtest_start() only returns after
> + * the QMP handshake completes). We must unlink the firmware blob right
> + * here, because any assertion firing below would leak it in the
> + * filesystem. This is also the reason why we recreate the blob every time
> + * this function is invoked.
> + */
> unlink(fw_pathname);
> g_free(fw_pathname);
>
> + /* check below 4G */
> + buf = g_malloc0(FW_SIZE);
> + memread(0x100000000ULL - FW_SIZE, buf, FW_SIZE);
Zero-fill immediately followed by read. Suggest g_malloc().
> + for (i = 0; i < FW_SIZE; ++i) {
> + g_assert_cmphex(buf[i], ==, (char unsigned)i);
(unsigned char), please.
> + }
> +
> + /* check in ISA space too */
> + memset(buf, 0, FW_SIZE);
> + isa_bios_size = ISA_BIOS_MAXSZ < FW_SIZE ? ISA_BIOS_MAXSZ : FW_SIZE;
> + memread(0x100000 - isa_bios_size, buf, isa_bios_size);
Zero-fill immediately followed by read. Suggest to drop memset().
> + for (i = 0; i < isa_bios_size; ++i) {
> + g_assert_cmphex(buf[i], ==,
> + (char unsigned)((FW_SIZE - isa_bios_size) + i));
Again.
> + }
> +
> + g_free(buf);
> + qtest_end();
> +}
> +
> +static void add_firmware_test(const char *testpath,
> + void (*setup_fixture)(FirmwareTestFixture *f,
> + gconstpointer test_data))
> +{
> + g_test_add(testpath, FirmwareTestFixture, NULL, setup_fixture,
> + test_i440fx_firmware, NULL);
> +}
> +
> +static void request_bios(FirmwareTestFixture *fixture,
> + gconstpointer user_data)
> +{
> + fixture->is_bios = true;
> +}
> +
> +static void request_pflash(FirmwareTestFixture *fixture,
> + gconstpointer user_data)
> +{
> + fixture->is_bios = false;
> +}
> +
I'm not sure fixtures are justified here. Perhaps having the test
function's test data argument point to the flag would be simpler.
> +int main(int argc, char **argv)
> +{
> + TestData data;
> + int ret;
> +
> + g_test_init(&argc, &argv, NULL);
> +
> data.num_cpus = 1;
>
> g_test_add_data_func("/i440fx/defaults", &data, test_i440fx_defaults);
> g_test_add_data_func("/i440fx/pam", &data, test_i440fx_pam);
> + add_firmware_test("/i440fx/firmware/bios", request_bios);
> + add_firmware_test("/i440fx/firmware/pflash", request_pflash);
>
> ret = g_test_run();
> return ret;
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH v2 2/4] i440fx-test: give each GTest case its own qtest
2013-11-28 18:09 ` [Qemu-devel] [PATCH v2 2/4] i440fx-test: give each GTest case its own qtest Laszlo Ersek
@ 2013-11-29 14:53 ` Eduardo Habkost
2013-11-29 15:35 ` Laszlo Ersek
0 siblings, 1 reply; 18+ messages in thread
From: Eduardo Habkost @ 2013-11-29 14:53 UTC (permalink / raw)
To: Laszlo Ersek; +Cc: qemu-devel
On Thu, Nov 28, 2013 at 07:09:13PM +0100, Laszlo Ersek wrote:
> The current two GTest cases, /i440fx/defaults and /i440fx/pam can share a
> qemu process, but the next two cases will need dedicated instances. It is
> messy (and order-dependent) to dynamically configure GTest cases one by
> one to start, stop, or keep the current qtest (*); let's just have each
> GTest work with its own qtest. The performance difference should be
> negligible.
>
> (*) As g_test_run() can be invoked at most once per process startup, and
> it runs GTest cases in sequence, we'd need clumsy data structures to
> control each GTest case to start/stop/keep the qemu instance. Or, we'd
> have to code the same information into the test methods themselves, which
> would make them even more order-dependent.
>
> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
[...]
> static void test_i440fx_defaults(gconstpointer opaque)
> {
> const TestData *s = opaque;
> + QPCIBus *bus;
> QPCIDevice *dev;
> uint32_t value;
>
> - dev = qpci_device_find(s->bus, QPCI_DEVFN(0, 0));
> + bus = test_start_get_bus(s);
> + dev = qpci_device_find(bus, QPCI_DEVFN(0, 0));
You can use GTest setup/teardown functions to do this automatically on
all tests, but I am not sure the code really looks better when using it.
I gave it a try and it looks like this:
diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c
index 6ac46bf..c9d6f6a 100644
--- a/tests/i440fx-test.c
+++ b/tests/i440fx-test.c
@@ -25,15 +25,35 @@
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
-typedef struct TestData
+typedef struct TestArgs
{
int num_cpus;
+} TestArgs;
+
+typedef struct TestData
+{
QPCIBus *bus;
} TestData;
-static void test_i440fx_defaults(gconstpointer opaque)
+static void test_setup(TestData *s, gconstpointer user_data)
+{
+ const TestArgs *args = user_data;
+ char *cmdline;
+
+ cmdline = g_strdup_printf("-smp %d", args->num_cpus);
+ qtest_start(cmdline);
+ g_free(cmdline);
+ s->bus = qpci_init_pc();
+}
+
+static void test_teardown(TestData *s, gconstpointer user_data)
+{
+ qtest_end();
+}
+
+static void test_i440fx_defaults(TestData *s, gconstpointer user_data)
{
- const TestData *s = opaque;
+ const TestArgs *args = user_data;
QPCIDevice *dev;
uint32_t value;
@@ -62,7 +82,7 @@ static void test_i440fx_defaults(gconstpointer opaque)
/* 3.2.11 */
value = qpci_config_readw(dev, 0x50); /* PMCCFG */
- if (s->num_cpus == 1) { /* WPE */
+ if (args->num_cpus == 1) { /* WPE */
g_assert(!(value & (1 << 15)));
} else {
g_assert((value & (1 << 15)));
@@ -176,9 +196,8 @@ static void write_area(uint32_t start, uint32_t end, uint8_t value)
g_free(data);
}
-static void test_i440fx_pam(gconstpointer opaque)
+static void test_i440fx_pam(TestData *s, gconstpointer user_data)
{
- const TestData *s = opaque;
QPCIDevice *dev;
int i;
static struct {
@@ -258,26 +277,16 @@ static void test_i440fx_pam(gconstpointer opaque)
int main(int argc, char **argv)
{
- TestData data;
- char *cmdline;
int ret;
+ TestArgs args = { .num_cpus = 1 };
g_test_init(&argc, &argv, NULL);
- data.num_cpus = 1;
-
- cmdline = g_strdup_printf("-smp %d", data.num_cpus);
- qtest_start(cmdline);
- g_free(cmdline);
-
- data.bus = qpci_init_pc();
-
- g_test_add_data_func("/i440fx/defaults", &data, test_i440fx_defaults);
- g_test_add_data_func("/i440fx/pam", &data, test_i440fx_pam);
+ g_test_add("/i440fx/defaults", TestData, &args, test_setup,
+ test_i440fx_defaults, test_teardown);
+ g_test_add("/i440fx/pam", TestData, &args, test_setup,
+ test_i440fx_pam, test_teardown);
ret = g_test_run();
-
- qtest_end();
-
return ret;
}
--
1.8.3.1
--
Eduardo
^ permalink raw reply related [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH v2 3/4] i440fx-test: generate temporary firmware blob
2013-11-29 13:57 ` Markus Armbruster
@ 2013-11-29 15:07 ` Laszlo Ersek
2013-11-29 16:26 ` Paolo Bonzini
2013-12-02 9:28 ` Markus Armbruster
0 siblings, 2 replies; 18+ messages in thread
From: Laszlo Ersek @ 2013-11-29 15:07 UTC (permalink / raw)
To: Markus Armbruster; +Cc: qemu-devel
On 11/29/13 14:57, Markus Armbruster wrote:
> Laszlo Ersek <lersek@redhat.com> writes:
>
>> The blob is 64K in size and contains 0x00..0xFF repeatedly.
>>
>> The client code added to main() wouldn't make much sense in the long term.
>> It helps with debugging and it silences gcc about create_firmware() being
>> unused, and we'll replace it in the next patch anyway.
>>
>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>> ---
>> tests/i440fx-test.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 62 insertions(+)
>>
>> diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c
>> index 3962bca..5506421 100644
>> --- a/tests/i440fx-test.c
>> +++ b/tests/i440fx-test.c
>> @@ -20,6 +20,11 @@
>>
>> #include <glib.h>
>> #include <string.h>
>> +#include <stdio.h>
>> +#include <unistd.h>
>> +#include <errno.h>
>> +#include <sys/mman.h>
>> +#include <stdlib.h>
>>
>> #define BROKEN 1
>>
>> @@ -272,13 +277,70 @@ static void test_i440fx_pam(gconstpointer opaque)
>> qtest_end();
>> }
>>
>> +#define FW_SIZE ((size_t)65536)
>
> Any particular reason for the cast?
Yes. It is a size. It is used in the controlling expression of a for()
statement, where the loop variable is a subscript. The variable is
size_t too (as it should be).
>
>> +
>> +/* Create a temporary firmware blob, and return its absolute pathname as a
>> + * dynamically allocated string.
>> + * The file is closed before the function returns; it should be unlinked after
>> + * use.
>> + * In case of error, NULL is returned. The function prints the error message.
>> + */
>
> Actually, this creates a blob file. Its temporariness and firmware-ness
> are all the caller's business. Rephrase accordingly?
I think that would be overkill. The function has a specific use.
>
> Could this function be generally useful for tests?
Not sure.
>
>> +static char *create_firmware(void)
>> +{
>> + int ret, fd;
>> + char *pathname;
>> + GError *error = NULL;
>> +
>> + ret = -1;
>> + fd = g_file_open_tmp("fw_blob_XXXXXX", &pathname, &error);
>> + if (fd == -1) {
>> + fprintf(stderr, "unable to create temporary firmware blob: %s\n",
>> + error->message);
>> + g_error_free(error);
>> + } else {
>> + if (ftruncate(fd, FW_SIZE) == -1) {
>> + fprintf(stderr, "ftruncate(\"%s\", %zu): %s\n", pathname, FW_SIZE,
>> + strerror(errno));
>
> I wonder whether glib wants us to use g_test_message() here.
g_test_message()s are normally supressed, with and without gtester. With
gtester, even --verbose doesn't display them (in the default config).
"tests/i440fx-test --verbose" displays those messages.
This is an error message and I didn't want it to depend on gtester
settings or command line arguments.
>
>> + } else {
>> + void *buf;
>> +
>> + buf = mmap(NULL, FW_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
>> + if (buf == MAP_FAILED) {
>> + fprintf(stderr, "mmap(\"%s\", %zu): %s\n", pathname, FW_SIZE,
>> + strerror(errno));
>> + } else {
>> + size_t i;
>> +
>> + for (i = 0; i < FW_SIZE; ++i) {
>> + ((char unsigned *)buf)[i] = i;
>
> (unsigned char *), please
>
> Why not simply unsigned char *buf?
Because mmap() returns a (void*), and I need to compare that against
MAP_FAILED.
The <sys/mman.h> header shall define the symbolic constant
MAP_FAILED which shall have type void * and shall be used to
indicate a failure from the mmap() function .
Your suggestion would include automatic conversion of MAP_FAILED to
(char *), which I did not want.
>
>> + }
>> + munmap(buf, FW_SIZE);
>> + ret = 0;
>> + }
>> + }
>
> Not sure I like this use of mmap(), but it's certainly covered by your
> artistic license.
Well, thanks for recognizing that. Do you think you could extend your
leniency to "char unsigned" as well?
My reason for writing these types in this order (char unsigned, long
unsigned, long long unsigned) is to follow printf() format specifiers.
As in , "%lu", "%llu". (Char types are promoted so no extra width
specifiers for them in printf(), but I like to be consistent with myself.)
>
>> + close(fd);
>> + if (ret == -1) {
>> + unlink(pathname);
>> + g_free(pathname);
>> + }
>> + }
>> +
>> + return ret == -1 ? NULL : pathname;
>> +}
>
> Works. Stylistic nitpick: I'd do the error handling differently,
> though. I prefer
>
> if fail
> report
> bail out
> continue normally
>
> to
>
> if fail
> report
> else
> continue normally
>
> because it keeps the normal workings flowing down "straight" rather than
> increasingly indented.
Normally I'm OK with cascading goto's and/or early returns.
I think that the way I did it here matches this situation well. After
the g_file_open_tmp() call succeeds, we must close fd in any case
(independently of whether as a whole the function succeeds or not).
Optionally, we must also unlink the file, in the same logical spot where
the close() is. (Because g_file_open() creates three resources at once,
a node in the filesystem, a file descriptor in the process, and a
dynamically allocated string.)
>
> static char *create_firmware(void)
> {
> int fd, i;
> char *pathname;
> GError *error = NULL;
> unsigned char *buf;
>
> fd = g_file_open_tmp("fw_blob_XXXXXX", &pathname, &error);
> g_assert_no_error(error);
>
> if (ftruncate(fd, FW_SIZE) < 0) {
> fprintf(stderr, "ftruncate(\"%s\", %zu): %s\n", pathname, FW_SIZE,
> strerror(errno));
> goto fail;
> }
>
> buf = mmap(NULL, FW_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
> if (buf == MAP_FAILED) {
> fprintf(stderr, "mmap(\"%s\", %zu): %s\n", pathname, FW_SIZE,
> strerror(errno));
> goto fail;
> }
>
> for (i = 0; i < FW_SIZE; ++i) {
> buf[i] = i;
> }
> munmap(buf, FW_SIZE);
>
> close(fd);
> return pathname;
>
> err:
> close(fd);
> unlink(pathname);
> g_free(pathname);
> return NULL;
> }
Your proposal duplicates the close(fd) call.
>
>> +
>> int main(int argc, char **argv)
>> {
>> + char *fw_pathname;
>> TestData data;
>> int ret;
>>
>> g_test_init(&argc, &argv, NULL);
>>
>> + fw_pathname = create_firmware();
>> + g_assert(fw_pathname != NULL);
>> + unlink(fw_pathname);
>> + g_free(fw_pathname);
>> +
>> data.num_cpus = 1;
>>
>> g_test_add_data_func("/i440fx/defaults", &data, test_i440fx_defaults);
I'm sure you're not deliberately trying to destroy my will to contribute
to upstream qemu.
Thanks
Laszlo
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH v2 4/4] i440fx-test: verify firmware under 4G and 1M, both -bios and -pflash
2013-11-29 14:09 ` Markus Armbruster
@ 2013-11-29 15:30 ` Laszlo Ersek
2013-11-29 16:29 ` Paolo Bonzini
0 siblings, 1 reply; 18+ messages in thread
From: Laszlo Ersek @ 2013-11-29 15:30 UTC (permalink / raw)
To: Markus Armbruster; +Cc: qemu-devel
On 11/29/13 15:09, Markus Armbruster wrote:
> Laszlo Ersek <lersek@redhat.com> writes:
>> + /* check below 4G */
>> + buf = g_malloc0(FW_SIZE);
>> + memread(0x100000000ULL - FW_SIZE, buf, FW_SIZE);
>
> Zero-fill immediately followed by read. Suggest g_malloc().
This was intentional. Please see the preexistent use of memread() in
this file, in verify_area().
>
>> + for (i = 0; i < FW_SIZE; ++i) {
>> + g_assert_cmphex(buf[i], ==, (char unsigned)i);
>
> (unsigned char), please.
>
>> + }
>> +
>> + /* check in ISA space too */
>> + memset(buf, 0, FW_SIZE);
>> + isa_bios_size = ISA_BIOS_MAXSZ < FW_SIZE ? ISA_BIOS_MAXSZ : FW_SIZE;
>> + memread(0x100000 - isa_bios_size, buf, isa_bios_size);
>
> Zero-fill immediately followed by read. Suggest to drop memset().
Same as above. memread() is unable to report errors. Some C library
functions also require you to set errno to zero first, then call the
function, then check errno, because some of the return values are
overlapped by success and failure returns. For memread() there's no
distinction in return value at all.
>
>> + for (i = 0; i < isa_bios_size; ++i) {
>> + g_assert_cmphex(buf[i], ==,
>> + (char unsigned)((FW_SIZE - isa_bios_size) + i));
>
> Again.
>
>> + }
>> +
>> + g_free(buf);
>> + qtest_end();
>> +}
>> +
>> +static void add_firmware_test(const char *testpath,
>> + void (*setup_fixture)(FirmwareTestFixture *f,
>> + gconstpointer test_data))
>> +{
>> + g_test_add(testpath, FirmwareTestFixture, NULL, setup_fixture,
>> + test_i440fx_firmware, NULL);
>> +}
>> +
>> +static void request_bios(FirmwareTestFixture *fixture,
>> + gconstpointer user_data)
>> +{
>> + fixture->is_bios = true;
>> +}
>> +
>> +static void request_pflash(FirmwareTestFixture *fixture,
>> + gconstpointer user_data)
>> +{
>> + fixture->is_bios = false;
>> +}
>> +
>
> I'm not sure fixtures are justified here. Perhaps having the test
> function's test data argument point to the flag would be simpler.
I gave a lot of thought to this. Fixtures are needed. See the
explanation in patch#2.
In more detail here: the gtest framework operates in two distinct
phases. First, you set up all of your test cases in *declarative* style.
That's what all the g_test_add_*() invocations do. You need to describe
everything in advance.
Then, you call g_test_run(), and it runs your declarative script in one
atomic sequence. You cannot change stuff *between* tests. If I want to
run the same test function twice, but with different data, I have the
following choices:
- Allocate and initialize two distinct memory areas. The lifetimes of
both of these will be identical, and they will both live during the
entire test series. I configure the first test case with the address of
the first area, and the 2nd case with the address of the 2nd area.
- Keep one global (shared) area, and let the tests read and write it as
they are executed by the framework. If you reorder the tests in the
outer script, things break.
- Use fixtures. The framework allocates the area for you before each
test, calls your init function on it, runs the test, then tears down the
area. No dependency between tests. Also, if you have N cases in your
test series, you still allocate at most 1 area at any point (as opposed
to at most N, like under option #1).
>
>> +int main(int argc, char **argv)
>> +{
>> + TestData data;
>> + int ret;
>> +
>> + g_test_init(&argc, &argv, NULL);
>> +
>> data.num_cpus = 1;
>>
>> g_test_add_data_func("/i440fx/defaults", &data, test_i440fx_defaults);
>> g_test_add_data_func("/i440fx/pam", &data, test_i440fx_pam);
>> + add_firmware_test("/i440fx/firmware/bios", request_bios);
>> + add_firmware_test("/i440fx/firmware/pflash", request_pflash);
>>
>> ret = g_test_run();
>> return ret;
Thanks
Laszlo
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH v2 2/4] i440fx-test: give each GTest case its own qtest
2013-11-29 14:53 ` Eduardo Habkost
@ 2013-11-29 15:35 ` Laszlo Ersek
0 siblings, 0 replies; 18+ messages in thread
From: Laszlo Ersek @ 2013-11-29 15:35 UTC (permalink / raw)
To: Eduardo Habkost; +Cc: qemu-devel
On 11/29/13 15:53, Eduardo Habkost wrote:
> On Thu, Nov 28, 2013 at 07:09:13PM +0100, Laszlo Ersek wrote:
>> The current two GTest cases, /i440fx/defaults and /i440fx/pam can share a
>> qemu process, but the next two cases will need dedicated instances. It is
>> messy (and order-dependent) to dynamically configure GTest cases one by
>> one to start, stop, or keep the current qtest (*); let's just have each
>> GTest work with its own qtest. The performance difference should be
>> negligible.
>>
>> (*) As g_test_run() can be invoked at most once per process startup, and
>> it runs GTest cases in sequence, we'd need clumsy data structures to
>> control each GTest case to start/stop/keep the qemu instance. Or, we'd
>> have to code the same information into the test methods themselves, which
>> would make them even more order-dependent.
>>
>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
> [...]
>> static void test_i440fx_defaults(gconstpointer opaque)
>> {
>> const TestData *s = opaque;
>> + QPCIBus *bus;
>> QPCIDevice *dev;
>> uint32_t value;
>>
>> - dev = qpci_device_find(s->bus, QPCI_DEVFN(0, 0));
>> + bus = test_start_get_bus(s);
>> + dev = qpci_device_find(bus, QPCI_DEVFN(0, 0));
>
> You can use GTest setup/teardown functions to do this automatically on
> all tests, but I am not sure the code really looks better when using it.
> I gave it a try and it looks like this:
>
> diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c
> index 6ac46bf..c9d6f6a 100644
> --- a/tests/i440fx-test.c
> +++ b/tests/i440fx-test.c
> @@ -25,15 +25,35 @@
>
> #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
>
> -typedef struct TestData
> +typedef struct TestArgs
> {
> int num_cpus;
> +} TestArgs;
> +
> +typedef struct TestData
> +{
> QPCIBus *bus;
> } TestData;
>
> -static void test_i440fx_defaults(gconstpointer opaque)
> +static void test_setup(TestData *s, gconstpointer user_data)
> +{
> + const TestArgs *args = user_data;
> + char *cmdline;
> +
> + cmdline = g_strdup_printf("-smp %d", args->num_cpus);
> + qtest_start(cmdline);
> + g_free(cmdline);
> + s->bus = qpci_init_pc();
> +}
> +
> +static void test_teardown(TestData *s, gconstpointer user_data)
> +{
> + qtest_end();
> +}
> +
> +static void test_i440fx_defaults(TestData *s, gconstpointer user_data)
> {
> - const TestData *s = opaque;
> + const TestArgs *args = user_data;
> QPCIDevice *dev;
> uint32_t value;
>
> @@ -62,7 +82,7 @@ static void test_i440fx_defaults(gconstpointer opaque)
>
> /* 3.2.11 */
> value = qpci_config_readw(dev, 0x50); /* PMCCFG */
> - if (s->num_cpus == 1) { /* WPE */
> + if (args->num_cpus == 1) { /* WPE */
> g_assert(!(value & (1 << 15)));
> } else {
> g_assert((value & (1 << 15)));
> @@ -176,9 +196,8 @@ static void write_area(uint32_t start, uint32_t end, uint8_t value)
> g_free(data);
> }
>
> -static void test_i440fx_pam(gconstpointer opaque)
> +static void test_i440fx_pam(TestData *s, gconstpointer user_data)
> {
> - const TestData *s = opaque;
> QPCIDevice *dev;
> int i;
> static struct {
> @@ -258,26 +277,16 @@ static void test_i440fx_pam(gconstpointer opaque)
>
> int main(int argc, char **argv)
> {
> - TestData data;
> - char *cmdline;
> int ret;
> + TestArgs args = { .num_cpus = 1 };
>
> g_test_init(&argc, &argv, NULL);
>
> - data.num_cpus = 1;
> -
> - cmdline = g_strdup_printf("-smp %d", data.num_cpus);
> - qtest_start(cmdline);
> - g_free(cmdline);
> -
> - data.bus = qpci_init_pc();
> -
> - g_test_add_data_func("/i440fx/defaults", &data, test_i440fx_defaults);
> - g_test_add_data_func("/i440fx/pam", &data, test_i440fx_pam);
> + g_test_add("/i440fx/defaults", TestData, &args, test_setup,
> + test_i440fx_defaults, test_teardown);
> + g_test_add("/i440fx/pam", TestData, &args, test_setup,
> + test_i440fx_pam, test_teardown);
>
> ret = g_test_run();
> -
> - qtest_end();
> -
> return ret;
> }
>
I agree that this specific use of the fixtures doesn't really pay off.
Thank you
Laszlo
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH v2 3/4] i440fx-test: generate temporary firmware blob
2013-11-29 15:07 ` Laszlo Ersek
@ 2013-11-29 16:26 ` Paolo Bonzini
2013-12-02 9:28 ` Markus Armbruster
1 sibling, 0 replies; 18+ messages in thread
From: Paolo Bonzini @ 2013-11-29 16:26 UTC (permalink / raw)
To: Laszlo Ersek; +Cc: Markus Armbruster, qemu-devel
Il 29/11/2013 16:07, Laszlo Ersek ha scritto:
> I think that the way I did it here matches this situation well. After
> the g_file_open_tmp() call succeeds, we must close fd in any case
> (independently of whether as a whole the function succeeds or not).
> Optionally, we must also unlink the file, in the same logical spot where
> the close() is. (Because g_file_open() creates three resources at once,
> a node in the filesystem, a file descriptor in the process, and a
> dynamically allocated string.)
I agree that the way you wrote it makes sense if you do not use goto.
Paolo
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH v2 4/4] i440fx-test: verify firmware under 4G and 1M, both -bios and -pflash
2013-11-29 15:30 ` Laszlo Ersek
@ 2013-11-29 16:29 ` Paolo Bonzini
0 siblings, 0 replies; 18+ messages in thread
From: Paolo Bonzini @ 2013-11-29 16:29 UTC (permalink / raw)
To: Laszlo Ersek; +Cc: Markus Armbruster, qemu-devel
Il 29/11/2013 16:30, Laszlo Ersek ha scritto:
>> > Zero-fill immediately followed by read. Suggest to drop memset().
> Same as above. memread() is unable to report errors. Some C library
> functions also require you to set errno to zero first, then call the
> function, then check errno, because some of the return values are
> overlapped by success and failure returns. For memread() there's no
> distinction in return value at all.
>
Errors in memread() will cause an assertion failure, but I think it's
okay to use g_malloc0.
That said, I agree that the common coding conventions (as well as the
English grammar) makes "char unsigned" look a bit weird.
Regarding usage of fixtures, I think it's within your artistic license
(quoting) and it's good to have an example to cut-and-paste from
Paolo.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH v2 0/4] i440fx-test: check firmware visibility
2013-11-28 18:18 ` [Qemu-devel] [PATCH v2 0/4] i440fx-test: check firmware visibility Laszlo Ersek
@ 2013-11-29 17:12 ` Andreas Färber
2013-11-29 17:18 ` Laszlo Ersek
0 siblings, 1 reply; 18+ messages in thread
From: Andreas Färber @ 2013-11-29 17:12 UTC (permalink / raw)
To: Laszlo Ersek; +Cc: qemu-devel
Am 28.11.2013 19:18, schrieb Laszlo Ersek:
> On 11/28/13 19:09, Laszlo Ersek wrote:
>
>> Changes in v2:
>> - Rebased ("-display none" is the default now).
>>
>> - I looked into memory management prudence in qtest, and I can see that
>> we don't care about leaking small objects at all. (See for example
>> qpci_device_foreach(), which calls qpci_device_find() in a loop, and
>> leaks the retval every time.)
>
> Sorry, last sentence missing:
> "Which is why I didn't try to plug such leaks in my series."
Laszlo,
> NB: If I don't get feedback for this, I'm dropping it.
> http://thread.gmane.org/gmane.comp.emulators.qemu/240173/focus=241207
If you're specifically waiting on my review, being the only one CC'ed,
you'll need to be more patient as other series have been waiting longer
on me.
Sorry,
Andreas
P.S. Hope you're feeling better since KVM Forum. :)
--
SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH v2 0/4] i440fx-test: check firmware visibility
2013-11-29 17:12 ` Andreas Färber
@ 2013-11-29 17:18 ` Laszlo Ersek
0 siblings, 0 replies; 18+ messages in thread
From: Laszlo Ersek @ 2013-11-29 17:18 UTC (permalink / raw)
To: Andreas Färber; +Cc: qemu-devel
On 11/29/13 18:12, Andreas Färber wrote:
> Am 28.11.2013 19:18, schrieb Laszlo Ersek:
>> On 11/28/13 19:09, Laszlo Ersek wrote:
>>
>>> Changes in v2:
>>> - Rebased ("-display none" is the default now).
>>>
>>> - I looked into memory management prudence in qtest, and I can see that
>>> we don't care about leaking small objects at all. (See for example
>>> qpci_device_foreach(), which calls qpci_device_find() in a loop, and
>>> leaks the retval every time.)
>>
>> Sorry, last sentence missing:
>> "Which is why I didn't try to plug such leaks in my series."
>
> Laszlo,
>
>> NB: If I don't get feedback for this, I'm dropping it.
>> http://thread.gmane.org/gmane.comp.emulators.qemu/240173/focus=241207
>
> If you're specifically waiting on my review, being the only one CC'ed,
> you'll need to be more patient as other series have been waiting longer
> on me.
>
> Sorry,
> Andreas
I included the reference to show that I'm doing this in response to your
request. It's not something I'd have invested time in on my own behalf.
(Especially not three rounds.)
Review-wise, I'm fine with any review that gets the series merged and
the burden off my shoulder. There are patches where I feel I need advice
or sanity checking, this series is not one of them.
> P.S. Hope you're feeling better since KVM Forum. :)
My health is better, thank you very much for asking!
Cheers,
Laszlo
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [Qemu-devel] [PATCH v2 3/4] i440fx-test: generate temporary firmware blob
2013-11-29 15:07 ` Laszlo Ersek
2013-11-29 16:26 ` Paolo Bonzini
@ 2013-12-02 9:28 ` Markus Armbruster
1 sibling, 0 replies; 18+ messages in thread
From: Markus Armbruster @ 2013-12-02 9:28 UTC (permalink / raw)
To: Laszlo Ersek; +Cc: qemu-devel
Laszlo Ersek <lersek@redhat.com> writes:
> On 11/29/13 14:57, Markus Armbruster wrote:
>> Laszlo Ersek <lersek@redhat.com> writes:
>>
>>> The blob is 64K in size and contains 0x00..0xFF repeatedly.
>>>
>>> The client code added to main() wouldn't make much sense in the long term.
>>> It helps with debugging and it silences gcc about create_firmware() being
>>> unused, and we'll replace it in the next patch anyway.
>>>
>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com>
>>> ---
>>> tests/i440fx-test.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>>> 1 file changed, 62 insertions(+)
>>>
>>> diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c
>>> index 3962bca..5506421 100644
>>> --- a/tests/i440fx-test.c
>>> +++ b/tests/i440fx-test.c
>>> @@ -20,6 +20,11 @@
>>>
>>> #include <glib.h>
>>> #include <string.h>
>>> +#include <stdio.h>
>>> +#include <unistd.h>
>>> +#include <errno.h>
>>> +#include <sys/mman.h>
>>> +#include <stdlib.h>
>>>
>>> #define BROKEN 1
>>>
>>> @@ -272,13 +277,70 @@ static void test_i440fx_pam(gconstpointer opaque)
>>> qtest_end();
>>> }
>>>
>>> +#define FW_SIZE ((size_t)65536)
>>
>> Any particular reason for the cast?
>
> Yes. It is a size. It is used in the controlling expression of a for()
> statement, where the loop variable is a subscript. The variable is
> size_t too (as it should be).
I disagree, but it's your code.
>>> +
>>> +/* Create a temporary firmware blob, and return its absolute pathname as a
>>> + * dynamically allocated string.
>>> + * The file is closed before the function returns; it should be
>>> unlinked after
>>> + * use.
>>> + * In case of error, NULL is returned. The function prints the
>>> error message.
>>> + */
>>
>> Actually, this creates a blob file. Its temporariness and firmware-ness
>> are all the caller's business. Rephrase accordingly?
>
> I think that would be overkill. The function has a specific use.
>
>>
>> Could this function be generally useful for tests?
>
> Not sure.
>
>>
>>> +static char *create_firmware(void)
>>> +{
>>> + int ret, fd;
>>> + char *pathname;
>>> + GError *error = NULL;
>>> +
>>> + ret = -1;
>>> + fd = g_file_open_tmp("fw_blob_XXXXXX", &pathname, &error);
>>> + if (fd == -1) {
>>> + fprintf(stderr, "unable to create temporary firmware blob: %s\n",
>>> + error->message);
>>> + g_error_free(error);
>>> + } else {
>>> + if (ftruncate(fd, FW_SIZE) == -1) {
>>> + fprintf(stderr, "ftruncate(\"%s\", %zu): %s\n", pathname,
>>> FW_SIZE,
>>> + strerror(errno));
>>
>> I wonder whether glib wants us to use g_test_message() here.
>
> g_test_message()s are normally supressed, with and without gtester. With
> gtester, even --verbose doesn't display them (in the default config).
> "tests/i440fx-test --verbose" displays those messages.
>
> This is an error message and I didn't want it to depend on gtester
> settings or command line arguments.
Makes sense.
Does glib provide means to report test errors other than g_assert() &
friends? I'm asking because this would be the first use of stderr in
tests/*-test.c.
Losely related: should we embrace the fact that this is a test function,
whose failure is always ultimately fatal, and terminate the test right
there?
>>
>>> + } else {
>>> + void *buf;
>>> +
>>> + buf = mmap(NULL, FW_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
>>> + if (buf == MAP_FAILED) {
>>> + fprintf(stderr, "mmap(\"%s\", %zu): %s\n", pathname, FW_SIZE,
>>> + strerror(errno));
>>> + } else {
>>> + size_t i;
>>> +
>>> + for (i = 0; i < FW_SIZE; ++i) {
>>> + ((char unsigned *)buf)[i] = i;
>>
>> (unsigned char *), please
>>
>> Why not simply unsigned char *buf?
>
> Because mmap() returns a (void*), and I need to compare that against
> MAP_FAILED.
>
> The <sys/mman.h> header shall define the symbolic constant
> MAP_FAILED which shall have type void * and shall be used to
> indicate a failure from the mmap() function .
>
> Your suggestion would include automatic conversion of MAP_FAILED to
> (char *), which I did not want.
This isn't C++. Generic pointers are a feature of C. I don't think
littering the code with casts to avoid them is a good idea, because
1. casts defeat the type checker, and 2. brevity is a virtue.
>>> + }
>>> + munmap(buf, FW_SIZE);
>>> + ret = 0;
>>> + }
>>> + }
>>
>> Not sure I like this use of mmap(), but it's certainly covered by your
>> artistic license.
>
> Well, thanks for recognizing that. Do you think you could extend your
> leniency to "char unsigned" as well?
>
> My reason for writing these types in this order (char unsigned, long
> unsigned, long long unsigned) is to follow printf() format specifiers.
> As in , "%lu", "%llu". (Char types are promoted so no extra width
> specifiers for them in printf(), but I like to be consistent with myself.)
I prefer to be consistent with the rest of the code
$ git-grep 'unsigned char'|wc -l
553
$ git-grep 'char unsigned'|wc -l
8
even when I find it rather disagreable, like CamelCasedTypeDefs.
Besides, C uses words borrowed from English, so sticking to English
grammar (unsigned character) rather than French grammar (charactère
non-signée) makes some sense. My brain parses the familiar "unsigned
char" more quickly than the unusual "char unsigned".
>>> + close(fd);
>>> + if (ret == -1) {
>>> + unlink(pathname);
>>> + g_free(pathname);
>>> + }
>>> + }
>>> +
>>> + return ret == -1 ? NULL : pathname;
>>> +}
>>
>> Works. Stylistic nitpick: I'd do the error handling differently,
>> though. I prefer
>>
>> if fail
>> report
>> bail out
>> continue normally
>>
>> to
>>
>> if fail
>> report
>> else
>> continue normally
>>
>> because it keeps the normal workings flowing down "straight" rather than
>> increasingly indented.
>
> Normally I'm OK with cascading goto's and/or early returns.
>
> I think that the way I did it here matches this situation well. After
> the g_file_open_tmp() call succeeds, we must close fd in any case
> (independently of whether as a whole the function succeeds or not).
> Optionally, we must also unlink the file, in the same logical spot where
> the close() is. (Because g_file_open() creates three resources at once,
> a node in the filesystem, a file descriptor in the process, and a
> dynamically allocated string.)
>
>>
>> static char *create_firmware(void)
>> {
>> int fd, i;
>> char *pathname;
>> GError *error = NULL;
>> unsigned char *buf;
>>
>> fd = g_file_open_tmp("fw_blob_XXXXXX", &pathname, &error);
>> g_assert_no_error(error);
>>
>> if (ftruncate(fd, FW_SIZE) < 0) {
>> fprintf(stderr, "ftruncate(\"%s\", %zu): %s\n", pathname, FW_SIZE,
>> strerror(errno));
>> goto fail;
>> }
>>
>> buf = mmap(NULL, FW_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
>> if (buf == MAP_FAILED) {
>> fprintf(stderr, "mmap(\"%s\", %zu): %s\n", pathname, FW_SIZE,
>> strerror(errno));
>> goto fail;
>> }
>>
>> for (i = 0; i < FW_SIZE; ++i) {
>> buf[i] = i;
>> }
>> munmap(buf, FW_SIZE);
>>
>> close(fd);
>> return pathname;
>>
>> err:
>> close(fd);
>> unlink(pathname);
>> g_free(pathname);
>> return NULL;
>> }
>
> Your proposal duplicates the close(fd) call.
Yes, but there's one less variable to keep in short-term memory (ret),
and less nesting. Overall, I find it easier to follow. Anyway, your
choice.
>>> +
>>> int main(int argc, char **argv)
>>> {
>>> + char *fw_pathname;
>>> TestData data;
>>> int ret;
>>>
>>> g_test_init(&argc, &argv, NULL);
>>>
>>> + fw_pathname = create_firmware();
>>> + g_assert(fw_pathname != NULL);
>>> + unlink(fw_pathname);
>>> + g_free(fw_pathname);
>>> +
>>> data.num_cpus = 1;
>>>
>>> g_test_add_data_func("/i440fx/defaults", &data, test_i440fx_defaults);
>
> I'm sure you're not deliberately trying to destroy my will to contribute
> to upstream qemu.
Nope, and I'm sure you're not deliberately trying to destroy my will to
review your upstream QEMU patches.
I just re-read my review, and I still believe it was obvious enough that
most of my comments where of the type "have you thought of this, perhaps
you like it better (I do)". I'm sorry this brushed you the wrong way.
Such comments will happen again, I'm afraid. If I knew how to make them
more obvious without adding verbiage, I'd try.
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2013-12-02 9:28 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-11-28 18:09 [Qemu-devel] [PATCH v2 0/4] i440fx-test: check firmware visibility Laszlo Ersek
2013-11-28 18:09 ` [Qemu-devel] [PATCH v2 1/4] i440fx-test: qtest_start() should be paired with qtest_end() Laszlo Ersek
2013-11-29 13:23 ` Markus Armbruster
2013-11-28 18:09 ` [Qemu-devel] [PATCH v2 2/4] i440fx-test: give each GTest case its own qtest Laszlo Ersek
2013-11-29 14:53 ` Eduardo Habkost
2013-11-29 15:35 ` Laszlo Ersek
2013-11-28 18:09 ` [Qemu-devel] [PATCH v2 3/4] i440fx-test: generate temporary firmware blob Laszlo Ersek
2013-11-29 13:57 ` Markus Armbruster
2013-11-29 15:07 ` Laszlo Ersek
2013-11-29 16:26 ` Paolo Bonzini
2013-12-02 9:28 ` Markus Armbruster
2013-11-28 18:09 ` [Qemu-devel] [PATCH v2 4/4] i440fx-test: verify firmware under 4G and 1M, both -bios and -pflash Laszlo Ersek
2013-11-29 14:09 ` Markus Armbruster
2013-11-29 15:30 ` Laszlo Ersek
2013-11-29 16:29 ` Paolo Bonzini
2013-11-28 18:18 ` [Qemu-devel] [PATCH v2 0/4] i440fx-test: check firmware visibility Laszlo Ersek
2013-11-29 17:12 ` Andreas Färber
2013-11-29 17:18 ` Laszlo Ersek
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).