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