Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • pigeonmoelleux/skavos
1 result
Show changes
//! Implementation of the Peripheral Component Interconnect (PCI) used
//!
//! Read more informations about this specification on the [OSdev wiki](https://wiki.osdev.org/PCI)
use alloc::collections::btree_map::Entry;
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use x86_64::instructions::port::Port;
/// Offset mask for the [`CONFIG_ADDRESS`] register
const CONFIG_ADDRESS_OFFSET_MASK: u8 = 0xFC;
/// Specifies the configuration address that is required to be accesses
const CONFIG_ADDRESS: u16 = 0xCF8;
/// Accessing this register generates the configuration access and transfers the data to or from this register
const CONFIG_DATA: u16 = 0xCFC;
/// Vendor ID offset for every PCI compliant device
const VENDOR_ID_OFFSET: u8 = 0x00;
/// Device ID offset for every PCI compliant device
const DEVICE_ID_OFFSET: u8 = 0x02;
/// Command offset for every PCI compliant device
const COMMAND_OFFSET: u8 = 0x04;
/// Status offset for every PCI compliant device
const STATUS_OFFSET: u8 = 0x06;
/// Revision ID offset for every PCI compliant device
const REVISION_ID_OFFSET: u8 = 0x08;
/// Prog IF offset for every PCI compliant device
const PROG_IF_OFFSET: u8 = 0x09;
/// Subclass offset for every PCI compliant device
const SUBCLASS_OFFSET: u8 = 0x0A;
/// Class code offset for every PCI compliant device
const CLASS_CODE_OFFSET: u8 = 0x0B;
/// Cache line size offset for every PCI compliant device
const CACHE_LINE_SIZE_OFFSET: u8 = 0x0C;
/// Latency timer offset for every PCI compliant device
const LATENCY_TIMER_OFFSET: u8 = 0x0D;
/// Header type offset for every PCI compliant device
const HEADER_TYPE_OFFSET: u8 = 0x0E;
/// BIST offset for every PCI compliant device
const BIST_OFFSET: u8 = 0x0F;
/// Interrupt Line offset for every PCI compliant device with a header type of 0x0, 0x1 or 0x2
const INTERRUPT_LINE_OFFSET: u8 = 0x3C;
/// Interrupt PIN offset for every PCI compliant device with a header type of 0x0, 0x1 or 0x2
const INTERRUPT_PIN_OFFSET: u8 = 0x3D;
/// Base address #0 offset for every PCI compliant device with a header type of 0x0
const BASE_ADDRESS_0_HEADER_0_OFFSET: u8 = 0x10;
/// Base address #1 offset for every PCI compliant device with a header type of 0x0
const BASE_ADDRESS_1_HEADER_0_OFFSET: u8 = 0x14;
/// Base address #2 offset for every PCI compliant device with a header type of 0x0
const BASE_ADDRESS_2_HEADER_0_OFFSET: u8 = 0x18;
/// Base address #3 offset for every PCI compliant device with a header type of 0x0
const BASE_ADDRESS_3_HEADER_0_OFFSET: u8 = 0x1C;
/// Base address #4 offset for every PCI compliant device with a header type of 0x0
const BASE_ADDRESS_4_HEADER_0_OFFSET: u8 = 0x20;
/// Base address #5 offset for every PCI compliant device with a header type of 0x0
const BASE_ADDRESS_5_HEADER_0_OFFSET: u8 = 0x24;
/// Cardbus CIS Pointer offset for every PCI compliant device with a header type of 0x0
const CARDBUS_CIS_POINTER_HEADER_0_OFFSET: u8 = 0x28;
/// Subsystem Vendor ID offset for every PCI compliant device with a header type of 0x0
const SUBSYSTEM_VENDOR_ID_HEADER_0_OFFSET: u8 = 0x2C;
/// Subsystem ID offset for every PCI compliant device with a header type of 0x0
const SUBSYSTEM_ID_HEADER_0_OFFSET: u8 = 0x2E;
/// Expansion ROM base address offset for every PCI compliant device with a header type of 0x0
const EXPANSION_ROM_BASE_ADDRESS_HEADER_0_OFFSET: u8 = 0x30;
/// Capabilities Pointer offset for every PCI compliant device with a header type of 0x0
const CAPABILITIES_POINTER_HEADER_0_OFFSET: u8 = 0x34;
/// Min Grant offset for every PCI compliant device with a header type of 0x0
const MIN_GRANT_OFFSET_HEADER_0_OFFSET: u8 = 0x3E;
/// Max Grant offset for every PCI compliant device with a header type of 0x0
const MAX_GRANT_OFFSET_HEADER_0_OFFSET: u8 = 0x3F;
/// Base address #0 offset for every PCI compliant device with a header type of 0x1
const BASE_ADDRESS_0_HEADER_1_OFFSET: u8 = 0x10;
/// Base address #1 offset for every PCI compliant device with a header type of 0x1
const BASE_ADDRESS_1_HEADER_1_OFFSET: u8 = 0x14;
/// Primary bus number offset for every PCI compliant device with a header type of 0x1
const PRIMARY_BUS_NUMBER_HEADER_1_OFFSET: u8 = 0x18;
/// Secondary bus number offset for every PCI compliant device with a header type of 0x1
const SECONDARY_BUS_NUMBER_HEADER_1_OFFSET: u8 = 0x19;
/// Subordinate bus number offset for every PCI compliant device with a header type of 0x1
const SUBORDINATE_BUS_NUMBER_HEADER_1_OFFSET: u8 = 0x1A;
/// Secondary latency timer offset for every PCI compliant device with a header type of 0x1
const SECONDARY_LATENCY_TIMER_HEADER_1_OFFSET: u8 = 0x1B;
/// I/O base offset for every PCI compliant device with a header type of 0x1
const IO_BASE_HEADER_1_OFFSET: u8 = 0x1C;
/// I/O limit offset for every PCI compliant device with a header type of 0x1
const IO_LIMIT_HEADER_1_OFFSET: u8 = 0x1D;
/// Secondary status offset for every PCI compliant device with a header type of 0x1
const SECONDARY_STATUS_HEADER_1_OFFSET: u8 = 0x1E;
/// Memory base offset for every PCI compliant device with a header type of 0x1
const MEMORY_BASE_HEADER_1_OFFSET: u8 = 0x20;
/// Memory limit offset for every PCI compliant device with a header type of 0x1
const MEMORY_LIMIT_HEADER_1_OFFSET: u8 = 0x22;
/// Prefetchable memory base offset for every PCI compliant device with a header type of 0x1
const PREFETCHABLE_MEMORY_BASE_HEADER_1_OFFSET: u8 = 0x24;
/// Prefetchable memory limit offset for every PCI compliant device with a header type of 0x1
const PREFETCHABLE_MEMORY_LIMIT_HEADER_1_OFFSET: u8 = 0x26;
/// Prefetchable base upper offset for every PCI compliant device with a header type of 0x1
const PREFETCHABLE_BASE_UPPER_HEADER_1_OFFSET: u8 = 0x28;
/// Prefetchable base limit offset for every PCI compliant device with a header type of 0x1
const PREFETCHABLE_BASE_LIMIT_HEADER_1_OFFSET: u8 = 0x2C;
/// Prefetchable base limit offset for every PCI compliant device with a header type of 0x1
const IO_BASE_UPPER_HEADER_1_OFFSET: u8 = 0x30;
/// I/O limit upper offset for every PCI compliant device with a header type of 0x1
const IO_LIMIT_UPPER_HEADER_1_OFFSET: u8 = 0x32;
/// Capability pointer offset for every PCI compliant device with a header type of 0x1
const CAPABILITY_POINTER_HEADER_1_OFFSET: u8 = 0x34;
/// Expansion ROM address offset for every PCI compliant device with a header type of 0x1
const EXPANSION_ROM_ADDRESS_HEADER_1_OFFSET: u8 = 0x38;
/// Bridge control offset for every PCI compliant device with a header type of 0x1
const BRIDGE_CONTROL_HEADER_1_OFFSET: u8 = 0x3E;
/// Cardbus socket base address offset for every PCI compliant device with a header type of 0x1
const CARDBUS_SOCKET_BASE_ADDRESS_HEADER_2_OFFSET: u8 = 0x10;
/// Offset capability list offset for every PCI compliant device with a header type of 0x1
const OFFSET_CAPABILITY_LIST_HEADER_2_OFFSET: u8 = 0x14;
/// Secondary status offset for every PCI compliant device with a header type of 0x1
const SECONDARY_STATUS_HEADER_2_OFFSET: u8 = 0x16;
/// PCI bus number offset for every PCI compliant device with a header type of 0x1
const PCI_BUS_NUMBER_HEADER_2_OFFSET: u8 = 0x18;
/// Cardbus bus number offset for every PCI compliant device with a header type of 0x1
const CARDBUS_BUS_NUMBER_HEADER_2_OFFSET: u8 = 0x19;
/// Subordinate bus number offset for every PCI compliant device with a header type of 0x1
const SUBORDINATE_BUS_NUMBER_HEADER_2_OFFSET: u8 = 0x1A;
/// Cardbus latency timer offset for every PCI compliant device with a header type of 0x1
const CARDBUS_LATENCY_TIMER_HEADER_2_OFFSET: u8 = 0x1B;
/// Memory base address #0 offset for every PCI compliant device with a header type of 0x1
const MEMORY_BASE_ADDRESS_0_HEADER_2_OFFSET: u8 = 0x1C;
/// Memory limit offset #0 for every PCI compliant device with a header type of 0x1
const MEMORY_LIMIT_0_HEADER_2_OFFSET: u8 = 0x20;
/// Memory base address #1 offset for every PCI compliant device with a header type of 0x1
const MEMORY_BASE_ADDRESS_1_HEADER_2_OFFSET: u8 = 0x24;
/// Memory limit #1 offset for every PCI compliant device with a header type of 0x1
const MEMORY_LIMIT_1_HEADER_2_OFFSET: u8 = 0x28;
/// I/O base address #0 offset for every PCI compliant device with a header type of 0x1
const IO_BASE_ADDRESS_0_HEADER_2_OFFSET: u8 = 0x2C;
/// I/O limit #0 offset for every PCI compliant device with a header type of 0x1
const IO_LIMIT_0_HEADER_2_OFFSET: u8 = 0x30;
/// I/O base address #1 offset for every PCI compliant device with a header type of 0x1
const IO_BASE_ADDRESS_1_HEADER_2_OFFSET: u8 = 0x34;
/// I/O limit #1 offset for every PCI compliant device with a header type of 0x1
const IO_LIMIT_1_HEADER_2_OFFSET: u8 = 0x38;
/// Bridge control offset for every PCI compliant device with a header type of 0x1
const BRIDGE_CONTROL_HEADER_2_OFFSET: u8 = 0x3E;
/// Subsystem device ID offset for every PCI compliant device with a header type of 0x1
const SUBSYSTEM_DEVICE_ID_HEADER_2_OFFSET: u8 = 0x40;
/// Subsystem Vendor ID offset for every PCI compliant device with a header type of 0x1
const SUBSYSTEM_VENDOR_ID_HEADER_2_OFFSET: u8 = 0x42;
/// Legacy mode base address offset for every PCI compliant device with a header type of 0x1
const LEGACY_MODE_BASE_ADDRESS_HEADER_2_OFFSET: u8 = 0x44;
/// Location of a PCI compliant device
#[derive(Debug, Clone, Copy)]
pub struct Location {
/// Bus of the device
bus: u8,
/// Slot of the device
slot: u8,
/// Function number of the device
func: u8,
}
impl Location {
/// Returns the location of a PCI compliant device
const fn new(bus: u8, slot: u8, func: u8) -> Self {
Self { bus, slot, func }
}
/// Returns the configuration address of a device from its location and a given offset
///
/// The algorithm can be found [here](https://wiki.osdev.org/PCI#Configuration_Space_Access_Mechanism_.231)
fn address(self, offset: u8) -> u32 {
u32::from(self.bus) << 16_u8
| u32::from(self.slot) << 11_u8
| u32::from(self.func) << 8_u8
| u32::from(offset & CONFIG_ADDRESS_OFFSET_MASK)
| 0x8000_0000
}
/// Write location address into [`CONFIG_ADDRESS`] register
fn write_adress_port(self, offset: u8) {
let mut address_port = Port::new(CONFIG_ADDRESS);
// SAFETY: the `CONFIG_DATA` constant is the one indicated in the documentation
unsafe {
address_port.write(self.address(offset));
};
}
/// Reads the [`CONFIG_DATA`] register
fn read_data_port() -> u32 {
let mut data_port = Port::new(CONFIG_DATA);
// SAFETY: the `CONFIG_DATA` constant is the one indicated in the documentation
unsafe { data_port.read() }
}
/// Reads 8 bits at the given `offset`
#[allow(clippy::cast_possible_truncation)]
fn read_u8(self, offset: u8) -> u8 {
self.write_adress_port(offset);
(Self::read_data_port() >> ((offset & !CONFIG_ADDRESS_OFFSET_MASK) * 8)) as u8
}
/// Reads 16 bits at the given `offset`
#[allow(clippy::cast_possible_truncation)]
fn read_u16(self, offset: u8) -> u16 {
self.write_adress_port(offset);
(Self::read_data_port() >> ((offset & !CONFIG_ADDRESS_OFFSET_MASK) * 8)) as u16
}
/// Reads 16 bits at the given `offset`
fn read_u32(self, offset: u8) -> u32 {
self.write_adress_port(offset);
Self::read_data_port() >> ((offset & !CONFIG_ADDRESS_OFFSET_MASK) * 8)
}
}
/// Implementation of existing header types
///
/// See the [OSdev wiki section](https://wiki.osdev.org/PCI#PCI_Device_Structure) for more informations
#[derive(Debug)]
#[allow(unused, clippy::missing_docs_in_private_items)]
pub enum Header {
/// Header Type 0x0
X0 {
/// Base address #0
bar0: u32,
/// Base address #1
bar1: u32,
/// Base address #2
bar2: u32,
/// Base address #3
bar3: u32,
/// Base address #4
bar4: u32,
/// Base address #5
bar5: u32,
/// Points to the Card Information Structure and is used by devices that share silicon between CardBus and PCI
card_bus_cis_pointer: u32,
/// Subsystem Vendor ID
subsystem_vendor_id: u16,
/// Subsystem ID
subsystem_id: u16,
/// Expansion ROM base address
expansion_rom_base_address: u32,
/// Points (i.e. an offset into this function's configuration space) to a linked list of new capabilities implemented by
/// the device
capabilities_pointer: u8,
/// Specifies which input of the system interrupt controllers the device's interrupt pin is connected to and is implemented
/// by any device that makes use of an interrupt pin
///
/// For the x86 architecture this register corresponds to the PIC IRQ numbers 0-15 (and not I/O APIC IRQ numbers) and a
/// value of 0xFF defines no connection.
interrupt_line: u8,
/// Specifies which interrupt pin the device uses
///
/// A value of 0x1 is INTA#, 0x2 is INTB#, 0x3 is INTC#, 0x4 is INTD#, and 0x0 means the device does not use an interrupt
/// pin.
interrupt_pin: u8,
/// A read-only register that specifies how often the device needs access to the PCI bus
max_latency: u8,
/// A read-only register that specifies the burst period length, in 1/4 microsecond units, that the device needs
min_grant: u8,
},
/// Header Type 0x1
X1 {
bar0: u32,
bar1: u32,
primary_bus_number: u8,
secondary_bus_number: u8,
subordinate_bus_number: u8,
secondary_latency_timer: u8,
io_base: u8,
io_limit: u8,
secondary_status: u16,
memory_base: u16,
memory_limit: u16,
prefetchable_memory_base: u16,
prefetchable_memory_limit: u16,
prefetchable_base_upper: u32,
prefetchable_base_limit: u32,
io_base_upper: u16,
io_limit_upper: u16,
capability_pointer: u8,
expansion_rom_address: u32,
interrupt_line: u8,
interrupt_pin: u8,
bridge_control: u16,
},
/// Header Type 0x2
X2 {
cardbus_socket_base_address: u32,
offset_capability_list: u8,
secondary_status: u16,
pci_bus_number: u8,
cardbus_bus_number: u8,
subordinate_bus_number: u8,
cardbus_latency_timer: u8,
memory_base_address_0: u32,
memory_limit_0: u32,
memory_base_address_1: u32,
memory_limit_1: u32,
io_base_address_0: u32,
io_limit_0: u32,
io_base_address_1: u32,
io_limit_1: u32,
interrupt_line: u8,
interrupt_pin: u8,
bridge_control: u16,
subsystem_device_id: u16,
subsystem_vendor_id: u16,
legacy_mode_base_address: u32,
},
}
impl From<Location> for Header {
fn from(value: Location) -> Self {
let header_type = value.read_u8(HEADER_TYPE_OFFSET);
match header_type & 0x7F {
0 => Self::X0 {
bar0: value.read_u32(BASE_ADDRESS_0_HEADER_0_OFFSET),
bar1: value.read_u32(BASE_ADDRESS_1_HEADER_0_OFFSET),
bar2: value.read_u32(BASE_ADDRESS_2_HEADER_0_OFFSET),
bar3: value.read_u32(BASE_ADDRESS_3_HEADER_0_OFFSET),
bar4: value.read_u32(BASE_ADDRESS_4_HEADER_0_OFFSET),
bar5: value.read_u32(BASE_ADDRESS_5_HEADER_0_OFFSET),
card_bus_cis_pointer: value.read_u32(CARDBUS_CIS_POINTER_HEADER_0_OFFSET),
subsystem_vendor_id: value.read_u16(SUBSYSTEM_VENDOR_ID_HEADER_0_OFFSET),
subsystem_id: value.read_u16(SUBSYSTEM_ID_HEADER_0_OFFSET),
expansion_rom_base_address: value.read_u32(EXPANSION_ROM_BASE_ADDRESS_HEADER_0_OFFSET),
capabilities_pointer: value.read_u8(CAPABILITIES_POINTER_HEADER_0_OFFSET),
interrupt_line: value.read_u8(INTERRUPT_LINE_OFFSET),
interrupt_pin: value.read_u8(INTERRUPT_PIN_OFFSET),
max_latency: value.read_u8(MIN_GRANT_OFFSET_HEADER_0_OFFSET),
min_grant: value.read_u8(MAX_GRANT_OFFSET_HEADER_0_OFFSET),
},
1 => Self::X1 {
bar0: value.read_u32(BASE_ADDRESS_0_HEADER_1_OFFSET),
bar1: value.read_u32(BASE_ADDRESS_1_HEADER_1_OFFSET),
primary_bus_number: value.read_u8(PRIMARY_BUS_NUMBER_HEADER_1_OFFSET),
secondary_bus_number: value.read_u8(SECONDARY_BUS_NUMBER_HEADER_1_OFFSET),
subordinate_bus_number: value.read_u8(SUBORDINATE_BUS_NUMBER_HEADER_1_OFFSET),
secondary_latency_timer: value.read_u8(SECONDARY_LATENCY_TIMER_HEADER_1_OFFSET),
io_base: value.read_u8(IO_BASE_HEADER_1_OFFSET),
io_limit: value.read_u8(IO_LIMIT_HEADER_1_OFFSET),
secondary_status: value.read_u16(SECONDARY_STATUS_HEADER_1_OFFSET),
memory_base: value.read_u16(MEMORY_BASE_HEADER_1_OFFSET),
memory_limit: value.read_u16(MEMORY_LIMIT_HEADER_1_OFFSET),
prefetchable_memory_base: value.read_u16(PREFETCHABLE_MEMORY_BASE_HEADER_1_OFFSET),
prefetchable_memory_limit: value.read_u16(PREFETCHABLE_MEMORY_LIMIT_HEADER_1_OFFSET),
prefetchable_base_upper: value.read_u32(PREFETCHABLE_BASE_UPPER_HEADER_1_OFFSET),
prefetchable_base_limit: value.read_u32(PREFETCHABLE_BASE_LIMIT_HEADER_1_OFFSET),
io_base_upper: value.read_u16(IO_BASE_UPPER_HEADER_1_OFFSET),
io_limit_upper: value.read_u16(IO_LIMIT_UPPER_HEADER_1_OFFSET),
capability_pointer: value.read_u8(CAPABILITY_POINTER_HEADER_1_OFFSET),
expansion_rom_address: value.read_u32(EXPANSION_ROM_ADDRESS_HEADER_1_OFFSET),
interrupt_line: value.read_u8(INTERRUPT_LINE_OFFSET),
interrupt_pin: value.read_u8(INTERRUPT_PIN_OFFSET),
bridge_control: value.read_u16(BRIDGE_CONTROL_HEADER_1_OFFSET),
},
2 => Self::X2 {
cardbus_socket_base_address: value.read_u32(CARDBUS_SOCKET_BASE_ADDRESS_HEADER_2_OFFSET),
offset_capability_list: value.read_u8(OFFSET_CAPABILITY_LIST_HEADER_2_OFFSET),
secondary_status: value.read_u16(SECONDARY_STATUS_HEADER_2_OFFSET),
pci_bus_number: value.read_u8(PCI_BUS_NUMBER_HEADER_2_OFFSET),
cardbus_bus_number: value.read_u8(CARDBUS_BUS_NUMBER_HEADER_2_OFFSET),
subordinate_bus_number: value.read_u8(SUBORDINATE_BUS_NUMBER_HEADER_2_OFFSET),
cardbus_latency_timer: value.read_u8(CARDBUS_LATENCY_TIMER_HEADER_2_OFFSET),
memory_base_address_0: value.read_u32(MEMORY_BASE_ADDRESS_0_HEADER_2_OFFSET),
memory_limit_0: value.read_u32(MEMORY_LIMIT_0_HEADER_2_OFFSET),
memory_base_address_1: value.read_u32(MEMORY_BASE_ADDRESS_1_HEADER_2_OFFSET),
memory_limit_1: value.read_u32(MEMORY_LIMIT_1_HEADER_2_OFFSET),
io_base_address_0: value.read_u32(IO_BASE_ADDRESS_0_HEADER_2_OFFSET),
io_limit_0: value.read_u32(IO_LIMIT_0_HEADER_2_OFFSET),
io_base_address_1: value.read_u32(IO_BASE_ADDRESS_1_HEADER_2_OFFSET),
io_limit_1: value.read_u32(IO_LIMIT_1_HEADER_2_OFFSET),
interrupt_line: value.read_u8(INTERRUPT_LINE_OFFSET),
interrupt_pin: value.read_u8(INTERRUPT_PIN_OFFSET),
bridge_control: value.read_u16(BRIDGE_CONTROL_HEADER_2_OFFSET),
subsystem_device_id: value.read_u16(SUBSYSTEM_DEVICE_ID_HEADER_2_OFFSET),
subsystem_vendor_id: value.read_u16(SUBSYSTEM_VENDOR_ID_HEADER_2_OFFSET),
legacy_mode_base_address: value.read_u32(LEGACY_MODE_BASE_ADDRESS_HEADER_2_OFFSET),
},
_ => unreachable!("Every PCI header type is ether 0x0, 0x1 or 0x2"),
}
}
}
/// Interface for all PCI compliant devices
#[derive(Debug)]
#[allow(dead_code)]
pub struct Device {
/// Location of the device
pub location: Location,
/// Header of the device
pub header: Header,
/// Identifies the manufacturer of the device
pub vendor_id: u16,
/// Identifies the particular device
pub device_id: u16,
/// Provides control over a device's ability to generate and respond to PCI cycles
pub command: u16,
/// A register used to record status information for PCI bus related events
pub status: u16,
/// Specifies a revision identifier for a particular device
pub revision_id: u8,
/// A read-only register that specifies a register-level programming interface the device has, if it has any at all
pub prog_if: u8,
/// A read-only register that specifies the type of function the device performs
pub sub_class: u8,
/// A read-only register that specifies the type of function the device performs
///
/// A partial table of existing class codes can be found [here](https://wiki.osdev.org/PCI#Class_Codes)
pub class_code: u8,
/// Specifies the system cache line size in 32-bit units
pub cache_line_size: u8,
/// Specifies the latency timer in units of PCI bus clocks
pub latency_timer: u8,
/// Identifies the layout of the rest of the header beginning at byte 0x10 of the header
///
/// If bit 7 of this register is set, the device has multiple functions; otherwise, it is a single function device.
pub header_type: u8,
/// Represents that status and allows control of a devices BIST
pub bist: u8,
}
impl From<Location> for Device {
fn from(value: Location) -> Self {
Self {
location: value,
header: Header::from(value),
vendor_id: value.read_u16(VENDOR_ID_OFFSET),
device_id: value.read_u16(DEVICE_ID_OFFSET),
command: value.read_u16(COMMAND_OFFSET),
status: value.read_u16(STATUS_OFFSET),
revision_id: value.read_u8(REVISION_ID_OFFSET),
prog_if: value.read_u8(PROG_IF_OFFSET),
sub_class: value.read_u8(SUBCLASS_OFFSET),
class_code: value.read_u8(CLASS_CODE_OFFSET),
cache_line_size: value.read_u8(CACHE_LINE_SIZE_OFFSET),
latency_timer: value.read_u8(LATENCY_TIMER_OFFSET),
header_type: value.read_u8(HEADER_TYPE_OFFSET),
bist: value.read_u8(BIST_OFFSET),
}
}
}
/// Scans all PCI buses and returns all buses containing at least one device
///
/// The implementation used is [this algorithm from the OSdev wiki](https://wiki.osdev.org/PCI#.22Brute_Force.22_Scan)
pub fn scan() -> BTreeMap<u8, Vec<Device>> {
/// Maximal possible PCI bus number
const MIN_PCI_BUS_NUMBER: u8 = u8::MIN;
/// Maximal possible PCI bus number
const MAX_PCI_BUS_NUMBER: u8 = u8::MAX;
/// Maximal possible PCI bus number
const MIN_SLOT_NUMBER: u8 = u8::MIN;
/// Maximal possible PCI bus number
const MAX_SLOT_NUMBER: u8 = 0x20;
let mut buses: BTreeMap<u8, Vec<Device>> = BTreeMap::new();
for bus in MIN_PCI_BUS_NUMBER..MAX_PCI_BUS_NUMBER {
for slot in MIN_SLOT_NUMBER..MAX_SLOT_NUMBER {
if let Some(mut devices) = check_device(bus, slot) {
match buses.entry(bus) {
Entry::Vacant(vacant) => {
vacant.insert(devices);
},
Entry::Occupied(mut previous_devices) => previous_devices.get_mut().append(&mut devices),
}
}
}
}
buses
}
/// Checks if a device is present on a specific bus, and if it is multi-function or not
///
/// The implementation used is [this algorithm from the OSdev wiki](https://wiki.osdev.org/PCI#Enumerating_PCI_Buses)
fn check_device(bus: u8, slot: u8) -> Option<Vec<Device>> {
let location = Location::new(bus, slot, 0);
let vendor_id = location.read_u16(VENDOR_ID_OFFSET);
if vendor_id == 0xFFFF {
return None;
}
let mut devices = Vec::<Device>::new();
let header_type = location.read_u8(HEADER_TYPE_OFFSET);
let functions_to_check = if header_type & 0x80 == 0 { 0_u8..1_u8 } else { 0_u8..8_u8 };
for func in functions_to_check {
let location = Location::new(bus, slot, func);
let vendor_id = location.read_u16(VENDOR_ID_OFFSET);
if vendor_id == 0xFFFF {
continue;
}
devices.push(Device::from(location));
}
if devices.is_empty() { None } else { Some(devices) }
}
//! Interface to manage storage devices and controllers
use alloc::sync::Arc;
use alloc::vec::Vec;
use spin::Mutex;
use super::block::Manipulation;
/// Type alias representing a device UUID
pub type Uuid = [u8; 20];
/// Represents storage devices such as hard drive, SSDs, etc.
pub trait Device: Manipulation {
/// Returns the size of the device in blocks
fn size_in_blocks(&self) -> usize;
/// Returns a unique identifier for the device
///
/// This allows to be sure a same device will not be found several times.
fn uuid(&self) -> Uuid;
}
/// Represents a storage controller
pub trait Controller {
/// Returns the list of the [`Device`] controlled by this structure
fn devices<'device>(&self) -> Vec<Arc<Mutex<dyn Device + Send + 'device>>>;
}
......@@ -48,6 +48,7 @@ impl Display for Rtc {
/// See the [OSdev wiki](https://wiki.osdev.org/CMOS#Getting_Current_Date_and_Time_from_RTC) for more details
#[repr(u8)]
#[derive(Debug, Clone)]
#[allow(unused)]
enum Register {
/// Second register
Second = 0x00,
......@@ -213,6 +214,7 @@ impl Cmos {
}
/// Waits until the CMOS is updated
#[allow(unused)]
fn wait_while_updating(&mut self) {
while self.is_updating() {
spin_loop();
......@@ -220,6 +222,7 @@ impl Cmos {
}
/// Returns the current Real-Time Clock
#[allow(unused)]
pub fn rtc(&mut self) -> Rtc {
/// Believe in the docs
const SRB_BCD_MODE: u8 = 0x04;
......
......@@ -24,6 +24,12 @@ pub(super) enum InterruptIndex {
/// Real-time controller interrupt
Rtc = PIC2_OFFSET,
/// Standard ATA primary interrupt (0xE above base offset)
AtaPrimary = PIC1_OFFSET + 0x0E,
/// Standard ATA primary interrupt (0xF above base offset)
AtaSecondary = PIC1_OFFSET + 0x0F,
}
impl From<InterruptIndex> for u8 {
......@@ -46,6 +52,8 @@ pub(super) static IDT: Lazy<InterruptDescriptorTable> = Lazy::new(|| {
set_handler_fn(&mut idt, InterruptIndex::Timer, timer_interrupt_handler);
set_handler_fn(&mut idt, InterruptIndex::Keyboard, keyboard_interrupt_handler);
set_handler_fn(&mut idt, InterruptIndex::Rtc, rtc_interrupt_handler);
set_handler_fn(&mut idt, InterruptIndex::AtaPrimary, primary_ata_handler);
set_handler_fn(&mut idt, InterruptIndex::AtaSecondary, secondary_ata_handler);
idt
});
......@@ -102,7 +110,7 @@ extern "x86-interrupt" fn rtc_interrupt_handler(_stack_frame: InterruptStackFram
LAST_RTC_UPDATE.store(pit::ticks(), Ordering::Relaxed);
CMOS.notify_end_of_interrupt();
// SAFETY: Timer index is the same everywhere it should
// SAFETY: RTC index is the same everywhere it should
unsafe {
PICS.lock().notify_end_of_interrupt(InterruptIndex::Rtc.into());
};
......@@ -112,12 +120,28 @@ extern "x86-interrupt" fn rtc_interrupt_handler(_stack_frame: InterruptStackFram
extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) {
KeyQueue::handle_interrupt();
// SAFETY: Timer index is the same everywhere it should
// SAFETY: Keyboard index is the same everywhere it should
unsafe {
PICS.lock().notify_end_of_interrupt(InterruptIndex::Keyboard.into());
};
}
/// Primary ATA interrupt handler
extern "x86-interrupt" fn primary_ata_handler(_stack_frame: InterruptStackFrame) {
// SAFETY: ATA primary index is the standard one
unsafe {
PICS.lock().notify_end_of_interrupt(InterruptIndex::AtaPrimary.into());
};
}
/// Secondary ATA interrupt handler
extern "x86-interrupt" fn secondary_ata_handler(_stack_frame: InterruptStackFrame) {
// SAFETY: ATA secondary index is the standard one
unsafe {
PICS.lock().notify_end_of_interrupt(InterruptIndex::AtaSecondary.into());
};
}
#[cfg(test)]
mod test {
use x86_64::instructions::interrupts;
......
......@@ -151,7 +151,8 @@ impl KeyQueue {
///
/// Moreover, the following keys combinations are used :
/// - `CTRL` + `ALT` + `DEL`: reboots the computer (not implemented yet)
/// - `ALT` + `L`: flushes the logger
/// - `ALT` + `P`: displays all logs and clears the logger
/// - `ALT` + `L`: displays the oldest log then removes it from the logger
#[allow(clippy::wildcard_enum_match_arm)]
pub fn handle_key(key: DecodedKey) {
/// Tabulation char
......@@ -171,11 +172,16 @@ impl KeyQueue {
DecodedKey::RawKey(KeyCode::ArrowLeft) => Self::send_csi("1D"),
DecodedKey::Unicode(TAB) if shift_pressed => Self::send_csi("2"),
DecodedKey::Unicode(DEL) if alt_pressed && ctrl_pressed => warn!("TODO: implement reboot"),
DecodedKey::Unicode('l') if alt_pressed => {
DecodedKey::Unicode('p') if alt_pressed => {
let mut logger = SYS_LOGGER.lock();
logger.display_messages();
logger.clear();
},
DecodedKey::Unicode('l') if alt_pressed => {
let mut logger = SYS_LOGGER.lock();
logger.display_next_message();
logger.remove_oldest();
},
DecodedKey::Unicode(key) => Self::push_key(key),
_ => {},
}
......
......@@ -2,6 +2,7 @@
//!
//! This kernel is meant to be minimal and make easy the loading of other external modules
pub mod device;
pub mod interrupt;
pub mod memory;
pub mod screen;
......@@ -15,9 +16,7 @@ use x86_64::VirtAddr;
use self::memory::paging::{PhysFrameAllocator, MAPPER};
use self::memory::vmm::VIRTUAL_MEMORY_MANAGER;
use crate::kernel::interrupt::cmos::CMOS;
use crate::kernel::screen::buffer::Buffer;
use crate::println;
/// Initializes the kernel
pub fn init(boot_info: &'static mut BootInfo) {
......@@ -65,9 +64,10 @@ pub fn init(boot_info: &'static mut BootInfo) {
// Initialization of the CPU interruptions
interrupt::init(rsdp_addr);
info!("Interruptions initialized");
println!("Hello world!");
println!("It is currently {}.", CMOS.rtc());
// Initialization of the devices
device::init();
info!("Devices initialized");
}
//! Implementation of a simple competitive task executor
//! Implementation of a simple cooperative task executor
use alloc::collections::BTreeMap;
use alloc::sync::Arc;
......
......@@ -57,14 +57,22 @@
#![feature(const_mut_refs)]
#![feature(const_trait_impl)]
#![feature(custom_test_frameworks)]
#![feature(error_in_core)]
#![feature(extend_one)]
#![feature(int_roundings)]
#![feature(iter_advance_by)]
#![feature(new_uninit)]
#![feature(no_coverage)]
#![feature(strict_provenance)]
#![feature(trait_upcasting)]
#![feature(vec_into_raw_parts)]
#![reexport_test_harness_main = "test_main"]
#![test_runner(test::runner)]
extern crate alloc;
mod encoding;
mod fs;
mod kernel;
mod macros;
mod mutex;
......@@ -74,13 +82,18 @@ mod user;
#[cfg(test)]
mod test;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::alloc::Layout;
use bootloader_api::config::{Mapping, Mappings};
use bootloader_api::{entry_point, BootInfo, BootloaderConfig};
use kernel::device::storage::Device;
use kernel::device::{STORAGE_CONTROLLERS, UEFI_UUID};
use kernel::task::executor::Executor;
use kernel::task::Task;
use log::{logger, set_logger, set_max_level, LevelFilter};
use spin::Mutex;
#[cfg(test)]
use test::exit;
use user::tty::print_key;
......@@ -88,6 +101,7 @@ use user::tty::print_key;
use x86_64::instructions;
use self::syslog::SYS_LOGGER;
use crate::kernel::interrupt::cmos::CMOS;
/// Configuration of the bootloader
///
......@@ -108,12 +122,31 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
kernel::init(boot_info);
let storage_devices = STORAGE_CONTROLLERS
.get()
.expect("Storage controllers have not been initialized yet")
.iter()
.flat_map(|controller| controller.lock().devices())
.collect::<Vec<Arc<Mutex<dyn Device + Send>>>>()
.into_iter()
.filter(|device| device.lock().uuid() != UEFI_UUID)
.collect::<Vec<Arc<Mutex<dyn Device + Send>>>>();
if let Some(device) = storage_devices.first() {
fs::init(&Arc::clone(device));
}
#[cfg(test)]
{
test_main();
exit();
};
println!("Hello world!");
let rtc = CMOS.rtc();
println!("It is currently : {:0>2}:{:0>2}:{:0>2} (UTC+0)", rtc.hour, rtc.minute, rtc.second);
let mut executor = Executor::new();
executor.spawn(Task::new(print_key()));
executor.run();
......
//! Simple logger for `SkavOS`
use alloc::collections::BTreeMap;
use alloc::format;
use alloc::string::String;
use core::fmt::Display;
use core::sync::atomic::{AtomicU64, Ordering};
use circular_buffer::CircularBuffer;
use log::{Level, Log, Metadata, Record};
use crate::kernel::screen::color::{Color, CYAN, ORANGE, RED, WHITE, YELLOW};
......@@ -85,19 +85,32 @@ impl Message {
#[derive(Debug)]
pub struct SysLog {
/// Messages that have not been flushed yet
messages: BTreeMap<MessageId, Message>,
messages: CircularBuffer<100, Message>,
}
impl SysLog {
/// Creates a new logger
const fn new() -> Self {
Self {
messages: BTreeMap::new(),
messages: CircularBuffer::new(),
}
}
/// Prints the next message that have not been displayed yet
pub fn display_next_message(&mut self) {
if self.messages.is_empty() {
println!("Empty logger");
return;
}
// SAFETY: `self.messages` is not empty
unsafe {
self.messages.front().unwrap_unchecked().print();
};
}
/// Prints all messages contained in the logger
pub fn display_messages(&mut self) {
pub fn display_messages(&self) {
if self.messages.is_empty() {
println!("Empty logger");
return;
......@@ -105,11 +118,16 @@ impl SysLog {
println!("Logs:");
for message in self.messages.values() {
for message in &self.messages {
message.print();
}
}
/// Removes the oldest message saved in the logger
pub fn remove_oldest(&mut self) {
self.messages.pop_front();
}
/// Removes all the messages saved in the logger
pub fn clear(&mut self) {
self.messages.clear();
......@@ -124,12 +142,7 @@ impl Log for Locked<SysLog> {
fn log(&self, record: &Record) {
let content = format!("{}", record.args());
let message = Message::new(record.level(), content);
let message_id = message.id;
assert!(
self.lock().messages.insert(message.id, message).is_none(),
"The message with ID {message_id} has already been logged"
);
self.lock().messages.push_back(message);
}
fn flush(&self) {}
......@@ -182,14 +195,15 @@ mod test {
.args(format_args!("Hello world! {}", 42_usize))
.build(),
);
let previous_message_id = NEXT_MESSAGE_ID.load(Ordering::Relaxed) - 1;
assert_eq!(&syslog.lock().messages[&MessageId(previous_message_id)], &Message {
assert_eq!(syslog.lock().messages.back().unwrap(), &Message {
id: MessageId(previous_message_id),
level: Level::Info,
content: "Hello world! 42".to_owned()
});
syslog.log(&Record::builder().level(Level::Warn).args(format_args!("1 + {} = 2", 1_usize)).build());
assert_eq!(&syslog.lock().messages[&MessageId(previous_message_id + 1)], &Message {
assert_eq!(syslog.lock().messages.back().unwrap(), &Message {
id: MessageId(previous_message_id + 1),
level: Level::Warn,
content: "1 + 1 = 2".to_owned()
......