VMWareBackdoor.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. /*
  2. * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/OwnPtr.h>
  7. #include <AK/Singleton.h>
  8. #include <Kernel/API/MousePacket.h>
  9. #include <Kernel/Arch/x86/InterruptDisabler.h>
  10. #include <Kernel/CommandLine.h>
  11. #include <Kernel/Debug.h>
  12. #include <Kernel/Devices/VMWareBackdoor.h>
  13. #include <Kernel/Sections.h>
  14. namespace Kernel {
  15. #define VMWARE_CMD_GETVERSION 0x0a
  16. #define VMMOUSE_READ_ID 0x45414552
  17. #define VMMOUSE_DISABLE 0x000000f5
  18. #define VMMOUSE_REQUEST_RELATIVE 0x4c455252
  19. #define VMMOUSE_REQUEST_ABSOLUTE 0x53424152
  20. #define VMMOUSE_QEMU_VERSION 0x3442554a
  21. #define VMMOUSE_LEFT_CLICK 0x20
  22. #define VMMOUSE_RIGHT_CLICK 0x10
  23. #define VMMOUSE_MIDDLE_CLICK 0x08
  24. #define VMWARE_MAGIC 0x564D5868
  25. #define VMWARE_PORT 0x5658
  26. #define VMWARE_PORT_HIGHBANDWIDTH 0x5659
  27. inline void vmware_out(VMWareCommand& command)
  28. {
  29. command.magic = VMWARE_MAGIC;
  30. command.port = VMWARE_PORT;
  31. command.si = 0;
  32. command.di = 0;
  33. asm volatile("in %%dx, %0"
  34. : "+a"(command.ax), "+b"(command.bx), "+c"(command.cx), "+d"(command.dx), "+S"(command.si), "+D"(command.di));
  35. }
  36. inline void vmware_high_bandwidth_send(VMWareCommand& command)
  37. {
  38. command.magic = VMWARE_MAGIC;
  39. command.port = VMWARE_PORT_HIGHBANDWIDTH;
  40. asm volatile("cld; rep; outsb"
  41. : "+a"(command.ax), "+b"(command.bx), "+c"(command.cx), "+d"(command.dx), "+S"(command.si), "+D"(command.di));
  42. }
  43. inline void vmware_high_bandwidth_get(VMWareCommand& command)
  44. {
  45. command.magic = VMWARE_MAGIC;
  46. command.port = VMWARE_PORT_HIGHBANDWIDTH;
  47. asm volatile("cld; rep; insb"
  48. : "+a"(command.ax), "+b"(command.bx), "+c"(command.cx), "+d"(command.dx), "+S"(command.si), "+D"(command.di));
  49. }
  50. class VMWareBackdoorDetector {
  51. public:
  52. VMWareBackdoorDetector()
  53. {
  54. if (detect_presence())
  55. m_backdoor = make<VMWareBackdoor>();
  56. }
  57. VMWareBackdoor* get_instance()
  58. {
  59. return m_backdoor.ptr();
  60. }
  61. private:
  62. static bool detect_presence()
  63. {
  64. VMWareCommand command;
  65. command.bx = ~VMWARE_MAGIC;
  66. command.command = VMWARE_CMD_GETVERSION;
  67. vmware_out(command);
  68. if (command.bx != VMWARE_MAGIC || command.ax == 0xFFFFFFFF)
  69. return false;
  70. return true;
  71. }
  72. OwnPtr<VMWareBackdoor> m_backdoor;
  73. };
  74. static Singleton<VMWareBackdoorDetector> s_vmware_backdoor;
  75. VMWareBackdoor* VMWareBackdoor::the()
  76. {
  77. return s_vmware_backdoor->get_instance();
  78. }
  79. UNMAP_AFTER_INIT VMWareBackdoor::VMWareBackdoor()
  80. {
  81. if (kernel_command_line().is_vmmouse_enabled())
  82. enable_absolute_vmmouse();
  83. }
  84. bool VMWareBackdoor::detect_vmmouse()
  85. {
  86. VMWareCommand command;
  87. command.bx = VMMOUSE_READ_ID;
  88. command.command = VMMOUSE_COMMAND;
  89. send(command);
  90. command.bx = 1;
  91. command.command = VMMOUSE_DATA;
  92. send(command);
  93. if (command.ax != VMMOUSE_QEMU_VERSION)
  94. return false;
  95. return true;
  96. }
  97. bool VMWareBackdoor::vmmouse_is_absolute() const
  98. {
  99. return m_vmmouse_absolute;
  100. }
  101. void VMWareBackdoor::enable_absolute_vmmouse()
  102. {
  103. InterruptDisabler disabler;
  104. if (!detect_vmmouse())
  105. return;
  106. dmesgln("VMWareBackdoor: Enabling absolute mouse mode");
  107. VMWareCommand command;
  108. command.bx = 0;
  109. command.command = VMMOUSE_STATUS;
  110. send(command);
  111. if (command.ax == 0xFFFF0000) {
  112. dmesgln("VMWareBackdoor: VMMOUSE_STATUS got bad status");
  113. return;
  114. }
  115. // Enable absolute vmmouse
  116. command.bx = VMMOUSE_REQUEST_ABSOLUTE;
  117. command.command = VMMOUSE_COMMAND;
  118. send(command);
  119. m_vmmouse_absolute = true;
  120. }
  121. void VMWareBackdoor::disable_absolute_vmmouse()
  122. {
  123. InterruptDisabler disabler;
  124. VMWareCommand command;
  125. command.bx = VMMOUSE_REQUEST_RELATIVE;
  126. command.command = VMMOUSE_COMMAND;
  127. send(command);
  128. m_vmmouse_absolute = false;
  129. }
  130. void VMWareBackdoor::send_high_bandwidth(VMWareCommand& command)
  131. {
  132. vmware_high_bandwidth_send(command);
  133. dbgln_if(VMWARE_BACKDOOR_DEBUG, "VMWareBackdoor Command High bandwidth Send Results: EAX {:#x} EBX {:#x} ECX {:#x} EDX {:#x}",
  134. command.ax,
  135. command.bx,
  136. command.cx,
  137. command.dx);
  138. }
  139. void VMWareBackdoor::get_high_bandwidth(VMWareCommand& command)
  140. {
  141. vmware_high_bandwidth_get(command);
  142. dbgln_if(VMWARE_BACKDOOR_DEBUG, "VMWareBackdoor Command High bandwidth Get Results: EAX {:#x} EBX {:#x} ECX {:#x} EDX {:#x}",
  143. command.ax,
  144. command.bx,
  145. command.cx,
  146. command.dx);
  147. }
  148. void VMWareBackdoor::send(VMWareCommand& command)
  149. {
  150. vmware_out(command);
  151. dbgln_if(VMWARE_BACKDOOR_DEBUG, "VMWareBackdoor Command Send Results: EAX {:#x} EBX {:#x} ECX {:#x} EDX {:#x}",
  152. command.ax,
  153. command.bx,
  154. command.cx,
  155. command.dx);
  156. }
  157. Optional<MousePacket> VMWareBackdoor::receive_mouse_packet()
  158. {
  159. VMWareCommand command;
  160. command.bx = 0;
  161. command.command = VMMOUSE_STATUS;
  162. send(command);
  163. if (command.ax == 0xFFFF0000) {
  164. dbgln_if(PS2MOUSE_DEBUG, "PS2MouseDevice: Resetting VMWare mouse");
  165. disable_absolute_vmmouse();
  166. enable_absolute_vmmouse();
  167. return {};
  168. }
  169. int words = command.ax & 0xFFFF;
  170. if (!words || words % 4)
  171. return {};
  172. command.size = 4;
  173. command.command = VMMOUSE_DATA;
  174. send(command);
  175. int buttons = (command.ax & 0xFFFF);
  176. int x = (command.bx);
  177. int y = (command.cx);
  178. int z = (i8)(command.dx); // signed 8 bit value only!
  179. if constexpr (PS2MOUSE_DEBUG) {
  180. dbgln("Absolute Mouse: Buttons {:x}", buttons);
  181. dbgln("Mouse: x={}, y={}, z={}", x, y, z);
  182. }
  183. MousePacket packet;
  184. packet.x = x;
  185. packet.y = y;
  186. packet.z = z;
  187. if (buttons & VMMOUSE_LEFT_CLICK)
  188. packet.buttons |= MousePacket::LeftButton;
  189. if (buttons & VMMOUSE_RIGHT_CLICK)
  190. packet.buttons |= MousePacket::RightButton;
  191. if (buttons & VMMOUSE_MIDDLE_CLICK)
  192. packet.buttons |= MousePacket::MiddleButton;
  193. packet.is_relative = false;
  194. return packet;
  195. }
  196. }