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