[LWN Logo]
[LWN.net]
From:	 ebiederm@xmission.com (Eric W. Biederman)
To:	 Linus Torvalds <torvalds@transmeta.com>, Dave Jones <davej@suse.de>
Subject: [PATCH 2.5.12] x86 Boot enhancements, LinuxBIOS support 10/11
Date:	 02 May 2002 09:13:15 -0600
Cc:	 <linux-kernel@vger.kernel.org>

Please apply,

This patch adds support for LinuxBIOS.

Eric

diff -uNr linux-2.5.12.boot.proto/arch/i386/Config.help linux-2.5.12.boot.linuxbios/arch/i386/Config.help
--- linux-2.5.12.boot.proto/arch/i386/Config.help	Wed May  1 09:41:19 2002
+++ linux-2.5.12.boot.linuxbios/arch/i386/Config.help	Wed May  1 09:41:27 2002
@@ -803,6 +803,22 @@
   a work-around for a number of buggy BIOSes. Switch this option on if
   your computer crashes instead of powering off properly.
 
+LinuxBIOS support
+CONFIG_LINUXBIOS
+  LinuxBIOS is an alternative GPL'd firmware initially for x86
+  compatible machines.  LinuxBIOS attempts to be a simple and as fast
+  as possible, and is initially optimized for linux.  The interface to
+  the OS is a table at the beginning of memory.  Which should make the
+  inevitable firmware bugs easier to work around, as should the source
+  code :)  A goal of this table is to provide enough information so
+  that any OS but especially linux does not need to have motherboard
+  specific knowledge.
+
+  This first release only provides LinuxBIOS detection (by detecting
+  it's table) and memory size detection. 
+
+  For more information on LinuxBIOS see http://www.linuxbios.org
+
 CONFIG_CMDLINE
   Generally it is best to pass command line parameters via the
   bootloader but there are times it is convinient not to do this.
diff -uNr linux-2.5.12.boot.proto/arch/i386/config.in linux-2.5.12.boot.linuxbios/arch/i386/config.in
--- linux-2.5.12.boot.proto/arch/i386/config.in	Wed May  1 09:41:19 2002
+++ linux-2.5.12.boot.linuxbios/arch/i386/config.in	Wed May  1 09:41:27 2002
@@ -206,6 +206,7 @@
    fi
 fi
 
+bool 'LinuxBIOS support' CONFIG_LINUXBIOS
 endmenu
 
 mainmenu_option next_comment
diff -uNr linux-2.5.12.boot.proto/arch/i386/defconfig linux-2.5.12.boot.linuxbios/arch/i386/defconfig
--- linux-2.5.12.boot.proto/arch/i386/defconfig	Tue Apr 30 22:55:21 2002
+++ linux-2.5.12.boot.linuxbios/arch/i386/defconfig	Wed May  1 09:41:27 2002
@@ -119,6 +119,7 @@
 CONFIG_BINFMT_MISC=y
 CONFIG_PM=y
 # CONFIG_APM is not set
+# CONFIG_LINUXBIOS is not set
 
 #
 # Memory Technology Devices (MTD)
diff -uNr linux-2.5.12.boot.proto/arch/i386/kernel/Makefile linux-2.5.12.boot.linuxbios/arch/i386/kernel/Makefile
--- linux-2.5.12.boot.proto/arch/i386/kernel/Makefile	Tue Apr 16 11:10:43 2002
+++ linux-2.5.12.boot.linuxbios/arch/i386/kernel/Makefile	Wed May  1 09:41:27 2002
@@ -42,6 +42,7 @@
 obj-$(CONFIG_SMP)		+= smp.o smpboot.o trampoline.o
 obj-$(CONFIG_X86_LOCAL_APIC)	+= mpparse.o apic.o nmi.o
 obj-$(CONFIG_X86_IO_APIC)	+= io_apic.o
+obj-$(CONFIG_LINUXBIOS)		+= linuxbios.o
 ifdef CONFIG_VISWS
 obj-y += setup-visws.o
 obj-$(CONFIG_X86_VISWS_APIC)	+= visws_apic.o
diff -uNr linux-2.5.12.boot.proto/arch/i386/kernel/linuxbios.c linux-2.5.12.boot.linuxbios/arch/i386/kernel/linuxbios.c
--- linux-2.5.12.boot.proto/arch/i386/kernel/linuxbios.c	Wed Dec 31 17:00:00 1969
+++ linux-2.5.12.boot.linuxbios/arch/i386/kernel/linuxbios.c	Wed May  1 09:41:27 2002
@@ -0,0 +1,103 @@
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <net/checksum.h>
+#include <asm/linuxbios.h>
+#include <asm/e820.h>
+#include "linuxbios_tables.h"
+
+#define for_each_lbrec(head, rec) \
+	for(rec = (struct lb_record *)(((char *)head) + sizeof(*head)); \
+		(((char *)rec) < (((char *)head) + sizeof(*head) + head->table_bytes))  && \
+		(rec->size >= 1) && \
+		((((char *)rec) + rec->size) <= (((char *)head) + sizeof(*head) + head->table_bytes)); \
+		rec = (struct lb_record *)(((char *)rec) + rec->size)) 
+		
+
+static int __init count_lb_records(struct lb_header *head)
+{
+	struct lb_record *rec;
+	int count;
+	count = 0;
+	for_each_lbrec(head, rec) {
+		count++;
+	}
+	return count;
+}
+
+static struct lb_header * __init __find_lb_table(unsigned long start, unsigned long end)
+{
+	unsigned long addr;
+	/* For now be stupid.... */
+	for(addr = start; addr < end; addr += 16) {
+		struct lb_header *head = phys_to_virt(addr);
+		struct lb_record *recs = phys_to_virt(addr + sizeof(*head));
+		if (memcmp(head->signature, "LBIO", 4) != 0)
+			continue;
+		if (head->header_bytes != sizeof(*head))
+			continue;
+		if (ip_compute_csum((unsigned char *)head, sizeof(*head)) != 0)
+			continue;
+		if (ip_compute_csum((unsigned char *)recs, head->table_bytes) 
+			!= head->table_checksum)
+			continue;
+		if (count_lb_records(head) != head->table_entries)
+			continue;
+		printk(KERN_DEBUG "Found LinuxBIOS table at: %p\n", head);
+		return head;
+	};
+	return 0;
+}
+
+static struct lb_header * __init find_lb_table(void)
+{
+	struct lb_header *head;
+	head = 0;
+	if (!head) {
+		/* First try at address 0 */
+		head = __find_lb_table(0x00000, 0x1000);
+	}
+	if (!head) {
+		/* Then try at address 0xf0000 */
+		head = __find_lb_table(0xf0000, 0x100000);
+	}
+	return head;
+}
+
+void __init read_linuxbios_params(void)
+{
+	struct lb_header *head;
+	struct lb_record *rec;
+	struct lb_memory *mem;
+	int i, entries;
+	head = find_lb_table();
+	if (!head) {
+		return;
+	}
+	mem = 0;
+	for_each_lbrec(head, rec) {
+		if (rec->tag == LB_TAG_MEMORY) {
+			mem = (struct lb_memory *)rec;
+			break;
+		}
+	}
+	if (!mem) {
+		return;
+	}
+	entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]);
+	if (entries == 0)
+		return;
+	e820.nr_map = 0;
+	for(i = 0; i < entries; i++) {
+		unsigned long long start;
+		unsigned long long size;
+		unsigned long type;
+		start = mem->map[i].start;
+		size = mem->map[i].size;
+		type = (mem->map[i].type == LB_MEM_RAM)?E820_RAM: E820_RESERVED;
+		add_memory_region(start, size, type);
+	}
+	print_memory_map("LinuxBIOS");
+	return;
+}
+
diff -uNr linux-2.5.12.boot.proto/arch/i386/kernel/linuxbios_tables.h linux-2.5.12.boot.linuxbios/arch/i386/kernel/linuxbios_tables.h
--- linux-2.5.12.boot.proto/arch/i386/kernel/linuxbios_tables.h	Wed Dec 31 17:00:00 1969
+++ linux-2.5.12.boot.linuxbios/arch/i386/kernel/linuxbios_tables.h	Wed May  1 09:41:27 2002
@@ -0,0 +1,82 @@
+#ifndef LINUXBIOS_TABLES_H
+#define LINUXBIOS_TABLES_H
+
+#include <linux/types.h>
+
+/* The linuxbios 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 quering 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/correctnes 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 expidite to include extra
+ * table entries and be backwards compatible, but it is not required.
+ */
+
+
+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 enviroment 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 enviroment 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 {
+	uint64_t start;
+	uint64_t size;
+	uint32_t type;
+#define LB_MEM_RAM      1
+#define LB_MEM_RESERVED 2
+	
+};
+
+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;
+};
+
+#endif /* LINUXBIOS_TABLES_H */
diff -uNr linux-2.5.12.boot.proto/arch/i386/kernel/setup.c linux-2.5.12.boot.linuxbios/arch/i386/kernel/setup.c
--- linux-2.5.12.boot.proto/arch/i386/kernel/setup.c	Wed May  1 09:41:19 2002
+++ linux-2.5.12.boot.linuxbios/arch/i386/kernel/setup.c	Wed May  1 09:41:27 2002
@@ -119,6 +119,7 @@
 #include <asm/mmu_context.h>
 #include <asm/boot.h>
 #include <asm/boot_param.h>
+#include <asm/linuxbios.h>
 
 /*
  * Machine setup..
@@ -265,7 +266,7 @@
 	}
 }
 
-static void __init add_memory_region(unsigned long long start,
+void __init add_memory_region(unsigned long long start,
                                   unsigned long long size, int type)
 {
 	int x = e820.nr_map;
@@ -283,7 +284,7 @@
 
 #define E820_DEBUG	1
 
-static void __init print_memory_map(char *who)
+void __init print_memory_map(char *who)
 {
 	int i;
 
@@ -775,6 +776,9 @@
 	if (entry16) {
 		read_entry16_params(params);
 	}
+
+	/* Attempt to get the LinuxBIOS parameters */
+	read_linuxbios_params();
 
 	/* Read user specified params */
 	parse_mem_cmdline(cmdline_p);
diff -uNr linux-2.5.12.boot.proto/include/asm-i386/e820.h linux-2.5.12.boot.linuxbios/include/asm-i386/e820.h
--- linux-2.5.12.boot.proto/include/asm-i386/e820.h	Wed May  1 09:39:11 2002
+++ linux-2.5.12.boot.linuxbios/include/asm-i386/e820.h	Wed May  1 09:41:27 2002
@@ -36,6 +36,9 @@
 };
 
 extern struct e820map e820;
+extern void  add_memory_region(unsigned long long start,
+	unsigned long long size, int type);
+extern void print_memory_map(char *who);
 #endif/*!__ASSEMBLY__*/
 
 #endif/*__E820_HEADER*/
diff -uNr linux-2.5.12.boot.proto/include/asm-i386/linuxbios.h linux-2.5.12.boot.linuxbios/include/asm-i386/linuxbios.h
--- linux-2.5.12.boot.proto/include/asm-i386/linuxbios.h	Wed Dec 31 17:00:00 1969
+++ linux-2.5.12.boot.linuxbios/include/asm-i386/linuxbios.h	Wed May  1 09:41:27 2002
@@ -0,0 +1,10 @@
+#ifndef __ASMi386_LINUXBIOS_H
+#define __ASMi386_LINUXBIOS_H
+
+#ifdef CONFIG_LINUXBIOS
+void read_linuxbios_params(void);
+#else
+#define read_linuxbios_params()
+#endif /* CONFIG_LINUXBIOS */
+
+#endif
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/