1a33ff6d2SAlex Bennée /* 2a33ff6d2SAlex Bennée * Guest Loader 3a33ff6d2SAlex Bennée * 4a33ff6d2SAlex Bennée * Copyright (C) 2020 Linaro 5a33ff6d2SAlex Bennée * Written by Alex Bennée <alex.bennee@linaro.org> 6a33ff6d2SAlex Bennée * (based on the generic-loader by Li Guang <lig.fnst@cn.fujitsu.com>) 7a33ff6d2SAlex Bennée * 8a33ff6d2SAlex Bennée * SPDX-License-Identifier: GPL-2.0-or-later 9a33ff6d2SAlex Bennée * 10a33ff6d2SAlex Bennée * This work is licensed under the terms of the GNU GPL, version 2 or later. 11a33ff6d2SAlex Bennée * See the COPYING file in the top-level directory. 12a33ff6d2SAlex Bennée */ 13a33ff6d2SAlex Bennée 14a33ff6d2SAlex Bennée /* 15a33ff6d2SAlex Bennée * Much like the generic-loader this is treated as a special device 16a33ff6d2SAlex Bennée * inside QEMU. However unlike the generic-loader this device is used 17a33ff6d2SAlex Bennée * to load guest images for hypervisors. As part of that process the 18a33ff6d2SAlex Bennée * hypervisor needs to have platform information passed to it by the 19a33ff6d2SAlex Bennée * lower levels of the stack (e.g. firmware/bootloader). If you boot 20a33ff6d2SAlex Bennée * the hypervisor directly you use the guest-loader to load the Dom0 21a33ff6d2SAlex Bennée * or equivalent guest images in the right place in the same way a 22a33ff6d2SAlex Bennée * boot loader would. 23a33ff6d2SAlex Bennée * 24a33ff6d2SAlex Bennée * This is only relevant for full system emulation. 25a33ff6d2SAlex Bennée */ 26a33ff6d2SAlex Bennée 27a33ff6d2SAlex Bennée #include "qemu/osdep.h" 28a33ff6d2SAlex Bennée #include "hw/core/cpu.h" 29a33ff6d2SAlex Bennée #include "sysemu/dma.h" 30a33ff6d2SAlex Bennée #include "hw/loader.h" 31a33ff6d2SAlex Bennée #include "hw/qdev-properties.h" 32a33ff6d2SAlex Bennée #include "qapi/error.h" 33a33ff6d2SAlex Bennée #include "qemu/module.h" 34a33ff6d2SAlex Bennée #include "guest-loader.h" 35a33ff6d2SAlex Bennée #include "sysemu/device_tree.h" 36a33ff6d2SAlex Bennée #include "hw/boards.h" 37a33ff6d2SAlex Bennée 38a33ff6d2SAlex Bennée /* 39a33ff6d2SAlex Bennée * Insert some FDT nodes for the loaded blob. 40a33ff6d2SAlex Bennée */ 41a33ff6d2SAlex Bennée static void loader_insert_platform_data(GuestLoaderState *s, int size, 42a33ff6d2SAlex Bennée Error **errp) 43a33ff6d2SAlex Bennée { 44a33ff6d2SAlex Bennée MachineState *machine = MACHINE(qdev_get_machine()); 45a33ff6d2SAlex Bennée void *fdt = machine->fdt; 46a33ff6d2SAlex Bennée g_autofree char *node = g_strdup_printf("/chosen/module@0x%08" PRIx64, 47a33ff6d2SAlex Bennée s->addr); 48a33ff6d2SAlex Bennée uint64_t reg_attr[2] = {cpu_to_be64(s->addr), cpu_to_be64(size)}; 49a33ff6d2SAlex Bennée 50a33ff6d2SAlex Bennée if (!fdt) { 51a33ff6d2SAlex Bennée error_setg(errp, "Cannot modify FDT fields if the machine has none"); 52a33ff6d2SAlex Bennée return; 53a33ff6d2SAlex Bennée } 54a33ff6d2SAlex Bennée 55a33ff6d2SAlex Bennée qemu_fdt_add_subnode(fdt, node); 56a33ff6d2SAlex Bennée qemu_fdt_setprop(fdt, node, "reg", ®_attr, sizeof(reg_attr)); 57a33ff6d2SAlex Bennée 58a33ff6d2SAlex Bennée if (s->kernel) { 59a33ff6d2SAlex Bennée const char *compat[2] = { "multiboot,module", "multiboot,kernel" }; 60a33ff6d2SAlex Bennée if (qemu_fdt_setprop_string_array(fdt, node, "compatible", 61a33ff6d2SAlex Bennée (char **) &compat, 62a33ff6d2SAlex Bennée ARRAY_SIZE(compat)) < 0) { 63a33ff6d2SAlex Bennée error_setg(errp, "couldn't set %s/compatible", node); 64a33ff6d2SAlex Bennée return; 65a33ff6d2SAlex Bennée } 66a33ff6d2SAlex Bennée if (s->args) { 67a33ff6d2SAlex Bennée if (qemu_fdt_setprop_string(fdt, node, "bootargs", s->args) < 0) { 68a33ff6d2SAlex Bennée error_setg(errp, "couldn't set %s/bootargs", node); 69a33ff6d2SAlex Bennée } 70a33ff6d2SAlex Bennée } 71a33ff6d2SAlex Bennée } else if (s->initrd) { 72a33ff6d2SAlex Bennée const char *compat[2] = { "multiboot,module", "multiboot,ramdisk" }; 73a33ff6d2SAlex Bennée if (qemu_fdt_setprop_string_array(fdt, node, "compatible", 74a33ff6d2SAlex Bennée (char **) &compat, 75a33ff6d2SAlex Bennée ARRAY_SIZE(compat)) < 0) { 76a33ff6d2SAlex Bennée error_setg(errp, "couldn't set %s/compatible", node); 77a33ff6d2SAlex Bennée return; 78a33ff6d2SAlex Bennée } 79a33ff6d2SAlex Bennée } 80a33ff6d2SAlex Bennée } 81a33ff6d2SAlex Bennée 82a33ff6d2SAlex Bennée static void guest_loader_realize(DeviceState *dev, Error **errp) 83a33ff6d2SAlex Bennée { 84a33ff6d2SAlex Bennée GuestLoaderState *s = GUEST_LOADER(dev); 85a33ff6d2SAlex Bennée char *file = s->kernel ? s->kernel : s->initrd; 86a33ff6d2SAlex Bennée int size = 0; 87a33ff6d2SAlex Bennée 88a33ff6d2SAlex Bennée /* Perform some error checking on the user's options */ 89a33ff6d2SAlex Bennée if (s->kernel && s->initrd) { 90a33ff6d2SAlex Bennée error_setg(errp, "Cannot specify a kernel and initrd in same stanza"); 91a33ff6d2SAlex Bennée return; 92a33ff6d2SAlex Bennée } else if (!s->kernel && !s->initrd) { 93a33ff6d2SAlex Bennée error_setg(errp, "Need to specify a kernel or initrd image"); 94a33ff6d2SAlex Bennée return; 95a33ff6d2SAlex Bennée } else if (!s->addr) { 96a33ff6d2SAlex Bennée error_setg(errp, "Need to specify the address of guest blob"); 97a33ff6d2SAlex Bennée return; 98a33ff6d2SAlex Bennée } else if (s->args && !s->kernel) { 99a33ff6d2SAlex Bennée error_setg(errp, "Boot args only relevant to kernel blobs"); 100a33ff6d2SAlex Bennée } 101a33ff6d2SAlex Bennée 102a33ff6d2SAlex Bennée /* Default to the maximum size being the machine's ram size */ 103a33ff6d2SAlex Bennée size = load_image_targphys_as(file, s->addr, current_machine->ram_size, 104a33ff6d2SAlex Bennée NULL); 105a33ff6d2SAlex Bennée if (size < 0) { 106a33ff6d2SAlex Bennée error_setg(errp, "Cannot load specified image %s", file); 107a33ff6d2SAlex Bennée return; 108a33ff6d2SAlex Bennée } 109a33ff6d2SAlex Bennée 110a33ff6d2SAlex Bennée /* Now the image is loaded we need to update the platform data */ 111a33ff6d2SAlex Bennée loader_insert_platform_data(s, size, errp); 112a33ff6d2SAlex Bennée } 113a33ff6d2SAlex Bennée 114a33ff6d2SAlex Bennée static Property guest_loader_props[] = { 115a33ff6d2SAlex Bennée DEFINE_PROP_UINT64("addr", GuestLoaderState, addr, 0), 116a33ff6d2SAlex Bennée DEFINE_PROP_STRING("kernel", GuestLoaderState, kernel), 117a33ff6d2SAlex Bennée DEFINE_PROP_STRING("bootargs", GuestLoaderState, args), 118a33ff6d2SAlex Bennée DEFINE_PROP_STRING("initrd", GuestLoaderState, initrd), 119a33ff6d2SAlex Bennée DEFINE_PROP_END_OF_LIST(), 120a33ff6d2SAlex Bennée }; 121a33ff6d2SAlex Bennée 122a33ff6d2SAlex Bennée static void guest_loader_class_init(ObjectClass *klass, void *data) 123a33ff6d2SAlex Bennée { 124a33ff6d2SAlex Bennée DeviceClass *dc = DEVICE_CLASS(klass); 125a33ff6d2SAlex Bennée 126a33ff6d2SAlex Bennée dc->realize = guest_loader_realize; 127a33ff6d2SAlex Bennée device_class_set_props(dc, guest_loader_props); 128a33ff6d2SAlex Bennée dc->desc = "Guest Loader"; 129a33ff6d2SAlex Bennée set_bit(DEVICE_CATEGORY_MISC, dc->categories); 130a33ff6d2SAlex Bennée } 131a33ff6d2SAlex Bennée 132*5e78c98bSBernhard Beschow static const TypeInfo guest_loader_info = { 133a33ff6d2SAlex Bennée .name = TYPE_GUEST_LOADER, 134a33ff6d2SAlex Bennée .parent = TYPE_DEVICE, 135a33ff6d2SAlex Bennée .instance_size = sizeof(GuestLoaderState), 136a33ff6d2SAlex Bennée .class_init = guest_loader_class_init, 137a33ff6d2SAlex Bennée }; 138a33ff6d2SAlex Bennée 139a33ff6d2SAlex Bennée static void guest_loader_register_type(void) 140a33ff6d2SAlex Bennée { 141a33ff6d2SAlex Bennée type_register_static(&guest_loader_info); 142a33ff6d2SAlex Bennée } 143a33ff6d2SAlex Bennée 144a33ff6d2SAlex Bennée type_init(guest_loader_register_type) 145