1#![no_std]
6
7use deku::{DekuContainerWrite, DekuError};
8use flagset::FlagSet;
9use hmac::Mac;
10use log::debug;
11use mctp::AsyncRespChannel;
12use nvme::{
13 AdminGetLogPageLidRequestType, LidSupportedAndEffectsFlags, LogPageAttributes,
14 mi::ResponseStatus,
15};
16use uuid::Uuid;
17
18pub mod nvme;
19mod pcie;
20mod wire;
21
22extern crate deku;
23
24const MAX_CONTROLLERS: usize = 2;
25const MAX_NAMESPACES: usize = 4;
26const MAX_PORTS: usize = 2;
27const MAX_NIDTS: usize = 2;
28
29pub mod smbus {
30 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
31 pub enum BusFrequency {
32 NotSupported,
33 Freq100Khz,
34 Freq400Khz,
35 Freq1Mhz,
36 }
37}
38
39#[derive(Debug)]
40pub enum CommandEffect {
41 SetMtu {
42 port_id: PortId,
43 mtus: usize,
44 },
45 SetSmbusFreq {
46 port_id: PortId,
47 freq: smbus::BusFrequency,
48 },
49}
50
51#[derive(Debug)]
52pub enum CommandEffectError {
53 Unsupported,
54 InternalError,
55}
56
57trait RequestHandler {
58 type Ctx;
59
60 async fn handle<A, C>(
61 &self,
62 ctx: &Self::Ctx,
63 mep: &mut crate::ManagementEndpoint,
64 subsys: &mut crate::Subsystem,
65 rest: &[u8],
66 resp: &mut C,
67 app: A,
68 ) -> Result<(), ResponseStatus>
69 where
70 A: AsyncFnMut(CommandEffect) -> Result<(), CommandEffectError>,
71 C: AsyncRespChannel;
72}
73
74trait Encode<const S: usize>: DekuContainerWrite {
75 fn encode(&self) -> Result<([u8; S], usize), DekuError> {
76 let mut buf = [0u8; S];
77 self.to_slice(&mut buf).map(|len| (buf, len))
78 }
79}
80
81unsafe trait Discriminant<T: Copy> {
85 fn id(&self) -> T {
86 unsafe { *(self as *const Self as *const T) }
88 }
89}
90
91#[derive(Clone, Copy, Debug, Eq, PartialEq)]
92pub struct PciePort {
93 b: u16,
94 d: u16,
95 f: u16,
96 seg: u8,
97 mps: pcie::PayloadSize,
98 cls: pcie::LinkSpeed,
99 mlw: pcie::LinkWidth,
100 nlw: pcie::LinkWidth,
101}
102
103impl PciePort {
104 pub fn new() -> Self {
105 Self {
106 b: 0,
107 d: 0,
108 f: 0,
109 seg: 0,
110 mps: pcie::PayloadSize::Payload128B,
111 cls: pcie::LinkSpeed::Gts2p5,
112 mlw: pcie::LinkWidth::X2,
113 nlw: pcie::LinkWidth::X1,
114 }
115 }
116}
117
118impl Default for PciePort {
119 fn default() -> Self {
120 Self::new()
121 }
122}
123
124#[derive(Clone, Copy, Debug, Eq, PartialEq)]
125pub struct TwoWirePort {
126 cvpdaddr: u8,
128 mvpdfreq: smbus::BusFrequency,
129 cmeaddr: u8,
130 i3csprt: bool,
131 msmbfreq: smbus::BusFrequency,
132 nvmebms: bool,
133 smbfreq: smbus::BusFrequency,
135}
136
137impl TwoWirePort {
138 pub fn new() -> Self {
139 Self {
140 cvpdaddr: 0,
141 mvpdfreq: smbus::BusFrequency::NotSupported,
142 cmeaddr: 0x1d,
143 i3csprt: false,
144 msmbfreq: smbus::BusFrequency::Freq100Khz,
145 nvmebms: false,
146 smbfreq: smbus::BusFrequency::Freq100Khz,
147 }
148 }
149
150 pub fn builder() -> TwoWirePortBuilder {
151 Default::default()
152 }
153}
154
155impl Default for TwoWirePort {
156 fn default() -> Self {
157 Self::new()
158 }
159}
160
161pub struct TwoWirePortBuilder {
162 msmbfreq: smbus::BusFrequency,
163}
164
165impl TwoWirePortBuilder {
166 pub fn new() -> Self {
167 Self {
168 msmbfreq: smbus::BusFrequency::Freq100Khz,
169 }
170 }
171
172 pub fn msmbfreq(&mut self, freq: smbus::BusFrequency) -> &mut Self {
173 self.msmbfreq = freq;
174 self
175 }
176
177 pub fn build(&self) -> TwoWirePort {
178 TwoWirePort {
179 msmbfreq: self.msmbfreq,
180 ..Default::default()
181 }
182 }
183}
184
185impl Default for TwoWirePortBuilder {
186 fn default() -> Self {
187 Self::new()
188 }
189}
190
191#[derive(Debug, PartialEq, Eq)]
192#[repr(u8)]
193pub enum PortType {
194 Inactive,
195 Pcie(PciePort),
196 TwoWire(TwoWirePort),
197}
198
199#[derive(Clone, Copy, Debug)]
201struct PortCapabilities {
202 ciaps: bool,
203 aems: bool,
204}
205
206impl PortCapabilities {
207 fn new() -> Self {
208 PortCapabilities {
209 ciaps: false,
210 aems: false,
211 }
212 }
213}
214
215#[derive(Debug)]
216pub struct Port {
217 id: PortId,
218 typ: PortType,
220 caps: PortCapabilities,
221 mmtus: u16,
222 mebs: u32,
223 mtus: u16,
225}
226
227impl Port {
228 fn new(id: PortId, typ: PortType) -> Self {
229 Self {
230 id,
231 typ,
232 caps: PortCapabilities::new(),
233 mmtus: 64,
234 mebs: 0,
235 mtus: 64,
236 }
237 }
238}
239
240#[derive(Clone, Copy, Debug, PartialEq)]
241pub struct PortId(u8);
242
243#[derive(Clone, Copy, Debug, Default)]
244struct ManagementEndpointControllerState {
245 cc: nvme::ControllerConfiguration,
246 csts: FlagSet<nvme::ControllerStatusFlags>,
247 chscf: FlagSet<nvme::mi::ControllerHealthStatusChangedFlags>,
248}
249
250#[derive(Debug)]
251pub struct ManagementEndpoint {
252 #[expect(dead_code)]
253 port: PortId,
254 mecss: [ManagementEndpointControllerState; MAX_CONTROLLERS],
255 ccsf: nvme::mi::CompositeControllerStatusFlagSet,
256}
257
258impl ManagementEndpoint {
259 pub fn new(port: PortId) -> Self {
260 Self {
261 port,
262 mecss: [ManagementEndpointControllerState::default(); MAX_CONTROLLERS],
263 ccsf: nvme::mi::CompositeControllerStatusFlagSet::empty(),
264 }
265 }
266}
267
268#[derive(Debug)]
269struct MiCapability {
270 mjr: u8,
271 mnr: u8,
272}
273
274impl MiCapability {
275 fn new() -> Self {
276 Self { mjr: 1, mnr: 2 }
277 }
278}
279
280#[derive(Debug, PartialEq, Eq)]
281pub enum UnitKind {
282 Kelvin,
283 Percent,
284}
285
286#[derive(Debug)]
287pub enum Temperature<T> {
288 Kelvin(T),
289 Celcius(T),
290}
291
292#[derive(Debug)]
293struct OperatingRange<T> {
294 kind: UnitKind,
295 lower: T,
296 upper: T,
297}
298
299impl<T> OperatingRange<T> {
300 fn new(kind: UnitKind, lower: T, upper: T) -> Self {
301 Self { kind, lower, upper }
302 }
303}
304
305#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
306pub struct ControllerId(u16);
307
308#[derive(Debug)]
309pub struct SecondaryController {
310 #[expect(dead_code)]
311 id: ControllerId,
312}
313
314#[derive(Debug, Clone, Copy, PartialEq)]
315enum ControllerType {
316 Io,
317 #[expect(dead_code)]
318 Discovery,
319 #[expect(dead_code)]
320 Administrative,
321}
322
323#[derive(Debug)]
324pub struct Controller {
325 id: ControllerId,
326 cntrltype: ControllerType,
327 port: PortId,
328 secondaries: heapless::Vec<SecondaryController, 0>,
329 active_ns: heapless::Vec<NamespaceId, MAX_NAMESPACES>,
330 temp: u16,
331 temp_range: OperatingRange<u16>,
332 capacity: u64,
333 spare: u64,
334 spare_range: OperatingRange<u64>,
335 write_age: u64,
336 write_lifespan: u64,
337 ro: bool,
338 cc: nvme::ControllerConfiguration,
339 csts: FlagSet<nvme::ControllerStatusFlags>,
340 lpa: FlagSet<LogPageAttributes>,
341 lsaes: [FlagSet<LidSupportedAndEffectsFlags>; 130],
342 fna: FlagSet<nvme::FormatNvmAttributes>,
343}
344
345#[derive(Debug)]
346pub enum ControllerError {
347 NamespaceAlreadyAttached,
348 NamespaceNotAttached,
349 NamespaceAttachmentLimitExceeded,
350}
351
352impl Controller {
353 fn new(id: ControllerId, port: PortId) -> Self {
354 Self {
355 id,
356 cntrltype: ControllerType::Io,
357 port,
358 secondaries: heapless::Vec::new(),
359 active_ns: heapless::Vec::new(),
360 temp: 293,
361 temp_range: OperatingRange::new(UnitKind::Kelvin, 213, 400),
362 capacity: 100,
363 spare: 100,
364 spare_range: OperatingRange::new(UnitKind::Percent, 5, 100),
365 write_age: 38,
366 write_lifespan: 100,
367 ro: false,
368 cc: nvme::ControllerConfiguration::default(),
369 csts: FlagSet::empty(),
370 lpa: FlagSet::empty(),
371 lsaes: {
372 let mut arr = [FlagSet::default(); 130];
373 arr[AdminGetLogPageLidRequestType::SupportedLogPages.id() as usize] =
374 LidSupportedAndEffectsFlags::Lsupp.into();
375 arr[AdminGetLogPageLidRequestType::SmartHealthInformation.id() as usize] =
376 LidSupportedAndEffectsFlags::Lsupp.into();
377 arr[AdminGetLogPageLidRequestType::FeatureIdentifiersSupportedAndEffects.id()
378 as usize] = LidSupportedAndEffectsFlags::Lsupp.into();
379 arr[AdminGetLogPageLidRequestType::SanitizeStatus.id() as usize] =
380 LidSupportedAndEffectsFlags::Lsupp.into();
381 arr
382 },
383 fna: (nvme::FormatNvmAttributes::Fns
384 | nvme::FormatNvmAttributes::Sens
385 | nvme::FormatNvmAttributes::Fnvmbs),
386 }
387 }
388
389 pub fn set_property(&mut self, prop: nvme::ControllerProperties) {
390 match prop {
391 nvme::ControllerProperties::Cc(cc) => {
392 self.cc = cc;
393 if self.cc.en {
394 self.csts |= nvme::ControllerStatusFlags::Rdy;
395 } else {
396 self.csts -= nvme::ControllerStatusFlags::Rdy;
397 }
398 }
399 }
400 }
401
402 pub fn set_temperature(&mut self, temp: Temperature<u16>) {
403 let Temperature::Kelvin(k) = temp else {
404 todo!("Support units other than kelvin");
405 };
406
407 self.temp = k;
408 }
409
410 pub fn attach_namespace(&mut self, nsid: NamespaceId) -> Result<(), ControllerError> {
411 debug!("Attaching NSID {} to CTLRID {}", nsid.0, self.id.0);
412 if self.active_ns.iter().any(|ns| ns.0 == nsid.0) {
413 return Err(ControllerError::NamespaceAlreadyAttached);
414 }
415
416 if self.active_ns.push(nsid).is_err() {
417 return Err(ControllerError::NamespaceAttachmentLimitExceeded);
418 }
419
420 Ok(())
421 }
422
423 pub fn detach_namespace(&mut self, nsid: NamespaceId) -> Result<(), ControllerError> {
424 debug!("Detaching NSID {} from CTRLID {}", nsid.0, self.id.0);
425 let Some((idx, _)) = self
426 .active_ns
427 .iter()
428 .enumerate()
429 .find(|args| args.1.0 == nsid.0)
430 else {
431 return Err(ControllerError::NamespaceNotAttached);
432 };
433
434 let _ = self.active_ns.swap_remove(idx);
435
436 Ok(())
437 }
438}
439
440#[derive(Debug)]
441struct SubsystemHealth {
442 nss: FlagSet<crate::nvme::mi::NvmSubsystemStatusFlags>,
443}
444
445impl SubsystemHealth {
446 fn new() -> Self {
447 Self {
448 nss: crate::nvme::mi::NvmSubsystemStatusFlags::Rnr
449 | crate::nvme::mi::NvmSubsystemStatusFlags::Df,
450 }
451 }
452}
453
454#[derive(Clone, Copy, Debug)]
455pub enum NamespaceIdentifierType {
456 Ieuid([u8; 8]),
457 Nguid([u8; 16]),
458 Nuuid(Uuid),
459 Csi(nvme::CommandSetIdentifier),
460}
461
462#[derive(Clone, Copy, Debug)]
465enum NamespaceIdDisposition<'a> {
466 Invalid,
467 Broadcast,
468 Unallocated,
469 Inactive(&'a Namespace),
470 Active(&'a Namespace),
471}
472
473#[derive(Debug, Clone, Copy, PartialEq, Eq)]
475pub struct NamespaceId(u32);
476
477impl NamespaceId {
478 fn disposition<'a>(&self, subsys: &'a Subsystem) -> NamespaceIdDisposition<'a> {
479 if self.0 == 0 {
480 return NamespaceIdDisposition::Invalid;
481 }
482
483 if self.0 == u32::MAX {
484 return NamespaceIdDisposition::Broadcast;
485 }
486
487 assert!(subsys.nss.capacity() <= u32::MAX.try_into().unwrap());
488 if self.0 > subsys.nss.capacity() as u32 {
489 return NamespaceIdDisposition::Invalid;
490 }
491
492 let Some(ns) = subsys.nss.iter().find(|nsid| self.0 == nsid.id.0) else {
493 return NamespaceIdDisposition::Unallocated;
494 };
495
496 if !subsys
497 .ctlrs
498 .iter()
499 .flat_map(|c| c.active_ns.iter())
500 .any(|&nsid| nsid.0 == self.0)
501 {
502 return NamespaceIdDisposition::Inactive(ns);
503 }
504
505 NamespaceIdDisposition::Active(ns)
506 }
507
508 fn max(subsys: &Subsystem) -> u32 {
509 subsys
510 .nss
511 .capacity()
512 .try_into()
513 .expect("Too many namespaces")
514 }
515}
516
517#[derive(Debug)]
518pub struct Namespace {
519 id: NamespaceId,
520 size: u64,
521 capacity: u64,
522 used: u64,
523 block_order: u8,
524 nids: [NamespaceIdentifierType; 2],
525}
526
527impl Namespace {
528 fn generate_uuid(seed: &[u8], nsid: NamespaceId) -> Uuid {
529 let mut hasher = hmac::Hmac::<sha2::Sha256>::new_from_slice(seed).unwrap();
530 hasher.update(&nsid.0.to_be_bytes());
531 let digest = hasher.finalize().into_bytes();
532 let digest: [u8; 16] = digest[..16].try_into().unwrap();
533 uuid::Builder::from_random_bytes(digest).into_uuid()
534 }
535
536 pub fn new(nsid: NamespaceId, uuid: Uuid, capacity: u64) -> Self {
537 Self {
538 id: nsid,
539 size: capacity,
540 capacity,
541 used: 0,
542 block_order: 9,
543 nids: [
544 NamespaceIdentifierType::Nuuid(uuid),
545 NamespaceIdentifierType::Csi(nvme::CommandSetIdentifier::Nvm),
546 ],
547 }
548 }
549}
550
551#[derive(Debug, Eq, PartialEq)]
552pub enum SubsystemError {
553 ControllerLimitExceeded,
554 NamespaceIdentifierUnavailable,
555}
556
557#[derive(Clone, Copy, Debug)]
558pub struct SubsystemInfo {
559 pub pci_vid: u16,
560 pub pci_did: u16,
561 pub pci_svid: u16,
562 pub pci_sdid: u16,
563 pub ieee_oui: [u8; 3],
564 pub instance: [u8; 16],
565}
566
567impl SubsystemInfo {
568 fn acquire_source_date_epoch() -> u64 {
569 env!("SOURCE_DATE_EPOCH").parse::<u64>().unwrap_or(0)
570 }
571
572 fn acquire_ieee_oui() -> [u8; 3] {
573 let mut oui = [0u8; 3];
574 for (idx, val) in option_env!("NVME_MI_DEV_IEEE_OUI")
577 .unwrap_or("ac-de-48")
578 .split('-')
579 .take(oui.len())
580 .map(|v| {
581 u8::from_str_radix(v, 16).expect(
582 "NVME_MI_DEV_IEEE_OUI must be set in the IEEE RA hexadecimal representation",
583 )
584 })
585 .enumerate()
586 {
587 oui[idx] = val;
588 }
589 oui
590 }
591
592 fn acquire_pci_ids() -> (u16, u16, u16, u16) {
593 let vid = u16::from_str_radix(option_env!("NVME_MI_DEV_PCI_VID").unwrap_or("ffff"), 16)
595 .expect("NVME_MI_DEV_PCI_VID must be set to a 16-bit value in base-16 representation");
596 let did = u16::from_str_radix(option_env!("NVME_MI_DEV_PCI_DID").unwrap_or("ffff"), 16)
597 .expect("NVME_MI_DEV_PCI_DID must be set to a 16-bit value in base-16 representation");
598 let svid = u16::from_str_radix(option_env!("NVME_MI_DEV_PCI_SVID").unwrap_or("ffff"), 16)
599 .expect("NVME_MI_DEV_PCI_SVID must be set to a 16-bit value in base-16 representation");
600 let sdid = u16::from_str_radix(option_env!("NVME_MI_DEV_PCI_SDID").unwrap_or("ffff"), 16)
601 .expect("NVME_MI_DEV_PCI_SDID must be set to a 16-bit value in base-16 representation");
602 (vid, did, svid, sdid)
603 }
604
605 pub fn invalid() -> Self {
606 Self {
607 pci_vid: 0xffff,
608 pci_did: 0xffff,
609 pci_svid: 0xffff,
610 pci_sdid: 0xffff,
611 ieee_oui: [0xac, 0xde, 0x48],
612 instance: [0; 16],
613 }
614 }
615
616 pub fn environment() -> Self {
617 let (vid, did, svid, sdid) = SubsystemInfo::acquire_pci_ids();
618 let sde = SubsystemInfo::acquire_source_date_epoch().to_le_bytes();
619 let mut instance = [0u8; 16];
620 instance[..sde.len()].copy_from_slice(&sde);
621 Self {
622 pci_vid: vid,
623 pci_did: did,
624 pci_svid: svid,
625 pci_sdid: sdid,
626 ieee_oui: SubsystemInfo::acquire_ieee_oui(),
627 instance,
628 }
629 }
630}
631
632#[derive(Debug)]
633pub struct Subsystem {
634 info: SubsystemInfo,
635 caps: nvme::mi::SubsystemCapabilities,
636 ports: heapless::Vec<Port, MAX_PORTS>,
637 ctlrs: heapless::Vec<Controller, MAX_CONTROLLERS>,
638 nsids: u32,
639 nss: heapless::Vec<Namespace, MAX_NAMESPACES>,
640 health: SubsystemHealth,
641 sanicap: FlagSet<nvme::SanitizeCapabilityFlags>,
642 nodmmas: nvme::NoDeallocateModifiesMediaAfterSanitize,
643 ssi: nvme::SanitizeStateInformation,
644 sstat: nvme::SanitizeStatus,
645 sconf: Option<nvme::AdminSanitizeConfiguration>,
646 mi: MiCapability,
647 sn: &'static str,
648 mn: &'static str,
649 fr: &'static str,
650}
651
652impl Subsystem {
653 pub fn new(info: SubsystemInfo) -> Self {
654 Subsystem {
655 info,
656 caps: nvme::mi::SubsystemCapabilities::new(),
657 ports: heapless::Vec::new(),
658 ctlrs: heapless::Vec::new(),
659 nsids: 0,
660 nss: heapless::Vec::new(),
661 health: SubsystemHealth::new(),
662 mi: MiCapability::new(),
663 sn: "1000",
664 mn: "MIDEV",
665 fr: "00.00.01",
666 sstat: Default::default(),
667 sconf: None,
668 ssi: Default::default(),
669 sanicap: nvme::SanitizeCapabilityFlags::Ces
670 | nvme::SanitizeCapabilityFlags::Bes
671 | nvme::SanitizeCapabilityFlags::Ows
672 | nvme::SanitizeCapabilityFlags::Ndi,
673 nodmmas: Default::default(),
674 }
675 }
676
677 pub fn add_port(&mut self, typ: PortType) -> Result<PortId, Port> {
678 debug_assert!(self.ctlrs.len() <= u8::MAX.into());
679 let p = Port::new(PortId(self.ports.len() as u8), typ);
680 self.ports.push(p).map(|_p| self.ports.last().unwrap().id)
681 }
682
683 pub fn add_controller(&mut self, port: PortId) -> Result<ControllerId, SubsystemError> {
684 debug_assert!(self.ctlrs.len() <= u16::MAX.into());
685 let cid = ControllerId(self.ctlrs.len() as u16);
686 let c = Controller::new(cid, port);
687 self.ctlrs
688 .push(c)
689 .map_err(|_| SubsystemError::ControllerLimitExceeded)?;
690 Ok(cid)
691 }
692
693 pub fn controller_mut(&mut self, id: ControllerId) -> &mut Controller {
694 self.ctlrs
695 .get_mut(id.0 as usize)
696 .expect("Invalid ControllerId provided")
697 }
698
699 pub fn add_namespace(&mut self, capacity: u64) -> Result<NamespaceId, SubsystemError> {
700 let Some(allocated) = self.nsids.checked_add(1) else {
701 debug!("Implement allocation tracking with reuse");
702 return Err(SubsystemError::NamespaceIdentifierUnavailable);
703 };
704 self.nsids = allocated;
705 let nsid = NamespaceId(self.nsids);
706 let ns = Namespace::new(
707 nsid,
708 Namespace::generate_uuid(&self.info.instance, nsid),
709 capacity,
710 );
711 match self.nss.push(ns) {
712 Ok(_) => Ok(nsid),
713 Err(_) => Err(SubsystemError::NamespaceIdentifierUnavailable),
714 }
715 }
716
717 pub fn remove_namespace(&mut self, nsid: NamespaceId) -> Result<(), SubsystemError> {
718 if nsid.0 == u32::MAX {
719 self.nss.clear();
720 return Ok(());
721 }
722 let Some(e) = self.nss.iter().enumerate().find(|args| args.1.id == nsid) else {
723 return Err(SubsystemError::NamespaceIdentifierUnavailable);
724 };
725 let _ = self.nss.swap_remove(e.0);
726 Ok(())
727 }
728}