Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging
[sdk/emulator/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 "hw/hw.h"
31 #include "hw/qdev-core.h"
32
33 typedef struct FWBootEntry FWBootEntry;
34
35 struct FWBootEntry {
36     QTAILQ_ENTRY(FWBootEntry) link;
37     int32_t bootindex;
38     DeviceState *dev;
39     char *suffix;
40 };
41
42 static QTAILQ_HEAD(, FWBootEntry) fw_boot_order =
43     QTAILQ_HEAD_INITIALIZER(fw_boot_order);
44 static QEMUBootSetHandler *boot_set_handler;
45 static void *boot_set_opaque;
46
47 void qemu_register_boot_set(QEMUBootSetHandler *func, void *opaque)
48 {
49     boot_set_handler = func;
50     boot_set_opaque = opaque;
51 }
52
53 void qemu_boot_set(const char *boot_order, Error **errp)
54 {
55     Error *local_err = NULL;
56
57     if (!boot_set_handler) {
58         error_setg(errp, "no function defined to set boot device list for"
59                          " this architecture");
60         return;
61     }
62
63     validate_bootdevices(boot_order, &local_err);
64     if (local_err) {
65         error_propagate(errp, local_err);
66         return;
67     }
68
69     boot_set_handler(boot_set_opaque, boot_order, errp);
70 }
71
72 void validate_bootdevices(const char *devices, Error **errp)
73 {
74     /* We just do some generic consistency checks */
75     const char *p;
76     int bitmap = 0;
77
78     for (p = devices; *p != '\0'; p++) {
79         /* Allowed boot devices are:
80          * a-b: floppy disk drives
81          * c-f: IDE disk drives
82          * g-m: machine implementation dependent drives
83          * n-p: network devices
84          * It's up to each machine implementation to check if the given boot
85          * devices match the actual hardware implementation and firmware
86          * features.
87          */
88         if (*p < 'a' || *p > 'p') {
89             error_setg(errp, "Invalid boot device '%c'", *p);
90             return;
91         }
92         if (bitmap & (1 << (*p - 'a'))) {
93             error_setg(errp, "Boot device '%c' was given twice", *p);
94             return;
95         }
96         bitmap |= 1 << (*p - 'a');
97     }
98 }
99
100 void restore_boot_order(void *opaque)
101 {
102     char *normal_boot_order = opaque;
103     static int first = 1;
104
105     /* Restore boot order and remove ourselves after the first boot */
106     if (first) {
107         first = 0;
108         return;
109     }
110
111     if (boot_set_handler) {
112         qemu_boot_set(normal_boot_order, &error_abort);
113     }
114
115     qemu_unregister_reset(restore_boot_order, normal_boot_order);
116     g_free(normal_boot_order);
117 }
118
119 void check_boot_index(int32_t bootindex, Error **errp)
120 {
121     FWBootEntry *i;
122
123     if (bootindex >= 0) {
124         QTAILQ_FOREACH(i, &fw_boot_order, link) {
125             if (i->bootindex == bootindex) {
126                 error_setg(errp, "The bootindex %d has already been used",
127                            bootindex);
128                 return;
129             }
130         }
131     }
132 }
133
134 void del_boot_device_path(DeviceState *dev, const char *suffix)
135 {
136     FWBootEntry *i;
137
138     if (dev == NULL) {
139         return;
140     }
141
142     QTAILQ_FOREACH(i, &fw_boot_order, link) {
143         if ((!suffix || !g_strcmp0(i->suffix, suffix)) &&
144              i->dev == dev) {
145             QTAILQ_REMOVE(&fw_boot_order, i, link);
146             g_free(i->suffix);
147             g_free(i);
148
149             break;
150         }
151     }
152 }
153
154 void add_boot_device_path(int32_t bootindex, DeviceState *dev,
155                           const char *suffix)
156 {
157     FWBootEntry *node, *i;
158
159     if (bootindex < 0) {
160         del_boot_device_path(dev, suffix);
161         return;
162     }
163
164     assert(dev != NULL || suffix != NULL);
165
166     del_boot_device_path(dev, suffix);
167
168     node = g_malloc0(sizeof(FWBootEntry));
169     node->bootindex = bootindex;
170     node->suffix = g_strdup(suffix);
171     node->dev = dev;
172
173     QTAILQ_FOREACH(i, &fw_boot_order, link) {
174         if (i->bootindex == bootindex) {
175             error_report("Two devices with same boot index %d", bootindex);
176             exit(1);
177         } else if (i->bootindex < bootindex) {
178             continue;
179         }
180         QTAILQ_INSERT_BEFORE(i, node, link);
181         return;
182     }
183     QTAILQ_INSERT_TAIL(&fw_boot_order, node, link);
184 }
185
186 DeviceState *get_boot_device(uint32_t position)
187 {
188     uint32_t counter = 0;
189     FWBootEntry *i = NULL;
190     DeviceState *res = NULL;
191
192     if (!QTAILQ_EMPTY(&fw_boot_order)) {
193         QTAILQ_FOREACH(i, &fw_boot_order, link) {
194             if (counter == position) {
195                 res = i->dev;
196                 break;
197             }
198             counter++;
199         }
200     }
201     return res;
202 }
203
204 /*
205  * This function returns null terminated string that consist of new line
206  * separated device paths.
207  *
208  * memory pointed by "size" is assigned total length of the array in bytes
209  *
210  */
211 char *get_boot_devices_list(size_t *size, bool ignore_suffixes)
212 {
213     FWBootEntry *i;
214     size_t total = 0;
215     char *list = NULL;
216
217     QTAILQ_FOREACH(i, &fw_boot_order, link) {
218         char *devpath = NULL,  *suffix = NULL;
219         char *bootpath;
220         char *d;
221         size_t len;
222
223         if (i->dev) {
224             devpath = qdev_get_fw_dev_path(i->dev);
225             assert(devpath);
226         }
227
228         if (!ignore_suffixes) {
229             if (i->dev) {
230                 d = qdev_get_own_fw_dev_path_from_handler(i->dev->parent_bus,
231                                                           i->dev);
232                 if (d) {
233                     assert(!i->suffix);
234                     suffix = d;
235                 } else {
236                     suffix = g_strdup(i->suffix);
237                 }
238             } else {
239                 suffix = g_strdup(i->suffix);
240             }
241         }
242
243         bootpath = g_strdup_printf("%s%s",
244                                    devpath ? devpath : "",
245                                    suffix ? suffix : "");
246         g_free(devpath);
247         g_free(suffix);
248
249         if (total) {
250             list[total-1] = '\n';
251         }
252         len = strlen(bootpath) + 1;
253         list = g_realloc(list, total + len);
254         memcpy(&list[total], bootpath, len);
255         total += len;
256         g_free(bootpath);
257     }
258
259     *size = total;
260
261     if (boot_strict && *size > 0) {
262         list[total-1] = '\n';
263         list = g_realloc(list, total + 5);
264         memcpy(&list[total], "HALT", 5);
265         *size = total + 5;
266     }
267     return list;
268 }
269
270 typedef struct {
271     int32_t *bootindex;
272     const char *suffix;
273     DeviceState *dev;
274 } BootIndexProperty;
275
276 static void device_get_bootindex(Object *obj, Visitor *v, const char *name,
277                                  void *opaque, Error **errp)
278 {
279     BootIndexProperty *prop = opaque;
280     visit_type_int32(v, name, prop->bootindex, errp);
281 }
282
283 static void device_set_bootindex(Object *obj, Visitor *v, const char *name,
284                                  void *opaque, Error **errp)
285 {
286     BootIndexProperty *prop = opaque;
287     int32_t boot_index;
288     Error *local_err = NULL;
289
290     visit_type_int32(v, name, &boot_index, &local_err);
291     if (local_err) {
292         goto out;
293     }
294     /* check whether bootindex is present in fw_boot_order list  */
295     check_boot_index(boot_index, &local_err);
296     if (local_err) {
297         goto out;
298     }
299     /* change bootindex to a new one */
300     *prop->bootindex = boot_index;
301
302     add_boot_device_path(*prop->bootindex, prop->dev, prop->suffix);
303
304 out:
305     error_propagate(errp, local_err);
306 }
307
308 static void property_release_bootindex(Object *obj, const char *name,
309                                        void *opaque)
310
311 {
312     BootIndexProperty *prop = opaque;
313
314     del_boot_device_path(prop->dev, prop->suffix);
315     g_free(prop);
316 }
317
318 void device_add_bootindex_property(Object *obj, int32_t *bootindex,
319                                    const char *name, const char *suffix,
320                                    DeviceState *dev, Error **errp)
321 {
322     Error *local_err = NULL;
323     BootIndexProperty *prop = g_malloc0(sizeof(*prop));
324
325     prop->bootindex = bootindex;
326     prop->suffix = suffix;
327     prop->dev = dev;
328
329     object_property_add(obj, name, "int32",
330                         device_get_bootindex,
331                         device_set_bootindex,
332                         property_release_bootindex,
333                         prop, &local_err);
334
335     if (local_err) {
336         error_propagate(errp, local_err);
337         g_free(prop);
338         return;
339     }
340     /* initialize devices' bootindex property to -1 */
341     object_property_set_int(obj, -1, name, NULL);
342 }