diff --git a/hal/sparcv8leon3/Makefile b/hal/sparcv8leon3/Makefile index 66a3dc12e..9ad707cdd 100644 --- a/hal/sparcv8leon3/Makefile +++ b/hal/sparcv8leon3/Makefile @@ -4,8 +4,14 @@ # Copyright 2022 Phoenix Systems # -include hal/sparcv8leon3/$(TARGET_SUBFAMILY)/Makefile +include hal/$(TARGET_SUFF)/gaisler/Makefile -CFLAGS += -Ihal/sparcv8leon3 -Ihal/sparcv8leon3/$(TARGET_SUBFAMILY) +CFLAGS := -Ihal/$(TARGET_SUFF) $(CFLAGS) -OBJS += $(addprefix $(PREFIX_O)hal/sparcv8leon3/, cpu.o exceptions.o hal.o interrupts.o pmap.o spinlock.o string.o _init.o _interrupts.o _traps.o) +OBJS += $(addprefix $(PREFIX_O)hal/$(TARGET_SUFF)/, cpu.o hal.o spinlock.o string.o _traps.o) + +ifeq ($(findstring -DNOMMU,$(CPPFLAGS)),-DNOMMU) + OBJS += $(addprefix $(PREFIX_O)hal/$(TARGET_SUFF)/, exceptions-nommu.o pmap-nommu.o _interrupts-nommu.o) +else + OBJS += $(addprefix $(PREFIX_O)hal/$(TARGET_SUFF)/, exceptions.o pmap.o srmmu.o _interrupts.o) +endif diff --git a/hal/sparcv8leon3/_interrupts-nommu.S b/hal/sparcv8leon3/_interrupts-nommu.S new file mode 100644 index 000000000..1a1b1fa97 --- /dev/null +++ b/hal/sparcv8leon3/_interrupts-nommu.S @@ -0,0 +1,286 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * Interrupts handlers (NOMMU) for sparcv8leon3 + * + * Copyright 2022, 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#define __ASSEMBLY__ + +#include +#include + +.extern hal_cpuKernelStack + +.section ".text" +.align 4 + +.global _interrupts_dispatch +.type _interrupts_dispatch, #function + +/* Interrupt handler + * on entry: + * %l0: psr + * %l1: pc + * %l2: npc + * %l3: irq number + */ + _interrupts_dispatch: + /* %g2, g3 used during manual window overflow */ + mov %g2, %l4 + mov %g3, %l5 + + mov %wim, %g2 + /* check if we've just overflowed + * window overflow if wim == (1 << CWP) + * wim >> l0[4:0] - shift wim by CWP (lowest 5 bits from psr) + */ + srl %g2, %l0, %g3 + cmp %g3, 1 + + bne irq_wovfl_done + sll %g2, (NWINDOWS - 1), %g3 + + /* calculate new wim: current %wim in %g2, %g3 is scratch */ + srl %g2, 1, %g2 + + save + wr %g2, %g3, %wim + nop + nop + nop + std %l0, [%sp + 0x00] + std %l2, [%sp + 0x08] + std %l4, [%sp + 0x10] + std %l6, [%sp + 0x18] + std %i0, [%sp + 0x20] + std %i2, [%sp + 0x28] + std %i4, [%sp + 0x30] + std %fp, [%sp + 0x38] + restore + +irq_wovfl_done: + /* check if we need to swap to kernel stack + * i.e. when PSR_PS is not set + */ + andcc %l0, PSR_PS, %g0 + bnz irq_no_kstack_switch + + set hal_cpuKernelStack, %l6 + /* Extract CPU ID and add offset */ + rd %asr17, %l7 + srl %l7, 28, %l7 + sll %l7, 2, %l7 + ld [%l6 + %l7], %l7 + ba irq_kstack_set + sub %l7, 0x50, %sp + +irq_no_kstack_switch: + /* we came from kernel, make space for context */ + sub %fp, 0x50, %sp + +irq_kstack_set: + /* Save context on kernel stack - we have enough space for 1 window. + * Here only a part of thread context is saved, + * all windows are saved only if we're switching context. + * + * Registers saved: + * %sp, %y, %psr, PC, nPC, %g1, %g2 (in %l4), %g3 (in %l5), %g4-%g7, %i0-%i7 + */ + + st %sp, [%sp + 0x00] /* sp */ + rd %y, %g2 + st %g2, [%sp + 0x04] /* y */ + + std %l0, [%sp + 0x08] /* psr, PC */ + st %l2, [%sp + 0x10] /* nPC */ + st %g1, [%sp + 0x14] /* g1 */ + std %l4, [%sp + 0x18] /* g2, g3 */ + std %g4, [%sp + 0x20] /* g4, g5 */ + std %g6, [%sp + 0x28] /* g6, g7 */ + + /* input registers here are the outputs of the interrupted window */ + + std %i0, [%sp + 0x30] /* i0, i1 */ + std %i2, [%sp + 0x38] /* i2, i3 */ + std %i4, [%sp + 0x40] /* i4, i5 */ + std %fp, [%sp + 0x48] /* fp (task's sp), i7 */ + + mov %sp, %l7 + sub %sp, 0x60, %sp + + mov %l7, %o1 /* (cpu_context_t *) */ + + /* enable traps, disable interrupts */ + or %l0, (PSR_PIL | PSR_ET), %l0 + wr %l0, %psr + nop + nop + nop + + /* void interrupts_dispatch(unsigned int irq, cpu_context_t *) */ + call interrupts_dispatch + mov %l3, %o0 /* irq */ + + /* disable traps */ + pwr 0, %psr + nop + nop + nop + + /* l7 still points to bottom of context */ + mov %l7, %sp + + /* check if we're going to switch context (sp != *(sp)) */ + ld [%sp], %g2 + cmp %sp, %g2 + be irq_no_switch + nop + + /* We're switching, save used register windows on stack + * and load only the window we'll be returning to. + * The rest will be restored on window underflows. + */ + + rd %psr, %g3 + and %g3, PSR_CWP, %g3 + + /* Current state of registers: + * %g2 - %sp of new task + * %g3 - CWP + * freely usable: %g4, %g5 + */ + + /* set bit in register %g3, which corresponds to CWP + * %g3 = 1 << %g3 (CWP) + */ + mov 1, %g4 + sll %g4, %g3, %g3 + + /* save context on stack */ + sethi %hi(_interrupts_save_context), %g5 + jmpl %g5 + %lo(_interrupts_save_context), %g1 /* clobbers %g1, %g3 */ + rd %wim, %g4 + + /* At this point, we've saved all registers that the previous + * task used, and we're ready to switch to the new task. + * + * %g2 points to the new task's context. + */ + + mov %g0, %wim /* we don't need it now */ + ld [%g2 + 0x08], %g1 + nop + andn %g1, PSR_ET, %g1 /* leave traps disabled */ + + /* Set %psr of the new task. + * This will cause window to be switched + * to the window in interrupt handler. + */ + + wr %g1, %psr + /* no delay needed, we're using global registers */ + + sethi %hi(_interrupts_restore_context), %g5 + jmpl %g5 + %lo(_interrupts_restore_context), %g1 + nop + + /* check CWP overflow (same as before) */ + and %g2, PSR_CWP, %g2 + add %g2, 1, %g2 + cmp %g2, NWINDOWS + bne irq_cwp_done + mov 1, %g3 + + mov 0, %g2 + +irq_cwp_done: + /* set %wim to 1 << %g2 (CWP + 2) */ + sll %g3, %g2, %g2 + mov %g2, %wim + + /* restore %g1, %g2, %g3 */ + ld [%sp + 0x14], %g1 + + andn %l0, PSR_ET, %l0 + + ba irq_return + ldd [%sp + 0x18], %g2 + + +irq_no_switch: + /* restore current window */ + ld [%sp + 0x04], %g1 /* y */ + ldd [%sp + 0x08], %l0 /* psr, PC */ + wr %g1, %y + ld [%sp + 0x10], %l2 /* nPC */ + ld [%sp + 0x14], %g1 + ldd [%sp + 0x18], %g2 + ldd [%sp + 0x20], %g4 + ldd [%sp + 0x28], %g6 + + ldd [%sp + 0x30], %i0 + ldd [%sp + 0x38], %i2 + ldd [%sp + 0x40], %i4 + ldd [%sp + 0x48], %fp + + /* Check if restore would cause window underflow. + * After restore: CWP = CWP + 1 (mod NWINDOWS) + * i.e. wim >> (CWP + 1) == 1 + */ + + and %l0, PSR_CWP, %l5 + add %l5, 1, %l5 + cmp %l5, NWINDOWS + bne irq_cwp_done2 + rd %wim, %l4 + + /* we'd end up in non-existent window #NWINDOWS, it means it's #0 */ + mov 0, %l5 + +irq_cwp_done2: + /* l4 = wim, l5 = CWP + 1 (mod NWINDOWS) + * check if wim >> (CWP + 1) == 1 (window underflow) + */ + srl %l4, %l5, %l6 + cmp %l6, 1 + bne irq_return + /* uses the delay slot + * calculate new wim + * %l4 = current %wim + * wim = (wim << 1) ^ (wim >> (NWINDOWS - 1)) + */ + sll %l4, 1, %l5 + srl %l4, (NWINDOWS - 1), %l4 + wr %l4, %l5, %wim + nop + nop + nop + restore + ldd [%sp + 0x00], %l0 + ldd [%sp + 0x08], %l2 + ldd [%sp + 0x10], %l4 + ldd [%sp + 0x18], %l6 + ldd [%sp + 0x20], %i0 + ldd [%sp + 0x28], %i2 + ldd [%sp + 0x30], %i4 + ldd [%sp + 0x38], %fp + save + +irq_return: + wr %l0, %psr + nop + nop + nop + + jmp %l1 + rett %l2 +.size _interrupts_dispatch, . - _interrupts_dispatch diff --git a/hal/sparcv8leon3/_interrupts.S b/hal/sparcv8leon3/_interrupts.S index 546a10061..592e092d5 100644 --- a/hal/sparcv8leon3/_interrupts.S +++ b/hal/sparcv8leon3/_interrupts.S @@ -18,108 +18,13 @@ #include #include -.extern _end +#include "gaisler/grlib-tn-0018.h" + .extern hal_cpuKernelStack .section ".text" .align 4 -.global _interrupts_save_context -.type _interrupts_save_context, #function -_interrupts_save_context: - /* This function saves all valid windows on stack. - * Before calling, following registers must be set: - * %g1 - call address - * %g3 - bit set for CWP - * %g4 - wim - * - * Clobbers %g1, %g3 - */ - -save_context_loop: - sll %g3, 1, %l0 - srl %g3, (NWINDOWS - 1), %g3 - or %g3, %l0, %g3 - /* check if restore won't underflow */ - andcc %g3, %g4, %g0 - bnz save_context_done - nop - - /* if not, advance to next window */ - restore - - /* save window on current %sp */ - std %l0, [%sp + 0x00] - std %l2, [%sp + 0x08] - std %l4, [%sp + 0x10] - std %l6, [%sp + 0x18] - std %i0, [%sp + 0x20] - std %i2, [%sp + 0x28] - std %i4, [%sp + 0x30] - std %fp, [%sp + 0x38] - - ba save_context_loop - nop - -save_context_done: - jmpl %g1 + 8, %g0 - nop -.size _interrupts_save_context, . - _interrupts_save_context - - -.global _interrupts_restore_context -.type _interrupts_restore_context, #function -_interrupts_restore_context: - /* This function restores current window (except %g1, %g2, %g3) - * and first task's window. - * Before calling, following registers must be set: - * %g1 - call address - * %g2 - pointer to context - * - * On return: task's %psr in %g2 - */ - - /* switch to new task's stack */ - mov %g2, %sp - - /* restore current window */ - ld [%sp + 0x04], %g2 /* y */ - wr %g2, %y - ldd [%sp + 0x08], %l0 /* psr, PC */ - ld [%sp + 0x10], %l2 /* nPC */ - - /* %g1, %g2, %g3 must be restored later */ - - ldd [%sp + 0x20], %g4 - ldd [%sp + 0x28], %g6 - - ldd [%sp + 0x30], %i0 - ldd [%sp + 0x38], %i2 - ldd [%sp + 0x40], %i4 - ldd [%sp + 0x48], %fp - - /* switch window to the task's window and restore context */ - restore - - ldd [%sp + 0x00], %l0 - ldd [%sp + 0x08], %l2 - ldd [%sp + 0x10], %l4 - ldd [%sp + 0x18], %l6 - ldd [%sp + 0x20], %i0 - ldd [%sp + 0x28], %i2 - ldd [%sp + 0x30], %i4 - ldd [%sp + 0x38], %fp - - rd %psr, %g2 - - /* go back to handler's window */ - save - - jmpl %g1 + 8, %g0 - nop -.size _interrupts_restore_context, . - _interrupts_restore_context - - .global _interrupts_dispatch .type _interrupts_dispatch, #function @@ -171,8 +76,12 @@ irq_wovfl_done: andcc %l0, PSR_PS, %g0 bnz irq_no_kstack_switch - sethi %hi(hal_cpuKernelStack), %l6 - ld [%l6 + %lo(hal_cpuKernelStack)], %l7 + set hal_cpuKernelStack, %l6 + /* Extract CPU ID and add offset */ + rd %asr17, %l7 + srl %l7, 28, %l7 + sll %l7, 2, %l7 + ld [%l6 + %l7], %l7 ba irq_kstack_set sub %l7, 0x50, %sp @@ -181,10 +90,7 @@ irq_no_kstack_switch: sub %fp, 0x50, %sp irq_kstack_set: - /* Save context on kernel stack - we have enough space for 1 window. - * Here only a part of thread context is saved, - * all windows are saved only if we're switching context. - * + /* Save context on kernel stack. * Registers saved: * %sp, %y, %psr, PC, nPC, %g1, %g2 (in %l4), %g3 (in %l5), %g4-%g7, %i0-%i7 */ @@ -212,9 +118,30 @@ irq_kstack_set: mov %l7, %o1 /* (cpu_context_t *) */ - /* enable traps, disable interrupts */ - or %l0, (PSR_PIL | PSR_ET), %l0 - wr %l0, %psr + mov %l0, %g2 + mov 1, %g4 + sll %g4, %g2, %g3 /* %g3 = 1 << %g2[4:0] (CWP) */ + + sethi %hi(_interrupts_save_context), %g5 + jmpl %g5 + %lo(_interrupts_save_context), %g1 /* clobbers %g1, %g3, %l2 */ + rd %wim, %g4 + + /* enable traps, disable interrupts, restore CWP */ + or %g2, (PSR_PIL | PSR_ET), %g2 + wr %g2, %psr + + and %g2, PSR_CWP, %g2 + add %g2, 1, %g2 + cmp %g2, NWINDOWS + bne irq_cwp_done0 + mov 1, %g3 + + mov 0, %g2 + +irq_cwp_done0: + /* set %wim to 1 << %g2 (CWP + 1) */ + sll %g3, %g2, %g2 + wr %g2, %wim nop nop nop @@ -224,10 +151,14 @@ irq_kstack_set: mov %l3, %o0 /* irq */ /* disable traps */ +#ifndef __CPU_GR712RC pwr 0, %psr nop nop nop +#else + ta 0xb +#endif /* l7 still points to bottom of context */ mov %l7, %sp @@ -238,47 +169,14 @@ irq_kstack_set: be irq_no_switch nop - /* We're switching, save used register windows on stack - * and load only the window we'll be returning to. - * The rest will be restored on window underflows. - */ - - rd %psr, %g3 - and %g3, PSR_CWP, %g3 - - /* Current state of registers: - * %g2 - %sp of new task - * %g3 - CWP - * freely usable: %g4, %g5 - */ - - /* set bit in register %g3, which corresponds to CWP - * %g3 = 1 << %g3 (CWP) - */ - mov 1, %g4 - sll %g4, %g3, %g3 - - /* save context on stack */ - sethi %hi(_interrupts_save_context), %g5 - jmpl %g5 + %lo(_interrupts_save_context), %g1 /* clobbers %g1, %g3 */ - rd %wim, %g4 - - /* At this point, we've saved all registers that the previous - * task used, and we're ready to switch to the new task. - * - * %g2 points to the new task's context. - */ + /* %g2 points to the new task's context */ mov %g0, %wim /* we don't need it now */ ld [%g2 + 0x08], %g1 nop andn %g1, PSR_ET, %g1 /* leave traps disabled */ - /* Set %psr of the new task. - * This will cause window to be switched - * to the window in interrupt handler. - */ - + /* Set %psr of the new task */ wr %g1, %psr /* no delay needed, we're using global registers */ @@ -291,12 +189,12 @@ irq_kstack_set: add %g2, 1, %g2 cmp %g2, NWINDOWS bne irq_cwp_done - nop + mov 1, %g3 + mov 0, %g2 irq_cwp_done: /* set %wim to 1 << %g2 (CWP + 2) */ - mov 1, %g3 sll %g3, %g2, %g2 mov %g2, %wim @@ -336,7 +234,7 @@ irq_no_switch: bne irq_cwp_done2 rd %wim, %l4 - /* we'd end up in non-existent window #31, it means it's #0 */ + /* we'd end up in non-existent window #NWINDOWS, it means it's #0 */ mov 0, %l5 irq_cwp_done2: @@ -369,300 +267,15 @@ irq_cwp_done2: save irq_return: - wr %l0, %psr - nop - nop - nop + TN_0018_WAIT_ICACHE(%l3, %l4) - jmp %l1 - rett %l2 -.size _interrupts_dispatch, . - _interrupts_dispatch - - -/* Disable/Enable interrupts - * on entry: - * %psr in %l0, PC in %l1, nPC in %l2 - */ - -.global _interrupts_disable -.type _interrupts_disable, #function -_interrupts_disable: - or %l0, PSR_PIL, %l0 wr %l0, %psr nop nop nop - jmp %l2 - rett %l2 + 4 -.size _interrupts_disable, . - _interrupts_disable - - -.global _interrupts_enable -.type _interrupts_enable, #function -_interrupts_enable: - andn %l0, PSR_PIL, %l0 - wr %l0, %psr - nop - nop - nop - jmp %l2 - rett %l2 + 4 -.size _interrupts_enable, . - _interrupts_enable - - -/* int hal_cpuReschedule(struct _spinlock_t *spinlock, spinlock_ctx_t *scp) */ -.global hal_cpuReschedule -.type hal_cpuReschedule, #function -hal_cpuReschedule: - save %sp, -0x50, %sp - - pwr 0, %psr - nop - nop - nop - - /* this is voluntary reschedule, - * no need to store caller-saves registers - * (outputs of previous window and g1-g5) - */ - - rd %psr, %g2 - or %g2, PSR_PS, %g2 - - mov %sp, %l1 /* save pointer to context */ - - st %sp, [%sp + 0x00] - st %g0, [%sp + 0x04] - st %g2, [%sp + 0x08] /* psr */ - add %i7, 0x8, %g3 - st %g3, [%sp + 0x0c] /* pc */ - add %g3, 0x4, %g3 - st %g3, [%sp + 0x10] /* npc */ - std %g6, [%sp + 0x28] - st %g0, [%sp + 0x30] /* default return value */ - st %fp, [%sp + 0x48] - - sub %sp, 0x60, %sp - - /* check if spinlock is not NULL */ - cmp %i0, %g0 - beq r_spinlock_done - nop - -r_spinlock: - /* clear spinlock */ - stbar - stub %g0, [%i0 + 0x0c] - -r_spinlock_done: - /* disable interrupts, enable traps */ - or %g2, (PSR_PIL | PSR_ET), %g2 - wr %g2, %psr - nop - nop - nop - - clr %o0 - mov %l1, %o1 /* cpu_context_t * */ - call threads_schedule - clr %o2 - - /* disable traps */ - pwr 0, %psr - nop - nop - nop - - ld [%l1], %g2 /* ctx pointer */ - - rd %psr, %g3 - and %g3, PSR_CWP, %g3 - - /* set bit in register %g3, which corresponds to CWP - * %g3 = 1 << %g3 (CWP) - */ - mov 1, %g4 - sll %g4, %g3, %g3 - - sethi %hi(_interrupts_save_context), %g5 - jmpl %g5 + %lo(_interrupts_save_context), %g1 /* clobbers %g1, %g3 */ - rd %wim, %g4 - - /* Set %psr of the new task. - * This will cause window to be switched - * so that the new task's window is CWP + 1. - */ - - mov %g0, %wim /* we don't need it now */ - ld [%g2 + 0x08], %g1 - nop - andn %g1, PSR_ET, %g1 /* disable traps */ - wr %g1, %psr - - sethi %hi(_interrupts_restore_context), %g5 - jmpl %g5 + %lo(_interrupts_restore_context), %g1 - nop - - /* check CWP overflow (same as before) */ - and %g2, PSR_CWP, %g2 - add %g2, 1, %g2 - cmp %g2, NWINDOWS - bne r_cwp_done - nop - mov 0, %g2 - -r_cwp_done: - /* set %wim to 1 << %g2 (CWP + 1) */ - mov 1, %g3 - sll %g3, %g2, %g2 - wr %g2, %wim - nop - - /* restore %g1, %g2, %g3 */ - ld [%sp + 0x14], %g1 - ldd [%sp + 0x18], %g2 - - andn %l0, PSR_ET, %l0 - wr %l0, %psr - nop - nop - nop - - /* Set both PC and nPC */ - jmp %l1 - rett %l2 -.size hal_cpuReschedule, . - hal_cpuReschedule - - -/* void hal_jmp(void *f, void *kstack, void *stack, int argc) */ -.global hal_jmp -.type hal_jmp, #function -hal_jmp: - save %sp, -0x60, %sp - pwr 0, %psr - nop - nop - nop - /* calculate new wim */ - rd %psr, %l0 - and %l0, PSR_CWP, %l1 - add %l1, 1, %l1 - cmp %l1, NWINDOWS - bne 1f - nop - - mov 0, %l1 -1: - mov 1, %l2 - sll %l2, %l1, %l1 - wr %l1, %wim - nop - nop - nop - - cmp %i2, %g0 /* stack != NULL */ - bne 3f - nop - mov %i1, %fp /* fp = kstack */ - subcc %i3, 1, %i3 - bneg 2f - nop - subcc %i3, 1, %i3 - bneg 2f - ld [%fp], %o0 - subcc %i3, 1, %i3 - bneg 2f - ld [%fp + 4], %o1 - subcc %i3, 1, %i3 - bneg 2f - ld [%fp + 8], %o2 - ld [%fp + 12], %o3 -2: - andn %fp, 0x7, %fp - sub %fp, 0x60, %sp - wr %l0, PSR_ET, %psr - nop - nop - nop - call %i0 - nop -3: - subcc %i3, 1, %i3 - bneg 4f - mov %i2, %o0 - subcc %i3, 1, %i3 - bneg 4f - ld [%i2], %o0 - subcc %i3, 1, %i3 - bneg 4f - ld [%i2 + 4], %o1 - subcc %i3, 1, %i3 - bneg 4f - ld [%i2 + 8], %o2 - ld [%i2 + 12], %o3 -4: - andn %i2, 0x7, %fp - sub %fp, 0x60, %sp - andn %l0, (PSR_S | PSR_PIL), %l0 - wr %l0, PSR_ET, %psr - nop - nop - nop - call %i0 - nop -.size hal_jmp, . - hal_jmp - - -/* void hal_longjmp(cpu_context_t *ctx); */ -.global hal_longjmp -.type hal_longjmp, #function -hal_longjmp: - /* disable interrupts */ - ta 0x09 - mov %o0, %g2 - /* flush windows onto stack */ - ta 0x03 - - mov %g0, %wim - - ld [%g2 + 0x08], %g1 /* psr */ - andn %g1, PSR_ET, %g1 /* disable traps */ - or %g1, (PSR_PIL | PSR_S), %g3 /* disable interrupts, set supervisor mode */ - - /* writing psr might cause window switch */ - wr %g3, %psr - - sethi %hi(_interrupts_restore_context), %g5 - jmpl %g5 + %lo(_interrupts_restore_context), %g1 - nop - - /* check CWP overflow */ - and %g2, PSR_CWP, %g2 - add %g2, 1, %g2 - cmp %g2, NWINDOWS - bne 5f - nop - mov 0, %g2 -5: - /* set %wim to 1 << %g2 (CWP + 1) */ - mov 1, %g3 - sll %g3, %g2, %g2 - wr %g2, %wim - nop - - /* restore %g1-%g3 */ - ld [%sp + 0x14], %g1 - ldd [%sp + 0x18], %g2 - - /* restore psr */ - or %l0, PSR_S, %l0 - andn %l0, PSR_ET, %l0 - wr %l0, %psr - nop - nop - nop + TN_0018_FIX(%l3, %l4) jmp %l1 rett %l2 -.size hal_longjmp, . - hal_longjmp +.size _interrupts_dispatch, . - _interrupts_dispatch diff --git a/hal/sparcv8leon3/_traps.S b/hal/sparcv8leon3/_traps.S index 48d83d602..1c18f730d 100644 --- a/hal/sparcv8leon3/_traps.S +++ b/hal/sparcv8leon3/_traps.S @@ -18,6 +18,8 @@ #include #include +#include "gaisler/grlib-tn-0018.h" + #define TBR_TT_MSK 0xFF0 #define TBR_TT_SHIFT 4 @@ -42,16 +44,17 @@ mov (irqn), %l3; #define RESET \ - mov %g0, %g4; \ - sethi %hi(_init), %g4; \ - jmp %g4 + %lo(_init); \ + /* PC-relative, no need to worry about MMU */ \ + call _init; \ + nop; \ + nop; \ nop; /* When trap occurs, traps are disabled: ET <- 0 * existing supervisor mode is saved in PS <- S * supervisor mode is enabled: S <- 1 - * register window is advanced to the next window: CWP <- (CWP - 1) % 31 - * without test for window overflow + * register window is advanced to the next window: CWP <- (CWP - 1) % NWINDOWS, + * without test for window overflow; * PSR -> %l0, PC -> %l1, nPC -> %l2 * tt field is written to the particular value that identifies the trap */ @@ -142,16 +145,17 @@ _start: /* Reserved for OS */ TRAP(_interrupts_disable) /* 0x89 - disable interrupts (reserved for OS) */ TRAP(_interrupts_enable) /* 0x8a - enable interrupts (reserved for OS) */ - BAD_TRAP + TRAP(_traps_disable) /* 0x8b - disable traps - where pwr not supported (reserved for OS) */ + TRAP(_traps_halJmpRet) /* 0x8c - safe hal_jmp return (reserved for OS) */ - /* 0x8c - 0xFF - reserved for OS */ + /* 0x8d - 0xFF - reserved for OS */ .rept 116 BAD_TRAP .endr -/* Trap handlers */ +/**************** Trap handlers ****************/ .section ".text" .align 4 @@ -264,9 +268,10 @@ _traps_flushWindows: sll %g4, %g3, %g3 mov %l0, %g5 /* save %psr in %g5 */ + mov %l2, %l1 /* save %nPC in %l1 */ sethi %hi(_interrupts_save_context), %l0 - jmpl %l0 + %lo(_interrupts_save_context), %g1 /* clobbers %g1, %g3 */ + jmpl %l0 + %lo(_interrupts_save_context), %g1 /* clobbers %g1, %g3, %l2 */ rd %wim, %g4 flush_win_done: @@ -276,7 +281,7 @@ flush_win_done: add %g2, 2, %g2 cmp %g2, NWINDOWS bl fw_cwp_done - nop + mov 1, %g3 be fw_cwp_done mov 0, %g2 @@ -284,7 +289,6 @@ flush_win_done: fw_cwp_done: /* %g2 = CWP + 2 (mod NWINDOWS) */ - mov 1, %g3 sll %g3, %g2, %g3 wr %g3, %wim nop @@ -304,8 +308,8 @@ fw_cwp_done: mov %l6, %g4 mov %l7, %g5 - jmp %l2 - rett %l2 + 4 + jmp %l1 + rett %l1 + 4 .size _traps_flushWindows, . - _traps_flushWindows @@ -318,71 +322,190 @@ fw_cwp_done: .global _exceptions_dispatch .type _exceptions_dispatch, #function _exceptions_dispatch: - /* go back to window that caused the trap */ - restore - /* 152 bytes needed for exception context */ - sub %sp, 0x98, %sp - - /* save context on stack */ - std %g0, [%sp + 0x00] - std %g2, [%sp + 0x08] - std %g4, [%sp + 0x10] - std %g6, [%sp + 0x18] - - std %o0, [%sp + 0x20] - std %o2, [%sp + 0x28] - std %o4, [%sp + 0x30] - /* sp stored later */ - st %o7, [%sp + 0x3c] - - std %l0, [%sp + 0x40] - std %l2, [%sp + 0x48] - std %l4, [%sp + 0x50] - std %l6, [%sp + 0x58] - - std %i0, [%sp + 0x60] - std %i2, [%sp + 0x68] - std %i4, [%sp + 0x70] - std %fp, [%sp + 0x78] - - /* save sp */ - add %sp, 0x98, %l0 - st %l0, [%sp + 0x38] - - rd %y, %g1 - st %g1, [%sp + 0x80] - rd %psr, %g1 - xor %g1, PSR_ET, %g1 - st %g1, [%sp + 0x84] - rd %wim, %g1 - st %g1, [%sp + 0x88] - rd %tbr, %g1 - st %g1, [%sp + 0x8c] + /* %g2, g3 used during manual window overflow */ + mov %g2, %l4 + mov %g3, %l5 - save + mov %wim, %g2 + /* check if we've just overflowed + * window overflow if wim == (1 << CWP) + * wim >> l0[4:0] - shift wim by CWP (lowest 5 bits from psr) + */ + srl %g2, %l0, %g3 + cmp %g3, 1 - /* store pc, npc */ - st %l1, [%fp + 0x90] - st %l2, [%fp + 0x94] + bne exc_wovfl_done + sll %g2, (NWINDOWS - 1), %g3 - /* allocate stack frame for exception handler */ - sub %fp, 0x60, %sp + /* calculate new wim: current %wim in %g2, %g3 is scratch */ + srl %g2, 1, %g2 - /* enable traps, disable interrupts */ - or %l0, PSR_PIL, %l4 - wr %l4, PSR_ET, %psr + save + wr %g2, %g3, %wim + nop nop nop + std %l0, [%sp + 0x00] + std %l2, [%sp + 0x08] + std %l4, [%sp + 0x10] + std %l6, [%sp + 0x18] + std %i0, [%sp + 0x20] + std %i2, [%sp + 0x28] + std %i4, [%sp + 0x30] + std %fp, [%sp + 0x38] + restore + +exc_wovfl_done: + /* check if we need to swap to kernel stack + * i.e. when PSR_PS is not set + */ + andcc %l0, PSR_PS, %g0 + bnz exc_no_kstack_switch + + set hal_cpuKernelStack, %l6 + /* Extract CPU ID and add offset */ + rd %asr17, %l7 + srl %l7, 28, %l7 + sll %l7, 2, %l7 + ld [%l6 + %l7], %l7 + ba exc_kstack_set + sub %l7, 0x60, %sp + + exc_no_kstack_switch: + /* we came from kernel, make space for context */ + sub %fp, 0x60, %sp + + exc_kstack_set: + /* Save context on kernel stack */ + + st %sp, [%sp + 0x00] /* sp */ + rd %y, %g2 + st %g2, [%sp + 0x04] /* y */ + + std %l0, [%sp + 0x08] /* psr, PC */ + st %l2, [%sp + 0x10] /* nPC */ + st %g1, [%sp + 0x14] /* g1 */ + std %l4, [%sp + 0x18] /* g2, g3 */ + std %g4, [%sp + 0x20] /* g4, g5 */ + std %g6, [%sp + 0x28] /* g6, g7 */ + + /* input registers here are the outputs of the interrupted window */ + + std %i0, [%sp + 0x30] /* i0, i1 */ + std %i2, [%sp + 0x38] /* i2, i3 */ + std %i4, [%sp + 0x40] /* i4, i5 */ + std %fp, [%sp + 0x48] /* fp (task's sp), i7 */ + + rd %wim, %g2 + rd %tbr, %g3 + std %g2, [%sp + 0x50] /* wim */ + + mov %sp, %l7 + sub %sp, 0x60, %sp + + mov %l7, %o1 /* (exc_context_t *) */ + + mov %l0, %g2 + mov 1, %g4 + sll %g4, %g2, %g3 /* %g3 = 1 << %g2[4:0] (CWP) */ + + sethi %hi(_interrupts_save_context), %g5 + jmpl %g5 + %lo(_interrupts_save_context), %g1 /* clobbers %g1, %g3, %l2 */ + rd %wim, %g4 + + /* enable traps, disable interrupts, restore CWP */ + or %g2, (PSR_PIL | PSR_ET), %g2 + wr %g2, %psr + nop nop /* trap type */ - and %g1, TBR_TT, %o0 - srl %o0, TBR_TT_SHIFT, %o0 - + set TBR_TT_MSK, %g2 + and %g1, %g2, %o0 + /* void exceptions_dispatch(unsigned int n, exc_context_t *ctx) */ call exceptions_dispatch - /* ctx */ - mov %fp, %o1 + srl %o0, TBR_TT_SHIFT, %o0 + + /* disable traps */ +#ifndef __CPU_GR712RC + pwr 0, %psr + nop + nop + nop +#else + ta 0xb +#endif + /* restore current window */ + ld [%sp + 0x04], %g1 /* y */ + ldd [%sp + 0x08], %l0 /* psr, PC */ + wr %g1, %y + ld [%sp + 0x10], %l2 /* nPC */ + ld [%sp + 0x14], %g1 + ldd [%sp + 0x18], %g2 + ldd [%sp + 0x20], %g4 + ldd [%sp + 0x28], %g6 + + ldd [%sp + 0x30], %i0 + ldd [%sp + 0x38], %i2 + ldd [%sp + 0x40], %i4 + ldd [%sp + 0x48], %fp + + /* Check if restore would cause window underflow. + * After restore: CWP = CWP + 1 (mod NWINDOWS) + * i.e. wim >> (CWP + 1) == 1 + */ + + and %l0, PSR_CWP, %l5 + add %l5, 1, %l5 + cmp %l5, NWINDOWS + bne exc_cwp_done1 + rd %wim, %l4 + + /* we'd end up in non-existent window #NWINDOWS, it means it's #0 */ + mov 0, %l5 + +exc_cwp_done1: + /* l4 = wim, l5 = CWP + 1 (mod NWINDOWS) + * check if wim >> (CWP + 1) == 1 (window underflow) + */ + srl %l4, %l5, %l6 + cmp %l6, 1 + bne exc_return + /* uses the delay slot + * calculate new wim + * %l4 = current %wim + * wim = (wim << 1) ^ (wim >> (NWINDOWS - 1)) + */ + sll %l4, 1, %l5 + srl %l4, (NWINDOWS - 1), %l4 + wr %l4, %l5, %wim + nop + nop + nop + restore + ldd [%sp + 0x00], %l0 + ldd [%sp + 0x08], %l2 + ldd [%sp + 0x10], %l4 + ldd [%sp + 0x18], %l6 + ldd [%sp + 0x20], %i0 + ldd [%sp + 0x28], %i2 + ldd [%sp + 0x30], %i4 + ldd [%sp + 0x38], %fp + save + +exc_return: + TN_0018_WAIT_ICACHE(%l3, %l4) + + wr %l0, %psr + nop + nop + nop + + TN_0018_FIX(%l3, %l4) + + jmp %l1 + rett %l2 .size _exceptions_dispatch, . - _exceptions_dispatch /* syscall dispatcher @@ -431,17 +554,23 @@ _traps_syscall: restore s_wovfl_done: - sub %fp, 0x18, %sp - /* write arguments to stack */ - std %i0, [%sp] - std %i2, [%sp + 0x8] - std %i4, [%sp + 0x10] + /* write arguments to stack reserved space */ + st %i0, [%fp + 0x44] + st %i1, [%fp + 0x48] + st %i2, [%fp + 0x4c] + st %i3, [%fp + 0x50] + st %i4, [%fp + 0x54] + st %i5, [%fp + 0x58] - mov %sp, %o1 /* ustack */ + add %fp, 0x44, %o1 /* ustack */ /* swap to kernel stack */ - sethi %hi(hal_cpuKernelStack), %l5 - ld [%l5 + %lo(hal_cpuKernelStack)], %sp + set hal_cpuKernelStack, %l5 + /* Extract CPU ID and add offset */ + rd %asr17, %sp + srl %sp, 28, %sp + sll %sp, 2, %sp + ld [%l5 + %sp], %sp /* for signal handling: */ sub %sp, 0x50, %sp @@ -468,10 +597,14 @@ s_wovfl_done: nop /* disable traps */ +#ifndef __CPU_GR712RC pwr 0, %psr nop nop nop +#else + ta 0xb +#endif /* Check if restore would cause underflow. * After restore: CWP = CWP + 1 @@ -484,11 +617,11 @@ s_wovfl_done: add %g3, 1, %g3 cmp %g3, NWINDOWS bne s_cwp_done - nop + rd %wim, %g4 + mov 0, %g3 s_cwp_done: - rd %wim, %g4 srl %g4, %g3, %g3 cmp %g3, 1 /* if wim >> (CWP + 1) != 1, it's ok */ @@ -519,9 +652,8 @@ s_wunfl_done: mov %o0, %i0 /* pass return value to caller */ /* swap window if needed (after vfork) */ - andn %l0, PSR_CWP, %l0 + andn %l0, (PSR_CWP | PSR_PS | PSR_ET), %l0 or %l0, %g5, %l0 - andn %l0, (PSR_PS | PSR_ET), %l0 wr %l0, %psr nop nop @@ -530,3 +662,460 @@ s_wunfl_done: jmp %l2 rett %l2 + 4 .size _traps_syscall, . - _traps_syscall + + +/* Disable traps + * Must be called from supervisor mode + * Next instruction after `ta` instruction + * must not be a CTI - nPC not written. + */ +.global _traps_disable +.type _traps_disable, #function +_traps_disable: + jmp %l2 + restore +.size _traps_disable, . - _traps_disable + + +/* Disable/Enable interrupts + * on entry: + * %psr in %l0, PC in %l1, nPC in %l2 + */ + + .global _interrupts_disable + .type _interrupts_disable, #function + _interrupts_disable: + or %l0, PSR_PIL, %l0 + wr %l0, %psr + nop + nop + nop + jmp %l2 + rett %l2 + 4 + .size _interrupts_disable, . - _interrupts_disable + + + .global _interrupts_enable + .type _interrupts_enable, #function + _interrupts_enable: + andn %l0, PSR_PIL, %l0 + wr %l0, %psr + nop + nop + nop + jmp %l2 + rett %l2 + 4 + .size _interrupts_enable, . - _interrupts_enable + + + +/* Safe userspace hal_jmp return + * %l0 - psr + * %g1 - return address + */ +.global _traps_halJmpRet +.type _traps_halJmpRet, #function +_traps_halJmpRet: + andn %l0, (PSR_PIL | PSR_PS), %l0 + wr %l0, %psr + nop + nop + nop + jmp %g1 + rett %g1 + 4 +.size _traps_halJmpRet, . - _traps_halJmpRet + + +/*************** Helper functions ***************/ + + +/* This function saves all valid windows on stack. + * Before calling, following registers must be set: + * %g1 - call address + * %g3 - bit set for CWP + * %g4 - wim + * + * Clobbers %g1, %g3, %l2 + */ +.global _interrupts_save_context +.type _interrupts_save_context, #function +_interrupts_save_context: + +save_context_loop: + sll %g3, 1, %l2 + srl %g3, (NWINDOWS - 1), %g3 + or %g3, %l2, %g3 + /* check if restore won't underflow */ + andcc %g3, %g4, %g0 + bnz save_context_done + nop + + /* if not, advance to next window */ + restore + + /* save window on current %sp */ + std %l0, [%sp + 0x00] + std %l2, [%sp + 0x08] + std %l4, [%sp + 0x10] + std %l6, [%sp + 0x18] + std %i0, [%sp + 0x20] + std %i2, [%sp + 0x28] + std %i4, [%sp + 0x30] + + ba save_context_loop + std %fp, [%sp + 0x38] + +save_context_done: + jmpl %g1 + 8, %g0 + nop +.size _interrupts_save_context, . - _interrupts_save_context + + +/* This function restores current window (except %g1, %g2, %g3) + * and first task's window. + * Before calling, following registers must be set: + * %g1 - call address + * %g2 - pointer to context + * + * On return: task's %psr in %g2 + */ +.global _interrupts_restore_context +.type _interrupts_restore_context, #function +_interrupts_restore_context: + /* switch to new task's stack */ + mov %g2, %sp + + /* restore current window */ + ld [%sp + 0x04], %g2 /* y */ + wr %g2, %y + ldd [%sp + 0x08], %l0 /* psr, PC */ + ld [%sp + 0x10], %l2 /* nPC */ + + /* %g1, %g2, %g3 must be restored later */ + + ldd [%sp + 0x20], %g4 + ldd [%sp + 0x28], %g6 + + ldd [%sp + 0x30], %i0 + ldd [%sp + 0x38], %i2 + ldd [%sp + 0x40], %i4 + ldd [%sp + 0x48], %fp + + /* switch window to the task's window and restore context */ + restore + + ldd [%sp + 0x00], %l0 + ldd [%sp + 0x08], %l2 + ldd [%sp + 0x10], %l4 + ldd [%sp + 0x18], %l6 + ldd [%sp + 0x20], %i0 + ldd [%sp + 0x28], %i2 + ldd [%sp + 0x30], %i4 + ldd [%sp + 0x38], %fp + + rd %psr, %g2 + + jmpl %g1 + 8, %g0 + /* go back to handler's window */ + save +.size _interrupts_restore_context, . - _interrupts_restore_context + + +/* int hal_cpuReschedule(struct _spinlock_t *spinlock, spinlock_ctx_t *scp) */ +.global hal_cpuReschedule +.type hal_cpuReschedule, #function +hal_cpuReschedule: + save %sp, -0x50, %sp + +#ifndef __CPU_GR712RC + pwr 0, %psr + nop + nop + nop +#else + ta 0xb +#endif + + /* this is voluntary reschedule, + * no need to store caller-saves registers + * (outputs of previous window and g1-g5) + */ + rd %psr, %l0 + or %l0, PSR_PS, %l0 + + mov %sp, %l1 /* save pointer to context */ + + st %sp, [%sp + 0x00] + st %g0, [%sp + 0x04] + st %l0, [%sp + 0x08] /* psr */ + add %i7, 0x8, %g3 + st %g3, [%sp + 0x0c] /* pc */ + add %g3, 0x4, %g3 + st %g3, [%sp + 0x10] /* npc */ + std %g6, [%sp + 0x28] + st %g0, [%sp + 0x30] /* default return value */ + st %fp, [%sp + 0x48] + + sub %sp, 0x60, %sp + + /* enable traps, keep interrupts disabled */ + or %l0, (PSR_PIL | PSR_ET), %l0 + wr %l0, %psr + nop + nop + nop + + mov %l0, %g2 + + mov 1, %g4 + sll %g4, %g2, %g3 /* %g3 = 1 << %g2[4:0] (CWP) */ + + sethi %hi(_interrupts_save_context), %g5 + jmpl %g5 + %lo(_interrupts_save_context), %g1 /* clobbers %g1, %g3, %l2 */ + rd %wim, %g4 + + wr %g2, %psr + nop + nop + nop + + and %g2, PSR_CWP, %g2 + add %g2, 1, %g2 + cmp %g2, NWINDOWS + bne r_cwp_done + mov 1, %g3 + + mov 0, %g2 + +r_cwp_done: + /* set %wim to 1 << %g2 (CWP + 1) */ + sll %g3, %g2, %g2 + wr %g2, %wim + + /* check if spinlock is not NULL */ + cmp %i0, %g0 + beq r_no_spinlock + nop + +r_spinlock: + clr %o0 + mov %l1, %o1 /* cpu_context_t * */ + call _threads_schedule + clr %o2 + + /* clear spinlock */ + stbar + ba r_return + stub %g0, [%i0 + 0x0c] + +r_no_spinlock: + + clr %o0 + mov %l1, %o1 /* cpu_context_t * */ + call threads_schedule + clr %o2 + +r_return: + /* disable traps */ +#ifndef __CPU_GR712RC + pwr 0, %psr + nop + nop + nop +#else + ta 0xb +#endif + + ld [%l1], %g2 /* ctx pointer */ + + /* Set %psr of the new task. + * This will cause window to be switched + * so that the new task's window is CWP + 1. + */ + + mov %g0, %wim /* we don't need it now */ + ld [%g2 + 0x08], %g1 + nop + andn %g1, PSR_ET, %g1 /* disable traps */ + wr %g1, %psr + + sethi %hi(_interrupts_restore_context), %g5 + jmpl %g5 + %lo(_interrupts_restore_context), %g1 + nop + + /* check CWP overflow (same as before) */ + and %g2, PSR_CWP, %g2 + add %g2, 1, %g2 + cmp %g2, NWINDOWS + bne r_cwp_done1 + mov 1, %g3 + + mov 0, %g2 + +r_cwp_done1: + /* set %wim to 1 << %g2 (CWP + 1) */ + sll %g3, %g2, %g2 + wr %g2, %wim + nop + + /* restore %g1, %g2, %g3 */ + ld [%sp + 0x14], %g1 + ldd [%sp + 0x18], %g2 + + TN_0018_WAIT_ICACHE(%l3, %l4) + + andn %l0, PSR_ET, %l0 + wr %l0, %psr + nop + nop + nop + + TN_0018_FIX(%l3, %l4) + + /* Set both PC and nPC */ + jmp %l1 + rett %l2 +.size hal_cpuReschedule, . - hal_cpuReschedule + + +/**************** Jump functions ****************/ + + +/* void hal_jmp(void *f, void *kstack, void *stack, int argc) */ +.global hal_jmp +.type hal_jmp, #function +hal_jmp: + save %sp, -0x60, %sp +#ifndef __CPU_GR712RC + pwr 0, %psr + nop + nop + nop +#else + ta 0xb +#endif + + /* calculate new wim */ + rd %psr, %l0 + and %l0, PSR_CWP, %l1 + add %l1, 1, %l1 + cmp %l1, NWINDOWS + bne 1f + mov 1, %l2 + + mov 0, %l1 +1: + sll %l2, %l1, %l1 + wr %l1, %wim + nop + nop + nop + + cmp %i2, %g0 /* stack != NULL */ + bne 3f + nop + mov %i1, %fp /* fp = kstack */ + subcc %i3, 1, %i3 + bneg 2f + nop + subcc %i3, 1, %i3 + bneg 2f + ld [%fp], %o0 + subcc %i3, 1, %i3 + bneg 2f + ld [%fp + 4], %o1 + subcc %i3, 1, %i3 + bneg 2f + ld [%fp + 8], %o2 + ld [%fp + 12], %o3 +2: + andn %fp, 0x7, %fp + sub %fp, 0x60, %sp + wr %l0, PSR_ET, %psr + nop + nop + nop + call %i0 + nop +3: + subcc %i3, 1, %i3 + bneg 4f + mov %i2, %o0 + subcc %i3, 1, %i3 + bneg 4f + ld [%i2], %o0 + subcc %i3, 1, %i3 + bneg 4f + ld [%i2 + 4], %o1 + subcc %i3, 1, %i3 + bneg 4f + ld [%i2 + 8], %o2 + ld [%i2 + 12], %o3 +4: + andn %i2, 0x7, %fp + sub %fp, 0x60, %sp + + /* Enable traps, disable interrupts */ + or %l0, (PSR_ET | PSR_PIL), %l0 + wr %l0, %psr + nop + nop + nop + mov %i0, %g1 + /* safely jump to userspace through trap */ + ta 0xc +.size hal_jmp, . - hal_jmp + + +/* void hal_longjmp(cpu_context_t *ctx); */ +.global hal_longjmp +.type hal_longjmp, #function +hal_longjmp: + /* disable interrupts */ + ta 0x09 + mov %o0, %g2 + /* flush windows onto stack */ + ta 0x03 + + mov %g0, %wim + + ld [%g2 + 0x08], %g1 /* psr */ + andn %g1, PSR_ET, %g1 /* disable traps */ + or %g1, (PSR_PIL | PSR_S), %g3 /* disable interrupts, set supervisor mode */ + + /* writing psr might cause window switch */ + wr %g3, %psr + + sethi %hi(_interrupts_restore_context), %g5 + jmpl %g5 + %lo(_interrupts_restore_context), %g1 + nop + + /* check CWP overflow */ + and %g2, PSR_CWP, %g2 + add %g2, 1, %g2 + cmp %g2, NWINDOWS + bne 5f + mov 1, %g3 + + mov 0, %g2 +5: + /* set %wim to 1 << %g2 (CWP + 1) */ + sll %g3, %g2, %g2 + wr %g2, %wim + nop + + /* restore %g1-%g3 */ + ld [%sp + 0x14], %g1 + ldd [%sp + 0x18], %g2 + + /* restore psr */ + or %l0, PSR_S, %l0 + andn %l0, PSR_ET, %l0 + wr %l0, %psr + nop + nop + nop + + jmp %l1 + rett %l2 +.size hal_longjmp, . - hal_longjmp diff --git a/hal/sparcv8leon3/arch/cpu.h b/hal/sparcv8leon3/arch/cpu.h index bb316bd02..36314a7bb 100644 --- a/hal/sparcv8leon3/arch/cpu.h +++ b/hal/sparcv8leon3/arch/cpu.h @@ -16,7 +16,8 @@ #ifndef _HAL_LEON3_CPU_H_ #define _HAL_LEON3_CPU_H_ -#include "types.h" + +#ifdef NOMMU #define SIZE_PAGE 0x200 @@ -29,6 +30,21 @@ #define SIZE_USTACK (8 * SIZE_PAGE) #endif +#else + +#define SIZE_PAGE 0x1000 + +/* Default kernel and user stack sizes */ +#ifndef SIZE_KSTACK +#define SIZE_KSTACK (SIZE_PAGE) +#endif + +#ifndef SIZE_USTACK +#define SIZE_USTACK (SIZE_PAGE) +#endif + +#endif + /* Processor State Register */ #define PSR_CWP 0x1f /* Current window pointer */ @@ -41,12 +57,26 @@ #define PSR_ICC (0xf << 20) /* Integer condition codes */ -/* Trap Base Register */ -#define TBR_TT 0x3f /* Trap type */ +/* Cache control register */ +#define CCR_ICS (3 << 0) /* ICache state */ +#define CCR_DCS (3 << 2) /* DCache state */ +#define CCR_IF (1 << 4) /* ICache freeze on interrupt */ +#define CCR_DF (1 << 5) /* DCache freeze on interrupt */ +#define CCR_DP (1 << 14) /* DCache flush pending */ +#define CCR_IP (1 << 15) /* ICache flush pending */ +#define CCR_IB (1 << 16) /* ICache burst fetch en */ +#define CCR_FI (1 << 21) /* Flush ICache */ +#define CCR_FD (1 << 22) /* Flush DCache */ +#define CCR_DS (1 << 23) /* DCache snooping */ #ifndef __ASSEMBLY__ + +#include "types.h" +#include "gaisler/gaisler.h" + + #define SYSTICK_INTERVAL 1000 @@ -139,17 +169,6 @@ extern time_t hal_timerGetUs(void); /* performance */ -static inline void hal_cpuHalt(void) -{ - /* must be performed in supervisor mode with interrupts enabled */ - /* clang-format off */ - - __asm__ volatile ("wr %g0, %asr19"); - - /* clang-format on */ -} - - static inline void hal_cpuSetDevBusy(int s) { } @@ -229,7 +248,7 @@ static inline void hal_cpuRestore(cpu_context_t *curr, cpu_context_t *next) static inline unsigned int hal_cpuGetCount(void) { - return 1; + return NUM_CPUS; } diff --git a/hal/sparcv8leon3/arch/exceptions.h b/hal/sparcv8leon3/arch/exceptions.h index 3a50ee3da..f581d996c 100644 --- a/hal/sparcv8leon3/arch/exceptions.h +++ b/hal/sparcv8leon3/arch/exceptions.h @@ -17,63 +17,26 @@ #define _HAL_LEON3_EXCEPTIONS_H_ #include "types.h" +#include #define EXC_DEFAULT 128 -#define EXC_UNDEFINED 1 +#define EXC_UNDEFINED 2 + +#ifndef NOMMU +#define EXC_PAGEFAULT 1 +#endif #define SIZE_CTXDUMP 512 #pragma pack(push, 1) typedef struct _exc_context_t { - /* global */ - u32 g0; - u32 g1; - u32 g2; - u32 g3; - u32 g4; - u32 g5; - u32 g6; - u32 g7; - - /* out */ - u32 o0; - u32 o1; - u32 o2; - u32 o3; - u32 o4; - u32 o5; - u32 sp; - u32 o7; - - /* local */ - u32 l0; - u32 l1; - u32 l2; - u32 l3; - u32 l4; - u32 l5; - u32 l6; - u32 l7; - - /* in */ - u32 i0; - u32 i1; - u32 i2; - u32 i3; - u32 i4; - u32 i5; - u32 fp; - u32 i7; + cpu_context_t cpuCtx; /* special */ - u32 y; - u32 psr; u32 wim; u32 tbr; - u32 pc; - u32 npc; } exc_context_t; #pragma pack(pop) diff --git a/hal/sparcv8leon3/arch/interrupts.h b/hal/sparcv8leon3/arch/interrupts.h index 4b387fa1c..76ed2a3f9 100644 --- a/hal/sparcv8leon3/arch/interrupts.h +++ b/hal/sparcv8leon3/arch/interrupts.h @@ -24,7 +24,9 @@ typedef struct _intr_handler_t { unsigned int n; int (*f)(unsigned int, cpu_context_t *, void *); void *data; +#ifdef NOMMU void *got; +#endif } intr_handler_t; diff --git a/hal/sparcv8leon3/arch/pmap.h b/hal/sparcv8leon3/arch/pmap.h index a22da0aad..cd0395a5d 100644 --- a/hal/sparcv8leon3/arch/pmap.h +++ b/hal/sparcv8leon3/arch/pmap.h @@ -17,15 +17,30 @@ #define _HAL_LEON3_PMAP_H_ -#include "types.h" +#define SIZE_PDIR 0x1000 + +/* Predefined virtual addresses */ + +#define VADDR_KERNEL 0xc0000000 /* base virtual address of kernel space */ +#define VADDR_MIN 0x00000000 +#define VADDR_MAX 0xffffffff +#define VADDR_USR_MAX 0x80000000 +/* Architecture dependent page attributes */ -#define PGHD_PRESENT 0x01 -#define PGHD_USER 0x04 -#define PGHD_WRITE 0x02 -#define PGHD_EXEC 0x00 -#define PGHD_DEV 0x00 -#define PGHD_NOT_CACHED 0x00 +#define PGHD_READ (1 << 0) +#define PGHD_WRITE (1 << 1) +#define PGHD_EXEC (1 << 2) +#define PGHD_USER (1 << 3) +#define PGHD_PRESENT (1 << 4) +#define PGHD_DEV (1 << 5) +#define PGHD_NOT_CACHED (1 << 6) + +/* Page table entry types */ + +#define PAGE_INVALID 0x0u +#define PAGE_DESCR 0x1u +#define PAGE_ENTRY 0x2u /* Page flags */ @@ -43,9 +58,24 @@ #define PAGE_KERNEL_HEAP (6 << 4) +/* Page access permissions */ + +#define PERM_USER_RO 0x0 /* User read-only */ +#define PERM_USER_RW 0x1 /* User read-write */ +#define PERM_USER_RX 0x2 /* User read-exec */ +#define PERM_USER_RWX 0x3 /* User read-write-exec */ +#define PERM_USER_XO 0x4 /* User exec-only */ +#define PERM_SUPER_RW 0x5 /* Supervisor read-write */ +#define PERM_SUPER_RX 0x6 /* Supervisor read-exec */ +#define PERM_SUPER_RWX 0x7 /* Supervisor read-write-exec */ + + #ifndef __ASSEMBLY__ +#include "types.h" + + typedef struct _page_t { addr_t addr; u8 idx; @@ -55,6 +85,23 @@ typedef struct _page_t { } page_t; +#ifndef NOMMU + + +typedef struct _pmap_t { + u32 context; + u32 *pdir1; + addr_t addr; /* physical address of pdir */ + void *start; + void *end; + void *pmapv; + page_t *pmapp; +} pmap_t; + + +#else + + typedef struct _pmap_t { u32 mpr; void *start; @@ -62,7 +109,10 @@ typedef struct _pmap_t { } pmap_t; -#endif +#endif /* NOMMU */ + + +#endif /* __ASSEMBLY__ */ #endif diff --git a/hal/sparcv8leon3/cpu.c b/hal/sparcv8leon3/cpu.c index 2b556c70e..90601e4f1 100644 --- a/hal/sparcv8leon3/cpu.c +++ b/hal/sparcv8leon3/cpu.c @@ -18,19 +18,43 @@ #include "hal/interrupts.h" #include "hal/spinlock.h" #include "hal/string.h" +#include "hal/sparcv8leon3/sparcv8leon3.h" -#include "config.h" - - -#define BOOTSTRAP_ADDR 0x80008000 -#define BOOTSTRAP_SPIM 0x400BC003 +#define ASR17_FPU_MSK (3 << 10) #define STR(x) #x #define XSTR(x) STR(x) -ptr_t hal_cpuKernelStack; +ptr_t hal_cpuKernelStack[NUM_CPUS]; + + +static const char *hal_cpuGetFpuOption(void) +{ + u32 asr; + + /* clang-format off */ + __asm__ volatile("rd %%asr17, %0" : "=r"(asr)); + /* clang-format on */ + + switch ((asr & ASR17_FPU_MSK) >> 10) { + case 0x0: + return "No FPU"; + + case 0x1: + return "GRFPU"; + + case 0x2: + return "Meiko FPU"; + + case 0x3: + return "GRFPU-Lite"; + + default: + return "Unknown"; + } +} int hal_cpuCreateContext(cpu_context_t **nctx, void *start, void *kstack, size_t kstacksz, void *ustack, void *arg, hal_tls_t *tls) @@ -119,7 +143,7 @@ int hal_cpuCreateContext(cpu_context_t **nctx, void *start, void *kstack, size_t void _hal_cpuSetKernelStack(void *kstack) { - hal_cpuKernelStack = (ptr_t)kstack; + hal_cpuKernelStack[hal_cpuGetID()] = (ptr_t)kstack; } @@ -173,10 +197,13 @@ char *hal_cpuInfo(char *info) char *hal_cpuFeatures(char *features, unsigned int len) { unsigned int n = 0; + const char *fpu = hal_cpuGetFpuOption(); if ((len - n) > 12) { - hal_strcpy(features, "GRFPU-Lite, "); - n += 12; + hal_strcpy(features, fpu); + n += hal_strlen(fpu); + hal_strcpy(features + n, ", "); + n += 2; } if ((len - n) > 10 + hal_strlen(XSTR(NWINDOWS))) { hal_strcpy(features + n, XSTR(NWINDOWS) " windows, "); @@ -195,6 +222,8 @@ char *hal_cpuFeatures(char *features, unsigned int len) void hal_cleanDCache(ptr_t start, size_t len) { + (void)start; + (void)len; } @@ -205,26 +234,6 @@ void hal_cpuLowPower(time_t us, spinlock_t *spinlock, spinlock_ctx_t *sc) } -void hal_cpuReboot(void) -{ - /* Reset to the built-in bootloader */ - hal_cpuDisableInterrupts(); - - /* Reboot to SPIM */ - *(volatile u32 *)(BOOTSTRAP_ADDR) = BOOTSTRAP_SPIM; - - /* clang-format off */ - __asm__ volatile ( - "jmp %%g0\n\t" - "nop\n\t" - ::: - ); - /* clang-format on */ - - __builtin_unreachable(); -} - - void _hal_cpuInit(void) { return; diff --git a/hal/sparcv8leon3/exceptions-nommu.c b/hal/sparcv8leon3/exceptions-nommu.c new file mode 100644 index 000000000..ba5c68aac --- /dev/null +++ b/hal/sparcv8leon3/exceptions-nommu.c @@ -0,0 +1,171 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * Exception handling + * + * Copyright 2022 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include "hal/exceptions.h" +#include "hal/spinlock.h" +#include "hal/cpu.h" +#include "hal/console.h" +#include "hal/string.h" +#include "include/mman.h" + + +static const char *const hal_exceptionsType(int n) +{ + switch (n) { + case 0x0: + return " #Reset"; + case 0x1: + return " #Page fault - instruction fetch"; + case 0x2: + return " #Illegal instruction"; + case 0x3: + return " #Privileged instruction"; + case 0x4: + return " #FP disabled"; + case 0x7: + return " #Address not aligned"; + case 0x8: + return " #FP exception"; + case 0x9: + return " #Page fault - data load"; + case 0xa: + return " #Tag overflow"; + case 0xb: + return " #Watchpoint"; + case 0x2b: + return " #Data store error"; + case 0x81: + return " #Breakpoint"; + case 0x82: + return " #Division by zero"; + case 0x84: + return " #Clean windows"; + case 0x85: + return " #Range check"; + case 0x86: + return " #Fix alignment"; + case 0x87: + return " #Integer overflow"; + case 0x88: + return " #Syscall (unimplemented)"; + default: + return " #Reserved/Unknown"; + } +} + + +void hal_exceptionsDumpContext(char *buff, exc_context_t *ctx, int n) +{ + cpu_winContext_t *win = (cpu_winContext_t *)ctx->cpuCtx.sp; + size_t i = hal_i2s("\033[0m\nException: 0x", buff, n, 16, 0); + buff[i] = '\0'; + + hal_strcpy(buff += hal_strlen(buff), hal_exceptionsType(n)); + hal_strcpy(buff += hal_strlen(buff), "\n"); + buff += hal_strlen(buff); + + i = 0; + i += hal_i2s(" g0=", &buff[i], 0, 16, 1); + i += hal_i2s(" g1=", &buff[i], ctx->cpuCtx.g1, 16, 1); + i += hal_i2s(" g2=", &buff[i], ctx->cpuCtx.g2, 16, 1); + i += hal_i2s(" g3=", &buff[i], ctx->cpuCtx.g3, 16, 1); + i += hal_i2s("\n g4=", &buff[i], ctx->cpuCtx.g4, 16, 1); + i += hal_i2s(" g5=", &buff[i], ctx->cpuCtx.g5, 16, 1); + i += hal_i2s(" g6=", &buff[i], ctx->cpuCtx.g6, 16, 1); + i += hal_i2s(" g7=", &buff[i], ctx->cpuCtx.g7, 16, 1); + + i += hal_i2s("\n o0=", &buff[i], ctx->cpuCtx.o0, 16, 1); + i += hal_i2s(" o1=", &buff[i], ctx->cpuCtx.o1, 16, 1); + i += hal_i2s(" o2=", &buff[i], ctx->cpuCtx.o2, 16, 1); + i += hal_i2s(" o3=", &buff[i], ctx->cpuCtx.o3, 16, 1); + i += hal_i2s("\n o4=", &buff[i], ctx->cpuCtx.o4, 16, 1); + i += hal_i2s(" o5=", &buff[i], ctx->cpuCtx.o5, 16, 1); + i += hal_i2s(" sp=", &buff[i], ctx->cpuCtx.sp, 16, 1); + i += hal_i2s(" o7=", &buff[i], ctx->cpuCtx.o7, 16, 1); + + i += hal_i2s("\n l0=", &buff[i], win->l0, 16, 1); + i += hal_i2s(" l1=", &buff[i], win->l1, 16, 1); + i += hal_i2s(" l2=", &buff[i], win->l2, 16, 1); + i += hal_i2s(" l3=", &buff[i], win->l3, 16, 1); + i += hal_i2s("\n l4=", &buff[i], win->l4, 16, 1); + i += hal_i2s(" l5=", &buff[i], win->l5, 16, 1); + i += hal_i2s(" l6=", &buff[i], win->l6, 16, 1); + i += hal_i2s(" l7=", &buff[i], win->l7, 16, 1); + + i += hal_i2s("\n i0=", &buff[i], win->i0, 16, 1); + i += hal_i2s(" i1=", &buff[i], win->i1, 16, 1); + i += hal_i2s(" i2=", &buff[i], win->i2, 16, 1); + i += hal_i2s(" i3=", &buff[i], win->i3, 16, 1); + i += hal_i2s("\n i4=", &buff[i], win->i4, 16, 1); + i += hal_i2s(" i5=", &buff[i], win->i5, 16, 1); + i += hal_i2s(" fp=", &buff[i], win->fp, 16, 1); + i += hal_i2s(" i7=", &buff[i], win->i7, 16, 1); + + i += hal_i2s("\n y=", &buff[i], ctx->cpuCtx.y, 16, 1); + i += hal_i2s(" psr=", &buff[i], ctx->cpuCtx.psr, 16, 1); + i += hal_i2s(" wim=", &buff[i], ctx->wim, 16, 1); + i += hal_i2s(" tbr=", &buff[i], ctx->tbr, 16, 1); + i += hal_i2s("\n pc=", &buff[i], ctx->cpuCtx.pc, 16, 1); + i += hal_i2s(" npc=", &buff[i], ctx->cpuCtx.npc, 16, 1); + buff[i++] = '\n'; + buff[i] = '\0'; +} + + +void exceptions_dispatch(unsigned int n, exc_context_t *ctx) +{ + char buff[SIZE_CTXDUMP]; + + hal_cpuDisableInterrupts(); + + hal_exceptionsDumpContext(buff, ctx, n); + hal_consolePrint(ATTR_BOLD, buff); + +#ifdef NDEBUG + hal_cpuReboot(); +#endif + + for (;;) { + hal_cpuHalt(); + } +} + + +int hal_exceptionsFaultType(unsigned int n, exc_context_t *ctx) +{ + return 0; +} + + +ptr_t hal_exceptionsPC(exc_context_t *ctx) +{ + return ctx->cpuCtx.pc; +} + + +void *hal_exceptionsFaultAddr(unsigned int n, exc_context_t *ctx) +{ + return NULL; +} + + +int hal_exceptionsSetHandler(unsigned int n, void (*handler)(unsigned int, exc_context_t *)) +{ + return 0; +} + +void _hal_exceptionsInit(void) +{ +} diff --git a/hal/sparcv8leon3/exceptions.c b/hal/sparcv8leon3/exceptions.c index a28cc4377..c5ceeccf7 100644 --- a/hal/sparcv8leon3/exceptions.c +++ b/hal/sparcv8leon3/exceptions.c @@ -18,46 +18,56 @@ #include "hal/cpu.h" #include "hal/console.h" #include "hal/string.h" +#include "hal/sparcv8leon3/srmmu.h" #include "include/mman.h" +static struct { + void (*defaultHandler)(unsigned int, exc_context_t *); + void (*mmuFaultHandler)(unsigned int, exc_context_t *); + spinlock_t lock; +} exceptions_common; + + static const char *const hal_exceptionsType(int n) { switch (n) { case 0x0: - return "0 #Reset"; + return " #Reset"; case 0x1: - return "1 #Page fault - instruction fetch"; + return " #Page fault - instruction fetch"; case 0x2: - return "2 #Illegal instruction"; + return " #Illegal instruction"; case 0x3: - return "3 #Privileged instruction"; + return " #Privileged instruction"; case 0x4: - return "4 #FP disabled"; + return " #FP disabled"; case 0x7: - return "7 #Address not aligned"; + return " #Address not aligned"; case 0x8: - return "8 #FP exception"; + return " #FP exception"; case 0x9: - return "9 #Page fault - data load"; + return " #Page fault - data load"; case 0xa: - return "10 #Tag overflow"; + return " #Tag overflow"; case 0xb: - return "11 #Watchpoint"; + return " #Watchpoint"; + case 0x2b: + return " #Data store error"; case 0x81: - return "0x81 #Breakpoint"; + return " #Breakpoint"; case 0x82: - return "0x82 #Division by zero"; + return " #Division by zero"; case 0x84: - return "0x84 #Clean windows"; + return " #Clean windows"; case 0x85: - return "0x85 #Range check"; + return " #Range check"; case 0x86: - return "0x86 #Fix alignment"; + return " #Fix alignment"; case 0x87: - return "0x87 #Integer overflow"; + return " #Integer overflow"; case 0x88: - return "0x88 #Syscall (unimplemented)"; + return " #Syscall (unimplemented)"; default: return " #Reserved/Unknown"; } @@ -66,60 +76,63 @@ static const char *const hal_exceptionsType(int n) void hal_exceptionsDumpContext(char *buff, exc_context_t *ctx, int n) { - size_t i = 0; - hal_strcpy(buff, "\033[0m\nException: "); + cpu_winContext_t *win = (cpu_winContext_t *)ctx->cpuCtx.sp; + size_t i = hal_i2s("\033[0m\nException: 0x", buff, n, 16, 0); + buff[i] = '\0'; + hal_strcpy(buff += hal_strlen(buff), hal_exceptionsType(n)); hal_strcpy(buff += hal_strlen(buff), "\n"); buff += hal_strlen(buff); - i += hal_i2s(" g0=", &buff[i], ctx->g0, 16, 1); - i += hal_i2s(" g1=", &buff[i], ctx->g1, 16, 1); - i += hal_i2s(" g2=", &buff[i], ctx->g2, 16, 1); - i += hal_i2s(" g3=", &buff[i], ctx->g3, 16, 1); - i += hal_i2s("\n g4=", &buff[i], ctx->g4, 16, 1); - i += hal_i2s(" g5=", &buff[i], ctx->g5, 16, 1); - i += hal_i2s(" g6=", &buff[i], ctx->g6, 16, 1); - i += hal_i2s(" g7=", &buff[i], ctx->g7, 16, 1); - - i += hal_i2s("\n o0=", &buff[i], ctx->o0, 16, 1); - i += hal_i2s(" o1=", &buff[i], ctx->o1, 16, 1); - i += hal_i2s(" o2=", &buff[i], ctx->o2, 16, 1); - i += hal_i2s(" o3=", &buff[i], ctx->o3, 16, 1); - i += hal_i2s("\n o4=", &buff[i], ctx->o4, 16, 1); - i += hal_i2s(" o5=", &buff[i], ctx->o5, 16, 1); - i += hal_i2s(" sp=", &buff[i], ctx->sp, 16, 1); - i += hal_i2s(" o7=", &buff[i], ctx->o7, 16, 1); - - i += hal_i2s("\n l0=", &buff[i], ctx->l0, 16, 1); - i += hal_i2s(" l1=", &buff[i], ctx->l1, 16, 1); - i += hal_i2s(" l2=", &buff[i], ctx->l2, 16, 1); - i += hal_i2s(" l3=", &buff[i], ctx->l3, 16, 1); - i += hal_i2s("\n l4=", &buff[i], ctx->l4, 16, 1); - i += hal_i2s(" l5=", &buff[i], ctx->l5, 16, 1); - i += hal_i2s(" l6=", &buff[i], ctx->l6, 16, 1); - i += hal_i2s(" l7=", &buff[i], ctx->l7, 16, 1); - - i += hal_i2s("\n i0=", &buff[i], ctx->i0, 16, 1); - i += hal_i2s(" i1=", &buff[i], ctx->i1, 16, 1); - i += hal_i2s(" i2=", &buff[i], ctx->i2, 16, 1); - i += hal_i2s(" i3=", &buff[i], ctx->i3, 16, 1); - i += hal_i2s("\n i4=", &buff[i], ctx->i4, 16, 1); - i += hal_i2s(" i5=", &buff[i], ctx->i5, 16, 1); - i += hal_i2s(" fp=", &buff[i], ctx->fp, 16, 1); - i += hal_i2s(" i7=", &buff[i], ctx->i7, 16, 1); - - i += hal_i2s("\n y=", &buff[i], ctx->y, 16, 1); - i += hal_i2s(" psr=", &buff[i], ctx->psr, 16, 1); + i = 0; + i += hal_i2s(" g0=", &buff[i], 0, 16, 1); + i += hal_i2s(" g1=", &buff[i], ctx->cpuCtx.g1, 16, 1); + i += hal_i2s(" g2=", &buff[i], ctx->cpuCtx.g2, 16, 1); + i += hal_i2s(" g3=", &buff[i], ctx->cpuCtx.g3, 16, 1); + i += hal_i2s("\n g4=", &buff[i], ctx->cpuCtx.g4, 16, 1); + i += hal_i2s(" g5=", &buff[i], ctx->cpuCtx.g5, 16, 1); + i += hal_i2s(" g6=", &buff[i], ctx->cpuCtx.g6, 16, 1); + i += hal_i2s(" g7=", &buff[i], ctx->cpuCtx.g7, 16, 1); + + i += hal_i2s("\n o0=", &buff[i], ctx->cpuCtx.o0, 16, 1); + i += hal_i2s(" o1=", &buff[i], ctx->cpuCtx.o1, 16, 1); + i += hal_i2s(" o2=", &buff[i], ctx->cpuCtx.o2, 16, 1); + i += hal_i2s(" o3=", &buff[i], ctx->cpuCtx.o3, 16, 1); + i += hal_i2s("\n o4=", &buff[i], ctx->cpuCtx.o4, 16, 1); + i += hal_i2s(" o5=", &buff[i], ctx->cpuCtx.o5, 16, 1); + i += hal_i2s(" sp=", &buff[i], ctx->cpuCtx.sp, 16, 1); + i += hal_i2s(" o7=", &buff[i], ctx->cpuCtx.o7, 16, 1); + + i += hal_i2s("\n l0=", &buff[i], win->l0, 16, 1); + i += hal_i2s(" l1=", &buff[i], win->l1, 16, 1); + i += hal_i2s(" l2=", &buff[i], win->l2, 16, 1); + i += hal_i2s(" l3=", &buff[i], win->l3, 16, 1); + i += hal_i2s("\n l4=", &buff[i], win->l4, 16, 1); + i += hal_i2s(" l5=", &buff[i], win->l5, 16, 1); + i += hal_i2s(" l6=", &buff[i], win->l6, 16, 1); + i += hal_i2s(" l7=", &buff[i], win->l7, 16, 1); + + i += hal_i2s("\n i0=", &buff[i], win->i0, 16, 1); + i += hal_i2s(" i1=", &buff[i], win->i1, 16, 1); + i += hal_i2s(" i2=", &buff[i], win->i2, 16, 1); + i += hal_i2s(" i3=", &buff[i], win->i3, 16, 1); + i += hal_i2s("\n i4=", &buff[i], win->i4, 16, 1); + i += hal_i2s(" i5=", &buff[i], win->i5, 16, 1); + i += hal_i2s(" fp=", &buff[i], win->fp, 16, 1); + i += hal_i2s(" i7=", &buff[i], win->i7, 16, 1); + + i += hal_i2s("\n y=", &buff[i], ctx->cpuCtx.y, 16, 1); + i += hal_i2s(" psr=", &buff[i], ctx->cpuCtx.psr, 16, 1); i += hal_i2s(" wim=", &buff[i], ctx->wim, 16, 1); i += hal_i2s(" tbr=", &buff[i], ctx->tbr, 16, 1); - i += hal_i2s("\n pc=", &buff[i], ctx->pc, 16, 1); - i += hal_i2s(" npc=", &buff[i], ctx->npc, 16, 1); + i += hal_i2s("\n pc=", &buff[i], ctx->cpuCtx.pc, 16, 1); + i += hal_i2s(" npc=", &buff[i], ctx->cpuCtx.npc, 16, 1); buff[i++] = '\n'; buff[i] = '\0'; } -void exceptions_dispatch(unsigned int n, exc_context_t *ctx) +__attribute__((noreturn)) static void exceptions_defaultHandler(unsigned int n, exc_context_t *ctx) { char buff[SIZE_CTXDUMP]; @@ -129,38 +142,74 @@ void exceptions_dispatch(unsigned int n, exc_context_t *ctx) hal_consolePrint(ATTR_BOLD, buff); #ifdef NDEBUG - hal_cpuReboot(); + // hal_cpuReboot(); #endif for (;;) { hal_cpuHalt(); } + + __builtin_unreachable(); +} + + +extern void threads_setupUserReturn(void *retval); + + +void exceptions_dispatch(unsigned int n, exc_context_t *ctx) +{ + if (n == EXC_PAGEFAULT) { + exceptions_common.mmuFaultHandler(n, ctx); + } + else { + exceptions_common.defaultHandler(n, ctx); + } + + /* Handle signals if necessary */ + if (hal_cpuSupervisorMode(&ctx->cpuCtx) == 0) { + threads_setupUserReturn((void *)ctx->cpuCtx.o0); + } } int hal_exceptionsFaultType(unsigned int n, exc_context_t *ctx) { + if (n == EXC_PAGEFAULT) { + return hal_srmmuGetFaultSts(); + } + return 0; } ptr_t hal_exceptionsPC(exc_context_t *ctx) { - return ctx->pc; + return ctx->cpuCtx.pc; } void *hal_exceptionsFaultAddr(unsigned int n, exc_context_t *ctx) { - return NULL; + return (void *)hal_srmmuGetFaultAddr(); } int hal_exceptionsSetHandler(unsigned int n, void (*handler)(unsigned int, exc_context_t *)) { + if (n == EXC_PAGEFAULT) { + exceptions_common.mmuFaultHandler = handler; + } + else { + exceptions_common.defaultHandler = handler; + } + return 0; } void _hal_exceptionsInit(void) { + hal_spinlockCreate(&exceptions_common.lock, "exceptions.lock"); + + exceptions_common.defaultHandler = exceptions_defaultHandler; + exceptions_common.mmuFaultHandler = exceptions_defaultHandler; } diff --git a/hal/sparcv8leon3/gaisler/Makefile b/hal/sparcv8leon3/gaisler/Makefile new file mode 100644 index 000000000..f48e0119f --- /dev/null +++ b/hal/sparcv8leon3/gaisler/Makefile @@ -0,0 +1,19 @@ +# +# Makefile for Phoenix-RTOS kernel (SPARCV8 LEON3 HAL) +# +# Copyright 2023 Phoenix Systems +# +# %LICENSE% +# + +include hal/$(TARGET_SUFF)/gaisler/$(TARGET_SUBFAMILY)/Makefile + +CFLAGS := -Ihal/$(TARGET_SUFF)/gaisler/$(TARGET_SUBFAMILY) $(CFLAGS) + +OBJS += $(addprefix $(PREFIX_O)hal/$(TARGET_SUFF)/gaisler/, console.o timer.o) + +ifeq ($(TARGET_SUBFAMILY), gr712rc) + OBJS += $(addprefix $(PREFIX_O)hal/$(TARGET_SUFF)/gaisler/, irqmp.o) +else ifeq ($(TARGET_SUBFAMILY), gr716) + OBJS += $(addprefix $(PREFIX_O)hal/$(TARGET_SUFF)/gaisler/, irqamp.o) +endif diff --git a/hal/sparcv8leon3/gr716/console.c b/hal/sparcv8leon3/gaisler/console.c similarity index 73% rename from hal/sparcv8leon3/gr716/console.c rename to hal/sparcv8leon3/gaisler/console.c index 74aea245e..d08f584b2 100644 --- a/hal/sparcv8leon3/gr716/console.c +++ b/hal/sparcv8leon3/gaisler/console.c @@ -17,11 +17,18 @@ #include "hal/console.h" #include "hal/cpu.h" #include "include/arch/gr716.h" -#include "gr716.h" +#include "gaisler.h" #include +extern unsigned int _end; + + +#define CONCAT_(a, b) a##b +#define CONCAT(a, b) CONCAT_(a, b) + + /* UART control bits */ #define TX_EN (1 << 1) @@ -29,12 +36,17 @@ #define TX_FIFO_FULL (1 << 9) /* Console config */ -#define CONSOLE_RX UART2_RX -#define CONSOLE_TX UART2_TX -#define CONSOLE_BASE UART2_BASE -#define CONSOLE_CGU cgudev_apbuart2 +#define CONSOLE_RX CONCAT(UART, CONCAT(UART_CONSOLE_KERNEL, _RX)) +#define CONSOLE_TX CONCAT(UART, CONCAT(UART_CONSOLE_KERNEL, _TX)) +#define CONSOLE_CGU CONCAT(cgudev_apbuart, UART_CONSOLE_KERNEL) #define CONSOLE_BAUDRATE UART_BAUDRATE +#ifdef NOMMU +#define VADDR_CONSOLE CONCAT(UART, CONCAT(UART_CONSOLE_KERNEL, _BASE)) +#else +#define VADDR_CONSOLE (void *)((u32)VADDR_PERIPH_BASE + PAGE_OFFS_CONSOLE) +#endif + enum { uart_data, /* Data register : 0x00 */ @@ -97,10 +109,12 @@ void hal_consolePrint(int attr, const char *s) void _hal_consoleInit(void) { - _gr716_setIomuxCfg(CONSOLE_TX, 0x1, 0, 0); - _gr716_setIomuxCfg(CONSOLE_RX, 0x1, 0, 0); + gaisler_setIomuxCfg(CONSOLE_TX, 0x1, 0, 0); + gaisler_setIomuxCfg(CONSOLE_RX, 0x1, 0, 0); +#ifdef __CPU_GR716 _gr716_cguClkEnable(cgu_primary, CONSOLE_CGU); - halconsole_common.uart = CONSOLE_BASE; +#endif + halconsole_common.uart = VADDR_CONSOLE; *(halconsole_common.uart + uart_ctrl) = TX_EN; *(halconsole_common.uart + uart_scaler) = _hal_consoleCalcScaler(CONSOLE_BAUDRATE); hal_cpuDataStoreBarrier(); diff --git a/hal/sparcv8leon3/gaisler/gaisler.h b/hal/sparcv8leon3/gaisler/gaisler.h new file mode 100644 index 000000000..7b0bbae32 --- /dev/null +++ b/hal/sparcv8leon3/gaisler/gaisler.h @@ -0,0 +1,29 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * Gaisler CPU specific functions + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _GAISLER_H_ +#define _GAISLER_H_ + +#include +#include + + +int gaisler_setIomuxCfg(u8 pin, u8 opt, u8 pullup, u8 pulldn); + + +void gaisler_cpuHalt(void); + + +#endif diff --git a/hal/sparcv8leon3/gaisler/gr712rc/Makefile b/hal/sparcv8leon3/gaisler/gr712rc/Makefile new file mode 100644 index 000000000..7b91d340d --- /dev/null +++ b/hal/sparcv8leon3/gaisler/gr712rc/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for Phoenix-RTOS kernel (sparcv8leon3-gr712rc HAL) +# +# Copyright 2023 Phoenix Systems +# + +OBJS += $(addprefix $(PREFIX_O)hal/$(TARGET_SUFF)/gaisler/$(TARGET_SUBFAMILY)/, gr712rc.o _init.o) diff --git a/hal/sparcv8leon3/gaisler/gr712rc/_init.S b/hal/sparcv8leon3/gaisler/gr712rc/_init.S new file mode 100644 index 000000000..d963a2c70 --- /dev/null +++ b/hal/sparcv8leon3/gaisler/gr712rc/_init.S @@ -0,0 +1,222 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * Low level initialization + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#define __ASSEMBLY__ + +#include "config.h" +#include +#include + +#include "hal/sparcv8leon3/srmmu.h" + +#define ADDR_SRAM 0x40000000 + +#define PHY_ADDR(va) ((va) - VADDR_KERNEL + ADDR_SRAM) + +#define ADDR_CTXTAB PHY_ADDR(pmap_common) +#define ADDR_PDIR1 (ADDR_CTXTAB + 0x400) +#define ADDR_PDIR2 (ADDR_PDIR1 + 0x400) +#define ADDR_PDIR3 (ADDR_CTXTAB + 0x1000) + +#define VADDR_SYSPAGE (_end + SIZE_PAGE - 1) +#define PADDR_SYSPAGE PHY_ADDR(VADDR_SYSPAGE) +#define VADDR_PERIPH_BASE (_end + 2 * SIZE_PAGE - 1) +#define PADDR_PERIPH_BASE 0x80000000 + +#define VADDR_STACK (pmap_common + 7 * SIZE_PAGE) + + +.extern syspage +.extern _end + + +.macro calc_ptd paddr out + srl \paddr, 6, \out + sll \out, 2, \out + or \out, PAGE_DESCR, \out +.endm + + +.macro set_page_align addr out + sethi %hi(\addr), \out + srl \out, 12, \out + sll \out, 12, \out +.endm + + +.section ".text" +.align 4 +.global _init +_init: + wr %g0, %wim + nop + nop + nop + + wr %g0, PSR_S, %psr + + /* %g2 = syspage pa (from plo) */ + set PHY_ADDR(syspage), %g3 + set_page_align VADDR_SYSPAGE, %g1 + + /* store VADDR_SYSPAGE in syspage */ + st %g1, [%g3] + + set PHY_ADDR(relOffs), %g4 + /* %l0 = offset between VADDR_SYSPAGE and syspage pa */ + sub %g1, %g2, %l0 + st %l0, [%g4] + + set_page_align PADDR_SYSPAGE, %g3 + /* calculate pa of syspage end */ + ld [%g2 + 4], %l1 /* %l1 = syspage->size */ + add %g2, %l1, %l2 /* %l2 = plo syspage end */ + +syspage_cpy: + ld [%g2], %l0 + st %l0, [%g3] + add %g2, 4, %g2 + cmp %g2, %l2 + bl syspage_cpy + add %g3, 4, %g3 + + /* Flush TLB, I and D cache */ + sta %g0, [%g0] ASI_FLUSH_ALL + + /* Clear page tables */ + set ADDR_CTXTAB, %g2 /* %g2 = &pmap_common.pdir1 (phy) */ + set 0x5000, %g3 + clr %g1 + +clear_pdirs: + subcc %g3, 8, %g3 + std %g0, [%g2] + bnz clear_pdirs + add %g2, 8, %g2 + + /* Set context table pointer */ + set ADDR_CTXTAB, %g3 + set ADDR_PDIR1, %g4 + calc_ptd %g4, %g4 + st %g4, [%g3] + srl %g3, 6, %g3 + sll %g3, 2, %g3 + set MMU_CTX_PTR, %g4 + sta %g3, [%g4] ASI_MMU_REGS + + /* Choose context 0 */ + set MMU_CTX, %g3 + sta %g0, [%g3] ASI_MMU_REGS + + /* Set up page table level 1 */ + + set ADDR_PDIR1, %g1 /* %g1 = pmap_common.pdir1 */ + set ADDR_PDIR2, %g3 /* %g3 = pmap_common.pdir2 */ + + /* V 0x40000000 -> P 0x40000000 */ + sethi %hi(ADDR_SRAM), %g2 + srl %g2, 24, %g2 + sll %g2, 2, %g2 + add %g1, %g2, %g4 /* %g4 = &pmap_common.pdir1[(ADDR_SRAM >> 24)] */ + calc_ptd %g3, %g5 + st %g5, [%g4] /* pmap_common.pdir1[(ADDR_SRAM >> 24)] = PTD(&pmap_common.pdir2[0]) */ + + /* V 0xc0000000 -> P 0x40000000 */ + sethi %hi(VADDR_KERNEL), %g2 + srl %g2, 24, %g2 + sll %g2, 2, %g2 + /* %g1 = &pmap_common.pdir1 */ + add %g1, %g2, %g4 /* %g4 = &pmap_common.pdir1[(VADDR_KERNEL >> 24)] */ + st %g5, [%g4] /* pmap_common.pdir1[(VADDR_KERNEL >> 24)] = PTD(&pmap_common.pdir2[0]) */ + + /* Setup page tables level 2 & 3 */ + + set ADDR_PDIR2, %g1 /* %g1 = pmap_common.pdir2 */ + set ADDR_PDIR3, %g2 /* %g2 = pmap_common.pdir3 */ + sethi %hi(0x40000), %g3 /* 0x40000 = 256KB */ + sethi %hi(ADDR_SRAM), %g6 + or %g1, 0x100, %g5 /* end = 64 entries */ + set 0x1000, %l0 + + /* for (int i = 0; i < 64; i++) { + * pmap_common.pdir2[i] = PTD(&pmap_common.pdir3[i][0]); + * for (int j = 0; j < 64; j++) + * pmap_common.pdir3[i][j] = PTE(0x40000000 + (i * 256KB) + (j * 4KB)); + * } + */ + +set_pdir2: + add %g6, %g3, %g7 + calc_ptd %g2, %l2 + st %l2, [%g1] /* pmap_common.pdir2[i] = &pmap_common.pdir3[i][0] */ +set_pdir3: + srl %g6, 12, %l1 + sll %l1, 8, %l1 + or %l1, ((1 << 7) | (0x7 << 2) | PAGE_ENTRY), %l1 /* cacheable, supervisor RWX */ + st %l1, [%g2] + add %g6, %l0, %g6 + cmp %g6, %g7 + bne set_pdir3 + add %g2, 4, %g2 + + add %g1, 4, %g1 + cmp %g1, %g5 + bne set_pdir2 + nop + + /* Map peripherals */ + sethi %hi(VADDR_PERIPH_BASE), %g1 + set PADDR_PERIPH_BASE, %g2 + set ADDR_PDIR3, %g3 + + srl %g1, 12, %g1 + and %g1, 0x3f, %g1 + sll %g1, 2, %g1 /* pdir3 offs */ + add %g3, %g1, %g1 /* %g1 = &pmap_common.pdir3[0][pdir3 offs] */ + + /* calculate PTE */ + srl %g2, 12, %g2 + sll %g2, 8, %g2 + or %g2, ((0x5 << 2) | PAGE_ENTRY), %g2 /* non-cachable, supervisor RW */ + st %g2, [%g1] + + /* Enable MMU */ + mov 0x1, %g1 + sta %g1, [%g0] ASI_MMU_REGS + + /* Set up trap table */ + sethi %hi(_trap_table), %g1 + wr %g1, %tbr + + /* Set PSR to "supervisor", enable traps, disable interrupts, set CWP to 0 */ + mov %psr, %g1 + or %g1, (PSR_ET | PSR_S | PSR_PIL), %g1 + andn %g1, (PSR_CWP), %g1 + wr %g1, %psr + nop + nop + nop + + wr %g0, 0x2, %wim + + /* Set stack pointer */ + clr %fp + set VADDR_STACK, %sp + sub %sp, 0x60, %sp + + set main, %g1 + call %g1 + mov %g0, %g1 +.size _init, . - _init diff --git a/hal/sparcv8leon3/gaisler/gr712rc/config.h b/hal/sparcv8leon3/gaisler/gr712rc/config.h new file mode 100644 index 000000000..c3f0ef42f --- /dev/null +++ b/hal/sparcv8leon3/gaisler/gr712rc/config.h @@ -0,0 +1,54 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * Configuration file for sparcv8leon3-gr712rc + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _HAL_CONFIG_H_ +#define _HAL_CONFIG_H_ + + +#ifndef __ASSEMBLY__ + + +#include "gr712rc.h" + +#include "include/arch/syspage-sparcv8leon3.h" +#include "include/syspage.h" + +#define HAL_NAME_PLATFORM "SPARCv8 LEON3-GR712RC" + +#define SIZE_EXTEND_BSS (2 * SIZE_PAGE) + +#define ADDR_SRAM 0x40000000 +#define SIZE_SRAM (128 * 1024 * 1024) /* 128 MB */ + +extern unsigned int _end; + +#define VADDR_PERIPH_BASE (void *)(((u32)(&_end) + 2 * SIZE_PAGE - 1) & ~(SIZE_PAGE - 1)) + +/* Peripherals' offsets */ + +#define PAGE_OFFS_CONSOLE 0x100 +#define PAGE_OFFS_INT_CTRL 0x200 +#define PAGE_OFFS_GPTIMER0 0x300 +#define PAGE_OFFS_CGU 0xd00 + + +#endif /* __ASSEMBLY__ */ + + +#define NWINDOWS 8 +#define NUM_CPUS 2 + + +#endif diff --git a/hal/sparcv8leon3/gaisler/gr712rc/gr712rc.c b/hal/sparcv8leon3/gaisler/gr712rc/gr712rc.c new file mode 100644 index 000000000..c12638ed2 --- /dev/null +++ b/hal/sparcv8leon3/gaisler/gr712rc/gr712rc.c @@ -0,0 +1,168 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * HAL internal functions for sparcv8leon3-gr712rc + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include "hal/cpu.h" +#include "hal/sparcv8leon3/sparcv8leon3.h" + +#include "include/arch/gr712rc.h" + +#include "config.h" + +/* Clock gating unit */ + +#define VADDR_CGU (void *)((u32)VADDR_PERIPH_BASE + PAGE_OFFS_CGU) + +#define CGU_UNLOCK 0 /* Unlock register : 0x00 */ +#define CGU_CLK_EN 1 /* Clock enable register : 0x04 */ +#define CGU_CORE_RESET 2 /* Core reset register : 0x08 */ + + +static struct { + spinlock_t pltctlSp; + volatile u32 *cguBase; +} gr712rc_common; + + +void hal_cpuHalt(void) +{ + /* GR712RC errata 1.7.8 */ + u32 addr = 0xfffffff0u; + + /* clang-format off */ + + __asm__ volatile( + "wr %%g0, %%asr19\n\t" + "lda [%0] %c1, %%g0\n\t" + : + : "r"(addr), "i"(ASI_MMU_BYPASS) + ); + /* clang-format on */ +} + + +int gaisler_setIomuxCfg(u8 pin, u8 opt, u8 pullup, u8 pulldn) +{ + (void)pin; + (void)opt; + (void)pullup; + (void)pulldn; + + return 0; +} + +/* CGU setup - section 28.2 GR712RC manual */ + +void _gr712rc_cguClkEnable(u32 device) +{ + u32 msk = 1 << device; + + *(gr712rc_common.cguBase + CGU_UNLOCK) |= msk; + hal_cpuDataStoreBarrier(); + *(gr712rc_common.cguBase + CGU_CORE_RESET) |= msk; + *(gr712rc_common.cguBase + CGU_CLK_EN) |= msk; + *(gr712rc_common.cguBase + CGU_CORE_RESET) &= ~msk; + hal_cpuDataStoreBarrier(); + *(gr712rc_common.cguBase + CGU_UNLOCK) &= ~msk; +} + + +void _gr712rc_cguClkDisable(u32 device) +{ + u32 msk = 1 << device; + + *(gr712rc_common.cguBase + CGU_UNLOCK) |= msk; + hal_cpuDataStoreBarrier(); + *(gr712rc_common.cguBase + CGU_CORE_RESET) |= msk; + *(gr712rc_common.cguBase + CGU_CLK_EN) &= ~msk; + hal_cpuDataStoreBarrier(); + *(gr712rc_common.cguBase + CGU_UNLOCK) &= ~msk; +} + + +int _gr712rc_cguClkStatus(u32 device) +{ + u32 msk = 1 << device; + + return (*(gr712rc_common.cguBase + CGU_CLK_EN) & msk) ? 1 : 0; +} + + +void hal_wdgReload(void) +{ +} + + +int hal_platformctl(void *ptr) +{ + platformctl_t *data = (platformctl_t *)ptr; + spinlock_ctx_t sc; + int ret = -1; + + hal_spinlockSet(&gr712rc_common.pltctlSp, &sc); + + switch (data->type) { + case pctl_cguctrl: + if (data->action == pctl_set) { + if (data->cguctrl.state == disable) { + _gr712rc_cguClkDisable(data->cguctrl.cgudev); + } + else { + _gr712rc_cguClkEnable(data->cguctrl.cgudev); + } + ret = 0; + } + else if (data->action == pctl_get) { + data->cguctrl.stateVal = _gr712rc_cguClkStatus(data->cguctrl.cgudev); + ret = 0; + } + break; + + case pctl_iomux: + if (data->action == pctl_set) { + ret = gaisler_setIomuxCfg(data->iocfg.pin, data->iocfg.opt, data->iocfg.pullup, data->iocfg.pulldn); + } + break; + + case pctl_reboot: + if ((data->action == pctl_set) && (data->reboot.magic == PCTL_REBOOT_MAGIC)) { + hal_cpuReboot(); + } + break; + + default: + break; + } + hal_spinlockClear(&gr712rc_common.pltctlSp, &sc); + + return ret; +} + + +void hal_cpuReboot(void) +{ + /* TODO */ + for (;;) { + } + + __builtin_unreachable(); +} + + +void _hal_platformInit(void) +{ + hal_spinlockCreate(&gr712rc_common.pltctlSp, "pltctl"); + + gr712rc_common.cguBase = VADDR_CGU; +} diff --git a/hal/sparcv8leon3/gaisler/gr712rc/gr712rc.h b/hal/sparcv8leon3/gaisler/gr712rc/gr712rc.h new file mode 100644 index 000000000..48c1cf5e3 --- /dev/null +++ b/hal/sparcv8leon3/gaisler/gr712rc/gr712rc.h @@ -0,0 +1,72 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * HAL internal functions for sparcv8leon3-gr712rc + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _HAL_GR716_H_ +#define _HAL_GR716_H_ + + +#ifndef __ASSEMBLY__ + + +#include +#include + +#include "hal/sparcv8leon3/gaisler/gaisler.h" + +/* Timer registers */ + +#define GPT_SCALER 0 /* Scaler value register : 0x00 */ +#define GPT_SRELOAD 1 /* Scaler reload value register : 0x04 */ +#define GPT_CONFIG 2 /* Configuration register : 0x08 */ + +#define GPT_TCNTVAL1 4 /* Timer 1 counter value reg : 0x10 */ +#define GPT_TRLDVAL1 5 /* Timer 1 reload value reg : 0x14 */ +#define GPT_TCTRL1 6 /* Timer 1 control register : 0x18 */ + +#define GPT_TCNTVAL2 8 /* Timer 2 counter value reg : 0x20 */ +#define GPT_TRLDVAL2 9 /* Timer 2 reload value reg : 0x24 */ +#define GPT_TCTRL2 10 /* Timer 2 control register : 0x28 */ + +#define GPT_TCNTVAL3 12 /* Timer 3 counter value reg : 0x30 */ +#define GPT_TRLDVAL3 13 /* Timer 3 reload value reg : 0x34 */ +#define GPT_TCTRL3 14 /* Timer 3 control register : 0x38 */ + +#define GPT_TCNTVAL4 16 /* Timer 4 counter value reg : 0x40 */ +#define GPT_TRLDVAL4 17 /* Timer 4 reload value reg : 0x44 */ +#define GPT_TCTRL4 18 /* Timer 4 control register : 0x48 */ + + +int gaisler_setIomuxCfg(u8 pin, u8 opt, u8 pullup, u8 pulldn); + + +void _gr712rc_cguClkEnable(u32 device); + + +void _gr712rc_cguClkDisable(u32 device); + + +int _gr712rc_cguClkStatus(u32 device); + + +int hal_platformctl(void *ptr); + + +void _hal_platformInit(void); + + +#endif /* __ASSEMBLY__ */ + + +#endif diff --git a/hal/sparcv8leon3/gr716/Makefile b/hal/sparcv8leon3/gaisler/gr716/Makefile similarity index 50% rename from hal/sparcv8leon3/gr716/Makefile rename to hal/sparcv8leon3/gaisler/gr716/Makefile index 2993147ee..3cd0e8420 100644 --- a/hal/sparcv8leon3/gr716/Makefile +++ b/hal/sparcv8leon3/gaisler/gr716/Makefile @@ -4,4 +4,4 @@ # Copyright 2022 Phoenix Systems # -OBJS += $(addprefix $(PREFIX_O)hal/sparcv8leon3/gr716/, console.o gr716.o timer.o) +OBJS += $(addprefix $(PREFIX_O)hal/$(TARGET_SUFF)/gaisler/$(TARGET_SUBFAMILY)/, gr716.o _init.o) diff --git a/hal/sparcv8leon3/_init.S b/hal/sparcv8leon3/gaisler/gr716/_init.S similarity index 94% rename from hal/sparcv8leon3/_init.S rename to hal/sparcv8leon3/gaisler/gr716/_init.S index 4fe86e7c4..cc966a450 100644 --- a/hal/sparcv8leon3/_init.S +++ b/hal/sparcv8leon3/gaisler/gr716/_init.S @@ -34,6 +34,9 @@ _init: sethi %hi(syspage), %g1 st %g2, [%g1 + %lo(syspage)] + sethi %hi(relOffs), %g1 + st %g0, [%g1 + %lo(relOffs)] + /* Set PSR to "supervisor", enable traps, disable interrupts, set CWP to 0 */ mov %psr, %g1 or %g1, (PSR_ET | PSR_S | PSR_PIL), %g1 diff --git a/hal/sparcv8leon3/gr716/config.h b/hal/sparcv8leon3/gaisler/gr716/config.h similarity index 87% rename from hal/sparcv8leon3/gr716/config.h rename to hal/sparcv8leon3/gaisler/gr716/config.h index 407a9b223..48d7c7c3b 100644 --- a/hal/sparcv8leon3/gr716/config.h +++ b/hal/sparcv8leon3/gaisler/gr716/config.h @@ -31,8 +31,8 @@ #endif /* __ASSEMBLY__ */ -#define NWINDOWS 31 -#define INT_CTRL_BASE ((void *)0x80002000) +#define NWINDOWS 31 +#define NUM_CPUS 1 #endif diff --git a/hal/sparcv8leon3/gr716/gr716.c b/hal/sparcv8leon3/gaisler/gr716/gr716.c similarity index 87% rename from hal/sparcv8leon3/gr716/gr716.c rename to hal/sparcv8leon3/gaisler/gr716/gr716.c index 575c7640d..47637b125 100644 --- a/hal/sparcv8leon3/gr716/gr716.c +++ b/hal/sparcv8leon3/gaisler/gr716/gr716.c @@ -20,10 +20,13 @@ #include "gr716.h" -#define GRGPREG_BASE ((void *)0x8000D000) +#define GRGPREG_BASE ((void *)0x8000d000) #define CGU_BASE0 ((void *)0x80006000) #define CGU_BASE1 ((void *)0x80007000) +#define BOOTSTRAP_ADDR 0x80008000 +#define BOOTSTRAP_SPIM 0x400bc003 + /* System configuration registers */ @@ -66,6 +69,13 @@ struct { } gr716_common; +void hal_cpuHalt(void) +{ + /* must be performed in supervisor mode with int enabled */ + __asm__ volatile("wr %g0, %asr19"); +} + + int _gr716_getIomuxCfg(u8 pin, u8 *opt, u8 *pullup, u8 *pulldn) { if (pin > 63) { @@ -82,7 +92,7 @@ int _gr716_getIomuxCfg(u8 pin, u8 *opt, u8 *pullup, u8 *pulldn) } -int _gr716_setIomuxCfg(u8 pin, u8 opt, u8 pullup, u8 pulldn) +int gaisler_setIomuxCfg(u8 pin, u8 opt, u8 pullup, u8 pulldn) { volatile u32 old_cfg; @@ -182,7 +192,7 @@ int hal_platformctl(void *ptr) case pctl_iomux: if (data->action == pctl_set) { - ret = _gr716_setIomuxCfg(data->iocfg.pin, data->iocfg.opt, data->iocfg.pullup, data->iocfg.pulldn); + ret = gaisler_setIomuxCfg(data->iocfg.pin, data->iocfg.opt, data->iocfg.pullup, data->iocfg.pulldn); } else if (data->action == pctl_get) { ret = _gr716_getIomuxCfg(data->iocfg.pin, &data->iocfg.opt, &data->iocfg.pullup, &data->iocfg.pulldn); @@ -204,6 +214,26 @@ int hal_platformctl(void *ptr) } +void hal_cpuReboot(void) +{ + /* Reset to the built-in bootloader */ + hal_cpuDisableInterrupts(); + + /* Reboot to SPIM */ + *(volatile u32 *)(BOOTSTRAP_ADDR) = BOOTSTRAP_SPIM; + + /* clang-format off */ + __asm__ volatile ( + "jmp %%g0\n\t" + "nop\n\t" + ::: + ); + /* clang-format on */ + + __builtin_unreachable(); +} + + void _hal_platformInit(void) { hal_spinlockCreate(&gr716_common.pltctlSp, "pltctl"); diff --git a/hal/sparcv8leon3/gaisler/gr716/gr716.h b/hal/sparcv8leon3/gaisler/gr716/gr716.h new file mode 100644 index 000000000..58de61bb3 --- /dev/null +++ b/hal/sparcv8leon3/gaisler/gr716/gr716.h @@ -0,0 +1,88 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * HAL internal functions for sparcv8leon3-gr716 + * + * Copyright 2022 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _HAL_GR716_H_ +#define _HAL_GR716_H_ + + +#ifndef __ASSEMBLY__ + + +#include +#include + +#include "hal/sparcv8leon3/gaisler/gaisler.h" + + +#define TIMER_IRQ 9 + +/* Timer registers */ + +#define GPT_SCALER 0 /* Scaler value register : 0x00 */ +#define GPT_SRELOAD 1 /* Scaler reload value register : 0x04 */ +#define GPT_CONFIG 2 /* Configuration register : 0x08 */ +#define GPT_LATCHCFG 3 /* Latch configuration register : 0x0C */ +#define GPT_TCNTVAL1 4 /* Timer 1 counter value reg : 0x10 */ +#define GPT_TRLDVAL1 5 /* Timer 1 reload value reg : 0x14 */ +#define GPT_TCTRL1 6 /* Timer 1 control register : 0x18 */ +#define GPT_TLATCH1 7 /* Timer 1 latch register : 0x1C */ +#define GPT_TCNTVAL2 8 /* Timer 2 counter value reg : 0x20 */ +#define GPT_TRLDVAL2 9 /* Timer 2 reload value reg : 0x24 */ +#define GPT_TCTRL2 10 /* Timer 2 control register : 0x28 */ +#define GPT_TLATCH2 11 /* Timer 2 latch register : 0x2C */ +#define GPT_TCNTVAL3 12 /* Timer 3 counter value reg : 0x30 */ +#define GPT_TRLDVAL3 13 /* Timer 3 reload value reg : 0x34 */ +#define GPT_TCTRL3 14 /* Timer 3 control register : 0x38 */ +#define GPT_TLATCH3 15 /* Timer 3 latch register : 0x3C */ +#define GPT_TCNTVAL4 16 /* Timer 4 counter value reg : 0x40 */ +#define GPT_TRLDVAL4 17 /* Timer 4 reload value reg : 0x44 */ +#define GPT_TCTRL4 18 /* Timer 4 control register : 0x48 */ +#define GPT_TLATCH4 19 /* Timer 4 latch register : 0x4C */ +#define GPT_TCNTVAL5 20 /* Timer 5 counter value reg : 0x50 */ +#define GPT_TRLDVAL5 21 /* Timer 5 reload value reg : 0x54 */ +#define GPT_TCTRL5 22 /* Timer 5 control register : 0x58 */ +#define GPT_TLATCH5 23 /* Timer 5 latch register : 0x5C */ +#define GPT_TCNTVAL6 24 /* Timer 6 counter value reg : 0x60 */ +#define GPT_TRLDVAL6 25 /* Timer 6 reload value reg : 0x64 */ +#define GPT_TCTRL6 26 /* Timer 6 control register : 0x68 */ +#define GPT_TLATCH6 27 /* Timer 6 latch register : 0x6C */ +#define GPT_TCNTVAL7 28 /* Timer 7 counter value reg : 0x70 */ +#define GPT_TRLDVAL7 29 /* Timer 7 reload value reg : 0x74 */ +#define GPT_TCTRL7 30 /* Timer 7 control register : 0x78 */ +#define GPT_TLATCH7 31 /* Timer 7 latch register : 0x7C */ + + +extern int _gr716_getIomuxCfg(u8 pin, u8 *opt, u8 *pullup, u8 *pulldn); + + +extern void _gr716_cguClkEnable(u32 cgu, u32 device); + + +extern void _gr716_cguClkDisable(u32 cgu, u32 device); + + +extern int _gr716_cguClkStatus(u32 cgu, u32 device); + + +extern int hal_platformctl(void *ptr); + + +extern void _hal_platformInit(void); + + +#endif /* __ASSEMBLY__ */ + + +#endif diff --git a/hal/sparcv8leon3/gaisler/grlib-tn-0018.h b/hal/sparcv8leon3/gaisler/grlib-tn-0018.h new file mode 100644 index 000000000..d5c77da55 --- /dev/null +++ b/hal/sparcv8leon3/gaisler/grlib-tn-0018.h @@ -0,0 +1,55 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * GRLIB-TN-0018 Errata + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifdef LEON3_TN_0018_FIX + + +/* LEON3 Cache controller register - ASI 2 */ +#define ASI_CCTRL 0x2 +#define CCTRL_IP_BIT 15 /* ICache flush pending bit */ +#define CCTRL_ICS 0x3 /* ICache state */ + +/* clang-format off */ + +#define TN_0018_WAIT_ICACHE(out1, out2) \ +1: \ + /* Wait for ICache flush to complete */; \ + lda [%g0] ASI_CCTRL, out1; \ + srl out1, CCTRL_IP_BIT, out2; \ + andcc out2, 1, %g0; \ + bne 1b; \ + andn out1, CCTRL_ICS, out2 + + +#define TN_0018_FIX(in1, in2) \ +.align 0x20 /* Align sta for performance */; \ + sta in2, [%g0] ASI_CCTRL /* Disable ICache */; \ + nop /* Delay */; \ + or %l1, %l1, %l1 /* Delay + catch rf parity error on l1 */; \ + or %l2, %l2, %l2 /* Delay + catch rf parity error on l2 */; \ + sta in1, [%g0] ASI_CCTRL /* Re-enable ICache after rett */; \ + nop /* Delay ensures insn after gets cached */ + + +#else + + +#define TN_0018_WAIT_ICACHE(out1, out2) + +#define TN_0018_FIX(in1, in2) + +/* clang-format on */ + +#endif diff --git a/hal/sparcv8leon3/interrupts.c b/hal/sparcv8leon3/gaisler/irqamp.c similarity index 93% rename from hal/sparcv8leon3/interrupts.c rename to hal/sparcv8leon3/gaisler/irqamp.c index 0ee5daba5..01c8fbac1 100644 --- a/hal/sparcv8leon3/interrupts.c +++ b/hal/sparcv8leon3/gaisler/irqamp.c @@ -3,7 +3,7 @@ * * Operating system kernel * - * Interrupt handling for sparcv8leon3 + * Interrupt handling - IRQAMP controller * * Copyright 2022 Phoenix Systems * Author: Lukasz Leczkowski @@ -18,7 +18,17 @@ #include "hal/interrupts.h" #include "proc/userintr.h" -#include +#include "config.h" + + +extern unsigned int _end; + + +#ifdef NOMMU +#define VADDR_INT_CTRL INT_CTRL_BASE +#else +#define VADDR_INT_CTRL (void *)((u32)VADDR_PERIPH_BASE + PAGE_OFFS_INT_CTRL) +#endif #define SIZE_INTERRUPTS 32 #define SIZE_HANDLERS 4 @@ -123,7 +133,7 @@ void interrupts_dispatch(unsigned int n, cpu_context_t *ctx) int reschedule = 0; spinlock_ctx_t sc; - if (n == 1) { + if (n == EXTENDED_IRQN) { /* Extended interrupt (16 - 31) */ n = *(interrupts_common.int_ctrl + pextack) & 0x3F; } @@ -138,7 +148,9 @@ void interrupts_dispatch(unsigned int n, cpu_context_t *ctx) h = interrupts_common.handlers[n]; if (h != NULL) { do { +#ifdef NOMMU hal_cpuSetGot(h->got); +#endif reschedule |= h->f(n, ctx, h->data); h = h->next; } while (h != interrupts_common.handlers[n]); @@ -150,7 +162,7 @@ void interrupts_dispatch(unsigned int n, cpu_context_t *ctx) interrupts_clearIRQ(n); if (n > 15) { /* Extended interrupt sets bit n and bit 1 of pending register */ - interrupts_clearIRQ(1); + interrupts_clearIRQ(EXTENDED_IRQN); } hal_spinlockClear(&interrupts_common.spinlocks[n], &sc); } @@ -177,7 +189,9 @@ int hal_interruptsSetHandler(intr_handler_t *h) } hal_spinlockSet(&interrupts_common.spinlocks[h->n], &sc); +#ifdef NOMMU h->got = hal_cpuGetGot(); +#endif _intr_add(&interrupts_common.handlers[h->n], h); interrupts_enableIRQ(h->n); hal_spinlockClear(&interrupts_common.spinlocks[h->n], &sc); @@ -225,5 +239,5 @@ void _hal_interruptsInit(void) interrupts_common.counters[i] = 0; } - interrupts_common.int_ctrl = (void *)INT_CTRL_BASE; + interrupts_common.int_ctrl = (void *)VADDR_INT_CTRL; } diff --git a/hal/sparcv8leon3/gaisler/irqmp.c b/hal/sparcv8leon3/gaisler/irqmp.c new file mode 100644 index 000000000..6e05b146f --- /dev/null +++ b/hal/sparcv8leon3/gaisler/irqmp.c @@ -0,0 +1,207 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * Interrupt handling - IRQMP controller + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include "hal/cpu.h" +#include "hal/spinlock.h" +#include "hal/interrupts.h" +#include "proc/userintr.h" + +#include "config.h" + + +extern unsigned int _end; + + +#ifdef NOMMU +#define VADDR_INT_CTRL INT_CTRL_BASE +#else +#define VADDR_INT_CTRL (void *)((u32)VADDR_PERIPH_BASE + PAGE_OFFS_INT_CTRL) +#endif + +#define SIZE_INTERRUPTS 32 +#define SIZE_HANDLERS 4 + + +#define _intr_add(list, t) \ + do { \ + if (t == NULL) \ + break; \ + if (*list == NULL) { \ + t->next = t; \ + t->prev = t; \ + (*list) = t; \ + break; \ + } \ + t->prev = (*list)->prev; \ + (*list)->prev->next = t; \ + t->next = (*list); \ + (*list)->prev = t; \ + } while (0) + + +#define _intr_remove(list, t) \ + do { \ + if (t == NULL) \ + break; \ + if ((t->next == t) && (t->prev == t)) \ + (*list) = NULL; \ + else { \ + t->prev->next = t->next; \ + t->next->prev = t->prev; \ + if (t == (*list)) \ + (*list) = t->next; \ + } \ + t->next = NULL; \ + t->prev = NULL; \ + } while (0) + + +/* Interrupt controller */ + +#define INT_LEVEL 0 /* Interrupt level register : 0x00 */ +#define INT_PEND 1 /* Interrupt pending register : 0x04 */ +#define INT_FORCE 2 /* Interrupt force register (CPU 0) : 0x08 */ +#define INT_CLEAR 3 /* Interrupt clear register : 0x0C */ +#define INT_MPSTAT 4 /* Multiprocessor status register : 0x10 */ +#define INT_BRDCAST 5 /* Broadcast register : 0x14 */ +#define INT_MASK_0 16 /* Interrupt mask register (CPU 0) : 0x40 */ +#define INT_MASK_1 17 /* Interrupt mask register (CPU 1) : 0x44 */ +#define INT_FORCE_0 32 /* Interrupt force register (CPU 0) : 0x80 */ +#define INT_FORCE_1 33 /* Interrupt force register (CPU 1) : 0x84 */ +#define INT_EXTID_0 48 /* Extended interrupt ID register (CPU 0) : 0xC0 */ +#define INT_EXTID_1 49 /* Extended interrupt ID register (CPU 1) : 0xC4 */ + + +struct { + volatile u32 *int_ctrl; + spinlock_t spinlocks[SIZE_INTERRUPTS]; + intr_handler_t *handlers[SIZE_INTERRUPTS]; + unsigned int counters[SIZE_INTERRUPTS]; +} interrupts_common; + + +extern int threads_schedule(unsigned int n, cpu_context_t *context, void *arg); + + +void interrupts_dispatch(unsigned int n, cpu_context_t *ctx) +{ + intr_handler_t *h; + int reschedule = 0, cpuID = hal_cpuGetID(); + spinlock_ctx_t sc; + + if (n == EXTENDED_IRQN) { + /* Extended interrupt (16 - 31) */ + n = *(interrupts_common.int_ctrl + INT_EXTID_0 + cpuID) & 0x3F; + } + + if (n >= SIZE_INTERRUPTS) { + return; + } + + hal_spinlockSet(&interrupts_common.spinlocks[n], &sc); + + interrupts_common.counters[n]++; + h = interrupts_common.handlers[n]; + if (h != NULL) { + do { +#ifdef NOMMU + hal_cpuSetGot(h->got); +#endif + reschedule |= h->f(n, ctx, h->data); + h = h->next; + } while (h != interrupts_common.handlers[n]); + } + + if (reschedule != 0) { + threads_schedule(n, ctx, NULL); + } + + hal_spinlockClear(&interrupts_common.spinlocks[n], &sc); +} + + +static void interrupts_enableIRQ(unsigned int irqn) +{ + *(interrupts_common.int_ctrl + INT_MASK_0) |= (1 << irqn); +} + + +static void interrupts_disableIRQ(unsigned int irqn) +{ + *(interrupts_common.int_ctrl + INT_MASK_0) &= ~(1 << irqn); +} + + +int hal_interruptsSetHandler(intr_handler_t *h) +{ + spinlock_ctx_t sc; + + if (h == NULL || h->n >= SIZE_INTERRUPTS) { + return -1; + } + + hal_spinlockSet(&interrupts_common.spinlocks[h->n], &sc); +#ifdef NOMMU + h->got = hal_cpuGetGot(); +#endif + _intr_add(&interrupts_common.handlers[h->n], h); + interrupts_enableIRQ(h->n); + hal_spinlockClear(&interrupts_common.spinlocks[h->n], &sc); + + return 0; +} + + +int hal_interruptsDeleteHandler(intr_handler_t *h) +{ + spinlock_ctx_t sc; + + if (h == NULL || h->n >= SIZE_INTERRUPTS) { + return -1; + } + + hal_spinlockSet(&interrupts_common.spinlocks[h->n], &sc); + _intr_remove(&interrupts_common.handlers[h->n], h); + + if (interrupts_common.handlers[h->n] == NULL) { + interrupts_disableIRQ(h->n); + } + + hal_spinlockClear(&interrupts_common.spinlocks[h->n], &sc); + + return 0; +} + + +char *hal_interruptsFeatures(char *features, unsigned int len) +{ + hal_strncpy(features, "Using IRQMP interrupt controller", len); + features[len - 1] = 0; + + return features; +} + +void _hal_interruptsInit(void) +{ + int i; + + for (i = 0; i < SIZE_INTERRUPTS; ++i) { + hal_spinlockCreate(&interrupts_common.spinlocks[i], "interrupts_common"); + interrupts_common.handlers[i] = NULL; + interrupts_common.counters[i] = 0; + } + + interrupts_common.int_ctrl = (void *)VADDR_INT_CTRL; +} diff --git a/hal/sparcv8leon3/gaisler/timer.c b/hal/sparcv8leon3/gaisler/timer.c new file mode 100644 index 000000000..a48683a45 --- /dev/null +++ b/hal/sparcv8leon3/gaisler/timer.c @@ -0,0 +1,145 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * Timer controller + * + * Copyright 2022 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include +#include "hal/timer.h" +#include "hal/spinlock.h" + +#ifdef NOMMU +#define VADDR_GPTIMER0 GPTIMER0_BASE +#else +#define VADDR_GPTIMER0 (void *)((u32)VADDR_PERIPH_BASE + PAGE_OFFS_GPTIMER0) +#endif + +/* Timer control bitfields */ + +#define TIMER_ENABLE (1 << 0) +#define TIMER_PERIODIC (1 << 1) +#define TIMER_LOAD (1 << 2) +#define TIMER_INT_ENABLE (1 << 3) +#define TIMER_INT_PENDING (1 << 4) +#define TIMER_CHAIN (1 << 5) + + +/* clang-format off */ + +enum { timer1 = 0, timer2, timer3, timer4 }; + +/* clang-format on */ + + +struct { + volatile u32 *timer0_base; + intr_handler_t handler; + volatile time_t jiffies; + spinlock_t sp; + u32 ticksPerFreq; +} timer_common; + + +static int _timer_irqHandler(unsigned int irq, cpu_context_t *ctx, void *data) +{ + volatile u32 st = *(timer_common.timer0_base + GPT_TCTRL1) & TIMER_INT_PENDING; + + if (st != 0) { + ++timer_common.jiffies; + /* Clear irq status */ +#if defined(__CPU_GR712RC) + *(timer_common.timer0_base + GPT_TCTRL1) &= ~TIMER_INT_PENDING; +#else + *(timer_common.timer0_base + GPT_TCTRL1) |= TIMER_INT_PENDING; +#endif + } + + return 0; +} + + +static inline void timer_setReloadValue(int timer, u32 val) +{ + *(timer_common.timer0_base + GPT_TRLDVAL1 + timer * 4) = val; +} + + +static void timer_setPrescaler(int timer, u32 freq) +{ + u32 prescaler = SYSCLK_FREQ / 1000000; /* 1 MHz */ + u32 ticks = (SYSCLK_FREQ / prescaler) / freq; + + timer_setReloadValue(timer, ticks - 1); + *(timer_common.timer0_base + GPT_SRELOAD) = prescaler - 1; + + timer_common.ticksPerFreq = ticks; +} + + +time_t hal_timerGetUs(void) +{ + time_t val; + spinlock_ctx_t sc; + + hal_spinlockSet(&timer_common.sp, &sc); + val = timer_common.jiffies; + hal_spinlockClear(&timer_common.sp, &sc); + + return val * 1000ULL; +} + + +void hal_timerSetWakeup(u32 when) +{ +} + + +int hal_timerRegister(int (*f)(unsigned int, cpu_context_t *, void *), void *data, intr_handler_t *h) +{ + h->f = f; + h->n = TIMER_IRQ; + h->data = data; + + return hal_interruptsSetHandler(h); +} + + +void _hal_timerInit(u32 interval) +{ + timer_common.jiffies = 0; + timer_common.timer0_base = VADDR_GPTIMER0; + + /* Disable timer interrupts - bits cleared when written 1 */ + volatile u32 st = *(timer_common.timer0_base + GPT_TCTRL1) & (TIMER_INT_ENABLE | TIMER_INT_PENDING); + *(timer_common.timer0_base + GPT_TCTRL1) = st; + /* Disable timer */ + *(timer_common.timer0_base + GPT_TCTRL1) = 0; + /* Reset counter and reload value */ + *(timer_common.timer0_base + GPT_TCNTVAL1) = 0; + *(timer_common.timer0_base + GPT_TRLDVAL1) = 0; + + timer_common.handler.f = NULL; + timer_common.handler.n = TIMER_IRQ; + timer_common.handler.data = NULL; + + timer_setPrescaler(timer1, interval); + + hal_spinlockCreate(&timer_common.sp, "timer"); + timer_common.handler.f = _timer_irqHandler; + timer_common.handler.n = TIMER_IRQ; + timer_common.handler.data = NULL; + hal_interruptsSetHandler(&timer_common.handler); + + /* Enable timer and interrupts */ + /* Load reload value into counter register */ + *(timer_common.timer0_base + GPT_TCTRL1) |= TIMER_ENABLE | TIMER_INT_ENABLE | TIMER_LOAD | TIMER_PERIODIC; +} diff --git a/hal/sparcv8leon3/gr716/gr716.h b/hal/sparcv8leon3/gr716/gr716.h deleted file mode 100644 index b986104b0..000000000 --- a/hal/sparcv8leon3/gr716/gr716.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Phoenix-RTOS - * - * Operating system kernel - * - * HAL internal functions for sparcv8leon3-gr716 - * - * Copyright 2022 Phoenix Systems - * Author: Lukasz Leczkowski - * - * This file is part of Phoenix-RTOS. - * - * %LICENSE% - */ - -#ifndef _HAL_GR716_H_ -#define _HAL_GR716_H_ - - -#ifndef __ASSEMBLY__ - - -#include -#include - - -#define UART0_BASE ((void *)0x80300000) -#define UART1_BASE ((void *)0x80301000) -#define UART2_BASE ((void *)0x80302000) -#define UART3_BASE ((void *)0x80303000) -#define UART4_BASE ((void *)0x80304000) -#define UART5_BASE ((void *)0x80305000) - - -extern int _gr716_getIomuxCfg(u8 pin, u8 *opt, u8 *pullup, u8 *pulldn); - - -extern int _gr716_setIomuxCfg(u8 pin, u8 opt, u8 pullup, u8 pulldn); - - -extern void _gr716_cguClkEnable(u32 cgu, u32 device); - - -extern void _gr716_cguClkDisable(u32 cgu, u32 device); - - -extern int _gr716_cguClkStatus(u32 cgu, u32 device); - - -extern int hal_platformctl(void *ptr); - - -extern void _hal_platformInit(void); - - -#endif /* __ASSEMBLY__ */ - - -#endif diff --git a/hal/sparcv8leon3/gr716/timer.c b/hal/sparcv8leon3/gr716/timer.c deleted file mode 100644 index 93b38eb54..000000000 --- a/hal/sparcv8leon3/gr716/timer.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Phoenix-RTOS - * - * Operating system kernel - * - * Timer controller - * - * Copyright 2022 Phoenix Systems - * Author: Lukasz Leczkowski - * - * This file is part of Phoenix-RTOS. - * - * %LICENSE% - */ - -#include -#include "hal/timer.h" -#include "hal/spinlock.h" - -#define TIMER_IRQ 9 -#define GPTIMER0_BASE ((void *)0x80003000) - -/* Timer control bitfields */ -#define TIMER_ENABLE (1) -#define TIMER_ONESHOT (0 << 1) -#define TIMER_PERIODIC (1 << 1) -#define TIMER_LOAD (1 << 2) -#define TIMER_INT_ENABLE (1 << 3) -#define TIMER_INT_PENDING (1 << 4) -#define TIMER_CHAIN (1 << 5) - - -enum { - gpt_scaler = 0, /* Scaler value register : 0x00 */ - gpt_sreload, /* Scaler reload value register : 0x04 */ - gpt_config, /* Configuration register : 0x08 */ - gpt_latchcfg, /* Latch configuration register : 0x0C */ - gpt_tcntval1, /* Timer 1 counter value reg : 0x10 */ - gpt_trldval1, /* Timer 1 reload value reg : 0x14 */ - gpt_tctrl1, /* Timer 1 control register : 0x18 */ - gpt_tlatch1, /* Timer 1 latch register : 0x1C */ - gpt_tcntval2, /* Timer 2 counter value reg : 0x20 */ - gpt_trldval2, /* Timer 2 reload value reg : 0x24 */ - gpt_tctrl2, /* Timer 2 control register : 0x28 */ - gpt_tlatch2, /* Timer 2 latch register : 0x2C */ - gpt_tcntval3, /* Timer 3 counter value reg : 0x30 */ - gpt_trldval3, /* Timer 3 reload value reg : 0x34 */ - gpt_tctrl3, /* Timer 3 control register : 0x38 */ - gpt_tlatch3, /* Timer 3 latch register : 0x3C */ - gpt_tcntval4, /* Timer 4 counter value reg : 0x40 */ - gpt_trldval4, /* Timer 4 reload value reg : 0x44 */ - gpt_tctrl4, /* Timer 4 control register : 0x48 */ - gpt_tlatch4, /* Timer 4 latch register : 0x4C */ - gpt_tcntval5, /* Timer 5 counter value reg : 0x50 */ - gpt_trldval5, /* Timer 5 reload value reg : 0x54 */ - gpt_tctrl5, /* Timer 5 control register : 0x58 */ - gpt_tlatch5, /* Timer 5 latch register : 0x5C */ - gpt_tcntval6, /* Timer 6 counter value reg : 0x60 */ - gpt_trldval6, /* Timer 6 reload value reg : 0x64 */ - gpt_tctrl6, /* Timer 6 control register : 0x68 */ - gpt_tlatch6, /* Timer 6 latch register : 0x6C */ - gpt_tcntval7, /* Timer 7 counter value reg : 0x70 */ - gpt_trldval7, /* Timer 7 reload value reg : 0x74 */ - gpt_tctrl7, /* Timer 7 control register : 0x78 */ - gpt_tlatch7, /* Timer 7 latch register : 0x7C */ -}; - - -enum { - timer1 = 0, - timer2, - timer3, - timer4, - timer5, - timer6, - timer7 -}; - - -struct { - volatile u32 *timer0_base; - intr_handler_t handler; - volatile time_t jiffies; - spinlock_t sp; - u32 ticksPerFreq; -} timer_common; - - -static int _timer_irqHandler(unsigned int irq, cpu_context_t *ctx, void *data) -{ - volatile u32 st = *(timer_common.timer0_base + gpt_tctrl1) & TIMER_INT_PENDING; - - if (st != 0) { - ++timer_common.jiffies; - /* Clear irq status */ - *(timer_common.timer0_base + gpt_tctrl1) |= TIMER_INT_PENDING; - } - - return 0; -} - - -static inline void timer_setReloadValue(int timer, u32 val) -{ - *(timer_common.timer0_base + gpt_trldval1 + timer * 4) = val; -} - - -static void timer_setPrescaler(int timer, u32 freq) -{ - u32 prescaler = SYSCLK_FREQ / 1000000; /* 1 MHz */ - u32 ticks = (SYSCLK_FREQ / prescaler) / freq; - - timer_setReloadValue(timer, ticks - 1); - *(timer_common.timer0_base + gpt_sreload) = prescaler - 1; - - timer_common.ticksPerFreq = ticks; -} - - -time_t hal_timerGetUs(void) -{ - time_t val; - spinlock_ctx_t sc; - - hal_spinlockSet(&timer_common.sp, &sc); - val = timer_common.jiffies; - hal_spinlockClear(&timer_common.sp, &sc); - - return val * 1000ULL; -} - - -void hal_timerSetWakeup(u32 when) -{ -} - - -int hal_timerRegister(int (*f)(unsigned int, cpu_context_t *, void *), void *data, intr_handler_t *h) -{ - h->f = f; - h->n = TIMER_IRQ; - h->data = data; - - return hal_interruptsSetHandler(h); -} - - -void _hal_timerInit(u32 interval) -{ - timer_common.jiffies = 0; - timer_common.timer0_base = GPTIMER0_BASE; - - /* Disable timer interrupts - bits cleared when written 1 */ - volatile u32 st = *(timer_common.timer0_base + gpt_tctrl1) & (TIMER_INT_ENABLE | TIMER_INT_PENDING); - *(timer_common.timer0_base + gpt_tctrl1) = st; - /* Disable timer */ - *(timer_common.timer0_base + gpt_tctrl1) = 0; - /* Reset counter and reload value */ - *(timer_common.timer0_base + gpt_tcntval1) = 0; - *(timer_common.timer0_base + gpt_trldval1) = 0; - - timer_common.handler.f = NULL; - timer_common.handler.n = TIMER_IRQ; - timer_common.handler.data = NULL; - - timer_setPrescaler(timer1, interval); - - hal_spinlockCreate(&timer_common.sp, "timer"); - timer_common.handler.f = _timer_irqHandler; - timer_common.handler.n = TIMER_IRQ; - timer_common.handler.data = NULL; - hal_interruptsSetHandler(&timer_common.handler); - - /* Enable timer and interrupts */ - /* Load reload value into counter register */ - *(timer_common.timer0_base + gpt_tctrl1) |= TIMER_ENABLE | TIMER_INT_ENABLE | TIMER_LOAD | TIMER_PERIODIC; -} diff --git a/hal/sparcv8leon3/hal.c b/hal/sparcv8leon3/hal.c index 921af635d..6c8f38b9e 100644 --- a/hal/sparcv8leon3/hal.c +++ b/hal/sparcv8leon3/hal.c @@ -28,7 +28,7 @@ extern void _hal_cpuInit(void); void *hal_syspageRelocate(void *data) { - return data; + return ((u8 *)data + relOffs); } diff --git a/hal/sparcv8leon3/halsyspage.h b/hal/sparcv8leon3/halsyspage.h new file mode 100644 index 000000000..33f5107c5 --- /dev/null +++ b/hal/sparcv8leon3/halsyspage.h @@ -0,0 +1,24 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * System information page (prepared by kernel loader) + * + * Copyright 2022 Phoenix Systems + * Author: Hubert Buczynski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _HAL_SYSPAGE_H_ +#define _HAL_SYSPAGE_H_ + +#include +#include "config.h" + +extern syspage_t *syspage; + +#endif diff --git a/hal/sparcv8leon3/pmap-nommu.c b/hal/sparcv8leon3/pmap-nommu.c new file mode 100644 index 000000000..976b9867b --- /dev/null +++ b/hal/sparcv8leon3/pmap-nommu.c @@ -0,0 +1,105 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * pmap - machine dependent part of VM subsystem + * + * Copyright 2022 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#include "hal/pmap.h" +#include + + +/* Linker symbols */ +extern unsigned int _end; +extern unsigned int __bss_start; + +extern void *_init_stack; + + +/* Function creates empty page table */ +int pmap_create(pmap_t *pmap, pmap_t *kpmap, page_t *p, void *vaddr) +{ + return 0; +} + + +addr_t pmap_destroy(pmap_t *pmap, int *i) +{ + return 0; +} + + +void pmap_switch(pmap_t *pmap) +{ + return; +} + + +int pmap_enter(pmap_t *pmap, addr_t pa, void *vaddr, int attr, page_t *alloc) +{ + return 0; +} + + +int pmap_remove(pmap_t *pmap, void *vaddr) +{ + return 0; +} + + +addr_t pmap_resolve(pmap_t *pmap, void *vaddr) +{ + return (addr_t)vaddr; +} + + +int pmap_getPage(page_t *page, addr_t *addr) +{ + return 0; +} + + +char pmap_marker(page_t *p) +{ + return 0; +} + + +int _pmap_kernelSpaceExpand(pmap_t *pmap, void **start, void *end, page_t *dp) +{ + return 0; +} + + +int pmap_segment(unsigned int i, void **vaddr, size_t *size, int *prot, void **top) +{ + if (i != 0) { + return -1; + } + + /* Returns region above basic kernel's .bss section */ + *vaddr = (void *)&_end; + *size = (((size_t)(*top) + SIZE_PAGE - 1) & ~(SIZE_PAGE - 1)) - (size_t)&_end; + + return 0; +} + + +void _pmap_init(pmap_t *pmap, void **vstart, void **vend) +{ + (*vstart) = (void *)(((ptr_t)_init_stack + 7) & ~7); + (*vend) = (*((char **)vstart)) + SIZE_PAGE; + + pmap->start = (void *)&__bss_start; + + /* Initial size of kernel map */ + pmap->end = (void *)((addr_t)&__bss_start + 32 * 1024); +} diff --git a/hal/sparcv8leon3/pmap.c b/hal/sparcv8leon3/pmap.c index 976b9867b..377d45d60 100644 --- a/hal/sparcv8leon3/pmap.c +++ b/hal/sparcv8leon3/pmap.c @@ -13,93 +13,593 @@ * %LICENSE% */ -#include "hal/pmap.h" #include +#include "hal/cpu.h" +#include "hal/pmap.h" +#include "hal/string.h" +#include "hal/spinlock.h" +#include "hal/sparcv8leon3/sparcv8leon3.h" + +#include "include/errno.h" +#include "include/mman.h" + +#include "halsyspage.h" + + +#define MAX_CONTEXTS 256 +#define CONTEXT_INVALID 0xffffffffu +#define CONTEXT_SHARED 255 + +#define PDIR1_IDX(vaddr) ((u32)(vaddr) >> 24) +#define PDIR2_IDX(vaddr) (((u32)(vaddr) >> 18) & 0x3f) +#define PDIR3_IDX(vaddr) (((u32)(vaddr) >> 12) & 0x3f) + +#define UNCACHED 0 +#define CACHED 1 + +/* Page Table Descriptor */ +#define PTD(paddr) ((((u32)(paddr) >> 6) << 2) | PAGE_DESCR) +#define PTD_TO_ADDR(ptd) (((u32)(ptd) >> 2) << 6) +/* Page Table Entry */ +#define PTE(paddr, c, acc, type) ((((u32)(paddr) >> 12) << 8) | ((c & 0x1) << 7) | ((acc & 0x7) << 2) | (type & 0x3)) +#define PTE_TO_ADDR(pte) (((u32)(pte) >> 8) << 12) + +#define CEIL_PAGE(x) (((x) + SIZE_PAGE - 1) & ~(SIZE_PAGE - 1)) + /* Linker symbols */ extern unsigned int _end; +extern unsigned int _etext; extern unsigned int __bss_start; -extern void *_init_stack; + +struct { + /* Order of these field must be strictly preserved */ + u32 ctxTable[256]; + u32 pdir1[256]; + u32 pdir2[64]; + u32 pdir3[64][64] __attribute__((aligned(SIZE_PAGE))); + + u8 heap[SIZE_PAGE] __attribute__((aligned(SIZE_PAGE))); + u8 stack[SIZE_KSTACK] __attribute__((aligned(8))); + u32 ctxMap[MAX_CONTEXTS / 32]; /* Bitmap of context numbers, 0 = taken, 1 = free */ + u32 numCtxFree; + addr_t minAddr; + addr_t maxAddr; + u32 start; + u32 end; + spinlock_t lock; +} pmap_common __attribute__((aligned(SIZE_PAGE))); + + +static u32 pmap_attrToAcc(u32 attr) +{ + u32 acc; + + attr &= 0xf; /* Mask out cache, dev & present bits */ + + if ((attr & PGHD_USER) != 0) { + /* Mask out user bit */ + switch (attr & 0x7) { + case (PGHD_READ): + acc = PERM_USER_RO; + break; + + case (PGHD_WRITE): + case (PGHD_READ | PGHD_WRITE): + acc = PERM_USER_RW; + break; + + case (PGHD_READ | PGHD_EXEC): + acc = PERM_USER_RX; + break; + + case (PGHD_READ | PGHD_WRITE | PGHD_EXEC): + acc = PERM_USER_RWX; + break; + + case (PGHD_EXEC): + acc = PERM_USER_XO; + break; + + default: + acc = PERM_USER_RO; + break; + } + } + else { + switch (attr) { + case (PGHD_READ): + case (PGHD_WRITE): + case (PGHD_READ | PGHD_WRITE): + acc = PERM_SUPER_RW; + break; + + case (PGHD_READ | PGHD_EXEC): + acc = PERM_SUPER_RX; + break; + + case (PGHD_EXEC): + case (PGHD_READ | PGHD_WRITE | PGHD_EXEC): + acc = PERM_SUPER_RWX; + break; + + default: + acc = PERM_SUPER_RW; + break; + } + } + + return acc; +} + + +static u32 _pmap_contextAlloc(void) +{ + u8 ctxId; + + if (pmap_common.numCtxFree != 0) { + for (size_t i = 0; i < (MAX_CONTEXTS / 32); i++) { + if (pmap_common.ctxMap[i] != 0x0) { + ctxId = hal_cpuGetFirstBit(pmap_common.ctxMap[i]); + pmap_common.ctxMap[i] &= ~(1 << ctxId); + pmap_common.numCtxFree--; + return (i * 32) + ctxId; + } + } + } + + return CONTEXT_SHARED; +} + + +static void _pmap_contextDealloc(pmap_t *pmap) +{ + u32 ctxId = pmap->context; + if (ctxId != CONTEXT_SHARED) { + pmap_common.ctxMap[ctxId / 32] |= (1 << (ctxId % 32)); + pmap_common.numCtxFree++; + } + pmap->context = CONTEXT_INVALID; +} + + +static void _pmap_flushTLB(u32 context, void *vaddr) +{ + if (hal_srmmuGetContext() == context) { + if ((ptr_t)vaddr < VADDR_USR_MAX) { + hal_srmmuFlushTLB(vaddr, TLB_FLUSH_L3); + } + else { + hal_srmmuFlushTLB(vaddr, TLB_FLUSH_CTX); + } + } + else { + hal_srmmuFlushTLB(vaddr, TLB_FLUSH_ALL); + } +} /* Function creates empty page table */ int pmap_create(pmap_t *pmap, pmap_t *kpmap, page_t *p, void *vaddr) { + pmap->pdir1 = vaddr; + pmap->context = CONTEXT_INVALID; + hal_memset(pmap->pdir1, 0, 256 * sizeof(u32)); + hal_memcpy( + &pmap->pdir1[PDIR1_IDX(VADDR_KERNEL)], + &kpmap->pdir1[PDIR1_IDX(VADDR_KERNEL)], + (VADDR_MAX - VADDR_KERNEL + 1) >> 24); + return 0; } addr_t pmap_destroy(pmap_t *pmap, int *i) { + const u32 idx1 = PDIR1_IDX(VADDR_USR_MAX); + spinlock_ctx_t sc; + addr_t pdir2, pdir3; + u32 j; + + if (pmap->context != CONTEXT_INVALID) { + hal_spinlockSet(&pmap_common.lock, &sc); + pmap_common.ctxTable[pmap->context] = NULL; + _pmap_contextDealloc(pmap); + hal_spinlockClear(&pmap_common.lock, &sc); + } + + while (*i < idx1) { + pdir2 = PTD_TO_ADDR(pmap->pdir1[*i]); + if (pdir2 != NULL) { + for (j = 0; j < 64; j++) { + pdir3 = PTD_TO_ADDR(hal_cpuLoadPaddr(&((u32 *)pdir2)[j])); + if (pdir3 != NULL) { + hal_cpuStorePaddr(&((u32 *)pdir2)[j], 0); + hal_cpuflushDCache(); + + return pdir3; + } + } + (*i)++; + + return pdir2; + } + (*i)++; + } + return 0; } +static addr_t _pmap_resolve(pmap_t *pmap, void *vaddr) +{ + u32 idx1 = PDIR1_IDX(vaddr), idx2 = PDIR2_IDX(vaddr), idx3 = PDIR3_IDX(vaddr); + addr_t addr; + + addr = PTD_TO_ADDR(pmap->pdir1[idx1]); + if (addr == 0) { + return 0; + } + + addr = PTD_TO_ADDR(hal_cpuLoadPaddr(&((u32 *)addr)[idx2])); + if (addr == 0) { + return 0; + } + + addr = PTE_TO_ADDR(hal_cpuLoadPaddr(&((u32 *)addr)[idx3])); + + return addr; +} + + +/* Functions returns physical address associated with specified virtual address */ +addr_t pmap_resolve(pmap_t *pmap, void *vaddr) +{ + addr_t addr; + spinlock_ctx_t sc; + + hal_spinlockSet(&pmap_common.lock, &sc); + + addr = _pmap_resolve(pmap, vaddr); + + hal_spinlockClear(&pmap_common.lock, &sc); + + return addr; +} + + void pmap_switch(pmap_t *pmap) { + spinlock_ctx_t sc; + addr_t paddr; + + hal_spinlockSet(&pmap_common.lock, &sc); + if ((pmap->context == CONTEXT_INVALID) || ((pmap->context == CONTEXT_SHARED) && (pmap_common.numCtxFree != 0))) { + pmap->context = _pmap_contextAlloc(); + paddr = PTD(_pmap_resolve(pmap, pmap->pdir1) + ((u32)pmap->pdir1 & 0xfff)); + pmap_common.ctxTable[pmap->context] = paddr; + } + + hal_srmmuSetContext(pmap->context); + hal_cpuflushICache(); + hal_cpuflushDCache(); + + if (pmap->context == CONTEXT_SHARED) { + paddr = PTD(_pmap_resolve(pmap, pmap->pdir1) + ((u32)pmap->pdir1 & 0xfff)); + pmap_common.ctxTable[CONTEXT_SHARED] = paddr; + hal_srmmuFlushTLB(0, TLB_FLUSH_CTX); + } + + hal_spinlockClear(&pmap_common.lock, &sc); + return; } +/* Functions maps page at specified address */ int pmap_enter(pmap_t *pmap, addr_t pa, void *vaddr, int attr, page_t *alloc) { - return 0; + u8 idx1 = PDIR1_IDX(vaddr), idx2 = PDIR2_IDX(vaddr), idx3 = PDIR3_IDX(vaddr); + u8 newEntry = 0; + addr_t addr, pdir2; + spinlock_ctx_t sc; + u32 acc = pmap_attrToAcc(attr), entry; + + hal_spinlockSet(&pmap_common.lock, &sc); + + addr = PTD_TO_ADDR(pmap->pdir1[idx1]); + + if (addr == 0) { + /* Allocate PDIR2 */ + if (alloc == NULL) { + hal_srmmuFlushTLB(0, TLB_FLUSH_ALL); + hal_spinlockClear(&pmap_common.lock, &sc); + return -EFAULT; + } + + for (size_t i = 0; i < (SIZE_PAGE / sizeof(u32)); i++) { + hal_cpuStorePaddr((u32 *)alloc->addr + i, 0); + } + hal_cpuflushDCache(); + + pmap->pdir1[idx1] = PTD(alloc->addr); + + addr = PTD_TO_ADDR(pmap->pdir1[idx1]); + + alloc = NULL; + } + + /* addr points to 2nd level table */ + pdir2 = addr; + + /* Check if PDIR3 is allocated */ + addr = PTD_TO_ADDR(hal_cpuLoadPaddr(&((u32 *)addr)[idx2])); + + if (addr == 0) { + /* Allocate PDIR3 */ + if (alloc == NULL) { + hal_srmmuFlushTLB(0, TLB_FLUSH_ALL); + hal_spinlockClear(&pmap_common.lock, &sc); + return -EFAULT; + } + + for (size_t i = 0; i < (SIZE_PAGE / sizeof(u32)); i++) { + hal_cpuStorePaddr((u32 *)alloc->addr + i, 0); + } + hal_cpuflushDCache(); + + hal_cpuStorePaddr(&((u32 *)pdir2)[idx2], PTD(alloc->addr)); + hal_cpuflushDCache(); + + addr = PTD_TO_ADDR(hal_cpuLoadPaddr(&((u32 *)pdir2)[idx2])); + + alloc = NULL; + } + + entry = PTE(pa, ((attr & (PGHD_NOT_CACHED | PGHD_DEV)) != 0) ? UNCACHED : CACHED, acc, ((attr & PGHD_PRESENT) != 0) ? PAGE_ENTRY : 0); + newEntry = ((hal_cpuLoadPaddr(&((u32 *)addr)[idx3]) & 0x3) == PAGE_INVALID) ? 1 : 0; + + hal_cpuStorePaddr(&((u32 *)addr)[idx3], entry); + hal_cpuflushDCache(); + + if (newEntry == 0) { + /* Flush TLB only if entry existed earlier */ + _pmap_flushTLB(pmap->context, vaddr); + } + + hal_spinlockClear(&pmap_common.lock, &sc); + + return EOK; } int pmap_remove(pmap_t *pmap, void *vaddr) { - return 0; -} + u8 idx1 = PDIR1_IDX(vaddr), idx2 = PDIR2_IDX(vaddr), idx3 = PDIR3_IDX(vaddr); + addr_t addr, descr; + spinlock_ctx_t sc; + hal_spinlockSet(&pmap_common.lock, &sc); -addr_t pmap_resolve(pmap_t *pmap, void *vaddr) -{ - return (addr_t)vaddr; + descr = pmap->pdir1[idx1]; + + if ((descr & 0x3) == PAGE_INVALID) { + hal_spinlockClear(&pmap_common.lock, &sc); + return EOK; + } + + addr = PTD_TO_ADDR(descr); + descr = hal_cpuLoadPaddr(&((u32 *)addr)[idx2]); + + if ((descr & 0x3) == PAGE_INVALID) { + hal_spinlockClear(&pmap_common.lock, &sc); + return EOK; + } + + addr = PTD_TO_ADDR(descr); + hal_cpuStorePaddr(&((u32 *)addr)[idx3], 0); + hal_cpuflushDCache(); + + _pmap_flushTLB(pmap->context, vaddr); + + hal_spinlockClear(&pmap_common.lock, &sc); + + return EOK; } int pmap_getPage(page_t *page, addr_t *addr) { - return 0; + addr_t a, end, min, max, ptable, stack; + spinlock_ctx_t sc; + const syspage_prog_t *prog; + + a = (*addr) & ~(SIZE_PAGE - 1); + page->flags = 0; + + hal_spinlockSet(&pmap_common.lock, &sc); + min = pmap_common.minAddr; + max = pmap_common.maxAddr; + hal_spinlockClear(&pmap_common.lock, &sc); + + if (a < min) { + a = min; + } + if (a >= max) { + return -ENOMEM; + } + + page->addr = a; + (*addr) = a + SIZE_PAGE; + + prog = syspage->progs; + if (prog != NULL) { + do { + if ((page->addr >= prog->start) && (page->addr < prog->end)) { + page->flags = PAGE_OWNER_APP; + return EOK; + } + prog = prog->next; + } while (prog != syspage->progs); + } + + + if (page->addr >= min + (4 * 1024 * 1024)) { + page->flags = PAGE_FREE; + return EOK; + } + + page->flags = PAGE_OWNER_KERNEL; + stack = (addr_t)pmap_common.stack; + + if ((page->addr >= stack) && (page->addr < stack + SIZE_KSTACK)) { + page->flags |= PAGE_KERNEL_STACK; + return EOK; + } + + end = ((addr_t)&_end + SIZE_PAGE - 1) & ~(SIZE_PAGE - 1); + end += SIZE_EXTEND_BSS; + if (page->addr >= end - VADDR_KERNEL + min) { + page->flags |= PAGE_FREE; + return EOK; + } + + ptable = (addr_t)pmap_common.ctxTable - VADDR_KERNEL + min; + end = (addr_t)pmap_common.heap - VADDR_KERNEL + min; + if ((page->addr >= ptable) && (page->addr < end)) { + page->flags |= PAGE_KERNEL_PTABLE; + return EOK; + } + + return EOK; } char pmap_marker(page_t *p) { - return 0; + static const char *const marksets[4] = { "BBBBBBBBBBBBBBBB", "KYCPMSHKKKKKKKKK", "AAAAAAAAAAAAAAAA", "UUUUUUUUUUUUUUUU" }; + + if ((p->flags & PAGE_FREE) != 0) { + return '.'; + } + + return marksets[(p->flags >> 1) & 3][(p->flags >> 4) & 0xf]; } int _pmap_kernelSpaceExpand(pmap_t *pmap, void **start, void *end, page_t *dp) { - return 0; + void *vaddr = (void *)((u32)(*start + SIZE_PAGE - 1) & ~(SIZE_PAGE - 1)); + + if (vaddr >= end) { + return EOK; + } + + if (vaddr < (void *)VADDR_KERNEL) { + vaddr = (void *)VADDR_KERNEL; + } + + for (; vaddr < end; vaddr += (SIZE_PAGE << 10)) { + if (pmap_enter(pmap, 0, vaddr, ~PGHD_PRESENT, NULL) < 0) { + if (pmap_enter(pmap, 0, vaddr, ~PGHD_PRESENT, dp) < 0) { + return -ENOMEM; + } + dp = NULL; + } + (*start) = vaddr; + } + + pmap->start = (void *)VADDR_KERNEL; + pmap->end = end; + + return EOK; } int pmap_segment(unsigned int i, void **vaddr, size_t *size, int *prot, void **top) { - if (i != 0) { - return -1; + switch (i) { + case 0: + *vaddr = (void *)VADDR_KERNEL; + *size = (size_t)&_etext - VADDR_KERNEL; + *prot = (PROT_EXEC | PROT_READ); + break; + case 1: + *vaddr = &_etext; + *size = (size_t)(*top) - (size_t)&_etext; + *prot = (PROT_WRITE | PROT_READ); + break; + default: + return -EINVAL; } - /* Returns region above basic kernel's .bss section */ - *vaddr = (void *)&_end; - *size = (((size_t)(*top) + SIZE_PAGE - 1) & ~(SIZE_PAGE - 1)) - (size_t)&_end; - - return 0; + return EOK; } void _pmap_init(pmap_t *pmap, void **vstart, void **vend) { - (*vstart) = (void *)(((ptr_t)_init_stack + 7) & ~7); - (*vend) = (*((char **)vstart)) + SIZE_PAGE; + ptr_t i; + u32 pdir2, pdir3, pte; + + hal_memset(pmap_common.ctxMap, 0xff, sizeof(pmap_common.ctxMap)); + /* Context 255 is reserved as shared */ + pmap_common.ctxMap[7] &= ~(1 << 31); + pmap_common.numCtxFree = MAX_CONTEXTS - 1; + + /* Allocate context for kernel */ + pmap->context = _pmap_contextAlloc(); + + hal_spinlockCreate(&pmap_common.lock, "pmap_common.lock"); + + pmap_common.minAddr = ADDR_SRAM; + pmap_common.maxAddr = ADDR_SRAM + SIZE_SRAM; + + /* Initialize kernel page table */ + pmap->pdir1 = pmap_common.pdir1; + pmap->addr = (addr_t)pmap->pdir1 - VADDR_KERNEL + pmap_common.minAddr; + + /* Remove initial kernel mapping */ + pmap->pdir1[PDIR1_IDX(pmap_common.minAddr)] = 0; + + /* Map kernel text & rodata as RX */ + for (i = VADDR_KERNEL; i < CEIL_PAGE((ptr_t)(&__bss_start)); i += SIZE_PAGE) { + pdir2 = PTD_TO_ADDR(pmap->pdir1[PDIR1_IDX(i)]); + pdir3 = PTD_TO_ADDR(hal_cpuLoadPaddr(&((u32 *)pdir2)[PDIR2_IDX(i)])); + pte = hal_cpuLoadPaddr(&((u32 *)pdir3)[PDIR3_IDX(i)]); + pte &= ~(0x7 << 2); + pte |= (PERM_SUPER_RX << 2); + hal_cpuStorePaddr(&((u32 *)pdir3)[PDIR3_IDX(i)], pte); + hal_cpuflushDCache(); + } + + /* Map kernel bss and copied syspage as RW */ + for (i = CEIL_PAGE((ptr_t)(&__bss_start)); i < CEIL_PAGE((ptr_t)(&_end)) + SIZE_PAGE; i += SIZE_PAGE) { + pdir2 = PTD_TO_ADDR(pmap->pdir1[PDIR1_IDX(i)]); + pdir3 = PTD_TO_ADDR(hal_cpuLoadPaddr(&((u32 *)pdir2)[PDIR2_IDX(i)])); + pte = hal_cpuLoadPaddr(&((u32 *)pdir3)[PDIR3_IDX(i)]); + pte &= ~(0x7 << 2); + pte |= (PERM_SUPER_RW << 2); + hal_cpuStorePaddr(&((u32 *)pdir3)[PDIR3_IDX(i)], pte); + hal_cpuflushDCache(); + } + hal_srmmuFlushTLB(0, TLB_FLUSH_ALL); + + pmap->start = (void *)VADDR_KERNEL; + pmap->end = (void *)VADDR_MAX; + + /* Initialize kernel heap start address */ + (*vstart) = (void *)CEIL_PAGE((u32)&_end); + + /* Skip copied syspage and mapped peripherals */ + (*vstart) += SIZE_EXTEND_BSS; + (*vend) = (*vstart) + SIZE_PAGE; - pmap->start = (void *)&__bss_start; + pmap_common.start = (u32)pmap_common.heap - VADDR_KERNEL + pmap_common.minAddr; + pmap_common.end = pmap_common.start + SIZE_PAGE; - /* Initial size of kernel map */ - pmap->end = (void *)((addr_t)&__bss_start + 32 * 1024); + /* Create initial heap */ + pmap_enter(pmap, (addr_t)pmap_common.start, (*vstart), PGHD_WRITE | PGHD_READ | PGHD_PRESENT, NULL); } diff --git a/hal/sparcv8leon3/sparcv8leon3.h b/hal/sparcv8leon3/sparcv8leon3.h index c706130ca..7c7a6345b 100644 --- a/hal/sparcv8leon3/sparcv8leon3.h +++ b/hal/sparcv8leon3/sparcv8leon3.h @@ -17,8 +17,11 @@ #define _HAL_SPARCV8LEON3_H_ +#include #include +#include "hal/sparcv8leon3/srmmu.h" + static inline void hal_cpuDataStoreBarrier(void) { @@ -26,4 +29,63 @@ static inline void hal_cpuDataStoreBarrier(void) } +static inline u32 hal_cpuLoadAlternate(addr_t addr, const u32 asi) +{ + /* clang-format off */ + + __asm__ volatile( + "lda [%0] %c1, %0" + : "+r"(addr) + : "i"(asi) + ); + + /* clang-format on */ + + return addr; +} + + +static inline void hal_cpuStoreAlternate(addr_t addr, const u32 asi, u32 val) +{ + /* clang-format off */ + + __asm__ volatile( + "sta %0, [%1] %c2" + : + : "r"(val), "r"(addr), "i"(asi) + ); + + /* clang-format on */ +} + + +static inline void hal_cpuflushDCache(void) +{ + hal_cpuStoreAlternate(0, ASI_FLUSH_DCACHE, 0); +} + + +static inline void hal_cpuflushICache(void) +{ + u32 ccr = hal_cpuLoadAlternate(0, ASI_CACHE_CTRL); + ccr |= CCR_FI; + hal_cpuStoreAlternate(0, ASI_CACHE_CTRL, ccr); +} + +/* Bypass MMU - store to physical address. + * Use with care on GR712RC - errata 1.7.19. + * Store may update data cache - flush it after use. + */ +static inline void hal_cpuStorePaddr(u32 *paddr, u32 val) +{ + hal_cpuStoreAlternate((addr_t)paddr, ASI_MMU_BYPASS, val); +} + +/* Bypass MMU - load from physical address */ +static inline u32 hal_cpuLoadPaddr(u32 *paddr) +{ + return hal_cpuLoadAlternate((addr_t)paddr, ASI_MMU_BYPASS); +} + + #endif diff --git a/hal/sparcv8leon3/spinlock.c b/hal/sparcv8leon3/spinlock.c index 335ab71ac..d0fb5beeb 100644 --- a/hal/sparcv8leon3/spinlock.c +++ b/hal/sparcv8leon3/spinlock.c @@ -38,6 +38,7 @@ void hal_spinlockSet(spinlock_t *spinlock, spinlock_ctx_t *sc) nop; \ nop; \ nop; \ + .align 16; /* GRLIB TN-0011 errata */ \ 1: \ ldstub [%0], %%g2; \ tst %%g2; \ diff --git a/hal/sparcv8leon3/srmmu.c b/hal/sparcv8leon3/srmmu.c new file mode 100644 index 000000000..1da7ff06e --- /dev/null +++ b/hal/sparcv8leon3/srmmu.c @@ -0,0 +1,49 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * SPARC reference MMU routines + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + + +#include "srmmu.h" +#include "hal/sparcv8leon3/sparcv8leon3.h" + + +void hal_srmmuFlushTLB(void *vaddr, u8 type) +{ + addr_t addr = (addr_t)(((u32)vaddr & ~(0xfff)) | ((type & 0xf) << 8)); + hal_cpuStoreAlternate(addr, ASI_FLUSH_ALL, 0); +} + + +u32 hal_srmmuGetFaultSts(void) +{ + return hal_cpuLoadAlternate(MMU_FAULT_STS, ASI_MMU_REGS); +} + + +u32 hal_srmmuGetFaultAddr(void) +{ + return hal_cpuLoadAlternate(MMU_FAULT_ADDR, ASI_MMU_REGS); +} + + +void hal_srmmuSetContext(u32 ctx) +{ + hal_cpuStoreAlternate(MMU_CTX, ASI_MMU_REGS, ctx); +} + + +u32 hal_srmmuGetContext(void) +{ + return hal_cpuLoadAlternate(MMU_CTX, ASI_MMU_REGS); +} diff --git a/hal/sparcv8leon3/srmmu.h b/hal/sparcv8leon3/srmmu.h new file mode 100644 index 000000000..802b5b495 --- /dev/null +++ b/hal/sparcv8leon3/srmmu.h @@ -0,0 +1,78 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * SPARC reference MMU routines + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + + +#ifndef _HAL_SRMMU_H_ +#define _HAL_SRMMU_H_ + + +/* TLB flush types + * More info about flushing behaviour: SPARC Architecture Manual V8, pages 245-246 + */ + +#define TLB_FLUSH_L3 0 /* Level 3 PTE */ +#define TLB_FLUSH_L2 1 /* Level 2 & 3 PTE/PTDs */ +#define TLB_FLUSH_L1 2 /* Level 1, 2 & 3 PTE/PTDs */ +#define TLB_FLUSH_CTX 3 /* Level 0, 1, 2 & 3 PTE/PTDs */ +#define TLB_FLUSH_ALL 4 /* All PTEs/PTDs */ + +/* Address Space Identifiers */ + +#define ASI_FORCE_CACHE_MISS 0x01 +#define ASI_CACHE_CTRL 0x02 +#define ASI_ICACHE_TAGS 0x0c +#define ASI_ICACHE_DATA 0x0d +#define ASI_DCACHE_TAGS 0x0e +#define ASI_DCACHE_DATA 0x0f +#define ASI_FLUSH_IDCACHE 0x10 /* Writing will flush I and D cache */ +#define ASI_FLUSH_DCACHE 0x11 /* Writing will flush D cache */ +#define ASI_FLUSH_ALL 0x18 /* Writing will flush TLB, I and D cache */ +#define ASI_MMU_REGS 0x19 +#define ASI_MMU_BYPASS 0x1c + +/* MMU register addresses */ + +#define MMU_CTRL 0x0u +#define MMU_CTX_PTR 0x100u +#define MMU_CTX 0x200u +#define MMU_FAULT_STS 0x300u +#define MMU_FAULT_ADDR 0x400u + + +#ifndef __ASSEMBLY__ + + +#include + + +void hal_srmmuFlushTLB(void *vaddr, u8 type); + + +u32 hal_srmmuGetFaultSts(void); + + +u32 hal_srmmuGetFaultAddr(void); + + +void hal_srmmuSetContext(u32 ctx); + + +u32 hal_srmmuGetContext(void); + + +#endif + + +#endif diff --git a/include/arch/gr712rc.h b/include/arch/gr712rc.h new file mode 100644 index 000000000..92751a82c --- /dev/null +++ b/include/arch/gr712rc.h @@ -0,0 +1,58 @@ +/* + * Phoenix-RTOS + * + * Operating system kernel + * + * GR712RC basic peripherals control functions + * + * Copyright 2023 Phoenix Systems + * Author: Lukasz Leczkowski + * + * This file is part of Phoenix-RTOS. + * + * %LICENSE% + */ + +#ifndef _PHOENIX_ARCH_GR712RC_H_ +#define _PHOENIX_ARCH_GR712RC_H_ + +#define PCTL_REBOOT_MAGIC 0xaa55aa55UL + +/* clang-format off */ + +/* Clock gating unit devices */ + +enum { cgudev_eth = 0, cgudev_spw0, cgudev_spw1, cgudev_spw2, cgudev_spw3, cgudev_spw4, cgudev_spw5, cgudev_can, + cgudev_ccsdsEnc = 9, cgudev_ccsdsDec, cgudev_milStd1553 }; + + +typedef struct { + enum { pctl_set = 0, pctl_get } action; + enum { pctl_iomux = 0, pctl_cguctrl, pctl_reboot } type; + + union { + struct { + unsigned char pin; + unsigned char opt; + unsigned char pullup; + unsigned char pulldn; + } iocfg; + + struct { + union { + enum { disable = 0, enable } state; + int stateVal; + }; + unsigned int cgudev; + } cguctrl; + + struct { + unsigned int magic; + } reboot; + }; +} __attribute__((packed)) platformctl_t; + +/* clang-format on */ + + +#endif