1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
//! A minimal RISC-V's SBI implementation library in Rust.
//!
//! *Note: If you are a user looking for binary distribution download for RustSBI, you may consider
//! using the [RustSBI Prototyping System](https://github.com/rustsbi/standalone)
//! which will provide binaries for each platforms.
//! If you are a vendor or contributor who wants to adapt RustSBI to your new product or board,
//! you may consider adapting the Prototyping System first to get your board adapted in an afternoon;
//! you are only advised to build a discrete crate if your team have a lot of time working on this board.*
//!
//! *For more details on binary downloads the the RustSBI Prototyping System,
//! see section [Prototyping System vs discrete packages](#download-binary-file-the-prototyping-system-vs-discrete-packages).*
//!
//! The crate `rustsbi` acts as core trait and instance abstraction of the RustSBI ecosystem.
//!
//! # What is RISC-V SBI?
//!
//! RISC-V SBI is short for RISC-V Supervisor Binary Interface. SBI acts as an interface to environment
//! for your operating system kernel.
//! An SBI implementation will allow furtherly bootstrap your kernel, and provide an environment while the kernel is running.
//!
//! More generally, The SBI allows supervisor-mode (S-mode or VS-mode) software to be portable across
//! all RISC-V implementations by defining an abstraction for platform (or hypervisor) specific functionality.
//!
//! # Use RustSBI services in your supervisor software
//!
//! SBI environment features include boot sequence and a kernel environment. To bootstrap your kernel,
//! place kernel into RustSBI implementation defined address, then RustSBI will prepare an
//! environment and call the entry function on this address.
//!
//! ## Make SBI environment calls
//!
//! To use the kernel environment, you either use SBI calls or emulated instructions.
//! SBI calls are similar to operating systems' `syscall`s. RISC-V SBI defined many SBI extensions,
//! and in each extension there are different functions, you should pick a function before calling.
//! Then, you should prepare some parameters, whose definition are not the same among functions.
//!
//! Now you have an extension number, a function number, and a few SBI call parameters.
//! You invoke a special `ecall` instruction on supervisor level, and it will trap into machine level
//! SBI implementation. It will handle your `ecall`, similar to your kernel handling system calls
//! from user level.
//!
//! SBI functions return two values other than one. First value will be an error number,
//! it will tell if SBI call have succeeded, or which error have occurred.
//! Second value is the real return value, its meaning is different according to which function you calls.
//!
//! ## Call SBI in different programming languages
//!
//! Making SBI calls are similar to making system calls.
//!
//! Extension number is required to put on register `a7`, function number on `a6` if applicable.
//! Parameters should be placed from `a0` to `a5`, first into `a0`, second into `a1`, etc.
//! Unused parameters can be set to any value or leave untouched.
//!
//! After registers are ready, invoke an instruction called `ecall`.
//! Then, the return value is placed into `a0` and `a1` registers.
//! The error value could be read from `a0`, and return value is placed into `a1`.
//!
//! In Rust, here is an example to call SBI functions using inline assembly:
//!
//! ```no_run
//! # #[repr(C)] struct SbiRet { error: usize, value: usize }
//! # const EXTENSION_BASE: usize = 0x10;
//! # const FUNCTION_BASE_GET_SPEC_VERSION: usize = 0x0;
//! #[inline(always)]
//! fn sbi_call(extension: usize, function: usize, arg0: usize, arg1: usize) -> SbiRet {
//! let (error, value);
//! match () {
//! #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
//! () => unsafe { asm!(
//! "ecall",
//! in("a0") arg0, in("a1") arg1,
//! in("a6") function, in("a7") extension,
//! lateout("a0") error, lateout("a1") value,
//! ) },
//! #[cfg(not(any(target_arch = "riscv32", target_arch = "riscv64")))]
//! () => {
//! drop((extension, function, arg0, arg1));
//! unimplemented!("not RISC-V instruction set architecture")
//! }
//! };
//! SbiRet { error, value }
//! }
//!
//! #[inline]
//! pub fn get_spec_version() -> SbiRet {
//! sbi_call(EXTENSION_BASE, FUNCTION_BASE_GET_SPEC_VERSION, 0, 0)
//! }
//! ```
//!
//! SBI functions would return a result thus some of these may fail.
//! In this example we only take the value, but in complete designs we should handle the `error`
//! returned by SbiRet.
//!
//! You may use other languages to call SBI environment. In C programming language, we can call like this:
//!
//! ```text
//! #define SBI_CALL(ext, funct, arg0, arg1, arg2, arg3) ({ \
//! register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0); \
//! register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1); \
//! register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2); \
//! register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3); \
//! register uintptr_t a6 asm ("a6") = (uintptr_t)(funct); \
//! register uintptr_t a7 asm ("a7") = (uintptr_t)(ext); \
//! asm volatile ("ecall" \
//! : "+r" (a0), "+r" (a1) \
//! : "r" (a1), "r" (a2), "r" (a3), "r" (a6), "r" (a7) \
//! : "memory") \
//! {a0, a1}; \
//! })
//!
//! #define SBI_CALL_0(ext, funct) SBI_CALL(ext, funct, 0, 0, 0, 0)
//!
//! static inline sbiret get_spec_version() {
//! SBI_CALL_0(EXTENSION_BASE, FUNCTION_BASE_GET_SPEC_VERSION)
//! }
//! ```
//!
//! # Implement RustSBI on machine environment
//!
//! Boards, SoC vendors, machine environment emulators and research projects may adapt RustSBI
//! to specific environments.
//! RustSBI project supports these demands either by discrete package or the Prototyping System.
//! Developers may choose the Prototyping System to shorten development time,
//! or discrete packages to include fine-grained features.
//!
//! Hypervisor and supervisor environment emulator developers may refer to
//! [Hypervisor and emulator development with RustSBI](#hypervisor-and-emulator-development-with-rustsbi)
//! for such purposes as RustSBI provide different set of features dedicated for emulated or virtual
//! environments.
//!
//! ## Use the Prototyping System
//!
//! The RustSBI Prototyping System aims to get your platform working with SBI in an afternoon.
//! It supports most RISC-V platforms available by providing scalable set of drivers and features.
//! It provides custom features such as Penglai TEE, DramForever's emulated hypervisor extension, and Raven
//! the firmware debugger framework.
//!
//! You may find further documents on [RustSBI Prototyping System repository](https://github.com/rustsbi/standalone).
//!
//! ## Discrete RustSBI package on bare metal RISC-V hardware
//!
//! Discrete packages provide developers with most scalability and complete control of underlying
//! hardware. It is ideal if advanced low power features, management cores and other features should
//! be used in this implementation.
//!
//! RustSBI supports discrete package by default. Create a new `#![no_std]` bare-metal package
//! to get started. Add following lines to `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! rustsbi = "0.3.2"
//! ```
//!
//! After hardware initialization process, the part of firmware with RustSBI linked should run on memory
//! blocks with fast accesses, as it would be called frequently by operating system.
//! If the supervisor is called by trap generator semantics, insert `rustsbi::RustSBI` structure
//! in your hart executor structure.
//!
//! ```rust
//! # struct Clint;
//! # struct MyPlatRfnc;
//! # struct MyPlatHsm;
//! # struct MyBoardPower;
//! # struct MyPlatPmu;
//! # #[cfg(not(feature = "legacy"))]
//! use rustsbi::RustSBI;
//!
//! # struct SupervisorContext;
//! /// Executes the supervisor within.
//! struct Executor {
//! ctx: SupervisorContext,
//! /* other environment variables ... */
//! # #[cfg(not(feature = "legacy"))]
//! # #[cfg(not(feature = "sbi_2_0"))] // fixme: remove in 0.4.0
//! sbi: RustSBI<Clint, Clint, MyPlatRfnc, MyPlatHsm, MyBoardPower, MyPlatPmu>,
//! /* custom_1: CustomSBI<...> */
//! }
//!
//! # struct Trap;
//! impl Executor {
//! /// A function that runs the provided supervisor, uses `&mut self` for it
//! /// modifies `SupervisorContext`.
//! ///
//! /// It returns for every Trap the supervisor produces. Its handler should read
//! /// and modify `self.ctx` if necessary. After handled, `run()` this structure
//! /// again or exit execution process.
//! pub fn run(&mut self) -> Trap {
//! todo!("fill in generic or platform specific trampoline procedure")
//! }
//! }
//! ```
//!
//! After each `run()`, process the trap returned with the RustSBI instance in executor.
//! Call `RustSBI::handle_ecall` and fill in developer provided `SupervisorContext` if necessary.
//!
//! ```no_run
//! # use sbi_spec::binary::SbiRet;
//! # struct RustSBI {} // Mock, prevent doc test error when feature singleton is enabled
//! # impl RustSBI { fn handle_ecall(&self, e: (), f: (), p: ()) -> SbiRet { SbiRet::success(0) } }
//! # struct Executor { sbi: RustSBI }
//! # #[derive(Copy, Clone)] enum Trap { Exception(Exception) }
//! # impl Trap { fn cause(&self) -> Self { *self } }
//! # #[derive(Copy, Clone)] enum Exception { SupervisorEcall }
//! # impl Executor {
//! # fn new(board_params: BoardParams) -> Executor { let _ = board_params; Executor { sbi: RustSBI {} } }
//! # fn run(&mut self) -> Trap { Trap::Exception(Exception::SupervisorEcall) }
//! # fn sbi_extension(&self) -> () { }
//! # fn sbi_function(&self) -> () { }
//! # fn sbi_params(&self) -> () { }
//! # fn fill_sbi_return(&mut self, ans: SbiRet) { let _ = ans; }
//! # }
//! # struct BoardParams;
//! # const MY_SPECIAL_EXIT: usize = 0x233;
//! /// Board specific power operations.
//! enum Operation {
//! Reboot,
//! Shutdown,
//! }
//!
//! # impl From<SbiRet> for Operation { fn from(_: SbiRet) -> Self { todo!() } }
//! /// Execute supervisor in given board parameters.
//! pub fn execute_supervisor(board_params: BoardParams) -> Operation {
//! let mut exec = Executor::new(board_params);
//! loop {
//! let trap = exec.run();
//! if let Trap::Exception(Exception::SupervisorEcall) = trap.cause() {
//! let ans = exec.sbi.handle_ecall(
//! exec.sbi_extension(),
//! exec.sbi_function(),
//! exec.sbi_params(),
//! );
//! if ans.error == MY_SPECIAL_EXIT {
//! break Operation::from(ans)
//! }
//! // This line would also advance `sepc` with `4` to indicate the `ecall` is handled.
//! exec.fill_sbi_return(ans);
//! } else {
//! // other trap types ...
//! }
//! }
//! }
//! ```
//!
//! Now, call supervisor execution function in your bare metal package to finish the discrete
//! package project.
//!
//! ```no_run
//! # #[cfg(nightly)] // disable checks
//! #[naked]
//! #[link_section = ".text.entry"]
//! #[export_name = "_start"]
//! unsafe extern "C" fn entry() -> ! {
//! #[link_section = ".bss.uninit"]
//! static mut SBI_STACK: [u8; LEN_STACK_SBI] = [0; LEN_STACK_SBI];
//!
//! // Note: actual assembly code varies between platforms.
//! // Double check documents before continue on.
//! core::arch::asm!(
//! // 1. Turn off interrupt
//! "csrw mie, zero",
//! // 2. Initialize programming langauge runtime
//! // only clear bss if hartid is zero
//! "csrr t0, mhartid",
//! "bnez t0, 2f",
//! // 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:",
//! // 3. Prepare stack for each hart
//! "la sp, {stack}",
//! "li t0, {per_hart_stack_size}",
//! "csrr t1, mhartid",
//! "addi t1, t1, 1",
//! "1: ",
//! "add sp, sp, t0",
//! "addi t1, t1, -1",
//! "bnez t1, 1b",
//! "j {rust_main}",
//! // 4. Clean up
//! "j {finalize}",
//! per_hart_stack_size = const LEN_STACK_PER_HART,
//! stack = sym SBI_STACK,
//! rust_main = sym rust_main,
//! finalize = sym finalize,
//! options(noreturn)
//! )
//! }
//!
//! # fn board_init_once() {}
//! # fn print_information_once() {}
//! # fn execute_supervisor(_bp: &()) -> Operation { Operation::Shutdown }
//! /// Power operation after main function
//! enum Operation {
//! Reboot,
//! Shutdown,
//! // Add board specific low power modes if necessary. This will allow the
//! // function `finalize` to operate on board specific power management chips.
//! }
//!
//! /// Rust entry, call in `entry` assembly function
//! extern "C" fn rust_main(_hartid: usize, opaque: usize) -> Operation {
//! // .. board initialization process ...
//! let board_params = board_init_once();
//! // .. print necessary information and rustsbi::LOGO ..
//! print_information_once();
//! // execute supervisor, return as Operation
//! execute_supervisor(&board_params)
//! }
//!
//! # fn wfi() {}
//! /// Perform board specific power operations
//! ///
//! /// The function here provides a stub to example power operations.
//! /// Actual board developers should provide with more practical communications
//! /// to external chips on power operation.
//! unsafe extern "C" fn finalize(op: Operation) -> ! {
//! match op {
//! Operation::Shutdown => {
//! // easiest way to make a hart look like powered off
//! loop { wfi(); }
//! }
//! Operation::Reboot => {
//! # fn entry() -> ! { loop {} } // mock
//! // easiest software reset is to jump to entry directly
//! entry()
//! }
//! // .. more power operations goes here ..
//! }
//! }
//! ```
//!
//! Now RustSBI would run on machine environment, you may start a kernel or use an SBI test suite
//! to check if it is properly implemented.
//!
//! Some platforms would provide system memory under different grades in speed and size to reduce product cost.
//! Those platforms would typically provide two parts of code memory, first one being relatively small, not fast
//! but instantly available after chip start, while the second one is larger in size but typically requires
//! memory training. The former one would include built-in SRAM memory, and the later would include
//! external SRAM or DDR memory. On those platforms, a first stage bootloader is typically needed to
//! train memory for later stages. In such situation, RustSBI implementation should be linked or concated
//! to the second stage bootloader, and the first stage could be a standalone binary package bundled with it.
//!
//! ## Discrete RustSBI package by singleton based interface
//!
//! *Note: Using singleton based RustSBI interface is discouraged in newer designs, for it requires
//! nightly Rust and global static memory. It takes extra bss and data storage to build a global singleton
//! interface. New designs should follow the [instance based interface](#discrete-rustsbi-package-on-bare-metal-risc-v-hardware)
//! to build discrete RustSBI packages.*
//!
//! Other than instance based interface, some users may find it convenient by using
//! global singleton semantics. To use it users should enable the `singleton` feature by:
//!
//! ```toml
//! [dependencies]
//! rustsbi = { version = "0.3.2", features = ["singleton"] }
//! ```
//!
//! RustSBI library will disable all instance based interfaces but provide `init_*`
//! functions to allow initialize global RustSBI singleton instance.
//! By enabling this feature, RustSBI uses unstable Rust features to create a universal
//! lock structure by using atomic `amo` instructions other than `lr`/`sc`.
//!
//! # Hypervisor and emulator development with RustSBI
//!
//! RustSBI crate supports to develop RISC-V emulators, and both Type-1 and Type-2 hypervisors.
//! Hypervisor developers may find it easy to handle standard SBI functions with an instance
//! based RustSBI interface.
//!
//! ## Hypervisors using RustSBI
//!
//! Both Type-1 and Type-2 hypervisors on RISC-V run on HS-mode hardware. Depending on demands
//! of virtualized systems, hypervisors may either provide transparent information from host machine
//! or provide another set of information to override the current environment. Notably,
//! RISC-V hypervisors do not have direct access to machine mode (M-mode) registers.
//!
//! RustSBI supports both by providing a `MachineInfo` structure in instance based interface.
//! If RISC-V hypervisors choose to use existing information on current machine, it may require
//! to call underlying M-mode environment using SBI calls and fill in information into `MachineInfo`.
//! If hypervisors use customized information other than taking the same one from the
//! environment they reside in, they may fill in custom one into `MachineInfo` structures.
//! When creating RustSBI instance, `MachineInfo` structure is required as an input of constructor.
//!
//! To begin with, disable default features in file `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! rustsbi = { version = "0.3.2", default-features = false }
//! ```
//!
//! This will disable default feature `machine` which will assume that RustSBI runs on M-mode directly,
//! which is not appropriate in our purpose. After that, a `RustSBI` instance may be placed
//! in the virtual machine structure to prepare for SBI environment:
//!
//! ```rust
//! # struct RustSBI<>();
//! struct VmHart {
//! // other fields ...
//! env: RustSBI</* Types, .. */>,
//! }
//! ```
//!
//! When the virtual machine hart traps into hypervisor, its code should decide whether
//! this trap is an SBI environment call. If that is true, pass in parameters by `env.handle_ecall`
//! function. RustSBI will handle with SBI standard constants, call corresponding extension module
//! and provide parameters according to the extension and function IDs.
//!
//! Crate `rustsbi` adapts to standard RISC-V SBI calls.
//! If the hypervisor have custom SBI extensions that RustSBI does not recognize, those extension
//! and function IDs can be checked before calling RustSBI `env.handle_ecall`.
//!
//! ```no_run
//! # use sbi_spec::binary::SbiRet;
//! # struct MyExtensionEnv {}
//! # impl MyExtensionEnv { fn handle_ecall(&self, params: ()) -> SbiRet { SbiRet::success(0) } }
//! # struct RustSBI {} // Mock, prevent doc test error when feature singleton is enabled
//! # impl RustSBI { fn handle_ecall(&self, params: ()) -> SbiRet { SbiRet::success(0) } }
//! # struct VmHart { my_extension_env: MyExtensionEnv, env: RustSBI }
//! # #[derive(Copy, Clone)] enum Trap { Exception(Exception) }
//! # impl Trap { fn cause(&self) -> Self { *self } }
//! # #[derive(Copy, Clone)] enum Exception { SupervisorEcall }
//! # impl VmHart {
//! # fn new() -> VmHart { VmHart { my_extension_env: MyExtensionEnv {}, env: RustSBI {} } }
//! # fn run(&mut self) -> Trap { Trap::Exception(Exception::SupervisorEcall) }
//! # fn trap_params(&self) -> () { }
//! # fn fill_in(&mut self, ans: SbiRet) { let _ = ans; }
//! # }
//! let mut hart = VmHart::new();
//! loop {
//! let trap = hart.run();
//! if let Trap::Exception(Exception::SupervisorEcall) = trap.cause() {
//! // Firstly, handle custom extensions
//! let my_extension_sbiret = hart.my_extension_env.handle_ecall(hart.trap_params());
//! // If custom extension handles correctly, fill in its result and continue to hart.
//! // The custom handler may handle `probe_extension` in `base` extension as well
//! // to allow detections to whether custom extension exists.
//! if my_extension_sbiret != SbiRet::not_supported() {
//! hart.fill_in(my_extension_sbiret);
//! continue;
//! }
//! // Then, if it's not a custom extension, handle it using standard SBI handler.
//! let standard_sbiret = hart.env.handle_ecall(hart.trap_params());
//! hart.fill_in(standard_sbiret);
//! }
//! }
//! ```
//!
//! RustSBI would interact well with custom extension environments in this way.
//!
//! ## Emulators using RustSBI
//!
//! RustSBI library may be used to write RISC-V emulators. Other than hardware accelereted binary
//! translation methods, emulators typically do not use host hardware specific features,
//! thus may build and run on any architecture.
//! Like hardware RISC-V implementations, software emulated RISC-V environment would still need SBI
//! implementation to support supervisor environment.
//!
//! Writing emulators would follow the similiar process with writing hypervisors, see
//! [Hypervisors using RustSBI](#hypervisors-using-rustsbi) for details.
//!
//! # Download binary file: the Prototyping System vs discrete packages
//!
//! RustSBI ecosystem would typically provide support for most platforms. Those support packages
//! would be provided either from the RustSBI Prototyping System or vendor provided discrete SBI
//! implementation packages.
//!
//! The RustSBI Prototyping System is a universal support package provided by RustSBI ecosystem.
//! It is designed to save development time while providing most SBI feature possible.
//! It also includes a universal test kernel to allow testing SBI implementations on current environment.
//! Users may choose to download from [Prototyping System repository](https://github.com/rustsbi/standalone)
//! to get various types of RustSBI packages for their boards.
//! Vendors and contributors may find it easy to adapt new SoCs and boards into Prototyping System.
//!
//! Discrete SBI packages are SBI environment support packages specially designed for one board
//! or SoC, it will be provided by board vendor or RustSBI ecosystem.
//! Vendors may find it easy to include fine grained features in each support package, but the
//! maintainence situation would vary between vendors and it would likely to cost a lot of time
//! to develop from a bare-metal executable. Users may find a boost in performance, energy saving
//! indexes and feature granularity in discrete packages, but it would depends on whether the
//! vendor provide it.
//!
//! To download binary package for the Prototyping System, visit its project website for a download link.
//! To download them for discrete packages, RustSBI users may visit distribution source of SoC or board
//! manufacturers. Additionally, users may visit [the awesome page](https://github.com/rustsbi/awesome-rustsbi)
//! for a curated list ofboth Prototyping System and discrete packages provided by RustSBI ecosystem.
//!
//! # Notes for RustSBI developers
//!
//! Following useful hints are for firmware and kernel developers when working with SBI and RustSBI.
//!
//! ## RustSBI is a library for interfaces
//!
//! This library adapts to individual Rust traits to provide basic SBI features.
//! When building for own platform, implement traits in this library and pass them to the functions
//! begin with `init`. After that, you may call `rustsbi::ecall`, `RustSBI::handle_ecall` or
//! similiar functions in your own exception handler.
//! It would dispatch parameters from supervisor to the traits to execute SBI functions.
//!
//! The library also implements useful functions which may help with platform specific binaries.
//! The `LOGO` can be printed if necessary when the binary is initializing.
//!
//! Note that this crate is a library which contains common building blocks in SBI implementation.
//! The RustSBI ecosystem would provide different level of support for each board, those support
//! packages would use `rustsbi` crate as library to provide different type of SBI binary releases.
//!
//! ## Hardware discovery and feature detection
//!
//! According to the RISC-V SBI specification, SBI itself does not specify any method for hardware discovery.
//! The supervisor software must rely on the other industry standard hardware
//! discovery methods (i.e. Device Tree or ACPI) for that purpose.
//!
//! To detect any feature under bare metal or under supervisor level, developers may depend on
//! any hardware discovery methods, or use try-execute-trap method to detect any instructions or
//! CSRs. If SBI is implemented in user level emulators, it may requires to depend on operating
//! system calls or use the signal trap method to detect any RISC-V core features.
//!
//! ## Legacy SBI extension
//!
//! *Note: RustSBI legacy support is only designed for backward compability of deprecated legacy RISC-V
//! SBI standard. It's disabled by default and it's not suggested to include legacy extensions in newer
//! firmware designs. Extensions other than legacy console are replaced by individual extensions in SBI.
//! Kernels are not suggested to use legacy extensions in practice.
//! If you are a kernel developer, newer designs should consider relying on each SBI extension other than
//! legacy extensions.*
//!
//! The SBI includes legacy extension which dated back to SBI 0.1 specification. Most of its features
//! are replaced by individual SBI extensions, thus the entire legacy extension is deprecated by
//! SBI version 0.2. However, some users may find out SBI 0.1 legacy console useful in some situations
//! even if it's deprecated.
//!
//! RustSBI keeps SBI 0.1 legacy support under feature gate `legacy`. To use RustSBI with deprecated
//! legacy feature, you may change dependency configuration to:
//!
//! ```toml
//! [dependencies]
//! rustsbi = { version = "0.3.2", features = ["legacy"] }
//! ```
#![no_std]
#![cfg_attr(feature = "singleton", feature(ptr_metadata))]
#[cfg(feature = "legacy")]
#[doc(hidden)]
#[macro_use]
pub mod legacy_stdio;
mod base;
mod console;
#[cfg(feature = "singleton")]
mod ecall;
mod hart_mask;
mod hsm;
#[cfg(not(feature = "legacy"))]
mod instance;
mod ipi;
mod memory_range;
mod pmu;
mod reset;
mod rfence;
mod timer;
#[cfg(feature = "singleton")]
mod util;
/// The RustSBI logo without blank lines on the beginning
pub const LOGO: &str = r".______ __ __ _______.___________. _______..______ __
| _ \ | | | | / | | / || _ \ | |
| |_) | | | | | | (----`---| |----`| (----`| |_) || |
| / | | | | \ \ | | \ \ | _ < | |
| |\ \----.| `--' |.----) | | | .----) | | |_) || |
| _| `._____| \______/ |_______/ |__| |_______/ |______/ |__|";
// RustSBI supports RISC-V SBI specification 2.0-rc1
const SBI_SPEC_MAJOR: usize = match () {
#[cfg(feature = "sbi_2_0")]
() => 2,
#[cfg(not(feature = "sbi_2_0"))]
() => 1,
};
const SBI_SPEC_MINOR: usize = 0;
/// RustSBI implementation ID: 4
///
/// Ref: https://github.com/riscv-non-isa/riscv-sbi-doc/pull/61
const IMPL_ID_RUSTSBI: usize = 4;
const RUSTSBI_VERSION_MAJOR: usize = (env!("CARGO_PKG_VERSION_MAJOR").as_bytes()[0] - b'0') as _;
const RUSTSBI_VERSION_MINOR: usize = (env!("CARGO_PKG_VERSION_MINOR").as_bytes()[0] - b'0') as _;
const RUSTSBI_VERSION_PATCH: usize = (env!("CARGO_PKG_VERSION_PATCH").as_bytes()[0] - b'0') as _;
const RUSTSBI_VERSION: usize =
(RUSTSBI_VERSION_MAJOR << 16) + (RUSTSBI_VERSION_MINOR << 8) + RUSTSBI_VERSION_PATCH;
/// RustSBI version as a string
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub extern crate sbi_spec as spec;
pub use console::Console;
#[cfg(feature = "singleton")]
pub use ecall::handle_ecall as ecall;
pub use hart_mask::HartMask;
pub use hsm::Hsm;
#[cfg(not(feature = "legacy"))]
pub use instance::{Builder, RustSBI};
pub use ipi::Ipi;
#[cfg(feature = "legacy")]
#[doc(hidden)]
pub use legacy_stdio::{legacy_stdio_getchar, legacy_stdio_putchar};
pub use memory_range::Physical;
pub use pmu::Pmu;
pub use reset::Reset;
pub use rfence::Rfence as Fence;
pub use timer::Timer;
#[cfg(not(feature = "machine"))]
pub use instance::MachineInfo;
#[cfg(feature = "singleton")]
pub use {
hsm::init_hsm, ipi::init_ipi, pmu::init_pmu, reset::init_reset,
rfence::init_rfence as init_remote_fence, timer::init_timer,
};