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