Initial commit
This commit is contained in:
parent
504a78151b
commit
eaefd1f006
9 changed files with 1616 additions and 0 deletions
10
util/baremetal/hello/Makefile
Normal file
10
util/baremetal/hello/Makefile
Normal 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
|
||||
71
util/baremetal/hello/elfImage.lds
Normal file
71
util/baremetal/hello/elfImage.lds
Normal 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) }
|
||||
}
|
||||
24
util/baremetal/hello/hello.c
Normal file
24
util/baremetal/hello/hello.c
Normal 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);
|
||||
}
|
||||
36
util/baremetal/lib/Makefile
Normal file
36
util/baremetal/lib/Makefile
Normal 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
191
util/baremetal/lib/boot.c
Normal 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
|
||||
665
util/baremetal/lib/elfboot.c
Normal file
665
util/baremetal/lib/elfboot.c
Normal 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;
|
||||
|
||||
}
|
||||
305
util/baremetal/lib/linuxbios.c
Normal file
305
util/baremetal/lib/linuxbios.c
Normal 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
|
||||
215
util/baremetal/lib/serial_subr.c
Normal file
215
util/baremetal/lib/serial_subr.c
Normal 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
99
util/baremetal/lib/subr.c
Normal 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);
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue