diff mbox series

[PULL,13/18] hw/core: implement a guest-loader to support static hypervisor guests

Message ID 20210308135104.24903-14-alex.bennee@linaro.org
State Superseded
Headers show
Series testing, docs, semihosting move and guest-loader | expand

Commit Message

Alex Bennée March 8, 2021, 1:50 p.m. UTC
Hypervisors, especially type-1 ones, need the firmware/bootcode to put
their initial guest somewhere in memory and pass the information to it
via platform data. The guest-loader is modelled after the generic
loader for exactly this sort of purpose:

  $QEMU $ARGS  -kernel ~/xen.git/xen/xen \
    -append "dom0_mem=1G,max:1G loglvl=all guest_loglvl=all" \
    -device guest-loader,addr=0x42000000,kernel=Image,bootargs="root=/dev/sda2 ro console=hvc0 earlyprintk=xen" \
    -device guest-loader,addr=0x47000000,initrd=rootfs.cpio

Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

Reviewed-by: Alistair Francis <alistair.francis@wdc.com>

Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>

Message-Id: <20210303173642.3805-5-alex.bennee@linaro.org>

-- 
2.20.1

Comments

Christian Borntraeger March 15, 2021, 4:16 p.m. UTC | #1
On 08.03.21 14:50, Alex Bennée wrote:
> Hypervisors, especially type-1 ones, need the firmware/bootcode to put

> their initial guest somewhere in memory and pass the information to it

> via platform data. The guest-loader is modelled after the generic

> loader for exactly this sort of purpose:

> 

>    $QEMU $ARGS  -kernel ~/xen.git/xen/xen \

>      -append "dom0_mem=1G,max:1G loglvl=all guest_loglvl=all" \

>      -device guest-loader,addr=0x42000000,kernel=Image,bootargs="root=/dev/sda2 ro console=hvc0 earlyprintk=xen" \

>      -device guest-loader,addr=0x47000000,initrd=rootfs.cpio

> 

> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

> Reviewed-by: Alistair Francis <alistair.francis@wdc.com>

> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>

> Message-Id: <20210303173642.3805-5-alex.bennee@linaro.org>

> 


This now results in

     /usr/bin/ld: libcommon.fa.p/hw_core_guest-loader.c.o: in function `loader_insert_platform_data':
     build/../hw/core/guest-loader.c:56: undefined reference to `qemu_fdt_add_subnode'
     /usr/bin/ld: build/../hw/core/guest-loader.c:57: undefined reference to `qemu_fdt_setprop'
     /usr/bin/ld: build/../hw/core/guest-loader.c:61: undefined reference to `qemu_fdt_setprop_string_array'
     /usr/bin/ld: build/../hw/core/guest-loader.c:68: undefined reference to `qemu_fdt_setprop_string'
     /usr/bin/ld: build/../hw/core/guest-loader.c:74: undefined reference to `qemu_fdt_setprop_string_array'
     collect2: error: ld returned 1 exit status
     ninja: build stopped: subcommand failed.

when building s390-softmmu on s390 with  --disable-fdt, which was in my build script.
Philippe Mathieu-Daudé March 15, 2021, 4:44 p.m. UTC | #2
On 3/15/21 5:16 PM, Christian Borntraeger wrote:
> 

> 

> On 08.03.21 14:50, Alex Bennée wrote:

>> Hypervisors, especially type-1 ones, need the firmware/bootcode to put

>> their initial guest somewhere in memory and pass the information to it

>> via platform data. The guest-loader is modelled after the generic

>> loader for exactly this sort of purpose:

>>

>>    $QEMU $ARGS  -kernel ~/xen.git/xen/xen \

>>      -append "dom0_mem=1G,max:1G loglvl=all guest_loglvl=all" \

>>      -device

>> guest-loader,addr=0x42000000,kernel=Image,bootargs="root=/dev/sda2 ro

>> console=hvc0 earlyprintk=xen" \

>>      -device guest-loader,addr=0x47000000,initrd=rootfs.cpio

>>

>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

>> Reviewed-by: Alistair Francis <alistair.francis@wdc.com>

>> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>

>> Message-Id: <20210303173642.3805-5-alex.bennee@linaro.org>

>>

> 

> This now results in

> 

>     /usr/bin/ld: libcommon.fa.p/hw_core_guest-loader.c.o: in function

> `loader_insert_platform_data':

>     build/../hw/core/guest-loader.c:56: undefined reference to

> `qemu_fdt_add_subnode'

>     /usr/bin/ld: build/../hw/core/guest-loader.c:57: undefined reference

> to `qemu_fdt_setprop'

>     /usr/bin/ld: build/../hw/core/guest-loader.c:61: undefined reference

> to `qemu_fdt_setprop_string_array'

>     /usr/bin/ld: build/../hw/core/guest-loader.c:68: undefined reference

> to `qemu_fdt_setprop_string'

>     /usr/bin/ld: build/../hw/core/guest-loader.c:74: undefined reference

> to `qemu_fdt_setprop_string_array'

>     collect2: error: ld returned 1 exit status

>     ninja: build stopped: subcommand failed.

> 

> when building s390-softmmu on s390 with  --disable-fdt, which was in my

> build script.

>


Oops. Quick fix:

-- >8 --
diff --git a/hw/core/meson.build b/hw/core/meson.build
index 9cd72edf513..5827996206e 100644
--- a/hw/core/meson.build
+++ b/hw/core/meson.build
@@ -37,7 +37,7 @@
   'clock-vmstate.c',
 ))

-softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('guest-loader.c'))
+softmmu_ss.add(when: ['CONFIG_TCG', fdt], if_true: files('guest-loader.c'))

 specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files(
   'machine-qmp-cmds.c',

---

But better is a Kconfig entry to be able to deselect this
device.
Philippe Mathieu-Daudé March 15, 2021, 4:51 p.m. UTC | #3
On 3/15/21 5:44 PM, Philippe Mathieu-Daudé wrote:
> On 3/15/21 5:16 PM, Christian Borntraeger wrote:

>>

>>

>> On 08.03.21 14:50, Alex Bennée wrote:

>>> Hypervisors, especially type-1 ones, need the firmware/bootcode to put

>>> their initial guest somewhere in memory and pass the information to it

>>> via platform data. The guest-loader is modelled after the generic

>>> loader for exactly this sort of purpose:

>>>

>>>    $QEMU $ARGS  -kernel ~/xen.git/xen/xen \

>>>      -append "dom0_mem=1G,max:1G loglvl=all guest_loglvl=all" \

>>>      -device

>>> guest-loader,addr=0x42000000,kernel=Image,bootargs="root=/dev/sda2 ro

>>> console=hvc0 earlyprintk=xen" \

>>>      -device guest-loader,addr=0x47000000,initrd=rootfs.cpio

>>>

>>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

>>> Reviewed-by: Alistair Francis <alistair.francis@wdc.com>

>>> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>

>>> Message-Id: <20210303173642.3805-5-alex.bennee@linaro.org>

>>>

>>

>> This now results in

>>

>>     /usr/bin/ld: libcommon.fa.p/hw_core_guest-loader.c.o: in function

>> `loader_insert_platform_data':

>>     build/../hw/core/guest-loader.c:56: undefined reference to

>> `qemu_fdt_add_subnode'

>>     /usr/bin/ld: build/../hw/core/guest-loader.c:57: undefined reference

>> to `qemu_fdt_setprop'

>>     /usr/bin/ld: build/../hw/core/guest-loader.c:61: undefined reference

>> to `qemu_fdt_setprop_string_array'

>>     /usr/bin/ld: build/../hw/core/guest-loader.c:68: undefined reference

>> to `qemu_fdt_setprop_string'

>>     /usr/bin/ld: build/../hw/core/guest-loader.c:74: undefined reference

>> to `qemu_fdt_setprop_string_array'

>>     collect2: error: ld returned 1 exit status

>>     ninja: build stopped: subcommand failed.

>>

>> when building s390-softmmu on s390 with  --disable-fdt, which was in my

>> build script.

>>

> 

> Oops. Quick fix:

> 

> -- >8 --

> diff --git a/hw/core/meson.build b/hw/core/meson.build

> index 9cd72edf513..5827996206e 100644

> --- a/hw/core/meson.build

> +++ b/hw/core/meson.build

> @@ -37,7 +37,7 @@

>    'clock-vmstate.c',

>  ))

> 

> -softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('guest-loader.c'))

> +softmmu_ss.add(when: ['CONFIG_TCG', fdt], if_true: files('guest-loader.c'))

> 

>  specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files(

>    'machine-qmp-cmds.c',

> 

> ---

> 

> But better is a Kconfig entry to be able to deselect this

> device.


-- >8 --
diff --git a/hw/core/Kconfig b/hw/core/Kconfig
index fdf03514d7d..9397503656d 100644
--- a/hw/core/Kconfig
+++ b/hw/core/Kconfig
@@ -11,6 +11,11 @@ config GENERIC_LOADER
     bool
     default y

+config GUEST_LOADER
+    bool
+    default y
+    depends on TCG
+
 config OR_IRQ
     bool

diff --git a/hw/core/meson.build b/hw/core/meson.build
index 9cd72edf513..59f1605bb07 100644
--- a/hw/core/meson.build
+++ b/hw/core/meson.build
@@ -16,6 +16,7 @@
 common_ss.add(files('cpu.c'))
 common_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c'))
 common_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true:
files('generic-loader.c'))
+common_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true:
files('guest-loader.c'))
 common_ss.add(when: 'CONFIG_OR_IRQ', if_true: files('or-irq.c'))
 common_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true:
files('platform-bus.c'))
 common_ss.add(when: 'CONFIG_PTIMER', if_true: files('ptimer.c'))
@@ -37,8 +38,6 @@
   'clock-vmstate.c',
 ))

-softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('guest-loader.c'))
-
 specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files(
   'machine-qmp-cmds.c',
   'numa.c',
---
Christian Borntraeger March 15, 2021, 4:52 p.m. UTC | #4
On 15.03.21 17:44, Philippe Mathieu-Daudé wrote:
> On 3/15/21 5:16 PM, Christian Borntraeger wrote:

>>

>>

>> On 08.03.21 14:50, Alex Bennée wrote:

>>> Hypervisors, especially type-1 ones, need the firmware/bootcode to put

>>> their initial guest somewhere in memory and pass the information to it

>>> via platform data. The guest-loader is modelled after the generic

>>> loader for exactly this sort of purpose:

>>>

>>>     $QEMU $ARGS  -kernel ~/xen.git/xen/xen \

>>>       -append "dom0_mem=1G,max:1G loglvl=all guest_loglvl=all" \

>>>       -device

>>> guest-loader,addr=0x42000000,kernel=Image,bootargs="root=/dev/sda2 ro

>>> console=hvc0 earlyprintk=xen" \

>>>       -device guest-loader,addr=0x47000000,initrd=rootfs.cpio

>>>

>>> Signed-off-by: Alex Bennée <alex.bennee@linaro.org>

>>> Reviewed-by: Alistair Francis <alistair.francis@wdc.com>

>>> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>

>>> Message-Id: <20210303173642.3805-5-alex.bennee@linaro.org>

>>>

>>

>> This now results in

>>

>>      /usr/bin/ld: libcommon.fa.p/hw_core_guest-loader.c.o: in function

>> `loader_insert_platform_data':

>>      build/../hw/core/guest-loader.c:56: undefined reference to

>> `qemu_fdt_add_subnode'

>>      /usr/bin/ld: build/../hw/core/guest-loader.c:57: undefined reference

>> to `qemu_fdt_setprop'

>>      /usr/bin/ld: build/../hw/core/guest-loader.c:61: undefined reference

>> to `qemu_fdt_setprop_string_array'

>>      /usr/bin/ld: build/../hw/core/guest-loader.c:68: undefined reference

>> to `qemu_fdt_setprop_string'

>>      /usr/bin/ld: build/../hw/core/guest-loader.c:74: undefined reference

>> to `qemu_fdt_setprop_string_array'

>>      collect2: error: ld returned 1 exit status

>>      ninja: build stopped: subcommand failed.

>>

>> when building s390-softmmu on s390 with  --disable-fdt, which was in my

>> build script.

>>

> 

> Oops. Quick fix:

> 

> -- >8 --

> diff --git a/hw/core/meson.build b/hw/core/meson.build

> index 9cd72edf513..5827996206e 100644

> --- a/hw/core/meson.build

> +++ b/hw/core/meson.build

> @@ -37,7 +37,7 @@

>     'clock-vmstate.c',

>   ))

> 

> -softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('guest-loader.c'))

> +softmmu_ss.add(when: ['CONFIG_TCG', fdt], if_true: files('guest-loader.c'))

> 

>   specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files(

>     'machine-qmp-cmds.c',

> 


At least this one is
Tested-by: Christian Borntraeger <borntraeger@de.ibm.com>
Christian Borntraeger March 15, 2021, 4:59 p.m. UTC | #5
On 15.03.21 17:51, Philippe Mathieu-Daudé wrote:

> diff --git a/hw/core/Kconfig b/hw/core/Kconfig

> index fdf03514d7d..9397503656d 100644

> --- a/hw/core/Kconfig

> +++ b/hw/core/Kconfig

> @@ -11,6 +11,11 @@ config GENERIC_LOADER

>       bool

>       default y

> 

> +config GUEST_LOADER

> +    bool

> +    default y

> +    depends on TCG

> +

>   config OR_IRQ

>       bool

> 

> diff --git a/hw/core/meson.build b/hw/core/meson.build

> index 9cd72edf513..59f1605bb07 100644

> --- a/hw/core/meson.build

> +++ b/hw/core/meson.build

> @@ -16,6 +16,7 @@

>   common_ss.add(files('cpu.c'))

>   common_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c'))

>   common_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true:

> files('generic-loader.c'))

> +common_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true:

> files('guest-loader.c'))

>   common_ss.add(when: 'CONFIG_OR_IRQ', if_true: files('or-irq.c'))

>   common_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true:

> files('platform-bus.c'))

>   common_ss.add(when: 'CONFIG_PTIMER', if_true: files('ptimer.c'))

> @@ -37,8 +38,6 @@

>     'clock-vmstate.c',

>   ))

> 

> -softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('guest-loader.c'))

> -

>   specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files(

>     'machine-qmp-cmds.c',

>     'numa.c',



Also
Tested-by: Christian Borntraeger <borntraeger@de.ibm.com>
Philippe Mathieu-Daudé March 15, 2021, 5:05 p.m. UTC | #6
On 3/15/21 5:59 PM, Christian Borntraeger wrote:
> On 15.03.21 17:51, Philippe Mathieu-Daudé wrote:

> 

>> diff --git a/hw/core/Kconfig b/hw/core/Kconfig

>> index fdf03514d7d..9397503656d 100644

>> --- a/hw/core/Kconfig

>> +++ b/hw/core/Kconfig

>> @@ -11,6 +11,11 @@ config GENERIC_LOADER

>>       bool

>>       default y

>>

>> +config GUEST_LOADER

>> +    bool

>> +    default y

>> +    depends on TCG

>> +

>>   config OR_IRQ

>>       bool

>>

>> diff --git a/hw/core/meson.build b/hw/core/meson.build

>> index 9cd72edf513..59f1605bb07 100644

>> --- a/hw/core/meson.build

>> +++ b/hw/core/meson.build

>> @@ -16,6 +16,7 @@

>>   common_ss.add(files('cpu.c'))

>>   common_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c'))

>>   common_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true:

>> files('generic-loader.c'))

>> +common_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true:

>> files('guest-loader.c'))

>>   common_ss.add(when: 'CONFIG_OR_IRQ', if_true: files('or-irq.c'))

>>   common_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true:

>> files('platform-bus.c'))

>>   common_ss.add(when: 'CONFIG_PTIMER', if_true: files('ptimer.c'))

>> @@ -37,8 +38,6 @@

>>     'clock-vmstate.c',

>>   ))

>>

>> -softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('guest-loader.c'))

>> -

>>   specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files(

>>     'machine-qmp-cmds.c',

>>     'numa.c',

> 

> 

> Also

> Tested-by: Christian Borntraeger <borntraeger@de.ibm.com>


Thanks Christian!
Alex Bennée March 15, 2021, 6:01 p.m. UTC | #7
Philippe Mathieu-Daudé <philmd@redhat.com> writes:

> On 3/15/21 5:59 PM, Christian Borntraeger wrote:

>> On 15.03.21 17:51, Philippe Mathieu-Daudé wrote:

>> 

>>> diff --git a/hw/core/Kconfig b/hw/core/Kconfig

>>> index fdf03514d7d..9397503656d 100644

>>> --- a/hw/core/Kconfig

>>> +++ b/hw/core/Kconfig

>>> @@ -11,6 +11,11 @@ config GENERIC_LOADER

>>>       bool

>>>       default y

>>>

>>> +config GUEST_LOADER

>>> +    bool

>>> +    default y

>>> +    depends on TCG

>>> +

>>>   config OR_IRQ

>>>       bool

>>>

>>> diff --git a/hw/core/meson.build b/hw/core/meson.build

>>> index 9cd72edf513..59f1605bb07 100644

>>> --- a/hw/core/meson.build

>>> +++ b/hw/core/meson.build

>>> @@ -16,6 +16,7 @@

>>>   common_ss.add(files('cpu.c'))

>>>   common_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c'))

>>>   common_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true:

>>> files('generic-loader.c'))

>>> +common_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true:

>>> files('guest-loader.c'))

>>>   common_ss.add(when: 'CONFIG_OR_IRQ', if_true: files('or-irq.c'))

>>>   common_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true:

>>> files('platform-bus.c'))

>>>   common_ss.add(when: 'CONFIG_PTIMER', if_true: files('ptimer.c'))

>>> @@ -37,8 +38,6 @@

>>>     'clock-vmstate.c',

>>>   ))

>>>

>>> -softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('guest-loader.c'))

>>> -

>>>   specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files(

>>>     'machine-qmp-cmds.c',

>>>     'numa.c',

>> 

>> 

>> Also

>> Tested-by: Christian Borntraeger <borntraeger@de.ibm.com>

>

> Thanks Christian!


Can you send that to me as a proper patch (unless Christian wants to
take it through the s390 trees)?

-- 
Alex Bennée
diff mbox series

Patch

diff --git a/hw/core/guest-loader.h b/hw/core/guest-loader.h
new file mode 100644
index 0000000000..07f4b4884b
--- /dev/null
+++ b/hw/core/guest-loader.h
@@ -0,0 +1,34 @@ 
+/*
+ * Guest Loader
+ *
+ * Copyright (C) 2020 Linaro
+ * Written by Alex Bennée <alex.bennee@linaro.org>
+ * (based on the generic-loader by Li Guang <lig.fnst@cn.fujitsu.com>)
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef GUEST_LOADER_H
+#define GUEST_LOADER_H
+
+#include "hw/qdev-core.h"
+#include "qom/object.h"
+
+struct GuestLoaderState {
+    /* <private> */
+    DeviceState parent_obj;
+
+    /* <public> */
+    uint64_t addr;
+    char *kernel;
+    char *args;
+    char *initrd;
+};
+
+#define TYPE_GUEST_LOADER "guest-loader"
+OBJECT_DECLARE_SIMPLE_TYPE(GuestLoaderState, GUEST_LOADER)
+
+#endif
diff --git a/hw/core/guest-loader.c b/hw/core/guest-loader.c
new file mode 100644
index 0000000000..bde44e27b4
--- /dev/null
+++ b/hw/core/guest-loader.c
@@ -0,0 +1,145 @@ 
+/*
+ * Guest Loader
+ *
+ * Copyright (C) 2020 Linaro
+ * Written by Alex Bennée <alex.bennee@linaro.org>
+ * (based on the generic-loader by Li Guang <lig.fnst@cn.fujitsu.com>)
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+/*
+ * Much like the generic-loader this is treated as a special device
+ * inside QEMU. However unlike the generic-loader this device is used
+ * to load guest images for hypervisors. As part of that process the
+ * hypervisor needs to have platform information passed to it by the
+ * lower levels of the stack (e.g. firmware/bootloader). If you boot
+ * the hypervisor directly you use the guest-loader to load the Dom0
+ * or equivalent guest images in the right place in the same way a
+ * boot loader would.
+ *
+ * This is only relevant for full system emulation.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/core/cpu.h"
+#include "hw/sysbus.h"
+#include "sysemu/dma.h"
+#include "hw/loader.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "guest-loader.h"
+#include "sysemu/device_tree.h"
+#include "hw/boards.h"
+
+/*
+ * Insert some FDT nodes for the loaded blob.
+ */
+static void loader_insert_platform_data(GuestLoaderState *s, int size,
+                                        Error **errp)
+{
+    MachineState *machine = MACHINE(qdev_get_machine());
+    void *fdt = machine->fdt;
+    g_autofree char *node = g_strdup_printf("/chosen/module@0x%08" PRIx64,
+                                            s->addr);
+    uint64_t reg_attr[2] = {cpu_to_be64(s->addr), cpu_to_be64(size)};
+
+    if (!fdt) {
+        error_setg(errp, "Cannot modify FDT fields if the machine has none");
+        return;
+    }
+
+    qemu_fdt_add_subnode(fdt, node);
+    qemu_fdt_setprop(fdt, node, "reg", &reg_attr, sizeof(reg_attr));
+
+    if (s->kernel) {
+        const char *compat[2] = { "multiboot,module", "multiboot,kernel" };
+        if (qemu_fdt_setprop_string_array(fdt, node, "compatible",
+                                          (char **) &compat,
+                                          ARRAY_SIZE(compat)) < 0) {
+            error_setg(errp, "couldn't set %s/compatible", node);
+            return;
+        }
+        if (s->args) {
+            if (qemu_fdt_setprop_string(fdt, node, "bootargs", s->args) < 0) {
+                error_setg(errp, "couldn't set %s/bootargs", node);
+            }
+        }
+    } else if (s->initrd) {
+        const char *compat[2] = { "multiboot,module", "multiboot,ramdisk" };
+        if (qemu_fdt_setprop_string_array(fdt, node, "compatible",
+                                          (char **) &compat,
+                                          ARRAY_SIZE(compat)) < 0) {
+            error_setg(errp, "couldn't set %s/compatible", node);
+            return;
+        }
+    }
+}
+
+static void guest_loader_realize(DeviceState *dev, Error **errp)
+{
+    GuestLoaderState *s = GUEST_LOADER(dev);
+    char *file = s->kernel ? s->kernel : s->initrd;
+    int size = 0;
+
+    /* Perform some error checking on the user's options */
+    if (s->kernel && s->initrd) {
+        error_setg(errp, "Cannot specify a kernel and initrd in same stanza");
+        return;
+    } else if (!s->kernel && !s->initrd)  {
+        error_setg(errp, "Need to specify a kernel or initrd image");
+        return;
+    } else if (!s->addr) {
+        error_setg(errp, "Need to specify the address of guest blob");
+        return;
+    } else if (s->args && !s->kernel) {
+        error_setg(errp, "Boot args only relevant to kernel blobs");
+    }
+
+    /* Default to the maximum size being the machine's ram size */
+    size = load_image_targphys_as(file, s->addr, current_machine->ram_size,
+                                  NULL);
+    if (size < 0) {
+        error_setg(errp, "Cannot load specified image %s", file);
+        return;
+    }
+
+    /* Now the image is loaded we need to update the platform data */
+    loader_insert_platform_data(s, size, errp);
+}
+
+static Property guest_loader_props[] = {
+    DEFINE_PROP_UINT64("addr", GuestLoaderState, addr, 0),
+    DEFINE_PROP_STRING("kernel", GuestLoaderState, kernel),
+    DEFINE_PROP_STRING("bootargs", GuestLoaderState, args),
+    DEFINE_PROP_STRING("initrd", GuestLoaderState, initrd),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void guest_loader_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = guest_loader_realize;
+    device_class_set_props(dc, guest_loader_props);
+    dc->desc = "Guest Loader";
+    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static TypeInfo guest_loader_info = {
+    .name = TYPE_GUEST_LOADER,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(GuestLoaderState),
+    .class_init = guest_loader_class_init,
+};
+
+static void guest_loader_register_type(void)
+{
+    type_register_static(&guest_loader_info);
+}
+
+type_init(guest_loader_register_type)
diff --git a/MAINTAINERS b/MAINTAINERS
index ab3118429b..0134cad491 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2018,6 +2018,11 @@  F: hw/core/generic-loader.c
 F: include/hw/core/generic-loader.h
 F: docs/generic-loader.txt
 
+Guest Loader
+M: Alex Bennée <alex.bennee@linaro.org>
+S: Maintained
+F: hw/core/guest-loader.c
+
 Intel Hexadecimal Object File Loader
 M: Su Hang <suhang16@mails.ucas.ac.cn>
 S: Maintained
diff --git a/hw/core/meson.build b/hw/core/meson.build
index 032576f571..9cd72edf51 100644
--- a/hw/core/meson.build
+++ b/hw/core/meson.build
@@ -37,6 +37,8 @@  softmmu_ss.add(files(
   'clock-vmstate.c',
 ))
 
+softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('guest-loader.c'))
+
 specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files(
   'machine-qmp-cmds.c',
   'numa.c',