grml...
[seabios.git] / src / paravirt.c
1 // Paravirtualization support.
2 //
3 // Copyright (C) 2009 Red Hat Inc.
4 //
5 // Authors:
6 //  Gleb Natapov <gnatapov@redhat.com>
7 //
8 // This file may be distributed under the terms of the GNU LGPLv3 license.
9
10 #include "config.h" // CONFIG_COREBOOT
11 #include "util.h" // ntoh[ls]
12 #include "ioport.h" // outw
13 #include "paravirt.h" // qemu_cfg_port_probe
14 #include "smbios.h" // struct smbios_structure_header
15
16 int qemu_cfg_present;
17
18 static void
19 qemu_cfg_select(u16 f)
20 {
21     outw(f, PORT_QEMU_CFG_CTL);
22 }
23
24 static void
25 qemu_cfg_read(u8 *buf, int len)
26 {
27     insb(PORT_QEMU_CFG_DATA, buf, len);
28 }
29
30 static void
31 qemu_cfg_skip(int len)
32 {
33     while (len--)
34         inb(PORT_QEMU_CFG_DATA);
35 }
36
37 static void
38 qemu_cfg_read_entry(void *buf, int e, int len)
39 {
40     qemu_cfg_select(e);
41     qemu_cfg_read(buf, len);
42 }
43
44 void qemu_cfg_port_probe(void)
45 {
46     char *sig = "QEMU";
47     int i;
48
49     if (CONFIG_COREBOOT)
50         return;
51
52     qemu_cfg_present = 1;
53
54     qemu_cfg_select(QEMU_CFG_SIGNATURE);
55
56     for (i = 0; i < 4; i++)
57         if (inb(PORT_QEMU_CFG_DATA) != sig[i]) {
58             qemu_cfg_present = 0;
59             break;
60         }
61     dprintf(4, "qemu_cfg_present=%d\n", qemu_cfg_present);
62 }
63
64 void qemu_cfg_get_uuid(u8 *uuid)
65 {
66     if (!qemu_cfg_present)
67         return;
68
69     qemu_cfg_read_entry(uuid, QEMU_CFG_UUID, 16);
70 }
71
72 int qemu_cfg_show_boot_menu(void)
73 {
74     u16 v;
75     if (!qemu_cfg_present)
76         return 1;
77
78     qemu_cfg_read_entry(&v, QEMU_CFG_BOOT_MENU, sizeof(v));
79
80     return v;
81 }
82
83 int qemu_cfg_irq0_override(void)
84 {
85     u8 v;
86
87     if (!qemu_cfg_present)
88         return 0;
89
90     qemu_cfg_read_entry(&v, QEMU_CFG_IRQ0_OVERRIDE, sizeof(v));
91
92     return v;
93 }
94
95 u16 qemu_cfg_acpi_additional_tables(void)
96 {
97     u16 cnt;
98
99     if (!qemu_cfg_present)
100         return 0;
101
102     qemu_cfg_read_entry(&cnt, QEMU_CFG_ACPI_TABLES, sizeof(cnt));
103
104     return cnt;
105 }
106
107 u16 qemu_cfg_next_acpi_table_len(void)
108 {
109     u16 len;
110
111     qemu_cfg_read((u8*)&len, sizeof(len));
112
113     return len;
114 }
115
116 void* qemu_cfg_next_acpi_table_load(void *addr, u16 len)
117 {
118     qemu_cfg_read(addr, len);
119     return addr;
120 }
121
122 u16 qemu_cfg_smbios_entries(void)
123 {
124     u16 cnt;
125
126     if (!qemu_cfg_present)
127         return 0;
128
129     qemu_cfg_read_entry(&cnt, QEMU_CFG_SMBIOS_ENTRIES, sizeof(cnt));
130
131     return cnt;
132 }
133
134 u32 qemu_cfg_e820_entries(void)
135 {
136     u32 cnt;
137
138     if (!qemu_cfg_present)
139         return 0;
140
141     qemu_cfg_read_entry(&cnt, QEMU_CFG_E820_TABLE, sizeof(cnt));
142     return cnt;
143 }
144
145 void* qemu_cfg_e820_load_next(void *addr)
146 {
147     qemu_cfg_read(addr, sizeof(struct e820_reservation));
148     return addr;
149 }
150
151 struct smbios_header {
152     u16 length;
153     u8 type;
154 } PACKED;
155
156 struct smbios_field {
157     struct smbios_header header;
158     u8 type;
159     u16 offset;
160     u8 data[];
161 } PACKED;
162
163 struct smbios_table {
164     struct smbios_header header;
165     u8 data[];
166 } PACKED;
167
168 #define SMBIOS_FIELD_ENTRY 0
169 #define SMBIOS_TABLE_ENTRY 1
170
171 size_t qemu_cfg_smbios_load_field(int type, size_t offset, void *addr)
172 {
173     int i;
174
175     for (i = qemu_cfg_smbios_entries(); i > 0; i--) {
176         struct smbios_field field;
177
178         qemu_cfg_read((u8 *)&field, sizeof(struct smbios_header));
179         field.header.length -= sizeof(struct smbios_header);
180
181         if (field.header.type != SMBIOS_FIELD_ENTRY) {
182             qemu_cfg_skip(field.header.length);
183             continue;
184         }
185
186         qemu_cfg_read((u8 *)&field.type,
187                       sizeof(field) - sizeof(struct smbios_header));
188         field.header.length -= sizeof(field) - sizeof(struct smbios_header);
189
190         if (field.type != type || field.offset != offset) {
191             qemu_cfg_skip(field.header.length);
192             continue;
193         }
194
195         qemu_cfg_read(addr, field.header.length);
196         return (size_t)field.header.length;
197     }
198     return 0;
199 }
200
201 int qemu_cfg_smbios_load_external(int type, char **p, unsigned *nr_structs,
202                                   unsigned *max_struct_size, char *end)
203 {
204     static u64 used_bitmap[4] = { 0 };
205     char *start = *p;
206     int i;
207
208     /* Check if we've already reported these tables */
209     if (used_bitmap[(type >> 6) & 0x3] & (1ULL << (type & 0x3f)))
210         return 1;
211
212     /* Don't introduce spurious end markers */
213     if (type == 127)
214         return 0;
215
216     for (i = qemu_cfg_smbios_entries(); i > 0; i--) {
217         struct smbios_table table;
218         struct smbios_structure_header *header = (void *)*p;
219         int string;
220
221         qemu_cfg_read((u8 *)&table, sizeof(struct smbios_header));
222         table.header.length -= sizeof(struct smbios_header);
223
224         if (table.header.type != SMBIOS_TABLE_ENTRY) {
225             qemu_cfg_skip(table.header.length);
226             continue;
227         }
228
229         if (end - *p < sizeof(struct smbios_structure_header)) {
230             warn_noalloc();
231             break;
232         }
233
234         qemu_cfg_read((u8 *)*p, sizeof(struct smbios_structure_header));
235         table.header.length -= sizeof(struct smbios_structure_header);
236
237         if (header->type != type) {
238             qemu_cfg_skip(table.header.length);
239             continue;
240         }
241
242         *p += sizeof(struct smbios_structure_header);
243
244         /* Entries end with a double NULL char, if there's a string at
245          * the end (length is greater than formatted length), the string
246          * terminator provides the first NULL. */
247         string = header->length < table.header.length +
248                  sizeof(struct smbios_structure_header);
249
250         /* Read the rest and terminate the entry */
251         if (end - *p < table.header.length) {
252             warn_noalloc();
253             *p -= sizeof(struct smbios_structure_header);
254             continue;
255         }
256         qemu_cfg_read((u8 *)*p, table.header.length);
257         *p += table.header.length;
258         *((u8*)*p) = 0;
259         (*p)++;
260         if (!string) {
261             *((u8*)*p) = 0;
262             (*p)++;
263         }
264
265         (*nr_structs)++;
266         if (*p - (char *)header > *max_struct_size)
267             *max_struct_size = *p - (char *)header;
268     }
269
270     if (start != *p) {
271         /* Mark that we've reported on this type */
272         used_bitmap[(type >> 6) & 0x3] |= (1ULL << (type & 0x3f));
273         return 1;
274     }
275
276     return 0;
277 }
278
279 int qemu_cfg_get_numa_nodes(void)
280 {
281     u64 cnt;
282
283     qemu_cfg_read_entry(&cnt, QEMU_CFG_NUMA, sizeof(cnt));
284
285     return (int)cnt;
286 }
287
288 void qemu_cfg_get_numa_data(u64 *data, int n)
289 {
290     int i;
291
292     for (i = 0; i < n; i++)
293         qemu_cfg_read((u8*)(data + i), sizeof(u64));
294 }
295
296 u16 qemu_cfg_get_max_cpus(void)
297 {
298     u16 cnt;
299
300     if (!qemu_cfg_present)
301         return 0;
302
303     qemu_cfg_read_entry(&cnt, QEMU_CFG_MAX_CPUS, sizeof(cnt));
304
305     return cnt;
306 }
307
308 static QemuCfgFile LastFile;
309
310 static u32
311 __cfg_next_prefix_file(const char *prefix, int prefixlen, u32 prevselect)
312 {
313     if (!qemu_cfg_present)
314         return 0;
315
316     u32 count;
317     qemu_cfg_read_entry(&count, QEMU_CFG_FILE_DIR, sizeof(count));
318     count = ntohl(count);
319     u32 e;
320     for (e = 0; e < count; e++) {
321         qemu_cfg_read((void*)&LastFile, sizeof(LastFile));
322         u32 select = ntohs(LastFile.select);
323         if (select <= prevselect)
324             continue;
325         if (memcmp(prefix, LastFile.name, prefixlen) == 0)
326             return select;
327     }
328     return 0;
329 }
330
331 u32 qemu_cfg_next_prefix_file(const char *prefix, u32 prevselect)
332 {
333     return __cfg_next_prefix_file(prefix, strlen(prefix), prevselect);
334 }
335
336 u32 qemu_cfg_find_file(const char *name)
337 {
338     return __cfg_next_prefix_file(name, strlen(name) + 1, 0);
339 }
340
341 static int
342 __qemu_cfg_set_file(u32 select)
343 {
344     if (!qemu_cfg_present || !select)
345         return -1;
346     if (select == ntohs(LastFile.select))
347         return 0;
348
349     u32 count;
350     qemu_cfg_read_entry(&count, QEMU_CFG_FILE_DIR, sizeof(count));
351     count = ntohl(count);
352     u32 e;
353     for (e = 0; e < count; e++) {
354         qemu_cfg_read((void*)&LastFile, sizeof(LastFile));
355         if (select == ntohs(LastFile.select))
356             return 0;
357     }
358     return -1;
359 }
360
361 int qemu_cfg_size_file(u32 select)
362 {
363     if (__qemu_cfg_set_file(select))
364         return -1;
365     return ntohl(LastFile.size);
366 }
367
368 const char* qemu_cfg_name_file(u32 select)
369 {
370     if (__qemu_cfg_set_file(select))
371         return NULL;
372     return LastFile.name;
373 }
374
375 int qemu_cfg_read_file(u32 select, void *dst, u32 maxlen)
376 {
377     if (__qemu_cfg_set_file(select))
378         return -1;
379     int len = qemu_cfg_size_file(select);
380     if (len < 0 || len > maxlen)
381         return -1;
382     qemu_cfg_read_entry(dst, select, len);
383     return len;
384 }
385
386 // Helper function to find, malloc_tmphigh, and copy a romfile.  This
387 // function adds a trailing zero to the malloc'd copy.
388 void *
389 romfile_loadfile(const char *name, int *psize)
390 {
391     u32 file = romfile_find(name);
392     if (!file)
393         return NULL;
394
395     int filesize = romfile_size(file);
396     if (!filesize)
397         return NULL;
398
399     char *data = malloc_tmphigh(filesize+1);
400     if (!data) {
401         warn_noalloc();
402         return NULL;
403     }
404
405     dprintf(5, "Copying romfile '%s' (len %d)\n", name, filesize);
406     romfile_copy(file, data, filesize);
407     if (psize)
408         *psize = filesize;
409     data[filesize] = '\0';
410     return data;
411 }
412
413 // Attempt to load an integer from the given file - return 'defval'
414 // if unsuccesful.
415 u64
416 romfile_loadint(const char *name, u64 defval)
417 {
418     u32 file = romfile_find(name);
419     if (!file)
420         return defval;
421
422     int filesize = romfile_size(file);
423     if (!filesize || filesize > sizeof(u64) || (filesize & (filesize-1)))
424         // Doesn't look like a valid integer.
425         return defval;
426
427     u64 val = 0;
428     romfile_copy(file, &val, sizeof(val));
429     return val;
430 }