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