blob: 7171bac2bfe99beec790720e45488b49d52c054a [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{
27 while (len--)
28 *(buf++) = inb(PORT_QEMU_CFG_DATA);
29}
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
135struct smbios_header {
136 u16 length;
137 u8 type;
138} PACKED;
139
140struct smbios_field {
141 struct smbios_header header;
142 u8 type;
143 u16 offset;
144 u8 data[];
145} PACKED;
146
147struct smbios_table {
148 struct smbios_header header;
149 u8 data[];
150} PACKED;
151
152#define SMBIOS_FIELD_ENTRY 0
153#define SMBIOS_TABLE_ENTRY 1
154
155size_t qemu_cfg_smbios_load_field(int type, size_t offset, void *addr)
156{
157 int i;
158
159 for (i = qemu_cfg_smbios_entries(); i > 0; i--) {
160 struct smbios_field field;
161
162 qemu_cfg_read((u8 *)&field, sizeof(struct smbios_header));
163 field.header.length -= sizeof(struct smbios_header);
164
165 if (field.header.type != SMBIOS_FIELD_ENTRY) {
166 qemu_cfg_skip(field.header.length);
167 continue;
168 }
169
170 qemu_cfg_read((u8 *)&field.type,
171 sizeof(field) - sizeof(struct smbios_header));
172 field.header.length -= sizeof(field) - sizeof(struct smbios_header);
173
174 if (field.type != type || field.offset != offset) {
175 qemu_cfg_skip(field.header.length);
176 continue;
177 }
178
179 qemu_cfg_read(addr, field.header.length);
180 return (size_t)field.header.length;
181 }
182 return 0;
183}
184
Kevin O'Connor4e4b4102009-10-08 21:21:59 -0400185int qemu_cfg_smbios_load_external(int type, char **p, unsigned *nr_structs,
186 unsigned *max_struct_size, char *end)
187{
188 static u64 used_bitmap[4] = { 0 };
189 char *start = *p;
190 int i;
191
192 /* Check if we've already reported these tables */
193 if (used_bitmap[(type >> 6) & 0x3] & (1ULL << (type & 0x3f)))
194 return 1;
195
196 /* Don't introduce spurious end markers */
197 if (type == 127)
198 return 0;
199
200 for (i = qemu_cfg_smbios_entries(); i > 0; i--) {
201 struct smbios_table table;
202 struct smbios_structure_header *header = (void *)*p;
203 int string;
204
205 qemu_cfg_read((u8 *)&table, sizeof(struct smbios_header));
206 table.header.length -= sizeof(struct smbios_header);
207
208 if (table.header.type != SMBIOS_TABLE_ENTRY) {
209 qemu_cfg_skip(table.header.length);
210 continue;
211 }
212
213 if (end - *p < sizeof(struct smbios_structure_header)) {
214 dprintf(1, "No more memory for additional smbios tables\n");
215 break;
216 }
217
218 qemu_cfg_read((u8 *)*p, sizeof(struct smbios_structure_header));
219 table.header.length -= sizeof(struct smbios_structure_header);
220
221 if (header->type != type) {
222 qemu_cfg_skip(table.header.length);
223 continue;
224 }
225
226 *p += sizeof(struct smbios_structure_header);
227
228 /* Entries end with a double NULL char, if there's a string at
229 * the end (length is greater than formatted length), the string
230 * terminator provides the first NULL. */
231 string = header->length < table.header.length +
232 sizeof(struct smbios_structure_header);
233
234 /* Read the rest and terminate the entry */
235 if (end - *p < table.header.length) {
236 dprintf(1, "No memory for smbios table %d\n", header->type);
237 *p -= sizeof(struct smbios_structure_header);
238 continue;
239 }
240 qemu_cfg_read((u8 *)*p, table.header.length);
241 *p += table.header.length;
242 *((u8*)*p) = 0;
243 (*p)++;
244 if (!string) {
245 *((u8*)*p) = 0;
246 (*p)++;
247 }
248
249 (*nr_structs)++;
250 if (*p - (char *)header > *max_struct_size)
251 *max_struct_size = *p - (char *)header;
252 }
253
254 if (start != *p) {
255 /* Mark that we've reported on this type */
256 used_bitmap[(type >> 6) & 0x3] |= (1ULL << (type & 0x3f));
257 return 1;
258 }
259
260 return 0;
261}
262
Kevin O'Connor590e5542009-10-08 22:09:02 -0400263int qemu_cfg_get_numa_nodes(void)
264{
265 u64 cnt;
266
267 qemu_cfg_read_entry(&cnt, QEMU_CFG_NUMA, sizeof(cnt));
268
269 return (int)cnt;
270}
271
272void qemu_cfg_get_numa_data(u64 *data, int n)
273{
274 int i;
275
276 for (i = 0; i < n; i++)
277 qemu_cfg_read((u8*)(data + i), sizeof(u64));
278}
Kevin O'Connor84705852009-10-08 22:13:15 -0400279
280u16 qemu_cfg_get_max_cpus(void)
281{
282 u16 cnt;
283
284 if (!qemu_cfg_present)
285 return 0;
286
287 qemu_cfg_read_entry(&cnt, QEMU_CFG_MAX_CPUS, sizeof(cnt));
288
289 return cnt;
290}
Gerd Hoffmannc4c9fae2009-12-18 12:16:04 +0100291
292u16 qemu_cfg_first_file(QemuCfgFile *entry)
293{
294 memset(entry, 0, sizeof(*entry));
295 return qemu_cfg_next_file(entry);
296}
297
298u16 qemu_cfg_next_file(QemuCfgFile *entry)
299{
300 u16 last = ntohs(entry->select);
301 u32 e,count;
302
303 if (!qemu_cfg_present)
304 return 0;
305
306 qemu_cfg_read_entry(&count, QEMU_CFG_FILE_DIR, sizeof(count));
307 for (e = 0; e < ntohl(count); e++) {
308 qemu_cfg_read((void*)entry, sizeof(*entry));
309 if (ntohs(entry->select) > last) {
310 return 1;
311 }
312 }
313 return 0;
314}
315
316u32 qemu_cfg_read_file(QemuCfgFile *entry, void *dst, u32 maxlen)
317{
318 int len = ntohl(entry->size);
319
320 if (!qemu_cfg_present)
321 return 0;
322 if (len > maxlen)
323 return 0;
324 qemu_cfg_read_entry(dst, ntohs(entry->select), len);
325 return len;
326}