Alioth Code Coverage

pl011.rs70.48%

1// Copyright 2024 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::collections::VecDeque;
16use std::io::{Read, Write};
17use std::sync::Arc;
18
19use parking_lot::Mutex;
20
21use crate::device::console::{Console, ConsoleThread, UartRecv};
22use crate::device::{MmioDev, Pause, Result};
23use crate::hv::IrqSender;
24use crate::mem::emulated::{Action, Mmio};
25use crate::{bitflags, hv, mem};
26
27/// RW width 12/8 Data Register
28const UART_DR: u64 = 0x0;
29/// RO width 4 Receive Status Register
30const UART_RSR: u64 = 0x4;
31/// WO width 0  Error Clear Register
32const UART_ECR: u64 = 0x4;
33/// RO width 9 Flag Register
34const UART_FR: u64 = 0x18;
35/// RW width 8 IrDA Low-Power Counter Register
36const UART_ILPR: u64 = 0x20;
37/// RW width 16 Integer Baud Rate Register
38const UART_IBRD: u64 = 0x24;
39/// RW width 6 Fractional Baud Rate Register
40const UART_FBRD: u64 = 0x28;
41/// RW width 8 Line Control Register
42const UART_LCR_H: u64 = 0x2C;
43/// RW width 16 Control Register
44const UART_CR: u64 = 0x30;
45/// RW width 6 Interrupt FIFO Level Select Register
46const UART_IFLS: u64 = 0x34;
47/// RW width 11 Interrupt Mask Set/Clear Register
48const UART_IMSC: u64 = 0x38;
49/// RO width 11 Raw Interrupt Status Register
50const UART_RIS: u64 = 0x3C;
51/// RO width 11 Masked Interrupt Status Register
52const UART_MIS: u64 = 0x40;
53/// WO width 11 Interrupt Clear Register
54const UART_ICR: u64 = 0x44;
55/// RW width 3 DMA Control Register
56const UART_DMACR: u64 = 0x48;
57/// RO width 8 UARTPeriphID0 Register
58const UART_PERIPH_ID0: u64 = 0xFE0;
59/// RO width 8 UARTPeriphID1 Register
60const UART_PERIPH_ID1: u64 = 0xFE4;
61/// RO width 8 UARTPeriphID2 Register
62const UART_PERIPH_ID2: u64 = 0xFE8;
63/// RO width 8 UARTPeriphID3 Register
64const UART_PERIPH_ID3: u64 = 0xFEC;
65/// RO width 8 UARTPCellID0 Register
66const UART_PCELL_ID0: u64 = 0xFF0;
67/// RO width 8 UARTPCellID1 Register
68const UART_PCELL_ID1: u64 = 0xFF4;
69/// RO width 8 UARTPCellID2 Register
70const UART_PCELL_ID2: u64 = 0xFF8;
71/// RO width 8 UARTPCellID3 Register
72const UART_PCELL_ID3: u64 = 0xFFC;
73
74// https://developer.arm.com/documentation/ddi0183/g/programmers-model/register-descriptions/peripheral-identification-registers--uartperiphid0-3
75const PERIPH_ID: [u32; 4] = [0x11, 0x10, 0x14, 0x00];
76
77// https://developer.arm.com/documentation/ddi0183/g/programmers-model/register-descriptions/primecell-identification-registers--uartpcellid0-3
78const PCELL_ID: [u32; 4] = [0x0d, 0xf0, 0x05, 0xb1];
79
80bitflags! {
81 #[derive(Default)]
82 pub struct Flag(u16) {
83 RI = 1 << 8;
84 /// Transmit FIFO empty
85 TXFE = 1 << 7;
86 /// Receive FIFO full
87 RXFF = 1 << 6;
88 /// Transmit FIFO full.
89 TXFF = 1 << 5;
90 /// Receive FIFO empty
91 RXFE = 1 << 4;
92 BUSY = 1 << 3;
93 DCD = 1 << 2;
94 DSR = 1 << 1;
95 CTS = 1 << 0;
96 }
97}
98
99bitflags! {
100 #[derive(Default)]
101 pub struct Interrupt(u16) {
102 /// Overrun error interrupt status.
103 OERIS = 1 << 10;
104 /// Break error interrupt status.
105 BERIS = 1 << 9;
106 /// Parity error interrupt status.
107 PERIS = 1 << 8;
108 /// Framing error interrupt status.
109 FERIS = 1 << 7;
110 /// Receive timeout interrupt status.
111 RTRIS = 1 << 6;
112 /// Transmit interrupt status.
113 TXRIS = 1 << 5;
114 /// Receive interrupt status.
115 RXRIS = 1 << 4;
116 /// nUARTDSR modem interrupt status.
117 DSRRMIS = 1 << 3;
118 /// nUARTDCD modem interrupt status.
119 DCDRMIS = 1 << 2;
120 /// nUARTCTS modem interrupt status.
121 CTSRMIS = 1 << 1;
122 /// nUARTRI modem interrupt status.
123 RIRMIS = 1 << 0;
124 }
125}
126
127#[derive(Debug, Default)]
128struct Pl011Reg {
129 data: VecDeque<u8>,
130 flag: Flag,
131 lcr: u32,
132 rsr: u32,
133 cr: u32,
134 dmacr: u32,
135 ilpr: u32,
136 ibrd: u32,
137 fbrd: u32,
138 ifl: u32,
139 interrupt_mask: Interrupt,
140 interrupt_status: Interrupt,
141}
142
143/// https://developer.arm.com/documentation/ddi0183/g
144#[derive(Debug)]
145pub struct Pl011<I, C> {
146 name: Arc<str>,
147 irq_line: Arc<I>,
148 reg: Arc<Mutex<Pl011Reg>>,
149 console: Arc<C>,
150 _thread: ConsoleThread,
151}
152
153impl<I, C> Pl011<I, C>
154where
155 I: IrqSender,
156 C: Console,
157 for<'a> &'a C: Read + Write,
158{
159 pub fn new(base_addr: u64, irq_line: I, console: C) -> Result<Self> {12x
160 let irq_line = Arc::new(irq_line);12x
161 let console = Arc::new(console);12x
162 let mut reg = Pl011Reg::default();12x
163 reg.flag |= Flag::RXFE | Flag::TXFE;12x
164 let reg = Arc::new(Mutex::new(reg));12x
165 let name: Arc<str> = Arc::from(format!("pl011@{base_addr:#x}"));12x
166 let pl011_recv = Pl011Recv {12x
167 irq_line: irq_line.clone(),12x
168 reg: reg.clone(),12x
169 };12x
170 let thread = ConsoleThread::new(name.clone(), pl011_recv, console.clone())?;12x
171 let pl011 = Pl011 {12x
172 name,12x
173 irq_line,12x
174 reg,12x
175 console,12x
176 _thread: thread,12x
177 };12x
178 Ok(pl011)12x
179 }12x
180
181 fn update_interrupt(&self, reg: &Pl011Reg) -> Result<(), hv::Error> {21x
182 if (reg.interrupt_status & reg.interrupt_mask).bits() != 0 {21x
183 self.irq_line.send().unwrap();
184 }21x
185 Ok(())21x
186 }21x
187}
188
189impl<I, C> Mmio for Pl011<I, C>
190where
191 I: IrqSender,
192 C: Console,
193 for<'a> &'a C: Read + Write,
194{
195 fn size(&self) -> u64 {3x
196 0x10003x
197 }3x
198
199 fn read(&self, offset: u64, _size: u8) -> mem::Result<u64> {78x
200 let mut reg = self.reg.lock();78x
201 let ret = match offset {78x
202 UART_DR => {
203 let byte = reg.data.pop_front().unwrap_or(0);18x
204 if reg.data.is_empty() {18x
205 reg.flag.insert(Flag::RXFE);12x
206 reg.interrupt_status.remove(Interrupt::RXRIS);12x
207 }12x
208 self.update_interrupt(&reg)?;18x
209 byte as u3218x
210 }
211 UART_RSR => reg.rsr,
212 UART_FR => reg.flag.bits() as u32,15x
213 UART_ILPR => reg.ilpr,
214 UART_IBRD => reg.ibrd,
215 UART_FBRD => reg.fbrd,
216 UART_LCR_H => reg.lcr,
217 UART_CR => reg.cr,
218 UART_IFLS => reg.ifl,
219 UART_IMSC => reg.interrupt_mask.bits() as u32,
220 UART_RIS => reg.interrupt_status.bits() as u32,12x
221 UART_MIS => (reg.interrupt_mask & reg.interrupt_status).bits() as u32,9x
222 UART_ICR => {
223 log::error!("{}: UART_ICR is write only", self.name);
224 0
225 }
226 UART_DMACR => reg.dmacr,
227 UART_PERIPH_ID0 => PERIPH_ID[0],3x
228 UART_PERIPH_ID1 => PERIPH_ID[1],3x
229 UART_PERIPH_ID2 => PERIPH_ID[2],3x
230 UART_PERIPH_ID3 => PERIPH_ID[3],3x
231 UART_PCELL_ID0 => PCELL_ID[0],3x
232 UART_PCELL_ID1 => PCELL_ID[1],3x
233 UART_PCELL_ID2 => PCELL_ID[2],3x
234 UART_PCELL_ID3 => PCELL_ID[3],3x
235 _ => 0,
236 };
237 Ok(ret as u64)78x
238 }78x
239
240 fn write(&self, offset: u64, _size: u8, val: u64) -> mem::Result<Action> {12x
241 let mut reg = self.reg.lock();12x
242 match offset {12x
243 UART_DR => {
244 let _ = self.console.as_ref().write(&[val as u8]);3x
245 reg.interrupt_status.insert(Interrupt::TXRIS);3x
246 reg.flag.insert(Flag::TXFE);3x
247 self.update_interrupt(&reg)?;3x
248 }
249 UART_ECR => reg.rsr = 0,
250 UART_FR => log::error!("{}: UART_FR is read only", self.name),3x
251 UART_ILPR => reg.ilpr = val as u32,
252 UART_IBRD => reg.ibrd = val as u32,
253 UART_FBRD => reg.fbrd = val as u32,
254 UART_LCR_H => reg.lcr = val as u32,
255 UART_CR => reg.cr = val as u32,
256 UART_IFLS => reg.ifl = val as u32,
257 UART_IMSC => {6x
258 reg.interrupt_mask = Interrupt::from_bits_truncate(val as u16);6x
259 }6x
260 UART_RIS => log::error!("{}, UART_RIS is read only", self.name),
261 UART_MIS => log::error!("{}, UART_MIS is read only", self.name),
262 UART_ICR => reg.interrupt_status &= !Interrupt::from_bits_truncate(val as u16),
263 UART_DMACR => reg.dmacr = val as u32,
264 _ => {}
265 }
266 Ok(Action::None)12x
267 }12x
268}
269
270impl<I, C> Pause for Pl011<I, C>
271where
272 I: IrqSender,
273 C: Console,
274 for<'a> &'a C: Read + Write,
275{
276 fn pause(&self) -> Result<()> {
277 Ok(())
278 }
279
280 fn resume(&self) -> Result<()> {
281 Ok(())
282 }
283}
284
285impl<I, C> MmioDev for Pl011<I, C>
286where
287 I: IrqSender,
288 C: Console,
289 for<'a> &'a C: Read + Write,
290{
291}
292
293struct Pl011Recv<I: IrqSender> {
294 irq_line: Arc<I>,
295 reg: Arc<Mutex<Pl011Reg>>,
296}
297
298impl<I: IrqSender> UartRecv for Pl011Recv<I> {
299 fn receive(&self, bytes: &[u8]) {6x
300 let mut reg = self.reg.lock();6x
301 reg.data.extend(bytes);6x
302 reg.interrupt_status.insert(Interrupt::RXRIS);6x
303 reg.flag.remove(Flag::RXFE);6x
304
305 if (reg.interrupt_status & reg.interrupt_mask).bits() != 0 {6x
306 self.irq_line.send().unwrap();3x
307 }3x
308 }6x
309}
310
311#[cfg(test)]
312#[path = "pl011_test.rs"]
313mod tests;
314