fix very very insidious bug with mapping physical memory (TLB corruption). fix weirdness reported by michael (underlying cause: using cpu_ClockFrequency instead of wcpu_*; that made the timer run at freq=0)

mahaf: now disabled on Win2k because we can't prevent the TLB bug there.
removed the CopyPhysical API.
wutil: allow querying windows version via number

View: fix warning

This was SVN commit r5169.
This commit is contained in:
janwas
2007-06-12 23:29:27 +00:00
parent 52aa29681c
commit f331b7313d
23 changed files with 137 additions and 126 deletions
+1 -2
View File
@@ -22,7 +22,6 @@
#include "lib/posix/posix_pthread.h"
#include "lib/sysdep/cpu.h" // CAS
#include "lib/sysdep/sysdep.h"
#include "lib/res/file/file.h" // FILE_ACCESS
// some functions here are called from within mmgr; disable its hooks
// so that our allocations don't cause infinite recursion.
#ifdef REDEFINED_NEW
@@ -237,7 +236,7 @@ LibError debug_write_crashlog(const wchar_t* text)
if(!f)
{
in_progress = 0;
WARN_RETURN(ERR::FILE_ACCESS);
WARN_RETURN(ERR::FAIL);
}
fputwc(0xfeff, f); // BOM
+32 -29
View File
@@ -28,32 +28,27 @@ static u8 ComputeChecksum(const void* buf, size_t numBytes)
// free() the returned pointer.
static const AcpiTable* AllocateCopyOfTable(u64 physicalAddress)
{
// ACPI table sizes are not known until they've been mapped and copied.
// since that is slow, we don't want to do it twice; solution is to copy
// into a buffer big enough to hold a typical table. if it should be too
// small, we map and copy again. using a temporary buffer, rather than
// allocating that much up-front, avoids wasting memory for each table.
static const size_t tempTableSize = 4*KiB;
static u8 tempTableBuf[tempTableSize];
const AcpiTable* tempTable = (const AcpiTable*)tempTableBuf;
if(!mahaf_CopyPhysicalMemory(physicalAddress, tempTableSize, (void*)tempTable))
// ACPI table sizes are not known until they've been mapped. since that
// is slow, we don't want to do it twice; solution is map enough to
// hold a typical table. if it should be too small, we do so again.
static const size_t initialSize = 4*KiB;
const volatile AcpiTable* mappedTable = (const volatile AcpiTable*)mahaf_MapPhysicalMemory(physicalAddress, initialSize);
if(!mappedTable)
return 0;
// allocate the final table
const size_t size = tempTable->size;
const AcpiTable* table = (const AcpiTable*)malloc(size);
if(!table)
return 0;
// and fill it.
if(size <= tempTableSize)
cpu_memcpy((void*)table, tempTable, size);
else
const size_t size = mappedTable->size;
if(initialSize < size)
{
if(!mahaf_CopyPhysicalMemory(physicalAddress, size, (void*)table))
mappedTable = (const volatile AcpiTable*)mahaf_MapPhysicalMemory(physicalAddress, size);
if(!mappedTable)
return 0;
}
const AcpiTable* table = (const AcpiTable*)malloc(size);
if(table)
cpu_memcpy((void*)table, (const void*)mappedTable, size);
mahaf_UnmapPhysicalMemory((volatile void*)mappedTable);
return table;
}
@@ -128,31 +123,37 @@ static const RSDP* LocateRsdp(const u8* buf, size_t numBytes)
static bool LocateAndRetrieveRsdp(uintptr_t physicalAddress, size_t numBytes, RSDP& rsdp)
{
void* virtualAddress = mahaf_MapPhysicalMemory(physicalAddress, numBytes);
const volatile u8* buffer = (const volatile u8*)mahaf_MapPhysicalMemory(physicalAddress, numBytes);
if(!buffer)
return false;
const RSDP* prsdp = LocateRsdp((const u8*)virtualAddress, numBytes);
const RSDP* prsdp = LocateRsdp((const u8*)buffer, numBytes);
if(prsdp)
rsdp = *prsdp; // stash in output parameter before unmapping
mahaf_UnmapPhysicalMemory(virtualAddress);
mahaf_UnmapPhysicalMemory((volatile void*)buffer);
return (prsdp != 0);
}
static uintptr_t LocateEbdaPhysicalAddress()
{
#pragma pack(push, 1)
struct BiosDataArea
{
u16 serialBase[4];
u16 parallelBase[3];
u16 ebdaSegment;
// ...
};
const BiosDataArea* bda = (const BiosDataArea*)mahaf_MapPhysicalMemory(0x400, 0x100);
#pragma pack(pop)
const volatile BiosDataArea* bda = (const volatile BiosDataArea*)mahaf_MapPhysicalMemory(0x400, 0x100);
if(!bda)
{
debug_assert(0);
return 0;
const uintptr_t ebdaPhysicalAddress = ((uintptr_t)bda->ebdaSegment) * 16;
}
const uintptr_t ebdaPhysicalAddress = ((uintptr_t)bda->ebdaSegment) * 16;
mahaf_UnmapPhysicalMemory((void*)bda);
return ebdaPhysicalAddress;
}
@@ -161,7 +162,9 @@ static bool RetrieveRsdp(RSDP& rsdp)
{
// See ACPIspec30b, section 5.2.5.1:
// RSDP is either in the first KIB of the extended BIOS data area,
if(LocateAndRetrieveRsdp(LocateEbdaPhysicalAddress(), 1*KiB, rsdp))
const uintptr_t ebdaPhysicalAddress = LocateEbdaPhysicalAddress();
debug_assert(ebdaPhysicalAddress != 0);
if(LocateAndRetrieveRsdp(ebdaPhysicalAddress, 1*KiB, rsdp))
return true;
// or in read-only BIOS memory.
+51 -31
View File
@@ -13,8 +13,48 @@ extern "C" { // must come before ntddk.h
// memory mapping
//-----------------------------------------------------------------------------
// references: DDK mapmem.c sample,
// http://support.microsoft.com/kb/189327/en-us
/*
there are three approaches to mapping physical memory:
(http://www.microsoft.com/whdc/driver/kernel/mem-mgmt.mspx)
- MmMapIoSpace (http://support.microsoft.com/kb/189327/en-us). despite the
name, it maps physical pages of any kind by allocating PTEs. very easy to
implement, but occupies precious kernel address space. possible bugs:
http://www.osronline.com/showThread.cfm?link=96737
http://support.microsoft.com/kb/925793/en-us
- ZwMapViewOfSection of PhysicalMemory (http://tinyurl.com/yozmgy).
a bit bulky, but the WinXP implementation prevents mapping pages with
conflicting attributes (see below).
- MmMapLockedPagesSpecifyCache or MmGetSystemAddressForMdlSafe
(http://www.osronline.com/article.cfm?id=423). note: the latter is a macro
that calls the former. this is the 'normal' and fully documented way,
but it doesn't appear able to map a fixed physical address.
(MmAllocatePagesForMdl understandably doesn't work since some pages we
want to map are marked as unavailable for allocation, and I don't see
another documented way to fill an MDL with PFNs.)
our choice here is forced by a very insidious issue. if someone else has
already mapped a page with different attributes (e.g. cacheable), TLBs
may end up corrupted, leading to disaster. the search for a documented
means of accessing the page frame database (to check if mapped anywhere
and determine the previously set attributes) has not borne fruit, so we
must use ZwMapViewOfSection.
*/
static bool IsMemoryUncacheable(DWORD64 physicalAddress64)
{
// original PC memory - contains BIOS
if(physicalAddress64 < 0x100000)
return true;
return false;
}
static NTSTATUS AkenMapPhysicalMemory(const DWORD64 physicalAddress64, const DWORD64 numBytes64, DWORD64& virtualAddress64)
{
NTSTATUS ntStatus;
@@ -51,8 +91,11 @@ static NTSTATUS AkenMapPhysicalMemory(const DWORD64 physicalAddress64, const DWO
}
}
// map desired memory into user PTEs (note: don't use MmMapIoSpace
// because that occupies precious non-paged pool)
// note: mapmem.c does HalTranslateBusAddress, but we only care about
// system memory. translating doesn't appear to be necessary, even if
// much existing code uses it (probably due to cargo cult).
// map desired memory into user PTEs
{
const HANDLE hProcess = (HANDLE)-1;
PVOID virtualBaseAddress = 0; // let ZwMapViewOfSection pick
@@ -61,7 +104,9 @@ static NTSTATUS AkenMapPhysicalMemory(const DWORD64 physicalAddress64, const DWO
LARGE_INTEGER physicalBaseAddress = physicalAddress; // will be rounded down to 64KB boundary
const SECTION_INHERIT inheritDisposition = ViewShare;
const ULONG allocationType = 0;
const ULONG protect = PAGE_READWRITE|PAGE_NOCACHE;
ULONG protect = PAGE_READWRITE;
if(IsMemoryUncacheable(physicalAddress64))
protect |= PAGE_NOCACHE;
ntStatus = ZwMapViewOfSection(hMemory, hProcess, &virtualBaseAddress, zeroBits, mappedSize, &physicalBaseAddress, &mappedSize, inheritDisposition, allocationType, protect);
if(!NT_SUCCESS(ntStatus))
{
@@ -173,6 +218,7 @@ static NTSTATUS AkenIoctlMap(PVOID buf, const ULONG inSize, ULONG& outSize)
const AkenMapIn* in = (const AkenMapIn*)buf;
const DWORD64 physicalAddress = in->physicalAddress;
const DWORD64 numBytes = in->numBytes;
DWORD64 virtualAddress;
NTSTATUS ntStatus = AkenMapPhysicalMemory(physicalAddress, numBytes, virtualAddress);
@@ -193,29 +239,6 @@ static NTSTATUS AkenIoctlUnmap(PVOID buf, const ULONG inSize, ULONG& outSize)
return ntStatus;
}
static NTSTATUS AkenIoctlCopyPhysical(PVOID buf, const ULONG inSize, ULONG& outSize)
{
if(inSize != sizeof(AkenCopyPhysicalIn) || outSize != 0)
return STATUS_BUFFER_TOO_SMALL;
const AkenCopyPhysicalIn* in = (const AkenCopyPhysicalIn*)buf;
PHYSICAL_ADDRESS physicalAddress;
physicalAddress.QuadPart = in->physicalAddress;
const ULONG numBytes = (ULONG)in->numBytes;
void* userBuffer = (void*)(UINT_PTR)in->userAddress;
PVOID kernelBuffer = MmMapIoSpace(physicalAddress, numBytes, MmNonCached);
if(!kernelBuffer)
return STATUS_NO_MEMORY;
// (this works because we're called in the user's context)
RtlCopyMemory(userBuffer, kernelBuffer, numBytes);
MmUnmapIoSpace(kernelBuffer, numBytes);
return STATUS_SUCCESS;
}
static NTSTATUS AkenIoctlUnknown(PVOID buf, const ULONG inSize, ULONG& outSize)
{
KdPrint(("AkenIoctlUnknown\n"));
@@ -242,9 +265,6 @@ static inline AkenIoctl AkenIoctlFromCode(ULONG ioctlCode)
case IOCTL_AKEN_UNMAP:
return AkenIoctlUnmap;
case IOCTL_AKEN_COPY_PHYSICAL:
return AkenIoctlCopyPhysical;
default:
return AkenIoctlUnknown;
}
-8
View File
@@ -25,7 +25,6 @@
#define IOCTL_AKEN_WRITE_PORT CTL_CODE(FILE_DEVICE_AKEN, AKEN_IOCTL+1, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_AKEN_MAP CTL_CODE(FILE_DEVICE_AKEN, AKEN_IOCTL+2, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_AKEN_UNMAP CTL_CODE(FILE_DEVICE_AKEN, AKEN_IOCTL+3, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_AKEN_COPY_PHYSICAL CTL_CODE(FILE_DEVICE_AKEN, AKEN_IOCTL+4, METHOD_BUFFERED, FILE_ANY_ACCESS)
// input and output data structures for the IOCTLs
@@ -68,13 +67,6 @@ struct AkenUnmapIn
DWORD64 virtualAddress;
};
struct AkenCopyPhysicalIn
{
DWORD64 physicalAddress;
DWORD64 numBytes;
DWORD64 userAddress;
};
#pragma pack(pop)
#endif // #ifndef INCLUDED_AKEN
+9 -23
View File
@@ -94,8 +94,14 @@ void mahaf_WritePort32(u16 port, u32 value)
}
void* mahaf_MapPhysicalMemory(uintptr_t physicalAddress, size_t numBytes)
volatile void* mahaf_MapPhysicalMemory(uintptr_t physicalAddress, size_t numBytes)
{
// WinXP introduced checks that ensure we don't re-map pages with
// incompatible attributes. without this, mapping physical pages risks
// disaster due to TLB corruption, so disable it for safety.
if(wutil_WindowsVersion() < WUTIL_VERSION_XP)
return 0;
AkenMapIn in;
in.physicalAddress = (DWORD64)physicalAddress;
in.numBytes = (DWORD64)numBytes;
@@ -111,12 +117,12 @@ void* mahaf_MapPhysicalMemory(uintptr_t physicalAddress, size_t numBytes)
}
debug_assert(bytesReturned == sizeof(out));
void* virtualAddress = (void*)out.virtualAddress;
volatile void* virtualAddress = (volatile void*)out.virtualAddress;
return virtualAddress;
}
void mahaf_UnmapPhysicalMemory(void* virtualAddress)
void mahaf_UnmapPhysicalMemory(volatile void* virtualAddress)
{
AkenUnmapIn in;
in.virtualAddress = (DWORD64)virtualAddress;
@@ -128,26 +134,6 @@ void mahaf_UnmapPhysicalMemory(void* virtualAddress)
}
bool mahaf_CopyPhysicalMemory(uintptr_t physicalAddress, size_t numBytes, void* buffer)
{
AkenCopyPhysicalIn in;
in.physicalAddress = (DWORD64)physicalAddress;
in.numBytes = (DWORD64)numBytes;
in.userAddress = (DWORD64)buffer;
DWORD bytesReturned;
LPOVERLAPPED ovl = 0; // synchronous
BOOL ok = DeviceIoControl(hAken, (DWORD)IOCTL_AKEN_COPY_PHYSICAL, &in, sizeof(in), 0, 0, &bytesReturned, ovl);
if(!ok)
{
WARN_WIN32_ERR;
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// driver installation
//-----------------------------------------------------------------------------
+2 -7
View File
@@ -24,12 +24,7 @@ extern void mahaf_WritePort8 (u16 port, u8 value);
extern void mahaf_WritePort16(u16 port, u16 value);
extern void mahaf_WritePort32(u16 port, u32 value);
extern void* mahaf_MapPhysicalMemory(uintptr_t physicalAddress, size_t numBytes);
extern void mahaf_UnmapPhysicalMemory(void* virtualAddress);
/**
* @return false on failure (insufficient paged pool?)
**/
extern bool mahaf_CopyPhysicalMemory(uintptr_t physicalAddress, size_t numBytes, void* buffer);
extern volatile void* mahaf_MapPhysicalMemory(uintptr_t physicalAddress, size_t numBytes);
extern void mahaf_UnmapPhysicalMemory(volatile void* virtualAddress);
#endif // INCLUDED_MAHAF
+1 -1
View File
@@ -46,7 +46,7 @@ public:
/**
* initial measurement of the tick rate. not necessarily correct
* (e.g. when using TSC: cpu_ClockFrequency isn't exact).
* (e.g. when using TSC: wcpu_ClockFrequency isn't exact).
**/
virtual double NominalFrequency() const = 0;
+1 -1
View File
@@ -111,7 +111,7 @@ uint CounterHPET::CounterBits() const
/**
* initial measurement of the tick rate. not necessarily correct
* (e.g. when using TSC: cpu_ClockFrequency isn't exact).
* (e.g. when using TSC: wcpu_ClockFrequency isn't exact).
**/
double CounterHPET::NominalFrequency() const
{
+1 -1
View File
@@ -42,7 +42,7 @@ public:
/**
* initial measurement of the tick rate. not necessarily correct
* (e.g. when using TSC: cpu_ClockFrequency isn't exact).
* (e.g. when using TSC: wcpu_ClockFrequency isn't exact).
**/
virtual double NominalFrequency() const;
+1 -1
View File
@@ -83,7 +83,7 @@ uint CounterPMT::CounterBits() const
/**
* initial measurement of the tick rate. not necessarily correct
* (e.g. when using TSC: cpu_ClockFrequency isn't exact).
* (e.g. when using TSC: wcpu_ClockFrequency isn't exact).
**/
double CounterPMT::NominalFrequency() const
{
+1 -1
View File
@@ -43,7 +43,7 @@ public:
/**
* initial measurement of the tick rate. not necessarily correct
* (e.g. when using TSC: cpu_ClockFrequency isn't exact).
* (e.g. when using TSC: wcpu_ClockFrequency isn't exact).
**/
virtual double NominalFrequency() const;
+1 -1
View File
@@ -110,7 +110,7 @@ uint CounterQPC::CounterBits() const
/**
* initial measurement of the tick rate. not necessarily correct
* (e.g. when using TSC: cpu_ClockFrequency isn't exact).
* (e.g. when using TSC: wcpu_ClockFrequency isn't exact).
**/
double CounterQPC::NominalFrequency() const
{
+1 -1
View File
@@ -41,7 +41,7 @@ public:
/**
* initial measurement of the tick rate. not necessarily correct
* (e.g. when using TSC: cpu_ClockFrequency isn't exact).
* (e.g. when using TSC: wcpu_ClockFrequency isn't exact).
**/
virtual double NominalFrequency() const;
+1 -1
View File
@@ -69,7 +69,7 @@ uint CounterTGT::CounterBits() const
/**
* initial measurement of the tick rate. not necessarily correct
* (e.g. when using TSC: cpu_ClockFrequency isn't exact).
* (e.g. when using TSC: wcpu_ClockFrequency isn't exact).
**/
double CounterTGT::NominalFrequency() const
{
+1 -1
View File
@@ -36,7 +36,7 @@ public:
/**
* initial measurement of the tick rate. not necessarily correct
* (e.g. when using TSC: cpu_ClockFrequency isn't exact).
* (e.g. when using TSC: wcpu_ClockFrequency isn't exact).
**/
virtual double NominalFrequency() const;
+2 -2
View File
@@ -147,9 +147,9 @@ uint CounterTSC::CounterBits() const
/**
* initial measurement of the tick rate. not necessarily correct
* (e.g. when using TSC: cpu_ClockFrequency isn't exact).
* (e.g. when using TSC: wcpu_ClockFrequency isn't exact).
**/
double CounterTSC::NominalFrequency() const
{
return cpu_ClockFrequency();
return wcpu_ClockFrequency();
}
+1 -1
View File
@@ -36,7 +36,7 @@ public:
/**
* initial measurement of the tick rate. not necessarily correct
* (e.g. when using TSC: cpu_ClockFrequency isn't exact).
* (e.g. when using TSC: wcpu_ClockFrequency isn't exact).
**/
virtual double NominalFrequency() const;
};
+3
View File
@@ -283,6 +283,9 @@ static LibError whrt_Init()
InitCounter();
// latch initial counter value so that timer starts at 0
ts->counter = Counter(); // must come before UpdateTimerState
UpdateTimerState(); // must come before InitUpdateThread to avoid race
RETURN_ERR(InitUpdateThread());
+1 -1
View File
@@ -141,7 +141,7 @@ LibError win_get_snd_info()
// find all DLLs related to OpenAL, retrieve their versions,
// and store in snd_drv_ver string.
wdll_ver_list_init(snd_drv_ver, SND_DRV_VER_LEN);
if(!wutil_IsVista())
if(wutil_WindowsVersion() < WUTIL_VERSION_VISTA)
(void)wdll_ver_list_add(GetDirectSoundDriverPath());
StringSet dlls; // ensures uniqueness
(void)add_oal_dlls_in_dir(win_exe_dir, &dlls);
+16 -8
View File
@@ -228,7 +228,8 @@ static void EnableLowFragmentationHeap()
//-----------------------------------------------------------------------------
// version
static char versionString[20];
static char windowsVersionString[20];
static uint windowsVersion; // see WUTIL_VERSION_*
static void DetectWindowsVersion()
{
@@ -237,8 +238,14 @@ static void DetectWindowsVersion()
HKEY hKey;
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS)
{
DWORD size = ARRAY_SIZE(versionString);
(void)RegQueryValueEx(hKey, "CurrentVersion", 0, 0, (LPBYTE)versionString, &size);
DWORD size = ARRAY_SIZE(windowsVersionString);
(void)RegQueryValueEx(hKey, "CurrentVersion", 0, 0, (LPBYTE)windowsVersionString, &size);
uint major = 0, minor = 0;
int ret = sscanf(windowsVersionString, "%d.%d", &major, &minor);
debug_assert(ret == 2);
debug_assert(major <= 0xFF && minor <= 0xFF);
windowsVersion = (major << 8) | minor;
RegCloseKey(hKey);
}
@@ -248,16 +255,17 @@ static void DetectWindowsVersion()
const char* wutil_WindowsVersionString()
{
debug_assert(versionString[0] != '\0');
return versionString;
debug_assert(windowsVersionString[0] != '\0');
return windowsVersionString;
}
bool wutil_IsVista()
uint wutil_WindowsVersion()
{
debug_assert(versionString[0] != '\0');
return (versionString[0] >= '6');
debug_assert(windowsVersion != 0);
return windowsVersion;
}
//-----------------------------------------------------------------------------
// Wow64
+6 -1
View File
@@ -99,7 +99,12 @@ extern char win_exe_dir[MAX_PATH+1];
//
extern const char* wutil_WindowsVersionString();
extern bool wutil_IsVista();
// (same format as WINVER)
const uint WUTIL_VERSION_XP = 0x0501;
const uint WUTIL_VERSION_VISTA = 0x0600;
extern uint wutil_WindowsVersion();
//
+3 -3
View File
@@ -52,7 +52,9 @@ double get_time()
{
double t;
#if HAVE_CLOCK_GETTIME
#if OS_WIN
t = whrt_Time();
#elif HAVE_CLOCK_GETTIME
struct timespec cur;
(void)clock_gettime(CLOCK_REALTIME, &cur);
t = (cur.tv_sec - start.tv_sec) + (cur.tv_nsec - start.tv_nsec)*1e-9;
@@ -60,8 +62,6 @@ double get_time()
struct timeval cur;
gettimeofday(&cur, 0);
t = (cur.tv_sec - start.tv_sec) + (cur.tv_usec - start.tv_usec)*1e-6;
#elif OS_WIN
t = whrt_Time();
#else
# error "get_time: add timer implementation for this platform!"
#endif
+1 -1
View File
@@ -43,7 +43,7 @@ private:
CCamera dummyCamera;
};
struct SimState;
class SimState;
class ViewGame : public View
{