diff options
Diffstat (limited to 'qemu/roms/openbios/arch/amd64')
22 files changed, 2351 insertions, 0 deletions
diff --git a/qemu/roms/openbios/arch/amd64/Kconfig b/qemu/roms/openbios/arch/amd64/Kconfig new file mode 100644 index 000000000..f1f677dba --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/Kconfig @@ -0,0 +1,48 @@ +mainmenu "OpenBIOS Configuration" + +config AMD64 + bool + default y + help + Building for AMD64 hardware. + +config LITTLE_ENDIAN + bool + default y + help + AMD64 is little endian. + + +menu "Kernel binaries (AMD64)" + +config IMAGE_ELF + bool "ELF image (for LinuxBIOS)" + default y + help + Build a simple elf image that can be used with LinuxBIOS + This image will be called openbios.elf + +config IMAGE_ELF_EMBEDDED + bool "ELF image with embedded dictionary" + default y + help + Build an elf image with embedded dictionary. This image + can easily be used with etherboot. + The image filename is openbios.full + +config IMAGE_ELF_MULTIBOOT + bool "Multiboot image" + default y + help + Build a multiboot image for booting with grub + +endmenu + +menu "Build hosted UNIX Binary" +source "arch/unix/Kconfig" +endmenu + +source "kernel/Kconfig" +source "forth/Kconfig" +source "libopenbios/Kconfig" +source "drivers/Kconfig" diff --git a/qemu/roms/openbios/arch/amd64/boot.c b/qemu/roms/openbios/arch/amd64/boot.c new file mode 100644 index 000000000..0e1fe7efd --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/boot.c @@ -0,0 +1,41 @@ +/* + * + */ +#undef BOOTSTRAP +#include "config.h" +#include "libopenbios/bindings.h" +#include "libopenbios/elfload.h" +#include "arch/common/nvram.h" +#include "libc/diskio.h" +#include "libopenbios/sys_info.h" + +int elf_load(struct sys_info *, const char *filename, const char *cmdline); +int linux_load(struct sys_info *, const char *filename, const char *cmdline); + +void boot(void); + +void boot(void) +{ + char *path=pop_fstr_copy(), *param; + + // char *param="root=/dev/hda2 console=ttyS0,115200n8 console=tty0"; + + if(!path) { + printk("[x86] Booting default not supported.\n"); + return; + } + + param = strchr(path, ' '); + if(param) { + *param = '\0'; + param++; + } + + printk("[x86] Booting file '%s' with parameters '%s'\n",path, param); + + if (elf_load(&sys_info, path, param) == LOADER_NOT_SUPPORT) + if (linux_load(&sys_info, path, param) == LOADER_NOT_SUPPORT) + printk("Unsupported image format\n"); + + free(path); +} diff --git a/qemu/roms/openbios/arch/amd64/build.xml b/qemu/roms/openbios/arch/amd64/build.xml new file mode 100644 index 000000000..8f436d001 --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/build.xml @@ -0,0 +1,6 @@ +<build condition="AMD64"> + <dictionary name="openbios-amd64" init="openbios" target="forth"> + <object source="init.fs"/> + <object source="QEMU,VGA.bin" target="fcode" condition="DRIVER_VGA" /> + </dictionary> +</build> diff --git a/qemu/roms/openbios/arch/amd64/builtin.c b/qemu/roms/openbios/arch/amd64/builtin.c new file mode 100644 index 000000000..93ced0ae3 --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/builtin.c @@ -0,0 +1,25 @@ +/* tag: openbios forth starter for builtin dictionary for amd64 + * + * Copyright (C) 2003 Stefan Reinauer + * + * See the file "COPYING" for further information about + * the copyright and warranty status of this work. + */ + +#include "config.h" +#include <asm/types.h> +#include "libopenbios/sys_info.h" + +/* + * wrap an array around the hex'ed dictionary file + */ + +#include "static-dict.h" + +void collect_multiboot_info(struct sys_info *info); +void collect_multiboot_info(struct sys_info *info) +{ + info->dict_start=(unsigned long *)forth_dictionary; + info->dict_end=(unsigned long *)((ucell)forth_dictionary + + sizeof(forth_dictionary)); +} diff --git a/qemu/roms/openbios/arch/amd64/console.c b/qemu/roms/openbios/arch/amd64/console.c new file mode 100644 index 000000000..71a22b681 --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/console.c @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2003, 2004 Stefan Reinauer + * + * See the file "COPYING" for further information about + * the copyright and warranty status of this work. + */ + +#include "config.h" +#include "kernel/kernel.h" +#include "openbios.h" + +#ifdef CONFIG_DEBUG_CONSOLE + +/* ****************************************************************** + * serial console functions + * ****************************************************************** */ + +#ifdef CONFIG_DEBUG_CONSOLE_SERIAL + +#define RBR(x) x==2?0x2f8:0x3f8 +#define THR(x) x==2?0x2f8:0x3f8 +#define IER(x) x==2?0x2f9:0x3f9 +#define IIR(x) x==2?0x2fa:0x3fa +#define LCR(x) x==2?0x2fb:0x3fb +#define MCR(x) x==2?0x2fc:0x3fc +#define LSR(x) x==2?0x2fd:0x3fd +#define MSR(x) x==2?0x2fe:0x3fe +#define SCR(x) x==2?0x2ff:0x3ff +#define DLL(x) x==2?0x2f8:0x3f8 +#define DLM(x) x==2?0x2f9:0x3f9 + +static int uart_charav(int port) +{ + if (!port) + return -1; + return ((inb(LSR(port)) & 1) != 0); +} + +static char uart_getchar(int port) +{ + if (!port) + return -1; + while (!uart_charav(port)); + return ((char) inb(RBR(port)) & 0177); +} + +static void uart_putchar(int port, unsigned char c) +{ + if (!port) + return; + if (c == '\n') + uart_putchar(port, '\r'); + while (!(inb(LSR(port)) & 0x20)); + outb(c, THR(port)); +} + +static void uart_init_line(int port, unsigned long baud) +{ + int i, baudconst; + + if (!port) + return; + + switch (baud) { + case 115200: + baudconst = 1; + break; + case 57600: + baudconst = 2; + break; + case 38400: + baudconst = 3; + break; + case 19200: + baudconst = 6; + break; + case 9600: + default: + baudconst = 12; + break; + } + + outb(0x87, LCR(port)); + outb(0x00, DLM(port)); + outb(baudconst, DLL(port)); + outb(0x07, LCR(port)); + outb(0x0f, MCR(port)); + + for (i = 10; i > 0; i--) { + if (inb(LSR(port)) == (unsigned int) 0) + break; + inb(RBR(port)); + } +} + +int uart_init(int port, unsigned long speed) +{ + if (port) + uart_init_line(port, speed); + return -1; +} + +static void serial_putchar(int c) +{ + uart_putchar(CONFIG_SERIAL_PORT, (unsigned char) (c & 0xff)); +} + +static void serial_cls(void) +{ + serial_putchar(27); + serial_putchar('['); + serial_putchar('H'); + serial_putchar(27); + serial_putchar('['); + serial_putchar('J'); +} + +#endif + +/* ****************************************************************** + * simple polling video/keyboard console functions + * ****************************************************************** */ + +#ifdef CONFIG_DEBUG_CONSOLE_VGA + +/* raw vga text mode */ +#define COLUMNS 80 /* The number of columns. */ +#define LINES 25 /* The number of lines. */ +#define ATTRIBUTE 7 /* The attribute of an character. */ + +#define VGA_BASE 0xB8000 /* The video memory address. */ + +/* VGA Index and Data Registers */ +#define VGA_REG_INDEX 0x03D4 /* VGA index register */ +#define VGA_REG_DATA 0x03D5 /* VGA data register */ + +#define VGA_IDX_CURMSL 0x09 /* cursor maximum scan line */ +#define VGA_IDX_CURSTART 0x0A /* cursor start */ +#define VGA_IDX_CUREND 0x0B /* cursor end */ +#define VGA_IDX_CURLO 0x0F /* cursor position (low 8 bits) */ +#define VGA_IDX_CURHI 0x0E /* cursor position (high 8 bits) */ + +/* Save the X and Y position. */ +static int xpos, ypos; +/* Point to the video memory. */ +static volatile unsigned char *video = (unsigned char *) VGA_BASE; + +static void video_initcursor(void) +{ + u8 val; + outb(VGA_IDX_CURMSL, VGA_REG_INDEX); + val = inb(VGA_REG_DATA) & 0x1f; /* maximum scan line -1 */ + + outb(VGA_IDX_CURSTART, VGA_REG_INDEX); + outb(0, VGA_REG_DATA); + + outb(VGA_IDX_CUREND, VGA_REG_INDEX); + outb(val, VGA_REG_DATA); +} + + + +static void video_poscursor(unsigned int x, unsigned int y) +{ + unsigned short pos; + + /* Calculate new cursor position as a function of x and y */ + pos = (y * COLUMNS) + x; + + /* Output the new position to VGA card */ + outb(VGA_IDX_CURLO, VGA_REG_INDEX); /* output low 8 bits */ + outb((u8) (pos), VGA_REG_DATA); + outb(VGA_IDX_CURHI, VGA_REG_INDEX); /* output high 8 bits */ + outb((u8) (pos >> 8), VGA_REG_DATA); + +}; + + +static void video_newline(void) +{ + xpos = 0; + + if (ypos < LINES - 1) { + ypos++; + } else { + int i; + memmove((void *) video, (void *) (video + 2 * COLUMNS), + (LINES - 1) * COLUMNS * 2); + + for (i = ((LINES - 1) * 2 * COLUMNS); + i < 2 * COLUMNS * LINES;) { + video[i++] = 0; + video[i++] = ATTRIBUTE; + } + } + +} + +/* Put the character C on the screen. */ +static void video_putchar(int c) +{ + int p=1; + + if (c == '\n' || c == '\r') { + video_newline(); + return; + } + + if (c == '\b') { + if (xpos) xpos--; + c=' '; + p=0; + } + + + if (xpos >= COLUMNS) + video_newline(); + + *(video + (xpos + ypos * COLUMNS) * 2) = c & 0xFF; + *(video + (xpos + ypos * COLUMNS) * 2 + 1) = ATTRIBUTE; + + if (p) + xpos++; + + video_poscursor(xpos, ypos); +} + +static void video_cls(void) +{ + int i; + + for (i = 0; i < 2 * COLUMNS * LINES;) { + video[i++] = 0; + video[i++] = ATTRIBUTE; + } + + + xpos = 0; + ypos = 0; + + video_initcursor(); + video_poscursor(xpos, ypos); +} + +void video_init(void) +{ + video=phys_to_virt((unsigned char*)VGA_BASE); +} + +/* + * keyboard driver + */ + +static char normal[] = { + 0x0, 0x1b, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', + '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', + 'p', '[', ']', 0xa, 0x0, 'a', 's', 'd', 'f', 'g', 'h', 'j', + 'k', 'l', ';', 0x27, 0x60, 0x0, 0x5c, 'z', 'x', 'c', 'v', 'b', + 'n', 'm', ',', '.', '/', 0x0, '*', 0x0, ' ', 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, '0', 0x7f +}; + +static char shifted[] = { + 0x0, 0x1b, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', + '+', '\b', '\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', + 'P', '{', '}', 0xa, 0x0, 'A', 'S', 'D', 'F', 'G', 'H', 'J', + 'K', 'L', ':', 0x22, '~', 0x0, '|', 'Z', 'X', 'C', 'V', 'B', + 'N', 'M', '<', '>', '?', 0x0, '*', 0x0, ' ', 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, '7', '8', + '9', 0x0, '4', '5', '6', 0x0, '1', '2', '3', '0', 0x7f +}; + +static int key_ext; +static int key_lshift = 0, key_rshift = 0, key_caps = 0; + +static char last_key; + +static void keyboard_cmd(unsigned char cmd, unsigned char val) +{ + outb(cmd, 0x60); + /* wait until keyboard controller accepts cmds: */ + while (inb(0x64) & 2); + outb(val, 0x60); + while (inb(0x64) & 2); +} + +static char keyboard_poll(void) +{ + unsigned int c; + if (inb(0x64) & 1) { + c = inb(0x60); + switch (c) { + case 0xe0: + key_ext = 1; + return 0; + case 0x2a: + key_lshift = 1; + return 0; + case 0x36: + key_rshift = 1; + return 0; + case 0xaa: + key_lshift = 0; + return 0; + case 0xb6: + key_rshift = 0; + return 0; + case 0x3a: + if (key_caps) { + key_caps = 0; + keyboard_cmd(0xed, 0); + } else { + key_caps = 1; + keyboard_cmd(0xed, 4); /* set caps led */ + } + return 0; + } + + if (key_ext) { + // void printk(const char *format, ...); + printk("extended keycode: %x\n", c); + + key_ext = 0; + return 0; + } + + if (c & 0x80) /* unhandled key release */ + return 0; + + if (key_lshift || key_rshift) + return key_caps ? normal[c] : shifted[c]; + else + return key_caps ? shifted[c] : normal[c]; + } + return 0; +} + +static int keyboard_dataready(void) +{ + if (last_key) + return 1; + + last_key = keyboard_poll(); + + return (last_key != 0); +} + +static unsigned char keyboard_readdata(void) +{ + char tmp; + while (!keyboard_dataready()); + tmp = last_key; + last_key = 0; + return tmp; +} +#endif + + +/* ****************************************************************** + * common functions, implementing simple concurrent console + * ****************************************************************** */ + +int arch_putchar(int c) +{ +#ifdef CONFIG_DEBUG_CONSOLE_SERIAL + serial_putchar(c); +#endif +#ifdef CONFIG_DEBUG_CONSOLE_VGA + video_putchar(c); +#endif + return c; +} + +int arch_availchar(void) +{ +#ifdef CONFIG_DEBUG_CONSOLE_SERIAL + if (uart_charav(CONFIG_SERIAL_PORT)) + return 1; +#endif +#ifdef CONFIG_DEBUG_CONSOLE_VGA + if (keyboard_dataready()) + return 1; +#endif + return 0; +} + +int arch_getchar(void) +{ +#ifdef CONFIG_DEBUG_CONSOLE_SERIAL + if (uart_charav(CONFIG_SERIAL_PORT)) + return (uart_getchar(CONFIG_SERIAL_PORT)); +#endif +#ifdef CONFIG_DEBUG_CONSOLE_VGA + if (keyboard_dataready()) + return (keyboard_readdata()); +#endif + return 0; +} + +void cls(void) +{ +#ifdef CONFIG_DEBUG_CONSOLE_SERIAL + serial_cls(); +#endif +#ifdef CONFIG_DEBUG_CONSOLE_VGA + video_cls(); +#endif +} + +struct _console_ops arch_console_ops = { + .putchar = arch_putchar, + .availchar = arch_availchar, + .getchar = arch_getchar +}; + +#endif // CONFIG_DEBUG_CONSOLE diff --git a/qemu/roms/openbios/arch/amd64/context.c b/qemu/roms/openbios/arch/amd64/context.c new file mode 100644 index 000000000..2e4df6a3d --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/context.c @@ -0,0 +1,124 @@ +/* + * context switching + * 2003-10 by SONE Takeshi + */ + +#include "config.h" +#include "kernel/kernel.h" +#include "segment.h" +#include "context.h" + +#define MAIN_STACK_SIZE 16384 +#define IMAGE_STACK_SIZE 4096 + +#define debug printk + +static void start_main(void); /* forward decl. */ +void __exit_context(void); /* assembly routine */ + +/* + * Main context structure + * It is placed at the bottom of our stack, and loaded by assembly routine + * to start us up. + */ +struct context main_ctx __attribute__((section (".initctx"))) = { + .gdt_base = (uint64_t) gdt, + .gdt_limit = GDT_LIMIT, + .cs = FLAT_CS, + .ds = FLAT_DS, + .es = FLAT_DS, + .fs = FLAT_DS, + .gs = FLAT_DS, + .ss = FLAT_DS, + .esp = (uint32_t) ESP_LOC(&main_ctx), + .eip = (uint32_t) start_main, + .return_addr = (uint32_t) __exit_context, +}; + +/* This is used by assembly routine to load/store the context which + * it is to switch/switched. */ +struct context *__context = &main_ctx; + +/* Stack for loaded ELF image */ +static uint8_t image_stack[IMAGE_STACK_SIZE]; + +/* Pointer to startup context (physical address) */ +unsigned long __boot_ctx; + +/* + * Main starter + * This is the C function that runs first. + */ +static void start_main(void) +{ + int retval; + extern int openbios(void); + + /* Save startup context, so we can refer to it later. + * We have to keep it in physical address since we will relocate. */ + __boot_ctx = virt_to_phys(__context); + + /* Start the real fun */ + retval = openbios(); + + /* Pass return value to startup context. Bootloader may see it. */ + boot_ctx->eax = retval; + + /* Returning from here should jump to __exit_context */ + __context = boot_ctx; +} + +/* Setup a new context using the given stack. + */ +struct context * +init_context(uint8_t *stack, uint32_t stack_size, int num_params) +{ + struct context *ctx; + + ctx = (struct context *) + (stack + stack_size - (sizeof(*ctx) + num_params*sizeof(uint32_t))); + memset(ctx, 0, sizeof(*ctx)); + + /* Fill in reasonable default for flat memory model */ + ctx->gdt_base = virt_to_phys(gdt); + ctx->gdt_limit = GDT_LIMIT; + ctx->cs = FLAT_CS; + ctx->ds = FLAT_DS; + ctx->es = FLAT_DS; + ctx->fs = FLAT_DS; + ctx->gs = FLAT_DS; + ctx->ss = FLAT_DS; + ctx->esp = virt_to_phys(ESP_LOC(ctx)); + ctx->return_addr = virt_to_phys(__exit_context); + + return ctx; +} + +/* Switch to another context. */ +struct context *switch_to(struct context *ctx) +{ + struct context *save, *ret; + + debug("switching to new context:\n"); + save = __context; + __context = ctx; + asm ("pushl %cs; call __switch_context"); + ret = __context; + __context = save; + return ret; +} + +/* Start ELF Boot image */ +uint32_t start_elf(uint32_t entry_point, uint32_t param) +{ + struct context *ctx; + + ctx = init_context(image_stack, sizeof image_stack, 1); + ctx->eip = entry_point; + ctx->param[0] = param; + ctx->eax = 0xe1fb007; + ctx->ebx = param; + + ctx = switch_to(ctx); + return ctx->eax; +} diff --git a/qemu/roms/openbios/arch/amd64/context.h b/qemu/roms/openbios/arch/amd64/context.h new file mode 100644 index 000000000..4c3832efb --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/context.h @@ -0,0 +1,48 @@ +#ifndef AMD64_CONTEXT_H +#define AMD64_CONTEXT_H + +struct context { + /* Stack Segment, placed here because of the alignment issue... */ + uint16_t ss; + /* Used with sgdt/lgdt */ + uint16_t gdt_limit; + uint64_t gdt_base; + /* General registers, accessed with pushal/popal */ + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; /* points just below eax */ + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; +#define ESP_LOC(ctx) (&(ctx)->gs) + /* Segment registers */ + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + /* Flags */ + uint32_t eflags; + /* Code segment:offset */ + uint32_t eip; + uint32_t cs; + /* Optional stack contents */ + uint32_t return_addr; + uint32_t param[0]; +}; + +/* Create a new context in the given stack */ +struct context * +init_context(uint8_t *stack, uint32_t stack_size, int num_param); + +/* Switch context */ +struct context *switch_to(struct context *); + +/* Holds physical address of boot context */ +extern unsigned long __boot_ctx; + +/* This can always be safely used to refer to the boot context */ +#define boot_ctx ((struct context *) phys_to_virt(__boot_ctx)) + +#endif /* AMD64_CONTEXT_H */ diff --git a/qemu/roms/openbios/arch/amd64/defconfig b/qemu/roms/openbios/arch/amd64/defconfig new file mode 100644 index 000000000..570a6c869 --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/defconfig @@ -0,0 +1,65 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_AMD64=y +CONFIG_LITTLE_ENDIAN=y + +# +# Kernel binaries (AMD64) +# +# CONFIG_IMAGE_ELF is not set +# CONFIG_IMAGE_ELF_EMBEDDED is not set +# CONFIG_IMAGE_ELF_MULTIBOOT is not set + +# +# Build hosted UNIX Binary +# +CONFIG_HOST_UNIX=y +# CONFIG_PLUGIN_PCI is not set + +# +# Kernel Debugging +# +# CONFIG_DEBUG is not set +CONFIG_DEBUG_CONSOLE=y +CONFIG_DEBUG_CONSOLE_SERIAL=y +CONFIG_SERIAL_PORT=1 +CONFIG_SERIAL_SPEED=115200 +CONFIG_DEBUG_CONSOLE_VGA=y + +# +# Module Configuration +# +CONFIG_CMDLINE=y +CONFIG_DEBLOCKER=y + +# +# Filesystem Configuration +# +CONFIG_DISK_LABEL=y +CONFIG_PART_SUPPORT=y +CONFIG_PC_PARTS=y +CONFIG_FS=y +CONFIG_GRUBFS=y +CONFIG_FSYS_EXT2FS=y +CONFIG_FSYS_FAT=y +CONFIG_FSYS_JFS=y +# CONFIG_FSYS_MINIX is not set +CONFIG_FSYS_REISERFS=y +CONFIG_FSYS_XFS=y +CONFIG_FSYS_ISO9660=y +# CONFIG_FSYS_FFS is not set +# CONFIG_FSYS_VSTAFS is not set +# CONFIG_DEBUG_FS is not set + +# +# Miscellaneous +# +CONFIG_LINUXBIOS=y + +# +# Drivers +# +CONFIG_DRIVER_PCI=y +CONFIG_DRIVER_IDE=y +# CONFIG_DEBUG_IDE is not set diff --git a/qemu/roms/openbios/arch/amd64/init.fs b/qemu/roms/openbios/arch/amd64/init.fs new file mode 100644 index 000000000..fda3acdc8 --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/init.fs @@ -0,0 +1,83 @@ +include config.fs + +:noname + ." Type 'help' for detailed information" cr + \ ." boot secondary slave cdrom: " cr + \ ." 0 > boot hd:2,\boot\vmlinuz root=/dev/hda2" cr + ; DIAG-initializer + +" /" find-device + +new-device + " memory" device-name + \ 12230 encode-int " reg" property + external + : open true ; + : close ; + \ claim ( phys size align -- base ) + \ release ( phys size -- ) +finish-device + +new-device + " cpus" device-name + 1 " #address-cells" int-property + 0 " #size-cells" int-property + + external + : open true ; + : close ; + : decode-unit parse-hex ; + +finish-device + +: make-openable ( path ) + find-dev if + begin ?dup while + \ install trivial open and close methods + dup active-package! is-open + parent + repeat + then +; + +: preopen ( chosen-str node-path ) + 2dup make-openable + + " /chosen" find-device + open-dev ?dup if + encode-int 2swap property + else + 2drop + then +; + +:noname + set-defaults +; SYSTEM-initializer + +\ preopen device nodes (and store the ihandles under /chosen) +:noname + " memory" " /memory" preopen + " mmu" " /cpus/@0" preopen + " stdout" " /builtin/console" preopen + " stdin" " /builtin/console" preopen + +; SYSTEM-initializer + +\ use the tty interface if available +:noname + " /builtin/console" find-dev if drop + " /builtin/console" " input-device" $setenv + " /builtin/console" " output-device" $setenv + then +; SYSTEM-initializer + +:noname + " keyboard" input +; CONSOLE-IN-initializer + +\ Load VGA FCode driver blob +[IFDEF] CONFIG_DRIVER_VGA + -1 value vga-driver-fcode + " QEMU,VGA.bin" $encode-file to vga-driver-fcode +[THEN] diff --git a/qemu/roms/openbios/arch/amd64/ldscript b/qemu/roms/openbios/arch/amd64/ldscript new file mode 100644 index 000000000..8976c7af0 --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/ldscript @@ -0,0 +1,73 @@ +OUTPUT_FORMAT(elf32-i386) +OUTPUT_ARCH(i386) + +ENTRY(entry) + +/* Initial load address + * To be loaded by GRUB, this must be >= 1MB + */ +BASE_ADDR = 0x100000; + +/* 16KB heap and stack */ +HEAP_SIZE = 16384; +STACK_SIZE = 16384; + +SECTIONS +{ + . = BASE_ADDR; + + /* Put Multiboot header near beginning of file, if any. */ + .hdr : { *(.hdr) *(.hdr.*) } + + /* Start of the program. + * Now the version string is in the note, we must include it + * in the program. Otherwise we lose the string after relocation. */ + . = ALIGN(16); + _start = .; + + /* Putting ELF notes near beginning of file might help bootloaders. + * We discard .note sections other than .note.ELFBoot, + * because some versions of GCC generates useless ones. */ + .note : { *(.note.ELFBoot) } + + /* Normal sections */ + .text : { *(.text) *(.text.*) } + .rodata : { + . = ALIGN(4); + sound_drivers_start = .; + *(.rodata.sound_drivers) + sound_drivers_end = .; + *(.rodata) + *(.rodata.*) + } + .data : { *(.data) *(.data.*) } + + .bss : { + *(.bss) + *(.bss.*) + *(COMMON) + + /* Put heap and stack here, so they are included in PT_LOAD segment + * and the bootloader is aware of it. */ + + . = ALIGN(16); + _heap = .; + . += HEAP_SIZE; + . = ALIGN(16); + _eheap = .; + + _stack = .; + . += STACK_SIZE; + . = ALIGN(16); + _estack = .; + } + + .initctx : { + /* Initial contents of stack. This MUST BE just after the stack. */ + *(.initctx) + } + + _end = .; + + /DISCARD/ : { *(.comment) *(.note) } +} diff --git a/qemu/roms/openbios/arch/amd64/lib.c b/qemu/roms/openbios/arch/amd64/lib.c new file mode 100644 index 000000000..f04458e1a --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/lib.c @@ -0,0 +1,56 @@ +/* lib.c + * tag: simple function library + * + * Copyright (C) 2003 Stefan Reinauer + * + * See the file "COPYING" for further information about + * the copyright and warranty status of this work. + */ + +#include "config.h" +#include "asm/types.h" +#include <stdarg.h> +#include "libc/stdlib.h" +#include "libc/vsprintf.h" +#include "kernel/kernel.h" + +/* Format a string and print it on the screen, just like the libc + * function printf. + */ +int printk( const char *fmt, ... ) +{ + char *p, buf[512]; + va_list args; + int i; + + va_start(args, fmt); + i = vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + for( p=buf; *p; p++ ) + putchar(*p); + return i; +} + +// dumb quick memory allocator until we get a decent thing here. + +#define MEMSIZE 128*1024 +static char memory[MEMSIZE]; +static void *memptr=memory; +static int memsize=MEMSIZE; + +void *malloc(int size) +{ + void *ret=(void *)0; + if(memsize>=size) { + memsize-=size; + ret=memptr; + memptr+=size; + } + return ret; +} + +void free(void *ptr) +{ + /* Nothing yet */ +} diff --git a/qemu/roms/openbios/arch/amd64/linux_load.c b/qemu/roms/openbios/arch/amd64/linux_load.c new file mode 100644 index 000000000..f1ed98df1 --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/linux_load.c @@ -0,0 +1,647 @@ +/* + * Linux/i386 loader + * Supports bzImage, zImage and Image format. + * + * Based on work by Steve Gehlbach. + * Portions are taken from mkelfImage. + * + * 2003-09 by SONE Takeshi + */ + +#include "config.h" +#include "kernel/kernel.h" +#include "libopenbios/bindings.h" +#include "libopenbios/sys_info.h" +#include "context.h" +#include "segment.h" +#include "loadfs.h" + +#define printf printk +#define debug printk +#define strtoull_with_suffix strtol + +#define LINUX_PARAM_LOC 0x90000 +#define COMMAND_LINE_LOC 0x91000 +#define GDT_LOC 0x92000 +#define STACK_LOC 0x93000 + +#define E820MAX 32 /* number of entries in E820MAP */ +struct e820entry { + unsigned long long addr; /* start of memory segment */ + unsigned long long size; /* size of memory segment */ + unsigned long type; /* type of memory segment */ +#define E820_RAM 1 +#define E820_RESERVED 2 +#define E820_ACPI 3 /* usable as RAM once ACPI tables have been read */ +#define E820_NVS 4 +}; + +/* The header of Linux/i386 kernel */ +struct linux_header { + uint8_t reserved1[0x1f1]; /* 0x000 */ + uint8_t setup_sects; /* 0x1f1 */ + uint16_t root_flags; /* 0x1f2 */ + uint8_t reserved2[6]; /* 0x1f4 */ + uint16_t vid_mode; /* 0x1fa */ + uint16_t root_dev; /* 0x1fc */ + uint16_t boot_sector_magic; /* 0x1fe */ + /* 2.00+ */ + uint8_t reserved3[2]; /* 0x200 */ + uint8_t header_magic[4]; /* 0x202 */ + uint16_t protocol_version; /* 0x206 */ + uint32_t realmode_swtch; /* 0x208 */ + uint16_t start_sys; /* 0x20c */ + uint16_t kver_addr; /* 0x20e */ + uint8_t type_of_loader; /* 0x210 */ + uint8_t loadflags; /* 0x211 */ + uint16_t setup_move_size; /* 0x212 */ + uint32_t code32_start; /* 0x214 */ + uint32_t ramdisk_image; /* 0x218 */ + uint32_t ramdisk_size; /* 0x21c */ + uint8_t reserved4[4]; /* 0x220 */ + /* 2.01+ */ + uint16_t heap_end_ptr; /* 0x224 */ + uint8_t reserved5[2]; /* 0x226 */ + /* 2.02+ */ + uint32_t cmd_line_ptr; /* 0x228 */ + /* 2.03+ */ + uint32_t initrd_addr_max; /* 0x22c */ +} __attribute__ ((packed)); + + +/* Paramters passed to 32-bit part of Linux + * This is another view of the structure above.. */ +struct linux_params { + uint8_t orig_x; /* 0x00 */ + uint8_t orig_y; /* 0x01 */ + uint16_t ext_mem_k; /* 0x02 -- EXT_MEM_K sits here */ + uint16_t orig_video_page; /* 0x04 */ + uint8_t orig_video_mode; /* 0x06 */ + uint8_t orig_video_cols; /* 0x07 */ + uint16_t unused2; /* 0x08 */ + uint16_t orig_video_ega_bx; /* 0x0a */ + uint16_t unused3; /* 0x0c */ + uint8_t orig_video_lines; /* 0x0e */ + uint8_t orig_video_isVGA; /* 0x0f */ + uint16_t orig_video_points; /* 0x10 */ + + /* VESA graphic mode -- linear frame buffer */ + uint16_t lfb_width; /* 0x12 */ + uint16_t lfb_height; /* 0x14 */ + uint16_t lfb_depth; /* 0x16 */ + uint32_t lfb_base; /* 0x18 */ + uint32_t lfb_size; /* 0x1c */ + uint16_t cl_magic; /* 0x20 */ +#define CL_MAGIC_VALUE 0xA33F + uint16_t cl_offset; /* 0x22 */ + uint16_t lfb_linelength; /* 0x24 */ + uint8_t red_size; /* 0x26 */ + uint8_t red_pos; /* 0x27 */ + uint8_t green_size; /* 0x28 */ + uint8_t green_pos; /* 0x29 */ + uint8_t blue_size; /* 0x2a */ + uint8_t blue_pos; /* 0x2b */ + uint8_t rsvd_size; /* 0x2c */ + uint8_t rsvd_pos; /* 0x2d */ + uint16_t vesapm_seg; /* 0x2e */ + uint16_t vesapm_off; /* 0x30 */ + uint16_t pages; /* 0x32 */ + uint8_t reserved4[12]; /* 0x34 -- 0x3f reserved for future expansion */ + + //struct apm_bios_info apm_bios_info; /* 0x40 */ + uint8_t apm_bios_info[0x40]; + //struct drive_info_struct drive_info; /* 0x80 */ + uint8_t drive_info[0x20]; + //struct sys_desc_table sys_desc_table; /* 0xa0 */ + uint8_t sys_desc_table[0x140]; + uint32_t alt_mem_k; /* 0x1e0 */ + uint8_t reserved5[4]; /* 0x1e4 */ + uint8_t e820_map_nr; /* 0x1e8 */ + uint8_t reserved6[9]; /* 0x1e9 */ + uint16_t mount_root_rdonly; /* 0x1f2 */ + uint8_t reserved7[4]; /* 0x1f4 */ + uint16_t ramdisk_flags; /* 0x1f8 */ +#define RAMDISK_IMAGE_START_MASK 0x07FF +#define RAMDISK_PROMPT_FLAG 0x8000 +#define RAMDISK_LOAD_FLAG 0x4000 + uint8_t reserved8[2]; /* 0x1fa */ + uint16_t orig_root_dev; /* 0x1fc */ + uint8_t reserved9[1]; /* 0x1fe */ + uint8_t aux_device_info; /* 0x1ff */ + uint8_t reserved10[2]; /* 0x200 */ + uint8_t param_block_signature[4]; /* 0x202 */ + uint16_t param_block_version; /* 0x206 */ + uint8_t reserved11[8]; /* 0x208 */ + uint8_t loader_type; /* 0x210 */ +#define LOADER_TYPE_LOADLIN 1 +#define LOADER_TYPE_BOOTSECT_LOADER 2 +#define LOADER_TYPE_SYSLINUX 3 +#define LOADER_TYPE_ETHERBOOT 4 +#define LOADER_TYPE_KERNEL 5 + uint8_t loader_flags; /* 0x211 */ + uint8_t reserved12[2]; /* 0x212 */ + uint32_t kernel_start; /* 0x214 */ + uint32_t initrd_start; /* 0x218 */ + uint32_t initrd_size; /* 0x21c */ + uint8_t reserved12_5[8]; /* 0x220 */ + uint32_t cmd_line_ptr; /* 0x228 */ + uint8_t reserved13[164]; /* 0x22c */ + struct e820entry e820_map[E820MAX]; /* 0x2d0 */ + uint8_t reserved16[688]; /* 0x550 */ +#define COMMAND_LINE_SIZE 256 + /* Command line is copied here by 32-bit i386/kernel/head.S. + * So I will follow the boot protocol, rather than putting it + * directly here. --ts1 */ + uint8_t command_line[COMMAND_LINE_SIZE]; /* 0x800 */ + uint8_t reserved17[1792]; /* 0x900 - 0x1000 */ +}; + +uint64_t forced_memsize; + +/* Load the first part the file and check if it's Linux */ +static uint32_t load_linux_header(struct linux_header *hdr) +{ + int load_high; + uint32_t kern_addr; + + if (lfile_read(hdr, sizeof *hdr) != sizeof *hdr) { + debug("Can't read Linux header\n"); + return 0; + } + if (hdr->boot_sector_magic != 0xaa55) { + debug("Not a Linux kernel image\n"); + return 0; + } + + /* Linux is found. Print some information */ + if (memcmp(hdr->header_magic, "HdrS", 4) != 0) { + /* This may be floppy disk image or something. + * Perform a simple (incomplete) sanity check. */ + if (hdr->setup_sects >= 16 + || file_size() - (hdr->setup_sects<<9) >= 512<<10) { + debug("This looks like a bootdisk image but not like Linux...\n"); + return 0; + } + + printf("Possible very old Linux"); + /* This kernel does not even have a protocol version. + * Force the value. */ + hdr->protocol_version = 0; /* pre-2.00 */ + } else + printf("Found Linux"); + if (hdr->protocol_version >= 0x200 && hdr->kver_addr) { + char kver[256]; + file_seek(hdr->kver_addr + 0x200); + if (lfile_read(kver, sizeof kver) != 0) { + kver[255] = 0; + printf(" version %s", kver); + } + } + debug(" (protocol %#x)", hdr->protocol_version); + load_high = 0; + if (hdr->protocol_version >= 0x200) { + debug(" (loadflags %#x)", hdr->loadflags); + load_high = hdr->loadflags & 1; + } + if (load_high) { + printf(" bzImage"); + kern_addr = 0x100000; + } else { + printf(" zImage or Image"); + kern_addr = 0x1000; + } + printf(".\n"); + + return kern_addr; +} + +/* Set up parameters for 32-bit kernel */ +static void +init_linux_params(struct linux_params *params, struct linux_header *hdr) +{ + debug("Setting up paramters at %#lx\n", virt_to_phys(params)); + memset(params, 0, sizeof *params); + + /* Copy some useful values from header */ + params->mount_root_rdonly = hdr->root_flags; + params->orig_root_dev = hdr->root_dev; + + /* Video parameters. + * This assumes we have VGA in standard 80x25 text mode, + * just like our vga.c does. + * Cursor position is filled later to allow some more printf's. */ + params->orig_video_mode = 3; + params->orig_video_cols = 80; + params->orig_video_lines = 25; + params->orig_video_isVGA = 1; + params->orig_video_points = 16; + + params->loader_type = 0xff; /* Unregistered Linux loader */ +} + +/* Memory map */ +static void +set_memory_size(struct linux_params *params, struct sys_info *info) +{ + int i; + uint64_t end; + uint32_t ramtop = 0; + struct e820entry *linux_map; + struct memrange *filo_map; + + linux_map = params->e820_map; + filo_map = info->memrange; + for (i = 0; i < info->n_memranges; i++, linux_map++, filo_map++) { + if (i < E820MAX) { + /* Convert to BIOS e820 style */ + linux_map->addr = filo_map->base; + linux_map->size = filo_map->size; + linux_map->type = E820_RAM; + debug("%016Lx - %016Lx\n", linux_map->addr, + linux_map->addr + linux_map->size); + params->e820_map_nr = i+1; + } + + /* Find out top of RAM. XXX This ignores hole above 1MB */ + end = filo_map->base + filo_map->size; + if (end < (1ULL << 32)) { /* don't count memory above 4GB */ + if (end > ramtop) + ramtop = (uint32_t) end; + } + } + debug("ramtop=%#x\n", ramtop); + /* Size of memory above 1MB in KB */ + params->alt_mem_k = (ramtop - (1<<20)) >> 10; + /* old style, 64MB max */ + if (ramtop >= (64<<20)) + params->ext_mem_k = (63<<10); + else + params->ext_mem_k = params->alt_mem_k; + debug("ext_mem_k=%d, alt_mem_k=%d\n", params->ext_mem_k, params->alt_mem_k); +} + +/* + * Parse command line + * Some parameters, like initrd=<file>, are not passed to kernel, + * we are responsible to process them. + * Parameters for kernel are copied to kern_cmdline. Returns name of initrd. + */ +static char *parse_command_line(const char *orig_cmdline, char *kern_cmdline) +{ + const char *start, *sep, *end, *val; + char name[64]; + int len; + int k_len; + int to_kern; + char *initrd = 0; + int toolong = 0; + + forced_memsize = 0; + + if (!orig_cmdline) { + *kern_cmdline = 0; + return 0; + } + + k_len = 0; + debug("original command line: \"%s\"\n", orig_cmdline); + debug("kernel command line at %#lx\n", virt_to_phys(kern_cmdline)); + + start = orig_cmdline; + while (*start == ' ') + start++; + while (*start) { + end = strchr(start, ' '); + if (!end) + end = start + strlen(start); + sep = strchr(start, '='); + if (!sep || sep > end) + sep = end; + len = sep - start; + if (len >= sizeof(name)) + len = sizeof(name) - 1; + memcpy(name, start, len); + name[len] = 0; + + if (*sep == '=') { + val = sep + 1; + len = end - val; + } else { + val = 0; + len = 0; + } + + /* Only initrd= and mem= are handled here. vga= is not, + * which I believe is a paramter to the realmode part of Linux, + * which we don't execute. */ + if (strcmp(name, "initrd") == 0) { + if (!val) + printf("Missing filename to initrd parameter\n"); + else { + initrd = malloc(len + 1); + memcpy(initrd, val, len); + initrd[len] = 0; + debug("initrd=%s\n", initrd); + } + /* Don't pass this to kernel */ + to_kern = 0; + } else if (strcmp(name, "mem") == 0) { + if (!val) + printf("Missing value for mem parameter\n"); + else { + forced_memsize = strtoull_with_suffix(val, (char**)&val, 0); + if (forced_memsize == 0) + printf("Invalid mem option, ignored\n"); + if (val != end) { + printf("Garbage after mem=<size>, ignored\n"); + forced_memsize = 0; + } + debug("mem=%Lu\n", forced_memsize); + } + /* mem= is for both loader and kernel */ + to_kern = 1; + } else + to_kern = 1; + + if (to_kern) { + /* Copy to kernel command line buffer */ + if (k_len != 0) + kern_cmdline[k_len++] = ' '; /* put separator */ + len = end - start; + if (k_len + len >= COMMAND_LINE_SIZE) { + len = COMMAND_LINE_SIZE - k_len - 1; + if (!toolong) { + printf("Kernel command line is too long; truncated to " + "%d bytes\n", COMMAND_LINE_SIZE-1); + toolong = 1; + } + } + memcpy(kern_cmdline + k_len, start, len); + k_len += len; + } + + start = end; + while (*start == ' ') + start++; + } + kern_cmdline[k_len] = 0; + debug("kernel command line (%d bytes): \"%s\"\n", k_len, kern_cmdline); + + return initrd; +} + +/* Set command line location */ +static void set_command_line_loc(struct linux_params *params, + struct linux_header *hdr) +{ + if (hdr->protocol_version >= 0x202) { + /* new style */ + params->cmd_line_ptr = COMMAND_LINE_LOC; + } else { + /* old style */ + params->cl_magic = CL_MAGIC_VALUE; + params->cl_offset = COMMAND_LINE_LOC - LINUX_PARAM_LOC; + } +} + +/* Load 32-bit part of kernel */ +static int load_linux_kernel(struct linux_header *hdr, uint32_t kern_addr) +{ + uint32_t kern_offset, kern_size; + + if (hdr->setup_sects == 0) + hdr->setup_sects = 4; + kern_offset = (hdr->setup_sects + 1) * 512; + file_seek(kern_offset); + kern_size = file_size() - kern_offset; + debug("offset=%#x addr=%#x size=%#x\n", kern_offset, kern_addr, kern_size); + +#if 0 + if (using_devsize) { + printf("Attempt to load up to end of device as kernel; " + "specify the image size\n"); + return 0; + } +#endif + + printf("Loading kernel... "); + if (lfile_read(phys_to_virt(kern_addr), kern_size) != kern_size) { + printf("Can't read kernel\n"); + return 0; + } + printf("ok\n"); + + return kern_size; +} + +static int load_initrd(struct linux_header *hdr, struct sys_info *info, + uint32_t kern_end, struct linux_params *params, const char *initrd_file) +{ + uint32_t max; + uint32_t start, end, size; + uint64_t forced; + extern char _start[], _end[]; + + if (!file_open(initrd_file)) { + printf("Can't open initrd: %s\n", initrd_file); + return -1; + } + +#if 0 + if (using_devsize) { + printf("Attempt to load up to end of device as initrd; " + "specify the image size\n"); + return -1; + } +#endif + + size = file_size(); + + + /* Find out the kernel's restriction on how high the initrd can be + * placed */ + if (hdr->protocol_version >= 0x203) + max = hdr->initrd_addr_max; + else + max = 0x38000000; /* Hardcoded value for older kernels */ + + /* FILO itself is at the top of RAM. (relocated) + * So, try putting initrd just below us. */ + end = virt_to_phys(_start); + if (end > max) + end = max; + + /* If "mem=" option is given, we have to put the initrd within + * the specified range. */ + if (forced_memsize) { + forced = forced_memsize; + if (forced > max) + forced = max; + /* If the "mem=" is lower, it's easy */ + if (forced <= end) + end = forced; + else { + /* Otherwise, see if we can put it above us */ + if (virt_to_phys(_end) + size <= forced) + end = forced; /* Ok */ + } + } + + start = end - size; + start &= ~0xfff; /* page align */ + end = start + size; + + debug("start=%#x end=%#x\n", start, end); + + if (start < kern_end) { + printf("Initrd is too big to fit in memory\n"); + return -1; + } + + printf("Loading initrd... "); + if (lfile_read(phys_to_virt(start), size) != size) { + printf("Can't read initrd\n"); + return -1; + } + printf("ok\n"); + + params->initrd_start = start; + params->initrd_size = size; + + return 0; +} + +static void hardware_setup(void) +{ + /* Disable nmi */ + outb(0x80, 0x70); + + /* Make sure any coprocessor is properly reset.. */ + outb(0, 0xf0); + outb(0, 0xf1); + + /* we're getting screwed again and again by this problem of the 8259. + * so we're going to leave this lying around for inclusion into + * crt0.S on an as-needed basis. + * + * well, that went ok, I hope. Now we have to reprogram the interrupts :-( + * we put them right after the intel-reserved hardware interrupts, at + * int 0x20-0x2F. There they won't mess up anything. Sadly IBM really + * messed this up with the original PC, and they haven't been able to + * rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f, + * which is used for the internal hardware interrupts as well. We just + * have to reprogram the 8259's, and it isn't fun. + */ + + outb(0x11, 0x20); /* initialization sequence to 8259A-1 */ + outb(0x11, 0xA0); /* and to 8259A-2 */ + + outb(0x20, 0x21); /* start of hardware int's (0x20) */ + outb(0x28, 0xA1); /* start of hardware int's 2 (0x28) */ + + outb(0x04, 0x21); /* 8259-1 is master */ + outb(0x02, 0xA1); /* 8259-2 is slave */ + + outb(0x01, 0x21); /* 8086 mode for both */ + outb(0x01, 0xA1); + + outb(0xFF, 0xA1); /* mask off all interrupts for now */ + outb(0xFB, 0x21); /* mask all irq's but irq2 which is cascaded */ +} + +/* Start Linux */ +static int start_linux(uint32_t kern_addr, struct linux_params *params) +{ + struct segment_desc *linux_gdt; + struct context *ctx; + //extern int cursor_x, cursor_y; + + ctx = init_context(phys_to_virt(STACK_LOC), 4096, 0); + + /* Linux expects GDT being in low memory */ + linux_gdt = phys_to_virt(GDT_LOC); + memset(linux_gdt, 0, 13*sizeof(struct segment_desc)); + /* Normal kernel code/data segments */ + linux_gdt[2] = gdt[FLAT_CODE]; + linux_gdt[3] = gdt[FLAT_DATA]; + /* 2.6 kernel uses 12 and 13, but head.S uses backward-compatible + * segments (2 and 3), so it SHOULD not be a problem. + * However, some distro kernels (eg. RH9) with backported threading + * patch use 12 and 13 also when booting... */ + linux_gdt[12] = gdt[FLAT_CODE]; + linux_gdt[13] = gdt[FLAT_DATA]; + ctx->gdt_base = GDT_LOC; + ctx->gdt_limit = 14*8-1; + ctx->cs = 0x10; + ctx->ds = 0x18; + ctx->es = 0x18; + ctx->fs = 0x18; + ctx->gs = 0x18; + ctx->ss = 0x18; + + /* Parameter location */ + ctx->esi = virt_to_phys(params); + + /* Entry point */ + ctx->eip = kern_addr; + + debug("eip=%#x\n", kern_addr); + printf("Jumping to entry point...\n"); + +#ifdef VGA_CONSOLE + /* Update VGA cursor position. + * This must be here because the printf changes the value! */ + params->orig_x = cursor_x; + params->orig_y = cursor_y; +#endif + + /* Go... */ + ctx = switch_to(ctx); + + /* It's impossible but... */ + printf("Returned with eax=%#x\n", ctx->eax); + + return ctx->eax; +} + +int linux_load(struct sys_info *info, const char *file, const char *cmdline) +{ + struct linux_header hdr; + struct linux_params *params; + uint32_t kern_addr, kern_size; + char *initrd_file = 0; + + if (!file_open(file)) + return -1; + + kern_addr = load_linux_header(&hdr); + if (kern_addr == 0) + return LOADER_NOT_SUPPORT; + + params = phys_to_virt(LINUX_PARAM_LOC); + init_linux_params(params, &hdr); + set_memory_size(params, info); + initrd_file = parse_command_line(cmdline, phys_to_virt(COMMAND_LINE_LOC)); + set_command_line_loc(params, &hdr); + + kern_size = load_linux_kernel(&hdr, kern_addr); + if (kern_size == 0) { + if (initrd_file) + free(initrd_file); + return -1; + } + + if (initrd_file) { + if (load_initrd(&hdr, info, kern_addr+kern_size, params, initrd_file) + != 0) { + free(initrd_file); + return -1; + } + free(initrd_file); + } + + hardware_setup(); + + start_linux(kern_addr, params); + return 0; +} diff --git a/qemu/roms/openbios/arch/amd64/multiboot.c b/qemu/roms/openbios/arch/amd64/multiboot.c new file mode 100644 index 000000000..4271bd52b --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/multiboot.c @@ -0,0 +1,125 @@ +/* Support for Multiboot */ + +#include "config.h" +#include "asm/io.h" +#include "libopenbios/sys_info.h" +#include "multiboot.h" + +#define printf printk +#ifdef CONFIG_DEBUG_BOOT +#define debug printk +#else +#define debug(x...) +#endif + +struct mbheader { + unsigned int magic, flags, checksum; +}; +const struct mbheader multiboot_header + __attribute__((section (".hdr"))) = +{ + MULTIBOOT_HEADER_MAGIC, + MULTIBOOT_HEADER_FLAGS, + -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) +}; + +/* Multiboot information structure, provided by loader to us */ + +struct multiboot_mmap { + unsigned entry_size; + unsigned base_lo, base_hi; + unsigned size_lo, size_hi; + unsigned type; +}; + +#define MULTIBOOT_MEM_VALID 0x01 +#define MULTIBOOT_BOOT_DEV_VALID 0x02 +#define MULTIBOOT_CMDLINE_VALID 0x04 +#define MULTIBOOT_MODS_VALID 0x08 +#define MULTIBOOT_AOUT_SYMS_VALID 0x10 +#define MULTIBOOT_ELF_SYMS_VALID 0x20 +#define MULTIBOOT_MMAP_VALID 0x40 + +void collect_multiboot_info(struct sys_info *info); +void collect_multiboot_info(struct sys_info *info) +{ + struct multiboot_info *mbinfo; + struct multiboot_mmap *mbmem; + unsigned mbcount, mbaddr; + int i; + struct memrange *mmap; + int mmap_count; + module_t *mod; + + if (info->boot_type != 0x2BADB002) + return; + + debug("Using Multiboot information at %#lx\n", info->boot_data); + + mbinfo = phys_to_virt(info->boot_data); + + if (mbinfo->mods_count != 1) { + printf("Multiboot: no dictionary\n"); + return; + } + + mod = (module_t *) mbinfo->mods_addr; + info->dict_start=(unsigned long *)mod->mod_start; + info->dict_end=(unsigned long *)mod->mod_end; + + if (mbinfo->flags & MULTIBOOT_MMAP_VALID) { + /* convert mmap records */ + mbmem = phys_to_virt(mbinfo->mmap_addr); + mbcount = mbinfo->mmap_length / (mbmem->entry_size + 4); + mmap = malloc(mbcount * sizeof(struct memrange)); + mmap_count = 0; + mbaddr = mbinfo->mmap_addr; + for (i = 0; i < mbcount; i++) { + mbmem = phys_to_virt(mbaddr); + debug("%08x%08x %08x%08x (%d)\n", + mbmem->base_hi, + mbmem->base_lo, + mbmem->size_hi, + mbmem->size_lo, + mbmem->type); + if (mbmem->type == 1) { /* Only normal RAM */ + mmap[mmap_count].base = mbmem->base_lo + + (((unsigned long long) mbmem->base_hi) << 32); + mmap[mmap_count].size = mbmem->size_lo + + (((unsigned long long) mbmem->size_hi) << 32); + mmap_count++; + } + mbaddr += mbmem->entry_size + 4; + if (mbaddr >= mbinfo->mmap_addr + mbinfo->mmap_length) + break; + } + /* simple sanity check - there should be at least 2 RAM segments + * (base 640k and extended) */ + if (mmap_count >= 2) + goto got_it; + + printf("Multiboot mmap is broken\n"); + free(mmap); + /* fall back to mem_lower/mem_upper */ + } + + if (mbinfo->flags & MULTIBOOT_MEM_VALID) { + /* use mem_lower and mem_upper */ + mmap_count = 2; + mmap = malloc(2 * sizeof(*mmap)); + mmap[0].base = 0; + mmap[0].size = mbinfo->mem_lower << 10; + mmap[1].base = 1 << 20; /* 1MB */ + mmap[1].size = mbinfo->mem_upper << 10; + goto got_it; + } + + printf("Can't get memory information from Multiboot\n"); + return; + +got_it: + info->memrange = mmap; + info->n_memranges = mmap_count; + + return; +} diff --git a/qemu/roms/openbios/arch/amd64/multiboot.h b/qemu/roms/openbios/arch/amd64/multiboot.h new file mode 100644 index 000000000..17cf202ec --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/multiboot.h @@ -0,0 +1,96 @@ +/* multiboot.h + * tag: header for multiboot + * + * Copyright (C) 2003-2004 Stefan Reinauer + * + * See the file "COPYING" for further information about + * the copyright and warranty status of this work. + */ + +/* magic number for multiboot header */ +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002 + +/* flags for multiboot header */ +#define MULTIBOOT_HEADER_FLAGS 0x00010003 + +/* magic number passed by multiboot-compliant boot loader. */ +#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 + +/* The size of our stack (8KB). */ +#define STACK_SIZE 0x2000 + +/* C symbol format. HAVE_ASM_USCORE is defined by configure. */ +#ifdef HAVE_ASM_USCORE +# define EXT_C(sym) _ ## sym +#else +# define EXT_C(sym) sym +#endif + +#ifndef ASM +/* We don't want these declarations in boot.S */ + +/* multiboot header */ +typedef struct multiboot_header { + unsigned long magic; + unsigned long flags; + unsigned long checksum; + unsigned long header_addr; + unsigned long load_addr; + unsigned long load_end_addr; + unsigned long bss_end_addr; + unsigned long entry_addr; +} multiboot_header_t; + +/* symbol table for a.out */ +typedef struct aout_symbol_table { + unsigned long tabsize; + unsigned long strsize; + unsigned long addr; + unsigned long reserved; +} aout_symbol_table_t; + +/* section header table for ELF */ +typedef struct elf_section_header_table { + unsigned long num; + unsigned long size; + unsigned long addr; + unsigned long shndx; +} elf_section_header_table_t; + +/* multiboot information */ +typedef struct multiboot_info { + unsigned long flags; + unsigned long mem_lower; + unsigned long mem_upper; + unsigned long boot_device; + unsigned long cmdline; + unsigned long mods_count; + unsigned long mods_addr; + union { + aout_symbol_table_t aout_sym; + elf_section_header_table_t elf_sec; + } u; + unsigned long mmap_length; + unsigned long mmap_addr; +} multiboot_info_t; + +/* module structure */ +typedef struct module { + unsigned long mod_start; + unsigned long mod_end; + unsigned long string; + unsigned long reserved; +} module_t; + +/* memory map. Be careful that the offset 0 is base_addr_low + but no size. */ +typedef struct memory_map { + unsigned long size; + unsigned long base_addr_low; + unsigned long base_addr_high; + unsigned long length_low; + unsigned long length_high; + unsigned long type; +} memory_map_t; + +#endif /* ! ASM */ diff --git a/qemu/roms/openbios/arch/amd64/openbios.c b/qemu/roms/openbios/arch/amd64/openbios.c new file mode 100644 index 000000000..15d3b6254 --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/openbios.c @@ -0,0 +1,108 @@ +/* tag: openbios forth environment, executable code + * + * Copyright (C) 2003 Patrick Mauritz, Stefan Reinauer + * + * See the file "COPYING" for further information about + * the copyright and warranty status of this work. + */ + +#include "config.h" +#include "libopenbios/openbios.h" +#include "libopenbios/bindings.h" +#include "asm/types.h" +#include "dict.h" +#include "kernel/kernel.h" +#include "kernel/stack.h" +#include "libopenbios/sys_info.h" +#include "openbios.h" +#include "relocate.h" + +void boot(void); + +#define DICTIONARY_SIZE (256*1024) /* 256K for the dictionary */ +static char intdict[DICTIONARY_SIZE]; + +static void init_memory(void) +{ + /* push start and end of available memory to the stack + * so that the forth word QUIT can initialize memory + * management. For now we use hardcoded memory between + * 0x10000 and 0x9ffff (576k). If we need more memory + * than that we have serious bloat. + */ + + PUSH(0x10000); + PUSH(0x9FFFF); +} + +static void +arch_init( void ) +{ + void setup_timers(void); + + openbios_init(); + modules_init(); +#ifdef CONFIG_DRIVER_IDE + setup_timers(); + ob_ide_init("/pci/pci-ata", 0x1f0, 0x3f6, 0x170, 0x376); +#endif + device_end(); + bind_func("platform-boot", boot ); +} + +extern struct _console_ops arch_console_ops; + +int openbios(void) +{ +#ifdef CONFIG_DEBUG_CONSOLE + init_console(arch_console_ops); +#ifdef CONFIG_DEBUG_CONSOLE_SERIAL + uart_init(CONFIG_SERIAL_PORT, CONFIG_SERIAL_SPEED); +#endif + /* Clear the screen. */ + cls(); +#endif + + collect_sys_info(&sys_info); + + dict=intdict; + dictlimit = DICTIONARY_SIZE; + + load_dictionary((char *)sys_info.dict_start, + sys_info.dict_end-sys_info.dict_start); + forth_init(); + + relocate(&sys_info); + +#ifdef CONFIG_DEBUG_CONSOLE + video_init(); +#endif +#ifdef CONFIG_DEBUG_BOOT + printk("forth started.\n"); + printk("initializing memory..."); +#endif + + init_memory(); + +#ifdef CONFIG_DEBUG_BOOT + printk("done\n"); +#endif + + PUSH_xt( bind_noname_func(arch_init) ); + fword("PREPOST-initializer"); + + PC = (ucell)findword("initialize-of"); + + if (!PC) { + printk("panic: no dictionary entry point.\n"); + return -1; + } +#ifdef CONFIG_DEBUG_DICTIONARY + printk("done (%d bytes).\n", dicthead); + printk("Jumping to dictionary...\n"); +#endif + + enterforth((xt_t)PC); + + return 0; +} diff --git a/qemu/roms/openbios/arch/amd64/openbios.h b/qemu/roms/openbios/arch/amd64/openbios.h new file mode 100644 index 000000000..2d49dbf54 --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/openbios.h @@ -0,0 +1,29 @@ +/* + * Creation Date: <2004/01/15 16:14:05 samuel> + * Time-stamp: <2004/01/15 16:14:05 samuel> + * + * <openbios.h> + * + * + * + * Copyright (C) 2004 Samuel Rydh (samuel@ibrium.se) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 + * + */ + +#ifndef _H_OPENBIOS +#define _H_OPENBIOS + +int openbios(void); + +/* console.c */ +extern void cls(void); +#ifdef CONFIG_DEBUG_CONSOLE +extern int uart_init(int port, unsigned long speed); +extern void video_init(void); +#endif + +#endif /* _H_OPENBIOS */ diff --git a/qemu/roms/openbios/arch/amd64/plainboot.c b/qemu/roms/openbios/arch/amd64/plainboot.c new file mode 100644 index 000000000..08dab2d12 --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/plainboot.c @@ -0,0 +1,21 @@ +/* tag: openbios fixed address forth starter + * + * Copyright (C) 2003 Stefan Reinauer + * + * See the file "COPYING" for further information about + * the copyright and warranty status of this work. + */ + +#include "config.h" +#include "libopenbios/sys_info.h" +#include "multiboot.h" + +#define FIXED_DICTSTART 0xfffe0000 +#define FIXED_DICTEND 0xfffeffff + +void collect_multiboot_info(struct sys_info *info); +void collect_multiboot_info(struct sys_info *info) +{ + info->dict_start=(unsigned long *)FIXED_DICTSTART; + info->dict_end=(unsigned long *)FIXED_DICTEND; +} diff --git a/qemu/roms/openbios/arch/amd64/relocate.h b/qemu/roms/openbios/arch/amd64/relocate.h new file mode 100644 index 000000000..d91160a03 --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/relocate.h @@ -0,0 +1 @@ +void relocate(struct sys_info *); diff --git a/qemu/roms/openbios/arch/amd64/segment.c b/qemu/roms/openbios/arch/amd64/segment.c new file mode 100644 index 000000000..09763bd14 --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/segment.c @@ -0,0 +1,134 @@ +/* Segmentation of the AMD64 architecture. + * + * 2003-07 by SONE Takeshi + */ + +#include "config.h" +#include "kernel/kernel.h" +#include "libopenbios/sys_info.h" +#include "relocate.h" +#include "segment.h" + +#define printf printk +#ifdef CONFIG_DEBUG_BOOT +#define debug printk +#else +#define debug(x...) +#endif + +/* i386 lgdt argument */ +struct gdtarg { + unsigned short limit; + unsigned int base; +} __attribute__((packed)); + +/* How far the virtual address (used in C) is different from physical + * address. Since we start in flat mode, the initial value is zero. */ +unsigned long virt_offset = 0; + +/* GDT, the global descriptor table */ +struct segment_desc gdt[NUM_SEG] = { + /* 0x00: null segment */ + {0, 0, 0, 0, 0, 0}, + /* 0x08: flat code segment */ + {0xffff, 0, 0, 0x9f, 0xcf, 0}, + /* 0x10: flat data segment */ + {0xffff, 0, 0, 0x93, 0xcf, 0}, + /* 0x18: code segment for relocated execution */ + {0xffff, 0, 0, 0x9f, 0xcf, 0}, + /* 0x20: data segment for relocated execution */ + {0xffff, 0, 0, 0x93, 0xcf, 0}, +}; + +extern char _start[], _end[]; + +void relocate(struct sys_info *info) +{ + int i; + unsigned long prog_addr; + unsigned long prog_size; + unsigned long addr, new_base; + unsigned long long segsize; + unsigned long new_offset; + unsigned d0, d1, d2; + struct gdtarg gdtarg; +#define ALIGNMENT 16 + + prog_addr = virt_to_phys(&_start); + prog_size = virt_to_phys(&_end) - virt_to_phys(&_start); + debug("Current location: %#lx-%#lx\n", prog_addr, prog_addr+prog_size-1); + + new_base = 0; + for (i = 0; i < info->n_memranges; i++) { + if (info->memrange[i].base >= 1ULL<<32) + continue; + segsize = info->memrange[i].size; + if (info->memrange[i].base + segsize > 1ULL<<32) + segsize = (1ULL<<32) - info->memrange[i].base; + if (segsize < prog_size+ALIGNMENT) + continue; + addr = info->memrange[i].base + segsize - prog_size; + addr &= ~(ALIGNMENT-1); + if (addr >= prog_addr && addr < prog_addr + prog_size) + continue; + if (prog_addr >= addr && prog_addr < addr + prog_size) + continue; + if (addr > new_base) + new_base = addr; + } + if (new_base == 0) { + printf("Can't find address to relocate\n"); + return; + } + + debug("Relocating to %#lx-%#lx... ", + new_base, new_base + prog_size - 1); + + /* New virtual address offset */ + new_offset = new_base - (unsigned long) &_start; + + /* Tweak the GDT */ + gdt[RELOC_CODE].base_0 = (unsigned short) new_offset; + gdt[RELOC_CODE].base_16 = (unsigned char) (new_offset>>16); + gdt[RELOC_CODE].base_24 = (unsigned char) (new_offset>>24); + gdt[RELOC_DATA].base_0 = (unsigned short) new_offset; + gdt[RELOC_DATA].base_16 = (unsigned char) (new_offset>>16); + gdt[RELOC_DATA].base_24 = (unsigned char) (new_offset>>24); + + /* Load new GDT and reload segments */ + gdtarg.base = new_offset + (unsigned long) gdt; + gdtarg.limit = GDT_LIMIT; + __asm__ __volatile__ ( + "rep; movsb\n\t" /* copy everything */ + "lgdt %3\n\t" + "ljmp %4, $1f\n1:\t" + "movw %5, %%ds\n\t" + "movw %5, %%es\n\t" + "movw %5, %%fs\n\t" + "movw %5, %%gs\n\t" + "movw %5, %%ss\n" + : "=&S" (d0), "=&D" (d1), "=&c" (d2) + : "m" (gdtarg), "n" (RELOC_CS), "q" ((unsigned short) RELOC_DS), + "0" (&_start), "1" (new_base), "2" (prog_size)); + + virt_offset = new_offset; + debug("ok\n"); +} + +#if 0 +/* Copy GDT to new location and reload it */ +void move_gdt(unsigned long newgdt) +{ + struct gdtarg gdtarg; + + debug("Moving GDT to %#lx...", newgdt); + memcpy(phys_to_virt(newgdt), gdt, sizeof gdt); + gdtarg.base = newgdt; + gdtarg.limit = GDT_LIMIT; + debug("reloading GDT..."); + __asm__ __volatile__ ("lgdt %0\n\t" : : "m" (gdtarg)); + debug("reloading CS for fun..."); + __asm__ __volatile__ ("ljmp %0, $1f\n1:" : : "n" (RELOC_CS)); + debug("ok\n"); +} +#endif diff --git a/qemu/roms/openbios/arch/amd64/segment.h b/qemu/roms/openbios/arch/amd64/segment.h new file mode 100644 index 000000000..0371a80ae --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/segment.h @@ -0,0 +1,30 @@ + +/* Segment indexes. Must match the gdt definition in segment.c. */ +enum { + NULL_SEG, + FLAT_CODE, + FLAT_DATA, + RELOC_CODE, + RELOC_DATA, + NUM_SEG, +}; + +/* Values for segment selector register */ +#define FLAT_CS (FLAT_CODE << 3) +#define FLAT_DS (FLAT_DATA << 3) +#define RELOC_CS (RELOC_CODE << 3) +#define RELOC_DS (RELOC_DATA << 3) + +/* i386 segment descriptor */ +struct segment_desc { + unsigned short limit_0; + unsigned short base_0; + unsigned char base_16; + unsigned char types; + unsigned char flags; + unsigned char base_24; +}; + +extern struct segment_desc gdt[NUM_SEG]; + +#define GDT_LIMIT ((NUM_SEG << 3) - 1) diff --git a/qemu/roms/openbios/arch/amd64/switch.S b/qemu/roms/openbios/arch/amd64/switch.S new file mode 100644 index 000000000..66668150d --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/switch.S @@ -0,0 +1,116 @@ + .globl entry, __switch_context, __exit_context, halt + + .text + .align 4 + +/* + * Entry point + * We start execution from here. + * It is assumed that CPU is in 32-bit protected mode and + * all segments are 4GB and base zero (flat model). + */ +entry: + /* Save boot context and switch to our main context. + * Main context is statically defined in C. + */ + pushl %cs + call __switch_context + + /* We get here when the main context switches back to + * the boot context. + * Return to previous bootloader. + */ + ret + +/* + * Switch execution context + * This saves registers, segments, and GDT in the stack, then + * switches the stack, and restores everything from the new stack. + * This function takes no argument. New stack pointer is + * taken from global variable __context, and old stack pointer + * is also saved to __context. This way we can just jump to + * this routine to get back to the original context. + * + * Call this routine with lcall or pushl %cs; call. + */ +__switch_context: + /* Save everything in current stack */ + pushfl /* 56 */ + pushl %ds /* 52 */ + pushl %es /* 48 */ + pushl %fs /* 44 */ + pushl %gs /* 40 */ + pushal /* 8 */ + subl $8, %esp + movw %ss, (%esp) /* 0 */ + sgdt 2(%esp) /* 2 */ + +#if 0 + /* Swap %cs and %eip on the stack, so lret will work */ + movl 60(%esp), %eax + xchgl %eax, 64(%esp) + movl %eax, 60(%esp) +#endif + + /* At this point we don't know if we are on flat segment + * or relocated. So compute the address offset from %eip. + * Assuming CS.base==DS.base==SS.base. + */ + call 1f +1: popl %ebx + subl $1b, %ebx + + /* Interrupts are not allowed... */ + cli + + /* Current context pointer is our stack pointer */ + movl %esp, %esi + + /* Normalize the ctx pointer */ + subl %ebx, %esi + + /* Swap it with new value */ + xchgl %esi, __context(%ebx) + + /* Adjust new ctx pointer for current address offset */ + addl %ebx, %esi + + /* Load new %ss and %esp to temporary */ + movzwl (%esi), %edx + movl 20(%esi), %eax + + /* Load new GDT */ + lgdt 2(%esi) + + /* Load new stack segment with new GDT */ + movl %edx, %ss + + /* Set new stack pointer, but we have to adjust it because + * pushal saves %esp value before pushal, and we want the value + * after pushal. + */ + leal -32(%eax), %esp + + /* Load the rest from new stack */ + popal + popl %gs + popl %fs + popl %es + popl %ds + popfl + + /* Finally, load new %cs and %eip */ + lret + +__exit_context: + /* Get back to the original context */ + pushl %cs + call __switch_context + + /* We get here if the other context attempt to switch to this + * dead context. This should not happen. */ + +halt: + cli + hlt + jmp halt diff --git a/qemu/roms/openbios/arch/amd64/sys_info.c b/qemu/roms/openbios/arch/amd64/sys_info.c new file mode 100644 index 000000000..d62070be0 --- /dev/null +++ b/qemu/roms/openbios/arch/amd64/sys_info.c @@ -0,0 +1,58 @@ +#include "config.h" +#include "kernel/kernel.h" +#include "libopenbios/sys_info.h" +#include "context.h" + +#define printf printk +#ifdef CONFIG_DEBUG_BOOT +#define debug printk +#else +#define debug(x...) +#endif + +void collect_multiboot_info(struct sys_info *); +void collect_sys_info(struct sys_info *info); + +void collect_sys_info(struct sys_info *info) +{ + int i; + unsigned long long total = 0; + struct memrange *mmap; + + /* Pick up paramters given by bootloader to us */ + info->boot_type = boot_ctx->eax; + info->boot_data = boot_ctx->ebx; + info->boot_arg = boot_ctx->param[0]; + debug("boot eax = %#lx\n", info->boot_type); + debug("boot ebx = %#lx\n", info->boot_data); + debug("boot arg = %#lx\n", info->boot_arg); + + collect_elfboot_info(info); +#ifdef CONFIG_LINUXBIOS + collect_linuxbios_info(info); +#endif +#ifdef CONFIG_IMAGE_ELF_MULTIBOOT + collect_multiboot_info(info); +#endif + + if (!info->memrange) { + printf("Can't get memory map from firmware. " + "Using hardcoded default.\n"); + info->n_memranges = 2; + info->memrange = malloc(2 * sizeof(struct memrange)); + info->memrange[0].base = 0; + info->memrange[0].size = 640*1024; + info->memrange[1].base = 1024*1024; + info->memrange[1].size = 32*1024*1024 + - info->memrange[1].base; + } + + debug("\n"); + mmap=info->memrange; + for (i = 0; i < info->n_memranges; i++) { + debug("%016Lx-", mmap[i].base); + debug("%016Lx\n", mmap[i].base+mmap[i].size); + total += mmap[i].size; + } + debug("RAM %Ld MB\n", (total + 512*1024) >> 20); +} |