|
| 1 | +# Tutorial 02 - Inicio de Tiempo de Ejecución |
| 2 | + |
| 3 | +## tl;dr |
| 4 | + |
| 5 | +* Extendimos la funcionalidad de `boot.s` para que sea capaz de llamar código de Rust por primera vez. Antes de que el cambio a Rust ocurra, se realizan algunos trabajos de inicio de tiempo de ejecución (runtime init). |
| 6 | +* El código de Rust que es llamado solo pausa la ejecución con una llamada a `panic!()`. |
| 7 | +* Ejecuta `make qemu` de nuevo para que puedas ver el código adicional en acción. |
| 8 | + |
| 9 | +## Adiciones importantes |
| 10 | + |
| 11 | +* Adiciones importantes al script `link.ld`: |
| 12 | + |
| 13 | + * Nuevas secciones: `.rodata`, `.got`, `.data`, `.bss`. |
| 14 | + |
| 15 | + * Un lugar totalmente dedicado para enlazar argumentos de tiempo de arranque (boot-time) que necesitan estar listos cuando se llame a `_start()`. |
| 16 | + |
| 17 | +* `_start()` en `_arch/__arch_name__/cpu/boot.s`: |
| 18 | + |
| 19 | + 1. Para el núcleo del procesador `if core != core0` (si la variable `core` es diferente de la variable `core0`). |
| 20 | + |
| 21 | + 2. Inicializa la [`DRAM`](https://es.wikipedia.org/wiki/DRAM) haciendo ceros la sección de [`bsp`](https://en.wikipedia.org/wiki/.bss). |
| 22 | + |
| 23 | + 3. Configura el `stack pointer` (puntero a la memoria *stack*). |
| 24 | + |
| 25 | + 4. Salta hacia la función `_start_rust()`, definida en `arch/__arch_name__/cpu/boot.rs`. |
| 26 | + |
| 27 | +. `_start_rust()`: |
| 28 | + |
| 29 | + . Llama a `kernel_init()`, que llama a `panic!()`, que eventualmente también pausa a core0. |
| 30 | + |
| 31 | +. La librería ahora usa el crate [cortex-a](https://github.com/rust-embedded/cortex-a), que nos da abstracciones conocidas como *zero-overhead* y envulve las partes que hacen uso de un `unsafe` (partes con código que no es seguro y podría causar errores) cuando se trabaja con los recursos del procesador. |
| 32 | + |
| 33 | + . Lo puedes ver en acción en `_arch/__arch_name__/cpu.rs`. |
| 34 | + |
| 35 | +## Diff del archivo anterior |
| 36 | + |
| 37 | +```diff |
| 38 | +diff -uNr 01_wait_forever/Cargo.toml 02_runtime_init/Cargo.toml |
| 39 | +--- 01_wait_forever/Cargo.toml |
| 40 | ++++ 02_runtime_init/Cargo.toml |
| 41 | +@@ -1,6 +1,6 @@ |
| 42 | + [package] |
| 43 | + name = "mingo" |
| 44 | +-version = "0.1.0" |
| 45 | ++version = "0.2.0" |
| 46 | + authors = ["Andre Richter <andre.o.richter@gmail.com>"] |
| 47 | + edition = "2021" |
| 48 | + |
| 49 | +@@ -21,3 +21,7 @@ |
| 50 | + ##-------------------------------------------------------------------------------------------------- |
| 51 | + |
| 52 | + [dependencies] |
| 53 | ++ |
| 54 | ++# Platform specific dependencies |
| 55 | ++[target.'cfg(target_arch = "aarch64")'.dependencies] |
| 56 | ++cortex-a = { version = "7.x.x" } |
| 57 | + |
| 58 | +diff -uNr 01_wait_forever/Makefile 02_runtime_init/Makefile |
| 59 | +--- 01_wait_forever/Makefile |
| 60 | ++++ 02_runtime_init/Makefile |
| 61 | +@@ -153,6 +153,8 @@ |
| 62 | + $(call colorecho, "\nLaunching objdump") |
| 63 | + @$(DOCKER_TOOLS) $(OBJDUMP_BINARY) --disassemble --demangle \ |
| 64 | + --section .text \ |
| 65 | ++ --section .rodata \ |
| 66 | ++ --section .got \ |
| 67 | + $(KERNEL_ELF) | rustfilt |
| 68 | + |
| 69 | + ##------------------------------------------------------------------------------ |
| 70 | + |
| 71 | +diff -uNr 01_wait_forever/src/_arch/aarch64/cpu/boot.rs 02_runtime_init/src/_arch/aarch64/cpu/boot.rs |
| 72 | +--- 01_wait_forever/src/_arch/aarch64/cpu/boot.rs |
| 73 | ++++ 02_runtime_init/src/_arch/aarch64/cpu/boot.rs |
| 74 | +@@ -13,3 +13,15 @@ |
| 75 | + |
| 76 | + // Assembly counterpart to this file. |
| 77 | + core::arch::global_asm!(include_str!("boot.s")); |
| 78 | ++ |
| 79 | ++//-------------------------------------------------------------------------------------------------- |
| 80 | ++// Public Code |
| 81 | ++//-------------------------------------------------------------------------------------------------- |
| 82 | ++ |
| 83 | ++/// The Rust entry of the `kernel` binary. |
| 84 | ++/// |
| 85 | ++/// The function is called from the assembly `_start` function. |
| 86 | ++#[no_mangle] |
| 87 | ++pub unsafe fn _start_rust() -> ! { |
| 88 | ++ crate::kernel_init() |
| 89 | ++} |
| 90 | + |
| 91 | +diff -uNr 01_wait_forever/src/_arch/aarch64/cpu/boot.s 02_runtime_init/src/_arch/aarch64/cpu/boot.s |
| 92 | +--- 01_wait_forever/src/_arch/aarch64/cpu/boot.s |
| 93 | ++++ 02_runtime_init/src/_arch/aarch64/cpu/boot.s |
| 94 | +@@ -3,6 +3,24 @@ |
| 95 | + // Copyright (c) 2021-2022 Andre Richter <andre.o.richter@gmail.com> |
| 96 | + |
| 97 | + //-------------------------------------------------------------------------------------------------- |
| 98 | ++// Definitions |
| 99 | ++//-------------------------------------------------------------------------------------------------- |
| 100 | ++ |
| 101 | ++// Load the address of a symbol into a register, PC-relative. |
| 102 | ++// |
| 103 | ++// The symbol must lie within +/- 4 GiB of the Program Counter. |
| 104 | ++// |
| 105 | ++// # Resources |
| 106 | ++// |
| 107 | ++// - https://sourceware.org/binutils/docs-2.36/as/AArch64_002dRelocations.html |
| 108 | ++.macro ADR_REL register, symbol |
| 109 | ++ adrp \register, \symbol |
| 110 | ++ add \register, \register, #:lo12:\symbol |
| 111 | ++.endm |
| 112 | ++ |
| 113 | ++.equ _core_id_mask, 0b11 |
| 114 | ++ |
| 115 | ++//-------------------------------------------------------------------------------------------------- |
| 116 | + // Public Code |
| 117 | + //-------------------------------------------------------------------------------------------------- |
| 118 | + .section .text._start |
| 119 | +@@ -11,6 +29,34 @@ |
| 120 | + // fn _start() |
| 121 | + //------------------------------------------------------------------------------ |
| 122 | + _start: |
| 123 | ++ // Only proceed on the boot core. Park it otherwise. |
| 124 | ++ mrs x1, MPIDR_EL1 |
| 125 | ++ and x1, x1, _core_id_mask |
| 126 | ++ ldr x2, BOOT_CORE_ID // provided by bsp/__board_name__/cpu.rs |
| 127 | ++ cmp x1, x2 |
| 128 | ++ b.ne .L_parking_loop |
| 129 | ++ |
| 130 | ++ // If execution reaches here, it is the boot core. |
| 131 | ++ |
| 132 | ++ // Initialize DRAM. |
| 133 | ++ ADR_REL x0, __bss_start |
| 134 | ++ ADR_REL x1, __bss_end_exclusive |
| 135 | ++ |
| 136 | ++.L_bss_init_loop: |
| 137 | ++ cmp x0, x1 |
| 138 | ++ b.eq .L_prepare_rust |
| 139 | ++ stp xzr, xzr, [x0], #16 |
| 140 | ++ b .L_bss_init_loop |
| 141 | ++ |
| 142 | ++ // Prepare the jump to Rust code. |
| 143 | ++.L_prepare_rust: |
| 144 | ++ // Set the stack pointer. |
| 145 | ++ ADR_REL x0, __boot_core_stack_end_exclusive |
| 146 | ++ mov sp, x0 |
| 147 | ++ |
| 148 | ++ // Jump to Rust code. |
| 149 | ++ b _start_rust |
| 150 | ++ |
| 151 | + // Infinitely wait for events (aka "park the core"). |
| 152 | + .L_parking_loop: |
| 153 | + wfe |
| 154 | + |
| 155 | +diff -uNr 01_wait_forever/src/_arch/aarch64/cpu.rs 02_runtime_init/src/_arch/aarch64/cpu.rs |
| 156 | +--- 01_wait_forever/src/_arch/aarch64/cpu.rs |
| 157 | ++++ 02_runtime_init/src/_arch/aarch64/cpu.rs |
| 158 | +@@ -0,0 +1,26 @@ |
| 159 | ++// SPDX-License-Identifier: MIT OR Apache-2.0 |
| 160 | ++// |
| 161 | ++// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com> |
| 162 | ++ |
| 163 | ++//! Architectural processor code. |
| 164 | ++//! |
| 165 | ++//! # Orientation |
| 166 | ++//! |
| 167 | ++//! Since arch modules are imported into generic modules using the path attribute, the path of this |
| 168 | ++//! file is: |
| 169 | ++//! |
| 170 | ++//! crate::cpu::arch_cpu |
| 171 | ++ |
| 172 | ++use cortex_a::asm; |
| 173 | ++ |
| 174 | ++//-------------------------------------------------------------------------------------------------- |
| 175 | ++// Public Code |
| 176 | ++//-------------------------------------------------------------------------------------------------- |
| 177 | ++ |
| 178 | ++/// Pause execution on the core. |
| 179 | ++#[inline(always)] |
| 180 | ++pub fn wait_forever() -> ! { |
| 181 | ++ loop { |
| 182 | ++ asm::wfe() |
| 183 | ++ } |
| 184 | ++} |
| 185 | + |
| 186 | +diff -uNr 01_wait_forever/src/bsp/raspberrypi/cpu.rs 02_runtime_init/src/bsp/raspberrypi/cpu.rs |
| 187 | +--- 01_wait_forever/src/bsp/raspberrypi/cpu.rs |
| 188 | ++++ 02_runtime_init/src/bsp/raspberrypi/cpu.rs |
| 189 | +@@ -0,0 +1,14 @@ |
| 190 | ++// SPDX-License-Identifier: MIT OR Apache-2.0 |
| 191 | ++// |
| 192 | ++// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com> |
| 193 | ++ |
| 194 | ++//! BSP Processor code. |
| 195 | ++ |
| 196 | ++//-------------------------------------------------------------------------------------------------- |
| 197 | ++// Public Definitions |
| 198 | ++//-------------------------------------------------------------------------------------------------- |
| 199 | ++ |
| 200 | ++/// Used by `arch` code to find the early boot core. |
| 201 | ++#[no_mangle] |
| 202 | ++#[link_section = ".text._start_arguments"] |
| 203 | ++pub static BOOT_CORE_ID: u64 = 0; |
| 204 | + |
| 205 | +diff -uNr 01_wait_forever/src/bsp/raspberrypi/link.ld 02_runtime_init/src/bsp/raspberrypi/link.ld |
| 206 | +--- 01_wait_forever/src/bsp/raspberrypi/link.ld |
| 207 | ++++ 02_runtime_init/src/bsp/raspberrypi/link.ld |
| 208 | +@@ -3,6 +3,8 @@ |
| 209 | + * Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com> |
| 210 | + */ |
| 211 | + |
| 212 | ++__rpi_phys_dram_start_addr = 0; |
| 213 | ++ |
| 214 | + /* The physical address at which the the kernel binary will be loaded by the Raspberry's firmware */ |
| 215 | + __rpi_phys_binary_load_addr = 0x80000; |
| 216 | + |
| 217 | +@@ -13,21 +15,58 @@ |
| 218 | + * 4 == R |
| 219 | + * 5 == RX |
| 220 | + * 6 == RW |
| 221 | ++ * |
| 222 | ++ * Segments are marked PT_LOAD below so that the ELF file provides virtual and physical addresses. |
| 223 | ++ * It doesn't mean all of them need actually be loaded. |
| 224 | + */ |
| 225 | + PHDRS |
| 226 | + { |
| 227 | +- segment_code PT_LOAD FLAGS(5); |
| 228 | ++ segment_boot_core_stack PT_LOAD FLAGS(6); |
| 229 | ++ segment_code PT_LOAD FLAGS(5); |
| 230 | ++ segment_data PT_LOAD FLAGS(6); |
| 231 | + } |
| 232 | + |
| 233 | + SECTIONS |
| 234 | + { |
| 235 | +- . = __rpi_phys_binary_load_addr; |
| 236 | ++ . = __rpi_phys_dram_start_addr; |
| 237 | ++ |
| 238 | ++ /*********************************************************************************************** |
| 239 | ++ * Boot Core Stack |
| 240 | ++ ***********************************************************************************************/ |
| 241 | ++ .boot_core_stack (NOLOAD) : |
| 242 | ++ { |
| 243 | ++ /* ^ */ |
| 244 | ++ /* | stack */ |
| 245 | ++ . += __rpi_phys_binary_load_addr; /* | growth */ |
| 246 | ++ /* | direction */ |
| 247 | ++ __boot_core_stack_end_exclusive = .; /* | */ |
| 248 | ++ } :segment_boot_core_stack |
| 249 | + |
| 250 | + /*********************************************************************************************** |
| 251 | +- * Code |
| 252 | ++ * Code + RO Data + Global Offset Table |
| 253 | + ***********************************************************************************************/ |
| 254 | + .text : |
| 255 | + { |
| 256 | + KEEP(*(.text._start)) |
| 257 | ++ *(.text._start_arguments) /* Constants (or statics in Rust speak) read by _start(). */ |
| 258 | ++ *(.text._start_rust) /* The Rust entry point */ |
| 259 | ++ *(.text*) /* Everything else */ |
| 260 | + } :segment_code |
| 261 | ++ |
| 262 | ++ .rodata : ALIGN(8) { *(.rodata*) } :segment_code |
| 263 | ++ .got : ALIGN(8) { *(.got) } :segment_code |
| 264 | ++ |
| 265 | ++ /*********************************************************************************************** |
| 266 | ++ * Data + BSS |
| 267 | ++ ***********************************************************************************************/ |
| 268 | ++ .data : { *(.data*) } :segment_data |
| 269 | ++ |
| 270 | ++ /* Section is zeroed in pairs of u64. Align start and end to 16 bytes */ |
| 271 | ++ .bss (NOLOAD) : ALIGN(16) |
| 272 | ++ { |
| 273 | ++ __bss_start = .; |
| 274 | ++ *(.bss*); |
| 275 | ++ . = ALIGN(16); |
| 276 | ++ __bss_end_exclusive = .; |
| 277 | ++ } :segment_data |
| 278 | + } |
| 279 | + |
| 280 | +diff -uNr 01_wait_forever/src/bsp/raspberrypi.rs 02_runtime_init/src/bsp/raspberrypi.rs |
| 281 | +--- 01_wait_forever/src/bsp/raspberrypi.rs |
| 282 | ++++ 02_runtime_init/src/bsp/raspberrypi.rs |
| 283 | +@@ -4,4 +4,4 @@ |
| 284 | + |
| 285 | + //! Top-level BSP file for the Raspberry Pi 3 and 4. |
| 286 | + |
| 287 | +-// Coming soon. |
| 288 | ++pub mod cpu; |
| 289 | + |
| 290 | +diff -uNr 01_wait_forever/src/cpu.rs 02_runtime_init/src/cpu.rs |
| 291 | +--- 01_wait_forever/src/cpu.rs |
| 292 | ++++ 02_runtime_init/src/cpu.rs |
| 293 | +@@ -4,4 +4,13 @@ |
| 294 | + |
| 295 | + //! Processor code. |
| 296 | + |
| 297 | ++#[cfg(target_arch = "aarch64")] |
| 298 | ++#[path = "_arch/aarch64/cpu.rs"] |
| 299 | ++mod arch_cpu; |
| 300 | ++ |
| 301 | + mod boot; |
| 302 | ++ |
| 303 | ++//-------------------------------------------------------------------------------------------------- |
| 304 | ++// Architectural Public Reexports |
| 305 | ++//-------------------------------------------------------------------------------------------------- |
| 306 | ++pub use arch_cpu::wait_forever; |
| 307 | + |
| 308 | +diff -uNr 01_wait_forever/src/main.rs 02_runtime_init/src/main.rs |
| 309 | +--- 01_wait_forever/src/main.rs |
| 310 | ++++ 02_runtime_init/src/main.rs |
| 311 | +@@ -102,6 +102,7 @@ |
| 312 | + //! |
| 313 | + //! 1. The kernel's entry point is the function `cpu::boot::arch_boot::_start()`. |
| 314 | + //! - It is implemented in `src/_arch/__arch_name__/cpu/boot.s`. |
| 315 | ++//! 2. Once finished with architectural setup, the arch code calls `kernel_init()`. |
| 316 | + |
| 317 | + #![no_main] |
| 318 | + #![no_std] |
| 319 | +@@ -110,4 +111,11 @@ |
| 320 | + mod cpu; |
| 321 | + mod panic_wait; |
| 322 | + |
| 323 | +-// Kernel code coming next tutorial. |
| 324 | ++/// Early init code. |
| 325 | ++/// |
| 326 | ++/// # Safety |
| 327 | ++/// |
| 328 | ++/// - Only a single core must be active and running this function. |
| 329 | ++unsafe fn kernel_init() -> ! { |
| 330 | ++ panic!() |
| 331 | ++} |
| 332 | + |
| 333 | +diff -uNr 01_wait_forever/src/panic_wait.rs 02_runtime_init/src/panic_wait.rs |
| 334 | +--- 01_wait_forever/src/panic_wait.rs |
| 335 | ++++ 02_runtime_init/src/panic_wait.rs |
| 336 | +@@ -4,9 +4,10 @@ |
| 337 | + |
| 338 | + //! A panic handler that infinitely waits. |
| 339 | + |
| 340 | ++use crate::cpu; |
| 341 | + use core::panic::PanicInfo; |
| 342 | + |
| 343 | + #[panic_handler] |
| 344 | + fn panic(_info: &PanicInfo) -> ! { |
| 345 | +- unimplemented!() |
| 346 | ++ cpu::wait_forever() |
| 347 | + } |
| 348 | +``` |
0 commit comments