xref: /freebsd/sys/dev/coreboot/coreboot.c (revision d15f2551b25f79ddcbe289faa95e655100b952da)
1 /*
2  * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  */
6 
7 /*
8  * coreboot(4) - FreeBSD driver for coreboot firmware tables
9  *
10  * Discovers the coreboot table by scanning low memory for the "LBIO"
11  * signature, follows CB_TAG_FORWARD to the high-memory table, and
12  * exposes firmware information through sysctl(9) and character devices.
13  */
14 
15 #include <sys/systm.h>
16 #include <sys/bus.h>
17 #include <sys/kernel.h>
18 #include <sys/malloc.h>
19 #include <sys/module.h>
20 #include <sys/rman.h>
21 #include <sys/sysctl.h>
22 
23 #include <vm/vm.h>
24 #include <vm/vm_param.h>
25 #include <vm/pmap.h>
26 
27 #include <machine/bus.h>
28 #include <machine/resource.h>
29 
30 #include <dev/coreboot/coreboot.h>
31 
32 static struct coreboot_softc *coreboot_sc;
33 
34 /*
35  * Debug verbosity control, non-zero enables extra output.
36  * Tunable via loader.conf: hw.coreboot.debug=1
37  * Runtime: sysctl hw.coreboot.debug=1
38  * Registered dynamically under hw.coreboot in
39  * coreboot_register_sysctls().
40  */
41 static int coreboot_debug = 0;
42 TUNABLE_INT("hw.coreboot.debug", &coreboot_debug);
43 
44 struct coreboot_softc *
45 coreboot_get_softc(void)
46 {
47 	return (coreboot_sc);
48 }
49 
50 static void	coreboot_identify(driver_t *, device_t);
51 static int	coreboot_probe(device_t);
52 static int	coreboot_attach(device_t);
53 static int	coreboot_detach(device_t);
54 static int	coreboot_modevent(module_t, int, void *);
55 
56 /*
57  * Scan a physical memory region for the "LBIO" signature.
58  * Returns the physical address of the header, or 0 if not found.
59  */
60 static vm_paddr_t
61 coreboot_scan_region(vm_paddr_t start, vm_paddr_t end)
62 {
63 	vm_paddr_t addr;
64 	void *va;
65 	struct cb_header *hdr;
66 
67 	for (addr = (start == 0 ? CB_SCAN_LOW_STEP : start); addr < end;
68 	    addr += CB_SCAN_LOW_STEP) {
69 		va = pmap_mapbios(addr, sizeof(struct cb_header));
70 		if (va == NULL)
71 			continue;
72 
73 		hdr = (struct cb_header *)va;
74 		if (memcmp(hdr->signature, CB_HEADER_SIGNATURE,
75 		    CB_HEADER_SIG_LEN) == 0) {
76 			pmap_unmapbios(va, sizeof(struct cb_header));
77 			return (addr);
78 		}
79 		pmap_unmapbios(va, sizeof(struct cb_header));
80 	}
81 	return (0);
82 }
83 
84 /*
85  * Validate length fields in the header before using them for mappings
86  * and pointer arithmetic.
87  */
88 static int
89 coreboot_sanitize_header(const struct cb_header *hdr, vm_size_t *map_size)
90 {
91 	uint64_t total;
92 
93 	if (hdr->header_bytes < sizeof(*hdr) ||
94 	    hdr->header_bytes > CB_MAX_HEADER_BYTES)
95 		return (EINVAL);
96 	if ((hdr->header_bytes % CB_TABLE_ALIGN) != 0)
97 		return (EINVAL);
98 	if (hdr->table_bytes > CB_MAX_TABLE_BYTES)
99 		return (EINVAL);
100 	if ((hdr->table_bytes % CB_TABLE_ALIGN) != 0)
101 		return (EINVAL);
102 
103 	total = (uint64_t)hdr->header_bytes + (uint64_t)hdr->table_bytes;
104 	if (total > CB_MAX_TABLE_MAP_BYTES)
105 		return (EINVAL);
106 
107 	*map_size = (vm_size_t)total;
108 	return (0);
109 }
110 
111 /*
112  * Validate the coreboot header checksum.
113  * Returns 0 on success, non-zero on failure.
114  */
115 static int
116 coreboot_validate_header(struct cb_header *hdr, vm_size_t mapped_len)
117 {
118 	uint16_t cksum;
119 
120 	if (hdr->header_bytes > mapped_len)
121 		return (EINVAL);
122 
123 	cksum = cb_checksum(hdr, hdr->header_bytes);
124 	if (cksum != 0)
125 		return (EINVAL);
126 
127 	return (0);
128 }
129 
130 /*
131  * Validate checksum for the table payload.
132  */
133 static int
134 coreboot_validate_table(struct cb_header *hdr, vm_size_t mapped_len)
135 {
136 	const uint8_t *table;
137 	uint16_t cksum;
138 
139 	if (hdr->table_bytes == 0)
140 		return (0);
141 
142 	if ((uint64_t)hdr->header_bytes + (uint64_t)hdr->table_bytes >
143 	    mapped_len)
144 		return (EINVAL);
145 	if (hdr->table_checksum > UINT16_MAX)
146 		return (EINVAL);
147 
148 	table = (const uint8_t *)hdr + hdr->header_bytes;
149 	cksum = cb_checksum(table, hdr->table_bytes);
150 	if (cksum != (uint16_t)hdr->table_checksum)
151 		return (EINVAL);
152 
153 	return (0);
154 }
155 
156 static void
157 coreboot_copy_bounded_string(const char *src, size_t maxlen, char *dst,
158     size_t dstlen)
159 {
160 	size_t slen;
161 
162 	if (dstlen == 0)
163 		return;
164 
165 	slen = strnlen(src, maxlen);
166 	if (slen >= dstlen)
167 		slen = dstlen - 1;
168 	memcpy(dst, src, slen);
169 	dst[slen] = '\0';
170 }
171 
172 /*
173  * Copy a coreboot string record into a destination buffer.
174  */
175 static void
176 coreboot_copy_string(const struct cb_string *rec, char *dst, size_t dstlen)
177 {
178 	size_t slen;
179 
180 	slen = rec->size - sizeof(struct cb_record);
181 	if (slen >= dstlen)
182 		slen = dstlen - 1;
183 	memcpy(dst, rec->string, slen);
184 	dst[slen] = '\0';
185 
186 	/* Strip trailing whitespace/nulls */
187 	while (slen > 0 && (dst[slen - 1] == '\0' || dst[slen - 1] == ' ' ||
188 	    dst[slen - 1] == '\n'))
189 		dst[--slen] = '\0';
190 }
191 
192 /*
193  * Extract mainboard vendor and part number from the strings field.
194  */
195 static void
196 coreboot_parse_mainboard(struct coreboot_softc *sc,
197     const struct cb_mainboard *mb)
198 {
199 	const char *strings = (const char *)mb->strings;
200 	size_t total = mb->size - offsetof(struct cb_mainboard, strings);
201 	uint8_t vendor_off, part_off;
202 
203 	vendor_off = mb->vendor_idx;
204 	part_off = mb->part_idx;
205 
206 	if (vendor_off < total)
207 		coreboot_copy_bounded_string(strings + vendor_off,
208 		    total - vendor_off, sc->mb_vendor, sizeof(sc->mb_vendor));
209 	if (part_off < total)
210 		coreboot_copy_bounded_string(strings + part_off,
211 		    total - part_off, sc->mb_part, sizeof(sc->mb_part));
212 }
213 
214 /*
215  * Parse all records in the coreboot table and populate softc.
216  */
217 static void
218 coreboot_parse_table(struct coreboot_softc *sc, struct cb_header *hdr)
219 {
220 	uint8_t *entry;
221 	uint8_t *table_end;
222 	struct cb_record *rec;
223 
224 	entry = (uint8_t *)hdr + hdr->header_bytes;
225 	table_end = entry + hdr->table_bytes;
226 
227 	while ((size_t)(table_end - entry) >= sizeof(struct cb_record)) {
228 		size_t rec_size;
229 
230 		rec = (struct cb_record *)entry;
231 		rec_size = rec->size;
232 
233 		if (rec_size < sizeof(struct cb_record))
234 			break;
235 		if (rec_size > (size_t)(table_end - entry))
236 			break;
237 
238 		switch (rec->tag) {
239 		case CB_TAG_VERSION:
240 			coreboot_copy_string((struct cb_string *)rec,
241 			    sc->version, sizeof(sc->version));
242 			break;
243 
244 		case CB_TAG_EXTRA_VERSION:
245 			coreboot_copy_string((struct cb_string *)rec,
246 			    sc->extra_version, sizeof(sc->extra_version));
247 			break;
248 
249 		case CB_TAG_BUILD:
250 			coreboot_copy_string((struct cb_string *)rec,
251 			    sc->build, sizeof(sc->build));
252 			break;
253 
254 		case CB_TAG_COMPILE_TIME:
255 			coreboot_copy_string((struct cb_string *)rec,
256 			    sc->compile_time, sizeof(sc->compile_time));
257 			break;
258 
259 		case CB_TAG_COMPILER:
260 			coreboot_copy_string((struct cb_string *)rec,
261 			    sc->compiler, sizeof(sc->compiler));
262 			break;
263 
264 		case CB_TAG_PLATFORM_BLOB_VERSION:
265 			coreboot_copy_string((struct cb_string *)rec,
266 			    sc->platform_blob_version,
267 			    sizeof(sc->platform_blob_version));
268 			break;
269 
270 		case CB_TAG_SERIALNO:
271 			coreboot_copy_string((struct cb_string *)rec,
272 			    sc->serialno, sizeof(sc->serialno));
273 			break;
274 
275 		case CB_TAG_VERSION_TIMESTAMP: {
276 			struct cb_version_timestamp *ts =
277 			    (struct cb_version_timestamp *)rec;
278 
279 			if (rec_size < sizeof(*ts))
280 				break;
281 			sc->version_timestamp = ts->timestamp;
282 			sc->has_version_timestamp = 1;
283 			break;
284 		}
285 
286 		case CB_TAG_MAINBOARD:
287 			if (rec_size < offsetof(struct cb_mainboard, strings))
288 				break;
289 			coreboot_parse_mainboard(sc,
290 			    (struct cb_mainboard *)rec);
291 			break;
292 
293 		case CB_TAG_SERIAL: {
294 			struct cb_serial *ser = (struct cb_serial *)rec;
295 
296 			if (rec_size < sizeof(*ser))
297 				break;
298 			sc->serial_baseaddr = ser->baseaddr;
299 			sc->serial_baud = ser->baud;
300 			sc->serial_regwidth = ser->regwidth;
301 			sc->has_serial = 1;
302 			break;
303 		}
304 
305 		case CB_TAG_TSC_INFO: {
306 			struct cb_tsc_info *tsc = (struct cb_tsc_info *)rec;
307 
308 			if (rec_size < sizeof(*tsc))
309 				break;
310 			sc->tsc_freq_khz = tsc->freq_khz;
311 			sc->has_tsc_info = 1;
312 			break;
313 		}
314 
315 		case CB_TAG_PCIE: {
316 			struct cb_pcie *pcie = (struct cb_pcie *)rec;
317 
318 			if (rec_size < sizeof(*pcie))
319 				break;
320 			sc->pcie_ctrl_base = pcie->ctrl_base;
321 			sc->has_pcie = 1;
322 			break;
323 		}
324 
325 		case CB_TAG_BOOT_MEDIA_PARAMS: {
326 			struct cb_boot_media_params *bmp =
327 			    (struct cb_boot_media_params *)rec;
328 
329 			if (rec_size < sizeof(*bmp))
330 				break;
331 			sc->fmap_offset = bmp->fmap_offset;
332 			sc->cbfs_offset = bmp->cbfs_offset;
333 			sc->cbfs_size = bmp->cbfs_size;
334 			sc->boot_media_size = bmp->boot_media_size;
335 			sc->has_boot_media = 1;
336 			break;
337 		}
338 
339 		case CB_TAG_MMC_INFO: {
340 			struct cb_mmc_info *mmc = (struct cb_mmc_info *)rec;
341 
342 			if (rec_size < sizeof(*mmc))
343 				break;
344 			sc->mmc_early_cmd1_status = mmc->early_cmd1_status;
345 			sc->has_mmc_info = 1;
346 			break;
347 		}
348 
349 		case CB_TAG_CBMEM_CONSOLE: {
350 			struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
351 
352 			if (rec_size < sizeof(*ref))
353 				break;
354 			sc->console_paddr = (vm_paddr_t)ref->cbmem_addr;
355 			sc->has_console = 1;
356 			break;
357 		}
358 
359 		case CB_TAG_CBMEM_ENTRY: {
360 			struct cb_cbmem_entry *ent =
361 			    (struct cb_cbmem_entry *)rec;
362 
363 			if (rec_size < sizeof(*ent))
364 				break;
365 			if (sc->cbmem_count < CB_MAX_CBMEM_ENTRIES) {
366 				struct cbmem_entry_info *info =
367 				    &sc->cbmem_entries[sc->cbmem_count];
368 				info->id = ent->id;
369 				info->address = ent->address;
370 				info->size = ent->entry_size;
371 				strlcpy(info->name, cbmem_id_to_name(ent->id),
372 				    sizeof(info->name));
373 				sc->cbmem_count++;
374 			}
375 			break;
376 		}
377 
378 		case CB_TAG_BOARD_CONFIG: {
379 			struct cb_board_config *bc =
380 			    (struct cb_board_config *)rec;
381 
382 			if (rec_size < sizeof(*bc))
383 				break;
384 			sc->fw_config = bc->fw_config;
385 			sc->board_id = bc->board_id;
386 			sc->ram_code = bc->ram_code;
387 			sc->sku_id = bc->sku_id;
388 			sc->has_board_config = 1;
389 			break;
390 		}
391 
392 		case CB_TAG_MAC_ADDRS: {
393 			struct cb_macs *macs = (struct cb_macs *)rec;
394 			uint32_t i, count;
395 
396 			if (rec_size < sizeof(*macs))
397 				break;
398 			count = macs->count;
399 			if (count > CB_MAX_MAC_ADDRS)
400 				count = CB_MAX_MAC_ADDRS;
401 			if (rec_size < sizeof(*macs) +
402 			    count * sizeof(struct cb_mac_address))
403 				break;
404 			for (i = 0; i < count; i++)
405 				sc->macs[i] = macs->entries[i];
406 			sc->mac_count = count;
407 			break;
408 		}
409 
410 		case CB_TAG_ACPI_RSDP: {
411 			struct cb_acpi_rsdp *rsdp =
412 			    (struct cb_acpi_rsdp *)rec;
413 
414 			if (rec_size < sizeof(*rsdp))
415 				break;
416 			sc->acpi_rsdp = rsdp->rsdp_pointer;
417 			sc->has_acpi_rsdp = 1;
418 			break;
419 		}
420 
421 		case CB_TAG_SPI_FLASH: {
422 			struct cb_spi_flash *spi =
423 			    (struct cb_spi_flash *)rec;
424 
425 			if (rec_size < sizeof(*spi))
426 				break;
427 			sc->spi_flash_size = spi->flash_size;
428 			sc->spi_sector_size = spi->sector_size;
429 			sc->spi_erase_cmd = spi->erase_cmd;
430 			sc->spi_flags = spi->flags;
431 			sc->has_spi_flash = 1;
432 			break;
433 		}
434 
435 		case CB_TAG_CONSOLE: {
436 			struct cb_console *con = (struct cb_console *)rec;
437 
438 			if (rec_size < sizeof(*con))
439 				break;
440 			sc->console_type = con->type;
441 			sc->has_console_type = 1;
442 			break;
443 		}
444 
445 		case CB_TAG_FRAMEBUFFER: {
446 			struct cb_framebuffer *fb =
447 			    (struct cb_framebuffer *)rec;
448 
449 			if (rec_size < CB_FRAMEBUFFER_MIN_SIZE)
450 				break;
451 			sc->fb_addr = fb->physical_address;
452 			sc->fb_x_res = fb->x_resolution;
453 			sc->fb_y_res = fb->y_resolution;
454 			sc->fb_stride = fb->bytes_per_line;
455 			sc->fb_bpp = fb->bits_per_pixel;
456 			sc->has_framebuffer = 1;
457 			break;
458 		}
459 
460 		case CB_TAG_GPIO: {
461 			struct cb_gpios *gpios = (struct cb_gpios *)rec;
462 			uint32_t i, count;
463 
464 			if (rec_size < sizeof(*gpios))
465 				break;
466 			count = gpios->count;
467 			if (count > CB_MAX_GPIOS)
468 				count = CB_MAX_GPIOS;
469 			if (rec_size < sizeof(*gpios) +
470 			    count * sizeof(struct cb_gpio))
471 				break;
472 			for (i = 0; i < count; i++)
473 				sc->gpios[i] = gpios->entries[i];
474 			sc->gpio_count = count;
475 			break;
476 		}
477 
478 		case CB_TAG_TPM_PPI_HANDOFF: {
479 			struct cb_tpm_ppi *tpm = (struct cb_tpm_ppi *)rec;
480 
481 			if (rec_size < sizeof(*tpm))
482 				break;
483 			sc->tpm_ppi_addr = tpm->ppi_address;
484 			sc->tpm_version = tpm->tpm_version;
485 			sc->has_tpm = 1;
486 			break;
487 		}
488 
489 		case CB_TAG_TIMESTAMPS: {
490 			struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
491 
492 			if (rec_size < sizeof(*ref))
493 				break;
494 			sc->timestamps_paddr = (vm_paddr_t)ref->cbmem_addr;
495 			sc->has_timestamps = 1;
496 			break;
497 		}
498 
499 		case CB_TAG_ACPI_GNVS: {
500 			struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
501 
502 			if (rec_size < sizeof(*ref))
503 				break;
504 			sc->acpi_gnvs_paddr = (vm_paddr_t)ref->cbmem_addr;
505 			sc->has_acpi_gnvs = 1;
506 			break;
507 		}
508 
509 		case CB_TAG_ACPI_CNVS: {
510 			struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
511 
512 			if (rec_size < sizeof(*ref))
513 				break;
514 			sc->acpi_cnvs_paddr = (vm_paddr_t)ref->cbmem_addr;
515 			sc->has_acpi_cnvs = 1;
516 			break;
517 		}
518 
519 		case CB_TAG_VPD: {
520 			struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
521 
522 			if (rec_size < sizeof(*ref))
523 				break;
524 			sc->vpd_paddr = (vm_paddr_t)ref->cbmem_addr;
525 			sc->has_vpd = 1;
526 			break;
527 		}
528 
529 		case CB_TAG_WIFI_CALIBRATION: {
530 			struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
531 
532 			if (rec_size < sizeof(*ref))
533 				break;
534 			sc->wifi_cal_paddr = (vm_paddr_t)ref->cbmem_addr;
535 			sc->has_wifi_cal = 1;
536 			break;
537 		}
538 
539 		case CB_TAG_FMAP: {
540 			struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
541 
542 			if (rec_size < sizeof(*ref))
543 				break;
544 			sc->fmap_paddr = (vm_paddr_t)ref->cbmem_addr;
545 			sc->has_fmap = 1;
546 			break;
547 		}
548 
549 		case CB_TAG_VBOOT_WORKBUF: {
550 			struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
551 
552 			if (rec_size < sizeof(*ref))
553 				break;
554 			sc->vboot_workbuf_paddr = (vm_paddr_t)ref->cbmem_addr;
555 			sc->has_vboot_workbuf = 1;
556 			break;
557 		}
558 
559 		case CB_TAG_TYPE_C_INFO: {
560 			struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
561 
562 			if (rec_size < sizeof(*ref))
563 				break;
564 			sc->type_c_info_paddr = (vm_paddr_t)ref->cbmem_addr;
565 			sc->has_type_c_info = 1;
566 			break;
567 		}
568 
569 		case CB_TAG_ROOT_BRIDGE_INFO: {
570 			struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
571 
572 			if (rec_size < sizeof(*ref))
573 				break;
574 			sc->root_bridge_info_paddr =
575 			    (vm_paddr_t)ref->cbmem_addr;
576 			sc->has_root_bridge_info = 1;
577 			break;
578 		}
579 
580 		case CB_TAG_TPM_CB_LOG: {
581 			struct cb_cbmem_ref *ref = (struct cb_cbmem_ref *)rec;
582 
583 			if (rec_size < sizeof(*ref))
584 				break;
585 			sc->tpm_log_paddr = (vm_paddr_t)ref->cbmem_addr;
586 			sc->has_tpm_log = 1;
587 			break;
588 		}
589 
590 		case CB_TAG_SMMSTOREV2: {
591 			struct cb_smmstorev2 *smm =
592 			    (struct cb_smmstorev2 *)rec;
593 
594 			if (rec_size < CB_SMMSTOREV2_BASE_SIZE)
595 				break;
596 			sc->smmstore_num_blocks = smm->num_blocks;
597 			sc->smmstore_block_size = smm->block_size;
598 			sc->smmstore_com_buffer = smm->com_buffer;
599 			sc->smmstore_apm_cmd = smm->apm_cmd;
600 			/* 64-bit mmap_addr only present in newer coreboot */
601 			if (rec_size >= sizeof(*smm))
602 				sc->smmstore_mmap_addr = smm->mmap_addr;
603 			else
604 				sc->smmstore_mmap_addr =
605 				    (uint64_t)smm->mmap_addr_lo;
606 			sc->has_smmstore = 1;
607 			break;
608 		}
609 
610 		default:
611 			break;
612 		}
613 
614 		entry += rec_size;
615 	}
616 }
617 
618 /*
619  * Table-driven sysctl registration.
620  *
621  * Each leaf descriptor specifies the parent node, name, type, data offset
622  * into softc, a guard flag offset (or -1 for unconditional), and flags.
623  * Nodes that group related leaves are indexed by cb_sysctl_node.
624  */
625 
626 /* Node indices for parent selection */
627 enum cb_sysctl_node {
628 	CB_NODE_ROOT = 0,
629 	CB_NODE_MAINBOARD,
630 	CB_NODE_SERIAL,
631 	CB_NODE_BOARD,
632 	CB_NODE_BOOT_MEDIA,
633 	CB_NODE_SPI_FLASH,
634 	CB_NODE_FRAMEBUFFER,
635 	CB_NODE_TPM,
636 	CB_NODE_SMMSTORE,
637 	CB_NODE_CBMEM_REFS,
638 	CB_NODE_COUNT
639 };
640 
641 /* Sysctl value type discriminator */
642 enum cb_sysctl_type {
643 	CB_SYSCTL_U8,
644 	CB_SYSCTL_U16,
645 	CB_SYSCTL_U32,
646 	CB_SYSCTL_S32,
647 	CB_SYSCTL_U64,
648 	CB_SYSCTL_ULONG,
649 	CB_SYSCTL_STRING,
650 };
651 
652 /*
653  * Guard mode: how to decide whether a leaf should be registered.
654  *   STR_NONEMPTY: check that the char[] at guard_off is non-empty
655  *   FLAG_SET:     check that the int at guard_off is non-zero
656  *   ALWAYS:       unconditional (guard_off ignored)
657  */
658 enum cb_sysctl_guard {
659 	CB_GUARD_ALWAYS,
660 	CB_GUARD_FLAG_SET,
661 	CB_GUARD_STR_NONEMPTY,
662 };
663 
664 struct cb_sysctl_node_desc {
665 	enum cb_sysctl_node	id;
666 	enum cb_sysctl_node	parent;
667 	const char		*name;
668 	const char		*desc;
669 };
670 
671 struct cb_sysctl_leaf {
672 	enum cb_sysctl_node	parent;
673 	const char		*name;
674 	enum cb_sysctl_type	type;
675 	size_t			data_off;
676 	enum cb_sysctl_guard	guard;
677 	size_t			guard_off;
678 	int			flags;
679 	const char		*desc;
680 };
681 
682 /* Helper macros for field offset within coreboot_softc */
683 #define SC_OFF(field)	offsetof(struct coreboot_softc, field)
684 
685 static const struct cb_sysctl_node_desc cb_nodes[] = {
686 	{ CB_NODE_MAINBOARD,	CB_NODE_ROOT,	"mainboard",
687 	    "Mainboard information" },
688 	{ CB_NODE_SERIAL,	CB_NODE_ROOT,	"serial",
689 	    "Serial port" },
690 	{ CB_NODE_BOARD,	CB_NODE_ROOT,	"board",
691 	    "Board identification" },
692 	{ CB_NODE_BOOT_MEDIA,	CB_NODE_ROOT,	"boot_media",
693 	    "Boot media parameters" },
694 	{ CB_NODE_SPI_FLASH,	CB_NODE_ROOT,	"spi_flash",
695 	    "SPI flash parameters" },
696 	{ CB_NODE_FRAMEBUFFER,	CB_NODE_ROOT,	"framebuffer",
697 	    "Framebuffer information" },
698 	{ CB_NODE_TPM,		CB_NODE_ROOT,	"tpm",
699 	    "TPM information" },
700 	{ CB_NODE_SMMSTORE,	CB_NODE_ROOT,	"smmstore",
701 	    "SMMSTORE v2 configuration" },
702 	{ CB_NODE_CBMEM_REFS,	CB_NODE_ROOT,	"cbmem_refs",
703 	    "Additional CBMEM reference addresses" },
704 };
705 
706 static const struct cb_sysctl_leaf cb_leaves[] = {
707 	/* Root-level strings (guarded by non-empty string) */
708 	{ CB_NODE_ROOT, "version", CB_SYSCTL_STRING,
709 	    SC_OFF(version), CB_GUARD_STR_NONEMPTY, SC_OFF(version),
710 	    CTLFLAG_RD, "Firmware version" },
711 	{ CB_NODE_ROOT, "build", CB_SYSCTL_STRING,
712 	    SC_OFF(build), CB_GUARD_STR_NONEMPTY, SC_OFF(build),
713 	    CTLFLAG_RD, "Build date" },
714 	{ CB_NODE_ROOT, "compile_time", CB_SYSCTL_STRING,
715 	    SC_OFF(compile_time), CB_GUARD_STR_NONEMPTY, SC_OFF(compile_time),
716 	    CTLFLAG_RD, "Firmware compile time" },
717 	{ CB_NODE_ROOT, "compiler", CB_SYSCTL_STRING,
718 	    SC_OFF(compiler), CB_GUARD_STR_NONEMPTY, SC_OFF(compiler),
719 	    CTLFLAG_RD, "Compiler info" },
720 	{ CB_NODE_ROOT, "extra_version", CB_SYSCTL_STRING,
721 	    SC_OFF(extra_version), CB_GUARD_STR_NONEMPTY, SC_OFF(extra_version),
722 	    CTLFLAG_RD, "Extra version info" },
723 	{ CB_NODE_ROOT, "serialno", CB_SYSCTL_STRING,
724 	    SC_OFF(serialno), CB_GUARD_STR_NONEMPTY, SC_OFF(serialno),
725 	    CTLFLAG_RD, "Serial number" },
726 	{ CB_NODE_ROOT, "platform_blob_version", CB_SYSCTL_STRING,
727 	    SC_OFF(platform_blob_version), CB_GUARD_STR_NONEMPTY,
728 	    SC_OFF(platform_blob_version),
729 	    CTLFLAG_RD, "Platform blob version" },
730 
731 	/* Root-level scalars */
732 	{ CB_NODE_ROOT, "version_timestamp", CB_SYSCTL_U32,
733 	    SC_OFF(version_timestamp), CB_GUARD_FLAG_SET,
734 	    SC_OFF(has_version_timestamp),
735 	    CTLFLAG_RD, "Firmware version timestamp" },
736 	{ CB_NODE_ROOT, "table_addr", CB_SYSCTL_U64,
737 	    SC_OFF(table_paddr), CB_GUARD_ALWAYS, 0,
738 	    CTLFLAG_RD, "Physical address of coreboot table" },
739 	{ CB_NODE_ROOT, "table_size", CB_SYSCTL_ULONG,
740 	    SC_OFF(table_size), CB_GUARD_ALWAYS, 0,
741 	    CTLFLAG_RD, "Total coreboot table size" },
742 	{ CB_NODE_ROOT, "tsc_freq_khz", CB_SYSCTL_U32,
743 	    SC_OFF(tsc_freq_khz), CB_GUARD_FLAG_SET, SC_OFF(has_tsc_info),
744 	    CTLFLAG_RD, "TSC frequency in kHz" },
745 	{ CB_NODE_ROOT, "pcie_ctrl_base", CB_SYSCTL_U64,
746 	    SC_OFF(pcie_ctrl_base), CB_GUARD_FLAG_SET, SC_OFF(has_pcie),
747 	    CTLFLAG_RD, "PCIe controller base address" },
748 	{ CB_NODE_ROOT, "acpi_rsdp", CB_SYSCTL_U64,
749 	    SC_OFF(acpi_rsdp), CB_GUARD_FLAG_SET, SC_OFF(has_acpi_rsdp),
750 	    CTLFLAG_RD, "ACPI RSDP physical address" },
751 	{ CB_NODE_ROOT, "mmc_early_cmd1_status", CB_SYSCTL_S32,
752 	    SC_OFF(mmc_early_cmd1_status), CB_GUARD_FLAG_SET,
753 	    SC_OFF(has_mmc_info),
754 	    CTLFLAG_RD, "Early eMMC CMD1 status" },
755 	{ CB_NODE_ROOT, "console_type", CB_SYSCTL_U16,
756 	    SC_OFF(console_type), CB_GUARD_FLAG_SET, SC_OFF(has_console_type),
757 	    CTLFLAG_RD, "Firmware console type" },
758 	{ CB_NODE_ROOT, "timestamps_addr", CB_SYSCTL_U64,
759 	    SC_OFF(timestamps_paddr), CB_GUARD_FLAG_SET, SC_OFF(has_timestamps),
760 	    CTLFLAG_RD, "Timestamps CBMEM physical address" },
761 
762 	/* Mainboard children */
763 	{ CB_NODE_MAINBOARD, "vendor", CB_SYSCTL_STRING,
764 	    SC_OFF(mb_vendor), CB_GUARD_STR_NONEMPTY, SC_OFF(mb_vendor),
765 	    CTLFLAG_RD, "Board vendor" },
766 	{ CB_NODE_MAINBOARD, "part", CB_SYSCTL_STRING,
767 	    SC_OFF(mb_part), CB_GUARD_STR_NONEMPTY, SC_OFF(mb_part),
768 	    CTLFLAG_RD, "Board part number" },
769 
770 	/* Serial children */
771 	{ CB_NODE_SERIAL, "baseaddr", CB_SYSCTL_U32,
772 	    SC_OFF(serial_baseaddr), CB_GUARD_FLAG_SET, SC_OFF(has_serial),
773 	    CTLFLAG_RD, "Base address" },
774 	{ CB_NODE_SERIAL, "baud", CB_SYSCTL_U32,
775 	    SC_OFF(serial_baud), CB_GUARD_FLAG_SET, SC_OFF(has_serial),
776 	    CTLFLAG_RD, "Baud rate" },
777 	{ CB_NODE_SERIAL, "regwidth", CB_SYSCTL_U32,
778 	    SC_OFF(serial_regwidth), CB_GUARD_FLAG_SET, SC_OFF(has_serial),
779 	    CTLFLAG_RD, "Register width" },
780 
781 	/* Board config children */
782 	{ CB_NODE_BOARD, "fw_config", CB_SYSCTL_U64,
783 	    SC_OFF(fw_config), CB_GUARD_FLAG_SET, SC_OFF(has_board_config),
784 	    CTLFLAG_RD, "Firmware configuration bitmask" },
785 	{ CB_NODE_BOARD, "board_id", CB_SYSCTL_U32,
786 	    SC_OFF(board_id), CB_GUARD_FLAG_SET, SC_OFF(has_board_config),
787 	    CTLFLAG_RD, "Board ID" },
788 	{ CB_NODE_BOARD, "ram_code", CB_SYSCTL_U32,
789 	    SC_OFF(ram_code), CB_GUARD_FLAG_SET, SC_OFF(has_board_config),
790 	    CTLFLAG_RD, "RAM code" },
791 	{ CB_NODE_BOARD, "sku_id", CB_SYSCTL_U32,
792 	    SC_OFF(sku_id), CB_GUARD_FLAG_SET, SC_OFF(has_board_config),
793 	    CTLFLAG_RD, "SKU ID" },
794 
795 	/* Boot media children */
796 	{ CB_NODE_BOOT_MEDIA, "fmap_offset", CB_SYSCTL_U64,
797 	    SC_OFF(fmap_offset), CB_GUARD_FLAG_SET, SC_OFF(has_boot_media),
798 	    CTLFLAG_RD, "FMAP offset from boot media start" },
799 	{ CB_NODE_BOOT_MEDIA, "cbfs_offset", CB_SYSCTL_U64,
800 	    SC_OFF(cbfs_offset), CB_GUARD_FLAG_SET, SC_OFF(has_boot_media),
801 	    CTLFLAG_RD, "CBFS offset from boot media start" },
802 	{ CB_NODE_BOOT_MEDIA, "cbfs_size", CB_SYSCTL_U64,
803 	    SC_OFF(cbfs_size), CB_GUARD_FLAG_SET, SC_OFF(has_boot_media),
804 	    CTLFLAG_RD, "CBFS size in bytes" },
805 	{ CB_NODE_BOOT_MEDIA, "size", CB_SYSCTL_U64,
806 	    SC_OFF(boot_media_size), CB_GUARD_FLAG_SET, SC_OFF(has_boot_media),
807 	    CTLFLAG_RD, "Boot media size in bytes" },
808 
809 	/* SPI flash children */
810 	{ CB_NODE_SPI_FLASH, "size", CB_SYSCTL_U32,
811 	    SC_OFF(spi_flash_size), CB_GUARD_FLAG_SET, SC_OFF(has_spi_flash),
812 	    CTLFLAG_RD, "Flash size in bytes" },
813 	{ CB_NODE_SPI_FLASH, "sector_size", CB_SYSCTL_U32,
814 	    SC_OFF(spi_sector_size), CB_GUARD_FLAG_SET, SC_OFF(has_spi_flash),
815 	    CTLFLAG_RD, "Sector size in bytes" },
816 	{ CB_NODE_SPI_FLASH, "erase_cmd", CB_SYSCTL_U8,
817 	    SC_OFF(spi_erase_cmd), CB_GUARD_FLAG_SET, SC_OFF(has_spi_flash),
818 	    CTLFLAG_RD, "Erase command byte" },
819 
820 	/* Framebuffer children */
821 	{ CB_NODE_FRAMEBUFFER, "addr", CB_SYSCTL_U64,
822 	    SC_OFF(fb_addr), CB_GUARD_FLAG_SET, SC_OFF(has_framebuffer),
823 	    CTLFLAG_RD, "Physical address" },
824 	{ CB_NODE_FRAMEBUFFER, "x_res", CB_SYSCTL_U32,
825 	    SC_OFF(fb_x_res), CB_GUARD_FLAG_SET, SC_OFF(has_framebuffer),
826 	    CTLFLAG_RD, "Horizontal resolution" },
827 	{ CB_NODE_FRAMEBUFFER, "y_res", CB_SYSCTL_U32,
828 	    SC_OFF(fb_y_res), CB_GUARD_FLAG_SET, SC_OFF(has_framebuffer),
829 	    CTLFLAG_RD, "Vertical resolution" },
830 	{ CB_NODE_FRAMEBUFFER, "bpp", CB_SYSCTL_U8,
831 	    SC_OFF(fb_bpp), CB_GUARD_FLAG_SET, SC_OFF(has_framebuffer),
832 	    CTLFLAG_RD, "Bits per pixel" },
833 
834 	/* TPM children */
835 	{ CB_NODE_TPM, "version", CB_SYSCTL_U8,
836 	    SC_OFF(tpm_version), CB_GUARD_FLAG_SET, SC_OFF(has_tpm),
837 	    CTLFLAG_RD, "TPM version (1=1.2, 2=2.0)" },
838 	{ CB_NODE_TPM, "ppi_addr", CB_SYSCTL_U32,
839 	    SC_OFF(tpm_ppi_addr), CB_GUARD_FLAG_SET, SC_OFF(has_tpm),
840 	    CTLFLAG_RD, "PPI address" },
841 	{ CB_NODE_TPM, "cblog_addr", CB_SYSCTL_U64,
842 	    SC_OFF(tpm_log_paddr), CB_GUARD_FLAG_SET, SC_OFF(has_tpm_log),
843 	    CTLFLAG_RD, "TPM event log physical address" },
844 
845 	/* SMMSTORE children */
846 	{ CB_NODE_SMMSTORE, "num_blocks", CB_SYSCTL_U32,
847 	    SC_OFF(smmstore_num_blocks), CB_GUARD_FLAG_SET,
848 	    SC_OFF(has_smmstore),
849 	    CTLFLAG_RD, "Number of blocks" },
850 	{ CB_NODE_SMMSTORE, "block_size", CB_SYSCTL_U32,
851 	    SC_OFF(smmstore_block_size), CB_GUARD_FLAG_SET,
852 	    SC_OFF(has_smmstore),
853 	    CTLFLAG_RD, "Block size in bytes" },
854 	{ CB_NODE_SMMSTORE, "mmap_addr", CB_SYSCTL_U64,
855 	    SC_OFF(smmstore_mmap_addr), CB_GUARD_FLAG_SET,
856 	    SC_OFF(has_smmstore),
857 	    CTLFLAG_RD, "Memory-mapped address" },
858 	{ CB_NODE_SMMSTORE, "com_buffer", CB_SYSCTL_U32,
859 	    SC_OFF(smmstore_com_buffer), CB_GUARD_FLAG_SET,
860 	    SC_OFF(has_smmstore),
861 	    CTLFLAG_RD, "Communication buffer address" },
862 	{ CB_NODE_SMMSTORE, "apm_cmd", CB_SYSCTL_U8,
863 	    SC_OFF(smmstore_apm_cmd), CB_GUARD_FLAG_SET,
864 	    SC_OFF(has_smmstore),
865 	    CTLFLAG_RD, "APM command byte" },
866 
867 	/* CBMEM reference addresses */
868 	{ CB_NODE_CBMEM_REFS, "acpi_gnvs", CB_SYSCTL_U64,
869 	    SC_OFF(acpi_gnvs_paddr), CB_GUARD_FLAG_SET, SC_OFF(has_acpi_gnvs),
870 	    CTLFLAG_RD, "ACPI GNVS CBMEM physical address" },
871 	{ CB_NODE_CBMEM_REFS, "acpi_cnvs", CB_SYSCTL_U64,
872 	    SC_OFF(acpi_cnvs_paddr), CB_GUARD_FLAG_SET, SC_OFF(has_acpi_cnvs),
873 	    CTLFLAG_RD, "ACPI CNVS CBMEM physical address" },
874 	{ CB_NODE_CBMEM_REFS, "vpd", CB_SYSCTL_U64,
875 	    SC_OFF(vpd_paddr), CB_GUARD_FLAG_SET, SC_OFF(has_vpd),
876 	    CTLFLAG_RD, "VPD CBMEM physical address" },
877 	{ CB_NODE_CBMEM_REFS, "wifi_calibration", CB_SYSCTL_U64,
878 	    SC_OFF(wifi_cal_paddr), CB_GUARD_FLAG_SET, SC_OFF(has_wifi_cal),
879 	    CTLFLAG_RD, "WiFi calibration CBMEM physical address" },
880 	{ CB_NODE_CBMEM_REFS, "fmap", CB_SYSCTL_U64,
881 	    SC_OFF(fmap_paddr), CB_GUARD_FLAG_SET, SC_OFF(has_fmap),
882 	    CTLFLAG_RD, "FMAP CBMEM physical address" },
883 	{ CB_NODE_CBMEM_REFS, "vboot_workbuf", CB_SYSCTL_U64,
884 	    SC_OFF(vboot_workbuf_paddr), CB_GUARD_FLAG_SET,
885 	    SC_OFF(has_vboot_workbuf),
886 	    CTLFLAG_RD, "Vboot work buffer CBMEM physical address" },
887 	{ CB_NODE_CBMEM_REFS, "type_c_info", CB_SYSCTL_U64,
888 	    SC_OFF(type_c_info_paddr), CB_GUARD_FLAG_SET,
889 	    SC_OFF(has_type_c_info),
890 	    CTLFLAG_RD, "Type-C info CBMEM physical address" },
891 	{ CB_NODE_CBMEM_REFS, "root_bridge_info", CB_SYSCTL_U64,
892 	    SC_OFF(root_bridge_info_paddr), CB_GUARD_FLAG_SET,
893 	    SC_OFF(has_root_bridge_info),
894 	    CTLFLAG_RD, "Root bridge info CBMEM physical address" },
895 };
896 
897 /*
898  * Check whether a leaf's guard condition is satisfied.
899  */
900 static int
901 cb_sysctl_guard_check(const struct cb_sysctl_leaf *leaf,
902     const struct coreboot_softc *sc)
903 {
904 	const char *base;
905 
906 	base = (const char *)sc;
907 	switch (leaf->guard) {
908 	case CB_GUARD_ALWAYS:
909 		return (1);
910 	case CB_GUARD_FLAG_SET:
911 		return (*(const int *)(base + leaf->guard_off) != 0);
912 	case CB_GUARD_STR_NONEMPTY:
913 		return (*(base + leaf->guard_off) != '\0');
914 	}
915 	return (0);
916 }
917 
918 /*
919  * Type-to-handler mapping for sysctl_add_oid().
920  * Mirrors the SYSCTL_ADD_* macros but avoids their CTASSERT on flags.
921  */
922 static const struct {
923 	int		ctltype;
924 	int		(*handler)(SYSCTL_HANDLER_ARGS);
925 	const char	*fmt;
926 } cb_sysctl_types[] = {
927 	[CB_SYSCTL_U8]     = { CTLTYPE_U8,	sysctl_handle_8,	"CU" },
928 	[CB_SYSCTL_U16]    = { CTLTYPE_U16,	sysctl_handle_16,	"SU" },
929 	[CB_SYSCTL_U32]    = { CTLTYPE_U32,	sysctl_handle_32,	"IU" },
930 	[CB_SYSCTL_S32]    = { CTLTYPE_S32,	sysctl_handle_32,	"I" },
931 	[CB_SYSCTL_U64]    = { CTLTYPE_U64,	sysctl_handle_64,	"QU" },
932 	[CB_SYSCTL_ULONG]  = { CTLTYPE_ULONG,	sysctl_handle_long,	"LU" },
933 	[CB_SYSCTL_STRING] = { CTLTYPE_STRING,	sysctl_handle_string,	"A" },
934 };
935 
936 /*
937  * Add a single sysctl leaf under the given parent OID.
938  */
939 static void
940 cb_sysctl_add_leaf(struct sysctl_ctx_list *ctx, struct sysctl_oid *parent,
941     const struct cb_sysctl_leaf *leaf, struct coreboot_softc *sc)
942 {
943 	void *ptr;
944 
945 	ptr = (char *)sc + leaf->data_off;
946 
947 	sysctl_add_oid(ctx, SYSCTL_CHILDREN(parent), OID_AUTO,
948 	    leaf->name,
949 	    cb_sysctl_types[leaf->type].ctltype | CTLFLAG_MPSAFE | leaf->flags,
950 	    ptr, 0,
951 	    cb_sysctl_types[leaf->type].handler,
952 	    cb_sysctl_types[leaf->type].fmt,
953 	    __DESCR(leaf->desc), NULL);
954 }
955 
956 /*
957  * Register the sysctl tree under hw.coreboot.*
958  *
959  * Static leaves and nodes are driven by the cb_leaves[] and cb_nodes[]
960  * tables.  Dynamic entries (CBMEM, MAC, GPIO) that require loops over
961  * runtime-determined counts are handled explicitly below the table loop.
962  */
963 static void
964 coreboot_register_sysctls(struct coreboot_softc *sc)
965 {
966 	struct sysctl_oid *nodes[CB_NODE_COUNT];
967 	struct sysctl_oid *oid_cbmem, *oid_entry;
968 	struct sysctl_oid *oid_mac, *oid_gpio, *oid_pin;
969 	char numstr[8];
970 	uint32_t i;
971 	int any_cbref;
972 
973 	sysctl_ctx_init(&sc->sysctl_ctx);
974 	sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
975 	    SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "coreboot",
976 	    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "coreboot firmware information");
977 
978 	if (sc->sysctl_tree == NULL)
979 		return;
980 
981 	memset(nodes, 0, sizeof(nodes));
982 	nodes[CB_NODE_ROOT] = sc->sysctl_tree;
983 
984 	SYSCTL_ADD_INT(&sc->sysctl_ctx,
985 	    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "debug",
986 	    CTLFLAG_RW, &coreboot_debug, 0,
987 	    "Enable verbose coreboot diagnostics");
988 
989 	/*
990 	 * Create intermediate nodes on demand.
991 	 *
992 	 * The mainboard node is special: it appears when either vendor or
993 	 * part is present.  The TPM node appears when has_tpm or has_tpm_log
994 	 * is set.  The cbmem_refs node appears when any of its children
995 	 * would be registered.  All other nodes are gated by the guard
996 	 * flags on their children (a node is created the first time a child
997 	 * needs it).
998 	 */
999 
1000 	/* Pre-create mainboard node if either string is populated */
1001 	if (sc->mb_vendor[0] != '\0' || sc->mb_part[0] != '\0')
1002 		nodes[CB_NODE_MAINBOARD] = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
1003 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "mainboard",
1004 		    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Mainboard information");
1005 
1006 	/* TPM node appears for has_tpm OR has_tpm_log */
1007 	if (sc->has_tpm || sc->has_tpm_log)
1008 		nodes[CB_NODE_TPM] = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
1009 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "tpm",
1010 		    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "TPM information");
1011 
1012 	/* cbmem_refs node: created if any ref address is present */
1013 	any_cbref = sc->has_acpi_gnvs || sc->has_acpi_cnvs || sc->has_vpd ||
1014 	    sc->has_wifi_cal || sc->has_fmap || sc->has_vboot_workbuf ||
1015 	    sc->has_type_c_info || sc->has_root_bridge_info;
1016 	if (any_cbref)
1017 		nodes[CB_NODE_CBMEM_REFS] = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
1018 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "cbmem_refs",
1019 		    CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
1020 		    "Additional CBMEM reference addresses");
1021 
1022 	/* Walk the leaf table and register matching entries */
1023 	for (i = 0; i < nitems(cb_leaves); i++) {
1024 		const struct cb_sysctl_leaf *leaf = &cb_leaves[i];
1025 		enum cb_sysctl_node nid = leaf->parent;
1026 
1027 		if (!cb_sysctl_guard_check(leaf, sc))
1028 			continue;
1029 
1030 		/* Lazily create the parent node if not yet instantiated */
1031 		if (nodes[nid] == NULL) {
1032 			const struct cb_sysctl_node_desc *nd;
1033 			uint32_t j;
1034 
1035 			for (j = 0; j < nitems(cb_nodes); j++) {
1036 				if (cb_nodes[j].id == nid)
1037 					break;
1038 			}
1039 			if (j >= nitems(cb_nodes))
1040 				continue;
1041 			nd = &cb_nodes[j];
1042 			nodes[nid] = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
1043 			    SYSCTL_CHILDREN(nodes[nd->parent]), OID_AUTO,
1044 			    nd->name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
1045 			    nd->desc);
1046 			if (nodes[nid] == NULL)
1047 				continue;
1048 		}
1049 
1050 		cb_sysctl_add_leaf(&sc->sysctl_ctx, nodes[nid], leaf, sc);
1051 	}
1052 
1053 	/* --- Dynamic entries that don't fit the static table --- */
1054 
1055 	/* CBMEM entry enumeration */
1056 	if (sc->cbmem_count > 0) {
1057 		oid_cbmem = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
1058 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "cbmem",
1059 		    CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "CBMEM entries");
1060 
1061 		for (i = 0; i < sc->cbmem_count; i++) {
1062 			struct cbmem_entry_info *info = &sc->cbmem_entries[i];
1063 
1064 			snprintf(numstr, sizeof(numstr), "%u", i);
1065 			oid_entry = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
1066 			    SYSCTL_CHILDREN(oid_cbmem), OID_AUTO, numstr,
1067 			    CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
1068 			    "CBMEM entry");
1069 
1070 			SYSCTL_ADD_STRING(&sc->sysctl_ctx,
1071 			    SYSCTL_CHILDREN(oid_entry), OID_AUTO, "name",
1072 			    CTLFLAG_RD, info->name, 0, "Entry name");
1073 
1074 			SYSCTL_ADD_U32(&sc->sysctl_ctx,
1075 			    SYSCTL_CHILDREN(oid_entry), OID_AUTO, "id",
1076 			    CTLFLAG_RD, &info->id, 0, "Entry ID (hex)");
1077 
1078 			SYSCTL_ADD_U64(&sc->sysctl_ctx,
1079 			    SYSCTL_CHILDREN(oid_entry), OID_AUTO, "address",
1080 			    CTLFLAG_RD, (uint64_t *)&info->address, 0,
1081 			    "Physical address");
1082 
1083 			SYSCTL_ADD_U32(&sc->sysctl_ctx,
1084 			    SYSCTL_CHILDREN(oid_entry), OID_AUTO, "size",
1085 			    CTLFLAG_RD, &info->size, 0, "Entry size");
1086 		}
1087 	}
1088 
1089 	/* Factory MAC addresses */
1090 	if (sc->mac_count > 0) {
1091 		oid_mac = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
1092 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "mac",
1093 		    CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
1094 		    "Factory MAC addresses");
1095 
1096 		for (i = 0; i < sc->mac_count; i++) {
1097 			uint8_t *m = sc->macs[i].mac_addr;
1098 
1099 			snprintf(numstr, sizeof(numstr), "%u", i);
1100 			snprintf(sc->mac_strs[i], sizeof(sc->mac_strs[i]),
1101 			    "%02x:%02x:%02x:%02x:%02x:%02x",
1102 			    m[0], m[1], m[2], m[3], m[4], m[5]);
1103 
1104 			SYSCTL_ADD_STRING(&sc->sysctl_ctx,
1105 			    SYSCTL_CHILDREN(oid_mac), OID_AUTO, numstr,
1106 			    CTLFLAG_RD, sc->mac_strs[i], 0,
1107 			    "MAC address");
1108 		}
1109 	}
1110 
1111 	/* GPIO pins */
1112 	if (sc->gpio_count > 0) {
1113 		oid_gpio = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
1114 		    SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, "gpio",
1115 		    CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
1116 		    "GPIO pin states");
1117 
1118 		for (i = 0; i < sc->gpio_count; i++) {
1119 			struct cb_gpio *g = &sc->gpios[i];
1120 
1121 			snprintf(numstr, sizeof(numstr), "%u", i);
1122 			oid_pin = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
1123 			    SYSCTL_CHILDREN(oid_gpio), OID_AUTO, numstr,
1124 			    CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
1125 			    "GPIO pin");
1126 
1127 			/* Ensure name is NUL-terminated */
1128 			g->name[sizeof(g->name) - 1] = '\0';
1129 			SYSCTL_ADD_STRING(&sc->sysctl_ctx,
1130 			    SYSCTL_CHILDREN(oid_pin), OID_AUTO, "name",
1131 			    CTLFLAG_RD, g->name, 0, "Pin name");
1132 
1133 			SYSCTL_ADD_U32(&sc->sysctl_ctx,
1134 			    SYSCTL_CHILDREN(oid_pin), OID_AUTO, "port",
1135 			    CTLFLAG_RD, &g->port, 0, "Port number");
1136 
1137 			SYSCTL_ADD_U32(&sc->sysctl_ctx,
1138 			    SYSCTL_CHILDREN(oid_pin), OID_AUTO, "value",
1139 			    CTLFLAG_RD, &g->value, 0, "Pin value");
1140 
1141 			SYSCTL_ADD_U32(&sc->sysctl_ctx,
1142 			    SYSCTL_CHILDREN(oid_pin), OID_AUTO, "polarity",
1143 			    CTLFLAG_RD, &g->polarity, 0, "Pin polarity");
1144 		}
1145 	}
1146 
1147 	/* Timestamp PROC sysctl */
1148 	if (sc->has_timestamps)
1149 		coreboot_timestamps_register(sc, sc->sysctl_tree);
1150 }
1151 
1152 /*
1153  * Map and validate a coreboot table at physical address pa.
1154  * On success, *vap points to the mapped table and *sizep is the total size.
1155  * The caller must pmap_unmapbios(*vap, *sizep) when done.
1156  */
1157 static int
1158 coreboot_map_table(vm_paddr_t pa, void **vap, vm_size_t *sizep)
1159 {
1160 	struct cb_header *hdr;
1161 	void *va;
1162 	vm_size_t map_size;
1163 	int error;
1164 
1165 	va = pmap_mapbios(pa, sizeof(struct cb_header));
1166 	if (va == NULL)
1167 		return (ENOMEM);
1168 
1169 	hdr = (struct cb_header *)va;
1170 	if (memcmp(hdr->signature, CB_HEADER_SIGNATURE,
1171 	    CB_HEADER_SIG_LEN) != 0) {
1172 		pmap_unmapbios(va, sizeof(struct cb_header));
1173 		return (ENXIO);
1174 	}
1175 
1176 	error = coreboot_sanitize_header(hdr, &map_size);
1177 	pmap_unmapbios(va, sizeof(struct cb_header));
1178 	if (error != 0)
1179 		return (ENXIO);
1180 
1181 	va = pmap_mapbios(pa, map_size);
1182 	if (va == NULL)
1183 		return (ENOMEM);
1184 
1185 	hdr = (struct cb_header *)va;
1186 	if (coreboot_validate_header(hdr, map_size) != 0 ||
1187 	    coreboot_validate_table(hdr, map_size) != 0) {
1188 		pmap_unmapbios(va, map_size);
1189 		return (ENXIO);
1190 	}
1191 
1192 	*vap = va;
1193 	*sizep = map_size;
1194 	return (0);
1195 }
1196 
1197 /*
1198  * Identify: scan low memory for "LBIO" signature and register a child
1199  */
1200 static void
1201 coreboot_identify(driver_t *driver, device_t parent)
1202 {
1203 	vm_paddr_t low_addr, real_addr;
1204 	struct cb_header *hdr;
1205 	uint8_t *entry, *table_end;
1206 	struct cb_record *rec;
1207 	device_t child;
1208 	void *va;
1209 	vm_size_t map_size;
1210 	int error;
1211 
1212 	if (!device_is_alive(parent))
1213 		return;
1214 
1215 	if (device_find_child(parent, "coreboot", -1) != NULL)
1216 		return;
1217 
1218 	low_addr = coreboot_scan_region(CB_SCAN_LOW_START, CB_SCAN_LOW_END);
1219 	if (low_addr == 0)
1220 		return;
1221 
1222 	va = pmap_mapbios(low_addr, sizeof(struct cb_header));
1223 	if (va == NULL)
1224 		return;
1225 
1226 	/* Scan already verified the signature; re-read to get sizes. */
1227 	hdr = (struct cb_header *)va;
1228 	error = coreboot_sanitize_header(hdr, &map_size);
1229 	pmap_unmapbios(va, sizeof(struct cb_header));
1230 	if (error != 0)
1231 		return;
1232 
1233 	va = pmap_mapbios(low_addr, map_size);
1234 	if (va == NULL)
1235 		return;
1236 
1237 	hdr = (struct cb_header *)va;
1238 	if (coreboot_validate_header(hdr, map_size) != 0 ||
1239 	    coreboot_validate_table(hdr, map_size) != 0) {
1240 		pmap_unmapbios(va, map_size);
1241 		return;
1242 	}
1243 
1244 	/* Look for CB_TAG_FORWARD to find the real table in high memory */
1245 	real_addr = low_addr;
1246 	entry = (uint8_t *)hdr + hdr->header_bytes;
1247 	table_end = entry + hdr->table_bytes;
1248 	while ((size_t)(table_end - entry) >= sizeof(struct cb_record)) {
1249 		size_t rec_size;
1250 
1251 		rec = (struct cb_record *)entry;
1252 		rec_size = rec->size;
1253 		if (rec_size < sizeof(struct cb_record))
1254 			break;
1255 		if (rec_size > (size_t)(table_end - entry))
1256 			break;
1257 		if (rec->tag == CB_TAG_FORWARD) {
1258 			if (rec_size >= sizeof(struct cb_forward)) {
1259 				struct cb_forward *fwd;
1260 
1261 				fwd = (struct cb_forward *)entry;
1262 				real_addr = (vm_paddr_t)fwd->forward;
1263 			}
1264 			break;
1265 		}
1266 		entry += rec_size;
1267 	}
1268 	pmap_unmapbios(va, map_size);
1269 
1270 	child = BUS_ADD_CHILD(parent, 5, "coreboot", DEVICE_UNIT_ANY);
1271 	if (child == NULL)
1272 		return;
1273 	device_set_driver(child, driver);
1274 
1275 	bus_set_resource(child, SYS_RES_MEMORY, 0, real_addr, PAGE_SIZE);
1276 	device_set_desc(child, "coreboot firmware table");
1277 }
1278 
1279 /*
1280  * Probe: validate the coreboot header at the discovered address
1281  */
1282 static int
1283 coreboot_probe(device_t dev)
1284 {
1285 	vm_paddr_t pa;
1286 	void *va;
1287 	vm_size_t map_size;
1288 	int error;
1289 
1290 	pa = bus_get_resource_start(dev, SYS_RES_MEMORY, 0);
1291 	if (pa == 0)
1292 		return (ENXIO);
1293 
1294 	error = coreboot_map_table(pa, &va, &map_size);
1295 	if (error != 0)
1296 		return (error);
1297 
1298 	pmap_unmapbios(va, map_size);
1299 	return (BUS_PROBE_SPECIFIC);
1300 }
1301 
1302 /*
1303  * Attach: map the full table, parse records, register sysctls and cdevs
1304  */
1305 static int
1306 coreboot_attach(device_t dev)
1307 {
1308 	struct coreboot_softc *sc;
1309 	struct cb_header *hdr;
1310 	vm_paddr_t pa;
1311 	void *va;
1312 	vm_size_t map_size;
1313 	int error;
1314 
1315 	sc = device_get_softc(dev);
1316 	sc->dev = dev;
1317 
1318 	pa = bus_get_resource_start(dev, SYS_RES_MEMORY, 0);
1319 
1320 	error = coreboot_map_table(pa, &va, &map_size);
1321 	if (error != 0) {
1322 		device_printf(dev, "coreboot table validation failed at %#jx\n",
1323 		    (uintmax_t)pa);
1324 		return (error);
1325 	}
1326 
1327 	sc->table_paddr = pa;
1328 	sc->table_size = map_size;
1329 	sc->table_vaddr = va;
1330 
1331 	hdr = (struct cb_header *)va;
1332 	device_printf(dev, "coreboot table at %#jx (%u entries, %u bytes)\n",
1333 	    (uintmax_t)pa, hdr->table_entries, hdr->table_bytes);
1334 
1335 	coreboot_parse_table(sc, hdr);
1336 
1337 	if (sc->version[0] != '\0')
1338 		device_printf(dev, "firmware: %s\n", sc->version);
1339 	if (sc->mb_vendor[0] != '\0')
1340 		device_printf(dev, "mainboard: %s %s\n", sc->mb_vendor,
1341 		    sc->mb_part);
1342 	if (sc->has_console)
1343 		device_printf(dev, "CBMEM console at %#jx\n",
1344 		    (uintmax_t)sc->console_paddr);
1345 	device_printf(dev, "CBMEM entries: %u\n", sc->cbmem_count);
1346 	if (sc->has_board_config)
1347 		device_printf(dev,
1348 		    "board: id=%u sku=%u fw_config=%#jx\n",
1349 		    sc->board_id, sc->sku_id,
1350 		    (uintmax_t)sc->fw_config);
1351 	if (sc->mac_count > 0)
1352 		device_printf(dev, "factory MAC addresses: %u\n",
1353 		    sc->mac_count);
1354 	if (sc->has_acpi_rsdp)
1355 		device_printf(dev, "ACPI RSDP at %#jx\n",
1356 		    (uintmax_t)sc->acpi_rsdp);
1357 	if (sc->has_pcie)
1358 		device_printf(dev, "PCIe controller at %#jx\n",
1359 		    (uintmax_t)sc->pcie_ctrl_base);
1360 	if (sc->has_boot_media)
1361 		device_printf(dev, "boot media: %#jx bytes, CBFS %#jx+%#jx\n",
1362 		    (uintmax_t)sc->boot_media_size,
1363 		    (uintmax_t)sc->cbfs_offset, (uintmax_t)sc->cbfs_size);
1364 	if (sc->has_mmc_info)
1365 		device_printf(dev, "MMC early CMD1 status: %d\n",
1366 		    sc->mmc_early_cmd1_status);
1367 
1368 	if (bootverbose) {
1369 		if (sc->has_spi_flash)
1370 			device_printf(dev,
1371 			    "SPI flash: %u bytes, sector %u, erase %#x\n",
1372 			    sc->spi_flash_size, sc->spi_sector_size,
1373 			    sc->spi_erase_cmd);
1374 		if (sc->has_console_type)
1375 			device_printf(dev, "console type: %u\n",
1376 			    sc->console_type);
1377 		if (sc->has_framebuffer)
1378 			device_printf(dev,
1379 			    "framebuffer: %ux%u@%ubpp at %#jx\n",
1380 			    sc->fb_x_res, sc->fb_y_res, sc->fb_bpp,
1381 			    (uintmax_t)sc->fb_addr);
1382 		if (sc->gpio_count > 0)
1383 			device_printf(dev, "GPIO pins: %u\n",
1384 			    sc->gpio_count);
1385 		if (sc->has_tpm)
1386 			device_printf(dev, "TPM %u.%u PPI at %#x\n",
1387 			    sc->tpm_version == 2 ? 2 : 1,
1388 			    sc->tpm_version == 2 ? 0 : 2,
1389 			    sc->tpm_ppi_addr);
1390 	}
1391 
1392 	if (coreboot_debug) {
1393 		if (sc->has_smmstore)
1394 			device_printf(dev,
1395 			    "SMMSTORE v2: %u blocks x %u bytes, "
1396 			    "apm_cmd=%#x\n",
1397 			    sc->smmstore_num_blocks,
1398 			    sc->smmstore_block_size,
1399 			    sc->smmstore_apm_cmd);
1400 		if (sc->has_timestamps)
1401 			device_printf(dev, "timestamps at %#jx\n",
1402 			    (uintmax_t)sc->timestamps_paddr);
1403 		if (sc->has_tpm_log)
1404 			device_printf(dev, "TPM CB log at %#jx\n",
1405 			    (uintmax_t)sc->tpm_log_paddr);
1406 		if (sc->has_fmap)
1407 			device_printf(dev, "FMAP at %#jx\n",
1408 			    (uintmax_t)sc->fmap_paddr);
1409 	}
1410 
1411 	coreboot_register_sysctls(sc);
1412 
1413 	coreboot_sc = sc;
1414 
1415 	if (sc->has_console) {
1416 		error = coreboot_console_create(sc);
1417 		if (error != 0)
1418 			device_printf(dev,
1419 			    "failed to create /dev/coreboot_console (%d)\n",
1420 			    error);
1421 	}
1422 	if (sc->cbmem_count > 0) {
1423 		error = coreboot_cbmem_create(sc);
1424 		if (error != 0)
1425 			device_printf(dev, "failed to create /dev/cbmem (%d)\n",
1426 			    error);
1427 	}
1428 
1429 	return (0);
1430 }
1431 
1432 /*
1433  * Detach: unmap table, destroy cdevs and sysctls
1434  */
1435 static int
1436 coreboot_detach(device_t dev)
1437 {
1438 	struct coreboot_softc *sc;
1439 
1440 	sc = device_get_softc(dev);
1441 
1442 	coreboot_cbmem_destroy(sc);
1443 	coreboot_console_destroy(sc);
1444 
1445 	coreboot_sc = NULL;
1446 
1447 	sysctl_ctx_free(&sc->sysctl_ctx);
1448 
1449 	if (sc->table_vaddr != NULL) {
1450 		pmap_unmapbios(sc->table_vaddr, sc->table_size);
1451 		sc->table_vaddr = NULL;
1452 	}
1453 
1454 	if (sc->console_vaddr != NULL) {
1455 		pmap_unmapbios(sc->console_vaddr, sc->console_size);
1456 		sc->console_vaddr = NULL;
1457 		sc->console_size = 0;
1458 		sc->console_data_size = 0;
1459 	}
1460 
1461 	return (0);
1462 }
1463 
1464 static int
1465 coreboot_modevent(module_t mod, int what, void *arg)
1466 {
1467 	device_t *devs;
1468 	int count, i;
1469 
1470 	switch (what) {
1471 	case MOD_LOAD:
1472 		break;
1473 	case MOD_UNLOAD:
1474 		devclass_get_devices(devclass_find("coreboot"), &devs, &count);
1475 		for (i = 0; i < count; i++)
1476 			device_delete_child(device_get_parent(devs[i]),
1477 			    devs[i]);
1478 		free(devs, M_TEMP);
1479 		break;
1480 	default:
1481 		break;
1482 	}
1483 
1484 	return (0);
1485 }
1486 
1487 static device_method_t coreboot_methods[] = {
1488 	DEVMETHOD(device_identify,	coreboot_identify),
1489 	DEVMETHOD(device_probe,		coreboot_probe),
1490 	DEVMETHOD(device_attach,	coreboot_attach),
1491 	DEVMETHOD(device_detach,	coreboot_detach),
1492 	DEVMETHOD_END
1493 };
1494 
1495 static driver_t coreboot_driver = {
1496 	"coreboot",
1497 	coreboot_methods,
1498 	sizeof(struct coreboot_softc),
1499 };
1500 
1501 DRIVER_MODULE(coreboot, nexus, coreboot_driver, coreboot_modevent, NULL);
1502 MODULE_VERSION(coreboot, 1);
1503