RTC 驱动程序
QEMU aarch64 虚拟机在 0x9010000 地址处 配备了 PL031 实时时钟。对于本练习,应该为其编写驱动程序。
- 使用该时钟可将当前时间输出到串行控制台。您可以使用 chrono crate 设置日期/时间格式。
- 通过匹配寄存器和原始中断状态,使得系统在某段指定的时间内一直进行繁忙等待(例如 3 秒后)。(在循环操作中调用 core::hint::spin_loop。)
- _进行扩展(如有时间):_启用并处理由 RTC 匹配产生的中断。可以使用 arm-gic crate 中提供的驱动程序来配置 Arm 通用中断控制器。
- 请使用 RTC 中断,将其作为
IntId::spi(2)连接到 GIC。 - 启用中断后,可以通过
arm_gic::wfi()让核心进入休眠状态,直到它收到中断信号。
- 请使用 RTC 中断,将其作为
下载 练习模板 并在 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) {// SAFETY: `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);// SAFETY: `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: mainuse 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: Flagsuse 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: Flagsbitflags! {/// 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) {}// SAFETY: 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 {// SAFETY: We know that self.registers points to the control// registers of a PL011 device which is appropriately mapped.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 {// SAFETY: 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: Uartimpl 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.5.0"chrono = { version = "0.4.37", default-features = false }log = "0.4.21"smccc = "0.1.1"spin = "0.9.8"[build-dependencies]cc = "1.0.94"
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:reqadrp \reg, \symadd \reg, \reg, :lo12:\sym.endm.macro mov_i, reg:req, imm:reqmovz \reg, :abs_g3:\immmovk \reg, :abs_g2_nc:\immmovk \reg, :abs_g1_nc:\immmovk \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 entryentry:/* Load and apply the memory management configuration, ready to enable MMU and caches. */adrp x30, idmapmsr ttbr0_el1, x30mov_i x30, .Lmairvalmsr mair_el1, x30mov_i x30, .Ltcrval/* Copy the supported PA range into TCR_EL1.IPS. */mrs x29, id_aa64mmfr0_el1bfi x30, x29, #32, #4msr tcr_el1, x30mov_i x30, .Lsctlrval/** Ensure everything before this point has completed, then invalidate any potentially stale* local TLB entries before they start being used.*/isbtlbi vmalle1ic ialludsb nshisb/** Configure sctlr_el1 to enable MMU and cache and don't proceed until this has completed.*/msr sctlr_el1, x30isb/* Disable trapping floating point access in EL1. */mrs x30, cpacr_el1orr x30, x30, #(0x3 << 20)msr cpacr_el1, x30isb/* Zero out the bss section. */adr_l x29, bss_beginadr_l x30, bss_end0: cmp x29, x30b.hs 1fstp xzr, xzr, [x29], #16b 0b1: /* Prepare the stack. */adr_l x30, boot_stack_endmov sp, x30/* Set up exception vector. */adr x30, vector_table_el1msr vbar_el1, x30/* Call into Rust code. */bl main/* Loop forever waiting for interrupts. */2: wfib 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_el1mrs x1, spsr_el1stp 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, x0msr 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:reqmsr spsel, #1save_volatile_to_stackbl \handlerrestore_volatile_from_stackmsr spsel, #0eret.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:reqsave_volatile_to_stackbl \handlerrestore_volatile_from_stackeret.endm.section .text.vector_table_el1, "ax".global vector_table_el1.balign 0x800vector_table_el1:sync_cur_sp0:current_exception_sp0 sync_exception_current.balign 0x80irq_cur_sp0:current_exception_sp0 irq_current.balign 0x80fiq_cur_sp0:current_exception_sp0 fiq_current.balign 0x80serr_cur_sp0:current_exception_sp0 serr_current.balign 0x80sync_cur_spx:current_exception_spx sync_exception_current.balign 0x80irq_cur_spx:current_exception_spx irq_current.balign 0x80fiq_cur_spx:current_exception_spx fiq_current.balign 0x80serr_cur_spx:current_exception_spx serr_current.balign 0x80sync_lower_64:current_exception_spx sync_lower.balign 0x80irq_lower_64:current_exception_spx irq_lower.balign 0x80fiq_lower_64:current_exception_spx fiq_lower.balign 0x80serr_lower_64:current_exception_spx serr_lower.balign 0x80sync_lower_32:current_exception_spx sync_lower.balign 0x80irq_lower_32:current_exception_spx irq_lower.balign 0x80fiq_lower_32:current_exception_spx fiq_lower.balign 0x80serr_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 12idmap:/* 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.*)} >imagetext_end = .;/** Collect together read-only data.*/.rodata : ALIGN(4096) {rodata_begin = .;*(.rodata.*)} >image.got : {*(.got)} >imagerodata_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-gnuelseTARGET = aarch64-none-elfendifOBJCOPY = $(TARGET)-objcopy.PHONY: build qemu_minimal qemu qemu_loggerall: rtc.binbuild:cargo buildrtc.bin: build$(OBJCOPY) -O binary target/aarch64-unknown-none/debug/rtc $@qemu: rtc.binqemu-system-aarch64 -machine virt,gic-version=3 -cpu max -serial mon:stdio -display none -kernel $< -sclean:cargo cleanrm -f *.bin
.cargo/config.toml(无需对此进行更改):
[build]target = "aarch64-unknown-none"rustflags = ["-C", "link-arg=-Timage.ld"]
使用 make qemu 在 QEMU 中运行代码。
