main.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. #![no_std]
  2. #![no_main]
  3. mod exceptions;
  4. mod hal;
  5. mod logger;
  6. mod pl011;
  7. use core::{
  8. mem::size_of,
  9. panic::PanicInfo,
  10. ptr::{self, NonNull},
  11. };
  12. use fdt::{node::FdtNode, standard_nodes::Compatible, Fdt};
  13. use hal::HalImpl;
  14. use log::{debug, error, info, trace, warn, LevelFilter};
  15. use psci::system_off;
  16. use virtio_drivers::{
  17. pci::{
  18. bus::{BarInfo, Cam, Command, DeviceFunction, MemoryBarType, PciRoot},
  19. virtio_device_type, PciTransport,
  20. },
  21. DeviceType, MmioTransport, Transport, VirtIOBlk, VirtIOGpu, VirtIOHeader, VirtIONet,
  22. };
  23. #[no_mangle]
  24. extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {
  25. logger::init(LevelFilter::Debug).unwrap();
  26. info!("virtio-drivers example started.");
  27. debug!(
  28. "x0={:#018x}, x1={:#018x}, x2={:#018x}, x3={:#018x}",
  29. x0, x1, x2, x3
  30. );
  31. info!("Loading FDT from {:#018x}", x0);
  32. // Safe because the pointer is a valid pointer to unaliased memory.
  33. let fdt = unsafe { Fdt::from_ptr(x0 as *const u8).unwrap() };
  34. for node in fdt.all_nodes() {
  35. // Dump information about the node for debugging.
  36. trace!(
  37. "{}: {:?}",
  38. node.name,
  39. node.compatible().map(Compatible::first),
  40. );
  41. if let Some(reg) = node.reg() {
  42. for range in reg {
  43. trace!(
  44. " {:#018x?}, length {:?}",
  45. range.starting_address,
  46. range.size
  47. );
  48. }
  49. }
  50. // Check whether it is a VirtIO MMIO device.
  51. if let (Some(compatible), Some(region)) =
  52. (node.compatible(), node.reg().and_then(|mut reg| reg.next()))
  53. {
  54. if compatible.all().any(|s| s == "virtio,mmio")
  55. && region.size.unwrap_or(0) > size_of::<VirtIOHeader>()
  56. {
  57. debug!("Found VirtIO MMIO device at {:?}", region);
  58. let header = NonNull::new(region.starting_address as *mut VirtIOHeader).unwrap();
  59. match unsafe { MmioTransport::new(header) } {
  60. Err(e) => warn!("Error creating VirtIO MMIO transport: {}", e),
  61. Ok(transport) => {
  62. info!(
  63. "Detected virtio MMIO device with vendor id {:#X}, device type {:?}, version {:?}",
  64. transport.vendor_id(),
  65. transport.device_type(),
  66. transport.version(),
  67. );
  68. virtio_device(transport);
  69. }
  70. }
  71. }
  72. }
  73. }
  74. if let Some(pci_node) = fdt.find_compatible(&["pci-host-cam-generic"]) {
  75. info!("Found PCI node: {}", pci_node.name);
  76. enumerate_pci(pci_node, Cam::MmioCam);
  77. }
  78. if let Some(pcie_node) = fdt.find_compatible(&["pci-host-ecam-generic"]) {
  79. info!("Found PCIe node: {}", pcie_node.name);
  80. enumerate_pci(pcie_node, Cam::Ecam);
  81. }
  82. system_off().unwrap();
  83. }
  84. fn virtio_device(transport: impl Transport) {
  85. match transport.device_type() {
  86. DeviceType::Block => virtio_blk(transport),
  87. DeviceType::GPU => virtio_gpu(transport),
  88. DeviceType::Network => virtio_net(transport),
  89. t => warn!("Unrecognized virtio device: {:?}", t),
  90. }
  91. }
  92. fn virtio_blk<T: Transport>(transport: T) {
  93. let mut blk = VirtIOBlk::<HalImpl, T>::new(transport).expect("failed to create blk driver");
  94. let mut input = [0xffu8; 512];
  95. let mut output = [0; 512];
  96. for i in 0..32 {
  97. for x in input.iter_mut() {
  98. *x = i as u8;
  99. }
  100. blk.write_block(i, &input).expect("failed to write");
  101. blk.read_block(i, &mut output).expect("failed to read");
  102. assert_eq!(input, output);
  103. }
  104. info!("virtio-blk test finished");
  105. }
  106. fn virtio_gpu<T: Transport>(transport: T) {
  107. let mut gpu = VirtIOGpu::<HalImpl, T>::new(transport).expect("failed to create gpu driver");
  108. let (width, height) = gpu.resolution().expect("failed to get resolution");
  109. let width = width as usize;
  110. let height = height as usize;
  111. info!("GPU resolution is {}x{}", width, height);
  112. let fb = gpu.setup_framebuffer().expect("failed to get fb");
  113. for y in 0..height {
  114. for x in 0..width {
  115. let idx = (y * width + x) * 4;
  116. fb[idx] = x as u8;
  117. fb[idx + 1] = y as u8;
  118. fb[idx + 2] = (x + y) as u8;
  119. }
  120. }
  121. gpu.flush().expect("failed to flush");
  122. info!("virtio-gpu test finished");
  123. }
  124. fn virtio_net<T: Transport>(transport: T) {
  125. let mut net = VirtIONet::<HalImpl, T>::new(transport).expect("failed to create net driver");
  126. let mut buf = [0u8; 0x100];
  127. let len = net.recv(&mut buf).expect("failed to recv");
  128. info!("recv: {:?}", &buf[..len]);
  129. net.send(&buf[..len]).expect("failed to send");
  130. info!("virtio-net test finished");
  131. }
  132. #[derive(Copy, Clone, Debug, Eq, PartialEq)]
  133. enum PciRangeType {
  134. ConfigurationSpace,
  135. IoSpace,
  136. Memory32,
  137. Memory64,
  138. }
  139. impl From<u8> for PciRangeType {
  140. fn from(value: u8) -> Self {
  141. match value {
  142. 0 => Self::ConfigurationSpace,
  143. 1 => Self::IoSpace,
  144. 2 => Self::Memory32,
  145. 3 => Self::Memory64,
  146. _ => panic!("Tried to convert invalid range type {}", value),
  147. }
  148. }
  149. }
  150. fn enumerate_pci(pci_node: FdtNode, cam: Cam) {
  151. let reg = pci_node.reg().expect("PCI node missing reg property.");
  152. let mut allocator = PciMemory32Allocator::for_pci_ranges(&pci_node);
  153. for region in reg {
  154. info!(
  155. "Reg: {:?}-{:#x}",
  156. region.starting_address,
  157. region.starting_address as usize + region.size.unwrap()
  158. );
  159. // Safe because we know the pointer is to a valid MMIO region.
  160. let mut pci_root = unsafe { PciRoot::new(region.starting_address as *mut u8, cam) };
  161. for (device_function, info) in pci_root.enumerate_bus(0) {
  162. let (status, command) = pci_root.get_status_command(device_function);
  163. info!(
  164. "Found {} at {}, status {:?} command {:?}",
  165. info, device_function, status, command
  166. );
  167. if let Some(virtio_type) = virtio_device_type(&info) {
  168. info!(" VirtIO {:?}", virtio_type);
  169. allocate_bars(&mut pci_root, device_function, &mut allocator);
  170. dump_bar_contents(&mut pci_root, device_function, 4);
  171. let mut transport =
  172. PciTransport::new::<HalImpl>(pci_root.clone(), device_function).unwrap();
  173. info!(" Features: {:#018x}", transport.read_device_features());
  174. }
  175. }
  176. }
  177. }
  178. /// Allocates 32-bit memory addresses for PCI BARs.
  179. struct PciMemory32Allocator {
  180. start: u32,
  181. end: u32,
  182. }
  183. impl PciMemory32Allocator {
  184. /// Creates a new allocator based on the ranges property of the given PCI node.
  185. pub fn for_pci_ranges(pci_node: &FdtNode) -> Self {
  186. let ranges = pci_node
  187. .property("ranges")
  188. .expect("PCI node missing ranges property.");
  189. let mut memory_32_address = 0;
  190. let mut memory_32_size = 0;
  191. for i in 0..ranges.value.len() / 28 {
  192. let range = &ranges.value[i * 28..(i + 1) * 28];
  193. let prefetchable = range[0] & 0x80 != 0;
  194. let range_type = PciRangeType::from(range[0] & 0x3);
  195. let bus_address = u64::from_be_bytes(range[4..12].try_into().unwrap());
  196. let cpu_physical = u64::from_be_bytes(range[12..20].try_into().unwrap());
  197. let size = u64::from_be_bytes(range[20..28].try_into().unwrap());
  198. info!(
  199. "range: {:?} {}prefetchable bus address: {:#018x} host physical address: {:#018x} size: {:#018x}",
  200. range_type,
  201. if prefetchable { "" } else { "non-" },
  202. bus_address,
  203. cpu_physical,
  204. size,
  205. );
  206. if range_type == PciRangeType::Memory32 && size > memory_32_size.into() {
  207. assert_eq!(bus_address, cpu_physical);
  208. memory_32_address = u32::try_from(cpu_physical).unwrap();
  209. memory_32_size = u32::try_from(size).unwrap();
  210. }
  211. }
  212. if memory_32_size == 0 {
  213. panic!("No 32-bit PCI memory region found.");
  214. }
  215. Self {
  216. start: memory_32_address,
  217. end: memory_32_address + memory_32_size,
  218. }
  219. }
  220. /// Allocates a 32-bit memory address region for a PCI BAR of the given power-of-2 size.
  221. ///
  222. /// It will have alignment matching the size. The size must be a power of 2.
  223. pub fn allocate_memory_32(&mut self, size: u32) -> u32 {
  224. assert!(size.is_power_of_two());
  225. let allocated_address = align_up(self.start, size);
  226. assert!(allocated_address + size <= self.end);
  227. self.start = allocated_address + size;
  228. allocated_address
  229. }
  230. }
  231. const fn align_up(value: u32, alignment: u32) -> u32 {
  232. ((value - 1) | (alignment - 1)) + 1
  233. }
  234. fn dump_bar_contents(root: &mut PciRoot, device_function: DeviceFunction, bar_index: u8) {
  235. let bar_info = root.bar_info(device_function, bar_index).unwrap();
  236. trace!("Dumping bar {}: {:#x?}", bar_index, bar_info);
  237. if let BarInfo::Memory { address, size, .. } = bar_info {
  238. let start = address as *const u8;
  239. unsafe {
  240. let mut buf = [0u8; 32];
  241. for i in 0..size / 32 {
  242. let ptr = start.add(i as usize * 32);
  243. ptr::copy(ptr, buf.as_mut_ptr(), 32);
  244. if buf.iter().any(|b| *b != 0xff) {
  245. trace!(" {:?}: {:x?}", ptr, buf);
  246. }
  247. }
  248. }
  249. }
  250. trace!("End of dump");
  251. }
  252. /// Allocates appropriately-sized memory regions and assigns them to the device's BARs.
  253. fn allocate_bars(
  254. root: &mut PciRoot,
  255. device_function: DeviceFunction,
  256. allocator: &mut PciMemory32Allocator,
  257. ) {
  258. let mut bar_index = 0;
  259. while bar_index < 6 {
  260. let info = root.bar_info(device_function, bar_index).unwrap();
  261. debug!("BAR {}: {}", bar_index, info);
  262. // Ignore I/O bars, as they aren't required for the VirtIO driver.
  263. if let BarInfo::Memory {
  264. address_type, size, ..
  265. } = info
  266. {
  267. match address_type {
  268. MemoryBarType::Width32 => {
  269. if size > 0 {
  270. let address = allocator.allocate_memory_32(size);
  271. debug!("Allocated address {:#010x}", address);
  272. root.set_bar_32(device_function, bar_index, address);
  273. }
  274. }
  275. MemoryBarType::Width64 => {
  276. if size > 0 {
  277. let address = allocator.allocate_memory_32(size);
  278. debug!("Allocated address {:#010x}", address);
  279. root.set_bar_64(device_function, bar_index, address.into());
  280. }
  281. }
  282. _ => panic!("Memory BAR address type {:?} not supported.", address_type),
  283. }
  284. }
  285. bar_index += 1;
  286. if info.takes_two_entries() {
  287. bar_index += 1;
  288. }
  289. }
  290. // Enable the device to use its BARs.
  291. root.set_command(
  292. device_function,
  293. Command::IO_SPACE | Command::MEMORY_SPACE | Command::BUS_MASTER,
  294. );
  295. }
  296. #[panic_handler]
  297. fn panic(info: &PanicInfo) -> ! {
  298. error!("{}", info);
  299. system_off().unwrap();
  300. loop {}
  301. }