From 8c00a4145c5bc44c2df2f77412340f242f2d58b8 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Sat, 14 Sep 2013 17:10:14 +0200 Subject: [PATCH] Refactor kernel PAT, MTRR, and MSR usage. --- kernel/Makefile | 3 +- kernel/include/sortix/kernel/cpuid.h | 80 +++++ .../msr.h => include/sortix/kernel/mtrr.h} | 23 +- kernel/include/sortix/kernel/pat.h | 39 +++ kernel/x86-family/memorymanagement.cpp | 5 +- kernel/x86-family/msr.cpp | 274 ------------------ kernel/x86-family/mtrr.cpp | 194 +++++++++++++ kernel/x86-family/pat.cpp | 57 ++++ libc/include/msr.h | 73 +++++ 9 files changed, 462 insertions(+), 286 deletions(-) create mode 100644 kernel/include/sortix/kernel/cpuid.h rename kernel/{x86-family/msr.h => include/sortix/kernel/mtrr.h} (76%) create mode 100644 kernel/include/sortix/kernel/pat.h delete mode 100644 kernel/x86-family/msr.cpp create mode 100644 kernel/x86-family/mtrr.cpp create mode 100644 kernel/x86-family/pat.cpp create mode 100644 libc/include/msr.h diff --git a/kernel/Makefile b/kernel/Makefile index f97c4ca8..621bac3d 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -54,7 +54,8 @@ ifdef X86FAMILY $(CPU)/process.o \ x86-family/cmos.o \ x86-family/time.o \ - x86-family/msr.o \ + x86-family/mtrr.o \ + x86-family/pat.o \ x86-family/float.o \ x86-family/x86-family.o # TODO: Are these -m flags even needed in the first place? diff --git a/kernel/include/sortix/kernel/cpuid.h b/kernel/include/sortix/kernel/cpuid.h new file mode 100644 index 00000000..9f14fbef --- /dev/null +++ b/kernel/include/sortix/kernel/cpuid.h @@ -0,0 +1,80 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/kernel/cpuid.h + Interface to using the cpuid instruction. + +*******************************************************************************/ + +#ifndef INCLUDE_SORTIX_KERNEL_CPUID_H +#define INCLUDE_SORTIX_KERNEL_CPUID_H + +#include + +namespace Sortix { + +#if defined(__i386__) || defined(__x86_64__) + +__attribute__((const)) +__attribute__((unused)) +static inline bool IsCPUIdSupported() +{ +#if defined(__x86_64__) + // TODO: Isn't this always supported under x86_64? + uint64_t id_supported; + asm ("pushfq\n\t" + "popq %%rax /* Get EFLAGS into EAX */\n\t" + "movq %%rax, %%rcx /* Save original flags in ECX */\n\t" + "xorq $0x200000, %%rax /* Flip ID bit in EFLAGS */\n\t" + "pushq %%rax /* Store modified EFLAGS on stack */\n\t" + "popfq /* Replace current EFLAGS */\n\t" + "pushfq /* Read back the EFLAGS */\n\t" + "popq %%rax /* Get EFLAGS into EAX */\n\t" + "xorq %%rcx, %%rax /* Check if flag could be modified */\n\t" + : "=a" (id_supported) + : /* No inputs. */ + : /* Clobbered: */ "%rcx"); +#elif defined(__i386__) + uint32_t id_supported; + asm ("pushfl\n\t" + "popl %%eax /* Get EFLAGS into EAX */\n\t" + "movl %%eax, %%ecx /* Save original flags in ECX */\n\t" + "xorl $0x200000, %%eax /* Flip ID bit in EFLAGS */\n\t" + "pushl %%eax /* Store modified EFLAGS on stack */\n\t" + "popfl /* Replace current EFLAGS */\n\t" + "pushfl /* Read back the EFLAGS */\n\t" + "popl %%eax /* Get EFLAGS into EAX */\n\t" + "xorl %%ecx, %%eax /* Check if flag could be modified */\n\t" + : "=a" (id_supported) + : /* No inputs. */ + : /* Clobbered: */ "%rcx"); +#endif + return id_supported != 0; +} + +#define cpuid(num,a,b,c,d) \ + asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1" \ + : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ + : "0" (num)) + +#endif + +} // namespace Sortix + +#endif diff --git a/kernel/x86-family/msr.h b/kernel/include/sortix/kernel/mtrr.h similarity index 76% rename from kernel/x86-family/msr.h rename to kernel/include/sortix/kernel/mtrr.h index 40b0e515..e68d3abc 100644 --- a/kernel/x86-family/msr.h +++ b/kernel/include/sortix/kernel/mtrr.h @@ -1,6 +1,6 @@ /******************************************************************************* - Copyright(C) Jonas 'Sortie' Termansen 2012. + Copyright(C) Jonas 'Sortie' Termansen 2013. This file is part of Sortix. @@ -17,26 +17,31 @@ You should have received a copy of the GNU General Public License along with Sortix. If not, see . - x86-family/msr.h - Functions to manipulate Model Specific Registers. + sortix/kernel/mtrr.h + Functions to manipulate Memory Type Range Registers. *******************************************************************************/ -#ifndef SORTIX_X86_FAMILY_MSR_H -#define SORTIX_X86_FAMILY_MSR_H +#ifndef INCLUDE_SORTIX_KERNEL_MTRR_H +#define INCLUDE_SORTIX_KERNEL_MTRR_H + +#include +#include + +#include namespace Sortix { -namespace MSR { -bool IsPATSupported(); -void InitializePAT(); +#if defined(__i386__) || defined(__x86_64__) + bool IsMTRRSupported(); const char* SetupMTRRForWC(addr_t base, size_t size, int* ret = NULL); void EnableMTRR(int mtrr); void DisableMTRR(int mtrr); void CopyMTRR(int dst, int src); -} // namespace MSR +#endif + } // namespace Sortix #endif diff --git a/kernel/include/sortix/kernel/pat.h b/kernel/include/sortix/kernel/pat.h new file mode 100644 index 00000000..ff3f3810 --- /dev/null +++ b/kernel/include/sortix/kernel/pat.h @@ -0,0 +1,39 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + sortix/kernel/pat.h + PAT feature. + +*******************************************************************************/ + +#ifndef INCLUDE_SORTIX_KERNEL_PAT_H +#define INCLUDE_SORTIX_KERNEL_PAT_H + +namespace Sortix { + +#if defined(__i386__) || defined(__x86_64__) + +bool IsPATSupported(); +void InitializePAT(); + +#endif + +} // namespace Sortix + +#endif diff --git a/kernel/x86-family/memorymanagement.cpp b/kernel/x86-family/memorymanagement.cpp index e60e813e..e98f4490 100644 --- a/kernel/x86-family/memorymanagement.cpp +++ b/kernel/x86-family/memorymanagement.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include "multiboot.h" @@ -92,9 +93,9 @@ void InitCPU(multiboot_info_t* bootinfo) // If supported, setup the Page Attribute Table feature that allows // us to control the memory type (caching) of memory more precisely. - if ( MSR::IsPATSupported() ) + if ( IsPATSupported() ) { - MSR::InitializePAT(); + InitializePAT(); for ( addr_t i = 0; i < PAT_NUM; i++ ) PAT2PMLFlags[i] = EncodePATAsPMLFlag(i); } diff --git a/kernel/x86-family/msr.cpp b/kernel/x86-family/msr.cpp deleted file mode 100644 index 7f8ea857..00000000 --- a/kernel/x86-family/msr.cpp +++ /dev/null @@ -1,274 +0,0 @@ -/******************************************************************************* - - Copyright(C) Jonas 'Sortie' Termansen 2012. - Copyright(C) Free Software Foundation, Inc. 2005, 2006, 2007, 2008, 2009. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along with - Sortix. If not, see . - - x86-family/msr.cpp - Functions to manipulate Model Specific Registers. MTRR code is partially - based on code from GNU GRUB. - -*******************************************************************************/ - -#include - -#include "memorymanagement.h" - -namespace Sortix { -namespace MSR { - -const uint32_t bit_MTRR = 0x00001000U; -const uint32_t bit_PAT = 0x00010000U; - -// TODO: Move this to a better location or use . -static inline bool IsCPUIdSupported() -{ -#ifdef __x86_64__ - // TODO: Isn't this always supported under x86_64? - uint64_t id_supported; - asm ("pushfq\n\t" - "popq %%rax /* Get EFLAGS into EAX */\n\t" - "movq %%rax, %%rcx /* Save original flags in ECX */\n\t" - "xorq $0x200000, %%rax /* Flip ID bit in EFLAGS */\n\t" - "pushq %%rax /* Store modified EFLAGS on stack */\n\t" - "popfq /* Replace current EFLAGS */\n\t" - "pushfq /* Read back the EFLAGS */\n\t" - "popq %%rax /* Get EFLAGS into EAX */\n\t" - "xorq %%rcx, %%rax /* Check if flag could be modified */\n\t" - : "=a" (id_supported) - : /* No inputs. */ - : /* Clobbered: */ "%rcx"); -#else - uint32_t id_supported; - asm ("pushfl\n\t" - "popl %%eax /* Get EFLAGS into EAX */\n\t" - "movl %%eax, %%ecx /* Save original flags in ECX */\n\t" - "xorl $0x200000, %%eax /* Flip ID bit in EFLAGS */\n\t" - "pushl %%eax /* Store modified EFLAGS on stack */\n\t" - "popfl /* Replace current EFLAGS */\n\t" - "pushfl /* Read back the EFLAGS */\n\t" - "popl %%eax /* Get EFLAGS into EAX */\n\t" - "xorl %%ecx, %%eax /* Check if flag could be modified */\n\t" - : "=a" (id_supported) - : /* No inputs. */ - : /* Clobbered: */ "%rcx"); -#endif - return id_supported != 0; -} - -#define cpuid(num,a,b,c,d) \ - asm volatile ("xchgl %%ebx, %1; cpuid; xchgl %%ebx, %1" \ - : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ - : "0" (num)) - -#define rdmsr(num,a,d) \ - asm volatile ("rdmsr" : "=a" (a), "=d" (d) : "c" (num)) - -#define wrmsr(num,lo,hi) \ - asm volatile ("wrmsr" : : "c" (num), "a" (lo), "d" (hi) : "memory") - -#define mtrr_base(reg) (0x200 + (reg) * 2) -#define mtrr_mask(reg) (0x200 + (reg) * 2 + 1) - -void EnableMTRR(int mtrr) -{ - uint32_t eax, edx; - uint32_t mask_lo, mask_hi; - - rdmsr(mtrr_mask(mtrr), eax, edx); - mask_lo = eax; - mask_hi = edx; - - mask_lo |= 0x800 /* valid */; - wrmsr(mtrr_mask(mtrr), mask_lo, mask_hi); -} - -void DisableMTRR(int mtrr) -{ - uint32_t eax, edx; - uint32_t mask_lo, mask_hi; - - rdmsr(mtrr_mask(mtrr), eax, edx); - mask_lo = eax; - mask_hi = edx; - - mask_lo &= ~0x800 /* valid */; - wrmsr(mtrr_mask(mtrr), mask_lo, mask_hi); -} - -void CopyMTRR(int dst, int src) -{ - uint32_t base_lo, base_hi; - uint32_t mask_lo, mask_hi; - rdmsr(mtrr_base(src), base_lo, base_hi); - rdmsr(mtrr_mask(src), mask_lo, mask_hi); - wrmsr(mtrr_base(dst), base_lo, base_hi); - wrmsr(mtrr_mask(dst), mask_lo, mask_hi); -} - -bool IsPATSupported() -{ - if ( !IsCPUIdSupported() ) { return false; } - uint32_t eax, ebx, ecx, edx; - cpuid(1, eax, ebx, ecx, edx); - uint32_t features = edx; - return features & bit_PAT; -} - -void InitializePAT() -{ - using namespace Sortix::Memory; - const uint32_t LO = PA[0] << 0 | PA[1] << 8 | PA[2] << 16 | PA[3] << 24; - const uint32_t HI = PA[4] << 0 | PA[5] << 8 | PA[6] << 16 | PA[7] << 24; - const int PAT_REG = 0x0277; - wrmsr(PAT_REG, LO, HI); -} - -bool IsMTRRSupported() -{ - if ( !IsCPUIdSupported() ) { return false; } - uint32_t eax, ebx, ecx, edx; - cpuid(1, eax, ebx, ecx, edx); - uint32_t features = edx; - return features & bit_MTRR; -} - -// TODO: Yes, returning a string as an error and giving the result in a pointer -// is very bad design. Please fix this at some point. Also improve the code such -// that it is more flexible. -const char* SetupMTRRForWC(addr_t base, size_t size, int* ret) -{ - uint32_t eax, ebx, ecx, edx; - uint32_t mtrrcap; - int var_mtrrs; - uint32_t max_extended_cpuid; - uint32_t maxphyaddr; - uint64_t fb_base, fb_size; - uint64_t size_bits, fb_mask; - uint32_t bits_lo, bits_hi; - uint64_t bits; - int i, first_unused = -1; - uint32_t base_lo, base_hi, mask_lo, mask_hi; - - fb_base = (uint64_t) base; - fb_size = (uint64_t) size; - - // Check that fb_base and fb_size can be represented using a single MTRR. - - if ( fb_base < (1 << 20) ) - return "below 1MB, so covered by fixed-range MTRRs"; - if ( fb_base >= (1LL << 36) ) - return "over 36 bits, so out of range"; - if ( fb_size < (1 << 12) ) - return "variable-range MTRRs must cover at least 4KB"; - - size_bits = fb_size; - while ( size_bits > 1 ) - size_bits >>= 1; - if ( size_bits != 1 ) - return "not a power of two"; - - if ( fb_base & (fb_size - 1) ) - return "not aligned on size boundary"; - - fb_mask = ~(fb_size - 1); - - // Check CPU capabilities. - - if ( !IsCPUIdSupported() ) - return "cpuid not supported, therefore mtrr not supported"; - - if ( !IsMTRRSupported() ) - return "cpu does not support mtrr"; - - rdmsr(0xFE, eax, edx); - mtrrcap = eax; - if ( !(mtrrcap & 0x00000400) ) /* write-combining */ - return "write combining doesn't seem to be supported"; - var_mtrrs = (mtrrcap & 0xFF); - - cpuid (0x80000000, eax, ebx, ecx, edx); - max_extended_cpuid = eax; - if ( max_extended_cpuid >= 0x80000008 ) - { - cpuid(0x80000008, eax, ebx, ecx, edx); - maxphyaddr = (eax & 0xFF); - } - else - maxphyaddr = 36; - bits_lo = 0xFFFFF000; /* assume maxphyaddr >= 36 */ - bits_hi = (1 << (maxphyaddr - 32)) - 1; - bits = bits_lo | ((uint64_t) bits_hi << 32); - - // Check whether an MTRR already covers this region. If not, take an unused - // one if possible. - for ( i = 0; i < var_mtrrs; i++ ) - { - rdmsr(mtrr_mask (i), eax, edx); - mask_lo = eax; - mask_hi = edx; - if ( mask_lo & 0x800 ) /* valid */ - { - uint64_t real_base, real_mask; - - rdmsr(mtrr_base(i), eax, edx); - base_lo = eax; - base_hi = edx; - - real_base = ((uint64_t) (base_hi & bits_hi) << 32) | - (base_lo & bits_lo); - real_mask = ((uint64_t) (mask_hi & bits_hi) << 32) | - (mask_lo & bits_lo); - - if ( real_base < (fb_base + fb_size) && - real_base + (~real_mask & bits) >= fb_base) - return "region already covered by another mtrr"; - } - else if ( first_unused < 0 ) - first_unused = i; - } - - if ( first_unused < 0 ) - return "all MTRRs in use"; - - // Set up the first unused MTRR we found. - rdmsr(mtrr_base(first_unused), eax, edx); - base_lo = eax; - base_hi = edx; - rdmsr(mtrr_mask(first_unused), eax, edx); - mask_lo = eax; - mask_hi = edx; - - base_lo = (base_lo & ~bits_lo & ~0xFF) | - (fb_base & bits_lo) | 0x01 /* WC */; - base_hi = (base_hi & ~bits_hi) | - ((fb_base >> 32) & bits_hi); - wrmsr(mtrr_base(first_unused), base_lo, base_hi); - mask_lo = (mask_lo & ~bits_lo) | - (fb_mask & bits_lo) | 0x800 /* valid */; - mask_hi = (mask_hi & ~bits_hi) | - ((fb_mask >> 32) & bits_hi); - wrmsr(mtrr_mask(first_unused), mask_lo, mask_hi); - - if ( ret ) - *ret = first_unused; - - return NULL; -} - -} // namespace MSR -} // namespace Sortix diff --git a/kernel/x86-family/mtrr.cpp b/kernel/x86-family/mtrr.cpp new file mode 100644 index 00000000..df6062fe --- /dev/null +++ b/kernel/x86-family/mtrr.cpp @@ -0,0 +1,194 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. + Copyright(C) Free Software Foundation, Inc. 2005, 2006, 2007, 2008, 2009. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + x86-family/mtrr.cpp + Functions to access and modify memory type range registers. This is in part + based on code from GNU GRUB. + +*******************************************************************************/ + +#include +#include + +#include +#include +#include + +namespace Sortix { + +const uint32_t bit_MTRR = 0x00001000U; + +bool IsMTRRSupported() +{ + if ( !IsCPUIdSupported() ) + return false; + uint32_t eax, ebx, ecx, edx; + cpuid(1, eax, ebx, ecx, edx); + uint32_t features = edx; + return features & bit_MTRR; +} + +#define mtrr_base(reg) (0x200 + (reg) * 2 + 0) +#define mtrr_mask(reg) (0x200 + (reg) * 2 + 1) + +static const uint64_t MTRR_MASK_VALID = 0x800; + +void EnableMTRR(int mtrr) +{ + wrmsr(mtrr_mask(mtrr), rdmsr(mtrr_mask(mtrr)) | MTRR_MASK_VALID); +} + +void DisableMTRR(int mtrr) +{ + wrmsr(mtrr_mask(mtrr), rdmsr(mtrr_mask(mtrr)) & ~MTRR_MASK_VALID); +} + +void CopyMTRR(int dst, int src) +{ + uint64_t base = rdmsr(mtrr_base(src)); + uint64_t mask = rdmsr(mtrr_mask(src)); + wrmsr(mtrr_base(dst), base); + wrmsr(mtrr_mask(dst), mask); +} + +// TODO: Yes, returning a string as an error and giving the result in a pointer +// is very bad design. Please fix this at some point. Also improve the code such +// that it is more flexible. +const char* SetupMTRRForWC(addr_t base, size_t size, int* ret) +{ + uint32_t eax, ebx, ecx, edx; + uint32_t mtrrcap; + int var_mtrrs; + uint32_t max_extended_cpuid; + uint32_t maxphyaddr; + uint64_t fb_base, fb_size; + uint64_t size_bits, fb_mask; + uint32_t bits_lo, bits_hi; + uint64_t bits; + int i, first_unused = -1; + uint32_t base_lo, base_hi, mask_lo, mask_hi; + + fb_base = (uint64_t) base; + fb_size = (uint64_t) size; + + // Check that fb_base and fb_size can be represented using a single MTRR. + + if ( fb_base < 1 << 20 ) + return "below 1MB, so covered by fixed-range MTRRs"; + if ( fb_base >= 1LL << 36 ) + return "over 36 bits, so out of range"; + if ( fb_size < 1 << 12 ) + return "variable-range MTRRs must cover at least 4KB"; + + size_bits = fb_size; + while ( size_bits > 1 ) + size_bits >>= 1; + if ( size_bits != 1 ) + return "not a power of two"; + + if ( fb_base & (fb_size - 1) ) + return "not aligned on size boundary"; + + fb_mask = ~(fb_size - 1); + + // Check CPU capabilities. + + if ( !IsCPUIdSupported() ) + return "cpuid not supported, therefore mtrr not supported"; + + if ( !IsMTRRSupported() ) + return "cpu does not support mtrr"; + + rdmsr_split(0xFE, &eax, &edx); + mtrrcap = eax; + if ( !(mtrrcap & 0x00000400) ) /* write-combining */ + return "write combining doesn't seem to be supported"; + var_mtrrs = mtrrcap & 0xFF; + + cpuid(0x80000000, eax, ebx, ecx, edx); + max_extended_cpuid = eax; + if ( max_extended_cpuid >= 0x80000008 ) + { + cpuid(0x80000008, eax, ebx, ecx, edx); + maxphyaddr = eax & 0xFF; + } + else + maxphyaddr = 36; + bits_lo = 0xFFFFF000; /* assume maxphyaddr >= 36 */ + bits_hi = (1 << (maxphyaddr - 32)) - 1; + bits = bits_lo | (uint64_t) bits_hi << 32; + + // Check whether an MTRR already covers this region. If not, take an unused + // one if possible. + for ( i = 0; i < var_mtrrs; i++ ) + { + rdmsr_split(mtrr_mask(i), &eax, &edx); + mask_lo = eax; + mask_hi = edx; + if ( mask_lo & MTRR_MASK_VALID ) + { + uint64_t real_base, real_mask; + + rdmsr_split(mtrr_base(i), &eax, &edx); + base_lo = eax; + base_hi = edx; + + real_base = (uint64_t) (base_hi & bits_hi) << 32 | + (base_lo & bits_lo); + real_mask = (uint64_t) (mask_hi & bits_hi) << 32 | + (mask_lo & bits_lo); + + if ( real_base < fb_base + fb_size && + real_base + (~real_mask & bits) >= fb_base ) + return "region already covered by another mtrr"; + } + else if ( first_unused < 0 ) + first_unused = i; + } + + if ( first_unused < 0 ) + return "all MTRRs in use"; + + // Set up the first unused MTRR we found. + rdmsr_split(mtrr_base(first_unused), &eax, &edx); + base_lo = eax; + base_hi = edx; + rdmsr_split(mtrr_mask(first_unused), &eax, &edx); + mask_lo = eax; + mask_hi = edx; + + base_lo = (base_lo & ~bits_lo & ~0xFF) | + (fb_base & bits_lo) | 0x01 /* WC */; + base_hi = (base_hi & ~bits_hi) | + ((fb_base >> 32) & bits_hi); + wrmsr_split(mtrr_base(first_unused), base_lo, base_hi); + mask_lo = (mask_lo & ~bits_lo) | + (fb_mask & bits_lo) | MTRR_MASK_VALID; + mask_hi = (mask_hi & ~bits_hi) | + ((fb_mask >> 32) & bits_hi); + wrmsr_split(mtrr_mask(first_unused), mask_lo, mask_hi); + + if ( ret ) + *ret = first_unused; + + return NULL; +} + +} // namespace Sortix diff --git a/kernel/x86-family/pat.cpp b/kernel/x86-family/pat.cpp new file mode 100644 index 00000000..6c3d8d3e --- /dev/null +++ b/kernel/x86-family/pat.cpp @@ -0,0 +1,57 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012, 2013. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . + + x86-family/pat.cpp + Tests whether PAT is available and initializes it. + +*******************************************************************************/ + +#include +#include + +#include +#include +#include + +#include "memorymanagement.h" + +namespace Sortix { + +static const uint32_t bit_PAT = 0x00010000U; + +bool IsPATSupported() +{ + if ( !IsCPUIdSupported() ) + return false; + uint32_t eax, ebx, ecx, edx; + cpuid(1, eax, ebx, ecx, edx); + uint32_t features = edx; + return features & bit_PAT; +} + +void InitializePAT() +{ + using namespace Sortix::Memory; + const uint32_t LOW = PA[0] << 0 | PA[1] << 8 | PA[2] << 16 | PA[3] << 24; + const uint32_t HIGH = PA[4] << 0 | PA[5] << 8 | PA[6] << 16 | PA[7] << 24; + const int PAT_REG = 0x0277; + wrmsr(PAT_REG, (uint64_t) LOW << 0 | (uint64_t) HIGH << 32); +} + +} // namespace Sortix diff --git a/libc/include/msr.h b/libc/include/msr.h new file mode 100644 index 00000000..7cb5c3c3 --- /dev/null +++ b/libc/include/msr.h @@ -0,0 +1,73 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2013. + + This file is part of the Sortix C Library. + + The Sortix C Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or (at your + option) any later version. + + The Sortix C Library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with the Sortix C Library. If not, see . + + msr.h + High-level access to the rdmsr and wrmsr instructions for accessing and + modifying machine specific registers. + +*******************************************************************************/ + +#ifndef INCLUDE_MSR_H +#define INCLUDE_MSR_H + +#include + +#include + +#if defined(__i386__) || defined(__x86_64__) + +__BEGIN_DECLS + +__attribute__((unused)) +static inline uint64_t rdmsr(uint32_t msrid) +{ + uint32_t low; + uint32_t high; + asm volatile ("rdmsr" : "=a"(low), "=d"(high) : "c"(msrid)); + return (uint64_t) low << 0 | (uint64_t) high << 32; +} + +__attribute__((unused)) +static inline uint64_t wrmsr(uint32_t msrid, uint64_t value) +{ + uint32_t low = value >> 0 & 0xFFFFFFFF; + uint32_t high = value >> 32 & 0xFFFFFFFF;; + asm volatile ("wrmsr" : : "a"(low), "d"(high), "c"(msrid) : "memory"); + return value; +} + +__attribute__((unused)) +static inline void rdmsr_split(uint32_t msrid, uint32_t* low, uint32_t* high) +{ + uint64_t result = rdmsr(msrid); + *low = result >> 0 & 0xFFFFFFFF; + *high = result >> 32 & 0xFFFFFFFF; +} + +__attribute__((unused)) +static inline void wrmsr_split(uint32_t msrid, uint32_t low, uint32_t high) +{ + wrmsr(msrid, (uint64_t) low << 0 | (uint64_t) high << 32); +} + +__END_DECLS + +#endif + +#endif