/*
* head.S -- Bootloader Entry point
*
* Copyright (C) 1998, 1999 Gabriel Paubert, paubert@iram.es
*
* Modified to compile in RTEMS development environment
* by Eric Valette
*
* Copyright (C) 1999 Eric Valette. valette@crf.canon.fr
*
* The license and distribution terms for this file may be
* found in found in the file LICENSE in this distribution or at
* http://www.OARcorp.com/rtems/license.html.
*
* $Id$
*/
#include <sys/types.h>
#include <string.h>
#include <libcpu/cpu.h>
#include "bootldr.h"
#include <libcpu/spr.h>
#include "zlib.h"
#include <libcpu/page.h>
#include <libcpu/byteorder.h>
SPR_RW(DEC)
SPR_RO(PVR)
struct inode;
struct wait_queue;
struct buffer_head;
typedef struct { int counter; } atomic_t;
typedef struct page {
/* these must be first (free area handling) */
struct page *next;
struct page *prev;
struct inode *inode;
unsigned long offset;
struct page *next_hash;
atomic_t count;
unsigned long flags; /* atomic flags, some possibly updated asynchronously */
struct wait_queue *wait;
struct page **pprev_hash;
struct buffer_head * buffers;
} mem_map_t;
extern opaque mm_private, pci_private, v86_private, console_private;
#define CONSOLE_ON_SERIAL "console=ttyS0"
extern struct console_io vacuum_console_functions;
extern opaque log_console_setup, serial_console_setup, vga_console_setup;
boot_data __bd = {0, 0, 0, 0, 0, 0, 0, 0,
32, 0, 0, 0, 0, 0, 0,
&mm_private,
NULL,
&pci_private,
NULL,
&v86_private,
"root=/dev/hdc1"
};
static void exit(void) __attribute__((noreturn));
static void exit(void) {
printk("\nOnly way out is to press the reset button!\n");
asm volatile("": : :"memory");
while(1);
}
void hang(const char *s, u_long x, ctxt *p) {
u_long *r1;
#ifdef DEBUG
print_all_maps("\nMemory mappings at exception time:\n");
#endif
printk("%s %lx NIP: %p LR: %p\n"
"Callback trace (stack:return address)\n",
s, x, (void *) p->nip, (void *) p->lr);
asm volatile("lwz %0,0(1); lwz %0,0(%0); lwz %0,0(%0)": "=b" (r1));
while(r1) {
printk(" %p:%p\n", r1, (void *) r1[1]);
r1 = (u_long *) *r1;
}
exit();
};
void *zalloc(void *x, unsigned items, unsigned size)
{
void *p = salloc(items*size);
if (!p) {
printk("oops... not enough memory for gunzip\n");
}
return p;
}
void zfree(void *x, void *addr, unsigned nb)
{
sfree(addr);
}
#define HEAD_CRC 2
#define EXTRA_FIELD 4
#define ORIG_NAME 8
#define COMMENT 0x10
#define RESERVED 0xe0
#define DEFLATED 8
void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
{
z_stream s;
int r, i, flags;
/* skip header */
i = 10;
flags = src[3];
if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
printk("bad gzipped data\n");
exit();
}
if ((flags & EXTRA_FIELD) != 0)
i = 12 + src[10] + (src[11] << 8);
if ((flags & ORIG_NAME) != 0)
while (src[i++] != 0)
;
if ((flags & COMMENT) != 0)
while (src[i++] != 0)
;
if ((flags & HEAD_CRC) != 0)
i += 2;
if (i >= *lenp) {
printk("gunzip: ran out of data in header\n");
exit();
}
s.zalloc = zalloc;
s.zfree = zfree;
r = inflateInit2(&s, -MAX_WBITS);
if (r != Z_OK) {
printk("inflateInit2 returned %d\n", r);
exit();
}
s.next_in = src + i;
s.avail_in = *lenp - i;
s.next_out = dst;
s.avail_out = dstlen;
r = inflate(&s, Z_FINISH);
if (r != Z_OK && r != Z_STREAM_END) {
printk("inflate returned %d\n", r);
exit();
}
*lenp = s.next_out - (unsigned char *) dst;
inflateEnd(&s);
}
void decompress_kernel(int kernel_size, void * zimage_start, int len,
void * initrd_start, int initrd_len ) {
u_char *parea;
RESIDUAL* rescopy;
int zimage_size= len;
/* That's a mess, we have to copy the residual data twice just in
* case it happens to be in the low memory area where the kernel
* is going to be unpacked. Later we have to copy it back to
* lower addresses because only the lowest part of memory is mapped
* during boot.
*/
parea=__palloc(kernel_size, PA_LOW);
if(!parea) {
printk("Not enough memory to uncompress the kernel.");
exit();
}
/* Note that this clears the bss as a side effect, so some code
* with ugly special case for SMP could be removed from the kernel!
*/
memset(parea, 0, kernel_size);
printk("\nUncompressing the kernel...\n");
rescopy=salloc(sizeof(RESIDUAL));
/* Let us hope that residual data is aligned on word boundary */
*rescopy = *bd->residual;
bd->residual = (void *)PAGE_ALIGN(kernel_size);
gunzip(parea, kernel_size, zimage_start, &zimage_size);
bd->of_entry = 0;
bd->load_address = 0;
bd->r6 = (char *)bd->residual+PAGE_ALIGN(sizeof(RESIDUAL));
bd->r7 = bd->r6+strlen(bd->cmd_line);
if ( initrd_len ) {
/* We have to leave some room for the hash table and for the
* whole array of struct page. The hash table would be better
* located at the end of memory if possible. With some bridges
* DMA from the last pages of memory is slower because
* prefetching from PCI has to be disabled to avoid accessing
* non existing memory. So it is the ideal place to put the
* hash table.
*/
unsigned tmp = rescopy->TotalMemory;
/* It's equivalent to tmp & (-tmp), but using the negation
* operator on unsigned variables looks so ugly.
*/
if ((tmp & (~tmp+1)) != tmp) tmp <<= 1; /* Next power of 2 */
tmp /= 256; /* Size of hash table */
if (tmp> (2<<20)) tmp=2<<20;
tmp = tmp*2 + 0x40000; /* Alignment can double size + 256 kB */
tmp += (rescopy->TotalMemory / PAGE_SIZE)
* sizeof(struct page);
bd->load_address = (void *)PAGE_ALIGN((int)bd->r7 + tmp);
bd->of_entry = (char *)bd->load_address+initrd_len;
}
#ifdef DEBUG
printk("Kernel at 0x%p, size=0x%x\n", NULL, kernel_size);
printk("Initrd at 0x%p, size=0x%x\n",bd->load_address, initrd_len);
printk("Residual data at 0x%p\n", bd->residual);
printk("Command line at 0x%p\n",bd->r6);
#endif
printk("done\nNow booting...\n");
MMUoff(); /* We need to access address 0 ! */
codemove(0, parea, kernel_size, bd->cache_lsize);
codemove(bd->residual, rescopy, sizeof(RESIDUAL), bd->cache_lsize);
codemove(bd->r6, bd->cmd_line, sizeof(bd->cmd_line), bd->cache_lsize);
/* codemove checks for 0 length */
codemove(bd->load_address, initrd_start, initrd_len, bd->cache_lsize);
}
void
setup_hw(void)
{
char *cp, ch;
register RESIDUAL * res;
/* PPC_DEVICE * nvram; */
struct pci_dev *p, *default_vga;
int timer, err;
u_short default_vga_cmd;
static unsigned int indic;
indic = 0;
res=bd->residual;
default_vga=NULL;
default_vga_cmd = 0;
#define vpd res->VitalProductData
if (_read_PVR()>>16 != 1) {
if ( res && vpd.ProcessorBusHz ) {
ticks_per_ms = vpd.ProcessorBusHz/
(vpd.TimeBaseDivisor ? vpd.TimeBaseDivisor : 4000);
} else {
ticks_per_ms = 16500; /* assume 66 MHz on bus */
}
}
select_console(CONSOLE_LOG);
/* We check that the keyboard is present and immediately
* select the serial console if not.
*/
err = kbdreset();
if (err) select_console(CONSOLE_SERIAL);
printk("\nModel: %s\nSerial: %s\n"
"Processor/Bus frequencies (Hz): %ld/%ld\n"
"Time Base Divisor: %ld\n"
"Memory Size: %x\n",
vpd.PrintableModel,
vpd.Serial,
vpd.ProcessorHz,
vpd.ProcessorBusHz,
(vpd.TimeBaseDivisor ? vpd.TimeBaseDivisor : 4000),
res->TotalMemory);
printk("Original MSR: %lx\nOriginal HID0: %lx\nOriginal R31: %lx\n",
bd->o_msr, bd->o_hid0, bd->o_r31);
/* This reconfigures all the PCI subsystem */
pci_init();
/* The Motorola NT firmware does not set the correct mem size */
if ( vpd.FirmwareSupplier == 0x10000 ) {
int memsize;
memsize = find_max_mem(bd->pci_devices);
if ( memsize != res->TotalMemory ) {
printk("Changed Memory size from %lx to %x\n",
res->TotalMemory, memsize);
res->TotalMemory = memsize;
res->GoodMemory = memsize;
}
}
#define ENABLE_VGA_USAGE
#undef ENABLE_VGA_USAGE
#ifdef ENABLE_VGA_USAGE
/* Find the primary VGA device, chosing the first one found
* if none is enabled. The basic loop structure has been copied
* from linux/drivers/char/bttv.c by Alan Cox.
*/
for (p = bd->pci_devices; p; p = p->next) {
u_short cmd;
if (p->class != PCI_CLASS_NOT_DEFINED_VGA &&
((p->class) >> 16 != PCI_BASE_CLASS_DISPLAY))
continue;
if (p->bus->number != 0) {
printk("VGA device not on bus 0 not initialized!\n");
continue;
}
/* Only one can be active in text mode, which for now will
* be assumed as equivalent to having I/O response enabled.
*/
pci_read_config_word(p, PCI_COMMAND, &cmd);
if(cmd & PCI_COMMAND_IO || !default_vga) {
default_vga=p;
default_vga_cmd=cmd;
}
}
/* Disable the enabled VGA device, if any. */
if (default_vga)
pci_write_config_word(default_vga, PCI_COMMAND,
default_vga_cmd&
~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY));
init_v86();
/* Same loop copied from bttv.c, this time doing the serious work */
for (p = bd->pci_devices; p; p = p->next) {
u_short cmd;
if (p->class != PCI_CLASS_NOT_DEFINED_VGA &&
((p->class) >> 16 != PCI_BASE_CLASS_DISPLAY))
continue;
if (p->bus->number != 0) continue;
pci_read_config_word(p, PCI_COMMAND, &cmd);
pci_write_config_word(p, PCI_COMMAND,
cmd|PCI_COMMAND_IO|PCI_COMMAND_MEMORY);
printk("Calling the emulator.\n");
em86_main(p);
pci_write_config_word(p, PCI_COMMAND, cmd);
}
cleanup_v86_mess();
#endif
/* Reenable the primary VGA device */
if (default_vga) {
pci_write_config_word(default_vga, PCI_COMMAND,
default_vga_cmd|
(PCI_COMMAND_IO|PCI_COMMAND_MEMORY));
if (err) {
printk("Keyboard error %d, using serial console!\n",
err);
} else {
select_console(CONSOLE_VGA);
}
} else if (!err) {
select_console(CONSOLE_SERIAL);
if (bd->cmd_line[0] == '\0') {
strcat(&bd->cmd_line[0], CONSOLE_ON_SERIAL);
}
else {
int s = strlen (bd->cmd_line);
bd->cmd_line[s + 1] = ' ';
bd->cmd_line[s + 2] = '\0';
strcat(&bd->cmd_line[0], CONSOLE_ON_SERIAL);
}
}
#if 0
/* In the future we may use the NVRAM to store default
* kernel parameters.
*/
nvram=residual_find_device(~0UL, NULL, SystemPeripheral, NVRAM,
~0UL, 0);
if (nvram) {
PnP_TAG_PACKET * pkt;
switch (nvram->DevId.Interface) {
case IndirectNVRAM:
pkt=PnP_find_packet(res->DevicePnpHeap
+nvram->AllocatedOffset,
)
}
}
#endif
printk("\nRTEMS 4.x/PPC load: ");
timer = 0;
cp = bd->cmd_line+strlen(bd->cmd_line);
while (timer++ < 5*1000) {
if (debug_tstc()) {
while ((ch = debug_getc()) != '\n' && ch != '\r') {
if (ch == '\b' || ch == 0177) {
if (cp != bd->cmd_line) {
cp--;
printk("\b \b");
}
} else {
*cp++ = ch;
debug_putc(ch);
}
}
break; /* Exit 'timer' loop */
}
udelay(1000); /* 1 msec */
}
*cp = 0;
}
/* Functions to deal with the residual data */
static int same_DevID(unsigned short vendor,
unsigned short Number,
char * str)
{
static unsigned const char hexdigit[]="0123456789ABCDEF";
if (strlen(str)!=7) return 0;
if ( ( ((vendor>>10)&0x1f)+'A'-1 == str[0]) &&
( ((vendor>>5)&0x1f)+'A'-1 == str[1]) &&
( (vendor&0x1f)+'A'-1 == str[2]) &&
(hexdigit[(Number>>12)&0x0f] == str[3]) &&
(hexdigit[(Number>>8)&0x0f] == str[4]) &&
(hexdigit[(Number>>4)&0x0f] == str[5]) &&
(hexdigit[Number&0x0f] == str[6]) ) return 1;
return 0;
}
PPC_DEVICE *residual_find_device(unsigned long BusMask,
unsigned char * DevID,
int BaseType,
int SubType,
int Interface,
int n)
{
int i;
RESIDUAL *res = bd->residual;
if ( !res || !res->ResidualLength ) return NULL;
for (i=0; i<res->ActualNumDevices; i++) {
#define Dev res->Devices[i].DeviceId
if ( (Dev.BusId&BusMask) &&
(BaseType==-1 || Dev.BaseType==BaseType) &&
(SubType==-1 || Dev.SubType==SubType) &&
(Interface==-1 || Dev.Interface==Interface) &&
(DevID==NULL || same_DevID((Dev.DevId>>16)&0xffff,
Dev.DevId&0xffff, DevID)) &&
!(n--) ) return res->Devices+i;
#undef Dev
}
return 0;
}
PnP_TAG_PACKET *PnP_find_packet(unsigned char *p,
unsigned packet_tag,
int n)
{
unsigned mask, masked_tag, size;
if(!p) return 0;
if (tag_type(packet_tag)) mask=0xff; else mask=0xF8;
masked_tag = packet_tag&mask;
for(; *p != END_TAG; p+=size) {
if ((*p & mask) == masked_tag && !(n--))
return (PnP_TAG_PACKET *) p;
if (tag_type(*p))
size=ld_le16((unsigned short *)(p+1))+3;
else
size=tag_small_count(*p)+1;
}
return 0; /* not found */
}
PnP_TAG_PACKET *PnP_find_small_vendor_packet(unsigned char *p,
unsigned packet_type,
int n)
{
int next=0;
while (p) {
p = (unsigned char *) PnP_find_packet(p, 0x70, next);
if (p && p[1]==packet_type && !(n--))
return (PnP_TAG_PACKET *) p;
next = 1;
};
return 0; /* not found */
}
PnP_TAG_PACKET *PnP_find_large_vendor_packet(unsigned char *p,
unsigned packet_type,
int n)
{
int next=0;
while (p) {
p = (unsigned char *) PnP_find_packet(p, 0x84, next);
if (p && p[3]==packet_type && !(n--))
return (PnP_TAG_PACKET *) p;
next = 1;
};
return 0; /* not found */
}
/* Find out the amount of installed memory. For MPC105 and IBM 660 this
* can be done by finding the bank with the highest memory ending address
*/
int
find_max_mem( struct pci_dev *dev )
{
u_char banks,tmp;
int i, top, max;
max = 0;
for ( ; dev; dev = dev->next) {
if ( ((dev->vendor == PCI_VENDOR_ID_MOTOROLA) &&
(dev->device == PCI_DEVICE_ID_MOTOROLA_MPC105)) ||
((dev->vendor == PCI_VENDOR_ID_IBM) &&
(dev->device == 0x0037/*IBM 660 Bridge*/)) ) {
pci_read_config_byte(dev, 0xa0, &banks);
for (i = 0; i < 8; i++) {
if ( banks & (1<<i) ) {
pci_read_config_byte(dev, 0x90+i, &tmp);
top = tmp;
pci_read_config_byte(dev, 0x98+i, &tmp);
top |= (tmp&3)<<8;
if ( top > max ) max = top;
}
}
if ( max ) return ((max+1)<<20);
else return(0);
}
}
return(0);
}