From bf1e3432d734a44150c608124099f56a0352eef2 Mon Sep 17 00:00:00 2001 From: EnderIce2 Date: Sun, 2 Mar 2025 21:37:01 +0000 Subject: [PATCH] feat(kernel/drivers): migrate drivers to the kernel make the drivers builtin Signed-off-by: EnderIce2 --- Fennix Kernel.code-workspace | 3 +- Kernel/drivers/audio/ac97/ac97.cpp | 820 +++++++++++++++++++ Kernel/drivers/audio/hda/hda.cpp | 210 +++++ Kernel/drivers/audio/hda/hda.hpp | 627 +++++++++++++++ Kernel/drivers/fs/fat/fat.cpp | 37 + Kernel/drivers/fs/fat/fat.hpp | 287 +++++++ Kernel/drivers/misc/aip/aip.hpp | 43 + Kernel/drivers/misc/aip/ata.cpp | 70 ++ Kernel/drivers/misc/aip/keyboard.cpp | 224 ++++++ Kernel/drivers/misc/aip/main.cpp | 260 ++++++ Kernel/drivers/misc/aip/mouse.cpp | 263 ++++++ Kernel/drivers/misc/aip/scancodes.c | 154 ++++ Kernel/drivers/misc/aip/uart.cpp | 647 +++++++++++++++ Kernel/drivers/misc/mem/mem.cpp | 164 ++++ Kernel/drivers/misc/pty/pty.cpp | 192 +++++ Kernel/drivers/misc/vmware/vmware.cpp | 917 +++++++++++++++++++++ Kernel/drivers/net/e1000/e1000.cpp | 485 ++++++++++++ Kernel/drivers/net/e1000/e1000.hpp | 156 ++++ Kernel/drivers/net/rtl8139/rtl8139.cpp | 294 +++++++ Kernel/drivers/net/rtl8139/rtl8139.hpp | 88 +++ Kernel/drivers/storage/ahci/ahci.cpp | 1010 ++++++++++++++++++++++++ Kernel/drivers/video/kdm/kdm.cpp | 128 +++ 22 files changed, 7078 insertions(+), 1 deletion(-) create mode 100644 Kernel/drivers/audio/ac97/ac97.cpp create mode 100644 Kernel/drivers/audio/hda/hda.cpp create mode 100644 Kernel/drivers/audio/hda/hda.hpp create mode 100644 Kernel/drivers/fs/fat/fat.cpp create mode 100644 Kernel/drivers/fs/fat/fat.hpp create mode 100644 Kernel/drivers/misc/aip/aip.hpp create mode 100644 Kernel/drivers/misc/aip/ata.cpp create mode 100644 Kernel/drivers/misc/aip/keyboard.cpp create mode 100644 Kernel/drivers/misc/aip/main.cpp create mode 100644 Kernel/drivers/misc/aip/mouse.cpp create mode 100644 Kernel/drivers/misc/aip/scancodes.c create mode 100644 Kernel/drivers/misc/aip/uart.cpp create mode 100644 Kernel/drivers/misc/mem/mem.cpp create mode 100644 Kernel/drivers/misc/pty/pty.cpp create mode 100644 Kernel/drivers/misc/vmware/vmware.cpp create mode 100644 Kernel/drivers/net/e1000/e1000.cpp create mode 100644 Kernel/drivers/net/e1000/e1000.hpp create mode 100644 Kernel/drivers/net/rtl8139/rtl8139.cpp create mode 100644 Kernel/drivers/net/rtl8139/rtl8139.hpp create mode 100644 Kernel/drivers/storage/ahci/ahci.cpp create mode 100644 Kernel/drivers/video/kdm/kdm.cpp diff --git a/Fennix Kernel.code-workspace b/Fennix Kernel.code-workspace index 3298aaaa..db2b6dd4 100644 --- a/Fennix Kernel.code-workspace +++ b/Fennix Kernel.code-workspace @@ -33,7 +33,8 @@ "vscode", "kernel", "kernel/pci", - "kernel/driver" + "kernel/driver", + "kernel/drivers" ] } } diff --git a/Kernel/drivers/audio/ac97/ac97.cpp b/Kernel/drivers/audio/ac97/ac97.cpp new file mode 100644 index 00000000..8ceec9cf --- /dev/null +++ b/Kernel/drivers/audio/ac97/ac97.cpp @@ -0,0 +1,820 @@ +/* + 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 . +*/ + +#include +#include +#include +#include + +extern Driver::Manager *DriverManager; +extern PCI::Manager *PCIManager; +namespace Driver::AC97 +{ + dev_t DriverID; + +#define DescriptorListLength 0x20 + + enum AudioVolumeValues + { + AV_Maximum = 0x0, + AV_Minimum = 0x3F, + }; + + enum AudioEncodingValues + { + AE_PCMs8, + AE_PCMu8, + + AE_PCMs16le, + AE_PCMs20le, + AE_PCMs24le, + AE_PCMs32le, + + AE_PCMu16le, + AE_PCMu20le, + AE_PCMu24le, + AE_PCMu32le, + + AE_PCMs16be, + AE_PCMs20be, + AE_PCMs24be, + AE_PCMs32be, + + AE_PCMu16be, + AE_PCMu20be, + AE_PCMu24be, + AE_PCMu32be, + }; + + enum NativeAudioMixerRegisters + { + /** + * @brief Reset Register + * @note Length: word + */ + NAM_Reset = 0x00, + + /** + * @brief Master Volume Register + * @note Length: word + */ + NAM_MasterVolume = 0x02, + + /** + * @brief Microphone Volume Register + * @note Length: word + */ + NAM_MicrophoneVolume = 0x0E, + + /** + * @brief PCM Out Volume Register + * @note Length: word + */ + NAM_PCMOutVolume = 0x18, + + /** + * @brief Select Record Input Register + * @note Length: word + */ + NAM_SelectRecordInput = 0x1A, + + /** + * @brief Record Gain Register + * @note Length: word + */ + NAM_RecordGain = 0x1C, + + /** + * @brief Record Gain Microphone Register + * @note Length: word + */ + NAM_RecordGainMicrophone = 0x1E, + }; + + enum NativeAudioBusMasterRegisters + { + /** + * @brief Register box for PCM IN + * @note Length: below + */ + NABM_PCMInBox = 0x00, + + /** + * @brief Register box for PCM OUT + * @note Length: below + */ + NABM_PCMOutBox = 0x10, + + /** + * @brief Register box for Microphone + * @note Length: below + */ + NABM_MicrophoneBox = 0x20, + + /** + * @brief Global Control Register + * @note Length: dword + */ + NABM_GlobalControl = 0x2C, /* 0x30 */ + + /** + * @brief Global Status Register + * @note Length: dword + */ + NABM_GlobalStatus = 0x30, /* 0x34 */ + }; + + enum NativeAudioBusMasterBoxOffsets + { + /** + * @brief Physical Address of Buffer Descriptor List + * @note Length: dword + */ + NABMBOFF_BufferDescriptorList = 0x00, + + /** + * @brief Number of Actual Processed Buffer Descriptor Entry + * @note Length: byte + */ + NABMBOFF_BufferDescriptorEntry = 0x04, + + /** + * @brief Number of all Descriptor Entries + * @note Length: byte + */ + NABMBOFF_DescriptorEntries = 0x05, + + /** + * @brief Status of transferring Data + * @note Length: word + */ + NABMBOFF_Status = 0x06, + + /** + * @brief Number of transferred Samples in Actual Processed Entry + * @note Length: word + */ + NABMBOFF_TransferredSamples = 0x08, + + /** + * @brief Number of next processed Buffer Entry + * @note Length: byte + */ + NABMBOFF_NextProcessedBufferEntry = 0x0A, + + /** + * @brief Transfer Control + * @note Length: byte + */ + NABMBOFF_TransferControl = 0x0B, + }; + + enum OutputPulseCodeModulationRegisters + { + /** + * @brief Physical Address of Buffer Descriptor List + * @note Length: dword + */ + PCMOUT_BufferDescriptorList = (int)NABM_PCMOutBox + (int)NABMBOFF_BufferDescriptorList, + + /** + * @brief Number of Actual Processed Buffer Descriptor Entry + * @note Length: byte + */ + PCMOUT_BufferDescriptorEntry = (int)NABM_PCMOutBox + (int)NABMBOFF_BufferDescriptorEntry, + + /** + * @brief Number of all Descriptor Entries + * @note Length: byte + */ + PCMOUT_DescriptorEntries = (int)NABM_PCMOutBox + (int)NABMBOFF_DescriptorEntries, + + /** + * @brief Status of transferring Data + * @note Length: word + */ + PCMOUT_Status = (int)NABM_PCMOutBox + (int)NABMBOFF_Status, + + /** + * @brief Number of transferred Samples in Actual Processed Entry + * @note Length: word + */ + PCMOUT_TransferredSamples = (int)NABM_PCMOutBox + (int)NABMBOFF_TransferredSamples, + + /** + * @brief Number of next processed Buffer Entry + * @note Length: byte + */ + PCMOUT_NextProcessedBufferEntry = (int)NABM_PCMOutBox + (int)NABMBOFF_NextProcessedBufferEntry, + + /** + * @brief Transfer Control + * @note Length: byte + */ + PCMOUT_TransferControl = (int)NABM_PCMOutBox + (int)NABMBOFF_TransferControl, + }; + + enum TransferControlRegisters + { + /** + * @brief DMA controller control + * + * 0 = Pause transfer + * 1 = Transfer sound data + */ + TC_DMAControllerControl = 0x01, + + /** + * @brief Reset + * + * 0 = Remove reset condition + * 1 = Reset this NABM register box, this bit is cleared by card when is reset complete + */ + TC_TransferReset = 0x02, + + /** + * @brief Last Buffer Entry Interrupt enable + * + * 0 = Disable interrupt + * 1 = Enable interrupt + */ + TC_LastBufferEntryInterruptEnable = 0x04, + + /** + * @brief IOC Interrupt enable + * + * 0 = Disable interrupt + * 1 = Enable interrupt + */ + TC_IOCInterruptEnable = 0x08, + + /** + * @brief Fifo ERROR Interrupt enable + * + * 0 = Disable interrupt + * 1 = Enable interrupt + */ + TC_FifoERRORInterruptEnable = 0x10, + }; + + enum GlobalControlRegisters + { + /** + * @brief Global Interrupt Enable + * + * 0 = Disable Interrupts + * 1 = Enable Interrupts + */ + GC_GlobalInterruptEnable = 0x01, + + /** + * @brief Cold reset + * + * 0 = Device is in reset and can not be used + * 1 = Resume to operational state + */ + GC_ColdReset = 0x02, + + /** + * @brief Warm reset + */ + GC_WarmReset = 0x04, + + /** + * @brief Shut down + * + * 0 = Device is powered + * 1 = Shut down + */ + GC_ShutDown = 0x08, + + /** + * @brief Channels for PCM Output + * + * 00 = 2 channels + * 01 = 4 channels + * 10 = 6 channels + * 11 = Reserved + */ + GC_ChannelsForPCMOutput = 0x30, + + /** + * @brief PCM Output mode + * + * 00 = 16 bit samples + * 01 = 20 bit samples + */ + GC_PCMOutputMode = 0xC0, + }; + + struct BufferDescriptorList + { + /** + * @brief Physical Address to sound data in memory + * @note Length: dword + */ + uint32_t Address; + + /** + * @brief Number of samples in this buffer + * @note Length: word + */ + uint16_t SampleCount; + + /** + * @brief Flags + * @note Length: word + * + * Bit 15 = Interrupt fired when data from this entry is transferred + * Bit 14 = Last entry of buffer, stop playing + * Other bits = Reserved + */ + uint16_t Flags; + } __attribute__((packed)); + + uint16_t MixerVolume(uint8_t Left, uint8_t Right, bool Mute) + { + return ((uint16_t)((Right & 0x3F) | + ((Left & 0x3F) << 0x8) | + (Mute & 1 << 0xF))); + } + + class AC97Device + { + private: + PCI::PCIHeader0 *Header; + BufferDescriptorList *DescriptorList = nullptr; + + uint16_t MixerAddress; + uint16_t BusMasterAddress; + + AudioEncodingValues Encoding = AE_PCMs16le; + char Channels = 2; + uint8_t Volume = AV_Maximum; + bool Mute = false; + int SampleRate = 48000; + char SampleSize = 2; + + public: + size_t write(uint8_t *Buffer, size_t Size) + { + if (Buffer == nullptr) + { + info("Invalid buffer."); + return -EINVAL; + } + + if ((Size == 0) || (Size % (SampleSize * Channels))) + { + info("Invalid buffer length."); + return -EINVAL; + } + + int TotalBDLToFill = (int)((Size + PAGE_SIZE - 1) >> 12); + + while (Size > 0) + { + bool ActiveDMA = !(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl); + + if (ActiveDMA) + { + int RemainingBDL = 0; + + do + { + int CurrentBDL = inb(BusMasterAddress + PCMOUT_BufferDescriptorEntry); + int LastBDL = inb(BusMasterAddress + PCMOUT_DescriptorEntries); + + RemainingBDL = LastBDL - CurrentBDL; + if (RemainingBDL < 0) + RemainingBDL += DescriptorListLength; + + RemainingBDL += 1; + + if (RemainingBDL >= DescriptorListLength - 1) + { + long SampleCount = DescriptorList[(CurrentBDL + 1) % DescriptorListLength].SampleCount / Channels; + if (SampleCount > 0) + v0::Sleep(DriverID, SampleCount * 1000 / SampleRate); + } + + } while (RemainingBDL >= DescriptorListLength - 1 && + !(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl)); + } + + uint8_t CurrentBDL = inb(BusMasterAddress + PCMOUT_BufferDescriptorEntry); + uint8_t LastBDL = inb(BusMasterAddress + PCMOUT_DescriptorEntries); + uint8_t NextBDL = LastBDL % DescriptorListLength; + + ActiveDMA = !(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl); + if (ActiveDMA) + { + NextBDL = (uint8_t)((LastBDL + 1) % DescriptorListLength); + if (NextBDL == CurrentBDL) + continue; + } + + do + { + size_t Wrote = (PAGE_SIZE > Size) ? size_t(Size) + : size_t(PAGE_SIZE); + + if (Wrote == 0) + { + info("Wrote 0 bytes."); + break; + } + + memcpy((void *)((uint64_t)DescriptorList[NextBDL].Address), Buffer, Wrote); + DescriptorList[NextBDL].Flags = 0; + + Buffer += Wrote; + Size -= (unsigned int)Wrote; + + DescriptorList[NextBDL].SampleCount = uint16_t(Wrote / SampleSize); + TotalBDLToFill--; + NextBDL = (uint8_t)((NextBDL + 1) % DescriptorListLength); + } while (TotalBDLToFill-- && NextBDL != CurrentBDL); + + outb(BusMasterAddress + PCMOUT_DescriptorEntries, NextBDL - 1); + + ActiveDMA = !(inw(BusMasterAddress + PCMOUT_Status) & TC_DMAControllerControl); + if (!ActiveDMA) + { + // Start DMA + outb(BusMasterAddress + PCMOUT_TransferControl, + inb(BusMasterAddress + PCMOUT_TransferControl) | TC_DMAControllerControl); + } + } + return Size; + } + + int ioctl(AudioIoctl, void *) + { + // if (Data->AudioCallback.Adjust._Volume) + // { + // Volume = (uint8_t)(0x3F - (0x3F * Data->AudioCallback.Adjust.Volume / 100)); + // outw(BAR.MixerAddress + NAM_MasterVolume, MixerVolume(Volume, Volume, Mute)); + // // outw(BAR.MixerAddress + NAM_PCMOutVolume, MixerVolume(Volume, Volume, Mute)); + // } + // else if (Data->AudioCallback.Adjust._Encoding) + // { + // fixme("Encoding changing not supported yet."); + // } + // else if (Data->AudioCallback.Adjust._SampleRate) + // { + // switch (Data->AudioCallback.Adjust.SampleRate) + // { + // case 0: + // { + // SampleRate = 8000; + // break; + // } + // case 1: + // { + // SampleRate = 11025; + // break; + // } + // case 2: + // { + // SampleRate = 16000; + // break; + // } + // case 3: + // { + // SampleRate = 22050; + // break; + // } + // case 4: + // { + // SampleRate = 32000; + // break; + // } + // case 5: + // { + // SampleRate = 44100; + // break; + // } + // case 6: + // { + // SampleRate = 48000; + // break; + // } + // case 7: + // { + // SampleRate = 88200; + // break; + // } + // case 8: + // { + // SampleRate = 96000; + // break; + // } + // default: + // { + // SampleRate = 16000; + // error("Invalid sample rate. Defaulting to 16000."); + // break; + // } + // } + // } + // else if (Data->AudioCallback.Adjust._Channels) + // { + // switch (Data->AudioCallback.Adjust.Channels) + // { + // case 0: + // { + // Channels = 1; // Mono + // break; + // } + // case 1: + // { + // Channels = 2; // Stereo + // break; + // } + // default: + // { + // Channels = 2; + // error("Invalid channel count. Defaulting to 2."); + // break; + // } + // } + // } + return 0; + } + + void OnInterruptReceived(CPU::TrapFrame *) + { + uint16_t Status = inw(MixerAddress + PCMOUT_Status); + if (Status & TC_IOCInterruptEnable) + { + debug("IOC"); + outw(MixerAddress + PCMOUT_Status, TC_IOCInterruptEnable); + uint16_t CurrentBDL = inb(BusMasterAddress + PCMOUT_BufferDescriptorEntry); + uint16_t LastBDL = (CurrentBDL + 2) & (DescriptorListLength - 1); + outb(BusMasterAddress + PCMOUT_DescriptorEntries, LastBDL); + info("FIXME: CurrentBDL: %d, LastBDL: %d", CurrentBDL, LastBDL); + } + else if (Status & TC_LastBufferEntryInterruptEnable) + { + debug("Last buffer entry"); + // Stop DMA + uint8_t TransferControl = inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl)); + TransferControl &= ~TC_DMAControllerControl; + outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl), TransferControl); + + outw(MixerAddress + PCMOUT_Status, TC_LastBufferEntryInterruptEnable); + } + else if (Status & TC_FifoERRORInterruptEnable) + { + info("FIFO error"); + outw(MixerAddress + PCMOUT_Status, TC_FifoERRORInterruptEnable); + } + else + { + debug("Unknown interrupt status %#x", Status); + outw(MixerAddress + PCMOUT_Status, 0xFFFF); + } + } + + void Panic() + { + uint8_t TransferControl = inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl)); + TransferControl &= ~(TC_LastBufferEntryInterruptEnable | + TC_IOCInterruptEnable | + TC_FifoERRORInterruptEnable); + outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl), TransferControl); + + uint32_t GlobalControl = inl((uint16_t)(BusMasterAddress + NABM_GlobalControl)); + GlobalControl &= ~GC_GlobalInterruptEnable; + GlobalControl |= GC_ShutDown; + outl((uint16_t)(BusMasterAddress + NABM_GlobalControl), GlobalControl); + } + + AC97Device(PCI::PCIHeader0 *_Header) + : Header(_Header) + { + /* Native Audio Mixer Base Address */ + uint32_t PCIBAR0 = Header->BAR0; + + /* Native Audio Bus Master Base Address */ + uint32_t PCIBAR1 = Header->BAR1; + + // uint8_t Type = PCIBAR0 & 1; + MixerAddress = (uint16_t)(PCIBAR0 & (~3)); + BusMasterAddress = PCIBAR1 & (~15); + + uint16_t OutputPCMTransferControl = BusMasterAddress + PCMOUT_TransferControl; + + /* DescriptorList address MUST be physical. */ + DescriptorList = (BufferDescriptorList *)v0::AllocateMemory(DriverID, TO_PAGES(sizeof(BufferDescriptorList) * DescriptorListLength)); + memset(DescriptorList, 0, sizeof(BufferDescriptorList) * DescriptorListLength); + + uint16_t DLSampleCount = (uint16_t)(PAGE_SIZE / SampleSize); + for (int i = 0; i < DescriptorListLength; i++) + { + DescriptorList[i].Address = (uint32_t)(uintptr_t)v0::AllocateMemory(DriverID, TO_PAGES(sizeof(uint16_t *))); + DescriptorList[i].SampleCount = DLSampleCount; + DescriptorList[i].Flags = 0; + debug("DescriptorList[%d] = { Address: %#lx, SampleCount: %d, Flags: %#lx }", + i, + DescriptorList[i].Address, + DescriptorList[i].SampleCount, + DescriptorList[i].Flags); + } + + outw(MixerAddress + NAM_MasterVolume, MixerVolume(Volume, Volume, Mute)); + outw(MixerAddress + NAM_PCMOutVolume, MixerVolume(Volume, Volume, Mute)); + + Volume = 0x3F - (0x3F * /* VOL 50% */ 50 / 100); + outw(MixerAddress + NAM_MasterVolume, MixerVolume(Volume, Volume, Mute)); + + outb(OutputPCMTransferControl, inb(OutputPCMTransferControl) | TC_TransferReset); + while (inb(OutputPCMTransferControl) & TC_TransferReset) + ; + + uint32_t GlobalControl = inl(BusMasterAddress + NABM_GlobalControl); + GlobalControl = (GlobalControl & ~((0x3U) << 0x16)); /* PCM 16-bit mode */ + GlobalControl = (GlobalControl & ~((0x3U) << 20)); /* 2 channels */ + GlobalControl |= GC_GlobalInterruptEnable; + GlobalControl &= ~GC_ShutDown; + + outl(BusMasterAddress + PCMOUT_BufferDescriptorList, + (uint32_t)(uint64_t)DescriptorList); + + outl(BusMasterAddress + NABM_GlobalControl, GlobalControl); + + uint8_t TransferControl = inb(OutputPCMTransferControl); + TransferControl |= TC_IOCInterruptEnable | + TC_FifoERRORInterruptEnable; + outb(OutputPCMTransferControl, TransferControl); + + // Stop DMA + outb(OutputPCMTransferControl, inb(OutputPCMTransferControl) & ~TC_DMAControllerControl); + } + + ~AC97Device() + { + outw(MixerAddress + NAM_MasterVolume, MixerVolume(AV_Maximum, AV_Maximum, true)); + outw(MixerAddress + NAM_PCMOutVolume, MixerVolume(AV_Maximum, AV_Maximum, true)); + + // Stop DMA + outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl), + inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl)) & ~TC_DMAControllerControl); + + // Disable interrupts + uint8_t TransferControl = inb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl)); + + TransferControl &= ~(TC_LastBufferEntryInterruptEnable | + TC_IOCInterruptEnable | + TC_FifoERRORInterruptEnable); + outb((uint16_t)(BusMasterAddress + PCMOUT_TransferControl), TransferControl); + + // Disable global control + uint32_t GlobalControl = inl((uint16_t)(BusMasterAddress + NABM_GlobalControl)); + GlobalControl &= ~GC_GlobalInterruptEnable; + GlobalControl |= GC_ShutDown; + outl((uint16_t)(BusMasterAddress + NABM_GlobalControl), GlobalControl); + } + }; + + std::unordered_map Drivers; + + int Open(struct Inode *, int, mode_t) + { + return 0; + } + + int Close(struct Inode *) + { + return 0; + } + + ssize_t Read(struct Inode *, void *, size_t, off_t) + { + return 0; + } + + ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t) + { + return Drivers[Node->GetMinor()]->write((uint8_t *)Buffer, Size); + } + + int Ioctl(struct Inode *Node, unsigned long Request, void *Argp) + { + return Drivers[Node->GetMinor()]->ioctl((AudioIoctl)Request, Argp); + } + + 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 = nullptr, + .Stat = nullptr, + }; + + std::list Devices; + int Entry() + { + for (auto &&dev : Devices) + { + PCIManager->InitializeDevice(dev, KernelPageTable); + AC97Device *ac97 = new AC97Device((PCI::PCIHeader0 *)dev.Header); + dev_t ret = v0::RegisterDevice(DriverID, NETWORK_TYPE_ETHERNET, &ops); + Drivers[ret] = ac97; + } + + if (Drivers.empty()) + { + info("No valid AC'97 device found."); + return -EINVAL; + } + + return 0; + } + + int Final() + { + for (auto &&dev : Drivers) + { + dev_t ret = dev.first; + v0::UnregisterDevice(DriverID, ret); + delete dev.second; + } + return 0; + } + + int Panic() + { + for (auto &&i : Drivers) + i.second->Panic(); + return 0; + } + + int Probe() + { + Devices = PCIManager->FindPCIDevice( + { + 0x8086, /* Intel */ + }, + { + 0x2415, /* AC'97 */ + }); + + for (auto &&i : Devices) + { + PCI::PCIHeader0 *hdr0 = (PCI::PCIHeader0 *)i.Header; + uint8_t type = hdr0->BAR0 & 1; + if (type != 1) + { + warn("Device %x:%x.%d BAR0 is not I/O.", + hdr0->Header.VendorID, + hdr0->Header.DeviceID, + hdr0->Header.ProgIF); + continue; + } + } + + if (Devices.empty()) + { + info("No AC'97 device found."); + return -ENODEV; + } + return 0; + } + + REGISTER_BUILTIN_DRIVER(ac97, + "Audio Codec '97 Driver", + "enderice2", + 1, 0, 0, + Entry, + Final, + Panic, + Probe); +} diff --git a/Kernel/drivers/audio/hda/hda.cpp b/Kernel/drivers/audio/hda/hda.cpp new file mode 100644 index 00000000..8756072f --- /dev/null +++ b/Kernel/drivers/audio/hda/hda.cpp @@ -0,0 +1,210 @@ +/* + 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 . +*/ + +#include +#include +#include +#include "hda.hpp" + +extern Driver::Manager *DriverManager; +extern PCI::Manager *PCIManager; +namespace Driver::HighDefinitionAudio +{ + dev_t DriverID; + + class HDADevice + { + private: + PCI::PCIHeader0 *Header; + bool Initialized = false; + + ControllerRegisters *CTL; + + uint32_t *CORB; + uint64_t *RIRB; + + public: + bool IsInitialized() { return Initialized; } + + size_t write(uint8_t *, size_t Size) + { + return Size; + } + + int ioctl(AudioIoctl, void *) + { + return 0; + } + + void OnInterruptReceived(CPU::TrapFrame *) + { + } + + void Panic() + { + } + + HDADevice(PCI::PCIHeader0 *_Header) + : Header(_Header), + CORB((uint32_t *)(uintptr_t)DriverManager->AllocateMemory(DriverID, 1)), + RIRB((uint64_t *)DriverManager->AllocateMemory(DriverID, 1)) + { + CTL = (ControllerRegisters *)(uintptr_t)Header->BAR0; + fixme("Unimplemented HDA driver"); + return; + Initialized = true; + } + + ~HDADevice() + { + if (!Initialized) + return; + } + }; + + std::unordered_map Drivers; + + int Ioctl(struct Inode *Node, unsigned long Request, void *Argp) + { + return Drivers[Node->GetMinor()]->ioctl((AudioIoctl)Request, Argp); + } + + ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset) + { + return Drivers[Node->GetMinor()]->write((uint8_t *)Buffer, Size); + } + + const struct InodeOperations ops = { + .Lookup = nullptr, + .Create = nullptr, + .Remove = nullptr, + .Rename = nullptr, + .Read = nullptr, + .Write = Write, + .Truncate = nullptr, + .Open = nullptr, + .Close = nullptr, + .Ioctl = Ioctl, + .ReadDir = nullptr, + .MkDir = nullptr, + .RmDir = nullptr, + .SymLink = nullptr, + .ReadLink = nullptr, + .Seek = nullptr, + .Stat = nullptr, + }; + + std::list Devices; + int Entry() + { + for (auto &&dev : Devices) + { + PCI::PCIHeader0 *hdr0 = (PCI::PCIHeader0 *)dev.Header; + uint8_t type = hdr0->BAR0 & 1; + if (type == 1) + { + debug("Device %x:%x.%d BAR0 is I/O.", + hdr0->Header.VendorID, + hdr0->Header.DeviceID, + hdr0->Header.ProgIF); + continue; + } + + PCIManager->InitializeDevice(dev, KernelPageTable); + + HDADevice *driver = new HDADevice((PCI::PCIHeader0 *)dev.Header); + + if (driver->IsInitialized()) + { + dev_t ret = v0::RegisterDevice(DriverID, AUDIO_TYPE_PCM, &ops); + Drivers[ret] = driver; + } + } + + if (Drivers.empty()) + { + debug("No valid HDA device found."); + return -EINVAL; + } + + return 0; + } + + int Final() + { + for (auto &&dev : Drivers) + { + dev_t ret = dev.first; + v0::UnregisterDevice(DriverID, ret); + delete dev.second; + } + return 0; + } + + int Panic() + { + for (auto &&i : Drivers) + i.second->Panic(); + return 0; + } + + int Probe() + { + Devices = PCIManager->FindPCIDevice( + { + 0x8086, /* Intel */ + 0x15AD, /* VMware */ + }, + { + 0x9D71, /* Sunrise Point-LP HD Audio */ + 0x2668, /* ICH6 */ + 0x293E, /* ICH9 */ + }); + + if (Devices.empty()) + { + trace("No HDA device found."); + return -ENODEV; + } + + for (auto &&dev : Devices) + { + PCI::PCIHeader0 *PCIBaseAddress = (PCI::PCIHeader0 *)dev.Header; + uint32_t PCIBAR0 = PCIBaseAddress->BAR0; + uint8_t Type = PCIBAR0 & 1; + if (Type == 1) + { + debug("Device %x:%x.%d BAR0 is I/O.", + PCIBaseAddress->Header.VendorID, + PCIBaseAddress->Header.DeviceID, + PCIBaseAddress->Header.ProgIF); + continue; + } + } + + return 0; + } + + REGISTER_BUILTIN_DRIVER(hda, + "Intel High Definition Audio Driver", + "enderice2", + 1, 0, 0, + Entry, + Final, + Panic, + Probe); +} diff --git a/Kernel/drivers/audio/hda/hda.hpp b/Kernel/drivers/audio/hda/hda.hpp new file mode 100644 index 00000000..1d653d0c --- /dev/null +++ b/Kernel/drivers/audio/hda/hda.hpp @@ -0,0 +1,627 @@ +/* + 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 . +*/ + +#pragma once + +#include + +struct StreamDescriptor +{ + /** Control */ + uint32_t CTL : 24; + + /** Status */ + uint8_t STS; + + /** Link Position in Current Buffer */ + uint32_t LPIB; + + /** Cyclic Buffer Length */ + uint32_t CBL; + + /** Last Valid Index */ + uint16_t LVI; + + /** Reserved */ + uint8_t Rsvd0[2]; + + /** FIFO Size */ + uint16_t FIFOD; + + /** Format */ + uint16_t FMT; + + /** Reserved */ + uint8_t Rsvd1[4]; + + /** Buffer Descriptor List Pointer - Lower */ + uint32_t BDPL; + + /** Buffer Descriptor List Pointer - Upper */ + uint32_t BDPU; +} __attribute__((packed)); + +struct ControllerRegisters +{ + uint16_t GCAP; + uint8_t VMIN; + uint8_t VMJ; + uint16_t OUTPAY; + uint16_t INPAY; + uint32_t GCTL; + uint16_t WAKEEN; + uint16_t WAKESTS; + uint16_t GSTS; + uint8_t Rsvd0[6]; + uint16_t OUTSTRMPAY; + uint16_t INSTRMPAY; + uint8_t Rsvd1[4]; + uint32_t INTCTL; + uint32_t INTSTS; + uint8_t Rsvd2[8]; + uint32_t WALCLK; + uint8_t Rsvd3[4]; + uint32_t SSYNC; + uint8_t Rsvd4[4]; + uint32_t CORBLBASE; + uint32_t CORBUBASE; + uint16_t CORBWP; + uint16_t CORBRP; + uint8_t CORBCTL; + uint8_t CORBSTS; + uint8_t CORBSIZE; + uint8_t Rsvd5; + uint32_t RIRBLBASE; + uint32_t RIRBUBASE; + uint16_t RIRBWP; + uint16_t RINTCNT; + uint8_t RIRBCTL; + uint8_t RIRBSTS; + uint8_t RIRBSIZE; + uint8_t Rsvd6; + uint32_t ICOI; + uint32_t ICII; + uint16_t ICIS; + uint8_t Rsvd7[6]; + uint32_t DPIBLBASE; + uint32_t DPIBUBASE; + uint8_t Rsvd8[8]; + StreamDescriptor SD[]; +} __attribute__((packed)); + +/* Not working as expected */ +struct __ControllerRegisters +{ + /** Global Capabilities */ + union + { + struct + { + /** 64 Bit Address Supported + * + * 0 = 32-bit addressing + * 1 = 64-bit addressing + */ + uint16_t _64OK : 1; + + /** Number of Serial Data Out Signals + * + * 00 = 1 SDO + * 01 = 2 SDOs + * 10 = 4 SDOs + * 11 = Reserved + */ + uint16_t NSDO : 2; + + /** Number of Bidirectional Streams Supported + * + * 00000b = No bidirectional streams supported + * 00001b = 1 bidirectional stream supported + * ... + * 11110b = 30 bidirectional streams supported + */ + uint16_t BSS : 5; + + /** Number of Input Streams Supported + * + * 0000b = No input streams supported + * 0001b = 1 input stream supported + * ... + * 1111b = 15 input streams supported + */ + uint16_t ISS : 4; + + /** Number of Output Streams Supported + * + * 0000b = No output streams supported + * 0001b = 1 output stream supported + * ... + * 1111b = 15 output streams supported + */ + uint16_t OSS : 4; + } __attribute__((packed)); + uint16_t Raw; + } GCAP; + + /** Minor Version */ + uint8_t VMIN; + + /** Major Version */ + uint8_t VMJ; + + /** Output Payload Capability + * + * 00h = 0 Words + * 01h = 1 Word payload + * ... + * FFh = 255h Word payload + */ + uint16_t OUTPAY; + + /** Input Payload Capability + * + * 00h = 0 Words + * 01h = 1 Word payload + * ... + * FFh = 255h Word payload + */ + uint16_t INPAY; + + /** Global Control */ + union + { + struct + { + /** Controller Reset + * + * 0 = Reset + * 1 = Normal Operation + */ + uint32_t CRST : 1; + + /** Flush Control + * + * 0 = Idle + * 1 = Flush + */ + uint32_t FCNTRL : 1; + + /** Reserved */ + uint32_t RsvdP0 : 6; + + /** Accept Unsolicited Response Enable + * + * 0 = Disabled + * 1 = Enabled + */ + uint32_t UNSOL : 1; + + /** Reserved */ + uint32_t RsvdP1 : 23; + } __attribute__((packed)); + uint32_t Raw; + } GCTL; + + /** Wake Enable */ + union + { + struct + { + /** SDIN Wake Enable Flags */ + uint16_t SDIWEN : 15; + + /** Reserved */ + uint16_t RsvdP0 : 1; + } __attribute__((packed)); + uint16_t Raw; + } WAKEEN; + + /** Wake Status */ + union + { + struct + { + /** SDIN State Change Status Flags */ + uint16_t SDIWAKE : 15; + + /** Reserved */ + uint16_t RsvdZ0 : 1; + } __attribute__((packed)); + uint16_t Raw; + } WAKESTS; + + /** Global Status */ + union + { + struct + { + uint16_t RsvdZ0 : 1; + uint16_t FSTS : 1; + uint16_t RsvdZ1 : 14; + } __attribute__((packed)); + uint16_t Raw; + } GSTS; + + /** Reserved */ + uint8_t Rsvd0[6]; + + /** Output Stream Payload Capability */ + uint16_t OUTSTRMPAY; + + /** Input Stream Payload Capability */ + uint16_t INSTRMPAY; + + /** Reserved */ + uint8_t Rsvd1[4]; + + /** Interrupt Control */ + union + { + struct + { + /** Stream Interrupt Enable + * + * Bit 0 = Input Stream 0 + * Bit 1 = Input Stream 1 + * Bit 2 = Output Stream 0 + * Bit 3 = Output Stream 1 + * Bit 4 = Output Stream 2 + * Bit 5 = Bidirectional Stream 0 + * Bits 6-28 = Reserved + */ + uint32_t SIE : 30; + + /** Controller Interrupt Enable */ + uint32_t CIE : 1; + + /** Global Interrupt Enable */ + uint32_t GIE : 1; + } __attribute__((packed)); + uint32_t Raw; + } INTCTL; + + /** Interrupt Status */ + union + { + struct + { + /** Stream Interrupt Status */ + uint32_t SIS : 30; + + /** Controller Interrupt Status */ + uint32_t CIS : 1; + + /** Global Interrupt Status */ + uint32_t GIS : 1; + } __attribute__((packed)); + uint32_t Raw; + } INTSTS; + + /** Reserved */ + uint8_t Rsvd2[8]; + + /** Wall Clock Counter */ + uint32_t WALCLK; + + /** Reserved */ + uint8_t Rsvd3[4]; + + /** Stream Synchronization */ + union + { + struct + { + /** Stream Synchronization Bits */ + uint32_t SSYNC : 30; + + /** Reserved */ + uint32_t RsvdP0 : 2; + } __attribute__((packed)); + uint32_t Raw; + } SSYNC; + + /** Reserved */ + uint8_t Rsvd4[4]; + + /** CORB Lower Base Address */ + union + { + struct + { + /** CORB Lower Base Unimplemented Bits */ + uint32_t Unimplemented : 7; + + /** CORB Lower Base Address */ + uint32_t CORBLBASE : 25; + } __attribute__((packed)); + uint32_t Raw; + } CORBLBASE; + + /** CORB Upper Base Address */ + uint32_t CORBUBASE; + + /** CORB Write Pointer */ + union + { + struct + { + /** CORB Write Pointer */ + uint16_t CORBWP : 8; + + /** Reserved */ + uint16_t RsvdP0 : 8; + } __attribute__((packed)); + uint16_t Raw; + } CORBWP; + + /** CORB Read Pointer */ + union + { + struct + { + /** CORB Read Pointer */ + uint16_t CORBRP : 8; + + /** Reserved */ + uint16_t RsvdP0 : 7; + + /** CORB Read Pointer Reset */ + uint16_t CORBRPRST : 1; + } __attribute__((packed)); + uint16_t Raw; + } CORBRP; + + /** CORB Control */ + union + { + struct + { + /** CORB Memory Error Interrupt Enable */ + uint8_t CMEIE : 1; + + /** Enable CORB DMA Engine + * + * 0 = DMA Stop + * 1 = DMA Run + * + * @note Must read the value back. + */ + uint8_t CORBRUN : 1; + + /** Reserved */ + uint8_t RsvdP0 : 6; + } __attribute__((packed)); + uint8_t Raw; + } CORBCTL; + + /** CORB Status */ + union + { + struct + { + /** CORB Memory Error Indication */ + uint8_t CMEI : 1; + + /** Reserved */ + uint8_t RsvdZ0 : 7; + } __attribute__((packed)); + uint8_t Raw; + } CORBSTS; + + /** CORB Size */ + union + { + struct + { + /** CORB Size + * + * 00b = 2 entries + * 01b = 16 entries + * 10b = 256 entries + * 11b = Reserved + */ + uint8_t CORBSIZE : 2; + + /** Reserved */ + uint8_t RsvdP0 : 2; + + /** CORB Size Capability + * + * 0001b = 2 entries + * 0010b = 16 entries + * 0100b = 256 entries + * 1000b = Reserved + */ + uint8_t CORBSZCAP : 4; + } __attribute__((packed)); + uint8_t Raw; + } CORBSIZE; + + /** Reserved */ + uint8_t Rsvd5; + + /** RIRB Lower Base Address */ + union + { + struct + { + /** RIRB Lower Base Unimplemented Bits */ + uint32_t Unimplemented : 7; + + /** RIRB Lower Base Address */ + uint32_t RIRBLBASE : 25; + } __attribute__((packed)); + uint32_t Raw; + } RIRBLBASE; + + /** RIRB Upper Base Address */ + uint32_t RIRBUBASE; + + /** RIRB Write Pointer */ + union + { + struct + { + /** RIRB Write Pointer */ + uint16_t RIRBWP : 8; + + /** Reserved */ + uint16_t RsvdP0 : 7; + + /** RIRB Write Pointer Reset */ + uint16_t RIRBWPRST : 1; + } __attribute__((packed)); + uint16_t Raw; + } RIRBWP; + + /** Response Interrupt Count */ + union + { + struct + { + /** N Response Interrupt Count + * + * 00000001b = 1 Response sent to RIRB + * ... + * 11111111b = 255 Responses sent to RIRB + * 00000000b = 256 Response sent to RIRB + */ + uint16_t RINTCNT : 8; + + /** Reserved */ + uint16_t RsvdP0 : 8; + } __attribute__((packed)); + uint16_t Raw; + } RINTCNT; + + /** RIRB Control */ + union + { + struct + { + /** Response Interrupt Control + * + * 0 = Disable Interrupt + * 1 = Generate an interrupt after N responses are sent to the RIRB + */ + uint8_t RINTCTL : 1; + + /** RIRB DMA Enable + * + * 0 = DMA Stop + * 1 = DMA Run + */ + uint8_t RIRBDMAEN : 1; + + /** Response Overrun Interrupt Control */ + uint8_t RIRBOIC : 1; + + /** Reserved */ + uint8_t RsvdP0 : 5; + } __attribute__((packed)); + uint8_t Raw; + } RIRBCTL; + + /** RIRB Status */ + union + { + struct + { + /** Response Interrupt */ + uint8_t RINTFL : 1; + + /** Reserved */ + uint8_t RsvdZ0 : 1; + + /** Response Overrun Interrupt Status */ + uint8_t RIRBOIS : 1; + + /** Reserved */ + uint8_t RsvdZ1 : 5; + } __attribute__((packed)); + uint8_t Raw; + } RIRBSTS; + + /** RIRB Size */ + union + { + struct + { + /** RIRB Size + * + * 00b = 2 entries + * 01b = 16 entries + * 10b = 256 entries + * 11b = Reserved + */ + uint8_t RIRBSIZE : 2; + + /** Reserved */ + uint8_t RsvdP0 : 2; + + /** RIRB Size Capability + * + * 0001b = 2 entries + * 0010b = 16 entries + * 0100b = 256 entries + * 1000b = Reserved + */ + uint8_t RIRBSZCAP : 4; + } __attribute__((packed)); + uint8_t Raw; + } RIRBSIZE; + + /** Reserved */ + uint8_t Rsvd6; + + /** Immediate Command Output Interface */ + uint32_t ICOI; + + /** Immediate Command Input Interface */ + uint32_t ICII; + + /** Immediate Command Status */ + uint16_t ICIS; + + /** Reserved */ + uint8_t Rsvd7[6]; + + /** DMA Position Buffer Lower Base */ + union + { + struct + { + /** DMA Position Buffer Enable */ + uint32_t DPBEN : 1; + + /** Reserved */ + uint32_t RsvdZ0 : 6; + + /** DMA Position Lower Base Address */ + uint32_t DPLBASE : 25; + } __attribute__((packed)); + uint32_t Raw; + } DPIBLBASE; + + /** DMA Position Buffer Upper Base */ + uint32_t DPIBUBASE; + + /** Reserved */ + uint8_t Rsvd8[8]; + + StreamDescriptor SD[]; +} __attribute__((packed)); diff --git a/Kernel/drivers/fs/fat/fat.cpp b/Kernel/drivers/fs/fat/fat.cpp new file mode 100644 index 00000000..3feece7b --- /dev/null +++ b/Kernel/drivers/fs/fat/fat.cpp @@ -0,0 +1,37 @@ +/* + 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 . +*/ + +#include + +namespace Driver::FileAllocationTable +{ + dev_t DriverID; + + int Entry() { return 0; } + int Final() { return 0; } + int Panic() { return 0; } + int Probe() { return 0; } + + REGISTER_BUILTIN_DRIVER(fat, + "File Allocation Table Driver", + "enderice2", + 1, 0, 0, + Entry, + Final, + Panic, + Probe); +} diff --git a/Kernel/drivers/fs/fat/fat.hpp b/Kernel/drivers/fs/fat/fat.hpp new file mode 100644 index 00000000..3c416341 --- /dev/null +++ b/Kernel/drivers/fs/fat/fat.hpp @@ -0,0 +1,287 @@ +/* + 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 . +*/ + +#pragma once + +#include + +/* Source: https://wiki.osdev.org/FAT */ + +struct BIOSParameterBlock +{ + /** The first three bytes EB 3C 90 disassemble to JMP SHORT 3C NOP. + * (The 3C value may be different.) The reason for this is to jump + * over the disk format information (the BPB and EBPB). Since the + * first sector of the disk is loaded into ram at location + * 0x0000:0x7c00 and executed, without this jump, the processor + * would attempt to execute data that isn't code. Even for + * non-bootable volumes, code matching this pattern (or using the + * E9 jump opcode) is required to be present by both Windows and + * OS X. To fulfil this requirement, an infinite loop can be placed + * here with the bytes EB FE 90. */ + uint8_t JumpBoot[3]; + + /** OEM identifier. The first 8 Bytes (3 - 10) is the version of DOS + * being used. The next eight Bytes 29 3A 63 7E 2D 49 48 and 43 read + * out the name of the version. The official FAT Specification from + * Microsoft says that this field is really meaningless and is ignored + * by MS FAT Modules, however it does recommend the value "MSWIN4.1" + * as some 3rd party drivers supposedly check it and expect it to + * have that value. Older versions of dos also report MSDOS5.1, + * linux-formatted floppy will likely to carry "mkdosfs" here, and + * FreeDOS formatted disks have been observed to have "FRDOS5.1" here. + * If the string is less than 8 bytes, it is padded with spaces. */ + uint8_t OEM[8]; + + /** The number of Bytes per sector (remember, all numbers are in the + * little-endian format). */ + uint16_t BytesPerSector; + + /** Number of sectors per cluster. */ + uint8_t SectorsPerCluster; + + /** Number of reserved sectors. The boot record sectors are included + * in this value. */ + uint16_t ReservedSectors; + + /** Number of File Allocation Tables (FAT's) on the storage media. + * Often this value is 2. */ + uint8_t NumberOfFATs; + + /** Number of root directory entries (must be set so that the root + * directory occupies entire sectors). */ + uint16_t RootDirectoryEntries; + + /** The total sectors in the logical volume. If this value is 0, it + * means there are more than 65535 sectors in the volume, and the + * actual count is stored in the Large Sector Count entry at 0x20. */ + uint16_t Sectors16; + + /** This Byte indicates the media descriptor type. */ + uint8_t Media; + + /** Number of sectors per FAT. FAT12/FAT16 only. */ + uint16_t SectorsPerFAT; + + /** Number of sectors per track. */ + uint16_t SectorsPerTrack; + + /** Number of heads or sides on the storage media. */ + uint16_t NumberOfHeads; + + /** Number of hidden sectors. (i.e. the LBA of the beginning of + * the partition). */ + uint32_t HiddenSectors; + + /** Large sector count. This field is set if there are more than + * 65535 sectors in the volume, resulting in a value which does not + * fit in the Number of Sectors entry at 0x13. */ + uint32_t Sectors32; +} __packed; + +struct ExtendedBootRecord_FAT12_16 +{ + /** Drive number. The value here should be identical to the value + * returned by BIOS interrupt 0x13, or passed in the DL register; + * i.e. 0x00 for a floppy disk and 0x80 for hard disks. This number + * is useless because the media is likely to be moved to another + * machine and inserted in a drive with a different drive number. */ + uint8_t DriveNumber; + + /** Flags in Windows NT. Reserved otherwise. */ + uint8_t Flags; + + /** Signature (must be 0x28 or 0x29). */ + uint8_t Signature; + + /** VolumeID 'Serial' number. Used for tracking volumes between + * computers. You can ignore this if you want. */ + uint32_t VolumeID; + + /** Volume label string. This field is padded with spaces. */ + uint8_t VolumeLabel[11]; + + /** System identifier string. This field is a string representation + * of the FAT file system type. It is padded with spaces. The spec + * says never to trust the contents of this string for any use. */ + uint8_t SystemIdentifier[8]; + + /** Boot code. */ + uint8_t BootCode[448]; + + /** Bootable partition signature 0xAA55. */ + uint16_t BootSignature; +} __packed; + +struct ExtendedBootRecord_FAT32 +{ + /** Sectors per FAT. The size of the FAT in sectors. */ + uint32_t SectorsPerFAT; + + /** Flags. */ + uint16_t Flags; + + /** FAT version number. The high byte is the major version and the + * low byte is the minor version. FAT drivers should respect this + * field. */ + uint16_t FATVersion; + + /** The cluster number of the root directory. Often this field is + * set to 2. */ + uint32_t RootDirectoryCluster; + + /** The sector number of the FSInfo structure. */ + uint16_t FSInfoSector; + + /** The sector number of the backup boot sector. */ + uint16_t BackupBootSector; + + /** Reserved. When the volume is formated these bytes should be zero. */ + uint8_t Reserved[12]; + + /** Drive number. The values here are identical to the values returned + * by the BIOS interrupt 0x13. 0x00 for a floppy disk and 0x80 for + * hard disks. */ + uint8_t DriveNumber; + + /** Flags in Windows NT. Reserved otherwise. */ + uint8_t Flags2; + + /** Signature (must be 0x28 or 0x29). */ + uint8_t Signature; + + /** Volume ID 'Serial' number. Used for tracking volumes between + * computers. You can ignore this if you want. */ + uint32_t VolumeID; + + /** Volume label string. This field is padded with spaces. */ + uint8_t VolumeLabel[11]; + + /** System identifier string. Always "FAT32 ". The spec says never + * to trust the contents of this string for any use. */ + uint8_t SystemIdentifier[8]; + + /** Boot code. */ + uint8_t BootCode[420]; + + /** Bootable partition signature 0xAA55. */ + uint16_t BootSignature; +} __packed; + +struct FSInfo +{ + /** Lead signature (must be 0x41615252 to indicate a valid FSInfo + * structure). */ + uint32_t LeadSignature; + + /** Reserved, these bytes should never be used. */ + uint8_t Reserved1[480]; + + /** Another signature (must be 0x61417272). */ + uint32_t AnotherSignature; + + /** Contains the last known free cluster count on the volume. If the + * value is 0xFFFFFFFF, then the free count is unknown and must be + * computed. However, this value might be incorrect and should at + * least be range checked (<= volume cluster count). */ + uint32_t FreeClusterCount; + + /** Indicates the cluster number at which the filesystem driver should + * start looking for available clusters. If the value is 0xFFFFFFFF, + * then there is no hint and the driver should start searching at 2. + * Typically this value is set to the last allocated cluster number. + * As the previous field, this value should be range checked. */ + uint32_t NextFreeCluster; + + /** Reserved. */ + uint8_t Reserved2[12]; + + /** Trail signature (0xAA550000). */ + uint32_t TrailSignature; +} __packed; + +struct exFATBootRecord +{ + /** The first three bytes EB 3C 90 disassemble to JMP SHORT 3C NOP. + * (The 3C value may be different.) The reason for this is to jump + * over the disk format information (the BPB and EBPB). Since the + * first sector of the disk is loaded into ram at location + * 0x0000:0x7c00 and executed, without this jump, the processor + * would attempt to execute data that isn't code. Even for + * non-bootable volumes, code matching this pattern (or using the + * E9 jump opcode) is required to be present by both Windows and + * OS X. To fulfil this requirement, an infinite loop can be placed + * here with the bytes EB FE 90. */ + uint8_t JumpBoot[3]; + + /** OEM identifier. This contains the string "EXFAT ". Not to be + * used for filesystem determination, but it's a nice hint. */ + uint8_t OEM[8]; + + /** Set to zero. This makes sure any FAT driver will not be able to + * load it. */ + uint8_t Reserved1[53]; + + /** Partition offset. No idea why the partition itself would have + * this, but it's here. Might be wrong. Probably best to just ignore. */ + uint64_t PartitionOffset; + + /** Volume length. */ + uint64_t VolumeLength; + + /** FAT offset (in sectors) from start of partition. */ + uint32_t FATOffset; + + /** FAT length (in sectors). */ + uint32_t FATLength; + + /** Cluster heap offset (in sectors). */ + uint32_t ClusterHeapOffset; + + /** Cluster count. */ + uint32_t ClusterCount; + + /** Root directory cluster. Typically 4 (but just read this value). */ + uint32_t RootDirectoryCluster; + + /** Serial number of partition. */ + uint32_t SerialNumber; + + /** Filesystem revision. */ + uint16_t FilesystemRevision; + + /** Flags. */ + uint16_t Flags; + + /** Sector shift. */ + uint8_t SectorShift; + + /** Cluster shift. */ + uint8_t ClusterShift; + + /** Number of FATs. */ + uint8_t NumberOfFATs; + + /** Drive select. */ + uint8_t DriveSelect; + + /** Percentage in use. */ + uint8_t PercentageInUse; + + /** Reserved (set to 0). */ + uint8_t Reserved2[7]; +} __packed; diff --git a/Kernel/drivers/misc/aip/aip.hpp b/Kernel/drivers/misc/aip/aip.hpp new file mode 100644 index 00000000..6d611167 --- /dev/null +++ b/Kernel/drivers/misc/aip/aip.hpp @@ -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 . +*/ + +#pragma once + +#include +#include + +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(); +} diff --git a/Kernel/drivers/misc/aip/ata.cpp b/Kernel/drivers/misc/aip/ata.cpp new file mode 100644 index 00000000..adcdfbed --- /dev/null +++ b/Kernel/drivers/misc/aip/ata.cpp @@ -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 . +*/ + +#include +#include +#include +#include + +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; + } +} diff --git a/Kernel/drivers/misc/aip/keyboard.cpp b/Kernel/drivers/misc/aip/keyboard.cpp new file mode 100644 index 00000000..6d7898c1 --- /dev/null +++ b/Kernel/drivers/misc/aip/keyboard.cpp @@ -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 . +*/ + +#include "aip.hpp" + +#include +#include +#include +#include +#include + +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; + } +} diff --git a/Kernel/drivers/misc/aip/main.cpp b/Kernel/drivers/misc/aip/main.cpp new file mode 100644 index 00000000..ee73c6cf --- /dev/null +++ b/Kernel/drivers/misc/aip/main.cpp @@ -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 . +*/ + +#include +#include +#include +#include + +#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); +} diff --git a/Kernel/drivers/misc/aip/mouse.cpp b/Kernel/drivers/misc/aip/mouse.cpp new file mode 100644 index 00000000..65849621 --- /dev/null +++ b/Kernel/drivers/misc/aip/mouse.cpp @@ -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 . +*/ + +#include "aip.hpp" + +#include +#include +#include + +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; + } +} diff --git a/Kernel/drivers/misc/aip/scancodes.c b/Kernel/drivers/misc/aip/scancodes.c new file mode 100644 index 00000000..d4bf2c57 --- /dev/null +++ b/Kernel/drivers/misc/aip/scancodes.c @@ -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 . +*/ + +#include +#include + +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}; diff --git a/Kernel/drivers/misc/aip/uart.cpp b/Kernel/drivers/misc/aip/uart.cpp new file mode 100644 index 00000000..868276ad --- /dev/null +++ b/Kernel/drivers/misc/aip/uart.cpp @@ -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 . +*/ + +#include +#include + +#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); + // } +} diff --git a/Kernel/drivers/misc/mem/mem.cpp b/Kernel/drivers/misc/mem/mem.cpp new file mode 100644 index 00000000..5c452b19 --- /dev/null +++ b/Kernel/drivers/misc/mem/mem.cpp @@ -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 . +*/ + +#include +#include + +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); +} diff --git a/Kernel/drivers/misc/pty/pty.cpp b/Kernel/drivers/misc/pty/pty.cpp new file mode 100644 index 00000000..dcbe9552 --- /dev/null +++ b/Kernel/drivers/misc/pty/pty.cpp @@ -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 . +*/ + +#include +#include +#include +#include + +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); +} diff --git a/Kernel/drivers/misc/vmware/vmware.cpp b/Kernel/drivers/misc/vmware/vmware.cpp new file mode 100644 index 00000000..487a8191 --- /dev/null +++ b/Kernel/drivers/misc/vmware/vmware.cpp @@ -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 . +*/ + +#include +#include +#include +#include +#include + +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); +} diff --git a/Kernel/drivers/net/e1000/e1000.cpp b/Kernel/drivers/net/e1000/e1000.cpp new file mode 100644 index 00000000..063c7d3d --- /dev/null +++ b/Kernel/drivers/net/e1000/e1000.cpp @@ -0,0 +1,485 @@ +/* + 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 . +*/ + +#include +#include +#include +#include + +#include "e1000.hpp" + +extern Driver::Manager *DriverManager; +extern PCI::Manager *PCIManager; +namespace Driver::E1000 +{ + dev_t DriverID; + + class E1000Device + { + private: + PCI::PCIHeader0 *Header; + uint16_t DeviceID; + bool Initialized = false; + + bool EEPROMAvailable = false; + + struct BARData + { + uint8_t Type; + uint16_t IOBase; + uint64_t MemoryBase; + } BAR; + +#define E1000_NUM_RX_DESC 32 +#define E1000_NUM_TX_DESC 8 + RXDescriptor *RX[E1000_NUM_RX_DESC]; + TXDescriptor *TX[E1000_NUM_TX_DESC]; + + uint16_t RXCurrent; + uint16_t TXCurrent; + + const int BaseBufferSize = 8192; + const int AdditionalBytes = 16; + + uint32_t CurrentPacket; + + void WriteCMD(uint16_t Address, uint32_t Value) + { + if (BAR.Type == 0) + mmoutl((void *)(BAR.MemoryBase + Address), Value); + else + { + outl(BAR.IOBase, Address); + outl(BAR.IOBase + 4, Value); + } + } + + uint32_t ReadCMD(uint16_t Address) + { + if (BAR.Type == 0) + return mminl((void *)(BAR.MemoryBase + Address)); + else + { + outl(BAR.IOBase, Address); + return inl(BAR.IOBase + 0x4); + } + } + + uint32_t ReadEEPROM(uint8_t Address) + { + uint16_t Data = 0; + uint32_t temp = 0; + if (EEPROMAvailable) + { + WriteCMD(REG::EEPROM, (1) | ((uint32_t)(Address) << 8)); + while (!((temp = ReadCMD(REG::EEPROM)) & (1 << 4))) + ; + } + else + { + WriteCMD(REG::EEPROM, (1) | ((uint32_t)(Address) << 2)); + while (!((temp = ReadCMD(REG::EEPROM)) & (1 << 1))) + ; + } + Data = (uint16_t)((temp >> 16) & 0xFFFF); + return Data; + } + + void InitializeRX() + { + debug("Initializing RX..."); + uintptr_t Ptr = (uintptr_t)v0::AllocateMemory(DriverID, + TO_PAGES(sizeof(RXDescriptor) * + E1000_NUM_RX_DESC + + AdditionalBytes)); + + for (int i = 0; i < E1000_NUM_RX_DESC; i++) + { + RX[i] = (RXDescriptor *)(Ptr + i * 16); + RX[i]->Address = (uint64_t)v0::AllocateMemory(DriverID, + TO_PAGES(BaseBufferSize + AdditionalBytes)); + RX[i]->Status = 0; + } + +#pragma GCC diagnostic ignored "-Wshift-count-overflow" + + WriteCMD(REG::TXDESCLO, (uint32_t)(Ptr >> 32)); + WriteCMD(REG::TXDESCHI, (uint32_t)(Ptr & 0xFFFFFFFF)); + + WriteCMD(REG::RXDESCLO, (uint32_t)Ptr); + WriteCMD(REG::RXDESCHI, 0); + + WriteCMD(REG::RXDESCLEN, E1000_NUM_RX_DESC * 16); + + WriteCMD(REG::RXDESCHEAD, 0); + WriteCMD(REG::RXDESCTAIL, E1000_NUM_RX_DESC - 1); + RXCurrent = 0; + WriteCMD(REG::RCTRL, RCTL::EN | RCTL::SBP | RCTL::UPE | + RCTL::MPE | RCTL::LBM_NONE | + RTCL::RDMTS_HALF | RCTL::BAM | + RCTL::SECRC | RCTL::BSIZE_8192); + } + + void InitializeTX() + { + debug("Initializing TX..."); + uintptr_t Ptr = (uintptr_t)v0::AllocateMemory(DriverID, + TO_PAGES(sizeof(TXDescriptor) * + E1000_NUM_RX_DESC + + AdditionalBytes)); + + for (short i = 0; i < E1000_NUM_TX_DESC; i++) + { + TX[i] = (TXDescriptor *)((uintptr_t)Ptr + i * 16); + TX[i]->Address = 0; + TX[i]->Command = 0; + TX[i]->Status = TSTA::DD; + } + + WriteCMD(REG::TXDESCHI, (uint32_t)((uint64_t)Ptr >> 32)); + WriteCMD(REG::TXDESCLO, (uint32_t)((uint64_t)Ptr & 0xFFFFFFFF)); + + WriteCMD(REG::TXDESCLEN, E1000_NUM_TX_DESC * 16); + + WriteCMD(REG::TXDESCHEAD, 0); + WriteCMD(REG::TXDESCTAIL, 0); + TXCurrent = 0; + WriteCMD(REG::TCTRL, TCTL::EN_ | TCTL::PSP | + (15 << TCTL::CT_SHIFT) | + (64 << TCTL::COLD_SHIFT) | + TCTL::RTLC); + + WriteCMD(REG::TCTRL, 0b0110000000000111111000011111010); + WriteCMD(REG::TIPG, 0x0060200A); + } + + public: + dev_t ID; + + bool IsInitialized() { return Initialized; } + + size_t write(uint8_t *Buffer, size_t Size) + { + TX[TXCurrent]->Address = (uint64_t)Buffer; + TX[TXCurrent]->Length = (uint16_t)Size; + TX[TXCurrent]->Command = CMD::EOP | CMD::IFCS | CMD::RS; + TX[TXCurrent]->Status = 0; + uint16_t OldTXCurrent = TXCurrent; + TXCurrent = (uint16_t)((TXCurrent + 1) % E1000_NUM_TX_DESC); + WriteCMD(REG::TXDESCTAIL, TXCurrent); + while (!(TX[OldTXCurrent]->Status & 0xFF)) + v0::Yield(DriverID); + return Size; + } + + MediaAccessControl GetMAC() + { + MediaAccessControl mac; + if (EEPROMAvailable) + { + uint32_t temp; + temp = ReadEEPROM(0); + mac.Address[0] = temp & 0xff; + mac.Address[1] = (uint8_t)(temp >> 8); + temp = ReadEEPROM(1); + mac.Address[2] = temp & 0xff; + mac.Address[3] = (uint8_t)(temp >> 8); + temp = ReadEEPROM(2); + mac.Address[4] = temp & 0xff; + mac.Address[5] = (uint8_t)(temp >> 8); + } + else + { + uint8_t *BaseMac8 = (uint8_t *)(BAR.MemoryBase + 0x5400); + uint32_t *BaseMac32 = (uint32_t *)(BAR.MemoryBase + 0x5400); + if (BaseMac32[0] != 0) + for (int i = 0; i < 6; i++) + mac.Address[i] = BaseMac8[i]; + else + { + trace("No MAC address found."); + return MediaAccessControl(); + } + } + + return mac; + } + + int ioctl(NetIoctl req, void *arg) + { + switch (req) + { + case IOCTL_NET_GET_MAC: + { + MediaAccessControl mac = GetMAC(); + *((uint48_t *)arg) = mac.ToHex(); /* UNTESTED */ + return 0; + } + default: + return -EINVAL; + } + return 0; + } + + void OnInterruptReceived(CPU::TrapFrame *) + { + WriteCMD(REG::IMASK, 0x1); + uint32_t status = ReadCMD(0xC0); + UNUSED(status); + + while ((RX[RXCurrent]->Status & 0x1)) + { + uint8_t *data = (uint8_t *)RX[RXCurrent]->Address; + uint16_t dataSz = RX[RXCurrent]->Length; + + // ReportNetworkPacket(ID, data, dataSz); + /* FIXME: Implement */ + trace("FIXME: Received packet"); + (void)data; + (void)dataSz; + + RX[RXCurrent]->Status = 0; + uint16_t OldRXCurrent = RXCurrent; + RXCurrent = (uint16_t)((RXCurrent + 1) % E1000_NUM_RX_DESC); + WriteCMD(REG::RXDESCTAIL, OldRXCurrent); + } + } + + void Panic() + { + WriteCMD(REG::IMASK, 0x00000000); + WriteCMD(REG::ITR, 0x00000000); + WriteCMD(REG::IAM, 0x00000000); + } + + E1000Device(PCI::PCIHeader0 *_Header, uint16_t _DeviceID) + : Header(_Header), + DeviceID(_DeviceID) + { + uint32_t PCIBAR0 = Header->BAR0; + uint32_t PCIBAR1 = Header->BAR1; + BAR.Type = PCIBAR0 & 1; + BAR.IOBase = (uint16_t)(PCIBAR0 & (~3)); + BAR.MemoryBase = PCIBAR1 & (~15); + + switch (DeviceID) + { + case 0x100E: + { + trace("Found Intel 82540EM Gigabit Ethernet Controller."); + + /* Detect EEPROM */ + WriteCMD(REG::EEPROM, 0x1); + for (int i = 0; i < 1000 && !EEPROMAvailable; i++) + if (ReadCMD(REG::EEPROM) & 0x10) + EEPROMAvailable = true; + else + EEPROMAvailable = false; + + if (!GetMAC().Valid()) + { + trace("Failed to get MAC"); + return; + } + + /* Start link */ + uint32_t cmdret = ReadCMD(REG::CTRL); + WriteCMD(REG::CTRL, cmdret | ECTRL::SLU); + + for (int i = 0; i < 0x80; i++) + WriteCMD((uint16_t)(0x5200 + i * 4), 0); + + WriteCMD(REG::IMASK, 0x1F6DC); + WriteCMD(REG::IMASK, 0xFF & ~4); + ReadCMD(0xC0); + + InitializeRX(); + InitializeTX(); + break; + } + default: + { + trace("Unimplemented E1000 device."); + return; + } + } + + Initialized = true; + } + + ~E1000Device() + { + if (!Initialized) + return; + + switch (DeviceID) + { + case 0x100E: + { + // Clearing Enable bit in Receive Control Register + uint32_t cmdret = ReadCMD(REG::RCTRL); + WriteCMD(REG::RCTRL, cmdret & ~RCTL::EN); + + // Masking Interrupt Mask, Interrupt Throttling Rate & Interrupt Auto-Mask + WriteCMD(REG::IMASK, 0x00000000); + WriteCMD(REG::ITR, 0x00000000); + WriteCMD(REG::IAM, 0x00000000); + + // Clearing SLU bit in Device Control Register + cmdret = ReadCMD(REG::CTRL); + WriteCMD(REG::CTRL, cmdret & ~ECTRL::SLU); + + // Clear the Interrupt Cause Read register by reading it + ReadCMD(REG::ICR); + + // Powering down the device (?) + WriteCMD(REG::CTRL, PCTRL::POWER_DOWN); + /* TODO: Stop link; further testing required */ + break; + } + default: + { + trace("Unimplemented E1000 device."); + return; + } + } + + ((PCI::PCIDeviceHeader *)Header)->Command |= PCI::PCI_COMMAND_INTX_DISABLE; + } + }; + + std::unordered_map Drivers; + + int Open(struct Inode *, int, mode_t) + { + return 0; + } + + int Close(struct Inode *) + { + return 0; + } + + ssize_t Read(struct Inode *, void *, size_t, off_t) + { + return 0; + } + + ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t) + { + return Drivers[Node->GetMinor()]->write((uint8_t *)Buffer, Size); + } + + int Ioctl(struct Inode *Node, unsigned long Request, void *Argp) + { + return Drivers[Node->GetMinor()]->ioctl((NetIoctl)Request, Argp); + } + + 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 = nullptr, + .Stat = nullptr, + }; + + std::list Devices; + int Entry() + { + for (auto &&dev : Devices) + { + PCIManager->InitializeDevice(dev, KernelPageTable); + E1000Device *e1000 = new E1000Device((PCI::PCIHeader0 *)dev.Header, + dev.Header->DeviceID); + + if (e1000->IsInitialized()) + { + dev_t ret = v0::RegisterDevice(DriverID, NETWORK_TYPE_ETHERNET, &ops); + Drivers[ret] = e1000; + } + } + + if (Drivers.empty()) + { + trace("No valid E1000 device found."); + return -EINVAL; + } + + return 0; + } + + int Final() + { + for (auto &&dev : Drivers) + { + dev_t ret = dev.first; + v0::UnregisterDevice(DriverID, ret); + delete dev.second; + } + return 0; + } + + int Panic() + { + for (auto &&i : Drivers) + i.second->Panic(); + return 0; + } + + int Probe() + { + Devices = PCIManager->FindPCIDevice( + { + 0x8086, /* Intel */ + }, + { + 0x100E, /* 82540EM */ + 0x100F, /* 82545EM */ + 0x10D3, /* 82574L */ + 0x10EA, /* I217-LM */ + 0x153A, /* 82577LM */ + }); + + if (Devices.empty()) + { + trace("No E1000 device found."); + return -ENODEV; + } + return 0; + } + + REGISTER_BUILTIN_DRIVER(e1000, + "Intel(R) PRO/1000 Network Driver", + "enderice2", + 1, 0, 0, + Entry, + Final, + Panic, + Probe); +} diff --git a/Kernel/drivers/net/e1000/e1000.hpp b/Kernel/drivers/net/e1000/e1000.hpp new file mode 100644 index 00000000..4c4ac0fd --- /dev/null +++ b/Kernel/drivers/net/e1000/e1000.hpp @@ -0,0 +1,156 @@ +/* + 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 . +*/ + +#pragma once + +#include + +enum REG +{ + CTRL = 0x0000, + STATUS = 0x0008, + ICR = 0x000C, + EEPROM = 0x0014, + CTRL_EXT = 0x0018, + ITR = 0x00C4, + IMASK = 0x00D0, + IAM = 0x00D8, + RCTRL = 0x0100, + RXDESCLO = 0x2800, + RXDESCHI = 0x2804, + RXDESCLEN = 0x2808, + RXDESCHEAD = 0x2810, + RXDESCTAIL = 0x2818, + TCTRL = 0x0400, + TXDESCLO = 0x3800, + TXDESCHI = 0x3804, + TXDESCLEN = 0x3808, + TXDESCHEAD = 0x3810, + TXDESCTAIL = 0x3818, + RDTR = 0x2820, + RXDCTL = 0x3828, + RADV = 0x282C, + RSRPD = 0x2C00, + TIPG = 0x0410 +}; + +enum PCTRL +{ + RESERVED = 0b000000, + SPEED_SELECTION_MSB = 0b010000, + UPDATE_COLLISION_TEST = 0b001000, + DUPLEX_MODE = 0b000100, + RESTART_AUTO_NEGOTIATION = 0b000010, + ISOLATE = 0b000001, + POWER_DOWN = 0b100000, + SPEED_SELECTION_LSB = 0b100000, +}; + +enum ECTRL +{ + SLU = 0x40 +}; + +enum RTCL +{ + RDMTS_HALF = (0 << 8), + RDMTS_QUARTER = (1 << 8), + RDMTS_EIGHTH = (2 << 8) +}; + +enum RCTL +{ + EN = (1 << 1), + SBP = (1 << 2), + UPE = (1 << 3), + MPE = (1 << 4), + LPE = (1 << 5), + LBM_NONE = (0 << 6), + LBM_PHY = (3 << 6), + MO_36 = (0 << 12), + MO_35 = (1 << 12), + MO_34 = (2 << 12), + MO_32 = (3 << 12), + BAM = (1 << 15), + VFE = (1 << 18), + CFIEN = (1 << 19), + CFI = (1 << 20), + DPF = (1 << 22), + PMCF = (1 << 23), + SECRC = (1 << 26), + BSIZE_256 = (3 << 16), + BSIZE_512 = (2 << 16), + BSIZE_1024 = (1 << 16), + BSIZE_2048 = (0 << 16), + BSIZE_4096 = ((3 << 16) | (1 << 25)), + BSIZE_8192 = ((2 << 16) | (1 << 25)), + BSIZE_16384 = ((1 << 16) | (1 << 25)) +}; + +enum CMD +{ + EOP = (1 << 0), + IFCS = (1 << 1), + IC = (1 << 2), + RS = (1 << 3), + RPS = (1 << 4), + VLE = (1 << 6), + IDE = (1 << 7) +}; + +enum TCTL +{ + EN_ = (1 << 1), + PSP = (1 << 3), + CT_SHIFT = 4, + COLD_SHIFT = 12, + SWXOFF = (1 << 22), + RTLC = (1 << 24) +}; + +enum TSTA +{ + DD = (1 << 0), + EC = (1 << 1), + LC = (1 << 2) +}; + +enum LSTA +{ + LSTA_TU = (1 << 3) +}; + +struct RXDescriptor +{ + volatile uint64_t Address; + volatile uint16_t Length; + volatile uint16_t Checksum; + volatile uint8_t Status; + volatile uint8_t Errors; + volatile uint16_t Special; +} __attribute__((packed)); + +struct TXDescriptor +{ + volatile uint64_t Address; + volatile uint16_t Length; + volatile uint8_t cso; + volatile uint8_t Command; + volatile uint8_t Status; + volatile uint8_t css; + volatile uint16_t Special; +} __attribute__((packed)); diff --git a/Kernel/drivers/net/rtl8139/rtl8139.cpp b/Kernel/drivers/net/rtl8139/rtl8139.cpp new file mode 100644 index 00000000..a785c70d --- /dev/null +++ b/Kernel/drivers/net/rtl8139/rtl8139.cpp @@ -0,0 +1,294 @@ +/* + 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 . +*/ + +#include +#include +#include +#include +#include + +#include "rtl8139.hpp" + +extern Driver::Manager *DriverManager; +extern PCI::Manager *PCIManager; +namespace Driver::RTL8139 +{ + dev_t DriverID; + + class RTL8139Device + { + private: + PCI::PCIHeader0 *Header; + bool Initialized = false; + + struct BARData + { + uint8_t Type; + uint16_t IOBase; + uint64_t MemoryBase; + } BAR; + + const int BaseBufferSize = 8192; + const int WRAPBytes = 1500; + const int AdditionalBytes = 16; + const int BufferSize = BaseBufferSize + + WRAPBytes + + AdditionalBytes; + + uint8_t *RXBuffer = nullptr; + int TXCurrent = 0; + uint16_t CurrentPacket = 0; + + uint8_t TSAD[4] = {0x20, 0x24, 0x28, 0x2C}; + uint8_t TSD[4] = {0x10, 0x14, 0x18, 0x1C}; + + public: + dev_t ID; + + bool IsInitialized() { return Initialized; } + + size_t write(uint8_t *Buffer, size_t Size) + { + outl(TSAD[TXCurrent], (uint32_t)(reinterpret_cast(Buffer))); + outl(TSD[TXCurrent++], (uint32_t)Size); + if (TXCurrent > 3) + TXCurrent = 0; + return Size; + } + + MediaAccessControl GetMAC() + { + return MediaAccessControl(); + } + + int ioctl(NetIoctl req, void *) + { + switch (req) + { + case IOCTL_NET_GET_MAC: + { + return -ENOSYS; + } + default: + return -EINVAL; + } + return 0; + } + + void OnInterruptReceived(CPU::TrapFrame *) + { + /* Acknowledge interrupt */ + uint16_t status = inw(RegISR); + debug("%#lx", status); + + /* Read status */ + if (status & RecOK) + { + /* Get the current packet */ + uint16_t *data = (uint16_t *)(RXBuffer + CurrentPacket); + uint16_t dataSz = *(data + 1); + data += 2; + + // ReportNetworkPacket(ID, data, dataSz); + /* FIXME: Implement */ + fixme("Received packet"); + (void)data; + (void)dataSz; + + /* Update CAPR */ +#define RX_READ_PTR_MASK (~0x3) + CurrentPacket = (uint16_t)((CurrentPacket + dataSz + 4 + 3) & RX_READ_PTR_MASK); + if (CurrentPacket > BufferSize) + CurrentPacket -= uint16_t(BufferSize); + outw(RegCAPR, CurrentPacket - 0x10); + } + + /* Clear interrupt */ + outw(RegISR, (RecOK | RecBad | SendOK | SendBad)); + } + + void Panic() + { + } + + RTL8139Device(PCI::PCIHeader0 *_Header) + : Header(_Header) + { + uint32_t PCIBAR0 = Header->BAR0; + uint32_t PCIBAR1 = Header->BAR1; + BAR.Type = PCIBAR0 & 1; + BAR.IOBase = (uint16_t)(PCIBAR0 & (~3)); + BAR.MemoryBase = PCIBAR1 & (~15); + + RXBuffer = (uint8_t *)v0::AllocateMemory(DriverID, TO_PAGES(BufferSize)); + + /* Power on */ + outb(RegCONFIG1, 0x0); + + /* Software Reset */ + outb(RegCMD, 0x10); + while (inb(RegCMD) & 0x10) + v0::Yield(DriverID); + + /* Initialize receive buffer */ + outl(RegRBSTART, (uint32_t)(reinterpret_cast(RXBuffer))); + + /* Configure interrupt mask register */ + outw(RegIMR, (RecOK | RecBad | SendOK | SendBad)); + outl(regRCR, (RcAB | RcAM | RcAPM | RcAAP) | RcWRAP); + + /* Enable receive and transmit */ + outb(RegCMD, 0xC); /* 0xC = RE and TE bit */ + + uint32_t MAC1 = inl(RegMAC); + uint16_t MAC2 = inw(RegMAR); + MediaAccessControl mac = { + mac.Address[0] = (uint8_t)MAC1, + mac.Address[1] = (uint8_t)(MAC1 >> 8), + mac.Address[2] = (uint8_t)(MAC1 >> 16), + mac.Address[3] = (uint8_t)(MAC1 >> 24), + mac.Address[4] = (uint8_t)MAC2, + mac.Address[5] = (uint8_t)(MAC2 >> 8)}; + + Initialized = true; + } + + ~RTL8139Device() + { + if (!Initialized) + return; + + ((PCI::PCIDeviceHeader *)Header)->Command |= PCI::PCI_COMMAND_INTX_DISABLE; + /* FIXME: Shutdown code */ + } + }; + + std::unordered_map Drivers; + + int Open(struct Inode *, int, mode_t) + { + return 0; + } + + int Close(struct Inode *) + { + return 0; + } + + ssize_t Read(struct Inode *, void *, size_t, off_t) + { + return 0; + } + + ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t) + { + return Drivers[Node->GetMinor()]->write((uint8_t *)Buffer, Size); + } + + int Ioctl(struct Inode *Node, unsigned long Request, void *Argp) + { + return Drivers[Node->GetMinor()]->ioctl((NetIoctl)Request, Argp); + } + + 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 = nullptr, + .Stat = nullptr, + }; + + std::list Devices; + int Entry() + { + for (auto &&dev : Devices) + { + PCIManager->InitializeDevice(dev, KernelPageTable); + RTL8139Device *rtl8139 = new RTL8139Device((PCI::PCIHeader0 *)dev.Header); + if (rtl8139->IsInitialized()) + { + dev_t ret = v0::RegisterDevice(DriverID, NETWORK_TYPE_ETHERNET, &ops); + Drivers[ret] = rtl8139; + } + } + + if (Drivers.empty()) + { + trace("No valid RTL8139 device found."); + return -EINVAL; + } + + return 0; + } + + int Final() + { + for (auto &&dev : Drivers) + { + dev_t ret = dev.first; + v0::UnregisterDevice(DriverID, ret); + delete dev.second; + } + return 0; + } + + int Panic() + { + for (auto &&i : Drivers) + i.second->Panic(); + return 0; + } + + int Probe() + { + Devices = PCIManager->FindPCIDevice( + { + 0x10EC, /* Realtek */ + }, + { + 0x8139, /* RTL8139 */ + }); + + if (Devices.empty()) + { + trace("No RTL8139 device found."); + return -ENODEV; + } + return 0; + } + + REGISTER_BUILTIN_DRIVER(rtl8139, + "Realtek RTL8139 Network Driver", + "enderice2", + 1, 0, 0, + Entry, + Final, + Panic, + Probe); +} diff --git a/Kernel/drivers/net/rtl8139/rtl8139.hpp b/Kernel/drivers/net/rtl8139/rtl8139.hpp new file mode 100644 index 00000000..2ac45242 --- /dev/null +++ b/Kernel/drivers/net/rtl8139/rtl8139.hpp @@ -0,0 +1,88 @@ +/* + 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 . +*/ + +#pragma once + +#include + +struct RXQueueEntry +{ + uint8_t *Buffer; + uint16_t Size; +} __attribute__((packed)); + +enum InterruptStatus +{ + RecOK = 0x1, + RecBad = 0x2, + SendOK = 0x4, + SendBad = 0x8, +}; + +enum ReceiveConfig +{ + /** + * Accept broadcast packets + * sent to mac ff:ff:ff:ff:ff:ff + */ + RcAB = 0x1, + + /** + * Accept packets sent to the + * multicast address + */ + RcAM = 0x2, + + /** + * Accept packets sent to the + * NIC's MAC address + */ + RcAPM = 0x4, + + /** + * Accept all packets + * (promiscuous mode) + */ + RcAAP = 0x8, + + /** + * The WRAP bit is used to tell the + * NIC to wrap around the ring + * buffer when it reaches the end + * of the buffer. + * + * @note If this bit is set, the + * buffer must have an additional + * 1500 bytes of space at the end + * of the buffer to prevent the + * NIC from overflowing the buffer. + */ + RcWRAP = 0x80, +}; + +enum Registers +{ + RegMAC = 0x0, + RegMAR = 0x8, + RegRBSTART = 0x30, + RegCMD = 0x37, + RegCAPR = 0x38, + regRCR = 0x44, + RegCONFIG1 = 0x52, + RegIMR = 0x3C, + RegISR = 0x3E, +}; diff --git a/Kernel/drivers/storage/ahci/ahci.cpp b/Kernel/drivers/storage/ahci/ahci.cpp new file mode 100644 index 00000000..a7f097ed --- /dev/null +++ b/Kernel/drivers/storage/ahci/ahci.cpp @@ -0,0 +1,1010 @@ +/* + 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 . +*/ + +#include +#include +#include + +extern Driver::Manager *DriverManager; +extern PCI::Manager *PCIManager; +EXTERNC void KPrint(const char *Format, ...); +namespace Driver::AHCI +{ + dev_t DriverID; + +#define ATA_DEV_DRQ 0x08 +#define ATA_DEV_BUSY 0x80 +#define ATA_CMD_READ_DMA_EX 0x25 +#define ATA_CMD_WRITE_DMA_EX 0x35 +#define ATA_CMD_IDENTIFY 0xEC + +#define ATAPI_CMD_IDENTIFY_PACKET 0xA1 + +#define HBA_PORT_IPM_ACTIVE 0x1 +#define HBA_PORT_DEV_PRESENT 0x3 +#define HBA_PxIS_TFES (1 << 30) + +#define SATA_SIG_ATA 0x00000101 +#define SATA_SIG_PM 0x96690101 +#define SATA_SIG_SEMB 0xC33C0101 +#define SATA_SIG_ATAPI 0xEB140101 + +#define HBA_PxCMD_ST 0x0001 +#define HBA_PxCMD_FRE 0x0010 +#define HBA_PxCMD_FR 0x4000 +#define HBA_PxCMD_CR 0x8000 + + enum PortType + { + None = 0, + SATA = 1, + SEMB = 2, + PM = 3, + SATAPI = 4, + }; + + enum FIS_TYPE + { + FIS_TYPE_REG_H2D = 0x27, + FIS_TYPE_REG_D2H = 0x34, + FIS_TYPE_DMA_ACT = 0x39, + FIS_TYPE_DMA_SETUP = 0x41, + FIS_TYPE_DATA = 0x46, + FIS_TYPE_BIST = 0x58, + FIS_TYPE_PIO_SETUP = 0x5F, + FIS_TYPE_DEV_BITS = 0xA1, + }; + + struct HBAPort + { + uint32_t CommandListBase; + uint32_t CommandListBaseUpper; + uint32_t FISBaseAddress; + uint32_t FISBaseAddressUpper; + uint32_t InterruptStatus; + uint32_t InterruptEnable; + uint32_t CommandStatus; + uint32_t Reserved0; + uint32_t TaskFileData; + uint32_t Signature; + uint32_t SataStatus; + uint32_t SataControl; + uint32_t SataError; + uint32_t SataActive; + uint32_t CommandIssue; + uint32_t SataNotification; + uint32_t FISSwitchControl; + uint32_t Reserved1[11]; + uint32_t Vendor[4]; + }; + + struct HBAMemory + { + uint32_t HostCapability; + uint32_t GlobalHostControl; + uint32_t InterruptStatus; + uint32_t PortsImplemented; + uint32_t Version; + uint32_t CCCControl; + uint32_t CCCPorts; + uint32_t EnclosureManagementLocation; + uint32_t EnclosureManagementControl; + uint32_t HostCapabilitiesExtended; + uint32_t BIOSHandoffControlStatus; + uint8_t Reserved0[0x74]; + uint8_t Vendor[0x60]; + HBAPort Ports[1]; + }; + + struct HBACommandHeader + { + uint8_t CommandFISLength : 5; + uint8_t ATAPI : 1; + uint8_t Write : 1; + uint8_t Preferable : 1; + uint8_t Reset : 1; + uint8_t BIST : 1; + uint8_t ClearBusy : 1; + uint8_t Reserved0 : 1; + uint8_t PortMultiplier : 4; + uint16_t PRDTLength; + uint32_t PRDBCount; + uint32_t CommandTableBaseAddress; + uint32_t CommandTableBaseAddressUpper; + uint32_t Reserved1[4]; + }; + + struct HBAPRDTEntry + { + uint32_t DataBaseAddress; + uint32_t DataBaseAddressUpper; + uint32_t Reserved0; + uint32_t ByteCount : 22; + uint32_t Reserved1 : 9; + uint32_t InterruptOnCompletion : 1; + }; + + struct HBACommandTable + { + uint8_t CommandFIS[64]; + uint8_t ATAPICommand[16]; + uint8_t Reserved[48]; + HBAPRDTEntry PRDTEntry[]; + }; + + struct FIS_REG_H2D + { + uint8_t FISType; + uint8_t PortMultiplier : 4; + uint8_t Reserved0 : 3; + uint8_t CommandControl : 1; + uint8_t Command; + uint8_t FeatureLow; + uint8_t LBA0; + uint8_t LBA1; + uint8_t LBA2; + uint8_t DeviceRegister; + uint8_t LBA3; + uint8_t LBA4; + uint8_t LBA5; + uint8_t FeatureHigh; + uint8_t CountLow; + uint8_t CountHigh; + uint8_t ISOCommandCompletion; + uint8_t Control; + uint8_t Reserved1[4]; + }; + + /* https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ata/ns-ata-_identify_device_data */ + struct __packed ATA_IDENTIFY + { + struct __packed + { + uint16_t Reserved1 : 1; + uint16_t Retired3 : 1; + uint16_t ResponseIncomplete : 1; + uint16_t Retired2 : 3; + uint16_t FixedDevice : 1; + uint16_t RemovableMedia : 1; + uint16_t Retired1 : 7; + uint16_t DeviceType : 1; + } GeneralConfiguration; + uint16_t NumCylinders; + uint16_t SpecificConfiguration; + uint16_t NumHeads; + uint16_t Retired1[2]; + uint16_t NumSectorsPerTrack; + uint16_t VendorUnique1[3]; + uint8_t SerialNumber[20]; + uint16_t Retired2[2]; + uint16_t Obsolete1; + uint8_t FirmwareRevision[8]; + uint8_t ModelNumber[40]; + uint8_t MaximumBlockTransfer; + uint8_t VendorUnique2; + struct __packed + { + uint16_t FeatureSupported : 1; + uint16_t Reserved : 15; + } TrustedComputing; + struct __packed + { + uint8_t CurrentLongPhysicalSectorAlignment : 2; + uint8_t ReservedByte49 : 6; + uint8_t DmaSupported : 1; + uint8_t LbaSupported : 1; + uint8_t IordyDisable : 1; + uint8_t IordySupported : 1; + uint8_t Reserved1 : 1; + uint8_t StandybyTimerSupport : 1; + uint8_t Reserved2 : 2; + uint16_t ReservedWord50; + } Capabilities; + uint16_t ObsoleteWords51[2]; + uint16_t TranslationFieldsValid : 3; + uint16_t Reserved3 : 5; + uint16_t FreeFallControlSensitivity : 8; + uint16_t NumberOfCurrentCylinders; + uint16_t NumberOfCurrentHeads; + uint16_t CurrentSectorsPerTrack; + uint32_t CurrentSectorCapacity; + uint8_t CurrentMultiSectorSetting; + uint8_t MultiSectorSettingValid : 1; + uint8_t ReservedByte59 : 3; + uint8_t SanitizeFeatureSupported : 1; + uint8_t CryptoScrambleExtCommandSupported : 1; + uint8_t OverwriteExtCommandSupported : 1; + uint8_t BlockEraseExtCommandSupported : 1; + uint32_t UserAddressableSectors; + uint16_t ObsoleteWord62; + uint16_t MultiWordDMASupport : 8; + uint16_t MultiWordDMAActive : 8; + uint16_t AdvancedPIOModes : 8; + uint16_t ReservedByte64 : 8; + uint16_t MinimumMWXferCycleTime; + uint16_t RecommendedMWXferCycleTime; + uint16_t MinimumPIOCycleTime; + uint16_t MinimumPIOCycleTimeIORDY; + struct __packed + { + uint16_t ZonedCapabilities : 2; + uint16_t NonVolatileWriteCache : 1; + uint16_t ExtendedUserAddressableSectorsSupported : 1; + uint16_t DeviceEncryptsAllUserData : 1; + uint16_t ReadZeroAfterTrimSupported : 1; + uint16_t Optional28BitCommandsSupported : 1; + uint16_t IEEE1667 : 1; + uint16_t DownloadMicrocodeDmaSupported : 1; + uint16_t SetMaxSetPasswordUnlockDmaSupported : 1; + uint16_t WriteBufferDmaSupported : 1; + uint16_t ReadBufferDmaSupported : 1; + uint16_t DeviceConfigIdentifySetDmaSupported : 1; + uint16_t LPSAERCSupported : 1; + uint16_t DeterministicReadAfterTrimSupported : 1; + uint16_t CFastSpecSupported : 1; + } AdditionalSupported; + uint16_t ReservedWords70[5]; + uint16_t QueueDepth : 5; + uint16_t ReservedWord75 : 11; + struct __packed + { + uint16_t Reserved0 : 1; + uint16_t SataGen1 : 1; + uint16_t SataGen2 : 1; + uint16_t SataGen3 : 1; + uint16_t Reserved1 : 4; + uint16_t NCQ : 1; + uint16_t HIPM : 1; + uint16_t PhyEvents : 1; + uint16_t NcqUnload : 1; + uint16_t NcqPriority : 1; + uint16_t HostAutoPS : 1; + uint16_t DeviceAutoPS : 1; + uint16_t ReadLogDMA : 1; + uint16_t Reserved2 : 1; + uint16_t CurrentSpeed : 3; + uint16_t NcqStreaming : 1; + uint16_t NcqQueueMgmt : 1; + uint16_t NcqReceiveSend : 1; + uint16_t DEVSLPtoReducedPwrState : 1; + uint16_t Reserved3 : 8; + } SerialAtaCapabilities; + struct __packed + { + uint16_t Reserved0 : 1; + uint16_t NonZeroOffsets : 1; + uint16_t DmaSetupAutoActivate : 1; + uint16_t DIPM : 1; + uint16_t InOrderData : 1; + uint16_t HardwareFeatureControl : 1; + uint16_t SoftwareSettingsPreservation : 1; + uint16_t NCQAutosense : 1; + uint16_t DEVSLP : 1; + uint16_t HybridInformation : 1; + uint16_t Reserved1 : 6; + } SerialAtaFeaturesSupported; + struct __packed + { + uint16_t Reserved0 : 1; + uint16_t NonZeroOffsets : 1; + uint16_t DmaSetupAutoActivate : 1; + uint16_t DIPM : 1; + uint16_t InOrderData : 1; + uint16_t HardwareFeatureControl : 1; + uint16_t SoftwareSettingsPreservation : 1; + uint16_t DeviceAutoPS : 1; + uint16_t DEVSLP : 1; + uint16_t HybridInformation : 1; + uint16_t Reserved1 : 6; + } SerialAtaFeaturesEnabled; + uint16_t MajorRevision; + uint16_t MinorRevision; + struct __packed + { + uint16_t SmartCommands : 1; + uint16_t SecurityMode : 1; + uint16_t RemovableMediaFeature : 1; + uint16_t PowerManagement : 1; + uint16_t Reserved1 : 1; + uint16_t WriteCache : 1; + uint16_t LookAhead : 1; + uint16_t ReleaseInterrupt : 1; + uint16_t ServiceInterrupt : 1; + uint16_t DeviceReset : 1; + uint16_t HostProtectedArea : 1; + uint16_t Obsolete1 : 1; + uint16_t WriteBuffer : 1; + uint16_t ReadBuffer : 1; + uint16_t Nop : 1; + uint16_t Obsolete2 : 1; + uint16_t DownloadMicrocode : 1; + uint16_t DmaQueued : 1; + uint16_t Cfa : 1; + uint16_t AdvancedPm : 1; + uint16_t Msn : 1; + uint16_t PowerUpInStandby : 1; + uint16_t ManualPowerUp : 1; + uint16_t Reserved2 : 1; + uint16_t SetMax : 1; + uint16_t Acoustics : 1; + uint16_t BigLba : 1; + uint16_t DeviceConfigOverlay : 1; + uint16_t FlushCache : 1; + uint16_t FlushCacheExt : 1; + uint16_t WordValid83 : 2; + uint16_t SmartErrorLog : 1; + uint16_t SmartSelfTest : 1; + uint16_t MediaSerialNumber : 1; + uint16_t MediaCardPassThrough : 1; + uint16_t StreamingFeature : 1; + uint16_t GpLogging : 1; + uint16_t WriteFua : 1; + uint16_t WriteQueuedFua : 1; + uint16_t WWN64Bit : 1; + uint16_t URGReadStream : 1; + uint16_t URGWriteStream : 1; + uint16_t ReservedForTechReport : 2; + uint16_t IdleWithUnloadFeature : 1; + uint16_t WordValid : 2; + } CommandSetSupport; + struct __packed + { + uint16_t SmartCommands : 1; + uint16_t SecurityMode : 1; + uint16_t RemovableMediaFeature : 1; + uint16_t PowerManagement : 1; + uint16_t Reserved1 : 1; + uint16_t WriteCache : 1; + uint16_t LookAhead : 1; + uint16_t ReleaseInterrupt : 1; + uint16_t ServiceInterrupt : 1; + uint16_t DeviceReset : 1; + uint16_t HostProtectedArea : 1; + uint16_t Obsolete1 : 1; + uint16_t WriteBuffer : 1; + uint16_t ReadBuffer : 1; + uint16_t Nop : 1; + uint16_t Obsolete2 : 1; + uint16_t DownloadMicrocode : 1; + uint16_t DmaQueued : 1; + uint16_t Cfa : 1; + uint16_t AdvancedPm : 1; + uint16_t Msn : 1; + uint16_t PowerUpInStandby : 1; + uint16_t ManualPowerUp : 1; + uint16_t Reserved2 : 1; + uint16_t SetMax : 1; + uint16_t Acoustics : 1; + uint16_t BigLba : 1; + uint16_t DeviceConfigOverlay : 1; + uint16_t FlushCache : 1; + uint16_t FlushCacheExt : 1; + uint16_t Resrved3 : 1; + uint16_t Words119_120Valid : 1; + uint16_t SmartErrorLog : 1; + uint16_t SmartSelfTest : 1; + uint16_t MediaSerialNumber : 1; + uint16_t MediaCardPassThrough : 1; + uint16_t StreamingFeature : 1; + uint16_t GpLogging : 1; + uint16_t WriteFua : 1; + uint16_t WriteQueuedFua : 1; + uint16_t WWN64Bit : 1; + uint16_t URGReadStream : 1; + uint16_t URGWriteStream : 1; + uint16_t ReservedForTechReport : 2; + uint16_t IdleWithUnloadFeature : 1; + uint16_t Reserved4 : 2; + } CommandSetActive; + uint16_t UltraDMASupport : 8; + uint16_t UltraDMAActive : 8; + struct __packed + { + uint16_t TimeRequired : 15; + uint16_t ExtendedTimeReported : 1; + } NormalSecurityEraseUnit; + struct __packed + { + uint16_t TimeRequired : 15; + uint16_t ExtendedTimeReported : 1; + } EnhancedSecurityEraseUnit; + uint16_t CurrentAPMLevel : 8; + uint16_t ReservedWord91 : 8; + uint16_t MasterPasswordID; + uint16_t HardwareResetResult; + uint16_t CurrentAcousticValue : 8; + uint16_t RecommendedAcousticValue : 8; + uint16_t StreamMinRequestSize; + uint16_t StreamingTransferTimeDMA; + uint16_t StreamingAccessLatencyDMAPIO; + uint32_t StreamingPerfGranularity; + uint32_t Max48BitLBA[2]; + uint16_t StreamingTransferTime; + uint16_t DsmCap; + struct __packed + { + uint16_t LogicalSectorsPerPhysicalSector : 4; + uint16_t Reserved0 : 8; + uint16_t LogicalSectorLongerThan256Words : 1; + uint16_t MultipleLogicalSectorsPerPhysicalSector : 1; + uint16_t Reserved1 : 2; + } PhysicalLogicalSectorSize; + uint16_t InterSeekDelay; + uint16_t WorldWideName[4]; + uint16_t ReservedForWorldWideName128[4]; + uint16_t ReservedForTlcTechnicalReport; + uint16_t WordsPerLogicalSector[2]; + struct __packed + { + uint16_t ReservedForDrqTechnicalReport : 1; + uint16_t WriteReadVerify : 1; + uint16_t WriteUncorrectableExt : 1; + uint16_t ReadWriteLogDmaExt : 1; + uint16_t DownloadMicrocodeMode3 : 1; + uint16_t FreefallControl : 1; + uint16_t SenseDataReporting : 1; + uint16_t ExtendedPowerConditions : 1; + uint16_t Reserved0 : 6; + uint16_t WordValid : 2; + } CommandSetSupportExt; + struct __packed + { + uint16_t ReservedForDrqTechnicalReport : 1; + uint16_t WriteReadVerify : 1; + uint16_t WriteUncorrectableExt : 1; + uint16_t ReadWriteLogDmaExt : 1; + uint16_t DownloadMicrocodeMode3 : 1; + uint16_t FreefallControl : 1; + uint16_t SenseDataReporting : 1; + uint16_t ExtendedPowerConditions : 1; + uint16_t Reserved0 : 6; + uint16_t Reserved1 : 2; + } CommandSetActiveExt; + uint16_t ReservedForExpandedSupportandActive[6]; + uint16_t MsnSupport : 2; + uint16_t ReservedWord127 : 14; + struct __packed + { + uint16_t SecuritySupported : 1; + uint16_t SecurityEnabled : 1; + uint16_t SecurityLocked : 1; + uint16_t SecurityFrozen : 1; + uint16_t SecurityCountExpired : 1; + uint16_t EnhancedSecurityEraseSupported : 1; + uint16_t Reserved0 : 2; + uint16_t SecurityLevel : 1; + uint16_t Reserved1 : 7; + } SecurityStatus; + uint16_t ReservedWord129[31]; + struct __packed + { + uint16_t MaximumCurrentInMA : 12; + uint16_t CfaPowerMode1Disabled : 1; + uint16_t CfaPowerMode1Required : 1; + uint16_t Reserved0 : 1; + uint16_t Word160Supported : 1; + } CfaPowerMode1; + uint16_t ReservedForCfaWord161[7]; + uint16_t NominalFormFactor : 4; + uint16_t ReservedWord168 : 12; + struct __packed + { + uint16_t SupportsTrim : 1; + uint16_t Reserved0 : 15; + } DataSetManagementFeature; + uint16_t AdditionalProductID[4]; + uint16_t ReservedForCfaWord174[2]; + uint16_t CurrentMediaSerialNumber[30]; + struct __packed + { + uint16_t Supported : 1; + uint16_t Reserved0 : 1; + uint16_t WriteSameSuported : 1; + uint16_t ErrorRecoveryControlSupported : 1; + uint16_t FeatureControlSuported : 1; + uint16_t DataTablesSuported : 1; + uint16_t Reserved1 : 6; + uint16_t VendorSpecific : 4; + } SCTCommandTransport; + uint16_t ReservedWord207[2]; + struct __packed + { + uint16_t AlignmentOfLogicalWithinPhysical : 14; + uint16_t Word209Supported : 1; + uint16_t Reserved0 : 1; + } BlockAlignment; + uint16_t WriteReadVerifySectorCountMode3Only[2]; + uint16_t WriteReadVerifySectorCountMode2Only[2]; + struct __packed + { + uint16_t NVCachePowerModeEnabled : 1; + uint16_t Reserved0 : 3; + uint16_t NVCacheFeatureSetEnabled : 1; + uint16_t Reserved1 : 3; + uint16_t NVCachePowerModeVersion : 4; + uint16_t NVCacheFeatureSetVersion : 4; + } NVCacheCapabilities; + uint16_t NVCacheSizeLSW; + uint16_t NVCacheSizeMSW; + uint16_t NominalMediaRotationRate; + uint16_t ReservedWord218; + struct __packed + { + uint8_t NVCacheEstimatedTimeToSpinUpInSeconds; + uint8_t Reserved; + } NVCacheOptions; + uint16_t WriteReadVerifySectorCountMode : 8; + uint16_t ReservedWord220 : 8; + uint16_t ReservedWord221; + struct __packed + { + uint16_t MajorVersion : 12; + uint16_t TransportType : 4; + } TransportMajorVersion; + uint16_t TransportMinorVersion; + uint16_t ReservedWord224[6]; + uint32_t ExtendedNumberOfUserAddressableSectors[2]; + uint16_t MinBlocksPerDownloadMicrocodeMode03; + uint16_t MaxBlocksPerDownloadMicrocodeMode03; + uint16_t ReservedWord236[19]; + uint16_t Signature : 8; + uint16_t CheckSum : 8; + }; + + class Port + { + public: + PortType AHCIPortType; + HBAPort *HBAPortPtr; + uint8_t *Buffer; + uint8_t PortNumber; + ATA_IDENTIFY *IdentifyData; + + Port(PortType Type, HBAPort *PortPtr, uint8_t PortNumber) + { + this->AHCIPortType = Type; + this->HBAPortPtr = PortPtr; + this->Buffer = static_cast(v0::AllocateMemory(DriverID, 1)); + memset(this->Buffer, 0, PAGE_SIZE); + this->IdentifyData = static_cast(v0::AllocateMemory(DriverID, 1)); + memset(this->IdentifyData, 0, PAGE_SIZE); + this->PortNumber = PortNumber; + } + + ~Port() + { + v0::FreeMemory(DriverID, this->Buffer, 1); + } + + void StartCMD() + { + while (HBAPortPtr->CommandStatus & HBA_PxCMD_CR) + v0::Yield(DriverID); + HBAPortPtr->CommandStatus |= HBA_PxCMD_FRE; + HBAPortPtr->CommandStatus |= HBA_PxCMD_ST; + } + + void StopCMD() + { + HBAPortPtr->CommandStatus &= ~HBA_PxCMD_ST; + HBAPortPtr->CommandStatus &= ~HBA_PxCMD_FRE; + while (true) + { + if (HBAPortPtr->CommandStatus & HBA_PxCMD_FR) + continue; + if (HBAPortPtr->CommandStatus & HBA_PxCMD_CR) + continue; + break; + } + } + + void Configure() + { + this->StopCMD(); + void *CmdBase = v0::AllocateMemory(DriverID, 1); + HBAPortPtr->CommandListBase = (uint32_t)(uint64_t)CmdBase; + HBAPortPtr->CommandListBaseUpper = (uint32_t)((uint64_t)CmdBase >> 32); + memset(reinterpret_cast(HBAPortPtr->CommandListBase), 0, 1024); + + void *FISBase = v0::AllocateMemory(DriverID, 1); + HBAPortPtr->FISBaseAddress = (uint32_t)(uint64_t)FISBase; + HBAPortPtr->FISBaseAddressUpper = (uint32_t)((uint64_t)FISBase >> 32); + memset(FISBase, 0, 256); + + HBACommandHeader *CommandHeader = (HBACommandHeader *)((uint64_t)HBAPortPtr->CommandListBase + ((uint64_t)HBAPortPtr->CommandListBaseUpper << 32)); + for (int i = 0; i < 32; i++) + { + CommandHeader[i].PRDTLength = 8; + void *CommandTableAddress = v0::AllocateMemory(DriverID, 1); + uint64_t Address = (uint64_t)CommandTableAddress + (i << 8); + CommandHeader[i].CommandTableBaseAddress = (uint32_t)(uint64_t)Address; + CommandHeader[i].CommandTableBaseAddressUpper = (uint32_t)((uint64_t)Address >> 32); + memset(CommandTableAddress, 0, 256); + } + this->StartCMD(); + + Identify(); + + trace("Port %d \"%x %x %x %x\" configured", PortNumber, + HBAPortPtr->Vendor[0], HBAPortPtr->Vendor[1], + HBAPortPtr->Vendor[2], HBAPortPtr->Vendor[3]); + } + + bool ReadWrite(uint64_t Sector, uint32_t SectorCount, void *Buffer, bool Write) + { + if (this->AHCIPortType == PortType::SATAPI && Write == true) + { + trace("SATAPI port does not support write."); + return false; + } + + uint32_t SectorL = (uint32_t)Sector; + uint32_t SectorH = (uint32_t)(Sector >> 32); + + HBAPortPtr->InterruptStatus = 0xFFFFFFFF; /* Clear pending interrupt bits */ + + HBACommandHeader *CommandHeader = reinterpret_cast(HBAPortPtr->CommandListBase); + CommandHeader->CommandFISLength = sizeof(FIS_REG_H2D) / sizeof(uint32_t); + if (Write) + CommandHeader->Write = 1; + else + CommandHeader->Write = 0; + CommandHeader->PRDTLength = 1; + + HBACommandTable *CommandTable = reinterpret_cast(CommandHeader->CommandTableBaseAddress); + memset(CommandTable, 0, sizeof(HBACommandTable) + (CommandHeader->PRDTLength - 1) * sizeof(HBAPRDTEntry)); + + CommandTable->PRDTEntry[0].DataBaseAddress = (uint32_t)(uint64_t)Buffer; + CommandTable->PRDTEntry[0].DataBaseAddressUpper = (uint32_t)((uint64_t)Buffer >> 32); + +#pragma GCC diagnostic push +/* conversion from 'uint32_t' {aka 'unsigned int'} to 'unsigned int:22' may change value */ +#pragma GCC diagnostic ignored "-Wconversion" + CommandTable->PRDTEntry[0].ByteCount = (SectorCount << 9) - 1; /* 512 bytes per sector */ +#pragma GCC diagnostic pop + + CommandTable->PRDTEntry[0].InterruptOnCompletion = 1; + + FIS_REG_H2D *CommandFIS = (FIS_REG_H2D *)(&CommandTable->CommandFIS); + + CommandFIS->FISType = FIS_TYPE_REG_H2D; + CommandFIS->CommandControl = 1; + if (Write) + CommandFIS->Command = ATA_CMD_WRITE_DMA_EX; + else + CommandFIS->Command = ATA_CMD_READ_DMA_EX; + + CommandFIS->LBA0 = (uint8_t)SectorL; + CommandFIS->LBA1 = (uint8_t)(SectorL >> 8); + CommandFIS->LBA2 = (uint8_t)(SectorL >> 16); + CommandFIS->LBA3 = (uint8_t)SectorH; + CommandFIS->LBA4 = (uint8_t)(SectorH >> 8); + CommandFIS->LBA5 = (uint8_t)(SectorH >> 16); + + CommandFIS->DeviceRegister = 1 << 6; /* LBA mode */ + CommandFIS->CountLow = SectorCount & 0xFF; + CommandFIS->CountHigh = (SectorCount >> 8) & 0xFF; + + uint64_t spinLock = 0; + while ((HBAPortPtr->TaskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && spinLock < 1000000) + spinLock++; + + if (spinLock == 1000000) + { + trace("Port not responding."); + return false; + } + + HBAPortPtr->CommandIssue = 1; + + spinLock = 0; + int retries = 0; + while (true) + { + if (spinLock > 100000000) + { + trace("Port %d not responding. (%d)", + this->PortNumber, retries); + + spinLock = 0; + retries++; + if (retries > 10) + return false; + } + + if (HBAPortPtr->CommandIssue == 0) + break; + spinLock++; + + if (HBAPortPtr->InterruptStatus & HBA_PxIS_TFES) + { + trace("Error reading/writing (%d).", Write); + return false; + } + } + + return true; + } + + void Identify() + { + memset(this->IdentifyData, 0, sizeof(ATA_IDENTIFY)); + HBACommandHeader *CommandHeader = reinterpret_cast(HBAPortPtr->CommandListBase); + CommandHeader->CommandFISLength = sizeof(FIS_REG_H2D) / sizeof(uint32_t); + CommandHeader->Write = 0; + CommandHeader->PRDTLength = 1; + + HBACommandTable *CommandTable = reinterpret_cast(CommandHeader->CommandTableBaseAddress); + memset(CommandTable, 0, sizeof(HBACommandTable) + (CommandHeader->PRDTLength - 1) * sizeof(HBAPRDTEntry)); + + CommandTable->PRDTEntry[0].DataBaseAddress = (uint32_t)(uint64_t)this->IdentifyData; + CommandTable->PRDTEntry[0].DataBaseAddressUpper = (uint32_t)((uint64_t)this->IdentifyData >> 32); + CommandTable->PRDTEntry[0].ByteCount = 511; + CommandTable->PRDTEntry[0].InterruptOnCompletion = 1; + + FIS_REG_H2D *CommandFIS = (FIS_REG_H2D *)(&CommandTable->CommandFIS); + CommandFIS->FISType = FIS_TYPE_REG_H2D; + CommandFIS->CommandControl = 1; + CommandFIS->Command = this->AHCIPortType == PortType::SATAPI ? ATAPI_CMD_IDENTIFY_PACKET : ATA_CMD_IDENTIFY; + + HBAPortPtr->CommandIssue = 1; + + while (HBAPortPtr->CommandIssue) + v0::Yield(DriverID); + + if (HBAPortPtr->InterruptStatus & HBA_PxIS_TFES) + { + trace("Error reading IDENTIFY command."); + return; + } + + if (IdentifyData->Signature != 0xA5) + trace("Port %d has no validity signature.", PortNumber); + else + { + uint8_t *ptr = (uint8_t *)IdentifyData; + uint8_t sum = 0; + for (size_t i = 0; i < sizeof(ATA_IDENTIFY); i++) + sum += ptr[i]; + if (sum != 0) + { + trace("Port %d has invalid checksum.", PortNumber); + return; + } + else + trace("Port %d has valid checksum.", PortNumber); + } + + char *Model = (char *)this->IdentifyData->ModelNumber; + char ModelSwap[41]; + for (size_t i = 0; i < 40; i += 2) + { + ModelSwap[i] = Model[i + 1]; + ModelSwap[i + 1] = Model[i]; + } + ModelSwap[40] = 0; + + trace("Port %d \"%s\" identified", PortNumber, + ModelSwap); + trace("Port %d is %s (%d rotation rate)", PortNumber, + IdentifyData->NominalMediaRotationRate == 1 ? "SSD" : "HDD", + IdentifyData->NominalMediaRotationRate); + } + }; + + std::unordered_map PortDevices; + + const char *PortTypeName[] = {"None", + "SATA", + "SEMB", + "PM", + "SATAPI"}; + + PortType CheckPortType(HBAPort *Port) + { + uint32_t SataStatus = Port->SataStatus; + uint8_t InterfacePowerManagement = (SataStatus >> 8) & 0b111; + uint8_t DeviceDetection = SataStatus & 0b111; + + if (DeviceDetection != HBA_PORT_DEV_PRESENT) + return PortType::None; + if (InterfacePowerManagement != HBA_PORT_IPM_ACTIVE) + return PortType::None; + + switch (Port->Signature) + { + case SATA_SIG_ATAPI: + return PortType::SATAPI; + case SATA_SIG_ATA: + return PortType::SATA; + case SATA_SIG_PM: + return PortType::PM; + case SATA_SIG_SEMB: + return PortType::SEMB; + default: + return PortType::None; + } + } + + int Open(struct Inode *, int, mode_t) + { + return 0; + } + + int Close(struct Inode *) + { + return 0; + } + + ssize_t Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset) + { + uint64_t sector = Offset / 512; + uint32_t sectorCount = uint32_t(Size / 512); + int num = Node->GetMinor(); + bool ok = PortDevices[num]->ReadWrite(sector, sectorCount, Buffer, false); + return ok ? Size : 0; + } + + ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset) + { + uint64_t sector = Offset / 512; + uint32_t sectorCount = uint32_t(Size / 512); + int num = Node->GetMinor(); + bool ok = PortDevices[num]->ReadWrite(sector, sectorCount, (void *)Buffer, true); + return ok ? Size : 0; + } + + const struct InodeOperations ops = { + .Lookup = nullptr, + .Create = nullptr, + .Remove = nullptr, + .Rename = nullptr, + .Read = Read, + .Write = Write, + .Truncate = nullptr, + .Open = Open, + .Close = Close, + .Ioctl = nullptr, + .ReadDir = nullptr, + .MkDir = nullptr, + .RmDir = nullptr, + .SymLink = nullptr, + .ReadLink = nullptr, + .Seek = nullptr, + .Stat = nullptr, + }; + + std::list Devices; + int Entry() + { + /* We loop through all the devices and initialize them */ + for (auto &&dev : Devices) + { + PCIManager->InitializeDevice(dev, KernelPageTable); + PCI::PCIHeader0 *hdr0 = (PCI::PCIHeader0 *)dev.Header; + HBAMemory *hba = (HBAMemory *)(uintptr_t)hdr0->BAR5; + uint32_t portsImplemented = hba->PortsImplemented; + trace("AHCI ports implemented: %x", portsImplemented); + + for (int i = 0; i < 32; i++) + { + if (!(portsImplemented & (1 << i))) + continue; + + trace("Port %d implemented", i); + + PortType portType = CheckPortType(&hba->Ports[i]); + switch (portType) + { + case PortType::SATA: + case PortType::SATAPI: + { + KPrint("%s drive found at port %d", PortTypeName[portType], i); + Port *port = new Port(portType, &hba->Ports[i], i); + dev_t ret = v0::RegisterDevice(DriverID, BLOCK_TYPE_HDD, &ops); + PortDevices[ret] = port; + break; + } + case PortType::SEMB: + case PortType::PM: + { + KPrint("Unimplemented drive type %s found at port %d", + PortTypeName[portType], i); + break; + } + default: + { + trace("Unsupported drive type %s found at port %d", + PortTypeName[portType], i); + break; + } + } + } + } + + if (PortDevices.empty()) + { + info("No valid AHCI device found."); + return -ENODEV; + } + + trace("Initializing AHCI ports"); + for (auto &&p : PortDevices) + p.second->Configure(); + + /* We don't use the interrupt handler now... maybe we will in the future */ + // RegisterInterruptHandler(iLine(ctx->Device), (void *)OnInterruptReceived); + + return 0; + } + + int Final() + { + for (auto &&p : PortDevices) + { + p.second->StopCMD(); + v0::UnregisterDevice(DriverID, p.first); + delete p.second; + } + + /* Making sure that PortDevices is empty */ + PortDevices.clear(); + // ctx->Device->Header->Command |= PCI::PCI_COMMAND_INTX_DISABLE; + + // std::list Devices = PCIManager->FindPCIDevice(VendorIDs, DeviceIDs); + // foreach (auto dev in Devices) + // Interrupts::RemoveHandler(OnInterruptReceived, iLine(dev)); + return 0; + } + + int Panic() + { + for (auto &&p : PortDevices) + p.second->StopCMD(); + return 0; + } + + int Probe() + { + Devices = PCIManager->FindPCIDevice( + { + 0x8086, /* Intel */ + 0x15AD, /* VMware */ + }, + { + 0x2922, /* ICH9 */ + 0x2829, /* ICH8 */ + 0x07E0, /* SATA AHCI (VMware) */ + }); + + if (Devices.empty()) + { + trace("No AHCI device found."); + return -ENODEV; + } + return 0; + } + + REGISTER_BUILTIN_DRIVER(ahci, + "Advanced Host Controller Interface Driver", + "enderice2", + 1, 0, 0, + Entry, + Final, + Panic, + Probe); +} diff --git a/Kernel/drivers/video/kdm/kdm.cpp b/Kernel/drivers/video/kdm/kdm.cpp new file mode 100644 index 00000000..921ece1a --- /dev/null +++ b/Kernel/drivers/video/kdm/kdm.cpp @@ -0,0 +1,128 @@ +/* + 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 . +*/ + +#include + +extern Driver::Manager *DriverManager; +namespace Driver::KernelDisplayManager +{ + dev_t DriverID; + + struct + { + dev_t kdm; + } ids; + + int Open(struct Inode *Node, int Flags, mode_t Mode) + { + dev_t min = Node->GetMinor(); + if (min == ids.kdm) + return -ENOSYS; + + return -ENODEV; + } + + int Close(struct Inode *Node) + { + dev_t min = Node->GetMinor(); + if (min == ids.kdm) + return -ENOSYS; + + return -ENODEV; + } + + int Ioctl(struct Inode *Node, unsigned long Request, void *Argp) + { + dev_t min = Node->GetMinor(); + if (min == ids.kdm) + 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.kdm) + 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.kdm) + 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() + { + mode_t mode = 0; + + /* c rw- rw- rw- */ + mode = S_IRUSR | S_IWUSR | + S_IRGRP | S_IWGRP | + S_IROTH | S_IWOTH | + S_IFCHR; + ids.kdm = DriverManager->CreateDeviceFile(DriverID, "kdm", mode, &ops); + return 0; + } + + int Final() + { + DriverManager->UnregisterDevice(DriverID, ids.kdm); + return 0; + } + + int Panic() { return 0; } + int Probe() { return 0; } + + REGISTER_BUILTIN_DRIVER(kdm, + "Kernel Display Manager Driver", + "enderice2", + 1, 0, 0, + Entry, + Final, + Panic, + Probe); +}