]> Git Repo - qemu.git/blob - bootdevice.c
fw_cfg: ignore suffixes in the bootdevice list dependent on machine class
[qemu.git] / bootdevice.c
1 /*
2  * QEMU Boot Device Implement
3  *
4  * Copyright (c) 2014 HUAWEI TECHNOLOGIES CO., LTD.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  */
24
25 #include "qemu/osdep.h"
26 #include "qapi/error.h"
27 #include "sysemu/sysemu.h"
28 #include "qapi/visitor.h"
29 #include "qemu/error-report.h"
30 #include "sysemu/reset.h"
31 #include "hw/qdev-core.h"
32 #include "hw/boards.h"
33
34 typedef struct FWBootEntry FWBootEntry;
35
36 struct FWBootEntry {
37     QTAILQ_ENTRY(FWBootEntry) link;
38     int32_t bootindex;
39     DeviceState *dev;
40     char *suffix;
41 };
42
43 static QTAILQ_HEAD(, FWBootEntry) fw_boot_order =
44     QTAILQ_HEAD_INITIALIZER(fw_boot_order);
45 static QEMUBootSetHandler *boot_set_handler;
46 static void *boot_set_opaque;
47
48 void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque)
49 {
50     boot_set_handler = func;
51     boot_set_opaque = opaque;
52 }
53
54 void qemu_boot_set(const char *boot_order, Error **errp)
55 {
56     Error *local_err = NULL;
57
58     if (!boot_set_handler) {
59         error_setg(errp, "no function defined to set boot device list for"
60                          " this architecture");
61         return;
62     }
63
64     validate_bootdevices(boot_order, &local_err);
65     if (local_err) {
66         error_propagate(errp, local_err);
67         return;
68     }
69
70     boot_set_handler(boot_set_opaque, boot_order, errp);
71 }
72
73 void validate_bootdevices(const char *devices, Error **errp)
74 {
75     /* We just do some generic consistency checks */
76     const char *p;
77     int bitmap = 0;
78
79     for (p = devices; *p != '\0'; p++) {
80         /* Allowed boot devices are:
81          * a-b: floppy disk drives
82          * c-f: IDE disk drives
83          * g-m: machine implementation dependent drives
84          * n-p: network devices
85          * It's up to each machine implementation to check if the given boot
86          * devices match the actual hardware implementation and firmware
87          * features.
88          */
89         if (*p < 'a' || *p > 'p') {
90             error_setg(errp, "Invalid boot device '%c'", *p);
91             return;
92         }
93         if (bitmap & (1 << (*p - 'a'))) {
94             error_setg(errp, "Boot device '%c' was given twice", *p);
95             return;
96         }
97         bitmap |= 1 << (*p - 'a');
98     }
99 }
100
101 void restore_boot_order(void *opaque)
102 {
103     char *normal_boot_order = opaque;
104     static int first = 1;
105
106     /* Restore boot order and remove ourselves after the first boot */
107     if (first) {
108         first = 0;
109         return;
110     }
111
112     if (boot_set_handler) {
113         qemu_boot_set(normal_boot_order, &error_abort);
114     }
115
116     qemu_unregister_reset(restore_boot_order, normal_boot_order);
117     g_free(normal_boot_order);
118 }
119
120 void check_boot_index(int32_t bootindex, Error **errp)
121 {
122     FWBootEntry *i;
123
124     if (bootindex >= 0) {
125         QTAILQ_FOREACH(i, &fw_boot_order, link) {
126             if (i->bootindex == bootindex) {
127                 error_setg(errp, "The bootindex %d has already been used",
128                            bootindex);
129                 return;
130             }
131         }
132     }
133 }
134
135 void del_boot_device_path(DeviceState *dev, const char *suffix)
136 {
137     FWBootEntry *i;
138
139     if (dev == NULL) {
140         return;
141     }
142
143     QTAILQ_FOREACH(i, &fw_boot_order, link) {
144         if ((!suffix || !g_strcmp0(i->suffix, suffix)) &&
145              i->dev == dev) {
146             QTAILQ_REMOVE(&fw_boot_order, i, link);
147             g_free(i->suffix);
148             g_free(i);
149
150             break;
151         }
152     }
153 }
154
155 void add_boot_device_path(int32_t bootindex, DeviceState *dev,
156                           const char *suffix)
157 {
158     FWBootEntry *node, *i;
159
160     if (bootindex < 0) {
161         del_boot_device_path(dev, suffix);
162         return;
163     }
164
165     assert(dev != NULL || suffix != NULL);
166
167     del_boot_device_path(dev, suffix);
168
169     node = g_malloc0(sizeof(FWBootEntry));
170     node->bootindex = bootindex;
171     node->suffix = g_strdup(suffix);
172     node->dev = dev;
173
174     QTAILQ_FOREACH(i, &fw_boot_order, link) {
175         if (i->bootindex == bootindex) {
176             error_report("Two devices with same boot index %d", bootindex);
177             exit(1);
178         } else if (i->bootindex < bootindex) {
179             continue;
180         }
181         QTAILQ_INSERT_BEFORE(i, node, link);
182         return;
183     }
184     QTAILQ_INSERT_TAIL(&fw_boot_order, node, link);
185 }
186
187 DeviceState *get_boot_device(uint32_t position)
188 {
189     uint32_t counter = 0;
190     FWBootEntry *i = NULL;
191     DeviceState *res = NULL;
192
193     if (!QTAILQ_EMPTY(&fw_boot_order)) {
194         QTAILQ_FOREACH(i, &fw_boot_order, link) {
195             if (counter == position) {
196                 res = i->dev;
197                 break;
198             }
199             counter++;
200         }
201     }
202     return res;
203 }
204
205 /*
206  * This function returns null terminated string that consist of new line
207  * separated device paths.
208  *
209  * memory pointed by "size" is assigned total length of the array in bytes
210  *
211  */
212 char *get_boot_devices_list(size_t *size)
213 {
214     FWBootEntry *i;
215     size_t total = 0;
216     char *list = NULL;
217     MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
218     bool ignore_suffixes = mc->ignore_boot_device_suffixes;
219
220     QTAILQ_FOREACH(i, &fw_boot_order, link) {
221         char *devpath = NULL,  *suffix = NULL;
222         char *bootpath;
223         char *d;
224         size_t len;
225
226         if (i->dev) {
227             devpath = qdev_get_fw_dev_path(i->dev);
228             assert(devpath);
229         }
230
231         if (!ignore_suffixes) {
232             if (i->dev) {
233                 d = qdev_get_own_fw_dev_path_from_handler(i->dev->parent_bus,
234                                                           i->dev);
235                 if (d) {
236                     assert(!i->suffix);
237                     suffix = d;
238                 } else {
239                     suffix = g_strdup(i->suffix);
240                 }
241             } else {
242                 suffix = g_strdup(i->suffix);
243             }
244         }
245
246         bootpath = g_strdup_printf("%s%s",
247                                    devpath ? devpath : "",
248                                    suffix ? suffix : "");
249         g_free(devpath);
250         g_free(suffix);
251
252         if (total) {
253             list[total-1] = '\n';
254         }
255         len = strlen(bootpath) + 1;
256         list = g_realloc(list, total + len);
257         memcpy(&list[total], bootpath, len);
258         total += len;
259         g_free(bootpath);
260     }
261
262     *size = total;
263
264     if (boot_strict && *size > 0) {
265         list[total-1] = '\n';
266         list = g_realloc(list, total + 5);
267         memcpy(&list[total], "HALT", 5);
268         *size = total + 5;
269     }
270     return list;
271 }
272
273 typedef struct {
274     int32_t *bootindex;
275     const char *suffix;
276     DeviceState *dev;
277 } BootIndexProperty;
278
279 static void device_get_bootindex(Object *obj, Visitor *v, const char *name,
280                                  void *opaque, Error **errp)
281 {
282     BootIndexProperty *prop = opaque;
283     visit_type_int32(v, name, prop->bootindex, errp);
284 }
285
286 static void device_set_bootindex(Object *obj, Visitor *v, const char *name,
287                                  void *opaque, Error **errp)
288 {
289     BootIndexProperty *prop = opaque;
290     int32_t boot_index;
291     Error *local_err = NULL;
292
293     visit_type_int32(v, name, &boot_index, &local_err);
294     if (local_err) {
295         goto out;
296     }
297     /* check whether bootindex is present in fw_boot_order list  */
298     check_boot_index(boot_index, &local_err);
299     if (local_err) {
300         goto out;
301     }
302     /* change bootindex to a new one */
303     *prop->bootindex = boot_index;
304
305     add_boot_device_path(*prop->bootindex, prop->dev, prop->suffix);
306
307 out:
308     error_propagate(errp, local_err);
309 }
310
311 static void property_release_bootindex(Object *obj, const char *name,
312                                        void *opaque)
313
314 {
315     BootIndexProperty *prop = opaque;
316
317     del_boot_device_path(prop->dev, prop->suffix);
318     g_free(prop);
319 }
320
321 void device_add_bootindex_property(Object *obj, int32_t *bootindex,
322                                    const char *name, const char *suffix,
323                                    DeviceState *dev, Error **errp)
324 {
325     Error *local_err = NULL;
326     BootIndexProperty *prop = g_malloc0(sizeof(*prop));
327
328     prop->bootindex = bootindex;
329     prop->suffix = suffix;
330     prop->dev = dev;
331
332     object_property_add(obj, name, "int32",
333                         device_get_bootindex,
334                         device_set_bootindex,
335                         property_release_bootindex,
336                         prop, &local_err);
337
338     if (local_err) {
339         error_propagate(errp, local_err);
340         g_free(prop);
341         return;
342     }
343     /* initialize devices' bootindex property to -1 */
344     object_property_set_int(obj, -1, name, NULL);
345 }
This page took 0.043514 seconds and 4 git commands to generate.