1#![crate_name = "rv32i"]
8#![crate_type = "rlib"]
9#![no_std]
10
11use core::fmt::Write;
12
13use kernel::utilities::registers::interfaces::{Readable, Writeable};
14
15pub mod clic;
16pub mod machine_timer;
17pub mod pmp;
18pub mod support;
19pub mod syscall;
20
21pub use riscv::csr;
24
25extern "C" {
26 static _estack: usize;
29 static _sstack: usize;
30
31 static mut _szero: usize;
33 static mut _ezero: usize;
34
35 static mut _etext: usize;
37
38 static mut _srelocate: usize;
40 static mut _erelocate: usize;
41
42 #[link_name = "__global_pointer$"]
44 static __global_pointer: usize;
45}
46
47#[cfg(any(doc, all(target_arch = "riscv32", target_os = "none")))]
48extern "C" {
49 pub fn _start();
60}
61
62#[cfg(any(doc, all(target_arch = "riscv32", target_os = "none")))]
63core::arch::global_asm!("
64 .section .riscv.start, \"ax\"
65 .globl _start
66 _start:
67
68 // Set the global pointer register using the variable defined in the
69 // linker script. This register is only set once. The global pointer
70 // is a method for sharing state between the linker and the CPU so
71 // that the linker can emit code with offsets that are relative to
72 // the gp register, and the CPU can successfully execute them.
73 //
74 // https://gnu-mcu-eclipse.github.io/arch/riscv/programmer/#the-gp-global-pointer-register
75 // https://groups.google.com/a/groups.riscv.org/forum/#!msg/sw-dev/60IdaZj27dY/5MydPLnHAQAJ
76 // https://www.sifive.com/blog/2017/08/28/all-aboard-part-3-linker-relaxation-in-riscv-toolchain/
77 //
78 // Disable linker relaxation for code that sets up GP so that this doesn't
79 // get turned into `mv gp, gp`.
80 .option push
81 .option norelax
82
83 la gp, {gp} // Set the global pointer from linker script.
84
85 // Re-enable linker relaxations.
86 .option pop
87
88 // Initialize the stack pointer register. This comes directly from
89 // the linker script.
90 la sp, {estack} // Set the initial stack pointer.
91
92 // Set s0 (the frame pointer) to the start of the stack.
93 add s0, sp, zero // s0 = sp
94
95 // Initialize mscratch to 0 so that we know that we are currently
96 // in the kernel. This is used for the check in the trap handler.
97 csrw 0x340, zero // CSR=0x340=mscratch
98
99 // INITIALIZE MEMORY
100
101 // Start by initializing .bss memory. The Tock linker script defines
102 // `_szero` and `_ezero` to mark the .bss segment.
103 la a0, {sbss} // a0 = first address of .bss
104 la a1, {ebss} // a1 = first address after .bss
105
106 100: // bss_init_loop
107 beq a0, a1, 101f // If a0 == a1, we are done.
108 sw zero, 0(a0) // *a0 = 0. Write 0 to the memory location in a0.
109 addi a0, a0, 4 // a0 = a0 + 4. Increment pointer to next word.
110 j 100b // Continue the loop.
111
112 101: // bss_init_done
113
114
115 // Now initialize .data memory. This involves coping the values right at the
116 // end of the .text section (in flash) into the .data section (in RAM).
117 la a0, {sdata} // a0 = first address of data section in RAM
118 la a1, {edata} // a1 = first address after data section in RAM
119 la a2, {etext} // a2 = address of stored data initial values
120
121 200: // data_init_loop
122 beq a0, a1, 201f // If we have reached the end of the .data
123 // section then we are done.
124 lw a3, 0(a2) // a3 = *a2. Load value from initial values into a3.
125 sw a3, 0(a0) // *a0 = a3. Store initial value into
126 // next place in .data.
127 addi a0, a0, 4 // a0 = a0 + 4. Increment to next word in memory.
128 addi a2, a2, 4 // a2 = a2 + 4. Increment to next word in flash.
129 j 200b // Continue the loop.
130
131 201: // data_init_done
132
133 // With that initial setup out of the way, we now branch to the main
134 // code, likely defined in a board's main.rs.
135 j main
136 ",
137gp = sym __global_pointer,
138estack = sym _estack,
139sbss = sym _szero,
140ebss = sym _ezero,
141sdata = sym _srelocate,
142edata = sym _erelocate,
143etext = sym _etext,
144);
145
146pub enum PermissionMode {
148 User = 0x0,
149 Supervisor = 0x1,
150 Reserved = 0x2,
151 Machine = 0x3,
152}
153
154pub unsafe fn configure_trap_handler() {
162 csr::CSR.mscratch.set(0);
164
165 csr::CSR.mtvec.write(
169 csr::mtvec::mtvec::trap_addr.val(_start_trap as usize >> 2)
170 + csr::mtvec::mtvec::mode::CLEAR,
171 );
172}
173
174#[cfg(not(any(doc, all(target_arch = "riscv32", target_os = "none"))))]
176pub extern "C" fn _start_trap() {
177 unimplemented!()
178}
179
180#[cfg(any(doc, all(target_arch = "riscv32", target_os = "none")))]
181extern "C" {
182 pub fn _start_trap();
269}
270
271#[cfg(any(doc, all(target_arch = "riscv32", target_os = "none")))]
272core::arch::global_asm!(
273 "
274 .section .riscv.trap, \"ax\"
275 .globl _start_trap
276 _start_trap:
277 // This is the global trap handler. By default, Tock expects this
278 // trap handler to be registered at all times, and that all traps
279 // and interrupts occurring in all modes of execution (M-, S-, and
280 // U-mode) will cause this trap handler to be executed.
281 //
282 // For documentation of its behavior, and how process
283 // implementations can hook their own trap handler code, see the
284 // comment on the `extern C _start_trap` symbol above.
285
286 // Atomically swap s0 and mscratch:
287 csrrw s0, mscratch, s0 // s0 = mscratch; mscratch = s0
288
289 // If mscratch contained 0, invoke the kernel trap handler.
290 beq s0, x0, 100f // if s0==x0: goto 100
291
292 // Else, save the current value of s1 to `0*4(s0)`, load `1*4(s0)`
293 // into s1 and jump to it (invoking a custom trap handler).
294 sw s1, 0*4(s0) // *s0 = s1
295 lw s1, 1*4(s0) // s1 = *(s0+4)
296 jr s1 // goto s1
297
298 100: // _start_kernel_trap
299
300 // The global trap handler has swapped s0 into mscratch. We can thus
301 // freely clobber s0 without losing any information.
302 //
303 // Since we want to use the stack to save kernel registers, we
304 // first need to make sure that the trap wasn't the result of a
305 // stack overflow, in which case we can't use the current stack
306 // pointer. Use s0 as a scratch register:
307
308 // Load the address of the bottom of the stack (`_sstack`) into our
309 // newly freed-up s0 register.
310 la s0, {sstack} // s0 = _sstack
311
312 // Compare the kernel stack pointer to the bottom of the stack. If
313 // the stack pointer is above the bottom of the stack, then continue
314 // handling the fault as normal.
315 bgtu sp, s0, 200f // branch if sp > s0
316
317 // If we get here, then we did encounter a stack overflow. We are
318 // going to panic at this point, but for that to work we need a
319 // valid stack to run the panic code. We do this by just starting
320 // over with the kernel stack and placing the stack pointer at the
321 // top of the original stack.
322 la sp, {estack} // sp = _estack
323
324 200: // _start_kernel_trap_continue
325
326 // Restore s0. We reset mscratch to 0 (kernel trap handler mode)
327 csrrw s0, mscratch, zero // s0 = mscratch; mscratch = 0
328
329 // Make room for the caller saved registers we need to restore after
330 // running any trap handler code.
331 addi sp, sp, -16*4
332
333 // Save all of the caller saved registers.
334 sw ra, 0*4(sp)
335 sw t0, 1*4(sp)
336 sw t1, 2*4(sp)
337 sw t2, 3*4(sp)
338 sw t3, 4*4(sp)
339 sw t4, 5*4(sp)
340 sw t5, 6*4(sp)
341 sw t6, 7*4(sp)
342 sw a0, 8*4(sp)
343 sw a1, 9*4(sp)
344 sw a2, 10*4(sp)
345 sw a3, 11*4(sp)
346 sw a4, 12*4(sp)
347 sw a5, 13*4(sp)
348 sw a6, 14*4(sp)
349 sw a7, 15*4(sp)
350
351 // Jump to board-specific trap handler code. Likely this was an
352 // interrupt and we want to disable a particular interrupt, but each
353 // board/chip can customize this as needed.
354 jal ra, _start_trap_rust_from_kernel
355
356 // Restore the registers from the stack.
357 lw ra, 0*4(sp)
358 lw t0, 1*4(sp)
359 lw t1, 2*4(sp)
360 lw t2, 3*4(sp)
361 lw t3, 4*4(sp)
362 lw t4, 5*4(sp)
363 lw t5, 6*4(sp)
364 lw t6, 7*4(sp)
365 lw a0, 8*4(sp)
366 lw a1, 9*4(sp)
367 lw a2, 10*4(sp)
368 lw a3, 11*4(sp)
369 lw a4, 12*4(sp)
370 lw a5, 13*4(sp)
371 lw a6, 14*4(sp)
372 lw a7, 15*4(sp)
373
374 // Reset the stack pointer.
375 addi sp, sp, 16*4
376
377 // mret returns from the trap handler. The PC is set to what is in
378 // mepc and execution proceeds from there. Since we did not modify
379 // mepc we will return to where the exception occurred.
380 mret
381 ",
382 estack = sym _estack,
383 sstack = sym _sstack,
384);
385
386#[cfg(any(doc, all(target_arch = "riscv32", target_os = "none")))]
398pub unsafe fn semihost_command(command: usize, arg0: usize, arg1: usize) -> usize {
399 use core::arch::asm;
400 let res;
401 asm!(
402 "
403 .balign 16
404 .option push
405 .option norelax
406 .option norvc
407 slli x0, x0, 0x1f
408 ebreak
409 srai x0, x0, 7
410 .option pop
411 ",
412 in("a0") command,
413 in("a1") arg0,
414 in("a2") arg1,
415 lateout("a0") res,
416 );
417 res
418}
419
420#[cfg(not(any(doc, all(target_arch = "riscv32", target_os = "none"))))]
422pub unsafe fn semihost_command(_command: usize, _arg0: usize, _arg1: usize) -> usize {
423 unimplemented!()
424}
425
426pub unsafe fn print_mcause(mcval: csr::mcause::Trap, writer: &mut dyn Write) {
428 match mcval {
429 csr::mcause::Trap::Interrupt(interrupt) => match interrupt {
430 csr::mcause::Interrupt::UserSoft => {
431 let _ = writer.write_fmt(format_args!("User software interrupt"));
432 }
433 csr::mcause::Interrupt::SupervisorSoft => {
434 let _ = writer.write_fmt(format_args!("Supervisor software interrupt"));
435 }
436 csr::mcause::Interrupt::MachineSoft => {
437 let _ = writer.write_fmt(format_args!("Machine software interrupt"));
438 }
439 csr::mcause::Interrupt::UserTimer => {
440 let _ = writer.write_fmt(format_args!("User timer interrupt"));
441 }
442 csr::mcause::Interrupt::SupervisorTimer => {
443 let _ = writer.write_fmt(format_args!("Supervisor timer interrupt"));
444 }
445 csr::mcause::Interrupt::MachineTimer => {
446 let _ = writer.write_fmt(format_args!("Machine timer interrupt"));
447 }
448 csr::mcause::Interrupt::UserExternal => {
449 let _ = writer.write_fmt(format_args!("User external interrupt"));
450 }
451 csr::mcause::Interrupt::SupervisorExternal => {
452 let _ = writer.write_fmt(format_args!("Supervisor external interrupt"));
453 }
454 csr::mcause::Interrupt::MachineExternal => {
455 let _ = writer.write_fmt(format_args!("Machine external interrupt"));
456 }
457 csr::mcause::Interrupt::Unknown(_) => {
458 let _ = writer.write_fmt(format_args!("Reserved/Unknown"));
459 }
460 },
461 csr::mcause::Trap::Exception(exception) => match exception {
462 csr::mcause::Exception::InstructionMisaligned => {
463 let _ = writer.write_fmt(format_args!("Instruction access misaligned"));
464 }
465 csr::mcause::Exception::InstructionFault => {
466 let _ = writer.write_fmt(format_args!("Instruction access fault"));
467 }
468 csr::mcause::Exception::IllegalInstruction => {
469 let _ = writer.write_fmt(format_args!("Illegal instruction"));
470 }
471 csr::mcause::Exception::Breakpoint => {
472 let _ = writer.write_fmt(format_args!("Breakpoint"));
473 }
474 csr::mcause::Exception::LoadMisaligned => {
475 let _ = writer.write_fmt(format_args!("Load address misaligned"));
476 }
477 csr::mcause::Exception::LoadFault => {
478 let _ = writer.write_fmt(format_args!("Load access fault"));
479 }
480 csr::mcause::Exception::StoreMisaligned => {
481 let _ = writer.write_fmt(format_args!("Store/AMO address misaligned"));
482 }
483 csr::mcause::Exception::StoreFault => {
484 let _ = writer.write_fmt(format_args!("Store/AMO access fault"));
485 }
486 csr::mcause::Exception::UserEnvCall => {
487 let _ = writer.write_fmt(format_args!("Environment call from U-mode"));
488 }
489 csr::mcause::Exception::SupervisorEnvCall => {
490 let _ = writer.write_fmt(format_args!("Environment call from S-mode"));
491 }
492 csr::mcause::Exception::MachineEnvCall => {
493 let _ = writer.write_fmt(format_args!("Environment call from M-mode"));
494 }
495 csr::mcause::Exception::InstructionPageFault => {
496 let _ = writer.write_fmt(format_args!("Instruction page fault"));
497 }
498 csr::mcause::Exception::LoadPageFault => {
499 let _ = writer.write_fmt(format_args!("Load page fault"));
500 }
501 csr::mcause::Exception::StorePageFault => {
502 let _ = writer.write_fmt(format_args!("Store/AMO page fault"));
503 }
504 csr::mcause::Exception::Unknown => {
505 let _ = writer.write_fmt(format_args!("Reserved"));
506 }
507 },
508 }
509}
510
511pub unsafe fn print_riscv_state(writer: &mut dyn Write) {
514 let mcval: csr::mcause::Trap = core::convert::From::from(csr::CSR.mcause.extract());
515 let _ = writer.write_fmt(format_args!("\r\n---| RISC-V Machine State |---\r\n"));
516 let _ = writer.write_fmt(format_args!("Last cause (mcause): "));
517 print_mcause(mcval, writer);
518 let interrupt = csr::CSR.mcause.read(csr::mcause::mcause::is_interrupt);
519 let code = csr::CSR.mcause.read(csr::mcause::mcause::reason);
520 let _ = writer.write_fmt(format_args!(
521 " (interrupt={}, exception code={:#010X})",
522 interrupt, code
523 ));
524 let _ = writer.write_fmt(format_args!(
525 "\r\nLast value (mtval): {:#010X}\
526 \r\n\
527 \r\nSystem register dump:\
528 \r\n mepc: {:#010X} mstatus: {:#010X}\
529 \r\n mcycle: {:#010X} minstret: {:#010X}\
530 \r\n mtvec: {:#010X}",
531 csr::CSR.mtval.get(),
532 csr::CSR.mepc.get(),
533 csr::CSR.mstatus.get(),
534 csr::CSR.mcycle.get(),
535 csr::CSR.minstret.get(),
536 csr::CSR.mtvec.get()
537 ));
538 let mstatus = csr::CSR.mstatus.extract();
539 let uie = mstatus.is_set(csr::mstatus::mstatus::uie);
540 let sie = mstatus.is_set(csr::mstatus::mstatus::sie);
541 let mie = mstatus.is_set(csr::mstatus::mstatus::mie);
542 let upie = mstatus.is_set(csr::mstatus::mstatus::upie);
543 let spie = mstatus.is_set(csr::mstatus::mstatus::spie);
544 let mpie = mstatus.is_set(csr::mstatus::mstatus::mpie);
545 let spp = mstatus.is_set(csr::mstatus::mstatus::spp);
546 let _ = writer.write_fmt(format_args!(
547 "\r\n mstatus: {:#010X}\
548 \r\n uie: {:5} upie: {}\
549 \r\n sie: {:5} spie: {}\
550 \r\n mie: {:5} mpie: {}\
551 \r\n spp: {}",
552 mstatus.get(),
553 uie,
554 upie,
555 sie,
556 spie,
557 mie,
558 mpie,
559 spp
560 ));
561 let e_usoft = csr::CSR.mie.is_set(csr::mie::mie::usoft);
562 let e_ssoft = csr::CSR.mie.is_set(csr::mie::mie::ssoft);
563 let e_msoft = csr::CSR.mie.is_set(csr::mie::mie::msoft);
564 let e_utimer = csr::CSR.mie.is_set(csr::mie::mie::utimer);
565 let e_stimer = csr::CSR.mie.is_set(csr::mie::mie::stimer);
566 let e_mtimer = csr::CSR.mie.is_set(csr::mie::mie::mtimer);
567 let e_uext = csr::CSR.mie.is_set(csr::mie::mie::uext);
568 let e_sext = csr::CSR.mie.is_set(csr::mie::mie::sext);
569 let e_mext = csr::CSR.mie.is_set(csr::mie::mie::mext);
570
571 let p_usoft = csr::CSR.mip.is_set(csr::mip::mip::usoft);
572 let p_ssoft = csr::CSR.mip.is_set(csr::mip::mip::ssoft);
573 let p_msoft = csr::CSR.mip.is_set(csr::mip::mip::msoft);
574 let p_utimer = csr::CSR.mip.is_set(csr::mip::mip::utimer);
575 let p_stimer = csr::CSR.mip.is_set(csr::mip::mip::stimer);
576 let p_mtimer = csr::CSR.mip.is_set(csr::mip::mip::mtimer);
577 let p_uext = csr::CSR.mip.is_set(csr::mip::mip::uext);
578 let p_sext = csr::CSR.mip.is_set(csr::mip::mip::sext);
579 let p_mext = csr::CSR.mip.is_set(csr::mip::mip::mext);
580 let _ = writer.write_fmt(format_args!(
581 "\r\n mie: {:#010X} mip: {:#010X}\
582 \r\n usoft: {:6} {:6}\
583 \r\n ssoft: {:6} {:6}\
584 \r\n msoft: {:6} {:6}\
585 \r\n utimer: {:6} {:6}\
586 \r\n stimer: {:6} {:6}\
587 \r\n mtimer: {:6} {:6}\
588 \r\n uext: {:6} {:6}\
589 \r\n sext: {:6} {:6}\
590 \r\n mext: {:6} {:6}\r\n",
591 csr::CSR.mie.get(),
592 csr::CSR.mip.get(),
593 e_usoft,
594 p_usoft,
595 e_ssoft,
596 p_ssoft,
597 e_msoft,
598 p_msoft,
599 e_utimer,
600 p_utimer,
601 e_stimer,
602 p_stimer,
603 e_mtimer,
604 p_mtimer,
605 e_uext,
606 p_uext,
607 e_sext,
608 p_sext,
609 e_mext,
610 p_mext
611 ));
612}