crosvm/devices/src/direct_io.rs
Xiong Zhang 46471a03e1 Devices: Use mmap for direct mmio r/w
write_all_at()/read_exact_at() fail to access pmc_mux through /dev/mem.
while devmem2 tool success to access them through /dev/mem, so this
commit reference devmem2 implementation and use mmap to access direct
mmio.

BUG=b:199354528
TEST=Apply https://chromium-review.googlesource.com/c/chromiumos/platform/initramfs/+/3194150
, then verify platform device (like typec) function in ManaTEE CrOS

Change-Id: Id69c44444e2dc1ef6d40cb7b36febda38848d4f0
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3259931
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
2021-11-23 20:16:54 +00:00

199 lines
5.6 KiB
Rust

// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::{BusAccessInfo, BusDevice, BusDeviceSync, BusRange};
use base::{
error, pagesize, round_up_to_page_size, MemoryMapping, MemoryMappingBuilder, Protection,
};
use std::fs::{File, OpenOptions};
use std::io;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::prelude::FileExt;
use std::path::Path;
use std::sync::Mutex;
pub struct DirectIo {
dev: Mutex<File>,
read_only: bool,
}
impl DirectIo {
/// Create simple direct I/O access device.
pub fn new(path: &Path, read_only: bool) -> Result<Self, io::Error> {
let dev = OpenOptions::new().read(true).write(!read_only).open(path)?;
Ok(DirectIo {
dev: Mutex::new(dev),
read_only,
})
}
fn iowr(&self, port: u64, data: &[u8]) {
if !self.read_only {
if let Ok(ref mut dev) = self.dev.lock() {
let _ = dev.write_all_at(data, port);
}
}
}
fn iord(&self, port: u64, data: &mut [u8]) {
if let Ok(ref mut dev) = self.dev.lock() {
let _ = dev.read_exact_at(data, port);
}
}
}
impl BusDevice for DirectIo {
fn debug_label(&self) -> String {
"direct-io".to_string()
}
/// Reads at `offset` from this device
fn read(&mut self, ai: BusAccessInfo, data: &mut [u8]) {
self.iord(ai.address, data);
}
/// Writes at `offset` into this device
fn write(&mut self, ai: BusAccessInfo, data: &[u8]) {
self.iowr(ai.address, data);
}
}
impl BusDeviceSync for DirectIo {
/// Reads at `offset` from this device
fn read(&self, ai: BusAccessInfo, data: &mut [u8]) {
self.iord(ai.address, data);
}
/// Writes at `offset` into this device
fn write(&self, ai: BusAccessInfo, data: &[u8]) {
self.iowr(ai.address, data);
}
}
pub struct DirectMmio {
dev: Mutex<Vec<(BusRange, MemoryMapping)>>,
read_only: bool,
}
impl DirectMmio {
/// Create simple direct mmio access device.
pub fn new(path: &Path, read_only: bool, ranges: Vec<BusRange>) -> Result<Self, io::Error> {
let dev = OpenOptions::new()
.read(true)
.write(!read_only)
.custom_flags(libc::O_SYNC)
.open(path)?;
let mut mmap_info = Vec::new();
let protection = if read_only {
Protection::read()
} else {
Protection::read_write()
};
for range in &ranges {
// set to the page start
let start = range.base & (!((pagesize() - 1) as u64));
// set to the next page of the end address
let end = round_up_to_page_size((range.base + range.len) as usize);
let len = end - start as usize;
let mmap = match MemoryMappingBuilder::new(len)
.from_file(&dev)
.offset(start)
.protection(protection)
.build()
{
Ok(m) => m,
Err(e) => {
error!(
"failed to create mmap for mmio: {:x} ~ {:x}, error: {}",
range.base,
range.base + range.len,
e
);
continue;
}
};
mmap_info.push((*range, mmap));
}
Ok(DirectMmio {
dev: Mutex::new(mmap_info),
read_only,
})
}
fn iowr(&self, ai: BusAccessInfo, data: &[u8]) {
if self.read_only {
return;
}
let dev = match self.dev.lock() {
Ok(d) => d,
Err(_) => return,
};
for (range, mmap) in dev.iter() {
if !range.contains(ai.address) || !range.contains(ai.address + data.len() as u64) {
continue;
}
let page_mask = (pagesize() - 1) as u64;
let offset = (range.base & page_mask) + ai.offset;
if let Err(e) = mmap.write_slice(data, offset as usize) {
error!("write mmio {:x} error, {}", ai.address, e);
}
return;
}
}
fn iord(&self, ai: BusAccessInfo, data: &mut [u8]) {
let dev = match self.dev.lock() {
Ok(d) => d,
Err(_) => return,
};
for (range, mmap) in dev.iter() {
if !range.contains(ai.address) || !range.contains(ai.address + data.len() as u64) {
continue;
}
let page_mask = (pagesize() - 1) as u64;
let offset = (range.base & page_mask) + ai.offset;
if let Err(e) = mmap.read_slice(data, offset as usize) {
error!("read mmio {:x} error {}", ai.address, e);
}
return;
}
}
}
impl BusDevice for DirectMmio {
fn debug_label(&self) -> String {
"direct-mmio".to_string()
}
/// Reads at `offset` from this device
fn read(&mut self, ai: BusAccessInfo, data: &mut [u8]) {
self.iord(ai, data);
}
/// Writes at `offset` into this device
fn write(&mut self, ai: BusAccessInfo, data: &[u8]) {
self.iowr(ai, data);
}
}
impl BusDeviceSync for DirectMmio {
/// Reads at `offset` from this device
fn read(&self, ai: BusAccessInfo, data: &mut [u8]) {
self.iord(ai, data);
}
/// Writes at `offset` into this device
fn write(&self, ai: BusAccessInfo, data: &[u8]) {
self.iowr(ai, data);
}
}