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::Process;
28use kernel::scheduler::cooperative::CooperativeSched;
29use kernel::syscall::SyscallDriver;
30use kernel::{create_capability, static_init, Kernel};
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// Actual memory for holding the active process structures. Need an empty list
51// at least.
52static mut PROCESSES: [Option<&'static dyn Process>; NUM_PROCS] = [None; NUM_PROCS];
53
54// Reference to the chip for panic dumps
55static mut CHIP: Option<&'static Pc> = None;
56
57// Reference to the process printer for panic dumps.
58static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
59    None;
60
61// How should the kernel respond when a process faults.
62const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
63    capsules_system::process_policies::PanicFaultPolicy {};
64
65/// Dummy buffer that causes the linker to reserve enough space for the stack.
66#[no_mangle]
67#[link_section = ".stack_buffer"]
68static mut STACK_MEMORY: [u8; 0x1000] = [0; 0x1000];
69
70// Static allocations used for page tables
71//
72// These are placed into custom sections so they can be properly aligned and padded in layout.ld
73#[no_mangle]
74#[link_section = ".pde"]
75pub static mut PAGE_DIR: PD = [PDEntry(0); 1024];
76#[no_mangle]
77#[link_section = ".pte"]
78pub static mut PAGE_TABLE: PT = [PTEntry(0); 1024];
79
80pub struct QemuI386Q35Platform {
81    pconsole: &'static capsules_core::process_console::ProcessConsole<
82        'static,
83        { capsules_core::process_console::DEFAULT_COMMAND_HISTORY_LEN },
84        VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>,
85        components::process_console::Capability,
86    >,
87    console: &'static Console<'static>,
88    lldb: &'static capsules_core::low_level_debug::LowLevelDebug<
89        'static,
90        capsules_core::virtualizers::virtual_uart::UartDevice<'static>,
91    >,
92    alarm: &'static capsules_core::alarm::AlarmDriver<
93        'static,
94        VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>,
95    >,
96    ipc: IPC<{ NUM_PROCS as u8 }>,
97    scheduler: &'static CooperativeSched<'static>,
98    scheduler_timer:
99        &'static VirtualSchedulerTimer<VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>>,
100}
101
102impl SyscallDriverLookup for QemuI386Q35Platform {
103    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
104    where
105        F: FnOnce(Option<&dyn SyscallDriver>) -> R,
106    {
107        match driver_num {
108            console::DRIVER_NUM => f(Some(self.console)),
109            alarm::DRIVER_NUM => f(Some(self.alarm)),
110            capsules_core::low_level_debug::DRIVER_NUM => f(Some(self.lldb)),
111            kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
112            _ => f(None),
113        }
114    }
115}
116
117impl<C: Chip> KernelResources<C> for QemuI386Q35Platform {
118    type SyscallDriverLookup = Self;
119    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
120        self
121    }
122
123    type SyscallFilter = ();
124    fn syscall_filter(&self) -> &Self::SyscallFilter {
125        &()
126    }
127
128    type ProcessFault = ();
129    fn process_fault(&self) -> &Self::ProcessFault {
130        &()
131    }
132
133    type Scheduler = CooperativeSched<'static>;
134    fn scheduler(&self) -> &Self::Scheduler {
135        self.scheduler
136    }
137
138    type SchedulerTimer =
139        VirtualSchedulerTimer<VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>>;
140    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
141        self.scheduler_timer
142    }
143
144    type WatchDog = ();
145    fn watchdog(&self) -> &Self::WatchDog {
146        &()
147    }
148
149    type ContextSwitchCallback = ();
150    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
151        &()
152    }
153}
154
155#[no_mangle]
156unsafe extern "cdecl" fn main() {
157    // ---------- BASIC INITIALIZATION -----------
158
159    // Basic setup of the i486 platform
160    let chip = PcComponent::new(
161        &mut *ptr::addr_of_mut!(PAGE_DIR),
162        &mut *ptr::addr_of_mut!(PAGE_TABLE),
163    )
164    .finalize(x86_q35::x86_q35_component_static!());
165
166    // Acquire required capabilities
167    let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
168    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
169    let main_loop_cap = create_capability!(capabilities::MainLoopCapability);
170
171    // Create a board kernel instance
172    let board_kernel = static_init!(Kernel, Kernel::new(&*ptr::addr_of!(PROCESSES)));
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, 115200)
179        .finalize(components::uart_mux_component_static!());
180
181    // Create a shared virtualization mux layer on top of a single hardware
182    // alarm.
183    let mux_alarm = static_init!(
184        MuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>,
185        MuxAlarm::new(&chip.pit),
186    );
187    hil::time::Alarm::set_alarm_client(&chip.pit, mux_alarm);
188
189    // Virtual alarm for the scheduler
190    let systick_virtual_alarm = static_init!(
191        VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>,
192        VirtualMuxAlarm::new(mux_alarm)
193    );
194    systick_virtual_alarm.setup();
195
196    // Virtual alarm and driver for userspace
197    let virtual_alarm_user = static_init!(
198        VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>,
199        VirtualMuxAlarm::new(mux_alarm)
200    );
201    virtual_alarm_user.setup();
202
203    let alarm = static_init!(
204        capsules_core::alarm::AlarmDriver<
205            'static,
206            VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>,
207        >,
208        capsules_core::alarm::AlarmDriver::new(
209            virtual_alarm_user,
210            board_kernel.create_grant(capsules_core::alarm::DRIVER_NUM, &memory_allocation_cap)
211        )
212    );
213    hil::time::Alarm::set_alarm_client(virtual_alarm_user, alarm);
214
215    // ---------- INITIALIZE CHIP, ENABLE INTERRUPTS ---------
216
217    // PIT interrupts need to be started manually
218    chip.pit.start();
219
220    // Enable interrupts after all drivers are initialized
221    irq::enable();
222
223    // ---------- FINAL SYSTEM INITIALIZATION ----------
224
225    // Create the process printer used in panic prints, etc.
226    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
227        .finalize(components::process_printer_text_component_static!());
228    PROCESS_PRINTER = Some(process_printer);
229
230    // Initialize the kernel's process console.
231    let pconsole = components::process_console::ProcessConsoleComponent::new(
232        board_kernel,
233        uart_mux,
234        mux_alarm,
235        process_printer,
236        None,
237    )
238    .finalize(components::process_console_component_static!(
239        Pit<'static, RELOAD_1KHZ>
240    ));
241
242    // Setup the console.
243    let console = ConsoleComponent::new(board_kernel, console::DRIVER_NUM, uart_mux)
244        .finalize(components::console_component_static!());
245
246    // Create the debugger object that handles calls to `debug!()`.
247    DebugWriterComponent::new(
248        uart_mux,
249        create_capability!(capabilities::SetDebugWriterCapability),
250    )
251    .finalize(components::debug_writer_component_static!());
252
253    let lldb = components::lldb::LowLevelDebugComponent::new(
254        board_kernel,
255        capsules_core::low_level_debug::DRIVER_NUM,
256        uart_mux,
257    )
258    .finalize(components::low_level_debug_component_static!());
259
260    let scheduler =
261        components::sched::cooperative::CooperativeComponent::new(&*ptr::addr_of!(PROCESSES))
262            .finalize(components::cooperative_component_static!(NUM_PROCS));
263
264    let scheduler_timer = static_init!(
265        VirtualSchedulerTimer<VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>>,
266        VirtualSchedulerTimer::new(systick_virtual_alarm)
267    );
268
269    let platform = QemuI386Q35Platform {
270        pconsole,
271        console,
272        alarm,
273        lldb,
274        scheduler,
275        scheduler_timer,
276        ipc: kernel::ipc::IPC::new(
277            board_kernel,
278            kernel::ipc::DRIVER_NUM,
279            &memory_allocation_cap,
280        ),
281    };
282
283    // Start the process console:
284    let _ = platform.pconsole.start();
285
286    debug!("QEMU i486 \"Q35\" machine, initialization complete.");
287    debug!("Entering main loop.");
288
289    // These symbols are defined in the linker script.
290    extern "C" {
291        /// Beginning of the ROM region containing app images.
292        static _sapps: u8;
293        /// End of the ROM region containing app images.
294        static _eapps: u8;
295        /// Beginning of the RAM region for app memory.
296        static mut _sappmem: u8;
297        /// End of the RAM region for app memory.
298        static _eappmem: u8;
299    }
300
301    // ---------- PROCESS LOADING, SCHEDULER LOOP ----------
302
303    kernel::process::load_processes(
304        board_kernel,
305        chip,
306        core::slice::from_raw_parts(
307            ptr::addr_of!(_sapps),
308            ptr::addr_of!(_eapps) as usize - ptr::addr_of!(_sapps) as usize,
309        ),
310        core::slice::from_raw_parts_mut(
311            ptr::addr_of_mut!(_sappmem),
312            ptr::addr_of!(_eappmem) as usize - ptr::addr_of!(_sappmem) as usize,
313        ),
314        &mut *ptr::addr_of_mut!(PROCESSES),
315        &FAULT_RESPONSE,
316        &process_mgmt_cap,
317    )
318    .unwrap_or_else(|err| {
319        debug!("Error loading processes!");
320        debug!("{:?}", err);
321    });
322
323    board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_cap);
324}