1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-03-30 01:16:17 +02:00

Add optional extension to RTC exercise (#562)

* Add methods to mask and clear interrupts.

* Start on GICv3 driver.

* Enable and use RTC interrupt.

* WFI

* Add newtype for interrupt ID.

* Add extension to use interrupt.

* Add method to send an SGI.

* Silence warnings about unused methods in provided drivers.

* Implement Debug manually for IntId.

It's useful to say what kind of interrupt it is.

* Acknowledge and log interrupt.

We should end it as well, but doing so results in a loop unless we first
clear the match.

* cargo fmt with imports_granularity = "module"

* Use arm-gic crate rather than including driver in the example.
This commit is contained in:
Andrew Walbran 2023-04-17 15:38:51 +01:00 committed by GitHub
parent d143528e07
commit c5863f6642
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 109 additions and 8 deletions

View File

@ -7,6 +7,8 @@ should write a driver for it.
date/time formatting.
2. Use the match register and raw interrupt status to busy-wait until a given time, e.g. 3 seconds
in the future. (Call [`core::hint::spin_loop`][3] inside the loop.)
3. _Extension if you have time:_ Enable and handle the interrupt generated by the RTC match. You can
use the driver provided in the `arm-gic` crate to configure the Arm Generic Interrupt Controller.
Download the [exercise template](../../comprehensive-rust-exercises.zip) and look in the `rtc`
directory for the following files.
@ -29,7 +31,7 @@ directory for the following files.
{{#include rtc/src/main.rs:main_end}}
```
`src/exceptions.rs` (you shouldn't need to change this):
`src/exceptions.rs` (you should only need to change this for the 3rd part of the exercise):
<!-- File src/exceptions.rs -->

View File

@ -2,6 +2,15 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "arm-gic"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d16805d71f41d1714a51ee34f40633c42980a2afaf8ae237bcba09d6e15494ef"
dependencies = [
"bitflags",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -10,9 +19,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "2.0.2"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1"
checksum = "c70beb79cbb5ce9c4f8e20849978f34225931f665bb49efa6982875a4d5facb3"
[[package]]
name = "cc"
@ -84,6 +93,7 @@ checksum = "3374e3ae47f134467227a48be93b929e5d304efcd25ce5d176006403ca1d9bab"
name = "rtc"
version = "0.1.0"
dependencies = [
"arm-gic",
"bitflags",
"cc",
"chrono",

View File

@ -7,6 +7,7 @@ edition = "2021"
publish = false
[dependencies]
arm-gic = "0.1.0"
bitflags = "2.0.0"
chrono = { version = "0.4.24", default-features = false }
log = "0.4.17"

View File

@ -31,7 +31,7 @@ rtc.bin: build
$(OBJCOPY) -O binary target/aarch64-unknown-none/debug/rtc $@
qemu: rtc.bin
qemu-system-aarch64 -machine virt -cpu max -serial mon:stdio -display none -kernel $< -s
qemu-system-aarch64 -machine virt,gic_version=3 -cpu max -serial mon:stdio -display none -kernel $< -s
clean:
cargo clean

View File

@ -12,7 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use log::error;
use arm_gic::gicv3::GicV3;
use log::{error, info, trace};
use psci::system_off;
#[no_mangle]
@ -23,8 +24,9 @@ extern "C" fn sync_exception_current(_elr: u64, _spsr: u64) {
#[no_mangle]
extern "C" fn irq_current(_elr: u64, _spsr: u64) {
error!("irq_current");
system_off().unwrap();
trace!("irq_current");
let intid = GicV3::get_and_acknowledge_interrupt().expect("No pending interrupt");
info!("IRQ {intid:?}");
}
#[no_mangle]

View File

@ -23,20 +23,29 @@ mod pl011;
mod pl031;
use crate::pl031::Rtc;
use arm_gic::gicv3::{IntId, Trigger};
use arm_gic::{irq_enable, wfi};
use chrono::{TimeZone, Utc};
use core::hint::spin_loop;
// ANCHOR: imports
use crate::pl011::Uart;
use arm_gic::gicv3::GicV3;
use core::panic::PanicInfo;
use log::{error, info, LevelFilter};
use log::{error, info, trace, LevelFilter};
use psci::system_off;
/// Base addresses of the GICv3.
const GICD_BASE_ADDRESS: *mut u64 = 0x800_0000 as _;
const GICR_BASE_ADDRESS: *mut u64 = 0x80A_0000 as _;
/// Base address of the primary PL011 UART.
const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _;
// ANCHOR_END: imports
/// Base address of the PL031 RTC.
const PL031_BASE_ADDRESS: *mut u32 = 0x901_0000 as _;
/// The IRQ used by the PL031 RTC.
const PL031_IRQ: IntId = IntId::spi(2);
// ANCHOR: main
#[no_mangle]
@ -47,6 +56,12 @@ extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {
logger::init(uart, LevelFilter::Trace).unwrap();
info!("main({:#x}, {:#x}, {:#x}, {:#x})", x0, x1, x2, x3);
// Safe because `GICD_BASE_ADDRESS` and `GICR_BASE_ADDRESS` are the base
// addresses of a GICv3 distributor and redistributor respectively, and
// nothing else accesses those address ranges.
let mut gic = unsafe { GicV3::new(GICD_BASE_ADDRESS, GICR_BASE_ADDRESS) };
gic.setup();
// ANCHOR_END: main
// Safe because `PL031_BASE_ADDRESS` is the base address of a PL031 device,
@ -56,6 +71,12 @@ extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {
let time = Utc.timestamp_opt(timestamp.into(), 0).unwrap();
info!("RTC: {time}");
GicV3::set_priority_mask(0xff);
gic.set_interrupt_priority(PL031_IRQ, 0x80);
gic.set_trigger(PL031_IRQ, Trigger::Level);
irq_enable();
gic.enable_interrupt(PL031_IRQ, true);
// Wait for 3 seconds, without interrupts.
let target = timestamp + 3;
rtc.set_match(target);
@ -63,9 +84,43 @@ extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {
"Waiting for {}",
Utc.timestamp_opt(target.into(), 0).unwrap()
);
trace!(
"matched={}, interrupt_pending={}",
rtc.matched(),
rtc.interrupt_pending()
);
while !rtc.matched() {
spin_loop();
}
trace!(
"matched={}, interrupt_pending={}",
rtc.matched(),
rtc.interrupt_pending()
);
info!("Finished waiting");
// Wait another 3 seconds for an interrupt.
let target = timestamp + 6;
info!(
"Waiting for {}",
Utc.timestamp_opt(target.into(), 0).unwrap()
);
rtc.set_match(target);
rtc.clear_interrupt();
rtc.enable_interrupt(true);
trace!(
"matched={}, interrupt_pending={}",
rtc.matched(),
rtc.interrupt_pending()
);
while !rtc.interrupt_pending() {
wfi();
}
trace!(
"matched={}, interrupt_pending={}",
rtc.matched(),
rtc.interrupt_pending()
);
info!("Finished waiting");
// ANCHOR: main_end

View File

@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![allow(unused)]
use core::fmt::{self, Write};
use core::ptr::{addr_of, addr_of_mut};

View File

@ -83,6 +83,35 @@ impl Rtc {
let ris = unsafe { addr_of!((*self.registers).ris).read_volatile() };
(ris & 0x01) != 0
}
/// Returns whether there is currently an interrupt pending.
///
/// This should be true iff `matched` returns true and the interrupt is
/// masked.
pub fn interrupt_pending(&self) -> bool {
// Safe because we know that self.registers points to the control
// registers of a PL031 device which is appropriately mapped.
let ris = unsafe { addr_of!((*self.registers).mis).read_volatile() };
(ris & 0x01) != 0
}
/// Sets or clears the interrupt mask.
///
/// When the mask is true the interrupt is enabled; when it is false the
/// interrupt is disabled.
pub fn enable_interrupt(&mut self, mask: bool) {
let imsc = if mask { 0x01 } else { 0x00 };
// Safe because we know that self.registers points to the control
// registers of a PL031 device which is appropriately mapped.
unsafe { addr_of_mut!((*self.registers).imsc).write_volatile(imsc) }
}
/// Clears a pending interrupt, if any.
pub fn clear_interrupt(&mut self) {
// Safe because we know that self.registers points to the control
// registers of a PL031 device which is appropriately mapped.
unsafe { addr_of_mut!((*self.registers).icr).write_volatile(0x01) }
}
}
// Safe because it just contains a pointer to device memory, which can be