mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-05-24 03:10:28 +02:00
Start on GICv3 driver.
This commit is contained in:
parent
ce752db15a
commit
9aed038d8e
@ -37,6 +37,14 @@ directory for the following files.
|
|||||||
{{#include rtc/src/exceptions.rs}}
|
{{#include rtc/src/exceptions.rs}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`src/gicv3.rs` (you shouldn't need to change this):
|
||||||
|
|
||||||
|
<!-- File src/gicv3.rs -->
|
||||||
|
|
||||||
|
```rust,compile_fail
|
||||||
|
{{#include rtc/src/gicv3.rs}}
|
||||||
|
```
|
||||||
|
|
||||||
`src/logger.rs` (you shouldn't need to change this):
|
`src/logger.rs` (you shouldn't need to change this):
|
||||||
|
|
||||||
<!-- File src/logger.rs -->
|
<!-- File src/logger.rs -->
|
||||||
|
@ -31,7 +31,7 @@ rtc.bin: build
|
|||||||
$(OBJCOPY) -O binary target/aarch64-unknown-none/debug/rtc $@
|
$(OBJCOPY) -O binary target/aarch64-unknown-none/debug/rtc $@
|
||||||
|
|
||||||
qemu: rtc.bin
|
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:
|
clean:
|
||||||
cargo clean
|
cargo clean
|
||||||
|
555
src/exercises/bare-metal/rtc/src/gicv3.rs
Normal file
555
src/exercises/bare-metal/rtc/src/gicv3.rs
Normal file
@ -0,0 +1,555 @@
|
|||||||
|
// Copyright 2023 Google LLC
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use bitflags::bitflags;
|
||||||
|
use core::{
|
||||||
|
arch::asm,
|
||||||
|
hint::spin_loop,
|
||||||
|
mem::size_of,
|
||||||
|
ptr::{addr_of, addr_of_mut},
|
||||||
|
};
|
||||||
|
use log::trace;
|
||||||
|
|
||||||
|
/// Reads and returns the value of the given aarch64 system register.
|
||||||
|
macro_rules! read_sysreg {
|
||||||
|
($name:ident) => {
|
||||||
|
{
|
||||||
|
let mut value: u64;
|
||||||
|
::core::arch::asm!(
|
||||||
|
concat!("mrs {value:x}, ", ::core::stringify!($name)),
|
||||||
|
value = out(reg) value,
|
||||||
|
options(nomem, nostack),
|
||||||
|
);
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Writes the given value to the given aarch64 system register.
|
||||||
|
macro_rules! write_sysreg {
|
||||||
|
($name:ident, $value:expr) => {
|
||||||
|
{
|
||||||
|
let v: u64 = $value;
|
||||||
|
::core::arch::asm!(
|
||||||
|
concat!("msr ", ::core::stringify!($name), ", {value:x}"),
|
||||||
|
value = in(reg) v,
|
||||||
|
options(nomem, nostack),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The offset in bytes from `RD_base` to `SGI_base`.
|
||||||
|
const SGI_OFFSET: usize = 0x10000;
|
||||||
|
|
||||||
|
/// The ID of the first Software Generated Interrupt.
|
||||||
|
pub const SGI_START: u32 = 0;
|
||||||
|
|
||||||
|
/// The ID of the first Private Peripheral Interrupt.
|
||||||
|
pub const PPI_START: u32 = 16;
|
||||||
|
|
||||||
|
/// The ID of the first Shared Peripheral Interrupt.
|
||||||
|
pub const SPI_START: u32 = 32;
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
struct GicdCtlr: u32 {
|
||||||
|
const RWP = 1 << 31;
|
||||||
|
const nASSGIreq = 1 << 8;
|
||||||
|
const E1NWF = 1 << 7;
|
||||||
|
const DS = 1 << 6;
|
||||||
|
const ARE_NS = 1 << 5;
|
||||||
|
const ARE_S = 1 << 4;
|
||||||
|
const EnableGrp1S = 1 << 2;
|
||||||
|
const EnableGrp1NS = 1 << 1;
|
||||||
|
const EnableGrp0 = 1 << 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GIC Distributor registers.
|
||||||
|
#[repr(C, align(8))]
|
||||||
|
struct GICD {
|
||||||
|
/// Distributor control register.
|
||||||
|
ctlr: GicdCtlr,
|
||||||
|
/// Interrupt controller type register.
|
||||||
|
typer: u32,
|
||||||
|
/// Distributor implementer identification register.
|
||||||
|
iidr: u32,
|
||||||
|
/// Interrupt controller type register 2.
|
||||||
|
typer2: u32,
|
||||||
|
/// Error reporting status register.
|
||||||
|
statusr: u32,
|
||||||
|
_reserved0: [u32; 3],
|
||||||
|
/// Implementation defined registers.
|
||||||
|
implementation_defined: [u32; 8],
|
||||||
|
/// Set SPI register.
|
||||||
|
setspi_nsr: u32,
|
||||||
|
_reserved1: u32,
|
||||||
|
/// Clear SPI register.
|
||||||
|
clrspi_nsr: u32,
|
||||||
|
_reserved2: u32,
|
||||||
|
/// Set SPI secure register.
|
||||||
|
setspi_sr: u32,
|
||||||
|
_reserved3: u32,
|
||||||
|
/// Clear SPI secure register.
|
||||||
|
clrspi_sr: u32,
|
||||||
|
_reserved4: [u32; 9],
|
||||||
|
/// Interrupt group registers.
|
||||||
|
igroupr: [u32; 32],
|
||||||
|
/// Interrupt set-enable registers.
|
||||||
|
isenabler: [u32; 32],
|
||||||
|
/// Interrupt clear-enable registers.
|
||||||
|
icenabler: [u32; 32],
|
||||||
|
/// Interrupt set-pending registers.
|
||||||
|
ispendr: [u32; 32],
|
||||||
|
/// Interrupt clear-pending registers.
|
||||||
|
icpendr: [u32; 32],
|
||||||
|
/// Interrupt set-active registers.
|
||||||
|
isactiver: [u32; 32],
|
||||||
|
/// Interrupt clear-active registers.
|
||||||
|
icactiver: [u32; 32],
|
||||||
|
/// Interrupt priority registers.
|
||||||
|
ipriorityr: [u8; 1024],
|
||||||
|
/// Interrupt processor targets registers.
|
||||||
|
itargetsr: [u32; 256],
|
||||||
|
/// Interrupt configuration registers.
|
||||||
|
icfgr: [u32; 64],
|
||||||
|
/// Interrupt group modifier registers.
|
||||||
|
igrpmodr: [u32; 32],
|
||||||
|
_reserved5: [u32; 32],
|
||||||
|
/// Non-secure access control registers.
|
||||||
|
nsacr: [u32; 64],
|
||||||
|
/// Software generated interrupt register.
|
||||||
|
sigr: u32,
|
||||||
|
_reserved6: [u32; 3],
|
||||||
|
/// SGI clear-pending registers.
|
||||||
|
cpendsgir: [u32; 4],
|
||||||
|
/// SGI set-pending registers.
|
||||||
|
spendsgir: [u32; 4],
|
||||||
|
_reserved7: [u32; 20],
|
||||||
|
/// Non-maskable interrupt registers.
|
||||||
|
inmir: [u32; 32],
|
||||||
|
/// Interrupt group registers for extended SPI range.
|
||||||
|
igroupr_e: [u32; 32],
|
||||||
|
_reserved8: [u32; 96],
|
||||||
|
/// Interrupt set-enable registers for extended SPI range.
|
||||||
|
isenabler_e: [u32; 32],
|
||||||
|
_reserved9: [u32; 96],
|
||||||
|
/// Interrupt clear-enable registers for extended SPI range.
|
||||||
|
icenabler_e: [u32; 32],
|
||||||
|
_reserved10: [u32; 96],
|
||||||
|
/// Interrupt set-pending registers for extended SPI range.
|
||||||
|
ispendr_e: [u32; 32],
|
||||||
|
_reserved11: [u32; 96],
|
||||||
|
/// Interrupt clear-pending registers for extended SPI range.
|
||||||
|
icpendr_e: [u32; 32],
|
||||||
|
_reserved12: [u32; 96],
|
||||||
|
/// Interrupt set-active registers for extended SPI range.
|
||||||
|
isactive_e: [u32; 32],
|
||||||
|
_reserved13: [u32; 96],
|
||||||
|
/// Interrupt clear-active registers for extended SPI range.
|
||||||
|
icactive_e: [u32; 32],
|
||||||
|
_reserved14: [u32; 224],
|
||||||
|
/// Interrupt priority registers for extended SPI range.
|
||||||
|
ipriorityr_e: [u8; 1024],
|
||||||
|
_reserved15: [u32; 768],
|
||||||
|
/// Extended SPI configuration registers.
|
||||||
|
icfgr_e: [u32; 64],
|
||||||
|
_reserved16: [u32; 192],
|
||||||
|
/// Interrupt group modifier registers for extended SPI range.
|
||||||
|
igrpmodr_e: [u32; 32],
|
||||||
|
_reserved17: [u32; 96],
|
||||||
|
/// Non-secure access control registers for extended SPI range.
|
||||||
|
nsacr_e: [u32; 32],
|
||||||
|
_reserved18: [u32; 288],
|
||||||
|
/// Non-maskable interrupt registers for extended SPI range.
|
||||||
|
inmr_e: [u32; 32],
|
||||||
|
_reserved19: [u32; 2400],
|
||||||
|
/// Interrupt routing registers.
|
||||||
|
irouter: [u32; 1975],
|
||||||
|
_reserved20: [u32; 9],
|
||||||
|
/// Interrupt routing registers for extended SPI range.
|
||||||
|
irouter_e: [u32; 2048],
|
||||||
|
_reserved21: [u32; 2048],
|
||||||
|
/// Implementation defined registers.
|
||||||
|
implementation_defined2: [u32; 4084],
|
||||||
|
/// ID registers.
|
||||||
|
id_registers: [u32; 12],
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
struct Waker: u32 {
|
||||||
|
const CHILDREN_ASLEEP = 1 << 2;
|
||||||
|
const PROCESSOR_SLEEP = 1 << 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GIC Redistributor registers.
|
||||||
|
#[repr(C, align(8))]
|
||||||
|
struct GICR {
|
||||||
|
/// Redistributor control register.
|
||||||
|
ctlr: u32,
|
||||||
|
/// Implementer identification register.
|
||||||
|
iidr: u32,
|
||||||
|
/// Redistributor type register.
|
||||||
|
typer: u64,
|
||||||
|
/// Error reporting status register.
|
||||||
|
statusr: u32,
|
||||||
|
/// Redistributor wake register.
|
||||||
|
waker: Waker,
|
||||||
|
/// Report maximum PARTID and PMG register.
|
||||||
|
mpamidr: u32,
|
||||||
|
/// Set PARTID and PMG register.
|
||||||
|
partidr: u32,
|
||||||
|
/// Implementation defined registers.
|
||||||
|
implementation_defined1: [u32; 8],
|
||||||
|
/// Set LPI pending register.
|
||||||
|
setlprir: u64,
|
||||||
|
/// Clear LPI pending register.
|
||||||
|
clrlpir: u64,
|
||||||
|
_reserved0: [u32; 8],
|
||||||
|
/// Redistributor properties base address register.
|
||||||
|
propbaser: u64,
|
||||||
|
/// Redistributor LPI pending table base address register.
|
||||||
|
pendbaser: u64,
|
||||||
|
_reserved1: [u32; 8],
|
||||||
|
/// Redistributor invalidate LPI register.
|
||||||
|
invlpir: u64,
|
||||||
|
_reserved2: u64,
|
||||||
|
/// Redistributor invalidate all register.
|
||||||
|
invallr: u64,
|
||||||
|
_reserved3: u64,
|
||||||
|
/// Redistributor synchronize register.
|
||||||
|
syncr: u32,
|
||||||
|
_reserved4: [u32; 15],
|
||||||
|
/// Implementation defined registers.
|
||||||
|
implementation_defined2: u64,
|
||||||
|
_reserved5: u64,
|
||||||
|
/// Implementation defined registers.
|
||||||
|
implementation_defined3: u64,
|
||||||
|
_reserved6: [u32; 12218],
|
||||||
|
/// Implementation defined registers.
|
||||||
|
implementation_defined4: [u32; 4084],
|
||||||
|
/// ID registers.
|
||||||
|
id_registers: [u32; 12],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GIC Redistributor SGI and PPI registers.
|
||||||
|
#[repr(C, align(8))]
|
||||||
|
struct SGI {
|
||||||
|
_reserved0: [u32; 32],
|
||||||
|
/// Interrupt group register 0.
|
||||||
|
igroupr0: u32,
|
||||||
|
/// Interrupt group registers for extended PPI range.
|
||||||
|
igroupr_e: [u32; 2],
|
||||||
|
_reserved1: [u32; 29],
|
||||||
|
/// Interrupt set-enable register 0.
|
||||||
|
isenabler0: u32,
|
||||||
|
/// Interrupt set-enable registers for extended PPI range.
|
||||||
|
isenabler_e: [u32; 2],
|
||||||
|
_reserved2: [u32; 29],
|
||||||
|
/// Interrupt clear-enable register 0.
|
||||||
|
icenabler0: u32,
|
||||||
|
/// Interrupt clear-enable registers for extended PPI range.
|
||||||
|
icenabler_e: [u32; 2],
|
||||||
|
_reserved3: [u32; 29],
|
||||||
|
/// Interrupt set-pending register 0.
|
||||||
|
ispendr0: u32,
|
||||||
|
/// Interrupt set-pending registers for extended PPI range.
|
||||||
|
ispendr_e: [u32; 2],
|
||||||
|
_reserved4: [u32; 29],
|
||||||
|
/// Interrupt clear-pending register 0.
|
||||||
|
icpendr0: u32,
|
||||||
|
/// Interrupt clear-pending registers for extended PPI range.
|
||||||
|
icpendr_e: [u32; 2],
|
||||||
|
_reserved5: [u32; 29],
|
||||||
|
/// Interrupt set-active register 0.
|
||||||
|
isactiver0: u32,
|
||||||
|
/// Interrupt set-active registers for extended PPI range.
|
||||||
|
isactive_e: [u32; 2],
|
||||||
|
_reserved6: [u32; 29],
|
||||||
|
/// Interrupt clear-active register 0.
|
||||||
|
icactiver0: u32,
|
||||||
|
/// Interrupt clear-active registers for extended PPI range.
|
||||||
|
icactive_e: [u32; 2],
|
||||||
|
_reserved7: [u32; 29],
|
||||||
|
/// Interrupt priority registers.
|
||||||
|
ipriorityr: [u8; 32],
|
||||||
|
/// Interrupt priority registers for extended PPI range.
|
||||||
|
ipriorityr_e: [u8; 64],
|
||||||
|
_reserved8: [u32; 488],
|
||||||
|
/// SGI configuration register, PPI configuration register and extended PPI
|
||||||
|
/// configuration registers.
|
||||||
|
icfgr: [u32; 6],
|
||||||
|
_reserved9: [u32; 58],
|
||||||
|
/// Interrupt group modifier register 0.
|
||||||
|
igrpmodr0: u32,
|
||||||
|
/// Interrupt group modifier registers for extended PPI range.
|
||||||
|
igrpmodr_e: [u32; 2],
|
||||||
|
_reserved10: [u32; 61],
|
||||||
|
/// Non-secure access control register.
|
||||||
|
nsacr: u32,
|
||||||
|
_reserved11: [u32; 95],
|
||||||
|
/// Non-maskable interrupt register for PPIs.
|
||||||
|
inmir0: u32,
|
||||||
|
/// Non-maskable interrupt register for extended PPIs.
|
||||||
|
inmir_e: [u32; 31],
|
||||||
|
_reserved12: [u32; 11264],
|
||||||
|
/// Implementation defined registers.
|
||||||
|
implementation_defined: [u32; 4084],
|
||||||
|
_reserved13: [u32; 12],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Driver for an Arm Generic Interrupt Controller version 3 (or 4).
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct GicV3 {
|
||||||
|
gicd: *mut GICD,
|
||||||
|
gicr: *mut GICR,
|
||||||
|
sgi: *mut SGI,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GicV3 {
|
||||||
|
/// Constructs a new instance of the driver for a GIC with the given
|
||||||
|
/// distributor and redistributor base addresses.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The given base addresses must point to the GIC distributor and
|
||||||
|
/// redistributor registers respectively. These regions must be mapped into
|
||||||
|
/// the address space of the process as device memory, and not have any
|
||||||
|
/// other aliases, either via another instance of this driver or otherwise.
|
||||||
|
pub unsafe fn new(gicd: *mut u64, gicr: *mut u64) -> Self {
|
||||||
|
Self {
|
||||||
|
gicd: gicd as _,
|
||||||
|
gicr: gicr as _,
|
||||||
|
sgi: gicr.wrapping_add(SGI_OFFSET / size_of::<u64>()) as _,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialises the GIC.
|
||||||
|
pub fn setup(&mut self) {
|
||||||
|
// Safe because writing to this system register doesn't access memory in
|
||||||
|
// any way.
|
||||||
|
unsafe {
|
||||||
|
// Enable system register access.
|
||||||
|
write_sysreg!(icc_sre_el1, 0x01);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safe because we know that `self.gicr` is a valid and unique pointer
|
||||||
|
// to the registers of a GIC redistributor interface.
|
||||||
|
unsafe {
|
||||||
|
// Mark this CPU core as awake, and wait until the GIC wakes up
|
||||||
|
// before continuing.
|
||||||
|
let mut waker = addr_of!((*self.gicr).waker).read_volatile();
|
||||||
|
trace!("WAKER: {:?}", waker);
|
||||||
|
waker -= Waker::PROCESSOR_SLEEP;
|
||||||
|
addr_of_mut!((*self.gicr).waker).write_volatile(waker);
|
||||||
|
|
||||||
|
while addr_of!((*self.gicr).waker)
|
||||||
|
.read_volatile()
|
||||||
|
.contains(Waker::CHILDREN_ASLEEP)
|
||||||
|
{
|
||||||
|
spin_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safe because accessing this system register doesn't access memory in
|
||||||
|
// any way.
|
||||||
|
unsafe {
|
||||||
|
trace!("ICC_CTLR_EL1={:#x}", read_sysreg!(icc_ctlr_el1));
|
||||||
|
// Disable use of `ICC_PMR_EL1` as a hint for interrupt
|
||||||
|
// distribution, configure a write to an EOI register to also
|
||||||
|
// deactivate the interrupt, and configure preemption groups for
|
||||||
|
// group 0 and group 1 interrupts separately.
|
||||||
|
write_sysreg!(icc_ctlr_el1, 0);
|
||||||
|
trace!("ICC_CTLR_EL1={:#x}", read_sysreg!(icc_ctlr_el1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safe because we know that `self.gicd` is a valid and unique pointer
|
||||||
|
// to the registers of a GIC distributor interface.
|
||||||
|
unsafe {
|
||||||
|
// Enable affinity routing and non-secure group 1 interrupts.
|
||||||
|
addr_of_mut!((*self.gicd).ctlr)
|
||||||
|
.write_volatile(GicdCtlr::ARE_S | GicdCtlr::EnableGrp1NS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safe because we know that `self.gicd` is a valid and unique pointer
|
||||||
|
// to the registers of a GIC distributor interface, and `self.sgi` to
|
||||||
|
// the SGI and PPI registers of a GIC redistributor interface.
|
||||||
|
unsafe {
|
||||||
|
// Put all SGIs and PPIs into non-secure group 1.
|
||||||
|
addr_of_mut!((*self.sgi).igroupr0).write_volatile(0xffffffff);
|
||||||
|
// Put all SPIs into non-secure group 1.
|
||||||
|
for i in 0..32 {
|
||||||
|
addr_of_mut!((*self.gicd).igroupr[i]).write_volatile(0xffffffff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safe because writing to this system register doesn't access memory in
|
||||||
|
// any way.
|
||||||
|
unsafe {
|
||||||
|
// Enable non-secure group 1.
|
||||||
|
write_sysreg!(icc_igrpen1_el1, 0x00000001);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables or disables the interrupt with the given ID.
|
||||||
|
pub fn enable_interrupt(&mut self, intid: u32, enable: bool) {
|
||||||
|
let index = (intid / 32) as usize;
|
||||||
|
let bit = 1 << (intid % 32);
|
||||||
|
|
||||||
|
// Safe because we know that `self.gicd` is a valid and unique pointer
|
||||||
|
// to the registers of a GIC distributor interface, and `self.sgi` to
|
||||||
|
// the SGI and PPI registers of a GIC redistributor interface.
|
||||||
|
unsafe {
|
||||||
|
if enable {
|
||||||
|
addr_of_mut!((*self.gicd).isenabler[index]).write_volatile(bit);
|
||||||
|
if intid < SPI_START {
|
||||||
|
addr_of_mut!((*self.sgi).isenabler0).write_volatile(bit);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addr_of_mut!((*self.gicd).icenabler[index]).write_volatile(bit);
|
||||||
|
if intid < SPI_START {
|
||||||
|
addr_of_mut!((*self.sgi).icenabler0).write_volatile(bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables all interrupts.
|
||||||
|
pub fn enable_all_interrupts(&mut self, enable: bool) {
|
||||||
|
for i in 0..32 {
|
||||||
|
// Safe because we know that `self.gicd` is a valid and unique
|
||||||
|
// pointer to the registers of a GIC distributor interface.
|
||||||
|
unsafe {
|
||||||
|
if enable {
|
||||||
|
addr_of_mut!((*self.gicd).isenabler[i]).write_volatile(0xffffffff);
|
||||||
|
} else {
|
||||||
|
addr_of_mut!((*self.gicd).icenabler[i]).write_volatile(0xffffffff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Safe because we know that `self.sgi` is a valid and unique pointer
|
||||||
|
// to the SGI and PPI registers of a GIC redistributor interface.
|
||||||
|
unsafe {
|
||||||
|
if enable {
|
||||||
|
addr_of_mut!((*self.sgi).isenabler0).write_volatile(0xffffffff);
|
||||||
|
} else {
|
||||||
|
addr_of_mut!((*self.sgi).icenabler0).write_volatile(0xffffffff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the priority mask for the current CPU core.
|
||||||
|
///
|
||||||
|
/// Only interrupts with a higher priority (numerically lower) will be
|
||||||
|
/// signalled.
|
||||||
|
pub fn set_priority_mask(min_priority: u8) {
|
||||||
|
// Safe because writing to this system register doesn't access memory in
|
||||||
|
// any way.
|
||||||
|
unsafe {
|
||||||
|
write_sysreg!(icc_pmr_el1, min_priority.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the priority of the interrupt with the given ID.
|
||||||
|
///
|
||||||
|
/// Note that lower numbers correspond to higher priorities; i.e. 0 is the
|
||||||
|
/// highest priority, and 255 is the lowest.
|
||||||
|
pub fn set_interrupt_priority(&mut self, intid: u32, priority: u8) {
|
||||||
|
// Safe because we know that `self.gicd` is a valid and unique pointer
|
||||||
|
// to the registers of a GIC distributor interface, and `self.sgi` to
|
||||||
|
// the SGI and PPI registers of a GIC redistributor interface.
|
||||||
|
unsafe {
|
||||||
|
// Affinity routing is enabled, so use the GICR for SGIs and PPIs.
|
||||||
|
if intid < SPI_START {
|
||||||
|
addr_of_mut!((*self.sgi).ipriorityr[intid as usize])
|
||||||
|
.write_volatile(priority);
|
||||||
|
} else {
|
||||||
|
addr_of_mut!((*self.gicd).ipriorityr[intid as usize])
|
||||||
|
.write_volatile(priority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configures the trigger type for the interrupt with the given ID.
|
||||||
|
pub fn set_trigger(&mut self, intid: u32, trigger: Trigger) {
|
||||||
|
let index = (intid / 16) as usize;
|
||||||
|
let bit = 1 << (((intid % 16) * 2) + 1);
|
||||||
|
|
||||||
|
// Safe because we know that `self.gicd` is a valid and unique pointer
|
||||||
|
// to the registers of a GIC distributor interface, and `self.sgi` to
|
||||||
|
// the SGI and PPI registers of a GIC redistributor interface.
|
||||||
|
unsafe {
|
||||||
|
// Affinity routing is enabled, so use the GICR for SGIs and PPIs.
|
||||||
|
let register = if intid < SPI_START {
|
||||||
|
addr_of_mut!((*self.sgi).icfgr[index])
|
||||||
|
} else {
|
||||||
|
addr_of_mut!((*self.gicd).icfgr[index])
|
||||||
|
};
|
||||||
|
let v = register.read_volatile();
|
||||||
|
register.write_volatile(match trigger {
|
||||||
|
Trigger::Edge => v | bit,
|
||||||
|
Trigger::Level => v & !bit,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the ID of the highest priority signalled interrupt, and
|
||||||
|
/// acknowledges it.
|
||||||
|
pub fn get_and_acknowledge_interrupt() -> u32 {
|
||||||
|
// Safe because reading this system register doesn't access memory in
|
||||||
|
// any way.
|
||||||
|
(unsafe { read_sysreg!(icc_iar1_el1) }) as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Informs the interrupt controller that the CPU has completed processing
|
||||||
|
/// the given interrupt. This drops the interrupt priority and deactivates
|
||||||
|
/// the interrupt.
|
||||||
|
pub fn end_interrupt(intid: u32) {
|
||||||
|
// Safe because writing to this system register doesn't access memory in
|
||||||
|
// any way.
|
||||||
|
unsafe { write_sysreg!(icc_eoir1_el1, intid.into()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The trigger configuration for an interrupt.
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum Trigger {
|
||||||
|
/// The interrupt is edge triggered.
|
||||||
|
Edge,
|
||||||
|
/// The interrupt is level triggered.
|
||||||
|
Level,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Disables debug, SError, IRQ and FIQ exceptions.
|
||||||
|
pub fn irq_disable() {
|
||||||
|
// Safe because writing to this system register doesn't access memory in any
|
||||||
|
// way.
|
||||||
|
unsafe {
|
||||||
|
asm!("msr DAIFSet, #0xf", options(nomem, nostack));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enables debug, SError, IRQ and FIQ exceptions.
|
||||||
|
pub fn irq_enable() {
|
||||||
|
// Safe because writing to this system register doesn't access memory in any
|
||||||
|
// way.
|
||||||
|
unsafe {
|
||||||
|
asm!("msr DAIFClr, #0xf", options(nomem, nostack));
|
||||||
|
}
|
||||||
|
}
|
@ -17,11 +17,13 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
mod exceptions;
|
mod exceptions;
|
||||||
|
mod gicv3;
|
||||||
mod logger;
|
mod logger;
|
||||||
mod pl011;
|
mod pl011;
|
||||||
// ANCHOR_END: top
|
// ANCHOR_END: top
|
||||||
mod pl031;
|
mod pl031;
|
||||||
|
|
||||||
|
use crate::gicv3::GicV3;
|
||||||
use crate::pl031::Rtc;
|
use crate::pl031::Rtc;
|
||||||
use chrono::{TimeZone, Utc};
|
use chrono::{TimeZone, Utc};
|
||||||
use core::hint::spin_loop;
|
use core::hint::spin_loop;
|
||||||
@ -31,6 +33,10 @@ use core::panic::PanicInfo;
|
|||||||
use log::{error, info, trace, LevelFilter};
|
use log::{error, info, trace, LevelFilter};
|
||||||
use psci::system_off;
|
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.
|
/// Base address of the primary PL011 UART.
|
||||||
const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _;
|
const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _;
|
||||||
// ANCHOR_END: imports
|
// ANCHOR_END: imports
|
||||||
@ -49,6 +55,12 @@ extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {
|
|||||||
info!("main({:#x}, {:#x}, {:#x}, {:#x})", x0, x1, x2, x3);
|
info!("main({:#x}, {:#x}, {:#x}, {:#x})", x0, x1, x2, x3);
|
||||||
// ANCHOR_END: main
|
// ANCHOR_END: main
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
// Safe because `PL031_BASE_ADDRESS` is the base address of a PL031 device,
|
// Safe because `PL031_BASE_ADDRESS` is the base address of a PL031 device,
|
||||||
// and nothing else accesses that address range.
|
// and nothing else accesses that address range.
|
||||||
let mut rtc = unsafe { Rtc::new(PL031_BASE_ADDRESS) };
|
let mut rtc = unsafe { Rtc::new(PL031_BASE_ADDRESS) };
|
||||||
|
Loading…
x
Reference in New Issue
Block a user