1use std::ops::Range;
6
7use anyhow::bail;
8use base::fold_into_i32;
9use base::warn;
10use base::NegativeI32;
11use base::U31;
12use hypervisor::HypercallAbi;
13use static_assertions::const_assert_ne;
14use uuid::uuid;
15use uuid::Uuid;
16use vm_control::DeviceId;
17use vm_control::PlatformDeviceId;
18
19use crate::BusAccessInfo;
20use crate::BusDevice;
21use crate::BusDeviceSync;
22use crate::Suspendable;
23
24#[repr(i32)]
28#[derive(Clone, Copy, Debug)]
29enum SmcccTrngError {
30 NotSupported = -1,
31 InvalidParameters = -2,
32 #[allow(unused)]
33 NoEntropy = -3,
34}
35
36impl SmcccTrngError {
37 const fn as_i32(&self) -> i32 {
38 *self as _
39 }
40}
41
42impl From<SmcccTrngError> for i32 {
43 fn from(e: SmcccTrngError) -> Self {
44 e.as_i32()
45 }
46}
47
48impl From<SmcccTrngError> for NegativeI32 {
49 fn from(e: SmcccTrngError) -> Self {
50 Self::new(e.as_i32()).unwrap()
51 }
52}
53
54pub struct SmcccTrng {}
58
59impl Default for SmcccTrng {
60 fn default() -> Self {
61 Self::new()
62 }
63}
64
65impl SmcccTrng {
66 pub const HVC32_FID_RANGE: Range<u32> = 0x8400_0050..0x8400_0060;
68 pub const HVC64_FID_RANGE: Range<u32> = 0xC400_0050..0xC400_0060;
70
71 const FID_TRNG_VERSION: u32 = 0x8400_0050;
72 const FID_TRNG_FEATURES: u32 = 0x8400_0051;
73 const FID_TRNG_GET_UUID: u32 = 0x8400_0052;
74 const FID_TRNG_RND32: u32 = 0x8400_0053;
75 const FID_TRNG_RND64: u32 = 0xC400_0053;
76
77 const VERSION: (u16, u16) = (1, 0);
78 const UUID: Uuid = uuid!("534d4343-4354-824e-872d-43726f73564d");
82
83 pub fn new() -> Self {
85 Self {}
86 }
87
88 fn version(&self) -> Result<U31, SmcccTrngError> {
89 Ok(U31::new(((Self::VERSION.0 as u32) << 16) | (Self::VERSION.1 as u32)).unwrap())
90 }
91
92 fn features(&self, func_id: u32) -> Result<U31, SmcccTrngError> {
93 const AVAILABLE: U31 = U31::new(0).unwrap();
94 match func_id {
95 Self::FID_TRNG_VERSION => Ok(AVAILABLE),
96 Self::FID_TRNG_FEATURES => Ok(AVAILABLE),
97 Self::FID_TRNG_GET_UUID => Ok(AVAILABLE),
98 Self::FID_TRNG_RND32 => Ok(AVAILABLE),
99 Self::FID_TRNG_RND64 => Ok(AVAILABLE),
100 _ => Err(SmcccTrngError::NotSupported),
101 }
102 }
103
104 fn get_uuid(&self) -> Result<[u32; 4], SmcccTrngError> {
105 const UUID: u128 = SmcccTrng::UUID.to_u128_le();
106 const R3: u32 = (UUID >> (3 * u32::BITS)) as _;
107 const R2: u32 = (UUID >> (2 * u32::BITS)) as _;
108 const R1: u32 = (UUID >> u32::BITS) as _;
109 const R0: u32 = UUID as _;
110 const_assert_ne!(R0, u32::MAX);
112
113 Ok([R0, R1, R2, R3])
114 }
115
116 fn rnd32(&self, n_bits: u32) -> Result<[u32; 3], SmcccTrngError> {
117 match n_bits.div_ceil(u32::BITS) {
118 1 => Ok([rand::random(), 0, 0]),
119 2 => Ok([rand::random(), rand::random(), 0]),
120 3 => Ok([rand::random(), rand::random(), rand::random()]),
121 n => {
122 warn!("SMCCC TRNG: Invalid request for {n} u32 words");
123 Err(SmcccTrngError::InvalidParameters)
124 }
125 }
126 }
127
128 fn rnd64(&self, n_bits: u64) -> Result<[u64; 3], SmcccTrngError> {
129 match n_bits.div_ceil(u64::BITS.into()) {
130 1 => Ok([rand::random(), 0, 0]),
131 2 => Ok([rand::random(), rand::random(), 0]),
132 3 => Ok([rand::random(), rand::random(), rand::random()]),
133 n => {
134 warn!("SMCCC TRNG: Invalid request for {n} u64 words");
135 Err(SmcccTrngError::InvalidParameters)
136 }
137 }
138 }
139}
140
141fn as_signed_usize(i: i32) -> usize {
142 let sign_extended = i64::from(i);
143 (sign_extended as u64).try_into().unwrap()
144}
145
146impl BusDevice for SmcccTrng {
147 fn device_id(&self) -> DeviceId {
148 PlatformDeviceId::SmcccTrng.into()
149 }
150
151 fn debug_label(&self) -> String {
152 "SmcccTrng".to_owned()
153 }
154
155 fn handle_hypercall(&self, abi: &mut HypercallAbi) -> anyhow::Result<()> {
156 let regs = match abi.hypercall_id() as u32 {
157 Self::FID_TRNG_VERSION => {
158 let r0 = as_signed_usize(fold_into_i32(self.version()));
159 [r0, 0, 0, 0]
160 }
161 Self::FID_TRNG_FEATURES => {
162 let feat = (*abi.get_argument(0).unwrap()) as u32;
163 let r0 = as_signed_usize(fold_into_i32(self.features(feat)));
164 [r0, 0, 0, 0]
165 }
166 Self::FID_TRNG_GET_UUID => match self.get_uuid() {
167 Ok(uuid) => [
168 uuid[0].try_into().unwrap(),
169 uuid[1].try_into().unwrap(),
170 uuid[2].try_into().unwrap(),
171 uuid[3].try_into().unwrap(),
172 ],
173 Err(e) => [as_signed_usize(e.into()), 0, 0, 0],
174 },
175 Self::FID_TRNG_RND32 => {
176 let n_bits = (*abi.get_argument(0).unwrap()) as u32;
177 match self.rnd32(n_bits) {
178 Ok(entropy) => [
179 0,
180 entropy[0].try_into().unwrap(),
181 entropy[1].try_into().unwrap(),
182 entropy[2].try_into().unwrap(),
183 ],
184 Err(e) => [as_signed_usize(e.into()), 0, 0, 0],
185 }
186 }
187 Self::FID_TRNG_RND64 => {
188 let n_bits = (*abi.get_argument(0).unwrap()).try_into().unwrap();
189 match self.rnd64(n_bits) {
190 Ok(entropy) => [
191 0,
192 entropy[0].try_into().unwrap(),
193 entropy[1].try_into().unwrap(),
194 entropy[2].try_into().unwrap(),
195 ],
196 Err(e) => [as_signed_usize(e.into()), 0, 0, 0],
197 }
198 }
199 fid => bail!("SmcccTrng: Call {fid:#x} is not implemented"),
200 };
201 abi.set_results(®s);
202 Ok(())
203 }
204
205 fn read(&mut self, _info: BusAccessInfo, _data: &mut [u8]) {
206 unimplemented!("SmcccTrng: read not supported");
207 }
208
209 fn write(&mut self, _info: BusAccessInfo, _data: &[u8]) {
210 unimplemented!("SmcccTrng: write not supported");
211 }
212}
213
214impl BusDeviceSync for SmcccTrng {
215 fn read(&self, _info: BusAccessInfo, _data: &mut [u8]) {
216 unimplemented!("SmcccTrng: read not supported");
217 }
218
219 fn write(&self, _info: BusAccessInfo, _data: &[u8]) {
220 unimplemented!("SmcccTrng: write not supported");
221 }
222}
223
224impl Suspendable for SmcccTrng {}