dynamic.rs 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. //! Frequently used first boot stage dynamic information on RISC-V.
  2. use core::{mem::size_of, ops::Range};
  3. use riscv::register::mstatus;
  4. /// M-mode firmware dynamic information.
  5. #[derive(Clone, Copy)]
  6. #[repr(C)]
  7. pub struct DynamicInfo {
  8. /// Dynamic information magic value.
  9. pub magic: usize,
  10. /// Version of dynamic information.
  11. pub version: usize,
  12. /// Address of the next boot-loading stage.
  13. pub next_addr: usize,
  14. /// RISC-V privilege mode of the next boot-loading stage.
  15. pub next_mode: usize,
  16. /// M-mode firmware options; its definition varies between SBI implementations.
  17. pub options: usize,
  18. /// Boot hart ID of current environment.
  19. pub boot_hart: usize,
  20. }
  21. // Definition of `boot_hart` can be found at:
  22. // https://github.com/riscv-software-src/opensbi/blob/019a8e69a1dc0c0f011fabd0372e1ba80e40dd7c/include/sbi/fw_dynamic.h#L75
  23. const DYNAMIC_INFO_VALID_ADDRESSES: Range<usize> = 0x1000..0xf000;
  24. const NEXT_ADDR_VALID_ADDRESSES: Range<usize> = 0x80000000..0x90000000;
  25. pub(crate) const MAGIC: usize = 0x4942534f;
  26. const SUPPORTED_VERSION: Range<usize> = 2..3;
  27. pub struct DynamicReadError {
  28. pub bad_paddr: Option<usize>,
  29. pub bad_magic: Option<usize>,
  30. pub bad_version: Option<usize>,
  31. }
  32. // TODO unconstrained lifetime
  33. pub fn read_paddr(paddr: usize) -> Result<DynamicInfo, DynamicReadError> {
  34. let mut error = DynamicReadError {
  35. bad_paddr: None,
  36. bad_magic: None,
  37. bad_version: None,
  38. };
  39. // check pointer before dereference
  40. if !DYNAMIC_INFO_VALID_ADDRESSES.contains(&paddr)
  41. || !DYNAMIC_INFO_VALID_ADDRESSES.contains(&(paddr + size_of::<DynamicInfo>()))
  42. {
  43. error.bad_paddr = Some(paddr);
  44. return Err(error);
  45. }
  46. let ans = unsafe { *(paddr as *const DynamicInfo) };
  47. if ans.magic != MAGIC {
  48. error.bad_magic = Some(ans.magic);
  49. }
  50. if !SUPPORTED_VERSION.contains(&ans.version) {
  51. error.bad_version = Some(ans.version);
  52. }
  53. if error.bad_magic.is_some() || error.bad_version.is_some() {
  54. return Err(error);
  55. }
  56. Ok(ans)
  57. }
  58. pub struct DynamicError<'a> {
  59. pub invalid_mpp: bool,
  60. pub invalid_next_addr: bool,
  61. pub bad_info: &'a DynamicInfo,
  62. }
  63. pub fn mpp_next_addr(info: &DynamicInfo) -> Result<(mstatus::MPP, usize), DynamicError> {
  64. let mut error = DynamicError {
  65. invalid_mpp: false,
  66. invalid_next_addr: false,
  67. bad_info: info,
  68. };
  69. // fail safe, errors will be aggregated after whole checking process.
  70. let next_addr_valid = NEXT_ADDR_VALID_ADDRESSES.contains(&info.next_addr);
  71. let mpp_valid = matches!(info.next_mode, 0 | 1 | 3);
  72. if !next_addr_valid {
  73. error.invalid_next_addr = true;
  74. }
  75. if !mpp_valid {
  76. error.invalid_mpp = true;
  77. }
  78. if !next_addr_valid || !mpp_valid {
  79. return Err(error);
  80. }
  81. let mpp = match info.next_mode {
  82. 3 => mstatus::MPP::Machine,
  83. 1 => mstatus::MPP::Supervisor,
  84. // pattern `_` avoids `unreachable!`` which introduces panic handler.
  85. 0 | _ => mstatus::MPP::User,
  86. };
  87. Ok((mpp, info.next_addr))
  88. }