Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ x86_64 = { version = "0.15", default-features = false, features = ["instructions

[target.'cfg(target_arch = "aarch64")'.dependencies]
aarch64-cpu = "10"
enum_dispatch = "0.3"
fdt = { version = "0.1" }
goblin = { version = "0.10", default-features = false, features = ["elf64"] }
volatile = { version = "0.6", features = ["derive"] }

[target.'cfg(target_os = "uefi")'.dependencies]
uefi = { version = "0.35", features = ["alloc", "panic_handler", "qemu"] }
Expand Down
57 changes: 33 additions & 24 deletions src/arch/aarch64/console.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
use core::ptr::NonNull;
use core::num::NonZeroU32;

use fdt::Fdt;

use crate::arch::aarch64::drivers::SerialDriver;
use crate::arch::drivers::qemu_serial::QemuSerial;
use crate::arch::drivers::xlnx_serial::XlnxSerial;
use crate::arch::drivers::{SerialPort, get_device};

pub struct Console {
stdout: NonNull<u8>,
stdout: SerialPort,
}

fn stdout() -> u32 {
fn stdout() -> SerialPort {
/// Physical address of UART0 at Qemu's virt emulation
const SERIAL_PORT_ADDRESS: u32 = 0x09000000;

Expand All @@ -16,40 +21,44 @@ fn stdout() -> u32 {
};

let property = dtb.chosen().stdout();
if let Some(stdout) = property {
let stdout = stdout.name.trim_matches(char::from(0));
if let Some(pos) = stdout.find('@') {
let len = stdout.len();
u32::from_str_radix(&stdout[pos + 1..len], 16).unwrap_or(SERIAL_PORT_ADDRESS)
} else {
SERIAL_PORT_ADDRESS
}
} else {
SERIAL_PORT_ADDRESS
}
property
.and_then(|node| get_device(node))
.unwrap_or(SerialPort::Qemu(QemuSerial::from_addr(
NonZeroU32::new(SERIAL_PORT_ADDRESS).unwrap(),
)))
}

impl Console {
pub fn write_bytes(&mut self, bytes: &[u8]) {
for byte in bytes.iter().copied() {
unsafe {
self.stdout.as_ptr().write_volatile(byte);
}
}
self.stdout.putstr(bytes);
}

pub(super) fn get_stdout(&self) -> NonNull<u8> {
self.stdout
pub(super) fn get_stdout(&self) -> u32 {
self.stdout.get_addr()
}

pub(crate) fn set_stdout(&mut self, stdout: u32) {
match self.stdout {
SerialPort::Qemu(_) => {
self.stdout =
SerialPort::Qemu(QemuSerial::from_addr(NonZeroU32::new(stdout).unwrap()))
}
SerialPort::Xlnx(_) => {
self.stdout =
SerialPort::Xlnx(XlnxSerial::from_addr(NonZeroU32::new(stdout).unwrap()))
}
}
self.stdout.init();
}

pub(super) fn set_stdout(&mut self, stdout: NonNull<u8>) {
self.stdout = stdout;
pub(crate) fn wait_empty(&mut self) {
self.stdout.wait_empty();
}
}

impl Default for Console {
fn default() -> Self {
let stdout = NonNull::new(stdout() as *mut u8).unwrap();
let stdout = stdout();
Self { stdout }
}
}
Expand Down
47 changes: 47 additions & 0 deletions src/arch/aarch64/drivers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use core::num::NonZeroU32;

use enum_dispatch::enum_dispatch;
use fdt::node::FdtNode;
use qemu_serial::QemuSerial;
use xlnx_serial::XlnxSerial;

pub mod qemu_serial;
pub mod xlnx_serial;

pub enum SerialSuccess<T> {
Success(T),
ERetry,
}

#[enum_dispatch]
pub trait SerialDriver {
fn init(&mut self);
fn putc(&mut self, c: u8) -> SerialSuccess<u8>;
fn putstr(&mut self, s: &[u8]);
fn get_addr(&self) -> u32;
fn wait_empty(&mut self);
}

#[enum_dispatch(SerialDriver)]
pub enum SerialPort {
Qemu(QemuSerial),
Xlnx(XlnxSerial),
}

pub fn get_device<'a>(node: FdtNode<'_, 'a>) -> Option<SerialPort> {
let compat = node.compatible()?;
let reg = node.reg()?.next()?;

for id in compat.all() {
if id == "arm,pl011" {
return Some(SerialPort::Qemu(QemuSerial::from_addr(
NonZeroU32::new(reg.starting_address as u32).unwrap(),
)));
} else if id == "xlnx,xuartlite" {
return Some(SerialPort::Xlnx(XlnxSerial::from_addr(
NonZeroU32::new(reg.starting_address as u32).unwrap(),
)));
}
}
None
}
46 changes: 46 additions & 0 deletions src/arch/aarch64/drivers/qemu_serial.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use core::num::NonZeroU32;
use core::ptr::NonNull;

use volatile::{VolatileFieldAccess, VolatileRef};

use crate::arch::drivers::SerialSuccess::Success;
use crate::arch::drivers::{SerialDriver, SerialSuccess};

#[repr(C)]
#[derive(VolatileFieldAccess)]
struct QemuPort {
out: u8,
}

pub struct QemuSerial {
regs: VolatileRef<'static, QemuPort>,
}

impl QemuSerial {
pub fn from_addr(base_addr: NonZeroU32) -> QemuSerial {
Self {
regs: unsafe {
VolatileRef::new(NonNull::new_unchecked(base_addr.get() as *mut QemuPort))
},
}
}
}

impl SerialDriver for QemuSerial {
fn init(&mut self) {}
fn putc(&mut self, c: u8) -> SerialSuccess<u8> {
self.regs.as_mut_ptr().out().write(c);
Success(c)
}

fn putstr(&mut self, s: &[u8]) {
for c in s.iter().copied() {
let _ = self.putc(c);
}
}
fn get_addr(&self) -> u32 {
self.regs.as_ptr().as_raw_ptr().as_ptr() as u32
}

fn wait_empty(&mut self) {}
}
108 changes: 108 additions & 0 deletions src/arch/aarch64/drivers/xlnx_serial.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use core::hint;
use core::num::NonZeroU32;
use core::ptr::NonNull;

use aarch64_cpu::asm::barrier;
use aarch64_cpu::asm::barrier::SY;
use volatile::{VolatileFieldAccess, VolatileRef};

use crate::arch::drivers::SerialSuccess::{ERetry, Success};
use crate::arch::drivers::{SerialDriver, SerialSuccess};

const ZYNQ_UART_SR_TXACTIVE: u32 = 1 << 11;
const ZYNQ_UART_SR_TXFULL: u32 = 1 << 4;
const ZYNQ_UART_SR_TXEMPTY: u32 = 1 << 3;

const ZYNQ_UART_CR_TX_EN: u32 = 1 << 4;
const ZYNQ_UART_CR_RX_EN: u32 = 1 << 2;
const ZYNQ_UART_CR_TXRST: u32 = 1 << 1;
const ZYNQ_UART_CR_RXRST: u32 = 1 << 0;

const ZYNQ_UART_MR_PARITY_NONE: u32 = 0x00000020;

#[repr(C)]
#[derive(VolatileFieldAccess)]
pub struct XlnxRegisters {
control: u32,
mode: u32,
reserved1: [u32; 4],
baud_rate_gen: u32,
reserved2: [u32; 4],
channel_sts: u32,
tx_rx_fifo: u32,
baud_rate_divider: u32,
}

pub struct XlnxSerial {
regs: VolatileRef<'static, XlnxRegisters>,
}

impl XlnxSerial {
pub fn from_addr(base_addr: NonZeroU32) -> XlnxSerial {
Self {
regs: unsafe {
VolatileRef::new(NonNull::new_unchecked(base_addr.get() as *mut XlnxRegisters))
},
}
}
}

impl SerialDriver for XlnxSerial {
fn init(&mut self) {
self.regs.as_mut_ptr().control().write(
ZYNQ_UART_CR_TX_EN | ZYNQ_UART_CR_RX_EN | ZYNQ_UART_CR_TXRST | ZYNQ_UART_CR_RXRST,
);
self.regs
.as_mut_ptr()
.mode()
.write(ZYNQ_UART_MR_PARITY_NONE);
}

fn putc(&mut self, c: u8) -> SerialSuccess<u8> {
barrier::dmb(SY);
barrier::dsb(SY);
if self.regs.as_mut_ptr().channel_sts().read() & ZYNQ_UART_SR_TXFULL != 0 {
return ERetry;
}

self.regs.as_mut_ptr().tx_rx_fifo().write(c as u32);
Success(c)
}

fn putstr(&mut self, s: &[u8]) {
'foo: for c in s.iter().copied() {
if c == b'\n' {
for _ in 0..1000 {
match self.putc(b'\r') {
ERetry => continue,
Success(_) => break,
}
}
}
for _ in 0..1000 {
hint::spin_loop();
match self.putc(c) {
ERetry => continue,
Success(_) => continue 'foo,
}
}
self.init();
while self.regs.as_mut_ptr().channel_sts().read() & ZYNQ_UART_SR_TXEMPTY != 0 {
hint::spin_loop();
}
}
}

fn get_addr(&self) -> u32 {
self.regs.as_ptr().as_raw_ptr().as_ptr() as u32
}

fn wait_empty(&mut self) {
while self.regs.as_mut_ptr().channel_sts().read() & ZYNQ_UART_SR_TXACTIVE != 0 {
hint::spin_loop();
}
while self.regs.as_mut_ptr().channel_sts().read() & ZYNQ_UART_SR_TXEMPTY != 0 {
hint::spin_loop();
}
}
}
Loading
Loading