370 lines
11 KiB
C
370 lines
11 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 "config.h"
|
|
#include "libopenbios/bindings.h"
|
|
#include "libopenbios/elfload.h"
|
|
#include "arch/common/nvram.h"
|
|
#include "libc/diskio.h"
|
|
#include "libc/vsprintf.h"
|
|
#include "mol/mol.h"
|
|
#include "libopenbios/ofmem.h"
|
|
#include "osi_calls.h"
|
|
#include "ablk_sh.h"
|
|
#include "boothelper_sh.h"
|
|
|
|
|
|
static void patch_newworld_rom( char *start, size_t size );
|
|
static void newworld_timer_hack( char *start, size_t size );
|
|
|
|
static void
|
|
transfer_control_to_elf( unsigned long entry )
|
|
{
|
|
extern void call_elf( unsigned long entry );
|
|
printk("Starting ELF boot loader\n");
|
|
call_elf( entry );
|
|
|
|
fatal_error("call_elf returned unexpectedly\n");
|
|
}
|
|
|
|
static int
|
|
load_elf_rom( unsigned long *entry, int fd )
|
|
{
|
|
int i, lszz_offs, elf_offs;
|
|
char buf[128], *addr;
|
|
Elf_ehdr ehdr;
|
|
Elf_phdr *phdr;
|
|
size_t s;
|
|
|
|
printk("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 ) {
|
|
printk("----> %s is not an ELF image\n", buf );
|
|
exit(1);
|
|
}
|
|
if( !(phdr=elf_readhdrs(fd, elf_offs, &ehdr)) )
|
|
fatal_error("elf_readhdrs failed\n");
|
|
|
|
*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 );
|
|
|
|
/* printk("filesz: %08lX memsz: %08lX p_offset: %08lX p_vaddr %08lX\n",
|
|
phdr[i].p_filesz, phdr[i].p_memsz, phdr[i].p_offset,
|
|
phdr[i].p_vaddr ); */
|
|
|
|
if( phdr[i].p_vaddr != phdr[i].p_paddr )
|
|
printk("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 )
|
|
fatal_error("read failed\n");
|
|
|
|
/* patch CODE segment */
|
|
if( *entry >= phdr[i].p_vaddr && *entry < phdr[i].p_vaddr + s ) {
|
|
patch_newworld_rom( (char*)phdr[i].p_vaddr, s );
|
|
newworld_timer_hack( (char*)phdr[i].p_vaddr, s );
|
|
}
|
|
flush_icache_range( addr, addr+s );
|
|
|
|
/* printk("ELF ROM-section loaded at %08lX (size %08lX)\n",
|
|
(unsigned long)phdr[i].p_vaddr, (unsigned long)phdr[i].p_memsz );*/
|
|
}
|
|
free( phdr );
|
|
return lszz_offs;
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* newworld ROM loading */
|
|
/************************************************************************/
|
|
|
|
#define ROM_BASE 0x1100000 /* where we decide to put things */
|
|
|
|
/* fix bug present in the 2.4 and the 3.0 Apple ROM */
|
|
static void
|
|
patch_newworld_rom( char *start, size_t size )
|
|
{
|
|
int s;
|
|
unsigned long mark[] = { 0x7c7d1b78, /* mr r29,r3 */
|
|
0x7c9c2378, /* mr r28,r4 */
|
|
0x7cc33378, /* mr r3,r6 */
|
|
0x7c864214, /* add r4,r6,r8 <------ BUG -- */
|
|
0x80b10000, /* lwz r5,0(r17) */
|
|
0x38a500e8 }; /* addi r5,r5,232 */
|
|
|
|
/* Correcting add r4,r6,r8 ----> addi r4,r6,8 */
|
|
for( s=0; s<size-sizeof(mark); s+=4 )
|
|
if( memcmp( start+s, mark, sizeof(mark)) == 0 ) {
|
|
printk("FIXING ROM BUG @ %X!\n", s+12);
|
|
((unsigned long*)(start+s))[3] = 0x38860008; /* addi r4,r6,8 */
|
|
}
|
|
}
|
|
|
|
/* This hack is only needed on machines with a timebase slower than 12.5 MHz
|
|
* (50 MHz bus frequency). Typically only old, accelerated machines fall
|
|
* into this category. The cause of the problem is an overflow in Apple's
|
|
* calibration routine.
|
|
*/
|
|
static void
|
|
newworld_timer_hack( char *start, size_t size )
|
|
{
|
|
int s;
|
|
unsigned long mark[] = { 0x7d0000a6, 0x5507045e, 0x7ce00124, 0x4c00012c,
|
|
0x38e00000, 0x3c80000f, 0x6084ffff, 0x98830c00,
|
|
0x7c0006ac, 0x98830a00, 0x7c0006ac, 0x7c9603a6,
|
|
0x4c00012c, 0x7cb602a6, 0x2c050000, 0x4181fff8,
|
|
0x7c0004ac, 0x88830a00, 0x7c0006ac, 0x88a30800,
|
|
0x7c0006ac, 0x88c30a00, 0x7c0006ac, 0x7c043040,
|
|
0x40a2ffe4, 0x5085442e, 0x7ca500d0, 0x54a5043e,
|
|
0x7c053840, 0x7ca72b78, 0x4082ff9c, 0x7ca32b78,
|
|
0x7d000124, 0x4c00012c, 0x4e800020
|
|
};
|
|
|
|
/* return #via ticks corresponding to 0xfffff DEC ticks (VIA frequency == 47/60 MHz) */
|
|
for( s=0; s < size-sizeof(mark); s+=4 ) {
|
|
if( !memcmp( start+s, mark, sizeof(mark)) ) {
|
|
extern char timer_calib_start[], timer_calib_end[];
|
|
extern unsigned long nw_dec_calibration;
|
|
int hz = OSI_UsecsToMticks(1000);
|
|
nw_dec_calibration = OSI_MticksToUsecs(0xfffff*47)/60;
|
|
memcpy( start + s, timer_calib_start, timer_calib_end - timer_calib_start );
|
|
|
|
printk("Timer calibration fix: %d.%02d MHz [%ld]\n",
|
|
hz/1000, (hz/10)%100, nw_dec_calibration );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static unsigned long
|
|
load_newworld_rom( int fd )
|
|
{
|
|
int lszz_offs, lszz_size;
|
|
unsigned long entry, data[2];
|
|
phandle_t ph;
|
|
|
|
lszz_offs = load_elf_rom( &entry, fd );
|
|
seek_io( fd, -1 );
|
|
lszz_size = tell(fd) - lszz_offs;
|
|
seek_io( fd, lszz_offs );
|
|
|
|
/* printk("Compressed ROM image: offset %08X, size %08X loaded at %08x\n",
|
|
lszz_offs, lszz_size, ROM_BASE ); */
|
|
|
|
if( ofmem_claim(ROM_BASE, lszz_size, 0) == -1 )
|
|
fatal_error("Claim failure (lszz)!\n");
|
|
|
|
read_io( fd, (char*)ROM_BASE, lszz_size );
|
|
|
|
/* Fix the /rom/macos/AAPL,toolbox-image,lzss property (phys, size) */
|
|
#if 0
|
|
if( (ph=prom_create_node("/rom/macos/")) == -1 )
|
|
fatal_error("Failed creating /rom/macos/");
|
|
#else
|
|
ph = find_dev("/rom/macos");
|
|
#endif
|
|
data[0] = ROM_BASE;
|
|
data[1] = lszz_size;
|
|
set_property( ph, "AAPL,toolbox-image,lzss", (char*)data, sizeof(data) );
|
|
|
|
/* The 7.8 rom (MacOS 9.2) uses AAPL,toolbox-parcels instead of
|
|
* AAPL,toolbox-image,lzss. It probably doesn't hurt to have it
|
|
* always present (we don't have an easy way to determine ROM version...)
|
|
*/
|
|
set_property( ph, "AAPL,toolbox-parcels", (char*)data, sizeof(data) );
|
|
return entry;
|
|
}
|
|
|
|
static int
|
|
search_nwrom( int fd, int fast )
|
|
{
|
|
char *s, buf[128];
|
|
int found = 0;
|
|
|
|
if( fast ) {
|
|
int ind;
|
|
found = !reopen( fd, "\\\\:tbxi" );
|
|
for( ind=0; !found && (s=BootHGetStrResInd("macos_rompath", buf, sizeof(buf), ind++, 0)) ; )
|
|
found = !reopen( fd, s );
|
|
for( ind=0; !found && (s=BootHGetStrResInd("macos_rompath_", buf, sizeof(buf), ind++, 0)) ; )
|
|
found = !reopen( fd, s );
|
|
} else {
|
|
printk("Searching %s for a 'Mac OS ROM' file\n", get_volume_name(fd) );
|
|
if( !(found=reopen_nwrom(fd)) ) {
|
|
printk(" \n**** HINT ***************************************************\n");
|
|
printk("* The booting can be speeded up by adding the line\n");
|
|
printk("* macos_rompath: '%s'\n", get_file_path(fd) );
|
|
printk("* to the /etc/mol/molrc.macos (recommended).\n");
|
|
printk("*************************************************************\n \n");
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
static char *
|
|
newworld_load( const char *node_path, const char *spec, int do_search )
|
|
{
|
|
char *p, *entry, buf[80];
|
|
int fd, len;
|
|
|
|
if( (fd=open_io(spec)) == -1 )
|
|
return NULL;
|
|
|
|
if( !search_nwrom(fd, do_search) ) {
|
|
close_io(fd);
|
|
return NULL;
|
|
}
|
|
printk("Boot Disk: %s [%s]\n", spec, get_fstype(fd) );
|
|
|
|
entry = (char*)load_newworld_rom( fd );
|
|
|
|
#if 1
|
|
PUSH_ih( get_ih_from_fd(fd) );
|
|
fword("get-instance-path");
|
|
len = POP();
|
|
p = (char*)POP();
|
|
buf[0] = 0;
|
|
if( len < sizeof(buf) ) {
|
|
memcpy( buf, p, len );
|
|
buf[len] =0;
|
|
}
|
|
strcat( buf, "/x@:" );
|
|
printk("boot_path: %s\n", buf );
|
|
encode_bootpath( buf, "" );
|
|
#endif
|
|
close_io( fd );
|
|
return entry;
|
|
}
|
|
|
|
static void
|
|
newworld_startup( void )
|
|
{
|
|
int i, j, bootunit, type, fd;
|
|
ablk_disk_info_t info;
|
|
char *entry = NULL;
|
|
char spec[80];
|
|
phandle_t ph;
|
|
|
|
char path[]="/pci/pci-bridge/mol-blk";
|
|
if( !(ph=find_dev(path)) )
|
|
fatal_error("MOLBlockDriver node not found\n");
|
|
|
|
/* user-specified newworld ROMs take precedence */
|
|
if( (fd=open_io("pseudo:,nwrom")) >= 0 ) {
|
|
entry = (char*)load_newworld_rom( fd );
|
|
close_io( fd );
|
|
}
|
|
|
|
/* determine boot volume */
|
|
for( bootunit=-1, type=0; bootunit==-1 && type<3 ; type++ ) {
|
|
for( i=0; !OSI_ABlkDiskInfo(0, i, &info) ; i++ ) {
|
|
if( type<=1 && !(info.flags & ABLK_BOOT_HINT) )
|
|
continue;
|
|
if( type>1 && (info.flags & ABLK_BOOT_HINT) )
|
|
continue;
|
|
|
|
for( j=0; !entry && j<32; j++ ) {
|
|
snprintf( spec, sizeof(spec), "%s/disk@%x:%d",
|
|
path, i, j );
|
|
entry = newworld_load( path, spec, (!type || type==2) );
|
|
}
|
|
if( entry ) {
|
|
bootunit = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( entry ) {
|
|
OSI_ABlkBlessDisk( 0 /*channel*/, bootunit );
|
|
|
|
update_nvram();
|
|
transfer_control_to_elf( (unsigned long)entry );
|
|
/* won't come here */
|
|
return;
|
|
}
|
|
|
|
printk("\n--- No bootable disk was found! -----------------------------\n");
|
|
printk("If this is an oldworld machine, try booting from the MacOS\n");
|
|
printk("install CD and install MacOS from within MOL.\n");
|
|
printk("-------------------------------------------------------------\n");
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* yaboot booting */
|
|
/************************************************************************/
|
|
|
|
static void
|
|
yaboot_startup( void )
|
|
{
|
|
const char *paths[] = { "pseudo:,ofclient", "pseudo:,yaboot", NULL };
|
|
unsigned long entry;
|
|
int i, fd;
|
|
|
|
for( i=0; paths[i]; i++ ) {
|
|
if( (fd=open_io(paths[i])) == -1 )
|
|
continue;
|
|
(void) load_elf_rom( &entry, fd );
|
|
close_io( fd );
|
|
encode_bootpath( paths[i], "" );
|
|
|
|
update_nvram();
|
|
transfer_control_to_elf( entry );
|
|
/* won't come here */
|
|
}
|
|
printk("*** Boot failure! No secondary bootloader specified ***\n");
|
|
exit(1);
|
|
}
|
|
|
|
|
|
/************************************************************************/
|
|
/* entry */
|
|
/************************************************************************/
|
|
|
|
void
|
|
boot( void )
|
|
{
|
|
fword("update-chosen");
|
|
if( find_dev("/mol-platform") )
|
|
yaboot_startup();
|
|
else
|
|
newworld_startup();
|
|
}
|