main.rs 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. // RustSBI derive example. To derive RustSBI implementation, first we use `RustSBI`
  2. // derive macro using code `use rustsbi::RustSBI`.
  3. use rustsbi::RustSBI;
  4. mod commons;
  5. use commons::*;
  6. // Now we create a new structure and fill fields into it.
  7. #[derive(RustSBI)]
  8. struct MySBI {
  9. // We include a SBI RFNC (rustsbi::Fence) extension implementation by including
  10. // a struct field. The name `fence` is special; RustSBI derive macro will identify
  11. // fence implementation using the variable name. Valid names are listed in RISC-V
  12. // SBI specification.
  13. // Here we include a mock MyFence implementation; this structure prints to output
  14. // then the SBI function `remote_fence_i` is called. Actual code should use any
  15. // machine-mode mechanism as a valid RISC-V SBI implementation.
  16. fence: MyFence,
  17. // Machine information is required by RISC-V SBI specification to provide supervisor
  18. // with some method to read `mvendorid`, `marchid` and `mimpid` values from the SBI
  19. // environment.
  20. // By default RustSBI requires the implementation to declare machine info values
  21. // for the environment explicitly, which is suitable for emulators and hypervisors.
  22. // For bare metal developers, RustSBI also provides a way to read from machine-mode
  23. // CSR accesses; developers should enable RustSBI feature `machine` in this case.
  24. // The name `info` is also special, like the name `fence` we have mentioned;
  25. // RustSBI identifies machine information from the field name `info`.
  26. info: MyEnvInfo,
  27. }
  28. // We have a properly defined RustSBI implementation called `MySBI`. Now `MySBI`
  29. // implements Rust trait `rustsbi::RustSBI` with derived code dealing with RISC-V
  30. // SBI extensions, functions and forward it to all fields of `MySBI` with minimum
  31. // runtime cost. Let's try to use it!
  32. fn main() {
  33. // In main program, create an SBI instance. It's normally located in global storages
  34. // like global variables or stack of the main function. As a mock example we define it
  35. // as a stack variable for now.
  36. let sbi = MySBI {
  37. fence: MyFence,
  38. info: MyEnvInfo,
  39. };
  40. // In S-mode environment call handler, call the `handle_ecall` of the SBI instance.
  41. // We mock this method by providing consts here; actual implementation should fill
  42. // `extension`, `function` and `param` from trap context.
  43. let ret = sbi.handle_ecall(sbi_spec::rfnc::EID_RFNC, 0, [0; 6]);
  44. // Finally, fill SBI return value into exception environment and return.
  45. // In bare metal: fill `a0` and `a1` register in trap context with `SbiRet` value;
  46. // In hypervisor: fill guest supervisor `a0` and `a1` with `SbiRet` value.
  47. let _ = ret; // It should be filled into context on real programs.
  48. // Congratulations! You have learned how to use RustSBI to create your SBI implementaion.
  49. // You may consider using the RustSBI Prototyping System, build a standalone
  50. // binary package with runtime environment from scratch, or begin with your hypervisor
  51. // development.
  52. // Additionally, we present another mock function suggesting this instance is running
  53. // RustSBI by showing that SBI implementation ID equals 4.
  54. let ret = sbi.handle_ecall(0x10, 0x1, [0; 6]);
  55. println!("SBI implementation ID: {:x?}", ret.value);
  56. }