blob: ca646d4366b57bc5bf8fd288558f33c2f9292857 [file] [log] [blame]
Kevin O'Connore7cc7642009-10-04 10:05:16 -04001// 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
Kevin O'Connor01a85202009-10-18 09:49:59 -040010#include "config.h" // CONFIG_COREBOOT
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +010011#include "util.h" // ntoh[ls]
Kevin O'Connor01a85202009-10-18 09:49:59 -040012#include "ioport.h" // outw
13#include "paravirt.h" // qemu_cfg_port_probe
14#include "smbios.h" // struct smbios_structure_header
Kevin O'Connore7cc7642009-10-04 10:05:16 -040015
16int qemu_cfg_present;
17
18static void
19qemu_cfg_select(u16 f)
20{
21 outw(f, PORT_QEMU_CFG_CTL);
22}
23
24static void
25qemu_cfg_read(u8 *buf, int len)
26{
Kevin O'Connor6039fc52010-08-25 21:43:19 -040027 insb(PORT_QEMU_CFG_DATA, buf, len);
Kevin O'Connore7cc7642009-10-04 10:05:16 -040028}
29
30static void
Kevin O'Connor4e4b4102009-10-08 21:21:59 -040031qemu_cfg_skip(int len)
32{
33 while (len--)
34 inb(PORT_QEMU_CFG_DATA);
35}
36
37static void
Kevin O'Connore7cc7642009-10-04 10:05:16 -040038qemu_cfg_read_entry(void *buf, int e, int len)
39{
40 qemu_cfg_select(e);
41 qemu_cfg_read(buf, len);
42}
43
44void 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
64void 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
72int 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
Kevin O'Connor4d2b6192009-10-08 21:37:21 -040083int 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
Kevin O'Connorcc6dc462009-10-08 21:18:41 -040095u16 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
107u16 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
116void* qemu_cfg_next_acpi_table_load(void *addr, u16 len)
117{
118 qemu_cfg_read(addr, len);
119 return addr;
120}
121
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400122u16 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
Jes Sorensen0360e8e2010-02-16 09:46:08 +0100134u32 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
145void* qemu_cfg_e820_load_next(void *addr)
146{
147 qemu_cfg_read(addr, sizeof(struct e820_reservation));
148 return addr;
149}
150
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400151struct smbios_header {
152 u16 length;
153 u8 type;
154} PACKED;
155
156struct smbios_field {
157 struct smbios_header header;
158 u8 type;
159 u16 offset;
160 u8 data[];
161} PACKED;
162
163struct 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
171size_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
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400201int 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)) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500230 warn_noalloc();
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400231 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) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500252 warn_noalloc();
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400253 *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
Kevin O'Connor590e5542009-10-08 22:09:02 -0400279int 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
288void 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}
Kevin O'Connor84705852009-10-08 22:13:15 -0400295
296u16 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}
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100307
Kevin O'Connore2304262010-06-13 16:05:17 -0400308static QemuCfgFile LastFile;
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100309
Kevin O'Connore2304262010-06-13 16:05:17 -0400310static u32
311__cfg_next_prefix_file(const char *prefix, int prefixlen, u32 prevselect)
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100312{
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100313 if (!qemu_cfg_present)
314 return 0;
315
Kevin O'Connore2304262010-06-13 16:05:17 -0400316 u32 count;
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100317 qemu_cfg_read_entry(&count, QEMU_CFG_FILE_DIR, sizeof(count));
Kevin O'Connore2304262010-06-13 16:05:17 -0400318 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;
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100327 }
328 return 0;
329}
330
Kevin O'Connore2304262010-06-13 16:05:17 -0400331u32 qemu_cfg_next_prefix_file(const char *prefix, u32 prevselect)
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100332{
Kevin O'Connore2304262010-06-13 16:05:17 -0400333 return __cfg_next_prefix_file(prefix, strlen(prefix), prevselect);
334}
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100335
Kevin O'Connore2304262010-06-13 16:05:17 -0400336u32 qemu_cfg_find_file(const char *name)
337{
338 return __cfg_next_prefix_file(name, strlen(name) + 1, 0);
339}
340
Kevin O'Connorf79a4622010-07-04 08:32:38 -0400341int qemu_cfg_size_file(u32 select)
342{
343 if (select != ntohs(LastFile.select))
344 return -1;
345 return ntohl(LastFile.size);
346}
347
Kevin O'Connore2304262010-06-13 16:05:17 -0400348int qemu_cfg_read_file(u32 select, void *dst, u32 maxlen)
349{
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100350 if (!qemu_cfg_present)
Kevin O'Connore2304262010-06-13 16:05:17 -0400351 return -1;
352 if (!select || select != ntohs(LastFile.select))
353 return -1;
Kevin O'Connorf79a4622010-07-04 08:32:38 -0400354 int len = qemu_cfg_size_file(select);
355 if (len < 0 || len > maxlen)
Kevin O'Connore2304262010-06-13 16:05:17 -0400356 return -1;
357 qemu_cfg_read_entry(dst, select, len);
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100358 return len;
359}