1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
//! [SerialIO](crate::serial::SerialIO) UART implementations.
//!
//! ## License
//!
//! Copyright (C) Microsoft Corporation.
//!
//! SPDX-License-Identifier: Apache-2.0
//!
/// A null (stub) device that does nothing.
#[derive(Debug)]
pub struct UartNull {}
impl super::SerialIO for UartNull {
fn init(&self) {}
fn write(&self, _buffer: &[u8]) {}
fn read(&self) -> u8 {
// PANIC: Would loop forever, better to panic.
panic!();
}
fn try_read(&self) -> Option<u8> {
None
}
}
cfg_if::cfg_if! {
if #[cfg(all(target_arch = "x86_64", any(target_os = "uefi", feature = "doc")))] {
use uart_16550::MmioSerialPort;
use uart_16550::SerialPort as IoSerialPort;
use x86_64::instructions::interrupts;
/// An interface for writing to a Uart16550 device.
#[derive(Debug)]
pub enum Uart16550 {
/// The I/O interface for the Uart16550 serial port.
Io {
/// The base address of the UART control registers.
base: u16
},
/// The Memory Mapped I/O interface for the Uart16550 serial port.
Mmio {
/// The base address of the UART control registers.
base: usize,
/// The number of bytes between consecutive registers.
reg_stride: usize
},
}
impl super::SerialIO for Uart16550 {
fn init(&self) {
match self {
Uart16550::Io { base } => {
// SAFETY: The base address is provided during Uart16550 construction and is assumed to be valid for I/O port access.
let mut serial_port = unsafe { IoSerialPort::new(*base) };
serial_port.init();
}
Uart16550::Mmio { base, reg_stride } => {
// SAFETY: The base address and stride are provided during Uart16550 construction and are assumed to be valid for MMIO access.
let mut serial_port = unsafe { MmioSerialPort::new_with_stride(*base, *reg_stride) };
serial_port.init();
}
}
}
fn write(&self, buffer: &[u8]) {
match self {
Uart16550::Io { base } => {
// SAFETY: The base address is provided during Uart16550 construction and is assumed to be valid for I/O port access.
let mut serial_port = unsafe { IoSerialPort::new(*base) };
interrupts::without_interrupts(|| {
for b in buffer {
serial_port.send(*b);
}
});
}
Uart16550::Mmio { base, reg_stride } => {
// SAFETY: The base address and stride are provided during Uart16550 construction and are assumed to be valid for MMIO access.
let mut serial_port = unsafe { MmioSerialPort::new_with_stride(*base, *reg_stride) };
interrupts::without_interrupts(|| {
for b in buffer {
serial_port.send(*b);
}
});
}
}
}
fn read(&self) -> u8 {
match self {
Uart16550::Io { base } => {
// SAFETY: The base address is provided during Uart16550 construction and is assumed to be valid for I/O port access.
let mut serial_port = unsafe { IoSerialPort::new(*base) };
serial_port.receive()
}
Uart16550::Mmio { base, reg_stride } => {
// SAFETY: The base address and stride are provided during Uart16550 construction and are assumed to be valid for MMIO access.
let mut serial_port = unsafe { MmioSerialPort::new_with_stride(*base, *reg_stride) };
serial_port.receive()
}
}
}
fn try_read(&self) -> Option<u8> {
match self {
Uart16550::Io { base } => {
// SAFETY: The base address is provided during Uart16550 construction and is assumed to be valid for I/O port access.
let mut serial_port = unsafe { IoSerialPort::new(*base) };
serial_port.try_receive().ok()
}
Uart16550::Mmio { base, reg_stride } => {
// SAFETY: The base address and stride are provided during Uart16550 construction and are assumed to be valid for MMIO access.
let mut serial_port = unsafe { MmioSerialPort::new_with_stride(*base, *reg_stride) };
serial_port.try_receive().ok()
}
}
}
}
}
}
cfg_if::cfg_if! {
if #[cfg(any(feature = "doc", all(target_os = "uefi", target_arch = "aarch64")))] {
use core::ptr::NonNull;
use crate::mmio::{field, fields::{ReadPure, ReadWrite}, UniqueMmioPointer};
/// PL011 flag register bit: UART busy.
const FR_BUSY: u8 = 1 << 3;
/// PL011 flag register bit: receive FIFO empty.
const FR_RXFE: u8 = 1 << 4;
/// PL011 flag register bit: transmit FIFO full.
const FR_TXFF: u8 = 1 << 5;
/// PL011 MMIO register block.
///
/// Models the Data Register (DR) at offset 0x00 and the Flag Register (FR) at offset 0x18.
/// Intermediate registers are represented as reserved padding.
#[repr(C)]
struct Pl011Registers {
/// Data Register: reading pops from receive FIFO (side-effect), writing pushes to
/// transmit FIFO.
dr: ReadWrite<u8>,
/// Reserved registers between DR (0x00) and FR (0x18).
_reserved: [u8; 0x17],
/// Flag Register: reading has no side-effects (pure status bits).
fr: ReadPure<u8>,
}
/// An interface for writing to a UartPl011 device.
#[derive(Debug)]
pub struct UartPl011 {
/// The base address of the UART control registers.
base_address: usize,
}
impl UartPl011 {
/// Constructs a new instance of the UART driver for a PL011 device at the
/// given base address.
///
/// # Safety
///
/// The given base address must point to the MMIO control registers of a
/// PL011 device, which must be mapped into the address space of the process
/// as device memory and not have any other aliases.
pub const fn new(base_address: usize) -> Self {
Self { base_address }
}
/// Returns a [`UniqueMmioPointer`] to the PL011 register block.
///
/// # Safety
///
/// The caller must ensure that no other `UniqueMmioPointer` to the same
/// MMIO region exists for the duration of the returned pointer's use.
unsafe fn registers(&self) -> UniqueMmioPointer<'_, Pl011Registers> {
// SAFETY: The base address is required by the safety contract of new() to point
// to a PL011 register block that is mapped as device memory.
unsafe {
UniqueMmioPointer::new(NonNull::new(self.base_address as *mut Pl011Registers).unwrap())
}
}
/// Writes a single byte to the UART.
pub fn write_byte(&self, byte: u8) {
// SAFETY: Exclusive MMIO access is given by calling `UartPl011::new`.
let mut regs = unsafe { self.registers() };
// Wait until there is room in the TX buffer.
while field!(regs, fr).read() & FR_TXFF != 0 {}
// Write to the TX buffer.
field!(regs, dr).write(byte);
// Wait until the UART is no longer busy.
while field!(regs, fr).read() & FR_BUSY != 0 {}
}
/// Reads a single byte from the UART.
pub fn read_byte(&self) -> Option<u8> {
// SAFETY: Exclusive MMIO access is given by calling `UartPl011::new`.
let mut regs = unsafe { self.registers() };
// Check if the RX buffer is empty.
if field!(regs, fr).read() & FR_RXFE != 0 {
return None;
}
// Read from the RX buffer.
Some(field!(regs, dr).read())
}
}
impl super::SerialIO for UartPl011 {
fn init(&self) {}
fn write(&self, buffer: &[u8]) {
for byte in buffer {
self.write_byte(*byte);
}
}
fn read(&self) -> u8 {
loop {
if let Some(byte) = self.read_byte() {
return byte;
}
}
}
fn try_read(&self) -> Option<u8> {
self.read_byte()
}
}
}
}