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