blob: 2a98d53ed058dde2b0a783d8035483337f1456da [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'Connor59d6ca52012-05-31 00:20:55 -0400308int qemu_cfg_read_file(struct romfile_s *file, void *dst, u32 maxlen)
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100309{
Kevin O'Connor59d6ca52012-05-31 00:20:55 -0400310 if (file->size > maxlen)
311 return -1;
312 qemu_cfg_read_entry(dst, file->id, file->size);
313 return file->size;
314}
315
316struct QemuCfgFile {
317 u32 size; /* file size */
318 u16 select; /* write this to 0x510 to read it */
319 u16 reserved;
320 char name[56];
321};
322
323void qemu_cfg_romfile_setup(void)
324{
325 if (CONFIG_COREBOOT || !qemu_cfg_present)
326 return;
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100327
Kevin O'Connore2304262010-06-13 16:05:17 -0400328 u32 count;
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100329 qemu_cfg_read_entry(&count, QEMU_CFG_FILE_DIR, sizeof(count));
Kevin O'Connore2304262010-06-13 16:05:17 -0400330 count = ntohl(count);
331 u32 e;
332 for (e = 0; e < count; e++) {
Kevin O'Connor59d6ca52012-05-31 00:20:55 -0400333 struct QemuCfgFile qfile;
334 qemu_cfg_read((void*)&qfile, sizeof(qfile));
335 struct romfile_s *file = malloc_tmp(sizeof(*file));
336 if (!file) {
337 warn_noalloc();
338 return;
339 }
340 memset(file, 0, sizeof(*file));
341 strtcpy(file->name, qfile.name, sizeof(file->name));
342 file->size = ntohl(qfile.size);
343 file->id = ntohs(qfile.select);
344 file->copy = qemu_cfg_read_file;
345 romfile_add(file);
346 dprintf(3, "Found fw_cfg file: %s (size=%d)\n", file->name, file->size);
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100347 }
Kevin O'Connor8b565782011-07-05 20:32:44 -0400348}