From cac91d5895dbb3c6e9a4e0cbe1fb99ecbe8d8466 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 23 Nov 2022 05:00:21 +0200 Subject: [PATCH] Added VMware mouse driver --- .gitignore | 1 + Input/Makefile | 5 + Input/VMwareMouse/Makefile | 100 ++++++++++ Input/VMwareMouse/arch/amd64/linker.ld | 42 ++++ Input/VMwareMouse/vmware_mouse.cpp | 261 +++++++++++++++++++++++++ Makefile | 2 + Network/E1000/e1000.cpp | 2 +- Network/RTL8139/rtl8139.cpp | 2 +- 8 files changed, 413 insertions(+), 2 deletions(-) create mode 100644 Input/Makefile create mode 100644 Input/VMwareMouse/Makefile create mode 100644 Input/VMwareMouse/arch/amd64/linker.ld create mode 100644 Input/VMwareMouse/vmware_mouse.cpp diff --git a/.gitignore b/.gitignore index 45e10c1..8c827b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ out *.map *.o +.dccache diff --git a/Input/Makefile b/Input/Makefile new file mode 100644 index 0000000..f0d89ac --- /dev/null +++ b/Input/Makefile @@ -0,0 +1,5 @@ +build: + make --quiet -C VMwareMouse build + +clean: + make -C VMwareMouse clean diff --git a/Input/VMwareMouse/Makefile b/Input/VMwareMouse/Makefile new file mode 100644 index 0000000..7476487 --- /dev/null +++ b/Input/VMwareMouse/Makefile @@ -0,0 +1,100 @@ +# Config file +include ../../../Makefile.conf + +FILENAME = VMwareGuestDriver.fex + +CC = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)gcc +CPP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)g++ +LD = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)ld +AS = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)as +OBJDUMP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)objdump + +GIT_COMMIT = $(shell git rev-parse HEAD) +GIT_COMMIT_SHORT = $(shell git rev-parse --short HEAD) + +ifeq ($(OSARCH), amd64) +S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/i686/*" -not -path "./arch/aarch64/*") +C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/i686/*" -not -path "./arch/aarch64/*") +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/i686/*" -not -path "./arch/aarch64/*") +else ifeq ($(OSARCH), i686) +S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") +C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") +else ifeq ($(OSARCH), aarch64) +S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/amd64/*" -not -path "./arch/i686/*") +C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/amd64/*" -not -path "./arch/i686/*") +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/amd64/*" -not -path "./arch/i686/*") +endif +HEADERS = $(sort $(dir $(wildcard ../../include/*))) +OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o) +INCLUDE_DIR = ../../include + +LDFLAGS := \ + -fPIC -fno-pie \ + -Wl,-static,--no-dynamic-linker,-ztext \ + -nostdlib -nodefaultlibs -nolibc \ + -zmax-page-size=0x1000 \ + -Wl,-Map file.map -shared + +WARNCFLAG = -Wall -Wextra + +CFLAGS := \ + -I$(INCLUDE_DIR) \ + -DGIT_COMMIT='"$(GIT_COMMIT)"' \ + -DGIT_COMMIT_SHORT='"$(GIT_COMMIT_SHORT)"' + +ifeq ($(OSARCH), amd64) + +CFLAGS += -fPIC -fno-pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 \ + -march=x86-64 -pipe -ffunction-sections \ + -mcmodel=kernel -msoft-float -fno-builtin +LDFLAGS += -Tarch/amd64/linker.ld + +else ifeq ($(OSARCH), i686) + +CFLAGS += -fPIC -fno-pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 -ffunction-sections \ + -march=i686 -pipe -msoft-float -fno-builtin +LDFLAGS += -Tarch/i686/linker.ld + +else ifeq ($(OSARCH), aarch64) + +CFLAGS += -pipe -fno-builtin -fPIC +LDFLAGS += -Tarch/aarch64/linker.ld + +endif + +build: $(FILENAME) +ifeq ($(OSARCH), amd64) + $(OBJDUMP) -b binary -D -m i386:x86-64 -d $(FILENAME) > file_dump.map +else ifeq ($(OSARCH), i686) + +else ifeq ($(OSARCH), aarch64) + +endif + mv $(FILENAME) ../../out/$(FILENAME) + +$(FILENAME): $(OBJ) + $(CC) $(LDFLAGS) $(OBJ) -o $@ + +%.o: %.c $(HEADERS) + $(info Compiling $<) + $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ + +%.o: %.cpp $(HEADERS) + $(info Compiling $<) + $(CPP) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fexceptions -c $< -o $@ -fno-rtti + +%.o: %.S + $(info Compiling $<) +ifeq ($(OSARCH), amd64) + $(AS) -o $@ $< +else ifeq ($(OSARCH), i686) + $(AS) -o $@ $< +else ifeq ($(OSARCH), aarch64) + $(AS) -o $@ $< +endif + +clean: + rm -f *.o file.map file_dump.map $(OBJ) diff --git a/Input/VMwareMouse/arch/amd64/linker.ld b/Input/VMwareMouse/arch/amd64/linker.ld new file mode 100644 index 0000000..511369e --- /dev/null +++ b/Input/VMwareMouse/arch/amd64/linker.ld @@ -0,0 +1,42 @@ +/* EXPERIMENTAL */ + +OUTPUT_FORMAT(binary) +OUTPUT_ARCH(i386:x86-64) + +ENTRY(DriverEntry) + +SECTIONS +{ + .header : + { + *(.header .header.*) + *(.extended .extended.*) + } + + .text : + { + *(.text .text.*) + } + + .data : + { + *(.data .data.*) + } + + .rodata : + { + *(.rodata .rodata.*) + } + + .bss : + { + *(COMMON) + *(.bss .bss.*) + } + + /DISCARD/ : + { + *(.eh_frame) + *(.note .note.*) + } +} diff --git a/Input/VMwareMouse/vmware_mouse.cpp b/Input/VMwareMouse/vmware_mouse.cpp new file mode 100644 index 0000000..2cb45aa --- /dev/null +++ b/Input/VMwareMouse/vmware_mouse.cpp @@ -0,0 +1,261 @@ +#include +#include + +#include "../../../Kernel/DAPI.hpp" +#include "../../../Kernel/Fex.hpp" + +extern "C" int DriverEntry(void *Data); +int CallbackHandler(KernelCallback *Data); + +HEAD(FexFormatType_Driver, FexOSType_Fennix, DriverEntry); + +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" + +__attribute__((section(".extended"))) FexExtended ExtendedHeader = { + .Driver = { + .Name = "VMware Virtual Mouse Driver", + .Type = FexDriverType_Input, + .Callback = CallbackHandler, + .Bind = { + .Type = BIND_INTERRUPT, + .Interrupt = { + .Vector = {0xC}, // IRQ12 + }}}}; + +KernelAPI *KAPI; + +/* --------------------------------------------------------------------------------------------------------- */ + +/* https://wiki.osdev.org/VMware_tools */ + +typedef struct +{ + union + { + uint32_t ax; + uint32_t magic; + }; + union + { + uint32_t bx; + size_t size; + }; + union + { + uint32_t cx; + uint16_t command; + }; + union + { + uint32_t dx; + uint16_t port; + }; + uint32_t si; + uint32_t di; +} VMwareCommand; + +#define VMWARE_MAGIC 0x564D5868 /* hXMV */ +#define VMWARE_PORT 0x5658 + +#define CMD_GETVERSION 0xA +#define CMD_ABSPOINTER_DATA 0x27 +#define CMD_ABSPOINTER_STATUS 0x28 +#define CMD_ABSPOINTER_COMMAND 0x29 + +#define ABSPOINTER_ENABLE 0x45414552 /* Q E A E */ +#define ABSPOINTER_RELATIVE 0xF5 +#define ABSPOINTER_ABSOLUTE 0x53424152 /* R A B S */ + +void CommandSend(VMwareCommand *cmd) +{ + cmd->magic = VMWARE_MAGIC; + cmd->port = VMWARE_PORT; + asm volatile("in %%dx, %0" + : "+a"(cmd->ax), "+b"(cmd->bx), "+c"(cmd->cx), "+d"(cmd->dx), "+S"(cmd->si), "+D"(cmd->di)); +} + +bool IsVMwareBackdoorAvailable(void) +{ + VMwareCommand cmd; + cmd.bx = ~VMWARE_MAGIC; + cmd.command = CMD_GETVERSION; + CommandSend(&cmd); + if (cmd.bx != VMWARE_MAGIC || cmd.ax == 0xFFFFFFFF) + return false; + return true; +} + +int DriverEntry(void *Data) +{ + if (!Data) + return INVALID_KERNEL_API; + KAPI = (KernelAPI *)Data; + if (KAPI->Version.Major < 0 || KAPI->Version.Minor < 0 || KAPI->Version.Patch < 0) + return KERNEL_API_VERSION_NOT_SUPPORTED; + + if (!IsVMwareBackdoorAvailable()) + return SYSTEM_NOT_SUPPORTED; + + return OK; +} + +void Absolute(void) +{ + VMwareCommand cmd; + + /* Enable */ + cmd.bx = ABSPOINTER_ENABLE; + cmd.command = CMD_ABSPOINTER_COMMAND; + CommandSend(&cmd); + + /* Status */ + cmd.bx = 0; + cmd.command = CMD_ABSPOINTER_STATUS; + CommandSend(&cmd); + + /* Read data (1) */ + cmd.bx = 1; + cmd.command = CMD_ABSPOINTER_DATA; + CommandSend(&cmd); + + /* Enable absolute */ + cmd.bx = ABSPOINTER_ABSOLUTE; + cmd.command = CMD_ABSPOINTER_COMMAND; + CommandSend(&cmd); +} + +void Relative(void) +{ + VMwareCommand cmd; + cmd.bx = ABSPOINTER_RELATIVE; + cmd.command = CMD_ABSPOINTER_COMMAND; + CommandSend(&cmd); +} + +enum Config +{ + READ_CONFIG = 0x20, + WRITE_CONFIG = 0x60 +}; + +enum Ports +{ + DATA = 0x60, + STATUS = 0x64, + COMMAND = 0x64, +}; + +enum State +{ + OUTPUT_FULL = (1 << 0), + INPUT_FULL = (1 << 1), + MOUSE_BYTE = (1 << 5) +}; + +void WaitRead() +{ + uint64_t Timeout = 100000; + while (Timeout--) + if (inb(Ports::STATUS) & State::OUTPUT_FULL) + return; +} + +void WaitWrite() +{ + uint64_t Timeout = 100000; + while (Timeout--) + if ((inb(Ports::STATUS) & State::INPUT_FULL) == 0) + return; +} + +void Write(uint16_t Port, uint8_t Value) +{ + WaitWrite(); + outb(Port, Value); +} + +uint8_t Read() +{ + WaitRead(); + return inb(Ports::DATA); +} + +int MouseX = 0, MouseY = 0, MouseZ = 0; +int MouseButton = 0; + +int CallbackHandler(KernelCallback *Data) +{ + switch (Data->Reason) + { + case AcknowledgeReason: + { + KAPI->Util.DebugPrint(((char *)"Kernel acknowledged the driver." + KAPI->Info.Offset), KAPI->Info.DriverUID); + break; + } + case ConfigurationReason: + { + outb(COMMAND, 0xA8); + Write(COMMAND, READ_CONFIG); + uint8_t Status = Read(); + Status |= 0b10; + Write(COMMAND, WRITE_CONFIG); + Write(DATA, Status); + Write(COMMAND, 0xD4); + Write(DATA, 0xF6); + Read(); + Write(COMMAND, 0xD4); + Write(DATA, 0xF4); + Read(); + Absolute(); + KAPI->Util.DebugPrint(((char *)"VMware mouse configured." + KAPI->Info.Offset), KAPI->Info.DriverUID); + break; + } + case InterruptReason: + { + uint8_t Data = inb(0x60); + (void)Data; + VMwareCommand cmd; + cmd.bx = 0; + cmd.command = CMD_ABSPOINTER_STATUS; + CommandSend(&cmd); + + if (cmd.ax == 0xFFFF0000) + { + KAPI->Util.DebugPrint(((char *)"VMware mouse is not connected?" + KAPI->Info.Offset), KAPI->Info.DriverUID); + Relative(); + Absolute(); + return ERROR; + } + if ((cmd.ax & 0xFFFF) < 4) + return ERROR; + + cmd.bx = 4; + cmd.command = CMD_ABSPOINTER_DATA; + CommandSend(&cmd); + + int flags = (cmd.ax & 0xFFFF0000) >> 16; /* Not important */ + (void)flags; + MouseButton = (cmd.ax & 0xFFFF); /* 0x10 = Right, 0x20 = Left, 0x08 = Middle */ + MouseX = cmd.bx; /* Both X and Y are scaled from 0 to 0xFFFF */ + MouseY = cmd.cx; /* You should map these somewhere to the actual resolution. */ + MouseZ = (int8_t)cmd.dx; /* Z is a single signed byte indicating scroll direction. */ + break; + } + case FetchReason: + { + Data->InputCallback.Mouse.X = MouseX; + Data->InputCallback.Mouse.Y = MouseY; + Data->InputCallback.Mouse.Z = MouseZ; + Data->InputCallback.Mouse.Buttons.Left = MouseButton & 0x20; + Data->InputCallback.Mouse.Buttons.Right = MouseButton & 0x10; + Data->InputCallback.Mouse.Buttons.Middle = MouseButton & 0x08; + break; + } + default: + { + KAPI->Util.DebugPrint(((char *)"Unknown reason." + KAPI->Info.Offset), KAPI->Info.DriverUID); + break; + } + } + return OK; +} diff --git a/Makefile b/Makefile index 63eed7e..99b7c87 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ build: mkdir -p out + make --quiet -C Input build make --quiet -C Disk build make --quiet -C Audio build make --quiet -C FileSystem build @@ -12,6 +13,7 @@ prepare: clean: rm -rf out + make -C Input clean make -C Disk clean make -C Audio clean make -C FileSystem clean diff --git a/Network/E1000/e1000.cpp b/Network/E1000/e1000.cpp index bf80ff2..05bf901 100644 --- a/Network/E1000/e1000.cpp +++ b/Network/E1000/e1000.cpp @@ -390,7 +390,7 @@ int CallbackHandler(KernelCallback *Data) { uint8_t *Data = (uint8_t *)RX[RXCurrent]->Address; uint16_t DataLength = RX[RXCurrent]->Length; - KAPI->Commmand.Network.ReceivePacket(KAPI->Info.DriverUID, Data, DataLength); + KAPI->Command.Network.ReceivePacket(KAPI->Info.DriverUID, Data, DataLength); RX[RXCurrent]->Status = 0; uint16_t OldRXCurrent = RXCurrent; RXCurrent = (RXCurrent + 1) % E1000_NUM_RX_DESC; diff --git a/Network/RTL8139/rtl8139.cpp b/Network/RTL8139/rtl8139.cpp index 1596a9b..55ba787 100644 --- a/Network/RTL8139/rtl8139.cpp +++ b/Network/RTL8139/rtl8139.cpp @@ -173,7 +173,7 @@ int CallbackHandler(KernelCallback *Data) uint16_t *Data = (uint16_t *)(RXBuffer + CurrentPacket); uint16_t DataLength = *(Data + 1); Data = Data + 2; - KAPI->Commmand.Network.ReceivePacket(KAPI->Info.DriverUID, (uint8_t *)Data, DataLength); + KAPI->Command.Network.ReceivePacket(KAPI->Info.DriverUID, (uint8_t *)Data, DataLength); CurrentPacket = (CurrentPacket + DataLength + 4 + 3) & (~3); if (CurrentPacket > 8192) CurrentPacket -= 8192;