1#![no_std]
8#![cfg_attr(not(doc), no_main)]
11
12use capsules_core::alarm;
13use capsules_core::console::{self, Console};
14use capsules_core::rng::RngDriver;
15use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
16use components::console::ConsoleComponent;
17use components::debug_writer::DebugWriterComponent;
18use core::ptr;
19use kernel::capabilities;
20use kernel::component::Component;
21use kernel::debug;
22use kernel::deferred_call::DeferredCallClient;
23use kernel::hil;
24use kernel::ipc::IPC;
25use kernel::platform::chip::InterruptService;
26use kernel::platform::{KernelResources, SyscallDriverLookup};
27use kernel::process::ProcessArray;
28use kernel::scheduler::cooperative::CooperativeSched;
29use kernel::syscall::SyscallDriver;
30use kernel::utilities::cells::OptionalCell;
31use kernel::{create_capability, static_init};
32use virtio::devices::virtio_rng::VirtIORng;
33use virtio::devices::VirtIODeviceType;
34use virtio_pci_x86::VirtIOPCIDevice;
35use x86::registers::bits32::paging::{PDEntry, PTEntry, PD, PT};
36use x86::registers::irq;
37use x86_q35::pit::{Pit, RELOAD_1KHZ};
38use x86_q35::{Pc, PcDefaultPeripherals};
39
40mod multiboot;
41use multiboot::MultibootV1Header;
42
43mod io;
44
45#[cfg_attr(not(target_os = "macos"), link_section = ".multiboot")]
51#[used]
52static MULTIBOOT_V1_HEADER: MultibootV1Header = MultibootV1Header::new(0);
53
54const NUM_PROCS: usize = 4;
55
56type ChipHw = Pc<'static, (), ()>;
57type AlarmHw = Pit<'static, RELOAD_1KHZ>;
58type SchedulerTimerHw =
59 components::virtual_scheduler_timer::VirtualSchedulerTimerComponentType<AlarmHw>;
60
61static mut PROCESSES: Option<&'static ProcessArray<NUM_PROCS>> = None;
63
64static mut CHIP: Option<&'static ChipHw> = None;
66
67static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
69 None;
70
71const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
73 capsules_system::process_policies::PanicFaultPolicy {};
74
75kernel::stack_size! {0x1000}
76
77#[no_mangle]
81#[cfg_attr(not(target_os = "macos"), link_section = ".pde")]
82pub static mut PAGE_DIR: PD = [PDEntry(0); 1024];
83#[no_mangle]
84#[cfg_attr(not(target_os = "macos"), link_section = ".pte")]
85pub static mut PAGE_TABLE: PT = [PTEntry(0); 1024];
86
87fn init_virtio_dev(
99 dev: pci_x86::Device,
100 dev_type: VirtIODeviceType,
101) -> Option<(u8, VirtIOPCIDevice)> {
102 use pci_x86::cap::Cap;
103
104 let int_line = dev.int_line()?;
105
106 for cap in dev.capabilities() {
107 match cap {
108 Cap::Msi(cap) => {
109 cap.disable();
110 }
111 Cap::Msix(cap) => {
112 cap.disable();
113 }
114 _ => {}
115 }
116 }
117
118 let dev = VirtIOPCIDevice::from_pci_device(dev, dev_type)?;
119
120 Some((int_line, dev))
121}
122
123struct VirtioDevices {
126 rng: OptionalCell<(u8, &'static VirtIOPCIDevice)>,
127}
128
129impl InterruptService for VirtioDevices {
130 unsafe fn service_interrupt(&self, interrupt: u32) -> bool {
131 let mut handled = false;
132
133 self.rng.map(|(int_line, dev)| {
134 if interrupt == (int_line as u32) {
135 dev.handle_interrupt();
136 handled = true;
137 }
138 });
139
140 handled
141 }
142}
143
144pub struct QemuI386Q35Platform {
145 pconsole: &'static capsules_core::process_console::ProcessConsole<
146 'static,
147 { capsules_core::process_console::DEFAULT_COMMAND_HISTORY_LEN },
148 VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>,
149 components::process_console::Capability,
150 >,
151 console: &'static Console<'static>,
152 lldb: &'static capsules_core::low_level_debug::LowLevelDebug<
153 'static,
154 capsules_core::virtualizers::virtual_uart::UartDevice<'static>,
155 >,
156 alarm: &'static capsules_core::alarm::AlarmDriver<
157 'static,
158 VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>,
159 >,
160 ipc: IPC<{ NUM_PROCS as u8 }>,
161 scheduler: &'static CooperativeSched<'static>,
162 scheduler_timer: &'static SchedulerTimerHw,
163 rng: Option<&'static RngDriver<'static, VirtIORng<'static, 'static>>>,
164}
165
166impl SyscallDriverLookup for QemuI386Q35Platform {
167 fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
168 where
169 F: FnOnce(Option<&dyn SyscallDriver>) -> R,
170 {
171 match driver_num {
172 console::DRIVER_NUM => f(Some(self.console)),
173 alarm::DRIVER_NUM => f(Some(self.alarm)),
174 capsules_core::low_level_debug::DRIVER_NUM => f(Some(self.lldb)),
175 capsules_core::rng::DRIVER_NUM => {
176 if let Some(rng) = self.rng {
177 f(Some(rng))
178 } else {
179 f(None)
180 }
181 }
182 kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
183 _ => f(None),
184 }
185 }
186}
187
188impl<C: kernel::platform::chip::Chip> KernelResources<C> for QemuI386Q35Platform {
189 type SyscallDriverLookup = Self;
190 fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
191 self
192 }
193
194 type SyscallFilter = ();
195 fn syscall_filter(&self) -> &Self::SyscallFilter {
196 &()
197 }
198
199 type ProcessFault = ();
200 fn process_fault(&self) -> &Self::ProcessFault {
201 &()
202 }
203
204 type Scheduler = CooperativeSched<'static>;
205 fn scheduler(&self) -> &Self::Scheduler {
206 self.scheduler
207 }
208
209 type SchedulerTimer = SchedulerTimerHw;
210 fn scheduler_timer(&self) -> &Self::SchedulerTimer {
211 self.scheduler_timer
212 }
213
214 type WatchDog = ();
215 fn watchdog(&self) -> &Self::WatchDog {
216 &()
217 }
218
219 type ContextSwitchCallback = ();
220 fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
221 &()
222 }
223}
224#[allow(unsupported_calling_conventions)]
228#[no_mangle]
229unsafe extern "cdecl" fn main() {
230 kernel::deferred_call::initialize_deferred_call_state::<
234 <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
235 >();
236
237 let default_peripherals = unsafe {
240 static_init!(
241 PcDefaultPeripherals,
242 PcDefaultPeripherals::new(
243 (
244 (kernel::static_buf!(x86_q35::serial::SerialPort<'static>),),
245 (kernel::static_buf!(x86_q35::serial::SerialPort<'static>),),
246 (kernel::static_buf!(x86_q35::serial::SerialPort<'static>),),
247 (kernel::static_buf!(x86_q35::serial::SerialPort<'static>),),
248 kernel::static_buf!(x86_q35::vga_uart_driver::VgaText<'static>),
249 ),
250 &mut *ptr::addr_of_mut!(PAGE_DIR),
251 )
252 )
253 };
254 default_peripherals.setup_circular_deps();
255 let virtio_devs = static_init!(
256 VirtioDevices,
257 VirtioDevices {
258 rng: OptionalCell::empty(),
259 }
260 );
261 let chip: &'static Pc<PcDefaultPeripherals, VirtioDevices> = unsafe {
262 static_init!(
263 Pc<PcDefaultPeripherals, VirtioDevices>,
264 Pc::new(
265 &*default_peripherals,
266 &mut *ptr::addr_of_mut!(PAGE_DIR),
267 &mut *ptr::addr_of_mut!(PAGE_TABLE),
268 virtio_devs,
269 ),
270 )
271 };
272
273 let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
275 let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
276 let main_loop_cap = create_capability!(capabilities::MainLoopCapability);
277
278 let processes = components::process_array::ProcessArrayComponent::new()
280 .finalize(components::process_array_component_static!(NUM_PROCS));
281 PROCESSES = Some(processes);
282
283 let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
285
286 let uart_mux = components::console::UartMuxComponent::new(chip.com1, 115_200)
291 .finalize(components::uart_mux_component_static!());
292
293 let vga_uart_mux = components::console::UartMuxComponent::new(chip.vga, 115_200)
295 .finalize(components::uart_mux_component_static!());
296
297 let debug_uart_device = vga_uart_mux;
303
304 let mux_alarm = static_init!(
309 MuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>,
310 MuxAlarm::new(chip.pit),
311 );
312 hil::time::Alarm::set_alarm_client(chip.pit, mux_alarm);
313
314 let virtual_alarm_user = static_init!(
316 VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>,
317 VirtualMuxAlarm::new(mux_alarm)
318 );
319 virtual_alarm_user.setup();
320
321 let alarm = static_init!(
322 capsules_core::alarm::AlarmDriver<
323 'static,
324 VirtualMuxAlarm<'static, Pit<'static, RELOAD_1KHZ>>,
325 >,
326 capsules_core::alarm::AlarmDriver::new(
327 virtual_alarm_user,
328 board_kernel.create_grant(capsules_core::alarm::DRIVER_NUM, &memory_allocation_cap)
329 )
330 );
331 hil::time::Alarm::set_alarm_client(virtual_alarm_user, alarm);
332
333 let mut virtio_rng_dev = None;
340 for dev in pci_x86::iter() {
341 use virtio::devices::VirtIODeviceType;
342 use virtio_pci_x86::{DEVICE_ID_BASE, VENDOR_ID};
343
344 if dev.vendor_id() != VENDOR_ID {
346 continue;
347 }
348 let dev_id = dev.device_id();
349 if dev_id < DEVICE_ID_BASE {
350 continue;
351 }
352
353 let dev_id = (dev_id - DEVICE_ID_BASE) as u32;
355 let Some(dev_type) = VirtIODeviceType::from_device_id(dev_id) else {
356 continue;
357 };
358
359 if dev_type == VirtIODeviceType::EntropySource {
360 if virtio_rng_dev.is_some() {
362 continue;
363 }
364
365 virtio_rng_dev = Some(dev);
366 }
367 }
368
369 let virtio_rng: Option<&'static VirtIORng> = if let Some(rng_dev) = virtio_rng_dev {
372 use virtio::queues::split_queue::{
373 SplitVirtqueue, VirtqueueAvailableRing, VirtqueueDescriptors, VirtqueueUsedRing,
374 };
375 use virtio::queues::Virtqueue;
376 use virtio::transports::VirtIOTransport;
377
378 let (int_line, transport) = init_virtio_dev(rng_dev, VirtIODeviceType::EntropySource)
380 .expect("virtio pci init failed");
381 let transport = static_init!(VirtIOPCIDevice, transport);
382
383 let descriptors = static_init!(VirtqueueDescriptors<1>, VirtqueueDescriptors::default(),);
385 let available_ring =
386 static_init!(VirtqueueAvailableRing<1>, VirtqueueAvailableRing::default(),);
387 let used_ring = static_init!(VirtqueueUsedRing<1>, VirtqueueUsedRing::default(),);
388 let queue = static_init!(
389 SplitVirtqueue<1>,
390 SplitVirtqueue::new(descriptors, available_ring, used_ring),
391 );
392 queue.set_transport(transport);
393
394 let rng = static_init!(VirtIORng, VirtIORng::new(queue));
396 DeferredCallClient::register(rng);
397 queue.set_client(rng);
398
399 let queues = static_init!([&'static dyn Virtqueue; 1], [queue; 1]);
402 transport.initialize(rng, queues).unwrap();
403
404 let rng_buffer = static_init!([u8; 64], [0; 64]);
406 rng.provide_buffer(rng_buffer)
407 .expect("rng: providing initial buffer failed");
408
409 virtio_devs.rng.set((int_line, transport));
412
413 Some(rng)
414 } else {
415 None
416 };
417
418 chip.pit.start();
422
423 irq::enable();
425
426 let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
430 .finalize(components::process_printer_text_component_static!());
431 PROCESS_PRINTER = Some(process_printer);
432
433 let console_uart_device = uart_mux;
443
444 let pconsole = components::process_console::ProcessConsoleComponent::new(
446 board_kernel,
447 console_uart_device,
448 mux_alarm,
449 process_printer,
450 None,
451 )
452 .finalize(components::process_console_component_static!(
453 Pit<'static, RELOAD_1KHZ>
454 ));
455
456 let console = ConsoleComponent::new(board_kernel, console::DRIVER_NUM, console_uart_device)
458 .finalize(components::console_component_static!());
459
460 DebugWriterComponent::new::<<ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider>(
462 debug_uart_device,
463 create_capability!(capabilities::SetDebugWriterCapability),
464 )
465 .finalize(components::debug_writer_component_static!());
466
467 let lldb = components::lldb::LowLevelDebugComponent::new(
468 board_kernel,
469 capsules_core::low_level_debug::DRIVER_NUM,
470 uart_mux,
471 )
472 .finalize(components::low_level_debug_component_static!());
473
474 let rng_driver = virtio_rng.map(|rng| {
478 components::rng::RngRandomComponent::new(board_kernel, capsules_core::rng::DRIVER_NUM, rng)
479 .finalize(components::rng_random_component_static!(VirtIORng))
480 });
481
482 let scheduler = components::sched::cooperative::CooperativeComponent::new(processes)
483 .finalize(components::cooperative_component_static!(NUM_PROCS));
484
485 let scheduler_timer =
486 components::virtual_scheduler_timer::VirtualSchedulerTimerComponent::new(mux_alarm)
487 .finalize(components::virtual_scheduler_timer_component_static!(
488 AlarmHw
489 ));
490
491 let platform = QemuI386Q35Platform {
492 pconsole,
493 console,
494 alarm,
495 lldb,
496 scheduler,
497 scheduler_timer,
498 rng: rng_driver,
499 ipc: kernel::ipc::IPC::new(
500 board_kernel,
501 kernel::ipc::DRIVER_NUM,
502 &memory_allocation_cap,
503 ),
504 };
505
506 let _ = platform.pconsole.start();
508
509 debug!("QEMU i486 \"Q35\" machine, initialization complete.");
510 debug!("Entering main loop.");
511
512 extern "C" {
514 static _sapps: u8;
516 static _eapps: u8;
518 static mut _sappmem: u8;
520 static _eappmem: u8;
522 }
523
524 kernel::process::load_processes(
527 board_kernel,
528 chip,
529 core::slice::from_raw_parts(
530 ptr::addr_of!(_sapps),
531 ptr::addr_of!(_eapps) as usize - ptr::addr_of!(_sapps) as usize,
532 ),
533 core::slice::from_raw_parts_mut(
534 ptr::addr_of_mut!(_sappmem),
535 ptr::addr_of!(_eappmem) as usize - ptr::addr_of!(_sappmem) as usize,
536 ),
537 &FAULT_RESPONSE,
538 &process_mgmt_cap,
539 )
540 .unwrap_or_else(|err| {
541 debug!("Error loading processes!");
542 debug!("{:?}", err);
543 });
544
545 board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_cap);
546}