Streamlining, cleanup, and minor fixes.

pull/15/head
Andre Richter 6 years ago
parent c017fc16dd
commit 90d88f65b6
No known key found for this signature in database
GPG Key ID: 2116C1AB102F615E

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy -- OBJCOPY = cargo objcopy --
OBJCOPY_PARAMS = --strip-all -O binary OBJCOPY_PARAMS = --strip-all -O binary
@ -31,26 +33,18 @@ UTILS_CONTAINER = andrerichter/raspi3-utils
DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
all: clean kernel8.img .PHONY: all qemu clippy clean objdump nm
target/$(TARGET)/debug/kernel8: src/main.rs all: clean kernel8.img
cargo xbuild --target=$(TARGET)
cp $@ .
target/$(TARGET)/release/kernel8: src/main.rs target/$(TARGET)/release/kernel8: $(SOURCES)
cargo xbuild --target=$(TARGET) --release cargo xbuild --target=$(TARGET) --release
cp $@ .
ifeq ($(DEBUG),1)
kernel8: target/$(TARGET)/debug/kernel8
else
kernel8: target/$(TARGET)/release/kernel8
endif
kernel8.img: kernel8 kernel8.img: target/$(TARGET)/release/kernel8
cp $< .
$(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img
qemu: qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -d in_asm $(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -d in_asm
clean: clean:

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy -- OBJCOPY = cargo objcopy --
OBJCOPY_PARAMS = --strip-all -O binary OBJCOPY_PARAMS = --strip-all -O binary
@ -31,26 +33,18 @@ UTILS_CONTAINER = andrerichter/raspi3-utils
DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
all: clean kernel8.img .PHONY: all qemu clippy clean objdump nm
target/$(TARGET)/debug/kernel8: src/main.rs all: clean kernel8.img
cargo xbuild --target=$(TARGET)
cp $@ .
target/$(TARGET)/release/kernel8: src/main.rs target/$(TARGET)/release/kernel8: $(SOURCES)
cargo xbuild --target=$(TARGET) --release cargo xbuild --target=$(TARGET) --release
cp $@ .
ifeq ($(DEBUG),1)
kernel8: target/$(TARGET)/debug/kernel8
else
kernel8: target/$(TARGET)/release/kernel8
endif
kernel8.img: kernel8 kernel8.img: target/$(TARGET)/release/kernel8
cp $< .
$(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img
qemu: qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -d in_asm $(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -d in_asm
clippy: clippy:

@ -25,8 +25,8 @@
#![no_std] #![no_std]
#![no_main] #![no_main]
raspi3_boot::entry!(kernel_entry);
fn kernel_entry() -> ! { fn kernel_entry() -> ! {
loop {} loop {}
} }
raspi3_boot::entry!(kernel_entry);

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy -- OBJCOPY = cargo objcopy --
OBJCOPY_PARAMS = --strip-all -O binary OBJCOPY_PARAMS = --strip-all -O binary
@ -31,26 +33,18 @@ UTILS_CONTAINER = andrerichter/raspi3-utils
DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
all: clean kernel8.img .PHONY: all qemu clippy clean objdump nm
target/$(TARGET)/debug/kernel8: src/main.rs all: clean kernel8.img
cargo xbuild --target=$(TARGET)
cp $@ .
target/$(TARGET)/release/kernel8: src/main.rs target/$(TARGET)/release/kernel8: $(SOURCES)
cargo xbuild --target=$(TARGET) --release cargo xbuild --target=$(TARGET) --release
cp $@ .
ifeq ($(DEBUG),1)
kernel8: target/$(TARGET)/debug/kernel8
else
kernel8: target/$(TARGET)/release/kernel8
endif
kernel8.img: kernel8 kernel8.img: target/$(TARGET)/release/kernel8
cp $< .
$(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img
qemu: qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial null -serial stdio $(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial null -serial stdio
clippy: clippy:

@ -43,8 +43,8 @@ the host PC.
```console ```console
ferris@box:~$ make qemu ferris@box:~$ make qemu
<Press any key> [0] UART is live!
Hello Rustacean! [1] Press a key to continue booting... Greetings fellow Rustacean!
``` ```
However, let it be said that it is more thrilling to see your first output from However, let it be said that it is more thrilling to see your first output from

Binary file not shown.

Binary file not shown.

@ -31,19 +31,21 @@ const MMIO_BASE: u32 = 0x3F00_0000;
mod gpio; mod gpio;
mod uart; mod uart;
raspi3_boot::entry!(kernel_entry);
fn kernel_entry() -> ! { fn kernel_entry() -> ! {
let uart = uart::MiniUart::new(); let uart = uart::MiniUart::new();
// set up serial console // set up serial console
uart.init(); uart.init();
uart.puts("\n[0] UART is live!\n");
uart.getc(); // Press a key first before being greeted uart.puts("[1] Press a key to continue booting... ");
uart.puts("Hello Rustacean!\n"); uart.getc();
uart.puts("Greetings fellow Rustacean!\n");
// echo everything back // echo everything back
loop { loop {
uart.send(uart.getc()); uart.send(uart.getc());
} }
} }
raspi3_boot::entry!(kernel_entry);

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy -- OBJCOPY = cargo objcopy --
OBJCOPY_PARAMS = --strip-all -O binary OBJCOPY_PARAMS = --strip-all -O binary
@ -31,27 +33,19 @@ UTILS_CONTAINER = andrerichter/raspi3-utils
DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
all: clean kernel8.img .PHONY: all qemu clippy clean objdump nm
target/$(TARGET)/debug/kernel8: src/main.rs all: clean kernel8.img
cargo xbuild --target=$(TARGET)
cp $@ .
target/$(TARGET)/release/kernel8: src/main.rs target/$(TARGET)/release/kernel8: $(SOURCES)
cargo xbuild --target=$(TARGET) --release cargo xbuild --target=$(TARGET) --release
cp $@ .
ifeq ($(DEBUG),1)
kernel8: target/$(TARGET)/debug/kernel8
else
kernel8: target/$(TARGET)/release/kernel8
endif
kernel8.img: kernel8 kernel8.img: target/$(TARGET)/release/kernel8
cp $< .
$(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img
qemu: qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial null -serial stdio $(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
clippy: clippy:
cargo xclippy --target=$(TARGET) cargo xclippy --target=$(TARGET)

Binary file not shown.

Binary file not shown.

@ -34,14 +34,17 @@ mod uart;
use core::sync::atomic::{compiler_fence, Ordering}; use core::sync::atomic::{compiler_fence, Ordering};
raspi3_boot::entry!(kernel_entry);
fn kernel_entry() -> ! { fn kernel_entry() -> ! {
let mut mbox = mbox::Mbox::new(); let mut mbox = mbox::Mbox::new();
let uart = uart::MiniUart::new(); let uart = uart::MiniUart::new();
// set up serial console // set up serial console
uart.init(); uart.init();
uart.puts("\n[0] UART is live!\n");
uart.puts("[1] Press a key to continue booting... ");
uart.getc();
uart.puts("Greetings fellow Rustacean!\n");
// get the board's unique serial number with a mailbox call // get the board's unique serial number with a mailbox call
mbox.buffer[0] = 8 * 4; // length of the message mbox.buffer[0] = 8 * 4; // length of the message
@ -64,16 +67,13 @@ fn kernel_entry() -> ! {
Ok(()) => true, Ok(()) => true,
}; };
uart.getc(); // Press a key first before being greeted
uart.puts("Hello Rustacean!\n");
if serial_avail { if serial_avail {
uart.puts("My serial number is: "); uart.puts("[i] My serial number is: 0x");
uart.hex(mbox.buffer[6]); uart.hex(mbox.buffer[6]);
uart.hex(mbox.buffer[5]); uart.hex(mbox.buffer[5]);
uart.puts("\n"); uart.puts("\n");
} else { } else {
uart.puts("Unable to query serial!\n"); uart.puts("[i] Unable to query serial!\n");
} }
// echo everything back // echo everything back
@ -81,3 +81,5 @@ fn kernel_entry() -> ! {
uart.send(uart.getc()); uart.send(uart.getc());
} }
} }
raspi3_boot::entry!(kernel_entry);

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy -- OBJCOPY = cargo objcopy --
OBJCOPY_PARAMS = --strip-all -O binary OBJCOPY_PARAMS = --strip-all -O binary
@ -31,26 +33,18 @@ UTILS_CONTAINER = andrerichter/raspi3-utils
DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
all: clean kernel8.img .PHONY: all qemu clippy clean objdump nm
target/$(TARGET)/debug/kernel8: src/main.rs all: clean kernel8.img
cargo xbuild --target=$(TARGET)
cp $@ .
target/$(TARGET)/release/kernel8: src/main.rs target/$(TARGET)/release/kernel8: $(SOURCES)
cargo xbuild --target=$(TARGET) --release cargo xbuild --target=$(TARGET) --release
cp $@ .
ifeq ($(DEBUG),1)
kernel8: target/$(TARGET)/debug/kernel8
else
kernel8: target/$(TARGET)/release/kernel8
endif
kernel8.img: kernel8 kernel8.img: target/$(TARGET)/release/kernel8
cp $< .
$(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img
qemu: qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio $(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
clippy: clippy:

Binary file not shown.

Binary file not shown.

@ -34,17 +34,22 @@ mod uart;
use core::sync::atomic::{compiler_fence, Ordering}; use core::sync::atomic::{compiler_fence, Ordering};
raspi3_boot::entry!(kernel_entry);
fn kernel_entry() -> ! { fn kernel_entry() -> ! {
let mut mbox = mbox::Mbox::new(); let mut mbox = mbox::Mbox::new();
let uart = uart::Uart::new(); let uart = uart::Uart::new();
// set up serial console // set up serial console
if uart.init(&mut mbox).is_err() { match uart.init(&mut mbox) {
unsafe { asm!("wfe" :::: "volatile") }; // If UART fails, abort early Ok(_) => uart.puts("\n[0] UART is live!\n"),
Err(_) => loop {
unsafe { asm!("wfe" :::: "volatile") }; // If UART fails, abort early
},
} }
uart.puts("[1] Press a key to continue booting... ");
uart.getc();
uart.puts("Greetings fellow Rustacean!\n");
// get the board's unique serial number with a mailbox call // get the board's unique serial number with a mailbox call
mbox.buffer[0] = 8 * 4; // length of the message mbox.buffer[0] = 8 * 4; // length of the message
mbox.buffer[1] = mbox::REQUEST; // this is a request message mbox.buffer[1] = mbox::REQUEST; // this is a request message
@ -66,16 +71,13 @@ fn kernel_entry() -> ! {
Ok(()) => true, Ok(()) => true,
}; };
uart.getc(); // Press a key first before being greeted
uart.puts("Hello Rustacean!\n");
if serial_avail { if serial_avail {
uart.puts("My serial number is: "); uart.puts("[i] My serial number is: 0x");
uart.hex(mbox.buffer[6]); uart.hex(mbox.buffer[6]);
uart.hex(mbox.buffer[5]); uart.hex(mbox.buffer[5]);
uart.puts("\n"); uart.puts("\n");
} else { } else {
uart.puts("Unable to query serial!\n"); uart.puts("[i] Unable to query serial!\n");
} }
// echo everything back // echo everything back
@ -83,3 +85,5 @@ fn kernel_entry() -> ! {
uart.send(uart.getc()); uart.send(uart.getc());
} }
} }
raspi3_boot::entry!(kernel_entry);

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy -- OBJCOPY = cargo objcopy --
OBJCOPY_PARAMS = --strip-all -O binary OBJCOPY_PARAMS = --strip-all -O binary
@ -31,26 +33,18 @@ UTILS_CONTAINER = andrerichter/raspi3-utils
DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work DOCKER_CMD = docker run -it --rm -v $(shell pwd):/work -w /work
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
all: clean kernel8.img .PHONY: all qemu clippy clean objdump nm
target/$(TARGET)/debug/kernel8: src/main.rs all: clean kernel8.img
cargo xbuild --target=$(TARGET)
cp $@ .
target/$(TARGET)/release/kernel8: src/main.rs target/$(TARGET)/release/kernel8: $(SOURCES)
cargo xbuild --target=$(TARGET) --release cargo xbuild --target=$(TARGET) --release
cp $@ .
ifeq ($(DEBUG),1)
kernel8: target/$(TARGET)/debug/kernel8
else
kernel8: target/$(TARGET)/release/kernel8
endif
kernel8.img: kernel8 kernel8.img: target/$(TARGET)/release/kernel8
cp $< .
$(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img
qemu: qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio $(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
clippy: clippy:

@ -32,8 +32,6 @@ mod gpio;
mod mbox; mod mbox;
mod uart; mod uart;
raspi3_boot::entry!(kernel_entry);
fn kernel_entry() -> ! { fn kernel_entry() -> ! {
let mut mbox = mbox::Mbox::new(); let mut mbox = mbox::Mbox::new();
let uart = uart::Uart::new(); let uart = uart::Uart::new();
@ -77,3 +75,5 @@ fn kernel_entry() -> ! {
// Jump to loaded kernel and never return! // Jump to loaded kernel and never return!
kernel() kernel()
} }
raspi3_boot::entry!(kernel_entry);

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy -- OBJCOPY = cargo objcopy --
OBJCOPY_PARAMS = --strip-all -O binary OBJCOPY_PARAMS = --strip-all -O binary
@ -33,29 +35,21 @@ DOCKER_TTY = --privileged -v /dev:/dev
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img
all: clean kernel8.img .PHONY: all qemu raspboot clippy clean objdump nm
target/$(TARGET)/debug/kernel8: src/main.rs all: clean kernel8.img
cargo xbuild --target=$(TARGET)
cp $@ .
target/$(TARGET)/release/kernel8: src/main.rs target/$(TARGET)/release/kernel8: $(SOURCES)
cargo xbuild --target=$(TARGET) --release cargo xbuild --target=$(TARGET) --release
cp $@ .
ifeq ($(DEBUG),1)
kernel8: target/$(TARGET)/debug/kernel8
else
kernel8: target/$(TARGET)/release/kernel8
endif
kernel8.img: kernel8 kernel8.img: target/$(TARGET)/release/kernel8
cp $< .
$(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img
qemu: qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio $(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
raspboot: raspboot: all
$(DOCKER_CMD) $(DOCKER_TTY) $(UTILS_CONTAINER) $(RASPBOOT_CMD) $(DOCKER_CMD) $(DOCKER_TTY) $(UTILS_CONTAINER) $(RASPBOOT_CMD)
clippy: clippy:

@ -64,20 +64,20 @@ replaced it with a Rust function. Why? Because we can, for the fun of it.
#[link_section = ".text.boot"] #[link_section = ".text.boot"]
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn _boot_cores() -> ! { pub unsafe extern "C" fn _boot_cores() -> ! {
use cortex_a::{asm, regs::mpidr_el1::*, regs::sp::*}; use cortex_a::{asm, regs::*};
const CORE_0: u64 = 0;
const CORE_MASK: u64 = 0x3; const CORE_MASK: u64 = 0x3;
const STACK_START: u64 = 0x80_000; const STACK_START: u64 = 0x80_000;
match MPIDR_EL1.get() & CORE_MASK { if CORE_0 == MPIDR_EL1.get() & CORE_MASK {
0 => { SP.set(STACK_START);
SP.set(STACK_START); reset()
reset() } else {
} // if not core0, infinitely wait for events
_ => loop { loop {
// if not core0, infinitely wait for events
asm::wfe(); asm::wfe();
}, }
} }
} }
``` ```
@ -93,7 +93,7 @@ should yield something like the following, where you can see that the stack
pointer is not used apart from ourselves setting it. pointer is not used apart from ourselves setting it.
```console ```console
ferris@box:~$ cargo objdump --target aarch64-raspi3-none-elf.json -- -disassemble -print-imm-hex kernel8 ferris@box:~$ cargo objdump --target aarch64-unknown-none -- -disassemble -print-imm-hex kernel8
[...] (Some output omitted) [...] (Some output omitted)
@ -103,9 +103,9 @@ _boot_cores:
80008: 60 00 00 54 b.eq #0xc <_boot_cores+0x14> 80008: 60 00 00 54 b.eq #0xc <_boot_cores+0x14>
8000c: 5f 20 03 d5 wfe 8000c: 5f 20 03 d5 wfe
80010: ff ff ff 17 b #-0x4 <_boot_cores+0xc> 80010: ff ff ff 17 b #-0x4 <_boot_cores+0xc>
80014: e8 03 09 32 orr w8, wzr, #0x800000 80014: e8 03 0d 32 orr w8, wzr, #0x80000
80018: 1f 01 00 91 mov sp, x8 80018: 1f 01 00 91 mov sp, x8
8001c: 35 02 00 94 bl #0x8d4 <raspi3_boot::reset::h90bc56752de44d1b> 8001c: e0 01 00 94 bl #0x780 <raspi3_boot::reset::h6e794100bed457dc>
``` ```
It is important to always manually check this, and not blindly rely on the It is important to always manually check this, and not blindly rely on the

Binary file not shown.

Binary file not shown.

@ -73,17 +73,17 @@ unsafe fn reset() -> ! {
pub unsafe extern "C" fn _boot_cores() -> ! { pub unsafe extern "C" fn _boot_cores() -> ! {
use cortex_a::{asm, regs::*}; use cortex_a::{asm, regs::*};
const CORE_0: u64 = 0;
const CORE_MASK: u64 = 0x3; const CORE_MASK: u64 = 0x3;
const STACK_START: u64 = 0x80_000; const STACK_START: u64 = 0x80_000;
match MPIDR_EL1.get() & CORE_MASK { if CORE_0 == MPIDR_EL1.get() & CORE_MASK {
0 => { SP.set(STACK_START);
SP.set(STACK_START); reset()
reset() } else {
} // if not core0, infinitely wait for events
_ => loop { loop {
// if not core0, infinitely wait for events
asm::wfe(); asm::wfe();
}, }
} }
} }

@ -33,19 +33,22 @@ mod uart;
use core::sync::atomic::{compiler_fence, Ordering}; use core::sync::atomic::{compiler_fence, Ordering};
raspi3_boot::entry!(kernel_entry);
fn kernel_entry() -> ! { fn kernel_entry() -> ! {
let mut mbox = mbox::Mbox::new(); let mut mbox = mbox::Mbox::new();
let uart = uart::Uart::new(); let uart = uart::Uart::new();
// set up serial console // set up serial console
if uart.init(&mut mbox).is_err() { match uart.init(&mut mbox) {
loop { Ok(_) => uart.puts("\n[0] UART is live!\n"),
cortex_a::asm::wfe() Err(_) => loop {
} // If UART fails, abort early cortex_a::asm::wfe() // If UART fails, abort early
},
} }
uart.puts("[1] Press a key to continue booting... ");
uart.getc();
uart.puts("Greetings fellow Rustacean!\n");
// get the board's unique serial number with a mailbox call // get the board's unique serial number with a mailbox call
mbox.buffer[0] = 8 * 4; // length of the message mbox.buffer[0] = 8 * 4; // length of the message
mbox.buffer[1] = mbox::REQUEST; // this is a request message mbox.buffer[1] = mbox::REQUEST; // this is a request message
@ -67,16 +70,13 @@ fn kernel_entry() -> ! {
Ok(()) => true, Ok(()) => true,
}; };
uart.getc(); // Press a key first before being greeted
uart.puts("Hello Rustacean!\n");
if serial_avail { if serial_avail {
uart.puts("My serial number is: "); uart.puts("[i] My serial number is: 0x");
uart.hex(mbox.buffer[6]); uart.hex(mbox.buffer[6]);
uart.hex(mbox.buffer[5]); uart.hex(mbox.buffer[5]);
uart.puts("\n"); uart.puts("\n");
} else { } else {
uart.puts("Unable to query serial!\n"); uart.puts("[i] Unable to query serial!\n");
} }
// echo everything back // echo everything back
@ -84,3 +84,5 @@ fn kernel_entry() -> ! {
uart.send(uart.getc()); uart.send(uart.getc());
} }
} }
raspi3_boot::entry!(kernel_entry);

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy -- OBJCOPY = cargo objcopy --
OBJCOPY_PARAMS = --strip-all -O binary OBJCOPY_PARAMS = --strip-all -O binary
@ -33,29 +35,21 @@ DOCKER_TTY = --privileged -v /dev:/dev
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img
all: clean kernel8.img .PHONY: all qemu raspboot clippy clean objdump nm
target/$(TARGET)/debug/kernel8: src/main.rs all: clean kernel8.img
cargo xbuild --target=$(TARGET)
cp $@ .
target/$(TARGET)/release/kernel8: src/main.rs target/$(TARGET)/release/kernel8: $(SOURCES)
cargo xbuild --target=$(TARGET) --release cargo xbuild --target=$(TARGET) --release
cp $@ .
ifeq ($(DEBUG),1)
kernel8: target/$(TARGET)/debug/kernel8
else
kernel8: target/$(TARGET)/release/kernel8
endif
kernel8.img: kernel8 kernel8.img: target/$(TARGET)/release/kernel8
cp $< .
$(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img
qemu: qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio $(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
raspboot: raspboot: all
$(DOCKER_CMD) $(DOCKER_TTY) $(UTILS_CONTAINER) $(RASPBOOT_CMD) $(DOCKER_CMD) $(DOCKER_TTY) $(UTILS_CONTAINER) $(RASPBOOT_CMD)
clippy: clippy:

Binary file not shown.

Binary file not shown.

@ -73,17 +73,17 @@ unsafe fn reset() -> ! {
pub unsafe extern "C" fn _boot_cores() -> ! { pub unsafe extern "C" fn _boot_cores() -> ! {
use cortex_a::{asm, regs::*}; use cortex_a::{asm, regs::*};
const CORE_0: u64 = 0;
const CORE_MASK: u64 = 0x3; const CORE_MASK: u64 = 0x3;
const STACK_START: u64 = 0x80_000; const STACK_START: u64 = 0x80_000;
match MPIDR_EL1.get() & CORE_MASK { if CORE_0 == MPIDR_EL1.get() & CORE_MASK {
0 => { SP.set(STACK_START);
SP.set(STACK_START); reset()
reset() } else {
} // if not core0, infinitely wait for events
_ => loop { loop {
// if not core0, infinitely wait for events
asm::wfe(); asm::wfe();
}, }
} }
} }

@ -32,26 +32,28 @@ mod mbox;
mod rand; mod rand;
mod uart; mod uart;
raspi3_boot::entry!(kernel_entry);
fn kernel_entry() -> ! { fn kernel_entry() -> ! {
let mut mbox = mbox::Mbox::new(); let mut mbox = mbox::Mbox::new();
let uart = uart::Uart::new(); let uart = uart::Uart::new();
// set up serial console // set up serial console
if uart.init(&mut mbox).is_err() { match uart.init(&mut mbox) {
loop { Ok(_) => uart.puts("\n[0] UART is live!\n"),
cortex_a::asm::wfe() Err(_) => loop {
} // If UART fails, abort early cortex_a::asm::wfe() // If UART fails, abort early
},
} }
uart.getc(); // Press a key first before being greeted uart.puts("[1] Press a key to continue booting... ");
uart.puts("Hello Rustacean!\n"); uart.getc();
uart.puts("Greetings fellow Rustacean!\n");
// set up random number generator // set up random number generator
let rng = rand::Rng::new(); let rng = rand::Rng::new();
rng.init(); rng.init();
uart.puts("[2] RNG ready.\n\n");
uart.puts("Press any key to generate random numbers.\n"); uart.puts("Press any key to generate random numbers.\n");
// echo everything back // echo everything back
@ -63,3 +65,5 @@ fn kernel_entry() -> ! {
uart.puts("\n"); uart.puts("\n");
} }
} }
raspi3_boot::entry!(kernel_entry);

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy -- OBJCOPY = cargo objcopy --
OBJCOPY_PARAMS = --strip-all -O binary OBJCOPY_PARAMS = --strip-all -O binary
@ -33,29 +35,21 @@ DOCKER_TTY = --privileged -v /dev:/dev
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img
all: clean kernel8.img .PHONY: all qemu raspboot clippy clean objdump nm
target/$(TARGET)/debug/kernel8: src/main.rs all: clean kernel8.img
cargo xbuild --target=$(TARGET)
cp $@ .
target/$(TARGET)/release/kernel8: src/main.rs target/$(TARGET)/release/kernel8: $(SOURCES)
cargo xbuild --target=$(TARGET) --release cargo xbuild --target=$(TARGET) --release
cp $@ .
ifeq ($(DEBUG),1)
kernel8: target/$(TARGET)/debug/kernel8
else
kernel8: target/$(TARGET)/release/kernel8
endif
kernel8.img: kernel8 kernel8.img: target/$(TARGET)/release/kernel8
cp $< .
$(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img
qemu: qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio $(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
raspboot: raspboot: all
$(DOCKER_CMD) $(DOCKER_TTY) $(UTILS_CONTAINER) $(RASPBOOT_CMD) $(DOCKER_CMD) $(DOCKER_TTY) $(UTILS_CONTAINER) $(RASPBOOT_CMD)
clippy: clippy:

Binary file not shown.

Binary file not shown.

@ -73,17 +73,17 @@ unsafe fn reset() -> ! {
pub unsafe extern "C" fn _boot_cores() -> ! { pub unsafe extern "C" fn _boot_cores() -> ! {
use cortex_a::{asm, regs::*}; use cortex_a::{asm, regs::*};
const CORE_0: u64 = 0;
const CORE_MASK: u64 = 0x3; const CORE_MASK: u64 = 0x3;
const STACK_START: u64 = 0x80_000; const STACK_START: u64 = 0x80_000;
match MPIDR_EL1.get() & CORE_MASK { if CORE_0 == MPIDR_EL1.get() & CORE_MASK {
0 => { SP.set(STACK_START);
SP.set(STACK_START); reset()
reset() } else {
} // if not core0, infinitely wait for events
_ => loop { loop {
// if not core0, infinitely wait for events
asm::wfe(); asm::wfe();
}, }
} }
} }

@ -32,40 +32,42 @@ mod gpio;
mod mbox; mod mbox;
mod uart; mod uart;
raspi3_boot::entry!(kernel_entry);
fn kernel_entry() -> ! { fn kernel_entry() -> ! {
let mut mbox = mbox::Mbox::new(); let mut mbox = mbox::Mbox::new();
let uart = uart::Uart::new(); let uart = uart::Uart::new();
// set up serial console // set up serial console
if uart.init(&mut mbox).is_err() { match uart.init(&mut mbox) {
loop { Ok(_) => uart.puts("\n[0] UART is live!\n"),
cortex_a::asm::wfe() Err(_) => loop {
} // If UART fails, abort early cortex_a::asm::wfe() // If UART fails, abort early
},
} }
uart.getc(); // Press a key first before being greeted uart.puts("[1] Press a key to continue booting... ");
uart.puts("Hello Rustacean!\n"); uart.getc();
uart.puts("Greetings fellow Rustacean!\n");
uart.puts("Waiting 1_000_000 CPU cycles (ARM CPU): "); uart.puts("[i] Waiting 1_000_000 CPU cycles (ARM CPU): ");
delays::wait_cycles(1_000_000); delays::wait_cycles(1_000_000);
uart.puts("OK\n"); uart.puts("OK\n");
uart.puts("Waiting 1 second (ARM CPU): "); uart.puts("[i] Waiting 1 second (ARM CPU): ");
delays::wait_msec(1_000_000); delays::wait_msec(1_000_000);
uart.puts("OK\n"); uart.puts("OK\n");
let t = delays::SysTmr::new(); let t = delays::SysTmr::new();
if t.get_system_timer() != 0 { if t.get_system_timer() != 0 {
uart.puts("Waiting 1 second (BCM System Timer): "); uart.puts("[i] Waiting 1 second (BCM System Timer): ");
t.wait_msec_st(1_000_000); t.wait_msec_st(1_000_000);
uart.puts("OK\n"); uart.puts("OK\n");
} }
uart.puts("Looping forever now!\n"); uart.puts("[i] Looping forever now!\n");
loop { loop {
delays::wait_msec(1_000_000); delays::wait_msec(1_000_000);
uart.puts("Tick: 1s\n"); uart.puts("Tick: 1s\n");
} }
} }
raspi3_boot::entry!(kernel_entry);

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy -- OBJCOPY = cargo objcopy --
OBJCOPY_PARAMS = --strip-all -O binary OBJCOPY_PARAMS = --strip-all -O binary
@ -33,29 +35,21 @@ DOCKER_TTY = --privileged -v /dev:/dev
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img
all: clean kernel8.img .PHONY: all qemu raspboot clippy clean objdump nm
target/$(TARGET)/debug/kernel8: src/main.rs all: clean kernel8.img
cargo xbuild --target=$(TARGET)
cp $@ .
target/$(TARGET)/release/kernel8: src/main.rs target/$(TARGET)/release/kernel8: $(SOURCES)
cargo xbuild --target=$(TARGET) --release cargo xbuild --target=$(TARGET) --release
cp $@ .
ifeq ($(DEBUG),1)
kernel8: target/$(TARGET)/debug/kernel8
else
kernel8: target/$(TARGET)/release/kernel8
endif
kernel8.img: kernel8 kernel8.img: target/$(TARGET)/release/kernel8
cp $< .
$(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img
qemu: qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio $(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
raspboot: raspboot: all
$(DOCKER_CMD) $(DOCKER_TTY) $(UTILS_CONTAINER) $(RASPBOOT_CMD) $(DOCKER_CMD) $(DOCKER_TTY) $(UTILS_CONTAINER) $(RASPBOOT_CMD)
clippy: clippy:

Binary file not shown.

Binary file not shown.

@ -73,17 +73,17 @@ unsafe fn reset() -> ! {
pub unsafe extern "C" fn _boot_cores() -> ! { pub unsafe extern "C" fn _boot_cores() -> ! {
use cortex_a::{asm, regs::*}; use cortex_a::{asm, regs::*};
const CORE_0: u64 = 0;
const CORE_MASK: u64 = 0x3; const CORE_MASK: u64 = 0x3;
const STACK_START: u64 = 0x80_000; const STACK_START: u64 = 0x80_000;
match MPIDR_EL1.get() & CORE_MASK { if CORE_0 == MPIDR_EL1.get() & CORE_MASK {
0 => { SP.set(STACK_START);
SP.set(STACK_START); reset()
reset() } else {
} // if not core0, infinitely wait for events
_ => loop { loop {
// if not core0, infinitely wait for events
asm::wfe(); asm::wfe();
}, }
} }
} }

@ -33,8 +33,6 @@ mod mbox;
mod power; mod power;
mod uart; mod uart;
raspi3_boot::entry!(kernel_entry);
fn kernel_entry() -> ! { fn kernel_entry() -> ! {
let gpio = gpio::GPIO::new(); let gpio = gpio::GPIO::new();
let mut mbox = mbox::Mbox::new(); let mut mbox = mbox::Mbox::new();
@ -42,14 +40,16 @@ fn kernel_entry() -> ! {
let power = power::Power::new(); let power = power::Power::new();
// set up serial console // set up serial console
if uart.init(&mut mbox, &gpio).is_err() { match uart.init(&mut mbox, &gpio) {
loop { Ok(_) => uart.puts("\n[0] UART is live!\n"),
cortex_a::asm::wfe() Err(_) => loop {
} // If UART fails, abort early cortex_a::asm::wfe() // If UART fails, abort early
},
} }
uart.getc(); // Press a key first before being greeted uart.puts("[1] Press a key to continue booting... ");
uart.puts("Hello Rustacean!\n\n"); uart.getc();
uart.puts("Greetings fellow Rustacean!\n");
loop { loop {
uart.puts("\n 1 - power off\n 2 - reset\nChoose one: "); uart.puts("\n 1 - power off\n 2 - reset\nChoose one: ");
@ -67,3 +67,5 @@ fn kernel_entry() -> ! {
} }
} }
} }
raspi3_boot::entry!(kernel_entry);

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy -- OBJCOPY = cargo objcopy --
OBJCOPY_PARAMS = --strip-all -O binary OBJCOPY_PARAMS = --strip-all -O binary
@ -33,23 +35,15 @@ DOCKER_TTY = --privileged -v /dev:/dev
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img
all: clean kernel8.img .PHONY: all qemu raspboot clippy clean objdump nm
target/$(TARGET)/debug/kernel8: src/main.rs all: clean kernel8.img
cargo xbuild --target=$(TARGET)
cp $@ .
target/$(TARGET)/release/kernel8: src/main.rs target/$(TARGET)/release/kernel8: $(SOURCES)
cargo xbuild --target=$(TARGET) --release cargo xbuild --target=$(TARGET) --release
cp $@ .
ifeq ($(DEBUG),1)
kernel8: target/$(TARGET)/debug/kernel8
else
kernel8: target/$(TARGET)/release/kernel8
endif
kernel8.img: kernel8 kernel8.img: target/$(TARGET)/release/kernel8
cp $< .
$(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img
qemu: all qemu: all

@ -8,20 +8,20 @@ Cortex-A53 processor: `Exception levels`.
TODO: Write rest of tutorial. TODO: Write rest of tutorial.
```text ```text
raspi3_boot::setup_and_enter_el1_from_el2::hb2c2ac4f6a7ddb7e: raspi3_boot::setup_and_enter_el1_from_el2::h568f1410ae7cc9b8:
80954: e8 03 1f aa mov x8, xzr 808c0: e8 03 1f aa mov x8, xzr
80958: e9 07 00 32 orr w9, wzr, #0x3 808c4: e9 07 00 32 orr w9, wzr, #0x3
8095c: 4a 00 80 52 mov w10, #0x2 808c8: 09 e1 1c d5 msr CNTHCTL_EL2, x9
80960: 0a 00 b0 72 movk w10, #0x8000, lsl #16 808cc: 4a 00 80 52 mov w10, #0x2
80964: 09 e1 1c d5 msr CNTHCTL_EL2, x9 808d0: 68 e0 1c d5 msr CNTVOFF_EL2, x8
80968: 68 e0 1c d5 msr CNTVOFF_EL2, x8 808d4: 08 00 00 90 adrp x8, #0x0
8096c: 08 00 00 90 adrp x8, #0x0 808d8: 0a 00 b0 72 movk w10, #0x8000, lsl #16
80970: ab 78 80 52 mov w11, #0x3c5 808dc: 0a 11 1c d5 msr HCR_EL2, x10
80974: 0a 11 1c d5 msr HCR_EL2, x10 808e0: ab 78 80 52 mov w11, #0x3c5
80978: ec 03 0d 32 orr w12, wzr, #0x80000 808e4: 0b 40 1c d5 msr SPSR_EL2, x11
8097c: 08 81 24 91 add x8, x8, #0x920 808e8: ec 03 0d 32 orr w12, wzr, #0x80000
80980: 0b 40 1c d5 msr SPSR_EL2, x11 808ec: 08 31 22 91 add x8, x8, #0x88c
80984: 28 40 1c d5 msr ELR_EL2, x8 808f0: 28 40 1c d5 msr ELR_EL2, x8
80988: 0c 41 1c d5 msr SP_EL1, x12 808f4: 0c 41 1c d5 msr SP_EL1, x12
8098c: e0 03 9f d6 eret 808f8: e0 03 9f d6 eret
``` ```

Binary file not shown.

Binary file not shown.

@ -69,6 +69,8 @@ unsafe fn reset() -> ! {
fn setup_and_enter_el1_from_el2() -> ! { fn setup_and_enter_el1_from_el2() -> ! {
use cortex_a::{asm, regs::*}; use cortex_a::{asm, regs::*};
const STACK_START: u64 = 0x80_000;
// Enable timer counter registers for EL1 // Enable timer counter registers for EL1
CNTHCTL_EL2.write(CNTHCTL_EL2::EL1PCEN::SET + CNTHCTL_EL2::EL1PCTEN::SET); CNTHCTL_EL2.write(CNTHCTL_EL2::EL1PCEN::SET + CNTHCTL_EL2::EL1PCTEN::SET);
@ -96,7 +98,7 @@ fn setup_and_enter_el1_from_el2() -> ! {
// Set up SP_EL1 (stack pointer), which will be used by EL1 once // Set up SP_EL1 (stack pointer), which will be used by EL1 once
// we "return" to it. // we "return" to it.
SP_EL1.set(0x80_000); SP_EL1.set(STACK_START);
// Use `eret` to "return" to EL1. This will result in execution of // Use `eret` to "return" to EL1. This will result in execution of
// `reset()` in EL1. // `reset()` in EL1.
@ -116,10 +118,8 @@ pub unsafe extern "C" fn _boot_cores() -> ! {
const CORE_MASK: u64 = 0x3; const CORE_MASK: u64 = 0x3;
const EL2: u32 = CurrentEL::EL::EL2.value; const EL2: u32 = CurrentEL::EL::EL2.value;
if let CORE_0 = MPIDR_EL1.get() & CORE_MASK { if (CORE_0 == MPIDR_EL1.get() & CORE_MASK) && (EL2 == CurrentEL.get()) {
if let EL2 = CurrentEL.get() { setup_and_enter_el1_from_el2()
setup_and_enter_el1_from_el2()
}
} }
// if not core0 or EL != 2, infinitely wait for events // if not core0 or EL != 2, infinitely wait for events

@ -22,6 +22,7 @@
* SOFTWARE. * SOFTWARE.
*/ */
use cortex_a::asm;
use cortex_a::regs::*; use cortex_a::regs::*;
/* /*
@ -53,3 +54,15 @@ pub fn wait_msec(n: u32) {
// Disable counting again // Disable counting again
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR); CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
} }
/*
*
* Using the CPU's cycles
*
*/
/// Wait N CPU cycles (ARM CPU only)
pub fn wait_cycles(cyc: u32) {
for _ in 0..cyc {
asm::nop();
}
}

@ -23,6 +23,7 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops;
use register::{mmio::ReadWrite, register_bitfields}; use register::{mmio::ReadWrite, register_bitfields};
// Descriptions taken from // Descriptions taken from
@ -66,10 +67,55 @@ register_bitfields! {
] ]
} }
pub const GPFSEL1: *const ReadWrite<u32, GPFSEL1::Register> = const GPIO_BASE: u32 = MMIO_BASE + 0x200_000;
(MMIO_BASE + 0x0020_0004) as *const ReadWrite<u32, GPFSEL1::Register>;
pub const GPPUD: *const ReadWrite<u32> = (MMIO_BASE + 0x0020_0094) as *const ReadWrite<u32>; #[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
pub GPFSEL0: ReadWrite<u32>, // 0x00
pub GPFSEL1: ReadWrite<u32, GPFSEL1::Register>, // 0x04
pub GPFSEL2: ReadWrite<u32>, // 0x08
pub GPFSEL3: ReadWrite<u32>, // 0x0C
pub GPFSEL4: ReadWrite<u32>, // 0x10
pub GPFSEL5: ReadWrite<u32>, // 0x14
__reserved_0: u32, // 0x18
GPSET0: ReadWrite<u32>, // 0x1C
GPSET1: ReadWrite<u32>, // 0x20
__reserved_1: u32, //
GPCLR0: ReadWrite<u32>, // 0x28
__reserved_2: [u32; 2], //
GPLEV0: ReadWrite<u32>, // 0x34
GPLEV1: ReadWrite<u32>, // 0x38
__reserved_3: u32, //
GPEDS0: ReadWrite<u32>, // 0x40
GPEDS1: ReadWrite<u32>, // 0x44
__reserved_4: [u32; 7], //
GPHEN0: ReadWrite<u32>, // 0x64
GPHEN1: ReadWrite<u32>, // 0x68
__reserved_5: [u32; 10], //
pub GPPUD: ReadWrite<u32>, // 0x94
pub GPPUDCLK0: ReadWrite<u32, GPPUDCLK0::Register>, // 0x98
pub GPPUDCLK1: ReadWrite<u32>, // 0x9C
}
/// Public interface to the GPIO MMIO area
pub struct GPIO;
pub const GPPUDCLK0: *const ReadWrite<u32, GPPUDCLK0::Register> = impl ops::Deref for GPIO {
(MMIO_BASE + 0x0020_0098) as *const ReadWrite<u32, GPPUDCLK0::Register>; type Target = RegisterBlock;
fn deref(&self) -> &Self::Target {
unsafe { &*Self::ptr() }
}
}
impl GPIO {
pub fn new() -> GPIO {
GPIO
}
/// Returns a pointer to the register block
fn ptr() -> *const RegisterBlock {
GPIO_BASE as *const _
}
}

@ -36,17 +36,17 @@ use cortex_a::regs::*;
fn check_timer(uart: &uart::Uart) { fn check_timer(uart: &uart::Uart) {
uart.puts( uart.puts(
"Testing EL1 access to timer registers.\n\ "Testing EL1 access to timer registers:\
Delaying for 3 seconds now.\n", \n Delaying for 3 seconds now.\n",
); );
delays::wait_msec(1000); delays::wait_msec(1_000_000);
uart.puts("1.."); uart.puts(" 1..");
delays::wait_msec(1000); delays::wait_msec(1_000_000);
uart.puts("2.."); uart.puts("2..");
delays::wait_msec(1000); delays::wait_msec(1_000_000);
uart.puts( uart.puts(
"3\n\ "3\
Works!\n\n", \n Works!\n\n",
); );
} }
@ -55,10 +55,10 @@ fn check_daif(uart: &uart::Uart) {
let daif = DAIF.extract(); let daif = DAIF.extract();
for x in &[ for x in &[
("D: ", DAIF::D), (" D: ", DAIF::D),
("A: ", DAIF::A), (" A: ", DAIF::A),
("I: ", DAIF::I), (" I: ", DAIF::I),
("F: ", DAIF::F), (" F: ", DAIF::F),
] { ] {
uart.puts(x.0); uart.puts(x.0);
if daif.is_set(x.1) { if daif.is_set(x.1) {
@ -69,24 +69,25 @@ fn check_daif(uart: &uart::Uart) {
} }
} }
raspi3_boot::entry!(kernel_entry);
fn kernel_entry() -> ! { fn kernel_entry() -> ! {
let gpio = gpio::GPIO::new();
let mut mbox = mbox::Mbox::new(); let mut mbox = mbox::Mbox::new();
let uart = uart::Uart::new(); let uart = uart::Uart::new();
// set up serial console // set up serial console
if uart.init(&mut mbox).is_err() { match uart.init(&mut mbox, &gpio) {
loop { Ok(_) => uart.puts("\n[0] UART is live!\n"),
Err(_) => loop {
cortex_a::asm::wfe() // If UART fails, abort early cortex_a::asm::wfe() // If UART fails, abort early
} },
} }
uart.getc(); // Press a key first before being greeted uart.puts("[1] Press a key to continue booting... ");
uart.puts("Hello Rustacean!\n\n"); uart.getc();
uart.puts("Greetings fellow Rustacean!\n");
uart.puts("Executing in EL: "); uart.puts("[i] Executing in EL: ");
uart.hex(CurrentEL.read(CurrentEL::EL)); uart.dec(CurrentEL.read(CurrentEL::EL));
uart.puts("\n\n"); uart.puts("\n\n");
check_timer(&uart); check_timer(&uart);
@ -97,3 +98,5 @@ fn kernel_entry() -> ! {
uart.send(uart.getc()); uart.send(uart.getc());
} }
} }
raspi3_boot::entry!(kernel_entry);

@ -23,6 +23,7 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use crate::delays;
use crate::gpio; use crate::gpio;
use crate::mbox; use crate::mbox;
use core::{ use core::{
@ -162,7 +163,7 @@ impl Uart {
} }
///Set baud rate and characteristics (115200 8N1) and map to GPIO ///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> { pub fn init(&self, mbox: &mut mbox::Mbox, gpio: &gpio::GPIO) -> Result<()> {
// turn off UART0 // turn off UART0
self.CR.set(0); self.CR.set(0);
@ -187,23 +188,18 @@ impl Uart {
}; };
// map UART0 to GPIO pins // map UART0 to GPIO pins
unsafe { gpio.GPFSEL1
(*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0); .modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
(*gpio::GPPUD).set(0); // enable pins 14 and 15 gpio.GPPUD.set(0); // enable pins 14 and 15
for _ in 0..150 { delays::wait_cycles(150);
asm::nop();
}
(*gpio::GPPUDCLK0).modify( gpio.GPPUDCLK0.modify(
gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock, gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
); );
for _ in 0..150 { delays::wait_cycles(150);
asm::nop();
}
(*gpio::GPPUDCLK0).set(0); gpio.GPPUDCLK0.set(0);
}
self.ICR.write(ICR::ALL::CLEAR); self.ICR.write(ICR::ALL::CLEAR);
self.IBRD.write(IBRD::IBRD.val(2)); // Results in 115200 baud self.IBRD.write(IBRD::IBRD.val(2)); // Results in 115200 baud
@ -264,23 +260,23 @@ impl Uart {
} }
} }
/// Display a binary value in hexadecimal /// Display a binary value in decimal
pub fn hex(&self, d: u32) { pub fn dec(&self, d: u32) {
let mut n; let mut digits: [char; 10] = ['\0'; 10];
let mut d = d;
for i in 0..8 { for i in digits.iter_mut() {
// get highest tetrad *i = ((d % 10) + 0x30) as u8 as char;
n = d.wrapping_shr(28 - i * 4) & 0xF;
// 0-9 => '0'-'9', 10-15 => 'A'-'F' d /= 10;
// Add proper offset for ASCII table
if n > 9 { if d == 0 {
n += 0x37; break;
} else {
n += 0x30;
} }
}
self.send(n as u8 as char); for c in digits.iter().rev() {
self.send(*c);
} }
} }
} }

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy -- OBJCOPY = cargo objcopy --
OBJCOPY_PARAMS = --strip-all -O binary OBJCOPY_PARAMS = --strip-all -O binary
@ -33,23 +35,15 @@ DOCKER_TTY = --privileged -v /dev:/dev
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img
all: clean kernel8.img .PHONY: all qemu raspboot clippy clean objdump nm
target/$(TARGET)/debug/kernel8: src/main.rs all: clean kernel8.img
cargo xbuild --target=$(TARGET)
cp $@ .
target/$(TARGET)/release/kernel8: src/main.rs target/$(TARGET)/release/kernel8: $(SOURCES)
cargo xbuild --target=$(TARGET) --release cargo xbuild --target=$(TARGET) --release
cp $@ .
ifeq ($(DEBUG),1)
kernel8: target/$(TARGET)/debug/kernel8
else
kernel8: target/$(TARGET)/release/kernel8
endif
kernel8.img: kernel8 kernel8.img: target/$(TARGET)/release/kernel8
cp $< .
$(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img
qemu: all qemu: all

Binary file not shown.

Binary file not shown.

@ -69,6 +69,8 @@ unsafe fn reset() -> ! {
fn setup_and_enter_el1_from_el2() -> ! { fn setup_and_enter_el1_from_el2() -> ! {
use cortex_a::{asm, regs::*}; use cortex_a::{asm, regs::*};
const STACK_START: u64 = 0x80_000;
// Enable timer counter registers for EL1 // Enable timer counter registers for EL1
CNTHCTL_EL2.write(CNTHCTL_EL2::EL1PCEN::SET + CNTHCTL_EL2::EL1PCTEN::SET); CNTHCTL_EL2.write(CNTHCTL_EL2::EL1PCEN::SET + CNTHCTL_EL2::EL1PCTEN::SET);
@ -96,7 +98,7 @@ fn setup_and_enter_el1_from_el2() -> ! {
// Set up SP_EL1 (stack pointer), which will be used by EL1 once // Set up SP_EL1 (stack pointer), which will be used by EL1 once
// we "return" to it. // we "return" to it.
SP_EL1.set(0x80_000); SP_EL1.set(STACK_START);
// Use `eret` to "return" to EL1. This will result in execution of // Use `eret` to "return" to EL1. This will result in execution of
// `reset()` in EL1. // `reset()` in EL1.
@ -116,10 +118,8 @@ pub unsafe extern "C" fn _boot_cores() -> ! {
const CORE_MASK: u64 = 0x3; const CORE_MASK: u64 = 0x3;
const EL2: u32 = CurrentEL::EL::EL2.value; const EL2: u32 = CurrentEL::EL::EL2.value;
if let CORE_0 = MPIDR_EL1.get() & CORE_MASK { if (CORE_0 == MPIDR_EL1.get() & CORE_MASK) && (EL2 == CurrentEL.get()) {
if let EL2 = CurrentEL.get() { setup_and_enter_el1_from_el2()
setup_and_enter_el1_from_el2()
}
} }
// if not core0 or EL != 2, infinitely wait for events // if not core0 or EL != 2, infinitely wait for events

@ -0,0 +1,37 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* 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.
*/
use cortex_a::asm;
/*
*
* Using the CPU's cycles
*
*/
/// Wait N CPU cycles (ARM CPU only)
pub fn wait_cycles(cyc: u32) {
for _ in 0..cyc {
asm::nop();
}
}

@ -23,6 +23,7 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops;
use register::{mmio::ReadWrite, register_bitfields}; use register::{mmio::ReadWrite, register_bitfields};
// Descriptions taken from // Descriptions taken from
@ -66,10 +67,55 @@ register_bitfields! {
] ]
} }
pub const GPFSEL1: *const ReadWrite<u32, GPFSEL1::Register> = const GPIO_BASE: u32 = MMIO_BASE + 0x200_000;
(MMIO_BASE + 0x0020_0004) as *const ReadWrite<u32, GPFSEL1::Register>;
pub const GPPUD: *const ReadWrite<u32> = (MMIO_BASE + 0x0020_0094) as *const ReadWrite<u32>; #[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
pub GPFSEL0: ReadWrite<u32>, // 0x00
pub GPFSEL1: ReadWrite<u32, GPFSEL1::Register>, // 0x04
pub GPFSEL2: ReadWrite<u32>, // 0x08
pub GPFSEL3: ReadWrite<u32>, // 0x0C
pub GPFSEL4: ReadWrite<u32>, // 0x10
pub GPFSEL5: ReadWrite<u32>, // 0x14
__reserved_0: u32, // 0x18
GPSET0: ReadWrite<u32>, // 0x1C
GPSET1: ReadWrite<u32>, // 0x20
__reserved_1: u32, //
GPCLR0: ReadWrite<u32>, // 0x28
__reserved_2: [u32; 2], //
GPLEV0: ReadWrite<u32>, // 0x34
GPLEV1: ReadWrite<u32>, // 0x38
__reserved_3: u32, //
GPEDS0: ReadWrite<u32>, // 0x40
GPEDS1: ReadWrite<u32>, // 0x44
__reserved_4: [u32; 7], //
GPHEN0: ReadWrite<u32>, // 0x64
GPHEN1: ReadWrite<u32>, // 0x68
__reserved_5: [u32; 10], //
pub GPPUD: ReadWrite<u32>, // 0x94
pub GPPUDCLK0: ReadWrite<u32, GPPUDCLK0::Register>, // 0x98
pub GPPUDCLK1: ReadWrite<u32>, // 0x9C
}
/// Public interface to the GPIO MMIO area
pub struct GPIO;
pub const GPPUDCLK0: *const ReadWrite<u32, GPPUDCLK0::Register> = impl ops::Deref for GPIO {
(MMIO_BASE + 0x0020_0098) as *const ReadWrite<u32, GPPUDCLK0::Register>; type Target = RegisterBlock;
fn deref(&self) -> &Self::Target {
unsafe { &*Self::ptr() }
}
}
impl GPIO {
pub fn new() -> GPIO {
GPIO
}
/// Returns a pointer to the register block
fn ptr() -> *const RegisterBlock {
GPIO_BASE as *const _
}
}

@ -29,14 +29,14 @@
const MMIO_BASE: u32 = 0x3F00_0000; const MMIO_BASE: u32 = 0x3F00_0000;
mod delays;
mod gpio; mod gpio;
mod mbox; mod mbox;
mod mmu; mod mmu;
mod uart; mod uart;
raspi3_boot::entry!(kernel_entry);
fn kernel_entry() -> ! { fn kernel_entry() -> ! {
let gpio = gpio::GPIO::new();
let mut mbox = mbox::Mbox::new(); let mut mbox = mbox::Mbox::new();
{ {
@ -44,18 +44,20 @@ fn kernel_entry() -> ! {
let uart = uart::Uart::new(uart::UART_PHYS_BASE); let uart = uart::Uart::new(uart::UART_PHYS_BASE);
// set up serial console // set up serial console
if uart.init(&mut mbox).is_err() { match uart.init(&mut mbox, &gpio) {
loop { Ok(_) => uart.puts("\n[0] UART is live!\n"),
Err(_) => loop {
cortex_a::asm::wfe() // If UART fails, abort early cortex_a::asm::wfe() // If UART fails, abort early
} },
} }
uart.getc(); // Press a key first before being greeted uart.puts("[1] Press a key to continue booting... ");
uart.puts("Hello Rustacean!\n\n"); uart.getc();
uart.puts("Greetings fellow Rustacean!\n");
mmu::print_features(&uart); mmu::print_features(&uart);
uart.puts("\nSwitching MMU on now..."); uart.puts("[2] Switching MMU on now... ");
} // After this closure, the UART instance is not valid anymore. } // After this closure, the UART instance is not valid anymore.
unsafe { mmu::init() }; unsafe { mmu::init() };
@ -66,7 +68,7 @@ fn kernel_entry() -> ! {
let uart = uart::Uart::new(UART_VIRT_BASE); let uart = uart::Uart::new(UART_VIRT_BASE);
uart.puts("MMU is live \\o/\n\nWriting through the virtual mapping at 0x"); uart.puts("MMU is live \\o/\n\nWriting through the virtual mapping at 0x");
uart.hex(UART_VIRT_BASE); uart.hex(UART_VIRT_BASE as u64);
uart.puts(".\n"); uart.puts(".\n");
// echo everything back // echo everything back
@ -74,3 +76,5 @@ fn kernel_entry() -> ! {
uart.send(uart.getc()); uart.send(uart.getc());
} }
} }
raspi3_boot::entry!(kernel_entry);

@ -34,13 +34,13 @@ pub fn print_features(uart: &uart::Uart) {
if let Some(ID_AA64MMFR0_EL1::TGran4::Value::Supported) = if let Some(ID_AA64MMFR0_EL1::TGran4::Value::Supported) =
mmfr.read_as_enum(ID_AA64MMFR0_EL1::TGran4) mmfr.read_as_enum(ID_AA64MMFR0_EL1::TGran4)
{ {
uart.puts("MMU: 4 KiB granule supported!\n"); uart.puts("[i] MMU: 4 KiB granule supported!\n");
} }
if let Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_40) = if let Some(ID_AA64MMFR0_EL1::PARange::Value::Bits_40) =
mmfr.read_as_enum(ID_AA64MMFR0_EL1::PARange) mmfr.read_as_enum(ID_AA64MMFR0_EL1::PARange)
{ {
uart.puts("MMU: Up to 40 Bit physical address range supported!\n"); uart.puts("[i] MMU: Up to 40 Bit physical address range supported!\n");
} }
} }

@ -23,6 +23,7 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use crate::delays;
use crate::gpio; use crate::gpio;
use crate::mbox; use crate::mbox;
use core::{ use core::{
@ -164,7 +165,7 @@ impl Uart {
} }
///Set baud rate and characteristics (115200 8N1) and map to GPIO ///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> { pub fn init(&self, mbox: &mut mbox::Mbox, gpio: &gpio::GPIO) -> Result<()> {
// turn off UART0 // turn off UART0
self.CR.set(0); self.CR.set(0);
@ -189,23 +190,18 @@ impl Uart {
}; };
// map UART0 to GPIO pins // map UART0 to GPIO pins
unsafe { gpio.GPFSEL1
(*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0); .modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
(*gpio::GPPUD).set(0); // enable pins 14 and 15 gpio.GPPUD.set(0); // enable pins 14 and 15
for _ in 0..150 { delays::wait_cycles(150);
asm::nop();
}
(*gpio::GPPUDCLK0).modify( gpio.GPPUDCLK0.modify(
gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock, gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
); );
for _ in 0..150 { delays::wait_cycles(150);
asm::nop();
}
(*gpio::GPPUDCLK0).set(0); gpio.GPPUDCLK0.set(0);
}
self.ICR.write(ICR::ALL::CLEAR); self.ICR.write(ICR::ALL::CLEAR);
self.IBRD.write(IBRD::IBRD.val(2)); // Results in 115200 baud self.IBRD.write(IBRD::IBRD.val(2)); // Results in 115200 baud
@ -267,12 +263,12 @@ impl Uart {
} }
/// Display a binary value in hexadecimal /// Display a binary value in hexadecimal
pub fn hex(&self, d: u32) { pub fn hex(&self, d: u64) {
let mut n; let mut n;
for i in 0..8 { for i in 0..16 {
// get highest tetrad // get highest tetrad
n = d.wrapping_shr(28 - i * 4) & 0xF; n = d.wrapping_shr(60 - i * 4) & 0xF;
// 0-9 => '0'-'9', 10-15 => 'A'-'F' // 0-9 => '0'-'9', 10-15 => 'A'-'F'
// Add proper offset for ASCII table // Add proper offset for ASCII table

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy -- OBJCOPY = cargo objcopy --
OBJCOPY_PARAMS = --strip-all -O binary OBJCOPY_PARAMS = --strip-all -O binary
@ -33,23 +35,15 @@ DOCKER_TTY = --privileged -v /dev:/dev
QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img QEMU_CMD = qemu-system-aarch64 -M raspi3 -kernel kernel8.img
RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img RASPBOOT_CMD = raspbootcom /dev/ttyUSB0 kernel8.img
all: clean kernel8.img .PHONY: all qemu raspboot clippy clean objdump nm
target/$(TARGET)/debug/kernel8: src/main.rs all: clean kernel8.img
cargo xbuild --target=$(TARGET)
cp $@ .
target/$(TARGET)/release/kernel8: src/main.rs target/$(TARGET)/release/kernel8: $(SOURCES)
cargo xbuild --target=$(TARGET) --release cargo xbuild --target=$(TARGET) --release
cp $@ .
ifeq ($(DEBUG),1)
kernel8: target/$(TARGET)/debug/kernel8
else
kernel8: target/$(TARGET)/release/kernel8
endif
kernel8.img: kernel8 kernel8.img: target/$(TARGET)/release/kernel8
cp $< .
$(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img $(OBJCOPY) $(OBJCOPY_PARAMS) $< kernel8.img
qemu: all qemu: all

@ -43,13 +43,13 @@ showcase how much faster it is to operate on DRAM with caching enabled.
On my Raspberry, I get the following results: On my Raspberry, I get the following results:
```text ```text
Benchmarking non-cacheable DRAM modifications at virtual 0x00200000, physical 0x00400000: Benchmarking non-cacheable DRAM modifications at virtual 0x0000000000200000, physical 0x0000000000400000:
1040 miliseconds. 1040 miliseconds.
Benchmarking cacheable DRAM modifications at virtual 0x00400000, physical 0x00400000: Benchmarking cacheable DRAM modifications at virtual 0x0000000000400000, physical 0x0000000000400000:
53 miliseconds. 53 miliseconds.
With caching, the function is 1862% faster! With caching, the function is 1800% faster!
``` ```
Impressive, isn't it? Impressive, isn't it?

Binary file not shown.

Binary file not shown.

@ -69,6 +69,8 @@ unsafe fn reset() -> ! {
fn setup_and_enter_el1_from_el2() -> ! { fn setup_and_enter_el1_from_el2() -> ! {
use cortex_a::{asm, regs::*}; use cortex_a::{asm, regs::*};
const STACK_START: u64 = 0x80_000;
// Enable timer counter registers for EL1 // Enable timer counter registers for EL1
CNTHCTL_EL2.write(CNTHCTL_EL2::EL1PCEN::SET + CNTHCTL_EL2::EL1PCTEN::SET); CNTHCTL_EL2.write(CNTHCTL_EL2::EL1PCEN::SET + CNTHCTL_EL2::EL1PCTEN::SET);
@ -96,7 +98,7 @@ fn setup_and_enter_el1_from_el2() -> ! {
// Set up SP_EL1 (stack pointer), which will be used by EL1 once // Set up SP_EL1 (stack pointer), which will be used by EL1 once
// we "return" to it. // we "return" to it.
SP_EL1.set(0x80_000); SP_EL1.set(STACK_START);
// Use `eret` to "return" to EL1. This will result in execution of // Use `eret` to "return" to EL1. This will result in execution of
// `reset()` in EL1. // `reset()` in EL1.
@ -116,10 +118,8 @@ pub unsafe extern "C" fn _boot_cores() -> ! {
const CORE_MASK: u64 = 0x3; const CORE_MASK: u64 = 0x3;
const EL2: u32 = CurrentEL::EL::EL2.value; const EL2: u32 = CurrentEL::EL::EL2.value;
if let CORE_0 = MPIDR_EL1.get() & CORE_MASK { if (CORE_0 == MPIDR_EL1.get() & CORE_MASK) && (EL2 == CurrentEL.get()) {
if let EL2 = CurrentEL.get() { setup_and_enter_el1_from_el2()
setup_and_enter_el1_from_el2()
}
} }
// if not core0 or EL != 2, infinitely wait for events // if not core0 or EL != 2, infinitely wait for events

@ -53,9 +53,9 @@ pub fn run(uart: &uart::Uart) {
let cacheable_addr: u64 = 2 * SIZE_2MIB; let cacheable_addr: u64 = 2 * SIZE_2MIB;
uart.puts("Benchmarking non-cacheable DRAM modifications at virtual 0x"); uart.puts("Benchmarking non-cacheable DRAM modifications at virtual 0x");
uart.hex(non_cacheable_addr as u32); uart.hex(non_cacheable_addr);
uart.puts(", physical 0x"); uart.puts(", physical 0x");
uart.hex(2 * SIZE_2MIB as u32); uart.hex(2 * SIZE_2MIB);
uart.puts(":\n"); uart.puts(":\n");
let result_nc = match batch_modify_time(non_cacheable_addr) { let result_nc = match batch_modify_time(non_cacheable_addr) {
@ -71,9 +71,9 @@ pub fn run(uart: &uart::Uart) {
}; };
uart.puts("Benchmarking cacheable DRAM modifications at virtual 0x"); uart.puts("Benchmarking cacheable DRAM modifications at virtual 0x");
uart.hex(cacheable_addr as u32); uart.hex(cacheable_addr);
uart.puts(", physical 0x"); uart.puts(", physical 0x");
uart.hex(2 * SIZE_2MIB as u32); uart.hex(2 * SIZE_2MIB);
uart.puts(":\n"); uart.puts(":\n");
let result_c = match batch_modify_time(cacheable_addr) { let result_c = match batch_modify_time(cacheable_addr) {

@ -0,0 +1,37 @@
/*
* MIT License
*
* Copyright (c) 2018 Andre Richter <andre.o.richter@gmail.com>
*
* 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.
*/
use cortex_a::asm;
/*
*
* Using the CPU's cycles
*
*/
/// Wait N CPU cycles (ARM CPU only)
pub fn wait_cycles(cyc: u32) {
for _ in 0..cyc {
asm::nop();
}
}

@ -23,6 +23,7 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use core::ops;
use register::{mmio::ReadWrite, register_bitfields}; use register::{mmio::ReadWrite, register_bitfields};
// Descriptions taken from // Descriptions taken from
@ -66,10 +67,55 @@ register_bitfields! {
] ]
} }
pub const GPFSEL1: *const ReadWrite<u32, GPFSEL1::Register> = const GPIO_BASE: u32 = MMIO_BASE + 0x200_000;
(MMIO_BASE + 0x0020_0004) as *const ReadWrite<u32, GPFSEL1::Register>;
pub const GPPUD: *const ReadWrite<u32> = (MMIO_BASE + 0x0020_0094) as *const ReadWrite<u32>; #[allow(non_snake_case)]
#[repr(C)]
pub struct RegisterBlock {
pub GPFSEL0: ReadWrite<u32>, // 0x00
pub GPFSEL1: ReadWrite<u32, GPFSEL1::Register>, // 0x04
pub GPFSEL2: ReadWrite<u32>, // 0x08
pub GPFSEL3: ReadWrite<u32>, // 0x0C
pub GPFSEL4: ReadWrite<u32>, // 0x10
pub GPFSEL5: ReadWrite<u32>, // 0x14
__reserved_0: u32, // 0x18
GPSET0: ReadWrite<u32>, // 0x1C
GPSET1: ReadWrite<u32>, // 0x20
__reserved_1: u32, //
GPCLR0: ReadWrite<u32>, // 0x28
__reserved_2: [u32; 2], //
GPLEV0: ReadWrite<u32>, // 0x34
GPLEV1: ReadWrite<u32>, // 0x38
__reserved_3: u32, //
GPEDS0: ReadWrite<u32>, // 0x40
GPEDS1: ReadWrite<u32>, // 0x44
__reserved_4: [u32; 7], //
GPHEN0: ReadWrite<u32>, // 0x64
GPHEN1: ReadWrite<u32>, // 0x68
__reserved_5: [u32; 10], //
pub GPPUD: ReadWrite<u32>, // 0x94
pub GPPUDCLK0: ReadWrite<u32, GPPUDCLK0::Register>, // 0x98
pub GPPUDCLK1: ReadWrite<u32>, // 0x9C
}
/// Public interface to the GPIO MMIO area
pub struct GPIO;
pub const GPPUDCLK0: *const ReadWrite<u32, GPPUDCLK0::Register> = impl ops::Deref for GPIO {
(MMIO_BASE + 0x0020_0098) as *const ReadWrite<u32, GPPUDCLK0::Register>; type Target = RegisterBlock;
fn deref(&self) -> &Self::Target {
unsafe { &*Self::ptr() }
}
}
impl GPIO {
pub fn new() -> GPIO {
GPIO
}
/// Returns a pointer to the register block
fn ptr() -> *const RegisterBlock {
GPIO_BASE as *const _
}
}

@ -30,28 +30,30 @@
const MMIO_BASE: u32 = 0x3F00_0000; const MMIO_BASE: u32 = 0x3F00_0000;
mod benchmark; mod benchmark;
mod delays;
mod gpio; mod gpio;
mod mbox; mod mbox;
mod mmu; mod mmu;
mod uart; mod uart;
raspi3_boot::entry!(kernel_entry);
fn kernel_entry() -> ! { fn kernel_entry() -> ! {
let gpio = gpio::GPIO::new();
let mut mbox = mbox::Mbox::new(); let mut mbox = mbox::Mbox::new();
let uart = uart::Uart::new(uart::UART_PHYS_BASE); let uart = uart::Uart::new(uart::UART_PHYS_BASE);
// set up serial console // set up serial console
if uart.init(&mut mbox).is_err() { match uart.init(&mut mbox, &gpio) {
loop { Ok(_) => uart.puts("\n[0] UART is live!\n"),
Err(_) => loop {
cortex_a::asm::wfe() // If UART fails, abort early cortex_a::asm::wfe() // If UART fails, abort early
} },
} }
uart.getc(); // Press a key first before being greeted uart.puts("[1] Press a key to continue booting... ");
uart.puts("Hello Rustacean!\n\n"); uart.getc();
uart.puts("Greetings fellow Rustacean!\n");
uart.puts("\nSwitching MMU on now..."); uart.puts("[2] Switching MMU on now... ");
unsafe { mmu::init() }; unsafe { mmu::init() };
@ -64,3 +66,5 @@ fn kernel_entry() -> ! {
uart.send(uart.getc()); uart.send(uart.getc());
} }
} }
raspi3_boot::entry!(kernel_entry);

@ -23,6 +23,7 @@
*/ */
use super::MMIO_BASE; use super::MMIO_BASE;
use crate::delays;
use crate::gpio; use crate::gpio;
use crate::mbox; use crate::mbox;
use core::{ use core::{
@ -164,7 +165,7 @@ impl Uart {
} }
///Set baud rate and characteristics (115200 8N1) and map to GPIO ///Set baud rate and characteristics (115200 8N1) and map to GPIO
pub fn init(&self, mbox: &mut mbox::Mbox) -> Result<()> { pub fn init(&self, mbox: &mut mbox::Mbox, gpio: &gpio::GPIO) -> Result<()> {
// turn off UART0 // turn off UART0
self.CR.set(0); self.CR.set(0);
@ -189,23 +190,18 @@ impl Uart {
}; };
// map UART0 to GPIO pins // map UART0 to GPIO pins
unsafe { gpio.GPFSEL1
(*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0); .modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
(*gpio::GPPUD).set(0); // enable pins 14 and 15 gpio.GPPUD.set(0); // enable pins 14 and 15
for _ in 0..150 { delays::wait_cycles(150);
asm::nop();
}
(*gpio::GPPUDCLK0).modify( gpio.GPPUDCLK0.modify(
gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock, gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
); );
for _ in 0..150 { delays::wait_cycles(150);
asm::nop();
}
(*gpio::GPPUDCLK0).set(0); gpio.GPPUDCLK0.set(0);
}
self.ICR.write(ICR::ALL::CLEAR); self.ICR.write(ICR::ALL::CLEAR);
self.IBRD.write(IBRD::IBRD.val(2)); // Results in 115200 baud self.IBRD.write(IBRD::IBRD.val(2)); // Results in 115200 baud
@ -267,12 +263,12 @@ impl Uart {
} }
/// Display a binary value in hexadecimal /// Display a binary value in hexadecimal
pub fn hex(&self, d: u32) { pub fn hex(&self, d: u64) {
let mut n; let mut n;
for i in 0..8 { for i in 0..16 {
// get highest tetrad // get highest tetrad
n = d.wrapping_shr(28 - i * 4) & 0xF; n = d.wrapping_shr(60 - i * 4) & 0xF;
// 0-9 => '0'-'9', 10-15 => 'A'-'F' // 0-9 => '0'-'9', 10-15 => 'A'-'F'
// Add proper offset for ASCII table // Add proper offset for ASCII table

Loading…
Cancel
Save