[PATCH 2/4] coreboot: Add support for detecting Coreboot BIOS signatures

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Add support for Coreboot BIOS detection. This in turn can be used by
platform drivers to verify they are running on the correct hardware,
as many of the low-volume SBC's (especially in the Atom and Geode
universe) don't always identify themselves via DMI or PCI-ID.

The coreboot project lives at:

http://www.coreboot.org/

and the related project Flashrom lives here:

http://flashrom.org/

This library pulls from both, though predominantly the former.

The library locates the Coreboot tables in memory, parses them, and
provides a way to extract from the table the motherboard vendor and
model fields for use in platform drivers.

Signed-off-by: Philip Prindeville <philipp@xxxxxxxxxxxxxxxxxxxxx>
Reviewed-by: Ed Wildgoose <ed@xxxxxxxxxxxxxx>
Cc: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx>
Cc: Andres Salomon <dilinger@xxxxxxxxxx>
Cc: Nathan Williams <nathan@xxxxxxxxxxxxxxx>
Cc: Guy Ellis <guy@xxxxxxxxxxxxxxx>
Cc: David Woodhouse <dwmw2@xxxxxxxxxxxxx>
Cc: Patrick Georgi <patrick.georgi@xxxxxxxxxxx>
Cc: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@xxxxxxx>
Cc: linux-geode@xxxxxxxxxxxxxxxxxxx
Cc: linux-mm@xxxxxxxxx
---
 Documentation/x86/coreboot.txt |   31 +++++
 include/linux/coreboot.h       |  182 +++++++++++++++++++++++++
 lib/Kconfig                    |    8 +
 lib/Makefile                   |    1 +
 lib/coreboot.c                 |  290 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 512 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/x86/coreboot.txt
 create mode 100644 include/linux/coreboot.h
 create mode 100644 lib/coreboot.c

diff --git a/Documentation/x86/coreboot.txt b/Documentation/x86/coreboot.txt
new file mode 100644
index 0000000..35be360
--- /dev/null
+++ b/Documentation/x86/coreboot.txt
@@ -0,0 +1,31 @@
+Coreboot is an open source bootloader/BIOS. It currently is supported on x86, but
+has been made to run on PPC hardware as well.  The project lives here:
+
+http://www.coreboot.org/
+
+It was previously known as LinuxBIOS.
+
+It has been ported to various boards, including the Geode single-board computers alix, wrap, and geos.
+
+The list is here:
+
+http://www.coreboot.org/Supported_Motherboards
+
+http://www.coreboot.org/Supported_Chipsets_and_Devices
+
+Coreboot requires gcc, binutils, and make to be built.
+
+Part of coreboot's initialization includes creating a table (called, not surprisingly, the
+coreboot table) that contains various information about the system, such as memory apertures,
+serial ports, the manufacturer and model number of the system, etc.
+
+A bootable utility called FlashROM is available for programming flash chips. It lives here:
+
+http://flashrom.org/Documentation
+
+it includes a library for reading coreboot tables. Source for flashrom is here:
+
+svn://flashrom.org/flashrom/trunk
+
+The lib/coreboot.c module borrows heavily from flashrom's cbtable.c and coreboot_tables.h
+
diff --git a/include/linux/coreboot.h b/include/linux/coreboot.h
new file mode 100644
index 0000000..0ed6118
--- /dev/null
+++ b/include/linux/coreboot.h
@@ -0,0 +1,182 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2002 Linux Networx
+ * (Written by Eric Biederman <ebiederman@xxxxxxxx> for Linux Networx)
+ * Copyright (C) 2005-2007 coresystems GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ */
+
+#ifndef COREBOOT_H
+#define COREBOOT_H
+
+#include <linux/types.h>
+
+/* The coreboot table information is for conveying information
+ * from the firmware to the loaded OS image.  Primarily this
+ * is expected to be information that cannot be discovered by
+ * other means, such as querying the hardware directly.
+ *
+ * All of the information should be Position Independent Data.  
+ * That is it should be safe to relocated any of the information
+ * without it's meaning/correctness changing.   For table that
+ * can reasonably be used on multiple architectures the data
+ * size should be fixed.  This should ease the transition between
+ * 32 bit and 64 bit architectures etc.
+ *
+ * The completeness test for the information in this table is:
+ * - Can all of the hardware be detected?
+ * - Are the per motherboard constants available?
+ * - Is there enough to allow a kernel to run that was written before
+ *   a particular motherboard is constructed? (Assuming the kernel
+ *   has drivers for all of the hardware but it does not have
+ *   assumptions on how the hardware is connected together).
+ *
+ * With this test it should be straight forward to determine if a
+ * table entry is required or not.  This should remove much of the
+ * long term compatibility burden as table entries which are
+ * irrelevant or have been replaced by better alternatives may be
+ * dropped.  Of course it is polite and expedite to include extra
+ * table entries and be backwards compatible, but it is not required.
+ */
+
+/* Since coreboot is usually compiled 32bit, gcc will align 64bit 
+ * types to 32bit boundaries. If the coreboot table is dumped on a 
+ * 64bit system, a uint64_t would be aligned to 64bit boundaries, 
+ * breaking the table format.
+ *
+ * lb_uint64 will keep 64bit coreboot table values aligned to 32bit
+ * to ensure compatibility. They can be accessed with the two functions
+ * below: unpack_lb64() and pack_lb64()
+ *
+ * See also: util/lbtdump/lbtdump.c
+ */
+
+struct lb_uint64 {
+	uint32_t lo;
+	uint32_t hi;
+};
+
+static inline uint64_t unpack_lb64(struct lb_uint64 value)
+{
+	uint64_t result;
+	result = value.hi;
+	result = (result << 32) + value.lo;
+	return result;
+}
+
+static inline struct lb_uint64 pack_lb64(uint64_t value)
+{
+	struct lb_uint64 result;
+	result.lo = (value >> 0) & 0xffffffff;
+	result.hi = (value >> 32) & 0xffffffff;
+	return result;
+}
+
+struct lb_header {
+	uint8_t signature[4];	/* LBIO */
+	uint32_t header_bytes;
+	uint32_t header_checksum;
+	uint32_t table_bytes;
+	uint32_t table_checksum;
+	uint32_t table_entries;
+};
+
+/* Every entry in the boot environment list will correspond to a boot
+ * info record.  Encoding both type and size.  The type is obviously
+ * so you can tell what it is.  The size allows you to skip that
+ * boot environment record if you don't know what it easy.  This allows
+ * forward compatibility with records not yet defined.
+ */
+struct lb_record {
+	uint32_t tag;		/* tag ID */
+	uint32_t size;		/* size of record (in bytes) */
+};
+
+#define LB_TAG_UNUSED	0x0000
+
+#define LB_TAG_MEMORY	0x0001
+
+struct lb_memory_range {
+	struct lb_uint64 start;
+	struct lb_uint64 size;
+	uint32_t type;
+#define LB_MEM_RAM       1	/* Memory anyone can use */
+#define LB_MEM_RESERVED  2	/* Don't use this memory region */
+#define LB_MEM_TABLE     16	/* Ram configuration tables are kept in */
+};
+
+struct lb_memory {
+	uint32_t tag;
+	uint32_t size;
+	struct lb_memory_range map[0];
+};
+
+#define LB_TAG_HWRPB	0x0002
+struct lb_hwrpb {
+	uint32_t tag;
+	uint32_t size;
+	uint64_t hwrpb;
+};
+
+#define LB_TAG_MAINBOARD	0x0003
+struct lb_mainboard {
+	uint32_t tag;
+	uint32_t size;
+	uint8_t vendor_idx;
+	uint8_t part_number_idx;
+	uint8_t strings[0];
+};
+
+#define LB_TAG_VERSION		0x0004
+#define LB_TAG_EXTRA_VERSION	0x0005
+#define LB_TAG_BUILD		0x0006
+#define LB_TAG_COMPILE_TIME	0x0007
+#define LB_TAG_COMPILE_BY	0x0008
+#define LB_TAG_COMPILE_HOST	0x0009
+#define LB_TAG_COMPILE_DOMAIN	0x000a
+#define LB_TAG_COMPILER		0x000b
+#define LB_TAG_LINKER		0x000c
+#define LB_TAG_ASSEMBLER	0x000d
+struct lb_string {
+	uint32_t tag;
+	uint32_t size;
+	uint8_t string[0];
+};
+
+#define LB_TAG_FORWARD		0x0011
+struct lb_forward {
+	uint32_t tag;
+	uint32_t size;
+	uint64_t forward;
+};
+
+/*
+ * vendor string, taken from mainboard record.
+ */
+extern const char *coreboot_vendor(void);
+
+/*
+ * part # string, taken from mainboard record.
+ */
+extern const char *coreboot_part(void);
+
+/*
+ * search for Coreboot tables. if found, search entries for
+ * mainboard record and parse it. return address of first record.
+ */
+extern struct lb_record *coreboot_init(void);
+
+#endif				/* COREBOOT_TABLES_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index 201e1b3..d880af8 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -98,6 +98,14 @@ config AUDIT_GENERIC
 	depends on AUDIT && !AUDIT_ARCH
 	default y
 
+config COREBOOT
+	bool "Coreboot location/parsing support"
+	depends on X86
+	help
+	  Support for boards running coreboot. It allows platform drivers to
+	  detect their manufacturer and part number by interrogating the BIOS
+	  populated tables.
+
 #
 # compression support is select'ed if needed
 #
diff --git a/lib/Makefile b/lib/Makefile
index dace162..9dd493d 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_CRC7)	+= crc7.o
 obj-$(CONFIG_LIBCRC32C)	+= libcrc32c.o
 obj-$(CONFIG_CRC8)	+= crc8.o
 obj-$(CONFIG_GENERIC_ALLOCATOR) += genalloc.o
+obj-$(CONFIG_COREBOOT)	+= coreboot.o
 
 obj-$(CONFIG_ZLIB_INFLATE) += zlib_inflate/
 obj-$(CONFIG_ZLIB_DEFLATE) += zlib_deflate/
diff --git a/lib/coreboot.c b/lib/coreboot.c
new file mode 100644
index 0000000..f3dd191
--- /dev/null
+++ b/lib/coreboot.c
@@ -0,0 +1,290 @@
+/*
+ * This file was extracted from parts of the flashrom project.
+ *
+ * Copyright (C) 2002 Steven James <pyro@xxxxxxxxxxxxx>
+ * Copyright (C) 2002 Linux Networx
+ * (Written by Eric Biederman <ebiederman@xxxxxxxx> for Linux Networx)
+ * Copyright (C) 2006-2009 coresystems GmbH
+ * (Written by Stefan Reinauer <stepan@xxxxxxxxxxxxxx> for coresystems GmbH)
+ * Copyright (C) 2010 Carl-Daniel Hailfinger
+ * Copyright (C) 2011 Philip Prindeville
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ */
+
+#include <linux/types.h>
+#include <linux/unistd.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/io.h>
+
+#include <linux/ioport.h>
+
+#include <asm/checksum.h>
+#include <asm/page.h>
+
+#include <linux/coreboot.h>
+
+#define for_each_lbrec(head, rec) \
+	for(rec = (struct lb_record *)(((caddr_t)head) + sizeof(*head)); \
+		(((caddr_t)rec) < (((caddr_t)head) + sizeof(*head) + head->table_bytes))  && \
+		(rec->size >= 1) && \
+		((((caddr_t)rec) + rec->size) <= (((caddr_t)head) + sizeof(*head) + head->table_bytes)); \
+		rec = (struct lb_record *)(((caddr_t)rec) + rec->size))
+
+static char lb_part[32], lb_vendor[32];
+
+static inline __sum16 compute_checksum(void *addr, unsigned long length)
+{
+	return ip_compute_csum(addr, length);
+}
+
+static inline uintptr_t vpage_offset(void *addr)
+{
+	return ((uintptr_t)addr & ~PAGE_MASK);
+}
+
+static inline void *vpage_base(void *addr)
+{
+	return (void *)((uintptr_t)addr & PAGE_MASK);
+}
+
+static inline void *__v_offset(void *addr, unsigned offset)
+{
+	return (void *)((caddr_t)addr + offset);
+}
+
+static inline uintptr_t ppage_offset(phys_addr_t addr)
+{
+	return (addr & ~PAGE_MASK);
+}
+
+static inline phys_addr_t ppage_base(phys_addr_t addr)
+{
+	return (phys_addr_t)(addr & PAGE_MASK);
+}
+
+static unsigned count_lb_records(const struct lb_header *head)
+{
+	struct lb_record *rec;
+	unsigned count;
+
+	count = 0;
+	for_each_lbrec(head, rec) {
+		count++;
+	}
+
+	return count;
+}
+
+#ifdef DEBUG
+static void dump_lb_header(const struct lb_header *lh)
+{
+	printk(KERN_DEBUG "header 0x%p\n"
+			  "  sig: %.4s, bytes %u, chksum %#04x,"
+			  " bytes %u, chksum %#04x, entries %u\n",
+	       lh, lh->signature, lh->header_bytes, lh->header_checksum,
+	       lh->table_bytes, lh->table_checksum, lh->table_entries);
+}
+
+static void dump_lb_forward(const struct lb_forward *lf)
+{
+	printk(KERN_DEBUG "forward 0x%p\n  tag %04x, size %u, forward %#llx\n",
+	       lf, lf->tag, lf->size, lf->forward);
+}
+
+static void dump_lb_mainboard(struct lb_mainboard *lm)
+{
+	printk(KERN_DEBUG "mainboard: 0x%p\n  tag %04x, size %u, vendor %s, part %s\n",
+	       lm, lm->tag, lm->size, &lm->strings[lm->vendor_idx],
+	       &lm->strings[lm->part_number_idx]);
+}
+#endif
+
+static struct lb_header *find_lb_table(void *base, void *end)
+{
+	void *addr;
+
+	printk(KERN_DEBUG "Starting search at 0x%p\n", base);
+	/* For now be stupid.... */
+	for (addr = base; addr < end; addr += 16) {
+		struct lb_header *head = (struct lb_header *)addr;
+		struct lb_record *recs;
+
+		if (memcmp(head->signature, "LBIO", 4) != 0)
+			continue;
+		printk(KERN_DEBUG "Found candidate at: 0x%p-%p\n",
+			     addr, addr + head->table_bytes);
+#ifdef DEBUG
+		dump_lb_header(head);
+#endif
+		if (head->header_bytes != sizeof(*head)) {
+			printk(KERN_DEBUG "Header bytes of %u are incorrect.\n",
+				head->header_bytes);
+			continue;
+		}
+		if (count_lb_records(head) != head->table_entries) {
+			printk(KERN_DEBUG "Bad record count: %u.\n",
+				head->table_entries);
+			continue;
+		}
+		if (compute_checksum(head, sizeof(*head)) != 0) {
+			printk(KERN_DEBUG "Bad header checksum.\n");
+			continue;
+		}
+		recs = (struct lb_record *)__v_offset(addr, sizeof(*head));
+		if (compute_checksum(recs, head->table_bytes)
+		    != head->table_checksum) {
+			printk(KERN_DEBUG "Bad table checksum: %#04x.\n",
+				head->table_checksum);
+			continue;
+		}
+		printk(KERN_DEBUG "Found coreboot table at 0x%p.\n", addr);
+		return head;
+
+	};
+
+	return 0;
+}
+
+static void find_mainboard(struct lb_record *ptr)
+{
+	struct lb_mainboard *rec = (struct lb_mainboard *)ptr;
+
+#ifdef DEBUG
+	dump_lb_mainboard(rec);
+#endif
+
+	strlcpy(lb_vendor, &rec->strings[rec->vendor_idx], sizeof(lb_vendor));
+	strlcpy(lb_part, &rec->strings[rec->part_number_idx], sizeof(lb_part));
+}
+
+static struct lb_record *next_record(struct lb_record *rec)
+{
+	return (struct lb_record *)__v_offset(rec, rec->size);
+}
+
+static void search_lb_records(struct lb_record *rec, struct lb_record *last)
+{
+	struct lb_record *next;
+
+	for (next = next_record(rec); (rec < last) && (next <= last); rec = next) {
+		next = next_record(rec);
+		if (rec->tag == LB_TAG_MAINBOARD) {
+			find_mainboard(rec);
+			break;
+		}
+	}
+}
+
+
+#define BYTES_TO_MAP (1024*1024)
+struct lb_record *coreboot_init(void)
+{
+	phys_addr_t start;
+	void *addr, *remap;
+	struct lb_header *lb_table;
+	struct lb_record *rec, *last;
+
+	remap = NULL;
+
+	start = 0x0;
+ 	addr = phys_to_virt(start);
+
+	lb_table = find_lb_table(addr, addr + 0x1000);
+	if (!lb_table) {
+		start = 0xf0000;
+		addr = phys_to_virt(start);
+		lb_table = find_lb_table(addr, addr + BYTES_TO_MAP);
+	}
+	if (lb_table) {
+		struct lb_forward *forward = (struct lb_forward *)
+			__v_offset(lb_table, lb_table->header_bytes);
+
+		if (forward->tag == LB_TAG_FORWARD) {
+			int mapped = 0;
+			phys_addr_t forward_phys = forward->forward;
+			phys_addr_t upper = iomem_map_find_boundary(forward_phys, &mapped);
+			unsigned extent = BYTES_TO_MAP;
+
+#ifdef DEBUG
+			dump_lb_forward(forward);
+#endif
+
+			if (!upper)
+				goto no_region;	/* not a valid address */
+
+			if (mapped) {
+				addr = phys_to_virt(forward_phys);
+			} else {
+				phys_addr_t base = ppage_base(forward_phys);
+
+				upper += 1;
+				if (base + extent > upper)
+					extent = upper - base;
+
+				remap = ioremap(base, extent);
+				if (!remap) {
+					printk(KERN_DEBUG "Couldn't map %x, %u.\n",
+						base, extent);
+					return NULL;
+				}
+					
+				addr = remap + ppage_offset(forward_phys);
+			}
+
+			lb_table = find_lb_table(addr, __v_offset(vpage_base(addr), extent));
+		}
+	}
+
+	if (!lb_table) {
+no_region:
+		printk(KERN_DEBUG "No coreboot table found.\n");
+		return NULL;
+	}
+
+	printk(KERN_DEBUG "coreboot table found at 0x%x.\n", virt_to_phys(lb_table));
+
+	rec = (struct lb_record *)__v_offset(lb_table, lb_table->header_bytes);
+	last = (struct lb_record *)__v_offset(rec, lb_table->table_bytes);
+
+#ifdef DEBUG
+	dump_lb_header(lb_table);
+#endif
+	search_lb_records(rec, last);
+
+	if (remap) {
+		iounmap(remap);
+	}
+
+	return rec;
+}
+EXPORT_SYMBOL(coreboot_init);
+
+const char *coreboot_vendor(void)
+{
+	return lb_vendor;
+}
+EXPORT_SYMBOL(coreboot_vendor);
+
+const char *coreboot_part(void)
+{
+	return lb_part;
+}
+EXPORT_SYMBOL(coreboot_part);
+
-- 
1.7.7.4

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@xxxxxxxxx.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@xxxxxxxxx";> email@xxxxxxxxx </a>


[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]