1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""A server that serves MSR values over TCP. Takes a port as its sole parameter.
6
7The reference client for this server is msr_power_monitor.MsrPowerMonitor.
8
9Must be run as Administrator. We use TCP instead of named pipes or another IPC
10to avoid dealing with the pipe security mechanisms. We take the port as a
11parameter instead of choosing one, because it's hard to communicate the port
12number across integrity levels.
13
14Requires WinRing0 to be installed in the Python directory.
15msr_power_monitor.MsrPowerMonitor does this if needed.
16"""
17
18import argparse
19import ctypes
20import os
21import SocketServer
22import struct
23import sys
24try:
25  import win32api  # pylint: disable=import-error
26  import win32file  # pylint: disable=import-error
27except ImportError:
28  win32api = None
29  win32file = None
30
31
32WINRING0_STATUS_MESSAGES = (
33    'No error',
34    'Unsupported platform',
35    'Driver not loaded. You may need to run as Administrator',
36    'Driver not found',
37    'Driver unloaded by other process',
38    'Driver not loaded because of executing on Network Drive',
39    'Unknown error',
40)
41
42
43# The DLL initialization is global, so put it in a global variable.
44_winring0 = None
45
46
47class WinRing0Error(OSError):
48  pass
49
50
51def _WinRing0Path():
52  python_is_64_bit = sys.maxsize > 2 ** 32
53  dll_file_name = 'WinRing0x64.dll' if python_is_64_bit else 'WinRing0.dll'
54  return os.path.join(os.path.dirname(sys.executable), dll_file_name)
55
56
57def _Initialize():
58  global _winring0
59  if not _winring0:
60    winring0 = ctypes.WinDLL(_WinRing0Path())
61    if not winring0.InitializeOls():
62      winring0_status = winring0.GetDllStatus()
63      raise WinRing0Error(winring0_status,
64                          'Unable to initialize WinRing0: %s' %
65                          WINRING0_STATUS_MESSAGES[winring0_status])
66    _winring0 = winring0
67
68
69def _Deinitialize():
70  global _winring0
71  if _winring0:
72    _winring0.DeinitializeOls()
73    _winring0 = None
74
75
76def _ReadMsr(msr_number):
77  low = ctypes.c_uint()
78  high = ctypes.c_uint()
79  _winring0.Rdmsr(ctypes.c_uint(msr_number),
80                  ctypes.byref(low), ctypes.byref(high))
81  return high.value << 32 | low.value
82
83
84class MsrRequestHandler(SocketServer.StreamRequestHandler):
85  def handle(self):
86    msr_number = struct.unpack('I', self.rfile.read(4))[0]
87    self.wfile.write(struct.pack('Q', _ReadMsr(msr_number)))
88
89
90def main():
91  parser = argparse.ArgumentParser()
92  parser.add_argument('pipe_name', type=str)
93  args = parser.parse_args()
94
95  _Initialize()
96  try:
97    SocketServer.TCPServer.allow_reuse_address = True
98    server_address = ('127.0.0.1', 0)
99    server = SocketServer.TCPServer(server_address, MsrRequestHandler)
100    handle = win32file.CreateFile(args.pipe_name,
101                                  win32file.GENERIC_WRITE,
102                                  0, None,
103                                  win32file.OPEN_EXISTING,
104                                  0, None)
105    _, port = server.server_address
106    win32file.WriteFile(handle, str(port))
107    win32api.CloseHandle(handle)
108    server.serve_forever()
109  finally:
110    _Deinitialize()
111
112
113if __name__ == '__main__':
114  main()
115