Hardware/Ring0.cs
author moel.mich
Sun, 23 Sep 2012 18:37:43 +0000
changeset 380 573f1fff48b2
parent 379 7af3aaeb42e9
permissions -rw-r--r--
Fixed Issue 387. The new implementation does not try to start a ring 0 driver that already exists, but could not be opened. It tries to delete the driver and install it new. The driver is now stored temporarily in the application folder. The driver is not correctly removed on system shutdown.
     1 /*
     2  
     3   This Source Code Form is subject to the terms of the Mozilla Public
     4   License, v. 2.0. If a copy of the MPL was not distributed with this
     5   file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6  
     7   Copyright (C) 2010-2012 Michael Möller <mmoeller@openhardwaremonitor.org>
     8 	
     9 */
    10 
    11 using System;
    12 using System.IO;
    13 using System.Reflection;
    14 using System.Runtime.InteropServices;
    15 using System.Security.AccessControl;
    16 using System.Threading;
    17 using System.Text;
    18 
    19 namespace OpenHardwareMonitor.Hardware {
    20   internal static class Ring0 {
    21 
    22     private static KernelDriver driver;
    23     private static string fileName;
    24     private static Mutex isaBusMutex;
    25     private static readonly StringBuilder report = new StringBuilder();
    26 
    27     private const uint OLS_TYPE = 40000;
    28     private static IOControlCode
    29       IOCTL_OLS_GET_REFCOUNT = new IOControlCode(OLS_TYPE, 0x801,
    30         IOControlCode.Access.Any),
    31       IOCTL_OLS_GET_DRIVER_VERSION = new IOControlCode(OLS_TYPE, 0x800,
    32         IOControlCode.Access.Any),
    33       IOCTL_OLS_READ_MSR = new IOControlCode(OLS_TYPE, 0x821,
    34         IOControlCode.Access.Any),
    35       IOCTL_OLS_WRITE_MSR = new IOControlCode(OLS_TYPE, 0x822, 
    36         IOControlCode.Access.Any),
    37       IOCTL_OLS_READ_IO_PORT_BYTE = new IOControlCode(OLS_TYPE, 0x833,
    38         IOControlCode.Access.Read),
    39       IOCTL_OLS_WRITE_IO_PORT_BYTE = new IOControlCode(OLS_TYPE, 0x836, 
    40         IOControlCode.Access.Write),
    41       IOCTL_OLS_READ_PCI_CONFIG = new IOControlCode(OLS_TYPE, 0x851, 
    42         IOControlCode.Access.Read),
    43       IOCTL_OLS_WRITE_PCI_CONFIG = new IOControlCode(OLS_TYPE, 0x852,
    44         IOControlCode.Access.Write),
    45       IOCTL_OLS_READ_MEMORY = new IOControlCode(OLS_TYPE, 0x841,
    46         IOControlCode.Access.Read);
    47 
    48     private static string GetTempFileName() {
    49       
    50       // try to create one in the application folder
    51       string fileName = Path.ChangeExtension(
    52         Assembly.GetEntryAssembly().Location, ".sys");
    53       try {
    54         using (FileStream stream = File.Create(fileName)) {
    55           return fileName;
    56         }        
    57       } catch (IOException) { } 
    58         catch (UnauthorizedAccessException) { }
    59 
    60       // if this failed, try to get a file in the temporary folder
    61       try {
    62         return Path.GetTempFileName();        
    63       } catch (IOException) { 
    64           // some I/O exception
    65       } 
    66       catch (UnauthorizedAccessException) { 
    67         // we do not have the right to create a file in the temp folder
    68       }
    69       catch (NotSupportedException) {
    70         // invalid path format of the TMP system environment variable
    71       }
    72      
    73       return null;
    74     }
    75 
    76     private static bool ExtractDriver(string fileName) {
    77       string resourceName = "OpenHardwareMonitor.Hardware." +
    78         (OperatingSystem.Is64BitOperatingSystem() ? "WinRing0x64.sys" : 
    79         "WinRing0.sys");
    80 
    81       string[] names =
    82         Assembly.GetExecutingAssembly().GetManifestResourceNames();
    83       byte[] buffer = null;
    84       for (int i = 0; i < names.Length; i++) {
    85         if (names[i].Replace('\\', '.') == resourceName) {
    86           using (Stream stream = Assembly.GetExecutingAssembly().
    87             GetManifestResourceStream(names[i])) 
    88           {
    89               buffer = new byte[stream.Length];
    90               stream.Read(buffer, 0, buffer.Length);
    91           }
    92         }
    93       }
    94 
    95       if (buffer == null)
    96         return false;
    97 
    98       try {
    99         using (FileStream target = new FileStream(fileName, FileMode.Create)) {
   100           target.Write(buffer, 0, buffer.Length);
   101           target.Flush();
   102         }
   103       } catch (IOException) { 
   104         // for example there is not enough space on the disk
   105         return false; 
   106       }
   107 
   108       // make sure the file is actually writen to the file system
   109       for (int i = 0; i < 20; i++) {
   110         try {
   111           if (File.Exists(fileName) &&
   112             new FileInfo(fileName).Length == buffer.Length) 
   113           {
   114             return true;
   115           }
   116           Thread.Sleep(100);
   117         } catch (IOException) {
   118           Thread.Sleep(10);
   119         }
   120       }
   121       
   122       // file still has not the right size, something is wrong
   123       return false;
   124     }
   125 
   126     public static void Open() {
   127       // no implementation for unix systems
   128       int p = (int)Environment.OSVersion.Platform;
   129       if ((p == 4) || (p == 128))
   130         return;  
   131       
   132       if (driver != null)
   133         return;
   134 
   135       // clear the current report
   136       report.Length = 0;
   137      
   138       driver = new KernelDriver("WinRing0_1_2_0");
   139       driver.Open();
   140 
   141       if (!driver.IsOpen) {
   142         // driver is not loaded, try to install and open
   143 
   144         fileName = GetTempFileName();
   145         if (fileName != null && ExtractDriver(fileName)) {
   146           string installError;
   147           if (driver.Install(fileName, out installError)) {
   148             driver.Open();
   149 
   150             if (!driver.IsOpen) {
   151               driver.Delete();
   152               report.AppendLine("Status: Opening driver failed after install");
   153             }
   154           } else {
   155             string errorFirstInstall = installError;
   156    
   157             // install failed, try to delete and reinstall
   158             driver.Delete();
   159 
   160             // wait a short moment to give the OS a chance to remove the driver
   161             Thread.Sleep(2000);
   162 
   163             string errorSecondInstall;
   164             if (driver.Install(fileName, out errorSecondInstall)) {
   165               driver.Open();
   166 
   167               if (!driver.IsOpen) {
   168                 driver.Delete();
   169                 report.AppendLine(
   170                   "Status: Opening driver failed after reinstall");
   171               }
   172             } else {
   173               report.AppendLine("Status: Installing driver \"" +
   174                 fileName + "\" failed" +
   175                 (File.Exists(fileName) ? " and file exists" : ""));
   176               report.AppendLine("First Exception: " + errorFirstInstall);
   177               report.AppendLine("Second Exception: " + errorSecondInstall);
   178             }
   179           }
   180         } else {
   181           report.AppendLine("Status: Extracting driver failed");
   182         }
   183 
   184         try {
   185           // try to delte the driver file
   186           if (File.Exists(fileName))
   187             File.Delete(fileName);
   188           fileName = null;
   189         } catch (IOException) { } 
   190           catch (UnauthorizedAccessException) { }
   191       }
   192 
   193       if (!driver.IsOpen) 
   194         driver = null;
   195 
   196       string mutexName = "Global\\Access_ISABUS.HTP.Method";
   197       try {
   198         isaBusMutex = new Mutex(false, mutexName);
   199       } catch (UnauthorizedAccessException) {
   200         try {
   201           isaBusMutex = Mutex.OpenExisting(mutexName, MutexRights.Synchronize);
   202         } catch { }
   203       }
   204     }
   205 
   206     public static bool IsOpen {
   207       get { return driver != null; }
   208     }
   209 
   210     public static void Close() {
   211       if (driver == null)
   212         return;
   213 
   214       uint refCount = 0;
   215       driver.DeviceIOControl(IOCTL_OLS_GET_REFCOUNT, null, ref refCount);
   216 
   217       driver.Close();
   218 
   219       if (refCount <= 1)
   220         driver.Delete();
   221 
   222       driver = null;
   223 
   224       if (isaBusMutex != null) {
   225         isaBusMutex.Close();
   226         isaBusMutex = null;
   227       }
   228 
   229       // try to delete temporary driver file again if failed during open
   230       if (fileName != null && File.Exists(fileName)) {
   231         try {
   232           File.Delete(fileName);
   233           fileName = null;
   234         } catch (IOException) { } 
   235           catch (UnauthorizedAccessException) { }
   236       }
   237     }
   238 
   239     public static string GetReport() {
   240       if (report.Length > 0) {
   241         StringBuilder r = new StringBuilder();
   242         r.AppendLine("Ring0");
   243         r.AppendLine();
   244         r.Append(report);
   245         r.AppendLine();
   246         return r.ToString();
   247       } else
   248         return null;
   249     }
   250 
   251     public static bool WaitIsaBusMutex(int millisecondsTimeout) {
   252       if (isaBusMutex == null)
   253         return true;
   254       try {
   255         return isaBusMutex.WaitOne(millisecondsTimeout, false);
   256       } catch (AbandonedMutexException) { return false; } 
   257         catch (InvalidOperationException) { return false; }
   258     }
   259 
   260     public static void ReleaseIsaBusMutex() {
   261       if (isaBusMutex == null)
   262         return;
   263       isaBusMutex.ReleaseMutex();
   264     }
   265 
   266     public static bool Rdmsr(uint index, out uint eax, out uint edx) {
   267       if (driver == null) {
   268         eax = 0;
   269         edx = 0;
   270         return false;
   271       }
   272 
   273       ulong buffer = 0;
   274       bool result = driver.DeviceIOControl(IOCTL_OLS_READ_MSR, index,
   275         ref buffer);
   276 
   277       edx = (uint)((buffer >> 32) & 0xFFFFFFFF);
   278       eax = (uint)(buffer & 0xFFFFFFFF);
   279       return result;
   280     }
   281 
   282     public static bool RdmsrTx(uint index, out uint eax, out uint edx,
   283       ulong threadAffinityMask) 
   284     {
   285       ulong mask = ThreadAffinity.Set(threadAffinityMask);
   286 
   287       bool result = Rdmsr(index, out eax, out edx);
   288 
   289       ThreadAffinity.Set(mask);
   290       return result;
   291     }
   292 
   293     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   294     private struct WrmsrInput {
   295       public uint Register;
   296       public ulong Value;
   297     }
   298 
   299     public static bool Wrmsr(uint index, uint eax, uint edx) {
   300       if (driver == null)
   301         return false;
   302 
   303       WrmsrInput input = new WrmsrInput();
   304       input.Register = index;
   305       input.Value = ((ulong)edx << 32) | eax;
   306 
   307       return driver.DeviceIOControl(IOCTL_OLS_WRITE_MSR, input);
   308     }
   309 
   310     public static byte ReadIoPort(uint port) {
   311       if (driver == null)
   312         return 0;
   313 
   314       uint value = 0;
   315       driver.DeviceIOControl(IOCTL_OLS_READ_IO_PORT_BYTE, port, ref value);
   316 
   317       return (byte)(value & 0xFF);
   318     }
   319 
   320     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   321     private struct WriteIoPortInput {
   322       public uint PortNumber;
   323       public byte Value;
   324     }
   325 
   326     public static void WriteIoPort(uint port, byte value) {
   327       if (driver == null)
   328         return;
   329 
   330       WriteIoPortInput input = new WriteIoPortInput();
   331       input.PortNumber = port;
   332       input.Value = value;
   333 
   334       driver.DeviceIOControl(IOCTL_OLS_WRITE_IO_PORT_BYTE, input);
   335     }
   336 
   337     public const uint InvalidPciAddress = 0xFFFFFFFF;
   338 
   339     public static uint GetPciAddress(byte bus, byte device, byte function) {
   340       return
   341         (uint)(((bus & 0xFF) << 8) | ((device & 0x1F) << 3) | (function & 7));
   342     }
   343 
   344     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   345     private struct ReadPciConfigInput {
   346       public uint PciAddress;
   347       public uint RegAddress;
   348     }
   349 
   350     public static bool ReadPciConfig(uint pciAddress, uint regAddress, 
   351       out uint value) 
   352     {
   353       if (driver == null || (regAddress & 3) != 0) {
   354         value = 0;
   355         return false;
   356       }
   357 
   358       ReadPciConfigInput input = new ReadPciConfigInput();
   359       input.PciAddress = pciAddress;
   360       input.RegAddress = regAddress;
   361 
   362       value = 0;
   363       return driver.DeviceIOControl(IOCTL_OLS_READ_PCI_CONFIG, input, 
   364         ref value);
   365     }
   366 
   367     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   368     private struct WritePciConfigInput {
   369       public uint PciAddress;
   370       public uint RegAddress;
   371       public uint Value;
   372     }
   373 
   374     public static bool WritePciConfig(uint pciAddress, uint regAddress, 
   375       uint value) 
   376     {
   377       if (driver == null || (regAddress & 3) != 0)
   378         return false;
   379 
   380       WritePciConfigInput input = new WritePciConfigInput();
   381       input.PciAddress = pciAddress;
   382       input.RegAddress = regAddress;
   383       input.Value = value;
   384 
   385       return driver.DeviceIOControl(IOCTL_OLS_WRITE_PCI_CONFIG, input);
   386     }
   387 
   388     [StructLayout(LayoutKind.Sequential, Pack = 1)]
   389     private struct ReadMemoryInput {
   390       public ulong address;
   391       public uint unitSize;
   392       public uint count;
   393     }
   394 
   395     public static bool ReadMemory<T>(ulong address, ref T buffer) {
   396       if (driver == null) {
   397         return false;
   398       }
   399 
   400       ReadMemoryInput input = new ReadMemoryInput();
   401       input.address = address;
   402       input.unitSize = 1;
   403       input.count = (uint)Marshal.SizeOf(buffer);
   404 
   405       return driver.DeviceIOControl(IOCTL_OLS_READ_MEMORY, input,
   406         ref buffer);
   407     }
   408   }
   409 }