You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
364 lines
9.8 KiB
364 lines
9.8 KiB
#![feature(asm)]
|
|
#![feature(linkage)]
|
|
#![deny(warnings)]
|
|
|
|
extern crate alloc;
|
|
#[macro_use]
|
|
extern crate log;
|
|
|
|
use {
|
|
alloc::boxed::Box,
|
|
alloc::collections::VecDeque,
|
|
async_std::task_local,
|
|
core::time::Duration,
|
|
core::{cell::Cell, future::Future, pin::Pin},
|
|
git_version::git_version,
|
|
lazy_static::*,
|
|
std::fmt::{Debug, Formatter},
|
|
std::fs::{File, OpenOptions},
|
|
std::io::Error,
|
|
std::os::unix::io::AsRawFd,
|
|
std::sync::Mutex,
|
|
std::time::SystemTime,
|
|
tempfile::tempdir,
|
|
};
|
|
|
|
use kernel_hal::vdso::*;
|
|
pub use kernel_hal::{defs::*, *};
|
|
pub use trapframe::syscall_fn_entry as syscall_entry;
|
|
|
|
#[repr(C)]
|
|
pub struct Thread {
|
|
thread: usize,
|
|
}
|
|
|
|
impl Thread {
|
|
#[export_name = "hal_thread_spawn"]
|
|
pub fn spawn(
|
|
future: Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
|
|
_vmtoken: usize,
|
|
) -> Self {
|
|
async_std::task::spawn(future);
|
|
Thread { thread: 0 }
|
|
}
|
|
|
|
#[export_name = "hal_thread_set_tid"]
|
|
pub fn set_tid(tid: u64, pid: u64) {
|
|
TID.with(|x| x.set(tid));
|
|
PID.with(|x| x.set(pid));
|
|
}
|
|
|
|
#[export_name = "hal_thread_get_tid"]
|
|
pub fn get_tid() -> (u64, u64) {
|
|
(TID.with(|x| x.get()), PID.with(|x| x.get()))
|
|
}
|
|
}
|
|
|
|
task_local! {
|
|
static TID: Cell<u64> = Cell::new(0);
|
|
static PID: Cell<u64> = Cell::new(0);
|
|
}
|
|
|
|
/// Get current time.
|
|
#[export_name = "hal_timer_now"]
|
|
pub fn timer_now() -> Duration {
|
|
SystemTime::now()
|
|
.duration_since(SystemTime::UNIX_EPOCH)
|
|
.unwrap()
|
|
}
|
|
|
|
/// Initialize the HAL.
|
|
///
|
|
/// This function must be called at the beginning.
|
|
pub fn init() {
|
|
#[cfg(target_os = "macos")]
|
|
unimplemented!()
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct PhysFrame {
|
|
paddr: PhysAddr,
|
|
}
|
|
|
|
impl Debug for PhysFrame {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::result::Result<(), std::fmt::Error> {
|
|
write!(f, "PhysFrame({:#x})", self.paddr)
|
|
}
|
|
}
|
|
|
|
const PMEM_SIZE: usize = 0x4000_0000; // 1GiB
|
|
const PAGE_SIZE: usize = 0x1000;
|
|
fn page_aligned(x: VirtAddr) -> bool {
|
|
x % PAGE_SIZE == 0
|
|
}
|
|
|
|
lazy_static! {
|
|
static ref FRAME_FILE: File = create_pmem_file();
|
|
}
|
|
|
|
fn create_pmem_file() -> File {
|
|
let dir = tempdir().expect("failed to create pmem dir");
|
|
let path = dir.path().join("pmem");
|
|
|
|
// workaround on macOS to avoid permission denied.
|
|
// see https://jiege.ch/software/2020/02/07/macos-mmap-exec/ for analysis on this problem.
|
|
#[cfg(target_os = "macos")]
|
|
std::mem::forget(dir);
|
|
|
|
let file = OpenOptions::new()
|
|
.read(true)
|
|
.write(true)
|
|
.create(true)
|
|
.open(&path)
|
|
.expect("failed to create pmem file");
|
|
file.set_len(PMEM_SIZE as u64)
|
|
.expect("failed to resize file");
|
|
trace!("create pmem file: path={:?}, size={:#x}", path, PMEM_SIZE);
|
|
let prot = libc::PROT_READ | libc::PROT_WRITE;
|
|
mmap(file.as_raw_fd(), 0, PMEM_SIZE, phys_to_virt(0), prot);
|
|
file
|
|
}
|
|
|
|
/// Mmap frame file `fd` to `vaddr`.
|
|
fn mmap(fd: libc::c_int, offset: usize, len: usize, vaddr: VirtAddr, prot: libc::c_int) {
|
|
// workaround on macOS to write text section.
|
|
#[cfg(target_os = "macos")]
|
|
let prot = if prot & libc::PROT_EXEC != 0 {
|
|
prot | libc::PROT_WRITE
|
|
} else {
|
|
prot
|
|
};
|
|
|
|
let ret = unsafe {
|
|
let flags = libc::MAP_SHARED | libc::MAP_FIXED;
|
|
libc::mmap(vaddr as _, len, prot, flags, fd, offset as _)
|
|
} as usize;
|
|
trace!(
|
|
"mmap file: fd={}, offset={:#x}, len={:#x}, vaddr={:#x}, prot={:#b}",
|
|
fd,
|
|
offset,
|
|
len,
|
|
vaddr,
|
|
prot,
|
|
);
|
|
assert_eq!(ret, vaddr, "failed to mmap: {:?}", Error::last_os_error());
|
|
}
|
|
|
|
lazy_static! {
|
|
static ref AVAILABLE_FRAMES: Mutex<VecDeque<usize>> =
|
|
Mutex::new((PAGE_SIZE..PMEM_SIZE).step_by(PAGE_SIZE).collect());
|
|
}
|
|
|
|
impl PhysFrame {
|
|
#[export_name = "hal_frame_alloc"]
|
|
pub fn alloc() -> Option<Self> {
|
|
let ret = AVAILABLE_FRAMES
|
|
.lock()
|
|
.unwrap()
|
|
.pop_front()
|
|
.map(|paddr| PhysFrame { paddr });
|
|
trace!("frame alloc: {:?}", ret);
|
|
ret
|
|
}
|
|
#[export_name = "hal_zero_frame_paddr"]
|
|
pub fn zero_frame_addr() -> PhysAddr {
|
|
0
|
|
}
|
|
}
|
|
|
|
impl Drop for PhysFrame {
|
|
#[export_name = "hal_frame_dealloc"]
|
|
fn drop(&mut self) {
|
|
trace!("frame dealloc: {:?}", self);
|
|
AVAILABLE_FRAMES.lock().unwrap().push_back(self.paddr);
|
|
}
|
|
}
|
|
|
|
fn phys_to_virt(paddr: PhysAddr) -> VirtAddr {
|
|
/// Map physical memory from here.
|
|
const PMEM_BASE: VirtAddr = 0x8_0000_0000;
|
|
|
|
PMEM_BASE + paddr
|
|
}
|
|
|
|
/// Ensure physical memory are mmapped and accessible.
|
|
fn ensure_mmap_pmem() {
|
|
FRAME_FILE.as_raw_fd();
|
|
}
|
|
|
|
/// Read physical memory from `paddr` to `buf`.
|
|
#[export_name = "hal_pmem_read"]
|
|
pub fn pmem_read(paddr: PhysAddr, buf: &mut [u8]) {
|
|
trace!("pmem read: paddr={:#x}, len={:#x}", paddr, buf.len());
|
|
assert!(paddr + buf.len() <= PMEM_SIZE);
|
|
ensure_mmap_pmem();
|
|
unsafe {
|
|
(phys_to_virt(paddr) as *const u8).copy_to_nonoverlapping(buf.as_mut_ptr(), buf.len());
|
|
}
|
|
}
|
|
|
|
/// Write physical memory to `paddr` from `buf`.
|
|
#[export_name = "hal_pmem_write"]
|
|
pub fn pmem_write(paddr: PhysAddr, buf: &[u8]) {
|
|
trace!("pmem write: paddr={:#x}, len={:#x}", paddr, buf.len());
|
|
assert!(paddr + buf.len() <= PMEM_SIZE);
|
|
ensure_mmap_pmem();
|
|
unsafe {
|
|
buf.as_ptr()
|
|
.copy_to_nonoverlapping(phys_to_virt(paddr) as _, buf.len());
|
|
}
|
|
}
|
|
|
|
/// Zero physical memory at `[paddr, paddr + len)`
|
|
#[export_name = "hal_pmem_zero"]
|
|
pub fn pmem_zero(paddr: PhysAddr, len: usize) {
|
|
trace!("pmem_zero: addr={:#x}, len={:#x}", paddr, len);
|
|
assert!(paddr + len <= PMEM_SIZE);
|
|
ensure_mmap_pmem();
|
|
unsafe {
|
|
core::ptr::write_bytes(phys_to_virt(paddr) as *mut u8, 0, len);
|
|
}
|
|
}
|
|
|
|
/// Copy content of `src` frame to `target` frame
|
|
#[export_name = "hal_frame_copy"]
|
|
pub fn frame_copy(src: PhysAddr, target: PhysAddr) {
|
|
trace!("frame_copy: {:#x} <- {:#x}", target, src);
|
|
assert!(src + PAGE_SIZE <= PMEM_SIZE && target + PAGE_SIZE <= PMEM_SIZE);
|
|
ensure_mmap_pmem();
|
|
unsafe {
|
|
let buf = phys_to_virt(src) as *const u8;
|
|
buf.copy_to_nonoverlapping(phys_to_virt(target) as _, PAGE_SIZE);
|
|
}
|
|
}
|
|
|
|
/// Flush the physical frame.
|
|
#[export_name = "hal_frame_flush"]
|
|
pub fn frame_flush(_target: PhysAddr) {
|
|
// do nothing
|
|
}
|
|
|
|
/// Page Table
|
|
#[repr(C)]
|
|
pub struct PageTable {
|
|
table_phys: PhysAddr,
|
|
}
|
|
|
|
impl PageTable {
|
|
/// Create a new `PageTable`.
|
|
#[allow(clippy::new_without_default)]
|
|
#[export_name = "hal_pt_new"]
|
|
pub fn new() -> Self {
|
|
PageTable { table_phys: 0 }
|
|
}
|
|
}
|
|
|
|
impl PageTableTrait for PageTable {
|
|
/// Map the page of `vaddr` to the frame of `paddr` with `flags`.
|
|
#[export_name = "hal_pt_map"]
|
|
fn map(&mut self, vaddr: VirtAddr, paddr: PhysAddr, flags: MMUFlags) -> Result<()> {
|
|
debug_assert!(page_aligned(vaddr));
|
|
debug_assert!(page_aligned(paddr));
|
|
let prot = flags.to_mmap_prot();
|
|
mmap(FRAME_FILE.as_raw_fd(), paddr, PAGE_SIZE, vaddr, prot);
|
|
Ok(())
|
|
}
|
|
|
|
/// Unmap the page of `vaddr`.
|
|
#[export_name = "hal_pt_unmap"]
|
|
fn unmap(&mut self, vaddr: VirtAddr) -> Result<()> {
|
|
self.unmap_cont(vaddr, 1)
|
|
}
|
|
|
|
/// Change the `flags` of the page of `vaddr`.
|
|
#[export_name = "hal_pt_protect"]
|
|
fn protect(&mut self, vaddr: VirtAddr, flags: MMUFlags) -> Result<()> {
|
|
debug_assert!(page_aligned(vaddr));
|
|
let prot = flags.to_mmap_prot();
|
|
let ret = unsafe { libc::mprotect(vaddr as _, PAGE_SIZE, prot) };
|
|
assert_eq!(ret, 0, "failed to mprotect: {:?}", Error::last_os_error());
|
|
Ok(())
|
|
}
|
|
|
|
/// Query the physical address which the page of `vaddr` maps to.
|
|
#[export_name = "hal_pt_query"]
|
|
fn query(&mut self, vaddr: VirtAddr) -> Result<PhysAddr> {
|
|
debug_assert!(page_aligned(vaddr));
|
|
unimplemented!()
|
|
}
|
|
|
|
/// Get the physical address of root page table.
|
|
#[export_name = "hal_pt_table_phys"]
|
|
fn table_phys(&self) -> PhysAddr {
|
|
self.table_phys
|
|
}
|
|
|
|
#[export_name = "hal_pt_unmap_cont"]
|
|
fn unmap_cont(&mut self, vaddr: VirtAddr, pages: usize) -> Result<()> {
|
|
if pages == 0 {
|
|
return Ok(());
|
|
}
|
|
debug_assert!(page_aligned(vaddr));
|
|
let ret = unsafe { libc::munmap(vaddr as _, PAGE_SIZE * pages) };
|
|
assert_eq!(ret, 0, "failed to munmap: {:?}", Error::last_os_error());
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
trait FlagsExt {
|
|
fn to_mmap_prot(&self) -> libc::c_int;
|
|
}
|
|
|
|
impl FlagsExt for MMUFlags {
|
|
fn to_mmap_prot(&self) -> libc::c_int {
|
|
let mut flags = 0;
|
|
if self.contains(MMUFlags::READ) {
|
|
flags |= libc::PROT_READ;
|
|
}
|
|
if self.contains(MMUFlags::WRITE) {
|
|
flags |= libc::PROT_WRITE;
|
|
}
|
|
if self.contains(MMUFlags::EXECUTE) {
|
|
flags |= libc::PROT_EXEC;
|
|
}
|
|
flags
|
|
}
|
|
}
|
|
|
|
#[export_name = "hal_context_run"]
|
|
unsafe fn context_run(context: &mut UserContext) {
|
|
context.run_fncall();
|
|
}
|
|
|
|
#[export_name = "hal_vdso_constants"]
|
|
pub fn vdso_constants() -> VdsoConstants {
|
|
let tsc_frequency = 3000u16;
|
|
let mut constants = VdsoConstants {
|
|
max_num_cpus: 1,
|
|
features: Features {
|
|
cpu: 0,
|
|
hw_breakpoint_count: 0,
|
|
hw_watchpoint_count: 0,
|
|
},
|
|
dcache_line_size: 0,
|
|
icache_line_size: 0,
|
|
ticks_per_second: tsc_frequency as u64 * 1_000_000,
|
|
ticks_to_mono_numerator: 1000,
|
|
ticks_to_mono_denominator: tsc_frequency as u32,
|
|
physmem: PMEM_SIZE as u64,
|
|
version_string_len: 0,
|
|
version_string: Default::default(),
|
|
};
|
|
constants.set_version_string(git_version!(
|
|
prefix = "git-",
|
|
args = ["--always", "--abbrev=40", "--dirty=-dirty"]
|
|
));
|
|
constants
|
|
}
|
|
|
|
/// Output a char to console.
|
|
#[export_name = "hal_serial_write"]
|
|
pub fn serial_write(s: &str) {
|
|
eprint!("{}", s);
|
|
}
|