From 0076f8bf1def0789543e88417bf329a3cb351942 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Thu, 23 Apr 2020 14:37:29 +1000 Subject: [PATCH] XICS test case Checks interrupt masking and priorities. Adds to `make test_xics` which is run in `make check` also. Signed-off-by: Michael Neuling --- tests/test_xics.bin | Bin 0 -> 12384 bytes tests/test_xics.console_out | 3 + tests/update_console_tests | 2 +- tests/xics/Makefile | 5 + tests/xics/head.S | 186 +++++++++++++++++++++++++ tests/xics/powerpc.lds | 13 ++ tests/xics/xics.c | 262 ++++++++++++++++++++++++++++++++++++ tests/xics/xics.h | 36 +++++ 8 files changed, 506 insertions(+), 1 deletion(-) create mode 100755 tests/test_xics.bin create mode 100644 tests/test_xics.console_out create mode 100644 tests/xics/Makefile create mode 100644 tests/xics/head.S create mode 100644 tests/xics/powerpc.lds create mode 100644 tests/xics/xics.c create mode 100644 tests/xics/xics.h diff --git a/tests/test_xics.bin b/tests/test_xics.bin new file mode 100755 index 0000000000000000000000000000000000000000..6dd993ce0af3fd582dcb3d1914ca85dcf8b48a1b GIT binary patch literal 12384 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 PH_gZ&CI0%Qdm;Y<^ycm; literal 0 HcmV?d00001 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)); +}