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