view ipl/loader.c @ 3:ec991c3809db

make the ipl code generic
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Sat, 26 Feb 2011 12:28:22 -0500
parents 5d9f272f4db6
children
line wrap: on
line source

#ifndef BLOCK_SIZE
#error missing BLOCK_SIZE
#endif

#define TEMP_BASE	((unsigned char*) 0x400000) /* 4MB */

typedef unsigned long u64;
typedef signed long s64;

typedef unsigned int u32;
typedef signed int s32;

typedef unsigned short u16;
typedef signed short s16;

typedef unsigned char u8;
typedef signed char s8;

#define EI_MAG0		0
#define EI_MAG1		1
#define EI_MAG2		2
#define EI_MAG3		3
#define EI_CLASS	4
#define EI_DATA		5
#define EI_VERSION	6
#define EI_OSABI	7
#define EI_ABIVERSION	8
#define EI_PAD		9
#define EI_NIDENT	16

#define ELFCLASS32	1
#define ELFCLASS64	2

#define ELFDATA2LSB	1
#define ELFDATA2MSB	2

#define EV_CURRENT	1

#define ET_NONE		0
#define ET_REL		1
#define ET_EXEC		2
#define ET_DYN		3
#define ET_CORE		4
#define ET_LOOS		0xfe00
#define ET_HIOS		0xfeff
#define ET_LOPROC	0xff00
#define ET_HIPROC	0xffff

#define SHT_NULL	0
#define SHT_PROGBITS	1
#define SHT_SYMTAB	2
#define SHT_STRTAB	3
#define SHT_RELA	4
#define SHT_HASH	5
#define SHT_DYNAMIC	6
#define SHT_NOTE	7
#define SHT_NOBITS	8
#define SHT_REL		9
#define SHT_SHLIB	10
#define SHT_DYNSYM	11
#define SHT_LOOS	0x60000000
#define SHT_HIOS	0x6fffffff
#define SHT_LOPROC	0x70000000
#define SHT_HIPROC	0x7fffffff

typedef u64 Elf64_Addr;
typedef u64 Elf64_Off;
typedef u16 Elf64_Half;
typedef u32 Elf64_Word;
typedef s32 Elf64_Sword;
typedef u64 Elf64_Xword;
typedef s64 Elf64_Sxword;

/*
 * ELF file header
 */
typedef struct {
	unsigned char   e_ident[EI_NIDENT];	/* ELF identification */
	Elf64_Half      e_type;			/* Object file type */
	Elf64_Half      e_machine;		/* Machine type */
	Elf64_Word      e_version;		/* Object file version */
	Elf64_Addr      e_entry;		/* Entry point address */
	Elf64_Off       e_phoff;		/* Program header offset */
	Elf64_Off       e_shoff;		/* Section header offset */
	Elf64_Word      e_flags;		/* Processor-specific flags */
	Elf64_Half      e_ehsize;		/* ELF header size */
	Elf64_Half      e_phentsize;		/* Size of program header entry */
	Elf64_Half      e_phnum;		/* Number of program header entries */
	Elf64_Half      e_shentsize;		/* Size of section header entries */
	Elf64_Half      e_shnum;		/* Number of section header entries */
	Elf64_Half      e_shstrndx;		/* Section name string table index */
} Elf64_Ehdr;

/*
 * ELF section header
 */
typedef struct {
	Elf64_Word	sh_name;		/* Section name */
	Elf64_Word	sh_type;		/* Section type */
	Elf64_Xword	sh_flags;		/* Section attributes */
	Elf64_Addr	sh_addr;		/* Virtual address in memory */
	Elf64_Off	sh_offset;		/* Offset in file */
	Elf64_Xword	sh_size;		/* Size of section */
	Elf64_Word	sh_link;		/* Link to other section */
	Elf64_Word	sh_info;		/* Misc information */
	Elf64_Xword	sh_addralign;		/* Address alignment boundary */
	Elf64_Xword	sh_entsize;		/* Size of entries, if section has table */
} Elf64_Shdr;

static unsigned char read_ccw[8] __attribute__ ((aligned (8))) = {
	/*
	 * CCW2; read the entire system ELF
	 */

	0x02,
	/*   bits  value   name                        desc             */
	/*    0-7      2   Cmd Code                    read, no modifiers*/

	0xff, 0xff, 0xff,
	/*   bits  value   name                        desc             */
	/*   8-31   addr   Data Address                dest of the read */

	0x20,
	/*   bits  value   name                        desc             */
	/*     32      0   Chain-Data (CD)             don't chain      */
	/*     33      0   Chain-Command (CC)          don't chain      */
	/*     34      1   Sup.-Len.-Inditcation (SLI) suppress         */
	/*     35      0   Skip (SKP)                  issue read       */
	/*     36      0   Prog.-Contr.-Inter. (PCI)   don't interrupt  */
	/*     37      0   Indir.-Data-Addr. (IDA)     real addr        */
	/*     38      0   Suspend (S)                 don't suspend    */
	/*     39      0   Modified I.D.A. (MIDA)      real addr        */

	0x00,
	/*   bits  value   name                        desc             */
	/*  40-47      0   <ignored>                                    */

	0xff, 0xff,
	/*   bits  value   name                        desc             */
	/*  48-63    len   number of bytes to read                      */
};

unsigned char ORB[32] __attribute__ ((aligned (16))) = {
	/* Word 0 */
	0x12,0x34,0x56,0x78,
	/*   bits  value   name                        desc             */
	/*   0-31  magic   Interrupt Parameter                          */

	/* Word 1 */
	0x00,
	/*   bits  value   name                        desc             */
	/*    0-3      0   Subchannel Key                               */
	/*      4      0   Suspend Control                              */
	/*      5      0   Streaming-Mode Control                       */
	/*      6      0   Modification Control                         */
	/*      7      0   Synchronization Control                      */

	0x00,
	/*   bits  value   name                        desc             */
	/*      8      0   Format Control              format-0 CCWs    */
	/*      9      0   Prefetch Control                             */
	/*     10      0   Initial-Status-Interruption Control          */
	/*     11      0   Address-Limit-Checking Control               */
	/*     12      0   Suppress-Suspended-Interruption Control      */
	/*     13      0   <zero>                                       */
	/*     14      0   Format-2-IDAW Control                        */
	/*     15      0   2K-IDAW Control                              */

	0xff,
	/*   bits  value   name                        desc             */
	/*  16-23   0xff   Logical-Path Mask           All paths        */

	0x00,
	/*   bits  value   name                        desc             */
	/*     24      0   Incorrect-Length-Suppression Mode            */
	/*     25      0   Modified-CCW-Indirect-Data-Addressing Control*/
	/*  26-30      0   <zero>                                       */
	/*     31      0   ORB-Extension Control                        */

	/* Word 2 */
	0xff,0xff,0xff,0xff,
	/*   bits  value   name                        desc             */
	/*   0-31   addr   Channel-Program Address                      */

	/* Word 3 */
	0x00,
	/*   bits  value   name                        desc             */
	/*    0-7      0   Channel-Subsystem Priority                   */

	0x00,
	/*   bits  value   name                        desc             */
	/*   8-15      0   <zero/reserved>                              */

	0x00,
	/*   bits  value   name                        desc             */
	/*  15-23      0   Control-Unit Priority                        */

	0x00,
	/*   bits  value   name                        desc             */
	/*  24-31      0   <zero/reserved>                              */

	/* Word 4 */
	0x00,0x00,0x00,0x00,
	/*   bits  value   name                        desc             */
	/*   0-31      0   <zero/reserved>                              */

	/* Word 5 */
	0x00,0x00,0x00,0x00,
	/*   bits  value   name                        desc             */
	/*   0-31      0   <zero/reserved>                              */

	/* Word 6 */
	0x00,0x00,0x00,0x00,
	/*   bits  value   name                        desc             */
	/*   0-31      0   <zero/reserved>                              */

	/* Word 7 */
	0x00,0x00,0x00,0x00,
	/*   bits  value   name                        desc             */
	/*   0-31      0   <zero/reserved>                              */
};

#define memcpy(d,s,l)	__builtin_memcpy((d), (s), (l))
#define memset(s,c,n)	__builtin_memset((s),(c),(n))

/*
 * halt the cpu
 *
 * NOTE: we don't care about not clobbering registers as when this
 * code executes, the CPU will be stopped.
 */
static inline void die()
{
	asm volatile(
		"SR	%r1, %r1	# not used, but should be zero\n"
		"SR	%r3, %r3 	# CPU Address\n"
		"SIGP	%r1, %r3, 0x05	# Signal, order 0x05\n"
	);

	/*
	 * Just in case SIGP fails
	 */
	for(;;);
}

/*
 * It is easier to write this thing in assembly...
 */
extern int __do_io();
extern void PGMHANDLER();

static u64 pgm_new_psw[2] = {
	0x0000000180000000ULL, (u64) &PGMHANDLER,
};

static u64 pgm_new_psw_real[2] = {
	0x0002000180000000ULL, 0xfa11,
};

/*
 * determine amount of storage
 */
static u64 sense_memsize()
{
	u64 size;
	int cc;

#define SKIP_SIZE	(1024*1024ULL)

	/* set new PGM psw */
	memcpy((void*)0x1d0, pgm_new_psw, 16);

	for(size = 0; size < ((u64)~SKIP_SIZE)-1; size += SKIP_SIZE) {
		asm volatile(
			"lg	%%r1,%1\n"
			"tprot	0(%%r1),0\n"
			"ipm	%0\n"
			"srl    %0,28\n"
		: /* output */
		  "=d" (cc)
		: /* input */
		  "m" (size)
		: /* clobber */
		  "cc", "r1"
		);

		/*
		 * we cheat here a little...if we try to tprot a location
		 * that isn't part of the configuration, a program exception
		 * fires off, but our handler sets the CC to 3, and resumes
		 * execution
		 */
		if (cc == 3)
			break;
	}

	/* invalidate new PGM psw */
	memcpy((void*)0x1d0, pgm_new_psw_real, 16);

	return size;
}

/*
 * read the entire system into into TEMP_BASE
 */
static inline void readsystem()
{
	register unsigned long base;

	/*
	 * Read in BLOCK_SIZE chunks of system
	 */

	/* set the CCW address in the ORB */
	*((u32 *) &ORB[8]) = (u32) (u64) read_ccw;

	read_ccw[6] = ((unsigned char) (BLOCK_SIZE >> 8) & 0xff);
	read_ccw[7] = ((unsigned char) (BLOCK_SIZE & 0xff));

	base = (unsigned long) TEMP_BASE;
	for(;; base += BLOCK_SIZE) {
		read_ccw[1] = ((unsigned char) (base >> 16));
		read_ccw[2] = ((unsigned char) (base >> 8) & 0xff);
		read_ccw[3] = ((unsigned char) (base & 0xff));
		if (__do_io())
			break;
	}
}

static inline void enable_bfp()
{
	u64 cr0;

	asm volatile(
		"stctg	0,0,%0\n"
		"oi	%1,0x04\n"
		"lctlg	0,0,%0\n"
	: /* output */
	: /* input */
	  "m" (cr0),
	  "m" (*(u64*) (((u8*)&cr0) + 5))
	);
}

void load_system(void)
{
	/*
	 * These are all stored in registers
	 */
	register int i;
	register Elf64_Ehdr *system_elf;
	register Elf64_Shdr *section;
	register void (*start_sym)(u64);

	/*
	 * Read entire ELF to temporary location
	 */
	readsystem();

	system_elf = (Elf64_Ehdr*) TEMP_BASE;

	/*
	 * Check that this looks like a valid ELF
	 */
	if (system_elf->e_ident[0] != '\x7f' ||
	    system_elf->e_ident[1] != 'E' ||
	    system_elf->e_ident[2] != 'L' ||
	    system_elf->e_ident[3] != 'F' ||
	    system_elf->e_ident[EI_CLASS] != ELFCLASS64 ||
	    system_elf->e_ident[EI_DATA] != ELFDATA2MSB ||
	    system_elf->e_ident[EI_VERSION] != EV_CURRENT ||
	    system_elf->e_type != ET_EXEC ||
	    system_elf->e_machine != 0x16 || // FIXME: find the name for the #define
	    system_elf->e_version != EV_CURRENT)
		die();

	/*
	 * Iterate through each section, and copy it to the final
	 * destination as necessary
	 */
	for (i=0; i<system_elf->e_shnum; i++) {
		section = (Elf64_Shdr*) (TEMP_BASE +
					 system_elf->e_shoff +
					 system_elf->e_shentsize * i);

		switch (section->sh_type) {
			case SHT_PROGBITS:
				if (!section->sh_addr)
					break;

				/*
				 * just copy the data from TEMP_BASE to
				 * where it wants to be
				 */
				memcpy((void*) section->sh_addr,
					TEMP_BASE + section->sh_offset,
					section->sh_size);
				break;
			case SHT_NOBITS:
				/*
				 * No action needed as there's no data to
				 * copy, and we assume that the ELF sections
				 * don't overlap
				 */
				memset((void*) section->sh_addr,
				       0, section->sh_size);
				break;
			default:
				/* Ignoring */
				break;
		}
	}

	enable_bfp();

	/*
	 * Now, jump to the system entry point
	 */
	start_sym = (void*) system_elf->e_entry;
	start_sym(sense_memsize());
}