Files
openbios/arch/ppc/qemu/main.c
Laurent Vivier 3888c71730 Add quik support (oldworld bootloader).
Load two first sectors of the partition and check quik signature ('QUIK').
If it is quik, execute it to boot.

Currently partition and parameter are hardcoded to "hd:2" and "Linux".



git-svn-id: svn://coreboot.org/openbios/openbios-devel@385 f158a5a8-5612-0410-a976-696ce0be7e32
2009-01-11 00:19:02 +00:00

395 lines
10 KiB
C

/*
* Creation Date: <2002/10/02 22:24:24 samuel>
* Time-stamp: <2004/03/27 01:57:55 samuel>
*
* <main.c>
*
*
*
* Copyright (C) 2002, 2003, 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
* as published by the Free Software Foundation
*
*/
#include "openbios/config.h"
#include "openbios/bindings.h"
#include "openbios/elfload.h"
#include "openbios/nvram.h"
#include "libc/diskio.h"
#include "libc/vsprintf.h"
#include "kernel.h"
#include "ofmem.h"
//#define DEBUG_ELF
#ifdef DEBUG_ELF
#define ELF_DPRINTF(fmt, args...) \
do { printk("ELF - %s: " fmt, __func__ , ##args); } while (0)
#else
#define ELF_DPRINTF(fmt, args...) do { } while (0)
#endif
#define NVRAM_ADDR_LO 0x74
#define NVRAM_ADDR_HI 0x75
#define NVRAM_DATA 0x77
static void
transfer_control_to_elf( ulong elf_entry )
{
ELF_DPRINTF("Starting ELF boot loader\n");
call_elf( 0, 0, elf_entry );
fatal_error("call_elf returned unexpectedly\n");
}
static int
load_elf_rom( ulong *elf_entry, int fd )
{
int i, lszz_offs, elf_offs;
char *addr;
Elf_ehdr ehdr;
Elf_phdr *phdr;
size_t s;
ELF_DPRINTF("Loading '%s' from '%s'\n", get_file_path(fd),
get_volume_name(fd) );
/* the ELF-image (usually) starts at offset 0x4000 */
if( (elf_offs=find_elf(fd)) < 0 ) {
ELF_DPRINTF("----> %s is not an ELF image\n", get_file_path(fd));
return -1;
}
if( !(phdr=elf_readhdrs(fd, elf_offs, &ehdr)) ) {
ELF_DPRINTF("elf_readhdrs failed\n");
return -1;
}
*elf_entry = ehdr.e_entry;
/* load segments. Compressed ROM-image assumed to be located immediately
* after the last segment */
lszz_offs = elf_offs;
for( i=0; i<ehdr.e_phnum; i++ ) {
/* p_memsz, p_flags */
s = MIN( phdr[i].p_filesz, phdr[i].p_memsz );
seek_io( fd, elf_offs + phdr[i].p_offset );
ELF_DPRINTF("filesz: %08lX memsz: %08lX p_offset: %08lX p_vaddr %08lX\n",
(ulong)phdr[i].p_filesz, (ulong)phdr[i].p_memsz, (ulong)phdr[i].p_offset,
(ulong)phdr[i].p_vaddr );
if( phdr[i].p_vaddr != phdr[i].p_paddr )
ELF_DPRINTF("WARNING: ELF segment virtual addr != physical addr\n");
lszz_offs = MAX( lszz_offs, elf_offs + phdr[i].p_offset + phdr[i].p_filesz );
if( !s )
continue;
if( ofmem_claim( phdr[i].p_vaddr, phdr[i].p_memsz, 0 ) == -1 )
fatal_error("Claim failed!\n");
addr = (char*)phdr[i].p_vaddr;
if( read_io(fd, addr, s) != s ) {
ELF_DPRINTF("read failed\n");
return -1;
}
flush_icache_range( addr, addr+s );
ELF_DPRINTF("ELF ROM-section loaded at %08lX (size %08lX)\n",
(ulong)phdr[i].p_vaddr, (ulong)phdr[i].p_memsz );
}
free( phdr );
return lszz_offs;
}
static void
encode_bootpath( const char *spec, const char *args )
{
phandle_t chosen_ph = find_dev("/chosen");
set_property( chosen_ph, "bootpath", spec, strlen(spec)+1 );
set_property( chosen_ph, "bootargs", args, strlen(args)+1 );
}
/************************************************************************/
/* qemu booting */
/************************************************************************/
static void
try_path(const char *path, const char *param)
{
ulong elf_entry;
int fd, ret;
ELF_DPRINTF("Trying %s %s\n", path, param);
if ((fd = open_io(path)) == -1) {
ELF_DPRINTF("Can't open %s\n", path);
return;
}
ret = load_elf_rom( &elf_entry, fd );
if (ret < 0)
return;
close_io( fd );
encode_bootpath( path, param );
update_nvram();
ELF_DPRINTF("Transfering control to %s %s\n",
path, param);
transfer_control_to_elf( elf_entry );
/* won't come here */
}
/*
Parse SGML structure like:
<chrp-boot>
<description>Debian/GNU Linux Installation on IBM CHRP hardware</description>
<os-name>Debian/GNU Linux for PowerPC</os-name>
<boot-script>boot &device;:\install\yaboot</boot-script>
<icon size=64,64 color-space=3,3,2>
CHRP system bindings are described at:
http://playground.sun.com/1275/bindings/chrp/chrp1_7a.ps
*/
static void
try_bootinfo(const char *path)
{
int fd;
char tagbuf[256], bootscript[256], c, *left, *right;
int len, tag, taglen, script, scriptlen;
snprintf(bootscript, sizeof(bootscript), "%s,ppc\\bootinfo.txt", path);
ELF_DPRINTF("Trying %s\n", bootscript);
if ((fd = open_io(bootscript)) == -1) {
ELF_DPRINTF("Can't open %s\n", bootscript);
return;
}
len = read_io(fd, tagbuf, 11);
tagbuf[11] = '\0';
if (len < 0 || strcasecmp(tagbuf, "<chrp-boot>") != 0)
goto badf;
tag = 0;
taglen = 0;
script = 0;
scriptlen = 0;
do {
len = read_io(fd, &c, 1);
if (len < 0)
goto badf;
if (c == '<') {
tag = 1;
taglen = 0;
} else if (c == '>') {
tag = 0;
tagbuf[taglen] = '\0';
if (strcasecmp(tagbuf, "boot-script") == 0)
script = 1;
else if (strcasecmp(tagbuf, "/boot-script") == 0) {
bootscript[scriptlen] = '\0';
break;
}
} else if (tag && taglen < sizeof(tagbuf)) {
tagbuf[taglen++] = c;
} else if (script && scriptlen < sizeof(bootscript)) {
bootscript[scriptlen++] = c;
}
} while (1);
ELF_DPRINTF("got bootscript %s\n", bootscript);
// Replace &device;: with original path
push_str(bootscript);
PUSH('&');
fword("left-split");
fword("2swap");
PUSH(':');
fword("left-split");
fword("2drop");
right = pop_fstr_copy();
left = pop_fstr_copy();
snprintf(bootscript, sizeof(bootscript), "%s%s,%s", left, path, right);
ELF_DPRINTF("fixed bootscript %s\n", bootscript);
feval(bootscript);
badf:
close_io( fd );
}
static uint8_t nvram_read(uint16_t offset)
{
outb(offset & 0xff, NVRAM_ADDR_LO);
outb(offset >> 8, NVRAM_ADDR_HI);
return inb(NVRAM_DATA);
}
static uint32_t nvram_read_be32(uint16_t offset)
{
uint32_t ret;
ret = nvram_read(offset) << 24;
ret |= nvram_read(offset + 1) << 16;
ret |= nvram_read(offset + 2) << 8;
ret |= nvram_read(offset + 3);
return ret;
}
#define QUIK_SECOND_BASEADDR 0x3e0000
#define QUIK_FIRST_BASEADDR 0x3f4000
#define QUIK_SECOND_SIZE (QUIK_FIRST_BASEADDR - QUIK_SECOND_BASEADDR)
#define QUIK_FIRST_INFO_OFF 0x2c8
struct first_info {
char quik_vers[8];
int nblocks;
int blocksize;
unsigned second_base;
int conf_part;
char conf_file[32];
unsigned blknos[64];
};
static void
quik_startup( void )
{
int fd;
int len;
const char *path = "hd:2";
union {
char buffer[1024];
int first_word;
struct {
char pad[QUIK_FIRST_INFO_OFF];
struct first_info fi;
} fi;
} u;
phandle_t ph;
if ((fd = open_io(path)) == -1) {
ELF_DPRINTF("Can't open %s\n", path);
return;
}
seek_io(fd, 0);
len = read_io(fd, u.buffer, sizeof(u));
close_io( fd );
if (len == -1) {
ELF_DPRINTF("Can't read %s\n", path);
return;
}
/* is it quik ? */
if (memcmp(u.fi.fi.quik_vers, "QUIK", 4))
return;
ph = find_dev("/options");
set_property(ph, "boot-device", path, strlen(path) + 1);
ph = find_dev("/chosen");
set_property(ph, "bootargs", "Linux", 6);
if( ofmem_claim( QUIK_FIRST_BASEADDR, len, 0 ) == -1 )
fatal_error("Claim failed!\n");
memcpy(QUIK_FIRST_BASEADDR, u.buffer, len);
/* quik fist level doesn't claim second level memory */
if( ofmem_claim( QUIK_SECOND_BASEADDR, QUIK_SECOND_SIZE, 0 ) == -1 )
fatal_error("Claim failed!\n");
call_elf(0, 0, QUIK_FIRST_BASEADDR);
return;
}
static void
yaboot_startup( void )
{
static const char * const paths[] = { "hd:2,\\ofclient", "hd:2,\\yaboot" };
static const char * const args[] = { "", "conf=hd:2,\\yaboot.conf" };
char *path = pop_fstr_copy(), *param;
int i;
if (!path) {
ELF_DPRINTF("Entering boot, no path\n");
push_str("boot-device");
push_str("/options");
fword("(find-dev)");
POP();
fword("get-package-property");
if (!POP()) {
path = pop_fstr_copy();
param = strchr(path, ' ');
if (param) {
*param = '\0';
param++;
} else {
push_str("boot-args");
push_str("/options");
fword("(find-dev)");
POP();
fword("get-package-property");
POP();
param = pop_fstr_copy();
}
try_path(path, param);
try_bootinfo(path);
} else {
char boot_device = nvram_read(0x34);
switch (boot_device) {
case 'c':
path = strdup("hd:0");
break;
default:
case 'd':
path = strdup("cd:0");
break;
}
try_bootinfo(path);
}
} else {
ELF_DPRINTF("Entering boot, path %s\n", path);
try_path(path, "");
try_bootinfo(path);
}
for( i=0; i < sizeof(paths) / sizeof(paths[0]); i++ ) {
try_path(paths[i], args[i]);
}
printk("*** Boot failure! No secondary bootloader specified ***\n");
}
static void check_preloaded_kernel(void)
{
unsigned long kernel_image, kernel_size, cmdline;
unsigned long initrd_image, initrd_size;
kernel_size = nvram_read_be32(0x3c);
if (kernel_size) {
kernel_image = nvram_read_be32(0x38);
cmdline = nvram_read_be32(0x40);
initrd_image = nvram_read_be32(0x48);
initrd_size = nvram_read_be32(0x4c);
printk("[ppc] Kernel already loaded (0x%8.8lx + 0x%8.8lx) "
"(initrd 0x%8.8lx + 0x%8.8lx)\n",
kernel_image, kernel_size, initrd_image, initrd_size);
call_elf(initrd_image, initrd_size, kernel_image);
}
}
/************************************************************************/
/* entry */
/************************************************************************/
void
boot( void )
{
fword("update-chosen");
check_preloaded_kernel();
quik_startup();
yaboot_startup();
}