From eaefd1f00665f71de978fe1257ffd751366e06c9 Mon Sep 17 00:00:00 2001 From: Steven James Date: Tue, 12 Nov 2002 17:20:38 +0000 Subject: [PATCH] Initial commit --- util/baremetal/hello/Makefile | 10 + util/baremetal/hello/elfImage.lds | 71 ++++ util/baremetal/hello/hello.c | 24 ++ util/baremetal/lib/Makefile | 36 ++ util/baremetal/lib/boot.c | 191 +++++++++ util/baremetal/lib/elfboot.c | 665 ++++++++++++++++++++++++++++++ util/baremetal/lib/linuxbios.c | 305 ++++++++++++++ util/baremetal/lib/serial_subr.c | 215 ++++++++++ util/baremetal/lib/subr.c | 99 +++++ 9 files changed, 1616 insertions(+) create mode 100644 util/baremetal/hello/Makefile create mode 100644 util/baremetal/hello/elfImage.lds create mode 100644 util/baremetal/hello/hello.c create mode 100644 util/baremetal/lib/Makefile create mode 100644 util/baremetal/lib/boot.c create mode 100644 util/baremetal/lib/elfboot.c create mode 100644 util/baremetal/lib/linuxbios.c create mode 100644 util/baremetal/lib/serial_subr.c create mode 100644 util/baremetal/lib/subr.c diff --git a/util/baremetal/hello/Makefile b/util/baremetal/hello/Makefile new file mode 100644 index 0000000000..d47505ce27 --- /dev/null +++ b/util/baremetal/hello/Makefile @@ -0,0 +1,10 @@ +CC = gcc +CFLAGS = -I ../include -O2 + +DEPS = hello.o ../lib/baremetal.a + +hello.elf: $(DEPS) + ld -defsym HEAPSIZE=0x100 -T elfImage.lds $(DEPS) -o hello.elf + +clean: + rm hello.elf *.o diff --git a/util/baremetal/hello/elfImage.lds b/util/baremetal/hello/elfImage.lds new file mode 100644 index 0000000000..ed4cbc3792 --- /dev/null +++ b/util/baremetal/hello/elfImage.lds @@ -0,0 +1,71 @@ +/* This setup assumes you have at least a 16M machine + * The problem is that with 2GB of RAM you use nearly 23M + * of memory in the kernel page tables, and since 2.2 doesn't check + * where the initrd is placed before allocating memory this is a + * problem. With a 8M Ramdisk + 23M kernel that is 31M leaving + * little room for things to grow. + * With the limit at 112M (i.e. 0x00700000) we should be o.k.) + * + * If you need to change the amount of assumed memory. + * The uppper.LENGTH needs to change so that + * upper.LENGTH + upper.ORIGIN = MEMORY_SIZE + * and the computation just before ramdisk placing also should + * be corrected, to be: + * . = MEMORY_SIZE - ((ramdisk_data_end - ramdisk_data) + 4095) + * .ramdisk(ALIGN(4096)) + */ + +/* INCLUDE mkelfImage.lds */ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) + +HEAPSIZE = DEFINED(HEAPSIZE) ? HEAPSIZE : 0x8000; + +ENTRY(main) +SECTIONS +{ +/* . = 0x10000 - (elf_note - elf_header); */ + . = 0x10000; + _text = .; /* Text and read-only data */ + .text . : { + *(.text) + *(.fixup) + *(.gnu.warning) + } = 0x9090 + .rodata (.): { *(.rodata) } + .kstrtab (.): { *(.kstrtab) } + + + . = ALIGN(16); /* Exception table */ + _etext = .; /* End of text section */ + + .data (.): { /* Data */ + *(.data) + CONSTRUCTORS + } + + _edata = .; /* End of data section */ + __bss_start = .; /* BSS */ + .bss (.): { + *(.bss) + } + _ebss = .; + _heap = .; + .heap (.): { + . = HEAPSIZE; + } + _eheap = .; + _end = . ; + + _ram_seg = _text; + _eram_seg = _end; + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } +} diff --git a/util/baremetal/hello/hello.c b/util/baremetal/hello/hello.c new file mode 100644 index 0000000000..4fc2de5644 --- /dev/null +++ b/util/baremetal/hello/hello.c @@ -0,0 +1,24 @@ +#include + + +char buffer[512]; + +int main(void) +{ + int i; + + printk("Hello World!\n"); + + ide_init(); + + ide_read_sector(0, buffer, 0, 0, 512); + + printk("%s", buffer+4); + + printk("\nBYE!\n"); + + + + + return(0); +} diff --git a/util/baremetal/lib/Makefile b/util/baremetal/lib/Makefile new file mode 100644 index 0000000000..0fe88872fe --- /dev/null +++ b/util/baremetal/lib/Makefile @@ -0,0 +1,36 @@ + +ARCH = i386 +CC = gcc + +CFLAGS = -I ../include -I ../include/$(ARCH) -O2 + +LB_SOURCE=../../../src + +DEPS = printk.o serial_subr.o subr.o vsprintf.o memcpy.o malloc.o memset.o compute_ip_checksum.o elfboot.o boot.o ide.o linuxbios.o + +baremetal.a : $(DEPS) + ar -cr baremetal.a $(DEPS) + +compute_ip_checksum.o: $(LB_SOURCE)/lib/compute_ip_checksum.c + $(CC) $(CFLAGS) -c $< + +memcpy.o: $(LB_SOURCE)/lib/memcpy.c + $(CC) $(CFLAGS) -c $< + +vsprintf.o: $(LB_SOURCE)/lib/vsprintf.c + $(CC) $(CFLAGS) -c $< + +ide.o: $(LB_SOURCE)/pc80/ide/ide.c + $(CC) $(CFLAGS) -c $< + +memset.o: $(LB_SOURCE)/lib/memset.c + $(CC) $(CFLAGS) -c $< + +malloc.o: $(LB_SOURCE)/lib/malloc.c + $(CC) $(CFLAGS) -c $< + +printk.o: $(LB_SOURCE)/lib/printk.c + $(CC) $(CFLAGS) -c $< + +clean: + rm -f $(DEPS) baremetal.a diff --git a/util/baremetal/lib/boot.c b/util/baremetal/lib/boot.c new file mode 100644 index 0000000000..747ff3e1e6 --- /dev/null +++ b/util/baremetal/lib/boot.c @@ -0,0 +1,191 @@ +#include +#include +#include +#include +#include + + +#ifndef CMD_LINE +#define CMD_LINE "" +#endif + + + +#define UPSZ(X) ((sizeof(X) + 3) &~3) + +static struct { + Elf_Bhdr hdr; + Elf_Nhdr ft_hdr; + unsigned char ft_desc[UPSZ(FIRMWARE_TYPE)]; + Elf_Nhdr bl_hdr; + unsigned char bl_desc[UPSZ(BOOTLOADER)]; + Elf_Nhdr blv_hdr; + unsigned char blv_desc[UPSZ(BOOTLOADER_VERSION)]; + Elf_Nhdr cmd_hdr; + unsigned char cmd_desc[UPSZ(CMD_LINE)]; +} elf_boot_notes = { + .hdr = { + .b_signature = 0x0E1FB007, + .b_size = sizeof(elf_boot_notes), + .b_checksum = 0, + .b_records = 4, + }, + .ft_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(FIRMWARE_TYPE), + .n_type = EBN_FIRMWARE_TYPE, + }, + .ft_desc = FIRMWARE_TYPE, + .bl_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(BOOTLOADER), + .n_type = EBN_BOOTLOADER_NAME, + }, + .bl_desc = BOOTLOADER, + .blv_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(BOOTLOADER_VERSION), + .n_type = EBN_BOOTLOADER_VERSION, + }, + .blv_desc = BOOTLOADER_VERSION, + .cmd_hdr = { + .n_namesz = 0, + .n_descsz = sizeof(CMD_LINE), + .n_type = EBN_COMMAND_LINE, + }, + .cmd_desc = CMD_LINE, +}; + + +int elf_check_arch(Elf_ehdr *ehdr) +{ + return ( + ((ehdr->e_machine == EM_386) || (ehdr->e_machine == EM_486)) && + (ehdr->e_ident[EI_CLASS] == ELFCLASS32) && + (ehdr->e_ident[EI_DATA] == ELFDATA2LSB) + ); + +} + +#if 0 +void jmp_to_elf_entry(void *entry, unsigned long buffer) +{ + extern unsigned char _ram_seg, _eram_seg; + unsigned long lb_start, lb_size; + unsigned long adjust, adjusted_boot_notes; + unsigned long type; + + elf_boot_notes.hdr.b_checksum = + compute_ip_checksum(&elf_boot_notes, sizeof(elf_boot_notes)); + + type = 0x0E1FB007; + lb_start = (unsigned long)&_ram_seg; + lb_size = (unsigned long)(&_eram_seg - &_ram_seg); + adjust = buffer + lb_size - lb_start; + + adjusted_boot_notes = (unsigned long)&elf_boot_notes; + adjusted_boot_notes += adjust; + + printk_spew("entry = 0x%08lx\n", (unsigned long)entry); + printk_spew("lb_start = 0x%08lx\n", lb_start); + printk_spew("lb_size = 0x%08lx\n", lb_size); + printk_spew("adjust = 0x%08lx\n", adjust); + printk_spew("buffer = 0x%08lx\n", buffer); + printk_spew(" elf_boot_notes = 0x%08lx\n", (unsigned long)&elf_boot_notes); + printk_spew("adjusted_boot_notes = 0x%08lx\n", adjusted_boot_notes); + + /* Jump to kernel */ + __asm__ __volatile__( + " cld \n\t" + /* Save the callee save registers... */ + " pushl %%esi\n\t" + " pushl %%edi\n\t" + " pushl %%ebx\n\t" + /* Save the parameters I was passed */ + " pushl $0\n\t" /* 20 adjust */ + " pushl %0\n\t" /* 16 lb_start */ + " pushl %1\n\t" /* 12 buffer */ + " pushl %2\n\t" /* 8 lb_size */ + " pushl %3\n\t" /* 4 entry */ + " pushl %4\n\t" /* 0 elf_boot_notes */ + /* Compute the adjustment */ + " xorl %%eax, %%eax\n\t" + " subl 16(%%esp), %%eax\n\t" + " addl 12(%%esp), %%eax\n\t" + " addl 8(%%esp), %%eax\n\t" + " movl %%eax, 20(%%esp)\n\t" + /* Place a copy of linuxBIOS in it's new location */ + /* Move ``longs'' the linuxBIOS size is 4 byte aligned */ + " movl 12(%%esp), %%edi\n\t" + " addl 8(%%esp), %%edi\n\t" + " movl 16(%%esp), %%esi\n\t" + " movl 8(%%esp), %%ecx\n\n" + " shrl $2, %%ecx\n\t" + " rep movsl\n\t" + + /* Adjust the stack pointer to point into the new linuxBIOS image */ + " addl 20(%%esp), %%esp\n\t" + /* Adjust the instruction pointer to point into the new linuxBIOS image */ + " movl $1f, %%eax\n\t" + " addl 20(%%esp), %%eax\n\t" + " jmp *%%eax\n\t" + "1: \n\t" + + /* Copy the linuxBIOS bounce buffer over linuxBIOS */ + /* Move ``longs'' the linuxBIOS size is 4 byte aligned */ + " movl 16(%%esp), %%edi\n\t" + " movl 12(%%esp), %%esi\n\t" + " movl 8(%%esp), %%ecx\n\t" + " shrl $2, %%ecx\n\t" + " rep movsl\n\t" + + /* Now jump to the loaded image */ + " movl $0x0E1FB007, %%eax\n\t" + " movl 0(%%esp), %%ebx\n\t" + " call *4(%%esp)\n\t" + + /* The loaded image returned? */ + " cli \n\t" + " cld \n\t" + + /* Copy the saved copy of linuxBIOS where linuxBIOS runs */ + /* Move ``longs'' the linuxBIOS size is 4 byte aligned */ + " movl 16(%%esp), %%edi\n\t" + " movl 12(%%esp), %%esi\n\t" + " addl 8(%%esp), %%esi\n\t" + " movl 8(%%esp), %%ecx\n\t" + " shrl $2, %%ecx\n\t" + " rep movsl\n\t" + + /* Adjust the stack pointer to point into the old linuxBIOS image */ + " subl 20(%%esp), %%esp\n\t" + + /* Adjust the instruction pointer to point into the old linuxBIOS image */ + " movl $1f, %%eax\n\t" + " subl 20(%%esp), %%eax\n\t" + " jmp *%%eax\n\t" + "1: \n\t" + + /* Drop the parameters I was passed */ + " addl $24, %%esp\n\t" + + /* Restore the callee save registers */ + " popl %%ebx\n\t" + " popl %%edi\n\t" + " popl %%esi\n\t" + + :: + "g" (lb_start), "g" (buffer), "g" (lb_size), + "g" (entry), "g"(adjusted_boot_notes) + ); +} + +#else +void jmp_to_elf_entry(void *entry, unsigned long buffer) +{ + void (*newmain)() = entry; + + newmain(); + return; +} +#endif diff --git a/util/baremetal/lib/elfboot.c b/util/baremetal/lib/elfboot.c new file mode 100644 index 0000000000..93ace53f67 --- /dev/null +++ b/util/baremetal/lib/elfboot.c @@ -0,0 +1,665 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Maximum physical address we can use for the linuxBIOS bounce buffer. + */ +#ifndef MAX_ADDR +#define MAX_ADDR -1UL +#endif + +extern unsigned char _ram_seg; +extern unsigned char _eram_seg; + +struct segment { + struct segment *next; + struct segment *prev; + struct segment *phdr_next; + struct segment *phdr_prev; + unsigned long s_addr; + unsigned long s_memsz; + unsigned long s_offset; + unsigned long s_filesz; +}; + +struct verify_callback { + struct verify_callback *next; + int (*callback)(struct verify_callback *vcb, + Elf_ehdr *ehdr, Elf_phdr *phdr, struct segment *head); + unsigned long desc_offset; + unsigned long desc_addr; +}; + +struct ip_checksum_vcb { + struct verify_callback data; + unsigned short ip_checksum; +}; + +int verify_ip_checksum( + struct verify_callback *vcb, + Elf_ehdr *ehdr, Elf_phdr *phdr, struct segment *head) +{ + struct ip_checksum_vcb *cb; + struct segment *ptr; + unsigned long bytes; + unsigned long checksum; + unsigned char buff[2], *n_desc; + cb = (struct ip_checksum_vcb *)vcb; + /* zero the checksum so it's value won't + * get in the way of verifying the checksum. + */ + n_desc = 0; + if (vcb->desc_addr) { + n_desc = (unsigned char *)(vcb->desc_addr); + memcpy(buff, n_desc, 2); + memset(n_desc, 0, 2); + } + bytes = 0; + checksum = compute_ip_checksum(ehdr, sizeof(*ehdr)); + bytes += sizeof(*ehdr); + checksum = add_ip_checksums(bytes, checksum, + compute_ip_checksum(phdr, ehdr->e_phnum*sizeof(*phdr))); + bytes += ehdr->e_phnum*sizeof(*phdr); + for(ptr = head->phdr_next; ptr != head; ptr = ptr->phdr_next) { + checksum = add_ip_checksums(bytes, checksum, + compute_ip_checksum((void *)ptr->s_addr, ptr->s_memsz)); + bytes += ptr->s_memsz; + } + if (n_desc != 0) { + memcpy(n_desc, buff, 2); + } + if (checksum != cb->ip_checksum) { + printk_err("Image checksum: %04x != computed checksum: %04x\n", + cb->ip_checksum, checksum); + } + return checksum == cb->ip_checksum; +} + +/* The problem: + * Static executables all want to share the same addresses + * in memory because only a few addresses are reliably present on + * a machine, and implementing general relocation is hard. + * + * The solution: + * - Allocate a buffer twice the size of the linuxBIOS image. + * - Anything that would overwrite linuxBIOS copy into the lower half of + * the buffer. + * - After loading an ELF image copy linuxBIOS to the upper half of the + * buffer. + * - Then jump to the loaded image. + * + * Benefits: + * - Nearly arbitrary standalone executables can be loaded. + * - LinuxBIOS is preserved, so it can be returned to. + * - The implementation is still relatively simple, + * and much simpler then the general case implemented in kexec. + * + */ + +static unsigned long get_bounce_buffer(struct lb_memory *mem) +{ + unsigned long lb_size; + unsigned long mem_entries; + unsigned long buffer; + int i; + lb_size = (unsigned long)(&_eram_seg - &_ram_seg); + /* Double linuxBIOS size so I have somewhere to place a copy to return to */ + lb_size = lb_size + lb_size; + mem_entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + buffer = 0; + for(i = 0; i < mem_entries; i++) { + unsigned long mstart, mend; + unsigned long msize; + unsigned long tbuffer; + if (mem->map[i].type != LB_MEM_RAM) + continue; + if (mem->map[i].start > MAX_ADDR) + continue; + if (mem->map[i].size < lb_size) + continue; + mstart = mem->map[i].start; + msize = MAX_ADDR - mstart +1; + if (msize > mem->map[i].size) + msize = mem->map[i].size; + mend = mstart + msize; + tbuffer = mend - lb_size; + if (tbuffer < buffer) + continue; + buffer = tbuffer; + } + return buffer; +} + + +static struct verify_callback *process_elf_notes( + unsigned char *header, + unsigned long offset, unsigned long length) +{ + struct verify_callback *cb_chain; + unsigned char *note, *end; + char *program, *version; + + cb_chain = 0; + note = header + offset; + end = note + length; + program = version = 0; + while(note < end) { + Elf_Nhdr *hdr; + unsigned char *n_name, *n_desc, *next; + hdr = (Elf_Nhdr *)note; + n_name = note + sizeof(*hdr); + n_desc = n_name + ((hdr->n_namesz + 3) & ~3); + next = n_desc + ((hdr->n_descsz + 3) & ~3); + if (next > end) { + break; + } + if ((hdr->n_namesz == sizeof(ELF_NOTE_BOOT)) && + (memcmp(n_name, ELF_NOTE_BOOT, sizeof(ELF_NOTE_BOOT)) == 0)) { + switch(hdr->n_type) { + case EIN_PROGRAM_NAME: + if (n_desc[hdr->n_descsz -1] == 0) { + program = n_desc; + } + break; + case EIN_PROGRAM_VERSION: + if (n_desc[hdr->n_descsz -1] == 0) { + version = n_desc; + } + break; + case EIN_PROGRAM_CHECKSUM: + { + struct ip_checksum_vcb *cb; + cb = malloc(sizeof(*cb)); + cb->ip_checksum = *((uint16_t *)n_desc); + cb->data.callback = verify_ip_checksum; + cb->data.next = cb_chain; + cb->data.desc_offset = n_desc - header; + cb_chain = &cb->data; + break; + } + } + } + printk_spew("n_type: %08x n_name(%d): %-*.*s n_desc(%d): %-*.*s\n", + hdr->n_type, + hdr->n_namesz, hdr->n_namesz, hdr->n_namesz, n_name, + hdr->n_descsz,hdr->n_descsz, hdr->n_descsz, n_desc); + note = next; + } + if (program && version) { + printk_info("Loading %s version: %s\n", + program, version); + } + return cb_chain; +} + +static int valid_area(struct lb_memory *mem, unsigned long buffer, + unsigned long start, unsigned long len) +{ + /* Check through all of the memory segments and ensure + * the segment that was passed in is completely contained + * in RAM. + */ + int i; + unsigned long end = start + len; + unsigned long mem_entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + + /* See if I conflict with the bounce buffer */ + if (end >= buffer) { + return 0; + } + + /* Walk through the table of valid memory ranges and see if I + * have a match. + */ + for(i = 0; i < mem_entries; i++) { + uint64_t mstart, mend; + uint32_t mtype; + mtype = mem->map[i].type; + mstart = mem->map[i].start; + mend = mstart + mem->map[i].size; + if ((mtype == LB_MEM_RAM) && (start < mend) && (end > mstart)) { + break; + } + } + if (i == mem_entries) { + printk_err("No matching ram area found for range:\n"); + printk_err(" [0x%016lx, 0x%016lx)\n", start, end); + printk_err("Ram areas\n"); + for(i = 0; i < mem_entries; i++) { + uint64_t mstart, mend; + uint32_t mtype; + mtype = mem->map[i].type; + mstart = mem->map[i].start; + mend = mstart + mem->map[i].size; + printk_err(" [0x%016lx, 0x%016lx) %s\n", + (unsigned long)mstart, + (unsigned long)mend, + (mtype == LB_MEM_RAM)?"RAM":"Reserved"); + + } + return 0; + } + return 1; +} + +static void relocate_segment(unsigned long buffer, struct segment *seg) +{ + /* Modify all segments that want to load onto linuxBIOS + * to load onto the bounce buffer instead. + */ + unsigned long lb_start = (unsigned long)&_ram_seg; + unsigned long lb_end = (unsigned long)&_eram_seg; + unsigned long start, middle, end; + + printk_spew("lb: [0x%016lx, 0x%016lx)\n", + lb_start, lb_end); + + start = seg->s_addr; + middle = start + seg->s_filesz; + end = start + seg->s_memsz; + /* I don't conflict with linuxBIOS so get out of here */ + if ((end <= lb_start) || (start >= lb_end)) + return; + + printk_spew("segment: [0x%016lx, 0x%016lx, 0x%016lx)\n", + start, middle, end); + + /* Slice off a piece at the beginning + * that doesn't conflict with linuxBIOS. + */ + if (start < lb_start) { + struct segment *new; + unsigned long len = lb_start - start; + new = malloc(sizeof(*new)); + *new = *seg; + new->s_memsz = len; + seg->s_memsz -= len; + seg->s_addr += len; + seg->s_offset += len; + if (seg->s_filesz > len) { + new->s_filesz = len; + seg->s_filesz -= len; + } else { + seg->s_filesz = 0; + } + + /* Order by stream offset */ + new->next = seg; + new->prev = seg->prev; + seg->prev->next = new; + seg->prev = new; + /* Order by original program header order */ + new->phdr_next = seg; + new->phdr_prev = seg->phdr_prev; + seg->phdr_prev->phdr_next = new; + seg->phdr_prev = new; + + /* compute the new value of start */ + start = seg->s_addr; + + printk_spew(" early: [0x%016lx, 0x%016lx, 0x%016lx)\n", + new->s_addr, + new->s_addr + new->s_filesz, + new->s_addr + new->s_memsz); + } + + /* Slice off a piece at the end + * that doesn't conflict with linuxBIOS + */ + if (end > lb_end) { + unsigned long len = lb_end - start; + struct segment *new; + new = malloc(sizeof(*new)); + *new = *seg; + seg->s_memsz = len; + new->s_memsz -= len; + new->s_addr += len; + new->s_offset += len; + if (seg->s_filesz > len) { + seg->s_filesz = len; + new->s_filesz -= len; + } else { + new->s_filesz = 0; + } + /* Order by stream offset */ + new->next = seg->next; + new->prev = seg; + seg->next->prev = new; + seg->next = new; + /* Order by original program header order */ + new->phdr_next = seg->phdr_next; + new->phdr_prev = seg; + seg->phdr_next->phdr_prev = new; + seg->phdr_next = new; + + /* compute the new value of end */ + end = start + len; + + printk_spew(" late: [0x%016lx, 0x%016lx, 0x%016lx)\n", + new->s_addr, + new->s_addr + new->s_filesz, + new->s_addr + new->s_memsz); + + } + /* Now retarget this segment onto the bounce buffer */ + seg->s_addr = buffer + (seg->s_addr - lb_start); + + printk_spew(" bounce: [0x%016lx, 0x%016lx, 0x%016lx)\n", + seg->s_addr, + seg->s_addr + seg->s_filesz, + seg->s_addr + seg->s_memsz); +} + + +static int build_elf_segment_list( + struct segment *head, + unsigned long bounce_buffer, struct lb_memory *mem, + Elf_phdr *phdr, int headers) +{ + struct segment *ptr; + int i; + memset(head, 0, sizeof(*head)); + head->next = head->prev = head; + for(i = 0; i < headers; i++) { + struct segment *new; + /* Ignore data that I don't need to handle */ + if (phdr[i].p_type != PT_LOAD) { + printk_debug("Dropping non PT_LOAD segment\n"); + continue; + } + if (phdr[i].p_memsz == 0) { + printk_debug("Dropping empty segment\n"); + continue; + } + new = malloc(sizeof(*new)); + new->s_addr = phdr[i].p_paddr; + new->s_memsz = phdr[i].p_memsz; + new->s_offset = phdr[i].p_offset; + new->s_filesz = phdr[i].p_filesz; + printk_debug("New segment addr 0x%lx size 0x%lx offset 0x%lx filesize 0x%lx\n", + new->s_addr, new->s_memsz, new->s_offset, new->s_filesz); + /* Clean up the values */ + if (new->s_filesz > new->s_memsz) { + new->s_filesz = new->s_memsz; + } + printk_debug("(cleaned up) New segment addr 0x%lx size 0x%lx offset 0x%lx filesize 0x%lx\n", + new->s_addr, new->s_memsz, new->s_offset, new->s_filesz); + for(ptr = head->next; ptr != head; ptr = ptr->next) { + if (new->s_offset < ptr->s_offset) + break; + } + /* Order by stream offset */ + new->next = ptr; + new->prev = ptr->prev; + ptr->prev->next = new; + ptr->prev = new; + /* Order by original program header order */ + new->phdr_next = head; + new->phdr_prev = head->phdr_prev; + head->phdr_prev->phdr_next = new; + head->phdr_prev = new; + + /* Verify the memory addresses in the segment are valid */ + if (!valid_area(mem, bounce_buffer, new->s_addr, new->s_memsz)) + goto out; + + /* Modify the segment to load onto the bounce_buffer if necessary. + */ + relocate_segment(bounce_buffer, new); + } + return 1; + out: + return 0; +} + +static int load_elf_segments( + struct segment *head, struct stream *stream, + unsigned char *header, unsigned long header_size) +{ + unsigned long offset; + struct segment *ptr; + + offset = 0; + for(ptr = head->next; ptr != head; ptr = ptr->next) { + unsigned long start_offset; + unsigned long skip_bytes, read_bytes; + unsigned char *dest, *middle, *end; + byte_offset_t result; + printk_debug("Loading Segment: addr: 0x%016lx memsz: 0x%016lx filesz: 0x%016lx\n", + ptr->s_addr, ptr->s_memsz, ptr->s_filesz); + + /* Compute the boundaries of the segment */ + dest = (unsigned char *)(ptr->s_addr); + end = dest + ptr->s_memsz; + middle = dest + ptr->s_filesz; + start_offset = ptr->s_offset; + + printk_spew("[ 0x%016lx, %016lx, 0x%016lx) <- %016lx\n", + (unsigned long)dest, + (unsigned long)middle, + (unsigned long)end, + (unsigned long)start_offset); + + /* Skip intial buffer unused bytes */ + if (offset < header_size) { + if (start_offset < header_size) { + offset = start_offset; + } else { + offset = header_size; + } + } + + /* Skip the unused bytes */ + skip_bytes = start_offset - offset; + if (skip_bytes && + ((result = stream->skip(skip_bytes)) != skip_bytes)) { + printk_err("ERROR: Skip of %ld bytes skiped %ld bytes\n", + skip_bytes, result); + goto out; + } + offset = start_offset; + + /* Copy data from the initial buffer */ + if (offset < header_size) { + size_t len; + if ((ptr->s_filesz + start_offset) > header_size) { + len = header_size - start_offset; + } + else { + len = ptr->s_filesz; + } + memcpy(dest, &header[start_offset], len); + dest += len; + } + + /* Read the segment into memory */ + read_bytes = middle - dest; + if (read_bytes && + ((result = stream->read(dest, read_bytes)) != read_bytes)) { + printk_err("ERROR: Read of %ld bytes read %ld bytes...\n", + read_bytes, result); + goto out; + } + offset += ptr->s_filesz; + + /* Zero the extra bytes between middle & end */ + if (middle < end) { + printk_debug("Clearing Segment: addr: 0x%016lx memsz: 0x%016lx\n", + (unsigned long)middle, end - middle); + + /* Zero the extra bytes */ + memset(middle, 0, end - middle); + } + } + return 1; + out: + return 0; +} + +static int verify_loaded_image( + struct verify_callback *vcb, + Elf_ehdr *ehdr, Elf_phdr *phdr, + struct segment *head + ) +{ + struct segment *ptr; + int ok; + ok = 1; + for(; ok && vcb ; vcb = vcb->next) { + /* Find where the note is loaded */ + /* The whole note must be loaded intact + * so an address of 0 for the descriptor is impossible + */ + vcb->desc_addr = 0; + for(ptr = head->next; ptr != head; ptr = ptr->next) { + unsigned long desc_addr; + desc_addr = ptr->s_addr + vcb->desc_offset - ptr->s_offset; + if ((desc_addr >= ptr->s_addr) && + (desc_addr < (ptr->s_addr + ptr->s_filesz))) { + vcb->desc_addr = desc_addr; + } + } + ok = vcb->callback(vcb, ehdr, phdr, head); + } + return ok; +} + +int elfload(struct stream *stream, struct lb_memory *mem, + unsigned char *header, unsigned long header_size) +{ + Elf_ehdr *ehdr; + Elf_phdr *phdr; + void *entry; + struct segment head; + struct verify_callback *cb_chain; + unsigned long bounce_buffer; + + /* Find a bounce buffer so I can load to linuxBIOS's current location */ + bounce_buffer = get_bounce_buffer(mem); + if (!bounce_buffer) { + printk_err("Could not find a bounce buffer...\n"); + goto out; + } + + ehdr = (Elf_ehdr *)header; + entry = (void *)(ehdr->e_entry); + phdr = (Elf_phdr *)(&header[ehdr->e_phoff]); + + /* Digest elf note information... */ + cb_chain = 0; + if ((phdr[0].p_type == PT_NOTE) && + ((phdr[0].p_offset + phdr[0].p_filesz) < header_size)) { + cb_chain = process_elf_notes(header, + phdr[0].p_offset, phdr[0].p_filesz); + } + + /* Preprocess the elf segments */ + if (!build_elf_segment_list(&head, + bounce_buffer, mem, phdr, ehdr->e_phnum)) + goto out; + + /* Load the segments */ + if (!load_elf_segments(&head, stream, header, header_size)) + goto out; + + printk_spew("Loaded segments\n"); + /* Verify the loaded image */ + if (!verify_loaded_image(cb_chain, ehdr, phdr, &head)) + goto out; + + printk_spew("verified segments\n"); + /* Shutdown the stream device */ + stream->fini(); + + printk_spew("closed down stream\n"); + /* Reset to booting from this image as late as possible */ + boot_successful(); + + printk_debug("Jumping to boot code at 0x%x\n", entry); + post_code(0xfe); + + /* Jump to kernel */ + jmp_to_elf_entry(entry, bounce_buffer); + return 1; + + out: + return 0; +} + +int elfboot(struct stream *stream, struct lb_memory *mem) +{ + Elf_ehdr *ehdr; + static unsigned char header[ELF_HEAD_SIZE]; + int header_offset; + int i, result; + + result = 0; + printk_info("\n"); + printk_info("Welcome to %s, the open sourced starter.\n", BOOTLOADER); + printk_info("January 2002, Eric Biederman.\n"); + printk_info("Version %s\n", BOOTLOADER_VERSION); + printk_info("\n"); + post_code(0xf8); + +// if (stream->init() < 0) { +// printk_err("Could not initialize driver...\n"); +// goto out; +// } + + /* Read in the initial ELF_HEAD_SIZE bytes */ + if (stream->read(header, ELF_HEAD_SIZE) != ELF_HEAD_SIZE) { + printk_err("Read failed...\n"); + goto out; + } + /* Scan for an elf header */ + header_offset = -1; + for(i = 0; i < ELF_HEAD_SIZE - (sizeof(Elf_ehdr) + sizeof(Elf_phdr)); i+=16) { + ehdr = (Elf_ehdr *)(&header[i]); + if (memcmp(ehdr->e_ident, ELFMAG, 4) != 0) { + printk_spew("NO header at %d\n", i); + continue; + } + printk_debug("Found ELF candiate at offset %d\n", i); + /* Sanity check the elf header */ + if ((ehdr->e_type == ET_EXEC) && + elf_check_arch(ehdr) && + (ehdr->e_ident[EI_VERSION] == EV_CURRENT) && + (ehdr->e_version == EV_CURRENT) && + (ehdr->e_ehsize == sizeof(Elf_ehdr)) && + (ehdr->e_phentsize = sizeof(Elf_phdr)) && + (ehdr->e_phoff < (ELF_HEAD_SIZE - i)) && + ((ehdr->e_phoff + (ehdr->e_phentsize * ehdr->e_phnum)) <= + (ELF_HEAD_SIZE - i))) { + header_offset = i; + break; + } + ehdr = 0; + } + printk_spew("header_offset is %d\n", header_offset); + if (header_offset == -1) { + goto out; + } + + printk_spew("Try to load at offset 0x%x\n", header_offset); + result = elfload(stream, mem, + header + header_offset , ELF_HEAD_SIZE - header_offset); + out: + if (!result) { + /* Shutdown the stream device */ + stream->fini(); + + printk_err("Cannot Load ELF Image\n"); + + post_code(0xff); + } + return 0; + +} diff --git a/util/baremetal/lib/linuxbios.c b/util/baremetal/lib/linuxbios.c new file mode 100644 index 0000000000..8899dd5fcb --- /dev/null +++ b/util/baremetal/lib/linuxbios.c @@ -0,0 +1,305 @@ + +//#include +#include +#include +#include + +static int lb_failsafe = 1; + +#undef DEBUG_LINUXBIOS + + +#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)) + + +#define for_each_crec(tbl, rec) \ + for(rec = (struct lb_record *)(((char *)tbl) + tbl->header_length); \ + (((char *)rec) < (((char *)tbl) + tbl->size)) && \ + (rec->size >= 1) && \ + ((((char *)rec) + rec->size) <= (((char *)tbl) + tbl->size)); \ + rec = (struct lb_record *)(((char *)rec) + rec->size)) + + + +#if 0 +static void read_lb_memory( + struct meminfo *info, struct lb_memory *mem) +{ + int i; + int entries; + entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]); + for(i = 0; (i < entries); i++) { + if (info->map_count < E820MAX) { + info->map[info->map_count].addr = mem->map[i].start; + info->map[info->map_count].size = mem->map[i].size; + info->map[info->map_count].type = mem->map[i].type; + info->map_count++; + } + switch(mem->map[i].type) { + case LB_MEM_RAM: + { + unsigned long long end; + unsigned long mem_k; + end = mem->map[i].start + mem->map[i].size; +#if defined(DEBUG_LINUXBIOS) + printf("lb: %X%X - %X%X (ram)\n", + (unsigned long)(mem->map[i].start >>32), + (unsigned long)(mem->map[i].start & 0xFFFFFFFF), + (unsigned long)(end >> 32), + (unsigned long)(end & 0xFFFFFFFF)); +#endif /* DEBUG_LINUXBIOS */ + end >>= 10; + mem_k = end; + if (end & 0xFFFFFFFF00000000ULL) { + mem_k = 0xFFFFFFFF; + } + set_base_mem_k(info, mem_k); + set_high_mem_k(info, mem_k); + break; + } + case LB_MEM_RESERVED: + default: +#if defined(DEBUG_LINUXBIOS) + { + unsigned long long end; + end = mem->map[i].start + mem->map[i].size; + printf("lb: %X%X - %X%X (reserved)\n", + (unsigned long)(mem->map[i].start >>32), + (unsigned long)(mem->map[i].start & 0xFFFFFFFF), + (unsigned long)(end >> 32), + (unsigned long)(end & 0xFFFFFFFF)); + } +#endif /* DEBUG_LINUXBIOS */ + break; + } + } +} + +static unsigned cmos_read(unsigned offset, unsigned int size) +{ + unsigned addr, old_addr; + unsigned value; + + addr = offset/8; + + old_addr = inb(0x70); + outb(addr | (old_addr &0x80), 0x70); + value = inb(0x71); + outb(old_addr, 0x70); + + value >>= offset & 0x7; + value &= ((1 << size) - 1); + + return value; +} + +static unsigned cmos_read_checksum(void) +{ + unsigned sum = + (cmos_read(lb_checksum.location, 8) << 8) | + cmos_read(lb_checksum.location +8, 8); + return sum & 0xffff; +} + +static int cmos_valid(void) +{ + unsigned i; + unsigned sum, old_sum; + sum = 0; + if ((lb_checksum.tag != LB_TAG_OPTION_CHECKSUM) || + (lb_checksum.type != CHECKSUM_PCBIOS) || + (lb_checksum.size != sizeof(lb_checksum))) { + return 0; + } + for(i = lb_checksum.range_start; i <= lb_checksum.range_end; i+= 8) { + sum += cmos_read(i, 8); + } + sum = (~sum)&0x0ffff; + old_sum = cmos_read_checksum(); + return sum == old_sum; +} + +static void cmos_write(unsigned offset, unsigned int size, unsigned setting) +{ + unsigned addr, old_addr; + unsigned value, mask, shift; + unsigned sum; + + addr = offset/8; + + shift = offset & 0x7; + mask = ((1 << size) - 1) << shift; + setting = (setting << shift) & mask; + + old_addr = inb(0x70); + sum = cmos_read_checksum(); + sum = (~sum) & 0xffff; + + outb(addr | (old_addr &0x80), 0x70); + value = inb(0x71); + sum -= value; + value &= ~mask; + value |= setting; + sum += value; + outb(value, 0x71); + + sum = (~sum) & 0x0ffff; + outb((lb_checksum.location/8) | (old_addr & 0x80), 0x70); + outb((sum >> 8) & 0xff, 0x71); + outb(((lb_checksum.location +8)/8) | (old_addr & 0x80), 0x70); + outb(sum & 0xff, 0x71); + + outb(old_addr, 0x70); + + return; +} + +#endif + +struct lb_memory *mem = NULL; + +static void read_linuxbios_values( struct lb_header *head) +{ + /* Read linuxbios tables... */ + struct lb_record *rec; + for_each_lbrec(head, rec) { + switch(rec->tag) { + case LB_TAG_MEMORY: + { + mem = (struct lb_memory *) rec; +// read_lb_memory(info, mem); + break; + } + case LB_TAG_CMOS_OPTION_TABLE: + { + break; + } + default: + break; + }; + } +} + + + +static unsigned long count_lb_records(void *start, unsigned long length) +{ + struct lb_record *rec; + void *end; + unsigned long count; + count = 0; + end = ((char *)start) + length; + for(rec = start; ((void *)rec < end) && + ((signed long)rec->size <= (end - (void *)rec)); + rec = (void *)(((char *)rec) + rec->size)) { + count++; + } + return count; +} + +static int find_lb_table(void *start, void *end, struct lb_header **result) +{ + unsigned char *ptr; + /* For now be stupid.... */ + for(ptr = start; (void *)ptr < end; ptr += 16) { + struct lb_header *head = (struct lb_header *)ptr; + if ( (head->signature[0] != 'L') || + (head->signature[1] != 'B') || + (head->signature[2] != 'I') || + (head->signature[3] != 'O')) { + continue; + } + if (head->header_bytes != sizeof(*head)) + continue; +#if defined(DEBUG_LINUXBIOS) + printf("Found canidate at: %X\n", (unsigned long)head); +#endif + if (compute_ip_checksum((uint16_t *)head, sizeof(*head)) != 0) + continue; +#if defined(DEBUG_LINUXBIOS) + printf("header checksum o.k.\n"); +#endif + if (compute_ip_checksum((uint16_t *)(ptr + sizeof(*head)), head->table_bytes) != + head->table_checksum) { + continue; + } +#if defined(DEBUG_LINUXBIOS) + printf("table checksum o.k.\n"); +#endif + if (count_lb_records(ptr + sizeof(*head), head->table_bytes) != + head->table_entries) { + continue; + } +#if defined(DEBUG_LINUXBIOS) + printf("record count o.k.\n"); +#endif + *result = head; + return 1; + }; + return 0; +} + +struct lb_memory *get_lbmem(void) +{ + struct lb_header *lb_table; + int found; +#if defined(DEBUG_LINUXBIOS) + printf("\nSearching for linuxbios tables...\n"); +#endif /* DEBUG_LINUXBIOS */ + found = 0; + + /* This code is specific to linuxBIOS but could + * concievably be extended to work under a normal bios. + * but size is important... + */ + if (!found) { + found = find_lb_table((void *)0x00000, (void *) 0x01000, &lb_table); + } + if (!found) { + found = find_lb_table( (void *) 0xf0000, (void *) 0x100000, &lb_table); + } + if (found) { + printk("Found LinuxBIOS table at: %X\n", (unsigned long)lb_table); +// read_linuxbios_values(&meminfo, lb_table); + } + + if(!found) + return(0); + + read_linuxbios_values(lb_table); + return(mem); + +} + +#if 0 +unsigned long get_boot_order(unsigned long order) +{ + int i; + int checksum_valid; + checksum_valid = cmos_valid(); + for(i = 0; i < MAX_BOOT_ENTRIES; i++) { + unsigned long boot; + boot = order >> (i*BOOT_BITS) & BOOT_MASK; + if (!lb_failsafe && checksum_valid && (lb_boot[i].bit > 0)) { + boot = cmos_read(lb_boot[i].bit, lb_boot[i].length); + if ((boot & BOOT_TYPE_MASK) >= BOOT_NOTHING) { + boot = BOOT_NOTHING; + } else { + /* Set the failsafe bit on all of + * the boot entries... + */ + cmos_write(lb_boot[i].bit, lb_boot[i].length, + boot | BOOT_FAILSAFE); + } + } + order &= ~(BOOT_MASK << (i * BOOT_BITS)); + order |= (boot << (i*BOOT_BITS)); + } + return order; +} +#endif diff --git a/util/baremetal/lib/serial_subr.c b/util/baremetal/lib/serial_subr.c new file mode 100644 index 0000000000..2bb2d4aaf6 --- /dev/null +++ b/util/baremetal/lib/serial_subr.c @@ -0,0 +1,215 @@ +#ifndef lint +static char rcsid[] = "$Id$"; +#endif + +#include +#include +#include +#include +#include + +/* Base Address */ +#ifndef TTYS0_BASE +#define TTYS0_BASE 0x3f8 +#endif + +#ifndef TTYS0_BAUD +#define TTYS0_BAUD 115200 +#endif + +#if ((115200%TTYS0_BAUD) != 0) +#error Bad ttys0 baud rate +#endif + +#define TTYS0_DIV (115200/TTYS0_BAUD) + +/* Line Control Settings */ +#ifndef TTYS0_LCS +/* Set 8bit, 1 stop bit, no parity */ +#define TTYS0_LCS 0x3 +#endif + +#define UART_LCS TTYS0_LCS + +/* Data */ +#define UART_RBR 0x00 +#define UART_TBR 0x00 + +/* Control */ +#define UART_IER 0x01 +#define UART_IIR 0x02 +#define UART_FCR 0x02 +#define UART_LCR 0x03 +#define UART_MCR 0x04 +#define UART_DLL 0x00 +#define UART_DLM 0x01 + +/* Status */ +#define UART_LSR 0x05 +#define UART_MSR 0x06 +#define UART_SCR 0x07 + +static inline int uart_can_tx_byte(unsigned base_port) +{ + return inb(base_port + UART_LSR) & 0x20; +} + +static inline void uart_wait_to_tx_byte(unsigned base_port) +{ + while(!uart_can_tx_byte(base_port)) + ; +} + +static inline void uart_wait_until_sent(unsigned base_port) +{ + while(!(inb(base_port + UART_LSR) & 0x40)) + ; +} + +static inline void uart_tx_byte(unsigned base_port, unsigned char data) +{ + uart_wait_to_tx_byte(base_port); + outb(data, base_port + UART_TBR); + /* Make certain the data clears the fifos */ + uart_wait_until_sent(base_port); +} + +static inline void uart_tx_bytes(unsigned base_port, char *data, unsigned len) +{ + do { + uart_wait_to_tx_byte(base_port); + outb(*data, base_port + UART_TBR); + data++; + len--; + } while(len); + uart_wait_until_sent(base_port); +} + + +static inline int uart_have_rx_byte(unsigned base_port) +{ + return inb(base_port + UART_LSR) & 0x1; +} + +static inline void uart_enable_rx_byte(unsigned base_port) +{ + unsigned char byte; + /* say we are ready for a byte */ + byte = inb(base_port + UART_MCR); + byte |= 0x02; + outb(byte, base_port + UART_MCR); +} + +static inline void uart_disable_rx_byte(unsigned base_port) +{ + unsigned char byte; + /* say we aren't ready for another byte */ + byte = inb(base_port + UART_MCR); + byte &= ~0x02; + outb(byte, base_port + UART_MCR); +} + +static inline void uart_wait_for_rx_byte(unsigned base_port) +{ + uart_enable_rx_byte(base_port); + while(!uart_have_rx_byte(base_port)) + ; + uart_disable_rx_byte(base_port); +} + +static inline unsigned char uart_rx_byte(unsigned base_port) +{ + unsigned char data; + if (!uart_have_rx_byte(base_port)) { + uart_wait_for_rx_byte(base_port); + } + data = inb(base_port + UART_RBR); + return data; +} + +static inline unsigned long uart_rx_bytes(unsigned base_port, + char * buffer, unsigned long size) +{ + unsigned long bytes = 0; + if (size == 0) { + return 0; + } + if (!uart_have_rx_byte(base_port)) { + uart_wait_for_rx_byte(base_port); + } + do { + buffer[bytes++] = inb(base_port + UART_RBR); + } while((bytes < size) && uart_have_rx_byte(base_port)); + return bytes; +} + +inline void uart_init(unsigned base_port, unsigned divisor) +{ + /* disable interrupts */ + outb(0x0, base_port + UART_IER); + /* enable fifo's */ + outb(0x01, base_port + UART_FCR); + /* Set Baud Rate Divisor to 12 ==> 115200 Baud */ + outb(0x80 | UART_LCS, base_port + UART_LCR); + outb(divisor & 0xFF, base_port + UART_DLL); + outb((divisor >> 8) & 0xFF, base_port + UART_DLM); + outb(UART_LCS, base_port + UART_LCR); +} + +void ttys0_init(void) +{ + static unsigned char div[8]={1,2,3,6,12,24,48,96}; + int b_index=0; + unsigned int divisor=TTYS0_DIV; + + if(get_option(&b_index,"baud_rate")==0) { + divisor=div[b_index]; + } + uart_init(TTYS0_BASE, divisor); +} + +void ttys0_tx_byte(unsigned char data) +{ + uart_tx_byte(TTYS0_BASE, data); +} + +void ttys0_tx_bytes(char *data, unsigned len) +{ + uart_tx_bytes(TTYS0_BASE, data,len); +} + +unsigned char ttys0_rx_byte(void) +{ + return uart_rx_byte(TTYS0_BASE); +} + +unsigned long ttys0_rx_bytes(char *buffer, unsigned long size) +{ + return uart_rx_bytes(TTYS0_BASE, buffer, size); +} + +#ifdef PYRO_SERIAL +/* experimental serial read stuffs */ +int iskey(void) +{ + return uart_have_rx_byte(TTYS0_BASE); +} + +char ttys0_rx_char(void) +{ + return ttys0_rx_byte(); +} + +void ttys0_rx_line(char *buffer, int *len) +{ + int pos=0; + char chargot=0; + + while(chargot != '\r' && chargot != '\n' && pos< *len) { + chargot = ttys0_rx_char(); + buffer[pos++] = chargot; + } + + *len = pos-1; +} +#endif diff --git a/util/baremetal/lib/subr.c b/util/baremetal/lib/subr.c new file mode 100644 index 0000000000..5c12c7c1a2 --- /dev/null +++ b/util/baremetal/lib/subr.c @@ -0,0 +1,99 @@ +/* + * Bootstrap code for the INTEL + * $Id$ + * + */ + +#ifndef lint +static char rcsid[] = "$Id$"; +#endif + +#include +#include + +#include +#include +#include +#include + +#ifdef SERIAL_CONSOLE +#include +#endif +#ifdef VIDEO_CONSOLE +#include +#endif +#ifdef LOGBUF_CONSOLE +#include +#endif + +#define SERIAL_CONSOLE +// initialize the display +void displayinit(void) +{ + +#ifdef VIDEO_CONSOLE + video_init(); +#endif +#ifdef SERIAL_CONSOLE + ttys0_init(); +#endif +} + +static void __display_tx_byte(unsigned char byte) +{ +#ifdef VIDEO_CONSOLE + video_tx_byte(byte); +#endif +#ifdef SERIAL_CONSOLE + ttys0_tx_byte(byte); +#endif +#ifdef SROM_CONSOLE + srom_tx_byte(byte); +#endif +#ifdef LOGBUF_CONSOLE + logbuf_tx_byte(byte); +#endif +} + +void display_tx_break(void) +{ +} + +void display_tx_byte(unsigned char byte) +{ + if (byte == '\n') + __display_tx_byte('\r'); + __display_tx_byte(byte); +} + +void display(char *string) +{ + while(*string) { + display_tx_byte(*string); + string++; + } + display_tx_break(); +} + +void error(char errmsg[]) +{ + display(errmsg); + post_code(0xff); + while (1); /* Halt */ +} + +/* + * Write POST information + */ +void post_code(uint8_t value) +{ +#if SERIAL_POST + unsigned long hi, lo; + // DAMMIT! This just broke! + //rdtsc(lo, hi); + printk_info("POST: 0x%02x, TSC Lo: %d, Hi: %d\n", + value, lo, hi); +#endif + outb(value, 0x80); +} +