1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
#![no_std]
#![cfg_attr(target_arch = "x86_64", feature(trait_alias))]
extern crate alloc;
use log::*;
#[cfg(target_arch = "x86_64")]
use {
mpmc::Queue,
event_types::Event,
memory::MemoryManagementInfo,
alloc::vec::Vec,
io::{ByteReaderWriterWrapper, LockableIo, ReaderWriter},
storage_manager::StorageDevice,
memory::PhysicalAddress,
serial_port::{SerialPortAddress, init_serial_port, take_serial_port_basic},
};
/// Performs early-stage initialization for simple devices needed during early boot.
///
/// This includes:
/// * [`acpi`] tables for system configuration info, including the IOAPIC.
#[cfg(target_arch = "x86_64")]
pub fn early_init(
rsdp_address: Option<PhysicalAddress>,
kernel_mmi: &mut MemoryManagementInfo
) -> Result<(), &'static str> {
// Parse the ACPI tables to acquire system configuration info.
acpi::init(rsdp_address, &mut kernel_mmi.page_table)?;
Ok(())
}
/// Initializes all other devices not initialized during [`early_init()`].
///
/// Devices include:
/// * At least one [`serial_port`] (e.g., `COM1`) with full interrupt support,
/// * The fully-featured system [`logger`],
/// * The legacy PS2 controller and any connected devices: [`keyboard`] and [`mouse`],
/// * All other devices discovered on the [`pci`] bus.
pub fn init(
#[cfg(target_arch = "x86_64")]
key_producer: Queue<Event>,
#[cfg(target_arch = "x86_64")]
mouse_producer: Queue<Event>,
) -> Result<(), &'static str> {
let serial_ports = logger::take_early_log_writers();
let logger_writers = IntoIterator::into_iter(serial_ports)
.flatten()
.filter_map(|sp| serial_port::init_serial_port(sp.base_port_address(), sp))
.cloned();
logger::init(None, logger_writers);
info!("Initialized full logger.");
// COM1 is the only UART on aarch64; it's used for logging as well as for the console.
#[cfg(target_arch = "x86_64")] {
// Ensure that both COM1 and COM2 are initialized, for logging and/or headless operation.
// If a serial port was used for logging (as configured in [`logger::early_init()`]),
// ignore its inputs for purposes of starting new console instances.
let init_serial_port = |spa: SerialPortAddress| {
if let Some(sp) = take_serial_port_basic(spa) {
init_serial_port(spa, sp);
} else {
console::ignore_serial_port_input(spa as u16);
info!("Ignoring input on {:?} because it is being used for logging.", spa);
}
};
init_serial_port(SerialPortAddress::COM1);
init_serial_port(SerialPortAddress::COM2);
}
// PS/2 is x86_64 only
#[cfg(target_arch = "x86_64")] {
let ps2_controller = ps2::init()?;
if let Some(kb) = ps2_controller.keyboard_ref() {
keyboard::init(kb, key_producer)?;
}
if let Some(m) = ps2_controller.mouse_ref() {
mouse::init(m, mouse_producer)?;
}
}
// Initialize/scan the PCI bus to discover PCI devices
for dev in pci::pci_device_iter()? {
debug!("Found PCI device: {:X?}", dev);
}
// store all the initialized ixgbe NICs here to be added to the network interface list
// No NIC support on aarch64 at the moment
#[cfg(target_arch = "x86_64")]
let mut ixgbe_devs = Vec::new();
// Iterate over all PCI devices and initialize the drivers for the devices we support.
for dev in pci::pci_device_iter()? {
// Currently we skip Bridge devices, since we have no use for them yet.
if dev.class == 0x06 {
continue;
}
// If this is a storage device, initialize it as such.
// No storage device support on aarch64 at the moment
#[cfg(target_arch = "x86_64")]
match storage_manager::init_device(dev) {
// Successfully initialized this storage device.
Ok(Some(_storage_controller)) => continue,
// Not a storage device, so fall through and let another handler deal with it.
Ok(None) => { }
// Error initializing this device, so skip it.
Err(e) => {
error!("Failed to initialize storage device, it will be unavailable.\n{:?}\nError: {}", dev, e);
continue;
}
}
// If this is a network device, initialize it as such.
// Look for networking controllers, specifically ethernet cards
// No NIC support on aarch64 at the moment
#[cfg(target_arch = "x86_64")]
if dev.class == 0x02 && dev.subclass == 0x00 {
if dev.vendor_id == e1000::INTEL_VEND && dev.device_id == e1000::E1000_DEV {
info!("e1000 PCI device found at: {:?}", dev.location);
let nic = e1000::E1000Nic::init(dev)?;
let interface = net::register_device(nic);
nic.lock().init_interrupts(interface)?;
continue;
}
if dev.vendor_id == ixgbe::INTEL_VEND && dev.device_id == ixgbe::INTEL_82599 {
info!("ixgbe PCI device found at: {:?}", dev.location);
// Initialization parameters of the NIC.
// These can be changed according to the requirements specified in the ixgbe init function.
const VIRT_ENABLED: bool = true;
const RSS_ENABLED: bool = false;
const RX_DESCS: u16 = 8;
const TX_DESCS: u16 = 8;
let ixgbe_nic = ixgbe::IxgbeNic::init(
dev,
dev.location,
VIRT_ENABLED,
None,
RSS_ENABLED,
ixgbe::RxBufferSizeKiB::Buffer2KiB,
RX_DESCS,
TX_DESCS
)?;
ixgbe_devs.push(ixgbe_nic);
continue;
}
if dev.vendor_id == mlx5::MLX_VEND && (dev.device_id == mlx5::CONNECTX5_DEV || dev.device_id == mlx5::CONNECTX5_EX_DEV) {
info!("mlx5 PCI device found at: {:?}", dev.location);
const RX_DESCS: usize = 512;
const TX_DESCS: usize = 8192;
const MAX_MTU: u16 = 9000;
mlx5::ConnectX5Nic::init(dev, TX_DESCS, RX_DESCS, MAX_MTU)?;
continue;
}
// here: check for and initialize other ethernet cards
}
warn!("Ignoring PCI device with no handler. {:X?}", dev);
}
// Once all the NICs have been initialized, we can store them and add them to the list of network interfaces.
// No NIC support on aarch64 at the moment
#[cfg(target_arch = "x86_64")] {
let ixgbe_nics = ixgbe::IXGBE_NICS.call_once(|| ixgbe_devs);
for ixgbe_nic_ref in ixgbe_nics.iter() {
net::register_device(ixgbe_nic_ref);
}
}
// Convenience notification for developers to inform them of no networking devices
// No NIC support on aarch64 at the moment
#[cfg(target_arch = "x86_64")]
if net::get_default_interface().is_none() {
warn!("Note: no network devices found on this system.");
}
// Discover filesystems from each storage device on the storage controllers initialized above
// and mount each filesystem to the root directory by default.
// No storage device support on aarch64 at the moment
#[cfg(target_arch = "x86_64")]
if false {
for storage_device in storage_manager::storage_devices() {
let disk = fatfs_adapter::FatFsAdapter::new(
ReaderWriter::new(
ByteReaderWriterWrapper::from(
LockableIo::<dyn StorageDevice + Send, spin::Mutex<_>, _>::from(storage_device)
)
),
);
if let Ok(filesystem) = fatfs::FileSystem::new(disk, fatfs::FsOptions::new()) {
debug!("FATFS data:
fat_type: {:?},
volume_id: {:X?},
volume_label: {:?},
cluster_size: {:?},
status_flags: {:?},
stats: {:?}",
filesystem.fat_type(),
filesystem.volume_id(),
filesystem.volume_label(),
filesystem.cluster_size(),
filesystem.read_status_flags(),
filesystem.stats(),
);
let root = filesystem.root_dir();
debug!("Root directory contents:");
for f in root.iter() {
debug!("\t {:X?}", f.map(|entry| (entry.file_name(), entry.attributes(), entry.len())));
}
}
}
}
Ok(())
}
#[cfg(target_arch = "x86_64")]
mod fatfs_adapter {
// TODO: move the following `FatFsAdapter` stuff into a separate crate.
use derive_more::{From, Into};
/// An adapter (wrapper type) that implements traits required by the [`fatfs`] crate
/// for any I/O device that wants to be usable by [`fatfs`].
///
/// To meet [`fatfs`]'s requirements, the underlying I/O stream must be able to
/// read, write, and seek while tracking its current offset.
/// We use traits from the [`core2`] crate to meet these requirements,
/// thus, the given `IO` parameter must implement those [`core2`] traits.
///
/// For example, this allows one to access a FAT filesystem
/// by reading from or writing to a storage device.
pub struct FatFsAdapter<IO>(IO);
impl<IO> FatFsAdapter<IO> {
pub fn new(io: IO) -> FatFsAdapter<IO> { FatFsAdapter(io) }
}
/// This tells the `fatfs` crate that our read/write/seek functions
/// may return errors of the type [`FatFsIoErrorAdapter`],
/// which is a simple wrapper around [`core2::io::Error`].
impl<IO> fatfs::IoBase for FatFsAdapter<IO> {
type Error = FatFsIoErrorAdapter;
}
impl<IO> fatfs::Read for FatFsAdapter<IO> where IO: core2::io::Read {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
self.0.read(buf).map_err(Into::into)
}
}
impl<IO> fatfs::Write for FatFsAdapter<IO> where IO: core2::io::Write {
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
self.0.write(buf).map_err(Into::into)
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.0.flush().map_err(Into::into)
}
}
impl<IO> fatfs::Seek for FatFsAdapter<IO> where IO: core2::io::Seek {
fn seek(&mut self, pos: fatfs::SeekFrom) -> Result<u64, Self::Error> {
let core2_pos = match pos {
fatfs::SeekFrom::Start(s) => core2::io::SeekFrom::Start(s),
fatfs::SeekFrom::Current(c) => core2::io::SeekFrom::Current(c),
fatfs::SeekFrom::End(e) => core2::io::SeekFrom::End(e),
};
self.0.seek(core2_pos).map_err(Into::into)
}
}
/// This struct exists to enable us to implement the [`fatfs::IoError`] trait
/// for the [`core2::io::Error`] trait.
///
/// This is required because Rust prevents implementing foreign traits for foreign types.
#[derive(Debug, From, Into)]
pub struct FatFsIoErrorAdapter(core2::io::Error);
impl fatfs::IoError for FatFsIoErrorAdapter {
fn is_interrupted(&self) -> bool {
self.0.kind() == core2::io::ErrorKind::Interrupted
}
fn new_unexpected_eof_error() -> Self {
FatFsIoErrorAdapter(core2::io::ErrorKind::UnexpectedEof.into())
}
fn new_write_zero_error() -> Self {
FatFsIoErrorAdapter(core2::io::ErrorKind::WriteZero.into())
}
}
}