00ff58ca4b8362bd5745f43d0ccbde2489d0327a
[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     while (len--)
28         *(buf++) = inb(PORT_QEMU_CFG_DATA);
29 }
30
31 static void
32 qemu_cfg_skip(int len)
33 {
34     while (len--)
35         inb(PORT_QEMU_CFG_DATA);
36 }
37
38 static void
39 qemu_cfg_read_entry(void *buf, int e, int len)
40 {
41     qemu_cfg_select(e);
42     qemu_cfg_read(buf, len);
43 }
44
45 void qemu_cfg_port_probe(void)
46 {
47     char *sig = "QEMU";
48     int i;
49
50     if (CONFIG_COREBOOT)
51         return;
52
53     qemu_cfg_present = 1;
54
55     qemu_cfg_select(QEMU_CFG_SIGNATURE);
56
57     for (i = 0; i < 4; i++)
58         if (inb(PORT_QEMU_CFG_DATA) != sig[i]) {
59             qemu_cfg_present = 0;
60             break;
61         }
62     dprintf(4, "qemu_cfg_present=%d\n", qemu_cfg_present);
63 }
64
65 void qemu_cfg_get_uuid(u8 *uuid)
66 {
67     if (!qemu_cfg_present)
68         return;
69
70     qemu_cfg_read_entry(uuid, QEMU_CFG_UUID, 16);
71 }
72
73 int qemu_cfg_show_boot_menu(void)
74 {
75     u16 v;
76     if (!qemu_cfg_present)
77         return 1;
78
79     qemu_cfg_read_entry(&v, QEMU_CFG_BOOT_MENU, sizeof(v));
80
81     return v;
82 }
83
84 int qemu_cfg_irq0_override(void)
85 {
86     u8 v;
87
88     if (!qemu_cfg_present)
89         return 0;
90
91     qemu_cfg_read_entry(&v, QEMU_CFG_IRQ0_OVERRIDE, sizeof(v));
92
93     return v;
94 }
95
96 u16 qemu_cfg_acpi_additional_tables(void)
97 {
98     u16 cnt;
99
100     if (!qemu_cfg_present)
101         return 0;
102
103     qemu_cfg_read_entry(&cnt, QEMU_CFG_ACPI_TABLES, sizeof(cnt));
104
105     return cnt;
106 }
107
108 u16 qemu_cfg_next_acpi_table_len(void)
109 {
110     u16 len;
111
112     qemu_cfg_read((u8*)&len, sizeof(len));
113
114     return len;
115 }
116
117 void* qemu_cfg_next_acpi_table_load(void *addr, u16 len)
118 {
119     qemu_cfg_read(addr, len);
120     return addr;
121 }
122
123 u16 qemu_cfg_smbios_entries(void)
124 {
125     u16 cnt;
126
127     if (!qemu_cfg_present)
128         return 0;
129
130     qemu_cfg_read_entry(&cnt, QEMU_CFG_SMBIOS_ENTRIES, sizeof(cnt));
131
132     return cnt;
133 }
134
135 u32 qemu_cfg_e820_entries(void)
136 {
137     u32 cnt;
138
139     if (!qemu_cfg_present)
140         return 0;
141
142     qemu_cfg_read_entry(&cnt, QEMU_CFG_E820_TABLE, sizeof(cnt));
143     return cnt;
144 }
145
146 void* qemu_cfg_e820_load_next(void *addr)
147 {
148     qemu_cfg_read(addr, sizeof(struct e820_reservation));
149     return addr;
150 }
151
152 struct smbios_header {
153     u16 length;
154     u8 type;
155 } PACKED;
156
157 struct smbios_field {
158     struct smbios_header header;
159     u8 type;
160     u16 offset;
161     u8 data[];
162 } PACKED;
163
164 struct smbios_table {
165     struct smbios_header header;
166     u8 data[];
167 } PACKED;
168
169 #define SMBIOS_FIELD_ENTRY 0
170 #define SMBIOS_TABLE_ENTRY 1
171
172 size_t qemu_cfg_smbios_load_field(int type, size_t offset, void *addr)
173 {
174     int i;
175
176     for (i = qemu_cfg_smbios_entries(); i > 0; i--) {
177         struct smbios_field field;
178
179         qemu_cfg_read((u8 *)&field, sizeof(struct smbios_header));
180         field.header.length -= sizeof(struct smbios_header);
181
182         if (field.header.type != SMBIOS_FIELD_ENTRY) {
183             qemu_cfg_skip(field.header.length);
184             continue;
185         }
186
187         qemu_cfg_read((u8 *)&field.type,
188                       sizeof(field) - sizeof(struct smbios_header));
189         field.header.length -= sizeof(field) - sizeof(struct smbios_header);
190
191         if (field.type != type || field.offset != offset) {
192             qemu_cfg_skip(field.header.length);
193             continue;
194         }
195
196         qemu_cfg_read(addr, field.header.length);
197         return (size_t)field.header.length;
198     }
199     return 0;
200 }
201
202 int qemu_cfg_smbios_load_external(int type, char **p, unsigned *nr_structs,
203                                   unsigned *max_struct_size, char *end)
204 {
205     static u64 used_bitmap[4] = { 0 };
206     char *start = *p;
207     int i;
208
209     /* Check if we've already reported these tables */
210     if (used_bitmap[(type >> 6) & 0x3] & (1ULL << (type & 0x3f)))
211         return 1;
212
213     /* Don't introduce spurious end markers */
214     if (type == 127)
215         return 0;
216
217     for (i = qemu_cfg_smbios_entries(); i > 0; i--) {
218         struct smbios_table table;
219         struct smbios_structure_header *header = (void *)*p;
220         int string;
221
222         qemu_cfg_read((u8 *)&table, sizeof(struct smbios_header));
223         table.header.length -= sizeof(struct smbios_header);
224
225         if (table.header.type != SMBIOS_TABLE_ENTRY) {
226             qemu_cfg_skip(table.header.length);
227             continue;
228         }
229
230         if (end - *p < sizeof(struct smbios_structure_header)) {
231             warn_noalloc();
232             break;
233         }
234
235         qemu_cfg_read((u8 *)*p, sizeof(struct smbios_structure_header));
236         table.header.length -= sizeof(struct smbios_structure_header);
237
238         if (header->type != type) {
239             qemu_cfg_skip(table.header.length);
240             continue;
241         }
242
243         *p += sizeof(struct smbios_structure_header);
244
245         /* Entries end with a double NULL char, if there's a string at
246          * the end (length is greater than formatted length), the string
247          * terminator provides the first NULL. */
248         string = header->length < table.header.length +
249                  sizeof(struct smbios_structure_header);
250
251         /* Read the rest and terminate the entry */
252         if (end - *p < table.header.length) {
253             warn_noalloc();
254             *p -= sizeof(struct smbios_structure_header);
255             continue;
256         }
257         qemu_cfg_read((u8 *)*p, table.header.length);
258         *p += table.header.length;
259         *((u8*)*p) = 0;
260         (*p)++;
261         if (!string) {
262             *((u8*)*p) = 0;
263             (*p)++;
264         }
265
266         (*nr_structs)++;
267         if (*p - (char *)header > *max_struct_size)
268             *max_struct_size = *p - (char *)header;
269     }
270
271     if (start != *p) {
272         /* Mark that we've reported on this type */
273         used_bitmap[(type >> 6) & 0x3] |= (1ULL << (type & 0x3f));
274         return 1;
275     }
276
277     return 0;
278 }
279
280 int qemu_cfg_get_numa_nodes(void)
281 {
282     u64 cnt;
283
284     qemu_cfg_read_entry(&cnt, QEMU_CFG_NUMA, sizeof(cnt));
285
286     return (int)cnt;
287 }
288
289 void qemu_cfg_get_numa_data(u64 *data, int n)
290 {
291     int i;
292
293     for (i = 0; i < n; i++)
294         qemu_cfg_read((u8*)(data + i), sizeof(u64));
295 }
296
297 u16 qemu_cfg_get_max_cpus(void)
298 {
299     u16 cnt;
300
301     if (!qemu_cfg_present)
302         return 0;
303
304     qemu_cfg_read_entry(&cnt, QEMU_CFG_MAX_CPUS, sizeof(cnt));
305
306     return cnt;
307 }
308
309 static QemuCfgFile LastFile;
310
311 static u32
312 __cfg_next_prefix_file(const char *prefix, int prefixlen, u32 prevselect)
313 {
314     if (!qemu_cfg_present)
315         return 0;
316
317     u32 count;
318     qemu_cfg_read_entry(&count, QEMU_CFG_FILE_DIR, sizeof(count));
319     count = ntohl(count);
320     u32 e;
321     for (e = 0; e < count; e++) {
322         qemu_cfg_read((void*)&LastFile, sizeof(LastFile));
323         u32 select = ntohs(LastFile.select);
324         if (select <= prevselect)
325             continue;
326         if (memcmp(prefix, LastFile.name, prefixlen) == 0)
327             return select;
328     }
329     return 0;
330 }
331
332 u32 qemu_cfg_next_prefix_file(const char *prefix, u32 prevselect)
333 {
334     return __cfg_next_prefix_file(prefix, strlen(prefix), prevselect);
335 }
336
337 u32 qemu_cfg_find_file(const char *name)
338 {
339     return __cfg_next_prefix_file(name, strlen(name) + 1, 0);
340 }
341
342 int qemu_cfg_read_file(u32 select, void *dst, u32 maxlen)
343 {
344     if (!qemu_cfg_present)
345         return -1;
346     if (!select || select != ntohs(LastFile.select))
347         return -1;
348     int len = ntohl(LastFile.size);
349     if (len > maxlen)
350         return -1;
351     qemu_cfg_read_entry(dst, select, len);
352     return len;
353 }