diff --git a/tests/test_xics.bin b/tests/test_xics.bin new file mode 100755 index 0000000..6dd993c Binary files /dev/null and b/tests/test_xics.bin differ diff --git a/tests/test_xics.console_out b/tests/test_xics.console_out new file mode 100644 index 0000000..8c7ae53 --- /dev/null +++ b/tests/test_xics.console_out @@ -0,0 +1,3 @@ +Test 0:PASS +Test 1:PASS +Test 2:PASS diff --git a/tests/update_console_tests b/tests/update_console_tests index c17c12b..11306bb 100755 --- a/tests/update_console_tests +++ b/tests/update_console_tests @@ -3,7 +3,7 @@ # Script to update console related tests from source # -for i in sc illegal decrementer ; do +for i in sc illegal decrementer xics ; do cd $i make cd - diff --git a/tests/xics/Makefile b/tests/xics/Makefile new file mode 100644 index 0000000..8224a99 --- /dev/null +++ b/tests/xics/Makefile @@ -0,0 +1,5 @@ +TEST=xics + +include ../Makefile.test + +xics.o : xics.h diff --git a/tests/xics/head.S b/tests/xics/head.S new file mode 100644 index 0000000..c513a02 --- /dev/null +++ b/tests/xics/head.S @@ -0,0 +1,186 @@ +/* Copyright 2013-2014 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define STACK_TOP 0x4000 + +/* Load an immediate 64-bit value into a register */ +#define LOAD_IMM64(r, e) \ + lis r,(e)@highest; \ + ori r,r,(e)@higher; \ + rldicr r,r, 32, 31; \ + oris r,r, (e)@h; \ + ori r,r, (e)@l; + + .section ".head","ax" + + /* Microwatt currently enters in LE mode at 0x0 */ + . = 0 +.global _start +_start: + LOAD_IMM64(%r12, 0x000000000ffffff) + mtdec %r12 + LOAD_IMM64(%r12, 0x9000000000008003) + mtmsrd %r12 // EE on + /* setup stack */ + LOAD_IMM64(%r1, STACK_TOP - 0x100) + LOAD_IMM64(%r12, main) + mtctr %r12 + bctrl + attn // terminate on exit + b . + +#define EXCEPTION(nr) \ + .= nr ;\ + b . + + /* More exception stubs */ + EXCEPTION(0x300) + EXCEPTION(0x380) + EXCEPTION(0x400) + EXCEPTION(0x480) + . = 0x500 + b __isr + + EXCEPTION(0x600) + EXCEPTION(0x700) + EXCEPTION(0x800) + EXCEPTION(0x900) + EXCEPTION(0x980) + EXCEPTION(0xa00) + EXCEPTION(0xb00) + EXCEPTION(0xc00) + EXCEPTION(0xd00) + +// ISR data + +#define REDZONE_SIZE (512) +#define REG_SAVE_SIZE ((32 + 5)*8) +#define STACK_FRAME_C_MINIMAL 64 + +#define SAVE_NIA (32*8) +#define SAVE_LR (33*8) +#define SAVE_CTR (34*8) +#define SAVE_CR (35*8) +#define SAVE_SRR1 (36*8) + +__isr: +/* + * Assume where we are coming from has a stack and can save there. + * We save the full register set. Since we are calling out to C, we + * could just save the ABI volatile registers + */ + stdu %r1,-(REG_SAVE_SIZE+REDZONE_SIZE)(%r1) + std %r0, 1*8(%r1) +// std %r1, 1*8(%r1) + std %r2, 2*8(%r1) + std %r3, 3*8(%r1) + std %r4, 4*8(%r1) + std %r5, 5*8(%r1) + std %r6, 6*8(%r1) + std %r7, 7*8(%r1) + std %r8, 8*8(%r1) + std %r9, 9*8(%r1) + std %r10, 10*8(%r1) + std %r11, 11*8(%r1) + std %r12, 12*8(%r1) + std %r13, 13*8(%r1) + std %r14, 14*8(%r1) + std %r15, 15*8(%r1) + std %r16, 16*8(%r1) + std %r17, 17*8(%r1) + std %r18, 18*8(%r1) + std %r19, 19*8(%r1) + std %r20, 20*8(%r1) + std %r21, 21*8(%r1) + std %r22, 22*8(%r1) + std %r23, 23*8(%r1) + std %r24, 24*8(%r1) + std %r25, 25*8(%r1) + std %r26, 26*8(%r1) + std %r27, 27*8(%r1) + std %r28, 28*8(%r1) + std %r29, 29*8(%r1) + std %r30, 30*8(%r1) + std %r31, 31*8(%r1) + mfsrr0 %r0 + std %r0, SAVE_NIA*8(%r1) + mflr %r0 + std %r0, SAVE_LR*8(%r1) + mfctr %r0 + std %r0, SAVE_CTR*8(%r1) + mfcr %r0 + std %r0, SAVE_CR*8(%r1) + mfsrr1 %r0 + std %r0, SAVE_SRR1*8(%r1) + + stdu %r1,-STACK_FRAME_C_MINIMAL(%r1) + LOAD_IMM64(%r3, isr) + mtctr %r3, + bctrl + nop + ld %r1, 0(%r1) + + ld %r0, 1*8(%r1) +// ld %r1, 1*8(%r1) // do this at rfid + ld %r2, 2*8(%r1) +// ld %r3, 3*8(%r1) // do this at rfid + ld %r4, 4*8(%r1) + ld %r5, 5*8(%r1) + ld %r6, 6*8(%r1) + ld %r7, 7*8(%r1) + ld %r8, 8*8(%r1) + ld %r9, 9*8(%r1) + ld %r10, 10*8(%r1) + ld %r11, 11*8(%r1) + ld %r12, 12*8(%r1) + ld %r13, 13*8(%r1) + ld %r14, 14*8(%r1) + ld %r15, 15*8(%r1) + ld %r16, 16*8(%r1) + ld %r17, 17*8(%r1) + ld %r18, 18*8(%r1) + ld %r19, 19*8(%r1) + ld %r20, 20*8(%r1) + ld %r21, 21*8(%r1) + ld %r22, 22*8(%r1) + ld %r23, 23*8(%r1) + ld %r24, 24*8(%r1) + ld %r25, 25*8(%r1) + ld %r26, 26*8(%r1) + ld %r27, 27*8(%r1) + ld %r28, 28*8(%r1) + ld %r29, 29*8(%r1) + ld %r30, 30*8(%r1) + ld %r31, 31*8(%r1) + + ld %r3, SAVE_LR*8(%r1) + mtlr %r3 + ld %r3, SAVE_CTR*8(%r1) + mtctr %r3 + ld %r3, SAVE_CR*8(%r1) + mtcr %r3 + ld %r3, SAVE_SRR1*8(%r1) + mtsrr1 %r3 + ld %r3, SAVE_NIA*8(%r1) + mtsrr0 %r3 + + /* restore %r3 */ + ld %r3, 3*8(%r1) + + /* do final fixup r1 */ + ld %r1, 0*8(%r1) + + rfid diff --git a/tests/xics/powerpc.lds b/tests/xics/powerpc.lds new file mode 100644 index 0000000..c4bff13 --- /dev/null +++ b/tests/xics/powerpc.lds @@ -0,0 +1,13 @@ +SECTIONS +{ + _start = .; + . = 0; + .head : { + KEEP(*(.head)) + } + . = 0x1000; + .text : { *(.text) } + . = 0x3000; + .data : { *(.data) } + .bss : { *(.bss) } +} diff --git a/tests/xics/xics.c b/tests/xics/xics.c new file mode 100644 index 0000000..6652cbf --- /dev/null +++ b/tests/xics/xics.c @@ -0,0 +1,262 @@ +#include +#include +#include +#include + +#include "console.h" +#include "xics.h" + +#undef DEBUG +//#define DEBUG 1 + +void print_number(unsigned int i) // only for i = 0-999 +{ + unsigned int j, k, m; + bool zeros = false; + + k = 1000000000; + + for (m = 0; m < 10 ; m++) { + j = i/k; + if (m == 9) zeros = true; + if (zeros || (j != 0)) { + putchar(48 + j); + zeros = true; + } + i = i % k; + k = k / 10; + } +} + +#ifdef DEBUG +#define DEBUG_STR "\r\nDEBUG: " +void debug_print(int i) +{ + putstr(DEBUG_STR, strlen(DEBUG_STR)); + print_number(i); + putstr("\r\n", 2); +} + +#define debug_putstr(a, b) putstr(a,b) +#else +#define debug_putstr(a, b) +#define debug_print(i) +#endif + +#define ASSERT_FAIL "() ASSERT_FAILURE!\r\n " +#define assert(cond) \ + if (!(cond)) { \ + putstr(__FILE__, strlen(__FILE__)); \ + putstr(":", 1); \ + print_number(__LINE__); \ + putstr(":", 1); \ + putstr(__FUNCTION__, strlen(__FUNCTION__));\ + putstr(ASSERT_FAIL, strlen(ASSERT_FAIL)); \ + __asm__ ("attn"); \ + } + + +volatile uint64_t isrs_run; + +#define ISR_IPI 0x0000000000000001 +#define ISR_UART 0x0000000000000002 +#define ISR_SPURIOUS 0x0000000000000004 + +#define IPI "IPI\r\n" +void ipi_isr(void) { + debug_putstr(IPI, strlen(IPI)); + + isrs_run |= ISR_IPI; +} + + +#define UART "UART\r\n" +void uart_isr(void) { + debug_putstr(UART, strlen(UART)); + + potato_uart_irq_dis(); // disable interrupt to ack it + + isrs_run |= ISR_UART; +} + +// The hardware doesn't support this but it's part of XICS so add it. +#define SPURIOUS "SPURIOUS\r\n" +void spurious_isr(void) { + debug_putstr(SPURIOUS, strlen(SPURIOUS)); + + isrs_run |= ISR_SPURIOUS; +} + +struct isr_op { + void (*func)(void); + int source_id; +}; + +struct isr_op isr_table[] = { + { .func = ipi_isr, .source_id = 2 }, + { .func = uart_isr, .source_id = 16 }, + { .func = spurious_isr, .source_id = 0 }, + { .func = NULL, .source_id = 0 } +}; + +bool ipi_running; + +#define ISR "ISR XISR=" +void isr(void) +{ + struct isr_op *op; + uint32_t xirr; + + assert(!ipi_running); // check we aren't reentrant + ipi_running = true; + + xirr = xics_read32(XICS_XIRR); // read hardware irq source + +#ifdef DEBUG + putstr(ISR, strlen(ISR)); + print_number(xirr & 0xff); + putstr("\r\n", 2); +#endif + + op = isr_table; + while (1) { + assert(op->func); // didn't find isr + if (op->source_id == (xirr & 0x00ffffff)) { + op->func(); + break; + } + op++; + } + + xics_write32(XICS_XIRR, xirr); // EOI + + ipi_running = false; +} + +/*****************************************/ + +int xics_test_0(void) +{ + // setup + xics_write8(XICS_XIRR, 0x00); // mask all interrupts + isrs_run = 0; + + xics_write8(XICS_XIRR, 0x00); // mask all interrupts + + // trigger two interrupts + potato_uart_irq_en(); // cause 0x500 interrupt + xics_write8(XICS_MFRR, 0x05); // cause 0x500 interrupt + + // still masked, so shouldn't happen yet + assert(isrs_run == 0); + + // unmask IPI only + xics_write8(XICS_XIRR, 0x40); + assert(isrs_run == ISR_IPI); + + // unmask UART + xics_write8(XICS_XIRR, 0xc0); + assert(isrs_run == (ISR_IPI | ISR_UART)); + + // cleanup + xics_write8(XICS_XIRR, 0x00); // mask all interrupts + isrs_run = 0; + + return 0; +} + +int xics_test_1(void) +{ + // setup + xics_write8(XICS_XIRR, 0x00); // mask all interrupts + isrs_run = 0; + + xics_write8(XICS_XIRR, 0xff); // allow all interrupts + + // should be none pending + assert(isrs_run == 0); + + // trigger both + potato_uart_irq_en(); // cause 0x500 interrupt + xics_write8(XICS_MFRR, 0x05); // cause 0x500 interrupt + + assert(isrs_run == (ISR_IPI | ISR_UART)); + + // cleanup + xics_write8(XICS_XIRR, 0x00); // mask all interrupts + isrs_run = 0; + + return 0; +} + +void mtmsrd(uint64_t val) +{ + __asm__ volatile("mtmsrd %0" : : "r" (val)); +} + +int xics_test_2(void) +{ + // setup + xics_write8(XICS_XIRR, 0x00); // mask all interrupts + isrs_run = 0; + + // trigger interrupts with MSR[EE]=0 and show they are not run + mtmsrd(0x9000000000000003); // EE off + + xics_write8(XICS_XIRR, 0xff); // allow all interrupts + + // trigger an IPI + xics_write8(XICS_MFRR, 0x05); // cause 0x500 interrupt + + assert(isrs_run == 0); + + mtmsrd(0x9000000000008003); // EE on + assert(isrs_run == ISR_IPI); + + // cleanup + xics_write8(XICS_XIRR, 0x00); // mask all interrupts + isrs_run = 0; + + return 0; +} + +#define TEST "Test " +#define PASS "PASS\r\n" +#define FAIL "FAIL\r\n" + +int (*tests[])(void) = { + xics_test_0, + xics_test_1, + xics_test_2, + NULL +}; + +int main(void) +{ + int fail = 0; + int i = 0; + int (*t)(void); + + potato_uart_init(); + ipi_running = false; + + /* run the tests */ + while (1) { + t = tests[i]; + if (!t) + break; + + putstr(TEST, strlen(TEST)); + print_number(i); + putstr(": ", 1); + if (t() != 0) { + fail = 1; + putstr(FAIL, strlen(FAIL)); + } else + putstr(PASS, strlen(PASS)); + + i++; + } + + return fail; +} diff --git a/tests/xics/xics.h b/tests/xics/xics.h new file mode 100644 index 0000000..09238cc --- /dev/null +++ b/tests/xics/xics.h @@ -0,0 +1,36 @@ +#include + +#define XICS_BASE 0xc0004000 + +static uint64_t xics_base = XICS_BASE; + +#define XICS_XIRR_POLL 0x0 +#define XICS_XIRR 0x4 +#define XICS_RESV 0x8 +#define XICS_MFRR 0xC + +uint8_t xics_read8(int offset) +{ + uint32_t val; + + __asm__ volatile("lbzcix %0,%1,%2" : "=r" (val) : "b" (xics_base), "r" (offset)); + return val; +} + +void xics_write8(int offset, uint8_t val) +{ + __asm__ volatile("stbcix %0,%1,%2" : : "r" (val), "b" (xics_base), "r" (offset)); +} + +uint32_t xics_read32(int offset) +{ + uint32_t val; + + __asm__ volatile("lwzcix %0,%1,%2" : "=r" (val) : "b" (xics_base), "r" (offset)); + return val; +} + +void xics_write32(int offset, uint32_t val) +{ + __asm__ volatile("stwcix %0,%1,%2" : : "r" (val), "b" (xics_base), "r" (offset)); +}