commit
3f2beab52d
@ -0,0 +1,3 @@
|
||||
[submodule "riscv-pk"]
|
||||
path = riscv-pk
|
||||
url = https://github.com/riscv-and-rust-and-decaf/riscv-pk.git
|
@ -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.
|
@ -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) }
|
||||
}
|
||||
}
|
@ -1,16 +1,18 @@
|
||||
//! Raspberry PI 3 Model B/B+
|
||||
|
||||
use once::*;
|
||||
|
||||
pub mod irq;
|
||||
pub mod timer;
|
||||
pub mod serial;
|
||||
|
||||
pub const IO_REMAP_BASE: usize = bcm2837::IO_BASE;
|
||||
pub const IO_REMAP_END: usize = 0x40001000;
|
||||
|
||||
pub fn init() {
|
||||
// FIXME
|
||||
// assert_has_not_been_called!("board::init must be called only once");
|
||||
assert_has_not_been_called!("board::init must be called only once");
|
||||
|
||||
unsafe {
|
||||
serial::SERIAL_PORT.init();
|
||||
}
|
||||
serial::SERIAL_PORT.lock().init();
|
||||
|
||||
println!("Hello Raspberry Pi!");
|
||||
}
|
||||
|
@ -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,222 +1,310 @@
|
||||
//! Page table implementations for aarch64.
|
||||
|
||||
use ucore_memory::memory_set::*;
|
||||
use ucore_memory::PAGE_SIZE;
|
||||
use ucore_memory::paging::*;
|
||||
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};
|
||||
|
||||
type VirtAddr = usize;
|
||||
type PhysAddr = usize;
|
||||
// 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();
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
use alloc::alloc::{alloc, Layout};
|
||||
use crate::memory::{active_table, alloc_frame, dealloc_frame};
|
||||
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());
|
||||
|
||||
/// TODO
|
||||
pub struct ActivePageTable {
|
||||
// TODO
|
||||
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();
|
||||
}
|
||||
|
||||
impl ActivePageTable {
|
||||
/// TODO
|
||||
pub unsafe fn new() -> Self {
|
||||
unimplemented!()
|
||||
}
|
||||
pub struct ActivePageTable(RecursivePageTable<'static>);
|
||||
|
||||
pub fn token() -> usize {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
pub struct PageEntry(PageTableEntry);
|
||||
|
||||
impl PageTable for ActivePageTable {
|
||||
type Entry = PageEntry;
|
||||
|
||||
fn map(&mut self, addr: VirtAddr, target: PhysAddr) -> &mut Self::Entry {
|
||||
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 unmap(&mut self, addr: VirtAddr) {
|
||||
unimplemented!()
|
||||
|
||||
fn unmap(&mut self, addr: usize) {
|
||||
let (frame, flush) = self.0.unmap(Page::of_addr(addr)).unwrap();
|
||||
flush.flush();
|
||||
}
|
||||
|
||||
fn get_entry(&mut self, addr: VirtAddr) -> Option<&mut Self::Entry> {
|
||||
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) })
|
||||
}
|
||||
|
||||
// For testing with mock
|
||||
fn get_page_slice_mut<'a, 'b>(&'a mut self, addr: VirtAddr) -> &'b mut [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 read(&mut self, addr: VirtAddr) -> u8 {
|
||||
unimplemented!()
|
||||
fn read(&mut self, addr: usize) -> u8 {
|
||||
unsafe { *(addr as *const u8) }
|
||||
}
|
||||
|
||||
fn write(&mut self, addr: VirtAddr, data: u8) {
|
||||
unimplemented!()
|
||||
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!()
|
||||
}
|
||||
|
||||
/// Will be set when accessed
|
||||
fn accessed(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Will be set when written
|
||||
fn dirty(&self) -> bool {
|
||||
unimplemented!()
|
||||
let addr = VirtAddr::new_unchecked((self as *const _ as u64) << 9);
|
||||
tlb_invalidate(addr);
|
||||
}
|
||||
|
||||
/// Will PageFault when try to write page where writable=0
|
||||
fn writable(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Will PageFault when try to access page where present=0
|
||||
fn present(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
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 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() }
|
||||
|
||||
fn target(&self) -> PhysAddr {
|
||||
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);
|
||||
}
|
||||
|
||||
fn set_target(&mut self, target: PhysAddr) {
|
||||
unimplemented!()
|
||||
fn set_writable(&mut self, value: bool)
|
||||
{
|
||||
self.as_flags().set(EF::AP_RO, !value);
|
||||
self.as_flags().set(EF::WRITE, value);
|
||||
}
|
||||
|
||||
// For Copy-on-write extension
|
||||
fn writable_shared(&self) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn readonly_shared(&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 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!()
|
||||
}
|
||||
|
||||
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!()
|
||||
}
|
||||
|
||||
let flags = self.as_flags();
|
||||
flags.set(EF::WRITABLE_SHARED, writable);
|
||||
flags.set(EF::READONLY_SHARED, !writable);
|
||||
}
|
||||
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!()
|
||||
}
|
||||
}
|
||||
|
||||
#[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!()
|
||||
if self.user() {
|
||||
self.as_flags().set(EF::XN, !value)
|
||||
} else {
|
||||
self.as_flags().set(EF::PXN, !value)
|
||||
}
|
||||
}
|
||||
pub fn p2_index(&self) -> usize {
|
||||
unimplemented!()
|
||||
fn mmio(&self) -> bool {
|
||||
self.0.attr().value == MairDevice::attr_value().value
|
||||
}
|
||||
pub fn p1_index(&self) -> usize {
|
||||
unimplemented!()
|
||||
fn set_mmio(&mut self, value: bool) {
|
||||
if value {
|
||||
self.0.modify_attr(MairDevice::attr_value())
|
||||
} else {
|
||||
self.0.modify_attr(MairNormal::attr_value())
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
Subproject commit 71b2addd7ada2f07ca8e6f02787d706afa4fbe66
|
@ -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`.
|
@ -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
|
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
|
File diff suppressed because it is too large
Load Diff
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