Merge branch 'bootloader' into biscuit

toolchain_update
equation314 6 years ago
commit 18626a2a19

1
.gitignore vendored

@ -4,6 +4,7 @@ target
Cargo.lock
!kernel/Cargo.lock
!bootloader/Cargo.lock
!user/Cargo.lock
.DS_Store

235
bootloader/Cargo.lock generated

@ -0,0 +1,235 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aarch64"
version = "2.2.2"
source = "git+https://github.com/equation314/aarch64#14a08f4d285ae0ff515b03bff9f5e66eb68feaed"
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)",
"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.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bcm2837"
version = "0.1.0"
source = "git+https://github.com/equation314/bcm2837#446f0ea04deb5216ba5e08f10af36e5c1729e6fd"
dependencies = [
"volatile 0.2.6 (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 = "cc"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "fixedvec"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"skeptic 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "getopts"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libc"
version = "0.2.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pulldown-cmark"
version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rcore-bootloader"
version = "0.1.0"
dependencies = [
"aarch64 2.2.2 (git+https://github.com/equation314/aarch64)",
"bcm2837 0.1.0 (git+https://github.com/equation314/bcm2837)",
"cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
"fixedvec 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.3.1 (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 = "remove_dir_all"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "skeptic"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tempdir"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"remove_dir_all 0.5.1 (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 = "unicode-width"
version = "0.1.5"
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.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "volatile"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "xmas-elf"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"zero 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "zero"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum aarch64 2.2.2 (git+https://github.com/equation314/aarch64)" = "<none>"
"checksum bcm2837 0.1.0 (git+https://github.com/equation314/bcm2837)" = "<none>"
"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 cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)" = "d01c69d08ff207f231f07196e30f84c70f1c815b04f980f8b7b01ff01f05eb92"
"checksum fixedvec 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7c6c16d316ccdac21a4dd648e314e76facbbaf316e83ca137d0857a9c07419d0"
"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
"checksum getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0a7292d30132fb5424b354f5dc02512a86e4c516fe544bb7a25e7f266951b797"
"checksum libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "aab692d7759f5cd8c859e169db98ae5b52c924add2af5fbbca11d12fefb567c1"
"checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07"
"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0"
"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
"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 skeptic 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "061203a849117b0f7090baf8157aa91dac30545208fbb85166ac58b4ca33d89c"
"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8"
"checksum tock-registers 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a385d94f3f62e60445a0adb9ff8d9621faa272234530d4c0f848ec98f88e316"
"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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88dfeb711b61ce620c0cb6fd9f8e3e678622f0c971da2a63c4b3e25e88ed012f"
"checksum volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6af0edf5b4faacc31fc51159244d78d65ec580f021afcef7bd53c04aeabc7f29"
"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"
"checksum xmas-elf 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "22678df5df766e8d1e5d609da69f0c3132d794edf6ab5e75e7abcd2270d4cf58"
"checksum zero 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f1bc8a6b2005884962297587045002d8cfb8dcec9db332f4ca216ddc5de82c5"

@ -0,0 +1,16 @@
[package]
name = "rcore-bootloader"
version = "0.1.0"
authors = ["equation314 <equation618@gmail.com>"]
edition = "2018"
[dependencies]
xmas-elf = "0.6.2"
fixedvec = "0.2.3"
[target.'cfg(target_arch = "aarch64")'.dependencies]
aarch64 = { git = "https://github.com/equation314/aarch64" }
bcm2837 = { git = "https://github.com/equation314/bcm2837" }
[build-dependencies]
cc = "1.0"

@ -0,0 +1,47 @@
arch ?= aarch64
mode ?= debug
target := $(arch)
payload ?=
bootloader := target/$(target)/$(mode)/rcore-bootloader
ifeq ($(arch), x86_64)
ifeq ($(uname), Darwin)
prefix := x86_64-elf-
endif
else ifeq ($(arch), riscv32)
prefix := riscv64-unknown-elf-
else ifeq ($(arch), riscv64)
prefix := riscv64-unknown-elf-
else ifeq ($(arch), aarch64)
prefix ?= aarch64-none-elf-
ifeq (,$(shell which $(prefix)ld))
prefix := aarch64-elf-
endif
endif
ld := $(prefix)ld
objdump := $(prefix)objdump
objcopy := $(prefix)objcopy
cc := $(prefix)gcc
as := $(prefix)as
gdb := $(prefix)gdb
strip := $(prefix)strip
export CC = $(cc)
export PAYLOAD = $(payload)
build_args := --target=targets/$(arch).json
ifeq ($(mode), release)
build_args += --release
endif
.PHONY: all clean
all: bootloader
bootloader: $(payload)
@cargo xbuild $(build_args)
clean:
@cargo clean

@ -0,0 +1,40 @@
extern crate cc;
use std::fs::File;
use std::io::{Result, Write};
use std::path::Path;
fn main() {
if let Ok(file_path) = gen_payload_asm() {
cc::Build::new().file(&file_path).compile("payload");
}
}
fn gen_payload_asm() -> Result<std::path::PathBuf> {
let out_dir = std::env::var("OUT_DIR").unwrap();
let payload = std::env::var("PAYLOAD").unwrap();
if !Path::new(&payload).is_file() {
panic!("Kernel payload `{}` not found", payload)
}
let file_path = Path::new(&out_dir).join("payload.S");
let mut f = File::create(&file_path).unwrap();
println!("{:x?} {:x?}", payload, file_path);
write!(f, "# generated by build.rs - do not edit")?;
write!(f, r#"
.section .payload,"a"
.align 12
.global _kernel_payload_start, _kernel_payload_end
_kernel_payload_start:
.incbin "{}"
_kernel_payload_end:
"#, payload)?;
println!("cargo:rerun-if-changed={}", payload);
println!("cargo:rerun-if-env-changed=PAYLOAD");
Ok(file_path)
}

@ -1,8 +1,7 @@
# TODO rewrite in Rust, use crate cortex-a
.section .text.boot
.globl _start
boot:
_start:
# read cpu affinity, start core 0, halt rest
mrs x1, mpidr_el1
and x1, x1, #3
@ -93,22 +92,16 @@ set_stack:
zero_bss:
# load the start address and number of bytes in BSS section
ldr x1, =sbss
ldr x2, =__bss_length
ldr x1, =_sbss
ldr x2, =_ebss
zero_bss_loop:
# zero out the BSS section, 64-bits at a time
cbz x2, zero_bss_loop_end
cmp x1, x2
b.ge zero_bss_loop_end
str xzr, [x1], #8
sub x2, x2, #8
cbnz x2, zero_bss_loop
b zero_bss_loop
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
bl boot_main
b halt

@ -0,0 +1,35 @@
ENTRY(_start)
SECTIONS {
. = 0x80000; /* Raspbery Pi 3 AArch64 (kernel8.img) load address */
.text : {
KEEP(*(.text.boot)) /* from boot.S */
*(.text .text.* .gnu.linkonce.t*)
. = ALIGN(4K);
}
.rodata : {
*(.rodata .rodata.* .gnu.linkonce.r*)
. = ALIGN(4K);
}
.data : {
*(.data .data.* .gnu.linkonce.d*)
. = ALIGN(4K);
}
.bss : {
_sbss = .;
*(.bss .bss.*)
*(COMMON)
. = ALIGN(4K);
_ebss = .;
}
.payload : {
*(.payload)
}
/DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
}

@ -0,0 +1,136 @@
use aarch64::addr::{VirtAddr, PhysAddr};
use aarch64::paging::{memory_attribute::*, Page, PageTable, PageTableFlags as EF, PhysFrame};
use aarch64::paging::{Size4KiB, Size2MiB, Size1GiB};
use aarch64::{asm::*, barrier, regs::*};
use bcm2837::consts::RAW_IO_BASE;
use core::ptr;
use fixedvec::FixedVec;
use xmas_elf::program::{ProgramHeader64, Type};
const PAGE_SIZE: usize = 4096;
const ALIGN_2MB: u64 = 0x200000;
const RECURSIVE_INDEX: usize = 0o777;
const KERNEL_OFFSET: u64 = 0xFFFF_0000_0000_0000;
global_asm!(include_str!("boot.S"));
fn setup_temp_page_table(start_vaddr: VirtAddr, end_vaddr: VirtAddr, offset: u64) {
#[repr(align(4096))]
struct PageData([u8; PAGE_SIZE]);
static mut PAGE_TABLE_LVL4: PageData = PageData([0; PAGE_SIZE]);
static mut PAGE_TABLE_LVL3: PageData = PageData([0; PAGE_SIZE]);
static mut PAGE_TABLE_LVL2: PageData = PageData([0; PAGE_SIZE]);
let frame_lvl4 = unsafe { PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(&PAGE_TABLE_LVL4 as *const _ as u64)) };
let frame_lvl3 = unsafe { PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(&PAGE_TABLE_LVL3 as *const _ as u64)) };
let frame_lvl2 = unsafe { PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(&PAGE_TABLE_LVL2 as *const _ as u64)) };
let p4 = unsafe { &mut *(frame_lvl4.start_address().as_u64() as *mut PageTable) };
let p3 = unsafe { &mut *(frame_lvl3.start_address().as_u64() as *mut PageTable) };
let p2 = unsafe { &mut *(frame_lvl2.start_address().as_u64() as *mut PageTable) };
p4.zero();
p3.zero();
p2.zero();
let block_flags = EF::VALID | EF::AF | EF::WRITE | EF::UXN;
// normal memory
for page in Page::<Size2MiB>::range_of(start_vaddr.as_u64(), end_vaddr.as_u64()) {
let paddr = PhysAddr::new(page.start_address().as_u64().wrapping_add(offset));
p2[page.p2_index()].set_block::<Size2MiB>(paddr, block_flags, MairNormal::attr_value());
}
// device memory
for page in Page::<Size2MiB>::range_of(RAW_IO_BASE as u64, 0x4000_0000) {
let paddr = PhysAddr::new(page.start_address().as_u64());
p2[page.p2_index()].set_block::<Size2MiB>(paddr, block_flags | EF::PXN, MairDevice::attr_value());
}
p3[0].set_frame(frame_lvl2, EF::default(), MairNormal::attr_value());
p3[1].set_block::<Size1GiB>(PhysAddr::new(0x4000_0000), block_flags | EF::PXN, MairDevice::attr_value());
p4[0].set_frame(frame_lvl3, EF::default(), MairNormal::attr_value());
p4[RECURSIVE_INDEX].set_frame(frame_lvl4, EF::default(), MairNormal::attr_value());
ttbr_el1_write(0, frame_lvl4);
ttbr_el1_write(1, frame_lvl4);
tlb_invalidate_all();
}
fn enable_mmu() {
MAIR_EL1.write(
MAIR_EL1::Attr0.val(MairNormal::config_value()) +
MAIR_EL1::Attr1.val(MairDevice::config_value()) +
MAIR_EL1::Attr2.val(MairNormalNonCacheable::config_value()),
);
// Configure various settings of stage 1 of the EL1 translation regime.
let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange);
TCR_EL1.write(
TCR_EL1::TBI1::Ignored +
TCR_EL1::TBI0::Ignored +
TCR_EL1::AS::Bits_16 +
TCR_EL1::IPS.val(ips) +
TCR_EL1::TG1::KiB_4 +
TCR_EL1::SH1::Inner +
TCR_EL1::ORGN1::WriteBack_ReadAlloc_WriteAlloc_Cacheable +
TCR_EL1::IRGN1::WriteBack_ReadAlloc_WriteAlloc_Cacheable +
TCR_EL1::EPD1::EnableTTBR1Walks +
TCR_EL1::A1::UseTTBR1ASID +
TCR_EL1::T1SZ.val(16) +
TCR_EL1::TG0::KiB_4 +
TCR_EL1::SH0::Inner +
TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable +
TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable +
TCR_EL1::EPD0::EnableTTBR0Walks +
TCR_EL1::T0SZ.val(16),
);
// Switch the MMU on.
//
// First, force all previous changes to be seen before the MMU is enabled.
unsafe { barrier::isb(barrier::SY) }
// Enable the MMU and turn on data and instruction caching.
SCTLR_EL1.modify(SCTLR_EL1::M::Enable + SCTLR_EL1::C::Cacheable + SCTLR_EL1::I::Cacheable);
// Force MMU init to complete before next instruction
unsafe { barrier::isb(barrier::SY) }
}
pub fn map_kernel(kernel_start: usize, segments: &FixedVec<ProgramHeader64>) {
// reverse program headers to avoid overlapping in memory copying
let mut space = alloc_stack!([ProgramHeader64; 32]);
let mut rev_segments = FixedVec::new(&mut space);
for i in (0..segments.len()).rev() {
rev_segments.push(segments[i]).unwrap();
}
let (mut start_vaddr, mut end_vaddr) = (VirtAddr::new(core::u64::MAX), VirtAddr::zero());
for segment in &rev_segments {
if segment.get_type() != Ok(Type::Load) {
continue;
}
let virt_addr = segment.virtual_addr;
let offset = segment.offset;
let file_size = segment.file_size;
let mem_size = segment.mem_size;
unsafe {
let src = (kernel_start as u64 + offset) as *const u8;
let dst = virt_addr.wrapping_sub(KERNEL_OFFSET) as *mut u8;
ptr::copy(src, dst, file_size as usize);
ptr::write_bytes(dst.offset(file_size as isize), 0, (mem_size - file_size) as usize);
}
if virt_addr < start_vaddr.as_u64() {
start_vaddr = VirtAddr::new(virt_addr).align_down(ALIGN_2MB);
}
if virt_addr + mem_size > end_vaddr.as_u64() {
end_vaddr = VirtAddr::new(virt_addr + mem_size).align_up(ALIGN_2MB);
}
}
setup_temp_page_table(start_vaddr, end_vaddr, KERNEL_OFFSET.wrapping_neg());
enable_mmu();
}

@ -0,0 +1,7 @@
use core::panic::PanicInfo;
#[panic_handler]
#[no_mangle]
pub extern "C" fn panic(_info: &PanicInfo) -> ! {
loop {}
}

@ -0,0 +1,56 @@
#![no_std]
#![no_main]
#![feature(lang_items)]
#![feature(global_asm)]
#[macro_use]
extern crate fixedvec;
extern crate xmas_elf;
use core::mem::transmute;
use core::slice;
use fixedvec::FixedVec;
use xmas_elf::{
header,
program::{ProgramHeader, ProgramHeader64},
ElfFile,
};
#[cfg(target_arch = "aarch64")]
#[path = "arch/aarch64/mod.rs"]
pub mod arch;
pub mod lang_items;
extern "C" {
fn _kernel_payload_start();
fn _kernel_payload_end();
}
/// The entry point of bootloader
#[no_mangle] // don't mangle the name of this function
pub extern "C" fn boot_main() -> ! {
let kernel_size = _kernel_payload_end as usize - _kernel_payload_start as usize;
let kernel = unsafe { slice::from_raw_parts(_kernel_payload_start as *const u8, kernel_size) };
let kernel_elf = ElfFile::new(kernel).unwrap();
header::sanity_check(&kernel_elf).unwrap();
let mut preallocated_space = alloc_stack!([ProgramHeader64; 32]);
let mut segments = FixedVec::new(&mut preallocated_space);
for program_header in kernel_elf.program_iter() {
match program_header {
ProgramHeader::Ph64(header) => segments
.push(*header)
.expect("does not support more than 32 program segments"),
ProgramHeader::Ph32(_) => panic!("does not support 32 bit elf files"),
}
}
let entry = kernel_elf.header.pt2.entry_point();
let kernel_main: extern "C" fn() = unsafe { transmute(entry) };
arch::map_kernel(_kernel_payload_start as usize, &segments);
kernel_main();
loop {}
}

@ -0,0 +1,36 @@
{
"abi-blacklist": [
"stdcall",
"fastcall",
"vectorcall",
"thiscall",
"win64",
"sysv64"
],
"arch": "aarch64",
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
"executables": true,
"linker": "rust-lld",
"linker-flavor": "ld.lld",
"linker-is-gnu": true,
"pre-link-args": {
"ld.lld": [
"-Tsrc/arch/aarch64/boot.ld"
]
},
"llvm-target": "aarch64-unknown-none",
"no-compiler-rt": true,
"features": "+a53,+strict-align,-neon",
"max-atomic-width": 128,
"os": "none",
"panic": "abort",
"panic-strategy": "abort",
"relocation-model": "static",
"position-independent-executables": true,
"target-c-int-width": "32",
"target-endian": "little",
"target-pointer-width": "64",
"target-family": "unix",
"disable-redzone": true,
"eliminate-frame-pointer": false
}

@ -1,6 +0,0 @@
[package]
name = "atags"
version = "0.1.0"
authors = ["koumingyang <1761674434@qq.com>"]
[dependencies]

@ -1,67 +0,0 @@
use raw;
use core::slice;
use core::str;
pub use raw::{Core, Mem};
/// An ATAG.
#[derive(Debug, Copy, Clone)]
pub enum Atag {
Core(raw::Core),
Mem(raw::Mem),
Cmd(&'static str),
Unknown(u32),
None
}
impl Atag {
/// Returns `Some` if this is a `Core` ATAG. Otherwise returns `None`.
pub fn core(self) -> Option<Core> {
match self {
Atag::Core(x) => Some(x),
_ => None,
}
}
/// Returns `Some` if this is a `Mem` ATAG. Otherwise returns `None`.
pub fn mem(self) -> Option<Mem> {
match self {
Atag::Mem(x) => Some(x),
_ => None,
}
}
/// Returns `Some` with the command line string if this is a `Cmd` ATAG.
/// Otherwise returns `None`.
pub fn cmd(self) -> Option<&'static str> {
match self {
Atag::Cmd(x) => Some(x),
_ => None,
}
}
}
// Convert between raw::* types and Atag wrapper.
impl<'a> From<&'a raw::Atag> for Atag {
fn from(atag: &raw::Atag) -> Atag {
unsafe {
match (atag.tag, &atag.kind) {
(raw::Atag::CORE, &raw::Kind { core }) => Atag::Core(core),
(raw::Atag::MEM, &raw::Kind { mem }) => Atag::Mem(mem),
(raw::Atag::CMDLINE, &raw::Kind { ref cmd }) => {
let mut cmd_ptr: *const u8 = &cmd.cmd as *const u8;
let mut len: usize = 0;
while *cmd_ptr.add(len) != 0 {
len += 1;
}
let cmd_slice = slice::from_raw_parts(cmd_ptr, len);
Atag::Cmd(str::from_utf8_unchecked(cmd_slice))
},
(raw::Atag::NONE, _) => Atag::None,
(id, _) => Atag::Unknown(id),
}
}
}
}

@ -1,37 +0,0 @@
pub use atag::*;
use raw;
/// The address at which the firmware loads the ATAGS.
const ATAG_BASE: usize = 0x100;
/// An iterator over the ATAGS on this system.
pub struct Atags {
ptr: &'static raw::Atag,
}
impl Atags {
/// Returns an instance of `Atags`, an iterator over ATAGS on this system.
pub fn get() -> Atags {
Atags {
ptr: unsafe { &*(ATAG_BASE as *const raw::Atag) }
}
}
}
impl Iterator for Atags {
type Item = Atag;
/// Iterate over Atags. Returns a valid Atag until the iterator hits the
/// Atag::None.
fn next(&mut self) -> Option<Atag> {
let cur = self.ptr;
match cur.next() {
Some(next) => {
let result = Some(Atag::from(cur));
self.ptr = next;
result
},
None => None,
}
}
}

@ -1,6 +0,0 @@
#![no_std]
mod raw;
mod atag;
pub mod atags;

@ -1,67 +0,0 @@
/// A raw `ATAG` as laid out in memory.
#[repr(C)]
pub struct Atag {
pub dwords: u32,
pub tag: u32,
pub kind: Kind
}
impl Atag {
pub const NONE: u32 = 0x00000000;
pub const CORE: u32 = 0x54410001;
pub const MEM: u32 = 0x54410002;
pub const VIDEOTEXT: u32 = 0x54410003;
pub const RAMDISK: u32 = 0x54410004;
pub const INITRD2: u32 = 0x54420005;
pub const SERIAL: u32 = 0x54410006;
pub const REVISION: u32 = 0x54410007;
pub const VIDEOLFB: u32 = 0x54410008;
pub const CMDLINE: u32 = 0x54410009;
/// Returns the ATAG following `self`, if there is one.
pub fn next(&self) -> Option<&Atag> {
if self.tag == Atag::NONE {
None
} else {
let current = self as *const Atag as *const u32;
let next: &Atag = unsafe {
&*(current.add(self.dwords as usize) as *const Atag)
};
Some(next)
}
}
}
/// The possible variant of an ATAG.
#[repr(C)]
pub union Kind {
pub core: Core,
pub mem: Mem,
pub cmd: Cmd
}
/// A `CORE` ATAG.
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Core {
pub flags: u32,
pub page_size: u32,
pub root_dev: u32
}
/// A `MEM` ATAG.
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Mem {
pub size: u32,
pub start: u32
}
/// A `CMDLINE` ATAG.
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct Cmd {
/// The first byte of the command line string.
pub cmd: u8
}

@ -1,12 +0,0 @@
[package]
name = "bcm2837"
version = "0.1.0"
authors = ["equation314 <equation618@gmail.com>"]
edition = "2018"
[features]
use_generic_timer = ["aarch64"]
[dependencies]
volatile = "0.2.4"
aarch64= { git = "https://github.com/equation314/aarch64", optional = true }

@ -1,163 +0,0 @@
use crate::IO_BASE;
use crate::timer::delay;
use core::marker::PhantomData;
use volatile::{ReadOnly, Volatile, WriteOnly};
/// The base address of the `GPIO` registers.
const GPIO_BASE: usize = IO_BASE + 0x200000;
/// An alternative GPIO function. (ref: peripherals 6.1, page 92)
#[repr(u8)]
pub enum Function {
Input = 0b000,
Output = 0b001,
Alt0 = 0b100,
Alt1 = 0b101,
Alt2 = 0b110,
Alt3 = 0b111,
Alt4 = 0b011,
Alt5 = 0b010,
}
/// GPIO registers starting from `GPIO_BASE` (ref: peripherals 6.1, page 90)
#[repr(C)]
#[allow(non_snake_case)]
struct Registers {
FSEL: [Volatile<u32>; 6],
__reserved0: u32,
SET: [WriteOnly<u32>; 2],
__reserved1: u32,
CLR: [WriteOnly<u32>; 2],
__reserved2: u32,
LEV: [ReadOnly<u32>; 2],
__reserved3: u32,
EDS: [Volatile<u32>; 2],
__reserved4: u32,
REN: [Volatile<u32>; 2],
__reserved5: u32,
FEN: [Volatile<u32>; 2],
__reserved6: u32,
HEN: [Volatile<u32>; 2],
__reserved7: u32,
LEN: [Volatile<u32>; 2],
__reserved8: u32,
AREN: [Volatile<u32>; 2],
__reserved9: u32,
AFEN: [Volatile<u32>; 2],
__reserved10: u32,
PUD: Volatile<u32>,
PUDCLK: [Volatile<u32>; 2],
}
/// Possible states for a GPIO pin.
pub enum Uninitialized {}
pub enum Input {}
pub enum Output {}
pub enum Alt {}
/// A GPIO pin in state `State`.
///
/// The `State` generic always corresponds to an uninstantiatable type that is
/// use solely to mark and track the state of a given GPIO pin. A `Gpio`
/// structure starts in the `Uninitialized` state and must be transitions into
/// one of `Input`, `Output`, or `Alt` via the `into_input`, `into_output`, and
/// `into_alt` methods before it can be used.
pub struct Gpio<State> {
pin: u8,
registers: &'static mut Registers,
_state: PhantomData<State>,
}
impl<T> Gpio<T> {
/// Transitions `self` to state `S`, consuming `self` and returning a new
/// `Gpio` instance in state `S`. This method should _never_ be exposed to
/// the public!
#[inline(always)]
fn transition<S>(self) -> Gpio<S> {
Gpio {
pin: self.pin,
registers: self.registers,
_state: PhantomData,
}
}
/// Set the Gpio pull-up/pull-down state for values in `pin_value`
/// (ref: peripherals 6.1, page 101)
pub fn set_gpio_pd(&mut self, pud_value: u8) {
let index = if self.pin >= 32 { 1 } else { 0 };
self.registers.PUD.write(pud_value as u32);
delay(150);
self.registers.PUDCLK[index as usize].write((1 << self.pin) as u32);
delay(150);
self.registers.PUD.write(0);
self.registers.PUDCLK[index as usize].write(0);
}
}
impl Gpio<Uninitialized> {
/// Returns a new `GPIO` structure for pin number `pin`.
///
/// # Panics
///
/// Panics if `pin` > `53`.
pub fn new(pin: u8) -> Gpio<Uninitialized> {
if pin > 53 {
panic!("Gpio::new(): pin {} exceeds maximum of 53", pin);
}
Gpio {
registers: unsafe { &mut *(GPIO_BASE as *mut Registers) },
pin: pin,
_state: PhantomData,
}
}
/// Enables the alternative function `function` for `self`. Consumes self
/// and returns a `Gpio` structure in the `Alt` state.
pub fn into_alt(self, function: Function) -> Gpio<Alt> {
let select = (self.pin / 10) as usize;
let offset = 3 * (self.pin % 10) as usize;
self.registers.FSEL[select].update(|value| {
*value &= !(0b111 << offset);
*value |= (function as u32) << offset;
});
self.transition()
}
/// Sets this pin to be an _output_ pin. Consumes self and returns a `Gpio`
/// structure in the `Output` state.
pub fn into_output(self) -> Gpio<Output> {
self.into_alt(Function::Output).transition()
}
/// Sets this pin to be an _input_ pin. Consumes self and returns a `Gpio`
/// structure in the `Input` state.
pub fn into_input(self) -> Gpio<Input> {
self.into_alt(Function::Input).transition()
}
}
impl Gpio<Output> {
/// Sets (turns on) the pin.
pub fn set(&mut self) {
let index = if self.pin >= 32 { 1 } else { 0 };
self.registers.SET[index as usize].write(1 << (self.pin - index * 32));
}
/// Clears (turns off) the pin.
pub fn clear(&mut self) {
let index = if self.pin >= 32 { 1 } else { 0 };
self.registers.CLR[index as usize].write(1 << (self.pin - index * 32));
}
}
impl Gpio<Input> {
/// Reads the pin's value. Returns `true` if the level is high and `false`
/// if the level is low.
pub fn level(&mut self) -> bool {
let index = if self.pin >= 32 { 1 } else { 0 };
let high = 1 << (self.pin - index * 32);
(self.registers.LEV[index as usize].read() & high) == high
}
}

@ -1,88 +0,0 @@
use crate::IO_BASE;
use volatile::{ReadOnly, Volatile};
const INT_BASE: usize = IO_BASE + 0xB000 + 0x200;
/// Allowed interrupts (ref: peripherals 7.5, page 113)
#[repr(u8)]
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Interrupt {
Timer1 = 1,
Timer3 = 3,
Usb = 9,
Aux = 29,
Gpio0 = 49,
Gpio1 = 50,
Gpio2 = 51,
Gpio3 = 52,
Uart = 57,
}
/// Interrupts registers starting from `INT_BASE` (ref: peripherals 7.5, page 112)
#[repr(C)]
#[allow(non_snake_case)]
struct Registers {
IRQBasicPending: ReadOnly<u32>,
IRQPending: [ReadOnly<u32>; 2],
FIQControl: Volatile<u32>,
EnableIRQ: [Volatile<u32>; 2],
EnableBasicIRQ: Volatile<u32>,
DisableIRQ: [Volatile<u32>; 2],
DisableBasicIRQ: Volatile<u32>,
}
/// Pending interrupts
pub struct PendingInterrupts(u64);
impl Iterator for PendingInterrupts {
type Item = usize;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let int = self.0.trailing_zeros();
if int < 64 {
self.0 &= !(1 << int);
Some(int as usize)
} else {
None
}
}
}
/// An interrupt controller. Used to enable and disable interrupts as well as to
/// check if an interrupt is pending.
pub struct Controller {
registers: &'static mut Registers,
}
impl Controller {
/// Returns a new handle to the interrupt controller.
#[inline]
pub fn new() -> Controller {
Controller {
registers: unsafe { &mut *(INT_BASE as *mut Registers) },
}
}
/// Enables the interrupt `int`.
pub fn enable(&mut self, int: Interrupt) {
self.registers.EnableIRQ[int as usize / 32].write(1 << (int as usize) % 32);
}
/// Disables the interrupt `int`.
pub fn disable(&mut self, int: Interrupt) {
self.registers.DisableIRQ[int as usize / 32].write(1 << (int as usize) % 32);
}
/// Returns `true` if `int` is pending. Otherwise, returns `false`.
pub fn is_pending(&self, int: Interrupt) -> bool {
self.registers.IRQPending[int as usize / 32].read() & (1 << (int as usize) % 32) != 0
}
/// Return all pending interrupts.
pub fn pending_interrupts(&self) -> PendingInterrupts {
let irq1 = self.registers.IRQPending[0].read() as u64;
let irq2 = self.registers.IRQPending[1].read() as u64;
PendingInterrupts((irq2 << 32) | irq1)
}
}

@ -1,12 +0,0 @@
#![no_std]
#![feature(asm)]
extern crate volatile;
pub mod gpio;
pub mod timer;
pub mod mailbox;
pub mod mini_uart;
pub mod interrupt;
pub const IO_BASE: usize = 0x3F000000;

@ -1,80 +0,0 @@
use crate::IO_BASE;
use volatile::{ReadOnly, Volatile, WriteOnly};
/// The base address for the `MU` registers.
const MAILBOX_BASE: usize = IO_BASE + 0xB000 + 0x880;
/// Available mailbox channels
///
/// (ref: https://github.com/raspberrypi/firmware/wiki/Mailboxes)
#[repr(u8)]
#[derive(Copy, Clone, Debug)]
pub enum MailboxChannel {
Framebuffer = 1,
Property = 8,
}
/// Read from mailbox status register (MAILx_STA).
#[repr(u32)]
enum MailboxStatus {
MailboxEmpty = 1 << 30,
MailboxFull = 1 << 31,
}
/// Mailbox registers. We basically only support mailbox 0 & 1. We
/// deliver to the VC in mailbox 1, it delivers to us in mailbox 0. See
/// BCM2835-ARM-Peripherals.pdf section 1.3 for an explanation about
/// the placement of memory barriers.
///
/// (ref: https://github.com/raspberrypi/firmware/wiki/Mailboxes)
#[repr(C)]
#[allow(non_snake_case)]
struct Registers {
MAIL0_RD: ReadOnly<u32>, // 0x00
__reserved0: [u32; 3],
MAIL0_POL: ReadOnly<u32>, // 0x10
MAIL0_SND: ReadOnly<u32>, // 0x14
MAIL0_STA: ReadOnly<u32>, // 0x18
MAIL0_CNF: Volatile<u32>, // 0x1c
MAIL1_WRT: WriteOnly<u32>, // 0x20
__reserved1: [u32; 3],
_MAIL1_POL: ReadOnly<u32>, // 0x30
_MAIL1_SND: ReadOnly<u32>, // 0x34
MAIL1_STA: ReadOnly<u32>, // 0x38
_MAIL1_CNF: Volatile<u32>, // 0x3c
}
/// The Raspberry Pi's mailbox.
///
/// (ref: https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes)
pub struct Mailbox {
registers: &'static mut Registers,
}
impl Mailbox {
/// Returns a new instance of `Mailbox`.
#[inline]
pub fn new() -> Mailbox {
Mailbox {
registers: unsafe { &mut *(MAILBOX_BASE as *mut Registers) },
}
}
/// Read from the requested channel of mailbox 0.
pub fn read(&self, channel: MailboxChannel) -> u32 {
loop {
while self.registers.MAIL0_STA.read() & (MailboxStatus::MailboxEmpty as u32) != 0 {}
let data = self.registers.MAIL0_RD.read();
if data & 0xF == channel as u32 {
return data & !0xF;
}
}
}
/// Write to the requested channel of mailbox 1.
pub fn write(&mut self, channel: MailboxChannel, data: u32) {
while self.registers.MAIL1_STA.read() & (MailboxStatus::MailboxFull as u32) != 0 {}
self.registers.MAIL1_WRT.write((data & !0xF) | (channel as u32));
}
}

@ -1,133 +0,0 @@
use crate::IO_BASE;
use crate::gpio::{Function, Gpio};
use volatile::{ReadOnly, Volatile};
/// The `AUXENB` register from page 9 of the BCM2837 documentation.
const AUX_ENABLES: *mut Volatile<u8> = (IO_BASE + 0x215004) as *mut Volatile<u8>;
/// The base address for the `MU` registers.
const MU_REG_BASE: usize = IO_BASE + 0x215040;
/// Enum representing bit fields of the `AUX_MU_IIR_REG` register.
#[repr(u8)]
pub enum MiniUartInterruptId {
Transmit = 0b010,
Recive = 0b100,
}
/// Enum representing bit fields of the `AUX_MU_LSR_REG` register.
#[repr(u8)]
enum LsrStatus {
DataReady = 1,
TxAvailable = 1 << 5,
}
/// MU registers starting from `MU_REG_BASE` (ref: peripherals 2.1, page 8)
#[repr(C)]
#[allow(non_snake_case)]
struct Registers {
AUX_MU_IO_REG: Volatile<u8>,
__r0: [u8; 3],
AUX_MU_IER_REG: Volatile<u8>,
__r1: [u8; 3],
AUX_MU_IIR_REG: Volatile<u8>,
__r2: [u8; 3],
AUX_MU_LCR_REG: Volatile<u8>,
__r3: [u8; 3],
AUX_MU_MCR_REG: Volatile<u8>,
__r4: [u8; 3],
AUX_MU_LSR_REG: ReadOnly<u8>,
__r5: [u8; 3],
AUX_MU_MSR_REG: ReadOnly<u8>,
__r6: [u8; 3],
AUX_MU_SCRATCH: Volatile<u8>,
__r7: [u8; 3],
AUX_MU_CNTL_REG: Volatile<u8>,
__r8: [u8; 3],
AUX_MU_STAT_REG: ReadOnly<u32>,
AUX_MU_BAUD_REG: Volatile<u16>,
}
/// The Raspberry Pi's "mini UART".
pub struct MiniUart {
registers: &'static mut Registers,
timeout: Option<u32>,
}
impl MiniUart {
/// Returns a new instance of `MiniUart`.
#[inline]
pub fn new() -> MiniUart {
let registers = unsafe { &mut *(MU_REG_BASE as *mut Registers) };
MiniUart {
registers: registers,
timeout: None,
}
}
/// Initializes the mini UART by enabling it as an auxiliary peripheral,
/// setting the data size to 8 bits, setting the BAUD rate to ~115200 (baud
/// divider of 270), setting GPIO pins 14 and 15 to alternative function 5
/// (TXD1/RDXD1), and finally enabling the UART transmitter and receiver.
///
/// By default, reads will never time out. To set a read timeout, use
/// `set_read_timeout()`.
pub fn init(&mut self) {
// Enable the mini UART as an auxiliary device.
unsafe { (*AUX_ENABLES).write(1) }
Gpio::new(14).into_alt(Function::Alt5).set_gpio_pd(0);
Gpio::new(15).into_alt(Function::Alt5).set_gpio_pd(0);
self.registers.AUX_MU_CNTL_REG.write(0); // Disable auto flow control and disable receiver and transmitter (for now)
self.registers.AUX_MU_IER_REG.write(1); // Enable receive interrupts and disable transmit interrupts
self.registers.AUX_MU_LCR_REG.write(3); // Enable 8 bit mode
self.registers.AUX_MU_MCR_REG.write(0); // Set RTS line to be always high
self.registers.AUX_MU_BAUD_REG.write(270); // Set baud rate to 115200
self.registers.AUX_MU_CNTL_REG.write(3); // Finally, enable transmitter and receiver
}
/// Set the read timeout to `milliseconds` milliseconds.
pub fn set_read_timeout(&mut self, milliseconds: u32) {
self.timeout = Some(milliseconds)
}
/// Write the byte `byte`. This method blocks until there is space available
/// in the output FIFO.
pub fn write_byte(&mut self, byte: u8) {
while self.registers.AUX_MU_LSR_REG.read() & (LsrStatus::TxAvailable as u8) == 0 {}
self.registers.AUX_MU_IO_REG.write(byte);
}
/// Returns `true` if there is at least one byte ready to be read. If this
/// method returns `true`, a subsequent call to `read_byte` is guaranteed to
/// return immediately. This method does not block.
pub fn has_byte(&self) -> bool {
self.registers.AUX_MU_LSR_REG.read() & (LsrStatus::DataReady as u8) != 0
}
/// Blocks until there is a byte ready to read. If a read timeout is set,
/// this method blocks for at most that amount of time. Otherwise, this
/// method blocks indefinitely until there is a byte to read.
///
/// Returns `Ok(())` if a byte is ready to read. Returns `Err(())` if the
/// timeout expired while waiting for a byte to be ready. If this method
/// returns `Ok(())`, a subsequent call to `read_byte` is guaranteed to
/// return immediately.
pub fn wait_for_byte(&self) -> Result<(), ()> {
unimplemented!()
}
/// Reads a byte. Blocks indefinitely until a byte is ready to be read.
pub fn read_byte(&self) -> u8 {
while !self.has_byte() {}
self.registers.AUX_MU_IO_REG.read()
}
// Read `AUX_MU_IIR_REG` and determine if the interrupt `id` is pending.
pub fn interrupt_is_pending(&self, id: MiniUartInterruptId) -> bool {
self.registers.AUX_MU_IIR_REG.read() & 0b110 == id as u8
}
}

@ -1,76 +0,0 @@
extern crate aarch64;
use super::BasicTimer;
use aarch64::regs::*;
use volatile::*;
/// The base address for the ARM generic timer, IRQs, mailboxes
const GEN_TIMER_REG_BASE: usize = 0x40000000;
/// Core interrupt sources (ref: QA7 4.10, page 16)
#[repr(u8)]
#[allow(dead_code)]
#[allow(non_snake_case)]
#[derive(Copy, Clone, PartialEq, Debug)]
enum CoreInterrupt {
CNTPSIRQ = 0,
CNTPNSIRQ = 1,
CNTHPIRQ = 2,
CNTVIRQ = 3,
Mailbox0 = 4,
Mailbox1 = 5,
Mailbox2 = 6,
Mailbox3 = 7,
Gpu = 8,
Pmu = 9,
AxiOutstanding = 10,
LocalTimer = 11,
}
/// Timer, IRQs, mailboxes registers (ref: QA7 chapter 4, page 7)
#[allow(non_snake_case)]
#[repr(C)]
struct Registers {
CONTROL: Volatile<u32>,
_unused1: [Volatile<u32>; 8],
LOCAL_IRQ: Volatile<u32>,
_unused2: [Volatile<u32>; 3],
LOCAL_TIMER_CTL: Volatile<u32>,
LOCAL_TIMER_FLAGS: Volatile<u32>,
_unused3: Volatile<u32>,
CORE_TIMER_IRQCNTL: [Volatile<u32>; 4],
CORE_MAILBOX_IRQCNTL: [Volatile<u32>; 4],
CORE_IRQ_SRC: [Volatile<u32>; 4],
}
/// The ARM generic timer.
pub struct GenericTimer {
registers: &'static mut Registers,
}
impl BasicTimer for GenericTimer {
fn new() -> Self {
GenericTimer {
registers: unsafe { &mut *(GEN_TIMER_REG_BASE as *mut Registers) },
}
}
fn init(&mut self) {
self.registers.CORE_TIMER_IRQCNTL[0].write(1 << (CoreInterrupt::CNTPNSIRQ as u8));
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET);
}
fn read(&self) -> u64 {
let cntfrq = CNTFRQ_EL0.get(); // 62500000
(CNTPCT_EL0.get() * 1000000 / (cntfrq as u64)) as u64
}
fn tick_in(&mut self, us: u32) {
let cntfrq = CNTFRQ_EL0.get(); // 62500000
CNTP_TVAL_EL0.set(((cntfrq as f64) * (us as f64) / 1000000.0) as u32);
}
fn is_pending(&self) -> bool {
self.registers.CORE_IRQ_SRC[0].read() & (1 << (CoreInterrupt::CNTPNSIRQ as u8)) != 0
}
}

@ -1,38 +0,0 @@
#[cfg(feature = "use_generic_timer")]
mod generic_timer;
#[cfg(feature = "use_generic_timer")]
pub use self::generic_timer::GenericTimer as Timer;
#[cfg(not(feature = "use_generic_timer"))]
mod system_timer;
#[cfg(not(feature = "use_generic_timer"))]
pub use self::system_timer::SystemTimer as Timer;
/// The Raspberry Pi timer.
pub trait BasicTimer {
/// Returns a new instance.
fn new() -> Self;
/// Initialization timer.
fn init(&mut self);
/// Reads the timer's counter and returns the 64-bit counter value.
/// The returned value is the number of elapsed microseconds.
fn read(&self) -> u64;
/// Sets up a match in timer 1 to occur `us` microseconds from now. If
/// interrupts for timer 1 are enabled and IRQs are unmasked, then a timer
/// interrupt will be issued in `us` microseconds.
fn tick_in(&mut self, us: u32);
/// Returns `true` if timer interruption is pending. Otherwise, returns `false`.
fn is_pending(&self) -> bool;
}
/// wait for `cycle` CPU cycles
#[inline(always)]
pub fn delay(cycle: u32) {
for _ in 0..cycle {
unsafe { asm!("nop") }
}
}

@ -1,62 +0,0 @@
use super::BasicTimer;
use crate::interrupt::{Controller, Interrupt};
use crate::IO_BASE;
use volatile::{ReadOnly, Volatile};
/// The base address for the ARM system timer registers.
const TIMER_REG_BASE: usize = IO_BASE + 0x3000;
/// System timer registers (ref: peripherals 12.1, page 172)
#[repr(C)]
#[allow(non_snake_case)]
struct Registers {
CS: Volatile<u32>,
CLO: ReadOnly<u32>,
CHI: ReadOnly<u32>,
COMPARE: [Volatile<u32>; 4],
}
#[repr(u8)]
#[allow(dead_code)]
#[derive(Copy, Clone, PartialEq, Debug)]
enum SystemTimerId {
Timer0 = 0,
Timer1 = 1,
Timer2 = 2,
Timer3 = 3,
}
/// The Raspberry Pi ARM system timer.
pub struct SystemTimer {
registers: &'static mut Registers,
}
impl BasicTimer for SystemTimer {
fn new() -> Self {
SystemTimer {
registers: unsafe { &mut *(TIMER_REG_BASE as *mut Registers) },
}
}
fn init(&mut self) {
Controller::new().enable(Interrupt::Timer1);
}
fn read(&self) -> u64 {
let low = self.registers.CLO.read();
let high = self.registers.CHI.read();
((high as u64) << 32) | (low as u64)
}
fn tick_in(&mut self, us: u32) {
let current_low = self.registers.CLO.read();
let compare = current_low.wrapping_add(us);
self.registers.COMPARE[SystemTimerId::Timer1 as usize].write(compare);
self.registers.CS.write(1 << (SystemTimerId::Timer1 as usize)); // unmask
}
fn is_pending(&self) -> bool {
let controller = Controller::new();
controller.is_pending(Interrupt::Timer1)
}
}

11
kernel/Cargo.lock generated

@ -3,7 +3,7 @@
[[package]]
name = "aarch64"
version = "2.2.2"
source = "git+https://github.com/equation314/aarch64#ad81f8f0ebd6fed15b2b0696f5d1b566d36f1172"
source = "git+https://github.com/equation314/aarch64#14a08f4d285ae0ff515b03bff9f5e66eb68feaed"
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)",
@ -30,10 +30,6 @@ dependencies = [
"nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "atags"
version = "0.1.0"
[[package]]
name = "bare-metal"
version = "0.2.4"
@ -49,6 +45,7 @@ version = "0.1.0"
[[package]]
name = "bcm2837"
version = "0.1.0"
source = "git+https://github.com/equation314/bcm2837#446f0ea04deb5216ba5e08f10af36e5c1729e6fd"
dependencies = [
"aarch64 2.2.2 (git+https://github.com/equation314/aarch64)",
"volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
@ -281,9 +278,8 @@ 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",
"bcm2837 0.1.0",
"bcm2837 0.1.0 (git+https://github.com/equation314/bcm2837)",
"bit-allocator 0.1.0",
"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)",
@ -567,6 +563,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum apic 0.1.0 (git+https://github.com/wangrunji0408/APIC-Rust)" = "<none>"
"checksum array-init 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72"
"checksum bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a3caf393d93b2d453e80638d0674597020cef3382ada454faacd43d1a55a735a"
"checksum bcm2837 0.1.0 (git+https://github.com/equation314/bcm2837)" = "<none>"
"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 bitvec 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cfadef5c4e2c2e64067b9ecc061179f12ac7ec65ba613b1f60f3972bbada1f5b"

@ -27,9 +27,9 @@ board_u540 = ["sv39"]
# (for aarch64 RaspberryPi3)
nographic = []
board_raspi3 = ["bcm2837", "link_user"]
raspi3_use_generic_timer = ["bcm2837/use_generic_timer"]
# (for riscv64)
board_k210 = ["m_mode"]
raspi3_use_generic_timer = ["bcm2837/use_generic_timer"]
# Hard link user program
link_user = []
@ -71,8 +71,7 @@ bbl = { path = "../crate/bbl" }
[target.'cfg(target_arch = "aarch64")'.dependencies]
aarch64 = { git = "https://github.com/equation314/aarch64" }
atags = { path = "../crate/atags" }
bcm2837 = { path = "../crate/bcm2837", optional = true }
bcm2837 = { git = "https://github.com/equation314/bcm2837", optional = true }
[package.metadata.bootimage]
default-target = "targets/x86_64.json"

@ -32,8 +32,10 @@ m_mode ?=
target := $(arch)
kernel := target/$(target)/$(mode)/rcore
bin := target/$(target)/$(mode)/kernel.bin
kernel_img := target/$(target)/$(mode)/kernel.img
bootimage := target/$(target)/bootimage.bin
bootloader_dir = ../bootloader
bootloader := $(bootloader_dir)/target/$(target)/$(mode)/rcore-bootloader
bbl_path := $(PWD)/../riscv-pk
user_dir := ../user
k210_lib := ../tools/k210/libkendryte.a
@ -53,9 +55,7 @@ endif
export M_MODE = $(m_mode)
ifeq ($(arch), aarch64)
graphic ?= on
else
graphic ?= off
graphic := on
endif
### qemu options ###
@ -76,7 +76,7 @@ qemu_net_opts += \
else ifeq ($(arch), riscv32)
qemu_opts += \
-machine virt \
-kernel $(bin) \
-kernel $(kernel_img) \
-drive file=$(SFSIMG),format=raw,id=sfs \
-device virtio-blk-device,drive=sfs
qemu_net_opts += \
@ -89,7 +89,7 @@ endif
else ifeq ($(arch), riscv64)
qemu_opts += \
-machine virt \
-kernel $(bin) \
-kernel $(kernel_img) \
-drive file=$(SFSIMG),format=raw,id=sfs \
-device virtio-blk-device,drive=sfs
qemu_net_opts += \
@ -103,7 +103,7 @@ else ifeq ($(arch), aarch64)
qemu_opts += \
-machine $(board) \
-serial null -serial mon:stdio \
-kernel $(bin)
-kernel $(kernel_img)
endif
ifdef d
@ -170,8 +170,9 @@ objdump := $(prefix)objdump
objcopy := $(prefix)objcopy
cc := $(prefix)gcc
as := $(prefix)as
gdb := gdb
export CC=$(cc)
gdb := $(prefix)gdb
strip := $(prefix)strip
export CC = $(cc)
.PHONY: all clean run build asm doc justrun debug kernel sfsimg install runnet
@ -179,6 +180,7 @@ all: kernel
clean:
@cargo clean
@cd $(bootloader_dir) && make clean
@cd $(user_dir) && make clean
doc:
@ -199,12 +201,12 @@ justrunui: build
-device virtio-gpu-device \
-device virtio-mouse-device
debug: $(kernel) $(bin)
debug: $(kernel) $(kernel_img)
@qemu-system-$(arch) $(qemu_opts) -s -S &
@sleep 1
@$(gdb) $(kernel) -x ../tools/gdbinit
build: $(bin)
build: $(kernel_img)
asm:
@$(objdump) -d $(kernel) | less
@ -215,7 +217,15 @@ header:
sym:
@$(objdump) -t $(kernel) | less
$(bin): kernel
$(bootloader): $(kernel)
ifeq ($(arch), aarch64)
@echo Building $(arch) bootloader
@$(strip) $(kernel) -o $(kernel)_stripped
@cd $(bootloader_dir) && make arch=$(arch) mode=$(mode) payload=../kernel/$(kernel)_stripped
@rm $(kernel)_stripped
endif
$(kernel_img): kernel $(bootloader)
ifeq ($(arch), riscv32)
@mkdir -p target/$(target)/bbl && \
cd target/$(target)/bbl && \
@ -243,10 +253,11 @@ else
cp bbl $(abspath $@)
endif
else ifeq ($(arch), aarch64)
@$(objcopy) $(kernel) --strip-all -O binary $@
@$(objcopy) $(bootloader) --strip-all -O binary $@
endif
kernel:
@echo Building $(arch) kernel
ifeq ($(arch), x86_64)
@bootimage build $(build_args)
else ifeq ($(arch), riscv32)
@ -287,8 +298,8 @@ endif
ifdef sd_card
.PHONY:
install: $(bin)
cp $(bin) $(sd_card)/kernel8.img
install: $(kernel_img)
cp $(kernel_img) $(sd_card)/kernel8.img
sudo umount $(sd_card)
endif
@ -296,9 +307,9 @@ endif
ifeq ($(board), k210)
.PHONY:
install: $(bin)
install: $(kernel_img)
## baudrate no more than 600000
@python3 ../tools/k210/kflash.py $(bin) -b 600000
@python3 ../tools/k210/kflash.py $(kernel_img) -b 600000
endif
.PHONY:

@ -53,19 +53,19 @@ union ColorBuffer {
}
impl ColorBuffer {
fn new(color_depth: ColorDepth, bus_addr: u32, size: u32) -> ColorBuffer {
fn new(color_depth: ColorDepth, base_addr: usize, size: usize) -> ColorBuffer {
unsafe {
match color_depth {
ColorDepth16 => ColorBuffer {
buf16: core::slice::from_raw_parts_mut(
bus_addr as *mut u16,
(size / 2) as usize,
base_addr as *mut u16,
size / 2,
),
},
ColorDepth32 => ColorBuffer {
buf32: core::slice::from_raw_parts_mut(
bus_addr as *mut u32,
(size / 4) as usize,
base_addr as *mut u32,
size / 4,
),
},
}
@ -144,7 +144,7 @@ impl Framebuffer {
use crate::arch::memory;
let paddr = info.bus_addr & !0xC0000000;
let vaddr = memory::ioremap(paddr as usize, info.screen_size as usize, "fb") as u32;
let vaddr = memory::ioremap(paddr as usize, info.screen_size as usize, "fb");
if vaddr == 0 {
Err(format!(
"cannot remap memory range [{:#x?}..{:#x?}]",
@ -153,7 +153,7 @@ impl Framebuffer {
))?;
}
Ok(Framebuffer {
buf: ColorBuffer::new(color_depth, vaddr, info.screen_size),
buf: ColorBuffer::new(color_depth, vaddr, info.screen_size as usize),
color_depth,
fb_info: info,
})

@ -1,6 +1,7 @@
//! Raspberry PI 3 Model B/B+
use once::*;
use bcm2837::atags::Atags;
pub mod fb;
pub mod irq;
@ -8,8 +9,8 @@ pub mod timer;
pub mod serial;
pub mod mailbox;
pub const IO_REMAP_BASE: usize = bcm2837::IO_BASE;
pub const IO_REMAP_END: usize = 0x40001000;
pub const IO_REMAP_BASE: usize = bcm2837::consts::IO_BASE;
pub const IO_REMAP_END: usize = bcm2837::consts::KERNEL_OFFSET + 0x4000_1000;
/// Initialize serial port before other initializations.
pub fn init_serial_early() {
@ -26,3 +27,17 @@ pub fn init_driver() {
fb::init();
timer::init();
}
/// Returns the (start address, end address) of the physical 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 probe_memory() -> Option<(usize, usize)> {
let mut atags: Atags = Atags::get();
while let Some(atag) = atags.next() {
if let Some(mem) = atag.mem() {
return Some((mem.start as usize, (mem.start + mem.size) as usize));
}
}
None
}

@ -0,0 +1,8 @@
.section .text.entry
.globl _start
_start:
ldr x0, =bootstacktop
mov sp, x0
bl rust_main
1: b 1b

@ -1,14 +1,7 @@
ENTRY(_start)
SECTIONS {
. = 0x80000; /* Raspbery Pi 3 AArch64 (kernel8.img) load address */
.boot : {
KEEP(*(.text.boot)) /* from boot.S */
}
. = 0x100000; /* Load the kernel at this address. It's also kernel stack top address */
bootstacktop = .;
. = 0xffff000000100000; /* Load the kernel at this address. It's also kernel stack top address */
.text : {
stext = .;
@ -32,7 +25,7 @@ SECTIONS {
edata = .;
}
.bss (NOLOAD) : {
.bss : {
. = ALIGN(32);
sbss = .;
*(.bss .bss.*)
@ -41,12 +34,15 @@ SECTIONS {
ebss = .;
}
.stack : {
. = ALIGN(4K);
bootstack = .;
. += 0x100000;
bootstacktop = .;
}
/* end of the binary */
_end = ALIGN(8);
/* number of bytes in BSS section and complete binary */
__bss_length = (ebss - sbss);
__binary_length = (_end - _start);
/DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
}

@ -1,8 +1,8 @@
pub const RECURSIVE_INDEX: usize = 0o777;
pub const KERNEL_OFFSET: usize = 0;
pub const KERNEL_OFFSET: usize = 0xFFFF_0000_0000_0000;
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_8000_0000_0000;
pub const USER_STACK_OFFSET: usize = 0x0000_8000_0000_0000 - USER_STACK_SIZE;
pub const USER_STACK_SIZE: usize = 1 * 1024 * 1024;
pub const USER32_STACK_OFFSET: usize = USER_STACK_OFFSET;
pub const USER32_STACK_OFFSET: usize = USER_STACK_OFFSET;

@ -127,11 +127,11 @@ impl Context {
}
pub unsafe fn switch(&mut self, target: &mut Self) {
self.ttbr = ttbr_el1_read(1);
self.ttbr = ttbr_el1_read(0);
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);
ttbr_el1_write_asid(0, target.asid.value, target.ttbr);
barrier::dsb(barrier::ISH);
Self::__switch(&mut self.stack_top, &mut target.stack_top);
}

@ -1,10 +1,9 @@
//! Memory initialization for aarch64.
use crate::memory::{init_heap, Linear, MemoryAttr, MemorySet, FRAME_ALLOCATOR};
use crate::consts::{MEMORY_OFFSET, KERNEL_OFFSET};
use super::paging::MMIOType;
use aarch64::paging::{memory_attribute::*, PhysFrame as Frame};
use aarch64::{addr::*, barrier, regs::*};
use atags::atags::Atags;
use aarch64::regs::*;
use log::*;
use rcore_memory::PAGE_SIZE;
@ -16,68 +15,12 @@ pub fn init() {
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]);
static PAGE_TABLE_LVL3: PageData = PageData([0; PAGE_SIZE]);
static PAGE_TABLE_LVL2: PageData = PageData([0; PAGE_SIZE]);
let frame_lvl4 = Frame::containing_address(PhysAddr::new(&PAGE_TABLE_LVL4 as *const _ as u64));
let frame_lvl3 = Frame::containing_address(PhysAddr::new(&PAGE_TABLE_LVL3 as *const _ as u64));
let frame_lvl2 = Frame::containing_address(PhysAddr::new(&PAGE_TABLE_LVL2 as *const _ as u64));
super::paging::setup_temp_page_table(frame_lvl4, frame_lvl3, frame_lvl2);
// device.
MAIR_EL1.write(
MAIR_EL1::Attr0.val(MairNormal::config_value()) +
MAIR_EL1::Attr1.val(MairDevice::config_value()) +
MAIR_EL1::Attr2.val(MairNormalNonCacheable::config_value()),
);
// Configure various settings of stage 1 of the EL1 translation regime.
let ips = ID_AA64MMFR0_EL1.read(ID_AA64MMFR0_EL1::PARange);
TCR_EL1.write(
TCR_EL1::TBI1::Ignored +
TCR_EL1::TBI0::Ignored +
TCR_EL1::AS::Bits_16 +
TCR_EL1::IPS.val(ips) +
TCR_EL1::TG1::KiB_4 +
TCR_EL1::SH1::Inner +
TCR_EL1::ORGN1::WriteBack_ReadAlloc_WriteAlloc_Cacheable +
TCR_EL1::IRGN1::WriteBack_ReadAlloc_WriteAlloc_Cacheable +
TCR_EL1::EPD1::EnableTTBR1Walks +
TCR_EL1::A1::UseTTBR1ASID +
TCR_EL1::T1SZ.val(16) +
TCR_EL1::TG0::KiB_4 +
TCR_EL1::SH0::Inner +
TCR_EL1::ORGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable +
TCR_EL1::IRGN0::WriteBack_ReadAlloc_WriteAlloc_Cacheable +
TCR_EL1::EPD0::EnableTTBR0Walks +
TCR_EL1::T0SZ.val(16),
);
// Switch the MMU on.
//
// First, force all previous changes to be seen before the MMU is enabled.
unsafe { barrier::isb(barrier::SY) }
// Enable the MMU and turn on data and instruction caching.
SCTLR_EL1.modify(SCTLR_EL1::M::Enable + SCTLR_EL1::C::Cacheable + SCTLR_EL1::I::Cacheable);
// Force MMU init to complete before next instruction
unsafe { barrier::isb(barrier::SY) }
}
fn init_frame_allocator() {
use crate::consts::MEMORY_OFFSET;
use bit_allocator::BitAlloc;
use core::ops::Range;
let (start, end) = memory_map().expect("failed to find memory map");
let end = super::board::probe_memory().expect("failed to find memory map").1;
let start = (_end as u64 + PAGE_SIZE as u64).wrapping_sub(KERNEL_OFFSET as u64) as usize;
let mut ba = FRAME_ALLOCATOR.lock();
ba.insert(to_range(start, end));
info!("FrameAllocator init end");
@ -102,48 +45,34 @@ static mut KERNEL_MEMORY_SET: Option<MemorySet> = None;
/// remap kernel page table after all initialization.
fn remap_the_kernel() {
let offset = -(KERNEL_OFFSET as isize);
let mut ms = MemorySet::new_bare();
ms.push(0, bootstacktop as usize, MemoryAttr::default(), Linear::new(0), "kstack");
ms.push(stext as usize, etext as usize, MemoryAttr::default().execute().readonly(), Linear::new(0), "text");
ms.push(sdata as usize, edata as usize, MemoryAttr::default(), Linear::new(0), "data");
ms.push(srodata as usize, erodata as usize, MemoryAttr::default().readonly(), Linear::new(0), "rodata");
ms.push(sbss as usize, ebss as usize, MemoryAttr::default(), Linear::new(0), "bss");
ms.push(stext as usize, etext as usize, MemoryAttr::default().execute().readonly(), Linear::new(offset), "text");
ms.push(sdata as usize, edata as usize, MemoryAttr::default(), Linear::new(offset), "data");
ms.push(srodata as usize, erodata as usize, MemoryAttr::default().readonly(), Linear::new(offset), "rodata");
ms.push(sbss as usize, ebss as usize, MemoryAttr::default(), Linear::new(offset), "bss");
ms.push(bootstack as usize, bootstacktop as usize, MemoryAttr::default(), Linear::new(offset), "kstack");
use super::board::{IO_REMAP_BASE, IO_REMAP_END};
ms.push(IO_REMAP_BASE, IO_REMAP_END, MemoryAttr::default().mmio(MMIOType::Device as u8), Linear::new(0), "io_remap");
ms.push(IO_REMAP_BASE, IO_REMAP_END, MemoryAttr::default().mmio(MMIOType::Device as u8), Linear::new(offset), "io_remap");
info!("{:#x?}", ms);
unsafe { ms.get_page_table_mut().activate_as_kernel() }
unsafe { KERNEL_MEMORY_SET = Some(ms) }
info!("kernel remap end");
}
pub fn ioremap(start: usize, len: usize, name: &'static str) -> usize {
pub fn ioremap(paddr: usize, len: usize, name: &'static str) -> usize {
let offset = -(KERNEL_OFFSET as isize);
let vaddr = paddr.wrapping_add(KERNEL_OFFSET);
if let Some(ms) = unsafe { KERNEL_MEMORY_SET.as_mut() } {
ms.push(start, start + len, MemoryAttr::default().mmio(MMIOType::NormalNonCacheable as u8), Linear::new(0), name);
return start;
ms.push(vaddr, vaddr + len, MemoryAttr::default().mmio(MMIOType::NormalNonCacheable as u8), Linear::new(offset), name);
return vaddr;
}
0
}
/// 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 = _end as u32;
let mut atags: Atags = Atags::get();
while let Some(atag) = atags.next() {
if let Some(mem) = atag.mem() {
return Some((binary_end as usize, (mem.start + mem.size) as usize));
}
}
None
}
extern "C" {
fn bootstacktop();
fn stext();
fn etext();
fn sdata();
@ -152,6 +81,8 @@ extern "C" {
fn erodata();
fn sbss();
fn ebss();
fn bootstack();
fn bootstacktop();
fn _start();
fn _end();
}

@ -14,12 +14,11 @@ pub mod syscall;
#[path = "board/raspi3/mod.rs"]
pub mod board;
global_asm!(include_str!("boot/boot.S"));
global_asm!(include_str!("boot/entry.S"));
/// The entry point of kernel
#[no_mangle] // don't mangle the name of this function
pub extern "C" fn rust_main() -> ! {
memory::init_mmu_early(); // Enable mmu and paging
board::init_serial_early();
crate::logging::init();

@ -3,45 +3,13 @@ use rcore_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, Size1GiB};
use aarch64::paging::{FrameAllocator, FrameDeallocator, Page, PhysFrame as Frame, Size4KiB};
use aarch64::paging::memory_attribute::*;
use log::*;
// Depends on kernel
use crate::consts::{KERNEL_PML4, RECURSIVE_INDEX};
use crate::consts::{KERNEL_OFFSET, 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) {
let p4 = unsafe { &mut *(frame_lvl4.start_address().as_u64() as *mut Aarch64PageTable) };
let p3 = unsafe { &mut *(frame_lvl3.start_address().as_u64() as *mut Aarch64PageTable) };
let p2 = unsafe { &mut *(frame_lvl2.start_address().as_u64() as *mut Aarch64PageTable) };
p4.zero();
p3.zero();
p2.zero();
let (start_addr, end_addr) = (0, 0x40000000);
let block_flags = EF::VALID | EF::AF | EF::WRITE | EF::UXN;
for page in Page::<Size2MiB>::range_of(start_addr, end_addr) {
let paddr = PhysAddr::new(page.start_address().as_u64());
use super::board::IO_REMAP_BASE;
if paddr.as_u64() >= IO_REMAP_BASE as u64 {
p2[page.p2_index()].set_block::<Size2MiB>(paddr, block_flags | EF::PXN, MairDevice::attr_value());
} else {
p2[page.p2_index()].set_block::<Size2MiB>(paddr, block_flags, MairNormal::attr_value());
}
}
p3[0].set_frame(frame_lvl2, EF::default(), MairNormal::attr_value());
p3[1].set_block::<Size1GiB>(PhysAddr::new(0x40000000), block_flags | EF::PXN, MairDevice::attr_value());
p4[0].set_frame(frame_lvl3, EF::default(), MairNormal::attr_value());
p4[RECURSIVE_INDEX].set_frame(frame_lvl4, EF::default(), MairNormal::attr_value());
ttbr_el1_write(0, frame_lvl4);
tlb_invalidate_all();
}
pub struct ActivePageTable(RecursivePageTable<'static>);
pub struct PageEntry(PageTableEntry);
@ -62,16 +30,21 @@ impl PageTable for ActivePageTable {
fn get_entry(&mut self, vaddr: usize) -> Option<&mut Entry> {
// get p1 entry
let entry_addr = ((vaddr >> 9) & 0o777_777_777_7770) | (RECURSIVE_INDEX << 39)
| (vaddr & 0xffff_0000_0000_0000);
let entry_addr = ((vaddr >> 9) & 0o777_777_777_7770) | (RECURSIVE_INDEX << 39) | (vaddr & KERNEL_OFFSET);
Some(unsafe { &mut *(entry_addr as *mut PageEntry) })
}
}
impl PageTableExt for ActivePageTable {}
impl PageTableExt for ActivePageTable {
const TEMP_PAGE_ADDR: usize = KERNEL_OFFSET | 0xcafeb000;
}
const ROOT_PAGE_TABLE: *mut Aarch64PageTable =
((RECURSIVE_INDEX << 39) | (RECURSIVE_INDEX << 30) | (RECURSIVE_INDEX << 21) | (RECURSIVE_INDEX << 12)) as *mut Aarch64PageTable;
(KERNEL_OFFSET |
(RECURSIVE_INDEX << 39) |
(RECURSIVE_INDEX << 30) |
(RECURSIVE_INDEX << 21) |
(RECURSIVE_INDEX << 12)) as *mut Aarch64PageTable;
impl ActivePageTable {
pub unsafe fn new() -> Self {
@ -215,11 +188,11 @@ impl InactivePageTable for InactivePageTable0 {
}
unsafe fn set_token(token: usize) {
ttbr_el1_write(1, Frame::containing_address(PhysAddr::new(token as u64)));
ttbr_el1_write(0, Frame::containing_address(PhysAddr::new(token as u64)));
}
fn active_token() -> usize {
ttbr_el1_read(1).start_address().as_u64() as usize
ttbr_el1_read(0).start_address().as_u64() as usize
}
fn flush_tlb() {
@ -227,14 +200,14 @@ impl InactivePageTable for InactivePageTable0 {
}
fn edit<T>(&mut self, f: impl FnOnce(&mut Self::Active) -> T) -> T {
let target = ttbr_el1_read(0).start_address().as_u64() as usize;
let target = ttbr_el1_read(1).start_address().as_u64() as usize;
active_table().with_temporary_map(target, |active_table, p4_table: &mut Aarch64PageTable| {
let backup = p4_table[RECURSIVE_INDEX].clone();
let old_frame = ttbr_el1_read(1);
let old_frame = ttbr_el1_read(0);
// overwrite recursive mapping
p4_table[RECURSIVE_INDEX].set_frame(self.p4_frame.clone(), EF::default(), MairNormal::attr_value());
ttbr_el1_write(1, self.p4_frame.clone());
ttbr_el1_write(0, self.p4_frame.clone());
tlb_invalidate_all();
// execute f in the new context
@ -242,7 +215,7 @@ impl InactivePageTable for InactivePageTable0 {
// restore recursive mapping to original p4 table
p4_table[RECURSIVE_INDEX] = backup;
ttbr_el1_write(1, old_frame);
ttbr_el1_write(0, old_frame);
tlb_invalidate_all();
ret
})
@ -253,11 +226,12 @@ impl InactivePageTable0 {
/// 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 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(0, Frame::of_addr(0));
ttbr_el1_write(1, new_frame);
tlb_invalidate_all();
}
}

@ -410,9 +410,6 @@ fn memory_set_from(elf: &ElfFile<'_>) -> (MemorySet, usize) {
let file_size = ph.file_size() as usize;
let mem_size = ph.mem_size() as usize;
#[cfg(target_arch = "aarch64")]
assert_eq!((virt_addr >> 48), 0xffff, "Segment Fault");
// Get target slice
#[cfg(feature = "no_mmu")]
let target = &mut target[virt_addr - va_begin..virt_addr - va_begin + mem_size];

Loading…
Cancel
Save