diff --git a/examples/synthetic.rs b/examples/synthetic.rs new file mode 100644 index 0000000..aad871d --- /dev/null +++ b/examples/synthetic.rs @@ -0,0 +1,484 @@ +//! An example of loading and printing features from a CPUID dump. +extern crate raw_cpuid; + +use raw_cpuid::{ + ApmInfo, CpuIdDump, CpuIdResult, ExtendedFeatureIdentification2, ExtendedFeatures, + ExtendedProcessorFeatureIdentifiers, ExtendedState, ExtendedStateInfo, ExtendedTopologyLevel, + FeatureInfo, L1CacheTlbInfo, L2And3CacheTlbInfo, PerformanceOptimizationInfo, + ProcessorCapacityAndFeatureInfo, ProcessorTopologyInfo, ThermalPowerInfo, Tlb1gbPageInfo, + Vendor, VendorInfo, +}; + +// Construct a CPUID profile that looks something like a virtual machine might see on an AMD Milan +// (e.g. 7003-series) processor. +fn synthetic_milan() -> CpuIdDump { + let mut cpuid = raw_cpuid::CpuId::with_cpuid_reader(CpuIdDump::new()); + let leaf = VendorInfo::amd(); + cpuid.set_vendor_info(Some(leaf)).unwrap(); + cpuid.set_extended_function_info(Some(leaf)).unwrap(); + + let mut leaf = FeatureInfo::new(Vendor::Amd); + + // Set up EAX + leaf.set_extended_family_id(0x00); + leaf.set_extended_family_id(0xA); + leaf.set_base_family_id(0x0F); + leaf.set_base_model_id(0x01); + leaf.set_stepping_id(0x01); + + // Set up EBX + leaf.set_brand_index(0); + leaf.set_cflush_cache_line_size(8); + leaf.set_initial_local_apic_id(0); // Populated dynamically in a real system. + leaf.set_max_logical_processor_ids(0); // Populated dynamically in a real system. + + // Set up ECX + leaf.set_sse3(true); + leaf.set_pclmulqdq(true); + leaf.set_ds_area(false); + leaf.set_monitor_mwait(false); + + leaf.set_cpl(false); + leaf.set_vmx(false); + leaf.set_smx(false); + leaf.set_eist(false); + + leaf.set_tm2(false); + leaf.set_ssse3(true); + leaf.set_cnxtid(false); + // bit 11 is reserved + + leaf.set_fma(true); + leaf.set_cmpxchg16b(true); + // bit 14 is reserved + leaf.set_pdcm(false); + + //bit 16 is reserved + leaf.set_pcid(false); + leaf.set_dca(false); + leaf.set_sse41(true); + + leaf.set_sse42(true); + leaf.set_x2apic(true); + leaf.set_movbe(true); + leaf.set_popcnt(true); + + leaf.set_tsc_deadline(false); + leaf.set_aesni(true); + leaf.set_xsave(true); + leaf.set_oxsave(false); // Populated dynamically in a real system. + + leaf.set_avx(true); + leaf.set_f16c(true); + leaf.set_rdrand(true); + leaf.set_hypervisor(true); // This CPUID leaf will be presented to hypervisor guests + + // Set up EDX + leaf.set_fpu(true); + leaf.set_vme(true); + leaf.set_de(true); + leaf.set_pse(true); + + leaf.set_tsc(true); + leaf.set_msr(true); + leaf.set_pae(true); + leaf.set_mce(true); + + leaf.set_cmpxchg8b(true); + leaf.set_apic(true); + // bit 10 is reserved + leaf.set_sysenter_sysexit(true); + + leaf.set_mtrr(true); + leaf.set_pge(true); + leaf.set_mca(true); + leaf.set_cmov(true); + + leaf.set_pat(true); + leaf.set_pse36(true); + // bit 18 is reserved + leaf.set_clflush(true); + + // bit 20 is reserved + // bit 21 is reserved + // bit 22 is reserved + leaf.set_mmx(true); + + leaf.set_fxsave_fxstor(true); + leaf.set_sse(true); + leaf.set_sse2(true); + // bit 27 is reserved + + leaf.set_htt(true); + // bits 29-31 are not used here. + + cpuid.set_feature_info(Some(leaf)).unwrap(); + + // Leaf 2, 3, 4: all skipped on AMD + + // Leaf 5: Monitor and MWait. These are hidden from the guest, so zero this leaf. + cpuid.set_monitor_mwait_info(None).unwrap(); + + // Leaf 6: Power management and some more feature bits. + let mut leaf = ThermalPowerInfo::empty(); + leaf.set_arat(true); + leaf.set_hw_coord_feedback(false); + + cpuid.set_thermal_power_info(Some(leaf)).unwrap(); + + // Leaf 7: Extended features + let mut leaf = ExtendedFeatures::new(); + leaf.set_fsgsbase(true); + leaf.set_tsc_adjust_msr(false); + leaf.set_sgx(false); + leaf.set_bmi1(true); + + leaf.set_hle(false); + leaf.set_avx2(true); + leaf.set_fdp(false); + leaf.set_smep(true); + + leaf.set_bmi2(true); + leaf.set_rep_movsb_stosb(true); // aka ERMS + leaf.set_invpcid(false); + // Bit 11 is reserved on AMD + + // PQM (bit 12) is clear here + // Bit 13 is reserved on AMD + // Bit 14 is reserved on AMD + // Bit 15 is reserved on AMD + + leaf.set_avx512f(false); + leaf.set_avx512dq(false); + leaf.set_rdseed(true); + leaf.set_adx(true); + + leaf.set_smap(true); + leaf.set_avx512_ifma(false); + // Bit 22 is reserved on AMD + leaf.set_clflushopt(true); + + leaf.set_clwb(true); + // Bit 25 is reserved on AMD + // Bit 26 is reserved on AMD + // Bit 27 is reserved on AMD + + leaf.set_avx512cd(false); + leaf.set_sha(true); + leaf.set_avx512bw(false); + leaf.set_avx512vl(false); + + // Set up leaf 7 ECX + + // Bit 0 is reserved on AMD + leaf.set_avx512vbmi(false); + leaf.set_umip(false); + leaf.set_pku(false); + + leaf.set_ospke(false); + // Bit 5 is reserved on AMD + leaf.set_avx512vbmi2(false); + leaf.set_cet_ss(false); + + leaf.set_gfni(false); + leaf.set_vaes(true); + leaf.set_vpclmulqdq(true); + leaf.set_avx512vnni(false); + + leaf.set_avx512bitalg(false); + // Bit 13 is reserved on AMD + leaf.set_avx512vpopcntdq(false); + // Bit 15 is reserved on AMD + + // Bits 16 through 31 are either reserved or zero on Milan. + + // Set up leaf 7 EDX + leaf.set_fsrm(true); + cpuid.set_extended_feature_info(Some(leaf)).unwrap(); + + // Set up extended topology info (leaf Bh) + let mut levels = Vec::new(); + + let mut topo_level1 = ExtendedTopologyLevel::empty(); + topo_level1.set_shift_right_for_next_apic_id(1); + topo_level1.set_processors(2); + topo_level1.set_level_number(0); + topo_level1.set_level_type(1); + + levels.push(topo_level1); + + let mut topo_level2 = ExtendedTopologyLevel::empty(); + topo_level2.set_shift_right_for_next_apic_id(7); + topo_level2.set_processors(32); + topo_level2.set_level_number(1); + topo_level2.set_level_type(2); + + levels.push(topo_level2); + + let mut topo_level3 = ExtendedTopologyLevel::empty(); + topo_level3.set_level_number(2); + topo_level3.set_level_type(0); // This level is invalid. + + levels.push(topo_level3); + cpuid + .set_extended_topology_info(Some(levels.as_slice())) + .unwrap(); + + // TODO: it is not great to pass another `CpuIdDump` here just to create the type.. + let mut state = ExtendedStateInfo::empty(CpuIdDump::new()); + state.set_xcr0_supports_legacy_x87(true); + state.set_xcr0_supports_sse_128(true); + state.set_xcr0_supports_avx_256(true); + state.set_xsave_area_size_enabled_features(0x340); // Populated dynamically in a real system. + state.set_xsave_area_size_supported_features(0x340); + + state.set_xsaveopt(true); + state.set_xsavec(true); + state.set_xgetbv(true); + state.set_xsave_size(0x340); + + let mut leaves = state.into_leaves().to_vec(); + let mut ymm_state = ExtendedState::empty(); + ymm_state.set_size(0x100); + ymm_state.set_offset(0x240); + leaves.push(Some(ymm_state.into_leaf())); + + cpuid.set_extended_state_info(Some(&leaves[..])).unwrap(); + + let mut leaf = ExtendedProcessorFeatureIdentifiers::empty(Vendor::Amd); + // This is the same as the leaf 1 EAX configured earlier. + leaf.set_extended_signature(0x00A00F11); + + // Set up EBX + leaf.set_pkg_type(0x4); + + // Set up ECX + leaf.set_lahf_sahf(true); + leaf.set_cmp_legacy(false); + leaf.set_svm(false); + leaf.set_ext_apic_space(false); + + leaf.set_alt_mov_cr8(true); + leaf.set_lzcnt(true); + leaf.set_sse4a(true); + leaf.set_misaligned_sse_mode(true); + + leaf.set_prefetchw(true); + // Probably set in hardware, but hide this and the MSR from guests. + leaf.set_osvw(false); + leaf.set_ibs(false); + leaf.set_xop(false); + + leaf.set_skinit(false); + leaf.set_wdt(false); + // Bit 15 is reserved here. + leaf.set_lwp(false); + + leaf.set_fma4(false); // Not on Milan + + // Bits 17-19 are reserved + + // Bit 20 is reserved + // Bit 21 is reserved, formerly TBM + leaf.set_topology_extensions(true); + leaf.set_perf_cntr_extensions(false); + + leaf.set_nb_perf_cntr_extensions(false); + // Bit 25 is reserved + leaf.set_data_access_bkpt_extension(true); + leaf.set_perf_tsc(false); + + leaf.set_perf_cntr_llc_extensions(false); + leaf.set_monitorx_mwaitx(false); + leaf.set_addr_mask_extension(true); + // Bit 31 is reserved + + // Set up EDX + leaf.set_syscall_sysret(true); + leaf.set_execute_disable(true); + leaf.set_mmx_extensions(true); + leaf.set_fast_fxsave_fxstor(true); + leaf.set_1gib_pages(true); + leaf.set_rdtscp(false); + leaf.set_64bit_mode(true); + + cpuid + .set_extended_processor_and_feature_identifiers(Some(leaf)) + .unwrap(); + + // Leaves 8000_0002 through 8000_0005 + cpuid + .set_processor_brand_string(Some(b"AMD EPYC Processor")) + .unwrap(); + + // Set up L1 cache+TLB info (leaf 8000_0005h) + let mut leaf = L1CacheTlbInfo::empty(); + + leaf.set_itlb_2m_4m_size(0x40); + leaf.set_itlb_2m_4m_associativity(0xff); + leaf.set_dtlb_2m_4m_size(0x40); + leaf.set_dtlb_2m_4m_associativity(0xff); + + leaf.set_itlb_4k_size(0x40); + leaf.set_itlb_4k_associativity(0xff); + leaf.set_dtlb_4k_size(0x40); + leaf.set_dtlb_4k_associativity(0xff); + + leaf.set_dcache_line_size(0x40); + leaf.set_dcache_lines_per_tag(0x01); + leaf.set_dcache_associativity(0x08); + leaf.set_dcache_size(0x20); + + leaf.set_icache_line_size(0x40); + leaf.set_icache_lines_per_tag(0x01); + leaf.set_icache_associativity(0x08); + leaf.set_icache_size(0x20); + + cpuid.set_l1_cache_and_tlb_info(Some(leaf)).unwrap(); + + // Set up L2 and L3 cache+TLB info (leaf 8000_0006h) + let mut leaf = L2And3CacheTlbInfo::empty(); + + // Set up leaf 8000_0006h EAX + leaf.set_itlb_2m_4m_size(0x200); + leaf.set_itlb_2m_4m_associativity(0x2); + leaf.set_dtlb_2m_4m_size(0x800); + leaf.set_dtlb_2m_4m_associativity(0x4); + + // Set up leaf 8000_0006h EBX + leaf.set_itlb_4k_size(0x200); + leaf.set_itlb_4k_associativity(0x4); + leaf.set_dtlb_4k_size(0x800); + leaf.set_dtlb_4k_associativity(0x6); + + // Set up leaf 8000_0006h ECX + leaf.set_l2cache_line_size(0x40); + leaf.set_l2cache_lines_per_tag(0x1); + leaf.set_l2cache_associativity(0x6); + leaf.set_l2cache_size(0x0200); + + // Set up leaf 8000_0006h EDX + leaf.set_l3cache_line_size(0x40); + leaf.set_l3cache_lines_per_tag(0x1); + leaf.set_l3cache_associativity(0x9); + leaf.set_l3cache_size(0x0200); + + cpuid.set_l2_l3_cache_and_tlb_info(Some(leaf)).unwrap(); + + // Set up advanced power management info (leaf 8000_0007h) + let mut leaf = ApmInfo::empty(); + leaf.set_invariant_tsc(true); + cpuid.set_advanced_power_mgmt_info(Some(leaf)).unwrap(); + + // Set up processor capacity info (leaf 8000_0008h) + let mut leaf = ProcessorCapacityAndFeatureInfo::empty(); + + // Set up leaf 8000_0008 EAX + leaf.set_physical_address_bits(0x30); + leaf.set_linear_address_bits(0x30); + leaf.set_guest_physical_address_bits(0); + + // St up leaf 8000_0008 EBX + leaf.set_cl_zero(true); + leaf.set_restore_fp_error_ptrs(true); + leaf.set_wbnoinvd(true); + + leaf.set_num_phys_threads(1); // Populated dynamically in a real system. + leaf.set_apic_id_size(0); + leaf.set_perf_tsc_size(0); + + leaf.set_invlpgb_max_pages(0); + leaf.set_max_rdpru_id(0); + + cpuid + .set_processor_capacity_feature_info(Some(leaf)) + .unwrap(); + + // Leaf 8000_000Ah is zeroed out for guests. + cpuid.set_svm_info(None).unwrap(); + + // Set up TLB information for 1GiB pages (leaf 8000_0019h) + let mut leaf = Tlb1gbPageInfo::empty(); + leaf.set_dtlb_l1_1gb_associativity(0xF); + leaf.set_dtlb_l1_1gb_size(0x40); + leaf.set_itlb_l1_1gb_associativity(0xF); + leaf.set_itlb_l1_1gb_size(0x40); + leaf.set_dtlb_l2_1gb_associativity(0xF); + leaf.set_dtlb_l2_1gb_size(0x40); + leaf.set_itlb_l2_1gb_associativity(0); + leaf.set_itlb_l2_1gb_size(0); + cpuid.set_tlb_1gb_page_info(Some(leaf)).unwrap(); + + // Set up processor optimization info (leaf 8000_001Ah) + let mut leaf = PerformanceOptimizationInfo::empty(); + leaf.set_movu(true); + leaf.set_fp256(true); + cpuid.set_performance_optimization_info(Some(leaf)).unwrap(); + + // Leaf 8000_001B + // TODO: no support for leaf 8000_001B, but zero is what we wanted. + // Leaf 8000_001C + // TODO: no support for leaf 8000_001C, but zero is what we wanted. + + // Leaf 8000_001D + let levels = vec![ + CpuIdResult { + eax: 0x00000121, + ebx: 0x01C0003F, + ecx: 0x0000003F, + edx: 0x00000000, + }, + CpuIdResult { + eax: 0x00000122, + ebx: 0x01C0003F, + ecx: 0x0000003F, + edx: 0x00000000, + }, + CpuIdResult { + eax: 0x00000143, + ebx: 0x01C0003F, + ecx: 0x000003FF, + edx: 0x00000002, + }, + CpuIdResult { + eax: 0x00000163, + ebx: 0x03C0003F, + ecx: 0x00007FFF, + edx: 0x00000001, + }, + ]; + cpuid + .set_extended_cache_parameters(Some(levels.as_slice())) + .unwrap(); + + let mut leaf = ProcessorTopologyInfo::empty(); + leaf.set_threads_per_core(2); + cpuid.set_processor_topology_info(Some(leaf)).unwrap(); + + cpuid.set_memory_encryption_info(None).unwrap(); + + let mut leaf = ExtendedFeatureIdentification2::empty(); + leaf.set_no_nested_data_bp(true); + leaf.set_lfence_always_serializing(true); + leaf.set_null_select_clears_base(true); + cpuid + .set_extended_feature_identification_2(Some(leaf)) + .unwrap(); + + cpuid.into_source() +} + +fn main() { + let synthetic_milan = synthetic_milan(); + + for (leaf, subleaf, regs) in synthetic_milan.into_iter() { + let place = match subleaf { + Some(subleaf) => format!("leaf {:08x}h.{}h", leaf, subleaf), + None => format!("leaf {:08x}h", leaf), + }; + + let CpuIdResult { eax, ebx, ecx, edx } = regs; + + println!("{place}: eax=0x{eax:08x} ebx=0x{ebx:08x} ecx=0x{ecx:08x} edx=0x{edx:08x}"); + } +} diff --git a/src/display.rs b/src/display.rs index d3fd4df..64a9afa 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1368,4 +1368,217 @@ pub fn markdown(cpuid: crate::CpuId) { ], ); } + + if let Some(info) = cpuid.get_pqos_extended_feature_info() { + print_title(&skin, "Platform Quality of Service (0x8000_0020/0):"); + table2( + &skin, + &[ + RowGen::tuple("Memory Bandwidth Enforcement", info.has_l3mbe()), + RowGen::tuple("Slow Memory Bandwidth Enforcement", info.has_l3smbe()), + RowGen::tuple("Bandwidth Monitoring Event Configuration", info.has_bmec()), + RowGen::tuple("L3 Range Reservation", info.has_l3rr()), + RowGen::tuple("Assignable Bandwidth Monitoring Counters", info.has_abmc()), + RowGen::tuple( + "Smart Data Cache Injection (SDCI) Allocation Enforcement", + info.has_sdciae(), + ), + ], + ); + if let Some(info) = info.get_l3_memory_bandwidth_enforcement_info() { + print_title( + &skin, + "L3 Memory Bandwidth Enforcement Information (0x8000_0020/1):", + ); + table2( + &skin, + &[ + RowGen::tuple("Bandwidth Length", info.bandwidth_length()), + RowGen::tuple("COS Number", info.cos_max()), + ], + ); + } + if let Some(info) = info.get_l3_slow_memory_bandwidth_enforcement_info() { + print_title( + &skin, + "L3 Slow Memory Bandwidth Enforcement Information (0x8000_0020/2):", + ); + table2( + &skin, + &[ + RowGen::tuple("Bandwidth Length", info.bandwidth_length()), + RowGen::tuple("COS Number", info.cos_max()), + ], + ); + } + if let Some(info) = info.get_bandwidth_monitoring_event_counters_info() { + print_title( + &skin, + "Bandwidth Monitoring Event Counters Information (0x8000_0020/3):", + ); + table2( + &skin, + &[ + RowGen::tuple( + "Number of configurable bandwidth events", + info.number_events(), + ), + RowGen::tuple( + "Reads to local DRAM memory", + info.has_l3_cache_lcl_bw_fill_mon(), + ), + RowGen::tuple( + "Reads to remote DRAM memory", + info.has_l3_cache_rmt_bw_fill_mon(), + ), + RowGen::tuple( + "Non-temporal writes to local memory", + info.has_l3_cache_lcl_bw_nt_wr_mon(), + ), + RowGen::tuple( + "Non-temporal writes to remote memory", + info.has_l3_cache_rmt_bw_nt_wr_mon(), + ), + RowGen::tuple( + "Reads to local memory identified as 'Slow Memory'", + info.has_l3_cache_lcl_slow_bw_fill_mon(), + ), + RowGen::tuple( + "Reads to remote memory identified as 'Slow Memory'", + info.has_l3_cache_rmt_slow_bw_fill_mon(), + ), + RowGen::tuple( + "Dirty victim writes to all types of memory”.", + info.has_l3_cache_vic_mon(), + ), + ], + ); + } + if let Some(info) = info.get_assignable_bandwidth_monitoring_counters_info() { + print_title( + &skin, + "Assignable Bandwidth Monitoring Counters Information (0x8000_0020/5):", + ); + table2( + &skin, + &[ + RowGen::tuple( + "Indicates that QM_CTR bit 61 is an overflow bit", + info.has_overflow_bit(), + ), + RowGen::tuple( + "QM_CTR counter width, offset from 24 bits", + info.counter_size(), + ), + RowGen::tuple("Maximum supported ABMC counter ID", info.max_abmc()), + RowGen::tuple( + "Bandwidth counters can be configured", + info.has_select_cos(), + ), + ], + ); + } + } + + if let Some(info) = cpuid.get_extended_feature_identification_2() { + print_title(&skin, "Extended Feature Identification (0x8000_0021):"); + table2( + &skin, + &[ + RowGen::tuple("Processor ignores nested data breakpoints", info.has_no_nested_data_bp()), + RowGen::tuple("LFENCE is always dispatch serializing", info.has_lfence_always_serializing()), + RowGen::tuple("SMM paging configuration lock supported", info.has_smm_pg_cfg_lock()), + RowGen::tuple("Null segment selector loads also clear the destination segment register base and limit", info.has_null_select_clears_base()), + RowGen::tuple("Upper Address Ignore is supported", info.has_upper_address_ignore()), + RowGen::tuple("Automatic IBRS", info.has_automatic_ibrs()), + RowGen::tuple("SMM_CTL MSR (C001_0116h) is not supporte", info.has_no_smm_ctl_msr()), + RowGen::tuple("Prefetch control MSR supporte", info.has_prefetch_ctl_msr()), + RowGen::tuple("CPUID disable for non-privileged softwar", info.has_cpuid_user_dis()), + RowGen::tuple("The size of the Microcode patch in 16-byte multiples", info.microcode_patch_size()), + ], + ); + } + + if let Some(info) = cpuid.get_extended_performance_monitoring_and_debug() { + print_title( + &skin, + "Extended Performance Monitoring and Debug (0x8000_0022):", + ); + table2( + &skin, + &[ + RowGen::tuple( + "Performance Monitoring Version 2 supported", + info.has_perf_mon_v2(), + ), + RowGen::tuple("Last Branch Record Stack supported", info.has_lbr_stack()), + RowGen::tuple("LbrAndPmcFreeze", info.has_lbr_and_pmc_freeze()), + RowGen::tuple( + "Number of Core Performance Counters", + info.num_perf_ctr_core(), + ), + RowGen::tuple( + "Number of Last Branch Record Stack entries", + info.num_lbr_stack_size(), + ), + RowGen::tuple( + "Number of Northbridge Performance Monitor Counters", + info.num_perf_ctr_nb(), + ), + ], + ); + } + + if let Some(info) = cpuid.get_multi_key_encrypted_memory_capabilities() { + print_title( + &skin, + "Multi-Key Encrypted Memory Capabilities (0x8000_0023):", + ); + table2( + &skin, + &[ + RowGen::tuple( + "Secure Host Multi-Key Memory (MEM-HMK) Encryption Mode Supported", + info.has_mem_hmk(), + ), + RowGen::tuple("MaxMemHmkEncrKeyID", info.max_mem_hmk_encr_key_id()), + ], + ); + } + + if let Some(info) = cpuid.get_extended_cpu_topology() { + for (level, info) in info.enumerate() { + print_title( + &skin, + &format!("Extended CPU Topology (0x8000_0026/{level}):"), + ); + table2( + &skin, + &[ + RowGen::tuple("Mask Width", info.mask_width()), + RowGen::tuple( + "Efficiency Ranking Available", + info.has_efficiency_ranking_available(), + ), + RowGen::tuple("Heterogeneous Cores", info.has_heterogeneous_cores()), + RowGen::tuple("Asymmetric Topology", info.has_asymmetric_topology()), + RowGen::tuple( + "Number of logical processors at the current hierarchy level", + info.num_logical_processors(), + ), + RowGen::tuple( + "Static efficiency ranking between cores of a specific core type", + info.pwr_efficiency_ranking(), + ), + RowGen::tuple("Native Mode Id", info.native_mode_id()), + RowGen::tuple("Core Type", info.core_type()), + RowGen::tuple("Level Type", info.level_type().to_string()), + RowGen::tuple( + "Extended APIC ID of the logical processo", + info.extended_apic_id(), + ), + ], + ); + } + } } diff --git a/src/dump.rs b/src/dump.rs new file mode 100644 index 0000000..dcfdfe9 --- /dev/null +++ b/src/dump.rs @@ -0,0 +1,281 @@ +use crate::{CpuIdReader, CpuIdResult, CpuIdWriter}; +use std::collections::HashMap; + +#[derive(Clone)] +enum LeafOrSubleaves { + Leaf(CpuIdResult), + Subleaf(HashMap), +} + +// TODO: Clone is necessary because CpuIdReader wants it (for leaves with more complex subleaf +// structures, like the extended topology info leaf) +// +// This implies that there's a full clone of the dump held on for those leaf-specific views, which +// is unfortunate! It's also not yet really clear how to assemble those more complex leaves for +// writer purposes. +#[derive(Clone)] +pub struct CpuIdDump { + leaves: HashMap, +} + +impl CpuIdDump { + // TODO: probably should just take vendor in the initial constructor here + // (that also lets this pick the right leaf/subleaf fallback behavior from the get-go) + pub fn new() -> Self { + Self { + leaves: HashMap::new(), + } + } +} + +pub struct CpuIdDumpIter { + // It's straightforward enough to use `hash_map::Drain` to walk the top-level map but it's more + // annoying for inner collections of subleaves because `Drain` holds a borrow of the + // to-be-drained map. Here, that'd mean the struct is self-referential with `current_subleaf` + // borrowing `dump`. So, just be naive the whole way through (much to the dismay of `impl + // Iterator` below..) + dump: CpuIdDump, + leaf: u32, + current_subleaf: Option>, +} + +impl IntoIterator for CpuIdDump { + type Item = (u32, Option, CpuIdResult); + type IntoIter = CpuIdDumpIter; + + fn into_iter(self) -> Self::IntoIter { + CpuIdDumpIter { + dump: self, + leaf: 0, + current_subleaf: None, + } + } +} + +impl Iterator for CpuIdDumpIter { + type Item = (u32, Option, CpuIdResult); + + fn next(&mut self) -> Option { + loop { + if let Some(subleaves) = self.current_subleaf.as_mut() { + if let Some(subleaf) = subleaves.keys().next().cloned() { + let regs = subleaves.remove(&subleaf).expect("subleaf is present"); + return Some((self.leaf, Some(subleaf), regs)); + } else { + // We've exhauted this subleaf, move on. + self.current_subleaf = None; + } + } + + let Some(first_key) = self.dump.leaves.keys().next() else { + // We've exhausted the whole map! + return None; + }; + + self.leaf = *first_key; + let entry = self + .dump + .leaves + .remove(&self.leaf) + .expect("leaf is present"); + match entry { + LeafOrSubleaves::Leaf(regs) => { + return Some((self.leaf, None, regs)); + } + LeafOrSubleaves::Subleaf(subleaves) => { + self.current_subleaf = Some(subleaves); + } + } + } + } +} + +const DEFAULT_LEAF: CpuIdResult = CpuIdResult { + eax: 0, + ebx: 0, + ecx: 0, + edx: 0, +}; + +impl CpuIdWriter for CpuIdDump { + fn set_leaf(&mut self, leaf: u32, mut bits: Option) { + // Many bits in 8000_0001h EDX, if present, mirror leaf 1h EDX. Maintain that before + // storing the bits. + if let Some(bits) = bits.as_mut() { + const MIRROR_MASK: u32 = 0b0000_0001_1000_0011_1111_0011_1111_1111; + + if leaf == 0x0000_0001 { + // We're updating leaf 1h, so go fix up leaf 8000_0001h (if present) + match self.leaves.get_mut(&0x8000_0001) { + Some(LeafOrSubleaves::Leaf(ext_info)) => { + ext_info.edx &= !MIRROR_MASK; + ext_info.edx |= bits.edx & MIRROR_MASK; + } + Some(_) => { + panic!("extended feature information leaf (8000_0001h) had subleaves?"); + } + None => { + // No leaf 8000_0001h to mirror to (yet?) + } + } + } else if leaf == 0x8000_0001 { + match self.leaves.get(&0x0000_0001) { + Some(LeafOrSubleaves::Leaf(prior_bits)) => { + bits.edx &= !MIRROR_MASK; + bits.edx |= prior_bits.edx & MIRROR_MASK; + } + Some(_) => { + panic!("feature information leaf (01h) had subleaves?"); + } + None => { + // No leaf 1h to shadow (yet?) + } + } + }; + } + + if let Some(bits) = bits { + self.leaves.insert(leaf, LeafOrSubleaves::Leaf(bits)); + } else { + self.leaves.remove(&leaf); + } + + self.update_max_leaves(); + } + fn set_subleaf(&mut self, leaf: u32, subleaf: u32, bits: Option) { + if let Some(bits) = bits { + match self + .leaves + .entry(leaf) + .or_insert(LeafOrSubleaves::Subleaf(HashMap::new())) + { + LeafOrSubleaves::Leaf(_) => { + panic!("adding a subleaf where there's a leaf. no"); + } + LeafOrSubleaves::Subleaf(leaves) => { + leaves.insert(subleaf, bits); + } + } + } else { + self.leaves.get_mut(&leaf).map(|ent| { + if let LeafOrSubleaves::Subleaf(leaves) = ent { + leaves.remove(&subleaf); + } else { + panic!("removing a subleaf when there's a leaf. no"); + } + }); + } + + self.update_max_leaves(); + } +} + +impl CpuIdDump { + fn update_max_leaves(&mut self) { + let mut max_standard = None; + let mut max_hv = None; + let mut max_extended = None; + + for k in self.leaves.keys() { + let k = *k; + if k < 0x40000000 { + max_standard = Some(match max_standard { + None => k, + Some(prev) => core::cmp::max(k, prev), + }); + } else if k < 0x80000000 { + max_hv = Some(match max_hv { + None => k, + Some(prev) => core::cmp::max(k, prev), + }); + } else if k < 0xc0000000 { + max_extended = Some(match max_extended { + None => k, + Some(prev) => core::cmp::max(k, prev), + }); + } + } + + if let Some(eax) = max_standard { + match self + .leaves + .entry(0) + .or_insert(LeafOrSubleaves::Leaf(CpuIdResult::empty())) + { + LeafOrSubleaves::Leaf(leaf) => { + leaf.eax = eax; + } + _ => { + panic!("cannot update leaf 1.EAX: leaf has subleaves?"); + } + } + } + + if let Some(eax) = max_hv { + match self + .leaves + .entry(0x40000000) + .or_insert(LeafOrSubleaves::Leaf(CpuIdResult::empty())) + { + LeafOrSubleaves::Leaf(leaf) => { + leaf.eax = eax; + } + _ => { + panic!("cannot update leaf 0x40000000.EAX: leaf has subleaves?"); + } + } + } + + if let Some(eax) = max_extended { + match self + .leaves + .entry(0x80000000) + .or_insert(LeafOrSubleaves::Leaf(CpuIdResult::empty())) + { + LeafOrSubleaves::Leaf(leaf) => { + leaf.eax = eax; + } + _ => { + panic!("cannot update leaf 0x80000000.EAX: leaf has subleaves?"); + } + } + } + } +} + +impl CpuIdReader for CpuIdDump { + fn cpuid1(&self, leaf: u32) -> CpuIdResult { + match self.leaves.get(&leaf) { + Some(LeafOrSubleaves::Leaf(res)) => *res, + Some(LeafOrSubleaves::Subleaf(subleaves)) => { + *subleaves.get(&0).unwrap_or_else(|| { + // TODO: vendor-specific fallback behavior + &DEFAULT_LEAF + }) + } + None => { + // TODO: more vendor-specific fallback behavior + DEFAULT_LEAF + } + } + } + + fn cpuid2(&self, leaf: u32, subleaf: u32) -> CpuIdResult { + match self.leaves.get(&leaf) { + Some(LeafOrSubleaves::Leaf(_res)) => { + // TODO: vendor-specific fallback behavior + DEFAULT_LEAF + } + Some(LeafOrSubleaves::Subleaf(subleaves)) => { + *subleaves.get(&subleaf).unwrap_or_else(|| { + // TODO: vendor-specific fallback behavior + &DEFAULT_LEAF + }) + } + None => { + // TODO: more vendor-specific fallback behavior + DEFAULT_LEAF + } + } + } +} diff --git a/src/extended.rs b/src/extended.rs index 9c05272..9251a9d 100644 --- a/src/extended.rs +++ b/src/extended.rs @@ -5,7 +5,10 @@ use core::mem::size_of; use core::slice; use core::str; -use crate::{get_bits, CpuIdResult, Vendor}; +use crate::{ + get_bits, set_bits, CpuIdReader, CpuIdResult, Vendor, EAX_EXTENDED_CPU_TOPOLOGY, + EAX_PQOS_EXTENDED_FEATURES, +}; /// Extended Processor and Processor Feature Identifiers (LEAF=0x8000_0001) /// @@ -13,13 +16,23 @@ use crate::{get_bits, CpuIdResult, Vendor}; /// ✅ AMD 🟡 Intel pub struct ExtendedProcessorFeatureIdentifiers { vendor: Vendor, - eax: u32, - ebx: u32, - ecx: ExtendedFunctionInfoEcx, - edx: ExtendedFunctionInfoEdx, + pub(crate) eax: u32, + pub(crate) ebx: u32, + pub(crate) ecx: ExtendedFunctionInfoEcx, + pub(crate) edx: ExtendedFunctionInfoEdx, } impl ExtendedProcessorFeatureIdentifiers { + pub fn empty(vendor: Vendor) -> Self { + Self { + vendor, + eax: 0, + ebx: 0, + ecx: ExtendedFunctionInfoEcx::from_bits_truncate(0), + edx: ExtendedFunctionInfoEdx::from_bits_truncate(0), + } + } + pub(crate) fn new(vendor: Vendor, data: CpuIdResult) -> Self { Self { vendor, @@ -46,6 +59,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.eax } + pub fn set_extended_signature(&mut self, bits: u32) -> &mut Self { + self.eax = bits; + self + } + /// Returns package type on AMD. /// /// Package type. If `(Family[7:0] >= 10h)`, this field is valid. If @@ -57,6 +75,11 @@ impl ExtendedProcessorFeatureIdentifiers { get_bits(self.ebx, 28, 31) } + pub fn set_pkg_type(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ebx, bits, 28, 31); + self + } + /// Returns brand ID on AMD. /// /// This field, in conjunction with CPUID `LEAF=0x0000_0001_EBX[8BitBrandId]`, and used @@ -68,6 +91,11 @@ impl ExtendedProcessorFeatureIdentifiers { get_bits(self.ebx, 0, 15) } + pub fn set_brand_id(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ebx, bits, 0, 15); + self + } + /// Is LAHF/SAHF available in 64-bit mode? /// /// # Platforms @@ -76,6 +104,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.ecx.contains(ExtendedFunctionInfoEcx::LAHF_SAHF) } + pub fn set_lahf_sahf(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::LAHF_SAHF, bit); + self + } + /// Check support legacy cmp. /// /// # Platform @@ -84,6 +117,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::CMP_LEGACY) } + pub fn set_cmp_legacy(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::CMP_LEGACY, bit); + self + } + /// Secure virtual machine supported. /// /// # Platform @@ -92,6 +130,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::SVM) } + pub fn set_svm(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::SVM, bit); + self + } + /// Extended APIC space. /// /// This bit indicates the presence of extended APIC register space starting at offset @@ -103,6 +146,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::EXT_APIC_SPACE) } + pub fn set_ext_apic_space(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::EXT_APIC_SPACE, bit); + self + } + /// LOCK MOV CR0 means MOV CR8. See “MOV(CRn)” in APM3. /// /// # Platform @@ -111,6 +159,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::ALTMOVCR8) } + pub fn set_alt_mov_cr8(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::ALTMOVCR8, bit); + self + } + /// Is LZCNT available? /// /// # AMD @@ -123,6 +176,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.ecx.contains(ExtendedFunctionInfoEcx::LZCNT) } + pub fn set_lzcnt(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::LZCNT, bit); + self + } + /// XTRQ, INSERTQ, MOVNTSS, and MOVNTSD instruction support. /// /// See “EXTRQ”, “INSERTQ”,“MOVNTSS”, and “MOVNTSD” in APM4. @@ -133,6 +191,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::SSE4A) } + pub fn set_sse4a(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::SSE4A, bit); + self + } + /// Misaligned SSE mode. See “Misaligned Access Support Added for SSE Instructions” in /// APM1. /// @@ -142,6 +205,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::MISALIGNSSE) } + pub fn set_misaligned_sse_mode(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::MISALIGNSSE, bit); + self + } + /// Is PREFETCHW available? /// /// # AMD @@ -153,6 +221,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.ecx.contains(ExtendedFunctionInfoEcx::PREFETCHW) } + pub fn set_prefetchw(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::PREFETCHW, bit); + self + } + /// Indicates OS-visible workaround support /// /// # Platform @@ -161,6 +234,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::OSVW) } + pub fn set_osvw(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::OSVW, bit); + self + } + /// Instruction based sampling. /// /// # Platform @@ -169,6 +247,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::IBS) } + pub fn set_ibs(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::IBS, bit); + self + } + /// Extended operation support. /// /// # Platform @@ -177,6 +260,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::XOP) } + pub fn set_xop(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::XOP, bit); + self + } + /// SKINIT and STGI are supported. /// /// Indicates support for SKINIT and STGI, independent of the value of @@ -188,6 +276,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::SKINIT) } + pub fn set_skinit(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::SKINIT, bit); + self + } + /// Watchdog timer support. /// /// Indicates support for MSRC001_0074. @@ -198,6 +291,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::WDT) } + pub fn set_wdt(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::WDT, bit); + self + } + /// Lightweight profiling support /// /// # Platform @@ -206,6 +304,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::LWP) } + pub fn set_lwp(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::LWP, bit); + self + } + /// Four-operand FMA instruction support. /// /// # Platform @@ -214,6 +317,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::FMA4) } + pub fn set_fma4(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::FMA4, bit); + self + } + /// Trailing bit manipulation instruction support. /// /// # Platform @@ -222,6 +330,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::TBM) } + pub fn set_tbm(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::TBM, bit); + self + } + /// Topology extensions support. /// /// Indicates support for CPUID `Fn8000_001D_EAX_x[N:0]-CPUID Fn8000_001E_EDX`. @@ -232,6 +345,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::TOPEXT) } + pub fn set_topology_extensions(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::TOPEXT, bit); + self + } + /// Processor performance counter extensions support. /// /// Indicates support for `MSRC001_020[A,8,6,4,2,0]` and `MSRC001_020[B,9,7,5,3,1]`. @@ -242,6 +360,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::PERFCTREXT) } + pub fn set_perf_cntr_extensions(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::PERFCTREXT, bit); + self + } + /// NB performance counter extensions support. /// /// Indicates support for `MSRC001_024[6,4,2,0]` and `MSRC001_024[7,5,3,1]`. @@ -252,6 +375,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::PERFCTREXTNB) } + pub fn set_nb_perf_cntr_extensions(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::PERFCTREXTNB, bit); + self + } + /// Data access breakpoint extension. /// /// Indicates support for `MSRC001_1027` and `MSRC001_101[B:9]`. @@ -262,6 +390,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::DATABRKPEXT) } + pub fn set_data_access_bkpt_extension(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::DATABRKPEXT, bit); + self + } + /// Performance time-stamp counter. /// /// Indicates support for `MSRC001_0280` `[Performance Time Stamp Counter]`. @@ -272,6 +405,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::PERFTSC) } + pub fn set_perf_tsc(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::PERFTSC, bit); + self + } + /// Support for L3 performance counter extension. /// /// # Platform @@ -280,6 +418,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::PERFCTREXTLLC) } + pub fn set_perf_cntr_llc_extensions(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::PERFCTREXTLLC, bit); + self + } + /// Support for MWAITX and MONITORX instructions. /// /// # Platform @@ -288,6 +431,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::MONITORX) } + pub fn set_monitorx_mwaitx(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::MONITORX, bit); + self + } + /// Breakpoint Addressing masking extended to bit 31. /// /// # Platform @@ -296,6 +444,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.ecx.contains(ExtendedFunctionInfoEcx::ADDRMASKEXT) } + pub fn set_addr_mask_extension(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFunctionInfoEcx::ADDRMASKEXT, bit); + self + } + /// Are fast system calls available. /// /// # Platforms @@ -304,6 +457,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.edx.contains(ExtendedFunctionInfoEdx::SYSCALL_SYSRET) } + pub fn set_syscall_sysret(&mut self, bit: bool) -> &mut Self { + self.edx.set(ExtendedFunctionInfoEdx::SYSCALL_SYSRET, bit); + self + } + /// Is there support for execute disable bit. /// /// # Platforms @@ -312,6 +470,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.edx.contains(ExtendedFunctionInfoEdx::EXECUTE_DISABLE) } + pub fn set_execute_disable(&mut self, bit: bool) -> &mut Self { + self.edx.set(ExtendedFunctionInfoEdx::EXECUTE_DISABLE, bit); + self + } + /// AMD extensions to MMX instructions. /// /// # Platform @@ -320,6 +483,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.edx.contains(ExtendedFunctionInfoEdx::MMXEXT) } + pub fn set_mmx_extensions(&mut self, bit: bool) -> &mut Self { + self.edx.set(ExtendedFunctionInfoEdx::MMXEXT, bit); + self + } + /// FXSAVE and FXRSTOR instruction optimizations. /// /// # Platform @@ -328,6 +496,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.edx.contains(ExtendedFunctionInfoEdx::FFXSR) } + pub fn set_fast_fxsave_fxstor(&mut self, bit: bool) -> &mut Self { + self.edx.set(ExtendedFunctionInfoEdx::FFXSR, bit); + self + } + /// Is there support for 1GiB pages. /// /// # Platforms @@ -336,6 +509,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.edx.contains(ExtendedFunctionInfoEdx::GIB_PAGES) } + pub fn set_1gib_pages(&mut self, bit: bool) -> &mut Self { + self.edx.set(ExtendedFunctionInfoEdx::GIB_PAGES, bit); + self + } + /// Check support for rdtscp instruction. /// /// # Platforms @@ -344,6 +522,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.edx.contains(ExtendedFunctionInfoEdx::RDTSCP) } + pub fn set_rdtscp(&mut self, bit: bool) -> &mut Self { + self.edx.set(ExtendedFunctionInfoEdx::RDTSCP, bit); + self + } + /// Check support for 64-bit mode. /// /// # Platforms @@ -352,6 +535,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.edx.contains(ExtendedFunctionInfoEdx::I64BIT_MODE) } + pub fn set_64bit_mode(&mut self, bit: bool) -> &mut Self { + self.edx.set(ExtendedFunctionInfoEdx::I64BIT_MODE, bit); + self + } + /// 3DNow AMD extensions. /// /// # Platform @@ -360,6 +548,11 @@ impl ExtendedProcessorFeatureIdentifiers { self.vendor == Vendor::Amd && self.edx.contains(ExtendedFunctionInfoEdx::THREEDNOWEXT) } + pub fn set_amd_3dnow_extensions(&mut self, bit: bool) -> &mut Self { + self.edx.set(ExtendedFunctionInfoEdx::THREEDNOWEXT, bit); + self + } + /// 3DNow extensions. /// /// # Platform @@ -367,6 +560,11 @@ impl ExtendedProcessorFeatureIdentifiers { pub fn has_3dnow(&self) -> bool { self.vendor == Vendor::Amd && self.edx.contains(ExtendedFunctionInfoEdx::THREEDNOW) } + + pub fn set_3dnow(&mut self, bit: bool) -> &mut Self { + self.edx.set(ExtendedFunctionInfoEdx::THREEDNOW, bit); + self + } } impl Debug for ExtendedProcessorFeatureIdentifiers { @@ -387,7 +585,7 @@ impl Debug for ExtendedProcessorFeatureIdentifiers { bitflags! { #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] - struct ExtendedFunctionInfoEcx: u32 { + pub(crate) struct ExtendedFunctionInfoEcx: u32 { const LAHF_SAHF = 1 << 0; const CMP_LEGACY = 1 << 1; const SVM = 1 << 2; @@ -419,11 +617,11 @@ bitflags! { bitflags! { #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] - struct ExtendedFunctionInfoEdx: u32 { + pub(crate) struct ExtendedFunctionInfoEdx: u32 { const SYSCALL_SYSRET = 1 << 11; const EXECUTE_DISABLE = 1 << 20; const MMXEXT = 1 << 22; - const FFXSR = 1 << 24; + const FFXSR = 1 << 25; const GIB_PAGES = 1 << 26; const RDTSCP = 1 << 27; const I64BIT_MODE = 1 << 29; @@ -483,13 +681,22 @@ impl Debug for ProcessorBrandString { /// ✅ AMD ❌ Intel (reserved=0) #[derive(PartialEq, Eq, Debug)] pub struct L1CacheTlbInfo { - eax: u32, - ebx: u32, - ecx: u32, - edx: u32, + pub(crate) eax: u32, + pub(crate) ebx: u32, + pub(crate) ecx: u32, + pub(crate) edx: u32, } impl L1CacheTlbInfo { + pub fn empty() -> Self { + Self { + eax: 0, + ebx: 0, + ecx: 0, + edx: 0, + } + } + pub(crate) fn new(data: CpuIdResult) -> Self { Self { eax: data.eax, @@ -505,6 +712,11 @@ impl L1CacheTlbInfo { Associativity::for_l1(assoc_bits) } + pub fn set_dtlb_2m_4m_associativity(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.eax, bits, 24, 31); + self + } + /// Data TLB number of entries for 2-MB and 4-MB pages. /// /// The value returned is for the number of entries available for the 2-MB page size; @@ -514,12 +726,22 @@ impl L1CacheTlbInfo { get_bits(self.eax, 16, 23) as u8 } + pub fn set_dtlb_2m_4m_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.eax, bits, 16, 23); + self + } + /// Instruction TLB associativity for 2-MB and 4-MB pages. pub fn itlb_2m_4m_associativity(&self) -> Associativity { let assoc_bits = get_bits(self.eax, 8, 15) as u8; Associativity::for_l1(assoc_bits) } + pub fn set_itlb_2m_4m_associativity(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.eax, bits, 8, 15); + self + } + /// Instruction TLB number of entries for 2-MB and 4-MB pages. /// /// The value returned is for the number of entries available for the 2-MB page size; @@ -529,69 +751,134 @@ impl L1CacheTlbInfo { get_bits(self.eax, 0, 7) as u8 } + pub fn set_itlb_2m_4m_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.eax, bits, 0, 7); + self + } + /// Data TLB associativity for 4K pages. pub fn dtlb_4k_associativity(&self) -> Associativity { let assoc_bits = get_bits(self.ebx, 24, 31) as u8; Associativity::for_l1(assoc_bits) } + pub fn set_dtlb_4k_associativity(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ebx, bits, 24, 31); + self + } + /// Data TLB number of entries for 4K pages. pub fn dtlb_4k_size(&self) -> u8 { get_bits(self.ebx, 16, 23) as u8 } + pub fn set_dtlb_4k_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ebx, bits, 16, 23); + self + } + /// Instruction TLB associativity for 4K pages. pub fn itlb_4k_associativity(&self) -> Associativity { let assoc_bits = get_bits(self.ebx, 8, 15) as u8; Associativity::for_l1(assoc_bits) } + pub fn set_itlb_4k_associativity(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ebx, bits, 8, 15); + self + } + /// Instruction TLB number of entries for 4K pages. pub fn itlb_4k_size(&self) -> u8 { get_bits(self.ebx, 0, 7) as u8 } + pub fn set_itlb_4k_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ebx, bits, 0, 7); + self + } + /// L1 data cache size in KB pub fn dcache_size(&self) -> u8 { get_bits(self.ecx, 24, 31) as u8 } + pub fn set_dcache_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ecx, bits, 24, 31); + self + } + /// L1 data cache associativity. pub fn dcache_associativity(&self) -> Associativity { let assoc_bits = get_bits(self.ecx, 16, 23) as u8; Associativity::for_l1(assoc_bits) } + pub fn set_dcache_associativity(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ecx, bits, 16, 23); + self + } + /// L1 data cache lines per tag. pub fn dcache_lines_per_tag(&self) -> u8 { get_bits(self.ecx, 8, 15) as u8 } + pub fn set_dcache_lines_per_tag(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ecx, bits, 8, 15); + self + } + /// L1 data cache line size in bytes. pub fn dcache_line_size(&self) -> u8 { get_bits(self.ecx, 0, 7) as u8 } + pub fn set_dcache_line_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ecx, bits, 0, 7); + self + } + /// L1 instruction cache size in KB pub fn icache_size(&self) -> u8 { get_bits(self.edx, 24, 31) as u8 } + pub fn set_icache_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.edx, bits, 24, 31); + self + } + /// L1 instruction cache associativity. pub fn icache_associativity(&self) -> Associativity { let assoc_bits = get_bits(self.edx, 16, 23) as u8; Associativity::for_l1(assoc_bits) } + pub fn set_icache_associativity(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.edx, bits, 16, 23); + self + } + /// L1 instruction cache lines per tag. pub fn icache_lines_per_tag(&self) -> u8 { get_bits(self.edx, 8, 15) as u8 } + pub fn set_icache_lines_per_tag(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.edx, bits, 8, 15); + self + } + /// L1 instruction cache line size in bytes. pub fn icache_line_size(&self) -> u8 { get_bits(self.edx, 0, 7) as u8 } + + pub fn set_icache_line_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.edx, bits, 0, 7); + self + } } /// L2/L3 Cache and TLB Information (LEAF=0x8000_0006). @@ -600,13 +887,22 @@ impl L1CacheTlbInfo { /// ✅ AMD 🟡 Intel #[derive(PartialEq, Eq, Debug)] pub struct L2And3CacheTlbInfo { - eax: u32, - ebx: u32, - ecx: u32, - edx: u32, + pub(crate) eax: u32, + pub(crate) ebx: u32, + pub(crate) ecx: u32, + pub(crate) edx: u32, } impl L2And3CacheTlbInfo { + pub fn empty() -> Self { + Self { + eax: 0, + ebx: 0, + ecx: 0, + edx: 0, + } + } + pub(crate) fn new(data: CpuIdResult) -> Self { Self { eax: data.eax, @@ -625,6 +921,11 @@ impl L2And3CacheTlbInfo { Associativity::for_l2(assoc_bits) } + pub fn set_dtlb_2m_4m_associativity(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.eax, bits, 28, 31); + self + } + /// L2 Data TLB number of entries for 2-MB and 4-MB pages. /// /// The value returned is for the number of entries available for the 2-MB page size; @@ -637,6 +938,11 @@ impl L2And3CacheTlbInfo { get_bits(self.eax, 16, 27) as u16 } + pub fn set_dtlb_2m_4m_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.eax, bits, 16, 27); + self + } + /// L2 Instruction TLB associativity for 2-MB and 4-MB pages. /// /// # Availability @@ -646,6 +952,11 @@ impl L2And3CacheTlbInfo { Associativity::for_l2(assoc_bits) } + pub fn set_itlb_2m_4m_associativity(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.eax, bits, 12, 15); + self + } + /// L2 Instruction TLB number of entries for 2-MB and 4-MB pages. /// /// The value returned is for the number of entries available for the 2-MB page size; @@ -658,6 +969,11 @@ impl L2And3CacheTlbInfo { get_bits(self.eax, 0, 11) as u16 } + pub fn set_itlb_2m_4m_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.eax, bits, 0, 11); + self + } + /// L2 Data TLB associativity for 4K pages. /// /// # Availability @@ -667,6 +983,11 @@ impl L2And3CacheTlbInfo { Associativity::for_l2(assoc_bits) } + pub fn set_dtlb_4k_associativity(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ebx, bits, 28, 31); + self + } + /// L2 Data TLB number of entries for 4K pages. /// /// # Availability @@ -675,6 +996,11 @@ impl L2And3CacheTlbInfo { get_bits(self.ebx, 16, 27) as u16 } + pub fn set_dtlb_4k_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ebx, bits, 16, 27); + self + } + /// L2 Instruction TLB associativity for 4K pages. /// /// # Availability @@ -684,6 +1010,11 @@ impl L2And3CacheTlbInfo { Associativity::for_l2(assoc_bits) } + pub fn set_itlb_4k_associativity(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ebx, bits, 12, 15); + self + } + /// L2 Instruction TLB number of entries for 4K pages. /// /// # Availability @@ -692,6 +1023,11 @@ impl L2And3CacheTlbInfo { get_bits(self.ebx, 0, 11) as u16 } + pub fn set_itlb_4k_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ebx, bits, 0, 11); + self + } + /// L2 Cache Line size in bytes /// /// # Platforms @@ -700,6 +1036,11 @@ impl L2And3CacheTlbInfo { get_bits(self.ecx, 0, 7) as u8 } + pub fn set_l2cache_line_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ecx, bits, 0, 7); + self + } + /// L2 cache lines per tag. /// /// # Availability @@ -708,6 +1049,11 @@ impl L2And3CacheTlbInfo { get_bits(self.ecx, 8, 11) as u8 } + pub fn set_l2cache_lines_per_tag(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ecx, bits, 8, 11); + self + } + /// L2 Associativity field /// /// # Availability @@ -717,6 +1063,11 @@ impl L2And3CacheTlbInfo { Associativity::for_l2(assoc_bits) } + pub fn set_l2cache_associativity(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ecx, bits, 12, 15); + self + } + /// Cache size in KB. /// /// # Platforms @@ -725,6 +1076,11 @@ impl L2And3CacheTlbInfo { get_bits(self.ecx, 16, 31) as u16 } + pub fn set_l2cache_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ecx, bits, 16, 31); + self + } + /// L2 Cache Line size in bytes /// /// # Platforms @@ -733,6 +1089,11 @@ impl L2And3CacheTlbInfo { get_bits(self.edx, 0, 7) as u8 } + pub fn set_l3cache_line_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.edx, bits, 0, 7); + self + } + /// L2 cache lines per tag. /// /// # Availability @@ -741,6 +1102,11 @@ impl L2And3CacheTlbInfo { get_bits(self.edx, 8, 11) as u8 } + pub fn set_l3cache_lines_per_tag(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.edx, bits, 8, 11); + self + } + /// L2 Associativity field /// /// # Availability @@ -750,6 +1116,11 @@ impl L2And3CacheTlbInfo { Associativity::for_l3(assoc_bits) } + pub fn set_l3cache_associativity(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.edx, bits, 12, 15); + self + } + /// Specifies the L3 cache size range /// /// `(L3Size[31:18] * 512KB) <= L3 cache size < ((L3Size[31:18]+1) * 512KB)`. @@ -759,6 +1130,11 @@ impl L2And3CacheTlbInfo { pub fn l3cache_size(&self) -> u16 { get_bits(self.edx, 18, 31) as u16 } + + pub fn set_l3cache_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.edx, bits, 18, 31); + self + } } /// Info about cache Associativity. @@ -832,13 +1208,22 @@ impl Associativity { #[derive(Debug, PartialEq, Eq)] pub struct ApmInfo { /// Reserved on AMD and Intel. - _eax: u32, - ebx: RasCapabilities, - ecx: u32, - edx: ApmInfoEdx, + pub(crate) _eax: u32, + pub(crate) ebx: RasCapabilities, + pub(crate) ecx: u32, + pub(crate) edx: ApmInfoEdx, } impl ApmInfo { + pub fn empty() -> Self { + Self { + _eax: 0, + ebx: RasCapabilities::from_bits_truncate(0), + ecx: 0, + edx: ApmInfoEdx::from_bits_truncate(0), + } + } + pub(crate) fn new(data: CpuIdResult) -> Self { Self { _eax: data.eax, @@ -860,6 +1245,11 @@ impl ApmInfo { self.ebx.contains(RasCapabilities::MCAOVFLRECOV) } + pub fn set_mca_overflow_recovery(&mut self, bit: bool) -> &mut Self { + self.ebx.set(RasCapabilities::MCAOVFLRECOV, bit); + self + } + /// Has Software uncorrectable error containment and recovery capability? /// /// The processor supports software containment of uncorrectable errors @@ -872,6 +1262,11 @@ impl ApmInfo { self.ebx.contains(RasCapabilities::SUCCOR) } + pub fn set_succor(&mut self, bit: bool) -> &mut Self { + self.ebx.set(RasCapabilities::SUCCOR, bit); + self + } + /// Has Hardware assert supported? /// /// Indicates support for `MSRC001_10[DF:C0]`. @@ -882,6 +1277,11 @@ impl ApmInfo { self.ebx.contains(RasCapabilities::HWA) } + pub fn set_hwa(&mut self, bit: bool) -> &mut Self { + self.ebx.set(RasCapabilities::HWA, bit); + self + } + /// Specifies the ratio of the compute unit power accumulator sample period /// to the TSC counter period. /// @@ -893,6 +1293,11 @@ impl ApmInfo { self.ecx } + pub fn set_pwr_sample_time_ratio(&mut self, bits: u32) -> &mut Self { + self.ecx = bits; + self + } + /// Is Temperature Sensor available? /// /// # Platforms @@ -901,6 +1306,11 @@ impl ApmInfo { self.edx.contains(ApmInfoEdx::TS) } + pub fn set_ts(&mut self, bit: bool) -> &mut Self { + self.edx.set(ApmInfoEdx::TS, bit); + self + } + /// Frequency ID control. /// /// # Note @@ -912,6 +1322,11 @@ impl ApmInfo { self.edx.contains(ApmInfoEdx::FID) } + pub fn set_freq_id_ctrl(&mut self, bit: bool) -> &mut Self { + self.edx.set(ApmInfoEdx::FID, bit); + self + } + /// Voltage ID control. /// /// # Note @@ -923,6 +1338,11 @@ impl ApmInfo { self.edx.contains(ApmInfoEdx::VID) } + pub fn set_volt_id_ctrl(&mut self, bit: bool) -> &mut Self { + self.edx.set(ApmInfoEdx::VID, bit); + self + } + /// Has THERMTRIP? /// /// # Platforms @@ -931,6 +1351,11 @@ impl ApmInfo { self.edx.contains(ApmInfoEdx::TTP) } + pub fn set_thermtrip(&mut self, bit: bool) -> &mut Self { + self.edx.set(ApmInfoEdx::TTP, bit); + self + } + /// Hardware thermal control (HTC)? /// /// # Platforms @@ -939,6 +1364,11 @@ impl ApmInfo { self.edx.contains(ApmInfoEdx::TM) } + pub fn set_tm(&mut self, bit: bool) -> &mut Self { + self.edx.set(ApmInfoEdx::TM, bit); + self + } + /// Has 100 MHz multiplier Control? /// /// # Platforms @@ -947,6 +1377,11 @@ impl ApmInfo { self.edx.contains(ApmInfoEdx::MHZSTEPS100) } + pub fn set_100mhz_steps(&mut self, bit: bool) -> &mut Self { + self.edx.set(ApmInfoEdx::MHZSTEPS100, bit); + self + } + /// Has Hardware P-state control? /// /// MSRC001_0061 [P-state Current Limit], MSRC001_0062 [P-state Control] and @@ -958,6 +1393,11 @@ impl ApmInfo { self.edx.contains(ApmInfoEdx::HWPSTATE) } + pub fn set_hw_pstate(&mut self, bit: bool) -> &mut Self { + self.edx.set(ApmInfoEdx::HWPSTATE, bit); + self + } + /// Is Invariant TSC available? /// /// # Platforms @@ -966,6 +1406,11 @@ impl ApmInfo { self.edx.contains(ApmInfoEdx::INVTSC) } + pub fn set_invariant_tsc(&mut self, bit: bool) -> &mut Self { + self.edx.set(ApmInfoEdx::INVTSC, bit); + self + } + /// Has Core performance boost? /// /// # Platforms @@ -974,6 +1419,11 @@ impl ApmInfo { self.edx.contains(ApmInfoEdx::CPB) } + pub fn set_cpb(&mut self, bit: bool) -> &mut Self { + self.edx.set(ApmInfoEdx::CPB, bit); + self + } + /// Has Read-only effective frequency interface? /// /// Indicates presence of MSRC000_00E7 [Read-Only Max Performance Frequency @@ -986,6 +1436,11 @@ impl ApmInfo { self.edx.contains(ApmInfoEdx::EFFFREQRO) } + pub fn set_ro_effective_freq_iface(&mut self, bit: bool) -> &mut Self { + self.edx.set(ApmInfoEdx::EFFFREQRO, bit); + self + } + /// Indicates support for processor feedback interface. /// /// # Note @@ -997,6 +1452,11 @@ impl ApmInfo { self.edx.contains(ApmInfoEdx::PROCFEEDBACKIF) } + pub fn set_feedback_iface(&mut self, bit: bool) -> &mut Self { + self.edx.set(ApmInfoEdx::PROCFEEDBACKIF, bit); + self + } + /// Has Processor power reporting interface? /// /// # Platforms @@ -1004,12 +1464,17 @@ impl ApmInfo { pub fn has_power_reporting_iface(&self) -> bool { self.edx.contains(ApmInfoEdx::PROCPWRREPORT) } + + pub fn set_power_reporting_iface(&mut self, bit: bool) -> &mut Self { + self.edx.set(ApmInfoEdx::PROCPWRREPORT, bit); + self + } } bitflags! { #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] - struct ApmInfoEdx: u32 { + pub(crate) struct ApmInfoEdx: u32 { const TS = 1 << 0; const FID = 1 << 1; const VID = 1 << 2; @@ -1028,7 +1493,7 @@ bitflags! { bitflags! { #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] - struct RasCapabilities: u32 { + pub(crate) struct RasCapabilities: u32 { const MCAOVFLRECOV = 1 << 0; const SUCCOR = 1 << 1; const HWA = 1 << 2; @@ -1046,13 +1511,22 @@ bitflags! { /// ✅ AMD 🟡 Intel #[derive(PartialEq, Eq)] pub struct ProcessorCapacityAndFeatureInfo { - eax: u32, - ebx: ProcessorCapacityAndFeatureEbx, - ecx: u32, - edx: u32, + pub(crate) eax: u32, + pub(crate) ebx: ProcessorCapacityAndFeatureEbx, + pub(crate) ecx: u32, + pub(crate) edx: u32, } impl ProcessorCapacityAndFeatureInfo { + pub fn empty() -> Self { + Self { + eax: 0, + ebx: ProcessorCapacityAndFeatureEbx::from_bits_truncate(0), + ecx: 0, + edx: 0, + } + } + pub(crate) fn new(data: CpuIdResult) -> Self { Self { eax: data.eax, @@ -1070,6 +1544,11 @@ impl ProcessorCapacityAndFeatureInfo { get_bits(self.eax, 0, 7) as u8 } + pub fn set_physical_address_bits(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.eax, bits, 0, 7); + self + } + /// Linear Address Bits /// /// # Platforms @@ -1078,6 +1557,11 @@ impl ProcessorCapacityAndFeatureInfo { get_bits(self.eax, 8, 15) as u8 } + pub fn set_linear_address_bits(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.eax, bits, 8, 15); + self + } + /// Guest Physical Address Bits /// /// This number applies only to guests using nested paging. When this field @@ -1090,6 +1574,11 @@ impl ProcessorCapacityAndFeatureInfo { get_bits(self.eax, 16, 23) as u8 } + pub fn set_guest_physical_address_bits(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.eax, bits, 16, 23); + self + } + /// CLZERO instruction supported if set. /// /// # Platforms @@ -1098,6 +1587,11 @@ impl ProcessorCapacityAndFeatureInfo { self.ebx.contains(ProcessorCapacityAndFeatureEbx::CLZERO) } + pub fn set_cl_zero(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ProcessorCapacityAndFeatureEbx::CLZERO, bit); + self + } + /// Instruction Retired Counter MSR available if set. /// /// # Platforms @@ -1107,6 +1601,12 @@ impl ProcessorCapacityAndFeatureInfo { .contains(ProcessorCapacityAndFeatureEbx::INST_RETCNT_MSR) } + pub fn set_inst_ret_cntr_msr(&mut self, bit: bool) -> &mut Self { + self.ebx + .set(ProcessorCapacityAndFeatureEbx::INST_RETCNT_MSR, bit); + self + } + /// FP Error Pointers Restored by XRSTOR if set. /// /// # Platforms @@ -1116,6 +1616,12 @@ impl ProcessorCapacityAndFeatureInfo { .contains(ProcessorCapacityAndFeatureEbx::RSTR_FP_ERR_PTRS) } + pub fn set_restore_fp_error_ptrs(&mut self, bit: bool) -> &mut Self { + self.ebx + .set(ProcessorCapacityAndFeatureEbx::RSTR_FP_ERR_PTRS, bit); + self + } + /// INVLPGB and TLBSYNC instruction supported if set. /// /// # Platforms @@ -1124,6 +1630,11 @@ impl ProcessorCapacityAndFeatureInfo { self.ebx.contains(ProcessorCapacityAndFeatureEbx::INVLPGB) } + pub fn set_invlpgb(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ProcessorCapacityAndFeatureEbx::INVLPGB, bit); + self + } + /// RDPRU instruction supported if set. /// /// # Platforms @@ -1132,6 +1643,11 @@ impl ProcessorCapacityAndFeatureInfo { self.ebx.contains(ProcessorCapacityAndFeatureEbx::RDPRU) } + pub fn set_rdpru(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ProcessorCapacityAndFeatureEbx::RDPRU, bit); + self + } + /// MCOMMIT instruction supported if set. /// /// # Platforms @@ -1140,6 +1656,11 @@ impl ProcessorCapacityAndFeatureInfo { self.ebx.contains(ProcessorCapacityAndFeatureEbx::MCOMMIT) } + pub fn set_mcommit(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ProcessorCapacityAndFeatureEbx::MCOMMIT, bit); + self + } + /// WBNOINVD instruction supported if set. /// /// # Platforms @@ -1148,6 +1669,11 @@ impl ProcessorCapacityAndFeatureInfo { self.ebx.contains(ProcessorCapacityAndFeatureEbx::WBNOINVD) } + pub fn set_wbnoinvd(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ProcessorCapacityAndFeatureEbx::WBNOINVD, bit); + self + } + /// WBINVD/WBNOINVD are interruptible if set. /// /// # Platforms @@ -1157,6 +1683,12 @@ impl ProcessorCapacityAndFeatureInfo { .contains(ProcessorCapacityAndFeatureEbx::INT_WBINVD) } + pub fn set_int_wbinvd(&mut self, bit: bool) -> &mut Self { + self.ebx + .set(ProcessorCapacityAndFeatureEbx::INT_WBINVD, bit); + self + } + /// EFER.LMSLE is unsupported if set. /// /// # Platforms @@ -1166,6 +1698,12 @@ impl ProcessorCapacityAndFeatureInfo { .contains(ProcessorCapacityAndFeatureEbx::EFER_LMSLE_UNSUPP) } + pub fn set_unsupported_efer_lmsle(&mut self, bit: bool) -> &mut Self { + self.ebx + .set(ProcessorCapacityAndFeatureEbx::EFER_LMSLE_UNSUPP, bit); + self + } + /// INVLPGB support for invalidating guest nested translations if set. /// /// # Platforms @@ -1175,6 +1713,12 @@ impl ProcessorCapacityAndFeatureInfo { .contains(ProcessorCapacityAndFeatureEbx::INVLPGB_NESTED) } + pub fn set_invlpgb_nested(&mut self, bit: bool) -> &mut Self { + self.ebx + .set(ProcessorCapacityAndFeatureEbx::INVLPGB_NESTED, bit); + self + } + /// Performance time-stamp counter size (in bits). /// /// Indicates the size of `MSRC001_0280[PTSC]`. @@ -1192,6 +1736,11 @@ impl ProcessorCapacityAndFeatureInfo { } } + pub fn set_perf_tsc_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ecx, bits, 16, 17); + self + } + /// APIC ID size. /// /// A value of zero indicates that legacy methods must be used to determine @@ -1204,6 +1753,11 @@ impl ProcessorCapacityAndFeatureInfo { get_bits(self.ecx, 12, 15) as u8 } + pub fn set_apic_id_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ecx, bits, 12, 15); + self + } + /// The size of the `apic_id_size` field determines the maximum number of /// logical processors (MNLP) that the package could theoretically support, /// and not the actual number of logical processors that are implemented or @@ -1225,6 +1779,14 @@ impl ProcessorCapacityAndFeatureInfo { get_bits(self.ecx, 0, 7) as usize + 1 } + /// Set the number of physical threads in the processor. + /// + /// The value provided here is one more than will be recorded in the CPUID leaf. + pub fn set_num_phys_threads(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ecx, bits - 1, 0, 7); + self + } + /// Maximum page count for INVLPGB instruction. /// /// # Platforms @@ -1233,6 +1795,11 @@ impl ProcessorCapacityAndFeatureInfo { get_bits(self.edx, 0, 15) as u16 } + pub fn set_invlpgb_max_pages(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.edx, bits, 0, 15); + self + } + /// The maximum ECX value recognized by RDPRU. /// /// # Platforms @@ -1240,6 +1807,11 @@ impl ProcessorCapacityAndFeatureInfo { pub fn max_rdpru_id(&self) -> u16 { get_bits(self.edx, 16, 31) as u16 } + + pub fn set_max_rdpru_id(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.edx, bits, 16, 31); + self + } } impl Debug for ProcessorCapacityAndFeatureInfo { @@ -1283,7 +1855,7 @@ impl Debug for ProcessorCapacityAndFeatureInfo { bitflags! { #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] - struct ProcessorCapacityAndFeatureEbx: u32 { + pub(crate) struct ProcessorCapacityAndFeatureEbx: u32 { const CLZERO = 1 << 0; const INST_RETCNT_MSR = 1 << 1; const RSTR_FP_ERR_PTRS = 1 << 2; @@ -1307,14 +1879,23 @@ bitflags! { /// ✅ AMD ❌ Intel #[derive(PartialEq, Eq, Debug)] pub struct SvmFeatures { - eax: u32, - ebx: u32, + pub(crate) eax: u32, + pub(crate) ebx: u32, /// Reserved - _ecx: u32, - edx: SvmFeaturesEdx, + pub(crate) _ecx: u32, + pub(crate) edx: SvmFeaturesEdx, } impl SvmFeatures { + pub fn empty() -> Self { + Self { + eax: 0, + ebx: 0, + _ecx: 0, + edx: SvmFeaturesEdx::from_bits_truncate(0), + } + } + pub(crate) fn new(data: CpuIdResult) -> Self { Self { eax: data.eax, @@ -1433,7 +2014,7 @@ impl SvmFeatures { bitflags! { #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] - struct SvmFeaturesEdx: u32 { + pub(crate) struct SvmFeaturesEdx: u32 { const NP = 1 << 0; const LBR_VIRT = 1 << 1; const SVML = 1 << 2; @@ -1461,15 +2042,24 @@ bitflags! { /// ✅ AMD ❌ Intel #[derive(PartialEq, Eq, Debug)] pub struct Tlb1gbPageInfo { - eax: u32, - ebx: u32, + pub(crate) eax: u32, + pub(crate) ebx: u32, /// Reserved - _ecx: u32, + pub(crate) _ecx: u32, /// Reserved - _edx: u32, + pub(crate) _edx: u32, } impl Tlb1gbPageInfo { + pub fn empty() -> Self { + Self { + eax: 0, + ebx: 0, + _ecx: 0, + _edx: 0, + } + } + pub(crate) fn new(data: CpuIdResult) -> Self { Self { eax: data.eax, @@ -1485,43 +2075,83 @@ impl Tlb1gbPageInfo { Associativity::for_l2(assoc_bits) } + pub fn set_dtlb_l1_1gb_associativity(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.eax, bits, 28, 31); + self + } + /// L1 Data TLB number of entries for 1-GB pages. pub fn dtlb_l1_1gb_size(&self) -> u8 { get_bits(self.eax, 16, 27) as u8 } + pub fn set_dtlb_l1_1gb_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.eax, bits, 16, 27); + self + } + /// L1 Instruction TLB associativity for 1-GB pages. pub fn itlb_l1_1gb_associativity(&self) -> Associativity { let assoc_bits = get_bits(self.eax, 12, 15) as u8; Associativity::for_l2(assoc_bits) } + pub fn set_itlb_l1_1gb_associativity(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.eax, bits, 12, 15); + self + } + /// L1 Instruction TLB number of entries for 1-GB pages. pub fn itlb_l1_1gb_size(&self) -> u8 { get_bits(self.eax, 0, 11) as u8 } + pub fn set_itlb_l1_1gb_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.eax, bits, 0, 11); + self + } + /// L2 Data TLB associativity for 1-GB pages. pub fn dtlb_l2_1gb_associativity(&self) -> Associativity { let assoc_bits = get_bits(self.ebx, 28, 31) as u8; Associativity::for_l2(assoc_bits) } + pub fn set_dtlb_l2_1gb_associativity(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ebx, bits, 28, 31); + self + } + /// L2 Data TLB number of entries for 1-GB pages. pub fn dtlb_l2_1gb_size(&self) -> u8 { get_bits(self.ebx, 16, 27) as u8 } + pub fn set_dtlb_l2_1gb_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ebx, bits, 16, 27); + self + } + /// L2 Instruction TLB associativity for 1-GB pages. pub fn itlb_l2_1gb_associativity(&self) -> Associativity { let assoc_bits = get_bits(self.ebx, 12, 15) as u8; Associativity::for_l2(assoc_bits) } + pub fn set_itlb_l2_1gb_associativity(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ebx, bits, 12, 15); + self + } + /// L2 Instruction TLB number of entries for 1-GB pages. pub fn itlb_l2_1gb_size(&self) -> u8 { get_bits(self.ebx, 0, 11) as u8 } + + pub fn set_itlb_l2_1gb_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ebx, bits, 0, 11); + self + } } /// Performance Optimization Identifier (LEAF=0x8000_001A). @@ -1530,16 +2160,25 @@ impl Tlb1gbPageInfo { /// ✅ AMD ❌ Intel #[derive(PartialEq, Eq, Debug)] pub struct PerformanceOptimizationInfo { - eax: PerformanceOptimizationInfoEax, + pub(crate) eax: PerformanceOptimizationInfoEax, /// Reserved - _ebx: u32, + pub(crate) _ebx: u32, /// Reserved - _ecx: u32, + pub(crate) _ecx: u32, /// Reserved - _edx: u32, + pub(crate) _edx: u32, } impl PerformanceOptimizationInfo { + pub fn empty() -> Self { + Self { + eax: PerformanceOptimizationInfoEax::from_bits_truncate(0), + _ebx: 0, + _ecx: 0, + _edx: 0, + } + } + pub(crate) fn new(data: CpuIdResult) -> Self { Self { eax: PerformanceOptimizationInfoEax::from_bits_truncate(data.eax), @@ -1554,22 +2193,37 @@ impl PerformanceOptimizationInfo { self.eax.contains(PerformanceOptimizationInfoEax::FP128) } + pub fn set_fp128(&mut self, bit: bool) -> &mut Self { + self.eax.set(PerformanceOptimizationInfoEax::FP128, bit); + self + } + /// MOVU (Move Unaligned) SSE instructions are efficient more than /// MOVL/MOVH SSE if set. pub fn has_movu(&self) -> bool { self.eax.contains(PerformanceOptimizationInfoEax::MOVU) } + pub fn set_movu(&mut self, bit: bool) -> &mut Self { + self.eax.set(PerformanceOptimizationInfoEax::MOVU, bit); + self + } + /// The internal FP/SIMD execution datapath is 256 bits wide if set. pub fn has_fp256(&self) -> bool { self.eax.contains(PerformanceOptimizationInfoEax::FP256) } + + pub fn set_fp256(&mut self, bit: bool) -> &mut Self { + self.eax.set(PerformanceOptimizationInfoEax::FP256, bit); + self + } } bitflags! { #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] - struct PerformanceOptimizationInfoEax: u32 { + pub(crate) struct PerformanceOptimizationInfoEax: u32 { const FP128 = 1 << 0; const MOVU = 1 << 1; const FP256 = 1 << 2; @@ -1582,14 +2236,23 @@ bitflags! { /// ✅ AMD ❌ Intel #[derive(PartialEq, Eq)] pub struct ProcessorTopologyInfo { - eax: u32, - ebx: u32, - ecx: u32, + pub(crate) eax: u32, + pub(crate) ebx: u32, + pub(crate) ecx: u32, /// Reserved - _edx: u32, + pub(crate) _edx: u32, } impl ProcessorTopologyInfo { + pub fn empty() -> Self { + Self { + eax: 0, + ebx: 0, + ecx: 0, + _edx: 0, + } + } + pub(crate) fn new(data: CpuIdResult) -> Self { Self { eax: data.eax, @@ -1620,6 +2283,15 @@ impl ProcessorTopologyInfo { get_bits(self.ebx, 8, 15) as u8 + 1 } + /// Set threads per core (or `Cores per Compute Unit` on AMD Family >=15h) + /// + /// # Note + /// The value provided here is one more than will be recorded in the CPUID leaf. + pub fn set_threads_per_core(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ebx, bits - 1, 8, 15); + self + } + /// Node ID pub fn node_id(&self) -> u8 { get_bits(self.ecx, 0, 7) as u8 @@ -1649,10 +2321,10 @@ impl Debug for ProcessorTopologyInfo { /// ✅ AMD ❌ Intel #[derive(Debug, PartialEq, Eq)] pub struct MemoryEncryptionInfo { - eax: MemoryEncryptionInfoEax, - ebx: u32, - ecx: u32, - edx: u32, + pub(crate) eax: MemoryEncryptionInfoEax, + pub(crate) ebx: u32, + pub(crate) ecx: u32, + pub(crate) edx: u32, } impl MemoryEncryptionInfo { @@ -1754,7 +2426,7 @@ impl MemoryEncryptionInfo { bitflags! { #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] - struct MemoryEncryptionInfoEax: u32 { + pub(crate) struct MemoryEncryptionInfoEax: u32 { const SME = 1 << 0; const SEV = 1 << 1; const PAGE_FLUSH_MSR = 1 << 2; @@ -1770,3 +2442,766 @@ bitflags! { const VTE = 1 << 16; } } + +/// Platform Quality of Service Information (LEAF=0x8000_0020). +/// +/// # Platforms +/// ✅ AMD ❌ Intel +#[derive(PartialEq, Eq)] +pub struct PqosExtendedFeatureInfo { + source: R, + _eax: u32, + ebx: PqosExtendedFeatureInfoEbx, + _ecx: u32, + _edx: u32, +} + +impl PqosExtendedFeatureInfo { + pub(crate) fn new(source: R) -> Self { + let data = source.cpuid2(EAX_PQOS_EXTENDED_FEATURES, 0); + Self { + source, + _eax: data.eax, + ebx: PqosExtendedFeatureInfoEbx::from_bits_truncate(data.ebx), + _ecx: data.ecx, + _edx: data.edx, + } + } + + /// Memory Bandwidth Enforcement is supported if set. + pub fn has_l3mbe(&self) -> bool { + self.ebx.contains(PqosExtendedFeatureInfoEbx::L3MBE) + } + + /// Slow Memory Bandwidth Enforcement is supported if set. + pub fn has_l3smbe(&self) -> bool { + self.ebx.contains(PqosExtendedFeatureInfoEbx::L3SMBE) + } + + /// Bandwidth Monitoring Event Configuration is supported if set. + pub fn has_bmec(&self) -> bool { + self.ebx.contains(PqosExtendedFeatureInfoEbx::BMEC) + } + + /// L3 Range Reservations. See “L3 Range Reservation” in APM + /// Volume 2 is supported if set. + pub fn has_l3rr(&self) -> bool { + self.ebx.contains(PqosExtendedFeatureInfoEbx::L3RR) + } + + /// Assignable Bandwidth Monitoring Counters is supported if set. + pub fn has_abmc(&self) -> bool { + self.ebx.contains(PqosExtendedFeatureInfoEbx::ABMC) + } + + /// Smart Data Cache Injection (SDCI) Allocation Enforcement is supported if set. + pub fn has_sdciae(&self) -> bool { + self.ebx.contains(PqosExtendedFeatureInfoEbx::SDCIAE) + } + + /// Get L3 Memory Bandwidth Enforcement Information + pub fn get_l3_memory_bandwidth_enforcement_info( + &self, + ) -> Option { + if self.has_l3mbe() { + Some(L3MemoryBandwidthEnforcementInformation::new( + self.source.cpuid2(EAX_PQOS_EXTENDED_FEATURES, 1), + )) + } else { + None + } + } + + /// Get L3 Slow Memory Bandwidth Enforcement Information + pub fn get_l3_slow_memory_bandwidth_enforcement_info( + &self, + ) -> Option { + if self.has_l3smbe() { + Some(L3MemoryBandwidthEnforcementInformation::new( + self.source.cpuid2(EAX_PQOS_EXTENDED_FEATURES, 2), + )) + } else { + None + } + } + + /// Get Bandwidth Monitoring Event Counters Information + pub fn get_bandwidth_monitoring_event_counters_info( + &self, + ) -> Option { + if self.has_bmec() { + Some(BandwidthMonitoringEventCounters::new( + self.source.cpuid2(EAX_PQOS_EXTENDED_FEATURES, 3), + )) + } else { + None + } + } + + /// Get Bandwidth Monitoring Event Counters Information + pub fn get_assignable_bandwidth_monitoring_counters_info( + &self, + ) -> Option { + if self.has_abmc() { + Some(AssignableBandwidthMonitoringCounterInfo::new( + self.source.cpuid2(EAX_PQOS_EXTENDED_FEATURES, 5), + )) + } else { + None + } + } +} + +bitflags! { + #[repr(transparent)] + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + struct PqosExtendedFeatureInfoEbx: u32 { + const L3MBE = 1 << 1; + const L3SMBE = 1 << 2; + const BMEC = 1 << 3; + const L3RR = 1 << 4; + const ABMC = 1 << 5; + const SDCIAE = 1 << 6; + } +} + +bitflags! { + #[repr(transparent)] + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + struct PqosExtendedFeatureInfoEbx5: u32 { + const SELECT_COS = 1 << 0; + } +} + +impl Debug for PqosExtendedFeatureInfo { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("PqosExtendedFeatureInfo") + .field("has_l3mbe", &self.has_l3mbe()) + .field("has_l3smbe", &self.has_l3smbe()) + .field("has_bmec", &self.has_bmec()) + .field("has_l3rr", &self.has_l3rr()) + .field("has_abmc", &self.has_abmc()) + .field("has_sdciae", &self.has_sdciae()) + .finish() + } +} + +/// L3 Memory Bandwidth Enforcement Information (LEAF=0x8000_0020_x1 and x2). +/// +/// # Platforms +/// ✅ AMD ❌ Intel +#[derive(PartialEq, Eq, Debug)] +pub struct L3MemoryBandwidthEnforcementInformation { + eax: u32, + _ebx: u32, + _ecx: u32, + edx: u32, +} + +impl L3MemoryBandwidthEnforcementInformation { + pub(crate) fn new(data: CpuIdResult) -> Self { + Self { + eax: data.eax, + _ebx: data.ebx, + _ecx: data.ecx, + edx: data.edx, + } + } + + /// Identifies the size of the bandwidth specifier field in the + /// L3QOS_BW_Control_n MSRs + pub fn bandwidth_length(&self) -> u32 { + self.eax + } + + /// Maximum COS number supported by the L3MBE feature + pub fn cos_max(&self) -> u32 { + self.edx + } +} + +/// Bandwidth Monitoring Event Counters Information (LEAF=0x8000_0020_x3). +/// +/// # Platforms +/// ✅ AMD ❌ Intel +#[derive(PartialEq, Eq, Debug)] +pub struct BandwidthMonitoringEventCounters { + _eax: u32, + ebx: u32, + ecx: BandwidthMonitoringEventCountersEcx, + _edx: u32, +} + +impl BandwidthMonitoringEventCounters { + pub(crate) fn new(data: CpuIdResult) -> Self { + Self { + _eax: data.eax, + ebx: data.ebx, + ecx: BandwidthMonitoringEventCountersEcx::from_bits_truncate(data.ecx), + _edx: data.edx, + } + } + + /// Get Number of configurable bandwidth events + pub fn number_events(&self) -> u32 { + get_bits(self.ebx, 0, 7) + } + + /// Reads to local DRAM memory is supported if set. + pub fn has_l3_cache_lcl_bw_fill_mon(&self) -> bool { + self.ecx + .contains(BandwidthMonitoringEventCountersEcx::L3_CACHE_LCL_BW_FILL_MON) + } + + /// Reads to remote DRAM memory is supported if set. + pub fn has_l3_cache_rmt_bw_fill_mon(&self) -> bool { + self.ecx + .contains(BandwidthMonitoringEventCountersEcx::L3_CACHE_RMT_BW_FILL_MON) + } + + /// Non-temporal writes to local memory is supported if set. + pub fn has_l3_cache_lcl_bw_nt_wr_mon(&self) -> bool { + self.ecx + .contains(BandwidthMonitoringEventCountersEcx::L3_CACHE_LCL_BW_NT_WR_MON) + } + + /// Non-temporal writes to remote memory is supported if set. + pub fn has_l3_cache_rmt_bw_nt_wr_mon(&self) -> bool { + self.ecx + .contains(BandwidthMonitoringEventCountersEcx::L3_CACHE_RMT_BW_NT_WR_MON) + } + + /// Reads to local memory identified as “Slow Memory” is supported if set. + pub fn has_l3_cache_lcl_slow_bw_fill_mon(&self) -> bool { + self.ecx + .contains(BandwidthMonitoringEventCountersEcx::L3_CACHE_LCL_SLOW_BW_FILL_MON) + } + + /// Reads to remote memory identified as “Slow Memory” is supported if set. + pub fn has_l3_cache_rmt_slow_bw_fill_mon(&self) -> bool { + self.ecx + .contains(BandwidthMonitoringEventCountersEcx::L3_CACHE_RMT_SLOW_BW_FILL_MON) + } + + /// Dirty victim writes to all types of memory is supported if set. + pub fn has_l3_cache_vic_mon(&self) -> bool { + self.ecx + .contains(BandwidthMonitoringEventCountersEcx::L3_CACHE_VIC_MON) + } +} + +bitflags! { + #[repr(transparent)] + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + struct BandwidthMonitoringEventCountersEcx: u32 { + const L3_CACHE_LCL_BW_FILL_MON = 1 << 0; + const L3_CACHE_RMT_BW_FILL_MON = 1 << 1; + const L3_CACHE_LCL_BW_NT_WR_MON = 1 << 2; + const L3_CACHE_RMT_BW_NT_WR_MON = 1 << 3; + const L3_CACHE_LCL_SLOW_BW_FILL_MON = 1 << 4; + const L3_CACHE_RMT_SLOW_BW_FILL_MON = 1 << 5; + const L3_CACHE_VIC_MON = 1 << 6; + } +} + +/// L3 Memory Bandwidth Enforcement Information (LEAF=0x8000_0020_x5). +/// +/// # Platforms +/// ✅ AMD ❌ Intel +#[derive(PartialEq, Eq, Debug)] +pub struct AssignableBandwidthMonitoringCounterInfo { + eax: u32, + ebx: u32, + ecx: u32, + _edx: u32, +} + +impl AssignableBandwidthMonitoringCounterInfo { + pub(crate) fn new(data: CpuIdResult) -> Self { + Self { + eax: data.eax, + ebx: data.ebx, + ecx: data.ecx, + _edx: data.edx, + } + } + + /// Get QM_CTR counter width, offset from 24 bits + pub fn counter_size(&self) -> u8 { + get_bits(self.eax, 0, 7) as u8 + } + + /// Indicates that QM_CTR bit 61 is an overflow bit if set + pub fn has_overflow_bit(&self) -> bool { + (self.eax & (1 << 8)) > 0 + } + + /// Get Maximum supported ABMC counter ID + pub fn max_abmc(&self) -> u16 { + get_bits(self.ebx, 0, 15) as u16 + } + + /// Bandwidth counters can be configured to measure + /// bandwidth consumed by a COS instead of an RMID if set + pub fn has_select_cos(&self) -> bool { + (self.ecx & 1) > 0 + } +} + +/// Extended Feature Identification 2 (LEAF=0x8000_0021). +/// +/// # Platforms +/// ✅ AMD ❌ Intel +#[derive(PartialEq, Eq, Debug)] +pub struct ExtendedFeatureIdentification2 { + pub(crate) eax: ExtendedFeatureIdentification2Eax, + pub(crate) ebx: u32, + pub(crate) _ecx: u32, + pub(crate) _edx: u32, +} + +impl ExtendedFeatureIdentification2 { + pub fn empty() -> Self { + Self { + eax: ExtendedFeatureIdentification2Eax::from_bits_truncate(0), + ebx: 0, + _ecx: 0, + _edx: 0, + } + } + + pub(crate) fn new(data: CpuIdResult) -> Self { + Self { + eax: ExtendedFeatureIdentification2Eax::from_bits_truncate(data.eax), + ebx: data.ebx, + _ecx: data.ecx, + _edx: data.edx, + } + } + + /// Processor ignores nested data breakpoints if set + pub fn has_no_nested_data_bp(&self) -> bool { + self.eax + .contains(ExtendedFeatureIdentification2Eax::NO_NESTED_DATA_BP) + } + + pub fn set_no_nested_data_bp(&mut self, bit: bool) -> &mut Self { + self.eax + .set(ExtendedFeatureIdentification2Eax::NO_NESTED_DATA_BP, bit); + self + } + + /// LFENCE is always dispatch serializing if set + pub fn has_lfence_always_serializing(&self) -> bool { + self.eax + .contains(ExtendedFeatureIdentification2Eax::LFENCE_ALWAYS_SERIALIZING) + } + + pub fn set_lfence_always_serializing(&mut self, bit: bool) -> &mut Self { + self.eax.set( + ExtendedFeatureIdentification2Eax::LFENCE_ALWAYS_SERIALIZING, + bit, + ); + self + } + + /// SMM paging configuration lock supported if set + pub fn has_smm_pg_cfg_lock(&self) -> bool { + self.eax + .contains(ExtendedFeatureIdentification2Eax::SMM_PG_CFG_LOCK) + } + + pub fn set_smm_pg_cfg_lock(&mut self, bit: bool) -> &mut Self { + self.eax + .set(ExtendedFeatureIdentification2Eax::SMM_PG_CFG_LOCK, bit); + self + } + + /// Null segment selector loads also clear the destination segment register + /// base and limit supported if set + pub fn has_null_select_clears_base(&self) -> bool { + self.eax + .contains(ExtendedFeatureIdentification2Eax::NULL_SELECT_CLEARS_BASE) + } + + pub fn set_null_select_clears_base(&mut self, bit: bool) -> &mut Self { + self.eax.set( + ExtendedFeatureIdentification2Eax::NULL_SELECT_CLEARS_BASE, + bit, + ); + self + } + + /// Upper Address Ignore is supported if set + pub fn has_upper_address_ignore(&self) -> bool { + self.eax + .contains(ExtendedFeatureIdentification2Eax::UPPER_ADDRESS_IGNORE) + } + + pub fn set_upper_address_ignore(&mut self, bit: bool) -> &mut Self { + self.eax + .set(ExtendedFeatureIdentification2Eax::UPPER_ADDRESS_IGNORE, bit); + self + } + + /// Automatic IBRS if set + pub fn has_automatic_ibrs(&self) -> bool { + self.eax + .contains(ExtendedFeatureIdentification2Eax::AUTOMATIC_IBRS) + } + + pub fn set_automatic_ibrs(&mut self, bit: bool) -> &mut Self { + self.eax + .set(ExtendedFeatureIdentification2Eax::AUTOMATIC_IBRS, bit); + self + } + + /// SMM_CTL MSR (C001_0116h) is not supported if set + pub fn has_no_smm_ctl_msr(&self) -> bool { + self.eax + .contains(ExtendedFeatureIdentification2Eax::NO_SMM_CTL_MSR) + } + + pub fn set_no_smm_ctl_msr(&mut self, bit: bool) -> &mut Self { + self.eax + .set(ExtendedFeatureIdentification2Eax::NO_SMM_CTL_MSR, bit); + self + } + + /// Prefetch control MSR supported if set + pub fn has_prefetch_ctl_msr(&self) -> bool { + self.eax + .contains(ExtendedFeatureIdentification2Eax::PREFETCH_CTL_MSR) + } + + pub fn set_prefetch_ctl_msr(&mut self, bit: bool) -> &mut Self { + self.eax + .set(ExtendedFeatureIdentification2Eax::PREFETCH_CTL_MSR, bit); + self + } + + /// CPUID disable for non-privileged software if set + pub fn has_cpuid_user_dis(&self) -> bool { + self.eax + .contains(ExtendedFeatureIdentification2Eax::CPUID_USER_DIS) + } + + pub fn set_cpuid_user_dis(&mut self, bit: bool) -> &mut Self { + self.eax + .set(ExtendedFeatureIdentification2Eax::CPUID_USER_DIS, bit); + self + } + + /// The size of the Microcode patch in 16-byte multiples. If 0, the size of the + /// patch is at most 5568 (15C0h) bytes. + pub fn microcode_patch_size(&self) -> u16 { + get_bits(self.ebx, 0, 11) as u16 + } + + pub fn microcode_patcset_size(&mut self, bits: u32) -> &mut Self { + set_bits(&mut self.ebx, bits, 0, 11); + self + } +} + +bitflags! { + #[repr(transparent)] + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub(crate) struct ExtendedFeatureIdentification2Eax: u32 { + const NO_NESTED_DATA_BP = 1 << 0; + const LFENCE_ALWAYS_SERIALIZING = 1 << 2; + const SMM_PG_CFG_LOCK = 1 << 3; + const NULL_SELECT_CLEARS_BASE = 1 << 6; + const UPPER_ADDRESS_IGNORE = 1 << 7; + const AUTOMATIC_IBRS = 1 << 8; + const NO_SMM_CTL_MSR = 1 << 9; + const PREFETCH_CTL_MSR = 1 << 13; + const CPUID_USER_DIS = 1 << 17; + } +} + +/// Extended Feature Identification 2 (LEAF=0x8000_0021). +/// +/// # Platforms +/// ✅ AMD ❌ Intel +#[derive(PartialEq, Eq, Debug)] +pub struct ExtendedPerformanceMonitoringDebug { + eax: ExtendedPerformanceMonitoringDebugEax, + ebx: u32, + _ecx: u32, + _edx: u32, +} + +impl ExtendedPerformanceMonitoringDebug { + pub(crate) fn new(data: CpuIdResult) -> Self { + Self { + eax: ExtendedPerformanceMonitoringDebugEax::from_bits_truncate(data.eax), + ebx: data.ebx, + _ecx: data.ecx, + _edx: data.edx, + } + } + + /// Performance Monitoring Version 2 supported if set + pub fn has_perf_mon_v2(&self) -> bool { + self.eax + .contains(ExtendedPerformanceMonitoringDebugEax::PERF_MON_V2) + } + + /// Last Branch Record Stack supported if set + pub fn has_lbr_stack(&self) -> bool { + self.eax + .contains(ExtendedPerformanceMonitoringDebugEax::LBR_STACK) + } + + /// Freezing Core Performance Counters and LBR Stack on Core + /// Performance Counter overflow supported if set + pub fn has_lbr_and_pmc_freeze(&self) -> bool { + self.eax + .contains(ExtendedPerformanceMonitoringDebugEax::LBR_AND_PMC_FREEZE) + } + + /// Number of Core Performance Counters + pub fn num_perf_ctr_core(&self) -> u8 { + get_bits(self.ebx, 0, 3) as u8 + } + + /// Number of Last Branch Record Stack entries + pub fn num_lbr_stack_size(&self) -> u8 { + get_bits(self.ebx, 4, 9) as u8 + } + + /// Number of Northbridge Performance Monitor Counters + pub fn num_perf_ctr_nb(&self) -> u8 { + get_bits(self.ebx, 10, 15) as u8 + } +} + +bitflags! { + #[repr(transparent)] + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + struct ExtendedPerformanceMonitoringDebugEax: u32 { + const PERF_MON_V2 = 1 << 0; + const LBR_STACK = 1 << 1; + const LBR_AND_PMC_FREEZE = 1 << 2; + } +} + +/// Extended Feature Identification 2 (LEAF=0x8000_0021). +/// +/// # Platforms +/// ✅ AMD ❌ Intel +#[derive(PartialEq, Eq, Debug)] +pub struct MultiKeyEncryptedMemoryCapabilities { + eax: MultiKeyEncryptedMemoryCapabilitiesEax, + ebx: u32, + _ecx: u32, + _edx: u32, +} + +impl MultiKeyEncryptedMemoryCapabilities { + pub(crate) fn new(data: CpuIdResult) -> Self { + Self { + eax: MultiKeyEncryptedMemoryCapabilitiesEax::from_bits_truncate(data.eax), + ebx: data.ebx, + _ecx: data.ecx, + _edx: data.edx, + } + } + + /// Secure Host Multi-Key Memory (MEM-HMK) Encryption Mode Supported if set + pub fn has_mem_hmk(&self) -> bool { + self.eax + .contains(MultiKeyEncryptedMemoryCapabilitiesEax::MEM_HMK) + } + + /// Number of simultaneously available host encryption key IDs in MEM-HMK + /// encryption mode. + pub fn max_mem_hmk_encr_key_id(&self) -> u16 { + get_bits(self.ebx, 0, 15) as u16 + } +} + +bitflags! { + #[repr(transparent)] + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + struct MultiKeyEncryptedMemoryCapabilitiesEax: u32 { + const MEM_HMK = 1 << 0; + } +} + +/// Extended CPU Topology (LEAF=0x8000_0026). +/// +/// Iterates over the extended cpu topology in order to retrieve more information for logical +/// processors, including asymmetric and heterogenous topology descriptions. Individual +/// logical processors may report different values in systems with asynchronous and +/// heterogeneous topologies +/// +/// # Platforms +/// ✅ AMD ❌ Intel +#[derive(Clone)] +pub struct ExtendedCpuTopologyIter { + source: R, + level: u32, +} + +impl ExtendedCpuTopologyIter { + pub fn new(source: R) -> Self { + Self { source, level: 0 } + } +} + +/// Gives information about the current level in the cpu topology. +#[derive(PartialEq, Eq, Debug)] +pub struct ExtendedCpuTopologyLevel { + eax: u32, + ebx: u32, + ecx: u32, + edx: u32, +} + +impl ExtendedCpuTopologyLevel { + pub(crate) fn new(data: CpuIdResult) -> Self { + Self { + eax: data.eax, + ebx: data.ebx, + ecx: data.ecx, + edx: data.edx, + } + } + + /// Number of bits to shift Extended APIC ID right to get a unique topology ID + /// of the current hierarchy level. + pub fn mask_width(&self) -> u8 { + get_bits(self.eax, 0, 4) as u8 + } + + /// Set to 1 if processor power efficiency ranking (PwrEfficiencyRanking) is + /// available and varies between cores. Only valid for LevelType = 1h (Core). + pub fn has_efficiency_ranking_available(&self) -> bool { + self.eax & (1 << 29) > 0 + } + + /// Set to 1 if all components at the current hierarchy level do not consist of + /// the cores that report the same core type (CoreType). + pub fn has_heterogeneous_cores(&self) -> bool { + self.eax & (1 << 30) > 0 + } + + /// Set to 1 if all components at the current hierarchy level do not report the + /// same number of logical processors (NumLogProc). + pub fn has_asymmetric_topology(&self) -> bool { + self.eax & (1 << 31) > 0 + } + + /// Number of logical processors at the current hierarchy level + pub fn num_logical_processors(&self) -> u16 { + get_bits(self.ebx, 0, 15) as u16 + } + + /// Reports a static efficiency ranking between cores of a specific core type, + /// where a lower value indicates comparatively lower power consumption + /// and lower performance. Only valid for LevelType = 1h (Core) + pub fn pwr_efficiency_ranking(&self) -> u8 { + get_bits(self.ebx, 16, 23) as u8 + } + + /// Reports a value that may be used to further differentiate implementation + /// specific features. Native mode ID is used in conjunction with the family, + /// model, and stepping identifiers. Refer to the Processor Programming + /// Reference Manual applicable to your product for a list of Native Mode + /// IDs. Only valid for LevelType = 1h (Core) + pub fn native_mode_id(&self) -> u8 { + get_bits(self.ebx, 24, 27) as u8 + } + + /// Reports a value that may be used to distinguish between cores with + /// different architectural and microarchitectural properties (for example, + /// cores with different performance or power characteristics). Refer to the + /// Processor Programming Reference Manual applicable to your product for + /// a list of the available core types. Only valid for LevelType = 1h (Core) + pub fn core_type(&self) -> u8 { + get_bits(self.ebx, 28, 31) as u8 + } + + /// Input ECX + pub fn input_ecx(&self) -> u8 { + get_bits(self.ecx, 0, 7) as u8 + } + + /// Encoded hierarchy level type + pub fn level_type(&self) -> HierarchyLevelType { + HierarchyLevelType::from(get_bits(self.ecx, 8, 15) as u8) + } + + /// Extended APIC ID of the logical processor + pub fn extended_apic_id(&self) -> u32 { + self.edx + } +} + +impl Iterator for ExtendedCpuTopologyIter { + type Item = ExtendedCpuTopologyLevel; + + fn next(&mut self) -> Option { + let res = self.source.cpuid2(EAX_EXTENDED_CPU_TOPOLOGY, self.level); + self.level += 1; + + let ect = ExtendedCpuTopologyLevel::new(res); + if ect.level_type() == HierarchyLevelType::Reserved { + None + } else { + Some(ect) + } + } +} + +#[repr(u8)] +#[derive(PartialEq, Eq)] +pub enum HierarchyLevelType { + Reserved = 0, + Core = 1, + Complex = 2, + Die = 3, + Socket = 4, + Unknown(u8), +} + +impl From for HierarchyLevelType { + fn from(value: u8) -> Self { + match value { + 0 => Self::Reserved, + 1 => Self::Core, + 2 => Self::Complex, + 3 => Self::Die, + 4 => Self::Socket, + x => Self::Unknown(x), + } + } +} + +impl Display for HierarchyLevelType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + HierarchyLevelType::Reserved => write!(f, "Reserved (0)"), + HierarchyLevelType::Core => write!(f, "Core (1)"), + HierarchyLevelType::Complex => write!(f, "Complex (2)"), + HierarchyLevelType::Die => write!(f, "DIE (3)"), + HierarchyLevelType::Socket => write!(f, "Socket (4)"), + HierarchyLevelType::Unknown(x) => write!(f, "Unknown ({x})"), + } + } +} + +impl Debug for HierarchyLevelType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::Reserved => write!(f, "Reserved"), + Self::Core => write!(f, "Core"), + Self::Complex => write!(f, "Complex"), + Self::Die => write!(f, "Die"), + Self::Socket => write!(f, "Socket"), + Self::Unknown(arg0) => f.debug_tuple("Unknown").field(arg0).finish(), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 9d10761..724c94c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,6 +54,8 @@ extern crate std; #[cfg(feature = "display")] pub mod display; +#[cfg(feature = "std")] +mod dump; mod extended; #[cfg(test)] mod tests; @@ -67,6 +69,8 @@ use core::str; #[cfg(feature = "serialize")] use serde_derive::{Deserialize, Serialize}; +#[cfg(feature = "std")] +pub use dump::CpuIdDump; pub use extended::*; /// Uses Rust's `cpuid` function from the `arch` module. @@ -144,12 +148,57 @@ fn get_bits(r: u32, from: u32, to: u32) -> u32 { (r & mask) >> from } +fn set_bits(r: &mut u32, v: u32, from: u32, to: u32) { + assert!(from <= 31); + assert!(to <= 31); + assert!(from <= to); + + let width = to - from + 1; + let max = match width { + 32 => 0xffffffff, + _ => (1 << (to - from + 1)) - 1, + }; + assert!(v <= max); + + let mask = max << from; + + *r &= !mask; + *r |= v << from; +} + +#[test] +fn set_bits_is_reasonable() { + let mut r = 0u32; + set_bits(&mut r, 1, 0, 0); + assert_eq!(r, 0b0001); + set_bits(&mut r, 1, 1, 1); + assert_eq!(r, 0b0011); + set_bits(&mut r, 0, 0, 0); + assert_eq!(r, 0b0010); + set_bits(&mut r, 0b11, 0, 31); + assert_eq!(r, 0b0011); + + set_bits(&mut r, 0x2000_0000, 2, 31); + assert_eq!(r, 0x8000_0003); + assert_eq!(0b10, get_bits(r, 30, 31)); + + set_bits(&mut r, 0x3fff_ffff, 2, 31); + assert_eq!(r, 0xffff_ffff); + set_bits(&mut r, 0xffff_ffff, 0, 31); + assert_eq!(r, 0xffff_ffff); +} + macro_rules! check_flag { - ($doc:meta, $fun:ident, $flags:ident, $flag:expr) => { + ($doc:meta, $fun:ident, $setfun:ident, $flags:ident, $flag:expr) => { #[$doc] pub fn $fun(&self) -> bool { self.$flags.contains($flag) } + + pub fn $setfun(&mut self, bit: bool) -> &mut Self { + self.$flags.set($flag, bit); + self + } }; } @@ -168,7 +217,16 @@ macro_rules! check_bit_fn { }; } -/// Implements function to read/write cpuid. +/// Implements function to write cpuid. +/// +/// Implementations are expected to maintain CPUID leaf limits (EAX in leaves 0h, 4000_0000h, and +/// 8000_000h) cover defined leaves automatically as leaves are added and removed. +pub trait CpuIdWriter { + fn set_leaf(&mut self, leaf: u32, bits: Option); + fn set_subleaf(&mut self, leaf: u32, subleaf: u32, bits: Option); +} + +/// Implements function to read cpuid. /// This allows to conveniently swap out the underlying cpuid implementation /// with one that returns data that is deterministic (for unit-testing). pub trait CpuIdReader: Clone { @@ -188,7 +246,7 @@ where } #[derive(Debug, Eq, PartialEq, Clone, Copy)] -enum Vendor { +pub enum Vendor { Intel, Amd, Unknown(u32, u32, u32), @@ -216,7 +274,7 @@ impl Vendor { #[derive(Clone, Copy)] pub struct CpuId { /// A generic reader to abstract the cpuid interface. - read: R, + source: R, /// CPU vendor to differentiate cases where logic needs to differ in code . vendor: Vendor, /// How many basic leafs are supported (EAX < EAX_HYPERVISOR_INFO) @@ -225,6 +283,12 @@ pub struct CpuId { supported_extended_leafs: u32, } +impl CpuId { + pub fn into_source(self) -> S { + self.source + } +} + #[cfg(any( all(target_arch = "x86", not(target_env = "sgx"), target_feature = "sse"), all(target_arch = "x86_64", not(target_env = "sgx")) @@ -263,6 +327,15 @@ pub struct CpuIdResult { } impl CpuIdResult { + pub fn empty() -> Self { + CpuIdResult { + eax: 0, + ebx: 0, + ecx: 0, + edx: 0, + } + } + pub fn all_zero(&self) -> bool { self.eax == 0 && self.ebx == 0 && self.ecx == 0 && self.edx == 0 } @@ -323,6 +396,668 @@ const EAX_CACHE_PARAMETERS_AMD: u32 = 0x8000_001D; const EAX_PROCESSOR_TOPOLOGY_INFO: u32 = 0x8000_001E; const EAX_MEMORY_ENCRYPTION_INFO: u32 = 0x8000_001F; const EAX_SVM_FEATURES: u32 = 0x8000_000A; +const EAX_PQOS_EXTENDED_FEATURES: u32 = 0x8000_0020; +const EAX_EXTENDED_FEATURE_IDENTIFICATION_2: u32 = 0x8000_0021; +const EAX_EXTENDED_PERFORMANCE_MONITORING_AND_DEBUG: u32 = 0x8000_0022; +const EAX_MULTI_KEY_ENCRYPTED_MEMORY_CAPABILITIES: u32 = 0x8000_0023; +const EAX_EXTENDED_CPU_TOPOLOGY: u32 = 0x8000_0026; + +// TODO: Would be nice to not require CpuIdReader here, as we don't use it, but: +// * `struct CpuId` requires `source`: implements `CpuIdReader` +// * without a `CpuIdReader` here, Rust infers the relevant `CpuIdReader` impl to be the blanket +// `impl CpuIdReader for F where F: Fn(u32, u32) -> CpuIdResult + Clone`, which then.. +// * Errors here about "expected an `Fn(u32, u32)` closure, found `W`" +// +// it's somewhat less confusing to just require `: CpuIdReader` here. +impl CpuId { + /// Set information about the vendor (LEAF=0x00). + /// + /// This leaf will contain a ASCII readable string such as "GenuineIntel" + /// for Intel CPUs or "AuthenticAMD" for AMD CPUs. + /// + /// # Platforms + /// ✅ AMD ✅ Intel + pub fn set_vendor_info(&mut self, leaf: Option) -> Result<(), ()> { + if let Some(leaf) = leaf { + // Leaf 0 EAX is ignored: it is the maximum supported "standard" (0x00XXXXXX) leaf and + // calculated based on what leaves have been set (or cleared). + self.source.set_leaf( + EAX_VENDOR_INFO, + Some(CpuIdResult { + eax: 0, + ebx: leaf.ebx, + ecx: leaf.ecx, + edx: leaf.edx, + }), + ); + } else { + self.source.set_leaf(EAX_VENDOR_INFO, None); + } + Ok(()) + } + + /// Set extended function info (LEAF=0x8000_0000). + /// + /// Similar to leaf 0h, this leaf describes limit of valid extended CPUID leaves (at or above + /// 0x8000_0000) in EAX, with a vendor identifier (typically the same as leaf 0h) in EBX, ECX, + /// EDX. + /// + /// While the name is different, the leaf is structurally identical to leaf 0h (vendor info), + /// so that structure is taken to define this leaf as well. + /// + /// # Platforms + /// ✅ AMD ✅ Intel + pub fn set_extended_function_info(&mut self, leaf: Option) -> Result<(), ()> { + if let Some(leaf) = leaf { + self.source.set_leaf( + EAX_EXTENDED_FUNCTION_INFO, + Some(CpuIdResult { + eax: 0, + ebx: leaf.ebx, + ecx: leaf.ecx, + edx: leaf.edx, + }), + ); + } else { + self.source.set_leaf(EAX_EXTENDED_FUNCTION_INFO, None); + } + + Ok(()) + } + + /// Query a set of features that are available on this CPU (LEAF=0x01). + /// + /// # Platforms + /// ✅ AMD ✅ Intel + pub fn set_feature_info(&mut self, leaf: Option) -> Result<(), ()> { + if let Some(leaf) = leaf { + let edx_ecx = leaf.edx_ecx.bits(); + let bytes = edx_ecx.to_le_bytes(); + use core::convert::TryInto; + let ecx = u32::from_le_bytes(bytes[0..4].try_into().expect("four bytes")); + let edx = u32::from_le_bytes(bytes[4..8].try_into().expect("four bytes")); + self.source.set_leaf( + EAX_FEATURE_INFO, + Some(CpuIdResult { + eax: leaf.eax, + ebx: leaf.ebx, + ecx, + edx, + }), + ); + } else { + self.source.set_leaf(EAX_FEATURE_INFO, None); + } + Ok(()) + } + + /* + * TODO: + * set_cache_info (leaf 2h) + * set_processor_serial (leaf 3h) + * set_cache_parameters (leaf 4h) + * set_extended_topology_info_v2 (leaf 1Fh) + * set_rdt_monitoring_info (leaf Fh) + * set_rdt_allocation_info (leaf 10H) + * set_sgx_info (leaf 12h) + * set_processor_trace_info (leaf 14h) + * set_tsc_info (leaf 15h) + * set_processor_frequency_info (leaf 16h) + * set_soc_vendor_info (leaf 17h) + * set_deterministic_address_translation_info (leaf 18h) + * set_hypervisor_info + */ + + /// Set information about how monitor/mwait works on this CPU (LEAF=0x05). + /// + /// # Platforms + /// 🟡 AMD ✅ Intel + pub fn set_monitor_mwait_info(&mut self, leaf: Option) -> Result<(), ()> { + if let Some(leaf) = leaf { + self.source.set_leaf( + EAX_MONITOR_MWAIT_INFO, + Some(CpuIdResult { + eax: leaf.eax, + ebx: leaf.ebx, + ecx: leaf.ecx, + edx: leaf.edx, + }), + ); + } else { + self.source.set_leaf(EAX_MONITOR_MWAIT_INFO, None); + } + Ok(()) + } + + /// Set information about thermal and power management features of the CPU (LEAF=0x06). + /// + /// # Platforms + /// 🟡 AMD ✅ Intel + pub fn set_thermal_power_info(&mut self, leaf: Option) -> Result<(), ()> { + if let Some(leaf) = leaf { + self.source.set_leaf( + EAX_THERMAL_POWER_INFO, + Some(CpuIdResult { + eax: leaf.eax.bits(), + ebx: leaf.ebx, + ecx: leaf.ecx.bits(), + edx: leaf._edx, + }), + ); + } else { + self.source.set_leaf(EAX_THERMAL_POWER_INFO, None); + } + Ok(()) + } + + /// Set bits for extended feature information (LEAF=0x07) + /// + /// # Platforms + /// 🟡 AMD ✅ Intel + pub fn set_extended_feature_info(&mut self, leaf: Option) -> Result<(), ()> { + if let Some(leaf) = leaf { + self.source.set_subleaf( + EAX_STRUCTURED_EXTENDED_FEATURE_INFO, + 0, + Some(CpuIdResult { + eax: leaf.eax, + ebx: leaf.ebx.bits(), + ecx: leaf.ecx.bits(), + edx: leaf.edx.bits(), + }), + ); + self.source.set_subleaf( + EAX_STRUCTURED_EXTENDED_FEATURE_INFO, + 1, + Some(CpuIdResult { + eax: leaf.eax1.bits(), + ebx: leaf._ebx1, + ecx: leaf._ecx1, + edx: leaf.edx1.bits(), + }), + ); + } else { + self.source.set_leaf(EAX_THERMAL_POWER_INFO, None); + } + Ok(()) + } + + /// Direct cache access info (LEAF=0x09). + /// + /// # Platforms + /// ❌ AMD ✅ Intel + pub fn set_direct_cache_access_info( + &mut self, + leaf: Option, + ) -> Result<(), ()> { + if let Some(leaf) = leaf { + self.source.set_leaf( + EAX_DIRECT_CACHE_ACCESS_INFO, + Some(CpuIdResult { + eax: leaf.eax, + ebx: 0, + ecx: 0, + edx: 0, + }), + ); + } else { + self.source.set_leaf(EAX_DIRECT_CACHE_ACCESS_INFO, None); + } + + Ok(()) + } + + /// Info about performance monitoring (LEAF=0x0A). + /// + /// # Platforms + /// ❌ AMD ✅ Intel + pub fn set_performance_monitoring_info( + &mut self, + leaf: Option, + ) -> Result<(), ()> { + if let Some(leaf) = leaf { + self.source.set_leaf( + EAX_PERFORMANCE_MONITOR_INFO, + Some(CpuIdResult { + eax: leaf.eax, + ebx: leaf.ebx.bits(), + ecx: leaf._ecx, + edx: leaf.edx, + }), + ); + } else { + self.source.set_leaf(EAX_PERFORMANCE_MONITOR_INFO, None); + } + + Ok(()) + } + + /// Information about topology (LEAF=0x0B). + /// + /// Intel SDM suggests software should check support for leaf 0x1F + /// ([`CpuId::get_extended_topology_info_v2`]), and if supported, enumerate + /// that leaf instead. + /// + /// # Platforms + /// ✅ AMD ✅ Intel + pub fn set_extended_topology_info( + &mut self, + topo: Option<&[ExtendedTopologyLevel]>, + ) -> Result<(), ()> { + // Since the whole topology must be provided at once, blow away any prior leaves up front. + self.source.set_leaf(EAX_EXTENDED_TOPOLOGY_INFO, None); + + if let Some(topo) = topo { + for (idx, level) in topo.iter().enumerate() { + use core::convert::TryInto; + let idx = idx + .try_into() + .expect("extended topology subleaf fits into u32"); + self.source.set_subleaf( + EAX_EXTENDED_TOPOLOGY_INFO, + idx, + Some(CpuIdResult { + eax: level.eax, + ebx: level.ebx, + ecx: level.ecx, + edx: level.edx, + }), + ); + } + } + + Ok(()) + } + + /// Information for saving/restoring extended register state (LEAF=0x0D). + /// + /// # Platforms + /// ✅ AMD ✅ Intel + pub fn set_extended_state_info( + &mut self, + info: Option<&[Option]>, + ) -> Result<(), ()> { + // Since all extended state info must be provided at once, blow away any prior leaves up + // front. + self.source.set_leaf(EAX_EXTENDED_STATE_INFO, None); + + if let Some(info) = info { + for (idx, level) in info.iter().enumerate() { + use core::convert::TryInto; + let idx = idx + .try_into() + .expect("extended state subleaf fits into u32"); + self.source + .set_subleaf(EAX_EXTENDED_STATE_INFO, idx, *level); + } + } + + Ok(()) + } + + /// Extended Processor and Processor Feature Identifiers (LEAF=0x8000_0001). + /// + /// # Platforms + /// ✅ AMD 🟡 Intel + pub fn set_extended_processor_and_feature_identifiers( + &mut self, + leaf: Option, + ) -> Result<(), ()> { + if let Some(leaf) = leaf { + self.source.set_leaf( + EAX_EXTENDED_PROCESSOR_AND_FEATURE_IDENTIFIERS, + Some(CpuIdResult { + eax: leaf.eax, + ebx: leaf.ebx, + ecx: leaf.ecx.bits(), + edx: leaf.edx.bits(), + }), + ); + } else { + self.source + .set_leaf(EAX_EXTENDED_PROCESSOR_AND_FEATURE_IDENTIFIERS, None); + } + + Ok(()) + } + + /// Retrieve processor brand string (LEAF=0x8000_000{2..4}). + /// + /// # Platforms + /// ✅ AMD ✅ Intel + pub fn set_processor_brand_string( + &mut self, + brand_string: Option<&'static [u8]>, + ) -> Result<(), ()> { + if let Some(mut brand_string) = brand_string { + if brand_string.len() > 48 { + return Err(()); + } + + fn next_dword(buf: &mut &[u8]) -> u32 { + let to_read = core::cmp::min(buf.len(), 4); + + let (current, remainder) = buf.split_at(to_read); + + let mut res = [0u8; 4]; + res[..current.len()].copy_from_slice(¤t); + *buf = remainder; + + // Brand strings are space-padded. Null-terminated, too, but that's handled by + // callers. + for i in to_read..4 { + res[i] = 0x20; + } + + u32::from_le_bytes(res) + } + + let brand_string_0 = CpuIdResult { + eax: next_dword(&mut brand_string), + ebx: next_dword(&mut brand_string), + ecx: next_dword(&mut brand_string), + edx: next_dword(&mut brand_string), + }; + let brand_string_1 = CpuIdResult { + eax: next_dword(&mut brand_string), + ebx: next_dword(&mut brand_string), + ecx: next_dword(&mut brand_string), + edx: next_dword(&mut brand_string), + }; + let mut brand_string_2 = CpuIdResult { + eax: next_dword(&mut brand_string), + ebx: next_dword(&mut brand_string), + ecx: next_dword(&mut brand_string), + edx: next_dword(&mut brand_string), + }; + + // Mask out the last byte in the brand string section; this makes the string + // null-terminated. + brand_string_2.edx &= 0x00ffffff; + + self.source + .set_leaf(EAX_EXTENDED_BRAND_STRING, Some(brand_string_0)); + self.source + .set_leaf(EAX_EXTENDED_BRAND_STRING + 1, Some(brand_string_1)); + self.source + .set_leaf(EAX_EXTENDED_BRAND_STRING + 2, Some(brand_string_2)); + } else { + self.source.set_leaf(EAX_EXTENDED_BRAND_STRING, None); + self.source.set_leaf(EAX_EXTENDED_BRAND_STRING + 1, None); + self.source.set_leaf(EAX_EXTENDED_BRAND_STRING + 2, None); + } + Ok(()) + } + + /// L1 Instruction Cache Information (LEAF=0x8000_0005) + /// + /// # Platforms + /// ✅ AMD ❌ Intel (reserved) + pub fn set_l1_cache_and_tlb_info(&mut self, leaf: Option) -> Result<(), ()> { + if let Some(leaf) = leaf { + self.source.set_leaf( + EAX_L1_CACHE_INFO, + Some(CpuIdResult { + eax: leaf.eax, + ebx: leaf.ebx, + ecx: leaf.ecx, + edx: leaf.edx, + }), + ); + } else { + self.source.set_leaf(EAX_L1_CACHE_INFO, None); + } + + Ok(()) + } + + /// L2/L3 Cache and TLB Information (LEAF=0x8000_0006). + /// + /// # Platforms + /// ✅ AMD 🟡 Intel + pub fn set_l2_l3_cache_and_tlb_info( + &mut self, + leaf: Option, + ) -> Result<(), ()> { + if let Some(leaf) = leaf { + self.source.set_leaf( + EAX_L2_L3_CACHE_INFO, + Some(CpuIdResult { + eax: leaf.eax, + ebx: leaf.ebx, + ecx: leaf.ecx, + edx: leaf.edx, + }), + ); + } else { + self.source.set_leaf(EAX_L2_L3_CACHE_INFO, None); + } + + Ok(()) + } + + /// Advanced Power Management Information (LEAF=0x8000_0007). + /// + /// # Platforms + /// ✅ AMD 🟡 Intel + pub fn set_advanced_power_mgmt_info(&mut self, leaf: Option) -> Result<(), ()> { + if let Some(leaf) = leaf { + self.source.set_leaf( + EAX_ADVANCED_POWER_MGMT_INFO, + Some(CpuIdResult { + eax: leaf._eax, + ebx: leaf.ebx.bits(), + ecx: leaf.ecx, + edx: leaf.edx.bits(), + }), + ); + } else { + self.source.set_leaf(EAX_ADVANCED_POWER_MGMT_INFO, None); + } + + Ok(()) + } + + /// Processor Capacity Parameters and Extended Feature Identification (LEAF=0x8000_0008). + /// + /// # Platforms + /// ✅ AMD 🟡 Intel + pub fn set_processor_capacity_feature_info( + &mut self, + leaf: Option, + ) -> Result<(), ()> { + if let Some(leaf) = leaf { + self.source.set_leaf( + EAX_PROCESSOR_CAPACITY_INFO, + Some(CpuIdResult { + eax: leaf.eax, + ebx: leaf.ebx.bits(), + ecx: leaf.ecx, + edx: leaf.edx, + }), + ); + } else { + self.source.set_leaf(EAX_PROCESSOR_CAPACITY_INFO, None); + } + + Ok(()) + } + + /// Set information about the SVM features that the processor + /// supports. (LEAF=0x8000_000A) + /// + /// [ExtendedProcessorFeatureIdentifiers::has_svm] is not automatically managed. If removing + /// SVM info, users must clear that feature bit as well. If adding SVM info, users must set + /// that feature bit. + /// + /// # Platforms + /// ✅ AMD ❌ Intel + pub fn set_svm_info(&mut self, leaf: Option) -> Result<(), ()> { + if let Some(leaf) = leaf { + self.source.set_leaf( + EAX_SVM_FEATURES, + Some(CpuIdResult { + eax: leaf.eax, + ebx: leaf.ebx, + ecx: leaf._ecx, + edx: leaf.edx.bits(), + }), + ); + } else { + self.source.set_leaf(EAX_SVM_FEATURES, None); + } + + Ok(()) + } + + /// TLB 1-GiB Pages Information (LEAF=0x8000_0019) + /// + /// # Platforms + /// ✅ AMD ❌ Intel + pub fn set_tlb_1gb_page_info(&mut self, leaf: Option) -> Result<(), ()> { + if let Some(leaf) = leaf { + self.source.set_leaf( + EAX_TLB_1GB_PAGE_INFO, + Some(CpuIdResult { + eax: leaf.eax, + ebx: leaf.ebx, + ecx: leaf._ecx, + edx: leaf._edx, + }), + ); + } else { + self.source.set_leaf(EAX_TLB_1GB_PAGE_INFO, None); + } + + Ok(()) + } + + /// Informations about performance optimization (LEAF=0x8000_001A) + /// + /// # Platforms + /// ✅ AMD ❌ Intel (reserved) + pub fn set_performance_optimization_info( + &mut self, + leaf: Option, + ) -> Result<(), ()> { + if let Some(leaf) = leaf { + self.source.set_leaf( + EAX_PERFORMANCE_OPTIMIZATION_INFO, + Some(CpuIdResult { + eax: leaf.eax.bits(), + ebx: leaf._ebx, + ecx: leaf._ecx, + edx: leaf._edx, + }), + ); + } else { + self.source + .set_leaf(EAX_PERFORMANCE_OPTIMIZATION_INFO, None); + } + + Ok(()) + } + + /// Information about processor cache hierarchy (LEAF=0x8000_001D) + /// + /// # Platforms + /// ✅ AMD ❌ Intel (reserved) + pub fn set_extended_cache_parameters( + &mut self, + levels: Option<&[CpuIdResult]>, + ) -> Result<(), ()> { + self.source.set_leaf(EAX_CACHE_PARAMETERS_AMD, None); + + if let Some(levels) = levels { + for (idx, level) in levels.iter().enumerate() { + use core::convert::TryInto; + let idx = idx + .try_into() + .expect("extended cache parameters subleaf fits into u32"); + self.source + .set_subleaf(EAX_CACHE_PARAMETERS_AMD, idx, Some(*level)); + } + } + + Ok(()) + } + + /// Informations about processor topology (LEAF=0x8000_001E) + /// + /// # Platforms + /// ✅ AMD ❌ Intel (reserved) + pub fn set_processor_topology_info( + &mut self, + leaf: Option, + ) -> Result<(), ()> { + if let Some(leaf) = leaf { + self.source.set_leaf( + EAX_PROCESSOR_TOPOLOGY_INFO, + Some(CpuIdResult { + eax: leaf.eax, + ebx: leaf.ebx, + ecx: leaf.ecx, + edx: leaf._edx, + }), + ); + } else { + self.source.set_leaf(EAX_PROCESSOR_TOPOLOGY_INFO, None); + } + + Ok(()) + } + + /// Informations about memory encryption support (LEAF=0x8000_001F) + /// + /// # Platforms + /// ✅ AMD ❌ Intel (reserved) + pub fn set_memory_encryption_info( + &mut self, + leaf: Option, + ) -> Result<(), ()> { + if let Some(leaf) = leaf { + self.source.set_leaf( + EAX_MEMORY_ENCRYPTION_INFO, + Some(CpuIdResult { + eax: leaf.eax.bits(), + ebx: leaf.ebx, + ecx: leaf.ecx, + edx: leaf.edx, + }), + ); + } else { + self.source.set_leaf(EAX_MEMORY_ENCRYPTION_INFO, None); + } + + Ok(()) + } + + /// Extended Feature Identification 2 (LEAF=0x8000_0021) + /// + /// # Platforms + /// ✅ AMD ❌ Intel (reserved) + pub fn set_extended_feature_identification_2( + &mut self, + leaf: Option, + ) -> Result<(), ()> { + if let Some(leaf) = leaf { + self.source.set_leaf( + EAX_EXTENDED_FEATURE_IDENTIFICATION_2, + Some(CpuIdResult { + eax: leaf.eax.bits(), + ebx: leaf.ebx, + ecx: leaf._ecx, + edx: leaf._edx, + }), + ); + } else { + self.source + .set_leaf(EAX_EXTENDED_FEATURE_IDENTIFICATION_2, None); + } + + Ok(()) + } +} impl CpuId { /// Return new CpuId struct with custom reader function. @@ -336,7 +1071,7 @@ impl CpuId { supported_leafs: vendor_leaf.eax, supported_extended_leafs: extended_leaf.eax, vendor: Vendor::from_vendor_leaf(vendor_leaf), - read: cpuid_fn, + source: cpuid_fn, } } @@ -373,7 +1108,7 @@ impl CpuId { /// ✅ AMD ✅ Intel pub fn get_vendor_info(&self) -> Option { if self.leaf_is_supported(EAX_VENDOR_INFO) { - let res = self.read.cpuid1(EAX_VENDOR_INFO); + let res = self.source.cpuid1(EAX_VENDOR_INFO); Some(VendorInfo { ebx: res.ebx, ecx: res.ecx, @@ -390,7 +1125,7 @@ impl CpuId { /// ✅ AMD ✅ Intel pub fn get_feature_info(&self) -> Option { if self.leaf_is_supported(EAX_FEATURE_INFO) { - let res = self.read.cpuid1(EAX_FEATURE_INFO); + let res = self.source.cpuid1(EAX_FEATURE_INFO); Some(FeatureInfo { vendor: self.vendor, eax: res.eax, @@ -410,7 +1145,7 @@ impl CpuId { /// ❌ AMD ✅ Intel pub fn get_cache_info(&self) -> Option { if self.leaf_is_supported(EAX_CACHE_INFO) { - let res = self.read.cpuid1(EAX_CACHE_INFO); + let res = self.source.cpuid1(EAX_CACHE_INFO); Some(CacheInfoIter { current: 1, eax: res.eax, @@ -430,8 +1165,8 @@ impl CpuId { pub fn get_processor_serial(&self) -> Option { if self.leaf_is_supported(EAX_PROCESSOR_SERIAL) { // upper 64-96 bits are in res1.eax: - let res1 = self.read.cpuid1(EAX_FEATURE_INFO); - let res = self.read.cpuid1(EAX_PROCESSOR_SERIAL); + let res1 = self.source.cpuid1(EAX_FEATURE_INFO); + let res = self.source.cpuid1(EAX_PROCESSOR_SERIAL); Some(ProcessorSerial { ecx: res.ecx, edx: res.edx, @@ -455,7 +1190,7 @@ impl CpuId { || (self.vendor == Vendor::Amd && self.leaf_is_supported(EAX_CACHE_PARAMETERS_AMD)) { Some(CacheParametersIter { - read: self.read.clone(), + source: self.source.clone(), leaf: if self.vendor == Vendor::Amd { EAX_CACHE_PARAMETERS_AMD } else { @@ -474,7 +1209,7 @@ impl CpuId { /// 🟡 AMD ✅ Intel pub fn get_monitor_mwait_info(&self) -> Option { if self.leaf_is_supported(EAX_MONITOR_MWAIT_INFO) { - let res = self.read.cpuid1(EAX_MONITOR_MWAIT_INFO); + let res = self.source.cpuid1(EAX_MONITOR_MWAIT_INFO); Some(MonitorMwaitInfo { eax: res.eax, ebx: res.ebx, @@ -492,7 +1227,7 @@ impl CpuId { /// 🟡 AMD ✅ Intel pub fn get_thermal_power_info(&self) -> Option { if self.leaf_is_supported(EAX_THERMAL_POWER_INFO) { - let res = self.read.cpuid1(EAX_THERMAL_POWER_INFO); + let res = self.source.cpuid1(EAX_THERMAL_POWER_INFO); Some(ThermalPowerInfo { eax: ThermalPowerFeaturesEax::from_bits_truncate(res.eax), ebx: res.ebx, @@ -510,10 +1245,10 @@ impl CpuId { /// 🟡 AMD ✅ Intel pub fn get_extended_feature_info(&self) -> Option { if self.leaf_is_supported(EAX_STRUCTURED_EXTENDED_FEATURE_INFO) { - let res = self.read.cpuid1(EAX_STRUCTURED_EXTENDED_FEATURE_INFO); - let res1 = self.read.cpuid2(EAX_STRUCTURED_EXTENDED_FEATURE_INFO, 1); + let res = self.source.cpuid1(EAX_STRUCTURED_EXTENDED_FEATURE_INFO); + let res1 = self.source.cpuid2(EAX_STRUCTURED_EXTENDED_FEATURE_INFO, 1); Some(ExtendedFeatures { - _eax: res.eax, + eax: res.eax, ebx: ExtendedFeaturesEbx::from_bits_truncate(res.ebx), ecx: ExtendedFeaturesEcx::from_bits_truncate(res.ecx), edx: ExtendedFeaturesEdx::from_bits_truncate(res.edx), @@ -533,7 +1268,7 @@ impl CpuId { /// ❌ AMD ✅ Intel pub fn get_direct_cache_access_info(&self) -> Option { if self.leaf_is_supported(EAX_DIRECT_CACHE_ACCESS_INFO) { - let res = self.read.cpuid1(EAX_DIRECT_CACHE_ACCESS_INFO); + let res = self.source.cpuid1(EAX_DIRECT_CACHE_ACCESS_INFO); Some(DirectCacheAccessInfo { eax: res.eax }) } else { None @@ -546,7 +1281,7 @@ impl CpuId { /// ❌ AMD ✅ Intel pub fn get_performance_monitoring_info(&self) -> Option { if self.leaf_is_supported(EAX_PERFORMANCE_MONITOR_INFO) { - let res = self.read.cpuid1(EAX_PERFORMANCE_MONITOR_INFO); + let res = self.source.cpuid1(EAX_PERFORMANCE_MONITOR_INFO); Some(PerformanceMonitoringInfo { eax: res.eax, ebx: PerformanceMonitoringFeaturesEbx::from_bits_truncate(res.ebx), @@ -569,7 +1304,7 @@ impl CpuId { pub fn get_extended_topology_info(&self) -> Option> { if self.leaf_is_supported(EAX_EXTENDED_TOPOLOGY_INFO) { Some(ExtendedTopologyIter { - read: self.read.clone(), + source: self.source.clone(), level: 0, is_v2: false, }) @@ -585,7 +1320,7 @@ impl CpuId { pub fn get_extended_topology_info_v2(&self) -> Option> { if self.leaf_is_supported(EAX_EXTENDED_TOPOLOGY_INFO_V2) { Some(ExtendedTopologyIter { - read: self.read.clone(), + source: self.source.clone(), level: 0, is_v2: true, }) @@ -600,10 +1335,10 @@ impl CpuId { /// ✅ AMD ✅ Intel pub fn get_extended_state_info(&self) -> Option> { if self.leaf_is_supported(EAX_EXTENDED_STATE_INFO) { - let res = self.read.cpuid2(EAX_EXTENDED_STATE_INFO, 0); - let res1 = self.read.cpuid2(EAX_EXTENDED_STATE_INFO, 1); + let res = self.source.cpuid2(EAX_EXTENDED_STATE_INFO, 0); + let res1 = self.source.cpuid2(EAX_EXTENDED_STATE_INFO, 1); Some(ExtendedStateInfo { - read: self.read.clone(), + source: self.source.clone(), eax: ExtendedStateInfoXCR0Flags::from_bits_truncate(res.eax), ebx: res.ebx, ecx: res.ecx, @@ -623,11 +1358,11 @@ impl CpuId { /// # Platforms /// ❌ AMD ✅ Intel pub fn get_rdt_monitoring_info(&self) -> Option> { - let res = self.read.cpuid1(EAX_RDT_MONITORING); + let res = self.source.cpuid1(EAX_RDT_MONITORING); if self.leaf_is_supported(EAX_RDT_MONITORING) { Some(RdtMonitoringInfo { - read: self.read.clone(), + source: self.source.clone(), ebx: res.ebx, edx: res.edx, }) @@ -641,11 +1376,11 @@ impl CpuId { /// # Platforms /// ❌ AMD ✅ Intel pub fn get_rdt_allocation_info(&self) -> Option> { - let res = self.read.cpuid1(EAX_RDT_ALLOCATION); + let res = self.source.cpuid1(EAX_RDT_ALLOCATION); if self.leaf_is_supported(EAX_RDT_ALLOCATION) { Some(RdtAllocationInfo { - read: self.read.clone(), + source: self.source.clone(), ebx: res.ebx, }) } else { @@ -661,10 +1396,10 @@ impl CpuId { // Leaf 12H sub-leaf 0 (ECX = 0) is supported if CPUID.(EAX=07H, ECX=0H):EBX[SGX] = 1. self.get_extended_feature_info().and_then(|info| { if self.leaf_is_supported(EAX_SGX) && info.has_sgx() { - let res = self.read.cpuid2(EAX_SGX, 0); - let res1 = self.read.cpuid2(EAX_SGX, 1); + let res = self.source.cpuid2(EAX_SGX, 0); + let res1 = self.source.cpuid2(EAX_SGX, 1); Some(SgxInfo { - read: self.read.clone(), + source: self.source.clone(), eax: res.eax, ebx: res.ebx, _ecx: res.ecx, @@ -686,9 +1421,9 @@ impl CpuId { /// ❌ AMD ✅ Intel pub fn get_processor_trace_info(&self) -> Option { if self.leaf_is_supported(EAX_TRACE_INFO) { - let res = self.read.cpuid2(EAX_TRACE_INFO, 0); + let res = self.source.cpuid2(EAX_TRACE_INFO, 0); let res1 = if res.eax >= 1 { - Some(self.read.cpuid2(EAX_TRACE_INFO, 1)) + Some(self.source.cpuid2(EAX_TRACE_INFO, 1)) } else { None }; @@ -711,7 +1446,7 @@ impl CpuId { /// ❌ AMD ✅ Intel pub fn get_tsc_info(&self) -> Option { if self.leaf_is_supported(EAX_TIME_STAMP_COUNTER_INFO) { - let res = self.read.cpuid2(EAX_TIME_STAMP_COUNTER_INFO, 0); + let res = self.source.cpuid2(EAX_TIME_STAMP_COUNTER_INFO, 0); Some(TscInfo { eax: res.eax, ebx: res.ebx, @@ -728,7 +1463,7 @@ impl CpuId { /// ❌ AMD ✅ Intel pub fn get_processor_frequency_info(&self) -> Option { if self.leaf_is_supported(EAX_FREQUENCY_INFO) { - let res = self.read.cpuid1(EAX_FREQUENCY_INFO); + let res = self.source.cpuid1(EAX_FREQUENCY_INFO); Some(ProcessorFrequencyInfo { eax: res.eax, ebx: res.ebx, @@ -745,9 +1480,9 @@ impl CpuId { /// ❌ AMD ✅ Intel pub fn get_soc_vendor_info(&self) -> Option> { if self.leaf_is_supported(EAX_SOC_VENDOR_INFO) { - let res = self.read.cpuid1(EAX_SOC_VENDOR_INFO); + let res = self.source.cpuid1(EAX_SOC_VENDOR_INFO); Some(SoCVendorInfo { - read: self.read.clone(), + source: self.source.clone(), eax: res.eax, ebx: res.ebx, ecx: res.ecx, @@ -765,10 +1500,10 @@ impl CpuId { pub fn get_deterministic_address_translation_info(&self) -> Option> { if self.leaf_is_supported(EAX_DETERMINISTIC_ADDRESS_TRANSLATION_INFO) { let res = self - .read + .source .cpuid2(EAX_DETERMINISTIC_ADDRESS_TRANSLATION_INFO, 0); Some(DatIter { - read: self.read.clone(), + source: self.source.clone(), current: 0, count: res.eax, }) @@ -788,10 +1523,10 @@ impl CpuId { self.get_feature_info() .filter(|fi| fi.has_hypervisor()) .and_then(|_| { - let res = self.read.cpuid1(EAX_HYPERVISOR_INFO); + let res = self.source.cpuid1(EAX_HYPERVISOR_INFO); if res.eax > 0 { Some(HypervisorInfo { - read: self.read.clone(), + source: self.source.clone(), res, }) } else { @@ -810,7 +1545,7 @@ impl CpuId { if self.leaf_is_supported(EAX_EXTENDED_PROCESSOR_AND_FEATURE_IDENTIFIERS) { Some(ExtendedProcessorFeatureIdentifiers::new( self.vendor, - self.read + self.source .cpuid1(EAX_EXTENDED_PROCESSOR_AND_FEATURE_IDENTIFIERS), )) } else { @@ -828,9 +1563,9 @@ impl CpuId { && self.leaf_is_supported(EAX_EXTENDED_BRAND_STRING + 2) { Some(ProcessorBrandString::new([ - self.read.cpuid1(EAX_EXTENDED_BRAND_STRING), - self.read.cpuid1(EAX_EXTENDED_BRAND_STRING + 1), - self.read.cpuid1(EAX_EXTENDED_BRAND_STRING + 2), + self.source.cpuid1(EAX_EXTENDED_BRAND_STRING), + self.source.cpuid1(EAX_EXTENDED_BRAND_STRING + 1), + self.source.cpuid1(EAX_EXTENDED_BRAND_STRING + 2), ])) } else { None @@ -843,7 +1578,7 @@ impl CpuId { /// ✅ AMD ❌ Intel (reserved) pub fn get_l1_cache_and_tlb_info(&self) -> Option { if self.vendor == Vendor::Amd && self.leaf_is_supported(EAX_L1_CACHE_INFO) { - Some(L1CacheTlbInfo::new(self.read.cpuid1(EAX_L1_CACHE_INFO))) + Some(L1CacheTlbInfo::new(self.source.cpuid1(EAX_L1_CACHE_INFO))) } else { None } @@ -856,7 +1591,7 @@ impl CpuId { pub fn get_l2_l3_cache_and_tlb_info(&self) -> Option { if self.leaf_is_supported(EAX_L2_L3_CACHE_INFO) { Some(L2And3CacheTlbInfo::new( - self.read.cpuid1(EAX_L2_L3_CACHE_INFO), + self.source.cpuid1(EAX_L2_L3_CACHE_INFO), )) } else { None @@ -869,7 +1604,9 @@ impl CpuId { /// ✅ AMD 🟡 Intel pub fn get_advanced_power_mgmt_info(&self) -> Option { if self.leaf_is_supported(EAX_ADVANCED_POWER_MGMT_INFO) { - Some(ApmInfo::new(self.read.cpuid1(EAX_ADVANCED_POWER_MGMT_INFO))) + Some(ApmInfo::new( + self.source.cpuid1(EAX_ADVANCED_POWER_MGMT_INFO), + )) } else { None } @@ -882,14 +1619,14 @@ impl CpuId { pub fn get_processor_capacity_feature_info(&self) -> Option { if self.leaf_is_supported(EAX_PROCESSOR_CAPACITY_INFO) { Some(ProcessorCapacityAndFeatureInfo::new( - self.read.cpuid1(EAX_PROCESSOR_CAPACITY_INFO), + self.source.cpuid1(EAX_PROCESSOR_CAPACITY_INFO), )) } else { None } } - /// This function provides information about the SVM features that the processory + /// This function provides information about the SVM features that the processor /// supports. (LEAF=0x8000_000A) /// /// If SVM is not supported if [ExtendedProcessorFeatureIdentifiers::has_svm] is @@ -902,7 +1639,7 @@ impl CpuId { .get_extended_processor_and_feature_identifiers() .map_or(false, |f| f.has_svm()); if has_svm && self.leaf_is_supported(EAX_SVM_FEATURES) { - Some(SvmFeatures::new(self.read.cpuid1(EAX_SVM_FEATURES))) + Some(SvmFeatures::new(self.source.cpuid1(EAX_SVM_FEATURES))) } else { None } @@ -914,7 +1651,9 @@ impl CpuId { /// ✅ AMD ❌ Intel pub fn get_tlb_1gb_page_info(&self) -> Option { if self.leaf_is_supported(EAX_TLB_1GB_PAGE_INFO) { - Some(Tlb1gbPageInfo::new(self.read.cpuid1(EAX_TLB_1GB_PAGE_INFO))) + Some(Tlb1gbPageInfo::new( + self.source.cpuid1(EAX_TLB_1GB_PAGE_INFO), + )) } else { None } @@ -927,7 +1666,7 @@ impl CpuId { pub fn get_performance_optimization_info(&self) -> Option { if self.leaf_is_supported(EAX_PERFORMANCE_OPTIMIZATION_INFO) { Some(PerformanceOptimizationInfo::new( - self.read.cpuid1(EAX_PERFORMANCE_OPTIMIZATION_INFO), + self.source.cpuid1(EAX_PERFORMANCE_OPTIMIZATION_INFO), )) } else { None @@ -941,7 +1680,7 @@ impl CpuId { pub fn get_processor_topology_info(&self) -> Option { if self.leaf_is_supported(EAX_PROCESSOR_TOPOLOGY_INFO) { Some(ProcessorTopologyInfo::new( - self.read.cpuid1(EAX_PROCESSOR_TOPOLOGY_INFO), + self.source.cpuid1(EAX_PROCESSOR_TOPOLOGY_INFO), )) } else { None @@ -955,12 +1694,84 @@ impl CpuId { pub fn get_memory_encryption_info(&self) -> Option { if self.leaf_is_supported(EAX_MEMORY_ENCRYPTION_INFO) { Some(MemoryEncryptionInfo::new( - self.read.cpuid1(EAX_MEMORY_ENCRYPTION_INFO), + self.source.cpuid1(EAX_MEMORY_ENCRYPTION_INFO), + )) + } else { + None + } + } + + /// Informations about Platform Quality of Service (LEAF=0x8000_0020) + /// + /// # Platforms + /// ✅ AMD ❌ Intel (reserved) + pub fn get_pqos_extended_feature_info(&self) -> Option> { + if self.leaf_is_supported(EAX_PQOS_EXTENDED_FEATURES) { + Some(PqosExtendedFeatureInfo::new(self.source.clone())) + } else { + None + } + } + + /// Extended Feature Identification 2 (LEAF=0x8000_0021) + /// + /// # Platforms + /// ✅ AMD ❌ Intel (reserved) + pub fn get_extended_feature_identification_2(&self) -> Option { + if self.leaf_is_supported(EAX_EXTENDED_FEATURE_IDENTIFICATION_2) { + Some(ExtendedFeatureIdentification2::new( + self.source.cpuid1(EAX_EXTENDED_FEATURE_IDENTIFICATION_2), + )) + } else { + None + } + } + + /// Extended Performance Monitoring and Debug (LEAF=0x8000_0022) + /// + /// # Platforms + /// ✅ AMD ❌ Intel (reserved) + pub fn get_extended_performance_monitoring_and_debug( + &self, + ) -> Option { + if self.leaf_is_supported(EAX_EXTENDED_PERFORMANCE_MONITORING_AND_DEBUG) { + Some(ExtendedPerformanceMonitoringDebug::new( + self.source + .cpuid1(EAX_EXTENDED_PERFORMANCE_MONITORING_AND_DEBUG), + )) + } else { + None + } + } + + /// Multi-Key Encrypted Memory Capabilities (LEAF=0x8000_0023) + /// + /// # Platforms + /// ✅ AMD ❌ Intel (reserved) + pub fn get_multi_key_encrypted_memory_capabilities( + &self, + ) -> Option { + if self.leaf_is_supported(EAX_MULTI_KEY_ENCRYPTED_MEMORY_CAPABILITIES) { + Some(MultiKeyEncryptedMemoryCapabilities::new( + self.source + .cpuid1(EAX_MULTI_KEY_ENCRYPTED_MEMORY_CAPABILITIES), )) } else { None } } + + /// Extended CPU Topolog (LEAF=0x8000_0026) + /// + /// # Platforms + /// ✅ AMD ❌ Intel (reserved) + pub fn get_extended_cpu_topology(&self) -> Option> { + if self.leaf_is_supported(EAX_EXTENDED_CPU_TOPOLOGY) { + Some(ExtendedCpuTopologyIter::new(self.source.clone())) + } else { + None + } + } } impl Debug for CpuId { @@ -1046,7 +1857,7 @@ impl Debug for CpuId { /// /// # Platforms /// ✅ AMD ✅ Intel -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Copy, Clone)] #[repr(C)] pub struct VendorInfo { ebx: u32, @@ -1055,6 +1866,14 @@ pub struct VendorInfo { } impl VendorInfo { + pub fn amd() -> Self { + Self { + ebx: 0x68747541, + ecx: 0x444D4163, + edx: 0x69746E65, + } + } + /// Return vendor identification as human readable string. pub fn as_str(&self) -> &str { let brand_string_start = self as *const VendorInfo as *const u8; @@ -1838,26 +2657,51 @@ pub struct FeatureInfo { } impl FeatureInfo { + pub fn new(vendor: Vendor) -> Self { + Self { + vendor, + eax: 0, + ebx: 0, + edx_ecx: FeatureInfoFlags::from_bits_truncate(0), + } + } + /// Version Information: Extended Family pub fn extended_family_id(&self) -> u8 { get_bits(self.eax, 20, 27) as u8 } + pub fn set_extended_family_id(&mut self, bits: u32) { + set_bits(&mut self.eax, bits, 20, 27) + } + /// Version Information: Extended Model pub fn extended_model_id(&self) -> u8 { get_bits(self.eax, 16, 19) as u8 } + pub fn set_extended_model_id(&mut self, bits: u32) { + set_bits(&mut self.eax, bits, 16, 19) + } + /// Version Information: Family pub fn base_family_id(&self) -> u8 { get_bits(self.eax, 8, 11) as u8 } + pub fn set_base_family_id(&mut self, bits: u32) { + set_bits(&mut self.eax, bits, 8, 11) + } + /// Version Information: Model pub fn base_model_id(&self) -> u8 { get_bits(self.eax, 4, 7) as u8 } + pub fn set_base_model_id(&mut self, bits: u32) { + set_bits(&mut self.eax, bits, 4, 7) + } + pub fn family_id(&self) -> u8 { let base_family_id = self.base_family_id(); let extended_family_id = self.extended_family_id(); @@ -1890,30 +2734,51 @@ impl FeatureInfo { get_bits(self.eax, 0, 3) as u8 } + pub fn set_stepping_id(&mut self, bits: u32) { + set_bits(&mut self.eax, bits, 0, 3) + } + /// Brand Index pub fn brand_index(&self) -> u8 { get_bits(self.ebx, 0, 7) as u8 } + pub fn set_brand_index(&mut self, bits: u32) { + set_bits(&mut self.ebx, bits, 0, 7) + } + /// CLFLUSH line size (Value ∗ 8 = cache line size in bytes) pub fn cflush_cache_line_size(&self) -> u8 { get_bits(self.ebx, 8, 15) as u8 } + pub fn set_cflush_cache_line_size(&mut self, bits: u32) { + set_bits(&mut self.ebx, bits, 8, 15) + } + /// Initial APIC ID pub fn initial_local_apic_id(&self) -> u8 { get_bits(self.ebx, 24, 31) as u8 } + pub fn set_initial_local_apic_id(&mut self, bits: u32) { + set_bits(&mut self.ebx, bits, 24, 31) + } + /// Maximum number of addressable IDs for logical processors in this physical package. pub fn max_logical_processor_ids(&self) -> u8 { get_bits(self.ebx, 16, 23) as u8 } + pub fn set_max_logical_processor_ids(&mut self, bits: u32) { + set_bits(&mut self.ebx, bits, 16, 23) + } + check_flag!( doc = "Streaming SIMD Extensions 3 (SSE3). A value of 1 indicates the processor \ supports this technology.", has_sse3, + set_sse3, edx_ecx, FeatureInfoFlags::SSE3 ); @@ -1922,6 +2787,7 @@ impl FeatureInfo { doc = "PCLMULQDQ. A value of 1 indicates the processor supports the PCLMULQDQ \ instruction", has_pclmulqdq, + set_pclmulqdq, edx_ecx, FeatureInfoFlags::PCLMULQDQ ); @@ -1930,6 +2796,7 @@ impl FeatureInfo { doc = "64-bit DS Area. A value of 1 indicates the processor supports DS area \ using 64-bit layout", has_ds_area, + set_ds_area, edx_ecx, FeatureInfoFlags::DTES64 ); @@ -1937,6 +2804,7 @@ impl FeatureInfo { check_flag!( doc = "MONITOR/MWAIT. A value of 1 indicates the processor supports this feature.", has_monitor_mwait, + set_monitor_mwait, edx_ecx, FeatureInfoFlags::MONITOR ); @@ -1946,6 +2814,7 @@ impl FeatureInfo { the extensions to the Debug Store feature to allow for branch message \ storage qualified by CPL.", has_cpl, + set_cpl, edx_ecx, FeatureInfoFlags::DSCPL ); @@ -1954,6 +2823,7 @@ impl FeatureInfo { doc = "Virtual Machine Extensions. A value of 1 indicates that the processor \ supports this technology.", has_vmx, + set_vmx, edx_ecx, FeatureInfoFlags::VMX ); @@ -1962,6 +2832,7 @@ impl FeatureInfo { doc = "Safer Mode Extensions. A value of 1 indicates that the processor supports \ this technology. See Chapter 5, Safer Mode Extensions Reference.", has_smx, + set_smx, edx_ecx, FeatureInfoFlags::SMX ); @@ -1970,6 +2841,7 @@ impl FeatureInfo { doc = "Enhanced Intel SpeedStep® technology. A value of 1 indicates that the \ processor supports this technology.", has_eist, + set_eist, edx_ecx, FeatureInfoFlags::EIST ); @@ -1978,6 +2850,7 @@ impl FeatureInfo { doc = "Thermal Monitor 2. A value of 1 indicates whether the processor supports \ this technology.", has_tm2, + set_tm2, edx_ecx, FeatureInfoFlags::TM2 ); @@ -1987,6 +2860,7 @@ impl FeatureInfo { Extensions 3 (SSSE3). A value of 0 indicates the instruction extensions \ are not present in the processor", has_ssse3, + set_ssse3, edx_ecx, FeatureInfoFlags::SSSE3 ); @@ -1997,6 +2871,7 @@ impl FeatureInfo { feature is not supported. See definition of the IA32_MISC_ENABLE MSR Bit \ 24 (L1 Data Cache Context Mode) for details.", has_cnxtid, + set_cnxtid, edx_ecx, FeatureInfoFlags::CNXTID ); @@ -2005,6 +2880,7 @@ impl FeatureInfo { doc = "A value of 1 indicates the processor supports FMA extensions using YMM \ state.", has_fma, + set_fma, edx_ecx, FeatureInfoFlags::FMA ); @@ -2014,6 +2890,7 @@ impl FeatureInfo { available. See the CMPXCHG8B/CMPXCHG16B Compare and Exchange Bytes \ section. 14", has_cmpxchg16b, + set_cmpxchg16b, edx_ecx, FeatureInfoFlags::CMPXCHG16B ); @@ -2023,6 +2900,7 @@ impl FeatureInfo { supports the performance and debug feature indication MSR \ IA32_PERF_CAPABILITIES.", has_pdcm, + set_pdcm, edx_ecx, FeatureInfoFlags::PDCM ); @@ -2031,6 +2909,7 @@ impl FeatureInfo { doc = "Process-context identifiers. A value of 1 indicates that the processor \ supports PCIDs and the software may set CR4.PCIDE to 1.", has_pcid, + set_pcid, edx_ecx, FeatureInfoFlags::PCID ); @@ -2039,6 +2918,7 @@ impl FeatureInfo { doc = "A value of 1 indicates the processor supports the ability to prefetch \ data from a memory mapped device.", has_dca, + set_dca, edx_ecx, FeatureInfoFlags::DCA ); @@ -2046,6 +2926,7 @@ impl FeatureInfo { check_flag!( doc = "A value of 1 indicates that the processor supports SSE4.1.", has_sse41, + set_sse41, edx_ecx, FeatureInfoFlags::SSE41 ); @@ -2053,6 +2934,7 @@ impl FeatureInfo { check_flag!( doc = "A value of 1 indicates that the processor supports SSE4.2.", has_sse42, + set_sse42, edx_ecx, FeatureInfoFlags::SSE42 ); @@ -2060,6 +2942,7 @@ impl FeatureInfo { check_flag!( doc = "A value of 1 indicates that the processor supports x2APIC feature.", has_x2apic, + set_x2apic, edx_ecx, FeatureInfoFlags::X2APIC ); @@ -2067,6 +2950,7 @@ impl FeatureInfo { check_flag!( doc = "A value of 1 indicates that the processor supports MOVBE instruction.", has_movbe, + set_movbe, edx_ecx, FeatureInfoFlags::MOVBE ); @@ -2074,6 +2958,7 @@ impl FeatureInfo { check_flag!( doc = "A value of 1 indicates that the processor supports the POPCNT instruction.", has_popcnt, + set_popcnt, edx_ecx, FeatureInfoFlags::POPCNT ); @@ -2082,6 +2967,7 @@ impl FeatureInfo { doc = "A value of 1 indicates that the processors local APIC timer supports \ one-shot operation using a TSC deadline value.", has_tsc_deadline, + set_tsc_deadline, edx_ecx, FeatureInfoFlags::TSC_DEADLINE ); @@ -2090,6 +2976,7 @@ impl FeatureInfo { doc = "A value of 1 indicates that the processor supports the AESNI instruction \ extensions.", has_aesni, + set_aesni, edx_ecx, FeatureInfoFlags::AESNI ); @@ -2099,6 +2986,7 @@ impl FeatureInfo { processor extended states feature, the XSETBV/XGETBV instructions, and \ XCR0.", has_xsave, + set_xsave, edx_ecx, FeatureInfoFlags::XSAVE ); @@ -2108,6 +2996,7 @@ impl FeatureInfo { to access XCR0, and support for processor extended state management using \ XSAVE/XRSTOR.", has_oxsave, + set_oxsave, edx_ecx, FeatureInfoFlags::OSXSAVE ); @@ -2116,6 +3005,7 @@ impl FeatureInfo { doc = "A value of 1 indicates the processor supports the AVX instruction \ extensions.", has_avx, + set_avx, edx_ecx, FeatureInfoFlags::AVX ); @@ -2124,6 +3014,7 @@ impl FeatureInfo { doc = "A value of 1 indicates that processor supports 16-bit floating-point \ conversion instructions.", has_f16c, + set_f16c, edx_ecx, FeatureInfoFlags::F16C ); @@ -2131,6 +3022,7 @@ impl FeatureInfo { check_flag!( doc = "A value of 1 indicates that processor supports RDRAND instruction.", has_rdrand, + set_rdrand, edx_ecx, FeatureInfoFlags::RDRAND ); @@ -2138,6 +3030,7 @@ impl FeatureInfo { check_flag!( doc = "A value of 1 indicates the indicates the presence of a hypervisor.", has_hypervisor, + set_hypervisor, edx_ecx, FeatureInfoFlags::HYPERVISOR ); @@ -2145,6 +3038,7 @@ impl FeatureInfo { check_flag!( doc = "Floating Point Unit On-Chip. The processor contains an x87 FPU.", has_fpu, + set_fpu, edx_ecx, FeatureInfoFlags::FPU ); @@ -2155,6 +3049,7 @@ impl FeatureInfo { interrupts, software interrupt indirection, expansion of the TSS with the \ software indirection bitmap, and EFLAGS.VIF and EFLAGS.VIP flags.", has_vme, + set_vme, edx_ecx, FeatureInfoFlags::VME ); @@ -2163,6 +3058,7 @@ impl FeatureInfo { doc = "Debugging Extensions. Support for I/O breakpoints, including CR4.DE for \ controlling the feature, and optional trapping of accesses to DR4 and DR5.", has_de, + set_de, edx_ecx, FeatureInfoFlags::DE ); @@ -2172,6 +3068,7 @@ impl FeatureInfo { CR4.PSE for controlling the feature, the defined dirty bit in PDE (Page \ Directory Entries), optional reserved bit trapping in CR3, PDEs, and PTEs.", has_pse, + set_pse, edx_ecx, FeatureInfoFlags::PSE ); @@ -2180,6 +3077,7 @@ impl FeatureInfo { doc = "Time Stamp Counter. The RDTSC instruction is supported, including CR4.TSD \ for controlling privilege.", has_tsc, + set_tsc, edx_ecx, FeatureInfoFlags::TSC ); @@ -2189,6 +3087,7 @@ impl FeatureInfo { WRMSR instructions are supported. Some of the MSRs are implementation \ dependent.", has_msr, + set_msr, edx_ecx, FeatureInfoFlags::MSR ); @@ -2199,6 +3098,7 @@ impl FeatureInfo { translation tables is defined, 2-MByte pages are supported instead of 4 \ Mbyte pages if PAE bit is 1.", has_pae, + set_pae, edx_ecx, FeatureInfoFlags::PAE ); @@ -2211,6 +3111,7 @@ impl FeatureInfo { have to depend on processor version to do model specific processing of \ the exception, or test for the presence of the Machine Check feature.", has_mce, + set_mce, edx_ecx, FeatureInfoFlags::MCE ); @@ -2219,6 +3120,7 @@ impl FeatureInfo { doc = "CMPXCHG8B Instruction. The compare-and-exchange 8 bytes (64 bits) \ instruction is supported (implicitly locked and atomic).", has_cmpxchg8b, + set_cmpxchg8b, edx_ecx, FeatureInfoFlags::CX8 ); @@ -2229,6 +3131,7 @@ impl FeatureInfo { address range FFFE0000H to FFFE0FFFH (by default - some processors permit \ the APIC to be relocated).", has_apic, + set_apic, edx_ecx, FeatureInfoFlags::APIC ); @@ -2237,6 +3140,7 @@ impl FeatureInfo { doc = "SYSENTER and SYSEXIT Instructions. The SYSENTER and SYSEXIT and \ associated MSRs are supported.", has_sysenter_sysexit, + set_sysenter_sysexit, edx_ecx, FeatureInfoFlags::SEP ); @@ -2246,6 +3150,7 @@ impl FeatureInfo { contains feature bits that describe what memory types are supported, how \ many variable MTRRs are supported, and whether fixed MTRRs are supported.", has_mtrr, + set_mtrr, edx_ecx, FeatureInfoFlags::MTRR ); @@ -2255,6 +3160,7 @@ impl FeatureInfo { that map a page, indicating TLB entries that are common to different \ processes and need not be flushed. The CR4.PGE bit controls this feature.", has_pge, + set_pge, edx_ecx, FeatureInfoFlags::PGE ); @@ -2265,6 +3171,7 @@ impl FeatureInfo { contains feature bits describing how many banks of error reporting MSRs \ are supported.", has_mca, + set_mca, edx_ecx, FeatureInfoFlags::MCA ); @@ -2274,6 +3181,7 @@ impl FeatureInfo { supported. In addition, if x87 FPU is present as indicated by the \ CPUID.FPU feature bit, then the FCOMI and FCMOV instructions are supported", has_cmov, + set_cmov, edx_ecx, FeatureInfoFlags::CMOV ); @@ -2284,6 +3192,7 @@ impl FeatureInfo { system to specify attributes of memory accessed through a linear address \ on a 4KB granularity.", has_pat, + set_pat, edx_ecx, FeatureInfoFlags::PAT ); @@ -2295,6 +3204,7 @@ impl FeatureInfo { bits 20:13 of the page-directory entry. Such physical addresses are \ limited by MAXPHYADDR and may be up to 40 bits in size.", has_pse36, + set_pse36, edx_ecx, FeatureInfoFlags::PSE36 ); @@ -2303,6 +3213,7 @@ impl FeatureInfo { doc = "Processor Serial Number. The processor supports the 96-bit processor \ identification number feature and the feature is enabled.", has_psn, + set_psn, edx_ecx, FeatureInfoFlags::PSN ); @@ -2310,6 +3221,7 @@ impl FeatureInfo { check_flag!( doc = "CLFLUSH Instruction. CLFLUSH Instruction is supported.", has_clflush, + set_clflush, edx_ecx, FeatureInfoFlags::CLFSH ); @@ -2322,6 +3234,7 @@ impl FeatureInfo { in the Intel® 64 and IA-32 Architectures Software Developers Manual, \ Volume 3C).", has_ds, + set_ds, edx_ecx, FeatureInfoFlags::DS ); @@ -2332,6 +3245,7 @@ impl FeatureInfo { and processor performance to be modulated in predefined duty cycles under \ software control.", has_acpi, + set_acpi, edx_ecx, FeatureInfoFlags::ACPI ); @@ -2339,6 +3253,7 @@ impl FeatureInfo { check_flag!( doc = "Intel MMX Technology. The processor supports the Intel MMX technology.", has_mmx, + set_mmx, edx_ecx, FeatureInfoFlags::MMX ); @@ -2350,6 +3265,7 @@ impl FeatureInfo { operating system to indicate that it supports the FXSAVE and FXRSTOR \ instructions.", has_fxsave_fxstor, + set_fxsave_fxstor, edx_ecx, FeatureInfoFlags::FXSR ); @@ -2357,6 +3273,7 @@ impl FeatureInfo { check_flag!( doc = "SSE. The processor supports the SSE extensions.", has_sse, + set_sse, edx_ecx, FeatureInfoFlags::SSE ); @@ -2364,6 +3281,7 @@ impl FeatureInfo { check_flag!( doc = "SSE2. The processor supports the SSE2 extensions.", has_sse2, + set_sse2, edx_ecx, FeatureInfoFlags::SSE2 ); @@ -2373,6 +3291,7 @@ impl FeatureInfo { types by performing a snoop of its own cache structure for transactions \ issued to the bus.", has_ss, + set_ss, edx_ecx, FeatureInfoFlags::SS ); @@ -2385,6 +3304,7 @@ impl FeatureInfo { addressable IDs for logical processors in this package) is valid for the \ package.", has_htt, + set_htt, edx_ecx, FeatureInfoFlags::HTT ); @@ -2393,6 +3313,7 @@ impl FeatureInfo { doc = "Thermal Monitor. The processor implements the thermal monitor automatic \ thermal control circuitry (TCC).", has_tm, + set_tm, edx_ecx, FeatureInfoFlags::TM ); @@ -2404,6 +3325,7 @@ impl FeatureInfo { processor should return to normal operation to handle the interrupt. Bit \ 10 (PBE enable) in the IA32_MISC_ENABLE MSR enables this capability.", has_pbe, + set_pbe, edx_ecx, FeatureInfoFlags::PBE ); @@ -2566,7 +3488,7 @@ bitflags! { /// 🟡 AMD ✅ Intel #[derive(Clone, Copy)] pub struct CacheParametersIter { - read: R, + source: R, leaf: u32, current: u32, } @@ -2580,7 +3502,7 @@ impl Iterator for CacheParametersIter { /// cpuid is called every-time we advance the iterator to get information /// about the next cache. fn next(&mut self) -> Option { - let res = self.read.cpuid2(self.leaf, self.current); + let res = self.source.cpuid2(self.leaf, self.current); let cp = CacheParameter { eax: res.eax, ebx: res.ebx, @@ -2802,6 +3724,15 @@ pub struct MonitorMwaitInfo { } impl MonitorMwaitInfo { + pub fn empty() -> Self { + Self { + eax: 0, + ebx: 0, + ecx: 0, + edx: 0, + } + } + /// Smallest monitor-line size in bytes (default is processor's monitor granularity) /// /// # Platforms @@ -2810,7 +3741,11 @@ impl MonitorMwaitInfo { get_bits(self.eax, 0, 15) as u16 } - /// Largest monitor-line size in bytes (default is processor's monitor granularity + pub fn set_smallest_monitor_line(&mut self, bits: u32) { + set_bits(&mut self.eax, bits, 0, 15) + } + + /// Largest monitor-line size in bytes (default is processor's monitor granularity) /// /// # Platforms /// ✅ AMD ✅ Intel @@ -2818,6 +3753,10 @@ impl MonitorMwaitInfo { get_bits(self.ebx, 0, 15) as u16 } + pub fn set_largest_monitor_line(&mut self, bits: u32) { + set_bits(&mut self.ebx, bits, 0, 15) + } + /// Enumeration of Monitor-Mwait extensions (beyond EAX and EBX registers) supported /// /// # Platforms @@ -2826,6 +3765,10 @@ impl MonitorMwaitInfo { get_bits(self.ecx, 0, 0) == 1 } + pub fn set_extensions_supported(&mut self, bits: u32) { + set_bits(&mut self.ecx, bits, 0, 0) + } + /// Supports treating interrupts as break-event for MWAIT, even when interrupts disabled /// /// # Platforms @@ -2834,6 +3777,10 @@ impl MonitorMwaitInfo { get_bits(self.ecx, 1, 1) == 1 } + pub fn set_interrupts_as_break_event(&mut self, bits: u32) { + set_bits(&mut self.ecx, bits, 1, 1) + } + /// Number of C0 sub C-states supported using MWAIT (Bits 03 - 00) /// /// # Platforms @@ -2842,6 +3789,10 @@ impl MonitorMwaitInfo { get_bits(self.edx, 0, 3) as u16 } + pub fn set_supported_c0_states(&mut self, bits: u32) { + set_bits(&mut self.edx, bits, 0, 3) + } + /// Number of C1 sub C-states supported using MWAIT (Bits 07 - 04) /// /// # Platforms @@ -2850,6 +3801,10 @@ impl MonitorMwaitInfo { get_bits(self.edx, 4, 7) as u16 } + pub fn set_supported_c1_states(&mut self, bits: u32) { + set_bits(&mut self.edx, bits, 4, 7) + } + /// Number of C2 sub C-states supported using MWAIT (Bits 11 - 08) /// /// # Platforms @@ -2858,6 +3813,10 @@ impl MonitorMwaitInfo { get_bits(self.edx, 8, 11) as u16 } + pub fn set_supported_c2_states(&mut self, bits: u32) { + set_bits(&mut self.edx, bits, 8, 11) + } + /// Number of C3 sub C-states supported using MWAIT (Bits 15 - 12) /// /// # Platforms @@ -2866,6 +3825,10 @@ impl MonitorMwaitInfo { get_bits(self.edx, 12, 15) as u16 } + pub fn set_supported_c3_states(&mut self, bits: u32) { + set_bits(&mut self.edx, bits, 12, 15) + } + /// Number of C4 sub C-states supported using MWAIT (Bits 19 - 16) /// /// # Platforms @@ -2874,6 +3837,10 @@ impl MonitorMwaitInfo { get_bits(self.edx, 16, 19) as u16 } + pub fn set_supported_c4_states(&mut self, bits: u32) { + set_bits(&mut self.edx, bits, 16, 19) + } + /// Number of C5 sub C-states supported using MWAIT (Bits 23 - 20) /// /// # Platforms @@ -2882,6 +3849,10 @@ impl MonitorMwaitInfo { get_bits(self.edx, 20, 23) as u16 } + pub fn set_supported_c5_states(&mut self, bits: u32) { + set_bits(&mut self.edx, bits, 20, 23) + } + /// Number of C6 sub C-states supported using MWAIT (Bits 27 - 24) /// /// # Platforms @@ -2890,6 +3861,10 @@ impl MonitorMwaitInfo { get_bits(self.edx, 24, 27) as u16 } + pub fn set_supported_c6_states(&mut self, bits: u32) { + set_bits(&mut self.edx, bits, 24, 27) + } + /// Number of C7 sub C-states supported using MWAIT (Bits 31 - 28) /// /// # Platforms @@ -2897,6 +3872,10 @@ impl MonitorMwaitInfo { pub fn supported_c7_states(&self) -> u16 { get_bits(self.edx, 28, 31) as u16 } + + pub fn set_supported_c7_states(&mut self, bits: u32) { + set_bits(&mut self.edx, bits, 28, 31) + } } impl Debug for MonitorMwaitInfo { @@ -2933,6 +3912,15 @@ pub struct ThermalPowerInfo { } impl ThermalPowerInfo { + pub fn empty() -> Self { + Self { + eax: ThermalPowerFeaturesEax::from_bits_truncate(0), + ebx: 0, + ecx: ThermalPowerFeaturesEcx::from_bits_truncate(0), + _edx: 0, + } + } + /// Number of Interrupt Thresholds in Digital Thermal Sensor /// /// # Platforms @@ -2941,6 +3929,10 @@ impl ThermalPowerInfo { get_bits(self.ebx, 0, 3) as u8 } + pub fn set_dts_irq_threshold(&mut self, bits: u32) { + set_bits(&mut self.ebx, bits, 0, 3) + } + /// Digital temperature sensor is supported if set. /// /// # Platforms @@ -2949,6 +3941,11 @@ impl ThermalPowerInfo { self.eax.contains(ThermalPowerFeaturesEax::DTS) } + pub fn set_dts(&mut self, bit: bool) -> &mut Self { + self.eax.set(ThermalPowerFeaturesEax::DTS, bit); + self + } + /// Intel Turbo Boost Technology Available (see description of /// IA32_MISC_ENABLE\[38\]). /// @@ -2958,6 +3955,11 @@ impl ThermalPowerInfo { self.eax.contains(ThermalPowerFeaturesEax::TURBO_BOOST) } + pub fn set_turbo_boost(&mut self, bit: bool) -> &mut Self { + self.eax.set(ThermalPowerFeaturesEax::TURBO_BOOST, bit); + self + } + /// ARAT. APIC-Timer-always-running feature is supported if set. /// /// # Platforms @@ -2966,6 +3968,11 @@ impl ThermalPowerInfo { self.eax.contains(ThermalPowerFeaturesEax::ARAT) } + pub fn set_arat(&mut self, bit: bool) -> &mut Self { + self.eax.set(ThermalPowerFeaturesEax::ARAT, bit); + self + } + /// PLN. Power limit notification controls are supported if set. /// /// # Platforms @@ -2974,6 +3981,11 @@ impl ThermalPowerInfo { self.eax.contains(ThermalPowerFeaturesEax::PLN) } + pub fn set_pln(&mut self, bit: bool) -> &mut Self { + self.eax.set(ThermalPowerFeaturesEax::PLN, bit); + self + } + /// ECMD. Clock modulation duty cycle extension is supported if set. /// /// # Platforms @@ -2982,6 +3994,11 @@ impl ThermalPowerInfo { self.eax.contains(ThermalPowerFeaturesEax::ECMD) } + pub fn set_ecmd(&mut self, bit: bool) -> &mut Self { + self.eax.set(ThermalPowerFeaturesEax::ECMD, bit); + self + } + /// PTM. Package thermal management is supported if set. /// /// # Platforms @@ -2990,6 +4007,11 @@ impl ThermalPowerInfo { self.eax.contains(ThermalPowerFeaturesEax::PTM) } + pub fn set_ptm(&mut self, bit: bool) -> &mut Self { + self.eax.set(ThermalPowerFeaturesEax::PTM, bit); + self + } + /// HWP. HWP base registers (IA32_PM_ENABLE[bit 0], IA32_HWP_CAPABILITIES, /// IA32_HWP_REQUEST, IA32_HWP_STATUS) are supported if set. /// @@ -2999,6 +4021,11 @@ impl ThermalPowerInfo { self.eax.contains(ThermalPowerFeaturesEax::HWP) } + pub fn set_hwp(&mut self, bit: bool) -> &mut Self { + self.eax.set(ThermalPowerFeaturesEax::HWP, bit); + self + } + /// HWP Notification. IA32_HWP_INTERRUPT MSR is supported if set. /// /// # Platforms @@ -3007,6 +4034,11 @@ impl ThermalPowerInfo { self.eax.contains(ThermalPowerFeaturesEax::HWP_NOTIFICATION) } + pub fn set_hwp_notification(&mut self, bit: bool) -> &mut Self { + self.eax.set(ThermalPowerFeaturesEax::HWP_NOTIFICATION, bit); + self + } + /// HWP Activity Window. IA32_HWP_REQUEST[bits 41:32] is supported if set. /// /// # Platforms @@ -3016,6 +4048,12 @@ impl ThermalPowerInfo { .contains(ThermalPowerFeaturesEax::HWP_ACTIVITY_WINDOW) } + pub fn set_hwp_activity_window(&mut self, bit: bool) -> &mut Self { + self.eax + .set(ThermalPowerFeaturesEax::HWP_ACTIVITY_WINDOW, bit); + self + } + /// HWP Energy Performance Preference. IA32_HWP_REQUEST[bits 31:24] is /// supported if set. /// @@ -3026,6 +4064,14 @@ impl ThermalPowerInfo { .contains(ThermalPowerFeaturesEax::HWP_ENERGY_PERFORMANCE_PREFERENCE) } + pub fn set_hwp_energy_performance_preference(&mut self, bit: bool) -> &mut Self { + self.eax.set( + ThermalPowerFeaturesEax::HWP_ENERGY_PERFORMANCE_PREFERENCE, + bit, + ); + self + } + /// HWP Package Level Request. IA32_HWP_REQUEST_PKG MSR is supported if set. /// /// # Platforms @@ -3035,6 +4081,12 @@ impl ThermalPowerInfo { .contains(ThermalPowerFeaturesEax::HWP_PACKAGE_LEVEL_REQUEST) } + pub fn set_hwp_package_level_request(&mut self, bit: bool) -> &mut Self { + self.eax + .set(ThermalPowerFeaturesEax::HWP_PACKAGE_LEVEL_REQUEST, bit); + self + } + /// HDC. HDC base registers IA32_PKG_HDC_CTL, IA32_PM_CTL1, /// IA32_THREAD_STALL MSRs are supported if set. /// @@ -3044,6 +4096,11 @@ impl ThermalPowerInfo { self.eax.contains(ThermalPowerFeaturesEax::HDC) } + pub fn set_hdc(&mut self, bit: bool) -> &mut Self { + self.eax.set(ThermalPowerFeaturesEax::HDC, bit); + self + } + /// Intel® Turbo Boost Max Technology 3.0 available. /// /// # Platforms @@ -3052,6 +4109,11 @@ impl ThermalPowerInfo { self.eax.contains(ThermalPowerFeaturesEax::TURBO_BOOST_3) } + pub fn set_turbo_boost3(&mut self, bit: bool) -> &mut Self { + self.eax.set(ThermalPowerFeaturesEax::TURBO_BOOST_3, bit); + self + } + /// HWP Capabilities. Highest Performance change is supported if set. /// /// # Platforms @@ -3060,6 +4122,11 @@ impl ThermalPowerInfo { self.eax.contains(ThermalPowerFeaturesEax::HWP_CAPABILITIES) } + pub fn set_hwp_capabilities(&mut self, bit: bool) -> &mut Self { + self.eax.set(ThermalPowerFeaturesEax::HWP_CAPABILITIES, bit); + self + } + /// HWP PECI override is supported if set. /// /// # Platforms @@ -3069,6 +4136,12 @@ impl ThermalPowerInfo { .contains(ThermalPowerFeaturesEax::HWP_PECI_OVERRIDE) } + pub fn set_hwp_peci_override(&mut self, bit: bool) -> &mut Self { + self.eax + .set(ThermalPowerFeaturesEax::HWP_PECI_OVERRIDE, bit); + self + } + /// Flexible HWP is supported if set. /// /// # Platforms @@ -3077,6 +4150,11 @@ impl ThermalPowerInfo { self.eax.contains(ThermalPowerFeaturesEax::FLEXIBLE_HWP) } + pub fn set_flexible_hwp(&mut self, bit: bool) -> &mut Self { + self.eax.set(ThermalPowerFeaturesEax::FLEXIBLE_HWP, bit); + self + } + /// Fast access mode for the IA32_HWP_REQUEST MSR is supported if set. /// /// # Platforms @@ -3086,6 +4164,12 @@ impl ThermalPowerInfo { .contains(ThermalPowerFeaturesEax::HWP_REQUEST_MSR_FAST_ACCESS) } + pub fn set_hwp_fast_access_mode(&mut self, bit: bool) -> &mut Self { + self.eax + .set(ThermalPowerFeaturesEax::HWP_REQUEST_MSR_FAST_ACCESS, bit); + self + } + /// Ignoring Idle Logical Processor HWP request is supported if set. /// /// # Platforms @@ -3095,6 +4179,14 @@ impl ThermalPowerInfo { .contains(ThermalPowerFeaturesEax::IGNORE_IDLE_PROCESSOR_HWP_REQUEST) } + pub fn set_ignore_idle_processor_hwp_request(&mut self, bit: bool) -> &mut Self { + self.eax.set( + ThermalPowerFeaturesEax::IGNORE_IDLE_PROCESSOR_HWP_REQUEST, + bit, + ); + self + } + /// Hardware Coordination Feedback Capability /// /// Presence of IA32_MPERF and IA32_APERF. @@ -3111,6 +4203,12 @@ impl ThermalPowerInfo { .contains(ThermalPowerFeaturesEcx::HW_COORD_FEEDBACK) } + pub fn set_hw_coord_feedback(&mut self, bit: bool) -> &mut Self { + self.ecx + .set(ThermalPowerFeaturesEcx::HW_COORD_FEEDBACK, bit); + self + } + /// The processor supports performance-energy bias preference if /// CPUID.06H:ECX.SETBH[bit 3] is set and it also implies the presence of a /// new architectural MSR called IA32_ENERGY_PERF_BIAS (1B0H) @@ -3120,6 +4218,11 @@ impl ThermalPowerInfo { pub fn has_energy_bias_pref(&self) -> bool { self.ecx.contains(ThermalPowerFeaturesEcx::ENERGY_BIAS_PREF) } + + pub fn set_energy_bias_pref(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ThermalPowerFeaturesEcx::ENERGY_BIAS_PREF, bit); + self + } } impl Debug for ThermalPowerInfo { @@ -3220,7 +4323,7 @@ bitflags! { /// # Platforms /// 🟡 AMD ✅ Intel pub struct ExtendedFeatures { - _eax: u32, + eax: u32, ebx: ExtendedFeaturesEbx, ecx: ExtendedFeaturesEcx, edx: ExtendedFeaturesEdx, @@ -3231,6 +4334,31 @@ pub struct ExtendedFeatures { } impl ExtendedFeatures { + pub fn new() -> Self { + Self { + eax: 0, + ebx: ExtendedFeaturesEbx::from_bits_truncate(0), + ecx: ExtendedFeaturesEcx::from_bits_truncate(0), + edx: ExtendedFeaturesEdx::from_bits_truncate(0), + eax1: ExtendedFeaturesEax1::from_bits_truncate(0), + _ebx1: 0, + _ecx1: 0, + edx1: ExtendedFeaturesEdx1::from_bits_truncate(0), + } + } + + /// Ensure the maximum valid subleaf for this leaf is at least high enough to cover `level`. + /// + /// This is mostly useful for keeping the max valid subleaf high enough to encompass feature + /// bits set in this leaf. + /// + // Private only because there hasn't been a need to make this public (yet?). It probably makes + // more sense to just set `eax` public if there's need to manage that precisely. + fn used_subleaf(&mut self, level: u32) -> &mut Self { + self.eax = core::cmp::max(self.eax, level); + self + } + /// FSGSBASE. Supports RDFSBASE/RDGSBASE/WRFSBASE/WRGSBASE if 1. /// /// # Platforms @@ -3240,6 +4368,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::FSGSBASE) } + pub fn set_fsgsbase(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::FSGSBASE, bit); + self + } + /// IA32_TSC_ADJUST MSR is supported if 1. /// /// # Platforms @@ -3249,6 +4382,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::ADJUST_MSR) } + pub fn set_tsc_adjust_msr(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::ADJUST_MSR, bit); + self + } + /// BMI1 /// /// # Platforms @@ -3258,6 +4396,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::BMI1) } + pub fn set_bmi1(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::BMI1, bit); + self + } + /// HLE /// /// # Platforms @@ -3267,6 +4410,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::HLE) } + pub fn set_hle(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::HLE, bit); + self + } + /// AVX2 /// /// # Platforms @@ -3276,6 +4424,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::AVX2) } + pub fn set_avx2(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::AVX2, bit); + self + } + /// FDP_EXCPTN_ONLY. x87 FPU Data Pointer updated only on x87 exceptions if /// 1. /// @@ -3286,6 +4439,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::FDP) } + pub fn set_fdp(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::FDP, bit); + self + } + /// SMEP. Supports Supervisor-Mode Execution Prevention if 1. /// /// # Platforms @@ -3295,6 +4453,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::SMEP) } + pub fn set_smep(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::SMEP, bit); + self + } + /// BMI2 /// /// # Platforms @@ -3304,6 +4467,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::BMI2) } + pub fn set_bmi2(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::BMI2, bit); + self + } + /// Supports Enhanced REP MOVSB/STOSB if 1. /// /// # Platforms @@ -3313,6 +4481,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::REP_MOVSB_STOSB) } + pub fn set_rep_movsb_stosb(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::REP_MOVSB_STOSB, bit); + self + } + /// INVPCID. If 1, supports INVPCID instruction for system software that /// manages process-context identifiers. /// @@ -3323,6 +4496,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::INVPCID) } + pub fn set_invpcid(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::INVPCID, bit); + self + } + /// RTM /// /// # Platforms @@ -3332,6 +4510,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::RTM) } + pub fn set_rtm(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::RTM, bit); + self + } + /// Supports Intel Resource Director Technology (RDT) Monitoring capability. /// /// # Platforms @@ -3341,6 +4524,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::RDTM) } + pub fn set_rdtm(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::RDTM, bit); + self + } + /// Deprecates FPU CS and FPU DS values if 1. /// /// # Platforms @@ -3350,6 +4538,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::DEPRECATE_FPU_CS_DS) } + pub fn set_fpu_cs_ds_deprecated(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::DEPRECATE_FPU_CS_DS, bit); + self + } + /// MPX. Supports Intel Memory Protection Extensions if 1. /// /// # Platforms @@ -3359,6 +4552,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::MPX) } + pub fn set_mpx(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::MPX, bit); + self + } + /// Supports Intel Resource Director Technology (RDT) Allocation capability. /// /// # Platforms @@ -3368,6 +4566,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::RDTA) } + pub fn set_rdta(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::RDTA, bit); + self + } + /// Supports RDSEED. /// /// # Platforms @@ -3377,6 +4580,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::RDSEED) } + pub fn set_rdseed(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::RDSEED, bit); + self + } + /// Supports ADX. /// /// # Platforms @@ -3386,6 +4594,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::ADX) } + pub fn set_adx(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::ADX, bit); + self + } + /// SMAP. Supports Supervisor-Mode Access Prevention (and the CLAC/STAC /// instructions) if 1. /// @@ -3396,6 +4609,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::SMAP) } + pub fn set_smap(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::SMAP, bit); + self + } + /// Supports CLFLUSHOPT. /// /// # Platforms @@ -3405,6 +4623,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::CLFLUSHOPT) } + pub fn set_clflushopt(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::CLFLUSHOPT, bit); + self + } + /// Supports Intel Processor Trace. /// /// # Platforms @@ -3414,6 +4637,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::PROCESSOR_TRACE) } + pub fn set_processor_trace(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::PROCESSOR_TRACE, bit); + self + } + /// Supports SHA Instructions. /// /// # Platforms @@ -3423,6 +4651,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::SHA) } + pub fn set_sha(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::SHA, bit); + self + } + /// Supports Intel® Software Guard Extensions (Intel® SGX Extensions). /// /// # Platforms @@ -3432,6 +4665,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::SGX) } + pub fn set_sgx(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::SGX, bit); + self + } + /// Supports AVX512F. /// /// # Platforms @@ -3441,6 +4679,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::AVX512F) } + pub fn set_avx512f(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::AVX512F, bit); + self + } + /// Supports AVX512DQ. /// /// # Platforms @@ -3450,6 +4693,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::AVX512DQ) } + pub fn set_avx512dq(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::AVX512DQ, bit); + self + } + /// AVX512_IFMA /// /// # Platforms @@ -3459,6 +4707,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::AVX512_IFMA) } + pub fn set_avx512_ifma(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::AVX512_IFMA, bit); + self + } + /// AVX512PF /// /// # Platforms @@ -3468,6 +4721,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::AVX512PF) } + pub fn set_avx512pf(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::AVX512PF, bit); + self + } + /// AVX512ER /// /// # Platforms @@ -3477,6 +4735,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::AVX512ER) } + pub fn set_avx512er(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::AVX512ER, bit); + self + } + /// AVX512CD /// /// # Platforms @@ -3486,6 +4749,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::AVX512CD) } + pub fn set_avx512cd(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::AVX512CD, bit); + self + } + /// AVX512BW /// /// # Platforms @@ -3495,6 +4763,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::AVX512BW) } + pub fn set_avx512bw(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::AVX512BW, bit); + self + } + /// AVX512VL /// /// # Platforms @@ -3504,6 +4777,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::AVX512VL) } + pub fn set_avx512vl(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::AVX512VL, bit); + self + } + /// CLWB /// /// # Platforms @@ -3513,6 +4791,11 @@ impl ExtendedFeatures { self.ebx.contains(ExtendedFeaturesEbx::CLWB) } + pub fn set_clwb(&mut self, bit: bool) -> &mut Self { + self.ebx.set(ExtendedFeaturesEbx::CLWB, bit); + self + } + /// Has PREFETCHWT1 (Intel® Xeon Phi™ only). /// /// # Platforms @@ -3522,6 +4805,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::PREFETCHWT1) } + pub fn set_prefetchwt1(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::PREFETCHWT1, bit); + self + } + /// AVX512VBMI /// /// ✅ AMD ✅ Intel @@ -3530,6 +4818,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::AVX512VBMI) } + pub fn set_avx512vbmi(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::AVX512VBMI, bit); + self + } + /// Supports user-mode instruction prevention if 1. /// /// # Platforms @@ -3539,6 +4832,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::UMIP) } + pub fn set_umip(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::UMIP, bit); + self + } + /// Supports protection keys for user-mode pages. /// /// # Platforms @@ -3548,6 +4846,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::PKU) } + pub fn set_pku(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::PKU, bit); + self + } + /// OS has set CR4.PKE to enable protection keys (and the RDPKRU/WRPKRU /// instructions. /// @@ -3558,6 +4861,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::OSPKE) } + pub fn set_ospke(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::OSPKE, bit); + self + } + /// WAITPKG /// /// ❓ AMD ✅ Intel @@ -3566,6 +4874,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::WAITPKG) } + pub fn set_waitpkg(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::WAITPKG, bit); + self + } + /// AVX512VBMI2 /// /// ✅ AMD ✅ Intel @@ -3575,6 +4888,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::AVX512VBMI2) } + pub fn set_av512vbmi2(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::AVX512VBMI2, bit); + self + } + /// AVX512VBMI2 /// /// ✅ AMD ✅ Intel @@ -3583,6 +4901,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::AVX512VBMI2) } + pub fn set_avx512vbmi2(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::AVX512VBMI2, bit); + self + } + /// Supports CET shadow stack features. Processors that set this bit define bits 0..2 of the /// IA32_U_CET and IA32_S_CET MSRs. Enumerates support for the following MSRs: /// IA32_INTERRUPT_SPP_TABLE_ADDR, IA32_PL3_SSP, IA32_PL2_SSP, IA32_PL1_SSP, and IA32_PL0_SSP. @@ -3593,6 +4916,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::CETSS) } + pub fn set_cet_ss(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::CETSS, bit); + self + } + /// GFNI /// /// ❓ AMD ✅ Intel @@ -3601,6 +4929,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::GFNI) } + pub fn set_gfni(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::GFNI, bit); + self + } + /// VAES /// /// ❓ AMD ✅ Intel @@ -3609,6 +4942,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::VAES) } + pub fn set_vaes(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::VAES, bit); + self + } + /// VPCLMULQDQ /// /// ❓ AMD ✅ Intel @@ -3617,6 +4955,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::VPCLMULQDQ) } + pub fn set_vpclmulqdq(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::VPCLMULQDQ, bit); + self + } + /// AVX512VNNI /// /// # Platforms @@ -3626,6 +4969,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::AVX512VNNI) } + pub fn set_avx512vnni(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::AVX512VNNI, bit); + self + } + /// AVX512BITALG /// /// ✅ AMD ✅ Intel @@ -3634,6 +4982,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::AVX512BITALG) } + pub fn set_avx512bitalg(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::AVX512BITALG, bit); + self + } + /// Indicates the following MSRs are supported: IA32_TME_CAPABILITY, IA32_TME_ACTIVATE, /// IA32_TME_EXCLUDE_MASK, and IA32_TME_EXCLUDE_BASE. /// @@ -3643,6 +4996,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::TMEEN) } + pub fn set_tme_en(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::TMEEN, bit); + self + } + /// AVX512VPOPCNTDQ /// /// ✅ AMD ✅ Intel @@ -3651,6 +5009,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::AVX512VPOPCNTDQ) } + pub fn set_avx512vpopcntdq(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::AVX512VPOPCNTDQ, bit); + self + } + /// Supports 57-bit linear addresses and five-level paging if 1. /// /// # Platforms @@ -3660,6 +5023,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::LA57) } + pub fn set_la57(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::LA57, bit); + self + } + /// RDPID and IA32_TSC_AUX are available. /// /// # Bug @@ -3674,6 +5042,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::RDPID) } + pub fn set_rdpid(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::RDPID, bit); + self + } + /// Supports SGX Launch Configuration. /// /// # Platforms @@ -3683,6 +5056,11 @@ impl ExtendedFeatures { self.ecx.contains(ExtendedFeaturesEcx::SGX_LC) } + pub fn set_sgx_lc(&mut self, bit: bool) -> &mut Self { + self.ecx.set(ExtendedFeaturesEcx::SGX_LC, bit); + self + } + /// The value of MAWAU used by the BNDLDX and BNDSTX instructions in 64-bit mode. /// /// # Platforms @@ -3692,6 +5070,10 @@ impl ExtendedFeatures { get_bits(self.ecx.bits(), 17, 21) as u8 } + pub fn set_mawau_value(&mut self, bits: u32) { + set_bits(&mut self.ecx.bits(), bits, 17, 21) + } + /// Supports AVX512_4VNNIW. /// /// # Platforms @@ -3701,6 +5083,11 @@ impl ExtendedFeatures { self.edx.contains(ExtendedFeaturesEdx::AVX512_4VNNIW) } + pub fn set_avx512_4vnniw(&mut self, bit: bool) -> &mut Self { + self.edx.set(ExtendedFeaturesEdx::AVX512_4VNNIW, bit); + self + } + /// Supports AVX512_4FMAPS. /// /// # Platforms @@ -3710,6 +5097,25 @@ impl ExtendedFeatures { self.edx.contains(ExtendedFeaturesEdx::AVX512_4FMAPS) } + pub fn set_avx512_4fmaps(&mut self, bit: bool) -> &mut Self { + self.edx.set(ExtendedFeaturesEdx::AVX512_4FMAPS, bit); + self + } + + /// Supports FSRM. + /// + /// # Platforms + /// ✅ AMD ✅ Intel + #[inline] + pub const fn has_fsrm(&self) -> bool { + self.edx.contains(ExtendedFeaturesEdx::FSRM) + } + + pub fn set_fsrm(&mut self, bit: bool) -> &mut Self { + self.edx.set(ExtendedFeaturesEdx::FSRM, bit); + self + } + /// Supports AVX512_VP2INTERSECT. /// /// # Platforms @@ -3719,6 +5125,11 @@ impl ExtendedFeatures { self.edx.contains(ExtendedFeaturesEdx::AVX512_VP2INTERSECT) } + pub fn set_avx512_vp2intersect(&mut self, bit: bool) -> &mut Self { + self.edx.set(ExtendedFeaturesEdx::AVX512_VP2INTERSECT, bit); + self + } + /// Supports AMX_BF16. /// /// # Platforms @@ -3728,6 +5139,11 @@ impl ExtendedFeatures { self.edx.contains(ExtendedFeaturesEdx::AMX_BF16) } + pub fn set_amx_bf16(&mut self, bit: bool) -> &mut Self { + self.edx.set(ExtendedFeaturesEdx::AMX_BF16, bit); + self + } + /// Supports AVX512_FP16. /// /// # Platforms @@ -3737,6 +5153,11 @@ impl ExtendedFeatures { self.edx.contains(ExtendedFeaturesEdx::AVX512_FP16) } + pub fn set_avx512_fp16(&mut self, bit: bool) -> &mut Self { + self.edx.set(ExtendedFeaturesEdx::AVX512_FP16, bit); + self + } + /// Supports AMX_TILE. /// /// # Platforms @@ -3746,6 +5167,11 @@ impl ExtendedFeatures { self.edx.contains(ExtendedFeaturesEdx::AMX_TILE) } + pub fn set_amx_tile(&mut self, bit: bool) -> &mut Self { + self.edx.set(ExtendedFeaturesEdx::AMX_TILE, bit); + self + } + /// Supports AMX_INT8. /// /// # Platforms @@ -3755,6 +5181,11 @@ impl ExtendedFeatures { self.edx.contains(ExtendedFeaturesEdx::AMX_INT8) } + pub fn set_amx_int8(&mut self, bit: bool) -> &mut Self { + self.edx.set(ExtendedFeaturesEdx::AMX_INT8, bit); + self + } + /// Supports AVX_VNNI. /// /// # Platforms @@ -3764,6 +5195,12 @@ impl ExtendedFeatures { self.eax1.contains(ExtendedFeaturesEax1::AVX_VNNI) } + pub fn set_avx_vnni(&mut self, bit: bool) -> &mut Self { + self.used_subleaf(1); + self.eax1.set(ExtendedFeaturesEax1::AVX_VNNI, bit); + self + } + /// Supports AVX512_BF16. /// /// # Platforms @@ -3773,6 +5210,12 @@ impl ExtendedFeatures { self.eax1.contains(ExtendedFeaturesEax1::AVX512_BF16) } + pub fn set_avx512_bf16(&mut self, bit: bool) -> &mut Self { + self.used_subleaf(1); + self.eax1.set(ExtendedFeaturesEax1::AVX512_BF16, bit); + self + } + /// Supports Fast zero-length REP MOVSB /// /// # Platforms @@ -3782,6 +5225,12 @@ impl ExtendedFeatures { self.eax1.contains(ExtendedFeaturesEax1::FZRM) } + pub fn set_fzrm(&mut self, bit: bool) -> &mut Self { + self.used_subleaf(1); + self.eax1.set(ExtendedFeaturesEax1::FZRM, bit); + self + } + /// Supports Fast Short REP STOSB /// /// # Platforms @@ -3791,6 +5240,12 @@ impl ExtendedFeatures { self.eax1.contains(ExtendedFeaturesEax1::FSRS) } + pub fn set_fsrs(&mut self, bit: bool) -> &mut Self { + self.used_subleaf(1); + self.eax1.set(ExtendedFeaturesEax1::FSRS, bit); + self + } + /// Supports Fast Short REP CMPSB, REP SCASB /// /// # Platforms @@ -3800,6 +5255,12 @@ impl ExtendedFeatures { self.eax1.contains(ExtendedFeaturesEax1::FSRCRS) } + pub fn set_fsrcrs(&mut self, bit: bool) -> &mut Self { + self.used_subleaf(1); + self.eax1.set(ExtendedFeaturesEax1::FSRCRS, bit); + self + } + /// Supports HRESET /// /// # Platforms @@ -3809,6 +5270,12 @@ impl ExtendedFeatures { self.eax1.contains(ExtendedFeaturesEax1::HRESET) } + pub fn set_hreset(&mut self, bit: bool) -> &mut Self { + self.used_subleaf(1); + self.eax1.set(ExtendedFeaturesEax1::HRESET, bit); + self + } + /// Supports AVX-IFMA Instructions. /// /// # Platforms @@ -3818,6 +5285,12 @@ impl ExtendedFeatures { self.eax1.contains(ExtendedFeaturesEax1::AVX_IFMA) } + pub fn set_avx_ifma(&mut self, bit: bool) -> &mut Self { + self.used_subleaf(1); + self.eax1.set(ExtendedFeaturesEax1::AVX_IFMA, bit); + self + } + /// Supports Linear Address Masking. /// /// # Platforms @@ -3827,6 +5300,12 @@ impl ExtendedFeatures { self.eax1.contains(ExtendedFeaturesEax1::LAM) } + pub fn set_lam(&mut self, bit: bool) -> &mut Self { + self.used_subleaf(1); + self.eax1.set(ExtendedFeaturesEax1::LAM, bit); + self + } + /// Supports RDMSRLIST and WRMSRLIST Instructions and the IA32_BARRIER MSR. /// /// # Platforms @@ -3836,6 +5315,12 @@ impl ExtendedFeatures { self.eax1.contains(ExtendedFeaturesEax1::MSRLIST) } + pub fn set_msrlist(&mut self, bit: bool) -> &mut Self { + self.used_subleaf(1); + self.eax1.set(ExtendedFeaturesEax1::MSRLIST, bit); + self + } + /// Supports INVD execution prevention after BIOS Done. /// /// # Platforms @@ -3846,6 +5331,13 @@ impl ExtendedFeatures { .contains(ExtendedFeaturesEax1::INVD_DISABLE_POST_BIOS_DONE) } + pub fn set_invd_disable_post_bios_done(&mut self, bit: bool) -> &mut Self { + self.used_subleaf(1); + self.eax1 + .set(ExtendedFeaturesEax1::INVD_DISABLE_POST_BIOS_DONE, bit); + self + } + /// Supports AVX_VNNI_INT8 /// /// # Platforms @@ -3855,6 +5347,12 @@ impl ExtendedFeatures { self.edx1.contains(ExtendedFeaturesEdx1::AVX_VNNI_INT8) } + pub fn set_avx_vnni_int8(&mut self, bit: bool) -> &mut Self { + self.used_subleaf(1); + self.edx1.set(ExtendedFeaturesEdx1::AVX_VNNI_INT8, bit); + self + } + /// Supports AVX_NE_CONVERT /// /// # Platforms @@ -3864,6 +5362,12 @@ impl ExtendedFeatures { self.edx1.contains(ExtendedFeaturesEdx1::AVX_NE_CONVERT) } + pub fn set_avx_ne_convert(&mut self, bit: bool) -> &mut Self { + self.used_subleaf(1); + self.edx1.set(ExtendedFeaturesEdx1::AVX_NE_CONVERT, bit); + self + } + /// Supports AVX_VNNI_INT16 /// /// # Platforms @@ -3873,6 +5377,12 @@ impl ExtendedFeatures { self.edx1.contains(ExtendedFeaturesEdx1::AVX_VNNI_INT16) } + pub fn set_avx_vnni_int16(&mut self, bit: bool) -> &mut Self { + self.used_subleaf(1); + self.edx1.set(ExtendedFeaturesEdx1::AVX_VNNI_INT16, bit); + self + } + /// Supports PREFETCHI /// /// # Platforms @@ -3882,6 +5392,12 @@ impl ExtendedFeatures { self.edx1.contains(ExtendedFeaturesEdx1::PREFETCHI) } + pub fn set_prefetchi(&mut self, bit: bool) -> &mut Self { + self.used_subleaf(1); + self.edx1.set(ExtendedFeaturesEdx1::PREFETCHI, bit); + self + } + /// Supports UIRET_UIF /// /// # Platforms @@ -3891,6 +5407,12 @@ impl ExtendedFeatures { self.edx1.contains(ExtendedFeaturesEdx1::UIRET_UIF) } + pub fn set_uiret_uif(&mut self, bit: bool) -> &mut Self { + self.used_subleaf(1); + self.edx1.set(ExtendedFeaturesEdx1::UIRET_UIF, bit); + self + } + /// Supports CET_SSS /// /// # Platforms @@ -3900,6 +5422,12 @@ impl ExtendedFeatures { self.edx1.contains(ExtendedFeaturesEdx1::CET_SSS) } + pub fn set_cet_sss(&mut self, bit: bool) -> &mut Self { + self.used_subleaf(1); + self.edx1.set(ExtendedFeaturesEdx1::CET_SSS, bit); + self + } + /// Supports AVX10 /// /// # Platforms @@ -3908,6 +5436,12 @@ impl ExtendedFeatures { pub const fn has_avx10(&self) -> bool { self.edx1.contains(ExtendedFeaturesEdx1::AVX10) } + + pub fn set_avx10(&mut self, bit: bool) -> &mut Self { + self.used_subleaf(1); + self.edx1.set(ExtendedFeaturesEdx1::AVX10, bit); + self + } } impl Debug for ExtendedFeatures { @@ -3952,7 +5486,7 @@ bitflags! { const RDTM = 1 << 12; /// Deprecates FPU CS and FPU DS values if 1. (Bit 13) const DEPRECATE_FPU_CS_DS = 1 << 13; - /// Deprecates FPU CS and FPU DS values if 1. (Bit 14) + /// Supports Intel Memory Protection Extensions if set. (Bit 14) const MPX = 1 << 14; /// Supports Intel Resource Director Technology (RDT) Allocation capability if 1. const RDTA = 1 << 15; @@ -4053,6 +5587,9 @@ bitflags! { const AVX512_4VNNIW = 1 << 2; /// Bit 03: AVX512_4FMAPS. (Intel® Xeon Phi™ only). const AVX512_4FMAPS = 1 << 3; + /// Bit 04: Fast Short REP MOVSB (FSRM). Documented only in Intel SDM, present on Intel and + /// AMD (Zen 3 and later). + const FSRM = 1 << 4; /// Bit 08: AVX512_VP2INTERSECT. const AVX512_VP2INTERSECT = 1 << 8; /// Bit 22: AMX-BF16. If 1, the processor supports tile computational operations on bfloat16 numbers. @@ -4063,6 +5600,8 @@ bitflags! { const AMX_TILE = 1 << 24; /// Bit 25: AMX-INT8. If 1, the processor supports tile computational operations on 8-bit integers. const AMX_INT8 = 1 << 25; + /// Bit 26: IBRS and IBPB. If 1, the processor supports Indirect Branch Restricted Speculation and Indirect Branch Prediction Barrier controls. + const IBRS = 1 << 26; } } @@ -4157,31 +5696,55 @@ impl PerformanceMonitoringInfo { get_bits(self.eax, 0, 7) as u8 } + pub fn set_version_id(&mut self, bits: u32) { + set_bits(&mut self.eax, bits, 0, 7) + } + /// Number of general-purpose performance monitoring counter per logical processor. (Bits 15- 08) pub fn number_of_counters(&self) -> u8 { get_bits(self.eax, 8, 15) as u8 } + pub fn set_number_of_counters(&mut self, bits: u32) { + set_bits(&mut self.eax, bits, 8, 15) + } + /// Bit width of general-purpose, performance monitoring counter. (Bits 23 - 16) pub fn counter_bit_width(&self) -> u8 { get_bits(self.eax, 16, 23) as u8 } + pub fn set_counter_bit_width(&mut self, bits: u32) { + set_bits(&mut self.eax, bits, 16, 23) + } + /// Length of EBX bit vector to enumerate architectural performance monitoring events. (Bits 31 - 24) pub fn ebx_length(&self) -> u8 { get_bits(self.eax, 24, 31) as u8 } + pub fn set_ebx_length(&mut self, bits: u32) { + set_bits(&mut self.eax, bits, 24, 31) + } + /// Number of fixed-function performance counters (if Version ID > 1). (Bits 04 - 00) pub fn fixed_function_counters(&self) -> u8 { get_bits(self.edx, 0, 4) as u8 } + pub fn set_fixed_function_counters(&mut self, bits: u32) { + set_bits(&mut self.edx, bits, 0, 4) + } + /// Bit width of fixed-function performance counters (if Version ID > 1). (Bits 12- 05) pub fn fixed_function_counters_bit_width(&self) -> u8 { get_bits(self.edx, 5, 12) as u8 } + pub fn set_fixed_function_counters_bit_width(&mut self, bits: u32) { + set_bits(&mut self.edx, bits, 5, 12) + } + check_bit_fn!( doc = "AnyThread deprecation", has_any_thread_deprecation, @@ -4192,6 +5755,7 @@ impl PerformanceMonitoringInfo { check_flag!( doc = "Core cycle event not available if 1.", is_core_cyc_ev_unavailable, + has_core_cyc_ev_unavailable, ebx, PerformanceMonitoringFeaturesEbx::CORE_CYC_EV_UNAVAILABLE ); @@ -4199,6 +5763,7 @@ impl PerformanceMonitoringInfo { check_flag!( doc = "Instruction retired event not available if 1.", is_inst_ret_ev_unavailable, + has_inst_ret_ev_unavailable, ebx, PerformanceMonitoringFeaturesEbx::INST_RET_EV_UNAVAILABLE ); @@ -4206,6 +5771,7 @@ impl PerformanceMonitoringInfo { check_flag!( doc = "Reference cycles event not available if 1.", is_ref_cycle_ev_unavailable, + has_ref_cycle_ev_unavailable, ebx, PerformanceMonitoringFeaturesEbx::REF_CYC_EV_UNAVAILABLE ); @@ -4213,6 +5779,7 @@ impl PerformanceMonitoringInfo { check_flag!( doc = "Last-level cache reference event not available if 1.", is_cache_ref_ev_unavailable, + has_cache_ref_ev_unavailable, ebx, PerformanceMonitoringFeaturesEbx::CACHE_REF_EV_UNAVAILABLE ); @@ -4220,6 +5787,7 @@ impl PerformanceMonitoringInfo { check_flag!( doc = "Last-level cache misses event not available if 1.", is_ll_cache_miss_ev_unavailable, + has_ll_cache_miss_ev_unavailable, ebx, PerformanceMonitoringFeaturesEbx::LL_CACHE_MISS_EV_UNAVAILABLE ); @@ -4227,6 +5795,7 @@ impl PerformanceMonitoringInfo { check_flag!( doc = "Branch instruction retired event not available if 1.", is_branch_inst_ret_ev_unavailable, + has_branch_inst_ret_ev_unavailable, ebx, PerformanceMonitoringFeaturesEbx::BRANCH_INST_RET_EV_UNAVAILABLE ); @@ -4234,6 +5803,7 @@ impl PerformanceMonitoringInfo { check_flag!( doc = "Branch mispredict retired event not available if 1.", is_branch_midpred_ev_unavailable, + has_branch_midpred_ev_unavailable, ebx, PerformanceMonitoringFeaturesEbx::BRANCH_MISPRED_EV_UNAVAILABLE ); @@ -4286,7 +5856,7 @@ bitflags! { /// ✅ AMD ✅ Intel #[derive(Clone)] pub struct ExtendedTopologyIter { - read: R, + source: R, level: u32, is_v2: bool, } @@ -4315,17 +5885,34 @@ impl fmt::Debug for ExtendedTopologyLevel { } impl ExtendedTopologyLevel { + pub fn empty() -> Self { + Self { + eax: 0, + ebx: 0, + ecx: 0, + edx: 0, + } + } + /// Number of logical processors at this level type. /// The number reflects configuration as shipped. pub fn processors(&self) -> u16 { get_bits(self.ebx, 0, 15) as u16 } + pub fn set_processors(&mut self, bits: u32) { + set_bits(&mut self.ebx, bits, 0, 15) + } + /// Level number. pub fn level_number(&self) -> u8 { get_bits(self.ecx, 0, 7) as u8 } + pub fn set_level_number(&mut self, bits: u32) { + set_bits(&mut self.ecx, bits, 0, 7) + } + // Level type. pub fn level_type(&self) -> TopologyType { match get_bits(self.ecx, 8, 15) { @@ -4339,6 +5926,10 @@ impl ExtendedTopologyLevel { } } + pub fn set_level_type(&mut self, level: u32) { + set_bits(&mut self.ecx, level, 8, 15) + } + /// x2APIC ID the current logical processor. (Bits 31-00) pub fn x2apic_id(&self) -> u32 { self.edx @@ -4349,6 +5940,10 @@ impl ExtendedTopologyLevel { pub fn shift_right_for_next_apic_id(&self) -> u32 { get_bits(self.eax, 0, 4) } + + pub fn set_shift_right_for_next_apic_id(&mut self, bits: u32) { + set_bits(&mut self.eax, bits, 0, 4) + } } /// What type of core we have at this level in the topology (real CPU or hyper-threaded). @@ -4383,9 +5978,10 @@ impl Iterator for ExtendedTopologyIter { fn next(&mut self) -> Option { let res = if self.is_v2 { - self.read.cpuid2(EAX_EXTENDED_TOPOLOGY_INFO_V2, self.level) + self.source + .cpuid2(EAX_EXTENDED_TOPOLOGY_INFO_V2, self.level) } else { - self.read.cpuid2(EAX_EXTENDED_TOPOLOGY_INFO, self.level) + self.source.cpuid2(EAX_EXTENDED_TOPOLOGY_INFO, self.level) }; self.level += 1; @@ -4490,7 +6086,7 @@ bitflags! { /// # Platforms /// ✅ AMD ✅ Intel pub struct ExtendedStateInfo { - read: R, + source: R, eax: ExtendedStateInfoXCR0Flags, ebx: u32, ecx: u32, @@ -4502,9 +6098,41 @@ pub struct ExtendedStateInfo { } impl ExtendedStateInfo { + pub fn empty(source: F) -> ExtendedStateInfo { + Self { + source, + eax: ExtendedStateInfoXCR0Flags::from_bits_truncate(0), + ebx: 0, + ecx: 0, + _edx: 0, + eax1: 0, + ebx1: 0, + ecx1: ExtendedStateInfoXSSFlags::from_bits_truncate(0), + _edx1: 0, + } + } + + pub fn into_leaves(&self) -> [Option; 2] { + [ + Some(CpuIdResult { + eax: self.eax.bits(), + ebx: self.ebx, + ecx: self.ecx, + edx: self._edx, + }), + Some(CpuIdResult { + eax: self.eax1, + ebx: self.ebx1, + ecx: self.ecx1.bits(), + edx: self._edx1, + }), + ] + } + check_flag!( doc = "Support for legacy x87 in XCR0.", xcr0_supports_legacy_x87, + set_xcr0_supports_legacy_x87, eax, ExtendedStateInfoXCR0Flags::LEGACY_X87 ); @@ -4512,6 +6140,7 @@ impl ExtendedStateInfo { check_flag!( doc = "Support for SSE 128-bit in XCR0.", xcr0_supports_sse_128, + set_xcr0_supports_sse_128, eax, ExtendedStateInfoXCR0Flags::SSE128 ); @@ -4519,6 +6148,7 @@ impl ExtendedStateInfo { check_flag!( doc = "Support for AVX 256-bit in XCR0.", xcr0_supports_avx_256, + set_xcr0_supports_avx_256, eax, ExtendedStateInfoXCR0Flags::AVX256 ); @@ -4526,6 +6156,7 @@ impl ExtendedStateInfo { check_flag!( doc = "Support for MPX BNDREGS in XCR0.", xcr0_supports_mpx_bndregs, + set_xcr0_supports_mpx_bndregs, eax, ExtendedStateInfoXCR0Flags::MPX_BNDREGS ); @@ -4533,6 +6164,7 @@ impl ExtendedStateInfo { check_flag!( doc = "Support for MPX BNDCSR in XCR0.", xcr0_supports_mpx_bndcsr, + set_xcr0_supports_mpx_bndcsr, eax, ExtendedStateInfoXCR0Flags::MPX_BNDCSR ); @@ -4540,6 +6172,7 @@ impl ExtendedStateInfo { check_flag!( doc = "Support for AVX512 OPMASK in XCR0.", xcr0_supports_avx512_opmask, + set_xcr0_supports_avx512_opmask, eax, ExtendedStateInfoXCR0Flags::AVX512_OPMASK ); @@ -4547,6 +6180,7 @@ impl ExtendedStateInfo { check_flag!( doc = "Support for AVX512 ZMM Hi256 XCR0.", xcr0_supports_avx512_zmm_hi256, + set_xcr0_supports_avx512_zmm_hi256, eax, ExtendedStateInfoXCR0Flags::AVX512_ZMM_HI256 ); @@ -4554,6 +6188,7 @@ impl ExtendedStateInfo { check_flag!( doc = "Support for AVX512 ZMM Hi16 in XCR0.", xcr0_supports_avx512_zmm_hi16, + set_xcr0_supports_avx512_zmm_hi16, eax, ExtendedStateInfoXCR0Flags::AVX512_ZMM_HI16 ); @@ -4561,6 +6196,7 @@ impl ExtendedStateInfo { check_flag!( doc = "Support for PKRU in XCR0.", xcr0_supports_pkru, + set_xcr0_supports_pkru, eax, ExtendedStateInfoXCR0Flags::PKRU ); @@ -4568,6 +6204,7 @@ impl ExtendedStateInfo { check_flag!( doc = "Support for PT in IA32_XSS.", ia32_xss_supports_pt, + set_ia32_xss_supports_pt, ecx1, ExtendedStateInfoXSSFlags::PT ); @@ -4575,6 +6212,7 @@ impl ExtendedStateInfo { check_flag!( doc = "Support for HDC in IA32_XSS.", ia32_xss_supports_hdc, + set_ia32_xss_supports_hdc, ecx1, ExtendedStateInfoXSSFlags::HDC ); @@ -4586,6 +6224,11 @@ impl ExtendedStateInfo { self.ebx } + pub fn set_xsave_area_size_enabled_features(&mut self, bits: u32) -> &mut Self { + self.ebx = bits; + self + } + /// Maximum size (bytes, from the beginning of the XSAVE/XRSTOR save area) of the /// XSAVE/XRSTOR save area required by all supported features in the processor, /// i.e all the valid bit fields in XCR0. @@ -4593,35 +6236,69 @@ impl ExtendedStateInfo { self.ecx } + pub fn set_xsave_area_size_supported_features(&mut self, bits: u32) -> &mut Self { + self.ecx = bits; + self + } + /// CPU has xsaveopt feature. pub fn has_xsaveopt(&self) -> bool { self.eax1 & 0x1 > 0 } + pub fn set_xsaveopt(&mut self, bit: bool) -> &mut Self { + self.eax1 &= !(1 << 0); + self.eax1 |= bit as u32; + self + } + /// Supports XSAVEC and the compacted form of XRSTOR if set. pub fn has_xsavec(&self) -> bool { self.eax1 & 0b10 > 0 } + pub fn set_xsavec(&mut self, bit: bool) -> &mut Self { + self.eax1 &= !(1 << 1); + self.eax1 |= (bit as u32) << 1; + self + } + /// Supports XGETBV with ECX = 1 if set. pub fn has_xgetbv(&self) -> bool { self.eax1 & 0b100 > 0 } + pub fn set_xgetbv(&mut self, bit: bool) -> &mut Self { + self.eax1 &= !(1 << 2); + self.eax1 |= (bit as u32) << 2; + self + } + /// Supports XSAVES/XRSTORS and IA32_XSS if set. pub fn has_xsaves_xrstors(&self) -> bool { self.eax1 & 0b1000 > 0 } + pub fn set_xsavex_xrstors(&mut self, bit: bool) -> &mut Self { + self.eax1 &= !(1 << 3); + self.eax1 |= (bit as u32) << 3; + self + } + /// The size in bytes of the XSAVE area containing all states enabled by XCRO | IA32_XSS. pub fn xsave_size(&self) -> u32 { self.ebx1 } + pub fn set_xsave_size(&mut self, bits: u32) -> &mut Self { + self.ebx1 = bits; + self + } + /// Iterator over extended state enumeration levels >= 2. pub fn iter(&self) -> ExtendedStateIter { ExtendedStateIter { - read: self.read.clone(), + source: self.source.clone(), level: 1, supported_xcr0: self.eax.bits(), supported_xss: self.ecx1.bits(), @@ -4655,7 +6332,7 @@ impl Debug for ExtendedStateInfo { /// Yields [ExtendedState] structs. #[derive(Clone)] pub struct ExtendedStateIter { - read: R, + source: R, level: u32, supported_xcr0: u32, supported_xss: u32, @@ -4678,7 +6355,7 @@ impl Iterator for ExtendedStateIter { let bit = 1 << self.level; if (self.supported_xcr0 & bit > 0) || (self.supported_xss & bit > 0) { - let res = self.read.cpuid2(EAX_EXTENDED_STATE_INFO, self.level); + let res = self.source.cpuid2(EAX_EXTENDED_STATE_INFO, self.level); return Some(ExtendedState { subleaf: self.level, eax: res.eax, @@ -4782,6 +6459,23 @@ pub struct ExtendedState { } impl ExtendedState { + pub fn empty() -> Self { + Self { + subleaf: 0, + eax: 0, + ebx: 0, + ecx: 0, + } + } + + pub fn into_leaf(self) -> CpuIdResult { + CpuIdResult { + eax: self.eax, + ebx: self.ebx, + ecx: self.ecx, + edx: 0, + } + } /// Returns which register this specific extended subleaf contains information for. pub fn register(&self) -> ExtendedRegisterType { self.subleaf.into() @@ -4794,12 +6488,22 @@ impl ExtendedState { self.eax } + pub fn set_size(&mut self, bits: u32) -> &mut Self { + self.eax = bits; + self + } + /// The offset in bytes of this extended state components save area /// from the beginning of the XSAVE/XRSTOR area. pub fn offset(&self) -> u32 { self.ebx } + pub fn set_offset(&mut self, bits: u32) -> &mut Self { + self.ebx = bits; + self + } + pub fn location(&self) -> ExtendedRegisterStateLocation { if self.is_in_xcr0() { ExtendedRegisterStateLocation::Xcr0 @@ -4852,7 +6556,7 @@ impl Debug for ExtendedState { /// # Platforms /// ❌ AMD ✅ Intel pub struct RdtMonitoringInfo { - read: R, + source: R, ebx: u32, edx: u32, } @@ -4873,7 +6577,7 @@ impl RdtMonitoringInfo { /// L3 Cache Monitoring. pub fn l3_monitoring(&self) -> Option { if self.has_l3_monitoring() { - let res = self.read.cpuid2(EAX_RDT_MONITORING, 1); + let res = self.source.cpuid2(EAX_RDT_MONITORING, 1); Some(L3MonitoringInfo { ebx: res.ebx, ecx: res.ecx, @@ -4948,7 +6652,7 @@ impl Debug for L3MonitoringInfo { /// # Platforms /// ❌ AMD ✅ Intel pub struct RdtAllocationInfo { - read: R, + source: R, ebx: u32, } @@ -4967,7 +6671,7 @@ impl RdtAllocationInfo { /// L3 Cache Allocation Information. pub fn l3_cat(&self) -> Option { if self.has_l3_cat() { - let res = self.read.cpuid2(EAX_RDT_ALLOCATION, 1); + let res = self.source.cpuid2(EAX_RDT_ALLOCATION, 1); Some(L3CatInfo { eax: res.eax, ebx: res.ebx, @@ -4982,7 +6686,7 @@ impl RdtAllocationInfo { /// L2 Cache Allocation Information. pub fn l2_cat(&self) -> Option { if self.has_l2_cat() { - let res = self.read.cpuid2(EAX_RDT_ALLOCATION, 2); + let res = self.source.cpuid2(EAX_RDT_ALLOCATION, 2); Some(L2CatInfo { eax: res.eax, ebx: res.ebx, @@ -4996,7 +6700,7 @@ impl RdtAllocationInfo { /// Memory Bandwidth Allocation Information. pub fn memory_bandwidth_allocation(&self) -> Option { if self.has_memory_bandwidth_allocation() { - let res = self.read.cpuid2(EAX_RDT_ALLOCATION, 3); + let res = self.source.cpuid2(EAX_RDT_ALLOCATION, 3); Some(MemBwAllocationInfo { eax: res.eax, ecx: res.ecx, @@ -5045,6 +6749,10 @@ impl L3CatInfo { get_bits(self.edx, 0, 15) as u16 } + pub fn set_highest_cos(&mut self, bits: u32) { + set_bits(&mut self.edx, bits, 0, 15) + } + check_bit_fn!( doc = "Is Code and Data Prioritization Technology supported?", has_code_data_prioritization, @@ -5086,6 +6794,10 @@ impl L2CatInfo { pub fn highest_cos(&self) -> u16 { get_bits(self.edx, 0, 15) as u16 } + + pub fn set_highest_cos(&mut self, bits: u32) { + set_bits(&mut self.edx, bits, 0, 15) + } } impl Debug for L2CatInfo { @@ -5117,6 +6829,10 @@ impl MemBwAllocationInfo { get_bits(self.edx, 0, 15) as u16 } + pub fn set_highest_cos(&mut self, bits: u32) { + set_bits(&mut self.edx, bits, 0, 15) + } + check_bit_fn!( doc = "Reports whether the response of the delay values is linear.", has_linear_response_delay, @@ -5145,7 +6861,7 @@ impl Debug for MemBwAllocationInfo { /// # Platforms /// ❌ AMD ✅ Intel pub struct SgxInfo { - read: R, + source: R, eax: u32, ebx: u32, _ecx: u32, @@ -5184,11 +6900,19 @@ impl SgxInfo { get_bits(self.edx, 0, 7) as u8 } + pub fn set_max_enclave_size_non_64bit(&mut self, bits: u32) { + set_bits(&mut self.edx, bits, 0, 7) + } + /// The maximum supported enclave size in 64-bit mode is 2^retval. pub fn max_enclave_size_64bit(&self) -> u8 { get_bits(self.edx, 8, 15) as u8 } + pub fn set_max_enclave_size_64bit(&mut self, bits: u32) { + set_bits(&mut self.edx, bits, 8, 15) + } + /// Reports the valid bits of SECS.ATTRIBUTES\[127:0\] that software can set with ECREATE. pub fn secs_attributes(&self) -> (u64, u64) { let lower = self.eax1 as u64 | (self.ebx1 as u64) << 32; @@ -5198,7 +6922,7 @@ impl SgxInfo { /// Iterator over SGX sub-leafs. pub fn iter(&self) -> SgxSectionIter { SgxSectionIter { - read: self.read.clone(), + source: self.source.clone(), current: 2, } } @@ -5231,7 +6955,7 @@ impl Debug for SgxInfo { /// Iterator over the SGX sub-leafs (ECX >= 2). #[derive(Clone)] pub struct SgxSectionIter { - read: R, + source: R, current: u32, } @@ -5239,7 +6963,7 @@ impl Iterator for SgxSectionIter { type Item = SgxSectionInfo; fn next(&mut self) -> Option { - let res = self.read.cpuid2(EAX_SGX, self.current); + let res = self.source.cpuid2(EAX_SGX, self.current); self.current += 1; match get_bits(res.eax, 0, 3) { 0b0001 => Some(SgxSectionInfo::Epc(EpcSection { @@ -5505,15 +7229,27 @@ impl ProcessorFrequencyInfo { get_bits(self.eax, 0, 15) as u16 } + pub fn set_processor_base_frequency(&mut self, bits: u32) { + set_bits(&mut self.eax, bits, 0, 15) + } + /// Maximum Frequency (in MHz). pub fn processor_max_frequency(&self) -> u16 { get_bits(self.ebx, 0, 15) as u16 } + pub fn set_processor_max_frequency(&mut self, bits: u32) { + set_bits(&mut self.ebx, bits, 0, 15) + } + /// Bus (Reference) Frequency (in MHz). pub fn bus_frequency(&self) -> u16 { get_bits(self.ecx, 0, 15) as u16 } + + pub fn set_bus_frequency(&mut self, bits: u32) { + set_bits(&mut self.ecx, bits, 0, 15) + } } impl fmt::Debug for ProcessorFrequencyInfo { @@ -5532,7 +7268,7 @@ impl fmt::Debug for ProcessorFrequencyInfo { /// ❌ AMD ✅ Intel #[derive(Clone)] pub struct DatIter { - read: R, + source: R, current: u32, count: u32, } @@ -5549,7 +7285,7 @@ impl Iterator for DatIter { } let res = self - .read + .source .cpuid2(EAX_DETERMINISTIC_ADDRESS_TRANSLATION_INFO, self.current); self.current += 1; @@ -5715,7 +7451,7 @@ impl fmt::Display for DatType { /// # Platforms /// ❌ AMD ✅ Intel pub struct SoCVendorInfo { - read: R, + source: R, /// MaxSOCID_Index eax: u32, ebx: u32, @@ -5739,9 +7475,9 @@ impl SoCVendorInfo { pub fn get_vendor_brand(&self) -> Option { // Leaf 17H is valid if MaxSOCID_Index >= 3. if self.eax >= 3 { - let r1 = self.read.cpuid2(EAX_SOC_VENDOR_INFO, 1); - let r2 = self.read.cpuid2(EAX_SOC_VENDOR_INFO, 2); - let r3 = self.read.cpuid2(EAX_SOC_VENDOR_INFO, 3); + let r1 = self.source.cpuid2(EAX_SOC_VENDOR_INFO, 1); + let r2 = self.source.cpuid2(EAX_SOC_VENDOR_INFO, 2); + let r3 = self.source.cpuid2(EAX_SOC_VENDOR_INFO, 3); Some(SoCVendorBrand { data: [r1, r2, r3] }) } else { None @@ -5751,7 +7487,7 @@ impl SoCVendorInfo { pub fn get_vendor_attributes(&self) -> Option> { if self.eax > 3 { Some(SoCVendorAttributesIter { - read: self.read.clone(), + source: self.source.clone(), count: self.eax, current: 3, }) @@ -5775,7 +7511,7 @@ impl fmt::Debug for SoCVendorInfo { /// Iterator for SoC vendor attributes. pub struct SoCVendorAttributesIter { - read: R, + source: R, count: u32, current: u32, } @@ -5798,7 +7534,7 @@ impl Iterator for SoCVendorAttributesIter { return None; } self.count += 1; - Some(self.read.cpuid2(EAX_SOC_VENDOR_INFO, self.count)) + Some(self.source.cpuid2(EAX_SOC_VENDOR_INFO, self.count)) } } @@ -5840,7 +7576,7 @@ impl fmt::Display for SoCVendorBrand { /// More information about this semi-official leaf can be found here /// pub struct HypervisorInfo { - read: R, + source: R, res: CpuIdResult, } @@ -5916,7 +7652,7 @@ impl HypervisorInfo { // vm aware tsc frequency retrieval: // # EAX: (Virtual) TSC frequency in kHz. if self.res.eax >= 0x40000010 { - let virt_tinfo = self.read.cpuid2(0x40000010, 0); + let virt_tinfo = self.source.cpuid2(0x40000010, 0); Some(virt_tinfo.eax) } else { None @@ -5927,7 +7663,7 @@ impl HypervisorInfo { pub fn apic_frequency(&self) -> Option { // # EBX: (Virtual) Bus (local apic timer) frequency in kHz. if self.res.eax >= 0x40000010 { - let virt_tinfo = self.read.cpuid2(0x40000010, 0); + let virt_tinfo = self.source.cpuid2(0x40000010, 0); Some(virt_tinfo.ebx) } else { None diff --git a/src/tests/i5_3337u.rs b/src/tests/i5_3337u.rs index 30eff14..1df3a71 100644 --- a/src/tests/i5_3337u.rs +++ b/src/tests/i5_3337u.rs @@ -433,7 +433,7 @@ fn extended_topology_info_v2() { #[test] fn extended_state_info() { let es = ExtendedStateInfo { - read: CpuIdReaderNative, + source: CpuIdReaderNative, eax: ExtendedStateInfoXCR0Flags::from_bits_truncate(7), ebx: 832, ecx: 832, @@ -460,7 +460,7 @@ fn extended_state_info3() { });*/ let esi = ExtendedStateInfo { - read: CpuIdReaderNative, + source: CpuIdReaderNative, eax: ExtendedStateInfoXCR0Flags::LEGACY_X87 | ExtendedStateInfoXCR0Flags::SSE128 | ExtendedStateInfoXCR0Flags::AVX256 @@ -619,7 +619,7 @@ fn extended_state_info3() { #[test] fn extended_state_info2() { let es = ExtendedStateInfo { - read: CpuIdReaderNative, + source: CpuIdReaderNative, eax: ExtendedStateInfoXCR0Flags::from_bits_truncate(31), ebx: 1088, ecx: 1088, @@ -694,7 +694,7 @@ fn extended_state_info2() { #[test] fn quality_of_service_info() { let qos = RdtMonitoringInfo { - read: CpuIdReaderNative, + source: CpuIdReaderNative, ebx: 832, edx: 0, }; @@ -755,7 +755,7 @@ fn processor_brand_string() { #[test] fn sgx_test() { let sgx = SgxInfo { - read: CpuIdReaderNative, + source: CpuIdReaderNative, eax: 1, ebx: 0, _ecx: 0, diff --git a/src/tests/ryzen_matisse.rs b/src/tests/ryzen_matisse.rs index eaf8c1b..6c58baf 100644 --- a/src/tests/ryzen_matisse.rs +++ b/src/tests/ryzen_matisse.rs @@ -1268,4 +1268,34 @@ fn remaining_unsupported_leafs() { assert!(cpuid.get_deterministic_address_translation_info().is_none()); assert!(cpuid.get_soc_vendor_info().is_none()); assert!(cpuid.get_extended_topology_info_v2().is_none()); + assert!(cpuid.get_extended_feature_identification_2().is_none()); + assert!(cpuid + .get_extended_performance_monitoring_and_debug() + .is_none()); + assert!(cpuid + .get_multi_key_encrypted_memory_capabilities() + .is_none()); + assert!(cpuid.get_extended_cpu_topology().is_none()); +} + +#[test] +fn platform_quality_service() { + let cpuid = CpuId::with_cpuid_fn(cpuid_reader); + let e = cpuid + .get_pqos_extended_feature_info() + .expect("Leaf is supported"); + + assert!(e.has_l3mbe()); + assert!(!e.has_l3smbe()); + assert!(!e.has_abmc()); + assert!(!e.has_bmec()); + assert!(!e.has_l3rr()); + assert!(!e.has_sdciae()); + + let f = e + .get_l3_memory_bandwidth_enforcement_info() + .expect("SubLeaf is supported"); + + assert!(f.bandwidth_length() == 0x0000_000b); + assert!(f.cos_max() == 0x0000_000f); }