Skip to content

Commit 97b142f

Browse files
committed
Updated to support biRISC-V
1 parent 9dfe8c5 commit 97b142f

17 files changed

+559
-44
lines changed

README.md

+11-1
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,22 @@ A very simple bootstrap for starting the Linux kernel on RISC-V.
66
This takes a vmlinux ELF and a device tree (DTS) file, converts them to binaries,
77
and then embeds these into the bootstrap ELF.
88

9+
If atomic instruction support is not present on the target platform, the atomic instruction set will be emulated in SW.
10+
11+
This bootloader implements the required SBI calls used by standard RISC-V Linux kernel builds.
12+
13+
Used by the biRISC-V core to boot Linux: [http://github.com/ultraembedded/biriscv](http://github.com/ultraembedded/biriscv)
14+
15+
## Hardware Dependencies
16+
* UART: Xilinx UARTLite style UART @ 0x92000000
17+
* Timer: Non-std RISC-V mtime, mtimecmp implementation.
18+
919
## Cloning
1020
```
1121
git clone https://github.com/ultraembedded/riscv-linux-boot.git
1222
```
1323

1424
## Building
1525
```
16-
make LINUX_DIR=/path/to/riscv-linux DTS_FILE=/path/to/config.dts
26+
make LINUX_DIR=/path/to/riscv-linux VMLINUX=/path/to/vmlinux[.elf] DTS_FILE=/path/to/config.dts
1727
```

assert.c

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include "serial.h"
2+
3+
extern void _exit(int rc);
4+
5+
//-----------------------------------------------------------------
6+
// assert_handler:
7+
//-----------------------------------------------------------------
8+
void assert_handler(const char * type, const char *reason, const char *file, int line)
9+
{
10+
serial_putstr("ASSERT: ");
11+
serial_putstr(type);
12+
serial_putstr(" ");
13+
serial_putstr(reason);
14+
serial_putstr(" ");
15+
serial_putstr_hex(":0x", line);
16+
_exit(-1);
17+
}

assert.h

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#ifndef __ASSERT_H__
2+
#define __ASSERT_H__
3+
4+
extern void assert_handler(const char * type, const char *reason, const char *file, int line);
5+
6+
#define assert(exp) do { if (!(exp)) assert_handler("ASSERT", #exp, __FILE__, __LINE__ ); } while (0)
7+
#define panic(reason) do { assert_handler("PANIC", #reason, __FILE__, __LINE__ ); } while (0)
8+
9+
#endif
10+

boot.S

+1-1
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,6 @@ bss_clear:
137137
#####################################################
138138
.global _exit
139139
_exit:
140-
csrw dscratch, x0
140+
csrw 0x8b2, x0
141141
_exit_loop:
142142
jal _exit_loop

csr.h

+33-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,34 @@
66
//-----------------------------------------------------------------
77
// Defines:
88
//-----------------------------------------------------------------
9-
#define MSTATUS_MPP_SHIFT 11
10-
#define PRV_S 1
11-
#define PRV_M 3
9+
#define SR_SIE (1 << 1)
10+
#define SR_MIE (1 << 3)
11+
#define SR_SPIE (1 << 5)
12+
#define SR_MPIE (1 << 7)
13+
14+
#define IRQ_S_SOFT 1
15+
#define IRQ_M_SOFT 3
16+
#define IRQ_S_TIMER 5
17+
#define IRQ_M_TIMER 7
18+
#define IRQ_S_EXT 9
19+
#define IRQ_M_EXT 11
20+
21+
#define SR_IP_MSIP (1 << IRQ_M_SOFT)
22+
#define SR_IP_MTIP (1 << IRQ_M_TIMER)
23+
#define SR_IP_MEIP (1 << IRQ_M_EXT)
24+
#define SR_IP_SSIP (1 << IRQ_S_SOFT)
25+
#define SR_IP_STIP (1 << IRQ_S_TIMER)
26+
#define SR_IP_SEIP (1 << IRQ_S_EXT)
27+
28+
#define MSTATUS_SIE 0x00000002
29+
#define MSTATUS_MIE 0x00000008
30+
#define MSTATUS_SPIE 0x00000020
31+
#define MSTATUS_MPIE 0x00000080
32+
#define MSTATUS_SPP 0x00000100
33+
#define MSTATUS_MPP 0x00001800
34+
#define MSTATUS_MPRV 0x00020000
35+
#define MSTATUS_SUM 0x00040000
36+
#define MSTATUS_MXR 0x00080000
1237

1338
//-----------------------------------------------------------------
1439
// Helpers:
@@ -28,4 +53,9 @@
2853
asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \
2954
__tmp; })
3055

56+
#define csr_swap(reg, val) ({ \
57+
unsigned long __v = (unsigned long)(val); \
58+
asm volatile ("csrrw %0, " #reg ", %1" : "=r" (__v) : "rK" (__v) : "memory"); \
59+
__v; })
60+
3161
#endif

emulation.c

+225
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
#include "serial.h"
2+
#include "csr.h"
3+
#include "exception.h"
4+
#include "assert.h"
5+
#include "emulation.h"
6+
7+
extern void isr_vector(void);
8+
9+
//-----------------------------------------------------------------
10+
// Defines:
11+
//-----------------------------------------------------------------
12+
#define max(a,b) \
13+
({ __typeof__ (a) _a = (a); \
14+
__typeof__ (b) _b = (b); \
15+
_a > _b ? _a : _b; })
16+
17+
18+
#define min(a,b) \
19+
({ __typeof__ (a) _a = (a); \
20+
__typeof__ (b) _b = (b); \
21+
_a < _b ? _a : _b; })
22+
23+
//-----------------------------------------------------------------
24+
// Locals
25+
//-----------------------------------------------------------------
26+
static uint32_t load_reservation = 0;
27+
28+
//-----------------------------------------------------------------
29+
// emulation_read_word: Read word with fault catching
30+
//-----------------------------------------------------------------
31+
static int32_t emulation_read_word(uint32_t address, int32_t *data)
32+
{
33+
int32_t result, tmp;
34+
int32_t fail;
35+
__asm__ __volatile__ (
36+
" li %[tmp], 0x00020000\n" \
37+
" csrs mstatus, %[tmp]\n" \
38+
" la %[tmp], 1f\n" \
39+
" csrw mtvec, %[tmp]\n" \
40+
" li %[fail], 1\n" \
41+
" lw %[result], 0(%[address])\n"
42+
" li %[fail], 0\n" \
43+
"1:\n" \
44+
" li %[tmp], 0x00020000\n" \
45+
" csrc mstatus, %[tmp]\n" \
46+
: [result]"=&r" (result), [fail]"=&r" (fail), [tmp]"=&r" (tmp)
47+
: [address]"r" (address)
48+
: "memory"
49+
);
50+
51+
*data = result;
52+
return fail;
53+
}
54+
//-----------------------------------------------------------------
55+
// emulation_write_word: Write word with fault catching
56+
//-----------------------------------------------------------------
57+
static int32_t emulation_write_word(uint32_t address, int32_t data)
58+
{
59+
int32_t tmp;
60+
int32_t fail;
61+
__asm__ __volatile__ (
62+
" li %[tmp], 0x00020000\n" \
63+
" csrs mstatus, %[tmp]\n" \
64+
" la %[tmp], 1f\n" \
65+
" csrw mtvec, %[tmp]\n" \
66+
" li %[fail], 1\n" \
67+
" sw %[data], 0(%[address])\n"
68+
" li %[fail], 0\n" \
69+
"1:\n" \
70+
" li %[tmp], 0x00020000\n" \
71+
" csrc mstatus, %[tmp]\n" \
72+
: [fail]"=&r" (fail), [tmp]"=&r" (tmp)
73+
: [address]"r" (address), [data]"r" (data)
74+
: "memory"
75+
);
76+
return fail;
77+
}
78+
//-----------------------------------------------------------------
79+
// emulation_trap_to_supervisor: Divert fault to supervisor mode
80+
//-----------------------------------------------------------------
81+
static void emulation_trap_to_supervisor(struct irq_context *ctx, uint32_t sepc, uint32_t mstatus)
82+
{
83+
csr_write(mtvec, isr_vector);
84+
csr_write(0x143, csr_read(0x343)); // mbadaddr/mtval -> sbadaddr/stval
85+
csr_write(scause, csr_read(mcause));
86+
csr_write(sepc, sepc);
87+
88+
// Return to supervisor trap address
89+
ctx->pc = csr_read(stvec);
90+
ctx->status = (mstatus & ~(MSTATUS_SPP | MSTATUS_MPP | MSTATUS_SIE | MSTATUS_SPIE))
91+
| ((mstatus >> 3) & MSTATUS_SPP)
92+
| (0x0800 | MSTATUS_MPIE)
93+
| ((mstatus & MSTATUS_SIE) << 4);
94+
}
95+
//-----------------------------------------------------------------
96+
// trap_invalid_inst: Invalid instruction handler
97+
//-----------------------------------------------------------------
98+
static struct irq_context *trap_invalid_inst(struct irq_context *ctx)
99+
{
100+
uint32_t mepc = ctx->pc;
101+
uint32_t mstatus = ctx->status;
102+
uint32_t instr = csr_read(0x343); // mbadaddr or mtval
103+
104+
uint32_t opcode = instr & 0x7f;
105+
uint32_t funct3 = (instr >> 12) & 0x7;
106+
uint32_t sel = (instr >> 27);
107+
uint32_t rd = (instr >> 7) & 0x1f;
108+
uint32_t rs1 = (instr >> 15) & 0x1f;
109+
uint32_t rs2 = (instr >> 20) & 0x1f;
110+
111+
// LR
112+
if (opcode == 0x2f && funct3 == 0x2 && sel == 0x2)
113+
{
114+
uint32_t addr = ctx->reg[rs1];
115+
int32_t data_read = 0;
116+
117+
// Load
118+
if (emulation_read_word(addr, &data_read))
119+
{
120+
// Load fault - stop and redirect to supervisor
121+
emulation_trap_to_supervisor(ctx, mepc, mstatus);
122+
return ctx;
123+
}
124+
125+
// TODO: This should be on the physical address or take into account ctx switches...
126+
load_reservation = addr;
127+
128+
ctx->reg[rd] = data_read;
129+
}
130+
// SC
131+
else if (opcode == 0x2f && funct3 == 0x2 && sel == 0x3)
132+
{
133+
uint32_t addr = (rs1 != 0) ? ctx->reg[rs1] : 0;
134+
int32_t data_write = (rs2 != 0) ? ctx->reg[rs2] : 0;
135+
136+
if (load_reservation == addr)
137+
{
138+
// Store
139+
if (emulation_write_word(addr, data_write))
140+
{
141+
// Store fault - stop and redirect to supervisor
142+
emulation_trap_to_supervisor(ctx, mepc, mstatus);
143+
return ctx;
144+
}
145+
146+
load_reservation = 0;
147+
ctx->reg[rd] = 0;
148+
}
149+
else
150+
ctx->reg[rd] = 1;
151+
}
152+
// Atomics
153+
else if (opcode == 0x2f)
154+
{
155+
switch(funct3)
156+
{
157+
case 0x2:
158+
{
159+
uint32_t addr = (rs1 != 0) ? ctx->reg[rs1] : 0;
160+
int32_t src = (rs2 != 0) ? ctx->reg[rs2] : 0;
161+
int32_t data_read = 0;
162+
int32_t data_write = 0;
163+
164+
// Load
165+
if (emulation_read_word(addr, &data_read))
166+
{
167+
// Load fault - stop and redirect to supervisor
168+
emulation_trap_to_supervisor(ctx, mepc, mstatus);
169+
return ctx;
170+
}
171+
172+
switch(sel)
173+
{
174+
case 0x0: data_write = src + data_read; break; // amoadd.w
175+
case 0x1: data_write = src; break; // amoswap.w
176+
case 0x4: data_write = src ^ data_read; break; // amoxor.w
177+
case 0xC: data_write = src & data_read; break; // amoand.w
178+
case 0x8: data_write = src | data_read; break; // amoor.w
179+
case 0x10: data_write = min((int32_t)src, (int32_t)data_read); break; // amomin.w
180+
case 0x14: data_write = max((int32_t)src, (int32_t)data_read); break; // amomax.w
181+
case 0x18: data_write = min((uint32_t)src, (uint32_t)data_read); break; // amominu.w
182+
case 0x1C: data_write = max((uint32_t)src, (uint32_t)data_read); break; // amomaxu.w
183+
default: assert(!"error"); break;
184+
}
185+
186+
// Store
187+
if (emulation_write_word(addr, data_write))
188+
{
189+
// Store fault - stop and redirect to supervisor
190+
emulation_trap_to_supervisor(ctx, mepc, mstatus);
191+
return ctx;
192+
}
193+
ctx->reg[rd] = data_read;
194+
} break;
195+
default: assert(!"error"); break;
196+
}
197+
}
198+
else
199+
{
200+
serial_putstr_hex("ERROR: Invalid opcode: ", instr);
201+
serial_putstr_hex(" at PC: ", ctx->pc);
202+
assert(!"error");
203+
}
204+
205+
// Skip faulting instruction
206+
ctx->pc += 4;
207+
208+
// Force MTVEC back to default handler
209+
csr_write(mtvec, isr_vector);
210+
return ctx;
211+
}
212+
//-----------------------------------------------------------------
213+
// emulation_init: Configure emulation
214+
//-----------------------------------------------------------------
215+
void emulation_init(void)
216+
{
217+
exception_set_handler(CAUSE_ILLEGAL_INSTRUCTION, trap_invalid_inst);
218+
}
219+
//-----------------------------------------------------------------
220+
// emulation_take_irq: On interrupt, clear load reservation
221+
//-----------------------------------------------------------------
222+
void emulation_take_irq(void)
223+
{
224+
load_reservation = 0;
225+
}

emulation.h

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#ifndef EMULATION_H
2+
#define EMULATION_H
3+
4+
void emulation_init(void);
5+
void emulation_take_irq(void);
6+
7+
#endif

0 commit comments

Comments
 (0)