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
//! Provides high-level access to the EEPROM APIs.
use crate::error::Error;
use crate::helpers::Ignore;
use minicbor::bytes::ByteSlice;
use oc_wasm_futures::invoke::{component_method, Buffer};
use oc_wasm_helpers::{error::NullAndStringOr, Lockable};
use oc_wasm_safe::{
component::{Invoker, MethodCallError},
extref, Address,
};
/// The type name for EEPROM components.
pub const TYPE: &str = "eeprom";
/// An EEPROM component.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Eeprom(Address);
impl Eeprom {
/// Creates a wrapper around an EEPROM.
///
/// The `address` parameter is the address of the EEPROM. It is not checked for correctness at
/// this time because network topology could change after this function returns; as such, each
/// usage of the value may fail instead.
#[must_use = "This function is only useful for its return value"]
pub fn new(address: Address) -> Self {
Self(address)
}
/// Returns the address of the EEPROM.
#[must_use = "This function is only useful for its return value"]
pub fn address(&self) -> &Address {
&self.0
}
}
impl<'invoker, 'buffer, B: 'buffer + Buffer> Lockable<'invoker, 'buffer, B> for Eeprom {
type Locked = Locked<'invoker, 'buffer, B>;
fn lock(&self, invoker: &'invoker mut Invoker, buffer: &'buffer mut B) -> Self::Locked {
Locked {
address: self.0,
invoker,
buffer,
}
}
}
/// An EEPROM component on which methods can be invoked.
///
/// This type combines an EEPROM address, an [`Invoker`](Invoker) that can be used to make method
/// calls, and a scratch buffer used to perform CBOR encoding and decoding. A value of this type
/// can be created by calling [`Eeprom::lock`](Eeprom::lock), and it can be dropped to return the
/// borrow of the invoker and buffer to the caller so they can be reused for other purposes.
///
/// The `'invoker` lifetime is the lifetime of the invoker. The `'buffer` lifetime is the lifetime
/// of the buffer. The `B` type is the type of scratch buffer to use.
pub struct Locked<'invoker, 'buffer, B: Buffer> {
/// The component address.
address: Address,
/// The invoker.
invoker: &'invoker mut Invoker,
/// The buffer.
buffer: &'buffer mut B,
}
impl<'invoker, 'buffer, B: Buffer> Locked<'invoker, 'buffer, B> {
/// Returns the contents of the main storage area.
///
/// In an EEPROM used for booting, the main storage area contains the BIOS code.
///
/// The returned byte slice points into, and therefore retains ownership of, the scratch
/// buffer. Consequently, the `Locked` is consumed and cannot be reused.
///
/// # Errors
/// * [`BadComponent`](Error::BadComponent)
/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
pub async fn get(self) -> Result<&'buffer [u8], Error> {
let ret: (&ByteSlice,) =
component_method::<(), _, _>(self.invoker, self.buffer, &self.address, "get", None)
.await?;
Ok(ret.0)
}
/// Writes to the main storage area.
///
/// In an EEPROM used for booting, the main storage area contains the BIOS code.
///
/// # Errors
/// * [`BadComponent`](Error::BadComponent)
/// * [`DataTooLarge`](Error::DataTooLarge)
/// * [`NotEnoughEnergy`](Error::NotEnoughEnergy)
/// * [`StorageReadOnly`](Error::StorageReadOnly)
/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
pub async fn set(&mut self, data: &[u8]) -> Result<(), Error> {
// SAFETY: component_method() both encodes and submits the CBOR in one go.
let data = unsafe { extref::Bytes::new(data) };
Self::map_errors::<Ignore>(
component_method(
self.invoker,
self.buffer,
&self.address,
"set",
Some(&(data,)),
)
.await,
Error::DataTooLarge,
)?;
Ok(())
}
/// Returns the label, if it has one.
///
/// The label is displayed in the item’s tooltip.
///
/// The returned string slice points into, and therefore retains ownership of, the scratch
/// buffer. Consequently, the `Locked` is consumed and cannot be reused.
///
/// # Errors
/// * [`BadComponent`](Error::BadComponent)
/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
pub async fn get_label(self) -> Result<&'buffer str, Error> {
let ret: (&'buffer str,) = component_method::<(), _, _>(
self.invoker,
self.buffer,
&self.address,
"getLabel",
None,
)
.await?;
Ok(ret.0)
}
/// Sets the label and returns the new label, which may be truncated.
///
/// The label is displayed in the item’s tooltip.
///
/// The returned string slice points into, and therefore retains ownership of, the scratch
/// buffer. Consequently, the `Locked` is consumed and cannot be reused.
///
/// # Errors
/// * [`BadComponent`](Error::BadComponent)
/// * [`StorageReadOnly`](Error::StorageReadOnly)
/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
pub async fn set_label(self, label: &str) -> Result<&'buffer str, Error> {
// SAFETY: component_method() both encodes and submits the CBOR in one go.
let label = unsafe { extref::String::new(label) };
let ret: (&'buffer str,) = Self::map_errors(
component_method(
self.invoker,
self.buffer,
&self.address,
"setLabel",
Some(&(label,)),
)
.await,
Error::BadComponent(oc_wasm_safe::error::Error::Unknown),
)?;
Ok(ret.0)
}
/// Returns the capacity, in bytes, of the main storage area.
///
/// # Errors
/// * [`BadComponent`](Error::BadComponent)
/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
pub async fn get_size(&mut self) -> Result<usize, Error> {
let ret: (usize,) =
component_method::<(), _, _>(self.invoker, self.buffer, &self.address, "getSize", None)
.await?;
Ok(ret.0)
}
/// Returns the CRC32 of the contents of the main storage area.
///
/// # Errors
/// * [`BadComponent`](Error::BadComponent)
/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
pub async fn get_checksum(&mut self) -> Result<u32, Error> {
let ret: (&'_ str,) = component_method::<(), _, _>(
self.invoker,
self.buffer,
&self.address,
"getChecksum",
None,
)
.await?;
let ret = ret.0;
if let Ok(ret) = u32::from_str_radix(ret, 16) {
Ok(ret)
} else {
Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
}
}
/// Makes the EEPROM read-only.
///
/// A read-only EEPROM’s main storage area and label cannot be modified. Its volatile data area
/// can still be modified. A read-only EEPROM cannot be made read-write again later.
///
/// For safety, the checksum value must be passed as a parameter.
///
/// If the EEPROM is already read-only, this method successfully does nothing (assuming the
/// checksum is correct).
///
/// # Errors
/// * [`BadComponent`](Error::BadComponent)
/// * [`ChecksumMismatch`](Error::ChecksumMismatch)
/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
pub async fn make_read_only(&mut self, checksum: u32) -> Result<(), Error> {
let checksum = alloc::format!("{checksum:08x}");
// SAFETY: component_method() both encodes and submits the CBOR in one go.
let checksum = unsafe { extref::String::new(&checksum) };
Self::map_errors::<Ignore>(
component_method(
self.invoker,
self.buffer,
&self.address,
"makeReadonly",
Some(&(checksum,)),
)
.await,
Error::BadComponent(oc_wasm_safe::error::Error::Unknown),
)?;
Ok(())
}
/// Returns the capacity, in bytes, of the volatile data area.
///
/// # Errors
/// * [`BadComponent`](Error::BadComponent)
/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
pub async fn get_data_size(&mut self) -> Result<usize, Error> {
let ret: (usize,) = component_method::<(), _, _>(
self.invoker,
self.buffer,
&self.address,
"getDataSize",
None,
)
.await?;
Ok(ret.0)
}
/// Returns the contents of the volatile data area.
///
/// In an EEPROM used for booting, the volatile data area contains the UUID of the filesystem
/// to prefer booting from.
///
/// The returned byte slice points into, and therefore retains ownership of, the scratch
/// buffer. Consequently, the `Locked` is consumed and cannot be reused.
///
/// # Errors
/// * [`BadComponent`](Error::BadComponent)
/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
pub async fn get_data(self) -> Result<&'buffer [u8], Error> {
let ret: (&ByteSlice,) =
component_method::<(), _, _>(self.invoker, self.buffer, &self.address, "getData", None)
.await?;
Ok(ret.0)
}
/// Writes to the volatile data area.
///
/// In an EEPROM used for booting, the volatile data area contains the UUID of the filesystem
/// to prefer booting from.
///
/// # Errors
/// * [`BadComponent`](Error::BadComponent)
/// * [`DataTooLarge`](Error::DataTooLarge)
/// * [`NotEnoughEnergy`](Error::NotEnoughEnergy)
/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
pub async fn set_data(&mut self, data: &[u8]) -> Result<(), Error> {
// SAFETY: component_method() both encodes and submits the CBOR in one go.
let data = unsafe { extref::Bytes::new(data) };
Self::map_errors::<Ignore>(
component_method(
self.invoker,
self.buffer,
&self.address,
"setData",
Some(&(data,)),
)
.await,
Error::DataTooLarge,
)?;
Ok(())
}
/// Given a `Result<NullAndStringOr<T>, MethodCallError>`, maps the errors (both exceptions and
/// null-and-string-style errors) to appropriate error constants, returning any success object
/// unmodified. [`BadParameters`](MethodCallError::BadParameters) is mapped to a specified
/// value.
///
/// # Errors
/// * [`BadComponent`](Error::BadComponent) is returned for any unrecognized error.
/// * [`ChecksumMismatch`](Error::ChecksumMismatch)
/// * [`NotEnoughEnergy`](Error::NotEnoughEnergy)
/// * [`StorageReadOnly`](Error::StorageReadOnly)
/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
fn map_errors<T>(
x: Result<NullAndStringOr<'_, T>, MethodCallError<'_>>,
bad_parameters: Error,
) -> Result<T, Error> {
match x {
Ok(NullAndStringOr::Ok(x)) => Ok(x),
Ok(NullAndStringOr::Err("incorrect checksum")) => Err(Error::ChecksumMismatch),
Ok(NullAndStringOr::Err("not enough energy")) => Err(Error::NotEnoughEnergy),
Ok(NullAndStringOr::Err("storage is readonly")) => Err(Error::StorageReadOnly),
Ok(NullAndStringOr::Err(_)) => {
Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
}
Err(MethodCallError::BadParameters(_)) => Err(bad_parameters),
Err(MethodCallError::TooManyDescriptors) => Err(Error::TooManyDescriptors),
Err(e) => Err(Error::BadComponent(e.into())),
}
}
}