diff --git a/headers/private/kernel/arch/x86/arch_cpu.h b/headers/private/kernel/arch/x86/arch_cpu.h index 160b5b76d9..41781e7e4c 100644 --- a/headers/private/kernel/arch/x86/arch_cpu.h +++ b/headers/private/kernel/arch/x86/arch_cpu.h @@ -57,6 +57,7 @@ #define IA32_MSR_MTRR_DEFAULT_TYPE 0x2ff #define IA32_MSR_MTRR_PHYSICAL_BASE_0 0x200 #define IA32_MSR_MTRR_PHYSICAL_MASK_0 0x201 +#define IA32_MSR_PAT 0x277 // MSR SPEC CTRL bits #define IA32_MSR_SPEC_CTRL_IBRS (1 << 0) @@ -177,6 +178,16 @@ #define IA32_HWP_REQUEST_MAXIMUM_VALID (1ULL << 62) #define IA32_HWP_REQUEST_MINIMUM_VALID (1ULL << 63) +// IA32_MSR_PAT bits +#define IA32_MSR_PAT_ENTRY_MASK 0x7ULL +#define IA32_MSR_PAT_ENTRY_SHIFT(x) (x * 8) +#define IA32_MSR_PAT_TYPE_UNCACHEABLE 0x0ULL +#define IA32_MSR_PAT_TYPE_WRITE_COMBINING 0x1ULL +#define IA32_MSR_PAT_TYPE_WRITE_THROUGH 0x4ULL +#define IA32_MSR_PAT_TYPE_WRITE_PROTECTED 0x5ULL +#define IA32_MSR_PAT_TYPE_WRITE_BACK 0x6ULL +#define IA32_MSR_PAT_TYPE_UNCACHED 0x7ULL + // x86 features from cpuid eax 1, edx register // reference http://www.intel.com/Assets/en_US/PDF/appnote/241618.pdf (Table 5-5) #define IA32_FEATURE_FPU (1 << 0) // x87 fpu @@ -668,6 +679,7 @@ void x86_set_mtrrs(uint8 defaultType, const x86_mtrr_info* infos, uint32 count); void x86_init_fpu(); bool x86_check_feature(uint32 feature, enum x86_feature_type type); +bool x86_use_pat(); void* x86_get_double_fault_stack(int32 cpu, size_t* _size); int32 x86_double_fault_get_cpu(void); diff --git a/headers/private/system/safemode_defs.h b/headers/private/system/safemode_defs.h index 086156e36a..d575f2827a 100644 --- a/headers/private/system/safemode_defs.h +++ b/headers/private/system/safemode_defs.h @@ -15,6 +15,7 @@ #define B_SAFEMODE_DISABLE_SMEP_SMAP "disable_smep_smap" #define B_SAFEMODE_DISABLE_APM "disable_apm" #define B_SAFEMODE_DISABLE_SMP "disable_smp" +#define B_SAFEMODE_DISABLE_PAT "disable_pat" #define B_SAFEMODE_DISABLE_HYPER_THREADING "disable_hyperthreading" #define B_SAFEMODE_FAIL_SAFE_VIDEO_MODE "fail_safe_video_mode" #define B_SAFEMODE_4_GB_MEMORY_LIMIT "4gb_memory_limit" diff --git a/src/system/boot/platform/bios_ia32/smp.cpp b/src/system/boot/platform/bios_ia32/smp.cpp index 9956a2c95a..1f594006ff 100644 --- a/src/system/boot/platform/bios_ia32/smp.cpp +++ b/src/system/boot/platform/bios_ia32/smp.cpp @@ -614,6 +614,16 @@ smp_add_safemode_menus(Menu *menu) } } + cpuid_info info; + if (get_current_cpuid(&info, 1, 0) == B_OK + && (info.regs.edx & IA32_FEATURE_PAT) != 0) { + menu->AddItem(item = new(nothrow) MenuItem("Disable PAT")); + item->SetType(MENU_ITEM_MARKABLE); + item->SetData(B_SAFEMODE_DISABLE_PAT); + item->SetHelpText("Disables using page attribute tables for memory " + "type setting, falling back to MTRRs."); + } + if (gKernelArgs.num_cpus < 2) return; diff --git a/src/system/boot/platform/efi/arch/x86/arch_smp.cpp b/src/system/boot/platform/efi/arch/x86/arch_smp.cpp index fad379d67f..fd0f283c6b 100644 --- a/src/system/boot/platform/efi/arch/x86/arch_smp.cpp +++ b/src/system/boot/platform/efi/arch/x86/arch_smp.cpp @@ -362,6 +362,16 @@ arch_smp_add_safemode_menus(Menu *menu) } } + cpuid_info info; + if (get_current_cpuid(&info, 1, 0) == B_OK + && (info.regs.edx & IA32_FEATURE_PAT) != 0) { + menu->AddItem(item = new(nothrow) MenuItem("Disable PAT")); + item->SetType(MENU_ITEM_MARKABLE); + item->SetData(B_SAFEMODE_DISABLE_PAT); + item->SetHelpText("Disables using page attribute tables for memory " + "type setting, falling back to MTRRs."); + } + if (gKernelArgs.num_cpus < 2) return; diff --git a/src/system/kernel/arch/x86/arch_cpu.cpp b/src/system/kernel/arch/x86/arch_cpu.cpp index 064eaa1556..455dcb8fa0 100644 --- a/src/system/kernel/arch/x86/arch_cpu.cpp +++ b/src/system/kernel/arch/x86/arch_cpu.cpp @@ -128,6 +128,8 @@ static size_t sUcodeDataSize = 0; static void* sLoadedUcodeUpdate; static spinlock sUcodeUpdateLock = B_SPINLOCK_INITIALIZER; +static bool sUsePAT = false; + static status_t acpi_shutdown(bool rebootSystem) @@ -263,6 +265,17 @@ init_mtrrs(void* _unused, int cpu) uint32 x86_count_mtrrs(void) { + if (sUsePAT) { + // When PAT is supported, we completely ignore MTRRs and leave them as + // initialized by firmware. This follows the suggestion in Intel SDM + // that these don't usually need to be touched by anything after system + // init. Using page attributes is the more flexible and modern approach + // to memory type handling and they can override MTRRs in the critical + // case of write-combining, usually used for framebuffers. + dprintf("ignoring MTRRs due to PAT support\n"); + return 0; + } + if (sCpuModule == NULL) return 0; @@ -309,6 +322,25 @@ x86_set_mtrrs(uint8 defaultType, const x86_mtrr_info* infos, uint32 count) } +static void +init_pat(int cpu) +{ + disable_caches(); + + uint64 value = x86_read_msr(IA32_MSR_PAT); + dprintf("PAT MSR on CPU %d before init: %#" B_PRIx64 "\n", cpu, value); + + // Use PAT entry 4 for write-combining, leave the rest as is + value &= ~(IA32_MSR_PAT_ENTRY_MASK << IA32_MSR_PAT_ENTRY_SHIFT(4)); + value |= IA32_MSR_PAT_TYPE_WRITE_COMBINING << IA32_MSR_PAT_ENTRY_SHIFT(4); + + dprintf("PAT MSR on CPU %d after init: %#" B_PRIx64 "\n", cpu, value); + x86_write_msr(IA32_MSR_PAT, value); + + enable_caches(); +} + + void x86_init_fpu(void) { @@ -1476,6 +1508,13 @@ x86_check_feature(uint32 feature, enum x86_feature_type type) } +bool +x86_use_pat() +{ + return sUsePAT; +} + + void* x86_get_double_fault_stack(int32 cpu, size_t* _size) { @@ -1700,6 +1739,22 @@ arch_cpu_init_percpu(kernel_args* args, int cpu) if (x86_check_feature(IA32_FEATURE_MCE, FEATURE_COMMON)) x86_write_cr4(x86_read_cr4() | IA32_CR4_MCE); + if (cpu == 0) { + bool supportsPAT = x86_check_feature(IA32_FEATURE_PAT, FEATURE_COMMON); + sUsePAT = supportsPAT + && !get_safemode_boolean_early(args, B_SAFEMODE_DISABLE_PAT, false); + + if (sUsePAT) { + dprintf("using PAT for memory type configuration\n"); + } else { + dprintf("not using PAT for memory type configuration (%s)\n", + supportsPAT ? "disabled" : "unsupported"); + } + } + + if (sUsePAT) + init_pat(cpu); + #ifdef __x86_64__ // if RDTSCP or RDPID are available write cpu number in TSC_AUX if (x86_check_feature(IA32_FEATURE_AMD_EXT_RDTSCP, FEATURE_EXT_AMD) diff --git a/src/system/kernel/arch/x86/arch_vm.cpp b/src/system/kernel/arch/x86/arch_vm.cpp index 902fa7bbe4..9a6e611172 100644 --- a/src/system/kernel/arch/x86/arch_vm.cpp +++ b/src/system/kernel/arch/x86/arch_vm.cpp @@ -717,8 +717,6 @@ arch_vm_init_post_modules(kernel_args *args) // the x86 CPU modules are now accessible sMemoryTypeRegisterCount = x86_count_mtrrs(); - if (sMemoryTypeRegisterCount == 0) - return B_OK; // not very likely, but play safe here if (sMemoryTypeRegisterCount > kMaxMemoryTypeRegisters) diff --git a/src/system/kernel/arch/x86/paging/32bit/X86PagingMethod32Bit.h b/src/system/kernel/arch/x86/paging/32bit/X86PagingMethod32Bit.h index 7af0f1d3e8..04b43a2185 100644 --- a/src/system/kernel/arch/x86/paging/32bit/X86PagingMethod32Bit.h +++ b/src/system/kernel/arch/x86/paging/32bit/X86PagingMethod32Bit.h @@ -147,18 +147,14 @@ X86PagingMethod32Bit::ClearPageTableEntryFlags(page_table_entry* entry, uint32 f /*static*/ inline uint32 X86PagingMethod32Bit::MemoryTypeToPageTableEntryFlags(uint32 memoryType) { - // ATM we only handle the uncacheable and write-through type explicitly. For - // all other types we rely on the MTRRs to be set up correctly. Since we set - // the default memory type to write-back and since the uncacheable type in - // the PTE overrides any MTRR attribute (though, as per the specs, that is - // not recommended for performance reasons), this reduces the work we - // actually *have* to do with the MTRRs to setting the remaining types - // (usually only write-combining for the frame buffer). switch (memoryType) { case B_MTR_UC: return X86_PTE_CACHING_DISABLED | X86_PTE_WRITE_THROUGH; case B_MTR_WC: + if (x86_use_pat()) + return X86_PTE_PAT; + // X86_PTE_WRITE_THROUGH would be closer, but the combination with // MTRR WC is "implementation defined" for Pentium Pro/II. return 0; diff --git a/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.h b/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.h index e1e4a9c6d2..58c70510fd 100644 --- a/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.h +++ b/src/system/kernel/arch/x86/paging/64bit/X86PagingMethod64Bit.h @@ -167,21 +167,12 @@ X86PagingMethod64Bit::ClearTableEntryFlags(uint64_t* entryPointer, /*static*/ inline uint64 X86PagingMethod64Bit::MemoryTypeToPageTableEntryFlags(uint32 memoryType) { - // ATM we only handle the uncacheable and write-through type explicitly. For - // all other types we rely on the MTRRs to be set up correctly. Since we set - // the default memory type to write-back and since the uncacheable type in - // the PTE overrides any MTRR attribute (though, as per the specs, that is - // not recommended for performance reasons), this reduces the work we - // actually *have* to do with the MTRRs to setting the remaining types - // (usually only write-combining for the frame buffer). switch (memoryType) { case B_MTR_UC: return X86_64_PTE_CACHING_DISABLED | X86_64_PTE_WRITE_THROUGH; case B_MTR_WC: - // X86_PTE_WRITE_THROUGH would be closer, but the combination with - // MTRR WC is "implementation defined" for Pentium Pro/II. - return 0; + return x86_use_pat() ? X86_64_PTE_PAT : 0; case B_MTR_WT: return X86_64_PTE_WRITE_THROUGH; diff --git a/src/system/kernel/arch/x86/paging/pae/X86PagingMethodPAE.h b/src/system/kernel/arch/x86/paging/pae/X86PagingMethodPAE.h index bcc25f6696..7fd288a803 100644 --- a/src/system/kernel/arch/x86/paging/pae/X86PagingMethodPAE.h +++ b/src/system/kernel/arch/x86/paging/pae/X86PagingMethodPAE.h @@ -192,18 +192,14 @@ X86PagingMethodPAE::ClearTableEntryFlags(uint64_t* entry, uint64_t flags) /*static*/ inline uint64 X86PagingMethodPAE::MemoryTypeToPageTableEntryFlags(uint32 memoryType) { - // ATM we only handle the uncacheable and write-through type explicitly. For - // all other types we rely on the MTRRs to be set up correctly. Since we set - // the default memory type to write-back and since the uncacheable type in - // the PTE overrides any MTRR attribute (though, as per the specs, that is - // not recommended for performance reasons), this reduces the work we - // actually *have* to do with the MTRRs to setting the remaining types - // (usually only write-combining for the frame buffer). switch (memoryType) { case B_MTR_UC: return X86_PAE_PTE_CACHING_DISABLED | X86_PAE_PTE_WRITE_THROUGH; case B_MTR_WC: + if (x86_use_pat()) + return X86_PAE_PTE_PAT; + // X86_PTE_WRITE_THROUGH would be closer, but the combination with // MTRR WC is "implementation defined" for Pentium Pro/II. return 0;