blob: 4b5c4417dddb44762b0527449abc03d93ca5eda7 [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
Kevin O'Connorb3064592012-08-14 21:20:10 -040011#include "util.h" // dprintf
12#include "byteorder.h" // be32_to_cpu
Kevin O'Connor01a85202009-10-18 09:49:59 -040013#include "ioport.h" // outw
14#include "paravirt.h" // qemu_cfg_port_probe
15#include "smbios.h" // struct smbios_structure_header
Kevin O'Connore7cc7642009-10-04 10:05:16 -040016
17int qemu_cfg_present;
18
19static void
20qemu_cfg_select(u16 f)
21{
22 outw(f, PORT_QEMU_CFG_CTL);
23}
24
25static void
26qemu_cfg_read(u8 *buf, int len)
27{
Kevin O'Connor6039fc52010-08-25 21:43:19 -040028 insb(PORT_QEMU_CFG_DATA, buf, len);
Kevin O'Connore7cc7642009-10-04 10:05:16 -040029}
30
31static void
Kevin O'Connor4e4b4102009-10-08 21:21:59 -040032qemu_cfg_skip(int len)
33{
34 while (len--)
35 inb(PORT_QEMU_CFG_DATA);
36}
37
38static void
Kevin O'Connore7cc7642009-10-04 10:05:16 -040039qemu_cfg_read_entry(void *buf, int e, int len)
40{
41 qemu_cfg_select(e);
42 qemu_cfg_read(buf, len);
43}
44
45void 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
65void 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
73int 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
Kevin O'Connor4d2b6192009-10-08 21:37:21 -040084int 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
Kevin O'Connorcc6dc462009-10-08 21:18:41 -040096u16 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
108u16 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
117void* qemu_cfg_next_acpi_table_load(void *addr, u16 len)
118{
119 qemu_cfg_read(addr, len);
120 return addr;
121}
122
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400123u16 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
Jes Sorensen0360e8e2010-02-16 09:46:08 +0100135u32 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
146void* qemu_cfg_e820_load_next(void *addr)
147{
148 qemu_cfg_read(addr, sizeof(struct e820_reservation));
149 return addr;
150}
151
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400152struct smbios_header {
153 u16 length;
154 u8 type;
155} PACKED;
156
157struct smbios_field {
158 struct smbios_header header;
159 u8 type;
160 u16 offset;
161 u8 data[];
162} PACKED;
163
164struct 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
172size_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
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400202int 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)) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500231 warn_noalloc();
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400232 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) {
Kevin O'Connorcfdc13f2010-02-14 13:07:54 -0500253 warn_noalloc();
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400254 *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
Kevin O'Connor590e5542009-10-08 22:09:02 -0400280int 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
289void 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}
Kevin O'Connor84705852009-10-08 22:13:15 -0400296
297u16 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}
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100308
Kevin O'Connor59d6ca52012-05-31 00:20:55 -0400309int qemu_cfg_read_file(struct romfile_s *file, void *dst, u32 maxlen)
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100310{
Kevin O'Connor59d6ca52012-05-31 00:20:55 -0400311 if (file->size > maxlen)
312 return -1;
313 qemu_cfg_read_entry(dst, file->id, file->size);
314 return file->size;
315}
316
317struct QemuCfgFile {
318 u32 size; /* file size */
319 u16 select; /* write this to 0x510 to read it */
320 u16 reserved;
321 char name[56];
322};
323
324void qemu_cfg_romfile_setup(void)
325{
326 if (CONFIG_COREBOOT || !qemu_cfg_present)
327 return;
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100328
Kevin O'Connore2304262010-06-13 16:05:17 -0400329 u32 count;
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100330 qemu_cfg_read_entry(&count, QEMU_CFG_FILE_DIR, sizeof(count));
Kevin O'Connorb3064592012-08-14 21:20:10 -0400331 count = be32_to_cpu(count);
Kevin O'Connore2304262010-06-13 16:05:17 -0400332 u32 e;
333 for (e = 0; e < count; e++) {
Kevin O'Connor59d6ca52012-05-31 00:20:55 -0400334 struct QemuCfgFile qfile;
335 qemu_cfg_read((void*)&qfile, sizeof(qfile));
336 struct romfile_s *file = malloc_tmp(sizeof(*file));
337 if (!file) {
338 warn_noalloc();
339 return;
340 }
341 memset(file, 0, sizeof(*file));
342 strtcpy(file->name, qfile.name, sizeof(file->name));
Kevin O'Connorb3064592012-08-14 21:20:10 -0400343 file->size = be32_to_cpu(qfile.size);
344 file->id = be16_to_cpu(qfile.select);
Kevin O'Connor59d6ca52012-05-31 00:20:55 -0400345 file->copy = qemu_cfg_read_file;
346 romfile_add(file);
347 dprintf(3, "Found fw_cfg file: %s (size=%d)\n", file->name, file->size);
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100348 }
Kevin O'Connor8b565782011-07-05 20:32:44 -0400349}