Streamlining, cleanup, and minor fixes.

pull/15/head
Andre Richter 5 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
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy --
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
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
cargo xbuild --target=$(TARGET)
cp $@ .
all: clean kernel8.img
target/$(TARGET)/release/kernel8: src/main.rs
target/$(TARGET)/release/kernel8: $(SOURCES)
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
qemu:
qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -d in_asm
clean:

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy --
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
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
cargo xbuild --target=$(TARGET)
cp $@ .
all: clean kernel8.img
target/$(TARGET)/release/kernel8: src/main.rs
target/$(TARGET)/release/kernel8: $(SOURCES)
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
qemu:
qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -d in_asm
clippy:

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

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy --
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
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
cargo xbuild --target=$(TARGET)
cp $@ .
all: clean kernel8.img
target/$(TARGET)/release/kernel8: src/main.rs
target/$(TARGET)/release/kernel8: $(SOURCES)
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
qemu:
qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial null -serial stdio
clippy:

@ -43,8 +43,8 @@ the host PC.
```console
ferris@box:~$ make qemu
<Press any key>
Hello Rustacean!
[0] UART is live!
[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

Binary file not shown.

Binary file not shown.

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

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy --
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
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
cargo xbuild --target=$(TARGET)
cp $@ .
all: clean kernel8.img
target/$(TARGET)/release/kernel8: src/main.rs
target/$(TARGET)/release/kernel8: $(SOURCES)
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
qemu:
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial null -serial stdio
qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
clippy:
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};
raspi3_boot::entry!(kernel_entry);
fn kernel_entry() -> ! {
let mut mbox = mbox::Mbox::new();
let uart = uart::MiniUart::new();
// set up serial console
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
mbox.buffer[0] = 8 * 4; // length of the message
@ -64,16 +67,13 @@ fn kernel_entry() -> ! {
Ok(()) => true,
};
uart.getc(); // Press a key first before being greeted
uart.puts("Hello Rustacean!\n");
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[5]);
uart.puts("\n");
} else {
uart.puts("Unable to query serial!\n");
uart.puts("[i] Unable to query serial!\n");
}
// echo everything back
@ -81,3 +81,5 @@ fn kernel_entry() -> ! {
uart.send(uart.getc());
}
}
raspi3_boot::entry!(kernel_entry);

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy --
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
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
cargo xbuild --target=$(TARGET)
cp $@ .
all: clean kernel8.img
target/$(TARGET)/release/kernel8: src/main.rs
target/$(TARGET)/release/kernel8: $(SOURCES)
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
qemu:
qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
clippy:

Binary file not shown.

Binary file not shown.

@ -34,17 +34,22 @@ mod uart;
use core::sync::atomic::{compiler_fence, Ordering};
raspi3_boot::entry!(kernel_entry);
fn kernel_entry() -> ! {
let mut mbox = mbox::Mbox::new();
let uart = uart::Uart::new();
// set up serial console
if uart.init(&mut mbox).is_err() {
unsafe { asm!("wfe" :::: "volatile") }; // If UART fails, abort early
match uart.init(&mut mbox) {
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
mbox.buffer[0] = 8 * 4; // length of the message
mbox.buffer[1] = mbox::REQUEST; // this is a request message
@ -66,16 +71,13 @@ fn kernel_entry() -> ! {
Ok(()) => true,
};
uart.getc(); // Press a key first before being greeted
uart.puts("Hello Rustacean!\n");
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[5]);
uart.puts("\n");
} else {
uart.puts("Unable to query serial!\n");
uart.puts("[i] Unable to query serial!\n");
}
// echo everything back
@ -83,3 +85,5 @@ fn kernel_entry() -> ! {
uart.send(uart.getc());
}
}
raspi3_boot::entry!(kernel_entry);

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy --
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
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
cargo xbuild --target=$(TARGET)
cp $@ .
all: clean kernel8.img
target/$(TARGET)/release/kernel8: src/main.rs
target/$(TARGET)/release/kernel8: $(SOURCES)
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
qemu:
qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
clippy:

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

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy --
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
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
cargo xbuild --target=$(TARGET)
cp $@ .
all: clean kernel8.img
target/$(TARGET)/release/kernel8: src/main.rs
target/$(TARGET)/release/kernel8: $(SOURCES)
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
qemu:
qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
raspboot:
raspboot: all
$(DOCKER_CMD) $(DOCKER_TTY) $(UTILS_CONTAINER) $(RASPBOOT_CMD)
clippy:

@ -64,20 +64,20 @@ replaced it with a Rust function. Why? Because we can, for the fun of it.
#[link_section = ".text.boot"]
#[no_mangle]
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 STACK_START: u64 = 0x80_000;
match MPIDR_EL1.get() & CORE_MASK {
0 => {
SP.set(STACK_START);
reset()
}
_ => loop {
// if not core0, infinitely wait for events
if CORE_0 == MPIDR_EL1.get() & CORE_MASK {
SP.set(STACK_START);
reset()
} else {
// if not core0, infinitely wait for events
loop {
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.
```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)
@ -103,9 +103,9 @@ _boot_cores:
80008: 60 00 00 54 b.eq #0xc <_boot_cores+0x14>
8000c: 5f 20 03 d5 wfe
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
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

Binary file not shown.

Binary file not shown.

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

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

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy --
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
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
cargo xbuild --target=$(TARGET)
cp $@ .
all: clean kernel8.img
target/$(TARGET)/release/kernel8: src/main.rs
target/$(TARGET)/release/kernel8: $(SOURCES)
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
qemu:
qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
raspboot:
raspboot: all
$(DOCKER_CMD) $(DOCKER_TTY) $(UTILS_CONTAINER) $(RASPBOOT_CMD)
clippy:

Binary file not shown.

Binary file not shown.

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

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

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy --
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
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
cargo xbuild --target=$(TARGET)
cp $@ .
all: clean kernel8.img
target/$(TARGET)/release/kernel8: src/main.rs
target/$(TARGET)/release/kernel8: $(SOURCES)
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
qemu:
qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
raspboot:
raspboot: all
$(DOCKER_CMD) $(DOCKER_TTY) $(UTILS_CONTAINER) $(RASPBOOT_CMD)
clippy:

Binary file not shown.

Binary file not shown.

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

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

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy --
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
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
cargo xbuild --target=$(TARGET)
cp $@ .
all: clean kernel8.img
target/$(TARGET)/release/kernel8: src/main.rs
target/$(TARGET)/release/kernel8: $(SOURCES)
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
qemu:
qemu: all
$(DOCKER_CMD) $(UTILS_CONTAINER) $(QEMU_CMD) -serial stdio
raspboot:
raspboot: all
$(DOCKER_CMD) $(DOCKER_TTY) $(UTILS_CONTAINER) $(RASPBOOT_CMD)
clippy:

Binary file not shown.

Binary file not shown.

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

@ -33,8 +33,6 @@ mod mbox;
mod power;
mod uart;
raspi3_boot::entry!(kernel_entry);
fn kernel_entry() -> ! {
let gpio = gpio::GPIO::new();
let mut mbox = mbox::Mbox::new();
@ -42,14 +40,16 @@ fn kernel_entry() -> ! {
let power = power::Power::new();
// set up serial console
if uart.init(&mut mbox, &gpio).is_err() {
loop {
cortex_a::asm::wfe()
} // If UART fails, abort early
match uart.init(&mut mbox, &gpio) {
Ok(_) => uart.puts("\n[0] UART is live!\n"),
Err(_) => loop {
cortex_a::asm::wfe() // If UART fails, abort early
},
}
uart.getc(); // Press a key first before being greeted
uart.puts("Hello Rustacean!\n\n");
uart.puts("[1] Press a key to continue booting... ");
uart.getc();
uart.puts("Greetings fellow Rustacean!\n");
loop {
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
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy --
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
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
cargo xbuild --target=$(TARGET)
cp $@ .
all: clean kernel8.img
target/$(TARGET)/release/kernel8: src/main.rs
target/$(TARGET)/release/kernel8: $(SOURCES)
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
qemu: all

@ -8,20 +8,20 @@ Cortex-A53 processor: `Exception levels`.
TODO: Write rest of tutorial.
```text
raspi3_boot::setup_and_enter_el1_from_el2::hb2c2ac4f6a7ddb7e:
80954: e8 03 1f aa mov x8, xzr
80958: e9 07 00 32 orr w9, wzr, #0x3
8095c: 4a 00 80 52 mov w10, #0x2
80960: 0a 00 b0 72 movk w10, #0x8000, lsl #16
80964: 09 e1 1c d5 msr CNTHCTL_EL2, x9
80968: 68 e0 1c d5 msr CNTVOFF_EL2, x8
8096c: 08 00 00 90 adrp x8, #0x0
80970: ab 78 80 52 mov w11, #0x3c5
80974: 0a 11 1c d5 msr HCR_EL2, x10
80978: ec 03 0d 32 orr w12, wzr, #0x80000
8097c: 08 81 24 91 add x8, x8, #0x920
80980: 0b 40 1c d5 msr SPSR_EL2, x11
80984: 28 40 1c d5 msr ELR_EL2, x8
80988: 0c 41 1c d5 msr SP_EL1, x12
8098c: e0 03 9f d6 eret
raspi3_boot::setup_and_enter_el1_from_el2::h568f1410ae7cc9b8:
808c0: e8 03 1f aa mov x8, xzr
808c4: e9 07 00 32 orr w9, wzr, #0x3
808c8: 09 e1 1c d5 msr CNTHCTL_EL2, x9
808cc: 4a 00 80 52 mov w10, #0x2
808d0: 68 e0 1c d5 msr CNTVOFF_EL2, x8
808d4: 08 00 00 90 adrp x8, #0x0
808d8: 0a 00 b0 72 movk w10, #0x8000, lsl #16
808dc: 0a 11 1c d5 msr HCR_EL2, x10
808e0: ab 78 80 52 mov w11, #0x3c5
808e4: 0b 40 1c d5 msr SPSR_EL2, x11
808e8: ec 03 0d 32 orr w12, wzr, #0x80000
808ec: 08 31 22 91 add x8, x8, #0x88c
808f0: 28 40 1c d5 msr ELR_EL2, x8
808f4: 0c 41 1c d5 msr SP_EL1, x12
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() -> ! {
use cortex_a::{asm, regs::*};
const STACK_START: u64 = 0x80_000;
// Enable timer counter registers for EL1
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
// 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
// `reset()` in EL1.
@ -116,10 +118,8 @@ pub unsafe extern "C" fn _boot_cores() -> ! {
const CORE_MASK: u64 = 0x3;
const EL2: u32 = CurrentEL::EL::EL2.value;
if let CORE_0 = MPIDR_EL1.get() & CORE_MASK {
if let EL2 = CurrentEL.get() {
setup_and_enter_el1_from_el2()
}
if (CORE_0 == MPIDR_EL1.get() & CORE_MASK) && (EL2 == CurrentEL.get()) {
setup_and_enter_el1_from_el2()
}
// if not core0 or EL != 2, infinitely wait for events

@ -22,6 +22,7 @@
* SOFTWARE.
*/
use cortex_a::asm;
use cortex_a::regs::*;
/*
@ -53,3 +54,15 @@ pub fn wait_msec(n: u32) {
// Disable counting again
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 core::ops;
use register::{mmio::ReadWrite, register_bitfields};
// Descriptions taken from
@ -66,10 +67,55 @@ register_bitfields! {
]
}
pub const GPFSEL1: *const ReadWrite<u32, GPFSEL1::Register> =
(MMIO_BASE + 0x0020_0004) as *const ReadWrite<u32, GPFSEL1::Register>;
const GPIO_BASE: u32 = MMIO_BASE + 0x200_000;
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> =
(MMIO_BASE + 0x0020_0098) as *const ReadWrite<u32, GPPUDCLK0::Register>;
impl ops::Deref for GPIO {
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) {
uart.puts(
"Testing EL1 access to timer registers.\n\
Delaying for 3 seconds now.\n",
"Testing EL1 access to timer registers:\
\n Delaying for 3 seconds now.\n",
);
delays::wait_msec(1000);
uart.puts("1..");
delays::wait_msec(1000);
delays::wait_msec(1_000_000);
uart.puts(" 1..");
delays::wait_msec(1_000_000);
uart.puts("2..");
delays::wait_msec(1000);
delays::wait_msec(1_000_000);
uart.puts(
"3\n\
Works!\n\n",
"3\
\n Works!\n\n",
);
}
@ -55,10 +55,10 @@ fn check_daif(uart: &uart::Uart) {
let daif = DAIF.extract();
for x in &[
("D: ", DAIF::D),
("A: ", DAIF::A),
("I: ", DAIF::I),
("F: ", DAIF::F),
(" D: ", DAIF::D),
(" A: ", DAIF::A),
(" I: ", DAIF::I),
(" F: ", DAIF::F),
] {
uart.puts(x.0);
if daif.is_set(x.1) {
@ -69,24 +69,25 @@ fn check_daif(uart: &uart::Uart) {
}
}
raspi3_boot::entry!(kernel_entry);
fn kernel_entry() -> ! {
let gpio = gpio::GPIO::new();
let mut mbox = mbox::Mbox::new();
let uart = uart::Uart::new();
// set up serial console
if uart.init(&mut mbox).is_err() {
loop {
match uart.init(&mut mbox, &gpio) {
Ok(_) => uart.puts("\n[0] UART is live!\n"),
Err(_) => loop {
cortex_a::asm::wfe() // If UART fails, abort early
}
},
}
uart.getc(); // Press a key first before being greeted
uart.puts("Hello Rustacean!\n\n");
uart.puts("[1] Press a key to continue booting... ");
uart.getc();
uart.puts("Greetings fellow Rustacean!\n");
uart.puts("Executing in EL: ");
uart.hex(CurrentEL.read(CurrentEL::EL));
uart.puts("[i] Executing in EL: ");
uart.dec(CurrentEL.read(CurrentEL::EL));
uart.puts("\n\n");
check_timer(&uart);
@ -97,3 +98,5 @@ fn kernel_entry() -> ! {
uart.send(uart.getc());
}
}
raspi3_boot::entry!(kernel_entry);

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

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy --
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
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
cargo xbuild --target=$(TARGET)
cp $@ .
all: clean kernel8.img
target/$(TARGET)/release/kernel8: src/main.rs
target/$(TARGET)/release/kernel8: $(SOURCES)
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
qemu: all

Binary file not shown.

Binary file not shown.

@ -69,6 +69,8 @@ unsafe fn reset() -> ! {
fn setup_and_enter_el1_from_el2() -> ! {
use cortex_a::{asm, regs::*};
const STACK_START: u64 = 0x80_000;
// Enable timer counter registers for EL1
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
// 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
// `reset()` in EL1.
@ -116,10 +118,8 @@ pub unsafe extern "C" fn _boot_cores() -> ! {
const CORE_MASK: u64 = 0x3;
const EL2: u32 = CurrentEL::EL::EL2.value;
if let CORE_0 = MPIDR_EL1.get() & CORE_MASK {
if let EL2 = CurrentEL.get() {
setup_and_enter_el1_from_el2()
}
if (CORE_0 == MPIDR_EL1.get() & CORE_MASK) && (EL2 == CurrentEL.get()) {
setup_and_enter_el1_from_el2()
}
// 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 core::ops;
use register::{mmio::ReadWrite, register_bitfields};
// Descriptions taken from
@ -66,10 +67,55 @@ register_bitfields! {
]
}
pub const GPFSEL1: *const ReadWrite<u32, GPFSEL1::Register> =
(MMIO_BASE + 0x0020_0004) as *const ReadWrite<u32, GPFSEL1::Register>;
const GPIO_BASE: u32 = MMIO_BASE + 0x200_000;
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> =
(MMIO_BASE + 0x0020_0098) as *const ReadWrite<u32, GPPUDCLK0::Register>;
impl ops::Deref for GPIO {
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;
mod delays;
mod gpio;
mod mbox;
mod mmu;
mod uart;
raspi3_boot::entry!(kernel_entry);
fn kernel_entry() -> ! {
let gpio = gpio::GPIO::new();
let mut mbox = mbox::Mbox::new();
{
@ -44,18 +44,20 @@ fn kernel_entry() -> ! {
let uart = uart::Uart::new(uart::UART_PHYS_BASE);
// set up serial console
if uart.init(&mut mbox).is_err() {
loop {
match uart.init(&mut mbox, &gpio) {
Ok(_) => uart.puts("\n[0] UART is live!\n"),
Err(_) => loop {
cortex_a::asm::wfe() // If UART fails, abort early
}
},
}
uart.getc(); // Press a key first before being greeted
uart.puts("Hello Rustacean!\n\n");
uart.puts("[1] Press a key to continue booting... ");
uart.getc();
uart.puts("Greetings fellow Rustacean!\n");
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.
unsafe { mmu::init() };
@ -66,7 +68,7 @@ fn kernel_entry() -> ! {
let uart = uart::Uart::new(UART_VIRT_BASE);
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");
// echo everything back
@ -74,3 +76,5 @@ fn kernel_entry() -> ! {
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) =
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) =
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 crate::delays;
use crate::gpio;
use crate::mbox;
use core::{
@ -164,7 +165,7 @@ impl Uart {
}
///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
self.CR.set(0);
@ -189,23 +190,18 @@ impl Uart {
};
// map UART0 to GPIO pins
unsafe {
(*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
gpio.GPFSEL1
.modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
(*gpio::GPPUD).set(0); // enable pins 14 and 15
for _ in 0..150 {
asm::nop();
}
gpio.GPPUD.set(0); // enable pins 14 and 15
delays::wait_cycles(150);
(*gpio::GPPUDCLK0).modify(
gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
);
for _ in 0..150 {
asm::nop();
}
gpio.GPPUDCLK0.modify(
gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
);
delays::wait_cycles(150);
(*gpio::GPPUDCLK0).set(0);
}
gpio.GPPUDCLK0.set(0);
self.ICR.write(ICR::ALL::CLEAR);
self.IBRD.write(IBRD::IBRD.val(2)); // Results in 115200 baud
@ -267,12 +263,12 @@ impl Uart {
}
/// Display a binary value in hexadecimal
pub fn hex(&self, d: u32) {
pub fn hex(&self, d: u64) {
let mut n;
for i in 0..8 {
for i in 0..16 {
// 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'
// Add proper offset for ASCII table

@ -24,6 +24,8 @@
TARGET = aarch64-unknown-none
SOURCES = $(wildcard **/*.rs) $(wildcard **/*.S) link.ld
OBJCOPY = cargo objcopy --
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
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
cargo xbuild --target=$(TARGET)
cp $@ .
all: clean kernel8.img
target/$(TARGET)/release/kernel8: src/main.rs
target/$(TARGET)/release/kernel8: $(SOURCES)
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
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:
```text
Benchmarking non-cacheable DRAM modifications at virtual 0x00200000, physical 0x00400000:
Benchmarking non-cacheable DRAM modifications at virtual 0x0000000000200000, physical 0x0000000000400000:
1040 miliseconds.
Benchmarking cacheable DRAM modifications at virtual 0x00400000, physical 0x00400000:
Benchmarking cacheable DRAM modifications at virtual 0x0000000000400000, physical 0x0000000000400000:
53 miliseconds.
With caching, the function is 1862% faster!
With caching, the function is 1800% faster!
```
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() -> ! {
use cortex_a::{asm, regs::*};
const STACK_START: u64 = 0x80_000;
// Enable timer counter registers for EL1
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
// 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
// `reset()` in EL1.
@ -116,10 +118,8 @@ pub unsafe extern "C" fn _boot_cores() -> ! {
const CORE_MASK: u64 = 0x3;
const EL2: u32 = CurrentEL::EL::EL2.value;
if let CORE_0 = MPIDR_EL1.get() & CORE_MASK {
if let EL2 = CurrentEL.get() {
setup_and_enter_el1_from_el2()
}
if (CORE_0 == MPIDR_EL1.get() & CORE_MASK) && (EL2 == CurrentEL.get()) {
setup_and_enter_el1_from_el2()
}
// 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;
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.hex(2 * SIZE_2MIB as u32);
uart.hex(2 * SIZE_2MIB);
uart.puts(":\n");
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.hex(cacheable_addr as u32);
uart.hex(cacheable_addr);
uart.puts(", physical 0x");
uart.hex(2 * SIZE_2MIB as u32);
uart.hex(2 * SIZE_2MIB);
uart.puts(":\n");
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 core::ops;
use register::{mmio::ReadWrite, register_bitfields};
// Descriptions taken from
@ -66,10 +67,55 @@ register_bitfields! {
]
}
pub const GPFSEL1: *const ReadWrite<u32, GPFSEL1::Register> =
(MMIO_BASE + 0x0020_0004) as *const ReadWrite<u32, GPFSEL1::Register>;
const GPIO_BASE: u32 = MMIO_BASE + 0x200_000;
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> =
(MMIO_BASE + 0x0020_0098) as *const ReadWrite<u32, GPPUDCLK0::Register>;
impl ops::Deref for GPIO {
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;
mod benchmark;
mod delays;
mod gpio;
mod mbox;
mod mmu;
mod uart;
raspi3_boot::entry!(kernel_entry);
fn kernel_entry() -> ! {
let gpio = gpio::GPIO::new();
let mut mbox = mbox::Mbox::new();
let uart = uart::Uart::new(uart::UART_PHYS_BASE);
// set up serial console
if uart.init(&mut mbox).is_err() {
loop {
match uart.init(&mut mbox, &gpio) {
Ok(_) => uart.puts("\n[0] UART is live!\n"),
Err(_) => loop {
cortex_a::asm::wfe() // If UART fails, abort early
}
},
}
uart.getc(); // Press a key first before being greeted
uart.puts("Hello Rustacean!\n\n");
uart.puts("[1] Press a key to continue booting... ");
uart.getc();
uart.puts("Greetings fellow Rustacean!\n");
uart.puts("\nSwitching MMU on now...");
uart.puts("[2] Switching MMU on now... ");
unsafe { mmu::init() };
@ -64,3 +66,5 @@ fn kernel_entry() -> ! {
uart.send(uart.getc());
}
}
raspi3_boot::entry!(kernel_entry);

@ -23,6 +23,7 @@
*/
use super::MMIO_BASE;
use crate::delays;
use crate::gpio;
use crate::mbox;
use core::{
@ -164,7 +165,7 @@ impl Uart {
}
///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
self.CR.set(0);
@ -189,23 +190,18 @@ impl Uart {
};
// map UART0 to GPIO pins
unsafe {
(*gpio::GPFSEL1).modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
gpio.GPFSEL1
.modify(gpio::GPFSEL1::FSEL14::TXD0 + gpio::GPFSEL1::FSEL15::RXD0);
(*gpio::GPPUD).set(0); // enable pins 14 and 15
for _ in 0..150 {
asm::nop();
}
gpio.GPPUD.set(0); // enable pins 14 and 15
delays::wait_cycles(150);
(*gpio::GPPUDCLK0).modify(
gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
);
for _ in 0..150 {
asm::nop();
}
gpio.GPPUDCLK0.modify(
gpio::GPPUDCLK0::PUDCLK14::AssertClock + gpio::GPPUDCLK0::PUDCLK15::AssertClock,
);
delays::wait_cycles(150);
(*gpio::GPPUDCLK0).set(0);
}
gpio.GPPUDCLK0.set(0);
self.ICR.write(ICR::ALL::CLEAR);
self.IBRD.write(IBRD::IBRD.val(2)); // Results in 115200 baud
@ -267,12 +263,12 @@ impl Uart {
}
/// Display a binary value in hexadecimal
pub fn hex(&self, d: u32) {
pub fn hex(&self, d: u64) {
let mut n;
for i in 0..8 {
for i in 0..16 {
// 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'
// Add proper offset for ASCII table

Loading…
Cancel
Save