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,
};