[PATCH] x86 boot enhancements, linuxbios support

Eric W. Biederman (ebiederm@xmission.com)
03 Apr 2002 09:47:35 -0700


Linus please apply,

This patch is the first patch takes advantage of the earlier work in
this series of patches to implement native LinuxBIOS support.

For now I just detect LinuxBIOS and get the memory size. More will
come as the interface firms up. To the generic code is a 3 line code
change. Two functions are made non-static, and a call is inserted to
see what we can read from the LinuxBIOS table.

Eric

diff -uNr linux-2.5.7.boot3.proto/arch/i386/Config.help linux-2.5.7.boot3.linuxbios/arch/i386/Config.help
--- linux-2.5.7.boot3.proto/arch/i386/Config.help Wed Apr 3 02:06:43 2002
+++ linux-2.5.7.boot3.linuxbios/arch/i386/Config.help Wed Apr 3 02:07:21 2002
@@ -797,6 +797,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.7.boot3.proto/arch/i386/config.in linux-2.5.7.boot3.linuxbios/arch/i386/config.in
--- linux-2.5.7.boot3.proto/arch/i386/config.in Wed Apr 3 02:06:43 2002
+++ linux-2.5.7.boot3.linuxbios/arch/i386/config.in Wed Apr 3 02:07:21 2002
@@ -203,6 +203,7 @@
fi
fi

+bool 'LinuxBIOS support' CONFIG_LINUXBIOS
endmenu

mainmenu_option next_comment
diff -uNr linux-2.5.7.boot3.proto/arch/i386/defconfig linux-2.5.7.boot3.linuxbios/arch/i386/defconfig
--- linux-2.5.7.boot3.proto/arch/i386/defconfig Wed Mar 20 07:18:31 2002
+++ linux-2.5.7.boot3.linuxbios/arch/i386/defconfig Wed Apr 3 02:07:21 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.7.boot3.proto/arch/i386/kernel/Makefile linux-2.5.7.boot3.linuxbios/arch/i386/kernel/Makefile
--- linux-2.5.7.boot3.proto/arch/i386/kernel/Makefile Wed Mar 20 07:18:31 2002
+++ linux-2.5.7.boot3.linuxbios/arch/i386/kernel/Makefile Wed Apr 3 02:07:21 2002
@@ -41,6 +41,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.7.boot3.proto/arch/i386/kernel/linuxbios.c linux-2.5.7.boot3.linuxbios/arch/i386/kernel/linuxbios.c
--- linux-2.5.7.boot3.proto/arch/i386/kernel/linuxbios.c Wed Dec 31 17:00:00 1969
+++ linux-2.5.7.boot3.linuxbios/arch/i386/kernel/linuxbios.c Wed Apr 3 02:07:21 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.7.boot3.proto/arch/i386/kernel/linuxbios_tables.h linux-2.5.7.boot3.linuxbios/arch/i386/kernel/linuxbios_tables.h
--- linux-2.5.7.boot3.proto/arch/i386/kernel/linuxbios_tables.h Wed Dec 31 17:00:00 1969
+++ linux-2.5.7.boot3.linuxbios/arch/i386/kernel/linuxbios_tables.h Wed Apr 3 02:20:41 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.7.boot3.proto/arch/i386/kernel/setup.c linux-2.5.7.boot3.linuxbios/arch/i386/kernel/setup.c
--- linux-2.5.7.boot3.proto/arch/i386/kernel/setup.c Wed Apr 3 02:06:43 2002
+++ linux-2.5.7.boot3.linuxbios/arch/i386/kernel/setup.c Wed Apr 3 02:07:21 2002
@@ -117,6 +117,7 @@
#include <asm/mmu_context.h>
#include <asm/boot.h>
#include <asm/boot_param.h>
+#include <asm/linuxbios.h>

/*
* Machine setup..
@@ -262,7 +263,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;
@@ -280,7 +281,7 @@

#define E820_DEBUG 1

-static void __init print_memory_map(char *who)
+void __init print_memory_map(char *who)
{
int i;

@@ -751,6 +752,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.7.boot3.proto/include/asm-i386/e820.h linux-2.5.7.boot3.linuxbios/include/asm-i386/e820.h
--- linux-2.5.7.boot3.proto/include/asm-i386/e820.h Tue Apr 2 11:50:27 2002
+++ linux-2.5.7.boot3.linuxbios/include/asm-i386/e820.h Wed Apr 3 02:07:21 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.7.boot3.proto/include/asm-i386/linuxbios.h linux-2.5.7.boot3.linuxbios/include/asm-i386/linuxbios.h
--- linux-2.5.7.boot3.proto/include/asm-i386/linuxbios.h Wed Dec 31 17:00:00 1969
+++ linux-2.5.7.boot3.linuxbios/include/asm-i386/linuxbios.h Wed Apr 3 02:20:20 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/