I have just updated my patch so that etherboot can be loaded directly from linuxBIOS, to be against 5.0.2. You can now do ``make bin32/driver.ebi'' to build the driver. This should have no effects on the normal etherboot compilation. When compiling for use under linuxBIOS some compilation options fail to compile becase no native PC compatible BIOS is present, and the support routines have all been removed. This patch is archived at: ftp://download.linuxnetworx.com/pub/src/ethetboot/ethetboot-5.0.2.eb1.diff Eric diff -uNr etherboot-5.0.2/src/Config etherboot-5.0.2.eb1/src/Config --- etherboot-5.0.2/src/Config Sat Jun 23 02:54:49 2001 +++ etherboot-5.0.2.eb1/src/Config Sat Jun 30 17:35:07 2001 @@ -172,6 +172,11 @@ # Define this for PCI BIOSes that do not implement # BIOS32 or not correctly. Normally not needed. # Only works for BIOSes of a certain era. +# -DCONFIG_TSC_CURRTICKS +# Uses the processor time stamp counter instead of reading +# the BIOS time counter. This allows etherboot to work +# even without a BIOS. This only works on late model +# 486s and above. # # Obscure options you probably don't need to touch: # @@ -226,6 +231,9 @@ # Change download protocol to NFS, default is TFTP # CFLAGS32+= -DDOWNLOAD_PROTO_NFS + +# Options to make a version of etherboot that will work under linuxBIOS. +#CFLAGS32+= -DCONFIG_TSC_CURRTICKS -DCONSOLE_SERIAL -DCOMCONSOLE=0x3f8 +-DCONSPEED=115200 -DCONFIG_PCI_DIRECT -DELF_IMAGE -DIMAGE_MULTIBOOT # These flags affect the loader that is prepended to the Etherboot image LCONFIG+= -DMOVEROM diff -uNr etherboot-5.0.2/src/Makefile etherboot-5.0.2.eb1/src/Makefile --- etherboot-5.0.2/src/Makefile Thu Jun 21 09:44:39 2001 +++ etherboot-5.0.2.eb1/src/Makefile Sat Jun 30 17:04:21 2001 @@ -87,10 +87,11 @@ GCC= gcc CPP= gcc -E +STRIP= strip OBJCOPY= objcopy VERSION_MAJOR= 5 VERSION_MINOR= 0 -VERSION_PATCH= 2 +VERSION_PATCH= 2.eb1 VERSION= $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH) CFLAGS32+= -DVERSION_MAJOR=$(VERSION_MAJOR) \ -DVERSION_MINOR=$(VERSION_MINOR) \ @@ -158,6 +159,7 @@ LILOPREFIX= bin/liloprefix.bin START32= bin32/start32.o +UBE_START32 = bin32/ube_start32.o bin32/ube.o BOBJS32= bin32/main.o bin32/osloader.o bin32/nfs.o bin32/misc.o BOBJS32+= bin32/ansiesc.o bin32/bootmenu.o bin32/md5.o bin32/floppy.o @@ -166,6 +168,7 @@ LIBS32= $(BLIB32) $(LIBC32) UTILS+= bin/makerom bin/lzhuf STDDEPS32= $(START32) $(BLIB32) $(UTILS) +UBE_DEPS32= $(UBE_START32) $(BLIB2) MAKEDEPS= Makefile Config Roms CHECKSIZE= { read d1; read d1 d2 d3 size d4; [ $$size -gt $(ROMLIMIT) ] &&\ @@ -389,6 +392,7 @@ $(RM) bin32/*.dsk bin32/*.lzdsk $(RM) bin32/*.lilo bin32/*.lzlilo $(RM) bin32/*.pxe bin32/*.lzpxe + $(RM) bin32/*.elf bin32/*.ebi tarball: (echo -n $(VERSION) ''; date -u +'%Y-%m-%d') > ../VERSION diff -uNr etherboot-5.0.2/src/genrules.pl etherboot-5.0.2.eb1/src/genrules.pl --- etherboot-5.0.2/src/genrules.pl Mon Apr 23 07:51:53 2001 +++ etherboot-5.0.2.eb1/src/genrules.pl Sat Jun 30 17:05:29 2001 @@ -141,6 +141,9 @@ cat \$(PRZLOADER) \$< > \$@ bin/makerom \$(MAKEROM_\$*) -p $ids -i\$(IDENT32) \$@ +bin32/$rom.ebi: bin32/$drv.elf + cp bin32/$drv.elf \$@ + \$(STRIP) -R .comment -R .note \$@ EOF } foreach $rom (sort keys %roms_isa) { @@ -160,6 +163,10 @@ print <<EOF; bin32/$key.tmp: bin32/$key.o bin32/config-$key.o bin32/pci.o \$(STDDEPS32) \$(LD32) \$(LDFLAGS32) -o \$@ \$(START32) bin32/config-$key.o bin32/$key.o bin32/pci.o \$(LIBS32) + @\$(SIZE32) \$@ | \$(CHECKSIZE) + +bin32/$key.elf: bin32/$key.o bin32/config-$key.o bin32/pci.o \$(UBE_DEPS32) + \$(LD32) \$(LDFLAGS32) -o \$@ \$(UBE_START32) bin32/config-$key.o bin32/$key.o +bin32/pci.o \$(LIBS32) @\$(SIZE32) \$@ | \$(CHECKSIZE) bin32/$key.img: bin32/$key.o bin32/$key.tmp bin32/config-$key.o bin32/pci.o \$(STDDEPS32) diff -uNr etherboot-5.0.2/src/start32.S etherboot-5.0.2.eb1/src/start32.S --- etherboot-5.0.2/src/start32.S Thu Mar 8 04:51:42 2001 +++ etherboot-5.0.2.eb1/src/start32.S Sat Jun 30 17:23:06 2001 @@ -112,6 +112,12 @@ lret .code32 +#if defined(CONFIG_TSC_CURRTICKS) +#undef CONFIG_BIOS_CURRTICKS +#else +#define CONFIG_BIOS_CURRTICKS 1 +#endif +#if defined(CONFIG_BIOS_CURRTICKS) /************************************************************************** CURRTICKS - Get Time Use direct memory access to BIOS variables, longword 0040:006C (ticks @@ -141,7 +147,7 @@ popl %ebx popl %ebp ret - +#endif /* CONFIG_BIOS_CURRTICKS */ /************************************************************************** CONSOLE_PUTC - Print a character on console **************************************************************************/ diff -uNr etherboot-5.0.2/src/timer.c etherboot-5.0.2.eb1/src/timer.c --- etherboot-5.0.2/src/timer.c Sun Dec 10 00:40:48 2000 +++ etherboot-5.0.2.eb1/src/timer.c Sat Jun 30 17:01:54 2001 @@ -18,3 +18,110 @@ outb(ticks & 0xFF, TIMER2_PORT); outb(ticks >> 8, TIMER2_PORT); } + +#if defined(CONFIG_TSC_CURRTICKS) +#define rdtsc(low,high) \ + __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high)) + +#define rdtscll(val) \ + __asm__ __volatile__ ("rdtsc" : "=A" (val)) + + +#define HZ TICKS_PER_SEC +#define CLOCK_TICK_RATE 1193180U /* Underlying HZ */ +/* LATCH is used in the interval timer and ftape setup. */ +#define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */ + + +/* ------ Calibrate the TSC ------- + * Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset(). + * Too much 64-bit arithmetic here to do this cleanly in C, and for + * accuracy's sake we want to keep the overhead on the CTC speaker (channel 2) + * output busy loop as low as possible. We avoid reading the CTC registers + * directly because of the awkward 8-bit access mechanism of the 82C54 + * device. + */ + +#define CALIBRATE_LATCH (5 * LATCH) + +static unsigned long long calibrate_tsc(void) +{ + /* Set the Gate high, disable speaker */ + outb((inb(0x61) & ~0x02) | 0x01, 0x61); + + /* + * Now let's take care of CTC channel 2 + * + * Set the Gate high, program CTC channel 2 for mode 0, + * (interrupt on terminal count mode), binary count, + * load 5 * LATCH count, (LSB and MSB) to begin countdown. + */ + outb(0xb0, 0x43); /* binary, mode 0, LSB/MSB, Ch 2 */ + outb(CALIBRATE_LATCH & 0xff, 0x42); /* LSB of count */ + outb(CALIBRATE_LATCH >> 8, 0x42); /* MSB of count */ + + { + unsigned long startlow, starthigh; + unsigned long endlow, endhigh; + unsigned long count; + + rdtsc(startlow,starthigh); + count = 0; + do { + count++; + } while ((inb(0x61) & 0x20) == 0); + rdtsc(endlow,endhigh); + + /* Error: ECTCNEVERSET */ + if (count <= 1) + goto bad_ctc; + + /* 64-bit subtract - gcc just messes up with long longs */ + __asm__("subl %2,%0\n\t" + "sbbl %3,%1" + :"=a" (endlow), "=d" (endhigh) + :"g" (startlow), "g" (starthigh), + "0" (endlow), "1" (endhigh)); + + /* Error: ECPUTOOFAST */ + if (endhigh) + goto bad_ctc; + + endlow /= 5; + return endlow; + } + + /* + * The CTC wasn't reliable: we got a hit on the very first read, + * or the CPU was so fast/slow that the quotient wouldn't fit in + * 32 bits.. + */ +bad_ctc: + printf("bad_ctc\n"); + return 0; +} + + +unsigned long currticks(void) +{ + static unsigned long clocks_per_tick; + unsigned long clocks_high, clocks_low; + unsigned long currticks; + if (!clocks_per_tick) { + clocks_per_tick = calibrate_tsc(); + printf("clocks_per_tick = %d\n", clocks_per_tick); + } + + /* Read the Time Stamp Counter */ + rdtsc(clocks_low, clocks_high); + + /* currticks = clocks / clocks_per_tick; */ + __asm__("divl %1" + :"=a" (currticks) + :"r" (clocks_per_tick), "0" (clocks_low), "d" (clocks_high)); + + + return currticks; +} + +#endif /* RTC_CURRTICKS */ diff -uNr etherboot-5.0.2/src/ube.c etherboot-5.0.2.eb1/src/ube.c --- etherboot-5.0.2/src/ube.c Wed Dec 31 17:00:00 1969 +++ etherboot-5.0.2.eb1/src/ube.c Sat Jun 30 17:01:54 2001 @@ -0,0 +1,159 @@ +#include "etherboot.h" +#include "uniform_boot.h" + +static struct bootinfo { + unsigned base_mem_k; + unsigned high_mem_k; +} bootinfo; + + + +static unsigned long uniform_boot_compute_header_checksum( + struct uniform_boot_header *header) +{ + unsigned short *ptr; + unsigned long sum; + unsigned long len; + /* compute an ip style checksum on the header */ + sum = 0; + len = header->header_bytes >> 1; + ptr = (void *)header; + while (len--) { + sum += *(ptr++); + if (sum > 0xFFFF) + sum -= 0xFFFF; + } + return (~sum) & 0xFFFF; +} + +static void set_base_mem_k(struct bootinfo *info, unsigned mem_k) +{ + if ((mem_k <= 640) && (info->base_mem_k <= mem_k)) { + info->base_mem_k = mem_k; + } +} +static void set_high_mem_k(struct bootinfo *info, unsigned mem_k) +{ + /* Shave off a megabyte before playing */ + if (mem_k < 1024) { + return; + } + mem_k -= 1024; + if (info->high_mem_k <= mem_k) { + info->high_mem_k = mem_k; + } +} +static void read_uniform_boot_memory( + struct bootinfo *info, struct ube_memory *mem) +{ + int i; + int entries; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + for(i = 0; (i < entries); i++) { + switch(mem->map[i].type) { + case UBE_MEM_RAM: + { + unsigned long long high; + unsigned long mem_k; + high = mem->map[i].start + mem->map[i].size; +#if defined(DEBUG_UBE) + printf("ube: ram start: %X%X size: %X%X high: %X%X\n", + (unsigned long)(mem->map[i].start >>32), + (unsigned long)(mem->map[i].start & 0xFFFFFFFF), + (unsigned long)(mem->map[i].size >> 32), + (unsigned long)(mem->map[i].size & 0xFFFFFFFF), + (unsigned long)(high >> 32), + (unsigned long)(high & 0xFFFFFFFF)); +#endif /* DEBUG_UBE */ + high >>= 10; + mem_k = high; + if (high & 0xFFFFFFFF00000000ULL) { + mem_k = 0xFFFFFFFF; + } + set_base_mem_k(info, mem_k); + set_high_mem_k(info, mem_k); + break; + } + case UBE_MEM_ACPI: + break; + case UBE_MEM_NVS: + break; + case UBE_MEM_RESERVED: + default: + break; + } + } +} + + +static void read_uniform_boot_data(struct bootinfo *info, + struct uniform_boot_header *header) +{ + /* Uniform boot environment */ + unsigned long env_bytes; + char *env; + unsigned long checksum; + checksum = uniform_boot_compute_header_checksum(header); + if (checksum != 0) { + printf("Bad uniform boot header checksum!\n"); + } + if (header->arg_bytes) { + /* Ignore the command line I am passed */ + } + env = (void *)(header->env); + env_bytes = header->env_bytes; + while(env_bytes) { + struct ube_record *record; + unsigned long mem_k; + record = (void *)env; + if (record->tag == UBE_TAG_MEMORY) { + read_uniform_boot_memory(info, (void *)record); + } + env += record->size; + env_bytes -= record->size; + } +} + +static void initialize_bootinfo(void) +{ + extern unsigned long initeax; + extern unsigned long initebx; + static int initialized = 0; + unsigned long type; + void *ptr; + if (initialized) { + return; + } + initialized = 1; +#if defined(DEBUG_UBE) + printf("\nReading bootinfo initeax = %X initebx = %X\n", + initeax, initebx); +#endif /* DEBUG_UBE */ + type = initeax; + ptr = (void *)initebx; + bootinfo.base_mem_k = 0; + bootinfo.high_mem_k = 0; + if (type == 0x0A11B007) { + read_uniform_boot_data(&bootinfo, ptr); + } + if (bootinfo.base_mem_k == 0) { + printf("No base memory found assuming 640K\n"); + bootinfo.base_mem_k = 640; + } +#if defined(DEBUG_UBE) + printf("base_mem_k = %d high_mem_k = %d\n", + bootinfo.base_mem_k, bootinfo.high_mem_k); +#endif /* DEBUG_UBE */ +} +unsigned short basememsize(void) +{ + initialize_bootinfo(); + return bootinfo.base_mem_k; +} + +unsigned int memsize(void) +{ + initialize_bootinfo(); + return bootinfo.high_mem_k; +} + diff -uNr etherboot-5.0.2/src/ube_start32.S etherboot-5.0.2.eb1/src/ube_start32.S --- etherboot-5.0.2/src/ube_start32.S Wed Dec 31 17:00:00 1969 +++ etherboot-5.0.2.eb1/src/ube_start32.S Sat Jun 30 17:01:54 2001 @@ -0,0 +1,165 @@ +/* #defines because ljmp wants a number, probably gas bug */ +/* .equ KERN_CODE_SEG,_pmcs-_gdt */ +#define KERN_CODE_SEG 0x08 + .equ KERN_DATA_SEG,_pmds-_gdt +/* .equ REAL_CODE_SEG,_rmcs-_gdt */ +#define REAL_CODE_SEG 0x18 + .equ REAL_DATA_SEG,_rmds-_gdt + .equ CR0_PE,1 + +#ifdef GAS291 +#define DATA32 data32; +#define ADDR32 addr32; +#define LJMPI(x) ljmp x +#else +#define DATA32 data32 +#define ADDR32 addr32 +/* newer GAS295 require #define LJMPI(x) ljmp *x */ +#define LJMPI(x) ljmp x +#endif + +/* + * NOTE: if you write a subroutine that is called from C code (gcc/egcs), + * then you only have to take care of %ebx, %esi, %edi and %ebp. These + * registers must not be altered under any circumstance. All other registers + * may be clobbered without any negative side effects. If you don't follow + * this rule then you'll run into strange effects that only occur on some + * gcc versions (because the register allocator may use different registers). + * + * All the data32 prefixes for the ljmp instructions are necessary, because + * the assembler emits code with a relocation address of 0. This means that + * all destinations are initially negative, which the assembler doesn't grok, + * because for some reason negative numbers don't fit into 16 bits. The addr32 + * prefixes are there for the same reasons, because otherwise the memory + * references are only 16 bit wide. Theoretically they are all superfluous. + * One last note about prefixes: the data32 prefixes on all call _real_to_prot + * instructions could be removed if the _real_to_prot function is changed to + * deal correctly with 16 bit return addresses. I tried it, but failed. + */ + + + +/************************************************************************** +START - Where all the fun begins.... +**************************************************************************/ +/* this must be the first thing in the file because we enter from the top */ + .global _start + .code32 +#ifdef IMAGE_MULTIBOOT +/************************************************************************** +XEND - Restart Etherboot from the beginning (from protected mode) +**************************************************************************/ + .globl xend +xend: +#endif +_start: + cli + + cs;lgdt gdtarg + ljmp $KERN_CODE_SEG, $1f +1: + /* reload other segment registers */ + movl $KERN_DATA_SEG, %ebp + movl %ebp,%ds + movl %ebp,%es + movl %ebp,%ss + movl %ebp,%fs + movl %ebp,%gs + + movl %esp, initesp + movl %eax, initeax + movl %ebx, initebx + + movl $_estack, %esp + + call main + /* fall through */ + + .globl exit +exit: + movl initesp, %esp + ret + + +/************************************************************************** +SETJMP - Save stack context for non-local goto +**************************************************************************/ + .globl setjmp +setjmp: + movl 4(%esp),%ecx + movl 0(%esp),%edx + movl %edx,0(%ecx) + movl %ebx,4(%ecx) + movl %esp,8(%ecx) + movl %ebp,12(%ecx) + movl %esi,16(%ecx) + movl %edi,20(%ecx) + movl %eax,24(%ecx) + movl $0,%eax + ret + +/************************************************************************** +LONGJMP - Non-local jump to a saved stack context +**************************************************************************/ + .globl longjmp +longjmp: + movl 4(%esp),%edx + movl 8(%esp),%eax + movl 0(%edx),%ecx + movl 4(%edx),%ebx + movl 8(%edx),%esp + movl 12(%edx),%ebp + movl 16(%edx),%esi + movl 20(%edx),%edi + cmpl $0,%eax + jne 1f + movl $1,%eax +1: movl %ecx,0(%esp) + ret + +/************************************************************************** +GLOBAL DESCRIPTOR TABLE +**************************************************************************/ + .align 4 +_gdt: +gdtarg: + .word 0x27 /* limit */ + .long _gdt /* addr */ + .word 0 + +_pmcs: + /* 32 bit protected mode code segment */ + .word 0xffff,0 + .byte 0,0x9f,0xcf,0 + +_pmds: + /* 32 bit protected mode data segment */ + .word 0xffff,0 + .byte 0,0x93,0xcf,0 + +_rmcs: + /* 16 bit real mode code segment */ + .word 0xffff,(RELOC&0xffff) + .byte (RELOC>>16),0x9b,0x00,(RELOC>>24) + +_rmds: + /* 16 bit real mode data segment */ + .word 0xffff,(RELOC&0xffff) + .byte (RELOC>>16),0x93,0x00,(RELOC>>24) + +initesp: .long 0 + .globl initeax +initeax: .long 0 + .globl initebx +initebx: .long 0 + + + .align 4 + + .section ".bss" + .p2align 3 + /* allocate a 4K stack in the bss segment */ +_stack: + .space 4096 +_estack: + diff -uNr etherboot-5.0.2/src/uniform_boot.h etherboot-5.0.2.eb1/src/uniform_boot.h --- etherboot-5.0.2/src/uniform_boot.h Wed Dec 31 17:00:00 1969 +++ etherboot-5.0.2.eb1/src/uniform_boot.h Sat Jun 30 17:01:54 2001 @@ -0,0 +1,67 @@ +#ifndef __UNIFORM_BOOT_H +#define __UNIFORM_BOOT_H + +/* The uniform boot environment information is restricted to + * hardware information. In particular for a simple enough machine + * all of the environment information should be able to reside in + * a rom and not need to be moved. This information is the + * information a trivial boot room can pass to linux to let it + * run the hardware. + * + * Also 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. The exception is the + * uniform_boot_header with it's two pointers arg & env. + * + * The addresses in the arg & env pointers must be physical + * addresses. A physical address is an address you put in the page + * table. + * + * The Command line is for user policy. Things like the default + * root device. + * + */ + +struct uniform_boot_header +{ + unsigned long header_bytes; + unsigned long header_checksum; + unsigned long arg; + unsigned long arg_bytes; + unsigned long env; + unsigned long env_bytes; +}; + +/* 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 ube_record { + unsigned long tag; /* tag ID */ + unsigned long size; /* size of record (in bytes) */ + unsigned long data[0]; /* data */ +}; + + +#define UBE_TAG_MEMORY 0x0001 + +struct ube_memory_range { + unsigned long long start; + unsigned long long size; + unsigned long type; +#define UBE_MEM_RAM 1 +#define UBE_MEM_RESERVED 2 +#define UBE_MEM_ACPI 3 +#define UBE_MEM_NVS 4 + +}; + +struct ube_memory { + unsigned long tag; + unsigned long size; + struct ube_memory_range map[0]; +}; + +#endif /* _UNIFORM_BOOT_H */
