RTC driver

The QEMU aarch64 virt machine has a PL031 real-time clock at 0x9010000. For this exercise, you should write a driver for it.

  1. Use it to print the current time to the serial console. You can use the chrono crate for 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 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.
    • Use the RTC interrupt, which is wired to the GIC as IntId::spi(2).
    • Once the interrupt is enabled, you can put the core to sleep via arm_gic::wfi(), which will cause the core to sleep until it receives an interrupt.

Download the exercise template and look in the rtc directory for the following files.

src/main.rs:

  1. #![no_main]
  2. #![no_std]
  3. mod exceptions;
  4. mod logger;
  5. mod pl011;
  6. use crate::pl011::Uart;
  7. use arm_gic::gicv3::GicV3;
  8. use core::panic::PanicInfo;
  9. use log::{error, info, trace, LevelFilter};
  10. use smccc::psci::system_off;
  11. use smccc::Hvc;
  12. /// Base addresses of the GICv3.
  13. const GICD_BASE_ADDRESS: *mut u64 = 0x800_0000 as _;
  14. const GICR_BASE_ADDRESS: *mut u64 = 0x80A_0000 as _;
  15. /// Base address of the primary PL011 UART.
  16. const PL011_BASE_ADDRESS: *mut u32 = 0x900_0000 as _;
  17. // SAFETY: There is no other global function of this name.
  18. #[unsafe(no_mangle)]
  19. extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) {
  20.     // SAFETY: `PL011_BASE_ADDRESS` is the base address of a PL011 device, and
  21.     // nothing else accesses that address range.
  22.     let uart = unsafe { Uart::new(PL011_BASE_ADDRESS) };
  23.     logger::init(uart, LevelFilter::Trace).unwrap();
  24.     info!("main({:#x}, {:#x}, {:#x}, {:#x})", x0, x1, x2, x3);
  25.     // SAFETY: `GICD_BASE_ADDRESS` and `GICR_BASE_ADDRESS` are the base
  26.     // addresses of a GICv3 distributor and redistributor respectively, and
  27.     // nothing else accesses those address ranges.
  28.     let mut gic = unsafe { GicV3::new(GICD_BASE_ADDRESS, GICR_BASE_ADDRESS) };
  29.     gic.setup();
  30.     // TODO: Create instance of RTC driver and print current time.
  31.     // TODO: Wait for 3 seconds.
  32.     system_off::<Hvc>().unwrap();
  33. }
  34. #[panic_handler]
  35. fn panic(info: &PanicInfo) -> ! {
  36.     error!("{info}");
  37.     system_off::<Hvc>().unwrap();
  38.     loop {}
  39. }

src/exceptions.rs (you should only need to change this for the 3rd part of the exercise):

  1. #![allow(unused)]
  2. fn main() {
  3. // Copyright 2023 Google LLC
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. //      http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. use arm_gic::gicv3::GicV3;
  17. use log::{error, info, trace};
  18. use smccc::psci::system_off;
  19. use smccc::Hvc;
  20. // SAFETY: There is no other global function of this name.
  21. #[unsafe(no_mangle)]
  22. extern "C" fn sync_exception_current(_elr: u64, _spsr: u64) {
  23.     error!("sync_exception_current");
  24.     system_off::<Hvc>().unwrap();
  25. }
  26. // SAFETY: There is no other global function of this name.
  27. #[unsafe(no_mangle)]
  28. extern "C" fn irq_current(_elr: u64, _spsr: u64) {
  29.     trace!("irq_current");
  30.     let intid =
  31.         GicV3::get_and_acknowledge_interrupt().expect("No pending interrupt");
  32.     info!("IRQ {intid:?}");
  33. }
  34. // SAFETY: There is no other global function of this name.
  35. #[unsafe(no_mangle)]
  36. extern "C" fn fiq_current(_elr: u64, _spsr: u64) {
  37.     error!("fiq_current");
  38.     system_off::<Hvc>().unwrap();
  39. }
  40. // SAFETY: There is no other global function of this name.
  41. #[unsafe(no_mangle)]
  42. extern "C" fn serr_current(_elr: u64, _spsr: u64) {
  43.     error!("serr_current");
  44.     system_off::<Hvc>().unwrap();
  45. }
  46. // SAFETY: There is no other global function of this name.
  47. #[unsafe(no_mangle)]
  48. extern "C" fn sync_lower(_elr: u64, _spsr: u64) {
  49.     error!("sync_lower");
  50.     system_off::<Hvc>().unwrap();
  51. }
  52. // SAFETY: There is no other global function of this name.
  53. #[unsafe(no_mangle)]
  54. extern "C" fn irq_lower(_elr: u64, _spsr: u64) {
  55.     error!("irq_lower");
  56.     system_off::<Hvc>().unwrap();
  57. }
  58. // SAFETY: There is no other global function of this name.
  59. #[unsafe(no_mangle)]
  60. extern "C" fn fiq_lower(_elr: u64, _spsr: u64) {
  61.     error!("fiq_lower");
  62.     system_off::<Hvc>().unwrap();
  63. }
  64. // SAFETY: There is no other global function of this name.
  65. #[unsafe(no_mangle)]
  66. extern "C" fn serr_lower(_elr: u64, _spsr: u64) {
  67.     error!("serr_lower");
  68.     system_off::<Hvc>().unwrap();
  69. }
  70. }

src/logger.rs (you shouldn’t need to change this):

  1. #![allow(unused)]
  2. fn main() {
  3. // Copyright 2023 Google LLC
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. //      http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. // ANCHOR: main
  17. use crate::pl011::Uart;
  18. use core::fmt::Write;
  19. use log::{LevelFilter, Log, Metadata, Record, SetLoggerError};
  20. use spin::mutex::SpinMutex;
  21. static LOGGER: Logger = Logger { uart: SpinMutex::new(None) };
  22. struct Logger {
  23.     uart: SpinMutex<Option<Uart>>,
  24. }
  25. impl Log for Logger {
  26.     fn enabled(&self, _metadata: &Metadata) -> bool {
  27.         true
  28.     }
  29.     fn log(&self, record: &Record) {
  30.         writeln!(
  31.             self.uart.lock().as_mut().unwrap(),
  32.             "[{}] {}",
  33.             record.level(),
  34.             record.args()
  35.         )
  36.         .unwrap();
  37.     }
  38.     fn flush(&self) {}
  39. }
  40. /// Initialises UART logger.
  41. pub fn init(uart: Uart, max_level: LevelFilter) -> Result<(), SetLoggerError> {
  42.     LOGGER.uart.lock().replace(uart);
  43.     log::set_logger(&LOGGER)?;
  44.     log::set_max_level(max_level);
  45.     Ok(())
  46. }
  47. }

src/pl011.rs (you shouldn’t need to change this):

  1. #![allow(unused)]
  2. fn main() {
  3. // Copyright 2023 Google LLC
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. //      http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. #![allow(unused)]
  17. use core::fmt::{self, Write};
  18. // ANCHOR: Flags
  19. use bitflags::bitflags;
  20. bitflags! {
  21.     /// Flags from the UART flag register.
  22.     #[repr(transparent)]
  23.     #[derive(Copy, Clone, Debug, Eq, PartialEq)]
  24.     struct Flags: u16 {
  25.         /// Clear to send.
  26.         const CTS = 1 << 0;
  27.         /// Data set ready.
  28.         const DSR = 1 << 1;
  29.         /// Data carrier detect.
  30.         const DCD = 1 << 2;
  31.         /// UART busy transmitting data.
  32.         const BUSY = 1 << 3;
  33.         /// Receive FIFO is empty.
  34.         const RXFE = 1 << 4;
  35.         /// Transmit FIFO is full.
  36.         const TXFF = 1 << 5;
  37.         /// Receive FIFO is full.
  38.         const RXFF = 1 << 6;
  39.         /// Transmit FIFO is empty.
  40.         const TXFE = 1 << 7;
  41.         /// Ring indicator.
  42.         const RI = 1 << 8;
  43.     }
  44. }
  45. // ANCHOR_END: Flags
  46. bitflags! {
  47.     /// Flags from the UART Receive Status Register / Error Clear Register.
  48.     #[repr(transparent)]
  49.     #[derive(Copy, Clone, Debug, Eq, PartialEq)]
  50.     struct ReceiveStatus: u16 {
  51.         /// Framing error.
  52.         const FE = 1 << 0;
  53.         /// Parity error.
  54.         const PE = 1 << 1;
  55.         /// Break error.
  56.         const BE = 1 << 2;
  57.         /// Overrun error.
  58.         const OE = 1 << 3;
  59.     }
  60. }
  61. // ANCHOR: Registers
  62. #[repr(C, align(4))]
  63. struct Registers {
  64.     dr: u16,
  65.     _reserved0: [u8; 2],
  66.     rsr: ReceiveStatus,
  67.     _reserved1: [u8; 19],
  68.     fr: Flags,
  69.     _reserved2: [u8; 6],
  70.     ilpr: u8,
  71.     _reserved3: [u8; 3],
  72.     ibrd: u16,
  73.     _reserved4: [u8; 2],
  74.     fbrd: u8,
  75.     _reserved5: [u8; 3],
  76.     lcr_h: u8,
  77.     _reserved6: [u8; 3],
  78.     cr: u16,
  79.     _reserved7: [u8; 3],
  80.     ifls: u8,
  81.     _reserved8: [u8; 3],
  82.     imsc: u16,
  83.     _reserved9: [u8; 2],
  84.     ris: u16,
  85.     _reserved10: [u8; 2],
  86.     mis: u16,
  87.     _reserved11: [u8; 2],
  88.     icr: u16,
  89.     _reserved12: [u8; 2],
  90.     dmacr: u8,
  91.     _reserved13: [u8; 3],
  92. }
  93. // ANCHOR_END: Registers
  94. // ANCHOR: Uart
  95. /// Driver for a PL011 UART.
  96. #[derive(Debug)]
  97. pub struct Uart {
  98.     registers: *mut Registers,
  99. }
  100. impl Uart {
  101.     /// Constructs a new instance of the UART driver for a PL011 device at the
  102.     /// given base address.
  103.     ///
  104.     /// # Safety
  105.     ///
  106.     /// The given base address must point to the MMIO control registers of a
  107.     /// PL011 device, which must be mapped into the address space of the process
  108.     /// as device memory and not have any other aliases.
  109.     pub unsafe fn new(base_address: *mut u32) -> Self {
  110.         Self { registers: base_address as *mut Registers }
  111.     }
  112.     /// Writes a single byte to the UART.
  113.     pub fn write_byte(&self, byte: u8) {
  114.         // Wait until there is room in the TX buffer.
  115.         while self.read_flag_register().contains(Flags::TXFF) {}
  116.         // SAFETY: We know that self.registers points to the control registers
  117.         // of a PL011 device which is appropriately mapped.
  118.         unsafe {
  119.             // Write to the TX buffer.
  120.             (&raw mut (*self.registers).dr).write_volatile(byte.into());
  121.         }
  122.         // Wait until the UART is no longer busy.
  123.         while self.read_flag_register().contains(Flags::BUSY) {}
  124.     }
  125.     /// Reads and returns a pending byte, or `None` if nothing has been
  126.     /// received.
  127.     pub fn read_byte(&self) -> Option<u8> {
  128.         if self.read_flag_register().contains(Flags::RXFE) {
  129.             None
  130.         } else {
  131.             // SAFETY: We know that self.registers points to the control
  132.             // registers of a PL011 device which is appropriately mapped.
  133.             let data = unsafe { (&raw const (*self.registers).dr).read_volatile() };
  134.             // TODO: Check for error conditions in bits 8-11.
  135.             Some(data as u8)
  136.         }
  137.     }
  138.     fn read_flag_register(&self) -> Flags {
  139.         // SAFETY: We know that self.registers points to the control registers
  140.         // of a PL011 device which is appropriately mapped.
  141.         unsafe { (&raw const (*self.registers).fr).read_volatile() }
  142.     }
  143. }
  144. // ANCHOR_END: Uart
  145. impl Write for Uart {
  146.     fn write_str(&mut self, s: &str) -> fmt::Result {
  147.         for c in s.as_bytes() {
  148.             self.write_byte(*c);
  149.         }
  150.         Ok(())
  151.     }
  152. }
  153. // Safe because it just contains a pointer to device memory, which can be
  154. // accessed from any context.
  155. unsafe impl Send for Uart {}
  156. }

Cargo.toml (you shouldn’t need to change this):

  1. [workspace]
  2. [package]
  3. name = "rtc"
  4. version = "0.1.0"
  5. edition = "2021"
  6. publish = false
  7. [dependencies]
  8. arm-gic = "0.1.1"
  9. bitflags = "2.6.0"
  10. chrono = { version = "0.4.38", default-features = false }
  11. log = "0.4.22"
  12. smccc = "0.1.1"
  13. spin = "0.9.8"
  14. [build-dependencies]
  15. cc = "1.1.31"

build.rs (you shouldn’t need to change this):

  1. // Copyright 2023 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. //      http://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. use cc::Build;
  15. use std::env;
  16. fn main() {
  17.     env::set_var("CROSS_COMPILE", "aarch64-none-elf");
  18.     env::set_var("CC", "clang");
  19.     Build::new()
  20.         .file("entry.S")
  21.         .file("exceptions.S")
  22.         .file("idmap.S")
  23.         .compile("empty")
  24. }

entry.S (you shouldn’t need to change this):

  1. /*
  2. * Copyright 2023 Google LLC
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * https://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. .macro adr_l, reg:req, sym:req
  17. adrp \reg, \sym
  18. add \reg, \reg, :lo12:\sym
  19. .endm
  20. .macro mov_i, reg:req, imm:req
  21. movz \reg, :abs_g3:\imm
  22. movk \reg, :abs_g2_nc:\imm
  23. movk \reg, :abs_g1_nc:\imm
  24. movk \reg, :abs_g0_nc:\imm
  25. .endm
  26. .set .L_MAIR_DEV_nGnRE, 0x04
  27. .set .L_MAIR_MEM_WBWA, 0xff
  28. .set .Lmairval, .L_MAIR_DEV_nGnRE | (.L_MAIR_MEM_WBWA << 8)
  29. /* 4 KiB granule size for TTBR0_EL1. */
  30. .set .L_TCR_TG0_4KB, 0x0 << 14
  31. /* 4 KiB granule size for TTBR1_EL1. */
  32. .set .L_TCR_TG1_4KB, 0x2 << 30
  33. /* Disable translation table walk for TTBR1_EL1, generating a translation fault instead. */
  34. .set .L_TCR_EPD1, 0x1 << 23
  35. /* Translation table walks for TTBR0_EL1 are inner sharable. */
  36. .set .L_TCR_SH_INNER, 0x3 << 12
  37. /*
  38. * Translation table walks for TTBR0_EL1 are outer write-back read-allocate write-allocate
  39. * cacheable.
  40. */
  41. .set .L_TCR_RGN_OWB, 0x1 << 10
  42. /*
  43. * Translation table walks for TTBR0_EL1 are inner write-back read-allocate write-allocate
  44. * cacheable.
  45. */
  46. .set .L_TCR_RGN_IWB, 0x1 << 8
  47. /* Size offset for TTBR0_EL1 is 2**39 bytes (512 GiB). */
  48. .set .L_TCR_T0SZ_512, 64 - 39
  49. .set .Ltcrval, .L_TCR_TG0_4KB | .L_TCR_TG1_4KB | .L_TCR_EPD1 | .L_TCR_RGN_OWB
  50. .set .Ltcrval, .Ltcrval | .L_TCR_RGN_IWB | .L_TCR_SH_INNER | .L_TCR_T0SZ_512
  51. /* Stage 1 instruction access cacheability is unaffected. */
  52. .set .L_SCTLR_ELx_I, 0x1 << 12
  53. /* SP alignment fault if SP is not aligned to a 16 byte boundary. */
  54. .set .L_SCTLR_ELx_SA, 0x1 << 3
  55. /* Stage 1 data access cacheability is unaffected. */
  56. .set .L_SCTLR_ELx_C, 0x1 << 2
  57. /* EL0 and EL1 stage 1 MMU enabled. */
  58. .set .L_SCTLR_ELx_M, 0x1 << 0
  59. /* Privileged Access Never is unchanged on taking an exception to EL1. */
  60. .set .L_SCTLR_EL1_SPAN, 0x1 << 23
  61. /* SETEND instruction disabled at EL0 in aarch32 mode. */
  62. .set .L_SCTLR_EL1_SED, 0x1 << 8
  63. /* Various IT instructions are disabled at EL0 in aarch32 mode. */
  64. .set .L_SCTLR_EL1_ITD, 0x1 << 7
  65. .set .L_SCTLR_EL1_RES1, (0x1 << 11) | (0x1 << 20) | (0x1 << 22) | (0x1 << 28) | (0x1 << 29)
  66. .set .Lsctlrval, .L_SCTLR_ELx_M | .L_SCTLR_ELx_C | .L_SCTLR_ELx_SA | .L_SCTLR_EL1_ITD | .L_SCTLR_EL1_SED
  67. .set .Lsctlrval, .Lsctlrval | .L_SCTLR_ELx_I | .L_SCTLR_EL1_SPAN | .L_SCTLR_EL1_RES1
  68. /**
  69. * This is a generic entry point for an image. It carries out the operations required to prepare the
  70. * loaded image to be run. Specifically, it zeroes the bss section using registers x25 and above,
  71. * prepares the stack, enables floating point, and sets up the exception vector. It preserves x0-x3
  72. * for the Rust entry point, as these may contain boot parameters.
  73. */
  74. .section .init.entry, "ax"
  75. .global entry
  76. entry:
  77. /* Load and apply the memory management configuration, ready to enable MMU and caches. */
  78. adrp x30, idmap
  79. msr ttbr0_el1, x30
  80. mov_i x30, .Lmairval
  81. msr mair_el1, x30
  82. mov_i x30, .Ltcrval
  83. /* Copy the supported PA range into TCR_EL1.IPS. */
  84. mrs x29, id_aa64mmfr0_el1
  85. bfi x30, x29, #32, #4
  86. msr tcr_el1, x30
  87. mov_i x30, .Lsctlrval
  88. /*
  89. * Ensure everything before this point has completed, then invalidate any potentially stale
  90. * local TLB entries before they start being used.
  91. */
  92. isb
  93. tlbi vmalle1
  94. ic iallu
  95. dsb nsh
  96. isb
  97. /*
  98. * Configure sctlr_el1 to enable MMU and cache and don't proceed until this has completed.
  99. */
  100. msr sctlr_el1, x30
  101. isb
  102. /* Disable trapping floating point access in EL1. */
  103. mrs x30, cpacr_el1
  104. orr x30, x30, #(0x3 << 20)
  105. msr cpacr_el1, x30
  106. isb
  107. /* Zero out the bss section. */
  108. adr_l x29, bss_begin
  109. adr_l x30, bss_end
  110. 0: cmp x29, x30
  111. b.hs 1f
  112. stp xzr, xzr, [x29], #16
  113. b 0b
  114. 1: /* Prepare the stack. */
  115. adr_l x30, boot_stack_end
  116. mov sp, x30
  117. /* Set up exception vector. */
  118. adr x30, vector_table_el1
  119. msr vbar_el1, x30
  120. /* Call into Rust code. */
  121. bl main
  122. /* Loop forever waiting for interrupts. */
  123. 2: wfi
  124. b 2b

exceptions.S (you shouldn’t need to change this):

  1. /*
  2. * Copyright 2023 Google LLC
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * https://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /**
  17. * Saves the volatile registers onto the stack. This currently takes 14
  18. * instructions, so it can be used in exception handlers with 18 instructions
  19. * left.
  20. *
  21. * On return, x0 and x1 are initialised to elr_el2 and spsr_el2 respectively,
  22. * which can be used as the first and second arguments of a subsequent call.
  23. */
  24. .macro save_volatile_to_stack
  25. /* Reserve stack space and save registers x0-x18, x29 & x30. */
  26. stp x0, x1, [sp, #-(8 * 24)]!
  27. stp x2, x3, [sp, #8 * 2]
  28. stp x4, x5, [sp, #8 * 4]
  29. stp x6, x7, [sp, #8 * 6]
  30. stp x8, x9, [sp, #8 * 8]
  31. stp x10, x11, [sp, #8 * 10]
  32. stp x12, x13, [sp, #8 * 12]
  33. stp x14, x15, [sp, #8 * 14]
  34. stp x16, x17, [sp, #8 * 16]
  35. str x18, [sp, #8 * 18]
  36. stp x29, x30, [sp, #8 * 20]
  37. /*
  38. * Save elr_el1 & spsr_el1. This such that we can take nested exception
  39. * and still be able to unwind.
  40. */
  41. mrs x0, elr_el1
  42. mrs x1, spsr_el1
  43. stp x0, x1, [sp, #8 * 22]
  44. .endm
  45. /**
  46. * Restores the volatile registers from the stack. This currently takes 14
  47. * instructions, so it can be used in exception handlers while still leaving 18
  48. * instructions left; if paired with save_volatile_to_stack, there are 4
  49. * instructions to spare.
  50. */
  51. .macro restore_volatile_from_stack
  52. /* Restore registers x2-x18, x29 & x30. */
  53. ldp x2, x3, [sp, #8 * 2]
  54. ldp x4, x5, [sp, #8 * 4]
  55. ldp x6, x7, [sp, #8 * 6]
  56. ldp x8, x9, [sp, #8 * 8]
  57. ldp x10, x11, [sp, #8 * 10]
  58. ldp x12, x13, [sp, #8 * 12]
  59. ldp x14, x15, [sp, #8 * 14]
  60. ldp x16, x17, [sp, #8 * 16]
  61. ldr x18, [sp, #8 * 18]
  62. ldp x29, x30, [sp, #8 * 20]
  63. /* Restore registers elr_el1 & spsr_el1, using x0 & x1 as scratch. */
  64. ldp x0, x1, [sp, #8 * 22]
  65. msr elr_el1, x0
  66. msr spsr_el1, x1
  67. /* Restore x0 & x1, and release stack space. */
  68. ldp x0, x1, [sp], #8 * 24
  69. .endm
  70. /**
  71. * This is a generic handler for exceptions taken at the current EL while using
  72. * SP0. It behaves similarly to the SPx case by first switching to SPx, doing
  73. * the work, then switching back to SP0 before returning.
  74. *
  75. * Switching to SPx and calling the Rust handler takes 16 instructions. To
  76. * restore and return we need an additional 16 instructions, so we can implement
  77. * the whole handler within the allotted 32 instructions.
  78. */
  79. .macro current_exception_sp0 handler:req
  80. msr spsel, #1
  81. save_volatile_to_stack
  82. bl \handler
  83. restore_volatile_from_stack
  84. msr spsel, #0
  85. eret
  86. .endm
  87. /**
  88. * This is a generic handler for exceptions taken at the current EL while using
  89. * SPx. It saves volatile registers, calls the Rust handler, restores volatile
  90. * registers, then returns.
  91. *
  92. * This also works for exceptions taken from EL0, if we don't care about
  93. * non-volatile registers.
  94. *
  95. * Saving state and jumping to the Rust handler takes 15 instructions, and
  96. * restoring and returning also takes 15 instructions, so we can fit the whole
  97. * handler in 30 instructions, under the limit of 32.
  98. */
  99. .macro current_exception_spx handler:req
  100. save_volatile_to_stack
  101. bl \handler
  102. restore_volatile_from_stack
  103. eret
  104. .endm
  105. .section .text.vector_table_el1, "ax"
  106. .global vector_table_el1
  107. .balign 0x800
  108. vector_table_el1:
  109. sync_cur_sp0:
  110. current_exception_sp0 sync_exception_current
  111. .balign 0x80
  112. irq_cur_sp0:
  113. current_exception_sp0 irq_current
  114. .balign 0x80
  115. fiq_cur_sp0:
  116. current_exception_sp0 fiq_current
  117. .balign 0x80
  118. serr_cur_sp0:
  119. current_exception_sp0 serr_current
  120. .balign 0x80
  121. sync_cur_spx:
  122. current_exception_spx sync_exception_current
  123. .balign 0x80
  124. irq_cur_spx:
  125. current_exception_spx irq_current
  126. .balign 0x80
  127. fiq_cur_spx:
  128. current_exception_spx fiq_current
  129. .balign 0x80
  130. serr_cur_spx:
  131. current_exception_spx serr_current
  132. .balign 0x80
  133. sync_lower_64:
  134. current_exception_spx sync_lower
  135. .balign 0x80
  136. irq_lower_64:
  137. current_exception_spx irq_lower
  138. .balign 0x80
  139. fiq_lower_64:
  140. current_exception_spx fiq_lower
  141. .balign 0x80
  142. serr_lower_64:
  143. current_exception_spx serr_lower
  144. .balign 0x80
  145. sync_lower_32:
  146. current_exception_spx sync_lower
  147. .balign 0x80
  148. irq_lower_32:
  149. current_exception_spx irq_lower
  150. .balign 0x80
  151. fiq_lower_32:
  152. current_exception_spx fiq_lower
  153. .balign 0x80
  154. serr_lower_32:
  155. current_exception_spx serr_lower

idmap.S (you shouldn’t need to change this):

  1. /*
  2. * Copyright 2023 Google LLC
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * https://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. .set .L_TT_TYPE_BLOCK, 0x1
  17. .set .L_TT_TYPE_PAGE, 0x3
  18. .set .L_TT_TYPE_TABLE, 0x3
  19. /* Access flag. */
  20. .set .L_TT_AF, 0x1 << 10
  21. /* Not global. */
  22. .set .L_TT_NG, 0x1 << 11
  23. .set .L_TT_XN, 0x3 << 53
  24. .set .L_TT_MT_DEV, 0x0 << 2 // MAIR #0 (DEV_nGnRE)
  25. .set .L_TT_MT_MEM, (0x1 << 2) | (0x3 << 8) // MAIR #1 (MEM_WBWA), inner shareable
  26. .set .L_BLOCK_DEV, .L_TT_TYPE_BLOCK | .L_TT_MT_DEV | .L_TT_AF | .L_TT_XN
  27. .set .L_BLOCK_MEM, .L_TT_TYPE_BLOCK | .L_TT_MT_MEM | .L_TT_AF | .L_TT_NG
  28. .section ".rodata.idmap", "a", %progbits
  29. .global idmap
  30. .align 12
  31. idmap:
  32. /* level 1 */
  33. .quad .L_BLOCK_DEV | 0x0 // 1 GiB of device mappings
  34. .quad .L_BLOCK_MEM | 0x40000000 // 1 GiB of DRAM
  35. .fill 254, 8, 0x0 // 254 GiB of unmapped VA space
  36. .quad .L_BLOCK_DEV | 0x4000000000 // 1 GiB of device mappings
  37. .fill 255, 8, 0x0 // 255 GiB of remaining VA space

image.ld (you shouldn’t need to change this):

  1. /*
  2. * Copyright 2023 Google LLC
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * https://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /*
  17. * Code will start running at this symbol which is placed at the start of the
  18. * image.
  19. */
  20. ENTRY(entry)
  21. MEMORY
  22. {
  23. image : ORIGIN = 0x40080000, LENGTH = 2M
  24. }
  25. SECTIONS
  26. {
  27. /*
  28. * Collect together the code.
  29. */
  30. .init : ALIGN(4096) {
  31. text_begin = .;
  32. *(.init.entry)
  33. *(.init.*)
  34. } >image
  35. .text : {
  36. *(.text.*)
  37. } >image
  38. text_end = .;
  39. /*
  40. * Collect together read-only data.
  41. */
  42. .rodata : ALIGN(4096) {
  43. rodata_begin = .;
  44. *(.rodata.*)
  45. } >image
  46. .got : {
  47. *(.got)
  48. } >image
  49. rodata_end = .;
  50. /*
  51. * Collect together the read-write data including .bss at the end which
  52. * will be zero'd by the entry code.
  53. */
  54. .data : ALIGN(4096) {
  55. data_begin = .;
  56. *(.data.*)
  57. /*
  58. * The entry point code assumes that .data is a multiple of 32
  59. * bytes long.
  60. */
  61. . = ALIGN(32);
  62. data_end = .;
  63. } >image
  64. /* Everything beyond this point will not be included in the binary. */
  65. bin_end = .;
  66. /* The entry point code assumes that .bss is 16-byte aligned. */
  67. .bss : ALIGN(16) {
  68. bss_begin = .;
  69. *(.bss.*)
  70. *(COMMON)
  71. . = ALIGN(16);
  72. bss_end = .;
  73. } >image
  74. .stack (NOLOAD) : ALIGN(4096) {
  75. boot_stack_begin = .;
  76. . += 40 * 4096;
  77. . = ALIGN(4096);
  78. boot_stack_end = .;
  79. } >image
  80. . = ALIGN(4K);
  81. PROVIDE(dma_region = .);
  82. /*
  83. * Remove unused sections from the image.
  84. */
  85. /DISCARD/ : {
  86. /* The image loads itself so doesn't need these sections. */
  87. *(.gnu.hash)
  88. *(.hash)
  89. *(.interp)
  90. *(.eh_frame_hdr)
  91. *(.eh_frame)
  92. *(.note.gnu.build-id)
  93. }
  94. }

Makefile (you shouldn’t need to change this):

  1. # Copyright 2023 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. # http://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. .PHONY: build qemu_minimal qemu qemu_logger
  15. all: rtc.bin
  16. build:
  17. cargo build
  18. rtc.bin: build
  19. cargo objcopy -- -O binary $@
  20. qemu: rtc.bin
  21. qemu-system-aarch64 -machine virt,gic-version=3 -cpu max -serial mon:stdio -display none -kernel $< -s
  22. clean:
  23. cargo clean
  24. rm -f *.bin

.cargo/config.toml (you shouldn’t need to change this):

  1. [build]
  2. target = "aarch64-unknown-none"
  3. rustflags = ["-C", "link-arg=-Timage.ld"]

Run the code in QEMU with make qemu.