Initial commit

This commit is contained in:
Steven James 2002-11-12 17:20:38 +00:00
commit eaefd1f006
9 changed files with 1616 additions and 0 deletions

View file

@ -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

View file

@ -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) }
}

View file

@ -0,0 +1,24 @@
#include <printk.h>
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);
}

View file

@ -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

191
util/baremetal/lib/boot.c Normal file
View file

@ -0,0 +1,191 @@
#include <ip_checksum.h>
#include <boot/elf.h>
#include <boot/elf_boot.h>
#include <string.h>
#include <printk.h>
#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

View file

@ -0,0 +1,665 @@
#include <printk.h>
#include <part/fallback_boot.h>
#include <boot/elf.h>
#include <boot/elf_boot.h>
#include <boot/linuxbios_tables.h>
#include <ip_checksum.h>
#include <rom/read_bytes.h>
#include <string.h>
#include <subr.h>
#include <stdint.h>
#include <stdlib.h>
/* 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;
}

View file

@ -0,0 +1,305 @@
//#include <linuxbios_tables.h>
#include <printk.h>
#include <boot/linuxbios_table.h>
#include <stdlib.h>
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

View file

@ -0,0 +1,215 @@
#ifndef lint
static char rcsid[] = "$Id$";
#endif
#include <config.h>
#include <arch/io.h>
#include <serial_subr.h>
#include <printk.h>
#include <pc80/mc146818rtc.h>
/* 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

99
util/baremetal/lib/subr.c Normal file
View file

@ -0,0 +1,99 @@
/*
* Bootstrap code for the INTEL
* $Id$
*
*/
#ifndef lint
static char rcsid[] = "$Id$";
#endif
#include <config.h>
#include <arch/io.h>
#include <printk.h>
#include <subr.h>
#include <string.h>
#include <pc80/mc146818rtc.h>
#ifdef SERIAL_CONSOLE
#include <serial_subr.h>
#endif
#ifdef VIDEO_CONSOLE
#include <video_subr.h>
#endif
#ifdef LOGBUF_CONSOLE
#include <logbuf_subr.h>
#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);
}