XICS test case
authorMichael Neuling <mikey@neuling.org>
Thu, 23 Apr 2020 04:37:29 +0000 (14:37 +1000)
committerMichael Neuling <mikey@neuling.org>
Thu, 23 Apr 2020 06:36:49 +0000 (16:36 +1000)
Checks interrupt masking and priorities.

Adds to `make test_xics` which is run in `make check` also.

Signed-off-by: Michael Neuling <mikey@neuling.org>
tests/test_xics.bin [new file with mode: 0755]
tests/test_xics.console_out [new file with mode: 0644]
tests/update_console_tests
tests/xics/Makefile [new file with mode: 0644]
tests/xics/head.S [new file with mode: 0644]
tests/xics/powerpc.lds [new file with mode: 0644]
tests/xics/xics.c [new file with mode: 0644]
tests/xics/xics.h [new file with mode: 0644]

diff --git a/tests/test_xics.bin b/tests/test_xics.bin
new file mode 100755 (executable)
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 (file)
index 0000000..8c7ae53
--- /dev/null
@@ -0,0 +1,3 @@
+Test 0:PASS\r
+Test 1:PASS\r
+Test 2:PASS\r
index c17c12b7c584b4632b9bbccc17c5f8b73b1ecbde..11306bbfb656b1acda221632d92778e6fccb8e20 100755 (executable)
@@ -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 (file)
index 0000000..8224a99
--- /dev/null
@@ -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 (file)
index 0000000..c513a02
--- /dev/null
@@ -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 (file)
index 0000000..c4bff13
--- /dev/null
@@ -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 (file)
index 0000000..6652cbf
--- /dev/null
@@ -0,0 +1,262 @@
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <limits.h>
+
+#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 (file)
index 0000000..09238cc
--- /dev/null
@@ -0,0 +1,36 @@
+#include <stdint.h>
+
+#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));
+}