blob: 305ba40fe1cb551d7efcad924849cdf39c46d354 [file] [log] [blame]
Sven Schnelle164bcfd2011-08-14 20:56:34 +02001/*
2 * This file is part of the coreboot project.
3 *
Timothy Pearson821217b2015-03-27 22:47:25 -05004 * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering
Sven Schnelle164bcfd2011-08-14 20:56:34 +02005 * Copyright (C) 2011 Sven Schnelle <svens@stackframe.org>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; version 2 of
10 * the License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
Sven Schnelle164bcfd2011-08-14 20:56:34 +020016 */
17
18#include <stdlib.h>
19#include <string.h>
20#include <smbios.h>
21#include <console/console.h>
Kyösti Mälkkic36af7b2014-11-18 12:41:16 +020022#include <version.h>
Sven Schnelle164bcfd2011-08-14 20:56:34 +020023#include <device/device.h>
24#include <arch/cpu.h>
25#include <cpu/x86/name.h>
Duncan Laurie472ec9c2012-06-23 16:13:42 -070026#include <elog.h>
Julius Werner9ff8f6f2015-02-23 14:31:09 -080027#include <endian.h>
Kane Chen33faac62014-07-27 12:54:44 -070028#include <memory_info.h>
29#include <spd.h>
30#include <cbmem.h>
Stefan Reinauerc6b21662012-04-03 16:02:54 -070031#if CONFIG_CHROMEOS
32#include <vendorcode/google/chromeos/gnvs.h>
33#endif
Sven Schnelle164bcfd2011-08-14 20:56:34 +020034
35static u8 smbios_checksum(u8 *p, u32 length)
36{
37 u8 ret = 0;
38 while (length--)
39 ret += *p++;
40 return -ret;
41}
42
43
44int smbios_add_string(char *start, const char *str)
45{
46 int i = 1;
47 char *p = start;
48
Ben Gardner6b07cba2015-12-09 11:24:35 -060049 /*
50 * Return 0 as required for empty strings.
51 * See Section 6.1.3 "Text Strings" of the SMBIOS specification.
52 */
53 if (*str == '\0')
54 return 0;
55
Sven Schnelle164bcfd2011-08-14 20:56:34 +020056 for(;;) {
57 if (!*p) {
58 strcpy(p, str);
59 p += strlen(str);
60 *p++ = '\0';
61 *p++ = '\0';
62 return i;
63 }
64
65 if (!strcmp(p, str))
66 return i;
67
68 p += strlen(p)+1;
69 i++;
70 }
71}
72
73int smbios_string_table_len(char *start)
74{
75 char *p = start;
76 int i, len = 0;
77
78 while(*p) {
79 i = strlen(p) + 1;
80 p += i;
81 len += i;
82 }
83 return len + 1;
84}
85
86static int smbios_cpu_vendor(char *start)
87{
Rudolf Marek06253cd2012-02-25 23:51:12 +010088 char tmp[13] = "Unknown";
Sven Schnelle164bcfd2011-08-14 20:56:34 +020089 u32 *_tmp = (u32 *)tmp;
Rudolf Marek06253cd2012-02-25 23:51:12 +010090 struct cpuid_result res;
Sven Schnelle164bcfd2011-08-14 20:56:34 +020091
Rudolf Marek06253cd2012-02-25 23:51:12 +010092 if (cpu_have_cpuid()) {
93 res = cpuid(0);
94 _tmp[0] = res.ebx;
95 _tmp[1] = res.edx;
96 _tmp[2] = res.ecx;
97 tmp[12] = '\0';
98 }
99
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200100 return smbios_add_string(start, tmp);
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200101}
102
103static int smbios_processor_name(char *start)
104{
Rudolf Marek06253cd2012-02-25 23:51:12 +0100105 char tmp[49] = "Unknown Processor Name";
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200106 u32 *_tmp = (u32 *)tmp;
107 struct cpuid_result res;
108 int i;
109
Rudolf Marek06253cd2012-02-25 23:51:12 +0100110 if (cpu_have_cpuid()) {
111 res = cpuid(0x80000000);
112 if (res.eax >= 0x80000004) {
113 for (i = 0; i < 3; i++) {
114 res = cpuid(0x80000002 + i);
115 _tmp[i * 4 + 0] = res.eax;
116 _tmp[i * 4 + 1] = res.ebx;
117 _tmp[i * 4 + 2] = res.ecx;
118 _tmp[i * 4 + 3] = res.edx;
119 }
120 tmp[48] = 0;
121 }
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200122 }
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200123 return smbios_add_string(start, tmp);
124}
125
Kane Chen33faac62014-07-27 12:54:44 -0700126/* this function will fill the corresponding manufacturer */
Timothy Pearson4785f2a2015-03-27 23:05:36 -0500127void smbios_fill_dimm_manufacturer_from_id(uint16_t mod_id, struct smbios_type17 *t)
Kane Chen33faac62014-07-27 12:54:44 -0700128{
129 switch (mod_id) {
Timothy Pearsoncfbcba52015-07-28 09:22:59 -0500130 case 0x2c80:
131 t->manufacturer = smbios_add_string(t->eos,
132 "Crucial");
133 break;
Timothy Pearsonace35252015-06-25 18:37:58 -0500134 case 0x9801:
135 t->manufacturer = smbios_add_string(t->eos,
136 "Kingston");
137 break;
Timothy Pearson821217b2015-03-27 22:47:25 -0500138 case 0x987f:
139 t->manufacturer = smbios_add_string(t->eos,
140 "Hynix");
141 break;
Timothy Pearsonace35252015-06-25 18:37:58 -0500142 case 0x9e02:
143 t->manufacturer = smbios_add_string(t->eos,
144 "Corsair");
145 break;
Kane Chen33faac62014-07-27 12:54:44 -0700146 case 0xad80:
147 t->manufacturer = smbios_add_string(t->eos,
148 "Hynix/Hyundai");
149 break;
150 case 0xce80:
151 t->manufacturer = smbios_add_string(t->eos,
152 "Samsung");
153 break;
154 case 0xfe02:
155 t->manufacturer = smbios_add_string(t->eos,
156 "Elpida");
157 break;
Timothy Pearson821217b2015-03-27 22:47:25 -0500158 case 0xff2c:
Kane Chen33faac62014-07-27 12:54:44 -0700159 t->manufacturer = smbios_add_string(t->eos,
Timothy Pearson821217b2015-03-27 22:47:25 -0500160 "Micron");
Kane Chen33faac62014-07-27 12:54:44 -0700161 break;
Timothy Pearson821217b2015-03-27 22:47:25 -0500162 default: {
163 char string_buffer[256];
164 snprintf(string_buffer, sizeof(string_buffer),
165 "Unknown (%x)", mod_id);
166 t->manufacturer = smbios_add_string(t->eos,
167 string_buffer);
168 break;
169 }
Kane Chen33faac62014-07-27 12:54:44 -0700170 }
171}
172
173static int create_smbios_type17_for_dimm(struct dimm_info *dimm,
174 unsigned long *current, int *handle)
175{
176 struct smbios_type17 *t = (struct smbios_type17 *)*current;
177 uint8_t length;
178 char locator[40];
179
180 memset(t, 0, sizeof(struct smbios_type17));
181 t->memory_type = dimm->ddr_type;
182 t->clock_speed = dimm->ddr_frequency;
183 t->speed = dimm->ddr_frequency;
184 t->type = SMBIOS_MEMORY_DEVICE;
185 t->size = dimm->dimm_size;
186 t->data_width = 8 * (1 << (dimm->bus_width & 0x7));
187 t->total_width = t->data_width + 8 * ((dimm->bus_width & 0x18) >> 3);
188
189 switch (dimm->mod_type) {
190 case SPD_RDIMM:
191 case SPD_MINI_RDIMM:
192 t->form_factor = MEMORY_FORMFACTOR_RIMM;
193 break;
194 case SPD_UDIMM:
195 case SPD_MICRO_DIMM:
196 case SPD_MINI_UDIMM:
197 t->form_factor = MEMORY_FORMFACTOR_DIMM;
198 break;
199 case SPD_SODIMM:
200 t->form_factor = MEMORY_FORMFACTOR_SODIMM;
201 break;
202 default:
203 t->form_factor = MEMORY_FORMFACTOR_UNKNOWN;
204 break;
205 }
206
Timothy Pearson4785f2a2015-03-27 23:05:36 -0500207 smbios_fill_dimm_manufacturer_from_id(dimm->mod_id, t);
Kane Chen33faac62014-07-27 12:54:44 -0700208 /* put '\0' in the end of data */
209 length = sizeof(dimm->serial);
210 dimm->serial[length - 1] = '\0';
211 if (dimm->serial[0] == 0)
212 t->serial_number = smbios_add_string(t->eos, "None");
213 else
214 t->serial_number = smbios_add_string(t->eos,
215 (const char *)dimm->serial);
216
217 snprintf(locator, sizeof(locator), "Channel-%d-DIMM-%d",
218 dimm->channel_num, dimm->dimm_num);
219 t->device_locator = smbios_add_string(t->eos, locator);
220
221 snprintf(locator, sizeof(locator), "BANK %d", dimm->bank_locator);
222 t->bank_locator = smbios_add_string(t->eos, locator);
223
224 /* put '\0' in the end of data */
225 length = sizeof(dimm->module_part_number);
226 dimm->module_part_number[length - 1] = '\0';
227 t->part_number = smbios_add_string(t->eos,
228 (const char *)dimm->module_part_number);
229
230 /* Synchronous = 1 */
231 t->type_detail = 0x0080;
232 /* no handle for error information */
233 t->memory_error_information_handle = 0xFFFE;
234 t->attributes = dimm->rank_per_dimm;
235 t->handle = *handle;
236 *handle += 1;
237 t->length = sizeof(struct smbios_type17) - 2;
238 return t->length + smbios_string_table_len(t->eos);
239}
240
Vladimir Serbinenko63acd222014-06-01 00:26:48 +0200241const char *__attribute__((weak)) smbios_mainboard_bios_version(void)
242{
243 if (strlen(CONFIG_LOCALVERSION))
244 return CONFIG_LOCALVERSION;
245 else
Kyösti Mälkkic36af7b2014-11-18 12:41:16 +0200246 return coreboot_version;
Vladimir Serbinenko63acd222014-06-01 00:26:48 +0200247}
248
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200249static int smbios_write_type0(unsigned long *current, int handle)
250{
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200251 struct smbios_type0 *t = (struct smbios_type0 *)*current;
252 int len = sizeof(struct smbios_type0);
253
254 memset(t, 0, sizeof(struct smbios_type0));
255 t->type = SMBIOS_BIOS_INFORMATION;
256 t->handle = handle;
257 t->length = len - 2;
258
259 t->vendor = smbios_add_string(t->eos, "coreboot");
Stefan Reinauerc6b21662012-04-03 16:02:54 -0700260#if !CONFIG_CHROMEOS
Kyösti Mälkkic36af7b2014-11-18 12:41:16 +0200261 t->bios_release_date = smbios_add_string(t->eos, coreboot_dmi_date);
Christian Gmeiner5e272a42013-02-04 16:22:46 +0100262
Vladimir Serbinenko63acd222014-06-01 00:26:48 +0200263 t->bios_version = smbios_add_string(t->eos, smbios_mainboard_bios_version());
Stefan Reinauerc6b21662012-04-03 16:02:54 -0700264#else
265#define SPACES \
266 " "
Kyösti Mälkkic36af7b2014-11-18 12:41:16 +0200267 t->bios_release_date = smbios_add_string(t->eos, coreboot_dmi_date);
Lee Leahydbdd0662015-02-26 14:33:18 -0800268#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
Stefan Reinauerc6b21662012-04-03 16:02:54 -0700269 u32 version_offset = (u32)smbios_string_table_len(t->eos);
Lee Leahydbdd0662015-02-26 14:33:18 -0800270#endif
Stefan Reinauerc6b21662012-04-03 16:02:54 -0700271 t->bios_version = smbios_add_string(t->eos, SPACES);
Lee Leahydbdd0662015-02-26 14:33:18 -0800272
273#if IS_ENABLED(CONFIG_HAVE_ACPI_TABLES)
Stefan Reinauerc6b21662012-04-03 16:02:54 -0700274 /* SMBIOS offsets start at 1 rather than 0 */
275 vboot_data->vbt10 = (u32)t->eos + (version_offset - 1);
276#endif
Lee Leahydbdd0662015-02-26 14:33:18 -0800277#endif /* CONFIG_CHROMEOS */
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200278
Aaron Durbin9eebbd42015-03-26 14:55:34 -0500279 t->bios_rom_size = (CONFIG_ROM_SIZE / 65535) - 1;
Hung-Te Lin6fe0cab2013-01-22 18:57:56 +0800280
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200281 t->system_bios_major_release = 4;
282 t->bios_characteristics =
283 BIOS_CHARACTERISTICS_PCI_SUPPORTED |
284#if CONFIG_CARDBUS_PLUGIN_SUPPORT
285 BIOS_CHARACTERISTICS_PC_CARD |
286#endif
287 BIOS_CHARACTERISTICS_SELECTABLE_BOOT |
288 BIOS_CHARACTERISTICS_UPGRADEABLE;
289
Vladimir Serbinenko822bc652014-01-03 15:55:40 +0100290#if CONFIG_HAVE_ACPI_TABLES
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200291 t->bios_characteristics_ext1 = BIOS_EXT1_CHARACTERISTICS_ACPI;
292#endif
293 t->bios_characteristics_ext2 = BIOS_EXT2_CHARACTERISTICS_TARGET;
294 len = t->length + smbios_string_table_len(t->eos);
295 *current += len;
296 return len;
297}
298
Vladimir Serbinenko0afdec42015-05-30 23:08:26 +0200299#if !CONFIG_SMBIOS_PROVIDED_BY_MOBO
300
Christian Gmeinerac3aa092012-07-25 13:42:40 +0200301const char *__attribute__((weak)) smbios_mainboard_serial_number(void)
302{
303 return CONFIG_MAINBOARD_SERIAL_NUMBER;
304}
305
306const char *__attribute__((weak)) smbios_mainboard_version(void)
307{
308 return CONFIG_MAINBOARD_VERSION;
309}
310
Gerd Hoffmann06262742013-11-13 13:37:23 +0100311const char *__attribute__((weak)) smbios_mainboard_manufacturer(void)
312{
313 return CONFIG_MAINBOARD_SMBIOS_MANUFACTURER;
314}
315
316const char *__attribute__((weak)) smbios_mainboard_product_name(void)
317{
318 return CONFIG_MAINBOARD_SMBIOS_PRODUCT_NAME;
319}
320
321void __attribute__((weak)) smbios_mainboard_set_uuid(u8 *uuid)
322{
323 /* leave all zero */
324}
Vladimir Serbinenko0afdec42015-05-30 23:08:26 +0200325#endif
Gerd Hoffmann06262742013-11-13 13:37:23 +0100326
Kane Chen51bdc472014-09-08 18:40:30 -0700327#ifdef CONFIG_MAINBOARD_FAMILY
328const char *smbios_mainboard_family(void)
329{
330 return CONFIG_MAINBOARD_FAMILY;
331}
332#endif /* CONFIG_MAINBOARD_FAMILY */
333
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200334static int smbios_write_type1(unsigned long *current, int handle)
335{
336 struct smbios_type1 *t = (struct smbios_type1 *)*current;
337 int len = sizeof(struct smbios_type1);
338
339 memset(t, 0, sizeof(struct smbios_type1));
340 t->type = SMBIOS_SYSTEM_INFORMATION;
341 t->handle = handle;
342 t->length = len - 2;
Gerd Hoffmann06262742013-11-13 13:37:23 +0100343 t->manufacturer = smbios_add_string(t->eos, smbios_mainboard_manufacturer());
344 t->product_name = smbios_add_string(t->eos, smbios_mainboard_product_name());
Christian Gmeinerac3aa092012-07-25 13:42:40 +0200345 t->serial_number = smbios_add_string(t->eos, smbios_mainboard_serial_number());
346 t->version = smbios_add_string(t->eos, smbios_mainboard_version());
Marc Jonesf43ba9c2015-06-09 21:10:43 -0600347#ifdef CONFIG_MAINBOARD_FAMILY
348 t->family = smbios_add_string(t->eos, smbios_mainboard_family());
349#endif
Gerd Hoffmann06262742013-11-13 13:37:23 +0100350 smbios_mainboard_set_uuid(t->uuid);
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200351 len = t->length + smbios_string_table_len(t->eos);
352 *current += len;
353 return len;
354}
355
Vladimir Serbinenko47089f22014-03-02 19:14:44 +0100356static int smbios_write_type2(unsigned long *current, int handle)
357{
358 struct smbios_type2 *t = (struct smbios_type2 *)*current;
359 int len = sizeof(struct smbios_type2);
360
361 memset(t, 0, sizeof(struct smbios_type2));
362 t->type = SMBIOS_BOARD_INFORMATION;
363 t->handle = handle;
364 t->length = len - 2;
365 t->manufacturer = smbios_add_string(t->eos, smbios_mainboard_manufacturer());
366 t->product_name = smbios_add_string(t->eos, smbios_mainboard_product_name());
367 t->serial_number = smbios_add_string(t->eos, smbios_mainboard_serial_number());
368 t->version = smbios_add_string(t->eos, smbios_mainboard_version());
369 len = t->length + smbios_string_table_len(t->eos);
370 *current += len;
371 return len;
372}
373
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200374static int smbios_write_type3(unsigned long *current, int handle)
375{
376 struct smbios_type3 *t = (struct smbios_type3 *)*current;
377 int len = sizeof(struct smbios_type3);
378
379 memset(t, 0, sizeof(struct smbios_type3));
380 t->type = SMBIOS_SYSTEM_ENCLOSURE;
381 t->handle = handle;
382 t->length = len - 2;
Vladimir Serbinenko4ba3b792015-05-30 23:09:53 +0200383 t->manufacturer = smbios_add_string(t->eos, smbios_mainboard_manufacturer());
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200384 t->bootup_state = SMBIOS_STATE_SAFE;
385 t->power_supply_state = SMBIOS_STATE_SAFE;
386 t->thermal_state = SMBIOS_STATE_SAFE;
Vladimir Serbinenkoa9db82f2014-10-16 13:21:47 +0200387 if(IS_ENABLED(CONFIG_SYSTEM_TYPE_LAPTOP)) {
388 t->_type = SMBIOS_ENCLOSURE_NOTEBOOK;
389 } else {
390 t->_type = SMBIOS_ENCLOSURE_DESKTOP;
391 }
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200392 t->security_status = SMBIOS_STATE_SAFE;
393 len = t->length + smbios_string_table_len(t->eos);
394 *current += len;
395 return len;
396}
397
398static int smbios_write_type4(unsigned long *current, int handle)
399{
400 struct cpuid_result res;
401 struct smbios_type4 *t = (struct smbios_type4 *)*current;
402 int len = sizeof(struct smbios_type4);
403
Rudolf Marek06253cd2012-02-25 23:51:12 +0100404 /* Provide sane defaults even for CPU without CPUID */
405 res.eax = res.edx = 0;
406 res.ebx = 0x10000;
407
408 if (cpu_have_cpuid()) {
409 res = cpuid(1);
410 }
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200411
412 memset(t, 0, sizeof(struct smbios_type4));
413 t->type = SMBIOS_PROCESSOR_INFORMATION;
414 t->handle = handle;
415 t->length = len - 2;
416 t->processor_id[0] = res.eax;
417 t->processor_id[1] = res.edx;
418 t->processor_manufacturer = smbios_cpu_vendor(t->eos);
419 t->processor_version = smbios_processor_name(t->eos);
Rudolf Marek06253cd2012-02-25 23:51:12 +0100420 t->processor_family = (res.eax > 0) ? 0x0c : 0x6;
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200421 t->processor_type = 3; /* System Processor */
422 t->processor_upgrade = 0x06;
423 t->core_count = (res.ebx >> 16) & 0xff;
424 t->l1_cache_handle = 0xffff;
425 t->l2_cache_handle = 0xffff;
426 t->l3_cache_handle = 0xffff;
427 t->processor_upgrade = 1;
428 len = t->length + smbios_string_table_len(t->eos);
429 *current += len;
430 return len;
431}
432
Vladimir Serbinenko6abb33c2014-08-27 23:42:45 +0200433static int smbios_write_type11(unsigned long *current, int *handle)
Peter Stugec392b642013-07-06 19:51:12 +0200434{
435 struct smbios_type11 *t = (struct smbios_type11 *)*current;
Vladimir Serbinenko6abb33c2014-08-27 23:42:45 +0200436 int len;
Edward O'Callaghan2c9d2cf2014-10-27 23:29:29 +1100437 struct device *dev;
Peter Stugec392b642013-07-06 19:51:12 +0200438
439 memset(t, 0, sizeof *t);
440 t->type = SMBIOS_OEM_STRINGS;
Vladimir Serbinenko6abb33c2014-08-27 23:42:45 +0200441 t->handle = *handle;
Peter Stugec392b642013-07-06 19:51:12 +0200442 t->length = len = sizeof *t - 2;
443
Vladimir Serbinenko6abb33c2014-08-27 23:42:45 +0200444 for(dev = all_devices; dev; dev = dev->next) {
445 if (dev->ops && dev->ops->get_smbios_strings)
446 dev->ops->get_smbios_strings(dev, t);
447 }
448
449 if (t->count == 0) {
450 memset(t, 0, sizeof *t);
451 return 0;
452 }
Peter Stugec392b642013-07-06 19:51:12 +0200453
454 len += smbios_string_table_len(t->eos);
Vladimir Serbinenko6abb33c2014-08-27 23:42:45 +0200455
Peter Stugec392b642013-07-06 19:51:12 +0200456 *current += len;
Vladimir Serbinenko6abb33c2014-08-27 23:42:45 +0200457 (*handle)++;
Peter Stugec392b642013-07-06 19:51:12 +0200458 return len;
459}
460
Kane Chen33faac62014-07-27 12:54:44 -0700461static int smbios_write_type17(unsigned long *current, int *handle)
462{
463 int len = sizeof(struct smbios_type17);
464 int i;
465
466 struct memory_info *meminfo;
467 meminfo = cbmem_find(CBMEM_ID_MEMINFO);
468 if (meminfo == NULL)
469 return 0; /* can't find mem info in cbmem */
470
471 printk(BIOS_INFO, "Create SMBIOS type 17\n");
472 for (i = 0; i < meminfo->dimm_cnt && i < ARRAY_SIZE(meminfo->dimm); i++) {
473 struct dimm_info *dimm;
474 dimm = &meminfo->dimm[i];
475 len = create_smbios_type17_for_dimm(dimm, current, handle);
476 *current += len;
477 }
478 return meminfo->dimm_cnt * len;
479}
480
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200481static int smbios_write_type32(unsigned long *current, int handle)
482{
483 struct smbios_type32 *t = (struct smbios_type32 *)*current;
484 int len = sizeof(struct smbios_type32);
485
486 memset(t, 0, sizeof(struct smbios_type32));
487 t->type = SMBIOS_SYSTEM_BOOT_INFORMATION;
488 t->handle = handle;
489 t->length = len - 2;
490 *current += len;
491 return len;
492}
493
Duncan Laurie21a78702013-05-23 14:17:05 -0700494int smbios_write_type41(unsigned long *current, int *handle,
495 const char *name, u8 instance, u16 segment,
496 u8 bus, u8 device, u8 function)
497{
498 struct smbios_type41 *t = (struct smbios_type41 *)*current;
499 int len = sizeof(struct smbios_type41);
500
501 memset(t, 0, sizeof(struct smbios_type41));
502 t->type = SMBIOS_ONBOARD_DEVICES_EXTENDED_INFORMATION;
503 t->handle = *handle;
504 t->length = len - 2;
505 t->reference_designation = smbios_add_string(t->eos, name);
506 t->device_type = SMBIOS_DEVICE_TYPE_OTHER;
507 t->device_status = 1;
508 t->device_type_instance = instance;
509 t->segment_group_number = segment;
510 t->bus_number = bus;
511 t->device_number = device;
512 t->function_number = function;
513
514 len = t->length + smbios_string_table_len(t->eos);
515 *current += len;
516 *handle += 1;
517 return len;
518}
519
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200520static int smbios_write_type127(unsigned long *current, int handle)
521{
522 struct smbios_type127 *t = (struct smbios_type127 *)*current;
523 int len = sizeof(struct smbios_type127);
524
525 memset(t, 0, sizeof(struct smbios_type127));
526 t->type = SMBIOS_END_OF_TABLE;
527 t->handle = handle;
528 t->length = len - 2;
529 *current += len;
530 return len;
531}
532
Edward O'Callaghan2c9d2cf2014-10-27 23:29:29 +1100533static int smbios_walk_device_tree(struct device *tree, int *handle, unsigned long *current)
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200534{
Edward O'Callaghan2c9d2cf2014-10-27 23:29:29 +1100535 struct device *dev;
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200536 int len = 0;
537
538 for(dev = tree; dev; dev = dev->next) {
Kyösti Mälkki7baadac2012-10-07 14:57:15 +0200539 printk(BIOS_INFO, "%s (%s)\n", dev_path(dev), dev_name(dev));
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200540
541 if (dev->ops && dev->ops->get_smbios_data)
542 len += dev->ops->get_smbios_data(dev, handle, current);
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200543 }
544 return len;
545}
546
Ben Frisch72af5d72015-05-09 19:52:18 -0500547#define update_max(len, max_len, stmt) do { int tmp = stmt; max_len = MAX(max_len, tmp); len += tmp; } while(0)
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200548unsigned long smbios_write_tables(unsigned long current)
549{
550 struct smbios_entry *se;
551 unsigned long tables;
Ben Frisch72af5d72015-05-09 19:52:18 -0500552 int len = 0;
553 int max_struct_size = 0;
554 int handle = 0;
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200555
556 current = ALIGN(current, 16);
557 printk(BIOS_DEBUG, "%s: %08lx\n", __func__, current);
558
559 se = (struct smbios_entry *)current;
560 current += sizeof(struct smbios_entry);
561 current = ALIGN(current, 16);
562
563 tables = current;
Ben Frisch72af5d72015-05-09 19:52:18 -0500564 update_max(len, max_struct_size, smbios_write_type0(&current, handle++));
565 update_max(len, max_struct_size, smbios_write_type1(&current, handle++));
566 update_max(len, max_struct_size, smbios_write_type2(&current, handle++));
567 update_max(len, max_struct_size, smbios_write_type3(&current, handle++));
568 update_max(len, max_struct_size, smbios_write_type4(&current, handle++));
569 update_max(len, max_struct_size, smbios_write_type11(&current, &handle));
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700570#if CONFIG_ELOG
Patrick Georgi10ef8722015-07-07 17:29:43 +0200571 update_max(len, max_struct_size, elog_smbios_write_type15(&current, handle++));
Duncan Laurie472ec9c2012-06-23 16:13:42 -0700572#endif
Ben Frisch72af5d72015-05-09 19:52:18 -0500573 update_max(len, max_struct_size, smbios_write_type17(&current, &handle));
574 update_max(len, max_struct_size, smbios_write_type32(&current, handle++));
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200575
Ben Frisch72af5d72015-05-09 19:52:18 -0500576 update_max(len, max_struct_size, smbios_walk_device_tree(all_devices, &handle, &current));
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200577
Ben Frisch72af5d72015-05-09 19:52:18 -0500578 update_max(len, max_struct_size, smbios_write_type127(&current, handle++));
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200579
580 memset(se, 0, sizeof(struct smbios_entry));
581 memcpy(se->anchor, "_SM_", 4);
582 se->length = sizeof(struct smbios_entry);
583 se->major_version = 2;
584 se->minor_version = 7;
Ben Frisch72af5d72015-05-09 19:52:18 -0500585 se->max_struct_size = max_struct_size;
Sven Schnelle164bcfd2011-08-14 20:56:34 +0200586 se->struct_count = handle;
587 memcpy(se->intermediate_anchor_string, "_DMI_", 5);
588
589 se->struct_table_address = (u32)tables;
590 se->struct_table_length = len;
591
592 se->intermediate_checksum = smbios_checksum((u8 *)se + 0x10,
593 sizeof(struct smbios_entry) - 0x10);
594 se->checksum = smbios_checksum((u8 *)se, sizeof(struct smbios_entry));
595 return current;
596}