From 3871c99e6efc4572516fb2b8f793842bcf1a430e Mon Sep 17 00:00:00 2001 From: Ivan Tan Date: Sat, 9 Dec 2023 12:16:06 +0800 Subject: [PATCH] add bcm2835-sdhci driver --- Cargo.lock | 18 + Cargo.toml | 2 + bcm2835-sdhcid/Cargo.toml | 15 + bcm2835-sdhcid/src/main.rs | 115 ++++++ bcm2835-sdhcid/src/scheme.rs | 441 ++++++++++++++++++++ bcm2835-sdhcid/src/sd/mod.rs | 776 +++++++++++++++++++++++++++++++++++ common/src/lib.rs | 2 + 7 files changed, 1369 insertions(+) create mode 100644 bcm2835-sdhcid/Cargo.toml create mode 100644 bcm2835-sdhcid/src/main.rs create mode 100644 bcm2835-sdhcid/src/scheme.rs create mode 100644 bcm2835-sdhcid/src/sd/mod.rs diff --git a/Cargo.lock b/Cargo.lock index ce0e684304..434df3e8e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -158,6 +158,18 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" +[[package]] +name = "bcm2835-sdhcid" +version = "0.1.0" +dependencies = [ + "block-io-wrapper", + "common", + "fdt", + "partitionlib", + "redox-daemon", + "redox_syscall 0.4.1", +] + [[package]] name = "bgad" version = "0.1.0" @@ -384,6 +396,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "fdt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67" + [[package]] name = "fuchsia-cprng" version = "0.1.1" diff --git a/Cargo.toml b/Cargo.toml index a1eba0a22b..2298868ad1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,8 @@ members = [ "virtio-netd", "virtio-gpud", "virtio-core", + + "bcm2835-sdhcid", ] [profile.release] diff --git a/bcm2835-sdhcid/Cargo.toml b/bcm2835-sdhcid/Cargo.toml new file mode 100644 index 0000000000..faa5ddfb77 --- /dev/null +++ b/bcm2835-sdhcid/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "bcm2835-sdhcid" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +fdt = "0.1.5" +redox_syscall = "0.4" +partitionlib = { git = "https://gitlab.redox-os.org/redox-os/partitionlib.git" } +block-io-wrapper = { path = "../block-io-wrapper" } +common = { path = "../common" } + +redox-daemon = "0.1" diff --git a/bcm2835-sdhcid/src/main.rs b/bcm2835-sdhcid/src/main.rs new file mode 100644 index 0000000000..f70e8cca4a --- /dev/null +++ b/bcm2835-sdhcid/src/main.rs @@ -0,0 +1,115 @@ +use std::{fs::File, io::{Read, Write}, process}; + +use fdt::{Fdt, node::FdtNode}; +use syscall::{Packet, SchemeBlockMut}; + +use crate::sd::Disk; +use crate::scheme::DiskScheme; + +mod sd; +mod scheme; + +#[cfg(target_os = "redox")] +fn get_dtb() -> Vec { + std::fs::read("kernel.dtb:").unwrap() +} + +#[cfg(target_os = "linux")] +fn get_dtb() -> Vec { + use std::env; + if let Some(arg1) = env::args().nth(1) { + std::fs::read(arg1).unwrap() + } else { + Vec::new() + } +} + +fn main() { + redox_daemon::Daemon::new(daemon).expect("mmc:failed to daemonize"); +} + +fn daemon(daemon: redox_daemon::Daemon) -> ! { + let dtb_data = get_dtb(); + println!("read from OS, len = {}", dtb_data.len()); + if dtb_data.len() == 0 { + process::exit(0); + } + + let fdt = Fdt::new(&dtb_data).unwrap(); + println!("DTB model = {}", fdt.root().model()); + let with = ["brcm,bcm2835-sdhcid"]; + let compat_node = fdt.find_compatible(&with).unwrap(); + let reg = compat_node.reg().unwrap().next().unwrap(); + let reg_size = reg.size.unwrap(); + println!("DeviceMemory start = 0x{:08x}, size = 0x{:08x}", reg.starting_address as usize, reg_size); + let addr = unsafe { + common::physmap(reg.starting_address as usize, reg_size, common::Prot::RW, common::MemoryType::DeviceMemory) + .expect("bcm2835-sdhcid: failed to map address") as usize + }; + println!("ioremap 0x{:08x} to 0x{:08x} 2222", reg.starting_address as usize, addr); + let mut sdhci = sd::SdHostCtrl::new(addr); + unsafe { + sdhci.init(); + /* + let mut buf1 = [0u32; 512]; + sdhci.sd_readblock(1, &mut buf1, 1); + println!("readblock {:?}", buf1); + buf1[0] = 0xdead_0000; + buf1[1] = 0xdead_0000; + buf1[2] = 0x0000_dead; + buf1[3] = 0x0000_dead; + sdhci.sd_writeblock(1, &buf1, 1); + sdhci.sd_readblock(1, &mut buf1, 1); + println!("readblock {:?}", buf1); + */ + /* + let mut buf1 = [0u8; 512]; + sdhci.read(1, &mut buf1); + println!("readblock {:?}", buf1); + buf1[0] = 0xde; + buf1[1] = 0xad; + buf1[2] = 0xde; + buf1[3] = 0xad; + sdhci.write(1, &buf1); + sdhci.read(1, &mut buf1); + println!("readblock {:?}", buf1); + */ + } + + + let scheme_name = ":disk.mmc"; + let mut socket = File::create(scheme_name).expect("mmc: failed to create disk scheme"); + syscall::setrens(0, 0).expect("mmc: failed to enter null namespace"); + + daemon.ready().expect("mmc: failed to notify parent"); + + let mut todo = Vec::new(); + let mut disks = Vec::new(); + + disks.push(Box::new(sdhci) as Box); + let mut scheme = DiskScheme::new(scheme_name.to_string(), disks); + loop { + let mut packet = Packet::default(); + if socket.read(&mut packet).expect("mmc: failed to read event") == 0 { + println!("zero, break"); + break; + } + if let Some(a) = scheme.handle(&packet) { + packet.a = a; + socket.write(&packet).expect("mmcd: failed to write disk scheme"); + } else { + todo.push(packet); + } + let mut i = 0; + while i < todo.len() { + if let Some(a) = scheme.handle(&todo[i]) { + let mut packet = todo.remove(i); + packet.a = a; + socket.write(&packet).expect("mmcd: failed to write disk scheme"); + } else { + i += 1; + } + } + } + process::exit(0); +} diff --git a/bcm2835-sdhcid/src/scheme.rs b/bcm2835-sdhcid/src/scheme.rs new file mode 100644 index 0000000000..fce64bb363 --- /dev/null +++ b/bcm2835-sdhcid/src/scheme.rs @@ -0,0 +1,441 @@ +use std::collections::BTreeMap; +use std::{cmp, str}; +use std::convert::{TryFrom}; +use std::fmt::Write; +use std::io::prelude::*; +use std::io::SeekFrom; +use std::io; +use std::sync::{Arc, Mutex}; + +use syscall::{ + Error, EACCES, EBADF, EINVAL, EISDIR, ENOENT, ENOLCK, EOVERFLOW, Result, + Io, SchemeBlockMut, Stat, MODE_DIR, MODE_FILE, O_DIRECTORY, + O_STAT, SEEK_CUR, SEEK_END, SEEK_SET}; + +use crate::sd::Disk; + +use partitionlib::{LogicalBlockSize, PartitionTable}; + +#[derive(Clone)] +enum Handle { + List(Vec, usize), // Dir contents buffer, position + Disk(usize, usize), // Disk index, position + Partition(usize, u32, usize), // Disk index, partition index, position +} + +pub struct DiskWrapper { + disk: Box, + pt: Option, +} + +impl DiskWrapper { + fn pt(disk: &mut dyn Disk) -> Option { + let bs = match disk.block_length() { + Ok(512) => LogicalBlockSize::Lb512, + _ => return None, + }; + struct Device<'a, 'b> { disk: &'a mut dyn Disk, offset: u64, block_bytes: &'b mut [u8] } + + impl<'a, 'b> Seek for Device<'a, 'b> { + fn seek(&mut self, from: SeekFrom) -> io::Result { + let size = i64::try_from(self.disk.size()).or(Err(io::Error::new(io::ErrorKind::Other, "Disk larger than 2^63 - 1 bytes")))?; + + self.offset = match from { + SeekFrom::Start(new_pos) => cmp::min(self.disk.size(), new_pos), + SeekFrom::Current(new_pos) => cmp::max(0, cmp::min(size, self.offset as i64 + new_pos)) as u64, + SeekFrom::End(new_pos) => cmp::max(0, cmp::min(size + new_pos, size)) as u64, + }; + + Ok(self.offset) + } + } + // TODO: Perhaps this impl should be used in the rest of the scheme. + impl<'a, 'b> Read for Device<'a, 'b> { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let blksize = self.disk.block_length().map_err(|err| io::Error::from_raw_os_error(err.errno))?; + let size_in_blocks = self.disk.size() / u64::from(blksize); + + let disk = &mut self.disk; + + let read_block = |block: u64, block_bytes: &mut [u8]| { + if block >= size_in_blocks { + return Err(io::Error::from_raw_os_error(syscall::EOVERFLOW)); + } + loop { + match disk.read(block, block_bytes) { + Ok(Some(bytes)) => { + assert_eq!(bytes, block_bytes.len()); + assert_eq!(bytes, blksize as usize); + return Ok(()); + } + Ok(None) => { std::thread::yield_now(); continue } + Err(err) => return Err(io::Error::from_raw_os_error(err.errno)), + } + } + }; + let bytes_read = block_io_wrapper::read(self.offset, blksize, buf, self.block_bytes, read_block)?; + + self.offset += bytes_read as u64; + Ok(bytes_read) + } + } + + let mut block_bytes = [0u8; 4096]; + + partitionlib::get_partitions(&mut Device { disk, offset: 0, block_bytes: &mut block_bytes[..bs.into()] }, bs).ok().flatten() + } + fn new(mut disk: Box) -> Self { + Self { + pt: Self::pt(&mut *disk), + disk, + } + } +} + +impl std::ops::Deref for DiskWrapper { + type Target = dyn Disk; + + fn deref(&self) -> &Self::Target { + &*self.disk + } +} +impl std::ops::DerefMut for DiskWrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut *self.disk + } +} + +pub struct DiskScheme { + scheme_name: String, + disks: Box<[DiskWrapper]>, + handles: BTreeMap, + next_id: usize +} + +impl DiskScheme { + pub fn new(scheme_name: String, disks: Vec>) -> DiskScheme { + DiskScheme { + scheme_name: scheme_name, + disks: disks.into_iter().map(DiskWrapper::new).collect::>().into_boxed_slice(), + handles: BTreeMap::new(), + next_id: 0 + } + } + + // Checks if any conflicting handles already exist + fn check_locks(&self, disk_i: usize, part_i_opt: Option) -> Result<()> { + for (_, handle) in self.handles.iter() { + match handle { + Handle::Disk(i, _) => if disk_i == *i { + return Err(Error::new(ENOLCK)); + }, + Handle::Partition(i, p, _) => if disk_i == *i { + match part_i_opt { + Some(part_i) => if part_i == *p { + return Err(Error::new(ENOLCK)); + }, + None => { + return Err(Error::new(ENOLCK)); + } + } + }, + _ => (), + } + } + Ok(()) + } +} + +impl SchemeBlockMut for DiskScheme { + fn open(&mut self, path: &str, flags: usize, uid: u32, _gid: u32) -> Result> { + if uid == 0 { + let path_str = path.trim_matches('/'); + if path_str.is_empty() { + if flags & O_DIRECTORY == O_DIRECTORY || flags & O_STAT == O_STAT { + let mut list = String::new(); + + for (disk_index, disk) in self.disks.iter().enumerate() { + write!(list, "{}\n", disk_index).unwrap(); + + if disk.pt.is_none() { + continue + } + for part_index in 0..disk.pt.as_ref().unwrap().partitions.len() { + write!(list, "{}p{}\n", disk_index, part_index).unwrap(); + } + } + + let id = self.next_id; + self.next_id += 1; + self.handles.insert(id, Handle::List(list.into_bytes(), 0)); + Ok(Some(id)) + } else { + Err(Error::new(EISDIR)) + } + } else if let Some(p_pos) = path_str.chars().position(|c| c == 'p') { + let disk_id_str = &path_str[..p_pos]; + if p_pos + 1 >= path_str.len() { + return Err(Error::new(ENOENT)); + } + let part_id_str = &path_str[p_pos + 1..]; + let i = disk_id_str.parse::().or(Err(Error::new(ENOENT)))?; + let p = part_id_str.parse::().or(Err(Error::new(ENOENT)))?; + + if let Some(disk) = self.disks.get(i) { + if disk.pt.is_none() || disk.pt.as_ref().unwrap().partitions.get(p as usize).is_none() { + return Err(Error::new(ENOENT)); + } + + self.check_locks(i, Some(p))?; + + let id = self.next_id; + self.next_id += 1; + self.handles.insert(id, Handle::Partition(i, p, 0)); + Ok(Some(id)) + } else { + Err(Error::new(ENOENT)) + } + } else { + let i = path_str.parse::().or(Err(Error::new(ENOENT)))?; + + if self.disks.get(i).is_some() { + self.check_locks(i, None)?; + + let id = self.next_id; + self.next_id += 1; + self.handles.insert(id, Handle::Disk(i, 0)); + Ok(Some(id)) + } else { + Err(Error::new(ENOENT)) + } + } + } else { + Err(Error::new(EACCES)) + } + } + + fn dup(&mut self, id: usize, buf: &[u8]) -> Result> { + if ! buf.is_empty() { + return Err(Error::new(EINVAL)); + } + + let new_handle = { + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + handle.clone() + }; + + let new_id = self.next_id; + self.next_id += 1; + self.handles.insert(new_id, new_handle); + Ok(Some(new_id)) + } + + fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result> { + match *self.handles.get(&id).ok_or(Error::new(EBADF))? { + Handle::List(ref data, _) => { + stat.st_mode = MODE_DIR; + stat.st_size = data.len() as u64; + Ok(Some(0)) + }, + Handle::Disk(number, _) => { + let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?; + stat.st_mode = MODE_FILE; + stat.st_size = disk.size(); + stat.st_blksize = disk.block_length()?; + Ok(Some(0)) + } + Handle::Partition(disk_id, part_num, _) => { + let disk = self.disks.get_mut(disk_id).ok_or(Error::new(EBADF))?; + let size = { + let pt = disk.pt.as_ref().ok_or(Error::new(EBADF))?; + let partition = pt.partitions.get(part_num as usize).ok_or(Error::new(EBADF))?; + partition.size + }; + + stat.st_mode = MODE_FILE; // TODO: Block device? + stat.st_size = size * u64::from(disk.block_length()?); + stat.st_blksize = disk.block_length()?; + stat.st_blocks = size; + Ok(Some(0)) + } + } + } + + fn fpath(&mut self, id: usize, buf: &mut [u8]) -> Result> { + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + let mut i = 0; + + let scheme_name = self.scheme_name.as_bytes(); + let mut j = 0; + while i < buf.len() && j < scheme_name.len() { + buf[i] = scheme_name[j]; + i += 1; + j += 1; + } + + if i < buf.len() { + buf[i] = b':'; + i += 1; + } + + match *handle { + Handle::List(_, _) => (), + Handle::Disk(number, _) => { + let number_str = format!("{}", number); + let number_bytes = number_str.as_bytes(); + j = 0; + while i < buf.len() && j < number_bytes.len() { + buf[i] = number_bytes[j]; + i += 1; + j += 1; + } + } + Handle::Partition(disk_num, part_num, _) => { + let path = format!("{}p{}", disk_num, part_num); + let path_bytes = path.as_bytes(); + j = 0; + while i < buf.len() && j < path_bytes.len() { + buf[i] = path_bytes[j]; + i += 1; + j += 1; + } + } + } + + Ok(Some(i)) + } + + fn read(&mut self, id: usize, buf: &mut [u8]) -> Result> { + match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? { + Handle::List(ref handle, ref mut size) => { + let count = (&handle[*size..]).read(buf).unwrap(); + *size += count; + Ok(Some(count)) + }, + Handle::Disk(number, ref mut size) => { + let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?; + let blk_len = disk.block_length()?; + if let Some(count) = disk.read((*size as u64)/(blk_len as u64), buf)? { + *size += count; + Ok(Some(count)) + } else { + Ok(None) + } + } + Handle::Partition(disk_num, part_num, ref mut position) => { + let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?; + let blksize = disk.block_length()?; + + // validate that we're actually reading within the bounds of the partition + let rel_block = *position as u64 / blksize as u64; + + let abs_block = { + let pt = disk.pt.as_ref().ok_or(Error::new(EBADF))?; + let partition = pt.partitions.get(part_num as usize).ok_or(Error::new(EBADF))?; + + let abs_block = partition.start_lba + rel_block; + if rel_block >= partition.size { + return Err(Error::new(EOVERFLOW)); + } + abs_block + }; + + if let Some(count) = disk.read(abs_block, buf)? { + Ok(Some(count)) + } else { + Ok(None) + } + } + } + } + + fn write(&mut self, id: usize, buf: &[u8]) -> Result> { + match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? { + Handle::List(_, _) => { + Err(Error::new(EBADF)) + }, + Handle::Disk(number, ref mut size) => { + let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?; + let blk_len = disk.block_length()?; + if let Some(count) = disk.write((*size as u64)/(blk_len as u64), buf)? { + *size += count; + Ok(Some(count)) + } else { + Ok(None) + } + } + Handle::Partition(disk_num, part_num, ref mut position) => { + let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?; + let blksize = disk.block_length()?; + + // validate that we're actually reading within the bounds of the partition + let rel_block = *position as u64 / blksize as u64; + + let abs_block = { + let pt = disk.pt.as_ref().ok_or(Error::new(EBADF))?; + let partition = pt.partitions.get(part_num as usize).ok_or(Error::new(EBADF))?; + + let abs_block = partition.start_lba + rel_block; + if rel_block >= partition.size { + return Err(Error::new(EOVERFLOW)); + } + abs_block + }; + + if let Some(count) = disk.write(abs_block, buf)? { + Ok(Some(count)) + } else { + Ok(None) + } + } + } + } + + fn seek(&mut self, id: usize, pos: isize, whence: usize) -> Result> { + let pos = pos as usize; + + match *self.handles.get_mut(&id).ok_or(Error::new(EBADF))? { + Handle::List(ref mut handle, ref mut size) => { + let len = handle.len() as usize; + *size = match whence { + SEEK_SET => cmp::min(len, pos), + SEEK_CUR => cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize, + SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize, + _ => return Err(Error::new(EINVAL)) + }; + + Ok(Some(*size as isize)) + }, + Handle::Disk(number, ref mut size) => { + let disk = self.disks.get_mut(number).ok_or(Error::new(EBADF))?; + let len = disk.size() as usize; + *size = match whence { + SEEK_SET => cmp::min(len, pos), + SEEK_CUR => cmp::max(0, cmp::min(len as isize, *size as isize + pos as isize)) as usize, + SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize, + _ => return Err(Error::new(EINVAL)) + }; + + Ok(Some(*size as isize)) + } + Handle::Partition(disk_num, part_num, ref mut position) => { + let disk = self.disks.get_mut(disk_num).ok_or(Error::new(EBADF))?; + let block_count = disk.pt.as_ref().ok_or(Error::new(EBADF))?.partitions.get(part_num as usize).ok_or(Error::new(EBADF))?.size; + let len = u64::from(disk.block_length()?) * block_count; + + *position = match whence { + SEEK_SET => cmp::min(len as usize, pos) as usize, // Why isn't pos u64? + SEEK_CUR => cmp::max(0, cmp::min(len as isize, *position as isize + pos as isize)) as usize, + SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize, + _ => return Err(Error::new(EINVAL)), + }; + Ok(Some(*position as isize)) + } + } + } + + fn close(&mut self, id: usize) -> Result> { + self.handles.remove(&id).ok_or(Error::new(EBADF)).and(Ok(Some(0))) + } +} diff --git a/bcm2835-sdhcid/src/sd/mod.rs b/bcm2835-sdhcid/src/sd/mod.rs new file mode 100644 index 0000000000..be5c37df7b --- /dev/null +++ b/bcm2835-sdhcid/src/sd/mod.rs @@ -0,0 +1,776 @@ +use std::{sync::{Mutex, RwLock}, time::{self, Duration}, thread}; +use syscall::{io::Mmio, Io, Error, Result, EINVAL}; +use core::ptr::{read_volatile, write_volatile}; + +#[cfg(target_arch = "aarch64")] +#[inline(always)] +pub(crate) unsafe fn wait_cycles(mut n: usize) { + use core::arch::asm; + + while n > 0 { + asm!("nop"); + n -= 1; + } +} + +#[cfg(target_arch = "aarch64")] +#[inline(always)] +pub(crate) unsafe fn wait_msec(mut n: usize) { + use core::arch::asm; + + let mut f: usize; + let mut t: usize; + let mut r: usize; + + asm!("mrs {0}, cntfrq_el0", out(reg) f); + asm!("mrs {0}, cntpct_el0", out(reg) t); + + + t += ((f / 1000) * n) / 1000; + + loop { + asm!("mrs {0}, cntpct_el0", out(reg) r); + if r >= t { + break; + } + }; +} + +#[cfg(target_arch = "x86_64")] +#[inline(always)] +pub(crate) unsafe fn wait_msec(mut n: usize) { + thread::sleep(Duration::from_millis(n as u64)); +} + +//cmd Flags +const CMD_NEED_APP: u32 = 0x8000_0000; +const CMD_RSPNS_48: u32 = 0x0002_0000; +const CMD_ERRORS_MASK: u32 = 0xfff9_c004; +const CMD_RCA_MASK: u32 = 0xffff_0000; + +//CMD +const CMD_GO_IDLE: u32 = 0x0000_0000; +const CMD_ALL_SEND_CID: u32 = 0x0201_0000; +const CMD_SEND_CSD: u32 = 0x0901_0000; +const CMD_SEND_REL_ADDR: u32 = 0x0302_0000; +const CMD_CARD_SELECT: u32 = 0x0703_0000; +const CMD_SEND_IF_COND: u32 = 0x0802_0000; +const CMD_STOP_TRANS: u32 = 0x0c03_0000; +const CMD_READ_SINGLE: u32 = 0x1122_0010; +const CMD_READ_MULTI: u32 = 0x1222_0032; +const CMD_SET_BLOCKCNT: u32 = 0x1702_0000; +const CMD_WRITE_SINGLE:u32 = 0x1822_0000; +const CMD_WRITE_MULTI:u32 = 0x1922_0022; + +const CMD_APP_CMD: u32 = 0x3700_0000; +const CMD_SET_BUS_WIDTH: u32 = 0x0602_0000|CMD_NEED_APP; +const CMD_SEND_OP_COND: u32 = 0x2902_0000|CMD_NEED_APP; +const CMD_SEND_SCR: u32 = 0x3322_0010|CMD_NEED_APP; + + + +//STATUS register settings +const SR_READ_AVAILABLE: u32 = 0x0000_0800; +const SR_WRITE_AVAILABLE: u32 = 0x0000_0400; +const SR_DAT_INHIBIT: u32 = 0x0000_0002; +const SR_CMD_INHIBIT: u32 = 0x0000_0001; +const SR_APP_CMD: u32 = 0x0000_0020; + +//CONTROL register settings + +const C0_SPI_MODE_EN: u32 = 0x0010_0000; +const C0_HCTL_HS_EN: u32 = 0x0000_0004; +const C0_HCTL_DWITDH: u32 = 0x0000_0002; + +const C1_SRST_DATA: u32 = 0x0400_0000; +const C1_SRST_CMD: u32 = 0x0200_0000; +const C1_SRST_HC: u32 = 0x0100_0000; +const C1_TOUNIT_DIS: u32 = 0x000f_0000; +const C1_TOUNIT_MAX: u32 = 0x000e_0000; +const C1_CLK_GENSEL: u32 = 0x0000_0020; +const C1_CLK_EN: u32 = 0x0000_0004; +const C1_CLK_STABLE: u32 = 0x0000_0002; +const C1_CLK_INTLEN: u32 = 0x0000_0001; + + +//INTERRUPT register settings +const INT_DATA_TIMEOUT: u32 = 0x0010_0000; +const INT_CMD_TIMEOUT: u32 = 0x0001_0000; +const INT_READ_RDY: u32 = 0x0000_0020; +const INT_WRITE_RDY: u32 = 0x0000_0010; +const INT_DATA_DONE: u32 = 0x0000_0002; +const INT_CMD_DONE: u32 = 0x0000_0001; +const INT_ERROR_MASK: u32 = 0x017e_8000; + +const HOST_SPEC_VERSION_OFFSET: u32 = 16; +const HOST_SPEC_VERSION_MASK: u32 = 0x00ff_0000; +const HOST_SPEC_V3: u32 = 2; +const HOST_SPEC_V2: u32 = 1; +const HOST_SPEC_V1: u32 = 0; + +const ACMD41_VOLTAGE: u32 = 0x00ff_8000; +const ACMD41_CMD_COMPLETE: u32 = 0x8000_0000; +const ACMD41_CMD_CCS: u32 = 0x4000_0000; +const ACMD41_ARG_HC: u32 = 0x51ff_8000; + +const SCR_SD_BUS_WIDTH_4: u32 = 0x0000_0400; +const SCR_SUPP_SET_BLKCNT: u32 = 0x0200_0000; +//added by bztsrc driver +const SCR_SUPP_CCS: u32 = 0x0000_0001; + +#[repr(packed)] +pub struct SdHostCtrlRegs { + //LSB + + //ACMD23 Argument + _arg2: Mmio, + + //Block Size and Count + blksizecnt: Mmio, + + //Argument + arg1: Mmio, + + //Command and Transfer Mode + cmdtm: Mmio, + + //Response bit 0-127 + resp0: Mmio, + resp1: Mmio, + resp2: Mmio, + resp3: Mmio, + + //Data + data: Mmio, + + //Status + status: Mmio, + + //Host Configuration bits + control0: Mmio, + + //Host Configuration bits + control1: Mmio, + + //Interrupt Flags + interrupt: Mmio, + + //Interrupt Flag Enable + irpt_mask: Mmio, + + //Interrupt Generation Enable + irpt_en: Mmio, + + //Host Configuration bits + _control2: Mmio, + + _rsvd: [Mmio; 47], + + //Slot Interrupt Status and Version + slotisr_ver: Mmio +} + +//TODO: refactor, sd/sdhci/bcmh2835-sdhci three different modules. +pub struct SdHostCtrl { + regs: RwLock<&'static mut SdHostCtrlRegs>, + host_spec_ver: u32, + cid: [u32; 4], + csd: [u32; 4], + rca: u32, //relative card address + scr: [u32; 2], + ocr: u32, + size: u64 +} + +impl SdHostCtrl { + pub fn new(address: usize) -> Self { + SdHostCtrl { + regs: RwLock::new(unsafe { &mut *(address as *mut SdHostCtrlRegs)}), + host_spec_ver: 0, + cid: [0; 4], + csd: [0; 4], + rca: 0, + scr: [0; 2], + ocr: 0, + size: 0, + } + } + + pub unsafe fn init(&mut self) { + let regs = self.regs.get_mut().unwrap(); + + let mut reg_val = regs.slotisr_ver.read(); + self.host_spec_ver = (reg_val & HOST_SPEC_VERSION_MASK) >> HOST_SPEC_VERSION_OFFSET; + + regs.control0.write(0x0); + reg_val = regs.control1.read(); + regs.control1.write(reg_val | C1_SRST_HC); + let mut cnt = 1000; + while (cnt >= 0) && + ((regs.control1.read() & C1_SRST_HC) == C1_SRST_HC) { + cnt -= 1; + wait_msec(10); + } + + if cnt < 0 { + println!("ERROR: failed to reset EMMC"); + return ; + } + println!("EMMC: reset OK"); + reg_val = regs.control1.read(); + regs.control1.write(reg_val | C1_CLK_INTLEN | C1_TOUNIT_MAX); + + wait_msec(10); + + { + if let Err(_) = self.set_clock(40_0000) { + println!("ERROR: failed to set clock {}", 40_0000); + return ; + } + } + + let regs = self.regs.get_mut().unwrap(); + regs.irpt_en.write(0xffff_ffff); + regs.irpt_mask.write(0xffff_ffff); + + if let Err(_) = self.sd_cmd(CMD_GO_IDLE, 0) { + println!("failed to go idle"); + return ; + } + + if let Err(_) = self.sd_cmd(CMD_SEND_IF_COND, 0x0000_01aa) { + println!("failed to send if cond"); + return ; + } + + cnt = 6; + reg_val = 0; + + while ((reg_val & ACMD41_CMD_COMPLETE) == 0) && cnt > 0 { + wait_msec(10); + cnt -= 1; + + if let Ok(val) = self.sd_cmd(CMD_SEND_OP_COND, ACMD41_ARG_HC) { + reg_val = val; + self.ocr = reg_val; + print!("EMMC: CMD_SEND_OP_COND returned 0x{:08x} = ", reg_val); + + if (reg_val & ACMD41_CMD_COMPLETE) != 0 { + print!("COMPLETE "); + } + if (reg_val & ACMD41_VOLTAGE) != 0 { + print!("VOLTAGE "); + } + if (reg_val & ACMD41_CMD_CCS) != 0 { + print!("CCS "); + } + print!("\n"); + } else { + println!("ERROR: EMMC ACMD41 returned error"); + return ; + } + } + + if (reg_val & ACMD41_CMD_COMPLETE) == 0 || cnt <= 0 { + println!("ACMD41 TIMEOUT"); + return ; + } + + if (reg_val & ACMD41_VOLTAGE) == 0 { + println!("ACMD41 VOLTAGE NOT FOUND!"); + return ; + } + + let ccs = if (reg_val & ACMD41_CMD_CCS) != 0 { SCR_SUPP_CCS } else { 0 }; + + if let Err(_) = self.sd_cmd(CMD_ALL_SEND_CID, 0) { + println!("CMD_ALL_SEND_CID ERROR, IGNORE!"); + } + + let sd_rca = self.sd_cmd(CMD_SEND_REL_ADDR, 0x0).unwrap(); + println!("CMD_SEND_REL_ADDR = 0x{:08x}", sd_rca); + self.rca = sd_rca; + + if let Err(_) = self.sd_cmd(CMD_SEND_CSD, sd_rca) { + println!("failed to get csd"); + return ; + } + + let (csize, cmult) = if (self.ocr & ACMD41_CMD_CCS) != 0 { + let csize = (self.csd[1] & 0x3f) << 16 | (self.csd[2] & 0xffff_0000) >> 16; + let cmult = 8; + (csize as u64, cmult as u64) + } else { + let csize = (self.csd[1] & 0x3ff) << 2 | (self.csd[2] & 0xc000_0000) >> 30; + let cmult = (self.csd[2] & 0x0003_8000) >> 15; + (csize as u64, cmult as u64) + }; + self.size = ((csize + 1) << (cmult + 2)) * 512; + println!("mmc size = 0x{:08x}", self.size); + + + if let Err(_) = self.set_clock(2500_0000) { + println!("failed to set clock 2500_0000 Hz"); + return ; + } + + if let Err(_) = self.sd_cmd(CMD_CARD_SELECT, sd_rca) { + println!("failed to CMD_CARD_SELECT 0x{:08x}", sd_rca); + return ; + } + + if let Err(_) = self.sd_status(SR_DAT_INHIBIT) { + println!("SR_DAT_INHIBIT return"); + return ; + } + + let regs = self.regs.get_mut().unwrap(); + regs.blksizecnt.write(1 << 16 | 8); + + if let Err(_) = self.sd_cmd(CMD_SEND_SCR, 0) { + println!("failed to CMD_SEND_SCR"); + return ; + } + + if let Err(_) = self.sd_int(INT_READ_RDY) { + println!("failed to INT_READ_RDY"); + return ; + } + + cnt = 10000; + let mut i = 0; + let regs = self.regs.get_mut().unwrap(); + while i < 2 && cnt > 0 { + reg_val = regs.status.read(); + cnt -= 1; + if (reg_val & SR_READ_AVAILABLE) != 0 { + self.scr[i] = regs.data.read(); + i += 1; + } else { + wait_msec(10); + cnt -= 1; + } + } + if i != 2 { + println!("SD TIMEOUT FOR SCR[; 2]"); + return ; + } + + if (self.scr[0] & SCR_SD_BUS_WIDTH_4) != 0 { + if let Err(_) = self.sd_cmd(CMD_SET_BUS_WIDTH, sd_rca | 2) { + println!("failed to set bus width, {}", sd_rca | 2); + return ; + } + let regs = self.regs.get_mut().unwrap(); + regs.control0.write(C0_HCTL_DWITDH); + } + + print!("EMMC: supports "); + + if (self.scr[0] & SCR_SUPP_SET_BLKCNT) != 0 { + print!("SET_BLKCNT "); + } + + if ccs != 0 { + print!("CCS "); + } + + print!("\n"); + + self.scr[0] &= !SCR_SUPP_CCS; + self.scr[0] |= ccs; + } + + pub unsafe fn set_clock(&mut self, freq: u32) -> Result<()> { + let mut cnt: i32 = 0; + let regs = self.regs.get_mut().unwrap(); + + let mut reg_val = regs.status.read() & (SR_CMD_INHIBIT | SR_DAT_INHIBIT); + cnt = 10_0000; + while (cnt > 0) && reg_val != 0 { + wait_msec(1); + cnt -= 1; + reg_val = regs.status.read() & (SR_CMD_INHIBIT | SR_DAT_INHIBIT); + } + + if cnt <= 0 { + println!("ERROR: TIMEOUT WAITING FOR INHIBIT FLAG"); + return Err(Error::new(EINVAL)); + } + + reg_val = regs.control1.read(); + reg_val &= !C1_CLK_EN; + regs.control1.write(reg_val); + wait_msec(10); + + let c = 4166_6666 / freq; + let mut x: u32 = c - 1; + let mut s: u32 = 32; + + if x == 0 { + s = 0; + } else { + if (x & 0xffff_0000) == 0 { x <<= 16; s -= 16; } + if (x & 0xff00_0000) == 0 { x <<= 8; s -= 8; } + if (x & 0xf000_0000) == 0 { x <<= 4; s -= 4; } + if (x & 0xc000_0000) == 0 { x <<= 2; s -= 2; } + if (x & 0x8000_0000) == 0 { x <<= 1; s -= 1; } + if s > 0 { s -= 1; } + if s > 7 { s = 7; } + } + let mut d = 0; + if self.host_spec_ver > HOST_SPEC_V2 { + d = c; + } else { + d = 1 << s; + } + + if d <= 2 { + d = 2; + s = 0; + } + println!("sd clk divisor: 0x{:08x}, shift: 0x{:08x}", d, s); + + let mut h = 0; + if self.host_spec_ver > HOST_SPEC_V2 { + h = (d & 0x300) >> 2; + } + + d = ( ((d & 0xff) << 8)| h); + reg_val = regs.control1.read() & 0xffff_003f; + regs.control1.write(reg_val | d); + wait_msec(10); + reg_val = regs.control1.read(); + regs.control1.write(reg_val | C1_CLK_EN); + wait_msec(10); + + reg_val = regs.control1.read() & C1_CLK_STABLE; + cnt = 10000; + while cnt > 0 && reg_val == 0 { + wait_msec(10); + cnt -= 1; + reg_val = regs.control1.read() & C1_CLK_STABLE; + } + + if cnt <= 0 { + println!("ERROR: failed to get stable clock"); + return Err(Error::new(EINVAL)); + } + + Ok(()) + } + + pub unsafe fn sd_cmd(&mut self, mut code: u32, arg: u32) -> Result { + if (code & CMD_NEED_APP) != 0 { + let pre_cmd = CMD_APP_CMD | if self.rca != 0 { CMD_RSPNS_48 } else { 0 }; + match self.sd_cmd(pre_cmd, self.rca) { + Err(_) => { + println!("ERROR: failed to send SD APP command"); + return Err(Error::new(EINVAL)); + } + Ok(_) => { + code &= !CMD_NEED_APP; + } + } + } + + if let Err(_) = self.sd_status(SR_CMD_INHIBIT) { + println!("ERROR: Emmc busy"); + return Err(Error::new(EINVAL)); + } + + //println!("EMMC: Sending command 0x{:08x}, arg 0x{:08x}", code, arg); + + let regs = self.regs.get_mut().unwrap(); + let mut reg_val = regs.interrupt.read(); + regs.interrupt.write(reg_val); + regs.arg1.write(arg); + regs.cmdtm.write(code); + + if code == CMD_SEND_OP_COND { + wait_msec(1000); + } else if code == CMD_SEND_IF_COND || code == CMD_APP_CMD { + wait_msec(200); + } + + if let Err(_) = self.sd_int(INT_CMD_DONE) { + println!("ERROR: failed to send EMMC command"); + return Err(Error::new(EINVAL)); + } + + let regs = self.regs.get_mut().unwrap(); + reg_val = regs.resp0.read(); + + if code == CMD_GO_IDLE || code == CMD_APP_CMD { + return Ok(0); + } else if code == (CMD_APP_CMD | CMD_RSPNS_48) { + return Ok(reg_val & SR_APP_CMD); + } else if code == CMD_SEND_OP_COND { + return Ok(reg_val); + } else if code == CMD_SEND_IF_COND { + if reg_val == arg { + return Ok(0); + } else { + return Err(Error::new(EINVAL)); + } + } else if code == CMD_ALL_SEND_CID { + self.cid[0] = reg_val; + self.cid[1] = regs.resp1.read(); + self.cid[2] = regs.resp2.read(); + self.cid[3] = regs.resp3.read(); + + //FIXME: wrong implement, see CMD_SEND_CSD for detail + return Ok(reg_val); + } else if code == CMD_SEND_CSD { + let tmp0 = reg_val; + let tmp1 = regs.resp1.read(); + let tmp2 = regs.resp2.read(); + let tmp3 = regs.resp3.read(); + + self.csd[0] = tmp3 << 8 | tmp2 >> 24; + self.csd[1] = tmp2 << 8 | tmp1 >> 24; + self.csd[2] = tmp1 << 8 | tmp0 >> 24; + self.csd[3] = tmp0 << 8; + + //FIXME: support variable length of result. + return Ok(reg_val); + } else if code == CMD_SEND_REL_ADDR { + let mut err = reg_val & 0x1fff; + err |= ((reg_val & 0x2000) << 6); + err |= ((reg_val & 0x4000) << 8); + err |= ((reg_val & 0x8000) << 8); + err &= CMD_ERRORS_MASK; + + if err != 0 { + return Err(Error::new(EINVAL)); + } else { + return Ok(reg_val & CMD_RCA_MASK); + } + } else { + return Ok(reg_val & CMD_ERRORS_MASK); + } + + } + + pub unsafe fn sd_status(&mut self, mask: u32) -> Result<()> { + let regs = self.regs.get_mut().unwrap(); + let mut cnt = 500000; + + let mut reg_val = regs.status.read() & mask; + let mut reg_val1 = regs.interrupt.read() & INT_ERROR_MASK; + + while cnt > 0 && reg_val != 0 && reg_val1 == 0 { + wait_msec(1); + cnt -= 1; + reg_val = regs.status.read() & mask; + reg_val1 = regs.interrupt.read() & INT_ERROR_MASK; + } + reg_val1 = regs.interrupt.read() & INT_ERROR_MASK; + + if cnt <= 0 || reg_val1 != 0 { + return Err(Error::new(EINVAL)); + } else { + return Ok(()); + } + } + pub unsafe fn sd_int(&mut self, mask: u32) -> Result<()> { + let regs = self.regs.get_mut().unwrap(); + let mut cnt = 100_0000; + let m = mask | INT_ERROR_MASK; + + let mut reg_val = regs.interrupt.read() & m; + + while cnt > 0 && reg_val == 0 { + wait_msec(1); + cnt -= 1; + reg_val = regs.interrupt.read() & m; + } + reg_val = regs.interrupt.read(); + let err = reg_val & (INT_CMD_TIMEOUT | INT_DATA_TIMEOUT | INT_ERROR_MASK); + + if cnt <= 0 || err != 0 { + regs.interrupt.write(reg_val); + return Err(Error::new(EINVAL)); + } else { + regs.interrupt.write(mask); + return Ok(()); + } + } + + pub unsafe fn sd_readblock(&mut self, lba: u32, buf: &mut [u32], num: u32) -> Result { + let num = if num < 1 { 1 } else { num }; + let mut reg_val: usize = 0; + + //println!("sd_readblock lba 0x{:x}, num 0x{:x}", lba, num); + + if let Err(_) = self.sd_status(SR_DAT_INHIBIT) { + println!("SR_DAT_INHIBIT TIMEOUT"); + return Err(Error::new(EINVAL)); + } + + if (self.scr[0] & SCR_SUPP_CCS) != 0 { + if num > 1 && ((self.scr[0] & SCR_SUPP_SET_BLKCNT) != 0) { + if let Err(_) = self.sd_cmd(CMD_SET_BLOCKCNT, num) { + println!("CMD_SET_BLOCKCNT ERROR"); + return Err(Error::new(EINVAL)); + } + } + let regs = self.regs.get_mut().unwrap(); + regs.blksizecnt.write((num) << 16 | 512); + if num == 1 { + self.sd_cmd(CMD_READ_SINGLE, lba).unwrap(); + } else { + self.sd_cmd(CMD_READ_MULTI, lba).unwrap(); + } + } else { + let regs = self.regs.get_mut().unwrap(); + regs.blksizecnt.write(1 << 16 | 512); + } + + let mut cnt = 0; + while cnt < num { + if (self.scr[0] & SCR_SUPP_CCS) == 0 { + self.sd_cmd(CMD_READ_SINGLE, (lba + cnt) * 512).unwrap(); + } + + if let Err(_) = self.sd_int(INT_READ_RDY) { + println!("ERROR: Timeout waiting for ready to read"); + return Err(Error::new(EINVAL)); + } + + let regs = self.regs.get_mut().unwrap(); + regs.blksizecnt.write(1 << 16 | 512); + for d in 0..128 { + buf[(128 * cnt + d) as usize] = regs.data.read(); + } + cnt += 1; + } + + if num > 1 && (self.scr[0] & SCR_SUPP_SET_BLKCNT) == 0 && (self.scr[0] & SCR_SUPP_CCS) != 0 { + self.sd_cmd(CMD_STOP_TRANS, 0).unwrap(); + } + Ok((num * 512) as usize) + } + + pub unsafe fn sd_writeblock(&mut self, lba: u32, buf: &[u32], num: u32) -> Result { + let num = if num < 1 { 1 } else { num }; + let mut reg_val: usize = 0; + + //println!("sd_writelock lba 0x{:x}, num 0x{:x}", lba, num); + + if let Err(_) = self.sd_status(SR_DAT_INHIBIT | SR_WRITE_AVAILABLE) { + println!("SR_DAT_INHIBIT TIMEOUT"); + return Err(Error::new(EINVAL)); + } + + if (self.scr[0] & SCR_SUPP_CCS) != 0 { + if num > 1 && ((self.scr[0] & SCR_SUPP_SET_BLKCNT) != 0) { + if let Err(_) = self.sd_cmd(CMD_SET_BLOCKCNT, num) { + println!("CMD_SET_BLOCKCNT ERROR"); + return Err(Error::new(EINVAL)); + } + } + let regs = self.regs.get_mut().unwrap(); + regs.blksizecnt.write((num) << 16 | 512); + if num == 1 { + self.sd_cmd(CMD_WRITE_SINGLE, lba).unwrap(); + } else { + self.sd_cmd(CMD_WRITE_MULTI, lba).unwrap(); + } + } else { + let regs = self.regs.get_mut().unwrap(); + regs.blksizecnt.write(1 << 16 | 512); + } + + let mut cnt = 0; + while cnt < num { + if (self.scr[0] & SCR_SUPP_CCS) == 0 { + self.sd_cmd(CMD_WRITE_SINGLE, (lba + cnt) * 512).unwrap(); + } + + if let Err(_) = self.sd_int(INT_WRITE_RDY) { + println!("ERROR: Timeout waiting for ready to write"); + return Err(Error::new(EINVAL)); + } + + let regs = self.regs.get_mut().unwrap(); + regs.blksizecnt.write(1 << 16 | 512); + for d in 0..128 { + regs.data.write(buf[(128 * cnt + d) as usize]); + } + cnt += 1; + } + + if let Err(_) = self.sd_int(INT_DATA_DONE) { + println!("ERROR: Timeout waiting for data done"); + return Err(Error::new(EINVAL)); + } + + if num > 1 && (self.scr[0] & SCR_SUPP_SET_BLKCNT) == 0 && (self.scr[0] & SCR_SUPP_CCS) != 0 { + self.sd_cmd(CMD_STOP_TRANS, 0).unwrap(); + } + Ok((num * 512) as usize) + } +} + +pub trait Disk { + fn id(&self) -> usize; + fn size(&mut self) -> u64; + fn read(&mut self, block:u64, buffer: &mut [u8]) -> syscall::error::Result>; + fn write(&mut self, block:u64, buffer: &[u8]) -> syscall::error::Result>; + fn block_length(&mut self) -> syscall::error::Result; +} + +impl Disk for SdHostCtrl { + fn id(&self) -> usize { + 0xdead_dead + } + + fn size(&mut self) -> u64 { + //assert 512MiB + self.size + } + + fn read(&mut self, block:u64, buffer: &mut [u8]) -> Result> { + if (buffer.len() % 512) != 0 { + println!("buffer.len {} should be aligned to {}", + buffer.len(), 512); + return Err(Error::new(EINVAL)); + } + let u32_len = buffer.len() / core::mem::size_of::(); + let num = buffer.len() / 512; + let u8_ptr = buffer.as_mut_ptr(); + let ret = unsafe { + let u32_buffer = core::slice::from_raw_parts_mut(u8_ptr as *mut u32, u32_len); + self.sd_readblock(block as u32, u32_buffer, num as u32) + }; + match ret { + Ok(cnt) => Ok(Some(cnt)), + Err(err) => Err(err) + } + } + + fn write(&mut self, block:u64, buffer: &[u8]) -> Result> { + if (buffer.len() % 512) != 0 { + println!("buffer.len {} should be aligned to {}", + buffer.len(), 512); + return Err(Error::new(EINVAL)); + } + let u32_len = buffer.len() / core::mem::size_of::(); + let num = buffer.len() / 512; + let u8_ptr = buffer.as_ptr(); + let ret = unsafe { + let u32_buffer = core::slice::from_raw_parts(u8_ptr as *const u32, u32_len); + self.sd_writeblock(block as u32, u32_buffer, num as u32) + }; + match ret { + Ok(cnt) => Ok(Some(cnt)), + Err(err) => Err(err) + } + } + + fn block_length(&mut self) -> Result { + Ok(512) + } + +} diff --git a/common/src/lib.rs b/common/src/lib.rs index 35827a2197..b35bdefe68 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -11,6 +11,7 @@ pub enum MemoryType { Writeback, Uncacheable, WriteCombining, + DeviceMemory, } impl Default for MemoryType { fn default() -> Self { @@ -35,6 +36,7 @@ pub unsafe fn physmap(base_phys: usize, len: usize, Prot { read, write }: Prot, MemoryType::Writeback => "wb", MemoryType::Uncacheable => "uc", MemoryType::WriteCombining => "wc", + MemoryType::DeviceMemory => "dev", }); let mode = match (read, write) { (true, true) => O_RDWR,