diff mbox series

hw/core/loader: allow loading larger ROMs

Message ID 20240627020207.630125-1-gregorhaas1997@gmail.com
State New
Headers show
Series hw/core/loader: allow loading larger ROMs | expand

Commit Message

Gregor Haas June 27, 2024, 2:02 a.m. UTC
The read() syscall is not guaranteed to return all data from a file. The
default ROM loader implementation currently does not take this into account,
instead failing if all bytes are not read at once. This change wraps the
read() syscall in a do/while loop to ensure all bytes of the ROM are read.

Signed-off-by: Gregor Haas <gregorhaas1997@gmail.com>
---
 hw/core/loader.c | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

Comments

Gregor Haas June 27, 2024, 5:35 p.m. UTC | #1
Hi Xingtao,

> Can you reproduce this issue?
Absolutely! I encountered this when trying to load an OpenSBI payload
firmware using the bios option for the QEMU RISC-V virt board. These
payload firmwares bundle the entire next boot stage, which in my case is a
build of the Linux kernel (which is a standard configuration, supported by
tools such as Buildroot [1]). My kernel (configured with the default 64-bit
RISC-V configuration) comes in at 9.8M, which is copied into the OpenSBI
firmware of final size 10M. Then, I run the following QEMU command:

qemu-system-riscv64 -machine virt -m 4G -nographic -bios fw_payload.bin

and get the following output:

rom: file fw_payload.bin: read error: rc=2147479552 (expected 2303760392)
qemu-system-riscv64: could not load firmware 'fw_payload.bin'

This is from my development machine, running Arch Linux with kernel 6.9.6
and root filesystem ZFS 2.2.4. Please let me know if you'd like me to make
a minimal reproducer for this, or if you need any more information.

Thanks,
Gregor

[1]
https://github.com/buildroot/buildroot/blob/master/boot/opensbi/Config.in#L95


On Wed, Jun 26, 2024 at 11:11 PM Xingtao Yao (Fujitsu) <
yaoxt.fnst@fujitsu.com> wrote:

> Hi, Gregor
> >
> > The read() syscall is not guaranteed to return all data from a file. The
> > default ROM loader implementation currently does not take this into
> account,
> > instead failing if all bytes are not read at once. This change wraps the
> > read() syscall in a do/while loop to ensure all bytes of the ROM are
> read.
> Can you reproduce this issue?
>
> Thanks
> Xingtao
>
Gregor Haas June 28, 2024, 12:53 a.m. UTC | #2
Hi Xingtao,

Thank you for reproducing this -- I agree with your conclusion and will
send a v2 patchset momentarily.

Thank you,
Gregor

On Thu, Jun 27, 2024 at 5:44 PM Xingtao Yao (Fujitsu) <
yaoxt.fnst@fujitsu.com> wrote:

> Hi, Gregor
>
>
>
> >rom: file fw_payload.bin: read error: rc=2147479552 (expected 2303760392)
> >qemu-system-riscv64: could not load firmware 'fw_payload.bin'
>
> Thanks, I was able to reproduce the problem when the images size is
> larger than 2147479552.
>
>
>
> I found that in my test environment, the maximum value returned by a read
> operation is 2147479552,
>
> which was affected by the operating system.
>
>
>
> We can find this limitation in the man page:
>
> NOTES
>
>        The types size_t and ssize_t are, respectively, unsigned and
> signed integer data types specified by POSIX.1.
>
>
>
>        On Linux, read() (and similar system calls) will transfer at most
> 0x7ffff000 (2,147,479,552) bytes, returning the number of bytes actually
> transferred.  (This is true on both
>
>        32-bit and 64-bit systems.)
>
>
>
>
>
> > +    do {
>
> > +        rc = read(fd, &rom->data[sz], rom->datasize);
>
> > +        if (rc == -1) {
>
> > +                fprintf(stderr, "rom: file %-20s: read error: %s\n",
>
> > +                        rom->name, strerror(errno));
>
> > +                goto err;
>
> > +        }
>
> > +        sz += rc;
>
> > +    } while (sz != rom->datasize);
>
> I think we can use load_image_size() instead.
>
>
>
>
>
>
>
>
>
> *From:* Gregor Haas <gregorhaas1997@gmail.com>
> *Sent:* Friday, June 28, 2024 1:35 AM
> *To:* Yao, Xingtao/姚 幸涛 <yaoxt.fnst@fujitsu.com>
> *Cc:* qemu-devel@nongnu.org; philmd@linaro.org;
> richard.henderson@linaro.org
> *Subject:* Re: [PATCH] hw/core/loader: allow loading larger ROMs
>
>
>
> Hi Xingtao,
>
> > Can you reproduce this issue?
> Absolutely! I encountered this when trying to load an OpenSBI payload
> firmware using the bios option for the QEMU RISC-V virt board. These
> payload firmwares bundle the entire next boot stage, which in my case is a
> build of the Linux kernel (which is a standard configuration, supported by
> tools such as Buildroot [1]). My kernel (configured with the default 64-bit
> RISC-V configuration) comes in at 9.8M, which is copied into the OpenSBI
> firmware of final size 10M. Then, I run the following QEMU command:
>
> qemu-system-riscv64 -machine virt -m 4G -nographic -bios fw_payload.bin
>
> and get the following output:
>
> rom: file fw_payload.bin: read error: rc=2147479552 (expected 2303760392)
> qemu-system-riscv64: could not load firmware 'fw_payload.bin'
>
> This is from my development machine, running Arch Linux with kernel 6.9.6
> and root filesystem ZFS 2.2.4. Please let me know if you'd like me to make
> a minimal reproducer for this, or if you need any more information.
>
> Thanks,
> Gregor
>
> [1]
> https://github.com/buildroot/buildroot/blob/master/boot/opensbi/Config.in#L95
>
>
>
> On Wed, Jun 26, 2024 at 11:11 PM Xingtao Yao (Fujitsu) <
> yaoxt.fnst@fujitsu.com> wrote:
>
> Hi, Gregor
> >
> > The read() syscall is not guaranteed to return all data from a file. The
> > default ROM loader implementation currently does not take this into
> account,
> > instead failing if all bytes are not read at once. This change wraps the
> > read() syscall in a do/while loop to ensure all bytes of the ROM are
> read.
> Can you reproduce this issue?
>
> Thanks
> Xingtao
>
>
diff mbox series

Patch

diff --git a/hw/core/loader.c b/hw/core/loader.c
index 2f8105d7de..afa26dccb1 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -1075,7 +1075,7 @@  ssize_t rom_add_file(const char *file, const char *fw_dir,
 {
     MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
     Rom *rom;
-    ssize_t rc;
+    ssize_t rc, sz = 0;
     int fd = -1;
     char devpath[100];
 
@@ -1116,12 +1116,15 @@  ssize_t rom_add_file(const char *file, const char *fw_dir,
     rom->datasize = rom->romsize;
     rom->data     = g_malloc0(rom->datasize);
     lseek(fd, 0, SEEK_SET);
-    rc = read(fd, rom->data, rom->datasize);
-    if (rc != rom->datasize) {
-        fprintf(stderr, "rom: file %-20s: read error: rc=%zd (expected %zd)\n",
-                rom->name, rc, rom->datasize);
-        goto err;
-    }
+    do {
+        rc = read(fd, &rom->data[sz], rom->datasize);
+        if (rc == -1) {
+                fprintf(stderr, "rom: file %-20s: read error: %s\n",
+                        rom->name, strerror(errno));
+                goto err;
+        }
+        sz += rc;
+    } while (sz != rom->datasize);
     close(fd);
     rom_insert(rom);
     if (rom->fw_file && fw_cfg) {