• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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