1 // Copyright 2021 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 use crate::{BusAccessInfo, BusDevice, BusDeviceSync, BusRange}; 6 use base::{ 7 error, pagesize, round_up_to_page_size, MemoryMapping, MemoryMappingBuilder, Protection, 8 }; 9 use std::fs::{File, OpenOptions}; 10 use std::io; 11 use std::os::unix::fs::OpenOptionsExt; 12 use std::os::unix::prelude::FileExt; 13 use std::path::Path; 14 use std::sync::Mutex; 15 16 pub struct DirectIo { 17 dev: Mutex<File>, 18 read_only: bool, 19 } 20 21 impl DirectIo { 22 /// Create simple direct I/O access device. new(path: &Path, read_only: bool) -> Result<Self, io::Error>23 pub fn new(path: &Path, read_only: bool) -> Result<Self, io::Error> { 24 let dev = OpenOptions::new().read(true).write(!read_only).open(path)?; 25 Ok(DirectIo { 26 dev: Mutex::new(dev), 27 read_only, 28 }) 29 } 30 iowr(&self, port: u64, data: &[u8])31 fn iowr(&self, port: u64, data: &[u8]) { 32 if !self.read_only { 33 if let Ok(ref mut dev) = self.dev.lock() { 34 let _ = dev.write_all_at(data, port); 35 } 36 } 37 } 38 iord(&self, port: u64, data: &mut [u8])39 fn iord(&self, port: u64, data: &mut [u8]) { 40 if let Ok(ref mut dev) = self.dev.lock() { 41 let _ = dev.read_exact_at(data, port); 42 } 43 } 44 } 45 46 impl BusDevice for DirectIo { debug_label(&self) -> String47 fn debug_label(&self) -> String { 48 "direct-io".to_string() 49 } 50 51 /// Reads at `offset` from this device read(&mut self, ai: BusAccessInfo, data: &mut [u8])52 fn read(&mut self, ai: BusAccessInfo, data: &mut [u8]) { 53 self.iord(ai.address, data); 54 } 55 56 /// Writes at `offset` into this device write(&mut self, ai: BusAccessInfo, data: &[u8])57 fn write(&mut self, ai: BusAccessInfo, data: &[u8]) { 58 self.iowr(ai.address, data); 59 } 60 } 61 62 impl BusDeviceSync for DirectIo { 63 /// Reads at `offset` from this device read(&self, ai: BusAccessInfo, data: &mut [u8])64 fn read(&self, ai: BusAccessInfo, data: &mut [u8]) { 65 self.iord(ai.address, data); 66 } 67 68 /// Writes at `offset` into this device write(&self, ai: BusAccessInfo, data: &[u8])69 fn write(&self, ai: BusAccessInfo, data: &[u8]) { 70 self.iowr(ai.address, data); 71 } 72 } 73 74 pub struct DirectMmio { 75 dev: Mutex<Vec<(BusRange, MemoryMapping)>>, 76 read_only: bool, 77 } 78 79 impl DirectMmio { 80 /// Create simple direct mmio access device. new(path: &Path, read_only: bool, ranges: &[BusRange]) -> Result<Self, io::Error>81 pub fn new(path: &Path, read_only: bool, ranges: &[BusRange]) -> Result<Self, io::Error> { 82 let dev = OpenOptions::new() 83 .read(true) 84 .write(!read_only) 85 .custom_flags(libc::O_SYNC) 86 .open(path)?; 87 let mut mmap_info = Vec::new(); 88 89 let protection = if read_only { 90 Protection::read() 91 } else { 92 Protection::read_write() 93 }; 94 95 for range in ranges { 96 // set to the page start 97 let start = range.base & (!((pagesize() - 1) as u64)); 98 // set to the next page of the end address 99 let end = round_up_to_page_size((range.base + range.len) as usize); 100 let len = end - start as usize; 101 let mmap = match MemoryMappingBuilder::new(len) 102 .from_file(&dev) 103 .offset(start) 104 .protection(protection) 105 .build() 106 { 107 Ok(m) => m, 108 Err(e) => { 109 error!( 110 "failed to create mmap for mmio: {:x} ~ {:x}, error: {}", 111 range.base, 112 range.base + range.len, 113 e 114 ); 115 continue; 116 } 117 }; 118 119 mmap_info.push((*range, mmap)); 120 } 121 122 Ok(DirectMmio { 123 dev: Mutex::new(mmap_info), 124 read_only, 125 }) 126 } 127 iowr(&self, ai: BusAccessInfo, data: &[u8])128 fn iowr(&self, ai: BusAccessInfo, data: &[u8]) { 129 if self.read_only { 130 return; 131 } 132 133 let dev = match self.dev.lock() { 134 Ok(d) => d, 135 Err(_) => return, 136 }; 137 138 for (range, mmap) in dev.iter() { 139 if !range.contains(ai.address) || !range.contains(ai.address + data.len() as u64) { 140 continue; 141 } 142 143 let page_mask = (pagesize() - 1) as u64; 144 let offset = (range.base & page_mask) + ai.offset; 145 if let Err(e) = mmap.write_slice(data, offset as usize) { 146 error!("write mmio {:x} error, {}", ai.address, e); 147 } 148 return; 149 } 150 } 151 iord(&self, ai: BusAccessInfo, data: &mut [u8])152 fn iord(&self, ai: BusAccessInfo, data: &mut [u8]) { 153 let dev = match self.dev.lock() { 154 Ok(d) => d, 155 Err(_) => return, 156 }; 157 158 for (range, mmap) in dev.iter() { 159 if !range.contains(ai.address) || !range.contains(ai.address + data.len() as u64) { 160 continue; 161 } 162 163 let page_mask = (pagesize() - 1) as u64; 164 let offset = (range.base & page_mask) + ai.offset; 165 if let Err(e) = mmap.read_slice(data, offset as usize) { 166 error!("read mmio {:x} error {}", ai.address, e); 167 } 168 return; 169 } 170 } 171 } 172 173 impl BusDevice for DirectMmio { debug_label(&self) -> String174 fn debug_label(&self) -> String { 175 "direct-mmio".to_string() 176 } 177 178 /// Reads at `offset` from this device read(&mut self, ai: BusAccessInfo, data: &mut [u8])179 fn read(&mut self, ai: BusAccessInfo, data: &mut [u8]) { 180 self.iord(ai, data); 181 } 182 183 /// Writes at `offset` into this device write(&mut self, ai: BusAccessInfo, data: &[u8])184 fn write(&mut self, ai: BusAccessInfo, data: &[u8]) { 185 self.iowr(ai, data); 186 } 187 } 188 189 impl BusDeviceSync for DirectMmio { 190 /// Reads at `offset` from this device read(&self, ai: BusAccessInfo, data: &mut [u8])191 fn read(&self, ai: BusAccessInfo, data: &mut [u8]) { 192 self.iord(ai, data); 193 } 194 195 /// Writes at `offset` into this device write(&self, ai: BusAccessInfo, data: &[u8])196 fn write(&self, ai: BusAccessInfo, data: &[u8]) { 197 self.iowr(ai, data); 198 } 199 } 200