handle irq in the bootloader

This commit is contained in:
Mateusz Faderewski 2024-05-22 00:17:24 +02:00
parent 31ea6e6016
commit 7d8614e456
13 changed files with 224 additions and 110 deletions

View File

@ -11,7 +11,7 @@ N64_ELFCOMPRESS = $(N64_BINDIR)/n64elfcompress
N64_TOOL = $(N64_BINDIR)/n64tool
PYTHON = python3
FLAGS = -march=vr4300 -mtune=vr4300 $(USER_FLAGS)
FLAGS = -march=vr4300 -mtune=vr4300 -mfix4300 $(USER_FLAGS)
CFLAGS = -Os -Wall -ffunction-sections -fdata-sections -ffreestanding -MMD -MP
ASFLAGS = -Wa,-I$(N64_INST)/mips64-elf/lib
LDFLAGS = -lc -nostartfiles -Wl,--gc-sections

View File

@ -1,4 +1,3 @@
#include <stdbool.h>
#include <stdio.h>
#include "display.h"
#include "font.h"
@ -181,6 +180,10 @@ void display_init (uint32_t *background) {
}
}
bool display_ready (void) {
return vi_configured;
}
void display_vprintf (const char *fmt, va_list args) {
char line[256];

View File

@ -3,10 +3,12 @@
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
void display_init (uint32_t *background);
bool display_ready (void);
void display_vprintf (const char *fmt, va_list args);
void display_printf (const char* fmt, ...);

View File

@ -1,13 +1,23 @@
#include <stdarg.h>
#include "exception.h"
#include "display.h"
#include "init.h"
#include "version.h"
#include "../assets/assets.h"
void error_display (const char *fmt, ...) {
va_list args;
deinit();
display_init((uint32_t *) (&assets_sc64_logo_640_240_dimmed));
version_print();
display_printf("[ Runtime error ]\n");
va_start(args, fmt);
EXCEPTION_TRIGGER(TRIGGER_CODE_ERROR);
display_vprintf(fmt, args);
va_end(args);
display_printf("\n");
while (1);
}

View File

@ -96,17 +96,11 @@ exception_handler:
move $sp, $k0
exception_check_type:
mfc0 $a0, C0_CAUSE
sw $a0, C0_CAUSE_OFFSET($k0)
move $a1, $a0
move $t0, $a0
andi $t0, C0_CR_IP7
andi $a0, C0_CR_EC_MASK
srl $a0, $a0, C0_CR_EC_BIT
andi $a1, C0_CR_IP_MASK
srl $a1, $a1, C0_CR_IP_BIT
bne $t0, $zero, exception_fatal
beq $a0, $zero, exception_interrupt
mfc0 $t0, C0_CAUSE
sw $t0, C0_CAUSE_OFFSET($k0)
andi $a0, $t0, C0_CR_EC_MASK
srl $a0, C0_CR_EC_BIT
beqz $a0, exception_interrupt
exception_fatal:
sd $k0, K0_OFFSET($k0)
@ -117,16 +111,16 @@ exception_fatal:
sd $t0, C0_EPC_OFFSET($k0)
dmfc0 $t0, C0_BADVADDR
sd $t0, C0_BADVADDR_OFFSET($k0)
move $a2, $k0
la $t1, exception_fatal_handler
jalr $t1
move $a1, $k0
jal exception_fatal_handler
ld $t0, C0_EPC_OFFSET($k0)
dmtc0 $t0, C0_EPC
j exception_restore
exception_interrupt:
la $t1, exception_interrupt_handler
jalr $t1
andi $a0, $t0, C0_CR_IP_MASK
srl $a0, C0_CR_IP_BIT
jal exception_interrupt_handler
exception_restore:
.set noat
@ -169,8 +163,8 @@ exception_enable_interrupts:
.type exception_enable_interrupts, %function
.global exception_enable_interrupts
mfc0 $t0, C0_STATUS
li $t1, C0_SR_IE
or $t0, $t0, $t1
li $t1, (C0_SR_IM4 | C0_SR_IM3 | C0_SR_IE)
or $t0, $t1
mtc0 $t0, C0_STATUS
jr $ra
@ -180,8 +174,8 @@ exception_disable_interrupts:
.type exception_disable_interrupts, %function
.global exception_disable_interrupts
mfc0 $t0, C0_STATUS
li $t1, ~(C0_SR_IE)
and $t0, $t0, $t1
li $t1, ~(C0_SR_IM4 | C0_SR_IM3 | C0_SR_IE)
and $t0, $t1
mtc0 $t0, C0_STATUS
jr $ra
@ -195,7 +189,7 @@ exception_enable_watchdog:
mtc0 $t1, C0_COMPARE
mfc0 $t0, C0_STATUS
li $t1, C0_SR_IM7
or $t0, $t0, $t1
or $t0, $t1
mtc0 $t0, C0_STATUS
jr $ra
@ -206,7 +200,7 @@ exception_disable_watchdog:
.global exception_disable_watchdog
mfc0 $t0, C0_STATUS
li $t1, ~(C0_SR_IM7)
and $t0, $t0, $t1
and $t0, $t1
mtc0 $t0, C0_STATUS
mtc0 $zero, C0_COMPARE
jr $ra

View File

@ -8,15 +8,6 @@
#include "../assets/assets.h"
#define EXCEPTION_INTERRUPT (0)
#define EXCEPTION_SYSCALL (8)
#define INTERRUPT_MASK_TIMER (1 << 7)
#define SYSCALL_CODE_MASK (0x03FFFFC0UL)
#define SYSCALL_CODE_BIT (6)
static const char *exception_get_description (uint8_t exception_code) {
switch (exception_code) {
case 0: return "Interrupt";
@ -41,18 +32,11 @@ static const char *exception_get_description (uint8_t exception_code) {
}
void exception_fatal_handler (uint32_t exception_code, uint32_t interrupt_mask, exception_t *e) {
version_t *version = version_get();
uint32_t *instruction_address = (((uint32_t *) (e->epc.u32)) + ((e->cr & C0_CR_BD) ? 1 : 0));
void exception_fatal_handler (uint32_t exception_code, exception_t *e) {
display_init((uint32_t *) (&assets_sc64_logo_640_240_dimmed));
display_printf("[ SC64 bootloader metadata ]\n");
display_printf("branch: %s | tag: %s\n", version->git_branch, version->git_tag);
display_printf("sha: %s\n", version->git_sha);
display_printf("msg: %s\n\n", version->git_message);
if (exception_code != EXCEPTION_SYSCALL) {
version_print();
display_printf("[ Unhandled exception ]\n");
display_printf("%s\n", exception_get_description(exception_code));
display_printf(" pc: 0x%08lX sr: 0x%08lX cr: 0x%08lX va: 0x%08lX\n", e->epc.u32, e->sr, e->cr, e->badvaddr.u32);
display_printf(" zr: 0x%08lX at: 0x%08lX v0: 0x%08lX v1: 0x%08lX\n", e->zr.u32, e->at.u32, e->v0.u32, e->v1.u32);
@ -63,26 +47,6 @@ void exception_fatal_handler (uint32_t exception_code, uint32_t interrupt_mask,
display_printf(" s4: 0x%08lX s5: 0x%08lX s6: 0x%08lX s7: 0x%08lX\n", e->s4.u32, e->s5.u32, e->s6.u32, e->s7.u32);
display_printf(" t8: 0x%08lX t9: 0x%08lX k0: 0x%08lX k1: 0x%08lX\n", e->t8.u32, e->t9.u32, e->k0.u32, e->k1.u32);
display_printf(" gp: 0x%08lX sp: 0x%08lX s8: 0x%08lX ra: 0x%08lX\n\n", e->gp.u32, e->sp.u32, e->s8.u32, e->ra.u32);
} else {
display_printf("[ Runtime error ]\n");
}
if (exception_code == EXCEPTION_INTERRUPT) {
if (interrupt_mask & INTERRUPT_MASK_TIMER) {
exception_disable_watchdog();
display_printf("Still loading after 5 second limit...\n\n");
return;
}
} else if (exception_code == EXCEPTION_SYSCALL) {
uint32_t code = (((*instruction_address) & SYSCALL_CODE_MASK) >> SYSCALL_CODE_BIT);
if (code == TRIGGER_CODE_ERROR) {
const char *fmt = (const char *) (e->a0.u32);
va_list args = *((va_list *) (e->sp.u32));
display_vprintf(fmt, args);
display_printf("\n");
}
}
while (1);
}

View File

@ -2,11 +2,6 @@
#define EXCEPTION_H__
#define TRIGGER_CODE_ERROR (0)
#define EXCEPTION_TRIGGER(code) { asm volatile ("syscall %[c]\n" :: [c] "i" (code)); }
void exception_enable_interrupts (void);
void exception_disable_interrupts (void);
void exception_enable_watchdog (void);

View File

@ -1,6 +1,69 @@
#include "exception_regs.h"
#include "display.h"
#include "sc64.h"
#include "version.h"
#include "../assets/assets.h"
void exception_interrupt_handler (uint32_t exception_code, uint32_t interrupt_mask, exception_t *e) {
typedef enum {
INTERRUPT_SW_0 = (1 << 0),
INTERRUPT_SW_1 = (1 << 1),
INTERRUPT_RCP = (1 << 2),
INTERRUPT_CART = (1 << 3),
INTERRUPT_PRENMI = (1 << 4),
INTERRUPT_HW_5 = (1 << 5),
INTERRUPT_HW_6 = (1 << 6),
INTERRUPT_TIMER = (1 << 7),
} interrupt_t;
static void exception_interrupt_unhandled (uint8_t interrupt) {
display_init((uint32_t *) (&assets_sc64_logo_640_240_dimmed));
version_print();
display_printf("[ Unhandled interrupt ]\n");
display_printf("Pending (0x%02X):\n", interrupt);
for (int i = 0; i < 8; i++) {
switch (interrupt & (1 << i)) {
case INTERRUPT_SW_0: display_printf(" Software interrupt (0)\n"); break;
case INTERRUPT_SW_1: display_printf(" Software interrupt (1)\n"); break;
case INTERRUPT_RCP: display_printf(" RCP interrupt (2)\n"); break;
case INTERRUPT_CART: display_printf(" CART interrupt (3)\n"); break;
case INTERRUPT_PRENMI: display_printf(" Pre NMI interrupt (4)\n"); break;
case INTERRUPT_HW_5: display_printf(" Hardware interrupt (5)\n"); break;
case INTERRUPT_HW_6: display_printf(" Hardware interrupt (6)\n"); break;
case INTERRUPT_TIMER: display_printf(" Timer interrupt (7)\n"); break;
default: break;
}
}
while (1);
}
void exception_interrupt_handler (uint8_t interrupt) {
if (interrupt & INTERRUPT_CART) {
sc64_irq_t irq = sc64_irq_pending();
if (irq != SC64_IRQ_NONE) {
return sc64_irq_callback(irq);
}
}
if (interrupt & INTERRUPT_PRENMI) {
if (display_ready()) {
display_init(NULL);
display_printf("Resetting...\n");
}
while (1);
}
if (interrupt & INTERRUPT_TIMER) {
display_init((uint32_t *) (&assets_sc64_logo_640_240_dimmed));
version_print();
display_printf("[ Watchdog ]\n");
display_printf("SC64 bootloader did not finish loading in 5 seconds\n");
while (1);
}
exception_interrupt_unhandled(interrupt);
}

View File

@ -1,5 +1,6 @@
#include "io.h"
#include "sc64.h"
#include "error.h"
typedef struct {
@ -7,13 +8,16 @@ typedef struct {
io32_t DATA[2];
io32_t IDENTIFIER;
io32_t KEY;
io32_t IRQ;
} sc64_regs_t;
#define SC64_REGS_BASE (0x1FFF0000UL)
#define SC64_REGS ((sc64_regs_t *) SC64_REGS_BASE)
#define SC64_SR_IRQ_PENDING (1 << 29)
#define SC64_SR_CMD_IRQ_REQUEST (1 << 8)
#define SC64_SR_CMD_IRQ_PENDING (1 << 28)
#define SC64_SR_MCU_IRQ_PENDING (1 << 29)
#define SC64_SR_CMD_ERROR (1 << 30)
#define SC64_SR_CPU_BUSY (1 << 31)
@ -24,6 +28,9 @@ typedef struct {
#define SC64_KEY_UNLOCK_2 (0x4F434B5FUL)
#define SC64_KEY_LOCK (0xFFFFFFFFUL)
#define SC64_IRQ_CMD_CLEAR (1 << 30)
#define SC64_IRQ_MCU_CLEAR (1 << 31)
typedef enum {
CMD_ID_IDENTIFIER_GET = 'v',
@ -67,16 +74,30 @@ typedef struct {
} sc64_cmd_t;
static bool use_cmd_irq = false;
static volatile bool wait_cmd_irq = false;
static sc64_error_t sc64_execute_cmd (sc64_cmd_t *cmd) {
uint32_t sr;
pi_io_write(&SC64_REGS->DATA[0], cmd->arg[0]);
pi_io_write(&SC64_REGS->DATA[1], cmd->arg[1]);
if (use_cmd_irq) {
wait_cmd_irq = true;
pi_io_write(&SC64_REGS->SR_CMD, (SC64_SR_CMD_IRQ_REQUEST | (cmd->id & 0xFF)));
while (wait_cmd_irq);
sr = pi_io_read(&SC64_REGS->SR_CMD);
if (sr & SC64_SR_CPU_BUSY) {
error_display("[Unexpected] SC64 CMD busy flag set");
}
} else {
pi_io_write(&SC64_REGS->SR_CMD, (cmd->id & 0xFF));
uint32_t sr;
do {
sr = pi_io_read(&SC64_REGS->SR_CMD);
} while (sr & SC64_SR_CPU_BUSY);
}
if (sr & SC64_SR_CMD_ERROR) {
return (sc64_error_t) (pi_io_read(&SC64_REGS->DATA[0]));
@ -88,6 +109,18 @@ static sc64_error_t sc64_execute_cmd (sc64_cmd_t *cmd) {
return SC64_OK;
}
static void sc64_mcu_irq_callback (void) {
error_display("[Unexpected] SC64 MCU interrupt received");
}
static void sc64_cmd_irq_callback (void) {
if (wait_cmd_irq) {
wait_cmd_irq = false;
} else {
error_display("[Unexpected] SC64 CMD interrupt received");
}
}
const char *sc64_error_description (sc64_error_t error) {
sc64_error_type_t type = (sc64_error_type_t) ((error >> 24) & 0xFF);
@ -164,12 +197,31 @@ bool sc64_check_presence (void) {
}
bool sc64_irq_pending (void) {
return (pi_io_read(&SC64_REGS->SR_CMD) & SC64_SR_IRQ_PENDING);
void sc64_cmd_irq_enable (bool enable) {
use_cmd_irq = enable;
}
void sc64_irq_clear (void) {
pi_io_write(&SC64_REGS->IDENTIFIER, 0);
sc64_irq_t sc64_irq_pending (void) {
uint32_t sr = pi_io_read(&SC64_REGS->SR_CMD);
sc64_irq_t irq = SC64_IRQ_NONE;
if (sr & SC64_SR_MCU_IRQ_PENDING) {
irq |= SC64_IRQ_MCU;
}
if (sr & SC64_SR_CMD_IRQ_PENDING) {
irq |= SC64_IRQ_CMD;
}
return irq;
}
void sc64_irq_callback (sc64_irq_t irq) {
if (irq & SC64_IRQ_MCU) {
sc64_mcu_irq_callback();
pi_io_write(&SC64_REGS->IRQ, SC64_IRQ_MCU_CLEAR);
}
if (irq & SC64_IRQ_CMD) {
sc64_cmd_irq_callback();
pi_io_write(&SC64_REGS->IRQ, SC64_IRQ_CMD_CLEAR);
}
}

View File

@ -159,6 +159,12 @@ typedef enum {
SD_CARD_STATUS_BYTE_SWAP = (1 << 4),
} sc64_sd_card_status_t;
typedef enum {
SC64_IRQ_NONE = 0,
SC64_IRQ_MCU = (1 << 0),
SC64_IRQ_CMD = (1 << 1),
} sc64_irq_t;
typedef struct {
volatile uint8_t BUFFER[8192];
@ -177,8 +183,9 @@ void sc64_unlock (void);
void sc64_lock (void);
bool sc64_check_presence (void);
bool sc64_irq_pending (void);
void sc64_irq_clear (void);
void sc64_cmd_irq_enable (bool enable);
sc64_irq_t sc64_irq_pending (void);
void sc64_irq_callback (sc64_irq_t irq);
sc64_error_t sc64_get_identifier (uint32_t *identifier);
sc64_error_t sc64_get_version (uint16_t *major, uint16_t *minor, uint32_t *revision);

View File

@ -115,6 +115,22 @@ static void test_sc64_cfg (void) {
display_printf("%02d:%02d:%02d", FROM_BCD(t.hour), FROM_BCD(t.minute), FROM_BCD(t.second));
display_printf(" (%s)", weekdays[FROM_BCD(t.weekday)]);
display_printf("\n");
int count = 65536;
for (int i = 0; i < count; i++) {
if ((i % (count / 64)) == 0) {
display_printf(".");
}
if ((error = sc64_get_identifier(&identifier)) != SC64_OK) {
error_display("Command IDENTIFIER_GET failed\n (%08X) - %s", error, sc64_error_description(error));
}
if (identifier != 0x53437632) {
error_display("Invalid identifier received: 0x%08X", identifier);
}
}
display_printf("\n");
}
static void test_pi (void) {
@ -539,8 +555,18 @@ static struct {
void test_execute (void) {
sc64_error_t error;
const int test_count = sizeof(tests) / sizeof(tests[0]);
int current = 0;
display_init(NULL);
display_printf("SC64 Test suite (%d / %d)\n\n", 0, test_count);
display_printf("Initializing...\n");
pi_io_config(0x0F, 0x05, 0x0C, 0x02);
sc64_cmd_irq_enable(true);
if ((error = sc64_set_config(CFG_ID_ROM_WRITE_ENABLE, true)) != SC64_OK) {
error_display("Command CONFIG_SET [ROM_WRITE_ENABLE] failed\n (%08X) - %s", error, sc64_error_description(error));
}
@ -551,9 +577,6 @@ void test_execute (void) {
random_seed = __entropy + c0_count();
const int test_count = sizeof(tests) / sizeof(tests[0]);
int current = 0;
while (true) {
display_init(NULL);
display_printf("SC64 Test suite (%d / %d)\n\n", current + 1, test_count);

View File

@ -1,7 +1,12 @@
#include "version.h"
#include "display.h"
static version_t version = {
const struct {
const char *git_branch;
const char *git_tag;
const char *git_sha;
const char *git_message;
} version = {
#ifdef GIT_BRANCH
.git_branch = GIT_BRANCH,
#else
@ -29,6 +34,10 @@ static version_t version = {
};
version_t *version_get (void) {
return &version;
void version_print (void) {
display_printf("[ SC64 bootloader metadata ]\n");
display_printf("branch: %s | tag: %s\n", version.git_branch, version.git_tag);
display_printf("sha: %s\n", version.git_sha);
display_printf("msg: %s\n", version.git_message);
display_printf("\n");
}

View File

@ -2,15 +2,7 @@
#define VERSION_H__
typedef const struct {
const char *git_branch;
const char *git_tag;
const char *git_sha;
const char *git_message;
} version_t;
const version_t *version_get (void);
void version_print (void);
#endif