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 <mikey@neuling.org>pull/165/head
parent
b4f20c20b9
commit
0076f8bf1d
Binary file not shown.
@ -0,0 +1,3 @@
|
|||||||
|
Test 0:PASS
|
||||||
|
Test 1:PASS
|
||||||
|
Test 2:PASS
|
@ -0,0 +1,5 @@
|
|||||||
|
TEST=xics
|
||||||
|
|
||||||
|
include ../Makefile.test
|
||||||
|
|
||||||
|
xics.o : xics.h
|
@ -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
|
@ -0,0 +1,13 @@
|
|||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
_start = .;
|
||||||
|
. = 0;
|
||||||
|
.head : {
|
||||||
|
KEEP(*(.head))
|
||||||
|
}
|
||||||
|
. = 0x1000;
|
||||||
|
.text : { *(.text) }
|
||||||
|
. = 0x3000;
|
||||||
|
.data : { *(.data) }
|
||||||
|
.bss : { *(.bss) }
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -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));
|
||||||
|
}
|
Loading…
Reference in New Issue