qemu_i486_q35/
main.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2024.
4
5//! Board file for qemu-system-i486 "q35" machine type
6
7#![no_std]
8// Disable this attribute when documenting, as a workaround for
9// https://github.com/rust-lang/rust/issues/62184.
10#![cfg_attr(not(doc), no_main)]
11
12use core::ptr;
13
14use capsules_core::alarm;
15use capsules_core::console::{self, Console};
16use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
17use components::console::ConsoleComponent;
18use components::debug_writer::DebugWriterComponent;
19use kernel::capabilities;
20use kernel::component::Component;
21use kernel::debug;
22use kernel::hil;
23use kernel::ipc::IPC;
24use kernel::platform::chip::Chip;
25use kernel::platform::scheduler_timer::VirtualSchedulerTimer;
26use kernel::platform::{KernelResources, SyscallDriverLookup};
27use kernel::process::ProcessArray;
28use kernel::scheduler::cooperative::CooperativeSched;
29use kernel::syscall::SyscallDriver;
30use kernel::{create_capability, static_init};
31
32use x86::registers::bits32::paging::{PDEntry, PTEntry, PD, PT};
33use x86::registers::irq;
34
35use x86_q35::pit::{Pit, RELOAD_1KHZ};
36use x86_q35::{Pc, PcComponent};
37
38mod multiboot;
39use multiboot::MultibootV1Header;
40
41mod io;
42
43/// Multiboot V1 header, allowing this kernel to be booted directly by QEMU
44#[link_section = ".vectors"]
45#[used]
46static MULTIBOOT_V1_HEADER: MultibootV1Header = MultibootV1Header::new(0);
47
48const NUM_PROCS: usize = 4;
49
50/// Static variables used by io.rs.
51static mut PROCESSES: Option<&'static ProcessArray<NUM_PROCS>> = None;
52
53// Reference to the chip for panic dumps
54static mut CHIP: Option<&'static Pc> = None;
55
56// Reference to the process printer for panic dumps.
57static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
58    None;
59
60// How should the kernel respond when a process faults.
61const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
62    capsules_system::process_policies::PanicFaultPolicy {};
63
64/// Dummy buffer that causes the linker to reserve enough space for the stack.
65#[no_mangle]
66#[link_section = ".stack_buffer"]
67static mut STACK_MEMORY: [u8; 0x1000] = [0; 0x1000];
68
69// Static allocations used for page tables
70//
71// These are placed into custom sections so they can be properly aligned and padded in layout.ld
72#[no_mangle]
73#[link_section = ".pde"]
74pub static mut PAGE_DIR: PD = [PDEntry(0); 1024];
75#[no_mangle]
76#[link_section = ".pte"]
77pub static mut PAGE_TABLE: PT = [PTEntry(0); 1024];
78
79pub struct QemuI386Q35Platform {
80    pconsole: &'static capsules_core::process_console::ProcessConsole<
81        'static,
82        { capsules_core::process_console::DEFAULT_COMMAND_HISTORY_LEN },
83        VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>,
84        components::process_console::Capability,
85    >,
86    console: &'static Console<'static>,
87    lldb: &'static capsules_core::low_level_debug::LowLevelDebug<
88        'static,
89        capsules_core::virtualizers::virtual_uart::UartDevice<'static>,
90    >,
91    alarm: &'static capsules_core::alarm::AlarmDriver<
92        'static,
93        VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>,
94    >,
95    ipc: IPC<{ NUM_PROCS as u8 }>,
96    scheduler: &'static CooperativeSched<'static>,
97    scheduler_timer:
98        &'static VirtualSchedulerTimer<VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>>,
99}
100
101impl SyscallDriverLookup for QemuI386Q35Platform {
102    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
103    where
104        F: FnOnce(Option<&dyn SyscallDriver>) -> R,
105    {
106        match driver_num {
107            console::DRIVER_NUM => f(Some(self.console)),
108            alarm::DRIVER_NUM => f(Some(self.alarm)),
109            capsules_core::low_level_debug::DRIVER_NUM => f(Some(self.lldb)),
110            kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
111            _ => f(None),
112        }
113    }
114}
115
116impl<C: Chip> KernelResources<C> for QemuI386Q35Platform {
117    type SyscallDriverLookup = Self;
118    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
119        self
120    }
121
122    type SyscallFilter = ();
123    fn syscall_filter(&self) -> &Self::SyscallFilter {
124        &()
125    }
126
127    type ProcessFault = ();
128    fn process_fault(&self) -> &Self::ProcessFault {
129        &()
130    }
131
132    type Scheduler = CooperativeSched<'static>;
133    fn scheduler(&self) -> &Self::Scheduler {
134        self.scheduler
135    }
136
137    type SchedulerTimer =
138        VirtualSchedulerTimer<VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>>;
139    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
140        self.scheduler_timer
141    }
142
143    type WatchDog = ();
144    fn watchdog(&self) -> &Self::WatchDog {
145        &()
146    }
147
148    type ContextSwitchCallback = ();
149    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
150        &()
151    }
152}
153
154#[no_mangle]
155unsafe extern "cdecl" fn main() {
156    // ---------- BASIC INITIALIZATION -----------
157
158    // Basic setup of the i486 platform
159    let chip = PcComponent::new(
160        &mut *ptr::addr_of_mut!(PAGE_DIR),
161        &mut *ptr::addr_of_mut!(PAGE_TABLE),
162    )
163    .finalize(x86_q35::x86_q35_component_static!());
164
165    // Acquire required capabilities
166    let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
167    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
168    let main_loop_cap = create_capability!(capabilities::MainLoopCapability);
169
170    // Create an array to hold process references.
171    let processes = components::process_array::ProcessArrayComponent::new()
172        .finalize(components::process_array_component_static!(NUM_PROCS));
173    PROCESSES = Some(processes);
174
175    // Setup space to store the core kernel data structure.
176    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
177
178    // ---------- QEMU-SYSTEM-I386 "Q35" MACHINE PERIPHERALS ----------
179
180    // Create a shared UART channel for the console and for kernel
181    // debug over the provided 8250-compatible UART.
182    let uart_mux = components::console::UartMuxComponent::new(chip.com1, 115200)
183        .finalize(components::uart_mux_component_static!());
184
185    // Create a shared virtualization mux layer on top of a single hardware
186    // alarm.
187    let mux_alarm = static_init!(
188        MuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>,
189        MuxAlarm::new(&chip.pit),
190    );
191    hil::time::Alarm::set_alarm_client(&chip.pit, mux_alarm);
192
193    // Virtual alarm for the scheduler
194    let systick_virtual_alarm = static_init!(
195        VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>,
196        VirtualMuxAlarm::new(mux_alarm)
197    );
198    systick_virtual_alarm.setup();
199
200    // Virtual alarm and driver for userspace
201    let virtual_alarm_user = static_init!(
202        VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>,
203        VirtualMuxAlarm::new(mux_alarm)
204    );
205    virtual_alarm_user.setup();
206
207    let alarm = static_init!(
208        capsules_core::alarm::AlarmDriver<
209            'static,
210            VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>,
211        >,
212        capsules_core::alarm::AlarmDriver::new(
213            virtual_alarm_user,
214            board_kernel.create_grant(capsules_core::alarm::DRIVER_NUM, &memory_allocation_cap)
215        )
216    );
217    hil::time::Alarm::set_alarm_client(virtual_alarm_user, alarm);
218
219    // ---------- INITIALIZE CHIP, ENABLE INTERRUPTS ---------
220
221    // PIT interrupts need to be started manually
222    chip.pit.start();
223
224    // Enable interrupts after all drivers are initialized
225    irq::enable();
226
227    // ---------- FINAL SYSTEM INITIALIZATION ----------
228
229    // Create the process printer used in panic prints, etc.
230    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
231        .finalize(components::process_printer_text_component_static!());
232    PROCESS_PRINTER = Some(process_printer);
233
234    // Initialize the kernel's process console.
235    let pconsole = components::process_console::ProcessConsoleComponent::new(
236        board_kernel,
237        uart_mux,
238        mux_alarm,
239        process_printer,
240        None,
241    )
242    .finalize(components::process_console_component_static!(
243        Pit<'static, RELOAD_1KHZ>
244    ));
245
246    // Setup the console.
247    let console = ConsoleComponent::new(board_kernel, console::DRIVER_NUM, uart_mux)
248        .finalize(components::console_component_static!());
249
250    // Create the debugger object that handles calls to `debug!()`.
251    DebugWriterComponent::new(
252        uart_mux,
253        create_capability!(capabilities::SetDebugWriterCapability),
254    )
255    .finalize(components::debug_writer_component_static!());
256
257    let lldb = components::lldb::LowLevelDebugComponent::new(
258        board_kernel,
259        capsules_core::low_level_debug::DRIVER_NUM,
260        uart_mux,
261    )
262    .finalize(components::low_level_debug_component_static!());
263
264    let scheduler = components::sched::cooperative::CooperativeComponent::new(processes)
265        .finalize(components::cooperative_component_static!(NUM_PROCS));
266
267    let scheduler_timer = static_init!(
268        VirtualSchedulerTimer<VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>>,
269        VirtualSchedulerTimer::new(systick_virtual_alarm)
270    );
271
272    let platform = QemuI386Q35Platform {
273        pconsole,
274        console,
275        alarm,
276        lldb,
277        scheduler,
278        scheduler_timer,
279        ipc: kernel::ipc::IPC::new(
280            board_kernel,
281            kernel::ipc::DRIVER_NUM,
282            &memory_allocation_cap,
283        ),
284    };
285
286    // Start the process console:
287    let _ = platform.pconsole.start();
288
289    debug!("QEMU i486 \"Q35\" machine, initialization complete.");
290    debug!("Entering main loop.");
291
292    // These symbols are defined in the linker script.
293    extern "C" {
294        /// Beginning of the ROM region containing app images.
295        static _sapps: u8;
296        /// End of the ROM region containing app images.
297        static _eapps: u8;
298        /// Beginning of the RAM region for app memory.
299        static mut _sappmem: u8;
300        /// End of the RAM region for app memory.
301        static _eappmem: u8;
302    }
303
304    // ---------- PROCESS LOADING, SCHEDULER LOOP ----------
305
306    kernel::process::load_processes(
307        board_kernel,
308        chip,
309        core::slice::from_raw_parts(
310            ptr::addr_of!(_sapps),
311            ptr::addr_of!(_eapps) as usize - ptr::addr_of!(_sapps) as usize,
312        ),
313        core::slice::from_raw_parts_mut(
314            ptr::addr_of_mut!(_sappmem),
315            ptr::addr_of!(_eappmem) as usize - ptr::addr_of!(_sappmem) as usize,
316        ),
317        &FAULT_RESPONSE,
318        &process_mgmt_cap,
319    )
320    .unwrap_or_else(|err| {
321        debug!("Error loading processes!");
322        debug!("{:?}", err);
323    });
324
325    board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_cap);
326}