123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494 |
- #![no_std]
- #![no_main]
- #![feature(naked_functions)]
- #![allow(static_mut_refs)]
- #[macro_use]
- extern crate rcore_console;
- use core::{
- arch::{asm, naked_asm},
- ptr::null,
- };
- use riscv::register::cycle;
- use sbi_spec::{
- binary::{CounterMask, HartMask, SbiRet},
- pmu::firmware_event,
- };
- use sbi_testing::sbi::{self, ConfigFlags, StartFlags, StopFlags};
- // use sbi_spec::pmu::*;
- use uart16550::Uart16550;
- const RISCV_HEAD_FLAGS: u64 = 0;
- const RISCV_HEADER_VERSION: u32 = 0x2;
- const RISCV_IMAGE_MAGIC: u64 = 0x5643534952; /* Magic number, little endian, "RISCV" */
- const RISCV_IMAGE_MAGIC2: u32 = 0x05435352; /* Magic number 2, little endian, "RSC\x05" */
- /// boot header
- #[naked]
- #[unsafe(no_mangle)]
- #[unsafe(link_section = ".head.text")]
- unsafe extern "C" fn _boot_header() -> ! {
- unsafe {
- naked_asm!(
- "j _start",
- ".word 0",
- ".balign 8",
- ".dword 0x200000",
- ".dword iend - istart",
- ".dword {RISCV_HEAD_FLAGS}",
- ".word {RISCV_HEADER_VERSION}",
- ".word 0",
- ".dword 0",
- ".dword {RISCV_IMAGE_MAGIC}",
- ".balign 4",
- ".word {RISCV_IMAGE_MAGIC2}",
- ".word 0",
- RISCV_HEAD_FLAGS = const RISCV_HEAD_FLAGS,
- RISCV_HEADER_VERSION = const RISCV_HEADER_VERSION,
- RISCV_IMAGE_MAGIC = const RISCV_IMAGE_MAGIC,
- RISCV_IMAGE_MAGIC2 = const RISCV_IMAGE_MAGIC2,
- );
- }
- }
- /// 内核入口。
- ///
- /// # Safety
- ///
- /// 裸函数。
- #[naked]
- #[unsafe(no_mangle)]
- #[unsafe(link_section = ".text.entry")]
- unsafe extern "C" fn _start(hartid: usize, device_tree_paddr: usize) -> ! {
- const STACK_SIZE: usize = 16384; // 16 KiB
- #[unsafe(link_section = ".bss.uninit")]
- static mut STACK: [u8; STACK_SIZE] = [0u8; STACK_SIZE];
- unsafe {
- naked_asm!(
- // clear bss segment
- " la t0, sbss
- la t1, ebss
- 1: bgeu t0, t1, 2f
- sd zero, 0(t0)
- addi t0, t0, 8
- j 1b",
- "2:",
- " la sp, {stack} + {stack_size}",
- " j {main}",
- stack_size = const STACK_SIZE,
- stack = sym STACK,
- main = sym rust_main,
- )
- }
- }
- extern "C" fn rust_main(hartid: usize, dtb_pa: usize) -> ! {
- let BoardInfo {
- smp,
- frequency,
- uart,
- } = BoardInfo::parse(dtb_pa);
- unsafe { UART = Uart16550Map(uart as _) };
- rcore_console::init_console(&Console);
- rcore_console::set_log_level(option_env!("LOG"));
- println!(
- r"
- _____ _ _ __ _
- |_ _|__ ___| |_ | |/ /___ _ __ _ __ ___| |
- | |/ _ \/ __| __| | ' // _ \ '__| '_ \ / _ \ |
- | | __/\__ \ |_ | . \ __/ | | | | | __/ |
- |_|\___||___/\__| |_|\_\___|_| |_| |_|\___|_|
- ================================================
- | boot hart id | {hartid:20} |
- | smp | {smp:20} |
- | timebase frequency | {frequency:17} Hz |
- | dtb physical address | {dtb_pa:#20x} |
- ------------------------------------------------"
- );
- let testing = sbi_testing::Testing {
- hartid,
- hart_mask: (1 << smp) - 1,
- hart_mask_base: 0,
- delay: frequency,
- };
- let test_result = testing.test();
- // PMU test, only available in qemu-system-riscv64 single core
- let counters_num = sbi::pmu_num_counters();
- println!("[pmu] counters number: {}", counters_num);
- for idx in 0..counters_num {
- let counter_info = sbi::pmu_counter_get_info(idx);
- let counter_info = CounterInfo::new(counter_info.value);
- if counter_info.is_firmware_counter() {
- println!("[pmu] counter index:{:>2}, is a firmware counter", idx);
- } else {
- println!(
- "[pmu] counter index:{:>2}, csr num: {:#03x}, width: {}",
- idx,
- counter_info.get_csr(),
- counter_info.get_width()
- );
- }
- }
- /* PMU test for hardware event */
- let counter_mask = CounterMask::from_mask_base(0x7ffff, 0);
- let result = sbi::pmu_counter_config_matching(counter_mask, Flag::new(0b110), 0x2, 0);
- assert!(result.is_ok());
- let result = sbi::pmu_counter_config_matching(counter_mask, Flag::new(0b110), 0x10019, 0);
- assert!(result.is_ok());
- let result = sbi::pmu_counter_config_matching(counter_mask, Flag::new(0b110), 0x1001b, 0);
- assert!(result.is_ok());
- let result = sbi::pmu_counter_config_matching(counter_mask, Flag::new(0b110), 0x10021, 0);
- assert!(result.is_ok());
- let result = sbi::pmu_counter_config_matching(counter_mask, Flag::new(0b110), 0x3, 0);
- assert_eq!(result, SbiRet::not_supported());
- // `SBI_PMU_HW_CPU_CYCLES` event test
- let result = sbi::pmu_counter_config_matching(counter_mask, Flag::new(0b010), 0x1, 0);
- assert!(result.is_ok());
- // the counter index should be 0(mcycle)
- assert_eq!(result.value, 0);
- let cycle_counter_idx = result.value;
- let cycle_num = cycle::read64();
- assert_eq!(cycle_num, 0);
- // Start counting `SBI_PMU_HW_CPU_CYCLES` events
- let start_result = sbi::pmu_counter_start(
- CounterMask::from_mask_base(0x1, cycle_counter_idx),
- Flag::new(0x1),
- 0xffff,
- );
- assert!(start_result.is_ok());
- let cycle_num = cycle::read64();
- assert!(cycle_num >= 0xffff);
- // Stop counting `SBI_PMU_HW_CPU_CYCLES` events
- let stop_result = sbi::pmu_counter_stop(
- CounterMask::from_mask_base(0x1, cycle_counter_idx),
- Flag::new(0x0),
- );
- assert!(stop_result.is_ok());
- let old_cycle_num = cycle::read64();
- let mut _j = 0;
- for i in 0..1000 {
- _j += i
- }
- let new_cycle_num = cycle::read64();
- assert_eq!(old_cycle_num, new_cycle_num);
- // Restart counting `SBI_PMU_HW_CPU_CYCLES` events
- let start_result = sbi::pmu_counter_start(
- CounterMask::from_mask_base(0x1, cycle_counter_idx),
- Flag::new(0x0),
- 0,
- );
- assert!(start_result.is_ok());
- let mut _j = 0;
- for i in 0..1000 {
- _j += i
- }
- let restart_cycle_num = cycle::read64();
- assert!(restart_cycle_num > new_cycle_num);
- /* PMU test for firmware event */
- let counter_mask = CounterMask::from_mask_base(0x7ffffffff, 0);
- // Mapping a counter to the `SBI_PMU_FW_ACCESS_LOAD` event should result in unsupported
- let result = sbi::pmu_counter_config_matching(
- counter_mask,
- Flag::new(0b010),
- EventIdx::new_firmware_event(firmware_event::ACCESS_LOAD).raw(),
- 0,
- );
- assert_eq!(result, SbiRet::not_supported());
- // Map a counter to the `SBI_PMU_FW_IPI_SENT` event.
- // This counter should be a firmware counter and its value should be initialized to 0.
- let result = sbi::pmu_counter_config_matching(
- counter_mask,
- Flag::new(0b010),
- EventIdx::new_firmware_event(firmware_event::IPI_SENT).raw(),
- 0,
- );
- assert!(result.is_ok());
- assert!(result.value >= 19);
- let ipi_counter_idx = result.value;
- let ipi_num = sbi::pmu_counter_fw_read(ipi_counter_idx);
- assert!(ipi_num.is_ok());
- assert_eq!(ipi_num.value, 0);
- // Start counting `SBI_PMU_FW_IPI_SENT` events and assign an initial value of 25 to the event counter
- let start_result = sbi::pmu_counter_start(
- CounterMask::from_mask_base(0x1, ipi_counter_idx),
- Flag::new(0x1),
- 25,
- );
- assert!(start_result.is_ok());
- // Read the value of the `SBI_PMU_FW_IPI_SENT` event counter, which should be 25
- let ipi_num = sbi::pmu_counter_fw_read(ipi_counter_idx);
- assert!(ipi_num.is_ok());
- assert_eq!(ipi_num.value, 25);
- // Send IPI to other core, and the `SBI_PMU_FW_IPI_SENT` event counter value increases by one
- let send_ipi_result = sbi::send_ipi(HartMask::from_mask_base(0b10, 0));
- assert_eq!(send_ipi_result, SbiRet::invalid_param());
- // Read the value of the `SBI_PMU_FW_IPI_SENT` event counter, which should be 26
- let ipi_num = sbi::pmu_counter_fw_read(ipi_counter_idx);
- assert!(ipi_num.is_ok());
- assert_eq!(ipi_num.value, 26);
- // Stop counting `SBI_PMU_FW_IPI_SENT` events
- let stop_result = sbi::pmu_counter_stop(
- CounterMask::from_mask_base(0x1, ipi_counter_idx),
- Flag::new(0x0),
- );
- assert!(stop_result.is_ok());
- // Restop counting `SBI_PMU_FW_IPI_SENT` events, the result should be already stop
- let stop_result = sbi::pmu_counter_stop(
- CounterMask::from_mask_base(0x1, ipi_counter_idx),
- Flag::new(0x0),
- );
- assert_eq!(stop_result, SbiRet::already_stopped());
- // Send IPI to other core, `SBI_PMU_FW_IPI_SENT` event counter should not change
- let send_ipi_result = sbi::send_ipi(HartMask::from_mask_base(0b10, 0));
- assert_eq!(send_ipi_result, SbiRet::invalid_param());
- // Read the value of the `SBI_PMU_FW_IPI_SENT` event counter, which should be 26
- let ipi_num = sbi::pmu_counter_fw_read(ipi_counter_idx);
- assert!(ipi_num.is_ok());
- assert_eq!(ipi_num.value, 26);
- // Restart counting `SBI_PMU_FW_IPI_SENT` events
- let start_result = sbi::pmu_counter_start(
- CounterMask::from_mask_base(0x1, ipi_counter_idx),
- Flag::new(0x0),
- 0,
- );
- assert!(start_result.is_ok());
- // Send IPI to other core, and the `SBI_PMU_FW_IPI_SENT` event counter value increases by one
- let send_ipi_result = sbi::send_ipi(HartMask::from_mask_base(0b10, 0));
- assert_eq!(send_ipi_result, SbiRet::invalid_param());
- // Read the value of the `SBI_PMU_FW_IPI_SENT` event counter, which should be 27
- let ipi_num = sbi::pmu_counter_fw_read(ipi_counter_idx);
- assert!(ipi_num.is_ok());
- assert_eq!(ipi_num.value, 27);
- if test_result {
- sbi::system_reset(sbi::Shutdown, sbi::NoReason);
- } else {
- sbi::system_reset(sbi::Shutdown, sbi::SystemFailure);
- }
- unreachable!()
- }
- #[cfg_attr(not(test), panic_handler)]
- fn panic(info: &core::panic::PanicInfo) -> ! {
- let (hart_id, pc): (usize, usize);
- unsafe { asm!("mv {}, tp", out(reg) hart_id) };
- unsafe { asm!("auipc {}, 0", out(reg) pc) };
- println!("[test-kernel-panic] hart {hart_id} {info}");
- println!("[test-kernel-panic] pc = {pc:#x}");
- println!("[test-kernel-panic] SBI test FAILED due to panic");
- sbi::system_reset(sbi::Shutdown, sbi::SystemFailure);
- loop {}
- }
- struct BoardInfo {
- smp: usize,
- frequency: u64,
- uart: usize,
- }
- impl BoardInfo {
- fn parse(dtb_pa: usize) -> Self {
- use dtb_walker::{Dtb, DtbObj, HeaderError as E, Property, Str, WalkOperation::*};
- let mut ans = Self {
- smp: 0,
- frequency: 0,
- uart: 0,
- };
- unsafe {
- Dtb::from_raw_parts_filtered(dtb_pa as _, |e| {
- matches!(e, E::Misaligned(4) | E::LastCompVersion(_))
- })
- }
- .unwrap()
- .walk(|ctx, obj| match obj {
- DtbObj::SubNode { name } => {
- if ctx.is_root() && (name == Str::from("cpus") || name == Str::from("soc")) {
- StepInto
- } else if ctx.name() == Str::from("cpus") && name.starts_with("cpu@") {
- ans.smp += 1;
- StepOver
- } else if ctx.name() == Str::from("soc")
- && (name.starts_with("uart") || name.starts_with("serial"))
- {
- StepInto
- } else {
- StepOver
- }
- }
- DtbObj::Property(Property::Reg(mut reg)) => {
- if ctx.name().starts_with("uart") || ctx.name().starts_with("serial") {
- ans.uart = reg.next().unwrap().start;
- }
- StepOut
- }
- DtbObj::Property(Property::General { name, value }) => {
- if ctx.name() == Str::from("cpus") && name == Str::from("timebase-frequency") {
- ans.frequency = match *value {
- [a, b, c, d] => u32::from_be_bytes([a, b, c, d]) as _,
- [a, b, c, d, e, f, g, h] => u64::from_be_bytes([a, b, c, d, e, f, g, h]),
- _ => unreachable!(),
- };
- }
- StepOver
- }
- DtbObj::Property(_) => StepOver,
- });
- ans
- }
- }
- struct Console;
- static mut UART: Uart16550Map = Uart16550Map(null());
- pub struct Uart16550Map(*const Uart16550<u8>);
- unsafe impl Sync for Uart16550Map {}
- impl Uart16550Map {
- #[inline]
- pub fn get(&self) -> &Uart16550<u8> {
- unsafe { &*self.0 }
- }
- }
- impl rcore_console::Console for Console {
- #[inline]
- fn put_char(&self, c: u8) {
- unsafe { UART.get().write(core::slice::from_ref(&c)) };
- }
- #[inline]
- fn put_str(&self, s: &str) {
- unsafe { UART.get().write(s.as_bytes()) };
- }
- }
- struct Flag {
- inner: usize,
- }
- impl ConfigFlags for Flag {
- fn raw(&self) -> usize {
- self.inner
- }
- }
- impl StartFlags for Flag {
- fn raw(&self) -> usize {
- self.inner
- }
- }
- impl StopFlags for Flag {
- fn raw(&self) -> usize {
- self.inner
- }
- }
- impl Flag {
- pub fn new(flag: usize) -> Self {
- Self { inner: flag }
- }
- }
- /// Wrap for counter info
- struct CounterInfo {
- /// Packed representation of counter information:
- /// - Bits [11:0]: CSR number for hardware counters
- /// - Bits [17:12]: Counter width (typically 63 for RV64)
- /// - MSB: Set for firmware counters, clear for hardware counters
- inner: usize,
- }
- #[allow(unused)]
- impl CounterInfo {
- const CSR_MASK: usize = 0xFFF; // Bits [11:0]
- const WIDTH_MASK: usize = 0x3F << 12; // Bits [17:12]
- const FIRMWARE_FLAG: usize = 1 << (size_of::<usize>() * 8 - 1); // MSB
- #[inline]
- pub const fn new(counter_info: usize) -> Self {
- Self {
- inner: counter_info,
- }
- }
- #[inline]
- pub fn set_csr(&mut self, csr_num: u16) {
- self.inner = (self.inner & !Self::CSR_MASK) | ((csr_num as usize) & Self::CSR_MASK);
- }
- #[inline]
- pub fn get_csr(&self) -> usize {
- self.inner & Self::CSR_MASK
- }
- #[inline]
- pub fn set_width(&mut self, width: u8) {
- self.inner = (self.inner & !Self::WIDTH_MASK) | (((width as usize) & 0x3F) << 12);
- }
- #[inline]
- pub fn get_width(&self) -> usize {
- (self.inner & Self::WIDTH_MASK) >> 12
- }
- #[inline]
- pub fn is_firmware_counter(&self) -> bool {
- self.inner & Self::FIRMWARE_FLAG != 0
- }
- #[inline]
- pub const fn with_hardware_info(csr_num: u16, width: u8) -> Self {
- Self {
- inner: ((csr_num as usize) & Self::CSR_MASK) | (((width as usize) & 0x3F) << 12),
- }
- }
- #[inline]
- pub const fn with_firmware_info() -> Self {
- Self {
- inner: Self::FIRMWARE_FLAG,
- }
- }
- #[inline]
- pub const fn inner(self) -> usize {
- self.inner
- }
- }
- struct EventIdx {
- inner: usize,
- }
- impl EventIdx {
- fn raw(&self) -> usize {
- self.inner
- }
- fn new_firmware_event(event_code: usize) -> Self {
- let inner = 0xf << 16 | event_code;
- Self { inner }
- }
- }
|