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