RTC 驅動程式

QEMU aarch64 虛擬機器的 PL031 即時時鐘位於 0x9010000。在這個練習中,您應為該時鐘編寫驅動程式。

  1. 使用該時鐘將目前時間顯示至序列控制台。您可以使用 chrono Crate 設定日期/時間格式。
  2. 使用比對暫存器和原始中斷狀態,忙碌等待至指定時間,例如未來 3 秒 (呼叫迴圈中的 core::hint::spin_loop)。
  3. 擴充功能 (如有時間):啟用並處理因 RTC 比對而產生的中斷情形。您可以使用 arm-gic Crate 中提供的驅動程式,設定 Arm 泛型中斷控制器。
    • 使用做為 IntId::spi(2) 有線連結至 GIC 的 RTC 中斷。
    • 啟用中斷功能後,您可以透過 arm_gic::wfi() 將核心設為休眠,這樣核心就會進入休眠狀態,直到遭中斷為止。

請下載練習範本,並在 rtc 目錄中查看下列檔案。

src/main.rs:

#![no_main] #![no_std] mod exceptions; mod logger; mod pl011; use crate::pl011::Uart; use arm_gic::gicv3::GicV3; use core::panic::PanicInfo; use log::{error, info, trace, LevelFilter}; use smccc::psci::system_off; use smccc::Hvc; /// 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 _; #[no_mangle] extern "C" fn main(x0: u64, x1: u64, x2: u64, x3: u64) { // Safe because `PL011_BASE_ADDRESS` is the base address of a PL011 device, // and nothing else accesses that address range. let uart = unsafe { Uart::new(PL011_BASE_ADDRESS) }; 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(); // TODO: Create instance of RTC driver and print current time. // TODO: Wait for 3 seconds. system_off::<Hvc>().unwrap(); } #[panic_handler] fn panic(info: &PanicInfo) -> ! { error!("{info}"); system_off::<Hvc>().unwrap(); loop {} }

src/exceptions.rs (您應該只需為練習的第 3 部分變更此項目):

#![allow(unused)] fn main() { // 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 arm_gic::gicv3::GicV3; use log::{error, info, trace}; use smccc::psci::system_off; use smccc::Hvc; #[no_mangle] extern "C" fn sync_exception_current(_elr: u64, _spsr: u64) { error!("sync_exception_current"); system_off::<Hvc>().unwrap(); } #[no_mangle] extern "C" fn irq_current(_elr: u64, _spsr: u64) { trace!("irq_current"); let intid = GicV3::get_and_acknowledge_interrupt().expect("No pending interrupt"); info!("IRQ {intid:?}"); } #[no_mangle] extern "C" fn fiq_current(_elr: u64, _spsr: u64) { error!("fiq_current"); system_off::<Hvc>().unwrap(); } #[no_mangle] extern "C" fn serr_current(_elr: u64, _spsr: u64) { error!("serr_current"); system_off::<Hvc>().unwrap(); } #[no_mangle] extern "C" fn sync_lower(_elr: u64, _spsr: u64) { error!("sync_lower"); system_off::<Hvc>().unwrap(); } #[no_mangle] extern "C" fn irq_lower(_elr: u64, _spsr: u64) { error!("irq_lower"); system_off::<Hvc>().unwrap(); } #[no_mangle] extern "C" fn fiq_lower(_elr: u64, _spsr: u64) { error!("fiq_lower"); system_off::<Hvc>().unwrap(); } #[no_mangle] extern "C" fn serr_lower(_elr: u64, _spsr: u64) { error!("serr_lower"); system_off::<Hvc>().unwrap(); } }

src/logger.rs (您應該不需要變更此項目):

#![allow(unused)] fn main() { // 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. // ANCHOR: main use crate::pl011::Uart; use core::fmt::Write; use log::{LevelFilter, Log, Metadata, Record, SetLoggerError}; use spin::mutex::SpinMutex; static LOGGER: Logger = Logger { uart: SpinMutex::new(None) }; struct Logger { uart: SpinMutex<Option<Uart>>, } impl Log for Logger { fn enabled(&self, _metadata: &Metadata) -> bool { true } fn log(&self, record: &Record) { writeln!( self.uart.lock().as_mut().unwrap(), "[{}] {}", record.level(), record.args() ) .unwrap(); } fn flush(&self) {} } /// Initialises UART logger. pub fn init(uart: Uart, max_level: LevelFilter) -> Result<(), SetLoggerError> { LOGGER.uart.lock().replace(uart); log::set_logger(&LOGGER)?; log::set_max_level(max_level); Ok(()) } }

src/pl011.rs (您應該不需要變更此項目):

#![allow(unused)] fn main() { // 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. #![allow(unused)] use core::fmt::{self, Write}; use core::ptr::{addr_of, addr_of_mut}; // ANCHOR: Flags use bitflags::bitflags; bitflags! { /// Flags from the UART flag register. #[repr(transparent)] #[derive(Copy, Clone, Debug, Eq, PartialEq)] struct Flags: u16 { /// Clear to send. const CTS = 1 << 0; /// Data set ready. const DSR = 1 << 1; /// Data carrier detect. const DCD = 1 << 2; /// UART busy transmitting data. const BUSY = 1 << 3; /// Receive FIFO is empty. const RXFE = 1 << 4; /// Transmit FIFO is full. const TXFF = 1 << 5; /// Receive FIFO is full. const RXFF = 1 << 6; /// Transmit FIFO is empty. const TXFE = 1 << 7; /// Ring indicator. const RI = 1 << 8; } } // ANCHOR_END: Flags bitflags! { /// Flags from the UART Receive Status Register / Error Clear Register. #[repr(transparent)] #[derive(Copy, Clone, Debug, Eq, PartialEq)] struct ReceiveStatus: u16 { /// Framing error. const FE = 1 << 0; /// Parity error. const PE = 1 << 1; /// Break error. const BE = 1 << 2; /// Overrun error. const OE = 1 << 3; } } // ANCHOR: Registers #[repr(C, align(4))] struct Registers { dr: u16, _reserved0: [u8; 2], rsr: ReceiveStatus, _reserved1: [u8; 19], fr: Flags, _reserved2: [u8; 6], ilpr: u8, _reserved3: [u8; 3], ibrd: u16, _reserved4: [u8; 2], fbrd: u8, _reserved5: [u8; 3], lcr_h: u8, _reserved6: [u8; 3], cr: u16, _reserved7: [u8; 3], ifls: u8, _reserved8: [u8; 3], imsc: u16, _reserved9: [u8; 2], ris: u16, _reserved10: [u8; 2], mis: u16, _reserved11: [u8; 2], icr: u16, _reserved12: [u8; 2], dmacr: u8, _reserved13: [u8; 3], } // ANCHOR_END: Registers // ANCHOR: Uart /// Driver for a PL011 UART. #[derive(Debug)] pub struct Uart { registers: *mut Registers, } impl Uart { /// 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 unsafe fn new(base_address: *mut u32) -> Self { Self { registers: base_address as *mut Registers } } /// Writes a single byte to the UART. pub fn write_byte(&self, byte: u8) { // Wait until there is room in the TX buffer. while self.read_flag_register().contains(Flags::TXFF) {} // Safe because we know that self.registers points to the control // registers of a PL011 device which is appropriately mapped. unsafe { // Write to the TX buffer. addr_of_mut!((*self.registers).dr).write_volatile(byte.into()); } // Wait until the UART is no longer busy. while self.read_flag_register().contains(Flags::BUSY) {} } /// Reads and returns a pending byte, or `None` if nothing has been /// received. pub fn read_byte(&self) -> Option<u8> { if self.read_flag_register().contains(Flags::RXFE) { None } else { let data = unsafe { addr_of!((*self.registers).dr).read_volatile() }; // TODO: Check for error conditions in bits 8-11. Some(data as u8) } } fn read_flag_register(&self) -> Flags { // Safe because we know that self.registers points to the control // registers of a PL011 device which is appropriately mapped. unsafe { addr_of!((*self.registers).fr).read_volatile() } } } // ANCHOR_END: Uart impl Write for Uart { fn write_str(&mut self, s: &str) -> fmt::Result { for c in s.as_bytes() { self.write_byte(*c); } Ok(()) } } // Safe because it just contains a pointer to device memory, which can be // accessed from any context. unsafe impl Send for Uart {} }

Cargo.toml (您應該不需要變更此項目):

[workspace] [package] name = "rtc" version = "0.1.0" edition = "2021" publish = false [dependencies] arm-gic = "0.1.0" bitflags = "2.4.2" chrono = { version = "0.4.34", default-features = false } log = "0.4.21" smccc = "0.1.1" spin = "0.9.8" [build-dependencies] cc = "1.0.88"

build.rs (您應該不需要變更此項目):

// 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 cc::Build; use std::env; fn main() { #[cfg(target_os = "linux")] env::set_var("CROSS_COMPILE", "aarch64-linux-gnu"); #[cfg(not(target_os = "linux"))] env::set_var("CROSS_COMPILE", "aarch64-none-elf"); Build::new() .file("entry.S") .file("exceptions.S") .file("idmap.S") .compile("empty") }

entry.S (您應該不需要變更此項目):

/* * 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 * * https://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. */ .macro adr_l, reg:req, sym:req adrp \reg, \sym add \reg, \reg, :lo12:\sym .endm .macro mov_i, reg:req, imm:req movz \reg, :abs_g3:\imm movk \reg, :abs_g2_nc:\imm movk \reg, :abs_g1_nc:\imm movk \reg, :abs_g0_nc:\imm .endm .set .L_MAIR_DEV_nGnRE, 0x04 .set .L_MAIR_MEM_WBWA, 0xff .set .Lmairval, .L_MAIR_DEV_nGnRE | (.L_MAIR_MEM_WBWA << 8) /* 4 KiB granule size for TTBR0_EL1. */ .set .L_TCR_TG0_4KB, 0x0 << 14 /* 4 KiB granule size for TTBR1_EL1. */ .set .L_TCR_TG1_4KB, 0x2 << 30 /* Disable translation table walk for TTBR1_EL1, generating a translation fault instead. */ .set .L_TCR_EPD1, 0x1 << 23 /* Translation table walks for TTBR0_EL1 are inner sharable. */ .set .L_TCR_SH_INNER, 0x3 << 12 /* * Translation table walks for TTBR0_EL1 are outer write-back read-allocate write-allocate * cacheable. */ .set .L_TCR_RGN_OWB, 0x1 << 10 /* * Translation table walks for TTBR0_EL1 are inner write-back read-allocate write-allocate * cacheable. */ .set .L_TCR_RGN_IWB, 0x1 << 8 /* Size offset for TTBR0_EL1 is 2**39 bytes (512 GiB). */ .set .L_TCR_T0SZ_512, 64 - 39 .set .Ltcrval, .L_TCR_TG0_4KB | .L_TCR_TG1_4KB | .L_TCR_EPD1 | .L_TCR_RGN_OWB .set .Ltcrval, .Ltcrval | .L_TCR_RGN_IWB | .L_TCR_SH_INNER | .L_TCR_T0SZ_512 /* Stage 1 instruction access cacheability is unaffected. */ .set .L_SCTLR_ELx_I, 0x1 << 12 /* SP alignment fault if SP is not aligned to a 16 byte boundary. */ .set .L_SCTLR_ELx_SA, 0x1 << 3 /* Stage 1 data access cacheability is unaffected. */ .set .L_SCTLR_ELx_C, 0x1 << 2 /* EL0 and EL1 stage 1 MMU enabled. */ .set .L_SCTLR_ELx_M, 0x1 << 0 /* Privileged Access Never is unchanged on taking an exception to EL1. */ .set .L_SCTLR_EL1_SPAN, 0x1 << 23 /* SETEND instruction disabled at EL0 in aarch32 mode. */ .set .L_SCTLR_EL1_SED, 0x1 << 8 /* Various IT instructions are disabled at EL0 in aarch32 mode. */ .set .L_SCTLR_EL1_ITD, 0x1 << 7 .set .L_SCTLR_EL1_RES1, (0x1 << 11) | (0x1 << 20) | (0x1 << 22) | (0x1 << 28) | (0x1 << 29) .set .Lsctlrval, .L_SCTLR_ELx_M | .L_SCTLR_ELx_C | .L_SCTLR_ELx_SA | .L_SCTLR_EL1_ITD | .L_SCTLR_EL1_SED .set .Lsctlrval, .Lsctlrval | .L_SCTLR_ELx_I | .L_SCTLR_EL1_SPAN | .L_SCTLR_EL1_RES1 /** * This is a generic entry point for an image. It carries out the operations required to prepare the * loaded image to be run. Specifically, it zeroes the bss section using registers x25 and above, * prepares the stack, enables floating point, and sets up the exception vector. It preserves x0-x3 * for the Rust entry point, as these may contain boot parameters. */ .section .init.entry, "ax" .global entry entry: /* Load and apply the memory management configuration, ready to enable MMU and caches. */ adrp x30, idmap msr ttbr0_el1, x30 mov_i x30, .Lmairval msr mair_el1, x30 mov_i x30, .Ltcrval /* Copy the supported PA range into TCR_EL1.IPS. */ mrs x29, id_aa64mmfr0_el1 bfi x30, x29, #32, #4 msr tcr_el1, x30 mov_i x30, .Lsctlrval /* * Ensure everything before this point has completed, then invalidate any potentially stale * local TLB entries before they start being used. */ isb tlbi vmalle1 ic iallu dsb nsh isb /* * Configure sctlr_el1 to enable MMU and cache and don't proceed until this has completed. */ msr sctlr_el1, x30 isb /* Disable trapping floating point access in EL1. */ mrs x30, cpacr_el1 orr x30, x30, #(0x3 << 20) msr cpacr_el1, x30 isb /* Zero out the bss section. */ adr_l x29, bss_begin adr_l x30, bss_end 0: cmp x29, x30 b.hs 1f stp xzr, xzr, [x29], #16 b 0b 1: /* Prepare the stack. */ adr_l x30, boot_stack_end mov sp, x30 /* Set up exception vector. */ adr x30, vector_table_el1 msr vbar_el1, x30 /* Call into Rust code. */ bl main /* Loop forever waiting for interrupts. */ 2: wfi b 2b

exceptions.S (您應該不需要變更此項目):

/* * 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 * * https://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. */ /** * Saves the volatile registers onto the stack. This currently takes 14 * instructions, so it can be used in exception handlers with 18 instructions * left. * * On return, x0 and x1 are initialised to elr_el2 and spsr_el2 respectively, * which can be used as the first and second arguments of a subsequent call. */ .macro save_volatile_to_stack /* Reserve stack space and save registers x0-x18, x29 & x30. */ stp x0, x1, [sp, #-(8 * 24)]! stp x2, x3, [sp, #8 * 2] stp x4, x5, [sp, #8 * 4] stp x6, x7, [sp, #8 * 6] stp x8, x9, [sp, #8 * 8] stp x10, x11, [sp, #8 * 10] stp x12, x13, [sp, #8 * 12] stp x14, x15, [sp, #8 * 14] stp x16, x17, [sp, #8 * 16] str x18, [sp, #8 * 18] stp x29, x30, [sp, #8 * 20] /* * Save elr_el1 & spsr_el1. This such that we can take nested exception * and still be able to unwind. */ mrs x0, elr_el1 mrs x1, spsr_el1 stp x0, x1, [sp, #8 * 22] .endm /** * Restores the volatile registers from the stack. This currently takes 14 * instructions, so it can be used in exception handlers while still leaving 18 * instructions left; if paired with save_volatile_to_stack, there are 4 * instructions to spare. */ .macro restore_volatile_from_stack /* Restore registers x2-x18, x29 & x30. */ ldp x2, x3, [sp, #8 * 2] ldp x4, x5, [sp, #8 * 4] ldp x6, x7, [sp, #8 * 6] ldp x8, x9, [sp, #8 * 8] ldp x10, x11, [sp, #8 * 10] ldp x12, x13, [sp, #8 * 12] ldp x14, x15, [sp, #8 * 14] ldp x16, x17, [sp, #8 * 16] ldr x18, [sp, #8 * 18] ldp x29, x30, [sp, #8 * 20] /* Restore registers elr_el1 & spsr_el1, using x0 & x1 as scratch. */ ldp x0, x1, [sp, #8 * 22] msr elr_el1, x0 msr spsr_el1, x1 /* Restore x0 & x1, and release stack space. */ ldp x0, x1, [sp], #8 * 24 .endm /** * This is a generic handler for exceptions taken at the current EL while using * SP0. It behaves similarly to the SPx case by first switching to SPx, doing * the work, then switching back to SP0 before returning. * * Switching to SPx and calling the Rust handler takes 16 instructions. To * restore and return we need an additional 16 instructions, so we can implement * the whole handler within the allotted 32 instructions. */ .macro current_exception_sp0 handler:req msr spsel, #1 save_volatile_to_stack bl \handler restore_volatile_from_stack msr spsel, #0 eret .endm /** * This is a generic handler for exceptions taken at the current EL while using * SPx. It saves volatile registers, calls the Rust handler, restores volatile * registers, then returns. * * This also works for exceptions taken from EL0, if we don't care about * non-volatile registers. * * Saving state and jumping to the Rust handler takes 15 instructions, and * restoring and returning also takes 15 instructions, so we can fit the whole * handler in 30 instructions, under the limit of 32. */ .macro current_exception_spx handler:req save_volatile_to_stack bl \handler restore_volatile_from_stack eret .endm .section .text.vector_table_el1, "ax" .global vector_table_el1 .balign 0x800 vector_table_el1: sync_cur_sp0: current_exception_sp0 sync_exception_current .balign 0x80 irq_cur_sp0: current_exception_sp0 irq_current .balign 0x80 fiq_cur_sp0: current_exception_sp0 fiq_current .balign 0x80 serr_cur_sp0: current_exception_sp0 serr_current .balign 0x80 sync_cur_spx: current_exception_spx sync_exception_current .balign 0x80 irq_cur_spx: current_exception_spx irq_current .balign 0x80 fiq_cur_spx: current_exception_spx fiq_current .balign 0x80 serr_cur_spx: current_exception_spx serr_current .balign 0x80 sync_lower_64: current_exception_spx sync_lower .balign 0x80 irq_lower_64: current_exception_spx irq_lower .balign 0x80 fiq_lower_64: current_exception_spx fiq_lower .balign 0x80 serr_lower_64: current_exception_spx serr_lower .balign 0x80 sync_lower_32: current_exception_spx sync_lower .balign 0x80 irq_lower_32: current_exception_spx irq_lower .balign 0x80 fiq_lower_32: current_exception_spx fiq_lower .balign 0x80 serr_lower_32: current_exception_spx serr_lower

idmap.S (您應該不需要變更此項目):

/* * 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 * * https://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. */ .set .L_TT_TYPE_BLOCK, 0x1 .set .L_TT_TYPE_PAGE, 0x3 .set .L_TT_TYPE_TABLE, 0x3 /* Access flag. */ .set .L_TT_AF, 0x1 << 10 /* Not global. */ .set .L_TT_NG, 0x1 << 11 .set .L_TT_XN, 0x3 << 53 .set .L_TT_MT_DEV, 0x0 << 2 // MAIR #0 (DEV_nGnRE) .set .L_TT_MT_MEM, (0x1 << 2) | (0x3 << 8) // MAIR #1 (MEM_WBWA), inner shareable .set .L_BLOCK_DEV, .L_TT_TYPE_BLOCK | .L_TT_MT_DEV | .L_TT_AF | .L_TT_XN .set .L_BLOCK_MEM, .L_TT_TYPE_BLOCK | .L_TT_MT_MEM | .L_TT_AF | .L_TT_NG .section ".rodata.idmap", "a", %progbits .global idmap .align 12 idmap: /* level 1 */ .quad .L_BLOCK_DEV | 0x0 // 1 GiB of device mappings .quad .L_BLOCK_MEM | 0x40000000 // 1 GiB of DRAM .fill 254, 8, 0x0 // 254 GiB of unmapped VA space .quad .L_BLOCK_DEV | 0x4000000000 // 1 GiB of device mappings .fill 255, 8, 0x0 // 255 GiB of remaining VA space

image.ld (您應該不需要變更此項目):

/* * 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 * * https://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. */ /* * Code will start running at this symbol which is placed at the start of the * image. */ ENTRY(entry) MEMORY { image : ORIGIN = 0x40080000, LENGTH = 2M } SECTIONS { /* * Collect together the code. */ .init : ALIGN(4096) { text_begin = .; *(.init.entry) *(.init.*) } >image .text : { *(.text.*) } >image text_end = .; /* * Collect together read-only data. */ .rodata : ALIGN(4096) { rodata_begin = .; *(.rodata.*) } >image .got : { *(.got) } >image rodata_end = .; /* * Collect together the read-write data including .bss at the end which * will be zero'd by the entry code. */ .data : ALIGN(4096) { data_begin = .; *(.data.*) /* * The entry point code assumes that .data is a multiple of 32 * bytes long. */ . = ALIGN(32); data_end = .; } >image /* Everything beyond this point will not be included in the binary. */ bin_end = .; /* The entry point code assumes that .bss is 16-byte aligned. */ .bss : ALIGN(16) { bss_begin = .; *(.bss.*) *(COMMON) . = ALIGN(16); bss_end = .; } >image .stack (NOLOAD) : ALIGN(4096) { boot_stack_begin = .; . += 40 * 4096; . = ALIGN(4096); boot_stack_end = .; } >image . = ALIGN(4K); PROVIDE(dma_region = .); /* * Remove unused sections from the image. */ /DISCARD/ : { /* The image loads itself so doesn't need these sections. */ *(.gnu.hash) *(.hash) *(.interp) *(.eh_frame_hdr) *(.eh_frame) *(.note.gnu.build-id) } }

Makefile (您應該不需要變更此項目):

# 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. UNAME := $(shell uname -s) ifeq ($(UNAME),Linux) TARGET = aarch64-linux-gnu else TARGET = aarch64-none-elf endif OBJCOPY = $(TARGET)-objcopy .PHONY: build qemu_minimal qemu qemu_logger all: rtc.bin build: cargo build rtc.bin: build $(OBJCOPY) -O binary target/aarch64-unknown-none/debug/rtc $@ qemu: rtc.bin qemu-system-aarch64 -machine virt,gic-version=3 -cpu max -serial mon:stdio -display none -kernel $< -s clean: cargo clean rm -f *.bin

.cargo/config.toml (您應該不需要變更此項目):

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

使用 make qemu,在 QEMU 中執行程式碼。