#![no_std]
#![cfg_attr(not(doc), no_main)]
use core::ptr::addr_of;
use core::ptr::addr_of_mut;
use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
use kernel::capabilities;
use kernel::component::Component;
use kernel::hil;
use kernel::platform::scheduler_timer::VirtualSchedulerTimer;
use kernel::platform::KernelResources;
use kernel::platform::SyscallDriverLookup;
use kernel::scheduler::cooperative::CooperativeSched;
use kernel::utilities::registers::interfaces::ReadWriteable;
use kernel::{create_capability, debug, static_init};
use qemu_rv32_virt_chip::chip::{QemuRv32VirtChip, QemuRv32VirtDefaultPeripherals};
use rv32i::csr;
pub mod io;
pub const NUM_PROCS: usize = 4;
static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] =
[None; NUM_PROCS];
static mut CHIP: Option<&'static QemuRv32VirtChip<QemuRv32VirtDefaultPeripherals>> = None;
static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
None;
const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
capsules_system::process_policies::PanicFaultPolicy {};
#[no_mangle]
#[link_section = ".stack_buffer"]
pub static mut STACK_MEMORY: [u8; 0x8000] = [0; 0x8000];
struct QemuRv32VirtPlatform {
pconsole: &'static capsules_core::process_console::ProcessConsole<
'static,
{ capsules_core::process_console::DEFAULT_COMMAND_HISTORY_LEN },
capsules_core::virtualizers::virtual_alarm::VirtualMuxAlarm<
'static,
qemu_rv32_virt_chip::chip::QemuRv32VirtClint<'static>,
>,
components::process_console::Capability,
>,
console: &'static capsules_core::console::Console<'static>,
lldb: &'static capsules_core::low_level_debug::LowLevelDebug<
'static,
capsules_core::virtualizers::virtual_uart::UartDevice<'static>,
>,
alarm: &'static capsules_core::alarm::AlarmDriver<
'static,
VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint<'static>>,
>,
ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
scheduler: &'static CooperativeSched<'static>,
scheduler_timer: &'static VirtualSchedulerTimer<
VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint<'static>>,
>,
virtio_rng: Option<
&'static capsules_core::rng::RngDriver<
'static,
qemu_rv32_virt_chip::virtio::devices::virtio_rng::VirtIORng<'static, 'static>,
>,
>,
}
impl SyscallDriverLookup for QemuRv32VirtPlatform {
fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
where
F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
{
match driver_num {
capsules_core::console::DRIVER_NUM => f(Some(self.console)),
capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
capsules_core::low_level_debug::DRIVER_NUM => f(Some(self.lldb)),
capsules_core::rng::DRIVER_NUM => {
if let Some(rng_driver) = self.virtio_rng {
f(Some(rng_driver))
} else {
f(None)
}
}
kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
_ => f(None),
}
}
}
impl
KernelResources<
qemu_rv32_virt_chip::chip::QemuRv32VirtChip<
'static,
QemuRv32VirtDefaultPeripherals<'static>,
>,
> for QemuRv32VirtPlatform
{
type SyscallDriverLookup = Self;
type SyscallFilter = ();
type ProcessFault = ();
type Scheduler = CooperativeSched<'static>;
type SchedulerTimer = VirtualSchedulerTimer<
VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint<'static>>,
>;
type WatchDog = ();
type ContextSwitchCallback = ();
fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
self
}
fn syscall_filter(&self) -> &Self::SyscallFilter {
&()
}
fn process_fault(&self) -> &Self::ProcessFault {
&()
}
fn scheduler(&self) -> &Self::Scheduler {
self.scheduler
}
fn scheduler_timer(&self) -> &Self::SchedulerTimer {
self.scheduler_timer
}
fn watchdog(&self) -> &Self::WatchDog {
&()
}
fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
&()
}
}
#[inline(never)]
unsafe fn start() -> (
&'static kernel::Kernel,
QemuRv32VirtPlatform,
&'static qemu_rv32_virt_chip::chip::QemuRv32VirtChip<
'static,
QemuRv32VirtDefaultPeripherals<'static>,
>,
) {
extern "C" {
static _sapps: u8;
static _eapps: u8;
static mut _sappmem: u8;
static _eappmem: u8;
static _stext: u8;
static _etext: u8;
static _sflash: u8;
static _eflash: u8;
static _ssram: u8;
static _esram: u8;
}
rv32i::configure_trap_handler();
let epmp = rv32i::pmp::kernel_protection_mml_epmp::KernelProtectionMMLEPMP::new(
rv32i::pmp::kernel_protection_mml_epmp::FlashRegion(
rv32i::pmp::NAPOTRegionSpec::new(
core::ptr::addr_of!(_sflash),
core::ptr::addr_of!(_eflash) as usize - core::ptr::addr_of!(_sflash) as usize,
)
.unwrap(),
),
rv32i::pmp::kernel_protection_mml_epmp::RAMRegion(
rv32i::pmp::NAPOTRegionSpec::new(
core::ptr::addr_of!(_ssram),
core::ptr::addr_of!(_esram) as usize - core::ptr::addr_of!(_ssram) as usize,
)
.unwrap(),
),
rv32i::pmp::kernel_protection_mml_epmp::MMIORegion(
rv32i::pmp::NAPOTRegionSpec::new(
core::ptr::null::<u8>(), 0x20000000, )
.unwrap(),
),
rv32i::pmp::kernel_protection_mml_epmp::KernelTextRegion(
rv32i::pmp::TORRegionSpec::new(
core::ptr::addr_of!(_stext),
core::ptr::addr_of!(_etext),
)
.unwrap(),
),
)
.unwrap();
let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(&*addr_of!(PROCESSES)));
let peripherals = static_init!(
QemuRv32VirtDefaultPeripherals,
QemuRv32VirtDefaultPeripherals::new(),
);
let uart_mux = components::console::UartMuxComponent::new(&peripherals.uart0, 115200)
.finalize(components::uart_mux_component_static!());
let hardware_timer = static_init!(
qemu_rv32_virt_chip::chip::QemuRv32VirtClint,
qemu_rv32_virt_chip::chip::QemuRv32VirtClint::new(&qemu_rv32_virt_chip::clint::CLINT_BASE)
);
let mux_alarm = static_init!(
MuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint>,
MuxAlarm::new(hardware_timer)
);
hil::time::Alarm::set_alarm_client(hardware_timer, mux_alarm);
let systick_virtual_alarm = static_init!(
VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint>,
VirtualMuxAlarm::new(mux_alarm)
);
systick_virtual_alarm.setup();
let virtual_alarm_user = static_init!(
VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint>,
VirtualMuxAlarm::new(mux_alarm)
);
virtual_alarm_user.setup();
let alarm = static_init!(
capsules_core::alarm::AlarmDriver<
'static,
VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint>,
>,
capsules_core::alarm::AlarmDriver::new(
virtual_alarm_user,
board_kernel.create_grant(capsules_core::alarm::DRIVER_NUM, &memory_allocation_cap)
)
);
hil::time::Alarm::set_alarm_client(virtual_alarm_user, alarm);
let (mut virtio_net_idx, mut virtio_rng_idx) = (None, None);
for (i, virtio_device) in peripherals.virtio_mmio.iter().enumerate() {
use qemu_rv32_virt_chip::virtio::devices::VirtIODeviceType;
match virtio_device.query() {
Some(VirtIODeviceType::NetworkCard) => {
virtio_net_idx = Some(i);
}
Some(VirtIODeviceType::EntropySource) => {
virtio_rng_idx = Some(i);
}
_ => (),
}
}
let virtio_rng_driver: Option<
&'static capsules_core::rng::RngDriver<
'static,
qemu_rv32_virt_chip::virtio::devices::virtio_rng::VirtIORng<'static, 'static>,
>,
> = if let Some(rng_idx) = virtio_rng_idx {
use kernel::hil::rng::Rng;
use qemu_rv32_virt_chip::virtio::devices::virtio_rng::VirtIORng;
use qemu_rv32_virt_chip::virtio::queues::split_queue::{
SplitVirtqueue, VirtqueueAvailableRing, VirtqueueDescriptors, VirtqueueUsedRing,
};
use qemu_rv32_virt_chip::virtio::queues::Virtqueue;
use qemu_rv32_virt_chip::virtio::transports::VirtIOTransport;
let descriptors = static_init!(VirtqueueDescriptors<1>, VirtqueueDescriptors::default(),);
let available_ring =
static_init!(VirtqueueAvailableRing<1>, VirtqueueAvailableRing::default(),);
let used_ring = static_init!(VirtqueueUsedRing<1>, VirtqueueUsedRing::default(),);
let queue = static_init!(
SplitVirtqueue<1>,
SplitVirtqueue::new(descriptors, available_ring, used_ring),
);
queue.set_transport(&peripherals.virtio_mmio[rng_idx]);
let rng = static_init!(VirtIORng, VirtIORng::new(queue));
kernel::deferred_call::DeferredCallClient::register(rng);
queue.set_client(rng);
let mmio_queues = static_init!([&'static dyn Virtqueue; 1], [queue; 1]);
peripherals.virtio_mmio[rng_idx]
.initialize(rng, mmio_queues)
.unwrap();
let rng_buffer = static_init!([u8; 64], [0; 64]);
rng.provide_buffer(rng_buffer)
.expect("rng: providing initial buffer failed");
let rng_driver = static_init!(
capsules_core::rng::RngDriver<VirtIORng>,
capsules_core::rng::RngDriver::new(
rng,
board_kernel.create_grant(capsules_core::rng::DRIVER_NUM, &memory_allocation_cap),
),
);
rng.set_client(rng_driver);
Some(rng_driver as &'static capsules_core::rng::RngDriver<VirtIORng>)
} else {
None
};
let _virtio_net_if: Option<
&'static qemu_rv32_virt_chip::virtio::devices::virtio_net::VirtIONet<'static>,
> = if let Some(net_idx) = virtio_net_idx {
use qemu_rv32_virt_chip::virtio::devices::virtio_net::VirtIONet;
use qemu_rv32_virt_chip::virtio::queues::split_queue::{
SplitVirtqueue, VirtqueueAvailableRing, VirtqueueDescriptors, VirtqueueUsedRing,
};
use qemu_rv32_virt_chip::virtio::queues::Virtqueue;
use qemu_rv32_virt_chip::virtio::transports::VirtIOTransport;
let tx_descriptors =
static_init!(VirtqueueDescriptors<2>, VirtqueueDescriptors::default(),);
let tx_available_ring =
static_init!(VirtqueueAvailableRing<2>, VirtqueueAvailableRing::default(),);
let tx_used_ring = static_init!(VirtqueueUsedRing<2>, VirtqueueUsedRing::default(),);
let tx_queue = static_init!(
SplitVirtqueue<2>,
SplitVirtqueue::new(tx_descriptors, tx_available_ring, tx_used_ring),
);
tx_queue.set_transport(&peripherals.virtio_mmio[net_idx]);
let rx_descriptors =
static_init!(VirtqueueDescriptors<2>, VirtqueueDescriptors::default(),);
let rx_available_ring =
static_init!(VirtqueueAvailableRing<2>, VirtqueueAvailableRing::default(),);
let rx_used_ring = static_init!(VirtqueueUsedRing<2>, VirtqueueUsedRing::default(),);
let rx_queue = static_init!(
SplitVirtqueue<2>,
SplitVirtqueue::new(rx_descriptors, rx_available_ring, rx_used_ring),
);
rx_queue.set_transport(&peripherals.virtio_mmio[net_idx]);
let tx_header_buf = static_init!([u8; 12], [0; 12]);
let rx_header_buf = static_init!([u8; 12], [0; 12]);
let rx_buffer = static_init!([u8; 1526], [0; 1526]);
let virtio_net = static_init!(
VirtIONet<'static>,
VirtIONet::new(
0,
tx_queue,
tx_header_buf,
rx_queue,
rx_header_buf,
rx_buffer,
),
);
tx_queue.set_client(virtio_net);
rx_queue.set_client(virtio_net);
let mmio_queues = static_init!([&'static dyn Virtqueue; 2], [rx_queue, tx_queue]);
peripherals.virtio_mmio[net_idx]
.initialize(virtio_net, mmio_queues)
.unwrap();
Some(virtio_net as &'static VirtIONet)
} else {
None
};
let chip = static_init!(
QemuRv32VirtChip<QemuRv32VirtDefaultPeripherals>,
QemuRv32VirtChip::new(peripherals, hardware_timer, epmp),
);
CHIP = Some(chip);
chip.enable_plic_interrupts();
csr::CSR
.mie
.modify(csr::mie::mie::mext::SET + csr::mie::mie::msoft::SET + csr::mie::mie::mtimer::SET);
csr::CSR.mstatus.modify(csr::mstatus::mstatus::mie::SET);
let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
.finalize(components::process_printer_text_component_static!());
PROCESS_PRINTER = Some(process_printer);
let pconsole = components::process_console::ProcessConsoleComponent::new(
board_kernel,
uart_mux,
mux_alarm,
process_printer,
None,
)
.finalize(components::process_console_component_static!(
qemu_rv32_virt_chip::chip::QemuRv32VirtClint
));
let console = components::console::ConsoleComponent::new(
board_kernel,
capsules_core::console::DRIVER_NUM,
uart_mux,
)
.finalize(components::console_component_static!());
components::debug_writer::DebugWriterComponent::new(uart_mux)
.finalize(components::debug_writer_component_static!());
let lldb = components::lldb::LowLevelDebugComponent::new(
board_kernel,
capsules_core::low_level_debug::DRIVER_NUM,
uart_mux,
)
.finalize(components::low_level_debug_component_static!());
let scheduler =
components::sched::cooperative::CooperativeComponent::new(&*addr_of!(PROCESSES))
.finalize(components::cooperative_component_static!(NUM_PROCS));
let scheduler_timer = static_init!(
VirtualSchedulerTimer<
VirtualMuxAlarm<'static, qemu_rv32_virt_chip::chip::QemuRv32VirtClint<'static>>,
>,
VirtualSchedulerTimer::new(systick_virtual_alarm)
);
let platform = QemuRv32VirtPlatform {
pconsole,
console,
alarm,
lldb,
scheduler,
scheduler_timer,
virtio_rng: virtio_rng_driver,
ipc: kernel::ipc::IPC::new(
board_kernel,
kernel::ipc::DRIVER_NUM,
&memory_allocation_cap,
),
};
let _ = platform.pconsole.start();
debug!("QEMU RISC-V 32-bit \"virt\" machine, initialization complete.");
debug!("Entering main loop.");
kernel::process::load_processes(
board_kernel,
chip,
core::slice::from_raw_parts(
core::ptr::addr_of!(_sapps),
core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
),
core::slice::from_raw_parts_mut(
core::ptr::addr_of_mut!(_sappmem),
core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
),
&mut *addr_of_mut!(PROCESSES),
&FAULT_RESPONSE,
&process_mgmt_cap,
)
.unwrap_or_else(|err| {
debug!("Error loading processes!");
debug!("{:?}", err);
});
(board_kernel, platform, chip)
}
#[no_mangle]
pub unsafe fn main() {
let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
let (board_kernel, platform, chip) = start();
board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_capability);
}