diff --git a/Cargo.lock b/Cargo.lock index 44e9cca..33ed53b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "able_graphics_library" +version = "0.1.2" +source = "git+https://git.ablecorp.us/ableos/ableos_userland#70a65fc10d2caafe370be90eb0e31aa7d9347218" +dependencies = [ + "embedded-graphics", + "versioning", +] + [[package]] name = "ahash" version = "0.7.6" @@ -65,6 +74,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "az" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" + [[package]] name = "bit" version = "0.1.1" @@ -125,7 +140,7 @@ dependencies = [ [[package]] name = "clparse" version = "0.1.0" -source = "git+https://git.ablecorp.us/ableos/ableos_userland#8fa6c705f23ae310e3c4395a61823449ed5a1d02" +source = "git+https://git.ablecorp.us/ableos/ableos_userland#70a65fc10d2caafe370be90eb0e31aa7d9347218" dependencies = [ "hashbrown 0.13.2", "log", @@ -228,6 +243,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "embedded-graphics" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "750082c65094fbcc4baf9ba31583ce9a8bb7f52cadfb96f6164b1bc7f922f32b" +dependencies = [ + "az", + "byteorder", + "embedded-graphics-core", + "float-cmp", + "micromath", +] + +[[package]] +name = "embedded-graphics-core" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8b1239db5f3eeb7e33e35bd10bd014e7b2537b17e071f726a09351431337cfa" +dependencies = [ + "az", + "byteorder", +] + [[package]] name = "env_logger" version = "0.10.0" @@ -294,6 +332,15 @@ dependencies = [ "log", ] +[[package]] +name = "float-cmp" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4" +dependencies = [ + "num-traits", +] + [[package]] name = "getrandom" version = "0.2.8" @@ -416,9 +463,11 @@ dependencies = [ name = "kernel" version = "0.2.0" dependencies = [ + "able_graphics_library", "clparse", "crossbeam-queue", "derive_more", + "embedded-graphics", "error-stack 0.3.1", "hashbrown 0.13.2", "limine", @@ -429,6 +478,7 @@ dependencies = [ "spin", "uart_16550", "versioning", + "virtio-drivers", "wasmi", "x2apic", "x86_64", @@ -492,6 +542,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "micromath" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc4010833aea396656c2f91ee704d51a6f1329ec2ab56ffd00bfd56f7481ea94" + [[package]] name = "num-integer" version = "0.1.45" @@ -791,11 +847,22 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "versioning" version = "0.1.3" -source = "git+https://git.ablecorp.us/ableos/ableos_userland#8fa6c705f23ae310e3c4395a61823449ed5a1d02" +source = "git+https://git.ablecorp.us/ableos/ableos_userland#70a65fc10d2caafe370be90eb0e31aa7d9347218" dependencies = [ "serde", ] +[[package]] +name = "virtio-drivers" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42299957c6f61af586fe3eae398c16ec07f33a02579fa1d41ae96156ce437029" +dependencies = [ + "bitflags", + "log", + "zerocopy", +] + [[package]] name = "volatile" version = "0.4.6" @@ -1108,7 +1175,28 @@ dependencies = [ [[package]] name = "xml" version = "0.1.0" -source = "git+https://git.ablecorp.us/ableos/ableos_userland#8fa6c705f23ae310e3c4395a61823449ed5a1d02" +source = "git+https://git.ablecorp.us/ableos/ableos_userland#70a65fc10d2caafe370be90eb0e31aa7d9347218" dependencies = [ "serde", ] + +[[package]] +name = "zerocopy" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332f188cc1bcf1fe1064b8c58d150f497e697f49774aa846f2dc949d9a25f236" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6505e6815af7de1746a08f69c69606bb45695a17149517680f3b2149713b19a3" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 9ae4e69..ef59c0e 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -5,6 +5,11 @@ version = "0.2.0" resolver = "2" [dependencies] + + +embedded-graphics = "0.7.1" + + error-stack = { version = "0.3", default-features = false } log = "0.4" spin = "0.9" @@ -14,6 +19,7 @@ xml = { git = "https://git.ablecorp.us/ableos/ableos_userland" } clparse = { git = "https://git.ablecorp.us/ableos/ableos_userland", default-features = false } versioning = { git = "https://git.ablecorp.us/ableos/ableos_userland" } +able_graphics_library = { git = "https://git.ablecorp.us/ableos/ableos_userland" } wasmi = { version = "0.29.0", default-features = false } hashbrown = "*" @@ -43,6 +49,7 @@ features = [ limine = { version = "0.1", git = "https://github.com/limine-bootloader/limine-rs" } x86_64 = "0.14" x2apic = "0.4" +virtio-drivers = "0.4.0" # rdrand = "*" rdrand = { version = "0.8", default-features = false } diff --git a/kernel/src/arch/x86_64/cpuid.rs b/kernel/src/arch/x86_64/cpuid.rs new file mode 100644 index 0000000..98ac9dd --- /dev/null +++ b/kernel/src/arch/x86_64/cpuid.rs @@ -0,0 +1,924 @@ +use core::{arch::asm, fmt, ops::Deref, slice, str}; + +#[repr(u32)] +pub enum RequestType { + BasicInformation = 0x00000000, + VersionInformation = 0x00000001, + ThermalPowerManagementInformation = 0x00000006, + StructuredExtendedInformation = 0x00000007, + ExtendedFunctionInformation = 0x80000000, + ExtendedProcessorSignature = 0x80000001, + BrandString1 = 0x80000002, + BrandString2 = 0x80000003, + BrandString3 = 0x80000004, + // reserved = 0x80000005, + CacheLine = 0x80000006, + TimeStampCounter = 0x80000007, + PhysicalAddressSize = 0x80000008, +} + +pub fn cpuid(code: RequestType) -> (u32, u32, u32, u32) { + let eax; + let ebx; + let ecx; + let edx; + + unsafe { + asm!( + "movq %rbx, {0:r}", + "cpuid", + "xchgq %rbx, {0:r}", + lateout(reg) ebx, + inlateout("eax") code as u32 => eax, + inlateout("ecx") 0 => ecx, + lateout("edx") edx, + options(nostack, preserves_flags, att_syntax), + ); + } + + (eax, ebx, ecx, edx) +} + +/// The main entrypoint to the CPU information +#[cfg(any(target_arch = "x86_64", target_arch = "x86"))] +pub fn master() -> Option { + Some(Master::new()) +} + +// This matches the Intel Architecture guide, with bits 31 -> 0. +// The bit positions are inclusive. +fn bits_of(val: u32, start_bit: u8, end_bit: u8) -> u32 { + let mut silly = 0; + + for _ in start_bit..end_bit + 1 { + silly <<= 1; + silly |= 1; + } + + (val >> start_bit) & silly +} + +pub fn as_bytes(v: &u32) -> &[u8] { + let start = v as *const u32 as *const u8; + // TODO: use u32::BYTES + unsafe { slice::from_raw_parts(start, 4) } +} + +macro_rules! bit { + ($reg:ident, {$($idx:expr => $name:ident),+}) => { + $(pub fn $name(self) -> bool { + ((self.$reg >> $idx) & 1) != 0 + })+ + } +} + +macro_rules! dump { + ($me:expr, $f: expr, $sname:expr, {$($name:ident),+}) => { + $f.debug_struct($sname) + $(.field(stringify!($name), &$me.$name()))+ + .finish() + } +} + +macro_rules! delegate_flag { + ($item:ident, {$($name:ident),+}) => { + $(pub fn $name(&self) -> bool { + self.$item.map(|i| i.$name()).unwrap_or(false) + })+ + } +} + +macro_rules! master_attr_reader { + ($name:ident, $kind:ty) => { + pub fn $name(&self) -> Option<&$kind> { + self.$name.as_ref() + } + }; +} + +#[derive(Copy, Clone)] +pub struct VersionInformation { + eax: u32, + ebx: u32, + ecx: u32, + edx: u32, +} + +impl VersionInformation { + pub fn new() -> VersionInformation { + let (a, b, c, d) = cpuid(RequestType::VersionInformation); + VersionInformation { + eax: a, + ebx: b, + ecx: c, + edx: d, + } + } + + pub fn family_id(self) -> u32 { + let family_id = bits_of(self.eax, 8, 11); + let extended_family_id = bits_of(self.eax, 20, 27); + + if family_id != 0x0F { + family_id + } else { + extended_family_id + family_id + } + } + + pub fn model_id(self) -> u32 { + let family_id = self.family_id(); + let model_id = bits_of(self.eax, 4, 7); + let extended_model_id = bits_of(self.eax, 16, 19); + + if family_id == 0x06 || family_id == 0x0F { + (extended_model_id << 4) + model_id + } else { + model_id + } + } + + pub fn stepping(self) -> u32 { + bits_of(self.eax, 0, 3) + } + + fn processor_signature(self) -> u32 { + self.eax + } + + pub fn brand_string(self) -> Option<&'static str> { + let brand_index = bits_of(self.ebx, 0, 7); + let processor_signature = self.processor_signature(); + + match brand_index { + 0x00 => None, + 0x01 => Some("Intel(R) Celeron(R)"), + 0x02 => Some("Intel(R) Pentium(R) III"), + 0x03 => { + if processor_signature == 0x06B1 { + Some("Intel(R) Celeron(R)") + } else { + Some("Intel(R) Pentium(R) III Xeon(R)") + } + } + 0x04 => Some("Intel(R) Pentium(R) III"), + 0x06 => Some("Mobile Intel(R) Pentium(R) III-M"), + 0x07 => Some("Mobile Intel(R) Celeron(R)"), + 0x08 => Some("Intel(R) Pentium(R) 4"), + 0x09 => Some("Intel(R) Pentium(R) 4"), + 0x0A => Some("Intel(R) Celeron(R)"), + 0x0B => { + if processor_signature == 0x0F13 { + Some("Intel(R) Xeon(R) MP") + } else { + Some("Intel(R) Xeon(R)") + } + } + 0x0C => Some("Intel(R) Xeon(R) MP"), + 0x0E => { + if processor_signature == 0x0F13 { + Some("Intel(R) Xeon(R)") + } else { + Some("Mobile Intel(R) Pentium(R) 4-M") + } + } + 0x0F => Some("Mobile Intel(R) Celeron(R)"), + 0x11 => Some("Mobile Genuine Intel(R)"), + 0x12 => Some("Intel(R) Celeron(R) M"), + 0x13 => Some("Mobile Intel(R) Celeron(R)"), + 0x14 => Some("Intel(R) Celeron(R)"), + 0x15 => Some("Mobile Genuine Intel(R)"), + 0x16 => Some("Intel(R) Pentium(R) M"), + 0x17 => Some("Mobile Intel(R) Celeron(R)"), + _ => None, + } + } + + bit!(ecx, { + 0 => sse3, + 1 => pclmulqdq, + 2 => dtes64, + 3 => monitor, + 4 => ds_cpl, + 5 => vmx, + 6 => smx, + 7 => eist, + 8 => tm2, + 9 => ssse3, + 10 => cnxt_id, + 11 => sdbg, + 12 => fma, + 13 => cmpxchg16b, + 14 => xtpr_update_control, + 15 => pdcm, + // 16 - reserved + 17 => pcid, + 18 => dca, + 19 => sse4_1, + 20 => sse4_2, + 21 => x2apic, + 22 => movbe, + 23 => popcnt, + 24 => tsc_deadline, + 25 => aesni, + 26 => xsave, + 27 => osxsave, + 28 => avx, + 29 => f16c, + 30 => rdrand + // 31 - unused + }); + + bit!(edx, { + 0 => fpu, + 1 => vme, + 2 => de, + 3 => pse, + 4 => tsc, + 5 => msr, + 6 => pae, + 7 => mce, + 8 => cx8, + 9 => apic, + // 10 - reserved + 11 => sep, + 12 => mtrr, + 13 => pge, + 14 => mca, + 15 => cmov, + 16 => pat, + 17 => pse_36, + 18 => psn, + 19 => clfsh, + // 20 - reserved + 21 => ds, + 22 => acpi, + 23 => mmx, + 24 => fxsr, + 25 => sse, + 26 => sse2, + 27 => ss, + 28 => htt, + 29 => tm, + // 30 -reserved + 31 => pbe + }); +} + +impl fmt::Debug for VersionInformation { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + dump!(self, f, "VersionInformation", { + family_id, + model_id, + stepping, + brand_string, + sse3, + pclmulqdq, + dtes64, + monitor, + ds_cpl, + vmx, + smx, + eist, + tm2, + ssse3, + cnxt_id, + sdbg, + fma, + cmpxchg16b, + xtpr_update_control, + pdcm, + pcid, + dca, + sse4_1, + sse4_2, + x2apic, + movbe, + popcnt, + tsc_deadline, + aesni, + xsave, + osxsave, + avx, + f16c, + rdrand, + fpu, + vme, + de, + pse, + tsc, + msr, + pae, + mce, + cx8, + apic, + sep, + mtrr, + pge, + mca, + cmov, + pat, + pse_36, + psn, + clfsh, + ds, + acpi, + mmx, + fxsr, + sse, + sse2, + ss, + htt, + tm, + pbe + }) + } +} + +#[derive(Copy, Clone)] +pub struct ExtendedProcessorSignature { + ecx: u32, + edx: u32, +} + +impl ExtendedProcessorSignature { + fn new() -> ExtendedProcessorSignature { + let (_, _, c, d) = cpuid(RequestType::ExtendedProcessorSignature); + ExtendedProcessorSignature { ecx: c, edx: d } + } + + bit!(ecx, { + 0 => lahf_sahf_in_64_bit, + // 1-4 reserved + 5 => lzcnt, + // 6-7 reserved + 8 => prefetchw + // 9-31 reserved + }); + + bit!(edx, { + // 0-10 reserved + 11 => syscall_sysret_in_64_bit, + // 12-19 reserved + 20 => execute_disable, + // 21-25 reserved + 26 => gigabyte_pages, + 27 => rdtscp_and_ia32_tsc_aux, + // 28 reserved + 29 => intel_64_bit_architecture + // 30-31 reserved + }); +} + +impl fmt::Debug for ExtendedProcessorSignature { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + dump!(self, f, "ThermalPowerManagementInformation", { + lahf_sahf_in_64_bit, + lzcnt, + prefetchw, + syscall_sysret_in_64_bit, + execute_disable, + gigabyte_pages, + rdtscp_and_ia32_tsc_aux, + intel_64_bit_architecture + }) + } +} + +// 3 calls of 4 registers of 4 bytes +const BRAND_STRING_LENGTH: usize = 3 * 4 * 4; + +pub struct BrandString { + bytes: [u8; BRAND_STRING_LENGTH], +} + +impl BrandString { + fn new() -> BrandString { + fn append_bytes(a: RequestType, bytes: &mut [u8]) { + let (a, b, c, d) = cpuid(a); + + let result_bytes = as_bytes(&a) + .iter() + .chain(as_bytes(&b).iter()) + .chain(as_bytes(&c).iter()) + .chain(as_bytes(&d).iter()); + + for (output, input) in bytes.iter_mut().zip(result_bytes) { + *output = *input + } + } + + let mut brand_string = BrandString { + bytes: [0; BRAND_STRING_LENGTH], + }; + append_bytes(RequestType::BrandString1, &mut brand_string.bytes[0..]); + append_bytes(RequestType::BrandString2, &mut brand_string.bytes[16..]); + append_bytes(RequestType::BrandString3, &mut brand_string.bytes[32..]); + brand_string + } +} + +impl Clone for BrandString { + fn clone(&self) -> Self { + let mut bytes = [0; BRAND_STRING_LENGTH]; + for (d, s) in bytes.iter_mut().zip(self.bytes.iter()) { + *d = *s; + } + BrandString { bytes: bytes } + } +} + +impl Deref for BrandString { + type Target = str; + + fn deref(&self) -> &str { + let nul_terminator = self.bytes.iter().position(|&b| b == 0).unwrap_or(0); + let usable_bytes = &self.bytes[..nul_terminator]; + unsafe { str::from_utf8_unchecked(usable_bytes) }.trim() + } +} + +impl fmt::Display for BrandString { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + (self as &str).fmt(f) + } +} + +impl fmt::Debug for BrandString { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + (self as &str).fmt(f) + } +} + +#[derive(Copy, Clone)] +pub struct ThermalPowerManagementInformation { + eax: u32, + ebx: u32, + ecx: u32, +} + +impl ThermalPowerManagementInformation { + fn new() -> ThermalPowerManagementInformation { + let (a, b, c, _) = cpuid(RequestType::ThermalPowerManagementInformation); + ThermalPowerManagementInformation { + eax: a, + ebx: b, + ecx: c, + } + } + + bit!(eax, { + 0 => digital_temperature_sensor, + 1 => intel_turbo_boost, + 2 => arat, + // 3 - reserved + 4 => pln, + 5 => ecmd, + 6 => ptm, + 7 => hwp, + 8 => hwp_notification, + 9 => hwp_activity_window, + 10 => hwp_energy_performance_preference, + // 12 - reserved + 13 => hdc + }); + + pub fn number_of_interrupt_thresholds(self) -> u32 { + bits_of(self.ebx, 0, 3) + } + + bit!(ecx, { + 0 => hardware_coordination_feedback, + // 1-2 - reserved + 3 => performance_energy_bias + }); +} + +impl fmt::Debug for ThermalPowerManagementInformation { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + dump!(self, f, "ThermalPowerManagementInformation", { + digital_temperature_sensor, + intel_turbo_boost, + arat, + pln, + ecmd, + ptm, + hwp, + hwp_notification, + hwp_activity_window, + hwp_energy_performance_preference, + hdc, + + number_of_interrupt_thresholds, + + hardware_coordination_feedback, + performance_energy_bias + }) + } +} + +#[derive(Copy, Clone)] +pub struct StructuredExtendedInformation { + ebx: u32, + ecx: u32, +} + +impl StructuredExtendedInformation { + fn new() -> StructuredExtendedInformation { + let (_, b, c, _) = cpuid(RequestType::StructuredExtendedInformation); + StructuredExtendedInformation { ebx: b, ecx: c } + } + + bit!(ebx, { + 0 => fsgsbase, + 1 => ia32_tsc_adjust_msr, + // 2 - reserved + 3 => bmi1, + 4 => hle, + 5 => avx2, + // 6 - reserved + 7 => smep, + 8 => bmi2, + 9 => enhanced_rep_movsb_stosb, + 10 => invpcid, + 11 => rtm, + 12 => pqm, + 13 => deprecates_fpu_cs_ds, + // 14 - reserved + 15 => pqe, + // 16-17 - reserved + 18 => rdseed, + 19 => adx, + 20 => smap, + // 21-24 - reserved + 25 => intel_processor_trace + // 26-31 - reserved + }); + + bit!(ecx, { + 0 => prefetchwt1 + }); +} + +impl fmt::Debug for StructuredExtendedInformation { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + dump!(self, f, "StructuredExtendedInformation", { + fsgsbase, + ia32_tsc_adjust_msr, + bmi1, + hle, + avx2, + smep, + bmi2, + enhanced_rep_movsb_stosb, + invpcid, + rtm, + pqm, + deprecates_fpu_cs_ds, + pqe, + rdseed, + adx, + smap, + intel_processor_trace, + prefetchwt1 + }) + } +} + +#[derive(Debug, Copy, Clone)] +pub enum CacheLineAssociativity { + Disabled, + DirectMapped, + TwoWay, + FourWay, + EightWay, + SixteenWay, + Full, +} + +#[derive(Copy, Clone)] +pub struct CacheLine(u32); + +impl CacheLine { + fn new() -> CacheLine { + let (_, _, c, _) = cpuid(RequestType::CacheLine); + CacheLine(c) + } + + pub fn cache_line_size(self) -> u32 { + bits_of(self.0, 0, 7) + } + + pub fn l2_associativity(self) -> Option { + match bits_of(self.0, 12, 15) { + 0x00 => Some(CacheLineAssociativity::Disabled), + 0x01 => Some(CacheLineAssociativity::DirectMapped), + 0x02 => Some(CacheLineAssociativity::TwoWay), + 0x04 => Some(CacheLineAssociativity::FourWay), + 0x06 => Some(CacheLineAssociativity::EightWay), + 0x08 => Some(CacheLineAssociativity::SixteenWay), + 0x0F => Some(CacheLineAssociativity::Full), + _ => None, + } + } + + pub fn cache_size(self) -> u32 { + bits_of(self.0, 16, 31) + } +} + +impl fmt::Debug for CacheLine { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + dump!(self, f, "CacheLine", { + cache_line_size, + l2_associativity, + cache_size + }) + } +} + +#[derive(Copy, Clone)] +pub struct TimeStampCounter { + edx: u32, +} + +impl TimeStampCounter { + fn new() -> TimeStampCounter { + let (_, _, _, d) = cpuid(RequestType::TimeStampCounter); + TimeStampCounter { edx: d } + } + + bit!(edx, { + // 0-7 - reserved + 8 => invariant_tsc + // 9-31 - reserved + }); +} + +impl fmt::Debug for TimeStampCounter { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + dump!(self, f, "TimeStampCounter", { invariant_tsc }) + } +} + +#[derive(Copy, Clone)] +pub struct PhysicalAddressSize(u32); + +impl PhysicalAddressSize { + fn new() -> PhysicalAddressSize { + let (a, _, _, _) = cpuid(RequestType::PhysicalAddressSize); + PhysicalAddressSize(a) + } + + pub fn physical_address_bits(self) -> u32 { + bits_of(self.0, 0, 7) + } + + pub fn linear_address_bits(self) -> u32 { + bits_of(self.0, 8, 15) + } +} + +impl fmt::Debug for PhysicalAddressSize { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + dump!(self, f, "PhysicalAddressSize", { + physical_address_bits, + linear_address_bits + }) + } +} + +/// Information about the currently running processor +/// +/// Feature flags match the feature mnemonic listed in the Intel +/// Instruction Set Reference. This struct provides a facade for flags +/// so the consumer doesn't need to worry about which particular CPUID +/// leaf provides the information. +/// +/// For data beyond simple feature flags, you will need to retrieve +/// the nested struct and call the appropriate methods on it. +#[derive(Debug, Clone)] +pub struct Master { + // TODO: Rename struct + version_information: Option, + thermal_power_management_information: Option, + structured_extended_information: Option, + extended_processor_signature: Option, + brand_string: Option, + cache_line: Option, + time_stamp_counter: Option, + physical_address_size: Option, +} + +impl Master { + pub fn new() -> Master { + fn when_supported(max: u32, kind: RequestType, then: F) -> Option + where + F: FnOnce() -> T, + { + if max >= kind as u32 { + Some(then()) + } else { + None + } + } + + let (max_value, _, _, _) = cpuid(RequestType::BasicInformation); + + let vi = when_supported(max_value, RequestType::VersionInformation, || { + VersionInformation::new() + }); + let tpm = when_supported( + max_value, + RequestType::ThermalPowerManagementInformation, + || ThermalPowerManagementInformation::new(), + ); + let sei = when_supported( + max_value, + RequestType::StructuredExtendedInformation, + || StructuredExtendedInformation::new(), + ); + + // Extended information + + let (max_value, _, _, _) = cpuid(RequestType::ExtendedFunctionInformation); + + let eps = when_supported(max_value, RequestType::ExtendedProcessorSignature, || { + ExtendedProcessorSignature::new() + }); + let brand_string = + when_supported(max_value, RequestType::BrandString3, || BrandString::new()); + let cache_line = when_supported(max_value, RequestType::CacheLine, || CacheLine::new()); + let tsc = when_supported(max_value, RequestType::TimeStampCounter, || { + TimeStampCounter::new() + }); + let pas = when_supported(max_value, RequestType::PhysicalAddressSize, || { + PhysicalAddressSize::new() + }); + + Master { + version_information: vi, + thermal_power_management_information: tpm, + structured_extended_information: sei, + extended_processor_signature: eps, + brand_string: brand_string, + cache_line: cache_line, + time_stamp_counter: tsc, + physical_address_size: pas, + } + } + + master_attr_reader!(version_information, VersionInformation); + master_attr_reader!( + thermal_power_management_information, + ThermalPowerManagementInformation + ); + master_attr_reader!( + structured_extended_information, + StructuredExtendedInformation + ); + master_attr_reader!(extended_processor_signature, ExtendedProcessorSignature); + master_attr_reader!(cache_line, CacheLine); + master_attr_reader!(time_stamp_counter, TimeStampCounter); + master_attr_reader!(physical_address_size, PhysicalAddressSize); + + pub fn brand_string(&self) -> Option<&str> { + self.brand_string + .as_ref() + .map(|bs| bs as &str) + .or(self.version_information.and_then(|vi| vi.brand_string())) + } + + delegate_flag!(version_information, { + sse3, + pclmulqdq, + dtes64, + monitor, + ds_cpl, + vmx, + smx, + eist, + tm2, + ssse3, + cnxt_id, + sdbg, + fma, + cmpxchg16b, + xtpr_update_control, + pdcm, + pcid, + dca, + sse4_1, + sse4_2, + x2apic, + movbe, + popcnt, + tsc_deadline, + aesni, + xsave, + osxsave, + avx, + f16c, + rdrand, + fpu, + vme, + de, + pse, + tsc, + msr, + pae, + mce, + cx8, + apic, + sep, + mtrr, + pge, + mca, + cmov, + pat, + pse_36, + psn, + clfsh, + ds, + acpi, + mmx, + fxsr, + sse, + sse2, + ss, + htt, + tm, + pbe + }); + + delegate_flag!(thermal_power_management_information, { + digital_temperature_sensor, + intel_turbo_boost, + arat, + pln, + ecmd, + ptm, + hwp, + hwp_notification, + hwp_activity_window, + hwp_energy_performance_preference, + hdc, + hardware_coordination_feedback, + performance_energy_bias + }); + + delegate_flag!(structured_extended_information, { + fsgsbase, + ia32_tsc_adjust_msr, + bmi1, + hle, + avx2, + smep, + bmi2, + enhanced_rep_movsb_stosb, + invpcid, + rtm, + pqm, + deprecates_fpu_cs_ds, + pqe, + rdseed, + adx, + smap, + intel_processor_trace, + prefetchwt1 + }); + + delegate_flag!(extended_processor_signature, { + lahf_sahf_in_64_bit, + lzcnt, + prefetchw, + syscall_sysret_in_64_bit, + execute_disable, + gigabyte_pages, + rdtscp_and_ia32_tsc_aux, + intel_64_bit_architecture + }); + + delegate_flag!(time_stamp_counter, { invariant_tsc }); +} +/* +cfg_if! { + if #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] { + + #[test] + fn basic_genuine_intel() { + let (_, b, c, d) = cpuid(RequestType::BasicInformation); + + assert_eq!(b"Genu", as_bytes(&b)); + assert_eq!(b"ntel", as_bytes(&c)); + assert_eq!(b"ineI", as_bytes(&d)); + } + + #[test] + fn brand_string_contains_intel() { + assert!(master().unwrap().brand_string().unwrap().contains("Intel(R)")) + } + + } else {} +} +*/ diff --git a/kernel/src/arch/x86_64/graphics.rs b/kernel/src/arch/x86_64/graphics.rs new file mode 100644 index 0000000..3ad4346 --- /dev/null +++ b/kernel/src/arch/x86_64/graphics.rs @@ -0,0 +1,56 @@ +use virtio_drivers::transport::Transport; + +use crate::arch::virtio::AbleosHal; + +use {embedded_graphics::pixelcolor::Rgb888, virtio_drivers::device::gpu::VirtIOGpu}; + +use { + able_graphics_library::raw_pixel::Display, + embedded_graphics::prelude::*, + limine::FramebufferRequest, + spin::{Lazy, Mutex}, +}; + +pub static DISPLAY: Lazy> = Lazy::new(|| { + static FB_REQ: FramebufferRequest = FramebufferRequest::new(0); + let fb1 = &FB_REQ.get_response().get().unwrap().framebuffers()[0]; + let m = Mutex::new(Display { + fb: fb1.address.as_ptr().unwrap().cast(), + size: Size::new(fb1.width as u32, fb1.height as u32), + color: Rgb888::WHITE, + }); + log::info!("Graphics initialised"); + m +}); + +pub fn init() { + Lazy::force(&DISPLAY); +} +pub fn virtio_gpu(transport: T) { + let mut gpu = VirtIOGpu::::new(transport).expect("failed to create gpu driver"); + let (width, height) = gpu.resolution().expect("failed to get resolution"); + let width = width as usize; + let height = height as usize; + log::info!("GPU resolution is {}x{}", width, height); + let fb = gpu.setup_framebuffer().expect("failed to get fb"); + for y in 0..height { + for x in 0..width { + let idx = (y * width + x) * 4; + fb[idx] = x as u8; + fb[idx + 1] = y as u8; + fb[idx + 2] = (x + y) as u8; + } + } + gpu.flush().expect("failed to flush"); + //delay some time + log::info!("virtio-gpu show graphics...."); + for _ in 0..100000 { + for _ in 0..100000 { + unsafe { + core::arch::asm!("nop"); + } + } + } + + log::info!("virtio-gpu test finished"); +} diff --git a/kernel/src/arch/x86_64/interrupts.rs b/kernel/src/arch/x86_64/interrupts.rs index 090f031..651b155 100644 --- a/kernel/src/arch/x86_64/interrupts.rs +++ b/kernel/src/arch/x86_64/interrupts.rs @@ -1,6 +1,10 @@ -use spin::{Lazy, Mutex}; -use x2apic::lapic::{LocalApic, LocalApicBuilder}; -use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}; +use { + spin::{Lazy, Mutex}, + x2apic::lapic::{LocalApic, LocalApicBuilder}, + x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode}, +}; + +use log::info; use crate::interp::wasm; @@ -14,6 +18,7 @@ pub unsafe fn init() { #[repr(u8)] enum Interrupt { Timer = 32, + Keyboard = 33, ApicErr = u8::MAX - 1, Spurious = u8::MAX, } @@ -46,6 +51,7 @@ static IDT: Lazy = Lazy::new(|| { idt[Interrupt::Spurious as usize].set_handler_fn(spurious); idt[Interrupt::Timer as usize].set_handler_fn(timer); + idt }); diff --git a/kernel/src/arch/x86_64/logging.rs b/kernel/src/arch/x86_64/logging.rs index 5f64096..e01a506 100644 --- a/kernel/src/arch/x86_64/logging.rs +++ b/kernel/src/arch/x86_64/logging.rs @@ -1,46 +1,48 @@ //! Logging (as in terms of console / serial output) -use core::fmt::Write; -use limine::{TerminalRequest, TerminalResponse}; -use spin::{Lazy, Mutex}; -use uart_16550::SerialPort; +use { + core::fmt::Write, + limine::{TerminalRequest, TerminalResponse}, + spin::{Lazy, Mutex}, + uart_16550::SerialPort, +}; -static SERIAL_CONSOLE: Mutex = Mutex::new(unsafe { SerialPort::new(0x3f8) }); -static TERMINAL_LOGGER: Lazy> = Lazy::new(|| Mutex::new(TermLogger::new())); +static SERIAL_CONSOLE: Mutex = Mutex::new(unsafe { SerialPort::new(0x3F8) }); +// static TERMINAL_LOGGER: Lazy> = Lazy::new(|| Mutex::new(TermLogger::new())); pub fn init() { SERIAL_CONSOLE.lock().init(); - Lazy::force(&TERMINAL_LOGGER); + // Lazy::force(&TERMINAL_LOGGER); } pub fn log(args: core::fmt::Arguments<'_>) -> core::fmt::Result { x86_64::instructions::interrupts::without_interrupts(|| { - TERMINAL_LOGGER.lock().write_fmt(args)?; + // TERMINAL_LOGGER.lock().write_fmt(args)?; SERIAL_CONSOLE.lock().write_fmt(args) }) } -struct TermLogger(&'static TerminalResponse); -unsafe impl Send for TermLogger {} -impl TermLogger { - pub fn new() -> Self { - static TERM_REQ: TerminalRequest = TerminalRequest::new(0); - Self( - TERM_REQ - .get_response() - .get() - .expect("failed to get terminal response"), - ) - } -} +// struct TermLogger(&'static TerminalResponse); +// unsafe impl Send for TermLogger {} +// impl TermLogger { +// pub fn new() -> Self { +// static TERM_REQ: TerminalRequest = TerminalRequest::new(0); +// Self( +// TERM_REQ +// .get_response() +// .get() +// .expect("failed to get terminal response"), +// ) +// } +// } -impl Write for TermLogger { - fn write_str(&mut self, s: &str) -> core::fmt::Result { - if let (Some(w), ts) = (self.0.write(), self.0.terminals()) { - for term in ts { - w(term, s); - } - } - Ok(()) - } -} +// impl Write for TermLogger { +// fn write_str(&mut self, s: &str) -> core::fmt::Result { +// if let (Some(w), ts) = (self.0.write(), self.0.terminals()) { +// for term in ts { +// w(term, s); +// } +// } +// Ok(()) +// } +// } diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index b889364..f12f8fd 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -1,15 +1,26 @@ +use {limine::SmpRequest, xml::XMLElement}; + +use embedded_graphics::pixelcolor::Rgb888; + +use crate::{arch::x86_64::graphics::DISPLAY, kmain::DEVICE_TREE}; + pub mod memory; +mod cpuid; mod gdt; +mod graphics; pub(crate) mod interrupts; mod logging; +pub mod pci; +pub mod virtio; -pub use logging::log; -pub use memory::PAGE_SIZE; +pub use {logging::log, memory::PAGE_SIZE}; -use crate::allocator; -use limine::{HhdmRequest, KernelFileRequest, MemmapRequest, ModuleRequest}; -use x86_64::VirtAddr; +use { + crate::allocator, + limine::{HhdmRequest, KernelFileRequest, MemmapRequest, ModuleRequest}, + x86_64::VirtAddr, +}; extern "C" { fn _initial_kernel_heap_start(); @@ -51,6 +62,65 @@ unsafe extern "C" fn _kernel_start() -> ! { static KFILE_REQ: KernelFileRequest = KernelFileRequest::new(0); static MOD_REQ: ModuleRequest = ModuleRequest::new(0); + static SMP: SmpRequest = SmpRequest::new(0); + let smp = SMP.get_response().get().unwrap(); + use crate::alloc::string::ToString; + + let cpuinfo = cpuid::master().unwrap(); + let brand_string = cpuinfo.brand_string().unwrap_or("Unknown").to_string(); + DEVICE_TREE.force_unlock(); + + pci::init(); + + let mut dt = DEVICE_TREE.lock(); + + let cpus = dt.devices.get_mut("CPUs").unwrap(); + let mut cpu = XMLElement::new("cpu"); + let core_count = smp.cpu_count.to_string(); + cpu.set_attribute("core count", core_count); + cpu.set_attribute("brand string", brand_string); + + let cpu_speed = 0; + + cpu.set_attribute("speed", "Unknown"); + + if false { + // disable() // disable interrupts (if still not done) + let i = 0; + let start = cpuinfo.time_stamp_counter(); + + log::info!("{:?}", start.unwrap().invariant_tsc()); + for x in 0..1000 {} + let end = cpuinfo.time_stamp_counter(); + } + + let mut cpu_features = xml::XMLElement::new("CPU Features"); + { + let x2 = cpuinfo.x2apic(); + let apic = cpuinfo.apic(); + let avx = cpuinfo.avx(); + + cpu_features.set_attribute("apic", apic.to_string()); + cpu_features.set_attribute("avx", apic.to_string()); + cpu_features.set_attribute("x2apic", x2.to_string()); + } + + cpu.set_child(cpu_features); + cpus.push(cpu); + drop(dt); + + // Graphics test + { + let mut dis = DISPLAY.lock(); + use {able_graphics_library::raw_pixel, embedded_graphics::prelude::RgbColor}; + + dis.set_color(Rgb888::GREEN); + + dis.line(1, 1, 10, 10, 4); + dis.line(1, 200, 100, 10, 1); + dis.line(1, 1, 200, 10, 1); + } + crate::kmain::kmain( KFILE_REQ .get_response() @@ -84,8 +154,7 @@ pub fn sloop() -> ! { } pub fn hardware_random_u64() -> u64 { - use log::trace; - use rdrand::RdRand; + use {log::trace, rdrand::RdRand}; let gen = RdRand::new().unwrap(); let ret = gen.try_next_u64().unwrap(); trace!("Random {}", ret); diff --git a/kernel/src/arch/x86_64/pci/mod.rs b/kernel/src/arch/x86_64/pci/mod.rs new file mode 100644 index 0000000..eb1696f --- /dev/null +++ b/kernel/src/arch/x86_64/pci/mod.rs @@ -0,0 +1,490 @@ +#[derive(Copy, Clone, Debug)] +/// A struct containing info about a PCI device. +pub struct PciDeviceInfo { + pub header_type: u8, + pub device: u8, + pub bus: u8, + pub device_id: DeviceID, + pub full_class: PciFullClass, + pub rev_id: u8, +} +use crate::alloc::string::ToString; +/// Enumerate PCI devices and run initialisation routines on ones we support +pub fn init() { + let mut dt = DEVICE_TREE.lock(); + dt.devices + .insert("Unidentified PCI".to_string(), alloc::vec![]); + let mut devices = alloc::vec![]; + + for bus in 0..=255 { + for device in 0..32 { + if let Some(device_info) = check_device(bus, device) { + let vendor = device_info.device_id.vendor; + let id = device_info.device_id.id; + use Vendor::*; + let (dev_type, dev_name) = match (vendor, id) { + (VirtIO, 4176) => ("GPUs", "VirtIO PCI GPU"), + (CirrusLogic, 184) => ("GPUs", "Cirrus SVGA"), //GD 5446? + (_, _) => ("Unidentified PCI", "UNKNOWN DEVICE"), + }; + + let mut dev = xml::XMLElement::new(dev_name); + let mut pci_info = xml::XMLElement::new("PCI Info"); + pci_info.set_attribute("id", id); + pci_info.set_attribute("device", device_info.device); + pci_info.set_attribute("vendor", vendor); + pci_info.set_attribute("class", device_info.full_class.to_string()); + dev.set_child(pci_info); + devices.push((dev_type, dev)); + } + } + } + for (dev_type, dev) in devices { + if let Some(abc) = dt.devices.get_mut(dev_type) { + abc.push(dev); + } + } +} + +pub fn check_device(bus: u8, device: u8) -> Option { + assert!(device < 32); + let (device_id, vendor_id) = get_ids(bus, device, 0); + if vendor_id == 0xFFFF { + // Device doesn't exist + return None; + } + + let reg2 = unsafe { pci_config_read(bus, device, 0, 0x8) }; + let class = ((reg2 >> 16) & 0x0000FFFF) as u16; + let pci_class = PciFullClass::from_u16(class); + let header_type = get_header_type(bus, device, 0); + + Some(PciDeviceInfo { + header_type, + device, + bus, + device_id: DeviceID { + vendor: vendor_id.into(), + id: device_id, + }, + full_class: pci_class, + rev_id: (reg2 & 0x000000FF) as u8, + }) +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct DeviceID { + pub vendor: Vendor, + pub id: u16, +} +impl DeviceID { + pub const fn new(vendor: Vendor, id: u16) -> Self { + Self { vendor, id } + } +} + +#[derive(PartialEq, Debug, Copy, Clone, Eq)] +#[repr(u16)] +pub enum Vendor { + ThreeDfxInteractiveInc = 0x121A, + ThreeDLabs = 0x3D3D, + AllianceSemiconductorCorp = 0x1142, + ARKLogicInc = 0xEDD8, + ATITechnologiesInc = 0x1002, + AvanceLogicIncALI = 0x1005, + ChipsandTechnologies = 0x102C, + CirrusLogic = 0x1013, + Compaq = 0x0E11, + CyrixCorp = 0x1078, + DiamondMultimediaSystems = 0x1092, + DigitalEquipmentCorp = 0x1011, + Iit = 0x1061, + IntegratedMicroSolutionsInc = 0x10E0, + IntelCorp = 0x8086, + IntergraphicsSystems = 0x10EA, + MacronixInc = 0x10D9, + MatroxGraphicsInc = 0x102B, + MiroComputersProductsAG = 0x1031, + NationalSemiconductorCorp = 0x100B, + NeoMagicCorp = 0x10C8, + Number9ComputerCompany = 0x105D, + NVidiaCorporation = 0x10DE, + NVidiaSgsthomson = 0x12D2, + OakTechnologyInc = 0x104E, + Qemu = 0x1234, + QuantumDesignsHKLtd = 0x1098, + Real3D = 0x003D, + Rendition = 0x1163, + S3Inc = 0x5333, + SierraSemiconductor = 0x10A8, + SiliconIntegratedSystemsSiS = 0x1039, + SiliconMotionInc = 0x126F, + STBSystemsInc = 0x10B4, + TexasInstruments = 0x104C, + ToshibaAmericaInfoSystems = 0x1179, + TridentMicrosystems = 0x1023, + TsengLabsInc = 0x100C, + TundraSemiconductorCorp = 0x10E3, + VIATechnologiesInc = 0x1106, + VirtIO = 0x1AF4, + VMWareInc = 0x15AD, + Weitek = 0x100E, + Unknown(u16), +} + +impl From for Vendor { + fn from(vendor_id: u16) -> Self { + use Vendor::*; + match vendor_id { + 0x121A => ThreeDfxInteractiveInc, + 0x3D3D => ThreeDLabs, + 0x1142 => AllianceSemiconductorCorp, + 0xEDD8 => ARKLogicInc, + 0x1002 => ATITechnologiesInc, + 0x1005 => AvanceLogicIncALI, + 0x102C => ChipsandTechnologies, + 0x1013 => CirrusLogic, + 0x0E11 => Compaq, + 0x1078 => CyrixCorp, + 0x1092 => DiamondMultimediaSystems, + 0x1011 => DigitalEquipmentCorp, + 0x1061 => Iit, + 0x10E0 => IntegratedMicroSolutionsInc, + 0x8086 => IntelCorp, + 0x10EA => IntergraphicsSystems, + 0x10D9 => MacronixInc, + 0x102B => MatroxGraphicsInc, + 0x1031 => MiroComputersProductsAG, + 0x100B => NationalSemiconductorCorp, + 0x10C8 => NeoMagicCorp, + 0x105D => Number9ComputerCompany, + 0x10DE => NVidiaCorporation, + 0x12D2 => NVidiaSgsthomson, + 0x104E => OakTechnologyInc, + 0x1234 => Qemu, + 0x1098 => QuantumDesignsHKLtd, + 0x003D => Real3D, + 0x1163 => Rendition, + 0x5333 => S3Inc, + 0x10A8 => SierraSemiconductor, + 0x1039 => SiliconIntegratedSystemsSiS, + 0x126F => SiliconMotionInc, + 0x10B4 => STBSystemsInc, + 0x104C => TexasInstruments, + 0x1179 => ToshibaAmericaInfoSystems, + 0x1023 => TridentMicrosystems, + 0x100C => TsengLabsInc, + 0x10E3 => TundraSemiconductorCorp, + 0x1106 => VIATechnologiesInc, + 0x1AF4 => VirtIO, + 0x15AD => VMWareInc, + 0x100E => Weitek, + id => Unknown(id), + } + } +} + +impl Into for Vendor { + fn into(self) -> u16 { + use Vendor::*; + match self { + ThreeDfxInteractiveInc => 0x121A, + ThreeDLabs => 0x3D3D, + AllianceSemiconductorCorp => 0x1142, + ARKLogicInc => 0xEDD8, + ATITechnologiesInc => 0x1002, + AvanceLogicIncALI => 0x1005, + ChipsandTechnologies => 0x102C, + CirrusLogic => 0x1013, + Compaq => 0x0E11, + CyrixCorp => 0x1078, + DiamondMultimediaSystems => 0x1092, + DigitalEquipmentCorp => 0x1011, + Iit => 0x1061, + IntegratedMicroSolutionsInc => 0x10E0, + IntelCorp => 0x8086, + IntergraphicsSystems => 0x10EA, + MacronixInc => 0x10D9, + MatroxGraphicsInc => 0x102B, + MiroComputersProductsAG => 0x1031, + NationalSemiconductorCorp => 0x100B, + NeoMagicCorp => 0x10C8, + Number9ComputerCompany => 0x105D, + NVidiaCorporation => 0x10DE, + NVidiaSgsthomson => 0x12D2, + OakTechnologyInc => 0x104E, + Qemu => 0x1234, + QuantumDesignsHKLtd => 0x1098, + Real3D => 0x003D, + Rendition => 0x1163, + S3Inc => 0x5333, + SierraSemiconductor => 0x10A8, + SiliconIntegratedSystemsSiS => 0x1039, + SiliconMotionInc => 0x126F, + STBSystemsInc => 0x10B4, + TexasInstruments => 0x104C, + ToshibaAmericaInfoSystems => 0x1179, + TridentMicrosystems => 0x1023, + TsengLabsInc => 0x100C, + TundraSemiconductorCorp => 0x10E3, + VIATechnologiesInc => 0x1106, + VirtIO => 0x1AF4, + VMWareInc => 0x15AD, + Weitek => 0x100E, + Unknown(id) => id, + } + } +} + +impl core::fmt::Display for Vendor { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + use Vendor::*; + + match self { + Qemu => write!(f, "QEMU (0x1234)"), + VirtIO => write!(f, "VirtIO (0x1AF4)"), + VMWareInc => write!(f, "VMWARE (0x15AD)"), + S3Inc => write!(f, "S3 Incorporated (0x5333)"), + IntelCorp => write!(f, "Intel Corp. (0x8086)"), + ATITechnologiesInc => write!(f, "ATI (0x1002)"), + Unknown(id) => write!(f, "Unknown ({:#6})", id), + other => write!(f, "{other:?}"), + }?; + + Ok(()) + } +} + +use core::fmt::Display; + +use crate::kmain::DEVICE_TREE; + +use {alloc::fmt::format, x86_64::instructions::port::Port}; + +#[allow(non_camel_case_types, dead_code)] +#[derive(Debug, Clone, Copy, PartialEq)] +#[repr(C)] +/// Class specification for a PCI device +pub enum PciClass { + Unclassified = 0x00, + MassStorage = 0x01, + Network = 0x02, + Display = 0x03, + Multimedia = 0x04, + Memory = 0x05, + Bridge = 0x06, + Unknown = 0xFF, +} + +impl From for PciClass { + /// Convert a u8 into the corresponding PciClass + fn from(n: u8) -> Self { + use PciClass::*; + match n { + 0x00 => Unclassified, + 0x01 => MassStorage, + 0x02 => Network, + 0x03 => Display, + 0x04 => Multimedia, + 0x05 => Memory, + 0x06 => Bridge, + _ => Unknown, + } + } +} + +#[allow(non_camel_case_types, dead_code)] +#[derive(Debug, Clone, Copy, PartialEq)] +#[repr(C)] +/// Full class specification (type and subtype) for a PCI device. +/// +/// Uses non-camel-case types for readability. +pub enum PciFullClass { + Unclassified_NonVgaCompatible = 0x0000, + Unclassified_VgaCompatible = 0x0001, + + MassStorage_ScsiBus = 0x0100, + MassStorage_IDE = 0x0101, + MassStorage_Floppy = 0x0102, + MassStorage_IpiBus = 0x0103, + MassStorage_RAID = 0x0104, + MassStorage_ATA = 0x0105, + MassStorage_SATA = 0x0106, + MassStorage_SerialSCSI = 0x0107, + MassStorage_NVM = 0x0108, + MassStorage_Other = 0x0180, + + Network_Ethernet = 0x0200, + Network_TokenRing = 0x0201, + Network_FDDI = 0x0202, + Network_ATM = 0x0203, + Network_ISDN = 0x0204, + Network_WorldFlip = 0x0205, + Network_PICMG = 0x0206, + Network_Infiniband = 0x0207, + Network_Fabric = 0x0208, + Network_Other = 0x0280, + + Display_VGA = 0x0300, + Display_XGA = 0x0301, + Display_3D = 0x0302, + Display_Other = 0x0380, + + Multimedia_Video = 0x0400, + Multimedia_AudioController = 0x0401, + Multimedia_Telephony = 0x0402, + Multimedia_AudioDevice = 0x0403, + Multimedia_Other = 0x0480, + + Memory_RAM = 0x0500, + Memory_Flash = 0x0501, + Memory_Other = 0x0580, + + Bridge_Host = 0x0600, + Bridge_ISA = 0x0601, + Bridge_EISA = 0x0602, + Bridge_MCA = 0x0603, + Bridge_PciToPci = 0x0604, + Bridge_PCMCIA = 0x0605, + Bridge_NuBus = 0x0606, + Bridge_CardBus = 0x0607, + Bridge_RACEway = 0x0608, + Bridge_PciToPciSemiTransparent = 0x0609, + Bridge_InfinibandToPci = 0x060A, + Bridge_Other = 0x0680, + + Unknown = 0xFFFF, +} + +impl PciFullClass { + // listen, i know this sucks, but i didn't want to include + // `num`, `num-traits` and `num-derive` as dependencies for + // this crate just for a convenience function + /// Convert a u16 into the corresponding PciFullClass + pub fn from_u16(n: u16) -> PciFullClass { + match n { + 0x0000 => PciFullClass::Unclassified_NonVgaCompatible, + 0x0001 => PciFullClass::Unclassified_VgaCompatible, + + 0x0100 => PciFullClass::MassStorage_ScsiBus, + 0x0101 => PciFullClass::MassStorage_IDE, + 0x0102 => PciFullClass::MassStorage_Floppy, + 0x0103 => PciFullClass::MassStorage_IpiBus, + 0x0104 => PciFullClass::MassStorage_RAID, + 0x0105 => PciFullClass::MassStorage_ATA, + 0x0106 => PciFullClass::MassStorage_SATA, + 0x0107 => PciFullClass::MassStorage_SerialSCSI, + 0x0108 => PciFullClass::MassStorage_NVM, + 0x0180 => PciFullClass::MassStorage_Other, + + 0x0200 => PciFullClass::Network_Ethernet, + 0x0201 => PciFullClass::Network_TokenRing, + 0x0202 => PciFullClass::Network_FDDI, + 0x0203 => PciFullClass::Network_ATM, + 0x0204 => PciFullClass::Network_ISDN, + 0x0205 => PciFullClass::Network_WorldFlip, + 0x0206 => PciFullClass::Network_PICMG, + 0x0207 => PciFullClass::Network_Infiniband, + 0x0208 => PciFullClass::Network_Fabric, + 0x0280 => PciFullClass::Network_Other, + + 0x0300 => PciFullClass::Display_VGA, + 0x0301 => PciFullClass::Display_XGA, + 0x0302 => PciFullClass::Display_3D, + 0x0380 => PciFullClass::Display_Other, + + 0x0400 => PciFullClass::Multimedia_Video, + 0x0401 => PciFullClass::Multimedia_AudioController, + 0x0402 => PciFullClass::Multimedia_Telephony, + 0x0403 => PciFullClass::Multimedia_AudioDevice, + 0x0480 => PciFullClass::Multimedia_Other, + + 0x0500 => PciFullClass::Memory_RAM, + 0x0501 => PciFullClass::Memory_Flash, + 0x0580 => PciFullClass::Memory_Other, + + 0x0600 => PciFullClass::Bridge_Host, + 0x0601 => PciFullClass::Bridge_ISA, + 0x0602 => PciFullClass::Bridge_EISA, + 0x0603 => PciFullClass::Bridge_MCA, + 0x0604 => PciFullClass::Bridge_PciToPci, + 0x0605 => PciFullClass::Bridge_PCMCIA, + 0x0606 => PciFullClass::Bridge_NuBus, + 0x0607 => PciFullClass::Bridge_CardBus, + 0x0608 => PciFullClass::Bridge_RACEway, + 0x0609 => PciFullClass::Bridge_PciToPciSemiTransparent, + 0x060A => PciFullClass::Bridge_InfinibandToPci, + 0x0680 => PciFullClass::Bridge_Other, + + _ => PciFullClass::Unknown, + } + } + + /// Convert a PciFullClass to its u16 representation + pub fn as_u16(&self) -> u16 { + *self as u16 + } +} + +impl From for PciFullClass { + /// Convert a u16 into the corresponding PciFullClass + fn from(n: u16) -> Self { + Self::from_u16(n) + } +} + +impl Display for PciFullClass { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{:?} ({:#06X})", self, self.as_u16())?; + + Ok(()) + } +} + +unsafe fn pci_config_read(bus: u8, device: u8, func: u8, offset: u8) -> u32 { + let bus = bus as u32; + let device = device as u32; + let func = func as u32; + let offset = offset as u32; + // construct address param + let address = + ((bus << 16) | (device << 11) | (func << 8) | (offset & 0xFC) | 0x80000000) as u32; + + // write address + Port::new(0xCF8).write(address); + + // read data + Port::new(0xCFC).read() +} + +unsafe fn pci_config_write(bus: u8, device: u8, func: u8, offset: u8, value: u32) { + let bus = bus as u32; + let device = device as u32; + let func = func as u32; + let offset = offset as u32; + // construct address param + let address = + ((bus << 16) | (device << 11) | (func << 8) | (offset & 0xFC) | 0x80000000) as u32; + + // write address + Port::new(0xCF8).write(address); + + // write data + Port::new(0xCFC).write(value); +} + +fn get_header_type(bus: u8, device: u8, function: u8) -> u8 { + assert!(device < 32); + assert!(function < 8); + let res = unsafe { pci_config_read(bus, device, function, 0x0C) }; + ((res >> 16) & 0xFF) as u8 +} + +fn get_ids(bus: u8, device: u8, function: u8) -> (u16, u16) { + assert!(device < 32); + assert!(function < 8); + let res = unsafe { pci_config_read(bus, device, function, 0) }; + let dev_id = ((res >> 16) & 0xFFFF) as u16; + let vnd_id = (res & 0xFFFF) as u16; + (dev_id, vnd_id) +} diff --git a/kernel/src/arch/x86_64/virtio/mod.rs b/kernel/src/arch/x86_64/virtio/mod.rs new file mode 100644 index 0000000..4af981d --- /dev/null +++ b/kernel/src/arch/x86_64/virtio/mod.rs @@ -0,0 +1,38 @@ +use { + alloc::alloc::{alloc_zeroed, dealloc, handle_alloc_error}, + core::{alloc::Layout, ptr::NonNull}, + virtio_drivers::{BufferDirection, Hal, PhysAddr, PAGE_SIZE}, +}; + +pub fn test() { + let ps = virtio_drivers::PAGE_SIZE; +} + +pub struct AbleosHal; + +unsafe impl Hal for AbleosHal { + fn dma_alloc(pages: usize, _direction: BufferDirection) -> (PhysAddr, NonNull) { + todo!(); + } + + unsafe fn dma_dealloc(_paddr: PhysAddr, vaddr: NonNull, pages: usize) -> i32 { + todo!() + } + + unsafe fn mmio_phys_to_virt(paddr: PhysAddr, _size: usize) -> NonNull { + NonNull::new(paddr as _).unwrap() + } + + unsafe fn share(buffer: NonNull<[u8]>, _direction: BufferDirection) -> PhysAddr { + todo!() + } + + unsafe fn unshare(_paddr: PhysAddr, _buffer: NonNull<[u8]>, _direction: BufferDirection) { + // Nothing to do, as the host already has access to all memory and we didn't copy the buffer + // anywhere else. + todo!() + } +} +fn virt_to_phys(vaddr: usize) -> PhysAddr { + vaddr +} diff --git a/kernel/src/device_tree.rs b/kernel/src/device_tree.rs new file mode 100644 index 0000000..62c01f8 --- /dev/null +++ b/kernel/src/device_tree.rs @@ -0,0 +1,71 @@ +use alloc::{string::String, vec::Vec}; + +use {crate::alloc::string::ToString, core::fmt, hashbrown::HashMap, xml::XMLElement}; +pub type Device = xml::XMLElement; + +#[derive(Debug)] +pub struct DeviceTree { + pub devices: HashMap>, +} +impl DeviceTree { + pub fn new() -> Self { + let mut dt = Self { + devices: HashMap::new(), + }; + + // Human input devices + { + dt.devices.insert("Mice".to_string(), Vec::new()); + dt.devices.insert("Keyboards".to_string(), Vec::new()); + dt.devices.insert("Controllers".to_string(), Vec::new()); + // Human Input Devices that do not fit into other catagories go in this spot + dt.devices.insert("Generic HIDs".to_string(), Vec::new()); + } + + { + dt.devices.insert("Disk Drives".to_string(), Vec::new()); + dt.devices.insert("CD Drives".to_string(), Vec::new()); + } + + dt.devices.insert("Batteries".to_string(), Vec::new()); + dt.devices.insert("Monitors".to_string(), Vec::new()); + dt.devices.insert("GPUs".to_string(), Vec::new()); + dt.devices.insert("CPUs".to_string(), Vec::new()); + + dt.devices.insert("USB".to_string(), Vec::new()); + dt.devices.insert("Serial Ports".to_string(), Vec::new()); + dt.devices.insert("Cameras".to_string(), Vec::new()); + dt.devices + .insert("Biometric Devices".to_string(), Vec::new()); + + dt + } +} +use crate::utils::TAB; + +impl fmt::Display for DeviceTree { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for (device_type, devices) in &self.devices { + writeln!(f, "\r{TAB}{}/\r", device_type)?; + for device in devices { + writeln!(f, "{TAB}{TAB}{}/\r", device.name)?; + for attr in &device.attributes { + writeln!(f, "{TAB}{TAB}{TAB}{}\r", attr)?; + } + for child in &device.children { + writeln!(f, "{TAB}{TAB}{TAB}{}\r", child.name)?; + for attr in &child.attributes { + writeln!(f, "{TAB}{TAB}{TAB}{TAB}{}\r", attr)?; + } + for child in &child.children { + writeln!(f, "{TAB}{TAB}{TAB}{TAB}{}\r", child.name)?; + for attr in &child.attributes { + writeln!(f, "{TAB}{TAB}{TAB}{TAB}{TAB}{}\r", attr)?; + } + } + } + } + } + Ok(()) + } +} diff --git a/kernel/src/interp/mod.rs b/kernel/src/interp/mod.rs index 550cd0b..97e7cb5 100644 --- a/kernel/src/interp/mod.rs +++ b/kernel/src/interp/mod.rs @@ -18,9 +18,9 @@ use alloc::vec; #[derive(Debug)] pub struct WasmContext { - pub proc_id: Option, + pub proc_id: Option, pub instance: Instance, - pub store: Store, + pub store: Store, } pub fn wasm() -> Result<(), wasmi::Error> { diff --git a/kernel/src/kmain.rs b/kernel/src/kmain.rs index 19388f4..eb54979 100644 --- a/kernel/src/kmain.rs +++ b/kernel/src/kmain.rs @@ -2,14 +2,13 @@ // use std::collections::HashMap; -use alloc::vec::Vec; -use log::{info, trace}; -use spin::{Lazy, Mutex}; +use { + alloc::vec::Vec, + log::{info, trace}, + spin::{Lazy, Mutex}, +}; -use crate::arch::{hardware_random_u64, sloop}; -use crate::handle::Handle; -use crate::schedule::Scheduler; -use crate::{interp, task}; +use crate::{arch, device_tree::DeviceTree, schedule::Scheduler}; use crate::alloc::string::ToString; @@ -23,13 +22,13 @@ pub fn kmain(cmdline: &str, bootstrap: Option<&'static [u8]>) -> ! { let kcmd = clparse::Arguments::parse(cmdline.to_string()).unwrap(); log::info!("Cmdline: {kcmd:?}"); - // if kcmd.arguments.get("baka") == Some(&"true".to_string()) { - // let _ = crate::arch::log(format_args!(include_str!("../data/⑨. バカ"))); - // } + if kcmd.arguments.get("baka") == Some(&"true".to_string()) { + let _ = crate::arch::log(format_args!(include_str!("../data/⑨. バカ"))); + } - // if kcmd.arguments.get("foobles") == Some(&"true".to_string()) { - // let _ = crate::arch::log(format_args!("foobles\n")); - // } + if kcmd.arguments.get("foobles") == Some(&"true".to_string()) { + let _ = crate::arch::log(format_args!("foobles\n\r")); + } let bootstrap = bootstrap/*.expect("no bootstrap found")*/; match bootstrap { @@ -38,24 +37,9 @@ pub fn kmain(cmdline: &str, bootstrap: Option<&'static [u8]>) -> ! { info!("No bootstrap module loaded.") } } + let mut dt = DEVICE_TREE.lock(); - // use xml::XMLElement; - // let kcmd = XMLElement::new("cmdline"); - // let hnd = Handle::new(); - // kcmd.set_attribute("") - // OBJECTS.lock().insert(hnd, kcmd); - - let abc = interp::wasm(); - - trace!("{:?}", abc); - - // let sch = SCHEDULER; - // let mut sch = sch.lock(); - // let wc = interp::build_wasm_context(alloc::vec::Vec::new()).unwrap(); - // sch.schedule(wc, crate::schedule::ContextWake::None); - - // sch.run(); - + info!("Device Tree {}", &dt); crate::arch::sloop() } @@ -63,3 +47,8 @@ pub const SCHEDULER: Lazy> = Lazy::new(|| { let mut sch = Scheduler::new(); Mutex::new(sch) }); + +pub static DEVICE_TREE: Lazy> = Lazy::new(|| { + let mut dt = DeviceTree::new(); + Mutex::new(dt) +}); diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index c2e1808..3068586 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -16,6 +16,7 @@ extern crate alloc; mod allocator; mod arch; +pub mod device_tree; pub mod handle; pub mod interp; mod kmain; @@ -23,6 +24,7 @@ mod logger; mod memory; mod schedule; mod task; +pub mod utils; use versioning::Version; diff --git a/kernel/src/utils.rs b/kernel/src/utils.rs new file mode 100644 index 0000000..5477d28 --- /dev/null +++ b/kernel/src/utils.rs @@ -0,0 +1 @@ +pub const TAB: &str = " "; diff --git a/repbuild/limine.cfg b/repbuild/limine.cfg index 8afc3c6..149bd06 100644 --- a/repbuild/limine.cfg +++ b/repbuild/limine.cfg @@ -2,7 +2,7 @@ ${ABLEOS_KERNEL}=boot:///kernel # TODO: Make a boot background image for ableOS DEFAULT_ENTRY=1 -TIMEOUT=50 +TIMEOUT=0 VERBOSE=yes INTERFACE_RESOLUTION=1024x768 # Terminal related settings diff --git a/repbuild/src/main.rs b/repbuild/src/main.rs index 9ddc68e..3242f3f 100644 --- a/repbuild/src/main.rs +++ b/repbuild/src/main.rs @@ -1,6 +1,8 @@ -use error_stack::{bail, report, Context, IntoReport, Result, ResultExt}; -use fatfs::{FileSystem, FormatVolumeOptions, FsOptions, ReadWriteSeek}; -use std::{fmt::Display, fs::File, io, path::Path, process::Command}; +use { + error_stack::{bail, report, Context, IntoReport, Result, ResultExt}, + fatfs::{FileSystem, FormatVolumeOptions, FsOptions, ReadWriteSeek}, + std::{fmt::Display, fs::File, io, path::Path, process::Command}, +}; fn main() -> Result<(), Error> { env_logger::init(); @@ -153,19 +155,32 @@ fn run(release: bool, target: Target) -> Result<(), Error> { if target == Target::X86_64 { #[rustfmt::skip] com.args([ + + + + "-bios", std::env::var("REPBUILD_QEMU_FIRMWARE_PATH") .as_deref() .unwrap_or("/usr/share/ovmf/x64/OVMF_CODE.fd"), "-drive", "file=target/disk.img,format=raw", "-m", "4G", - "-serial", "stdio", - "-smp", "cores=2", + // "-serial", "stdio", + "-smp", "cores=4", + "-vga", "cirrus", + "-device", "virtio-gpu-pci", + + "-device", "virtio-serial,id=virtio-serial0", + "-chardev", "stdio,id=char0,mux=on", + "-device", "virtconsole,chardev=char0", + "-device", "virtio-mouse-pci", + + // "-device", "ati-vga", "model=rage128p" ]); #[cfg(target_os = "linux")] { - com.args(["-enable-kvm", "-cpu", "host"]); + // com.args(["-enable-kvm", "-cpu", "host"]); } }