From ef213d60bb29c9acee1eb47211cb341d89f89a6d Mon Sep 17 00:00:00 2001 From: koumingyang <1761674434@qq.com> Date: Thu, 15 Nov 2018 14:22:57 +0800 Subject: [PATCH 01/39] mmu --- crate/aarch64/Cargo.lock | 68 +++ crate/aarch64/Cargo.toml | 16 + crate/aarch64/src/addr.rs | 418 ++++++++++++++ crate/aarch64/src/asm.rs | 162 ++++++ crate/aarch64/src/barrier.rs | 87 +++ crate/aarch64/src/lib.rs | 29 + crate/aarch64/src/paging/frame_alloc.rs | 15 + crate/aarch64/src/paging/mod.rs | 528 +++++++++++++++++ crate/aarch64/src/paging/page_table.rs | 185 ++++++ crate/aarch64/src/paging/recursive.rs | 623 +++++++++++++++++++++ crate/aarch64/src/regs/cntfrq_el0.rs | 31 + crate/aarch64/src/regs/cnthctl_el2.rs | 75 +++ crate/aarch64/src/regs/cntp_ctl_el0.rs | 62 ++ crate/aarch64/src/regs/cntp_tval_el0.rs | 30 + crate/aarch64/src/regs/cntpct_el0.rs | 29 + crate/aarch64/src/regs/cntvoff_el2.rs | 32 ++ crate/aarch64/src/regs/currentel.rs | 52 ++ crate/aarch64/src/regs/daif.rs | 90 +++ crate/aarch64/src/regs/elr_el2.rs | 30 + crate/aarch64/src/regs/hcr_el2.rs | 123 ++++ crate/aarch64/src/regs/id_aa64mmfr0_el1.rs | 82 +++ crate/aarch64/src/regs/macros.rs | 85 +++ crate/aarch64/src/regs/mair_el1.rs | 105 ++++ crate/aarch64/src/regs/mod.rs | 51 ++ crate/aarch64/src/regs/mpidr_el1.rs | 30 + crate/aarch64/src/regs/sctlr_el1.rs | 103 ++++ crate/aarch64/src/regs/sp.rs | 28 + crate/aarch64/src/regs/sp_el0.rs | 31 + crate/aarch64/src/regs/sp_el1.rs | 36 ++ crate/aarch64/src/regs/spsel.rs | 48 ++ crate/aarch64/src/regs/spsr_el2.rs | 106 ++++ crate/aarch64/src/regs/tcr_el1.rs | 178 ++++++ crate/aarch64/src/regs/ttbr0_el1.rs | 56 ++ kernel/Cargo.lock | 14 + kernel/Cargo.toml | 1 + kernel/src/arch/aarch64/memory.rs | 65 ++- kernel/src/arch/aarch64/paging.rs | 339 ++++++----- kernel/src/lib.rs | 2 + 38 files changed, 3896 insertions(+), 149 deletions(-) create mode 100644 crate/aarch64/Cargo.lock create mode 100644 crate/aarch64/Cargo.toml create mode 100644 crate/aarch64/src/addr.rs create mode 100644 crate/aarch64/src/asm.rs create mode 100644 crate/aarch64/src/barrier.rs create mode 100644 crate/aarch64/src/lib.rs create mode 100644 crate/aarch64/src/paging/frame_alloc.rs create mode 100644 crate/aarch64/src/paging/mod.rs create mode 100644 crate/aarch64/src/paging/page_table.rs create mode 100644 crate/aarch64/src/paging/recursive.rs create mode 100644 crate/aarch64/src/regs/cntfrq_el0.rs create mode 100644 crate/aarch64/src/regs/cnthctl_el2.rs create mode 100644 crate/aarch64/src/regs/cntp_ctl_el0.rs create mode 100644 crate/aarch64/src/regs/cntp_tval_el0.rs create mode 100644 crate/aarch64/src/regs/cntpct_el0.rs create mode 100644 crate/aarch64/src/regs/cntvoff_el2.rs create mode 100644 crate/aarch64/src/regs/currentel.rs create mode 100644 crate/aarch64/src/regs/daif.rs create mode 100644 crate/aarch64/src/regs/elr_el2.rs create mode 100644 crate/aarch64/src/regs/hcr_el2.rs create mode 100644 crate/aarch64/src/regs/id_aa64mmfr0_el1.rs create mode 100644 crate/aarch64/src/regs/macros.rs create mode 100644 crate/aarch64/src/regs/mair_el1.rs create mode 100644 crate/aarch64/src/regs/mod.rs create mode 100644 crate/aarch64/src/regs/mpidr_el1.rs create mode 100644 crate/aarch64/src/regs/sctlr_el1.rs create mode 100644 crate/aarch64/src/regs/sp.rs create mode 100644 crate/aarch64/src/regs/sp_el0.rs create mode 100644 crate/aarch64/src/regs/sp_el1.rs create mode 100644 crate/aarch64/src/regs/spsel.rs create mode 100644 crate/aarch64/src/regs/spsr_el2.rs create mode 100644 crate/aarch64/src/regs/tcr_el1.rs create mode 100644 crate/aarch64/src/regs/ttbr0_el1.rs diff --git a/crate/aarch64/Cargo.lock b/crate/aarch64/Cargo.lock new file mode 100644 index 0000000..2d0a329 --- /dev/null +++ b/crate/aarch64/Cargo.lock @@ -0,0 +1,68 @@ +[[package]] +name = "aarch64" +version = "0.1.0" +dependencies = [ + "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cortex-a 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ux 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bit_field" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cortex-a" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "os_bootinfo" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "register" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "tock-registers 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tock-registers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "usize_conversions" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ux" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum cortex-a 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2b187d0d728b4a99ba1d79f9671b976bcdd71a8a2c719585218fd2dc14a4d08c" +"checksum os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "66481dbeb5e773e7bd85b63cd6042c30786f834338288c5ec4f3742673db360a" +"checksum register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e10f31b6d2299e5620986ad9fcdd66463e125ad72af4f403f9aedf7592d5ccdb" +"checksum tock-registers 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a385d94f3f62e60445a0adb9ff8d9621faa272234530d4c0f848ec98f88e316" +"checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" +"checksum ux 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53d8df5dd8d07fedccd202de1887d94481fadaea3db70479f459e8163a1fab41" diff --git a/crate/aarch64/Cargo.toml b/crate/aarch64/Cargo.toml new file mode 100644 index 0000000..7514dd0 --- /dev/null +++ b/crate/aarch64/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "aarch64" +version = "0.1.0" +authors = ["koumingyang <1761674434@qq.com>"] + +[dependencies] +register = "0.2.0" +bit_field = "0.9.0" +bitflags = "1.0.1" +usize_conversions = "0.2.0" +os_bootinfo = "0.2.0" +bare-metal = "0.2.0" + +[dependencies.ux] +default-features = false +version = "0.1.0" \ No newline at end of file diff --git a/crate/aarch64/src/addr.rs b/crate/aarch64/src/addr.rs new file mode 100644 index 0000000..e347567 --- /dev/null +++ b/crate/aarch64/src/addr.rs @@ -0,0 +1,418 @@ +use core::convert::{Into, TryInto}; +use core::fmt; +use core::ops::{Add, AddAssign, Sub, SubAssign}; + +use bit_field::BitField; +use usize_conversions::FromUsize; +use ux::*; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct VirtAddr(u64); + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[repr(transparent)] +pub struct PhysAddr(u64); + +#[derive(Debug)] +pub struct VirtAddrNotValid(u64); + +impl VirtAddr { + /// Creates a new canonical virtual address. + /// + /// This function performs sign extension of bit 47 to make the address canonical. Panics + /// if the bits in the range 48 to 64 contain data (i.e. are not null and no sign extension). + pub fn new(addr: u64) -> VirtAddr { + Self::try_new(addr).expect( + "invalid virtual address", + ) + } + + /// Tries to create a new canonical virtual address. + /// in aarch64, valid virtual address starts with 0x0000 or 0xffff. + pub fn try_new(addr: u64) -> Result { + match addr.get_bits(48..64) { + 0 | 0xffff => Ok(VirtAddr(addr)), // address is canonical + other => Err(VirtAddrNotValid(other)), + } + } + + pub fn new_unchecked(addr: u64) -> VirtAddr { + VirtAddr(addr) + } + + /// Creates a virtual address that points to `0`. + pub const fn zero() -> VirtAddr { + VirtAddr(0) + } + + /// Converts the address to an `u64`. + pub fn as_u64(self) -> u64 { + self.0 + } + + /// Creates a virtual address from the given pointer + pub fn from_ptr(ptr: *const T) -> Self { + use usize_conversions::FromUsize; + + Self::new(u64::from_usize(ptr as usize)) + } + + /// Converts the address to a raw pointer. + #[cfg(target_pointer_width = "64")] + pub fn as_ptr(self) -> *const T { + use usize_conversions::usize_from; + + usize_from(self.as_u64()) as *const T + } + + /// Converts the address to a mutable raw pointer. + #[cfg(target_pointer_width = "64")] + pub fn as_mut_ptr(self) -> *mut T { + self.as_ptr::() as *mut T + } + + /// Aligns the virtual address upwards to the given alignment. + /// + /// See the `align_up` function for more information. + pub fn align_up(self, align: U) -> Self + where + U: Into, + { + VirtAddr(align_up(self.0, align.into())) + } + + /// Aligns the virtual address downwards to the given alignment. + /// + /// See the `align_down` function for more information. + pub fn align_down(self, align: U) -> Self + where + U: Into, + { + VirtAddr(align_down(self.0, align.into())) + } + + /// Checks whether the virtual address has the demanded alignment. + pub fn is_aligned(self, align: U) -> bool + where + U: Into, + { + self.align_down(align) == self + } + + /// Returns the 12-bit page offset of this virtual address. + pub fn page_offset(&self) -> u12 { + u12::new((self.0 & 0xfff).try_into().unwrap()) + } + + /// Returns the 9-bit level 1 page table index. + pub fn p1_index(&self) -> u9 { + u9::new(((self.0 >> 12) & 0o777).try_into().unwrap()) + } + + /// Returns the 9-bit level 2 page table index. + pub fn p2_index(&self) -> u9 { + u9::new(((self.0 >> 12 >> 9) & 0o777).try_into().unwrap()) + } + + /// Returns the 9-bit level 3 page table index. + pub fn p3_index(&self) -> u9 { + u9::new(((self.0 >> 12 >> 9 >> 9) & 0o777).try_into().unwrap()) + } + + /// Returns the 9-bit level 4 page table index. + pub fn p4_index(&self) -> u9 { + u9::new(((self.0 >> 12 >> 9 >> 9 >> 9) & 0o777).try_into().unwrap()) + } +} + +impl fmt::Debug for VirtAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "VirtAddr({:#x})", self.0) + } +} + +impl Add for VirtAddr { + type Output = Self; + fn add(self, rhs: u64) -> Self::Output { + VirtAddr::new(self.0 + rhs) + } +} + +impl AddAssign for VirtAddr { + fn add_assign(&mut self, rhs: u64) { + *self = *self + rhs; + } +} + +impl Add for VirtAddr +where + u64: FromUsize, +{ + type Output = Self; + fn add(self, rhs: usize) -> Self::Output { + self + u64::from_usize(rhs) + } +} + +impl AddAssign for VirtAddr +where + u64: FromUsize, +{ + fn add_assign(&mut self, rhs: usize) { + self.add_assign(u64::from_usize(rhs)) + } +} + +impl Sub for VirtAddr { + type Output = Self; + fn sub(self, rhs: u64) -> Self::Output { + VirtAddr::new(self.0.checked_sub(rhs).unwrap()) + } +} + +impl SubAssign for VirtAddr { + fn sub_assign(&mut self, rhs: u64) { + *self = *self - rhs; + } +} + +impl Sub for VirtAddr +where + u64: FromUsize, +{ + type Output = Self; + fn sub(self, rhs: usize) -> Self::Output { + self - u64::from_usize(rhs) + } +} + +impl SubAssign for VirtAddr +where + u64: FromUsize, +{ + fn sub_assign(&mut self, rhs: usize) { + self.sub_assign(u64::from_usize(rhs)) + } +} + +impl Sub for VirtAddr { + type Output = u64; + fn sub(self, rhs: VirtAddr) -> Self::Output { + self.as_u64().checked_sub(rhs.as_u64()).unwrap() + } +} + +/// A passed `u64` was not a valid physical address. +/// +/// This means that bits 52 to 64 are not were not all null. +#[derive(Debug)] +pub struct PhysAddrNotValid(u64); + +impl PhysAddr { + /// Creates a new physical address. + /// + /// Panics if a bit in the range 52 to 64 is set. + pub fn new(addr: u64) -> PhysAddr { + assert_eq!( + addr.get_bits(52..64), + 0, + "physical addresses must not have any bits in the range 52 to 64 set" + ); + PhysAddr(addr) + } + + /// Tries to create a new physical address. + /// + /// Fails if any bits in the range 52 to 64 are set. + pub fn try_new(addr: u64) -> Result { + match addr.get_bits(52..64) { + 0 => Ok(PhysAddr(addr)), // address is valid + other => Err(PhysAddrNotValid(other)), + } + } + + /// Converts the address to an `u64`. + pub fn as_u64(self) -> u64 { + self.0 + } + + /// Convenience method for checking if a physical address is null. + pub fn is_null(&self) -> bool { + self.0 == 0 + } + + /// Aligns the physical address upwards to the given alignment. + /// + /// See the `align_up` function for more information. + pub fn align_up(self, align: U) -> Self + where + U: Into, + { + PhysAddr(align_up(self.0, align.into())) + } + + /// Aligns the physical address downwards to the given alignment. + /// + /// See the `align_down` function for more information. + pub fn align_down(self, align: U) -> Self + where + U: Into, + { + PhysAddr(align_down(self.0, align.into())) + } + + /// Checks whether the physical address has the demanded alignment. + pub fn is_aligned(self, align: U) -> bool + where + U: Into, + { + self.align_down(align) == self + } +} + +impl fmt::Debug for PhysAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "PhysAddr({:#x})", self.0) + } +} + +impl fmt::Binary for PhysAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl fmt::LowerHex for PhysAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl fmt::Octal for PhysAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl fmt::UpperHex for PhysAddr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Add for PhysAddr { + type Output = Self; + fn add(self, rhs: u64) -> Self::Output { + PhysAddr::new(self.0 + rhs) + } +} + +impl AddAssign for PhysAddr { + fn add_assign(&mut self, rhs: u64) { + *self = *self + rhs; + } +} + +impl Add for PhysAddr +where + u64: FromUsize, +{ + type Output = Self; + fn add(self, rhs: usize) -> Self::Output { + self + u64::from_usize(rhs) + } +} + +impl AddAssign for PhysAddr +where + u64: FromUsize, +{ + fn add_assign(&mut self, rhs: usize) { + self.add_assign(u64::from_usize(rhs)) + } +} + +impl Sub for PhysAddr { + type Output = Self; + fn sub(self, rhs: u64) -> Self::Output { + PhysAddr::new(self.0.checked_sub(rhs).unwrap()) + } +} + +impl SubAssign for PhysAddr { + fn sub_assign(&mut self, rhs: u64) { + *self = *self - rhs; + } +} + +impl Sub for PhysAddr +where + u64: FromUsize, +{ + type Output = Self; + fn sub(self, rhs: usize) -> Self::Output { + self - u64::from_usize(rhs) + } +} + +impl SubAssign for PhysAddr +where + u64: FromUsize, +{ + fn sub_assign(&mut self, rhs: usize) { + self.sub_assign(u64::from_usize(rhs)) + } +} + +impl Sub for PhysAddr { + type Output = u64; + fn sub(self, rhs: PhysAddr) -> Self::Output { + self.as_u64().checked_sub(rhs.as_u64()).unwrap() + } +} + +/// Align address downwards. +/// +/// Returns the greatest x with alignment `align` so that x <= addr. The alignment must be +/// a power of 2. +pub fn align_down(addr: u64, align: u64) -> u64 { + assert!(align.is_power_of_two(), "`align` must be a power of two"); + addr & !(align - 1) +} + +/// Align address upwards. +/// +/// Returns the smallest x with alignment `align` so that x >= addr. The alignment must be +/// a power of 2. +pub fn align_up(addr: u64, align: u64) -> u64 { + assert!(align.is_power_of_two(), "`align` must be a power of two"); + let align_mask = align - 1; + if addr & align_mask == 0 { + addr // already aligned + } else { + (addr | align_mask) + 1 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + pub fn test_align_up() { + // align 1 + assert_eq!(align_up(0, 1), 0); + assert_eq!(align_up(1234, 1), 1234); + assert_eq!(align_up(0xffffffffffffffff, 1), 0xffffffffffffffff); + // align 2 + assert_eq!(align_up(0, 2), 0); + assert_eq!(align_up(1233, 2), 1234); + assert_eq!(align_up(0xfffffffffffffffe, 2), 0xfffffffffffffffe); + // address 0 + assert_eq!(align_up(0, 128), 0); + assert_eq!(align_up(0, 1), 0); + assert_eq!(align_up(0, 2), 0); + assert_eq!(align_up(0, 0x8000000000000000), 0); + } +} diff --git a/crate/aarch64/src/asm.rs b/crate/aarch64/src/asm.rs new file mode 100644 index 0000000..d97fa78 --- /dev/null +++ b/crate/aarch64/src/asm.rs @@ -0,0 +1,162 @@ +use paging::PhysFrame; +use addr::PhysAddr; +use regs::*; + +#[inline(always)] +pub fn tlb_invalidate() { + unsafe{ + asm!("dsb ishst + tlbi vmalle1is + dsb ish + tlbi vmalle1is + isb"); + } +} + +/// Returns the current stack pointer. +#[inline(always)] +pub fn sp() -> *const u8 { + let ptr: usize; + unsafe { + asm!("mov $0, sp" : "=r"(ptr)); + } + + ptr as *const u8 +} + +#[inline(always)] +pub unsafe fn get_pc() -> usize { + let pc: usize; + asm!("ADR $0, ." : "=r"(pc)); + pc +} + +/// Returns the current exception level. +/// +/// # Safety +/// This function should only be called when EL is >= 1. +#[inline(always)] +pub unsafe fn current_el() -> u8 { + let el_reg: u64; + asm!("mrs $0, CurrentEL" : "=r"(el_reg)); + ((el_reg & 0b1100) >> 2) as u8 +} + +#[inline(always)] +pub unsafe fn get_far() -> usize { + let far: usize; + asm!("mrs $0, far_el1" : "=r"(far)); + far +} + +#[inline(always)] +pub unsafe fn get_ttbr0() -> usize { + let ttbr0: usize; + asm!("mrs $0, ttbr0_el1" : "=r"(ttbr0)); + ttbr0 +} + +#[inline(always)] +pub unsafe fn get_ttbr1() -> usize { + let ttbr0: usize; + asm!("mrs $0, ttbr1_el1" : "=r"(ttbr0)); + ttbr0 +} + +/// Returns the SPSel value. +#[inline(always)] +pub fn sp_sel() -> u8 { + let ptr: u32; + unsafe { + asm!("mrs $0, SPSel" : "=r"(ptr)); + } + + (ptr & 1) as u8 +} + +/// Returns the core currently executing. +/// +/// # Safety +/// +/// This function should only be called when EL is >= 1. +pub unsafe fn affinity() -> usize { + let x: usize; + asm!("mrs $0, mpidr_el1 + and $0, $0, #3" + : "=r"(x)); + + x +} + +pub fn wfi() { + unsafe { + asm!("wfi" :::: "volatile"); + } +} + + +/// The classic no-op +#[inline] +pub fn nop() { + match () { + #[cfg(target_arch = "aarch64")] + () => unsafe { asm!("nop" :::: "volatile") }, + + #[cfg(not(target_arch = "aarch64"))] + () => unimplemented!(), + } +} + +/// Wait For Event +#[inline] +pub fn wfe() { + match () { + #[cfg(target_arch = "aarch64")] + () => unsafe { asm!("wfe" :::: "volatile") }, + + #[cfg(not(target_arch = "aarch64"))] + () => unimplemented!(), + } +} + +/// Exception return +/// +/// Will jump to wherever the corresponding link register points to, and +/// therefore never return. +#[inline] +pub fn eret() -> ! { + use core; + + match () { + #[cfg(target_arch = "aarch64")] + () => unsafe { + asm!("eret" :::: "volatile"); + core::intrinsics::unreachable() + }, + + #[cfg(not(target_arch = "aarch64"))] + () => unimplemented!(), + } +} + +bitflags! { + /// Controls cache settings for the level 4 page table. + pub struct ttbr0_el1_Flags: u64 { + + const COMMON_NOT_PRIVATE = 1 << 0; + } +} + +pub fn ttbr0_el1_read() -> (PhysFrame, ttbr0_el1_Flags) { + let value = TTBR0_EL1.get(); + let flags = ttbr0_el1_Flags::from_bits_truncate(value); + let addr = PhysAddr::new(value & 0x_000f_ffff_ffff_f000); + let frame = PhysFrame::containing_address(addr); + (frame, flags) +} + +pub fn ttbr0_el1_write(frame: PhysFrame) { + let addr = frame.start_address(); + let value = addr.as_u64(); + TTBR0_EL1.set_baddr(value); +} diff --git a/crate/aarch64/src/barrier.rs b/crate/aarch64/src/barrier.rs new file mode 100644 index 0000000..0e48209 --- /dev/null +++ b/crate/aarch64/src/barrier.rs @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +// Borrow implementations from the pending upstream ACLE implementation until it +// is merged. Afterwards, we'll probably just reexport them, hoping that the API +// doesn't change. +// +// https://github.com/rust-lang-nursery/stdsimd/pull/557 + +mod sealed { + pub trait Dmb { + unsafe fn __dmb(&self); + } + + pub trait Dsb { + unsafe fn __dsb(&self); + } + + pub trait Isb { + unsafe fn __isb(&self); + } +} + +macro_rules! dmb_dsb { + ($A:ident) => { + impl sealed::Dmb for $A { + #[inline(always)] + unsafe fn __dmb(&self) { + asm!(concat!("DMB ", stringify!($A)) : : : "memory" : "volatile") + } + } + impl sealed::Dsb for $A { + #[inline(always)] + unsafe fn __dsb(&self) { + asm!(concat!("DSB ", stringify!($A)) : : : "memory" : "volatile") + } + } + }; +} + +pub struct SY; + +dmb_dsb!(SY); + +impl sealed::Isb for SY { + #[inline(always)] + unsafe fn __isb(&self) { + asm!("ISB SY" : : : "memory" : "volatile") + } +} + +#[inline(always)] +pub unsafe fn dmb(arg: A) +where + A: sealed::Dmb, +{ + arg.__dmb() +} + +#[inline(always)] +pub unsafe fn dsb(arg: A) +where + A: sealed::Dsb, +{ + arg.__dsb() +} + +#[inline(always)] +pub unsafe fn isb(arg: A) +where + A: sealed::Isb, +{ + arg.__isb() +} diff --git a/crate/aarch64/src/lib.rs b/crate/aarch64/src/lib.rs new file mode 100644 index 0000000..bb8bf72 --- /dev/null +++ b/crate/aarch64/src/lib.rs @@ -0,0 +1,29 @@ +#![no_std] +//#![deny(warnings)] +#![feature(asm)] +#![feature(const_fn)] +#![feature(core_intrinsics)] +#![feature(try_from)] + +extern crate bare_metal; +#[macro_use] +extern crate register; +#[macro_use] +extern crate bitflags; +extern crate bit_field; +extern crate os_bootinfo; +extern crate usize_conversions; + +/// Provides the non-standard-width integer types `u2`–`u63`. +/// +/// We use these integer types in various APIs, for example `u9` for page tables indices. +pub extern crate ux; + +pub use addr::{align_down, align_up, PhysAddr, VirtAddr}; + +pub mod asm; +pub mod addr; +pub mod paging; +pub mod barrier; +pub mod regs; + diff --git a/crate/aarch64/src/paging/frame_alloc.rs b/crate/aarch64/src/paging/frame_alloc.rs new file mode 100644 index 0000000..f3ea4c8 --- /dev/null +++ b/crate/aarch64/src/paging/frame_alloc.rs @@ -0,0 +1,15 @@ +//! Traits for abstracting away frame allocation and deallocation. + +use paging::{PageSize, PhysFrame}; + +/// A trait for types that can allocate a frame of memory. +pub trait FrameAllocator { + /// Allocate a frame of the appropriate size and return it if possible. + fn alloc(&mut self) -> Option>; +} + +/// A trait for types that can deallocate a frame of memory. +pub trait FrameDeallocator { + /// Deallocate the given frame of memory. + fn dealloc(&mut self, frame: PhysFrame); +} diff --git a/crate/aarch64/src/paging/mod.rs b/crate/aarch64/src/paging/mod.rs new file mode 100644 index 0000000..56073b2 --- /dev/null +++ b/crate/aarch64/src/paging/mod.rs @@ -0,0 +1,528 @@ +//! Abstractions for page tables and other paging related structures. +//! +//! Page tables translate virtual memory “pages” to physical memory “frames”. + +pub use self::frame_alloc::*; +pub use self::page_table::*; +#[cfg(target_arch = "aarch64")] +pub use self::recursive::*; + +use core::fmt; +use core::marker::PhantomData; +use core::ops::{Add, AddAssign, Sub, SubAssign}; +use os_bootinfo; +use ux::*; +use addr::{PhysAddr, VirtAddr}; + +mod frame_alloc; +mod page_table; +mod recursive; + +/// Trait for abstracting over the three possible page sizes on x86_64, 4KiB, 2MiB, 1GiB. +pub trait PageSize: Copy + Eq + PartialOrd + Ord { + /// The page size in bytes. + const SIZE: u64; + + /// A string representation of the page size for debug output. + const SIZE_AS_DEBUG_STR: &'static str; +} + +/// This trait is implemented for 4KiB and 2MiB pages, but not for 1GiB pages. +pub trait NotGiantPageSize: PageSize {} + +/// A standard 4KiB page. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum Size4KiB {} + +/// A “huge” 2MiB page. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum Size2MiB {} + +/// A “giant” 1GiB page. +/// +/// (Only available on newer x86_64 CPUs.) +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum Size1GiB {} + +impl PageSize for Size4KiB { + const SIZE: u64 = 4096; + const SIZE_AS_DEBUG_STR: &'static str = "4KiB"; +} + +impl NotGiantPageSize for Size4KiB {} + +impl PageSize for Size2MiB { + const SIZE: u64 = Size4KiB::SIZE * 512; + const SIZE_AS_DEBUG_STR: &'static str = "2MiB"; +} + +impl NotGiantPageSize for Size2MiB {} + +impl PageSize for Size1GiB { + const SIZE: u64 = Size2MiB::SIZE * 512; + const SIZE_AS_DEBUG_STR: &'static str = "1GiB"; +} + +/// A virtual memory page. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[repr(C)] +pub struct Page { + start_address: VirtAddr, + size: PhantomData, +} + +impl Page { + /// Returns the page that starts at the given virtual address. + /// + /// Returns an error if the address is not correctly aligned (i.e. is not a valid page start). + pub fn from_start_address(address: VirtAddr) -> Result { + if !address.is_aligned(S::SIZE) { + return Err(()); + } + Ok(Page::containing_address(address)) + } + + /// Returns the page that contains the given virtual address. + pub fn containing_address(address: VirtAddr) -> Self { + Page { + start_address: address.align_down(S::SIZE), + size: PhantomData, + } + } + + /// Returns the start address of the page. + pub fn start_address(&self) -> VirtAddr { + self.start_address + } + + /// Returns the size the page (4KB, 2MB or 1GB). + pub const fn size(&self) -> u64 { + S::SIZE + } + + /// Returns the level 4 page table index of this page. + pub fn p4_index(&self) -> u9 { + self.start_address().p4_index() + } + + /// Returns the level 3 page table index of this page. + pub fn p3_index(&self) -> u9 { + self.start_address().p3_index() + } + + /// Returns a range of pages, exclusive `end`. + pub fn range(start: Self, end: Self) -> PageRange { + PageRange { start, end } + } + + /// Returns a range of pages, inclusive `end`. + pub fn range_inclusive(start: Self, end: Self) -> PageRangeInclusive { + PageRangeInclusive { start, end } + } +} + +impl Page { + /// Returns the level 2 page table index of this page. + pub fn p2_index(&self) -> u9 { + self.start_address().p2_index() + } +} + +impl Page { + /// Returns the 1GiB memory page with the specified page table indices. + pub fn from_page_table_indices_1gib(p4_index: u9, p3_index: u9) -> Self { + use bit_field::BitField; + + let mut addr = 0; + addr.set_bits(39..48, u64::from(p4_index)); + addr.set_bits(30..39, u64::from(p3_index)); + Page::containing_address(VirtAddr::new(addr)) + } +} + +impl Page { + /// Returns the 2MiB memory page with the specified page table indices. + pub fn from_page_table_indices_2mib(p4_index: u9, p3_index: u9, p2_index: u9) -> Self { + use bit_field::BitField; + + let mut addr = 0; + addr.set_bits(39..48, u64::from(p4_index)); + addr.set_bits(30..39, u64::from(p3_index)); + addr.set_bits(21..30, u64::from(p2_index)); + Page::containing_address(VirtAddr::new(addr)) + } +} + +impl Page { + /// Returns the 4KiB memory page with the specified page table indices. + pub fn from_page_table_indices(p4_index: u9, p3_index: u9, p2_index: u9, p1_index: u9) -> Self { + use bit_field::BitField; + + let mut addr = 0; + addr.set_bits(39..48, u64::from(p4_index)); + addr.set_bits(30..39, u64::from(p3_index)); + addr.set_bits(21..30, u64::from(p2_index)); + addr.set_bits(12..21, u64::from(p1_index)); + Page::containing_address(VirtAddr::new(addr)) + } + + /// Returns the level 1 page table index of this page. + pub fn p1_index(&self) -> u9 { + self.start_address().p1_index() + } +} + +impl fmt::Debug for Page { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_fmt(format_args!( + "Page[{}]({:#x})", + S::SIZE_AS_DEBUG_STR, + self.start_address().as_u64() + )) + } +} + +impl Add for Page { + type Output = Self; + fn add(self, rhs: u64) -> Self::Output { + Page::containing_address(self.start_address() + rhs * u64::from(S::SIZE)) + } +} + +impl AddAssign for Page { + fn add_assign(&mut self, rhs: u64) { + *self = self.clone() + rhs; + } +} + +impl Sub for Page { + type Output = Self; + fn sub(self, rhs: u64) -> Self::Output { + Page::containing_address(self.start_address() - rhs * u64::from(S::SIZE)) + } +} + +impl SubAssign for Page { + fn sub_assign(&mut self, rhs: u64) { + *self = self.clone() - rhs; + } +} + +impl Sub for Page { + type Output = u64; + fn sub(self, rhs: Self) -> Self::Output { + (self.start_address - rhs.start_address) / S::SIZE + } +} + +/// A range of pages with exclusive upper bound. +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(C)] +pub struct PageRange { + /// The start of the range, inclusive. + pub start: Page, + /// The end of the range, exclusive. + pub end: Page, +} + +impl PageRange { + /// Returns wether this range contains no pages. + pub fn is_empty(&self) -> bool { + !(self.start < self.end) + } +} + +impl Iterator for PageRange { + type Item = Page; + + fn next(&mut self) -> Option { + if self.start < self.end { + let page = self.start.clone(); + self.start += 1; + Some(page) + } else { + None + } + } +} + +impl PageRange { + /// Converts the range of 2MiB pages to a range of 4KiB pages. + pub fn as_4kib_page_range(self) -> PageRange { + PageRange { + start: Page::containing_address(self.start.start_address()), + end: Page::containing_address(self.end.start_address()), + } + } +} + +impl fmt::Debug for PageRange { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("PageRange") + .field("start", &self.start) + .field("end", &self.end) + .finish() + } +} + +/// A range of pages with inclusive upper bound. +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(C)] +pub struct PageRangeInclusive { + /// The start of the range, inclusive. + pub start: Page, + /// The end of the range, inclusive. + pub end: Page, +} + +impl PageRangeInclusive { + /// Returns wether this range contains no pages. + pub fn is_empty(&self) -> bool { + !(self.start <= self.end) + } +} + +impl Iterator for PageRangeInclusive { + type Item = Page; + + fn next(&mut self) -> Option { + if self.start <= self.end { + let page = self.start.clone(); + self.start += 1; + Some(page) + } else { + None + } + } +} + +impl fmt::Debug for PageRangeInclusive { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("PageRangeInclusive") + .field("start", &self.start) + .field("end", &self.end) + .finish() + } +} + +/// A physical memory frame. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[repr(C)] +pub struct PhysFrame { + start_address: PhysAddr, + size: PhantomData, +} + +impl PhysFrame { + /// Returns the frame that starts at the given virtual address. + /// + /// Returns an error if the address is not correctly aligned (i.e. is not a valid frame start). + pub fn from_start_address(address: PhysAddr) -> Result { + if !address.is_aligned(S::SIZE) { + return Err(()); + } + Ok(PhysFrame::containing_address(address)) + } + + /// Returns the frame that contains the given physical address. + pub fn containing_address(address: PhysAddr) -> Self { + PhysFrame { + start_address: address.align_down(S::SIZE), + size: PhantomData, + } + } + + /// Returns the start address of the frame. + pub fn start_address(&self) -> PhysAddr { + self.start_address + } + + /// Returns the size the frame (4KB, 2MB or 1GB). + pub fn size(&self) -> u64 { + S::SIZE + } + + /// Returns a range of frames, exclusive `end`. + pub fn range(start: PhysFrame, end: PhysFrame) -> PhysFrameRange { + PhysFrameRange { start, end } + } + + /// Returns a range of frames, inclusive `end`. + pub fn range_inclusive(start: PhysFrame, end: PhysFrame) -> PhysFrameRangeInclusive { + PhysFrameRangeInclusive { start, end } + } +} + +impl fmt::Debug for PhysFrame { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_fmt(format_args!( + "PhysFrame[{}]({:#x})", + S::SIZE_AS_DEBUG_STR, + self.start_address().as_u64() + )) + } +} + +impl Add for PhysFrame { + type Output = Self; + fn add(self, rhs: u64) -> Self::Output { + PhysFrame::containing_address(self.start_address() + rhs * u64::from(S::SIZE)) + } +} + +impl AddAssign for PhysFrame { + fn add_assign(&mut self, rhs: u64) { + *self = self.clone() + rhs; + } +} + +impl Sub for PhysFrame { + type Output = Self; + fn sub(self, rhs: u64) -> Self::Output { + PhysFrame::containing_address(self.start_address() - rhs * u64::from(S::SIZE)) + } +} + +impl SubAssign for PhysFrame { + fn sub_assign(&mut self, rhs: u64) { + *self = self.clone() - rhs; + } +} + +impl Sub> for PhysFrame { + type Output = u64; + fn sub(self, rhs: PhysFrame) -> Self::Output { + (self.start_address - rhs.start_address) / S::SIZE + } +} + +/// An range of physical memory frames, exclusive the upper bound. +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(C)] +pub struct PhysFrameRange { + /// The start of the range, inclusive. + pub start: PhysFrame, + /// The end of the range, exclusive. + pub end: PhysFrame, +} + +impl PhysFrameRange { + /// Returns whether the range contains no frames. + pub fn is_empty(&self) -> bool { + !(self.start < self.end) + } +} + +impl Iterator for PhysFrameRange { + type Item = PhysFrame; + + fn next(&mut self) -> Option { + if self.start < self.end { + let frame = self.start.clone(); + self.start += 1; + Some(frame) + } else { + None + } + } +} + +impl From for PhysFrameRange { + fn from(range: os_bootinfo::FrameRange) -> Self { + PhysFrameRange { + start: PhysFrame::from_start_address(PhysAddr::new(range.start_addr())).unwrap(), + end: PhysFrame::from_start_address(PhysAddr::new(range.end_addr())).unwrap(), + } + } +} + +impl Into for PhysFrameRange { + fn into(self) -> os_bootinfo::FrameRange { + os_bootinfo::FrameRange::new( + self.start.start_address().as_u64(), + self.end.start_address().as_u64(), + ) + } +} + +impl fmt::Debug for PhysFrameRange { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("PhysFrameRange") + .field("start", &self.start) + .field("end", &self.end) + .finish() + } +} + +/// An range of physical memory frames, inclusive the upper bound. +#[derive(Clone, Copy, PartialEq, Eq)] +#[repr(C)] +pub struct PhysFrameRangeInclusive { + /// The start of the range, inclusive. + pub start: PhysFrame, + /// The start of the range, exclusive. + pub end: PhysFrame, +} + +impl PhysFrameRangeInclusive { + /// Returns whether the range contains no frames. + pub fn is_empty(&self) -> bool { + !(self.start <= self.end) + } +} + +impl Iterator for PhysFrameRangeInclusive { + type Item = PhysFrame; + + fn next(&mut self) -> Option { + if self.start <= self.end { + let frame = self.start.clone(); + self.start += 1; + Some(frame) + } else { + None + } + } +} + +impl fmt::Debug for PhysFrameRangeInclusive { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("PhysFrameRangeInclusive") + .field("start", &self.start) + .field("end", &self.end) + .finish() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + pub fn test_page_ranges() { + let page_size = Size4KiB::SIZE; + let number = 1000; + + let start_addr = VirtAddr::new(0xdeadbeaf); + let start: Page = Page::containing_address(start_addr); + let end = start.clone() + number; + + let mut range = Page::range(start.clone(), end.clone()); + for i in 0..number { + assert_eq!( + range.next(), + Some(Page::containing_address(start_addr + page_size * i)) + ); + } + assert_eq!(range.next(), None); + + let mut range_inclusive = Page::range_inclusive(start, end); + for i in 0..=number { + assert_eq!( + range_inclusive.next(), + Some(Page::containing_address(start_addr + page_size * i)) + ); + } + assert_eq!(range_inclusive.next(), None); + } +} diff --git a/crate/aarch64/src/paging/page_table.rs b/crate/aarch64/src/paging/page_table.rs new file mode 100644 index 0000000..b074768 --- /dev/null +++ b/crate/aarch64/src/paging/page_table.rs @@ -0,0 +1,185 @@ +use core::fmt; +use core::ops::{Index, IndexMut}; + +use super::{PageSize, PhysFrame, Size4KiB}; +use addr::PhysAddr; + +use usize_conversions::usize_from; +use ux::*; + +/// The error returned by the `PageTableEntry::frame` method. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum FrameError { + /// The entry does not have the `PRESENT` flag set, so it isn't currently mapped to a frame. + FrameNotPresent, + /// The entry does have the `HUGE_PAGE` flag set. The `frame` method has a standard 4KiB frame + /// as return type, so a huge frame can't be returned. + HugeFrame, +} + +/// A 64-bit page table entry. +#[derive(Clone)] +#[repr(transparent)] +pub struct PageTableEntry { + entry: u64, +} + +impl PageTableEntry { + /// Returns whether this entry is zero. + pub fn is_unused(&self) -> bool { + self.entry == 0 + } + + /// Sets this entry to zero. + pub fn set_unused(&mut self) { + self.entry = 0; + } + + /// Returns the flags of this entry. + pub fn flags(&self) -> PageTableFlags { + PageTableFlags::from_bits_truncate(self.entry) + } + + /// Returns the physical address mapped by this entry, might be zero. + pub fn addr(&self) -> PhysAddr { + PhysAddr::new(self.entry & 0x000fffff_fffff000) + } + + /// Returns the physical frame mapped by this entry. + /// + /// Returns the following errors: + /// + /// - `FrameError::FrameNotPresent` if the entry doesn't have the `PRESENT` flag set. + /// - `FrameError::HugeFrame` if the entry has the `HUGE_PAGE` flag set (for huge pages the + /// `addr` function must be used) + pub fn frame(&self) -> Result { + if !self.flags().contains(PageTableFlags::PRESENT) { + Err(FrameError::FrameNotPresent) + } else if self.flags().contains(PageTableFlags::HUGE_PAGE) { + Err(FrameError::HugeFrame) + } else { + Ok(PhysFrame::containing_address(self.addr())) + } + } + + /// Map the entry to the specified physical address with the specified flags. + pub fn set_addr(&mut self, addr: PhysAddr, flags: PageTableFlags) { + assert!(addr.is_aligned(Size4KiB::SIZE)); + self.entry = (addr.as_u64()) | flags.bits(); + } + + /// Map the entry to the specified physical frame with the specified flags. + pub fn set_frame(&mut self, frame: PhysFrame, flags: PageTableFlags) { + assert!(!flags.contains(PageTableFlags::HUGE_PAGE)); + self.set_addr(frame.start_address(), flags) + } + + /// Sets the flags of this entry. + pub fn set_flags(&mut self, flags: PageTableFlags) { + self.entry = self.addr().as_u64() | flags.bits(); + } +} + +impl fmt::Debug for PageTableEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut f = f.debug_struct("PageTableEntry"); + f.field("addr", &self.addr()); + f.field("flags", &self.flags()); + f.finish() + } +} + +bitflags! { + /// Possible flags for a page table entry. + pub struct PageTableFlags: u64 { + const ALL = 0xffffffff_ffffffff; + const TYPE_MASK = 3 << 0; + const TYPE_FAULT = 0 << 0; + const TYPE_PAGE = 3 << 0; + const TABLE_BIT = 1 << 1; + + const PRESENT = 1 << 0; + const USER_ACCESSIBLE = 1 << 6; /* AP[1] */ + const RDONLY = 1 << 7; /* AP[2] */ + const SHARED = 3 << 8; /* SH[1:0], inner shareable */ + const BIT_8 = 1 << 8; + const BIT_9 = 1 << 9; + /* + pub const ATTRIB_SH_NON_SHAREABLE: usize = 0x0 << 8; + pub const ATTRIB_SH_OUTER_SHAREABLE: usize = 0x2 << 8; + pub const ATTRIB_SH_INNER_SHAREABLE: usize = 0x3 << 8; + */ + + const ACCESSED = 1 << 10; /* AF, Access Flag */ + const NONE_GLOBAL = 1 << 11; /* None Global */ + const GLOBAL = (!(1 << 11)); + const DBM = 1 << 51; /* Dirty Bit Management */ + const WRITE = 1 << 51; /* DBM */ + const CONT = 1 << 52; /* Contiguous range */ + const PXN = 1 << 53; /* Privileged XN */ + const UXN = 1 << 54; /* User XN */ + const HYP_XN = 1 << 54; /* HYP XN */ + const DIRTY = 1 << 55; + const SWAPPED = 1 << 56; + const HUGE_PAGE = 1 << 57; + const PROT_NONE = 1 << 58; + + } +} + +/// The number of entries in a page table. +const ENTRY_COUNT: usize = 512; + +/// Represents a page table. +/// +/// Always page-sized. +/// +/// This struct implements the `Index` and `IndexMut` traits, so the entries can be accessed +/// through index operations. For example, `page_table[15]` returns the 15th page table entry. +#[repr(transparent)] +pub struct PageTable { + entries: [PageTableEntry; ENTRY_COUNT], +} + +impl PageTable { + /// Clears all entries. + pub fn zero(&mut self) { + for entry in self.entries.iter_mut() { + entry.set_unused(); + } + } +} + +impl Index for PageTable { + type Output = PageTableEntry; + + fn index(&self, index: usize) -> &Self::Output { + &self.entries[index] + } +} + +impl IndexMut for PageTable { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.entries[index] + } +} + +impl Index for PageTable { + type Output = PageTableEntry; + + fn index(&self, index: u9) -> &Self::Output { + &self.entries[usize_from(u16::from(index))] + } +} + +impl IndexMut for PageTable { + fn index_mut(&mut self, index: u9) -> &mut Self::Output { + &mut self.entries[usize_from(u16::from(index))] + } +} + +impl fmt::Debug for PageTable { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.entries[..].fmt(f) + } +} diff --git a/crate/aarch64/src/paging/recursive.rs b/crate/aarch64/src/paging/recursive.rs new file mode 100644 index 0000000..0cbc6d0 --- /dev/null +++ b/crate/aarch64/src/paging/recursive.rs @@ -0,0 +1,623 @@ +#![cfg(target_arch = "aarch64")] + +use asm::tlb_invalidate; +use paging::{ + frame_alloc::FrameAllocator, + page_table::{FrameError, PageTable, PageTableEntry, PageTableFlags}, + NotGiantPageSize, Page, PageSize, PhysFrame, Size1GiB, Size2MiB, Size4KiB, +}; +use paging::page_table::PageTableFlags as Flags; +use asm::ttbr0_el1_read; +use ux::u9; +use addr::{PhysAddr, VirtAddr}; + +/// This type represents a page whose mapping has changed in the page table. +/// +/// The old mapping might be still cached in the translation lookaside buffer (TLB), so it needs +/// to be flushed from the TLB before it's accessed. This type is returned from function that +/// change the mapping of a page to ensure that the TLB flush is not forgotten. +#[derive(Debug)] +#[must_use = "Page Table changes must be flushed or ignored."] +pub struct MapperFlush(Page); + +impl MapperFlush { + /// Create a new flush promise + fn new(page: Page) -> Self { + MapperFlush(page) + } + + /// Flush the page from the TLB to ensure that the newest mapping is used. + pub fn flush(self) { + tlb_invalidate(); + } + + /// Don't flush the TLB and silence the “must be used” warning. + pub fn ignore(self) {} +} + +/// A trait for common page table operations. +pub trait Mapper { + /// Creates a new mapping in the page table. + /// + /// This function might need additional physical frames to create new page tables. These + /// frames are allocated from the `allocator` argument. At most three frames are required. + fn map_to( + &mut self, + page: Page, + frame: PhysFrame, + flags: PageTableFlags, + allocator: &mut A, + ) -> Result, MapToError> + where + A: FrameAllocator; + + /// Removes a mapping from the page table and returns the frame that used to be mapped. + /// + /// Note that no page tables or pages are deallocated. + fn unmap(&mut self, page: Page) -> Result<(PhysFrame, MapperFlush), UnmapError>; + + /// Updates the flags of an existing mapping. + fn update_flags( + &mut self, + page: Page, + flags: PageTableFlags, + ) -> Result, FlagUpdateError>; + + /// Return the frame that the specified page is mapped to. + fn translate_page(&self, page: Page) -> Option>; + + /// Maps the given frame to the virtual page with the same address. + fn identity_map( + &mut self, + frame: PhysFrame, + flags: PageTableFlags, + allocator: &mut A, + ) -> Result, MapToError> + where + A: FrameAllocator, + S: PageSize, + Self: Mapper, + { + let page = Page::containing_address(VirtAddr::new(frame.start_address().as_u64())); + self.map_to(page, frame, flags, allocator) + } +} + +/// A recursive page table is a last level page table with an entry mapped to the table itself. +/// +/// This recursive mapping allows accessing all page tables in the hierarchy: +/// +/// - To access the level 4 page table, we “loop“ (i.e. follow the recursively mapped entry) four +/// times. +/// - To access a level 3 page table, we “loop” three times and then use the level 4 index. +/// - To access a level 2 page table, we “loop” two times, then use the level 4 index, then the +/// level 3 index. +/// - To access a level 1 page table, we “loop” once, then use the level 4 index, then the +/// level 3 index, then the level 2 index. +/// +/// This struct implements the `Mapper` trait. +#[derive(Debug)] +pub struct RecursivePageTable<'a> { + p4: &'a mut PageTable, + recursive_index: u9, +} + +/// An error indicating that the given page table is not recursively mapped. +/// +/// Returned from `RecursivePageTable::new`. +#[derive(Debug)] +pub struct NotRecursivelyMapped; + +/// This error is returned from `map_to` and similar methods. +#[derive(Debug)] +pub enum MapToError { + /// An additional frame was needed for the mapping process, but the frame allocator + /// returned `None`. + FrameAllocationFailed, + /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the + /// given page is part of an already mapped huge page. + ParentEntryHugePage, + /// The given page is already mapped to a physical frame. + PageAlreadyMapped, +} + +/// An error indicating that an `unmap` call failed. +#[derive(Debug)] +pub enum UnmapError { + /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the + /// given page is part of a huge page and can't be freed individually. + ParentEntryHugePage, + /// The given page is not mapped to a physical frame. + PageNotMapped, + /// The page table entry for the given page points to an invalid physical address. + InvalidFrameAddress(PhysAddr), +} + +/// An error indicating that an `update_flags` call failed. +#[derive(Debug)] +pub enum FlagUpdateError { + /// The given page is not mapped to a physical frame. + PageNotMapped, +} + +impl<'a> RecursivePageTable<'a> { + /// Creates a new RecursivePageTable from the passed level 4 PageTable. + /// + /// The page table must be recursively mapped, that means: + /// + /// - The page table must have one recursive entry, i.e. an entry that points to the table + /// itself. + /// - The reference must use that “loop”, i.e. be of the form `0o_xxx_xxx_xxx_xxx_0000` + /// where `xxx` is the recursive entry. + /// - The page table must be active, i.e. the CR3 register must contain its physical address. + /// + /// Otherwise `Err(NotRecursivelyMapped)` is returned. + pub fn new(table: &'a mut PageTable) -> Result { + let page = Page::containing_address(VirtAddr::new(table as *const _ as u64)); + let recursive_index = page.p4_index(); + + if page.p3_index() != recursive_index + || page.p2_index() != recursive_index + || page.p1_index() != recursive_index + { + return Err(NotRecursivelyMapped); + } + if Ok(ttbr0_el1_read().0) != table[recursive_index].frame() { + return Err(NotRecursivelyMapped); + } + + Ok(RecursivePageTable { + p4: table, + recursive_index, + }) + } + + /// Creates a new RecursivePageTable without performing any checks. + /// + /// The `recursive_index` parameter must be the index of the recursively mapped entry. + pub unsafe fn new_unchecked(table: &'a mut PageTable, recursive_index: u9) -> Self { + RecursivePageTable { + p4: table, + recursive_index, + } + } + + /// Internal helper function to create the page table of the next level if needed. + /// + /// If the passed entry is unused, a new frame is allocated from the given allocator, zeroed, + /// and the entry is updated to that address. If the passed entry is already mapped, the next + /// table is returned directly. + /// + /// The `next_page_table` page must be the page of the next page table in the hierarchy. + /// + /// Returns `MapToError::FrameAllocationFailed` if the entry is unused and the allocator + /// returned `None`. Returns `MapToError::ParentEntryHugePage` if the `HUGE_PAGE` flag is set + /// in the passed entry. + unsafe fn create_next_table<'b, A>( + entry: &'b mut PageTableEntry, + next_table_page: Page, + allocator: &mut A, + ) -> Result<&'b mut PageTable, MapToError> + where + A: FrameAllocator, + { + /// This inner function is used to limit the scope of `unsafe`. + /// + /// This is a safe function, so we need to use `unsafe` blocks when we do something unsafe. + fn inner<'b, A>( + entry: &'b mut PageTableEntry, + next_table_page: Page, + allocator: &mut A, + ) -> Result<&'b mut PageTable, MapToError> + where + A: FrameAllocator, + { + + let created; + + if entry.is_unused() { + if let Some(frame) = allocator.alloc() { + entry.set_frame(frame, Flags::PRESENT | Flags::WRITE); + created = true; + } else { + return Err(MapToError::FrameAllocationFailed); + } + } else { + created = false; + } + if entry.flags().contains(Flags::HUGE_PAGE) { + return Err(MapToError::ParentEntryHugePage); + } + + let page_table_ptr = next_table_page.start_address().as_mut_ptr(); + let page_table: &mut PageTable = unsafe { &mut *(page_table_ptr) }; + if created { + page_table.zero(); + } + Ok(page_table) + } + + inner(entry, next_table_page, allocator) + } +} + +impl<'a> Mapper for RecursivePageTable<'a> { + fn map_to( + &mut self, + page: Page, + frame: PhysFrame, + flags: PageTableFlags, + allocator: &mut A, + ) -> Result, MapToError> + where + A: FrameAllocator, + { + let p4 = &mut self.p4; + + let p3_page = p3_page(page, self.recursive_index); + let p3 = unsafe { Self::create_next_table(&mut p4[page.p4_index()], p3_page, allocator)? }; + + if !p3[page.p3_index()].is_unused() { + return Err(MapToError::PageAlreadyMapped); + } + p3[page.p3_index()].set_addr(frame.start_address(), flags | Flags::HUGE_PAGE); + + Ok(MapperFlush::new(page)) + } + + fn unmap( + &mut self, + page: Page, + ) -> Result<(PhysFrame, MapperFlush), UnmapError> { + let p4 = &mut self.p4; + let p4_entry = &p4[page.p4_index()]; + + p4_entry.frame().map_err(|err| match err { + FrameError::FrameNotPresent => UnmapError::PageNotMapped, + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, + })?; + + let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; + let p3_entry = &mut p3[page.p3_index()]; + let flags = p3_entry.flags(); + + if !flags.contains(PageTableFlags::PRESENT) { + return Err(UnmapError::PageNotMapped); + } + if !flags.contains(PageTableFlags::HUGE_PAGE) { + return Err(UnmapError::ParentEntryHugePage); + } + + let frame = PhysFrame::from_start_address(p3_entry.addr()) + .map_err(|()| UnmapError::InvalidFrameAddress(p3_entry.addr()))?; + + p3_entry.set_unused(); + Ok((frame, MapperFlush::new(page))) + } + + fn update_flags( + &mut self, + page: Page, + flags: PageTableFlags, + ) -> Result, FlagUpdateError> { + let p4 = &mut self.p4; + + if p4[page.p4_index()].is_unused() { + return Err(FlagUpdateError::PageNotMapped); + } + + let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; + + if p3[page.p3_index()].is_unused() { + return Err(FlagUpdateError::PageNotMapped); + } + p3[page.p3_index()].set_flags(flags | Flags::HUGE_PAGE); + + Ok(MapperFlush::new(page)) + } + + fn translate_page(&self, page: Page) -> Option> { + let p4 = &self.p4; + + if p4[page.p4_index()].is_unused() { + return None; + } + + let p3 = unsafe { &*(p3_ptr(page, self.recursive_index)) }; + let p3_entry = &p3[page.p3_index()]; + + if p3_entry.is_unused() { + return None; + } + + PhysFrame::from_start_address(p3_entry.addr()).ok() + } +} + +impl<'a> Mapper for RecursivePageTable<'a> { + fn map_to( + &mut self, + page: Page, + frame: PhysFrame, + flags: PageTableFlags, + allocator: &mut A, + ) -> Result, MapToError> + where + A: FrameAllocator, + { + let p4 = &mut self.p4; + + let p3_page = p3_page(page, self.recursive_index); + let p3 = unsafe { Self::create_next_table(&mut p4[page.p4_index()], p3_page, allocator)? }; + + let p2_page = p2_page(page, self.recursive_index); + let p2 = unsafe { Self::create_next_table(&mut p3[page.p3_index()], p2_page, allocator)? }; + + if !p2[page.p2_index()].is_unused() { + return Err(MapToError::PageAlreadyMapped); + } + p2[page.p2_index()].set_addr(frame.start_address(), flags | Flags::HUGE_PAGE); + + Ok(MapperFlush::new(page)) + } + + fn unmap( + &mut self, + page: Page, + ) -> Result<(PhysFrame, MapperFlush), UnmapError> { + let p4 = &mut self.p4; + let p4_entry = &p4[page.p4_index()]; + p4_entry.frame().map_err(|err| match err { + FrameError::FrameNotPresent => UnmapError::PageNotMapped, + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, + })?; + + let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; + let p3_entry = &p3[page.p3_index()]; + p3_entry.frame().map_err(|err| match err { + FrameError::FrameNotPresent => UnmapError::PageNotMapped, + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, + })?; + + let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; + let p2_entry = &mut p2[page.p2_index()]; + let flags = p2_entry.flags(); + + if !flags.contains(PageTableFlags::PRESENT) { + return Err(UnmapError::PageNotMapped); + } + if !flags.contains(PageTableFlags::HUGE_PAGE) { + return Err(UnmapError::ParentEntryHugePage); + } + + let frame = PhysFrame::from_start_address(p2_entry.addr()) + .map_err(|()| UnmapError::InvalidFrameAddress(p2_entry.addr()))?; + + p2_entry.set_unused(); + Ok((frame, MapperFlush::new(page))) + } + + fn update_flags( + &mut self, + page: Page, + flags: PageTableFlags, + ) -> Result, FlagUpdateError> { + let p4 = &mut self.p4; + + if p4[page.p4_index()].is_unused() { + return Err(FlagUpdateError::PageNotMapped); + } + + let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; + + if p3[page.p3_index()].is_unused() { + return Err(FlagUpdateError::PageNotMapped); + } + + let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; + + if p2[page.p2_index()].is_unused() { + return Err(FlagUpdateError::PageNotMapped); + } + + p2[page.p2_index()].set_flags(flags | Flags::HUGE_PAGE); + + Ok(MapperFlush::new(page)) + } + + fn translate_page(&self, page: Page) -> Option> { + let p4 = &self.p4; + + if p4[page.p4_index()].is_unused() { + return None; + } + + let p3 = unsafe { &*(p3_ptr(page, self.recursive_index)) }; + let p3_entry = &p3[page.p3_index()]; + + if p3_entry.is_unused() { + return None; + } + + let p2 = unsafe { &*(p2_ptr(page, self.recursive_index)) }; + let p2_entry = &p2[page.p2_index()]; + + if p2_entry.is_unused() { + return None; + } + + PhysFrame::from_start_address(p2_entry.addr()).ok() + } +} + +impl<'a> Mapper for RecursivePageTable<'a> { + fn map_to( + &mut self, + page: Page, + frame: PhysFrame, + flags: PageTableFlags, + allocator: &mut A, + ) -> Result, MapToError> + where + A: FrameAllocator, + { + let p4 = &mut self.p4; + + let p3_page = p3_page(page, self.recursive_index); + let p3 = unsafe { Self::create_next_table(&mut p4[page.p4_index()], p3_page, allocator)? }; + + let p2_page = p2_page(page, self.recursive_index); + let p2 = unsafe { Self::create_next_table(&mut p3[page.p3_index()], p2_page, allocator)? }; + + let p1_page = p1_page(page, self.recursive_index); + let p1 = unsafe { Self::create_next_table(&mut p2[page.p2_index()], p1_page, allocator)? }; + + if !p1[page.p1_index()].is_unused() { + return Err(MapToError::PageAlreadyMapped); + } + p1[page.p1_index()].set_frame(frame, flags); + + Ok(MapperFlush::new(page)) + } + + fn unmap( + &mut self, + page: Page, + ) -> Result<(PhysFrame, MapperFlush), UnmapError> { + let p4 = &mut self.p4; + let p4_entry = &p4[page.p4_index()]; + p4_entry.frame().map_err(|err| match err { + FrameError::FrameNotPresent => UnmapError::PageNotMapped, + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, + })?; + + let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; + let p3_entry = &p3[page.p3_index()]; + p3_entry.frame().map_err(|err| match err { + FrameError::FrameNotPresent => UnmapError::PageNotMapped, + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, + })?; + + let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; + let p2_entry = &p2[page.p2_index()]; + p2_entry.frame().map_err(|err| match err { + FrameError::FrameNotPresent => UnmapError::PageNotMapped, + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, + })?; + + let p1 = unsafe { &mut *(p1_ptr(page, self.recursive_index)) }; + let p1_entry = &mut p1[page.p1_index()]; + + let frame = p1_entry.frame().map_err(|err| match err { + FrameError::FrameNotPresent => UnmapError::PageNotMapped, + FrameError::HugeFrame => UnmapError::ParentEntryHugePage, + })?; + + p1_entry.set_unused(); + Ok((frame, MapperFlush::new(page))) + } + + fn update_flags( + &mut self, + page: Page, + flags: PageTableFlags, + ) -> Result, FlagUpdateError> { + let p4 = &mut self.p4; + + if p4[page.p4_index()].is_unused() { + return Err(FlagUpdateError::PageNotMapped); + } + + let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; + + if p3[page.p3_index()].is_unused() { + return Err(FlagUpdateError::PageNotMapped); + } + + let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; + + if p2[page.p2_index()].is_unused() { + return Err(FlagUpdateError::PageNotMapped); + } + + let p1 = unsafe { &mut *(p1_ptr(page, self.recursive_index)) }; + + if p1[page.p1_index()].is_unused() { + return Err(FlagUpdateError::PageNotMapped); + } + + p1[page.p1_index()].set_flags(flags); + + Ok(MapperFlush::new(page)) + } + + fn translate_page(&self, page: Page) -> Option> { + let p4 = &self.p4; + + if p4[page.p4_index()].is_unused() { + return None; + } + + let p3 = unsafe { &*(p3_ptr(page, self.recursive_index)) }; + let p3_entry = &p3[page.p3_index()]; + + if p3_entry.is_unused() { + return None; + } + + let p2 = unsafe { &*(p2_ptr(page, self.recursive_index)) }; + let p2_entry = &p2[page.p2_index()]; + + if p2_entry.is_unused() { + return None; + } + + let p1 = unsafe { &*(p1_ptr(page, self.recursive_index)) }; + let p1_entry = &p1[page.p1_index()]; + + if p1_entry.is_unused() { + return None; + } + + PhysFrame::from_start_address(p1_entry.addr()).ok() + } +} + +fn p3_ptr(page: Page, recursive_index: u9) -> *mut PageTable { + p3_page(page, recursive_index).start_address().as_mut_ptr() +} + +fn p3_page(page: Page, recursive_index: u9) -> Page { + Page::from_page_table_indices( + recursive_index, + recursive_index, + recursive_index, + page.p4_index(), + ) +} + +fn p2_ptr(page: Page, recursive_index: u9) -> *mut PageTable { + p2_page(page, recursive_index).start_address().as_mut_ptr() +} + +fn p2_page(page: Page, recursive_index: u9) -> Page { + Page::from_page_table_indices( + recursive_index, + recursive_index, + page.p4_index(), + page.p3_index(), + ) +} + +fn p1_ptr(page: Page, recursive_index: u9) -> *mut PageTable { + p1_page(page, recursive_index).start_address().as_mut_ptr() +} + +fn p1_page(page: Page, recursive_index: u9) -> Page { + Page::from_page_table_indices( + recursive_index, + page.p4_index(), + page.p3_index(), + page.p2_index(), + ) +} diff --git a/crate/aarch64/src/regs/cntfrq_el0.rs b/crate/aarch64/src/regs/cntfrq_el0.rs new file mode 100644 index 0000000..df56ac9 --- /dev/null +++ b/crate/aarch64/src/regs/cntfrq_el0.rs @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! Counter-timer Frequency register - EL0 +//! +//! This register is provided so that software can discover the frequency of the +//! system counter. It must be programmed with this value as part of system +//! initialization. The value of the register is not interpreted by hardware. + +use register::cpu::RegisterReadOnly; + +pub struct Reg; + +impl RegisterReadOnly for Reg { + sys_coproc_read_raw!(u32, "CNTFRQ_EL0"); +} + +pub static CNTFRQ_EL0: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/cnthctl_el2.rs b/crate/aarch64/src/regs/cnthctl_el2.rs new file mode 100644 index 0000000..f5e3c2c --- /dev/null +++ b/crate/aarch64/src/regs/cnthctl_el2.rs @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! Counter-timer Hypervisor Control register - EL2 +//! +//! Controls the generation of an event stream from the physical counter, and +//! access from Non-secure EL1 to the physical counter and the Non-secure EL1 +//! physical timer. + +use register::cpu::RegisterReadWrite; + +// When HCR_EL2.E2H == 0: +// TODO: Figure out how we can differentiate depending on HCR_EL2.E2H state +// +// For now, implement the HCR_EL2.E2H == 0 version +register_bitfields! {u32, + CNTHCTL_EL2 [ + /// Traps Non-secure EL0 and EL1 accesses to the physical timer + /// registers to EL2. + /// + /// 0 From AArch64 state: Non-secure EL0 and EL1 accesses to the + /// CNTP_CTL_EL0, CNTP_CVAL_EL0, and CNTP_TVAL_EL0 are trapped to EL2, + /// unless it is trapped by CNTKCTL_EL1.EL0PTEN. + /// + /// From AArch32 state: Non-secure EL0 and EL1 accesses to the + /// CNTP_CTL, CNTP_CVAL, and CNTP_TVAL are trapped to EL2, unless it + /// is trapped by CNTKCTL_EL1.EL0PTEN or CNTKCTL.PL0PTEN. + /// + /// 1 This control does not cause any instructions to be trapped. + /// + /// If EL3 is implemented and EL2 is not implemented, behavior is as if + /// this bit is 1 other than for the purpose of a direct read. + EL1PCEN OFFSET(1) NUMBITS(1) [], + + /// Traps Non-secure EL0 and EL1 accesses to the physical counter + /// register to EL2. + /// + /// 0 From AArch64 state: Non-secure EL0 and EL1 accesses to the + /// CNTPCT_EL0 are trapped to EL2, unless it is trapped by + /// CNTKCTL_EL1.EL0PCTEN. + /// + /// From AArch32 state: Non-secure EL0 and EL1 accesses to the CNTPCT + /// are trapped to EL2, unless it is trapped by CNTKCTL_EL1.EL0PCTEN + /// or CNTKCTL.PL0PCTEN. + /// + /// 1 This control does not cause any instructions to be trapped. + /// + /// If EL3 is implemented and EL2 is not implemented, behavior is as if + /// this bit is 1 other than for the purpose of a direct read. + EL1PCTEN OFFSET(0) NUMBITS(1) [] + ] +} + +pub struct Reg; + +impl RegisterReadWrite for Reg { + sys_coproc_read_raw!(u32, "CNTHCTL_EL2"); + sys_coproc_write_raw!(u32, "CNTHCTL_EL2"); +} + +#[allow(non_upper_case_globals)] +pub static CNTHCTL_EL2: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/cntp_ctl_el0.rs b/crate/aarch64/src/regs/cntp_ctl_el0.rs new file mode 100644 index 0000000..76991eb --- /dev/null +++ b/crate/aarch64/src/regs/cntp_ctl_el0.rs @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! Counter-timer Physical Timer Control register - EL0 +//! +//! Control register for the EL1 physical timer. + +use register::cpu::RegisterReadWrite; + +register_bitfields! {u32, + CNTP_CTL_EL0 [ + /// The status of the timer. This bit indicates whether the timer + /// condition is met: + /// + /// 0 Timer condition is not met. + /// 1 Timer condition is met. + /// + /// When the value of the ENABLE bit is 1, ISTATUS indicates whether the + /// timer condition is met. ISTATUS takes no account of the value of the + /// IMASK bit. If the value of ISTATUS is 1 and the value of IMASK is 0 + /// then the timer interrupt is asserted. + /// + /// When the value of the ENABLE bit is 0, the ISTATUS field is UNKNOWN. + /// + /// This bit is read-only. + ISTATUS OFFSET(2) NUMBITS(1) [], + + /// Timer interrupt mask bit. Permitted values are: + /// + /// 0 Timer interrupt is not masked by the IMASK bit. + /// 1 Timer interrupt is masked by the IMASK bit. + IMASK OFFSET(1) NUMBITS(1) [], + + /// Enables the timer. Permitted values are: + /// + /// 0 Timer disabled. + /// 1 Timer enabled. + ENABLE OFFSET(0) NUMBITS(1) [] + ] +} + +pub struct Reg; + +impl RegisterReadWrite for Reg { + sys_coproc_read_raw!(u32, "CNTP_CTL_EL0"); + sys_coproc_write_raw!(u32, "CNTP_CTL_EL0"); +} + +pub static CNTP_CTL_EL0: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/cntp_tval_el0.rs b/crate/aarch64/src/regs/cntp_tval_el0.rs new file mode 100644 index 0000000..bdf5f6a --- /dev/null +++ b/crate/aarch64/src/regs/cntp_tval_el0.rs @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! Counter-timer Physical Timer TimerValue register - EL0 +//! +//! Holds the timer value for the EL1 physical timer. + +use register::cpu::RegisterReadWrite; + +pub struct Reg; + +impl RegisterReadWrite for Reg { + sys_coproc_read_raw!(u32, "CNTP_TVAL_EL0"); + sys_coproc_write_raw!(u32, "CNTP_TVAL_EL0"); +} + +pub static CNTP_TVAL_EL0: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/cntpct_el0.rs b/crate/aarch64/src/regs/cntpct_el0.rs new file mode 100644 index 0000000..b381d99 --- /dev/null +++ b/crate/aarch64/src/regs/cntpct_el0.rs @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! Counter-timer Physical Count register - EL0 +//! +//! Holds the 64-bit physical count value. + +use register::cpu::RegisterReadOnly; + +pub struct Reg; + +impl RegisterReadOnly for Reg { + sys_coproc_read_raw!(u64, "CNTPCT_EL0"); +} + +pub static CNTPCT_EL0: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/cntvoff_el2.rs b/crate/aarch64/src/regs/cntvoff_el2.rs new file mode 100644 index 0000000..aff7074 --- /dev/null +++ b/crate/aarch64/src/regs/cntvoff_el2.rs @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! Counter-timer Virtual Offset register - EL2 +//! +//! Holds the 64-bit virtual offset. This is the offset between the physical +//! count value visible in CNTPCT_EL0 and the virtual count value visible in +//! CNTVCT_EL0. + +use register::cpu::RegisterReadWrite; + +pub struct Reg; + +impl RegisterReadWrite for Reg { + sys_coproc_read_raw!(u64, "CNTVOFF_EL2"); + sys_coproc_write_raw!(u64, "CNTVOFF_EL2"); +} + +pub static CNTVOFF_EL2: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/currentel.rs b/crate/aarch64/src/regs/currentel.rs new file mode 100644 index 0000000..91b8e0a --- /dev/null +++ b/crate/aarch64/src/regs/currentel.rs @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! Current Exception Level +//! +//! Holds the current Exception level. + +use register::cpu::RegisterReadOnly; + +register_bitfields! {u32, + CurrentEL [ + /// Current Exception level. Possible values of this field are: + /// + /// 00 EL0 + /// 01 EL1 + /// 10 EL2 + /// 11 EL3 + /// + /// When the HCR_EL2.NV bit is 1, Non-secure EL1 read accesses to the + /// CurrentEL register return the value of 0x2 in this field. + /// + /// This field resets to a value that is architecturally UNKNOWN. + EL OFFSET(2) NUMBITS(2) [ + EL0 = 0, + EL1 = 1, + EL2 = 2, + EL3 = 3 + ] + ] +} + +pub struct Reg; + +impl RegisterReadOnly for Reg { + sys_coproc_read_raw!(u32, "CurrentEL"); +} + +#[allow(non_upper_case_globals)] +pub static CurrentEL: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/daif.rs b/crate/aarch64/src/regs/daif.rs new file mode 100644 index 0000000..bf810a2 --- /dev/null +++ b/crate/aarch64/src/regs/daif.rs @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! Interrupt Mask Bits +//! +//! Allows access to the interrupt mask bits. + +use register::cpu::RegisterReadWrite; + +register_bitfields! {u32, + DAIF [ + /// Process state D mask. The possible values of this bit are: + /// + /// 0 Watchpoint, Breakpoint, and Software Step exceptions targeted at + /// the current Exception level are not masked. + /// + /// 1 Watchpoint, Breakpoint, and Software Step exceptions targeted at + /// the current Exception level are masked. + /// + /// When the target Exception level of the debug exception is higher + /// than the current Exception level, the exception is not masked by + /// this bit. + /// + /// When this register has an architecturally-defined reset value, this + /// field resets to 1. + D OFFSET(9) NUMBITS(1) [ + Unmasked = 0, + Masked = 1 + ], + + /// SError interrupt mask bit. The possible values of this bit are: + /// + /// 0 Exception not masked. + /// 1 Exception masked. + /// + /// When this register has an architecturally-defined reset value, this + /// field resets to 1 . + A OFFSET(8) NUMBITS(1) [ + Unmasked = 0, + Masked = 1 + ], + + /// IRQ mask bit. The possible values of this bit are: + /// + /// 0 Exception not masked. + /// 1 Exception masked. + /// + /// When this register has an architecturally-defined reset value, this + /// field resets to 1 . + I OFFSET(7) NUMBITS(1) [ + Unmasked = 0, + Masked = 1 + ], + + /// FIQ mask bit. The possible values of this bit are: + /// + /// 0 Exception not masked. + /// 1 Exception masked. + /// + /// When this register has an architecturally-defined reset value, this + /// field resets to 1 . + F OFFSET(6) NUMBITS(1) [ + Unmasked = 0, + Masked = 1 + ] + ] +} + + +pub struct Reg; + +impl RegisterReadWrite for Reg { + sys_coproc_read_raw!(u32, "DAIF"); + sys_coproc_write_raw!(u32, "DAIF"); +} + +pub static DAIF: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/elr_el2.rs b/crate/aarch64/src/regs/elr_el2.rs new file mode 100644 index 0000000..0786fbb --- /dev/null +++ b/crate/aarch64/src/regs/elr_el2.rs @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! Exception Link Register - EL2 +//! +//! When taking an exception to EL2, holds the address to return to. + +use register::cpu::RegisterReadWrite; + +pub struct Reg; + +impl RegisterReadWrite for Reg { + sys_coproc_read_raw!(u64, "ELR_EL2"); + sys_coproc_write_raw!(u64, "ELR_EL2"); +} + +pub static ELR_EL2: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/hcr_el2.rs b/crate/aarch64/src/regs/hcr_el2.rs new file mode 100644 index 0000000..683bbef --- /dev/null +++ b/crate/aarch64/src/regs/hcr_el2.rs @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! Hypervisor Configuration Register - EL2 +//! +//! Provides configuration controls for virtualization, including defining +//! whether various Non-secure operations are trapped to EL2. + +use register::cpu::RegisterReadWrite; + +register_bitfields! {u64, + HCR_EL2 [ + /// Execution state control for lower Exception levels: + /// + /// 0 Lower levels are all AArch32. + /// 1 The Execution state for EL1 is AArch64. The Execution state for + /// EL0 is determined by the current value of PSTATE.nRW when + /// executing at EL0. + /// + /// If all lower Exception levels cannot use AArch32 then this bit is + /// RAO/WI. + /// + /// In an implementation that includes EL3, when SCR_EL3.NS==0, the PE + /// behaves as if this bit has the same value as the SCR_EL3.RW bit for + /// all purposes other than a direct read or write access of HCR_EL2. + /// + /// The RW bit is permitted to be cached in a TLB. + /// + /// When ARMv8.1-VHE is implemented, and the value of HCR_EL2.{E2H, TGE} + /// is {1, 1}, this field behaves as 1 for all purposes other than a + /// direct read of the value of this bit. + RW OFFSET(31) NUMBITS(1) [ + AllLowerELsAreAarch32 = 0, + EL1IsAarch64 = 1 + ], + + /// Default Cacheability. + /// + /// 0 This control has no effect on the Non-secure EL1&0 translation + /// regime. + /// + /// 1 In Non-secure state: + /// - When EL1 is using AArch64, the PE behaves as if the value of + /// the SCTLR_EL1.M field is 0 for all purposes other than + /// returning the value of a direct read of SCTLR_EL1. + /// + /// - When EL1 is using AArch32, the PE behaves as if the value of + /// the SCTLR.M field is 0 for all purposes other than returning + /// the value of a direct read of SCTLR. + /// + /// - The PE behaves as if the value of the HCR_EL2.VM field is 1 + /// for all purposes other than returning the value of a direct + /// read of HCR_EL2. + /// + /// - The memory type produced by stage 1 of the EL1&0 translation + /// regime is Normal Non-Shareable, Inner Write-Back Read-Allocate + /// Write-Allocate, Outer Write-Back Read-Allocate Write-Allocate. + /// + /// This field has no effect on the EL2, EL2&0, and EL3 translation + /// regimes. + /// + /// This field is permitted to be cached in a TLB. + /// + /// In an implementation that includes EL3, when the value of SCR_EL3.NS + /// is 0 the PE behaves as if this field is 0 for all purposes other + /// than a direct read or write access of HCR_EL2. + /// + /// When ARMv8.1-VHE is implemented, and the value of HCR_EL2.{E2H, TGE} + /// is {1, 1}, this field behaves as 0 for all purposes other than a + /// direct read of the value of this field. + DC OFFSET(12) NUMBITS(1) [], + + /// Set/Way Invalidation Override. Causes Non-secure EL1 execution of + /// the data cache invalidate by set/way instructions to perform a data + /// cache clean and invalidate by set/way: + /// + /// 0 This control has no effect on the operation of data cache + /// invalidate by set/way instructions. + /// + /// 1 Data cache invalidate by set/way instructions perform a data cache + /// clean and invalidate by set/way. + /// + /// When the value of this bit is 1: + /// + /// AArch32: DCISW performs the same invalidation as a DCCISW + /// instruction. + /// + /// AArch64: DC ISW performs the same invalidation as a DC CISW + /// instruction. + /// + /// This bit can be implemented as RES 1. + /// + /// In an implementation that includes EL3, when the value of SCR_EL3.NS + /// is 0 the PE behaves as if this field is 0 for all purposes other + /// than a direct read or write access of HCR_EL2. + /// + /// When HCR_EL2.TGE is 1, the PE ignores the value of this field for + /// all purposes other than a direct read of this field. + SWIO OFFSET(1) NUMBITS(1) [] + ] +} + +pub struct Reg; + +impl RegisterReadWrite for Reg { + sys_coproc_read_raw!(u64, "HCR_EL2"); + sys_coproc_write_raw!(u64, "HCR_EL2"); +} + +pub static HCR_EL2: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/id_aa64mmfr0_el1.rs b/crate/aarch64/src/regs/id_aa64mmfr0_el1.rs new file mode 100644 index 0000000..f75813c --- /dev/null +++ b/crate/aarch64/src/regs/id_aa64mmfr0_el1.rs @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! AArch64 Memory Model Feature Register 0 - EL1 +//! +//! Provides information about the implemented memory model and memory +//! management support in AArch64 state. + +use register::cpu::RegisterReadOnly; + +register_bitfields! {u64, + ID_AA64MMFR0_EL1 [ + /// Support for 4KiB memory translation granule size. Defined values + /// are: + /// + /// 0000 4KiB granule supported. + /// 1111 4KiB granule not supported. + /// + /// All other values are reserved. + TGran4 OFFSET(28) NUMBITS(4) [ + Supported = 0b0000, + NotSupported = 0b1111 + ], + + /// Support for 64KiB memory translation granule size. Defined values + /// are: + /// + /// 0000 64KiB granule supported. + /// 1111 64KiB granule not supported. + /// + /// All other values are reserved. + TGran64 OFFSET(24) NUMBITS(4) [ + Supported = 0b0000, + NotSupported = 0b1111 + ], + + /// Physical Address range supported. Defined values are: + /// + /// 0000 32 bits, 4GiB. + /// 0001 36 bits, 64GiB. + /// 0010 40 bits, 1TiB. + /// 0011 42 bits, 4TiB. + /// 0100 44 bits, 16TiB. + /// 0101 48 bits, 256TiB. + /// 0110 52 bits, 4PiB. + /// + /// All other values are reserved. + /// + /// The value 0110 is permitted only if the implementation includes + /// ARMv8.2-LPA, otherwise it is reserved. + PARange OFFSET(0) NUMBITS(4) [ + Bits_32 = 0b0000, + Bits_36 = 0b0001, + Bits_40 = 0b0010, + Bits_42 = 0b0011, + Bits_44 = 0b0100, + Bits_48 = 0b0101, + Bits_52 = 0b0110 + ] + ] +} + +pub struct Reg; + +impl RegisterReadOnly for Reg { + sys_coproc_read_raw!(u64, "ID_AA64MMFR0_EL1"); +} + +pub static ID_AA64MMFR0_EL1: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/macros.rs b/crate/aarch64/src/regs/macros.rs new file mode 100644 index 0000000..bd4439c --- /dev/null +++ b/crate/aarch64/src/regs/macros.rs @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +macro_rules! __read_raw { + ($width:ty, $asm_instr:tt, $asm_reg_name:tt) => { + /// Reads the raw bits of the CPU register. + #[inline] + fn get(&self) -> $width { + match () { + #[cfg(target_arch = "aarch64")] + () => { + let reg; + unsafe { + asm!(concat!($asm_instr, " $0, ", $asm_reg_name) : "=r"(reg) ::: "volatile"); + } + reg + } + + #[cfg(not(target_arch = "aarch64"))] + () => unimplemented!(), + } + } + }; +} + +macro_rules! __write_raw { + ($width:ty, $asm_instr:tt, $asm_reg_name:tt) => { + /// Writes raw bits to the CPU register. + #[cfg_attr(not(target_arch = "aarch64"), allow(unused_variables))] + #[inline] + fn set(&self, value: $width) { + match () { + #[cfg(target_arch = "aarch64")] + () => { + unsafe { + asm!(concat!($asm_instr, " ", $asm_reg_name, ", $0") :: "r"(value) :: "volatile") + } + } + + #[cfg(not(target_arch = "aarch64"))] + () => unimplemented!(), + } + } + }; +} + +/// Raw read from system coprocessor registers +macro_rules! sys_coproc_read_raw { + ($width:ty, $asm_reg_name:tt) => { + __read_raw!($width, "mrs", $asm_reg_name); + }; +} + +/// Raw write to system coprocessor registers +macro_rules! sys_coproc_write_raw { + ($width:ty, $asm_reg_name:tt) => { + __write_raw!($width, "msr", $asm_reg_name); + }; +} + +/// Raw read from (ordinary) registers +macro_rules! read_raw { + ($width:ty, $asm_reg_name:tt) => { + __read_raw!($width, "mov", $asm_reg_name); + }; +} +/// Raw write to (ordinary) registers +macro_rules! write_raw { + ($width:ty, $asm_reg_name:tt) => { + __write_raw!($width, "mov", $asm_reg_name); + }; +} diff --git a/crate/aarch64/src/regs/mair_el1.rs b/crate/aarch64/src/regs/mair_el1.rs new file mode 100644 index 0000000..2c7c7da --- /dev/null +++ b/crate/aarch64/src/regs/mair_el1.rs @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! Memory Attribute Indirection Register - EL1 +//! +//! Provides the memory attribute encodings corresponding to the possible +//! AttrIndx values in a Long-descriptor format translation table entry for +//! stage 1 translations at EL1. + +use register::cpu::RegisterReadWrite; + +register_bitfields! {u64, + MAIR_EL1 [ + // TODO: Macrofy this + + /// Attribute 7 + Attr7_HIGH OFFSET(60) NUMBITS(4) [], + Attr7_LOW_DEVICE OFFSET(56) NUMBITS(4) [], + Attr7_LOW_MEMORY OFFSET(56) NUMBITS(4) [], + + /// Attribute 6 + Attr6_HIGH OFFSET(52) NUMBITS(4) [], + Attr6_LOW_DEVICE OFFSET(48) NUMBITS(4) [], + Attr6_LOW_MEMORY OFFSET(48) NUMBITS(4) [], + + /// Attribute 5 + Attr5_HIGH OFFSET(44) NUMBITS(4) [], + Attr5_LOW_DEVICE OFFSET(40) NUMBITS(4) [], + Attr5_LOW_MEMORY OFFSET(40) NUMBITS(4) [], + + /// Attribute 4 + Attr4_HIGH OFFSET(36) NUMBITS(4) [], + Attr4_LOW_DEVICE OFFSET(32) NUMBITS(4) [], + Attr4_LOW_MEMORY OFFSET(32) NUMBITS(4) [], + + /// Attribute 3 + Attr3_HIGH OFFSET(28) NUMBITS(4) [], + Attr3_LOW_DEVICE OFFSET(24) NUMBITS(4) [], + Attr3_LOW_MEMORY OFFSET(24) NUMBITS(4) [], + + /// Attribute 2 + Attr2_HIGH OFFSET(20) NUMBITS(4) [ + Device = 0b0000, + Memory_OuterNonCacheable = 0b0100, + Memory_OuterWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 + ], + Attr2_LOW_DEVICE OFFSET(16) NUMBITS(4) [ + Device_nGnRE = 0b0100 + ], + Attr2_LOW_MEMORY OFFSET(16) NUMBITS(4) [ + InnerNonCacheable = 0b0100, + InnerWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 + ], + + /// Attribute 1 + Attr1_HIGH OFFSET(12) NUMBITS(4) [ + Device = 0b0000, + Memory_OuterNonCacheable = 0b0100, + Memory_OuterWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 + ], + Attr1_LOW_DEVICE OFFSET(8) NUMBITS(4) [ + Device_nGnRE = 0b0100 + ], + Attr1_LOW_MEMORY OFFSET(8) NUMBITS(4) [ + InnerNonCacheable = 0b0100, + InnerWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 + ], + + /// Attribute 0 + Attr0_HIGH OFFSET(4) NUMBITS(4) [ + Device = 0b0000, + Memory_OuterNonCacheable = 0b0100, + Memory_OuterWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 + ], + Attr0_LOW_DEVICE OFFSET(0) NUMBITS(4) [ + Device_nGnRE = 0b0100 + ], + Attr0_LOW_MEMORY OFFSET(0) NUMBITS(4) [ + InnerNonCacheable = 0b0100, + InnerWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 + ] + ] +} + +pub struct Reg; + +impl RegisterReadWrite for Reg { + sys_coproc_read_raw!(u64, "MAIR_EL1"); + sys_coproc_write_raw!(u64, "MAIR_EL1"); +} + +pub static MAIR_EL1: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/mod.rs b/crate/aarch64/src/regs/mod.rs new file mode 100644 index 0000000..f6a0e3d --- /dev/null +++ b/crate/aarch64/src/regs/mod.rs @@ -0,0 +1,51 @@ +//! Processor core registers + +#[macro_use] +mod macros; + +mod cntfrq_el0; +mod cnthctl_el2; +mod cntp_ctl_el0; +mod cntp_tval_el0; +mod cntpct_el0; +mod cntvoff_el2; +mod currentel; +mod daif; +mod elr_el2; +mod hcr_el2; +mod id_aa64mmfr0_el1; +mod mair_el1; +mod mpidr_el1; +mod sctlr_el1; +mod sp; +mod sp_el0; +mod sp_el1; +mod spsel; +mod spsr_el2; +mod tcr_el1; +mod ttbr0_el1; + +// Export only the R/W traits and the static reg definitions +pub use register::cpu::*; + +pub use self::cntfrq_el0::CNTFRQ_EL0; +pub use self::cnthctl_el2::CNTHCTL_EL2; +pub use self::cntp_ctl_el0::CNTP_CTL_EL0; +pub use self::cntp_tval_el0::CNTP_TVAL_EL0; +pub use self::cntpct_el0::CNTPCT_EL0; +pub use self::cntvoff_el2::CNTVOFF_EL2; +pub use self::currentel::CurrentEL; +pub use self::daif::DAIF; +pub use self::elr_el2::ELR_EL2; +pub use self::hcr_el2::HCR_EL2; +pub use self::id_aa64mmfr0_el1::ID_AA64MMFR0_EL1; +pub use self::mair_el1::MAIR_EL1; +pub use self::mpidr_el1::MPIDR_EL1; +pub use self::sctlr_el1::SCTLR_EL1; +pub use self::sp::SP; +pub use self::sp_el0::SP_EL0; +pub use self::sp_el1::SP_EL1; +pub use self::spsel::SPSel; +pub use self::spsr_el2::SPSR_EL2; +pub use self::tcr_el1::TCR_EL1; +pub use self::ttbr0_el1::TTBR0_EL1; diff --git a/crate/aarch64/src/regs/mpidr_el1.rs b/crate/aarch64/src/regs/mpidr_el1.rs new file mode 100644 index 0000000..6fbfea0 --- /dev/null +++ b/crate/aarch64/src/regs/mpidr_el1.rs @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! Multiprocessor Affinity Register - EL1 +//! +//! In a multiprocessor system, provides an additional PE +//! identification mechanism for scheduling purposes. + +use register::cpu::RegisterReadOnly; + +pub struct Reg; + +impl RegisterReadOnly for Reg { + sys_coproc_read_raw!(u64, "MPIDR_EL1"); +} + +pub static MPIDR_EL1: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/sctlr_el1.rs b/crate/aarch64/src/regs/sctlr_el1.rs new file mode 100644 index 0000000..1f463b4 --- /dev/null +++ b/crate/aarch64/src/regs/sctlr_el1.rs @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! System Control Register - EL1 +//! +//! Provides top level control of the system, including its memory system, at +//! EL1 and EL0. + +use register::cpu::RegisterReadWrite; + +register_bitfields! {u32, + SCTLR_EL1 [ + /// Instruction access Cacheability control, for accesses at EL0 and + /// EL1: + /// + /// 0 All instruction access to Normal memory from EL0 and EL1 are + /// Non-cacheable for all levels of instruction and unified cache. + /// + /// If the value of SCTLR_EL1.M is 0, instruction accesses from stage + /// 1 of the EL1&0 translation regime are to Normal, Outer Shareable, + /// Inner Non-cacheable, Outer Non-cacheable memory. + /// + /// 1 This control has no effect on the Cacheability of instruction + /// access to Normal memory from EL0 and EL1. + /// + /// If the value of SCTLR_EL1.M is 0, instruction accesses from stage + /// 1 of the EL1&0 translation regime are to Normal, Outer Shareable, + /// Inner Write-Through, Outer Write-Through memory. + /// + /// When the value of the HCR_EL2.DC bit is 1, then instruction access + /// to Normal memory from EL0 and EL1 are Cacheable regardless of the + /// value of the SCTLR_EL1.I bit. + /// + /// When ARMv8.1-VHE is implemented, and the value of HCR_EL2.{E2H, TGE} + /// is {1, 1}, this bit has no effect on the PE. + /// + /// When this register has an architecturally-defined reset value, this + /// field resets to 0. + I OFFSET(12) NUMBITS(1) [ + NonCacheable = 0, + Cacheable = 1 + ], + + /// Cacheability control, for data accesses. + /// + /// 0 All data access to Normal memory from EL0 and EL1, and all Normal + /// memory accesses to the EL1&0 stage 1 translation tables, are + /// Non-cacheable for all levels of data and unified cache. + /// + /// 1 This control has no effect on the Cacheability of: + /// - Data access to Normal memory from EL0 and EL1. + /// - Normal memory accesses to the EL1&0 stage 1 translation + /// tables. + /// + /// When the value of the HCR_EL2.DC bit is 1, the PE ignores + /// SCLTR.C. This means that Non-secure EL0 and Non-secure EL1 data + /// accesses to Normal memory are Cacheable. + /// + /// When ARMv8.1-VHE is implemented, and the value of HCR_EL2.{E2H, TGE} + /// is {1, 1}, this bit has no effect on the PE. + /// + /// When this register has an architecturally-defined reset value, this + /// field resets to 0. + C OFFSET(2) NUMBITS(1) [ + NonCacheable = 0, + Cacheable = 1 + ], + + /// MMU enable for EL1 and EL0 stage 1 address translation. Possible + /// values of this bit are: + /// + /// 0 EL1 and EL0 stage 1 address translation disabled. + /// See the SCTLR_EL1.I field for the behavior of instruction accesses + /// to Normal memory. + /// 1 EL1 and EL0 stage 1 address translation enabled. + M OFFSET(0) NUMBITS(1) [ + Disable = 0, + Enable = 1 + ] + ] +} + +pub struct Reg; + +impl RegisterReadWrite for Reg { + sys_coproc_read_raw!(u32, "SCTLR_EL1"); + sys_coproc_write_raw!(u32, "SCTLR_EL1"); +} + +pub static SCTLR_EL1: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/sp.rs b/crate/aarch64/src/regs/sp.rs new file mode 100644 index 0000000..f9f578b --- /dev/null +++ b/crate/aarch64/src/regs/sp.rs @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! The stack pointer + +use register::cpu::RegisterReadWrite; + +pub struct Reg; + +impl RegisterReadWrite for Reg { + read_raw!(u64, "sp"); + write_raw!(u64, "sp"); +} + +pub static SP: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/sp_el0.rs b/crate/aarch64/src/regs/sp_el0.rs new file mode 100644 index 0000000..aa82fdb --- /dev/null +++ b/crate/aarch64/src/regs/sp_el0.rs @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! The stack pointer - EL0 +//! +//! Holds the stack pointer associated with EL0. At higher Exception levels, +//! this is used as the current stack pointer when the value of SPSel.SP is 0. + +use register::cpu::RegisterReadWrite; + +pub struct Reg; + +impl RegisterReadWrite for Reg { + sys_coproc_read_raw!(u64, "SP_EL0"); + sys_coproc_write_raw!(u64, "SP_EL0"); +} + +pub static SP_EL0: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/sp_el1.rs b/crate/aarch64/src/regs/sp_el1.rs new file mode 100644 index 0000000..4357412 --- /dev/null +++ b/crate/aarch64/src/regs/sp_el1.rs @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! The stack pointer - EL1 +//! +//! Holds the stack pointer associated with EL1. When executing at EL1, the +//! value of SPSel.SP determines the current stack pointer: +//! +//! SPSel.SP | current stack pointer +//! -------------------------------- +//! 0 | SP_EL0 +//! 1 | SP_EL1 + +use register::cpu::RegisterReadWrite; + +pub struct Reg; + +impl RegisterReadWrite for Reg { + sys_coproc_read_raw!(u64, "SP_EL1"); + sys_coproc_write_raw!(u64, "SP_EL1"); +} + +pub static SP_EL1: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/spsel.rs b/crate/aarch64/src/regs/spsel.rs new file mode 100644 index 0000000..91e3694 --- /dev/null +++ b/crate/aarch64/src/regs/spsel.rs @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! Stack Pointer Select +//! +//! Allows the Stack Pointer to be selected between SP_EL0 and SP_ELx. + +use register::cpu::RegisterReadWrite; + +register_bitfields! {u32, + SPSel [ + /// Stack pointer to use. Possible values of this bit are: + /// + /// 0 Use SP_EL0 at all Exception levels. + /// 1 Use SP_ELx for Exception level ELx. + /// + /// When this register has an architecturally-defined reset value, this + /// field resets to 1. + SP OFFSET(0) NUMBITS(1) [ + EL0 = 0, + ELx = 1 + ] + ] +} + + +pub struct Reg; + +impl RegisterReadWrite for Reg { + sys_coproc_read_raw!(u32, "SPSEL"); + sys_coproc_write_raw!(u32, "SPSEL"); +} + +#[allow(non_upper_case_globals)] +pub static SPSel: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/spsr_el2.rs b/crate/aarch64/src/regs/spsr_el2.rs new file mode 100644 index 0000000..56078a4 --- /dev/null +++ b/crate/aarch64/src/regs/spsr_el2.rs @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! Saved Program Status Register - EL2 +//! +//! Holds the saved process state when an exception is taken to EL2. + +use register::cpu::RegisterReadWrite; + +register_bitfields! {u32, + SPSR_EL2 [ + /// Process state D mask. The possible values of this bit are: + /// + /// 0 Watchpoint, Breakpoint, and Software Step exceptions targeted at + /// the current Exception level are not masked. + /// + /// 1 Watchpoint, Breakpoint, and Software Step exceptions targeted at + /// the current Exception level are masked. + /// + /// When the target Exception level of the debug exception is higher + /// than the current Exception level, the exception is not masked by + /// this bit. + D OFFSET(9) NUMBITS(1) [ + Unmasked = 0, + Masked = 1 + ], + + /// SError interrupt mask bit. The possible values of this bit are: + /// + /// 0 Exception not masked. + /// 1 Exception masked. + A OFFSET(8) NUMBITS(1) [ + Unmasked = 0, + Masked = 1 + ], + + /// IRQ mask bit. The possible values of this bit are: + /// + /// 0 Exception not masked. + /// 1 Exception masked. + I OFFSET(7) NUMBITS(1) [ + Unmasked = 0, + Masked = 1 + ], + + /// FIQ mask bit. The possible values of this bit are: + /// + /// 0 Exception not masked. + /// 1 Exception masked. + F OFFSET(6) NUMBITS(1) [ + Unmasked = 0, + Masked = 1 + ], + + /// AArch64 state (Exception level and selected SP) that an exception + /// was taken from. The possible values are: + /// + /// M[3:0] | State + /// -------------- + /// 0b0000 | EL0t + /// 0b0100 | EL1t + /// 0b0101 | EL1h + /// 0b1000 | EL2t + /// 0b1001 | EL2h + /// + /// Other values are reserved, and returning to an Exception level that + /// is using AArch64 with a reserved value in this field is treated as + /// an illegal exception return. + /// + /// The bits in this field are interpreted as follows: + /// - M[3:2] holds the Exception Level. + /// - M[1] is unused and is RES 0 for all non-reserved values. + /// - M[0] is used to select the SP: + /// - 0 means the SP is always SP0. + /// - 1 means the exception SP is determined by the EL. + M OFFSET(0) NUMBITS(4) [ + EL0t = 0b0000, + EL1t = 0b0100, + EL1h = 0b0101, + EL2t = 0b1000, + EL2h = 0b1001 + ] + ] +} + +pub struct Reg; + +impl RegisterReadWrite for Reg { + sys_coproc_read_raw!(u32, "SPSR_EL2"); + sys_coproc_write_raw!(u32, "SPSR_EL2"); +} + +pub static SPSR_EL2: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/tcr_el1.rs b/crate/aarch64/src/regs/tcr_el1.rs new file mode 100644 index 0000000..bcd0425 --- /dev/null +++ b/crate/aarch64/src/regs/tcr_el1.rs @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! Translation Control Register - EL1 +//! +//! The control register for stage 1 of the EL1&0 translation regime. + +use register::cpu::RegisterReadWrite; + +register_bitfields! {u64, + TCR_EL1 [ + /// Top Byte ignored - indicates whether the top byte of an address is + /// used for address match for the TTBR0_EL1 region, or ignored and used + /// for tagged addresses. Defined values are: + /// + /// 0 Top Byte used in the address calculation. + /// 1 Top Byte ignored in the address calculation. + TBI0 OFFSET(37) NUMBITS(1) [ + Used = 0, + Ignored = 1 + ], + + /// Intermediate Physical Address Size. + /// + /// 000 32 bits, 4GiB. + /// 001 36 bits, 64GiB. + /// 010 40 bits, 1TiB. + /// 011 42 bits, 4TiB. + /// 100 44 bits, 16TiB. + /// 101 48 bits, 256TiB. + /// 110 52 bits, 4PiB + /// + /// Other values are reserved. + /// + /// The reserved values behave in the same way as the 101 or 110 + /// encoding, but software must not rely on this property as the + /// behavior of the reserved values might change in a future revision of + /// the architecture. + /// + /// The value 110 is permitted only if ARMv8.2-LPA is implemented and + /// the translation granule size is 64KiB. + /// + /// In an implementation that supports 52-bit PAs, if the value of this + /// field is not 110 , then bits[51:48] of every translation table base + /// address for the stage of translation controlled by TCR_EL1 are 0000 + /// . + IPS OFFSET(32) NUMBITS(3) [ + Bits_32 = 0b000, + Bits_36 = 0b001, + Bits_40 = 0b010, + Bits_42 = 0b011, + Bits_44 = 0b100, + Bits_48 = 0b101, + Bits_52 = 0b110 + ], + + /// Granule size for the TTBR0_EL1. + /// + /// 00 4KiB + /// 01 64KiB + /// 10 16KiB + /// + /// Other values are reserved. + /// + /// If the value is programmed to either a reserved value, or a size + /// that has not been implemented, then the hardware will treat the + /// field as if it has been programmed to an IMPLEMENTATION DEFINED + /// choice of the sizes that has been implemented for all purposes other + /// than the value read back from this register. + /// + /// It is IMPLEMENTATION DEFINED whether the value read back is the + /// value programmed or the value that corresponds to the size chosen. + TG0 OFFSET(14) NUMBITS(2) [ + KiB_4 = 0b00, + KiB_16 = 0b10, + KiB_64 = 0b01 + ], + + /// Shareability attribute for memory associated with translation table + /// walks using TTBR0_EL1. + /// + /// 00 Non-shareable + /// 10 Outer Shareable + /// 11 Inner Shareable + /// + /// Other values are reserved. + SH0 OFFSET(12) NUMBITS(2) [ + None = 0b00, + Outer = 0b10, + Inner = 0b11 + ], + + /// Outer cacheability attribute for memory associated with translation + /// table walks using TTBR0_EL1. + /// + /// 00 Normal memory, Outer Non-cacheable + /// + /// 01 Normal memory, Outer Write-Back Read-Allocate Write-Allocate + /// Cacheable + /// + /// 10 Normal memory, Outer Write-Through Read-Allocate No + /// Write-Allocate Cacheable + /// + /// 11 Normal memory, Outer Write-Back Read-Allocate No Write-Allocate + /// Cacheable + ORGN0 OFFSET(10) NUMBITS(2) [ + NonCacheable = 0b00, + WriteBack_ReadAlloc_WriteAlloc_Cacheable = 0b01, + WriteThrough_ReadAlloc_NoWriteAlloc_Cacheable = 0b10, + WriteBack_ReadAlloc_NoWriteAlloc_Cacheable = 0b11 + ], + + /// Inner cacheability attribute for memory associated with translation + /// table walks using TTBR0_EL1. + /// + /// 00 Normal memory, Inner Non-cacheable + /// + /// 01 Normal memory, Inner Write-Back Read-Allocate Write-Allocate + /// Cacheable + /// + /// 10 Normal memory, Inner Write-Through Read-Allocate No + /// Write-Allocate Cacheable + /// + /// 11 Normal memory, Inner Write-Back Read-Allocate No Write-Allocate + /// Cacheable + IRGN0 OFFSET(8) NUMBITS(2) [ + NonCacheable = 0b00, + WriteBack_ReadAlloc_WriteAlloc_Cacheable = 0b01, + WriteThrough_ReadAlloc_NoWriteAlloc_Cacheable = 0b10, + WriteBack_ReadAlloc_NoWriteAlloc_Cacheable = 0b11 + ], + + /// Translation table walk disable for translations using + /// TTBR0_EL1. This bit controls whether a translation table walk is + /// performed on a TLB miss, for an address that is translated using + /// TTBR0_EL1. The encoding of this bit is: + /// + /// 0 Perform translation table walks using TTBR0_EL1. + /// + /// 1 A TLB miss on an address that is translated using TTBR0_EL1 + /// generates a Translation fault. No translation table walk is + /// performed. + EPD0 OFFSET(7) NUMBITS(1) [ + EnableTTBR0Walks = 0, + DisableTTBR0Walks = 1 + ], + + /// The size offset of the memory region addressed by TTBR0_EL1. The + /// region size is 2^(64-T0SZ) bytes. + /// + /// The maximum and minimum possible values for T0SZ depend on the level + /// of translation table and the memory translation granule size, as + /// described in the AArch64 Virtual Memory System Architecture chapter. + T0SZ OFFSET(0) NUMBITS(6) [] + ] +} + +pub struct Reg; + +impl RegisterReadWrite for Reg { + sys_coproc_read_raw!(u64, "TCR_EL1"); + sys_coproc_write_raw!(u64, "TCR_EL1"); +} + +pub static TCR_EL1: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/ttbr0_el1.rs b/crate/aarch64/src/regs/ttbr0_el1.rs new file mode 100644 index 0000000..c111256 --- /dev/null +++ b/crate/aarch64/src/regs/ttbr0_el1.rs @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! Translation Table Base Register 0 - EL1 +//! +//! Holds the base address of the translation table for the initial lookup for +//! stage 1 of the translation of an address from the lower VA range in the +//! EL1&0 translation regime, and other information for this translation regime. + +use register::cpu::RegisterReadWrite; + +register_bitfields! {u64, + TTBR0_EL1 [ + /// An ASID for the translation table base address. The TCR_EL1.A1 field + /// selects either TTBR0_EL1.ASID or TTBR1_EL1.ASID. + /// + /// If the implementation has only 8 bits of ASID, then the upper 8 bits + /// of this field are RES 0. + ASID OFFSET(48) NUMBITS(16) [], + + /// Translation table base address + BADDR OFFSET(1) NUMBITS(47) [], + + /// Common not Private + CnP OFFSET(0) NUMBITS(1) [] + ] +} + +pub struct Reg; + +impl RegisterReadWrite for Reg { + sys_coproc_read_raw!(u64, "TTBR0_EL1"); + sys_coproc_write_raw!(u64, "TTBR0_EL1"); +} + +impl Reg { + #[inline] + pub fn set_baddr(&self, addr: u64) { + self.write(TTBR0_EL1::BADDR.val(addr >> 1)); + } +} + +pub static TTBR0_EL1: Reg = Reg {}; diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index e1b7d05..05f0710 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -1,3 +1,16 @@ +[[package]] +name = "aarch64" +version = "0.1.0" +dependencies = [ + "bare-metal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ux 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "atags" version = "0.1.0" @@ -243,6 +256,7 @@ dependencies = [ name = "ucore" version = "0.1.0" dependencies = [ + "aarch64 0.1.0", "atags 0.1.0", "bbl 0.1.0", "bcm2837 0.1.0", diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index e2bba97..c80b6ef 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -45,6 +45,7 @@ bbl = { path = "../crate/bbl" } [target.'cfg(target_arch = "aarch64")'.dependencies] cortex-a = "2.2.1" +aarch64 = { path = "../crate/aarch64" } atags = { path = "../crate/atags" } bcm2837 = { path = "../crate/bcm2837", features = ["use_generic_timer"] } diff --git a/kernel/src/arch/aarch64/memory.rs b/kernel/src/arch/aarch64/memory.rs index bf55354..32990d2 100644 --- a/kernel/src/arch/aarch64/memory.rs +++ b/kernel/src/arch/aarch64/memory.rs @@ -1,22 +1,78 @@ //! Memory initialization for aarch64. +use bit_allocator::BitAlloc; use ucore_memory::PAGE_SIZE; +use memory::{FRAME_ALLOCATOR, init_heap}; use super::atags::atags::Atags; -use super::super::HEAP_ALLOCATOR; +//use super::super::HEAP_ALLOCATOR; +use aarch64::{barrier, regs::*}; +use core::ops::Range; /// Memory initialization. pub fn init() { - let (start, end) = memory_map().expect("failed to find memory map"); + /*let (start, end) = memory_map().expect("failed to find memory map"); unsafe { HEAP_ALLOCATOR.lock().init(start, end - start); - } - info!("memory: init end"); + }*/ + + init_frame_allocator(); + init_heap(); + init_mmu(); } extern "C" { static _end: u8; } + +fn init_frame_allocator() { + let mut ba = FRAME_ALLOCATOR.lock(); + let (start, end) = memory_map().expect("failed to find memory map"); + ba.insert(to_range(start, end)); + info!("FrameAllocator init end"); + + fn to_range(start: usize, end: usize) -> Range { + let page_start = start / PAGE_SIZE; + let page_end = (end - 1) / PAGE_SIZE + 1; + page_start..page_end + } +} + +fn init_mmu() { + // device. + MAIR_EL1.write( + // Attribute 1 + MAIR_EL1::Attr1_HIGH::Device + + MAIR_EL1::Attr1_LOW_DEVICE::Device_nGnRE + // Attribute 0 + + MAIR_EL1::Attr0_HIGH::Memory_OuterWriteBack_NonTransient_ReadAlloc_WriteAlloc + + MAIR_EL1::Attr0_LOW_MEMORY::InnerWriteBack_NonTransient_ReadAlloc_WriteAlloc, + ); + // Configure various settings of stage 1 of the EL1 translation regime. + let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); + TCR_EL1.write( + TCR_EL1::TBI0::Ignored + + TCR_EL1::IPS.val(ips) + + TCR_EL1::TG0::KiB_4 // 4 KiB granule + + TCR_EL1::SH0::Inner + + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL1::EPD0::EnableTTBR0Walks + + TCR_EL1::T0SZ.val(34), // Start walks at level 2 + ); + + // Switch the MMU on. + // + // First, force all previous changes to be seen before the MMU is enabled. + unsafe { barrier::isb(barrier::SY); } + + // Enable the MMU and turn on data and instruction caching. + SCTLR_EL1.modify(SCTLR_EL1::M::Enable + SCTLR_EL1::C::Cacheable + SCTLR_EL1::I::Cacheable); + + // Force MMU init to complete before next instruction + unsafe { barrier::isb(barrier::SY); } +} + /// Returns the (start address, end address) of the available memory on this /// system if it can be determined. If it cannot, `None` is returned. /// @@ -33,3 +89,4 @@ pub fn memory_map() -> Option<(usize, usize)> { None } + diff --git a/kernel/src/arch/aarch64/paging.rs b/kernel/src/arch/aarch64/paging.rs index e9cc43e..b468f8b 100644 --- a/kernel/src/arch/aarch64/paging.rs +++ b/kernel/src/arch/aarch64/paging.rs @@ -1,218 +1,233 @@ //! Page table implementations for aarch64. - +use bit_allocator::{BitAlloc}; +// Depends on kernel +use memory::{active_table, alloc_frame, alloc_stack, dealloc_frame}; use ucore_memory::memory_set::*; +use ucore_memory::PAGE_SIZE; use ucore_memory::paging::*; - -type VirtAddr = usize; -type PhysAddr = usize; - -use alloc::alloc::{alloc, Layout}; -use memory::{active_table, alloc_frame, alloc_stack, dealloc_frame}; - -/// TODO -pub struct ActivePageTable { - // TODO +use aarch64::asm::{tlb_invalidate, ttbr0_el1_read, ttbr0_el1_write}; +use aarch64::{PhysAddr, VirtAddr}; +use aarch64::paging::{Mapper, PageTable as Aarch64PageTable, PageTableEntry, PageTableFlags as EF, RecursivePageTable}; +use aarch64::paging::{FrameAllocator, FrameDeallocator, Page, PageRange, PhysFrame as Frame, Size4KiB}; +use aarch64::{regs::*}; + +pub trait PageExt { + fn of_addr(address: usize) -> Self; + fn range_of(begin: usize, end: usize) -> PageRange; } -impl ActivePageTable { - /// TODO - pub unsafe fn new() -> Self { - unimplemented!() - } -} - -impl PageTable for ActivePageTable { - type Entry = PageEntry; - - fn map(&mut self, addr: VirtAddr, target: PhysAddr) -> &mut Self::Entry { - unimplemented!() - } - fn unmap(&mut self, addr: VirtAddr) { - unimplemented!() - } - - fn get_entry(&mut self, addr: VirtAddr) -> &mut Self::Entry { - unimplemented!() - } - - // For testing with mock - fn get_page_slice_mut<'a, 'b>(&'a mut self, addr: VirtAddr) -> &'b mut [u8] { - unimplemented!() +impl PageExt for Page { + fn of_addr(address: usize) -> Self { + Page::containing_address(VirtAddr::new(address as u64)) } - - fn read(&mut self, addr: VirtAddr) -> u8 { - unimplemented!() - } - - fn write(&mut self, addr: VirtAddr, data: u8) { - unimplemented!() + fn range_of(begin: usize, end: usize) -> PageRange { + Page::range(Page::of_addr(begin), Page::of_addr(end - 1) + 1) } } -/// TODO -pub struct PageEntry { - // TODO +pub trait FrameExt { + fn of_addr(address: usize) -> Self; } -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!() +impl FrameExt for Frame { + fn of_addr(address: usize) -> Self { + Frame::containing_address(PhysAddr::new(address as u64)) } +} - /// Will be set when written - fn dirty(&self) -> bool { - unimplemented!() - } +pub struct ActivePageTable(RecursivePageTable<'static>); - /// Will PageFault when try to write page where writable=0 - fn writable(&self) -> bool { - unimplemented!() - } +pub struct PageEntry(PageTableEntry); - /// Will PageFault when try to access page where present=0 - fn present(&self) -> bool { - unimplemented!() - } - - fn clear_accessed(&mut self) { - unimplemented!() - } +impl PageTable for ActivePageTable { + type Entry = PageEntry; - fn clear_dirty(&mut self) { - unimplemented!() + fn map(&mut self, addr: usize, target: usize) -> &mut PageEntry { + let flags = EF::PRESENT | EF::WRITE | EF::UXN; + self.0.map_to(Page::of_addr(addr), Frame::of_addr(target), flags, &mut FrameAllocatorForAarch64) + .unwrap().flush(); + self.get_entry(addr) } - fn set_writable(&mut self, value: bool) { - unimplemented!() + fn unmap(&mut self, addr: usize) { + let (frame, flush) = self.0.unmap(Page::of_addr(addr)).unwrap(); + flush.flush(); } - fn set_present(&mut self, value: bool) { - unimplemented!() + fn get_entry(&mut self, addr: usize) -> &mut PageEntry { + let entry_addr = ((addr >> 9) & 0o777_777_777_7770) | 0xffffff80_00000000; + unsafe { &mut *(entry_addr as *mut PageEntry) } } - fn target(&self) -> PhysAddr { - 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 set_target(&mut self, target: PhysAddr) { - unimplemented!() + fn read(&mut self, addr: usize) -> u8 { + unsafe { *(addr as *const u8) } } - // For Copy-on-write extension - fn writable_shared(&self) -> bool { - unimplemented!() + fn write(&mut self, addr: usize, data: u8) { + unsafe { *(addr as *mut u8) = data; } } +} - fn readonly_shared(&self) -> bool { - unimplemented!() +impl ActivePageTable { + pub unsafe fn new() -> Self { + ActivePageTable(RecursivePageTable::new(&mut *(0xffffffff_fffff000 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); } +} - fn set_shared(&mut self, writable: bool) { - unimplemented!() +impl Entry for PageEntry { + fn update(&mut self) { + tlb_invalidate(); } - fn clear_shared(&mut self) { - unimplemented!() - } + fn present(&self) -> bool { self.0.flags().contains(EF::PRESENT) } + fn accessed(&self) -> bool { self.0.flags().contains(EF::ACCESSED) } + fn writable(&self) -> bool { self.0.flags().contains(EF::WRITE) } + fn dirty(&self) -> bool { self.hw_dirty() && self.sw_dirty() } - // For Swap extension - fn swapped(&self) -> bool { - unimplemented!() + fn clear_accessed(&mut self) { self.as_flags().remove(EF::ACCESSED); } + fn clear_dirty(&mut self) + { + self.as_flags().remove(EF::DIRTY); + self.as_flags().insert(EF::RDONLY); } - - fn set_swapped(&mut self, value: bool) { - unimplemented!() + fn set_writable(&mut self, value: bool) + { + self.as_flags().set(EF::RDONLY, !value); + self.as_flags().set(EF::WRITE, value); } - - fn user(&self) -> bool { - unimplemented!() + fn set_present(&mut self, value: bool) { self.as_flags().set(EF::PRESENT, value); } + fn target(&self) -> usize { self.0.addr().as_u64() as usize } + fn set_target(&mut self, target: usize) { + let flags = self.0.flags(); + self.0.set_addr(PhysAddr::new(target as u64), flags); } - + fn writable_shared(&self) -> bool { self.0.flags().contains(EF::BIT_9) } + fn readonly_shared(&self) -> bool { self.0.flags().contains(EF::BIT_9) } + fn set_shared(&mut self, writable: bool) { + let flags = self.as_flags(); + flags.set(EF::BIT_8, writable); + flags.set(EF::BIT_9, writable); + } + fn clear_shared(&mut self) { self.as_flags().remove(EF::BIT_8 | EF::BIT_9); } + fn user(&self) -> bool { self.0.flags().contains(EF::USER_ACCESSIBLE) } + 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!() - } - - fn execute(&self) -> bool { - unimplemented!() - } - - fn set_execute(&mut self, value: bool) { - unimplemented!() + self.as_flags().set(EF::USER_ACCESSIBLE, value); + if value { + let mut addr = self as *const _ as usize; + for _ in 0..3 { + // Upper level entry + addr = ((addr >> 9) & 0o777_777_777_7770) | 0xffffff80_00000000; + // set USER_ACCESSIBLE + unsafe { (*(addr as *mut EF)).insert(EF::USER_ACCESSIBLE) }; + } + } } + fn execute(&self) -> bool { !self.0.flags().contains(EF::UXN) } + fn set_execute(&mut self, value: bool) { self.as_flags().set(EF::UXN, !value); } } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct MockFrame(PhysAddr); - -impl MockFrame { - pub fn of_addr(addr: PhysAddr) -> Self { - MockFrame(addr) - } - pub fn start_address(&self) -> PhysAddr { - unimplemented!() - } - pub fn p2_index(&self) -> usize { - unimplemented!() - } - pub fn p1_index(&self) -> usize { - unimplemented!() - } - pub fn number(&self) -> usize { - unimplemented!() +impl PageEntry { + fn read_only(&self) -> bool { self.0.flags().contains(EF::RDONLY) } + 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::(); - let ptr = alloc(layout); - let frame = MockFrame::of_addr(*ptr as usize); - InactivePageTable0 { p4_frame: frame } - } + let mut pt = Self::new_bare(); + pt.map_kernel(); + pt } 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[511].set_frame(frame.clone(), EF::PRESENT | EF::WRITE); + }); + InactivePageTable0 { p4_frame: frame } } fn edit(&mut self, f: impl FnOnce(&mut Self::Active)) { - unimplemented!() + active_table().with_temporary_map(&ttbr0_el1_read().0, |active_table, p4_table: &mut Aarch64PageTable| { + let backup = p4_table[0o777].clone(); + + // overwrite recursive mapping + p4_table[0o777].set_frame(self.p4_frame.clone(), EF::PRESENT | EF::WRITE); + tlb_invalidate(); + + // execute f in the new context + f(active_table); + + // restore recursive mapping to original p4 table + p4_table[0o777] = backup; + tlb_invalidate(); + }); } unsafe fn activate(&self) { - unimplemented!() + let old_frame = ttbr0_el1_read().0; + let new_frame = self.p4_frame.clone(); + debug!("switch table {:?} -> {:?}", old_frame, new_frame); + if old_frame != new_frame { + ttbr0_el1_write(new_frame); + } } unsafe fn with(&self, f: impl FnOnce()) { - unimplemented!() + let old_frame = ttbr0_el1_read().0; + let new_frame = self.p4_frame.clone(); + debug!("switch table {:?} -> {:?}", old_frame, new_frame); + if old_frame != new_frame { + ttbr0_el1_write(new_frame); + } + f(); + debug!("switch table {:?} -> {:?}", new_frame, old_frame); + if old_frame != new_frame { + ttbr0_el1_write(old_frame); + } } fn token(&self) -> usize { - 0 + self.p4_frame.start_address().as_u64() as usize // as CR3 } - fn alloc_frame() -> Option { + fn alloc_frame() -> Option { alloc_frame() } - fn dealloc_frame(target: PhysAddr) { + fn dealloc_frame(target: usize) { dealloc_frame(target) } @@ -220,3 +235,37 @@ impl InactivePageTable for InactivePageTable0 { alloc_stack() } } + +impl InactivePageTable0 { + fn map_kernel(&mut self) { + let mut table = unsafe { &mut *(0xffffffff_fffff000 as *mut Aarch64PageTable) }; + // Kernel at 0xffff_ff00_0000_0000 + // Kernel stack at 0x0000_57ac_0000_0000 (defined in bootloader crate) + let e0 = table[0].clone(); + self.edit(|_| { + table[0].set_addr(e0.addr(), e0.flags() & EF::GLOBAL); + //table[175].set_addr(estack.addr(), estack.flags() & EF::GLOBAL); + }); + } +} + +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 for FrameAllocatorForAarch64 { + fn alloc(&mut self) -> Option { + alloc_frame().map(|addr| Frame::of_addr(addr)) + } +} + +impl FrameDeallocator for FrameAllocatorForAarch64 { + fn dealloc(&mut self, frame: Frame) { + dealloc_frame(frame.start_address().as_u64() as usize); + } +} \ No newline at end of file diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 4474c9c..063b1d0 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -33,6 +33,8 @@ extern crate volatile; #[cfg(target_arch = "x86_64")] extern crate x86_64; extern crate xmas_elf; +#[cfg(target_arch = "aarch64")] +extern crate aarch64; use linked_list_allocator::LockedHeap; From 576ae1d9111050ae57f66bef39a74f272ed3dfc5 Mon Sep 17 00:00:00 2001 From: equation314 Date: Thu, 15 Nov 2018 19:12:02 +0800 Subject: [PATCH 02/39] add setup_page_table --- crate/aarch64/src/paging/page_table.rs | 16 ++++++--- kernel/src/arch/aarch64/memory.rs | 46 ++++++++++++++++++++++---- kernel/src/arch/aarch64/paging.rs | 32 ++++++++++++++++-- 3 files changed, 80 insertions(+), 14 deletions(-) diff --git a/crate/aarch64/src/paging/page_table.rs b/crate/aarch64/src/paging/page_table.rs index b074768..1525b17 100644 --- a/crate/aarch64/src/paging/page_table.rs +++ b/crate/aarch64/src/paging/page_table.rs @@ -104,11 +104,10 @@ bitflags! { const SHARED = 3 << 8; /* SH[1:0], inner shareable */ const BIT_8 = 1 << 8; const BIT_9 = 1 << 9; - /* - pub const ATTRIB_SH_NON_SHAREABLE: usize = 0x0 << 8; - pub const ATTRIB_SH_OUTER_SHAREABLE: usize = 0x2 << 8; - pub const ATTRIB_SH_INNER_SHAREABLE: usize = 0x3 << 8; - */ + + // pub const ATTRIB_SH_NON_SHAREABLE: usize = 0x0 << 8; + const OUTER_SHAREABLE = 0b10 << 8; + const INNER_SHAREABLE = 0b11 << 8; const ACCESSED = 1 << 10; /* AF, Access Flag */ const NONE_GLOBAL = 1 << 11; /* None Global */ @@ -148,6 +147,13 @@ impl PageTable { entry.set_unused(); } } + + /// Setup identity map: VirtPage at pagenumber -> PhysFrame at pagenumber + /// pn: pagenumber = addr>>12 in riscv32. + pub fn map_identity(&mut self, p4num: usize, flags: PageTableFlags) { + let entry = self.entries[p4num].clone(); + self.entries[p4num].set_addr(entry.addr(), flags); + } } impl Index for PageTable { diff --git a/kernel/src/arch/aarch64/memory.rs b/kernel/src/arch/aarch64/memory.rs index 32990d2..126cf57 100644 --- a/kernel/src/arch/aarch64/memory.rs +++ b/kernel/src/arch/aarch64/memory.rs @@ -5,7 +5,8 @@ use ucore_memory::PAGE_SIZE; use memory::{FRAME_ALLOCATOR, init_heap}; use super::atags::atags::Atags; //use super::super::HEAP_ALLOCATOR; -use aarch64::{barrier, regs::*}; +use aarch64::{barrier, regs::*, addr::*}; +use aarch64::paging::{FrameAllocator, FrameDeallocator, Page, PageRange, PhysFrame as Frame, Size4KiB}; use core::ops::Range; /// Memory initialization. @@ -15,9 +16,27 @@ pub fn init() { HEAP_ALLOCATOR.lock().init(start, end - start); }*/ + + + #[repr(align(4096))] + struct PageData([u8; PAGE_SIZE]); + static PAGE_TABLE_ROOT: PageData = PageData([0; PAGE_SIZE]); + + let frame = Frame::containing_address(PhysAddr::new(&PAGE_TABLE_ROOT as *const _ as u64)); + super::paging::setup_page_table(frame); + + init_mmu(); + init_frame_allocator(); init_heap(); - init_mmu(); + + let (start, end) = memory_map().expect("failed to find memory map"); + let mut v = vec![]; + for i in 0..(20 + (start & 0xf)) { + v.push(i); + println!("{:x?} {:x?}", &v[i] as * const _ as usize, v); + } + } extern "C" { @@ -26,19 +45,30 @@ extern "C" { fn init_frame_allocator() { - let mut ba = FRAME_ALLOCATOR.lock(); + use consts::{MEMORY_OFFSET}; let (start, end) = memory_map().expect("failed to find memory map"); + info!("{:x?} {:x?}", start, end); + + let mut ba = FRAME_ALLOCATOR.lock(); + use core::mem::size_of; + use ::memory::FrameAlloc; + info!("{:x?} {:x?}", &FRAME_ALLOCATOR as *const _ as usize, size_of::()); + use consts::{KERNEL_HEAP_OFFSET, KERNEL_HEAP_SIZE}; + info!("{:x?} {:x?}", KERNEL_HEAP_OFFSET + KERNEL_HEAP_SIZE, end); ba.insert(to_range(start, end)); info!("FrameAllocator init end"); fn to_range(start: usize, end: usize) -> Range { - let page_start = start / PAGE_SIZE; - let page_end = (end - 1) / PAGE_SIZE + 1; + let page_start = (start - MEMORY_OFFSET) / PAGE_SIZE; + let page_end = (end - MEMORY_OFFSET - 1) / PAGE_SIZE + 1; + // info!("{:x?} {:x?}", page_start, page_end); page_start..page_end } } fn init_mmu() { + info!("init_mmu"); + // device. MAIR_EL1.write( // Attribute 1 @@ -50,6 +80,7 @@ fn init_mmu() { ); // Configure various settings of stage 1 of the EL1 translation regime. let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); + info!("{:x?}", ips); TCR_EL1.write( TCR_EL1::TBI0::Ignored + TCR_EL1::IPS.val(ips) @@ -58,7 +89,7 @@ fn init_mmu() { + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + TCR_EL1::EPD0::EnableTTBR0Walks - + TCR_EL1::T0SZ.val(34), // Start walks at level 2 + + TCR_EL1::T0SZ.val(16), // Start walks at level 2 ); // Switch the MMU on. @@ -66,11 +97,14 @@ fn init_mmu() { // First, force all previous changes to be seen before the MMU is enabled. unsafe { barrier::isb(barrier::SY); } + info!("{:x?}", TCR_EL1.get()); // Enable the MMU and turn on data and instruction caching. SCTLR_EL1.modify(SCTLR_EL1::M::Enable + SCTLR_EL1::C::Cacheable + SCTLR_EL1::I::Cacheable); // Force MMU init to complete before next instruction unsafe { barrier::isb(barrier::SY); } + + info!("mmu enabled!"); } /// Returns the (start address, end address) of the available memory on this diff --git a/kernel/src/arch/aarch64/paging.rs b/kernel/src/arch/aarch64/paging.rs index b468f8b..603346d 100644 --- a/kernel/src/arch/aarch64/paging.rs +++ b/kernel/src/arch/aarch64/paging.rs @@ -11,6 +11,32 @@ use aarch64::paging::{Mapper, PageTable as Aarch64PageTable, PageTableEntry, Pag use aarch64::paging::{FrameAllocator, FrameDeallocator, Page, PageRange, PhysFrame as Frame, Size4KiB}; use aarch64::{regs::*}; +// need 1 page +pub fn setup_page_table(frame: Frame) { + let p4 = unsafe { &mut *(frame.start_address().as_u64() as *mut Aarch64PageTable) }; + p4.zero(); + + + // p4.set_recursive(RECURSIVE_PAGE_PML4, frame.clone()); + + // Set kernel identity map + // 0x10000000 ~ 1K area + p4.map_identity(0o777, EF::PRESENT | EF::PXN | EF::UXN); + + // 0x80000000 ~ 8K area + p4.map_identity(0, EF::PRESENT); + // p2.map_identity(KERNEL_PML4, EF::PRESENT | EF::READABLE | EF::WRITABLE); + // p2.map_identity(KERNEL_PML4 + 1, EF::VALID | EF::READABLE | EF::WRITABLE | EF::EXECUTABLE); + + // use super::riscv::register::satp; + // unsafe { satp::set(satp::Mode::Sv32, 0, frame); } + // sfence_vma_all(); + + ttbr0_el1_write(frame); + tlb_invalidate(); + info!("setup init page table end"); +} + pub trait PageExt { fn of_addr(address: usize) -> Self; fn range_of(begin: usize, end: usize) -> PageRange; @@ -75,7 +101,7 @@ impl PageTable for ActivePageTable { impl ActivePageTable { pub unsafe fn new() -> Self { - ActivePageTable(RecursivePageTable::new(&mut *(0xffffffff_fffff000 as *mut _)).unwrap()) + ActivePageTable(RecursivePageTable::new(&mut *(0xffff_ffff_ffff_f000 as *mut _)).unwrap()) } fn with_temporary_map(&mut self, frame: &Frame, f: impl FnOnce(&mut ActivePageTable, &mut Aarch64PageTable)) { // Create a temporary page @@ -238,7 +264,7 @@ impl InactivePageTable for InactivePageTable0 { impl InactivePageTable0 { fn map_kernel(&mut self) { - let mut table = unsafe { &mut *(0xffffffff_fffff000 as *mut Aarch64PageTable) }; + let mut table = unsafe { &mut *(0xffff_ffff_ffff_f000 as *mut Aarch64PageTable) }; // Kernel at 0xffff_ff00_0000_0000 // Kernel stack at 0x0000_57ac_0000_0000 (defined in bootloader crate) let e0 = table[0].clone(); @@ -268,4 +294,4 @@ impl FrameDeallocator for FrameAllocatorForAarch64 { fn dealloc(&mut self, frame: Frame) { dealloc_frame(frame.start_address().as_u64() as usize); } -} \ No newline at end of file +} From 9e8124abbbf4dace6e2eef451db74793c536fdc2 Mon Sep 17 00:00:00 2001 From: equation314 Date: Tue, 20 Nov 2018 16:46:16 +0800 Subject: [PATCH 03/39] aarch64/mmu: mmu enabled --- crate/aarch64/Cargo.lock | 68 ------ crate/aarch64/src/asm.rs | 27 ++- crate/aarch64/src/paging/mod.rs | 57 ----- crate/aarch64/src/paging/page_table.rs | 9 +- crate/aarch64/src/paging/recursive.rs | 212 +------------------ kernel/Cargo.lock | 17 ++ kernel/Cargo.toml | 1 + kernel/src/arch/aarch64/boot/boot.S | 2 + kernel/src/arch/aarch64/interrupt/handler.rs | 5 +- kernel/src/arch/aarch64/memory.rs | 55 ++--- kernel/src/arch/aarch64/paging.rs | 163 +++++++++++--- kernel/src/consts.rs | 17 +- kernel/src/lib.rs | 4 + kernel/src/memory.rs | 6 +- 14 files changed, 215 insertions(+), 428 deletions(-) delete mode 100644 crate/aarch64/Cargo.lock diff --git a/crate/aarch64/Cargo.lock b/crate/aarch64/Cargo.lock deleted file mode 100644 index 2d0a329..0000000 --- a/crate/aarch64/Cargo.lock +++ /dev/null @@ -1,68 +0,0 @@ -[[package]] -name = "aarch64" -version = "0.1.0" -dependencies = [ - "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-a 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ux 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bit_field" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitflags" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cortex-a" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "os_bootinfo" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "register" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "tock-registers 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "tock-registers" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "usize_conversions" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ux" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" -"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum cortex-a 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2b187d0d728b4a99ba1d79f9671b976bcdd71a8a2c719585218fd2dc14a4d08c" -"checksum os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "66481dbeb5e773e7bd85b63cd6042c30786f834338288c5ec4f3742673db360a" -"checksum register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e10f31b6d2299e5620986ad9fcdd66463e125ad72af4f403f9aedf7592d5ccdb" -"checksum tock-registers 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a385d94f3f62e60445a0adb9ff8d9621faa272234530d4c0f848ec98f88e316" -"checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" -"checksum ux 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53d8df5dd8d07fedccd202de1887d94481fadaea3db70479f459e8163a1fab41" diff --git a/crate/aarch64/src/asm.rs b/crate/aarch64/src/asm.rs index d97fa78..17e6c29 100644 --- a/crate/aarch64/src/asm.rs +++ b/crate/aarch64/src/asm.rs @@ -4,12 +4,13 @@ use regs::*; #[inline(always)] pub fn tlb_invalidate() { - unsafe{ - asm!("dsb ishst - tlbi vmalle1is - dsb ish - tlbi vmalle1is - isb"); + unsafe { + asm!( + "dsb ishst + tlbi vmalle1is + dsb ish + isb" + ); } } @@ -63,6 +64,15 @@ pub unsafe fn get_ttbr1() -> usize { ttbr0 } +#[inline(always)] +pub fn address_translate(vaddr: usize) -> usize { + let paddr: usize; + unsafe { + asm!("at S1E1R, $1; mrs $0, par_el1" : "=r"(paddr) : "r"(vaddr)); + } + paddr +} + /// Returns the SPSel value. #[inline(always)] pub fn sp_sel() -> u8 { @@ -94,7 +104,6 @@ pub fn wfi() { } } - /// The classic no-op #[inline] pub fn nop() { @@ -142,7 +151,7 @@ pub fn eret() -> ! { bitflags! { /// Controls cache settings for the level 4 page table. pub struct ttbr0_el1_Flags: u64 { - + const COMMON_NOT_PRIVATE = 1 << 0; } } @@ -150,7 +159,7 @@ bitflags! { pub fn ttbr0_el1_read() -> (PhysFrame, ttbr0_el1_Flags) { let value = TTBR0_EL1.get(); let flags = ttbr0_el1_Flags::from_bits_truncate(value); - let addr = PhysAddr::new(value & 0x_000f_ffff_ffff_f000); + let addr = PhysAddr::new(value & 0x_0000_ffff_ffff_f000); let frame = PhysFrame::containing_address(addr); (frame, flags) } diff --git a/crate/aarch64/src/paging/mod.rs b/crate/aarch64/src/paging/mod.rs index 56073b2..ebc00dc 100644 --- a/crate/aarch64/src/paging/mod.rs +++ b/crate/aarch64/src/paging/mod.rs @@ -34,16 +34,6 @@ pub trait NotGiantPageSize: PageSize {} #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum Size4KiB {} -/// A “huge” 2MiB page. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum Size2MiB {} - -/// A “giant” 1GiB page. -/// -/// (Only available on newer x86_64 CPUs.) -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum Size1GiB {} - impl PageSize for Size4KiB { const SIZE: u64 = 4096; const SIZE_AS_DEBUG_STR: &'static str = "4KiB"; @@ -51,18 +41,6 @@ impl PageSize for Size4KiB { impl NotGiantPageSize for Size4KiB {} -impl PageSize for Size2MiB { - const SIZE: u64 = Size4KiB::SIZE * 512; - const SIZE_AS_DEBUG_STR: &'static str = "2MiB"; -} - -impl NotGiantPageSize for Size2MiB {} - -impl PageSize for Size1GiB { - const SIZE: u64 = Size2MiB::SIZE * 512; - const SIZE_AS_DEBUG_STR: &'static str = "1GiB"; -} - /// A virtual memory page. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[repr(C)] @@ -128,31 +106,6 @@ impl Page { } } -impl Page { - /// Returns the 1GiB memory page with the specified page table indices. - pub fn from_page_table_indices_1gib(p4_index: u9, p3_index: u9) -> Self { - use bit_field::BitField; - - let mut addr = 0; - addr.set_bits(39..48, u64::from(p4_index)); - addr.set_bits(30..39, u64::from(p3_index)); - Page::containing_address(VirtAddr::new(addr)) - } -} - -impl Page { - /// Returns the 2MiB memory page with the specified page table indices. - pub fn from_page_table_indices_2mib(p4_index: u9, p3_index: u9, p2_index: u9) -> Self { - use bit_field::BitField; - - let mut addr = 0; - addr.set_bits(39..48, u64::from(p4_index)); - addr.set_bits(30..39, u64::from(p3_index)); - addr.set_bits(21..30, u64::from(p2_index)); - Page::containing_address(VirtAddr::new(addr)) - } -} - impl Page { /// Returns the 4KiB memory page with the specified page table indices. pub fn from_page_table_indices(p4_index: u9, p3_index: u9, p2_index: u9, p1_index: u9) -> Self { @@ -246,16 +199,6 @@ impl Iterator for PageRange { } } -impl PageRange { - /// Converts the range of 2MiB pages to a range of 4KiB pages. - pub fn as_4kib_page_range(self) -> PageRange { - PageRange { - start: Page::containing_address(self.start.start_address()), - end: Page::containing_address(self.end.start_address()), - } - } -} - impl fmt::Debug for PageRange { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("PageRange") diff --git a/crate/aarch64/src/paging/page_table.rs b/crate/aarch64/src/paging/page_table.rs index 1525b17..c06123e 100644 --- a/crate/aarch64/src/paging/page_table.rs +++ b/crate/aarch64/src/paging/page_table.rs @@ -21,7 +21,7 @@ pub enum FrameError { #[derive(Clone)] #[repr(transparent)] pub struct PageTableEntry { - entry: u64, + pub entry: u64, } impl PageTableEntry { @@ -42,7 +42,7 @@ impl PageTableEntry { /// Returns the physical address mapped by this entry, might be zero. pub fn addr(&self) -> PhysAddr { - PhysAddr::new(self.entry & 0x000fffff_fffff000) + PhysAddr::new(self.entry & 0x0000_ffff_ffff_f000) } /// Returns the physical frame mapped by this entry. @@ -83,6 +83,7 @@ impl PageTableEntry { impl fmt::Debug for PageTableEntry { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut f = f.debug_struct("PageTableEntry"); + f.field("value", &self.entry); f.field("addr", &self.addr()); f.field("flags", &self.flags()); f.finish() @@ -94,9 +95,11 @@ bitflags! { pub struct PageTableFlags: u64 { const ALL = 0xffffffff_ffffffff; const TYPE_MASK = 3 << 0; - const TYPE_FAULT = 0 << 0; + // const TYPE_FAULT = 0 << 0; const TYPE_PAGE = 3 << 0; const TABLE_BIT = 1 << 1; + // const BLOCK_BIT = 0 << 1; + const PAGE_BIT = 1 << 1; const PRESENT = 1 << 0; const USER_ACCESSIBLE = 1 << 6; /* AP[1] */ diff --git a/crate/aarch64/src/paging/recursive.rs b/crate/aarch64/src/paging/recursive.rs index 0cbc6d0..d735216 100644 --- a/crate/aarch64/src/paging/recursive.rs +++ b/crate/aarch64/src/paging/recursive.rs @@ -4,7 +4,7 @@ use asm::tlb_invalidate; use paging::{ frame_alloc::FrameAllocator, page_table::{FrameError, PageTable, PageTableEntry, PageTableFlags}, - NotGiantPageSize, Page, PageSize, PhysFrame, Size1GiB, Size2MiB, Size4KiB, + NotGiantPageSize, Page, PageSize, PhysFrame, Size4KiB, }; use paging::page_table::PageTableFlags as Flags; use asm::ttbr0_el1_read; @@ -217,7 +217,7 @@ impl<'a> RecursivePageTable<'a> { if entry.is_unused() { if let Some(frame) = allocator.alloc() { - entry.set_frame(frame, Flags::PRESENT | Flags::WRITE); + entry.set_frame(frame, Flags::PRESENT | Flags::WRITE | Flags::ACCESSED | Flags::PAGE_BIT); created = true; } else { return Err(MapToError::FrameAllocationFailed); @@ -241,214 +241,6 @@ impl<'a> RecursivePageTable<'a> { } } -impl<'a> Mapper for RecursivePageTable<'a> { - fn map_to( - &mut self, - page: Page, - frame: PhysFrame, - flags: PageTableFlags, - allocator: &mut A, - ) -> Result, MapToError> - where - A: FrameAllocator, - { - let p4 = &mut self.p4; - - let p3_page = p3_page(page, self.recursive_index); - let p3 = unsafe { Self::create_next_table(&mut p4[page.p4_index()], p3_page, allocator)? }; - - if !p3[page.p3_index()].is_unused() { - return Err(MapToError::PageAlreadyMapped); - } - p3[page.p3_index()].set_addr(frame.start_address(), flags | Flags::HUGE_PAGE); - - Ok(MapperFlush::new(page)) - } - - fn unmap( - &mut self, - page: Page, - ) -> Result<(PhysFrame, MapperFlush), UnmapError> { - let p4 = &mut self.p4; - let p4_entry = &p4[page.p4_index()]; - - p4_entry.frame().map_err(|err| match err { - FrameError::FrameNotPresent => UnmapError::PageNotMapped, - FrameError::HugeFrame => UnmapError::ParentEntryHugePage, - })?; - - let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; - let p3_entry = &mut p3[page.p3_index()]; - let flags = p3_entry.flags(); - - if !flags.contains(PageTableFlags::PRESENT) { - return Err(UnmapError::PageNotMapped); - } - if !flags.contains(PageTableFlags::HUGE_PAGE) { - return Err(UnmapError::ParentEntryHugePage); - } - - let frame = PhysFrame::from_start_address(p3_entry.addr()) - .map_err(|()| UnmapError::InvalidFrameAddress(p3_entry.addr()))?; - - p3_entry.set_unused(); - Ok((frame, MapperFlush::new(page))) - } - - fn update_flags( - &mut self, - page: Page, - flags: PageTableFlags, - ) -> Result, FlagUpdateError> { - let p4 = &mut self.p4; - - if p4[page.p4_index()].is_unused() { - return Err(FlagUpdateError::PageNotMapped); - } - - let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; - - if p3[page.p3_index()].is_unused() { - return Err(FlagUpdateError::PageNotMapped); - } - p3[page.p3_index()].set_flags(flags | Flags::HUGE_PAGE); - - Ok(MapperFlush::new(page)) - } - - fn translate_page(&self, page: Page) -> Option> { - let p4 = &self.p4; - - if p4[page.p4_index()].is_unused() { - return None; - } - - let p3 = unsafe { &*(p3_ptr(page, self.recursive_index)) }; - let p3_entry = &p3[page.p3_index()]; - - if p3_entry.is_unused() { - return None; - } - - PhysFrame::from_start_address(p3_entry.addr()).ok() - } -} - -impl<'a> Mapper for RecursivePageTable<'a> { - fn map_to( - &mut self, - page: Page, - frame: PhysFrame, - flags: PageTableFlags, - allocator: &mut A, - ) -> Result, MapToError> - where - A: FrameAllocator, - { - let p4 = &mut self.p4; - - let p3_page = p3_page(page, self.recursive_index); - let p3 = unsafe { Self::create_next_table(&mut p4[page.p4_index()], p3_page, allocator)? }; - - let p2_page = p2_page(page, self.recursive_index); - let p2 = unsafe { Self::create_next_table(&mut p3[page.p3_index()], p2_page, allocator)? }; - - if !p2[page.p2_index()].is_unused() { - return Err(MapToError::PageAlreadyMapped); - } - p2[page.p2_index()].set_addr(frame.start_address(), flags | Flags::HUGE_PAGE); - - Ok(MapperFlush::new(page)) - } - - fn unmap( - &mut self, - page: Page, - ) -> Result<(PhysFrame, MapperFlush), UnmapError> { - let p4 = &mut self.p4; - let p4_entry = &p4[page.p4_index()]; - p4_entry.frame().map_err(|err| match err { - FrameError::FrameNotPresent => UnmapError::PageNotMapped, - FrameError::HugeFrame => UnmapError::ParentEntryHugePage, - })?; - - let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; - let p3_entry = &p3[page.p3_index()]; - p3_entry.frame().map_err(|err| match err { - FrameError::FrameNotPresent => UnmapError::PageNotMapped, - FrameError::HugeFrame => UnmapError::ParentEntryHugePage, - })?; - - let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; - let p2_entry = &mut p2[page.p2_index()]; - let flags = p2_entry.flags(); - - if !flags.contains(PageTableFlags::PRESENT) { - return Err(UnmapError::PageNotMapped); - } - if !flags.contains(PageTableFlags::HUGE_PAGE) { - return Err(UnmapError::ParentEntryHugePage); - } - - let frame = PhysFrame::from_start_address(p2_entry.addr()) - .map_err(|()| UnmapError::InvalidFrameAddress(p2_entry.addr()))?; - - p2_entry.set_unused(); - Ok((frame, MapperFlush::new(page))) - } - - fn update_flags( - &mut self, - page: Page, - flags: PageTableFlags, - ) -> Result, FlagUpdateError> { - let p4 = &mut self.p4; - - if p4[page.p4_index()].is_unused() { - return Err(FlagUpdateError::PageNotMapped); - } - - let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; - - if p3[page.p3_index()].is_unused() { - return Err(FlagUpdateError::PageNotMapped); - } - - let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; - - if p2[page.p2_index()].is_unused() { - return Err(FlagUpdateError::PageNotMapped); - } - - p2[page.p2_index()].set_flags(flags | Flags::HUGE_PAGE); - - Ok(MapperFlush::new(page)) - } - - fn translate_page(&self, page: Page) -> Option> { - let p4 = &self.p4; - - if p4[page.p4_index()].is_unused() { - return None; - } - - let p3 = unsafe { &*(p3_ptr(page, self.recursive_index)) }; - let p3_entry = &p3[page.p3_index()]; - - if p3_entry.is_unused() { - return None; - } - - let p2 = unsafe { &*(p2_ptr(page, self.recursive_index)) }; - let p2_entry = &p2[page.p2_index()]; - - if p2_entry.is_unused() { - return None; - } - - PhysFrame::from_start_address(p2_entry.addr()).ok() - } -} impl<'a> Mapper for RecursivePageTable<'a> { fn map_to( diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index 05f0710..7795f1f 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -5,6 +5,7 @@ dependencies = [ "bare-metal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -176,6 +177,14 @@ name = "redox_syscall" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "register" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "register" version = "0.2.1" @@ -238,6 +247,11 @@ dependencies = [ "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tock-registers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "tock-registers" version = "0.2.0" @@ -271,6 +285,7 @@ dependencies = [ "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "once 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "riscv 0.3.0", "simple-filesystem 0.0.1 (git+https://github.com/wangrunji0408/SimpleFileSystem-Rust)", "spin 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -384,6 +399,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07" "checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" +"checksum register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e985243ba7e1c336b62444ef2a10d7bd87cf41a222285ae3de605c859006479" "checksum register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e10f31b6d2299e5620986ad9fcdd66463e125ad72af4f403f9aedf7592d5ccdb" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum simple-filesystem 0.0.1 (git+https://github.com/wangrunji0408/SimpleFileSystem-Rust)" = "" @@ -391,6 +407,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum spin 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "37b5646825922b96b5d7d676b5bb3458a54498e96ed7b0ce09dc43a07038fea4" "checksum static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +"checksum tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2acc33f980e23cee18d234a32d0637fbc1ea55e13ab04012fa857b899fa1b7a9" "checksum tock-registers 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a385d94f3f62e60445a0adb9ff8d9621faa272234530d4c0f848ec98f88e316" "checksum uart_16550 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "269f953d8de3226f7c065c589c7b4a3e83d10a419c7c3b5e2e0f197e6acc966e" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index c80b6ef..06ea088 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -32,6 +32,7 @@ bit-allocator = { path = "../crate/bit-allocator" } ucore-memory = { path = "../crate/memory" } ucore-process = { path = "../crate/process" } simple-filesystem = { git = "https://github.com/wangrunji0408/SimpleFileSystem-Rust" } +register="0.1.0" [target.'cfg(target_arch = "x86_64")'.dependencies] bootloader = "0.3" diff --git a/kernel/src/arch/aarch64/boot/boot.S b/kernel/src/arch/aarch64/boot/boot.S index b8a142e..57a4239 100644 --- a/kernel/src/arch/aarch64/boot/boot.S +++ b/kernel/src/arch/aarch64/boot/boot.S @@ -17,6 +17,8 @@ halt: setup: # store the desired EL1 stack pointer in x1 adr x1, _start + # FIXME + lsl x1, x1, #2 # use SP_ELx for Exception level ELx msr SPsel, #1 diff --git a/kernel/src/arch/aarch64/interrupt/handler.rs b/kernel/src/arch/aarch64/interrupt/handler.rs index 85d1faf..787cbf5 100644 --- a/kernel/src/arch/aarch64/interrupt/handler.rs +++ b/kernel/src/arch/aarch64/interrupt/handler.rs @@ -38,10 +38,11 @@ pub struct Info { /// the trap frame for the exception. #[no_mangle] pub extern "C" fn rust_trap(info: Info, esr: u32, tf: &mut TrapFrame) { - let syndrome = Syndrome::from(esr); - trace!("Interrupt: {:?} from: {:?}", syndrome, info); + trace!("Interrupt: {:?}, ELR: {:#x?}", info, tf.elr); match info.kind { Kind::Synchronous => { + let syndrome = Syndrome::from(esr); + trace!("ESR: {:#x?}, Syndrome: {:?}", esr, syndrome); // syndrome is only valid with sync match syndrome { Syndrome::Brk(brk) => handle_break(brk, tf), diff --git a/kernel/src/arch/aarch64/memory.rs b/kernel/src/arch/aarch64/memory.rs index 126cf57..3e7f6d8 100644 --- a/kernel/src/arch/aarch64/memory.rs +++ b/kernel/src/arch/aarch64/memory.rs @@ -11,64 +11,55 @@ use core::ops::Range; /// Memory initialization. pub fn init() { - /*let (start, end) = memory_map().expect("failed to find memory map"); - unsafe { - HEAP_ALLOCATOR.lock().init(start, end - start); - }*/ - - - #[repr(align(4096))] struct PageData([u8; PAGE_SIZE]); - static PAGE_TABLE_ROOT: PageData = PageData([0; PAGE_SIZE]); + static PAGE_TABLE_LVL4: PageData = PageData([0; PAGE_SIZE]); + static PAGE_TABLE_LVL3: PageData = PageData([0; PAGE_SIZE]); + static PAGE_TABLE_LVL2: PageData = PageData([0; PAGE_SIZE]); - let frame = Frame::containing_address(PhysAddr::new(&PAGE_TABLE_ROOT as *const _ as u64)); - super::paging::setup_page_table(frame); + let frame_lvl4 = Frame::containing_address(PhysAddr::new(&PAGE_TABLE_LVL4 as *const _ as u64)); + let frame_lvl3 = Frame::containing_address(PhysAddr::new(&PAGE_TABLE_LVL3 as *const _ as u64)); + let frame_lvl2 = Frame::containing_address(PhysAddr::new(&PAGE_TABLE_LVL2 as *const _ as u64)); + super::paging::setup_page_table(frame_lvl4, frame_lvl3, frame_lvl2); init_mmu(); - init_frame_allocator(); init_heap(); - let (start, end) = memory_map().expect("failed to find memory map"); - let mut v = vec![]; - for i in 0..(20 + (start & 0xf)) { - v.push(i); - println!("{:x?} {:x?}", &v[i] as * const _ as usize, v); - } - + info!("memory: init end"); } extern "C" { static _end: u8; } - fn init_frame_allocator() { + use bit_allocator::BitAlloc; + use core::ops::Range; use consts::{MEMORY_OFFSET}; - let (start, end) = memory_map().expect("failed to find memory map"); - info!("{:x?} {:x?}", start, end); + let (start, end) = memory_map().expect("failed to find memory map"); let mut ba = FRAME_ALLOCATOR.lock(); - use core::mem::size_of; - use ::memory::FrameAlloc; - info!("{:x?} {:x?}", &FRAME_ALLOCATOR as *const _ as usize, size_of::()); - use consts::{KERNEL_HEAP_OFFSET, KERNEL_HEAP_SIZE}; - info!("{:x?} {:x?}", KERNEL_HEAP_OFFSET + KERNEL_HEAP_SIZE, end); ba.insert(to_range(start, end)); info!("FrameAllocator init end"); + /* + * @param: + * start: start address + * end: end address + * @brief: + * transform the memory address to the page number + * @retval: + * the page number range from start address to end address + */ fn to_range(start: usize, end: usize) -> Range { let page_start = (start - MEMORY_OFFSET) / PAGE_SIZE; let page_end = (end - MEMORY_OFFSET - 1) / PAGE_SIZE + 1; - // info!("{:x?} {:x?}", page_start, page_end); page_start..page_end } } fn init_mmu() { - info!("init_mmu"); - // device. MAIR_EL1.write( // Attribute 1 @@ -80,7 +71,6 @@ fn init_mmu() { ); // Configure various settings of stage 1 of the EL1 translation regime. let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); - info!("{:x?}", ips); TCR_EL1.write( TCR_EL1::TBI0::Ignored + TCR_EL1::IPS.val(ips) @@ -97,21 +87,20 @@ fn init_mmu() { // First, force all previous changes to be seen before the MMU is enabled. unsafe { barrier::isb(barrier::SY); } - info!("{:x?}", TCR_EL1.get()); // Enable the MMU and turn on data and instruction caching. SCTLR_EL1.modify(SCTLR_EL1::M::Enable + SCTLR_EL1::C::Cacheable + SCTLR_EL1::I::Cacheable); // Force MMU init to complete before next instruction unsafe { barrier::isb(barrier::SY); } - info!("mmu enabled!"); + info!("mmu enabled"); } /// Returns the (start address, end address) of the available memory on this /// system if it can be determined. If it cannot, `None` is returned. /// /// This function is expected to return `Some` under all normal cirumstances. -pub fn memory_map() -> Option<(usize, usize)> { +fn memory_map() -> Option<(usize, usize)> { let binary_end = unsafe { (&_end as *const u8) as u32 }; let mut atags: Atags = Atags::get(); diff --git a/kernel/src/arch/aarch64/paging.rs b/kernel/src/arch/aarch64/paging.rs index 603346d..9696db9 100644 --- a/kernel/src/arch/aarch64/paging.rs +++ b/kernel/src/arch/aarch64/paging.rs @@ -1,6 +1,6 @@ //! Page table implementations for aarch64. -use bit_allocator::{BitAlloc}; // Depends on kernel +use consts::{KERNEL_PML4, RECURSIVE_INDEX}; use memory::{active_table, alloc_frame, alloc_stack, dealloc_frame}; use ucore_memory::memory_set::*; use ucore_memory::PAGE_SIZE; @@ -11,29 +11,121 @@ use aarch64::paging::{Mapper, PageTable as Aarch64PageTable, PageTableEntry, Pag use aarch64::paging::{FrameAllocator, FrameDeallocator, Page, PageRange, PhysFrame as Frame, Size4KiB}; use aarch64::{regs::*}; -// need 1 page -pub fn setup_page_table(frame: Frame) { - let p4 = unsafe { &mut *(frame.start_address().as_u64() as *mut Aarch64PageTable) }; - p4.zero(); - - - // p4.set_recursive(RECURSIVE_PAGE_PML4, frame.clone()); +register_bitfields! {u64, + // AArch64 Reference Manual page 2150 + STAGE1_DESCRIPTOR [ + /// Execute-never + XN OFFSET(54) NUMBITS(1) [ + False = 0, + True = 1 + ], + + /// Various address fields, depending on use case + LVL4_OUTPUT_ADDR_4KiB OFFSET(39) NUMBITS(9) [], // [47:39] + LVL3_OUTPUT_ADDR_4KiB OFFSET(30) NUMBITS(18) [], // [47:30] + LVL2_OUTPUT_ADDR_4KiB OFFSET(21) NUMBITS(27) [], // [47:21] + NEXT_LVL_TABLE_ADDR_4KiB OFFSET(12) NUMBITS(36) [], // [47:12] + + /// Access flag + AF OFFSET(10) NUMBITS(1) [ + False = 0, + True = 1 + ], + + /// Shareability field + SH OFFSET(8) NUMBITS(2) [ + OuterShareable = 0b10, + InnerShareable = 0b11 + ], + + /// Access Permissions + AP OFFSET(6) NUMBITS(2) [ + RW_EL1 = 0b00, + RW_EL1_EL0 = 0b01, + RO_EL1 = 0b10, + RO_EL1_EL0 = 0b11 + ], + + /// Memory attributes index into the MAIR_EL1 register + AttrIndx OFFSET(2) NUMBITS(3) [], + + TYPE OFFSET(1) NUMBITS(1) [ + Block = 0, + Table = 1 + ], + + VALID OFFSET(0) NUMBITS(1) [ + False = 0, + True = 1 + ] + ] +} - // Set kernel identity map - // 0x10000000 ~ 1K area - p4.map_identity(0o777, EF::PRESENT | EF::PXN | EF::UXN); +// need 3 page +pub fn setup_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(); - // 0x80000000 ~ 8K area - p4.map_identity(0, EF::PRESENT); - // p2.map_identity(KERNEL_PML4, EF::PRESENT | EF::READABLE | EF::WRITABLE); - // p2.map_identity(KERNEL_PML4 + 1, EF::VALID | EF::READABLE | EF::WRITABLE | EF::EXECUTABLE); + mod mair { + pub const NORMAL: u64 = 0; + pub const DEVICE: u64 = 1; + } - // use super::riscv::register::satp; - // unsafe { satp::set(satp::Mode::Sv32, 0, frame); } - // sfence_vma_all(); + // Fill the rest of the LVL2 (2MiB) entries as block + // descriptors. Differentiate between normal and device mem. + const MMIO_BASE: u64 = 0x3F000000; + let mmio_base: u64 = MMIO_BASE >> 21; + let mut common = STAGE1_DESCRIPTOR::VALID::True + + STAGE1_DESCRIPTOR::TYPE::Block + + STAGE1_DESCRIPTOR::AP::RW_EL1 + + STAGE1_DESCRIPTOR::AF::True; + // + STAGE1_DESCRIPTOR::XN::True; + + for i in 0..512 { + let j: u64 = i as u64; + + let mem_attr = if j >= mmio_base { + STAGE1_DESCRIPTOR::SH::OuterShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::DEVICE) + } else { + STAGE1_DESCRIPTOR::SH::InnerShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL) + }; + + p2[i].entry = (common + mem_attr + STAGE1_DESCRIPTOR::LVL2_OUTPUT_ADDR_4KiB.val(j)).value; + } - ttbr0_el1_write(frame); + common = common + STAGE1_DESCRIPTOR::SH::InnerShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL); + + p3[0].entry = (common + STAGE1_DESCRIPTOR::TYPE::Table + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(frame_lvl2.start_address().as_u64() >> 12)).value; + p3[1].entry = (common + STAGE1_DESCRIPTOR::LVL3_OUTPUT_ADDR_4KiB.val(1)).value; + p4[0].entry = (common + STAGE1_DESCRIPTOR::TYPE::Table + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(frame_lvl3.start_address().as_u64() >> 12)).value; + p4[RECURSIVE_INDEX].entry = (common + STAGE1_DESCRIPTOR::TYPE::Table + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(frame_lvl4.start_address().as_u64() >> 12)).value; + + // warn!("p2"); + // for i in 0..512 { + // if p2[i].flags().bits() != 0 { + // info!("{:x?} {:x?} {:x?}",i, &p2[i] as *const _ as usize, p2[i]); + // } + // } + // warn!("p3"); + // for i in 0..512 { + // if p3[i].flags().bits() != 0 { + // info!("{:x?} {:x?} {:x?}",i, &p3[i] as *const _ as usize, p3[i]); + // } + // } + // warn!("p4"); + // for i in 0..512 { + // if p4[i].flags().bits() != 0 { + // info!("{:x?} {:x?} {:x?}",i, &p4[i] as *const _ as usize, p4[i]); + // } + // } + + ttbr0_el1_write(frame_lvl4); tlb_invalidate(); + info!("setup init page table end"); } @@ -69,7 +161,7 @@ impl PageTable for ActivePageTable { type Entry = PageEntry; fn map(&mut self, addr: usize, target: usize) -> &mut PageEntry { - let flags = EF::PRESENT | EF::WRITE | EF::UXN; + let flags = EF::PRESENT | EF::WRITE | EF::ACCESSED | EF::UXN | EF::PAGE_BIT; self.0.map_to(Page::of_addr(addr), Frame::of_addr(target), flags, &mut FrameAllocatorForAarch64) .unwrap().flush(); self.get_entry(addr) @@ -81,7 +173,7 @@ impl PageTable for ActivePageTable { } fn get_entry(&mut self, addr: usize) -> &mut PageEntry { - let entry_addr = ((addr >> 9) & 0o777_777_777_7770) | 0xffffff80_00000000; + let entry_addr = ((addr >> 9) & 0o777_777_777_7770) | (RECURSIVE_INDEX << 39); unsafe { &mut *(entry_addr as *mut PageEntry) } } @@ -99,9 +191,12 @@ impl PageTable for ActivePageTable { } } +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 *(0xffff_ffff_ffff_f000 as *mut _)).unwrap()) + 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 @@ -161,7 +256,7 @@ impl Entry for PageEntry { let mut addr = self as *const _ as usize; for _ in 0..3 { // Upper level entry - addr = ((addr >> 9) & 0o777_777_777_7770) | 0xffffff80_00000000; + addr = ((addr >> 9) & 0o777_777_777_7770) | (RECURSIVE_INDEX << 39); // set USER_ACCESSIBLE unsafe { (*(addr as *mut EF)).insert(EF::USER_ACCESSIBLE) }; } @@ -200,24 +295,24 @@ impl InactivePageTable for InactivePageTable0 { active_table().with_temporary_map(&frame, |_, table: &mut Aarch64PageTable| { table.zero(); // set up recursive mapping for the table - table[511].set_frame(frame.clone(), EF::PRESENT | EF::WRITE); + table[RECURSIVE_INDEX].set_frame(frame.clone(), EF::PRESENT | EF::WRITE | EF::ACCESSED | EF::PAGE_BIT); }); InactivePageTable0 { p4_frame: frame } } fn edit(&mut self, f: impl FnOnce(&mut Self::Active)) { active_table().with_temporary_map(&ttbr0_el1_read().0, |active_table, p4_table: &mut Aarch64PageTable| { - let backup = p4_table[0o777].clone(); + let backup = p4_table[RECURSIVE_INDEX].clone(); // overwrite recursive mapping - p4_table[0o777].set_frame(self.p4_frame.clone(), EF::PRESENT | EF::WRITE); + p4_table[RECURSIVE_INDEX].set_frame(self.p4_frame.clone(), EF::PRESENT | EF::WRITE | EF::ACCESSED | EF::PAGE_BIT); tlb_invalidate(); // execute f in the new context f(active_table); // restore recursive mapping to original p4 table - p4_table[0o777] = backup; + p4_table[RECURSIVE_INDEX] = backup; tlb_invalidate(); }); } @@ -228,6 +323,7 @@ impl InactivePageTable for InactivePageTable0 { debug!("switch table {:?} -> {:?}", old_frame, new_frame); if old_frame != new_frame { ttbr0_el1_write(new_frame); + tlb_invalidate(); } } @@ -237,11 +333,13 @@ impl InactivePageTable for InactivePageTable0 { debug!("switch table {:?} -> {:?}", old_frame, new_frame); if old_frame != new_frame { ttbr0_el1_write(new_frame); + tlb_invalidate(); } f(); debug!("switch table {:?} -> {:?}", new_frame, old_frame); if old_frame != new_frame { ttbr0_el1_write(old_frame); + tlb_invalidate(); } } @@ -264,13 +362,12 @@ impl InactivePageTable for InactivePageTable0 { impl InactivePageTable0 { fn map_kernel(&mut self) { - let mut table = unsafe { &mut *(0xffff_ffff_ffff_f000 as *mut Aarch64PageTable) }; - // Kernel at 0xffff_ff00_0000_0000 - // Kernel stack at 0x0000_57ac_0000_0000 (defined in bootloader crate) - let e0 = table[0].clone(); + let table = unsafe { &mut *ROOT_PAGE_TABLE }; + let e0 = table[KERNEL_PML4].clone(); + assert!(!e0.is_unused()); + self.edit(|_| { - table[0].set_addr(e0.addr(), e0.flags() & EF::GLOBAL); - //table[175].set_addr(estack.addr(), estack.flags() & EF::GLOBAL); + table[KERNEL_PML4].set_addr(e0.addr(), e0.flags() & EF::GLOBAL); }); } } diff --git a/kernel/src/consts.rs b/kernel/src/consts.rs index b2cdefe..e879173 100644 --- a/kernel/src/consts.rs +++ b/kernel/src/consts.rs @@ -131,15 +131,12 @@ mod x86_64 { #[cfg(target_arch = "aarch64")] mod aarch64 { - //! 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 = 0x3000_0000; + pub const USER_STACK_SIZE: usize = 1 * 1024 * 1024; + pub const USER32_STACK_OFFSET: usize = USER_STACK_OFFSET; } diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 063b1d0..05cc181 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -38,6 +38,10 @@ extern crate aarch64; use linked_list_allocator::LockedHeap; + +#[macro_use] +extern crate register; + #[macro_use] // print! pub mod logging; mod memory; diff --git a/kernel/src/memory.rs b/kernel/src/memory.rs index bbe78db..100925e 100644 --- a/kernel/src/memory.rs +++ b/kernel/src/memory.rs @@ -1,5 +1,5 @@ pub use arch::paging::*; -use bit_allocator::{BitAlloc, BitAlloc4K, BitAlloc64K}; +use bit_allocator::{BitAlloc, BitAlloc4K, BitAlloc64K, BitAlloc1M}; use consts::MEMORY_OFFSET; use spin::{Mutex, MutexGuard}; use super::HEAP_ALLOCATOR; @@ -19,7 +19,7 @@ pub type FrameAlloc = BitAlloc4K; // Raspberry Pi 3 has 1G memory #[cfg(target_arch = "aarch64")] -pub type FrameAlloc = BitAlloc64K; +pub type FrameAlloc = BitAlloc1M; lazy_static! { pub static ref FRAME_ALLOCATOR: Mutex = Mutex::new(FrameAlloc::default()); @@ -76,4 +76,4 @@ pub fn init_heap() { // use ucore_memory::cow::test::test_with; // test_with(&mut active_table()); // } -//} \ No newline at end of file +//} From f398945ad3e1f04841bca744d8c3a3f50aca7d56 Mon Sep 17 00:00:00 2001 From: equation314 Date: Tue, 20 Nov 2018 18:29:51 +0800 Subject: [PATCH 04/39] aarch64: hard link user program --- kernel/src/fs.rs | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/kernel/src/fs.rs b/kernel/src/fs.rs index 6e4e0a3..6810916 100644 --- a/kernel/src/fs.rs +++ b/kernel/src/fs.rs @@ -9,9 +9,23 @@ use spin::Mutex; global_asm!(r#" .section .rodata .align 12 -_binary_user_riscv_img_start: + .global _user_img_start + .global _user_img_end +_user_img_start: .incbin "../user/user-riscv.img" -_binary_user_riscv_img_end: +_user_img_end: +"#); + +// Hard link user program +#[cfg(target_arch = "aarch64")] +global_asm!(r#" + .section .rodata + .align 12 + .global _user_img_start + .global _user_img_end +_user_img_start: + .incbin "../user/user-riscv.img" +_user_img_end: "#); const LOGO: &str = r#" @@ -85,22 +99,18 @@ pub fn test_shell(prefix: &str) -> ! { pub fn shell() { show_logo(); - #[cfg(target_arch = "riscv32")] + #[cfg(any(target_arch = "riscv32", target_arch = "aarch64"))] let device = { extern { - fn _binary_user_riscv_img_start(); - fn _binary_user_riscv_img_end(); + fn _user_img_start(); + fn _user_img_end(); } - Box::new(unsafe { MemBuf::new(_binary_user_riscv_img_start, _binary_user_riscv_img_end) }) + Box::new(unsafe { MemBuf::new(_user_img_start, _user_img_end) }) }; #[cfg(target_arch = "x86_64")] let device = Box::new(&ide::DISK1); - #[cfg(target_arch = "aarch64")] - // TODO - let device: Box = unimplemented!(); - let sfs = SimpleFileSystem::open(device).expect("failed to open SFS"); let root = sfs.root_inode(); let files = root.borrow().list().unwrap(); From 13be52133da4825c9430f3f40bbd8cc8a7cfe6e2 Mon Sep 17 00:00:00 2001 From: equation314 Date: Tue, 20 Nov 2018 18:32:26 +0800 Subject: [PATCH 05/39] aarch64: move kernel stack top to 0x100000 --- kernel/src/arch/aarch64/boot/boot.S | 14 +++++++++----- kernel/src/arch/aarch64/boot/linker.ld | 9 ++++++--- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/kernel/src/arch/aarch64/boot/boot.S b/kernel/src/arch/aarch64/boot/boot.S index b8a142e..e525340 100644 --- a/kernel/src/arch/aarch64/boot/boot.S +++ b/kernel/src/arch/aarch64/boot/boot.S @@ -2,8 +2,7 @@ .section .text.boot -.global _start -_start: +boot: # read cpu affinity, start core 0, halt rest mrs x1, mpidr_el1 and x1, x1, #3 @@ -16,7 +15,7 @@ halt: setup: # store the desired EL1 stack pointer in x1 - adr x1, _start + ldr x1, =_start # use SP_ELx for Exception level ELx msr SPsel, #1 @@ -99,12 +98,17 @@ zero_bss: zero_bss_loop: # zero out the BSS section, 64-bits at a time - cbz x2, go_kmain + cbz x2, zero_bss_loop_end str xzr, [x1], #8 sub x2, x2, #8 cbnz x2, zero_bss_loop -go_kmain: +zero_bss_loop_end: + b _start + +.section .text.entry +.globl _start +_start: # jump to rust_main, which shouldn't return. halt if it does bl rust_main b halt diff --git a/kernel/src/arch/aarch64/boot/linker.ld b/kernel/src/arch/aarch64/boot/linker.ld index ce4c0c2..34858e3 100644 --- a/kernel/src/arch/aarch64/boot/linker.ld +++ b/kernel/src/arch/aarch64/boot/linker.ld @@ -3,11 +3,14 @@ ENTRY(_start) SECTIONS { . = 0x80000; /* Raspbery Pi 3 Aarch64 (kernel8.img) load address */ - /* start of the binary */ - _start = .; + .boot : { + KEEP(*(.text.boot)) /* from boot.S */ + } + + . = 0x100000; /* Load the kernel at this address. It's also kernel stack top address */ .text : { - KEEP(*(.text.boot)) /* from boot.S */ + *(.text.entry) *(.text .text.* .gnu.linkonce.t*) } From 4034a473bd63edc47ef59617a392804f471ec7f1 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Tue, 20 Nov 2018 11:24:25 +0800 Subject: [PATCH 06/39] update `user` crate to 2018 edition. add travis for user --- .travis.yml | 3 ++- user/Cargo.toml | 1 + user/Makefile | 2 +- user/ucore-ulib/Cargo.toml | 1 + user/ucore-ulib/src/lang_items.rs | 4 ++-- user/ucore-ulib/src/lib.rs | 1 - 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 53abd7e..56d7ee9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,4 +38,5 @@ before_script: - (test -x $HOME/.cargo/bin/bootimage || cargo install bootimage) script: - - cd kernel && make build arch=$ARCH + - cd kernel && make build arch=$ARCH && cd .. + - cd user && make arch=$ARCH diff --git a/user/Cargo.toml b/user/Cargo.toml index 0c95325..42811de 100644 --- a/user/Cargo.toml +++ b/user/Cargo.toml @@ -2,6 +2,7 @@ name = "ucore-user-programs" version = "0.1.0" authors = ["Runji Wang "] +edition = "2018" [dependencies] "ucore-ulib" = { path = "ucore-ulib" } \ No newline at end of file diff --git a/user/Makefile b/user/Makefile index 185ca0f..fccacbb 100644 --- a/user/Makefile +++ b/user/Makefile @@ -1,4 +1,4 @@ -# arch = {riscv32, x86_64} +# arch = {riscv32, x86_64, aarch64} arch := riscv32 all: diff --git a/user/ucore-ulib/Cargo.toml b/user/ucore-ulib/Cargo.toml index 186517c..27915b1 100644 --- a/user/ucore-ulib/Cargo.toml +++ b/user/ucore-ulib/Cargo.toml @@ -2,5 +2,6 @@ name = "ucore-ulib" version = "0.1.0" authors = ["WangRunji "] +edition = "2018" [dependencies] diff --git a/user/ucore-ulib/src/lang_items.rs b/user/ucore-ulib/src/lang_items.rs index 89e5334..06b4511 100644 --- a/user/ucore-ulib/src/lang_items.rs +++ b/user/ucore-ulib/src/lang_items.rs @@ -1,4 +1,4 @@ -use syscall::sys_exit; +use crate::syscall::sys_exit; use core::alloc::Layout; use core::panic::PanicInfo; @@ -17,7 +17,7 @@ pub extern fn _start(_argc: isize, _argv: *const *const u8) -> ! { #[lang = "eh_personality"] fn eh_personality() {} -#[panic_implementation] +#[panic_handler] fn panic(info: &PanicInfo) -> ! { let location = info.location().unwrap(); let message = info.message().unwrap(); diff --git a/user/ucore-ulib/src/lib.rs b/user/ucore-ulib/src/lib.rs index fb4cf19..4908eb5 100644 --- a/user/ucore-ulib/src/lib.rs +++ b/user/ucore-ulib/src/lib.rs @@ -1,7 +1,6 @@ #![no_std] #![feature(asm)] #![feature(lang_items)] -#![feature(panic_implementation)] #![feature(panic_info_message)] #![feature(linkage)] #![feature(compiler_builtins_lib)] From a9de99d3a9edc73e1b555efb38de5cc0e49886c1 Mon Sep 17 00:00:00 2001 From: equation314 Date: Wed, 21 Nov 2018 01:50:34 +0800 Subject: [PATCH 07/39] aarch64/mmu: remap kernel memory ranges at the end of memory::init() --- crate/aarch64/src/paging/mod.rs | 69 ++++++++++++++- crate/aarch64/src/paging/recursive.rs | 115 +++++++++++++------------ crate/memory/src/memory_set.rs | 5 +- kernel/src/arch/aarch64/boot/boot.S | 2 +- kernel/src/arch/aarch64/boot/linker.ld | 18 +++- kernel/src/arch/aarch64/memory.rs | 52 ++++++++--- kernel/src/arch/aarch64/paging.rs | 64 ++++++++------ 7 files changed, 224 insertions(+), 101 deletions(-) diff --git a/crate/aarch64/src/paging/mod.rs b/crate/aarch64/src/paging/mod.rs index ebc00dc..855b876 100644 --- a/crate/aarch64/src/paging/mod.rs +++ b/crate/aarch64/src/paging/mod.rs @@ -18,7 +18,7 @@ mod frame_alloc; mod page_table; mod recursive; -/// Trait for abstracting over the three possible page sizes on x86_64, 4KiB, 2MiB, 1GiB. +/// Trait for abstracting over the three possible block/page sizes on aarch64, 4KiB, 2MiB, 1GiB. pub trait PageSize: Copy + Eq + PartialOrd + Ord { /// The page size in bytes. const SIZE: u64; @@ -34,6 +34,14 @@ pub trait NotGiantPageSize: PageSize {} #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum Size4KiB {} +/// A “huge” 2MiB page. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum Size2MiB {} + +/// A “giant” 1GiB page. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub enum Size1GiB {} + impl PageSize for Size4KiB { const SIZE: u64 = 4096; const SIZE_AS_DEBUG_STR: &'static str = "4KiB"; @@ -41,6 +49,18 @@ impl PageSize for Size4KiB { impl NotGiantPageSize for Size4KiB {} +impl PageSize for Size2MiB { + const SIZE: u64 = Size4KiB::SIZE * 512; + const SIZE_AS_DEBUG_STR: &'static str = "2MiB"; +} + +impl NotGiantPageSize for Size2MiB {} + +impl PageSize for Size1GiB { + const SIZE: u64 = Size2MiB::SIZE * 512; + const SIZE_AS_DEBUG_STR: &'static str = "1GiB"; +} + /// A virtual memory page. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[repr(C)] @@ -97,6 +117,14 @@ impl Page { pub fn range_inclusive(start: Self, end: Self) -> PageRangeInclusive { PageRangeInclusive { start, end } } + + pub fn of_addr(address: usize) -> Self { + Self::containing_address(VirtAddr::new(address as u64)) + } + + pub fn range_of(begin: usize, end: usize) -> PageRange { + Self::range(Page::of_addr(begin), Page::of_addr(end - 1) + 1) + } } impl Page { @@ -106,6 +134,31 @@ impl Page { } } +impl Page { + /// Returns the 1GiB memory page with the specified page table indices. + pub fn from_page_table_indices_1gib(p4_index: u9, p3_index: u9) -> Self { + use bit_field::BitField; + + let mut addr = 0; + addr.set_bits(39..48, u64::from(p4_index)); + addr.set_bits(30..39, u64::from(p3_index)); + Page::containing_address(VirtAddr::new(addr)) + } +} + +impl Page { + /// Returns the 2MiB memory page with the specified page table indices. + pub fn from_page_table_indices_2mib(p4_index: u9, p3_index: u9, p2_index: u9) -> Self { + use bit_field::BitField; + + let mut addr = 0; + addr.set_bits(39..48, u64::from(p4_index)); + addr.set_bits(30..39, u64::from(p3_index)); + addr.set_bits(21..30, u64::from(p2_index)); + Page::containing_address(VirtAddr::new(addr)) + } +} + impl Page { /// Returns the 4KiB memory page with the specified page table indices. pub fn from_page_table_indices(p4_index: u9, p3_index: u9, p2_index: u9, p1_index: u9) -> Self { @@ -199,6 +252,16 @@ impl Iterator for PageRange { } } +impl PageRange { + /// Converts the range of 2MiB pages to a range of 4KiB pages. + pub fn as_4kib_page_range(self) -> PageRange { + PageRange { + start: Page::containing_address(self.start.start_address()), + end: Page::containing_address(self.end.start_address()), + } + } +} + impl fmt::Debug for PageRange { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("PageRange") @@ -294,6 +357,10 @@ impl PhysFrame { pub fn range_inclusive(start: PhysFrame, end: PhysFrame) -> PhysFrameRangeInclusive { PhysFrameRangeInclusive { start, end } } + + pub fn of_addr(address: usize) -> Self { + Self::containing_address(PhysAddr::new(address as u64)) + } } impl fmt::Debug for PhysFrame { diff --git a/crate/aarch64/src/paging/recursive.rs b/crate/aarch64/src/paging/recursive.rs index d735216..31d1802 100644 --- a/crate/aarch64/src/paging/recursive.rs +++ b/crate/aarch64/src/paging/recursive.rs @@ -239,6 +239,45 @@ impl<'a> RecursivePageTable<'a> { inner(entry, next_table_page, allocator) } + + pub fn p3_ptr(&self, page: Page) -> *mut PageTable { + self.p3_page(page).start_address().as_mut_ptr() + } + + pub fn p2_ptr(&self, page: Page) -> *mut PageTable { + self.p2_page(page).start_address().as_mut_ptr() + } + + pub fn p1_ptr(&self, page: Page) -> *mut PageTable { + self.p1_page(page).start_address().as_mut_ptr() + } + + fn p3_page(&self, page: Page) -> Page { + Page::from_page_table_indices( + self.recursive_index, + self.recursive_index, + self.recursive_index, + page.p4_index(), + ) + } + + fn p2_page(&self, page: Page) -> Page { + Page::from_page_table_indices( + self.recursive_index, + self.recursive_index, + page.p4_index(), + page.p3_index(), + ) + } + + fn p1_page(&self, page: Page) -> Page { + Page::from_page_table_indices( + self.recursive_index, + page.p4_index(), + page.p3_index(), + page.p2_index(), + ) + } } @@ -253,15 +292,16 @@ impl<'a> Mapper for RecursivePageTable<'a> { where A: FrameAllocator, { - let p4 = &mut self.p4; + let self_mut = unsafe{ &mut *(self as *const _ as *mut Self) }; + let p4 = &mut self_mut.p4; - let p3_page = p3_page(page, self.recursive_index); + let p3_page = self.p3_page(page); let p3 = unsafe { Self::create_next_table(&mut p4[page.p4_index()], p3_page, allocator)? }; - let p2_page = p2_page(page, self.recursive_index); + let p2_page = self.p2_page(page); let p2 = unsafe { Self::create_next_table(&mut p3[page.p3_index()], p2_page, allocator)? }; - let p1_page = p1_page(page, self.recursive_index); + let p1_page = self.p1_page(page); let p1 = unsafe { Self::create_next_table(&mut p2[page.p2_index()], p1_page, allocator)? }; if !p1[page.p1_index()].is_unused() { @@ -276,28 +316,30 @@ impl<'a> Mapper for RecursivePageTable<'a> { &mut self, page: Page, ) -> Result<(PhysFrame, MapperFlush), UnmapError> { - let p4 = &mut self.p4; + let self_mut = unsafe{ &mut *(self as *const _ as *mut Self) }; + let p4 = &mut self_mut.p4; + let p4_entry = &p4[page.p4_index()]; p4_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; - let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; + let p3 = unsafe { &mut *(self.p3_ptr(page)) }; let p3_entry = &p3[page.p3_index()]; p3_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; - let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; + let p2 = unsafe { &mut *(self.p2_ptr(page)) }; let p2_entry = &p2[page.p2_index()]; p2_entry.frame().map_err(|err| match err { FrameError::FrameNotPresent => UnmapError::PageNotMapped, FrameError::HugeFrame => UnmapError::ParentEntryHugePage, })?; - let p1 = unsafe { &mut *(p1_ptr(page, self.recursive_index)) }; + let p1 = unsafe { &mut *(self.p1_ptr(page)) }; let p1_entry = &mut p1[page.p1_index()]; let frame = p1_entry.frame().map_err(|err| match err { @@ -314,25 +356,26 @@ impl<'a> Mapper for RecursivePageTable<'a> { page: Page, flags: PageTableFlags, ) -> Result, FlagUpdateError> { - let p4 = &mut self.p4; + let self_mut = unsafe{ &mut *(self as *const _ as *mut Self) }; + let p4 = &mut self_mut.p4; if p4[page.p4_index()].is_unused() { return Err(FlagUpdateError::PageNotMapped); } - let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; + let p3 = unsafe { &mut *(self.p3_ptr(page)) }; if p3[page.p3_index()].is_unused() { return Err(FlagUpdateError::PageNotMapped); } - let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; + let p2 = unsafe { &mut *(self.p2_ptr(page)) }; if p2[page.p2_index()].is_unused() { return Err(FlagUpdateError::PageNotMapped); } - let p1 = unsafe { &mut *(p1_ptr(page, self.recursive_index)) }; + let p1 = unsafe { &mut *(self.p1_ptr(page)) }; if p1[page.p1_index()].is_unused() { return Err(FlagUpdateError::PageNotMapped); @@ -344,27 +387,28 @@ impl<'a> Mapper for RecursivePageTable<'a> { } fn translate_page(&self, page: Page) -> Option> { - let p4 = &self.p4; + let self_mut = unsafe{ &mut *(self as *const _ as *mut Self) }; + let p4 = &mut self_mut.p4; if p4[page.p4_index()].is_unused() { return None; } - let p3 = unsafe { &*(p3_ptr(page, self.recursive_index)) }; + let p3 = unsafe { &*(self.p3_ptr(page)) }; let p3_entry = &p3[page.p3_index()]; if p3_entry.is_unused() { return None; } - let p2 = unsafe { &*(p2_ptr(page, self.recursive_index)) }; + let p2 = unsafe { &*(self.p2_ptr(page)) }; let p2_entry = &p2[page.p2_index()]; if p2_entry.is_unused() { return None; } - let p1 = unsafe { &*(p1_ptr(page, self.recursive_index)) }; + let p1 = unsafe { &*(self.p1_ptr(page)) }; let p1_entry = &p1[page.p1_index()]; if p1_entry.is_unused() { @@ -374,42 +418,3 @@ impl<'a> Mapper for RecursivePageTable<'a> { PhysFrame::from_start_address(p1_entry.addr()).ok() } } - -fn p3_ptr(page: Page, recursive_index: u9) -> *mut PageTable { - p3_page(page, recursive_index).start_address().as_mut_ptr() -} - -fn p3_page(page: Page, recursive_index: u9) -> Page { - Page::from_page_table_indices( - recursive_index, - recursive_index, - recursive_index, - page.p4_index(), - ) -} - -fn p2_ptr(page: Page, recursive_index: u9) -> *mut PageTable { - p2_page(page, recursive_index).start_address().as_mut_ptr() -} - -fn p2_page(page: Page, recursive_index: u9) -> Page { - Page::from_page_table_indices( - recursive_index, - recursive_index, - page.p4_index(), - page.p3_index(), - ) -} - -fn p1_ptr(page: Page, recursive_index: u9) -> *mut PageTable { - p1_page(page, recursive_index).start_address().as_mut_ptr() -} - -fn p1_page(page: Page, recursive_index: u9) -> Page { - Page::from_page_table_indices( - recursive_index, - page.p4_index(), - page.p3_index(), - page.p2_index(), - ) -} diff --git a/crate/memory/src/memory_set.rs b/crate/memory/src/memory_set.rs index ca3e7dc..f67c956 100644 --- a/crate/memory/src/memory_set.rs +++ b/crate/memory/src/memory_set.rs @@ -166,6 +166,9 @@ impl MemorySet { pub fn iter(&self) -> impl Iterator { self.areas.iter() } + pub fn edit(&mut self, f: impl FnOnce(&mut T::Active)) { + self.page_table.edit(f); + } pub unsafe fn with(&self, f: impl FnOnce()) { self.page_table.with(f); } @@ -223,4 +226,4 @@ impl Debug for MemorySet { pub struct Stack { pub top: usize, pub bottom: usize, -} \ No newline at end of file +} diff --git a/kernel/src/arch/aarch64/boot/boot.S b/kernel/src/arch/aarch64/boot/boot.S index e525340..7a126bd 100644 --- a/kernel/src/arch/aarch64/boot/boot.S +++ b/kernel/src/arch/aarch64/boot/boot.S @@ -93,7 +93,7 @@ set_stack: zero_bss: # load the start address and number of bytes in BSS section - ldr x1, =__bss_start + ldr x1, =sbss ldr x2, =__bss_length zero_bss_loop: diff --git a/kernel/src/arch/aarch64/boot/linker.ld b/kernel/src/arch/aarch64/boot/linker.ld index 34858e3..2c7bdce 100644 --- a/kernel/src/arch/aarch64/boot/linker.ld +++ b/kernel/src/arch/aarch64/boot/linker.ld @@ -8,34 +8,44 @@ SECTIONS { } . = 0x100000; /* Load the kernel at this address. It's also kernel stack top address */ + bootstacktop = .; .text : { + stext = .; *(.text.entry) *(.text .text.* .gnu.linkonce.t*) + . = ALIGN(4K); + etext = .; } .rodata : { + srodata = .; *(.rodata .rodata.* .gnu.linkonce.r*) + . = ALIGN(4K); + erodata = .; } .data : { + sdata = .; *(.data .data.* .gnu.linkonce.d*) + . = ALIGN(4K); + edata = .; } .bss (NOLOAD) : { . = ALIGN(32); - __bss_start = .; + sbss = .; *(.bss .bss.*) *(COMMON) - . = ALIGN(8); - __bss_end = .; + . = ALIGN(4K); + ebss = .; } /* end of the binary */ _end = ALIGN(8); /* number of bytes in BSS section and complete binary */ - __bss_length = (__bss_end - __bss_start); + __bss_length = (ebss - sbss); __binary_length = (_end - _start); /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } diff --git a/kernel/src/arch/aarch64/memory.rs b/kernel/src/arch/aarch64/memory.rs index 3e7f6d8..c04e9c3 100644 --- a/kernel/src/arch/aarch64/memory.rs +++ b/kernel/src/arch/aarch64/memory.rs @@ -1,13 +1,9 @@ //! Memory initialization for aarch64. -use bit_allocator::BitAlloc; use ucore_memory::PAGE_SIZE; -use memory::{FRAME_ALLOCATOR, init_heap}; +use memory::{FRAME_ALLOCATOR, init_heap, MemoryArea, MemoryAttr, MemorySet, Stack}; use super::atags::atags::Atags; -//use super::super::HEAP_ALLOCATOR; -use aarch64::{barrier, regs::*, addr::*}; -use aarch64::paging::{FrameAllocator, FrameDeallocator, Page, PageRange, PhysFrame as Frame, Size4KiB}; -use core::ops::Range; +use aarch64::{barrier, regs::*, addr::*, paging::PhysFrame as Frame}; /// Memory initialization. pub fn init() { @@ -25,14 +21,11 @@ pub fn init() { init_mmu(); init_frame_allocator(); init_heap(); + remap_the_kernel(); info!("memory: init end"); } -extern "C" { - static _end: u8; -} - fn init_frame_allocator() { use bit_allocator::BitAlloc; use core::ops::Range; @@ -96,12 +89,36 @@ fn init_mmu() { info!("mmu enabled"); } +fn remap_the_kernel() { + let (bottom, top) = (0, bootstacktop as usize); + let kstack = Stack { + top, + bottom, + }; + static mut SPACE: [u8; 0x1000] = [0; 0x1000]; + let mut ms = unsafe { MemorySet::new_from_raw_space(&mut SPACE, kstack) }; + ms.push(MemoryArea::new_identity(bottom, top, MemoryAttr::default(), "kstack")); + ms.push(MemoryArea::new_identity(stext as usize, etext as usize, MemoryAttr::default().execute().readonly(), "text")); + ms.push(MemoryArea::new_identity(sdata as usize, edata as usize, MemoryAttr::default(), "data")); + ms.push(MemoryArea::new_identity(srodata as usize, erodata as usize, MemoryAttr::default().readonly(), "rodata")); + ms.push(MemoryArea::new_identity(sbss as usize, ebss as usize, MemoryAttr::default(), "bss")); + + // ensure the level 2 page table exists + ms.push(MemoryArea::new_identity(0x40000000, 0x40200000, MemoryAttr::default(), "arm_control")); + super::paging::remap_device_2mib(&mut ms, 0x3F000000, 0x40200000); + + unsafe { ms.activate(); } + use core::mem::forget; + forget(ms); + info!("kernel remap end"); +} + /// Returns the (start address, end address) of the available memory on this /// system if it can be determined. If it cannot, `None` is returned. /// /// This function is expected to return `Some` under all normal cirumstances. fn memory_map() -> Option<(usize, usize)> { - let binary_end = unsafe { (&_end as *const u8) as u32 }; + let binary_end = unsafe { _end as u32 }; let mut atags: Atags = Atags::get(); while let Some(atag) = atags.next() { @@ -113,3 +130,16 @@ fn memory_map() -> Option<(usize, usize)> { None } +extern { + fn bootstacktop(); + fn stext(); + fn etext(); + fn sdata(); + fn edata(); + fn srodata(); + fn erodata(); + fn sbss(); + fn ebss(); + fn _start(); + fn _end(); +} diff --git a/kernel/src/arch/aarch64/paging.rs b/kernel/src/arch/aarch64/paging.rs index 9696db9..a023237 100644 --- a/kernel/src/arch/aarch64/paging.rs +++ b/kernel/src/arch/aarch64/paging.rs @@ -8,8 +8,7 @@ use ucore_memory::paging::*; use aarch64::asm::{tlb_invalidate, ttbr0_el1_read, ttbr0_el1_write}; use aarch64::{PhysAddr, VirtAddr}; use aarch64::paging::{Mapper, PageTable as Aarch64PageTable, PageTableEntry, PageTableFlags as EF, RecursivePageTable}; -use aarch64::paging::{FrameAllocator, FrameDeallocator, Page, PageRange, PhysFrame as Frame, Size4KiB}; -use aarch64::{regs::*}; +use aarch64::paging::{FrameAllocator, FrameDeallocator, Page, PageRange, PhysFrame as Frame, Size4KiB, Size2MiB}; register_bitfields! {u64, // AArch64 Reference Manual page 2150 @@ -61,6 +60,11 @@ register_bitfields! {u64, ] } +mod mair { + pub const NORMAL: u64 = 0; + pub const DEVICE: u64 = 1; +} + // need 3 page pub fn setup_page_table(frame_lvl4: Frame, frame_lvl3: Frame, frame_lvl2: Frame) { let p4 = unsafe { &mut *(frame_lvl4.start_address().as_u64() as *mut Aarch64PageTable) }; @@ -70,11 +74,6 @@ pub fn setup_page_table(frame_lvl4: Frame, frame_lvl3: Frame, frame_lvl2: Frame) p3.zero(); p2.zero(); - mod mair { - pub const NORMAL: u64 = 0; - pub const DEVICE: u64 = 1; - } - // Fill the rest of the LVL2 (2MiB) entries as block // descriptors. Differentiate between normal and device mem. const MMIO_BASE: u64 = 0x3F000000; @@ -129,28 +128,37 @@ pub fn setup_page_table(frame_lvl4: Frame, frame_lvl3: Frame, frame_lvl2: Frame) info!("setup init page table end"); } -pub trait PageExt { - fn of_addr(address: usize) -> Self; - fn range_of(begin: usize, end: usize) -> PageRange; -} - -impl PageExt for Page { - fn of_addr(address: usize) -> Self { - Page::containing_address(VirtAddr::new(address as u64)) - } - fn range_of(begin: usize, end: usize) -> PageRange { - Page::range(Page::of_addr(begin), Page::of_addr(end - 1) + 1) - } -} - -pub trait FrameExt { - fn of_addr(address: usize) -> Self; -} +/// map the range [start, end) as device memory, insert to the MemorySet +pub fn remap_device_2mib(ms: &mut MemorySet, start_addr: usize, end_addr: usize) { + ms.edit(|active_table| { + let common = STAGE1_DESCRIPTOR::VALID::True + + STAGE1_DESCRIPTOR::TYPE::Block + + STAGE1_DESCRIPTOR::AP::RW_EL1 + + STAGE1_DESCRIPTOR::AF::True + + STAGE1_DESCRIPTOR::XN::True; + + let mem_attr = STAGE1_DESCRIPTOR::SH::OuterShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::DEVICE); + + type Page2MiB = Page; + for page in Page2MiB::range_of(start_addr, end_addr) { + let p2 = unsafe { &mut *active_table.0.p2_ptr(page) }; + p2[page.p2_index()].entry = (common + mem_attr + STAGE1_DESCRIPTOR::LVL2_OUTPUT_ADDR_4KiB.val(page.start_address().as_u64() >> 21)).value; + } -impl FrameExt for Frame { - fn of_addr(address: usize) -> Self { - Frame::containing_address(PhysAddr::new(address as u64)) - } + // let p2 = unsafe { &mut *(0o777_777_000_000_0000 as *mut Aarch64PageTable) }; + // for i in 0..512 { + // if p2[i].flags().bits() != 0 { + // info!("{:x?} {:x?} {:x?}",i, &p2[i] as *const _ as usize, p2[i]); + // } + // } + + // let p2 = unsafe { &mut *(0o777_777_000_001_0000 as *mut Aarch64PageTable) }; + // for i in 0..512 { + // if p2[i].flags().bits() != 0 { + // info!("{:x?} {:x?} {:x?}",i, &p2[i] as *const _ as usize, p2[i]); + // } + // } + }); } pub struct ActivePageTable(RecursivePageTable<'static>); From bb1c1abaa480d21c920082950effb5d35df8c55e Mon Sep 17 00:00:00 2001 From: equation314 Date: Wed, 21 Nov 2018 20:02:34 +0800 Subject: [PATCH 08/39] aarch64/mmu: can run on the real raspi3 --- crate/aarch64/src/asm.rs | 16 ++++- crate/aarch64/src/paging/recursive.rs | 3 +- kernel/src/arch/aarch64/board/raspi3/mod.rs | 7 +- .../src/arch/aarch64/board/raspi3/serial.rs | 8 +-- kernel/src/arch/aarch64/io.rs | 12 ++-- kernel/src/arch/aarch64/memory.rs | 70 +++++++++---------- kernel/src/arch/aarch64/mod.rs | 6 +- kernel/src/arch/aarch64/paging.rs | 19 +++-- 8 files changed, 72 insertions(+), 69 deletions(-) diff --git a/crate/aarch64/src/asm.rs b/crate/aarch64/src/asm.rs index 17e6c29..63ce23a 100644 --- a/crate/aarch64/src/asm.rs +++ b/crate/aarch64/src/asm.rs @@ -1,9 +1,9 @@ use paging::PhysFrame; -use addr::PhysAddr; +use addr::{PhysAddr, VirtAddr}; use regs::*; #[inline(always)] -pub fn tlb_invalidate() { +pub fn tlb_invalidate_all() { unsafe { asm!( "dsb ishst @@ -14,6 +14,18 @@ pub fn tlb_invalidate() { } } +#[inline(always)] +pub fn tlb_invalidate(vaddr: VirtAddr) { + unsafe { + asm!( + "dsb ishst + tlbi vaae1is, $0 + dsb ish + isb" :: "r"(vaddr.as_u64() >> 12) + ); + } +} + /// Returns the current stack pointer. #[inline(always)] pub fn sp() -> *const u8 { diff --git a/crate/aarch64/src/paging/recursive.rs b/crate/aarch64/src/paging/recursive.rs index 31d1802..79b0a0d 100644 --- a/crate/aarch64/src/paging/recursive.rs +++ b/crate/aarch64/src/paging/recursive.rs @@ -28,7 +28,7 @@ impl MapperFlush { /// Flush the page from the TLB to ensure that the newest mapping is used. pub fn flush(self) { - tlb_invalidate(); + tlb_invalidate(self.0.start_address()); } /// Don't flush the TLB and silence the “must be used” warning. @@ -232,6 +232,7 @@ impl<'a> RecursivePageTable<'a> { let page_table_ptr = next_table_page.start_address().as_mut_ptr(); let page_table: &mut PageTable = unsafe { &mut *(page_table_ptr) }; if created { + tlb_invalidate(next_table_page.start_address()); page_table.zero(); } Ok(page_table) diff --git a/kernel/src/arch/aarch64/board/raspi3/mod.rs b/kernel/src/arch/aarch64/board/raspi3/mod.rs index 2b32a10..2d13315 100644 --- a/kernel/src/arch/aarch64/board/raspi3/mod.rs +++ b/kernel/src/arch/aarch64/board/raspi3/mod.rs @@ -7,12 +7,9 @@ pub mod timer; pub mod serial; 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!"); } diff --git a/kernel/src/arch/aarch64/board/raspi3/serial.rs b/kernel/src/arch/aarch64/board/raspi3/serial.rs index 5434071..b6c29f7 100644 --- a/kernel/src/arch/aarch64/board/raspi3/serial.rs +++ b/kernel/src/arch/aarch64/board/raspi3/serial.rs @@ -20,8 +20,7 @@ impl SerialPort { /// Init a newly created SerialPort, can only be called once. pub fn init(&mut self) { - // FIXME - // assert_has_not_been_called!("SerialPort::init must be called only once"); + assert_has_not_been_called!("SerialPort::init must be called only once"); self.mu = Some(MiniUart::new()); } @@ -71,7 +70,4 @@ impl fmt::Write for SerialPort { } } -// FIXME -// pub static SERIAL_PORT: Mutex = Mutex::new(SerialPort::new()); -pub static mut SERIAL_PORT: SerialPort = SerialPort::new(); - +pub static SERIAL_PORT: Mutex = Mutex::new(SerialPort::new()); diff --git a/kernel/src/arch/aarch64/io.rs b/kernel/src/arch/aarch64/io.rs index deda39c..1a9d1d4 100644 --- a/kernel/src/arch/aarch64/io.rs +++ b/kernel/src/arch/aarch64/io.rs @@ -4,15 +4,11 @@ use core::fmt::{Arguments, Write}; use super::board::serial::{SerialRead, SERIAL_PORT}; pub fn getchar() -> char { - // FIXME - unsafe { - SERIAL_PORT.receive() as char - } + unsafe { SERIAL_PORT.force_unlock(); } + SERIAL_PORT.lock().receive() as char } pub fn putfmt(fmt: Arguments) { - // FIXME - unsafe { - SERIAL_PORT.write_fmt(fmt).unwrap() - } + unsafe { SERIAL_PORT.force_unlock(); } + SERIAL_PORT.lock().write_fmt(fmt).unwrap() } diff --git a/kernel/src/arch/aarch64/memory.rs b/kernel/src/arch/aarch64/memory.rs index c04e9c3..3f93e31 100644 --- a/kernel/src/arch/aarch64/memory.rs +++ b/kernel/src/arch/aarch64/memory.rs @@ -7,6 +7,14 @@ use aarch64::{barrier, regs::*, addr::*, paging::PhysFrame as Frame}; /// Memory initialization. pub fn init() { + init_frame_allocator(); + init_heap(); + remap_the_kernel(); + info!("memory: init end"); +} + +/// initialize temporary paging and enable mmu immediately after boot. Serial port is disabled at this time. +pub fn init_mmu_early() { #[repr(align(4096))] struct PageData([u8; PAGE_SIZE]); static PAGE_TABLE_LVL4: PageData = PageData([0; PAGE_SIZE]); @@ -18,41 +26,6 @@ pub fn init() { let frame_lvl2 = Frame::containing_address(PhysAddr::new(&PAGE_TABLE_LVL2 as *const _ as u64)); super::paging::setup_page_table(frame_lvl4, frame_lvl3, frame_lvl2); - init_mmu(); - init_frame_allocator(); - init_heap(); - remap_the_kernel(); - - info!("memory: init end"); -} - -fn init_frame_allocator() { - use bit_allocator::BitAlloc; - use core::ops::Range; - use consts::{MEMORY_OFFSET}; - - let (start, end) = memory_map().expect("failed to find memory map"); - let mut ba = FRAME_ALLOCATOR.lock(); - ba.insert(to_range(start, end)); - info!("FrameAllocator init end"); - - /* - * @param: - * start: start address - * end: end address - * @brief: - * transform the memory address to the page number - * @retval: - * the page number range from start address to end address - */ - fn to_range(start: usize, end: usize) -> Range { - let page_start = (start - MEMORY_OFFSET) / PAGE_SIZE; - let page_end = (end - MEMORY_OFFSET - 1) / PAGE_SIZE + 1; - page_start..page_end - } -} - -fn init_mmu() { // device. MAIR_EL1.write( // Attribute 1 @@ -85,10 +58,35 @@ fn init_mmu() { // Force MMU init to complete before next instruction unsafe { barrier::isb(barrier::SY); } +} + +fn init_frame_allocator() { + use bit_allocator::BitAlloc; + use core::ops::Range; + use consts::{MEMORY_OFFSET}; + + let (start, end) = memory_map().expect("failed to find memory map"); + let mut ba = FRAME_ALLOCATOR.lock(); + ba.insert(to_range(start, end)); + info!("FrameAllocator init end"); - info!("mmu enabled"); + /* + * @param: + * start: start address + * end: end address + * @brief: + * transform the memory address to the page number + * @retval: + * the page number range from start address to end address + */ + fn to_range(start: usize, end: usize) -> Range { + let page_start = (start - MEMORY_OFFSET) / PAGE_SIZE; + let page_end = (end - MEMORY_OFFSET - 1) / PAGE_SIZE + 1; + page_start..page_end + } } +/// remap kernel page table after all initialization. fn remap_the_kernel() { let (bottom, top) = (0, bootstacktop as usize); let kstack = Stack { diff --git a/kernel/src/arch/aarch64/mod.rs b/kernel/src/arch/aarch64/mod.rs index 35613ab..ccd6230 100644 --- a/kernel/src/arch/aarch64/mod.rs +++ b/kernel/src/arch/aarch64/mod.rs @@ -17,9 +17,13 @@ pub use self::board::timer; /// The entry point of kernel #[no_mangle] // don't mangle the name of this function pub extern "C" fn rust_main() -> ! { + // Enable mmu and paging + memory::init_mmu_early(); + // Init board to enable serial port. board::init(); - ::logging::init(); // FIXME + + ::logging::init(); interrupt::init(); memory::init(); timer::init(); diff --git a/kernel/src/arch/aarch64/paging.rs b/kernel/src/arch/aarch64/paging.rs index a023237..c7c4e99 100644 --- a/kernel/src/arch/aarch64/paging.rs +++ b/kernel/src/arch/aarch64/paging.rs @@ -5,7 +5,7 @@ use memory::{active_table, alloc_frame, alloc_stack, dealloc_frame}; use ucore_memory::memory_set::*; use ucore_memory::PAGE_SIZE; use ucore_memory::paging::*; -use aarch64::asm::{tlb_invalidate, ttbr0_el1_read, ttbr0_el1_write}; +use aarch64::asm::{tlb_invalidate, tlb_invalidate_all, ttbr0_el1_read, ttbr0_el1_write}; use aarch64::{PhysAddr, VirtAddr}; use aarch64::paging::{Mapper, PageTable as Aarch64PageTable, PageTableEntry, PageTableFlags as EF, RecursivePageTable}; use aarch64::paging::{FrameAllocator, FrameDeallocator, Page, PageRange, PhysFrame as Frame, Size4KiB, Size2MiB}; @@ -123,9 +123,7 @@ pub fn setup_page_table(frame_lvl4: Frame, frame_lvl3: Frame, frame_lvl2: Frame) // } ttbr0_el1_write(frame_lvl4); - tlb_invalidate(); - - info!("setup init page table end"); + tlb_invalidate_all(); } /// map the range [start, end) as device memory, insert to the MemorySet @@ -222,7 +220,8 @@ impl ActivePageTable { impl Entry for PageEntry { fn update(&mut self) { - tlb_invalidate(); + let addr = VirtAddr::new_unchecked((self as *const _ as u64) << 9); + tlb_invalidate(addr); } fn present(&self) -> bool { self.0.flags().contains(EF::PRESENT) } @@ -314,14 +313,14 @@ impl InactivePageTable for InactivePageTable0 { // overwrite recursive mapping p4_table[RECURSIVE_INDEX].set_frame(self.p4_frame.clone(), EF::PRESENT | EF::WRITE | EF::ACCESSED | EF::PAGE_BIT); - tlb_invalidate(); + 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(); + tlb_invalidate_all(); }); } @@ -331,7 +330,7 @@ impl InactivePageTable for InactivePageTable0 { debug!("switch table {:?} -> {:?}", old_frame, new_frame); if old_frame != new_frame { ttbr0_el1_write(new_frame); - tlb_invalidate(); + tlb_invalidate_all(); } } @@ -341,13 +340,13 @@ impl InactivePageTable for InactivePageTable0 { debug!("switch table {:?} -> {:?}", old_frame, new_frame); if old_frame != new_frame { ttbr0_el1_write(new_frame); - tlb_invalidate(); + tlb_invalidate_all(); } f(); debug!("switch table {:?} -> {:?}", new_frame, old_frame); if old_frame != new_frame { ttbr0_el1_write(old_frame); - tlb_invalidate(); + tlb_invalidate_all(); } } From 46f0e85230b3697d59a03f053594dc0a6da8e889 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Thu, 22 Nov 2018 18:06:39 +0800 Subject: [PATCH 09/39] add `ucore-i386-pic.img` user programs. reorganize imgs. --- kernel/Cargo.toml | 2 +- kernel/Makefile | 2 +- kernel/src/fs.rs | 2 +- user/img/ucore-i386-pic.img | Bin 0 -> 1572864 bytes user/{ucore32.img => img/ucore-i386.img} | Bin user/{user-riscv.img => img/ucore-rv32.img} | Bin user/{xv6_64.img => img/xv6-x86_64.img} | Bin 7 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 user/img/ucore-i386-pic.img rename user/{ucore32.img => img/ucore-i386.img} (100%) rename user/{user-riscv.img => img/ucore-rv32.img} (100%) rename user/{xv6_64.img => img/xv6-x86_64.img} (100%) diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index ba57b56..4df6d88 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -58,7 +58,7 @@ minimum-image-size = 0 # The minimum output file size (in MiB) # (the "{}" will be replaced with the path to the bootable disk image) run-command = ["qemu-system-x86_64", "-drive", "format=raw,file={}", - "-drive", "format=raw,file=../user/ucore32.img,media=disk,cache=writeback", + "-drive", "format=raw,file=../user/img/ucore-i386-pic.img,media=disk,cache=writeback", "-serial", "mon:stdio", "-device", "isa-debug-exit", "-smp", "4" diff --git a/kernel/Makefile b/kernel/Makefile index 0e1a2e8..2329032 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -30,7 +30,7 @@ bootimage := target/$(target)/bootimage.bin user_bin_path := ../user/target/$(arch)-ucore/debug user_bins := $(patsubst $(user_bin_path)/%.d, $(user_bin_path)/%, $(wildcard $(user_bin_path)/*.d)) user_obj := build/$(arch)/user.o -SFSIMG := ../user/ucore32.img +SFSIMG := ../user/img/ucore-i386-pic.img ### qemu options ### ifeq ($(arch), x86_64) diff --git a/kernel/src/fs.rs b/kernel/src/fs.rs index 9fea7d7..fe38f0c 100644 --- a/kernel/src/fs.rs +++ b/kernel/src/fs.rs @@ -18,7 +18,7 @@ global_asm!(r#" .global _user_img_start .global _user_img_end _user_img_start: - .incbin "../user/user-riscv.img" + .incbin "../user/img/ucore-rv32.img" _user_img_end: "#); diff --git a/user/img/ucore-i386-pic.img b/user/img/ucore-i386-pic.img new file mode 100644 index 0000000000000000000000000000000000000000..a70e2638de791dea0b14ef46988054e1dc33ab68 GIT binary patch literal 1572864 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ȁE9icW>wYzLtmeI&sgW+>&RF#j`@{ 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

4+h+cEsljJW62v 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%0Doiq<@S9!q1nDuQrfD`wQ#{Zg+ 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 Date: Fri, 23 Nov 2018 00:43:18 +0800 Subject: [PATCH 10/39] aarch64/mmu: use both TTBR0_EL1 & TTBR1_EL1 --- crate/aarch64/src/addr.rs | 23 ++++ crate/aarch64/src/asm.rs | 36 +++-- crate/aarch64/src/paging/mod.rs | 5 + crate/aarch64/src/paging/recursive.rs | 16 +-- crate/aarch64/src/regs/mod.rs | 2 + crate/aarch64/src/regs/tcr_el1.rs | 135 +++++++++++++++++++ crate/aarch64/src/regs/ttbr0_el1.rs | 5 + crate/aarch64/src/regs/ttbr1_el1.rs | 61 +++++++++ kernel/src/arch/aarch64/interrupt/context.rs | 23 ++-- kernel/src/arch/aarch64/memory.rs | 31 +++-- kernel/src/arch/aarch64/paging.rs | 31 ++--- kernel/src/consts.rs | 2 +- kernel/src/process/context.rs | 4 + 13 files changed, 309 insertions(+), 65 deletions(-) create mode 100644 crate/aarch64/src/regs/ttbr1_el1.rs diff --git a/crate/aarch64/src/addr.rs b/crate/aarch64/src/addr.rs index e347567..cf3f092 100644 --- a/crate/aarch64/src/addr.rs +++ b/crate/aarch64/src/addr.rs @@ -6,6 +6,15 @@ use bit_field::BitField; use usize_conversions::FromUsize; use ux::*; +#[derive(Debug)] +#[repr(u8)] +pub enum VirtAddrRange { + /// 0x0000000000000000 to 0x0000FFFFFFFFFFFF + BottomRange = 0, + /// 0xFFFF000000000000 to 0xFFFFFFFFFFFFFFFF. + TopRange = 1, +} + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] pub struct VirtAddr(u64); @@ -105,6 +114,20 @@ impl VirtAddr { u12::new((self.0 & 0xfff).try_into().unwrap()) } + /// Returns the VA range + pub fn va_range(&self) -> Result { + match self.va_range_bits() { + 0x0000 => Ok(VirtAddrRange::BottomRange), + 0xffff => Ok(VirtAddrRange::TopRange), + _ => Err(VirtAddrNotValid(self.0)), + } + } + + /// Returns the top 16 bits + pub fn va_range_bits(&self) -> u16 { + ((self.0 >> 48) & 0xffff) as u16 + } + /// Returns the 9-bit level 1 page table index. pub fn p1_index(&self) -> u9 { u9::new(((self.0 >> 12) & 0o777).try_into().unwrap()) diff --git a/crate/aarch64/src/asm.rs b/crate/aarch64/src/asm.rs index 63ce23a..a5b16d5 100644 --- a/crate/aarch64/src/asm.rs +++ b/crate/aarch64/src/asm.rs @@ -160,24 +160,20 @@ pub fn eret() -> ! { } } -bitflags! { - /// Controls cache settings for the level 4 page table. - pub struct ttbr0_el1_Flags: u64 { - - const COMMON_NOT_PRIVATE = 1 << 0; - } -} - -pub fn ttbr0_el1_read() -> (PhysFrame, ttbr0_el1_Flags) { - let value = TTBR0_EL1.get(); - let flags = ttbr0_el1_Flags::from_bits_truncate(value); - let addr = PhysAddr::new(value & 0x_0000_ffff_ffff_f000); - let frame = PhysFrame::containing_address(addr); - (frame, flags) -} - -pub fn ttbr0_el1_write(frame: PhysFrame) { - let addr = frame.start_address(); - let value = addr.as_u64(); - TTBR0_EL1.set_baddr(value); +pub fn ttbr_el1_read(which: u8) -> (PhysFrame) { + let baddr = match which { + 0 => TTBR0_EL1.get_baddr(), + 1 => TTBR1_EL1.get_baddr(), + _ => 0, + }; + PhysFrame::containing_address(PhysAddr::new(baddr)) +} + +pub fn ttbr_el1_write(which: u8, frame: PhysFrame) { + let baddr = frame.start_address().as_u64(); + match which { + 0 => TTBR0_EL1.set_baddr(baddr), + 1 => TTBR1_EL1.set_baddr(baddr), + _ => {} + }; } diff --git a/crate/aarch64/src/paging/mod.rs b/crate/aarch64/src/paging/mod.rs index 855b876..c73d5b4 100644 --- a/crate/aarch64/src/paging/mod.rs +++ b/crate/aarch64/src/paging/mod.rs @@ -98,6 +98,11 @@ impl Page { S::SIZE } + /// Returns the level 4 page table index of this page. + pub fn va_range_bits(&self) -> u16 { + self.start_address().va_range_bits() + } + /// Returns the level 4 page table index of this page. pub fn p4_index(&self) -> u9 { self.start_address().p4_index() diff --git a/crate/aarch64/src/paging/recursive.rs b/crate/aarch64/src/paging/recursive.rs index 79b0a0d..ee61cba 100644 --- a/crate/aarch64/src/paging/recursive.rs +++ b/crate/aarch64/src/paging/recursive.rs @@ -7,7 +7,7 @@ use paging::{ NotGiantPageSize, Page, PageSize, PhysFrame, Size4KiB, }; use paging::page_table::PageTableFlags as Flags; -use asm::ttbr0_el1_read; +use asm::ttbr_el1_read; use ux::u9; use addr::{PhysAddr, VirtAddr}; @@ -162,7 +162,9 @@ impl<'a> RecursivePageTable<'a> { { return Err(NotRecursivelyMapped); } - if Ok(ttbr0_el1_read().0) != table[recursive_index].frame() { + if Ok(ttbr_el1_read(page.start_address().va_range().unwrap() as u8)) != + table[recursive_index].frame() + { return Err(NotRecursivelyMapped); } @@ -212,7 +214,6 @@ impl<'a> RecursivePageTable<'a> { where A: FrameAllocator, { - let created; if entry.is_unused() { @@ -281,7 +282,6 @@ impl<'a> RecursivePageTable<'a> { } } - impl<'a> Mapper for RecursivePageTable<'a> { fn map_to( &mut self, @@ -293,7 +293,7 @@ impl<'a> Mapper for RecursivePageTable<'a> { where A: FrameAllocator, { - let self_mut = unsafe{ &mut *(self as *const _ as *mut Self) }; + let self_mut = unsafe { &mut *(self as *const _ as *mut Self) }; let p4 = &mut self_mut.p4; let p3_page = self.p3_page(page); @@ -317,7 +317,7 @@ impl<'a> Mapper for RecursivePageTable<'a> { &mut self, page: Page, ) -> Result<(PhysFrame, MapperFlush), UnmapError> { - let self_mut = unsafe{ &mut *(self as *const _ as *mut Self) }; + let self_mut = unsafe { &mut *(self as *const _ as *mut Self) }; let p4 = &mut self_mut.p4; let p4_entry = &p4[page.p4_index()]; @@ -357,7 +357,7 @@ impl<'a> Mapper for RecursivePageTable<'a> { page: Page, flags: PageTableFlags, ) -> Result, FlagUpdateError> { - let self_mut = unsafe{ &mut *(self as *const _ as *mut Self) }; + let self_mut = unsafe { &mut *(self as *const _ as *mut Self) }; let p4 = &mut self_mut.p4; if p4[page.p4_index()].is_unused() { @@ -388,7 +388,7 @@ impl<'a> Mapper for RecursivePageTable<'a> { } fn translate_page(&self, page: Page) -> Option> { - let self_mut = unsafe{ &mut *(self as *const _ as *mut Self) }; + let self_mut = unsafe { &mut *(self as *const _ as *mut Self) }; let p4 = &mut self_mut.p4; if p4[page.p4_index()].is_unused() { diff --git a/crate/aarch64/src/regs/mod.rs b/crate/aarch64/src/regs/mod.rs index f6a0e3d..5a7e556 100644 --- a/crate/aarch64/src/regs/mod.rs +++ b/crate/aarch64/src/regs/mod.rs @@ -24,6 +24,7 @@ mod spsel; mod spsr_el2; mod tcr_el1; mod ttbr0_el1; +mod ttbr1_el1; // Export only the R/W traits and the static reg definitions pub use register::cpu::*; @@ -49,3 +50,4 @@ pub use self::spsel::SPSel; pub use self::spsr_el2::SPSR_EL2; pub use self::tcr_el1::TCR_EL1; pub use self::ttbr0_el1::TTBR0_EL1; +pub use self::ttbr1_el1::TTBR1_EL1; diff --git a/crate/aarch64/src/regs/tcr_el1.rs b/crate/aarch64/src/regs/tcr_el1.rs index bcd0425..9ebb6d7 100644 --- a/crate/aarch64/src/regs/tcr_el1.rs +++ b/crate/aarch64/src/regs/tcr_el1.rs @@ -22,6 +22,17 @@ use register::cpu::RegisterReadWrite; register_bitfields! {u64, TCR_EL1 [ + /// Top Byte ignored - indicates whether the top byte of an address is + /// used for address match for the TTBR1_EL1 region, or ignored and used + /// for tagged addresses. Defined values are: + /// + /// 0 Top Byte used in the address calculation. + /// 1 Top Byte ignored in the address calculation. + TBI1 OFFSET(38) NUMBITS(1) [ + Used = 0, + Ignored = 1 + ], + /// Top Byte ignored - indicates whether the top byte of an address is /// used for address match for the TTBR0_EL1 region, or ignored and used /// for tagged addresses. Defined values are: @@ -33,6 +44,20 @@ register_bitfields! {u64, Ignored = 1 ], + /// ASID Size. Defined values are: + /// + /// 0 8 bit - the upper 8 bits of TTBR0_EL1 and TTBR1_EL1 are ignored by + /// hardware for every purpose except reading back the register, and are + /// treated as if they are all zeros for when used for allocation and matching entries in the TLB. + /// 1 16 bit - the upper 16 bits of TTBR0_EL1 and TTBR1_EL1 are used for + /// allocation and matching in the TLB. + /// + /// If the implementation has only 8 bits of ASID, this field is RES0. + AS OFFSET(36) NUMBITS(1) [ + Bits_8 = 0, + Bits_16 = 1 + ], + /// Intermediate Physical Address Size. /// /// 000 32 bits, 4GiB. @@ -67,6 +92,116 @@ register_bitfields! {u64, Bits_52 = 0b110 ], + /// Granule size for the TTBR1_EL1. + /// + /// 01 16KiB + /// 10 4KiB + /// 11 64KiB + /// + /// Other values are reserved. + /// + /// If the value is programmed to either a reserved value, or a size + /// that has not been implemented, then the hardware will treat the + /// field as if it has been programmed to an IMPLEMENTATION DEFINED + /// choice of the sizes that has been implemented for all purposes other + /// than the value read back from this register. + /// + /// It is IMPLEMENTATION DEFINED whether the value read back is the + /// value programmed or the value that corresponds to the size chosen. + TG1 OFFSET(30) NUMBITS(2) [ + KiB_4 = 0b10, + KiB_16 = 0b01, + KiB_64 = 0b11 + ], + + /// Shareability attribute for memory associated with translation table + /// walks using TTBR1_EL1. + /// + /// 00 Non-shareable + /// 10 Outer Shareable + /// 11 Inner Shareable + /// + /// Other values are reserved. + SH1 OFFSET(28) NUMBITS(2) [ + None = 0b00, + Outer = 0b10, + Inner = 0b11 + ], + + /// Outer cacheability attribute for memory associated with translation + /// table walks using TTBR1_EL1. + /// + /// 00 Normal memory, Outer Non-cacheable + /// + /// 01 Normal memory, Outer Write-Back Read-Allocate Write-Allocate + /// Cacheable + /// + /// 10 Normal memory, Outer Write-Through Read-Allocate No + /// Write-Allocate Cacheable + /// + /// 11 Normal memory, Outer Write-Back Read-Allocate No Write-Allocate + /// Cacheable + ORGN1 OFFSET(26) NUMBITS(2) [ + NonCacheable = 0b00, + WriteBack_ReadAlloc_WriteAlloc_Cacheable = 0b01, + WriteThrough_ReadAlloc_NoWriteAlloc_Cacheable = 0b10, + WriteBack_ReadAlloc_NoWriteAlloc_Cacheable = 0b11 + ], + + /// Inner cacheability attribute for memory associated with translation + /// table walks using TTBR1_EL1. + /// + /// 00 Normal memory, Inner Non-cacheable + /// + /// 01 Normal memory, Inner Write-Back Read-Allocate Write-Allocate + /// Cacheable + /// + /// 10 Normal memory, Inner Write-Through Read-Allocate No + /// Write-Allocate Cacheable + /// + /// 11 Normal memory, Inner Write-Back Read-Allocate No Write-Allocate + /// Cacheable + IRGN1 OFFSET(24) NUMBITS(2) [ + NonCacheable = 0b00, + WriteBack_ReadAlloc_WriteAlloc_Cacheable = 0b01, + WriteThrough_ReadAlloc_NoWriteAlloc_Cacheable = 0b10, + WriteBack_ReadAlloc_NoWriteAlloc_Cacheable = 0b11 + ], + + /// Translation table walk disable for translations using + /// TTBR1_EL1. This bit controls whether a translation table walk is + /// performed on a TLB miss, for an address that is translated using + /// TTBR1_EL1. The encoding of this bit is: + /// + /// 0 Perform translation table walks using TTBR1_EL1. + /// + /// 1 A TLB miss on an address that is translated using TTBR1_EL1 + /// generates a Translation fault. No translation table walk is + /// performed. + EPD1 OFFSET(23) NUMBITS(1) [ + EnableTTBR1Walks = 0, + DisableTTBR1Walks = 1 + ], + + /// Selects whether TTBR0_EL1 or TTBR1_EL1 defines the ASID. The encoding + /// of this bit is: + /// + /// 0 TTBR0_EL1.ASID defines the ASID. + /// + /// 1 TTBR1_EL1.ASID defines the ASID. + A1 OFFSET(22) NUMBITS(1) [ + UseTTBR0ASID = 0b0, + UseTTBR1ASID = 0b1 + ], + + /// The size offset of the memory region addressed by TTBR1_EL1. The + /// region size is 2^(64-T1SZ) bytes. + /// + /// The maximum and minimum possible values for T1SZ depend on the level + /// of translation table and the memory translation granule size, as + /// described in the AArch64 Virtual Memory System Architecture chapter. + T1SZ OFFSET(16) NUMBITS(6) [], + /// Granule size for the TTBR0_EL1. /// /// 00 4KiB diff --git a/crate/aarch64/src/regs/ttbr0_el1.rs b/crate/aarch64/src/regs/ttbr0_el1.rs index c111256..a29ff79 100644 --- a/crate/aarch64/src/regs/ttbr0_el1.rs +++ b/crate/aarch64/src/regs/ttbr0_el1.rs @@ -47,6 +47,11 @@ impl RegisterReadWrite for Reg { } impl Reg { + #[inline] + pub fn get_baddr(&self) -> u64 { + self.read(TTBR0_EL1::BADDR) << 1 + } + #[inline] pub fn set_baddr(&self, addr: u64) { self.write(TTBR0_EL1::BADDR.val(addr >> 1)); diff --git a/crate/aarch64/src/regs/ttbr1_el1.rs b/crate/aarch64/src/regs/ttbr1_el1.rs new file mode 100644 index 0000000..7df383c --- /dev/null +++ b/crate/aarch64/src/regs/ttbr1_el1.rs @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! Translation Table Base Register 1 - EL1 +//! +//! Holds the base address of the translation table for the initial lookup for +//! stage 1 of the translation of an address from the upper VA range in the +//! EL1&0 translation regime, and other information for this translation regime. + +use register::cpu::RegisterReadWrite; + +register_bitfields! {u64, + TTBR1_EL1 [ + /// An ASID for the translation table base address. The TCR_EL1.A1 field + /// selects either TTBR0_EL1.ASID or TTBR1_EL1.ASID. + /// + /// If the implementation has only 8 bits of ASID, then the upper 8 bits + /// of this field are RES 0. + ASID OFFSET(48) NUMBITS(16) [], + + /// Translation table base address + BADDR OFFSET(1) NUMBITS(47) [], + + /// Common not Private + CnP OFFSET(0) NUMBITS(1) [] + ] +} + +pub struct Reg; + +impl RegisterReadWrite for Reg { + sys_coproc_read_raw!(u64, "TTBR1_EL1"); + sys_coproc_write_raw!(u64, "TTBR1_EL1"); +} + +impl Reg { + #[inline] + pub fn get_baddr(&self) -> u64 { + self.read(TTBR1_EL1::BADDR) << 1 + } + + #[inline] + pub fn set_baddr(&self, addr: u64) { + self.write(TTBR1_EL1::BADDR.val(addr >> 1)); + } +} + +pub static TTBR1_EL1: Reg = Reg {}; diff --git a/kernel/src/arch/aarch64/interrupt/context.rs b/kernel/src/arch/aarch64/interrupt/context.rs index df2b5b3..5bff366 100644 --- a/kernel/src/arch/aarch64/interrupt/context.rs +++ b/kernel/src/arch/aarch64/interrupt/context.rs @@ -63,12 +63,12 @@ extern { struct ContextData { x19to29: [usize; 11], lr: usize, - ttbr0: usize, + ttbr1: usize, } impl ContextData { - fn new(ttbr0: usize) -> Self { - ContextData { lr: __trapret as usize, ttbr0, ..ContextData::default() } + fn new(ttbr1: usize) -> Self { + ContextData { lr: __trapret as usize, ttbr1, ..ContextData::default() } } } @@ -98,7 +98,7 @@ impl Context { stp x25, x26, [x8], #16 stp x27, x28, [x8], #16 stp x29, lr, [x8], #16 - mrs x9, ttbr0_el1 + mrs x9, ttbr1_el1 str x9, [x8], #8 ldr x8, [x1] @@ -111,7 +111,8 @@ impl Context { ldr x9, [x8], #8 mov sp, x8 - msr ttbr0_el1, x9 // set new page directory + msr ttbr1_el1, x9 // set new page directory + // TODO: with ASID we needn't flush TLB dsb ishst // ensure write has completed tlbi vmalle1is // invalidate the TLB entry for the entry that changes dsb ish // ensure TLB invalidation is complete @@ -126,21 +127,21 @@ impl Context { Context(0) } - pub unsafe fn new_kernel_thread(entry: extern fn(usize) -> !, arg: usize, kstack_top: usize, ttbr0: usize) -> Self { + pub unsafe fn new_kernel_thread(entry: extern fn(usize) -> !, arg: usize, kstack_top: usize, ttbr: usize) -> Self { InitStack { - context: ContextData::new(ttbr0), + context: ContextData::new(ttbr), tf: TrapFrame::new_kernel_thread(entry, arg, kstack_top), }.push_at(kstack_top) } - pub unsafe fn new_user_thread(entry_addr: usize, ustack_top: usize, kstack_top: usize, is32: bool, ttbr0: usize) -> Self { + pub unsafe fn new_user_thread(entry_addr: usize, ustack_top: usize, kstack_top: usize, is32: bool, ttbr: usize) -> Self { InitStack { - context: ContextData::new(ttbr0), + context: ContextData::new(ttbr), // TODO: set ASID tf: TrapFrame::new_user_thread(entry_addr, ustack_top), }.push_at(kstack_top) } - pub unsafe fn new_fork(tf: &TrapFrame, kstack_top: usize, ttbr0: usize) -> Self { + pub unsafe fn new_fork(tf: &TrapFrame, kstack_top: usize, ttbr: usize) -> Self { InitStack { - context: ContextData::new(ttbr0), + context: ContextData::new(ttbr), // TODO: set ASID tf: { let mut tf = tf.clone(); tf.x0 = 0; diff --git a/kernel/src/arch/aarch64/memory.rs b/kernel/src/arch/aarch64/memory.rs index 3f93e31..1c486d2 100644 --- a/kernel/src/arch/aarch64/memory.rs +++ b/kernel/src/arch/aarch64/memory.rs @@ -38,14 +38,25 @@ pub fn init_mmu_early() { // Configure various settings of stage 1 of the EL1 translation regime. let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); TCR_EL1.write( - TCR_EL1::TBI0::Ignored - + TCR_EL1::IPS.val(ips) - + TCR_EL1::TG0::KiB_4 // 4 KiB granule - + TCR_EL1::SH0::Inner - + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable - + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable - + TCR_EL1::EPD0::EnableTTBR0Walks - + TCR_EL1::T0SZ.val(16), // Start walks at level 2 + TCR_EL1::TBI1::Ignored + + TCR_EL1::TBI0::Ignored + + TCR_EL1::AS::Bits_16 + + TCR_EL1::IPS.val(ips) + + + TCR_EL1::TG1::KiB_4 + + TCR_EL1::SH1::Inner + + TCR_EL1::ORGN1::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL1::IRGN1::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL1::EPD1::EnableTTBR1Walks + + TCR_EL1::A1::UseTTBR1ASID + + TCR_EL1::T1SZ.val(16) + + + TCR_EL1::TG0::KiB_4 + + TCR_EL1::SH0::Inner + + TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable + + TCR_EL1::EPD0::EnableTTBR0Walks + + TCR_EL1::T0SZ.val(16), ); // Switch the MMU on. @@ -63,7 +74,7 @@ pub fn init_mmu_early() { fn init_frame_allocator() { use bit_allocator::BitAlloc; use core::ops::Range; - use consts::{MEMORY_OFFSET}; + use consts::MEMORY_OFFSET; let (start, end) = memory_map().expect("failed to find memory map"); let mut ba = FRAME_ALLOCATOR.lock(); @@ -116,7 +127,7 @@ fn remap_the_kernel() { /// /// This function is expected to return `Some` under all normal cirumstances. fn memory_map() -> Option<(usize, usize)> { - let binary_end = unsafe { _end as u32 }; + let binary_end = _end as u32; let mut atags: Atags = Atags::get(); while let Some(atag) = atags.next() { diff --git a/kernel/src/arch/aarch64/paging.rs b/kernel/src/arch/aarch64/paging.rs index c7c4e99..09606a6 100644 --- a/kernel/src/arch/aarch64/paging.rs +++ b/kernel/src/arch/aarch64/paging.rs @@ -5,7 +5,7 @@ use memory::{active_table, alloc_frame, alloc_stack, dealloc_frame}; use ucore_memory::memory_set::*; use ucore_memory::PAGE_SIZE; use ucore_memory::paging::*; -use aarch64::asm::{tlb_invalidate, tlb_invalidate_all, ttbr0_el1_read, ttbr0_el1_write}; +use aarch64::asm::{tlb_invalidate, tlb_invalidate_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, PageRange, PhysFrame as Frame, Size4KiB, Size2MiB}; @@ -122,7 +122,7 @@ pub fn setup_page_table(frame_lvl4: Frame, frame_lvl3: Frame, frame_lvl2: Frame) // } // } - ttbr0_el1_write(frame_lvl4); + ttbr_el1_write(0, frame_lvl4); tlb_invalidate_all(); } @@ -291,9 +291,9 @@ impl InactivePageTable for InactivePageTable0 { type Active = ActivePageTable; fn new() -> Self { - let mut pt = Self::new_bare(); - pt.map_kernel(); - pt + // 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 { @@ -308,7 +308,7 @@ impl InactivePageTable for InactivePageTable0 { } fn edit(&mut self, f: impl FnOnce(&mut Self::Active)) { - active_table().with_temporary_map(&ttbr0_el1_read().0, |active_table, p4_table: &mut Aarch64PageTable| { + 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 @@ -325,33 +325,34 @@ impl InactivePageTable for InactivePageTable0 { } unsafe fn activate(&self) { - let old_frame = ttbr0_el1_read().0; + let old_frame = ttbr_el1_read(0); let new_frame = self.p4_frame.clone(); - debug!("switch table {:?} -> {:?}", old_frame, new_frame); + debug!("switch TTBR0 {:?} -> {:?}", old_frame, new_frame); if old_frame != new_frame { - ttbr0_el1_write(new_frame); + ttbr_el1_write(0, new_frame); tlb_invalidate_all(); } } unsafe fn with(&self, f: impl FnOnce()) { - let old_frame = ttbr0_el1_read().0; + // Just need to switch the user TTBR + let old_frame = ttbr_el1_read(1); let new_frame = self.p4_frame.clone(); - debug!("switch table {:?} -> {:?}", old_frame, new_frame); + debug!("switch TTBR1 {:?} -> {:?}", old_frame, new_frame); if old_frame != new_frame { - ttbr0_el1_write(new_frame); + ttbr_el1_write(1, new_frame); tlb_invalidate_all(); } f(); - debug!("switch table {:?} -> {:?}", new_frame, old_frame); + debug!("switch TTBR1 {:?} -> {:?}", new_frame, old_frame); if old_frame != new_frame { - ttbr0_el1_write(old_frame); + ttbr_el1_write(1, old_frame); tlb_invalidate_all(); } } fn token(&self) -> usize { - self.p4_frame.start_address().as_u64() as usize // as CR3 + self.p4_frame.start_address().as_u64() as usize // as TTBRx_EL1 } fn alloc_frame() -> Option { diff --git a/kernel/src/consts.rs b/kernel/src/consts.rs index e879173..487b6a6 100644 --- a/kernel/src/consts.rs +++ b/kernel/src/consts.rs @@ -136,7 +136,7 @@ mod aarch64 { pub const KERNEL_PML4: usize = 0; pub const KERNEL_HEAP_SIZE: usize = 8 * 1024 * 1024; pub const MEMORY_OFFSET: usize = 0; - pub const USER_STACK_OFFSET: usize = 0x3000_0000; + pub const USER_STACK_OFFSET: usize = 0xffff_ffff_0000_0000; pub const USER_STACK_SIZE: usize = 1 * 1024 * 1024; pub const USER32_STACK_OFFSET: usize = USER_STACK_OFFSET; } diff --git a/kernel/src/process/context.rs b/kernel/src/process/context.rs index f20d209..088b1b8 100644 --- a/kernel/src/process/context.rs +++ b/kernel/src/process/context.rs @@ -142,6 +142,10 @@ fn memory_set_from<'a>(elf: &'a ElfFile<'a>) -> MemorySet { ProgramHeader::Ph32(ph) => (ph.virtual_addr as usize, ph.mem_size as usize, ph.flags), ProgramHeader::Ph64(ph) => (ph.virtual_addr as usize, ph.mem_size as usize, ph.flags), }; + + #[cfg(target_arch = "aarch64")] + assert_eq!((virt_addr >> 48), 0xffff, "Segment Fault"); + set.push(MemoryArea::new(virt_addr, virt_addr + mem_size, memory_attr_from(flags), "")); } set From 33d4b6975b54bdc77a14c1133cd6793cc976139e Mon Sep 17 00:00:00 2001 From: equation314 Date: Fri, 23 Nov 2018 15:23:00 +0800 Subject: [PATCH 11/39] aarch64/mmu: use DSB instead of TLB flush after modifying PTE --- crate/aarch64/src/barrier.rs | 4 ++++ crate/aarch64/src/paging/recursive.rs | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/crate/aarch64/src/barrier.rs b/crate/aarch64/src/barrier.rs index 0e48209..3211480 100644 --- a/crate/aarch64/src/barrier.rs +++ b/crate/aarch64/src/barrier.rs @@ -52,8 +52,12 @@ macro_rules! dmb_dsb { } pub struct SY; +pub struct ISH; +pub struct ISHST; dmb_dsb!(SY); +dmb_dsb!(ISH); +dmb_dsb!(ISHST); impl sealed::Isb for SY { #[inline(always)] diff --git a/crate/aarch64/src/paging/recursive.rs b/crate/aarch64/src/paging/recursive.rs index ee61cba..31039d5 100644 --- a/crate/aarch64/src/paging/recursive.rs +++ b/crate/aarch64/src/paging/recursive.rs @@ -1,13 +1,13 @@ #![cfg(target_arch = "aarch64")] -use asm::tlb_invalidate; use paging::{ frame_alloc::FrameAllocator, page_table::{FrameError, PageTable, PageTableEntry, PageTableFlags}, NotGiantPageSize, Page, PageSize, PhysFrame, Size4KiB, }; use paging::page_table::PageTableFlags as Flags; -use asm::ttbr_el1_read; +use asm::{ttbr_el1_read, tlb_invalidate}; +use barrier; use ux::u9; use addr::{PhysAddr, VirtAddr}; @@ -233,7 +233,7 @@ impl<'a> RecursivePageTable<'a> { let page_table_ptr = next_table_page.start_address().as_mut_ptr(); let page_table: &mut PageTable = unsafe { &mut *(page_table_ptr) }; if created { - tlb_invalidate(next_table_page.start_address()); + unsafe { barrier::dsb(barrier::ISHST); } page_table.zero(); } Ok(page_table) From de6354ddd338060726d6cce659b3b7796ed97146 Mon Sep 17 00:00:00 2001 From: equation314 Date: Fri, 23 Nov 2018 23:25:06 +0800 Subject: [PATCH 12/39] aarch64/mmu: use ASID to avoid flush TLB frequently in context switch --- crate/aarch64/src/asm.rs | 9 ++ kernel/src/arch/aarch64/interrupt/context.rs | 109 ++++++++++++++----- kernel/src/arch/aarch64/paging.rs | 10 +- 3 files changed, 92 insertions(+), 36 deletions(-) diff --git a/crate/aarch64/src/asm.rs b/crate/aarch64/src/asm.rs index a5b16d5..1c1ee6d 100644 --- a/crate/aarch64/src/asm.rs +++ b/crate/aarch64/src/asm.rs @@ -177,3 +177,12 @@ pub fn ttbr_el1_write(which: u8, frame: PhysFrame) { _ => {} }; } + +pub fn ttbr_el1_write_asid(which: u8, asid: u16, frame: PhysFrame) { + let baddr = frame.start_address().as_u64(); + match which { + 0 => TTBR0_EL1.write(TTBR0_EL1::ASID.val(asid as u64) + TTBR0_EL1::BADDR.val(baddr >> 1)), + 1 => TTBR1_EL1.write(TTBR1_EL1::ASID.val(asid as u64) + TTBR1_EL1::BADDR.val(baddr >> 1)), + _ => {} + }; +} diff --git a/kernel/src/arch/aarch64/interrupt/context.rs b/kernel/src/arch/aarch64/interrupt/context.rs index 5bff366..fe93839 100644 --- a/kernel/src/arch/aarch64/interrupt/context.rs +++ b/kernel/src/arch/aarch64/interrupt/context.rs @@ -1,12 +1,20 @@ //! TrapFrame and context definitions for aarch64. +extern crate aarch64; + +use spin::{Mutex}; +use aarch64::barrier; +use aarch64::addr::PhysAddr; +use aarch64::paging::PhysFrame; +use aarch64::asm::{tlb_invalidate_all, ttbr_el1_write_asid}; + #[repr(C)] #[derive(Default, Debug, Copy, Clone)] pub struct TrapFrame { pub elr: usize, pub spsr: usize, pub sp: usize, - pub tpidr: usize, + pub tpidr: usize, // currently unused // pub q0to31: [u128; 32], // disable SIMD/FP registers pub x1to29: [usize; 29], pub __reserved: usize, @@ -47,10 +55,14 @@ pub struct InitStack { } impl InitStack { - unsafe fn push_at(self, stack_top: usize) -> Context { + unsafe fn push_at(self, stack_top: usize, ttbr: usize) -> Context { let ptr = (stack_top as *mut Self).offset(-1); *ptr = self; - Context(ptr as usize) + Context { + stack_top: ptr as usize, + ttbr: PhysFrame::containing_address(PhysAddr::new(ttbr as u64)), + asid: Asid::default(), + } } } @@ -63,18 +75,20 @@ extern { struct ContextData { x19to29: [usize; 11], lr: usize, - ttbr1: usize, } impl ContextData { - fn new(ttbr1: usize) -> Self { - ContextData { lr: __trapret as usize, ttbr1, ..ContextData::default() } + fn new() -> Self { + ContextData { lr: __trapret as usize, ..ContextData::default() } } } - #[derive(Debug)] -pub struct Context(usize); +pub struct Context { + stack_top: usize, + ttbr: PhysFrame, + asid: Asid, +} impl Context { /// Switch to another kernel thread. @@ -86,10 +100,10 @@ impl Context { /// Pop all callee-saved registers, then return to the target. #[naked] #[inline(never)] - pub unsafe extern fn switch(&mut self, target: &mut Self) { + unsafe extern fn __switch(self_stack: &mut usize, target_stack: &mut usize) { asm!( " - mov x10, #-(13 * 8) + mov x10, #-(12 * 8) add x8, sp, x10 str x8, [x0] stp x19, x20, [x8], #16 // store callee-saved registers @@ -98,8 +112,6 @@ impl Context { stp x25, x26, [x8], #16 stp x27, x28, [x8], #16 stp x29, lr, [x8], #16 - mrs x9, ttbr1_el1 - str x9, [x8], #8 ldr x8, [x1] ldp x19, x20, [x8], #16 // restore callee-saved registers @@ -108,45 +120,88 @@ impl Context { ldp x25, x26, [x8], #16 ldp x27, x28, [x8], #16 ldp x29, lr, [x8], #16 - ldr x9, [x8], #8 mov sp, x8 - msr ttbr1_el1, x9 // set new page directory - // TODO: with ASID we needn't flush TLB - dsb ishst // ensure write has completed - tlbi vmalle1is // invalidate the TLB entry for the entry that changes - dsb ish // ensure TLB invalidation is complete - isb // synchronize context on this processor - str xzr, [x1] ret" : : : : "volatile" ); } + pub unsafe fn switch(&mut self, target: &mut Self) { + target.asid = ASID_ALLOCATOR.lock().alloc(target.asid); + + // with ASID we needn't flush TLB frequently + ttbr_el1_write_asid(1, target.asid.value, target.ttbr); + barrier::dsb(barrier::ISH); + Self::__switch(&mut self.stack_top, &mut target.stack_top); + } + pub unsafe fn null() -> Self { - Context(0) + Context { + stack_top: 0, + ttbr: PhysFrame::containing_address(PhysAddr::new(0)), + asid: Asid::default(), + } } pub unsafe fn new_kernel_thread(entry: extern fn(usize) -> !, arg: usize, kstack_top: usize, ttbr: usize) -> Self { InitStack { - context: ContextData::new(ttbr), + context: ContextData::new(), tf: TrapFrame::new_kernel_thread(entry, arg, kstack_top), - }.push_at(kstack_top) + }.push_at(kstack_top, ttbr) } pub unsafe fn new_user_thread(entry_addr: usize, ustack_top: usize, kstack_top: usize, is32: bool, ttbr: usize) -> Self { InitStack { - context: ContextData::new(ttbr), // TODO: set ASID + context: ContextData::new(), tf: TrapFrame::new_user_thread(entry_addr, ustack_top), - }.push_at(kstack_top) + }.push_at(kstack_top, ttbr) } pub unsafe fn new_fork(tf: &TrapFrame, kstack_top: usize, ttbr: usize) -> Self { InitStack { - context: ContextData::new(ttbr), // TODO: set ASID + context: ContextData::new(), tf: { let mut tf = tf.clone(); tf.x0 = 0; tf }, - }.push_at(kstack_top) + }.push_at(kstack_top, ttbr) } } + +const ASID_MASK: u16 = 0xffff; + +#[derive(Debug, Copy, Clone, Default)] +#[repr(C)] +struct Asid { + value: u16, + generation: u16, +} + +struct AsidAllocator(Asid); + +impl AsidAllocator { + fn new() -> Self { + AsidAllocator(Asid { value: 0, generation: 1 }) + } + + fn alloc(&mut self, old_asid: Asid) -> Asid { + if self.0.generation == old_asid.generation { + return old_asid; + } + + if self.0.value == ASID_MASK { + self.0.value = 0; + self.0.generation = self.0.generation.wrapping_add(1); + if self.0.generation == 0 { + self.0.generation += 1; + } + tlb_invalidate_all(); + } + self.0.value += 1; + return self.0; + } +} + +lazy_static! { + static ref ASID_ALLOCATOR: Mutex = Mutex::new(AsidAllocator::new()); +} diff --git a/kernel/src/arch/aarch64/paging.rs b/kernel/src/arch/aarch64/paging.rs index 09606a6..f1df051 100644 --- a/kernel/src/arch/aarch64/paging.rs +++ b/kernel/src/arch/aarch64/paging.rs @@ -259,15 +259,7 @@ impl Entry for PageEntry { fn set_swapped(&mut self, value: bool) { self.as_flags().set(EF::SWAPPED, value); } fn set_user(&mut self, value: bool) { self.as_flags().set(EF::USER_ACCESSIBLE, value); - if value { - let mut addr = self as *const _ as usize; - for _ in 0..3 { - // Upper level entry - addr = ((addr >> 9) & 0o777_777_777_7770) | (RECURSIVE_INDEX << 39); - // set USER_ACCESSIBLE - unsafe { (*(addr as *mut EF)).insert(EF::USER_ACCESSIBLE) }; - } - } + self.as_flags().set(EF::NONE_GLOBAL, value); // set non-global to use ASID } fn execute(&self) -> bool { !self.0.flags().contains(EF::UXN) } fn set_execute(&mut self, value: bool) { self.as_flags().set(EF::UXN, !value); } From f9e47b2fd8f142cbbbb4bd98f826a85149ff96ef Mon Sep 17 00:00:00 2001 From: equation314 Date: Sun, 25 Nov 2018 00:29:39 +0800 Subject: [PATCH 13/39] aarch64/mmu: refactor PageTableFlags --- crate/aarch64/src/paging/page_table.rs | 172 ++++++++++++++++++------- crate/aarch64/src/paging/recursive.rs | 7 +- kernel/src/arch/aarch64/paging.rs | 57 ++++---- kernel/src/consts.rs | 2 +- 4 files changed, 163 insertions(+), 75 deletions(-) diff --git a/crate/aarch64/src/paging/page_table.rs b/crate/aarch64/src/paging/page_table.rs index c06123e..2047e6b 100644 --- a/crate/aarch64/src/paging/page_table.rs +++ b/crate/aarch64/src/paging/page_table.rs @@ -1,12 +1,27 @@ use core::fmt; use core::ops::{Index, IndexMut}; -use super::{PageSize, PhysFrame, Size4KiB}; +use super::PhysFrame; use addr::PhysAddr; use usize_conversions::usize_from; use ux::*; +use register::FieldValue; +use register::cpu::RegisterReadWrite; + +/// Memory attribute fields mask +const MEMORY_ATTR_MASK: u64 = (MEMORY_ATTRIBUTE::SH.mask << MEMORY_ATTRIBUTE::SH.shift) + | (MEMORY_ATTRIBUTE::AttrIndx.mask << MEMORY_ATTRIBUTE::AttrIndx.shift); +/// Output address mask +const ADDR_MASK: u64 = 0x0000_ffff_ffff_f000; +/// Other flags mask +const FLAGS_MASK: u64 = !(MEMORY_ATTR_MASK | ADDR_MASK); + +/// Memory attribute fields +type PageTableAttributeFieldValue = FieldValue; +pub struct PageTableAttribute(PageTableAttributeFieldValue); + /// The error returned by the `PageTableEntry::frame` method. #[derive(Debug, Clone, Copy, PartialEq)] pub enum FrameError { @@ -24,6 +39,18 @@ pub struct PageTableEntry { pub entry: u64, } +impl RegisterReadWrite for PageTableEntry { + #[inline] + fn get(&self) -> u64 { + self.entry + } + + #[inline] + fn set(&self, value: u64) { + unsafe { *(&self.entry as *const u64 as *mut u64) = value } + } +} + impl PageTableEntry { /// Returns whether this entry is zero. pub fn is_unused(&self) -> bool { @@ -42,7 +69,16 @@ impl PageTableEntry { /// Returns the physical address mapped by this entry, might be zero. pub fn addr(&self) -> PhysAddr { - PhysAddr::new(self.entry & 0x0000_ffff_ffff_f000) + PhysAddr::new(self.entry & ADDR_MASK) + } + + /// Returns the memory attribute fields of this entry. + pub fn attr(&self) -> PageTableAttribute { + PageTableAttribute(PageTableAttributeFieldValue::new( + MEMORY_ATTR_MASK, + 0, + self.entry & MEMORY_ATTR_MASK, + )) } /// Returns the physical frame mapped by this entry. @@ -53,30 +89,36 @@ impl PageTableEntry { /// - `FrameError::HugeFrame` if the entry has the `HUGE_PAGE` flag set (for huge pages the /// `addr` function must be used) pub fn frame(&self) -> Result { - if !self.flags().contains(PageTableFlags::PRESENT) { + if !self.flags().contains(PageTableFlags::VALID) { Err(FrameError::FrameNotPresent) - } else if self.flags().contains(PageTableFlags::HUGE_PAGE) { + } else if !self.flags().contains(PageTableFlags::TABLE_OR_PAGE) { + // is a huge page (block) Err(FrameError::HugeFrame) } else { Ok(PhysFrame::containing_address(self.addr())) } } + /// Map the entry to the specified physical frame with the specified flags. + pub fn set_frame(&mut self, frame: PhysFrame, flags: PageTableFlags) { + // is not a huge page (block) + assert!(flags.contains(PageTableFlags::TABLE_OR_PAGE)); + self.set(frame.start_address().as_u64() | flags.bits()); + } + /// Map the entry to the specified physical address with the specified flags. - pub fn set_addr(&mut self, addr: PhysAddr, flags: PageTableFlags) { - assert!(addr.is_aligned(Size4KiB::SIZE)); - self.entry = (addr.as_u64()) | flags.bits(); + pub fn modify_addr(&mut self, addr: PhysAddr) { + self.entry = (self.entry & !ADDR_MASK) | addr.as_u64(); } - /// Map the entry to the specified physical frame with the specified flags. - pub fn set_frame(&mut self, frame: PhysFrame, flags: PageTableFlags) { - assert!(!flags.contains(PageTableFlags::HUGE_PAGE)); - self.set_addr(frame.start_address(), flags) + /// Sets the flags of this entry. + pub fn modify_flags(&mut self, flags: PageTableFlags) { + self.entry = (self.entry & !FLAGS_MASK) | flags.bits(); } /// Sets the flags of this entry. - pub fn set_flags(&mut self, flags: PageTableFlags) { - self.entry = self.addr().as_u64() | flags.bits(); + pub fn modify_attr(&mut self, attr: PageTableAttribute) { + self.entry = (self.entry & !MEMORY_ATTR_MASK) | attr.0.value; } } @@ -90,42 +132,78 @@ impl fmt::Debug for PageTableEntry { } } +register_bitfields! {u64, + // Memory attribute fields in the VMSAv8-64 translation table format descriptors (Page 2148~2152) + MEMORY_ATTRIBUTE [ + /// Shareability field + SH OFFSET(8) NUMBITS(2) [ + NonShareable = 0b00, + OuterShareable = 0b10, + InnerShareable = 0b11 + ], + + /// Memory attributes index into the MAIR_EL1 register + AttrIndx OFFSET(2) NUMBITS(3) [] + ] +} + bitflags! { /// Possible flags for a page table entry. pub struct PageTableFlags: u64 { - const ALL = 0xffffffff_ffffffff; - const TYPE_MASK = 3 << 0; - // const TYPE_FAULT = 0 << 0; - const TYPE_PAGE = 3 << 0; - const TABLE_BIT = 1 << 1; - // const BLOCK_BIT = 0 << 1; - const PAGE_BIT = 1 << 1; - - const PRESENT = 1 << 0; - const USER_ACCESSIBLE = 1 << 6; /* AP[1] */ - const RDONLY = 1 << 7; /* AP[2] */ - const SHARED = 3 << 8; /* SH[1:0], inner shareable */ - const BIT_8 = 1 << 8; - const BIT_9 = 1 << 9; + // const SHARED = 3 << 8; /* SH[1:0], inner shareable */ + // const BIT_8 = 1 << 8; + // const BIT_9 = 1 << 9; // pub const ATTRIB_SH_NON_SHAREABLE: usize = 0x0 << 8; - const OUTER_SHAREABLE = 0b10 << 8; - const INNER_SHAREABLE = 0b11 << 8; - - const ACCESSED = 1 << 10; /* AF, Access Flag */ - const NONE_GLOBAL = 1 << 11; /* None Global */ - const GLOBAL = (!(1 << 11)); - const DBM = 1 << 51; /* Dirty Bit Management */ - const WRITE = 1 << 51; /* DBM */ - const CONT = 1 << 52; /* Contiguous range */ - const PXN = 1 << 53; /* Privileged XN */ - const UXN = 1 << 54; /* User XN */ - const HYP_XN = 1 << 54; /* HYP XN */ + // const OUTER_SHAREABLE = 0b10 << 8; + // const INNER_SHAREABLE = 0b11 << 8; + + /// identifies whether the descriptor is valid + const VALID = 1 << 0; + /// the descriptor type + /// 0, Block + /// 1, Table/Page + const TABLE_OR_PAGE = 1 << 1; + /// Access permission: accessable at EL0 + const AP_EL0 = 1 << 6; + /// Access permission: read-only + const AP_RO = 1 << 7; + /// Access flag + const AF = 1 << 10; + /// not global bit + const nG = 1 << 11; + /// Dirty Bit Modifier + const DBM = 1 << 51; + + /// A hint bit indicating that the translation table entry is one of a contiguous set or + /// entries + const Contiguous = 1 << 52; + /// Privileged Execute-never + const PXN = 1 << 53; + /// Execute-never/Unprivileged execute-never + const XN = 1 << 54; + + /// Software Dirty Bit Modifier + const WRITE = 1 << 51; + /// Software dirty bit const DIRTY = 1 << 55; + /// Software swapped bit const SWAPPED = 1 << 56; - const HUGE_PAGE = 1 << 57; - const PROT_NONE = 1 << 58; + /// Software writable shared bit for COW + const WRITABLE_SHARED = 1 << 57; + /// Software readonly shared bit for COW + const READONLY_SHARED = 1 << 58; + + /// Privileged Execute-never for table descriptors + const PXNTable = 1 << 59; + /// Execute-never/Unprivileged execute-never for table descriptors + const XNTable = 1 << 60; + } +} +impl Default for PageTableFlags { + fn default() -> Self { + Self::VALID | Self::TABLE_OR_PAGE | Self::AF | Self::WRITE | Self::PXN | Self::XN } } @@ -151,12 +229,12 @@ impl PageTable { } } - /// Setup identity map: VirtPage at pagenumber -> PhysFrame at pagenumber - /// pn: pagenumber = addr>>12 in riscv32. - pub fn map_identity(&mut self, p4num: usize, flags: PageTableFlags) { - let entry = self.entries[p4num].clone(); - self.entries[p4num].set_addr(entry.addr(), flags); - } + // Setup identity map: VirtPage at pagenumber -> PhysFrame at pagenumber + // pn: pagenumber = addr>>12 in riscv32. + // pub fn map_identity(&mut self, p4num: usize, flags: PageTableFlags) { + // let entry = self.entries[p4num].clone(); + // self.entries[p4num].set_addr(entry.addr(), flags); + // } } impl Index for PageTable { diff --git a/crate/aarch64/src/paging/recursive.rs b/crate/aarch64/src/paging/recursive.rs index 31039d5..e878f98 100644 --- a/crate/aarch64/src/paging/recursive.rs +++ b/crate/aarch64/src/paging/recursive.rs @@ -218,7 +218,7 @@ impl<'a> RecursivePageTable<'a> { if entry.is_unused() { if let Some(frame) = allocator.alloc() { - entry.set_frame(frame, Flags::PRESENT | Flags::WRITE | Flags::ACCESSED | Flags::PAGE_BIT); + entry.set_frame(frame, Flags::default()); created = true; } else { return Err(MapToError::FrameAllocationFailed); @@ -226,7 +226,8 @@ impl<'a> RecursivePageTable<'a> { } else { created = false; } - if entry.flags().contains(Flags::HUGE_PAGE) { + // is a huge page (block) + if !entry.flags().contains(Flags::TABLE_OR_PAGE) { return Err(MapToError::ParentEntryHugePage); } @@ -382,7 +383,7 @@ impl<'a> Mapper for RecursivePageTable<'a> { return Err(FlagUpdateError::PageNotMapped); } - p1[page.p1_index()].set_flags(flags); + p1[page.p1_index()].modify_flags(flags); Ok(MapperFlush::new(page)) } diff --git a/kernel/src/arch/aarch64/paging.rs b/kernel/src/arch/aarch64/paging.rs index f1df051..9986faa 100644 --- a/kernel/src/arch/aarch64/paging.rs +++ b/kernel/src/arch/aarch64/paging.rs @@ -8,7 +8,7 @@ use ucore_memory::paging::*; use aarch64::asm::{tlb_invalidate, tlb_invalidate_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, PageRange, PhysFrame as Frame, Size4KiB, Size2MiB}; +use aarch64::paging::{FrameAllocator, FrameDeallocator, Page, PhysFrame as Frame, Size4KiB, Size2MiB}; register_bitfields! {u64, // AArch64 Reference Manual page 2150 @@ -167,7 +167,7 @@ impl PageTable for ActivePageTable { type Entry = PageEntry; fn map(&mut self, addr: usize, target: usize) -> &mut PageEntry { - let flags = EF::PRESENT | EF::WRITE | EF::ACCESSED | EF::UXN | EF::PAGE_BIT; + let flags = EF::default(); self.0.map_to(Page::of_addr(addr), Frame::of_addr(target), flags, &mut FrameAllocatorForAarch64) .unwrap().flush(); self.get_entry(addr) @@ -224,49 +224,58 @@ impl Entry for PageEntry { tlb_invalidate(addr); } - fn present(&self) -> bool { self.0.flags().contains(EF::PRESENT) } - fn accessed(&self) -> bool { self.0.flags().contains(EF::ACCESSED) } + 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 clear_accessed(&mut self) { self.as_flags().remove(EF::ACCESSED); } + 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::RDONLY); + self.as_flags().insert(EF::AP_RO); } fn set_writable(&mut self, value: bool) { - self.as_flags().set(EF::RDONLY, !value); + self.as_flags().set(EF::AP_RO, !value); self.as_flags().set(EF::WRITE, value); } - fn set_present(&mut self, value: bool) { self.as_flags().set(EF::PRESENT, value); } + 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) { - let flags = self.0.flags(); - self.0.set_addr(PhysAddr::new(target as u64), flags); + self.0.modify_addr(PhysAddr::new(target as u64)); } - fn writable_shared(&self) -> bool { self.0.flags().contains(EF::BIT_9) } - fn readonly_shared(&self) -> bool { self.0.flags().contains(EF::BIT_9) } + 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) { let flags = self.as_flags(); - flags.set(EF::BIT_8, writable); - flags.set(EF::BIT_9, writable); + flags.set(EF::WRITABLE_SHARED, writable); + flags.set(EF::READONLY_SHARED, !writable); } - fn clear_shared(&mut self) { self.as_flags().remove(EF::BIT_8 | EF::BIT_9); } - fn user(&self) -> bool { self.0.flags().contains(EF::USER_ACCESSIBLE) } + 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) { - self.as_flags().set(EF::USER_ACCESSIBLE, value); - self.as_flags().set(EF::NONE_GLOBAL, value); // set non-global to use ASID + 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 { + match self.user() { + true => !self.0.flags().contains(EF::XN), + false => !self.0.flags().contains(EF::PXN), + } + } + fn set_execute(&mut self, value: bool) { + match self.user() { + true => self.as_flags().set(EF::XN, !value), + false => self.as_flags().set(EF::PXN, !value), + } } - fn execute(&self) -> bool { !self.0.flags().contains(EF::UXN) } - fn set_execute(&mut self, value: bool) { self.as_flags().set(EF::UXN, !value); } } impl PageEntry { - fn read_only(&self) -> bool { self.0.flags().contains(EF::RDONLY) } + 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 { @@ -294,7 +303,7 @@ impl InactivePageTable for InactivePageTable0 { 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::PRESENT | EF::WRITE | EF::ACCESSED | EF::PAGE_BIT); + table[RECURSIVE_INDEX].set_frame(frame.clone(), EF::default()); }); InactivePageTable0 { p4_frame: frame } } @@ -304,7 +313,7 @@ impl InactivePageTable for InactivePageTable0 { let backup = p4_table[RECURSIVE_INDEX].clone(); // overwrite recursive mapping - p4_table[RECURSIVE_INDEX].set_frame(self.p4_frame.clone(), EF::PRESENT | EF::WRITE | EF::ACCESSED | EF::PAGE_BIT); + p4_table[RECURSIVE_INDEX].set_frame(self.p4_frame.clone(), EF::default()); tlb_invalidate_all(); // execute f in the new context @@ -367,7 +376,7 @@ impl InactivePageTable0 { assert!(!e0.is_unused()); self.edit(|_| { - table[KERNEL_PML4].set_addr(e0.addr(), e0.flags() & EF::GLOBAL); + table[KERNEL_PML4].set_frame(Frame::containing_address(e0.addr()), EF::default()); }); } } diff --git a/kernel/src/consts.rs b/kernel/src/consts.rs index 487b6a6..b2be26c 100644 --- a/kernel/src/consts.rs +++ b/kernel/src/consts.rs @@ -136,7 +136,7 @@ mod aarch64 { pub const KERNEL_PML4: usize = 0; pub const KERNEL_HEAP_SIZE: usize = 8 * 1024 * 1024; pub const MEMORY_OFFSET: usize = 0; - pub const USER_STACK_OFFSET: usize = 0xffff_ffff_0000_0000; + 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; } From a8b334123adf8f3b56e097efbfde194c70e8d499 Mon Sep 17 00:00:00 2001 From: equation314 Date: Sun, 25 Nov 2018 17:58:17 +0800 Subject: [PATCH 14/39] aarch64/mmu: add memory region attribute config --- crate/aarch64/src/paging/memory_attribute.rs | 64 ++++++++++ crate/aarch64/src/paging/mod.rs | 6 + crate/aarch64/src/paging/page_table.rs | 51 ++++---- crate/aarch64/src/paging/recursive.rs | 11 +- crate/aarch64/src/regs/mair_el1.rs | 67 ++++------- crate/aarch64/src/regs/mod.rs | 2 +- kernel/src/arch/aarch64/board/raspi3/mod.rs | 2 + kernel/src/arch/aarch64/memory.rs | 13 +- kernel/src/arch/aarch64/paging.rs | 119 ++++--------------- 9 files changed, 155 insertions(+), 180 deletions(-) create mode 100644 crate/aarch64/src/paging/memory_attribute.rs diff --git a/crate/aarch64/src/paging/memory_attribute.rs b/crate/aarch64/src/paging/memory_attribute.rs new file mode 100644 index 0000000..beb9b69 --- /dev/null +++ b/crate/aarch64/src/paging/memory_attribute.rs @@ -0,0 +1,64 @@ +//!Memory region attributes (D4.5, page 2174) + +use super::{PageTableAttribute, MEMORY_ATTRIBUTE}; +use regs::*; + +pub trait MairType { + const INDEX: u64; + + #[inline] + fn config_value() -> u64; + + #[inline] + fn attr_value() -> PageTableAttribute; +} + +pub enum MairDevice {} +pub enum MairNormal {} +pub enum MairNormalNonCacheable {} + +impl MairType for MairDevice { + const INDEX: u64 = 0; + + #[inline] + fn config_value() -> u64 { + (MAIR_ATTR::Attr_HIGH::Device + MAIR_ATTR::Attr_LOW_DEVICE::Device_nGnRE).value + } + + #[inline] + fn attr_value() -> PageTableAttribute { + MEMORY_ATTRIBUTE::SH::OuterShareable + MEMORY_ATTRIBUTE::AttrIndx.val(Self::INDEX) + } +} + +impl MairType for MairNormal { + const INDEX: u64 = 1; + + #[inline] + fn config_value() -> u64 { + (MAIR_ATTR::Attr_HIGH::Memory_OuterWriteBack_NonTransient_ReadAlloc_WriteAlloc + + MAIR_ATTR::Attr_LOW_MEMORY::InnerWriteBack_NonTransient_ReadAlloc_WriteAlloc) + .value + } + + #[inline] + fn attr_value() -> PageTableAttribute { + MEMORY_ATTRIBUTE::SH::InnerShareable + MEMORY_ATTRIBUTE::AttrIndx.val(Self::INDEX) + } +} + +impl MairType for MairNormalNonCacheable { + const INDEX: u64 = 2; + + #[inline] + fn config_value() -> u64 { + (MAIR_ATTR::Attr_HIGH::Memory_OuterNonCacheable + + MAIR_ATTR::Attr_LOW_MEMORY::InnerNonCacheable) + .value + } + + #[inline] + fn attr_value() -> PageTableAttribute { + MEMORY_ATTRIBUTE::SH::NonShareable + MEMORY_ATTRIBUTE::AttrIndx.val(Self::INDEX) + } +} diff --git a/crate/aarch64/src/paging/mod.rs b/crate/aarch64/src/paging/mod.rs index c73d5b4..552d6f6 100644 --- a/crate/aarch64/src/paging/mod.rs +++ b/crate/aarch64/src/paging/mod.rs @@ -18,6 +18,8 @@ mod frame_alloc; mod page_table; mod recursive; +pub mod memory_attribute; + /// Trait for abstracting over the three possible block/page sizes on aarch64, 4KiB, 2MiB, 1GiB. pub trait PageSize: Copy + Eq + PartialOrd + Ord { /// The page size in bytes. @@ -366,6 +368,10 @@ impl PhysFrame { pub fn of_addr(address: usize) -> Self { Self::containing_address(PhysAddr::new(address as u64)) } + + pub fn range_of(begin: usize, end: usize) -> PhysFrameRange { + Self::range(Self::of_addr(begin), Self::of_addr(end - 1) + 1) + } } impl fmt::Debug for PhysFrame { diff --git a/crate/aarch64/src/paging/page_table.rs b/crate/aarch64/src/paging/page_table.rs index 2047e6b..6d33fa6 100644 --- a/crate/aarch64/src/paging/page_table.rs +++ b/crate/aarch64/src/paging/page_table.rs @@ -1,7 +1,7 @@ use core::fmt; use core::ops::{Index, IndexMut}; -use super::PhysFrame; +use super::{PhysFrame, PageSize}; use addr::PhysAddr; use usize_conversions::usize_from; @@ -19,8 +19,7 @@ const ADDR_MASK: u64 = 0x0000_ffff_ffff_f000; const FLAGS_MASK: u64 = !(MEMORY_ATTR_MASK | ADDR_MASK); /// Memory attribute fields -type PageTableAttributeFieldValue = FieldValue; -pub struct PageTableAttribute(PageTableAttributeFieldValue); +pub type PageTableAttribute = FieldValue; /// The error returned by the `PageTableEntry::frame` method. #[derive(Debug, Clone, Copy, PartialEq)] @@ -53,32 +52,33 @@ impl RegisterReadWrite for PageTableEntry { impl PageTableEntry { /// Returns whether this entry is zero. + #[inline] pub fn is_unused(&self) -> bool { self.entry == 0 } /// Sets this entry to zero. + #[inline] pub fn set_unused(&mut self) { self.entry = 0; } /// Returns the flags of this entry. + #[inline] pub fn flags(&self) -> PageTableFlags { PageTableFlags::from_bits_truncate(self.entry) } /// Returns the physical address mapped by this entry, might be zero. + #[inline] pub fn addr(&self) -> PhysAddr { PhysAddr::new(self.entry & ADDR_MASK) } /// Returns the memory attribute fields of this entry. + #[inline] pub fn attr(&self) -> PageTableAttribute { - PageTableAttribute(PageTableAttributeFieldValue::new( - MEMORY_ATTR_MASK, - 0, - self.entry & MEMORY_ATTR_MASK, - )) + PageTableAttribute::new(MEMORY_ATTR_MASK, 0, self.entry & MEMORY_ATTR_MASK) } /// Returns the physical frame mapped by this entry. @@ -99,11 +99,19 @@ impl PageTableEntry { } } - /// Map the entry to the specified physical frame with the specified flags. - pub fn set_frame(&mut self, frame: PhysFrame, flags: PageTableFlags) { - // is not a huge page (block) + /// Map the entry to the specified physical frame with the specified flags and memory attribute. + pub fn set_frame(&mut self, frame: PhysFrame, flags: PageTableFlags, attr: PageTableAttribute) { + // is not a block assert!(flags.contains(PageTableFlags::TABLE_OR_PAGE)); - self.set(frame.start_address().as_u64() | flags.bits()); + self.set(frame.start_address().as_u64() | flags.bits() | attr.value); + } + + /// The descriptor gives the base address of a block of memory, and the attributes for that + /// memory region. + pub fn set_block(&mut self, addr: PhysAddr, flags: PageTableFlags, attr: PageTableAttribute) { + // is a block + assert!(!flags.contains(PageTableFlags::TABLE_OR_PAGE)); + self.set(addr.align_down(S::SIZE).as_u64() | flags.bits() | attr.value); } /// Map the entry to the specified physical address with the specified flags. @@ -118,7 +126,7 @@ impl PageTableEntry { /// Sets the flags of this entry. pub fn modify_attr(&mut self, attr: PageTableAttribute) { - self.entry = (self.entry & !MEMORY_ATTR_MASK) | attr.0.value; + self.entry = (self.entry & !MEMORY_ATTR_MASK) | attr.value; } } @@ -128,6 +136,7 @@ impl fmt::Debug for PageTableEntry { f.field("value", &self.entry); f.field("addr", &self.addr()); f.field("flags", &self.flags()); + f.field("attr", &self.attr().value); f.finish() } } @@ -150,14 +159,6 @@ register_bitfields! {u64, bitflags! { /// Possible flags for a page table entry. pub struct PageTableFlags: u64 { - // const SHARED = 3 << 8; /* SH[1:0], inner shareable */ - // const BIT_8 = 1 << 8; - // const BIT_9 = 1 << 9; - - // pub const ATTRIB_SH_NON_SHAREABLE: usize = 0x0 << 8; - // const OUTER_SHAREABLE = 0b10 << 8; - // const INNER_SHAREABLE = 0b11 << 8; - /// identifies whether the descriptor is valid const VALID = 1 << 0; /// the descriptor type @@ -202,6 +203,7 @@ bitflags! { } impl Default for PageTableFlags { + #[inline] fn default() -> Self { Self::VALID | Self::TABLE_OR_PAGE | Self::AF | Self::WRITE | Self::PXN | Self::XN } @@ -228,13 +230,6 @@ impl PageTable { entry.set_unused(); } } - - // Setup identity map: VirtPage at pagenumber -> PhysFrame at pagenumber - // pn: pagenumber = addr>>12 in riscv32. - // pub fn map_identity(&mut self, p4num: usize, flags: PageTableFlags) { - // let entry = self.entries[p4num].clone(); - // self.entries[p4num].set_addr(entry.addr(), flags); - // } } impl Index for PageTable { diff --git a/crate/aarch64/src/paging/recursive.rs b/crate/aarch64/src/paging/recursive.rs index e878f98..a1633ff 100644 --- a/crate/aarch64/src/paging/recursive.rs +++ b/crate/aarch64/src/paging/recursive.rs @@ -5,7 +5,7 @@ use paging::{ page_table::{FrameError, PageTable, PageTableEntry, PageTableFlags}, NotGiantPageSize, Page, PageSize, PhysFrame, Size4KiB, }; -use paging::page_table::PageTableFlags as Flags; +use paging::{page_table::PageTableFlags as Flags, PageTableAttribute, memory_attribute::*}; use asm::{ttbr_el1_read, tlb_invalidate}; use barrier; use ux::u9; @@ -46,6 +46,7 @@ pub trait Mapper { page: Page, frame: PhysFrame, flags: PageTableFlags, + attr: PageTableAttribute, allocator: &mut A, ) -> Result, MapToError> where @@ -71,6 +72,7 @@ pub trait Mapper { &mut self, frame: PhysFrame, flags: PageTableFlags, + attr: PageTableAttribute, allocator: &mut A, ) -> Result, MapToError> where @@ -79,7 +81,7 @@ pub trait Mapper { Self: Mapper, { let page = Page::containing_address(VirtAddr::new(frame.start_address().as_u64())); - self.map_to(page, frame, flags, allocator) + self.map_to(page, frame, flags, attr, allocator) } } @@ -218,7 +220,7 @@ impl<'a> RecursivePageTable<'a> { if entry.is_unused() { if let Some(frame) = allocator.alloc() { - entry.set_frame(frame, Flags::default()); + entry.set_frame(frame, Flags::default(), MairNormal::attr_value()); created = true; } else { return Err(MapToError::FrameAllocationFailed); @@ -289,6 +291,7 @@ impl<'a> Mapper for RecursivePageTable<'a> { page: Page, frame: PhysFrame, flags: PageTableFlags, + attr: PageTableAttribute, allocator: &mut A, ) -> Result, MapToError> where @@ -309,7 +312,7 @@ impl<'a> Mapper for RecursivePageTable<'a> { if !p1[page.p1_index()].is_unused() { return Err(MapToError::PageAlreadyMapped); } - p1[page.p1_index()].set_frame(frame, flags); + p1[page.p1_index()].set_frame(frame, flags, attr); Ok(MapperFlush::new(page)) } diff --git a/crate/aarch64/src/regs/mair_el1.rs b/crate/aarch64/src/regs/mair_el1.rs index 2c7c7da..dbd7f9d 100644 --- a/crate/aarch64/src/regs/mair_el1.rs +++ b/crate/aarch64/src/regs/mair_el1.rs @@ -24,72 +24,49 @@ use register::cpu::RegisterReadWrite; register_bitfields! {u64, MAIR_EL1 [ - // TODO: Macrofy this - /// Attribute 7 - Attr7_HIGH OFFSET(60) NUMBITS(4) [], - Attr7_LOW_DEVICE OFFSET(56) NUMBITS(4) [], - Attr7_LOW_MEMORY OFFSET(56) NUMBITS(4) [], + Attr7 OFFSET(56) NUMBITS(8) [], /// Attribute 6 - Attr6_HIGH OFFSET(52) NUMBITS(4) [], - Attr6_LOW_DEVICE OFFSET(48) NUMBITS(4) [], - Attr6_LOW_MEMORY OFFSET(48) NUMBITS(4) [], + Attr6 OFFSET(48) NUMBITS(8) [], /// Attribute 5 - Attr5_HIGH OFFSET(44) NUMBITS(4) [], - Attr5_LOW_DEVICE OFFSET(40) NUMBITS(4) [], - Attr5_LOW_MEMORY OFFSET(40) NUMBITS(4) [], + Attr5 OFFSET(40) NUMBITS(8) [], /// Attribute 4 - Attr4_HIGH OFFSET(36) NUMBITS(4) [], - Attr4_LOW_DEVICE OFFSET(32) NUMBITS(4) [], - Attr4_LOW_MEMORY OFFSET(32) NUMBITS(4) [], + Attr4 OFFSET(32) NUMBITS(8) [], /// Attribute 3 - Attr3_HIGH OFFSET(28) NUMBITS(4) [], - Attr3_LOW_DEVICE OFFSET(24) NUMBITS(4) [], - Attr3_LOW_MEMORY OFFSET(24) NUMBITS(4) [], + Attr3 OFFSET(24) NUMBITS(8) [], /// Attribute 2 - Attr2_HIGH OFFSET(20) NUMBITS(4) [ - Device = 0b0000, - Memory_OuterNonCacheable = 0b0100, - Memory_OuterWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 - ], - Attr2_LOW_DEVICE OFFSET(16) NUMBITS(4) [ - Device_nGnRE = 0b0100 - ], - Attr2_LOW_MEMORY OFFSET(16) NUMBITS(4) [ - InnerNonCacheable = 0b0100, - InnerWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 - ], + Attr2 OFFSET(16) NUMBITS(8) [], /// Attribute 1 - Attr1_HIGH OFFSET(12) NUMBITS(4) [ - Device = 0b0000, - Memory_OuterNonCacheable = 0b0100, - Memory_OuterWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 - ], - Attr1_LOW_DEVICE OFFSET(8) NUMBITS(4) [ - Device_nGnRE = 0b0100 - ], - Attr1_LOW_MEMORY OFFSET(8) NUMBITS(4) [ - InnerNonCacheable = 0b0100, - InnerWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 - ], + Attr1 OFFSET(8) NUMBITS(8) [], /// Attribute 0 - Attr0_HIGH OFFSET(4) NUMBITS(4) [ + Attr0 OFFSET(0) NUMBITS(8) [] + ] +} + +register_bitfields! {u64, + MAIR_ATTR [ + Attr_HIGH OFFSET(4) NUMBITS(4) [ Device = 0b0000, Memory_OuterNonCacheable = 0b0100, + Memory_OuterWriteThrough_NonTransient_ReadAlloc_WriteAlloc = 0b1011, Memory_OuterWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 ], - Attr0_LOW_DEVICE OFFSET(0) NUMBITS(4) [ - Device_nGnRE = 0b0100 + Attr_LOW_DEVICE OFFSET(0) NUMBITS(4) [ + Device_nGnRnE = 0b0000, + Device_nGnRE = 0b0100, + Device_nGRE = 0b1000, + Device_GRE = 0b1100 ], - Attr0_LOW_MEMORY OFFSET(0) NUMBITS(4) [ + Attr_LOW_MEMORY OFFSET(0) NUMBITS(4) [ InnerNonCacheable = 0b0100, + InnerWriteThrough_NonTransient_ReadAlloc_WriteAlloc = 0b1011, InnerWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 ] ] diff --git a/crate/aarch64/src/regs/mod.rs b/crate/aarch64/src/regs/mod.rs index 5a7e556..f69f0c4 100644 --- a/crate/aarch64/src/regs/mod.rs +++ b/crate/aarch64/src/regs/mod.rs @@ -40,7 +40,7 @@ pub use self::daif::DAIF; pub use self::elr_el2::ELR_EL2; pub use self::hcr_el2::HCR_EL2; pub use self::id_aa64mmfr0_el1::ID_AA64MMFR0_EL1; -pub use self::mair_el1::MAIR_EL1; +pub use self::mair_el1::{MAIR_EL1, MAIR_ATTR}; pub use self::mpidr_el1::MPIDR_EL1; pub use self::sctlr_el1::SCTLR_EL1; pub use self::sp::SP; diff --git a/kernel/src/arch/aarch64/board/raspi3/mod.rs b/kernel/src/arch/aarch64/board/raspi3/mod.rs index 2d13315..0db5133 100644 --- a/kernel/src/arch/aarch64/board/raspi3/mod.rs +++ b/kernel/src/arch/aarch64/board/raspi3/mod.rs @@ -6,6 +6,8 @@ pub mod irq; pub mod timer; pub mod serial; +pub const IO_BASE: usize = bcm2837::IO_BASE; + pub fn init() { assert_has_not_been_called!("board::init must be called only once"); diff --git a/kernel/src/arch/aarch64/memory.rs b/kernel/src/arch/aarch64/memory.rs index 1c486d2..7536dc4 100644 --- a/kernel/src/arch/aarch64/memory.rs +++ b/kernel/src/arch/aarch64/memory.rs @@ -3,7 +3,8 @@ use ucore_memory::PAGE_SIZE; use memory::{FRAME_ALLOCATOR, init_heap, MemoryArea, MemoryAttr, MemorySet, Stack}; use super::atags::atags::Atags; -use aarch64::{barrier, regs::*, addr::*, paging::PhysFrame as Frame}; +use aarch64::{barrier, regs::*, addr::*}; +use aarch64::paging::{PhysFrame as Frame, memory_attribute::*}; /// Memory initialization. pub fn init() { @@ -28,13 +29,11 @@ pub fn init_mmu_early() { // device. MAIR_EL1.write( - // Attribute 1 - MAIR_EL1::Attr1_HIGH::Device - + MAIR_EL1::Attr1_LOW_DEVICE::Device_nGnRE - // Attribute 0 - + MAIR_EL1::Attr0_HIGH::Memory_OuterWriteBack_NonTransient_ReadAlloc_WriteAlloc - + MAIR_EL1::Attr0_LOW_MEMORY::InnerWriteBack_NonTransient_ReadAlloc_WriteAlloc, + MAIR_EL1::Attr0.val(MairDevice::config_value()) + + MAIR_EL1::Attr1.val(MairNormal::config_value()) + + MAIR_EL1::Attr2.val(MairNormalNonCacheable::config_value()), ); + // Configure various settings of stage 1 of the EL1 translation regime. let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange); TCR_EL1.write( diff --git a/kernel/src/arch/aarch64/paging.rs b/kernel/src/arch/aarch64/paging.rs index 9986faa..f11820c 100644 --- a/kernel/src/arch/aarch64/paging.rs +++ b/kernel/src/arch/aarch64/paging.rs @@ -8,62 +8,8 @@ use ucore_memory::paging::*; use aarch64::asm::{tlb_invalidate, tlb_invalidate_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}; - -register_bitfields! {u64, - // AArch64 Reference Manual page 2150 - STAGE1_DESCRIPTOR [ - /// Execute-never - XN OFFSET(54) NUMBITS(1) [ - False = 0, - True = 1 - ], - - /// Various address fields, depending on use case - LVL4_OUTPUT_ADDR_4KiB OFFSET(39) NUMBITS(9) [], // [47:39] - LVL3_OUTPUT_ADDR_4KiB OFFSET(30) NUMBITS(18) [], // [47:30] - LVL2_OUTPUT_ADDR_4KiB OFFSET(21) NUMBITS(27) [], // [47:21] - NEXT_LVL_TABLE_ADDR_4KiB OFFSET(12) NUMBITS(36) [], // [47:12] - - /// Access flag - AF OFFSET(10) NUMBITS(1) [ - False = 0, - True = 1 - ], - - /// Shareability field - SH OFFSET(8) NUMBITS(2) [ - OuterShareable = 0b10, - InnerShareable = 0b11 - ], - - /// Access Permissions - AP OFFSET(6) NUMBITS(2) [ - RW_EL1 = 0b00, - RW_EL1_EL0 = 0b01, - RO_EL1 = 0b10, - RO_EL1_EL0 = 0b11 - ], - - /// Memory attributes index into the MAIR_EL1 register - AttrIndx OFFSET(2) NUMBITS(3) [], - - TYPE OFFSET(1) NUMBITS(1) [ - Block = 0, - Table = 1 - ], - - VALID OFFSET(0) NUMBITS(1) [ - False = 0, - True = 1 - ] - ] -} - -mod mair { - pub const NORMAL: u64 = 0; - pub const DEVICE: u64 = 1; -} +use aarch64::paging::{FrameAllocator, FrameDeallocator, Page, PhysFrame as Frame, Size4KiB, Size2MiB, Size1GiB}; +use aarch64::paging::memory_attribute::*; // need 3 page pub fn setup_page_table(frame_lvl4: Frame, frame_lvl3: Frame, frame_lvl2: Frame) { @@ -74,34 +20,24 @@ pub fn setup_page_table(frame_lvl4: Frame, frame_lvl3: Frame, frame_lvl2: Frame) p3.zero(); p2.zero(); - // Fill the rest of the LVL2 (2MiB) entries as block - // descriptors. Differentiate between normal and device mem. - const MMIO_BASE: u64 = 0x3F000000; - let mmio_base: u64 = MMIO_BASE >> 21; - let mut common = STAGE1_DESCRIPTOR::VALID::True - + STAGE1_DESCRIPTOR::TYPE::Block - + STAGE1_DESCRIPTOR::AP::RW_EL1 - + STAGE1_DESCRIPTOR::AF::True; - // + STAGE1_DESCRIPTOR::XN::True; - - for i in 0..512 { - let j: u64 = i as u64; - - let mem_attr = if j >= mmio_base { - STAGE1_DESCRIPTOR::SH::OuterShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::DEVICE) - } else { - STAGE1_DESCRIPTOR::SH::InnerShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL) - }; + let (start_addr, end_addr) = (0, 0x40000000); + let block_flags = EF::VALID | EF::AF | EF::WRITE | EF::XN; + for page in Page::::range_of(start_addr, end_addr) { + let paddr = PhysAddr::new(page.start_address().as_u64()); - p2[i].entry = (common + mem_attr + STAGE1_DESCRIPTOR::LVL2_OUTPUT_ADDR_4KiB.val(j)).value; + use arch::board::IO_BASE; + if paddr.as_u64() >= IO_BASE as u64 { + p2[page.p2_index()].set_block::(paddr, block_flags | EF::PXN, MairDevice::attr_value()); + } else { + p2[page.p2_index()].set_block::(paddr, block_flags, MairNormal::attr_value()); + } } - common = common + STAGE1_DESCRIPTOR::SH::InnerShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::NORMAL); + p3[0].set_frame(frame_lvl2, EF::default(), MairNormal::attr_value()); + p3[1].set_block::(PhysAddr::new(0x40000000), block_flags | EF::PXN, MairDevice::attr_value()); - p3[0].entry = (common + STAGE1_DESCRIPTOR::TYPE::Table + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(frame_lvl2.start_address().as_u64() >> 12)).value; - p3[1].entry = (common + STAGE1_DESCRIPTOR::LVL3_OUTPUT_ADDR_4KiB.val(1)).value; - p4[0].entry = (common + STAGE1_DESCRIPTOR::TYPE::Table + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(frame_lvl3.start_address().as_u64() >> 12)).value; - p4[RECURSIVE_INDEX].entry = (common + STAGE1_DESCRIPTOR::TYPE::Table + STAGE1_DESCRIPTOR::NEXT_LVL_TABLE_ADDR_4KiB.val(frame_lvl4.start_address().as_u64() >> 12)).value; + p4[0].set_frame(frame_lvl3, EF::default(), MairNormal::attr_value()); + p4[RECURSIVE_INDEX].set_frame(frame_lvl4, EF::default(), MairNormal::attr_value()); // warn!("p2"); // for i in 0..512 { @@ -129,18 +65,10 @@ pub fn setup_page_table(frame_lvl4: Frame, frame_lvl3: Frame, frame_lvl2: Frame) /// map the range [start, end) as device memory, insert to the MemorySet pub fn remap_device_2mib(ms: &mut MemorySet, start_addr: usize, end_addr: usize) { ms.edit(|active_table| { - let common = STAGE1_DESCRIPTOR::VALID::True - + STAGE1_DESCRIPTOR::TYPE::Block - + STAGE1_DESCRIPTOR::AP::RW_EL1 - + STAGE1_DESCRIPTOR::AF::True - + STAGE1_DESCRIPTOR::XN::True; - - let mem_attr = STAGE1_DESCRIPTOR::SH::OuterShareable + STAGE1_DESCRIPTOR::AttrIndx.val(mair::DEVICE); - - type Page2MiB = Page; - for page in Page2MiB::range_of(start_addr, end_addr) { + for page in Page::::range_of(start_addr, end_addr) { + let paddr = PhysAddr::new(page.start_address().as_u64()); let p2 = unsafe { &mut *active_table.0.p2_ptr(page) }; - p2[page.p2_index()].entry = (common + mem_attr + STAGE1_DESCRIPTOR::LVL2_OUTPUT_ADDR_4KiB.val(page.start_address().as_u64() >> 21)).value; + p2[page.p2_index()].set_block::(paddr, EF::default() - EF::TABLE_OR_PAGE, MairDevice::attr_value()); } // let p2 = unsafe { &mut *(0o777_777_000_000_0000 as *mut Aarch64PageTable) }; @@ -168,7 +96,8 @@ impl PageTable for ActivePageTable { fn map(&mut self, addr: usize, target: usize) -> &mut PageEntry { let flags = EF::default(); - self.0.map_to(Page::of_addr(addr), Frame::of_addr(target), flags, &mut FrameAllocatorForAarch64) + 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) } @@ -303,7 +232,7 @@ impl InactivePageTable for InactivePageTable0 { 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()); + table[RECURSIVE_INDEX].set_frame(frame.clone(), EF::default(), MairNormal::attr_value()); }); InactivePageTable0 { p4_frame: frame } } @@ -313,7 +242,7 @@ impl InactivePageTable for InactivePageTable0 { let backup = p4_table[RECURSIVE_INDEX].clone(); // overwrite recursive mapping - p4_table[RECURSIVE_INDEX].set_frame(self.p4_frame.clone(), EF::default()); + p4_table[RECURSIVE_INDEX].set_frame(self.p4_frame.clone(), EF::default(), MairNormal::attr_value()); tlb_invalidate_all(); // execute f in the new context @@ -376,7 +305,7 @@ impl InactivePageTable0 { assert!(!e0.is_unused()); self.edit(|_| { - table[KERNEL_PML4].set_frame(Frame::containing_address(e0.addr()), EF::default()); + table[KERNEL_PML4].set_frame(Frame::containing_address(e0.addr()), EF::default(), MairNormal::attr_value()); }); } } From 264600f145465bce0411b3600303c412db05e1fc Mon Sep 17 00:00:00 2001 From: equation314 Date: Sun, 25 Nov 2018 18:27:31 +0800 Subject: [PATCH 15/39] aarch64/mmu: add io remap --- crate/memory/src/memory_set.rs | 8 +++- crate/memory/src/paging/mod.rs | 2 + kernel/src/arch/aarch64/board/raspi3/mod.rs | 3 +- kernel/src/arch/aarch64/memory.rs | 7 ++- kernel/src/arch/aarch64/paging.rs | 52 ++------------------- kernel/src/arch/riscv32/paging.rs | 2 + kernel/src/arch/x86_64/paging.rs | 4 +- 7 files changed, 24 insertions(+), 54 deletions(-) diff --git a/crate/memory/src/memory_set.rs b/crate/memory/src/memory_set.rs index f67c956..316c28b 100644 --- a/crate/memory/src/memory_set.rs +++ b/crate/memory/src/memory_set.rs @@ -98,6 +98,7 @@ pub struct MemoryAttr { user: bool, readonly: bool, execute: bool, + mmio: bool, hide: bool, } @@ -114,6 +115,10 @@ impl MemoryAttr { self.execute = true; self } + pub fn mmio(mut self) -> Self { + self.mmio = true; + self + } pub fn hide(mut self) -> Self { self.hide = true; self @@ -122,8 +127,9 @@ impl MemoryAttr { if self.user { entry.set_user(true); } if self.readonly { entry.set_writable(false); } if self.execute { entry.set_execute(true); } + if self.mmio { entry.set_mmio(false); } if self.hide { entry.set_present(false); } - if self.user || self.readonly || self.execute || self.hide { entry.update(); } + if self.user || self.readonly || self.execute || self.mmio || self.hide { entry.update(); } } } diff --git a/crate/memory/src/paging/mod.rs b/crate/memory/src/paging/mod.rs index 0a3d4c0..0f38ef1 100644 --- a/crate/memory/src/paging/mod.rs +++ b/crate/memory/src/paging/mod.rs @@ -57,4 +57,6 @@ pub trait Entry { fn set_user(&mut self, value: bool); fn execute(&self) -> bool; fn set_execute(&mut self, value: bool); + fn mmio(&self) -> bool; + fn set_mmio(&mut self, value: bool); } diff --git a/kernel/src/arch/aarch64/board/raspi3/mod.rs b/kernel/src/arch/aarch64/board/raspi3/mod.rs index 0db5133..98e59e0 100644 --- a/kernel/src/arch/aarch64/board/raspi3/mod.rs +++ b/kernel/src/arch/aarch64/board/raspi3/mod.rs @@ -6,7 +6,8 @@ pub mod irq; pub mod timer; pub mod serial; -pub const IO_BASE: usize = bcm2837::IO_BASE; +pub const IO_REMAP_BASE: usize = bcm2837::IO_BASE; +pub const IO_REMAP_END: usize = 0x40001000; pub fn init() { assert_has_not_been_called!("board::init must be called only once"); diff --git a/kernel/src/arch/aarch64/memory.rs b/kernel/src/arch/aarch64/memory.rs index 7536dc4..29f3009 100644 --- a/kernel/src/arch/aarch64/memory.rs +++ b/kernel/src/arch/aarch64/memory.rs @@ -25,7 +25,7 @@ pub fn init_mmu_early() { let frame_lvl4 = Frame::containing_address(PhysAddr::new(&PAGE_TABLE_LVL4 as *const _ as u64)); let frame_lvl3 = Frame::containing_address(PhysAddr::new(&PAGE_TABLE_LVL3 as *const _ as u64)); let frame_lvl2 = Frame::containing_address(PhysAddr::new(&PAGE_TABLE_LVL2 as *const _ as u64)); - super::paging::setup_page_table(frame_lvl4, frame_lvl3, frame_lvl2); + super::paging::setup_temp_page_table(frame_lvl4, frame_lvl3, frame_lvl2); // device. MAIR_EL1.write( @@ -111,9 +111,8 @@ fn remap_the_kernel() { ms.push(MemoryArea::new_identity(srodata as usize, erodata as usize, MemoryAttr::default().readonly(), "rodata")); ms.push(MemoryArea::new_identity(sbss as usize, ebss as usize, MemoryAttr::default(), "bss")); - // ensure the level 2 page table exists - ms.push(MemoryArea::new_identity(0x40000000, 0x40200000, MemoryAttr::default(), "arm_control")); - super::paging::remap_device_2mib(&mut ms, 0x3F000000, 0x40200000); + use arch::board::{IO_REMAP_BASE, IO_REMAP_END}; + ms.push(MemoryArea::new_identity(IO_REMAP_BASE, IO_REMAP_END, MemoryAttr::default().mmio(), "io_remap")); unsafe { ms.activate(); } use core::mem::forget; diff --git a/kernel/src/arch/aarch64/paging.rs b/kernel/src/arch/aarch64/paging.rs index f11820c..eab53f4 100644 --- a/kernel/src/arch/aarch64/paging.rs +++ b/kernel/src/arch/aarch64/paging.rs @@ -12,7 +12,7 @@ use aarch64::paging::{FrameAllocator, FrameDeallocator, Page, PhysFrame as Frame use aarch64::paging::memory_attribute::*; // need 3 page -pub fn setup_page_table(frame_lvl4: Frame, frame_lvl3: Frame, frame_lvl2: Frame) { +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) }; @@ -25,8 +25,8 @@ pub fn setup_page_table(frame_lvl4: Frame, frame_lvl3: Frame, frame_lvl2: Frame) for page in Page::::range_of(start_addr, end_addr) { let paddr = PhysAddr::new(page.start_address().as_u64()); - use arch::board::IO_BASE; - if paddr.as_u64() >= IO_BASE as u64 { + use arch::board::IO_REMAP_BASE; + if paddr.as_u64() >= IO_REMAP_BASE as u64 { p2[page.p2_index()].set_block::(paddr, block_flags | EF::PXN, MairDevice::attr_value()); } else { p2[page.p2_index()].set_block::(paddr, block_flags, MairNormal::attr_value()); @@ -39,54 +39,10 @@ pub fn setup_page_table(frame_lvl4: Frame, frame_lvl3: Frame, frame_lvl2: Frame) p4[0].set_frame(frame_lvl3, EF::default(), MairNormal::attr_value()); p4[RECURSIVE_INDEX].set_frame(frame_lvl4, EF::default(), MairNormal::attr_value()); - // warn!("p2"); - // for i in 0..512 { - // if p2[i].flags().bits() != 0 { - // info!("{:x?} {:x?} {:x?}",i, &p2[i] as *const _ as usize, p2[i]); - // } - // } - // warn!("p3"); - // for i in 0..512 { - // if p3[i].flags().bits() != 0 { - // info!("{:x?} {:x?} {:x?}",i, &p3[i] as *const _ as usize, p3[i]); - // } - // } - // warn!("p4"); - // for i in 0..512 { - // if p4[i].flags().bits() != 0 { - // info!("{:x?} {:x?} {:x?}",i, &p4[i] as *const _ as usize, p4[i]); - // } - // } - ttbr_el1_write(0, frame_lvl4); tlb_invalidate_all(); } -/// map the range [start, end) as device memory, insert to the MemorySet -pub fn remap_device_2mib(ms: &mut MemorySet, start_addr: usize, end_addr: usize) { - ms.edit(|active_table| { - for page in Page::::range_of(start_addr, end_addr) { - let paddr = PhysAddr::new(page.start_address().as_u64()); - let p2 = unsafe { &mut *active_table.0.p2_ptr(page) }; - p2[page.p2_index()].set_block::(paddr, EF::default() - EF::TABLE_OR_PAGE, MairDevice::attr_value()); - } - - // let p2 = unsafe { &mut *(0o777_777_000_000_0000 as *mut Aarch64PageTable) }; - // for i in 0..512 { - // if p2[i].flags().bits() != 0 { - // info!("{:x?} {:x?} {:x?}",i, &p2[i] as *const _ as usize, p2[i]); - // } - // } - - // let p2 = unsafe { &mut *(0o777_777_000_001_0000 as *mut Aarch64PageTable) }; - // for i in 0..512 { - // if p2[i].flags().bits() != 0 { - // info!("{:x?} {:x?} {:x?}",i, &p2[i] as *const _ as usize, p2[i]); - // } - // } - }); -} - pub struct ActivePageTable(RecursivePageTable<'static>); pub struct PageEntry(PageTableEntry); @@ -201,6 +157,8 @@ impl Entry for PageEntry { false => self.as_flags().set(EF::PXN, !value), } } + fn mmio(&self) -> bool { self.0.attr().value == MairDevice::attr_value().value } + fn set_mmio(&mut self, value: bool) { self.0.modify_attr(MairDevice::attr_value()); } } impl PageEntry { diff --git a/kernel/src/arch/riscv32/paging.rs b/kernel/src/arch/riscv32/paging.rs index 680448b..ef0710f 100644 --- a/kernel/src/arch/riscv32/paging.rs +++ b/kernel/src/arch/riscv32/paging.rs @@ -126,6 +126,8 @@ impl Entry for PageEntry { fn set_user(&mut self, value: bool) { self.as_flags().set(EF::USER, value); } fn execute(&self) -> bool { self.0.flags().contains(EF::EXECUTABLE) } fn set_execute(&mut self, value: bool) { self.as_flags().set(EF::EXECUTABLE, value); } + fn mmio(&self) -> bool { unimplemented!() } + fn set_mmio(&mut self, value: bool) { unimplemented!() } } impl PageEntry { diff --git a/kernel/src/arch/x86_64/paging.rs b/kernel/src/arch/x86_64/paging.rs index 8717703..9e164bb 100644 --- a/kernel/src/arch/x86_64/paging.rs +++ b/kernel/src/arch/x86_64/paging.rs @@ -138,6 +138,8 @@ impl Entry for PageEntry { } fn execute(&self) -> bool { !self.0.flags().contains(EF::NO_EXECUTE) } fn set_execute(&mut self, value: bool) { self.as_flags().set(EF::NO_EXECUTE, !value); } + fn mmio(&self) -> bool { unimplemented!() } + fn set_mmio(&mut self, value: bool) { unimplemented!() } } impl PageEntry { @@ -261,4 +263,4 @@ impl FrameDeallocator for FrameAllocatorForX86 { fn dealloc(&mut self, frame: Frame) { dealloc_frame(frame.start_address().as_u64() as usize); } -} \ No newline at end of file +} From d30c7e59cabfc2a1d7b3e5e2bf1b22d1539b6b35 Mon Sep 17 00:00:00 2001 From: equation314 Date: Sun, 25 Nov 2018 20:01:13 +0800 Subject: [PATCH 16/39] aarch64: minor modify --- crate/aarch64/Cargo.lock | 108 ++++++++++++++++ crate/aarch64/src/asm.rs | 152 ++++++++--------------- kernel/Cargo.lock | 18 --- kernel/Cargo.toml | 2 - kernel/src/arch/aarch64/interrupt/mod.rs | 3 +- kernel/src/arch/aarch64/mod.rs | 1 - kernel/src/arch/aarch64/paging.rs | 26 ++-- 7 files changed, 183 insertions(+), 127 deletions(-) create mode 100644 crate/aarch64/Cargo.lock diff --git a/crate/aarch64/Cargo.lock b/crate/aarch64/Cargo.lock new file mode 100644 index 0000000..1038287 --- /dev/null +++ b/crate/aarch64/Cargo.lock @@ -0,0 +1,108 @@ +[[package]] +name = "aarch64" +version = "0.1.0" +dependencies = [ + "bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ux 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bare-metal" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bit_field" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "os_bootinfo" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "register" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "tock-registers 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "tock-registers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "usize_conversions" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ux" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a3caf393d93b2d453e80638d0674597020cef3382ada454faacd43d1a55a735a" +"checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" +"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" +"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" +"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" +"checksum os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "66481dbeb5e773e7bd85b63cd6042c30786f834338288c5ec4f3742673db360a" +"checksum register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e10f31b6d2299e5620986ad9fcdd66463e125ad72af4f403f9aedf7592d5ccdb" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum tock-registers 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a385d94f3f62e60445a0adb9ff8d9621faa272234530d4c0f848ec98f88e316" +"checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" +"checksum ux 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53d8df5dd8d07fedccd202de1887d94481fadaea3db70479f459e8163a1fab41" diff --git a/crate/aarch64/src/asm.rs b/crate/aarch64/src/asm.rs index 1c1ee6d..4cd37d7 100644 --- a/crate/aarch64/src/asm.rs +++ b/crate/aarch64/src/asm.rs @@ -1,31 +1,9 @@ +//! Miscellaneous assembly instructions and functions + use paging::PhysFrame; use addr::{PhysAddr, VirtAddr}; use regs::*; -#[inline(always)] -pub fn tlb_invalidate_all() { - unsafe { - asm!( - "dsb ishst - tlbi vmalle1is - dsb ish - isb" - ); - } -} - -#[inline(always)] -pub fn tlb_invalidate(vaddr: VirtAddr) { - unsafe { - asm!( - "dsb ishst - tlbi vaae1is, $0 - dsb ish - isb" :: "r"(vaddr.as_u64() >> 12) - ); - } -} - /// Returns the current stack pointer. #[inline(always)] pub fn sp() -> *const u8 { @@ -37,91 +15,32 @@ pub fn sp() -> *const u8 { ptr as *const u8 } +/// Returns the current point counter. #[inline(always)] pub unsafe fn get_pc() -> usize { let pc: usize; - asm!("ADR $0, ." : "=r"(pc)); + asm!("adr $0, ." : "=r"(pc)); pc } -/// Returns the current exception level. -/// -/// # Safety -/// This function should only be called when EL is >= 1. -#[inline(always)] -pub unsafe fn current_el() -> u8 { - let el_reg: u64; - asm!("mrs $0, CurrentEL" : "=r"(el_reg)); - ((el_reg & 0b1100) >> 2) as u8 -} - -#[inline(always)] -pub unsafe fn get_far() -> usize { - let far: usize; - asm!("mrs $0, far_el1" : "=r"(far)); - far -} - -#[inline(always)] -pub unsafe fn get_ttbr0() -> usize { - let ttbr0: usize; - asm!("mrs $0, ttbr0_el1" : "=r"(ttbr0)); - ttbr0 -} - -#[inline(always)] -pub unsafe fn get_ttbr1() -> usize { - let ttbr0: usize; - asm!("mrs $0, ttbr1_el1" : "=r"(ttbr0)); - ttbr0 -} - -#[inline(always)] -pub fn address_translate(vaddr: usize) -> usize { - let paddr: usize; - unsafe { - asm!("at S1E1R, $1; mrs $0, par_el1" : "=r"(paddr) : "r"(vaddr)); - } - paddr -} - -/// Returns the SPSel value. -#[inline(always)] -pub fn sp_sel() -> u8 { - let ptr: u32; - unsafe { - asm!("mrs $0, SPSel" : "=r"(ptr)); - } - - (ptr & 1) as u8 -} - -/// Returns the core currently executing. -/// -/// # Safety -/// -/// This function should only be called when EL is >= 1. -pub unsafe fn affinity() -> usize { - let x: usize; - asm!("mrs $0, mpidr_el1 - and $0, $0, #3" - : "=r"(x)); - - x -} +/// The classic no-op +#[inline] +pub fn nop() { + match () { + #[cfg(target_arch = "aarch64")] + () => unsafe { asm!("nop" :::: "volatile") }, -pub fn wfi() { - unsafe { - asm!("wfi" :::: "volatile"); + #[cfg(not(target_arch = "aarch64"))] + () => unimplemented!(), } } -/// The classic no-op +/// Wait For Interrupt #[inline] -pub fn nop() { +pub fn wfi() { match () { #[cfg(target_arch = "aarch64")] - () => unsafe { asm!("nop" :::: "volatile") }, + () => unsafe { asm!("wfi" :::: "volatile") }, #[cfg(not(target_arch = "aarch64"))] () => unimplemented!(), @@ -160,7 +79,44 @@ pub fn eret() -> ! { } } -pub fn ttbr_el1_read(which: u8) -> (PhysFrame) { +/// Invalidate all TLB entries. +#[inline(always)] +pub fn tlb_invalidate_all() { + unsafe { + asm!( + "dsb ishst + tlbi vmalle1is + dsb ish + isb" + ); + } +} + +/// Invalidate TLB entries that would be used to translate the specified address. +#[inline(always)] +pub fn tlb_invalidate(vaddr: VirtAddr) { + unsafe { + asm!( + "dsb ishst + tlbi vaae1is, $0 + dsb ish + isb" :: "r"(vaddr.as_u64() >> 12) + ); + } +} + +/// Address Translate. +#[inline(always)] +pub fn address_translate(vaddr: usize) -> usize { + let paddr: usize; + unsafe { + asm!("at S1E1R, $1; mrs $0, par_el1" : "=r"(paddr) : "r"(vaddr)); + } + paddr +} + +/// Read TTBRx_EL1 as PhysFrame +pub fn ttbr_el1_read(which: u8) -> PhysFrame { let baddr = match which { 0 => TTBR0_EL1.get_baddr(), 1 => TTBR1_EL1.get_baddr(), @@ -169,6 +125,7 @@ pub fn ttbr_el1_read(which: u8) -> (PhysFrame) { PhysFrame::containing_address(PhysAddr::new(baddr)) } +/// Write TTBRx_EL1 from PhysFrame pub fn ttbr_el1_write(which: u8, frame: PhysFrame) { let baddr = frame.start_address().as_u64(); match which { @@ -178,6 +135,7 @@ pub fn ttbr_el1_write(which: u8, frame: PhysFrame) { }; } +/// write TTBRx_EL1 from PhysFrame and ASID pub fn ttbr_el1_write_asid(which: u8, asid: u16, frame: PhysFrame) { let baddr = frame.start_address().as_u64(); match which { diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index 7795f1f..4e2b6dc 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -5,7 +5,6 @@ dependencies = [ "bare-metal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -177,14 +176,6 @@ name = "redox_syscall" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "register" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "register" version = "0.2.1" @@ -247,11 +238,6 @@ dependencies = [ "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "tock-registers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "tock-registers" version = "0.2.0" @@ -279,13 +265,11 @@ dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "bootloader 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-a 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "linked_list_allocator 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "once 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "riscv 0.3.0", "simple-filesystem 0.0.1 (git+https://github.com/wangrunji0408/SimpleFileSystem-Rust)", "spin 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -399,7 +383,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07" "checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" "checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1" -"checksum register 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0e985243ba7e1c336b62444ef2a10d7bd87cf41a222285ae3de605c859006479" "checksum register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e10f31b6d2299e5620986ad9fcdd66463e125ad72af4f403f9aedf7592d5ccdb" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum simple-filesystem 0.0.1 (git+https://github.com/wangrunji0408/SimpleFileSystem-Rust)" = "" @@ -407,7 +390,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum spin 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "37b5646825922b96b5d7d676b5bb3458a54498e96ed7b0ce09dc43a07038fea4" "checksum static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" -"checksum tock-registers 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2acc33f980e23cee18d234a32d0637fbc1ea55e13ab04012fa857b899fa1b7a9" "checksum tock-registers 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a385d94f3f62e60445a0adb9ff8d9621faa272234530d4c0f848ec98f88e316" "checksum uart_16550 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "269f953d8de3226f7c065c589c7b4a3e83d10a419c7c3b5e2e0f197e6acc966e" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 06ea088..20b696f 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -32,7 +32,6 @@ bit-allocator = { path = "../crate/bit-allocator" } ucore-memory = { path = "../crate/memory" } ucore-process = { path = "../crate/process" } simple-filesystem = { git = "https://github.com/wangrunji0408/SimpleFileSystem-Rust" } -register="0.1.0" [target.'cfg(target_arch = "x86_64")'.dependencies] bootloader = "0.3" @@ -45,7 +44,6 @@ riscv = { path = "../crate/riscv" } bbl = { path = "../crate/bbl" } [target.'cfg(target_arch = "aarch64")'.dependencies] -cortex-a = "2.2.1" aarch64 = { path = "../crate/aarch64" } atags = { path = "../crate/atags" } bcm2837 = { path = "../crate/bcm2837", features = ["use_generic_timer"] } diff --git a/kernel/src/arch/aarch64/interrupt/mod.rs b/kernel/src/arch/aarch64/interrupt/mod.rs index 140e9b8..bba925a 100644 --- a/kernel/src/arch/aarch64/interrupt/mod.rs +++ b/kernel/src/arch/aarch64/interrupt/mod.rs @@ -4,7 +4,8 @@ mod handler; mod context; mod syndrome; -use super::cortex_a::regs::*; +use aarch64::regs::*; + pub use self::context::*; pub use self::handler::*; diff --git a/kernel/src/arch/aarch64/mod.rs b/kernel/src/arch/aarch64/mod.rs index ccd6230..685072e 100644 --- a/kernel/src/arch/aarch64/mod.rs +++ b/kernel/src/arch/aarch64/mod.rs @@ -1,7 +1,6 @@ //! Entrance and initialization for aarch64. extern crate atags; -extern crate cortex_a; pub mod io; pub mod paging; diff --git a/kernel/src/arch/aarch64/paging.rs b/kernel/src/arch/aarch64/paging.rs index eab53f4..0093385 100644 --- a/kernel/src/arch/aarch64/paging.rs +++ b/kernel/src/arch/aarch64/paging.rs @@ -146,19 +146,29 @@ impl Entry for PageEntry { self.as_flags().set(EF::nG, value); // set non-global to use ASID } fn execute(&self) -> bool { - match self.user() { - true => !self.0.flags().contains(EF::XN), - false => !self.0.flags().contains(EF::PXN), + if self.user() { + !self.0.flags().contains(EF::XN) + } else { + !self.0.flags().contains(EF::PXN) } } fn set_execute(&mut self, value: bool) { - match self.user() { - true => self.as_flags().set(EF::XN, !value), - false => self.as_flags().set(EF::PXN, !value), + if self.user() { + self.as_flags().set(EF::XN, !value) + } else { + self.as_flags().set(EF::PXN, !value) + } + } + fn mmio(&self) -> bool { + self.0.attr().value == MairDevice::attr_value().value + } + fn set_mmio(&mut self, value: bool) { + if value { + self.0.modify_attr(MairDevice::attr_value()) + } else { + self.0.modify_attr(MairNormal::attr_value()) } } - fn mmio(&self) -> bool { self.0.attr().value == MairDevice::attr_value().value } - fn set_mmio(&mut self, value: bool) { self.0.modify_attr(MairDevice::attr_value()); } } impl PageEntry { From 2db453556d68c24604d003218fe2c92b9f6cbc1c Mon Sep 17 00:00:00 2001 From: WangRunji Date: Mon, 26 Nov 2018 16:58:45 +0800 Subject: [PATCH 17/39] try to fix interrupt & deadlock on RV32 --- crate/process/src/interrupt.rs | 4 ++-- crate/process/src/processor.rs | 3 +++ kernel/src/logging.rs | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/crate/process/src/interrupt.rs b/crate/process/src/interrupt.rs index e0f2283..17d62ce 100644 --- a/crate/process/src/interrupt.rs +++ b/crate/process/src/interrupt.rs @@ -10,8 +10,8 @@ pub unsafe fn disable_and_store() -> usize { #[cfg(target_arch = "riscv32")] pub unsafe fn disable_and_store() -> usize { let sstatus: usize; - asm!("csrrci $0, 0x100, 1" : "=r"(sstatus)); - sstatus & 1 + asm!("csrrci $0, 0x100, 2" : "=r"(sstatus)); + sstatus & 2 } #[inline(always)] diff --git a/crate/process/src/processor.rs b/crate/process/src/processor.rs index ef617fe..d29d560 100644 --- a/crate/process/src/processor.rs +++ b/crate/process/src/processor.rs @@ -92,7 +92,10 @@ impl Processor { } pub fn tick(&self) { + let flags = unsafe { interrupt::disable_and_store() }; let need_reschedule = self.manager().tick(self.pid()); + unsafe { interrupt::restore(flags); } + if need_reschedule { self.yield_now(); } diff --git a/kernel/src/logging.rs b/kernel/src/logging.rs index 513e0c0..86bd1c2 100644 --- a/kernel/src/logging.rs +++ b/kernel/src/logging.rs @@ -1,6 +1,6 @@ use core::fmt; use log::{self, Level, LevelFilter, Log, Metadata, Record}; -use spin::Mutex; +use crate::sync::SpinNoIrqLock as Mutex; use lazy_static::lazy_static; lazy_static! { From 52fe93188ddd376d87aec12f48aafc6d151948d5 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Tue, 27 Nov 2018 01:18:30 +0800 Subject: [PATCH 18/39] disable delay allocating frame, remove `memory_set_record` --- crate/memory/src/memory_set.rs | 19 ++++----- crate/riscv | 1 - kernel/src/arch/riscv32/interrupt.rs | 2 +- kernel/src/fs.rs | 2 - kernel/src/lib.rs | 6 --- kernel/src/memory.rs | 54 ++++++------------------- kernel/src/process/context.rs | 59 +++++----------------------- 7 files changed, 29 insertions(+), 114 deletions(-) delete mode 160000 crate/riscv diff --git a/crate/memory/src/memory_set.rs b/crate/memory/src/memory_set.rs index 92f2dfc..9731c27 100644 --- a/crate/memory/src/memory_set.rs +++ b/crate/memory/src/memory_set.rs @@ -166,22 +166,17 @@ impl MemoryArea { } } None => { - info!("map delayed!"); for page in Page::range_of(self.start_addr, self.end_addr) { let addr = page.start_address(); - //let target = T::alloc_frame().expect("failed to allocate frame"); - //self.flags.apply(pt.map(addr, target)); + let target = T::alloc_frame().expect("failed to allocate frame"); + self.flags.apply(pt.map(addr, target)); // for frame delayed allocation - { - let entry = pt.map(addr,0); - self.flags.apply(entry); - } - let entry = pt.get_entry(addr).expect("fail to get entry"); - entry.set_present(false); - entry.update(); - +// let entry = pt.map(addr,0); +// self.flags.apply(entry); +// let entry = pt.get_entry(addr).expect("fail to get entry"); +// entry.set_present(false); +// entry.update(); } - info!("finish map delayed!"); } }; } diff --git a/crate/riscv b/crate/riscv deleted file mode 160000 index a37a65f..0000000 --- a/crate/riscv +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a37a65fa13a00c5aa0068c3f2b5d55af6a37dd93 diff --git a/kernel/src/arch/riscv32/interrupt.rs b/kernel/src/arch/riscv32/interrupt.rs index 83a7491..4d253fc 100644 --- a/kernel/src/arch/riscv32/interrupt.rs +++ b/kernel/src/arch/riscv32/interrupt.rs @@ -1,6 +1,6 @@ use riscv::register::*; pub use self::context::*; -use crate::memory::{MemorySet, InactivePageTable0, memory_set_record}; +use crate::memory::{MemorySet, InactivePageTable0}; use log::*; #[path = "context.rs"] diff --git a/kernel/src/fs.rs b/kernel/src/fs.rs index fe38f0c..3779ab5 100644 --- a/kernel/src/fs.rs +++ b/kernel/src/fs.rs @@ -3,8 +3,6 @@ use alloc::{boxed::Box, sync::Arc, string::String, collections::VecDeque, vec::V use core::any::Any; use core::slice; use lazy_static::lazy_static; -use crate::memory::{MemorySet, InactivePageTable0, memory_set_record}; -use crate::process::context::memory_set_map_swappable; #[cfg(target_arch = "x86_64")] use crate::arch::driver::ide; use crate::sync::Condvar; diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 043883e..c13f0cd 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -48,12 +48,6 @@ pub mod arch; pub fn kmain() -> ! { processor().run(); - -// thread::test::local_key(); -// thread::test::unpack(); -// sync::test::philosopher_using_mutex(); -// sync::test::philosopher_using_monitor(); -// sync::mpsc::test::test_all(); } /// Global heap allocator diff --git a/kernel/src/memory.rs b/kernel/src/memory.rs index 8ebca5f..1b2e069 100644 --- a/kernel/src/memory.rs +++ b/kernel/src/memory.rs @@ -30,15 +30,6 @@ pub type FrameAlloc = BitAlloc64K; lazy_static! { pub static ref FRAME_ALLOCATOR: SpinNoIrqLock = SpinNoIrqLock::new(FrameAlloc::default()); } -// record the user memory set for pagefault function (swap in/out and frame delayed allocate) temporarily when page fault in new_user() or fork() function -// after the process is set we can use use crate::processor() to get the inactive page table -lazy_static! { - pub static ref MEMORY_SET_RECORD: SpinNoIrqLock> = SpinNoIrqLock::new(VecDeque::default()); -} - -pub fn memory_set_record() -> MutexGuard<'static, VecDeque, SpinNoIrq> { - MEMORY_SET_RECORD.lock() -} lazy_static! { static ref ACTIVE_TABLE: SpinNoIrqLock> = SpinNoIrqLock::new(unsafe { @@ -115,43 +106,20 @@ pub fn page_fault_handler(addr: usize) -> bool { //unsafe { ACTIVE_TABLE_SWAP.force_unlock(); } info!("active page table token in pg fault is {:x?}", ActivePageTable::token()); - let mmset_record = memory_set_record(); - let id = mmset_record.iter() - .position(|x| unsafe{(*(x.clone() as *mut MemorySet)).get_page_table_mut().token() == ActivePageTable::token()}); /*LAB3 EXERCISE 1: YOUR STUDENT NUMBER * handle the frame deallocated */ - match id { - Some(targetid) => { - info!("get id from memroy set recorder."); - let mmset_ptr = mmset_record.get(targetid).expect("fail to get mmset_ptr").clone(); - // get current mmset - - let current_mmset = unsafe{&mut *(mmset_ptr as *mut MemorySet)}; - //check whether the vma is legal - if current_mmset.find_area(addr).is_none(){ - return false; - } - - let pt = current_mmset.get_page_table_mut(); - info!("pt got!"); - if active_table_swap().page_fault_handler(pt as *mut InactivePageTable0, addr, false, || alloc_frame().expect("fail to alloc frame")){ - return true; - } - }, - None => { - info!("get pt from processor()"); - if process().get_memory_set_mut().find_area(addr).is_none(){ - return false; - } - - let pt = process().get_memory_set_mut().get_page_table_mut(); - info!("pt got"); - if active_table_swap().page_fault_handler(pt as *mut InactivePageTable0, addr, true, || alloc_frame().expect("fail to alloc frame")){ - return true; - } - }, - }; + + info!("get pt from processor()"); + if process().memory_set.find_area(addr).is_none(){ + return false; + } + + let pt = process().memory_set.get_page_table_mut(); + info!("pt got"); + if active_table_swap().page_fault_handler(pt as *mut InactivePageTable0, addr, true, || alloc_frame().expect("fail to alloc frame")){ + return true; + } ////////////////////////////////////////////////////////////////////////////// diff --git a/kernel/src/process/context.rs b/kernel/src/process/context.rs index 14b78b7..9d6afa5 100644 --- a/kernel/src/process/context.rs +++ b/kernel/src/process/context.rs @@ -1,5 +1,5 @@ use crate::arch::interrupt::{TrapFrame, Context as ArchContext}; -use crate::memory::{MemoryArea, MemoryAttr, MemorySet, KernelStack, active_table_swap, alloc_frame, InactivePageTable0, memory_set_record}; +use crate::memory::{MemoryArea, MemoryAttr, MemorySet, KernelStack, active_table_swap, alloc_frame, InactivePageTable0}; use xmas_elf::{ElfFile, header, program::{Flags, ProgramHeader, Type}}; use core::fmt::{Debug, Error, Formatter}; use alloc::{boxed::Box, collections::BTreeMap, vec::Vec, sync::Arc, string::String}; @@ -66,14 +66,6 @@ impl ContextImpl { } /// Make a new user thread from ELF data - /* - * @param: - * data: the ELF data stream - * @brief: - * make a new thread from ELF data - * @retval: - * the new user thread Context - */ pub fn new_user<'a, Iter>(data: &[u8], args: Iter) -> Box where Iter: Iterator { @@ -95,12 +87,6 @@ impl ContextImpl { // Make page table let mut memory_set = memory_set_from(&elf); - // add the new memory set to the recorder - let mmset_ptr = ((&mut memory_set) as * mut MemorySet) as usize; - memory_set_record().push_back(mmset_ptr); - //let id = memory_set_record().iter() - // .position(|x| unsafe { info!("current memory set record include {:x?}, {:x?}", x, (*(x.clone() as *mut MemorySet)).get_page_table_mut().token()); false }); - memory_set.push(MemoryArea::new(ustack_buttom, ustack_top, MemoryAttr::default().user(), "user_stack")); trace!("{:#x?}", memory_set); @@ -126,14 +112,11 @@ impl ContextImpl { } let kstack = KernelStack::new(); - { - let mut mmset_record = memory_set_record(); - let id = mmset_record.iter() - .position(|x| x.clone() == mmset_ptr).expect("id not exist"); - mmset_record.remove(id); - } - let mut ret = Box::new(ContextImpl { + //set the user Memory pages in the memory set swappable + memory_set_map_swappable(&mut memory_set); + + Box::new(ContextImpl { arch: unsafe { ArchContext::new_user_thread( entry_addr, ustack_top, kstack.top(), is32, memory_set.token()) @@ -142,10 +125,7 @@ impl ContextImpl { kstack, files: BTreeMap::default(), cwd: String::new(), - }); - //set the user Memory pages in the memory set swappable - memory_set_map_swappable(ret.get_memory_set_mut()); - ret + }) } /// Fork @@ -154,10 +134,6 @@ impl ContextImpl { // Clone memory set, make a new page table let mut memory_set = self.memory_set.clone(); info!("finish mmset clone in fork!"); - // add the new memory set to the recorder - info!("fork! new page table token: {:x?}", memory_set.token()); - let mmset_ptr = ((&mut memory_set) as * mut MemorySet) as usize; - memory_set_record().push_back(mmset_ptr); info!("before copy data to temp space"); // Copy data to temp space @@ -180,32 +156,17 @@ impl ContextImpl { info!("temporary copy data!"); let kstack = KernelStack::new(); - // remove the raw pointer for the memory set in memory_set_record - { - let mut mmset_record = memory_set_record(); - let id = mmset_record.iter() - .position(|x| x.clone() == mmset_ptr).expect("id not exist"); - mmset_record.remove(id); - } - + memory_set_map_swappable(&mut memory_set); + info!("FORK() finsihed!"); - let mut ret = Box::new(ContextImpl { + Box::new(ContextImpl { arch: unsafe { ArchContext::new_fork(tf, kstack.top(), memory_set.token()) }, memory_set, kstack, files: BTreeMap::default(), cwd: String::new(), - }); - - memory_set_map_swappable(ret.get_memory_set_mut()); - info!("FORK() finsihed!"); - ret - } - - pub fn get_memory_set_mut(&mut self) -> &mut MemorySet { - &mut self.memory_set + }) } - } impl Drop for ContextImpl{ From 18640b753720346501c62b3e82b71d9abcff142f Mon Sep 17 00:00:00 2001 From: WangRunji Date: Tue, 27 Nov 2018 01:23:07 +0800 Subject: [PATCH 19/39] impl NoMMU --- crate/memory/src/lib.rs | 1 + crate/memory/src/no_mmu.rs | 66 +++++++++++++++++ kernel/Cargo.lock | 16 ++--- kernel/Cargo.toml | 2 +- kernel/Makefile | 9 ++- kernel/src/arch/riscv32/memory.rs | 16 ++++- kernel/src/memory.rs | 28 +++++++- kernel/src/process/context.rs | 113 ++++++++++++++++-------------- 8 files changed, 181 insertions(+), 70 deletions(-) create mode 100644 crate/memory/src/no_mmu.rs diff --git a/crate/memory/src/lib.rs b/crate/memory/src/lib.rs index 649d29d..ac956fa 100644 --- a/crate/memory/src/lib.rs +++ b/crate/memory/src/lib.rs @@ -12,5 +12,6 @@ pub mod cow; pub mod swap; pub mod memory_set; mod addr; +pub mod no_mmu; pub use crate::addr::*; \ No newline at end of file diff --git a/crate/memory/src/no_mmu.rs b/crate/memory/src/no_mmu.rs new file mode 100644 index 0000000..1fe2123 --- /dev/null +++ b/crate/memory/src/no_mmu.rs @@ -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 { + areas: Vec>, + support: PhantomData, +} + +impl MemorySet { + 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(&self, f: impl FnOnce() -> T) -> T { f() } + pub fn token(&self) -> usize { 0 } + pub unsafe fn activate(&self) {} +} + +#[derive(Debug)] +struct MemoryArea { + ptr: usize, + layout: Layout, + support: PhantomData, +} + +impl MemoryArea { + 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 Clone for MemoryArea { + 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 Drop for MemoryArea { + fn drop(&mut self) { + unsafe { S::allocator().dealloc(self.ptr as *mut u8, self.layout) } + } +} \ No newline at end of file diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index 4a525d8..5a0a2b9 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -37,7 +37,7 @@ name = "bcm2837" version = "0.1.0" dependencies = [ "cortex-a 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "volatile 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "volatile 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -65,7 +65,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bootloader" version = "0.3.4" -source = "git+https://github.com/wangrunji0408/bootloader#dd5723c3d7d50856073a5003e0a355ea0fc3d46c" +source = "git+https://github.com/wangrunji0408/bootloader#24bdcafc302e7d2659ac126049a45571462907d9" dependencies = [ "apic 0.1.0 (git+https://github.com/wangrunji0408/APIC-Rust)", "fixedvec 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -132,7 +132,7 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.43" +version = "0.2.44" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -185,7 +185,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -324,7 +324,7 @@ dependencies = [ "uart_16550 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "ucore-memory 0.1.0", "ucore-process 0.1.0", - "volatile 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "volatile 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "x86_64 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -361,7 +361,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "volatile" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -446,7 +446,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0a7292d30132fb5424b354f5dc02512a86e4c516fe544bb7a25e7f266951b797" "checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1" -"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" +"checksum libc 0.2.44 (registry+https://github.com/rust-lang/crates.io-index)" = "10923947f84a519a45c8fefb7dd1b3e8c08747993381adee176d7a82b4195311" "checksum linked_list_allocator 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "655d57c71827fe0891ce72231b6aa5e14033dae3f604609e6a6f807267c1678d" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" @@ -472,7 +472,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" "checksum ux 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53d8df5dd8d07fedccd202de1887d94481fadaea3db70479f459e8163a1fab41" -"checksum volatile 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "54d4343a2df2d65144a874f95950754ee7b7e8594f6027aae8c7d0f4858a3fe8" +"checksum volatile 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d9ca391c55768e479d5c2f8beb40c136df09257292a809ea514e82cfdfc15d00" "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 4df6d88..8f3f947 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -5,8 +5,8 @@ authors = ["Runji Wang "] edition = "2018" [features] -link_user_program = [] no_bbl = [] +no_mmu = [] board_raspi3 = [] [profile.dev] diff --git a/kernel/Makefile b/kernel/Makefile index 2329032..e973700 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -52,7 +52,8 @@ qemu_opts := \ -machine $(board) \ -serial null -serial mon:stdio \ -nographic \ - -kernel $(bin) + -kernel $(bin) \ + -smp cores=$(smp) endif ifdef d @@ -66,10 +67,8 @@ features := $(features) no_bbl endif endif -# Link user binaries at ../user -ifdef link_user -features := $(features) link_user_program -assembly_object_files := $(assembly_object_files) $(user_obj) +ifdef nommu +features := $(features) no_mmu endif features := $(features) board_$(board) diff --git a/kernel/src/arch/riscv32/memory.rs b/kernel/src/arch/riscv32/memory.rs index 7251227..7ba0b9f 100644 --- a/kernel/src/arch/riscv32/memory.rs +++ b/kernel/src/arch/riscv32/memory.rs @@ -2,12 +2,24 @@ use core::{slice, mem}; use riscv::{addr::*, register::sstatus}; use ucore_memory::PAGE_SIZE; use log::*; -use crate::memory::{active_table, FRAME_ALLOCATOR, init_heap, MemoryArea, MemoryAttr, MemorySet}; +use crate::memory::{active_table, FRAME_ALLOCATOR, init_heap, MemoryArea, MemoryAttr, MemorySet, MEMORY_ALLOCATOR}; +use crate::consts::{MEMORY_OFFSET, MEMORY_END}; + +#[cfg(feature = "no_mmu")] +pub fn init() { + init_heap(); + + let heap_bottom = end as usize; + let heap_size = MEMORY_END - heap_bottom; + unsafe { MEMORY_ALLOCATOR.lock().init(heap_bottom, heap_size); } + info!("available memory: [{:#x}, {:#x})", heap_bottom, MEMORY_END); +} /* * @brief: * Init the mermory management module, allow memory access and set up page table and init heap and frame allocator */ +#[cfg(not(feature = "no_mmu"))] pub fn init() { #[repr(align(4096))] // align the PageData struct to 4096 bytes struct PageData([u8; PAGE_SIZE]); @@ -37,7 +49,6 @@ pub fn init_other() { fn init_frame_allocator() { use bit_allocator::BitAlloc; use core::ops::Range; - use crate::consts::{MEMORY_OFFSET, MEMORY_END}; let mut ba = FRAME_ALLOCATOR.lock(); ba.insert(to_range(end as usize + PAGE_SIZE, MEMORY_END)); @@ -63,6 +74,7 @@ fn init_frame_allocator() { * @brief: * remmap the kernel memory address with 4K page recorded in p1 page table */ +#[cfg(not(feature = "no_mmu"))] fn remap_the_kernel() { let mut ms = MemorySet::new_bare(); #[cfg(feature = "no_bbl")] diff --git a/kernel/src/memory.rs b/kernel/src/memory.rs index 1b2e069..27b85fe 100644 --- a/kernel/src/memory.rs +++ b/kernel/src/memory.rs @@ -4,16 +4,20 @@ use crate::consts::MEMORY_OFFSET; use super::HEAP_ALLOCATOR; use ucore_memory::{*, paging::PageTable}; use ucore_memory::cow::CowExt; -pub use ucore_memory::memory_set::{MemoryArea, MemoryAttr, MemorySet as MemorySet_, InactivePageTable}; +pub use ucore_memory::memory_set::{MemoryArea, MemoryAttr, InactivePageTable}; use ucore_memory::swap::*; use crate::process::{process}; use crate::sync::{SpinNoIrqLock, SpinNoIrq, MutexGuard}; use alloc::collections::VecDeque; use lazy_static::*; use log::*; +use linked_list_allocator::LockedHeap; +#[cfg(not(feature = "no_mmu"))] +pub type MemorySet = ucore_memory::memory_set::MemorySet; -pub type MemorySet = MemorySet_; +#[cfg(feature = "no_mmu")] +pub type MemorySet = ucore_memory::no_mmu::MemorySet; // x86_64 support up to 256M memory #[cfg(target_arch = "x86_64")] @@ -101,6 +105,7 @@ impl Drop for KernelStack { * @retval: * Return true to continue, false to halt */ +#[cfg(not(feature = "no_mmu"))] pub fn page_fault_handler(addr: usize) -> bool { info!("start handling swap in/out page fault"); //unsafe { ACTIVE_TABLE_SWAP.force_unlock(); } @@ -140,6 +145,25 @@ pub fn init_heap() { info!("heap init end"); } +/// Allocator for the rest memory space on NO-MMU case. +pub static MEMORY_ALLOCATOR: LockedHeap = LockedHeap::empty(); + +#[derive(Debug, Clone, Copy)] +pub struct NoMMUSupportImpl; + +impl ucore_memory::no_mmu::NoMMUSupport for NoMMUSupportImpl { + type Alloc = LockedHeap; + fn allocator() -> &'static Self::Alloc { + &MEMORY_ALLOCATOR + } +} + +#[cfg(feature = "no_mmu")] +pub fn page_fault_handler(_addr: usize) -> bool { + unreachable!() +} + + //pub mod test { // pub fn cow() { // use super::*; diff --git a/kernel/src/process/context.rs b/kernel/src/process/context.rs index 9d6afa5..684541c 100644 --- a/kernel/src/process/context.rs +++ b/kernel/src/process/context.rs @@ -1,6 +1,6 @@ use crate::arch::interrupt::{TrapFrame, Context as ArchContext}; use crate::memory::{MemoryArea, MemoryAttr, MemorySet, KernelStack, active_table_swap, alloc_frame, InactivePageTable0}; -use xmas_elf::{ElfFile, header, program::{Flags, ProgramHeader, Type}}; +use xmas_elf::{ElfFile, header, program::{Flags, ProgramHeader, Type, SegmentData}}; use core::fmt::{Debug, Error, Formatter}; use alloc::{boxed::Box, collections::BTreeMap, vec::Vec, sync::Arc, string::String}; use ucore_memory::{Page}; @@ -75,45 +75,44 @@ impl ContextImpl { header::HeaderPt2::Header32(_) => true, header::HeaderPt2::Header64(_) => false, }; - assert_eq!(elf.header.pt2.type_().as_type(), header::Type::Executable, "ELF is not executable"); - // User stack - use crate::consts::{USER_STACK_OFFSET, USER_STACK_SIZE, USER32_STACK_OFFSET}; - let (ustack_buttom, mut ustack_top) = match is32 { - true => (USER32_STACK_OFFSET, USER32_STACK_OFFSET + USER_STACK_SIZE), - false => (USER_STACK_OFFSET, USER_STACK_OFFSET + USER_STACK_SIZE), - }; + match elf.header.pt2.type_().as_type() { + header::Type::Executable => { +// #[cfg(feature = "no_mmu")] +// panic!("ELF is not shared object"); + }, + header::Type::SharedObject => {}, + _ => panic!("ELF is not executable or shared object"), + } // Make page table let mut memory_set = memory_set_from(&elf); - memory_set.push(MemoryArea::new(ustack_buttom, ustack_top, MemoryAttr::default().user(), "user_stack")); - trace!("{:#x?}", memory_set); - - let entry_addr = elf.header.pt2.entry_point() as usize; + // User stack + use crate::consts::{USER_STACK_OFFSET, USER_STACK_SIZE, USER32_STACK_OFFSET}; + #[cfg(not(feature = "no_mmu"))] + let mut ustack_top = { + let (ustack_buttom, ustack_top) = match is32 { + true => (USER32_STACK_OFFSET, USER32_STACK_OFFSET + USER_STACK_SIZE), + false => (USER_STACK_OFFSET, USER_STACK_OFFSET + USER_STACK_SIZE), + }; + memory_set.push(MemoryArea::new(ustack_buttom, ustack_top, MemoryAttr::default().user(), "user_stack")); + ustack_top + }; + #[cfg(feature = "no_mmu")] + let mut ustack_top = memory_set.push(USER_STACK_SIZE).as_ptr() as usize + USER_STACK_SIZE; - // Temporary switch to it, in order to copy data unsafe { - memory_set.with(|| { - for ph in elf.program_iter() { - let virt_addr = ph.virtual_addr() as usize; - let offset = ph.offset() as usize; - let file_size = ph.file_size() as usize; - let mem_size = ph.mem_size() as usize; - - let target = unsafe { ::core::slice::from_raw_parts_mut(virt_addr as *mut u8, mem_size) }; - if file_size != 0 { - target[..file_size].copy_from_slice(&data[offset..offset + file_size]); - } - target[file_size..].iter_mut().for_each(|x| *x = 0); - } - ustack_top = push_args_at_stack(args, ustack_top); - }); + memory_set.with(|| { ustack_top = push_args_at_stack(args, ustack_top) }); } + trace!("{:#x?}", memory_set); + + let entry_addr = elf.header.pt2.entry_point() as usize; let kstack = KernelStack::new(); //set the user Memory pages in the memory set swappable + #[cfg(not(feature = "no_mmu"))] memory_set_map_swappable(&mut memory_set); Box::new(ContextImpl { @@ -135,27 +134,20 @@ impl ContextImpl { let mut memory_set = self.memory_set.clone(); info!("finish mmset clone in fork!"); - info!("before copy data to temp space"); - // Copy data to temp space - use alloc::vec::Vec; - let datas: Vec> = memory_set.iter().map(|area| { - Vec::from(unsafe { area.as_slice() }) - }).collect(); - - info!("Finish copy data to temp space."); - - // Temporarily switch to it, in order to copy data - unsafe { - memory_set.with(|| { - for (area, data) in memory_set.iter().zip(datas.iter()) { - area.as_slice_mut().copy_from_slice(data.as_slice()) - } - }); + // MMU: copy data to the new space + // NoMMU: coping data has been done in `memory_set.clone()` + #[cfg(not(feature = "no_mmu"))] + for area in memory_set.iter() { + let data = Vec::::from(unsafe { area.as_slice() }); + unsafe { memory_set.with(|| { + area.as_slice_mut().copy_from_slice(data.as_slice()) + }) } } info!("temporary copy data!"); let kstack = KernelStack::new(); + #[cfg(not(feature = "no_mmu"))] memory_set_map_swappable(&mut memory_set); info!("FORK() finsihed!"); @@ -169,7 +161,8 @@ impl ContextImpl { } } -impl Drop for ContextImpl{ +#[cfg(not(feature = "no_mmu"))] +impl Drop for ContextImpl { fn drop(&mut self){ info!("come in to drop for ContextImpl"); //set the user Memory pages in the memory set unswappable @@ -232,19 +225,34 @@ unsafe fn push_args_at_stack<'a, Iter>(args: Iter, stack_top: usize) -> usize */ fn memory_set_from<'a>(elf: &'a ElfFile<'a>) -> MemorySet { debug!("come in to memory_set_from"); - let mut set = MemorySet::new(); + let mut ms = MemorySet::new(); for ph in elf.program_iter() { if ph.get_type() != Ok(Type::Load) { continue; } - let (virt_addr, mem_size, flags) = match ph { - ProgramHeader::Ph32(ph) => (ph.virtual_addr as usize, ph.mem_size as usize, ph.flags), - ProgramHeader::Ph64(ph) => (ph.virtual_addr as usize, ph.mem_size as usize, ph.flags), + let virt_addr = ph.virtual_addr() as usize; + let offset = ph.offset() as usize; + let file_size = ph.file_size() as usize; + let mem_size = ph.mem_size() as usize; + // Get target slice + #[cfg(feature = "no_mmu")] + let target = ms.push(mem_size); + #[cfg(not(feature = "no_mmu"))] + let target = { + ms.push(MemoryArea::new(virt_addr, virt_addr + mem_size, memory_attr_from(ph.flags()), "")); + unsafe { ::core::slice::from_raw_parts_mut(virt_addr as *mut u8, mem_size) } }; - set.push(MemoryArea::new(virt_addr, virt_addr + mem_size, memory_attr_from(flags), "")); - + // Copy data + unsafe { + ms.with(|| { + if file_size != 0 { + target[..file_size].copy_from_slice(&elf.input[offset..offset + file_size]); + } + target[file_size..].iter_mut().for_each(|x| *x = 0); + }); + } } - set + ms } fn memory_attr_from(elf_flags: Flags) -> MemoryAttr { @@ -260,6 +268,7 @@ fn memory_attr_from(elf_flags: Flags) -> MemoryAttr { * @brief: * map the memory area in the memory_set swappalbe, specially for the user process */ +#[cfg(not(feature = "no_mmu"))] pub fn memory_set_map_swappable(memory_set: &mut MemorySet){ info!("COME INTO memory set map swappable!"); let pt = unsafe { From 6921e9018d0b27dce26c9c50e56dc742bc1a301b Mon Sep 17 00:00:00 2001 From: WangRunji Date: Tue, 27 Nov 2018 11:26:37 +0800 Subject: [PATCH 20/39] fix NoMMU entry point --- kernel/src/process/context.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/kernel/src/process/context.rs b/kernel/src/process/context.rs index 684541c..f5d7587 100644 --- a/kernel/src/process/context.rs +++ b/kernel/src/process/context.rs @@ -86,7 +86,7 @@ impl ContextImpl { } // Make page table - let mut memory_set = memory_set_from(&elf); + let (mut memory_set, entry_addr) = memory_set_from(&elf); // User stack use crate::consts::{USER_STACK_OFFSET, USER_STACK_SIZE, USER32_STACK_OFFSET}; @@ -108,7 +108,6 @@ impl ContextImpl { trace!("{:#x?}", memory_set); - let entry_addr = elf.header.pt2.entry_point() as usize; let kstack = KernelStack::new(); //set the user Memory pages in the memory set swappable @@ -215,17 +214,12 @@ unsafe fn push_args_at_stack<'a, Iter>(args: Iter, stack_top: usize) -> usize } -/* -* @param: -* elf: the source ELF file -* @brief: -* generate a memory set according to the elf file -* @retval: -* the new memory set -*/ -fn memory_set_from<'a>(elf: &'a ElfFile<'a>) -> MemorySet { +/// Generate a MemorySet according to the ELF file. +/// Also return the real entry point address. +fn memory_set_from<'a>(elf: &'a ElfFile<'a>) -> (MemorySet, usize) { debug!("come in to memory_set_from"); let mut ms = MemorySet::new(); + let mut entry = None; for ph in elf.program_iter() { if ph.get_type() != Ok(Type::Load) { continue; @@ -251,8 +245,13 @@ fn memory_set_from<'a>(elf: &'a ElfFile<'a>) -> MemorySet { target[file_size..].iter_mut().for_each(|x| *x = 0); }); } + // Find real entry point + if ph.flags().is_execute() { + let origin_entry = elf.header.pt2.entry_point() as usize; + entry = Some(origin_entry - virt_addr + target.as_ptr() as usize); + } } - ms + (ms, entry.unwrap()) } fn memory_attr_from(elf_flags: Flags) -> MemoryAttr { From 02eb7d91254140edb36225485836655418c442bd Mon Sep 17 00:00:00 2001 From: WangRunji Date: Wed, 28 Nov 2018 23:59:55 +0800 Subject: [PATCH 21/39] update 'riscv-pk' to upstream submodule --- .gitmodules | 3 + kernel/Makefile | 6 +- riscv-pk | 1 + riscv-pk/.gitignore | 11 - riscv-pk/LICENSE | 24 - riscv-pk/Makefile.in | 455 -- riscv-pk/README.md | 40 - riscv-pk/aclocal.m4 | 333 -- riscv-pk/bbl/bbl.ac | 12 - riscv-pk/bbl/bbl.c | 67 - riscv-pk/bbl/bbl.h | 15 - riscv-pk/bbl/bbl.lds | 111 - riscv-pk/bbl/bbl.mk.in | 29 - riscv-pk/bbl/logo.c | 9 - riscv-pk/bbl/payload.S | 9 - riscv-pk/bbl/raw_logo.S | 7 - riscv-pk/bbl/riscv_logo.txt | 23 - riscv-pk/config.h.in | 52 - riscv-pk/configure | 5775 -------------------- riscv-pk/configure.ac | 138 - riscv-pk/dummy_payload/dummy_entry.S | 22 - riscv-pk/dummy_payload/dummy_payload.ac | 0 riscv-pk/dummy_payload/dummy_payload.c | 0 riscv-pk/dummy_payload/dummy_payload.lds | 7 - riscv-pk/dummy_payload/dummy_payload.mk.in | 13 - riscv-pk/machine/atomic.h | 78 - riscv-pk/machine/bits.h | 35 - riscv-pk/machine/disabled_hart_mask.h | 4 - riscv-pk/machine/emulation.c | 234 - riscv-pk/machine/emulation.h | 51 - riscv-pk/machine/encoding.h | 1471 ----- riscv-pk/machine/fdt.c | 740 --- riscv-pk/machine/fdt.h | 76 - riscv-pk/machine/finisher.c | 58 - riscv-pk/machine/finisher.h | 13 - riscv-pk/machine/flush_icache.c | 3 - riscv-pk/machine/htif.c | 138 - riscv-pk/machine/htif.h | 25 - riscv-pk/machine/machine.ac | 4 - riscv-pk/machine/machine.mk.in | 30 - riscv-pk/machine/mcall.h | 14 - riscv-pk/machine/mentry.S | 295 - riscv-pk/machine/minit.c | 200 - riscv-pk/machine/misaligned_ldst.c | 143 - riscv-pk/machine/mtrap.c | 235 - riscv-pk/machine/mtrap.h | 96 - riscv-pk/machine/muldiv_emulation.c | 64 - riscv-pk/machine/uart.c | 76 - riscv-pk/machine/uart.h | 21 - riscv-pk/machine/uart16550.c | 76 - riscv-pk/machine/uart16550.h | 12 - riscv-pk/machine/unprivileged_memory.h | 103 - riscv-pk/machine/vm.h | 35 - riscv-pk/scripts/config.guess | 1526 ------ riscv-pk/scripts/config.sub | 1662 ------ riscv-pk/scripts/install.sh | 238 - riscv-pk/scripts/mk-install-dirs.sh | 40 - riscv-pk/scripts/vcs-version.sh | 117 - riscv-pk/util/snprintf.c | 98 - riscv-pk/util/string.c | 90 - riscv-pk/util/util.ac | 0 riscv-pk/util/util.mk.in | 9 - 62 files changed, 8 insertions(+), 15264 deletions(-) create mode 100644 .gitmodules create mode 160000 riscv-pk delete mode 100644 riscv-pk/.gitignore delete mode 100644 riscv-pk/LICENSE delete mode 100644 riscv-pk/Makefile.in delete mode 100644 riscv-pk/README.md delete mode 100644 riscv-pk/aclocal.m4 delete mode 100644 riscv-pk/bbl/bbl.ac delete mode 100644 riscv-pk/bbl/bbl.c delete mode 100644 riscv-pk/bbl/bbl.h delete mode 100644 riscv-pk/bbl/bbl.lds delete mode 100644 riscv-pk/bbl/bbl.mk.in delete mode 100644 riscv-pk/bbl/logo.c delete mode 100644 riscv-pk/bbl/payload.S delete mode 100644 riscv-pk/bbl/raw_logo.S delete mode 100644 riscv-pk/bbl/riscv_logo.txt delete mode 100644 riscv-pk/config.h.in delete mode 100755 riscv-pk/configure delete mode 100644 riscv-pk/configure.ac delete mode 100644 riscv-pk/dummy_payload/dummy_entry.S delete mode 100644 riscv-pk/dummy_payload/dummy_payload.ac delete mode 100644 riscv-pk/dummy_payload/dummy_payload.c delete mode 100644 riscv-pk/dummy_payload/dummy_payload.lds delete mode 100644 riscv-pk/dummy_payload/dummy_payload.mk.in delete mode 100644 riscv-pk/machine/atomic.h delete mode 100644 riscv-pk/machine/bits.h delete mode 100644 riscv-pk/machine/disabled_hart_mask.h delete mode 100644 riscv-pk/machine/emulation.c delete mode 100644 riscv-pk/machine/emulation.h delete mode 100644 riscv-pk/machine/encoding.h delete mode 100644 riscv-pk/machine/fdt.c delete mode 100644 riscv-pk/machine/fdt.h delete mode 100644 riscv-pk/machine/finisher.c delete mode 100644 riscv-pk/machine/finisher.h delete mode 100644 riscv-pk/machine/flush_icache.c delete mode 100644 riscv-pk/machine/htif.c delete mode 100644 riscv-pk/machine/htif.h delete mode 100644 riscv-pk/machine/machine.ac delete mode 100644 riscv-pk/machine/machine.mk.in delete mode 100644 riscv-pk/machine/mcall.h delete mode 100644 riscv-pk/machine/mentry.S delete mode 100644 riscv-pk/machine/minit.c delete mode 100644 riscv-pk/machine/misaligned_ldst.c delete mode 100644 riscv-pk/machine/mtrap.c delete mode 100644 riscv-pk/machine/mtrap.h delete mode 100644 riscv-pk/machine/muldiv_emulation.c delete mode 100644 riscv-pk/machine/uart.c delete mode 100644 riscv-pk/machine/uart.h delete mode 100644 riscv-pk/machine/uart16550.c delete mode 100644 riscv-pk/machine/uart16550.h delete mode 100644 riscv-pk/machine/unprivileged_memory.h delete mode 100644 riscv-pk/machine/vm.h delete mode 100755 riscv-pk/scripts/config.guess delete mode 100755 riscv-pk/scripts/config.sub delete mode 100755 riscv-pk/scripts/install.sh delete mode 100755 riscv-pk/scripts/mk-install-dirs.sh delete mode 100755 riscv-pk/scripts/vcs-version.sh delete mode 100644 riscv-pk/util/snprintf.c delete mode 100644 riscv-pk/util/string.c delete mode 100644 riscv-pk/util/util.ac delete mode 100644 riscv-pk/util/util.mk.in diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b0a85f4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "riscv-pk"] + path = riscv-pk + url = https://github.com/riscv-and-rust-and-decaf/riscv-pk.git diff --git a/kernel/Makefile b/kernel/Makefile index e973700..041ea02 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -47,6 +47,9 @@ qemu_opts := \ -kernel $(bin) \ -nographic \ -smp cores=$(smp) +ifdef nommu +qemu_opts := $(qemu_opts) -cpu rv32imacu-nommu +endif else ifeq ($(arch), aarch64) qemu_opts := \ -machine $(board) \ @@ -143,8 +146,7 @@ else mkdir -p build && \ cd build && \ ../configure \ - --enable-32bit \ - --enable-logo \ + --with-arch=rv32imac \ --disable-fp-emulation \ --host=riscv64-unknown-elf \ --with-payload=$(abspath $(kernel)) && \ diff --git a/riscv-pk b/riscv-pk new file mode 160000 index 0000000..e125938 --- /dev/null +++ b/riscv-pk @@ -0,0 +1 @@ +Subproject commit e12593841af47b6b15c7de63804342ef87f271ce diff --git a/riscv-pk/.gitignore b/riscv-pk/.gitignore deleted file mode 100644 index 8595aa6..0000000 --- a/riscv-pk/.gitignore +++ /dev/null @@ -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 -*~ diff --git a/riscv-pk/LICENSE b/riscv-pk/LICENSE deleted file mode 100644 index 53e0e66..0000000 --- a/riscv-pk/LICENSE +++ /dev/null @@ -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. diff --git a/riscv-pk/Makefile.in b/riscv-pk/Makefile.in deleted file mode 100644 index 0268629..0000000 --- a/riscv-pk/Makefile.in +++ /dev/null @@ -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-. 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 diff --git a/riscv-pk/README.md b/riscv-pk/README.md deleted file mode 100644 index 799234a..0000000 --- a/riscv-pk/README.md +++ /dev/null @@ -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`. diff --git a/riscv-pk/aclocal.m4 b/riscv-pk/aclocal.m4 deleted file mode 100644 index 55a8b70..0000000 --- a/riscv-pk/aclocal.m4 +++ /dev/null @@ -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- 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- 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- the listed subprojects will be -# enabled. Groups can have the same name as a subproject and in that -# case whenever a user specifies --enable- 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}" -]) diff --git a/riscv-pk/bbl/bbl.ac b/riscv-pk/bbl/bbl.ac deleted file mode 100644 index 80d3b06..0000000 --- a/riscv-pk/bbl/bbl.ac +++ /dev/null @@ -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])]) diff --git a/riscv-pk/bbl/bbl.c b/riscv-pk/bbl/bbl.c deleted file mode 100644 index dbb9277..0000000 --- a/riscv-pk/bbl/bbl.c +++ /dev/null @@ -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 - -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); -} diff --git a/riscv-pk/bbl/bbl.h b/riscv-pk/bbl/bbl.h deleted file mode 100644 index c9a02e1..0000000 --- a/riscv-pk/bbl/bbl.h +++ /dev/null @@ -1,15 +0,0 @@ -// See LICENSE for license details. - -#ifndef _BBL_H -#define _BBL_H - -#ifndef __ASSEMBLER__ - -#include -#include - -void print_logo(); - -#endif // !__ASSEMBLER__ - -#endif diff --git a/riscv-pk/bbl/bbl.lds b/riscv-pk/bbl/bbl.lds deleted file mode 100644 index 2fd0d7c..0000000 --- a/riscv-pk/bbl/bbl.lds +++ /dev/null @@ -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 = .; -} diff --git a/riscv-pk/bbl/bbl.mk.in b/riscv-pk/bbl/bbl.mk.in deleted file mode 100644 index 4449f82..0000000 --- a/riscv-pk/bbl/bbl.mk.in +++ /dev/null @@ -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 \ diff --git a/riscv-pk/bbl/logo.c b/riscv-pk/bbl/logo.c deleted file mode 100644 index fcd43ec..0000000 --- a/riscv-pk/bbl/logo.c +++ /dev/null @@ -1,9 +0,0 @@ -#include -#include "mtrap.h" - -extern const char logo[]; - -void print_logo() -{ - putstring(logo); -} diff --git a/riscv-pk/bbl/payload.S b/riscv-pk/bbl/payload.S deleted file mode 100644 index e077f98..0000000 --- a/riscv-pk/bbl/payload.S +++ /dev/null @@ -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: diff --git a/riscv-pk/bbl/raw_logo.S b/riscv-pk/bbl/raw_logo.S deleted file mode 100644 index bddf8f4..0000000 --- a/riscv-pk/bbl/raw_logo.S +++ /dev/null @@ -1,7 +0,0 @@ -#include "encoding.h" - - .section .rodata - .globl logo -logo: - .incbin BBL_LOGO_FILE - .byte 0 diff --git a/riscv-pk/bbl/riscv_logo.txt b/riscv-pk/bbl/riscv_logo.txt deleted file mode 100644 index 1f4c9f5..0000000 --- a/riscv-pk/bbl/riscv_logo.txt +++ /dev/null @@ -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 diff --git a/riscv-pk/config.h.in b/riscv-pk/config.h.in deleted file mode 100644 index 4674a2e..0000000 --- a/riscv-pk/config.h.in +++ /dev/null @@ -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 diff --git a/riscv-pk/configure b/riscv-pk/configure deleted file mode 100755 index a41f908..0000000 --- a/riscv-pk/configure +++ /dev/null @@ -1,5775 +0,0 @@ -#! /bin/sh -# Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for RISC-V Proxy Kernel ?. -# -# Report bugs to . -# -# -# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. -# -# -# This configure script is free software; the Free Software Foundation -# gives unlimited permission to copy, distribute and modify it. -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - -as_nl=' -' -export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -case $0 in #(( - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break - done -IFS=$as_save_IFS - - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -# Use a proper internal environment variable to ensure we don't fall - # into an infinite loop, continuously re-executing ourselves. - if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then - _as_can_reexec=no; export _as_can_reexec; - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -as_fn_exit 255 - fi - # We don't want this to propagate to other subprocesses. - { _as_can_reexec=; unset _as_can_reexec;} -if test "x$CONFIG_SHELL" = x; then - as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which - # is contrary to our usage. Disable this feature. - alias -g '\${1+\"\$@\"}'='\"\$@\"' - setopt NO_GLOB_SUBST -else - case \`(set -o) 2>/dev/null\` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi -" - as_required="as_fn_return () { (exit \$1); } -as_fn_success () { as_fn_return 0; } -as_fn_failure () { as_fn_return 1; } -as_fn_ret_success () { return 0; } -as_fn_ret_failure () { return 1; } - -exitcode=0 -as_fn_success || { exitcode=1; echo as_fn_success failed.; } -as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } -as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } -as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } -if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : - -else - exitcode=1; echo positional parameters were not saved. -fi -test x\$exitcode = x0 || exit 1 -test -x / || exit 1" - as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO - as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO - eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && - test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 -test \$(( 1 + 1 )) = 2 || exit 1" - if (eval "$as_required") 2>/dev/null; then : - as_have_required=yes -else - as_have_required=no -fi - if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : - -else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -as_found=false -for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - as_found=: - case $as_dir in #( - /*) - for as_base in sh bash ksh sh5; do - # Try only shells that exist, to save several forks. - as_shell=$as_dir/$as_base - if { test -f "$as_shell" || test -f "$as_shell.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : - CONFIG_SHELL=$as_shell as_have_required=yes - if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : - break 2 -fi -fi - done;; - esac - as_found=false -done -$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : - CONFIG_SHELL=$SHELL as_have_required=yes -fi; } -IFS=$as_save_IFS - - - if test "x$CONFIG_SHELL" != x; then : - export CONFIG_SHELL - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -exit 255 -fi - - if test x$as_have_required = xno; then : - $as_echo "$0: This script requires a shell more modern than all" - $as_echo "$0: the shells that I found on your system." - if test x${ZSH_VERSION+set} = xset ; then - $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" - $as_echo "$0: be upgraded to zsh 4.3.4 or later." - else - $as_echo "$0: Please tell bug-autoconf@gnu.org and Andrew Waterman -$0: about your system, including any error possibly output -$0: before this message. Then install a modern shell, or -$0: manually run the script under such a shell if you do -$0: have one." - fi - exit 1 -fi -fi -fi -SHELL=${CONFIG_SHELL-/bin/sh} -export SHELL -# Unset more variables known to interfere with behavior of common tools. -CLICOLOR_FORCE= GREP_OPTIONS= -unset CLICOLOR_FORCE GREP_OPTIONS - -## --------------------- ## -## M4sh Shell Functions. ## -## --------------------- ## -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - $as_echo "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - - - as_lineno_1=$LINENO as_lineno_1a=$LINENO - as_lineno_2=$LINENO as_lineno_2a=$LINENO - eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && - test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { - # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) - sed -n ' - p - /[$]LINENO/= - ' <$as_myself | - sed ' - s/[$]LINENO.*/&-/ - t lineno - b - :lineno - N - :loop - s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ - t loop - s/-\n.*// - ' >$as_me.lineno && - chmod +x "$as_me.lineno" || - { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } - - # If we had to re-execute with $CONFIG_SHELL, we're ensured to have - # already done that, so ensure we don't try to do so again and fall - # in an infinite loop. This has already happened in practice. - _as_can_reexec=no; export _as_can_reexec - # Don't try to exec as it changes $[0], causing all sort of problems - # (the dirname of $[0] is not the place where we might find the - # original and so on. Autoconf is especially sensitive to this). - . "./$as_me.lineno" - # Exit status is that of the last command. - exit -} - -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -pR' - fi -else - as_ln_s='cp -pR' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - -as_test_x='test -x' -as_executable_p=as_fn_executable_p - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -test -n "$DJDIR" || exec 7<&0 &1 - -# Name of the host. -# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, -# so uname gets run too. -ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` - -# -# Initializations. -# -ac_default_prefix=/usr/local -ac_clean_files= -ac_config_libobj_dir=. -LIBOBJS= -cross_compiling=no -subdirs= -MFLAGS= -MAKEFLAGS= - -# Identity of this package. -#PACKAGE_NAME='RISC-V Proxy Kernel' -#PACKAGE_TARNAME='riscv-pk' -#PACKAGE_VERSION='?' -#PACKAGE_STRING='RISC-V Proxy Kernel ?' -#PACKAGE_BUGREPORT='Andrew Waterman' -#PACKAGE_URL='' - -#ac_unique_file="pk/pk.h" -ac_subst_vars='LTLIBOBJS -LIBOBJS -subprojects_enabled -subprojects -BBL_LOGO_FILE -BBL_PAYLOAD -install_subdir -RISCV -EGREP -GREP -CPP -stow -INSTALL_DATA -INSTALL_SCRIPT -INSTALL_PROGRAM -STOW_PREFIX -STOW_ROOT -enable_stow -OBJCOPY -READELF -RANLIB -AR -ac_ct_CXX -CXXFLAGS -CXX -OBJEXT -EXEEXT -ac_ct_CC -CPPFLAGS -LDFLAGS -CFLAGS -CC -host_os -host_vendor -host_cpu -host -build_os -build_vendor -build_cpu -build -target_alias -host_alias -build_alias -LIBS -ECHO_T -ECHO_N -ECHO_C -DEFS -mandir -localedir -libdir -psdir -pdfdir -dvidir -htmldir -infodir -docdir -oldincludedir -includedir -runstatedir -localstatedir -sharedstatedir -sysconfdir -datadir -datarootdir -libexecdir -sbindir -bindir -program_transform_name -prefix -exec_prefix -PACKAGE_URL -PACKAGE_BUGREPORT -PACKAGE_STRING -PACKAGE_VERSION -PACKAGE_TARNAME -PACKAGE_NAME -PATH_SEPARATOR -SHELL' -ac_subst_files='' -ac_user_opts=' -enable_option_checking -enable_stow -enable_32bit -enable_print_device_tree -enable_optional_subprojects -enable_vm -enable_logo -with_payload -with_logo -enable_fp_emulation -' - ac_precious_vars='build_alias -host_alias -target_alias -CC -CFLAGS -LDFLAGS -LIBS -CPPFLAGS -CXX -CXXFLAGS -CCC -STOW_ROOT -STOW_PREFIX -CPP -RISCV' - - -# Initialize some variables set by options. -ac_init_help= -ac_init_version=false -ac_unrecognized_opts= -ac_unrecognized_sep= -# The variables have the same names as the options, with -# dashes changed to underlines. -cache_file=/dev/null -exec_prefix=NONE -no_create= -no_recursion= -prefix=NONE -program_prefix=NONE -program_suffix=NONE -program_transform_name=s,x,x, -silent= -site= -srcdir= -verbose= -x_includes=NONE -x_libraries=NONE - -# Installation directory options. -# These are left unexpanded so users can "make install exec_prefix=/foo" -# and all the variables that are supposed to be based on exec_prefix -# by default will actually change. -# Use braces instead of parens because sh, perl, etc. also accept them. -# (The list follows the same order as the GNU Coding Standards.) -bindir='${exec_prefix}/bin' -sbindir='${exec_prefix}/sbin' -libexecdir='${exec_prefix}/libexec' -datarootdir='${prefix}/share' -datadir='${datarootdir}' -sysconfdir='${prefix}/etc' -sharedstatedir='${prefix}/com' -localstatedir='${prefix}/var' -runstatedir='${localstatedir}/run' -includedir='${prefix}/include' -oldincludedir='/usr/include' -docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' -infodir='${datarootdir}/info' -htmldir='${docdir}' -dvidir='${docdir}' -pdfdir='${docdir}' -psdir='${docdir}' -libdir='${exec_prefix}/lib' -localedir='${datarootdir}/locale' -mandir='${datarootdir}/man' - -ac_prev= -ac_dashdash= -for ac_option -do - # If the previous option needs an argument, assign it. - if test -n "$ac_prev"; then - eval $ac_prev=\$ac_option - ac_prev= - continue - fi - - case $ac_option in - *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; - *=) ac_optarg= ;; - *) ac_optarg=yes ;; - esac - - # Accept the important Cygnus configure options, so we can diagnose typos. - - case $ac_dashdash$ac_option in - --) - ac_dashdash=yes ;; - - -bindir | --bindir | --bindi | --bind | --bin | --bi) - ac_prev=bindir ;; - -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) - bindir=$ac_optarg ;; - - -build | --build | --buil | --bui | --bu) - ac_prev=build_alias ;; - -build=* | --build=* | --buil=* | --bui=* | --bu=*) - build_alias=$ac_optarg ;; - - -cache-file | --cache-file | --cache-fil | --cache-fi \ - | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) - ac_prev=cache_file ;; - -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ - | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) - cache_file=$ac_optarg ;; - - --config-cache | -C) - cache_file=config.cache ;; - - -datadir | --datadir | --datadi | --datad) - ac_prev=datadir ;; - -datadir=* | --datadir=* | --datadi=* | --datad=*) - datadir=$ac_optarg ;; - - -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ - | --dataroo | --dataro | --datar) - ac_prev=datarootdir ;; - -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ - | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) - datarootdir=$ac_optarg ;; - - -disable-* | --disable-*) - ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=no ;; - - -docdir | --docdir | --docdi | --doc | --do) - ac_prev=docdir ;; - -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) - docdir=$ac_optarg ;; - - -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) - ac_prev=dvidir ;; - -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) - dvidir=$ac_optarg ;; - - -enable-* | --enable-*) - ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=\$ac_optarg ;; - - -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ - | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ - | --exec | --exe | --ex) - ac_prev=exec_prefix ;; - -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ - | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ - | --exec=* | --exe=* | --ex=*) - exec_prefix=$ac_optarg ;; - - -gas | --gas | --ga | --g) - # Obsolete; use --with-gas. - with_gas=yes ;; - - -help | --help | --hel | --he | -h) - ac_init_help=long ;; - -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) - ac_init_help=recursive ;; - -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) - ac_init_help=short ;; - - -host | --host | --hos | --ho) - ac_prev=host_alias ;; - -host=* | --host=* | --hos=* | --ho=*) - host_alias=$ac_optarg ;; - - -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) - ac_prev=htmldir ;; - -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ - | --ht=*) - htmldir=$ac_optarg ;; - - -includedir | --includedir | --includedi | --included | --include \ - | --includ | --inclu | --incl | --inc) - ac_prev=includedir ;; - -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ - | --includ=* | --inclu=* | --incl=* | --inc=*) - includedir=$ac_optarg ;; - - -infodir | --infodir | --infodi | --infod | --info | --inf) - ac_prev=infodir ;; - -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) - infodir=$ac_optarg ;; - - -libdir | --libdir | --libdi | --libd) - ac_prev=libdir ;; - -libdir=* | --libdir=* | --libdi=* | --libd=*) - libdir=$ac_optarg ;; - - -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ - | --libexe | --libex | --libe) - ac_prev=libexecdir ;; - -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ - | --libexe=* | --libex=* | --libe=*) - libexecdir=$ac_optarg ;; - - -localedir | --localedir | --localedi | --localed | --locale) - ac_prev=localedir ;; - -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) - localedir=$ac_optarg ;; - - -localstatedir | --localstatedir | --localstatedi | --localstated \ - | --localstate | --localstat | --localsta | --localst | --locals) - ac_prev=localstatedir ;; - -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ - | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) - localstatedir=$ac_optarg ;; - - -mandir | --mandir | --mandi | --mand | --man | --ma | --m) - ac_prev=mandir ;; - -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) - mandir=$ac_optarg ;; - - -nfp | --nfp | --nf) - # Obsolete; use --without-fp. - with_fp=no ;; - - -no-create | --no-create | --no-creat | --no-crea | --no-cre \ - | --no-cr | --no-c | -n) - no_create=yes ;; - - -no-recursion | --no-recursion | --no-recursio | --no-recursi \ - | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) - no_recursion=yes ;; - - -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ - | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ - | --oldin | --oldi | --old | --ol | --o) - ac_prev=oldincludedir ;; - -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ - | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ - | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) - oldincludedir=$ac_optarg ;; - - -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) - ac_prev=prefix ;; - -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) - prefix=$ac_optarg ;; - - -program-prefix | --program-prefix | --program-prefi | --program-pref \ - | --program-pre | --program-pr | --program-p) - ac_prev=program_prefix ;; - -program-prefix=* | --program-prefix=* | --program-prefi=* \ - | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) - program_prefix=$ac_optarg ;; - - -program-suffix | --program-suffix | --program-suffi | --program-suff \ - | --program-suf | --program-su | --program-s) - ac_prev=program_suffix ;; - -program-suffix=* | --program-suffix=* | --program-suffi=* \ - | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) - program_suffix=$ac_optarg ;; - - -program-transform-name | --program-transform-name \ - | --program-transform-nam | --program-transform-na \ - | --program-transform-n | --program-transform- \ - | --program-transform | --program-transfor \ - | --program-transfo | --program-transf \ - | --program-trans | --program-tran \ - | --progr-tra | --program-tr | --program-t) - ac_prev=program_transform_name ;; - -program-transform-name=* | --program-transform-name=* \ - | --program-transform-nam=* | --program-transform-na=* \ - | --program-transform-n=* | --program-transform-=* \ - | --program-transform=* | --program-transfor=* \ - | --program-transfo=* | --program-transf=* \ - | --program-trans=* | --program-tran=* \ - | --progr-tra=* | --program-tr=* | --program-t=*) - program_transform_name=$ac_optarg ;; - - -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) - ac_prev=pdfdir ;; - -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) - pdfdir=$ac_optarg ;; - - -psdir | --psdir | --psdi | --psd | --ps) - ac_prev=psdir ;; - -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) - psdir=$ac_optarg ;; - - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - silent=yes ;; - - -runstatedir | --runstatedir | --runstatedi | --runstated \ - | --runstate | --runstat | --runsta | --runst | --runs \ - | --run | --ru | --r) - ac_prev=runstatedir ;; - -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ - | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ - | --run=* | --ru=* | --r=*) - runstatedir=$ac_optarg ;; - - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) - ac_prev=sbindir ;; - -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ - | --sbi=* | --sb=*) - sbindir=$ac_optarg ;; - - -sharedstatedir | --sharedstatedir | --sharedstatedi \ - | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ - | --sharedst | --shareds | --shared | --share | --shar \ - | --sha | --sh) - ac_prev=sharedstatedir ;; - -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ - | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ - | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ - | --sha=* | --sh=*) - sharedstatedir=$ac_optarg ;; - - -site | --site | --sit) - ac_prev=site ;; - -site=* | --site=* | --sit=*) - site=$ac_optarg ;; - - -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) - ac_prev=srcdir ;; - -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) - srcdir=$ac_optarg ;; - - -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ - | --syscon | --sysco | --sysc | --sys | --sy) - ac_prev=sysconfdir ;; - -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ - | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) - sysconfdir=$ac_optarg ;; - - -target | --target | --targe | --targ | --tar | --ta | --t) - ac_prev=target_alias ;; - -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) - target_alias=$ac_optarg ;; - - -v | -verbose | --verbose | --verbos | --verbo | --verb) - verbose=yes ;; - - -version | --version | --versio | --versi | --vers | -V) - ac_init_version=: ;; - - -with-* | --with-*) - ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=\$ac_optarg ;; - - -without-* | --without-*) - ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=no ;; - - --x) - # Obsolete; use --with-x. - with_x=yes ;; - - -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ - | --x-incl | --x-inc | --x-in | --x-i) - ac_prev=x_includes ;; - -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ - | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) - x_includes=$ac_optarg ;; - - -x-libraries | --x-libraries | --x-librarie | --x-librari \ - | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) - ac_prev=x_libraries ;; - -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ - | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) - x_libraries=$ac_optarg ;; - - -*) as_fn_error $? "unrecognized option: \`$ac_option' -Try \`$0 --help' for more information" - ;; - - *=*) - ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` - # Reject names that are not valid shell variable names. - case $ac_envvar in #( - '' | [0-9]* | *[!_$as_cr_alnum]* ) - as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; - esac - eval $ac_envvar=\$ac_optarg - export $ac_envvar ;; - - *) - # FIXME: should be removed in autoconf 3.0. - $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 - expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && - $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 - : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" - ;; - - esac -done - -if test -n "$ac_prev"; then - ac_option=--`echo $ac_prev | sed 's/_/-/g'` - as_fn_error $? "missing argument to $ac_option" -fi - -if test -n "$ac_unrecognized_opts"; then - case $enable_option_checking in - no) ;; - fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; - *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; - esac -fi - -# Check all directory arguments for consistency. -for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ - datadir sysconfdir sharedstatedir localstatedir includedir \ - oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir runstatedir -do - eval ac_val=\$$ac_var - # Remove trailing slashes. - case $ac_val in - */ ) - ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` - eval $ac_var=\$ac_val;; - esac - # Be sure to have absolute directory names. - case $ac_val in - [\\/$]* | ?:[\\/]* ) continue;; - NONE | '' ) case $ac_var in *prefix ) continue;; esac;; - esac - as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" -done - -# There might be people who depend on the old broken behavior: `$host' -# used to hold the argument of --host etc. -# FIXME: To remove some day. -build=$build_alias -host=$host_alias -target=$target_alias - -# FIXME: To remove some day. -if test "x$host_alias" != x; then - if test "x$build_alias" = x; then - cross_compiling=maybe - elif test "x$build_alias" != "x$host_alias"; then - cross_compiling=yes - fi -fi - -ac_tool_prefix= -test -n "$host_alias" && ac_tool_prefix=$host_alias- - -test "$silent" = yes && exec 6>/dev/null - - -ac_pwd=`pwd` && test -n "$ac_pwd" && -ac_ls_di=`ls -di .` && -ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || - as_fn_error $? "working directory cannot be determined" -test "X$ac_ls_di" = "X$ac_pwd_ls_di" || - as_fn_error $? "pwd does not report name of working directory" - - -# Find the source files, if location was not specified. -if test -z "$srcdir"; then - ac_srcdir_defaulted=yes - # Try the directory containing this script, then the parent directory. - ac_confdir=`$as_dirname -- "$as_myself" || -$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_myself" : 'X\(//\)[^/]' \| \ - X"$as_myself" : 'X\(//\)$' \| \ - X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_myself" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - srcdir=$ac_confdir - if test ! -r "$srcdir/$ac_unique_file"; then - srcdir=.. - fi -else - ac_srcdir_defaulted=no -fi -if test ! -r "$srcdir/$ac_unique_file"; then - test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." - as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" -fi -ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" -ac_abs_confdir=`( - cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" - pwd)` -# When building in place, set srcdir=. -if test "$ac_abs_confdir" = "$ac_pwd"; then - srcdir=. -fi -# Remove unnecessary trailing slashes from srcdir. -# Double slashes in file names in object file debugging info -# mess up M-x gdb in Emacs. -case $srcdir in -*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; -esac -for ac_var in $ac_precious_vars; do - eval ac_env_${ac_var}_set=\${${ac_var}+set} - eval ac_env_${ac_var}_value=\$${ac_var} - eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} - eval ac_cv_env_${ac_var}_value=\$${ac_var} -done - -# -# Report the --help message. -# -if test "$ac_init_help" = "long"; then - # Omit some internal or obsolete options to make the list less imposing. - # This message is too long to be a string in the A/UX 3.1 sh. - cat <<_ACEOF -\`configure' configures RISC-V Proxy Kernel ? to adapt to many kinds of systems. - -Usage: $0 [OPTION]... [VAR=VALUE]... - -To assign environment variables (e.g., CC, CFLAGS...), specify them as -VAR=VALUE. See below for descriptions of some of the useful variables. - -Defaults for the options are specified in brackets. - -Configuration: - -h, --help display this help and exit - --help=short display options specific to this package - --help=recursive display the short help of all the included packages - -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking ...' messages - --cache-file=FILE cache test results in FILE [disabled] - -C, --config-cache alias for \`--cache-file=config.cache' - -n, --no-create do not create output files - --srcdir=DIR find the sources in DIR [configure dir or \`..'] - -Installation directories: - --prefix=PREFIX install architecture-independent files in PREFIX - [$ac_default_prefix] - --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX - [PREFIX] - -By default, \`make install' will install all the files in -\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify -an installation prefix other than \`$ac_default_prefix' using \`--prefix', -for instance \`--prefix=\$HOME'. - -For better control, use the options below. - -Fine tuning of the installation directories: - --bindir=DIR user executables [EPREFIX/bin] - --sbindir=DIR system admin executables [EPREFIX/sbin] - --libexecdir=DIR program executables [EPREFIX/libexec] - --sysconfdir=DIR read-only single-machine data [PREFIX/etc] - --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] - --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] - --libdir=DIR object code libraries [EPREFIX/lib] - --includedir=DIR C header files [PREFIX/include] - --oldincludedir=DIR C header files for non-gcc [/usr/include] - --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] - --datadir=DIR read-only architecture-independent data [DATAROOTDIR] - --infodir=DIR info documentation [DATAROOTDIR/info] - --localedir=DIR locale-dependent data [DATAROOTDIR/locale] - --mandir=DIR man documentation [DATAROOTDIR/man] - --docdir=DIR documentation root [DATAROOTDIR/doc/riscv-pk] - --htmldir=DIR html documentation [DOCDIR] - --dvidir=DIR dvi documentation [DOCDIR] - --pdfdir=DIR pdf documentation [DOCDIR] - --psdir=DIR ps documentation [DOCDIR] -_ACEOF - - cat <<\_ACEOF - -System types: - --build=BUILD configure for building on BUILD [guessed] - --host=HOST cross-compile to build programs to run on HOST [BUILD] -_ACEOF -fi - -if test -n "$ac_init_help"; then - case $ac_init_help in - short | recursive ) echo "Configuration of RISC-V Proxy Kernel ?:";; - esac - cat <<\_ACEOF - -Optional Features: - --disable-option-checking ignore unrecognized --enable/--with options - --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) - --enable-FEATURE[=ARG] include FEATURE [ARG=yes] - --enable-stow Enable stow-based install - --enable-32bit Build a 32-bit pk - --enable-print-device-tree - Print DTS when booting - --enable-optional-subprojects - Enable all optional subprojects - --disable-vm Disable virtual memory - --enable-logo Enable boot logo - --disable-fp-emulation Disable floating-point emulation - -Optional Packages: - --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] - --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) - --with-payload Set ELF payload for bbl - --with-logo Specify a better logo - -Some influential environment variables: - CC C compiler command - CFLAGS C compiler flags - LDFLAGS linker flags, e.g. -L if you have libraries in a - nonstandard directory - LIBS libraries to pass to the linker, e.g. -l - CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if - you have headers in a nonstandard directory - CXX C++ compiler command - CXXFLAGS C++ compiler flags - STOW_ROOT Root for non-native stow-based installs - STOW_PREFIX Prefix for stow-based installs - CPP C preprocessor - RISCV top-level RISC-V install directory - -Use these variables to override the choices made by `configure' or to help -it to find libraries and programs with nonstandard names/locations. - -Report bugs to . -_ACEOF -ac_status=$? -fi - -if test "$ac_init_help" = "recursive"; then - # If there are subdirs, report their specific --help. - for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue - test -d "$ac_dir" || - { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || - continue - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - cd "$ac_dir" || { ac_status=$?; continue; } - # Check for guested configure. - if test -f "$ac_srcdir/configure.gnu"; then - echo && - $SHELL "$ac_srcdir/configure.gnu" --help=recursive - elif test -f "$ac_srcdir/configure"; then - echo && - $SHELL "$ac_srcdir/configure" --help=recursive - else - $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 - fi || ac_status=$? - cd "$ac_pwd" || { ac_status=$?; break; } - done -fi - -test -n "$ac_init_help" && exit $ac_status -if $ac_init_version; then - cat <<\_ACEOF -RISC-V Proxy Kernel configure ? -generated by GNU Autoconf 2.69 - -Copyright (C) 2012 Free Software Foundation, Inc. -This configure script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it. -_ACEOF - exit -fi - -## ------------------------ ## -## Autoconf initialization. ## -## ------------------------ ## - -# ac_fn_c_try_compile LINENO -# -------------------------- -# Try to compile conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext - if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_compile - -# ac_fn_cxx_try_compile LINENO -# ---------------------------- -# Try to compile conftest.$ac_ext, and return whether this succeeded. -ac_fn_cxx_try_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext - if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_cxx_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_cxx_try_compile - -# ac_fn_c_try_cpp LINENO -# ---------------------- -# Try to preprocess conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_cpp () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } > conftest.i && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_cpp - -# ac_fn_c_try_run LINENO -# ---------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes -# that executables *can* be run. -ac_fn_c_try_run () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then : - ac_retval=0 -else - $as_echo "$as_me: program exited with status $ac_status" >&5 - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=$ac_status -fi - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_run -cat >config.log <<_ACEOF -This file contains any messages produced by compilers while -running configure, to aid debugging if configure makes a mistake. - -It was created by RISC-V Proxy Kernel $as_me ?, which was -generated by GNU Autoconf 2.69. Invocation command line was - - $ $0 $@ - -_ACEOF -exec 5>>config.log -{ -cat <<_ASUNAME -## --------- ## -## Platform. ## -## --------- ## - -hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` - -/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` -/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` -/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` -/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` - -_ASUNAME - -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - $as_echo "PATH: $as_dir" - done -IFS=$as_save_IFS - -} >&5 - -cat >&5 <<_ACEOF - - -## ----------- ## -## Core tests. ## -## ----------- ## - -_ACEOF - - -# Keep a trace of the command line. -# Strip out --no-create and --no-recursion so they do not pile up. -# Strip out --silent because we don't want to record it for future runs. -# Also quote any args containing shell meta-characters. -# Make two passes to allow for proper duplicate-argument suppression. -ac_configure_args= -ac_configure_args0= -ac_configure_args1= -ac_must_keep_next=false -for ac_pass in 1 2 -do - for ac_arg - do - case $ac_arg in - -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - continue ;; - *\'*) - ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - case $ac_pass in - 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; - 2) - as_fn_append ac_configure_args1 " '$ac_arg'" - if test $ac_must_keep_next = true; then - ac_must_keep_next=false # Got value, back to normal. - else - case $ac_arg in - *=* | --config-cache | -C | -disable-* | --disable-* \ - | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ - | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ - | -with-* | --with-* | -without-* | --without-* | --x) - case "$ac_configure_args0 " in - "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; - esac - ;; - -* ) ac_must_keep_next=true ;; - esac - fi - as_fn_append ac_configure_args " '$ac_arg'" - ;; - esac - done -done -{ ac_configure_args0=; unset ac_configure_args0;} -{ ac_configure_args1=; unset ac_configure_args1;} - -# When interrupted or exit'd, cleanup temporary files, and complete -# config.log. We remove comments because anyway the quotes in there -# would cause problems or look ugly. -# WARNING: Use '\'' to represent an apostrophe within the trap. -# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. -trap 'exit_status=$? - # Save into config.log some information that might help in debugging. - { - echo - - $as_echo "## ---------------- ## -## Cache variables. ## -## ---------------- ##" - echo - # The following way of writing the cache mishandles newlines in values, -( - for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - (set) 2>&1 | - case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - sed -n \ - "s/'\''/'\''\\\\'\'''\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" - ;; #( - *) - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) - echo - - $as_echo "## ----------------- ## -## Output variables. ## -## ----------------- ##" - echo - for ac_var in $ac_subst_vars - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - $as_echo "$ac_var='\''$ac_val'\''" - done | sort - echo - - if test -n "$ac_subst_files"; then - $as_echo "## ------------------- ## -## File substitutions. ## -## ------------------- ##" - echo - for ac_var in $ac_subst_files - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - $as_echo "$ac_var='\''$ac_val'\''" - done | sort - echo - fi - - if test -s confdefs.h; then - $as_echo "## ----------- ## -## confdefs.h. ## -## ----------- ##" - echo - cat confdefs.h - echo - fi - test "$ac_signal" != 0 && - $as_echo "$as_me: caught signal $ac_signal" - $as_echo "$as_me: exit $exit_status" - } >&5 - rm -f core *.core core.conftest.* && - rm -f -r conftest* confdefs* conf$$* $ac_clean_files && - exit $exit_status -' 0 -for ac_signal in 1 2 13 15; do - trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal -done -ac_signal=0 - -# confdefs.h avoids OS command line length limits that DEFS can exceed. -rm -f -r conftest* confdefs.h - -$as_echo "/* confdefs.h */" > confdefs.h - -# Predefined preprocessor variables. - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_NAME "$PACKAGE_NAME" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_TARNAME "$PACKAGE_TARNAME" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_VERSION "$PACKAGE_VERSION" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_STRING "$PACKAGE_STRING" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_URL "$PACKAGE_URL" -_ACEOF - - -# Let the site file select an alternate cache file if it wants to. -# Prefer an explicitly selected file to automatically selected ones. -ac_site_file1=NONE -ac_site_file2=NONE -if test -n "$CONFIG_SITE"; then - # We do not want a PATH search for config.site. - case $CONFIG_SITE in #(( - -*) ac_site_file1=./$CONFIG_SITE;; - */*) ac_site_file1=$CONFIG_SITE;; - *) ac_site_file1=./$CONFIG_SITE;; - esac -elif test "x$prefix" != xNONE; then - ac_site_file1=$prefix/share/config.site - ac_site_file2=$prefix/etc/config.site -else - ac_site_file1=$ac_default_prefix/share/config.site - ac_site_file2=$ac_default_prefix/etc/config.site -fi -for ac_site_file in "$ac_site_file1" "$ac_site_file2" -do - test "x$ac_site_file" = xNONE && continue - if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 -$as_echo "$as_me: loading site script $ac_site_file" >&6;} - sed 's/^/| /' "$ac_site_file" >&5 - . "$ac_site_file" \ - || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "failed to load site script $ac_site_file -See \`config.log' for more details" "$LINENO" 5; } - fi -done - -if test -r "$cache_file"; then - # Some versions of bash will fail to source /dev/null (special files - # actually), so we avoid doing that. DJGPP emulates it as a regular file. - if test /dev/null != "$cache_file" && test -f "$cache_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 -$as_echo "$as_me: loading cache $cache_file" >&6;} - case $cache_file in - [\\/]* | ?:[\\/]* ) . "$cache_file";; - *) . "./$cache_file";; - esac - fi -else - { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 -$as_echo "$as_me: creating cache $cache_file" >&6;} - >$cache_file -fi - -# Check that the precious variables saved in the cache have kept the same -# value. -ac_cache_corrupted=false -for ac_var in $ac_precious_vars; do - eval ac_old_set=\$ac_cv_env_${ac_var}_set - eval ac_new_set=\$ac_env_${ac_var}_set - eval ac_old_val=\$ac_cv_env_${ac_var}_value - eval ac_new_val=\$ac_env_${ac_var}_value - case $ac_old_set,$ac_new_set in - set,) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,set) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,);; - *) - if test "x$ac_old_val" != "x$ac_new_val"; then - # differences in whitespace do not lead to failure. - ac_old_val_w=`echo x $ac_old_val` - ac_new_val_w=`echo x $ac_new_val` - if test "$ac_old_val_w" != "$ac_new_val_w"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 -$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} - ac_cache_corrupted=: - else - { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 -$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} - eval $ac_var=\$ac_old_val - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 -$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 -$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} - fi;; - esac - # Pass precious variables to config.status. - if test "$ac_new_set" = set; then - case $ac_new_val in - *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; - *) ac_arg=$ac_var=$ac_new_val ;; - esac - case " $ac_configure_args " in - *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. - *) as_fn_append ac_configure_args " '$ac_arg'" ;; - esac - fi -done -if $ac_cache_corrupted; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 -$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 -fi -## -------------------- ## -## Main body of script. ## -## -------------------- ## - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - - -ac_aux_dir= -for ac_dir in scripts "$srcdir"/scripts; do - if test -f "$ac_dir/install-sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install-sh -c" - break - elif test -f "$ac_dir/install.sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install.sh -c" - break - elif test -f "$ac_dir/shtool"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/shtool install -c" - break - fi -done -if test -z "$ac_aux_dir"; then - as_fn_error $? "cannot find install-sh, install.sh, or shtool in scripts \"$srcdir\"/scripts" "$LINENO" 5 -fi - -# These three variables are undocumented and unsupported, -# and are intended to be withdrawn in a future Autoconf release. -# They can cause serious problems if a builder's source tree is in a directory -# whose full name contains unusual characters. -ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. -ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. -ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. - - -# Make sure we can run config.sub. -$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || - as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 -$as_echo_n "checking build system type... " >&6; } -if ${ac_cv_build+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_build_alias=$build_alias -test "x$ac_build_alias" = x && - ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` -test "x$ac_build_alias" = x && - as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 -ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 -$as_echo "$ac_cv_build" >&6; } -case $ac_cv_build in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; -esac -build=$ac_cv_build -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_build -shift -build_cpu=$1 -build_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -build_os=$* -IFS=$ac_save_IFS -case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 -$as_echo_n "checking host system type... " >&6; } -if ${ac_cv_host+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "x$host_alias" = x; then - ac_cv_host=$ac_cv_build -else - ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 -$as_echo "$ac_cv_host" >&6; } -case $ac_cv_host in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; -esac -host=$ac_cv_host -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_host -shift -host_cpu=$1 -host_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -host_os=$* -IFS=$ac_save_IFS -case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac - - - -#------------------------------------------------------------------------- -# Checks for programs -#------------------------------------------------------------------------- - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - fi -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_CC" && break -done - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -fi - -fi - - -test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } - -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" -# Try to create an executable without -o first, disregard a.out. -# It will help us diagnose broken compilers, and finding out an intuition -# of exeext. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 -$as_echo_n "checking whether the C compiler works... " >&6; } -ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` - -# The possible output files: -ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" - -ac_rmfiles= -for ac_file in $ac_files -do - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - * ) ac_rmfiles="$ac_rmfiles $ac_file";; - esac -done -rm -f $ac_rmfiles - -if { { ac_try="$ac_link_default" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link_default") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. -# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' -# in a Makefile. We should not override ac_cv_exeext if it was cached, -# so that the user can short-circuit this test for compilers unknown to -# Autoconf. -for ac_file in $ac_files '' -do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) - ;; - [ab].out ) - # We found the default executable, but exeext='' is most - # certainly right. - break;; - *.* ) - if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; - then :; else - ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - fi - # We set ac_cv_exeext here because the later test for it is not - # safe: cross compilers may not add the suffix if given an `-o' - # argument, so we may need to know it at that point already. - # Even if this section looks crufty: it has the advantage of - # actually working. - break;; - * ) - break;; - esac -done -test "$ac_cv_exeext" = no && ac_cv_exeext= - -else - ac_file='' -fi -if test -z "$ac_file"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -$as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "C compiler cannot create executables -See \`config.log' for more details" "$LINENO" 5; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 -$as_echo_n "checking for C compiler default output file name... " >&6; } -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 -$as_echo "$ac_file" >&6; } -ac_exeext=$ac_cv_exeext - -rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out -ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 -$as_echo_n "checking for suffix of executables... " >&6; } -if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - # If both `conftest.exe' and `conftest' are `present' (well, observable) -# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will -# work properly (i.e., refer to `conftest.exe'), while it won't with -# `rm'. -for ac_file in conftest.exe conftest conftest.*; do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - break;; - * ) break;; - esac -done -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest conftest$ac_cv_exeext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 -$as_echo "$ac_cv_exeext" >&6; } - -rm -f conftest.$ac_ext -EXEEXT=$ac_cv_exeext -ac_exeext=$EXEEXT -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -FILE *f = fopen ("conftest.out", "w"); - return ferror (f) || fclose (f) != 0; - - ; - return 0; -} -_ACEOF -ac_clean_files="$ac_clean_files conftest.out" -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 -$as_echo_n "checking whether we are cross compiling... " >&6; } -if test "$cross_compiling" != yes; then - { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - if { ac_try='./conftest$ac_cv_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then - cross_compiling=no - else - if test "$cross_compiling" = maybe; then - cross_compiling=yes - else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details" "$LINENO" 5; } - fi - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 -$as_echo "$cross_compiling" >&6; } - -rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out -ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 -$as_echo_n "checking for suffix of object files... " >&6; } -if ${ac_cv_objext+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.o conftest.obj -if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - for ac_file in conftest.o conftest.obj conftest.*; do - test -f "$ac_file" || continue; - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; - *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` - break;; - esac -done -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of object files: cannot compile -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest.$ac_cv_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 -$as_echo "$ac_cv_objext" >&6; } -OBJEXT=$ac_cv_objext -ac_objext=$OBJEXT -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 -$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if ${ac_cv_c_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -$as_echo "$ac_cv_c_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GCC=yes -else - GCC= -fi -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -$as_echo_n "checking whether $CC accepts -g... " >&6; } -if ${ac_cv_prog_cc_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -else - CFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - -else - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -$as_echo "$ac_cv_prog_cc_g" >&6; } -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 -$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } -if ${ac_cv_prog_cc_c89+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_prog_cc_c89=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -struct stat; -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) 'x' -int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} -_ACEOF -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ - -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_c89=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c89" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC - -fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c89" in - x) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -$as_echo "none needed" >&6; } ;; - xno) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -$as_echo "unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c89" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 -$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c89" != xno; then : - -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu -if test -z "$CXX"; then - if test -n "$CCC"; then - CXX=$CCC - else - if test -n "$ac_tool_prefix"; then - for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CXX"; then - ac_cv_prog_CXX="$CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CXX=$ac_cv_prog_CXX -if test -n "$CXX"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 -$as_echo "$CXX" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CXX" && break - done -fi -if test -z "$CXX"; then - ac_ct_CXX=$CXX - for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CXX+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CXX"; then - ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CXX="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CXX=$ac_cv_prog_ac_ct_CXX -if test -n "$ac_ct_CXX"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 -$as_echo "$ac_ct_CXX" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_CXX" && break -done - - if test "x$ac_ct_CXX" = x; then - CXX="g++" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CXX=$ac_ct_CXX - fi -fi - - fi -fi -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 -$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } -if ${ac_cv_cxx_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_cxx_compiler_gnu=$ac_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 -$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GXX=yes -else - GXX= -fi -ac_test_CXXFLAGS=${CXXFLAGS+set} -ac_save_CXXFLAGS=$CXXFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 -$as_echo_n "checking whether $CXX accepts -g... " >&6; } -if ${ac_cv_prog_cxx_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_cxx_werror_flag=$ac_cxx_werror_flag - ac_cxx_werror_flag=yes - ac_cv_prog_cxx_g=no - CXXFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_prog_cxx_g=yes -else - CXXFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - -else - ac_cxx_werror_flag=$ac_save_cxx_werror_flag - CXXFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cv_prog_cxx_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_cxx_werror_flag=$ac_save_cxx_werror_flag -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 -$as_echo "$ac_cv_prog_cxx_g" >&6; } -if test "$ac_test_CXXFLAGS" = set; then - CXXFLAGS=$ac_save_CXXFLAGS -elif test $ac_cv_prog_cxx_g = yes; then - if test "$GXX" = yes; then - CXXFLAGS="-g -O2" - else - CXXFLAGS="-g" - fi -else - if test "$GXX" = yes; then - CXXFLAGS="-O2" - else - CXXFLAGS= - fi -fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. -set dummy ${ac_tool_prefix}ar; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_AR+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$AR"; then - ac_cv_prog_AR="$AR" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_AR="${ac_tool_prefix}ar" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -AR=$ac_cv_prog_AR -if test -n "$AR"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 -$as_echo "$AR" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_AR"; then - ac_ct_AR=$AR - # Extract the first word of "ar", so it can be a program name with args. -set dummy ar; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_AR+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_AR"; then - ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_AR="ar" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_AR=$ac_cv_prog_ac_ct_AR -if test -n "$ac_ct_AR"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 -$as_echo "$ac_ct_AR" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_AR" = x; then - AR="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - AR=$ac_ct_AR - fi -else - AR="$ac_cv_prog_AR" -fi - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. -set dummy ${ac_tool_prefix}ranlib; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_RANLIB+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$RANLIB"; then - ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -RANLIB=$ac_cv_prog_RANLIB -if test -n "$RANLIB"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 -$as_echo "$RANLIB" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_RANLIB"; then - ac_ct_RANLIB=$RANLIB - # Extract the first word of "ranlib", so it can be a program name with args. -set dummy ranlib; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_RANLIB"; then - ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_RANLIB="ranlib" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB -if test -n "$ac_ct_RANLIB"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 -$as_echo "$ac_ct_RANLIB" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_RANLIB" = x; then - RANLIB="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - RANLIB=$ac_ct_RANLIB - fi -else - RANLIB="$ac_cv_prog_RANLIB" -fi - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}readelf", so it can be a program name with args. -set dummy ${ac_tool_prefix}readelf; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_READELF+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$READELF"; then - ac_cv_prog_READELF="$READELF" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_READELF="${ac_tool_prefix}readelf" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -READELF=$ac_cv_prog_READELF -if test -n "$READELF"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $READELF" >&5 -$as_echo "$READELF" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_READELF"; then - ac_ct_READELF=$READELF - # Extract the first word of "readelf", so it can be a program name with args. -set dummy readelf; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_READELF+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_READELF"; then - ac_cv_prog_ac_ct_READELF="$ac_ct_READELF" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_READELF="readelf" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_READELF=$ac_cv_prog_ac_ct_READELF -if test -n "$ac_ct_READELF"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_READELF" >&5 -$as_echo "$ac_ct_READELF" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_READELF" = x; then - READELF="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - READELF=$ac_ct_READELF - fi -else - READELF="$ac_cv_prog_READELF" -fi - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}objcopy", so it can be a program name with args. -set dummy ${ac_tool_prefix}objcopy; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_OBJCOPY+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$OBJCOPY"; then - ac_cv_prog_OBJCOPY="$OBJCOPY" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_OBJCOPY="${ac_tool_prefix}objcopy" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -OBJCOPY=$ac_cv_prog_OBJCOPY -if test -n "$OBJCOPY"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJCOPY" >&5 -$as_echo "$OBJCOPY" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_OBJCOPY"; then - ac_ct_OBJCOPY=$OBJCOPY - # Extract the first word of "objcopy", so it can be a program name with args. -set dummy objcopy; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_OBJCOPY+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_OBJCOPY"; then - ac_cv_prog_ac_ct_OBJCOPY="$ac_ct_OBJCOPY" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_OBJCOPY="objcopy" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_OBJCOPY=$ac_cv_prog_ac_ct_OBJCOPY -if test -n "$ac_ct_OBJCOPY"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJCOPY" >&5 -$as_echo "$ac_ct_OBJCOPY" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_OBJCOPY" = x; then - OBJCOPY="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - OBJCOPY=$ac_ct_OBJCOPY - fi -else - OBJCOPY="$ac_cv_prog_OBJCOPY" -fi - - -#------------------------------------------------------------------------- -# 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. - -# Find a good install program. We prefer a C program (faster), -# so one script is as good as another. But avoid the broken or -# incompatible versions: -# SysV /etc/install, /usr/sbin/install -# SunOS /usr/etc/install -# IRIX /sbin/install -# AIX /bin/install -# AmigaOS /C/install, which installs bootblocks on floppy discs -# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag -# AFS /usr/afsws/bin/install, which mishandles nonexistent args -# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" -# OS/2's system install, which has a completely different semantic -# ./install, which can be erroneously created by make from ./install.sh. -# Reject install programs that cannot install multiple files. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 -$as_echo_n "checking for a BSD-compatible install... " >&6; } -if test -z "$INSTALL"; then -if ${ac_cv_path_install+:} false; then : - $as_echo_n "(cached) " >&6 -else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - # Account for people who put trailing slashes in PATH elements. -case $as_dir/ in #(( - ./ | .// | /[cC]/* | \ - /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ - ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ - /usr/ucb/* ) ;; - *) - # OSF1 and SCO ODT 3.0 have their own names for install. - # Don't use installbsd from OSF since it installs stuff as root - # by default. - for ac_prog in ginstall scoinst install; do - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then - if test $ac_prog = install && - grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then - # AIX install. It has an incompatible calling convention. - : - elif test $ac_prog = install && - grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then - # program-specific install script used by HP pwplus--don't use. - : - else - rm -rf conftest.one conftest.two conftest.dir - echo one > conftest.one - echo two > conftest.two - mkdir conftest.dir - if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && - test -s conftest.one && test -s conftest.two && - test -s conftest.dir/conftest.one && - test -s conftest.dir/conftest.two - then - ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" - break 3 - fi - fi - fi - done - done - ;; -esac - - done -IFS=$as_save_IFS - -rm -rf conftest.one conftest.two conftest.dir - -fi - if test "${ac_cv_path_install+set}" = set; then - INSTALL=$ac_cv_path_install - else - # As a last resort, use the slow shell script. Don't cache a - # value for INSTALL within a source directory, because that will - # break other packages using the cache if that directory is - # removed, or if the value is a relative name. - INSTALL=$ac_install_sh - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 -$as_echo "$INSTALL" >&6; } - -# Use test -z because SunOS4 sh mishandles braces in ${var-val}. -# It thinks the first close brace ends the variable substitution. -test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' - -test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' - -test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' - - - - # Configure command line option - - # Check whether --enable-stow was given. -if test "${enable_stow+set}" = set; then : - enableval=$enable_stow; enable_stow="yes" -else - enable_stow="no" -fi - - - - - # Environment variables - - - - - # Check for install script - - - - # Deterimine if native build and set prefix appropriately - - if test ${enable_stow} = "yes" ; then : - - for ac_prog in stow -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_stow+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$stow"; then - ac_cv_prog_stow="$stow" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_stow="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -stow=$ac_cv_prog_stow -if test -n "$stow"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $stow" >&5 -$as_echo "$stow" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$stow" && break -done -test -n "$stow" || stow="no" - - if test ${stow} = "no" ; then : - - as_fn_error $? "Cannot use --enable-stow since stow is not available" "$LINENO" 5 - -fi - - # Check if native or non-native build - - if test "${build}" = "${host}" ; then : - - - # build == host so this is a native build. Make sure --prefix not - # set and $STOW_PREFIX is set, then set prefix=$STOW_PREFIX. - - if test "${prefix}" = "NONE" && test -n "${STOW_PREFIX}" ; then : - - prefix="${STOW_PREFIX}" - { $as_echo "$as_me:${as_lineno-$LINENO}: Using \$STOW_PREFIX from environment" >&5 -$as_echo "$as_me: Using \$STOW_PREFIX from environment" >&6;} - { $as_echo "$as_me:${as_lineno-$LINENO}: prefix=${prefix}" >&5 -$as_echo "$as_me: prefix=${prefix}" >&6;} - -fi - - -else - - - # 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}. - - if test "${prefix}" = "NONE" && test -n "${STOW_ROOT}" ; then : - - prefix="${STOW_ROOT}/${host_alias}" - { $as_echo "$as_me:${as_lineno-$LINENO}: Using \$STOW_ROOT from environment" >&5 -$as_echo "$as_me: Using \$STOW_ROOT from environment" >&6;} - { $as_echo "$as_me:${as_lineno-$LINENO}: prefix=${prefix}" >&5 -$as_echo "$as_me: prefix=${prefix}" >&6;} - -fi - - -fi - - -fi - - - -#------------------------------------------------------------------------- -# Checks for header files -#------------------------------------------------------------------------- - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 -$as_echo_n "checking how to run the C preprocessor... " >&6; } -# On Suns, sometimes $CPP names a directory. -if test -n "$CPP" && test -d "$CPP"; then - CPP= -fi -if test -z "$CPP"; then - if ${ac_cv_prog_CPP+:} false; then : - $as_echo_n "(cached) " >&6 -else - # Double quotes because CPP needs to be expanded - for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" - do - ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - break -fi - - done - ac_cv_prog_CPP=$CPP - -fi - CPP=$ac_cv_prog_CPP -else - ac_cv_prog_CPP=$CPP -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 -$as_echo "$CPP" >&6; } -ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 -$as_echo_n "checking for grep that handles long lines and -e... " >&6; } -if ${ac_cv_path_GREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$GREP"; then - ac_path_GREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in grep ggrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_GREP" || continue -# Check for GNU ac_path_GREP and select it if it is found. - # Check for GNU $ac_path_GREP -case `"$ac_path_GREP" --version 2>&1` in -*GNU*) - ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'GREP' >> "conftest.nl" - "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_GREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_GREP="$ac_path_GREP" - ac_path_GREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_GREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_GREP"; then - as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_GREP=$GREP -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 -$as_echo "$ac_cv_path_GREP" >&6; } - GREP="$ac_cv_path_GREP" - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 -$as_echo_n "checking for egrep... " >&6; } -if ${ac_cv_path_EGREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 - then ac_cv_path_EGREP="$GREP -E" - else - if test -z "$EGREP"; then - ac_path_EGREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in egrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_EGREP" || continue -# Check for GNU ac_path_EGREP and select it if it is found. - # Check for GNU $ac_path_EGREP -case `"$ac_path_EGREP" --version 2>&1` in -*GNU*) - ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'EGREP' >> "conftest.nl" - "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_EGREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_EGREP="$ac_path_EGREP" - ac_path_EGREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_EGREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_EGREP"; then - as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_EGREP=$EGREP -fi - - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 -$as_echo "$ac_cv_path_EGREP" >&6; } - EGREP="$ac_cv_path_EGREP" - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 -$as_echo_n "checking for ANSI C header files... " >&6; } -if ${ac_cv_header_stdc+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#include -#include - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_header_stdc=yes -else - ac_cv_header_stdc=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - -if test $ac_cv_header_stdc = yes; then - # SunOS 4.x string.h does not declare mem*, contrary to ANSI. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "memchr" >/dev/null 2>&1; then : - -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "free" >/dev/null 2>&1; then : - -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. - if test "$cross_compiling" = yes; then : - : -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#if ((' ' & 0x0FF) == 0x020) -# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') -# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) -#else -# define ISLOWER(c) \ - (('a' <= (c) && (c) <= 'i') \ - || ('j' <= (c) && (c) <= 'r') \ - || ('s' <= (c) && (c) <= 'z')) -# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) -#endif - -#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) -int -main () -{ - int i; - for (i = 0; i < 256; i++) - if (XOR (islower (i), ISLOWER (i)) - || toupper (i) != TOUPPER (i)) - return 2; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - -else - ac_cv_header_stdc=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 -$as_echo "$ac_cv_header_stdc" >&6; } -if test $ac_cv_header_stdc = yes; then - -$as_echo "#define STDC_HEADERS 1" >>confdefs.h - -fi - - -#------------------------------------------------------------------------- -# Register RISCV environment variable -#------------------------------------------------------------------------- - - - -#------------------------------------------------------------------------- -# Set compiler flags -#------------------------------------------------------------------------- - -default_CFLAGS="-Wall -Werror -D__NO_INLINE__ -mcmodel=medany -O2 -std=gnu99 -Wno-unused -Wno-attributes -fno-delete-null-pointer-checks" - -# Check whether --enable-32bit was given. -if test "${enable_32bit+set}" = set; then : - enableval=$enable_32bit; BUILD_32BIT=$enableval -else - BUILD_32BIT=no -fi - - -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 - -# Check whether --enable-print-device-tree was given. -if test "${enable_print_device_tree+set}" = set; then : - enableval=$enable_print_device_tree; -fi - -if test "x$enable_print_device_tree" == "xyes"; then : - - -$as_echo "#define PK_PRINT_DEVICE_TREE /**/" >>confdefs.h - - -fi - - - -LIBS="-lgcc" - - - -#------------------------------------------------------------------------- -# 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. - - - - # Add command line argument to enable all optional subprojects - - # Check whether --enable-optional-subprojects was given. -if test "${enable_optional_subprojects+set}" = set; then : - enableval=$enable_optional_subprojects; -fi - - - # Loop through the subprojects given in the macro argument - - - - # Determine if this is a required or an optional subproject - - - - # Determine if there is a group with the same name - - - - # Create variations of the subproject name suitable for use as a CPP - # enabled define, a shell enabled variable, and a shell function - - - - - - - - - - - - # Add subproject to our running list - -# subprojects="$subprojects pk" - - # 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'. - - -# { $as_echo "$as_me:${as_lineno-$LINENO}: configuring default subproject : pk" >&5 -#$as_echo "$as_me: configuring default subproject : pk" >&6;} -# ac_config_files="$ac_config_files pk.mk:pk/pk.mk.in" - -# enable_pk_sproj="yes" -# subprojects_enabled="$subprojects_enabled pk" - -$as_echo "#define PK_ENABLED /**/" >>confdefs.h - - # Check whether --enable-vm was given. -if test "${enable_vm+set}" = set; then : - enableval=$enable_vm; -fi - -if test "x$enable_vm" != "xno"; then : - - -$as_echo "#define PK_ENABLE_VM /**/" >>confdefs.h - - -fi - - - - - - # Determine if this is a required or an optional subproject - - - - # Determine if there is a group with the same name - - - - # Create variations of the subproject name suitable for use as a CPP - # enabled define, a shell enabled variable, and a shell function - - - - - - - - - - - - # Add subproject to our running list - - subprojects="$subprojects bbl" - - # 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'. - - - { $as_echo "$as_me:${as_lineno-$LINENO}: configuring default subproject : bbl" >&5 -$as_echo "$as_me: configuring default subproject : bbl" >&6;} - ac_config_files="$ac_config_files bbl.mk:bbl/bbl.mk.in" - - enable_bbl_sproj="yes" - subprojects_enabled="$subprojects_enabled bbl" - -$as_echo "#define BBL_ENABLED /**/" >>confdefs.h - - # Check whether --enable-logo was given. -if test "${enable_logo+set}" = set; then : - enableval=$enable_logo; -fi - -if test "x$enable_logo" == "xyes"; then : - - -$as_echo "#define PK_ENABLE_LOGO /**/" >>confdefs.h - - -fi - - -# Check whether --with-payload was given. -if test "${with_payload+set}" = set; then : - withval=$with_payload; BBL_PAYLOAD=$with_payload - -else - BBL_PAYLOAD=dummy_payload - -fi - - - -# Check whether --with-logo was given. -if test "${with_logo+set}" = set; then : - withval=$with_logo; BBL_LOGO_FILE=$with_logo - -else - BBL_LOGO_FILE=riscv_logo.txt - -fi - - - - - - - # Determine if this is a required or an optional subproject - - - - # Determine if there is a group with the same name - - - - # Create variations of the subproject name suitable for use as a CPP - # enabled define, a shell enabled variable, and a shell function - - - - - - - - - - - - # Add subproject to our running list - -# subprojects="$subprojects softfloat" - - # 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'. - - -# { $as_echo "$as_me:${as_lineno-$LINENO}: configuring default subproject : softfloat" >&5 -#$as_echo "$as_me: configuring default subproject : softfloat" >&6;} -# ac_config_files="$ac_config_files softfloat.mk:softfloat/softfloat.mk.in" - -# enable_softfloat_sproj="yes" -# subprojects_enabled="$subprojects_enabled softfloat" - -#$as_echo "#define SOFTFLOAT_ENABLED /**/" >>confdefs.h - - - - - - - # Determine if this is a required or an optional subproject - - - - # Determine if there is a group with the same name - - - - # Create variations of the subproject name suitable for use as a CPP - # enabled define, a shell enabled variable, and a shell function - - - - - - - - - - - - # Add subproject to our running list - - subprojects="$subprojects dummy_payload" - - # 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'. - - - { $as_echo "$as_me:${as_lineno-$LINENO}: configuring default subproject : dummy_payload" >&5 -$as_echo "$as_me: configuring default subproject : dummy_payload" >&6;} - ac_config_files="$ac_config_files dummy_payload.mk:dummy_payload/dummy_payload.mk.in" - - enable_dummy_payload_sproj="yes" - subprojects_enabled="$subprojects_enabled dummy_payload" - -$as_echo "#define DUMMY_PAYLOAD_ENABLED /**/" >>confdefs.h - - - - - - - # Determine if this is a required or an optional subproject - - - - # Determine if there is a group with the same name - - - - # Create variations of the subproject name suitable for use as a CPP - # enabled define, a shell enabled variable, and a shell function - - - - - - - - - - - - # Add subproject to our running list - - subprojects="$subprojects machine" - - # 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'. - - - { $as_echo "$as_me:${as_lineno-$LINENO}: configuring default subproject : machine" >&5 -$as_echo "$as_me: configuring default subproject : machine" >&6;} - ac_config_files="$ac_config_files machine.mk:machine/machine.mk.in" - - enable_machine_sproj="yes" - subprojects_enabled="$subprojects_enabled machine" - -$as_echo "#define MACHINE_ENABLED /**/" >>confdefs.h - - # Check whether --enable-fp-emulation was given. -if test "${enable_fp_emulation+set}" = set; then : - enableval=$enable_fp_emulation; -fi - -if test "x$enable_fp_emulation" != "xno"; then : - - -$as_echo "#define PK_ENABLE_FP_EMULATION /**/" >>confdefs.h - - -fi - - - - - - # Determine if this is a required or an optional subproject - - - - # Determine if there is a group with the same name - - - - # Create variations of the subproject name suitable for use as a CPP - # enabled define, a shell enabled variable, and a shell function - - - - - - - - - - - - # Add subproject to our running list - - subprojects="$subprojects util" - - # 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'. - - - { $as_echo "$as_me:${as_lineno-$LINENO}: configuring default subproject : util" >&5 -$as_echo "$as_me: configuring default subproject : util" >&6;} - ac_config_files="$ac_config_files util.mk:util/util.mk.in" - - enable_util_sproj="yes" - subprojects_enabled="$subprojects_enabled util" - -$as_echo "#define UTIL_ENABLED /**/" >>confdefs.h - - - - - - - # Output make variables - - - - - - -#------------------------------------------------------------------------- -# 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="$ac_config_headers config.h" - -ac_config_files="$ac_config_files Makefile" - -cat >confcache <<\_ACEOF -# This file is a shell script that caches the results of configure -# tests run on this system so they can be shared between configure -# scripts and configure runs, see configure's option --config-cache. -# It is not useful on other systems. If it contains results you don't -# want to keep, you may remove or edit it. -# -# config.status only pays attention to the cache file if you give it -# the --recheck option to rerun configure. -# -# `ac_cv_env_foo' variables (set or unset) will be overridden when -# loading this file, other *unset* `ac_cv_foo' will be assigned the -# following values. - -_ACEOF - -# The following way of writing the cache mishandles newlines in values, -# but we know of no workaround that is simple, portable, and efficient. -# So, we kill variables containing newlines. -# Ultrix sh set writes to stderr and can't be redirected directly, -# and sets the high bit in the cache file unless we assign to the vars. -( - for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - - (set) 2>&1 | - case $as_nl`(ac_space=' '; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - # `set' does not quote correctly, so add quotes: double-quote - # substitution turns \\\\ into \\, and sed turns \\ into \. - sed -n \ - "s/'/'\\\\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" - ;; #( - *) - # `set' quotes correctly as required by POSIX, so do not add quotes. - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) | - sed ' - /^ac_cv_env_/b end - t clear - :clear - s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ - t end - s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ - :end' >>confcache -if diff "$cache_file" confcache >/dev/null 2>&1; then :; else - if test -w "$cache_file"; then - if test "x$cache_file" != "x/dev/null"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 -$as_echo "$as_me: updating cache $cache_file" >&6;} - if test ! -f "$cache_file" || test -h "$cache_file"; then - cat confcache >"$cache_file" - else - case $cache_file in #( - */* | ?:*) - mv -f confcache "$cache_file"$$ && - mv -f "$cache_file"$$ "$cache_file" ;; #( - *) - mv -f confcache "$cache_file" ;; - esac - fi - fi - else - { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 -$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} - fi -fi -rm -f confcache - -test "x$prefix" = xNONE && prefix=$ac_default_prefix -# Let make expand exec_prefix. -test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' - -DEFS=-DHAVE_CONFIG_H - -ac_libobjs= -ac_ltlibobjs= -U= -for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue - # 1. Remove the extension, and $U if already installed. - ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' - ac_i=`$as_echo "$ac_i" | sed "$ac_script"` - # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR - # will be set to the directory where LIBOBJS objects are built. - as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" - as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' -done -LIBOBJS=$ac_libobjs - -LTLIBOBJS=$ac_ltlibobjs - - - -: "${CONFIG_STATUS=./config.status}" -ac_write_fail=0 -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files $CONFIG_STATUS" -{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 -$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} -as_write_fail=0 -cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 -#! $SHELL -# Generated by $as_me. -# Run this file to recreate the current configuration. -# Compiler output produced by configure, useful for debugging -# configure, is in config.log if it exists. - -debug=false -ac_cs_recheck=false -ac_cs_silent=false - -SHELL=\${CONFIG_SHELL-$SHELL} -export SHELL -_ASEOF -cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - -as_nl=' -' -export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -case $0 in #(( - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break - done -IFS=$as_save_IFS - - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - $as_echo "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit - -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -pR' - fi -else - as_ln_s='cp -pR' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -as_test_x='test -x' -as_executable_p=as_fn_executable_p - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -exec 6>&1 -## ----------------------------------- ## -## Main body of $CONFIG_STATUS script. ## -## ----------------------------------- ## -_ASEOF -test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# Save the log message, to keep $0 and so on meaningful, and to -# report actual input values of CONFIG_FILES etc. instead of their -# values after options handling. -ac_log=" -This file was extended by RISC-V Proxy Kernel $as_me ?, which was -generated by GNU Autoconf 2.69. Invocation command line was - - CONFIG_FILES = $CONFIG_FILES - CONFIG_HEADERS = $CONFIG_HEADERS - CONFIG_LINKS = $CONFIG_LINKS - CONFIG_COMMANDS = $CONFIG_COMMANDS - $ $0 $@ - -on `(hostname || uname -n) 2>/dev/null | sed 1q` -" - -_ACEOF - -case $ac_config_files in *" -"*) set x $ac_config_files; shift; ac_config_files=$*;; -esac - -case $ac_config_headers in *" -"*) set x $ac_config_headers; shift; ac_config_headers=$*;; -esac - - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -# Files that config.status was made for. -config_files="$ac_config_files" -config_headers="$ac_config_headers" - -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -ac_cs_usage="\ -\`$as_me' instantiates files and other configuration actions -from templates according to the current configuration. Unless the files -and actions are specified as TAGs, all are instantiated by default. - -Usage: $0 [OPTION]... [TAG]... - - -h, --help print this help, then exit - -V, --version print version number and configuration settings, then exit - --config print configuration, then exit - -q, --quiet, --silent - do not print progress messages - -d, --debug don't remove temporary files - --recheck update $as_me by reconfiguring in the same conditions - --file=FILE[:TEMPLATE] - instantiate the configuration file FILE - --header=FILE[:TEMPLATE] - instantiate the configuration header FILE - -Configuration files: -$config_files - -Configuration headers: -$config_headers - -Report bugs to ." - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" -ac_cs_version="\\ -RISC-V Proxy Kernel config.status ? -configured by $0, generated by GNU Autoconf 2.69, - with options \\"\$ac_cs_config\\" - -Copyright (C) 2012 Free Software Foundation, Inc. -This config.status script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it." - -ac_pwd='$ac_pwd' -srcdir='$srcdir' -INSTALL='$INSTALL' -test -n "\$AWK" || AWK=awk -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# The default lists apply if the user does not specify any file. -ac_need_defaults=: -while test $# != 0 -do - case $1 in - --*=?*) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` - ac_shift=: - ;; - --*=) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg= - ac_shift=: - ;; - *) - ac_option=$1 - ac_optarg=$2 - ac_shift=shift - ;; - esac - - case $ac_option in - # Handling of the options. - -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) - ac_cs_recheck=: ;; - --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) - $as_echo "$ac_cs_version"; exit ;; - --config | --confi | --conf | --con | --co | --c ) - $as_echo "$ac_cs_config"; exit ;; - --debug | --debu | --deb | --de | --d | -d ) - debug=: ;; - --file | --fil | --fi | --f ) - $ac_shift - case $ac_optarg in - *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - '') as_fn_error $? "missing file argument" ;; - esac - as_fn_append CONFIG_FILES " '$ac_optarg'" - ac_need_defaults=false;; - --header | --heade | --head | --hea ) - $ac_shift - case $ac_optarg in - *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - as_fn_append CONFIG_HEADERS " '$ac_optarg'" - ac_need_defaults=false;; - --he | --h) - # Conflict between --help and --header - as_fn_error $? "ambiguous option: \`$1' -Try \`$0 --help' for more information.";; - --help | --hel | -h ) - $as_echo "$ac_cs_usage"; exit ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil | --si | --s) - ac_cs_silent=: ;; - - # This is an error. - -*) as_fn_error $? "unrecognized option: \`$1' -Try \`$0 --help' for more information." ;; - - *) as_fn_append ac_config_targets " $1" - ac_need_defaults=false ;; - - esac - shift -done - -ac_configure_extra_args= - -if $ac_cs_silent; then - exec 6>/dev/null - ac_configure_extra_args="$ac_configure_extra_args --silent" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -if \$ac_cs_recheck; then - set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion - shift - \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 - CONFIG_SHELL='$SHELL' - export CONFIG_SHELL - exec "\$@" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -exec 5>>config.log -{ - echo - sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX -## Running $as_me. ## -_ASBOX - $as_echo "$ac_log" -} >&5 - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - -# Handling of arguments. -for ac_config_target in $ac_config_targets -do - case $ac_config_target in - "pk.mk") CONFIG_FILES="$CONFIG_FILES pk.mk:pk/pk.mk.in" ;; - "bbl.mk") CONFIG_FILES="$CONFIG_FILES bbl.mk:bbl/bbl.mk.in" ;; - "softfloat.mk") CONFIG_FILES="$CONFIG_FILES softfloat.mk:softfloat/softfloat.mk.in" ;; - "dummy_payload.mk") CONFIG_FILES="$CONFIG_FILES dummy_payload.mk:dummy_payload/dummy_payload.mk.in" ;; - "machine.mk") CONFIG_FILES="$CONFIG_FILES machine.mk:machine/machine.mk.in" ;; - "util.mk") CONFIG_FILES="$CONFIG_FILES util.mk:util/util.mk.in" ;; - "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; - "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; - - *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; - esac -done - - -# If the user did not use the arguments to specify the items to instantiate, -# then the envvar interface is used. Set only those that are not. -# We use the long form for the default assignment because of an extremely -# bizarre bug on SunOS 4.1.3. -if $ac_need_defaults; then - test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files - test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers -fi - -# Have a temporary directory for convenience. Make it in the build tree -# simply because there is no reason against having it here, and in addition, -# creating and moving files from /tmp can sometimes cause problems. -# Hook for its removal unless debugging. -# Note that there is a small window in which the directory will not be cleaned: -# after its creation but before its name has been assigned to `$tmp'. -$debug || -{ - tmp= ac_tmp= - trap 'exit_status=$? - : "${ac_tmp:=$tmp}" - { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status -' 0 - trap 'as_fn_exit 1' 1 2 13 15 -} -# Create a (secure) tmp directory for tmp files. - -{ - tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && - test -d "$tmp" -} || -{ - tmp=./conf$$-$RANDOM - (umask 077 && mkdir "$tmp") -} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 -ac_tmp=$tmp - -# Set up the scripts for CONFIG_FILES section. -# No need to generate them if there are no CONFIG_FILES. -# This happens for instance with `./config.status config.h'. -if test -n "$CONFIG_FILES"; then - - -ac_cr=`echo X | tr X '\015'` -# On cygwin, bash can eat \r inside `` if the user requested igncr. -# But we know of no other shell where ac_cr would be empty at this -# point, so we can use a bashism as a fallback. -if test "x$ac_cr" = x; then - eval ac_cr=\$\'\\r\' -fi -ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` -if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then - ac_cs_awk_cr='\\r' -else - ac_cs_awk_cr=$ac_cr -fi - -echo 'BEGIN {' >"$ac_tmp/subs1.awk" && -_ACEOF - - -{ - echo "cat >conf$$subs.awk <<_ACEOF" && - echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && - echo "_ACEOF" -} >conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 -ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` -ac_delim='%!_!# ' -for ac_last_try in false false false false false :; do - . ./conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - - ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` - if test $ac_delim_n = $ac_delim_num; then - break - elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done -rm -f conf$$subs.sh - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && -_ACEOF -sed -n ' -h -s/^/S["/; s/!.*/"]=/ -p -g -s/^[^!]*!// -:repl -t repl -s/'"$ac_delim"'$// -t delim -:nl -h -s/\(.\{148\}\)..*/\1/ -t more1 -s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ -p -n -b repl -:more1 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t nl -:delim -h -s/\(.\{148\}\)..*/\1/ -t more2 -s/["\\]/\\&/g; s/^/"/; s/$/"/ -p -b -:more2 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t delim -' >$CONFIG_STATUS || ac_write_fail=1 -rm -f conf$$subs.awk -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -_ACAWK -cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && - for (key in S) S_is_set[key] = 1 - FS = "" - -} -{ - line = $ 0 - nfields = split(line, field, "@") - substed = 0 - len = length(field[1]) - for (i = 2; i < nfields; i++) { - key = field[i] - keylen = length(key) - if (S_is_set[key]) { - value = S[key] - line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) - len += length(value) + length(field[++i]) - substed = 1 - } else - len += 1 + keylen - } - - print line -} - -_ACAWK -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then - sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" -else - cat -fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ - || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 -_ACEOF - -# VPATH may cause trouble with some makes, so we remove sole $(srcdir), -# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and -# trailing colons and then remove the whole line if VPATH becomes empty -# (actually we leave an empty line to preserve line numbers). -if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ -h -s/// -s/^/:/ -s/[ ]*$/:/ -s/:\$(srcdir):/:/g -s/:\${srcdir}:/:/g -s/:@srcdir@:/:/g -s/^:*// -s/:*$// -x -s/\(=[ ]*\).*/\1/ -G -s/\n// -s/^[^=]*=[ ]*$// -}' -fi - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -fi # test -n "$CONFIG_FILES" - -# Set up the scripts for CONFIG_HEADERS section. -# No need to generate them if there are no CONFIG_HEADERS. -# This happens for instance with `./config.status Makefile'. -if test -n "$CONFIG_HEADERS"; then -cat >"$ac_tmp/defines.awk" <<\_ACAWK || -BEGIN { -_ACEOF - -# Transform confdefs.h into an awk script `defines.awk', embedded as -# here-document in config.status, that substitutes the proper values into -# config.h.in to produce config.h. - -# Create a delimiter string that does not exist in confdefs.h, to ease -# handling of long lines. -ac_delim='%!_!# ' -for ac_last_try in false false :; do - ac_tt=`sed -n "/$ac_delim/p" confdefs.h` - if test -z "$ac_tt"; then - break - elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done - -# For the awk script, D is an array of macro values keyed by name, -# likewise P contains macro parameters if any. Preserve backslash -# newline sequences. - -ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* -sed -n ' -s/.\{148\}/&'"$ac_delim"'/g -t rset -:rset -s/^[ ]*#[ ]*define[ ][ ]*/ / -t def -d -:def -s/\\$// -t bsnl -s/["\\]/\\&/g -s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ -D["\1"]=" \3"/p -s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p -d -:bsnl -s/["\\]/\\&/g -s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ -D["\1"]=" \3\\\\\\n"\\/p -t cont -s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p -t cont -d -:cont -n -s/.\{148\}/&'"$ac_delim"'/g -t clear -:clear -s/\\$// -t bsnlc -s/["\\]/\\&/g; s/^/"/; s/$/"/p -d -:bsnlc -s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p -b cont -' >$CONFIG_STATUS || ac_write_fail=1 - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 - for (key in D) D_is_set[key] = 1 - FS = "" -} -/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { - line = \$ 0 - split(line, arg, " ") - if (arg[1] == "#") { - defundef = arg[2] - mac1 = arg[3] - } else { - defundef = substr(arg[1], 2) - mac1 = arg[2] - } - split(mac1, mac2, "(") #) - macro = mac2[1] - prefix = substr(line, 1, index(line, defundef) - 1) - if (D_is_set[macro]) { - # Preserve the white space surrounding the "#". - print prefix "define", macro P[macro] D[macro] - next - } else { - # Replace #undef with comments. This is necessary, for example, - # in the case of _POSIX_SOURCE, which is predefined and required - # on some systems where configure will not decide to define it. - if (defundef == "undef") { - print "/*", prefix defundef, macro, "*/" - next - } - } -} -{ print } -_ACAWK -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 -fi # test -n "$CONFIG_HEADERS" - - -eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " -shift -for ac_tag -do - case $ac_tag in - :[FHLC]) ac_mode=$ac_tag; continue;; - esac - case $ac_mode$ac_tag in - :[FHL]*:*);; - :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; - :[FH]-) ac_tag=-:-;; - :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; - esac - ac_save_IFS=$IFS - IFS=: - set x $ac_tag - IFS=$ac_save_IFS - shift - ac_file=$1 - shift - - case $ac_mode in - :L) ac_source=$1;; - :[FH]) - ac_file_inputs= - for ac_f - do - case $ac_f in - -) ac_f="$ac_tmp/stdin";; - *) # Look for the file first in the build tree, then in the source tree - # (if the path is not absolute). The absolute path cannot be DOS-style, - # because $ac_f cannot contain `:'. - test -f "$ac_f" || - case $ac_f in - [\\/$]*) false;; - *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; - esac || - as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; - esac - case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac - as_fn_append ac_file_inputs " '$ac_f'" - done - - # Let's still pretend it is `configure' which instantiates (i.e., don't - # use $as_me), people would be surprised to read: - # /* config.h. Generated by config.status. */ - configure_input='Generated from '` - $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' - `' by configure.' - if test x"$ac_file" != x-; then - configure_input="$ac_file. $configure_input" - { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 -$as_echo "$as_me: creating $ac_file" >&6;} - fi - # Neutralize special characters interpreted by sed in replacement strings. - case $configure_input in #( - *\&* | *\|* | *\\* ) - ac_sed_conf_input=`$as_echo "$configure_input" | - sed 's/[\\\\&|]/\\\\&/g'`;; #( - *) ac_sed_conf_input=$configure_input;; - esac - - case $ac_tag in - *:-:* | *:-) cat >"$ac_tmp/stdin" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; - esac - ;; - esac - - ac_dir=`$as_dirname -- "$ac_file" || -$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$ac_file" : 'X\(//\)[^/]' \| \ - X"$ac_file" : 'X\(//\)$' \| \ - X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$ac_file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - as_dir="$ac_dir"; as_fn_mkdir_p - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - - case $ac_mode in - :F) - # - # CONFIG_FILE - # - - case $INSTALL in - [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; - *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; - esac -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# If the template does not know about datarootdir, expand it. -# FIXME: This hack should be removed a few years after 2.60. -ac_datarootdir_hack=; ac_datarootdir_seen= -ac_sed_dataroot=' -/datarootdir/ { - p - q -} -/@datadir@/p -/@docdir@/p -/@infodir@/p -/@localedir@/p -/@mandir@/p' -case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in -*datarootdir*) ac_datarootdir_seen=yes;; -*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 -$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 - ac_datarootdir_hack=' - s&@datadir@&$datadir&g - s&@docdir@&$docdir&g - s&@infodir@&$infodir&g - s&@localedir@&$localedir&g - s&@mandir@&$mandir&g - s&\\\${datarootdir}&$datarootdir&g' ;; -esac -_ACEOF - -# Neutralize VPATH when `$srcdir' = `.'. -# Shell code in configure.ac might set extrasub. -# FIXME: do we really want to maintain this feature? -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_sed_extra="$ac_vpsub -$extrasub -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -:t -/@[a-zA-Z_][a-zA-Z_0-9]*@/!b -s|@configure_input@|$ac_sed_conf_input|;t t -s&@top_builddir@&$ac_top_builddir_sub&;t t -s&@top_build_prefix@&$ac_top_build_prefix&;t t -s&@srcdir@&$ac_srcdir&;t t -s&@abs_srcdir@&$ac_abs_srcdir&;t t -s&@top_srcdir@&$ac_top_srcdir&;t t -s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t -s&@builddir@&$ac_builddir&;t t -s&@abs_builddir@&$ac_abs_builddir&;t t -s&@abs_top_builddir@&$ac_abs_top_builddir&;t t -s&@INSTALL@&$ac_INSTALL&;t t -$ac_datarootdir_hack -" -eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ - >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - -test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && - { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && - { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ - "$ac_tmp/out"`; test -z "$ac_out"; } && - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&5 -$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&2;} - - rm -f "$ac_tmp/stdin" - case $ac_file in - -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; - *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; - esac \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - ;; - :H) - # - # CONFIG_HEADER - # - if test x"$ac_file" != x-; then - { - $as_echo "/* $configure_input */" \ - && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" - } >"$ac_tmp/config.h" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then - { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 -$as_echo "$as_me: $ac_file is unchanged" >&6;} - else - rm -f "$ac_file" - mv "$ac_tmp/config.h" "$ac_file" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - fi - else - $as_echo "/* $configure_input */" \ - && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ - || as_fn_error $? "could not create -" "$LINENO" 5 - fi - ;; - - - esac - -done # for ac_tag - - -as_fn_exit 0 -_ACEOF -ac_clean_files=$ac_clean_files_save - -test $ac_write_fail = 0 || - as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 - - -# configure is writing to config.log, and then calls config.status. -# config.status does its own redirection, appending to config.log. -# Unfortunately, on DOS this fails, as config.log is still kept open -# by configure, so config.status won't be able to write to it; its -# output is simply discarded. So we exec the FD to /dev/null, -# effectively closing config.log, so it can be properly (re)opened and -# appended to by config.status. When coming back to configure, we -# need to make the FD available again. -if test "$no_create" != yes; then - ac_cs_success=: - ac_config_status_args= - test "$silent" = yes && - ac_config_status_args="$ac_config_status_args --quiet" - exec 5>/dev/null - $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false - exec 5>>config.log - # Use ||, not &&, to avoid exiting from the if with $? = 1, which - # would make configure fail if this is the last instruction. - $ac_cs_success || as_fn_exit 1 -fi -if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 -$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} -fi - diff --git a/riscv-pk/configure.ac b/riscv-pk/configure.ac deleted file mode 100644 index 20cd6d1..0000000 --- a/riscv-pk/configure.ac +++ /dev/null @@ -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 diff --git a/riscv-pk/dummy_payload/dummy_entry.S b/riscv-pk/dummy_payload/dummy_entry.S deleted file mode 100644 index 92d4652..0000000 --- a/riscv-pk/dummy_payload/dummy_entry.S +++ /dev/null @@ -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" diff --git a/riscv-pk/dummy_payload/dummy_payload.ac b/riscv-pk/dummy_payload/dummy_payload.ac deleted file mode 100644 index e69de29..0000000 diff --git a/riscv-pk/dummy_payload/dummy_payload.c b/riscv-pk/dummy_payload/dummy_payload.c deleted file mode 100644 index e69de29..0000000 diff --git a/riscv-pk/dummy_payload/dummy_payload.lds b/riscv-pk/dummy_payload/dummy_payload.lds deleted file mode 100644 index 0506a8d..0000000 --- a/riscv-pk/dummy_payload/dummy_payload.lds +++ /dev/null @@ -1,7 +0,0 @@ -ENTRY(_start) - -SECTIONS { - . = -0x80000000; - - .text.init : { *(.text.init) } -} diff --git a/riscv-pk/dummy_payload/dummy_payload.mk.in b/riscv-pk/dummy_payload/dummy_payload.mk.in deleted file mode 100644 index 4a133be..0000000 --- a/riscv-pk/dummy_payload/dummy_payload.mk.in +++ /dev/null @@ -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 \ diff --git a/riscv-pk/machine/atomic.h b/riscv-pk/machine/atomic.h deleted file mode 100644 index fe81566..0000000 --- a/riscv-pk/machine/atomic.h +++ /dev/null @@ -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 diff --git a/riscv-pk/machine/bits.h b/riscv-pk/machine/bits.h deleted file mode 100644 index e578903..0000000 --- a/riscv-pk/machine/bits.h +++ /dev/null @@ -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 diff --git a/riscv-pk/machine/disabled_hart_mask.h b/riscv-pk/machine/disabled_hart_mask.h deleted file mode 100644 index 2a3a73c..0000000 --- a/riscv-pk/machine/disabled_hart_mask.h +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef DISABLED_HART_MASK_H -#define DISABLED_HART_MASK_H -extern long disabled_hart_mask; -#endif diff --git a/riscv-pk/machine/emulation.c b/riscv-pk/machine/emulation.c deleted file mode 100644 index eb535d8..0000000 --- a/riscv-pk/machine/emulation.c +++ /dev/null @@ -1,234 +0,0 @@ -#include "emulation.h" -//#include "fp_emulation.h" -#include "config.h" -#include "unprivileged_memory.h" -#include "mtrap.h" -#include - -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); -} diff --git a/riscv-pk/machine/emulation.h b/riscv-pk/machine/emulation.h deleted file mode 100644 index f75173d..0000000 --- a/riscv-pk/machine/emulation.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef _RISCV_EMULATION_H -#define _RISCV_EMULATION_H - -#include "encoding.h" -#include "bits.h" -#include - -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 diff --git a/riscv-pk/machine/encoding.h b/riscv-pk/machine/encoding.h deleted file mode 100644 index c109ce1..0000000 --- a/riscv-pk/machine/encoding.h +++ /dev/null @@ -1,1471 +0,0 @@ -// See LICENSE for license details. - -#ifndef RISCV_CSR_ENCODING_H -#define RISCV_CSR_ENCODING_H - -#define MSTATUS_UIE 0x00000001 -#define MSTATUS_SIE 0x00000002 -#define MSTATUS_HIE 0x00000004 -#define MSTATUS_MIE 0x00000008 -#define MSTATUS_UPIE 0x00000010 -#define MSTATUS_SPIE 0x00000020 -#define MSTATUS_HPIE 0x00000040 -#define MSTATUS_MPIE 0x00000080 -#define MSTATUS_SPP 0x00000100 -#define MSTATUS_HPP 0x00000600 -#define MSTATUS_MPP 0x00001800 -#define MSTATUS_FS 0x00006000 -#define MSTATUS_XS 0x00018000 -#define MSTATUS_MPRV 0x00020000 -#define MSTATUS_SUM 0x00040000 -#define MSTATUS_MXR 0x00080000 -#define MSTATUS_TVM 0x00100000 -#define MSTATUS_TW 0x00200000 -#define MSTATUS_TSR 0x00400000 -#define MSTATUS32_SD 0x80000000 -#define MSTATUS_UXL 0x0000000300000000 -#define MSTATUS_SXL 0x0000000C00000000 -#define MSTATUS64_SD 0x8000000000000000 - -#define SSTATUS_UIE 0x00000001 -#define SSTATUS_SIE 0x00000002 -#define SSTATUS_UPIE 0x00000010 -#define SSTATUS_SPIE 0x00000020 -#define SSTATUS_SPP 0x00000100 -#define SSTATUS_FS 0x00006000 -#define SSTATUS_XS 0x00018000 -#define SSTATUS_SUM 0x00040000 -#define SSTATUS_MXR 0x00080000 -#define SSTATUS32_SD 0x80000000 -#define SSTATUS_UXL 0x0000000300000000 -#define SSTATUS64_SD 0x8000000000000000 - -#define DCSR_XDEBUGVER (3U<<30) -#define DCSR_NDRESET (1<<29) -#define DCSR_FULLRESET (1<<28) -#define DCSR_EBREAKM (1<<15) -#define DCSR_EBREAKH (1<<14) -#define DCSR_EBREAKS (1<<13) -#define DCSR_EBREAKU (1<<12) -#define DCSR_STOPCYCLE (1<<10) -#define DCSR_STOPTIME (1<<9) -#define DCSR_CAUSE (7<<6) -#define DCSR_DEBUGINT (1<<5) -#define DCSR_HALT (1<<3) -#define DCSR_STEP (1<<2) -#define DCSR_PRV (3<<0) - -#define DCSR_CAUSE_NONE 0 -#define DCSR_CAUSE_SWBP 1 -#define DCSR_CAUSE_HWBP 2 -#define DCSR_CAUSE_DEBUGINT 3 -#define DCSR_CAUSE_STEP 4 -#define DCSR_CAUSE_HALT 5 - -#define MCONTROL_TYPE(xlen) (0xfULL<<((xlen)-4)) -#define MCONTROL_DMODE(xlen) (1ULL<<((xlen)-5)) -#define MCONTROL_MASKMAX(xlen) (0x3fULL<<((xlen)-11)) - -#define MCONTROL_SELECT (1<<19) -#define MCONTROL_TIMING (1<<18) -#define MCONTROL_ACTION (0x3f<<12) -#define MCONTROL_CHAIN (1<<11) -#define MCONTROL_MATCH (0xf<<7) -#define MCONTROL_M (1<<6) -#define MCONTROL_H (1<<5) -#define MCONTROL_S (1<<4) -#define MCONTROL_U (1<<3) -#define MCONTROL_EXECUTE (1<<2) -#define MCONTROL_STORE (1<<1) -#define MCONTROL_LOAD (1<<0) - -#define MCONTROL_TYPE_NONE 0 -#define MCONTROL_TYPE_MATCH 2 - -#define MCONTROL_ACTION_DEBUG_EXCEPTION 0 -#define MCONTROL_ACTION_DEBUG_MODE 1 -#define MCONTROL_ACTION_TRACE_START 2 -#define MCONTROL_ACTION_TRACE_STOP 3 -#define MCONTROL_ACTION_TRACE_EMIT 4 - -#define MCONTROL_MATCH_EQUAL 0 -#define MCONTROL_MATCH_NAPOT 1 -#define MCONTROL_MATCH_GE 2 -#define MCONTROL_MATCH_LT 3 -#define MCONTROL_MATCH_MASK_LOW 4 -#define MCONTROL_MATCH_MASK_HIGH 5 - -#define MIP_SSIP (1 << IRQ_S_SOFT) -#define MIP_HSIP (1 << IRQ_H_SOFT) -#define MIP_MSIP (1 << IRQ_M_SOFT) -#define MIP_STIP (1 << IRQ_S_TIMER) -#define MIP_HTIP (1 << IRQ_H_TIMER) -#define MIP_MTIP (1 << IRQ_M_TIMER) -#define MIP_SEIP (1 << IRQ_S_EXT) -#define MIP_HEIP (1 << IRQ_H_EXT) -#define MIP_MEIP (1 << IRQ_M_EXT) - -#define SIP_SSIP MIP_SSIP -#define SIP_STIP MIP_STIP - -#define PRV_U 0 -#define PRV_S 1 -#define PRV_H 2 -#define PRV_M 3 - -#define SATP32_MODE 0x80000000 -#define SATP32_ASID 0x7FC00000 -#define SATP32_PPN 0x003FFFFF -#define SATP64_MODE 0xF000000000000000 -#define SATP64_ASID 0x0FFFF00000000000 -#define SATP64_PPN 0x00000FFFFFFFFFFF - -#define SATP_MODE_OFF 0 -#define SATP_MODE_SV32 1 -#define SATP_MODE_SV39 8 -#define SATP_MODE_SV48 9 -#define SATP_MODE_SV57 10 -#define SATP_MODE_SV64 11 - -#define PMP_R 0x01 -#define PMP_W 0x02 -#define PMP_X 0x04 -#define PMP_A 0x18 -#define PMP_L 0x80 -#define PMP_SHIFT 2 - -#define PMP_TOR 0x08 -#define PMP_NA4 0x10 -#define PMP_NAPOT 0x18 - -#define IRQ_S_SOFT 1 -#define IRQ_H_SOFT 2 -#define IRQ_M_SOFT 3 -#define IRQ_S_TIMER 5 -#define IRQ_H_TIMER 6 -#define IRQ_M_TIMER 7 -#define IRQ_S_EXT 9 -#define IRQ_H_EXT 10 -#define IRQ_M_EXT 11 -#define IRQ_COP 12 -#define IRQ_HOST 13 - -#define DEFAULT_RSTVEC 0x00001000 -#define CLINT_BASE 0x02000000 -#define CLINT_SIZE 0x000c0000 -#define EXT_IO_BASE 0x40000000 -#define DRAM_BASE 0x80000000 - -// page table entry (PTE) fields -#define PTE_V 0x001 // Valid -#define PTE_R 0x002 // Read -#define PTE_W 0x004 // Write -#define PTE_X 0x008 // Execute -#define PTE_U 0x010 // User -#define PTE_G 0x020 // Global -#define PTE_A 0x040 // Accessed -#define PTE_D 0x080 // Dirty -#define PTE_SOFT 0x300 // Reserved for Software - -#define PTE_PPN_SHIFT 10 - -#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V) - -#ifdef __riscv - -#if __riscv_xlen == 64 -# define MSTATUS_SD MSTATUS64_SD -# define SSTATUS_SD SSTATUS64_SD -# define RISCV_PGLEVEL_BITS 9 -# define SATP_MODE SATP64_MODE -#else -# define MSTATUS_SD MSTATUS32_SD -# define SSTATUS_SD SSTATUS32_SD -# define RISCV_PGLEVEL_BITS 10 -# define SATP_MODE SATP32_MODE -#endif -#define RISCV_PGSHIFT 12 -#define RISCV_PGSIZE (1 << RISCV_PGSHIFT) - -#ifndef __ASSEMBLER__ - -#ifdef __GNUC__ - -#define read_csr(reg) ({ unsigned long __tmp; \ - asm volatile ("csrr %0, " #reg : "=r"(__tmp)); \ - __tmp; }) - -#define write_csr(reg, val) ({ \ - asm volatile ("csrw " #reg ", %0" :: "rK"(val)); }) - -#define swap_csr(reg, val) ({ unsigned long __tmp; \ - asm volatile ("csrrw %0, " #reg ", %1" : "=r"(__tmp) : "rK"(val)); \ - __tmp; }) - -#define set_csr(reg, bit) ({ unsigned long __tmp; \ - asm volatile ("csrrs %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \ - __tmp; }) - -#define clear_csr(reg, bit) ({ unsigned long __tmp; \ - asm volatile ("csrrc %0, " #reg ", %1" : "=r"(__tmp) : "rK"(bit)); \ - __tmp; }) - -#define rdtime() read_csr(time) -#define rdcycle() read_csr(cycle) -#define rdinstret() read_csr(instret) - -#endif - -#endif - -#endif - -#endif -/* Automatically generated by parse-opcodes. */ -#ifndef RISCV_ENCODING_H -#define RISCV_ENCODING_H -#define MATCH_BEQ 0x63 -#define MASK_BEQ 0x707f -#define MATCH_BNE 0x1063 -#define MASK_BNE 0x707f -#define MATCH_BLT 0x4063 -#define MASK_BLT 0x707f -#define MATCH_BGE 0x5063 -#define MASK_BGE 0x707f -#define MATCH_BLTU 0x6063 -#define MASK_BLTU 0x707f -#define MATCH_BGEU 0x7063 -#define MASK_BGEU 0x707f -#define MATCH_JALR 0x67 -#define MASK_JALR 0x707f -#define MATCH_JAL 0x6f -#define MASK_JAL 0x7f -#define MATCH_LUI 0x37 -#define MASK_LUI 0x7f -#define MATCH_AUIPC 0x17 -#define MASK_AUIPC 0x7f -#define MATCH_ADDI 0x13 -#define MASK_ADDI 0x707f -#define MATCH_SLLI 0x1013 -#define MASK_SLLI 0xfc00707f -#define MATCH_SLTI 0x2013 -#define MASK_SLTI 0x707f -#define MATCH_SLTIU 0x3013 -#define MASK_SLTIU 0x707f -#define MATCH_XORI 0x4013 -#define MASK_XORI 0x707f -#define MATCH_SRLI 0x5013 -#define MASK_SRLI 0xfc00707f -#define MATCH_SRAI 0x40005013 -#define MASK_SRAI 0xfc00707f -#define MATCH_ORI 0x6013 -#define MASK_ORI 0x707f -#define MATCH_ANDI 0x7013 -#define MASK_ANDI 0x707f -#define MATCH_ADD 0x33 -#define MASK_ADD 0xfe00707f -#define MATCH_SUB 0x40000033 -#define MASK_SUB 0xfe00707f -#define MATCH_SLL 0x1033 -#define MASK_SLL 0xfe00707f -#define MATCH_SLT 0x2033 -#define MASK_SLT 0xfe00707f -#define MATCH_SLTU 0x3033 -#define MASK_SLTU 0xfe00707f -#define MATCH_XOR 0x4033 -#define MASK_XOR 0xfe00707f -#define MATCH_SRL 0x5033 -#define MASK_SRL 0xfe00707f -#define MATCH_SRA 0x40005033 -#define MASK_SRA 0xfe00707f -#define MATCH_OR 0x6033 -#define MASK_OR 0xfe00707f -#define MATCH_AND 0x7033 -#define MASK_AND 0xfe00707f -#define MATCH_ADDIW 0x1b -#define MASK_ADDIW 0x707f -#define MATCH_SLLIW 0x101b -#define MASK_SLLIW 0xfe00707f -#define MATCH_SRLIW 0x501b -#define MASK_SRLIW 0xfe00707f -#define MATCH_SRAIW 0x4000501b -#define MASK_SRAIW 0xfe00707f -#define MATCH_ADDW 0x3b -#define MASK_ADDW 0xfe00707f -#define MATCH_SUBW 0x4000003b -#define MASK_SUBW 0xfe00707f -#define MATCH_SLLW 0x103b -#define MASK_SLLW 0xfe00707f -#define MATCH_SRLW 0x503b -#define MASK_SRLW 0xfe00707f -#define MATCH_SRAW 0x4000503b -#define MASK_SRAW 0xfe00707f -#define MATCH_LB 0x3 -#define MASK_LB 0x707f -#define MATCH_LH 0x1003 -#define MASK_LH 0x707f -#define MATCH_LW 0x2003 -#define MASK_LW 0x707f -#define MATCH_LD 0x3003 -#define MASK_LD 0x707f -#define MATCH_LBU 0x4003 -#define MASK_LBU 0x707f -#define MATCH_LHU 0x5003 -#define MASK_LHU 0x707f -#define MATCH_LWU 0x6003 -#define MASK_LWU 0x707f -#define MATCH_SB 0x23 -#define MASK_SB 0x707f -#define MATCH_SH 0x1023 -#define MASK_SH 0x707f -#define MATCH_SW 0x2023 -#define MASK_SW 0x707f -#define MATCH_SD 0x3023 -#define MASK_SD 0x707f -#define MATCH_FENCE 0xf -#define MASK_FENCE 0x707f -#define MATCH_FENCE_I 0x100f -#define MASK_FENCE_I 0x707f -#define MATCH_MUL 0x2000033 -#define MASK_MUL 0xfe00707f -#define MATCH_MULH 0x2001033 -#define MASK_MULH 0xfe00707f -#define MATCH_MULHSU 0x2002033 -#define MASK_MULHSU 0xfe00707f -#define MATCH_MULHU 0x2003033 -#define MASK_MULHU 0xfe00707f -#define MATCH_DIV 0x2004033 -#define MASK_DIV 0xfe00707f -#define MATCH_DIVU 0x2005033 -#define MASK_DIVU 0xfe00707f -#define MATCH_REM 0x2006033 -#define MASK_REM 0xfe00707f -#define MATCH_REMU 0x2007033 -#define MASK_REMU 0xfe00707f -#define MATCH_MULW 0x200003b -#define MASK_MULW 0xfe00707f -#define MATCH_DIVW 0x200403b -#define MASK_DIVW 0xfe00707f -#define MATCH_DIVUW 0x200503b -#define MASK_DIVUW 0xfe00707f -#define MATCH_REMW 0x200603b -#define MASK_REMW 0xfe00707f -#define MATCH_REMUW 0x200703b -#define MASK_REMUW 0xfe00707f -#define MATCH_AMOADD_W 0x202f -#define MASK_AMOADD_W 0xf800707f -#define MATCH_AMOXOR_W 0x2000202f -#define MASK_AMOXOR_W 0xf800707f -#define MATCH_AMOOR_W 0x4000202f -#define MASK_AMOOR_W 0xf800707f -#define MATCH_AMOAND_W 0x6000202f -#define MASK_AMOAND_W 0xf800707f -#define MATCH_AMOMIN_W 0x8000202f -#define MASK_AMOMIN_W 0xf800707f -#define MATCH_AMOMAX_W 0xa000202f -#define MASK_AMOMAX_W 0xf800707f -#define MATCH_AMOMINU_W 0xc000202f -#define MASK_AMOMINU_W 0xf800707f -#define MATCH_AMOMAXU_W 0xe000202f -#define MASK_AMOMAXU_W 0xf800707f -#define MATCH_AMOSWAP_W 0x800202f -#define MASK_AMOSWAP_W 0xf800707f -#define MATCH_LR_W 0x1000202f -#define MASK_LR_W 0xf9f0707f -#define MATCH_SC_W 0x1800202f -#define MASK_SC_W 0xf800707f -#define MATCH_AMOADD_D 0x302f -#define MASK_AMOADD_D 0xf800707f -#define MATCH_AMOXOR_D 0x2000302f -#define MASK_AMOXOR_D 0xf800707f -#define MATCH_AMOOR_D 0x4000302f -#define MASK_AMOOR_D 0xf800707f -#define MATCH_AMOAND_D 0x6000302f -#define MASK_AMOAND_D 0xf800707f -#define MATCH_AMOMIN_D 0x8000302f -#define MASK_AMOMIN_D 0xf800707f -#define MATCH_AMOMAX_D 0xa000302f -#define MASK_AMOMAX_D 0xf800707f -#define MATCH_AMOMINU_D 0xc000302f -#define MASK_AMOMINU_D 0xf800707f -#define MATCH_AMOMAXU_D 0xe000302f -#define MASK_AMOMAXU_D 0xf800707f -#define MATCH_AMOSWAP_D 0x800302f -#define MASK_AMOSWAP_D 0xf800707f -#define MATCH_LR_D 0x1000302f -#define MASK_LR_D 0xf9f0707f -#define MATCH_SC_D 0x1800302f -#define MASK_SC_D 0xf800707f -#define MATCH_ECALL 0x73 -#define MASK_ECALL 0xffffffff -#define MATCH_EBREAK 0x100073 -#define MASK_EBREAK 0xffffffff -#define MATCH_URET 0x200073 -#define MASK_URET 0xffffffff -#define MATCH_SRET 0x10200073 -#define MASK_SRET 0xffffffff -#define MATCH_MRET 0x30200073 -#define MASK_MRET 0xffffffff -#define MATCH_DRET 0x7b200073 -#define MASK_DRET 0xffffffff -#define MATCH_SFENCE_VMA 0x12000073 -#define MASK_SFENCE_VMA 0xfe007fff -#define MATCH_WFI 0x10500073 -#define MASK_WFI 0xffffffff -#define MATCH_CSRRW 0x1073 -#define MASK_CSRRW 0x707f -#define MATCH_CSRRS 0x2073 -#define MASK_CSRRS 0x707f -#define MATCH_CSRRC 0x3073 -#define MASK_CSRRC 0x707f -#define MATCH_CSRRWI 0x5073 -#define MASK_CSRRWI 0x707f -#define MATCH_CSRRSI 0x6073 -#define MASK_CSRRSI 0x707f -#define MATCH_CSRRCI 0x7073 -#define MASK_CSRRCI 0x707f -#define MATCH_FADD_S 0x53 -#define MASK_FADD_S 0xfe00007f -#define MATCH_FSUB_S 0x8000053 -#define MASK_FSUB_S 0xfe00007f -#define MATCH_FMUL_S 0x10000053 -#define MASK_FMUL_S 0xfe00007f -#define MATCH_FDIV_S 0x18000053 -#define MASK_FDIV_S 0xfe00007f -#define MATCH_FSGNJ_S 0x20000053 -#define MASK_FSGNJ_S 0xfe00707f -#define MATCH_FSGNJN_S 0x20001053 -#define MASK_FSGNJN_S 0xfe00707f -#define MATCH_FSGNJX_S 0x20002053 -#define MASK_FSGNJX_S 0xfe00707f -#define MATCH_FMIN_S 0x28000053 -#define MASK_FMIN_S 0xfe00707f -#define MATCH_FMAX_S 0x28001053 -#define MASK_FMAX_S 0xfe00707f -#define MATCH_FSQRT_S 0x58000053 -#define MASK_FSQRT_S 0xfff0007f -#define MATCH_FADD_D 0x2000053 -#define MASK_FADD_D 0xfe00007f -#define MATCH_FSUB_D 0xa000053 -#define MASK_FSUB_D 0xfe00007f -#define MATCH_FMUL_D 0x12000053 -#define MASK_FMUL_D 0xfe00007f -#define MATCH_FDIV_D 0x1a000053 -#define MASK_FDIV_D 0xfe00007f -#define MATCH_FSGNJ_D 0x22000053 -#define MASK_FSGNJ_D 0xfe00707f -#define MATCH_FSGNJN_D 0x22001053 -#define MASK_FSGNJN_D 0xfe00707f -#define MATCH_FSGNJX_D 0x22002053 -#define MASK_FSGNJX_D 0xfe00707f -#define MATCH_FMIN_D 0x2a000053 -#define MASK_FMIN_D 0xfe00707f -#define MATCH_FMAX_D 0x2a001053 -#define MASK_FMAX_D 0xfe00707f -#define MATCH_FCVT_S_D 0x40100053 -#define MASK_FCVT_S_D 0xfff0007f -#define MATCH_FCVT_D_S 0x42000053 -#define MASK_FCVT_D_S 0xfff0007f -#define MATCH_FSQRT_D 0x5a000053 -#define MASK_FSQRT_D 0xfff0007f -#define MATCH_FADD_Q 0x6000053 -#define MASK_FADD_Q 0xfe00007f -#define MATCH_FSUB_Q 0xe000053 -#define MASK_FSUB_Q 0xfe00007f -#define MATCH_FMUL_Q 0x16000053 -#define MASK_FMUL_Q 0xfe00007f -#define MATCH_FDIV_Q 0x1e000053 -#define MASK_FDIV_Q 0xfe00007f -#define MATCH_FSGNJ_Q 0x26000053 -#define MASK_FSGNJ_Q 0xfe00707f -#define MATCH_FSGNJN_Q 0x26001053 -#define MASK_FSGNJN_Q 0xfe00707f -#define MATCH_FSGNJX_Q 0x26002053 -#define MASK_FSGNJX_Q 0xfe00707f -#define MATCH_FMIN_Q 0x2e000053 -#define MASK_FMIN_Q 0xfe00707f -#define MATCH_FMAX_Q 0x2e001053 -#define MASK_FMAX_Q 0xfe00707f -#define MATCH_FCVT_S_Q 0x40300053 -#define MASK_FCVT_S_Q 0xfff0007f -#define MATCH_FCVT_Q_S 0x46000053 -#define MASK_FCVT_Q_S 0xfff0007f -#define MATCH_FCVT_D_Q 0x42300053 -#define MASK_FCVT_D_Q 0xfff0007f -#define MATCH_FCVT_Q_D 0x46100053 -#define MASK_FCVT_Q_D 0xfff0007f -#define MATCH_FSQRT_Q 0x5e000053 -#define MASK_FSQRT_Q 0xfff0007f -#define MATCH_FLE_S 0xa0000053 -#define MASK_FLE_S 0xfe00707f -#define MATCH_FLT_S 0xa0001053 -#define MASK_FLT_S 0xfe00707f -#define MATCH_FEQ_S 0xa0002053 -#define MASK_FEQ_S 0xfe00707f -#define MATCH_FLE_D 0xa2000053 -#define MASK_FLE_D 0xfe00707f -#define MATCH_FLT_D 0xa2001053 -#define MASK_FLT_D 0xfe00707f -#define MATCH_FEQ_D 0xa2002053 -#define MASK_FEQ_D 0xfe00707f -#define MATCH_FLE_Q 0xa6000053 -#define MASK_FLE_Q 0xfe00707f -#define MATCH_FLT_Q 0xa6001053 -#define MASK_FLT_Q 0xfe00707f -#define MATCH_FEQ_Q 0xa6002053 -#define MASK_FEQ_Q 0xfe00707f -#define MATCH_FCVT_W_S 0xc0000053 -#define MASK_FCVT_W_S 0xfff0007f -#define MATCH_FCVT_WU_S 0xc0100053 -#define MASK_FCVT_WU_S 0xfff0007f -#define MATCH_FCVT_L_S 0xc0200053 -#define MASK_FCVT_L_S 0xfff0007f -#define MATCH_FCVT_LU_S 0xc0300053 -#define MASK_FCVT_LU_S 0xfff0007f -#define MATCH_FMV_X_W 0xe0000053 -#define MASK_FMV_X_W 0xfff0707f -#define MATCH_FCLASS_S 0xe0001053 -#define MASK_FCLASS_S 0xfff0707f -#define MATCH_FCVT_W_D 0xc2000053 -#define MASK_FCVT_W_D 0xfff0007f -#define MATCH_FCVT_WU_D 0xc2100053 -#define MASK_FCVT_WU_D 0xfff0007f -#define MATCH_FCVT_L_D 0xc2200053 -#define MASK_FCVT_L_D 0xfff0007f -#define MATCH_FCVT_LU_D 0xc2300053 -#define MASK_FCVT_LU_D 0xfff0007f -#define MATCH_FMV_X_D 0xe2000053 -#define MASK_FMV_X_D 0xfff0707f -#define MATCH_FCLASS_D 0xe2001053 -#define MASK_FCLASS_D 0xfff0707f -#define MATCH_FCVT_W_Q 0xc6000053 -#define MASK_FCVT_W_Q 0xfff0007f -#define MATCH_FCVT_WU_Q 0xc6100053 -#define MASK_FCVT_WU_Q 0xfff0007f -#define MATCH_FCVT_L_Q 0xc6200053 -#define MASK_FCVT_L_Q 0xfff0007f -#define MATCH_FCVT_LU_Q 0xc6300053 -#define MASK_FCVT_LU_Q 0xfff0007f -#define MATCH_FMV_X_Q 0xe6000053 -#define MASK_FMV_X_Q 0xfff0707f -#define MATCH_FCLASS_Q 0xe6001053 -#define MASK_FCLASS_Q 0xfff0707f -#define MATCH_FCVT_S_W 0xd0000053 -#define MASK_FCVT_S_W 0xfff0007f -#define MATCH_FCVT_S_WU 0xd0100053 -#define MASK_FCVT_S_WU 0xfff0007f -#define MATCH_FCVT_S_L 0xd0200053 -#define MASK_FCVT_S_L 0xfff0007f -#define MATCH_FCVT_S_LU 0xd0300053 -#define MASK_FCVT_S_LU 0xfff0007f -#define MATCH_FMV_W_X 0xf0000053 -#define MASK_FMV_W_X 0xfff0707f -#define MATCH_FCVT_D_W 0xd2000053 -#define MASK_FCVT_D_W 0xfff0007f -#define MATCH_FCVT_D_WU 0xd2100053 -#define MASK_FCVT_D_WU 0xfff0007f -#define MATCH_FCVT_D_L 0xd2200053 -#define MASK_FCVT_D_L 0xfff0007f -#define MATCH_FCVT_D_LU 0xd2300053 -#define MASK_FCVT_D_LU 0xfff0007f -#define MATCH_FMV_D_X 0xf2000053 -#define MASK_FMV_D_X 0xfff0707f -#define MATCH_FCVT_Q_W 0xd6000053 -#define MASK_FCVT_Q_W 0xfff0007f -#define MATCH_FCVT_Q_WU 0xd6100053 -#define MASK_FCVT_Q_WU 0xfff0007f -#define MATCH_FCVT_Q_L 0xd6200053 -#define MASK_FCVT_Q_L 0xfff0007f -#define MATCH_FCVT_Q_LU 0xd6300053 -#define MASK_FCVT_Q_LU 0xfff0007f -#define MATCH_FMV_Q_X 0xf6000053 -#define MASK_FMV_Q_X 0xfff0707f -#define MATCH_FLW 0x2007 -#define MASK_FLW 0x707f -#define MATCH_FLD 0x3007 -#define MASK_FLD 0x707f -#define MATCH_FLQ 0x4007 -#define MASK_FLQ 0x707f -#define MATCH_FSW 0x2027 -#define MASK_FSW 0x707f -#define MATCH_FSD 0x3027 -#define MASK_FSD 0x707f -#define MATCH_FSQ 0x4027 -#define MASK_FSQ 0x707f -#define MATCH_FMADD_S 0x43 -#define MASK_FMADD_S 0x600007f -#define MATCH_FMSUB_S 0x47 -#define MASK_FMSUB_S 0x600007f -#define MATCH_FNMSUB_S 0x4b -#define MASK_FNMSUB_S 0x600007f -#define MATCH_FNMADD_S 0x4f -#define MASK_FNMADD_S 0x600007f -#define MATCH_FMADD_D 0x2000043 -#define MASK_FMADD_D 0x600007f -#define MATCH_FMSUB_D 0x2000047 -#define MASK_FMSUB_D 0x600007f -#define MATCH_FNMSUB_D 0x200004b -#define MASK_FNMSUB_D 0x600007f -#define MATCH_FNMADD_D 0x200004f -#define MASK_FNMADD_D 0x600007f -#define MATCH_FMADD_Q 0x6000043 -#define MASK_FMADD_Q 0x600007f -#define MATCH_FMSUB_Q 0x6000047 -#define MASK_FMSUB_Q 0x600007f -#define MATCH_FNMSUB_Q 0x600004b -#define MASK_FNMSUB_Q 0x600007f -#define MATCH_FNMADD_Q 0x600004f -#define MASK_FNMADD_Q 0x600007f -#define MATCH_C_NOP 0x1 -#define MASK_C_NOP 0xffff -#define MATCH_C_ADDI16SP 0x6101 -#define MASK_C_ADDI16SP 0xef83 -#define MATCH_C_JR 0x8002 -#define MASK_C_JR 0xf07f -#define MATCH_C_JALR 0x9002 -#define MASK_C_JALR 0xf07f -#define MATCH_C_EBREAK 0x9002 -#define MASK_C_EBREAK 0xffff -#define MATCH_C_LD 0x6000 -#define MASK_C_LD 0xe003 -#define MATCH_C_SD 0xe000 -#define MASK_C_SD 0xe003 -#define MATCH_C_ADDIW 0x2001 -#define MASK_C_ADDIW 0xe003 -#define MATCH_C_LDSP 0x6002 -#define MASK_C_LDSP 0xe003 -#define MATCH_C_SDSP 0xe002 -#define MASK_C_SDSP 0xe003 -#define MATCH_C_ADDI4SPN 0x0 -#define MASK_C_ADDI4SPN 0xe003 -#define MATCH_C_FLD 0x2000 -#define MASK_C_FLD 0xe003 -#define MATCH_C_LW 0x4000 -#define MASK_C_LW 0xe003 -#define MATCH_C_FLW 0x6000 -#define MASK_C_FLW 0xe003 -#define MATCH_C_FSD 0xa000 -#define MASK_C_FSD 0xe003 -#define MATCH_C_SW 0xc000 -#define MASK_C_SW 0xe003 -#define MATCH_C_FSW 0xe000 -#define MASK_C_FSW 0xe003 -#define MATCH_C_ADDI 0x1 -#define MASK_C_ADDI 0xe003 -#define MATCH_C_JAL 0x2001 -#define MASK_C_JAL 0xe003 -#define MATCH_C_LI 0x4001 -#define MASK_C_LI 0xe003 -#define MATCH_C_LUI 0x6001 -#define MASK_C_LUI 0xe003 -#define MATCH_C_SRLI 0x8001 -#define MASK_C_SRLI 0xec03 -#define MATCH_C_SRAI 0x8401 -#define MASK_C_SRAI 0xec03 -#define MATCH_C_ANDI 0x8801 -#define MASK_C_ANDI 0xec03 -#define MATCH_C_SUB 0x8c01 -#define MASK_C_SUB 0xfc63 -#define MATCH_C_XOR 0x8c21 -#define MASK_C_XOR 0xfc63 -#define MATCH_C_OR 0x8c41 -#define MASK_C_OR 0xfc63 -#define MATCH_C_AND 0x8c61 -#define MASK_C_AND 0xfc63 -#define MATCH_C_SUBW 0x9c01 -#define MASK_C_SUBW 0xfc63 -#define MATCH_C_ADDW 0x9c21 -#define MASK_C_ADDW 0xfc63 -#define MATCH_C_J 0xa001 -#define MASK_C_J 0xe003 -#define MATCH_C_BEQZ 0xc001 -#define MASK_C_BEQZ 0xe003 -#define MATCH_C_BNEZ 0xe001 -#define MASK_C_BNEZ 0xe003 -#define MATCH_C_SLLI 0x2 -#define MASK_C_SLLI 0xe003 -#define MATCH_C_FLDSP 0x2002 -#define MASK_C_FLDSP 0xe003 -#define MATCH_C_LWSP 0x4002 -#define MASK_C_LWSP 0xe003 -#define MATCH_C_FLWSP 0x6002 -#define MASK_C_FLWSP 0xe003 -#define MATCH_C_MV 0x8002 -#define MASK_C_MV 0xf003 -#define MATCH_C_ADD 0x9002 -#define MASK_C_ADD 0xf003 -#define MATCH_C_FSDSP 0xa002 -#define MASK_C_FSDSP 0xe003 -#define MATCH_C_SWSP 0xc002 -#define MASK_C_SWSP 0xe003 -#define MATCH_C_FSWSP 0xe002 -#define MASK_C_FSWSP 0xe003 -#define MATCH_CUSTOM0 0xb -#define MASK_CUSTOM0 0x707f -#define MATCH_CUSTOM0_RS1 0x200b -#define MASK_CUSTOM0_RS1 0x707f -#define MATCH_CUSTOM0_RS1_RS2 0x300b -#define MASK_CUSTOM0_RS1_RS2 0x707f -#define MATCH_CUSTOM0_RD 0x400b -#define MASK_CUSTOM0_RD 0x707f -#define MATCH_CUSTOM0_RD_RS1 0x600b -#define MASK_CUSTOM0_RD_RS1 0x707f -#define MATCH_CUSTOM0_RD_RS1_RS2 0x700b -#define MASK_CUSTOM0_RD_RS1_RS2 0x707f -#define MATCH_CUSTOM1 0x2b -#define MASK_CUSTOM1 0x707f -#define MATCH_CUSTOM1_RS1 0x202b -#define MASK_CUSTOM1_RS1 0x707f -#define MATCH_CUSTOM1_RS1_RS2 0x302b -#define MASK_CUSTOM1_RS1_RS2 0x707f -#define MATCH_CUSTOM1_RD 0x402b -#define MASK_CUSTOM1_RD 0x707f -#define MATCH_CUSTOM1_RD_RS1 0x602b -#define MASK_CUSTOM1_RD_RS1 0x707f -#define MATCH_CUSTOM1_RD_RS1_RS2 0x702b -#define MASK_CUSTOM1_RD_RS1_RS2 0x707f -#define MATCH_CUSTOM2 0x5b -#define MASK_CUSTOM2 0x707f -#define MATCH_CUSTOM2_RS1 0x205b -#define MASK_CUSTOM2_RS1 0x707f -#define MATCH_CUSTOM2_RS1_RS2 0x305b -#define MASK_CUSTOM2_RS1_RS2 0x707f -#define MATCH_CUSTOM2_RD 0x405b -#define MASK_CUSTOM2_RD 0x707f -#define MATCH_CUSTOM2_RD_RS1 0x605b -#define MASK_CUSTOM2_RD_RS1 0x707f -#define MATCH_CUSTOM2_RD_RS1_RS2 0x705b -#define MASK_CUSTOM2_RD_RS1_RS2 0x707f -#define MATCH_CUSTOM3 0x7b -#define MASK_CUSTOM3 0x707f -#define MATCH_CUSTOM3_RS1 0x207b -#define MASK_CUSTOM3_RS1 0x707f -#define MATCH_CUSTOM3_RS1_RS2 0x307b -#define MASK_CUSTOM3_RS1_RS2 0x707f -#define MATCH_CUSTOM3_RD 0x407b -#define MASK_CUSTOM3_RD 0x707f -#define MATCH_CUSTOM3_RD_RS1 0x607b -#define MASK_CUSTOM3_RD_RS1 0x707f -#define MATCH_CUSTOM3_RD_RS1_RS2 0x707b -#define MASK_CUSTOM3_RD_RS1_RS2 0x707f -#define CSR_FFLAGS 0x1 -#define CSR_FRM 0x2 -#define CSR_FCSR 0x3 -#define CSR_CYCLE 0xc00 -#define CSR_TIME 0xc01 -#define CSR_INSTRET 0xc02 -#define CSR_HPMCOUNTER3 0xc03 -#define CSR_HPMCOUNTER4 0xc04 -#define CSR_HPMCOUNTER5 0xc05 -#define CSR_HPMCOUNTER6 0xc06 -#define CSR_HPMCOUNTER7 0xc07 -#define CSR_HPMCOUNTER8 0xc08 -#define CSR_HPMCOUNTER9 0xc09 -#define CSR_HPMCOUNTER10 0xc0a -#define CSR_HPMCOUNTER11 0xc0b -#define CSR_HPMCOUNTER12 0xc0c -#define CSR_HPMCOUNTER13 0xc0d -#define CSR_HPMCOUNTER14 0xc0e -#define CSR_HPMCOUNTER15 0xc0f -#define CSR_HPMCOUNTER16 0xc10 -#define CSR_HPMCOUNTER17 0xc11 -#define CSR_HPMCOUNTER18 0xc12 -#define CSR_HPMCOUNTER19 0xc13 -#define CSR_HPMCOUNTER20 0xc14 -#define CSR_HPMCOUNTER21 0xc15 -#define CSR_HPMCOUNTER22 0xc16 -#define CSR_HPMCOUNTER23 0xc17 -#define CSR_HPMCOUNTER24 0xc18 -#define CSR_HPMCOUNTER25 0xc19 -#define CSR_HPMCOUNTER26 0xc1a -#define CSR_HPMCOUNTER27 0xc1b -#define CSR_HPMCOUNTER28 0xc1c -#define CSR_HPMCOUNTER29 0xc1d -#define CSR_HPMCOUNTER30 0xc1e -#define CSR_HPMCOUNTER31 0xc1f -#define CSR_SSTATUS 0x100 -#define CSR_SIE 0x104 -#define CSR_STVEC 0x105 -#define CSR_SCOUNTEREN 0x106 -#define CSR_SSCRATCH 0x140 -#define CSR_SEPC 0x141 -#define CSR_SCAUSE 0x142 -#define CSR_STVAL 0x143 -#define CSR_SIP 0x144 -#define CSR_SATP 0x180 -#define CSR_MSTATUS 0x300 -#define CSR_MISA 0x301 -#define CSR_MEDELEG 0x302 -#define CSR_MIDELEG 0x303 -#define CSR_MIE 0x304 -#define CSR_MTVEC 0x305 -#define CSR_MCOUNTEREN 0x306 -#define CSR_MSCRATCH 0x340 -#define CSR_MEPC 0x341 -#define CSR_MCAUSE 0x342 -#define CSR_MTVAL 0x343 -#define CSR_MIP 0x344 -#define CSR_PMPCFG0 0x3a0 -#define CSR_PMPCFG1 0x3a1 -#define CSR_PMPCFG2 0x3a2 -#define CSR_PMPCFG3 0x3a3 -#define CSR_PMPADDR0 0x3b0 -#define CSR_PMPADDR1 0x3b1 -#define CSR_PMPADDR2 0x3b2 -#define CSR_PMPADDR3 0x3b3 -#define CSR_PMPADDR4 0x3b4 -#define CSR_PMPADDR5 0x3b5 -#define CSR_PMPADDR6 0x3b6 -#define CSR_PMPADDR7 0x3b7 -#define CSR_PMPADDR8 0x3b8 -#define CSR_PMPADDR9 0x3b9 -#define CSR_PMPADDR10 0x3ba -#define CSR_PMPADDR11 0x3bb -#define CSR_PMPADDR12 0x3bc -#define CSR_PMPADDR13 0x3bd -#define CSR_PMPADDR14 0x3be -#define CSR_PMPADDR15 0x3bf -#define CSR_TSELECT 0x7a0 -#define CSR_TDATA1 0x7a1 -#define CSR_TDATA2 0x7a2 -#define CSR_TDATA3 0x7a3 -#define CSR_DCSR 0x7b0 -#define CSR_DPC 0x7b1 -#define CSR_DSCRATCH 0x7b2 -#define CSR_MCYCLE 0xb00 -#define CSR_MINSTRET 0xb02 -#define CSR_MHPMCOUNTER3 0xb03 -#define CSR_MHPMCOUNTER4 0xb04 -#define CSR_MHPMCOUNTER5 0xb05 -#define CSR_MHPMCOUNTER6 0xb06 -#define CSR_MHPMCOUNTER7 0xb07 -#define CSR_MHPMCOUNTER8 0xb08 -#define CSR_MHPMCOUNTER9 0xb09 -#define CSR_MHPMCOUNTER10 0xb0a -#define CSR_MHPMCOUNTER11 0xb0b -#define CSR_MHPMCOUNTER12 0xb0c -#define CSR_MHPMCOUNTER13 0xb0d -#define CSR_MHPMCOUNTER14 0xb0e -#define CSR_MHPMCOUNTER15 0xb0f -#define CSR_MHPMCOUNTER16 0xb10 -#define CSR_MHPMCOUNTER17 0xb11 -#define CSR_MHPMCOUNTER18 0xb12 -#define CSR_MHPMCOUNTER19 0xb13 -#define CSR_MHPMCOUNTER20 0xb14 -#define CSR_MHPMCOUNTER21 0xb15 -#define CSR_MHPMCOUNTER22 0xb16 -#define CSR_MHPMCOUNTER23 0xb17 -#define CSR_MHPMCOUNTER24 0xb18 -#define CSR_MHPMCOUNTER25 0xb19 -#define CSR_MHPMCOUNTER26 0xb1a -#define CSR_MHPMCOUNTER27 0xb1b -#define CSR_MHPMCOUNTER28 0xb1c -#define CSR_MHPMCOUNTER29 0xb1d -#define CSR_MHPMCOUNTER30 0xb1e -#define CSR_MHPMCOUNTER31 0xb1f -#define CSR_MHPMEVENT3 0x323 -#define CSR_MHPMEVENT4 0x324 -#define CSR_MHPMEVENT5 0x325 -#define CSR_MHPMEVENT6 0x326 -#define CSR_MHPMEVENT7 0x327 -#define CSR_MHPMEVENT8 0x328 -#define CSR_MHPMEVENT9 0x329 -#define CSR_MHPMEVENT10 0x32a -#define CSR_MHPMEVENT11 0x32b -#define CSR_MHPMEVENT12 0x32c -#define CSR_MHPMEVENT13 0x32d -#define CSR_MHPMEVENT14 0x32e -#define CSR_MHPMEVENT15 0x32f -#define CSR_MHPMEVENT16 0x330 -#define CSR_MHPMEVENT17 0x331 -#define CSR_MHPMEVENT18 0x332 -#define CSR_MHPMEVENT19 0x333 -#define CSR_MHPMEVENT20 0x334 -#define CSR_MHPMEVENT21 0x335 -#define CSR_MHPMEVENT22 0x336 -#define CSR_MHPMEVENT23 0x337 -#define CSR_MHPMEVENT24 0x338 -#define CSR_MHPMEVENT25 0x339 -#define CSR_MHPMEVENT26 0x33a -#define CSR_MHPMEVENT27 0x33b -#define CSR_MHPMEVENT28 0x33c -#define CSR_MHPMEVENT29 0x33d -#define CSR_MHPMEVENT30 0x33e -#define CSR_MHPMEVENT31 0x33f -#define CSR_MVENDORID 0xf11 -#define CSR_MARCHID 0xf12 -#define CSR_MIMPID 0xf13 -#define CSR_MHARTID 0xf14 -#define CSR_CYCLEH 0xc80 -#define CSR_TIMEH 0xc81 -#define CSR_INSTRETH 0xc82 -#define CSR_HPMCOUNTER3H 0xc83 -#define CSR_HPMCOUNTER4H 0xc84 -#define CSR_HPMCOUNTER5H 0xc85 -#define CSR_HPMCOUNTER6H 0xc86 -#define CSR_HPMCOUNTER7H 0xc87 -#define CSR_HPMCOUNTER8H 0xc88 -#define CSR_HPMCOUNTER9H 0xc89 -#define CSR_HPMCOUNTER10H 0xc8a -#define CSR_HPMCOUNTER11H 0xc8b -#define CSR_HPMCOUNTER12H 0xc8c -#define CSR_HPMCOUNTER13H 0xc8d -#define CSR_HPMCOUNTER14H 0xc8e -#define CSR_HPMCOUNTER15H 0xc8f -#define CSR_HPMCOUNTER16H 0xc90 -#define CSR_HPMCOUNTER17H 0xc91 -#define CSR_HPMCOUNTER18H 0xc92 -#define CSR_HPMCOUNTER19H 0xc93 -#define CSR_HPMCOUNTER20H 0xc94 -#define CSR_HPMCOUNTER21H 0xc95 -#define CSR_HPMCOUNTER22H 0xc96 -#define CSR_HPMCOUNTER23H 0xc97 -#define CSR_HPMCOUNTER24H 0xc98 -#define CSR_HPMCOUNTER25H 0xc99 -#define CSR_HPMCOUNTER26H 0xc9a -#define CSR_HPMCOUNTER27H 0xc9b -#define CSR_HPMCOUNTER28H 0xc9c -#define CSR_HPMCOUNTER29H 0xc9d -#define CSR_HPMCOUNTER30H 0xc9e -#define CSR_HPMCOUNTER31H 0xc9f -#define CSR_MCYCLEH 0xb80 -#define CSR_MINSTRETH 0xb82 -#define CSR_MHPMCOUNTER3H 0xb83 -#define CSR_MHPMCOUNTER4H 0xb84 -#define CSR_MHPMCOUNTER5H 0xb85 -#define CSR_MHPMCOUNTER6H 0xb86 -#define CSR_MHPMCOUNTER7H 0xb87 -#define CSR_MHPMCOUNTER8H 0xb88 -#define CSR_MHPMCOUNTER9H 0xb89 -#define CSR_MHPMCOUNTER10H 0xb8a -#define CSR_MHPMCOUNTER11H 0xb8b -#define CSR_MHPMCOUNTER12H 0xb8c -#define CSR_MHPMCOUNTER13H 0xb8d -#define CSR_MHPMCOUNTER14H 0xb8e -#define CSR_MHPMCOUNTER15H 0xb8f -#define CSR_MHPMCOUNTER16H 0xb90 -#define CSR_MHPMCOUNTER17H 0xb91 -#define CSR_MHPMCOUNTER18H 0xb92 -#define CSR_MHPMCOUNTER19H 0xb93 -#define CSR_MHPMCOUNTER20H 0xb94 -#define CSR_MHPMCOUNTER21H 0xb95 -#define CSR_MHPMCOUNTER22H 0xb96 -#define CSR_MHPMCOUNTER23H 0xb97 -#define CSR_MHPMCOUNTER24H 0xb98 -#define CSR_MHPMCOUNTER25H 0xb99 -#define CSR_MHPMCOUNTER26H 0xb9a -#define CSR_MHPMCOUNTER27H 0xb9b -#define CSR_MHPMCOUNTER28H 0xb9c -#define CSR_MHPMCOUNTER29H 0xb9d -#define CSR_MHPMCOUNTER30H 0xb9e -#define CSR_MHPMCOUNTER31H 0xb9f -#define CAUSE_MISALIGNED_FETCH 0x0 -#define CAUSE_FETCH_ACCESS 0x1 -#define CAUSE_ILLEGAL_INSTRUCTION 0x2 -#define CAUSE_BREAKPOINT 0x3 -#define CAUSE_MISALIGNED_LOAD 0x4 -#define CAUSE_LOAD_ACCESS 0x5 -#define CAUSE_MISALIGNED_STORE 0x6 -#define CAUSE_STORE_ACCESS 0x7 -#define CAUSE_USER_ECALL 0x8 -#define CAUSE_SUPERVISOR_ECALL 0x9 -#define CAUSE_HYPERVISOR_ECALL 0xa -#define CAUSE_MACHINE_ECALL 0xb -#define CAUSE_FETCH_PAGE_FAULT 0xc -#define CAUSE_LOAD_PAGE_FAULT 0xd -#define CAUSE_STORE_PAGE_FAULT 0xf -#endif -#ifdef DECLARE_INSN -DECLARE_INSN(beq, MATCH_BEQ, MASK_BEQ) -DECLARE_INSN(bne, MATCH_BNE, MASK_BNE) -DECLARE_INSN(blt, MATCH_BLT, MASK_BLT) -DECLARE_INSN(bge, MATCH_BGE, MASK_BGE) -DECLARE_INSN(bltu, MATCH_BLTU, MASK_BLTU) -DECLARE_INSN(bgeu, MATCH_BGEU, MASK_BGEU) -DECLARE_INSN(jalr, MATCH_JALR, MASK_JALR) -DECLARE_INSN(jal, MATCH_JAL, MASK_JAL) -DECLARE_INSN(lui, MATCH_LUI, MASK_LUI) -DECLARE_INSN(auipc, MATCH_AUIPC, MASK_AUIPC) -DECLARE_INSN(addi, MATCH_ADDI, MASK_ADDI) -DECLARE_INSN(slli, MATCH_SLLI, MASK_SLLI) -DECLARE_INSN(slti, MATCH_SLTI, MASK_SLTI) -DECLARE_INSN(sltiu, MATCH_SLTIU, MASK_SLTIU) -DECLARE_INSN(xori, MATCH_XORI, MASK_XORI) -DECLARE_INSN(srli, MATCH_SRLI, MASK_SRLI) -DECLARE_INSN(srai, MATCH_SRAI, MASK_SRAI) -DECLARE_INSN(ori, MATCH_ORI, MASK_ORI) -DECLARE_INSN(andi, MATCH_ANDI, MASK_ANDI) -DECLARE_INSN(add, MATCH_ADD, MASK_ADD) -DECLARE_INSN(sub, MATCH_SUB, MASK_SUB) -DECLARE_INSN(sll, MATCH_SLL, MASK_SLL) -DECLARE_INSN(slt, MATCH_SLT, MASK_SLT) -DECLARE_INSN(sltu, MATCH_SLTU, MASK_SLTU) -DECLARE_INSN(xor, MATCH_XOR, MASK_XOR) -DECLARE_INSN(srl, MATCH_SRL, MASK_SRL) -DECLARE_INSN(sra, MATCH_SRA, MASK_SRA) -DECLARE_INSN(or, MATCH_OR, MASK_OR) -DECLARE_INSN(and, MATCH_AND, MASK_AND) -DECLARE_INSN(addiw, MATCH_ADDIW, MASK_ADDIW) -DECLARE_INSN(slliw, MATCH_SLLIW, MASK_SLLIW) -DECLARE_INSN(srliw, MATCH_SRLIW, MASK_SRLIW) -DECLARE_INSN(sraiw, MATCH_SRAIW, MASK_SRAIW) -DECLARE_INSN(addw, MATCH_ADDW, MASK_ADDW) -DECLARE_INSN(subw, MATCH_SUBW, MASK_SUBW) -DECLARE_INSN(sllw, MATCH_SLLW, MASK_SLLW) -DECLARE_INSN(srlw, MATCH_SRLW, MASK_SRLW) -DECLARE_INSN(sraw, MATCH_SRAW, MASK_SRAW) -DECLARE_INSN(lb, MATCH_LB, MASK_LB) -DECLARE_INSN(lh, MATCH_LH, MASK_LH) -DECLARE_INSN(lw, MATCH_LW, MASK_LW) -DECLARE_INSN(ld, MATCH_LD, MASK_LD) -DECLARE_INSN(lbu, MATCH_LBU, MASK_LBU) -DECLARE_INSN(lhu, MATCH_LHU, MASK_LHU) -DECLARE_INSN(lwu, MATCH_LWU, MASK_LWU) -DECLARE_INSN(sb, MATCH_SB, MASK_SB) -DECLARE_INSN(sh, MATCH_SH, MASK_SH) -DECLARE_INSN(sw, MATCH_SW, MASK_SW) -DECLARE_INSN(sd, MATCH_SD, MASK_SD) -DECLARE_INSN(fence, MATCH_FENCE, MASK_FENCE) -DECLARE_INSN(fence_i, MATCH_FENCE_I, MASK_FENCE_I) -DECLARE_INSN(mul, MATCH_MUL, MASK_MUL) -DECLARE_INSN(mulh, MATCH_MULH, MASK_MULH) -DECLARE_INSN(mulhsu, MATCH_MULHSU, MASK_MULHSU) -DECLARE_INSN(mulhu, MATCH_MULHU, MASK_MULHU) -DECLARE_INSN(div, MATCH_DIV, MASK_DIV) -DECLARE_INSN(divu, MATCH_DIVU, MASK_DIVU) -DECLARE_INSN(rem, MATCH_REM, MASK_REM) -DECLARE_INSN(remu, MATCH_REMU, MASK_REMU) -DECLARE_INSN(mulw, MATCH_MULW, MASK_MULW) -DECLARE_INSN(divw, MATCH_DIVW, MASK_DIVW) -DECLARE_INSN(divuw, MATCH_DIVUW, MASK_DIVUW) -DECLARE_INSN(remw, MATCH_REMW, MASK_REMW) -DECLARE_INSN(remuw, MATCH_REMUW, MASK_REMUW) -DECLARE_INSN(amoadd_w, MATCH_AMOADD_W, MASK_AMOADD_W) -DECLARE_INSN(amoxor_w, MATCH_AMOXOR_W, MASK_AMOXOR_W) -DECLARE_INSN(amoor_w, MATCH_AMOOR_W, MASK_AMOOR_W) -DECLARE_INSN(amoand_w, MATCH_AMOAND_W, MASK_AMOAND_W) -DECLARE_INSN(amomin_w, MATCH_AMOMIN_W, MASK_AMOMIN_W) -DECLARE_INSN(amomax_w, MATCH_AMOMAX_W, MASK_AMOMAX_W) -DECLARE_INSN(amominu_w, MATCH_AMOMINU_W, MASK_AMOMINU_W) -DECLARE_INSN(amomaxu_w, MATCH_AMOMAXU_W, MASK_AMOMAXU_W) -DECLARE_INSN(amoswap_w, MATCH_AMOSWAP_W, MASK_AMOSWAP_W) -DECLARE_INSN(lr_w, MATCH_LR_W, MASK_LR_W) -DECLARE_INSN(sc_w, MATCH_SC_W, MASK_SC_W) -DECLARE_INSN(amoadd_d, MATCH_AMOADD_D, MASK_AMOADD_D) -DECLARE_INSN(amoxor_d, MATCH_AMOXOR_D, MASK_AMOXOR_D) -DECLARE_INSN(amoor_d, MATCH_AMOOR_D, MASK_AMOOR_D) -DECLARE_INSN(amoand_d, MATCH_AMOAND_D, MASK_AMOAND_D) -DECLARE_INSN(amomin_d, MATCH_AMOMIN_D, MASK_AMOMIN_D) -DECLARE_INSN(amomax_d, MATCH_AMOMAX_D, MASK_AMOMAX_D) -DECLARE_INSN(amominu_d, MATCH_AMOMINU_D, MASK_AMOMINU_D) -DECLARE_INSN(amomaxu_d, MATCH_AMOMAXU_D, MASK_AMOMAXU_D) -DECLARE_INSN(amoswap_d, MATCH_AMOSWAP_D, MASK_AMOSWAP_D) -DECLARE_INSN(lr_d, MATCH_LR_D, MASK_LR_D) -DECLARE_INSN(sc_d, MATCH_SC_D, MASK_SC_D) -DECLARE_INSN(ecall, MATCH_ECALL, MASK_ECALL) -DECLARE_INSN(ebreak, MATCH_EBREAK, MASK_EBREAK) -DECLARE_INSN(uret, MATCH_URET, MASK_URET) -DECLARE_INSN(sret, MATCH_SRET, MASK_SRET) -DECLARE_INSN(mret, MATCH_MRET, MASK_MRET) -DECLARE_INSN(dret, MATCH_DRET, MASK_DRET) -DECLARE_INSN(sfence_vma, MATCH_SFENCE_VMA, MASK_SFENCE_VMA) -DECLARE_INSN(wfi, MATCH_WFI, MASK_WFI) -DECLARE_INSN(csrrw, MATCH_CSRRW, MASK_CSRRW) -DECLARE_INSN(csrrs, MATCH_CSRRS, MASK_CSRRS) -DECLARE_INSN(csrrc, MATCH_CSRRC, MASK_CSRRC) -DECLARE_INSN(csrrwi, MATCH_CSRRWI, MASK_CSRRWI) -DECLARE_INSN(csrrsi, MATCH_CSRRSI, MASK_CSRRSI) -DECLARE_INSN(csrrci, MATCH_CSRRCI, MASK_CSRRCI) -DECLARE_INSN(fadd_s, MATCH_FADD_S, MASK_FADD_S) -DECLARE_INSN(fsub_s, MATCH_FSUB_S, MASK_FSUB_S) -DECLARE_INSN(fmul_s, MATCH_FMUL_S, MASK_FMUL_S) -DECLARE_INSN(fdiv_s, MATCH_FDIV_S, MASK_FDIV_S) -DECLARE_INSN(fsgnj_s, MATCH_FSGNJ_S, MASK_FSGNJ_S) -DECLARE_INSN(fsgnjn_s, MATCH_FSGNJN_S, MASK_FSGNJN_S) -DECLARE_INSN(fsgnjx_s, MATCH_FSGNJX_S, MASK_FSGNJX_S) -DECLARE_INSN(fmin_s, MATCH_FMIN_S, MASK_FMIN_S) -DECLARE_INSN(fmax_s, MATCH_FMAX_S, MASK_FMAX_S) -DECLARE_INSN(fsqrt_s, MATCH_FSQRT_S, MASK_FSQRT_S) -DECLARE_INSN(fadd_d, MATCH_FADD_D, MASK_FADD_D) -DECLARE_INSN(fsub_d, MATCH_FSUB_D, MASK_FSUB_D) -DECLARE_INSN(fmul_d, MATCH_FMUL_D, MASK_FMUL_D) -DECLARE_INSN(fdiv_d, MATCH_FDIV_D, MASK_FDIV_D) -DECLARE_INSN(fsgnj_d, MATCH_FSGNJ_D, MASK_FSGNJ_D) -DECLARE_INSN(fsgnjn_d, MATCH_FSGNJN_D, MASK_FSGNJN_D) -DECLARE_INSN(fsgnjx_d, MATCH_FSGNJX_D, MASK_FSGNJX_D) -DECLARE_INSN(fmin_d, MATCH_FMIN_D, MASK_FMIN_D) -DECLARE_INSN(fmax_d, MATCH_FMAX_D, MASK_FMAX_D) -DECLARE_INSN(fcvt_s_d, MATCH_FCVT_S_D, MASK_FCVT_S_D) -DECLARE_INSN(fcvt_d_s, MATCH_FCVT_D_S, MASK_FCVT_D_S) -DECLARE_INSN(fsqrt_d, MATCH_FSQRT_D, MASK_FSQRT_D) -DECLARE_INSN(fadd_q, MATCH_FADD_Q, MASK_FADD_Q) -DECLARE_INSN(fsub_q, MATCH_FSUB_Q, MASK_FSUB_Q) -DECLARE_INSN(fmul_q, MATCH_FMUL_Q, MASK_FMUL_Q) -DECLARE_INSN(fdiv_q, MATCH_FDIV_Q, MASK_FDIV_Q) -DECLARE_INSN(fsgnj_q, MATCH_FSGNJ_Q, MASK_FSGNJ_Q) -DECLARE_INSN(fsgnjn_q, MATCH_FSGNJN_Q, MASK_FSGNJN_Q) -DECLARE_INSN(fsgnjx_q, MATCH_FSGNJX_Q, MASK_FSGNJX_Q) -DECLARE_INSN(fmin_q, MATCH_FMIN_Q, MASK_FMIN_Q) -DECLARE_INSN(fmax_q, MATCH_FMAX_Q, MASK_FMAX_Q) -DECLARE_INSN(fcvt_s_q, MATCH_FCVT_S_Q, MASK_FCVT_S_Q) -DECLARE_INSN(fcvt_q_s, MATCH_FCVT_Q_S, MASK_FCVT_Q_S) -DECLARE_INSN(fcvt_d_q, MATCH_FCVT_D_Q, MASK_FCVT_D_Q) -DECLARE_INSN(fcvt_q_d, MATCH_FCVT_Q_D, MASK_FCVT_Q_D) -DECLARE_INSN(fsqrt_q, MATCH_FSQRT_Q, MASK_FSQRT_Q) -DECLARE_INSN(fle_s, MATCH_FLE_S, MASK_FLE_S) -DECLARE_INSN(flt_s, MATCH_FLT_S, MASK_FLT_S) -DECLARE_INSN(feq_s, MATCH_FEQ_S, MASK_FEQ_S) -DECLARE_INSN(fle_d, MATCH_FLE_D, MASK_FLE_D) -DECLARE_INSN(flt_d, MATCH_FLT_D, MASK_FLT_D) -DECLARE_INSN(feq_d, MATCH_FEQ_D, MASK_FEQ_D) -DECLARE_INSN(fle_q, MATCH_FLE_Q, MASK_FLE_Q) -DECLARE_INSN(flt_q, MATCH_FLT_Q, MASK_FLT_Q) -DECLARE_INSN(feq_q, MATCH_FEQ_Q, MASK_FEQ_Q) -DECLARE_INSN(fcvt_w_s, MATCH_FCVT_W_S, MASK_FCVT_W_S) -DECLARE_INSN(fcvt_wu_s, MATCH_FCVT_WU_S, MASK_FCVT_WU_S) -DECLARE_INSN(fcvt_l_s, MATCH_FCVT_L_S, MASK_FCVT_L_S) -DECLARE_INSN(fcvt_lu_s, MATCH_FCVT_LU_S, MASK_FCVT_LU_S) -DECLARE_INSN(fmv_x_w, MATCH_FMV_X_W, MASK_FMV_X_W) -DECLARE_INSN(fclass_s, MATCH_FCLASS_S, MASK_FCLASS_S) -DECLARE_INSN(fcvt_w_d, MATCH_FCVT_W_D, MASK_FCVT_W_D) -DECLARE_INSN(fcvt_wu_d, MATCH_FCVT_WU_D, MASK_FCVT_WU_D) -DECLARE_INSN(fcvt_l_d, MATCH_FCVT_L_D, MASK_FCVT_L_D) -DECLARE_INSN(fcvt_lu_d, MATCH_FCVT_LU_D, MASK_FCVT_LU_D) -DECLARE_INSN(fmv_x_d, MATCH_FMV_X_D, MASK_FMV_X_D) -DECLARE_INSN(fclass_d, MATCH_FCLASS_D, MASK_FCLASS_D) -DECLARE_INSN(fcvt_w_q, MATCH_FCVT_W_Q, MASK_FCVT_W_Q) -DECLARE_INSN(fcvt_wu_q, MATCH_FCVT_WU_Q, MASK_FCVT_WU_Q) -DECLARE_INSN(fcvt_l_q, MATCH_FCVT_L_Q, MASK_FCVT_L_Q) -DECLARE_INSN(fcvt_lu_q, MATCH_FCVT_LU_Q, MASK_FCVT_LU_Q) -DECLARE_INSN(fmv_x_q, MATCH_FMV_X_Q, MASK_FMV_X_Q) -DECLARE_INSN(fclass_q, MATCH_FCLASS_Q, MASK_FCLASS_Q) -DECLARE_INSN(fcvt_s_w, MATCH_FCVT_S_W, MASK_FCVT_S_W) -DECLARE_INSN(fcvt_s_wu, MATCH_FCVT_S_WU, MASK_FCVT_S_WU) -DECLARE_INSN(fcvt_s_l, MATCH_FCVT_S_L, MASK_FCVT_S_L) -DECLARE_INSN(fcvt_s_lu, MATCH_FCVT_S_LU, MASK_FCVT_S_LU) -DECLARE_INSN(fmv_w_x, MATCH_FMV_W_X, MASK_FMV_W_X) -DECLARE_INSN(fcvt_d_w, MATCH_FCVT_D_W, MASK_FCVT_D_W) -DECLARE_INSN(fcvt_d_wu, MATCH_FCVT_D_WU, MASK_FCVT_D_WU) -DECLARE_INSN(fcvt_d_l, MATCH_FCVT_D_L, MASK_FCVT_D_L) -DECLARE_INSN(fcvt_d_lu, MATCH_FCVT_D_LU, MASK_FCVT_D_LU) -DECLARE_INSN(fmv_d_x, MATCH_FMV_D_X, MASK_FMV_D_X) -DECLARE_INSN(fcvt_q_w, MATCH_FCVT_Q_W, MASK_FCVT_Q_W) -DECLARE_INSN(fcvt_q_wu, MATCH_FCVT_Q_WU, MASK_FCVT_Q_WU) -DECLARE_INSN(fcvt_q_l, MATCH_FCVT_Q_L, MASK_FCVT_Q_L) -DECLARE_INSN(fcvt_q_lu, MATCH_FCVT_Q_LU, MASK_FCVT_Q_LU) -DECLARE_INSN(fmv_q_x, MATCH_FMV_Q_X, MASK_FMV_Q_X) -DECLARE_INSN(flw, MATCH_FLW, MASK_FLW) -DECLARE_INSN(fld, MATCH_FLD, MASK_FLD) -DECLARE_INSN(flq, MATCH_FLQ, MASK_FLQ) -DECLARE_INSN(fsw, MATCH_FSW, MASK_FSW) -DECLARE_INSN(fsd, MATCH_FSD, MASK_FSD) -DECLARE_INSN(fsq, MATCH_FSQ, MASK_FSQ) -DECLARE_INSN(fmadd_s, MATCH_FMADD_S, MASK_FMADD_S) -DECLARE_INSN(fmsub_s, MATCH_FMSUB_S, MASK_FMSUB_S) -DECLARE_INSN(fnmsub_s, MATCH_FNMSUB_S, MASK_FNMSUB_S) -DECLARE_INSN(fnmadd_s, MATCH_FNMADD_S, MASK_FNMADD_S) -DECLARE_INSN(fmadd_d, MATCH_FMADD_D, MASK_FMADD_D) -DECLARE_INSN(fmsub_d, MATCH_FMSUB_D, MASK_FMSUB_D) -DECLARE_INSN(fnmsub_d, MATCH_FNMSUB_D, MASK_FNMSUB_D) -DECLARE_INSN(fnmadd_d, MATCH_FNMADD_D, MASK_FNMADD_D) -DECLARE_INSN(fmadd_q, MATCH_FMADD_Q, MASK_FMADD_Q) -DECLARE_INSN(fmsub_q, MATCH_FMSUB_Q, MASK_FMSUB_Q) -DECLARE_INSN(fnmsub_q, MATCH_FNMSUB_Q, MASK_FNMSUB_Q) -DECLARE_INSN(fnmadd_q, MATCH_FNMADD_Q, MASK_FNMADD_Q) -DECLARE_INSN(c_nop, MATCH_C_NOP, MASK_C_NOP) -DECLARE_INSN(c_addi16sp, MATCH_C_ADDI16SP, MASK_C_ADDI16SP) -DECLARE_INSN(c_jr, MATCH_C_JR, MASK_C_JR) -DECLARE_INSN(c_jalr, MATCH_C_JALR, MASK_C_JALR) -DECLARE_INSN(c_ebreak, MATCH_C_EBREAK, MASK_C_EBREAK) -DECLARE_INSN(c_ld, MATCH_C_LD, MASK_C_LD) -DECLARE_INSN(c_sd, MATCH_C_SD, MASK_C_SD) -DECLARE_INSN(c_addiw, MATCH_C_ADDIW, MASK_C_ADDIW) -DECLARE_INSN(c_ldsp, MATCH_C_LDSP, MASK_C_LDSP) -DECLARE_INSN(c_sdsp, MATCH_C_SDSP, MASK_C_SDSP) -DECLARE_INSN(c_addi4spn, MATCH_C_ADDI4SPN, MASK_C_ADDI4SPN) -DECLARE_INSN(c_fld, MATCH_C_FLD, MASK_C_FLD) -DECLARE_INSN(c_lw, MATCH_C_LW, MASK_C_LW) -DECLARE_INSN(c_flw, MATCH_C_FLW, MASK_C_FLW) -DECLARE_INSN(c_fsd, MATCH_C_FSD, MASK_C_FSD) -DECLARE_INSN(c_sw, MATCH_C_SW, MASK_C_SW) -DECLARE_INSN(c_fsw, MATCH_C_FSW, MASK_C_FSW) -DECLARE_INSN(c_addi, MATCH_C_ADDI, MASK_C_ADDI) -DECLARE_INSN(c_jal, MATCH_C_JAL, MASK_C_JAL) -DECLARE_INSN(c_li, MATCH_C_LI, MASK_C_LI) -DECLARE_INSN(c_lui, MATCH_C_LUI, MASK_C_LUI) -DECLARE_INSN(c_srli, MATCH_C_SRLI, MASK_C_SRLI) -DECLARE_INSN(c_srai, MATCH_C_SRAI, MASK_C_SRAI) -DECLARE_INSN(c_andi, MATCH_C_ANDI, MASK_C_ANDI) -DECLARE_INSN(c_sub, MATCH_C_SUB, MASK_C_SUB) -DECLARE_INSN(c_xor, MATCH_C_XOR, MASK_C_XOR) -DECLARE_INSN(c_or, MATCH_C_OR, MASK_C_OR) -DECLARE_INSN(c_and, MATCH_C_AND, MASK_C_AND) -DECLARE_INSN(c_subw, MATCH_C_SUBW, MASK_C_SUBW) -DECLARE_INSN(c_addw, MATCH_C_ADDW, MASK_C_ADDW) -DECLARE_INSN(c_j, MATCH_C_J, MASK_C_J) -DECLARE_INSN(c_beqz, MATCH_C_BEQZ, MASK_C_BEQZ) -DECLARE_INSN(c_bnez, MATCH_C_BNEZ, MASK_C_BNEZ) -DECLARE_INSN(c_slli, MATCH_C_SLLI, MASK_C_SLLI) -DECLARE_INSN(c_fldsp, MATCH_C_FLDSP, MASK_C_FLDSP) -DECLARE_INSN(c_lwsp, MATCH_C_LWSP, MASK_C_LWSP) -DECLARE_INSN(c_flwsp, MATCH_C_FLWSP, MASK_C_FLWSP) -DECLARE_INSN(c_mv, MATCH_C_MV, MASK_C_MV) -DECLARE_INSN(c_add, MATCH_C_ADD, MASK_C_ADD) -DECLARE_INSN(c_fsdsp, MATCH_C_FSDSP, MASK_C_FSDSP) -DECLARE_INSN(c_swsp, MATCH_C_SWSP, MASK_C_SWSP) -DECLARE_INSN(c_fswsp, MATCH_C_FSWSP, MASK_C_FSWSP) -DECLARE_INSN(custom0, MATCH_CUSTOM0, MASK_CUSTOM0) -DECLARE_INSN(custom0_rs1, MATCH_CUSTOM0_RS1, MASK_CUSTOM0_RS1) -DECLARE_INSN(custom0_rs1_rs2, MATCH_CUSTOM0_RS1_RS2, MASK_CUSTOM0_RS1_RS2) -DECLARE_INSN(custom0_rd, MATCH_CUSTOM0_RD, MASK_CUSTOM0_RD) -DECLARE_INSN(custom0_rd_rs1, MATCH_CUSTOM0_RD_RS1, MASK_CUSTOM0_RD_RS1) -DECLARE_INSN(custom0_rd_rs1_rs2, MATCH_CUSTOM0_RD_RS1_RS2, MASK_CUSTOM0_RD_RS1_RS2) -DECLARE_INSN(custom1, MATCH_CUSTOM1, MASK_CUSTOM1) -DECLARE_INSN(custom1_rs1, MATCH_CUSTOM1_RS1, MASK_CUSTOM1_RS1) -DECLARE_INSN(custom1_rs1_rs2, MATCH_CUSTOM1_RS1_RS2, MASK_CUSTOM1_RS1_RS2) -DECLARE_INSN(custom1_rd, MATCH_CUSTOM1_RD, MASK_CUSTOM1_RD) -DECLARE_INSN(custom1_rd_rs1, MATCH_CUSTOM1_RD_RS1, MASK_CUSTOM1_RD_RS1) -DECLARE_INSN(custom1_rd_rs1_rs2, MATCH_CUSTOM1_RD_RS1_RS2, MASK_CUSTOM1_RD_RS1_RS2) -DECLARE_INSN(custom2, MATCH_CUSTOM2, MASK_CUSTOM2) -DECLARE_INSN(custom2_rs1, MATCH_CUSTOM2_RS1, MASK_CUSTOM2_RS1) -DECLARE_INSN(custom2_rs1_rs2, MATCH_CUSTOM2_RS1_RS2, MASK_CUSTOM2_RS1_RS2) -DECLARE_INSN(custom2_rd, MATCH_CUSTOM2_RD, MASK_CUSTOM2_RD) -DECLARE_INSN(custom2_rd_rs1, MATCH_CUSTOM2_RD_RS1, MASK_CUSTOM2_RD_RS1) -DECLARE_INSN(custom2_rd_rs1_rs2, MATCH_CUSTOM2_RD_RS1_RS2, MASK_CUSTOM2_RD_RS1_RS2) -DECLARE_INSN(custom3, MATCH_CUSTOM3, MASK_CUSTOM3) -DECLARE_INSN(custom3_rs1, MATCH_CUSTOM3_RS1, MASK_CUSTOM3_RS1) -DECLARE_INSN(custom3_rs1_rs2, MATCH_CUSTOM3_RS1_RS2, MASK_CUSTOM3_RS1_RS2) -DECLARE_INSN(custom3_rd, MATCH_CUSTOM3_RD, MASK_CUSTOM3_RD) -DECLARE_INSN(custom3_rd_rs1, MATCH_CUSTOM3_RD_RS1, MASK_CUSTOM3_RD_RS1) -DECLARE_INSN(custom3_rd_rs1_rs2, MATCH_CUSTOM3_RD_RS1_RS2, MASK_CUSTOM3_RD_RS1_RS2) -#endif -#ifdef DECLARE_CSR -DECLARE_CSR(fflags, CSR_FFLAGS) -DECLARE_CSR(frm, CSR_FRM) -DECLARE_CSR(fcsr, CSR_FCSR) -DECLARE_CSR(cycle, CSR_CYCLE) -DECLARE_CSR(time, CSR_TIME) -DECLARE_CSR(instret, CSR_INSTRET) -DECLARE_CSR(hpmcounter3, CSR_HPMCOUNTER3) -DECLARE_CSR(hpmcounter4, CSR_HPMCOUNTER4) -DECLARE_CSR(hpmcounter5, CSR_HPMCOUNTER5) -DECLARE_CSR(hpmcounter6, CSR_HPMCOUNTER6) -DECLARE_CSR(hpmcounter7, CSR_HPMCOUNTER7) -DECLARE_CSR(hpmcounter8, CSR_HPMCOUNTER8) -DECLARE_CSR(hpmcounter9, CSR_HPMCOUNTER9) -DECLARE_CSR(hpmcounter10, CSR_HPMCOUNTER10) -DECLARE_CSR(hpmcounter11, CSR_HPMCOUNTER11) -DECLARE_CSR(hpmcounter12, CSR_HPMCOUNTER12) -DECLARE_CSR(hpmcounter13, CSR_HPMCOUNTER13) -DECLARE_CSR(hpmcounter14, CSR_HPMCOUNTER14) -DECLARE_CSR(hpmcounter15, CSR_HPMCOUNTER15) -DECLARE_CSR(hpmcounter16, CSR_HPMCOUNTER16) -DECLARE_CSR(hpmcounter17, CSR_HPMCOUNTER17) -DECLARE_CSR(hpmcounter18, CSR_HPMCOUNTER18) -DECLARE_CSR(hpmcounter19, CSR_HPMCOUNTER19) -DECLARE_CSR(hpmcounter20, CSR_HPMCOUNTER20) -DECLARE_CSR(hpmcounter21, CSR_HPMCOUNTER21) -DECLARE_CSR(hpmcounter22, CSR_HPMCOUNTER22) -DECLARE_CSR(hpmcounter23, CSR_HPMCOUNTER23) -DECLARE_CSR(hpmcounter24, CSR_HPMCOUNTER24) -DECLARE_CSR(hpmcounter25, CSR_HPMCOUNTER25) -DECLARE_CSR(hpmcounter26, CSR_HPMCOUNTER26) -DECLARE_CSR(hpmcounter27, CSR_HPMCOUNTER27) -DECLARE_CSR(hpmcounter28, CSR_HPMCOUNTER28) -DECLARE_CSR(hpmcounter29, CSR_HPMCOUNTER29) -DECLARE_CSR(hpmcounter30, CSR_HPMCOUNTER30) -DECLARE_CSR(hpmcounter31, CSR_HPMCOUNTER31) -DECLARE_CSR(sstatus, CSR_SSTATUS) -DECLARE_CSR(sie, CSR_SIE) -DECLARE_CSR(stvec, CSR_STVEC) -DECLARE_CSR(scounteren, CSR_SCOUNTEREN) -DECLARE_CSR(sscratch, CSR_SSCRATCH) -DECLARE_CSR(sepc, CSR_SEPC) -DECLARE_CSR(scause, CSR_SCAUSE) -DECLARE_CSR(stval, CSR_STVAL) -DECLARE_CSR(sip, CSR_SIP) -DECLARE_CSR(satp, CSR_SATP) -DECLARE_CSR(mstatus, CSR_MSTATUS) -DECLARE_CSR(misa, CSR_MISA) -DECLARE_CSR(medeleg, CSR_MEDELEG) -DECLARE_CSR(mideleg, CSR_MIDELEG) -DECLARE_CSR(mie, CSR_MIE) -DECLARE_CSR(mtvec, CSR_MTVEC) -DECLARE_CSR(mcounteren, CSR_MCOUNTEREN) -DECLARE_CSR(mscratch, CSR_MSCRATCH) -DECLARE_CSR(mepc, CSR_MEPC) -DECLARE_CSR(mcause, CSR_MCAUSE) -DECLARE_CSR(mtval, CSR_MTVAL) -DECLARE_CSR(mip, CSR_MIP) -DECLARE_CSR(pmpcfg0, CSR_PMPCFG0) -DECLARE_CSR(pmpcfg1, CSR_PMPCFG1) -DECLARE_CSR(pmpcfg2, CSR_PMPCFG2) -DECLARE_CSR(pmpcfg3, CSR_PMPCFG3) -DECLARE_CSR(pmpaddr0, CSR_PMPADDR0) -DECLARE_CSR(pmpaddr1, CSR_PMPADDR1) -DECLARE_CSR(pmpaddr2, CSR_PMPADDR2) -DECLARE_CSR(pmpaddr3, CSR_PMPADDR3) -DECLARE_CSR(pmpaddr4, CSR_PMPADDR4) -DECLARE_CSR(pmpaddr5, CSR_PMPADDR5) -DECLARE_CSR(pmpaddr6, CSR_PMPADDR6) -DECLARE_CSR(pmpaddr7, CSR_PMPADDR7) -DECLARE_CSR(pmpaddr8, CSR_PMPADDR8) -DECLARE_CSR(pmpaddr9, CSR_PMPADDR9) -DECLARE_CSR(pmpaddr10, CSR_PMPADDR10) -DECLARE_CSR(pmpaddr11, CSR_PMPADDR11) -DECLARE_CSR(pmpaddr12, CSR_PMPADDR12) -DECLARE_CSR(pmpaddr13, CSR_PMPADDR13) -DECLARE_CSR(pmpaddr14, CSR_PMPADDR14) -DECLARE_CSR(pmpaddr15, CSR_PMPADDR15) -DECLARE_CSR(tselect, CSR_TSELECT) -DECLARE_CSR(tdata1, CSR_TDATA1) -DECLARE_CSR(tdata2, CSR_TDATA2) -DECLARE_CSR(tdata3, CSR_TDATA3) -DECLARE_CSR(dcsr, CSR_DCSR) -DECLARE_CSR(dpc, CSR_DPC) -DECLARE_CSR(dscratch, CSR_DSCRATCH) -DECLARE_CSR(mcycle, CSR_MCYCLE) -DECLARE_CSR(minstret, CSR_MINSTRET) -DECLARE_CSR(mhpmcounter3, CSR_MHPMCOUNTER3) -DECLARE_CSR(mhpmcounter4, CSR_MHPMCOUNTER4) -DECLARE_CSR(mhpmcounter5, CSR_MHPMCOUNTER5) -DECLARE_CSR(mhpmcounter6, CSR_MHPMCOUNTER6) -DECLARE_CSR(mhpmcounter7, CSR_MHPMCOUNTER7) -DECLARE_CSR(mhpmcounter8, CSR_MHPMCOUNTER8) -DECLARE_CSR(mhpmcounter9, CSR_MHPMCOUNTER9) -DECLARE_CSR(mhpmcounter10, CSR_MHPMCOUNTER10) -DECLARE_CSR(mhpmcounter11, CSR_MHPMCOUNTER11) -DECLARE_CSR(mhpmcounter12, CSR_MHPMCOUNTER12) -DECLARE_CSR(mhpmcounter13, CSR_MHPMCOUNTER13) -DECLARE_CSR(mhpmcounter14, CSR_MHPMCOUNTER14) -DECLARE_CSR(mhpmcounter15, CSR_MHPMCOUNTER15) -DECLARE_CSR(mhpmcounter16, CSR_MHPMCOUNTER16) -DECLARE_CSR(mhpmcounter17, CSR_MHPMCOUNTER17) -DECLARE_CSR(mhpmcounter18, CSR_MHPMCOUNTER18) -DECLARE_CSR(mhpmcounter19, CSR_MHPMCOUNTER19) -DECLARE_CSR(mhpmcounter20, CSR_MHPMCOUNTER20) -DECLARE_CSR(mhpmcounter21, CSR_MHPMCOUNTER21) -DECLARE_CSR(mhpmcounter22, CSR_MHPMCOUNTER22) -DECLARE_CSR(mhpmcounter23, CSR_MHPMCOUNTER23) -DECLARE_CSR(mhpmcounter24, CSR_MHPMCOUNTER24) -DECLARE_CSR(mhpmcounter25, CSR_MHPMCOUNTER25) -DECLARE_CSR(mhpmcounter26, CSR_MHPMCOUNTER26) -DECLARE_CSR(mhpmcounter27, CSR_MHPMCOUNTER27) -DECLARE_CSR(mhpmcounter28, CSR_MHPMCOUNTER28) -DECLARE_CSR(mhpmcounter29, CSR_MHPMCOUNTER29) -DECLARE_CSR(mhpmcounter30, CSR_MHPMCOUNTER30) -DECLARE_CSR(mhpmcounter31, CSR_MHPMCOUNTER31) -DECLARE_CSR(mhpmevent3, CSR_MHPMEVENT3) -DECLARE_CSR(mhpmevent4, CSR_MHPMEVENT4) -DECLARE_CSR(mhpmevent5, CSR_MHPMEVENT5) -DECLARE_CSR(mhpmevent6, CSR_MHPMEVENT6) -DECLARE_CSR(mhpmevent7, CSR_MHPMEVENT7) -DECLARE_CSR(mhpmevent8, CSR_MHPMEVENT8) -DECLARE_CSR(mhpmevent9, CSR_MHPMEVENT9) -DECLARE_CSR(mhpmevent10, CSR_MHPMEVENT10) -DECLARE_CSR(mhpmevent11, CSR_MHPMEVENT11) -DECLARE_CSR(mhpmevent12, CSR_MHPMEVENT12) -DECLARE_CSR(mhpmevent13, CSR_MHPMEVENT13) -DECLARE_CSR(mhpmevent14, CSR_MHPMEVENT14) -DECLARE_CSR(mhpmevent15, CSR_MHPMEVENT15) -DECLARE_CSR(mhpmevent16, CSR_MHPMEVENT16) -DECLARE_CSR(mhpmevent17, CSR_MHPMEVENT17) -DECLARE_CSR(mhpmevent18, CSR_MHPMEVENT18) -DECLARE_CSR(mhpmevent19, CSR_MHPMEVENT19) -DECLARE_CSR(mhpmevent20, CSR_MHPMEVENT20) -DECLARE_CSR(mhpmevent21, CSR_MHPMEVENT21) -DECLARE_CSR(mhpmevent22, CSR_MHPMEVENT22) -DECLARE_CSR(mhpmevent23, CSR_MHPMEVENT23) -DECLARE_CSR(mhpmevent24, CSR_MHPMEVENT24) -DECLARE_CSR(mhpmevent25, CSR_MHPMEVENT25) -DECLARE_CSR(mhpmevent26, CSR_MHPMEVENT26) -DECLARE_CSR(mhpmevent27, CSR_MHPMEVENT27) -DECLARE_CSR(mhpmevent28, CSR_MHPMEVENT28) -DECLARE_CSR(mhpmevent29, CSR_MHPMEVENT29) -DECLARE_CSR(mhpmevent30, CSR_MHPMEVENT30) -DECLARE_CSR(mhpmevent31, CSR_MHPMEVENT31) -DECLARE_CSR(mvendorid, CSR_MVENDORID) -DECLARE_CSR(marchid, CSR_MARCHID) -DECLARE_CSR(mimpid, CSR_MIMPID) -DECLARE_CSR(mhartid, CSR_MHARTID) -DECLARE_CSR(cycleh, CSR_CYCLEH) -DECLARE_CSR(timeh, CSR_TIMEH) -DECLARE_CSR(instreth, CSR_INSTRETH) -DECLARE_CSR(hpmcounter3h, CSR_HPMCOUNTER3H) -DECLARE_CSR(hpmcounter4h, CSR_HPMCOUNTER4H) -DECLARE_CSR(hpmcounter5h, CSR_HPMCOUNTER5H) -DECLARE_CSR(hpmcounter6h, CSR_HPMCOUNTER6H) -DECLARE_CSR(hpmcounter7h, CSR_HPMCOUNTER7H) -DECLARE_CSR(hpmcounter8h, CSR_HPMCOUNTER8H) -DECLARE_CSR(hpmcounter9h, CSR_HPMCOUNTER9H) -DECLARE_CSR(hpmcounter10h, CSR_HPMCOUNTER10H) -DECLARE_CSR(hpmcounter11h, CSR_HPMCOUNTER11H) -DECLARE_CSR(hpmcounter12h, CSR_HPMCOUNTER12H) -DECLARE_CSR(hpmcounter13h, CSR_HPMCOUNTER13H) -DECLARE_CSR(hpmcounter14h, CSR_HPMCOUNTER14H) -DECLARE_CSR(hpmcounter15h, CSR_HPMCOUNTER15H) -DECLARE_CSR(hpmcounter16h, CSR_HPMCOUNTER16H) -DECLARE_CSR(hpmcounter17h, CSR_HPMCOUNTER17H) -DECLARE_CSR(hpmcounter18h, CSR_HPMCOUNTER18H) -DECLARE_CSR(hpmcounter19h, CSR_HPMCOUNTER19H) -DECLARE_CSR(hpmcounter20h, CSR_HPMCOUNTER20H) -DECLARE_CSR(hpmcounter21h, CSR_HPMCOUNTER21H) -DECLARE_CSR(hpmcounter22h, CSR_HPMCOUNTER22H) -DECLARE_CSR(hpmcounter23h, CSR_HPMCOUNTER23H) -DECLARE_CSR(hpmcounter24h, CSR_HPMCOUNTER24H) -DECLARE_CSR(hpmcounter25h, CSR_HPMCOUNTER25H) -DECLARE_CSR(hpmcounter26h, CSR_HPMCOUNTER26H) -DECLARE_CSR(hpmcounter27h, CSR_HPMCOUNTER27H) -DECLARE_CSR(hpmcounter28h, CSR_HPMCOUNTER28H) -DECLARE_CSR(hpmcounter29h, CSR_HPMCOUNTER29H) -DECLARE_CSR(hpmcounter30h, CSR_HPMCOUNTER30H) -DECLARE_CSR(hpmcounter31h, CSR_HPMCOUNTER31H) -DECLARE_CSR(mcycleh, CSR_MCYCLEH) -DECLARE_CSR(minstreth, CSR_MINSTRETH) -DECLARE_CSR(mhpmcounter3h, CSR_MHPMCOUNTER3H) -DECLARE_CSR(mhpmcounter4h, CSR_MHPMCOUNTER4H) -DECLARE_CSR(mhpmcounter5h, CSR_MHPMCOUNTER5H) -DECLARE_CSR(mhpmcounter6h, CSR_MHPMCOUNTER6H) -DECLARE_CSR(mhpmcounter7h, CSR_MHPMCOUNTER7H) -DECLARE_CSR(mhpmcounter8h, CSR_MHPMCOUNTER8H) -DECLARE_CSR(mhpmcounter9h, CSR_MHPMCOUNTER9H) -DECLARE_CSR(mhpmcounter10h, CSR_MHPMCOUNTER10H) -DECLARE_CSR(mhpmcounter11h, CSR_MHPMCOUNTER11H) -DECLARE_CSR(mhpmcounter12h, CSR_MHPMCOUNTER12H) -DECLARE_CSR(mhpmcounter13h, CSR_MHPMCOUNTER13H) -DECLARE_CSR(mhpmcounter14h, CSR_MHPMCOUNTER14H) -DECLARE_CSR(mhpmcounter15h, CSR_MHPMCOUNTER15H) -DECLARE_CSR(mhpmcounter16h, CSR_MHPMCOUNTER16H) -DECLARE_CSR(mhpmcounter17h, CSR_MHPMCOUNTER17H) -DECLARE_CSR(mhpmcounter18h, CSR_MHPMCOUNTER18H) -DECLARE_CSR(mhpmcounter19h, CSR_MHPMCOUNTER19H) -DECLARE_CSR(mhpmcounter20h, CSR_MHPMCOUNTER20H) -DECLARE_CSR(mhpmcounter21h, CSR_MHPMCOUNTER21H) -DECLARE_CSR(mhpmcounter22h, CSR_MHPMCOUNTER22H) -DECLARE_CSR(mhpmcounter23h, CSR_MHPMCOUNTER23H) -DECLARE_CSR(mhpmcounter24h, CSR_MHPMCOUNTER24H) -DECLARE_CSR(mhpmcounter25h, CSR_MHPMCOUNTER25H) -DECLARE_CSR(mhpmcounter26h, CSR_MHPMCOUNTER26H) -DECLARE_CSR(mhpmcounter27h, CSR_MHPMCOUNTER27H) -DECLARE_CSR(mhpmcounter28h, CSR_MHPMCOUNTER28H) -DECLARE_CSR(mhpmcounter29h, CSR_MHPMCOUNTER29H) -DECLARE_CSR(mhpmcounter30h, CSR_MHPMCOUNTER30H) -DECLARE_CSR(mhpmcounter31h, CSR_MHPMCOUNTER31H) -#endif -#ifdef DECLARE_CAUSE -DECLARE_CAUSE("misaligned fetch", CAUSE_MISALIGNED_FETCH) -DECLARE_CAUSE("fetch access", CAUSE_FETCH_ACCESS) -DECLARE_CAUSE("illegal instruction", CAUSE_ILLEGAL_INSTRUCTION) -DECLARE_CAUSE("breakpoint", CAUSE_BREAKPOINT) -DECLARE_CAUSE("misaligned load", CAUSE_MISALIGNED_LOAD) -DECLARE_CAUSE("load access", CAUSE_LOAD_ACCESS) -DECLARE_CAUSE("misaligned store", CAUSE_MISALIGNED_STORE) -DECLARE_CAUSE("store access", CAUSE_STORE_ACCESS) -DECLARE_CAUSE("user_ecall", CAUSE_USER_ECALL) -DECLARE_CAUSE("supervisor_ecall", CAUSE_SUPERVISOR_ECALL) -DECLARE_CAUSE("hypervisor_ecall", CAUSE_HYPERVISOR_ECALL) -DECLARE_CAUSE("machine_ecall", CAUSE_MACHINE_ECALL) -DECLARE_CAUSE("fetch page fault", CAUSE_FETCH_PAGE_FAULT) -DECLARE_CAUSE("load page fault", CAUSE_LOAD_PAGE_FAULT) -DECLARE_CAUSE("store page fault", CAUSE_STORE_PAGE_FAULT) -#endif diff --git a/riscv-pk/machine/fdt.c b/riscv-pk/machine/fdt.c deleted file mode 100644 index 061b19e..0000000 --- a/riscv-pk/machine/fdt.c +++ /dev/null @@ -1,740 +0,0 @@ -#include -#include -#include -#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 diff --git a/riscv-pk/machine/fdt.h b/riscv-pk/machine/fdt.h deleted file mode 100644 index d436778..0000000 --- a/riscv-pk/machine/fdt.h +++ /dev/null @@ -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 diff --git a/riscv-pk/machine/finisher.c b/riscv-pk/machine/finisher.c deleted file mode 100644 index d113096..0000000 --- a/riscv-pk/machine/finisher.c +++ /dev/null @@ -1,58 +0,0 @@ -#include -#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); -} diff --git a/riscv-pk/machine/finisher.h b/riscv-pk/machine/finisher.h deleted file mode 100644 index 60d10b4..0000000 --- a/riscv-pk/machine/finisher.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef _RISCV_FINISHER_H -#define _RISCV_FINISHER_H - -#include - -#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 diff --git a/riscv-pk/machine/flush_icache.c b/riscv-pk/machine/flush_icache.c deleted file mode 100644 index 45ba204..0000000 --- a/riscv-pk/machine/flush_icache.c +++ /dev/null @@ -1,3 +0,0 @@ -void __riscv_flush_icache(void) { - __asm__ volatile ("fence.i"); -} diff --git a/riscv-pk/machine/htif.c b/riscv-pk/machine/htif.c deleted file mode 100644 index 44ec2dd..0000000 --- a/riscv-pk/machine/htif.c +++ /dev/null @@ -1,138 +0,0 @@ -#include "htif.h" -#include "atomic.h" -#include "mtrap.h" -#include "fdt.h" -#include - -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); -} diff --git a/riscv-pk/machine/htif.h b/riscv-pk/machine/htif.h deleted file mode 100644 index a96bf60..0000000 --- a/riscv-pk/machine/htif.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef _RISCV_HTIF_H -#define _RISCV_HTIF_H - -#include - -#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 diff --git a/riscv-pk/machine/machine.ac b/riscv-pk/machine/machine.ac deleted file mode 100644 index 65acf04..0000000 --- a/riscv-pk/machine/machine.ac +++ /dev/null @@ -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]) -]) diff --git a/riscv-pk/machine/machine.mk.in b/riscv-pk/machine/machine.mk.in deleted file mode 100644 index 6745fdd..0000000 --- a/riscv-pk/machine/machine.mk.in +++ /dev/null @@ -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 \ diff --git a/riscv-pk/machine/mcall.h b/riscv-pk/machine/mcall.h deleted file mode 100644 index 6a2c037..0000000 --- a/riscv-pk/machine/mcall.h +++ /dev/null @@ -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 diff --git a/riscv-pk/machine/mentry.S b/riscv-pk/machine/mentry.S deleted file mode 100644 index 41d7017..0000000 --- a/riscv-pk/machine/mentry.S +++ /dev/null @@ -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 diff --git a/riscv-pk/machine/minit.c b/riscv-pk/machine/minit.c deleted file mode 100644 index ca6f43b..0000000 --- a/riscv-pk/machine/minit.c +++ /dev/null @@ -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 -#include - -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(); -} diff --git a/riscv-pk/machine/misaligned_ldst.c b/riscv-pk/machine/misaligned_ldst.c deleted file mode 100644 index 7b7425c..0000000 --- a/riscv-pk/machine/misaligned_ldst.c +++ /dev/null @@ -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); -} diff --git a/riscv-pk/machine/mtrap.c b/riscv-pk/machine/mtrap.c deleted file mode 100644 index 61d0c7b..0000000 --- a/riscv-pk/machine/mtrap.c +++ /dev/null @@ -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 -#include -#include - -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); - } -} diff --git a/riscv-pk/machine/mtrap.h b/riscv-pk/machine/mtrap.h deleted file mode 100644 index b439088..0000000 --- a/riscv-pk/machine/mtrap.h +++ /dev/null @@ -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 -#include -#include - -#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 diff --git a/riscv-pk/machine/muldiv_emulation.c b/riscv-pk/machine/muldiv_emulation.c deleted file mode 100644 index 1fde345..0000000 --- a/riscv-pk/machine/muldiv_emulation.c +++ /dev/null @@ -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 diff --git a/riscv-pk/machine/uart.c b/riscv-pk/machine/uart.c deleted file mode 100644 index 0645500..0000000 --- a/riscv-pk/machine/uart.c +++ /dev/null @@ -1,76 +0,0 @@ -#include -#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); -} diff --git a/riscv-pk/machine/uart.h b/riscv-pk/machine/uart.h deleted file mode 100644 index 51e951f..0000000 --- a/riscv-pk/machine/uart.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef _RISCV_UART_H -#define _RISCV_UART_H - -#include - -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 diff --git a/riscv-pk/machine/uart16550.c b/riscv-pk/machine/uart16550.c deleted file mode 100644 index 93fbb43..0000000 --- a/riscv-pk/machine/uart16550.c +++ /dev/null @@ -1,76 +0,0 @@ -#include -#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); -} diff --git a/riscv-pk/machine/uart16550.h b/riscv-pk/machine/uart16550.h deleted file mode 100644 index d7a0805..0000000 --- a/riscv-pk/machine/uart16550.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _RISCV_16550_H -#define _RISCV_16550_H - -#include - -extern volatile uint8_t* uart16550; - -void uart16550_putchar(uint8_t ch); -int uart16550_getchar(); -void query_uart16550(uintptr_t dtb); - -#endif diff --git a/riscv-pk/machine/unprivileged_memory.h b/riscv-pk/machine/unprivileged_memory.h deleted file mode 100644 index 5cf2727..0000000 --- a/riscv-pk/machine/unprivileged_memory.h +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef _RISCV_MISALIGNED_H -#define _RISCV_MISALIGNED_H - -#include "encoding.h" -#include "bits.h" -#include - -#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 diff --git a/riscv-pk/machine/vm.h b/riscv-pk/machine/vm.h deleted file mode 100644 index 986f301..0000000 --- a/riscv-pk/machine/vm.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef _VM_H -#define _VM_H - -#include "encoding.h" -#include - -#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 diff --git a/riscv-pk/scripts/config.guess b/riscv-pk/scripts/config.guess deleted file mode 100755 index f32079a..0000000 --- a/riscv-pk/scripts/config.guess +++ /dev/null @@ -1,1526 +0,0 @@ -#! /bin/sh -# Attempt to guess a canonical system name. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 -# Free Software Foundation, Inc. - -timestamp='2008-01-23' - -# This file is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA -# 02110-1301, USA. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - - -# Originally written by Per Bothner . -# Please send patches to . Submit a context -# diff and a properly formatted ChangeLog entry. -# -# This script attempts to guess a canonical system name similar to -# config.sub. If it succeeds, it prints the system name on stdout, and -# exits with 0. Otherwise, it exits with 1. -# -# The plan is that this can be called by configure scripts if you -# don't specify an explicit build system type. - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] - -Output the configuration name of the system \`$me' is run on. - -Operation modes: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.guess ($timestamp) - -Originally written by Per Bothner. -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, -2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" >&2 - exit 1 ;; - * ) - break ;; - esac -done - -if test $# != 0; then - echo "$me: too many arguments$help" >&2 - exit 1 -fi - -trap 'exit 1' 1 2 15 - -# CC_FOR_BUILD -- compiler used by this script. Note that the use of a -# compiler to aid in system detection is discouraged as it requires -# temporary files to be created and, as you can see below, it is a -# headache to deal with in a portable fashion. - -# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still -# use `HOST_CC' if defined, but it is deprecated. - -# Portable tmp directory creation inspired by the Autoconf team. - -set_cc_for_build=' -trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; -trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; -: ${TMPDIR=/tmp} ; - { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || - { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || - { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || - { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; -dummy=$tmp/dummy ; -tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; -case $CC_FOR_BUILD,$HOST_CC,$CC in - ,,) echo "int x;" > $dummy.c ; - for c in cc gcc c89 c99 ; do - if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then - CC_FOR_BUILD="$c"; break ; - fi ; - done ; - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found ; - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; -esac ; set_cc_for_build= ;' - -# This is needed to find uname on a Pyramid OSx when run in the BSD universe. -# (ghazi@noc.rutgers.edu 1994-08-24) -if (test -f /.attbin/uname) >/dev/null 2>&1 ; then - PATH=$PATH:/.attbin ; export PATH -fi - -UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown -UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown -UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown -UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown - -# Note: order is significant - the case branches are not exclusive. - -case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in - *:NetBSD:*:*) - # NetBSD (nbsd) targets should (where applicable) match one or - # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, - # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently - # switched to ELF, *-*-netbsd* would select the old - # object file format. This provides both forward - # compatibility and a consistent mechanism for selecting the - # object file format. - # - # Note: NetBSD doesn't particularly care about the vendor - # portion of the name. We always set it to "unknown". - sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ - /usr/sbin/$sysctl 2>/dev/null || echo unknown)` - case "${UNAME_MACHINE_ARCH}" in - armeb) machine=armeb-unknown ;; - arm*) machine=arm-unknown ;; - sh3el) machine=shl-unknown ;; - sh3eb) machine=sh-unknown ;; - sh5el) machine=sh5le-unknown ;; - *) machine=${UNAME_MACHINE_ARCH}-unknown ;; - esac - # The Operating System including object format, if it has switched - # to ELF recently, or will in the future. - case "${UNAME_MACHINE_ARCH}" in - arm*|i386|m68k|ns32k|sh3*|sparc|vax) - eval $set_cc_for_build - if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep __ELF__ >/dev/null - then - # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). - # Return netbsd for either. FIX? - os=netbsd - else - os=netbsdelf - fi - ;; - *) - os=netbsd - ;; - esac - # The OS release - # Debian GNU/NetBSD machines have a different userland, and - # thus, need a distinct triplet. However, they do not need - # kernel version information, so it can be replaced with a - # suitable tag, in the style of linux-gnu. - case "${UNAME_VERSION}" in - Debian*) - release='-gnu' - ;; - *) - release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` - ;; - esac - # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: - # contains redundant information, the shorter form: - # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}" - exit ;; - *:OpenBSD:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} - exit ;; - *:ekkoBSD:*:*) - echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} - exit ;; - *:SolidBSD:*:*) - echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} - exit ;; - macppc:MirBSD:*:*) - echo powerpc-unknown-mirbsd${UNAME_RELEASE} - exit ;; - *:MirBSD:*:*) - echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} - exit ;; - alpha:OSF1:*:*) - case $UNAME_RELEASE in - *4.0) - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` - ;; - *5.*) - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` - ;; - esac - # According to Compaq, /usr/sbin/psrinfo has been available on - # OSF/1 and Tru64 systems produced since 1995. I hope that - # covers most systems running today. This code pipes the CPU - # types through head -n 1, so we only detect the type of CPU 0. - ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` - case "$ALPHA_CPU_TYPE" in - "EV4 (21064)") - UNAME_MACHINE="alpha" ;; - "EV4.5 (21064)") - UNAME_MACHINE="alpha" ;; - "LCA4 (21066/21068)") - UNAME_MACHINE="alpha" ;; - "EV5 (21164)") - UNAME_MACHINE="alphaev5" ;; - "EV5.6 (21164A)") - UNAME_MACHINE="alphaev56" ;; - "EV5.6 (21164PC)") - UNAME_MACHINE="alphapca56" ;; - "EV5.7 (21164PC)") - UNAME_MACHINE="alphapca57" ;; - "EV6 (21264)") - UNAME_MACHINE="alphaev6" ;; - "EV6.7 (21264A)") - UNAME_MACHINE="alphaev67" ;; - "EV6.8CB (21264C)") - UNAME_MACHINE="alphaev68" ;; - "EV6.8AL (21264B)") - UNAME_MACHINE="alphaev68" ;; - "EV6.8CX (21264D)") - UNAME_MACHINE="alphaev68" ;; - "EV6.9A (21264/EV69A)") - UNAME_MACHINE="alphaev69" ;; - "EV7 (21364)") - UNAME_MACHINE="alphaev7" ;; - "EV7.9 (21364A)") - UNAME_MACHINE="alphaev79" ;; - esac - # A Pn.n version is a patched version. - # A Vn.n version is a released version. - # A Tn.n version is a released field test version. - # A Xn.n version is an unreleased experimental baselevel. - # 1.2 uses "1.2" for uname -r. - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - exit ;; - Alpha\ *:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # Should we change UNAME_MACHINE based on the output of uname instead - # of the specific Alpha model? - echo alpha-pc-interix - exit ;; - 21064:Windows_NT:50:3) - echo alpha-dec-winnt3.5 - exit ;; - Amiga*:UNIX_System_V:4.0:*) - echo m68k-unknown-sysv4 - exit ;; - *:[Aa]miga[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-amigaos - exit ;; - *:[Mm]orph[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-morphos - exit ;; - *:OS/390:*:*) - echo i370-ibm-openedition - exit ;; - *:z/VM:*:*) - echo s390-ibm-zvmoe - exit ;; - *:OS400:*:*) - echo powerpc-ibm-os400 - exit ;; - arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix${UNAME_RELEASE} - exit ;; - arm:riscos:*:*|arm:RISCOS:*:*) - echo arm-unknown-riscos - exit ;; - SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) - echo hppa1.1-hitachi-hiuxmpp - exit ;; - Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) - # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "`(/bin/universe) 2>/dev/null`" = att ; then - echo pyramid-pyramid-sysv3 - else - echo pyramid-pyramid-bsd - fi - exit ;; - NILE*:*:*:dcosx) - echo pyramid-pyramid-svr4 - exit ;; - DRS?6000:unix:4.0:6*) - echo sparc-icl-nx6 - exit ;; - DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) - case `/usr/bin/uname -p` in - sparc) echo sparc-icl-nx7; exit ;; - esac ;; - sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) - echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4*:SunOS:6*:*) - # According to config.sub, this is the proper way to canonicalize - # SunOS6. Hard to guess exactly what SunOS6 will be like, but - # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4*:SunOS:*:*) - case "`/usr/bin/arch -k`" in - Series*|S4*) - UNAME_RELEASE=`uname -v` - ;; - esac - # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` - exit ;; - sun3*:SunOS:*:*) - echo m68k-sun-sunos${UNAME_RELEASE} - exit ;; - sun*:*:4.2BSD:*) - UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 - case "`/bin/arch`" in - sun3) - echo m68k-sun-sunos${UNAME_RELEASE} - ;; - sun4) - echo sparc-sun-sunos${UNAME_RELEASE} - ;; - esac - exit ;; - aushp:SunOS:*:*) - echo sparc-auspex-sunos${UNAME_RELEASE} - exit ;; - # The situation for MiNT is a little confusing. The machine name - # can be virtually everything (everything which is not - # "atarist" or "atariste" at least should have a processor - # > m68000). The system name ranges from "MiNT" over "FreeMiNT" - # to the lowercase version "mint" (or "freemint"). Finally - # the system name "TOS" denotes a system which is actually not - # MiNT. But MiNT is downward compatible to TOS, so this should - # be no problem. - atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; - atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; - *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; - milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint${UNAME_RELEASE} - exit ;; - hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint${UNAME_RELEASE} - exit ;; - *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint${UNAME_RELEASE} - exit ;; - m68k:machten:*:*) - echo m68k-apple-machten${UNAME_RELEASE} - exit ;; - powerpc:machten:*:*) - echo powerpc-apple-machten${UNAME_RELEASE} - exit ;; - RISC*:Mach:*:*) - echo mips-dec-mach_bsd4.3 - exit ;; - RISC*:ULTRIX:*:*) - echo mips-dec-ultrix${UNAME_RELEASE} - exit ;; - VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix${UNAME_RELEASE} - exit ;; - 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix${UNAME_RELEASE} - exit ;; - mips:*:*:UMIPS | mips:*:*:RISCos) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c -#ifdef __cplusplus -#include /* for printf() prototype */ - int main (int argc, char *argv[]) { -#else - int main (argc, argv) int argc; char *argv[]; { -#endif - #if defined (host_mips) && defined (MIPSEB) - #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); - #endif - #endif - exit (-1); - } -EOF - $CC_FOR_BUILD -o $dummy $dummy.c && - dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && - SYSTEM_NAME=`$dummy $dummyarg` && - { echo "$SYSTEM_NAME"; exit; } - echo mips-mips-riscos${UNAME_RELEASE} - exit ;; - Motorola:PowerMAX_OS:*:*) - echo powerpc-motorola-powermax - exit ;; - Motorola:*:4.3:PL8-*) - echo powerpc-harris-powermax - exit ;; - Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) - echo powerpc-harris-powermax - exit ;; - Night_Hawk:Power_UNIX:*:*) - echo powerpc-harris-powerunix - exit ;; - m88k:CX/UX:7*:*) - echo m88k-harris-cxux7 - exit ;; - m88k:*:4*:R4*) - echo m88k-motorola-sysv4 - exit ;; - m88k:*:3*:R3*) - echo m88k-motorola-sysv3 - exit ;; - AViiON:dgux:*:*) - # DG/UX returns AViiON for all architectures - UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] - then - if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ - [ ${TARGET_BINARY_INTERFACE}x = x ] - then - echo m88k-dg-dgux${UNAME_RELEASE} - else - echo m88k-dg-dguxbcs${UNAME_RELEASE} - fi - else - echo i586-dg-dgux${UNAME_RELEASE} - fi - exit ;; - M88*:DolphinOS:*:*) # DolphinOS (SVR3) - echo m88k-dolphin-sysv3 - exit ;; - M88*:*:R3*:*) - # Delta 88k system running SVR3 - echo m88k-motorola-sysv3 - exit ;; - XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) - echo m88k-tektronix-sysv3 - exit ;; - Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) - echo m68k-tektronix-bsd - exit ;; - *:IRIX*:*:*) - echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` - exit ;; - ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. - echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' - i*86:AIX:*:*) - echo i386-ibm-aix - exit ;; - ia64:AIX:*:*) - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` - else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} - fi - echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} - exit ;; - *:AIX:2:3) - if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - - main() - { - if (!__power_pc()) - exit(1); - puts("powerpc-ibm-aix3.2.5"); - exit(0); - } -EOF - if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` - then - echo "$SYSTEM_NAME" - else - echo rs6000-ibm-aix3.2.5 - fi - elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then - echo rs6000-ibm-aix3.2.4 - else - echo rs6000-ibm-aix3.2 - fi - exit ;; - *:AIX:*:[456]) - IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` - if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then - IBM_ARCH=rs6000 - else - IBM_ARCH=powerpc - fi - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` - else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} - fi - echo ${IBM_ARCH}-ibm-aix${IBM_REV} - exit ;; - *:AIX:*:*) - echo rs6000-ibm-aix - exit ;; - ibmrt:4.4BSD:*|romp-ibm:BSD:*) - echo romp-ibm-bsd4.4 - exit ;; - ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to - exit ;; # report: romp-ibm BSD 4.3 - *:BOSX:*:*) - echo rs6000-bull-bosx - exit ;; - DPX/2?00:B.O.S.:*:*) - echo m68k-bull-sysv3 - exit ;; - 9000/[34]??:4.3bsd:1.*:*) - echo m68k-hp-bsd - exit ;; - hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) - echo m68k-hp-bsd4.4 - exit ;; - 9000/[34678]??:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - case "${UNAME_MACHINE}" in - 9000/31? ) HP_ARCH=m68000 ;; - 9000/[34]?? ) HP_ARCH=m68k ;; - 9000/[678][0-9][0-9]) - if [ -x /usr/bin/getconf ]; then - sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` - sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` - case "${sc_cpu_version}" in - 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 - 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 - 532) # CPU_PA_RISC2_0 - case "${sc_kernel_bits}" in - 32) HP_ARCH="hppa2.0n" ;; - 64) HP_ARCH="hppa2.0w" ;; - '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 - esac ;; - esac - fi - if [ "${HP_ARCH}" = "" ]; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - - #define _HPUX_SOURCE - #include - #include - - int main () - { - #if defined(_SC_KERNEL_BITS) - long bits = sysconf(_SC_KERNEL_BITS); - #endif - long cpu = sysconf (_SC_CPU_VERSION); - - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1"); break; - case CPU_PA_RISC2_0: - #if defined(_SC_KERNEL_BITS) - switch (bits) - { - case 64: puts ("hppa2.0w"); break; - case 32: puts ("hppa2.0n"); break; - default: puts ("hppa2.0"); break; - } break; - #else /* !defined(_SC_KERNEL_BITS) */ - puts ("hppa2.0"); break; - #endif - default: puts ("hppa1.0"); break; - } - exit (0); - } -EOF - (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` - test -z "$HP_ARCH" && HP_ARCH=hppa - fi ;; - esac - if [ ${HP_ARCH} = "hppa2.0w" ] - then - eval $set_cc_for_build - - # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating - # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler - # generating 64-bit code. GNU and HP use different nomenclature: - # - # $ CC_FOR_BUILD=cc ./config.guess - # => hppa2.0w-hp-hpux11.23 - # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess - # => hppa64-hp-hpux11.23 - - if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | - grep __LP64__ >/dev/null - then - HP_ARCH="hppa2.0w" - else - HP_ARCH="hppa64" - fi - fi - echo ${HP_ARCH}-hp-hpux${HPUX_REV} - exit ;; - ia64:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - echo ia64-hp-hpux${HPUX_REV} - exit ;; - 3050*:HI-UX:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - int - main () - { - long cpu = sysconf (_SC_CPU_VERSION); - /* The order matters, because CPU_IS_HP_MC68K erroneously returns - true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct - results, however. */ - if (CPU_IS_PA_RISC (cpu)) - { - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; - case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; - default: puts ("hppa-hitachi-hiuxwe2"); break; - } - } - else if (CPU_IS_HP_MC68K (cpu)) - puts ("m68k-hitachi-hiuxwe2"); - else puts ("unknown-hitachi-hiuxwe2"); - exit (0); - } -EOF - $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && - { echo "$SYSTEM_NAME"; exit; } - echo unknown-hitachi-hiuxwe2 - exit ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) - echo hppa1.1-hp-bsd - exit ;; - 9000/8??:4.3bsd:*:*) - echo hppa1.0-hp-bsd - exit ;; - *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) - echo hppa1.0-hp-mpeix - exit ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) - echo hppa1.1-hp-osf - exit ;; - hp8??:OSF1:*:*) - echo hppa1.0-hp-osf - exit ;; - i*86:OSF1:*:*) - if [ -x /usr/sbin/sysversion ] ; then - echo ${UNAME_MACHINE}-unknown-osf1mk - else - echo ${UNAME_MACHINE}-unknown-osf1 - fi - exit ;; - parisc*:Lites*:*:*) - echo hppa1.1-hp-lites - exit ;; - C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) - echo c1-convex-bsd - exit ;; - C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit ;; - C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) - echo c34-convex-bsd - exit ;; - C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) - echo c38-convex-bsd - exit ;; - C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) - echo c4-convex-bsd - exit ;; - CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*[A-Z]90:*:*:*) - echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ - | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ - -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ - -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*TS:*:*:*) - echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*SV1:*:*:*) - echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - *:UNICOS/mp:*:*) - echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` - echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; - 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` - echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; - i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} - exit ;; - sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi${UNAME_RELEASE} - exit ;; - *:BSD/OS:*:*) - echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} - exit ;; - *:FreeBSD:*:*) - case ${UNAME_MACHINE} in - pc98) - echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - amd64) - echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - *) - echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; - esac - exit ;; - i*:CYGWIN*:*) - echo ${UNAME_MACHINE}-pc-cygwin - exit ;; - *:MINGW*:*) - echo ${UNAME_MACHINE}-pc-mingw32 - exit ;; - i*:windows32*:*) - # uname -m includes "-pc" on this system. - echo ${UNAME_MACHINE}-mingw32 - exit ;; - i*:PW*:*) - echo ${UNAME_MACHINE}-pc-pw32 - exit ;; - *:Interix*:[3456]*) - case ${UNAME_MACHINE} in - x86) - echo i586-pc-interix${UNAME_RELEASE} - exit ;; - EM64T | authenticamd) - echo x86_64-unknown-interix${UNAME_RELEASE} - exit ;; - IA64) - echo ia64-unknown-interix${UNAME_RELEASE} - exit ;; - esac ;; - [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) - echo i${UNAME_MACHINE}-pc-mks - exit ;; - i*:Windows_NT*:* | Pentium*:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we - # UNAME_MACHINE based on the output of uname instead of i386? - echo i586-pc-interix - exit ;; - i*:UWIN*:*) - echo ${UNAME_MACHINE}-pc-uwin - exit ;; - amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) - echo x86_64-unknown-cygwin - exit ;; - p*:CYGWIN*:*) - echo powerpcle-unknown-cygwin - exit ;; - prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - *:GNU:*:*) - # the GNU system - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` - exit ;; - *:GNU/*:*:*) - # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu - exit ;; - i*86:Minix:*:*) - echo ${UNAME_MACHINE}-pc-minix - exit ;; - arm*:Linux:*:*) - eval $set_cc_for_build - if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep -q __ARM_EABI__ - then - echo ${UNAME_MACHINE}-unknown-linux-gnu - else - echo ${UNAME_MACHINE}-unknown-linux-gnueabi - fi - exit ;; - avr32*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - cris:Linux:*:*) - echo cris-axis-linux-gnu - exit ;; - crisv32:Linux:*:*) - echo crisv32-axis-linux-gnu - exit ;; - frv:Linux:*:*) - echo frv-unknown-linux-gnu - exit ;; - ia64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - m32r*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - m68*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - mips:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #undef CPU - #undef mips - #undef mipsel - #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=mipsel - #else - #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=mips - #else - CPU= - #endif - #endif -EOF - eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' - /^CPU/{ - s: ::g - p - }'`" - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } - ;; - mips64:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #undef CPU - #undef mips64 - #undef mips64el - #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=mips64el - #else - #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=mips64 - #else - CPU= - #endif - #endif -EOF - eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' - /^CPU/{ - s: ::g - p - }'`" - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } - ;; - or32:Linux:*:*) - echo or32-unknown-linux-gnu - exit ;; - ppc:Linux:*:*) - echo powerpc-unknown-linux-gnu - exit ;; - ppc64:Linux:*:*) - echo powerpc64-unknown-linux-gnu - exit ;; - alpha:Linux:*:*) - case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in - EV5) UNAME_MACHINE=alphaev5 ;; - EV56) UNAME_MACHINE=alphaev56 ;; - PCA56) UNAME_MACHINE=alphapca56 ;; - PCA57) UNAME_MACHINE=alphapca56 ;; - EV6) UNAME_MACHINE=alphaev6 ;; - EV67) UNAME_MACHINE=alphaev67 ;; - EV68*) UNAME_MACHINE=alphaev68 ;; - esac - objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null - if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi - echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} - exit ;; - parisc:Linux:*:* | hppa:Linux:*:*) - # Look for CPU level - case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-gnu ;; - PA8*) echo hppa2.0-unknown-linux-gnu ;; - *) echo hppa-unknown-linux-gnu ;; - esac - exit ;; - parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-gnu - exit ;; - s390:Linux:*:* | s390x:Linux:*:*) - echo ${UNAME_MACHINE}-ibm-linux - exit ;; - sh64*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - sh*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - vax:Linux:*:*) - echo ${UNAME_MACHINE}-dec-linux-gnu - exit ;; - x86_64:Linux:*:*) - echo x86_64-unknown-linux-gnu - exit ;; - xtensa*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - i*86:Linux:*:*) - # The BFD linker knows what the default object file format is, so - # first see if it will tell us. cd to the root directory to prevent - # problems with other programs or directories called `ld' in the path. - # Set LC_ALL=C to ensure ld outputs messages in English. - ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ - | sed -ne '/supported targets:/!d - s/[ ][ ]*/ /g - s/.*supported targets: *// - s/ .*// - p'` - case "$ld_supported_targets" in - elf32-i386) - TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" - ;; - a.out-i386-linux) - echo "${UNAME_MACHINE}-pc-linux-gnuaout" - exit ;; - coff-i386) - echo "${UNAME_MACHINE}-pc-linux-gnucoff" - exit ;; - "") - # Either a pre-BFD a.out linker (linux-gnuoldld) or - # one that does not give us useful --help. - echo "${UNAME_MACHINE}-pc-linux-gnuoldld" - exit ;; - esac - # Determine whether the default compiler is a.out or elf - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - #ifdef __ELF__ - # ifdef __GLIBC__ - # if __GLIBC__ >= 2 - LIBC=gnu - # else - LIBC=gnulibc1 - # endif - # else - LIBC=gnulibc1 - # endif - #else - #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) - LIBC=gnu - #else - LIBC=gnuaout - #endif - #endif - #ifdef __dietlibc__ - LIBC=dietlibc - #endif -EOF - eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' - /^LIBC/{ - s: ::g - p - }'`" - test x"${LIBC}" != x && { - echo "${UNAME_MACHINE}-pc-linux-${LIBC}" - exit - } - test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; } - ;; - i*86:DYNIX/ptx:4*:*) - # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. - # earlier versions are messed up and put the nodename in both - # sysname and nodename. - echo i386-sequent-sysv4 - exit ;; - i*86:UNIX_SV:4.2MP:2.*) - # Unixware is an offshoot of SVR4, but it has its own version - # number series starting with 2... - # I am not positive that other SVR4 systems won't match this, - # I just have to hope. -- rms. - # Use sysv4.2uw... so that sysv4* matches it. - echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} - exit ;; - i*86:OS/2:*:*) - # If we were able to find `uname', then EMX Unix compatibility - # is probably installed. - echo ${UNAME_MACHINE}-pc-os2-emx - exit ;; - i*86:XTS-300:*:STOP) - echo ${UNAME_MACHINE}-unknown-stop - exit ;; - i*86:atheos:*:*) - echo ${UNAME_MACHINE}-unknown-atheos - exit ;; - i*86:syllable:*:*) - echo ${UNAME_MACHINE}-pc-syllable - exit ;; - i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) - echo i386-unknown-lynxos${UNAME_RELEASE} - exit ;; - i*86:*DOS:*:*) - echo ${UNAME_MACHINE}-pc-msdosdjgpp - exit ;; - i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) - UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` - if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} - else - echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} - fi - exit ;; - i*86:*:5:[678]*) - # UnixWare 7.x, OpenUNIX and OpenServer 6. - case `/bin/uname -X | grep "^Machine"` in - *486*) UNAME_MACHINE=i486 ;; - *Pentium) UNAME_MACHINE=i586 ;; - *Pent*|*Celeron) UNAME_MACHINE=i686 ;; - esac - echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} - exit ;; - i*86:*:3.2:*) - if test -f /usr/options/cb.name; then - UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then - UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` - (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 - (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ - && UNAME_MACHINE=i586 - (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ - && UNAME_MACHINE=i686 - (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ - && UNAME_MACHINE=i686 - echo ${UNAME_MACHINE}-pc-sco$UNAME_REL - else - echo ${UNAME_MACHINE}-pc-sysv32 - fi - exit ;; - pc:*:*:*) - # Left here for compatibility: - # uname -m prints for DJGPP always 'pc', but it prints nothing about - # the processor, so we play safe by assuming i386. - echo i386-pc-msdosdjgpp - exit ;; - Intel:Mach:3*:*) - echo i386-pc-mach3 - exit ;; - paragon:*:*:*) - echo i860-intel-osf1 - exit ;; - i860:*:4.*:*) # i860-SVR4 - if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 - else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 - fi - exit ;; - mini*:CTIX:SYS*5:*) - # "miniframe" - echo m68010-convergent-sysv - exit ;; - mc68k:UNIX:SYSTEM5:3.51m) - echo m68k-convergent-sysv - exit ;; - M680?0:D-NIX:5.3:*) - echo m68k-diab-dnix - exit ;; - M68*:*:R3V[5678]*:*) - test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; - 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) - OS_REL='' - test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; - 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4; exit; } ;; - m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos${UNAME_RELEASE} - exit ;; - mc68030:UNIX_System_V:4.*:*) - echo m68k-atari-sysv4 - exit ;; - TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos${UNAME_RELEASE} - exit ;; - rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos${UNAME_RELEASE} - exit ;; - PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) - echo powerpc-unknown-lynxos${UNAME_RELEASE} - exit ;; - SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv${UNAME_RELEASE} - exit ;; - RM*:ReliantUNIX-*:*:*) - echo mips-sni-sysv4 - exit ;; - RM*:SINIX-*:*:*) - echo mips-sni-sysv4 - exit ;; - *:SINIX-*:*:*) - if uname -p 2>/dev/null >/dev/null ; then - UNAME_MACHINE=`(uname -p) 2>/dev/null` - echo ${UNAME_MACHINE}-sni-sysv4 - else - echo ns32k-sni-sysv - fi - exit ;; - PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort - # says - echo i586-unisys-sysv4 - exit ;; - *:UNIX_System_V:4*:FTX*) - # From Gerald Hewes . - # How about differentiating between stratus architectures? -djm - echo hppa1.1-stratus-sysv4 - exit ;; - *:*:*:FTX*) - # From seanf@swdc.stratus.com. - echo i860-stratus-sysv4 - exit ;; - i*86:VOS:*:*) - # From Paul.Green@stratus.com. - echo ${UNAME_MACHINE}-stratus-vos - exit ;; - *:VOS:*:*) - # From Paul.Green@stratus.com. - echo hppa1.1-stratus-vos - exit ;; - mc68*:A/UX:*:*) - echo m68k-apple-aux${UNAME_RELEASE} - exit ;; - news*:NEWS-OS:6*:*) - echo mips-sony-newsos6 - exit ;; - R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if [ -d /usr/nec ]; then - echo mips-nec-sysv${UNAME_RELEASE} - else - echo mips-unknown-sysv${UNAME_RELEASE} - fi - exit ;; - BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. - echo powerpc-be-beos - exit ;; - BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. - echo powerpc-apple-beos - exit ;; - BePC:BeOS:*:*) # BeOS running on Intel PC compatible. - echo i586-pc-beos - exit ;; - SX-4:SUPER-UX:*:*) - echo sx4-nec-superux${UNAME_RELEASE} - exit ;; - SX-5:SUPER-UX:*:*) - echo sx5-nec-superux${UNAME_RELEASE} - exit ;; - SX-6:SUPER-UX:*:*) - echo sx6-nec-superux${UNAME_RELEASE} - exit ;; - SX-7:SUPER-UX:*:*) - echo sx7-nec-superux${UNAME_RELEASE} - exit ;; - SX-8:SUPER-UX:*:*) - echo sx8-nec-superux${UNAME_RELEASE} - exit ;; - SX-8R:SUPER-UX:*:*) - echo sx8r-nec-superux${UNAME_RELEASE} - exit ;; - Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody${UNAME_RELEASE} - exit ;; - *:Rhapsody:*:*) - echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} - exit ;; - *:Darwin:*:*) - UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown - case $UNAME_PROCESSOR in - unknown) UNAME_PROCESSOR=powerpc ;; - esac - echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} - exit ;; - *:procnto*:*:* | *:QNX:[0123456789]*:*) - UNAME_PROCESSOR=`uname -p` - if test "$UNAME_PROCESSOR" = "x86"; then - UNAME_PROCESSOR=i386 - UNAME_MACHINE=pc - fi - echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} - exit ;; - *:QNX:*:4*) - echo i386-pc-qnx - exit ;; - NSE-?:NONSTOP_KERNEL:*:*) - echo nse-tandem-nsk${UNAME_RELEASE} - exit ;; - NSR-?:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk${UNAME_RELEASE} - exit ;; - *:NonStop-UX:*:*) - echo mips-compaq-nonstopux - exit ;; - BS2000:POSIX*:*:*) - echo bs2000-siemens-sysv - exit ;; - DS/*:UNIX_System_V:*:*) - echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} - exit ;; - *:Plan9:*:*) - # "uname -m" is not consistent, so use $cputype instead. 386 - # is converted to i386 for consistency with other x86 - # operating systems. - if test "$cputype" = "386"; then - UNAME_MACHINE=i386 - else - UNAME_MACHINE="$cputype" - fi - echo ${UNAME_MACHINE}-unknown-plan9 - exit ;; - *:TOPS-10:*:*) - echo pdp10-unknown-tops10 - exit ;; - *:TENEX:*:*) - echo pdp10-unknown-tenex - exit ;; - KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) - echo pdp10-dec-tops20 - exit ;; - XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) - echo pdp10-xkl-tops20 - exit ;; - *:TOPS-20:*:*) - echo pdp10-unknown-tops20 - exit ;; - *:ITS:*:*) - echo pdp10-unknown-its - exit ;; - SEI:*:*:SEIUX) - echo mips-sei-seiux${UNAME_RELEASE} - exit ;; - *:DragonFly:*:*) - echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` - exit ;; - *:*VMS:*:*) - UNAME_MACHINE=`(uname -p) 2>/dev/null` - case "${UNAME_MACHINE}" in - A*) echo alpha-dec-vms ; exit ;; - I*) echo ia64-dec-vms ; exit ;; - V*) echo vax-dec-vms ; exit ;; - esac ;; - *:XENIX:*:SysV) - echo i386-pc-xenix - exit ;; - i*86:skyos:*:*) - echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' - exit ;; - i*86:rdos:*:*) - echo ${UNAME_MACHINE}-pc-rdos - exit ;; -esac - -#echo '(No uname command or uname output not recognized.)' 1>&2 -#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 - -eval $set_cc_for_build -cat >$dummy.c < -# include -#endif -main () -{ -#if defined (sony) -#if defined (MIPSEB) - /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, - I don't know.... */ - printf ("mips-sony-bsd\n"); exit (0); -#else -#include - printf ("m68k-sony-newsos%s\n", -#ifdef NEWSOS4 - "4" -#else - "" -#endif - ); exit (0); -#endif -#endif - -#if defined (__arm) && defined (__acorn) && defined (__unix) - printf ("arm-acorn-riscix\n"); exit (0); -#endif - -#if defined (hp300) && !defined (hpux) - printf ("m68k-hp-bsd\n"); exit (0); -#endif - -#if defined (NeXT) -#if !defined (__ARCHITECTURE__) -#define __ARCHITECTURE__ "m68k" -#endif - int version; - version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; - if (version < 4) - printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); - else - printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); - exit (0); -#endif - -#if defined (MULTIMAX) || defined (n16) -#if defined (UMAXV) - printf ("ns32k-encore-sysv\n"); exit (0); -#else -#if defined (CMU) - printf ("ns32k-encore-mach\n"); exit (0); -#else - printf ("ns32k-encore-bsd\n"); exit (0); -#endif -#endif -#endif - -#if defined (__386BSD__) - printf ("i386-pc-bsd\n"); exit (0); -#endif - -#if defined (sequent) -#if defined (i386) - printf ("i386-sequent-dynix\n"); exit (0); -#endif -#if defined (ns32000) - printf ("ns32k-sequent-dynix\n"); exit (0); -#endif -#endif - -#if defined (_SEQUENT_) - struct utsname un; - - uname(&un); - - if (strncmp(un.version, "V2", 2) == 0) { - printf ("i386-sequent-ptx2\n"); exit (0); - } - if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ - printf ("i386-sequent-ptx1\n"); exit (0); - } - printf ("i386-sequent-ptx\n"); exit (0); - -#endif - -#if defined (vax) -# if !defined (ultrix) -# include -# if defined (BSD) -# if BSD == 43 - printf ("vax-dec-bsd4.3\n"); exit (0); -# else -# if BSD == 199006 - printf ("vax-dec-bsd4.3reno\n"); exit (0); -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# endif -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# else - printf ("vax-dec-ultrix\n"); exit (0); -# endif -#endif - -#if defined (alliant) && defined (i860) - printf ("i860-alliant-bsd\n"); exit (0); -#endif - - exit (1); -} -EOF - -$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && - { echo "$SYSTEM_NAME"; exit; } - -# Apollos put the system type in the environment. - -test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } - -# Convex versions that predate uname can use getsysinfo(1) - -if [ -x /usr/convex/getsysinfo ] -then - case `getsysinfo -f cpu_type` in - c1*) - echo c1-convex-bsd - exit ;; - c2*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit ;; - c34*) - echo c34-convex-bsd - exit ;; - c38*) - echo c38-convex-bsd - exit ;; - c4*) - echo c4-convex-bsd - exit ;; - esac -fi - -cat >&2 < in order to provide the needed -information to handle your system. - -config.guess timestamp = $timestamp - -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null` - -hostinfo = `(hostinfo) 2>/dev/null` -/bin/universe = `(/bin/universe) 2>/dev/null` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` -/bin/arch = `(/bin/arch) 2>/dev/null` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` - -UNAME_MACHINE = ${UNAME_MACHINE} -UNAME_RELEASE = ${UNAME_RELEASE} -UNAME_SYSTEM = ${UNAME_SYSTEM} -UNAME_VERSION = ${UNAME_VERSION} -EOF - -exit 1 - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/riscv-pk/scripts/config.sub b/riscv-pk/scripts/config.sub deleted file mode 100755 index 0d8be3d..0000000 --- a/riscv-pk/scripts/config.sub +++ /dev/null @@ -1,1662 +0,0 @@ -#! /bin/sh -# Configuration validation subroutine script. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 -# Free Software Foundation, Inc. - -timestamp='2010-06-17' - -# This file is (in principle) common to ALL GNU software. -# The presence of a machine in this file suggests that SOME GNU software -# can handle that machine. It does not imply ALL GNU software can. -# -# This file is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA -# 02110-1301, USA. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - - -# Please send patches to . Submit a context -# diff and a properly formatted ChangeLog entry. -# -# Configuration subroutine to validate and canonicalize a configuration type. -# Supply the specified configuration type as an argument. -# If it is invalid, we print an error message on stderr and exit with code 1. -# Otherwise, we print the canonical config type on stdout and succeed. - -# This file is supposed to be the same for all GNU packages -# and recognize all the CPU types, system types and aliases -# that are meaningful with *any* GNU software. -# Each package is responsible for reporting which valid configurations -# it does not support. The user should be able to distinguish -# a failure to support a valid configuration from a meaningless -# configuration. - -# The goal of this file is to map all the various variations of a given -# machine specification into a single specification in the form: -# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM -# or in some cases, the newer four-part form: -# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM -# It is wrong to echo any other type of specification. - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS - $0 [OPTION] ALIAS - -Canonicalize a configuration name. - -Operation modes: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.sub ($timestamp) - -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, -2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" - exit 1 ;; - - *local*) - # First pass through any local machine types. - echo $1 - exit ;; - - * ) - break ;; - esac -done - -case $# in - 0) echo "$me: missing argument$help" >&2 - exit 1;; - 1) ;; - *) echo "$me: too many arguments$help" >&2 - exit 1;; -esac - -# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). -# Here we must recognize all the valid KERNEL-OS combinations. -maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` -case $maybe_os in - nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \ - uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \ - storm-chaos* | os2-emx* | rtmk-nova*) - os=-$maybe_os - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` - ;; - *) - basic_machine=`echo $1 | sed 's/-[^-]*$//'` - if [ $basic_machine != $1 ] - then os=`echo $1 | sed 's/.*-/-/'` - else os=; fi - ;; -esac - -### Let's recognize common machines as not being operating systems so -### that things like config.sub decstation-3100 work. We also -### recognize some manufacturers as not being operating systems, so we -### can provide default operating systems below. -case $os in - -sun*os*) - # Prevent following clause from handling this invalid input. - ;; - -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ - -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ - -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ - -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ - -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ - -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ - -apple | -axis | -knuth | -cray) - os= - basic_machine=$1 - ;; - -sim | -cisco | -oki | -wec | -winbond) - os= - basic_machine=$1 - ;; - -scout) - ;; - -wrs) - os=-vxworks - basic_machine=$1 - ;; - -chorusos*) - os=-chorusos - basic_machine=$1 - ;; - -chorusrdb) - os=-chorusrdb - basic_machine=$1 - ;; - -hiux*) - os=-hiuxwe2 - ;; - -sco6) - os=-sco5v6 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco5) - os=-sco3.2v5 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco4) - os=-sco3.2v4 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2.[4-9]*) - os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2v[4-9]*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco5v6*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco*) - os=-sco3.2v2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -udk*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -isc) - os=-isc2.2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -clix*) - basic_machine=clipper-intergraph - ;; - -isc*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -lynx*) - os=-lynxos - ;; - -ptx*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` - ;; - -windowsnt*) - os=`echo $os | sed -e 's/windowsnt/winnt/'` - ;; - -psos*) - os=-psos - ;; - -mint | -mint[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; -esac - -# Decode aliases for certain CPU-COMPANY combinations. -case $basic_machine in - # Recognize the basic CPU types without company name. - # Some are omitted here because they have special meanings below. - 1750a | 580 \ - | a29k \ - | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ - | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ - | am33_2.0 \ - | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ - | bfin \ - | c4x | clipper \ - | d10v | d30v | dlx | dsp16xx \ - | fido | fr30 | frv \ - | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ - | i370 | i860 | i960 | ia64 \ - | ip2k | iq2000 \ - | m32c | m32r | m32rle | m68000 | m68k | m88k \ - | maxq | mb | microblaze | mcore | mep \ - | mips | mipsbe | mipseb | mipsel | mipsle \ - | mips16 \ - | mips64 | mips64el \ - | mips64vr | mips64vrel \ - | mips64orion | mips64orionel \ - | mips64vr4100 | mips64vr4100el \ - | mips64vr4300 | mips64vr4300el \ - | mips64vr5000 | mips64vr5000el \ - | mips64vr5900 | mips64vr5900el \ - | mipsisa32 | mipsisa32el \ - | mipsisa32r2 | mipsisa32r2el \ - | mipsisa64 | mipsisa64el \ - | mipsisa64r2 | mipsisa64r2el \ - | mipsisa64sb1 | mipsisa64sb1el \ - | mipsisa64sr71k | mipsisa64sr71kel \ - | mipstx39 | mipstx39el \ - | mn10200 | mn10300 \ - | mt \ - | msp430 \ - | nios | nios2 \ - | ns16k | ns32k \ - | or32 \ - | pdp10 | pdp11 | pj | pjl \ - | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ - | pyramid \ - | score \ - | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ - | sh64 | sh64le \ - | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ - | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ - | spu | strongarm \ - | tahoe | thumb | tic4x | tic80 | tron \ - | v850 | v850e \ - | we32k \ - | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ - | z8k) - basic_machine=$basic_machine-unknown - ;; - m6811 | m68hc11 | m6812 | m68hc12) - # Motorola 68HC11/12. - basic_machine=$basic_machine-unknown - os=-none - ;; - m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) - ;; - ms1) - basic_machine=mt-unknown - ;; - - riscv) - basic_machine=mipsriscvel-ucb - ;; - - # We use `pc' rather than `unknown' - # because (1) that's what they normally are, and - # (2) the word "unknown" tends to confuse beginning users. - i*86 | x86_64) - basic_machine=$basic_machine-pc - ;; - # Object if more than one company name word. - *-*-*) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; - # Recognize the basic CPU types with company name. - 580-* \ - | a29k-* \ - | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ - | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ - | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ - | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ - | avr-* | avr32-* \ - | bfin-* | bs2000-* \ - | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ - | clipper-* | craynv-* | cydra-* \ - | d10v-* | d30v-* | dlx-* \ - | elxsi-* \ - | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ - | h8300-* | h8500-* \ - | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ - | i*86-* | i860-* | i960-* | ia64-* \ - | ip2k-* | iq2000-* \ - | m32c-* | m32r-* | m32rle-* \ - | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* \ - | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ - | mips16-* \ - | mips64-* | mips64el-* \ - | mips64vr-* | mips64vrel-* \ - | mips64orion-* | mips64orionel-* \ - | mips64vr4100-* | mips64vr4100el-* \ - | mips64vr4300-* | mips64vr4300el-* \ - | mips64vr5000-* | mips64vr5000el-* \ - | mips64vr5900-* | mips64vr5900el-* \ - | mipsisa32-* | mipsisa32el-* \ - | mipsisa32r2-* | mipsisa32r2el-* \ - | mipsisa64-* | mipsisa64el-* \ - | mipsisa64r2-* | mipsisa64r2el-* \ - | mipsisa64sb1-* | mipsisa64sb1el-* \ - | mipsisa64sr71k-* | mipsisa64sr71kel-* \ - | mipstx39-* | mipstx39el-* \ - | mmix-* \ - | mt-* \ - | msp430-* \ - | nios-* | nios2-* \ - | none-* | np1-* | ns16k-* | ns32k-* \ - | orion-* \ - | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ - | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ - | pyramid-* \ - | romp-* | rs6000-* \ - | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ - | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ - | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ - | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \ - | tahoe-* | thumb-* \ - | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ - | tron-* \ - | v850-* | v850e-* | vax-* \ - | we32k-* \ - | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ - | xstormy16-* | xtensa*-* \ - | ymp-* \ - | z8k-*) - ;; - # Recognize the basic CPU types without company name, with glob match. - xtensa*) - basic_machine=$basic_machine-unknown - ;; - # Recognize the various machine names and aliases which stand - # for a CPU type and a company and sometimes even an OS. - 386bsd) - basic_machine=i386-unknown - os=-bsd - ;; - 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - basic_machine=m68000-att - ;; - 3b*) - basic_machine=we32k-att - ;; - a29khif) - basic_machine=a29k-amd - os=-udi - ;; - abacus) - basic_machine=abacus-unknown - ;; - adobe68k) - basic_machine=m68010-adobe - os=-scout - ;; - alliant | fx80) - basic_machine=fx80-alliant - ;; - altos | altos3068) - basic_machine=m68k-altos - ;; - am29k) - basic_machine=a29k-none - os=-bsd - ;; - amd64) - basic_machine=x86_64-pc - ;; - amd64-*) - basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - amdahl) - basic_machine=580-amdahl - os=-sysv - ;; - amiga | amiga-*) - basic_machine=m68k-unknown - ;; - amigaos | amigados) - basic_machine=m68k-unknown - os=-amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - os=-sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - os=-sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - os=-bsd - ;; - aux) - basic_machine=m68k-apple - os=-aux - ;; - balance) - basic_machine=ns32k-sequent - os=-dynix - ;; - blackfin) - basic_machine=bfin-unknown - os=-linux - ;; - blackfin-*) - basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - c90) - basic_machine=c90-cray - os=-unicos - ;; - convex-c1) - basic_machine=c1-convex - os=-bsd - ;; - convex-c2) - basic_machine=c2-convex - os=-bsd - ;; - convex-c32) - basic_machine=c32-convex - os=-bsd - ;; - convex-c34) - basic_machine=c34-convex - os=-bsd - ;; - convex-c38) - basic_machine=c38-convex - os=-bsd - ;; - cray | j90) - basic_machine=j90-cray - os=-unicos - ;; - craynv) - basic_machine=craynv-cray - os=-unicosmp - ;; - cr16) - basic_machine=cr16-unknown - os=-elf - ;; - crds | unos) - basic_machine=m68k-crds - ;; - crisv32 | crisv32-* | etraxfs*) - basic_machine=crisv32-axis - ;; - cris | cris-* | etrax*) - basic_machine=cris-axis - ;; - crx) - basic_machine=crx-unknown - os=-elf - ;; - da30 | da30-*) - basic_machine=m68k-da30 - ;; - decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) - basic_machine=mips-dec - ;; - decsystem10* | dec10*) - basic_machine=pdp10-dec - os=-tops10 - ;; - decsystem20* | dec20*) - basic_machine=pdp10-dec - os=-tops20 - ;; - delta | 3300 | motorola-3300 | motorola-delta \ - | 3300-motorola | delta-motorola) - basic_machine=m68k-motorola - ;; - delta88) - basic_machine=m88k-motorola - os=-sysv3 - ;; - djgpp) - basic_machine=i586-pc - os=-msdosdjgpp - ;; - dpx20 | dpx20-*) - basic_machine=rs6000-bull - os=-bosx - ;; - dpx2* | dpx2*-bull) - basic_machine=m68k-bull - os=-sysv3 - ;; - ebmon29k) - basic_machine=a29k-amd - os=-ebmon - ;; - elxsi) - basic_machine=elxsi-elxsi - os=-bsd - ;; - encore | umax | mmax) - basic_machine=ns32k-encore - ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - os=-ose - ;; - fx2800) - basic_machine=i860-alliant - ;; - genix) - basic_machine=ns32k-ns - ;; - gmicro) - basic_machine=tron-gmicro - os=-sysv - ;; - go32) - basic_machine=i386-pc - os=-go32 - ;; - h3050r* | hiux*) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - h8300hms) - basic_machine=h8300-hitachi - os=-hms - ;; - h8300xray) - basic_machine=h8300-hitachi - os=-xray - ;; - h8500hms) - basic_machine=h8500-hitachi - os=-hms - ;; - harris) - basic_machine=m88k-harris - os=-sysv3 - ;; - hp300-*) - basic_machine=m68k-hp - ;; - hp300bsd) - basic_machine=m68k-hp - os=-bsd - ;; - hp300hpux) - basic_machine=m68k-hp - os=-hpux - ;; - hp3k9[0-9][0-9] | hp9[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k2[0-9][0-9] | hp9k31[0-9]) - basic_machine=m68000-hp - ;; - hp9k3[2-9][0-9]) - basic_machine=m68k-hp - ;; - hp9k6[0-9][0-9] | hp6[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k7[0-79][0-9] | hp7[0-79][0-9]) - basic_machine=hppa1.1-hp - ;; - hp9k78[0-9] | hp78[0-9]) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][13679] | hp8[0-9][13679]) - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][0-9] | hp8[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hppa-next) - os=-nextstep3 - ;; - hppaosf) - basic_machine=hppa1.1-hp - os=-osf - ;; - hppro) - basic_machine=hppa1.1-hp - os=-proelf - ;; - i370-ibm* | ibm*) - basic_machine=i370-ibm - ;; -# I'm not sure what "Sysv32" means. Should this be sysv3.2? - i*86v32) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv32 - ;; - i*86v4*) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv4 - ;; - i*86v) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv - ;; - i*86sol2) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-solaris2 - ;; - i386mach) - basic_machine=i386-mach - os=-mach - ;; - i386-vsta | vsta) - basic_machine=i386-unknown - os=-vsta - ;; - iris | iris4d) - basic_machine=mips-sgi - case $os in - -irix*) - ;; - *) - os=-irix4 - ;; - esac - ;; - isi68 | isi) - basic_machine=m68k-isi - os=-sysv - ;; - m68knommu) - basic_machine=m68k-unknown - os=-linux - ;; - m68knommu-*) - basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - m88k-omron*) - basic_machine=m88k-omron - ;; - magnum | m3230) - basic_machine=mips-mips - os=-sysv - ;; - merlin) - basic_machine=ns32k-utek - os=-sysv - ;; - mingw32) - basic_machine=i386-pc - os=-mingw32 - ;; - mingw32ce) - basic_machine=arm-unknown - os=-mingw32ce - ;; - miniframe) - basic_machine=m68000-convergent - ;; - *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; - mips3*-*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` - ;; - mips3*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown - ;; - monitor) - basic_machine=m68k-rom68k - os=-coff - ;; - morphos) - basic_machine=powerpc-unknown - os=-morphos - ;; - msdos) - basic_machine=i386-pc - os=-msdos - ;; - ms1-*) - basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` - ;; - mvs) - basic_machine=i370-ibm - os=-mvs - ;; - ncr3000) - basic_machine=i486-ncr - os=-sysv4 - ;; - netbsd386) - basic_machine=i386-unknown - os=-netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - os=-linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - os=-newsos - ;; - news1000) - basic_machine=m68030-sony - os=-newsos - ;; - news-3600 | risc-news) - basic_machine=mips-sony - os=-newsos - ;; - necv70) - basic_machine=v70-nec - os=-sysv - ;; - next | m*-next ) - basic_machine=m68k-next - case $os in - -nextstep* ) - ;; - -ns2*) - os=-nextstep2 - ;; - *) - os=-nextstep3 - ;; - esac - ;; - nh3000) - basic_machine=m68k-harris - os=-cxux - ;; - nh[45]000) - basic_machine=m88k-harris - os=-cxux - ;; - nindy960) - basic_machine=i960-intel - os=-nindy - ;; - mon960) - basic_machine=i960-intel - os=-mon960 - ;; - nonstopux) - basic_machine=mips-compaq - os=-nonstopux - ;; - np1) - basic_machine=np1-gould - ;; - nsr-tandem) - basic_machine=nsr-tandem - ;; - op50n-* | op60c-*) - basic_machine=hppa1.1-oki - os=-proelf - ;; - openrisc | openrisc-*) - basic_machine=or32-unknown - ;; - os400) - basic_machine=powerpc-ibm - os=-os400 - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - os=-ose - ;; - os68k) - basic_machine=m68k-none - os=-os68k - ;; - pa-hitachi) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - paragon) - basic_machine=i860-intel - os=-osf - ;; - parisc) - basic_machine=hppa-unknown - os=-linux - ;; - parisc-*) - basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` - os=-linux - ;; - pbd) - basic_machine=sparc-tti - ;; - pbb) - basic_machine=m68k-tti - ;; - pc532 | pc532-*) - basic_machine=ns32k-pc532 - ;; - pc98) - basic_machine=i386-pc - ;; - pc98-*) - basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentium | p5 | k5 | k6 | nexgen | viac3) - basic_machine=i586-pc - ;; - pentiumpro | p6 | 6x86 | athlon | athlon_*) - basic_machine=i686-pc - ;; - pentiumii | pentium2 | pentiumiii | pentium3) - basic_machine=i686-pc - ;; - pentium4) - basic_machine=i786-pc - ;; - pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) - basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumpro-* | p6-* | 6x86-* | athlon-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentium4-*) - basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pn) - basic_machine=pn-gould - ;; - power) basic_machine=power-ibm - ;; - ppc) basic_machine=powerpc-unknown - ;; - ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppcle | powerpclittle | ppc-le | powerpc-little) - basic_machine=powerpcle-unknown - ;; - ppcle-* | powerpclittle-*) - basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64) basic_machine=powerpc64-unknown - ;; - ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64le | powerpc64little | ppc64-le | powerpc64-little) - basic_machine=powerpc64le-unknown - ;; - ppc64le-* | powerpc64little-*) - basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ps2) - basic_machine=i386-ibm - ;; - pw32) - basic_machine=i586-unknown - os=-pw32 - ;; - rdos) - basic_machine=i386-pc - os=-rdos - ;; - rom68k) - basic_machine=m68k-rom68k - os=-coff - ;; - rm[46]00) - basic_machine=mips-siemens - ;; - rtpc | rtpc-*) - basic_machine=romp-ibm - ;; - s390 | s390-*) - basic_machine=s390-ibm - ;; - s390x | s390x-*) - basic_machine=s390x-ibm - ;; - sa29200) - basic_machine=a29k-amd - os=-udi - ;; - sb1) - basic_machine=mipsisa64sb1-unknown - ;; - sb1el) - basic_machine=mipsisa64sb1el-unknown - ;; - sde) - basic_machine=mipsisa32-sde - os=-elf - ;; - sei) - basic_machine=mips-sei - os=-seiux - ;; - sequent) - basic_machine=i386-sequent - ;; - sh) - basic_machine=sh-hitachi - os=-hms - ;; - sh5el) - basic_machine=sh5le-unknown - ;; - sh64) - basic_machine=sh64-unknown - ;; - sparclite-wrs | simso-wrs) - basic_machine=sparclite-wrs - os=-vxworks - ;; - sps7) - basic_machine=m68k-bull - os=-sysv2 - ;; - spur) - basic_machine=spur-unknown - ;; - st2000) - basic_machine=m68k-tandem - ;; - stratus) - basic_machine=i860-stratus - os=-sysv4 - ;; - sun2) - basic_machine=m68000-sun - ;; - sun2os3) - basic_machine=m68000-sun - os=-sunos3 - ;; - sun2os4) - basic_machine=m68000-sun - os=-sunos4 - ;; - sun3os3) - basic_machine=m68k-sun - os=-sunos3 - ;; - sun3os4) - basic_machine=m68k-sun - os=-sunos4 - ;; - sun4os3) - basic_machine=sparc-sun - os=-sunos3 - ;; - sun4os4) - basic_machine=sparc-sun - os=-sunos4 - ;; - sun4sol2) - basic_machine=sparc-sun - os=-solaris2 - ;; - sun3 | sun3-*) - basic_machine=m68k-sun - ;; - sun4) - basic_machine=sparc-sun - ;; - sun386 | sun386i | roadrunner) - basic_machine=i386-sun - ;; - sv1) - basic_machine=sv1-cray - os=-unicos - ;; - symmetry) - basic_machine=i386-sequent - os=-dynix - ;; - t3e) - basic_machine=alphaev5-cray - os=-unicos - ;; - t90) - basic_machine=t90-cray - os=-unicos - ;; - tic54x | c54x*) - basic_machine=tic54x-unknown - os=-coff - ;; - tic55x | c55x*) - basic_machine=tic55x-unknown - os=-coff - ;; - tic6x | c6x*) - basic_machine=tic6x-unknown - os=-coff - ;; - tile*) - basic_machine=tile-unknown - os=-linux-gnu - ;; - tx39) - basic_machine=mipstx39-unknown - ;; - tx39el) - basic_machine=mipstx39el-unknown - ;; - toad1) - basic_machine=pdp10-xkl - os=-tops20 - ;; - tower | tower-32) - basic_machine=m68k-ncr - ;; - tpf) - basic_machine=s390x-ibm - os=-tpf - ;; - udi29k) - basic_machine=a29k-amd - os=-udi - ;; - ultra3) - basic_machine=a29k-nyu - os=-sym1 - ;; - v810 | necv810) - basic_machine=v810-nec - os=-none - ;; - vaxv) - basic_machine=vax-dec - os=-sysv - ;; - vms) - basic_machine=vax-dec - os=-vms - ;; - vpp*|vx|vx-*) - basic_machine=f301-fujitsu - ;; - vxworks960) - basic_machine=i960-wrs - os=-vxworks - ;; - vxworks68) - basic_machine=m68k-wrs - os=-vxworks - ;; - vxworks29k) - basic_machine=a29k-wrs - os=-vxworks - ;; - w65*) - basic_machine=w65-wdc - os=-none - ;; - w89k-*) - basic_machine=hppa1.1-winbond - os=-proelf - ;; - xbox) - basic_machine=i686-pc - os=-mingw32 - ;; - xps | xps100) - basic_machine=xps100-honeywell - ;; - ymp) - basic_machine=ymp-cray - os=-unicos - ;; - z8k-*-coff) - basic_machine=z8k-unknown - os=-sim - ;; - none) - basic_machine=none-none - os=-none - ;; - -# Here we handle the default manufacturer of certain CPU types. It is in -# some cases the only manufacturer, in others, it is the most popular. - w89k) - basic_machine=hppa1.1-winbond - ;; - op50n) - basic_machine=hppa1.1-oki - ;; - op60c) - basic_machine=hppa1.1-oki - ;; - romp) - basic_machine=romp-ibm - ;; - mmix) - basic_machine=mmix-knuth - ;; - rs6000) - basic_machine=rs6000-ibm - ;; - vax) - basic_machine=vax-dec - ;; - pdp10) - # there are many clones, so DEC is not a safe bet - basic_machine=pdp10-unknown - ;; - pdp11) - basic_machine=pdp11-dec - ;; - we32k) - basic_machine=we32k-att - ;; - sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele) - basic_machine=sh-unknown - ;; - sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) - basic_machine=sparc-sun - ;; - cydra) - basic_machine=cydra-cydrome - ;; - orion) - basic_machine=orion-highlevel - ;; - orion105) - basic_machine=clipper-highlevel - ;; - mac | mpw | mac-mpw) - basic_machine=m68k-apple - ;; - pmac | pmac-mpw) - basic_machine=powerpc-apple - ;; - *-unknown) - # Make sure to match an already-canonicalized machine name. - ;; - *) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; -esac - -# Here we canonicalize certain aliases for manufacturers. -case $basic_machine in - *-digital*) - basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` - ;; - *-commodore*) - basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` - ;; - *) - ;; -esac - -# Decode manufacturer-specific aliases for certain operating systems. - -if [ x"$os" != x"" ] -then -case $os in - # First match some system type aliases - # that might get confused with valid system types. - # -solaris* is a basic system type, with this one exception. - -solaris1 | -solaris1.*) - os=`echo $os | sed -e 's|solaris1|sunos4|'` - ;; - -solaris) - os=-solaris2 - ;; - -svr4*) - os=-sysv4 - ;; - -unixware*) - os=-sysv4.2uw - ;; - -gnu/linux*) - os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` - ;; - # First accept the basic system types. - # The portable systems comes first. - # Each alternative MUST END IN A *, to match a version number. - # -sysv* is not here because it comes later, after sysvr4. - -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ - | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ - | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ - | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ - | -aos* \ - | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ - | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ - | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ - | -openbsd* | -solidbsd* \ - | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ - | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ - | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ - | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ - | -chorusos* | -chorusrdb* \ - | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \ - | -uxpv* | -beos* | -mpeix* | -udk* \ - | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ - | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ - | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ - | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ - | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ - | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku* | -rdos* | -toppers* | -drops*) - # Remember, each alternative MUST END IN *, to match a version number. - ;; - -qnx*) - case $basic_machine in - x86-* | i*86-*) - ;; - *) - os=-nto$os - ;; - esac - ;; - -nto-qnx*) - ;; - -nto*) - os=`echo $os | sed -e 's|nto|nto-qnx|'` - ;; - -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ - | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ - | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) - ;; - -mac*) - os=`echo $os | sed -e 's|mac|macos|'` - ;; - -linux-dietlibc) - os=-linux-dietlibc - ;; - -linux*) - os=`echo $os | sed -e 's|linux|linux-gnu|'` - ;; - -sunos5*) - os=`echo $os | sed -e 's|sunos5|solaris2|'` - ;; - -sunos6*) - os=`echo $os | sed -e 's|sunos6|solaris3|'` - ;; - -opened*) - os=-openedition - ;; - -os400*) - os=-os400 - ;; - -wince*) - os=-wince - ;; - -osfrose*) - os=-osfrose - ;; - -osf*) - os=-osf - ;; - -utek*) - os=-bsd - ;; - -dynix*) - os=-bsd - ;; - -acis*) - os=-aos - ;; - -atheos*) - os=-atheos - ;; - -syllable*) - os=-syllable - ;; - -386bsd) - os=-bsd - ;; - -ctix* | -uts*) - os=-sysv - ;; - -nova*) - os=-rtmk-nova - ;; - -ns2 ) - os=-nextstep2 - ;; - -nsk*) - os=-nsk - ;; - # Preserve the version number of sinix5. - -sinix5.*) - os=`echo $os | sed -e 's|sinix|sysv|'` - ;; - -sinix*) - os=-sysv4 - ;; - -tpf*) - os=-tpf - ;; - -triton*) - os=-sysv3 - ;; - -oss*) - os=-sysv3 - ;; - -svr4) - os=-sysv4 - ;; - -svr3) - os=-sysv3 - ;; - -sysvr4) - os=-sysv4 - ;; - # This must come after -sysvr4. - -sysv*) - ;; - -ose*) - os=-ose - ;; - -es1800*) - os=-ose - ;; - -xenix) - os=-xenix - ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - os=-mint - ;; - -aros*) - os=-aros - ;; - -kaos*) - os=-kaos - ;; - -zvmoe) - os=-zvmoe - ;; - -none) - ;; - *) - # Get rid of the `-' at the beginning of $os. - os=`echo $os | sed 's/[^-]*-//'` - echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 - exit 1 - ;; -esac -else - -# Here we handle the default operating systems that come with various machines. -# The value should be what the vendor currently ships out the door with their -# machine or put another way, the most popular os provided with the machine. - -# Note that if you're going to try to match "-MANUFACTURER" here (say, -# "-sun"), then you have to tell the case statement up towards the top -# that MANUFACTURER isn't an operating system. Otherwise, code above -# will signal an error saying that MANUFACTURER isn't an operating -# system, and we'll never get to this point. - -case $basic_machine in - score-*) - os=-elf - ;; - spu-*) - os=-elf - ;; - *-acorn) - os=-riscix1.2 - ;; - arm*-rebel) - os=-linux - ;; - arm*-semi) - os=-aout - ;; - c4x-* | tic4x-*) - os=-coff - ;; - # This must come before the *-dec entry. - pdp10-*) - os=-tops20 - ;; - pdp11-*) - os=-none - ;; - *-dec | vax-*) - os=-ultrix4.2 - ;; - m68*-apollo) - os=-domain - ;; - i386-sun) - os=-sunos4.0.2 - ;; - m68000-sun) - os=-sunos3 - # This also exists in the configure program, but was not the - # default. - # os=-sunos4 - ;; - m68*-cisco) - os=-aout - ;; - mep-*) - os=-elf - ;; - mips*-cisco) - os=-elf - ;; - mips*-*) - os=-elf - ;; - or32-*) - os=-coff - ;; - *-tti) # must be before sparc entry or we get the wrong os. - os=-sysv3 - ;; - sparc-* | *-sun) - os=-sunos4.1.1 - ;; - *-be) - os=-beos - ;; - *-haiku) - os=-haiku - ;; - *-ibm) - os=-aix - ;; - *-knuth) - os=-mmixware - ;; - *-wec) - os=-proelf - ;; - *-winbond) - os=-proelf - ;; - *-oki) - os=-proelf - ;; - *-hp) - os=-hpux - ;; - *-hitachi) - os=-hiux - ;; - i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) - os=-sysv - ;; - *-cbm) - os=-amigaos - ;; - *-dg) - os=-dgux - ;; - *-dolphin) - os=-sysv3 - ;; - m68k-ccur) - os=-rtu - ;; - m88k-omron*) - os=-luna - ;; - *-next ) - os=-nextstep - ;; - *-sequent) - os=-ptx - ;; - *-crds) - os=-unos - ;; - *-ns) - os=-genix - ;; - i370-*) - os=-mvs - ;; - *-next) - os=-nextstep3 - ;; - *-gould) - os=-sysv - ;; - *-highlevel) - os=-bsd - ;; - *-encore) - os=-bsd - ;; - *-sgi) - os=-irix - ;; - *-siemens) - os=-sysv4 - ;; - *-masscomp) - os=-rtu - ;; - f30[01]-fujitsu | f700-fujitsu) - os=-uxpv - ;; - *-rom68k) - os=-coff - ;; - *-*bug) - os=-coff - ;; - *-apple) - os=-macos - ;; - *-atari*) - os=-mint - ;; - *) - os=-none - ;; -esac -fi - -# Here we handle the case where we know the os, and the CPU type, but not the -# manufacturer. We pick the logical manufacturer. -vendor=unknown -case $basic_machine in - *-unknown) - case $os in - -riscix*) - vendor=acorn - ;; - -sunos*) - vendor=sun - ;; - -aix*) - vendor=ibm - ;; - -beos*) - vendor=be - ;; - -hpux*) - vendor=hp - ;; - -mpeix*) - vendor=hp - ;; - -hiux*) - vendor=hitachi - ;; - -unos*) - vendor=crds - ;; - -dgux*) - vendor=dg - ;; - -luna*) - vendor=omron - ;; - -genix*) - vendor=ns - ;; - -mvs* | -opened*) - vendor=ibm - ;; - -os400*) - vendor=ibm - ;; - -ptx*) - vendor=sequent - ;; - -tpf*) - vendor=ibm - ;; - -vxsim* | -vxworks* | -windiss*) - vendor=wrs - ;; - -aux*) - vendor=apple - ;; - -hms*) - vendor=hitachi - ;; - -mpw* | -macos*) - vendor=apple - ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - vendor=atari - ;; - -vos*) - vendor=stratus - ;; - esac - basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` - ;; -esac - -echo $basic_machine$os -exit - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/riscv-pk/scripts/install.sh b/riscv-pk/scripts/install.sh deleted file mode 100755 index 89fc9b0..0000000 --- a/riscv-pk/scripts/install.sh +++ /dev/null @@ -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 diff --git a/riscv-pk/scripts/mk-install-dirs.sh b/riscv-pk/scripts/mk-install-dirs.sh deleted file mode 100755 index 644b5f7..0000000 --- a/riscv-pk/scripts/mk-install-dirs.sh +++ /dev/null @@ -1,40 +0,0 @@ -#! /bin/sh -# mkinstalldirs --- make directory hierarchy -# Author: Noah Friedman -# 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 diff --git a/riscv-pk/scripts/vcs-version.sh b/riscv-pk/scripts/vcs-version.sh deleted file mode 100755 index 31fae86..0000000 --- a/riscv-pk/scripts/vcs-version.sh +++ /dev/null @@ -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; - diff --git a/riscv-pk/util/snprintf.c b/riscv-pk/util/snprintf.c deleted file mode 100644 index 1544a6c..0000000 --- a/riscv-pk/util/snprintf.c +++ /dev/null @@ -1,98 +0,0 @@ -// See LICENSE for license details. - -#include -#include -#include -#include - -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; -} diff --git a/riscv-pk/util/string.c b/riscv-pk/util/string.c deleted file mode 100644 index 41855c2..0000000 --- a/riscv-pk/util/string.c +++ /dev/null @@ -1,90 +0,0 @@ -#include -#include -#include - -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; -} diff --git a/riscv-pk/util/util.ac b/riscv-pk/util/util.ac deleted file mode 100644 index e69de29..0000000 diff --git a/riscv-pk/util/util.mk.in b/riscv-pk/util/util.mk.in deleted file mode 100644 index abbdbd1..0000000 --- a/riscv-pk/util/util.mk.in +++ /dev/null @@ -1,9 +0,0 @@ -util_subproject_deps = \ - -util_hdrs = \ - -util_c_srcs = \ - snprintf.c \ - string.c \ - -util_asm_srcs = \ From b3a80ba57178034aa71b6e008c3fee8ea4a9ddae Mon Sep 17 00:00:00 2001 From: WangRunji Date: Thu, 29 Nov 2018 00:20:12 +0800 Subject: [PATCH 22/39] update riscv-pk & README --- README.md | 2 +- riscv-pk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5724e44..6ebf56f 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ cargo install cargo-xbuild bootimage ``` ```bash -git clone https://github.com/wangrunji0408/RustOS.git +git clone https://github.com/wangrunji0408/RustOS.git --recursive cd RustOS/kernel rustup override set nightly make run arch=riscv32|x86_64|aarch64 diff --git a/riscv-pk b/riscv-pk index e125938..98d2ffb 160000 --- a/riscv-pk +++ b/riscv-pk @@ -1 +1 @@ -Subproject commit e12593841af47b6b15c7de63804342ef87f271ce +Subproject commit 98d2ffbb3203e1d9a936b067c51f8522a5ff9677 From a74b893bd2eafc5c32a2c347131f939bd18c4636 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Thu, 29 Nov 2018 21:39:43 +0800 Subject: [PATCH 23/39] update Makefile --- kernel/Makefile | 55 +++++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/kernel/Makefile b/kernel/Makefile index 041ea02..1deeeac 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -12,73 +12,71 @@ # d = int | in_asm | ... QEMU debug info # mode = debug | release # LOG = off | error | warn | info | debug | trace -# smp SMP core number +# smp = 1 | 2 | ... SMP core number # board = fpga Only available on riscv32, build without bbl, run on board # | raspi3 Only available on aarch64, run on Raspberry Pi 3 Model B/B+ +# nommu Only available on riscv32, build for M-Mode, without MMU arch ?= riscv32 board ?= raspi3 mode ?= debug LOG ?= debug smp ?= 4 +nommu ?= target := $(arch)-blog_os kernel := target/$(target)/$(mode)/ucore bin := target/$(target)/$(mode)/kernel.bin bootimage := target/$(target)/bootimage.bin -user_bin_path := ../user/target/$(arch)-ucore/debug -user_bins := $(patsubst $(user_bin_path)/%.d, $(user_bin_path)/%, $(wildcard $(user_bin_path)/*.d)) -user_obj := build/$(arch)/user.o -SFSIMG := ../user/img/ucore-i386-pic.img - ### qemu options ### -ifeq ($(arch), x86_64) qemu_opts := \ - -drive format=raw,file=$(bootimage) \ - -drive format=raw,file=$(SFSIMG),media=disk,cache=writeback \ -smp cores=$(smp) \ - -serial mon:stdio \ - -device isa-debug-exit \ -nographic + +ifeq ($(arch), x86_64) +qemu_opts += \ + -drive format=raw,file=$(bootimage) \ + -drive format=raw,file="../user/img/ucore-i386-pic.img",media=disk,cache=writeback \ + -serial mon:stdio \ + -device isa-debug-exit + else ifeq ($(arch), riscv32) -qemu_opts := \ +qemu_opts += \ -machine virt \ - -kernel $(bin) \ - -nographic \ - -smp cores=$(smp) + -kernel $(bin) ifdef nommu -qemu_opts := $(qemu_opts) -cpu rv32imacu-nommu +qemu_opts += -cpu rv32imacu-nommu endif + else ifeq ($(arch), aarch64) -qemu_opts := \ +qemu_opts += \ -machine $(board) \ -serial null -serial mon:stdio \ - -nographic \ - -kernel $(bin) \ - -smp cores=$(smp) + -kernel $(bin) endif ifdef d -qemu_opts := $(qemu_opts) -d $(d) +qemu_opts += -d $(d) endif ### build args ### ifeq ($(arch), riscv32) ifeq ($(board), fpga) -features := $(features) no_bbl +features += no_bbl endif endif ifdef nommu -features := $(features) no_mmu +features += no_mmu +bbl_m_mode := --enable-boot-machine endif -features := $(features) board_$(board) +features += board_$(board) build_args := --target $(target).json --features "$(features)" ifeq ($(mode), release) -build_args := $(build_args) --release +build_args += --release endif @@ -146,6 +144,7 @@ else mkdir -p build && \ cd build && \ ../configure \ + $(bbl_m_mode) \ --with-arch=rv32imac \ --disable-fp-emulation \ --host=riscv64-unknown-elf \ @@ -167,12 +166,6 @@ else @CC=$(cc) cargo xbuild $(build_args) endif -# make user.o from binary files -$(user_obj): $(user_bins) - @cd $(user_bin_path) && \ - $(ld) -o $(abspath $@) $(patsubst %, -b binary %, $(notdir $(user_bins))) - - ### install ### ifeq ($(board), raspi3) From 2f8cfabbcac949e464963727e9c71f671717d940 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Thu, 29 Nov 2018 22:00:35 +0800 Subject: [PATCH 24/39] remove redundant nightly features --- kernel/src/arch/x86_64/driver/vga.rs | 10 ++-------- kernel/src/lib.rs | 6 ------ 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/kernel/src/arch/x86_64/driver/vga.rs b/kernel/src/arch/x86_64/driver/vga.rs index a1ea621..b4f096b 100644 --- a/kernel/src/arch/x86_64/driver/vga.rs +++ b/kernel/src/arch/x86_64/driver/vga.rs @@ -1,15 +1,10 @@ -use core::ptr::Unique; use core::fmt; use spin::Mutex; use volatile::Volatile; +use lazy_static::lazy_static; use x86_64::instructions::port::Port; use crate::logging::Color; use crate::consts::KERNEL_OFFSET; -use lazy_static::lazy_static; - -pub const VGA_BUFFER: Unique = unsafe { - Unique::new_unchecked((KERNEL_OFFSET + 0xb8000) as *mut _) -}; #[derive(Debug, Clone, Copy)] struct ColorCode(u8); @@ -73,8 +68,7 @@ impl VgaBuffer { lazy_static! { pub static ref VGA_WRITER: Mutex = Mutex::new( - // It is the only user of VGA_BUFFER. So it's safe. - VgaWriter::new(unsafe{ &mut *VGA_BUFFER.as_ptr() }) + VgaWriter::new(unsafe{ &mut *((KERNEL_OFFSET + 0xb8000) as *mut VgaBuffer) }) ); } diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index c13f0cd..6513ac9 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -1,16 +1,10 @@ -#![feature(ptr_internals)] #![feature(lang_items)] -#![feature(const_fn)] #![feature(alloc)] #![feature(naked_functions)] #![feature(asm)] #![feature(optin_builtin_traits)] #![feature(panic_info_message)] #![feature(global_asm)] -#![feature(compiler_builtins_lib)] -#![feature(raw)] -#![feature(vec_resize_default)] -#![feature(extern_crate_item_prelude)] #![no_std] // just keep it ... From fcf5074500551efc00c942f4e00307ce33816463 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Sat, 1 Dec 2018 17:37:37 +0800 Subject: [PATCH 25/39] impl M-mode kernel for riscv32 --- crate/process/src/interrupt.rs | 28 +++++-- kernel/Cargo.lock | 2 +- kernel/Cargo.toml | 5 ++ kernel/Makefile | 12 +-- kernel/src/arch/riscv32/boot/trap.asm | 30 ++++--- kernel/src/arch/riscv32/context.rs | 46 +++++++---- kernel/src/arch/riscv32/interrupt.rs | 115 +++++++++----------------- kernel/src/arch/riscv32/io.rs | 30 ++++--- kernel/src/arch/riscv32/mod.rs | 46 ++++++++++- kernel/src/arch/riscv32/timer.rs | 3 + kernel/src/fs.rs | 8 ++ riscv-pk | 2 +- 12 files changed, 193 insertions(+), 134 deletions(-) diff --git a/crate/process/src/interrupt.rs b/crate/process/src/interrupt.rs index 17d62ce..c33be0c 100644 --- a/crate/process/src/interrupt.rs +++ b/crate/process/src/interrupt.rs @@ -6,14 +6,6 @@ pub unsafe fn disable_and_store() -> usize { rflags & (1 << 9) } -#[inline(always)] -#[cfg(target_arch = "riscv32")] -pub unsafe fn disable_and_store() -> usize { - let sstatus: usize; - asm!("csrrci $0, 0x100, 2" : "=r"(sstatus)); - sstatus & 2 -} - #[inline(always)] #[cfg(target_arch = "x86_64")] pub unsafe fn restore(flags: usize) { @@ -22,10 +14,28 @@ pub unsafe fn restore(flags: usize) { } } +#[inline(always)] +#[cfg(target_arch = "riscv32")] +pub unsafe fn disable_and_store() -> usize { + if option_env!("m_mode").is_some() { + let mstatus: usize; + asm!("csrrci $0, 0x300, 1 << 3" : "=r"(mstatus)); + mstatus & (1 << 3) + } else { + let sstatus: usize; + asm!("csrrci $0, 0x100, 1 << 1" : "=r"(sstatus)); + sstatus & (1 << 1) + } +} + #[inline(always)] #[cfg(target_arch = "riscv32")] pub unsafe fn restore(flags: usize) { - asm!("csrs 0x100, $0" :: "r"(flags)); + if option_env!("m_mode").is_some() { + asm!("csrs 0x300, $0" :: "r"(flags)); + } else { + asm!("csrs 0x100, $0" :: "r"(flags)); + } } #[inline(always)] diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index 5a0a2b9..91d5bfd 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -218,7 +218,7 @@ dependencies = [ [[package]] name = "riscv" version = "0.3.0" -source = "git+https://github.com/riscv-and-rust-and-decaf/riscv#f358204af01f2374ab6ed6ea059f724cd5f2fe6f" +source = "git+https://github.com/riscv-and-rust-and-decaf/riscv#966eb26d5e8d77677f645d5e32877c678dcee572" dependencies = [ "bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 8f3f947..00fcf23 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -5,8 +5,13 @@ authors = ["Runji Wang "] edition = "2018" [features] +# Without BBL (for riscv32 FPGA board) no_bbl = [] +# Disable paging (for riscv32) no_mmu = [] +# Kernel in M-mode (for riscv32) +m_mode = ["no_mmu"] +# (for aarch64 RaspberryPi3) board_raspi3 = [] [profile.dev] diff --git a/kernel/Makefile b/kernel/Makefile index 1deeeac..5ab7d19 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -15,14 +15,16 @@ # smp = 1 | 2 | ... SMP core number # board = fpga Only available on riscv32, build without bbl, run on board # | raspi3 Only available on aarch64, run on Raspberry Pi 3 Model B/B+ -# nommu Only available on riscv32, build for M-Mode, without MMU +# m_mode Only available on riscv32, build for M-Mode, without MMU arch ?= riscv32 board ?= raspi3 mode ?= debug LOG ?= debug smp ?= 4 -nommu ?= +# NOTE: crate 'process' use this name 'm_mode' as an environment +# to set interrupt (MIE or SIE) +m_mode ?= target := $(arch)-blog_os kernel := target/$(target)/$(mode)/ucore @@ -45,7 +47,7 @@ else ifeq ($(arch), riscv32) qemu_opts += \ -machine virt \ -kernel $(bin) -ifdef nommu +ifdef m_mode qemu_opts += -cpu rv32imacu-nommu endif @@ -67,8 +69,8 @@ features += no_bbl endif endif -ifdef nommu -features += no_mmu +ifdef m_mode +features += no_mmu m_mode bbl_m_mode := --enable-boot-machine endif diff --git a/kernel/src/arch/riscv32/boot/trap.asm b/kernel/src/arch/riscv32/boot/trap.asm index bf4aba5..0f3bc3d 100644 --- a/kernel/src/arch/riscv32/boot/trap.asm +++ b/kernel/src/arch/riscv32/boot/trap.asm @@ -1,11 +1,19 @@ +# Constants / Macros defined in Rust code: +# xscratch +# xstatus +# xepc +# xcause +# xtval +# XRET + .macro SAVE_ALL # If coming from userspace, preserve the user stack pointer and load # the kernel stack pointer. If we came from the kernel, sscratch # will contain 0, and we should continue on the current stack. - csrrw sp, 0x140, sp # sscratch + csrrw sp, (xscratch), sp bnez sp, _save_context _restore_kernel_sp: - csrr sp, 0x140 # sscratch + csrr sp, (xscratch) # sscratch = previous-sp, sp = kernel-sp _save_context: # provide room for trap frame @@ -45,11 +53,11 @@ _save_context: # get sp, sstatus, sepc, stval, scause # set sscratch = 0 - csrrw s0, 0x140, x0 # sscratch - csrr s1, 0x100 # sstatus - csrr s2, 0x141 # sepc - csrr s3, 0x143 # stval - csrr s4, 0x142 # scause + csrrw s0, (xscratch), x0 + csrr s1, (xstatus) + csrr s2, (xepc) + csrr s3, (xtval) + csrr s4, (xcause) # store sp, sstatus, sepc, sbadvaddr, scause sw s0, 2*4(sp) sw s1, 32*4(sp) @@ -65,11 +73,11 @@ _save_context: bnez s0, _restore_context # back to S-mode? (sstatus.SPP = 1) _save_kernel_sp: addi s0, sp, 36*4 - csrw 0x140, s0 # sscratch = kernel-sp + csrw (xscratch), s0 # sscratch = kernel-sp _restore_context: # restore sstatus, sepc - csrw 0x100, s1 - csrw 0x141, s2 + csrw (xstatus), s1 + csrw (xepc), s2 # restore x registers except x2 (sp) lw x1, 1*4(sp) @@ -116,4 +124,4 @@ __alltraps: __trapret: RESTORE_ALL # return from supervisor call - sret + XRET diff --git a/kernel/src/arch/riscv32/context.rs b/kernel/src/arch/riscv32/context.rs index 56f806c..11c657b 100644 --- a/kernel/src/arch/riscv32/context.rs +++ b/kernel/src/arch/riscv32/context.rs @@ -1,13 +1,24 @@ -use riscv::register::*; +#[cfg(feature = "m_mode")] +use riscv::register::{ + mstatus as xstatus, + mstatus::Mstatus as Xstatus, + mcause::Mcause, +}; +#[cfg(not(feature = "m_mode"))] +use riscv::register::{ + sstatus as xstatus, + sstatus::Sstatus as Xstatus, + mcause::Mcause, +}; #[derive(Clone)] #[repr(C)] pub struct TrapFrame { pub x: [usize; 32], // general registers - pub sstatus: sstatus::Sstatus, // Supervisor Status Register + pub sstatus: Xstatus, // Supervisor Status Register pub sepc: usize, // Supervisor exception program counter, save the trap virtual address (here is used to save the process program entry addr?) - pub sbadaddr: usize, // Supervisor bad address - pub scause: scause::Scause, // scause register: record the cause of exception/interrupt/trap + pub stval: usize, // Supervisor trap value + pub scause: Mcause, // scause register: record the cause of exception/interrupt/trap } /// Generate the trapframe for building new thread in kernel @@ -28,13 +39,13 @@ impl TrapFrame { tf.x[10] = arg; // a0 tf.x[2] = sp; tf.sepc = entry as usize; - tf.sstatus = sstatus::read(); - // Supervisor Previous Interrupt Enable - tf.sstatus.set_spie(true); - // Supervisor Interrupt Disable - tf.sstatus.set_sie(false); - // Supervisor Previous Privilege Mode is Supervisor - tf.sstatus.set_spp(sstatus::SPP::Supervisor); + tf.sstatus = xstatus::read(); + tf.sstatus.set_xpie(true); + tf.sstatus.set_xie(false); + #[cfg(feature = "m_mode")] + tf.sstatus.set_mpp(xstatus::MPP::Machine); + #[cfg(not(feature = "m_mode"))] + tf.sstatus.set_spp(xstatus::SPP::Supervisor); tf } @@ -52,10 +63,13 @@ impl TrapFrame { let mut tf: Self = unsafe { zeroed() }; tf.x[2] = sp; tf.sepc = entry_addr; - tf.sstatus = sstatus::read(); - tf.sstatus.set_spie(true); - tf.sstatus.set_sie(false); - tf.sstatus.set_spp(sstatus::SPP::User); + tf.sstatus = xstatus::read(); + tf.sstatus.set_xpie(true); + tf.sstatus.set_xie(false); + #[cfg(feature = "m_mode")] + tf.sstatus.set_mpp(xstatus::MPP::User); + #[cfg(not(feature = "m_mode"))] + tf.sstatus.set_spp(xstatus::SPP::User); tf } } @@ -78,7 +92,7 @@ impl Debug for TrapFrame { .field("regs", &Regs(&self.x)) .field("sstatus", &self.sstatus) .field("sepc", &self.sepc) - .field("sbadaddr", &self.sbadaddr) + .field("stval", &self.stval) .field("scause", &self.scause) .finish() } diff --git a/kernel/src/arch/riscv32/interrupt.rs b/kernel/src/arch/riscv32/interrupt.rs index 4d253fc..22a3674 100644 --- a/kernel/src/arch/riscv32/interrupt.rs +++ b/kernel/src/arch/riscv32/interrupt.rs @@ -1,4 +1,16 @@ -use riscv::register::*; +#[cfg(feature = "m_mode")] +use riscv::register::{ + mstatus as xstatus, + mscratch as xscratch, + mtvec as xtvec, +}; +#[cfg(not(feature = "m_mode"))] +use riscv::register::{ + sstatus as xstatus, + sscratch as xscratch, + stvec as xtvec, +}; +use riscv::register::{mcause, mepc, sie}; pub use self::context::*; use crate::memory::{MemorySet, InactivePageTable0}; use log::*; @@ -17,13 +29,16 @@ pub fn init() { unsafe { // Set sscratch register to 0, indicating to exception vector that we are // presently executing in the kernel - sscratch::write(0); + xscratch::write(0); // Set the exception vector address - stvec::write(__alltraps as usize, stvec::TrapMode::Direct); + xtvec::write(__alltraps as usize, xtvec::TrapMode::Direct); // Enable IPI sie::set_ssoft(); // Enable serial interrupt sie::set_sext(); + // NOTE: In M-mode: mie.MSIE is set by BBL. + // mie.MEIE can not be set in QEMU v3.0 + // (seems like a bug) } info!("interrupt: init end"); } @@ -34,7 +49,7 @@ pub fn init() { */ #[inline(always)] pub unsafe fn enable() { - sstatus::set_sie(); + xstatus::set_xie(); } /* @@ -45,8 +60,8 @@ pub unsafe fn enable() { */ #[inline(always)] pub unsafe fn disable_and_store() -> usize { - let e = sstatus::read().sie() as usize; - sstatus::clear_sie(); + let e = xstatus::read().xie() as usize; + xstatus::clear_xie(); e } @@ -59,7 +74,7 @@ pub unsafe fn disable_and_store() -> usize { #[inline(always)] pub unsafe fn restore(flags: usize) { if flags != 0 { - sstatus::set_sie(); + xstatus::set_xie(); } } @@ -71,9 +86,15 @@ pub unsafe fn restore(flags: usize) { */ #[no_mangle] pub extern fn rust_trap(tf: &mut TrapFrame) { - use riscv::register::scause::{Trap, Interrupt as I, Exception as E}; + use self::mcause::{Trap, Interrupt as I, Exception as E}; trace!("Interrupt @ CPU{}: {:?} ", super::cpu::id(), tf.scause.cause()); match tf.scause.cause() { + // M-mode only + Trap::Interrupt(I::MachineExternal) => serial(), + Trap::Interrupt(I::MachineSoft) => ipi(), + Trap::Interrupt(I::MachineTimer) => timer(), + Trap::Exception(E::MachineEnvCall) => sbi(tf), + Trap::Interrupt(I::SupervisorExternal) => serial(), Trap::Interrupt(I::SupervisorSoft) => ipi(), Trap::Interrupt(I::SupervisorTimer) => timer(), @@ -87,6 +108,12 @@ pub extern fn rust_trap(tf: &mut TrapFrame) { trace!("Interrupt end"); } +/// Call BBL SBI functions for M-mode kernel +fn sbi(tf: &mut TrapFrame) { + (super::BBL.mcall_trap)(tf.x.as_ptr(), tf.scause.bits(), tf.sepc); + tf.sepc += 4; +} + fn serial() { crate::trap::serial(super::io::getchar()); } @@ -101,8 +128,8 @@ fn ipi() { * process timer interrupt */ fn timer() { - crate::trap::timer(); super::timer::set_next(); + crate::trap::timer(); } /* @@ -124,9 +151,8 @@ fn syscall(tf: &mut TrapFrame) { * process IllegalInstruction exception */ fn illegal_inst(tf: &mut TrapFrame) { - if !emulate_mul_div(tf) { - crate::trap::error(tf); - } + (super::BBL.illegal_insn_trap)(tf.x.as_ptr(), tf.scause.bits(), tf.sepc); + tf.sepc = mepc::read(); } /* @@ -136,73 +162,10 @@ fn illegal_inst(tf: &mut TrapFrame) { * process page fault exception */ fn page_fault(tf: &mut TrapFrame) { - let addr = stval::read(); + let addr = tf.stval; trace!("\nEXCEPTION: Page Fault @ {:#x}", addr); if !crate::memory::page_fault_handler(addr) { crate::trap::error(tf); } } - -/// Migrate from riscv-pk -/* -* @param: -* TrapFrame: the Trapframe for the illegal inst exception -* @brief: -* emulate the multiply and divide operation (if not this kind of operation return false) -* @retval: -* a bool indicates whether emulate the multiply and divide operation successfully -*/ -fn emulate_mul_div(tf: &mut TrapFrame) -> bool { - let insn = unsafe { *(tf.sepc as *const usize) }; - let rs1 = tf.x[get_reg(insn, RS1)]; - let rs2 = tf.x[get_reg(insn, RS2)]; - - let rd = if (insn & MASK_MUL) == MATCH_MUL { - rs1 * rs2 - } else if (insn & MASK_DIV) == MATCH_DIV { - ((rs1 as i32) / (rs2 as i32)) as usize - } else if (insn & MASK_DIVU) == MATCH_DIVU { - rs1 / rs2 - } else if (insn & MASK_REM) == MATCH_REM { - ((rs1 as i32) % (rs2 as i32)) as usize - } else if (insn & MASK_REMU) == MATCH_REMU { - rs1 % rs2 - } else if (insn & MASK_MULH) == MATCH_MULH { - (((rs1 as i32 as i64) * (rs2 as i32 as i64)) >> 32) as usize - } else if (insn & MASK_MULHU) == MATCH_MULHU { - (((rs1 as i64) * (rs2 as i64)) >> 32) as usize - } else if (insn & MASK_MULHSU) == MATCH_MULHSU { - (((rs1 as i32 as i64) * (rs2 as i64)) >> 32) as usize - } else { - return false; - }; - tf.x[get_reg(insn, RD)] = rd; - tf.sepc += 4; // jump to next instruction - return true; - - fn get_reg(inst: usize, offset: usize) -> usize { - (inst >> offset) & 0x1f - } - - const RS1: usize = 15; - const RS2: usize = 20; - const RD: usize = 7; - - const MATCH_MUL: usize = 0x2000033; - const MASK_MUL: usize = 0xfe00707f; - const MATCH_MULH: usize = 0x2001033; - const MASK_MULH: usize = 0xfe00707f; - const MATCH_MULHSU: usize = 0x2002033; - const MASK_MULHSU: usize = 0xfe00707f; - const MATCH_MULHU: usize = 0x2003033; - const MASK_MULHU: usize = 0xfe00707f; - const MATCH_DIV: usize = 0x2004033; - const MASK_DIV: usize = 0xfe00707f; - const MATCH_DIVU: usize = 0x2005033; - const MASK_DIVU: usize = 0xfe00707f; - const MATCH_REM: usize = 0x2006033; - const MASK_REM: usize = 0xfe00707f; - const MATCH_REMU: usize = 0x2007033; - const MASK_REMU: usize = 0xfe00707f; -} \ No newline at end of file diff --git a/kernel/src/arch/riscv32/io.rs b/kernel/src/arch/riscv32/io.rs index 09e946e..b2c49d2 100644 --- a/kernel/src/arch/riscv32/io.rs +++ b/kernel/src/arch/riscv32/io.rs @@ -20,23 +20,29 @@ impl Write for SerialPort { } fn putchar(c: u8) { - #[cfg(feature = "no_bbl")] - unsafe { - while read_volatile(STATUS) & CAN_WRITE == 0 {} - write_volatile(DATA, c as u8); + if cfg!(feature = "no_bbl") { + unsafe { + while read_volatile(STATUS) & CAN_WRITE == 0 {} + write_volatile(DATA, c as u8); + } + } else if cfg!(feature = "m_mode") { + (super::BBL.mcall_console_putchar)(c); + } else { + sbi::console_putchar(c as usize); } - #[cfg(not(feature = "no_bbl"))] - sbi::console_putchar(c as usize); } pub fn getchar() -> char { - #[cfg(feature = "no_bbl")] - let c = unsafe { - while read_volatile(STATUS) & CAN_READ == 0 {} - read_volatile(DATA) + let c = if cfg!(feature = "no_bbl") { + unsafe { + // while read_volatile(STATUS) & CAN_READ == 0 {} + read_volatile(DATA) + } + } else if cfg!(feature = "m_mode") { + (super::BBL.mcall_console_getchar)() as u8 + } else { + sbi::console_getchar() as u8 }; - #[cfg(not(feature = "no_bbl"))] - let c = sbi::console_getchar() as u8; match c { 255 => '\0', // null diff --git a/kernel/src/arch/riscv32/mod.rs b/kernel/src/arch/riscv32/mod.rs index 549fbc2..eb2a17a 100644 --- a/kernel/src/arch/riscv32/mod.rs +++ b/kernel/src/arch/riscv32/mod.rs @@ -8,9 +8,10 @@ pub mod consts; pub mod cpu; #[no_mangle] -pub extern fn rust_main(hartid: usize, dtb: usize, hart_mask: usize) -> ! { +pub extern fn rust_main(hartid: usize, dtb: usize, hart_mask: usize, functions: usize) -> ! { unsafe { cpu::set_cpu_id(hartid); } - println!("Hello RISCV! in hart {}, {}, {}", hartid, dtb, hart_mask); + unsafe { BBL_FUNCTIONS_PTR = functions as *const _; } + println!("Hello RISCV! in hart {}, dtb @ {:#x}, functions @ {:#x}", hartid, dtb, functions); if hartid != 0 { while unsafe { !cpu::has_started(hartid) } { } @@ -35,7 +36,46 @@ fn others_main() -> ! { crate::kmain(); } + +/// Constant & Macro for `trap.asm` +#[cfg(feature = "m_mode")] +global_asm!(" + .equ xstatus, 0x300 + .equ xscratch, 0x340 + .equ xepc, 0x341 + .equ xcause, 0x342 + .equ xtval, 0x343 + .macro XRET\n mret\n .endm +"); +#[cfg(not(feature = "m_mode"))] +global_asm!(" + .equ xstatus, 0x100 + .equ xscratch, 0x140 + .equ xepc, 0x141 + .equ xcause, 0x142 + .equ xtval, 0x143 + .macro XRET\n sret\n .endm +"); + #[cfg(feature = "no_bbl")] global_asm!(include_str!("boot/boot.asm")); global_asm!(include_str!("boot/entry.asm")); -global_asm!(include_str!("boot/trap.asm")); \ No newline at end of file +global_asm!(include_str!("boot/trap.asm")); + + +/// Some symbols passed from BBL. +/// Used in M-mode kernel. +#[repr(C)] +struct BBLFunctions { + mcall_trap: BBLTrapHandler, + illegal_insn_trap: BBLTrapHandler, + mcall_console_putchar: extern fn(u8), + mcall_console_getchar: extern fn() -> usize, +} + +type BBLTrapHandler = extern fn(regs: *const usize, mcause: usize, mepc: usize); +static mut BBL_FUNCTIONS_PTR: *const BBLFunctions = ::core::ptr::null(); +use lazy_static::lazy_static; +lazy_static! { + static ref BBL: BBLFunctions = unsafe { BBL_FUNCTIONS_PTR.read() }; +} \ No newline at end of file diff --git a/kernel/src/arch/riscv32/timer.rs b/kernel/src/arch/riscv32/timer.rs index c6e5e2b..7588deb 100644 --- a/kernel/src/arch/riscv32/timer.rs +++ b/kernel/src/arch/riscv32/timer.rs @@ -33,6 +33,9 @@ pub fn get_cycle() -> u64 { */ pub fn init() { // Enable supervisor timer interrupt + #[cfg(feature = "m_mode")] + unsafe { mie::set_mtimer(); } + #[cfg(not(feature = "m_mode"))] unsafe { sie::set_stimer(); } set_next(); diff --git a/kernel/src/fs.rs b/kernel/src/fs.rs index 3779ab5..7ef2208 100644 --- a/kernel/src/fs.rs +++ b/kernel/src/fs.rs @@ -88,6 +88,14 @@ impl Stdin { self.pushed.notify_one(); } pub fn pop(&self) -> char { + // QEMU v3.0 don't support M-mode external interrupt (bug?) + // So we have to use polling. + #[cfg(feature = "m_mode")] + loop { + let c = crate::arch::io::getchar(); + if c != '\0' { return c; } + } + #[cfg(not(feature = "m_mode"))] loop { let ret = self.buf.lock().pop_front(); match ret { diff --git a/riscv-pk b/riscv-pk index 98d2ffb..71b2add 160000 --- a/riscv-pk +++ b/riscv-pk @@ -1 +1 @@ -Subproject commit 98d2ffbb3203e1d9a936b067c51f8522a5ff9677 +Subproject commit 71b2addd7ada2f07ca8e6f02787d706afa4fbe66 From 1f305a883cd99babbda2e29a040b3c497b22a6b1 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Sat, 1 Dec 2018 17:47:02 +0800 Subject: [PATCH 26/39] add travis for riscv32 M-mode --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 56d7ee9..a25c71a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,7 @@ addons: env: matrix: - ARCH="riscv32" + - ARCH="riscv32" OPTS="m_mode=1" - ARCH="x86_64" - ARCH="aarch64" @@ -38,5 +39,5 @@ before_script: - (test -x $HOME/.cargo/bin/bootimage || cargo install bootimage) script: - - cd kernel && make build arch=$ARCH && cd .. + - cd kernel && make build arch=$ARCH $OPTS && cd .. - cd user && make arch=$ARCH From 210854971d76bc6b8673f41b57313b32b5703a31 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Sat, 1 Dec 2018 18:21:15 +0800 Subject: [PATCH 27/39] add travis for macOS --- .travis.yml | 31 +++++++++++++++++++++++-------- user/x86_64-ucore.json | 1 + 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index a25c71a..5e713e8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,25 @@ -sudo: required +sudo: false language: rust rust: nightly +os: + - linux + - osx + cache: cargo: true + directories: + - $HOME/Library/Caches/Homebrew addons: apt: packages: - qemu + homebrew: + packages: + - qemu env: matrix: @@ -21,15 +30,21 @@ env: install: - if [ $ARCH = riscv32 ]; then - export FILE="riscv64-unknown-elf-gcc-20181030-x86_64-linux-ubuntu14"; - wget https://static.dev.sifive.com/dev-tools/$FILE.tar.gz; - tar xf $FILE.tar.gz; - export PATH=$PATH:$PWD/$FILE/bin; + [ $TRAVIS_OS_NAME = linux ] && export FILE="riscv64-unknown-elf-gcc-20181127-x86_64-linux-ubuntu14"; + [ $TRAVIS_OS_NAME = osx ] && export FILE="riscv64-unknown-elf-gcc-20181127-x86_64-apple-darwin"; + wget https://static.dev.sifive.com/dev-tools/$FILE.tar.gz; + tar xf $FILE.tar.gz; + export PATH=$PATH:$PWD/$FILE/bin; fi - if [ $ARCH = aarch64 ]; then - wget https://web.stanford.edu/class/cs140e/files/aarch64-none-elf-linux-x64.tar.gz; - tar -xzvf aarch64-none-elf-linux-x64.tar.gz; - export PATH=$PATH:$PWD/aarch64-none-elf/bin; + if [ $TRAVIS_OS_NAME = linux ]; then + wget https://web.stanford.edu/class/cs140e/files/aarch64-none-elf-linux-x64.tar.gz; + tar -xzvf aarch64-none-elf-linux-x64.tar.gz; + export PATH=$PATH:$PWD/aarch64-none-elf/bin; + elif [ $TRAVIS_OS_NAME = osx ]; then + brew tap SergioBenitez/osxct; + brew install aarch64-none-elf; + fi; fi diff --git a/user/x86_64-ucore.json b/user/x86_64-ucore.json index c587146..a2691a5 100644 --- a/user/x86_64-ucore.json +++ b/user/x86_64-ucore.json @@ -7,6 +7,7 @@ "target-c-int-width": "32", "os": "none", "executables": true, + "linker": "rust-lld", "linker-flavor": "ld.lld", "panic-strategy": "abort", "disable-redzone": true, From 0eb33b661f152c5de3a97c9bd0e3f0aaf82bf6aa Mon Sep 17 00:00:00 2001 From: equation314 Date: Fri, 30 Nov 2018 14:13:27 +0800 Subject: [PATCH 28/39] aarch64/mmu: fix some bugs --- crate/aarch64/Cargo.lock | 108 ------------------- crate/aarch64/src/paging/memory_attribute.rs | 2 +- crate/memory/src/memory_set.rs | 2 +- kernel/src/lib.rs | 4 - 4 files changed, 2 insertions(+), 114 deletions(-) delete mode 100644 crate/aarch64/Cargo.lock diff --git a/crate/aarch64/Cargo.lock b/crate/aarch64/Cargo.lock deleted file mode 100644 index 1038287..0000000 --- a/crate/aarch64/Cargo.lock +++ /dev/null @@ -1,108 +0,0 @@ -[[package]] -name = "aarch64" -version = "0.1.0" -dependencies = [ - "bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ux 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bare-metal" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "bit_field" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "bitflags" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "cfg-if" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "log" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "os_bootinfo" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "register" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "tock-registers 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "tock-registers" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "usize_conversions" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "ux" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[metadata] -"checksum bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a3caf393d93b2d453e80638d0674597020cef3382ada454faacd43d1a55a735a" -"checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" -"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" -"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" -"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" -"checksum os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "66481dbeb5e773e7bd85b63cd6042c30786f834338288c5ec4f3742673db360a" -"checksum register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e10f31b6d2299e5620986ad9fcdd66463e125ad72af4f403f9aedf7592d5ccdb" -"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum tock-registers 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a385d94f3f62e60445a0adb9ff8d9621faa272234530d4c0f848ec98f88e316" -"checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" -"checksum ux 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53d8df5dd8d07fedccd202de1887d94481fadaea3db70479f459e8163a1fab41" diff --git a/crate/aarch64/src/paging/memory_attribute.rs b/crate/aarch64/src/paging/memory_attribute.rs index beb9b69..4a24071 100644 --- a/crate/aarch64/src/paging/memory_attribute.rs +++ b/crate/aarch64/src/paging/memory_attribute.rs @@ -59,6 +59,6 @@ impl MairType for MairNormalNonCacheable { #[inline] fn attr_value() -> PageTableAttribute { - MEMORY_ATTRIBUTE::SH::NonShareable + MEMORY_ATTRIBUTE::AttrIndx.val(Self::INDEX) + MEMORY_ATTRIBUTE::SH::OuterShareable + MEMORY_ATTRIBUTE::AttrIndx.val(Self::INDEX) } } diff --git a/crate/memory/src/memory_set.rs b/crate/memory/src/memory_set.rs index 316c28b..b47a6c2 100644 --- a/crate/memory/src/memory_set.rs +++ b/crate/memory/src/memory_set.rs @@ -127,7 +127,7 @@ impl MemoryAttr { if self.user { entry.set_user(true); } if self.readonly { entry.set_writable(false); } if self.execute { entry.set_execute(true); } - if self.mmio { entry.set_mmio(false); } + if self.mmio { entry.set_mmio(true); } if self.hide { entry.set_present(false); } if self.user || self.readonly || self.execute || self.mmio || self.hide { entry.update(); } } diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 05cc181..063b1d0 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -38,10 +38,6 @@ extern crate aarch64; use linked_list_allocator::LockedHeap; - -#[macro_use] -extern crate register; - #[macro_use] // print! pub mod logging; mod memory; From d6a54496f0aaa479ae06ac0b80a7d85f98dc17ae Mon Sep 17 00:00:00 2001 From: equation314 Date: Sat, 1 Dec 2018 19:43:49 +0800 Subject: [PATCH 29/39] arch64/mmu: invalidate all icaches in InactivePageTable::with() --- crate/aarch64/src/asm.rs | 12 ++++++++++++ kernel/src/arch/aarch64/paging.rs | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/crate/aarch64/src/asm.rs b/crate/aarch64/src/asm.rs index 4cd37d7..bada049 100644 --- a/crate/aarch64/src/asm.rs +++ b/crate/aarch64/src/asm.rs @@ -105,6 +105,18 @@ pub fn tlb_invalidate(vaddr: VirtAddr) { } } +/// Invalidate all instruction caches in Inner Shareable domain to Point of Unification. +#[inline(always)] +pub fn flush_icache_all() { + unsafe { + asm!( + "ic ialluis + dsb ish + isb" + ); + } +} + /// Address Translate. #[inline(always)] pub fn address_translate(vaddr: usize) -> usize { diff --git a/kernel/src/arch/aarch64/paging.rs b/kernel/src/arch/aarch64/paging.rs index 0093385..0b7546e 100644 --- a/kernel/src/arch/aarch64/paging.rs +++ b/kernel/src/arch/aarch64/paging.rs @@ -5,7 +5,7 @@ use memory::{active_table, alloc_frame, alloc_stack, dealloc_frame}; use ucore_memory::memory_set::*; use ucore_memory::PAGE_SIZE; use ucore_memory::paging::*; -use aarch64::asm::{tlb_invalidate, tlb_invalidate_all, ttbr_el1_read, ttbr_el1_write}; +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}; @@ -246,6 +246,7 @@ impl InactivePageTable for InactivePageTable0 { if old_frame != new_frame { ttbr_el1_write(1, old_frame); tlb_invalidate_all(); + flush_icache_all(); } } From a59a7fbe9a3e4878bcd8abbc913e87b46c6c717d Mon Sep 17 00:00:00 2001 From: equation314 Date: Sat, 1 Dec 2018 20:00:35 +0800 Subject: [PATCH 30/39] aarch64/mmu: simply handle page fault --- crate/aarch64/src/regs/far_el1.rs | 31 ++++++++++++++++++++ crate/aarch64/src/regs/mod.rs | 2 ++ kernel/src/arch/aarch64/interrupt/handler.rs | 28 +++++++++++++++--- 3 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 crate/aarch64/src/regs/far_el1.rs diff --git a/crate/aarch64/src/regs/far_el1.rs b/crate/aarch64/src/regs/far_el1.rs new file mode 100644 index 0000000..fc809fa --- /dev/null +++ b/crate/aarch64/src/regs/far_el1.rs @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 by the author(s) + * + * ============================================================================= + * + * Licensed under either of + * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) + * - MIT License (http://opensource.org/licenses/MIT) + * at your option. + * + * ============================================================================= + * + * Author(s): + * - Andre Richter + */ + +//! Fault Address Register - EL1 +//! +//! Holds the faulting Virtual Address for all synchronous Instruction or Data +//! Abort, PC alignment fault and Watchpoint exceptions that are taken to EL1. + +use register::cpu::RegisterReadWrite; + +pub struct Reg; + +impl RegisterReadWrite for Reg { + sys_coproc_read_raw!(u64, "FAR_EL1"); + sys_coproc_write_raw!(u64, "FAR_EL1"); +} + +pub static FAR_EL1: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/mod.rs b/crate/aarch64/src/regs/mod.rs index f69f0c4..11a6d3f 100644 --- a/crate/aarch64/src/regs/mod.rs +++ b/crate/aarch64/src/regs/mod.rs @@ -12,6 +12,7 @@ mod cntvoff_el2; mod currentel; mod daif; mod elr_el2; +mod far_el1; mod hcr_el2; mod id_aa64mmfr0_el1; mod mair_el1; @@ -38,6 +39,7 @@ pub use self::cntvoff_el2::CNTVOFF_EL2; pub use self::currentel::CurrentEL; pub use self::daif::DAIF; pub use self::elr_el2::ELR_EL2; +pub use self::far_el1::FAR_EL1; pub use self::hcr_el2::HCR_EL2; pub use self::id_aa64mmfr0_el1::ID_AA64MMFR0_EL1; pub use self::mair_el1::{MAIR_EL1, MAIR_ATTR}; diff --git a/kernel/src/arch/aarch64/interrupt/handler.rs b/kernel/src/arch/aarch64/interrupt/handler.rs index 787cbf5..21142f9 100644 --- a/kernel/src/arch/aarch64/interrupt/handler.rs +++ b/kernel/src/arch/aarch64/interrupt/handler.rs @@ -2,7 +2,9 @@ use arch::board::irq::handle_irq; use super::context::TrapFrame; -use super::syndrome::Syndrome; +use super::syndrome::{Fault, Syndrome}; + +use aarch64::regs::*; global_asm!(include_str!("trap.S")); global_asm!(include_str!("vector.S")); @@ -46,7 +48,14 @@ pub extern "C" fn rust_trap(info: Info, esr: u32, tf: &mut TrapFrame) { // syndrome is only valid with sync match syndrome { Syndrome::Brk(brk) => handle_break(brk, tf), - Syndrome::Svc(_) => handle_syscall(tf), + Syndrome::Svc(svc) => handle_syscall(svc, tf), + Syndrome::DataAbort { kind, level: _ } + | Syndrome::InstructionAbort { kind, level: _ } => match kind { + Fault::Translation | Fault::AccessFlag | Fault::Permission => { + handle_page_fault(tf) + } + _ => ::trap::error(tf), + }, _ => ::trap::error(tf), } } @@ -57,12 +66,16 @@ pub extern "C" fn rust_trap(info: Info, esr: u32, tf: &mut TrapFrame) { trace!("Interrupt end"); } -fn handle_break(num: u16, tf: &mut TrapFrame) { +fn handle_break(_num: u16, tf: &mut TrapFrame) { // Skip the current brk instruction (ref: J1.1.2, page 6147) tf.elr += 4; } -fn handle_syscall(tf: &mut TrapFrame) { +fn handle_syscall(num: u16, tf: &mut TrapFrame) { + if num != 0 { + ::trap::error(tf); + } + // svc instruction has been skipped in syscall (ref: J1.1.2, page 6152) let ret = ::syscall::syscall( tf.x1to29[7] as usize, @@ -78,3 +91,10 @@ fn handle_syscall(tf: &mut TrapFrame) { ); tf.x0 = ret as usize; } + +fn handle_page_fault(tf: &mut TrapFrame) { + let addr = FAR_EL1.get(); + trace!("\nEXCEPTION: Page Fault @ {:#x}", addr); + + ::trap::error(tf); +} From 5a83d6a20f1f2214c2ccf700e4add752fc335a42 Mon Sep 17 00:00:00 2001 From: equation314 Date: Fri, 30 Nov 2018 13:36:17 +0800 Subject: [PATCH 31/39] aarch64: add user program SFS image --- kernel/src/fs.rs | 2 +- user/user-aarch64.img | Bin 0 -> 2097152 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 user/user-aarch64.img diff --git a/kernel/src/fs.rs b/kernel/src/fs.rs index 6810916..89e32aa 100644 --- a/kernel/src/fs.rs +++ b/kernel/src/fs.rs @@ -24,7 +24,7 @@ global_asm!(r#" .global _user_img_start .global _user_img_end _user_img_start: - .incbin "../user/user-riscv.img" + .incbin "../user/user-aarch64.img" _user_img_end: "#); diff --git a/user/user-aarch64.img b/user/user-aarch64.img new file mode 100644 index 0000000000000000000000000000000000000000..ba673f0526d82a59d91e1596bc28e41ddadf4ca9 GIT binary patch literal 2097152 zcmeFa3w#{amFIuEt1SskkZjq=U_fPT$BvS*4A@9E50_s77MNh8`G>)~t8Ta2cE@^m z%a17YrwQ3lGUM=HckrwUWS5q0993sBCp zV^RO#xm6`~mo2l8*_qkwulSU2-E+=8=iKjo)UC!L4c~gGQH7OSrj$x0JG$Ew=B6b6 zNbO9e6CDupA^{R00TLhq5+DH*AOR8}0TLhq68PVrK%K(pe^{YV2JQ&%D{wdBUW64TdB z`0*tyNq_`MfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14c zNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-L zfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@ z1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14c zNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-L zfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCNZ@1W14cNPq-LfCPU3 z2~0;9XW*WR`!d{hxM$(M95*i#AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq z5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH*AOR8}0TLhq5+DH* zAOR8}0TLjAe;fi;A^ku99}jj(pk9M|!LW)Wb5-`PklF)7`OgK6Ut|4G+GB>wj!sp@ zQ6rq+w>q5N|8Rx9|G`jp-&|EZYJ?gFARAbqiX(=~7b2PI`#-FTxeaOvP7bL;DEn-y z8hYZfO8d*Vs^Z;B?Kx1l#vXW3W%pIDwGY&V?S0jTT`+3%OQF+kRBiU7cs4_s=_g&; zr1?mEiRl$3d@D_Ec?J}mHptAEA7DrA^TuG7>ugy za|gaK{n-_2=thh~ZiO{8*8lvtAMqvESpQ)if6y?pwh_(_&W+e7Dl*e;FdqbSVV!SW zW9J(~@Vypy*cPAp#tJ-F+5@|)bgcakUt~+XCmy?4=lA7SRg|0%Lg#tKcn&`-%fVS4 z{?Tg~`;lE0_EC≈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 Date: Sat, 1 Dec 2018 22:26:55 +0800 Subject: [PATCH 32/39] aarch64/mmu: move crate aarch64 to remote --- crate/aarch64/Cargo.toml | 16 - crate/aarch64/src/addr.rs | 441 --------------- crate/aarch64/src/asm.rs | 158 ------ crate/aarch64/src/barrier.rs | 91 --- crate/aarch64/src/lib.rs | 29 - crate/aarch64/src/paging/frame_alloc.rs | 15 - crate/aarch64/src/paging/memory_attribute.rs | 64 --- crate/aarch64/src/paging/mod.rs | 549 ------------------- crate/aarch64/src/paging/page_table.rs | 267 --------- crate/aarch64/src/paging/recursive.rs | 425 -------------- crate/aarch64/src/regs/cntfrq_el0.rs | 31 -- crate/aarch64/src/regs/cnthctl_el2.rs | 75 --- crate/aarch64/src/regs/cntp_ctl_el0.rs | 62 --- crate/aarch64/src/regs/cntp_tval_el0.rs | 30 - crate/aarch64/src/regs/cntpct_el0.rs | 29 - crate/aarch64/src/regs/cntvoff_el2.rs | 32 -- crate/aarch64/src/regs/currentel.rs | 52 -- crate/aarch64/src/regs/daif.rs | 90 --- crate/aarch64/src/regs/elr_el2.rs | 30 - crate/aarch64/src/regs/far_el1.rs | 31 -- crate/aarch64/src/regs/hcr_el2.rs | 123 ----- crate/aarch64/src/regs/id_aa64mmfr0_el1.rs | 82 --- crate/aarch64/src/regs/macros.rs | 85 --- crate/aarch64/src/regs/mair_el1.rs | 82 --- crate/aarch64/src/regs/mod.rs | 55 -- crate/aarch64/src/regs/mpidr_el1.rs | 30 - crate/aarch64/src/regs/sctlr_el1.rs | 103 ---- crate/aarch64/src/regs/sp.rs | 28 - crate/aarch64/src/regs/sp_el0.rs | 31 -- crate/aarch64/src/regs/sp_el1.rs | 36 -- crate/aarch64/src/regs/spsel.rs | 48 -- crate/aarch64/src/regs/spsr_el2.rs | 106 ---- crate/aarch64/src/regs/tcr_el1.rs | 313 ----------- crate/aarch64/src/regs/ttbr0_el1.rs | 61 --- crate/aarch64/src/regs/ttbr1_el1.rs | 61 --- kernel/Cargo.lock | 6 +- kernel/Cargo.toml | 2 +- 37 files changed, 5 insertions(+), 3764 deletions(-) delete mode 100644 crate/aarch64/Cargo.toml delete mode 100644 crate/aarch64/src/addr.rs delete mode 100644 crate/aarch64/src/asm.rs delete mode 100644 crate/aarch64/src/barrier.rs delete mode 100644 crate/aarch64/src/lib.rs delete mode 100644 crate/aarch64/src/paging/frame_alloc.rs delete mode 100644 crate/aarch64/src/paging/memory_attribute.rs delete mode 100644 crate/aarch64/src/paging/mod.rs delete mode 100644 crate/aarch64/src/paging/page_table.rs delete mode 100644 crate/aarch64/src/paging/recursive.rs delete mode 100644 crate/aarch64/src/regs/cntfrq_el0.rs delete mode 100644 crate/aarch64/src/regs/cnthctl_el2.rs delete mode 100644 crate/aarch64/src/regs/cntp_ctl_el0.rs delete mode 100644 crate/aarch64/src/regs/cntp_tval_el0.rs delete mode 100644 crate/aarch64/src/regs/cntpct_el0.rs delete mode 100644 crate/aarch64/src/regs/cntvoff_el2.rs delete mode 100644 crate/aarch64/src/regs/currentel.rs delete mode 100644 crate/aarch64/src/regs/daif.rs delete mode 100644 crate/aarch64/src/regs/elr_el2.rs delete mode 100644 crate/aarch64/src/regs/far_el1.rs delete mode 100644 crate/aarch64/src/regs/hcr_el2.rs delete mode 100644 crate/aarch64/src/regs/id_aa64mmfr0_el1.rs delete mode 100644 crate/aarch64/src/regs/macros.rs delete mode 100644 crate/aarch64/src/regs/mair_el1.rs delete mode 100644 crate/aarch64/src/regs/mod.rs delete mode 100644 crate/aarch64/src/regs/mpidr_el1.rs delete mode 100644 crate/aarch64/src/regs/sctlr_el1.rs delete mode 100644 crate/aarch64/src/regs/sp.rs delete mode 100644 crate/aarch64/src/regs/sp_el0.rs delete mode 100644 crate/aarch64/src/regs/sp_el1.rs delete mode 100644 crate/aarch64/src/regs/spsel.rs delete mode 100644 crate/aarch64/src/regs/spsr_el2.rs delete mode 100644 crate/aarch64/src/regs/tcr_el1.rs delete mode 100644 crate/aarch64/src/regs/ttbr0_el1.rs delete mode 100644 crate/aarch64/src/regs/ttbr1_el1.rs diff --git a/crate/aarch64/Cargo.toml b/crate/aarch64/Cargo.toml deleted file mode 100644 index 7514dd0..0000000 --- a/crate/aarch64/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "aarch64" -version = "0.1.0" -authors = ["koumingyang <1761674434@qq.com>"] - -[dependencies] -register = "0.2.0" -bit_field = "0.9.0" -bitflags = "1.0.1" -usize_conversions = "0.2.0" -os_bootinfo = "0.2.0" -bare-metal = "0.2.0" - -[dependencies.ux] -default-features = false -version = "0.1.0" \ No newline at end of file diff --git a/crate/aarch64/src/addr.rs b/crate/aarch64/src/addr.rs deleted file mode 100644 index cf3f092..0000000 --- a/crate/aarch64/src/addr.rs +++ /dev/null @@ -1,441 +0,0 @@ -use core::convert::{Into, TryInto}; -use core::fmt; -use core::ops::{Add, AddAssign, Sub, SubAssign}; - -use bit_field::BitField; -use usize_conversions::FromUsize; -use ux::*; - -#[derive(Debug)] -#[repr(u8)] -pub enum VirtAddrRange { - /// 0x0000000000000000 to 0x0000FFFFFFFFFFFF - BottomRange = 0, - /// 0xFFFF000000000000 to 0xFFFFFFFFFFFFFFFF. - TopRange = 1, -} - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[repr(transparent)] -pub struct VirtAddr(u64); - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[repr(transparent)] -pub struct PhysAddr(u64); - -#[derive(Debug)] -pub struct VirtAddrNotValid(u64); - -impl VirtAddr { - /// Creates a new canonical virtual address. - /// - /// This function performs sign extension of bit 47 to make the address canonical. Panics - /// if the bits in the range 48 to 64 contain data (i.e. are not null and no sign extension). - pub fn new(addr: u64) -> VirtAddr { - Self::try_new(addr).expect( - "invalid virtual address", - ) - } - - /// Tries to create a new canonical virtual address. - /// in aarch64, valid virtual address starts with 0x0000 or 0xffff. - pub fn try_new(addr: u64) -> Result { - match addr.get_bits(48..64) { - 0 | 0xffff => Ok(VirtAddr(addr)), // address is canonical - other => Err(VirtAddrNotValid(other)), - } - } - - pub fn new_unchecked(addr: u64) -> VirtAddr { - VirtAddr(addr) - } - - /// Creates a virtual address that points to `0`. - pub const fn zero() -> VirtAddr { - VirtAddr(0) - } - - /// Converts the address to an `u64`. - pub fn as_u64(self) -> u64 { - self.0 - } - - /// Creates a virtual address from the given pointer - pub fn from_ptr(ptr: *const T) -> Self { - use usize_conversions::FromUsize; - - Self::new(u64::from_usize(ptr as usize)) - } - - /// Converts the address to a raw pointer. - #[cfg(target_pointer_width = "64")] - pub fn as_ptr(self) -> *const T { - use usize_conversions::usize_from; - - usize_from(self.as_u64()) as *const T - } - - /// Converts the address to a mutable raw pointer. - #[cfg(target_pointer_width = "64")] - pub fn as_mut_ptr(self) -> *mut T { - self.as_ptr::() as *mut T - } - - /// Aligns the virtual address upwards to the given alignment. - /// - /// See the `align_up` function for more information. - pub fn align_up(self, align: U) -> Self - where - U: Into, - { - VirtAddr(align_up(self.0, align.into())) - } - - /// Aligns the virtual address downwards to the given alignment. - /// - /// See the `align_down` function for more information. - pub fn align_down(self, align: U) -> Self - where - U: Into, - { - VirtAddr(align_down(self.0, align.into())) - } - - /// Checks whether the virtual address has the demanded alignment. - pub fn is_aligned(self, align: U) -> bool - where - U: Into, - { - self.align_down(align) == self - } - - /// Returns the 12-bit page offset of this virtual address. - pub fn page_offset(&self) -> u12 { - u12::new((self.0 & 0xfff).try_into().unwrap()) - } - - /// Returns the VA range - pub fn va_range(&self) -> Result { - match self.va_range_bits() { - 0x0000 => Ok(VirtAddrRange::BottomRange), - 0xffff => Ok(VirtAddrRange::TopRange), - _ => Err(VirtAddrNotValid(self.0)), - } - } - - /// Returns the top 16 bits - pub fn va_range_bits(&self) -> u16 { - ((self.0 >> 48) & 0xffff) as u16 - } - - /// Returns the 9-bit level 1 page table index. - pub fn p1_index(&self) -> u9 { - u9::new(((self.0 >> 12) & 0o777).try_into().unwrap()) - } - - /// Returns the 9-bit level 2 page table index. - pub fn p2_index(&self) -> u9 { - u9::new(((self.0 >> 12 >> 9) & 0o777).try_into().unwrap()) - } - - /// Returns the 9-bit level 3 page table index. - pub fn p3_index(&self) -> u9 { - u9::new(((self.0 >> 12 >> 9 >> 9) & 0o777).try_into().unwrap()) - } - - /// Returns the 9-bit level 4 page table index. - pub fn p4_index(&self) -> u9 { - u9::new(((self.0 >> 12 >> 9 >> 9 >> 9) & 0o777).try_into().unwrap()) - } -} - -impl fmt::Debug for VirtAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "VirtAddr({:#x})", self.0) - } -} - -impl Add for VirtAddr { - type Output = Self; - fn add(self, rhs: u64) -> Self::Output { - VirtAddr::new(self.0 + rhs) - } -} - -impl AddAssign for VirtAddr { - fn add_assign(&mut self, rhs: u64) { - *self = *self + rhs; - } -} - -impl Add for VirtAddr -where - u64: FromUsize, -{ - type Output = Self; - fn add(self, rhs: usize) -> Self::Output { - self + u64::from_usize(rhs) - } -} - -impl AddAssign for VirtAddr -where - u64: FromUsize, -{ - fn add_assign(&mut self, rhs: usize) { - self.add_assign(u64::from_usize(rhs)) - } -} - -impl Sub for VirtAddr { - type Output = Self; - fn sub(self, rhs: u64) -> Self::Output { - VirtAddr::new(self.0.checked_sub(rhs).unwrap()) - } -} - -impl SubAssign for VirtAddr { - fn sub_assign(&mut self, rhs: u64) { - *self = *self - rhs; - } -} - -impl Sub for VirtAddr -where - u64: FromUsize, -{ - type Output = Self; - fn sub(self, rhs: usize) -> Self::Output { - self - u64::from_usize(rhs) - } -} - -impl SubAssign for VirtAddr -where - u64: FromUsize, -{ - fn sub_assign(&mut self, rhs: usize) { - self.sub_assign(u64::from_usize(rhs)) - } -} - -impl Sub for VirtAddr { - type Output = u64; - fn sub(self, rhs: VirtAddr) -> Self::Output { - self.as_u64().checked_sub(rhs.as_u64()).unwrap() - } -} - -/// A passed `u64` was not a valid physical address. -/// -/// This means that bits 52 to 64 are not were not all null. -#[derive(Debug)] -pub struct PhysAddrNotValid(u64); - -impl PhysAddr { - /// Creates a new physical address. - /// - /// Panics if a bit in the range 52 to 64 is set. - pub fn new(addr: u64) -> PhysAddr { - assert_eq!( - addr.get_bits(52..64), - 0, - "physical addresses must not have any bits in the range 52 to 64 set" - ); - PhysAddr(addr) - } - - /// Tries to create a new physical address. - /// - /// Fails if any bits in the range 52 to 64 are set. - pub fn try_new(addr: u64) -> Result { - match addr.get_bits(52..64) { - 0 => Ok(PhysAddr(addr)), // address is valid - other => Err(PhysAddrNotValid(other)), - } - } - - /// Converts the address to an `u64`. - pub fn as_u64(self) -> u64 { - self.0 - } - - /// Convenience method for checking if a physical address is null. - pub fn is_null(&self) -> bool { - self.0 == 0 - } - - /// Aligns the physical address upwards to the given alignment. - /// - /// See the `align_up` function for more information. - pub fn align_up(self, align: U) -> Self - where - U: Into, - { - PhysAddr(align_up(self.0, align.into())) - } - - /// Aligns the physical address downwards to the given alignment. - /// - /// See the `align_down` function for more information. - pub fn align_down(self, align: U) -> Self - where - U: Into, - { - PhysAddr(align_down(self.0, align.into())) - } - - /// Checks whether the physical address has the demanded alignment. - pub fn is_aligned(self, align: U) -> bool - where - U: Into, - { - self.align_down(align) == self - } -} - -impl fmt::Debug for PhysAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "PhysAddr({:#x})", self.0) - } -} - -impl fmt::Binary for PhysAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) - } -} - -impl fmt::LowerHex for PhysAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) - } -} - -impl fmt::Octal for PhysAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) - } -} - -impl fmt::UpperHex for PhysAddr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0.fmt(f) - } -} - -impl Add for PhysAddr { - type Output = Self; - fn add(self, rhs: u64) -> Self::Output { - PhysAddr::new(self.0 + rhs) - } -} - -impl AddAssign for PhysAddr { - fn add_assign(&mut self, rhs: u64) { - *self = *self + rhs; - } -} - -impl Add for PhysAddr -where - u64: FromUsize, -{ - type Output = Self; - fn add(self, rhs: usize) -> Self::Output { - self + u64::from_usize(rhs) - } -} - -impl AddAssign for PhysAddr -where - u64: FromUsize, -{ - fn add_assign(&mut self, rhs: usize) { - self.add_assign(u64::from_usize(rhs)) - } -} - -impl Sub for PhysAddr { - type Output = Self; - fn sub(self, rhs: u64) -> Self::Output { - PhysAddr::new(self.0.checked_sub(rhs).unwrap()) - } -} - -impl SubAssign for PhysAddr { - fn sub_assign(&mut self, rhs: u64) { - *self = *self - rhs; - } -} - -impl Sub for PhysAddr -where - u64: FromUsize, -{ - type Output = Self; - fn sub(self, rhs: usize) -> Self::Output { - self - u64::from_usize(rhs) - } -} - -impl SubAssign for PhysAddr -where - u64: FromUsize, -{ - fn sub_assign(&mut self, rhs: usize) { - self.sub_assign(u64::from_usize(rhs)) - } -} - -impl Sub for PhysAddr { - type Output = u64; - fn sub(self, rhs: PhysAddr) -> Self::Output { - self.as_u64().checked_sub(rhs.as_u64()).unwrap() - } -} - -/// Align address downwards. -/// -/// Returns the greatest x with alignment `align` so that x <= addr. The alignment must be -/// a power of 2. -pub fn align_down(addr: u64, align: u64) -> u64 { - assert!(align.is_power_of_two(), "`align` must be a power of two"); - addr & !(align - 1) -} - -/// Align address upwards. -/// -/// Returns the smallest x with alignment `align` so that x >= addr. The alignment must be -/// a power of 2. -pub fn align_up(addr: u64, align: u64) -> u64 { - assert!(align.is_power_of_two(), "`align` must be a power of two"); - let align_mask = align - 1; - if addr & align_mask == 0 { - addr // already aligned - } else { - (addr | align_mask) + 1 - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - pub fn test_align_up() { - // align 1 - assert_eq!(align_up(0, 1), 0); - assert_eq!(align_up(1234, 1), 1234); - assert_eq!(align_up(0xffffffffffffffff, 1), 0xffffffffffffffff); - // align 2 - assert_eq!(align_up(0, 2), 0); - assert_eq!(align_up(1233, 2), 1234); - assert_eq!(align_up(0xfffffffffffffffe, 2), 0xfffffffffffffffe); - // address 0 - assert_eq!(align_up(0, 128), 0); - assert_eq!(align_up(0, 1), 0); - assert_eq!(align_up(0, 2), 0); - assert_eq!(align_up(0, 0x8000000000000000), 0); - } -} diff --git a/crate/aarch64/src/asm.rs b/crate/aarch64/src/asm.rs deleted file mode 100644 index bada049..0000000 --- a/crate/aarch64/src/asm.rs +++ /dev/null @@ -1,158 +0,0 @@ -//! Miscellaneous assembly instructions and functions - -use paging::PhysFrame; -use addr::{PhysAddr, VirtAddr}; -use regs::*; - -/// Returns the current stack pointer. -#[inline(always)] -pub fn sp() -> *const u8 { - let ptr: usize; - unsafe { - asm!("mov $0, sp" : "=r"(ptr)); - } - - ptr as *const u8 -} - -/// Returns the current point counter. -#[inline(always)] -pub unsafe fn get_pc() -> usize { - let pc: usize; - asm!("adr $0, ." : "=r"(pc)); - pc -} - -/// The classic no-op -#[inline] -pub fn nop() { - match () { - #[cfg(target_arch = "aarch64")] - () => unsafe { asm!("nop" :::: "volatile") }, - - #[cfg(not(target_arch = "aarch64"))] - () => unimplemented!(), - } -} - -/// Wait For Interrupt -#[inline] -pub fn wfi() { - match () { - #[cfg(target_arch = "aarch64")] - () => unsafe { asm!("wfi" :::: "volatile") }, - - #[cfg(not(target_arch = "aarch64"))] - () => unimplemented!(), - } -} - -/// Wait For Event -#[inline] -pub fn wfe() { - match () { - #[cfg(target_arch = "aarch64")] - () => unsafe { asm!("wfe" :::: "volatile") }, - - #[cfg(not(target_arch = "aarch64"))] - () => unimplemented!(), - } -} - -/// Exception return -/// -/// Will jump to wherever the corresponding link register points to, and -/// therefore never return. -#[inline] -pub fn eret() -> ! { - use core; - - match () { - #[cfg(target_arch = "aarch64")] - () => unsafe { - asm!("eret" :::: "volatile"); - core::intrinsics::unreachable() - }, - - #[cfg(not(target_arch = "aarch64"))] - () => unimplemented!(), - } -} - -/// Invalidate all TLB entries. -#[inline(always)] -pub fn tlb_invalidate_all() { - unsafe { - asm!( - "dsb ishst - tlbi vmalle1is - dsb ish - isb" - ); - } -} - -/// Invalidate TLB entries that would be used to translate the specified address. -#[inline(always)] -pub fn tlb_invalidate(vaddr: VirtAddr) { - unsafe { - asm!( - "dsb ishst - tlbi vaae1is, $0 - dsb ish - isb" :: "r"(vaddr.as_u64() >> 12) - ); - } -} - -/// Invalidate all instruction caches in Inner Shareable domain to Point of Unification. -#[inline(always)] -pub fn flush_icache_all() { - unsafe { - asm!( - "ic ialluis - dsb ish - isb" - ); - } -} - -/// Address Translate. -#[inline(always)] -pub fn address_translate(vaddr: usize) -> usize { - let paddr: usize; - unsafe { - asm!("at S1E1R, $1; mrs $0, par_el1" : "=r"(paddr) : "r"(vaddr)); - } - paddr -} - -/// Read TTBRx_EL1 as PhysFrame -pub fn ttbr_el1_read(which: u8) -> PhysFrame { - let baddr = match which { - 0 => TTBR0_EL1.get_baddr(), - 1 => TTBR1_EL1.get_baddr(), - _ => 0, - }; - PhysFrame::containing_address(PhysAddr::new(baddr)) -} - -/// Write TTBRx_EL1 from PhysFrame -pub fn ttbr_el1_write(which: u8, frame: PhysFrame) { - let baddr = frame.start_address().as_u64(); - match which { - 0 => TTBR0_EL1.set_baddr(baddr), - 1 => TTBR1_EL1.set_baddr(baddr), - _ => {} - }; -} - -/// write TTBRx_EL1 from PhysFrame and ASID -pub fn ttbr_el1_write_asid(which: u8, asid: u16, frame: PhysFrame) { - let baddr = frame.start_address().as_u64(); - match which { - 0 => TTBR0_EL1.write(TTBR0_EL1::ASID.val(asid as u64) + TTBR0_EL1::BADDR.val(baddr >> 1)), - 1 => TTBR1_EL1.write(TTBR1_EL1::ASID.val(asid as u64) + TTBR1_EL1::BADDR.val(baddr >> 1)), - _ => {} - }; -} diff --git a/crate/aarch64/src/barrier.rs b/crate/aarch64/src/barrier.rs deleted file mode 100644 index 3211480..0000000 --- a/crate/aarch64/src/barrier.rs +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -// Borrow implementations from the pending upstream ACLE implementation until it -// is merged. Afterwards, we'll probably just reexport them, hoping that the API -// doesn't change. -// -// https://github.com/rust-lang-nursery/stdsimd/pull/557 - -mod sealed { - pub trait Dmb { - unsafe fn __dmb(&self); - } - - pub trait Dsb { - unsafe fn __dsb(&self); - } - - pub trait Isb { - unsafe fn __isb(&self); - } -} - -macro_rules! dmb_dsb { - ($A:ident) => { - impl sealed::Dmb for $A { - #[inline(always)] - unsafe fn __dmb(&self) { - asm!(concat!("DMB ", stringify!($A)) : : : "memory" : "volatile") - } - } - impl sealed::Dsb for $A { - #[inline(always)] - unsafe fn __dsb(&self) { - asm!(concat!("DSB ", stringify!($A)) : : : "memory" : "volatile") - } - } - }; -} - -pub struct SY; -pub struct ISH; -pub struct ISHST; - -dmb_dsb!(SY); -dmb_dsb!(ISH); -dmb_dsb!(ISHST); - -impl sealed::Isb for SY { - #[inline(always)] - unsafe fn __isb(&self) { - asm!("ISB SY" : : : "memory" : "volatile") - } -} - -#[inline(always)] -pub unsafe fn dmb(arg: A) -where - A: sealed::Dmb, -{ - arg.__dmb() -} - -#[inline(always)] -pub unsafe fn dsb(arg: A) -where - A: sealed::Dsb, -{ - arg.__dsb() -} - -#[inline(always)] -pub unsafe fn isb(arg: A) -where - A: sealed::Isb, -{ - arg.__isb() -} diff --git a/crate/aarch64/src/lib.rs b/crate/aarch64/src/lib.rs deleted file mode 100644 index bb8bf72..0000000 --- a/crate/aarch64/src/lib.rs +++ /dev/null @@ -1,29 +0,0 @@ -#![no_std] -//#![deny(warnings)] -#![feature(asm)] -#![feature(const_fn)] -#![feature(core_intrinsics)] -#![feature(try_from)] - -extern crate bare_metal; -#[macro_use] -extern crate register; -#[macro_use] -extern crate bitflags; -extern crate bit_field; -extern crate os_bootinfo; -extern crate usize_conversions; - -/// Provides the non-standard-width integer types `u2`–`u63`. -/// -/// We use these integer types in various APIs, for example `u9` for page tables indices. -pub extern crate ux; - -pub use addr::{align_down, align_up, PhysAddr, VirtAddr}; - -pub mod asm; -pub mod addr; -pub mod paging; -pub mod barrier; -pub mod regs; - diff --git a/crate/aarch64/src/paging/frame_alloc.rs b/crate/aarch64/src/paging/frame_alloc.rs deleted file mode 100644 index f3ea4c8..0000000 --- a/crate/aarch64/src/paging/frame_alloc.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Traits for abstracting away frame allocation and deallocation. - -use paging::{PageSize, PhysFrame}; - -/// A trait for types that can allocate a frame of memory. -pub trait FrameAllocator { - /// Allocate a frame of the appropriate size and return it if possible. - fn alloc(&mut self) -> Option>; -} - -/// A trait for types that can deallocate a frame of memory. -pub trait FrameDeallocator { - /// Deallocate the given frame of memory. - fn dealloc(&mut self, frame: PhysFrame); -} diff --git a/crate/aarch64/src/paging/memory_attribute.rs b/crate/aarch64/src/paging/memory_attribute.rs deleted file mode 100644 index 4a24071..0000000 --- a/crate/aarch64/src/paging/memory_attribute.rs +++ /dev/null @@ -1,64 +0,0 @@ -//!Memory region attributes (D4.5, page 2174) - -use super::{PageTableAttribute, MEMORY_ATTRIBUTE}; -use regs::*; - -pub trait MairType { - const INDEX: u64; - - #[inline] - fn config_value() -> u64; - - #[inline] - fn attr_value() -> PageTableAttribute; -} - -pub enum MairDevice {} -pub enum MairNormal {} -pub enum MairNormalNonCacheable {} - -impl MairType for MairDevice { - const INDEX: u64 = 0; - - #[inline] - fn config_value() -> u64 { - (MAIR_ATTR::Attr_HIGH::Device + MAIR_ATTR::Attr_LOW_DEVICE::Device_nGnRE).value - } - - #[inline] - fn attr_value() -> PageTableAttribute { - MEMORY_ATTRIBUTE::SH::OuterShareable + MEMORY_ATTRIBUTE::AttrIndx.val(Self::INDEX) - } -} - -impl MairType for MairNormal { - const INDEX: u64 = 1; - - #[inline] - fn config_value() -> u64 { - (MAIR_ATTR::Attr_HIGH::Memory_OuterWriteBack_NonTransient_ReadAlloc_WriteAlloc - + MAIR_ATTR::Attr_LOW_MEMORY::InnerWriteBack_NonTransient_ReadAlloc_WriteAlloc) - .value - } - - #[inline] - fn attr_value() -> PageTableAttribute { - MEMORY_ATTRIBUTE::SH::InnerShareable + MEMORY_ATTRIBUTE::AttrIndx.val(Self::INDEX) - } -} - -impl MairType for MairNormalNonCacheable { - const INDEX: u64 = 2; - - #[inline] - fn config_value() -> u64 { - (MAIR_ATTR::Attr_HIGH::Memory_OuterNonCacheable - + MAIR_ATTR::Attr_LOW_MEMORY::InnerNonCacheable) - .value - } - - #[inline] - fn attr_value() -> PageTableAttribute { - MEMORY_ATTRIBUTE::SH::OuterShareable + MEMORY_ATTRIBUTE::AttrIndx.val(Self::INDEX) - } -} diff --git a/crate/aarch64/src/paging/mod.rs b/crate/aarch64/src/paging/mod.rs deleted file mode 100644 index 552d6f6..0000000 --- a/crate/aarch64/src/paging/mod.rs +++ /dev/null @@ -1,549 +0,0 @@ -//! Abstractions for page tables and other paging related structures. -//! -//! Page tables translate virtual memory “pages” to physical memory “frames”. - -pub use self::frame_alloc::*; -pub use self::page_table::*; -#[cfg(target_arch = "aarch64")] -pub use self::recursive::*; - -use core::fmt; -use core::marker::PhantomData; -use core::ops::{Add, AddAssign, Sub, SubAssign}; -use os_bootinfo; -use ux::*; -use addr::{PhysAddr, VirtAddr}; - -mod frame_alloc; -mod page_table; -mod recursive; - -pub mod memory_attribute; - -/// Trait for abstracting over the three possible block/page sizes on aarch64, 4KiB, 2MiB, 1GiB. -pub trait PageSize: Copy + Eq + PartialOrd + Ord { - /// The page size in bytes. - const SIZE: u64; - - /// A string representation of the page size for debug output. - const SIZE_AS_DEBUG_STR: &'static str; -} - -/// This trait is implemented for 4KiB and 2MiB pages, but not for 1GiB pages. -pub trait NotGiantPageSize: PageSize {} - -/// A standard 4KiB page. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum Size4KiB {} - -/// A “huge” 2MiB page. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum Size2MiB {} - -/// A “giant” 1GiB page. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum Size1GiB {} - -impl PageSize for Size4KiB { - const SIZE: u64 = 4096; - const SIZE_AS_DEBUG_STR: &'static str = "4KiB"; -} - -impl NotGiantPageSize for Size4KiB {} - -impl PageSize for Size2MiB { - const SIZE: u64 = Size4KiB::SIZE * 512; - const SIZE_AS_DEBUG_STR: &'static str = "2MiB"; -} - -impl NotGiantPageSize for Size2MiB {} - -impl PageSize for Size1GiB { - const SIZE: u64 = Size2MiB::SIZE * 512; - const SIZE_AS_DEBUG_STR: &'static str = "1GiB"; -} - -/// A virtual memory page. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[repr(C)] -pub struct Page { - start_address: VirtAddr, - size: PhantomData, -} - -impl Page { - /// Returns the page that starts at the given virtual address. - /// - /// Returns an error if the address is not correctly aligned (i.e. is not a valid page start). - pub fn from_start_address(address: VirtAddr) -> Result { - if !address.is_aligned(S::SIZE) { - return Err(()); - } - Ok(Page::containing_address(address)) - } - - /// Returns the page that contains the given virtual address. - pub fn containing_address(address: VirtAddr) -> Self { - Page { - start_address: address.align_down(S::SIZE), - size: PhantomData, - } - } - - /// Returns the start address of the page. - pub fn start_address(&self) -> VirtAddr { - self.start_address - } - - /// Returns the size the page (4KB, 2MB or 1GB). - pub const fn size(&self) -> u64 { - S::SIZE - } - - /// Returns the level 4 page table index of this page. - pub fn va_range_bits(&self) -> u16 { - self.start_address().va_range_bits() - } - - /// Returns the level 4 page table index of this page. - pub fn p4_index(&self) -> u9 { - self.start_address().p4_index() - } - - /// Returns the level 3 page table index of this page. - pub fn p3_index(&self) -> u9 { - self.start_address().p3_index() - } - - /// Returns a range of pages, exclusive `end`. - pub fn range(start: Self, end: Self) -> PageRange { - PageRange { start, end } - } - - /// Returns a range of pages, inclusive `end`. - pub fn range_inclusive(start: Self, end: Self) -> PageRangeInclusive { - PageRangeInclusive { start, end } - } - - pub fn of_addr(address: usize) -> Self { - Self::containing_address(VirtAddr::new(address as u64)) - } - - pub fn range_of(begin: usize, end: usize) -> PageRange { - Self::range(Page::of_addr(begin), Page::of_addr(end - 1) + 1) - } -} - -impl Page { - /// Returns the level 2 page table index of this page. - pub fn p2_index(&self) -> u9 { - self.start_address().p2_index() - } -} - -impl Page { - /// Returns the 1GiB memory page with the specified page table indices. - pub fn from_page_table_indices_1gib(p4_index: u9, p3_index: u9) -> Self { - use bit_field::BitField; - - let mut addr = 0; - addr.set_bits(39..48, u64::from(p4_index)); - addr.set_bits(30..39, u64::from(p3_index)); - Page::containing_address(VirtAddr::new(addr)) - } -} - -impl Page { - /// Returns the 2MiB memory page with the specified page table indices. - pub fn from_page_table_indices_2mib(p4_index: u9, p3_index: u9, p2_index: u9) -> Self { - use bit_field::BitField; - - let mut addr = 0; - addr.set_bits(39..48, u64::from(p4_index)); - addr.set_bits(30..39, u64::from(p3_index)); - addr.set_bits(21..30, u64::from(p2_index)); - Page::containing_address(VirtAddr::new(addr)) - } -} - -impl Page { - /// Returns the 4KiB memory page with the specified page table indices. - pub fn from_page_table_indices(p4_index: u9, p3_index: u9, p2_index: u9, p1_index: u9) -> Self { - use bit_field::BitField; - - let mut addr = 0; - addr.set_bits(39..48, u64::from(p4_index)); - addr.set_bits(30..39, u64::from(p3_index)); - addr.set_bits(21..30, u64::from(p2_index)); - addr.set_bits(12..21, u64::from(p1_index)); - Page::containing_address(VirtAddr::new(addr)) - } - - /// Returns the level 1 page table index of this page. - pub fn p1_index(&self) -> u9 { - self.start_address().p1_index() - } -} - -impl fmt::Debug for Page { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_fmt(format_args!( - "Page[{}]({:#x})", - S::SIZE_AS_DEBUG_STR, - self.start_address().as_u64() - )) - } -} - -impl Add for Page { - type Output = Self; - fn add(self, rhs: u64) -> Self::Output { - Page::containing_address(self.start_address() + rhs * u64::from(S::SIZE)) - } -} - -impl AddAssign for Page { - fn add_assign(&mut self, rhs: u64) { - *self = self.clone() + rhs; - } -} - -impl Sub for Page { - type Output = Self; - fn sub(self, rhs: u64) -> Self::Output { - Page::containing_address(self.start_address() - rhs * u64::from(S::SIZE)) - } -} - -impl SubAssign for Page { - fn sub_assign(&mut self, rhs: u64) { - *self = self.clone() - rhs; - } -} - -impl Sub for Page { - type Output = u64; - fn sub(self, rhs: Self) -> Self::Output { - (self.start_address - rhs.start_address) / S::SIZE - } -} - -/// A range of pages with exclusive upper bound. -#[derive(Clone, Copy, PartialEq, Eq)] -#[repr(C)] -pub struct PageRange { - /// The start of the range, inclusive. - pub start: Page, - /// The end of the range, exclusive. - pub end: Page, -} - -impl PageRange { - /// Returns wether this range contains no pages. - pub fn is_empty(&self) -> bool { - !(self.start < self.end) - } -} - -impl Iterator for PageRange { - type Item = Page; - - fn next(&mut self) -> Option { - if self.start < self.end { - let page = self.start.clone(); - self.start += 1; - Some(page) - } else { - None - } - } -} - -impl PageRange { - /// Converts the range of 2MiB pages to a range of 4KiB pages. - pub fn as_4kib_page_range(self) -> PageRange { - PageRange { - start: Page::containing_address(self.start.start_address()), - end: Page::containing_address(self.end.start_address()), - } - } -} - -impl fmt::Debug for PageRange { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("PageRange") - .field("start", &self.start) - .field("end", &self.end) - .finish() - } -} - -/// A range of pages with inclusive upper bound. -#[derive(Clone, Copy, PartialEq, Eq)] -#[repr(C)] -pub struct PageRangeInclusive { - /// The start of the range, inclusive. - pub start: Page, - /// The end of the range, inclusive. - pub end: Page, -} - -impl PageRangeInclusive { - /// Returns wether this range contains no pages. - pub fn is_empty(&self) -> bool { - !(self.start <= self.end) - } -} - -impl Iterator for PageRangeInclusive { - type Item = Page; - - fn next(&mut self) -> Option { - if self.start <= self.end { - let page = self.start.clone(); - self.start += 1; - Some(page) - } else { - None - } - } -} - -impl fmt::Debug for PageRangeInclusive { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("PageRangeInclusive") - .field("start", &self.start) - .field("end", &self.end) - .finish() - } -} - -/// A physical memory frame. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[repr(C)] -pub struct PhysFrame { - start_address: PhysAddr, - size: PhantomData, -} - -impl PhysFrame { - /// Returns the frame that starts at the given virtual address. - /// - /// Returns an error if the address is not correctly aligned (i.e. is not a valid frame start). - pub fn from_start_address(address: PhysAddr) -> Result { - if !address.is_aligned(S::SIZE) { - return Err(()); - } - Ok(PhysFrame::containing_address(address)) - } - - /// Returns the frame that contains the given physical address. - pub fn containing_address(address: PhysAddr) -> Self { - PhysFrame { - start_address: address.align_down(S::SIZE), - size: PhantomData, - } - } - - /// Returns the start address of the frame. - pub fn start_address(&self) -> PhysAddr { - self.start_address - } - - /// Returns the size the frame (4KB, 2MB or 1GB). - pub fn size(&self) -> u64 { - S::SIZE - } - - /// Returns a range of frames, exclusive `end`. - pub fn range(start: PhysFrame, end: PhysFrame) -> PhysFrameRange { - PhysFrameRange { start, end } - } - - /// Returns a range of frames, inclusive `end`. - pub fn range_inclusive(start: PhysFrame, end: PhysFrame) -> PhysFrameRangeInclusive { - PhysFrameRangeInclusive { start, end } - } - - pub fn of_addr(address: usize) -> Self { - Self::containing_address(PhysAddr::new(address as u64)) - } - - pub fn range_of(begin: usize, end: usize) -> PhysFrameRange { - Self::range(Self::of_addr(begin), Self::of_addr(end - 1) + 1) - } -} - -impl fmt::Debug for PhysFrame { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_fmt(format_args!( - "PhysFrame[{}]({:#x})", - S::SIZE_AS_DEBUG_STR, - self.start_address().as_u64() - )) - } -} - -impl Add for PhysFrame { - type Output = Self; - fn add(self, rhs: u64) -> Self::Output { - PhysFrame::containing_address(self.start_address() + rhs * u64::from(S::SIZE)) - } -} - -impl AddAssign for PhysFrame { - fn add_assign(&mut self, rhs: u64) { - *self = self.clone() + rhs; - } -} - -impl Sub for PhysFrame { - type Output = Self; - fn sub(self, rhs: u64) -> Self::Output { - PhysFrame::containing_address(self.start_address() - rhs * u64::from(S::SIZE)) - } -} - -impl SubAssign for PhysFrame { - fn sub_assign(&mut self, rhs: u64) { - *self = self.clone() - rhs; - } -} - -impl Sub> for PhysFrame { - type Output = u64; - fn sub(self, rhs: PhysFrame) -> Self::Output { - (self.start_address - rhs.start_address) / S::SIZE - } -} - -/// An range of physical memory frames, exclusive the upper bound. -#[derive(Clone, Copy, PartialEq, Eq)] -#[repr(C)] -pub struct PhysFrameRange { - /// The start of the range, inclusive. - pub start: PhysFrame, - /// The end of the range, exclusive. - pub end: PhysFrame, -} - -impl PhysFrameRange { - /// Returns whether the range contains no frames. - pub fn is_empty(&self) -> bool { - !(self.start < self.end) - } -} - -impl Iterator for PhysFrameRange { - type Item = PhysFrame; - - fn next(&mut self) -> Option { - if self.start < self.end { - let frame = self.start.clone(); - self.start += 1; - Some(frame) - } else { - None - } - } -} - -impl From for PhysFrameRange { - fn from(range: os_bootinfo::FrameRange) -> Self { - PhysFrameRange { - start: PhysFrame::from_start_address(PhysAddr::new(range.start_addr())).unwrap(), - end: PhysFrame::from_start_address(PhysAddr::new(range.end_addr())).unwrap(), - } - } -} - -impl Into for PhysFrameRange { - fn into(self) -> os_bootinfo::FrameRange { - os_bootinfo::FrameRange::new( - self.start.start_address().as_u64(), - self.end.start_address().as_u64(), - ) - } -} - -impl fmt::Debug for PhysFrameRange { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("PhysFrameRange") - .field("start", &self.start) - .field("end", &self.end) - .finish() - } -} - -/// An range of physical memory frames, inclusive the upper bound. -#[derive(Clone, Copy, PartialEq, Eq)] -#[repr(C)] -pub struct PhysFrameRangeInclusive { - /// The start of the range, inclusive. - pub start: PhysFrame, - /// The start of the range, exclusive. - pub end: PhysFrame, -} - -impl PhysFrameRangeInclusive { - /// Returns whether the range contains no frames. - pub fn is_empty(&self) -> bool { - !(self.start <= self.end) - } -} - -impl Iterator for PhysFrameRangeInclusive { - type Item = PhysFrame; - - fn next(&mut self) -> Option { - if self.start <= self.end { - let frame = self.start.clone(); - self.start += 1; - Some(frame) - } else { - None - } - } -} - -impl fmt::Debug for PhysFrameRangeInclusive { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("PhysFrameRangeInclusive") - .field("start", &self.start) - .field("end", &self.end) - .finish() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - pub fn test_page_ranges() { - let page_size = Size4KiB::SIZE; - let number = 1000; - - let start_addr = VirtAddr::new(0xdeadbeaf); - let start: Page = Page::containing_address(start_addr); - let end = start.clone() + number; - - let mut range = Page::range(start.clone(), end.clone()); - for i in 0..number { - assert_eq!( - range.next(), - Some(Page::containing_address(start_addr + page_size * i)) - ); - } - assert_eq!(range.next(), None); - - let mut range_inclusive = Page::range_inclusive(start, end); - for i in 0..=number { - assert_eq!( - range_inclusive.next(), - Some(Page::containing_address(start_addr + page_size * i)) - ); - } - assert_eq!(range_inclusive.next(), None); - } -} diff --git a/crate/aarch64/src/paging/page_table.rs b/crate/aarch64/src/paging/page_table.rs deleted file mode 100644 index 6d33fa6..0000000 --- a/crate/aarch64/src/paging/page_table.rs +++ /dev/null @@ -1,267 +0,0 @@ -use core::fmt; -use core::ops::{Index, IndexMut}; - -use super::{PhysFrame, PageSize}; -use addr::PhysAddr; - -use usize_conversions::usize_from; -use ux::*; - -use register::FieldValue; -use register::cpu::RegisterReadWrite; - -/// Memory attribute fields mask -const MEMORY_ATTR_MASK: u64 = (MEMORY_ATTRIBUTE::SH.mask << MEMORY_ATTRIBUTE::SH.shift) - | (MEMORY_ATTRIBUTE::AttrIndx.mask << MEMORY_ATTRIBUTE::AttrIndx.shift); -/// Output address mask -const ADDR_MASK: u64 = 0x0000_ffff_ffff_f000; -/// Other flags mask -const FLAGS_MASK: u64 = !(MEMORY_ATTR_MASK | ADDR_MASK); - -/// Memory attribute fields -pub type PageTableAttribute = FieldValue; - -/// The error returned by the `PageTableEntry::frame` method. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum FrameError { - /// The entry does not have the `PRESENT` flag set, so it isn't currently mapped to a frame. - FrameNotPresent, - /// The entry does have the `HUGE_PAGE` flag set. The `frame` method has a standard 4KiB frame - /// as return type, so a huge frame can't be returned. - HugeFrame, -} - -/// A 64-bit page table entry. -#[derive(Clone)] -#[repr(transparent)] -pub struct PageTableEntry { - pub entry: u64, -} - -impl RegisterReadWrite for PageTableEntry { - #[inline] - fn get(&self) -> u64 { - self.entry - } - - #[inline] - fn set(&self, value: u64) { - unsafe { *(&self.entry as *const u64 as *mut u64) = value } - } -} - -impl PageTableEntry { - /// Returns whether this entry is zero. - #[inline] - pub fn is_unused(&self) -> bool { - self.entry == 0 - } - - /// Sets this entry to zero. - #[inline] - pub fn set_unused(&mut self) { - self.entry = 0; - } - - /// Returns the flags of this entry. - #[inline] - pub fn flags(&self) -> PageTableFlags { - PageTableFlags::from_bits_truncate(self.entry) - } - - /// Returns the physical address mapped by this entry, might be zero. - #[inline] - pub fn addr(&self) -> PhysAddr { - PhysAddr::new(self.entry & ADDR_MASK) - } - - /// Returns the memory attribute fields of this entry. - #[inline] - pub fn attr(&self) -> PageTableAttribute { - PageTableAttribute::new(MEMORY_ATTR_MASK, 0, self.entry & MEMORY_ATTR_MASK) - } - - /// Returns the physical frame mapped by this entry. - /// - /// Returns the following errors: - /// - /// - `FrameError::FrameNotPresent` if the entry doesn't have the `PRESENT` flag set. - /// - `FrameError::HugeFrame` if the entry has the `HUGE_PAGE` flag set (for huge pages the - /// `addr` function must be used) - pub fn frame(&self) -> Result { - if !self.flags().contains(PageTableFlags::VALID) { - Err(FrameError::FrameNotPresent) - } else if !self.flags().contains(PageTableFlags::TABLE_OR_PAGE) { - // is a huge page (block) - Err(FrameError::HugeFrame) - } else { - Ok(PhysFrame::containing_address(self.addr())) - } - } - - /// Map the entry to the specified physical frame with the specified flags and memory attribute. - pub fn set_frame(&mut self, frame: PhysFrame, flags: PageTableFlags, attr: PageTableAttribute) { - // is not a block - assert!(flags.contains(PageTableFlags::TABLE_OR_PAGE)); - self.set(frame.start_address().as_u64() | flags.bits() | attr.value); - } - - /// The descriptor gives the base address of a block of memory, and the attributes for that - /// memory region. - pub fn set_block(&mut self, addr: PhysAddr, flags: PageTableFlags, attr: PageTableAttribute) { - // is a block - assert!(!flags.contains(PageTableFlags::TABLE_OR_PAGE)); - self.set(addr.align_down(S::SIZE).as_u64() | flags.bits() | attr.value); - } - - /// Map the entry to the specified physical address with the specified flags. - pub fn modify_addr(&mut self, addr: PhysAddr) { - self.entry = (self.entry & !ADDR_MASK) | addr.as_u64(); - } - - /// Sets the flags of this entry. - pub fn modify_flags(&mut self, flags: PageTableFlags) { - self.entry = (self.entry & !FLAGS_MASK) | flags.bits(); - } - - /// Sets the flags of this entry. - pub fn modify_attr(&mut self, attr: PageTableAttribute) { - self.entry = (self.entry & !MEMORY_ATTR_MASK) | attr.value; - } -} - -impl fmt::Debug for PageTableEntry { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut f = f.debug_struct("PageTableEntry"); - f.field("value", &self.entry); - f.field("addr", &self.addr()); - f.field("flags", &self.flags()); - f.field("attr", &self.attr().value); - f.finish() - } -} - -register_bitfields! {u64, - // Memory attribute fields in the VMSAv8-64 translation table format descriptors (Page 2148~2152) - MEMORY_ATTRIBUTE [ - /// Shareability field - SH OFFSET(8) NUMBITS(2) [ - NonShareable = 0b00, - OuterShareable = 0b10, - InnerShareable = 0b11 - ], - - /// Memory attributes index into the MAIR_EL1 register - AttrIndx OFFSET(2) NUMBITS(3) [] - ] -} - -bitflags! { - /// Possible flags for a page table entry. - pub struct PageTableFlags: u64 { - /// identifies whether the descriptor is valid - const VALID = 1 << 0; - /// the descriptor type - /// 0, Block - /// 1, Table/Page - const TABLE_OR_PAGE = 1 << 1; - /// Access permission: accessable at EL0 - const AP_EL0 = 1 << 6; - /// Access permission: read-only - const AP_RO = 1 << 7; - /// Access flag - const AF = 1 << 10; - /// not global bit - const nG = 1 << 11; - /// Dirty Bit Modifier - const DBM = 1 << 51; - - /// A hint bit indicating that the translation table entry is one of a contiguous set or - /// entries - const Contiguous = 1 << 52; - /// Privileged Execute-never - const PXN = 1 << 53; - /// Execute-never/Unprivileged execute-never - const XN = 1 << 54; - - /// Software Dirty Bit Modifier - const WRITE = 1 << 51; - /// Software dirty bit - const DIRTY = 1 << 55; - /// Software swapped bit - const SWAPPED = 1 << 56; - /// Software writable shared bit for COW - const WRITABLE_SHARED = 1 << 57; - /// Software readonly shared bit for COW - const READONLY_SHARED = 1 << 58; - - /// Privileged Execute-never for table descriptors - const PXNTable = 1 << 59; - /// Execute-never/Unprivileged execute-never for table descriptors - const XNTable = 1 << 60; - } -} - -impl Default for PageTableFlags { - #[inline] - fn default() -> Self { - Self::VALID | Self::TABLE_OR_PAGE | Self::AF | Self::WRITE | Self::PXN | Self::XN - } -} - -/// The number of entries in a page table. -const ENTRY_COUNT: usize = 512; - -/// Represents a page table. -/// -/// Always page-sized. -/// -/// This struct implements the `Index` and `IndexMut` traits, so the entries can be accessed -/// through index operations. For example, `page_table[15]` returns the 15th page table entry. -#[repr(transparent)] -pub struct PageTable { - entries: [PageTableEntry; ENTRY_COUNT], -} - -impl PageTable { - /// Clears all entries. - pub fn zero(&mut self) { - for entry in self.entries.iter_mut() { - entry.set_unused(); - } - } -} - -impl Index for PageTable { - type Output = PageTableEntry; - - fn index(&self, index: usize) -> &Self::Output { - &self.entries[index] - } -} - -impl IndexMut for PageTable { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.entries[index] - } -} - -impl Index for PageTable { - type Output = PageTableEntry; - - fn index(&self, index: u9) -> &Self::Output { - &self.entries[usize_from(u16::from(index))] - } -} - -impl IndexMut for PageTable { - fn index_mut(&mut self, index: u9) -> &mut Self::Output { - &mut self.entries[usize_from(u16::from(index))] - } -} - -impl fmt::Debug for PageTable { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.entries[..].fmt(f) - } -} diff --git a/crate/aarch64/src/paging/recursive.rs b/crate/aarch64/src/paging/recursive.rs deleted file mode 100644 index a1633ff..0000000 --- a/crate/aarch64/src/paging/recursive.rs +++ /dev/null @@ -1,425 +0,0 @@ -#![cfg(target_arch = "aarch64")] - -use paging::{ - frame_alloc::FrameAllocator, - page_table::{FrameError, PageTable, PageTableEntry, PageTableFlags}, - NotGiantPageSize, Page, PageSize, PhysFrame, Size4KiB, -}; -use paging::{page_table::PageTableFlags as Flags, PageTableAttribute, memory_attribute::*}; -use asm::{ttbr_el1_read, tlb_invalidate}; -use barrier; -use ux::u9; -use addr::{PhysAddr, VirtAddr}; - -/// This type represents a page whose mapping has changed in the page table. -/// -/// The old mapping might be still cached in the translation lookaside buffer (TLB), so it needs -/// to be flushed from the TLB before it's accessed. This type is returned from function that -/// change the mapping of a page to ensure that the TLB flush is not forgotten. -#[derive(Debug)] -#[must_use = "Page Table changes must be flushed or ignored."] -pub struct MapperFlush(Page); - -impl MapperFlush { - /// Create a new flush promise - fn new(page: Page) -> Self { - MapperFlush(page) - } - - /// Flush the page from the TLB to ensure that the newest mapping is used. - pub fn flush(self) { - tlb_invalidate(self.0.start_address()); - } - - /// Don't flush the TLB and silence the “must be used” warning. - pub fn ignore(self) {} -} - -/// A trait for common page table operations. -pub trait Mapper { - /// Creates a new mapping in the page table. - /// - /// This function might need additional physical frames to create new page tables. These - /// frames are allocated from the `allocator` argument. At most three frames are required. - fn map_to( - &mut self, - page: Page, - frame: PhysFrame, - flags: PageTableFlags, - attr: PageTableAttribute, - allocator: &mut A, - ) -> Result, MapToError> - where - A: FrameAllocator; - - /// Removes a mapping from the page table and returns the frame that used to be mapped. - /// - /// Note that no page tables or pages are deallocated. - fn unmap(&mut self, page: Page) -> Result<(PhysFrame, MapperFlush), UnmapError>; - - /// Updates the flags of an existing mapping. - fn update_flags( - &mut self, - page: Page, - flags: PageTableFlags, - ) -> Result, FlagUpdateError>; - - /// Return the frame that the specified page is mapped to. - fn translate_page(&self, page: Page) -> Option>; - - /// Maps the given frame to the virtual page with the same address. - fn identity_map( - &mut self, - frame: PhysFrame, - flags: PageTableFlags, - attr: PageTableAttribute, - allocator: &mut A, - ) -> Result, MapToError> - where - A: FrameAllocator, - S: PageSize, - Self: Mapper, - { - let page = Page::containing_address(VirtAddr::new(frame.start_address().as_u64())); - self.map_to(page, frame, flags, attr, allocator) - } -} - -/// A recursive page table is a last level page table with an entry mapped to the table itself. -/// -/// This recursive mapping allows accessing all page tables in the hierarchy: -/// -/// - To access the level 4 page table, we “loop“ (i.e. follow the recursively mapped entry) four -/// times. -/// - To access a level 3 page table, we “loop” three times and then use the level 4 index. -/// - To access a level 2 page table, we “loop” two times, then use the level 4 index, then the -/// level 3 index. -/// - To access a level 1 page table, we “loop” once, then use the level 4 index, then the -/// level 3 index, then the level 2 index. -/// -/// This struct implements the `Mapper` trait. -#[derive(Debug)] -pub struct RecursivePageTable<'a> { - p4: &'a mut PageTable, - recursive_index: u9, -} - -/// An error indicating that the given page table is not recursively mapped. -/// -/// Returned from `RecursivePageTable::new`. -#[derive(Debug)] -pub struct NotRecursivelyMapped; - -/// This error is returned from `map_to` and similar methods. -#[derive(Debug)] -pub enum MapToError { - /// An additional frame was needed for the mapping process, but the frame allocator - /// returned `None`. - FrameAllocationFailed, - /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the - /// given page is part of an already mapped huge page. - ParentEntryHugePage, - /// The given page is already mapped to a physical frame. - PageAlreadyMapped, -} - -/// An error indicating that an `unmap` call failed. -#[derive(Debug)] -pub enum UnmapError { - /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the - /// given page is part of a huge page and can't be freed individually. - ParentEntryHugePage, - /// The given page is not mapped to a physical frame. - PageNotMapped, - /// The page table entry for the given page points to an invalid physical address. - InvalidFrameAddress(PhysAddr), -} - -/// An error indicating that an `update_flags` call failed. -#[derive(Debug)] -pub enum FlagUpdateError { - /// The given page is not mapped to a physical frame. - PageNotMapped, -} - -impl<'a> RecursivePageTable<'a> { - /// Creates a new RecursivePageTable from the passed level 4 PageTable. - /// - /// The page table must be recursively mapped, that means: - /// - /// - The page table must have one recursive entry, i.e. an entry that points to the table - /// itself. - /// - The reference must use that “loop”, i.e. be of the form `0o_xxx_xxx_xxx_xxx_0000` - /// where `xxx` is the recursive entry. - /// - The page table must be active, i.e. the CR3 register must contain its physical address. - /// - /// Otherwise `Err(NotRecursivelyMapped)` is returned. - pub fn new(table: &'a mut PageTable) -> Result { - let page = Page::containing_address(VirtAddr::new(table as *const _ as u64)); - let recursive_index = page.p4_index(); - - if page.p3_index() != recursive_index - || page.p2_index() != recursive_index - || page.p1_index() != recursive_index - { - return Err(NotRecursivelyMapped); - } - if Ok(ttbr_el1_read(page.start_address().va_range().unwrap() as u8)) != - table[recursive_index].frame() - { - return Err(NotRecursivelyMapped); - } - - Ok(RecursivePageTable { - p4: table, - recursive_index, - }) - } - - /// Creates a new RecursivePageTable without performing any checks. - /// - /// The `recursive_index` parameter must be the index of the recursively mapped entry. - pub unsafe fn new_unchecked(table: &'a mut PageTable, recursive_index: u9) -> Self { - RecursivePageTable { - p4: table, - recursive_index, - } - } - - /// Internal helper function to create the page table of the next level if needed. - /// - /// If the passed entry is unused, a new frame is allocated from the given allocator, zeroed, - /// and the entry is updated to that address. If the passed entry is already mapped, the next - /// table is returned directly. - /// - /// The `next_page_table` page must be the page of the next page table in the hierarchy. - /// - /// Returns `MapToError::FrameAllocationFailed` if the entry is unused and the allocator - /// returned `None`. Returns `MapToError::ParentEntryHugePage` if the `HUGE_PAGE` flag is set - /// in the passed entry. - unsafe fn create_next_table<'b, A>( - entry: &'b mut PageTableEntry, - next_table_page: Page, - allocator: &mut A, - ) -> Result<&'b mut PageTable, MapToError> - where - A: FrameAllocator, - { - /// This inner function is used to limit the scope of `unsafe`. - /// - /// This is a safe function, so we need to use `unsafe` blocks when we do something unsafe. - fn inner<'b, A>( - entry: &'b mut PageTableEntry, - next_table_page: Page, - allocator: &mut A, - ) -> Result<&'b mut PageTable, MapToError> - where - A: FrameAllocator, - { - let created; - - if entry.is_unused() { - if let Some(frame) = allocator.alloc() { - entry.set_frame(frame, Flags::default(), MairNormal::attr_value()); - created = true; - } else { - return Err(MapToError::FrameAllocationFailed); - } - } else { - created = false; - } - // is a huge page (block) - if !entry.flags().contains(Flags::TABLE_OR_PAGE) { - return Err(MapToError::ParentEntryHugePage); - } - - let page_table_ptr = next_table_page.start_address().as_mut_ptr(); - let page_table: &mut PageTable = unsafe { &mut *(page_table_ptr) }; - if created { - unsafe { barrier::dsb(barrier::ISHST); } - page_table.zero(); - } - Ok(page_table) - } - - inner(entry, next_table_page, allocator) - } - - pub fn p3_ptr(&self, page: Page) -> *mut PageTable { - self.p3_page(page).start_address().as_mut_ptr() - } - - pub fn p2_ptr(&self, page: Page) -> *mut PageTable { - self.p2_page(page).start_address().as_mut_ptr() - } - - pub fn p1_ptr(&self, page: Page) -> *mut PageTable { - self.p1_page(page).start_address().as_mut_ptr() - } - - fn p3_page(&self, page: Page) -> Page { - Page::from_page_table_indices( - self.recursive_index, - self.recursive_index, - self.recursive_index, - page.p4_index(), - ) - } - - fn p2_page(&self, page: Page) -> Page { - Page::from_page_table_indices( - self.recursive_index, - self.recursive_index, - page.p4_index(), - page.p3_index(), - ) - } - - fn p1_page(&self, page: Page) -> Page { - Page::from_page_table_indices( - self.recursive_index, - page.p4_index(), - page.p3_index(), - page.p2_index(), - ) - } -} - -impl<'a> Mapper for RecursivePageTable<'a> { - fn map_to( - &mut self, - page: Page, - frame: PhysFrame, - flags: PageTableFlags, - attr: PageTableAttribute, - allocator: &mut A, - ) -> Result, MapToError> - where - A: FrameAllocator, - { - let self_mut = unsafe { &mut *(self as *const _ as *mut Self) }; - let p4 = &mut self_mut.p4; - - let p3_page = self.p3_page(page); - let p3 = unsafe { Self::create_next_table(&mut p4[page.p4_index()], p3_page, allocator)? }; - - let p2_page = self.p2_page(page); - let p2 = unsafe { Self::create_next_table(&mut p3[page.p3_index()], p2_page, allocator)? }; - - let p1_page = self.p1_page(page); - let p1 = unsafe { Self::create_next_table(&mut p2[page.p2_index()], p1_page, allocator)? }; - - if !p1[page.p1_index()].is_unused() { - return Err(MapToError::PageAlreadyMapped); - } - p1[page.p1_index()].set_frame(frame, flags, attr); - - Ok(MapperFlush::new(page)) - } - - fn unmap( - &mut self, - page: Page, - ) -> Result<(PhysFrame, MapperFlush), UnmapError> { - let self_mut = unsafe { &mut *(self as *const _ as *mut Self) }; - let p4 = &mut self_mut.p4; - - let p4_entry = &p4[page.p4_index()]; - p4_entry.frame().map_err(|err| match err { - FrameError::FrameNotPresent => UnmapError::PageNotMapped, - FrameError::HugeFrame => UnmapError::ParentEntryHugePage, - })?; - - let p3 = unsafe { &mut *(self.p3_ptr(page)) }; - let p3_entry = &p3[page.p3_index()]; - p3_entry.frame().map_err(|err| match err { - FrameError::FrameNotPresent => UnmapError::PageNotMapped, - FrameError::HugeFrame => UnmapError::ParentEntryHugePage, - })?; - - let p2 = unsafe { &mut *(self.p2_ptr(page)) }; - let p2_entry = &p2[page.p2_index()]; - p2_entry.frame().map_err(|err| match err { - FrameError::FrameNotPresent => UnmapError::PageNotMapped, - FrameError::HugeFrame => UnmapError::ParentEntryHugePage, - })?; - - let p1 = unsafe { &mut *(self.p1_ptr(page)) }; - let p1_entry = &mut p1[page.p1_index()]; - - let frame = p1_entry.frame().map_err(|err| match err { - FrameError::FrameNotPresent => UnmapError::PageNotMapped, - FrameError::HugeFrame => UnmapError::ParentEntryHugePage, - })?; - - p1_entry.set_unused(); - Ok((frame, MapperFlush::new(page))) - } - - fn update_flags( - &mut self, - page: Page, - flags: PageTableFlags, - ) -> Result, FlagUpdateError> { - let self_mut = unsafe { &mut *(self as *const _ as *mut Self) }; - let p4 = &mut self_mut.p4; - - if p4[page.p4_index()].is_unused() { - return Err(FlagUpdateError::PageNotMapped); - } - - let p3 = unsafe { &mut *(self.p3_ptr(page)) }; - - if p3[page.p3_index()].is_unused() { - return Err(FlagUpdateError::PageNotMapped); - } - - let p2 = unsafe { &mut *(self.p2_ptr(page)) }; - - if p2[page.p2_index()].is_unused() { - return Err(FlagUpdateError::PageNotMapped); - } - - let p1 = unsafe { &mut *(self.p1_ptr(page)) }; - - if p1[page.p1_index()].is_unused() { - return Err(FlagUpdateError::PageNotMapped); - } - - p1[page.p1_index()].modify_flags(flags); - - Ok(MapperFlush::new(page)) - } - - fn translate_page(&self, page: Page) -> Option> { - let self_mut = unsafe { &mut *(self as *const _ as *mut Self) }; - let p4 = &mut self_mut.p4; - - if p4[page.p4_index()].is_unused() { - return None; - } - - let p3 = unsafe { &*(self.p3_ptr(page)) }; - let p3_entry = &p3[page.p3_index()]; - - if p3_entry.is_unused() { - return None; - } - - let p2 = unsafe { &*(self.p2_ptr(page)) }; - let p2_entry = &p2[page.p2_index()]; - - if p2_entry.is_unused() { - return None; - } - - let p1 = unsafe { &*(self.p1_ptr(page)) }; - let p1_entry = &p1[page.p1_index()]; - - if p1_entry.is_unused() { - return None; - } - - PhysFrame::from_start_address(p1_entry.addr()).ok() - } -} diff --git a/crate/aarch64/src/regs/cntfrq_el0.rs b/crate/aarch64/src/regs/cntfrq_el0.rs deleted file mode 100644 index df56ac9..0000000 --- a/crate/aarch64/src/regs/cntfrq_el0.rs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! Counter-timer Frequency register - EL0 -//! -//! This register is provided so that software can discover the frequency of the -//! system counter. It must be programmed with this value as part of system -//! initialization. The value of the register is not interpreted by hardware. - -use register::cpu::RegisterReadOnly; - -pub struct Reg; - -impl RegisterReadOnly for Reg { - sys_coproc_read_raw!(u32, "CNTFRQ_EL0"); -} - -pub static CNTFRQ_EL0: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/cnthctl_el2.rs b/crate/aarch64/src/regs/cnthctl_el2.rs deleted file mode 100644 index f5e3c2c..0000000 --- a/crate/aarch64/src/regs/cnthctl_el2.rs +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! Counter-timer Hypervisor Control register - EL2 -//! -//! Controls the generation of an event stream from the physical counter, and -//! access from Non-secure EL1 to the physical counter and the Non-secure EL1 -//! physical timer. - -use register::cpu::RegisterReadWrite; - -// When HCR_EL2.E2H == 0: -// TODO: Figure out how we can differentiate depending on HCR_EL2.E2H state -// -// For now, implement the HCR_EL2.E2H == 0 version -register_bitfields! {u32, - CNTHCTL_EL2 [ - /// Traps Non-secure EL0 and EL1 accesses to the physical timer - /// registers to EL2. - /// - /// 0 From AArch64 state: Non-secure EL0 and EL1 accesses to the - /// CNTP_CTL_EL0, CNTP_CVAL_EL0, and CNTP_TVAL_EL0 are trapped to EL2, - /// unless it is trapped by CNTKCTL_EL1.EL0PTEN. - /// - /// From AArch32 state: Non-secure EL0 and EL1 accesses to the - /// CNTP_CTL, CNTP_CVAL, and CNTP_TVAL are trapped to EL2, unless it - /// is trapped by CNTKCTL_EL1.EL0PTEN or CNTKCTL.PL0PTEN. - /// - /// 1 This control does not cause any instructions to be trapped. - /// - /// If EL3 is implemented and EL2 is not implemented, behavior is as if - /// this bit is 1 other than for the purpose of a direct read. - EL1PCEN OFFSET(1) NUMBITS(1) [], - - /// Traps Non-secure EL0 and EL1 accesses to the physical counter - /// register to EL2. - /// - /// 0 From AArch64 state: Non-secure EL0 and EL1 accesses to the - /// CNTPCT_EL0 are trapped to EL2, unless it is trapped by - /// CNTKCTL_EL1.EL0PCTEN. - /// - /// From AArch32 state: Non-secure EL0 and EL1 accesses to the CNTPCT - /// are trapped to EL2, unless it is trapped by CNTKCTL_EL1.EL0PCTEN - /// or CNTKCTL.PL0PCTEN. - /// - /// 1 This control does not cause any instructions to be trapped. - /// - /// If EL3 is implemented and EL2 is not implemented, behavior is as if - /// this bit is 1 other than for the purpose of a direct read. - EL1PCTEN OFFSET(0) NUMBITS(1) [] - ] -} - -pub struct Reg; - -impl RegisterReadWrite for Reg { - sys_coproc_read_raw!(u32, "CNTHCTL_EL2"); - sys_coproc_write_raw!(u32, "CNTHCTL_EL2"); -} - -#[allow(non_upper_case_globals)] -pub static CNTHCTL_EL2: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/cntp_ctl_el0.rs b/crate/aarch64/src/regs/cntp_ctl_el0.rs deleted file mode 100644 index 76991eb..0000000 --- a/crate/aarch64/src/regs/cntp_ctl_el0.rs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! Counter-timer Physical Timer Control register - EL0 -//! -//! Control register for the EL1 physical timer. - -use register::cpu::RegisterReadWrite; - -register_bitfields! {u32, - CNTP_CTL_EL0 [ - /// The status of the timer. This bit indicates whether the timer - /// condition is met: - /// - /// 0 Timer condition is not met. - /// 1 Timer condition is met. - /// - /// When the value of the ENABLE bit is 1, ISTATUS indicates whether the - /// timer condition is met. ISTATUS takes no account of the value of the - /// IMASK bit. If the value of ISTATUS is 1 and the value of IMASK is 0 - /// then the timer interrupt is asserted. - /// - /// When the value of the ENABLE bit is 0, the ISTATUS field is UNKNOWN. - /// - /// This bit is read-only. - ISTATUS OFFSET(2) NUMBITS(1) [], - - /// Timer interrupt mask bit. Permitted values are: - /// - /// 0 Timer interrupt is not masked by the IMASK bit. - /// 1 Timer interrupt is masked by the IMASK bit. - IMASK OFFSET(1) NUMBITS(1) [], - - /// Enables the timer. Permitted values are: - /// - /// 0 Timer disabled. - /// 1 Timer enabled. - ENABLE OFFSET(0) NUMBITS(1) [] - ] -} - -pub struct Reg; - -impl RegisterReadWrite for Reg { - sys_coproc_read_raw!(u32, "CNTP_CTL_EL0"); - sys_coproc_write_raw!(u32, "CNTP_CTL_EL0"); -} - -pub static CNTP_CTL_EL0: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/cntp_tval_el0.rs b/crate/aarch64/src/regs/cntp_tval_el0.rs deleted file mode 100644 index bdf5f6a..0000000 --- a/crate/aarch64/src/regs/cntp_tval_el0.rs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! Counter-timer Physical Timer TimerValue register - EL0 -//! -//! Holds the timer value for the EL1 physical timer. - -use register::cpu::RegisterReadWrite; - -pub struct Reg; - -impl RegisterReadWrite for Reg { - sys_coproc_read_raw!(u32, "CNTP_TVAL_EL0"); - sys_coproc_write_raw!(u32, "CNTP_TVAL_EL0"); -} - -pub static CNTP_TVAL_EL0: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/cntpct_el0.rs b/crate/aarch64/src/regs/cntpct_el0.rs deleted file mode 100644 index b381d99..0000000 --- a/crate/aarch64/src/regs/cntpct_el0.rs +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! Counter-timer Physical Count register - EL0 -//! -//! Holds the 64-bit physical count value. - -use register::cpu::RegisterReadOnly; - -pub struct Reg; - -impl RegisterReadOnly for Reg { - sys_coproc_read_raw!(u64, "CNTPCT_EL0"); -} - -pub static CNTPCT_EL0: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/cntvoff_el2.rs b/crate/aarch64/src/regs/cntvoff_el2.rs deleted file mode 100644 index aff7074..0000000 --- a/crate/aarch64/src/regs/cntvoff_el2.rs +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! Counter-timer Virtual Offset register - EL2 -//! -//! Holds the 64-bit virtual offset. This is the offset between the physical -//! count value visible in CNTPCT_EL0 and the virtual count value visible in -//! CNTVCT_EL0. - -use register::cpu::RegisterReadWrite; - -pub struct Reg; - -impl RegisterReadWrite for Reg { - sys_coproc_read_raw!(u64, "CNTVOFF_EL2"); - sys_coproc_write_raw!(u64, "CNTVOFF_EL2"); -} - -pub static CNTVOFF_EL2: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/currentel.rs b/crate/aarch64/src/regs/currentel.rs deleted file mode 100644 index 91b8e0a..0000000 --- a/crate/aarch64/src/regs/currentel.rs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! Current Exception Level -//! -//! Holds the current Exception level. - -use register::cpu::RegisterReadOnly; - -register_bitfields! {u32, - CurrentEL [ - /// Current Exception level. Possible values of this field are: - /// - /// 00 EL0 - /// 01 EL1 - /// 10 EL2 - /// 11 EL3 - /// - /// When the HCR_EL2.NV bit is 1, Non-secure EL1 read accesses to the - /// CurrentEL register return the value of 0x2 in this field. - /// - /// This field resets to a value that is architecturally UNKNOWN. - EL OFFSET(2) NUMBITS(2) [ - EL0 = 0, - EL1 = 1, - EL2 = 2, - EL3 = 3 - ] - ] -} - -pub struct Reg; - -impl RegisterReadOnly for Reg { - sys_coproc_read_raw!(u32, "CurrentEL"); -} - -#[allow(non_upper_case_globals)] -pub static CurrentEL: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/daif.rs b/crate/aarch64/src/regs/daif.rs deleted file mode 100644 index bf810a2..0000000 --- a/crate/aarch64/src/regs/daif.rs +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! Interrupt Mask Bits -//! -//! Allows access to the interrupt mask bits. - -use register::cpu::RegisterReadWrite; - -register_bitfields! {u32, - DAIF [ - /// Process state D mask. The possible values of this bit are: - /// - /// 0 Watchpoint, Breakpoint, and Software Step exceptions targeted at - /// the current Exception level are not masked. - /// - /// 1 Watchpoint, Breakpoint, and Software Step exceptions targeted at - /// the current Exception level are masked. - /// - /// When the target Exception level of the debug exception is higher - /// than the current Exception level, the exception is not masked by - /// this bit. - /// - /// When this register has an architecturally-defined reset value, this - /// field resets to 1. - D OFFSET(9) NUMBITS(1) [ - Unmasked = 0, - Masked = 1 - ], - - /// SError interrupt mask bit. The possible values of this bit are: - /// - /// 0 Exception not masked. - /// 1 Exception masked. - /// - /// When this register has an architecturally-defined reset value, this - /// field resets to 1 . - A OFFSET(8) NUMBITS(1) [ - Unmasked = 0, - Masked = 1 - ], - - /// IRQ mask bit. The possible values of this bit are: - /// - /// 0 Exception not masked. - /// 1 Exception masked. - /// - /// When this register has an architecturally-defined reset value, this - /// field resets to 1 . - I OFFSET(7) NUMBITS(1) [ - Unmasked = 0, - Masked = 1 - ], - - /// FIQ mask bit. The possible values of this bit are: - /// - /// 0 Exception not masked. - /// 1 Exception masked. - /// - /// When this register has an architecturally-defined reset value, this - /// field resets to 1 . - F OFFSET(6) NUMBITS(1) [ - Unmasked = 0, - Masked = 1 - ] - ] -} - - -pub struct Reg; - -impl RegisterReadWrite for Reg { - sys_coproc_read_raw!(u32, "DAIF"); - sys_coproc_write_raw!(u32, "DAIF"); -} - -pub static DAIF: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/elr_el2.rs b/crate/aarch64/src/regs/elr_el2.rs deleted file mode 100644 index 0786fbb..0000000 --- a/crate/aarch64/src/regs/elr_el2.rs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! Exception Link Register - EL2 -//! -//! When taking an exception to EL2, holds the address to return to. - -use register::cpu::RegisterReadWrite; - -pub struct Reg; - -impl RegisterReadWrite for Reg { - sys_coproc_read_raw!(u64, "ELR_EL2"); - sys_coproc_write_raw!(u64, "ELR_EL2"); -} - -pub static ELR_EL2: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/far_el1.rs b/crate/aarch64/src/regs/far_el1.rs deleted file mode 100644 index fc809fa..0000000 --- a/crate/aarch64/src/regs/far_el1.rs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! Fault Address Register - EL1 -//! -//! Holds the faulting Virtual Address for all synchronous Instruction or Data -//! Abort, PC alignment fault and Watchpoint exceptions that are taken to EL1. - -use register::cpu::RegisterReadWrite; - -pub struct Reg; - -impl RegisterReadWrite for Reg { - sys_coproc_read_raw!(u64, "FAR_EL1"); - sys_coproc_write_raw!(u64, "FAR_EL1"); -} - -pub static FAR_EL1: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/hcr_el2.rs b/crate/aarch64/src/regs/hcr_el2.rs deleted file mode 100644 index 683bbef..0000000 --- a/crate/aarch64/src/regs/hcr_el2.rs +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! Hypervisor Configuration Register - EL2 -//! -//! Provides configuration controls for virtualization, including defining -//! whether various Non-secure operations are trapped to EL2. - -use register::cpu::RegisterReadWrite; - -register_bitfields! {u64, - HCR_EL2 [ - /// Execution state control for lower Exception levels: - /// - /// 0 Lower levels are all AArch32. - /// 1 The Execution state for EL1 is AArch64. The Execution state for - /// EL0 is determined by the current value of PSTATE.nRW when - /// executing at EL0. - /// - /// If all lower Exception levels cannot use AArch32 then this bit is - /// RAO/WI. - /// - /// In an implementation that includes EL3, when SCR_EL3.NS==0, the PE - /// behaves as if this bit has the same value as the SCR_EL3.RW bit for - /// all purposes other than a direct read or write access of HCR_EL2. - /// - /// The RW bit is permitted to be cached in a TLB. - /// - /// When ARMv8.1-VHE is implemented, and the value of HCR_EL2.{E2H, TGE} - /// is {1, 1}, this field behaves as 1 for all purposes other than a - /// direct read of the value of this bit. - RW OFFSET(31) NUMBITS(1) [ - AllLowerELsAreAarch32 = 0, - EL1IsAarch64 = 1 - ], - - /// Default Cacheability. - /// - /// 0 This control has no effect on the Non-secure EL1&0 translation - /// regime. - /// - /// 1 In Non-secure state: - /// - When EL1 is using AArch64, the PE behaves as if the value of - /// the SCTLR_EL1.M field is 0 for all purposes other than - /// returning the value of a direct read of SCTLR_EL1. - /// - /// - When EL1 is using AArch32, the PE behaves as if the value of - /// the SCTLR.M field is 0 for all purposes other than returning - /// the value of a direct read of SCTLR. - /// - /// - The PE behaves as if the value of the HCR_EL2.VM field is 1 - /// for all purposes other than returning the value of a direct - /// read of HCR_EL2. - /// - /// - The memory type produced by stage 1 of the EL1&0 translation - /// regime is Normal Non-Shareable, Inner Write-Back Read-Allocate - /// Write-Allocate, Outer Write-Back Read-Allocate Write-Allocate. - /// - /// This field has no effect on the EL2, EL2&0, and EL3 translation - /// regimes. - /// - /// This field is permitted to be cached in a TLB. - /// - /// In an implementation that includes EL3, when the value of SCR_EL3.NS - /// is 0 the PE behaves as if this field is 0 for all purposes other - /// than a direct read or write access of HCR_EL2. - /// - /// When ARMv8.1-VHE is implemented, and the value of HCR_EL2.{E2H, TGE} - /// is {1, 1}, this field behaves as 0 for all purposes other than a - /// direct read of the value of this field. - DC OFFSET(12) NUMBITS(1) [], - - /// Set/Way Invalidation Override. Causes Non-secure EL1 execution of - /// the data cache invalidate by set/way instructions to perform a data - /// cache clean and invalidate by set/way: - /// - /// 0 This control has no effect on the operation of data cache - /// invalidate by set/way instructions. - /// - /// 1 Data cache invalidate by set/way instructions perform a data cache - /// clean and invalidate by set/way. - /// - /// When the value of this bit is 1: - /// - /// AArch32: DCISW performs the same invalidation as a DCCISW - /// instruction. - /// - /// AArch64: DC ISW performs the same invalidation as a DC CISW - /// instruction. - /// - /// This bit can be implemented as RES 1. - /// - /// In an implementation that includes EL3, when the value of SCR_EL3.NS - /// is 0 the PE behaves as if this field is 0 for all purposes other - /// than a direct read or write access of HCR_EL2. - /// - /// When HCR_EL2.TGE is 1, the PE ignores the value of this field for - /// all purposes other than a direct read of this field. - SWIO OFFSET(1) NUMBITS(1) [] - ] -} - -pub struct Reg; - -impl RegisterReadWrite for Reg { - sys_coproc_read_raw!(u64, "HCR_EL2"); - sys_coproc_write_raw!(u64, "HCR_EL2"); -} - -pub static HCR_EL2: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/id_aa64mmfr0_el1.rs b/crate/aarch64/src/regs/id_aa64mmfr0_el1.rs deleted file mode 100644 index f75813c..0000000 --- a/crate/aarch64/src/regs/id_aa64mmfr0_el1.rs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! AArch64 Memory Model Feature Register 0 - EL1 -//! -//! Provides information about the implemented memory model and memory -//! management support in AArch64 state. - -use register::cpu::RegisterReadOnly; - -register_bitfields! {u64, - ID_AA64MMFR0_EL1 [ - /// Support for 4KiB memory translation granule size. Defined values - /// are: - /// - /// 0000 4KiB granule supported. - /// 1111 4KiB granule not supported. - /// - /// All other values are reserved. - TGran4 OFFSET(28) NUMBITS(4) [ - Supported = 0b0000, - NotSupported = 0b1111 - ], - - /// Support for 64KiB memory translation granule size. Defined values - /// are: - /// - /// 0000 64KiB granule supported. - /// 1111 64KiB granule not supported. - /// - /// All other values are reserved. - TGran64 OFFSET(24) NUMBITS(4) [ - Supported = 0b0000, - NotSupported = 0b1111 - ], - - /// Physical Address range supported. Defined values are: - /// - /// 0000 32 bits, 4GiB. - /// 0001 36 bits, 64GiB. - /// 0010 40 bits, 1TiB. - /// 0011 42 bits, 4TiB. - /// 0100 44 bits, 16TiB. - /// 0101 48 bits, 256TiB. - /// 0110 52 bits, 4PiB. - /// - /// All other values are reserved. - /// - /// The value 0110 is permitted only if the implementation includes - /// ARMv8.2-LPA, otherwise it is reserved. - PARange OFFSET(0) NUMBITS(4) [ - Bits_32 = 0b0000, - Bits_36 = 0b0001, - Bits_40 = 0b0010, - Bits_42 = 0b0011, - Bits_44 = 0b0100, - Bits_48 = 0b0101, - Bits_52 = 0b0110 - ] - ] -} - -pub struct Reg; - -impl RegisterReadOnly for Reg { - sys_coproc_read_raw!(u64, "ID_AA64MMFR0_EL1"); -} - -pub static ID_AA64MMFR0_EL1: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/macros.rs b/crate/aarch64/src/regs/macros.rs deleted file mode 100644 index bd4439c..0000000 --- a/crate/aarch64/src/regs/macros.rs +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -macro_rules! __read_raw { - ($width:ty, $asm_instr:tt, $asm_reg_name:tt) => { - /// Reads the raw bits of the CPU register. - #[inline] - fn get(&self) -> $width { - match () { - #[cfg(target_arch = "aarch64")] - () => { - let reg; - unsafe { - asm!(concat!($asm_instr, " $0, ", $asm_reg_name) : "=r"(reg) ::: "volatile"); - } - reg - } - - #[cfg(not(target_arch = "aarch64"))] - () => unimplemented!(), - } - } - }; -} - -macro_rules! __write_raw { - ($width:ty, $asm_instr:tt, $asm_reg_name:tt) => { - /// Writes raw bits to the CPU register. - #[cfg_attr(not(target_arch = "aarch64"), allow(unused_variables))] - #[inline] - fn set(&self, value: $width) { - match () { - #[cfg(target_arch = "aarch64")] - () => { - unsafe { - asm!(concat!($asm_instr, " ", $asm_reg_name, ", $0") :: "r"(value) :: "volatile") - } - } - - #[cfg(not(target_arch = "aarch64"))] - () => unimplemented!(), - } - } - }; -} - -/// Raw read from system coprocessor registers -macro_rules! sys_coproc_read_raw { - ($width:ty, $asm_reg_name:tt) => { - __read_raw!($width, "mrs", $asm_reg_name); - }; -} - -/// Raw write to system coprocessor registers -macro_rules! sys_coproc_write_raw { - ($width:ty, $asm_reg_name:tt) => { - __write_raw!($width, "msr", $asm_reg_name); - }; -} - -/// Raw read from (ordinary) registers -macro_rules! read_raw { - ($width:ty, $asm_reg_name:tt) => { - __read_raw!($width, "mov", $asm_reg_name); - }; -} -/// Raw write to (ordinary) registers -macro_rules! write_raw { - ($width:ty, $asm_reg_name:tt) => { - __write_raw!($width, "mov", $asm_reg_name); - }; -} diff --git a/crate/aarch64/src/regs/mair_el1.rs b/crate/aarch64/src/regs/mair_el1.rs deleted file mode 100644 index dbd7f9d..0000000 --- a/crate/aarch64/src/regs/mair_el1.rs +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! Memory Attribute Indirection Register - EL1 -//! -//! Provides the memory attribute encodings corresponding to the possible -//! AttrIndx values in a Long-descriptor format translation table entry for -//! stage 1 translations at EL1. - -use register::cpu::RegisterReadWrite; - -register_bitfields! {u64, - MAIR_EL1 [ - /// Attribute 7 - Attr7 OFFSET(56) NUMBITS(8) [], - - /// Attribute 6 - Attr6 OFFSET(48) NUMBITS(8) [], - - /// Attribute 5 - Attr5 OFFSET(40) NUMBITS(8) [], - - /// Attribute 4 - Attr4 OFFSET(32) NUMBITS(8) [], - - /// Attribute 3 - Attr3 OFFSET(24) NUMBITS(8) [], - - /// Attribute 2 - Attr2 OFFSET(16) NUMBITS(8) [], - - /// Attribute 1 - Attr1 OFFSET(8) NUMBITS(8) [], - - /// Attribute 0 - Attr0 OFFSET(0) NUMBITS(8) [] - ] -} - -register_bitfields! {u64, - MAIR_ATTR [ - Attr_HIGH OFFSET(4) NUMBITS(4) [ - Device = 0b0000, - Memory_OuterNonCacheable = 0b0100, - Memory_OuterWriteThrough_NonTransient_ReadAlloc_WriteAlloc = 0b1011, - Memory_OuterWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 - ], - Attr_LOW_DEVICE OFFSET(0) NUMBITS(4) [ - Device_nGnRnE = 0b0000, - Device_nGnRE = 0b0100, - Device_nGRE = 0b1000, - Device_GRE = 0b1100 - ], - Attr_LOW_MEMORY OFFSET(0) NUMBITS(4) [ - InnerNonCacheable = 0b0100, - InnerWriteThrough_NonTransient_ReadAlloc_WriteAlloc = 0b1011, - InnerWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 - ] - ] -} - -pub struct Reg; - -impl RegisterReadWrite for Reg { - sys_coproc_read_raw!(u64, "MAIR_EL1"); - sys_coproc_write_raw!(u64, "MAIR_EL1"); -} - -pub static MAIR_EL1: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/mod.rs b/crate/aarch64/src/regs/mod.rs deleted file mode 100644 index 11a6d3f..0000000 --- a/crate/aarch64/src/regs/mod.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! Processor core registers - -#[macro_use] -mod macros; - -mod cntfrq_el0; -mod cnthctl_el2; -mod cntp_ctl_el0; -mod cntp_tval_el0; -mod cntpct_el0; -mod cntvoff_el2; -mod currentel; -mod daif; -mod elr_el2; -mod far_el1; -mod hcr_el2; -mod id_aa64mmfr0_el1; -mod mair_el1; -mod mpidr_el1; -mod sctlr_el1; -mod sp; -mod sp_el0; -mod sp_el1; -mod spsel; -mod spsr_el2; -mod tcr_el1; -mod ttbr0_el1; -mod ttbr1_el1; - -// Export only the R/W traits and the static reg definitions -pub use register::cpu::*; - -pub use self::cntfrq_el0::CNTFRQ_EL0; -pub use self::cnthctl_el2::CNTHCTL_EL2; -pub use self::cntp_ctl_el0::CNTP_CTL_EL0; -pub use self::cntp_tval_el0::CNTP_TVAL_EL0; -pub use self::cntpct_el0::CNTPCT_EL0; -pub use self::cntvoff_el2::CNTVOFF_EL2; -pub use self::currentel::CurrentEL; -pub use self::daif::DAIF; -pub use self::elr_el2::ELR_EL2; -pub use self::far_el1::FAR_EL1; -pub use self::hcr_el2::HCR_EL2; -pub use self::id_aa64mmfr0_el1::ID_AA64MMFR0_EL1; -pub use self::mair_el1::{MAIR_EL1, MAIR_ATTR}; -pub use self::mpidr_el1::MPIDR_EL1; -pub use self::sctlr_el1::SCTLR_EL1; -pub use self::sp::SP; -pub use self::sp_el0::SP_EL0; -pub use self::sp_el1::SP_EL1; -pub use self::spsel::SPSel; -pub use self::spsr_el2::SPSR_EL2; -pub use self::tcr_el1::TCR_EL1; -pub use self::ttbr0_el1::TTBR0_EL1; -pub use self::ttbr1_el1::TTBR1_EL1; diff --git a/crate/aarch64/src/regs/mpidr_el1.rs b/crate/aarch64/src/regs/mpidr_el1.rs deleted file mode 100644 index 6fbfea0..0000000 --- a/crate/aarch64/src/regs/mpidr_el1.rs +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! Multiprocessor Affinity Register - EL1 -//! -//! In a multiprocessor system, provides an additional PE -//! identification mechanism for scheduling purposes. - -use register::cpu::RegisterReadOnly; - -pub struct Reg; - -impl RegisterReadOnly for Reg { - sys_coproc_read_raw!(u64, "MPIDR_EL1"); -} - -pub static MPIDR_EL1: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/sctlr_el1.rs b/crate/aarch64/src/regs/sctlr_el1.rs deleted file mode 100644 index 1f463b4..0000000 --- a/crate/aarch64/src/regs/sctlr_el1.rs +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! System Control Register - EL1 -//! -//! Provides top level control of the system, including its memory system, at -//! EL1 and EL0. - -use register::cpu::RegisterReadWrite; - -register_bitfields! {u32, - SCTLR_EL1 [ - /// Instruction access Cacheability control, for accesses at EL0 and - /// EL1: - /// - /// 0 All instruction access to Normal memory from EL0 and EL1 are - /// Non-cacheable for all levels of instruction and unified cache. - /// - /// If the value of SCTLR_EL1.M is 0, instruction accesses from stage - /// 1 of the EL1&0 translation regime are to Normal, Outer Shareable, - /// Inner Non-cacheable, Outer Non-cacheable memory. - /// - /// 1 This control has no effect on the Cacheability of instruction - /// access to Normal memory from EL0 and EL1. - /// - /// If the value of SCTLR_EL1.M is 0, instruction accesses from stage - /// 1 of the EL1&0 translation regime are to Normal, Outer Shareable, - /// Inner Write-Through, Outer Write-Through memory. - /// - /// When the value of the HCR_EL2.DC bit is 1, then instruction access - /// to Normal memory from EL0 and EL1 are Cacheable regardless of the - /// value of the SCTLR_EL1.I bit. - /// - /// When ARMv8.1-VHE is implemented, and the value of HCR_EL2.{E2H, TGE} - /// is {1, 1}, this bit has no effect on the PE. - /// - /// When this register has an architecturally-defined reset value, this - /// field resets to 0. - I OFFSET(12) NUMBITS(1) [ - NonCacheable = 0, - Cacheable = 1 - ], - - /// Cacheability control, for data accesses. - /// - /// 0 All data access to Normal memory from EL0 and EL1, and all Normal - /// memory accesses to the EL1&0 stage 1 translation tables, are - /// Non-cacheable for all levels of data and unified cache. - /// - /// 1 This control has no effect on the Cacheability of: - /// - Data access to Normal memory from EL0 and EL1. - /// - Normal memory accesses to the EL1&0 stage 1 translation - /// tables. - /// - /// When the value of the HCR_EL2.DC bit is 1, the PE ignores - /// SCLTR.C. This means that Non-secure EL0 and Non-secure EL1 data - /// accesses to Normal memory are Cacheable. - /// - /// When ARMv8.1-VHE is implemented, and the value of HCR_EL2.{E2H, TGE} - /// is {1, 1}, this bit has no effect on the PE. - /// - /// When this register has an architecturally-defined reset value, this - /// field resets to 0. - C OFFSET(2) NUMBITS(1) [ - NonCacheable = 0, - Cacheable = 1 - ], - - /// MMU enable for EL1 and EL0 stage 1 address translation. Possible - /// values of this bit are: - /// - /// 0 EL1 and EL0 stage 1 address translation disabled. - /// See the SCTLR_EL1.I field for the behavior of instruction accesses - /// to Normal memory. - /// 1 EL1 and EL0 stage 1 address translation enabled. - M OFFSET(0) NUMBITS(1) [ - Disable = 0, - Enable = 1 - ] - ] -} - -pub struct Reg; - -impl RegisterReadWrite for Reg { - sys_coproc_read_raw!(u32, "SCTLR_EL1"); - sys_coproc_write_raw!(u32, "SCTLR_EL1"); -} - -pub static SCTLR_EL1: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/sp.rs b/crate/aarch64/src/regs/sp.rs deleted file mode 100644 index f9f578b..0000000 --- a/crate/aarch64/src/regs/sp.rs +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! The stack pointer - -use register::cpu::RegisterReadWrite; - -pub struct Reg; - -impl RegisterReadWrite for Reg { - read_raw!(u64, "sp"); - write_raw!(u64, "sp"); -} - -pub static SP: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/sp_el0.rs b/crate/aarch64/src/regs/sp_el0.rs deleted file mode 100644 index aa82fdb..0000000 --- a/crate/aarch64/src/regs/sp_el0.rs +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! The stack pointer - EL0 -//! -//! Holds the stack pointer associated with EL0. At higher Exception levels, -//! this is used as the current stack pointer when the value of SPSel.SP is 0. - -use register::cpu::RegisterReadWrite; - -pub struct Reg; - -impl RegisterReadWrite for Reg { - sys_coproc_read_raw!(u64, "SP_EL0"); - sys_coproc_write_raw!(u64, "SP_EL0"); -} - -pub static SP_EL0: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/sp_el1.rs b/crate/aarch64/src/regs/sp_el1.rs deleted file mode 100644 index 4357412..0000000 --- a/crate/aarch64/src/regs/sp_el1.rs +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! The stack pointer - EL1 -//! -//! Holds the stack pointer associated with EL1. When executing at EL1, the -//! value of SPSel.SP determines the current stack pointer: -//! -//! SPSel.SP | current stack pointer -//! -------------------------------- -//! 0 | SP_EL0 -//! 1 | SP_EL1 - -use register::cpu::RegisterReadWrite; - -pub struct Reg; - -impl RegisterReadWrite for Reg { - sys_coproc_read_raw!(u64, "SP_EL1"); - sys_coproc_write_raw!(u64, "SP_EL1"); -} - -pub static SP_EL1: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/spsel.rs b/crate/aarch64/src/regs/spsel.rs deleted file mode 100644 index 91e3694..0000000 --- a/crate/aarch64/src/regs/spsel.rs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! Stack Pointer Select -//! -//! Allows the Stack Pointer to be selected between SP_EL0 and SP_ELx. - -use register::cpu::RegisterReadWrite; - -register_bitfields! {u32, - SPSel [ - /// Stack pointer to use. Possible values of this bit are: - /// - /// 0 Use SP_EL0 at all Exception levels. - /// 1 Use SP_ELx for Exception level ELx. - /// - /// When this register has an architecturally-defined reset value, this - /// field resets to 1. - SP OFFSET(0) NUMBITS(1) [ - EL0 = 0, - ELx = 1 - ] - ] -} - - -pub struct Reg; - -impl RegisterReadWrite for Reg { - sys_coproc_read_raw!(u32, "SPSEL"); - sys_coproc_write_raw!(u32, "SPSEL"); -} - -#[allow(non_upper_case_globals)] -pub static SPSel: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/spsr_el2.rs b/crate/aarch64/src/regs/spsr_el2.rs deleted file mode 100644 index 56078a4..0000000 --- a/crate/aarch64/src/regs/spsr_el2.rs +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! Saved Program Status Register - EL2 -//! -//! Holds the saved process state when an exception is taken to EL2. - -use register::cpu::RegisterReadWrite; - -register_bitfields! {u32, - SPSR_EL2 [ - /// Process state D mask. The possible values of this bit are: - /// - /// 0 Watchpoint, Breakpoint, and Software Step exceptions targeted at - /// the current Exception level are not masked. - /// - /// 1 Watchpoint, Breakpoint, and Software Step exceptions targeted at - /// the current Exception level are masked. - /// - /// When the target Exception level of the debug exception is higher - /// than the current Exception level, the exception is not masked by - /// this bit. - D OFFSET(9) NUMBITS(1) [ - Unmasked = 0, - Masked = 1 - ], - - /// SError interrupt mask bit. The possible values of this bit are: - /// - /// 0 Exception not masked. - /// 1 Exception masked. - A OFFSET(8) NUMBITS(1) [ - Unmasked = 0, - Masked = 1 - ], - - /// IRQ mask bit. The possible values of this bit are: - /// - /// 0 Exception not masked. - /// 1 Exception masked. - I OFFSET(7) NUMBITS(1) [ - Unmasked = 0, - Masked = 1 - ], - - /// FIQ mask bit. The possible values of this bit are: - /// - /// 0 Exception not masked. - /// 1 Exception masked. - F OFFSET(6) NUMBITS(1) [ - Unmasked = 0, - Masked = 1 - ], - - /// AArch64 state (Exception level and selected SP) that an exception - /// was taken from. The possible values are: - /// - /// M[3:0] | State - /// -------------- - /// 0b0000 | EL0t - /// 0b0100 | EL1t - /// 0b0101 | EL1h - /// 0b1000 | EL2t - /// 0b1001 | EL2h - /// - /// Other values are reserved, and returning to an Exception level that - /// is using AArch64 with a reserved value in this field is treated as - /// an illegal exception return. - /// - /// The bits in this field are interpreted as follows: - /// - M[3:2] holds the Exception Level. - /// - M[1] is unused and is RES 0 for all non-reserved values. - /// - M[0] is used to select the SP: - /// - 0 means the SP is always SP0. - /// - 1 means the exception SP is determined by the EL. - M OFFSET(0) NUMBITS(4) [ - EL0t = 0b0000, - EL1t = 0b0100, - EL1h = 0b0101, - EL2t = 0b1000, - EL2h = 0b1001 - ] - ] -} - -pub struct Reg; - -impl RegisterReadWrite for Reg { - sys_coproc_read_raw!(u32, "SPSR_EL2"); - sys_coproc_write_raw!(u32, "SPSR_EL2"); -} - -pub static SPSR_EL2: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/tcr_el1.rs b/crate/aarch64/src/regs/tcr_el1.rs deleted file mode 100644 index 9ebb6d7..0000000 --- a/crate/aarch64/src/regs/tcr_el1.rs +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! Translation Control Register - EL1 -//! -//! The control register for stage 1 of the EL1&0 translation regime. - -use register::cpu::RegisterReadWrite; - -register_bitfields! {u64, - TCR_EL1 [ - /// Top Byte ignored - indicates whether the top byte of an address is - /// used for address match for the TTBR1_EL1 region, or ignored and used - /// for tagged addresses. Defined values are: - /// - /// 0 Top Byte used in the address calculation. - /// 1 Top Byte ignored in the address calculation. - TBI1 OFFSET(38) NUMBITS(1) [ - Used = 0, - Ignored = 1 - ], - - /// Top Byte ignored - indicates whether the top byte of an address is - /// used for address match for the TTBR0_EL1 region, or ignored and used - /// for tagged addresses. Defined values are: - /// - /// 0 Top Byte used in the address calculation. - /// 1 Top Byte ignored in the address calculation. - TBI0 OFFSET(37) NUMBITS(1) [ - Used = 0, - Ignored = 1 - ], - - /// ASID Size. Defined values are: - /// - /// 0 8 bit - the upper 8 bits of TTBR0_EL1 and TTBR1_EL1 are ignored by - /// hardware for every purpose except reading back the register, and are - /// treated as if they are all zeros for when used for allocation and matching entries in the TLB. - /// 1 16 bit - the upper 16 bits of TTBR0_EL1 and TTBR1_EL1 are used for - /// allocation and matching in the TLB. - /// - /// If the implementation has only 8 bits of ASID, this field is RES0. - AS OFFSET(36) NUMBITS(1) [ - Bits_8 = 0, - Bits_16 = 1 - ], - - /// Intermediate Physical Address Size. - /// - /// 000 32 bits, 4GiB. - /// 001 36 bits, 64GiB. - /// 010 40 bits, 1TiB. - /// 011 42 bits, 4TiB. - /// 100 44 bits, 16TiB. - /// 101 48 bits, 256TiB. - /// 110 52 bits, 4PiB - /// - /// Other values are reserved. - /// - /// The reserved values behave in the same way as the 101 or 110 - /// encoding, but software must not rely on this property as the - /// behavior of the reserved values might change in a future revision of - /// the architecture. - /// - /// The value 110 is permitted only if ARMv8.2-LPA is implemented and - /// the translation granule size is 64KiB. - /// - /// In an implementation that supports 52-bit PAs, if the value of this - /// field is not 110 , then bits[51:48] of every translation table base - /// address for the stage of translation controlled by TCR_EL1 are 0000 - /// . - IPS OFFSET(32) NUMBITS(3) [ - Bits_32 = 0b000, - Bits_36 = 0b001, - Bits_40 = 0b010, - Bits_42 = 0b011, - Bits_44 = 0b100, - Bits_48 = 0b101, - Bits_52 = 0b110 - ], - - /// Granule size for the TTBR1_EL1. - /// - /// 01 16KiB - /// 10 4KiB - /// 11 64KiB - /// - /// Other values are reserved. - /// - /// If the value is programmed to either a reserved value, or a size - /// that has not been implemented, then the hardware will treat the - /// field as if it has been programmed to an IMPLEMENTATION DEFINED - /// choice of the sizes that has been implemented for all purposes other - /// than the value read back from this register. - /// - /// It is IMPLEMENTATION DEFINED whether the value read back is the - /// value programmed or the value that corresponds to the size chosen. - TG1 OFFSET(30) NUMBITS(2) [ - KiB_4 = 0b10, - KiB_16 = 0b01, - KiB_64 = 0b11 - ], - - /// Shareability attribute for memory associated with translation table - /// walks using TTBR1_EL1. - /// - /// 00 Non-shareable - /// 10 Outer Shareable - /// 11 Inner Shareable - /// - /// Other values are reserved. - SH1 OFFSET(28) NUMBITS(2) [ - None = 0b00, - Outer = 0b10, - Inner = 0b11 - ], - - /// Outer cacheability attribute for memory associated with translation - /// table walks using TTBR1_EL1. - /// - /// 00 Normal memory, Outer Non-cacheable - /// - /// 01 Normal memory, Outer Write-Back Read-Allocate Write-Allocate - /// Cacheable - /// - /// 10 Normal memory, Outer Write-Through Read-Allocate No - /// Write-Allocate Cacheable - /// - /// 11 Normal memory, Outer Write-Back Read-Allocate No Write-Allocate - /// Cacheable - ORGN1 OFFSET(26) NUMBITS(2) [ - NonCacheable = 0b00, - WriteBack_ReadAlloc_WriteAlloc_Cacheable = 0b01, - WriteThrough_ReadAlloc_NoWriteAlloc_Cacheable = 0b10, - WriteBack_ReadAlloc_NoWriteAlloc_Cacheable = 0b11 - ], - - /// Inner cacheability attribute for memory associated with translation - /// table walks using TTBR1_EL1. - /// - /// 00 Normal memory, Inner Non-cacheable - /// - /// 01 Normal memory, Inner Write-Back Read-Allocate Write-Allocate - /// Cacheable - /// - /// 10 Normal memory, Inner Write-Through Read-Allocate No - /// Write-Allocate Cacheable - /// - /// 11 Normal memory, Inner Write-Back Read-Allocate No Write-Allocate - /// Cacheable - IRGN1 OFFSET(24) NUMBITS(2) [ - NonCacheable = 0b00, - WriteBack_ReadAlloc_WriteAlloc_Cacheable = 0b01, - WriteThrough_ReadAlloc_NoWriteAlloc_Cacheable = 0b10, - WriteBack_ReadAlloc_NoWriteAlloc_Cacheable = 0b11 - ], - - /// Translation table walk disable for translations using - /// TTBR1_EL1. This bit controls whether a translation table walk is - /// performed on a TLB miss, for an address that is translated using - /// TTBR1_EL1. The encoding of this bit is: - /// - /// 0 Perform translation table walks using TTBR1_EL1. - /// - /// 1 A TLB miss on an address that is translated using TTBR1_EL1 - /// generates a Translation fault. No translation table walk is - /// performed. - EPD1 OFFSET(23) NUMBITS(1) [ - EnableTTBR1Walks = 0, - DisableTTBR1Walks = 1 - ], - - /// Selects whether TTBR0_EL1 or TTBR1_EL1 defines the ASID. The encoding - /// of this bit is: - /// - /// 0 TTBR0_EL1.ASID defines the ASID. - /// - /// 1 TTBR1_EL1.ASID defines the ASID. - A1 OFFSET(22) NUMBITS(1) [ - UseTTBR0ASID = 0b0, - UseTTBR1ASID = 0b1 - ], - - /// The size offset of the memory region addressed by TTBR1_EL1. The - /// region size is 2^(64-T1SZ) bytes. - /// - /// The maximum and minimum possible values for T1SZ depend on the level - /// of translation table and the memory translation granule size, as - /// described in the AArch64 Virtual Memory System Architecture chapter. - T1SZ OFFSET(16) NUMBITS(6) [], - - /// Granule size for the TTBR0_EL1. - /// - /// 00 4KiB - /// 01 64KiB - /// 10 16KiB - /// - /// Other values are reserved. - /// - /// If the value is programmed to either a reserved value, or a size - /// that has not been implemented, then the hardware will treat the - /// field as if it has been programmed to an IMPLEMENTATION DEFINED - /// choice of the sizes that has been implemented for all purposes other - /// than the value read back from this register. - /// - /// It is IMPLEMENTATION DEFINED whether the value read back is the - /// value programmed or the value that corresponds to the size chosen. - TG0 OFFSET(14) NUMBITS(2) [ - KiB_4 = 0b00, - KiB_16 = 0b10, - KiB_64 = 0b01 - ], - - /// Shareability attribute for memory associated with translation table - /// walks using TTBR0_EL1. - /// - /// 00 Non-shareable - /// 10 Outer Shareable - /// 11 Inner Shareable - /// - /// Other values are reserved. - SH0 OFFSET(12) NUMBITS(2) [ - None = 0b00, - Outer = 0b10, - Inner = 0b11 - ], - - /// Outer cacheability attribute for memory associated with translation - /// table walks using TTBR0_EL1. - /// - /// 00 Normal memory, Outer Non-cacheable - /// - /// 01 Normal memory, Outer Write-Back Read-Allocate Write-Allocate - /// Cacheable - /// - /// 10 Normal memory, Outer Write-Through Read-Allocate No - /// Write-Allocate Cacheable - /// - /// 11 Normal memory, Outer Write-Back Read-Allocate No Write-Allocate - /// Cacheable - ORGN0 OFFSET(10) NUMBITS(2) [ - NonCacheable = 0b00, - WriteBack_ReadAlloc_WriteAlloc_Cacheable = 0b01, - WriteThrough_ReadAlloc_NoWriteAlloc_Cacheable = 0b10, - WriteBack_ReadAlloc_NoWriteAlloc_Cacheable = 0b11 - ], - - /// Inner cacheability attribute for memory associated with translation - /// table walks using TTBR0_EL1. - /// - /// 00 Normal memory, Inner Non-cacheable - /// - /// 01 Normal memory, Inner Write-Back Read-Allocate Write-Allocate - /// Cacheable - /// - /// 10 Normal memory, Inner Write-Through Read-Allocate No - /// Write-Allocate Cacheable - /// - /// 11 Normal memory, Inner Write-Back Read-Allocate No Write-Allocate - /// Cacheable - IRGN0 OFFSET(8) NUMBITS(2) [ - NonCacheable = 0b00, - WriteBack_ReadAlloc_WriteAlloc_Cacheable = 0b01, - WriteThrough_ReadAlloc_NoWriteAlloc_Cacheable = 0b10, - WriteBack_ReadAlloc_NoWriteAlloc_Cacheable = 0b11 - ], - - /// Translation table walk disable for translations using - /// TTBR0_EL1. This bit controls whether a translation table walk is - /// performed on a TLB miss, for an address that is translated using - /// TTBR0_EL1. The encoding of this bit is: - /// - /// 0 Perform translation table walks using TTBR0_EL1. - /// - /// 1 A TLB miss on an address that is translated using TTBR0_EL1 - /// generates a Translation fault. No translation table walk is - /// performed. - EPD0 OFFSET(7) NUMBITS(1) [ - EnableTTBR0Walks = 0, - DisableTTBR0Walks = 1 - ], - - /// The size offset of the memory region addressed by TTBR0_EL1. The - /// region size is 2^(64-T0SZ) bytes. - /// - /// The maximum and minimum possible values for T0SZ depend on the level - /// of translation table and the memory translation granule size, as - /// described in the AArch64 Virtual Memory System Architecture chapter. - T0SZ OFFSET(0) NUMBITS(6) [] - ] -} - -pub struct Reg; - -impl RegisterReadWrite for Reg { - sys_coproc_read_raw!(u64, "TCR_EL1"); - sys_coproc_write_raw!(u64, "TCR_EL1"); -} - -pub static TCR_EL1: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/ttbr0_el1.rs b/crate/aarch64/src/regs/ttbr0_el1.rs deleted file mode 100644 index a29ff79..0000000 --- a/crate/aarch64/src/regs/ttbr0_el1.rs +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! Translation Table Base Register 0 - EL1 -//! -//! Holds the base address of the translation table for the initial lookup for -//! stage 1 of the translation of an address from the lower VA range in the -//! EL1&0 translation regime, and other information for this translation regime. - -use register::cpu::RegisterReadWrite; - -register_bitfields! {u64, - TTBR0_EL1 [ - /// An ASID for the translation table base address. The TCR_EL1.A1 field - /// selects either TTBR0_EL1.ASID or TTBR1_EL1.ASID. - /// - /// If the implementation has only 8 bits of ASID, then the upper 8 bits - /// of this field are RES 0. - ASID OFFSET(48) NUMBITS(16) [], - - /// Translation table base address - BADDR OFFSET(1) NUMBITS(47) [], - - /// Common not Private - CnP OFFSET(0) NUMBITS(1) [] - ] -} - -pub struct Reg; - -impl RegisterReadWrite for Reg { - sys_coproc_read_raw!(u64, "TTBR0_EL1"); - sys_coproc_write_raw!(u64, "TTBR0_EL1"); -} - -impl Reg { - #[inline] - pub fn get_baddr(&self) -> u64 { - self.read(TTBR0_EL1::BADDR) << 1 - } - - #[inline] - pub fn set_baddr(&self, addr: u64) { - self.write(TTBR0_EL1::BADDR.val(addr >> 1)); - } -} - -pub static TTBR0_EL1: Reg = Reg {}; diff --git a/crate/aarch64/src/regs/ttbr1_el1.rs b/crate/aarch64/src/regs/ttbr1_el1.rs deleted file mode 100644 index 7df383c..0000000 --- a/crate/aarch64/src/regs/ttbr1_el1.rs +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2018 by the author(s) - * - * ============================================================================= - * - * Licensed under either of - * - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) - * - MIT License (http://opensource.org/licenses/MIT) - * at your option. - * - * ============================================================================= - * - * Author(s): - * - Andre Richter - */ - -//! Translation Table Base Register 1 - EL1 -//! -//! Holds the base address of the translation table for the initial lookup for -//! stage 1 of the translation of an address from the upper VA range in the -//! EL1&0 translation regime, and other information for this translation regime. - -use register::cpu::RegisterReadWrite; - -register_bitfields! {u64, - TTBR1_EL1 [ - /// An ASID for the translation table base address. The TCR_EL1.A1 field - /// selects either TTBR0_EL1.ASID or TTBR1_EL1.ASID. - /// - /// If the implementation has only 8 bits of ASID, then the upper 8 bits - /// of this field are RES 0. - ASID OFFSET(48) NUMBITS(16) [], - - /// Translation table base address - BADDR OFFSET(1) NUMBITS(47) [], - - /// Common not Private - CnP OFFSET(0) NUMBITS(1) [] - ] -} - -pub struct Reg; - -impl RegisterReadWrite for Reg { - sys_coproc_read_raw!(u64, "TTBR1_EL1"); - sys_coproc_write_raw!(u64, "TTBR1_EL1"); -} - -impl Reg { - #[inline] - pub fn get_baddr(&self) -> u64 { - self.read(TTBR1_EL1::BADDR) << 1 - } - - #[inline] - pub fn set_baddr(&self, addr: u64) { - self.write(TTBR1_EL1::BADDR.val(addr >> 1)); - } -} - -pub static TTBR1_EL1: Reg = Reg {}; diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index 4e2b6dc..d3dd0be 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -1,6 +1,7 @@ [[package]] name = "aarch64" -version = "0.1.0" +version = "2.2.2" +source = "git+https://github.com/equation314/aarch64#47bf5439f5a1379f0fef6272853cf684207a4e45" dependencies = [ "bare-metal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -256,7 +257,7 @@ dependencies = [ name = "ucore" version = "0.1.0" dependencies = [ - "aarch64 0.1.0", + "aarch64 2.2.2 (git+https://github.com/equation314/aarch64)", "atags 0.1.0", "bbl 0.1.0", "bcm2837 0.1.0", @@ -362,6 +363,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] +"checksum aarch64 2.2.2 (git+https://github.com/equation314/aarch64)" = "" "checksum bare-metal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1bdcf9294ed648c7cd29b11db06ea244005aeef50ae8f605b1a3af2940bf8f92" "checksum bit-vec 0.5.0 (git+https://github.com/AltSysrq/bit-vec.git)" = "" "checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 20b696f..316b21c 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -44,7 +44,7 @@ riscv = { path = "../crate/riscv" } bbl = { path = "../crate/bbl" } [target.'cfg(target_arch = "aarch64")'.dependencies] -aarch64 = { path = "../crate/aarch64" } +aarch64 = { git = "https://github.com/equation314/aarch64" } atags = { path = "../crate/atags" } bcm2837 = { path = "../crate/bcm2837", features = ["use_generic_timer"] } From 4f592336ff3db6cc4e67d11128b037f98f3c8d16 Mon Sep 17 00:00:00 2001 From: equation314 Date: Sat, 1 Dec 2018 22:39:21 +0800 Subject: [PATCH 33/39] aarch64: remove test functions --- kernel/src/fs.rs | 56 ----------------------------------- kernel/src/lib.rs | 16 +--------- kernel/src/process/context.rs | 9 ------ 3 files changed, 1 insertion(+), 80 deletions(-) diff --git a/kernel/src/fs.rs b/kernel/src/fs.rs index 89e32aa..d25501b 100644 --- a/kernel/src/fs.rs +++ b/kernel/src/fs.rs @@ -40,62 +40,6 @@ pub fn show_logo() { println!("{}", LOGO); } -#[inline(always)] -fn sys_call(id: usize, arg0: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) -> i32 { - let ret: i32; - unsafe { - #[cfg(target_arch = "riscv32")] - asm!("ecall" - : "={x10}" (ret) - : "{x10}" (id), "{x11}" (arg0), "{x12}" (arg1), "{x13}" (arg2), "{x14}" (arg3), "{x15}" (arg4), "{x16}" (arg5) - : "memory" - : "volatile"); - #[cfg(target_arch = "x86_64")] - asm!("int 0x40" - : "={rax}" (ret) - : "{rax}" (id), "{rdi}" (arg0), "{rsi}" (arg1), "{rdx}" (arg2), "{rcx}" (arg3), "{r8}" (arg4), "{r9}" (arg5) - : "memory" - : "intel" "volatile"); - #[cfg(target_arch = "aarch64")] - asm!("svc 0" - : "={x0}" (ret) - : "{x8}" (id), "{x0}" (arg0), "{x1}" (arg1), "{x2}" (arg2), "{x3}" (arg3), "{x4}" (arg4), "{x5}" (arg5) - : "memory" - : "volatile"); - } - ret -} - -pub fn test_shell(prefix: &str) -> ! { - show_logo(); - loop { - print!("{}", prefix); - loop { - let c = super::arch::io::getchar(); - match c { - '\u{7f}' => { - print!("\u{7f}"); - } - 'c' => unsafe { - print!("sys_putc: "); - sys_call(30, 'A' as usize, 0, 0, 0, 0, 0); - }, - 't' => unsafe { - println!("sys_get_time: {}", sys_call(17, 0, 0, 0, 0, 0, 0)); - }, - ' '...'\u{7e}' => { - print!("{}", c); - } - '\n' | '\r' => { - print!("\n"); - break; - } - _ => {} - } - } - } -} - pub fn shell() { show_logo(); diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 063b1d0..38bd97c 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -68,14 +68,9 @@ pub mod arch; pub fn kmain() -> ! { process::init(); - - use process::*; - processor().add(Context::new_kernel(kernel_proc2, 2333)); - processor().add(Context::new_user_test(kernel_proc3)); - unsafe { arch::interrupt::enable(); } - // fs::shell(); + fs::shell(); // thread::test::local_key(); // thread::test::unpack(); @@ -93,12 +88,3 @@ pub fn kmain() -> ! { /// It should be defined in memory mod, but in Rust `global_allocator` must be in root mod. #[global_allocator] static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty(); - - -pub extern "C" fn kernel_proc2(arg: usize) -> ! { - fs::test_shell(&format!("proc2-{}>> ", arg)); -} - -pub extern "C" fn kernel_proc3(arg: usize) -> ! { - fs::test_shell(&format!("proc3-{}$ ", arg)); -} diff --git a/kernel/src/process/context.rs b/kernel/src/process/context.rs index 088b1b8..2df7a58 100644 --- a/kernel/src/process/context.rs +++ b/kernel/src/process/context.rs @@ -33,15 +33,6 @@ impl Context { } } - pub fn new_user_test(entry: extern fn(usize) -> !) -> Self { - let ms = MemorySet::new(); - let user_stack = ::memory::alloc_stack(); - Context { - arch: unsafe { ArchContext::new_user_thread(entry as usize, user_stack.top - 8, ms.kstack_top(), false, ms.token()) }, - memory_set: ms, - } - } - /// Make a new user thread from ELF data pub fn new_user(data: &[u8]) -> Self { // Parse elf From 7855b70bc0ac147cc1ead2034dd29bb2a92370af Mon Sep 17 00:00:00 2001 From: equation314 Date: Sat, 1 Dec 2018 23:30:53 +0800 Subject: [PATCH 34/39] aarch64: add origin raspberrypi config.txt --- kernel/Makefile | 4 +- tools/raspi-firmware/config.txt | 56 +++++++++++++++++++ {riscv-pk => tools/riscv-pk}/.gitignore | 0 {riscv-pk => tools/riscv-pk}/LICENSE | 0 {riscv-pk => tools/riscv-pk}/Makefile.in | 0 {riscv-pk => tools/riscv-pk}/README.md | 0 {riscv-pk => tools/riscv-pk}/aclocal.m4 | 0 {riscv-pk => tools/riscv-pk}/bbl/bbl.ac | 0 {riscv-pk => tools/riscv-pk}/bbl/bbl.c | 0 {riscv-pk => tools/riscv-pk}/bbl/bbl.h | 0 {riscv-pk => tools/riscv-pk}/bbl/bbl.lds | 0 {riscv-pk => tools/riscv-pk}/bbl/bbl.mk.in | 0 {riscv-pk => tools/riscv-pk}/bbl/logo.c | 0 {riscv-pk => tools/riscv-pk}/bbl/payload.S | 0 {riscv-pk => tools/riscv-pk}/bbl/raw_logo.S | 0 .../riscv-pk}/bbl/riscv_logo.txt | 0 {riscv-pk => tools/riscv-pk}/config.h.in | 0 {riscv-pk => tools/riscv-pk}/configure | 0 {riscv-pk => tools/riscv-pk}/configure.ac | 0 .../riscv-pk}/dummy_payload/dummy_entry.S | 0 .../riscv-pk}/dummy_payload/dummy_payload.ac | 0 .../riscv-pk}/dummy_payload/dummy_payload.c | 0 .../riscv-pk}/dummy_payload/dummy_payload.lds | 0 .../dummy_payload/dummy_payload.mk.in | 0 {riscv-pk => tools/riscv-pk}/machine/atomic.h | 0 {riscv-pk => tools/riscv-pk}/machine/bits.h | 0 .../riscv-pk}/machine/disabled_hart_mask.h | 0 .../riscv-pk}/machine/emulation.c | 0 .../riscv-pk}/machine/emulation.h | 0 .../riscv-pk}/machine/encoding.h | 0 {riscv-pk => tools/riscv-pk}/machine/fdt.c | 0 {riscv-pk => tools/riscv-pk}/machine/fdt.h | 0 .../riscv-pk}/machine/finisher.c | 0 .../riscv-pk}/machine/finisher.h | 0 .../riscv-pk}/machine/flush_icache.c | 0 {riscv-pk => tools/riscv-pk}/machine/htif.c | 0 {riscv-pk => tools/riscv-pk}/machine/htif.h | 0 .../riscv-pk}/machine/machine.ac | 0 .../riscv-pk}/machine/machine.mk.in | 0 {riscv-pk => tools/riscv-pk}/machine/mcall.h | 0 {riscv-pk => tools/riscv-pk}/machine/mentry.S | 0 {riscv-pk => tools/riscv-pk}/machine/minit.c | 0 .../riscv-pk}/machine/misaligned_ldst.c | 0 {riscv-pk => tools/riscv-pk}/machine/mtrap.c | 0 {riscv-pk => tools/riscv-pk}/machine/mtrap.h | 0 .../riscv-pk}/machine/muldiv_emulation.c | 0 {riscv-pk => tools/riscv-pk}/machine/uart.c | 0 {riscv-pk => tools/riscv-pk}/machine/uart.h | 0 .../riscv-pk}/machine/uart16550.c | 0 .../riscv-pk}/machine/uart16550.h | 0 .../riscv-pk}/machine/unprivileged_memory.h | 0 {riscv-pk => tools/riscv-pk}/machine/vm.h | 0 .../riscv-pk}/scripts/config.guess | 0 .../riscv-pk}/scripts/config.sub | 0 .../riscv-pk}/scripts/install.sh | 0 .../riscv-pk}/scripts/mk-install-dirs.sh | 0 .../riscv-pk}/scripts/vcs-version.sh | 0 {riscv-pk => tools/riscv-pk}/util/snprintf.c | 0 {riscv-pk => tools/riscv-pk}/util/string.c | 0 {riscv-pk => tools/riscv-pk}/util/util.ac | 0 {riscv-pk => tools/riscv-pk}/util/util.mk.in | 0 61 files changed, 58 insertions(+), 2 deletions(-) create mode 100755 tools/raspi-firmware/config.txt rename {riscv-pk => tools/riscv-pk}/.gitignore (100%) rename {riscv-pk => tools/riscv-pk}/LICENSE (100%) rename {riscv-pk => tools/riscv-pk}/Makefile.in (100%) rename {riscv-pk => tools/riscv-pk}/README.md (100%) rename {riscv-pk => tools/riscv-pk}/aclocal.m4 (100%) rename {riscv-pk => tools/riscv-pk}/bbl/bbl.ac (100%) rename {riscv-pk => tools/riscv-pk}/bbl/bbl.c (100%) rename {riscv-pk => tools/riscv-pk}/bbl/bbl.h (100%) rename {riscv-pk => tools/riscv-pk}/bbl/bbl.lds (100%) rename {riscv-pk => tools/riscv-pk}/bbl/bbl.mk.in (100%) rename {riscv-pk => tools/riscv-pk}/bbl/logo.c (100%) rename {riscv-pk => tools/riscv-pk}/bbl/payload.S (100%) rename {riscv-pk => tools/riscv-pk}/bbl/raw_logo.S (100%) rename {riscv-pk => tools/riscv-pk}/bbl/riscv_logo.txt (100%) rename {riscv-pk => tools/riscv-pk}/config.h.in (100%) rename {riscv-pk => tools/riscv-pk}/configure (100%) rename {riscv-pk => tools/riscv-pk}/configure.ac (100%) rename {riscv-pk => tools/riscv-pk}/dummy_payload/dummy_entry.S (100%) rename {riscv-pk => tools/riscv-pk}/dummy_payload/dummy_payload.ac (100%) rename {riscv-pk => tools/riscv-pk}/dummy_payload/dummy_payload.c (100%) rename {riscv-pk => tools/riscv-pk}/dummy_payload/dummy_payload.lds (100%) rename {riscv-pk => tools/riscv-pk}/dummy_payload/dummy_payload.mk.in (100%) rename {riscv-pk => tools/riscv-pk}/machine/atomic.h (100%) rename {riscv-pk => tools/riscv-pk}/machine/bits.h (100%) rename {riscv-pk => tools/riscv-pk}/machine/disabled_hart_mask.h (100%) rename {riscv-pk => tools/riscv-pk}/machine/emulation.c (100%) rename {riscv-pk => tools/riscv-pk}/machine/emulation.h (100%) rename {riscv-pk => tools/riscv-pk}/machine/encoding.h (100%) rename {riscv-pk => tools/riscv-pk}/machine/fdt.c (100%) rename {riscv-pk => tools/riscv-pk}/machine/fdt.h (100%) rename {riscv-pk => tools/riscv-pk}/machine/finisher.c (100%) rename {riscv-pk => tools/riscv-pk}/machine/finisher.h (100%) rename {riscv-pk => tools/riscv-pk}/machine/flush_icache.c (100%) rename {riscv-pk => tools/riscv-pk}/machine/htif.c (100%) rename {riscv-pk => tools/riscv-pk}/machine/htif.h (100%) rename {riscv-pk => tools/riscv-pk}/machine/machine.ac (100%) rename {riscv-pk => tools/riscv-pk}/machine/machine.mk.in (100%) rename {riscv-pk => tools/riscv-pk}/machine/mcall.h (100%) rename {riscv-pk => tools/riscv-pk}/machine/mentry.S (100%) rename {riscv-pk => tools/riscv-pk}/machine/minit.c (100%) rename {riscv-pk => tools/riscv-pk}/machine/misaligned_ldst.c (100%) rename {riscv-pk => tools/riscv-pk}/machine/mtrap.c (100%) rename {riscv-pk => tools/riscv-pk}/machine/mtrap.h (100%) rename {riscv-pk => tools/riscv-pk}/machine/muldiv_emulation.c (100%) rename {riscv-pk => tools/riscv-pk}/machine/uart.c (100%) rename {riscv-pk => tools/riscv-pk}/machine/uart.h (100%) rename {riscv-pk => tools/riscv-pk}/machine/uart16550.c (100%) rename {riscv-pk => tools/riscv-pk}/machine/uart16550.h (100%) rename {riscv-pk => tools/riscv-pk}/machine/unprivileged_memory.h (100%) rename {riscv-pk => tools/riscv-pk}/machine/vm.h (100%) rename {riscv-pk => tools/riscv-pk}/scripts/config.guess (100%) rename {riscv-pk => tools/riscv-pk}/scripts/config.sub (100%) rename {riscv-pk => tools/riscv-pk}/scripts/install.sh (100%) rename {riscv-pk => tools/riscv-pk}/scripts/mk-install-dirs.sh (100%) rename {riscv-pk => tools/riscv-pk}/scripts/vcs-version.sh (100%) rename {riscv-pk => tools/riscv-pk}/util/snprintf.c (100%) rename {riscv-pk => tools/riscv-pk}/util/string.c (100%) rename {riscv-pk => tools/riscv-pk}/util/util.ac (100%) rename {riscv-pk => tools/riscv-pk}/util/util.mk.in (100%) diff --git a/kernel/Makefile b/kernel/Makefile index de0ab82..a00f1f0 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -126,7 +126,7 @@ ifeq ($(arch), riscv32) ifeq ($(board), fpga) @cp $(kernel) $@ else - @cd ../riscv-pk && \ + @cd ../tools/riscv-pk && \ mkdir -p build && \ cd build && \ ../configure \ @@ -136,7 +136,7 @@ else --host=riscv64-unknown-elf \ --with-payload=$(abspath $(kernel)) && \ make && \ - cp bbl ../../kernel/$@ + cp bbl ../../../kernel/$@ endif else ifeq ($(arch), aarch64) $(objcopy) $(kernel) --strip-all -O binary $@ diff --git a/tools/raspi-firmware/config.txt b/tools/raspi-firmware/config.txt new file mode 100755 index 0000000..06364c5 --- /dev/null +++ b/tools/raspi-firmware/config.txt @@ -0,0 +1,56 @@ +# For more options and information see +# http://rpf.io/configtxt +# Some settings may impact device functionality. See link above for details + +# uncomment if you get no picture on HDMI for a default "safe" mode +#hdmi_safe=1 + +# uncomment this if your display has a black border of unused pixels visible +# and your display can output without overscan +#disable_overscan=1 + +# uncomment the following to adjust overscan. Use positive numbers if console +# goes off screen, and negative if there is too much border +#overscan_left=16 +#overscan_right=16 +#overscan_top=16 +#overscan_bottom=16 + +# uncomment to force a console size. By default it will be display's size minus +# overscan. +#framebuffer_width=1280 +#framebuffer_height=720 + +# uncomment if hdmi display is not detected and composite is being output +#hdmi_force_hotplug=1 + +# uncomment to force a specific HDMI mode (this will force VGA) +#hdmi_group=1 +#hdmi_mode=1 + +# uncomment to force a HDMI mode rather than DVI. This can make audio work in +# DMT (computer monitor) modes +#hdmi_drive=2 + +# uncomment to increase signal to HDMI, if you have interference, blanking, or +# no display +#config_hdmi_boost=4 + +# uncomment for composite PAL +#sdtv_mode=2 + +#uncomment to overclock the arm. 700 MHz is the default. +#arm_freq=800 + +# Uncomment some or all of these to enable the optional hardware interfaces +#dtparam=i2c_arm=on +#dtparam=i2s=on +#dtparam=spi=on + +# Uncomment this to enable the lirc-rpi module +#dtoverlay=lirc-rpi + +# Additional overlays and parameters are documented /boot/overlays/README + +# Enable audio (loads snd_bcm2835) +dtparam=audio=on diff --git a/riscv-pk/.gitignore b/tools/riscv-pk/.gitignore similarity index 100% rename from riscv-pk/.gitignore rename to tools/riscv-pk/.gitignore diff --git a/riscv-pk/LICENSE b/tools/riscv-pk/LICENSE similarity index 100% rename from riscv-pk/LICENSE rename to tools/riscv-pk/LICENSE diff --git a/riscv-pk/Makefile.in b/tools/riscv-pk/Makefile.in similarity index 100% rename from riscv-pk/Makefile.in rename to tools/riscv-pk/Makefile.in diff --git a/riscv-pk/README.md b/tools/riscv-pk/README.md similarity index 100% rename from riscv-pk/README.md rename to tools/riscv-pk/README.md diff --git a/riscv-pk/aclocal.m4 b/tools/riscv-pk/aclocal.m4 similarity index 100% rename from riscv-pk/aclocal.m4 rename to tools/riscv-pk/aclocal.m4 diff --git a/riscv-pk/bbl/bbl.ac b/tools/riscv-pk/bbl/bbl.ac similarity index 100% rename from riscv-pk/bbl/bbl.ac rename to tools/riscv-pk/bbl/bbl.ac diff --git a/riscv-pk/bbl/bbl.c b/tools/riscv-pk/bbl/bbl.c similarity index 100% rename from riscv-pk/bbl/bbl.c rename to tools/riscv-pk/bbl/bbl.c diff --git a/riscv-pk/bbl/bbl.h b/tools/riscv-pk/bbl/bbl.h similarity index 100% rename from riscv-pk/bbl/bbl.h rename to tools/riscv-pk/bbl/bbl.h diff --git a/riscv-pk/bbl/bbl.lds b/tools/riscv-pk/bbl/bbl.lds similarity index 100% rename from riscv-pk/bbl/bbl.lds rename to tools/riscv-pk/bbl/bbl.lds diff --git a/riscv-pk/bbl/bbl.mk.in b/tools/riscv-pk/bbl/bbl.mk.in similarity index 100% rename from riscv-pk/bbl/bbl.mk.in rename to tools/riscv-pk/bbl/bbl.mk.in diff --git a/riscv-pk/bbl/logo.c b/tools/riscv-pk/bbl/logo.c similarity index 100% rename from riscv-pk/bbl/logo.c rename to tools/riscv-pk/bbl/logo.c diff --git a/riscv-pk/bbl/payload.S b/tools/riscv-pk/bbl/payload.S similarity index 100% rename from riscv-pk/bbl/payload.S rename to tools/riscv-pk/bbl/payload.S diff --git a/riscv-pk/bbl/raw_logo.S b/tools/riscv-pk/bbl/raw_logo.S similarity index 100% rename from riscv-pk/bbl/raw_logo.S rename to tools/riscv-pk/bbl/raw_logo.S diff --git a/riscv-pk/bbl/riscv_logo.txt b/tools/riscv-pk/bbl/riscv_logo.txt similarity index 100% rename from riscv-pk/bbl/riscv_logo.txt rename to tools/riscv-pk/bbl/riscv_logo.txt diff --git a/riscv-pk/config.h.in b/tools/riscv-pk/config.h.in similarity index 100% rename from riscv-pk/config.h.in rename to tools/riscv-pk/config.h.in diff --git a/riscv-pk/configure b/tools/riscv-pk/configure similarity index 100% rename from riscv-pk/configure rename to tools/riscv-pk/configure diff --git a/riscv-pk/configure.ac b/tools/riscv-pk/configure.ac similarity index 100% rename from riscv-pk/configure.ac rename to tools/riscv-pk/configure.ac diff --git a/riscv-pk/dummy_payload/dummy_entry.S b/tools/riscv-pk/dummy_payload/dummy_entry.S similarity index 100% rename from riscv-pk/dummy_payload/dummy_entry.S rename to tools/riscv-pk/dummy_payload/dummy_entry.S diff --git a/riscv-pk/dummy_payload/dummy_payload.ac b/tools/riscv-pk/dummy_payload/dummy_payload.ac similarity index 100% rename from riscv-pk/dummy_payload/dummy_payload.ac rename to tools/riscv-pk/dummy_payload/dummy_payload.ac diff --git a/riscv-pk/dummy_payload/dummy_payload.c b/tools/riscv-pk/dummy_payload/dummy_payload.c similarity index 100% rename from riscv-pk/dummy_payload/dummy_payload.c rename to tools/riscv-pk/dummy_payload/dummy_payload.c diff --git a/riscv-pk/dummy_payload/dummy_payload.lds b/tools/riscv-pk/dummy_payload/dummy_payload.lds similarity index 100% rename from riscv-pk/dummy_payload/dummy_payload.lds rename to tools/riscv-pk/dummy_payload/dummy_payload.lds diff --git a/riscv-pk/dummy_payload/dummy_payload.mk.in b/tools/riscv-pk/dummy_payload/dummy_payload.mk.in similarity index 100% rename from riscv-pk/dummy_payload/dummy_payload.mk.in rename to tools/riscv-pk/dummy_payload/dummy_payload.mk.in diff --git a/riscv-pk/machine/atomic.h b/tools/riscv-pk/machine/atomic.h similarity index 100% rename from riscv-pk/machine/atomic.h rename to tools/riscv-pk/machine/atomic.h diff --git a/riscv-pk/machine/bits.h b/tools/riscv-pk/machine/bits.h similarity index 100% rename from riscv-pk/machine/bits.h rename to tools/riscv-pk/machine/bits.h diff --git a/riscv-pk/machine/disabled_hart_mask.h b/tools/riscv-pk/machine/disabled_hart_mask.h similarity index 100% rename from riscv-pk/machine/disabled_hart_mask.h rename to tools/riscv-pk/machine/disabled_hart_mask.h diff --git a/riscv-pk/machine/emulation.c b/tools/riscv-pk/machine/emulation.c similarity index 100% rename from riscv-pk/machine/emulation.c rename to tools/riscv-pk/machine/emulation.c diff --git a/riscv-pk/machine/emulation.h b/tools/riscv-pk/machine/emulation.h similarity index 100% rename from riscv-pk/machine/emulation.h rename to tools/riscv-pk/machine/emulation.h diff --git a/riscv-pk/machine/encoding.h b/tools/riscv-pk/machine/encoding.h similarity index 100% rename from riscv-pk/machine/encoding.h rename to tools/riscv-pk/machine/encoding.h diff --git a/riscv-pk/machine/fdt.c b/tools/riscv-pk/machine/fdt.c similarity index 100% rename from riscv-pk/machine/fdt.c rename to tools/riscv-pk/machine/fdt.c diff --git a/riscv-pk/machine/fdt.h b/tools/riscv-pk/machine/fdt.h similarity index 100% rename from riscv-pk/machine/fdt.h rename to tools/riscv-pk/machine/fdt.h diff --git a/riscv-pk/machine/finisher.c b/tools/riscv-pk/machine/finisher.c similarity index 100% rename from riscv-pk/machine/finisher.c rename to tools/riscv-pk/machine/finisher.c diff --git a/riscv-pk/machine/finisher.h b/tools/riscv-pk/machine/finisher.h similarity index 100% rename from riscv-pk/machine/finisher.h rename to tools/riscv-pk/machine/finisher.h diff --git a/riscv-pk/machine/flush_icache.c b/tools/riscv-pk/machine/flush_icache.c similarity index 100% rename from riscv-pk/machine/flush_icache.c rename to tools/riscv-pk/machine/flush_icache.c diff --git a/riscv-pk/machine/htif.c b/tools/riscv-pk/machine/htif.c similarity index 100% rename from riscv-pk/machine/htif.c rename to tools/riscv-pk/machine/htif.c diff --git a/riscv-pk/machine/htif.h b/tools/riscv-pk/machine/htif.h similarity index 100% rename from riscv-pk/machine/htif.h rename to tools/riscv-pk/machine/htif.h diff --git a/riscv-pk/machine/machine.ac b/tools/riscv-pk/machine/machine.ac similarity index 100% rename from riscv-pk/machine/machine.ac rename to tools/riscv-pk/machine/machine.ac diff --git a/riscv-pk/machine/machine.mk.in b/tools/riscv-pk/machine/machine.mk.in similarity index 100% rename from riscv-pk/machine/machine.mk.in rename to tools/riscv-pk/machine/machine.mk.in diff --git a/riscv-pk/machine/mcall.h b/tools/riscv-pk/machine/mcall.h similarity index 100% rename from riscv-pk/machine/mcall.h rename to tools/riscv-pk/machine/mcall.h diff --git a/riscv-pk/machine/mentry.S b/tools/riscv-pk/machine/mentry.S similarity index 100% rename from riscv-pk/machine/mentry.S rename to tools/riscv-pk/machine/mentry.S diff --git a/riscv-pk/machine/minit.c b/tools/riscv-pk/machine/minit.c similarity index 100% rename from riscv-pk/machine/minit.c rename to tools/riscv-pk/machine/minit.c diff --git a/riscv-pk/machine/misaligned_ldst.c b/tools/riscv-pk/machine/misaligned_ldst.c similarity index 100% rename from riscv-pk/machine/misaligned_ldst.c rename to tools/riscv-pk/machine/misaligned_ldst.c diff --git a/riscv-pk/machine/mtrap.c b/tools/riscv-pk/machine/mtrap.c similarity index 100% rename from riscv-pk/machine/mtrap.c rename to tools/riscv-pk/machine/mtrap.c diff --git a/riscv-pk/machine/mtrap.h b/tools/riscv-pk/machine/mtrap.h similarity index 100% rename from riscv-pk/machine/mtrap.h rename to tools/riscv-pk/machine/mtrap.h diff --git a/riscv-pk/machine/muldiv_emulation.c b/tools/riscv-pk/machine/muldiv_emulation.c similarity index 100% rename from riscv-pk/machine/muldiv_emulation.c rename to tools/riscv-pk/machine/muldiv_emulation.c diff --git a/riscv-pk/machine/uart.c b/tools/riscv-pk/machine/uart.c similarity index 100% rename from riscv-pk/machine/uart.c rename to tools/riscv-pk/machine/uart.c diff --git a/riscv-pk/machine/uart.h b/tools/riscv-pk/machine/uart.h similarity index 100% rename from riscv-pk/machine/uart.h rename to tools/riscv-pk/machine/uart.h diff --git a/riscv-pk/machine/uart16550.c b/tools/riscv-pk/machine/uart16550.c similarity index 100% rename from riscv-pk/machine/uart16550.c rename to tools/riscv-pk/machine/uart16550.c diff --git a/riscv-pk/machine/uart16550.h b/tools/riscv-pk/machine/uart16550.h similarity index 100% rename from riscv-pk/machine/uart16550.h rename to tools/riscv-pk/machine/uart16550.h diff --git a/riscv-pk/machine/unprivileged_memory.h b/tools/riscv-pk/machine/unprivileged_memory.h similarity index 100% rename from riscv-pk/machine/unprivileged_memory.h rename to tools/riscv-pk/machine/unprivileged_memory.h diff --git a/riscv-pk/machine/vm.h b/tools/riscv-pk/machine/vm.h similarity index 100% rename from riscv-pk/machine/vm.h rename to tools/riscv-pk/machine/vm.h diff --git a/riscv-pk/scripts/config.guess b/tools/riscv-pk/scripts/config.guess similarity index 100% rename from riscv-pk/scripts/config.guess rename to tools/riscv-pk/scripts/config.guess diff --git a/riscv-pk/scripts/config.sub b/tools/riscv-pk/scripts/config.sub similarity index 100% rename from riscv-pk/scripts/config.sub rename to tools/riscv-pk/scripts/config.sub diff --git a/riscv-pk/scripts/install.sh b/tools/riscv-pk/scripts/install.sh similarity index 100% rename from riscv-pk/scripts/install.sh rename to tools/riscv-pk/scripts/install.sh diff --git a/riscv-pk/scripts/mk-install-dirs.sh b/tools/riscv-pk/scripts/mk-install-dirs.sh similarity index 100% rename from riscv-pk/scripts/mk-install-dirs.sh rename to tools/riscv-pk/scripts/mk-install-dirs.sh diff --git a/riscv-pk/scripts/vcs-version.sh b/tools/riscv-pk/scripts/vcs-version.sh similarity index 100% rename from riscv-pk/scripts/vcs-version.sh rename to tools/riscv-pk/scripts/vcs-version.sh diff --git a/riscv-pk/util/snprintf.c b/tools/riscv-pk/util/snprintf.c similarity index 100% rename from riscv-pk/util/snprintf.c rename to tools/riscv-pk/util/snprintf.c diff --git a/riscv-pk/util/string.c b/tools/riscv-pk/util/string.c similarity index 100% rename from riscv-pk/util/string.c rename to tools/riscv-pk/util/string.c diff --git a/riscv-pk/util/util.ac b/tools/riscv-pk/util/util.ac similarity index 100% rename from riscv-pk/util/util.ac rename to tools/riscv-pk/util/util.ac diff --git a/riscv-pk/util/util.mk.in b/tools/riscv-pk/util/util.mk.in similarity index 100% rename from riscv-pk/util/util.mk.in rename to tools/riscv-pk/util/util.mk.in From 829b7b6b135019db6871c8cc7cd7618fda2f5b00 Mon Sep 17 00:00:00 2001 From: equation314 Date: Sat, 1 Dec 2018 23:31:53 +0800 Subject: [PATCH 35/39] aarch64: update raspi-firmware/config.txt --- tools/raspi-firmware/config.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/raspi-firmware/config.txt b/tools/raspi-firmware/config.txt index 06364c5..89ebfb4 100755 --- a/tools/raspi-firmware/config.txt +++ b/tools/raspi-firmware/config.txt @@ -54,3 +54,6 @@ # Enable audio (loads snd_bcm2835) dtparam=audio=on + +kernel=kernel8.img +device_tree= From b5ced136f75c67cf27df12beb13cbacf797e6936 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Sun, 2 Dec 2018 00:51:52 +0800 Subject: [PATCH 36/39] fix merge compile error --- kernel/Cargo.lock | 17 +++++- kernel/Makefile | 4 +- kernel/src/arch/aarch64/board/raspi3/mod.rs | 2 + .../src/arch/aarch64/board/raspi3/serial.rs | 1 + kernel/src/arch/aarch64/consts.rs | 17 +++--- kernel/src/arch/aarch64/interrupt/context.rs | 7 +-- kernel/src/arch/aarch64/interrupt/handler.rs | 6 +- kernel/src/arch/aarch64/memory.rs | 19 +++--- kernel/src/arch/aarch64/mod.rs | 61 +------------------ kernel/src/arch/aarch64/paging.rs | 14 +++-- kernel/src/arch/riscv32/paging.rs | 4 -- kernel/src/arch/x86_64/paging.rs | 3 - kernel/src/fs.rs | 4 +- kernel/src/memory.rs | 1 - 14 files changed, 51 insertions(+), 109 deletions(-) diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index 91d5bfd..b0600da 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -1,3 +1,17 @@ +[[package]] +name = "aarch64" +version = "2.2.2" +source = "git+https://github.com/equation314/aarch64#47bf5439f5a1379f0fef6272853cf684207a4e45" +dependencies = [ + "bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ux 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "apic" version = "0.1.0" @@ -302,6 +316,7 @@ dependencies = [ name = "ucore" version = "0.1.0" dependencies = [ + "aarch64 2.2.2 (git+https://github.com/equation314/aarch64)", "apic 0.1.0 (git+https://github.com/wangrunji0408/APIC-Rust)", "atags 0.1.0", "bbl 0.1.0", @@ -311,7 +326,6 @@ dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "bootloader 0.3.4 (git+https://github.com/wangrunji0408/bootloader)", "cc 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "cortex-a 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "linked_list_allocator 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -431,6 +445,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] +"checksum aarch64 2.2.2 (git+https://github.com/equation314/aarch64)" = "" "checksum apic 0.1.0 (git+https://github.com/wangrunji0408/APIC-Rust)" = "" "checksum array-init 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72" "checksum bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a3caf393d93b2d453e80638d0674597020cef3382ada454faacd43d1a55a735a" diff --git a/kernel/Makefile b/kernel/Makefile index e96afbf..5ab7d19 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -142,7 +142,7 @@ ifeq ($(arch), riscv32) ifeq ($(board), fpga) @cp $(kernel) $@ else - @cd ../tools/riscv-pk && \ + @cd ../riscv-pk && \ mkdir -p build && \ cd build && \ ../configure \ @@ -152,7 +152,7 @@ else --host=riscv64-unknown-elf \ --with-payload=$(abspath $(kernel)) && \ make && \ - cp bbl ../../../kernel/$@ + cp bbl ../../kernel/$@ endif else ifeq ($(arch), aarch64) @$(objcopy) $(kernel) --strip-all -O binary $@ diff --git a/kernel/src/arch/aarch64/board/raspi3/mod.rs b/kernel/src/arch/aarch64/board/raspi3/mod.rs index 26c2ff6..dd1abda 100644 --- a/kernel/src/arch/aarch64/board/raspi3/mod.rs +++ b/kernel/src/arch/aarch64/board/raspi3/mod.rs @@ -1,5 +1,7 @@ //! Raspberry PI 3 Model B/B+ +use once::*; + pub mod irq; pub mod timer; pub mod serial; diff --git a/kernel/src/arch/aarch64/board/raspi3/serial.rs b/kernel/src/arch/aarch64/board/raspi3/serial.rs index a7856b8..6e4fce8 100644 --- a/kernel/src/arch/aarch64/board/raspi3/serial.rs +++ b/kernel/src/arch/aarch64/board/raspi3/serial.rs @@ -1,6 +1,7 @@ use bcm2837::mini_uart::MiniUart; use core::fmt; use spin::Mutex; +use once::*; /// Struct to get a global SerialPort interface pub struct SerialPort { diff --git a/kernel/src/arch/aarch64/consts.rs b/kernel/src/arch/aarch64/consts.rs index 82d472b..af9f49c 100644 --- a/kernel/src/arch/aarch64/consts.rs +++ b/kernel/src/arch/aarch64/consts.rs @@ -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; \ No newline at end of file +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; \ No newline at end of file diff --git a/kernel/src/arch/aarch64/interrupt/context.rs b/kernel/src/arch/aarch64/interrupt/context.rs index f5f4eda..8b231e7 100644 --- a/kernel/src/arch/aarch64/interrupt/context.rs +++ b/kernel/src/arch/aarch64/interrupt/context.rs @@ -1,8 +1,7 @@ //! TrapFrame and context definitions for aarch64. -extern crate aarch64; - -use spin::{Mutex}; +use spin::Mutex; +use lazy_static::lazy_static; use aarch64::barrier; use aarch64::addr::PhysAddr; use aarch64::paging::PhysFrame; @@ -169,7 +168,7 @@ impl Context { /// Called at a new user context /// To get the init TrapFrame in sys_exec pub unsafe fn get_init_tf(&self) -> TrapFrame { - (*(self.0 as *const InitStack)).tf.clone() + (*(self.stack_top as *const InitStack)).tf.clone() } } diff --git a/kernel/src/arch/aarch64/interrupt/handler.rs b/kernel/src/arch/aarch64/interrupt/handler.rs index 2b5adcc..d9b4a20 100644 --- a/kernel/src/arch/aarch64/interrupt/handler.rs +++ b/kernel/src/arch/aarch64/interrupt/handler.rs @@ -55,7 +55,7 @@ pub extern "C" fn rust_trap(info: Info, esr: u32, tf: &mut TrapFrame) { Fault::Translation | Fault::AccessFlag | Fault::Permission => { handle_page_fault(tf) } - _ => ::trap::error(tf), + _ => crate::trap::error(tf), }, _ => crate::trap::error(tf), } @@ -73,7 +73,7 @@ fn handle_break(_num: u16, tf: &mut TrapFrame) { fn handle_syscall(num: u16, tf: &mut TrapFrame) { if num != 0 { - ::trap::error(tf); + crate::trap::error(tf); } // svc instruction has been skipped in syscall (ref: J1.1.2, page 6152) @@ -96,5 +96,5 @@ fn handle_page_fault(tf: &mut TrapFrame) { let addr = FAR_EL1.get(); trace!("\nEXCEPTION: Page Fault @ {:#x}", addr); - ::trap::error(tf); + crate::trap::error(tf); } diff --git a/kernel/src/arch/aarch64/memory.rs b/kernel/src/arch/aarch64/memory.rs index 29f3009..c4a2c5f 100644 --- a/kernel/src/arch/aarch64/memory.rs +++ b/kernel/src/arch/aarch64/memory.rs @@ -1,10 +1,11 @@ //! Memory initialization for aarch64. +use log::*; use ucore_memory::PAGE_SIZE; -use memory::{FRAME_ALLOCATOR, init_heap, MemoryArea, MemoryAttr, MemorySet, Stack}; -use super::atags::atags::Atags; +use atags::atags::Atags; use aarch64::{barrier, regs::*, addr::*}; use aarch64::paging::{PhysFrame as Frame, memory_attribute::*}; +use crate::memory::{FRAME_ALLOCATOR, init_heap, MemoryArea, MemoryAttr, MemorySet}; /// Memory initialization. pub fn init() { @@ -73,7 +74,7 @@ pub fn init_mmu_early() { fn init_frame_allocator() { use bit_allocator::BitAlloc; use core::ops::Range; - use consts::MEMORY_OFFSET; + use crate::consts::MEMORY_OFFSET; let (start, end) = memory_map().expect("failed to find memory map"); let mut ba = FRAME_ALLOCATOR.lock(); @@ -98,20 +99,14 @@ fn init_frame_allocator() { /// remap kernel page table after all initialization. fn remap_the_kernel() { - let (bottom, top) = (0, bootstacktop as usize); - let kstack = Stack { - top, - bottom, - }; - static mut SPACE: [u8; 0x1000] = [0; 0x1000]; - let mut ms = unsafe { MemorySet::new_from_raw_space(&mut SPACE, kstack) }; - ms.push(MemoryArea::new_identity(bottom, top, MemoryAttr::default(), "kstack")); + let mut ms = unsafe { MemorySet::new_bare() }; + ms.push(MemoryArea::new_identity(0, bootstacktop as usize, MemoryAttr::default(), "kstack")); ms.push(MemoryArea::new_identity(stext as usize, etext as usize, MemoryAttr::default().execute().readonly(), "text")); ms.push(MemoryArea::new_identity(sdata as usize, edata as usize, MemoryAttr::default(), "data")); ms.push(MemoryArea::new_identity(srodata as usize, erodata as usize, MemoryAttr::default().readonly(), "rodata")); ms.push(MemoryArea::new_identity(sbss as usize, ebss as usize, MemoryAttr::default(), "bss")); - use arch::board::{IO_REMAP_BASE, IO_REMAP_END}; + use super::board::{IO_REMAP_BASE, IO_REMAP_END}; ms.push(MemoryArea::new_identity(IO_REMAP_BASE, IO_REMAP_END, MemoryAttr::default().mmio(), "io_remap")); unsafe { ms.activate(); } diff --git a/kernel/src/arch/aarch64/mod.rs b/kernel/src/arch/aarch64/mod.rs index dabdc1f..b96f24e 100644 --- a/kernel/src/arch/aarch64/mod.rs +++ b/kernel/src/arch/aarch64/mod.rs @@ -23,30 +23,18 @@ pub extern "C" fn rust_main() -> ! { // Init board to enable serial port. board::init(); + println!("{}", LOGO); crate::logging::init(); interrupt::init(); memory::init(); timer::init(); - use crate::process::{processor, ContextImpl}; crate::process::init(); - processor().manager().add(ContextImpl::new_kernel(kernel_proc2, 2333), 0); - processor().manager().add(ContextImpl::new_user_test(kernel_proc3), 0); crate::kmain(); } -extern fn kernel_proc2(arg: usize) -> ! { - use alloc::format; - test_shell(&format!("proc2-{}>> ", arg)); -} - -extern fn kernel_proc3(arg: usize) -> ! { - use alloc::format; - test_shell(&format!("proc3-{}$ ", arg)); -} - const LOGO: &str = r#" ____ __ ____ _____ / __ \ __ __ _____ / /_ / __ \/ ___/ @@ -54,50 +42,3 @@ const LOGO: &str = r#" / _, _// /_/ /(__ )/ /_ / /_/ /___/ / /_/ |_| \__,_//____/ \__/ \____//____/ "#; - -pub fn show_logo() { - println!("{}", LOGO); -} - -#[inline(always)] -fn sys_call(id: usize, arg0: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) -> i32 { - let ret: i32; - unsafe { - asm!("svc 0" - : "={x0}" (ret) - : "{x8}" (id), "{x0}" (arg0), "{x1}" (arg1), "{x2}" (arg2), "{x3}" (arg3), "{x4}" (arg4), "{x5}" (arg5) - : "memory" - : "volatile"); - } - ret -} - -pub fn test_shell(prefix: &str) -> ! { - show_logo(); - loop { - print!("{}", prefix); - loop { - let c = io::getchar(); - match c { - '\u{7f}' => { - print!("\u{7f}"); - } - 'c' => unsafe { - print!("sys_putc: "); - sys_call(30, 'A' as usize, 0, 0, 0, 0, 0); - }, - 't' => unsafe { - println!("sys_get_time: {}", sys_call(17, 0, 0, 0, 0, 0, 0)); - }, - ' '...'\u{7e}' => { - print!("{}", c); - } - '\n' | '\r' => { - print!("\n"); - break; - } - _ => {} - } - } - } -} diff --git a/kernel/src/arch/aarch64/paging.rs b/kernel/src/arch/aarch64/paging.rs index c429a68..9685bc0 100644 --- a/kernel/src/arch/aarch64/paging.rs +++ b/kernel/src/arch/aarch64/paging.rs @@ -1,7 +1,4 @@ //! Page table implementations for aarch64. -// Depends on kernel -use consts::{KERNEL_PML4, RECURSIVE_INDEX}; -use memory::{active_table, alloc_frame, alloc_stack, dealloc_frame}; use ucore_memory::memory_set::*; use ucore_memory::PAGE_SIZE; use ucore_memory::paging::*; @@ -10,6 +7,10 @@ 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}; // need 3 page pub fn setup_temp_page_table(frame_lvl4: Frame, frame_lvl3: Frame, frame_lvl2: Frame) { @@ -25,7 +26,7 @@ pub fn setup_temp_page_table(frame_lvl4: Frame, frame_lvl3: Frame, frame_lvl2: F for page in Page::::range_of(start_addr, end_addr) { let paddr = PhysAddr::new(page.start_address().as_u64()); - use arch::board::IO_REMAP_BASE; + use super::board::IO_REMAP_BASE; if paddr.as_u64() >= IO_REMAP_BASE as u64 { p2[page.p2_index()].set_block::(paddr, block_flags | EF::PXN, MairDevice::attr_value()); } else { @@ -55,7 +56,7 @@ impl PageTable for ActivePageTable { 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) + self.get_entry(addr).expect("fail to get entry") } fn unmap(&mut self, addr: usize) { @@ -241,13 +242,14 @@ impl InactivePageTable for InactivePageTable0 { ttbr_el1_write(1, new_frame); tlb_invalidate_all(); } - f(); + 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 { diff --git a/kernel/src/arch/riscv32/paging.rs b/kernel/src/arch/riscv32/paging.rs index 2f2c0f1..6cce3f3 100644 --- a/kernel/src/arch/riscv32/paging.rs +++ b/kernel/src/arch/riscv32/paging.rs @@ -161,10 +161,6 @@ impl ActivePageTable { // Unmap the page self.unmap(0xcafebabe); } - - pub fn token() -> usize { - satp::read().frame().number() | (1 << 31) - } } /// implementation for the Entry trait in /crate/memory/src/paging/mod.rs impl Entry for PageEntry { diff --git a/kernel/src/arch/x86_64/paging.rs b/kernel/src/arch/x86_64/paging.rs index 5c5c896..bdf9a4c 100644 --- a/kernel/src/arch/x86_64/paging.rs +++ b/kernel/src/arch/x86_64/paging.rs @@ -96,9 +96,6 @@ impl ActivePageTable { // Unmap the page self.unmap(0xcafebabe); } - pub fn token() -> usize { - Cr3::read().0.start_address().as_u64() as usize - } } impl Entry for PageEntry { diff --git a/kernel/src/fs.rs b/kernel/src/fs.rs index e0fc3d9..3f5cc22 100644 --- a/kernel/src/fs.rs +++ b/kernel/src/fs.rs @@ -34,7 +34,7 @@ _user_img_end: lazy_static! { pub static ref ROOT_INODE: Arc = { - #[cfg(target_arch = "riscv32")] + #[cfg(any(target_arch = "riscv32", target_arch = "aarch64"))] let device = { extern { fn _user_img_start(); @@ -44,8 +44,6 @@ lazy_static! { }; #[cfg(target_arch = "x86_64")] let device = Box::new(ide::IDE::new(1)); - #[cfg(target_arch = "aarch64")] - let device = unimplemented!(); let sfs = SimpleFileSystem::open(device).expect("failed to open SFS"); sfs.root_inode() diff --git a/kernel/src/memory.rs b/kernel/src/memory.rs index e0a209a..7fdaa13 100644 --- a/kernel/src/memory.rs +++ b/kernel/src/memory.rs @@ -110,7 +110,6 @@ pub fn page_fault_handler(addr: usize) -> bool { info!("start handling swap in/out page fault"); //unsafe { ACTIVE_TABLE_SWAP.force_unlock(); } - info!("active page table token in pg fault is {:x?}", ActivePageTable::token()); /*LAB3 EXERCISE 1: YOUR STUDENT NUMBER * handle the frame deallocated */ From acafe19e269776372e114825e8031ab5613c3d0d Mon Sep 17 00:00:00 2001 From: WangRunji Date: Sun, 2 Dec 2018 19:51:16 +0800 Subject: [PATCH 37/39] fix runtime error on aarch64 - InactivePageTable::activate should be separated for user & kernel (TTBR1/0) - disable swappable for aarch64 (bug?) - use polling getchar as serial interrupt is not implemented --- kernel/src/arch/aarch64/interrupt/handler.rs | 2 +- kernel/src/arch/aarch64/memory.rs | 5 ++--- kernel/src/arch/aarch64/paging.rs | 17 ++++++++++++++--- kernel/src/fs.rs | 5 +++-- kernel/src/process/context.rs | 12 ++++++++---- kernel/src/process/mod.rs | 1 - 6 files changed, 28 insertions(+), 14 deletions(-) diff --git a/kernel/src/arch/aarch64/interrupt/handler.rs b/kernel/src/arch/aarch64/interrupt/handler.rs index d9b4a20..99652bc 100644 --- a/kernel/src/arch/aarch64/interrupt/handler.rs +++ b/kernel/src/arch/aarch64/interrupt/handler.rs @@ -94,7 +94,7 @@ fn handle_syscall(num: u16, tf: &mut TrapFrame) { fn handle_page_fault(tf: &mut TrapFrame) { let addr = FAR_EL1.get(); - trace!("\nEXCEPTION: Page Fault @ {:#x}", addr); + error!("\nEXCEPTION: Page Fault @ {:#x}", addr); crate::trap::error(tf); } diff --git a/kernel/src/arch/aarch64/memory.rs b/kernel/src/arch/aarch64/memory.rs index c4a2c5f..cf86c76 100644 --- a/kernel/src/arch/aarch64/memory.rs +++ b/kernel/src/arch/aarch64/memory.rs @@ -109,9 +109,8 @@ fn remap_the_kernel() { use super::board::{IO_REMAP_BASE, IO_REMAP_END}; ms.push(MemoryArea::new_identity(IO_REMAP_BASE, IO_REMAP_END, MemoryAttr::default().mmio(), "io_remap")); - unsafe { ms.activate(); } - use core::mem::forget; - forget(ms); + unsafe { ms.get_page_table_mut().activate_as_kernel(); } + ::core::mem::forget(ms); info!("kernel remap end"); } diff --git a/kernel/src/arch/aarch64/paging.rs b/kernel/src/arch/aarch64/paging.rs index 9685bc0..d44e4e2 100644 --- a/kernel/src/arch/aarch64/paging.rs +++ b/kernel/src/arch/aarch64/paging.rs @@ -224,11 +224,11 @@ impl InactivePageTable for InactivePageTable0 { } unsafe fn activate(&self) { - let old_frame = ttbr_el1_read(0); + let old_frame = ttbr_el1_read(1); let new_frame = self.p4_frame.clone(); - debug!("switch TTBR0 {:?} -> {:?}", old_frame, new_frame); + debug!("switch TTBR1 {:?} -> {:?}", old_frame, new_frame); if old_frame != new_frame { - ttbr_el1_write(0, new_frame); + ttbr_el1_write(1, new_frame); tlb_invalidate_all(); } } @@ -275,6 +275,17 @@ impl InactivePageTable0 { 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 { diff --git a/kernel/src/fs.rs b/kernel/src/fs.rs index 3f5cc22..2e59317 100644 --- a/kernel/src/fs.rs +++ b/kernel/src/fs.rs @@ -100,12 +100,13 @@ impl Stdin { pub fn pop(&self) -> char { // QEMU v3.0 don't support M-mode external interrupt (bug?) // So we have to use polling. - #[cfg(feature = "m_mode")] + // TODO: serial interrupt on aarch64 + #[cfg(any(feature = "m_mode", target_arch = "aarch64"))] loop { let c = crate::arch::io::getchar(); if c != '\0' { return c; } } - #[cfg(not(feature = "m_mode"))] + #[cfg(not(any(feature = "m_mode", target_arch = "aarch64")))] loop { let ret = self.buf.lock().pop_front(); match ret { diff --git a/kernel/src/process/context.rs b/kernel/src/process/context.rs index 82d0ed4..24701bd 100644 --- a/kernel/src/process/context.rs +++ b/kernel/src/process/context.rs @@ -97,7 +97,6 @@ impl ContextImpl { let kstack = KernelStack::new(); //set the user Memory pages in the memory set swappable - #[cfg(not(feature = "no_mmu"))] memory_set_map_swappable(&mut memory_set); Box::new(ContextImpl { @@ -132,7 +131,6 @@ impl ContextImpl { info!("temporary copy data!"); let kstack = KernelStack::new(); - #[cfg(not(feature = "no_mmu"))] memory_set_map_swappable(&mut memory_set); info!("FORK() finsihed!"); @@ -147,6 +145,7 @@ impl ContextImpl { } #[cfg(not(feature = "no_mmu"))] +#[cfg(not(target_arch = "aarch64"))] impl Drop for ContextImpl { fn drop(&mut self){ info!("come in to drop for ContextImpl"); @@ -257,8 +256,8 @@ fn memory_attr_from(elf_flags: Flags) -> MemoryAttr { * @brief: * map the memory area in the memory_set swappalbe, specially for the user process */ -#[cfg(not(feature = "no_mmu"))] -pub fn memory_set_map_swappable(memory_set: &mut MemorySet){ +#[cfg(not(any(feature = "no_mmu", target_arch = "aarch64")))] +pub fn memory_set_map_swappable(memory_set: &mut MemorySet) { info!("COME INTO memory set map swappable!"); let pt = unsafe { memory_set.get_page_table_mut() as *mut InactivePageTable0 @@ -272,3 +271,8 @@ pub fn memory_set_map_swappable(memory_set: &mut MemorySet){ info!("Finishing setting pages swappable"); } +#[cfg(any(feature = "no_mmu", target_arch = "aarch64"))] +pub fn memory_set_map_swappable(memory_set: &mut MemorySet) { + // FIXME: Page Fault on aarch64 + // NOTE: This function may disappear after refactor memory crate +} \ No newline at end of file diff --git a/kernel/src/process/mod.rs b/kernel/src/process/mod.rs index cb9c41e..a9a3f39 100644 --- a/kernel/src/process/mod.rs +++ b/kernel/src/process/mod.rs @@ -27,7 +27,6 @@ pub fn init() { for i in 0..4 { manager.add(ContextImpl::new_kernel(idle, i), 0); } - #[cfg(not(target_arch = "aarch64"))] crate::shell::run_user_shell(); info!("process init end"); From e7039945c121b30581684fac338d48d4e15a97a4 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Sun, 2 Dec 2018 19:56:20 +0800 Subject: [PATCH 38/39] move user img, remove useless files --- kernel/src/fs.rs | 2 +- tools/riscv-pk/dummy_payload/dummy_payload.ac | 0 tools/riscv-pk/dummy_payload/dummy_payload.c | 0 tools/riscv-pk/util/util.ac | 0 user/{user-aarch64.img => img/ucore-aarch64.img} | Bin 5 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 tools/riscv-pk/dummy_payload/dummy_payload.ac delete mode 100644 tools/riscv-pk/dummy_payload/dummy_payload.c delete mode 100644 tools/riscv-pk/util/util.ac rename user/{user-aarch64.img => img/ucore-aarch64.img} (100%) diff --git a/kernel/src/fs.rs b/kernel/src/fs.rs index 2e59317..f4f44fa 100644 --- a/kernel/src/fs.rs +++ b/kernel/src/fs.rs @@ -28,7 +28,7 @@ global_asm!(r#" .global _user_img_start .global _user_img_end _user_img_start: - .incbin "../user/user-aarch64.img" + .incbin "../user/img/ucore-aarch64.img" _user_img_end: "#); diff --git a/tools/riscv-pk/dummy_payload/dummy_payload.ac b/tools/riscv-pk/dummy_payload/dummy_payload.ac deleted file mode 100644 index e69de29..0000000 diff --git a/tools/riscv-pk/dummy_payload/dummy_payload.c b/tools/riscv-pk/dummy_payload/dummy_payload.c deleted file mode 100644 index e69de29..0000000 diff --git a/tools/riscv-pk/util/util.ac b/tools/riscv-pk/util/util.ac deleted file mode 100644 index e69de29..0000000 diff --git a/user/user-aarch64.img b/user/img/ucore-aarch64.img similarity index 100% rename from user/user-aarch64.img rename to user/img/ucore-aarch64.img From 6cf679b596d60fc1b10bb916f1d55ac7892bc87f Mon Sep 17 00:00:00 2001 From: WangRunji Date: Sun, 2 Dec 2018 22:19:32 +0800 Subject: [PATCH 39/39] update README. add LICENSE --- LICENSE | 21 +++++++++++++++++++++ README.md | 4 +++- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3312f1f --- /dev/null +++ b/LICENSE @@ -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. \ No newline at end of file diff --git a/README.md b/README.md index 6ebf56f..50f5122 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,9 @@ Rust version of THU [uCore OS](https://github.com/chyyuu/ucore_os_lab/). Going to be the next generation teaching operating system. -Support arch: x86_64, RISCV32I, AArch64 (WIP). +Supported architectures: x86_64, RISCV32IMA(S/M), AArch64 + +Tested boards: QEMU, Raspberry Pi 3B+ [Dev docs](https://rucore.gitbook.io/rust-os-docs/) (in Chinese)