Signed-off-by: Mark Cave-Ayland mark.cave-ayland@ilande.co.uk --- openbios-devel/arch/ppc/build.xml | 1 + openbios-devel/arch/ppc/qemu/context.c | 123 ++++++++++++++++++++++++ openbios-devel/arch/ppc/qemu/context.h | 34 +++++++ openbios-devel/arch/ppc/qemu/ldscript | 6 +- openbios-devel/arch/ppc/qemu/start.S | 2 +- openbios-devel/arch/ppc/qemu/switch.S | 165 +++++++++++++++++++++++++++++++- openbios-devel/include/arch/ppc/io.h | 2 +- 7 files changed, 327 insertions(+), 6 deletions(-) create mode 100644 openbios-devel/arch/ppc/qemu/context.c create mode 100644 openbios-devel/arch/ppc/qemu/context.h
diff --git a/openbios-devel/arch/ppc/build.xml b/openbios-devel/arch/ppc/build.xml index b40c81c..e606313 100644 --- a/openbios-devel/arch/ppc/build.xml +++ b/openbios-devel/arch/ppc/build.xml @@ -183,6 +183,7 @@ $(call quiet-command,$(STRIP) $@.nostrip -o $@," STRIP $(TARGET_DIR)$@")</rule> <object source="qemu/start.S"/> <object source="qemu/switch.S"/> + <object source="qemu/context.c"/> <object source="timebase.S"/> <external-object source="libqemu.a"/> <external-object source="libbootstrap.a"/> diff --git a/openbios-devel/arch/ppc/qemu/context.c b/openbios-devel/arch/ppc/qemu/context.c new file mode 100644 index 0000000..e56da71 --- /dev/null +++ b/openbios-devel/arch/ppc/qemu/context.c @@ -0,0 +1,123 @@ +/* + * context switching + * 2003-10 by SONE Takeshi + */ + +#include "config.h" +#include "kernel/kernel.h" +#include "context.h" +#include "libopenbios/sys_info.h" + +#define MAIN_STACK_SIZE 16384 +#define IMAGE_STACK_SIZE 4096*2 + +#define debug printk + +#ifdef CONFIG_PPC_64BITSUPPORT + #ifdef __powerpc64__ + #define ULONG_SIZE 8 + #define STACKFRAME_MINSIZE 48 + #define STKOFF STACKFRAME_MINSIZE + #define SAVE_SPACE 320 + #else + #define ULONG_SIZE 4 + #define STACKFRAME_MINSIZE 16 + #define STKOFF 8 + #define SAVE_SPACE 144 + #endif +#endif + +static void start_main(void); /* forward decl. */ +void __exit_context(void); /* assembly routine */ + +unsigned int start_elf(unsigned long entry_point, unsigned long param); +void entry(void); + +/* + * 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 = { + .sp = (unsigned long) &_estack - SAVE_SPACE, + .pc = (unsigned long) start_main, + .return_addr = (unsigned long) __exit_context, +}; + +/* This is used by assembly routine to load/store the context which + * it is to switch/switched. */ +struct context *__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 */ + entry(); + + /* Returning from here should jump to __exit_context */ + __context = boot_ctx; +} + +/* Setup a new context using the given stack. + */ +struct context * +init_context(uint8_t *stack, uint32_t stack_size, int num_params) +{ + struct context *ctx; + + ctx = (struct context *) + (stack + stack_size - (sizeof(*ctx) + num_params*sizeof(unsigned long))); + memset(ctx, 0, sizeof(*ctx)); + + /* Fill in reasonable default for flat memory model */ + ctx->sp = virt_to_phys(SP_LOC(ctx)); + ctx->return_addr = virt_to_phys(__exit_context); + + return ctx; +} + +/* Switch to another context. */ +struct context *switch_to(struct context *ctx) +{ + volatile struct context *save; + struct context *ret; + unsigned int lr; + + debug("switching to new context:\n"); + save = __context; + __context = ctx; + + asm __volatile__ ("mflr %0\n\t" : "=r" (lr) : : ); + asm __volatile__ ("bl __switch_context\n"); + asm __volatile__ ("mtlr %0\n\t" : : "r" (lr) : ); + + ret = __context; + __context = (struct context *)save; + return ret; +} + +/* Start ELF Boot image */ +unsigned int start_elf(unsigned long entry_point, unsigned long param) +{ + struct context *ctx; + + ctx = init_context(image_stack, sizeof image_stack, 1); + ctx->pc = entry_point; + ctx->param[0] = param; + + ctx = switch_to(ctx); + return ctx->regs[REG_R3]; +} diff --git a/openbios-devel/arch/ppc/qemu/context.h b/openbios-devel/arch/ppc/qemu/context.h new file mode 100644 index 0000000..8135bb4 --- /dev/null +++ b/openbios-devel/arch/ppc/qemu/context.h @@ -0,0 +1,34 @@ +#ifndef PPC_CONTEXT_H +#define PPC_CONTEXT_H + +struct context { +#define SP_LOC(ctx) (&(ctx)->sp) + unsigned long _sp; + unsigned long return_addr; + unsigned long sp; + unsigned long pc; + /* General registers */ + unsigned long regs[34]; +#define REG_R3 3 +#define REG_R5 8 +#define REG_R6 9 +#define REG_R7 10 + /* Flags */ + /* Optional stack contents */ + unsigned long param[0]; +}; + +/* Create a new context in the given stack */ +struct context * +init_context(uint8_t *stack, uint32_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 /* PPC_CONTEXT_H */ diff --git a/openbios-devel/arch/ppc/qemu/ldscript b/openbios-devel/arch/ppc/qemu/ldscript index 8027b39..11ebf4b 100644 --- a/openbios-devel/arch/ppc/qemu/ldscript +++ b/openbios-devel/arch/ppc/qemu/ldscript @@ -51,7 +51,11 @@ SECTIONS *(.bss) *(.bss.*) *(COMMON) - _ebss = .; + + _stack = .; + . += CSTACK_SIZE; + . = ALIGN(16); + _estack = .; }
. = HRESET_ADDR; diff --git a/openbios-devel/arch/ppc/qemu/start.S b/openbios-devel/arch/ppc/qemu/start.S index 483c498..a62d46e 100644 --- a/openbios-devel/arch/ppc/qemu/start.S +++ b/openbios-devel/arch/ppc/qemu/start.S @@ -482,7 +482,7 @@ real_entry: #endif
bl BRANCH_LABEL(setup_mmu) - bl BRANCH_LABEL(entry) + bl BRANCH_LABEL(__switch_context_nosave) 1: nop b 1b
diff --git a/openbios-devel/arch/ppc/qemu/switch.S b/openbios-devel/arch/ppc/qemu/switch.S index eabd6d0..c3d9d70 100644 --- a/openbios-devel/arch/ppc/qemu/switch.S +++ b/openbios-devel/arch/ppc/qemu/switch.S @@ -2,10 +2,19 @@ #include "asm/asmdefs.h" #include "asm/processor.h"
+ #ifdef CONFIG_PPC_64BITSUPPORT - #define STACKFRAME_MINSIZE 48 -#else /* !CONFIG_PPC_64BITSUPPORT */ - #define STACKFRAME_MINSIZE 16 + #ifdef __powerpc64__ + #define ULONG_SIZE 8 + #define STACKFRAME_MINSIZE 48 + #define STKOFF STACKFRAME_MINSIZE + #define SAVE_SPACE 320 + #else + #define ULONG_SIZE 4 + #define STACKFRAME_MINSIZE 16 + #define STKOFF 8 + #define SAVE_SPACE 144 + #endif #endif
/* According to IEEE 1275, PPC bindings: @@ -50,3 +59,153 @@ _GLOBAL(call_elf): // XXX: should restore r12-r31 etc.. // we should not really come here though blrl + +/* + * 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. + */ + +_GLOBAL(__switch_context): + /* save internal stack pointer */ +#ifdef CONFIG_PPC64 + PPC_STL r1, -(SAVE_SPACE + 16) + STKOFF(r1) +#else + PPC_STL r1, -SAVE_SPACE + STKOFF(r1) +#endif + +#ifdef CONFIG_PPC64 + PPC_STLU r1, -(SAVE_SPACE + 16)(r1) +#else + PPC_STLU r1, -SAVE_SPACE(r1) /* fits within alignment */ +#endif + + /* r4, r5 */ + PPC_STL r4, (STKOFF + 9 * ULONG_SIZE)(r1) + PPC_STL r5, (STKOFF + 10 * ULONG_SIZE)(r1) + + /* link register */ + mflr r4 + PPC_STL r4, PPC_LR_STKOFF(r1) + PPC_STL r4, (STKOFF + ULONG_SIZE)(r1) + + PPC_STL r3, (STKOFF + 5 * ULONG_SIZE)(r1) + PPC_STL r2, (STKOFF + 4 * ULONG_SIZE)(r1) + PPC_STL r0, (STKOFF + 3 * ULONG_SIZE)(r1) + + /* ctr, cr and xer */ + mfctr r4 + PPC_STL r4, (STKOFF + 6 * ULONG_SIZE)(r1) + mfcr r4 + PPC_STL r4, (STKOFF + 7 * ULONG_SIZE)(r1) + mfxer r4 + PPC_STL r4, (STKOFF + 8 * ULONG_SIZE)(r1) + + /* r6-r31 */ + PPC_STL r6, (STKOFF + 11 * ULONG_SIZE)(r1) + PPC_STL r7, (STKOFF + 12 * ULONG_SIZE)(r1) + PPC_STL r8, (STKOFF + 13 * ULONG_SIZE)(r1) + PPC_STL r9, (STKOFF + 14 * ULONG_SIZE)(r1) + PPC_STL r10, (STKOFF + 15 * ULONG_SIZE)(r1) + PPC_STL r11, (STKOFF + 16 * ULONG_SIZE)(r1) + PPC_STL r12, (STKOFF + 17 * ULONG_SIZE)(r1) + PPC_STL r13, (STKOFF + 18 * ULONG_SIZE)(r1) + PPC_STL r14, (STKOFF + 19 * ULONG_SIZE)(r1) + PPC_STL r15, (STKOFF + 20 * ULONG_SIZE)(r1) + PPC_STL r16, (STKOFF + 21 * ULONG_SIZE)(r1) + PPC_STL r17, (STKOFF + 22 * ULONG_SIZE)(r1) + PPC_STL r18, (STKOFF + 23 * ULONG_SIZE)(r1) + PPC_STL r19, (STKOFF + 24 * ULONG_SIZE)(r1) + PPC_STL r20, (STKOFF + 25 * ULONG_SIZE)(r1) + PPC_STL r21, (STKOFF + 26 * ULONG_SIZE)(r1) + PPC_STL r22, (STKOFF + 27 * ULONG_SIZE)(r1) + PPC_STL r23, (STKOFF + 28 * ULONG_SIZE)(r1) + PPC_STL r24, (STKOFF + 29 * ULONG_SIZE)(r1) + PPC_STL r25, (STKOFF + 30 * ULONG_SIZE)(r1) + PPC_STL r26, (STKOFF + 31 * ULONG_SIZE)(r1) + PPC_STL r27, (STKOFF + 32 * ULONG_SIZE)(r1) + PPC_STL r28, (STKOFF + 33 * ULONG_SIZE)(r1) + PPC_STL r29, (STKOFF + 34 * ULONG_SIZE)(r1) + PPC_STL r30, (STKOFF + 35 * ULONG_SIZE)(r1) + PPC_STL r31, (STKOFF + 36 * ULONG_SIZE)(r1) + + /* swap context */ + LOAD_REG_IMMEDIATE(r4, __context) + PPC_LL r5, 0(r4) + PPC_STL r1, 0(r4) + mr r4, r5 + + b __set_context + +_GLOBAL(__switch_context_nosave): + LOAD_REG_IMMEDIATE(r4, __context) + PPC_LL r4, 0(r4) + +__set_context: + /* link register */ + PPC_LL r5, (STKOFF + ULONG_SIZE)(r4) + mtlr r5 + + PPC_LL r3, (STKOFF + 5 * ULONG_SIZE)(r4) + PPC_LL r2, (STKOFF + 4 * ULONG_SIZE)(r4) + PPC_LL r0, (STKOFF + 3 * ULONG_SIZE)(r4) + + /* ctr, cr and xer */ + PPC_LL r5, (STKOFF + 6 * ULONG_SIZE)(r4) + mtctr r5 + PPC_LL r5, (STKOFF + 7 * ULONG_SIZE)(r4) + mtcr r5 + PPC_LL r5, (STKOFF + 8 * ULONG_SIZE)(r4) + mtxer r5 + + /* r5-r31 */ + PPC_LL r5, (STKOFF + 10 * ULONG_SIZE)(r4) + PPC_LL r6, (STKOFF + 11 * ULONG_SIZE)(r4) + PPC_LL r7, (STKOFF + 12 * ULONG_SIZE)(r4) + PPC_LL r8, (STKOFF + 13 * ULONG_SIZE)(r4) + PPC_LL r9, (STKOFF + 14 * ULONG_SIZE)(r4) + PPC_LL r10, (STKOFF + 15 * ULONG_SIZE)(r4) + PPC_LL r11, (STKOFF + 16 * ULONG_SIZE)(r4) + PPC_LL r12, (STKOFF + 17 * ULONG_SIZE)(r4) + PPC_LL r13, (STKOFF + 18 * ULONG_SIZE)(r4) + PPC_LL r14, (STKOFF + 19 * ULONG_SIZE)(r4) + PPC_LL r15, (STKOFF + 20 * ULONG_SIZE)(r4) + PPC_LL r16, (STKOFF + 21 * ULONG_SIZE)(r4) + PPC_LL r17, (STKOFF + 22 * ULONG_SIZE)(r4) + PPC_LL r18, (STKOFF + 23 * ULONG_SIZE)(r4) + PPC_LL r19, (STKOFF + 24 * ULONG_SIZE)(r4) + PPC_LL r20, (STKOFF + 25 * ULONG_SIZE)(r4) + PPC_LL r21, (STKOFF + 26 * ULONG_SIZE)(r4) + PPC_LL r22, (STKOFF + 27 * ULONG_SIZE)(r4) + PPC_LL r23, (STKOFF + 28 * ULONG_SIZE)(r4) + PPC_LL r24, (STKOFF + 29 * ULONG_SIZE)(r4) + PPC_LL r25, (STKOFF + 30 * ULONG_SIZE)(r4) + PPC_LL r26, (STKOFF + 31 * ULONG_SIZE)(r4) + PPC_LL r27, (STKOFF + 32 * ULONG_SIZE)(r4) + PPC_LL r28, (STKOFF + 33 * ULONG_SIZE)(r4) + PPC_LL r29, (STKOFF + 34 * ULONG_SIZE)(r4) + PPC_LL r30, (STKOFF + 35 * ULONG_SIZE)(r4) + PPC_LL r31, (STKOFF + 36 * ULONG_SIZE)(r4) + + /* r4, r1 */ + PPC_LL r1, STKOFF(r4) + PPC_LL r4, (STKOFF + 8 * ULONG_SIZE)(r4) + + LOAD_REG_IMMEDIATE(r0, MSR_FP | MSR_ME | MSR_DR | MSR_IR) + MTMSRD(r0) + + blrl + +#ifdef CONFIG_PPC64 + /* Restore SF bit */ + LOAD_REG_IMMEDIATE(r0, MSR_SF | MSR_FP | MSR_ME | MSR_DR | MSR_IR) + MTMSRD(r0) +#endif + +_GLOBAL(__exit_context): + /* Get back to the original context */ + b __switch_context diff --git a/openbios-devel/include/arch/ppc/io.h b/openbios-devel/include/arch/ppc/io.h index 3449c5b..39c60d7 100644 --- a/openbios-devel/include/arch/ppc/io.h +++ b/openbios-devel/include/arch/ppc/io.h @@ -6,7 +6,7 @@ #define NO_QEMU_PROTOS #include "arch/common/fw_cfg.h"
-extern char _start, _end; +extern char _start, _end, _estack; extern unsigned long virt_offset;
#define phys_to_virt(phys) ((void *) ((unsigned long) (phys) - virt_offset))