Make SMBIOS table pass MS SVVP test
[seabios.git] / src / smbios.c
1 // smbios table generation (on emulators)
2 //
3 // Copyright (C) 2008,2009  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2006 Fabrice Bellard
5 //
6 // This file may be distributed under the terms of the GNU LGPLv3 license.
7
8 #include "util.h" // dprintf
9 #include "biosvar.h" // GET_EBDA
10 #include "paravirt.h" // qemu_cfg_smbios_load_field
11 #include "smbios.h" // struct smbios_entry_point
12
13 static void
14 smbios_entry_point_init(u16 max_structure_size,
15                         u16 structure_table_length,
16                         void *structure_table_address,
17                         u16 number_of_structures)
18 {
19     struct smbios_entry_point *ep = malloc_fseg(sizeof(*ep));
20     if (! ep) {
21         dprintf(1, "No space for smbios entry table!\n");
22         return;
23     }
24
25     memcpy(ep->anchor_string, "_SM_", 4);
26     ep->length = 0x1f;
27     ep->smbios_major_version = 2;
28     ep->smbios_minor_version = 4;
29     ep->max_structure_size = max_structure_size;
30     ep->entry_point_revision = 0;
31     memset(ep->formatted_area, 0, 5);
32     memcpy(ep->intermediate_anchor_string, "_DMI_", 5);
33
34     ep->structure_table_length = structure_table_length;
35     ep->structure_table_address = (u32)structure_table_address;
36     ep->number_of_structures = number_of_structures;
37     ep->smbios_bcd_revision = 0x24;
38
39     ep->checksum -= checksum(ep, 0x10);
40
41     ep->intermediate_checksum -= checksum((void*)ep + 0x10, ep->length - 0x10);
42
43     dprintf(1, "SMBIOS ptr=%p table=%p\n", ep, structure_table_address);
44 }
45
46 #define load_str_field_with_default(type, field, def)                   \
47     do {                                                                \
48         size = qemu_cfg_smbios_load_field(type,                         \
49                                  offsetof(struct smbios_type_##type,    \
50                                           field), end);                 \
51         if (size > 0) {                                                 \
52             end += size;                                                \
53         } else {                                                        \
54             memcpy(end, def, sizeof(def));                              \
55             end += sizeof(def);                                         \
56         }                                                               \
57         p->field = ++str_index;                                         \
58     } while (0)
59
60 #define load_str_field_or_skip(type, field) \
61     do {                                                                \
62         size = qemu_cfg_smbios_load_field(type,                         \
63                                  offsetof(struct smbios_type_##type,    \
64                                           field), end);                 \
65         if (size > 0) {                                                 \
66             end += size;                                                \
67             p->field = ++str_index;                                     \
68         } else {                                                        \
69             p->field = 0;                                               \
70         }                                                               \
71     } while (0)
72
73 /* Type 0 -- BIOS Information */
74 #define RELEASE_DATE_STR "01/01/2007"
75 static void *
76 smbios_init_type_0(void *start)
77 {
78     struct smbios_type_0 *p = (struct smbios_type_0 *)start;
79     char *end = (char *)start + sizeof(struct smbios_type_0);
80     size_t size;
81     int str_index = 0;
82
83     p->header.type = 0;
84     p->header.length = sizeof(struct smbios_type_0);
85     p->header.handle = 0;
86
87     load_str_field_with_default(0, vendor_str, CONFIG_APPNAME);
88     load_str_field_with_default(0, bios_version_str, CONFIG_APPNAME);
89
90     p->bios_starting_address_segment = 0xe800;
91
92     load_str_field_with_default(0, bios_release_date_str, RELEASE_DATE_STR);
93
94     p->bios_rom_size = 0; /* FIXME */
95
96     memset(p->bios_characteristics, 0, 8);
97     p->bios_characteristics[0] = 0x08; /* BIOS characteristics not supported */
98     p->bios_characteristics_extension_bytes[0] = 0;
99     /* Enable targeted content distribution. Needed for SVVP */
100     p->bios_characteristics_extension_bytes[1] = 4;
101
102     if (!qemu_cfg_smbios_load_field(0, offsetof(struct smbios_type_0,
103                                                 system_bios_major_release),
104                                     &p->system_bios_major_release))
105         p->system_bios_major_release = 1;
106
107     if (!qemu_cfg_smbios_load_field(0, offsetof(struct smbios_type_0,
108                                                 system_bios_minor_release),
109                                     &p->system_bios_minor_release))
110         p->system_bios_minor_release = 0;
111
112     p->embedded_controller_major_release = 0xff;
113     p->embedded_controller_minor_release = 0xff;
114
115     *end = 0;
116     end++;
117
118     return end;
119 }
120
121 /* Type 1 -- System Information */
122 static void *
123 smbios_init_type_1(void *start)
124 {
125     struct smbios_type_1 *p = (struct smbios_type_1 *)start;
126     char *end = (char *)start + sizeof(struct smbios_type_1);
127     size_t size;
128     int str_index = 0;
129
130     p->header.type = 1;
131     p->header.length = sizeof(struct smbios_type_1);
132     p->header.handle = 0x100;
133
134     load_str_field_with_default(1, manufacturer_str, CONFIG_APPNAME);
135     load_str_field_with_default(1, product_name_str, CONFIG_APPNAME);
136     load_str_field_or_skip(1, version_str);
137     load_str_field_or_skip(1, serial_number_str);
138
139     size = qemu_cfg_smbios_load_field(1, offsetof(struct smbios_type_1,
140                                                   uuid), &p->uuid);
141     if (size == 0)
142         memset(p->uuid, 0, 16);
143
144     p->wake_up_type = 0x06; /* power switch */
145
146     load_str_field_or_skip(1, sku_number_str);
147     load_str_field_or_skip(1, family_str);
148
149     *end = 0;
150     end++;
151     if (!str_index) {
152         *end = 0;
153         end++;
154     }
155
156     return end;
157 }
158
159 /* Type 3 -- System Enclosure */
160 static void *
161 smbios_init_type_3(void *start)
162 {
163     struct smbios_type_3 *p = (struct smbios_type_3 *)start;
164
165     p->header.type = 3;
166     p->header.length = sizeof(struct smbios_type_3);
167     p->header.handle = 0x300;
168
169     p->manufacturer_str = 1;
170     p->type = 0x01; /* other */
171     p->version_str = 0;
172     p->serial_number_str = 0;
173     p->asset_tag_number_str = 0;
174     p->boot_up_state = 0x03; /* safe */
175     p->power_supply_state = 0x03; /* safe */
176     p->thermal_state = 0x03; /* safe */
177     p->security_status = 0x02; /* unknown */
178     p->oem_defined = 0;
179     p->height = 0;
180     p->number_of_power_cords = 0;
181     p->contained_element_count = 0;
182
183     start += sizeof(struct smbios_type_3);
184     memcpy((char *)start, CONFIG_APPNAME"\0\0", sizeof(CONFIG_APPNAME) + 1);
185
186     return start + sizeof(CONFIG_APPNAME) + 1;
187 }
188
189 /* Type 4 -- Processor Information */
190 static void *
191 smbios_init_type_4(void *start, unsigned int cpu_number)
192 {
193     struct smbios_type_4 *p = (struct smbios_type_4 *)start;
194
195     p->header.type = 4;
196     p->header.length = sizeof(struct smbios_type_4);
197     p->header.handle = 0x400 + cpu_number;
198
199     p->socket_designation_str = 1;
200     p->processor_type = 0x03; /* CPU */
201     p->processor_family = 0x01; /* other */
202     p->processor_manufacturer_str = 2;
203
204     u32 cpuid_signature, ebx, ecx, cpuid_features;
205     cpuid(1, &cpuid_signature, &ebx, &ecx, &cpuid_features);
206     p->processor_id[0] = cpuid_signature;
207     p->processor_id[1] = cpuid_features;
208
209     p->processor_version_str = 0;
210     p->voltage = 0;
211     p->external_clock = 0;
212
213     p->max_speed = 2000;
214     p->current_speed = 2000;
215
216     p->status = 0x41; /* socket populated, CPU enabled */
217     p->processor_upgrade = 0x01; /* other */
218
219     p->l1_cache_handle = 0xffff; /* cache information structure not provided */
220     p->l2_cache_handle = 0xffff;
221     p->l3_cache_handle = 0xffff;
222
223     start += sizeof(struct smbios_type_4);
224
225     snprintf((char*)start, 6, "CPU%2x", cpu_number);
226     start += 6;
227     memcpy((char *)start, CONFIG_APPNAME"\0\0", sizeof(CONFIG_APPNAME) + 1);
228
229     return start + sizeof(CONFIG_APPNAME) + 1;
230 }
231
232 /* Type 16 -- Physical Memory Array */
233 static void *
234 smbios_init_type_16(void *start, u32 memory_size_mb, int nr_mem_devs)
235 {
236     struct smbios_type_16 *p = (struct smbios_type_16*)start;
237
238     p->header.type = 16;
239     p->header.length = sizeof(struct smbios_type_16);
240     p->header.handle = 0x1000;
241
242     p->location = 0x01; /* other */
243     p->use = 0x03; /* system memory */
244     p->error_correction = 0x06; /* Multi-bit ECC to make Microsoft happy */
245     p->maximum_capacity = memory_size_mb * 1024;
246     p->memory_error_information_handle = 0xfffe; /* none provided */
247     p->number_of_memory_devices = nr_mem_devs;
248
249     start += sizeof(struct smbios_type_16);
250     *((u16 *)start) = 0;
251
252     return start + 2;
253 }
254
255 /* Type 17 -- Memory Device */
256 static void *
257 smbios_init_type_17(void *start, u32 memory_size_mb, int instance)
258 {
259     struct smbios_type_17 *p = (struct smbios_type_17 *)start;
260
261     p->header.type = 17;
262     p->header.length = sizeof(struct smbios_type_17);
263     p->header.handle = 0x1100 + instance;
264
265     p->physical_memory_array_handle = 0x1000;
266     p->total_width = 64;
267     p->data_width = 64;
268 /* TODO: should assert in case something is wrong   ASSERT((memory_size_mb & ~0x7fff) == 0); */
269     p->size = memory_size_mb;
270     p->form_factor = 0x09; /* DIMM */
271     p->device_set = 0;
272     p->device_locator_str = 1;
273     p->bank_locator_str = 0;
274     p->memory_type = 0x07; /* RAM */
275     p->type_detail = 0;
276
277     start += sizeof(struct smbios_type_17);
278     memcpy((char *)start, "DIMM 0", 7);
279     ((char*)start)[5] += instance;
280     start += 7;
281     *((u8 *)start) = 0;
282
283     return start+1;
284 }
285
286 /* Type 19 -- Memory Array Mapped Address */
287 static void *
288 smbios_init_type_19(void *start, u32 memory_size_mb, int instance)
289 {
290     struct smbios_type_19 *p = (struct smbios_type_19 *)start;
291
292     p->header.type = 19;
293     p->header.length = sizeof(struct smbios_type_19);
294     p->header.handle = 0x1300 + instance;
295
296     p->starting_address = instance << 24;
297     p->ending_address = p->starting_address + (memory_size_mb << 10) - 1;
298     p->memory_array_handle = 0x1000;
299     p->partition_width = 1;
300
301     start += sizeof(struct smbios_type_19);
302     *((u16 *)start) = 0;
303
304     return start + 2;
305 }
306
307 /* Type 20 -- Memory Device Mapped Address */
308 static void *
309 smbios_init_type_20(void *start, u32 memory_size_mb, int instance)
310 {
311     struct smbios_type_20 *p = (struct smbios_type_20 *)start;
312
313     p->header.type = 20;
314     p->header.length = sizeof(struct smbios_type_20);
315     p->header.handle = 0x1400 + instance;
316
317     p->starting_address = instance << 24;
318     p->ending_address = p->starting_address + (memory_size_mb << 10) - 1;
319     p->memory_device_handle = 0x1100 + instance;
320     p->memory_array_mapped_address_handle = 0x1300 + instance;
321     p->partition_row_position = 1;
322     p->interleave_position = 0;
323     p->interleaved_data_depth = 0;
324
325     start += sizeof(struct smbios_type_20);
326
327     *((u16 *)start) = 0;
328     return start+2;
329 }
330
331 /* Type 32 -- System Boot Information */
332 static void *
333 smbios_init_type_32(void *start)
334 {
335     struct smbios_type_32 *p = (struct smbios_type_32 *)start;
336
337     p->header.type = 32;
338     p->header.length = sizeof(struct smbios_type_32);
339     p->header.handle = 0x2000;
340     memset(p->reserved, 0, 6);
341     p->boot_status = 0; /* no errors detected */
342
343     start += sizeof(struct smbios_type_32);
344     *((u16 *)start) = 0;
345
346     return start+2;
347 }
348
349 /* Type 127 -- End of Table */
350 static void *
351 smbios_init_type_127(void *start)
352 {
353     struct smbios_type_127 *p = (struct smbios_type_127 *)start;
354
355     p->header.type = 127;
356     p->header.length = sizeof(struct smbios_type_127);
357     p->header.handle = 0x7f00;
358
359     start += sizeof(struct smbios_type_127);
360     *((u16 *)start) = 0;
361
362     return start + 2;
363 }
364
365 void
366 smbios_init(void)
367 {
368     if (! CONFIG_SMBIOS)
369         return;
370
371     dprintf(3, "init SMBIOS tables\n");
372
373     char *start = malloc_high(2048); // XXX - determine real size
374     if (! start) {
375         dprintf(1, "No memory for smbios tables\n");
376         return;
377     }
378
379     u32 nr_structs = 0, max_struct_size = 0;
380     char *q, *p = start, *end = start + 2048 - sizeof(struct smbios_type_127);
381
382 #define add_struct(type, args...)                                       \
383     do {                                                                \
384         if (!qemu_cfg_smbios_load_external(type, &p, &nr_structs,       \
385                                            &max_struct_size, end)) {    \
386             q = smbios_init_type_##type(args);                          \
387             nr_structs++;                                               \
388             if ((q - p) > max_struct_size)                              \
389                 max_struct_size = q - p;                                \
390             p = q;                                                      \
391         }                                                               \
392     } while (0)
393
394     add_struct(0, p);
395     add_struct(1, p);
396     add_struct(3, p);
397
398     int cpu_num;
399     for (cpu_num = 1; cpu_num <= MaxCountCPUs; cpu_num++)
400         add_struct(4, p, cpu_num);
401     u64 memsize = RamSizeOver4G;
402     if (memsize)
403         memsize += 0x100000000ull;
404     else
405         memsize = RamSize;
406     memsize = memsize / (1024 * 1024);
407     int nr_mem_devs = (memsize + 0x3fff) >> 14;
408     add_struct(16, p, memsize, nr_mem_devs);
409     int i;
410     for (i = 0; i < nr_mem_devs; i++) {
411         u32 dev_memsize = ((i == (nr_mem_devs - 1))
412                            ? (((memsize-1) & 0x3fff)+1) : 0x4000);
413         add_struct(17, p, dev_memsize, i);
414         add_struct(19, p, dev_memsize, i);
415         add_struct(20, p, dev_memsize, i);
416     }
417
418     add_struct(32, p);
419     /* Add any remaining provided entries before the end marker */
420     for (i = 0; i < 256; i++)
421         qemu_cfg_smbios_load_external(i, &p, &nr_structs, &max_struct_size,
422                                       end);
423     add_struct(127, p);
424
425 #undef add_struct
426
427     smbios_entry_point_init(max_struct_size, p - start, start, nr_structs);
428 }