feat(kernel/drivers): migrate drivers to the kernel

make the drivers builtin

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
This commit is contained in:
2025-03-02 21:37:01 +00:00
parent f824df9aad
commit bf1e3432d7
22 changed files with 7078 additions and 1 deletions

View File

@ -0,0 +1,43 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <types.h>
#include <cpu.hpp>
namespace Driver::AdvancedIntegratedPeripheral
{
extern uint8_t Device1ID[];
extern uint8_t Device2ID[];
void PS2KbdInterruptHandler(CPU::TrapFrame *);
int InitializeKeyboard();
int FinalizeKeyboard();
int DetectPS2Keyboard();
void PS2MouseInterruptHandler(CPU::TrapFrame *);
int InitializeMouse();
int FinalizeMouse();
int DetectPS2Mouse();
int DetectUART();
void MasterInterruptHandler(CPU::TrapFrame *);
void SlaveInterruptHandler(CPU::TrapFrame *);
int InitializeATA();
int FinalizeATA();
}

View File

@ -0,0 +1,70 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <cpu.hpp>
#include <pci.hpp>
#include <io.h>
extern Driver::Manager *DriverManager;
extern PCI::Manager *PCIManager;
namespace Driver::AdvancedIntegratedPeripheral
{
extern dev_t DriverID;
const struct InodeOperations opsATA = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = nullptr,
.Write = nullptr,
.Truncate = nullptr,
.Open = nullptr,
.Close = nullptr,
.Ioctl = nullptr,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = nullptr,
.Stat = nullptr,
};
void MasterInterruptHandler(CPU::TrapFrame *)
{
}
void SlaveInterruptHandler(CPU::TrapFrame *)
{
}
int InitializeATA()
{
v0::RegisterInterruptHandler(DriverID, 14, (void *)MasterInterruptHandler);
v0::RegisterInterruptHandler(DriverID, 15, (void *)SlaveInterruptHandler);
return 0;
}
int FinalizeATA()
{
v0::UnregisterInterruptHandler(DriverID, 14, (void *)MasterInterruptHandler);
v0::UnregisterInterruptHandler(DriverID, 15, (void *)SlaveInterruptHandler);
return 0;
}
}

View File

@ -0,0 +1,224 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#include "aip.hpp"
#include <driver.hpp>
#include <interface/input.h>
#include <interface/aip.h>
#include <cpu.hpp>
#include <io.h>
extern const unsigned short ScanCodeSet1[];
extern const unsigned short ScanCodeSet1mm[];
extern const unsigned short ScanCodeSet3[];
namespace Driver::AdvancedIntegratedPeripheral
{
extern dev_t DriverID;
uint8_t KeyboardScanCodeSet = 0;
dev_t KeyboardDevID = -1;
InputReport kir = {};
int ReportKeyboardEvent(dev_t Device, const unsigned short ScanCode, uint8_t Pressed)
{
kir.Type = INPUT_TYPE_KEYBOARD;
kir.Device = Device;
kir.Keyboard.Key = (KeyScanCodes)(ScanCode);
kir.Keyboard.Key = (KeyScanCodes)((int)kir.Keyboard.Key | (Pressed ? KEY_PRESSED : 0));
// kir.Keyboard.Key |= Pressed ? KEY_PRESSED : 0;
v0::ReportInputEvent(DriverID, &kir);
return 0;
}
bool IsE0 = false;
bool IsE1 = false;
void PS2KbdInterruptHandler(CPU::TrapFrame *)
{
uint8_t sc = inb(PS2_DATA);
if (sc == PS2_KBD_RESP_ACK ||
sc == PS2_KBD_RESP_ECHO ||
sc == PS2_KBD_RESP_RESEND)
return;
if (sc == 0xE0)
{
IsE0 = true;
return;
}
if (sc == 0xE1)
{
IsE1 = true;
return;
}
switch (KeyboardScanCodeSet)
{
case PS2_KBD_SC_SET_1:
case PS2_KBD_SC_SET_2:
{
if (IsE0)
{
IsE0 = false;
ReportKeyboardEvent(KeyboardDevID, ScanCodeSet1mm[sc], sc < 0x90);
return;
}
else
{
bool released = sc & 0x80;
uint8_t scFinal = released ? sc & 0x7F : sc;
ReportKeyboardEvent(KeyboardDevID, ScanCodeSet1[scFinal], !released);
return;
}
}
/* FIXME: https://wiki.osdev.org/PS/2_Keyboard */
// case PS2_KBD_SC_SET_2:
// {
// break;
// }
case PS2_KBD_SC_SET_3:
{
ReportKeyboardEvent(KeyboardDevID, ScanCodeSet3[sc], true);
ReportKeyboardEvent(KeyboardDevID, ScanCodeSet3[sc], false);
break;
}
default:
{
if (IsE0)
IsE0 = false;
trace("Unknown PS/2 Keyboard Scan Code Set: %#x", KeyboardScanCodeSet);
break;
}
}
}
int __fs_kb_Ioctl(struct Inode *, unsigned long, void *)
{
return 0;
}
const struct InodeOperations KbdOps = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = nullptr,
.Write = nullptr,
.Truncate = nullptr,
.Open = nullptr,
.Close = nullptr,
.Ioctl = __fs_kb_Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = nullptr,
.Stat = nullptr,
};
int InitializeKeyboard()
{
// v0::PS2WriteData(DriverID, PS2_KBD_CMD_RESET);
// uint8_t test = v0::PS2ReadData(DriverID);
// if (test != PS2_KBD_RESP_TEST_PASSED &&
// test != PS2_KBD_RESP_ACK)
// {
// trace("PS/2 keyboard reset failed (%#x)", test);
// return -EFAULT;
// }
v0::PS2WriteData(DriverID, PS2_KBD_CMD_DEFAULTS);
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 keyboard failed to set defaults");
v0::PS2WriteData(DriverID, PS2_KBD_CMD_SCAN_CODE_SET);
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 keyboard failed to set scan code set");
/* We want Scan Code Set 1 */
v0::PS2WriteData(DriverID, PS2_KBD_SCAN_CODE_SET_2); /* It will set to 1 but with translation? */
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 keyboard failed to set scan code set 2");
v0::PS2WriteData(DriverID, PS2_KBD_CMD_SCAN_CODE_SET);
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 keyboard failed to set scan code set");
v0::PS2WriteData(DriverID, PS2_KBD_SCAN_CODE_GET_CURRENT);
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 keyboard failed to get current scan code set");
KeyboardScanCodeSet = v0::PS2ReadAfterACK(DriverID);
trace("PS/2 Keyboard Scan Code Set: 0x%X", KeyboardScanCodeSet);
v0::PS2ClearOutputBuffer(DriverID);
v0::PS2WriteData(DriverID, PS2_KBD_CMD_ENABLE_SCANNING);
v0::RegisterInterruptHandler(DriverID, 1, (void *)PS2KbdInterruptHandler);
KeyboardDevID = v0::RegisterDevice(DriverID, INPUT_TYPE_KEYBOARD, &KbdOps);
return 0;
}
int FinalizeKeyboard()
{
v0::PS2WriteData(DriverID, PS2_KBD_CMD_DISABLE_SCANNING);
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 keyboard failed to disable scanning");
v0::UnregisterDevice(DriverID, KeyboardDevID);
return 0;
}
int DetectPS2Keyboard()
{
v0::PS2WriteData(DriverID, PS2_KBD_CMD_DISABLE_SCANNING);
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 keyboard failed to disable scanning");
v0::PS2WriteData(DriverID, PS2_KBD_CMD_IDENTIFY);
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 keyboard failed to identify");
uint8_t recByte;
int timeout = 1000000;
while (timeout--)
{
recByte = v0::PS2ReadData(DriverID);
if (recByte != PS2_ACK)
break;
}
Device1ID[0] = recByte;
timeout = 1000000;
while (timeout--)
{
recByte = v0::PS2ReadData(DriverID);
if (recByte != PS2_ACK)
break;
}
if (timeout == 0)
trace("PS/2 keyboard second byte timed out");
else
Device1ID[1] = recByte;
trace("PS2 Keyboard Device: 0x%X 0x%X", Device1ID[0], Device1ID[1]);
return 0;
}
}

View File

@ -0,0 +1,260 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <interface/aip.h>
#include <cpu.hpp>
#include <pci.hpp>
#include "aip.hpp"
extern Driver::Manager *DriverManager;
extern PCI::Manager *PCIManager;
EXTERNC void KPrint(const char *Format, ...);
namespace Driver::AdvancedIntegratedPeripheral
{
dev_t DriverID;
bool IsATAPresent()
{
outb(0x1F0 + 2, 0);
outb(0x1F0 + 3, 0);
outb(0x1F0 + 4, 0);
outb(0x1F0 + 5, 0);
outb(0x1F0 + 7, 0xEC);
if (inb(0x1F0 + 7) == 0 || inb(0x1F0 + 1) != 0)
return false;
return true;
}
bool IsKeyboard(uint8_t ID)
{
/* Common keyboard IDs */
return ID == 0xAB || ID == 0xAC || ID == 0x5D ||
ID == 0x2B || ID == 0x47 || ID == 0x60;
}
bool IsMouse(uint8_t ID)
{
/* Common mouse IDs */
return ID == 0x00 || ID == 0x03 || ID == 0x04;
}
const char *GetPS2DeviceName(uint8_t ID, uint8_t SubID)
{
switch (ID)
{
case 0x00:
return "Standard PS/2 Mouse";
case 0x03:
return "Mouse with scroll wheel";
case 0x04:
return "Mouse 5 buttons";
case 0xAB:
{
switch (SubID)
{
case 0x83: /* Normal */
case 0x41: /* Translated */
case 0xC1: /* Normal + Translated */
return "Standard PS/2 Keyboard";
case 0x84:
case 0x54:
return "IBM Thinkpad/Spacesaver Keyboard";
case 0x85:
return "NCD N-97/122-Key Host Connect(ed) Keyboard";
case 0x86:
return "122-Key Keyboard";
case 0x90:
return "Japanese \"G\" Keyboard";
case 0x91:
return "Japanese \"P\" Keyboard";
case 0x92:
return "Japanese \"A\" Keyboard";
default:
return "Unknown PS/2 Keyboard";
}
}
case 0xAC:
{
switch (SubID)
{
case 0xA1:
return "NCD Sun Keyboard";
default:
return "Unknown NCD Sun Keyboard";
}
}
case 0x5D:
case 0x2B:
return "Trust Keyboard";
case 0x47:
case 0x60:
return "NMB SGI Keyboard";
default:
return "Unknown PS/2 Device";
}
}
uint8_t Device1ID[2] = {0x00, 0x00};
uint8_t Device2ID[2] = {0x00, 0x00};
bool DualChannel = false;
bool ATAPresent = false;
int Entry()
{
v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_1);
v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_2);
v0::PS2ClearOutputBuffer(DriverID);
v0::PS2WriteCommand(DriverID, PS2_CMD_READ_CONFIG);
PS2_CONFIGURATION cfg = {.Raw = v0::PS2ReadData(DriverID)};
DualChannel = cfg.Port2Clock;
if (DualChannel)
trace("Dual channel PS/2 controller detected");
cfg.Port1Interrupt = 1;
cfg.Port2Interrupt = 1;
cfg.Port1Translation = 1;
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_CONFIG);
v0::PS2WriteData(DriverID, cfg.Raw);
v0::PS2WriteCommand(DriverID, PS2_CMD_TEST_CONTROLLER);
uint8_t test = v0::PS2ReadData(DriverID);
if (test != PS2_TEST_PASSED)
{
trace("PS/2 controller self test failed (%#x)", test);
return -EFAULT;
}
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_CONFIG);
v0::PS2WriteData(DriverID, cfg.Raw);
// bool port2avail = false;
// if (DualChannel)
// {
// v0::PS2WriteCommand(DriverID, PS2_CMD_ENABLE_PORT_1);
// v0::PS2WriteCommand(DriverID, PS2_CMD_READ_CONFIG);
// cfg.Raw = v0::PS2ReadData(DriverID);
// port2avail = cfg.Port2Clock;
// v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_1);
// }
v0::PS2WriteCommand(DriverID, PS2_CMD_TEST_PORT_1);
test = v0::PS2ReadData(DriverID);
if (test != 0x00)
{
trace("PS/2 Port 1 self test failed (%#x)", test);
return -EFAULT;
}
if (DualChannel)
{
v0::PS2WriteCommand(DriverID, PS2_CMD_TEST_PORT_2);
test = v0::PS2ReadData(DriverID);
if (test != 0x00)
{
trace("PS/2 Port 2 self test failed (%#x)", test);
return -EFAULT;
}
}
v0::PS2WriteCommand(DriverID, PS2_CMD_ENABLE_PORT_1);
if (DualChannel)
v0::PS2WriteCommand(DriverID, PS2_CMD_ENABLE_PORT_2);
int errK = InitializeKeyboard();
int errM = 0;
if (DualChannel)
errM = InitializeMouse();
ATAPresent = IsATAPresent();
if (errK != 0 && errM != 0 && ATAPresent == false)
return -ENODEV;
return 0;
}
int Final()
{
FinalizeKeyboard();
FinalizeMouse();
v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_1);
v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_2);
return 0;
}
int Panic()
{
v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_1);
v0::PS2WriteCommand(DriverID, PS2_CMD_DISABLE_PORT_2);
return 0;
}
void __intStub() {}
int Probe()
{
v0::RegisterInterruptHandler(DriverID, 1, (void *)__intStub);
v0::RegisterInterruptHandler(DriverID, 12, (void *)__intStub);
int kbd = DetectPS2Keyboard();
int mouse = DetectPS2Mouse();
int uart = DetectUART();
v0::UnregisterAllInterruptHandlers(DriverID, (void *)__intStub);
if (kbd != 0 && mouse != 0 && uart != 0)
return -ENODEV;
if (kbd == 0)
{
if (!IsKeyboard(Device1ID[0]))
{
trace("PS/2 Port 1 is not a keyboard");
// return -EINVAL;
}
}
if (mouse == 0)
{
if (!IsMouse(Device2ID[0]))
{
trace("PS/2 Port 2 is not a mouse");
// return -EINVAL;
}
}
KPrint("PS/2 Port 1: %s (0x%X 0x%X)",
GetPS2DeviceName(Device1ID[0], Device1ID[1]),
Device1ID[0], Device1ID[1]);
KPrint("PS/2 Port 2: %s (0x%X 0x%X)",
GetPS2DeviceName(Device2ID[0], Device2ID[1]),
Device2ID[0], Device2ID[1]);
return 0;
}
REGISTER_BUILTIN_DRIVER(aip,
"Advanced Integrated Peripheral Driver",
"enderice2",
1, 0, 0,
Entry,
Final,
Panic,
Probe);
}

View File

@ -0,0 +1,263 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#include "aip.hpp"
#include <driver.hpp>
#include <interface/aip.h>
#include <interface/input.h>
namespace Driver::AdvancedIntegratedPeripheral
{
extern dev_t DriverID;
dev_t MouseDevID = -1;
bool PacketReady = false;
bool FourPackets = false;
bool MouseButton45 = false;
uint8_t Cycle = 0;
PS2_MOUSE_PACKET Packet = {};
InputReport mir = {};
void PS2MouseInterruptHandler(CPU::TrapFrame *)
{
uint8_t data = v0::PS2ReadData(DriverID);
if (data == PS2_MOUSE_RESP_ACK ||
data == PS2_MOUSE_RESP_RESEND)
return;
if (!PacketReady)
{
switch (Cycle)
{
case 0:
{
if ((data & 0b00001000 /* Always 1 */) == 0)
return;
Packet.Base.Raw = data;
Cycle++;
break;
}
case 1:
{
Packet.XMovement = data;
Cycle++;
break;
}
case 2:
{
Packet.YMovement = data;
if (FourPackets)
Cycle++;
else
{
Cycle = 0;
PacketReady = true;
}
break;
}
case 3:
{
Packet.ZMovement.Raw = data;
Cycle = 0;
PacketReady = true;
break;
}
default:
break;
}
return;
}
/* https://stackoverflow.com/a/3208376/9352057 */
#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
#define BYTE_TO_BINARY(byte) \
((byte) & 0x80 ? '1' : '0'), \
((byte) & 0x40 ? '1' : '0'), \
((byte) & 0x20 ? '1' : '0'), \
((byte) & 0x10 ? '1' : '0'), \
((byte) & 0x08 ? '1' : '0'), \
((byte) & 0x04 ? '1' : '0'), \
((byte) & 0x02 ? '1' : '0'), \
((byte) & 0x01 ? '1' : '0')
debug("PS/2 Mouse Packet: [" BYTE_TO_BINARY_PATTERN ":" BYTE_TO_BINARY_PATTERN ":" BYTE_TO_BINARY_PATTERN ":" BYTE_TO_BINARY_PATTERN "] LB:%d RB:%d MB:%d A1:%d XS:%d YS:%d XO:%d YO:%d | X:%03d Y:%03d | Z:%d B4:%d B5:%d A0:%d A0:%d",
BYTE_TO_BINARY(Packet.Base.Raw),
BYTE_TO_BINARY(Packet.XMovement),
BYTE_TO_BINARY(Packet.YMovement),
BYTE_TO_BINARY(Packet.ZMovement.Raw),
Packet.Base.LeftButton, Packet.Base.RightButton, Packet.Base.MiddleButton,
Packet.Base.Always1,
Packet.Base.XSign, Packet.Base.YSign,
Packet.Base.XOverflow, Packet.Base.YOverflow,
Packet.XMovement, Packet.YMovement,
Packet.ZMovement.Z, Packet.ZMovement.Button4, Packet.ZMovement.Button5,
Packet.ZMovement.Always0, Packet.ZMovement.Always0_2);
int X, Y;
X = Packet.XMovement - (Packet.Base.XSign ? 256 : 0);
Y = Packet.YMovement - (Packet.Base.YSign ? 256 : 0);
if (Packet.Base.XOverflow)
X = 0;
if (Packet.Base.YOverflow)
Y = 0;
mir.Type = INPUT_TYPE_MOUSE;
mir.Device = MouseDevID;
mir.Mouse.LeftButton = Packet.Base.LeftButton;
mir.Mouse.RightButton = Packet.Base.RightButton;
mir.Mouse.MiddleButton = Packet.Base.MiddleButton;
mir.Mouse.Button4 = Packet.ZMovement.Button4;
mir.Mouse.Button5 = Packet.ZMovement.Button5;
mir.Mouse.X = X;
mir.Mouse.Y = -Y;
mir.Mouse.Z = Packet.ZMovement.Z;
v0::ReportInputEvent(DriverID, &mir);
PacketReady = false;
}
void MouseSampleRate(uint8_t SampleRate)
{
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_SET_SAMPLE_RATE);
v0::PS2ReadData(DriverID);
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, SampleRate);
v0::PS2ReadData(DriverID);
}
int __fs_ms_Ioctl(struct Inode *, unsigned long, void *)
{
return 0;
}
const struct InodeOperations MouseOps = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = nullptr,
.Write = nullptr,
.Truncate = nullptr,
.Open = nullptr,
.Close = nullptr,
.Ioctl = __fs_ms_Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = nullptr,
.Stat = nullptr,
};
int InitializeMouse()
{
v0::PS2WriteData(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_RESET);
uint8_t test = v0::PS2ReadData(DriverID);
if (test != PS2_MOUSE_RESP_TEST_PASSED &&
test != PS2_MOUSE_RESP_ACK)
{
trace("PS/2 mouse reset failed! (%#x)", test);
return -EFAULT;
}
v0::RegisterInterruptHandler(DriverID, 12, (void *)PS2MouseInterruptHandler);
MouseDevID = v0::RegisterDevice(DriverID, INPUT_TYPE_MOUSE, &MouseOps);
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_SET_DEFAULTS);
v0::PS2ReadData(DriverID);
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_ENABLE_DATA_REPORTING);
MouseSampleRate(200);
MouseSampleRate(100);
MouseSampleRate(80);
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_READ_ID);
uint8_t Device2ID = v0::PS2ReadData(DriverID);
trace("PS/2 Mouse ID: %#x", Device2ID);
MouseSampleRate(200);
MouseSampleRate(200);
MouseSampleRate(80);
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_READ_ID);
Device2ID = v0::PS2ReadData(DriverID);
trace("PS/2 Mouse ID: %#x", Device2ID);
if (Device2ID >= 3 && Device2ID <= 4)
FourPackets = true;
if (Device2ID == 4)
MouseButton45 = true;
return 0;
}
int FinalizeMouse()
{
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_DISABLE_DATA_REPORTING);
v0::UnregisterDevice(DriverID, MouseDevID);
return 0;
}
int DetectPS2Mouse()
{
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_DISABLE_DATA_REPORTING);
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 mouse failed to disable data reporting!");
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_READ_ID);
if (v0::PS2ACKTimeout(DriverID) != 0)
trace("PS/2 mouse failed to read ID!");
uint8_t recByte;
int timeout = 1000000;
while (timeout--)
{
recByte = v0::PS2ReadData(DriverID);
if (recByte != PS2_ACK)
break;
}
Device2ID[0] = recByte;
timeout = 1000000;
while (timeout--)
{
recByte = v0::PS2ReadData(DriverID);
if (recByte != PS2_ACK)
break;
}
Device2ID[1] = recByte;
trace("PS2 Mouse Device: 0x%X 0x%X", Device2ID[0], Device2ID[1]);
return 0;
}
}

View File

@ -0,0 +1,154 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <interface/driver.h>
#include <interface/input.h>
const unsigned short ScanCodeSet1[] =
{KEY_NULL, KEY_ESCAPE,
KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0,
KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, KEY_TAB,
KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P,
KEY_LEFT_BRACKET, KEY_RIGHT_BRACKET, KEY_RETURN, KEY_LEFT_CTRL,
KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L,
KEY_SEMICOLON, KEY_APOSTROPHE, KEY_BACK_TICK, KEY_LEFT_SHIFT, KEY_BACKSLASH,
KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M,
KEY_COMMA, KEY_PERIOD, KEY_SLASH, KEY_RIGHT_SHIFT,
KEYPAD_ASTERISK, KEY_LEFT_ALT, KEY_SPACE, KEY_CAPS_LOCK,
KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10,
KEY_NUM_LOCK, KEY_SCROLL_LOCK,
KEYPAD_7, KEYPAD_8, KEYPAD_9, KEYPAD_MINUS,
KEYPAD_4, KEYPAD_5, KEYPAD_6, KEYPAD_PLUS,
KEYPAD_1, KEYPAD_2, KEYPAD_3, KEYPAD_0, KEYPAD_PERIOD,
KEY_NULL, KEY_NULL, KEY_NULL,
KEY_F11, KEY_F12};
const unsigned short ScanCodeSet1mm[] = {
[0x10] = KEY_MULTIMEDIA_PREV_TRACK,
[0x19] = KEY_MULTIMEDIA_NEXT_TRACK,
[0x1C] = KEYPAD_RETURN,
[0x1D] = KEY_RIGHT_CTRL,
[0x20] = KEY_MULTIMEDIA_MUTE,
[0x21] = KEY_MULTIMEDIA_CALCULATOR,
[0x22] = KEY_MULTIMEDIA_PLAY,
[0x24] = KEY_MULTIMEDIA_STOP,
[0x2A] = KEY_PRINT_SCREEN,
[0x2E] = KEY_MULTIMEDIA_VOL_DOWN,
[0x30] = KEY_MULTIMEDIA_VOL_UP,
[0x32] = KEY_MULTIMEDIA_WWW_HOME,
[0x35] = KEYPAD_SLASH,
[0x37] = KEY_PRINT_SCREEN,
[0x38] = KEY_RIGHT_ALT,
[0x47] = KEY_HOME,
[0x48] = KEY_UP_ARROW,
[0x49] = KEY_PAGE_UP,
[0x4B] = KEY_LEFT_ARROW,
[0x4D] = KEY_RIGHT_ARROW,
[0x4F] = KEY_END,
[0x50] = KEY_DOWN_ARROW,
[0x51] = KEY_PAGE_DOWN,
[0x52] = KEY_INSERT,
[0x53] = KEY_DELETE,
[0x5B] = KEY_LEFT_GUI,
[0x5C] = KEY_RIGHT_GUI,
[0x5D] = KEY_APPS,
[0x5E] = KEY_ACPI_POWER,
[0x5F] = KEY_ACPI_SLEEP,
[0x63] = KEY_ACPI_WAKE,
[0x65] = KEY_MULTIMEDIA_WWW_SEARCH,
[0x66] = KEY_MULTIMEDIA_WWW_FAVORITES,
[0x67] = KEY_MULTIMEDIA_WWW_REFRESH,
[0x68] = KEY_MULTIMEDIA_WWW_STOP,
[0x69] = KEY_MULTIMEDIA_WWW_FORWARD,
[0x6A] = KEY_MULTIMEDIA_WWW_BACK,
[0x6B] = KEY_MULTIMEDIA_MY_COMPUTER,
[0x6C] = KEY_MULTIMEDIA_EMAIL,
[0x6D] = KEY_MULTIMEDIA_MEDIA_SELECT,
/* RELEASED */
[0x90] = KEY_MULTIMEDIA_PREV_TRACK,
[0x99] = KEY_MULTIMEDIA_NEXT_TRACK,
[0x9C] = KEYPAD_RETURN,
[0x9D] = KEY_RIGHT_CTRL,
[0xA0] = KEY_MULTIMEDIA_MUTE,
[0xA1] = KEY_MULTIMEDIA_CALCULATOR,
[0xA2] = KEY_MULTIMEDIA_PLAY,
[0xA4] = KEY_MULTIMEDIA_STOP,
[0xAA] = KEY_PRINT_SCREEN,
[0xAE] = KEY_MULTIMEDIA_VOL_DOWN,
[0xB0] = KEY_MULTIMEDIA_VOL_UP,
[0xB2] = KEY_MULTIMEDIA_WWW_HOME,
[0xB5] = KEYPAD_SLASH,
[0xB7] = KEY_PRINT_SCREEN,
[0xB8] = KEY_RIGHT_ALT,
[0xC7] = KEY_HOME,
[0xC8] = KEY_UP_ARROW,
[0xC9] = KEY_PAGE_UP,
[0xCB] = KEY_LEFT_ARROW,
[0xCD] = KEY_RIGHT_ARROW,
[0xCF] = KEY_END,
[0xD0] = KEY_DOWN_ARROW,
[0xD1] = KEY_PAGE_DOWN,
[0xD2] = KEY_INSERT,
[0xD3] = KEY_DELETE,
[0xDB] = KEY_LEFT_GUI,
[0xDC] = KEY_RIGHT_GUI,
[0xDD] = KEY_APPS,
[0xDE] = KEY_ACPI_POWER,
[0xDF] = KEY_ACPI_SLEEP,
[0xE3] = KEY_ACPI_WAKE,
[0xE5] = KEY_MULTIMEDIA_WWW_SEARCH,
[0xE6] = KEY_MULTIMEDIA_WWW_FAVORITES,
[0xE7] = KEY_MULTIMEDIA_WWW_REFRESH,
[0xE8] = KEY_MULTIMEDIA_WWW_STOP,
[0xE9] = KEY_MULTIMEDIA_WWW_FORWARD,
[0xEA] = KEY_MULTIMEDIA_WWW_BACK,
[0xEB] = KEY_MULTIMEDIA_MY_COMPUTER,
[0xEC] = KEY_MULTIMEDIA_EMAIL,
[0xED] = KEY_MULTIMEDIA_MEDIA_SELECT};
const unsigned short ScanCodeSet3[] = {
[0x15] = KEY_Q,
[0x1A] = KEY_Z,
[0x1B] = KEY_S,
[0x1C] = KEY_A,
[0x1D] = KEY_W,
[0x21] = KEY_C,
[0x22] = KEY_X,
[0x23] = KEY_D,
[0x24] = KEY_E,
[0x2A] = KEY_V,
[0x2B] = KEY_F,
[0x2C] = KEY_T,
[0x2D] = KEY_R,
[0x31] = KEY_N,
[0x32] = KEY_B,
[0x33] = KEY_H,
[0x34] = KEY_G,
[0x35] = KEY_Y,
[0x3A] = KEY_M,
[0x3B] = KEY_J,
[0x3C] = KEY_U,
[0x42] = KEY_K,
[0x43] = KEY_I,
[0x44] = KEY_O,
[0x4B] = KEY_L,
[0x4D] = KEY_P};

View File

@ -0,0 +1,647 @@
/*
This file is part of Fennix Drivers.
Fennix Drivers is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Drivers is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Drivers. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <io.h>
#include "aip.hpp"
namespace Driver::AdvancedIntegratedPeripheral
{
extern dev_t DriverID;
#define SERIAL_ENABLE_DLAB 0x80
#define SERIAL_BUFFER_EMPTY 0x20
enum Ports
{
COM1 = 0x3F8,
COM2 = 0x2F8,
COM3 = 0x3E8,
COM4 = 0x2E8,
COM5 = 0x5F8,
COM6 = 0x4F8,
COM7 = 0x5E8,
COM8 = 0x4E8,
LPT1 = 0x378,
LPT2 = 0x278,
LPT3 = 0x3BC
};
enum SerialSpeed
{
RATE_50_HI = 0x09,
RATE_50_LO = 0x00,
RATE_300_HI = 0x01,
RATE_300_LO = 0x80,
RATE_600_HI = 0x00,
RATE_600_LO = 0xC0,
RATE_2400_HI = 0x00,
RATE_2400_LO = 0x30,
RATE_4800_HI = 0x00,
RATE_4800_LO = 0x18,
RATE_9600_HI = 0x00,
RATE_9600_LO = 0x0C,
RATE_19200_HI = 0x00,
RATE_19200_LO = 0x06,
RATE_38400_HI = 0x00,
RATE_38400_LO = 0x03,
RATE_57600_HI = 0x00,
RATE_57600_LO = 0x02,
RATE_115200_HI = 0x00,
RATE_115200_LO = 0x01
};
/*
. Table of Registers .
/---------------------------------------------------------------------\
| Base Address | DLAB | R/W | Abr | Register Name |
|---------------------------------------------------------------------|
| +0 | =0 | W | - | Transmitter Holding Buffer |
| | =0 | R | - | Receiver Buffer |
| | =1 | R/W | - | Divisor Latch Low Byte |
| +1 | =0 | R/W | IER | Interrupt Enable Register |
| | =1 | R/W | - | Divisor Latch High Byte |
| +2 | - | R | IIR | Interrupt Identification Register |
| | - | W | FCR | FIFO Control Register |
| +3 | - | R/W | LCR | Line Control Register |
| +4 | - | R/W | MCR | Modem Control Register |
| +5 | - | R | LSR | Line Status Register |
| +6 | - | R | MSR | Modem Status Register |
| +7 | - | R/W | - | Scratch Register |
\---------------------------------------------------------------------/
Source:
Interfacing the Serial / RS232 Port V5.0
Table 5 : Table of Registers
*/
/** Interrupt Enable Register */
typedef union
{
struct
{
/* Enable Received Data Available Interrupt */
uint8_t InterruptOnReceive : 1;
/* Enable Transmitter Holding Register Empty Interrupt */
uint8_t InterruptOnTransmitter : 1;
/* Enable Receiver Line Status Interrupt */
uint8_t LineStatusInterrupt : 1;
/* Enable Modem Status Interrupt */
uint8_t ModemStatusInterrupt : 1;
/* Enables Sleep Mode (16750) */
uint8_t SleepMode : 1;
/* Enables Low Power Mode (16750) */
uint8_t LowPowerMode : 1;
/* Reserved */
uint8_t __reserved : 2;
};
uint8_t raw;
} IER;
/** Interrupt Identification Register */
typedef union
{
struct
{
/* Interrupt pending */
uint8_t InterruptPending : 1;
/**
* Interrupt Status
*
* 00b = Modem Status Interrupt
* 01b = Transmitter Holding Register Empty Interrupt
* 10b = Received Data Available Interrupt
* 11b = Receiver Line Status Interrupt
*/
uint8_t InterruptStatus : 2;
/**
* 16550 Time-out Interrupt Pending
*
* @note Reserved on 8250, 16450
*/
uint8_t TimeOutIP : 1;
/** Reserved */
uint8_t __reserved : 1;
/** 64 Byte Fifo Enabled (16750 only) */
uint8_t FIFO64 : 1;
/**
* Enable FIFO
*
* 00b = No FIFO
* 01b = FIFO Enabled but Unusable
* 11b = FIFO Enabled
*/
uint8_t FIFO : 2;
};
uint8_t raw;
} IIR;
/** First In / First Out Control Register */
typedef union
{
struct
{
/** Enable FIFO's */
uint8_t FIFO : 1;
/** Clear Receive FIFO */
uint8_t ClearRX : 1;
/** Clear Transmit FIFO */
uint8_t ClearTX : 1;
/** DMA Mode Select.
*
* Change status of RXRDY & TXRDY pins from mode 1 to mode 2.
*/
uint8_t DMAMode : 1;
/** Reserved */
uint8_t __reserved : 1;
/** Enable 64 Byte FIFO (16750 only) */
uint8_t FIFO64 : 1;
/** Interrupt Trigger Level
*
* 00b = 1 Byte
* 01b = 4 Bytes
* 10b = 8 Bytes
* 11b = 14 Bytes
*/
uint8_t TriggerLevel : 2;
};
uint8_t raw;
} FCR;
/** Line Control Register */
typedef union
{
struct
{
/** Word Length
*
* 00b = 5 bits
* 01b = 6 bits
* 10b = 7 bits
* 11b = 8 bits
*/
uint8_t WordLength : 2;
/** Length of Stop Bit
*
* 0b = One Stop Bit
* 1b = 2 Stop bits for words of length 6,7 or 8 bits or 1.5 Stop Bits for Word lengths of 5 bits.
*/
uint8_t StopBit : 1;
/** Parity Select
*
* 0b = No Parity
* 001b = Odd Parity
* 011b = Even Parity
* 101b = High Parity (Sticky)
* 111b = Low Parity (Sticky)
*/
uint8_t Parity : 3;
/** Set Break Enable */
uint8_t SetBreak : 1;
/**
* Divisor Latch Access
*
* 0b = Access to Receiver buffer, Transmitter buffer & Interrupt Enable Register
* 1b = Divisor Latch Access Bit
*/
uint8_t DLAB : 1;
};
uint8_t raw;
} LCR;
/** Modem Control Register */
typedef union
{
struct
{
/** Force Data Terminal Ready */
uint8_t DataTerminalReady : 1;
/** Force Request to Send */
uint8_t RequestToSend : 1;
/** Auxiliary Output 1 */
uint8_t Out1 : 1;
/** Auxiliary Output 2 */
uint8_t Out2 : 1;
/** Loopback Mode */
uint8_t Loopback : 1;
/** Autoflow Control Enabled (16750 only) */
uint8_t Autoflow : 1;
/** Reserved */
uint8_t __reserved : 2;
};
uint8_t raw;
} MCR;
/** Line Status Register */
typedef union
{
struct
{
/** Data Ready */
uint8_t DataReady : 1;
/** Overrun Error */
uint8_t OverrunError : 1;
/** Parity Error */
uint8_t ParityError : 1;
/** Framing Error */
uint8_t FramingError : 1;
/** Break Interrupt */
uint8_t BreakInterrupt : 1;
/** Empty Transmitter Holding Register */
uint8_t EmptyTransmitterHolding : 1;
/** Empty Data Holding Registers */
uint8_t EmptyDataHolding : 1;
/** Error in Received FIFO */
uint8_t ErrorReceivedFIFO : 1;
};
uint8_t raw;
} LSR;
/** Modem Status Register */
typedef union
{
struct
{
/** Delta Clear to Send */
uint8_t DeltaClearToSend : 1;
/** Delta Data Set Ready */
uint8_t DeltaDataSetReady : 1;
/** Trailing Edge Ring Indicator */
uint8_t TrailingEdgeRingIndicator : 1;
/** Delta Data Carrier Detect */
uint8_t DeltaDataCarrierDetect : 1;
/** Clear To Send */
uint8_t ClearToSend : 1;
/** Data Set Ready */
uint8_t DataSetReady : 1;
/** Ring Indicator */
uint8_t RingIndicator : 1;
/** Carrier Detect */
uint8_t CarrierDetect : 1;
};
uint8_t raw;
} MSR;
union UARTs
{
struct
{
uint8_t com1 : 1;
uint8_t com2 : 1;
uint8_t com3 : 1;
uint8_t com4 : 1;
uint8_t com5 : 1;
uint8_t com6 : 1;
uint8_t com7 : 1;
uint8_t com8 : 1;
uint8_t lpt1 : 1;
uint8_t lpt2 : 1;
uint8_t lpt3 : 1;
uint8_t __reserved : 5;
};
uint16_t raw;
} uart;
bool IsDataReady(uint16_t Port)
{
LSR lsr;
lsr.raw = inb(Port + 5);
return lsr.DataReady;
}
bool IsTransmitEmpty(uint16_t Port)
{
LSR lsr;
lsr.raw = inb(Port + 5);
return lsr.EmptyTransmitterHolding;
}
char ReadSerial(uint16_t Port)
{
while (!IsDataReady(Port))
v0::Yield(DriverID);
return inb(Port);
}
void WriteSerial(uint16_t Port, char Character)
{
while (!IsTransmitEmpty(Port))
v0::Yield(DriverID);
outb(Port, Character);
}
void ReportSerialReceived(uint8_t Data)
{
debug("%c", Data);
}
void UartCOM24(CPU::TrapFrame *)
{
LSR lsr2, lsr4;
do
{
lsr2.raw = inb(COM2 + 5);
if (lsr2.DataReady)
ReportSerialReceived(inb(COM2));
lsr4.raw = inb(COM4 + 5);
if (lsr4.DataReady)
ReportSerialReceived(inb(COM4));
} while (lsr2.DataReady || lsr4.DataReady);
}
void UartCOM13(CPU::TrapFrame *)
{
LSR lsr1, lsr3;
do
{
lsr1.raw = inb(COM1 + 5);
if (lsr1.DataReady)
ReportSerialReceived(inb(COM1));
lsr3.raw = inb(COM3 + 5);
if (lsr3.DataReady)
ReportSerialReceived(inb(COM3));
} while (lsr1.DataReady || lsr3.DataReady);
}
bool InitializePort(uint16_t Port)
{
v0::CriticalState cs = v0::EnterCriticalSection(DriverID);
LCR lcr = {};
IER ier = {};
FCR fcr = {};
MCR mcr = {};
outb(Port + 3, lcr.raw);
outb(Port + 1, ier.raw);
lcr.DLAB = 1;
outb(Port + 3, lcr.raw);
outb(Port + 0, RATE_115200_LO);
outb(Port + 1, RATE_115200_HI);
lcr.DLAB = 0;
lcr.WordLength = 0b11;
outb(Port + 3, lcr.raw);
fcr.FIFO = 1;
fcr.ClearRX = 1;
fcr.ClearTX = 1;
fcr.TriggerLevel = 0b11;
outb(Port + 2, fcr.raw);
mcr.DataTerminalReady = 1;
mcr.RequestToSend = 1;
mcr.Out2 = 1;
mcr.Loopback = 1;
outb(Port + 4, mcr.raw);
/* Test the serial port */
outb(Port + 0, 0x48);
uint8_t result = inb(Port + 0);
if (result != 0x48)
{
/* FIXME: DETECT BAUD RATE
Do multiple test to check if the output is garbage.
If so, reduce the baud rate until it works. */
v0::LeaveCriticalSection(DriverID, cs);
trace("Port %#X test failed!", Port);
return false;
}
/* Set normal operation mode */
mcr.DataTerminalReady = 1;
mcr.RequestToSend = 1;
mcr.Out1 = 1;
mcr.Out2 = 1;
mcr.Loopback = 0;
outb(Port + 4, mcr.raw);
/* Enable interrupts on receive */
ier.InterruptOnReceive = 1;
outb(Port + 1, ier.raw);
v0::RegisterInterruptHandler(DriverID, 3, (void *)UartCOM24);
v0::RegisterInterruptHandler(DriverID, 4, (void *)UartCOM13);
v0::LeaveCriticalSection(DriverID, cs);
trace("Port %#X initialized", Port);
return true;
}
int DetectUART()
{
uart.com1 = inb(COM1) != 0xFF ? true : false;
uart.com2 = inb(COM2) != 0xFF ? true : false;
uart.com3 = inb(COM3) != 0xFF ? true : false;
uart.com4 = inb(COM4) != 0xFF ? true : false;
uart.com5 = inb(COM5) != 0xFF ? true : false;
uart.com6 = inb(COM6) != 0xFF ? true : false;
uart.com7 = inb(COM7) != 0xFF ? true : false;
uart.com8 = inb(COM8) != 0xFF ? true : false;
uart.lpt1 = inb(LPT1) != 0xFF ? true : false;
uart.lpt2 = inb(LPT2) != 0xFF ? true : false;
uart.lpt3 = inb(LPT3) != 0xFF ? true : false;
if (uart.com1 == true)
if (InitializePort(COM1) == false)
uart.com1 = false;
if (uart.com2 == true)
if (InitializePort(COM2) == false)
uart.com1 = false;
if (uart.com3 == true)
if (InitializePort(COM3) == false)
uart.com1 = false;
if (uart.com4 == true)
if (InitializePort(COM4) == false)
uart.com1 = false;
if (uart.com5 == true)
if (InitializePort(COM5) == false)
uart.com1 = false;
if (uart.com6 == true)
if (InitializePort(COM6) == false)
uart.com1 = false;
if (uart.com7 == true)
if (InitializePort(COM7) == false)
uart.com1 = false;
if (uart.com8 == true)
if (InitializePort(COM8) == false)
uart.com1 = false;
if (uart.lpt1 == true)
trace("LPT1 is present");
if (uart.lpt2 == true)
trace("LPT2 is present");
if (uart.lpt3 == true)
trace("LPT3 is present");
return 0;
}
// static int once = 0;
// static uint8_t com4 = 0xFF;
// if (!once++)
// com4 = inb(0x2E8);
// if (com4 == 0xFF)
// CPU::Halt(true);
// char UserInputBuffer[256]{'\0'};
// int BackSpaceLimit = 0;
// while (true)
// {
// while ((inb(0x2E8 + 5) & 1) == 0)
// CPU::Pause();
// char key = inb(0x2E8);
// // debug("key: %d", key);
// if (key == '\x7f') /* Backspace (DEL) */
// {
// if (BackSpaceLimit <= 0)
// continue;
// char keyBuf[5] = {'\b', '\x1b', '[', 'K', '\0'};
// ExPrint(keyBuf);
// backspace(UserInputBuffer);
// BackSpaceLimit--;
// continue;
// }
// else if (key == '\x0d') /* Enter (CR) */
// {
// UserInput(UserInputBuffer);
// BackSpaceLimit = 0;
// UserInputBuffer[0] = '\0';
// continue;
// }
// else if (key == '\x1b') /* Escape */
// {
// char tmp[16]{'\0'};
// append(tmp, key);
// while ((inb(0x2E8 + 5) & 1) == 0)
// CPU::Pause();
// char key = inb(0x2E8);
// append(tmp, key);
// if (key == '[')
// {
// // 27 91
// // < 68
// // > 67
// // down 66
// // up 65
// while ((inb(0x2E8 + 5) & 1) == 0)
// CPU::Pause();
// key = inb(0x2E8);
// append(tmp, key);
// switch (key)
// {
// case 'A':
// key = KEY_D_UP;
// break;
// case 'B':
// key = KEY_D_DOWN;
// break;
// case 'C':
// key = KEY_D_RIGHT;
// break;
// case 'D':
// key = KEY_D_LEFT;
// break;
// default:
// {
// for (size_t i = 0; i < strlen(tmp); i++)
// {
// if ((int)sizeof(UserInputBuffer) <= BackSpaceLimit)
// continue;
// append(UserInputBuffer, tmp[i]);
// BackSpaceLimit++;
// char keyBuf[2] = {(char)tmp[i], '\0'};
// ExPrint(keyBuf);
// }
// continue;
// }
// }
// ArrowInput(key);
// continue;
// }
// }
// if ((int)sizeof(UserInputBuffer) <= BackSpaceLimit)
// continue;
// append(UserInputBuffer, key);
// BackSpaceLimit++;
// char keyBuf[2] = {(char)key, '\0'};
// ExPrint(keyBuf);
// }
}

View File

@ -0,0 +1,164 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <rand.hpp>
extern Driver::Manager *DriverManager;
namespace Driver::MemoryDevices
{
dev_t DriverID;
struct
{
dev_t null;
dev_t zero;
dev_t random;
dev_t urandom;
dev_t mem;
} ids;
int Open(struct Inode *Node, int Flags, mode_t Mode) { return -ENOENT; }
int Close(struct Inode *Node) { return -ENOSYS; }
int Ioctl(struct Inode *Node, unsigned long Request, void *Argp) { return -ENOSYS; }
ssize_t Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset)
{
dev_t min = Node->GetMinor();
if (min == ids.null)
return 0;
else if (min == ids.zero)
{
if (Size <= 0)
return 0;
memset(Buffer, 0, Size);
return Size;
}
else if (min == ids.random || min == ids.urandom)
{
if (Size <= 0)
return 0;
if (Size < sizeof(uint64_t))
{
uint8_t *buf = (uint8_t *)Buffer;
for (size_t i = 0; i < Size; i++)
buf[i] = (uint8_t)(Random::rand16() & 0xFF);
return Size;
}
uint64_t *buf = (uint64_t *)Buffer;
for (size_t i = 0; i < Size / sizeof(uint64_t); i++)
buf[i] = Random::rand64();
return Size;
}
else if (min == ids.mem)
{
stub;
return 0;
}
return -ENODEV;
}
ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset)
{
dev_t min = Node->GetMinor();
if (min == ids.null)
return Size;
else if (min == ids.zero)
return Size;
else if (min == ids.random || min == ids.urandom)
return Size;
else if (min == ids.mem)
return Size;
return -ENODEV;
}
off_t Seek(struct Inode *Node, off_t Offset) { return -ENOSYS; }
int Stat(struct Inode *Node, struct kstat *Stat) { return -ENOSYS; }
const struct InodeOperations ops = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = Read,
.Write = Write,
.Truncate = nullptr,
.Open = Open,
.Close = Close,
.Ioctl = Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = Seek,
.Stat = Stat,
};
int Entry()
{
mode_t mode = 0;
/* c rw- rw- rw- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH |
S_IFCHR;
ids.null = DriverManager->CreateDeviceFile(DriverID, "null", mode, &ops);
ids.zero = DriverManager->CreateDeviceFile(DriverID, "zero", mode, &ops);
ids.random = DriverManager->CreateDeviceFile(DriverID, "random", mode, &ops);
ids.urandom = DriverManager->CreateDeviceFile(DriverID, "urandom", mode, &ops);
/* c rw- r-- --- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP |
S_IFCHR;
ids.mem = DriverManager->CreateDeviceFile(DriverID, "mem", mode, &ops);
return 0;
}
int Final()
{
DriverManager->UnregisterDevice(DriverID, ids.null);
DriverManager->UnregisterDevice(DriverID, ids.zero);
DriverManager->UnregisterDevice(DriverID, ids.random);
DriverManager->UnregisterDevice(DriverID, ids.urandom);
DriverManager->UnregisterDevice(DriverID, ids.mem);
return 0;
}
int Panic() { return 0; }
int Probe() { return 0; }
REGISTER_BUILTIN_DRIVER(mem,
"Memory Devices Driver",
"enderice2",
1, 0, 0,
Entry,
Final,
Panic,
Probe);
}

View File

@ -0,0 +1,192 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <kcon.hpp>
#include <task.hpp>
#include <smp.hpp>
extern Driver::Manager *DriverManager;
namespace Driver::TeleTypeDevices
{
dev_t DriverID;
TTY::PTMXDevice *ptmx = nullptr;
struct
{
dev_t kcon;
dev_t tty;
dev_t ptmx;
} ids;
int Open(struct Inode *Node, int Flags, mode_t Mode)
{
dev_t min = Node->GetMinor();
if (min == ids.kcon)
return KernelConsole::CurrentTerminal.load()->Open(Flags, Mode);
else if (min == ids.tty)
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Open(Flags, Mode);
}
else if (min == ids.ptmx)
return ptmx->Open();
return -ENODEV;
}
int Close(struct Inode *Node)
{
dev_t min = Node->GetMinor();
if (min == ids.kcon)
return KernelConsole::CurrentTerminal.load()->Close();
else if (min == ids.tty)
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Close();
}
else if (min == ids.ptmx)
return ptmx->Close();
return -ENODEV;
}
int Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
{
dev_t min = Node->GetMinor();
if (min == ids.kcon)
return KernelConsole::CurrentTerminal.load()->Ioctl(Request, Argp);
else if (min == ids.tty)
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Ioctl(Request, Argp);
}
else if (min == ids.ptmx)
return -ENOSYS;
return -ENODEV;
}
ssize_t Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset)
{
dev_t min = Node->GetMinor();
if (min == ids.kcon)
return KernelConsole::CurrentTerminal.load()->Read(Buffer, Size, Offset);
else if (min == ids.tty)
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Read(Buffer, Size, Offset);
}
else if (min == ids.ptmx)
return -ENOSYS;
return -ENODEV;
}
ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset)
{
dev_t min = Node->GetMinor();
if (min == ids.kcon)
return KernelConsole::CurrentTerminal.load()->Write(Buffer, Size, Offset);
else if (min == ids.tty)
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Write(Buffer, Size, Offset);
}
else if (min == ids.ptmx)
return -ENOSYS;
return -ENODEV;
}
off_t Seek(struct Inode *Node, off_t Offset) { return -ENOSYS; }
int Stat(struct Inode *Node, struct kstat *Stat) { return -ENOSYS; }
const struct InodeOperations ops = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = Read,
.Write = Write,
.Truncate = nullptr,
.Open = Open,
.Close = Close,
.Ioctl = Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = Seek,
.Stat = Stat,
};
int Entry()
{
ptmx = new TTY::PTMXDevice;
mode_t mode = 0;
/* c rw- r-- --- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP |
S_IFCHR;
ids.kcon = DriverManager->CreateDeviceFile(DriverID, "kcon", mode, &ops);
/* c rw- rw- rw- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IRUSR | S_IWUSR |
S_IFCHR;
ids.tty = DriverManager->CreateDeviceFile(DriverID, "tty", mode, &ops);
ids.ptmx = DriverManager->CreateDeviceFile(DriverID, "ptmx", mode, &ops);
return 0;
}
int Final()
{
DriverManager->UnregisterDevice(DriverID, ids.kcon);
DriverManager->UnregisterDevice(DriverID, ids.tty);
DriverManager->UnregisterDevice(DriverID, ids.ptmx);
delete ptmx;
return 0;
}
int Panic() { return 0; }
int Probe() { return 0; }
REGISTER_BUILTIN_DRIVER(pty,
"Pseudo Terminal Devices Driver",
"enderice2",
1, 0, 0,
Entry,
Final,
Panic,
Probe);
}

View File

@ -0,0 +1,917 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <cpu.hpp>
#include <pci.hpp>
#include <io.h>
#include <interface/aip.h>
extern Driver::Manager *DriverManager;
extern PCI::Manager *PCIManager;
namespace Driver::VMwareToolBox
{
dev_t DriverID;
enum RPCMessages
{
MSG_OPEN,
MSG_SENDSIZE,
MSG_SENDPAYLOAD,
MSG_RECVSIZE,
MSG_RECVPAYLOAD,
MSG_RECVSTATUS,
MSG_CLOSE,
};
enum RPCStatus
{
STATUS_SUCCESS = 0x1,
STATUS_DORECV = 0x2,
STATUS_CPT = 0x10,
STATUS_HB = 0x80,
};
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
#define VMWARE_PORT 0x5658
#define VMWARE_PORTHB 0x5659
#define VMWARE_HYPERVISOR_HB 0x00000000
#define VMWARE_HYPERVISOR_OUT 0x00000001
#define CMD_GETVERSION 0xA
#define CMD_MESSAGE 0x1E
#define CMD_ABSPOINTER_DATA 0x27
#define CMD_ABSPOINTER_STATUS 0x28
#define CMD_ABSPOINTER_COMMAND 0x29
#define ABSPOINTER_ENABLE 0x45414552
#define ABSPOINTER_RELATIVE 0xF5
#define ABSPOINTER_ABSOLUTE 0x53424152
#define MESSAGE_RPCI 0x49435052
#define MESSAGE_TCLO 0x4f4c4354
#define FLAG_COOKIE 0x80000000
#define ToMsg(x) ((x) << 16 | CMD_MESSAGE)
#define HighWord(x) ((x & 0xFFFF0000) >> 16)
#define MESSAGE_HB_MSG 0
#define MESSAGE_OPEN_CHANNEL ToMsg(MSG_OPEN)
#define MESSAGE_CLOSE_CHANNEL ToMsg(MSG_CLOSE)
#define MESSAGE_SEND_SIZE ToMsg(MSG_SENDSIZE)
#define MESSAGE_SEND_PAYLOAD ToMsg(MSG_SENDPAYLOAD)
#define MESSAGE_RECV_SIZE ToMsg(MSG_RECVSIZE)
#define MESSAGE_RECV_PAYLOAD ToMsg(MSG_RECVPAYLOAD)
#define MESSAGE_RECV_STATUS ToMsg(MSG_RECVSTATUS)
#if defined(__amd64__)
#define VM_PORT(cmd, in_ebx, isi, idi, \
flags, magic, \
ax, bx, cx, dx, si, di) \
__asm__ __volatile__("movw $0x5658, %%dx\n" \
"inl %%dx, %%eax\n" \
: "=a"(ax), \
"=b"(bx), \
"=c"(cx), \
"=d"(dx), \
"=S"(si), \
"=D"(di) \
: "a"(magic), \
"b"(in_ebx), \
"c"(cmd), \
"d"(flags), \
"S"(isi), \
"D"(idi) : "memory")
#define VM_PORT_HB_OUT(cmd, in_ecx, isi, idi, \
flags, magic, bp, \
ax, bx, cx, dx, si, di) \
__asm__ __volatile__("push %%rbp\n" \
"mov %12, %%rbp\n" \
"movw $0x5659, %%dx\n" \
"rep outsb\n" \
"pop %%rbp\n" \
: "=a"(ax), \
"=b"(bx), \
"=c"(cx), \
"=d"(dx), \
"=S"(si), \
"=D"(di) \
: "a"(magic), \
"b"(cmd), \
"c"(in_ecx), \
"d"(flags), \
"S"(isi), \
"D"(idi), \
"r"(bp) : "memory", "cc")
#define VM_PORT_HB_IN(cmd, in_ecx, isi, idi, \
flags, magic, bp, \
ax, bx, cx, dx, si, di) \
__asm__ __volatile__("push %%rbp\n" \
"mov %12, %%rbp\n" \
"movw $0x5659, %%dx\n" \
"rep insb\n" \
"pop %%rbp\n" \
: "=a"(ax), \
"=b"(bx), \
"=c"(cx), \
"=d"(dx), \
"=S"(si), \
"=D"(di) \
: "a"(magic), \
"b"(cmd), \
"c"(in_ecx), \
"d"(flags), \
"S"(isi), \
"D"(idi), \
"r"(bp) : "memory", "cc")
#elif defined(__i386__)
#define VM_PORT(cmd, in_ebx, isi, idi, \
flags, magic, \
ax, bx, cx, dx, si, di)
#define VM_PORT_HB_OUT(cmd, in_ecx, isi, idi, \
flags, \
magic, bp, ax, \
bx, cx, dx, si, di)
#define VM_PORT_HB_IN(cmd, in_ecx, isi, idi, \
flags, magic, bp, \
ax, bx, cx, dx, si, di)
#endif
/* TODO:
- use vmcall or vmmcall instead of "out" and "in" if available
*/
typedef struct
{
int TCLOChannel;
uint16_t ChannelID;
uint32_t CookieHigh;
uint32_t CookieLow;
} ToolboxContext;
dev_t MouseDevID = -1;
int __strcmp(const char *l, const char *r)
{
for (; *l == *r && *l; l++, r++)
;
return *(unsigned char *)l - *(unsigned char *)r;
}
void __cpuid(uint32_t Function,
uint32_t *eax, uint32_t *ebx,
uint32_t *ecx, uint32_t *edx)
{
asmv("cpuid" : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) : "a"(Function));
}
bool __CheckHypervisorBit()
{
uint32_t eax, ebx, ecx, edx;
__cpuid(0x1, &eax, &ebx, &ecx, &edx);
if (!(ecx & (1 << 31)))
return false; /* Hypervisor not detected */
return true;
}
bool __VMwareBackdoorHypervisors()
{
const char hv[13] = {0};
uint32_t eax, ebx, ecx, edx;
__cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
*(uint32_t *)hv = ebx;
*(uint32_t *)(hv + 4) = ecx;
*(uint32_t *)(hv + 8) = edx;
if (__strcmp(hv, "VMwareVMware") != 0 &&
__strcmp(hv, "KVMKVMKVM") != 0 &&
__strcmp(hv, "TCGTCGTCGTCG") != 0)
{
return false;
}
return true;
}
bool IsVMwareBackdoorAvailable()
{
if (!__CheckHypervisorBit())
return false;
if (!__VMwareBackdoorHypervisors())
return false;
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;
} cmd;
cmd.si = cmd.di = 0;
cmd.bx = ~0x564D5868;
cmd.command = 0xA;
cmd.magic = 0x564D5868;
cmd.port = 0x5658;
asmv("in %%dx, %0" : "+a"(cmd.ax), "+b"(cmd.bx),
"+c"(cmd.cx), "+d"(cmd.dx),
"+S"(cmd.si), "+D"(cmd.di));
if (cmd.bx != 0x564D5868 ||
cmd.ax == 0xFFFFFFFF)
return false;
return true;
}
static int OpenMessageChannel(ToolboxContext *ctx, uint32_t Protocol)
{
uintptr_t ax, bx, cx, dx, si = 0, di = 0;
VM_PORT(MESSAGE_OPEN_CHANNEL,
(Protocol | FLAG_COOKIE), si, di,
0, VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
if ((HighWord(cx) & STATUS_SUCCESS) == 0)
{
trace("Failed to open message channel %#lx", Protocol);
return -EINVAL;
}
debug("Opened message channel %d (Protocol: %#lx)",
HighWord(dx), Protocol);
ctx->ChannelID = (uint16_t)HighWord(dx);
ctx->CookieHigh = si;
ctx->CookieLow = di;
return 0;
}
static void MessageClose(ToolboxContext *ctx)
{
uintptr_t ax, bx, cx, dx,
si = ctx->CookieHigh,
di = ctx->CookieLow;
VM_PORT(MESSAGE_CLOSE_CHANNEL,
0, si, di,
ctx->ChannelID << 16,
VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
debug("Closed message channel %d", ctx->ChannelID);
}
static uintptr_t MessageSendHB(ToolboxContext *ctx,
const char *Message)
{
uintptr_t ax, bx, cx, dx,
si = (uintptr_t)Message,
di = ctx->CookieLow,
bp = ctx->CookieHigh;
uint32_t ChannelID = ctx->ChannelID << 16;
size_t Size = strlen(Message);
VM_PORT_HB_OUT((STATUS_SUCCESS << 16) | MESSAGE_HB_MSG,
Size, si, di,
VMWARE_HYPERVISOR_HB | ChannelID | VMWARE_HYPERVISOR_OUT,
VMWARE_MAGIC, bp,
ax, bx, cx, dx, si, di);
return bx;
}
static uintptr_t MessageSendLB(ToolboxContext *ctx,
const char *Message)
{
uintptr_t ax, bx,
cx = STATUS_SUCCESS << 16,
dx, si, di;
size_t Size = strlen(Message);
while (Size &&
(HighWord(cx) & STATUS_SUCCESS))
{
uint32_t TotalBytes = MIN((uint32_t)Size, (uint32_t)4);
uint32_t Word = 0;
memcpy(&Word, Message, TotalBytes);
Message += TotalBytes;
si = ctx->CookieHigh;
di = ctx->CookieLow;
VM_PORT(MESSAGE_SEND_PAYLOAD,
Word, si, di,
ctx->ChannelID << 16,
VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
}
return cx;
}
static uintptr_t MessageReceiveHB(ToolboxContext *ctx,
char *Buffer,
size_t BufferSize)
{
uintptr_t ax, bx, cx, dx,
si = ctx->CookieHigh,
di = (uintptr_t)Buffer,
bp = ctx->CookieLow;
uint32_t ChannelID = ctx->ChannelID << 16;
VM_PORT_HB_IN((STATUS_SUCCESS << 16) | MESSAGE_HB_MSG,
BufferSize, si, di,
VMWARE_HYPERVISOR_HB | ChannelID | VMWARE_HYPERVISOR_OUT,
VMWARE_MAGIC, bp,
ax, bx, cx, dx, si, di);
return bx;
}
static uintptr_t MessageReceiveLB(ToolboxContext *ctx,
char *Buffer,
size_t BufferSize)
{
uintptr_t ax, bx,
cx = STATUS_SUCCESS << 16,
dx, si, di;
while (BufferSize)
{
uint32_t TotalBytes = MIN((uint32_t)BufferSize, (uint32_t)4);
si = ctx->CookieHigh;
di = ctx->CookieLow;
VM_PORT(MESSAGE_RECV_PAYLOAD,
STATUS_SUCCESS, si, di,
ctx->ChannelID << 16,
VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
if ((HighWord(cx) & STATUS_SUCCESS) == 0)
break;
memcpy(Buffer, &bx, TotalBytes);
Buffer += TotalBytes;
BufferSize -= TotalBytes;
}
return cx;
}
static int MessageSend(ToolboxContext *ctx,
const char *Message)
{
uintptr_t ax, bx, cx, dx, si, di;
size_t Size = strlen(Message);
int Retries = 0;
while (Retries < 2)
{
Retries++;
si = ctx->CookieHigh;
di = ctx->CookieLow;
VM_PORT(MESSAGE_SEND_SIZE,
Size, si, di,
ctx->ChannelID << 16,
VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
if ((HighWord(cx) & STATUS_SUCCESS) == 0)
{
trace("Failed to send message size for \"%s\": %d",
Message, cx);
return -EINVAL;
}
bool HighBand = (HighWord(cx) & STATUS_HB) != 0;
if (HighBand)
bx = MessageSendHB(ctx, Message);
else
bx = MessageSendLB(ctx, Message);
int status = HighWord(bx);
if ((status & STATUS_SUCCESS) != 0)
{
debug("Message \"%s\" sent", Message);
return 0;
}
else if ((status & STATUS_CPT) == 0)
{
trace("Checkpoint occurred for message \"%s\"", Message);
continue;
}
else
break;
}
trace("Failed to send message \"%s\": %#lx", Message, bx);
return -EINVAL;
}
static int MessageReceive(ToolboxContext *ctx,
char **Buffer,
size_t *BufferSize)
{
uintptr_t ax, bx, cx, dx, si, di;
int Retries = 0;
*Buffer = NULL;
*BufferSize = 0;
char *ReplyBuf = NULL;
size_t ReplyBufPages = 0;
size_t ReplySize = 0;
while (Retries < 2)
{
Retries++;
si = ctx->CookieHigh;
di = ctx->CookieLow;
VM_PORT(MESSAGE_RECV_SIZE,
0, si, di,
ctx->ChannelID << 16,
VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
if ((HighWord(cx) & STATUS_SUCCESS) == 0)
{
trace("Failed to receive message size: %d", cx);
return -EINVAL;
}
else if ((HighWord(cx) & STATUS_DORECV) == 0)
{
debug("No message to receive");
return -EAGAIN;
}
ReplySize = bx;
if (ReplyBuf != NULL)
DriverManager->FreeMemory(DriverID, ReplyBuf, ReplyBufPages);
ReplyBufPages = ReplySize / 0x1000 + 1;
ReplyBuf = (char *)DriverManager->AllocateMemory(DriverID, ReplyBufPages);
bool HighBand = (HighWord(cx) & STATUS_HB) != 0;
if (HighBand)
bx = MessageReceiveHB(ctx, ReplyBuf, ReplySize);
else
bx = MessageReceiveLB(ctx, ReplyBuf, ReplySize);
if ((HighWord(bx) & STATUS_SUCCESS) == 0)
{
if ((HighWord(bx) & STATUS_CPT) == 0)
{
trace("Checkpoint occurred for message payload");
continue;
}
trace("Failed to receive message payload: %d", HighWord(bx));
DriverManager->FreeMemory(DriverID, ReplyBuf, ReplyBufPages);
return -EINVAL;
}
ReplyBuf[ReplySize] = '\0';
si = ctx->CookieHigh;
di = ctx->CookieLow;
VM_PORT(MESSAGE_RECV_STATUS,
STATUS_SUCCESS, si, di,
ctx->ChannelID << 16,
VMWARE_MAGIC,
ax, bx, cx, dx, si, di);
if ((HighWord(cx) & STATUS_SUCCESS) == 0)
{
if ((HighWord(cx) & STATUS_CPT) == 0)
{
trace("Retrying message receive");
continue;
}
trace("Failed to receive message status: %d", HighWord(cx));
DriverManager->FreeMemory(DriverID, ReplyBuf, ReplyBufPages);
return -EINVAL;
}
break;
}
if (ReplyBuf == NULL)
{
trace("Failed to receive message");
return -EINVAL;
}
*Buffer = ReplyBuf;
*BufferSize = ReplySize;
debug("Received message \"%s\"", ReplyBuf);
return 0;
}
static int SendRPCI(ToolboxContext *, const char *Request)
{
ToolboxContext rpci_ctx = {};
int status = OpenMessageChannel(&rpci_ctx, MESSAGE_RPCI);
if (status < 0)
{
trace("Failed to open RPCI channel: %d", status);
return status;
}
status = MessageSend(&rpci_ctx, Request);
if (status < 0)
{
trace("Failed to send RPCI request: %d", status);
return status;
}
MessageClose(&rpci_ctx);
return 0;
}
int MsgEqual(const char *haystack, const char *needle)
{
return strstr(haystack, needle) == haystack;
}
static int DisplayGetSize(ToolboxContext *ctx)
{
if (ctx->TCLOChannel != -1)
MessageClose(ctx);
OpenMessageChannel(ctx, MESSAGE_TCLO);
char EmptyBuffer[256] = {'\0'};
MessageSend(ctx, EmptyBuffer);
while (true)
{
/* FIXME: buf memory leak */
char *buf;
size_t len;
int status = MessageReceive(ctx, &buf, &len);
if (status == -EAGAIN)
{
v0::Sleep(DriverID, 1000);
continue;
}
else if (status < 0)
{
trace("Failed to receive message");
return 1;
}
buf[strlen(buf)] = '\0';
if (MsgEqual(buf, "reset"))
{
if (MessageSend(ctx, "OK ATR toolbox") < 0)
return 1;
}
else if (MsgEqual(buf, "ping"))
{
if (MessageSend(ctx, "OK ") < 0)
return 1;
}
else if (MsgEqual(buf, "Capabilities_Register"))
{
SendRPCI(ctx, "tools.capability.resolution_set 1");
SendRPCI(ctx, "tools.capability.resolution_server toolbox 1");
SendRPCI(ctx, "tools.capability.display_topology_set 1");
SendRPCI(ctx, "tools.capability.color_depth_set 1");
SendRPCI(ctx, "tools.capability.resolution_min 0 0");
SendRPCI(ctx, "tools.capability.unity 1");
if (MessageSend(ctx, "OK ") < 0)
return 1;
}
else if (MsgEqual(buf, "Resolution_Set"))
{
debug("%s", buf);
if (MessageSend(ctx, "OK ") < 0)
return 1;
MessageClose(ctx);
return 0;
}
else
{
if (MessageSend(ctx, "ERROR Unknown command") < 0)
return 1;
}
}
}
pid_t dst_id = -1;
pid_t dst_pid = -1;
ToolboxContext *tb_ctx = NULL;
void DisplayScaleThread()
{
/* sizeof ToolboxContext */
tb_ctx = (ToolboxContext *)DriverManager->AllocateMemory(DriverID, 1);
v0::Sleep(DriverID, 2000);
while (true)
{
if (DisplayGetSize(tb_ctx) != 0)
trace("Failed to scale display");
v0::Sleep(DriverID, 1000);
}
}
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));
}
void Absolute()
{
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()
{
VMwareCommand cmd = {};
cmd.bx = ABSPOINTER_RELATIVE;
cmd.command = CMD_ABSPOINTER_COMMAND;
CommandSend(&cmd);
}
InputReport ir = {};
void InterruptHandler(CPU::TrapFrame *)
{
uint8_t Data = inb(0x60);
(void)Data;
VMwareCommand cmd = {};
cmd.bx = 0;
cmd.command = CMD_ABSPOINTER_STATUS;
CommandSend(&cmd);
if (cmd.ax == 0xFFFF0000)
{
trace("VMware mouse is not connected?");
Relative();
Absolute();
return;
}
if ((cmd.ax & 0xFFFF) < 4)
return;
cmd.bx = 4;
cmd.command = CMD_ABSPOINTER_DATA;
CommandSend(&cmd);
int Buttons = (cmd.ax & 0xFFFF);
/**
* How should I handle this?
* (cmd.[bx,cx] * Width) / 0xFFFF
* Maybe TODO: Width and Height API?
*/
uintptr_t AbsoluteX = cmd.bx;
uintptr_t AbsoluteY = cmd.cx;
ir.Type = INPUT_TYPE_MOUSE;
ir.Device = MouseDevID;
ir.Mouse.X = AbsoluteX;
ir.Mouse.Y = AbsoluteY;
ir.Mouse.Z = (int8_t)cmd.dx;
ir.Mouse.Absolute = 1;
ir.Mouse.LeftButton = Buttons & 0x20;
ir.Mouse.RightButton = Buttons & 0x10;
ir.Mouse.MiddleButton = Buttons & 0x08;
// ir.Mouse.Button4 = 0x0;
// ir.Mouse.Button5 = 0x0;
// ir.Mouse.Button6 = 0x0;
// ir.Mouse.Button7 = 0x0;
// ir.Mouse.Button8 = 0x0;
v0::ReportInputEvent(DriverID, &ir);
}
int Ioctl(struct Inode *, unsigned long Request, void *)
{
switch (Request)
{
case 0x1:
Relative();
break;
case 0x2:
Absolute();
break;
default:
return -EINVAL;
}
return 0;
}
const struct InodeOperations ops = {
.Lookup = nullptr,
.Create = nullptr,
.Remove = nullptr,
.Rename = nullptr,
.Read = nullptr,
.Write = nullptr,
.Truncate = nullptr,
.Open = nullptr,
.Close = nullptr,
.Ioctl = Ioctl,
.ReadDir = nullptr,
.MkDir = nullptr,
.RmDir = nullptr,
.SymLink = nullptr,
.ReadLink = nullptr,
.Seek = nullptr,
.Stat = nullptr,
};
bool ToolboxSupported = false;
int Entry()
{
ToolboxContext tb_ctx = {};
/* Test if it's supported */
int status = OpenMessageChannel(&tb_ctx, MESSAGE_TCLO);
if (status == 0)
{
ToolboxSupported = true;
MessageClose(&tb_ctx);
dst_id = v0::CreateKernelThread(DriverID, 0, "VMware Display Scale",
(void *)DisplayScaleThread, NULL);
dst_pid = v0::GetCurrentProcess(DriverID);
}
v0::PS2WriteCommand(DriverID, PS2_CMD_ENABLE_PORT_2);
v0::PS2WriteCommand(DriverID, PS2_CMD_READ_CONFIG);
PS2_CONFIGURATION config = {.Raw = v0::PS2ReadData(DriverID)};
config.Port2Interrupt = 1;
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_CONFIG);
v0::PS2WriteData(DriverID, config.Raw);
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_SET_DEFAULTS);
v0::PS2ReadData(DriverID);
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_ENABLE_DATA_REPORTING);
v0::PS2ReadData(DriverID);
Absolute();
/**
* If we have another driver using the PS/2 mouse, we need to
* override its interrupt handler.
*/
v0::OverrideInterruptHandler(DriverID, 12, (void *)InterruptHandler);
MouseDevID = v0::RegisterDevice(DriverID, INPUT_TYPE_MOUSE, &ops);
return 0;
}
int Final()
{
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_DISABLE_DATA_REPORTING);
Relative();
v0::UnregisterDevice(DriverID, MouseDevID);
if (ToolboxSupported)
{
v0::KillThread(DriverID, dst_id, dst_pid, 0);
if (tb_ctx->TCLOChannel != -1)
MessageClose(tb_ctx);
DriverManager->FreeMemory(DriverID, tb_ctx, 1);
}
return 0;
}
int Panic()
{
Relative();
v0::PS2WriteCommand(DriverID, PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT);
v0::PS2WriteData(DriverID, PS2_MOUSE_CMD_DISABLE_DATA_REPORTING);
return 0;
}
int Probe()
{
if (!IsVMwareBackdoorAvailable())
return -ENODEV;
return 0;
}
REGISTER_BUILTIN_DRIVER(vmware,
"VMware Tools Driver",
"enderice2",
1, 0, 0,
Entry,
Final,
Panic,
Probe);
}