diff options
Diffstat (limited to 'qemu/roms/openhackware/src/main.c')
-rw-r--r-- | qemu/roms/openhackware/src/main.c | 564 |
1 files changed, 564 insertions, 0 deletions
diff --git a/qemu/roms/openhackware/src/main.c b/qemu/roms/openhackware/src/main.c new file mode 100644 index 000000000..2d61d64ff --- /dev/null +++ b/qemu/roms/openhackware/src/main.c @@ -0,0 +1,564 @@ +/* + * Open Hack'Ware BIOS main. + * + * Copyright (c) 2004-2005 Jocelyn Mayer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License V2 + * as published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Status: + * - boots Linux 2.4 from floppy and IDE + * - preliminary residual data support + * - can find PREP boot images and Apple boot blocs + * + * TODO: + * 1/ Cleanify boot partitions: + * allow more than one boot bloc + fix PREP load again + * 2/ add ATAPI driver + * 3/ add a prompt to let the user choose its boot device / PREP image + * 4/ add POST + * 5/ add VGA driver (SVGA/VESA ?) + * 6/ add a user accessible setup + * 7/ add netboot + */ + +#include <stdlib.h> +#include <stdio.h> +#include "bios.h" + +#include "char.h" + +//#define DEBUG_MEMORY 1 + +/* Version string */ +const unsigned char *BIOS_str = +"PPC Open Hack'Ware BIOS for qemu version " BIOS_VERSION "\n"; +const unsigned char *copyright = "Copyright 2003-2005 Jocelyn Mayer\n"; + +uint32_t isa_io_base = ISA_IO_BASE; + +/* Temporary hack: boots only from floppy */ +int boot_device = 'a'; + +/* Some other PPC helper */ +/* Setup a memory mapping, using BAT0 + * BATU: + * BEPI : bloc virtual address + * BL : area size bits (128 kB is 0, 256 1, 512 3, ... + * Vs/Vp + * BATL: + * BPRN : bloc real address align on 4MB boundary + * WIMG : cache access mode : not used + * PP : protection bits + */ +static void BAT_setup (int nr, uint32_t virtual, uint32_t physical, + uint32_t size, int Vs, int Vp, int PP) +{ + uint32_t sz_bits, tmp_sz, align, tmp; + + sz_bits = 0; + align = 131072; + DPRINTF("Set BAT %d v=%0x p=%0x size=%0x\n", nr, virtual, physical, size); + if (size < 131072) + size = 131072; + for (tmp_sz = size / 131072; tmp_sz != 1; tmp_sz = tmp_sz >> 1) { + sz_bits = (sz_bits << 1) + 1; + align = align << 1; + } + tmp = virtual & ~(align - 1); /* Align virtual area start */ + tmp |= sz_bits << 2; /* Fix BAT size */ + tmp |= Vs << 1; /* Supervisor access */ + tmp |= Vp; /* User access */ + DPRINTF("Set BATU%d to %0x\n", nr, tmp); + switch (nr) { + case 0: + /* Setup IBAT0U */ + MTSPR(528, tmp); + /* Setup DBAT0U */ + MTSPR(536, tmp); + break; + case 1: + /* Setup DBAT1U */ + MTSPR(538, tmp); + break; + case 2: + /* Setup DBAT2U */ + MTSPR(540, tmp); + break; + } + tmp = physical & ~(align - 1); /* Align physical area start */ + tmp |= 0; /* Don't care about WIMG */ + tmp |= PP; /* Protection */ + DPRINTF("Set BATL%d to %0x\n", nr, tmp); + switch (nr) { + case 0: + /* Setup IBAT0L */ + MTSPR(529, tmp); + /* Setup DBAT0L */ + MTSPR(537, tmp); + break; + case 1: + /* Setup DBAT1L */ + MTSPR(539, tmp); + break; + case 2: + /* Setup DBAT2L */ + MTSPR(541, tmp); + break; + } +} + +typedef struct PPC_CPU_t { + uint32_t pvr; + uint32_t mask; + unsigned char *name; +} PPC_CPU_t; + +static const PPC_CPU_t CPU_PPC[] = { + /* For now, know only G3 */ + { + 0x00080000, + 0xFFFF0000, + "PowerPC,G3", + }, + { + 0x00000000, + 0x00000000, + "PowerPC,generic", + }, +}; + +static const unsigned char *CPU_get_name (uint32_t pvr) +{ + int i; + + for (i = 0;; i++) { + if ((pvr & CPU_PPC[i].mask) == CPU_PPC[i].pvr) + return CPU_PPC[i].name; + } + + return NULL; +} + +#define TB_FREQ (10 * 1000 * 1000) // XXX: should calibrate +void usleep (uint32_t usec) +{ +#if 0 // Buggy: makes OpenDarwin crash (!) + uint32_t tb0[2], tb1[2], count[2]; + uint32_t tpu; + int wrap = 0; + + tpu = TB_FREQ / (1000 * 1000); + mul64(count, usec, tpu); + mftb(tb0); + add64(count, count, tb0); + if (count[0] < tb0[0]) + wrap = 1; + while (1) { + mftb(tb1); + if (wrap == 1 && tb1[0] < tb0[0]) + wrap = 0; + if (wrap == 0 && + (tb1[0] > count[0] || + (tb1[0] == count[0] && tb1[1] >= count[1]))) + break; + tb0[0] = tb1[0]; + } +#else + uint32_t i, j; + + for (i = 0; i < (usec >> 16) * 50; i++) { + for (j = 0; j < (usec & 0xFFFF) * 50; j++) { + continue; + } + } +#endif +} + +void sleep (int sec) +{ + int i; + + for (i = 0; i < sec; i++) + usleep(1 * 1000 * 1000); +} + +/* Stolen from Linux code */ +#define CRCPOLY_LE 0xedb88320 + +uint32_t crc32 (uint32_t crc, const uint8_t *p, int len) +{ + int i; + + while (len--) { + crc ^= *p++; + for (i = 0; i < 8; i++) + crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); + } + + return crc; +} + +/* Fake write */ +int write (unused int fd, unused const char *buf, int len) +{ + return len; +} + +/* BIOS library functions */ +/* Fake memory management (to be removed) */ +void *mem_align (unused int align) +{ + return malloc(0); +} +#if 1 +void free (unused void *p) +{ +} +#endif + +void freep (void *p) +{ + void **_p = p; + + free(*_p); + *_p = NULL; +} + +static inline int in_area (const void *buf, + const void *start, const void *end) +{ + return buf >= start && buf <= end; +} + +#ifdef DEBUG_MEMORY +static void *load_dest, *load_end; +static int relax_check; +#endif + +void set_loadinfo (unused void *load_base, unused uint32_t size) +{ +#ifdef DEBUG_MEMORY + load_dest = load_base; + load_end = (char *)load_dest + size; +#endif +} + +void set_check (unused int do_it) +{ +#ifdef DEBUG_MEMORY + relax_check = do_it == 0 ? 1 : 0; +#endif +} + +void check_location (unused const void *buf, + unused const char *func, + unused const char *name) +{ +#ifdef DEBUG_MEMORY + if (relax_check != 0) + return; + if (!in_area(buf, &_data_start, &_data_end) && + !in_area(buf, &_OF_vars_start, &_OF_vars_end) && + !in_area(buf, &_sdata_start, &_sdata_end) && + !in_area(buf, &_ro_start, &_ro_end) && + !in_area(buf, &_RTAS_data_start, &_RTAS_data_end) && + !in_area(buf, &_bss_start, &_bss_end) && + !in_area(buf, &_ram_start, malloc_base) && + /* Let's say 64 kB of stack is enough */ + !in_area(buf, (void *)0x5ff0000, (void *)0x6000000) && + !in_area(buf, load_dest, load_end) && + /* IO area */ + !in_area(buf, (void *)0x80000000, (void *)0x88000000)) { + printf("**************************************************************" + "**************\n"); + printf("%s: %s: %p\n", func, name, buf); + printf(" data: %p %p\n", &_data_start, &_data_end); + printf(" OF_vars: %p %p\n", &_OF_vars_start, &_OF_vars_end); + printf(" sdata: %p %p\n", &_sdata_start, &_sdata_end); + printf(" rodata: %p %p\n", &_ro_start, &_ro_end); + printf(" RTAS_data: %p %p\n", &_RTAS_data_start, &_RTAS_data_end); + printf(" bss: %p %p\n", &_bss_start, &_bss_end); + printf(" mallocated: %p %p\n", &_ram_start, malloc_base); + printf(" stack : %p %p\n", (void *)0x5ff0000, + (void *)0x6000000); + printf(" load image: %p %p\n", load_dest, load_end); + printf("**************************************************************" + "**************\n"); + bug(); + } +#endif +} + +/* No overflow check here... */ +long strtol (const unsigned char *str, unsigned char **end, int base) +{ + long ret = 0, tmp, sign = 1; + + check_location(str, __func__, "str"); + if (base < 0 || base > 36) + return 0; + for (; *str == ' '; str++) + continue; + if (*str == '-') { + sign = -1; + str++; + } + for (;; str++) { + tmp = *str; + if (tmp < '0') + break; + if (base <= 10) { + if (tmp > '0' + base - 1) + break; + tmp -= '0'; + } else { + if (tmp <= '9') { + tmp -= '0'; + } else { + tmp &= ~0x20; + if (tmp < 'A' || tmp > 'A' + base - 11) + break; + tmp += 10 - 'A'; + } + } + ret = (ret * base) + tmp; + } + if (sign == -1) + ret = -ret; + if (end != NULL) + *end = (unsigned char *)str; + + return ret; +} + +nvram_t *nvram; +int arch; +/* HACK... */ +int vga_width, vga_height, vga_depth; + +part_t *boot_part; + +/* XXX: to fix */ +void mm_init (uint32_t memsize); +int page_descrs_init (void); + +int main (void) +{ + bloc_device_t *bd; + pci_host_t *pci_main; + void *res, *bootinfos; + void *boot_image, *cmdline, *ramdisk; + void *load_base, *load_entry, *last_alloc, *load_end; + uint32_t memsize, boot_image_size, cmdline_size, ramdisk_size; + uint32_t boot_nb; + int boot_device, i; + static const uint32_t isa_base_tab[3] = { + 0x80000000, /* PREP */ + 0xFE000000, /* Grackle (Heathrow) */ + 0xF2000000, /* UniNorth (Mac99) */ + }; + + /* Retrieve NVRAM configuration */ + for(i = 0; i < 3; i++) { + isa_io_base = isa_base_tab[i]; + nvram = NVRAM_get_config(&memsize, &boot_device, + &boot_image, &boot_image_size, + &cmdline, &cmdline_size, + &ramdisk, &ramdisk_size); + if (nvram) + break; + } + if (i == 3) { + ERROR("Unable to load configuration from NVRAM. Aborting...\n"); + return -1; + } +#if 1 + mm_init(memsize); +#endif +#if 1 + page_descrs_init(); +#endif +#ifdef USE_OPENFIRMWARE + OF_init(); +#endif + pci_main = pci_init(); + if (pci_main == NULL) + ERROR("Unable to configure PCI\n"); +#ifdef USE_OPENFIRMWARE + /* XXX: this mess needs a serious cleanup... */ + { + const unsigned char *cpu_name; + uint32_t pvr = mfpvr(); + + cpu_name = CPU_get_name(pvr); + OF_register_cpu(cpu_name, 0, pvr, + 200 * 1000 * 1000, 200 * 1000 * 1000, + 100 * 1000 * 1000, 100 * 1000 * 1000, + 0x0092); + } + OF_register_memory(memsize, 512 * 1024 /* TOFIX */); + /* Claim memory used by the BIOS */ + OF_claim_virt(0x05800000, 0x00080000, NULL); + OF_register_bootargs(cmdline); +#endif + if (isa_io_base == 0x80000000 || 1) { + pc_serial_register(0x3F8); +#ifdef USE_OPENFIRMWARE + OF_register_bus("isa", isa_io_base, "ISA"); + OF_register_serial("isa", "com1", 0x3F8, 4); + OF_register_stdio("com1", "com1"); +#endif + } +#ifdef USE_OPENFIRMWARE + RTAS_init(); +#endif + /* Get a console */ + console_open(); + printf("%s", BIOS_str); + printf("Build " stringify(BUILD_DATE) " " stringify(BUILD_TIME) "\n"); + printf("%s\n", copyright); + printf("Memory size: %d MB. \nBooting from device %c\n", + memsize >> 20, boot_device); + vga_puts(BIOS_str); + vga_puts("Build " stringify(BUILD_DATE) " " stringify(BUILD_TIME) "\n"); + vga_puts(copyright); + vga_puts("\n"); + +#if 0 + /* QEMU is quite incoherent: d is cdrom, not second drive */ + /* XXX: should probe CD-ROM position */ + if (boot_device == 'd') + boot_device = 'e'; +#endif + /* Open boot device */ + boot_part = bd_probe(boot_device); + if (boot_device == 'm') { + bd = bd_get('m'); + if (bd == NULL) { + ERROR("Unable to get memory bloc device\n"); + return -1; + } + printf("boot device: %p image %p size %d\n", + bd, boot_image, boot_image_size); + bd_ioctl(bd, MEM_SET_ADDR, boot_image); + bd_ioctl(bd, MEM_SET_SIZE, &boot_image_size); + boot_part = part_probe(bd, 1); + bd_put(bd); + printf("boot device: %p\n", bd); + } + if (boot_part == NULL) { + ERROR("Found no boot partition!\n"); + return -1; + } + ERROR("Found boot partition : %p %p\n", boot_part, part_fs(boot_part)); + mem_align(0x00001000); + res = malloc(0x4000); + last_alloc = malloc(0); + boot_nb = 0; + DPRINTF("Load base: %p - residual data: %p %p %p %p\n", + load_base, res, last_alloc, boot_part, part_fs(boot_part)); + /* Load the whole boot image */ + if (bootfile_load((void *)&load_base, (void *)&load_entry, + (void *)&load_end, boot_part, -1, NULL, 0) < 0) { + printf("Unable to load boot file\n"); + return -1; + } +#ifdef USE_OPENFIRMWARE + DPRINTF("Boot parameters: res: %p load_base: %p OF: %p entry: %p\n", + res, load_base, &OF_entry, load_entry); +#else + DPRINTF("Boot parameters: res: %p load_base: %p OF: %p entry: %p\n", + res, load_base, NULL, load_entry); +#endif + DPRINTF("Boot: %0x %0x %0x %0x\n", *(uint32_t *)load_entry, + *((uint32_t *)load_entry + 1), + *((uint32_t *)load_entry + 2), + *((uint32_t *)load_entry + 3)); + /* Fill residual data structure */ + residual_build(res, memsize, (uint32_t)load_base, + (uint32_t)boot_nb * part_blocsize(boot_part), + (uint32_t)last_alloc); + /* Fill bootinfos */ + bootinfos = (void *)(((uint32_t)load_end + (1 << 20) - 1) & ~((1 << 20) - 1)); + if (boot_image != NULL && cmdline == NULL) { + cmdline = bootinfos + 0x1000; + *(char *)cmdline = '\0'; + } + set_check(0); + prepare_bootinfos(bootinfos, memsize, cmdline, ramdisk, ramdisk_size); + set_check(1); + if (part_flags(boot_part) & PART_PREP) { + vga_prep_init(); + } + /* Format NVRAM */ + NVRAM_format(nvram); + /* Setup MMU and boot the loaded image */ + DPRINTF("\nSet up MMU context\n"); + /* Map all memory with transparent mapping */ + BAT_setup(0, 0x00000000, 0x00000000, memsize, 1, 0, 2); + /* Open IO ports */ + BAT_setup(1, isa_io_base, isa_io_base, 0x00800000, 1, 1, 2); +#if 0 + /* Open the frame-buffer area */ + BAT_setup(2, vga_fb_phys_addr, vga_fb_phys_addr, 0x00200000, 1, 1, 2); +#else + if (pci_main != NULL) { + uint32_t mem_start, mem_size; + pci_get_mem_range(pci_main, &mem_start, &mem_size); + BAT_setup(2, mem_start, mem_start, mem_size, 1, 1, 2); + } +#endif + /* Enable MMU */ + MMU_on(); + usleep(500); + dprintf("Boot: %08x %08x %08x %08x\n", *(uint32_t *)load_base, + *(uint32_t *)(load_base + 4), *(uint32_t *)(load_base + 8), + *(uint32_t *)(load_base + 12)); + dprintf("Bootinfos at : %p\n", bootinfos); + printf("\nNow boot it... (%p)\n\n", malloc(0)); + usleep(500); + { + register uint32_t r1 __asm__ ("r1"); + printf("stack: %0x malloc_base: %p 0x05800000 0x06000000\n", + r1, malloc(0)); + } + + if (part_flags(boot_part) & PART_PREP) { + printf("PREP boot... %p %p\n", load_entry, load_base); + /* Hack for Linux to boot without OpenFirmware */ + put_be32(load_base, 0xdeadc0de); + } + transfer_handler(res /* residual data */, + load_base /* load address */, +#ifdef USE_OPENFIRMWARE + &OF_entry /* OF entry point */, +#else + NULL, +#endif + bootinfos /* bootinfos for Linux */, + cmdline /* command line for Linux */, + NULL /* unused for now */, + load_entry /* start address */, +#if 0 + mem_align(0x00100000) /* Stack base */ +#else + (void *)0x05800000 +#endif + ); + /* Should never come here */ + + return 0; +} |