mirror of
https://github.com/rcore-os/rCore.git
synced 2024-11-25 17:33:28 +04:00
Merge branch 'master' into user-img
This commit is contained in:
commit
3f2beab52d
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "riscv-pk"]
|
||||
path = riscv-pk
|
||||
url = https://github.com/riscv-and-rust-and-decaf/riscv-pk.git
|
35
.travis.yml
35
.travis.yml
@ -1,34 +1,50 @@
|
||||
sudo: required
|
||||
sudo: false
|
||||
|
||||
language: rust
|
||||
|
||||
rust: nightly
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
cache:
|
||||
cargo: true
|
||||
directories:
|
||||
- $HOME/Library/Caches/Homebrew
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- qemu
|
||||
homebrew:
|
||||
packages:
|
||||
- qemu
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- ARCH="riscv32"
|
||||
- ARCH="riscv32" OPTS="m_mode=1"
|
||||
- ARCH="x86_64"
|
||||
- ARCH="aarch64"
|
||||
|
||||
install:
|
||||
- if [ $ARCH = riscv32 ]; then
|
||||
export FILE="riscv64-unknown-elf-gcc-20181030-x86_64-linux-ubuntu14";
|
||||
wget https://static.dev.sifive.com/dev-tools/$FILE.tar.gz;
|
||||
tar xf $FILE.tar.gz;
|
||||
export PATH=$PATH:$PWD/$FILE/bin;
|
||||
[ $TRAVIS_OS_NAME = linux ] && export FILE="riscv64-unknown-elf-gcc-20181127-x86_64-linux-ubuntu14";
|
||||
[ $TRAVIS_OS_NAME = osx ] && export FILE="riscv64-unknown-elf-gcc-20181127-x86_64-apple-darwin";
|
||||
wget https://static.dev.sifive.com/dev-tools/$FILE.tar.gz;
|
||||
tar xf $FILE.tar.gz;
|
||||
export PATH=$PATH:$PWD/$FILE/bin;
|
||||
fi
|
||||
- if [ $ARCH = aarch64 ]; then
|
||||
wget https://web.stanford.edu/class/cs140e/files/aarch64-none-elf-linux-x64.tar.gz;
|
||||
tar -xzvf aarch64-none-elf-linux-x64.tar.gz;
|
||||
export PATH=$PATH:$PWD/aarch64-none-elf/bin;
|
||||
if [ $TRAVIS_OS_NAME = linux ]; then
|
||||
wget https://web.stanford.edu/class/cs140e/files/aarch64-none-elf-linux-x64.tar.gz;
|
||||
tar -xzvf aarch64-none-elf-linux-x64.tar.gz;
|
||||
export PATH=$PATH:$PWD/aarch64-none-elf/bin;
|
||||
elif [ $TRAVIS_OS_NAME = osx ]; then
|
||||
brew tap SergioBenitez/osxct;
|
||||
brew install aarch64-none-elf;
|
||||
fi;
|
||||
fi
|
||||
|
||||
|
||||
@ -38,4 +54,5 @@ before_script:
|
||||
- (test -x $HOME/.cargo/bin/bootimage || cargo install bootimage)
|
||||
|
||||
script:
|
||||
- cd kernel && make build arch=$ARCH
|
||||
- cd kernel && make build arch=$ARCH $OPTS && cd ..
|
||||
- cd user && make arch=$ARCH
|
||||
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
@ -6,7 +6,9 @@ Rust version of THU [uCore OS](https://github.com/chyyuu/ucore_os_lab/).
|
||||
|
||||
Going to be the next generation teaching operating system.
|
||||
|
||||
Support arch: x86_64, RISCV32I, AArch64 (WIP).
|
||||
Supported architectures: x86_64, RISCV32IMA(S/M), AArch64
|
||||
|
||||
Tested boards: QEMU, Raspberry Pi 3B+
|
||||
|
||||
[Dev docs](https://rucore.gitbook.io/rust-os-docs/) (in Chinese)
|
||||
|
||||
@ -46,7 +48,7 @@ cargo install cargo-xbuild bootimage
|
||||
```
|
||||
|
||||
```bash
|
||||
git clone https://github.com/wangrunji0408/RustOS.git
|
||||
git clone https://github.com/wangrunji0408/RustOS.git --recursive
|
||||
cd RustOS/kernel
|
||||
rustup override set nightly
|
||||
make run arch=riscv32|x86_64|aarch64
|
||||
|
@ -12,5 +12,6 @@ pub mod cow;
|
||||
pub mod swap;
|
||||
pub mod memory_set;
|
||||
mod addr;
|
||||
pub mod no_mmu;
|
||||
|
||||
pub use crate::addr::*;
|
@ -166,22 +166,17 @@ impl MemoryArea {
|
||||
}
|
||||
}
|
||||
None => {
|
||||
info!("map delayed!");
|
||||
for page in Page::range_of(self.start_addr, self.end_addr) {
|
||||
let addr = page.start_address();
|
||||
//let target = T::alloc_frame().expect("failed to allocate frame");
|
||||
//self.flags.apply(pt.map(addr, target));
|
||||
let target = T::alloc_frame().expect("failed to allocate frame");
|
||||
self.flags.apply(pt.map(addr, target));
|
||||
// for frame delayed allocation
|
||||
{
|
||||
let entry = pt.map(addr,0);
|
||||
self.flags.apply(entry);
|
||||
}
|
||||
let entry = pt.get_entry(addr).expect("fail to get entry");
|
||||
entry.set_present(false);
|
||||
entry.update();
|
||||
|
||||
// let entry = pt.map(addr,0);
|
||||
// self.flags.apply(entry);
|
||||
// let entry = pt.get_entry(addr).expect("fail to get entry");
|
||||
// entry.set_present(false);
|
||||
// entry.update();
|
||||
}
|
||||
info!("finish map delayed!");
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -227,6 +222,7 @@ pub struct MemoryAttr {
|
||||
user: bool,
|
||||
readonly: bool,
|
||||
execute: bool,
|
||||
mmio: bool,
|
||||
hide: bool,
|
||||
}
|
||||
|
||||
@ -255,6 +251,10 @@ impl MemoryAttr {
|
||||
self.execute = true;
|
||||
self
|
||||
}
|
||||
pub fn mmio(mut self) -> Self {
|
||||
self.mmio = true;
|
||||
self
|
||||
}
|
||||
/*
|
||||
** @brief set the memory attribute's hide bit
|
||||
** @retval MemoryAttr the memory attribute itself
|
||||
@ -273,8 +273,9 @@ impl MemoryAttr {
|
||||
if self.user { entry.set_user(true); }
|
||||
if self.readonly { entry.set_writable(false); }
|
||||
if self.execute { entry.set_execute(true); }
|
||||
if self.mmio { entry.set_mmio(true); }
|
||||
if self.hide { entry.set_present(false); }
|
||||
if self.user || self.readonly || self.execute || self.hide { entry.update(); }
|
||||
if self.user || self.readonly || self.execute || self.mmio || self.hide { entry.update(); }
|
||||
}
|
||||
}
|
||||
|
||||
@ -330,6 +331,9 @@ impl<T: InactivePageTable> MemorySet<T> {
|
||||
pub fn iter(&self) -> impl Iterator<Item=&MemoryArea> {
|
||||
self.areas.iter()
|
||||
}
|
||||
pub fn edit(&mut self, f: impl FnOnce(&mut T::Active)) {
|
||||
self.page_table.edit(f);
|
||||
}
|
||||
/*
|
||||
** @brief execute function with the associated page table
|
||||
** @param f: impl FnOnce() the function to be executed
|
||||
|
66
crate/memory/src/no_mmu.rs
Normal file
66
crate/memory/src/no_mmu.rs
Normal file
@ -0,0 +1,66 @@
|
||||
use alloc::vec::Vec;
|
||||
use alloc::alloc::{Layout, GlobalAlloc};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
pub trait NoMMUSupport {
|
||||
type Alloc: GlobalAlloc;
|
||||
fn allocator() -> &'static Self::Alloc;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MemorySet<S: NoMMUSupport> {
|
||||
areas: Vec<MemoryArea<S>>,
|
||||
support: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<S: NoMMUSupport> MemorySet<S> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
areas: Vec::new(),
|
||||
support: PhantomData,
|
||||
}
|
||||
}
|
||||
/// Allocate `size` bytes space. Return the slice.
|
||||
pub fn push(&mut self, size: usize) -> &'static mut [u8] {
|
||||
let area = MemoryArea::new(size);
|
||||
let slice = unsafe { area.as_buf() };
|
||||
self.areas.push(area);
|
||||
slice
|
||||
}
|
||||
// empty impls
|
||||
pub fn with<T>(&self, f: impl FnOnce() -> T) -> T { f() }
|
||||
pub fn token(&self) -> usize { 0 }
|
||||
pub unsafe fn activate(&self) {}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MemoryArea<S: NoMMUSupport> {
|
||||
ptr: usize,
|
||||
layout: Layout,
|
||||
support: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<S: NoMMUSupport> MemoryArea<S> {
|
||||
fn new(size: usize) -> Self {
|
||||
let layout = Layout::from_size_align(size, 1).unwrap();
|
||||
let ptr = unsafe { S::allocator().alloc(layout) } as usize;
|
||||
MemoryArea { ptr, layout, support: PhantomData }
|
||||
}
|
||||
unsafe fn as_buf(&self) -> &'static mut [u8] {
|
||||
core::slice::from_raw_parts_mut(self.ptr as *mut u8, self.layout.size())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: NoMMUSupport> Clone for MemoryArea<S> {
|
||||
fn clone(&self) -> Self {
|
||||
let new_area = MemoryArea::new(self.layout.size());
|
||||
unsafe { new_area.as_buf().copy_from_slice(self.as_buf()) }
|
||||
new_area
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: NoMMUSupport> Drop for MemoryArea<S> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { S::allocator().dealloc(self.ptr as *mut u8, self.layout) }
|
||||
}
|
||||
}
|
@ -195,4 +195,6 @@ pub trait Entry {
|
||||
** @retval none
|
||||
*/
|
||||
fn set_execute(&mut self, value: bool);
|
||||
fn mmio(&self) -> bool;
|
||||
fn set_mmio(&mut self, value: bool);
|
||||
}
|
||||
|
@ -6,14 +6,6 @@ pub unsafe fn disable_and_store() -> usize {
|
||||
rflags & (1 << 9)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(target_arch = "riscv32")]
|
||||
pub unsafe fn disable_and_store() -> usize {
|
||||
let sstatus: usize;
|
||||
asm!("csrrci $0, 0x100, 1" : "=r"(sstatus));
|
||||
sstatus & 1
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub unsafe fn restore(flags: usize) {
|
||||
@ -22,10 +14,28 @@ pub unsafe fn restore(flags: usize) {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(target_arch = "riscv32")]
|
||||
pub unsafe fn disable_and_store() -> usize {
|
||||
if option_env!("m_mode").is_some() {
|
||||
let mstatus: usize;
|
||||
asm!("csrrci $0, 0x300, 1 << 3" : "=r"(mstatus));
|
||||
mstatus & (1 << 3)
|
||||
} else {
|
||||
let sstatus: usize;
|
||||
asm!("csrrci $0, 0x100, 1 << 1" : "=r"(sstatus));
|
||||
sstatus & (1 << 1)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[cfg(target_arch = "riscv32")]
|
||||
pub unsafe fn restore(flags: usize) {
|
||||
asm!("csrs 0x100, $0" :: "r"(flags));
|
||||
if option_env!("m_mode").is_some() {
|
||||
asm!("csrs 0x300, $0" :: "r"(flags));
|
||||
} else {
|
||||
asm!("csrs 0x100, $0" :: "r"(flags));
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -92,7 +92,10 @@ impl Processor {
|
||||
}
|
||||
|
||||
pub fn tick(&self) {
|
||||
let flags = unsafe { interrupt::disable_and_store() };
|
||||
let need_reschedule = self.manager().tick(self.pid());
|
||||
unsafe { interrupt::restore(flags); }
|
||||
|
||||
if need_reschedule {
|
||||
self.yield_now();
|
||||
}
|
||||
|
35
kernel/Cargo.lock
generated
35
kernel/Cargo.lock
generated
@ -1,3 +1,17 @@
|
||||
[[package]]
|
||||
name = "aarch64"
|
||||
version = "2.2.2"
|
||||
source = "git+https://github.com/equation314/aarch64#47bf5439f5a1379f0fef6272853cf684207a4e45"
|
||||
dependencies = [
|
||||
"bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ux 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "apic"
|
||||
version = "0.1.0"
|
||||
@ -37,7 +51,7 @@ name = "bcm2837"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cortex-a 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"volatile 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"volatile 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -65,7 +79,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
[[package]]
|
||||
name = "bootloader"
|
||||
version = "0.3.4"
|
||||
source = "git+https://github.com/wangrunji0408/bootloader#dd5723c3d7d50856073a5003e0a355ea0fc3d46c"
|
||||
source = "git+https://github.com/wangrunji0408/bootloader#24bdcafc302e7d2659ac126049a45571462907d9"
|
||||
dependencies = [
|
||||
"apic 0.1.0 (git+https://github.com/wangrunji0408/APIC-Rust)",
|
||||
"fixedvec 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -132,7 +146,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.43"
|
||||
version = "0.2.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -185,7 +199,7 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -218,7 +232,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "riscv"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/riscv-and-rust-and-decaf/riscv#f358204af01f2374ab6ed6ea059f724cd5f2fe6f"
|
||||
source = "git+https://github.com/riscv-and-rust-and-decaf/riscv#966eb26d5e8d77677f645d5e32877c678dcee572"
|
||||
dependencies = [
|
||||
"bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -302,6 +316,7 @@ dependencies = [
|
||||
name = "ucore"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aarch64 2.2.2 (git+https://github.com/equation314/aarch64)",
|
||||
"apic 0.1.0 (git+https://github.com/wangrunji0408/APIC-Rust)",
|
||||
"atags 0.1.0",
|
||||
"bbl 0.1.0",
|
||||
@ -311,7 +326,6 @@ dependencies = [
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bootloader 0.3.4 (git+https://github.com/wangrunji0408/bootloader)",
|
||||
"cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cortex-a 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"linked_list_allocator 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -324,7 +338,7 @@ dependencies = [
|
||||
"uart_16550 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ucore-memory 0.1.0",
|
||||
"ucore-process 0.1.0",
|
||||
"volatile 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"volatile 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"x86_64 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -361,7 +375,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "volatile"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -431,6 +445,7 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum aarch64 2.2.2 (git+https://github.com/equation314/aarch64)" = "<none>"
|
||||
"checksum apic 0.1.0 (git+https://github.com/wangrunji0408/APIC-Rust)" = "<none>"
|
||||
"checksum array-init 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72"
|
||||
"checksum bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a3caf393d93b2d453e80638d0674597020cef3382ada454faacd43d1a55a735a"
|
||||
@ -446,7 +461,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||
"checksum getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0a7292d30132fb5424b354f5dc02512a86e4c516fe544bb7a25e7f266951b797"
|
||||
"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
|
||||
"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d"
|
||||
"checksum libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)" = "10923947f84a519a45c8fefb7dd1b3e8c08747993381adee176d7a82b4195311"
|
||||
"checksum linked_list_allocator 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "655d57c71827fe0891ce72231b6aa5e14033dae3f604609e6a6f807267c1678d"
|
||||
"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6"
|
||||
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
|
||||
@ -472,7 +487,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
|
||||
"checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5"
|
||||
"checksum ux 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53d8df5dd8d07fedccd202de1887d94481fadaea3db70479f459e8163a1fab41"
|
||||
"checksum volatile 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "54d4343a2df2d65144a874f95950754ee7b7e8594f6027aae8c7d0f4858a3fe8"
|
||||
"checksum volatile 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d9ca391c55768e479d5c2f8beb40c136df09257292a809ea514e82cfdfc15d00"
|
||||
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
|
||||
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
@ -5,8 +5,13 @@ authors = ["Runji Wang <wangrunji0408@163.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
link_user_program = []
|
||||
# Without BBL (for riscv32 FPGA board)
|
||||
no_bbl = []
|
||||
# Disable paging (for riscv32)
|
||||
no_mmu = []
|
||||
# Kernel in M-mode (for riscv32)
|
||||
m_mode = ["no_mmu"]
|
||||
# (for aarch64 RaspberryPi3)
|
||||
board_raspi3 = []
|
||||
|
||||
[profile.dev]
|
||||
@ -46,7 +51,7 @@ riscv = { git = "https://github.com/riscv-and-rust-and-decaf/riscv" }
|
||||
bbl = { path = "../crate/bbl" }
|
||||
|
||||
[target.'cfg(target_arch = "aarch64")'.dependencies]
|
||||
cortex-a = "2.2.1"
|
||||
aarch64 = { git = "https://github.com/equation314/aarch64" }
|
||||
atags = { path = "../crate/atags" }
|
||||
bcm2837 = { path = "../crate/bcm2837", features = ["use_generic_timer"] }
|
||||
|
||||
@ -58,7 +63,7 @@ minimum-image-size = 0 # The minimum output file size (in MiB)
|
||||
# (the "{}" will be replaced with the path to the bootable disk image)
|
||||
run-command = ["qemu-system-x86_64",
|
||||
"-drive", "format=raw,file={}",
|
||||
"-drive", "format=raw,file=../user/ucore32.img,media=disk,cache=writeback",
|
||||
"-drive", "format=raw,file=../user/img/ucore-i386-pic.img,media=disk,cache=writeback",
|
||||
"-serial", "mon:stdio",
|
||||
"-device", "isa-debug-exit",
|
||||
"-smp", "4"
|
||||
|
@ -13,15 +13,19 @@
|
||||
# mode = debug | release
|
||||
# LOG = off | error | warn | info | debug | trace
|
||||
# SFSIMG = SFS image path of user programs
|
||||
# smp SMP core number
|
||||
# smp = 1 | 2 | ... SMP core number
|
||||
# board = fpga Only available on riscv32, build without bbl, run on board
|
||||
# | raspi3 Only available on aarch64, run on Raspberry Pi 3 Model B/B+
|
||||
# m_mode Only available on riscv32, build for M-Mode, without MMU
|
||||
|
||||
arch ?= riscv32
|
||||
board ?= raspi3
|
||||
mode ?= debug
|
||||
LOG ?= debug
|
||||
smp ?= 4
|
||||
# NOTE: crate 'process' use this name 'm_mode' as an environment
|
||||
# to set interrupt (MIE or SIE)
|
||||
m_mode ?=
|
||||
|
||||
target := $(arch)-blog_os
|
||||
kernel := target/$(target)/$(mode)/ucore
|
||||
@ -37,50 +41,53 @@ export ARCH = $(arch)
|
||||
export SFSIMG = $(user_dir)/build/user-$(arch).img
|
||||
|
||||
### qemu options ###
|
||||
ifeq ($(arch), x86_64)
|
||||
qemu_opts := \
|
||||
-drive format=raw,file=$(bootimage) \
|
||||
-drive format=raw,file=$(SFSIMG),media=disk,cache=writeback \
|
||||
-smp cores=$(smp) \
|
||||
-serial mon:stdio \
|
||||
-device isa-debug-exit \
|
||||
-nographic
|
||||
|
||||
ifeq ($(arch), x86_64)
|
||||
qemu_opts += \
|
||||
-drive format=raw,file=$(bootimage) \
|
||||
-drive format=raw,file="../user/img/ucore-i386-pic.img",media=disk,cache=writeback \
|
||||
-serial mon:stdio \
|
||||
-device isa-debug-exit
|
||||
|
||||
else ifeq ($(arch), riscv32)
|
||||
qemu_opts := \
|
||||
qemu_opts += \
|
||||
-machine virt \
|
||||
-kernel $(bin) \
|
||||
-nographic \
|
||||
-smp cores=$(smp)
|
||||
-kernel $(bin)
|
||||
ifdef m_mode
|
||||
qemu_opts += -cpu rv32imacu-nommu
|
||||
endif
|
||||
|
||||
else ifeq ($(arch), aarch64)
|
||||
qemu_opts := \
|
||||
qemu_opts += \
|
||||
-machine $(board) \
|
||||
-serial null -serial mon:stdio \
|
||||
-nographic \
|
||||
-kernel $(bin)
|
||||
endif
|
||||
|
||||
ifdef d
|
||||
qemu_opts := $(qemu_opts) -d $(d)
|
||||
qemu_opts += -d $(d)
|
||||
endif
|
||||
|
||||
### build args ###
|
||||
ifeq ($(arch), riscv32)
|
||||
ifeq ($(board), fpga)
|
||||
features := $(features) no_bbl
|
||||
features += no_bbl
|
||||
endif
|
||||
endif
|
||||
|
||||
# Link user binaries at ../user
|
||||
ifdef link_user
|
||||
features := $(features) link_user_program
|
||||
assembly_object_files := $(assembly_object_files) $(user_obj)
|
||||
ifdef m_mode
|
||||
features += no_mmu m_mode
|
||||
bbl_m_mode := --enable-boot-machine
|
||||
endif
|
||||
|
||||
features := $(features) board_$(board)
|
||||
features += board_$(board)
|
||||
build_args := --target $(target).json --features "$(features)"
|
||||
|
||||
ifeq ($(mode), release)
|
||||
build_args := $(build_args) --release
|
||||
build_args += --release
|
||||
endif
|
||||
|
||||
|
||||
@ -149,8 +156,8 @@ else
|
||||
mkdir -p build && \
|
||||
cd build && \
|
||||
../configure \
|
||||
--enable-32bit \
|
||||
--enable-logo \
|
||||
$(bbl_m_mode) \
|
||||
--with-arch=rv32imac \
|
||||
--disable-fp-emulation \
|
||||
--host=riscv64-unknown-elf \
|
||||
--with-payload=$(abspath $(kernel)) && \
|
||||
@ -175,7 +182,6 @@ endif
|
||||
endif
|
||||
|
||||
|
||||
|
||||
### user programs ###
|
||||
sfsimg:
|
||||
@cd $(user_dir) && make sfsimg
|
||||
@ -186,7 +192,6 @@ $(user_obj): $(user_bins)
|
||||
$(ld) -o $(abspath $@) $(patsubst %, -b binary %, $(notdir $(user_bins)))
|
||||
|
||||
|
||||
|
||||
### install ###
|
||||
ifeq ($(board), raspi3)
|
||||
sd_card ?=
|
||||
|
@ -1,16 +1,18 @@
|
||||
//! Raspberry PI 3 Model B/B+
|
||||
|
||||
use once::*;
|
||||
|
||||
pub mod irq;
|
||||
pub mod timer;
|
||||
pub mod serial;
|
||||
|
||||
pub fn init() {
|
||||
// FIXME
|
||||
// assert_has_not_been_called!("board::init must be called only once");
|
||||
pub const IO_REMAP_BASE: usize = bcm2837::IO_BASE;
|
||||
pub const IO_REMAP_END: usize = 0x40001000;
|
||||
|
||||
unsafe {
|
||||
serial::SERIAL_PORT.init();
|
||||
}
|
||||
pub fn init() {
|
||||
assert_has_not_been_called!("board::init must be called only once");
|
||||
|
||||
serial::SERIAL_PORT.lock().init();
|
||||
|
||||
println!("Hello Raspberry Pi!");
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use bcm2837::mini_uart::MiniUart;
|
||||
use core::fmt;
|
||||
use spin::Mutex;
|
||||
use once::*;
|
||||
|
||||
/// Struct to get a global SerialPort interface
|
||||
pub struct SerialPort {
|
||||
@ -19,8 +20,7 @@ impl SerialPort {
|
||||
|
||||
/// Init a newly created SerialPort, can only be called once.
|
||||
pub fn init(&mut self) {
|
||||
// FIXME
|
||||
// assert_has_not_been_called!("SerialPort::init must be called only once");
|
||||
assert_has_not_been_called!("SerialPort::init must be called only once");
|
||||
self.mu = Some(MiniUart::new());
|
||||
}
|
||||
|
||||
@ -70,7 +70,4 @@ impl fmt::Write for SerialPort {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME
|
||||
// pub static SERIAL_PORT: Mutex<SerialPort> = Mutex::new(SerialPort::new());
|
||||
pub static mut SERIAL_PORT: SerialPort = SerialPort::new();
|
||||
|
||||
pub static SERIAL_PORT: Mutex<SerialPort> = Mutex::new(SerialPort::new());
|
||||
|
@ -2,8 +2,7 @@
|
||||
|
||||
.section .text.boot
|
||||
|
||||
.global _start
|
||||
_start:
|
||||
boot:
|
||||
# read cpu affinity, start core 0, halt rest
|
||||
mrs x1, mpidr_el1
|
||||
and x1, x1, #3
|
||||
@ -16,7 +15,7 @@ halt:
|
||||
|
||||
setup:
|
||||
# store the desired EL1 stack pointer in x1
|
||||
adr x1, _start
|
||||
ldr x1, =_start
|
||||
|
||||
# use SP_ELx for Exception level ELx
|
||||
msr SPsel, #1
|
||||
@ -94,17 +93,22 @@ set_stack:
|
||||
|
||||
zero_bss:
|
||||
# load the start address and number of bytes in BSS section
|
||||
ldr x1, =__bss_start
|
||||
ldr x1, =sbss
|
||||
ldr x2, =__bss_length
|
||||
|
||||
zero_bss_loop:
|
||||
# zero out the BSS section, 64-bits at a time
|
||||
cbz x2, go_kmain
|
||||
cbz x2, zero_bss_loop_end
|
||||
str xzr, [x1], #8
|
||||
sub x2, x2, #8
|
||||
cbnz x2, zero_bss_loop
|
||||
|
||||
go_kmain:
|
||||
zero_bss_loop_end:
|
||||
b _start
|
||||
|
||||
.section .text.entry
|
||||
.globl _start
|
||||
_start:
|
||||
# jump to rust_main, which shouldn't return. halt if it does
|
||||
bl rust_main
|
||||
b halt
|
||||
|
@ -3,36 +3,49 @@ ENTRY(_start)
|
||||
SECTIONS {
|
||||
. = 0x80000; /* Raspbery Pi 3 Aarch64 (kernel8.img) load address */
|
||||
|
||||
/* start of the binary */
|
||||
_start = .;
|
||||
.boot : {
|
||||
KEEP(*(.text.boot)) /* from boot.S */
|
||||
}
|
||||
|
||||
. = 0x100000; /* Load the kernel at this address. It's also kernel stack top address */
|
||||
bootstacktop = .;
|
||||
|
||||
.text : {
|
||||
KEEP(*(.text.boot)) /* from boot.S */
|
||||
stext = .;
|
||||
*(.text.entry)
|
||||
*(.text .text.* .gnu.linkonce.t*)
|
||||
. = ALIGN(4K);
|
||||
etext = .;
|
||||
}
|
||||
|
||||
.rodata : {
|
||||
srodata = .;
|
||||
*(.rodata .rodata.* .gnu.linkonce.r*)
|
||||
. = ALIGN(4K);
|
||||
erodata = .;
|
||||
}
|
||||
|
||||
.data : {
|
||||
sdata = .;
|
||||
*(.data .data.* .gnu.linkonce.d*)
|
||||
. = ALIGN(4K);
|
||||
edata = .;
|
||||
}
|
||||
|
||||
.bss (NOLOAD) : {
|
||||
. = ALIGN(32);
|
||||
__bss_start = .;
|
||||
sbss = .;
|
||||
*(.bss .bss.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(8);
|
||||
__bss_end = .;
|
||||
. = ALIGN(4K);
|
||||
ebss = .;
|
||||
}
|
||||
|
||||
/* end of the binary */
|
||||
_end = ALIGN(8);
|
||||
|
||||
/* number of bytes in BSS section and complete binary */
|
||||
__bss_length = (__bss_end - __bss_start);
|
||||
__bss_length = (ebss - sbss);
|
||||
__binary_length = (_end - _start);
|
||||
|
||||
/DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
|
||||
|
@ -1,11 +1,8 @@
|
||||
//! TODO: replace unmiplemented consts with real value
|
||||
const UNIMPLEMENTED: usize = 0;
|
||||
pub const KERNEL_OFFSET: usize = UNIMPLEMENTED;
|
||||
pub const KERNEL_PML4: usize = UNIMPLEMENTED;
|
||||
pub const KERNEL_HEAP_OFFSET: usize = UNIMPLEMENTED;
|
||||
pub const RECURSIVE_INDEX: usize = 0o777;
|
||||
pub const KERNEL_OFFSET: usize = 0;
|
||||
pub const KERNEL_PML4: usize = 0;
|
||||
pub const KERNEL_HEAP_SIZE: usize = 8 * 1024 * 1024;
|
||||
pub const MEMORY_OFFSET: usize = UNIMPLEMENTED;
|
||||
pub const MEMORY_END: usize = UNIMPLEMENTED;
|
||||
pub const USER_STACK_OFFSET: usize = UNIMPLEMENTED;
|
||||
pub const USER_STACK_SIZE: usize = UNIMPLEMENTED;
|
||||
pub const USER32_STACK_OFFSET: usize = UNIMPLEMENTED;
|
||||
pub const MEMORY_OFFSET: usize = 0;
|
||||
pub const USER_STACK_OFFSET: usize = 0xffff_8000_0000_0000;
|
||||
pub const USER_STACK_SIZE: usize = 1 * 1024 * 1024;
|
||||
pub const USER32_STACK_OFFSET: usize = USER_STACK_OFFSET;
|
@ -1,12 +1,19 @@
|
||||
//! TrapFrame and context definitions for aarch64.
|
||||
|
||||
use spin::Mutex;
|
||||
use lazy_static::lazy_static;
|
||||
use aarch64::barrier;
|
||||
use aarch64::addr::PhysAddr;
|
||||
use aarch64::paging::PhysFrame;
|
||||
use aarch64::asm::{tlb_invalidate_all, ttbr_el1_write_asid};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Default, Debug, Copy, Clone)]
|
||||
pub struct TrapFrame {
|
||||
pub elr: usize,
|
||||
pub spsr: usize,
|
||||
pub sp: usize,
|
||||
pub tpidr: usize,
|
||||
pub tpidr: usize, // currently unused
|
||||
// pub q0to31: [u128; 32], // disable SIMD/FP registers
|
||||
pub x1to29: [usize; 29],
|
||||
pub __reserved: usize,
|
||||
@ -47,10 +54,14 @@ pub struct InitStack {
|
||||
}
|
||||
|
||||
impl InitStack {
|
||||
unsafe fn push_at(self, stack_top: usize) -> Context {
|
||||
unsafe fn push_at(self, stack_top: usize, ttbr: usize) -> Context {
|
||||
let ptr = (stack_top as *mut Self).offset(-1);
|
||||
*ptr = self;
|
||||
Context(ptr as usize)
|
||||
Context {
|
||||
stack_top: ptr as usize,
|
||||
ttbr: PhysFrame::containing_address(PhysAddr::new(ttbr as u64)),
|
||||
asid: Asid::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,18 +74,20 @@ extern {
|
||||
struct ContextData {
|
||||
x19to29: [usize; 11],
|
||||
lr: usize,
|
||||
ttbr0: usize,
|
||||
}
|
||||
|
||||
impl ContextData {
|
||||
fn new(ttbr0: usize) -> Self {
|
||||
ContextData { lr: __trapret as usize, ttbr0, ..ContextData::default() }
|
||||
fn new() -> Self {
|
||||
ContextData { lr: __trapret as usize, ..ContextData::default() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Context(usize);
|
||||
pub struct Context {
|
||||
stack_top: usize,
|
||||
ttbr: PhysFrame,
|
||||
asid: Asid,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Switch to another kernel thread.
|
||||
@ -86,10 +99,10 @@ impl Context {
|
||||
/// Pop all callee-saved registers, then return to the target.
|
||||
#[naked]
|
||||
#[inline(never)]
|
||||
pub unsafe extern fn switch(&mut self, target: &mut Self) {
|
||||
unsafe extern fn __switch(self_stack: &mut usize, target_stack: &mut usize) {
|
||||
asm!(
|
||||
"
|
||||
mov x10, #-(13 * 8)
|
||||
mov x10, #-(12 * 8)
|
||||
add x8, sp, x10
|
||||
str x8, [x0]
|
||||
stp x19, x20, [x8], #16 // store callee-saved registers
|
||||
@ -98,8 +111,6 @@ impl Context {
|
||||
stp x25, x26, [x8], #16
|
||||
stp x27, x28, [x8], #16
|
||||
stp x29, lr, [x8], #16
|
||||
mrs x9, ttbr0_el1
|
||||
str x9, [x8], #8
|
||||
|
||||
ldr x8, [x1]
|
||||
ldp x19, x20, [x8], #16 // restore callee-saved registers
|
||||
@ -108,50 +119,94 @@ impl Context {
|
||||
ldp x25, x26, [x8], #16
|
||||
ldp x27, x28, [x8], #16
|
||||
ldp x29, lr, [x8], #16
|
||||
ldr x9, [x8], #8
|
||||
mov sp, x8
|
||||
|
||||
msr ttbr0_el1, x9 // set new page directory
|
||||
dsb ishst // ensure write has completed
|
||||
tlbi vmalle1is // invalidate the TLB entry for the entry that changes
|
||||
dsb ish // ensure TLB invalidation is complete
|
||||
isb // synchronize context on this processor
|
||||
|
||||
str xzr, [x1]
|
||||
ret"
|
||||
: : : : "volatile" );
|
||||
}
|
||||
|
||||
pub unsafe fn null() -> Self {
|
||||
Context(0)
|
||||
pub unsafe fn switch(&mut self, target: &mut Self) {
|
||||
target.asid = ASID_ALLOCATOR.lock().alloc(target.asid);
|
||||
|
||||
// with ASID we needn't flush TLB frequently
|
||||
ttbr_el1_write_asid(1, target.asid.value, target.ttbr);
|
||||
barrier::dsb(barrier::ISH);
|
||||
Self::__switch(&mut self.stack_top, &mut target.stack_top);
|
||||
}
|
||||
|
||||
pub unsafe fn new_kernel_thread(entry: extern fn(usize) -> !, arg: usize, kstack_top: usize, ttbr0: usize) -> Self {
|
||||
pub unsafe fn null() -> Self {
|
||||
Context {
|
||||
stack_top: 0,
|
||||
ttbr: PhysFrame::containing_address(PhysAddr::new(0)),
|
||||
asid: Asid::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn new_kernel_thread(entry: extern fn(usize) -> !, arg: usize, kstack_top: usize, ttbr: usize) -> Self {
|
||||
InitStack {
|
||||
context: ContextData::new(ttbr0),
|
||||
context: ContextData::new(),
|
||||
tf: TrapFrame::new_kernel_thread(entry, arg, kstack_top),
|
||||
}.push_at(kstack_top)
|
||||
}.push_at(kstack_top, ttbr)
|
||||
}
|
||||
pub unsafe fn new_user_thread(entry_addr: usize, ustack_top: usize, kstack_top: usize, is32: bool, ttbr0: usize) -> Self {
|
||||
pub unsafe fn new_user_thread(entry_addr: usize, ustack_top: usize, kstack_top: usize, is32: bool, ttbr: usize) -> Self {
|
||||
InitStack {
|
||||
context: ContextData::new(ttbr0),
|
||||
context: ContextData::new(),
|
||||
tf: TrapFrame::new_user_thread(entry_addr, ustack_top),
|
||||
}.push_at(kstack_top)
|
||||
}.push_at(kstack_top, ttbr)
|
||||
}
|
||||
pub unsafe fn new_fork(tf: &TrapFrame, kstack_top: usize, ttbr0: usize) -> Self {
|
||||
pub unsafe fn new_fork(tf: &TrapFrame, kstack_top: usize, ttbr: usize) -> Self {
|
||||
InitStack {
|
||||
context: ContextData::new(ttbr0),
|
||||
context: ContextData::new(),
|
||||
tf: {
|
||||
let mut tf = tf.clone();
|
||||
tf.x0 = 0;
|
||||
tf
|
||||
},
|
||||
}.push_at(kstack_top)
|
||||
}.push_at(kstack_top, ttbr)
|
||||
}
|
||||
/// Called at a new user context
|
||||
/// To get the init TrapFrame in sys_exec
|
||||
pub unsafe fn get_init_tf(&self) -> TrapFrame {
|
||||
(*(self.0 as *const InitStack)).tf.clone()
|
||||
(*(self.stack_top as *const InitStack)).tf.clone()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const ASID_MASK: u16 = 0xffff;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default)]
|
||||
#[repr(C)]
|
||||
struct Asid {
|
||||
value: u16,
|
||||
generation: u16,
|
||||
}
|
||||
|
||||
struct AsidAllocator(Asid);
|
||||
|
||||
impl AsidAllocator {
|
||||
fn new() -> Self {
|
||||
AsidAllocator(Asid { value: 0, generation: 1 })
|
||||
}
|
||||
|
||||
fn alloc(&mut self, old_asid: Asid) -> Asid {
|
||||
if self.0.generation == old_asid.generation {
|
||||
return old_asid;
|
||||
}
|
||||
|
||||
if self.0.value == ASID_MASK {
|
||||
self.0.value = 0;
|
||||
self.0.generation = self.0.generation.wrapping_add(1);
|
||||
if self.0.generation == 0 {
|
||||
self.0.generation += 1;
|
||||
}
|
||||
tlb_invalidate_all();
|
||||
}
|
||||
self.0.value += 1;
|
||||
return self.0;
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref ASID_ALLOCATOR: Mutex<AsidAllocator> = Mutex::new(AsidAllocator::new());
|
||||
}
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
use crate::arch::board::irq::handle_irq;
|
||||
use super::context::TrapFrame;
|
||||
use super::syndrome::Syndrome;
|
||||
use super::syndrome::{Fault, Syndrome};
|
||||
|
||||
use aarch64::regs::*;
|
||||
use log::*;
|
||||
|
||||
global_asm!(include_str!("trap.S"));
|
||||
@ -39,14 +41,22 @@ pub struct Info {
|
||||
/// the trap frame for the exception.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn rust_trap(info: Info, esr: u32, tf: &mut TrapFrame) {
|
||||
let syndrome = Syndrome::from(esr);
|
||||
trace!("Interrupt: {:?} from: {:?}", syndrome, info);
|
||||
trace!("Interrupt: {:?}, ELR: {:#x?}", info, tf.elr);
|
||||
match info.kind {
|
||||
Kind::Synchronous => {
|
||||
let syndrome = Syndrome::from(esr);
|
||||
trace!("ESR: {:#x?}, Syndrome: {:?}", esr, syndrome);
|
||||
// syndrome is only valid with sync
|
||||
match syndrome {
|
||||
Syndrome::Brk(brk) => handle_break(brk, tf),
|
||||
Syndrome::Svc(_) => handle_syscall(tf),
|
||||
Syndrome::Svc(svc) => handle_syscall(svc, tf),
|
||||
Syndrome::DataAbort { kind, level: _ }
|
||||
| Syndrome::InstructionAbort { kind, level: _ } => match kind {
|
||||
Fault::Translation | Fault::AccessFlag | Fault::Permission => {
|
||||
handle_page_fault(tf)
|
||||
}
|
||||
_ => crate::trap::error(tf),
|
||||
},
|
||||
_ => crate::trap::error(tf),
|
||||
}
|
||||
}
|
||||
@ -56,12 +66,16 @@ pub extern "C" fn rust_trap(info: Info, esr: u32, tf: &mut TrapFrame) {
|
||||
trace!("Interrupt end");
|
||||
}
|
||||
|
||||
fn handle_break(num: u16, tf: &mut TrapFrame) {
|
||||
fn handle_break(_num: u16, tf: &mut TrapFrame) {
|
||||
// Skip the current brk instruction (ref: J1.1.2, page 6147)
|
||||
tf.elr += 4;
|
||||
}
|
||||
|
||||
fn handle_syscall(tf: &mut TrapFrame) {
|
||||
fn handle_syscall(num: u16, tf: &mut TrapFrame) {
|
||||
if num != 0 {
|
||||
crate::trap::error(tf);
|
||||
}
|
||||
|
||||
// svc instruction has been skipped in syscall (ref: J1.1.2, page 6152)
|
||||
let ret = crate::syscall::syscall(
|
||||
tf.x1to29[7] as usize,
|
||||
@ -77,3 +91,10 @@ fn handle_syscall(tf: &mut TrapFrame) {
|
||||
);
|
||||
tf.x0 = ret as usize;
|
||||
}
|
||||
|
||||
fn handle_page_fault(tf: &mut TrapFrame) {
|
||||
let addr = FAR_EL1.get();
|
||||
error!("\nEXCEPTION: Page Fault @ {:#x}", addr);
|
||||
|
||||
crate::trap::error(tf);
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ mod handler;
|
||||
mod context;
|
||||
mod syndrome;
|
||||
|
||||
use cortex_a::regs::*;
|
||||
use aarch64::regs::*;
|
||||
|
||||
pub use self::context::*;
|
||||
pub use self::handler::*;
|
||||
|
||||
|
@ -4,15 +4,11 @@ use core::fmt::{Arguments, Write};
|
||||
use super::board::serial::{SerialRead, SERIAL_PORT};
|
||||
|
||||
pub fn getchar() -> char {
|
||||
// FIXME
|
||||
unsafe {
|
||||
SERIAL_PORT.receive() as char
|
||||
}
|
||||
unsafe { SERIAL_PORT.force_unlock(); }
|
||||
SERIAL_PORT.lock().receive() as char
|
||||
}
|
||||
|
||||
pub fn putfmt(fmt: Arguments) {
|
||||
// FIXME
|
||||
unsafe {
|
||||
SERIAL_PORT.write_fmt(fmt).unwrap()
|
||||
}
|
||||
unsafe { SERIAL_PORT.force_unlock(); }
|
||||
SERIAL_PORT.lock().write_fmt(fmt).unwrap()
|
||||
}
|
||||
|
@ -1,29 +1,125 @@
|
||||
//! Memory initialization for aarch64.
|
||||
|
||||
use log::*;
|
||||
use ucore_memory::PAGE_SIZE;
|
||||
use atags::atags::Atags;
|
||||
use crate::HEAP_ALLOCATOR;
|
||||
use log::*;
|
||||
use aarch64::{barrier, regs::*, addr::*};
|
||||
use aarch64::paging::{PhysFrame as Frame, memory_attribute::*};
|
||||
use crate::memory::{FRAME_ALLOCATOR, init_heap, MemoryArea, MemoryAttr, MemorySet};
|
||||
|
||||
/// Memory initialization.
|
||||
pub fn init() {
|
||||
let (start, end) = memory_map().expect("failed to find memory map");
|
||||
unsafe {
|
||||
HEAP_ALLOCATOR.lock().init(start, end - start);
|
||||
}
|
||||
init_frame_allocator();
|
||||
init_heap();
|
||||
remap_the_kernel();
|
||||
info!("memory: init end");
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
static _end: u8;
|
||||
/// initialize temporary paging and enable mmu immediately after boot. Serial port is disabled at this time.
|
||||
pub fn init_mmu_early() {
|
||||
#[repr(align(4096))]
|
||||
struct PageData([u8; PAGE_SIZE]);
|
||||
static PAGE_TABLE_LVL4: PageData = PageData([0; PAGE_SIZE]);
|
||||
static PAGE_TABLE_LVL3: PageData = PageData([0; PAGE_SIZE]);
|
||||
static PAGE_TABLE_LVL2: PageData = PageData([0; PAGE_SIZE]);
|
||||
|
||||
let frame_lvl4 = Frame::containing_address(PhysAddr::new(&PAGE_TABLE_LVL4 as *const _ as u64));
|
||||
let frame_lvl3 = Frame::containing_address(PhysAddr::new(&PAGE_TABLE_LVL3 as *const _ as u64));
|
||||
let frame_lvl2 = Frame::containing_address(PhysAddr::new(&PAGE_TABLE_LVL2 as *const _ as u64));
|
||||
super::paging::setup_temp_page_table(frame_lvl4, frame_lvl3, frame_lvl2);
|
||||
|
||||
// device.
|
||||
MAIR_EL1.write(
|
||||
MAIR_EL1::Attr0.val(MairDevice::config_value()) +
|
||||
MAIR_EL1::Attr1.val(MairNormal::config_value()) +
|
||||
MAIR_EL1::Attr2.val(MairNormalNonCacheable::config_value()),
|
||||
);
|
||||
|
||||
// Configure various settings of stage 1 of the EL1 translation regime.
|
||||
let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange);
|
||||
TCR_EL1.write(
|
||||
TCR_EL1::TBI1::Ignored +
|
||||
TCR_EL1::TBI0::Ignored +
|
||||
TCR_EL1::AS::Bits_16 +
|
||||
TCR_EL1::IPS.val(ips) +
|
||||
|
||||
TCR_EL1::TG1::KiB_4 +
|
||||
TCR_EL1::SH1::Inner +
|
||||
TCR_EL1::ORGN1::WriteBack_ReadAlloc_WriteAlloc_Cacheable +
|
||||
TCR_EL1::IRGN1::WriteBack_ReadAlloc_WriteAlloc_Cacheable +
|
||||
TCR_EL1::EPD1::EnableTTBR1Walks +
|
||||
TCR_EL1::A1::UseTTBR1ASID +
|
||||
TCR_EL1::T1SZ.val(16) +
|
||||
|
||||
TCR_EL1::TG0::KiB_4 +
|
||||
TCR_EL1::SH0::Inner +
|
||||
TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable +
|
||||
TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable +
|
||||
TCR_EL1::EPD0::EnableTTBR0Walks +
|
||||
TCR_EL1::T0SZ.val(16),
|
||||
);
|
||||
|
||||
// Switch the MMU on.
|
||||
//
|
||||
// First, force all previous changes to be seen before the MMU is enabled.
|
||||
unsafe { barrier::isb(barrier::SY); }
|
||||
|
||||
// Enable the MMU and turn on data and instruction caching.
|
||||
SCTLR_EL1.modify(SCTLR_EL1::M::Enable + SCTLR_EL1::C::Cacheable + SCTLR_EL1::I::Cacheable);
|
||||
|
||||
// Force MMU init to complete before next instruction
|
||||
unsafe { barrier::isb(barrier::SY); }
|
||||
}
|
||||
|
||||
fn init_frame_allocator() {
|
||||
use bit_allocator::BitAlloc;
|
||||
use core::ops::Range;
|
||||
use crate::consts::MEMORY_OFFSET;
|
||||
|
||||
let (start, end) = memory_map().expect("failed to find memory map");
|
||||
let mut ba = FRAME_ALLOCATOR.lock();
|
||||
ba.insert(to_range(start, end));
|
||||
info!("FrameAllocator init end");
|
||||
|
||||
/*
|
||||
* @param:
|
||||
* start: start address
|
||||
* end: end address
|
||||
* @brief:
|
||||
* transform the memory address to the page number
|
||||
* @retval:
|
||||
* the page number range from start address to end address
|
||||
*/
|
||||
fn to_range(start: usize, end: usize) -> Range<usize> {
|
||||
let page_start = (start - MEMORY_OFFSET) / PAGE_SIZE;
|
||||
let page_end = (end - MEMORY_OFFSET - 1) / PAGE_SIZE + 1;
|
||||
page_start..page_end
|
||||
}
|
||||
}
|
||||
|
||||
/// remap kernel page table after all initialization.
|
||||
fn remap_the_kernel() {
|
||||
let mut ms = unsafe { MemorySet::new_bare() };
|
||||
ms.push(MemoryArea::new_identity(0, bootstacktop as usize, MemoryAttr::default(), "kstack"));
|
||||
ms.push(MemoryArea::new_identity(stext as usize, etext as usize, MemoryAttr::default().execute().readonly(), "text"));
|
||||
ms.push(MemoryArea::new_identity(sdata as usize, edata as usize, MemoryAttr::default(), "data"));
|
||||
ms.push(MemoryArea::new_identity(srodata as usize, erodata as usize, MemoryAttr::default().readonly(), "rodata"));
|
||||
ms.push(MemoryArea::new_identity(sbss as usize, ebss as usize, MemoryAttr::default(), "bss"));
|
||||
|
||||
use super::board::{IO_REMAP_BASE, IO_REMAP_END};
|
||||
ms.push(MemoryArea::new_identity(IO_REMAP_BASE, IO_REMAP_END, MemoryAttr::default().mmio(), "io_remap"));
|
||||
|
||||
unsafe { ms.get_page_table_mut().activate_as_kernel(); }
|
||||
::core::mem::forget(ms);
|
||||
info!("kernel remap end");
|
||||
}
|
||||
|
||||
/// Returns the (start address, end address) of the available memory on this
|
||||
/// system if it can be determined. If it cannot, `None` is returned.
|
||||
///
|
||||
/// This function is expected to return `Some` under all normal cirumstances.
|
||||
pub fn memory_map() -> Option<(usize, usize)> {
|
||||
let binary_end = unsafe { (&_end as *const u8) as u32 };
|
||||
fn memory_map() -> Option<(usize, usize)> {
|
||||
let binary_end = _end as u32;
|
||||
|
||||
let mut atags: Atags = Atags::get();
|
||||
while let Some(atag) = atags.next() {
|
||||
@ -34,3 +130,17 @@ pub fn memory_map() -> Option<(usize, usize)> {
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
extern {
|
||||
fn bootstacktop();
|
||||
fn stext();
|
||||
fn etext();
|
||||
fn sdata();
|
||||
fn edata();
|
||||
fn srodata();
|
||||
fn erodata();
|
||||
fn sbss();
|
||||
fn ebss();
|
||||
fn _start();
|
||||
fn _end();
|
||||
}
|
||||
|
@ -18,31 +18,23 @@ global_asm!(include_str!("boot/boot.S"));
|
||||
/// The entry point of kernel
|
||||
#[no_mangle] // don't mangle the name of this function
|
||||
pub extern "C" fn rust_main() -> ! {
|
||||
// Enable mmu and paging
|
||||
memory::init_mmu_early();
|
||||
|
||||
// Init board to enable serial port.
|
||||
board::init();
|
||||
crate::logging::init(); // FIXME
|
||||
println!("{}", LOGO);
|
||||
|
||||
crate::logging::init();
|
||||
interrupt::init();
|
||||
memory::init();
|
||||
timer::init();
|
||||
|
||||
use crate::process::{processor, ContextImpl};
|
||||
crate::process::init();
|
||||
processor().manager().add(ContextImpl::new_kernel(kernel_proc2, 2333), 0);
|
||||
processor().manager().add(ContextImpl::new_user_test(kernel_proc3), 0);
|
||||
|
||||
crate::kmain();
|
||||
}
|
||||
|
||||
extern fn kernel_proc2(arg: usize) -> ! {
|
||||
use alloc::format;
|
||||
test_shell(&format!("proc2-{}>> ", arg));
|
||||
}
|
||||
|
||||
extern fn kernel_proc3(arg: usize) -> ! {
|
||||
use alloc::format;
|
||||
test_shell(&format!("proc3-{}$ ", arg));
|
||||
}
|
||||
|
||||
const LOGO: &str = r#"
|
||||
____ __ ____ _____
|
||||
/ __ \ __ __ _____ / /_ / __ \/ ___/
|
||||
@ -50,50 +42,3 @@ const LOGO: &str = r#"
|
||||
/ _, _// /_/ /(__ )/ /_ / /_/ /___/ /
|
||||
/_/ |_| \__,_//____/ \__/ \____//____/
|
||||
"#;
|
||||
|
||||
pub fn show_logo() {
|
||||
println!("{}", LOGO);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn sys_call(id: usize, arg0: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) -> i32 {
|
||||
let ret: i32;
|
||||
unsafe {
|
||||
asm!("svc 0"
|
||||
: "={x0}" (ret)
|
||||
: "{x8}" (id), "{x0}" (arg0), "{x1}" (arg1), "{x2}" (arg2), "{x3}" (arg3), "{x4}" (arg4), "{x5}" (arg5)
|
||||
: "memory"
|
||||
: "volatile");
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn test_shell(prefix: &str) -> ! {
|
||||
show_logo();
|
||||
loop {
|
||||
print!("{}", prefix);
|
||||
loop {
|
||||
let c = io::getchar();
|
||||
match c {
|
||||
'\u{7f}' => {
|
||||
print!("\u{7f}");
|
||||
}
|
||||
'c' => unsafe {
|
||||
print!("sys_putc: ");
|
||||
sys_call(30, 'A' as usize, 0, 0, 0, 0, 0);
|
||||
},
|
||||
't' => unsafe {
|
||||
println!("sys_get_time: {}", sys_call(17, 0, 0, 0, 0, 0, 0));
|
||||
},
|
||||
' '...'\u{7e}' => {
|
||||
print!("{}", c);
|
||||
}
|
||||
'\n' | '\r' => {
|
||||
print!("\n");
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,222 +1,310 @@
|
||||
//! Page table implementations for aarch64.
|
||||
|
||||
use ucore_memory::memory_set::*;
|
||||
use ucore_memory::PAGE_SIZE;
|
||||
use ucore_memory::paging::*;
|
||||
|
||||
type VirtAddr = usize;
|
||||
type PhysAddr = usize;
|
||||
|
||||
use alloc::alloc::{alloc, Layout};
|
||||
use aarch64::asm::{tlb_invalidate, tlb_invalidate_all, flush_icache_all, ttbr_el1_read, ttbr_el1_write};
|
||||
use aarch64::{PhysAddr, VirtAddr};
|
||||
use aarch64::paging::{Mapper, PageTable as Aarch64PageTable, PageTableEntry, PageTableFlags as EF, RecursivePageTable};
|
||||
use aarch64::paging::{FrameAllocator, FrameDeallocator, Page, PhysFrame as Frame, Size4KiB, Size2MiB, Size1GiB};
|
||||
use aarch64::paging::memory_attribute::*;
|
||||
use log::*;
|
||||
// Depends on kernel
|
||||
use crate::consts::{KERNEL_PML4, RECURSIVE_INDEX};
|
||||
use crate::memory::{active_table, alloc_frame, dealloc_frame};
|
||||
|
||||
/// TODO
|
||||
pub struct ActivePageTable {
|
||||
// TODO
|
||||
}
|
||||
// need 3 page
|
||||
pub fn setup_temp_page_table(frame_lvl4: Frame, frame_lvl3: Frame, frame_lvl2: Frame) {
|
||||
let p4 = unsafe { &mut *(frame_lvl4.start_address().as_u64() as *mut Aarch64PageTable) };
|
||||
let p3 = unsafe { &mut *(frame_lvl3.start_address().as_u64() as *mut Aarch64PageTable) };
|
||||
let p2 = unsafe { &mut *(frame_lvl2.start_address().as_u64() as *mut Aarch64PageTable) };
|
||||
p4.zero();
|
||||
p3.zero();
|
||||
p2.zero();
|
||||
|
||||
impl ActivePageTable {
|
||||
/// TODO
|
||||
pub unsafe fn new() -> Self {
|
||||
unimplemented!()
|
||||
let (start_addr, end_addr) = (0, 0x40000000);
|
||||
let block_flags = EF::VALID | EF::AF | EF::WRITE | EF::XN;
|
||||
for page in Page::<Size2MiB>::range_of(start_addr, end_addr) {
|
||||
let paddr = PhysAddr::new(page.start_address().as_u64());
|
||||
|
||||
use super::board::IO_REMAP_BASE;
|
||||
if paddr.as_u64() >= IO_REMAP_BASE as u64 {
|
||||
p2[page.p2_index()].set_block::<Size2MiB>(paddr, block_flags | EF::PXN, MairDevice::attr_value());
|
||||
} else {
|
||||
p2[page.p2_index()].set_block::<Size2MiB>(paddr, block_flags, MairNormal::attr_value());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn token() -> usize {
|
||||
unimplemented!()
|
||||
}
|
||||
p3[0].set_frame(frame_lvl2, EF::default(), MairNormal::attr_value());
|
||||
p3[1].set_block::<Size1GiB>(PhysAddr::new(0x40000000), block_flags | EF::PXN, MairDevice::attr_value());
|
||||
|
||||
p4[0].set_frame(frame_lvl3, EF::default(), MairNormal::attr_value());
|
||||
p4[RECURSIVE_INDEX].set_frame(frame_lvl4, EF::default(), MairNormal::attr_value());
|
||||
|
||||
ttbr_el1_write(0, frame_lvl4);
|
||||
tlb_invalidate_all();
|
||||
}
|
||||
|
||||
pub struct ActivePageTable(RecursivePageTable<'static>);
|
||||
|
||||
pub struct PageEntry(PageTableEntry);
|
||||
|
||||
impl PageTable for ActivePageTable {
|
||||
type Entry = PageEntry;
|
||||
|
||||
fn map(&mut self, addr: VirtAddr, target: PhysAddr) -> &mut Self::Entry {
|
||||
unimplemented!()
|
||||
}
|
||||
fn unmap(&mut self, addr: VirtAddr) {
|
||||
unimplemented!()
|
||||
fn map(&mut self, addr: usize, target: usize) -> &mut PageEntry {
|
||||
let flags = EF::default();
|
||||
let attr = MairNormal::attr_value();
|
||||
self.0.map_to(Page::of_addr(addr), Frame::of_addr(target), flags, attr, &mut FrameAllocatorForAarch64)
|
||||
.unwrap().flush();
|
||||
self.get_entry(addr).expect("fail to get entry")
|
||||
}
|
||||
|
||||
fn get_entry(&mut self, addr: VirtAddr) -> Option<&mut Self::Entry> {
|
||||
unimplemented!()
|
||||
fn unmap(&mut self, addr: usize) {
|
||||
let (frame, flush) = self.0.unmap(Page::of_addr(addr)).unwrap();
|
||||
flush.flush();
|
||||
}
|
||||
|
||||
// For testing with mock
|
||||
fn get_page_slice_mut<'a, 'b>(&'a mut self, addr: VirtAddr) -> &'b mut [u8] {
|
||||
unimplemented!()
|
||||
fn get_entry(&mut self, addr: usize) -> Option<&mut PageEntry> {
|
||||
let entry_addr = ((addr >> 9) & 0o777_777_777_7770) | (RECURSIVE_INDEX << 39);
|
||||
Some(unsafe { &mut *(entry_addr as *mut PageEntry) })
|
||||
}
|
||||
|
||||
fn read(&mut self, addr: VirtAddr) -> u8 {
|
||||
unimplemented!()
|
||||
fn get_page_slice_mut<'a, 'b>(&'a mut self, addr: usize) -> &'b mut [u8] {
|
||||
use core::slice;
|
||||
unsafe { slice::from_raw_parts_mut((addr & !0xfffusize) as *mut u8, PAGE_SIZE) }
|
||||
}
|
||||
|
||||
fn write(&mut self, addr: VirtAddr, data: u8) {
|
||||
unimplemented!()
|
||||
fn read(&mut self, addr: usize) -> u8 {
|
||||
unsafe { *(addr as *const u8) }
|
||||
}
|
||||
|
||||
fn write(&mut self, addr: usize, data: u8) {
|
||||
unsafe { *(addr as *mut u8) = data; }
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO
|
||||
pub struct PageEntry {
|
||||
// TODO
|
||||
const ROOT_PAGE_TABLE: *mut Aarch64PageTable =
|
||||
((RECURSIVE_INDEX << 39) | (RECURSIVE_INDEX << 30) | (RECURSIVE_INDEX << 21) | (RECURSIVE_INDEX << 12)) as *mut Aarch64PageTable;
|
||||
|
||||
impl ActivePageTable {
|
||||
pub unsafe fn new() -> Self {
|
||||
ActivePageTable(RecursivePageTable::new(&mut *(ROOT_PAGE_TABLE as *mut _)).unwrap())
|
||||
}
|
||||
fn with_temporary_map(&mut self, frame: &Frame, f: impl FnOnce(&mut ActivePageTable, &mut Aarch64PageTable)) {
|
||||
// Create a temporary page
|
||||
let page = Page::of_addr(0xcafebabe);
|
||||
assert!(self.0.translate_page(page).is_none(), "temporary page is already mapped");
|
||||
// Map it to table
|
||||
self.map(page.start_address().as_u64() as usize, frame.start_address().as_u64() as usize);
|
||||
// Call f
|
||||
let table = unsafe { &mut *page.start_address().as_mut_ptr() };
|
||||
f(self, table);
|
||||
// Unmap the page
|
||||
self.unmap(0xcafebabe);
|
||||
}
|
||||
}
|
||||
|
||||
impl Entry for PageEntry {
|
||||
/// IMPORTANT!
|
||||
/// This must be called after any change to ensure it become effective.
|
||||
/// Usually this will make a flush to TLB/MMU.
|
||||
fn update(&mut self) {
|
||||
unimplemented!()
|
||||
let addr = VirtAddr::new_unchecked((self as *const _ as u64) << 9);
|
||||
tlb_invalidate(addr);
|
||||
}
|
||||
|
||||
/// Will be set when accessed
|
||||
fn accessed(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
fn present(&self) -> bool { self.0.flags().contains(EF::VALID) }
|
||||
fn accessed(&self) -> bool { self.0.flags().contains(EF::AF) }
|
||||
fn writable(&self) -> bool { self.0.flags().contains(EF::WRITE) }
|
||||
fn dirty(&self) -> bool { self.hw_dirty() && self.sw_dirty() }
|
||||
|
||||
/// Will be set when written
|
||||
fn dirty(&self) -> bool {
|
||||
unimplemented!()
|
||||
fn clear_accessed(&mut self) { self.as_flags().remove(EF::AF); }
|
||||
fn clear_dirty(&mut self)
|
||||
{
|
||||
self.as_flags().remove(EF::DIRTY);
|
||||
self.as_flags().insert(EF::AP_RO);
|
||||
}
|
||||
|
||||
/// Will PageFault when try to write page where writable=0
|
||||
fn writable(&self) -> bool {
|
||||
unimplemented!()
|
||||
fn set_writable(&mut self, value: bool)
|
||||
{
|
||||
self.as_flags().set(EF::AP_RO, !value);
|
||||
self.as_flags().set(EF::WRITE, value);
|
||||
}
|
||||
|
||||
/// Will PageFault when try to access page where present=0
|
||||
fn present(&self) -> bool {
|
||||
unimplemented!()
|
||||
fn set_present(&mut self, value: bool) { self.as_flags().set(EF::VALID, value); }
|
||||
fn target(&self) -> usize { self.0.addr().as_u64() as usize }
|
||||
fn set_target(&mut self, target: usize) {
|
||||
self.0.modify_addr(PhysAddr::new(target as u64));
|
||||
}
|
||||
|
||||
fn clear_accessed(&mut self) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn clear_dirty(&mut self) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn set_writable(&mut self, value: bool) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn set_present(&mut self, value: bool) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn target(&self) -> PhysAddr {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn set_target(&mut self, target: PhysAddr) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// For Copy-on-write extension
|
||||
fn writable_shared(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn readonly_shared(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn writable_shared(&self) -> bool { self.0.flags().contains(EF::WRITABLE_SHARED) }
|
||||
fn readonly_shared(&self) -> bool { self.0.flags().contains(EF::READONLY_SHARED) }
|
||||
fn set_shared(&mut self, writable: bool) {
|
||||
unimplemented!()
|
||||
let flags = self.as_flags();
|
||||
flags.set(EF::WRITABLE_SHARED, writable);
|
||||
flags.set(EF::READONLY_SHARED, !writable);
|
||||
}
|
||||
|
||||
fn clear_shared(&mut self) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// For Swap extension
|
||||
fn swapped(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn set_swapped(&mut self, value: bool) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn user(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn clear_shared(&mut self) { self.as_flags().remove(EF::WRITABLE_SHARED | EF::READONLY_SHARED); }
|
||||
fn user(&self) -> bool { self.0.flags().contains(EF::AP_EL0) }
|
||||
fn swapped(&self) -> bool { self.0.flags().contains(EF::SWAPPED) }
|
||||
fn set_swapped(&mut self, value: bool) { self.as_flags().set(EF::SWAPPED, value); }
|
||||
fn set_user(&mut self, value: bool) {
|
||||
unimplemented!()
|
||||
self.as_flags().set(EF::AP_EL0, value);
|
||||
self.as_flags().set(EF::nG, value); // set non-global to use ASID
|
||||
}
|
||||
|
||||
fn execute(&self) -> bool {
|
||||
unimplemented!()
|
||||
if self.user() {
|
||||
!self.0.flags().contains(EF::XN)
|
||||
} else {
|
||||
!self.0.flags().contains(EF::PXN)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_execute(&mut self, value: bool) {
|
||||
unimplemented!()
|
||||
if self.user() {
|
||||
self.as_flags().set(EF::XN, !value)
|
||||
} else {
|
||||
self.as_flags().set(EF::PXN, !value)
|
||||
}
|
||||
}
|
||||
fn mmio(&self) -> bool {
|
||||
self.0.attr().value == MairDevice::attr_value().value
|
||||
}
|
||||
fn set_mmio(&mut self, value: bool) {
|
||||
if value {
|
||||
self.0.modify_attr(MairDevice::attr_value())
|
||||
} else {
|
||||
self.0.modify_attr(MairNormal::attr_value())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct MockFrame(PhysAddr);
|
||||
|
||||
impl MockFrame {
|
||||
pub fn of_addr(addr: PhysAddr) -> Self {
|
||||
MockFrame(addr)
|
||||
}
|
||||
pub fn start_address(&self) -> PhysAddr {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn p2_index(&self) -> usize {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn p1_index(&self) -> usize {
|
||||
unimplemented!()
|
||||
}
|
||||
pub fn number(&self) -> usize {
|
||||
unimplemented!()
|
||||
impl PageEntry {
|
||||
fn read_only(&self) -> bool { self.0.flags().contains(EF::AP_RO) }
|
||||
fn hw_dirty(&self) -> bool { self.writable() && !self.read_only() }
|
||||
fn sw_dirty(&self) -> bool { self.0.flags().contains(EF::DIRTY) }
|
||||
fn as_flags(&mut self) -> &mut EF {
|
||||
unsafe { &mut *(self as *mut _ as *mut EF) }
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO
|
||||
#[derive(Debug)]
|
||||
pub struct InactivePageTable0 {
|
||||
p4_frame: MockFrame,
|
||||
p4_frame: Frame,
|
||||
}
|
||||
|
||||
/// TODO
|
||||
impl InactivePageTable for InactivePageTable0 {
|
||||
type Active = ActivePageTable;
|
||||
|
||||
fn new() -> Self {
|
||||
unsafe {
|
||||
let layout = Layout::new::<u64>();
|
||||
let ptr = alloc(layout);
|
||||
let frame = MockFrame::of_addr(*ptr as usize);
|
||||
InactivePageTable0 { p4_frame: frame }
|
||||
}
|
||||
// When the new InactivePageTable is created for the user MemorySet, it's use ttbr1 as the
|
||||
// TTBR. And the kernel TTBR ttbr0 will never changed, so we needn't call map_kernel()
|
||||
Self::new_bare()
|
||||
}
|
||||
|
||||
fn new_bare() -> Self {
|
||||
unimplemented!()
|
||||
let frame = Self::alloc_frame().map(|target| Frame::of_addr(target))
|
||||
.expect("failed to allocate frame");
|
||||
active_table().with_temporary_map(&frame, |_, table: &mut Aarch64PageTable| {
|
||||
table.zero();
|
||||
// set up recursive mapping for the table
|
||||
table[RECURSIVE_INDEX].set_frame(frame.clone(), EF::default(), MairNormal::attr_value());
|
||||
});
|
||||
InactivePageTable0 { p4_frame: frame }
|
||||
}
|
||||
|
||||
fn edit(&mut self, f: impl FnOnce(&mut Self::Active)) {
|
||||
unimplemented!()
|
||||
active_table().with_temporary_map(&ttbr_el1_read(0), |active_table, p4_table: &mut Aarch64PageTable| {
|
||||
let backup = p4_table[RECURSIVE_INDEX].clone();
|
||||
|
||||
// overwrite recursive mapping
|
||||
p4_table[RECURSIVE_INDEX].set_frame(self.p4_frame.clone(), EF::default(), MairNormal::attr_value());
|
||||
tlb_invalidate_all();
|
||||
|
||||
// execute f in the new context
|
||||
f(active_table);
|
||||
|
||||
// restore recursive mapping to original p4 table
|
||||
p4_table[RECURSIVE_INDEX] = backup;
|
||||
tlb_invalidate_all();
|
||||
});
|
||||
}
|
||||
|
||||
unsafe fn activate(&self) {
|
||||
unimplemented!()
|
||||
let old_frame = ttbr_el1_read(1);
|
||||
let new_frame = self.p4_frame.clone();
|
||||
debug!("switch TTBR1 {:?} -> {:?}", old_frame, new_frame);
|
||||
if old_frame != new_frame {
|
||||
ttbr_el1_write(1, new_frame);
|
||||
tlb_invalidate_all();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn with<T>(&self, f: impl FnOnce() -> T) -> T {
|
||||
unimplemented!()
|
||||
// Just need to switch the user TTBR
|
||||
let old_frame = ttbr_el1_read(1);
|
||||
let new_frame = self.p4_frame.clone();
|
||||
debug!("switch TTBR1 {:?} -> {:?}", old_frame, new_frame);
|
||||
if old_frame != new_frame {
|
||||
ttbr_el1_write(1, new_frame);
|
||||
tlb_invalidate_all();
|
||||
}
|
||||
let ret = f();
|
||||
debug!("switch TTBR1 {:?} -> {:?}", new_frame, old_frame);
|
||||
if old_frame != new_frame {
|
||||
ttbr_el1_write(1, old_frame);
|
||||
tlb_invalidate_all();
|
||||
flush_icache_all();
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn token(&self) -> usize {
|
||||
0
|
||||
self.p4_frame.start_address().as_u64() as usize // as TTBRx_EL1
|
||||
}
|
||||
|
||||
fn alloc_frame() -> Option<PhysAddr> {
|
||||
fn alloc_frame() -> Option<usize> {
|
||||
alloc_frame()
|
||||
}
|
||||
|
||||
fn dealloc_frame(target: PhysAddr) {
|
||||
fn dealloc_frame(target: usize) {
|
||||
dealloc_frame(target)
|
||||
}
|
||||
}
|
||||
|
||||
impl InactivePageTable0 {
|
||||
fn map_kernel(&mut self) {
|
||||
let table = unsafe { &mut *ROOT_PAGE_TABLE };
|
||||
let e0 = table[KERNEL_PML4].clone();
|
||||
assert!(!e0.is_unused());
|
||||
|
||||
self.edit(|_| {
|
||||
table[KERNEL_PML4].set_frame(Frame::containing_address(e0.addr()), EF::default(), MairNormal::attr_value());
|
||||
});
|
||||
}
|
||||
/// Activate as kernel page table (TTBR0).
|
||||
/// Used in `arch::memory::remap_the_kernel()`.
|
||||
pub unsafe fn activate_as_kernel(&self) {
|
||||
let old_frame = ttbr_el1_read(0);
|
||||
let new_frame = self.p4_frame.clone();
|
||||
debug!("switch TTBR0 {:?} -> {:?}", old_frame, new_frame);
|
||||
if old_frame != new_frame {
|
||||
ttbr_el1_write(0, new_frame);
|
||||
tlb_invalidate_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for InactivePageTable0 {
|
||||
fn drop(&mut self) {
|
||||
info!("PageTable dropping: {:?}", self);
|
||||
Self::dealloc_frame(self.p4_frame.start_address().as_u64() as usize);
|
||||
}
|
||||
}
|
||||
|
||||
struct FrameAllocatorForAarch64;
|
||||
|
||||
impl FrameAllocator<Size4KiB> for FrameAllocatorForAarch64 {
|
||||
fn alloc(&mut self) -> Option<Frame> {
|
||||
alloc_frame().map(|addr| Frame::of_addr(addr))
|
||||
}
|
||||
}
|
||||
|
||||
impl FrameDeallocator<Size4KiB> for FrameAllocatorForAarch64 {
|
||||
fn dealloc(&mut self, frame: Frame) {
|
||||
dealloc_frame(frame.start_address().as_u64() as usize);
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,19 @@
|
||||
# Constants / Macros defined in Rust code:
|
||||
# xscratch
|
||||
# xstatus
|
||||
# xepc
|
||||
# xcause
|
||||
# xtval
|
||||
# XRET
|
||||
|
||||
.macro SAVE_ALL
|
||||
# If coming from userspace, preserve the user stack pointer and load
|
||||
# the kernel stack pointer. If we came from the kernel, sscratch
|
||||
# will contain 0, and we should continue on the current stack.
|
||||
csrrw sp, 0x140, sp # sscratch
|
||||
csrrw sp, (xscratch), sp
|
||||
bnez sp, _save_context
|
||||
_restore_kernel_sp:
|
||||
csrr sp, 0x140 # sscratch
|
||||
csrr sp, (xscratch)
|
||||
# sscratch = previous-sp, sp = kernel-sp
|
||||
_save_context:
|
||||
# provide room for trap frame
|
||||
@ -45,11 +53,11 @@ _save_context:
|
||||
|
||||
# get sp, sstatus, sepc, stval, scause
|
||||
# set sscratch = 0
|
||||
csrrw s0, 0x140, x0 # sscratch
|
||||
csrr s1, 0x100 # sstatus
|
||||
csrr s2, 0x141 # sepc
|
||||
csrr s3, 0x143 # stval
|
||||
csrr s4, 0x142 # scause
|
||||
csrrw s0, (xscratch), x0
|
||||
csrr s1, (xstatus)
|
||||
csrr s2, (xepc)
|
||||
csrr s3, (xtval)
|
||||
csrr s4, (xcause)
|
||||
# store sp, sstatus, sepc, sbadvaddr, scause
|
||||
sw s0, 2*4(sp)
|
||||
sw s1, 32*4(sp)
|
||||
@ -65,11 +73,11 @@ _save_context:
|
||||
bnez s0, _restore_context # back to S-mode? (sstatus.SPP = 1)
|
||||
_save_kernel_sp:
|
||||
addi s0, sp, 36*4
|
||||
csrw 0x140, s0 # sscratch = kernel-sp
|
||||
csrw (xscratch), s0 # sscratch = kernel-sp
|
||||
_restore_context:
|
||||
# restore sstatus, sepc
|
||||
csrw 0x100, s1
|
||||
csrw 0x141, s2
|
||||
csrw (xstatus), s1
|
||||
csrw (xepc), s2
|
||||
|
||||
# restore x registers except x2 (sp)
|
||||
lw x1, 1*4(sp)
|
||||
@ -116,4 +124,4 @@ __alltraps:
|
||||
__trapret:
|
||||
RESTORE_ALL
|
||||
# return from supervisor call
|
||||
sret
|
||||
XRET
|
||||
|
@ -1,13 +1,24 @@
|
||||
use riscv::register::*;
|
||||
#[cfg(feature = "m_mode")]
|
||||
use riscv::register::{
|
||||
mstatus as xstatus,
|
||||
mstatus::Mstatus as Xstatus,
|
||||
mcause::Mcause,
|
||||
};
|
||||
#[cfg(not(feature = "m_mode"))]
|
||||
use riscv::register::{
|
||||
sstatus as xstatus,
|
||||
sstatus::Sstatus as Xstatus,
|
||||
mcause::Mcause,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[repr(C)]
|
||||
pub struct TrapFrame {
|
||||
pub x: [usize; 32], // general registers
|
||||
pub sstatus: sstatus::Sstatus, // Supervisor Status Register
|
||||
pub sstatus: Xstatus, // Supervisor Status Register
|
||||
pub sepc: usize, // Supervisor exception program counter, save the trap virtual address (here is used to save the process program entry addr?)
|
||||
pub sbadaddr: usize, // Supervisor bad address
|
||||
pub scause: scause::Scause, // scause register: record the cause of exception/interrupt/trap
|
||||
pub stval: usize, // Supervisor trap value
|
||||
pub scause: Mcause, // scause register: record the cause of exception/interrupt/trap
|
||||
}
|
||||
|
||||
/// Generate the trapframe for building new thread in kernel
|
||||
@ -28,13 +39,13 @@ impl TrapFrame {
|
||||
tf.x[10] = arg; // a0
|
||||
tf.x[2] = sp;
|
||||
tf.sepc = entry as usize;
|
||||
tf.sstatus = sstatus::read();
|
||||
// Supervisor Previous Interrupt Enable
|
||||
tf.sstatus.set_spie(true);
|
||||
// Supervisor Interrupt Disable
|
||||
tf.sstatus.set_sie(false);
|
||||
// Supervisor Previous Privilege Mode is Supervisor
|
||||
tf.sstatus.set_spp(sstatus::SPP::Supervisor);
|
||||
tf.sstatus = xstatus::read();
|
||||
tf.sstatus.set_xpie(true);
|
||||
tf.sstatus.set_xie(false);
|
||||
#[cfg(feature = "m_mode")]
|
||||
tf.sstatus.set_mpp(xstatus::MPP::Machine);
|
||||
#[cfg(not(feature = "m_mode"))]
|
||||
tf.sstatus.set_spp(xstatus::SPP::Supervisor);
|
||||
tf
|
||||
}
|
||||
|
||||
@ -52,10 +63,13 @@ impl TrapFrame {
|
||||
let mut tf: Self = unsafe { zeroed() };
|
||||
tf.x[2] = sp;
|
||||
tf.sepc = entry_addr;
|
||||
tf.sstatus = sstatus::read();
|
||||
tf.sstatus.set_spie(true);
|
||||
tf.sstatus.set_sie(false);
|
||||
tf.sstatus.set_spp(sstatus::SPP::User);
|
||||
tf.sstatus = xstatus::read();
|
||||
tf.sstatus.set_xpie(true);
|
||||
tf.sstatus.set_xie(false);
|
||||
#[cfg(feature = "m_mode")]
|
||||
tf.sstatus.set_mpp(xstatus::MPP::User);
|
||||
#[cfg(not(feature = "m_mode"))]
|
||||
tf.sstatus.set_spp(xstatus::SPP::User);
|
||||
tf
|
||||
}
|
||||
}
|
||||
@ -78,7 +92,7 @@ impl Debug for TrapFrame {
|
||||
.field("regs", &Regs(&self.x))
|
||||
.field("sstatus", &self.sstatus)
|
||||
.field("sepc", &self.sepc)
|
||||
.field("sbadaddr", &self.sbadaddr)
|
||||
.field("stval", &self.stval)
|
||||
.field("scause", &self.scause)
|
||||
.finish()
|
||||
}
|
||||
|
@ -1,6 +1,18 @@
|
||||
use riscv::register::*;
|
||||
#[cfg(feature = "m_mode")]
|
||||
use riscv::register::{
|
||||
mstatus as xstatus,
|
||||
mscratch as xscratch,
|
||||
mtvec as xtvec,
|
||||
};
|
||||
#[cfg(not(feature = "m_mode"))]
|
||||
use riscv::register::{
|
||||
sstatus as xstatus,
|
||||
sscratch as xscratch,
|
||||
stvec as xtvec,
|
||||
};
|
||||
use riscv::register::{mcause, mepc, sie};
|
||||
pub use self::context::*;
|
||||
use crate::memory::{MemorySet, InactivePageTable0, memory_set_record};
|
||||
use crate::memory::{MemorySet, InactivePageTable0};
|
||||
use log::*;
|
||||
|
||||
#[path = "context.rs"]
|
||||
@ -17,13 +29,16 @@ pub fn init() {
|
||||
unsafe {
|
||||
// Set sscratch register to 0, indicating to exception vector that we are
|
||||
// presently executing in the kernel
|
||||
sscratch::write(0);
|
||||
xscratch::write(0);
|
||||
// Set the exception vector address
|
||||
stvec::write(__alltraps as usize, stvec::TrapMode::Direct);
|
||||
xtvec::write(__alltraps as usize, xtvec::TrapMode::Direct);
|
||||
// Enable IPI
|
||||
sie::set_ssoft();
|
||||
// Enable serial interrupt
|
||||
sie::set_sext();
|
||||
// NOTE: In M-mode: mie.MSIE is set by BBL.
|
||||
// mie.MEIE can not be set in QEMU v3.0
|
||||
// (seems like a bug)
|
||||
}
|
||||
info!("interrupt: init end");
|
||||
}
|
||||
@ -34,7 +49,7 @@ pub fn init() {
|
||||
*/
|
||||
#[inline(always)]
|
||||
pub unsafe fn enable() {
|
||||
sstatus::set_sie();
|
||||
xstatus::set_xie();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -45,8 +60,8 @@ pub unsafe fn enable() {
|
||||
*/
|
||||
#[inline(always)]
|
||||
pub unsafe fn disable_and_store() -> usize {
|
||||
let e = sstatus::read().sie() as usize;
|
||||
sstatus::clear_sie();
|
||||
let e = xstatus::read().xie() as usize;
|
||||
xstatus::clear_xie();
|
||||
e
|
||||
}
|
||||
|
||||
@ -59,7 +74,7 @@ pub unsafe fn disable_and_store() -> usize {
|
||||
#[inline(always)]
|
||||
pub unsafe fn restore(flags: usize) {
|
||||
if flags != 0 {
|
||||
sstatus::set_sie();
|
||||
xstatus::set_xie();
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,9 +86,15 @@ pub unsafe fn restore(flags: usize) {
|
||||
*/
|
||||
#[no_mangle]
|
||||
pub extern fn rust_trap(tf: &mut TrapFrame) {
|
||||
use riscv::register::scause::{Trap, Interrupt as I, Exception as E};
|
||||
use self::mcause::{Trap, Interrupt as I, Exception as E};
|
||||
trace!("Interrupt @ CPU{}: {:?} ", super::cpu::id(), tf.scause.cause());
|
||||
match tf.scause.cause() {
|
||||
// M-mode only
|
||||
Trap::Interrupt(I::MachineExternal) => serial(),
|
||||
Trap::Interrupt(I::MachineSoft) => ipi(),
|
||||
Trap::Interrupt(I::MachineTimer) => timer(),
|
||||
Trap::Exception(E::MachineEnvCall) => sbi(tf),
|
||||
|
||||
Trap::Interrupt(I::SupervisorExternal) => serial(),
|
||||
Trap::Interrupt(I::SupervisorSoft) => ipi(),
|
||||
Trap::Interrupt(I::SupervisorTimer) => timer(),
|
||||
@ -87,6 +108,12 @@ pub extern fn rust_trap(tf: &mut TrapFrame) {
|
||||
trace!("Interrupt end");
|
||||
}
|
||||
|
||||
/// Call BBL SBI functions for M-mode kernel
|
||||
fn sbi(tf: &mut TrapFrame) {
|
||||
(super::BBL.mcall_trap)(tf.x.as_ptr(), tf.scause.bits(), tf.sepc);
|
||||
tf.sepc += 4;
|
||||
}
|
||||
|
||||
fn serial() {
|
||||
crate::trap::serial(super::io::getchar());
|
||||
}
|
||||
@ -101,8 +128,8 @@ fn ipi() {
|
||||
* process timer interrupt
|
||||
*/
|
||||
fn timer() {
|
||||
crate::trap::timer();
|
||||
super::timer::set_next();
|
||||
crate::trap::timer();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -124,9 +151,8 @@ fn syscall(tf: &mut TrapFrame) {
|
||||
* process IllegalInstruction exception
|
||||
*/
|
||||
fn illegal_inst(tf: &mut TrapFrame) {
|
||||
if !emulate_mul_div(tf) {
|
||||
crate::trap::error(tf);
|
||||
}
|
||||
(super::BBL.illegal_insn_trap)(tf.x.as_ptr(), tf.scause.bits(), tf.sepc);
|
||||
tf.sepc = mepc::read();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -136,73 +162,10 @@ fn illegal_inst(tf: &mut TrapFrame) {
|
||||
* process page fault exception
|
||||
*/
|
||||
fn page_fault(tf: &mut TrapFrame) {
|
||||
let addr = stval::read();
|
||||
let addr = tf.stval;
|
||||
trace!("\nEXCEPTION: Page Fault @ {:#x}", addr);
|
||||
|
||||
if !crate::memory::page_fault_handler(addr) {
|
||||
crate::trap::error(tf);
|
||||
}
|
||||
}
|
||||
|
||||
/// Migrate from riscv-pk
|
||||
/*
|
||||
* @param:
|
||||
* TrapFrame: the Trapframe for the illegal inst exception
|
||||
* @brief:
|
||||
* emulate the multiply and divide operation (if not this kind of operation return false)
|
||||
* @retval:
|
||||
* a bool indicates whether emulate the multiply and divide operation successfully
|
||||
*/
|
||||
fn emulate_mul_div(tf: &mut TrapFrame) -> bool {
|
||||
let insn = unsafe { *(tf.sepc as *const usize) };
|
||||
let rs1 = tf.x[get_reg(insn, RS1)];
|
||||
let rs2 = tf.x[get_reg(insn, RS2)];
|
||||
|
||||
let rd = if (insn & MASK_MUL) == MATCH_MUL {
|
||||
rs1 * rs2
|
||||
} else if (insn & MASK_DIV) == MATCH_DIV {
|
||||
((rs1 as i32) / (rs2 as i32)) as usize
|
||||
} else if (insn & MASK_DIVU) == MATCH_DIVU {
|
||||
rs1 / rs2
|
||||
} else if (insn & MASK_REM) == MATCH_REM {
|
||||
((rs1 as i32) % (rs2 as i32)) as usize
|
||||
} else if (insn & MASK_REMU) == MATCH_REMU {
|
||||
rs1 % rs2
|
||||
} else if (insn & MASK_MULH) == MATCH_MULH {
|
||||
(((rs1 as i32 as i64) * (rs2 as i32 as i64)) >> 32) as usize
|
||||
} else if (insn & MASK_MULHU) == MATCH_MULHU {
|
||||
(((rs1 as i64) * (rs2 as i64)) >> 32) as usize
|
||||
} else if (insn & MASK_MULHSU) == MATCH_MULHSU {
|
||||
(((rs1 as i32 as i64) * (rs2 as i64)) >> 32) as usize
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
tf.x[get_reg(insn, RD)] = rd;
|
||||
tf.sepc += 4; // jump to next instruction
|
||||
return true;
|
||||
|
||||
fn get_reg(inst: usize, offset: usize) -> usize {
|
||||
(inst >> offset) & 0x1f
|
||||
}
|
||||
|
||||
const RS1: usize = 15;
|
||||
const RS2: usize = 20;
|
||||
const RD: usize = 7;
|
||||
|
||||
const MATCH_MUL: usize = 0x2000033;
|
||||
const MASK_MUL: usize = 0xfe00707f;
|
||||
const MATCH_MULH: usize = 0x2001033;
|
||||
const MASK_MULH: usize = 0xfe00707f;
|
||||
const MATCH_MULHSU: usize = 0x2002033;
|
||||
const MASK_MULHSU: usize = 0xfe00707f;
|
||||
const MATCH_MULHU: usize = 0x2003033;
|
||||
const MASK_MULHU: usize = 0xfe00707f;
|
||||
const MATCH_DIV: usize = 0x2004033;
|
||||
const MASK_DIV: usize = 0xfe00707f;
|
||||
const MATCH_DIVU: usize = 0x2005033;
|
||||
const MASK_DIVU: usize = 0xfe00707f;
|
||||
const MATCH_REM: usize = 0x2006033;
|
||||
const MASK_REM: usize = 0xfe00707f;
|
||||
const MATCH_REMU: usize = 0x2007033;
|
||||
const MASK_REMU: usize = 0xfe00707f;
|
||||
}
|
@ -20,23 +20,29 @@ impl Write for SerialPort {
|
||||
}
|
||||
|
||||
fn putchar(c: u8) {
|
||||
#[cfg(feature = "no_bbl")]
|
||||
unsafe {
|
||||
while read_volatile(STATUS) & CAN_WRITE == 0 {}
|
||||
write_volatile(DATA, c as u8);
|
||||
if cfg!(feature = "no_bbl") {
|
||||
unsafe {
|
||||
while read_volatile(STATUS) & CAN_WRITE == 0 {}
|
||||
write_volatile(DATA, c as u8);
|
||||
}
|
||||
} else if cfg!(feature = "m_mode") {
|
||||
(super::BBL.mcall_console_putchar)(c);
|
||||
} else {
|
||||
sbi::console_putchar(c as usize);
|
||||
}
|
||||
#[cfg(not(feature = "no_bbl"))]
|
||||
sbi::console_putchar(c as usize);
|
||||
}
|
||||
|
||||
pub fn getchar() -> char {
|
||||
#[cfg(feature = "no_bbl")]
|
||||
let c = unsafe {
|
||||
while read_volatile(STATUS) & CAN_READ == 0 {}
|
||||
read_volatile(DATA)
|
||||
let c = if cfg!(feature = "no_bbl") {
|
||||
unsafe {
|
||||
// while read_volatile(STATUS) & CAN_READ == 0 {}
|
||||
read_volatile(DATA)
|
||||
}
|
||||
} else if cfg!(feature = "m_mode") {
|
||||
(super::BBL.mcall_console_getchar)() as u8
|
||||
} else {
|
||||
sbi::console_getchar() as u8
|
||||
};
|
||||
#[cfg(not(feature = "no_bbl"))]
|
||||
let c = sbi::console_getchar() as u8;
|
||||
|
||||
match c {
|
||||
255 => '\0', // null
|
||||
|
@ -2,12 +2,24 @@ use core::{slice, mem};
|
||||
use riscv::{addr::*, register::sstatus};
|
||||
use ucore_memory::PAGE_SIZE;
|
||||
use log::*;
|
||||
use crate::memory::{active_table, FRAME_ALLOCATOR, init_heap, MemoryArea, MemoryAttr, MemorySet};
|
||||
use crate::memory::{active_table, FRAME_ALLOCATOR, init_heap, MemoryArea, MemoryAttr, MemorySet, MEMORY_ALLOCATOR};
|
||||
use crate::consts::{MEMORY_OFFSET, MEMORY_END};
|
||||
|
||||
#[cfg(feature = "no_mmu")]
|
||||
pub fn init() {
|
||||
init_heap();
|
||||
|
||||
let heap_bottom = end as usize;
|
||||
let heap_size = MEMORY_END - heap_bottom;
|
||||
unsafe { MEMORY_ALLOCATOR.lock().init(heap_bottom, heap_size); }
|
||||
info!("available memory: [{:#x}, {:#x})", heap_bottom, MEMORY_END);
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief:
|
||||
* Init the mermory management module, allow memory access and set up page table and init heap and frame allocator
|
||||
*/
|
||||
#[cfg(not(feature = "no_mmu"))]
|
||||
pub fn init() {
|
||||
#[repr(align(4096))] // align the PageData struct to 4096 bytes
|
||||
struct PageData([u8; PAGE_SIZE]);
|
||||
@ -37,7 +49,6 @@ pub fn init_other() {
|
||||
fn init_frame_allocator() {
|
||||
use bit_allocator::BitAlloc;
|
||||
use core::ops::Range;
|
||||
use crate::consts::{MEMORY_OFFSET, MEMORY_END};
|
||||
|
||||
let mut ba = FRAME_ALLOCATOR.lock();
|
||||
ba.insert(to_range(end as usize + PAGE_SIZE, MEMORY_END));
|
||||
@ -63,6 +74,7 @@ fn init_frame_allocator() {
|
||||
* @brief:
|
||||
* remmap the kernel memory address with 4K page recorded in p1 page table
|
||||
*/
|
||||
#[cfg(not(feature = "no_mmu"))]
|
||||
fn remap_the_kernel() {
|
||||
let mut ms = MemorySet::new_bare();
|
||||
#[cfg(feature = "no_bbl")]
|
||||
|
@ -8,9 +8,10 @@ pub mod consts;
|
||||
pub mod cpu;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn rust_main(hartid: usize, dtb: usize, hart_mask: usize) -> ! {
|
||||
pub extern fn rust_main(hartid: usize, dtb: usize, hart_mask: usize, functions: usize) -> ! {
|
||||
unsafe { cpu::set_cpu_id(hartid); }
|
||||
println!("Hello RISCV! in hart {}, {}, {}", hartid, dtb, hart_mask);
|
||||
unsafe { BBL_FUNCTIONS_PTR = functions as *const _; }
|
||||
println!("Hello RISCV! in hart {}, dtb @ {:#x}, functions @ {:#x}", hartid, dtb, functions);
|
||||
|
||||
if hartid != 0 {
|
||||
while unsafe { !cpu::has_started(hartid) } { }
|
||||
@ -35,7 +36,46 @@ fn others_main() -> ! {
|
||||
crate::kmain();
|
||||
}
|
||||
|
||||
|
||||
/// Constant & Macro for `trap.asm`
|
||||
#[cfg(feature = "m_mode")]
|
||||
global_asm!("
|
||||
.equ xstatus, 0x300
|
||||
.equ xscratch, 0x340
|
||||
.equ xepc, 0x341
|
||||
.equ xcause, 0x342
|
||||
.equ xtval, 0x343
|
||||
.macro XRET\n mret\n .endm
|
||||
");
|
||||
#[cfg(not(feature = "m_mode"))]
|
||||
global_asm!("
|
||||
.equ xstatus, 0x100
|
||||
.equ xscratch, 0x140
|
||||
.equ xepc, 0x141
|
||||
.equ xcause, 0x142
|
||||
.equ xtval, 0x143
|
||||
.macro XRET\n sret\n .endm
|
||||
");
|
||||
|
||||
#[cfg(feature = "no_bbl")]
|
||||
global_asm!(include_str!("boot/boot.asm"));
|
||||
global_asm!(include_str!("boot/entry.asm"));
|
||||
global_asm!(include_str!("boot/trap.asm"));
|
||||
global_asm!(include_str!("boot/trap.asm"));
|
||||
|
||||
|
||||
/// Some symbols passed from BBL.
|
||||
/// Used in M-mode kernel.
|
||||
#[repr(C)]
|
||||
struct BBLFunctions {
|
||||
mcall_trap: BBLTrapHandler,
|
||||
illegal_insn_trap: BBLTrapHandler,
|
||||
mcall_console_putchar: extern fn(u8),
|
||||
mcall_console_getchar: extern fn() -> usize,
|
||||
}
|
||||
|
||||
type BBLTrapHandler = extern fn(regs: *const usize, mcause: usize, mepc: usize);
|
||||
static mut BBL_FUNCTIONS_PTR: *const BBLFunctions = ::core::ptr::null();
|
||||
use lazy_static::lazy_static;
|
||||
lazy_static! {
|
||||
static ref BBL: BBLFunctions = unsafe { BBL_FUNCTIONS_PTR.read() };
|
||||
}
|
@ -161,10 +161,6 @@ impl ActivePageTable {
|
||||
// Unmap the page
|
||||
self.unmap(0xcafebabe);
|
||||
}
|
||||
|
||||
pub fn token() -> usize {
|
||||
satp::read().frame().number() | (1 << 31)
|
||||
}
|
||||
}
|
||||
/// implementation for the Entry trait in /crate/memory/src/paging/mod.rs
|
||||
impl Entry for PageEntry {
|
||||
@ -200,6 +196,8 @@ impl Entry for PageEntry {
|
||||
fn set_user(&mut self, value: bool) { self.as_flags().set(EF::USER, value); }
|
||||
fn execute(&self) -> bool { self.0.flags().contains(EF::EXECUTABLE) }
|
||||
fn set_execute(&mut self, value: bool) { self.as_flags().set(EF::EXECUTABLE, value); }
|
||||
fn mmio(&self) -> bool { unimplemented!() }
|
||||
fn set_mmio(&mut self, value: bool) { unimplemented!() }
|
||||
}
|
||||
|
||||
impl PageEntry {
|
||||
|
@ -33,6 +33,9 @@ pub fn get_cycle() -> u64 {
|
||||
*/
|
||||
pub fn init() {
|
||||
// Enable supervisor timer interrupt
|
||||
#[cfg(feature = "m_mode")]
|
||||
unsafe { mie::set_mtimer(); }
|
||||
#[cfg(not(feature = "m_mode"))]
|
||||
unsafe { sie::set_stimer(); }
|
||||
|
||||
set_next();
|
||||
|
@ -1,15 +1,10 @@
|
||||
use core::ptr::Unique;
|
||||
use core::fmt;
|
||||
use spin::Mutex;
|
||||
use volatile::Volatile;
|
||||
use lazy_static::lazy_static;
|
||||
use x86_64::instructions::port::Port;
|
||||
use crate::logging::Color;
|
||||
use crate::consts::KERNEL_OFFSET;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
pub const VGA_BUFFER: Unique<VgaBuffer> = unsafe {
|
||||
Unique::new_unchecked((KERNEL_OFFSET + 0xb8000) as *mut _)
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct ColorCode(u8);
|
||||
@ -73,8 +68,7 @@ impl VgaBuffer {
|
||||
|
||||
lazy_static! {
|
||||
pub static ref VGA_WRITER: Mutex<VgaWriter> = Mutex::new(
|
||||
// It is the only user of VGA_BUFFER. So it's safe.
|
||||
VgaWriter::new(unsafe{ &mut *VGA_BUFFER.as_ptr() })
|
||||
VgaWriter::new(unsafe{ &mut *((KERNEL_OFFSET + 0xb8000) as *mut VgaBuffer) })
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -96,9 +96,6 @@ impl ActivePageTable {
|
||||
// Unmap the page
|
||||
self.unmap(0xcafebabe);
|
||||
}
|
||||
pub fn token() -> usize {
|
||||
Cr3::read().0.start_address().as_u64() as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl Entry for PageEntry {
|
||||
@ -145,6 +142,8 @@ impl Entry for PageEntry {
|
||||
}
|
||||
fn execute(&self) -> bool { !self.0.flags().contains(EF::NO_EXECUTE) }
|
||||
fn set_execute(&mut self, value: bool) { self.as_flags().set(EF::NO_EXECUTE, !value); }
|
||||
fn mmio(&self) -> bool { unimplemented!() }
|
||||
fn set_mmio(&mut self, value: bool) { unimplemented!() }
|
||||
}
|
||||
|
||||
fn get_entry_ptr(addr: usize, level: u8) -> *mut PageEntry {
|
||||
@ -271,4 +270,4 @@ impl FrameDeallocator<Size4KiB> for FrameAllocatorForX86 {
|
||||
fn dealloc(&mut self, frame: Frame) {
|
||||
dealloc_frame(frame.start_address().as_u64() as usize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,6 @@ use alloc::{boxed::Box, sync::Arc, string::String, collections::VecDeque, vec::V
|
||||
use core::any::Any;
|
||||
use core::slice;
|
||||
use lazy_static::lazy_static;
|
||||
use crate::memory::{MemorySet, InactivePageTable0, memory_set_record};
|
||||
use crate::process::context::memory_set_map_swappable;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::arch::driver::ide;
|
||||
use crate::sync::Condvar;
|
||||
@ -77,6 +75,15 @@ impl Stdin {
|
||||
self.pushed.notify_one();
|
||||
}
|
||||
pub fn pop(&self) -> char {
|
||||
// QEMU v3.0 don't support M-mode external interrupt (bug?)
|
||||
// So we have to use polling.
|
||||
// TODO: serial interrupt on aarch64
|
||||
#[cfg(any(feature = "m_mode", target_arch = "aarch64"))]
|
||||
loop {
|
||||
let c = crate::arch::io::getchar();
|
||||
if c != '\0' { return c; }
|
||||
}
|
||||
#[cfg(not(any(feature = "m_mode", target_arch = "aarch64")))]
|
||||
loop {
|
||||
let ret = self.buf.lock().pop_front();
|
||||
match ret {
|
||||
|
@ -1,16 +1,10 @@
|
||||
#![feature(ptr_internals)]
|
||||
#![feature(lang_items)]
|
||||
#![feature(const_fn)]
|
||||
#![feature(alloc)]
|
||||
#![feature(naked_functions)]
|
||||
#![feature(asm)]
|
||||
#![feature(optin_builtin_traits)]
|
||||
#![feature(panic_info_message)]
|
||||
#![feature(global_asm)]
|
||||
#![feature(compiler_builtins_lib)]
|
||||
#![feature(raw)]
|
||||
#![feature(vec_resize_default)]
|
||||
#![feature(extern_crate_item_prelude)]
|
||||
#![no_std]
|
||||
|
||||
// just keep it ...
|
||||
@ -48,12 +42,6 @@ pub mod arch;
|
||||
|
||||
pub fn kmain() -> ! {
|
||||
processor().run();
|
||||
|
||||
// thread::test::local_key();
|
||||
// thread::test::unpack();
|
||||
// sync::test::philosopher_using_mutex();
|
||||
// sync::test::philosopher_using_monitor();
|
||||
// sync::mpsc::test::test_all();
|
||||
}
|
||||
|
||||
/// Global heap allocator
|
||||
|
@ -1,6 +1,6 @@
|
||||
use core::fmt;
|
||||
use log::{self, Level, LevelFilter, Log, Metadata, Record};
|
||||
use spin::Mutex;
|
||||
use crate::sync::SpinNoIrqLock as Mutex;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
lazy_static! {
|
||||
|
@ -1,19 +1,23 @@
|
||||
pub use crate::arch::paging::*;
|
||||
use bit_allocator::{BitAlloc, BitAlloc4K, BitAlloc64K};
|
||||
use bit_allocator::{BitAlloc, BitAlloc4K, BitAlloc64K, BitAlloc1M};
|
||||
use crate::consts::MEMORY_OFFSET;
|
||||
use super::HEAP_ALLOCATOR;
|
||||
use ucore_memory::{*, paging::PageTable};
|
||||
use ucore_memory::cow::CowExt;
|
||||
pub use ucore_memory::memory_set::{MemoryArea, MemoryAttr, MemorySet as MemorySet_, InactivePageTable};
|
||||
pub use ucore_memory::memory_set::{MemoryArea, MemoryAttr, InactivePageTable};
|
||||
use ucore_memory::swap::*;
|
||||
use crate::process::{process};
|
||||
use crate::sync::{SpinNoIrqLock, SpinNoIrq, MutexGuard};
|
||||
use alloc::collections::VecDeque;
|
||||
use lazy_static::*;
|
||||
use log::*;
|
||||
use linked_list_allocator::LockedHeap;
|
||||
|
||||
#[cfg(not(feature = "no_mmu"))]
|
||||
pub type MemorySet = ucore_memory::memory_set::MemorySet<InactivePageTable0>;
|
||||
|
||||
pub type MemorySet = MemorySet_<InactivePageTable0>;
|
||||
#[cfg(feature = "no_mmu")]
|
||||
pub type MemorySet = ucore_memory::no_mmu::MemorySet<NoMMUSupportImpl>;
|
||||
|
||||
// x86_64 support up to 256M memory
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@ -25,20 +29,11 @@ pub type FrameAlloc = BitAlloc4K;
|
||||
|
||||
// Raspberry Pi 3 has 1G memory
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
pub type FrameAlloc = BitAlloc64K;
|
||||
pub type FrameAlloc = BitAlloc1M;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref FRAME_ALLOCATOR: SpinNoIrqLock<FrameAlloc> = SpinNoIrqLock::new(FrameAlloc::default());
|
||||
}
|
||||
// record the user memory set for pagefault function (swap in/out and frame delayed allocate) temporarily when page fault in new_user() or fork() function
|
||||
// after the process is set we can use use crate::processor() to get the inactive page table
|
||||
lazy_static! {
|
||||
pub static ref MEMORY_SET_RECORD: SpinNoIrqLock<VecDeque<usize>> = SpinNoIrqLock::new(VecDeque::default());
|
||||
}
|
||||
|
||||
pub fn memory_set_record() -> MutexGuard<'static, VecDeque<usize>, SpinNoIrq> {
|
||||
MEMORY_SET_RECORD.lock()
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref ACTIVE_TABLE: SpinNoIrqLock<CowExt<ActivePageTable>> = SpinNoIrqLock::new(unsafe {
|
||||
@ -110,48 +105,25 @@ impl Drop for KernelStack {
|
||||
* @retval:
|
||||
* Return true to continue, false to halt
|
||||
*/
|
||||
#[cfg(not(feature = "no_mmu"))]
|
||||
pub fn page_fault_handler(addr: usize) -> bool {
|
||||
info!("start handling swap in/out page fault");
|
||||
//unsafe { ACTIVE_TABLE_SWAP.force_unlock(); }
|
||||
|
||||
info!("active page table token in pg fault is {:x?}", ActivePageTable::token());
|
||||
let mmset_record = memory_set_record();
|
||||
let id = mmset_record.iter()
|
||||
.position(|x| unsafe{(*(x.clone() as *mut MemorySet)).get_page_table_mut().token() == ActivePageTable::token()});
|
||||
/*LAB3 EXERCISE 1: YOUR STUDENT NUMBER
|
||||
* handle the frame deallocated
|
||||
*/
|
||||
match id {
|
||||
Some(targetid) => {
|
||||
info!("get id from memroy set recorder.");
|
||||
let mmset_ptr = mmset_record.get(targetid).expect("fail to get mmset_ptr").clone();
|
||||
// get current mmset
|
||||
|
||||
let current_mmset = unsafe{&mut *(mmset_ptr as *mut MemorySet)};
|
||||
//check whether the vma is legal
|
||||
if current_mmset.find_area(addr).is_none(){
|
||||
return false;
|
||||
}
|
||||
info!("get pt from processor()");
|
||||
if process().memory_set.find_area(addr).is_none(){
|
||||
return false;
|
||||
}
|
||||
|
||||
let pt = current_mmset.get_page_table_mut();
|
||||
info!("pt got!");
|
||||
if active_table_swap().page_fault_handler(pt as *mut InactivePageTable0, addr, false, || alloc_frame().expect("fail to alloc frame")){
|
||||
return true;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
info!("get pt from processor()");
|
||||
if process().get_memory_set_mut().find_area(addr).is_none(){
|
||||
return false;
|
||||
}
|
||||
|
||||
let pt = process().get_memory_set_mut().get_page_table_mut();
|
||||
info!("pt got");
|
||||
if active_table_swap().page_fault_handler(pt as *mut InactivePageTable0, addr, true, || alloc_frame().expect("fail to alloc frame")){
|
||||
return true;
|
||||
}
|
||||
},
|
||||
};
|
||||
let pt = process().memory_set.get_page_table_mut();
|
||||
info!("pt got");
|
||||
if active_table_swap().page_fault_handler(pt as *mut InactivePageTable0, addr, true, || alloc_frame().expect("fail to alloc frame")){
|
||||
return true;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@ -172,10 +144,29 @@ pub fn init_heap() {
|
||||
info!("heap init end");
|
||||
}
|
||||
|
||||
/// Allocator for the rest memory space on NO-MMU case.
|
||||
pub static MEMORY_ALLOCATOR: LockedHeap = LockedHeap::empty();
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct NoMMUSupportImpl;
|
||||
|
||||
impl ucore_memory::no_mmu::NoMMUSupport for NoMMUSupportImpl {
|
||||
type Alloc = LockedHeap;
|
||||
fn allocator() -> &'static Self::Alloc {
|
||||
&MEMORY_ALLOCATOR
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "no_mmu")]
|
||||
pub fn page_fault_handler(_addr: usize) -> bool {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
|
||||
//pub mod test {
|
||||
// pub fn cow() {
|
||||
// use super::*;
|
||||
// use ucore_memory::cow::test::test_with;
|
||||
// test_with(&mut active_table());
|
||||
// }
|
||||
//}
|
||||
//}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::arch::interrupt::{TrapFrame, Context as ArchContext};
|
||||
use crate::memory::{MemoryArea, MemoryAttr, MemorySet, KernelStack, active_table_swap, alloc_frame, InactivePageTable0, memory_set_record};
|
||||
use xmas_elf::{ElfFile, header, program::{Flags, ProgramHeader, Type}};
|
||||
use crate::memory::{MemoryArea, MemoryAttr, MemorySet, KernelStack, active_table_swap, alloc_frame, InactivePageTable0};
|
||||
use xmas_elf::{ElfFile, header, program::{Flags, ProgramHeader, Type, SegmentData}};
|
||||
use core::fmt::{Debug, Error, Formatter};
|
||||
use alloc::{boxed::Box, collections::BTreeMap, vec::Vec, sync::Arc, string::String};
|
||||
use ucore_memory::{Page};
|
||||
@ -51,29 +51,7 @@ impl ContextImpl {
|
||||
})
|
||||
}
|
||||
|
||||
/// Temp for aarch64
|
||||
pub fn new_user_test(entry: extern fn(usize) -> !) -> Box<Context> {
|
||||
let memory_set = MemorySet::new();
|
||||
let kstack = KernelStack::new();
|
||||
let ustack = KernelStack::new();
|
||||
Box::new(ContextImpl {
|
||||
arch: unsafe { ArchContext::new_user_thread(entry as usize, ustack.top(), kstack.top(), false, memory_set.token()) },
|
||||
memory_set,
|
||||
kstack,
|
||||
files: BTreeMap::default(),
|
||||
cwd: String::new(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Make a new user thread from ELF data
|
||||
/*
|
||||
* @param:
|
||||
* data: the ELF data stream
|
||||
* @brief:
|
||||
* make a new thread from ELF data
|
||||
* @retval:
|
||||
* the new user thread Context
|
||||
*/
|
||||
pub fn new_user<'a, Iter>(data: &[u8], args: Iter) -> Box<ContextImpl>
|
||||
where Iter: Iterator<Item=&'a str>
|
||||
{
|
||||
@ -83,61 +61,45 @@ impl ContextImpl {
|
||||
header::HeaderPt2::Header32(_) => true,
|
||||
header::HeaderPt2::Header64(_) => false,
|
||||
};
|
||||
assert_eq!(elf.header.pt2.type_().as_type(), header::Type::Executable, "ELF is not executable");
|
||||
|
||||
match elf.header.pt2.type_().as_type() {
|
||||
header::Type::Executable => {
|
||||
// #[cfg(feature = "no_mmu")]
|
||||
// panic!("ELF is not shared object");
|
||||
},
|
||||
header::Type::SharedObject => {},
|
||||
_ => panic!("ELF is not executable or shared object"),
|
||||
}
|
||||
|
||||
// Make page table
|
||||
let (mut memory_set, entry_addr) = memory_set_from(&elf);
|
||||
|
||||
// User stack
|
||||
use crate::consts::{USER_STACK_OFFSET, USER_STACK_SIZE, USER32_STACK_OFFSET};
|
||||
let (ustack_buttom, mut ustack_top) = match is32 {
|
||||
true => (USER32_STACK_OFFSET, USER32_STACK_OFFSET + USER_STACK_SIZE),
|
||||
false => (USER_STACK_OFFSET, USER_STACK_OFFSET + USER_STACK_SIZE),
|
||||
#[cfg(not(feature = "no_mmu"))]
|
||||
let mut ustack_top = {
|
||||
let (ustack_buttom, ustack_top) = match is32 {
|
||||
true => (USER32_STACK_OFFSET, USER32_STACK_OFFSET + USER_STACK_SIZE),
|
||||
false => (USER_STACK_OFFSET, USER_STACK_OFFSET + USER_STACK_SIZE),
|
||||
};
|
||||
memory_set.push(MemoryArea::new(ustack_buttom, ustack_top, MemoryAttr::default().user(), "user_stack"));
|
||||
ustack_top
|
||||
};
|
||||
#[cfg(feature = "no_mmu")]
|
||||
let mut ustack_top = memory_set.push(USER_STACK_SIZE).as_ptr() as usize + USER_STACK_SIZE;
|
||||
|
||||
// Make page table
|
||||
let mut memory_set = memory_set_from(&elf);
|
||||
unsafe {
|
||||
memory_set.with(|| { ustack_top = push_args_at_stack(args, ustack_top) });
|
||||
}
|
||||
|
||||
// add the new memory set to the recorder
|
||||
let mmset_ptr = ((&mut memory_set) as * mut MemorySet) as usize;
|
||||
memory_set_record().push_back(mmset_ptr);
|
||||
//let id = memory_set_record().iter()
|
||||
// .position(|x| unsafe { info!("current memory set record include {:x?}, {:x?}", x, (*(x.clone() as *mut MemorySet)).get_page_table_mut().token()); false });
|
||||
|
||||
memory_set.push(MemoryArea::new(ustack_buttom, ustack_top, MemoryAttr::default().user(), "user_stack"));
|
||||
trace!("{:#x?}", memory_set);
|
||||
|
||||
let entry_addr = elf.header.pt2.entry_point() as usize;
|
||||
|
||||
// Temporary switch to it, in order to copy data
|
||||
unsafe {
|
||||
memory_set.with(|| {
|
||||
for ph in elf.program_iter() {
|
||||
if ph.get_type() != Ok(Type::Load) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let virt_addr = ph.virtual_addr() as usize;
|
||||
let offset = ph.offset() as usize;
|
||||
let file_size = ph.file_size() as usize;
|
||||
let mem_size = ph.mem_size() as usize;
|
||||
|
||||
let target = unsafe { ::core::slice::from_raw_parts_mut(virt_addr as *mut u8, mem_size) };
|
||||
if file_size != 0 {
|
||||
target[..file_size].copy_from_slice(&data[offset..offset + file_size]);
|
||||
}
|
||||
target[file_size..].iter_mut().for_each(|x| *x = 0);
|
||||
}
|
||||
ustack_top = push_args_at_stack(args, ustack_top);
|
||||
});
|
||||
}
|
||||
|
||||
let kstack = KernelStack::new();
|
||||
{
|
||||
let mut mmset_record = memory_set_record();
|
||||
let id = mmset_record.iter()
|
||||
.position(|x| x.clone() == mmset_ptr).expect("id not exist");
|
||||
mmset_record.remove(id);
|
||||
}
|
||||
|
||||
let mut ret = Box::new(ContextImpl {
|
||||
//set the user Memory pages in the memory set swappable
|
||||
memory_set_map_swappable(&mut memory_set);
|
||||
|
||||
Box::new(ContextImpl {
|
||||
arch: unsafe {
|
||||
ArchContext::new_user_thread(
|
||||
entry_addr, ustack_top, kstack.top(), is32, memory_set.token())
|
||||
@ -146,10 +108,7 @@ impl ContextImpl {
|
||||
kstack,
|
||||
files: BTreeMap::default(),
|
||||
cwd: String::new(),
|
||||
});
|
||||
//set the user Memory pages in the memory set swappable
|
||||
memory_set_map_swappable(ret.get_memory_set_mut());
|
||||
ret
|
||||
})
|
||||
}
|
||||
|
||||
/// Fork
|
||||
@ -158,61 +117,36 @@ impl ContextImpl {
|
||||
// Clone memory set, make a new page table
|
||||
let mut memory_set = self.memory_set.clone();
|
||||
info!("finish mmset clone in fork!");
|
||||
// add the new memory set to the recorder
|
||||
info!("fork! new page table token: {:x?}", memory_set.token());
|
||||
let mmset_ptr = ((&mut memory_set) as * mut MemorySet) as usize;
|
||||
memory_set_record().push_back(mmset_ptr);
|
||||
|
||||
info!("before copy data to temp space");
|
||||
// Copy data to temp space
|
||||
use alloc::vec::Vec;
|
||||
let datas: Vec<Vec<u8>> = memory_set.iter().map(|area| {
|
||||
Vec::from(unsafe { area.as_slice() })
|
||||
}).collect();
|
||||
|
||||
info!("Finish copy data to temp space.");
|
||||
|
||||
// Temporarily switch to it, in order to copy data
|
||||
unsafe {
|
||||
memory_set.with(|| {
|
||||
for (area, data) in memory_set.iter().zip(datas.iter()) {
|
||||
area.as_slice_mut().copy_from_slice(data.as_slice())
|
||||
}
|
||||
});
|
||||
// MMU: copy data to the new space
|
||||
// NoMMU: coping data has been done in `memory_set.clone()`
|
||||
#[cfg(not(feature = "no_mmu"))]
|
||||
for area in memory_set.iter() {
|
||||
let data = Vec::<u8>::from(unsafe { area.as_slice() });
|
||||
unsafe { memory_set.with(|| {
|
||||
area.as_slice_mut().copy_from_slice(data.as_slice())
|
||||
}) }
|
||||
}
|
||||
|
||||
info!("temporary copy data!");
|
||||
let kstack = KernelStack::new();
|
||||
|
||||
// remove the raw pointer for the memory set in memory_set_record
|
||||
{
|
||||
let mut mmset_record = memory_set_record();
|
||||
let id = mmset_record.iter()
|
||||
.position(|x| x.clone() == mmset_ptr).expect("id not exist");
|
||||
mmset_record.remove(id);
|
||||
}
|
||||
memory_set_map_swappable(&mut memory_set);
|
||||
info!("FORK() finsihed!");
|
||||
|
||||
|
||||
let mut ret = Box::new(ContextImpl {
|
||||
Box::new(ContextImpl {
|
||||
arch: unsafe { ArchContext::new_fork(tf, kstack.top(), memory_set.token()) },
|
||||
memory_set,
|
||||
kstack,
|
||||
files: BTreeMap::default(),
|
||||
cwd: String::new(),
|
||||
});
|
||||
|
||||
memory_set_map_swappable(ret.get_memory_set_mut());
|
||||
info!("FORK() finsihed!");
|
||||
ret
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_memory_set_mut(&mut self) -> &mut MemorySet {
|
||||
&mut self.memory_set
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Drop for ContextImpl{
|
||||
#[cfg(not(feature = "no_mmu"))]
|
||||
#[cfg(not(target_arch = "aarch64"))]
|
||||
impl Drop for ContextImpl {
|
||||
fn drop(&mut self){
|
||||
info!("come in to drop for ContextImpl");
|
||||
//set the user Memory pages in the memory set unswappable
|
||||
@ -265,29 +199,48 @@ unsafe fn push_args_at_stack<'a, Iter>(args: Iter, stack_top: usize) -> usize
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* @param:
|
||||
* elf: the source ELF file
|
||||
* @brief:
|
||||
* generate a memory set according to the elf file
|
||||
* @retval:
|
||||
* the new memory set
|
||||
*/
|
||||
fn memory_set_from<'a>(elf: &'a ElfFile<'a>) -> MemorySet {
|
||||
/// Generate a MemorySet according to the ELF file.
|
||||
/// Also return the real entry point address.
|
||||
fn memory_set_from<'a>(elf: &'a ElfFile<'a>) -> (MemorySet, usize) {
|
||||
debug!("come in to memory_set_from");
|
||||
let mut set = MemorySet::new();
|
||||
let mut ms = MemorySet::new();
|
||||
let mut entry = None;
|
||||
for ph in elf.program_iter() {
|
||||
if ph.get_type() != Ok(Type::Load) {
|
||||
continue;
|
||||
}
|
||||
let (virt_addr, mem_size, flags) = match ph {
|
||||
ProgramHeader::Ph32(ph) => (ph.virtual_addr as usize, ph.mem_size as usize, ph.flags),
|
||||
ProgramHeader::Ph64(ph) => (ph.virtual_addr as usize, ph.mem_size as usize, ph.flags),
|
||||
};
|
||||
set.push(MemoryArea::new(virt_addr, virt_addr + mem_size, memory_attr_from(flags), ""));
|
||||
let virt_addr = ph.virtual_addr() as usize;
|
||||
let offset = ph.offset() as usize;
|
||||
let file_size = ph.file_size() as usize;
|
||||
let mem_size = ph.mem_size() as usize;
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
assert_eq!((virt_addr >> 48), 0xffff, "Segment Fault");
|
||||
|
||||
// Get target slice
|
||||
#[cfg(feature = "no_mmu")]
|
||||
let target = ms.push(mem_size);
|
||||
#[cfg(not(feature = "no_mmu"))]
|
||||
let target = {
|
||||
ms.push(MemoryArea::new(virt_addr, virt_addr + mem_size, memory_attr_from(ph.flags()), ""));
|
||||
unsafe { ::core::slice::from_raw_parts_mut(virt_addr as *mut u8, mem_size) }
|
||||
};
|
||||
// Copy data
|
||||
unsafe {
|
||||
ms.with(|| {
|
||||
if file_size != 0 {
|
||||
target[..file_size].copy_from_slice(&elf.input[offset..offset + file_size]);
|
||||
}
|
||||
target[file_size..].iter_mut().for_each(|x| *x = 0);
|
||||
});
|
||||
}
|
||||
// Find real entry point
|
||||
if ph.flags().is_execute() {
|
||||
let origin_entry = elf.header.pt2.entry_point() as usize;
|
||||
entry = Some(origin_entry - virt_addr + target.as_ptr() as usize);
|
||||
}
|
||||
}
|
||||
set
|
||||
(ms, entry.unwrap())
|
||||
}
|
||||
|
||||
fn memory_attr_from(elf_flags: Flags) -> MemoryAttr {
|
||||
@ -303,7 +256,8 @@ fn memory_attr_from(elf_flags: Flags) -> MemoryAttr {
|
||||
* @brief:
|
||||
* map the memory area in the memory_set swappalbe, specially for the user process
|
||||
*/
|
||||
pub fn memory_set_map_swappable(memory_set: &mut MemorySet){
|
||||
#[cfg(not(any(feature = "no_mmu", target_arch = "aarch64")))]
|
||||
pub fn memory_set_map_swappable(memory_set: &mut MemorySet) {
|
||||
info!("COME INTO memory set map swappable!");
|
||||
let pt = unsafe {
|
||||
memory_set.get_page_table_mut() as *mut InactivePageTable0
|
||||
@ -317,3 +271,8 @@ pub fn memory_set_map_swappable(memory_set: &mut MemorySet){
|
||||
info!("Finishing setting pages swappable");
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "no_mmu", target_arch = "aarch64"))]
|
||||
pub fn memory_set_map_swappable(memory_set: &mut MemorySet) {
|
||||
// FIXME: Page Fault on aarch64
|
||||
// NOTE: This function may disappear after refactor memory crate
|
||||
}
|
@ -27,7 +27,6 @@ pub fn init() {
|
||||
for i in 0..4 {
|
||||
manager.add(ContextImpl::new_kernel(idle, i), 0);
|
||||
}
|
||||
#[cfg(not(target_arch = "aarch64"))]
|
||||
crate::shell::run_user_shell();
|
||||
|
||||
info!("process init end");
|
||||
|
1
riscv-pk
Submodule
1
riscv-pk
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 71b2addd7ada2f07ca8e6f02787d706afa4fbe66
|
11
riscv-pk/.gitignore
vendored
11
riscv-pk/.gitignore
vendored
@ -1,11 +0,0 @@
|
||||
#=========================================================================
|
||||
# Git Ignore Files
|
||||
#=========================================================================
|
||||
# We explicitly ignore a default build directory and the autoconf
|
||||
# generated autom4te.cache directory. This makes it easy to do a clean
|
||||
# build under the source directory and still have it appear clean to git.
|
||||
|
||||
build/
|
||||
autom4te.cache/
|
||||
*.swp
|
||||
*~
|
@ -1,24 +0,0 @@
|
||||
Copyright (c) 2013, The Regents of the University of California (Regents).
|
||||
All Rights Reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the Regents nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
|
||||
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
|
||||
OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS
|
||||
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
|
||||
HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
|
||||
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
@ -1,455 +0,0 @@
|
||||
#=========================================================================
|
||||
# Toplevel Makefile for the Modular C++ Build System
|
||||
#=========================================================================
|
||||
# Please read the documenation in 'mcppbs-doc.txt' for more details on
|
||||
# how the Modular C++ Build System works. For most projects, a developer
|
||||
# will not need to make any changes to this makefile. The key targets
|
||||
# are as follows:
|
||||
#
|
||||
# - default : build all libraries and programs
|
||||
# - check : build and run all unit tests
|
||||
# - install : install headers, project library, and some programs
|
||||
# - clean : remove all generated content (except autoconf files)
|
||||
# - dist : make a source tarball
|
||||
# - distcheck : make a source tarball, untar it, check it, clean it
|
||||
# - distclean : remove everything
|
||||
#
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Basic setup
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
# Remove all default implicit rules since they can cause subtle bugs
|
||||
# and they just make things run slower
|
||||
.SUFFIXES:
|
||||
% : %,v
|
||||
% : RCS/%,v
|
||||
% : RCS/%
|
||||
% : s.%
|
||||
% : SCCS/s.%
|
||||
|
||||
# Default is to build the prereqs of the all target (defined at bottom)
|
||||
default : all
|
||||
.PHONY : default
|
||||
|
||||
project_name := @PACKAGE_TARNAME@
|
||||
src_dir := @srcdir@
|
||||
scripts_dir := $(src_dir)/scripts
|
||||
|
||||
# If the version information is not in the configure script, then we
|
||||
# assume that we are in a working directory. We use the vcs-version.sh
|
||||
# script in the scripts directory to generate an appropriate version
|
||||
# string. Currently the way things are setup we have to run this script
|
||||
# everytime we run make so the script needs to be as fast as possible.
|
||||
|
||||
ifeq (@PACKAGE_VERSION@,?)
|
||||
project_ver:=$(shell $(scripts_dir)/vcs-version.sh $(src_dir))
|
||||
else
|
||||
project_ver:=@PACKAGE_VERSION@
|
||||
endif
|
||||
|
||||
# Installation directories
|
||||
|
||||
prefix := @prefix@
|
||||
enable_stow := @enable_stow@
|
||||
|
||||
ifeq ($(enable_stow),yes)
|
||||
stow_pkg_dir := $(prefix)/pkgs
|
||||
INSTALLDIR ?= $(DESTDIR)/$(stow_pkg_dir)/$(project_name)-$(project_ver)
|
||||
else
|
||||
INSTALLDIR ?= $(DESTDIR)/$(prefix)/@install_subdir@
|
||||
endif
|
||||
|
||||
install_hdrs_dir := $(INSTALLDIR)/include/$(project_name)
|
||||
install_libs_dir := $(INSTALLDIR)/lib/$(project_name)
|
||||
install_exes_dir := $(INSTALLDIR)/bin
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# List of subprojects
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
sprojs := @subprojects@
|
||||
sprojs_enabled := @subprojects_enabled@
|
||||
|
||||
sprojs_include := -I. $(addprefix -I$(src_dir)/, $(sprojs_enabled))
|
||||
VPATH := $(addprefix $(src_dir)/, $(sprojs_enabled))
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Programs and flags
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
# C++ compiler
|
||||
# - CPPFLAGS : flags for the preprocessor (eg. -I,-D)
|
||||
# - CXXFLAGS : flags for C++ compiler (eg. -Wall,-g,-O3)
|
||||
|
||||
CC := @CC@
|
||||
READELF := @READELF@
|
||||
OBJCOPY := @OBJCOPY@
|
||||
CFLAGS := @CFLAGS@ $(CFLAGS) -DBBL_PAYLOAD=\"bbl_payload\" -DBBL_LOGO_FILE=\"bbl_logo_file\"
|
||||
BBL_PAYLOAD := @BBL_PAYLOAD@
|
||||
COMPILE := $(CC) -MMD -MP $(CFLAGS) \
|
||||
$(sprojs_include)
|
||||
# Linker
|
||||
# - LDFLAGS : Flags for the linker (eg. -L)
|
||||
# - LIBS : Library flags (eg. -l)
|
||||
|
||||
LD := $(CC)
|
||||
LDFLAGS := @LDFLAGS@ -nostartfiles -nostdlib -static $(LDFLAGS)
|
||||
LIBS := @LIBS@
|
||||
LINK := $(LD) $(LDFLAGS)
|
||||
|
||||
# Library creation
|
||||
|
||||
AR := @AR@
|
||||
RANLIB := @RANLIB@
|
||||
|
||||
# Host simulator
|
||||
|
||||
RUN := @RUN@
|
||||
RUNFLAGS := @RUNFLAGS@
|
||||
|
||||
# Installation
|
||||
|
||||
MKINSTALLDIRS := $(scripts_dir)/mk-install-dirs.sh
|
||||
INSTALL := @INSTALL@
|
||||
INSTALL_HDR := $(INSTALL) -m 444
|
||||
INSTALL_LIB := $(INSTALL) -m 644
|
||||
INSTALL_EXE := $(INSTALL) -m 555
|
||||
STOW := @stow@
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Include subproject makefile fragments
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
sprojs_mk = $(addsuffix .mk, $(sprojs_enabled))
|
||||
|
||||
-include $(sprojs_mk)
|
||||
|
||||
dist_junk += $(sprojs_mk)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Reverse list helper function
|
||||
#-------------------------------------------------------------------------
|
||||
# This function is used by the subproject template to reverse the list
|
||||
# of dependencies. It uses recursion to perform the reversal.
|
||||
#
|
||||
# Arguments:
|
||||
# $(1) : space separated input list
|
||||
# retval : input list in reverse order
|
||||
#
|
||||
|
||||
reverse_list = $(call reverse_list_h,$(1),)
|
||||
define reverse_list_h
|
||||
$(if $(strip $(1)), \
|
||||
$(call reverse_list_h, \
|
||||
$(wordlist 2,$(words $(1)),$(1)), \
|
||||
$(firstword $(1)) $(2)), \
|
||||
$(2))
|
||||
endef
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Template for per subproject rules
|
||||
#-------------------------------------------------------------------------
|
||||
# The template is instantiated for each of the subprojects. It relies on
|
||||
# subprojects defining a certain set of make variables which are all
|
||||
# prefixed with the subproject name. Since subproject names can have
|
||||
# dashes in them (and the make variables are assumed to only use
|
||||
# underscores) the template takes two arguments - one with the regular
|
||||
# subproject name and one with dashes replaced with underscores.
|
||||
#
|
||||
# Arguments:
|
||||
# $(1) : real subproject name (ie with dashes)
|
||||
# $(2) : normalized subproject name (ie dashes replaced with underscores)
|
||||
#
|
||||
|
||||
define subproject_template
|
||||
|
||||
# In some (rare) cases, a subproject might not have any actual object
|
||||
# files. It might only include header files or program sources. To keep
|
||||
# things consistent we still want a library for this subproject, so in
|
||||
# this spectial case we create a dummy source file and thus the build
|
||||
# system will create a library for this subproject with just the
|
||||
# corresponding dummy object file.
|
||||
|
||||
ifeq ($$(strip $$($(2)_c_srcs)),)
|
||||
$(2)_c_srcs += _$(1).c
|
||||
$(2)_junk += _$(1).c
|
||||
endif
|
||||
|
||||
_$(1).c :
|
||||
echo "int _$(2)( int arg ) { return arg; }" > $$@
|
||||
|
||||
# Build the object files for this subproject
|
||||
|
||||
$(2)_c_objs := $$(patsubst %.c, %.o, $$($(2)_c_srcs))
|
||||
$(2)_asm_objs := $$(patsubst %.S, %.o, $$($(2)_asm_srcs))
|
||||
$(2)_c_deps := $$(patsubst %.o, %.d, $$($(2)_c_objs))
|
||||
$$($(2)_c_objs) : %.o : %.c
|
||||
$(COMPILE) -c $$<
|
||||
$$($(2)_asm_objs) : %.o : %.S
|
||||
$(COMPILE) -c $$<
|
||||
|
||||
$(2)_junk += $$($(2)_c_objs) $$($(2)_c_deps) $$($(2)_asm_objs)
|
||||
|
||||
# Build a library for this subproject
|
||||
|
||||
lib$(1).a : $$($(2)_c_objs) $$($(2)_asm_objs)
|
||||
$(AR) rcv $$@ $$^
|
||||
$(RANLIB) $$@
|
||||
|
||||
$(2)_junk += lib$(1).a
|
||||
|
||||
# Reverse the dependency list so that a given subproject only depends on
|
||||
# subprojects listed to its right. This is the correct order for linking
|
||||
# the list of subproject libraries.
|
||||
|
||||
$(2)_reverse_deps := $$(call reverse_list,$$($(2)_subproject_deps))
|
||||
|
||||
# Build unit tests
|
||||
|
||||
$(2)_test_objs := $$(patsubst %.c, %.o, $$($(2)_test_srcs))
|
||||
$(2)_test_deps := $$(patsubst %.o, %.d, $$($(2)_test_objs))
|
||||
$(2)_test_exes := $$(patsubst %.t.c, %-utst, $$($(2)_test_srcs))
|
||||
$(2)_test_outs := $$(patsubst %, %.out, $$($(2)_test_exes))
|
||||
$(2)_test_libs := $(1) $$($(2)_reverse_deps) utst
|
||||
$(2)_test_libnames := $$(patsubst %, lib%.a, $$($(2)_test_libs))
|
||||
$(2)_test_libarg := -L. $$(patsubst %, -l%, $$($(2)_test_libs))
|
||||
|
||||
$$($(2)_test_objs) : %.o : %.c
|
||||
$(COMPILE) -c $$<
|
||||
|
||||
$$($(2)_test_exes) : %-utst : %.t.o $$($(2)_test_libnames)
|
||||
$(LINK) -o $$@ $$< $$($(2)_test_libarg) $(LIBS)
|
||||
|
||||
$(2)_c_deps += $$($(2)_test_deps)
|
||||
$(2)_junk += \
|
||||
$$($(2)_test_objs) $$($(2)_test_deps) \
|
||||
$$($(2)_test_exes) *.junk-dat
|
||||
|
||||
# Run unit tests
|
||||
|
||||
$$($(2)_test_outs) : %.out : %
|
||||
$(RUN) $(RUNFLAGS) ./$$< default | tee $$@
|
||||
|
||||
$(2)_junk += $$($(2)_test_outs)
|
||||
|
||||
# Build programs
|
||||
|
||||
$(2)_prog_objs := $$(patsubst %.c, %.o, $$($(2)_prog_srcs))
|
||||
$(2)_prog_deps := $$(patsubst %.o, %.d, $$($(2)_prog_objs))
|
||||
$(2)_prog_exes := $$(patsubst %.c, %, $$($(2)_prog_srcs))
|
||||
$(2)_prog_libs := $(1) $$($(2)_reverse_deps)
|
||||
$(2)_prog_libnames := $$(patsubst %, lib%.a, $$($(2)_prog_libs))
|
||||
$(2)_prog_libarg := -L. $$(patsubst %, -l%, $$($(2)_prog_libs))
|
||||
|
||||
$$($(2)_prog_objs) : %.o : %.c
|
||||
$(COMPILE) -c $$<
|
||||
|
||||
$$($(2)_prog_exes) : % : %.o $$($(2)_prog_libnames)
|
||||
$(LINK) -o $$@ $$< $$($(2)_prog_libarg) $(LIBS)
|
||||
|
||||
$(2)_c_deps += $$($(2)_prog_deps)
|
||||
$(2)_junk += $$($(2)_prog_objs) $$($(2)_prog_deps) $$($(2)_prog_exes)
|
||||
|
||||
# Build programs which will be installed
|
||||
|
||||
$(2)_install_prog_objs := $$(patsubst %.c, %.o, $$($(2)_install_prog_srcs))
|
||||
$(2)_install_prog_deps := $$(patsubst %.o, %.d, $$($(2)_install_prog_objs))
|
||||
$(2)_install_prog_exes := $$(patsubst %.c, %, $$($(2)_install_prog_srcs))
|
||||
|
||||
$$($(2)_install_prog_objs) : %.o : %.c
|
||||
$(COMPILE) -c $$<
|
||||
|
||||
$$($(2)_install_prog_exes) : % : %.o $$($(2)_prog_libnames)
|
||||
$(LINK) -o $$@ $$< $$($(2)_prog_libarg) $(LIBS) -T $(src_dir)/$(2)/$(2).lds
|
||||
|
||||
$(2)_c_deps += $$($(2)_install_prog_deps)
|
||||
$(2)_junk += \
|
||||
$$($(2)_install_prog_objs) $$($(2)_install_prog_deps) \
|
||||
$$($(2)_install_prog_exes)
|
||||
|
||||
# Subproject specific targets
|
||||
|
||||
all-$(1) : lib$(1).a $$($(2)_install_prog_exes)
|
||||
|
||||
check-$(1) : $$($(2)_test_outs)
|
||||
echo; grep -h -e'Unit Tests' -e'FAILED' -e'Segementation' $$^; echo
|
||||
|
||||
clean-$(1) :
|
||||
rm -rf $$($(2)_junk)
|
||||
|
||||
.PHONY : all-$(1) check-$(1) clean-$(1)
|
||||
|
||||
# Update running variables
|
||||
|
||||
libs += lib$(1).a
|
||||
objs += $$($(2)_c_objs)
|
||||
srcs += $$(addprefix $(src_dir)/$(1)/, $$($(2)_c_srcs))
|
||||
hdrs += $$(addprefix $(src_dir)/$(1)/, $$($(2)_hdrs))
|
||||
junk += $$($(2)_junk)
|
||||
deps += $$($(2)_c_deps)
|
||||
|
||||
test_outs += $$($(2)_test_outs)
|
||||
|
||||
install_hdrs += $$(addprefix $(src_dir)/$(1)/, $$($(2)_hdrs))
|
||||
install_libs += lib$(1).a
|
||||
install_exes += $$($(2)_install_prog_exes)
|
||||
|
||||
endef
|
||||
|
||||
# Iterate over the subprojects and call the template for each one
|
||||
|
||||
$(foreach sproj,$(sprojs_enabled), \
|
||||
$(eval $(call subproject_template,$(sproj),$(subst -,_,$(sproj)))))
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Autodependency files
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
-include $(deps)
|
||||
|
||||
deps : $(deps)
|
||||
.PHONY : deps
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
check : $(test_outs)
|
||||
echo; grep -h -e'Unit Tests' -e'FAILED' -e'Segementation' $^; echo
|
||||
|
||||
.PHONY : check
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Installation
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
install-hdrs : $(install_hdrs)
|
||||
$(MKINSTALLDIRS) $(install_hdrs_dir)
|
||||
for file in $(install_hdrs); \
|
||||
do \
|
||||
$(INSTALL_HDR) $$file $(install_hdrs_dir); \
|
||||
done
|
||||
|
||||
install-libs : $(install_libs)
|
||||
$(MKINSTALLDIRS) $(install_libs_dir)
|
||||
for file in $(install_libs); \
|
||||
do \
|
||||
$(INSTALL_LIB) $$file $(install_libs_dir); \
|
||||
done
|
||||
|
||||
install-exes : $(install_exes)
|
||||
$(MKINSTALLDIRS) $(install_exes_dir)
|
||||
for file in $(install_exes); \
|
||||
do \
|
||||
$(INSTALL_EXE) $$file $(install_exes_dir); \
|
||||
done
|
||||
|
||||
install : install-hdrs install-libs install-exes
|
||||
ifeq ($(enable_stow),yes)
|
||||
$(MKINSTALLDIRS) $(stow_pkg_dir)
|
||||
cd $(stow_pkg_dir) && \
|
||||
$(STOW) --delete $(project_name)-* && \
|
||||
$(STOW) $(project_name)-$(project_ver)
|
||||
endif
|
||||
|
||||
.PHONY : install install-hdrs install-libs install-exes
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Regenerate configure information
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
config.status : $(src_dir)/configure
|
||||
./config.status --recheck
|
||||
|
||||
sprojs_mk_in = \
|
||||
$(join $(addprefix $(src_dir)/, $(sprojs_enabled)), \
|
||||
$(patsubst %, /%.mk.in, $(sprojs_enabled)))
|
||||
|
||||
Makefile : $(src_dir)/Makefile.in $(sprojs_mk_in) config.status
|
||||
./config.status
|
||||
|
||||
dist_junk += config.status config.h Makefile config.log
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Distribution
|
||||
#-------------------------------------------------------------------------
|
||||
# The distribution tarball is named project-ver.tar.gz and it includes
|
||||
# both enabled and disabled subprojects.
|
||||
|
||||
dist_files = \
|
||||
$(sprojs) \
|
||||
README \
|
||||
style-guide.txt \
|
||||
mcppbs-uguide.txt \
|
||||
scripts \
|
||||
configure.ac \
|
||||
aclocal.m4 \
|
||||
configure \
|
||||
config.h.in \
|
||||
Makefile.in \
|
||||
|
||||
dist_dir := $(project_name)-$(project_ver)
|
||||
dist_tgz := $(project_name)-$(project_ver).tar.gz
|
||||
|
||||
# Notice that when we make the distribution we rewrite the configure.ac
|
||||
# script with the current version and we rerun autoconf in the new
|
||||
# source directory so that the distribution will have the proper version
|
||||
# information. We also rewrite the "Version : " line in the README.
|
||||
|
||||
dist :
|
||||
rm -rf $(dist_dir)
|
||||
mkdir $(dist_dir)
|
||||
tar -C $(src_dir) -cf - $(dist_files) | tar -C $(dist_dir) -xpf -
|
||||
sed -i.bak 's/^\(# Version :\).*/\1 $(project_ver)/' $(dist_dir)/README
|
||||
sed -i.bak 's/\( proj_version,\).*/\1 [$(project_ver)])/' $(dist_dir)/configure.ac
|
||||
cd $(dist_dir) && \
|
||||
autoconf && autoheader && \
|
||||
rm -rf autom4te.cache configure.ac.bak README.bak
|
||||
tar -czvf $(dist_tgz) $(dist_dir)
|
||||
rm -rf $(dist_dir)
|
||||
|
||||
# You can use the distcheck target to try untarring the distribution and
|
||||
# then running configure, make, make check, and make distclean. A
|
||||
# "directory is not empty" error means distclean is not removing
|
||||
# everything.
|
||||
|
||||
distcheck : dist
|
||||
rm -rf $(dist_dir)
|
||||
tar -xzvf $(dist_tgz)
|
||||
mkdir -p $(dist_dir)/build
|
||||
cd $(dist_dir)/build; ../configure; make; make check; make distclean
|
||||
rm -rf $(dist_dir)
|
||||
|
||||
junk += $(project_name)-*.tar.gz
|
||||
|
||||
.PHONY : dist distcheck
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Default
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
all : $(install_hdrs) $(install_libs) $(install_exes)
|
||||
.PHONY : all
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Makefile debugging
|
||||
#-------------------------------------------------------------------------
|
||||
# This handy rule will display the contents of any make variable by
|
||||
# using the target debug-<varname>. So for example, make debug-junk will
|
||||
# display the contents of the junk variable.
|
||||
|
||||
debug-% :
|
||||
@echo $* = $($*)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Clean up junk
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
clean :
|
||||
rm -rf *~ \#* $(junk)
|
||||
|
||||
distclean :
|
||||
rm -rf *~ \#* $(junk) $(dist_junk)
|
||||
|
||||
.PHONY : clean distclean
|
@ -1,40 +0,0 @@
|
||||
RISC-V Proxy Kernel and Boot Loader
|
||||
=====================================
|
||||
|
||||
About
|
||||
---------
|
||||
|
||||
The RISC-V Proxy Kernel, `pk`, is a lightweight application execution
|
||||
environment that can host statically-linked RISC-V ELF binaries. It is
|
||||
designed to support tethered RISC-V implementations with limited I/O
|
||||
capability and and thus handles I/O-related system calls by proxying them to
|
||||
a host computer.
|
||||
|
||||
This package also contains the Berkeley Boot Loader, `bbl`, which is a
|
||||
supervisor execution environment for tethered RISC-V systems. It is
|
||||
designed to host the RISC-V Linux port.
|
||||
|
||||
Build Steps
|
||||
---------------
|
||||
|
||||
We assume that the RISCV environment variable is set to the RISC-V tools
|
||||
install path, and that the riscv-gnu-toolchain package is installed.
|
||||
Please note that building the binaries directly inside the source
|
||||
directory is not supported; you need to use a separate build directory.
|
||||
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ ../configure --prefix=$RISCV --host=riscv64-unknown-elf
|
||||
$ make
|
||||
$ make install
|
||||
|
||||
Alternatively, the GNU/Linux toolchain may be used to build this package,
|
||||
by setting `--host=riscv64-unknown-linux-gnu`.
|
||||
|
||||
By default, 64-bit (RV64) versions of `pk` and `bbl` are built. To
|
||||
built 32-bit (RV32) versions, supply a `--enable-32bit` flag to the
|
||||
configure command.
|
||||
|
||||
The `install` step installs 64-bit build products into
|
||||
`$RISCV/riscv64-unknown-elf`, and 32-bit versions into
|
||||
`$RISCV/riscv32-unknown-elf`.
|
333
riscv-pk/aclocal.m4
vendored
333
riscv-pk/aclocal.m4
vendored
@ -1,333 +0,0 @@
|
||||
#=========================================================================
|
||||
# Local Autoconf Macros
|
||||
#=========================================================================
|
||||
# This file contains the macros for the Modular C++ Build System and
|
||||
# additional autoconf macros which developers can use in their
|
||||
# configure.ac scripts. Please read the documentation in
|
||||
# 'mcppbs-doc.txt' for more details on how the Modular C++ Build System
|
||||
# works. The documenation for each macro should include information
|
||||
# about the author, date, and copyright.
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# MCPPBS_PROG_INSTALL
|
||||
#-------------------------------------------------------------------------
|
||||
# This macro will add an --enable-stow command line option to the
|
||||
# configure script. When enabled, this macro will first check to see if
|
||||
# the stow program is available and if so it will set the $stow shell
|
||||
# variable to the binary name and the $enable_stow shell variable to
|
||||
# "yes". These variables can be used in a makefile to conditionally use
|
||||
# stow for installation.
|
||||
#
|
||||
# This macro uses two environment variables to help setup default stow
|
||||
# locations. The $STOW_PREFIX is used for stowing native built packages.
|
||||
# The packages are staged in $STOW_PREFIX/pkgs and then symlinks are
|
||||
# created from within $STOW_PREFIX into the pkgs subdirectory. If you
|
||||
# only do native builds then this is all you need to set. If you don't
|
||||
# set $STOW_PREFIX then the default is just the normal default prefix
|
||||
# which is almost always /usr/local.
|
||||
#
|
||||
# For non-native builds we probably want to install the packages in a
|
||||
# different location which includes the host architecture name as part
|
||||
# of the prefix. For these kind of builds, we can specify the $STOW_ROOT
|
||||
# environment variable and the effective prefix will be
|
||||
# $STOW_ROOT/${host_alias} where ${host_alias} is specified on the
|
||||
# configure command line with "--host".
|
||||
#
|
||||
# Here is an example setup:
|
||||
#
|
||||
# STOW_ROOT="$HOME/install"
|
||||
# STOW_ARCH="i386-macosx10.4"
|
||||
# STOW_PREFIX="${STOW_ROOT}/${STOW_ARCH}"
|
||||
#
|
||||
|
||||
AC_DEFUN([MCPPBS_PROG_INSTALL],
|
||||
[
|
||||
|
||||
# Configure command line option
|
||||
|
||||
AC_ARG_ENABLE(stow,
|
||||
AS_HELP_STRING(--enable-stow,[Enable stow-based install]),
|
||||
[enable_stow="yes"],[enable_stow="no"])
|
||||
|
||||
AC_SUBST([enable_stow])
|
||||
|
||||
# Environment variables
|
||||
|
||||
AC_ARG_VAR([STOW_ROOT], [Root for non-native stow-based installs])
|
||||
AC_ARG_VAR([STOW_PREFIX], [Prefix for stow-based installs])
|
||||
|
||||
# Check for install script
|
||||
|
||||
AC_PROG_INSTALL
|
||||
|
||||
# Deterimine if native build and set prefix appropriately
|
||||
|
||||
AS_IF([ test ${enable_stow} = "yes" ],
|
||||
[
|
||||
AC_CHECK_PROGS([stow],[stow],[no])
|
||||
AS_IF([ test ${stow} = "no" ],
|
||||
[
|
||||
AC_MSG_ERROR([Cannot use --enable-stow since stow is not available])
|
||||
])
|
||||
|
||||
# Check if native or non-native build
|
||||
|
||||
AS_IF([ test "${build}" = "${host}" ],
|
||||
[
|
||||
|
||||
# build == host so this is a native build. Make sure --prefix not
|
||||
# set and $STOW_PREFIX is set, then set prefix=$STOW_PREFIX.
|
||||
|
||||
AS_IF([ test "${prefix}" = "NONE" && test -n "${STOW_PREFIX}" ],
|
||||
[
|
||||
prefix="${STOW_PREFIX}"
|
||||
AC_MSG_NOTICE([Using \$STOW_PREFIX from environment])
|
||||
AC_MSG_NOTICE([prefix=${prefix}])
|
||||
])
|
||||
|
||||
],[
|
||||
|
||||
# build != host so this is a non-native build. Make sure --prefix
|
||||
# not set and $STOW_ROOT is set, then set
|
||||
# prefix=$STOW_ROOT/${host_alias}.
|
||||
|
||||
AS_IF([ test "${prefix}" = "NONE" && test -n "${STOW_ROOT}" ],
|
||||
[
|
||||
prefix="${STOW_ROOT}/${host_alias}"
|
||||
AC_MSG_NOTICE([Using \$STOW_ROOT from environment])
|
||||
AC_MSG_NOTICE([prefix=${prefix}])
|
||||
])
|
||||
|
||||
])
|
||||
|
||||
])
|
||||
|
||||
])
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# MCPPBS_SUBPROJECTS([ sproj1, sproj2, ... ])
|
||||
#-------------------------------------------------------------------------
|
||||
# The developer should call this macro with a list of the subprojects
|
||||
# which make up this project. One should order the list such that any
|
||||
# given subproject only depends on subprojects listed before it. The
|
||||
# subproject names can also include an * suffix which indicates that
|
||||
# this is an optional subproject. Optional subprojects are only included
|
||||
# as part of the project build if enabled on the configure command line
|
||||
# with a --enable-<subproject> flag. The user can also specify that all
|
||||
# optional subprojects should be included in the build with the
|
||||
# --enable-optional-subprojects flag.
|
||||
#
|
||||
# Subproject names can also include a ** suffix which indicates that it
|
||||
# is an optional subproject, but there is a group with the same name.
|
||||
# Thus the --enable-<sproj> command line option will enable not just the
|
||||
# subproject sproj but all of the subprojects which are in the group.
|
||||
# There is no error checking to make sure that if you use the ** suffix
|
||||
# you actually define a group so be careful.
|
||||
#
|
||||
# Both required and optional subprojects should have a 'subproject.ac'
|
||||
# file. The script's filename should be the abbreivated subproject name
|
||||
# (assuming the subproject name is sproj then we would use 'sproj.ac')
|
||||
# The MCPPBS_SUBPROJECTS macro includes the 'subproject.ac' files for
|
||||
# enabled subprojects. Whitespace and newlines are allowed within the
|
||||
# list.
|
||||
#
|
||||
# Author : Christopher Batten
|
||||
# Date : September 10, 2008
|
||||
|
||||
AC_DEFUN([MCPPBS_SUBPROJECTS],
|
||||
[
|
||||
|
||||
# Add command line argument to enable all optional subprojects
|
||||
|
||||
AC_ARG_ENABLE(optional-subprojects,
|
||||
AS_HELP_STRING([--enable-optional-subprojects],
|
||||
[Enable all optional subprojects]))
|
||||
|
||||
# Loop through the subprojects given in the macro argument
|
||||
|
||||
m4_foreach([MCPPBS_SPROJ],[$1],
|
||||
[
|
||||
|
||||
# Determine if this is a required or an optional subproject
|
||||
|
||||
m4_define([MCPPBS_IS_REQ],
|
||||
m4_bmatch(MCPPBS_SPROJ,[\*+],[false],[true]))
|
||||
|
||||
# Determine if there is a group with the same name
|
||||
|
||||
m4_define([MCPPBS_IS_GROUP],
|
||||
m4_bmatch(MCPPBS_SPROJ,[\*\*],[true],[false]))
|
||||
|
||||
# Create variations of the subproject name suitable for use as a CPP
|
||||
# enabled define, a shell enabled variable, and a shell function
|
||||
|
||||
m4_define([MCPPBS_SPROJ_NORM],
|
||||
m4_normalize(m4_bpatsubsts(MCPPBS_SPROJ,[*],[])))
|
||||
|
||||
m4_define([MCPPBS_SPROJ_DEFINE],
|
||||
m4_toupper(m4_bpatsubst(MCPPBS_SPROJ_NORM[]_ENABLED,[-],[_])))
|
||||
|
||||
m4_define([MCPPBS_SPROJ_FUNC],
|
||||
m4_bpatsubst(_mpbp_[]MCPPBS_SPROJ_NORM[]_configure,[-],[_]))
|
||||
|
||||
m4_define([MCPPBS_SPROJ_UNDERSCORES],
|
||||
m4_bpatsubsts(MCPPBS_SPROJ,[-],[_]))
|
||||
|
||||
m4_define([MCPPBS_SPROJ_SHVAR],
|
||||
m4_bpatsubst(enable_[]MCPPBS_SPROJ_NORM[]_sproj,[-],[_]))
|
||||
|
||||
# Add subproject to our running list
|
||||
|
||||
subprojects="$subprojects MCPPBS_SPROJ_NORM"
|
||||
|
||||
# Process the subproject appropriately. If enabled add it to the
|
||||
# $enabled_subprojects running shell variable, set a
|
||||
# SUBPROJECT_ENABLED C define, and include the appropriate
|
||||
# 'subproject.ac'.
|
||||
|
||||
m4_if(MCPPBS_IS_REQ,[true],
|
||||
[
|
||||
AC_MSG_NOTICE([configuring default subproject : MCPPBS_SPROJ_NORM])
|
||||
AC_CONFIG_FILES(MCPPBS_SPROJ_NORM[].mk:MCPPBS_SPROJ_NORM[]/MCPPBS_SPROJ_NORM[].mk.in)
|
||||
MCPPBS_SPROJ_SHVAR="yes"
|
||||
subprojects_enabled="$subprojects_enabled MCPPBS_SPROJ_NORM"
|
||||
AC_DEFINE(MCPPBS_SPROJ_DEFINE,,
|
||||
[Define if subproject MCPPBS_SPROJ_NORM is enabled])
|
||||
m4_include(MCPPBS_SPROJ_NORM[]/MCPPBS_SPROJ_NORM[].ac)
|
||||
],[
|
||||
|
||||
# For optional subprojects we capture the 'subproject.ac' as a
|
||||
# shell function so that in the MCPPBS_GROUP macro we can just
|
||||
# call this shell function instead of reading in 'subproject.ac'
|
||||
# again.
|
||||
|
||||
MCPPBS_SPROJ_FUNC ()
|
||||
{
|
||||
AC_MSG_NOTICE([configuring optional subproject : MCPPBS_SPROJ_NORM])
|
||||
AC_CONFIG_FILES(MCPPBS_SPROJ_NORM[].mk:MCPPBS_SPROJ_NORM[]/MCPPBS_SPROJ_NORM[].mk.in)
|
||||
MCPPBS_SPROJ_SHVAR="yes"
|
||||
subprojects_enabled="$subprojects_enabled MCPPBS_SPROJ_NORM"
|
||||
AC_DEFINE(MCPPBS_SPROJ_DEFINE,,
|
||||
[Define if subproject MCPPBS_SPROJ_NORM is enabled])
|
||||
m4_include(MCPPBS_SPROJ_NORM[]/MCPPBS_SPROJ_NORM[].ac)
|
||||
};
|
||||
|
||||
# Optional subprojects add --enable-subproject command line
|
||||
# options, _if_ the subproject name is not also a group name.
|
||||
|
||||
m4_if(MCPPBS_IS_GROUP,[false],
|
||||
[
|
||||
AC_ARG_ENABLE(MCPPBS_SPROJ_NORM,
|
||||
AS_HELP_STRING(--enable-MCPPBS_SPROJ_NORM,
|
||||
[Subproject MCPPBS_SPROJ_NORM]),
|
||||
[MCPPBS_SPROJ_SHVAR="yes"],[MCPPBS_SPROJ_SHVAR="no"])
|
||||
|
||||
AS_IF([test "$MCPPBS_SPROJ_SHVAR" = "yes"],
|
||||
[
|
||||
eval "MCPPBS_SPROJ_FUNC"
|
||||
],[
|
||||
AC_MSG_NOTICE([processing optional subproject : MCPPBS_SPROJ_NORM])
|
||||
])
|
||||
|
||||
],[
|
||||
|
||||
# If the subproject name is also a group name then we need to
|
||||
# make sure that we set the shell variable for that subproject to
|
||||
# no so that the group code knows we haven't run it yet.
|
||||
|
||||
AC_MSG_NOTICE([processing optional subproject : MCPPBS_SPROJ_NORM])
|
||||
MCPPBS_SPROJ_SHVAR="no"
|
||||
|
||||
])
|
||||
|
||||
# Always execute the subproject configure code if we are enabling
|
||||
# all subprojects.
|
||||
|
||||
AS_IF([ test "$enable_optional_subprojects" = "yes" \
|
||||
&& test "$MCPPBS_SPROJ_SHVAR" = "no" ],
|
||||
[
|
||||
eval "MCPPBS_SPROJ_FUNC"
|
||||
])
|
||||
|
||||
])
|
||||
|
||||
])
|
||||
|
||||
# Output make variables
|
||||
|
||||
AC_SUBST([subprojects])
|
||||
AC_SUBST([subprojects_enabled])
|
||||
|
||||
])
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# MCPPBS_GROUP( [group-name], [ sproj1, sproj2, ... ] )
|
||||
#-------------------------------------------------------------------------
|
||||
# This macro creates a subproject group with the given group-name. When
|
||||
# a user specifies --enable-<group-name> the listed subprojects will be
|
||||
# enabled. Groups can have the same name as a subproject and in that
|
||||
# case whenever a user specifies --enable-<subproject> the subprojects
|
||||
# listed in the corresponding group will also be enabled. Groups are
|
||||
# useful for specifying related subprojects which are usually enabled
|
||||
# together, as well as for specifying that a specific optional
|
||||
# subproject has dependencies on other optional subprojects.
|
||||
#
|
||||
# Author : Christopher Batten
|
||||
# Date : September 10, 2008
|
||||
|
||||
AC_DEFUN([MCPPBS_GROUP],
|
||||
[
|
||||
|
||||
m4_define([MCPPBS_GROUP_NORM],
|
||||
m4_normalize([$1]))
|
||||
|
||||
m4_define([MCPPBS_GROUP_SHVAR],
|
||||
m4_bpatsubst(enable_[]MCPPBS_GROUP_NORM[]_group,[-],[_]))
|
||||
|
||||
AC_ARG_ENABLE(MCPPBS_GROUP_NORM,
|
||||
AS_HELP_STRING(--enable-MCPPBS_GROUP_NORM,
|
||||
[Group MCPPBS_GROUP_NORM: $2]),
|
||||
[MCPPBS_GROUP_SHVAR="yes"],[MCPPBS_GROUP_SHVAR="no"])
|
||||
|
||||
AS_IF([test "$MCPPBS_GROUP_SHVAR" = "yes" ],
|
||||
[
|
||||
AC_MSG_NOTICE([configuring optional group : MCPPBS_GROUP_NORM])
|
||||
])
|
||||
|
||||
m4_foreach([MCPPBS_SPROJ],[$2],
|
||||
[
|
||||
|
||||
m4_define([MCPPBS_SPROJ_NORM],
|
||||
m4_normalize(MCPPBS_SPROJ))
|
||||
|
||||
m4_define([MCPPBS_SPROJ_SHVAR],
|
||||
m4_bpatsubst(enable_[]MCPPBS_SPROJ_NORM[]_sproj,[-],[_]))
|
||||
|
||||
m4_define([MCPPBS_SPROJ_FUNC],
|
||||
m4_bpatsubst(_mpbp_[]MCPPBS_SPROJ_NORM[]_configure,[-],[_]))
|
||||
|
||||
AS_IF([ test "$MCPPBS_GROUP_SHVAR" = "yes" \
|
||||
&& test "$MCPPBS_SPROJ_SHVAR" = "no" ],
|
||||
[
|
||||
eval "MCPPBS_SPROJ_FUNC"
|
||||
])
|
||||
|
||||
])
|
||||
|
||||
])
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# AX_DEFAULT_CONFIGURE_ARG
|
||||
#-------------------------------------------------------------------------
|
||||
# Simple little macro which adds a configure commane line option to an
|
||||
# internal autoconf shell variable. Not sure how safe this is, but it
|
||||
# seems to work fine.
|
||||
#
|
||||
# Author : Christopher Batten
|
||||
# Date : August 20, 2009
|
||||
|
||||
AC_DEFUN([AX_DEFAULT_CONFIGURE_ARG],
|
||||
[
|
||||
AC_MSG_NOTICE([adding default configure arg: $1])
|
||||
ac_configure_args="$1 ${ac_configure_args}"
|
||||
])
|
@ -1,12 +0,0 @@
|
||||
AC_ARG_ENABLE([logo], AS_HELP_STRING([--enable-logo], [Enable boot logo]))
|
||||
AS_IF([test "x$enable_logo" == "xyes"], [
|
||||
AC_DEFINE([PK_ENABLE_LOGO],,[Define if the RISC-V logo is to be displayed])
|
||||
])
|
||||
|
||||
AC_ARG_WITH([payload], AS_HELP_STRING([--with-payload], [Set ELF payload for bbl]),
|
||||
[AC_SUBST([BBL_PAYLOAD], $with_payload, [Kernel payload for bbl])],
|
||||
[AC_SUBST([BBL_PAYLOAD], [dummy_payload], [Kernel payload for bbl])])
|
||||
|
||||
AC_ARG_WITH([logo], AS_HELP_STRING([--with-logo], [Specify a better logo]),
|
||||
[AC_SUBST([BBL_LOGO_FILE], $with_logo, [Logo for bbl])],
|
||||
[AC_SUBST([BBL_LOGO_FILE], [riscv_logo.txt], [Logo for bbl])])
|
@ -1,67 +0,0 @@
|
||||
#include "bbl.h"
|
||||
#include "mtrap.h"
|
||||
#include "atomic.h"
|
||||
#include "vm.h"
|
||||
#include "bits.h"
|
||||
#include "config.h"
|
||||
#include "fdt.h"
|
||||
#include <string.h>
|
||||
|
||||
static const void* entry_point;
|
||||
long disabled_hart_mask;
|
||||
|
||||
static uintptr_t dtb_output()
|
||||
{
|
||||
extern char _payload_end;
|
||||
uintptr_t end = (uintptr_t) &_payload_end;
|
||||
return (end + MEGAPAGE_SIZE - 1) / MEGAPAGE_SIZE * MEGAPAGE_SIZE;
|
||||
}
|
||||
|
||||
static void filter_dtb(uintptr_t source)
|
||||
{
|
||||
uintptr_t dest = dtb_output();
|
||||
uint32_t size = fdt_size(source);
|
||||
memcpy((void*)dest, (void*)source, size);
|
||||
|
||||
// Remove information from the chained FDT
|
||||
filter_harts(dest, &disabled_hart_mask);
|
||||
filter_plic(dest);
|
||||
filter_compat(dest, "riscv,clint0");
|
||||
filter_compat(dest, "riscv,debug-013");
|
||||
}
|
||||
|
||||
void boot_other_hart(uintptr_t unused __attribute__((unused)))
|
||||
{
|
||||
const void* entry;
|
||||
do {
|
||||
entry = entry_point;
|
||||
mb();
|
||||
} while (!entry);
|
||||
|
||||
long hartid = read_csr(mhartid);
|
||||
if ((1 << hartid) & disabled_hart_mask) {
|
||||
while (1) {
|
||||
__asm__ volatile("wfi");
|
||||
#ifdef __riscv_div
|
||||
__asm__ volatile("div x0, x0, x0");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
enter_supervisor_mode(entry, hartid, dtb_output(), ~disabled_hart_mask & hart_mask);
|
||||
}
|
||||
|
||||
void boot_loader(uintptr_t dtb)
|
||||
{
|
||||
extern char _payload_start;
|
||||
filter_dtb(dtb);
|
||||
#ifdef PK_ENABLE_LOGO
|
||||
print_logo();
|
||||
#endif
|
||||
#ifdef PK_PRINT_DEVICE_TREE
|
||||
fdt_print(dtb_output());
|
||||
#endif
|
||||
mb();
|
||||
entry_point = &_payload_start;
|
||||
boot_other_hart(0);
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
#ifndef _BBL_H
|
||||
#define _BBL_H
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
void print_logo();
|
||||
|
||||
#endif // !__ASSEMBLER__
|
||||
|
||||
#endif
|
@ -1,111 +0,0 @@
|
||||
OUTPUT_ARCH( "riscv" )
|
||||
|
||||
ENTRY( reset_vector )
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
|
||||
/*--------------------------------------------------------------------*/
|
||||
/* Code and read-only segment */
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
/* Begining of code and text segment */
|
||||
. = 0x80000000;
|
||||
_ftext = .;
|
||||
PROVIDE( eprol = . );
|
||||
|
||||
.text :
|
||||
{
|
||||
*(.text.init)
|
||||
}
|
||||
|
||||
/* text: Program code section */
|
||||
.text :
|
||||
{
|
||||
*(.text)
|
||||
*(.text.*)
|
||||
*(.gnu.linkonce.t.*)
|
||||
}
|
||||
|
||||
/* rodata: Read-only data */
|
||||
.rodata :
|
||||
{
|
||||
*(.rdata)
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
*(.gnu.linkonce.r.*)
|
||||
}
|
||||
|
||||
/* End of code and read-only segment */
|
||||
PROVIDE( etext = . );
|
||||
_etext = .;
|
||||
|
||||
/*--------------------------------------------------------------------*/
|
||||
/* HTIF, isolated onto separate page */
|
||||
/*--------------------------------------------------------------------*/
|
||||
. = ALIGN(0x1000);
|
||||
.htif :
|
||||
{
|
||||
PROVIDE( __htif_base = .);
|
||||
*(.htif)
|
||||
}
|
||||
. = ALIGN(0x1000);
|
||||
|
||||
/*--------------------------------------------------------------------*/
|
||||
/* Initialized data segment */
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
/* Start of initialized data segment */
|
||||
. = ALIGN(16);
|
||||
_fdata = .;
|
||||
|
||||
/* data: Writable data */
|
||||
.data :
|
||||
{
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
*(.srodata*)
|
||||
*(.gnu.linkonce.d.*)
|
||||
*(.comment)
|
||||
}
|
||||
|
||||
/* End of initialized data segment */
|
||||
. = ALIGN(4);
|
||||
PROVIDE( edata = . );
|
||||
_edata = .;
|
||||
|
||||
/*--------------------------------------------------------------------*/
|
||||
/* Uninitialized data segment */
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
/* Start of uninitialized data segment */
|
||||
. = .;
|
||||
_fbss = .;
|
||||
|
||||
/* sbss: Uninitialized writeable small data section */
|
||||
. = .;
|
||||
|
||||
/* bss: Uninitialized writeable data section */
|
||||
. = .;
|
||||
_bss_start = .;
|
||||
.bss :
|
||||
{
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
*(.sbss*)
|
||||
*(.gnu.linkonce.b.*)
|
||||
*(COMMON)
|
||||
}
|
||||
|
||||
.sbi :
|
||||
{
|
||||
*(.sbi)
|
||||
}
|
||||
|
||||
.payload :
|
||||
{
|
||||
*(.payload)
|
||||
}
|
||||
|
||||
_end = .;
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
bbl_subproject_deps = \
|
||||
util \
|
||||
machine \
|
||||
dummy_payload \
|
||||
|
||||
bbl_hdrs = \
|
||||
bbl.h \
|
||||
|
||||
bbl_c_srcs = \
|
||||
logo.c \
|
||||
|
||||
bbl_asm_srcs = \
|
||||
payload.S \
|
||||
raw_logo.S \
|
||||
|
||||
payload.o: bbl_payload
|
||||
|
||||
bbl_payload: $(BBL_PAYLOAD)
|
||||
if $(READELF) -h $< 2> /dev/null > /dev/null; then $(OBJCOPY) -O binary $< $@; else cp $< $@; fi
|
||||
|
||||
raw_logo.o: bbl_logo_file
|
||||
|
||||
bbl_logo_file: @BBL_LOGO_FILE@
|
||||
cat $^ | sed 's/$$/\r/' > $@
|
||||
|
||||
bbl_test_srcs =
|
||||
|
||||
bbl_install_prog_srcs = \
|
||||
bbl.c \
|
@ -1,9 +0,0 @@
|
||||
#include <string.h>
|
||||
#include "mtrap.h"
|
||||
|
||||
extern const char logo[];
|
||||
|
||||
void print_logo()
|
||||
{
|
||||
putstring(logo);
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
#include "encoding.h"
|
||||
|
||||
.section ".payload","a",@progbits
|
||||
.align 17
|
||||
|
||||
.globl _payload_start, _payload_end
|
||||
_payload_start:
|
||||
.incbin BBL_PAYLOAD
|
||||
_payload_end:
|
@ -1,7 +0,0 @@
|
||||
#include "encoding.h"
|
||||
|
||||
.section .rodata
|
||||
.globl logo
|
||||
logo:
|
||||
.incbin BBL_LOGO_FILE
|
||||
.byte 0
|
@ -1,23 +0,0 @@
|
||||
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
|
||||
vvvvvvvvvvvvvvvvvvvvvvvvvvvv
|
||||
rrrrrrrrrrrrr vvvvvvvvvvvvvvvvvvvvvvvvvv
|
||||
rrrrrrrrrrrrrrrr vvvvvvvvvvvvvvvvvvvvvvvv
|
||||
rrrrrrrrrrrrrrrrrr vvvvvvvvvvvvvvvvvvvvvvvv
|
||||
rrrrrrrrrrrrrrrrrr vvvvvvvvvvvvvvvvvvvvvvvv
|
||||
rrrrrrrrrrrrrrrrrr vvvvvvvvvvvvvvvvvvvvvvvv
|
||||
rrrrrrrrrrrrrrrr vvvvvvvvvvvvvvvvvvvvvv
|
||||
rrrrrrrrrrrrr vvvvvvvvvvvvvvvvvvvvvv
|
||||
rr vvvvvvvvvvvvvvvvvvvvvv
|
||||
rr vvvvvvvvvvvvvvvvvvvvvvvv rr
|
||||
rrrr vvvvvvvvvvvvvvvvvvvvvvvvvv rrrr
|
||||
rrrrrr vvvvvvvvvvvvvvvvvvvvvv rrrrrr
|
||||
rrrrrrrr vvvvvvvvvvvvvvvvvv rrrrrrrr
|
||||
rrrrrrrrrr vvvvvvvvvvvvvv rrrrrrrrrr
|
||||
rrrrrrrrrrrr vvvvvvvvvv rrrrrrrrrrrr
|
||||
rrrrrrrrrrrrrr vvvvvv rrrrrrrrrrrrrr
|
||||
rrrrrrrrrrrrrrrr vv rrrrrrrrrrrrrrrr
|
||||
rrrrrrrrrrrrrrrrrr rrrrrrrrrrrrrrrrrr
|
||||
rrrrrrrrrrrrrrrrrrrr rrrrrrrrrrrrrrrrrrrr
|
||||
rrrrrrrrrrrrrrrrrrrrrr rrrrrrrrrrrrrrrrrrrrrr
|
||||
|
||||
INSTRUCTION SETS WANT TO BE FREE
|
@ -1,52 +0,0 @@
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define if subproject MCPPBS_SPROJ_NORM is enabled */
|
||||
#undef BBL_ENABLED
|
||||
|
||||
/* Define if subproject MCPPBS_SPROJ_NORM is enabled */
|
||||
#undef DUMMY_PAYLOAD_ENABLED
|
||||
|
||||
/* Define if subproject MCPPBS_SPROJ_NORM is enabled */
|
||||
#undef MACHINE_ENABLED
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#undef PACKAGE_URL
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define if subproject MCPPBS_SPROJ_NORM is enabled */
|
||||
#undef PK_ENABLED
|
||||
|
||||
/* Define if floating-point emulation is enabled */
|
||||
#undef PK_ENABLE_FP_EMULATION
|
||||
|
||||
/* Define if the RISC-V logo is to be displayed */
|
||||
#undef PK_ENABLE_LOGO
|
||||
|
||||
/* Define if virtual memory support is enabled */
|
||||
#undef PK_ENABLE_VM
|
||||
|
||||
/* Define if the DTS is to be displayed */
|
||||
#undef PK_PRINT_DEVICE_TREE
|
||||
|
||||
/* Define if subproject MCPPBS_SPROJ_NORM is enabled */
|
||||
#undef SOFTFLOAT_ENABLED
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* Define if subproject MCPPBS_SPROJ_NORM is enabled */
|
||||
#undef UTIL_ENABLED
|
5775
riscv-pk/configure
vendored
5775
riscv-pk/configure
vendored
File diff suppressed because it is too large
Load Diff
@ -1,138 +0,0 @@
|
||||
#=========================================================================
|
||||
# Toplevel configure.ac for the Modular C++ Build System
|
||||
#=========================================================================
|
||||
# Please read the documenation in 'mcppbs-doc.txt' for more details on
|
||||
# how the Modular C++ Build System works. For most new projects, a
|
||||
# developer will only need to make the following changes:
|
||||
#
|
||||
# - change the project metadata listed right below
|
||||
# - update the list of subprojects via the 'MCPPBS_SUBPROJECTS' macro
|
||||
# - possibly add subproject groups if needed to ease configuration
|
||||
# - add more configure checks for platform specific configuration
|
||||
#
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Project metadata
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
m4_define( proj_name, [RISC-V Proxy Kernel])
|
||||
m4_define( proj_maintainer, [Andrew Waterman])
|
||||
m4_define( proj_abbreviation, [riscv-pk])
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Project version information
|
||||
#-------------------------------------------------------------------------
|
||||
# Version information is meant to be managed through a version control
|
||||
# system's tags and revision numbers. In a working copy the version will
|
||||
# not be defined here (you should just use the version control system's
|
||||
# mechanisms). When we make a distribution then we can set the version
|
||||
# here as formed by the scripts/vcs-version.sh script so that the
|
||||
# distribution knows what version it came from. If you are not using
|
||||
# version control then it is fine to set this directly.
|
||||
|
||||
m4_define( proj_version, [?])
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Setup
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
AC_INIT(proj_name,proj_version,proj_maintainer,proj_abbreviation)
|
||||
AC_CONFIG_SRCDIR([pk/pk.h])
|
||||
AC_CONFIG_AUX_DIR([scripts])
|
||||
AC_CANONICAL_BUILD
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Checks for programs
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AC_CHECK_TOOL([AR],[ar])
|
||||
AC_CHECK_TOOL([RANLIB],[ranlib])
|
||||
AC_CHECK_TOOL([READELF],[readelf])
|
||||
AC_CHECK_TOOL([OBJCOPY],[objcopy])
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# MCPPBS specific program checks
|
||||
#-------------------------------------------------------------------------
|
||||
# These macros check to see if we can do a stow-based install and also
|
||||
# check for an isa simulator suitable for running the unit test programs
|
||||
# via the makefile.
|
||||
|
||||
MCPPBS_PROG_INSTALL
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Checks for header files
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
AC_HEADER_STDC
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Register RISCV environment variable
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
AC_ARG_VAR(RISCV, [top-level RISC-V install directory])
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Set compiler flags
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
default_CFLAGS="-Wall -Werror -D__NO_INLINE__ -mcmodel=medany -O2 -std=gnu99 -Wno-unused -Wno-attributes -fno-delete-null-pointer-checks"
|
||||
|
||||
AC_ARG_ENABLE([32bit],
|
||||
AS_HELP_STRING([--enable-32bit], [Build a 32-bit pk]),
|
||||
BUILD_32BIT=$enableval,
|
||||
BUILD_32BIT=no)
|
||||
|
||||
case "${BUILD_32BIT}" in
|
||||
yes|default)
|
||||
echo "Building 32-bit pk"
|
||||
CFLAGS="$default_CFLAGS -march=rv32iac -mabi=ilp32"
|
||||
LDFLAGS="-march=rv32iac -mabi=ilp32"
|
||||
install_subdir="riscv32-unknown-elf"
|
||||
;;
|
||||
*)
|
||||
CFLAGS="$default_CFLAGS"
|
||||
LDFLAGS=
|
||||
install_subdir="riscv64-unknown-elf"
|
||||
;;
|
||||
esac
|
||||
|
||||
AC_ARG_ENABLE([print-device-tree], AS_HELP_STRING([--enable-print-device-tree], [Print DTS when booting]))
|
||||
AS_IF([test "x$enable_print_device_tree" == "xyes"], [
|
||||
AC_DEFINE([PK_PRINT_DEVICE_TREE],,[Define if the DTS is to be displayed])
|
||||
])
|
||||
|
||||
AC_SUBST(CFLAGS)
|
||||
AC_SUBST(LDFLAGS)
|
||||
AC_SUBST([LIBS], ["-lgcc"])
|
||||
AC_SUBST(install_subdir)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# MCPPBS subproject list
|
||||
#-------------------------------------------------------------------------
|
||||
# Order list so that subprojects only depend on those listed earlier.
|
||||
# The '*' suffix indicates an optional subproject. The '**' suffix
|
||||
# indicates an optional subproject which is also the name of a group.
|
||||
|
||||
MCPPBS_SUBPROJECTS([ pk, bbl, dummy_payload, machine, util ])
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# MCPPBS subproject groups
|
||||
#-------------------------------------------------------------------------
|
||||
# If a group has the same name as a subproject then you must add the
|
||||
# '**' suffix in the subproject list above. The list of subprojects in a
|
||||
# group should be ordered so that subprojets only depend on those listed
|
||||
# earlier. Here is an example:
|
||||
#
|
||||
# MCPPBS_GROUP( [group-name], [sproja,sprojb,...] )
|
||||
#
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Output
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_OUTPUT
|
@ -1,22 +0,0 @@
|
||||
#include "mcall.h"
|
||||
|
||||
.section ".text.init"
|
||||
.globl _start
|
||||
_start:
|
||||
la s0, str
|
||||
1:
|
||||
lbu a0, (s0)
|
||||
beqz a0, 1f
|
||||
li a7, SBI_CONSOLE_PUTCHAR
|
||||
ecall
|
||||
add s0, s0, 1
|
||||
j 1b
|
||||
|
||||
1:
|
||||
li a7, SBI_SHUTDOWN
|
||||
ecall
|
||||
|
||||
.data
|
||||
str:
|
||||
.asciz "This is bbl's dummy_payload. To boot a real kernel, reconfigure\n\
|
||||
bbl with the flag --with-payload=PATH, then rebuild bbl.\n"
|
@ -1,7 +0,0 @@
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS {
|
||||
. = -0x80000000;
|
||||
|
||||
.text.init : { *(.text.init) }
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
dummy_payload_subproject_deps = \
|
||||
|
||||
dummy_payload_hdrs = \
|
||||
|
||||
dummy_payload_c_srcs = \
|
||||
|
||||
dummy_payload_asm_srcs = \
|
||||
dummy_entry.S \
|
||||
|
||||
dummy_payload_test_srcs =
|
||||
|
||||
dummy_payload_install_prog_srcs = \
|
||||
dummy_payload.c \
|
@ -1,78 +0,0 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
#ifndef _RISCV_ATOMIC_H
|
||||
#define _RISCV_ATOMIC_H
|
||||
|
||||
#include "config.h"
|
||||
#include "encoding.h"
|
||||
|
||||
// Currently, interrupts are always disabled in M-mode.
|
||||
#define disable_irqsave() (0)
|
||||
#define enable_irqrestore(flags) ((void) (flags))
|
||||
|
||||
typedef struct { int lock; } spinlock_t;
|
||||
#define SPINLOCK_INIT {0}
|
||||
|
||||
#define mb() asm volatile ("fence" ::: "memory")
|
||||
#define atomic_set(ptr, val) (*(volatile typeof(*(ptr)) *)(ptr) = val)
|
||||
#define atomic_read(ptr) (*(volatile typeof(*(ptr)) *)(ptr))
|
||||
|
||||
#ifdef __riscv_atomic
|
||||
# define atomic_add(ptr, inc) __sync_fetch_and_add(ptr, inc)
|
||||
# define atomic_or(ptr, inc) __sync_fetch_and_or(ptr, inc)
|
||||
# define atomic_swap(ptr, swp) __sync_lock_test_and_set(ptr, swp)
|
||||
# define atomic_cas(ptr, cmp, swp) __sync_val_compare_and_swap(ptr, cmp, swp)
|
||||
#else
|
||||
# define atomic_binop(ptr, inc, op) ({ \
|
||||
long flags = disable_irqsave(); \
|
||||
typeof(*(ptr)) res = atomic_read(ptr); \
|
||||
atomic_set(ptr, op); \
|
||||
enable_irqrestore(flags); \
|
||||
res; })
|
||||
# define atomic_add(ptr, inc) atomic_binop(ptr, inc, res + (inc))
|
||||
# define atomic_or(ptr, inc) atomic_binop(ptr, inc, res | (inc))
|
||||
# define atomic_swap(ptr, inc) atomic_binop(ptr, inc, (inc))
|
||||
# define atomic_cas(ptr, cmp, swp) ({ \
|
||||
long flags = disable_irqsave(); \
|
||||
typeof(*(ptr)) res = *(volatile typeof(*(ptr)) *)(ptr); \
|
||||
if (res == (cmp)) *(volatile typeof(ptr))(ptr) = (swp); \
|
||||
enable_irqrestore(flags); \
|
||||
res; })
|
||||
#endif
|
||||
|
||||
static inline int spinlock_trylock(spinlock_t* lock)
|
||||
{
|
||||
int res = atomic_swap(&lock->lock, -1);
|
||||
mb();
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline void spinlock_lock(spinlock_t* lock)
|
||||
{
|
||||
do
|
||||
{
|
||||
while (atomic_read(&lock->lock))
|
||||
;
|
||||
} while (spinlock_trylock(lock));
|
||||
}
|
||||
|
||||
static inline void spinlock_unlock(spinlock_t* lock)
|
||||
{
|
||||
mb();
|
||||
atomic_set(&lock->lock,0);
|
||||
}
|
||||
|
||||
static inline long spinlock_lock_irqsave(spinlock_t* lock)
|
||||
{
|
||||
long flags = disable_irqsave();
|
||||
spinlock_lock(lock);
|
||||
return flags;
|
||||
}
|
||||
|
||||
static inline void spinlock_unlock_irqrestore(spinlock_t* lock, long flags)
|
||||
{
|
||||
spinlock_unlock(lock);
|
||||
enable_irqrestore(flags);
|
||||
}
|
||||
|
||||
#endif
|
@ -1,35 +0,0 @@
|
||||
#ifndef _RISCV_BITS_H
|
||||
#define _RISCV_BITS_H
|
||||
|
||||
#define likely(x) __builtin_expect((x), 1)
|
||||
#define unlikely(x) __builtin_expect((x), 0)
|
||||
|
||||
#define ROUNDUP(a, b) ((((a)-1)/(b)+1)*(b))
|
||||
#define ROUNDDOWN(a, b) ((a)/(b)*(b))
|
||||
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define CLAMP(a, lo, hi) MIN(MAX(a, lo), hi)
|
||||
|
||||
#define EXTRACT_FIELD(val, which) (((val) & (which)) / ((which) & ~((which)-1)))
|
||||
#define INSERT_FIELD(val, which, fieldval) (((val) & ~(which)) | ((fieldval) * ((which) & ~((which)-1))))
|
||||
|
||||
#define STR(x) XSTR(x)
|
||||
#define XSTR(x) #x
|
||||
|
||||
#if __riscv_xlen == 64
|
||||
# define SLL32 sllw
|
||||
# define STORE sd
|
||||
# define LOAD ld
|
||||
# define LWU lwu
|
||||
# define LOG_REGBYTES 3
|
||||
#else
|
||||
# define SLL32 sll
|
||||
# define STORE sw
|
||||
# define LOAD lw
|
||||
# define LWU lw
|
||||
# define LOG_REGBYTES 2
|
||||
#endif
|
||||
#define REGBYTES (1 << LOG_REGBYTES)
|
||||
|
||||
#endif
|
@ -1,4 +0,0 @@
|
||||
#ifndef DISABLED_HART_MASK_H
|
||||
#define DISABLED_HART_MASK_H
|
||||
extern long disabled_hart_mask;
|
||||
#endif
|
@ -1,234 +0,0 @@
|
||||
#include "emulation.h"
|
||||
//#include "fp_emulation.h"
|
||||
#include "config.h"
|
||||
#include "unprivileged_memory.h"
|
||||
#include "mtrap.h"
|
||||
#include <limits.h>
|
||||
|
||||
static DECLARE_EMULATION_FUNC(emulate_rvc)
|
||||
{
|
||||
#ifdef __riscv_compressed
|
||||
// the only emulable RVC instructions are FP loads and stores.
|
||||
# if !defined(__riscv_flen) && defined(PK_ENABLE_FP_EMULATION)
|
||||
write_csr(mepc, mepc + 2);
|
||||
|
||||
// if FPU is disabled, punt back to the OS
|
||||
if (unlikely((mstatus & MSTATUS_FS) == 0))
|
||||
return truly_illegal_insn(regs, mcause, mepc, mstatus, insn);
|
||||
|
||||
if ((insn & MASK_C_FLD) == MATCH_C_FLD) {
|
||||
uintptr_t addr = GET_RS1S(insn, regs) + RVC_LD_IMM(insn);
|
||||
if (unlikely(addr % sizeof(uintptr_t)))
|
||||
return misaligned_load_trap(regs, mcause, mepc);
|
||||
SET_F64_RD(RVC_RS2S(insn) << SH_RD, regs, load_uint64_t((void *)addr, mepc));
|
||||
} else if ((insn & MASK_C_FLDSP) == MATCH_C_FLDSP) {
|
||||
uintptr_t addr = GET_SP(regs) + RVC_LDSP_IMM(insn);
|
||||
if (unlikely(addr % sizeof(uintptr_t)))
|
||||
return misaligned_load_trap(regs, mcause, mepc);
|
||||
SET_F64_RD(insn, regs, load_uint64_t((void *)addr, mepc));
|
||||
} else if ((insn & MASK_C_FSD) == MATCH_C_FSD) {
|
||||
uintptr_t addr = GET_RS1S(insn, regs) + RVC_LD_IMM(insn);
|
||||
if (unlikely(addr % sizeof(uintptr_t)))
|
||||
return misaligned_store_trap(regs, mcause, mepc);
|
||||
store_uint64_t((void *)addr, GET_F64_RS2(RVC_RS2S(insn) << SH_RS2, regs), mepc);
|
||||
} else if ((insn & MASK_C_FSDSP) == MATCH_C_FSDSP) {
|
||||
uintptr_t addr = GET_SP(regs) + RVC_SDSP_IMM(insn);
|
||||
if (unlikely(addr % sizeof(uintptr_t)))
|
||||
return misaligned_store_trap(regs, mcause, mepc);
|
||||
store_uint64_t((void *)addr, GET_F64_RS2(RVC_RS2(insn) << SH_RS2, regs), mepc);
|
||||
} else
|
||||
# if __riscv_xlen == 32
|
||||
if ((insn & MASK_C_FLW) == MATCH_C_FLW) {
|
||||
uintptr_t addr = GET_RS1S(insn, regs) + RVC_LW_IMM(insn);
|
||||
if (unlikely(addr % 4))
|
||||
return misaligned_load_trap(regs, mcause, mepc);
|
||||
SET_F32_RD(RVC_RS2S(insn) << SH_RD, regs, load_int32_t((void *)addr, mepc));
|
||||
} else if ((insn & MASK_C_FLWSP) == MATCH_C_FLWSP) {
|
||||
uintptr_t addr = GET_SP(regs) + RVC_LWSP_IMM(insn);
|
||||
if (unlikely(addr % 4))
|
||||
return misaligned_load_trap(regs, mcause, mepc);
|
||||
SET_F32_RD(insn, regs, load_int32_t((void *)addr, mepc));
|
||||
} else if ((insn & MASK_C_FSW) == MATCH_C_FSW) {
|
||||
uintptr_t addr = GET_RS1S(insn, regs) + RVC_LW_IMM(insn);
|
||||
if (unlikely(addr % 4))
|
||||
return misaligned_store_trap(regs, mcause, mepc);
|
||||
store_uint32_t((void *)addr, GET_F32_RS2(RVC_RS2S(insn) << SH_RS2, regs), mepc);
|
||||
} else if ((insn & MASK_C_FSWSP) == MATCH_C_FSWSP) {
|
||||
uintptr_t addr = GET_SP(regs) + RVC_SWSP_IMM(insn);
|
||||
if (unlikely(addr % 4))
|
||||
return misaligned_store_trap(regs, mcause, mepc);
|
||||
store_uint32_t((void *)addr, GET_F32_RS2(RVC_RS2(insn) << SH_RS2, regs), mepc);
|
||||
} else
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
return truly_illegal_insn(regs, mcause, mepc, mstatus, insn);
|
||||
}
|
||||
|
||||
void illegal_insn_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc)
|
||||
{
|
||||
asm (".pushsection .rodata\n"
|
||||
"illegal_insn_trap_table:\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
#if !defined(__riscv_flen) && defined(PK_ENABLE_FP_EMULATION)
|
||||
" .word emulate_float_load\n"
|
||||
#else
|
||||
" .word truly_illegal_insn\n"
|
||||
#endif
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
#if !defined(__riscv_flen) && defined(PK_ENABLE_FP_EMULATION)
|
||||
" .word emulate_float_store\n"
|
||||
#else
|
||||
" .word truly_illegal_insn\n"
|
||||
#endif
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
#if !defined(__riscv_muldiv)
|
||||
" .word emulate_mul_div\n"
|
||||
#else
|
||||
" .word truly_illegal_insn\n"
|
||||
#endif
|
||||
" .word truly_illegal_insn\n"
|
||||
#if !defined(__riscv_muldiv) && __riscv_xlen >= 64
|
||||
" .word emulate_mul_div32\n"
|
||||
#else
|
||||
" .word truly_illegal_insn\n"
|
||||
#endif
|
||||
" .word truly_illegal_insn\n"
|
||||
#ifdef PK_ENABLE_FP_EMULATION
|
||||
" .word emulate_fmadd\n"
|
||||
" .word emulate_fmadd\n"
|
||||
" .word emulate_fmadd\n"
|
||||
" .word emulate_fmadd\n"
|
||||
" .word emulate_fp\n"
|
||||
#else
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
#endif
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word emulate_system_opcode\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
" .word truly_illegal_insn\n"
|
||||
" .popsection");
|
||||
|
||||
uintptr_t mstatus = read_csr(mstatus);
|
||||
insn_t insn = read_csr(mbadaddr);
|
||||
|
||||
if (unlikely((insn & 3) != 3)) {
|
||||
if (insn == 0)
|
||||
insn = get_insn(mepc, &mstatus);
|
||||
if ((insn & 3) != 3)
|
||||
return emulate_rvc(regs, mcause, mepc, mstatus, insn);
|
||||
}
|
||||
|
||||
write_csr(mepc, mepc + 4);
|
||||
|
||||
extern uint32_t illegal_insn_trap_table[];
|
||||
uint32_t* pf = (void*)illegal_insn_trap_table + (insn & 0x7c);
|
||||
emulation_func f = (emulation_func)(uintptr_t)*pf;
|
||||
f(regs, mcause, mepc, mstatus, insn);
|
||||
}
|
||||
|
||||
__attribute__((noinline))
|
||||
DECLARE_EMULATION_FUNC(truly_illegal_insn)
|
||||
{
|
||||
return redirect_trap(mepc, mstatus, insn);
|
||||
}
|
||||
|
||||
static inline int emulate_read_csr(int num, uintptr_t mstatus, uintptr_t* result)
|
||||
{
|
||||
uintptr_t counteren = -1;
|
||||
if (EXTRACT_FIELD(mstatus, MSTATUS_MPP) == PRV_U)
|
||||
counteren = read_csr(scounteren);
|
||||
|
||||
switch (num)
|
||||
{
|
||||
case CSR_TIME:
|
||||
if (!((counteren >> (CSR_TIME - CSR_CYCLE)) & 1))
|
||||
return -1;
|
||||
*result = *mtime;
|
||||
return 0;
|
||||
#if __riscv_xlen == 32
|
||||
case CSR_TIMEH:
|
||||
if (!((counteren >> (CSR_TIME - CSR_CYCLE)) & 1))
|
||||
return -1;
|
||||
*result = *mtime >> 32;
|
||||
return 0;
|
||||
#endif
|
||||
#if !defined(__riscv_flen) && defined(PK_ENABLE_FP_EMULATION)
|
||||
case CSR_FRM:
|
||||
if ((mstatus & MSTATUS_FS) == 0) break;
|
||||
*result = GET_FRM();
|
||||
return 0;
|
||||
case CSR_FFLAGS:
|
||||
if ((mstatus & MSTATUS_FS) == 0) break;
|
||||
*result = GET_FFLAGS();
|
||||
return 0;
|
||||
case CSR_FCSR:
|
||||
if ((mstatus & MSTATUS_FS) == 0) break;
|
||||
*result = GET_FCSR();
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline int emulate_write_csr(int num, uintptr_t value, uintptr_t mstatus)
|
||||
{
|
||||
switch (num)
|
||||
{
|
||||
#if !defined(__riscv_flen) && defined(PK_ENABLE_FP_EMULATION)
|
||||
case CSR_FRM: SET_FRM(value); return 0;
|
||||
case CSR_FFLAGS: SET_FFLAGS(value); return 0;
|
||||
case CSR_FCSR: SET_FCSR(value); return 0;
|
||||
#endif
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
DECLARE_EMULATION_FUNC(emulate_system_opcode)
|
||||
{
|
||||
int rs1_num = (insn >> 15) & 0x1f;
|
||||
uintptr_t rs1_val = GET_RS1(insn, regs);
|
||||
int csr_num = (uint32_t)insn >> 20;
|
||||
uintptr_t csr_val, new_csr_val;
|
||||
|
||||
if (emulate_read_csr(csr_num, mstatus, &csr_val))
|
||||
return truly_illegal_insn(regs, mcause, mepc, mstatus, insn);
|
||||
|
||||
int do_write = rs1_num;
|
||||
#if 0
|
||||
switch (GET_RM(insn))
|
||||
{
|
||||
case 0: return truly_illegal_insn(regs, mcause, mepc, mstatus, insn);
|
||||
case 1: new_csr_val = rs1_val; do_write = 1; break;
|
||||
case 2: new_csr_val = csr_val | rs1_val; break;
|
||||
case 3: new_csr_val = csr_val & ~rs1_val; break;
|
||||
case 4: return truly_illegal_insn(regs, mcause, mepc, mstatus, insn);
|
||||
case 5: new_csr_val = rs1_num; do_write = 1; break;
|
||||
case 6: new_csr_val = csr_val | rs1_num; break;
|
||||
case 7: new_csr_val = csr_val & ~rs1_num; break;
|
||||
}
|
||||
#endif
|
||||
if (do_write && emulate_write_csr(csr_num, new_csr_val, mstatus))
|
||||
return truly_illegal_insn(regs, mcause, mepc, mstatus, insn);
|
||||
|
||||
SET_RD(insn, regs, csr_val);
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
#ifndef _RISCV_EMULATION_H
|
||||
#define _RISCV_EMULATION_H
|
||||
|
||||
#include "encoding.h"
|
||||
#include "bits.h"
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uintptr_t insn_t;
|
||||
typedef void (*emulation_func)(uintptr_t*, uintptr_t, uintptr_t, uintptr_t, insn_t);
|
||||
#define DECLARE_EMULATION_FUNC(name) void name(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc, uintptr_t mstatus, insn_t insn)
|
||||
|
||||
void misaligned_load_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc);
|
||||
void misaligned_store_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc);
|
||||
void redirect_trap(uintptr_t epc, uintptr_t mstatus, uintptr_t badaddr);
|
||||
DECLARE_EMULATION_FUNC(truly_illegal_insn);
|
||||
DECLARE_EMULATION_FUNC(emulate_rvc_0);
|
||||
DECLARE_EMULATION_FUNC(emulate_rvc_2);
|
||||
|
||||
#define SH_RD 7
|
||||
#define SH_RS1 15
|
||||
#define SH_RS2 20
|
||||
#define SH_RS2C 2
|
||||
|
||||
#define RV_X(x, s, n) (((x) >> (s)) & ((1 << (n)) - 1))
|
||||
#define RVC_LW_IMM(x) ((RV_X(x, 6, 1) << 2) | (RV_X(x, 10, 3) << 3) | (RV_X(x, 5, 1) << 6))
|
||||
#define RVC_LD_IMM(x) ((RV_X(x, 10, 3) << 3) | (RV_X(x, 5, 2) << 6))
|
||||
#define RVC_LWSP_IMM(x) ((RV_X(x, 4, 3) << 2) | (RV_X(x, 12, 1) << 5) | (RV_X(x, 2, 2) << 6))
|
||||
#define RVC_LDSP_IMM(x) ((RV_X(x, 5, 2) << 3) | (RV_X(x, 12, 1) << 5) | (RV_X(x, 2, 3) << 6))
|
||||
#define RVC_SWSP_IMM(x) ((RV_X(x, 9, 4) << 2) | (RV_X(x, 7, 2) << 6))
|
||||
#define RVC_SDSP_IMM(x) ((RV_X(x, 10, 3) << 3) | (RV_X(x, 7, 3) << 6))
|
||||
#define RVC_RS1S(insn) (8 + RV_X(insn, SH_RD, 3))
|
||||
#define RVC_RS2S(insn) (8 + RV_X(insn, SH_RS2C, 3))
|
||||
#define RVC_RS2(insn) RV_X(insn, SH_RS2C, 5)
|
||||
|
||||
#define SHIFT_RIGHT(x, y) ((y) < 0 ? ((x) << -(y)) : ((x) >> (y)))
|
||||
#define GET_REG(insn, pos, regs) ({ \
|
||||
int mask = (1 << (5+LOG_REGBYTES)) - (1 << LOG_REGBYTES); \
|
||||
(uintptr_t*)((uintptr_t)regs + (SHIFT_RIGHT(insn, (pos) - LOG_REGBYTES) & (mask))); \
|
||||
})
|
||||
#define GET_RS1(insn, regs) (*GET_REG(insn, SH_RS1, regs))
|
||||
#define GET_RS2(insn, regs) (*GET_REG(insn, SH_RS2, regs))
|
||||
#define GET_RS1S(insn, regs) (*GET_REG(RVC_RS1S(insn), 0, regs))
|
||||
#define GET_RS2S(insn, regs) (*GET_REG(RVC_RS2S(insn), 0, regs))
|
||||
#define GET_RS2C(insn, regs) (*GET_REG(insn, SH_RS2C, regs))
|
||||
#define GET_SP(regs) (*GET_REG(2, 0, regs))
|
||||
#define SET_RD(insn, regs, val) (*GET_REG(insn, SH_RD, regs) = (val))
|
||||
#define IMM_I(insn) ((int32_t)(insn) >> 20)
|
||||
#define IMM_S(insn) (((int32_t)(insn) >> 25 << 5) | (int32_t)(((insn) >> 7) & 0x1f))
|
||||
#define MASK_FUNCT3 0x7000
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -1,740 +0,0 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "config.h"
|
||||
#include "fdt.h"
|
||||
#include "mtrap.h"
|
||||
|
||||
static inline uint32_t bswap(uint32_t x)
|
||||
{
|
||||
uint32_t y = (x & 0x00FF00FF) << 8 | (x & 0xFF00FF00) >> 8;
|
||||
uint32_t z = (y & 0x0000FFFF) << 16 | (y & 0xFFFF0000) >> 16;
|
||||
return z;
|
||||
}
|
||||
|
||||
static inline int isstring(char c)
|
||||
{
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
return 1;
|
||||
if (c >= 'a' && c <= 'z')
|
||||
return 1;
|
||||
if (c >= '0' && c <= '9')
|
||||
return 1;
|
||||
if (c == '\0' || c == ' ' || c == ',' || c == '-')
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t *fdt_scan_helper(
|
||||
uint32_t *lex,
|
||||
const char *strings,
|
||||
struct fdt_scan_node *node,
|
||||
const struct fdt_cb *cb)
|
||||
{
|
||||
struct fdt_scan_node child;
|
||||
struct fdt_scan_prop prop;
|
||||
int last = 0;
|
||||
|
||||
child.parent = node;
|
||||
// these are the default cell counts, as per the FDT spec
|
||||
child.address_cells = 2;
|
||||
child.size_cells = 1;
|
||||
prop.node = node;
|
||||
|
||||
while (1) {
|
||||
switch (bswap(lex[0])) {
|
||||
case FDT_NOP: {
|
||||
lex += 1;
|
||||
break;
|
||||
}
|
||||
case FDT_PROP: {
|
||||
assert (!last);
|
||||
prop.name = strings + bswap(lex[2]);
|
||||
prop.len = bswap(lex[1]);
|
||||
prop.value = lex + 3;
|
||||
if (node && !strcmp(prop.name, "#address-cells")) { node->address_cells = bswap(lex[3]); }
|
||||
if (node && !strcmp(prop.name, "#size-cells")) { node->size_cells = bswap(lex[3]); }
|
||||
lex += 3 + (prop.len+3)/4;
|
||||
cb->prop(&prop, cb->extra);
|
||||
break;
|
||||
}
|
||||
case FDT_BEGIN_NODE: {
|
||||
uint32_t *lex_next;
|
||||
if (!last && node && cb->done) cb->done(node, cb->extra);
|
||||
last = 1;
|
||||
child.name = (const char *)(lex+1);
|
||||
if (cb->open) cb->open(&child, cb->extra);
|
||||
lex_next = fdt_scan_helper(
|
||||
lex + 2 + strlen(child.name)/4,
|
||||
strings, &child, cb);
|
||||
if (cb->close && cb->close(&child, cb->extra) == -1)
|
||||
while (lex != lex_next) *lex++ = bswap(FDT_NOP);
|
||||
lex = lex_next;
|
||||
break;
|
||||
}
|
||||
case FDT_END_NODE: {
|
||||
if (!last && node && cb->done) cb->done(node, cb->extra);
|
||||
return lex + 1;
|
||||
}
|
||||
default: { // FDT_END
|
||||
if (!last && node && cb->done) cb->done(node, cb->extra);
|
||||
return lex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fdt_scan(uintptr_t fdt, const struct fdt_cb *cb)
|
||||
{
|
||||
struct fdt_header *header = (struct fdt_header *)fdt;
|
||||
|
||||
// Only process FDT that we understand
|
||||
if (bswap(header->magic) != FDT_MAGIC ||
|
||||
bswap(header->last_comp_version) > FDT_VERSION) return;
|
||||
|
||||
const char *strings = (const char *)(fdt + bswap(header->off_dt_strings));
|
||||
uint32_t *lex = (uint32_t *)(fdt + bswap(header->off_dt_struct));
|
||||
|
||||
fdt_scan_helper(lex, strings, 0, cb);
|
||||
}
|
||||
|
||||
uint32_t fdt_size(uintptr_t fdt)
|
||||
{
|
||||
struct fdt_header *header = (struct fdt_header *)fdt;
|
||||
|
||||
// Only process FDT that we understand
|
||||
if (bswap(header->magic) != FDT_MAGIC ||
|
||||
bswap(header->last_comp_version) > FDT_VERSION) return 0;
|
||||
return bswap(header->totalsize);
|
||||
}
|
||||
|
||||
const uint32_t *fdt_get_address(const struct fdt_scan_node *node, const uint32_t *value, uint64_t *result)
|
||||
{
|
||||
*result = 0;
|
||||
for (int cells = node->address_cells; cells > 0; --cells)
|
||||
*result = (*result << 32) + bswap(*value++);
|
||||
return value;
|
||||
}
|
||||
|
||||
const uint32_t *fdt_get_size(const struct fdt_scan_node *node, const uint32_t *value, uint64_t *result)
|
||||
{
|
||||
*result = 0;
|
||||
for (int cells = node->size_cells; cells > 0; --cells)
|
||||
*result = (*result << 32) + bswap(*value++);
|
||||
return value;
|
||||
}
|
||||
|
||||
int fdt_string_list_index(const struct fdt_scan_prop *prop, const char *str)
|
||||
{
|
||||
const char *list = (const char *)prop->value;
|
||||
const char *end = list + prop->len;
|
||||
int index = 0;
|
||||
while (end - list > 0) {
|
||||
if (!strcmp(list, str)) return index;
|
||||
++index;
|
||||
list += strlen(list) + 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////// MEMORY SCAN /////////////////////////////////////////
|
||||
|
||||
struct mem_scan {
|
||||
int memory;
|
||||
const uint32_t *reg_value;
|
||||
int reg_len;
|
||||
};
|
||||
|
||||
static void mem_open(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct mem_scan *scan = (struct mem_scan *)extra;
|
||||
memset(scan, 0, sizeof(*scan));
|
||||
}
|
||||
|
||||
static void mem_prop(const struct fdt_scan_prop *prop, void *extra)
|
||||
{
|
||||
struct mem_scan *scan = (struct mem_scan *)extra;
|
||||
if (!strcmp(prop->name, "device_type") && !strcmp((const char*)prop->value, "memory")) {
|
||||
scan->memory = 1;
|
||||
} else if (!strcmp(prop->name, "reg")) {
|
||||
scan->reg_value = prop->value;
|
||||
scan->reg_len = prop->len;
|
||||
}
|
||||
}
|
||||
|
||||
static void mem_done(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct mem_scan *scan = (struct mem_scan *)extra;
|
||||
const uint32_t *value = scan->reg_value;
|
||||
const uint32_t *end = value + scan->reg_len/4;
|
||||
uintptr_t self = (uintptr_t)mem_done;
|
||||
|
||||
if (!scan->memory) return;
|
||||
assert (scan->reg_value && scan->reg_len % 4 == 0);
|
||||
|
||||
while (end - value > 0) {
|
||||
uint64_t base, size;
|
||||
value = fdt_get_address(node->parent, value, &base);
|
||||
value = fdt_get_size (node->parent, value, &size);
|
||||
if (base <= self && self <= base + size) { mem_size = size; }
|
||||
}
|
||||
assert (end == value);
|
||||
}
|
||||
|
||||
void query_mem(uintptr_t fdt)
|
||||
{
|
||||
struct fdt_cb cb;
|
||||
struct mem_scan scan;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.open = mem_open;
|
||||
cb.prop = mem_prop;
|
||||
cb.done = mem_done;
|
||||
cb.extra = &scan;
|
||||
|
||||
mem_size = 0;
|
||||
fdt_scan(fdt, &cb);
|
||||
assert (mem_size > 0);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////// HART SCAN //////////////////////////////////////////
|
||||
|
||||
static uint32_t hart_phandles[MAX_HARTS];
|
||||
uint64_t hart_mask;
|
||||
|
||||
struct hart_scan {
|
||||
const struct fdt_scan_node *cpu;
|
||||
int hart;
|
||||
const struct fdt_scan_node *controller;
|
||||
int cells;
|
||||
uint32_t phandle;
|
||||
};
|
||||
|
||||
static void hart_open(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct hart_scan *scan = (struct hart_scan *)extra;
|
||||
if (!scan->cpu) {
|
||||
scan->hart = -1;
|
||||
}
|
||||
if (!scan->controller) {
|
||||
scan->cells = 0;
|
||||
scan->phandle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void hart_prop(const struct fdt_scan_prop *prop, void *extra)
|
||||
{
|
||||
struct hart_scan *scan = (struct hart_scan *)extra;
|
||||
if (!strcmp(prop->name, "device_type") && !strcmp((const char*)prop->value, "cpu")) {
|
||||
assert (!scan->cpu);
|
||||
scan->cpu = prop->node;
|
||||
} else if (!strcmp(prop->name, "interrupt-controller")) {
|
||||
assert (!scan->controller);
|
||||
scan->controller = prop->node;
|
||||
} else if (!strcmp(prop->name, "#interrupt-cells")) {
|
||||
scan->cells = bswap(prop->value[0]);
|
||||
} else if (!strcmp(prop->name, "phandle")) {
|
||||
scan->phandle = bswap(prop->value[0]);
|
||||
} else if (!strcmp(prop->name, "reg")) {
|
||||
uint64_t reg;
|
||||
fdt_get_address(prop->node->parent, prop->value, ®);
|
||||
scan->hart = reg;
|
||||
}
|
||||
}
|
||||
|
||||
static void hart_done(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct hart_scan *scan = (struct hart_scan *)extra;
|
||||
|
||||
if (scan->cpu == node) {
|
||||
assert (scan->hart >= 0);
|
||||
}
|
||||
|
||||
if (scan->controller == node && scan->cpu) {
|
||||
assert (scan->phandle > 0);
|
||||
assert (scan->cells == 1);
|
||||
|
||||
if (scan->hart < MAX_HARTS) {
|
||||
hart_phandles[scan->hart] = scan->phandle;
|
||||
hart_mask |= 1 << scan->hart;
|
||||
hls_init(scan->hart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int hart_close(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct hart_scan *scan = (struct hart_scan *)extra;
|
||||
if (scan->cpu == node) scan->cpu = 0;
|
||||
if (scan->controller == node) scan->controller = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void query_harts(uintptr_t fdt)
|
||||
{
|
||||
struct fdt_cb cb;
|
||||
struct hart_scan scan;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
memset(&scan, 0, sizeof(scan));
|
||||
cb.open = hart_open;
|
||||
cb.prop = hart_prop;
|
||||
cb.done = hart_done;
|
||||
cb.close= hart_close;
|
||||
cb.extra = &scan;
|
||||
|
||||
fdt_scan(fdt, &cb);
|
||||
|
||||
// The current hart should have been detected
|
||||
assert ((hart_mask >> read_csr(mhartid)) != 0);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////// CLINT SCAN /////////////////////////////////////////
|
||||
|
||||
struct clint_scan
|
||||
{
|
||||
int compat;
|
||||
uint64_t reg;
|
||||
const uint32_t *int_value;
|
||||
int int_len;
|
||||
int done;
|
||||
};
|
||||
|
||||
static void clint_open(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct clint_scan *scan = (struct clint_scan *)extra;
|
||||
scan->compat = 0;
|
||||
scan->reg = 0;
|
||||
scan->int_value = 0;
|
||||
}
|
||||
|
||||
static void clint_prop(const struct fdt_scan_prop *prop, void *extra)
|
||||
{
|
||||
struct clint_scan *scan = (struct clint_scan *)extra;
|
||||
if (!strcmp(prop->name, "compatible") && fdt_string_list_index(prop, "riscv,clint0") >= 0) {
|
||||
scan->compat = 1;
|
||||
} else if (!strcmp(prop->name, "reg")) {
|
||||
fdt_get_address(prop->node->parent, prop->value, &scan->reg);
|
||||
} else if (!strcmp(prop->name, "interrupts-extended")) {
|
||||
scan->int_value = prop->value;
|
||||
scan->int_len = prop->len;
|
||||
}
|
||||
}
|
||||
|
||||
static void clint_done(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct clint_scan *scan = (struct clint_scan *)extra;
|
||||
const uint32_t *value = scan->int_value;
|
||||
const uint32_t *end = value + scan->int_len/4;
|
||||
|
||||
if (!scan->compat) return;
|
||||
assert (scan->reg != 0);
|
||||
assert (scan->int_value && scan->int_len % 16 == 0);
|
||||
assert (!scan->done); // only one clint
|
||||
|
||||
scan->done = 1;
|
||||
mtime = (void*)((uintptr_t)scan->reg + 0xbff8);
|
||||
|
||||
for (int index = 0; end - value > 0; ++index) {
|
||||
uint32_t phandle = bswap(value[0]);
|
||||
int hart;
|
||||
for (hart = 0; hart < MAX_HARTS; ++hart)
|
||||
if (hart_phandles[hart] == phandle)
|
||||
break;
|
||||
if (hart < MAX_HARTS) {
|
||||
hls_t *hls = OTHER_HLS(hart);
|
||||
hls->ipi = (void*)((uintptr_t)scan->reg + index * 4);
|
||||
hls->timecmp = (void*)((uintptr_t)scan->reg + 0x4000 + (index * 8));
|
||||
}
|
||||
value += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void query_clint(uintptr_t fdt)
|
||||
{
|
||||
struct fdt_cb cb;
|
||||
struct clint_scan scan;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.open = clint_open;
|
||||
cb.prop = clint_prop;
|
||||
cb.done = clint_done;
|
||||
cb.extra = &scan;
|
||||
|
||||
scan.done = 0;
|
||||
fdt_scan(fdt, &cb);
|
||||
assert (scan.done);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////// PLIC SCAN /////////////////////////////////////////
|
||||
|
||||
struct plic_scan
|
||||
{
|
||||
int compat;
|
||||
uint64_t reg;
|
||||
uint32_t *int_value;
|
||||
int int_len;
|
||||
int done;
|
||||
int ndev;
|
||||
};
|
||||
|
||||
static void plic_open(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct plic_scan *scan = (struct plic_scan *)extra;
|
||||
scan->compat = 0;
|
||||
scan->reg = 0;
|
||||
scan->int_value = 0;
|
||||
}
|
||||
|
||||
static void plic_prop(const struct fdt_scan_prop *prop, void *extra)
|
||||
{
|
||||
struct plic_scan *scan = (struct plic_scan *)extra;
|
||||
if (!strcmp(prop->name, "compatible") && fdt_string_list_index(prop, "riscv,plic0") >= 0) {
|
||||
scan->compat = 1;
|
||||
} else if (!strcmp(prop->name, "reg")) {
|
||||
fdt_get_address(prop->node->parent, prop->value, &scan->reg);
|
||||
} else if (!strcmp(prop->name, "interrupts-extended")) {
|
||||
scan->int_value = prop->value;
|
||||
scan->int_len = prop->len;
|
||||
} else if (!strcmp(prop->name, "riscv,ndev")) {
|
||||
scan->ndev = bswap(prop->value[0]);
|
||||
}
|
||||
}
|
||||
|
||||
#define HART_BASE 0x200000
|
||||
#define HART_SIZE 0x1000
|
||||
#define ENABLE_BASE 0x2000
|
||||
#define ENABLE_SIZE 0x80
|
||||
|
||||
static void plic_done(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct plic_scan *scan = (struct plic_scan *)extra;
|
||||
const uint32_t *value = scan->int_value;
|
||||
const uint32_t *end = value + scan->int_len/4;
|
||||
|
||||
if (!scan->compat) return;
|
||||
assert (scan->reg != 0);
|
||||
assert (scan->int_value && scan->int_len % 8 == 0);
|
||||
assert (scan->ndev >= 0 && scan->ndev < 1024);
|
||||
assert (!scan->done); // only one plic
|
||||
|
||||
scan->done = 1;
|
||||
plic_priorities = (uint32_t*)(uintptr_t)scan->reg;
|
||||
plic_ndevs = scan->ndev;
|
||||
|
||||
for (int index = 0; end - value > 0; ++index) {
|
||||
uint32_t phandle = bswap(value[0]);
|
||||
uint32_t cpu_int = bswap(value[1]);
|
||||
int hart;
|
||||
for (hart = 0; hart < MAX_HARTS; ++hart)
|
||||
if (hart_phandles[hart] == phandle)
|
||||
break;
|
||||
if (hart < MAX_HARTS) {
|
||||
hls_t *hls = OTHER_HLS(hart);
|
||||
if (cpu_int == IRQ_M_EXT) {
|
||||
hls->plic_m_ie = (uintptr_t*)((uintptr_t)scan->reg + ENABLE_BASE + ENABLE_SIZE * index);
|
||||
hls->plic_m_thresh = (uint32_t*) ((uintptr_t)scan->reg + HART_BASE + HART_SIZE * index);
|
||||
} else if (cpu_int == IRQ_S_EXT) {
|
||||
hls->plic_s_ie = (uintptr_t*)((uintptr_t)scan->reg + ENABLE_BASE + ENABLE_SIZE * index);
|
||||
hls->plic_s_thresh = (uint32_t*) ((uintptr_t)scan->reg + HART_BASE + HART_SIZE * index);
|
||||
} else {
|
||||
printm("PLIC wired hart %d to wrong interrupt %d", hart, cpu_int);
|
||||
}
|
||||
}
|
||||
value += 2;
|
||||
}
|
||||
#if 0
|
||||
printm("PLIC: prio %x devs %d\r\n", (uint32_t)(uintptr_t)plic_priorities, plic_ndevs);
|
||||
for (int i = 0; i < MAX_HARTS; ++i) {
|
||||
hls_t *hls = OTHER_HLS(i);
|
||||
printm("CPU %d: %x %x %x %x\r\n", i, (uint32_t)(uintptr_t)hls->plic_m_ie, (uint32_t)(uintptr_t)hls->plic_m_thresh, (uint32_t)(uintptr_t)hls->plic_s_ie, (uint32_t)(uintptr_t)hls->plic_s_thresh);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void query_plic(uintptr_t fdt)
|
||||
{
|
||||
struct fdt_cb cb;
|
||||
struct plic_scan scan;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.open = plic_open;
|
||||
cb.prop = plic_prop;
|
||||
cb.done = plic_done;
|
||||
cb.extra = &scan;
|
||||
|
||||
scan.done = 0;
|
||||
fdt_scan(fdt, &cb);
|
||||
}
|
||||
|
||||
static void plic_redact(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct plic_scan *scan = (struct plic_scan *)extra;
|
||||
uint32_t *value = scan->int_value;
|
||||
uint32_t *end = value + scan->int_len/4;
|
||||
|
||||
if (!scan->compat) return;
|
||||
scan->done = 1;
|
||||
|
||||
while (end - value > 0) {
|
||||
if (bswap(value[1]) == IRQ_M_EXT) value[1] = bswap(-1);
|
||||
value += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void filter_plic(uintptr_t fdt)
|
||||
{
|
||||
struct fdt_cb cb;
|
||||
struct plic_scan scan;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.open = plic_open;
|
||||
cb.prop = plic_prop;
|
||||
cb.done = plic_redact;
|
||||
cb.extra = &scan;
|
||||
|
||||
scan.done = 0;
|
||||
fdt_scan(fdt, &cb);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////// COMPAT SCAN ////////////////////////////////////////
|
||||
|
||||
struct compat_scan
|
||||
{
|
||||
const char *compat;
|
||||
int depth;
|
||||
int kill;
|
||||
};
|
||||
|
||||
static void compat_open(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct compat_scan *scan = (struct compat_scan *)extra;
|
||||
++scan->depth;
|
||||
}
|
||||
|
||||
static void compat_prop(const struct fdt_scan_prop *prop, void *extra)
|
||||
{
|
||||
struct compat_scan *scan = (struct compat_scan *)extra;
|
||||
if (!strcmp(prop->name, "compatible") && fdt_string_list_index(prop, scan->compat) >= 0)
|
||||
if (scan->depth < scan->kill)
|
||||
scan->kill = scan->depth;
|
||||
}
|
||||
|
||||
static int compat_close(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct compat_scan *scan = (struct compat_scan *)extra;
|
||||
if (scan->kill == scan->depth--) {
|
||||
scan->kill = 999;
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void filter_compat(uintptr_t fdt, const char *compat)
|
||||
{
|
||||
struct fdt_cb cb;
|
||||
struct compat_scan scan;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.open = compat_open;
|
||||
cb.prop = compat_prop;
|
||||
cb.close = compat_close;
|
||||
cb.extra = &scan;
|
||||
|
||||
scan.compat = compat;
|
||||
scan.depth = 0;
|
||||
scan.kill = 999;
|
||||
fdt_scan(fdt, &cb);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////// HART FILTER ////////////////////////////////////////
|
||||
|
||||
struct hart_filter {
|
||||
int compat;
|
||||
int hart;
|
||||
char *status;
|
||||
char *mmu_type;
|
||||
long *disabled_hart_mask;
|
||||
};
|
||||
|
||||
static void hart_filter_open(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct hart_filter *filter = (struct hart_filter *)extra;
|
||||
filter->status = NULL;
|
||||
filter->mmu_type = NULL;
|
||||
filter->compat = 0;
|
||||
filter->hart = -1;
|
||||
}
|
||||
|
||||
static void hart_filter_prop(const struct fdt_scan_prop *prop, void *extra)
|
||||
{
|
||||
struct hart_filter *filter = (struct hart_filter *)extra;
|
||||
if (!strcmp(prop->name, "device_type") && !strcmp((const char*)prop->value, "cpu")) {
|
||||
filter->compat = 1;
|
||||
} else if (!strcmp(prop->name, "reg")) {
|
||||
uint64_t reg;
|
||||
fdt_get_address(prop->node->parent, prop->value, ®);
|
||||
filter->hart = reg;
|
||||
} else if (!strcmp(prop->name, "status")) {
|
||||
filter->status = (char*)prop->value;
|
||||
} else if (!strcmp(prop->name, "mmu-type")) {
|
||||
filter->mmu_type = (char*)prop->value;
|
||||
}
|
||||
}
|
||||
|
||||
static bool hart_filter_mask(const struct hart_filter *filter)
|
||||
{
|
||||
if (filter->mmu_type == NULL) return true;
|
||||
if (strcmp(filter->status, "okay")) return true;
|
||||
if (!strcmp(filter->mmu_type, "riscv,sv39")) return false;
|
||||
if (!strcmp(filter->mmu_type, "riscv,sv48")) return false;
|
||||
printm("hart_filter_mask saw unknown hart type: status=\"%s\", mmu_type=\"%s\"\n",
|
||||
filter->status, filter->mmu_type);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hart_filter_done(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct hart_filter *filter = (struct hart_filter *)extra;
|
||||
|
||||
if (!filter->compat) return;
|
||||
assert (filter->status);
|
||||
assert (filter->hart >= 0);
|
||||
|
||||
if (hart_filter_mask(filter)) {
|
||||
strcpy(filter->status, "masked");
|
||||
uint32_t *len = (uint32_t*)filter->status;
|
||||
len[-2] = bswap(strlen("masked")+1);
|
||||
*filter->disabled_hart_mask |= (1 << filter->hart);
|
||||
}
|
||||
}
|
||||
|
||||
void filter_harts(uintptr_t fdt, long *disabled_hart_mask)
|
||||
{
|
||||
struct fdt_cb cb;
|
||||
struct hart_filter filter;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.open = hart_filter_open;
|
||||
cb.prop = hart_filter_prop;
|
||||
cb.done = hart_filter_done;
|
||||
cb.extra = &filter;
|
||||
|
||||
filter.disabled_hart_mask = disabled_hart_mask;
|
||||
*disabled_hart_mask = 0;
|
||||
fdt_scan(fdt, &cb);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////// PRINT //////////////////////////////////////////////
|
||||
|
||||
#ifdef PK_PRINT_DEVICE_TREE
|
||||
#define FDT_PRINT_MAX_DEPTH 32
|
||||
|
||||
struct fdt_print_info {
|
||||
int depth;
|
||||
const struct fdt_scan_node *stack[FDT_PRINT_MAX_DEPTH];
|
||||
};
|
||||
|
||||
void fdt_print_printm(struct fdt_print_info *info, const char *format, ...)
|
||||
{
|
||||
va_list vl;
|
||||
|
||||
for (int i = 0; i < info->depth; ++i)
|
||||
printm(" ");
|
||||
|
||||
va_start(vl, format);
|
||||
vprintm(format, vl);
|
||||
va_end(vl);
|
||||
}
|
||||
|
||||
static void fdt_print_open(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct fdt_print_info *info = (struct fdt_print_info *)extra;
|
||||
|
||||
while (node->parent != NULL && info->stack[info->depth-1] != node->parent) {
|
||||
info->depth--;
|
||||
fdt_print_printm(info, "}\r\n");
|
||||
}
|
||||
|
||||
fdt_print_printm(info, "%s {\r\n", node->name);
|
||||
info->stack[info->depth] = node;
|
||||
info->depth++;
|
||||
}
|
||||
|
||||
static void fdt_print_prop(const struct fdt_scan_prop *prop, void *extra)
|
||||
{
|
||||
struct fdt_print_info *info = (struct fdt_print_info *)extra;
|
||||
int asstring = 1;
|
||||
char *char_data = (char *)(prop->value);
|
||||
|
||||
fdt_print_printm(info, "%s", prop->name);
|
||||
|
||||
if (prop->len == 0) {
|
||||
printm(";\r\n");
|
||||
return;
|
||||
} else {
|
||||
printm(" = ");
|
||||
}
|
||||
|
||||
/* It appears that dtc uses a hueristic to detect strings so I'm using a
|
||||
* similar one here. */
|
||||
for (int i = 0; i < prop->len; ++i) {
|
||||
if (!isstring(char_data[i]))
|
||||
asstring = 0;
|
||||
if (i > 0 && char_data[i] == '\0' && char_data[i-1] == '\0')
|
||||
asstring = 0;
|
||||
}
|
||||
|
||||
if (asstring) {
|
||||
for (size_t i = 0; i < prop->len; i += strlen(char_data + i) + 1) {
|
||||
if (i != 0)
|
||||
printm(", ");
|
||||
printm("\"%s\"", char_data + i);
|
||||
}
|
||||
} else {
|
||||
printm("<");
|
||||
for (size_t i = 0; i < prop->len/4; ++i) {
|
||||
if (i != 0)
|
||||
printm(" ");
|
||||
printm("0x%08x", bswap(prop->value[i]));
|
||||
}
|
||||
printm(">");
|
||||
}
|
||||
|
||||
printm(";\r\n");
|
||||
}
|
||||
|
||||
static void fdt_print_done(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct fdt_print_info *info = (struct fdt_print_info *)extra;
|
||||
}
|
||||
|
||||
static int fdt_print_close(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct fdt_print_info *info = (struct fdt_print_info *)extra;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fdt_print(uintptr_t fdt)
|
||||
{
|
||||
struct fdt_print_info info;
|
||||
struct fdt_cb cb;
|
||||
|
||||
info.depth = 0;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.open = fdt_print_open;
|
||||
cb.prop = fdt_print_prop;
|
||||
cb.done = fdt_print_done;
|
||||
cb.close = fdt_print_close;
|
||||
cb.extra = &info;
|
||||
|
||||
fdt_scan(fdt, &cb);
|
||||
|
||||
while (info.depth > 0) {
|
||||
info.depth--;
|
||||
fdt_print_printm(&info, "}\r\n");
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,76 +0,0 @@
|
||||
#ifndef FDT_H
|
||||
#define FDT_H
|
||||
|
||||
#define FDT_MAGIC 0xd00dfeed
|
||||
#define FDT_VERSION 17
|
||||
|
||||
struct fdt_header {
|
||||
uint32_t magic;
|
||||
uint32_t totalsize;
|
||||
uint32_t off_dt_struct;
|
||||
uint32_t off_dt_strings;
|
||||
uint32_t off_mem_rsvmap;
|
||||
uint32_t version;
|
||||
uint32_t last_comp_version; /* <= 17 */
|
||||
uint32_t boot_cpuid_phys;
|
||||
uint32_t size_dt_strings;
|
||||
uint32_t size_dt_struct;
|
||||
};
|
||||
|
||||
#define FDT_BEGIN_NODE 1
|
||||
#define FDT_END_NODE 2
|
||||
#define FDT_PROP 3
|
||||
#define FDT_NOP 4
|
||||
#define FDT_END 9
|
||||
|
||||
struct fdt_scan_node {
|
||||
const struct fdt_scan_node *parent;
|
||||
const char *name;
|
||||
int address_cells;
|
||||
int size_cells;
|
||||
};
|
||||
|
||||
struct fdt_scan_prop {
|
||||
const struct fdt_scan_node *node;
|
||||
const char *name;
|
||||
uint32_t *value;
|
||||
int len; // in bytes of value
|
||||
};
|
||||
|
||||
struct fdt_cb {
|
||||
void (*open)(const struct fdt_scan_node *node, void *extra);
|
||||
void (*prop)(const struct fdt_scan_prop *prop, void *extra);
|
||||
void (*done)(const struct fdt_scan_node *node, void *extra); // last property was seen
|
||||
int (*close)(const struct fdt_scan_node *node, void *extra); // -1 => delete the node + children
|
||||
void *extra;
|
||||
};
|
||||
|
||||
// Scan the contents of FDT
|
||||
void fdt_scan(uintptr_t fdt, const struct fdt_cb *cb);
|
||||
uint32_t fdt_size(uintptr_t fdt);
|
||||
|
||||
// Extract fields
|
||||
const uint32_t *fdt_get_address(const struct fdt_scan_node *node, const uint32_t *base, uint64_t *value);
|
||||
const uint32_t *fdt_get_size(const struct fdt_scan_node *node, const uint32_t *base, uint64_t *value);
|
||||
int fdt_string_list_index(const struct fdt_scan_prop *prop, const char *str); // -1 if not found
|
||||
|
||||
// Setup memory+clint+plic
|
||||
void query_mem(uintptr_t fdt);
|
||||
void query_harts(uintptr_t fdt);
|
||||
void query_plic(uintptr_t fdt);
|
||||
void query_clint(uintptr_t fdt);
|
||||
|
||||
// Remove information from FDT
|
||||
void filter_harts(uintptr_t fdt, long *disabled_hart_mask);
|
||||
void filter_plic(uintptr_t fdt);
|
||||
void filter_compat(uintptr_t fdt, const char *compat);
|
||||
|
||||
// The hartids of available harts
|
||||
extern uint64_t hart_mask;
|
||||
|
||||
#ifdef PK_PRINT_DEVICE_TREE
|
||||
// Prints the device tree to the console as a DTS
|
||||
void fdt_print(uintptr_t fdt);
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,58 +0,0 @@
|
||||
#include <string.h>
|
||||
#include "finisher.h"
|
||||
#include "fdt.h"
|
||||
|
||||
volatile uint32_t* finisher;
|
||||
|
||||
void finisher_exit(uint16_t code)
|
||||
{
|
||||
if (!finisher) return;
|
||||
if (code == 0) {
|
||||
*finisher = FINISHER_PASS;
|
||||
} else {
|
||||
*finisher = code << 16 | FINISHER_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
struct finisher_scan
|
||||
{
|
||||
int compat;
|
||||
uint64_t reg;
|
||||
};
|
||||
|
||||
static void finisher_open(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct finisher_scan *scan = (struct finisher_scan *)extra;
|
||||
memset(scan, 0, sizeof(*scan));
|
||||
}
|
||||
|
||||
static void finisher_prop(const struct fdt_scan_prop *prop, void *extra)
|
||||
{
|
||||
struct finisher_scan *scan = (struct finisher_scan *)extra;
|
||||
if (!strcmp(prop->name, "compatible") && !strcmp((const char*)prop->value, "sifive,test0")) {
|
||||
scan->compat = 1;
|
||||
} else if (!strcmp(prop->name, "reg")) {
|
||||
fdt_get_address(prop->node->parent, prop->value, &scan->reg);
|
||||
}
|
||||
}
|
||||
|
||||
static void finisher_done(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct finisher_scan *scan = (struct finisher_scan *)extra;
|
||||
if (!scan->compat || !scan->reg || finisher) return;
|
||||
finisher = (uint32_t*)(uintptr_t)scan->reg;
|
||||
}
|
||||
|
||||
void query_finisher(uintptr_t fdt)
|
||||
{
|
||||
struct fdt_cb cb;
|
||||
struct finisher_scan scan;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.open = finisher_open;
|
||||
cb.prop = finisher_prop;
|
||||
cb.done = finisher_done;
|
||||
cb.extra = &scan;
|
||||
|
||||
fdt_scan(fdt, &cb);
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
#ifndef _RISCV_FINISHER_H
|
||||
#define _RISCV_FINISHER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define FINISHER_REG_FINISH 0
|
||||
#define FINISHER_FAIL 0x3333
|
||||
#define FINISHER_PASS 0x5555
|
||||
|
||||
void finisher_exit(uint16_t code);
|
||||
void query_finisher(uintptr_t fdt);
|
||||
|
||||
#endif
|
@ -1,3 +0,0 @@
|
||||
void __riscv_flush_icache(void) {
|
||||
__asm__ volatile ("fence.i");
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
#include "htif.h"
|
||||
#include "atomic.h"
|
||||
#include "mtrap.h"
|
||||
#include "fdt.h"
|
||||
#include <string.h>
|
||||
|
||||
extern uint64_t __htif_base;
|
||||
volatile uint64_t tohost __attribute__((section(".htif")));
|
||||
volatile uint64_t fromhost __attribute__((section(".htif")));
|
||||
volatile int htif_console_buf;
|
||||
static spinlock_t htif_lock = SPINLOCK_INIT;
|
||||
uintptr_t htif;
|
||||
|
||||
#define TOHOST(base_int) (uint64_t *)(base_int + TOHOST_OFFSET)
|
||||
#define FROMHOST(base_int) (uint64_t *)(base_int + FROMHOST_OFFSET)
|
||||
|
||||
#define TOHOST_OFFSET ((uintptr_t)tohost - (uintptr_t)__htif_base)
|
||||
#define FROMHOST_OFFSET ((uintptr_t)fromhost - (uintptr_t)__htif_base)
|
||||
|
||||
static void __check_fromhost()
|
||||
{
|
||||
uint64_t fh = fromhost;
|
||||
if (!fh)
|
||||
return;
|
||||
fromhost = 0;
|
||||
|
||||
// this should be from the console
|
||||
assert(FROMHOST_DEV(fh) == 1);
|
||||
switch (FROMHOST_CMD(fh)) {
|
||||
case 0:
|
||||
htif_console_buf = 1 + (uint8_t)FROMHOST_DATA(fh);
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void __set_tohost(uintptr_t dev, uintptr_t cmd, uintptr_t data)
|
||||
{
|
||||
while (tohost)
|
||||
__check_fromhost();
|
||||
tohost = TOHOST_CMD(dev, cmd, data);
|
||||
}
|
||||
|
||||
int htif_console_getchar()
|
||||
{
|
||||
spinlock_lock(&htif_lock);
|
||||
__check_fromhost();
|
||||
int ch = htif_console_buf;
|
||||
if (ch >= 0) {
|
||||
htif_console_buf = -1;
|
||||
__set_tohost(1, 0, 0);
|
||||
}
|
||||
spinlock_unlock(&htif_lock);
|
||||
|
||||
return ch - 1;
|
||||
}
|
||||
|
||||
static void do_tohost_fromhost(uintptr_t dev, uintptr_t cmd, uintptr_t data)
|
||||
{
|
||||
spinlock_lock(&htif_lock);
|
||||
__set_tohost(dev, cmd, data);
|
||||
|
||||
while (1) {
|
||||
uint64_t fh = fromhost;
|
||||
if (fh) {
|
||||
if (FROMHOST_DEV(fh) == dev && FROMHOST_CMD(fh) == cmd) {
|
||||
fromhost = 0;
|
||||
break;
|
||||
}
|
||||
__check_fromhost();
|
||||
}
|
||||
}
|
||||
spinlock_unlock(&htif_lock);
|
||||
}
|
||||
|
||||
void htif_syscall(uintptr_t arg)
|
||||
{
|
||||
do_tohost_fromhost(0, 0, arg);
|
||||
}
|
||||
|
||||
void htif_console_putchar(uint8_t ch)
|
||||
{
|
||||
spinlock_lock(&htif_lock);
|
||||
__set_tohost(1, 1, ch);
|
||||
spinlock_unlock(&htif_lock);
|
||||
}
|
||||
|
||||
void htif_poweroff()
|
||||
{
|
||||
while (1) {
|
||||
fromhost = 0;
|
||||
tohost = 1;
|
||||
}
|
||||
}
|
||||
|
||||
struct htif_scan
|
||||
{
|
||||
int compat;
|
||||
};
|
||||
|
||||
static void htif_open(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct htif_scan *scan = (struct htif_scan *)extra;
|
||||
memset(scan, 0, sizeof(*scan));
|
||||
}
|
||||
|
||||
static void htif_prop(const struct fdt_scan_prop *prop, void *extra)
|
||||
{
|
||||
struct htif_scan *scan = (struct htif_scan *)extra;
|
||||
if (!strcmp(prop->name, "compatible") && !strcmp((const char*)prop->value, "ucb,htif0")) {
|
||||
scan->compat = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void htif_done(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct htif_scan *scan = (struct htif_scan *)extra;
|
||||
if (!scan->compat) return;
|
||||
|
||||
htif = 1;
|
||||
}
|
||||
|
||||
void query_htif(uintptr_t fdt)
|
||||
{
|
||||
struct fdt_cb cb;
|
||||
struct htif_scan scan;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.open = htif_open;
|
||||
cb.prop = htif_prop;
|
||||
cb.done = htif_done;
|
||||
cb.extra = &scan;
|
||||
|
||||
fdt_scan(fdt, &cb);
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
#ifndef _RISCV_HTIF_H
|
||||
#define _RISCV_HTIF_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if __riscv_xlen == 64
|
||||
# define TOHOST_CMD(dev, cmd, payload) \
|
||||
(((uint64_t)(dev) << 56) | ((uint64_t)(cmd) << 48) | (uint64_t)(payload))
|
||||
#else
|
||||
# define TOHOST_CMD(dev, cmd, payload) ({ \
|
||||
if ((dev) || (cmd)) __builtin_trap(); \
|
||||
(payload); })
|
||||
#endif
|
||||
#define FROMHOST_DEV(fromhost_value) ((uint64_t)(fromhost_value) >> 56)
|
||||
#define FROMHOST_CMD(fromhost_value) ((uint64_t)(fromhost_value) << 8 >> 56)
|
||||
#define FROMHOST_DATA(fromhost_value) ((uint64_t)(fromhost_value) << 16 >> 16)
|
||||
|
||||
extern uintptr_t htif;
|
||||
void query_htif(uintptr_t dtb);
|
||||
void htif_console_putchar(uint8_t);
|
||||
int htif_console_getchar();
|
||||
void htif_poweroff() __attribute__((noreturn));
|
||||
void htif_syscall(uintptr_t);
|
||||
|
||||
#endif
|
@ -1,4 +0,0 @@
|
||||
AC_ARG_ENABLE([fp-emulation], AS_HELP_STRING([--disable-fp-emulation], [Disable floating-point emulation]))
|
||||
AS_IF([test "x$enable_fp_emulation" != "xno"], [
|
||||
AC_DEFINE([PK_ENABLE_FP_EMULATION],,[Define if floating-point emulation is enabled])
|
||||
])
|
@ -1,30 +0,0 @@
|
||||
machine_hdrs = \
|
||||
atomic.h \
|
||||
bits.h \
|
||||
fdt.h \
|
||||
emulation.h \
|
||||
encoding.h \
|
||||
htif.h \
|
||||
mcall.h \
|
||||
mtrap.h \
|
||||
uart.h \
|
||||
uart16550.h \
|
||||
finisher.h \
|
||||
unprivileged_memory.h \
|
||||
vm.h \
|
||||
|
||||
machine_c_srcs = \
|
||||
fdt.c \
|
||||
mtrap.c \
|
||||
minit.c \
|
||||
htif.c \
|
||||
emulation.c \
|
||||
muldiv_emulation.c \
|
||||
uart.c \
|
||||
uart16550.c \
|
||||
finisher.c \
|
||||
misaligned_ldst.c \
|
||||
flush_icache.c \
|
||||
|
||||
machine_asm_srcs = \
|
||||
mentry.S \
|
@ -1,14 +0,0 @@
|
||||
#ifndef _RISCV_SBI_H
|
||||
#define _RISCV_SBI_H
|
||||
|
||||
#define SBI_SET_TIMER 0
|
||||
#define SBI_CONSOLE_PUTCHAR 1
|
||||
#define SBI_CONSOLE_GETCHAR 2
|
||||
#define SBI_CLEAR_IPI 3
|
||||
#define SBI_SEND_IPI 4
|
||||
#define SBI_REMOTE_FENCE_I 5
|
||||
#define SBI_REMOTE_SFENCE_VMA 6
|
||||
#define SBI_REMOTE_SFENCE_VMA_ASID 7
|
||||
#define SBI_SHUTDOWN 8
|
||||
|
||||
#endif
|
@ -1,295 +0,0 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
#include "mtrap.h"
|
||||
#include "bits.h"
|
||||
|
||||
.data
|
||||
.align 6
|
||||
trap_table:
|
||||
#define BAD_TRAP_VECTOR 0
|
||||
.word bad_trap
|
||||
.word pmp_trap
|
||||
.word illegal_insn_trap
|
||||
.word bad_trap
|
||||
.word misaligned_load_trap
|
||||
.word pmp_trap
|
||||
.word misaligned_store_trap
|
||||
.word pmp_trap
|
||||
.word bad_trap
|
||||
.word mcall_trap
|
||||
.word bad_trap
|
||||
.word bad_trap
|
||||
.word bad_trap
|
||||
#define TRAP_FROM_MACHINE_MODE_VECTOR 13
|
||||
.word __trap_from_machine_mode
|
||||
.word bad_trap
|
||||
.word bad_trap
|
||||
|
||||
.option norvc
|
||||
.section .text.init,"ax",@progbits
|
||||
.globl reset_vector
|
||||
reset_vector:
|
||||
j do_reset
|
||||
|
||||
trap_vector:
|
||||
csrrw sp, mscratch, sp
|
||||
beqz sp, .Ltrap_from_machine_mode
|
||||
|
||||
STORE a0, 10*REGBYTES(sp)
|
||||
STORE a1, 11*REGBYTES(sp)
|
||||
|
||||
csrr a1, mcause
|
||||
bgez a1, .Lhandle_trap_in_machine_mode
|
||||
|
||||
# This is an interrupt. Discard the mcause MSB and decode the rest.
|
||||
sll a1, a1, 1
|
||||
|
||||
# Is it a machine timer interrupt?
|
||||
li a0, IRQ_M_TIMER * 2
|
||||
bne a0, a1, 1f
|
||||
|
||||
# Yes. Simply clear MSIE and raise SSIP.
|
||||
li a0, MIP_MTIP
|
||||
csrc mie, a0
|
||||
li a0, MIP_STIP
|
||||
csrs mip, a0
|
||||
|
||||
.Lmret:
|
||||
# Go back whence we came.
|
||||
LOAD a0, 10*REGBYTES(sp)
|
||||
LOAD a1, 11*REGBYTES(sp)
|
||||
csrrw sp, mscratch, sp
|
||||
mret
|
||||
|
||||
1:
|
||||
# Is it an IPI?
|
||||
li a0, IRQ_M_SOFT * 2
|
||||
bne a0, a1, .Lbad_trap
|
||||
|
||||
# Yes. First, clear the MIPI bit.
|
||||
LOAD a0, MENTRY_IPI_OFFSET(sp)
|
||||
sw x0, (a0)
|
||||
fence
|
||||
|
||||
# Now, decode the cause(s).
|
||||
#ifdef __riscv_atomic
|
||||
addi a0, sp, MENTRY_IPI_PENDING_OFFSET
|
||||
amoswap.w a0, x0, (a0)
|
||||
#else
|
||||
lw a0, MENTRY_IPI_PENDING_OFFSET(a0)
|
||||
sw x0, MENTRY_IPI_PENDING_OFFSET(a0)
|
||||
#endif
|
||||
and a1, a0, IPI_SOFT
|
||||
beqz a1, 1f
|
||||
csrs mip, MIP_SSIP
|
||||
1:
|
||||
andi a1, a0, IPI_FENCE_I
|
||||
beqz a1, 1f
|
||||
fence.i
|
||||
1:
|
||||
andi a1, a0, IPI_SFENCE_VMA
|
||||
beqz a1, 1f
|
||||
sfence.vma
|
||||
1:
|
||||
j .Lmret
|
||||
|
||||
|
||||
.Lhandle_trap_in_machine_mode:
|
||||
# Preserve the registers. Compute the address of the trap handler.
|
||||
STORE ra, 1*REGBYTES(sp)
|
||||
STORE gp, 3*REGBYTES(sp)
|
||||
STORE tp, 4*REGBYTES(sp)
|
||||
STORE t0, 5*REGBYTES(sp)
|
||||
1:auipc t0, %pcrel_hi(trap_table) # t0 <- %hi(trap_table)
|
||||
STORE t1, 6*REGBYTES(sp)
|
||||
sll t1, a1, 2 # t1 <- mcause << 2
|
||||
STORE t2, 7*REGBYTES(sp)
|
||||
add t1, t0, t1 # t1 <- %hi(trap_table)[mcause]
|
||||
STORE s0, 8*REGBYTES(sp)
|
||||
LWU t1, %pcrel_lo(1b)(t1) # t1 <- trap_table[mcause]
|
||||
STORE s1, 9*REGBYTES(sp)
|
||||
mv a0, sp # a0 <- regs
|
||||
STORE a2,12*REGBYTES(sp)
|
||||
csrr a2, mepc # a2 <- mepc
|
||||
STORE a3,13*REGBYTES(sp)
|
||||
csrrw t0, mscratch, x0 # t0 <- user sp
|
||||
STORE a4,14*REGBYTES(sp)
|
||||
STORE a5,15*REGBYTES(sp)
|
||||
STORE a6,16*REGBYTES(sp)
|
||||
STORE a7,17*REGBYTES(sp)
|
||||
STORE s2,18*REGBYTES(sp)
|
||||
STORE s3,19*REGBYTES(sp)
|
||||
STORE s4,20*REGBYTES(sp)
|
||||
STORE s5,21*REGBYTES(sp)
|
||||
STORE s6,22*REGBYTES(sp)
|
||||
STORE s7,23*REGBYTES(sp)
|
||||
STORE s8,24*REGBYTES(sp)
|
||||
STORE s9,25*REGBYTES(sp)
|
||||
STORE s10,26*REGBYTES(sp)
|
||||
STORE s11,27*REGBYTES(sp)
|
||||
STORE t3,28*REGBYTES(sp)
|
||||
STORE t4,29*REGBYTES(sp)
|
||||
STORE t5,30*REGBYTES(sp)
|
||||
STORE t6,31*REGBYTES(sp)
|
||||
STORE t0, 2*REGBYTES(sp) # sp
|
||||
|
||||
#ifndef __riscv_flen
|
||||
lw tp, (sp) # Move the emulated FCSR from x0's save slot into tp.
|
||||
#endif
|
||||
STORE x0, (sp) # Zero x0's save slot.
|
||||
|
||||
# Invoke the handler.
|
||||
jalr t1
|
||||
|
||||
#ifndef __riscv_flen
|
||||
sw tp, (sp) # Move the emulated FCSR from tp into x0's save slot.
|
||||
#endif
|
||||
|
||||
restore_mscratch:
|
||||
# Restore mscratch, so future traps will know they didn't come from M-mode.
|
||||
csrw mscratch, sp
|
||||
|
||||
restore_regs:
|
||||
# Restore all of the registers.
|
||||
LOAD ra, 1*REGBYTES(sp)
|
||||
LOAD gp, 3*REGBYTES(sp)
|
||||
LOAD tp, 4*REGBYTES(sp)
|
||||
LOAD t0, 5*REGBYTES(sp)
|
||||
LOAD t1, 6*REGBYTES(sp)
|
||||
LOAD t2, 7*REGBYTES(sp)
|
||||
LOAD s0, 8*REGBYTES(sp)
|
||||
LOAD s1, 9*REGBYTES(sp)
|
||||
LOAD a0,10*REGBYTES(sp)
|
||||
LOAD a1,11*REGBYTES(sp)
|
||||
LOAD a2,12*REGBYTES(sp)
|
||||
LOAD a3,13*REGBYTES(sp)
|
||||
LOAD a4,14*REGBYTES(sp)
|
||||
LOAD a5,15*REGBYTES(sp)
|
||||
LOAD a6,16*REGBYTES(sp)
|
||||
LOAD a7,17*REGBYTES(sp)
|
||||
LOAD s2,18*REGBYTES(sp)
|
||||
LOAD s3,19*REGBYTES(sp)
|
||||
LOAD s4,20*REGBYTES(sp)
|
||||
LOAD s5,21*REGBYTES(sp)
|
||||
LOAD s6,22*REGBYTES(sp)
|
||||
LOAD s7,23*REGBYTES(sp)
|
||||
LOAD s8,24*REGBYTES(sp)
|
||||
LOAD s9,25*REGBYTES(sp)
|
||||
LOAD s10,26*REGBYTES(sp)
|
||||
LOAD s11,27*REGBYTES(sp)
|
||||
LOAD t3,28*REGBYTES(sp)
|
||||
LOAD t4,29*REGBYTES(sp)
|
||||
LOAD t5,30*REGBYTES(sp)
|
||||
LOAD t6,31*REGBYTES(sp)
|
||||
LOAD sp, 2*REGBYTES(sp)
|
||||
mret
|
||||
|
||||
.Ltrap_from_machine_mode:
|
||||
csrr sp, mscratch
|
||||
addi sp, sp, -INTEGER_CONTEXT_SIZE
|
||||
STORE a0,10*REGBYTES(sp)
|
||||
STORE a1,11*REGBYTES(sp)
|
||||
li a1, TRAP_FROM_MACHINE_MODE_VECTOR
|
||||
j .Lhandle_trap_in_machine_mode
|
||||
|
||||
.Lbad_trap:
|
||||
li a1, BAD_TRAP_VECTOR
|
||||
j .Lhandle_trap_in_machine_mode
|
||||
|
||||
.globl __redirect_trap
|
||||
__redirect_trap:
|
||||
# reset sp to top of M-mode stack
|
||||
li t0, MACHINE_STACK_SIZE
|
||||
add sp, sp, t0
|
||||
neg t0, t0
|
||||
and sp, sp, t0
|
||||
addi sp, sp, -MENTRY_FRAME_SIZE
|
||||
j restore_mscratch
|
||||
|
||||
__trap_from_machine_mode:
|
||||
jal trap_from_machine_mode
|
||||
j restore_regs
|
||||
|
||||
do_reset:
|
||||
li x1, 0
|
||||
li x2, 0
|
||||
li x3, 0
|
||||
li x4, 0
|
||||
li x5, 0
|
||||
li x6, 0
|
||||
li x7, 0
|
||||
li x8, 0
|
||||
li x9, 0
|
||||
// save a0 and a1; arguments from previous boot loader stage:
|
||||
// li x10, 0
|
||||
// li x11, 0
|
||||
li x12, 0
|
||||
li x13, 0
|
||||
li x14, 0
|
||||
li x15, 0
|
||||
li x16, 0
|
||||
li x17, 0
|
||||
li x18, 0
|
||||
li x19, 0
|
||||
li x20, 0
|
||||
li x21, 0
|
||||
li x22, 0
|
||||
li x23, 0
|
||||
li x24, 0
|
||||
li x25, 0
|
||||
li x26, 0
|
||||
li x27, 0
|
||||
li x28, 0
|
||||
li x29, 0
|
||||
li x30, 0
|
||||
li x31, 0
|
||||
csrw mscratch, x0
|
||||
|
||||
# write mtvec and make sure it sticks
|
||||
la t0, trap_vector
|
||||
csrw mtvec, t0
|
||||
csrr t1, mtvec
|
||||
1:bne t0, t1, 1b
|
||||
|
||||
la sp, stacks + RISCV_PGSIZE - MENTRY_FRAME_SIZE
|
||||
|
||||
csrr a3, mhartid
|
||||
slli a2, a3, RISCV_PGSHIFT
|
||||
add sp, sp, a2
|
||||
|
||||
# Boot on the first hart
|
||||
beqz a3, init_first_hart
|
||||
|
||||
# set MSIE bit to receive IPI
|
||||
li a2, MIP_MSIP
|
||||
csrw mie, a2
|
||||
|
||||
.LmultiHart:
|
||||
#if MAX_HARTS > 1
|
||||
# wait for an IPI to signal that it's safe to boot
|
||||
wfi
|
||||
|
||||
# masked harts never start
|
||||
la a4, disabled_hart_mask
|
||||
LOAD a4, 0(a4)
|
||||
srl a4, a4, a3
|
||||
andi a4, a4, 1
|
||||
bnez a4, .LmultiHart
|
||||
|
||||
# only start if mip is set
|
||||
csrr a2, mip
|
||||
andi a2, a2, MIP_MSIP
|
||||
beqz a2, .LmultiHart
|
||||
|
||||
# make sure our hart id is within a valid range
|
||||
fence
|
||||
li a2, MAX_HARTS
|
||||
bltu a3, a2, init_other_hart
|
||||
#endif
|
||||
wfi
|
||||
j .LmultiHart
|
||||
|
||||
.bss
|
||||
.align RISCV_PGSHIFT
|
||||
stacks:
|
||||
.skip RISCV_PGSIZE * MAX_HARTS
|
@ -1,200 +0,0 @@
|
||||
#include "mtrap.h"
|
||||
#include "atomic.h"
|
||||
#include "vm.h"
|
||||
//#include "fp_emulation.h"
|
||||
#include "bits.h"
|
||||
#include "fdt.h"
|
||||
#include "uart.h"
|
||||
#include "uart16550.h"
|
||||
#include "finisher.h"
|
||||
#include "disabled_hart_mask.h"
|
||||
#include "htif.h"
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
pte_t* root_page_table;
|
||||
uintptr_t mem_size;
|
||||
volatile uint64_t* mtime;
|
||||
volatile uint32_t* plic_priorities;
|
||||
size_t plic_ndevs;
|
||||
|
||||
static void mstatus_init()
|
||||
{
|
||||
// Enable FPU
|
||||
if (supports_extension('D') || supports_extension('F'))
|
||||
write_csr(mstatus, MSTATUS_FS);
|
||||
|
||||
// Enable user/supervisor use of perf counters
|
||||
if (supports_extension('S'))
|
||||
write_csr(scounteren, -1);
|
||||
write_csr(mcounteren, -1);
|
||||
|
||||
// Enable software interrupts
|
||||
write_csr(mie, MIP_MSIP);
|
||||
|
||||
// Disable paging
|
||||
if (supports_extension('S'))
|
||||
write_csr(sptbr, 0);
|
||||
}
|
||||
|
||||
// send S-mode interrupts and most exceptions straight to S-mode
|
||||
static void delegate_traps()
|
||||
{
|
||||
if (!supports_extension('S'))
|
||||
return;
|
||||
|
||||
uintptr_t interrupts = MIP_SSIP | MIP_STIP | MIP_SEIP;
|
||||
uintptr_t exceptions =
|
||||
(1U << CAUSE_MISALIGNED_FETCH) |
|
||||
(1U << CAUSE_FETCH_PAGE_FAULT) |
|
||||
(1U << CAUSE_BREAKPOINT) |
|
||||
(1U << CAUSE_LOAD_PAGE_FAULT) |
|
||||
(1U << CAUSE_STORE_PAGE_FAULT) |
|
||||
(1U << CAUSE_USER_ECALL);
|
||||
|
||||
write_csr(mideleg, interrupts);
|
||||
write_csr(medeleg, exceptions);
|
||||
assert(read_csr(mideleg) == interrupts);
|
||||
assert(read_csr(medeleg) == exceptions);
|
||||
}
|
||||
|
||||
static void fp_init()
|
||||
{
|
||||
if (!supports_extension('D') && !supports_extension('F'))
|
||||
return;
|
||||
|
||||
assert(read_csr(mstatus) & MSTATUS_FS);
|
||||
|
||||
#ifdef __riscv_flen
|
||||
// for (int i = 0; i < 32; i++)
|
||||
// init_fp_reg(i);
|
||||
// write_csr(fcsr, 0);
|
||||
#else
|
||||
uintptr_t fd_mask = (1 << ('F' - 'A')) | (1 << ('D' - 'A'));
|
||||
clear_csr(misa, fd_mask);
|
||||
assert(!(read_csr(misa) & fd_mask));
|
||||
#endif
|
||||
}
|
||||
|
||||
hls_t* hls_init(uintptr_t id)
|
||||
{
|
||||
hls_t* hls = OTHER_HLS(id);
|
||||
memset(hls, 0, sizeof(*hls));
|
||||
return hls;
|
||||
}
|
||||
|
||||
static void memory_init()
|
||||
{
|
||||
mem_size = mem_size / MEGAPAGE_SIZE * MEGAPAGE_SIZE;
|
||||
}
|
||||
|
||||
static void hart_init()
|
||||
{
|
||||
mstatus_init();
|
||||
// fp_init();
|
||||
delegate_traps();
|
||||
}
|
||||
|
||||
static void plic_init()
|
||||
{
|
||||
for (size_t i = 1; i <= plic_ndevs; i++)
|
||||
plic_priorities[i] = 1;
|
||||
}
|
||||
|
||||
static void prci_test()
|
||||
{
|
||||
assert(!(read_csr(mip) & MIP_MSIP));
|
||||
*HLS()->ipi = 1;
|
||||
assert(read_csr(mip) & MIP_MSIP);
|
||||
*HLS()->ipi = 0;
|
||||
|
||||
assert(!(read_csr(mip) & MIP_MTIP));
|
||||
*HLS()->timecmp = 0;
|
||||
assert(read_csr(mip) & MIP_MTIP);
|
||||
*HLS()->timecmp = -1ULL;
|
||||
}
|
||||
|
||||
static void hart_plic_init()
|
||||
{
|
||||
// clear pending interrupts
|
||||
*HLS()->ipi = 0;
|
||||
*HLS()->timecmp = -1ULL;
|
||||
write_csr(mip, 0);
|
||||
|
||||
if (!plic_ndevs)
|
||||
return;
|
||||
|
||||
size_t ie_words = plic_ndevs / sizeof(uintptr_t) + 1;
|
||||
for (size_t i = 0; i < ie_words; i++)
|
||||
HLS()->plic_s_ie[i] = ULONG_MAX;
|
||||
*HLS()->plic_m_thresh = 1;
|
||||
*HLS()->plic_s_thresh = 0;
|
||||
}
|
||||
|
||||
static void wake_harts()
|
||||
{
|
||||
for (int hart = 0; hart < MAX_HARTS; ++hart)
|
||||
if ((((~disabled_hart_mask & hart_mask) >> hart) & 1))
|
||||
*OTHER_HLS(hart)->ipi = 1; // wakeup the hart
|
||||
}
|
||||
|
||||
void init_first_hart(uintptr_t hartid, uintptr_t dtb)
|
||||
{
|
||||
// Confirm console as early as possible
|
||||
query_uart(dtb);
|
||||
query_uart16550(dtb);
|
||||
query_htif(dtb);
|
||||
|
||||
hart_init();
|
||||
hls_init(0); // this might get called again from parse_config_string
|
||||
|
||||
// Find the power button early as well so die() works
|
||||
query_finisher(dtb);
|
||||
|
||||
query_mem(dtb);
|
||||
query_harts(dtb);
|
||||
query_clint(dtb);
|
||||
query_plic(dtb);
|
||||
|
||||
wake_harts();
|
||||
|
||||
plic_init();
|
||||
hart_plic_init();
|
||||
//prci_test();
|
||||
memory_init();
|
||||
boot_loader(dtb);
|
||||
}
|
||||
|
||||
void init_other_hart(uintptr_t hartid, uintptr_t dtb)
|
||||
{
|
||||
hart_init();
|
||||
hart_plic_init();
|
||||
boot_other_hart(dtb);
|
||||
}
|
||||
|
||||
void enter_supervisor_mode(void (*fn)(uintptr_t), uintptr_t arg0, uintptr_t arg1, uintptr_t arg2)
|
||||
{
|
||||
// Set up a PMP to permit access to all of memory.
|
||||
// Ignore the illegal-instruction trap if PMPs aren't supported.
|
||||
uintptr_t pmpc = PMP_NAPOT | PMP_R | PMP_W | PMP_X;
|
||||
asm volatile ("la t0, 1f\n\t"
|
||||
"csrrw t0, mtvec, t0\n\t"
|
||||
"csrw pmpaddr0, %1\n\t"
|
||||
"csrw pmpcfg0, %0\n\t"
|
||||
".align 2\n\t"
|
||||
"1: csrw mtvec, t0"
|
||||
: : "r" (pmpc), "r" (-1UL) : "t0");
|
||||
|
||||
uintptr_t mstatus = read_csr(mstatus);
|
||||
mstatus = INSERT_FIELD(mstatus, MSTATUS_MPP, PRV_S);
|
||||
mstatus = INSERT_FIELD(mstatus, MSTATUS_MPIE, 0);
|
||||
write_csr(mstatus, mstatus);
|
||||
write_csr(mscratch, MACHINE_STACK_TOP() - MENTRY_FRAME_SIZE);
|
||||
write_csr(mepc, fn);
|
||||
|
||||
register uintptr_t a0 asm ("a0") = arg0;
|
||||
register uintptr_t a1 asm ("a1") = arg1;
|
||||
register uintptr_t a2 asm ("a2") = arg2;
|
||||
asm volatile ("mret" : : "r" (a0), "r" (a1), "r" (a2));
|
||||
__builtin_unreachable();
|
||||
}
|
@ -1,143 +0,0 @@
|
||||
#include "emulation.h"
|
||||
//#include "fp_emulation.h"
|
||||
#include "unprivileged_memory.h"
|
||||
#include "mtrap.h"
|
||||
#include "config.h"
|
||||
//#include "pk.h"
|
||||
|
||||
union byte_array {
|
||||
uint8_t bytes[8];
|
||||
uintptr_t intx;
|
||||
uint64_t int64;
|
||||
};
|
||||
|
||||
static inline int insn_len(long insn)
|
||||
{
|
||||
return (insn & 0x3) < 0x3 ? 2 : 4;
|
||||
}
|
||||
|
||||
void misaligned_load_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc)
|
||||
{
|
||||
union byte_array val;
|
||||
uintptr_t mstatus;
|
||||
insn_t insn = get_insn(mepc, &mstatus);
|
||||
uintptr_t npc = mepc + insn_len(insn);
|
||||
uintptr_t addr = read_csr(mbadaddr);
|
||||
|
||||
int shift = 0, fp = 0, len;
|
||||
if ((insn & MASK_LW) == MATCH_LW)
|
||||
len = 4, shift = 8*(sizeof(uintptr_t) - len);
|
||||
#if __riscv_xlen == 64
|
||||
else if ((insn & MASK_LD) == MATCH_LD)
|
||||
len = 8, shift = 8*(sizeof(uintptr_t) - len);
|
||||
else if ((insn & MASK_LWU) == MATCH_LWU)
|
||||
len = 4;
|
||||
#endif
|
||||
#ifdef PK_ENABLE_FP_EMULATION
|
||||
else if ((insn & MASK_FLD) == MATCH_FLD)
|
||||
fp = 1, len = 8;
|
||||
else if ((insn & MASK_FLW) == MATCH_FLW)
|
||||
fp = 1, len = 4;
|
||||
#endif
|
||||
else if ((insn & MASK_LH) == MATCH_LH)
|
||||
len = 2, shift = 8*(sizeof(uintptr_t) - len);
|
||||
else if ((insn & MASK_LHU) == MATCH_LHU)
|
||||
len = 2;
|
||||
#ifdef __riscv_compressed
|
||||
# if __riscv_xlen >= 64
|
||||
else if ((insn & MASK_C_LD) == MATCH_C_LD)
|
||||
len = 8, shift = 8*(sizeof(uintptr_t) - len), insn = RVC_RS2S(insn) << SH_RD;
|
||||
else if ((insn & MASK_C_LDSP) == MATCH_C_LDSP && ((insn >> SH_RD) & 0x1f))
|
||||
len = 8, shift = 8*(sizeof(uintptr_t) - len);
|
||||
# endif
|
||||
else if ((insn & MASK_C_LW) == MATCH_C_LW)
|
||||
len = 4, shift = 8*(sizeof(uintptr_t) - len), insn = RVC_RS2S(insn) << SH_RD;
|
||||
else if ((insn & MASK_C_LWSP) == MATCH_C_LWSP && ((insn >> SH_RD) & 0x1f))
|
||||
len = 4, shift = 8*(sizeof(uintptr_t) - len);
|
||||
# ifdef PK_ENABLE_FP_EMULATION
|
||||
else if ((insn & MASK_C_FLD) == MATCH_C_FLD)
|
||||
fp = 1, len = 8, insn = RVC_RS2S(insn) << SH_RD;
|
||||
else if ((insn & MASK_C_FLDSP) == MATCH_C_FLDSP)
|
||||
fp = 1, len = 8;
|
||||
# if __riscv_xlen == 32
|
||||
else if ((insn & MASK_C_FLW) == MATCH_C_FLW)
|
||||
fp = 1, len = 4, insn = RVC_RS2S(insn) << SH_RD;
|
||||
else if ((insn & MASK_C_FLWSP) == MATCH_C_FLWSP)
|
||||
fp = 1, len = 4;
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
else
|
||||
return truly_illegal_insn(regs, mcause, mepc, mstatus, insn);
|
||||
|
||||
val.int64 = 0;
|
||||
for (intptr_t i = 0; i < len; i++)
|
||||
val.bytes[i] = load_uint8_t((void *)(addr + i), mepc);
|
||||
|
||||
if (!fp)
|
||||
SET_RD(insn, regs, (intptr_t)val.intx << shift >> shift);
|
||||
#ifdef PK_ENABLE_FP_EMULATION
|
||||
else if (len == 8)
|
||||
SET_F64_RD(insn, regs, val.int64);
|
||||
else
|
||||
SET_F32_RD(insn, regs, val.intx);
|
||||
#endif
|
||||
write_csr(mepc, npc);
|
||||
}
|
||||
|
||||
void misaligned_store_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc)
|
||||
{
|
||||
union byte_array val;
|
||||
uintptr_t mstatus;
|
||||
insn_t insn = get_insn(mepc, &mstatus);
|
||||
uintptr_t npc = mepc + insn_len(insn);
|
||||
int len;
|
||||
|
||||
val.intx = GET_RS2(insn, regs);
|
||||
if ((insn & MASK_SW) == MATCH_SW)
|
||||
len = 4;
|
||||
#if __riscv_xlen == 64
|
||||
else if ((insn & MASK_SD) == MATCH_SD)
|
||||
len = 8;
|
||||
#endif
|
||||
#ifdef PK_ENABLE_FP_EMULATION
|
||||
else if ((insn & MASK_FSD) == MATCH_FSD)
|
||||
len = 8, val.int64 = GET_F64_RS2(insn, regs);
|
||||
else if ((insn & MASK_FSW) == MATCH_FSW)
|
||||
len = 4, val.intx = GET_F32_RS2(insn, regs);
|
||||
#endif
|
||||
else if ((insn & MASK_SH) == MATCH_SH)
|
||||
len = 2;
|
||||
#ifdef __riscv_compressed
|
||||
# if __riscv_xlen >= 64
|
||||
else if ((insn & MASK_C_SD) == MATCH_C_SD)
|
||||
len = 8, val.intx = GET_RS2S(insn, regs);
|
||||
else if ((insn & MASK_C_SDSP) == MATCH_C_SDSP && ((insn >> SH_RD) & 0x1f))
|
||||
len = 8, val.intx = GET_RS2C(insn, regs);
|
||||
# endif
|
||||
else if ((insn & MASK_C_SW) == MATCH_C_SW)
|
||||
len = 4, val.intx = GET_RS2S(insn, regs);
|
||||
else if ((insn & MASK_C_SWSP) == MATCH_C_SWSP && ((insn >> SH_RD) & 0x1f))
|
||||
len = 4, val.intx = GET_RS2C(insn, regs);
|
||||
# ifdef PK_ENABLE_FP_EMULATION
|
||||
else if ((insn & MASK_C_FSD) == MATCH_C_FSD)
|
||||
len = 8, val.int64 = GET_F64_RS2S(insn, regs);
|
||||
else if ((insn & MASK_C_FSDSP) == MATCH_C_FSDSP)
|
||||
len = 8, val.int64 = GET_F64_RS2C(insn, regs);
|
||||
# if __riscv_xlen == 32
|
||||
else if ((insn & MASK_C_FSW) == MATCH_C_FSW)
|
||||
len = 4, val.intx = GET_F32_RS2S(insn, regs);
|
||||
else if ((insn & MASK_C_FSWSP) == MATCH_C_FSWSP)
|
||||
len = 4, val.intx = GET_F32_RS2C(insn, regs);
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
else
|
||||
return truly_illegal_insn(regs, mcause, mepc, mstatus, insn);
|
||||
|
||||
uintptr_t addr = read_csr(mbadaddr);
|
||||
for (int i = 0; i < len; i++)
|
||||
store_uint8_t((void *)(addr + i), val.bytes[i], mepc);
|
||||
|
||||
write_csr(mepc, npc);
|
||||
}
|
@ -1,235 +0,0 @@
|
||||
#include "mtrap.h"
|
||||
#include "mcall.h"
|
||||
#include "htif.h"
|
||||
#include "atomic.h"
|
||||
#include "bits.h"
|
||||
#include "vm.h"
|
||||
#include "uart.h"
|
||||
#include "uart16550.h"
|
||||
#include "finisher.h"
|
||||
#include "fdt.h"
|
||||
#include "unprivileged_memory.h"
|
||||
#include "disabled_hart_mask.h"
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void __attribute__((noreturn)) bad_trap(uintptr_t* regs, uintptr_t dummy, uintptr_t mepc)
|
||||
{
|
||||
die("machine mode: unhandlable trap %d @ %p", read_csr(mcause), mepc);
|
||||
}
|
||||
|
||||
static uintptr_t mcall_console_putchar(uint8_t ch)
|
||||
{
|
||||
if (uart) {
|
||||
uart_putchar(ch);
|
||||
} else if (uart16550) {
|
||||
uart16550_putchar(ch);
|
||||
} else if (htif) {
|
||||
htif_console_putchar(ch);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void poweroff(uint16_t code)
|
||||
{
|
||||
printm("Power off\n");
|
||||
finisher_exit(code);
|
||||
if (htif) {
|
||||
htif_poweroff();
|
||||
} else {
|
||||
while (1) { asm volatile ("#noop\n"); }
|
||||
}
|
||||
}
|
||||
|
||||
void putstring(const char* s)
|
||||
{
|
||||
while (*s)
|
||||
mcall_console_putchar(*s++);
|
||||
}
|
||||
|
||||
void vprintm(const char* s, va_list vl)
|
||||
{
|
||||
char buf[256];
|
||||
vsnprintf(buf, sizeof buf, s, vl);
|
||||
putstring(buf);
|
||||
}
|
||||
|
||||
void printm(const char* s, ...)
|
||||
{
|
||||
va_list vl;
|
||||
|
||||
va_start(vl, s);
|
||||
vprintm(s, vl);
|
||||
va_end(vl);
|
||||
}
|
||||
|
||||
static void send_ipi(uintptr_t recipient, int event)
|
||||
{
|
||||
if (((disabled_hart_mask >> recipient) & 1)) return;
|
||||
atomic_or(&OTHER_HLS(recipient)->mipi_pending, event);
|
||||
mb();
|
||||
*OTHER_HLS(recipient)->ipi = 1;
|
||||
}
|
||||
|
||||
static uintptr_t mcall_console_getchar()
|
||||
{
|
||||
if (uart) {
|
||||
return uart_getchar();
|
||||
} else if (uart16550) {
|
||||
return uart16550_getchar();
|
||||
} else if (htif) {
|
||||
return htif_console_getchar();
|
||||
} else {
|
||||
return '\0';
|
||||
}
|
||||
}
|
||||
|
||||
static uintptr_t mcall_clear_ipi()
|
||||
{
|
||||
return clear_csr(mip, MIP_SSIP) & MIP_SSIP;
|
||||
}
|
||||
|
||||
static uintptr_t mcall_shutdown()
|
||||
{
|
||||
poweroff(0);
|
||||
}
|
||||
|
||||
static uintptr_t mcall_set_timer(uint64_t when)
|
||||
{
|
||||
*HLS()->timecmp = when;
|
||||
clear_csr(mip, MIP_STIP);
|
||||
set_csr(mie, MIP_MTIP);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void send_ipi_many(uintptr_t* pmask, int event)
|
||||
{
|
||||
_Static_assert(MAX_HARTS <= 8 * sizeof(*pmask), "# harts > uintptr_t bits");
|
||||
uintptr_t mask = hart_mask;
|
||||
if (pmask)
|
||||
mask &= load_uintptr_t(pmask, read_csr(mepc));
|
||||
|
||||
// send IPIs to everyone
|
||||
for (uintptr_t i = 0, m = mask; m; i++, m >>= 1)
|
||||
if (m & 1)
|
||||
send_ipi(i, event);
|
||||
|
||||
if (event == IPI_SOFT)
|
||||
return;
|
||||
|
||||
// wait until all events have been handled.
|
||||
// prevent deadlock by consuming incoming IPIs.
|
||||
uint32_t incoming_ipi = 0;
|
||||
for (uintptr_t i = 0, m = mask; m; i++, m >>= 1)
|
||||
if (m & 1)
|
||||
while (*OTHER_HLS(i)->ipi)
|
||||
incoming_ipi |= atomic_swap(HLS()->ipi, 0);
|
||||
|
||||
// if we got an IPI, restore it; it will be taken after returning
|
||||
if (incoming_ipi) {
|
||||
*HLS()->ipi = incoming_ipi;
|
||||
mb();
|
||||
}
|
||||
}
|
||||
|
||||
void redirect_trap(uintptr_t epc, uintptr_t mstatus, uintptr_t badaddr);
|
||||
|
||||
|
||||
void mcall_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc)
|
||||
{
|
||||
write_csr(mepc, mepc + 4);
|
||||
|
||||
uintptr_t n = regs[17], arg0 = regs[10], arg1 = regs[11], retval, ipi_type;
|
||||
|
||||
switch (n)
|
||||
{
|
||||
case SBI_CONSOLE_PUTCHAR:
|
||||
retval = mcall_console_putchar(arg0);
|
||||
break;
|
||||
case SBI_CONSOLE_GETCHAR:
|
||||
retval = mcall_console_getchar();
|
||||
break;
|
||||
case SBI_SEND_IPI:
|
||||
ipi_type = IPI_SOFT;
|
||||
goto send_ipi;
|
||||
case SBI_REMOTE_SFENCE_VMA:
|
||||
case SBI_REMOTE_SFENCE_VMA_ASID:
|
||||
ipi_type = IPI_SFENCE_VMA;
|
||||
goto send_ipi;
|
||||
case SBI_REMOTE_FENCE_I:
|
||||
ipi_type = IPI_FENCE_I;
|
||||
send_ipi:
|
||||
send_ipi_many((uintptr_t*)arg0, ipi_type);
|
||||
retval = 0;
|
||||
break;
|
||||
case SBI_CLEAR_IPI:
|
||||
retval = mcall_clear_ipi();
|
||||
break;
|
||||
case SBI_SHUTDOWN:
|
||||
retval = mcall_shutdown();
|
||||
break;
|
||||
case SBI_SET_TIMER:
|
||||
#if __riscv_xlen == 32
|
||||
retval = mcall_set_timer(arg0 + ((uint64_t)arg1 << 32));
|
||||
#else
|
||||
retval = mcall_set_timer(arg0);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
redirect_trap(read_csr(mepc), read_csr(mstatus), read_csr(mbadaddr));
|
||||
retval = -ENOSYS;
|
||||
break;
|
||||
}
|
||||
regs[10] = retval;
|
||||
}
|
||||
|
||||
void redirect_trap(uintptr_t epc, uintptr_t mstatus, uintptr_t badaddr)
|
||||
{
|
||||
write_csr(sbadaddr, badaddr);
|
||||
write_csr(sepc, epc);
|
||||
write_csr(scause, read_csr(mcause));
|
||||
write_csr(mepc, read_csr(stvec));
|
||||
|
||||
uintptr_t new_mstatus = mstatus & ~(MSTATUS_SPP | MSTATUS_SPIE | MSTATUS_SIE);
|
||||
uintptr_t mpp_s = MSTATUS_MPP & (MSTATUS_MPP >> 1);
|
||||
new_mstatus |= (mstatus * (MSTATUS_SPIE / MSTATUS_SIE)) & MSTATUS_SPIE;
|
||||
new_mstatus |= (mstatus / (mpp_s / MSTATUS_SPP)) & MSTATUS_SPP;
|
||||
new_mstatus |= mpp_s;
|
||||
write_csr(mstatus, new_mstatus);
|
||||
|
||||
extern void __redirect_trap();
|
||||
return __redirect_trap();
|
||||
}
|
||||
|
||||
void pmp_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc)
|
||||
{
|
||||
redirect_trap(mepc, read_csr(mstatus), read_csr(mbadaddr));
|
||||
}
|
||||
|
||||
static void machine_page_fault(uintptr_t* regs, uintptr_t dummy, uintptr_t mepc)
|
||||
{
|
||||
// MPRV=1 iff this trap occurred while emulating an instruction on behalf
|
||||
// of a lower privilege level. In that case, a2=epc and a3=mstatus.
|
||||
if (read_csr(mstatus) & MSTATUS_MPRV) {
|
||||
return redirect_trap(regs[12], regs[13], read_csr(mbadaddr));
|
||||
}
|
||||
bad_trap(regs, dummy, mepc);
|
||||
}
|
||||
|
||||
void trap_from_machine_mode(uintptr_t* regs, uintptr_t dummy, uintptr_t mepc)
|
||||
{
|
||||
uintptr_t mcause = read_csr(mcause);
|
||||
|
||||
switch (mcause)
|
||||
{
|
||||
case CAUSE_LOAD_PAGE_FAULT:
|
||||
case CAUSE_STORE_PAGE_FAULT:
|
||||
case CAUSE_FETCH_ACCESS:
|
||||
case CAUSE_LOAD_ACCESS:
|
||||
case CAUSE_STORE_ACCESS:
|
||||
return machine_page_fault(regs, dummy, mepc);
|
||||
default:
|
||||
bad_trap(regs, dummy, mepc);
|
||||
}
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
#ifndef _RISCV_MTRAP_H
|
||||
#define _RISCV_MTRAP_H
|
||||
|
||||
#include "encoding.h"
|
||||
|
||||
#ifdef __riscv_atomic
|
||||
# define MAX_HARTS 8 // arbitrary
|
||||
#else
|
||||
# define MAX_HARTS 1
|
||||
#endif
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define read_const_csr(reg) ({ unsigned long __tmp; \
|
||||
asm ("csrr %0, " #reg : "=r"(__tmp)); \
|
||||
__tmp; })
|
||||
|
||||
static inline int supports_extension(char ext)
|
||||
{
|
||||
return read_const_csr(misa) & (1 << (ext - 'A'));
|
||||
}
|
||||
|
||||
static inline int xlen()
|
||||
{
|
||||
return read_const_csr(misa) < 0 ? 64 : 32;
|
||||
}
|
||||
|
||||
extern uintptr_t mem_size;
|
||||
extern volatile uint64_t* mtime;
|
||||
extern volatile uint32_t* plic_priorities;
|
||||
extern size_t plic_ndevs;
|
||||
|
||||
typedef struct {
|
||||
volatile uint32_t* ipi;
|
||||
volatile int mipi_pending;
|
||||
|
||||
volatile uint64_t* timecmp;
|
||||
|
||||
volatile uint32_t* plic_m_thresh;
|
||||
volatile uintptr_t* plic_m_ie;
|
||||
volatile uint32_t* plic_s_thresh;
|
||||
volatile uintptr_t* plic_s_ie;
|
||||
} hls_t;
|
||||
|
||||
#define MACHINE_STACK_TOP() ({ \
|
||||
register uintptr_t sp asm ("sp"); \
|
||||
(void*)((sp + RISCV_PGSIZE) & -RISCV_PGSIZE); })
|
||||
|
||||
// hart-local storage, at top of stack
|
||||
#define HLS() ((hls_t*)(MACHINE_STACK_TOP() - HLS_SIZE))
|
||||
#define OTHER_HLS(id) ((hls_t*)((void*)HLS() + RISCV_PGSIZE * ((id) - read_const_csr(mhartid))))
|
||||
|
||||
hls_t* hls_init(uintptr_t hart_id);
|
||||
void parse_config_string();
|
||||
void poweroff(uint16_t code) __attribute((noreturn));
|
||||
void printm(const char* s, ...);
|
||||
void vprintm(const char *s, va_list args);
|
||||
void putstring(const char* s);
|
||||
#define assert(x) ({ if (!(x)) die("assertion failed: %s", #x); })
|
||||
#define die(str, ...) ({ printm("%s:%d: " str "\n", __FILE__, __LINE__, ##__VA_ARGS__); poweroff(-1); })
|
||||
|
||||
void enter_supervisor_mode(void (*fn)(uintptr_t), uintptr_t arg0, uintptr_t arg1, uintptr_t arg2)
|
||||
__attribute__((noreturn));
|
||||
void boot_loader(uintptr_t dtb);
|
||||
void boot_other_hart(uintptr_t dtb);
|
||||
|
||||
static inline void wfi()
|
||||
{
|
||||
asm volatile ("wfi" ::: "memory");
|
||||
}
|
||||
|
||||
#endif // !__ASSEMBLER__
|
||||
|
||||
#define IPI_SOFT 0x1
|
||||
#define IPI_FENCE_I 0x2
|
||||
#define IPI_SFENCE_VMA 0x4
|
||||
|
||||
#define MACHINE_STACK_SIZE RISCV_PGSIZE
|
||||
#define MENTRY_HLS_OFFSET (INTEGER_CONTEXT_SIZE + SOFT_FLOAT_CONTEXT_SIZE)
|
||||
#define MENTRY_FRAME_SIZE (MENTRY_HLS_OFFSET + HLS_SIZE)
|
||||
#define MENTRY_IPI_OFFSET (MENTRY_HLS_OFFSET)
|
||||
#define MENTRY_IPI_PENDING_OFFSET (MENTRY_HLS_OFFSET + REGBYTES)
|
||||
|
||||
#ifdef __riscv_flen
|
||||
# define SOFT_FLOAT_CONTEXT_SIZE 0
|
||||
#else
|
||||
# define SOFT_FLOAT_CONTEXT_SIZE (8 * 32)
|
||||
#endif
|
||||
#define HLS_SIZE 64
|
||||
#define INTEGER_CONTEXT_SIZE (32 * REGBYTES)
|
||||
|
||||
#endif
|
@ -1,64 +0,0 @@
|
||||
#include "emulation.h"
|
||||
|
||||
#ifndef __riscv_muldiv
|
||||
|
||||
#if __riscv_xlen == 64
|
||||
typedef __int128 double_int;
|
||||
#else
|
||||
typedef int64_t double_int;
|
||||
#endif
|
||||
|
||||
// These routines rely on the compiler to turn these operations into libcalls
|
||||
// when not natively supported. So work on making those go fast.
|
||||
|
||||
DECLARE_EMULATION_FUNC(emulate_mul_div)
|
||||
{
|
||||
uintptr_t rs1 = GET_RS1(insn, regs), rs2 = GET_RS2(insn, regs), val;
|
||||
|
||||
if ((insn & MASK_MUL) == MATCH_MUL)
|
||||
val = rs1 * rs2;
|
||||
else if ((insn & MASK_DIV) == MATCH_DIV)
|
||||
val = (intptr_t)rs1 / (intptr_t)rs2;
|
||||
else if ((insn & MASK_DIVU) == MATCH_DIVU)
|
||||
val = rs1 / rs2;
|
||||
else if ((insn & MASK_REM) == MATCH_REM)
|
||||
val = (intptr_t)rs1 % (intptr_t)rs2;
|
||||
else if ((insn & MASK_REMU) == MATCH_REMU)
|
||||
val = rs1 % rs2;
|
||||
else if ((insn & MASK_MULH) == MATCH_MULH)
|
||||
val = ((double_int)(intptr_t)rs1 * (double_int)(intptr_t)rs2) >> (8 * sizeof(rs1));
|
||||
else if ((insn & MASK_MULHU) == MATCH_MULHU)
|
||||
val = ((double_int)rs1 * (double_int)rs2) >> (8 * sizeof(rs1));
|
||||
else if ((insn & MASK_MULHSU) == MATCH_MULHSU)
|
||||
val = ((double_int)(intptr_t)rs1 * (double_int)rs2) >> (8 * sizeof(rs1));
|
||||
else
|
||||
return truly_illegal_insn(regs, mcause, mepc, mstatus, insn);
|
||||
|
||||
SET_RD(insn, regs, val);
|
||||
}
|
||||
|
||||
#if __riscv_xlen == 64
|
||||
|
||||
DECLARE_EMULATION_FUNC(emulate_mul_div32)
|
||||
{
|
||||
uint32_t rs1 = GET_RS1(insn, regs), rs2 = GET_RS2(insn, regs);
|
||||
int32_t val;
|
||||
|
||||
if ((insn & MASK_MULW) == MATCH_MULW)
|
||||
val = rs1 * rs2;
|
||||
else if ((insn & MASK_DIVW) == MATCH_DIVW)
|
||||
val = (int32_t)rs1 / (int32_t)rs2;
|
||||
else if ((insn & MASK_DIVUW) == MATCH_DIVUW)
|
||||
val = rs1 / rs2;
|
||||
else if ((insn & MASK_REMW) == MATCH_REMW)
|
||||
val = (int32_t)rs1 % (int32_t)rs2;
|
||||
else if ((insn & MASK_REMUW) == MATCH_REMUW)
|
||||
val = rs1 % rs2;
|
||||
else
|
||||
return truly_illegal_insn(regs, mcause, mepc, mstatus, insn);
|
||||
|
||||
SET_RD(insn, regs, val);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
@ -1,76 +0,0 @@
|
||||
#include <string.h>
|
||||
#include "uart.h"
|
||||
#include "fdt.h"
|
||||
|
||||
volatile uint32_t* uart;
|
||||
|
||||
void uart_putchar(uint8_t ch)
|
||||
{
|
||||
#ifdef __riscv_atomic
|
||||
int32_t r;
|
||||
do {
|
||||
__asm__ __volatile__ (
|
||||
"amoor.w %0, %2, %1\n"
|
||||
: "=r" (r), "+A" (uart[UART_REG_TXFIFO])
|
||||
: "r" (ch));
|
||||
} while (r < 0);
|
||||
#else
|
||||
volatile uint32_t *tx = uart + UART_REG_TXFIFO;
|
||||
while ((int32_t)(*tx) < 0);
|
||||
*tx = ch;
|
||||
#endif
|
||||
}
|
||||
|
||||
int uart_getchar()
|
||||
{
|
||||
int32_t ch = uart[UART_REG_RXFIFO];
|
||||
if (ch < 0) return -1;
|
||||
return ch;
|
||||
}
|
||||
|
||||
struct uart_scan
|
||||
{
|
||||
int compat;
|
||||
uint64_t reg;
|
||||
};
|
||||
|
||||
static void uart_open(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct uart_scan *scan = (struct uart_scan *)extra;
|
||||
memset(scan, 0, sizeof(*scan));
|
||||
}
|
||||
|
||||
static void uart_prop(const struct fdt_scan_prop *prop, void *extra)
|
||||
{
|
||||
struct uart_scan *scan = (struct uart_scan *)extra;
|
||||
if (!strcmp(prop->name, "compatible") && !strcmp((const char*)prop->value, "sifive,uart0")) {
|
||||
scan->compat = 1;
|
||||
} else if (!strcmp(prop->name, "reg")) {
|
||||
fdt_get_address(prop->node->parent, prop->value, &scan->reg);
|
||||
}
|
||||
}
|
||||
|
||||
static void uart_done(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct uart_scan *scan = (struct uart_scan *)extra;
|
||||
if (!scan->compat || !scan->reg || uart) return;
|
||||
|
||||
// Enable Rx/Tx channels
|
||||
uart = (void*)(uintptr_t)scan->reg;
|
||||
uart[UART_REG_TXCTRL] = UART_TXEN;
|
||||
uart[UART_REG_RXCTRL] = UART_RXEN;
|
||||
}
|
||||
|
||||
void query_uart(uintptr_t fdt)
|
||||
{
|
||||
struct fdt_cb cb;
|
||||
struct uart_scan scan;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.open = uart_open;
|
||||
cb.prop = uart_prop;
|
||||
cb.done = uart_done;
|
||||
cb.extra = &scan;
|
||||
|
||||
fdt_scan(fdt, &cb);
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
#ifndef _RISCV_UART_H
|
||||
#define _RISCV_UART_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern volatile uint32_t* uart;
|
||||
|
||||
#define UART_REG_TXFIFO 0
|
||||
#define UART_REG_RXFIFO 1
|
||||
#define UART_REG_TXCTRL 2
|
||||
#define UART_REG_RXCTRL 3
|
||||
#define UART_REG_DIV 4
|
||||
|
||||
#define UART_TXEN 0x1
|
||||
#define UART_RXEN 0x1
|
||||
|
||||
void uart_putchar(uint8_t ch);
|
||||
int uart_getchar();
|
||||
void query_uart(uintptr_t dtb);
|
||||
|
||||
#endif
|
@ -1,76 +0,0 @@
|
||||
#include <string.h>
|
||||
#include "uart16550.h"
|
||||
#include "fdt.h"
|
||||
|
||||
volatile uint8_t* uart16550;
|
||||
|
||||
#define UART_REG_QUEUE 0
|
||||
#define UART_REG_LINESTAT 5
|
||||
#define UART_REG_STATUS_RX 0x01
|
||||
#define UART_REG_STATUS_TX 0x20
|
||||
|
||||
void uart16550_putchar(uint8_t ch)
|
||||
{
|
||||
while ((uart16550[UART_REG_LINESTAT] & UART_REG_STATUS_TX) == 0);
|
||||
uart16550[UART_REG_QUEUE] = ch;
|
||||
}
|
||||
|
||||
int uart16550_getchar()
|
||||
{
|
||||
if (uart16550[UART_REG_LINESTAT] & UART_REG_STATUS_RX)
|
||||
return uart16550[UART_REG_QUEUE];
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct uart16550_scan
|
||||
{
|
||||
int compat;
|
||||
uint64_t reg;
|
||||
};
|
||||
|
||||
static void uart16550_open(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct uart16550_scan *scan = (struct uart16550_scan *)extra;
|
||||
memset(scan, 0, sizeof(*scan));
|
||||
}
|
||||
|
||||
static void uart16550_prop(const struct fdt_scan_prop *prop, void *extra)
|
||||
{
|
||||
struct uart16550_scan *scan = (struct uart16550_scan *)extra;
|
||||
if (!strcmp(prop->name, "compatible") && !strcmp((const char*)prop->value, "ns16550a")) {
|
||||
scan->compat = 1;
|
||||
} else if (!strcmp(prop->name, "reg")) {
|
||||
fdt_get_address(prop->node->parent, prop->value, &scan->reg);
|
||||
}
|
||||
}
|
||||
|
||||
static void uart16550_done(const struct fdt_scan_node *node, void *extra)
|
||||
{
|
||||
struct uart16550_scan *scan = (struct uart16550_scan *)extra;
|
||||
if (!scan->compat || !scan->reg || uart16550) return;
|
||||
|
||||
uart16550 = (void*)(uintptr_t)scan->reg;
|
||||
// http://wiki.osdev.org/Serial_Ports
|
||||
uart16550[1] = 0x00; // Disable all interrupts
|
||||
uart16550[3] = 0x80; // Enable DLAB (set baud rate divisor)
|
||||
uart16550[0] = 0x03; // Set divisor to 3 (lo byte) 38400 baud
|
||||
uart16550[1] = 0x00; // (hi byte)
|
||||
uart16550[3] = 0x03; // 8 bits, no parity, one stop bit
|
||||
uart16550[2] = 0xC7; // Enable FIFO, clear them, with 14-byte threshold
|
||||
uart16550[4] = 0x0B;
|
||||
uart16550[1] = 0x01; // Enable interrupt
|
||||
}
|
||||
|
||||
void query_uart16550(uintptr_t fdt)
|
||||
{
|
||||
struct fdt_cb cb;
|
||||
struct uart16550_scan scan;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.open = uart16550_open;
|
||||
cb.prop = uart16550_prop;
|
||||
cb.done = uart16550_done;
|
||||
cb.extra = &scan;
|
||||
|
||||
fdt_scan(fdt, &cb);
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
#ifndef _RISCV_16550_H
|
||||
#define _RISCV_16550_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
extern volatile uint8_t* uart16550;
|
||||
|
||||
void uart16550_putchar(uint8_t ch);
|
||||
int uart16550_getchar();
|
||||
void query_uart16550(uintptr_t dtb);
|
||||
|
||||
#endif
|
@ -1,103 +0,0 @@
|
||||
#ifndef _RISCV_MISALIGNED_H
|
||||
#define _RISCV_MISALIGNED_H
|
||||
|
||||
#include "encoding.h"
|
||||
#include "bits.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define DECLARE_UNPRIVILEGED_LOAD_FUNCTION(type, insn) \
|
||||
static inline type load_##type(const type* addr, uintptr_t mepc) \
|
||||
{ \
|
||||
register uintptr_t __mepc asm ("a2") = mepc; \
|
||||
register uintptr_t __mstatus asm ("a3"); \
|
||||
type val; \
|
||||
asm ("csrrs %0, mstatus, %3\n" \
|
||||
#insn " %1, %2\n" \
|
||||
"csrw mstatus, %0" \
|
||||
: "+&r" (__mstatus), "=&r" (val) \
|
||||
: "m" (*addr), "r" (MSTATUS_MPRV), "r" (__mepc)); \
|
||||
return val; \
|
||||
}
|
||||
|
||||
#define DECLARE_UNPRIVILEGED_STORE_FUNCTION(type, insn) \
|
||||
static inline void store_##type(type* addr, type val, uintptr_t mepc) \
|
||||
{ \
|
||||
register uintptr_t __mepc asm ("a2") = mepc; \
|
||||
register uintptr_t __mstatus asm ("a3"); \
|
||||
asm volatile ("csrrs %0, mstatus, %3\n" \
|
||||
#insn " %1, %2\n" \
|
||||
"csrw mstatus, %0" \
|
||||
: "+&r" (__mstatus) \
|
||||
: "r" (val), "m" (*addr), "r" (MSTATUS_MPRV), \
|
||||
"r" (__mepc)); \
|
||||
}
|
||||
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(uint8_t, lbu)
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(uint16_t, lhu)
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(int8_t, lb)
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(int16_t, lh)
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(int32_t, lw)
|
||||
DECLARE_UNPRIVILEGED_STORE_FUNCTION(uint8_t, sb)
|
||||
DECLARE_UNPRIVILEGED_STORE_FUNCTION(uint16_t, sh)
|
||||
DECLARE_UNPRIVILEGED_STORE_FUNCTION(uint32_t, sw)
|
||||
#if __riscv_xlen == 64
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(uint32_t, lwu)
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(uint64_t, ld)
|
||||
DECLARE_UNPRIVILEGED_STORE_FUNCTION(uint64_t, sd)
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(uintptr_t, ld)
|
||||
#else
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(uint32_t, lw)
|
||||
DECLARE_UNPRIVILEGED_LOAD_FUNCTION(uintptr_t, lw)
|
||||
|
||||
static inline uint64_t load_uint64_t(const uint64_t* addr, uintptr_t mepc)
|
||||
{
|
||||
return load_uint32_t((uint32_t*)addr, mepc)
|
||||
+ ((uint64_t)load_uint32_t((uint32_t*)addr + 1, mepc) << 32);
|
||||
}
|
||||
|
||||
static inline void store_uint64_t(uint64_t* addr, uint64_t val, uintptr_t mepc)
|
||||
{
|
||||
store_uint32_t((uint32_t*)addr, val, mepc);
|
||||
store_uint32_t((uint32_t*)addr + 1, val >> 32, mepc);
|
||||
}
|
||||
#endif
|
||||
|
||||
static uintptr_t __attribute__((always_inline)) get_insn(uintptr_t mepc, uintptr_t* mstatus)
|
||||
{
|
||||
register uintptr_t __mepc asm ("a2") = mepc;
|
||||
register uintptr_t __mstatus asm ("a3");
|
||||
uintptr_t val;
|
||||
#ifndef __riscv_compressed
|
||||
asm ("csrrs %[mstatus], mstatus, %[mprv]\n"
|
||||
STR(LWU) " %[insn], (%[addr])\n"
|
||||
"csrw mstatus, %[mstatus]"
|
||||
: [mstatus] "+&r" (__mstatus), [insn] "=&r" (val)
|
||||
: [mprv] "r" (MSTATUS_MPRV | MSTATUS_MXR), [addr] "r" (__mepc));
|
||||
#else
|
||||
uintptr_t rvc_mask = 3, tmp;
|
||||
asm ("csrrs %[mstatus], mstatus, %[mprv]\n"
|
||||
"and %[tmp], %[addr], 2\n"
|
||||
"bnez %[tmp], 1f\n"
|
||||
STR(LWU) " %[insn], (%[addr])\n"
|
||||
"and %[tmp], %[insn], %[rvc_mask]\n"
|
||||
"beq %[tmp], %[rvc_mask], 2f\n"
|
||||
"sll %[insn], %[insn], %[xlen_minus_16]\n"
|
||||
"srl %[insn], %[insn], %[xlen_minus_16]\n"
|
||||
"j 2f\n"
|
||||
"1:\n"
|
||||
"lhu %[insn], (%[addr])\n"
|
||||
"and %[tmp], %[insn], %[rvc_mask]\n"
|
||||
"bne %[tmp], %[rvc_mask], 2f\n"
|
||||
"lhu %[tmp], 2(%[addr])\n"
|
||||
"sll %[tmp], %[tmp], 16\n"
|
||||
"add %[insn], %[insn], %[tmp]\n"
|
||||
"2: csrw mstatus, %[mstatus]"
|
||||
: [mstatus] "+&r" (__mstatus), [insn] "=&r" (val), [tmp] "=&r" (tmp)
|
||||
: [mprv] "r" (MSTATUS_MPRV | MSTATUS_MXR), [addr] "r" (__mepc),
|
||||
[rvc_mask] "r" (rvc_mask), [xlen_minus_16] "i" (__riscv_xlen - 16));
|
||||
#endif
|
||||
*mstatus = __mstatus;
|
||||
return val;
|
||||
}
|
||||
|
||||
#endif
|
@ -1,35 +0,0 @@
|
||||
#ifndef _VM_H
|
||||
#define _VM_H
|
||||
|
||||
#include "encoding.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define MEGAPAGE_SIZE ((uintptr_t)(RISCV_PGSIZE << RISCV_PGLEVEL_BITS))
|
||||
#if __riscv_xlen == 64
|
||||
# define SATP_MODE_CHOICE INSERT_FIELD(0, SATP64_MODE, SATP_MODE_SV39)
|
||||
# define VA_BITS 39
|
||||
# define GIGAPAGE_SIZE (MEGAPAGE_SIZE << RISCV_PGLEVEL_BITS)
|
||||
#else
|
||||
# define SATP_MODE_CHOICE INSERT_FIELD(0, SATP32_MODE, SATP_MODE_SV32)
|
||||
# define VA_BITS 32
|
||||
#endif
|
||||
|
||||
typedef uintptr_t pte_t;
|
||||
extern pte_t* root_page_table;
|
||||
|
||||
static inline void flush_tlb()
|
||||
{
|
||||
asm volatile ("sfence.vma");
|
||||
}
|
||||
|
||||
static inline pte_t pte_create(uintptr_t ppn, int type)
|
||||
{
|
||||
return (ppn << PTE_PPN_SHIFT) | PTE_V | type;
|
||||
}
|
||||
|
||||
static inline pte_t ptd_create(uintptr_t ppn)
|
||||
{
|
||||
return pte_create(ppn, PTE_V);
|
||||
}
|
||||
|
||||
#endif
|
1526
riscv-pk/scripts/config.guess
vendored
1526
riscv-pk/scripts/config.guess
vendored
File diff suppressed because it is too large
Load Diff
1662
riscv-pk/scripts/config.sub
vendored
1662
riscv-pk/scripts/config.sub
vendored
File diff suppressed because it is too large
Load Diff
@ -1,238 +0,0 @@
|
||||
#! /bin/sh
|
||||
#
|
||||
# install - install a program, script, or datafile
|
||||
# This comes from X11R5.
|
||||
#
|
||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||
# `make' implicit rules from creating a file called install from it
|
||||
# when there is no Makefile.
|
||||
#
|
||||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch.
|
||||
#
|
||||
|
||||
|
||||
# set DOITPROG to echo to test this script
|
||||
|
||||
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||
doit="${DOITPROG-}"
|
||||
|
||||
|
||||
# put in absolute paths if you don't have them in your path; or use env. vars.
|
||||
|
||||
mvprog="${MVPROG-mv}"
|
||||
cpprog="${CPPROG-cp}"
|
||||
chmodprog="${CHMODPROG-chmod}"
|
||||
chownprog="${CHOWNPROG-chown}"
|
||||
chgrpprog="${CHGRPPROG-chgrp}"
|
||||
stripprog="${STRIPPROG-strip}"
|
||||
rmprog="${RMPROG-rm}"
|
||||
mkdirprog="${MKDIRPROG-mkdir}"
|
||||
|
||||
tranformbasename=""
|
||||
transform_arg=""
|
||||
instcmd="$mvprog"
|
||||
chmodcmd="$chmodprog 0755"
|
||||
chowncmd=""
|
||||
chgrpcmd=""
|
||||
stripcmd=""
|
||||
rmcmd="$rmprog -f"
|
||||
mvcmd="$mvprog"
|
||||
src=""
|
||||
dst=""
|
||||
dir_arg=""
|
||||
|
||||
while [ x"$1" != x ]; do
|
||||
case $1 in
|
||||
-c) instcmd="$cpprog"
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-d) dir_arg=true
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-m) chmodcmd="$chmodprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-s) stripcmd="$stripprog"
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
|
||||
shift
|
||||
continue;;
|
||||
|
||||
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
|
||||
shift
|
||||
continue;;
|
||||
|
||||
*) if [ x"$src" = x ]
|
||||
then
|
||||
src=$1
|
||||
else
|
||||
# this colon is to work around a 386BSD /bin/sh bug
|
||||
:
|
||||
dst=$1
|
||||
fi
|
||||
shift
|
||||
continue;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ x"$src" = x ]
|
||||
then
|
||||
echo "install: no input file specified"
|
||||
exit 1
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
if [ x"$dir_arg" != x ]; then
|
||||
dst=$src
|
||||
src=""
|
||||
|
||||
if [ -d $dst ]; then
|
||||
instcmd=:
|
||||
else
|
||||
instcmd=mkdir
|
||||
fi
|
||||
else
|
||||
|
||||
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# if $src (and thus $dsttmp) contains '*'.
|
||||
|
||||
if [ -f $src -o -d $src ]
|
||||
then
|
||||
true
|
||||
else
|
||||
echo "install: $src does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ x"$dst" = x ]
|
||||
then
|
||||
echo "install: no destination specified"
|
||||
exit 1
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
# If destination is a directory, append the input filename; if your system
|
||||
# does not like double slashes in filenames, you may need to add some logic
|
||||
|
||||
if [ -d $dst ]
|
||||
then
|
||||
dst="$dst"/`basename $src`
|
||||
else
|
||||
true
|
||||
fi
|
||||
fi
|
||||
|
||||
## this sed command emulates the dirname command
|
||||
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
|
||||
|
||||
# Make sure that the destination directory exists.
|
||||
# this part is taken from Noah Friedman's mkinstalldirs script
|
||||
|
||||
# Skip lots of stat calls in the usual case.
|
||||
if [ ! -d "$dstdir" ]; then
|
||||
defaultIFS='
|
||||
'
|
||||
IFS="${IFS-${defaultIFS}}"
|
||||
|
||||
oIFS="${IFS}"
|
||||
# Some sh's can't handle IFS=/ for some reason.
|
||||
IFS='%'
|
||||
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
|
||||
IFS="${oIFS}"
|
||||
|
||||
pathcomp=''
|
||||
|
||||
while [ $# -ne 0 ] ; do
|
||||
pathcomp="${pathcomp}${1}"
|
||||
shift
|
||||
|
||||
if [ ! -d "${pathcomp}" ] ;
|
||||
then
|
||||
$mkdirprog "${pathcomp}"
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
pathcomp="${pathcomp}/"
|
||||
done
|
||||
fi
|
||||
|
||||
if [ x"$dir_arg" != x ]
|
||||
then
|
||||
$doit $instcmd $dst &&
|
||||
|
||||
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
|
||||
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
|
||||
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
|
||||
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
|
||||
else
|
||||
|
||||
# If we're going to rename the final executable, determine the name now.
|
||||
|
||||
if [ x"$transformarg" = x ]
|
||||
then
|
||||
dstfile=`basename $dst`
|
||||
else
|
||||
dstfile=`basename $dst $transformbasename |
|
||||
sed $transformarg`$transformbasename
|
||||
fi
|
||||
|
||||
# don't allow the sed command to completely eliminate the filename
|
||||
|
||||
if [ x"$dstfile" = x ]
|
||||
then
|
||||
dstfile=`basename $dst`
|
||||
else
|
||||
true
|
||||
fi
|
||||
|
||||
# Make a temp file name in the proper directory.
|
||||
|
||||
dsttmp=$dstdir/#inst.$$#
|
||||
|
||||
# Move or copy the file name to the temp name
|
||||
|
||||
$doit $instcmd $src $dsttmp &&
|
||||
|
||||
trap "rm -f ${dsttmp}" 0 &&
|
||||
|
||||
# and set any options; do chmod last to preserve setuid bits
|
||||
|
||||
# If any of these fail, we abort the whole thing. If we want to
|
||||
# ignore errors from any of these, just make sure not to ignore
|
||||
# errors from the above "$doit $instcmd $src $dsttmp" command.
|
||||
|
||||
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
|
||||
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
|
||||
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
|
||||
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
|
||||
$doit $rmcmd -f $dstdir/$dstfile &&
|
||||
$doit $mvcmd $dsttmp $dstdir/$dstfile
|
||||
|
||||
fi &&
|
||||
|
||||
|
||||
exit 0
|
@ -1,40 +0,0 @@
|
||||
#! /bin/sh
|
||||
# mkinstalldirs --- make directory hierarchy
|
||||
# Author: Noah Friedman <friedman@prep.ai.mit.edu>
|
||||
# Created: 1993-05-16
|
||||
# Public domain
|
||||
|
||||
# $Id: mkinstalldirs,v 1.1 2003/09/09 22:24:03 mhampton Exp $
|
||||
|
||||
errstatus=0
|
||||
|
||||
for file
|
||||
do
|
||||
set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
|
||||
shift
|
||||
|
||||
pathcomp=
|
||||
for d
|
||||
do
|
||||
pathcomp="$pathcomp$d"
|
||||
case "$pathcomp" in
|
||||
-* ) pathcomp=./$pathcomp ;;
|
||||
esac
|
||||
|
||||
if test ! -d "$pathcomp"; then
|
||||
echo "mkdir $pathcomp" 1>&2
|
||||
|
||||
mkdir "$pathcomp" || lasterr=$?
|
||||
|
||||
if test ! -d "$pathcomp"; then
|
||||
errstatus=$lasterr
|
||||
fi
|
||||
fi
|
||||
|
||||
pathcomp="$pathcomp/"
|
||||
done
|
||||
done
|
||||
|
||||
exit $errstatus
|
||||
|
||||
# mkinstalldirs ends here
|
@ -1,117 +0,0 @@
|
||||
#!/bin/bash
|
||||
#=========================================================================
|
||||
# vcs-version.sh [options] [src-dir]
|
||||
#=========================================================================
|
||||
#
|
||||
# -h Display this message
|
||||
# -v Verbose mode
|
||||
#
|
||||
# This script will create a version string by querying a version control
|
||||
# system. The string is appropriate for use in installations and
|
||||
# distributions. Currently this script assumes we are using git as our
|
||||
# version control system but it would be possible to check and see if we
|
||||
# are using an alternative version control system and create a version
|
||||
# string appropriately.
|
||||
#
|
||||
# The script uses git describe plus a few other git commands to create a
|
||||
# version strings in the following format:
|
||||
#
|
||||
# X.Y[-Z-gN][-dirty]
|
||||
#
|
||||
# where X is the major release, Y is the minor release, Z is the number
|
||||
# of commits since the X.Y release, N is an eight digit abbreviated SHA
|
||||
# hash of the most recent commit and the dirty suffix is appended when
|
||||
# the working directory used to create the installation or distribution
|
||||
# is not a pristine checkout. Here are some example version strings:
|
||||
#
|
||||
# 0.0 : initial import
|
||||
# 0.0-3-g99ef6933 : 3rd commit since initial import (N=99ef6933)
|
||||
# 1.0 : release 1.0
|
||||
# 1.1-12-g3487ab12 : 12th commit since release 1.1 (N=3487ab12)
|
||||
# 1.1-12-g3487ab12-dirty : 12th commit since release 1.1 (N=3487ab12)
|
||||
#
|
||||
# The last example is from a dirty working directory. To find the last
|
||||
# release, the script looks for the last tag (does not need to be an
|
||||
# annotated tag, but probably should be) which matches the format rel-*.
|
||||
# If there is no such tag in the history, then the script uses 0.0 as
|
||||
# the release number and counts the total number of commits since the
|
||||
# original import for the commit count.
|
||||
#
|
||||
# If the current directory is not within the working directory, then the
|
||||
# path to the source directory should be supplied on the command line.
|
||||
#
|
||||
# Author : Christopher Batten
|
||||
# Date : August 5, 2009
|
||||
|
||||
set -e
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Command line parsing
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
if ( test "$1" = "-h" ); then
|
||||
echo ""
|
||||
sed -n '3p' $0 | sed -e 's/#//'
|
||||
sed -n '5,/^$/p' $0 | sed -e 's/#//'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Source directory command line option
|
||||
|
||||
src_dir="."
|
||||
if ( test -n "$1" ); then
|
||||
src_dir="$1"
|
||||
fi
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Verify source directory
|
||||
#-------------------------------------------------------------------------
|
||||
# If the source directory is not a git working directory output a
|
||||
# question mark. A distribution will not be in a working directory, but
|
||||
# the build system should be structured such that this script is not
|
||||
# executed (and instead the version information should probably come
|
||||
# from configure). If the user does not specify a source directory use
|
||||
# the current directory.
|
||||
|
||||
if !( git rev-parse --is-inside-work-tree &> /dev/null ); then
|
||||
echo "?"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
top_dir=`git rev-parse --show-cdup`
|
||||
cd ./${top_dir}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Create the version string
|
||||
#-------------------------------------------------------------------------
|
||||
# See if we can do a describe based on a tag and if not use a default
|
||||
# release number of 0.0 so that we always get canonical version number
|
||||
|
||||
if ( git describe --tags --match "rel-*" &> /dev/null ); then
|
||||
ver_str=`git describe --tags --match "rel-*" | sed 's/rel-//'`
|
||||
else
|
||||
ver_num="0.0"
|
||||
ver_commits=`git rev-list --all | wc -l | tr -d " "`
|
||||
ver_sha=`git describe --tags --match "rel-*" --always`
|
||||
ver_str="${ver_num}-${ver_commits}-g${ver_sha}"
|
||||
fi
|
||||
|
||||
# Add a dirty suffix if working directory is dirty
|
||||
|
||||
if !( git diff --quiet ); then
|
||||
ver_str="${ver_str}-dirty"
|
||||
else
|
||||
untracked=`git ls-files --directory --exclude-standard --others -t`
|
||||
if ( test -n "${untracked}" ); then
|
||||
ver_str="${ver_str}-dirty"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Output the final version string
|
||||
|
||||
echo "${ver_str}"
|
||||
|
||||
# Final exit status
|
||||
|
||||
exit 0;
|
||||
|
@ -1,98 +0,0 @@
|
||||
// See LICENSE for license details.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
int vsnprintf(char* out, size_t n, const char* s, va_list vl)
|
||||
{
|
||||
bool format = false;
|
||||
bool longarg = false;
|
||||
size_t pos = 0;
|
||||
for( ; *s; s++)
|
||||
{
|
||||
if(format)
|
||||
{
|
||||
switch(*s)
|
||||
{
|
||||
case 'l':
|
||||
longarg = true;
|
||||
break;
|
||||
case 'p':
|
||||
longarg = true;
|
||||
if (++pos < n) out[pos-1] = '0';
|
||||
if (++pos < n) out[pos-1] = 'x';
|
||||
case 'x':
|
||||
{
|
||||
long num = longarg ? va_arg(vl, long) : va_arg(vl, int);
|
||||
for(int i = 2*(longarg ? sizeof(long) : sizeof(int))-1; i >= 0; i--) {
|
||||
int d = (num >> (4*i)) & 0xF;
|
||||
if (++pos < n) out[pos-1] = (d < 10 ? '0'+d : 'a'+d-10);
|
||||
}
|
||||
longarg = false;
|
||||
format = false;
|
||||
break;
|
||||
}
|
||||
case 'd':
|
||||
{
|
||||
long num = longarg ? va_arg(vl, long) : va_arg(vl, int);
|
||||
if (num < 0) {
|
||||
num = -num;
|
||||
if (++pos < n) out[pos-1] = '-';
|
||||
}
|
||||
long digits = 1;
|
||||
for (long nn = num; nn /= 10; digits++)
|
||||
;
|
||||
for (int i = digits-1; i >= 0; i--) {
|
||||
if (pos + i + 1 < n) out[pos + i] = '0' + (num % 10);
|
||||
num /= 10;
|
||||
}
|
||||
pos += digits;
|
||||
longarg = false;
|
||||
format = false;
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
{
|
||||
const char* s2 = va_arg(vl, const char*);
|
||||
while (*s2) {
|
||||
if (++pos < n)
|
||||
out[pos-1] = *s2;
|
||||
s2++;
|
||||
}
|
||||
longarg = false;
|
||||
format = false;
|
||||
break;
|
||||
}
|
||||
case 'c':
|
||||
{
|
||||
if (++pos < n) out[pos-1] = (char)va_arg(vl,int);
|
||||
longarg = false;
|
||||
format = false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(*s == '%')
|
||||
format = true;
|
||||
else
|
||||
if (++pos < n) out[pos-1] = *s;
|
||||
}
|
||||
if (pos < n)
|
||||
out[pos] = 0;
|
||||
else if (n)
|
||||
out[n-1] = 0;
|
||||
return pos;
|
||||
}
|
||||
|
||||
int snprintf(char* out, size_t n, const char* s, ...)
|
||||
{
|
||||
va_list vl;
|
||||
va_start(vl, s);
|
||||
int res = vsnprintf(out, n, s, vl);
|
||||
va_end(vl);
|
||||
return res;
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <ctype.h>
|
||||
|
||||
void* memcpy(void* dest, const void* src, size_t len)
|
||||
{
|
||||
const char* s = src;
|
||||
char *d = dest;
|
||||
|
||||
if ((((uintptr_t)dest | (uintptr_t)src) & (sizeof(uintptr_t)-1)) == 0) {
|
||||
while ((void*)d < (dest + len - (sizeof(uintptr_t)-1))) {
|
||||
*(uintptr_t*)d = *(const uintptr_t*)s;
|
||||
d += sizeof(uintptr_t);
|
||||
s += sizeof(uintptr_t);
|
||||
}
|
||||
}
|
||||
|
||||
while (d < (char*)(dest + len))
|
||||
*d++ = *s++;
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
void* memset(void* dest, int byte, size_t len)
|
||||
{
|
||||
if ((((uintptr_t)dest | len) & (sizeof(uintptr_t)-1)) == 0) {
|
||||
uintptr_t word = byte & 0xFF;
|
||||
word |= word << 8;
|
||||
word |= word << 16;
|
||||
word |= word << 16 << 16;
|
||||
|
||||
uintptr_t *d = dest;
|
||||
while (d < (uintptr_t*)(dest + len))
|
||||
*d++ = word;
|
||||
} else {
|
||||
char *d = dest;
|
||||
while (d < (char*)(dest + len))
|
||||
*d++ = byte;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
size_t strlen(const char *s)
|
||||
{
|
||||
const char *p = s;
|
||||
while (*p)
|
||||
p++;
|
||||
return p - s;
|
||||
}
|
||||
|
||||
int strcmp(const char* s1, const char* s2)
|
||||
{
|
||||
unsigned char c1, c2;
|
||||
|
||||
do {
|
||||
c1 = *s1++;
|
||||
c2 = *s2++;
|
||||
} while (c1 != 0 && c1 == c2);
|
||||
|
||||
return c1 - c2;
|
||||
}
|
||||
|
||||
char* strcpy(char* dest, const char* src)
|
||||
{
|
||||
char* d = dest;
|
||||
while ((*d++ = *src++))
|
||||
;
|
||||
return dest;
|
||||
}
|
||||
|
||||
long atol(const char* str)
|
||||
{
|
||||
long res = 0;
|
||||
int sign = 0;
|
||||
|
||||
while (*str == ' ')
|
||||
str++;
|
||||
|
||||
if (*str == '-' || *str == '+') {
|
||||
sign = *str == '-';
|
||||
str++;
|
||||
}
|
||||
|
||||
while (*str) {
|
||||
res *= 10;
|
||||
res += *str++ - '0';
|
||||
}
|
||||
|
||||
return sign ? -res : res;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user