blob: 0cabe7ba606c257871a7c66455e9b732f986a54c [file] [log] [blame]
/* TILO: The TFTP Image LOader
Copyright (C) 1996 Jakub Jelinek
1998 Jan Vondrak
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; either version 2 of the License, or
(at your option) any later version.
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. */
#include <elf.h>
#include <silo.h>
#include <setjmp.h>
#ifndef NULL
#define NULL (void *)0
#endif
#ifdef SUPERTILO
#define MOVED_BASE 0x9c0000
#else
#ifndef LARGETILO
#define MOVED_BASE 0x3c0000
#else
#define MOVED_BASE 0x4c0000
#endif
#endif
/*
* gzip declarations
*/
#define OF(args) args
#define STATIC static
#define memzero(s, n) memset ((s), 0, (n))
typedef unsigned char uch;
typedef unsigned short ush;
typedef unsigned long ulg;
#define WSIZE 0x8000 /* Window size must be at least 32k, */
/* and a power of two */
static uch window[WSIZE]; /* Sliding window buffer */
static unsigned outcnt = 0; /* bytes in output buffer */
/* gzip flag byte */
#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */
#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
#define COMMENT 0x10 /* bit 4 set: file comment present */
#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */
#define RESERVED 0xC0 /* bit 6,7: reserved */
#define Assert(cond,msg)
#define Trace(x)
#define Tracev(x)
#define Tracevv(x)
#define Tracec(c,x)
#define Tracecv(c,x)
static void flush_window (void);
static void error (char *);
#define gzip_mark mark
static inline void gzip_release (void **p)
{
release (*p);
}
static long bytes_out;
static uch *output_data, *output_limit;
static unsigned char (*get_input_fun) (void);
static void (*unget_input_fun) (void);
jmp_buf gunzip_env;
#define get_byte() (*get_input_fun)()
#define unget_byte() (*unget_input_fun)()
#include "../common/inflate.c"
static void error (char *m)
{
printf ("\nDecompression error: %s\n", m);
longjmp (gunzip_env, 1);
}
static void flush_window ()
{
ulg c = crc;
unsigned n;
uch *in, ch;
in = window;
if (output_data + outcnt > output_limit)
error ("uncompressed image too long - wouldn't fit into destination");
for (n = 0; n < outcnt; n++) {
ch = *output_data++ = *in++;
c = crc_32_tab[((int) c ^ ch) & 0xff] ^ (c >> 8);
}
crc = c;
bytes_out += (ulg) outcnt;
outcnt = 0;
}
int decompress (char *outptr, char *outptrlim, unsigned char (*get_input) (void), void (*unget_input) (void))
{
void *save_ptr;
static int first = 1;
gzip_mark (&save_ptr);
if (setjmp (gunzip_env)) {
gzip_release (&save_ptr);
return -1;
}
output_data = (unsigned char *)outptr;
output_limit = (unsigned char *)outptrlim;
get_input_fun = get_input;
unget_input_fun = unget_input;
bytes_out = 0;
crc = 0xffffffffL;
if (first) {
makecrc ();
first = 0;
}
gunzip ();
gzip_release (&save_ptr);
#ifdef TILO_DEBUG
printf("Returning from decompress()\n");
#endif
return bytes_out;
}
static unsigned char *gzminp;
static unsigned char get_input(void)
{
return *gzminp++;
}
static void unget_input(void)
{
gzminp--;
}
extern char start, main_text_start, main_text_end, main_data_start, main_data_end, main_rodata_start, main_rodata_end, __bss_start;
struct ImageInfo
{
unsigned packed_start;
unsigned packed_len;
unsigned unpacked_len; /* this is meaningful for the kernel images only */
unsigned root_start; /* this is meaningful for the kernel images only */
};
extern struct ImageInfo image_table[4]; /* Sun4 kernel, Sun4c/d/m kernel, Sun4u kernel, root image */
#define SUN4_KERNEL 0
#define SUN4C_KERNEL 1
#define SUN4U_KERNEL 2
#define ROOT_IMAGE 3
#define HDRS_TAG (('H'<<24) | ('d'<<16) | ('r'<<8) | 'S')
static char *sun4u_memory_find (unsigned int len)
{
int n, node, i;
struct p1275_mem {
unsigned long long phys;
unsigned long long size;
} *p = (struct p1275_mem *)0;
unsigned int virt = 0x40000000;
unsigned long long phys = 0, phys_base;
p = (struct p1275_mem *)malloc(2048);
node = prom_finddevice("/memory");
n = prom_getproplen(node, "available");
if (!n || n == -1 ||
prom_getproperty(node, "available", (char *)p, 2048) == -1) {
free (p);
printf("Could not get available property\n");
return (char *)0;
}
phys = 0;
n /= sizeof(*p);
phys_base = ~(unsigned long long)0;
for (i = 0; i < n; i++) {
if (p[i].phys < phys_base)
phys_base = p[i].phys;
}
for (i = 0; i < n; i++) {
/* Do not mess with first 16 Megs of memory */
if (p[i].phys == phys_base) {
if (p[i].size <= 0x1000000)
continue;
p[i].phys += 0x1000000;
p[i].size -= 0x1000000;
}
if (p[i].size >= len) {
phys = p[i].phys;
break;
}
}
free (p);
if (!phys) {
printf("Could not find any available memory\n");
return (char *)0;
}
if (prom_map(PROM_MAP_DEFAULT, (unsigned long long)len, virt, phys) ==
-1) {
printf("Could not map memory\n");
return (char *)0;
}
return (char *)virt + 0x4000;
}
void parse_executable(char *base, int image_len)
{
union {
char *b;
struct aout_hdr *a;
Elf32_Ehdr *e;
Elf64_Ehdr *f;
} hp;
unsigned off = 0;
int len = 0;
/*
* Check if the image is an executable file, either an a.out or an elf
* binary.
*/
hp.b = base;
if (hp.a->magic == 0x01030107) {
off = sizeof (struct aout_hdr);
if (image_len > hp.a->ltext + hp.a->ldata)
len = hp.a->ltext + hp.a->ldata;
else
len = image_len;
} else if (hp.e->e_ident[EI_MAG0] == ELFMAG0 &&
hp.e->e_ident[EI_MAG1] == ELFMAG1 &&
hp.e->e_ident[EI_MAG2] == ELFMAG2 &&
hp.e->e_ident[EI_MAG3] == ELFMAG3) {
if (hp.e->e_ident[EI_DATA] != ELFDATA2MSB) {
printf("Image is not a MSB ELF.\n");
prom_halt();
}
if (hp.e->e_ident[EI_CLASS] == ELFCLASS32) {
Elf32_Phdr *p;
int i;
unsigned long n;
Elf32_Phdr *q;
p = (Elf32_Phdr *) (hp.b + hp.e->e_phoff);
if (p->p_type != PT_LOAD) {
printf("Cannot find a loadable segment in your ELF image.\n");
prom_halt();
}
q = p + 1;
for (i = 1; i < hp.e->e_phnum; i++, q++) {
if (q->p_type != PT_LOAD)
break;
n = q->p_offset - p->p_offset;
if (q->p_vaddr - p->p_vaddr == n &&
q->p_paddr - p->p_paddr == n &&
p->p_memsz == p->p_filesz &&
p->p_memsz <= n) {
p->p_filesz = n + q->p_filesz;
p->p_memsz = n + q->p_memsz;
} else {
printf("Multiple loadable segments in your ELF image.\n");
prom_halt();
}
}
off = p->p_offset + hp.e->e_entry - p->p_vaddr;
len = p->p_filesz;
if (len > image_len)
len = image_len;
} else if (hp.e->e_ident[EI_CLASS] == ELFCLASS64) {
Elf64_Phdr *p;
unsigned long long n;
int i;
Elf64_Phdr *q;
p = (Elf64_Phdr *) (hp.b + hp.f->e_phoff);
if (p->p_type != PT_LOAD) {
printf("Cannot find a loadable segment in your ELF image.\n");
prom_halt();
}
q = p + 1;
for (i = 1; i < hp.f->e_phnum; i++, q++) {
if (q->p_type != PT_LOAD)
break;
n = q->p_offset - p->p_offset;
if (q->p_vaddr - p->p_vaddr == n &&
q->p_paddr - p->p_paddr == n &&
p->p_memsz == p->p_filesz &&
p->p_memsz <= n) {
p->p_filesz = n + q->p_filesz;
p->p_memsz = n + q->p_memsz;
} else {
printf("Multiple loadable segments in your ELF image.\n");
prom_halt();
}
}
off = p->p_offset + hp.f->e_entry - p->p_vaddr;
len = p->p_filesz;
if (len > image_len)
len = image_len;
}
} else {
/* Assume "raw" a.out format prepared by tilo.sh. */
return;
}
memmove(base, base + off, len);
}
char *my_main (struct linux_romvec *promvec, void *cifh, void *cifs)
{
char *orig_code,*moved_code,*moved_ramdisk = NULL,*moved_kernel,*kernel_base;
unsigned *p,*q = NULL;
int kernel_number;
char *kernel_end, *kernel_limit;
int move_ramdisk;
prom_init(promvec, cifh, cifs);
printf ("TILO\n");
if (cifh)
{
kernel_number = SUN4U_KERNEL; /* Sun4u */
printf("Selecting sun4u kernel...\n");
}
else if ((long)promvec == 0x4000)
{
kernel_number = SUN4_KERNEL; /* Sun4 */
printf("Selecting sun4 kernel...\n");
}
else
{
kernel_number = SUN4C_KERNEL; /* Sun4c/d/m */
printf("Selecting sun4cdm kernel...\n");
}
if (image_table[kernel_number].packed_len == 0)
{
printf ("ERROR: No kernel for this architecture in this TILO image\n");
prom_halt ();
}
orig_code = (char*) 0x4000;
/*
* On sun4u we can allocate more memory and relocate the kernel.
*/
if (kernel_number == SUN4U_KERNEL) {
unsigned int size;
for (size = 64 * 1024 * 1024; size >= 4 * 1024 * 1024;
size -= 4 * 1024 * 1024) {
kernel_base = sun4u_memory_find(size);
if (kernel_base)
break;
}
if (!kernel_base)
goto no_mem;
kernel_limit = kernel_base + size;
gzminp = (unsigned char *)orig_code +
image_table[kernel_number].packed_start;
if (image_table[ROOT_IMAGE].packed_len)
image_table[kernel_number].root_start = (unsigned)orig_code +
image_table[ROOT_IMAGE].packed_start + 0x400000;
else
image_table[kernel_number].root_start = 0;
move_ramdisk = 0;
} else {
no_mem:
move_ramdisk = 1;
moved_code = (char*)MOVED_BASE;
moved_ramdisk = (char*)((long)(moved_code -
image_table[ROOT_IMAGE].packed_len) & ~0xfff);
moved_kernel = (char*)((long)(moved_ramdisk -
image_table[kernel_number].packed_len) & ~0xfff);
#ifdef TILO_DEBUG
printf("Locations: moved_code=%x moved_ramdisk=%x moved_kernel=%x\n",
moved_code, moved_ramdisk, moved_kernel);
#endif
memmove(moved_ramdisk, orig_code + image_table[ROOT_IMAGE].packed_start,
image_table[ROOT_IMAGE].packed_len);
memmove(moved_kernel,
orig_code + image_table[kernel_number].packed_start,
image_table[kernel_number].packed_len);
gzminp = (unsigned char *)moved_kernel; /* decompress kernel */
kernel_base = (char*) 0x4000;
kernel_limit = moved_kernel;
}
kernel_end = kernel_base +
((image_table[kernel_number].unpacked_len + 0xfff) & ~0xfff);
if (kernel_end > kernel_limit) {
printf("No space to decompress the kernel.\n");
prom_halt();
}
if (decompress (kernel_base, kernel_end, get_input, unget_input) == -1)
{
printf ("\nKernel decompression error\n");
prom_halt();
}
parse_executable(kernel_base, kernel_end - kernel_base);
switch (kernel_number)
{
case SUN4U_KERNEL:
/* find HdrS in Sun4u kernel */
q = (unsigned*)kernel_base + 2;
break;
case SUN4C_KERNEL:
/* find HdrS in Sun4c/m/d kernel */
p = (unsigned*)kernel_base;
p += *p & 0xffff; /* extract jump offset */
q = p - 16; /* from the branch instruction */
while (q < p && *q != HDRS_TAG)
q++;
break;
default:
/* find HdrS in Sun4 kernel */
printf ("Sun4 kernel not supported yet\n");
prom_halt ();
break;
}
if (*q != HDRS_TAG)
{
printf ("Can't find HdrS tag in kernel\n");
prom_halt ();
}
/* reset root flags */
q[2] &= 0xffff0000;
/* Set root device and flags. Basically read-write. 0x0100 is ramdisk */
q[3] = 0x01000000;
q[4] = image_table[kernel_number].root_start;
q[5] = image_table[ROOT_IMAGE].packed_len;
/* move root image */
if (move_ramdisk)
memmove ((void*)(image_table[kernel_number].root_start & 0x3fffff),
moved_ramdisk, image_table[ROOT_IMAGE].packed_len);
#ifdef TILO_DEBUG
printf("Returning from my_main() with address %x\n", kernel_base);
#endif
return kernel_base; /* return address to jump into kernel */
}