* [PATCH v4 0/2] tests/qtest/libqos: add DMA support for writing and reading fw_cfg files
@ 2025-01-09 7:49 Ani Sinha
2025-01-09 7:49 ` [PATCH v4 1/2] libqos/fw_cfg: refactor file directory iteraton to make it more reusable Ani Sinha
2025-01-09 7:49 ` [PATCH v4 2/2] tests/qtest/libqos: add DMA support for writing and reading fw_cfg files Ani Sinha
0 siblings, 2 replies; 9+ messages in thread
From: Ani Sinha @ 2025-01-09 7:49 UTC (permalink / raw)
Cc: berrange, armbru, Ani Sinha, kraxel, farosas, lvivier, pbonzini,
philmd, qemu-devel
This patchset enables DMA interface support for writing fw-cfg files in
libqtest. The first patch is just a code refactoring so that fw-cfg
directory parsing can be part of a separate helper function.
The second patch is the actual patch that introduces two new apis for
writing and reading fw-cfg files using DMA interface. The apis are
tested by writing a qtest for vmfwupdate. Please see
tests/qtest/vmfwupdate-test.c changes in
https://gitlab.com/anisinha/qemu/-/commit/4d94a67d1cf29780f136a8bd2c21ff0e57c7b2db
To test the code, you can pull in this branch
https://gitlab.com/anisinha/qemu/-/commits/fuki-hyperface which has
vmfwupdate changes on top of the patches sent in this series.
cc: kraxel@redhat.com
cc: farosas@suse.de
cc: lvivier@redhat.com
cc: pbonzini@redhat.com
cc: armbru@redhat.com
cc: philmd@linaro.org
cc: berrange@redhat.com
cc: qemu-devel@nongnu.org
Ani Sinha (2):
libqos/fw_cfg: refactor file directory iteraton to make it more
reusable
tests/qtest/libqos: add DMA support for writing and reading fw_cfg
files
tests/qtest/libqos/fw_cfg.c | 201 ++++++++++++++++++++++++++++++++----
tests/qtest/libqos/fw_cfg.h | 6 +-
2 files changed, 184 insertions(+), 23 deletions(-)
--
2.45.2
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v4 1/2] libqos/fw_cfg: refactor file directory iteraton to make it more reusable 2025-01-09 7:49 [PATCH v4 0/2] tests/qtest/libqos: add DMA support for writing and reading fw_cfg files Ani Sinha @ 2025-01-09 7:49 ` Ani Sinha 2025-01-09 20:01 ` Fabiano Rosas 2025-01-09 7:49 ` [PATCH v4 2/2] tests/qtest/libqos: add DMA support for writing and reading fw_cfg files Ani Sinha 1 sibling, 1 reply; 9+ messages in thread From: Ani Sinha @ 2025-01-09 7:49 UTC (permalink / raw) To: Philippe Mathieu-Daudé, Gerd Hoffmann, Fabiano Rosas, Laurent Vivier, Paolo Bonzini Cc: berrange, armbru, Ani Sinha, qemu-devel fw-cfg file directory iteration code can be used by other functions that may want to implement fw-cfg file operations. Refactor it into a smaller helper so that it can be reused. No functional change. Signed-off-by: Ani Sinha <anisinha@redhat.com> --- tests/qtest/libqos/fw_cfg.c | 63 ++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/tests/qtest/libqos/fw_cfg.c b/tests/qtest/libqos/fw_cfg.c index 89f053ccac..f1ed4898f7 100644 --- a/tests/qtest/libqos/fw_cfg.c +++ b/tests/qtest/libqos/fw_cfg.c @@ -60,6 +60,39 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) qtest_writew(fw_cfg->qts, fw_cfg->base, key); } +static bool +find_pdir_entry(QFWCFG *fw_cfg, const char *filename, + uint16_t *sel, uint32_t *size) +{ + g_autofree unsigned char *filesbuf = NULL; + uint32_t count; + size_t dsize; + FWCfgFile *pdir_entry; + uint32_t i; + bool found = false; + + *size = 0; + *sel = 0; + + qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count)); + count = be32_to_cpu(count); + dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file); + filesbuf = g_malloc(dsize); + g_assert(filesbuf); + qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize); + pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t)); + for (i = 0; i < count; ++i, ++pdir_entry) { + if (!strcmp(pdir_entry->name, filename)) { + *size = be32_to_cpu(pdir_entry->size); + *sel = be16_to_cpu(pdir_entry->select); + found = true; + break; + } + } + + return found; +} + /* * The caller need check the return value. When the return value is * nonzero, it means that some bytes have been transferred. @@ -75,32 +108,18 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename, void *data, size_t buflen) { - uint32_t count; - uint32_t i; - unsigned char *filesbuf = NULL; - size_t dsize; - FWCfgFile *pdir_entry; size_t filesize = 0; + uint32_t len; + uint16_t sel; - qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count)); - count = be32_to_cpu(count); - dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file); - filesbuf = g_malloc(dsize); - qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize); - pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t)); - for (i = 0; i < count; ++i, ++pdir_entry) { - if (!strcmp(pdir_entry->name, filename)) { - uint32_t len = be32_to_cpu(pdir_entry->size); - uint16_t sel = be16_to_cpu(pdir_entry->select); - filesize = len; - if (len > buflen) { - len = buflen; - } - qfw_cfg_get(fw_cfg, sel, data, len); - break; + if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { + filesize = len; + if (len > buflen) { + len = buflen; } + qfw_cfg_get(fw_cfg, sel, data, len); } - g_free(filesbuf); + return filesize; } -- 2.45.2 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v4 1/2] libqos/fw_cfg: refactor file directory iteraton to make it more reusable 2025-01-09 7:49 ` [PATCH v4 1/2] libqos/fw_cfg: refactor file directory iteraton to make it more reusable Ani Sinha @ 2025-01-09 20:01 ` Fabiano Rosas 2025-01-10 3:43 ` Ani Sinha 0 siblings, 1 reply; 9+ messages in thread From: Fabiano Rosas @ 2025-01-09 20:01 UTC (permalink / raw) To: Ani Sinha, Philippe Mathieu-Daudé, Gerd Hoffmann, Laurent Vivier, Paolo Bonzini Cc: berrange, armbru, Ani Sinha, qemu-devel Ani Sinha <anisinha@redhat.com> writes: > fw-cfg file directory iteration code can be used by other functions that may > want to implement fw-cfg file operations. Refactor it into a smaller helper > so that it can be reused. > > No functional change. > > Signed-off-by: Ani Sinha <anisinha@redhat.com> > --- > tests/qtest/libqos/fw_cfg.c | 63 ++++++++++++++++++++++++------------- > 1 file changed, 41 insertions(+), 22 deletions(-) > > diff --git a/tests/qtest/libqos/fw_cfg.c b/tests/qtest/libqos/fw_cfg.c > index 89f053ccac..f1ed4898f7 100644 > --- a/tests/qtest/libqos/fw_cfg.c > +++ b/tests/qtest/libqos/fw_cfg.c > @@ -60,6 +60,39 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) > qtest_writew(fw_cfg->qts, fw_cfg->base, key); > } > > +static bool > +find_pdir_entry(QFWCFG *fw_cfg, const char *filename, > + uint16_t *sel, uint32_t *size) > +{ > + g_autofree unsigned char *filesbuf = NULL; > + uint32_t count; > + size_t dsize; > + FWCfgFile *pdir_entry; > + uint32_t i; > + bool found = false; > + > + *size = 0; > + *sel = 0; > + > + qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count)); > + count = be32_to_cpu(count); > + dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file); > + filesbuf = g_malloc(dsize); > + g_assert(filesbuf); g_malloc already aborts the program if the allocation fails. > + qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize); > + pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t)); > + for (i = 0; i < count; ++i, ++pdir_entry) { > + if (!strcmp(pdir_entry->name, filename)) { > + *size = be32_to_cpu(pdir_entry->size); > + *sel = be16_to_cpu(pdir_entry->select); > + found = true; > + break; > + } > + } > + > + return found; > +} > + > /* > * The caller need check the return value. When the return value is > * nonzero, it means that some bytes have been transferred. > @@ -75,32 +108,18 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) > size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename, > void *data, size_t buflen) > { > - uint32_t count; > - uint32_t i; > - unsigned char *filesbuf = NULL; > - size_t dsize; > - FWCfgFile *pdir_entry; > size_t filesize = 0; > + uint32_t len; > + uint16_t sel; > > - qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count)); > - count = be32_to_cpu(count); > - dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file); > - filesbuf = g_malloc(dsize); > - qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize); > - pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t)); > - for (i = 0; i < count; ++i, ++pdir_entry) { > - if (!strcmp(pdir_entry->name, filename)) { > - uint32_t len = be32_to_cpu(pdir_entry->size); > - uint16_t sel = be16_to_cpu(pdir_entry->select); > - filesize = len; > - if (len > buflen) { > - len = buflen; > - } > - qfw_cfg_get(fw_cfg, sel, data, len); > - break; > + if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { > + filesize = len; > + if (len > buflen) { > + len = buflen; > } > + qfw_cfg_get(fw_cfg, sel, data, len); > } > - g_free(filesbuf); > + > return filesize; > } ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 1/2] libqos/fw_cfg: refactor file directory iteraton to make it more reusable 2025-01-09 20:01 ` Fabiano Rosas @ 2025-01-10 3:43 ` Ani Sinha 0 siblings, 0 replies; 9+ messages in thread From: Ani Sinha @ 2025-01-10 3:43 UTC (permalink / raw) To: Fabiano Rosas Cc: Philippe Mathieu-Daudé, Gerd Hoffmann, Laurent Vivier, Paolo Bonzini, Daniel Berrange, Markus Armbruster, qemu-devel > On 10 Jan 2025, at 1:31 AM, Fabiano Rosas <farosas@suse.de> wrote: > > Ani Sinha <anisinha@redhat.com> writes: > >> fw-cfg file directory iteration code can be used by other functions that may >> want to implement fw-cfg file operations. Refactor it into a smaller helper >> so that it can be reused. >> >> No functional change. >> >> Signed-off-by: Ani Sinha <anisinha@redhat.com> >> --- >> tests/qtest/libqos/fw_cfg.c | 63 ++++++++++++++++++++++++------------- >> 1 file changed, 41 insertions(+), 22 deletions(-) >> >> diff --git a/tests/qtest/libqos/fw_cfg.c b/tests/qtest/libqos/fw_cfg.c >> index 89f053ccac..f1ed4898f7 100644 >> --- a/tests/qtest/libqos/fw_cfg.c >> +++ b/tests/qtest/libqos/fw_cfg.c >> @@ -60,6 +60,39 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) >> qtest_writew(fw_cfg->qts, fw_cfg->base, key); >> } >> >> +static bool >> +find_pdir_entry(QFWCFG *fw_cfg, const char *filename, >> + uint16_t *sel, uint32_t *size) >> +{ >> + g_autofree unsigned char *filesbuf = NULL; >> + uint32_t count; >> + size_t dsize; >> + FWCfgFile *pdir_entry; >> + uint32_t i; >> + bool found = false; >> + >> + *size = 0; >> + *sel = 0; >> + >> + qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count)); >> + count = be32_to_cpu(count); >> + dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file); >> + filesbuf = g_malloc(dsize); >> + g_assert(filesbuf); > > g_malloc already aborts the program if the allocation fails. ok good point. Will remove them in the next patch spin-up. > >> + qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize); >> + pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t)); >> + for (i = 0; i < count; ++i, ++pdir_entry) { >> + if (!strcmp(pdir_entry->name, filename)) { >> + *size = be32_to_cpu(pdir_entry->size); >> + *sel = be16_to_cpu(pdir_entry->select); >> + found = true; >> + break; >> + } >> + } >> + >> + return found; >> +} >> + >> /* >> * The caller need check the return value. When the return value is >> * nonzero, it means that some bytes have been transferred. >> @@ -75,32 +108,18 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) >> size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename, >> void *data, size_t buflen) >> { >> - uint32_t count; >> - uint32_t i; >> - unsigned char *filesbuf = NULL; >> - size_t dsize; >> - FWCfgFile *pdir_entry; >> size_t filesize = 0; >> + uint32_t len; >> + uint16_t sel; >> >> - qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count)); >> - count = be32_to_cpu(count); >> - dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file); >> - filesbuf = g_malloc(dsize); >> - qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize); >> - pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t)); >> - for (i = 0; i < count; ++i, ++pdir_entry) { >> - if (!strcmp(pdir_entry->name, filename)) { >> - uint32_t len = be32_to_cpu(pdir_entry->size); >> - uint16_t sel = be16_to_cpu(pdir_entry->select); >> - filesize = len; >> - if (len > buflen) { >> - len = buflen; >> - } >> - qfw_cfg_get(fw_cfg, sel, data, len); >> - break; >> + if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { >> + filesize = len; >> + if (len > buflen) { >> + len = buflen; >> } >> + qfw_cfg_get(fw_cfg, sel, data, len); >> } >> - g_free(filesbuf); >> + >> return filesize; >> } ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v4 2/2] tests/qtest/libqos: add DMA support for writing and reading fw_cfg files 2025-01-09 7:49 [PATCH v4 0/2] tests/qtest/libqos: add DMA support for writing and reading fw_cfg files Ani Sinha 2025-01-09 7:49 ` [PATCH v4 1/2] libqos/fw_cfg: refactor file directory iteraton to make it more reusable Ani Sinha @ 2025-01-09 7:49 ` Ani Sinha 2025-01-09 20:30 ` Fabiano Rosas 1 sibling, 1 reply; 9+ messages in thread From: Ani Sinha @ 2025-01-09 7:49 UTC (permalink / raw) To: Philippe Mathieu-Daudé, Gerd Hoffmann, Fabiano Rosas, Laurent Vivier, Paolo Bonzini Cc: berrange, armbru, Ani Sinha, qemu-devel At present, the libqos/fw_cfg.c library does not support the modern DMA interface which is required to write to the fw_cfg files. It only uses the IO interface. Implement read and write methods based on DMA. This will enable developers to write tests that writes to the fw_cfg file(s). The structure of the code is taken from edk2 fw_cfg implementation. It has been tested by writing a qtest that writes to a fw_cfg file. This test will be part of a future patch series. Signed-off-by: Ani Sinha <anisinha@redhat.com> --- tests/qtest/libqos/fw_cfg.c | 138 ++++++++++++++++++++++++++++++++++++ tests/qtest/libqos/fw_cfg.h | 6 +- 2 files changed, 143 insertions(+), 1 deletion(-) diff --git a/tests/qtest/libqos/fw_cfg.c b/tests/qtest/libqos/fw_cfg.c index f1ed4898f7..b3ae97d32d 100644 --- a/tests/qtest/libqos/fw_cfg.c +++ b/tests/qtest/libqos/fw_cfg.c @@ -14,6 +14,8 @@ #include "qemu/osdep.h" #include "fw_cfg.h" +#include "malloc-pc.h" +#include "libqos-malloc.h" #include "../libqtest.h" #include "qemu/bswap.h" #include "hw/nvram/fw_cfg.h" @@ -60,6 +62,65 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) qtest_writew(fw_cfg->qts, fw_cfg->base, key); } +static void +qfw_cfg_dma_transfer(QFWCFG *fw_cfg, QOSState *qs, void *address, + uint32_t length, uint32_t control) +{ + FWCfgDmaAccess access; + uint32_t addr; + uint64_t guest_access_addr; + uint64_t gaddr; + + /* create a data buffer in guest memory */ + gaddr = guest_alloc(&qs->alloc, length); + g_assert(gaddr); + + if (control & FW_CFG_DMA_CTL_WRITE) { + qtest_bufwrite(fw_cfg->qts, gaddr, address, length); + } + access.address = cpu_to_be64(gaddr); + access.length = cpu_to_be32(length); + access.control = cpu_to_be32(control); + + /* now create a separate buffer in guest memory for 'access' */ + guest_access_addr = guest_alloc(&qs->alloc, sizeof(access)); + g_assert(guest_access_addr); + qtest_bufwrite(fw_cfg->qts, guest_access_addr, &access, sizeof(access)); + + /* write lower 32 bits of address */ + addr = cpu_to_be32((uint32_t)(uintptr_t)guest_access_addr); + qtest_outl(fw_cfg->qts, fw_cfg->base + 8, addr); + + /* write upper 32 bits of address */ + addr = cpu_to_be32((uint32_t)(uintptr_t)(guest_access_addr >> 32)); + qtest_outl(fw_cfg->qts, fw_cfg->base + 4, addr); + + g_assert(!(be32_to_cpu(access.control) & FW_CFG_DMA_CTL_ERROR)); + + if (control & FW_CFG_DMA_CTL_READ) { + qtest_bufread(fw_cfg->qts, gaddr, address, length); + } + + guest_free(&qs->alloc, guest_access_addr); + guest_free(&qs->alloc, gaddr); +} + +static void +qfw_cfg_write_entry(QFWCFG *fw_cfg, QOSState *qs, uint16_t key, + void *buf, uint32_t len) +{ + qfw_cfg_select(fw_cfg, key); + qfw_cfg_dma_transfer(fw_cfg, qs, buf, len, FW_CFG_DMA_CTL_WRITE); +} + +static void +qfw_cfg_read_entry(QFWCFG *fw_cfg, QOSState *qs, uint16_t key, + void *buf, uint32_t len) +{ + qfw_cfg_select(fw_cfg, key); + qfw_cfg_dma_transfer(fw_cfg, qs, buf, len, FW_CFG_DMA_CTL_READ); +} + static bool find_pdir_entry(QFWCFG *fw_cfg, const char *filename, uint16_t *sel, uint32_t *size) @@ -123,6 +184,83 @@ size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename, return filesize; } +/* + * The caller need check the return value. When the return value is + * nonzero, it means that some bytes have been transferred. + * + * If the fw_cfg file in question is smaller than the allocated & passed-in + * buffer, then the first len bytes were read. + * + * If the fw_cfg file in question is larger than the passed-in + * buffer, then the return value explains how much was actually read. + * + * It is illegal to call this function if fw_cfg does not support DMA + * interface. The caller should ensure that DMA is supported before + * calling this function. + * + * Passed QOSState pointer qs must be initialized. qs->alloc must also be + * properly initialized. + */ +size_t qfw_cfg_read_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, + void *data, size_t buflen) +{ + uint32_t len = 0; + uint16_t sel; + uint32_t id; + + g_assert(qs); + /* check if DMA is supported since we use DMA for read */ + id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID); + g_assert(id & FW_CFG_VERSION_DMA); + + if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { + if (len > buflen) { + len = buflen; + } + qfw_cfg_read_entry(fw_cfg, qs, sel, data, len); + } + + return len; +} + +/* + * The caller need check the return value. When the return value is + * nonzero, it means that some bytes have been transferred. + * + * If the fw_cfg file in question is smaller than the allocated & passed-in + * buffer, then the buffer has been partially written. + * + * If the fw_cfg file in question is larger than the passed-in + * buffer, then the return value explains how much was actually written. + * + * It is illegal to call this function if fw_cfg does not support DMA + * interface. The caller should ensure that DMA is supported before + * calling this function. + * + * Passed QOSState pointer qs must be initialized. qs->alloc must also be + * properly initialized. + */ +size_t qfw_cfg_write_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, + void *data, size_t buflen) +{ + uint32_t len = 0; + uint16_t sel; + uint32_t id; + + g_assert(qs); + /* write operation is only valid if DMA is supported */ + id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID); + g_assert(id & FW_CFG_VERSION_DMA); + + if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { + if (len > buflen) { + len = buflen; + } + qfw_cfg_write_entry(fw_cfg, qs, sel, data, len); + } + return len; +} + static void mm_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len) { uint8_t *ptr = data; diff --git a/tests/qtest/libqos/fw_cfg.h b/tests/qtest/libqos/fw_cfg.h index b0456a15df..6d6ff09725 100644 --- a/tests/qtest/libqos/fw_cfg.h +++ b/tests/qtest/libqos/fw_cfg.h @@ -14,6 +14,7 @@ #define LIBQOS_FW_CFG_H #include "../libqtest.h" +#include "libqos.h" typedef struct QFWCFG QFWCFG; @@ -33,7 +34,10 @@ uint32_t qfw_cfg_get_u32(QFWCFG *fw_cfg, uint16_t key); uint64_t qfw_cfg_get_u64(QFWCFG *fw_cfg, uint16_t key); size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename, void *data, size_t buflen); - +size_t qfw_cfg_write_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, + void *data, size_t buflen); +size_t qfw_cfg_read_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, + void *data, size_t buflen); QFWCFG *mm_fw_cfg_init(QTestState *qts, uint64_t base); void mm_fw_cfg_uninit(QFWCFG *fw_cfg); QFWCFG *io_fw_cfg_init(QTestState *qts, uint16_t base); -- 2.45.2 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v4 2/2] tests/qtest/libqos: add DMA support for writing and reading fw_cfg files 2025-01-09 7:49 ` [PATCH v4 2/2] tests/qtest/libqos: add DMA support for writing and reading fw_cfg files Ani Sinha @ 2025-01-09 20:30 ` Fabiano Rosas 2025-01-10 5:23 ` Ani Sinha 2025-01-10 6:43 ` Philippe Mathieu-Daudé 0 siblings, 2 replies; 9+ messages in thread From: Fabiano Rosas @ 2025-01-09 20:30 UTC (permalink / raw) To: Ani Sinha, Philippe Mathieu-Daudé, Gerd Hoffmann, Laurent Vivier, Paolo Bonzini Cc: berrange, armbru, Ani Sinha, qemu-devel Ani Sinha <anisinha@redhat.com> writes: > At present, the libqos/fw_cfg.c library does not support the modern DMA > interface which is required to write to the fw_cfg files. It only uses the IO > interface. Implement read and write methods based on DMA. This will enable > developers to write tests that writes to the fw_cfg file(s). The structure of > the code is taken from edk2 fw_cfg implementation. It has been tested by > writing a qtest that writes to a fw_cfg file. This test will be part of a > future patch series. What's the blocker for the rest of the series? It would be preferable to merge it all together, rather than this going in first without any users. > > Signed-off-by: Ani Sinha <anisinha@redhat.com> > --- > tests/qtest/libqos/fw_cfg.c | 138 ++++++++++++++++++++++++++++++++++++ > tests/qtest/libqos/fw_cfg.h | 6 +- > 2 files changed, 143 insertions(+), 1 deletion(-) > > diff --git a/tests/qtest/libqos/fw_cfg.c b/tests/qtest/libqos/fw_cfg.c > index f1ed4898f7..b3ae97d32d 100644 > --- a/tests/qtest/libqos/fw_cfg.c > +++ b/tests/qtest/libqos/fw_cfg.c > @@ -14,6 +14,8 @@ > > #include "qemu/osdep.h" > #include "fw_cfg.h" > +#include "malloc-pc.h" > +#include "libqos-malloc.h" > #include "../libqtest.h" > #include "qemu/bswap.h" > #include "hw/nvram/fw_cfg.h" > @@ -60,6 +62,65 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) > qtest_writew(fw_cfg->qts, fw_cfg->base, key); > } > > +static void > +qfw_cfg_dma_transfer(QFWCFG *fw_cfg, QOSState *qs, void *address, > + uint32_t length, uint32_t control) > +{ > + FWCfgDmaAccess access; > + uint32_t addr; > + uint64_t guest_access_addr; > + uint64_t gaddr; > + > + /* create a data buffer in guest memory */ > + gaddr = guest_alloc(&qs->alloc, length); > + g_assert(gaddr); Same here, none of these asserts are needed. > + > + if (control & FW_CFG_DMA_CTL_WRITE) { > + qtest_bufwrite(fw_cfg->qts, gaddr, address, length); > + } > + access.address = cpu_to_be64(gaddr); > + access.length = cpu_to_be32(length); > + access.control = cpu_to_be32(control); > + > + /* now create a separate buffer in guest memory for 'access' */ > + guest_access_addr = guest_alloc(&qs->alloc, sizeof(access)); > + g_assert(guest_access_addr); > + qtest_bufwrite(fw_cfg->qts, guest_access_addr, &access, sizeof(access)); > + > + /* write lower 32 bits of address */ > + addr = cpu_to_be32((uint32_t)(uintptr_t)guest_access_addr); > + qtest_outl(fw_cfg->qts, fw_cfg->base + 8, addr); > + > + /* write upper 32 bits of address */ > + addr = cpu_to_be32((uint32_t)(uintptr_t)(guest_access_addr >> 32)); > + qtest_outl(fw_cfg->qts, fw_cfg->base + 4, addr); > + > + g_assert(!(be32_to_cpu(access.control) & FW_CFG_DMA_CTL_ERROR)); > + > + if (control & FW_CFG_DMA_CTL_READ) { > + qtest_bufread(fw_cfg->qts, gaddr, address, length); > + } > + > + guest_free(&qs->alloc, guest_access_addr); > + guest_free(&qs->alloc, gaddr); > +} > + > +static void > +qfw_cfg_write_entry(QFWCFG *fw_cfg, QOSState *qs, uint16_t key, > + void *buf, uint32_t len) > +{ > + qfw_cfg_select(fw_cfg, key); > + qfw_cfg_dma_transfer(fw_cfg, qs, buf, len, FW_CFG_DMA_CTL_WRITE); > +} > + > +static void > +qfw_cfg_read_entry(QFWCFG *fw_cfg, QOSState *qs, uint16_t key, > + void *buf, uint32_t len) > +{ > + qfw_cfg_select(fw_cfg, key); > + qfw_cfg_dma_transfer(fw_cfg, qs, buf, len, FW_CFG_DMA_CTL_READ); > +} > + > static bool > find_pdir_entry(QFWCFG *fw_cfg, const char *filename, > uint16_t *sel, uint32_t *size) > @@ -123,6 +184,83 @@ size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename, > return filesize; > } > > +/* > + * The caller need check the return value. When the return value is > + * nonzero, it means that some bytes have been transferred. > + * > + * If the fw_cfg file in question is smaller than the allocated & passed-in > + * buffer, then the first len bytes were read. > + * > + * If the fw_cfg file in question is larger than the passed-in > + * buffer, then the return value explains how much was actually read. > + * > + * It is illegal to call this function if fw_cfg does not support DMA > + * interface. The caller should ensure that DMA is supported before > + * calling this function. > + * > + * Passed QOSState pointer qs must be initialized. qs->alloc must also be > + * properly initialized. > + */ > +size_t qfw_cfg_read_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, > + void *data, size_t buflen) > +{ > + uint32_t len = 0; > + uint16_t sel; > + uint32_t id; > + > + g_assert(qs); > + /* check if DMA is supported since we use DMA for read */ > + id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID); > + g_assert(id & FW_CFG_VERSION_DMA); > + > + if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { > + if (len > buflen) { > + len = buflen; > + } > + qfw_cfg_read_entry(fw_cfg, qs, sel, data, len); > + } > + > + return len; > +} > + > +/* > + * The caller need check the return value. When the return value is > + * nonzero, it means that some bytes have been transferred. > + * > + * If the fw_cfg file in question is smaller than the allocated & passed-in > + * buffer, then the buffer has been partially written. > + * > + * If the fw_cfg file in question is larger than the passed-in > + * buffer, then the return value explains how much was actually written. > + * > + * It is illegal to call this function if fw_cfg does not support DMA > + * interface. The caller should ensure that DMA is supported before > + * calling this function. > + * > + * Passed QOSState pointer qs must be initialized. qs->alloc must also be > + * properly initialized. > + */ > +size_t qfw_cfg_write_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, > + void *data, size_t buflen) > +{ > + uint32_t len = 0; > + uint16_t sel; > + uint32_t id; > + > + g_assert(qs); > + /* write operation is only valid if DMA is supported */ > + id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID); > + g_assert(id & FW_CFG_VERSION_DMA); > + > + if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { > + if (len > buflen) { > + len = buflen; > + } > + qfw_cfg_write_entry(fw_cfg, qs, sel, data, len); > + } > + return len; > +} > + > static void mm_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len) > { > uint8_t *ptr = data; > diff --git a/tests/qtest/libqos/fw_cfg.h b/tests/qtest/libqos/fw_cfg.h > index b0456a15df..6d6ff09725 100644 > --- a/tests/qtest/libqos/fw_cfg.h > +++ b/tests/qtest/libqos/fw_cfg.h > @@ -14,6 +14,7 @@ > #define LIBQOS_FW_CFG_H > > #include "../libqtest.h" > +#include "libqos.h" > > typedef struct QFWCFG QFWCFG; > > @@ -33,7 +34,10 @@ uint32_t qfw_cfg_get_u32(QFWCFG *fw_cfg, uint16_t key); > uint64_t qfw_cfg_get_u64(QFWCFG *fw_cfg, uint16_t key); > size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename, > void *data, size_t buflen); > - > +size_t qfw_cfg_write_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, > + void *data, size_t buflen); > +size_t qfw_cfg_read_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, > + void *data, size_t buflen); > QFWCFG *mm_fw_cfg_init(QTestState *qts, uint64_t base); > void mm_fw_cfg_uninit(QFWCFG *fw_cfg); > QFWCFG *io_fw_cfg_init(QTestState *qts, uint16_t base); ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 2/2] tests/qtest/libqos: add DMA support for writing and reading fw_cfg files 2025-01-09 20:30 ` Fabiano Rosas @ 2025-01-10 5:23 ` Ani Sinha 2025-01-10 10:12 ` Gerd Hoffmann 2025-01-10 6:43 ` Philippe Mathieu-Daudé 1 sibling, 1 reply; 9+ messages in thread From: Ani Sinha @ 2025-01-10 5:23 UTC (permalink / raw) To: Fabiano Rosas Cc: Philippe Mathieu-Daudé, Gerd Hoffmann, Laurent Vivier, Paolo Bonzini, Daniel Berrange, Markus Armbruster, qemu-devel > On 10 Jan 2025, at 2:00 AM, Fabiano Rosas <farosas@suse.de> wrote: > > Ani Sinha <anisinha@redhat.com> writes: > >> At present, the libqos/fw_cfg.c library does not support the modern DMA >> interface which is required to write to the fw_cfg files. It only uses the IO >> interface. Implement read and write methods based on DMA. This will enable >> developers to write tests that writes to the fw_cfg file(s). The structure of >> the code is taken from edk2 fw_cfg implementation. It has been tested by >> writing a qtest that writes to a fw_cfg file. This test will be part of a >> future patch series. > > What's the blocker for the rest of the series? The broker is that the consumer of this api is a new qtest which is written to test a brand new feature. I would rather post that patchset separately. A compromise is ... > It would be preferable to > merge it all together, rather than this going in first without any > users. In my cover letter I have pointed to the patch series that actually uses this api. That way those who want to check out that branch and test these changes can do so. > >> >> Signed-off-by: Ani Sinha <anisinha@redhat.com> >> --- >> tests/qtest/libqos/fw_cfg.c | 138 ++++++++++++++++++++++++++++++++++++ >> tests/qtest/libqos/fw_cfg.h | 6 +- >> 2 files changed, 143 insertions(+), 1 deletion(-) >> >> diff --git a/tests/qtest/libqos/fw_cfg.c b/tests/qtest/libqos/fw_cfg.c >> index f1ed4898f7..b3ae97d32d 100644 >> --- a/tests/qtest/libqos/fw_cfg.c >> +++ b/tests/qtest/libqos/fw_cfg.c >> @@ -14,6 +14,8 @@ >> >> #include "qemu/osdep.h" >> #include "fw_cfg.h" >> +#include "malloc-pc.h" >> +#include "libqos-malloc.h" >> #include "../libqtest.h" >> #include "qemu/bswap.h" >> #include "hw/nvram/fw_cfg.h" >> @@ -60,6 +62,65 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) >> qtest_writew(fw_cfg->qts, fw_cfg->base, key); >> } >> >> +static void >> +qfw_cfg_dma_transfer(QFWCFG *fw_cfg, QOSState *qs, void *address, >> + uint32_t length, uint32_t control) >> +{ >> + FWCfgDmaAccess access; >> + uint32_t addr; >> + uint64_t guest_access_addr; >> + uint64_t gaddr; >> + >> + /* create a data buffer in guest memory */ >> + gaddr = guest_alloc(&qs->alloc, length); >> + g_assert(gaddr); > > Same here, none of these asserts are needed. Ok will remove. > >> + >> + if (control & FW_CFG_DMA_CTL_WRITE) { >> + qtest_bufwrite(fw_cfg->qts, gaddr, address, length); >> + } >> + access.address = cpu_to_be64(gaddr); >> + access.length = cpu_to_be32(length); >> + access.control = cpu_to_be32(control); >> + >> + /* now create a separate buffer in guest memory for 'access' */ >> + guest_access_addr = guest_alloc(&qs->alloc, sizeof(access)); >> + g_assert(guest_access_addr); >> + qtest_bufwrite(fw_cfg->qts, guest_access_addr, &access, sizeof(access)); >> + >> + /* write lower 32 bits of address */ >> + addr = cpu_to_be32((uint32_t)(uintptr_t)guest_access_addr); >> + qtest_outl(fw_cfg->qts, fw_cfg->base + 8, addr); >> + >> + /* write upper 32 bits of address */ >> + addr = cpu_to_be32((uint32_t)(uintptr_t)(guest_access_addr >> 32)); >> + qtest_outl(fw_cfg->qts, fw_cfg->base + 4, addr); >> + >> + g_assert(!(be32_to_cpu(access.control) & FW_CFG_DMA_CTL_ERROR)); >> + >> + if (control & FW_CFG_DMA_CTL_READ) { >> + qtest_bufread(fw_cfg->qts, gaddr, address, length); >> + } >> + >> + guest_free(&qs->alloc, guest_access_addr); >> + guest_free(&qs->alloc, gaddr); >> +} >> + >> +static void >> +qfw_cfg_write_entry(QFWCFG *fw_cfg, QOSState *qs, uint16_t key, >> + void *buf, uint32_t len) >> +{ >> + qfw_cfg_select(fw_cfg, key); >> + qfw_cfg_dma_transfer(fw_cfg, qs, buf, len, FW_CFG_DMA_CTL_WRITE); >> +} >> + >> +static void >> +qfw_cfg_read_entry(QFWCFG *fw_cfg, QOSState *qs, uint16_t key, >> + void *buf, uint32_t len) >> +{ >> + qfw_cfg_select(fw_cfg, key); >> + qfw_cfg_dma_transfer(fw_cfg, qs, buf, len, FW_CFG_DMA_CTL_READ); >> +} >> + >> static bool >> find_pdir_entry(QFWCFG *fw_cfg, const char *filename, >> uint16_t *sel, uint32_t *size) >> @@ -123,6 +184,83 @@ size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename, >> return filesize; >> } >> >> +/* >> + * The caller need check the return value. When the return value is >> + * nonzero, it means that some bytes have been transferred. >> + * >> + * If the fw_cfg file in question is smaller than the allocated & passed-in >> + * buffer, then the first len bytes were read. >> + * >> + * If the fw_cfg file in question is larger than the passed-in >> + * buffer, then the return value explains how much was actually read. >> + * >> + * It is illegal to call this function if fw_cfg does not support DMA >> + * interface. The caller should ensure that DMA is supported before >> + * calling this function. >> + * >> + * Passed QOSState pointer qs must be initialized. qs->alloc must also be >> + * properly initialized. >> + */ >> +size_t qfw_cfg_read_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, >> + void *data, size_t buflen) >> +{ >> + uint32_t len = 0; >> + uint16_t sel; >> + uint32_t id; >> + >> + g_assert(qs); >> + /* check if DMA is supported since we use DMA for read */ >> + id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID); >> + g_assert(id & FW_CFG_VERSION_DMA); >> + >> + if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { >> + if (len > buflen) { >> + len = buflen; >> + } >> + qfw_cfg_read_entry(fw_cfg, qs, sel, data, len); >> + } >> + >> + return len; >> +} >> + >> +/* >> + * The caller need check the return value. When the return value is >> + * nonzero, it means that some bytes have been transferred. >> + * >> + * If the fw_cfg file in question is smaller than the allocated & passed-in >> + * buffer, then the buffer has been partially written. >> + * >> + * If the fw_cfg file in question is larger than the passed-in >> + * buffer, then the return value explains how much was actually written. >> + * >> + * It is illegal to call this function if fw_cfg does not support DMA >> + * interface. The caller should ensure that DMA is supported before >> + * calling this function. >> + * >> + * Passed QOSState pointer qs must be initialized. qs->alloc must also be >> + * properly initialized. >> + */ >> +size_t qfw_cfg_write_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, >> + void *data, size_t buflen) >> +{ >> + uint32_t len = 0; >> + uint16_t sel; >> + uint32_t id; >> + >> + g_assert(qs); >> + /* write operation is only valid if DMA is supported */ >> + id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID); >> + g_assert(id & FW_CFG_VERSION_DMA); >> + >> + if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { >> + if (len > buflen) { >> + len = buflen; >> + } >> + qfw_cfg_write_entry(fw_cfg, qs, sel, data, len); >> + } >> + return len; >> +} >> + >> static void mm_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len) >> { >> uint8_t *ptr = data; >> diff --git a/tests/qtest/libqos/fw_cfg.h b/tests/qtest/libqos/fw_cfg.h >> index b0456a15df..6d6ff09725 100644 >> --- a/tests/qtest/libqos/fw_cfg.h >> +++ b/tests/qtest/libqos/fw_cfg.h >> @@ -14,6 +14,7 @@ >> #define LIBQOS_FW_CFG_H >> >> #include "../libqtest.h" >> +#include "libqos.h" >> >> typedef struct QFWCFG QFWCFG; >> >> @@ -33,7 +34,10 @@ uint32_t qfw_cfg_get_u32(QFWCFG *fw_cfg, uint16_t key); >> uint64_t qfw_cfg_get_u64(QFWCFG *fw_cfg, uint16_t key); >> size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename, >> void *data, size_t buflen); >> - >> +size_t qfw_cfg_write_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, >> + void *data, size_t buflen); >> +size_t qfw_cfg_read_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, >> + void *data, size_t buflen); >> QFWCFG *mm_fw_cfg_init(QTestState *qts, uint64_t base); >> void mm_fw_cfg_uninit(QFWCFG *fw_cfg); >> QFWCFG *io_fw_cfg_init(QTestState *qts, uint16_t base); ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 2/2] tests/qtest/libqos: add DMA support for writing and reading fw_cfg files 2025-01-10 5:23 ` Ani Sinha @ 2025-01-10 10:12 ` Gerd Hoffmann 0 siblings, 0 replies; 9+ messages in thread From: Gerd Hoffmann @ 2025-01-10 10:12 UTC (permalink / raw) To: Ani Sinha Cc: Fabiano Rosas, Philippe Mathieu-Daudé, Laurent Vivier, Paolo Bonzini, Daniel Berrange, Markus Armbruster, qemu-devel On Fri, Jan 10, 2025 at 10:53:32AM +0530, Ani Sinha wrote: > > > > On 10 Jan 2025, at 2:00 AM, Fabiano Rosas <farosas@suse.de> wrote: > > > > Ani Sinha <anisinha@redhat.com> writes: > > > >> At present, the libqos/fw_cfg.c library does not support the modern DMA > >> interface which is required to write to the fw_cfg files. It only uses the IO > >> interface. Implement read and write methods based on DMA. This will enable > >> developers to write tests that writes to the fw_cfg file(s). The structure of > >> the code is taken from edk2 fw_cfg implementation. It has been tested by > >> writing a qtest that writes to a fw_cfg file. This test will be part of a > >> future patch series. > > > > What's the blocker for the rest of the series? > > The broker is that the consumer of this api is a new qtest which is written to test a brand new feature. A in-tree user of fw_cfg file writes is ramfb, so writing a ramfb test would be an option ... take care, Gerd ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v4 2/2] tests/qtest/libqos: add DMA support for writing and reading fw_cfg files 2025-01-09 20:30 ` Fabiano Rosas 2025-01-10 5:23 ` Ani Sinha @ 2025-01-10 6:43 ` Philippe Mathieu-Daudé 1 sibling, 0 replies; 9+ messages in thread From: Philippe Mathieu-Daudé @ 2025-01-10 6:43 UTC (permalink / raw) To: Fabiano Rosas, Ani Sinha, Gerd Hoffmann, Laurent Vivier, Paolo Bonzini Cc: berrange, armbru, qemu-devel On 9/1/25 21:30, Fabiano Rosas wrote: > Ani Sinha <anisinha@redhat.com> writes: > >> At present, the libqos/fw_cfg.c library does not support the modern DMA >> interface which is required to write to the fw_cfg files. It only uses the IO >> interface. Implement read and write methods based on DMA. This will enable >> developers to write tests that writes to the fw_cfg file(s). The structure of >> the code is taken from edk2 fw_cfg implementation. It has been tested by >> writing a qtest that writes to a fw_cfg file. This test will be part of a >> future patch series. > > What's the blocker for the rest of the series? It would be preferable to > merge it all together, rather than this going in first without any > users. Agreed. We could use a multiboot image, or a guest with vmcoreinfo support. Or a dumbier test just testing this API. >> >> Signed-off-by: Ani Sinha <anisinha@redhat.com> >> --- >> tests/qtest/libqos/fw_cfg.c | 138 ++++++++++++++++++++++++++++++++++++ >> tests/qtest/libqos/fw_cfg.h | 6 +- >> 2 files changed, 143 insertions(+), 1 deletion(-) ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2025-01-10 10:12 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-01-09 7:49 [PATCH v4 0/2] tests/qtest/libqos: add DMA support for writing and reading fw_cfg files Ani Sinha 2025-01-09 7:49 ` [PATCH v4 1/2] libqos/fw_cfg: refactor file directory iteraton to make it more reusable Ani Sinha 2025-01-09 20:01 ` Fabiano Rosas 2025-01-10 3:43 ` Ani Sinha 2025-01-09 7:49 ` [PATCH v4 2/2] tests/qtest/libqos: add DMA support for writing and reading fw_cfg files Ani Sinha 2025-01-09 20:30 ` Fabiano Rosas 2025-01-10 5:23 ` Ani Sinha 2025-01-10 10:12 ` Gerd Hoffmann 2025-01-10 6:43 ` Philippe Mathieu-Daudé
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.