Skip to content

Commit

Permalink
Make network parameters and allowed PCI devices configurable via cmdline
Browse files Browse the repository at this point in the history
These cmdline parameters are used for running Kerla on DigitalOcean.
  • Loading branch information
nuta committed Dec 14, 2021
1 parent 1f9c2b4 commit eca680e
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 22 deletions.
8 changes: 6 additions & 2 deletions kernel/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,11 +182,15 @@ pub fn boot_kernel(#[cfg_attr(debug_assertions, allow(unused))] bootinfo: &BootI
profiler.lap_time("virtio_net init");

// Initialize device drivers.
kerla_api::kernel_ops::init_drivers(bootinfo.pci_enabled, &bootinfo.virtio_mmio_devices);
kerla_api::kernel_ops::init_drivers(
bootinfo.pci_enabled,
&bootinfo.pci_allowlist,
&bootinfo.virtio_mmio_devices,
);
profiler.lap_time("drivers init");

// Connect to the network.
net::init_and_start_dhcp_discover();
net::init_and_start_dhcp_discover(bootinfo);
profiler.lap_time("net init");

// Prepare the root file system.
Expand Down
77 changes: 65 additions & 12 deletions kernel/net/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use alloc::vec::Vec;
use atomic_refcell::AtomicRefCell;
use crossbeam::queue::ArrayQueue;
use kerla_api::driver::net::EthernetDriver;
use kerla_runtime::bootinfo::BootInfo;
use kerla_runtime::spinlock::SpinLock;
use kerla_utils::once::Once;
use smoltcp::wire::{self, EthernetAddress, IpCidr};
Expand Down Expand Up @@ -60,6 +61,7 @@ impl From<MonotonicClock> for Instant {
pub(self) static SOCKETS: Once<SpinLock<SocketSet>> = Once::new();
static INTERFACE: Once<SpinLock<EthernetInterface<OurDevice>>> = Once::new();
static DHCP_CLIENT: Once<SpinLock<Dhcpv4Client>> = Once::new();
static DHCP_ENABLED: Once<bool> = Once::new();
pub(self) static SOCKET_WAIT_QUEUE: Once<WaitQueue> = Once::new();

pub fn process_packets() {
Expand Down Expand Up @@ -180,12 +182,60 @@ pub fn use_ethernet_driver<F: FnOnce(&Box<dyn EthernetDriver>) -> R, R>(f: F) ->
f(driver.as_ref().expect("no ethernet drivers"))
}

pub fn init_and_start_dhcp_discover() {
#[derive(Debug)]
struct IPv4AddrParseError;

/// Parses an IPv4 address (e.g. "10.123.123.123").
fn parse_ipv4_addr(addr: &str) -> Result<wire::Ipv4Address, IPv4AddrParseError> {
let mut iter = addr.splitn(4, '.');
let mut octets = [0; 4];
for octet in &mut octets {
*octet = iter
.next()
.and_then(|s| s.parse().ok())
.ok_or(IPv4AddrParseError)?;
}

Ok(wire::Ipv4Address::from_bytes(&octets))
}

/// Parses an IPv4 address with the prefix length (e.g. "10.123.123.123/24").
fn parse_ipv4_addr_with_prefix_len(
addr: &str,
) -> Result<(wire::Ipv4Address, u8), IPv4AddrParseError> {
let mut iter = addr.splitn(2, '/');
let ip = parse_ipv4_addr(iter.next().unwrap())?;
let prefix_len = iter
.next()
.ok_or(IPv4AddrParseError)?
.parse()
.map_err(|_| IPv4AddrParseError)?;

Ok((ip, prefix_len))
}
pub fn init_and_start_dhcp_discover(bootinfo: &BootInfo) {
let ip_addrs = match &bootinfo.ip4 {
Some(ip4_str) => {
let (ip4, prefix_len) = parse_ipv4_addr_with_prefix_len(ip4_str)
.expect("bootinfo.ip4 should be formed as 10.0.0.1/24");
info!("net: using a static IPv4 address: {}/{}", ip4, prefix_len);
[IpCidr::new(ip4.into(), prefix_len)]
}
None => [IpCidr::new(wire::Ipv4Address::UNSPECIFIED.into(), 0)],
};

let mut routes = Routes::new(BTreeMap::new());
if let Some(gateway_ip4_str) = &bootinfo.gateway_ip4 {
let gateway_ip4 = parse_ipv4_addr(gateway_ip4_str)
.expect("bootinfo.gateway_ip4 should be formed as 10.0.0.1");
info!("net: using a static gateway IPv4 address: {}", gateway_ip4);
routes.add_default_ipv4_route(gateway_ip4).unwrap();
};

let neighbor_cache = NeighborCache::new(BTreeMap::new());

let mac_addr = use_ethernet_driver(|driver| driver.mac_addr());
let ethernet_addr = EthernetAddress(mac_addr.as_array());
let ip_addrs = [IpCidr::new(wire::Ipv4Address::UNSPECIFIED.into(), 0)];
let routes = Routes::new(BTreeMap::new());
let iface = EthernetInterfaceBuilder::new(OurDevice)
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache)
Expand All @@ -194,20 +244,23 @@ pub fn init_and_start_dhcp_discover() {
.finalize();

let mut sockets = SocketSet::new(vec![]);
let dhcp_rx_buffer = RawSocketBuffer::new([RawPacketMetadata::EMPTY; 4], vec![0; 2048]);
let dhcp_tx_buffer = RawSocketBuffer::new([RawPacketMetadata::EMPTY; 4], vec![0; 2048]);
let dhcp = Dhcpv4Client::new(
&mut sockets,
dhcp_rx_buffer,
dhcp_tx_buffer,
read_monotonic_clock().into(),
);

DHCP_ENABLED.init(|| bootinfo.dhcp_enabled);
if *DHCP_ENABLED {
let dhcp_rx_buffer = RawSocketBuffer::new([RawPacketMetadata::EMPTY; 4], vec![0; 2048]);
let dhcp_tx_buffer = RawSocketBuffer::new([RawPacketMetadata::EMPTY; 4], vec![0; 2048]);
let dhcp = Dhcpv4Client::new(
&mut sockets,
dhcp_rx_buffer,
dhcp_tx_buffer,
read_monotonic_clock().into(),
);
DHCP_CLIENT.init(|| SpinLock::new(dhcp));
}
RX_PACKET_QUEUE.init(|| SpinLock::new(ArrayQueue::new(128)));
SOCKET_WAIT_QUEUE.init(WaitQueue::new);
INTERFACE.init(|| SpinLock::new(iface));
SOCKETS.init(|| SpinLock::new(sockets));
DHCP_CLIENT.init(|| SpinLock::new(dhcp));

process_packets();
}
21 changes: 19 additions & 2 deletions libs/kerla_api/driver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pub mod pci;
pub use kerla_runtime::bootinfo::VirtioMmioDevice;

use alloc::boxed::Box;
use kerla_runtime::spinlock::SpinLock;
use kerla_runtime::{bootinfo::AllowedPciDevice, spinlock::SpinLock};

use self::pci::PciDevice;

Expand All @@ -33,10 +33,27 @@ pub fn attach_irq<F: FnMut() + Send + Sync + 'static>(irq: u8, f: F) {
kernel_ops().attach_irq(irq, Box::new(f))
}

pub fn init(pci_enabled: bool, mmio_devices: &[VirtioMmioDevice]) {
pub fn init(
pci_enabled: bool,
pci_allowlist: &[AllowedPciDevice],
mmio_devices: &[VirtioMmioDevice],
) {
// Scan PCI devices.
if pci_enabled {
for device in pci::enumerate_pci_devices() {
if !pci_allowlist.is_empty()
&& !pci_allowlist
.iter()
.any(|e| e.bus == device.bus() && e.slot == device.slot())
{
trace!(
"pci: skipping not allowed device: id={:04x}:{:04x}",
device.config().vendor_id(),
device.config().device_id(),
);
continue;
}

trace!(
"pci: found a device: id={:04x}:{:04x}, bar0={:016x?}, irq={}",
device.config().vendor_id(),
Expand Down
13 changes: 11 additions & 2 deletions libs/kerla_api/driver/pci.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,14 @@ impl PciDevice {
);
}

pub fn bus(&self) -> u8 {
self.bus
}

pub fn slot(&self) -> u8 {
self.slot
}

pub fn capabilities(&self) -> &[PciCapability] {
&self.capabilities
}
Expand Down Expand Up @@ -214,13 +222,14 @@ impl Iterator for PciScanner {
}

let config = self.bus.read_device_config(self.bus_no, self.slot);
let slot = self.slot;
self.slot += 1;

if let Some(config) = config {
let capabilities = self.bus.read_capabilities(self.bus_no, self.slot - 1);
let capabilities = self.bus.read_capabilities(self.bus_no, slot);
return Some(PciDevice {
bus: self.bus_no,
slot: self.slot - 1,
slot,
config,
capabilities,
});
Expand Down
10 changes: 7 additions & 3 deletions libs/kerla_api/kernel_ops.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Internal APIs exposed for kerla_kernel crate. **Don't use from your kernel extensions!**
use alloc::boxed::Box;
use kerla_runtime::bootinfo::VirtioMmioDevice;
use kerla_runtime::bootinfo::{AllowedPciDevice, VirtioMmioDevice};
use kerla_utils::static_cell::StaticCell;

use crate::driver::{self, net::EthernetDriver};
Expand Down Expand Up @@ -33,6 +33,10 @@ pub fn init(ops: &'static dyn KernelOps) {
set_kernel_ops(ops);
}

pub fn init_drivers(pci_enabled: bool, mmio_devices: &[VirtioMmioDevice]) {
driver::init(pci_enabled, mmio_devices);
pub fn init_drivers(
pci_enabled: bool,
pci_allowlist: &[AllowedPciDevice],
mmio_devices: &[VirtioMmioDevice],
) {
driver::init(pci_enabled, pci_allowlist, mmio_devices);
}
9 changes: 9 additions & 0 deletions runtime/bootinfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,19 @@ pub struct VirtioMmioDevice {
pub irq: u8,
}

pub struct AllowedPciDevice {
pub bus: u8,
pub slot: u8,
}

pub struct BootInfo {
pub ram_areas: ArrayVec<RamArea, 8>,
pub virtio_mmio_devices: ArrayVec<VirtioMmioDevice, 4>,
pub log_filter: ArrayString<64>,
pub pci_enabled: bool,
pub pci_allowlist: ArrayVec<AllowedPciDevice, 4>,
pub use_second_serialport: bool,
pub dhcp_enabled: bool,
pub ip4: Option<ArrayString<18>>,
pub gateway_ip4: Option<ArrayString<15>>,
}
61 changes: 60 additions & 1 deletion runtime/x64/bootinfo.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::address::{PAddr, VAddr};
use crate::bootinfo::{BootInfo, RamArea, VirtioMmioDevice};
use crate::bootinfo::{AllowedPciDevice, BootInfo, RamArea, VirtioMmioDevice};
use arrayvec::{ArrayString, ArrayVec};
use core::cmp::max;
use core::mem::size_of;
Expand Down Expand Up @@ -129,6 +129,10 @@ struct Cmdline {
pub virtio_mmio_devices: ArrayVec<VirtioMmioDevice, 4>,
pub log_filter: ArrayString<64>,
pub use_second_serialport: bool,
pub dhcp_enabled: bool,
pub ip4: Option<ArrayString<18>>,
pub gateway_ip4: Option<ArrayString<15>>,
pub pci_allowlist: ArrayVec<AllowedPciDevice, 4>,
}

impl Cmdline {
Expand All @@ -137,17 +141,38 @@ impl Cmdline {
info!("cmdline: {}", if s.is_empty() { "(empty)" } else { s });

let mut pci_enabled = true;
let mut pci_allowlist = ArrayVec::new();
let mut virtio_mmio_devices = ArrayVec::new();
let mut log_filter = ArrayString::new();
let mut use_second_serialport = false;
let mut dhcp_enabled = true;
let mut ip4 = None;
let mut gateway_ip4 = None;
if !s.is_empty() {
for config in s.split(' ') {
if config.is_empty() {
continue;
}

let mut words = config.splitn(2, '=');
match (words.next(), words.next()) {
(Some("pci"), Some("off")) => {
warn!("bootinfo: PCI disabled");
pci_enabled = false;
}
(Some("pci_device"), Some(bus_and_slot)) => {
warn!("bootinfo: allowed PCI device: {}", bus_and_slot);
let mut iter = bus_and_slot.splitn(2, ':');
let bus = iter
.next()
.and_then(|w| w.parse().ok())
.expect("bootinfo.bus_and_slot must be formed as bus:slot");
let slot = iter
.next()
.and_then(|w| w.parse().ok())
.expect("bootinfo.bus_and_slot must be formed as bus:slot");
pci_allowlist.push(AllowedPciDevice { bus, slot });
}
(Some("serial1"), Some("on")) => {
info!("bootinfo: secondary serial port enabled");
use_second_serialport = true;
Expand Down Expand Up @@ -176,6 +201,24 @@ impl Cmdline {
irq,
})
}
(Some("dhcp"), Some("off")) => {
warn!("bootinfo: DHCP disabled");
dhcp_enabled = false;
}
(Some("ip4"), Some(value)) => {
let mut s = ArrayString::new();
if s.try_push_str(value).is_err() {
warn!("bootinfo: ip4 is too long");
}
ip4 = Some(s);
}
(Some("gateway_ip4"), Some(value)) => {
let mut s = ArrayString::new();
if s.try_push_str(value).is_err() {
warn!("bootinfo: gateway_ip4 is too long");
}
gateway_ip4 = Some(s);
}
(Some(path), None) if path.starts_with('/') => {
// QEMU appends a kernel image path. Just ignore it.
}
Expand All @@ -188,9 +231,13 @@ impl Cmdline {

Cmdline {
pci_enabled,
pci_allowlist,
virtio_mmio_devices,
log_filter,
use_second_serialport,
dhcp_enabled,
ip4,
gateway_ip4,
}
}
}
Expand Down Expand Up @@ -287,9 +334,13 @@ unsafe fn parse_multiboot2_info(header: &Multiboot2InfoHeader) -> BootInfo {
BootInfo {
ram_areas,
pci_enabled: cmdline.pci_enabled,
pci_allowlist: cmdline.pci_allowlist,
virtio_mmio_devices: cmdline.virtio_mmio_devices,
log_filter: cmdline.log_filter,
use_second_serialport: cmdline.use_second_serialport,
dhcp_enabled: cmdline.dhcp_enabled,
ip4: cmdline.ip4,
gateway_ip4: cmdline.gateway_ip4,
}
}

Expand Down Expand Up @@ -328,9 +379,13 @@ unsafe fn parse_multiboot_legacy_info(info: &MultibootLegacyInfo) -> BootInfo {
BootInfo {
ram_areas,
pci_enabled: cmdline.pci_enabled,
pci_allowlist: cmdline.pci_allowlist,
virtio_mmio_devices: cmdline.virtio_mmio_devices,
log_filter: cmdline.log_filter,
use_second_serialport: cmdline.use_second_serialport,
dhcp_enabled: cmdline.dhcp_enabled,
ip4: cmdline.ip4,
gateway_ip4: cmdline.gateway_ip4,
}
}

Expand Down Expand Up @@ -359,9 +414,13 @@ unsafe fn parse_linux_boot_params(boot_params: PAddr) -> BootInfo {
BootInfo {
ram_areas,
pci_enabled: cmdline.pci_enabled,
pci_allowlist: cmdline.pci_allowlist,
virtio_mmio_devices: cmdline.virtio_mmio_devices,
log_filter: cmdline.log_filter,
use_second_serialport: cmdline.use_second_serialport,
dhcp_enabled: cmdline.dhcp_enabled,
ip4: cmdline.ip4,
gateway_ip4: cmdline.gateway_ip4,
}
}

Expand Down

0 comments on commit eca680e

Please sign in to comment.