pci.rs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565
  1. //! PCI transport for VirtIO.
  2. pub mod bus;
  3. use self::bus::{DeviceFunction, DeviceFunctionInfo, PciError, PciRoot, PCI_CAP_ID_VNDR};
  4. use super::{DeviceStatus, DeviceType, Transport};
  5. use crate::{
  6. hal::{Hal, PhysAddr},
  7. nonnull_slice_from_raw_parts,
  8. volatile::{
  9. volread, volwrite, ReadOnly, Volatile, VolatileReadable, VolatileWritable, WriteOnly,
  10. },
  11. Error,
  12. };
  13. use core::{
  14. fmt::{self, Display, Formatter},
  15. mem::{align_of, size_of},
  16. ptr::{addr_of_mut, NonNull},
  17. };
  18. /// The PCI vendor ID for VirtIO devices.
  19. const VIRTIO_VENDOR_ID: u16 = 0x1af4;
  20. /// The offset to add to a VirtIO device ID to get the corresponding PCI device ID.
  21. const PCI_DEVICE_ID_OFFSET: u16 = 0x1040;
  22. const TRANSITIONAL_NETWORK: u16 = 0x1000;
  23. const TRANSITIONAL_BLOCK: u16 = 0x1001;
  24. const TRANSITIONAL_MEMORY_BALLOONING: u16 = 0x1002;
  25. const TRANSITIONAL_CONSOLE: u16 = 0x1003;
  26. const TRANSITIONAL_SCSI_HOST: u16 = 0x1004;
  27. const TRANSITIONAL_ENTROPY_SOURCE: u16 = 0x1005;
  28. const TRANSITIONAL_9P_TRANSPORT: u16 = 0x1009;
  29. /// The offset of the bar field within `virtio_pci_cap`.
  30. const CAP_BAR_OFFSET: u8 = 4;
  31. /// The offset of the offset field with `virtio_pci_cap`.
  32. const CAP_BAR_OFFSET_OFFSET: u8 = 8;
  33. /// The offset of the `length` field within `virtio_pci_cap`.
  34. const CAP_LENGTH_OFFSET: u8 = 12;
  35. /// The offset of the`notify_off_multiplier` field within `virtio_pci_notify_cap`.
  36. const CAP_NOTIFY_OFF_MULTIPLIER_OFFSET: u8 = 16;
  37. /// Common configuration.
  38. const VIRTIO_PCI_CAP_COMMON_CFG: u8 = 1;
  39. /// Notifications.
  40. const VIRTIO_PCI_CAP_NOTIFY_CFG: u8 = 2;
  41. /// ISR Status.
  42. const VIRTIO_PCI_CAP_ISR_CFG: u8 = 3;
  43. /// Device specific configuration.
  44. const VIRTIO_PCI_CAP_DEVICE_CFG: u8 = 4;
  45. fn device_type(pci_device_id: u16) -> DeviceType {
  46. match pci_device_id {
  47. TRANSITIONAL_NETWORK => DeviceType::Network,
  48. TRANSITIONAL_BLOCK => DeviceType::Block,
  49. TRANSITIONAL_MEMORY_BALLOONING => DeviceType::MemoryBalloon,
  50. TRANSITIONAL_CONSOLE => DeviceType::Console,
  51. TRANSITIONAL_SCSI_HOST => DeviceType::ScsiHost,
  52. TRANSITIONAL_ENTROPY_SOURCE => DeviceType::EntropySource,
  53. TRANSITIONAL_9P_TRANSPORT => DeviceType::_9P,
  54. id if id >= PCI_DEVICE_ID_OFFSET => DeviceType::from(id - PCI_DEVICE_ID_OFFSET),
  55. _ => DeviceType::Invalid,
  56. }
  57. }
  58. /// Returns the type of VirtIO device to which the given PCI vendor and device ID corresponds, or
  59. /// `None` if it is not a recognised VirtIO device.
  60. pub fn virtio_device_type(device_function_info: &DeviceFunctionInfo) -> Option<DeviceType> {
  61. if device_function_info.vendor_id == VIRTIO_VENDOR_ID {
  62. let device_type = device_type(device_function_info.device_id);
  63. if device_type != DeviceType::Invalid {
  64. return Some(device_type);
  65. }
  66. }
  67. None
  68. }
  69. /// PCI transport for VirtIO.
  70. ///
  71. /// Ref: 4.1 Virtio Over PCI Bus
  72. #[derive(Debug)]
  73. pub struct PciTransport {
  74. device_type: DeviceType,
  75. /// The bus, device and function identifier for the VirtIO device.
  76. device_function: DeviceFunction,
  77. /// The common configuration structure within some BAR.
  78. common_cfg: NonNull<CommonCfg>,
  79. /// The start of the queue notification region within some BAR.
  80. notify_region: NonNull<[WriteOnly<u16>]>,
  81. notify_off_multiplier: u32,
  82. /// The ISR status register within some BAR.
  83. isr_status: NonNull<Volatile<u8>>,
  84. /// The VirtIO device-specific configuration within some BAR.
  85. config_space: Option<NonNull<[u32]>>,
  86. }
  87. impl PciTransport {
  88. /// Construct a new PCI VirtIO device driver for the given device function on the given PCI
  89. /// root controller.
  90. ///
  91. /// The PCI device must already have had its BARs allocated.
  92. pub fn new<H: Hal>(
  93. root: &mut PciRoot,
  94. device_function: DeviceFunction,
  95. ) -> Result<Self, VirtioPciError> {
  96. let device_vendor = root.config_read_word(device_function, 0);
  97. let device_id = (device_vendor >> 16) as u16;
  98. let vendor_id = device_vendor as u16;
  99. if vendor_id != VIRTIO_VENDOR_ID {
  100. return Err(VirtioPciError::InvalidVendorId(vendor_id));
  101. }
  102. let device_type = device_type(device_id);
  103. // Find the PCI capabilities we need.
  104. let mut common_cfg = None;
  105. let mut notify_cfg = None;
  106. let mut notify_off_multiplier = 0;
  107. let mut isr_cfg = None;
  108. let mut device_cfg = None;
  109. for capability in root.capabilities(device_function) {
  110. if capability.id != PCI_CAP_ID_VNDR {
  111. continue;
  112. }
  113. let cap_len = capability.private_header as u8;
  114. let cfg_type = (capability.private_header >> 8) as u8;
  115. if cap_len < 16 {
  116. continue;
  117. }
  118. let struct_info = VirtioCapabilityInfo {
  119. bar: root.config_read_word(device_function, capability.offset + CAP_BAR_OFFSET)
  120. as u8,
  121. offset: root
  122. .config_read_word(device_function, capability.offset + CAP_BAR_OFFSET_OFFSET),
  123. length: root
  124. .config_read_word(device_function, capability.offset + CAP_LENGTH_OFFSET),
  125. };
  126. match cfg_type {
  127. VIRTIO_PCI_CAP_COMMON_CFG if common_cfg.is_none() => {
  128. common_cfg = Some(struct_info);
  129. }
  130. VIRTIO_PCI_CAP_NOTIFY_CFG if cap_len >= 20 && notify_cfg.is_none() => {
  131. notify_cfg = Some(struct_info);
  132. notify_off_multiplier = root.config_read_word(
  133. device_function,
  134. capability.offset + CAP_NOTIFY_OFF_MULTIPLIER_OFFSET,
  135. );
  136. }
  137. VIRTIO_PCI_CAP_ISR_CFG if isr_cfg.is_none() => {
  138. isr_cfg = Some(struct_info);
  139. }
  140. VIRTIO_PCI_CAP_DEVICE_CFG if device_cfg.is_none() => {
  141. device_cfg = Some(struct_info);
  142. }
  143. _ => {}
  144. }
  145. }
  146. let common_cfg = get_bar_region::<H, _>(
  147. root,
  148. device_function,
  149. &common_cfg.ok_or(VirtioPciError::MissingCommonConfig)?,
  150. )?;
  151. let notify_cfg = notify_cfg.ok_or(VirtioPciError::MissingNotifyConfig)?;
  152. if notify_off_multiplier % 2 != 0 {
  153. return Err(VirtioPciError::InvalidNotifyOffMultiplier(
  154. notify_off_multiplier,
  155. ));
  156. }
  157. let notify_region = get_bar_region_slice::<H, _>(root, device_function, &notify_cfg)?;
  158. let isr_status = get_bar_region::<H, _>(
  159. root,
  160. device_function,
  161. &isr_cfg.ok_or(VirtioPciError::MissingIsrConfig)?,
  162. )?;
  163. let config_space = if let Some(device_cfg) = device_cfg {
  164. Some(get_bar_region_slice::<H, _>(
  165. root,
  166. device_function,
  167. &device_cfg,
  168. )?)
  169. } else {
  170. None
  171. };
  172. Ok(Self {
  173. device_type,
  174. device_function,
  175. common_cfg,
  176. notify_region,
  177. notify_off_multiplier,
  178. isr_status,
  179. config_space,
  180. })
  181. }
  182. }
  183. impl Transport for PciTransport {
  184. fn device_type(&self) -> DeviceType {
  185. self.device_type
  186. }
  187. fn read_device_features(&mut self) -> u64 {
  188. // Safe because the common config pointer is valid and we checked in get_bar_region that it
  189. // was aligned.
  190. unsafe {
  191. volwrite!(self.common_cfg, device_feature_select, 0);
  192. let mut device_features_bits = volread!(self.common_cfg, device_feature) as u64;
  193. volwrite!(self.common_cfg, device_feature_select, 1);
  194. device_features_bits |= (volread!(self.common_cfg, device_feature) as u64) << 32;
  195. device_features_bits
  196. }
  197. }
  198. fn write_driver_features(&mut self, driver_features: u64) {
  199. // Safe because the common config pointer is valid and we checked in get_bar_region that it
  200. // was aligned.
  201. unsafe {
  202. volwrite!(self.common_cfg, driver_feature_select, 0);
  203. volwrite!(self.common_cfg, driver_feature, driver_features as u32);
  204. volwrite!(self.common_cfg, driver_feature_select, 1);
  205. volwrite!(
  206. self.common_cfg,
  207. driver_feature,
  208. (driver_features >> 32) as u32
  209. );
  210. }
  211. }
  212. fn max_queue_size(&self) -> u32 {
  213. // Safe because the common config pointer is valid and we checked in get_bar_region that it
  214. // was aligned.
  215. unsafe { volread!(self.common_cfg, queue_size) }.into()
  216. }
  217. fn notify(&mut self, queue: u16) {
  218. // Safe because the common config and notify region pointers are valid and we checked in
  219. // get_bar_region that they were aligned.
  220. unsafe {
  221. volwrite!(self.common_cfg, queue_select, queue);
  222. // TODO: Consider caching this somewhere (per queue).
  223. let queue_notify_off = volread!(self.common_cfg, queue_notify_off);
  224. let offset_bytes = usize::from(queue_notify_off) * self.notify_off_multiplier as usize;
  225. let index = offset_bytes / size_of::<u16>();
  226. addr_of_mut!((*self.notify_region.as_ptr())[index]).vwrite(queue);
  227. }
  228. }
  229. fn get_status(&self) -> DeviceStatus {
  230. // Safe because the common config pointer is valid and we checked in get_bar_region that it
  231. // was aligned.
  232. let status = unsafe { volread!(self.common_cfg, device_status) };
  233. DeviceStatus::from_bits_truncate(status.into())
  234. }
  235. fn set_status(&mut self, status: DeviceStatus) {
  236. // Safe because the common config pointer is valid and we checked in get_bar_region that it
  237. // was aligned.
  238. unsafe {
  239. volwrite!(self.common_cfg, device_status, status.bits() as u8);
  240. }
  241. }
  242. fn set_guest_page_size(&mut self, _guest_page_size: u32) {
  243. // No-op, the PCI transport doesn't care.
  244. }
  245. fn requires_legacy_layout(&self) -> bool {
  246. false
  247. }
  248. fn queue_set(
  249. &mut self,
  250. queue: u16,
  251. size: u32,
  252. descriptors: PhysAddr,
  253. driver_area: PhysAddr,
  254. device_area: PhysAddr,
  255. ) {
  256. // Safe because the common config pointer is valid and we checked in get_bar_region that it
  257. // was aligned.
  258. unsafe {
  259. volwrite!(self.common_cfg, queue_select, queue);
  260. volwrite!(self.common_cfg, queue_size, size as u16);
  261. volwrite!(self.common_cfg, queue_desc, descriptors as u64);
  262. volwrite!(self.common_cfg, queue_driver, driver_area as u64);
  263. volwrite!(self.common_cfg, queue_device, device_area as u64);
  264. volwrite!(self.common_cfg, queue_enable, 1);
  265. }
  266. }
  267. fn queue_unset(&mut self, _queue: u16) {
  268. // The VirtIO spec doesn't allow queues to be unset once they have been set up for the PCI
  269. // transport, so this is a no-op.
  270. }
  271. fn queue_used(&mut self, queue: u16) -> bool {
  272. // Safe because the common config pointer is valid and we checked in get_bar_region that it
  273. // was aligned.
  274. unsafe {
  275. volwrite!(self.common_cfg, queue_select, queue);
  276. volread!(self.common_cfg, queue_enable) == 1
  277. }
  278. }
  279. fn ack_interrupt(&mut self) -> bool {
  280. // Safe because the common config pointer is valid and we checked in get_bar_region that it
  281. // was aligned.
  282. // Reading the ISR status resets it to 0 and causes the device to de-assert the interrupt.
  283. let isr_status = unsafe { self.isr_status.as_ptr().vread() };
  284. // TODO: Distinguish between queue interrupt and device configuration interrupt.
  285. isr_status & 0x3 != 0
  286. }
  287. fn config_space<T>(&self) -> Result<NonNull<T>, Error> {
  288. if let Some(config_space) = self.config_space {
  289. if size_of::<T>() > config_space.len() * size_of::<u32>() {
  290. Err(Error::ConfigSpaceTooSmall)
  291. } else if align_of::<T>() > 4 {
  292. // Panic as this should only happen if the driver is written incorrectly.
  293. panic!(
  294. "Driver expected config space alignment of {} bytes, but VirtIO only guarantees 4 byte alignment.",
  295. align_of::<T>()
  296. );
  297. } else {
  298. // TODO: Use NonNull::as_non_null_ptr once it is stable.
  299. let config_space_ptr = NonNull::new(config_space.as_ptr() as *mut u32).unwrap();
  300. Ok(config_space_ptr.cast())
  301. }
  302. } else {
  303. Err(Error::ConfigSpaceMissing)
  304. }
  305. }
  306. }
  307. impl Drop for PciTransport {
  308. fn drop(&mut self) {
  309. // Reset the device when the transport is dropped.
  310. self.set_status(DeviceStatus::empty());
  311. while self.get_status() != DeviceStatus::empty() {}
  312. }
  313. }
  314. /// `virtio_pci_common_cfg`, see 4.1.4.3 "Common configuration structure layout".
  315. #[repr(C)]
  316. struct CommonCfg {
  317. device_feature_select: Volatile<u32>,
  318. device_feature: ReadOnly<u32>,
  319. driver_feature_select: Volatile<u32>,
  320. driver_feature: Volatile<u32>,
  321. msix_config: Volatile<u16>,
  322. num_queues: ReadOnly<u16>,
  323. device_status: Volatile<u8>,
  324. config_generation: ReadOnly<u8>,
  325. queue_select: Volatile<u16>,
  326. queue_size: Volatile<u16>,
  327. queue_msix_vector: Volatile<u16>,
  328. queue_enable: Volatile<u16>,
  329. queue_notify_off: Volatile<u16>,
  330. queue_desc: Volatile<u64>,
  331. queue_driver: Volatile<u64>,
  332. queue_device: Volatile<u64>,
  333. }
  334. /// Information about a VirtIO structure within some BAR, as provided by a `virtio_pci_cap`.
  335. #[derive(Clone, Debug, Eq, PartialEq)]
  336. struct VirtioCapabilityInfo {
  337. /// The bar in which the structure can be found.
  338. bar: u8,
  339. /// The offset within the bar.
  340. offset: u32,
  341. /// The length in bytes of the structure within the bar.
  342. length: u32,
  343. }
  344. fn get_bar_region<H: Hal, T>(
  345. root: &mut PciRoot,
  346. device_function: DeviceFunction,
  347. struct_info: &VirtioCapabilityInfo,
  348. ) -> Result<NonNull<T>, VirtioPciError> {
  349. let bar_info = root.bar_info(device_function, struct_info.bar)?;
  350. let (bar_address, bar_size) = bar_info
  351. .memory_address_size()
  352. .ok_or(VirtioPciError::UnexpectedIoBar)?;
  353. if bar_address == 0 {
  354. return Err(VirtioPciError::BarNotAllocated(struct_info.bar));
  355. }
  356. if struct_info.offset + struct_info.length > bar_size
  357. || size_of::<T>() > struct_info.length as usize
  358. {
  359. return Err(VirtioPciError::BarOffsetOutOfRange);
  360. }
  361. let paddr = bar_address as PhysAddr + struct_info.offset as PhysAddr;
  362. // Safe because the paddr and size describe a valid MMIO region, at least according to the PCI
  363. // bus.
  364. let vaddr = unsafe { H::mmio_phys_to_virt(paddr, struct_info.length as usize) };
  365. if vaddr.as_ptr() as usize % align_of::<T>() != 0 {
  366. return Err(VirtioPciError::Misaligned {
  367. vaddr,
  368. alignment: align_of::<T>(),
  369. });
  370. }
  371. Ok(vaddr.cast())
  372. }
  373. fn get_bar_region_slice<H: Hal, T>(
  374. root: &mut PciRoot,
  375. device_function: DeviceFunction,
  376. struct_info: &VirtioCapabilityInfo,
  377. ) -> Result<NonNull<[T]>, VirtioPciError> {
  378. let ptr = get_bar_region::<H, T>(root, device_function, struct_info)?;
  379. Ok(nonnull_slice_from_raw_parts(
  380. ptr,
  381. struct_info.length as usize / size_of::<T>(),
  382. ))
  383. }
  384. /// An error encountered initialising a VirtIO PCI transport.
  385. #[derive(Clone, Debug, Eq, PartialEq)]
  386. pub enum VirtioPciError {
  387. /// PCI device vender ID was not the VirtIO vendor ID.
  388. InvalidVendorId(u16),
  389. /// No valid `VIRTIO_PCI_CAP_COMMON_CFG` capability was found.
  390. MissingCommonConfig,
  391. /// No valid `VIRTIO_PCI_CAP_NOTIFY_CFG` capability was found.
  392. MissingNotifyConfig,
  393. /// `VIRTIO_PCI_CAP_NOTIFY_CFG` capability has a `notify_off_multiplier` that is not a multiple
  394. /// of 2.
  395. InvalidNotifyOffMultiplier(u32),
  396. /// No valid `VIRTIO_PCI_CAP_ISR_CFG` capability was found.
  397. MissingIsrConfig,
  398. /// An IO BAR was provided rather than a memory BAR.
  399. UnexpectedIoBar,
  400. /// A BAR which we need was not allocated an address.
  401. BarNotAllocated(u8),
  402. /// The offset for some capability was greater than the length of the BAR.
  403. BarOffsetOutOfRange,
  404. /// The virtual address was not aligned as expected.
  405. Misaligned {
  406. /// The virtual address in question.
  407. vaddr: NonNull<u8>,
  408. /// The expected alignment in bytes.
  409. alignment: usize,
  410. },
  411. /// A generic PCI error,
  412. Pci(PciError),
  413. }
  414. impl Display for VirtioPciError {
  415. fn fmt(&self, f: &mut Formatter) -> fmt::Result {
  416. match self {
  417. Self::InvalidVendorId(vendor_id) => write!(
  418. f,
  419. "PCI device vender ID {:#06x} was not the VirtIO vendor ID {:#06x}.",
  420. vendor_id, VIRTIO_VENDOR_ID
  421. ),
  422. Self::MissingCommonConfig => write!(
  423. f,
  424. "No valid `VIRTIO_PCI_CAP_COMMON_CFG` capability was found."
  425. ),
  426. Self::MissingNotifyConfig => write!(
  427. f,
  428. "No valid `VIRTIO_PCI_CAP_NOTIFY_CFG` capability was found."
  429. ),
  430. Self::InvalidNotifyOffMultiplier(notify_off_multiplier) => {
  431. write!(
  432. f,
  433. "`VIRTIO_PCI_CAP_NOTIFY_CFG` capability has a `notify_off_multiplier` that is not a multiple of 2: {}",
  434. notify_off_multiplier
  435. )
  436. }
  437. Self::MissingIsrConfig => {
  438. write!(f, "No valid `VIRTIO_PCI_CAP_ISR_CFG` capability was found.")
  439. }
  440. Self::UnexpectedIoBar => write!(f, "Unexpected IO BAR (expected memory BAR)."),
  441. Self::BarNotAllocated(bar_index) => write!(f, "Bar {} not allocated.", bar_index),
  442. Self::BarOffsetOutOfRange => write!(f, "Capability offset greater than BAR length."),
  443. Self::Misaligned { vaddr, alignment } => write!(
  444. f,
  445. "Virtual address {:#018?} was not aligned to a {} byte boundary as expected.",
  446. vaddr, alignment
  447. ),
  448. Self::Pci(pci_error) => pci_error.fmt(f),
  449. }
  450. }
  451. }
  452. impl From<PciError> for VirtioPciError {
  453. fn from(error: PciError) -> Self {
  454. Self::Pci(error)
  455. }
  456. }
  457. #[cfg(test)]
  458. mod tests {
  459. use super::*;
  460. #[test]
  461. fn transitional_device_ids() {
  462. assert_eq!(device_type(0x1000), DeviceType::Network);
  463. assert_eq!(device_type(0x1002), DeviceType::MemoryBalloon);
  464. assert_eq!(device_type(0x1009), DeviceType::_9P);
  465. }
  466. #[test]
  467. fn offset_device_ids() {
  468. assert_eq!(device_type(0x1045), DeviceType::MemoryBalloon);
  469. assert_eq!(device_type(0x1049), DeviceType::_9P);
  470. assert_eq!(device_type(0x1058), DeviceType::Memory);
  471. assert_eq!(device_type(0x1040), DeviceType::Invalid);
  472. assert_eq!(device_type(0x1059), DeviceType::Invalid);
  473. }
  474. #[test]
  475. fn virtio_device_type_valid() {
  476. assert_eq!(
  477. virtio_device_type(&DeviceFunctionInfo {
  478. vendor_id: VIRTIO_VENDOR_ID,
  479. device_id: TRANSITIONAL_BLOCK,
  480. class: 0,
  481. subclass: 0,
  482. prog_if: 0,
  483. revision: 0,
  484. header_type: bus::HeaderType::Standard,
  485. }),
  486. Some(DeviceType::Block)
  487. );
  488. }
  489. #[test]
  490. fn virtio_device_type_invalid() {
  491. // Non-VirtIO vendor ID.
  492. assert_eq!(
  493. virtio_device_type(&DeviceFunctionInfo {
  494. vendor_id: 0x1234,
  495. device_id: TRANSITIONAL_BLOCK,
  496. class: 0,
  497. subclass: 0,
  498. prog_if: 0,
  499. revision: 0,
  500. header_type: bus::HeaderType::Standard,
  501. }),
  502. None
  503. );
  504. // Invalid device ID.
  505. assert_eq!(
  506. virtio_device_type(&DeviceFunctionInfo {
  507. vendor_id: VIRTIO_VENDOR_ID,
  508. device_id: 0x1040,
  509. class: 0,
  510. subclass: 0,
  511. prog_if: 0,
  512. revision: 0,
  513. header_type: bus::HeaderType::Standard,
  514. }),
  515. None
  516. );
  517. }
  518. }