diff options
Diffstat (limited to 'roms/openbios/arch/sparc64')
28 files changed, 5543 insertions, 0 deletions
diff --git a/roms/openbios/arch/sparc64/boot.c b/roms/openbios/arch/sparc64/boot.c new file mode 100644 index 00000000..5107be6c --- /dev/null +++ b/roms/openbios/arch/sparc64/boot.c @@ -0,0 +1,130 @@ +/* + * + */ +#undef BOOTSTRAP +#include "config.h" +#include "libopenbios/bindings.h" +#include "arch/common/nvram.h" +#include "libc/diskio.h" +#include "libc/vsprintf.h" +#include "libopenbios/sys_info.h" +#include "boot.h" + +uint64_t kernel_image; +uint64_t kernel_size; +uint64_t qemu_cmdline; +uint64_t cmdline_size; +char boot_device; + +extern int sparc64_of_client_interface( int *params ); + + +void go(void) +{ +	ucell address, type, size; +	int image_retval = 0; + +	/* Get the entry point and the type (see forth/debugging/client.fs) */ +	feval("saved-program-state >sps.entry @"); +	address = POP(); +	feval("saved-program-state >sps.file-type @"); +	type = POP(); +	feval("saved-program-state >sps.file-size @"); +	size = POP(); + +	printk("\nJumping to entry point " FMT_ucellx " for type " FMT_ucellx "...\n", address, type); + +	switch (type) { +		case 0x0: +			/* Start ELF boot image */ +			image_retval = start_elf(address, (uint64_t)&elf_boot_notes); +			break; + +		case 0x1: +			/* Start ELF image */ +			image_retval = start_client_image(address, (uint64_t)&sparc64_of_client_interface); +			break; + +		case 0x5: +			/* Start a.out image */ +			image_retval = start_client_image(address, (uint64_t)&sparc64_of_client_interface); +			break; + +		case 0x10: +			/* Start Fcode image */ +			printk("Evaluating FCode...\n"); +			PUSH(address); +			PUSH(1); +			fword("byte-load"); +			image_retval = 0; +			break; + +		case 0x11: +			/* Start Forth image */ +			PUSH(address); +			PUSH(size); +			fword("eval2"); +			image_retval = 0; +			break; +	} + +	printk("Image returned with return value %#x\n", image_retval); +} + +/* ( path len -- path len ) */ + +void boot(void) +{ +	char *path, *param; + +	/* Copy the incoming path */ +	fword("2dup"); +	path = pop_fstr_copy(); + +	/* Boot preloaded kernel */ +        if (kernel_size) { +            void (*entry)(unsigned long p1, unsigned long p2, unsigned long p3, +                          unsigned long p4, unsigned long p5); + +            printk("[sparc64] Kernel already loaded\n"); +            entry = (void *) (unsigned long)kernel_image; +            entry(0, 0, 0, 0, (unsigned long)&sparc64_of_client_interface); +        } + +	/* Invoke Linux directly -- probably not supported */ +	if(!path) { +            /* No path specified, so grab defaults from /chosen */ +            push_str("bootpath"); +	    push_str("/chosen"); +            fword("(find-dev)"); +            POP(); +            fword("get-package-property"); +            POP(); +	    /* Update our local copy of path as well as the one on the stack */ +	    fword("2dup"); +            path = pop_fstr_copy(); +	} + +        if (path) { +            param = strchr(path, ' '); +            if(param) { +                *param = '\0'; +                param++; +            } else if (cmdline_size) { +                param = (char *)qemu_cmdline; +            } else { +                push_str("boot-args"); +                push_str("/options"); +                fword("(find-dev)"); +                POP(); +                fword("get-package-property"); +                POP(); +                param = pop_fstr_copy(); +            } + +            /* Invoke platform-specific Linux loader */ +            linux_load(&sys_info, path, param); + +            free(path); +        } +} diff --git a/roms/openbios/arch/sparc64/boot.h b/roms/openbios/arch/sparc64/boot.h new file mode 100644 index 00000000..3ab05e0a --- /dev/null +++ b/roms/openbios/arch/sparc64/boot.h @@ -0,0 +1,35 @@ +/* tag: openbios loader prototypes for sparc64 + * + * Copyright (C) 2004 Stefan Reinauer + * + * See the file "COPYING" for further information about + * the copyright and warranty status of this work. + */ + +// linux_load.c +int linux_load(struct sys_info *info, const char *file, const char *cmdline); + +// context.c +extern struct context * volatile __context; +uint64_t start_elf(uint64_t entry_point, uint64_t param); +uint64_t start_client_image(uint64_t entry_point, uint64_t cif_handler); + +// boot.c +extern uint64_t kernel_image; +extern uint64_t kernel_size; +extern uint64_t qemu_cmdline; +extern uint64_t cmdline_size; +extern char boot_device; +extern void boot(void); +extern void go(void); + +// sys_info.c +extern uint64_t qemu_mem_size; +extern void collect_sys_info(struct sys_info *info); + +// console.c +void ob_su_init(uint64_t base, uint64_t offset, int intr); +void cls(void); + +// lib.c +void ob_mmu_init(const char *cpuname, uint64_t ram_size); diff --git a/roms/openbios/arch/sparc64/build.xml b/roms/openbios/arch/sparc64/build.xml new file mode 100644 index 00000000..3a1cd346 --- /dev/null +++ b/roms/openbios/arch/sparc64/build.xml @@ -0,0 +1,72 @@ +<build condition="SPARC64"> + + <dictionary name="openbios-sparc64" init="openbios"> +  <object source="tree.fs" target="forth"/> +  <object source="init.fs" target="forth"/> +  <object source="QEMU,VGA.bin" target="fcode" condition="DRIVER_VGA"/> + </dictionary> + + <library name="sparc64" type="static" target="target"> +  <object source="openbios.c"/> +  <object source="console.c"/> +  <object source="lib.c"/> +  <object source="boot.c"/> +  <object source="context.c"/> +  <object source="switch.S"/> +  <object source="linux_load.c"/> +  <object source="sys_info.c"/> +  <object source="ofmem_sparc64.c"/> +  <object source="entry.S"/> +  <object source="vectors.S"/> +  <object source="call-client.S"/> + </library> + + <executable name="openbios-plain.elf" target="target" condition="IMAGE_ELF"> +  <rule> +	$(call quiet-command,$(LD) --warn-common -T $(SRCDIR)/arch/sparc64/ldscript -o $@.nostrip --whole-archive $^,"  LINK  $(TARGET_DIR)$@") +	$(call quiet-command,$(NM) $@.nostrip | sort > $(ODIR)/openbios-plain.syms,"  GEN   $(TARGET_DIR)$@.syms") +	$(call quiet-command,$(STRIP) $@.nostrip -o $@,"  STRIP $(TARGET_DIR)$@")</rule> +  <object source="plainboot.c"/> +  <external-object source="libsparc64.a"/> +  <external-object source="libbootstrap.a"/> +  <external-object source="libopenbios.a"/> +  <external-object source="libpackages.a"/> +  <external-object source="libdrivers.a"/> +  <external-object source="libfs.a"/> +  <external-object source="liblibc.a"/> +  <external-object source="libgcc.a"/> + </executable> + + <!-- HACK ALERT --> + + <executable name="target/include/static-dict.h" target="target" condition="IMAGE_ELF_EMBEDDED"> +  <rule><![CDATA[ +	$(call quiet-command,$(ODIR)/forthstrap -x -D $@ -d $< </dev/null, "  GEN   $(TARGET_DIR)$@")]]></rule> +  <external-object source="openbios-sparc64.dict"/> + </executable> + + <executable name="target/arch/sparc64/builtin.o" target="target" condition="IMAGE_ELF_EMBEDDED"> +  <rule><![CDATA[ $(SRCDIR)/arch/sparc64/builtin.c $(ODIR)/target/include/static-dict.h +	$(call quiet-command,$(CC) $$EXTRACFLAGS $(CFLAGS) $(INCLUDES) -c -o $@ $(SRCDIR)/arch/sparc64/builtin.c, "  CC    $(TARGET_DIR)$@")]]></rule> + </executable> + + <!-- END OF HACK ALERT --> + + <executable name="openbios-builtin.elf" target="target" condition="IMAGE_ELF_EMBEDDED"> + <!-- We use -N to reduce the file size by 1M --> +  <rule> +	$(call quiet-command,$(LD) --warn-common -N -T $(SRCDIR)/arch/sparc64/ldscript -o $@.nostrip --whole-archive $^,"  LINK  $(TARGET_DIR)$@") +	$(call quiet-command,$(NM) $@.nostrip | sort > $(ODIR)/openbios-builtin.syms,"  GEN   $(TARGET_DIR)$@.syms") +	$(call quiet-command,$(STRIP) $@.nostrip -o $@,"  STRIP $(TARGET_DIR)$@")</rule> +  <external-object source="target/arch/sparc64/builtin.o"/> +  <external-object source="libsparc64.a"/> +  <external-object source="libbootstrap.a"/> +  <external-object source="libopenbios.a"/> +  <external-object source="libpackages.a"/> +  <external-object source="libdrivers.a"/> +  <external-object source="libfs.a"/> +  <external-object source="liblibc.a"/> +  <external-object source="libgcc.a"/> + </executable> + +</build> diff --git a/roms/openbios/arch/sparc64/builtin.c b/roms/openbios/arch/sparc64/builtin.c new file mode 100644 index 00000000..864da797 --- /dev/null +++ b/roms/openbios/arch/sparc64/builtin.c @@ -0,0 +1,33 @@ +/* tag: openbios forth starter for builtin dictionary for sparc64 + * + * Copyright (C) 2003 Stefan Reinauer + * + * See the file "COPYING" for further information about + * the copyright and warranty status of this work. + */ + +#include "config.h" +#include "asm/types.h" +#include "libopenbios/sys_info.h" + +/* + * wrap an array around the hex'ed dictionary file + */ + +/* 512K for the dictionary */ +#define DICTIONARY_SIZE (512 * 1024 / sizeof(ucell)) +#define DICTIONARY_BASE ((ucell)((char *)&forth_dictionary)) + +static ucell forth_dictionary[DICTIONARY_SIZE] = { +#include "static-dict.h" +}; + +void collect_multiboot_info(struct sys_info *info); +void collect_multiboot_info(struct sys_info *info) +{ +	info->dict_start=(unsigned long *)forth_dictionary; +        info->dict_end = (unsigned long *)FORTH_DICTIONARY_END; +        info->dict_last = (ucell *)((unsigned char *)forth_dictionary + +                                            FORTH_DICTIONARY_LAST); +        info->dict_limit = sizeof(forth_dictionary); +} diff --git a/roms/openbios/arch/sparc64/call-client.S b/roms/openbios/arch/sparc64/call-client.S new file mode 100644 index 00000000..f365e3cb --- /dev/null +++ b/roms/openbios/arch/sparc64/call-client.S @@ -0,0 +1,236 @@ +	.globl	sparc64_of_client_interface, client_tba + + +/* + * SAVE_WINDOW_STATE and RESTORE_WINDOW_STATE are used to ensure + * that the CPU window state is preserved across CIF calls. This is + * to workaround a *BSD restriction that window fill/spill traps must + * be minimised during trap table takeover, and likely emulates the + * behaviour of OBP. + */ + +#define SAVE_WINDOW_STATE(type) \ +	setx	client_window, %g6, %g1; \ +	rdpr	%cwp, %g7; \ +	stx	%g7, [%g1]; \ +	rdpr	%cansave, %g7; \ +	stx	%g7, [%g1 + 0x8]; \ +	rdpr	%canrestore, %g7; \ +	stx	%g7, [%g1 + 0x10]; \ +	rdpr	%otherwin, %g7; \ +	stx	%g7, [%g1 + 0x18]; \ +	rdpr	%wstate, %g7; \ +	stx	%g7, [%g1 + 0x20]; \ +	rdpr	%cleanwin, %g7; \ +	stx	%g7, [%g1 + 0x28]; \ +	\ +	stx	%o0, [%g1 + 0x30]; \ +	stx	%o1, [%g1 + 0x38]; \ +	stx	%o2, [%g1 + 0x40]; \ +	stx	%o3, [%g1 + 0x48]; \ +	stx	%o4, [%g1 + 0x50]; \ +	stx	%o5, [%g1 + 0x58]; \ +	stx	%o6, [%g1 + 0x60]; \ +	stx	%o7, [%g1 + 0x68]; \ +	\ +	rdpr	%pstate, %g7; \ +	stx	%g7, [%g1 + 0x70]; \ +	rd	%y, %g7; \ +	stx	%g7, [%g1 + 0x78]; \ +	rd	%fprs, %g7; \ +	stx	%g7, [%g1 + 0x80]; \ +	\ +	/* Now iterate through all of the windows saving all l and i registers */ \ +	add	%g1, 0x90, %g5; \ +	\ +	/* Get the number of windows in %g6 */ \ +	rdpr	%ver, %g6; \ +	and	%g6, 0xf, %g6; \ +	inc	%g6; \ +	\ +save_cpu_window_##type: \ +	deccc	%g6; \ +	wrpr	%g6, %cwp; \ +	stx	%l0, [%g5]; \ +	stx	%l1, [%g5 + 0x8]; \ +	stx	%l2, [%g5 + 0x10]; \ +	stx	%l3, [%g5 + 0x18]; \ +	stx	%l4, [%g5 + 0x20]; \ +	stx	%l5, [%g5 + 0x28]; \ +	stx	%l6, [%g5 + 0x30]; \ +	stx	%l7, [%g5 + 0x38]; \ +	stx	%i0, [%g5 + 0x40]; \ +	stx	%i1, [%g5 + 0x48]; \ +	stx	%i2, [%g5 + 0x50]; \ +	stx	%i3, [%g5 + 0x58]; \ +	stx	%i4, [%g5 + 0x60]; \ +	stx	%i5, [%g5 + 0x68]; \ +	stx	%i6, [%g5 + 0x70]; \ +	stx	%i7, [%g5 + 0x78]; \ +	bne	save_cpu_window_##type; \ +	 add	%g5, 0x80, %g5; \ +	\ +	/* For 8 windows with 16 registers to save in the window, memory required \ +	is 16*8*8 = 0x400 bytes */ \ +	\ +	/* Now we should be in window 0 so update the other window registers */ \ +	rdpr	%ver, %g6; \ +	and	%g6, 0xf, %g6; \ +	dec	%g6; \ +	wrpr	%g6, %cansave; \ +	\ +	wrpr	%g0, %cleanwin; \ +	wrpr	%g0, %canrestore; \ +	wrpr	%g0, %otherwin; + + +#define RESTORE_WINDOW_STATE(type) \ +	setx	client_window, %g6, %g1; \ +	\ +	/* Get the number of windows in %g6 */ \ +	rdpr	%ver, %g6; \ +	and	%g6, 0xf, %g6; \ +	inc	%g6; \ +	\ +	/* Now iterate through all of the windows restoring all l and i registers */ \ +	add	%g1, 0x90, %g5; \ +	\ +restore_cpu_window_##type: \ +	deccc	%g6; \ +	wrpr	%g6, %cwp; \ +	ldx	[%g5], %l0; \ +	ldx	[%g5 + 0x8], %l1; \ +	ldx	[%g5 + 0x10], %l2; \ +	ldx	[%g5 + 0x18], %l3; \ +	ldx	[%g5 + 0x20], %l4; \ +	ldx	[%g5 + 0x28], %l5; \ +	ldx	[%g5 + 0x30], %l6; \ +	ldx	[%g5 + 0x38], %l7; \ +	ldx	[%g5 + 0x40], %i0; \ +	ldx	[%g5 + 0x48], %i1; \ +	ldx	[%g5 + 0x50], %i2; \ +	ldx	[%g5 + 0x58], %i3; \ +	ldx	[%g5 + 0x60], %i4; \ +	ldx	[%g5 + 0x68], %i5; \ +	ldx	[%g5 + 0x70], %i6; \ +	ldx	[%g5 + 0x78], %i7; \ +	bne	restore_cpu_window_##type; \ +	 add	%g5, 0x80, %g5; \ +	\ +	/* Restore the window registers to their original value */ \ +	ldx	[%g1], %g7; \ +	wrpr	%g7, %cwp; \ +	ldx	[%g1 + 0x8], %g7; \ +	wrpr	%g7, %cansave; \ +	ldx	[%g1 + 0x10], %g7; \ +	wrpr	%g7, %canrestore; \ +	ldx	[%g1 + 0x18], %g7; \ +	wrpr	%g7, %otherwin; \ +	ldx	[%g1 + 0x20], %g7; \ +	wrpr	%g7, %wstate; \ +	ldx	[%g1 + 0x28], %g7; \ +	wrpr	%g7, %cleanwin; \ +	\ +	ldx	[%g1 + 0x30], %o0; \ +	ldx	[%g1 + 0x38], %o1; \ +	ldx	[%g1 + 0x40], %o2; \ +	ldx	[%g1 + 0x48], %o3; \ +	ldx	[%g1 + 0x50], %o4; \ +	ldx	[%g1 + 0x58], %o5; \ +	ldx	[%g1 + 0x60], %o6; \ +	ldx	[%g1 + 0x68], %o7; \ +	\ +	ldx	[%g1 + 0x70], %g7; \ +	wrpr	%g7, %pstate; \ +	ldx	[%g1 + 0x78], %g7; \ +	wr	%g7, 0, %y; \ +	ldx	[%g1 + 0x80], %g7; \ +	wr	%g7, 0, %fprs + + +	.data +	.align	8 + +	.skip	16384 +openbios_stack: + +client_stack: +	.xword	0 +client_tba: +	.xword	0 +client_window: +	.skip	2048 + + +	.text +	.align	4 +        .register %g2, #scratch +        .register %g3, #scratch +        .register %g6, #scratch +        .register %g7, #scratch +/* +	make some more space on stack since linux kernel only provides 128 bytes +	without memory to spill registers (used by gcc in -O0 mode) +*/ + +sparc64_of_client_interface: + +	/* Save globals on callers stack */ +	add	%sp, -56, %sp + +	stx	%g1, [%sp + 2047 + 0] +	stx	%g2, [%sp + 2047 + 8] +	stx	%g3, [%sp + 2047 + 16] +	stx	%g4, [%sp + 2047 + 24] +	stx	%g5, [%sp + 2047 + 32] +	stx	%g6, [%sp + 2047 + 40] +	stx	%g7, [%sp + 2047 + 48] + +	/* Save client trap table */ +	setx	client_tba, %g6, %g7 +	rdpr	%tba, %g6 +	stx	%g6, [%g7] + +	/* Save existing stack */ +	setx	client_stack, %g6, %g7 +	stx	%sp, [%g7] + +	/* Save windows */ +	SAVE_WINDOW_STATE(cif) + +	/* Move to OpenBIOS stack */ +	setx	openbios_stack - 2047 - 192, %g6, %g7 +	mov	%g7, %sp + +	/* Call client inteface */ +	call of_client_interface +	 ldx	[%g1 + 0x30], %o0 + +	setx	client_window, %g6, %g1 +	stx	%o0, [%g1 + 0x30] + +	/* Restore windows */ +	RESTORE_WINDOW_STATE(cif) + +	/* Restore stack */ +	setx	client_stack, %g6, %g7 +	ldx	[%g7], %sp + +	/* Restore client trap table */ +	setx	client_tba, %g6, %g7 +	ldx	[%g7], %g6 +	wrpr	%g6, %tba + +	/* Restore globals */ +	ldx	[%sp + 2047 + 0], %g1 +	ldx	[%sp + 2047 + 8], %g2 +	ldx	[%sp + 2047 + 16], %g3 +	ldx	[%sp + 2047 + 24], %g4 +	ldx	[%sp + 2047 + 32], %g5 +	ldx	[%sp + 2047 + 40], %g6 +	ldx	[%sp + 2047 + 48], %g7 + +	add	%sp, 56, %sp + +	jmp	%o7+8 +	 nop diff --git a/roms/openbios/arch/sparc64/console.c b/roms/openbios/arch/sparc64/console.c new file mode 100644 index 00000000..6ab5cba4 --- /dev/null +++ b/roms/openbios/arch/sparc64/console.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2003, 2004 Stefan Reinauer + * + * See the file "COPYING" for further information about + * the copyright and warranty status of this work. + */ + +#include "config.h" +#include "libopenbios/bindings.h" +#include "libopenbios/console.h" +#include "kernel/kernel.h" +#include "drivers/drivers.h" +#include "libopenbios/fontdata.h" +#include "openbios.h" +#include "libc/vsprintf.h" +#include "libopenbios/sys_info.h" +#include "boot.h" + +/* ****************************************************************** + *          simple polling video/keyboard console functions + * ****************************************************************** */ + +#ifdef CONFIG_DEBUG_CONSOLE +/* ****************************************************************** + *      common functions, implementing simple concurrent console + * ****************************************************************** */ + +static int arch_putchar(int c) +{ +#ifdef CONFIG_DEBUG_CONSOLE_SERIAL +	uart_putchar(c); +#endif +	return c; +} + +static int arch_availchar(void) +{ +#ifdef CONFIG_DEBUG_CONSOLE_SERIAL +	if (uart_charav(CONFIG_SERIAL_PORT)) +		return 1; +#endif +#ifdef CONFIG_DEBUG_CONSOLE_VGA +        if (pc_kbd_dataready()) +		return 1; +#endif +	return 0; +} + +static int arch_getchar(void) +{ +#ifdef CONFIG_DEBUG_CONSOLE_SERIAL +	if (uart_charav(CONFIG_SERIAL_PORT)) +		return (uart_getchar(CONFIG_SERIAL_PORT)); +#endif +#ifdef CONFIG_DEBUG_CONSOLE_VGA +        if (pc_kbd_dataready()) +                return (pc_kbd_readdata()); +#endif +	return 0; +} + +struct _console_ops arch_console_ops = { +	.putchar = arch_putchar, +	.availchar = arch_availchar, +	.getchar = arch_getchar +}; + +#endif // CONFIG_DEBUG_CONSOLE diff --git a/roms/openbios/arch/sparc64/const.h b/roms/openbios/arch/sparc64/const.h new file mode 100644 index 00000000..8ad902b2 --- /dev/null +++ b/roms/openbios/arch/sparc64/const.h @@ -0,0 +1,19 @@ +/* const.h: Macros for dealing with constants.  */ + +#ifndef _SPARC64_CONST_H +#define _SPARC64_CONST_H + +/* Some constant macros are used in both assembler and + * C code.  Therefore we cannot annotate them always with + * 'UL' and other type specificers unilaterally.  We + * use the following macros to deal with this. + */ + +#ifdef __ASSEMBLY__ +#define _AC(X,Y)	X +#else +#define _AC(X,Y)	(X##Y) +#endif + + +#endif /* !(_SPARC64_CONST_H) */ diff --git a/roms/openbios/arch/sparc64/context.c b/roms/openbios/arch/sparc64/context.c new file mode 100644 index 00000000..2e766895 --- /dev/null +++ b/roms/openbios/arch/sparc64/context.c @@ -0,0 +1,129 @@ +/* + * context switching + * 2003-10 by SONE Takeshi + */ + +#include "config.h" +#include "kernel/kernel.h" +#include "context.h" +#include "libopenbios/sys_info.h" +#include "boot.h" +#include "openbios.h" + +#define MAIN_STACK_SIZE 16384 +#define IMAGE_STACK_SIZE 4096*4 + +#define debug printk + +static void start_main(void); /* forward decl. */ +void __exit_context(void); /* assembly routine */ + +/* + * Main context structure + * It is placed at the bottom of our stack, and loaded by assembly routine + * to start us up. + */ +static struct context main_ctx = { +    .regs[REG_SP] = (uint64_t) &_estack - STACK_BIAS - 96, +    .pc = (uint64_t) start_main, +    .npc = (uint64_t) start_main + 4, +    .return_addr = (uint64_t) __exit_context, +}; + +/* This is used by assembly routine to load/store the context which + * it is to switch/switched.  */ +struct context * volatile __context = &main_ctx; + +/* Stack for loaded ELF image */ +static uint8_t image_stack[IMAGE_STACK_SIZE]; + +/* Pointer to startup context (physical address) */ +unsigned long __boot_ctx; + +/* + * Main starter + * This is the C function that runs first. + */ +static void start_main(void) +{ +    /* Save startup context, so we can refer to it later. +     * We have to keep it in physical address since we will relocate. */ +    __boot_ctx = virt_to_phys(__context); + +    /* Start the real fun */ +    openbios(); + +    /* Returning from here should jump to __exit_context */ +    __context = boot_ctx; +} + +static uint64_t ALIGN_SIZE(uint64_t x, uint64_t a) +{ +    return (x + a - 1) & ~(a-1); +} + +/* Setup a new context using the given stack. + */ +struct context * +init_context(uint8_t *stack, uint64_t stack_size, int num_params) +{ +    struct context *ctx; +    uint8_t *stack_top = stack + stack_size; + +    ctx = (struct context *) +	(stack_top - ALIGN_SIZE(sizeof(*ctx) + num_params*sizeof(uint64_t), sizeof(uint64_t))); +    memset(ctx, 0, sizeof(*ctx)); + +    /* Fill in reasonable default for flat memory model */ +    ctx->regs[REG_SP] = virt_to_phys(stack_top - STACK_BIAS - 192); +    ctx->return_addr = virt_to_phys(__exit_context); + +    return ctx; +} + +/* Switch to another context. */ +struct context *switch_to(struct context *ctx) +{ +    struct context *save, *ret; + +    debug("switching to new context: entry point %#llx stack 0x%016llx\n", ctx->pc, ctx->regs[REG_SP]); +    save = __context; +    __context = ctx; +    //asm ("pushl %cs; call __switch_context"); +    asm ("call __switch_context_nosave; nop"); +    ret = __context; +    __context = save; +    return ret; +} + +/* Start ELF Boot image */ +uint64_t start_elf(uint64_t entry_point, uint64_t param) +{ +    struct context *ctx; + +    ctx = init_context(image_stack, sizeof image_stack, 1); +    ctx->pc = entry_point; +    ctx->param[0] = param; +    //ctx->eax = 0xe1fb007; +    //ctx->ebx = param; + +    ctx = switch_to(ctx); +    //return ctx->eax; +    return 0; +} + +/* Start client image */ +uint64_t start_client_image(uint64_t entry_point, uint64_t cif_handler) +{ +    struct context *ctx; + +    ctx = init_context(image_stack, sizeof image_stack, 0); +    ctx->pc  = entry_point; +    ctx->npc = entry_point+4; +    ctx->regs[REG_O0] = 0; +    ctx->regs[REG_O0+4] = cif_handler; + +    ctx = switch_to(ctx); + +    return 0; +} diff --git a/roms/openbios/arch/sparc64/context.h b/roms/openbios/arch/sparc64/context.h new file mode 100644 index 00000000..2756fa15 --- /dev/null +++ b/roms/openbios/arch/sparc64/context.h @@ -0,0 +1,33 @@ +#ifndef SPARC64_CONTEXT_H +#define SPARC64_CONTEXT_H + +#define STACK_BIAS             2047 + +struct context { +    /* General registers */ +    uint64_t regs[32]; +    uint64_t pc; +    uint64_t npc; +#define REG_O0 8 +#define REG_SP 14 +#define SP_LOC(ctx) (&(ctx)->regs[REG_SP]) +    /* Flags */ +    /* Optional stack contents */ +    uint64_t return_addr; +    uint64_t param[0]; +}; + +/* Create a new context in the given stack */ +struct context * +init_context(uint8_t *stack, uint64_t stack_size, int num_param); + +/* Switch context */ +struct context *switch_to(struct context *); + +/* Holds physical address of boot context */ +extern unsigned long __boot_ctx; + +/* This can always be safely used to refer to the boot context */ +#define boot_ctx ((struct context *) phys_to_virt(__boot_ctx)) + +#endif /* SPARC64_CONTEXT_H */ diff --git a/roms/openbios/arch/sparc64/entry.S b/roms/openbios/arch/sparc64/entry.S new file mode 100644 index 00000000..d03128af --- /dev/null +++ b/roms/openbios/arch/sparc64/entry.S @@ -0,0 +1,287 @@ +/** + ** Standalone startup code for Linux PROM emulator. + ** Copyright 1999 Pete A. Zaitcev + ** This code is licensed under GNU General Public License. + **/ +/* + * $Id: head.S,v 1.12 2002/07/23 05:47:09 zaitcev Exp $ + */ + +#define __ASSEMBLY__ +#include <asm/asi.h> +#include "pstate.h" +#include "lsu.h" +#define NO_QEMU_PROTOS +#define NO_OPENBIOS_PROTOS +#include "arch/common/fw_cfg.h" + +#define PROM_ADDR 0x1fff0000000 +#define CFG_ADDR  0x1fe02000510 +#define HZ        1 * 1000 * 1000 +#define TICK_INT_DIS 0x8000000000000000 + +        .globl	entry, _entry + +	.section ".text", "ax" +	.align	8 +        .register %g2, #scratch +        .register %g3, #scratch +        .register %g6, #scratch +        .register %g7, #scratch + +/* + * Entry point + * We start execution from here. + */ +_entry: +entry: +        ! Set up CPU state +        wrpr    %g0, PSTATE_PRIV, %pstate +        wr      %g0, 0, %fprs +        wrpr    %g0, 0x0, %tl + +        ! Extract NWINDOWS from %ver +        rdpr    %ver, %g1 +        and     %g1, 0xf, %g1 +        dec     %g1 +        wrpr    %g1, 0, %cleanwin +        wrpr    %g1, 0, %cansave +        wrpr    %g0, 0, %canrestore +        wrpr    %g0, 0, %otherwin +        wrpr    %g0, 0, %wstate +        ! disable timer now +        setx    TICK_INT_DIS, %g2, %g1 +        wr      %g1, 0, %tick_cmpr + +        ! Disable I/D MMUs and caches +        stxa    %g0, [%g0] ASI_LSU_CONTROL + +        ! Check signature "QEMU" +        setx    CFG_ADDR, %g2, %g5 +        mov     FW_CFG_SIGNATURE, %g2 +        stha    %g2, [%g5] ASI_PHYS_BYPASS_EC_E_L +        inc     %g5 +        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g2 +        cmp     %g2, 'Q' +        bne     bad_conf +         nop +        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g2 +        cmp     %g2, 'E' +        bne     bad_conf +         nop +        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g2 +        cmp     %g2, 'M' +        bne     bad_conf +         nop +        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g2 +        cmp     %g2, 'U' +        bne     bad_conf +         nop + +        ! Clear ITLB +        mov     6 << 3, %g1 +        stxa    %g0, [%g1] ASI_IMMU +        stxa    %g0, [%g1] ASI_DMMU +        mov     63 << 3, %g1 +1:      stxa    %g0, [%g1] ASI_ITLB_DATA_ACCESS +        subcc   %g1, 1 << 3, %g1 +        bpos    1b +         nop + +        ! Clear DTLB +        mov     63 << 3, %g1 +1:      stxa    %g0, [%g1] ASI_DTLB_DATA_ACCESS +        subcc   %g1, 1 << 3, %g1 +        bpos    1b +         nop + +        ! Get memory size from configuration device +        ! NB: little endian format +        mov     FW_CFG_RAM_SIZE, %g2 +        dec     %g5 +        stha    %g2, [%g5] ASI_PHYS_BYPASS_EC_E_L +        inc     %g5 +        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g4 + +        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g3 +        sllx    %g3, 8, %g3 +        or      %g3, %g4, %g4 + +        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g3 +        sllx    %g3, 16, %g3 +        or      %g3, %g4, %g4 + +        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g3 +        sllx    %g3, 24, %g3 +        or      %g3, %g4, %g4 + +        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g3 +        sllx    %g3, 32, %g3 +        or      %g3, %g4, %g4 + +        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g3 +        sllx    %g3, 40, %g3 +        or      %g3, %g4, %g4 + +        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g3 +        sllx    %g3, 48, %g3 +        or      %g3, %g4, %g4 + +        lduba   [%g5] ASI_PHYS_BYPASS_EC_E, %g3 +        sllx    %g3, 56, %g3 +        or      %g3, %g4, %g1 +        ! %g1 contains end of memory + +        setx    _end, %g7, %g3 +        set     0x7ffff, %g2 +        add     %g3, %g2, %g3 +        andn    %g3, %g2, %g3 +        setx    _data, %g7, %g2 +        sub     %g3, %g2, %g2 +        sub	%g1, %g2, %g2			! %g2 = start of private memory +        mov     %g2, %l0 + +	! setup .data & .bss +        setx    _data, %g7, %g4 +        sub     %g3, %g4, %g5 +        srlx    %g5, 19, %g6			! %g6 = # of 512k .bss pages +	set	0xc0000000, %g3 +	sllx	%g3, 32, %g3 +	or	%g3, 0x76, %g3 +	! valid, 512k, locked, cacheable(I/E/C), priv, writable +	set	48, %g7 +1:	stxa	%g4, [%g7] ASI_DMMU		! vaddr = _data + N * 0x80000, ctx=0 +	or	%g2, %g3, %g5 +	! paddr = start_mem + N * 0x80000 +	stxa	%g5, [%g0] ASI_DTLB_DATA_IN +	set	0x80000, %g5 +	add	%g2, %g5, %g2 +	add	%g4, %g5, %g4 +	deccc	%g6 +	bne	1b +	 nop + +	! setup .rodata, also make .text readable +	setx	_data, %g7, %g5 +	setx	_start, %g7, %g4 +        sub     %g5, %g4, %g5 +	srlx    %g5, 19, %g6			! %g6 = # of 512k .rodata pages +	set	48, %g7 +	set	0x80000, %g5 +        setx    PROM_ADDR, %l1, %l2 +1:      stxa	%g4, [%g7] ASI_DMMU		! vaddr = _rodata, ctx=0 +	set	0xc0000000, %g3 +	sllx	%g3, 32, %g3 +	or	%g3, 0x74, %g3 +	or	%l2, %g3, %g3 +	! valid, 512k, locked, cacheable(I/E/C), priv +	! paddr = _rodata + N * 0x10000 +	stxa	%g3, [%g0] ASI_DTLB_DATA_IN +	add	%g4, %g5, %g4 +	deccc	%g6 +	bne	1b +	 add	%l2, %g5, %l2 + +	membar	#Sync + +	setx	_start, %g7, %g4 +	setx	_rodata, %g7, %g5 +        sub     %g5, %g4, %g5 +	set 0x7ffff, %g7 +	add %g5, %g7, %g5					! round to 512k +        srlx    %g5, 19, %g6			! %g6 = # of 512k .text pages +	set	0x80000, %g5 +	set	48, %g7 +        setx    PROM_ADDR, %l1, %l2 +1:      stxa	%g4, [%g7] ASI_IMMU		! vaddr = _start, ctx=0 +	set	0xc0000000, %g3 +	sllx	%g3, 32, %g3 +	or	%g3, 0x74, %g3 +	or	%l2, %g3, %g3 +	! valid, 512k, locked, cacheable(I/E/C), priv +	! paddr = _start + N * 0x80000 +	stxa	%g3, [%g0] ASI_ITLB_DATA_IN +	add	%g4, %g5, %g4 +	deccc	%g6 +	bne	1b +	 add	%l2, %g5, %l2 + +	flush	%g4 + +	mov	%g1, %g3 + +	set     8, %g2 +	sta     %g0, [%g2] ASI_DMMU		! set primary ctx=0 + +	! Enable I/D MMUs and caches +        setx    lowmem, %g2, %g1 +	set	LSU_CONTROL_DM|LSU_CONTROL_IM|LSU_CONTROL_DC|LSU_CONTROL_IC, %g2 +        jmp     %g1 +         stxa	%g2, [%g0] ASI_LSU_CONTROL + +lowmem: +        /* Copy the DATA section from ROM. */ +        setx	_data - 8, %o7, %o0             ! First address of DATA +        setx	_bss, %o7, %o1                  ! Last address of DATA +        setx    _start, %o7, %o2 +        sub     %o0, %o2, %o2                   ! _data - _start +        setx    PROM_ADDR, %o7, %o3 +        add     %o3, %o2, %o2                   ! PROM_ADDR + (_data - _start) +        ba	2f +         nop +1: +        ldxa    [%o2] ASI_PHYS_BYPASS_EC_E, %g1 +        stx	%g1, [%o0] +2: +        add	%o2, 0x8, %o2 +        subcc	%o0, %o1, %g0 +        bl	1b +         add	%o0, 0x8, %o0 + +        /* Zero out our BSS section. */ +        setx    _bss - 8, %o7, %o0              ! First address of BSS +        setx    _end - 8, %o7, %o1                  ! Last address of BSS +        ba	2f +         nop +1: +        stx     %g0, [%o0] +2: +        subcc	%o0, %o1, %g0 +        bl	1b +         add	%o0, 0x8, %o0 + +        setx    trap_table, %g2, %g1 +        wrpr    %g1, %tba + +        setx    qemu_mem_size, %g7, %g1 +        stx     %g3, [%g1] + +        setx    _data, %g7, %g1                 ! Store va->pa conversion factor +        sub     %g1, %l0, %g2 +        setx    va_shift, %g7, %g1 +        stx     %g2, [%g1] + +        /* Finally, turn on traps so that we can call c-code. */ +	wrpr    %g0, (PSTATE_PRIV|PSTATE_PEF|PSTATE_IE), %pstate + +        ! 100 Hz timer +        setx    TICK_INT_DIS, %g2, %g1 +        rd      %tick, %g2 +        andn    %g2, %g1, %g2 +        set     HZ, %g1 +        add     %g1, %g2, %g1 +        wr      %g1, 0, %tick_cmpr + +        /* Switch to our main context. +         * Main context is statically defined in C. +         */ + +        call    __switch_context_nosave +         nop + +        /* We get here when the main context switches back to +         * the boot context. +         */ +bad_conf: +        b       bad_conf +         nop diff --git a/roms/openbios/arch/sparc64/init.fs b/roms/openbios/arch/sparc64/init.fs new file mode 100644 index 00000000..eb6c9da5 --- /dev/null +++ b/roms/openbios/arch/sparc64/init.fs @@ -0,0 +1,61 @@ +\ va>tte-data defer MMU virtual to physical address hook for Solaris +\ We need to make sure this is in the global wordlist +active-package 0 active-package! +defer va>tte-data +0 to va>tte-data +active-package! + +:noname +  ."   Type 'help' for detailed information" cr +  \ ."   boot secondary slave cdrom: " cr +  \ ."    0 >  boot hd:2,\boot\vmlinuz root=/dev/hda2" cr +  ; DIAG-initializer + +: make-openable ( path ) +  find-dev if +    begin ?dup while +      \ install trivial open and close methods +      dup active-package! is-open +      parent +    repeat +  then +; + +: preopen ( chosen-str node-path ) +  2dup make-openable + +  " /chosen" find-device +  open-dev ?dup if +    encode-int 2swap property +  else +    2drop +  then +; + +:noname +  set-defaults +; PREPOST-initializer + +\ preopen device nodes (and store the ihandles under /chosen) +:noname +  " memory" " /memory" preopen + +; SYSTEM-initializer + +\ use the tty interface if available +: activate-tty-interface +  " /packages/terminal-emulator" find-dev if drop +  then +; + +device-end + +: rmap@    ( virt -- rmentry ) +  drop 0 +  ; + +\ Load VGA FCode driver blob +[IFDEF] CONFIG_DRIVER_VGA +  -1 value vga-driver-fcode +  " QEMU,VGA.bin" $encode-file to vga-driver-fcode +[THEN] diff --git a/roms/openbios/arch/sparc64/ldscript b/roms/openbios/arch/sparc64/ldscript new file mode 100644 index 00000000..54288e82 --- /dev/null +++ b/roms/openbios/arch/sparc64/ldscript @@ -0,0 +1,67 @@ +OUTPUT_FORMAT(elf64-sparc) +OUTPUT_ARCH(sparc:v9) + +/* QEMU ELF loader can't handle very complex files, so we put ELFBoot +info to rodata and put initctx to data.*/ + +ENTRY(trap_table) + +/* Initial load address + */ +BASE_ADDR = 0x00000000ffd00000; + +/* 16KB stack */ +STACK_SIZE = 16384; +IOMEM_SIZE = 256 * 1024 + 768 * 1024; + +SECTIONS +{ +    . = BASE_ADDR; + +    /* Start of the program. +     * Now the version string is in the note, we must include it +     * in the program. Otherwise we lose the string after relocation. */ +    _start = .; + +    /* Normal sections */ +    .text ALIGN(524288): { +        *(.text.vectors) +        *(.text) +        *(.text.*) +    } +    .rodata ALIGN(524288): { +        _rodata = .; +	sound_drivers_start = .; +	*(.rodata.sound_drivers) +	sound_drivers_end = .; +	*(.rodata) +	*(.rodata.*) +        *(.note.ELFBoot) +    } +    .data ALIGN(524288): { +        _data = .; +        *(.data) +        *(.data.*) +    } + +    .bss ALIGN(4096): { +        _bss = .; +	*(.bss) +	*(.bss.*) +	*(COMMON) + +	_stack = .; +	. += STACK_SIZE; +	. = ALIGN(16); +	_estack = .; +    } + +    . = ALIGN(4096); +    _end = .; +    _iomem = _end + IOMEM_SIZE; + +    /* We discard .note sections other than .note.ELFBoot, +     * because some versions of GCC generates useless ones. */ + +    /DISCARD/ : { *(.comment*) *(.note.*) } +} diff --git a/roms/openbios/arch/sparc64/lib.c b/roms/openbios/arch/sparc64/lib.c new file mode 100644 index 00000000..e9101af5 --- /dev/null +++ b/roms/openbios/arch/sparc64/lib.c @@ -0,0 +1,508 @@ +/* lib.c + * tag: simple function library + * + * Copyright (C) 2003 Stefan Reinauer + * + * See the file "COPYING" for further information about + * the copyright and warranty status of this work. + */ + +#include "config.h" +#include "libc/vsprintf.h" +#include "libopenbios/bindings.h" +#include "spitfire.h" +#include "libopenbios/sys_info.h" +#include "boot.h" + +#include "arch/sparc64/ofmem_sparc64.h" + +/* Format a string and print it on the screen, just like the libc + * function printf. + */ +int printk( const char *fmt, ... ) +{ +        char *p, buf[512]; +	va_list args; +	int i; + +	va_start(args, fmt); +        i = vsnprintf(buf, sizeof(buf), fmt, args); +	va_end(args); + +	for( p=buf; *p; p++ ) +		putchar(*p); +	return i; +} + +/* Private functions for mapping between physical/virtual addresses */  +phys_addr_t +va2pa(unsigned long va) +{ +    if ((va >= (unsigned long)&_start) && +        (va < (unsigned long)&_end)) +        return va - va_shift; +    else +        return va; +} + +unsigned long +pa2va(phys_addr_t pa) +{ +    if ((pa + va_shift >= (unsigned long)&_start) && +        (pa + va_shift < (unsigned long)&_end)) +        return pa + va_shift; +    else +        return pa; +} + +void *malloc(int size) +{ +	return ofmem_malloc(size); +} + +void* realloc( void *ptr, size_t size ) +{ +	return ofmem_realloc(ptr, size); +} + +void free(void *ptr) +{ +	ofmem_free(ptr); +} + +static void +mmu_open(void) +{ +    RET(-1); +} + +static void +mmu_close(void) +{ +} + +void ofmem_walk_boot_map(translation_entry_cb cb) +{ +    unsigned long phys, virt, size, mode, data, mask; +    unsigned int i; + +    for (i = 0; i < 64; i++) { +        data = spitfire_get_dtlb_data(i); +        if (data & SPITFIRE_TTE_VALID) { +            switch ((data >> 61) & 3) { +            default: +            case 0x0: /* 8k */ +                mask = 0xffffffffffffe000ULL; +                size = PAGE_SIZE_8K; +                break; +            case 0x1: /* 64k */ +                mask = 0xffffffffffff0000ULL; +                size = PAGE_SIZE_64K; +                break; +            case 0x2: /* 512k */ +                mask = 0xfffffffffff80000ULL; +                size = PAGE_SIZE_512K; +                break; +            case 0x3: /* 4M */ +                mask = 0xffffffffffc00000ULL; +                size = PAGE_SIZE_4M; +                break; +            } + +            virt = spitfire_get_dtlb_tag(i); +            virt &= mask; + +            /* extract 41bit physical address */ +            phys = data & 0x000001fffffff000ULL; +			phys &= mask; + +			mode = data & 0xfff; + +			cb(phys, virt, size, mode); +        } +    } +} + +/* +  3.6.5 translate +  ( virt -- false | phys.lo ... phys.hi mode true ) +*/ +static void +mmu_translate(void) +{ +    ucell virt, mode; +    phys_addr_t phys; + +    virt = POP(); + +    phys = ofmem_translate(virt, &mode); + +    if (phys != -1UL) { +		PUSH(phys & 0xffffffff); +		PUSH(phys >> 32); +		PUSH(mode); +		PUSH(-1); +    } +    else { +    	PUSH(0); +    } +} + +/* + * D5.3 pgmap@ ( va -- tte ) + */ +static void +pgmap_fetch(void) +{ +    unsigned long va, tte_data; + +    va = POP(); + +    tte_data = find_tte(va); +    if (tte_data == -1) +        goto error; + +    /* return tte_data */ +    PUSH(tte_data); +    return; + +error: +    /* If we get here, there was no entry */ +    PUSH(0); +} + +/* +  ( index tte_data vaddr -- ? ) +*/ +static void +dtlb_load(void) +{ +    unsigned long vaddr, tte_data, idx; + +    vaddr = POP(); +    tte_data = POP(); +    idx = POP(); +    dtlb_load3(vaddr, tte_data, idx); +} + +/* MMU D-TLB miss handler */ +void +dtlb_miss_handler(void) +{ +	unsigned long faultva, tte_data = 0; + +	/* Grab fault address from MMU and round to nearest 8k page */ +	faultva = dtlb_faultva(); +	faultva >>= 13; +	faultva <<= 13; + +	/* If a valid va>tte-data routine has been set, invoke that Forth xt instead */ +	if (va2ttedata && *va2ttedata != 0) { + +		/* va>tte-data ( addr cnum -- false | tte-data true ) */ +		PUSH(faultva); +		PUSH(0); +		enterforth(*va2ttedata); + +		/* Check the result first... */ +		tte_data = POP(); +		if (!tte_data) { +			bug(); +		} else { +			/* Grab the real data */ +			tte_data = POP(); +		}		 +	} else { +		/* Search the ofmem linked list for this virtual address */ +		tte_data = find_tte(faultva); +	} + +	if (tte_data) { +		/* Update MMU */ +		dtlb_load2(faultva, tte_data); +	} else { +		/* If we got here, there was no translation so fail */ +		bug(); +	} + +} + +/* +  ( index tte_data vaddr -- ? ) +*/ +static void +itlb_load(void) +{ +    unsigned long vaddr, tte_data, idx; + +    vaddr = POP(); +    tte_data = POP(); +    idx = POP(); +    itlb_load3(vaddr, tte_data, idx); +} + +/* MMU I-TLB miss handler */ +void +itlb_miss_handler(void) +{ +	unsigned long faultva, tte_data = 0; + +	/* Grab fault address from MMU and round to nearest 8k page */ +	faultva = itlb_faultva(); +	faultva >>= 13; +	faultva <<= 13; + +	/* If a valid va>tte-data routine has been set, invoke that Forth xt instead */ +	if (va2ttedata && *va2ttedata != 0) { + +		/* va>tte-data ( addr cnum -- false | tte-data true ) */ +		PUSH(faultva); +		PUSH(0); +		enterforth(*va2ttedata); + +		/* Check the result first... */ +		tte_data = POP(); +		if (!tte_data) { +			bug(); +		} else { +			/* Grab the real data */ +			tte_data = POP(); +		}		 +	} else { +		/* Search the ofmem linked list for this virtual address */ +		tte_data = find_tte(faultva); +	} + +	if (tte_data) { +		/* Update MMU */ +		itlb_load2(faultva, tte_data); +	} else { +		/* If we got here, there was no translation so fail */ +		bug(); +	} +} + +/* +  3.6.5 map +  ( phys.lo ... phys.hi virt size mode -- ) +*/ +static void +mmu_map(void) +{ +    ucell virt, size, mode; +    phys_addr_t phys; + +    mode = POP(); +    size = POP(); +    virt = POP(); +    phys = POP(); +    phys <<= 32; +    phys |= POP(); + +    ofmem_map(phys, virt, size, mode); +} + +/* +  3.6.5 unmap +  ( virt size -- ) +*/ +static void +mmu_unmap(void) +{ +    ucell virt, size; + +    size = POP(); +    virt = POP(); +    ofmem_unmap(virt, size); +} + +/* +  3.6.5 claim +  ( virt size align -- base ) +*/ +static void +mmu_claim(void) +{ +    ucell virt=-1UL, size, align; + +    align = POP(); +    size = POP(); +    if (!align) { +    	virt = POP(); +    } + +    virt = ofmem_claim_virt(virt, size, align); + +    PUSH(virt); +} + +/* +  3.6.5 release +  ( virt size -- ) +*/ +static void +mmu_release(void) +{ +    ucell virt, size; + +    size = POP(); +    virt = POP(); + +    ofmem_release_virt(virt, size); +} + +/* ( phys size align --- base ) */ +static void +mem_claim( void ) +{ +    ucell size, align; +    phys_addr_t phys=-1UL; + +    align = POP(); +    size = POP(); +    if (!align) { +        phys = POP(); +        phys <<= 32; +        phys |= POP(); +    } + +    phys = ofmem_claim_phys(phys, size, align); + +    PUSH(phys & 0xffffffffUL); +    PUSH(phys >> 32); +} + +/* ( phys size --- ) */ +static void +mem_release( void ) +{ +    phys_addr_t phys; +    ucell size; + +    size = POP(); +    phys = POP(); +    phys <<= 32; +    phys |= POP(); + +    ofmem_release_phys(phys, size); +} + +/* ( name-cstr phys size align --- phys ) */ +static void +mem_retain ( void ) +{ +    ucell size, align; +    phys_addr_t phys=-1UL; + +    align = POP(); +    size = POP(); +    if (!align) { +        phys = POP(); +        phys <<= 32; +        phys |= POP(); +    } + +    /* Currently do nothing with the name */ +    POP(); + +    phys = ofmem_retain(phys, size, align); + +    PUSH(phys & 0xffffffffUL); +    PUSH(phys >> 32); +} + +/* ( virt size align -- baseaddr|-1 ) */ +static void +ciface_claim( void ) +{ +	ucell align = POP(); +	ucell size = POP(); +	ucell virt = POP(); +	ucell ret = ofmem_claim( virt, size, align ); + +	/* printk("ciface_claim: %08x %08x %x\n", virt, size, align ); */ +	PUSH( ret ); +} + +/* ( virt size -- ) */ +static void +ciface_release( void ) +{ +	ucell size = POP(); +	ucell virt = POP(); +	ofmem_release(virt, size); +} + +DECLARE_NODE(memory, INSTALL_OPEN, 0, "/memory"); + +NODE_METHODS( memory ) = { +    { "claim",              mem_claim       }, +    { "release",            mem_release     }, +    { "SUNW,retain",        mem_retain      }, +}; + +DECLARE_NODE(mmu, INSTALL_OPEN, 0, "/virtual-memory"); + +NODE_METHODS(mmu) = { +    { "open",               mmu_open              }, +    { "close",              mmu_close             }, +    { "translate",          mmu_translate         }, +    { "SUNW,dtlb-load",     dtlb_load             }, +    { "SUNW,itlb-load",     itlb_load             }, +    { "map",                mmu_map               }, +    { "unmap",              mmu_unmap             }, +    { "claim",              mmu_claim             }, +    { "release",            mmu_release           }, +}; + +void ob_mmu_init(const char *cpuname, uint64_t ram_size) +{ +    /* memory node */ +    REGISTER_NODE_METHODS(memory, "/memory"); + +    /* MMU node */ +    REGISTER_NODE_METHODS(mmu, "/virtual-memory"); + +    ofmem_register(find_dev("/memory"), find_dev("/virtual-memory")); + +    push_str("/chosen"); +    fword("find-device"); + +    push_str("/virtual-memory"); +    fword("open-dev"); +    fword("encode-int"); +    push_str("mmu"); +    fword("property"); + +    push_str("/memory"); +    fword("find-device"); + +    /* All memory: 0 to RAM_size */ +    PUSH(0); +    fword("encode-int"); +    PUSH(0); +    fword("encode-int"); +    fword("encode+"); +    PUSH((int)(ram_size >> 32)); +    fword("encode-int"); +    fword("encode+"); +    PUSH((int)(ram_size & 0xffffffff)); +    fword("encode-int"); +    fword("encode+"); +    push_str("reg"); +    fword("property"); + +    push_str("/openprom/client-services"); +    fword("find-device"); +    bind_func("cif-claim", ciface_claim); +    bind_func("cif-release", ciface_release); + +    /* Other MMU functions */ +    PUSH(0); +    fword("active-package!"); +    bind_func("pgmap@", pgmap_fetch); + +    /* Find address of va2ttedata defer word contents for MMU miss handlers */ +    va2ttedata = (ucell *)findword("va>tte-data"); +    va2ttedata++; +} diff --git a/roms/openbios/arch/sparc64/linux_load.c b/roms/openbios/arch/sparc64/linux_load.c new file mode 100644 index 00000000..e3afc2db --- /dev/null +++ b/roms/openbios/arch/sparc64/linux_load.c @@ -0,0 +1,653 @@ +/* + * Linux/i386 loader + * Supports bzImage, zImage and Image format. + * + * Based on work by Steve Gehlbach. + * Portions are taken from mkelfImage. + * + * 2003-09 by SONE Takeshi + */ + +#include "config.h" +#include "kernel/kernel.h" +#include "libopenbios/bindings.h" +#include "libopenbios/sys_info.h" +#include "context.h" +#include "libc/diskio.h" +#include "boot.h" + +#define printf printk +#define debug printk +#define strtoull_with_suffix strtol + +#define LINUX_PARAM_LOC 0x90000 +#define COMMAND_LINE_LOC 0x91000 +#define GDT_LOC 0x92000 +#define STACK_LOC 0x93000 + +#define E820MAX	32		/* number of entries in E820MAP */ +struct e820entry { +	unsigned long long addr;	/* start of memory segment */ +	unsigned long long size;	/* size of memory segment */ +	unsigned long type;		/* type of memory segment */ +#define E820_RAM	1 +#define E820_RESERVED	2 +#define E820_ACPI	3 /* usable as RAM once ACPI tables have been read */ +#define E820_NVS	4 +}; + +/* The header of Linux/i386 kernel */ +struct linux_header { +    uint8_t  reserved1[0x1f1];		/* 0x000 */ +    uint8_t  setup_sects;		/* 0x1f1 */ +    uint16_t root_flags;		/* 0x1f2 */ +    uint8_t  reserved2[6];		/* 0x1f4 */ +    uint16_t vid_mode;			/* 0x1fa */ +    uint16_t root_dev;			/* 0x1fc */ +    uint16_t boot_sector_magic;		/* 0x1fe */ +    /* 2.00+ */ +    uint8_t  reserved3[2];		/* 0x200 */ +    uint8_t  header_magic[4];		/* 0x202 */ +    uint16_t protocol_version;		/* 0x206 */ +    uint32_t realmode_swtch;		/* 0x208 */ +    uint16_t start_sys;			/* 0x20c */ +    uint16_t kver_addr;			/* 0x20e */ +    uint8_t  type_of_loader;		/* 0x210 */ +    uint8_t  loadflags;			/* 0x211 */ +    uint16_t setup_move_size;		/* 0x212 */ +    uint32_t code32_start;		/* 0x214 */ +    uint32_t ramdisk_image;		/* 0x218 */ +    uint32_t ramdisk_size;		/* 0x21c */ +    uint8_t  reserved4[4];		/* 0x220 */ +    /* 2.01+ */ +    uint16_t heap_end_ptr;		/* 0x224 */ +    uint8_t  reserved5[2];		/* 0x226 */ +    /* 2.02+ */ +    uint32_t cmd_line_ptr;		/* 0x228 */ +    /* 2.03+ */ +    uint32_t initrd_addr_max;		/* 0x22c */ +} __attribute__ ((packed)); + + +/* Paramters passed to 32-bit part of Linux + * This is another view of the structure above.. */ +struct linux_params { +    uint8_t  orig_x;			/* 0x00 */ +    uint8_t  orig_y;			/* 0x01 */ +    uint16_t ext_mem_k;			/* 0x02 -- EXT_MEM_K sits here */ +    uint16_t orig_video_page;		/* 0x04 */ +    uint8_t  orig_video_mode;		/* 0x06 */ +    uint8_t  orig_video_cols;		/* 0x07 */ +    uint16_t unused2;			/* 0x08 */ +    uint16_t orig_video_ega_bx;		/* 0x0a */ +    uint16_t unused3;			/* 0x0c */ +    uint8_t  orig_video_lines;		/* 0x0e */ +    uint8_t  orig_video_isVGA;		/* 0x0f */ +    uint16_t orig_video_points;		/* 0x10 */ + +    /* VESA graphic mode -- linear frame buffer */ +    uint16_t lfb_width;			/* 0x12 */ +    uint16_t lfb_height;		/* 0x14 */ +    uint16_t lfb_depth;			/* 0x16 */ +    uint32_t lfb_base;			/* 0x18 */ +    uint32_t lfb_size;			/* 0x1c */ +    uint16_t cl_magic;			/* 0x20 */ +#define CL_MAGIC_VALUE 0xA33F +    uint16_t cl_offset;			/* 0x22 */ +    uint16_t lfb_linelength;		/* 0x24 */ +    uint8_t  red_size;			/* 0x26 */ +    uint8_t  red_pos;			/* 0x27 */ +    uint8_t  green_size;		/* 0x28 */ +    uint8_t  green_pos;			/* 0x29 */ +    uint8_t  blue_size;			/* 0x2a */ +    uint8_t  blue_pos;			/* 0x2b */ +    uint8_t  rsvd_size;			/* 0x2c */ +    uint8_t  rsvd_pos;			/* 0x2d */ +    uint16_t vesapm_seg;		/* 0x2e */ +    uint16_t vesapm_off;		/* 0x30 */ +    uint16_t pages;			/* 0x32 */ +    uint8_t  reserved4[12];		/* 0x34 -- 0x3f reserved for future expansion */ + +    //struct apm_bios_info apm_bios_info;	/* 0x40 */ +    uint8_t  apm_bios_info[0x40]; +    //struct drive_info_struct drive_info;	/* 0x80 */ +    uint8_t  drive_info[0x20]; +    //struct sys_desc_table sys_desc_table;	/* 0xa0 */ +    uint8_t  sys_desc_table[0x140]; +    uint32_t alt_mem_k;			/* 0x1e0 */ +    uint8_t  reserved5[4];		/* 0x1e4 */ +    uint8_t  e820_map_nr;		/* 0x1e8 */ +    uint8_t  reserved6[9];		/* 0x1e9 */ +    uint16_t mount_root_rdonly;		/* 0x1f2 */ +    uint8_t  reserved7[4];		/* 0x1f4 */ +    uint16_t ramdisk_flags;		/* 0x1f8 */ +#define RAMDISK_IMAGE_START_MASK  	0x07FF +#define RAMDISK_PROMPT_FLAG		0x8000 +#define RAMDISK_LOAD_FLAG		0x4000 +    uint8_t  reserved8[2];		/* 0x1fa */ +    uint16_t orig_root_dev;		/* 0x1fc */ +    uint8_t  reserved9[1];		/* 0x1fe */ +    uint8_t  aux_device_info;		/* 0x1ff */ +    uint8_t  reserved10[2];		/* 0x200 */ +    uint8_t  param_block_signature[4];	/* 0x202 */ +    uint16_t param_block_version;	/* 0x206 */ +    uint8_t  reserved11[8];		/* 0x208 */ +    uint8_t  loader_type;		/* 0x210 */ +#define LOADER_TYPE_LOADLIN         1 +#define LOADER_TYPE_BOOTSECT_LOADER 2 +#define LOADER_TYPE_SYSLINUX        3 +#define LOADER_TYPE_ETHERBOOT       4 +#define LOADER_TYPE_KERNEL          5 +    uint8_t  loader_flags;		/* 0x211 */ +    uint8_t  reserved12[2];		/* 0x212 */ +    uint32_t kernel_start;		/* 0x214 */ +    uint32_t initrd_start;		/* 0x218 */ +    uint32_t initrd_size;		/* 0x21c */ +    uint8_t  reserved12_5[8];		/* 0x220 */ +    uint32_t cmd_line_ptr;		/* 0x228 */ +    uint8_t  reserved13[164];		/* 0x22c */ +    struct e820entry e820_map[E820MAX];	/* 0x2d0 */ +    uint8_t  reserved16[688];		/* 0x550 */ +#define COMMAND_LINE_SIZE 256 +    /* Command line is copied here by 32-bit i386/kernel/head.S. +     * So I will follow the boot protocol, rather than putting it +     * directly here. --ts1 */ +    uint8_t  command_line[COMMAND_LINE_SIZE]; /* 0x800 */ +    uint8_t  reserved17[1792];		/* 0x900 - 0x1000 */ +}; + +static uint64_t forced_memsize; +static int fd; + +static unsigned long file_size(void) +{ +	long long fpos, fsize; + +	/* Save current position */ +	fpos = tell(fd); + +	/* Go to end of file and get position */ +	seek_io(fd, -1); +	fsize = tell(fd); + +	/* Go back to old position */ +	seek_io(fd, 0); +	seek_io(fd, fpos); + +	return fsize; +} + +/* Load the first part the file and check if it's Linux */ +static uint32_t load_linux_header(struct linux_header *hdr) +{ +    int load_high; +    uint32_t kern_addr; + +    if (read_io(fd, hdr, sizeof *hdr) != sizeof *hdr) { +	debug("Can't read Linux header\n"); +	return 0; +    } +    if (hdr->boot_sector_magic != 0xaa55) { +	debug("Not a Linux kernel image\n"); +	return 0; +    } + +    /* Linux is found. Print some information */ +    if (memcmp(hdr->header_magic, "HdrS", 4) != 0) { +	/* This may be floppy disk image or something. +	 * Perform a simple (incomplete) sanity check. */ +	if (hdr->setup_sects >= 16 +		|| file_size() - (hdr->setup_sects<<9) >= 512<<10) { +	    debug("This looks like a bootdisk image but not like Linux...\n"); +	    return 0; +	} + +	printf("Possible very old Linux"); +	/* This kernel does not even have a protocol version. +	 * Force the value. */ +	hdr->protocol_version = 0; /* pre-2.00 */ +    } else +	printf("Found Linux"); +    if (hdr->protocol_version >= 0x200 && hdr->kver_addr) { +	char kver[256]; +	seek_io(fd, hdr->kver_addr + 0x200); +	if (read_io(fd, kver, sizeof kver) != 0) { +	    kver[255] = 0; +	    printf(" version %s", kver); +	} +    } +    debug(" (protocol %#x)", hdr->protocol_version); +    load_high = 0; +    if (hdr->protocol_version >= 0x200) { +	debug(" (loadflags %#x)", hdr->loadflags); +	load_high = hdr->loadflags & 1; +    } +    if (load_high) { +	printf(" bzImage"); +	kern_addr = 0x100000; +    } else { +	printf(" zImage or Image"); +	kern_addr = 0x1000; +    } +    printf(".\n"); + +    return kern_addr; +} + +/* Set up parameters for 32-bit kernel */ +static void +init_linux_params(struct linux_params *params, struct linux_header *hdr) +{ +    debug("Setting up paramters at %#lx\n", virt_to_phys(params)); +    memset(params, 0, sizeof *params); + +    /* Copy some useful values from header */ +    params->mount_root_rdonly = hdr->root_flags; +    params->orig_root_dev = hdr->root_dev; + +    /* Video parameters. +     * This assumes we have VGA in standard 80x25 text mode, +     * just like our vga.c does. +     * Cursor position is filled later to allow some more printf's. */ +    params->orig_video_mode = 3; +    params->orig_video_cols = 80; +    params->orig_video_lines = 25; +    params->orig_video_isVGA = 1; +    params->orig_video_points = 16; + +    params->loader_type = 0xff; /* Unregistered Linux loader */ +} + +/* Memory map */ +static void +set_memory_size(struct linux_params *params, struct sys_info *info) +{ +    int i; +    uint64_t end; +    uint32_t ramtop = 0; +    struct e820entry *linux_map; +    struct memrange *filo_map; + +    linux_map = params->e820_map; +    filo_map = info->memrange; +    for (i = 0; i < info->n_memranges; i++, linux_map++, filo_map++) { +	if (i < E820MAX) { +	    /* Convert to BIOS e820 style */ +	    linux_map->addr = filo_map->base; +	    linux_map->size = filo_map->size; +	    linux_map->type = E820_RAM; +	    debug("%016Lx - %016Lx\n", linux_map->addr, +		    linux_map->addr + linux_map->size); +	    params->e820_map_nr = i+1; +	} + +	/* Find out top of RAM. XXX This ignores hole above 1MB */ +	end = filo_map->base + filo_map->size; +	if (end < (1ULL << 32)) { /* don't count memory above 4GB */ +	    if (end > ramtop) +		ramtop = (uint32_t) end; +	} +    } +    debug("ramtop=%#x\n", ramtop); +    /* Size of memory above 1MB in KB */ +    params->alt_mem_k = (ramtop - (1<<20)) >> 10; +    /* old style, 64MB max */ +    if (ramtop >= (64<<20)) +	params->ext_mem_k = (63<<10); +    else +	params->ext_mem_k = params->alt_mem_k; +    debug("ext_mem_k=%d, alt_mem_k=%d\n", params->ext_mem_k, params->alt_mem_k); +} + +/* + * Parse command line + * Some parameters, like initrd=<file>, are not passed to kernel, + * we are responsible to process them. + * Parameters for kernel are copied to kern_cmdline. Returns name of initrd. + */ +static char *parse_command_line(const char *orig_cmdline, char *kern_cmdline) +{ +    const char *start, *sep, *end, *val; +    char name[64]; +    unsigned long len; +    int k_len; +    int to_kern; +    char *initrd = NULL; +    int toolong = 0; + +    forced_memsize = 0; + +    if (!orig_cmdline) { +        *kern_cmdline = '\0'; +        return NULL; +    } + +    k_len = 0; +    debug("original command line: \"%s\"\n", orig_cmdline); +    debug("kernel command line at %#lx\n", virt_to_phys(kern_cmdline)); + +    start = orig_cmdline; +    while (*start == ' ') +	start++; +    while (*start) { +	end = strchr(start, ' '); +	if (!end) +	    end = start + strlen(start); +	sep = strchr(start, '='); +	if (!sep || sep > end) +	    sep = end; +	len = sep - start; +	if (len >= sizeof(name)) +	    len = sizeof(name) - 1; +	memcpy(name, start, len); +	name[len] = 0; + +	if (*sep == '=') { +	    val = sep + 1; +	    len = end - val; +	} else { +            val = NULL; +	    len = 0; +	} + +	/* Only initrd= and mem= are handled here. vga= is not, +	 * which I believe is a paramter to the realmode part of Linux, +	 * which we don't execute. */ +	if (strcmp(name, "initrd") == 0) { +	    if (!val) +		printf("Missing filename to initrd parameter\n"); +	    else { +		initrd = malloc(len + 1); +		memcpy(initrd, val, len); +		initrd[len] = 0; +		debug("initrd=%s\n", initrd); +	    } +	    /* Don't pass this to kernel */ +	    to_kern = 0; +	} else if (strcmp(name, "mem") == 0) { +	    if (!val) +		printf("Missing value for mem parameter\n"); +	    else { +		forced_memsize = strtoull_with_suffix(val, (char**)&val, 0); +		if (forced_memsize == 0) +		    printf("Invalid mem option, ignored\n"); +		if (val != end) { +		    printf("Garbage after mem=<size>, ignored\n"); +		    forced_memsize = 0; +		} +		debug("mem=%llu\n", (unsigned long long)forced_memsize); +	    } +	    /* mem= is for both loader and kernel */ +	    to_kern = 1; +	} else +	    to_kern = 1; + +	if (to_kern) { +	    /* Copy to kernel command line buffer */ +	    if (k_len != 0) +		kern_cmdline[k_len++] = ' '; /* put separator */ +	    len = end - start; +	    if (k_len + len >= COMMAND_LINE_SIZE) { +		len = COMMAND_LINE_SIZE - k_len - 1; +		if (!toolong) { +		    printf("Kernel command line is too long; truncated to " +			    "%d bytes\n", COMMAND_LINE_SIZE-1); +		    toolong = 1; +		} +	    } +	    memcpy(kern_cmdline + k_len, start, len); +	    k_len += len; +	} + +	start = end; +	while (*start == ' ') +	    start++; +    } +    kern_cmdline[k_len] = 0; +    debug("kernel command line (%d bytes): \"%s\"\n", k_len, kern_cmdline); + +    return initrd; +} + +/* Set command line location */ +static void set_command_line_loc(struct linux_params *params, +	struct linux_header *hdr) +{ +    if (hdr->protocol_version >= 0x202) { +	/* new style */ +	params->cmd_line_ptr = COMMAND_LINE_LOC; +    } else { +	/* old style */ +	params->cl_magic = CL_MAGIC_VALUE; +	params->cl_offset = COMMAND_LINE_LOC - LINUX_PARAM_LOC; +    } +} + +/* Load 32-bit part of kernel */ +static int load_linux_kernel(struct linux_header *hdr, uint32_t kern_addr) +{ +    uint32_t kern_offset, kern_size; + +    if (hdr->setup_sects == 0) +	hdr->setup_sects = 4; +    kern_offset = (hdr->setup_sects + 1) * 512; +    seek_io(fd, kern_offset); +    kern_size = file_size() - kern_offset; +    debug("offset=%#x addr=%#x size=%#x\n", kern_offset, kern_addr, kern_size); + +#if 0 +    if (using_devsize) { +	printf("Attempt to load up to end of device as kernel; " +		"specify the image size\n"); +	return 0; +    } +#endif + +    printf("Loading kernel... "); +    if ((uint32_t)read_io(fd, phys_to_virt(kern_addr), kern_size) != kern_size) { +	printf("Can't read kernel\n"); +	return 0; +    } +    printf("ok\n"); + +    return kern_size; +} + +static int load_initrd(struct linux_header *hdr, uint32_t kern_end, +                       struct linux_params *params, const char *initrd_file) +{ +    uint32_t max; +    uint32_t start, end, size; +    uint64_t forced; + +    fd = open_io(initrd_file); +    if (fd == -1) { +	printf("Can't open initrd: %s\n", initrd_file); +	return -1; +    } + +#if 0 +    if (using_devsize) { +	printf("Attempt to load up to end of device as initrd; " +		"specify the image size\n"); +	return -1; +    } +#endif + +    size = file_size(); + + +    /* Find out the kernel's restriction on how high the initrd can be +     * placed */ +    if (hdr->protocol_version >= 0x203) +	max = hdr->initrd_addr_max; +    else +	max = 0x38000000; /* Hardcoded value for older kernels */ + +    /* FILO itself is at the top of RAM. (relocated) +     * So, try putting initrd just below us. */ +    end = virt_to_phys(_start); +    if (end > max) +	end = max; + +    /* If "mem=" option is given, we have to put the initrd within +     * the specified range. */ +    if (forced_memsize) { +	forced = forced_memsize; +	if (forced > max) +	    forced = max; +	/* If the "mem=" is lower, it's easy */ +	if (forced <= end) +	    end = forced; +	else { +	    /* Otherwise, see if we can put it above us */ +	    if (virt_to_phys(_end) + size <= forced) +		end = forced; /* Ok */ +	} +    } + +    start = end - size; +    start &= ~0xfff; /* page align */ +    end = start + size; + +    debug("start=%#x end=%#x\n", start, end); + +    if (start < kern_end) { +	printf("Initrd is too big to fit in memory\n"); +	return -1; +    } + +    printf("Loading initrd... "); +    if ((uint32_t)read_io(fd, phys_to_virt(start), size) != size) { +	printf("Can't read initrd\n"); +	return -1; +    } +    printf("ok\n"); + +    params->initrd_start = start; +    params->initrd_size = size; + +    close_io(fd); + +    return 0; +} + +static void hardware_setup(void) +{ +    /* Disable nmi */ +    outb(0x80, 0x70); + +    /* Make sure any coprocessor is properly reset.. */ +    outb(0, 0xf0); +    outb(0, 0xf1); + +    /* we're getting screwed again and again by this problem of the 8259. +     * so we're going to leave this lying around for inclusion into +     * crt0.S on an as-needed basis. +     * +     * well, that went ok, I hope. Now we have to reprogram the interrupts :-( +     * we put them right after the intel-reserved hardware interrupts, at +     * int 0x20-0x2F. There they won't mess up anything. Sadly IBM really +     * messed this up with the original PC, and they haven't been able to +     * rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f, +     * which is used for the internal hardware interrupts as well. We just +     * have to reprogram the 8259's, and it isn't fun. +     */ + +    outb(0x11, 0x20);		/* initialization sequence to 8259A-1 */ +    outb(0x11, 0xA0);		/* and to 8259A-2 */ + +    outb(0x20, 0x21);		/* start of hardware int's (0x20) */ +    outb(0x28, 0xA1);		/* start of hardware int's 2 (0x28) */ + +    outb(0x04, 0x21);		/* 8259-1 is master */ +    outb(0x02, 0xA1);		/* 8259-2 is slave */ + +    outb(0x01, 0x21);		/* 8086 mode for both */ +    outb(0x01, 0xA1); + +    outb(0xFF, 0xA1);		/* mask off all interrupts for now */ +    outb(0xFB, 0x21);		/* mask all irq's but irq2 which is cascaded */ +} + +/* Start Linux */ +static int start_linux(uint32_t kern_addr) +{ +    struct context *ctx; +    //extern int cursor_x, cursor_y; + +    ctx = init_context(phys_to_virt(STACK_LOC), 4096, 0); + +    /* Entry point */ +    ctx->pc = kern_addr; +    ctx->npc = kern_addr + 4; + +    debug("pc=%#x\n", kern_addr); +    printf("Jumping to entry point...\n"); + +#ifdef VGA_CONSOLE +    /* Update VGA cursor position. +     * This must be here because the printf changes the value! */ +    params->orig_x = cursor_x; +    params->orig_y = cursor_y; +#endif + +    /* Go... */ +    ctx = switch_to(ctx); + +    /* It's impossible but... */ +    printf("Returned with o0=%#llx\n", ctx->regs[REG_O0]); + +    return ctx->regs[REG_O0]; +} + +int linux_load(struct sys_info *info, const char *file, const char *cmdline) +{ +    struct linux_header hdr; +    struct linux_params *params; +    uint32_t kern_addr, kern_size; +    char *initrd_file = NULL; + +    fd = open_io(file); +    if (fd == -1) +	return -1; + +    kern_addr = load_linux_header(&hdr); +    if (kern_addr == 0) { +	close_io(fd); +	return LOADER_NOT_SUPPORT; +    } + +    debug("[sparc64] Booting kernel '%s' ", file); +    if (cmdline) +        debug("with parameters '%s'\n", cmdline); +    else +        debug("without parameters.\n"); + +    params = phys_to_virt(LINUX_PARAM_LOC); +    init_linux_params(params, &hdr); +    set_memory_size(params, info); +    initrd_file = parse_command_line(cmdline, phys_to_virt(COMMAND_LINE_LOC)); +    set_command_line_loc(params, &hdr); + +    kern_size = load_linux_kernel(&hdr, kern_addr); +    if (kern_size == 0) { +	if (initrd_file) +	    free(initrd_file); +	return -1; +    } + +    if (initrd_file) { +	if (load_initrd(&hdr, kern_addr+kern_size, params, initrd_file) +		!= 0) { +	    free(initrd_file); +	    return -1; +	} +	free(initrd_file); +    } + +    hardware_setup(); + +    start_linux(kern_addr); +    return 0; +} diff --git a/roms/openbios/arch/sparc64/lsu.h b/roms/openbios/arch/sparc64/lsu.h new file mode 100644 index 00000000..d85c33f2 --- /dev/null +++ b/roms/openbios/arch/sparc64/lsu.h @@ -0,0 +1,20 @@ +/* $Id: lsu.h,v 1.2 1997/04/04 00:50:22 davem Exp $ */ +#ifndef _SPARC64_LSU_H +#define _SPARC64_LSU_H + +#include "const.h" + +/* LSU Control Register */ +#define LSU_CONTROL_PM _AC(0x000001fe00000000,UL) /* Phys-watchpoint byte mask*/ +#define LSU_CONTROL_VM _AC(0x00000001fe000000,UL) /* Virt-watchpoint byte mask*/ +#define LSU_CONTROL_PR _AC(0x0000000001000000,UL) /* Phys-rd watchpoint enable*/ +#define LSU_CONTROL_PW _AC(0x0000000000800000,UL) /* Phys-wr watchpoint enable*/ +#define LSU_CONTROL_VR _AC(0x0000000000400000,UL) /* Virt-rd watchpoint enable*/ +#define LSU_CONTROL_VW _AC(0x0000000000200000,UL) /* Virt-wr watchpoint enable*/ +#define LSU_CONTROL_FM _AC(0x00000000000ffff0,UL) /* Parity mask enables.     */ +#define LSU_CONTROL_DM _AC(0x0000000000000008,UL) /* Data MMU enable.         */ +#define LSU_CONTROL_IM _AC(0x0000000000000004,UL) /* Instruction MMU enable.  */ +#define LSU_CONTROL_DC _AC(0x0000000000000002,UL) /* Data cache enable.       */ +#define LSU_CONTROL_IC _AC(0x0000000000000001,UL) /* Instruction cache enable.*/ + +#endif /* !(_SPARC64_LSU_H) */ diff --git a/roms/openbios/arch/sparc64/multiboot.c b/roms/openbios/arch/sparc64/multiboot.c new file mode 100644 index 00000000..8514ca0a --- /dev/null +++ b/roms/openbios/arch/sparc64/multiboot.c @@ -0,0 +1,125 @@ +/* Support for Multiboot */ + +#include "config.h" +#include "asm/io.h" +#include "libopenbios/sys_info.h" +#include "multiboot.h" + +#define printf printk +#ifdef CONFIG_DEBUG_BOOT +#define debug printk +#else +#define debug(x...) +#endif + +struct mbheader { +    unsigned int magic, flags, checksum; +}; +const struct mbheader multiboot_header +	__attribute__((section (".hdr"))) = +{ +    MULTIBOOT_HEADER_MAGIC, +    MULTIBOOT_HEADER_FLAGS, +    -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) +}; + +/* Multiboot information structure, provided by loader to us */ + +struct multiboot_mmap { +	unsigned entry_size; +	unsigned base_lo, base_hi; +	unsigned size_lo, size_hi; +	unsigned type; +}; + +#define MULTIBOOT_MEM_VALID       0x01 +#define MULTIBOOT_BOOT_DEV_VALID  0x02 +#define MULTIBOOT_CMDLINE_VALID   0x04 +#define MULTIBOOT_MODS_VALID      0x08 +#define MULTIBOOT_AOUT_SYMS_VALID 0x10 +#define MULTIBOOT_ELF_SYMS_VALID  0x20 +#define MULTIBOOT_MMAP_VALID      0x40 + +void collect_multiboot_info(struct sys_info *info); +void collect_multiboot_info(struct sys_info *info) +{ +    struct multiboot_info *mbinfo; +    struct multiboot_mmap *mbmem; +    unsigned mbcount, mbaddr; +    unsigned int i; +    struct memrange *mmap; +    int mmap_count; +    module_t *mod; + +    if (info->boot_type != 0x2BADB002) +	return; + +    debug("Using Multiboot information at %#lx\n", info->boot_data); + +    mbinfo = phys_to_virt(info->boot_data); + +    if (mbinfo->mods_count != 1) { +	    printf("Multiboot: no dictionary\n"); +	    return; +    } + +    mod = (module_t *) mbinfo->mods_addr; +    info->dict_start=(unsigned long *)mod->mod_start; +    info->dict_end=(unsigned long *)mod->mod_end; + +    if (mbinfo->flags & MULTIBOOT_MMAP_VALID) { +	/* convert mmap records */ +	mbmem = phys_to_virt(mbinfo->mmap_addr); +	mbcount = mbinfo->mmap_length / (mbmem->entry_size + 4); +	mmap = malloc(mbcount * sizeof(struct memrange)); +	mmap_count = 0; +	mbaddr = mbinfo->mmap_addr; +	for (i = 0; i < mbcount; i++) { +	    mbmem = phys_to_virt(mbaddr); +	    debug("%08x%08x %08x%08x (%d)\n", +		    mbmem->base_hi, +		    mbmem->base_lo, +		    mbmem->size_hi, +		    mbmem->size_lo, +		    mbmem->type); +	    if (mbmem->type == 1) { /* Only normal RAM */ +		mmap[mmap_count].base = mbmem->base_lo +		    + (((unsigned long long) mbmem->base_hi) << 32); +		mmap[mmap_count].size = mbmem->size_lo +		    + (((unsigned long long) mbmem->size_hi) << 32); +		mmap_count++; +	    } +	    mbaddr += mbmem->entry_size + 4; +	    if (mbaddr >= mbinfo->mmap_addr + mbinfo->mmap_length) +		break; +	} +	/* simple sanity check - there should be at least 2 RAM segments +	 * (base 640k and extended) */ +	if (mmap_count >= 2) +	    goto got_it; + +	printf("Multiboot mmap is broken\n"); +	free(mmap); +	/* fall back to mem_lower/mem_upper */ +    } + +    if (mbinfo->flags & MULTIBOOT_MEM_VALID) { +	/* use mem_lower and mem_upper */ +	mmap_count = 2; +	mmap = malloc(2 * sizeof(*mmap)); +	mmap[0].base = 0; +	mmap[0].size = mbinfo->mem_lower << 10; +	mmap[1].base = 1 << 20; /* 1MB */ +	mmap[1].size = mbinfo->mem_upper << 10; +	goto got_it; +    } + +    printf("Can't get memory information from Multiboot\n"); +    return; + +got_it: +    info->memrange = mmap; +    info->n_memranges = mmap_count; + +    return; +} diff --git a/roms/openbios/arch/sparc64/multiboot.h b/roms/openbios/arch/sparc64/multiboot.h new file mode 100644 index 00000000..17cf202e --- /dev/null +++ b/roms/openbios/arch/sparc64/multiboot.h @@ -0,0 +1,96 @@ +/* multiboot.h + * tag: header for multiboot + * + * Copyright (C) 2003-2004 Stefan Reinauer + * + * See the file "COPYING" for further information about + * the copyright and warranty status of this work. + */ + +/* magic number for multiboot header */ +#define MULTIBOOT_HEADER_MAGIC		0x1BADB002 + +/* flags for multiboot header */ +#define MULTIBOOT_HEADER_FLAGS		0x00010003 + +/* magic number passed by multiboot-compliant boot loader.  */ +#define MULTIBOOT_BOOTLOADER_MAGIC	0x2BADB002 + +/* The size of our stack (8KB).  */ +#define STACK_SIZE			0x2000 + +/* C symbol format. HAVE_ASM_USCORE is defined by configure.  */ +#ifdef HAVE_ASM_USCORE +# define EXT_C(sym)			_ ## sym +#else +# define EXT_C(sym)			sym +#endif + +#ifndef ASM +/* We don't want these declarations in boot.S  */ + +/* multiboot header */ +typedef struct multiboot_header { +	unsigned long magic; +	unsigned long flags; +	unsigned long checksum; +	unsigned long header_addr; +	unsigned long load_addr; +	unsigned long load_end_addr; +	unsigned long bss_end_addr; +	unsigned long entry_addr; +} multiboot_header_t; + +/* symbol table for a.out */ +typedef struct aout_symbol_table { +	unsigned long tabsize; +	unsigned long strsize; +	unsigned long addr; +	unsigned long reserved; +} aout_symbol_table_t; + +/* section header table for ELF */ +typedef struct elf_section_header_table { +	unsigned long num; +	unsigned long size; +	unsigned long addr; +	unsigned long shndx; +} elf_section_header_table_t; + +/* multiboot information */ +typedef struct multiboot_info { +	unsigned long flags; +	unsigned long mem_lower; +	unsigned long mem_upper; +	unsigned long boot_device; +	unsigned long cmdline; +	unsigned long mods_count; +	unsigned long mods_addr; +	union { +		aout_symbol_table_t aout_sym; +		elf_section_header_table_t elf_sec; +	} u; +	unsigned long mmap_length; +	unsigned long mmap_addr; +} multiboot_info_t; + +/* module structure */ +typedef struct module { +	unsigned long mod_start; +	unsigned long mod_end; +	unsigned long string; +	unsigned long reserved; +} module_t; + +/* memory map. Be careful that the offset 0 is base_addr_low +   but no size.  */ +typedef struct memory_map { +	unsigned long size; +	unsigned long base_addr_low; +	unsigned long base_addr_high; +	unsigned long length_low; +	unsigned long length_high; +	unsigned long type; +} memory_map_t; + +#endif				/* ! ASM */ diff --git a/roms/openbios/arch/sparc64/ofmem_sparc64.c b/roms/openbios/arch/sparc64/ofmem_sparc64.c new file mode 100644 index 00000000..bdfaf563 --- /dev/null +++ b/roms/openbios/arch/sparc64/ofmem_sparc64.c @@ -0,0 +1,381 @@ +/* + *	<ofmem_sparc64.c> + * + *	OF Memory manager + * + *   Copyright (C) 1999-2004 Samuel Rydh (samuel@ibrium.se) + *   Copyright (C) 2004 Stefan Reinauer + * + *   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 "libc/string.h" +#include "arch/sparc64/ofmem_sparc64.h" +#include "spitfire.h" + +#define OF_MALLOC_BASE		((char*)OFMEM + ALIGN_SIZE(sizeof(ofmem_t), 8)) + +#define MEMSIZE (128 * 1024) +static union { +	char memory[MEMSIZE]; +	ofmem_t ofmem; +} s_ofmem_data; + +#define OFMEM      	(&s_ofmem_data.ofmem) +#define TOP_OF_RAM 	(s_ofmem_data.memory + MEMSIZE) + +static retain_t s_retained; +translation_t **g_ofmem_translations = &s_ofmem_data.ofmem.trans; + +ucell *va2ttedata = 0; +extern uint64_t qemu_mem_size; + +static inline size_t ALIGN_SIZE(size_t x, size_t a) +{ +    return (x + a - 1) & ~(a-1); +} + +static ucell get_heap_top( void ) +{ +	return (ucell)TOP_OF_RAM; +} + +ofmem_t* ofmem_arch_get_private(void) +{ +	return OFMEM; +} + +void* ofmem_arch_get_malloc_base(void) +{ +	return OF_MALLOC_BASE; +} + +ucell ofmem_arch_get_heap_top(void) +{ +	return get_heap_top(); +} + +ucell ofmem_arch_get_virt_top(void) +{ +	return (ucell)TOP_OF_RAM; +} + +ucell ofmem_arch_get_iomem_base(void) +{ +	/* Currently unused */ +	return 0; +} + +ucell ofmem_arch_get_iomem_top(void) +{ +	/* Currently unused */ +	return 0; +} + +retain_t *ofmem_arch_get_retained(void) +{ +	return (&s_retained); +} + +int ofmem_arch_get_translation_entry_size(void) +{ +	/* Return size of a single MMU package translation property entry in cells */ +	return 3; +} + +void ofmem_arch_create_translation_entry(ucell *transentry, translation_t *t) +{ +	/* Generate translation property entry for SPARC. While there is no +	formal documentation for this, both Linux kernel and OpenSolaris sources +	expect a translation property entry to have the following layout: + +		virtual address +		length +		mode (valid TTE for start of translation region) +	*/ + +	transentry[0] = t->virt; +	transentry[1] = t->size; +	transentry[2] = t->phys | t->mode | SPITFIRE_TTE_VALID; +} + +/* Return the size of a memory available entry given the phandle in cells */ +int ofmem_arch_get_available_entry_size(phandle_t ph) +{ +	if (ph == s_phandle_memory) { +		return 1 + ofmem_arch_get_physaddr_cellsize(); +	} else { +		return 1 + 1; +	} +} + +/* Generate memory available property entry for Sparc64 */ +void ofmem_arch_create_available_entry(phandle_t ph, ucell *availentry, phys_addr_t start, ucell size) +{ +	int i = 0; + +	if (ph == s_phandle_memory) { +		i += ofmem_arch_encode_physaddr(availentry, start); +	} else { +		availentry[i++] = start; +	} +     +	availentry[i] = size; +} + +/* Unmap a set of pages */ +void ofmem_arch_unmap_pages(ucell virt, ucell size) +{ +    ucell va; + +    /* align address to 8k */ +    virt &= ~PAGE_MASK_8K; + +    /* align size to 8k */ +    size = (size + PAGE_MASK_8K) & ~PAGE_MASK_8K; + +    for (va = virt; va < virt + size; va += PAGE_SIZE_8K) { +        itlb_demap(va); +        dtlb_demap(va); +    }	 +} + +/* Map a set of pages */ +void ofmem_arch_map_pages(phys_addr_t phys, ucell virt, ucell size, ucell mode) +{ +    unsigned long tte_data, currsize; + +    /* Install locked tlb entries now */ +    if (mode & SPITFIRE_TTE_LOCKED) { + +        /* aligned to 8k page */ +        size = (size + PAGE_MASK_8K) & ~PAGE_MASK_8K; + +        while (size > 0) { +            currsize = size; +            if (currsize >= PAGE_SIZE_4M && +                (virt & PAGE_MASK_4M) == 0 && +                (phys & PAGE_MASK_4M) == 0) { +                currsize = PAGE_SIZE_4M; +                tte_data = 6ULL << 60; +            } else if (currsize >= PAGE_SIZE_512K && +                   (virt & PAGE_MASK_512K) == 0 && +                   (phys & PAGE_MASK_512K) == 0) { +                currsize = PAGE_SIZE_512K; +                tte_data = 4ULL << 60; +            } else if (currsize >= PAGE_SIZE_64K && +                   (virt & PAGE_MASK_64K) == 0 && +                   (phys & PAGE_MASK_64K) == 0) { +                currsize = PAGE_SIZE_64K; +                tte_data = 2ULL << 60; +            } else { +                currsize = PAGE_SIZE_8K; +                tte_data = 0; +            } + +            tte_data |= phys | mode | SPITFIRE_TTE_VALID; + +            itlb_load2(virt, tte_data); +            dtlb_load2(virt, tte_data); +     +            size -= currsize; +            phys += currsize; +            virt += currsize; +        } +    } +} + +/************************************************************************/ +/* misc                                                                 */ +/************************************************************************/ + +int ofmem_arch_get_physaddr_cellsize(void) +{ +    return 1; +} + +int ofmem_arch_encode_physaddr(ucell *p, phys_addr_t value) +{ +    p[0] = value; +    return 1; +} + +ucell ofmem_arch_default_translation_mode( phys_addr_t phys ) +{ +	/* Writable, cacheable */ +	/* Privileged and not locked */ +	return SPITFIRE_TTE_CP | SPITFIRE_TTE_CV | SPITFIRE_TTE_WRITABLE | SPITFIRE_TTE_PRIVILEGED; +} + +ucell ofmem_arch_io_translation_mode( phys_addr_t phys ) +{ +	/* Writable, privileged and not locked */ +	return SPITFIRE_TTE_CV | SPITFIRE_TTE_WRITABLE | SPITFIRE_TTE_PRIVILEGED; +} + +/* Architecture-specific OFMEM helpers */ +unsigned long +find_tte(unsigned long va) +{ +	translation_t *t = *g_ofmem_translations; +	unsigned long tte_data; +	 +	/* Search the ofmem linked list for this virtual address */ +	while (t != NULL) { +		/* Find the correct range */ +		if (va >= t->virt && va < (t->virt + t->size)) { + +			/* valid tte, 8k size */ +			tte_data = SPITFIRE_TTE_VALID; + +			/* mix in phys address mode */ +			tte_data |= t->mode; + +			/* mix in page physical address = t->phys + offset */ +			tte_data |= t->phys + (va - t->virt); + +			/* return tte_data */ +			return tte_data; +		} +		t = t->next; +	} + +	/* Couldn't find tte */ +	return -1; +} + +/* ITLB handlers */ +void +itlb_load2(unsigned long vaddr, unsigned long tte_data) +{ +    asm("stxa %0, [%1] %2\n" +        "stxa %3, [%%g0] %4\n" +        : : "r" (vaddr), "r" (48), "i" (ASI_IMMU), +          "r" (tte_data), "i" (ASI_ITLB_DATA_IN)); +} + +void +itlb_load3(unsigned long vaddr, unsigned long tte_data, +           unsigned long tte_index) +{ +    asm("stxa %0, [%1] %2\n" +        "stxa %3, [%4] %5\n" +        : : "r" (vaddr), "r" (48), "i" (ASI_IMMU), +          "r" (tte_data), "r" (tte_index << 3), "i" (ASI_ITLB_DATA_ACCESS)); +} + +unsigned long +itlb_faultva(void) +{ +    unsigned long faultva; + +    asm("ldxa [%1] %2, %0\n" +        : "=r" (faultva) +        : "r" (48), "i" (ASI_IMMU)); + +    return faultva; +} + +void +itlb_demap(unsigned long vaddr) +{ +    asm("stxa %0, [%0] %1\n" +        : : "r" (vaddr), "i" (ASI_IMMU_DEMAP)); +} + +/* DTLB handlers */ +void +dtlb_load2(unsigned long vaddr, unsigned long tte_data) +{ +    asm("stxa %0, [%1] %2\n" +        "stxa %3, [%%g0] %4\n" +        : : "r" (vaddr), "r" (48), "i" (ASI_DMMU), +          "r" (tte_data), "i" (ASI_DTLB_DATA_IN)); +} + +void +dtlb_load3(unsigned long vaddr, unsigned long tte_data, +           unsigned long tte_index) +{ +    asm("stxa %0, [%1] %2\n" +        "stxa %3, [%4] %5\n" +        : : "r" (vaddr), "r" (48), "i" (ASI_DMMU), +          "r" (tte_data), "r" (tte_index << 3), "i" (ASI_DTLB_DATA_ACCESS)); +} + +unsigned long +dtlb_faultva(void) +{ +    unsigned long faultva; + +    asm("ldxa [%1] %2, %0\n" +        : "=r" (faultva) +        : "r" (48), "i" (ASI_DMMU)); + +    return faultva; +} + +void +dtlb_demap(unsigned long vaddr) +{ +    asm("stxa %0, [%0] %1\n" +        : : "r" (vaddr), "i" (ASI_DMMU_DEMAP)); +} + +/************************************************************************/ +/* init / cleanup                                                       */ +/************************************************************************/ + +static int remap_page_range( phys_addr_t phys, ucell virt, ucell size, ucell mode ) +{ +	ofmem_claim_phys(phys, size, 0); +	ofmem_claim_virt(virt, size, 0); +	ofmem_map_page_range(phys, virt, size, mode); +	if (!(mode & SPITFIRE_TTE_LOCKED)) { +		OFMEM_TRACE("remap_page_range clearing translation " FMT_ucellx +				" -> " FMT_ucellx " " FMT_ucellx " mode " FMT_ucellx "\n", +				virt, phys, size, mode ); +		ofmem_arch_unmap_pages(virt, size); +	} +	return 0; +} + +#define RETAIN_MAGIC	0x1100220033004400 + +void ofmem_init( void ) +{ +	retain_t *retained = ofmem_arch_get_retained(); +	int i; + +	memset(&s_ofmem_data, 0, sizeof(s_ofmem_data)); +	s_ofmem_data.ofmem.ramsize = qemu_mem_size; + +	/* inherit translations set up by entry.S */ +	ofmem_walk_boot_map(remap_page_range); + +        /* Map the memory */ +        ofmem_map_page_range(PAGE_SIZE, PAGE_SIZE, 0x800000, 0x36); + +	if (!(retained->magic == RETAIN_MAGIC)) { +		OFMEM_TRACE("ofmem_init: no retained magic found, creating\n"); +		retained->magic = RETAIN_MAGIC; +		retained->numentries = 0; +	} else { +		OFMEM_TRACE("ofmem_init: retained magic found, total %lld mappings\n", retained->numentries);	 + +		/* Mark physical addresses as used so they are not reallocated */ +		for (i = 0; i < retained->numentries; i++) { +			ofmem_claim_phys(retained->retain_phys_range[i].start,  +				retained->retain_phys_range[i].size, 0); +		} + +		/* Reset retained area for next reset */ +		retained->magic = RETAIN_MAGIC; +		retained->numentries = 0; +	} +} diff --git a/roms/openbios/arch/sparc64/openbios.c b/roms/openbios/arch/sparc64/openbios.c new file mode 100644 index 00000000..4557f7f9 --- /dev/null +++ b/roms/openbios/arch/sparc64/openbios.c @@ -0,0 +1,664 @@ +/* tag: openbios forth environment, executable code + * + * Copyright (C) 2003 Patrick Mauritz, Stefan Reinauer + * + * See the file "COPYING" for further information about + * the copyright and warranty status of this work. + */ + +#include "config.h" +#include "libopenbios/openbios.h" +#include "libopenbios/bindings.h" +#include "libopenbios/console.h" +#include "drivers/drivers.h" +#include "dict.h" +#include "arch/common/nvram.h" +#include "packages/nvram.h" +#include "libopenbios/sys_info.h" +#include "openbios.h" +#include "drivers/pci.h" +#include "asm/pci.h" +#include "boot.h" +#include "../../drivers/timer.h" // XXX +#define NO_QEMU_PROTOS +#include "arch/common/fw_cfg.h" +#include "arch/sparc64/ofmem_sparc64.h" +#include "spitfire.h" + +#define UUID_FMT "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x" + +#define APB_SPECIAL_BASE     0x1fe00000000ULL +#define APB_MEM_BASE         0x1ff00000000ULL + +#define MEMORY_SIZE     (512*1024)      /* 512K ram for hosted system */ + +// XXX +#define NVRAM_BASE       0x2000 +#define NVRAM_SIZE       0x2000 +#define NVRAM_IDPROM     0x1fd8 +#define NVRAM_IDPROM_SIZE 32 +#define NVRAM_OB_START   (0) +#define NVRAM_OB_SIZE    ((NVRAM_IDPROM - NVRAM_OB_START) & ~15) + +static uint8_t idprom[NVRAM_IDPROM_SIZE]; + +struct hwdef { +    pci_arch_t pci; +    uint16_t machine_id_low, machine_id_high; +}; + +static const struct hwdef hwdefs[] = { +    { +        .pci = { +            .name = "SUNW,sabre", +            .vendor_id = PCI_VENDOR_ID_SUN, +            .device_id = PCI_DEVICE_ID_SUN_SABRE, +            .cfg_addr = APB_SPECIAL_BASE + 0x1000000ULL, // PCI bus configuration space +            .cfg_data = APB_MEM_BASE,                    // PCI bus memory space +            .cfg_base = APB_SPECIAL_BASE, +            .cfg_len = 0x2000000, +            .host_pci_base = APB_MEM_BASE, +            .pci_mem_base = 0x100000, /* avoid VGA at 0xa0000 */ +            .mem_len = 0x10000000, +            .io_base = APB_SPECIAL_BASE + 0x2000000ULL, // PCI Bus I/O space +            .io_len = 0x10000, +            .irqs = { 0, 1, 2, 3 }, +        }, +        .machine_id_low = 0, +        .machine_id_high = 255, +    }, +}; + +struct cpudef { +    unsigned long iu_version; +    const char *name; +    unsigned long ecache_associativity; +    unsigned long ecache_line_size; +    unsigned long ecache_size; +    unsigned long num_dtlb_entries; +    unsigned long dcache_associativity; +    unsigned long dcache_line_size; +    unsigned long dcache_size; +    unsigned long num_itlb_entries; +    unsigned long icache_associativity; +    unsigned long icache_line_size; +    unsigned long icache_size; +}; + +/* +  ( addr -- ? ) +*/ + +extern volatile uint64_t client_tba; + +static void +set_trap_table(void) +{ +    unsigned long addr; + +    addr = POP(); + +    /* Update client_tba to be updated on CIF exit */ +    client_tba = addr; +} + +/* Reset control register is defined in 17.2.7.3 of US IIi User Manual */ +static void +sparc64_reset_all(void) +{ +    unsigned long addr = 0x1fe0000f020ULL; +    unsigned long val = 1 << 29; + +    asm("stxa %0, [%1] 0x15\n\t" +        : : "r" (val), "r" (addr) : "memory"); +} + +/* PCI Target Address Space Register (see UltraSPARC IIi User's Manual +  section 19.3.0.4) */ +#define PBM_PCI_TARGET_AS              0x2028 +#define PBM_PCI_TARGET_AS_CD_ENABLE    0x40 + +static void +sparc64_set_tas_register(unsigned long val) +{ +    unsigned long addr = APB_SPECIAL_BASE + PBM_PCI_TARGET_AS; + +    asm("stxa %0, [%1] 0x15\n\t" +        : : "r" (val), "r" (addr) : "memory"); +} + +static void cpu_generic_init(const struct cpudef *cpu, uint32_t clock_frequency) +{ +    unsigned long iu_version; + +    push_str("/"); +    fword("find-device"); + +    fword("new-device"); + +    push_str(cpu->name); +    fword("device-name"); + +    push_str("cpu"); +    fword("device-type"); + +    asm("rdpr %%ver, %0\n" +        : "=r"(iu_version) :); + +    PUSH((iu_version >> 48) & 0xff); +    fword("encode-int"); +    push_str("manufacturer#"); +    fword("property"); + +    PUSH((iu_version >> 32) & 0xff); +    fword("encode-int"); +    push_str("implementation#"); +    fword("property"); + +    PUSH((iu_version >> 24) & 0xff); +    fword("encode-int"); +    push_str("mask#"); +    fword("property"); + +    PUSH(9); +    fword("encode-int"); +    push_str("sparc-version"); +    fword("property"); + +    PUSH(0); +    fword("encode-int"); +    push_str("cpuid"); +    fword("property"); + +    PUSH(0); +    fword("encode-int"); +    push_str("upa-portid"); +    fword("property"); + +    PUSH(clock_frequency); +    fword("encode-int"); +    push_str("clock-frequency"); +    fword("property"); + +    PUSH(cpu->ecache_associativity); +    fword("encode-int"); +    push_str("ecache-associativity"); +    fword("property"); + +    PUSH(cpu->ecache_line_size); +    fword("encode-int"); +    push_str("ecache-line-size"); +    fword("property"); + +    PUSH(cpu->ecache_size); +    fword("encode-int"); +    push_str("ecache-size"); +    fword("property"); + +    PUSH(cpu->dcache_associativity); +    fword("encode-int"); +    push_str("dcache-associativity"); +    fword("property"); + +    PUSH(cpu->dcache_line_size); +    fword("encode-int"); +    push_str("dcache-line-size"); +    fword("property"); + +    PUSH(cpu->dcache_size); +    fword("encode-int"); +    push_str("dcache-size"); +    fword("property"); + +    PUSH(cpu->icache_associativity); +    fword("encode-int"); +    push_str("icache-associativity"); +    fword("property"); + +    PUSH(cpu->ecache_line_size); +    fword("encode-int"); +    push_str("icache-line-size"); +    fword("property"); + +    PUSH(cpu->ecache_size); +    fword("encode-int"); +    push_str("icache-size"); +    fword("property"); + +    PUSH(cpu->num_itlb_entries); +    fword("encode-int"); +    push_str("#itlb-entries"); +    fword("property"); + +    PUSH(cpu->num_dtlb_entries); +    fword("encode-int"); +    push_str("#dtlb-entries"); +    fword("property"); + +    fword("finish-device"); + +    // Trap table +    push_str("/openprom/client-services"); +    fword("find-device"); +    bind_func("SUNW,set-trap-table", set_trap_table); + +    // Reset +    bind_func("sparc64-reset-all", sparc64_reset_all); +    push_str("' sparc64-reset-all to reset-all"); +    fword("eval"); +} + +static const struct cpudef sparc_defs[] = { +    { +        .iu_version = (0x04ULL << 48) | (0x02ULL << 32), +        .name = "FJSV,GP", +    }, +    { +        .iu_version = (0x04ULL << 48) | (0x03ULL << 32), +        .name = "FJSV,GPUSK", +    }, +    { +        .iu_version = (0x04ULL << 48) | (0x04ULL << 32), +        .name = "FJSV,GPUSC", +    }, +    { +        .iu_version = (0x04ULL << 48) | (0x05ULL << 32), +        .name = "FJSV,GPUZC", +    }, +    { +        .iu_version = (0x17ULL << 48) | (0x10ULL << 32), +        .name = "SUNW,UltraSPARC", +	.ecache_associativity = 1, .ecache_line_size = 0x40, .ecache_size = 0x100000, +	.dcache_associativity = 1, .dcache_line_size = 0x20, .dcache_size = 0x4000, +	.icache_associativity = 2, .icache_line_size = 0x20, .icache_size = 0x4000, +	.num_dtlb_entries = 0x40, .num_itlb_entries = 0x40, +    }, +    { +        .iu_version = (0x17ULL << 48) | (0x11ULL << 32), +        .name = "SUNW,UltraSPARC-II", +	.ecache_associativity = 1, .ecache_line_size = 0x40, .ecache_size = 0x100000, +	.dcache_associativity = 1, .dcache_line_size = 0x20, .dcache_size = 0x4000, +	.icache_associativity = 2, .icache_line_size = 0x20, .icache_size = 0x4000, +	.num_dtlb_entries = 0x40, .num_itlb_entries = 0x40, +    }, +    { +        .iu_version = (0x17ULL << 48) | (0x12ULL << 32), +        .name = "SUNW,UltraSPARC-IIi", +	.ecache_associativity = 1, .ecache_line_size = 0x40, .ecache_size = 0x40000, +	.dcache_associativity = 1, .dcache_line_size = 0x20, .dcache_size = 0x4000, +	.icache_associativity = 2, .icache_line_size = 0x20, .icache_size = 0x4000, +	.num_dtlb_entries = 0x40, .num_itlb_entries = 0x40, +    }, +    { +        .iu_version = (0x17ULL << 48) | (0x13ULL << 32), +        .name = "SUNW,UltraSPARC-IIe", +    }, +    { +        .iu_version = (0x3eULL << 48) | (0x14ULL << 32), +        .name = "SUNW,UltraSPARC-III", +    }, +    { +        .iu_version = (0x3eULL << 48) | (0x15ULL << 32), +        .name = "SUNW,UltraSPARC-III+", +    }, +    { +        .iu_version = (0x3eULL << 48) | (0x16ULL << 32), +        .name = "SUNW,UltraSPARC-IIIi", +    }, +    { +        .iu_version = (0x3eULL << 48) | (0x18ULL << 32), +        .name = "SUNW,UltraSPARC-IV", +    }, +    { +        .iu_version = (0x3eULL << 48) | (0x19ULL << 32), +        .name = "SUNW,UltraSPARC-IV+", +    }, +    { +        .iu_version = (0x3eULL << 48) | (0x22ULL << 32), +        .name = "SUNW,UltraSPARC-IIIi+", +    }, +    { +        .iu_version = (0x3eULL << 48) | (0x23ULL << 32), +        .name = "SUNW,UltraSPARC-T1", +    }, +    { +        .iu_version = (0x3eULL << 48) | (0x24ULL << 32), +        .name = "SUNW,UltraSPARC-T2", +    }, +    { +        .iu_version = (0x22ULL << 48) | (0x10ULL << 32), +        .name = "SUNW,UltraSPARC", +    }, +}; + +static const struct cpudef * +id_cpu(void) +{ +    unsigned long iu_version; +    unsigned int i; + +    asm("rdpr %%ver, %0\n" +        : "=r"(iu_version) :); +    iu_version &= 0xffffffff00000000ULL; + +    for (i = 0; i < sizeof(sparc_defs)/sizeof(struct cpudef); i++) { +        if (iu_version == sparc_defs[i].iu_version) +            return &sparc_defs[i]; +    } +    printk("Unknown cpu (psr %lx), freezing!\n", iu_version); +    for (;;); +} + +static void nvram_read(uint16_t offset, char *buf, unsigned int nbytes) +{ +    unsigned int i; + +    for (i = 0; i < nbytes; i++) { +        buf[i] = inb(NVRAM_BASE + offset + i); +    } +} + +static void nvram_write(uint16_t offset, const char *buf, unsigned int nbytes) +{ +    unsigned int i; + +    for (i = 0; i < nbytes; i++) { +        outb(buf[i], NVRAM_BASE + offset + i); +    } +} + +static uint8_t qemu_uuid[16]; + +void arch_nvram_get(char *data) +{ +    char *obio_cmdline; +    uint32_t size = 0; +    const struct cpudef *cpu; +    char buf[256]; +    uint32_t temp; +    uint64_t ram_size; +    uint32_t clock_frequency; +    uint16_t machine_id; +    const char *stdin_path, *stdout_path; + +    fw_cfg_init(); + +    fw_cfg_read(FW_CFG_SIGNATURE, buf, 4); +    buf[4] = '\0'; + +    printk("Configuration device id %s", buf); + +    temp = fw_cfg_read_i32(FW_CFG_ID); +    machine_id = fw_cfg_read_i16(FW_CFG_MACHINE_ID); + +    printk(" version %d machine id %d\n", temp, machine_id); + +    if (temp != 1) { +        printk("Incompatible configuration device version, freezing\n"); +        for(;;); +    } + +    kernel_size = fw_cfg_read_i32(FW_CFG_KERNEL_SIZE); +    if (kernel_size) +        kernel_image = fw_cfg_read_i64(FW_CFG_KERNEL_ADDR); + +    size = fw_cfg_read_i32(FW_CFG_CMDLINE_SIZE); +    if (size) { +	obio_cmdline = (char *)malloc(size + 1); +        fw_cfg_read(FW_CFG_CMDLINE_DATA, obio_cmdline, size); +	obio_cmdline[size] = '\0'; +    } else { +	obio_cmdline = strdup("");     +    } +    qemu_cmdline = (uint64_t)obio_cmdline; +    cmdline_size = size; +    boot_device = fw_cfg_read_i16(FW_CFG_BOOT_DEVICE); + +    if (kernel_size) +        printk("kernel addr %llx size %llx\n", kernel_image, kernel_size); +    if (size) +        printk("kernel cmdline %s\n", obio_cmdline); + +    nvram_read(NVRAM_OB_START, data, NVRAM_OB_SIZE); + +    temp = fw_cfg_read_i32(FW_CFG_NB_CPUS); + +    printk("CPUs: %x", temp); + +    clock_frequency = 100000000; + +    cpu = id_cpu(); +    //cpu->initfn(); +    cpu_generic_init(cpu, clock_frequency); +    printk(" x %s\n", cpu->name); + +    // Add /uuid +    fw_cfg_read(FW_CFG_UUID, (char *)qemu_uuid, 16); + +    printk("UUID: " UUID_FMT "\n", qemu_uuid[0], qemu_uuid[1], qemu_uuid[2], +           qemu_uuid[3], qemu_uuid[4], qemu_uuid[5], qemu_uuid[6], +           qemu_uuid[7], qemu_uuid[8], qemu_uuid[9], qemu_uuid[10], +           qemu_uuid[11], qemu_uuid[12], qemu_uuid[13], qemu_uuid[14], +           qemu_uuid[15]); + +    push_str("/"); +    fword("find-device"); + +    PUSH((long)&qemu_uuid); +    PUSH(16); +    fword("encode-bytes"); +    push_str("uuid"); +    fword("property"); + +    // Add /idprom +    nvram_read(NVRAM_IDPROM, (char *)idprom, NVRAM_IDPROM_SIZE); + +    PUSH((long)&idprom); +    PUSH(32); +    fword("encode-bytes"); +    push_str("idprom"); +    fword("property"); + +    PUSH(500 * 1000 * 1000); +    fword("encode-int"); +    push_str("clock-frequency"); +    fword("property"); + +    ram_size = fw_cfg_read_i64(FW_CFG_RAM_SIZE); + +    ob_mmu_init(cpu->name, ram_size); + +    /* Setup nvram variables */ +    push_str("/options"); +    fword("find-device"); + +    switch (boot_device) { +        case 'a': +            push_str("/obio/SUNW,fdtwo"); +            break; +        case 'c': +            push_str("disk:a"); +            break; +        default: +        case 'd': +            push_str("cdrom:f cdrom"); +            break; +        case 'n': +            push_str("net"); +            break; +    } + +    fword("encode-string"); +    push_str("boot-device"); +    fword("property"); + +    push_str(obio_cmdline); +    fword("encode-string"); +    push_str("boot-file"); +    fword("property"); + +    /* Set up other properties */ +    push_str("/chosen"); +    fword("find-device"); + +    if (fw_cfg_read_i16(FW_CFG_NOGRAPHIC)) { +        stdin_path = stdout_path = "ttya"; +    } else { +        stdin_path = "keyboard"; +        stdout_path = "screen"; +    } + +    push_str(stdin_path); +    push_str("input-device"); +    fword("$setenv"); + +    push_str(stdout_path); +    push_str("output-device"); +    fword("$setenv"); +} + +void arch_nvram_put(char *data) +{ +    nvram_write(0, data, NVRAM_OB_SIZE); +} + +int arch_nvram_size(void) +{ +    return NVRAM_OB_SIZE; +} + +void setup_timers(void) +{ +} + +void udelay(unsigned int usecs) +{ +    volatile int i; + +    for (i = 0; i < usecs * 100; i++); +} + +static void init_memory(void) +{ +    phys_addr_t phys; +    ucell virt; +     +    /* Claim the memory from OFMEM (align to 512K so we only take 1 TLB slot) */ +    phys = ofmem_claim_phys(-1, MEMORY_SIZE, PAGE_SIZE_512K); +    if (!phys) +        printk("panic: not enough physical memory on host system.\n"); +     +    virt = ofmem_claim_virt(-1, MEMORY_SIZE, PAGE_SIZE_512K); +    if (!virt) +        printk("panic: not enough virtual memory on host system.\n"); + +    /* Generate the mapping (and lock translation into the TLBs) */ +    ofmem_map(phys, virt, MEMORY_SIZE, ofmem_arch_default_translation_mode(phys) | SPITFIRE_TTE_LOCKED); + +    /* we push start and end of memory to the stack +     * so that it can be used by the forth word QUIT +     * to initialize the memory allocator +     */ +     +    PUSH(virt); +    PUSH(virt + MEMORY_SIZE); +} + +extern volatile uint64_t *obp_ticks_pointer; + +static void +arch_init( void ) +{ +	openbios_init(); +	modules_init(); +#ifdef CONFIG_DRIVER_PCI +        ob_pci_init(); + +        /* Set TAS register to match the virtual-dma properties +           set during sabre configure */ +        sparc64_set_tas_register(PBM_PCI_TARGET_AS_CD_ENABLE); +#endif +        nvconf_init(); +        device_end(); + +        /* Point to the Forth obp-ticks variable */ +        fword("obp-ticks"); +        obp_ticks_pointer = cell2pointer(POP()); + +	bind_func("platform-boot", boot ); +	bind_func("(go)", go); +} + +unsigned long isa_io_base; + +extern struct _console_ops arch_console_ops; + +int openbios(void) +{ +        unsigned int i; +        uint16_t machine_id; +        const struct hwdef *hwdef = NULL; + + +        for (i = 0; i < sizeof(hwdefs) / sizeof(struct hwdef); i++) { +            isa_io_base = hwdefs[i].pci.io_base; +            machine_id = fw_cfg_read_i16(FW_CFG_MACHINE_ID); +            if (hwdefs[i].machine_id_low <= machine_id && +                hwdefs[i].machine_id_high >= machine_id) { +                hwdef = &hwdefs[i]; +                arch = &hwdefs[i].pci; +                break; +            } +        } +        if (!hwdef) +            for(;;); // Internal inconsistency, hang + +#ifdef CONFIG_DEBUG_CONSOLE +        init_console(arch_console_ops); +#ifdef CONFIG_DEBUG_CONSOLE_SERIAL +	uart_init(CONFIG_SERIAL_PORT, CONFIG_SERIAL_SPEED); +#endif +        printk("OpenBIOS for Sparc64\n"); +#endif + +        ofmem_init(); + +        collect_sys_info(&sys_info); + +        dict = (unsigned char *)sys_info.dict_start; +        dicthead = (cell)sys_info.dict_end; +        last = sys_info.dict_last; +        dictlimit = sys_info.dict_limit; + +	forth_init(); + +#ifdef CONFIG_DEBUG_BOOT +	printk("forth started.\n"); +	printk("initializing memory..."); +#endif + +	init_memory(); + +#ifdef CONFIG_DEBUG_BOOT +	printk("done\n"); +#endif + +	PUSH_xt( bind_noname_func(arch_init) ); +	fword("PREPOST-initializer"); + +	PC = (ucell)findword("initialize-of"); + +	if (!PC) { +		printk("panic: no dictionary entry point.\n"); +		return -1; +	} +#ifdef CONFIG_DEBUG_DICTIONARY +	printk("done (%d bytes).\n", dicthead); +	printk("Jumping to dictionary...\n"); +#endif + +	enterforth((xt_t)PC); +        printk("falling off...\n"); +        free(dict); +	return 0; +} diff --git a/roms/openbios/arch/sparc64/openbios.h b/roms/openbios/arch/sparc64/openbios.h new file mode 100644 index 00000000..2146300d --- /dev/null +++ b/roms/openbios/arch/sparc64/openbios.h @@ -0,0 +1,27 @@ +/* + *   Creation Date: <2004/01/15 16:14:05 samuel> + *   Time-stamp: <2004/01/15 16:14:05 samuel> + * + *	<openbios.h> + * + * + * + *   Copyright (C) 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 + *   version 2 + * + */ + +#ifndef _H_OPENBIOS +#define _H_OPENBIOS + +int openbios(void); + +/* console.c */ +#ifdef CONFIG_DEBUG_CONSOLE +extern void     video_init(void); +#endif + +#endif   /* _H_OPENBIOS */ diff --git a/roms/openbios/arch/sparc64/openprom.h b/roms/openbios/arch/sparc64/openprom.h new file mode 100644 index 00000000..0a336901 --- /dev/null +++ b/roms/openbios/arch/sparc64/openprom.h @@ -0,0 +1,281 @@ +/* $Id: openprom.h,v 1.9 2001/03/16 10:22:02 davem Exp $ */ +#ifndef __SPARC64_OPENPROM_H +#define __SPARC64_OPENPROM_H + +/* openprom.h:  Prom structures and defines for access to the OPENBOOT + *              prom routines and data areas. + * + * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) + */ + +#ifndef __ASSEMBLY__ +/* V0 prom device operations. */ +struct linux_dev_v0_funcs { +	int (*v0_devopen)(char *device_str); +	int (*v0_devclose)(int dev_desc); +	int (*v0_rdblkdev)(int dev_desc, int num_blks, int blk_st, char *buf); +	int (*v0_wrblkdev)(int dev_desc, int num_blks, int blk_st, char *buf); +	int (*v0_wrnetdev)(int dev_desc, int num_bytes, char *buf); +	int (*v0_rdnetdev)(int dev_desc, int num_bytes, char *buf); +	int (*v0_rdchardev)(int dev_desc, int num_bytes, int dummy, char *buf); +	int (*v0_wrchardev)(int dev_desc, int num_bytes, int dummy, char *buf); +	int (*v0_seekdev)(int dev_desc, long logical_offst, int from); +}; + +/* V2 and later prom device operations. */ +struct linux_dev_v2_funcs { +	int (*v2_inst2pkg)(int d);	/* Convert ihandle to phandle */ +	char * (*v2_dumb_mem_alloc)(char *va, unsigned sz); +	void (*v2_dumb_mem_free)(char *va, unsigned sz); + +	/* To map devices into virtual I/O space. */ +	char * (*v2_dumb_mmap)(char *virta, int which_io, unsigned paddr, unsigned sz); +	void (*v2_dumb_munmap)(char *virta, unsigned size); + +	int (*v2_dev_open)(char *devpath); +	void (*v2_dev_close)(int d); +	int (*v2_dev_read)(int d, char *buf, int nbytes); +	int (*v2_dev_write)(int d, char *buf, int nbytes); +	int (*v2_dev_seek)(int d, int hi, int lo); + +	/* Never issued (multistage load support) */ +	void (*v2_wheee2)(void); +	void (*v2_wheee3)(void); +}; + +struct linux_mlist_v0 { +	struct linux_mlist_v0 *theres_more; +	unsigned start_adr; +	unsigned num_bytes; +}; + +struct linux_mem_v0 { +	struct linux_mlist_v0 **v0_totphys; +	struct linux_mlist_v0 **v0_prommap; +	struct linux_mlist_v0 **v0_available; /* What we can use */ +}; + +/* Arguments sent to the kernel from the boot prompt. */ +struct linux_arguments_v0 { +	char *argv[8]; +	char args[100]; +	char boot_dev[2]; +	int boot_dev_ctrl; +	int boot_dev_unit; +	int dev_partition; +	char *kernel_file_name; +	void *aieee1;           /* XXX */ +}; + +/* V2 and up boot things. */ +struct linux_bootargs_v2 { +	char **bootpath; +	char **bootargs; +	int *fd_stdin; +	int *fd_stdout; +}; + +/* The top level PROM vector. */ +struct linux_romvec { +	/* Version numbers. */ +	unsigned int pv_magic_cookie; +	unsigned int pv_romvers; +	unsigned int pv_plugin_revision; +	unsigned int pv_printrev; + +	/* Version 0 memory descriptors. */ +	struct linux_mem_v0 pv_v0mem; + +	/* Node operations. */ +	struct linux_nodeops *pv_nodeops; + +	char **pv_bootstr; +	struct linux_dev_v0_funcs pv_v0devops; + +	char *pv_stdin; +	char *pv_stdout; +#define	PROMDEV_KBD	0		/* input from keyboard */ +#define	PROMDEV_SCREEN	0		/* output to screen */ +#define	PROMDEV_TTYA	1		/* in/out to ttya */ +#define	PROMDEV_TTYB	2		/* in/out to ttyb */ + +	/* Blocking getchar/putchar.  NOT REENTRANT! (grr) */ +	int (*pv_getchar)(void); +	void (*pv_putchar)(int ch); + +	/* Non-blocking variants. */ +	int (*pv_nbgetchar)(void); +	int (*pv_nbputchar)(int ch); + +	void (*pv_putstr)(char *str, int len); + +	/* Miscellany. */ +	void (*pv_reboot)(char *bootstr); +	void (*pv_printf)(__const__ char *fmt, ...); +	void (*pv_abort)(void); +	__volatile__ int *pv_ticks; +	void (*pv_halt)(void); +	void (**pv_synchook)(void); + +	/* Evaluate a forth string, not different proto for V0 and V2->up. */ +	union { +		void (*v0_eval)(int len, char *str); +		void (*v2_eval)(char *str); +	} pv_fortheval; + +	struct linux_arguments_v0 **pv_v0bootargs; + +	/* Get ether address. */ +	unsigned int (*pv_enaddr)(int d, char *enaddr); + +	struct linux_bootargs_v2 pv_v2bootargs; +	struct linux_dev_v2_funcs pv_v2devops; + +	int filler[15]; + +	/* This one is sun4c/sun4 only. */ +	void (*pv_setctxt)(int ctxt, char *va, int pmeg); + +	/* Prom version 3 Multiprocessor routines. This stuff is crazy. +	 * No joke. Calling these when there is only one cpu probably +	 * crashes the machine, have to test this. :-) +	 */ + +	/* v3_cpustart() will start the cpu 'whichcpu' in mmu-context +	 * 'thiscontext' executing at address 'prog_counter' +	 */ +	int (*v3_cpustart)(unsigned int whichcpu, int ctxtbl_ptr, +			   int thiscontext, char *prog_counter); + +	/* v3_cpustop() will cause cpu 'whichcpu' to stop executing +	 * until a resume cpu call is made. +	 */ +	int (*v3_cpustop)(unsigned int whichcpu); + +	/* v3_cpuidle() will idle cpu 'whichcpu' until a stop or +	 * resume cpu call is made. +	 */ +	int (*v3_cpuidle)(unsigned int whichcpu); + +	/* v3_cpuresume() will resume processor 'whichcpu' executing +	 * starting with whatever 'pc' and 'npc' were left at the +	 * last 'idle' or 'stop' call. +	 */ +	int (*v3_cpuresume)(unsigned int whichcpu); +}; + +/* Routines for traversing the prom device tree. */ +struct linux_nodeops { +	int (*no_nextnode)(int node); +	int (*no_child)(int node); +	int (*no_proplen)(int node, char *name); +	int (*no_getprop)(int node, char *name, char *val); +	int (*no_setprop)(int node, char *name, char *val, int len); +	char * (*no_nextprop)(int node, char *name); +}; + +/* More fun PROM structures for device probing. */ +#define PROMREG_MAX     16 +#define PROMVADDR_MAX   16 +#define PROMINTR_MAX    15 + +struct linux_prom_registers { +	unsigned which_io;	/* hi part of physical address			*/ +	unsigned phys_addr;	/* The physical address of this register	*/ +	int reg_size;		/* How many bytes does this register take up?	*/ +}; + +struct linux_prom64_registers { +	long phys_addr; +	long reg_size; +}; + +struct linux_prom_irqs { +	int pri;    /* IRQ priority */ +	int vector; /* This is foobar, what does it do? */ +}; + +/* Element of the "ranges" vector */ +struct linux_prom_ranges { +	unsigned int ot_child_space; +	unsigned int ot_child_base;		/* Bus feels this */ +	unsigned int ot_parent_space; +	unsigned int ot_parent_base;		/* CPU looks from here */ +	unsigned int or_size; +}; + +struct linux_prom64_ranges { +	unsigned long ot_child_base;		/* Bus feels this */ +	unsigned long ot_parent_base;		/* CPU looks from here */ +	unsigned long or_size; +}; + +/* Ranges and reg properties are a bit different for PCI. */ +struct linux_prom_pci_registers { +	unsigned int phys_hi; +	unsigned int phys_mid; +	unsigned int phys_lo; + +	unsigned int size_hi; +	unsigned int size_lo; +}; + +struct linux_prom_pci_ranges { +	unsigned int child_phys_hi;	/* Only certain bits are encoded here. */ +	unsigned int child_phys_mid; +	unsigned int child_phys_lo; + +	unsigned int parent_phys_hi; +	unsigned int parent_phys_lo; + +	unsigned int size_hi; +	unsigned int size_lo; +}; + +struct linux_prom_pci_intmap { +	unsigned int phys_hi; +	unsigned int phys_mid; +	unsigned int phys_lo; + +	unsigned int interrupt; + +	int          cnode; +	unsigned int cinterrupt; +}; + +struct linux_prom_pci_intmask { +	unsigned int phys_hi; +	unsigned int phys_mid; +	unsigned int phys_lo; +	unsigned int interrupt; +}; + +struct linux_prom_ebus_ranges { +	unsigned int child_phys_hi; +	unsigned int child_phys_lo; + +	unsigned int parent_phys_hi; +	unsigned int parent_phys_mid; +	unsigned int parent_phys_lo; + +	unsigned int size; +}; + +struct linux_prom_ebus_intmap { +	unsigned int phys_hi; +	unsigned int phys_lo; + +	unsigned int interrupt; + +	int          cnode; +	unsigned int cinterrupt; +}; + +struct linux_prom_ebus_intmask { +	unsigned int phys_hi; +	unsigned int phys_lo; +	unsigned int interrupt; +}; +#endif /* !(__ASSEMBLY__) */ + +#endif /* !(__SPARC64_OPENPROM_H) */ diff --git a/roms/openbios/arch/sparc64/plainboot.c b/roms/openbios/arch/sparc64/plainboot.c new file mode 100644 index 00000000..08dab2d1 --- /dev/null +++ b/roms/openbios/arch/sparc64/plainboot.c @@ -0,0 +1,21 @@ +/* tag: openbios fixed address forth starter + * + * Copyright (C) 2003 Stefan Reinauer + * + * See the file "COPYING" for further information about + * the copyright and warranty status of this work. + */ + +#include "config.h" +#include "libopenbios/sys_info.h" +#include "multiboot.h" + +#define FIXED_DICTSTART 0xfffe0000 +#define FIXED_DICTEND   0xfffeffff + +void collect_multiboot_info(struct sys_info *info); +void collect_multiboot_info(struct sys_info *info) +{ +	info->dict_start=(unsigned long *)FIXED_DICTSTART; +	info->dict_end=(unsigned long *)FIXED_DICTEND; +} diff --git a/roms/openbios/arch/sparc64/pstate.h b/roms/openbios/arch/sparc64/pstate.h new file mode 100644 index 00000000..f32d6f7d --- /dev/null +++ b/roms/openbios/arch/sparc64/pstate.h @@ -0,0 +1,90 @@ +/* $Id: pstate.h,v 1.6 1997/06/25 07:39:45 jj Exp $ */ +#ifndef _SPARC64_PSTATE_H +#define _SPARC64_PSTATE_H + +#include "const.h" + +/* The V9 PSTATE Register (with SpitFire extensions). + * + * ----------------------------------------------------------------------- + * | Resv | IG | MG | CLE | TLE |  MM  | RED | PEF | AM | PRIV | IE | AG | + * ----------------------------------------------------------------------- + *  63  12  11   10    9     8    7   6   5     4     3     2     1    0 + */ +#define PSTATE_IG   _AC(0x0000000000000800,UL) /* Interrupt Globals.	*/ +#define PSTATE_MG   _AC(0x0000000000000400,UL) /* MMU Globals.		*/ +#define PSTATE_CLE  _AC(0x0000000000000200,UL) /* Current Little Endian.*/ +#define PSTATE_TLE  _AC(0x0000000000000100,UL) /* Trap Little Endian.	*/ +#define PSTATE_MM   _AC(0x00000000000000c0,UL) /* Memory Model.		*/ +#define PSTATE_TSO  _AC(0x0000000000000000,UL) /* MM: TotalStoreOrder	*/ +#define PSTATE_PSO  _AC(0x0000000000000040,UL) /* MM: PartialStoreOrder	*/ +#define PSTATE_RMO  _AC(0x0000000000000080,UL) /* MM: RelaxedMemoryOrder*/ +#define PSTATE_RED  _AC(0x0000000000000020,UL) /* Reset Error Debug.	*/ +#define PSTATE_PEF  _AC(0x0000000000000010,UL) /* Floating Point Enable.*/ +#define PSTATE_AM   _AC(0x0000000000000008,UL) /* Address Mask.		*/ +#define PSTATE_PRIV _AC(0x0000000000000004,UL) /* Privilege.		*/ +#define PSTATE_IE   _AC(0x0000000000000002,UL) /* Interrupt Enable.	*/ +#define PSTATE_AG   _AC(0x0000000000000001,UL) /* Alternate Globals.	*/ + +/* The V9 TSTATE Register (with SpitFire and Linux extensions). + * + * --------------------------------------------------------------- + * |  Resv  |  CCR  |  ASI  |  %pil  |  PSTATE  |  Resv  |  CWP  | + * --------------------------------------------------------------- + *  63    40 39   32 31   24 23    20 19       8 7      5 4     0 + */ +#define TSTATE_CCR	_AC(0x000000ff00000000,UL) /* Condition Codes.	*/ +#define TSTATE_XCC	_AC(0x000000f000000000,UL) /* Condition Codes.	*/ +#define TSTATE_XNEG	_AC(0x0000008000000000,UL) /* %xcc Negative.	*/ +#define TSTATE_XZERO	_AC(0x0000004000000000,UL) /* %xcc Zero.	*/ +#define TSTATE_XOVFL	_AC(0x0000002000000000,UL) /* %xcc Overflow.	*/ +#define TSTATE_XCARRY	_AC(0x0000001000000000,UL) /* %xcc Carry.	*/ +#define TSTATE_ICC	_AC(0x0000000f00000000,UL) /* Condition Codes.	*/ +#define TSTATE_INEG	_AC(0x0000000800000000,UL) /* %icc Negative.	*/ +#define TSTATE_IZERO	_AC(0x0000000400000000,UL) /* %icc Zero.	*/ +#define TSTATE_IOVFL	_AC(0x0000000200000000,UL) /* %icc Overflow.	*/ +#define TSTATE_ICARRY	_AC(0x0000000100000000,UL) /* %icc Carry.	*/ +#define TSTATE_ASI	_AC(0x00000000ff000000,UL) /* AddrSpace ID.	*/ +#define TSTATE_PIL	_AC(0x0000000000f00000,UL) /* %pil (Linux traps)*/ +#define TSTATE_PSTATE	_AC(0x00000000000fff00,UL) /* PSTATE.		*/ +#define TSTATE_IG	_AC(0x0000000000080000,UL) /* Interrupt Globals.*/ +#define TSTATE_MG	_AC(0x0000000000040000,UL) /* MMU Globals.	*/ +#define TSTATE_CLE	_AC(0x0000000000020000,UL) /* CurrLittleEndian.	*/ +#define TSTATE_TLE	_AC(0x0000000000010000,UL) /* TrapLittleEndian.	*/ +#define TSTATE_MM	_AC(0x000000000000c000,UL) /* Memory Model.	*/ +#define TSTATE_TSO	_AC(0x0000000000000000,UL) /* MM: TSO		*/ +#define TSTATE_PSO	_AC(0x0000000000004000,UL) /* MM: PSO		*/ +#define TSTATE_RMO	_AC(0x0000000000008000,UL) /* MM: RMO		*/ +#define TSTATE_RED	_AC(0x0000000000002000,UL) /* Reset Error Debug.*/ +#define TSTATE_PEF	_AC(0x0000000000001000,UL) /* FPU Enable.	*/ +#define TSTATE_AM	_AC(0x0000000000000800,UL) /* Address Mask.	*/ +#define TSTATE_PRIV	_AC(0x0000000000000400,UL) /* Privilege.	*/ +#define TSTATE_IE	_AC(0x0000000000000200,UL) /* Interrupt Enable.	*/ +#define TSTATE_AG	_AC(0x0000000000000100,UL) /* Alternate Globals.*/ +#define TSTATE_CWP	_AC(0x000000000000001f,UL) /* Curr Win-Pointer.	*/ + +/* Floating-Point Registers State Register. + * + * -------------------------------- + * |  Resv  |  FEF  |  DU  |  DL  | + * -------------------------------- + *  63     3    2       1      0 + */ +#define FPRS_FEF	_AC(0x0000000000000004,UL) /* FPU Enable.	*/ +#define FPRS_DU		_AC(0x0000000000000002,UL) /* Dirty Upper.	*/ +#define FPRS_DL		_AC(0x0000000000000001,UL) /* Dirty Lower.	*/ + +/* Version Register. + * + * ------------------------------------------------------ + * | MANUF | IMPL | MASK | Resv | MAXTL | Resv | MAXWIN | + * ------------------------------------------------------ + *  63   48 47  32 31  24 23  16 15    8 7    5 4      0 + */ +#define VERS_MANUF	_AC(0xffff000000000000,UL) /* Manufacturer.	*/ +#define VERS_IMPL	_AC(0x0000ffff00000000,UL) /* Implementation.	*/ +#define VERS_MASK	_AC(0x00000000ff000000,UL) /* Mask Set Revision.*/ +#define VERS_MAXTL	_AC(0x000000000000ff00,UL) /* Max Trap Level.	*/ +#define VERS_MAXWIN	_AC(0x000000000000001f,UL) /* Max RegWindow Idx.*/ + +#endif /* !(_SPARC64_PSTATE_H) */ diff --git a/roms/openbios/arch/sparc64/spitfire.h b/roms/openbios/arch/sparc64/spitfire.h new file mode 100644 index 00000000..4a951b1f --- /dev/null +++ b/roms/openbios/arch/sparc64/spitfire.h @@ -0,0 +1,510 @@ +/* $Id: spitfire.h,v 1.18 2001/11/29 16:42:10 kanoj Exp $ + * spitfire.h: SpitFire/BlackBird/Cheetah inline MMU operations. + * + * Copyright (C) 1996 David S. Miller (davem@caip.rutgers.edu) + */ + +#ifndef _SPARC64_SPITFIRE_H +#define _SPARC64_SPITFIRE_H + +#include <asm/asi.h> + +/* The following register addresses are accessible via ASI_DMMU + * and ASI_IMMU, that is there is a distinct and unique copy of + * each these registers for each TLB. + */ +#define TSB_TAG_TARGET		0x0000000000000000 /* All chips				*/ +#define TLB_SFSR		0x0000000000000018 /* All chips				*/ +#define TSB_REG			0x0000000000000028 /* All chips				*/ +#define TLB_TAG_ACCESS		0x0000000000000030 /* All chips				*/ +#define VIRT_WATCHPOINT		0x0000000000000038 /* All chips				*/ +#define PHYS_WATCHPOINT		0x0000000000000040 /* All chips				*/ +#define TSB_EXTENSION_P		0x0000000000000048 /* Ultra-III and later		*/ +#define TSB_EXTENSION_S		0x0000000000000050 /* Ultra-III and later, D-TLB only	*/ +#define TSB_EXTENSION_N		0x0000000000000058 /* Ultra-III and later		*/ +#define TLB_TAG_ACCESS_EXT	0x0000000000000060 /* Ultra-III+ and later		*/ + +/* These registers only exist as one entity, and are accessed + * via ASI_DMMU only. + */ +#define PRIMARY_CONTEXT		0x0000000000000008 +#define SECONDARY_CONTEXT	0x0000000000000010 +#define DMMU_SFAR		0x0000000000000020 +#define VIRT_WATCHPOINT		0x0000000000000038 +#define PHYS_WATCHPOINT		0x0000000000000040 + +#define SPITFIRE_HIGHEST_LOCKED_TLBENT	(64 - 1) + +/* translation table entry bits */ +#define SPITFIRE_TTE_WRITABLE   0x02 +#define SPITFIRE_TTE_PRIVILEGED 0x04 +#define SPITFIRE_TTE_CV         0x10 +#define SPITFIRE_TTE_CP         0x20 +#define SPITFIRE_TTE_LOCKED     0x40 +#define SPITFIRE_TTE_VALID      0x8000000000000000ULL + +#ifndef __ASSEMBLY__ + +enum ultra_tlb_layout { +	spitfire = 0, +	cheetah = 1, +	cheetah_plus = 2, +}; + +extern enum ultra_tlb_layout tlb_type; + +#define CHEETAH_HIGHEST_LOCKED_TLBENT	(16 - 1) + +#define L1DCACHE_SIZE		0x4000 + +#define sparc64_highest_locked_tlbent()	\ +	(tlb_type == spitfire ? \ +	 SPITFIRE_HIGHEST_LOCKED_TLBENT : \ +	 CHEETAH_HIGHEST_LOCKED_TLBENT) + +static __inline__ unsigned long spitfire_get_isfsr(void) +{ +	unsigned long ret; + +	__asm__ __volatile__("ldxa	[%1] %2, %0" +			     : "=r" (ret) +			     : "r" (TLB_SFSR), "i" (ASI_IMMU)); +	return ret; +} + +static __inline__ unsigned long spitfire_get_dsfsr(void) +{ +	unsigned long ret; + +	__asm__ __volatile__("ldxa	[%1] %2, %0" +			     : "=r" (ret) +			     : "r" (TLB_SFSR), "i" (ASI_DMMU)); +	return ret; +} + +static __inline__ unsigned long spitfire_get_sfar(void) +{ +	unsigned long ret; + +	__asm__ __volatile__("ldxa	[%1] %2, %0" +			     : "=r" (ret) +			     : "r" (DMMU_SFAR), "i" (ASI_DMMU)); +	return ret; +} + +static __inline__ void spitfire_put_isfsr(unsigned long sfsr) +{ +	__asm__ __volatile__("stxa	%0, [%1] %2\n\t" +			     "membar	#Sync" +			     : /* no outputs */ +			     : "r" (sfsr), "r" (TLB_SFSR), "i" (ASI_IMMU)); +} + +static __inline__ void spitfire_put_dsfsr(unsigned long sfsr) +{ +	__asm__ __volatile__("stxa	%0, [%1] %2\n\t" +			     "membar	#Sync" +			     : /* no outputs */ +			     : "r" (sfsr), "r" (TLB_SFSR), "i" (ASI_DMMU)); +} + +static __inline__ unsigned long spitfire_get_primary_context(void) +{ +	unsigned long ctx; + +	__asm__ __volatile__("ldxa	[%1] %2, %0" +			     : "=r" (ctx) +			     : "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU)); +	return ctx; +} + +static __inline__ void spitfire_set_primary_context(unsigned long ctx) +{ +	__asm__ __volatile__("stxa	%0, [%1] %2\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (ctx & 0x3ff), +			       "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU)); +	__asm__ __volatile__ ("membar #Sync" : : : "memory"); +} + +static __inline__ unsigned long spitfire_get_secondary_context(void) +{ +	unsigned long ctx; + +	__asm__ __volatile__("ldxa	[%1] %2, %0" +			     : "=r" (ctx) +			     : "r" (SECONDARY_CONTEXT), "i" (ASI_DMMU)); +	return ctx; +} + +static __inline__ void spitfire_set_secondary_context(unsigned long ctx) +{ +	__asm__ __volatile__("stxa	%0, [%1] %2\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (ctx & 0x3ff), +			       "r" (SECONDARY_CONTEXT), "i" (ASI_DMMU)); +	__asm__ __volatile__ ("membar #Sync" : : : "memory"); +} + +/* The data cache is write through, so this just invalidates the + * specified line. + */ +static __inline__ void spitfire_put_dcache_tag(unsigned long addr, unsigned long tag) +{ +	__asm__ __volatile__("stxa	%0, [%1] %2\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (tag), "r" (addr), "i" (ASI_DCACHE_TAG)); +	__asm__ __volatile__ ("membar #Sync" : : : "memory"); +} + +/* The instruction cache lines are flushed with this, but note that + * this does not flush the pipeline.  It is possible for a line to + * get flushed but stale instructions to still be in the pipeline, + * a flush instruction (to any address) is sufficient to handle + * this issue after the line is invalidated. + */ +static __inline__ void spitfire_put_icache_tag(unsigned long addr, unsigned long tag) +{ +	__asm__ __volatile__("stxa	%0, [%1] %2\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (tag), "r" (addr), "i" (ASI_IC_TAG)); +} + +static __inline__ unsigned long spitfire_get_dtlb_data(int entry) +{ +	unsigned long data; + +	__asm__ __volatile__("ldxa	[%1] %2, %0" +			     : "=r" (data) +			     : "r" (entry << 3), "i" (ASI_DTLB_DATA_ACCESS)); + +	/* Clear TTE diag bits. */ +	data &= ~0x0003fe0000000000UL; + +	return data; +} + +static __inline__ unsigned long spitfire_get_dtlb_tag(int entry) +{ +	unsigned long tag; + +	__asm__ __volatile__("ldxa	[%1] %2, %0" +			     : "=r" (tag) +			     : "r" (entry << 3), "i" (ASI_DTLB_TAG_READ)); +	return tag; +} + +static __inline__ void spitfire_put_dtlb_data(int entry, unsigned long data) +{ +	__asm__ __volatile__("stxa	%0, [%1] %2\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (data), "r" (entry << 3), +			       "i" (ASI_DTLB_DATA_ACCESS)); +} + +static __inline__ unsigned long spitfire_get_itlb_data(int entry) +{ +	unsigned long data; + +	__asm__ __volatile__("ldxa	[%1] %2, %0" +			     : "=r" (data) +			     : "r" (entry << 3), "i" (ASI_ITLB_DATA_ACCESS)); + +	/* Clear TTE diag bits. */ +	data &= ~0x0003fe0000000000UL; + +	return data; +} + +static __inline__ unsigned long spitfire_get_itlb_tag(int entry) +{ +	unsigned long tag; + +	__asm__ __volatile__("ldxa	[%1] %2, %0" +			     : "=r" (tag) +			     : "r" (entry << 3), "i" (ASI_ITLB_TAG_READ)); +	return tag; +} + +static __inline__ void spitfire_put_itlb_data(int entry, unsigned long data) +{ +	__asm__ __volatile__("stxa	%0, [%1] %2\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (data), "r" (entry << 3), +			       "i" (ASI_ITLB_DATA_ACCESS)); +} + +/* Spitfire hardware assisted TLB flushes. */ + +/* Context level flushes. */ +static __inline__ void spitfire_flush_dtlb_primary_context(void) +{ +	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (0x40), "i" (ASI_DMMU_DEMAP)); +} + +static __inline__ void spitfire_flush_itlb_primary_context(void) +{ +	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (0x40), "i" (ASI_IMMU_DEMAP)); +} + +static __inline__ void spitfire_flush_dtlb_secondary_context(void) +{ +	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (0x50), "i" (ASI_DMMU_DEMAP)); +} + +static __inline__ void spitfire_flush_itlb_secondary_context(void) +{ +	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (0x50), "i" (ASI_IMMU_DEMAP)); +} + +static __inline__ void spitfire_flush_dtlb_nucleus_context(void) +{ +	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (0x60), "i" (ASI_DMMU_DEMAP)); +} + +static __inline__ void spitfire_flush_itlb_nucleus_context(void) +{ +	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (0x60), "i" (ASI_IMMU_DEMAP)); +} + +/* Page level flushes. */ +static __inline__ void spitfire_flush_dtlb_primary_page(unsigned long page) +{ +	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (page), "i" (ASI_DMMU_DEMAP)); +} + +static __inline__ void spitfire_flush_itlb_primary_page(unsigned long page) +{ +	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (page), "i" (ASI_IMMU_DEMAP)); +} + +static __inline__ void spitfire_flush_dtlb_secondary_page(unsigned long page) +{ +	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (page | 0x10), "i" (ASI_DMMU_DEMAP)); +} + +static __inline__ void spitfire_flush_itlb_secondary_page(unsigned long page) +{ +	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (page | 0x10), "i" (ASI_IMMU_DEMAP)); +} + +static __inline__ void spitfire_flush_dtlb_nucleus_page(unsigned long page) +{ +	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (page | 0x20), "i" (ASI_DMMU_DEMAP)); +} + +static __inline__ void spitfire_flush_itlb_nucleus_page(unsigned long page) +{ +	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (page | 0x20), "i" (ASI_IMMU_DEMAP)); +} + +/* Cheetah has "all non-locked" tlb flushes. */ +static __inline__ void cheetah_flush_dtlb_all(void) +{ +	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (0x80), "i" (ASI_DMMU_DEMAP)); +} + +static __inline__ void cheetah_flush_itlb_all(void) +{ +	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (0x80), "i" (ASI_IMMU_DEMAP)); +} + +/* Cheetah has a 4-tlb layout so direct access is a bit different. + * The first two TLBs are fully assosciative, hold 16 entries, and are + * used only for locked and >8K sized translations.  One exists for + * data accesses and one for instruction accesses. + * + * The third TLB is for data accesses to 8K non-locked translations, is + * 2 way assosciative, and holds 512 entries.  The fourth TLB is for + * instruction accesses to 8K non-locked translations, is 2 way + * assosciative, and holds 128 entries. + * + * Cheetah has some bug where bogus data can be returned from + * ASI_{D,I}TLB_DATA_ACCESS loads, doing the load twice fixes + * the problem for me. -DaveM + */ +static __inline__ unsigned long cheetah_get_ldtlb_data(int entry) +{ +	unsigned long data; + +	__asm__ __volatile__("ldxa	[%1] %2, %%g0\n\t" +			     "ldxa	[%1] %2, %0" +			     : "=r" (data) +			     : "r" ((0 << 16) | (entry << 3)), +			     "i" (ASI_DTLB_DATA_ACCESS)); + +	return data; +} + +static __inline__ unsigned long cheetah_get_litlb_data(int entry) +{ +	unsigned long data; + +	__asm__ __volatile__("ldxa	[%1] %2, %%g0\n\t" +			     "ldxa	[%1] %2, %0" +			     : "=r" (data) +			     : "r" ((0 << 16) | (entry << 3)), +			     "i" (ASI_ITLB_DATA_ACCESS)); + +	return data; +} + +static __inline__ unsigned long cheetah_get_ldtlb_tag(int entry) +{ +	unsigned long tag; + +	__asm__ __volatile__("ldxa	[%1] %2, %0" +			     : "=r" (tag) +			     : "r" ((0 << 16) | (entry << 3)), +			     "i" (ASI_DTLB_TAG_READ)); + +	return tag; +} + +static __inline__ unsigned long cheetah_get_litlb_tag(int entry) +{ +	unsigned long tag; + +	__asm__ __volatile__("ldxa	[%1] %2, %0" +			     : "=r" (tag) +			     : "r" ((0 << 16) | (entry << 3)), +			     "i" (ASI_ITLB_TAG_READ)); + +	return tag; +} + +static __inline__ void cheetah_put_ldtlb_data(int entry, unsigned long data) +{ +	__asm__ __volatile__("stxa	%0, [%1] %2\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (data), +			       "r" ((0 << 16) | (entry << 3)), +			       "i" (ASI_DTLB_DATA_ACCESS)); +} + +static __inline__ void cheetah_put_litlb_data(int entry, unsigned long data) +{ +	__asm__ __volatile__("stxa	%0, [%1] %2\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (data), +			       "r" ((0 << 16) | (entry << 3)), +			       "i" (ASI_ITLB_DATA_ACCESS)); +} + +static __inline__ unsigned long cheetah_get_dtlb_data(int entry, int tlb) +{ +	unsigned long data; + +	__asm__ __volatile__("ldxa	[%1] %2, %%g0\n\t" +			     "ldxa	[%1] %2, %0" +			     : "=r" (data) +			     : "r" ((tlb << 16) | (entry << 3)), "i" (ASI_DTLB_DATA_ACCESS)); + +	return data; +} + +static __inline__ unsigned long cheetah_get_dtlb_tag(int entry, int tlb) +{ +	unsigned long tag; + +	__asm__ __volatile__("ldxa	[%1] %2, %0" +			     : "=r" (tag) +			     : "r" ((tlb << 16) | (entry << 3)), "i" (ASI_DTLB_TAG_READ)); +	return tag; +} + +static __inline__ void cheetah_put_dtlb_data(int entry, unsigned long data, int tlb) +{ +	__asm__ __volatile__("stxa	%0, [%1] %2\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (data), +			       "r" ((tlb << 16) | (entry << 3)), +			       "i" (ASI_DTLB_DATA_ACCESS)); +} + +static __inline__ unsigned long cheetah_get_itlb_data(int entry) +{ +	unsigned long data; + +	__asm__ __volatile__("ldxa	[%1] %2, %%g0\n\t" +			     "ldxa	[%1] %2, %0" +			     : "=r" (data) +			     : "r" ((2 << 16) | (entry << 3)), +                               "i" (ASI_ITLB_DATA_ACCESS)); + +	return data; +} + +static __inline__ unsigned long cheetah_get_itlb_tag(int entry) +{ +	unsigned long tag; + +	__asm__ __volatile__("ldxa	[%1] %2, %0" +			     : "=r" (tag) +			     : "r" ((2 << 16) | (entry << 3)), "i" (ASI_ITLB_TAG_READ)); +	return tag; +} + +static __inline__ void cheetah_put_itlb_data(int entry, unsigned long data) +{ +	__asm__ __volatile__("stxa	%0, [%1] %2\n\t" +			     "membar	#Sync" +			     : /* No outputs */ +			     : "r" (data), "r" ((2 << 16) | (entry << 3)), +			       "i" (ASI_ITLB_DATA_ACCESS)); +} + +#endif /* !(__ASSEMBLY__) */ + +#endif /* !(_SPARC64_SPITFIRE_H) */ diff --git a/roms/openbios/arch/sparc64/switch.S b/roms/openbios/arch/sparc64/switch.S new file mode 100644 index 00000000..d2cc7bed --- /dev/null +++ b/roms/openbios/arch/sparc64/switch.S @@ -0,0 +1,124 @@ +#include "pstate.h" +#include <asm/asi.h> +#define ASI_BP ASI_M_BYPASS +#define REGWIN_SZ   0x40 + +	.globl	__switch_context, __switch_context_nosave, __exit_context, halt + +	.text +	.align	4 +        .register %g2, #scratch +        .register %g3, #scratch +        .register %g6, #scratch +        .register %g7, #scratch + +/* + * Switch execution context + * This saves registers in the stack, then + * switches the stack, and restores everything from the new stack. + * This function takes no argument. New stack pointer is + * taken from global variable __context, and old stack pointer + * is also saved to __context. This way we can just jump to + * this routine to get back to the original context. + */ + +/* XXX: totally bogus for sparc, need to save and restore all windows */ +__switch_context: + +	/* make sure caller's windows are on caller's stack */ +	flushw; + +	/* Save everything in current stack */ + +	setx	__context, %g2, %g1 +        stx     %g3, [%g1 + 24] +        stx     %g4, [%g1 + 32] +        stx     %g5, [%g1 + 40] +        stx     %g6, [%g1 + 48] +        stx     %g7, [%g1 + 56] + +        stx     %o0, [%g1 + 64] +        stx     %o1, [%g1 + 72] +        stx     %o2, [%g1 + 80] +        stx     %o3, [%g1 + 88] +        stx     %o4, [%g1 + 96] +        stx     %o5, [%g1 + 104] +        stx     %o6, [%g1 + 112] +        stx     %o7, [%g1 + 120] + +        stx     %l0, [%g1 + 128] +        stx     %l1, [%g1 + 136] +        stx     %l2, [%g1 + 144] +        stx     %l3, [%g1 + 152] +        stx     %l4, [%g1 + 160] +        stx     %l5, [%g1 + 168] +        stx     %l6, [%g1 + 176] +        stx     %l7, [%g1 + 184] + +        stx     %i0, [%g1 + 192] +        stx     %i1, [%g1 + 200] +        stx     %i2, [%g1 + 208] +        stx     %i3, [%g1 + 216] +        stx     %i4, [%g1 + 224] +        stx     %i5, [%g1 + 232] +        stx     %i6, [%g1 + 240] +        stx     %i7, [%g1 + 248] + +__switch_context_nosave: +	/* Interrupts are not allowed... */ +	/* make sure caller's windows are on caller's stack */ +	flushw +	/* Load all registers +	 */ +	setx	__context, %g2, %g1 +        ldx     [%g1], %g1 +        ldx     [%g1 + 16], %g2 +        ldx     [%g1 + 24], %g3 +        ldx     [%g1 + 32], %g4 +        ldx     [%g1 + 40], %g5 +        ldx     [%g1 + 48], %g6 +        ldx     [%g1 + 56], %g7 + +        ldx     [%g1 + 64], %o0 +        ldx     [%g1 + 72], %o1 +        ldx     [%g1 + 80], %o2 +        ldx     [%g1 + 88], %o3 +        ldx     [%g1 + 96], %o4 +        ldx     [%g1 + 104], %o5 +        ldx     [%g1 + 112], %o6 +        ldx     [%g1 + 120], %o7 + +        ldx     [%g1 + 128], %l0 +        ldx     [%g1 + 136], %l1 +        ldx     [%g1 + 144], %l2 +        ldx     [%g1 + 152], %l3 +        ldx     [%g1 + 160], %l4 +        ldx     [%g1 + 168], %l5 +        ldx     [%g1 + 176], %l6 +        ldx     [%g1 + 184], %l7 + +        ldx     [%g1 + 192], %i0 +        ldx     [%g1 + 200], %i1 +        ldx     [%g1 + 208], %i2 +        ldx     [%g1 + 216], %i3 +        ldx     [%g1 + 224], %i4 +        ldx     [%g1 + 232], %i5 +        ldx     [%g1 + 240], %i6 +        ldx     [%g1 + 248], %i7 + +        ldx     [%g1 + 256], %g1 +       	/* Finally, load new %pc */ +        jmp     %g1 +         clr    %g1 + +__exit_context: +	/* Get back to the original context */ +	call	__switch_context +	 nop + +	/* We get here if the other context attempt to switch to this +	 * dead context. This should not happen. */ + +halt: +	b	halt +	 nop diff --git a/roms/openbios/arch/sparc64/sys_info.c b/roms/openbios/arch/sparc64/sys_info.c new file mode 100644 index 00000000..f70aa027 --- /dev/null +++ b/roms/openbios/arch/sparc64/sys_info.c @@ -0,0 +1,59 @@ +#include "config.h" +#include "kernel/kernel.h" +#include "arch/common/elf_boot.h" +#include "libopenbios/sys_info.h" +#include "context.h" +#include "boot.h" + +#define printf printk +#ifdef CONFIG_DEBUG_BOOT +#define debug printk +#else +#define debug(x...) +#endif + +uint64_t qemu_mem_size; +unsigned long va_shift; + +void collect_multiboot_info(struct sys_info *); + +void collect_sys_info(struct sys_info *info) +{ +    int i; +    unsigned long long total = 0; +    struct memrange *mmap; + +    /* Pick up paramters given by bootloader to us */ +    //info->boot_type = boot_ctx->eax; +    //info->boot_data = boot_ctx->ebx; +    info->boot_arg = boot_ctx->param[0]; +    //debug("boot eax = %#lx\n", info->boot_type); +    //debug("boot ebx = %#lx\n", info->boot_data); +    info->boot_type = ELF_BHDR_MAGIC; +    info->boot_data = virt_to_phys(&elf_image_notes); +    debug("boot arg = %#lx\n", info->boot_arg); + +    collect_elfboot_info(info); +#ifdef CONFIG_LINUXBIOS +    collect_linuxbios_info(info); +#endif +#ifdef CONFIG_IMAGE_ELF_MULTIBOOT +    collect_multiboot_info(info); +#endif + +    if (!info->memrange) { +	info->n_memranges = 1; +	info->memrange = malloc(1 * sizeof(struct memrange)); +	info->memrange[0].base = 0; +	info->memrange[0].size = qemu_mem_size; +    } + +    debug("\n"); +    mmap=info->memrange; +    for (i = 0; i < info->n_memranges; i++) { +	debug("%08lx-", (long)mmap[i].base); +	debug("%08lx\n", (long)mmap[i].base + (long)mmap[i].size); +	total += mmap[i].size; +    } +    debug("RAM %ld MB\n", (long)total >> 20); +} diff --git a/roms/openbios/arch/sparc64/tree.fs b/roms/openbios/arch/sparc64/tree.fs new file mode 100644 index 00000000..e034b593 --- /dev/null +++ b/roms/openbios/arch/sparc64/tree.fs @@ -0,0 +1,49 @@ +include config.fs + +\ ------------------------------------------------------------------------- +\ UPA encode/decode unit +\ ------------------------------------------------------------------------- + +: decode-unit-upa ( str len -- id lun ) +  ascii , left-split +  ( addr-R len-R addr-L len-L ) +  parse-hex +  -rot parse-hex +  swap +; + +: encode-unit-upa ( id lun -- str len) +  swap +  pocket tohexstr +  " ," pocket tmpstrcat >r +  rot pocket tohexstr r> tmpstrcat drop +; + +" /" find-device +  2 encode-int " #address-cells" property +  2 encode-int " #size-cells" property +  " sun4u" encode-string " compatible" property + +  : encode-unit encode-unit-upa ; +  : decode-unit decode-unit-upa ; + +new-device +  " memory" device-name +  " memory" device-type +  external +  : open true ; +  : close ; +  \ see arch/sparc64/lib.c for methods +finish-device + +new-device +  " virtual-memory" device-name +  external +  \ see arch/sparc64/lib.c for methods +finish-device + +" /options" find-device +  " disk" encode-string " boot-from" property + +" /openprom" find-device +  " OBP 3.10.24 1999/01/01 01:01" encode-string " version" property diff --git a/roms/openbios/arch/sparc64/vectors.S b/roms/openbios/arch/sparc64/vectors.S new file mode 100644 index 00000000..927c1cdc --- /dev/null +++ b/roms/openbios/arch/sparc64/vectors.S @@ -0,0 +1,765 @@ +/* + * <vectors.S> + * + * Sparc V9 Trap Table(s) with SpitFire/Cheetah extensions. + * + *   Copyright (C) 1996, 2001 David S. Miller (davem@caip.rutgers.edu) + * + *   This program is free software; you can redistribute it and/or + *   modify it under the terms of the GNU General Public License + *   version 2 as published by the Free Software Foundation. + * + *   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., 51 Franklin Street, Fifth Floor, Boston, + *   MA  02110-1301, USA. + *   This program is free software; you can redistribute it and/or + *   modify it under the terms of the GNU General Public License V2 + *   as published by the Free Software Foundation + */ + +#define __ASSEMBLY__ +#include "pstate.h" +#include <asm/asi.h> +#define ASI_BP ASI_PHYS_BYPASS_EC_E +#define PROM_ADDR 0x1fff0000000 +#define SER_ADDR 0x1fe020003f8 +#define TICK_INT_DIS 0x8000000000000000 +#define TICK_INTERVAL 1*1000*1000 + +        .section ".text.vectors", "ax" +        .align 16384 +/* Sparc64 trap table */ +        .globl trap_table, __divide_error, softint_irq, softint_irq_tl1 +        .register %g2, #scratch +        .register %g3, #scratch +        .register %g6, #scratch +        .register %g7, #scratch +trap_table: +#define SPILL_WINDOW                                    \ +        btst    1, %sp;                                 \ +        be      spill_32bit;                            \ +         nop;                                           \ +        stx     %l0, [%sp + STACK_BIAS + 0x00];         \ +        stx     %l1, [%sp + STACK_BIAS + 0x08];         \ +        stx     %l2, [%sp + STACK_BIAS + 0x10];         \ +        stx     %l3, [%sp + STACK_BIAS + 0x18];         \ +        stx     %l4, [%sp + STACK_BIAS + 0x20];         \ +        stx     %l5, [%sp + STACK_BIAS + 0x28];         \ +        stx     %l6, [%sp + STACK_BIAS + 0x30];         \ +        stx     %l7, [%sp + STACK_BIAS + 0x38];         \ +        stx     %i0, [%sp + STACK_BIAS + 0x40];         \ +        stx     %i1, [%sp + STACK_BIAS + 0x48];         \ +        stx     %i2, [%sp + STACK_BIAS + 0x50];         \ +        stx     %i3, [%sp + STACK_BIAS + 0x58];         \ +        stx     %i4, [%sp + STACK_BIAS + 0x60];         \ +        stx     %i5, [%sp + STACK_BIAS + 0x68];         \ +        stx     %i6, [%sp + STACK_BIAS + 0x70];         \ +        stx     %i7, [%sp + STACK_BIAS + 0x78];         \ +        saved; retry; nop; nop; nop; nop; nop; nop;     \ +        nop; nop; nop; nop; nop; + +#define FILL_WINDOW                                     \ +        btst    1, %sp;                                 \ +        be      fill_32bit;                             \ +         nop;                                           \ +        ldx     [%sp + STACK_BIAS + 0x00], %l0;         \ +        ldx     [%sp + STACK_BIAS + 0x08], %l1;         \ +        ldx     [%sp + STACK_BIAS + 0x10], %l2;         \ +        ldx     [%sp + STACK_BIAS + 0x18], %l3;         \ +        ldx     [%sp + STACK_BIAS + 0x20], %l4;         \ +        ldx     [%sp + STACK_BIAS + 0x28], %l5;         \ +        ldx     [%sp + STACK_BIAS + 0x30], %l6;         \ +        ldx     [%sp + STACK_BIAS + 0x38], %l7;         \ +        ldx     [%sp + STACK_BIAS + 0x40], %i0;         \ +        ldx     [%sp + STACK_BIAS + 0x48], %i1;         \ +        ldx     [%sp + STACK_BIAS + 0x50], %i2;         \ +        ldx     [%sp + STACK_BIAS + 0x58], %i3;         \ +        ldx     [%sp + STACK_BIAS + 0x60], %i4;         \ +        ldx     [%sp + STACK_BIAS + 0x68], %i5;         \ +        ldx     [%sp + STACK_BIAS + 0x70], %i6;         \ +        ldx     [%sp + STACK_BIAS + 0x78], %i7;         \ +        restored; retry; nop; nop; nop; nop; nop; nop;  \ +        nop; nop; nop; nop; nop; + +#define CLEAN_WINDOW                                                    \ +        rdpr    %cleanwin, %l0;         add     %l0, 1, %l0;            \ +        wrpr    %l0, 0x0, %cleanwin;                                    \ +        clr     %o0;    clr     %o1;    clr     %o2;    clr     %o3;    \ +        clr     %o4;    clr     %o5;    clr     %o6;    clr     %o7;    \ +        clr     %l0;    clr     %l1;    clr     %l2;    clr     %l3;    \ +        clr     %l4;    clr     %l5;    clr     %l6;    clr     %l7;    \ +        retry;                                                          \ +        nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop; + +#define TRAP_IRQ(routine, level)                        \ +                ba routine;  mov level, %g1; nop; nop; nop; nop; nop; nop; +#define BTRAP(lvl)                                      \ +                 ba bug; mov lvl, %g1; nop; nop; nop; nop; nop; nop; +#define BTRAPTL1(lvl) BTRAP(lvl) +#define BTRAPS(x) BTRAP(x) BTRAP(x+1) BTRAP(x+2) BTRAP(x+3) BTRAP(x+4) BTRAP(x+5) BTRAP(x+6) BTRAP(x+7) +#define BTRAPS4(x) BTRAP(x) BTRAP(x+1) BTRAP(x+2) BTRAP(x+3) +#define TRAP_HANDLER(routine) ba routine; nop; nop; nop; nop; nop; nop; nop; + +#define STACK_BIAS		2047 +	.globl	sparc64_ttable_tl0, sparc64_ttable_tl1 +sparc64_ttable_tl0: +                ba entry; nop; nop; nop; nop; nop; nop; nop;! XXX remove +                ba entry; nop; nop; nop; nop; nop; nop; nop;! Power-on reset +                ba entry; nop; nop; nop; nop; nop; nop; nop;! Watchdog reset +                ba entry; nop; nop; nop; nop; nop; nop; nop;! External reset +                ba entry; nop; nop; nop; nop; nop; nop; nop;! Software reset +                ba entry; nop; nop; nop; nop; nop; nop; nop;! RED state +                BTRAP(0x06) BTRAP(0x07) BTRAPS(0x08) +		BTRAPS(0x10) BTRAPS(0x18) +		BTRAP(0x20) BTRAP(0x21) BTRAP(0x22) BTRAP(0x23) +		CLEAN_WINDOW ! 24-27 +		BTRAPS(0x28) +		BTRAPS(0x30) BTRAPS(0x38) +		BTRAP(0x40) BTRAP(0x41) BTRAP(0x42) BTRAP(0x43) +tl0_irq4:	TRAP_IRQ(handler_irq, 4) +tl0_irq5:	TRAP_IRQ(handler_irq, 5)  TRAP_IRQ(handler_irq, 6) +tl0_irq7:	TRAP_IRQ(handler_irq, 7)  TRAP_IRQ(handler_irq, 8) +tl0_irq9:	TRAP_IRQ(handler_irq, 9)  TRAP_IRQ(handler_irq, 10) +tl0_irq11:	TRAP_IRQ(handler_irq, 11) TRAP_IRQ(handler_irq, 12) +tl0_irq13:	TRAP_IRQ(handler_irq, 13) +tl0_irq14:	TRAP_IRQ(softint_irq, 14) +tl0_irq15:	TRAP_IRQ(handler_irq, 15) +		BTRAPS(0x50) BTRAPS(0x58) +		BTRAPS4(0x60) +		TRAP_HANDLER(reload_IMMU_tlb)    ! 0x64 : instruction_access_MMU_miss +		TRAP_HANDLER(reload_IMMU_tlb)    ! 0x65 : instruction_access_MMU_miss +		TRAP_HANDLER(reload_IMMU_tlb)    ! 0x66 : instruction_access_MMU_miss +		TRAP_HANDLER(reload_IMMU_tlb)    ! 0x67 : instruction_access_MMU_miss +		TRAP_HANDLER(reload_DMMU_tlb)    ! 0x68 : data_access_MMU_miss +		TRAP_HANDLER(reload_DMMU_tlb)    ! 0x69 : data_access_MMU_miss +		TRAP_HANDLER(reload_DMMU_tlb)    ! 0x6A : data_access_MMU_miss +		TRAP_HANDLER(reload_DMMU_tlb)    ! 0x6B : data_access_MMU_miss +		BTRAPS4(0x6C)    ! data_access_protection +		BTRAPS(0x70) BTRAPS(0x78) +tl0_s0n:        SPILL_WINDOW +tl0_s1n:        SPILL_WINDOW +tl0_s2n:        SPILL_WINDOW +tl0_s3n:        SPILL_WINDOW +tl0_s4n:        SPILL_WINDOW +tl0_s5n:        SPILL_WINDOW +tl0_s6n:        SPILL_WINDOW +tl0_s7n:        SPILL_WINDOW +tl0_s0o:        SPILL_WINDOW +tl0_s1o:        SPILL_WINDOW +tl0_s2o:        SPILL_WINDOW +tl0_s3o:        SPILL_WINDOW +tl0_s4o:        SPILL_WINDOW +tl0_s5o:        SPILL_WINDOW +tl0_s6o:        SPILL_WINDOW +tl0_s7o:        SPILL_WINDOW +tl0_f0n:        FILL_WINDOW +tl0_f1n:        FILL_WINDOW +tl0_f2n:        FILL_WINDOW +tl0_f3n:        FILL_WINDOW +tl0_f4n:        FILL_WINDOW +tl0_f5n:        FILL_WINDOW +tl0_f6n:        FILL_WINDOW +tl0_f7n:        FILL_WINDOW +tl0_f0o:        FILL_WINDOW +tl0_f1o:        FILL_WINDOW +tl0_f2o:        FILL_WINDOW +tl0_f3o:        FILL_WINDOW +tl0_f4o:        FILL_WINDOW +tl0_f5o:        FILL_WINDOW +tl0_f6o:        FILL_WINDOW +tl0_f7o:        FILL_WINDOW +tl0_resv100:	BTRAPS(0x100) BTRAPS(0x108) +tl0_resv110:	BTRAPS(0x110) BTRAPS(0x118) +tl0_resv120:	BTRAPS(0x120) BTRAPS(0x128) +tl0_resv130:	BTRAPS(0x130) BTRAPS(0x138) +tl0_resv140:	BTRAPS(0x140) BTRAPS(0x148) +tl0_resv150:	BTRAPS(0x150) BTRAPS(0x158) +tl0_resv160:	BTRAPS(0x160) BTRAPS(0x168) +tl0_resv170:	BTRAPS(0x170) BTRAPS(0x178) +tl0_resv180:	BTRAPS(0x180) BTRAPS(0x188) +tl0_resv190:	BTRAPS(0x190) BTRAPS(0x198) +tl0_resv1a0:	BTRAPS(0x1a0) BTRAPS(0x1a8) +tl0_resv1b0:	BTRAPS(0x1b0) BTRAPS(0x1b8) +tl0_resv1c0:	BTRAPS(0x1c0) BTRAPS(0x1c8) +tl0_resv1d0:	BTRAPS(0x1d0) BTRAPS(0x1d8) +tl0_resv1e0:	BTRAPS(0x1e0) BTRAPS(0x1e8) +tl0_resv1f0:	BTRAPS(0x1f0) BTRAPS(0x1f8) + +#undef BTRAPS +#define BTRAPS(x) BTRAPTL1(x) BTRAPTL1(x+1) BTRAPTL1(x+2) BTRAPTL1(x+3) BTRAPTL1(x+4) BTRAPTL1(x+5) BTRAPTL1(x+6) BTRAPTL1(x+7) + +#define SKIP_IRQ(routine, level) \ +                retry;  nop; nop; nop; nop; nop; nop; nop; + +sparc64_ttable_tl1: +		BTRAPS(0x00) BTRAPS(0x08) +		BTRAPS(0x10) BTRAPS(0x18) +		BTRAPTL1(0x20) BTRAPTL1(0x21) BTRAPTL1(0x22) BTRAPTL1(0x23) +		CLEAN_WINDOW ! 24-27 +		BTRAPS(0x28) +		BTRAPS(0x30) BTRAPS(0x38) +		BTRAPTL1(0x40) BTRAPTL1(0x41) BTRAPTL1(0x42) BTRAPTL1(0x43) +tl1_irq4:	TRAP_IRQ(handler_irq, 4) +tl1_irq5:	TRAP_IRQ(handler_irq, 5)  TRAP_IRQ(handler_irq, 6) +tl1_irq7:	TRAP_IRQ(handler_irq, 7)  TRAP_IRQ(handler_irq, 8) +tl1_irq9:	TRAP_IRQ(handler_irq, 9)  TRAP_IRQ(handler_irq, 10) +tl1_irq11:	TRAP_IRQ(handler_irq, 11) TRAP_IRQ(handler_irq, 12) +tl1_irq13:	TRAP_IRQ(handler_irq, 13) +tl1_irq14:	SKIP_IRQ(softint_irq, 14) +tl1_irq15:	TRAP_IRQ(handler_irq, 15) +		BTRAPS(0x50) BTRAPS(0x58) +		BTRAPS4(0x60) +		TRAP_HANDLER(reload_IMMU_tlb)    ! 0x64 : instruction_access_MMU_miss +		TRAP_HANDLER(reload_IMMU_tlb)    ! 0x65 : instruction_access_MMU_miss +		TRAP_HANDLER(reload_IMMU_tlb)    ! 0x66 : instruction_access_MMU_miss +		TRAP_HANDLER(reload_IMMU_tlb)    ! 0x67 : instruction_access_MMU_miss +		TRAP_HANDLER(reload_DMMU_tlb)    ! 0x68 : data_access_MMU_miss +		TRAP_HANDLER(reload_DMMU_tlb)    ! 0x69 : data_access_MMU_miss +		TRAP_HANDLER(reload_DMMU_tlb)    ! 0x6A : data_access_MMU_miss +		TRAP_HANDLER(reload_DMMU_tlb)    ! 0x6B : data_access_MMU_miss +		BTRAPS4(0x6C)    ! data_access_protection +		BTRAPS(0x70) BTRAPS(0x78) +tl1_s0n:        SPILL_WINDOW +tl1_s1n:        SPILL_WINDOW +tl1_s2n:        SPILL_WINDOW +tl1_s3n:        SPILL_WINDOW +tl1_s4n:        SPILL_WINDOW +tl1_s5n:        SPILL_WINDOW +tl1_s6n:        SPILL_WINDOW +tl1_s7n:        SPILL_WINDOW +tl1_s0o:        SPILL_WINDOW +tl1_s1o:        SPILL_WINDOW +tl1_s2o:        SPILL_WINDOW +tl1_s3o:        SPILL_WINDOW +tl1_s4o:        SPILL_WINDOW +tl1_s5o:        SPILL_WINDOW +tl1_s6o:        SPILL_WINDOW +tl1_s7o:        SPILL_WINDOW +tl1_f0n:        FILL_WINDOW +tl1_f1n:        FILL_WINDOW +tl1_f2n:        FILL_WINDOW +tl1_f3n:        FILL_WINDOW +tl1_f4n:        FILL_WINDOW +tl1_f5n:        FILL_WINDOW +tl1_f6n:        FILL_WINDOW +tl1_f7n:        FILL_WINDOW +tl1_f0o:        FILL_WINDOW +tl1_f1o:        FILL_WINDOW +tl1_f2o:        FILL_WINDOW +tl1_f3o:        FILL_WINDOW +tl1_f4o:        FILL_WINDOW +tl1_f5o:        FILL_WINDOW +tl1_f6o:        FILL_WINDOW +tl1_f7o:        FILL_WINDOW +tl1_resv100:	BTRAPS(0x100) BTRAPS(0x108) +tl1_resv110:	BTRAPS(0x110) BTRAPS(0x118) +tl1_resv120:	BTRAPS(0x120) BTRAPS(0x128) +tl1_resv130:	BTRAPS(0x130) BTRAPS(0x138) +tl1_resv140:	BTRAPS(0x140) BTRAPS(0x148) +tl1_resv150:	BTRAPS(0x150) BTRAPS(0x158) +tl1_resv160:	BTRAPS(0x160) BTRAPS(0x168) +tl1_resv170:	BTRAPS(0x170) BTRAPS(0x178) +tl1_resv180:	BTRAPS(0x180) BTRAPS(0x188) +tl1_resv190:	BTRAPS(0x190) BTRAPS(0x198) +tl1_resv1a0:	BTRAPS(0x1a0) BTRAPS(0x1a8) +tl1_resv1b0:	BTRAPS(0x1b0) BTRAPS(0x1b8) +tl1_resv1c0:	BTRAPS(0x1c0) BTRAPS(0x1c8) +tl1_resv1d0:	BTRAPS(0x1d0) BTRAPS(0x1d8) +tl1_resv1e0:	BTRAPS(0x1e0) BTRAPS(0x1e8) +tl1_resv1f0:	BTRAPS(0x1f0) BTRAPS(0x1f8) + +	.section ".data" +	.align 8 +	.globl tlb_handler_stack_top, tlb_handler_stack_pointer, obp_ticks_pointer + +	! Stack for the tlb MMU trap handlers +tlb_handler_stack_bottom: +	.skip 8192 +tlb_handler_stack_top: +	.skip 8 + +	! MMU trap handler stack pointer +tlb_handler_stack_pointer: +	.xword tlb_handler_stack_top + +	! Pointer to current tick value +obp_ticks_pointer: +	.xword 0 + +        .section ".text", "ax" + +spill_32bit: +        srl     %sp, 0, %sp +        stw     %l0, [%sp + 0x00] +        stw     %l1, [%sp + 0x04] +        stw     %l2, [%sp + 0x08] +        stw     %l3, [%sp + 0x0c] +        stw     %l4, [%sp + 0x10] +        stw     %l5, [%sp + 0x14] +        stw     %l6, [%sp + 0x18] +        stw     %l7, [%sp + 0x1c] +        stw     %i0, [%sp + 0x20] +        stw     %i1, [%sp + 0x24] +        stw     %i2, [%sp + 0x28] +        stw     %i3, [%sp + 0x2c] +        stw     %i4, [%sp + 0x30] +        stw     %i5, [%sp + 0x34] +        stw     %i6, [%sp + 0x38] +        stw     %i7, [%sp + 0x3c] +        saved +        retry + +fill_32bit: +        srl     %sp, 0, %sp +        lduw    [%sp + 0x00], %l0 +        lduw    [%sp + 0x04], %l1 +        lduw    [%sp + 0x08], %l2 +        lduw    [%sp + 0x0c], %l3 +        lduw    [%sp + 0x10], %l4 +        lduw    [%sp + 0x14], %l5 +        lduw    [%sp + 0x18], %l6 +        lduw    [%sp + 0x1c], %l7 +        lduw    [%sp + 0x20], %i0 +        lduw    [%sp + 0x24], %i1 +        lduw    [%sp + 0x28], %i2 +        lduw    [%sp + 0x2c], %i3 +        lduw    [%sp + 0x30], %i4 +        lduw    [%sp + 0x34], %i5 +        lduw    [%sp + 0x38], %i6 +        lduw    [%sp + 0x3c], %i7 +        restored +        retry + +/* + * SAVE_CPU_STATE and RESTORE_CPU_STATE are macros used to enable a context switch + * to C to occur within the MMU I/D TLB miss handlers. + * + * Because these handlers are called on a TLB miss, we cannot use flushw to store + * processor window state on the stack, as the memory areas used by each window's + * stack pointer may not be in the TLB, causing recursive TLB miss traps. + * + * For this reason, we save window state by manually rotating the window registers + * and saving their contents (along with other vital registers) into a special + * tlb_handler_stack defined above which is guaranteed to be locked in the TLB, and + * so won't cause issues with trap recursion. + * + * Once this process is complete, we remain in a TL=0, CWP=0 state (with IE=1 to allow + * window fill/spill traps if required), switch to our safe tlb_handler_stack and  + * invoke the miss handler. + */ + +#define SAVE_CPU_STATE(type) \ +	/* Set up our exception stack pointer in %g1 */ \ +	setx	tlb_handler_stack_pointer, %g7, %g6; \ +	ldx	[%g6], %g1; \ +	add	%g1, -0x510, %g1; \ +	\ +	/* First save the various state registers */ \ +	rdpr	%cwp, %g7; \ +	stx	%g7, [%g1]; \ +	rdpr	%cansave, %g7; \ +	stx	%g7, [%g1 + 0x8]; \ +	rdpr	%canrestore, %g7; \ +	stx	%g7, [%g1 + 0x10]; \ +	rdpr	%otherwin, %g7; \ +	stx	%g7, [%g1 + 0x18]; \ +	rdpr	%wstate, %g7; \ +	stx	%g7, [%g1 + 0x20]; \ +	rdpr	%cleanwin, %g7; \ +	stx	%g7, [%g1 + 0x28]; \ +	rdpr	%pstate, %g7; \ +	stx	%g7, [%g1 + 0x30]; \ +	\ +	rd	%y, %g7; \ +	stx	%g7, [%g1 + 0x38]; \ +	rd	%fprs, %g7; \ +	stx	%g7, [%g1 + 0x40]; \ +	\ +	rdpr	%tl, %g7; \ +	stx	%g7, [%g1 + 0x48]; \ +	\ +	/* Trap state */ \ +	add	%g1, 0x50, %g5; \ +	mov	4, %g6; \ +	\ +save_trap_state_##type: \ +	deccc	%g6; \ +	wrpr	%g6, %tl; \ +	rdpr	%tpc, %g7; \ +	stx	%g7, [%g5]; \ +	rdpr	%tnpc, %g7; \ +	stx	%g7, [%g5 + 0x8]; \ +	rdpr	%tstate, %g7; \ +	stx	%g7, [%g5 + 0x10]; \ +	rdpr	%tt, %g7; \ +	stx	%g7, [%g5 + 0x18]; \ +	bne	save_trap_state_##type; \ +	 add	%g5, 0x20, %g5; \ +	\ +	/* For 4 trap levels with 4 registers, memory required is  +	4*8*4 = 0x80 bytes */ \ +	\ +	/* Save the o registers */ \ +	stx	%o0, [%g1 + 0xd0]; \ +	stx	%o1, [%g1 + 0xd8]; \ +	stx	%o2, [%g1 + 0xe0]; \ +	stx	%o3, [%g1 + 0xe8]; \ +	stx	%o4, [%g1 + 0xf0]; \ +	stx	%o5, [%g1 + 0xf8]; \ +	stx	%o6, [%g1 + 0x100]; \ +	stx	%o7, [%g1 + 0x108]; \ +	\ +	/* Now iterate through all of the windows saving all l and i registers */ \ +	add	%g1, 0x110, %g5; \ +	\ +	/* Get the number of windows in %g6 */ \ +	rdpr	%ver, %g6; \ +	and	%g6, 0xf, %g6; \ +	inc	%g6; \ +	\ +save_cpu_window_##type: \ +	deccc	%g6; \ +	wrpr	%g6, %cwp; \ +	stx	%l0, [%g5]; \ +	stx	%l1, [%g5 + 0x8]; \ +	stx	%l2, [%g5 + 0x10]; \ +	stx	%l3, [%g5 + 0x18]; \ +	stx	%l4, [%g5 + 0x20]; \ +	stx	%l5, [%g5 + 0x28]; \ +	stx	%l6, [%g5 + 0x30]; \ +	stx	%l7, [%g5 + 0x38]; \ +	stx	%i0, [%g5 + 0x40]; \ +	stx	%i1, [%g5 + 0x48]; \ +	stx	%i2, [%g5 + 0x50]; \ +	stx	%i3, [%g5 + 0x58]; \ +	stx	%i4, [%g5 + 0x60]; \ +	stx	%i5, [%g5 + 0x68]; \ +	stx	%i6, [%g5 + 0x70]; \ +	stx	%i7, [%g5 + 0x78]; \ +	bne	save_cpu_window_##type; \ +	 add	%g5, 0x80, %g5; \ +	\ +	/* For 8 windows with 16 registers to save in the window, memory required +	is 16*8*8 = 0x400 bytes */ \ +	\ +	/* Now we should be in window 0 so update the other window registers */ \ +	rdpr	%ver, %g6; \ +	and	%g6, 0xf, %g6; \ +	dec	%g6; \ +	wrpr	%g6, %cansave; \ +	\ +	wrpr	%g0, %cleanwin; \ +	wrpr	%g0, %canrestore; \ +	wrpr	%g0, %otherwin; \ +	\ +	/* Update our exception stack pointer */ \ +	setx	tlb_handler_stack_pointer, %g7, %g6; \ +	stx	%g1, [%g6]; + + +#define RESTORE_CPU_STATE(type) \ +	/* Set up our exception stack pointer in %g1 */ \ +	setx	tlb_handler_stack_pointer, %g7, %g6; \ +	ldx	[%g6], %g1; \ +	\ +	/* Get the number of windows in %g6 */ \ +	rdpr	%ver, %g6; \ +	and	%g6, 0xf, %g6; \ +	inc	%g6; \ +	\ +	/* Now iterate through all of the windows restoring all l and i registers */ \ +	add	%g1, 0x110, %g5; \ +	\ +restore_cpu_window_##type: \ +	deccc	%g6; \ +	wrpr	%g6, %cwp; \ +	ldx	[%g5], %l0; \ +	ldx	[%g5 + 0x8], %l1; \ +	ldx	[%g5 + 0x10], %l2; \ +	ldx	[%g5 + 0x18], %l3; \ +	ldx	[%g5 + 0x20], %l4; \ +	ldx	[%g5 + 0x28], %l5; \ +	ldx	[%g5 + 0x30], %l6; \ +	ldx	[%g5 + 0x38], %l7; \ +	ldx	[%g5 + 0x40], %i0; \ +	ldx	[%g5 + 0x48], %i1; \ +	ldx	[%g5 + 0x50], %i2; \ +	ldx	[%g5 + 0x58], %i3; \ +	ldx	[%g5 + 0x60], %i4; \ +	ldx	[%g5 + 0x68], %i5; \ +	ldx	[%g5 + 0x70], %i6; \ +	ldx	[%g5 + 0x78], %i7; \ +	bne	restore_cpu_window_##type; \ +	 add	%g5, 0x80, %g5; \ +	\ +	/* Restore the window registers to their original value */ \ +	ldx	[%g1], %g7; \ +	wrpr	%g7, %cwp; \ +	ldx	[%g1 + 0x8], %g7; \ +	wrpr	%g7, %cansave; \ +	ldx	[%g1 + 0x10], %g7; \ +	wrpr	%g7, %canrestore; \ +	ldx	[%g1 + 0x18], %g7; \ +	wrpr	%g7, %otherwin; \ +	ldx	[%g1 + 0x20], %g7; \ +	wrpr	%g7, %wstate; \ +	ldx	[%g1 + 0x28], %g7; \ +	wrpr	%g7, %cleanwin; \ +	ldx	[%g1 + 0x30], %g7; \ +	wrpr	%g7, %pstate; \ +	\ +	/* Restore the o registers */ \ +	ldx	[%g1 + 0xd0], %o0; \ +	ldx	[%g1 + 0xd8], %o1; \ +	ldx	[%g1 + 0xe0], %o2; \ +	ldx	[%g1 + 0xe8], %o3; \ +	ldx	[%g1 + 0xf0], %o4; \ +	ldx	[%g1 + 0xf8], %o5; \ +	ldx	[%g1 + 0x100], %o6; \ +	ldx	[%g1 + 0x108], %o7; \ +	\ +	/* Restore the trap state */ \ +	add	%g1, 0x50, %g5; \ +	mov	4, %g6; \ +	\ +restore_trap_state_##type: \ +	deccc	%g6; \ +	wrpr	%g6, %tl; \ +	ldx	[%g5], %g7; \ +	wrpr	%g7, %tpc; \ +	ldx	[%g5 + 0x8], %g7; \ +	wrpr	%g7, %tnpc; \ +	ldx	[%g5 + 0x10], %g7; \ +	wrpr	%g7, %tstate; \ +	ldx	[%g5 + 0x18], %g7; \ +	wrpr	%g7, %tt; \ +	bne	restore_trap_state_##type; \ +	 add	%g5, 0x20, %g5; \ +	\ +	ldx	[%g1 + 0x38], %g7; \ +	wr	%g7, 0, %y; \ +	ldx	[%g1 + 0x40], %g7; \ +	wr	%g7, 0, %fprs; \ +	ldx	[%g1 + 0x48], %g7; \ +	wrpr	%g7, %tl; \ +	\ +	/* Restore exception stack pointer to previous value */ \ +	setx	tlb_handler_stack_pointer, %g7, %g6; \ +	add	%g1, 0x510, %g1; \ +	stx	%g1, [%g6]; + + +        .globl reload_DMMU_tlb, reload_IMMU_tlb, bug + +reload_DMMU_tlb: + +	SAVE_CPU_STATE(dtlb) + +	/* Switch to TLB locked stack space (note we add an additional 192 bytes required for +	   gcc to save its arguments when building with -O0) */ +	add 	%g1, -STACK_BIAS - 192, %sp + +	/* Enable interrupts for window spill/fill traps */ +	rdpr	%pstate, %g7 +	or	%g7, PSTATE_IE, %g7 +	wrpr	%g7, %pstate	 + +	call	dtlb_miss_handler +	 nop + +	/* Disable interrupts */ +	rdpr	%pstate, %g7 +	andn	%g7, PSTATE_IE, %g7 +	wrpr	%g7, %pstate + +	RESTORE_CPU_STATE(dtlb) + +        retry + +reload_IMMU_tlb: + +	SAVE_CPU_STATE(itlb) + +	/* Switch to TLB locked stack space (note we add an additional 192 bytes required for +	   gcc to save its arguments when building with -O0) */ +	add 	%g1, -STACK_BIAS - 192, %sp + +	/* Enable interrupts for window spill/fill traps */ +	rdpr	%pstate, %g7 +	or	%g7, PSTATE_IE, %g7 +	wrpr	%g7, %pstate	 + +	call	itlb_miss_handler +	 nop + +	/* Disable interrupts */ +	rdpr	%pstate, %g7 +	andn	%g7, PSTATE_IE, %g7 +	wrpr	%g7, %pstate + +	RESTORE_CPU_STATE(itlb) + +        retry + +softint_irq_tl1: +softint_irq: +        mov     1, %g2 +        /* clear tick interrupt */ +        wr      %g2, 0x0, %clear_softint +        sll     %g2, %g1, %g2 +        sra     %g2, 0, %g2 +        /* clear softint interrupt */ +        wr      %g2, 0x0, %clear_softint + +        setx    TICK_INT_DIS, %g2, %g1 +        rd      %tick, %g2 +        and     %g1, %g2, %g1 +        brnz,pn %g1, tick_compare_disabled +         nop + +        /* update tick value if pointer set */ +        setx    obp_ticks_pointer, %g3, %g1 +        ldx     [%g1], %g3 +        brz     %g3, tick_rearm +         nop + +        ldx     [%g3], %g1 +        add     %g1, 10, %g1    ! 100Hz = 10ms +        stx     %g1, [%g3] +          +tick_rearm:          +        set     TICK_INTERVAL, %g1 +        add     %g1, %g2, %g1 +        wr      %g1, 0, %tick_cmpr +tick_compare_disabled: +        retry + +handler_irq: +__divide_error: +bug: +        /* Dump the exception and its context */ +        ! Set up CPU state +        ! Don't change the global register set or we lose %g1 (exception level) +        rdpr    %pstate, %g2 +        or      %g2, PSTATE_PRIV, %g2 +        wrpr    %g2, %pstate +        wr      %g0, 0, %fprs + +        ! Jump to ROM ... +        setx    _start, %g2, %g3 +        setx    highmem, %g2, %g4 +        sub     %g4, %g3, %g4 +        setx    PROM_ADDR, %g2, %g3 +        add     %g4, %g3, %g3 +        jmp     %g3 +        ! ... while disabling I/D MMUs and caches +         stxa    %g0, [%g0] ASI_LSU_CONTROL + +highmem: +        ! Extract NWINDOWS from %ver +        rdpr    %ver, %g2 +        and     %g2, 0xf, %g2 +        wrpr    %g2, 0, %cleanwin +        wrpr    %g2, 0, %cansave +        wrpr    %g0, 0, %canrestore +        wrpr    %g0, 0, %otherwin +        wrpr    %g0, 0, %wstate + +        b       dump_exception +         nop + +outstr: +        /* void outstr (unsigned long port, const unsigned char *str); +         * Writes a string on an IO port. +         */ +1:      ldub    [%o1], %o3 +        cmp     %o3, 0 +        be      2f +         nop +        stba    %o3, [%o0] ASI_BP +        b       1b +         inc    %o1 +2:      retl +         nop + +outdigit: +        /* void outdigit (unsigned long port, uint8_t digit); +         * Dumps a single digit on serial port. +         */ +        add     %o1, '0', %o1 +        retl +         stba   %o1, [%o0] ASI_BP + +outhex: +        /* void outhex (unsigned long port, uint64_t value); +         * Dumps a 64 bits hex number on serial port +         */ +        mov     %o1, %o2 +        set     60, %o3 +        srlx    %o2, %o3, %o1 +1:      and     %o1, 0xf, %o1 +        cmp     %o1, 9 +        bgt     2f +         nop +        b       3f +         add    %o1, '0', %o1 +2:      add     %o1, 'a' - 10, %o1 +3:      stba    %o1, [%o0] ASI_BP +        subcc   %o3, 4, %o3 +        bge     1b +         srlx   %o2, %o3, %o1 +        retl +         nop + +        /* void dump_exception (); +         * +         * Dump a message when catching an exception +         */ +dump_exception: +        setx    SER_ADDR, %o3, %o0 +        set     _start, %g3 +        set     (_BUG_message_0), %o1 +        sub     %o1, %g3, %g4 +        setx    PROM_ADDR, %g2, %g3 +        add     %g4, %g3, %g3 +        call    outstr +         mov    %g3, %o1 + +        call    outhex +         mov    %g1, %o1 + +        call    outstr +         add    %g3, (_BUG_message_1 -  _BUG_message_0), %o1 + +        call    outhex +         rdpr   %tpc, %o1 + +        call    outstr +         add    %g3, (_BUG_message_2 -  _BUG_message_0), %o1 + +        call    outhex +         rdpr   %tnpc, %o1 + +        call    outstr +         add    %g3, (_BUG_message_3 -  _BUG_message_0), %o1 + +_forever: +        /* Loop forever */ +        b       _forever                                  ; +         nop + +        .section .rodata +_BUG_message_0: +        .string "Unhandled Exception 0x" +_BUG_message_1: +        .string "\nPC = 0x" +_BUG_message_2: +        .string " NPC = 0x" +_BUG_message_3: +        .string "\nStopping execution\n"  | 
