diff --git a/.gitignore b/.gitignore index 8c827b2..6f7b02f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ out *.map *.o +*.a +*.su .dccache diff --git a/.vscode/c_boilerplates.code-snippets b/.vscode/c_boilerplates.code-snippets index 366f30a..a3c12a1 100644 --- a/.vscode/c_boilerplates.code-snippets +++ b/.vscode/c_boilerplates.code-snippets @@ -24,5 +24,29 @@ "/** @brief $0 */" ], "description": "Create documentation brief." + }, + "License": { + "prefix": [ + "license", + ], + "body": [ + "/*", + "\tThis file is part of Fennix Drivers.", + "", + "\tFennix Drivers is free software: you can redistribute it and/or", + "\tmodify it under the terms of the GNU General Public License as", + "\tpublished by the Free Software Foundation, either version 3 of", + "\tthe License, or (at your option) any later version.", + "", + "\tFennix Drivers is distributed in the hope that it will be useful,", + "\tbut WITHOUT ANY WARRANTY; without even the implied warranty of", + "\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the", + "\tGNU General Public License for more details.", + "", + "\tYou should have received a copy of the GNU General Public License", + "\talong with Fennix Drivers. If not, see .", + "*/" + ], + "description": "Create kernel license." } } \ No newline at end of file diff --git a/Audio/Makefile b/Audio/Makefile deleted file mode 100644 index cbb34d8..0000000 --- a/Audio/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -build: - -clean: diff --git a/Doxyfile b/Doxyfile index 0a1450e..b278f92 100644 --- a/Doxyfile +++ b/Doxyfile @@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "Fennix Modules" +PROJECT_NAME = "Fennix Drivers" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version @@ -44,7 +44,7 @@ PROJECT_NUMBER = 1.0.0 # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = "Fennix Userspace Documentation" +PROJECT_BRIEF = "Driver API Documentation" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 @@ -864,7 +864,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = Modules tools/Doxygen_README.md +INPUT = Drivers/include tools/doxymds/drivers.md # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -1064,7 +1064,7 @@ FILTER_SOURCE_PATTERNS = # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. -USE_MDFILE_AS_MAINPAGE = tools/Doxygen_README.md +USE_MDFILE_AS_MAINPAGE = tools/doxymds/drivers.md #--------------------------------------------------------------------------- # Configuration options related to source browsing @@ -1224,7 +1224,7 @@ GENERATE_HTML = YES # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_OUTPUT = doxygen-doc/modules +HTML_OUTPUT = doxygen-doc/drivers # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). diff --git a/FileSystem/Makefile b/FileSystem/Makefile deleted file mode 100644 index cbb34d8..0000000 --- a/FileSystem/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -build: - -clean: diff --git a/Generic/ExampleDriver/Example.cpp b/Generic/ExampleDriver/Example.cpp deleted file mode 100644 index 139a52e..0000000 --- a/Generic/ExampleDriver/Example.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "../../../Kernel/mapi.hpp" -#include "../../../Kernel/Fex.hpp" - -extern "C" int ModuleEntry(void *Data); -int CallbackHandler(KernelCallback *Data); -int InterruptCallback(CPURegisters *Registers); - -/* The module is - * This is a module for Fennix Module Entry Extended Header - * * * * */ -HEAD(FexFormatType_Module, FexOSType_Fennix, ModuleEntry); - -/* Ignore the warning about missing field initializers. */ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" - -/* Extended header which is used to give additional information to the kernel. */ -__attribute__((section(".extended"))) FexExtended ExtendedHeader = { - .Module = { - .Name = "Example Module", - .Type = FexModuleType_Generic, - .Callback = CallbackHandler, - .InterruptCallback = InterruptCallback, - .Bind = { - .Type = BIND_INTERRUPT, - .Interrupt = { - .Vector = {222}, /* IRQ222 */ - }}}}; - -#pragma GCC diagnostic pop - -/* Global structure that holds the kernel API. */ -KernelAPI KAPI{}; - -/* Macro that prints a message to the kernel debugger. */ -#define print(msg) KAPI.Util.DebugPrint((char *)(msg), KAPI.Info.modUniqueID) - -/* --------------------------------------------------------------------------------------------------------- */ - -/* Module entry point. This is called at initialization. - "Data" argument points to the kernel API structure. */ -int ModuleEntry(void *Data) -{ - /* Check if kernel API is valid */ - if (!Data) - return INVALID_KERNEL_API; - - /* Set the global structure to the kernel API. */ - KAPI = *(KernelAPI *)Data; - - /* Check if kernel API version is valid. - This is important because the kernel - API may change in the future. */ - if (KAPI.Version.Major < 0 || KAPI.Version.Minor < 0 || KAPI.Version.Patch < 0) - return KERNEL_API_VERSION_NOT_SUPPORTED; - - /* We print "Hello World!" to the kernel debugger. */ - print("Hello World!"); - return OK; -} - -/* This is called when the module is bound to an interrupt, process, - or PCI device or when the kernel wants to send a message to the module. */ -int CallbackHandler(KernelCallback *Data) -{ - switch (Data->Reason) - { - case AcknowledgeReason: - { - print("Kernel acknowledged the module."); - break; - } - case StopReason: - { - print("Module stopped."); - break; - } - default: - { - print("Unknown reason."); - break; - } - } - return OK; -} - -/* Interrupt handler. */ -int InterruptCallback(CPURegisters *) -{ - print("Interrupt received."); - return OK; -} diff --git a/Generic/ExampleDriver/Makefile b/Generic/ExampleDriver/Makefile deleted file mode 100644 index 2d586cc..0000000 --- a/Generic/ExampleDriver/Makefile +++ /dev/null @@ -1,98 +0,0 @@ -# Config file -include ../../../Makefile.conf - -FILENAME = ExampleDriver.fex - -CC = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)gcc -CPP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)g++ -LD = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)ld -AS = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)as -OBJDUMP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)objdump - -GIT_COMMIT = $(shell git rev-parse HEAD) -GIT_COMMIT_SHORT = $(shell git rev-parse --short HEAD) - -ifeq ($(OSARCH), amd64) -S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*") -C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*") -CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*") -else ifeq ($(OSARCH), i386) -S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") -C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") -CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*") -else ifeq ($(OSARCH), aarch64) -S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/amd64/*" -not -path "./arch/i386/*") -C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/amd64/*" -not -path "./arch/i386/*") -CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/amd64/*" -not -path "./arch/i386/*") -endif -HEADERS = $(sort $(dir $(wildcard ../../include/*))) -OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o) -INCLUDE_DIR = ../../include - -LDFLAGS := \ - -fPIC -fPIE -pie -Wl,-eDriverEntry \ - -Wl,-static,--no-dynamic-linker,-ztext,--no-warn-rwx-segment \ - -nostdlib -nodefaultlibs -nolibc \ - -zmax-page-size=0x1000 \ - -Wl,-Map file.map -static - -WARNCFLAG = -Wall -Wextra - -CFLAGS := \ - -I$(INCLUDE_DIR) \ - -DGIT_COMMIT='"$(GIT_COMMIT)"' \ - -DGIT_COMMIT_SHORT='"$(GIT_COMMIT_SHORT)"' - -ifeq ($(OSARCH), amd64) - -LDFLAGS += -Tlinker_amd64.ld - -CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ - -mno-red-zone -mno-sse -mno-sse2 \ - -march=x86-64 -pipe -ffunction-sections \ - -msoft-float -fno-builtin - -else ifeq ($(OSARCH), i386) - -LDFLAGS += -Tlinker_i386.ld - -CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ - -mno-red-zone -mno-sse -mno-sse2 -ffunction-sections \ - -march=i386 -pipe -msoft-float -fno-builtin - -else ifeq ($(OSARCH), aarch64) - -LDFLAGS += -Tlinker_aarch64.ld - -CFLAGS += -pipe -fno-builtin -fPIC - -endif - -build: $(FILENAME) -ifeq ($(OSARCH), amd64) - $(OBJDUMP) -b binary -D -m i386:x86-64 -d $(FILENAME) > file_dump.map -else ifeq ($(OSARCH), i386) - $(OBJDUMP) -b binary -D -m i386 -d $(FILENAME) > file_dump.map -else ifeq ($(OSARCH), aarch64) - $(OBJDUMP) -b binary -D -m aarch64 -d $(FILENAME) > file_dump.map -endif - mv $(FILENAME) ../../out/$(FILENAME) - -$(FILENAME): $(OBJ) - $(info Linking $@) - $(CC) $(LDFLAGS) $(OBJ) -o $@ - -%.o: %.c $(HEADERS) - $(info Compiling $<) - $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ - -%.o: %.cpp $(HEADERS) - $(info Compiling $<) - $(CPP) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fexceptions -c $< -o $@ - -%.o: %.S - $(info Compiling $<) - $(AS) -o $@ $< - -clean: - rm -f file.map file_dump.map $(OBJ) diff --git a/Generic/ExampleDriver/linker_amd64.ld b/Generic/ExampleDriver/linker_amd64.ld deleted file mode 100644 index 29cd11c..0000000 --- a/Generic/ExampleDriver/linker_amd64.ld +++ /dev/null @@ -1,40 +0,0 @@ -OUTPUT_FORMAT(binary) -OUTPUT_ARCH(i386:x86-64) - -ENTRY(DriverEntry) - -SECTIONS -{ - .header : - { - *(.header .header.*) - *(.extended .extended.*) - } - - .text : - { - *(.text .text.*) - } - - .data : - { - *(.data .data.*) - } - - .rodata : - { - *(.rodata .rodata.*) - } - - .bss : - { - *(COMMON) - *(.bss .bss.*) - } - - /DISCARD/ : - { - *(.eh_frame) - *(.note .note.*) - } -} diff --git a/Generic/ExampleDriver/linker_i386.ld b/Generic/ExampleDriver/linker_i386.ld deleted file mode 100644 index 9d72554..0000000 --- a/Generic/ExampleDriver/linker_i386.ld +++ /dev/null @@ -1,41 +0,0 @@ -OUTPUT_FORMAT(binary) -OUTPUT_ARCH(i386) - -ENTRY(DriverEntry) - -SECTIONS -{ - .header : - { - *(.header .header.*) - *(.extended .extended.*) - } - - .text : - { - *(.text .text.*) - } - - .data : - { - *(.data .data.*) - PROVIDE(_GLOBAL_OFFSET_TABLE_ = .); - } - - .rodata : - { - *(.rodata .rodata.*) - } - - .bss : - { - *(COMMON) - *(.bss .bss.*) - } - - /DISCARD/ : - { - *(.eh_frame) - *(.note .note.*) - } -} diff --git a/Generic/Makefile b/Generic/Makefile deleted file mode 100644 index f011df9..0000000 --- a/Generic/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -build: - make -C ExampleDriver build - -clean: - make -C ExampleDriver clean diff --git a/Input/Makefile b/Input/Makefile deleted file mode 100644 index cbb34d8..0000000 --- a/Input/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -build: - -clean: diff --git a/Makefile b/Makefile index 8f4b619..324f07d 100644 --- a/Makefile +++ b/Makefile @@ -1,22 +1,21 @@ build: + cp -f ../Kernel/driver.h include/driver.h mkdir -p out - make -C Input build - make -C Storage build - make -C Audio build - make -C FileSystem build - make -C Generic build - make -C Network build - make -C Video build + make -C library build + make -C audio build + make -C input build + make -C misc build + make -C network build + make -C storage build prepare: $(info Nothing to prepare) clean: rm -rf out - make -C Input clean - make -C Storage clean - make -C Audio clean - make -C FileSystem clean - make -C Generic clean - make -C Network clean - make -C Video clean + make -C library clean + make -C audio clean + make -C input clean + make -C misc clean + make -C network clean + make -C storage clean diff --git a/Network/Makefile b/Network/Makefile deleted file mode 100644 index cbb34d8..0000000 --- a/Network/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -build: - -clean: diff --git a/README.md b/README.md index 49e33e2..e863fdd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Modules +# Drivers -Modules for [Fennix](https://github.com/Fennix-Project/Fennix). +Drivers for [Fennix](https://github.com/Fennix-Project/Fennix). --- diff --git a/Storage/Makefile b/Storage/Makefile deleted file mode 100644 index cbb34d8..0000000 --- a/Storage/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -build: - -clean: diff --git a/Video/Makefile b/Video/Makefile deleted file mode 100644 index cbb34d8..0000000 --- a/Video/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -build: - -clean: diff --git a/audio/Makefile b/audio/Makefile new file mode 100644 index 0000000..017a1dd --- /dev/null +++ b/audio/Makefile @@ -0,0 +1,7 @@ +build: + make -C ac97 build + make -C hda build + +clean: + make -C ac97 clean + make -C hda clean diff --git a/audio/ac97/Makefile b/audio/ac97/Makefile new file mode 100644 index 0000000..a76f72b --- /dev/null +++ b/audio/ac97/Makefile @@ -0,0 +1,83 @@ +# Config file +include ../../../Makefile.conf + +FILENAME = ac97.drv + +CC = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)gcc +CPP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)g++ +LD = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)ld +AS = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)as +OBJDUMP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)objdump + +S_SOURCES = $(shell find ./ -type f -name '*.S') +C_SOURCES = $(shell find ./ -type f -name '*.c') +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp') +HEADERS = $(sort $(dir $(wildcard ../../include/*))) +OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o) +STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CPP_SOURCES:.cpp=.su) +INCLUDE_DIR = ../../include + +LIBS := ../../out/dcrt0.o -L../../out -ldriver + +LDFLAGS := \ + -fPIC -fPIE -pie \ + -Wl,--no-dynamic-linker,-ztext,--no-warn-rwx-segment \ + -nostdlib -nodefaultlibs -nolibc \ + -zmax-page-size=0x1000 \ + -Wl,-Map file.map -shared -fvisibility=hidden + +WARNCFLAG = -Wall -Wextra + +CFLAGS := -I$(INCLUDE_DIR) -fvisibility=hidden + +ifeq ($(OSARCH), amd64) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 \ + -march=x86-64 -pipe -ffunction-sections \ + -msoft-float -fno-builtin + +else ifeq ($(OSARCH), i386) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 -ffunction-sections \ + -march=i386 -pipe -msoft-float -fno-builtin + +else ifeq ($(OSARCH), aarch64) + +CFLAGS += -pipe -fno-builtin -fPIC + +endif + +ifeq ($(DEBUG), 1) + CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fstack-usage +ifeq ($(OSARCH), amd64) + CFLAGS += -fverbose-asm +endif +ifneq ($(OSARCH), aarch64) + CFLAGS += -fstack-check +endif + LDFLAGS += -ggdb3 -O0 +endif + +build: $(FILENAME) + mv $(FILENAME) ../../out/$(FILENAME) + +$(FILENAME): $(OBJ) + $(info Linking $@) + $(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@ + +%.o: %.c $(HEADERS) + $(info Compiling $<) + $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ + +%.o: %.cpp $(HEADERS) + $(info Compiling $<) + $(CPP) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fno-exceptions -fno-rtti -c $< -o $@ + +%.o: %.S + $(info Compiling $<) + $(AS) -o $@ $< + +clean: + rm -f file.map $(OBJ) $(STACK_USAGE_OBJ) diff --git a/audio/ac97/ac97.cpp b/audio/ac97/ac97.cpp new file mode 100644 index 0000000..d12163b --- /dev/null +++ b/audio/ac97/ac97.cpp @@ -0,0 +1,857 @@ +/* + 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 + +#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: + 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) + { + Log("Invalid buffer."); + return -EINVAL; + } + + if ((Size == 0) || (Size % (SampleSize * Channels))) + { + Log("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) + Sleep(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) + { + Log("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(TrapFrame *) + { + uint16_t Status = inw(MixerAddress + PCMOUT_Status); + if (Status & TC_IOCInterruptEnable) + { + DebugLog("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); + Log("FIXME: CurrentBDL: %d, LastBDL: %d", CurrentBDL, LastBDL); + } + else if (Status & TC_LastBufferEntryInterruptEnable) + { + DebugLog("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) + { + Log("FIFO error"); + outw(MixerAddress + PCMOUT_Status, TC_FifoERRORInterruptEnable); + } + else + { + DebugLog("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(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 *)AllocateMemory(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)AllocateMemory(TO_PAGES(sizeof(uint16_t *))); + DescriptorList[i].SampleCount = DLSampleCount; + DescriptorList[i].Flags = 0; + DebugLog("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); + } +}; + +AC97Device *Drivers[4] = {nullptr}; +dev_t AudioID[4] = {0}; + +#define OIR(x) OIR_##x +#define CREATE_OIR(x) \ + void OIR_##x(TrapFrame *f) { Drivers[x]->OnInterruptReceived(f); } + +CREATE_OIR(0); +CREATE_OIR(1); +CREATE_OIR(2); +CREATE_OIR(3); + +int drvOpen(dev_t, dev_t, int, mode_t) { return 0; } +int drvClose(dev_t, dev_t) { return 0; } +size_t drvRead(dev_t, dev_t, uint8_t *, size_t, off_t) { return 0; } + +size_t drvWrite(dev_t, dev_t min, uint8_t *Buffer, size_t Size, off_t) +{ + return Drivers[AudioID[min]]->write(Buffer, Size); +} + +int drvIoctl(dev_t, dev_t min, unsigned long Request, void *Argp) +{ + return Drivers[AudioID[min]]->ioctl((AudioIoctl)Request, Argp); +} + +PCIArray *Devices; +EXTERNC int cxx_Panic() +{ + PCIArray *ctx = Devices; + short Count = 0; + while (ctx != nullptr) + { + if (Drivers[Count] != nullptr) + Drivers[Count]->Panic(); + Count++; + ctx = (PCIArray *)ctx->Next; + } + + return 0; +} + +EXTERNC int cxx_Probe() +{ + uint16_t VendorIDs[] = {0x8086, PCI_END}; + uint16_t DeviceIDs[] = {0x2415, PCI_END}; + Devices = FindPCIDevices(VendorIDs, DeviceIDs); + if (Devices == nullptr) + { + Log("No AC'97 device found."); + return -ENODEV; + } + + PCIArray *ctx = Devices; + bool Found = false; + size_t Count = 0; + while (ctx != nullptr) + { + if (Count++ > sizeof(Drivers) / sizeof(AC97Device *)) + break; + + PCIHeader0 *PCIBaseAddress = (PCIHeader0 *)ctx->Device->Header; + uint32_t PCIBAR0 = PCIBaseAddress->BAR0; + uint8_t Type = PCIBAR0 & 1; + if (Type != 1) + { + Log("Device %x:%x.%d BAR0 is not I/O.", + PCIBaseAddress->Header.VendorID, + PCIBaseAddress->Header.DeviceID, + PCIBaseAddress->Header.ProgIF); + continue; + } + + Found = true; + ctx = (PCIArray *)ctx->Next; + } + + if (!Found) + { + Log("No valid AC'97 device found."); + return -EINVAL; + } + return 0; +} + +EXTERNC int cxx_Initialize() +{ + PCIArray *ctx = Devices; + size_t Count = 0; + while (ctx != nullptr) + { + if (Count > sizeof(Drivers) / sizeof(AC97Device *)) + break; + + PCIHeader0 *PCIBaseAddress = (PCIHeader0 *)ctx->Device->Header; + uint32_t PCIBAR0 = PCIBaseAddress->BAR0; + uint8_t Type = PCIBAR0 & 1; + if (Type != 1) + { + Log("Device %x:%x.%d BAR0 is not I/O.", + PCIBaseAddress->Header.VendorID, + PCIBaseAddress->Header.DeviceID, + PCIBaseAddress->Header.ProgIF); + continue; + } + + InitializePCI(ctx->Device); + + Drivers[Count] = new AC97Device((PCIHeader0 *)ctx->Device->Header); + /* FIXME: bad code */ + switch (Count) + { + case 0: + RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(0)); + break; + case 1: + RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(1)); + break; + case 2: + RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(2)); + break; + case 3: + RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(3)); + break; + default: + break; + } + dev_t ret = RegisterAudioDevice(ddt_Audio, + drvOpen, drvClose, + drvRead, drvWrite, + drvIoctl); + AudioID[Count] = ret; + Count++; + ctx = (PCIArray *)ctx->Next; + } + + return 0; +} + +EXTERNC int cxx_Finalize() +{ + PCIArray *ctx = Devices; + size_t Count = 0; + while (ctx != nullptr) + { + if (Count++ > sizeof(Drivers) / sizeof(AC97Device *)) + break; + + PCIHeader0 *PCIBaseAddress = (PCIHeader0 *)ctx->Device->Header; + uint32_t PCIBAR0 = PCIBaseAddress->BAR0; + uint8_t Type = PCIBAR0 & 1; + if (Type != 1) + { + Log("Device %x:%x.%d BAR0 is not I/O.", + PCIBaseAddress->Header.VendorID, + PCIBaseAddress->Header.DeviceID, + PCIBaseAddress->Header.ProgIF); + continue; + } + + delete Drivers[Count++]; + ctx->Device->Header->Command |= PCI_COMMAND_INTX_DISABLE; + ctx = (PCIArray *)ctx->Next; + } + + return 0; +} diff --git a/audio/ac97/ac97.hpp b/audio/ac97/ac97.hpp new file mode 100644 index 0000000..99242b9 --- /dev/null +++ b/audio/ac97/ac97.hpp @@ -0,0 +1,25 @@ +/* + 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 . +*/ + +#pragma once + +#include + +EXTERNC int cxx_Panic(); +EXTERNC int cxx_Probe(); +EXTERNC int cxx_Initialize(); +EXTERNC int cxx_Finalize(); diff --git a/audio/ac97/main.c b/audio/ac97/main.c new file mode 100644 index 0000000..bfb84d5 --- /dev/null +++ b/audio/ac97/main.c @@ -0,0 +1,31 @@ +/* + 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 "ac97.hpp" + +int DriverEntry() { return cxx_Initialize(); } +int DriverFinal() { return cxx_Finalize(); } +int DriverPanic() { return cxx_Panic(); } +int DriverProbe() { return cxx_Probe(); } + +DriverInfo("ac97", + "Audio Codec '97 Driver", + "EnderIce2", + "0.1", + "GPLv3"); diff --git a/audio/hda/Makefile b/audio/hda/Makefile new file mode 100644 index 0000000..ade5826 --- /dev/null +++ b/audio/hda/Makefile @@ -0,0 +1,83 @@ +# Config file +include ../../../Makefile.conf + +FILENAME = hda.drv + +CC = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)gcc +CPP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)g++ +LD = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)ld +AS = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)as +OBJDUMP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)objdump + +S_SOURCES = $(shell find ./ -type f -name '*.S') +C_SOURCES = $(shell find ./ -type f -name '*.c') +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp') +HEADERS = $(sort $(dir $(wildcard ../../include/*))) +OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o) +STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CPP_SOURCES:.cpp=.su) +INCLUDE_DIR = ../../include + +LIBS := ../../out/dcrt0.o -L../../out -ldriver + +LDFLAGS := \ + -fPIC -fPIE -pie \ + -Wl,--no-dynamic-linker,-ztext,--no-warn-rwx-segment \ + -nostdlib -nodefaultlibs -nolibc \ + -zmax-page-size=0x1000 \ + -Wl,-Map file.map -shared -fvisibility=hidden + +WARNCFLAG = -Wall -Wextra + +CFLAGS := -I$(INCLUDE_DIR) -fvisibility=hidden + +ifeq ($(OSARCH), amd64) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 \ + -march=x86-64 -pipe -ffunction-sections \ + -msoft-float -fno-builtin + +else ifeq ($(OSARCH), i386) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 -ffunction-sections \ + -march=i386 -pipe -msoft-float -fno-builtin + +else ifeq ($(OSARCH), aarch64) + +CFLAGS += -pipe -fno-builtin -fPIC + +endif + +ifeq ($(DEBUG), 1) + CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fstack-usage +ifeq ($(OSARCH), amd64) + CFLAGS += -fverbose-asm +endif +ifneq ($(OSARCH), aarch64) + CFLAGS += -fstack-check +endif + LDFLAGS += -ggdb3 -O0 +endif + +build: $(FILENAME) + mv $(FILENAME) ../../out/$(FILENAME) + +$(FILENAME): $(OBJ) + $(info Linking $@) + $(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@ + +%.o: %.c $(HEADERS) + $(info Compiling $<) + $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ + +%.o: %.cpp $(HEADERS) + $(info Compiling $<) + $(CPP) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fno-exceptions -fno-rtti -c $< -o $@ + +%.o: %.S + $(info Compiling $<) + $(AS) -o $@ $< + +clean: + rm -f file.map $(OBJ) $(STACK_USAGE_OBJ) diff --git a/audio/hda/hda.cpp b/audio/hda/hda.cpp new file mode 100644 index 0000000..40303de --- /dev/null +++ b/audio/hda/hda.cpp @@ -0,0 +1,239 @@ +/* + 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 + +#include "hda.hpp" + +class HDADevice +{ +private: + 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(TrapFrame *) + { + } + + void Panic() + { + } + + HDADevice(PCIHeader0 *_Header) + : Header(_Header), + CORB((uint32_t *)(uintptr_t)AllocateMemory(1)), + RIRB((uint64_t *)AllocateMemory(1)) + { + CTL = (ControllerRegisters *)(uintptr_t)Header->BAR0; + Log("Unimplemented HDA driver"); + return; + Initialized = true; + } + + ~HDADevice() + { + if (!Initialized) + return; + } +}; + +HDADevice *Drivers[4] = {nullptr}; +dev_t AudioID[4] = {0}; + +#define OIR(x) OIR_##x +#define CREATE_OIR(x) \ + void OIR_##x(TrapFrame *f) { Drivers[x]->OnInterruptReceived(f); } + +CREATE_OIR(0); +CREATE_OIR(1); +CREATE_OIR(2); +CREATE_OIR(3); + +int drvOpen(dev_t, dev_t, int, mode_t) { return 0; } +int drvClose(dev_t, dev_t) { return 0; } +size_t drvRead(dev_t, dev_t, uint8_t *, size_t, off_t) { return 0; } + +size_t drvWrite(dev_t, dev_t min, uint8_t *Buffer, size_t Size, off_t) +{ + return Drivers[AudioID[min]]->write(Buffer, Size); +} + +int drvIoctl(dev_t, dev_t min, unsigned long Request, void *Argp) +{ + return Drivers[AudioID[min]]->ioctl((AudioIoctl)Request, Argp); +} + +PCIArray *Devices; +EXTERNC int cxx_Panic() +{ + PCIArray *ctx = Devices; + short Count = 0; + while (ctx != nullptr) + { + if (Drivers[Count] != nullptr) + Drivers[Count]->Panic(); + Count++; + ctx = (PCIArray *)ctx->Next; + } + + return 0; +} + +EXTERNC int cxx_Probe() +{ + uint16_t VendorIDs[] = {0x8086, /* Intel */ + 0x15AD, /* VMware */ + PCI_END}; + uint16_t DeviceIDs[] = {0x9D71 /* Sunrise Point-LP HD Audio */, + 0x2668 /* ICH6 */, + 0x293E /* ICH9 */, + PCI_END}; + Devices = FindPCIDevices(VendorIDs, DeviceIDs); + if (Devices == nullptr) + { + Log("No HDA device found."); + return -ENODEV; + } + + PCIArray *ctx = Devices; + bool Found = false; + size_t Count = 0; + while (ctx != nullptr) + { + if (Count++ > sizeof(Drivers) / sizeof(HDADevice *)) + break; + + PCIHeader0 *PCIBaseAddress = (PCIHeader0 *)ctx->Device->Header; + uint32_t PCIBAR0 = PCIBaseAddress->BAR0; + uint8_t Type = PCIBAR0 & 1; + if (Type == 1) + { + Log("Device %x:%x.%d BAR0 is I/O.", + PCIBaseAddress->Header.VendorID, + PCIBaseAddress->Header.DeviceID, + PCIBaseAddress->Header.ProgIF); + continue; + } + + Found = true; + ctx = (PCIArray *)ctx->Next; + } + + if (!Found) + { + Log("No valid HDA device found."); + return -EINVAL; + } + return 0; +} + +EXTERNC int cxx_Initialize() +{ + PCIArray *ctx = Devices; + size_t Count = 0; + while (ctx != nullptr) + { + if (Count > sizeof(Drivers) / sizeof(HDADevice *)) + break; + + PCIHeader0 *PCIBaseAddress = (PCIHeader0 *)ctx->Device->Header; + uint32_t PCIBAR0 = PCIBaseAddress->BAR0; + uint8_t Type = PCIBAR0 & 1; + if (Type == 1) + { + Log("Device %x:%x.%d BAR0 is I/O.", + PCIBaseAddress->Header.VendorID, + PCIBaseAddress->Header.DeviceID, + PCIBaseAddress->Header.ProgIF); + continue; + } + + InitializePCI(ctx->Device); + + Drivers[Count] = new HDADevice((PCIHeader0 *)ctx->Device->Header); + + if (Drivers[Count]->IsInitialized()) + { + dev_t ret = RegisterAudioDevice(ddt_Audio, + drvOpen, drvClose, + drvRead, drvWrite, + drvIoctl); + AudioID[Count] = ret; + Count++; + } + ctx = (PCIArray *)ctx->Next; + } + + if (Count == 0) + { + Log("No valid HDA device found."); + return -EINVAL; + } + + return 0; +} + +EXTERNC int cxx_Finalize() +{ + PCIArray *ctx = Devices; + size_t Count = 0; + while (ctx != nullptr) + { + if (Count++ > sizeof(Drivers) / sizeof(HDADevice *)) + break; + + PCIHeader0 *PCIBaseAddress = (PCIHeader0 *)ctx->Device->Header; + uint32_t PCIBAR0 = PCIBaseAddress->BAR0; + uint8_t Type = PCIBAR0 & 1; + if (Type == 1) + { + Log("Device %x:%x.%d BAR0 is I/O.", + PCIBaseAddress->Header.VendorID, + PCIBaseAddress->Header.DeviceID, + PCIBaseAddress->Header.ProgIF); + continue; + } + + delete Drivers[Count++]; + ctx = (PCIArray *)ctx->Next; + } + + return 0; +} diff --git a/audio/hda/hda.hpp b/audio/hda/hda.hpp new file mode 100644 index 0000000..d1587c5 --- /dev/null +++ b/audio/hda/hda.hpp @@ -0,0 +1,635 @@ +/* + 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 . +*/ + +#pragma once + +#include + +#ifdef __cplusplus + +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)); +#endif + +EXTERNC int cxx_Panic(); +EXTERNC int cxx_Probe(); +EXTERNC int cxx_Initialize(); +EXTERNC int cxx_Finalize(); diff --git a/audio/hda/main.c b/audio/hda/main.c new file mode 100644 index 0000000..771e0f8 --- /dev/null +++ b/audio/hda/main.c @@ -0,0 +1,31 @@ +/* + 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 "hda.hpp" + +int DriverEntry() { return cxx_Initialize(); } +int DriverFinal() { return cxx_Finalize(); } +int DriverPanic() { return cxx_Panic(); } +int DriverProbe() { return cxx_Probe(); } + +DriverInfo("hda", + "Intel High Definition Audio Driver", + "EnderIce2", + "0.1", + "GPLv3"); diff --git a/driver.template b/driver.template new file mode 100644 index 0000000..91b4354 --- /dev/null +++ b/driver.template @@ -0,0 +1,50 @@ +/* + 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 + +void InterruptHandler(TrapFrame *Frame) +{ +} + +int DriverEntry() +{ + return 0; +} + +int DriverFinal() +{ + return 0; +} + +int DriverPanic() +{ + return 0; +} + +int DriverProbe() +{ + return 0; +} + +DriverInfo("name", + "description", + "author", + "version", + "license"); diff --git a/include/aip.h b/include/aip.h new file mode 100644 index 0000000..0a070b5 --- /dev/null +++ b/include/aip.h @@ -0,0 +1,123 @@ +/* + 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 . +*/ + +#ifndef __FENNIX_API_AIP_H__ +#define __FENNIX_API_AIP_H__ + +#include +#include +#include + +#define PIC1_CMD 0x20 +#define PIC1_DATA (PIC1_CMD + 1) +#define PIC2_CMD 0xA0 +#define PIC2_DATA (PIC2_CMD + 1) +#define _PIC_EOI 0x20 + +#define PS2_DATA 0x60 +#define PS2_STATUS 0x64 +#define PS2_CMD PS2_STATUS +#define PS2_ACK 0xFA +#define PS2_TEST_PASSED 0x55 +#define PS2_TEST_FAILED 0xFC + +#define PS2_CMD_READ_CONFIG 0x20 +#define PS2_CMD_READ_CONFIG_N(n) (PS2_CMD_READ_CONFIG + n) +#define PS2_CMD_WRITE_CONFIG 0x60 +#define PS2_CMD_WRITE_CONFIG_N(n) (PS2_CMD_WRITE_CONFIG + n) +#define PS2_CMD_DISABLE_PORT_2 0xA7 +#define PS2_CMD_ENABLE_PORT_2 0xA8 +#define PS2_CMD_TEST_PORT_2 0xA9 +#define PS2_CMD_TEST_CONTROLLER 0xAA +#define PS2_CMD_TEST_PORT_1 0xAB +#define PS2_CMD_DIAGNOSTIC_DUMP 0xAC +#define PS2_CMD_DISABLE_PORT_1 0xAD +#define PS2_CMD_ENABLE_PORT_1 0xAE +#define PS2_CMD_READ_INPUT_PORT 0xC0 +#define PS2_CMD_COPY_INPUT_0_3_TO_4_7_STATUS 0xC1 +#define PS2_CMD_COPY_INPUT_4_7_TO_4_7_STATUS 0xC2 +#define PS2_CMD_READ_OUTPUT_PORT 0xD0 +#define PS2_CMD_WRITE_NEXT_BYTE_TO_OUTPUT_PORT 0xD1 +#define PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_1_OUTPUT 0xD2 +#define PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_OUTPUT 0xD3 +#define PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT 0xD4 +#define PS2_CMD_PULSE_OUTPUT_LINE(n) (0xF0 + n) + +typedef union +{ + struct + { + uint8_t OutputBufferFull : 1; + uint8_t InputBufferFull : 1; + uint8_t SystemFlag : 1; + uint8_t CommandData : 1; + uint8_t Unknown1 : 1; + uint8_t Unknown2 : 1; + uint8_t TimeoutError : 1; + uint8_t ParityError : 1; + }; + uint8_t Raw; +} PS2_STATUSES; + +typedef union +{ + struct + { + uint8_t Port1Interrupt : 1; + uint8_t Port2Interrupt : 1; + uint8_t SystemFlag : 1; + uint8_t Zero0 : 1; + uint8_t Port1Clock : 1; + uint8_t Port2Clock : 1; + uint8_t Port1Translation : 1; + uint8_t Zero1 : 1; + }; + uint8_t Raw; +} PS2_CONFIGURATION; + +typedef union +{ + struct + { + uint8_t SystemReset : 1; + uint8_t A20Gate : 1; + uint8_t Port2Clock : 1; + uint8_t Port2Data : 1; + uint8_t Port1OutputBufferFull : 1; + uint8_t Port2OutputBufferFull : 1; + uint8_t Port1InputBufferFull : 1; + uint8_t Port2InputBufferFull : 1; + }; + uint8_t Raw; +} PS2_OUTPUT_PORT; + +void PIC_EOI(uint8_t IRQ); +void IRQ_MASK(uint8_t IRQ); +void IRQ_UNMASK(uint8_t IRQ); +void PS2Wait(const bool Output); +void PS2WriteCommand(uint8_t Command); +void PS2WriteData(uint8_t Data); +uint8_t PS2ReadData(); +uint8_t PS2ReadStatus(); +uint8_t PS2ReadAfterACK(); +void PS2ClearOutputBuffer(); +int PS2ACKTimeout(); + +#define WaitOutput PS2Wait(true) +#define WaitInput PS2Wait(false) + +#endif // !__FENNIX_API_AIP_H__ diff --git a/include/aip/kbd.h b/include/aip/kbd.h new file mode 100644 index 0000000..c174a3c --- /dev/null +++ b/include/aip/kbd.h @@ -0,0 +1,93 @@ +/* + 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 . +*/ + +#ifndef __FENNIX_API_AIP_KBD_H__ +#define __FENNIX_API_AIP_KBD_H__ + +#include + +#define PS2_KBD_CMD_SET_LEDS 0xED +#define PS2_KBD_CMD_ECHO 0xEE +#define PS2_KBD_CMD_SCAN_CODE_SET 0xF0 +#define PS2_KBD_CMD_IDENTIFY 0xF2 +#define PS2_KBD_CMD_TYPEMATIC 0xF3 +#define PS2_KBD_CMD_ENABLE_SCANNING 0xF4 +#define PS2_KBD_CMD_DISABLE_SCANNING 0xF5 +#define PS2_KBD_CMD_DEFAULTS 0xF6 +#define PS2_KBD_CMD_ALL_TYPEMATIC 0xF7 +#define PS2_KBD_CMD_ALL_MAKE_RELEASE 0xF8 +#define PS2_KBD_CMD_ALL_MAKE 0xF9 +#define PS2_KBD_CMD_ALL_TYPEMATIC_MAKE_RELEASE 0xFA +#define PS2_KBD_CMD_SPECIFIC_TYPEMATIC 0xFB +#define PS2_KBD_CMD_SPECIFIC_MAKE_RELEASE 0xFC +#define PS2_KBD_CMD_SPECIFIC_MAKE 0xFD +#define PS2_KBD_CMD_RESEND 0xFE +#define PS2_KBD_CMD_RESET 0xFF + +#define PS2_KBD_RESP_ACK 0xFA +#define PS2_KBD_RESP_ECHO 0xEE +#define PS2_KBD_RESP_RESEND 0xFE +#define PS2_KBD_RESP_TEST_PASSED 0xAA +#define PS2_KBD_RESP_TEST_FAILED 0xFC +#define PS2_KBD_RESP_TEST_FAILED_2 0xFD + +typedef enum +{ + PS2_KBD_LED_SCROLL_LOCK = 1, + PS2_KBD_LED_NUM_LOCK = 2, + PS2_KBD_LED_CAPS_LOCK = 4 +} PS2_KBD_LEDS; + +typedef enum +{ + PS2_KBD_SCAN_CODE_GET_CURRENT = 0, + PS2_KBD_SCAN_CODE_SET_1 = 1, + PS2_KBD_SCAN_CODE_SET_2 = 2, + PS2_KBD_SCAN_CODE_SET_3 = 3, + + PS2_KBD_SC_SET_1 = 0x43, + PS2_KBD_SC_SET_2 = 0x41, + PS2_KBD_SC_SET_3 = 0x3F +} PS2_KBD_SCAN_CODE_SET; + +typedef union +{ + struct + { + /** + * 00000b - 30Hz + * 11111b - 2Hz + */ + uint8_t RepeatRate : 5; + + /** + * 00b - 250ms + * 01b - 500ms + * 10b - 750ms + * 11b - 1000ms + */ + uint8_t Delay : 2; + + /** + * Must be zero + */ + uint8_t Zero : 1; + }; + uint8_t Raw; +} PS2_KBD_TYPEMATIC; + +#endif // !__FENNIX_API_AIP_KBD_H__ diff --git a/include/aip/mouse.h b/include/aip/mouse.h new file mode 100644 index 0000000..5ebed16 --- /dev/null +++ b/include/aip/mouse.h @@ -0,0 +1,89 @@ +/* + 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 . +*/ + +#ifndef __FENNIX_API_AIP_MOUSE_H__ +#define __FENNIX_API_AIP_MOUSE_H__ + +#include + +#define PS2_MOUSE_CMD_SET_SCALING_1_1 0xE6 +#define PS2_MOUSE_CMD_SET_SCALING_2_1 0xE7 +#define PS2_MOUSE_CMD_SET_RESOLUTION 0xE8 +#define PS2_MOUSE_CMD_GET_STATUS 0xE9 +#define PS2_MOUSE_CMD_SET_STREAM_MODE 0xEA +#define PS2_MOUSE_CMD_READ_DATA 0xEB +#define PS2_MOUSE_CMD_RESET_WRAP_MODE 0xEC +#define PS2_MOUSE_CMD_SET_WRAP_MODE 0xEE +#define PS2_MOUSE_CMD_SET_REMOTE_MODE 0xF0 +#define PS2_MOUSE_CMD_READ_ID 0xF2 +/** Values: 10, 20, 40, 60, 80, 100, 200 */ +#define PS2_MOUSE_CMD_SET_SAMPLE_RATE 0xF3 +#define PS2_MOUSE_CMD_ENABLE_DATA_REPORTING 0xF4 +#define PS2_MOUSE_CMD_DISABLE_DATA_REPORTING 0xF5 +#define PS2_MOUSE_CMD_SET_DEFAULTS 0xF6 +#define PS2_MOUSE_CMD_RESEND 0xFE +#define PS2_MOUSE_CMD_RESET 0xFF + +#define PS2_MOUSE_RESP_ACK 0xFA +#define PS2_MOUSE_RESP_RESEND 0xFE +#define PS2_MOUSE_RESP_TEST_PASSED 0xAA +#define PS2_MOUSE_RESP_TEST_FAILED 0xFC + +typedef enum +{ + PS2_MOUSE_RES_1 = 0, + PS2_MOUSE_RES_2 = 1, + PS2_MOUSE_RES_4 = 2, + PS2_MOUSE_RES_8 = 3 +} PS2_MOUSE_RESOLUTION; + +typedef struct +{ + union + { + struct + { + uint8_t LeftButton : 1; + uint8_t RightButton : 1; + uint8_t MiddleButton : 1; + uint8_t Always1 : 1; + uint8_t XSign : 1; + uint8_t YSign : 1; + uint8_t XOverflow : 1; + uint8_t YOverflow : 1; + } __attribute__((packed)); + uint8_t Raw; + } Base; + + uint8_t XMovement; + uint8_t YMovement; + + union + { + struct + { + uint8_t Z : 4; + uint8_t Button4 : 1; + uint8_t Button5 : 1; + uint8_t Always0 : 1; + uint8_t Always0_2 : 1; + } __attribute__((packed)); + uint8_t Raw; + } ZMovement; +} PS2_MOUSE_PACKET; + +#endif // !__FENNIX_API_AIP_MOUSE_H__ diff --git a/include/audio.h b/include/audio.h new file mode 100644 index 0000000..73183b0 --- /dev/null +++ b/include/audio.h @@ -0,0 +1,45 @@ +/* + 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 . +*/ + +#ifndef __FENNIX_API_AUDIO_H__ +#define __FENNIX_API_AUDIO_H__ + +#include +#include + +typedef int (*drvOpen_t)(dev_t, dev_t, int, mode_t); +typedef int (*drvClose_t)(dev_t, dev_t); +typedef size_t (*drvRead_t)(dev_t, dev_t, uint8_t *, size_t, off_t); +typedef size_t (*drvWrite_t)(dev_t, dev_t, uint8_t *, size_t, off_t); +typedef int (*drvIoctl_t)(dev_t, dev_t, unsigned long, void *); + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + + dev_t RegisterAudioDevice(DeviceDriverType Type, + drvOpen_t Open, drvClose_t Close, + drvRead_t Read, drvWrite_t Write, drvIoctl_t Ioctl); + + int UnregisterAudioDevice(dev_t DeviceID, DeviceDriverType Type); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // !__FENNIX_API_AUDIO_H__ diff --git a/include/base.h b/include/base.h new file mode 100644 index 0000000..8806d2a --- /dev/null +++ b/include/base.h @@ -0,0 +1,277 @@ +/* + 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 . +*/ + +#ifndef __FENNIX_API_BASE_H__ +#define __FENNIX_API_BASE_H__ + +#include + +#define PAGE_SIZE 0x1000 + +typedef int CriticalState; + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + + /** + * @brief Allocate an amount of memory pages + * @param Pages The amount of pages to allocate + * @return A pointer to the allocated memory, NULL if failed + */ + void *AllocateMemory(size_t Pages); + + /** + * @brief Free an amount of memory pages + * @param Pointer The pointer to the memory to free + * @param Pages The amount of pages to free + * + * @note If the pointer is invalid, the error is ignored and the + * function returns + */ + void FreeMemory(void *Pointer, size_t Pages); + + /** + * @brief Append a flag to a page + * @param Address The address of the page + * @param Flag The flag to append + */ + void AppendMapFlag(void *Address, PageMapFlags Flag); + + /** + * @brief Remove a flag from a page + * @param Address The address of the page + * @param Flag The flag to remove + */ + void RemoveMapFlag(void *Address, PageMapFlags Flag); + + /** + * @brief Map pages + * @param PhysicalAddress The physical address to map + * @param VirtualAddress The virtual address to map to + * @param Pages The amount of pages to map + * @param Flags The flags to map the pages with + */ + void MapPages(void *PhysicalAddress, void *VirtualAddress, size_t Pages, uint32_t Flags); + + /** + * @brief Unmap pages + * @param VirtualAddress The virtual address to unmap + * @param Pages The amount of pages to unmap + */ + void UnmapPages(void *VirtualAddress, size_t Pages); + + /** + * @brief Print to the screen + * @param Format The format string + * @param ... The arguments to the format string + * + * @note The newline character is automatically appended + */ + void KPrint(const char *Format, ...); + + /** + * @brief Print to the kernel logger + * @param Format The format string + * @param ... The arguments to the format string + */ + void Log(const char *Format, ...); + + /** + * @brief Register an interrupt handler + * @param IRQ The IRQ to register the handler for (IRQ0 != 0x32 but 0) + * @param Handler Function pointer to the handler + * @return 0 on success, errno on failure + */ + int RegisterInterruptHandler(uint8_t IRQ, void *Handler); + + /** + * @brief Override an interrupt handler + * + * This function will check all registered handlers (by the drivers) + * for the given IRQ and remove them. Then it will register the new + * handler. + * + * @param IRQ The IRQ to override the handler for (IRQ0 != 0x32 but 0) + * @param Handler Function pointer to the handler + * @return 0 on success, errno on failure + */ + int OverrideInterruptHandler(uint8_t IRQ, void *Handler); + + /** + * @brief Unregister an interrupt handler + * @param IRQ The IRQ to unregister the handler for (IRQ0 != 0x32 but 0) + * @param Handler Function pointer to the handler + * @return 0 on success, errno on failure + */ + int UnregisterInterruptHandler(uint8_t IRQ, void *Handler); + + /** + * @brief Unregister all interrupt handlers for a given handler + * @param Handler Function pointer to the handler + * @return 0 on success, errno on failure + */ + int UnregisterAllInterruptHandlers(void *Handler); + + /** + * @brief Copy memory + * @param Destination The destination + * @param Source The source + * @param Length The length of the memory to copy + * @return The destination + */ + void *MemoryCopy(void *Destination, const void *Source, size_t Length); + + /** + * @brief Set memory + * @param Destination The destination + * @param Value The value to set + * @param Length The length of the memory to set + * @return The destination + */ + void *MemorySet(void *Destination, int Value, size_t Length); + + /** + * @brief Move memory + * @param Destination The destination + * @param Source The source + * @param Length The length of the memory to move + * @return The destination + */ + void *MemoryMove(void *Destination, const void *Source, size_t Length); + + /** + * @brief String length + * @param String The string + * @return The length of the string + */ + size_t StringLength(const char String[]); + + char *strstr(const char *Haystack, const char *Needle); + + /** + * @brief Create a kernel process + * @param Name The name of the process + * @return The PID of the process, this function never fails + */ + pid_t CreateKernelProcess(const char *Name); + + /** + * @brief Create a kernel thread + * @param pId The PID of the process to create the thread in + * @param Name The name of the thread + * @param EntryPoint The entry point of the thread + * @param Argument The argument to pass to the thread (rdi) + * @return The TID of the thread, this function never fails + */ + pid_t CreateKernelThread(pid_t pId, const char *Name, void *EntryPoint, + void *Argument); + + /** + * @brief Kill a process + * @param pId The PID of the process to kill + * @param ExitCode The exit code of the process + * @return 0 on success, errno on failure + */ + int KillProcess(pid_t pId, int ExitCode); + + /** + * @brief Kill a thread + * @param tId The TID of the thread to kill + * @param ExitCode The exit code of the thread + * @return 0 on success, errno on failure + */ + int KillThread(pid_t tId, int ExitCode); + + /** + * @brief Yield the current thread + * + * This function will yield the current thread to the scheduler. + */ + void Yield(); + + /** + * @brief Sleep for a given amount of milliseconds + * @param Milliseconds The amount of milliseconds to sleep + */ + void Sleep(uint64_t Milliseconds); + + /** + * @brief Enter a critical section + * @return The previous interrupt state + * + * This function will disable interrupts and return the previous + * interrupt state. + */ + CriticalState EnterCriticalSection(); + + /** + * @brief Leave a critical section + * @param PreviousState The previous interrupt state + * + * This function will restore the previous interrupt state. + */ + void LeaveCriticalSection(CriticalState PreviousState); + + /** @copydoc EnterCriticalSection */ +#define ECS CriticalState __ECS = EnterCriticalSection() + + /** @copydoc LeaveCriticalSection */ +#define LCS LeaveCriticalSection(__ECS) + +#define DriverInfo(Name, Description, Author, Version, License) \ + void __IdentifyDriver( \ + dev_t ID, \ + int (*GetDriverInfo)(dev_t, const char *, const char *, \ + const char *, const char *, const char *)) \ + { \ + GetDriverInfo(ID, Name, Description, Author, Version, License); \ + } + +#ifdef DEBUG +#define DebugLog(m, ...) Log(m, ##__VA_ARGS__) +#else +#define DebugLog(m, ...) +#endif // DEBUG + +#ifdef __cplusplus +} +#endif // __cplusplus + +#ifdef __cplusplus +namespace std +{ + typedef decltype(sizeof(0)) size_t; +} + +void *operator new(std::size_t Size); +void *operator new[](std::size_t Size); +void operator delete(void *Pointer); +void operator delete[](void *Pointer); +void operator delete(void *Pointer, std::size_t Size); +void operator delete[](void *Pointer, std::size_t Size); + +#endif // __cplusplus + +#define memcpy(dest, src, len) MemoryCopy(dest, src, len) +#define memset(dest, val, len) MemorySet(dest, val, len) +#define memmove(dest, src, len) MemoryMove(dest, src, len) + +#define strlen(str) StringLength(str) + +#endif // !__FENNIX_API_BASE_H__ diff --git a/include/block.h b/include/block.h new file mode 100644 index 0000000..e87fa2d --- /dev/null +++ b/include/block.h @@ -0,0 +1,45 @@ +/* + 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 . +*/ + +#ifndef __FENNIX_API_BLOCK_H__ +#define __FENNIX_API_BLOCK_H__ + +#include +#include + +typedef int (*drvOpen_t)(dev_t, dev_t, int, mode_t); +typedef int (*drvClose_t)(dev_t, dev_t); +typedef size_t (*drvRead_t)(dev_t, dev_t, uint8_t *, size_t, off_t); +typedef size_t (*drvWrite_t)(dev_t, dev_t, uint8_t *, size_t, off_t); +typedef int (*drvIoctl_t)(dev_t, dev_t, unsigned long, void *); + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + + dev_t RegisterBlockDevice(DeviceDriverType Type, + drvOpen_t Open, drvClose_t Close, + drvRead_t Read, drvWrite_t Write, drvIoctl_t Ioctl); + + int UnregisterBlockDevice(dev_t DeviceID, DeviceDriverType Type); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // !__FENNIX_API_BLOCK_H__ diff --git a/include/driver.h b/include/driver.h new file mode 100644 index 0000000..b072c0c --- /dev/null +++ b/include/driver.h @@ -0,0 +1,326 @@ +/* + 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 . +*/ + +#ifndef __FENNIX_API_DRIVER_FUNCTIONS_H__ +#define __FENNIX_API_DRIVER_FUNCTIONS_H__ + +#include + +typedef enum +{ + _drf_Entry, + _drf_Final, + _drf_Panic, + _drf_Probe, +} __driverRegFunc; + +typedef union +{ + struct + { + uint8_t LeftButton : 1; + uint8_t RightButton : 1; + uint8_t MiddleButton : 1; + uint8_t Button4 : 1; + uint8_t Button5 : 1; + uint8_t Button6 : 1; + uint8_t Button7 : 1; + uint8_t Button8 : 1; + }; + uint8_t Value; +} __MouseButtons; + +typedef struct +{ + /* PCIDevice */ void *Device; + /* __PCIArray */ void *Next; +} __PCIArray; + +/* ========================================== */ + +#define PCI_END 0x0000 +#define KEY_NULL 0x00 + +typedef enum +{ + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_0, + + KEY_Q, + KEY_W, + KEY_E, + KEY_R, + KEY_T, + KEY_Y, + KEY_U, + KEY_I, + KEY_O, + KEY_P, + KEY_A, + KEY_S, + KEY_D, + KEY_F, + KEY_G, + KEY_H, + KEY_J, + KEY_K, + KEY_L, + KEY_Z, + KEY_X, + KEY_C, + KEY_V, + KEY_B, + KEY_N, + KEY_M, + + KEY_F1, + KEY_F2, + KEY_F3, + KEY_F4, + KEY_F5, + KEY_F6, + KEY_F7, + KEY_F8, + KEY_F9, + KEY_F10, + KEY_F11, + KEY_F12, + + 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, + KEYPAD_RETURN, + KEYPAD_ASTERISK, + KEYPAD_SLASH, + + KEY_LEFT_CTRL, + KEY_RIGHT_CTRL, + KEY_LEFT_SHIFT, + KEY_RIGHT_SHIFT, + KEY_LEFT_ALT, + KEY_RIGHT_ALT, + KEY_ESCAPE, + KEY_MINUS, + KEY_EQUAL, + KEY_BACKSPACE, + KEY_TAB, + KEY_LEFT_BRACKET, + KEY_RIGHT_BRACKET, + KEY_RETURN, + KEY_SEMICOLON, + KEY_APOSTROPHE, + KEY_BACK_TICK, + KEY_BACKSLASH, + KEY_COMMA, + KEY_PERIOD, + KEY_SLASH, + KEY_SPACE, + KEY_CAPS_LOCK, + KEY_NUM_LOCK, + KEY_SCROLL_LOCK, + KEY_PRINT_SCREEN, + + KEY_HOME, + KEY_UP_ARROW, + KEY_LEFT_ARROW, + KEY_RIGHT_ARROW, + KEY_DOWN_ARROW, + KEY_PAGE_UP, + KEY_PAGE_DOWN, + KEY_END, + KEY_INSERT, + KEY_DELETE, + KEY_LEFT_GUI, + KEY_RIGHT_GUI, + KEY_APPS, + + KEY_MULTIMEDIA_PREV_TRACK, + KEY_MULTIMEDIA_NEXT_TRACK, + KEY_MULTIMEDIA_MUTE, + KEY_MULTIMEDIA_CALCULATOR, + KEY_MULTIMEDIA_PLAY, + KEY_MULTIMEDIA_STOP, + KEY_MULTIMEDIA_VOL_DOWN, + KEY_MULTIMEDIA_VOL_UP, + KEY_MULTIMEDIA_WWW_HOME, + KEY_MULTIMEDIA_WWW_SEARCH, + KEY_MULTIMEDIA_WWW_FAVORITES, + KEY_MULTIMEDIA_WWW_REFRESH, + KEY_MULTIMEDIA_WWW_STOP, + KEY_MULTIMEDIA_WWW_FORWARD, + KEY_MULTIMEDIA_WWW_BACK, + KEY_MULTIMEDIA_MY_COMPUTER, + KEY_MULTIMEDIA_EMAIL, + KEY_MULTIMEDIA_MEDIA_SELECT, + + KEY_ACPI_POWER, + KEY_ACPI_SLEEP, + KEY_ACPI_WAKE, + + KEY_PRESSED = 0x80, +} KeyScanCodes; + +typedef enum +{ + ddt_Keyboard, + ddt_Mouse, + ddt_Joystick, + ddt_Gamepad, + ddt_Touchpad, + ddt_Touchscreen, + + ddt_SATA, + ddt_ATA, + ddt_NVMe, + + ddt_Audio, + + ddt_Network, +} DeviceDriverType; + +typedef enum +{ + IOCTL_AUDIO_GET_VOLUME, + IOCTL_AUDIO_SET_VOLUME, + + IOCTL_AUDIO_GET_MUTE, + IOCTL_AUDIO_SET_MUTE, + + IOCTL_AUDIO_GET_SAMPLE_RATE, + IOCTL_AUDIO_SET_SAMPLE_RATE, + + IOCTL_AUDIO_GET_CHANNELS, + IOCTL_AUDIO_SET_CHANNELS, +} AudioIoctl; + +typedef enum +{ + IOCTL_NET_GET_MAC, +} NetIoctl; + +typedef enum +{ + MAP_PRESENT = 1 << 0, + MAP_WRITE = 1 << 1, + MAP_USER = 1 << 2, + MAP_WRITE_THROUGH = 1 << 3, + MAP_CACHE_DISABLE = 1 << 4, +} PageMapFlags; + +typedef struct +{ + struct + { + uint8_t Major; + uint8_t Minor; + uint8_t Patch; + } APIVersion; + + dev_t MajorID; + uintptr_t Base; + + /* Internal */ + int (*RegisterFunction)(dev_t MajorID, void *Function, __driverRegFunc Type); + int (*GetDriverInfo)(dev_t MajorID, const char *Name, const char *Description, const char *Author, const char *Version, const char *License); + + /* Interrupts */ + int (*RegisterInterruptHandler)(dev_t MajorID, uint8_t IRQ, void *Handler); + int (*OverrideInterruptHandler)(dev_t MajorID, uint8_t IRQ, void *Handler); + int (*UnregisterInterruptHandler)(dev_t MajorID, uint8_t IRQ, void *Handler); + int (*UnregisterAllInterruptHandlers)(dev_t MajorID, void *Handler); + + /* Input */ + dev_t (*RegisterInputDevice)(dev_t MajorID, DeviceDriverType Type); + int (*UnregisterInputDevice)(dev_t MajorID, dev_t MinorID, DeviceDriverType Type); + int (*ReportKeyboardEvent)(dev_t MajorID, dev_t MinorID, uint8_t ScanCode); + int (*ReportRelativeMouseEvent)(dev_t MajorID, dev_t MinorID, __MouseButtons Button, int X, int Y, int8_t Z); + int (*ReportAbsoluteMouseEvent)(dev_t MajorID, dev_t MinorID, __MouseButtons Button, uintptr_t X, uintptr_t Y, int8_t Z); + + /* Storage */ + dev_t (*RegisterBlockDevice)(dev_t MajorID, DeviceDriverType Type, void *Open, void *Close, void *Read, void *Write, void *Ioctl); + int (*UnregisterBlockDevice)(dev_t MajorID, dev_t MinorID, DeviceDriverType Type); + + /* Audio */ + dev_t (*RegisterAudioDevice)(dev_t MajorID, DeviceDriverType Type, void *Open, void *Close, void *Read, void *Write, void *Ioctl); + int (*UnregisterAudioDevice)(dev_t MajorID, dev_t MinorID, DeviceDriverType Type); + + /* Network */ + dev_t (*RegisterNetDevice)(dev_t MajorID, DeviceDriverType Type, void *Open, void *Close, void *Read, void *Write, void *Ioctl); + int (*UnregisterNetDevice)(dev_t MajorID, dev_t MinorID, DeviceDriverType Type); + int (*ReportNetworkPacket)(dev_t MajorID, dev_t MinorID, void *Buffer, size_t Size); + + /* Logging */ + void (*KPrint)(dev_t MajorID, const char *Format, va_list args); + void (*KernelLog)(dev_t MajorID, const char *Format, va_list args); + + /* Memory */ + void *(*RequestPages)(dev_t MajorID, size_t Pages); + void (*FreePages)(dev_t MajorID, void *Pointer, size_t Pages); + + /* Mapping */ + void (*AppendMapFlag)(dev_t MajorID, void *Address, PageMapFlags Flag); + void (*RemoveMapFlag)(dev_t MajorID, void *Address, PageMapFlags Flag); + void (*MapPages)(dev_t MajorID, void *PhysicalAddress, void *VirtualAddress, size_t Pages, uint32_t Flags); + void (*UnmapPages)(dev_t MajorID, void *VirtualAddress, size_t Pages); + + /* Scheduling */ + pid_t (*CreateKernelProcess)(dev_t MajorID, const char *Name); + pid_t (*CreateKernelThread)(dev_t MajorID, pid_t pId, const char *Name, void *EntryPoint, void *Argument); + int (*KillProcess)(dev_t MajorID, pid_t pId, int ExitCode); + int (*KillThread)(dev_t MajorID, pid_t tId, int ExitCode); + void (*Yield)(dev_t MajorID); + void (*Sleep)(dev_t MajorID, uint64_t Milliseconds); + + /* PCI */ + __PCIArray *(*GetPCIDevices)(dev_t MajorID, uint16_t Vendors[], uint16_t Devices[]); + void (*InitializePCI)(dev_t MajorID, void *Header); + uint32_t (*GetBAR)(dev_t MajorID, uint8_t Index, void *Header); + + /* Kernel std API */ + void *(*memcpy)(dev_t MajorID, void *Destination, const void *Source, size_t Length); + void *(*memset)(dev_t MajorID, void *Destination, int Value, size_t Length); + void *(*memmove)(dev_t MajorID, void *Destination, const void *Source, size_t Length); + int (*memcmp)(dev_t MajorID, const void *Left, const void *Right, size_t Length); + size_t (*strlen)(dev_t MajorID, const char *String); + char *(*strcpy)(dev_t MajorID, char *Destination, const char *Source); + char *(*strcat)(dev_t MajorID, char *Destination, const char *Source); + int (*strcmp)(dev_t MajorID, const char *Left, const char *Right); + int (*strncmp)(dev_t MajorID, const char *Left, const char *Right, size_t Length); + char *(*strchr)(dev_t MajorID, const char *String, int Character); + char *(*strrchr)(dev_t MajorID, const char *String, int Character); + char *(*strstr)(dev_t MajorID, const char *Haystack, const char *Needle); +} __driverAPI; + +#endif // !__FENNIX_API_DRIVER_FUNCTIONS_H__ diff --git a/include/errno.h b/include/errno.h new file mode 100644 index 0000000..159afeb --- /dev/null +++ b/include/errno.h @@ -0,0 +1,408 @@ +/* + 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 . +*/ + +#ifndef _ERRNO_H +#define _ERRNO_H + +/** Operation not permitted */ +#define EPERM 1 + +/** No such file or directory */ +#define ENOENT 2 + +/** No such process */ +#define ESRCH 3 + +/** Interrupted system call */ +#define EINTR 4 + +/** I/O error */ +#define EIO 5 + +/** No such device or address */ +#define ENXIO 6 + +/** Argument list too long */ +#define E2BIG 7 + +/** Exec format error */ +#define ENOEXEC 8 + +/** Bad file number */ +#define EBADF 9 + +/** No child processes */ +#define ECHILD 10 + +/** Try again */ +#define EAGAIN 11 + +/** Out of memory */ +#define ENOMEM 12 + +/** Permission denied */ +#define EACCES 13 + +/** Bad address */ +#define EFAULT 14 + +/** Block device required */ +#define ENOTBLK 15 + +/** Device or resource busy */ +#define EBUSY 16 + +/** File exists */ +#define EEXIST 17 + +/** Cross-device link */ +#define EXDEV 18 + +/** No such device */ +#define ENODEV 19 + +/** Not a directory */ +#define ENOTDIR 20 + +/** Is a directory */ +#define EISDIR 21 + +/** Invalid argument */ +#define EINVAL 22 + +/** File table overflow */ +#define ENFILE 23 + +/** Too many open files */ +#define EMFILE 24 + +/** Not a typewriter */ +#define ENOTTY 25 + +/** Text file busy */ +#define ETXTBSY 26 + +/** File too large */ +#define EFBIG 27 + +/** No space left on device */ +#define ENOSPC 28 + +/** Illegal seek */ +#define ESPIPE 29 + +/** Read-only file system */ +#define EROFS 30 + +/** Too many links */ +#define EMLINK 31 + +/** Broken pipe */ +#define EPIPE 32 + +/** Math argument out of domain of func */ +#define EDOM 33 + +/** Math result not representable */ +#define ERANGE 34 + +/** Resource deadlock would occur */ +#define EDEADLK 35 + +/** File name too long */ +#define ENAMETOOLONG 36 + +/** No record locks available */ +#define ENOLCK 37 + +/** Function not implemented */ +#define ENOSYS 38 + +/** Directory not empty */ +#define ENOTEMPTY 39 + +/** Too many symbolic links encountered */ +#define ELOOP 40 + +/** No message of desired type */ +#define ENOMSG 42 + +/** Identifier removed */ +#define EIDRM 43 + +/** Channel number out of range */ +#define ECHRNG 44 + +/** Level 2 not synchronized */ +#define EL2NSYNC 45 + +/** Level 3 halted */ +#define EL3HLT 46 + +/** Level 3 reset */ +#define EL3RST 47 + +/** Link number out of range */ +#define ELNRNG 48 + +/** Protocol driver not attached */ +#define EUNATCH 49 + +/** No CSI structure available */ +#define ENOCSI 50 + +/** Level 2 halted */ +#define EL2HLT 51 + +/** Invalid exchange */ +#define EBADE 52 + +/** Invalid request descriptor */ +#define EBADR 53 + +/** Exchange full */ +#define EXFULL 54 + +/** No anode */ +#define ENOANO 55 + +/** Invalid request code */ +#define EBADRQC 56 + +/** Invalid slot */ +#define EBADSLT 57 + +/** Bad font file format */ +#define EBFONT 59 + +/** Device not a stream */ +#define ENOSTR 60 + +/** No data available */ +#define ENODATA 61 + +/** Timer expired */ +#define ETIME 62 + +/** Out of streams resources */ +#define ENOSR 63 + +/** Machine is not on the network */ +#define ENONET 64 + +/** Package not installed */ +#define ENOPKG 65 + +/** Object is remote */ +#define EREMOTE 66 + +/** Link has been severed */ +#define ENOLINK 67 + +/** Advertise error */ +#define EADV 68 + +/** Srmount error */ +#define ESRMNT 69 + +/** Communication error on send */ +#define ECOMM 70 + +/** Protocol error */ +#define EPROTO 71 + +/** Multihop attempted */ +#define EMULTIHOP 72 + +/** RFS specific error */ +#define EDOTDOT 73 + +/** Not a data message */ +#define EBADMSG 74 + +/** Value too large for defined data type */ +#define EOVERFLOW 75 + +/** Name not unique on network */ +#define ENOTUNIQ 76 + +/** File descriptor in bad state */ +#define EBADFD 77 + +/** Remote address changed */ +#define EREMCHG 78 + +/** Can not access a needed shared library */ +#define ELIBACC 79 + +/** Accessing a corrupted shared library */ +#define ELIBBAD 80 + +/** .lib section in a.out corrupted */ +#define ELIBSCN 81 + +/** Attempting to link in too many shared libraries */ +#define ELIBMAX 82 + +/** Cannot exec a shared library directly */ +#define ELIBEXEC 83 + +/** Illegal byte sequence */ +#define EILSEQ 84 + +/** Interrupted system call should be restarted */ +#define ERESTART 85 + +/** Streams pipe error */ +#define ESTRPIPE 86 + +/** Too many users */ +#define EUSERS 87 + +/** Socket operation on non-socket */ +#define ENOTSOCK 88 + +/** Destination address required */ +#define EDESTADDRREQ 89 + +/** Message too long */ +#define EMSGSIZE 90 + +/** Protocol wrong type for socket */ +#define EPROTOTYPE 91 + +/** Protocol not available */ +#define ENOPROTOOPT 92 + +/** Protocol not supported */ +#define EPROTONOSUPPORT 93 + +/** Socket type not supported */ +#define ESOCKTNOSUPPORT 94 + +/** Operation not supported on transport endpoint */ +#define EOPNOTSUPP 95 + +/** Protocol family not supported */ +#define EPFNOSUPPORT 96 + +/** Address family not supported by protocol */ +#define EAFNOSUPPORT 97 + +/** Address already in use */ +#define EADDRINUSE 98 + +/** Cannot assign requested address */ +#define EADDRNOTAVAIL 99 + +/** Network is down */ +#define ENETDOWN 100 + +/** Network is unreachable */ +#define ENETUNREACH 101 + +/** Network dropped connection because of reset */ +#define ENETRESET 102 + +/** Software caused connection abort */ +#define ECONNABORTED 103 + +/** Connection reset by peer */ +#define ECONNRESET 104 + +/** No buffer space available */ +#define ENOBUFS 105 + +/** Transport endpoint is already connected */ +#define EISCONN 106 + +/** Transport endpoint is not connected */ +#define ENOTCONN 107 + +/** Cannot send after transport endpoint shutdown */ +#define ESHUTDOWN 108 + +/** Too many references: cannot splice */ +#define ETOOMANYREFS 109 + +/** Connection timed out */ +#define ETIMEDOUT 110 + +/** Connection refused */ +#define ECONNREFUSED 111 + +/** Host is down */ +#define EHOSTDOWN 112 + +/** No route to host */ +#define EHOSTUNREACH 113 + +/** Operation already in progress */ +#define EALREADY 114 + +/** Operation now in progress */ +#define EINPROGRESS 115 + +/** Stale NFS file handle */ +#define ESTALE 116 + +/** Structure needs cleaning */ +#define EUCLEAN 117 + +/** Not a XENIX named type file */ +#define ENOTNAM 118 + +/** No XENIX semaphores available */ +#define ENAVAIL 119 + +/** Is a named type file */ +#define EISNAM 120 + +/** Remote I/O error */ +#define EREMOTEIO 121 + +/** Quota exceeded */ +#define EDQUOT 122 + +/** No medium found */ +#define ENOMEDIUM 123 + +/** Wrong medium type */ +#define EMEDIUMTYPE 124 + +/** Operation Canceled */ +#define ECANCELED 125 + +/** Required key not available */ +#define ENOKEY 126 + +/** Key has expired */ +#define EKEYEXPIRED 127 + +/** Key has been revoked */ +#define EKEYREVOKED 128 + +/** Key was rejected by service */ +#define EKEYREJECTED 129 + +/** Owner died */ +#define EOWNERDEAD 130 + +/** State not recoverable */ +#define ENOTRECOVERABLE 131 + +#endif // !_ERRNO_H diff --git a/include/input.h b/include/input.h new file mode 100644 index 0000000..4117043 --- /dev/null +++ b/include/input.h @@ -0,0 +1,66 @@ +/* + 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 . +*/ + +#ifndef __FENNIX_API_INPUT_H__ +#define __FENNIX_API_INPUT_H__ + +#include +#include + +typedef struct +{ + union + { + struct + { + uint8_t LeftButton : 1; + uint8_t RightButton : 1; + uint8_t MiddleButton : 1; + uint8_t Button4 : 1; + uint8_t Button5 : 1; + }; + uint8_t Buttons; + }; + + /** + * @note Ignored if is absolute + */ + int X; + + /** + * @note Ignored if is absolute + */ + int Y; + int8_t Z; +} MouseReport; + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + + dev_t RegisterInputDevice(DeviceDriverType Type); + int UnregisterInputDevice(dev_t DeviceID, DeviceDriverType Type); + int ReportKeyboardEvent(dev_t DeviceID, KeyScanCodes ScanCode, uint8_t Pressed); + int ReportRelativeMouseEvent(dev_t DeviceID, MouseReport Report); + int ReportAbsoluteMouseEvent(dev_t DeviceID, MouseReport Report, uintptr_t X, uintptr_t Y); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // !__FENNIX_API_INPUT_H__ diff --git a/include/io.h b/include/io.h index ca17bc7..cb1d9d9 100644 --- a/include/io.h +++ b/include/io.h @@ -1,3 +1,20 @@ +/* + 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 . +*/ + #ifndef __FENNIX_API_IO_H__ #define __FENNIX_API_IO_H__ @@ -9,201 +26,203 @@ extern "C" { #endif - static inline uint8_t inportb(uint16_t Port) - { - uint8_t Result; - __asm__("in %%dx, %%al" - : "=a"(Result) - : "d"(Port)); - return Result; - } - static inline uint16_t inportw(uint16_t Port) - { - uint16_t Result; - __asm__("in %%dx, %%ax" - : "=a"(Result) - : "d"(Port)); - return Result; - } + static inline uint8_t inportb(uint16_t Port) + { + uint8_t Result; + __asm__("in %%dx, %%al" + : "=a"(Result) + : "d"(Port)); + return Result; + } - static inline uint32_t inportl(uint16_t Port) - { - uint32_t Result; - __asm__ volatile("inl %1, %0" - : "=a"(Result) - : "dN"(Port)); - return Result; - } + static inline uint16_t inportw(uint16_t Port) + { + uint16_t Result; + __asm__("in %%dx, %%ax" + : "=a"(Result) + : "d"(Port)); + return Result; + } - static inline void outportb(uint16_t Port, uint8_t Data) - { - __asm__ volatile("out %%al, %%dx" - : - : "a"(Data), "d"(Port)); - } + static inline uint32_t inportl(uint16_t Port) + { + uint32_t Result; + __asm__ volatile("inl %1, %0" + : "=a"(Result) + : "dN"(Port)); + return Result; + } - static inline void outportw(uint16_t Port, uint16_t Data) - { - __asm__ volatile("out %%ax, %%dx" - : - : "a"(Data), "d"(Port)); - } + static inline void outportb(uint16_t Port, uint8_t Data) + { + __asm__ volatile("out %%al, %%dx" + : + : "a"(Data), "d"(Port)); + } - static inline void outportl(uint16_t Port, uint32_t Data) - { - __asm__ volatile("outl %1, %0" - : - : "dN"(Port), "a"(Data)); - } + static inline void outportw(uint16_t Port, uint16_t Data) + { + __asm__ volatile("out %%ax, %%dx" + : + : "a"(Data), "d"(Port)); + } - static inline uint8_t mmioin8(uint64_t Address) - { - __asm__ volatile("" :: - : "memory"); - uint8_t Result = *(volatile uint8_t *)Address; - __asm__ volatile("" :: - : "memory"); - return Result; - } + static inline void outportl(uint16_t Port, uint32_t Data) + { + __asm__ volatile("outl %1, %0" + : + : "dN"(Port), "a"(Data)); + } - static inline uint16_t mmioin16(uint64_t Address) - { - __asm__ volatile("" :: - : "memory"); - uint16_t Result = *(volatile uint16_t *)Address; - __asm__ volatile("" :: - : "memory"); - return Result; - } + static inline uint8_t mmioin8(uint64_t Address) + { + __asm__ volatile("" :: + : "memory"); + uint8_t Result = *(volatile uint8_t *)Address; + __asm__ volatile("" :: + : "memory"); + return Result; + } - static inline uint32_t mmioin32(uint64_t Address) - { - __asm__ volatile("" :: - : "memory"); - uint32_t Result = *(volatile uint32_t *)Address; - __asm__ volatile("" :: - : "memory"); - return Result; - } + static inline uint16_t mmioin16(uint64_t Address) + { + __asm__ volatile("" :: + : "memory"); + uint16_t Result = *(volatile uint16_t *)Address; + __asm__ volatile("" :: + : "memory"); + return Result; + } - static inline uint64_t mmioin64(uint64_t Address) - { - __asm__ volatile("" :: - : "memory"); - uint64_t Result = *(volatile uint64_t *)Address; - __asm__ volatile("" :: - : "memory"); - return Result; - } + static inline uint32_t mmioin32(uint64_t Address) + { + __asm__ volatile("" :: + : "memory"); + uint32_t Result = *(volatile uint32_t *)Address; + __asm__ volatile("" :: + : "memory"); + return Result; + } - static inline void mmioout8(uint64_t Address, uint8_t Data) - { - __asm__ volatile("" :: - : "memory"); - *(volatile uint8_t *)Address = Data; - __asm__ volatile("" :: - : "memory"); - } + static inline uint64_t mmioin64(uint64_t Address) + { + __asm__ volatile("" :: + : "memory"); + uint64_t Result = *(volatile uint64_t *)Address; + __asm__ volatile("" :: + : "memory"); + return Result; + } - static inline void mmioout16(uint64_t Address, uint16_t Data) - { - __asm__ volatile("" :: - : "memory"); - *(volatile uint16_t *)Address = Data; - __asm__ volatile("" :: - : "memory"); - } + static inline void mmioout8(uint64_t Address, uint8_t Data) + { + __asm__ volatile("" :: + : "memory"); + *(volatile uint8_t *)Address = Data; + __asm__ volatile("" :: + : "memory"); + } - static inline void mmioout32(uint64_t Address, uint32_t Data) - { - __asm__ volatile("" :: - : "memory"); - *(volatile uint32_t *)Address = Data; - __asm__ volatile("" :: - : "memory"); - } + static inline void mmioout16(uint64_t Address, uint16_t Data) + { + __asm__ volatile("" :: + : "memory"); + *(volatile uint16_t *)Address = Data; + __asm__ volatile("" :: + : "memory"); + } - static inline void mmioout64(uint64_t Address, uint64_t Data) - { - __asm__ volatile("" :: - : "memory"); - *(volatile uint64_t *)Address = Data; - __asm__ volatile("" :: - : "memory"); - } + static inline void mmioout32(uint64_t Address, uint32_t Data) + { + __asm__ volatile("" :: + : "memory"); + *(volatile uint32_t *)Address = Data; + __asm__ volatile("" :: + : "memory"); + } - static inline void mmoutb(void *Address, uint8_t Value) - { - __asm__ volatile("mov %1, %0" - : "=m"((*(uint8_t *)(Address))) - : "r"(Value) - : "memory"); - } + static inline void mmioout64(uint64_t Address, uint64_t Data) + { + __asm__ volatile("" :: + : "memory"); + *(volatile uint64_t *)Address = Data; + __asm__ volatile("" :: + : "memory"); + } - static inline void mmoutw(void *Address, uint16_t Value) - { - __asm__ volatile("mov %1, %0" - : "=m"((*(uint16_t *)(Address))) - : "r"(Value) - : "memory"); - } + static inline void mmoutb(void *Address, uint8_t Value) + { + __asm__ volatile("mov %1, %0" + : "=m"((*(uint8_t *)(Address))) + : "r"(Value) + : "memory"); + } - static inline void mmoutl(void *Address, uint32_t Value) - { - __asm__ volatile("mov %1, %0" - : "=m"((*(uint32_t *)(Address))) - : "r"(Value) - : "memory"); - } + static inline void mmoutw(void *Address, uint16_t Value) + { + __asm__ volatile("mov %1, %0" + : "=m"((*(uint16_t *)(Address))) + : "r"(Value) + : "memory"); + } - static inline void mmoutq(void *Address, uint64_t Value) - { - __asm__ volatile("mov %1, %0" - : "=m"((*(uint64_t *)(Address))) - : "r"(Value) - : "memory"); - } + static inline void mmoutl(void *Address, uint32_t Value) + { + __asm__ volatile("mov %1, %0" + : "=m"((*(uint32_t *)(Address))) + : "r"(Value) + : "memory"); + } - static inline uint8_t mminb(void *Address) - { - uint8_t Result; - __asm__ volatile("mov %1, %0" - : "=r"(Result) - : "m"((*(uint8_t *)(Address))) - : "memory"); - return Result; - } + static inline void mmoutq(void *Address, uint64_t Value) + { + __asm__ volatile("mov %1, %0" + : "=m"((*(uint64_t *)(Address))) + : "r"(Value) + : "memory"); + } - static inline uint16_t mminw(void *Address) - { - uint16_t Result; - __asm__ volatile("mov %1, %0" - : "=r"(Result) - : "m"((*(uint16_t *)(Address))) - : "memory"); - return Result; - } + static inline uint8_t mminb(void *Address) + { + uint8_t Result; + __asm__ volatile("mov %1, %0" + : "=r"(Result) + : "m"((*(uint8_t *)(Address))) + : "memory"); + return Result; + } - static inline uint32_t mminl(void *Address) - { - uint32_t Result; - __asm__ volatile("mov %1, %0" - : "=r"(Result) - : "m"((*(uint32_t *)(Address))) - : "memory"); - return Result; - } + static inline uint16_t mminw(void *Address) + { + uint16_t Result; + __asm__ volatile("mov %1, %0" + : "=r"(Result) + : "m"((*(uint16_t *)(Address))) + : "memory"); + return Result; + } + + static inline uint32_t mminl(void *Address) + { + uint32_t Result; + __asm__ volatile("mov %1, %0" + : "=r"(Result) + : "m"((*(uint32_t *)(Address))) + : "memory"); + return Result; + } + + static inline uint64_t mminq(void *Address) + { + uint64_t Result; + __asm__ volatile("mov %1, %0" + : "=r"(Result) + : "m"((*(uint64_t *)(Address))) + : "memory"); + return Result; + } - static inline uint64_t mminq(void *Address) - { - uint64_t Result; - __asm__ volatile("mov %1, %0" - : "=r"(Result) - : "m"((*(uint64_t *)(Address))) - : "memory"); - return Result; - } #ifdef __cplusplus } #endif diff --git a/include/net.h b/include/net.h new file mode 100644 index 0000000..42ca0de --- /dev/null +++ b/include/net.h @@ -0,0 +1,47 @@ +/* + 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 . +*/ + +#ifndef __FENNIX_API_AUDIO_H__ +#define __FENNIX_API_AUDIO_H__ + +#include +#include + +typedef int (*drvOpen_t)(dev_t, dev_t, int, mode_t); +typedef int (*drvClose_t)(dev_t, dev_t); +typedef size_t (*drvRead_t)(dev_t, dev_t, uint8_t *, size_t, off_t); +typedef size_t (*drvWrite_t)(dev_t, dev_t, uint8_t *, size_t, off_t); +typedef int (*drvIoctl_t)(dev_t, dev_t, unsigned long, void *); + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + + dev_t RegisterNetDevice(DeviceDriverType Type, + drvOpen_t Open, drvClose_t Close, + drvRead_t Read, drvWrite_t Write, drvIoctl_t Ioctl); + + int UnregisterNetDevice(dev_t DeviceID, DeviceDriverType Type); + + int ReportNetworkPacket(dev_t DeviceID, void *Buffer, size_t Size); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // !__FENNIX_API_AUDIO_H__ diff --git a/include/netools.h b/include/netools.h index 7f92626..3e795fd 100644 --- a/include/netools.h +++ b/include/netools.h @@ -1,3 +1,20 @@ +/* + 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 . +*/ + #ifndef __FENNIX_API_NETWORK_UTILS_H__ #define __FENNIX_API_NETWORK_UTILS_H__ diff --git a/include/pci.h b/include/pci.h index 0751259..a307cf7 100644 --- a/include/pci.h +++ b/include/pci.h @@ -1,3 +1,20 @@ +/* + 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 . +*/ + #ifndef __FENNIX_API_PCI_H__ #define __FENNIX_API_PCI_H__ @@ -6,123 +23,162 @@ /* https://sites.uclouvain.be/SystInfo/usr/include/linux/pci_regs.h.html */ enum PCICommands { - /** @brief Enable response in I/O space */ - PCI_COMMAND_IO = 0x1, - /** @brief Enable response in Memory space */ - PCI_COMMAND_MEMORY = 0x2, - /** @brief Enable bus mastering */ - PCI_COMMAND_MASTER = 0x4, - /** @brief Enable response to special cycles */ - PCI_COMMAND_SPECIAL = 0x8, - /** @brief Use memory write and invalidate */ - PCI_COMMAND_INVALIDATE = 0x10, - /** @brief Enable palette snooping */ - PCI_COMMAND_VGA_PALETTE = 0x20, - /** @brief Enable parity checking */ - PCI_COMMAND_PARITY = 0x40, - /** @brief Enable address/data stepping */ - PCI_COMMAND_WAIT = 0x80, - /** @brief Enable SERR */ - PCI_COMMAND_SERR = 0x100, - /** @brief Enable back-to-back writes */ - PCI_COMMAND_FAST_BACK = 0x200, - /** @brief INTx Emulation Disable */ - PCI_COMMAND_INTX_DISABLE = 0x400 + /** @brief Enable response in I/O space */ + PCI_COMMAND_IO = 0x1, + /** @brief Enable response in Memory space */ + PCI_COMMAND_MEMORY = 0x2, + /** @brief Enable bus mastering */ + PCI_COMMAND_MASTER = 0x4, + /** @brief Enable response to special cycles */ + PCI_COMMAND_SPECIAL = 0x8, + /** @brief Use memory write and invalidate */ + PCI_COMMAND_INVALIDATE = 0x10, + /** @brief Enable palette snooping */ + PCI_COMMAND_VGA_PALETTE = 0x20, + /** @brief Enable parity checking */ + PCI_COMMAND_PARITY = 0x40, + /** @brief Enable address/data stepping */ + PCI_COMMAND_WAIT = 0x80, + /** @brief Enable SERR */ + PCI_COMMAND_SERR = 0x100, + /** @brief Enable back-to-back writes */ + PCI_COMMAND_FAST_BACK = 0x200, + /** @brief INTx Emulation Disable */ + PCI_COMMAND_INTX_DISABLE = 0x400 }; -struct PCIDeviceHeader +typedef struct { - uint16_t VendorID; - uint16_t DeviceID; - uint16_t Command; - uint16_t Status; - uint8_t RevisionID; - uint8_t ProgIF; - uint8_t Subclass; - uint8_t Class; - uint8_t CacheLineSize; - uint8_t LatencyTimer; - uint8_t HeaderType; - uint8_t BIST; -}; + uint16_t VendorID; + uint16_t DeviceID; + uint16_t Command; + uint16_t Status; + uint8_t RevisionID; + uint8_t ProgIF; + uint8_t Subclass; + uint8_t Class; + uint8_t CacheLineSize; + uint8_t LatencyTimer; + uint8_t HeaderType; + uint8_t BIST; +} __attribute__((packed)) PCIDeviceHeader; -struct PCIHeader0 +typedef struct { - PCIDeviceHeader Header; - uint32_t BAR0; - uint32_t BAR1; - uint32_t BAR2; - uint32_t BAR3; - uint32_t BAR4; - uint32_t BAR5; - uint32_t CardbusCISPointer; - uint16_t SubsystemVendorID; - uint16_t SubsystemID; - uint32_t ExpansionROMBaseAddress; - uint8_t CapabilitiesPointer; - uint8_t Reserved0; - uint16_t Reserved1; - uint32_t Reserved2; - uint8_t InterruptLine; - uint8_t InterruptPin; - uint8_t MinGrant; - uint8_t MaxLatency; -}; + PCIDeviceHeader Header; + uint32_t BAR0; + uint32_t BAR1; + uint32_t BAR2; + uint32_t BAR3; + uint32_t BAR4; + uint32_t BAR5; + uint32_t CardbusCISPointer; + uint16_t SubsystemVendorID; + uint16_t SubsystemID; + uint32_t ExpansionROMBaseAddress; + uint8_t CapabilitiesPointer; + uint8_t Reserved0; + uint16_t Reserved1; + uint32_t Reserved2; + uint8_t InterruptLine; + uint8_t InterruptPin; + uint8_t MinGrant; + uint8_t MaxLatency; +} __attribute__((packed)) PCIHeader0; -struct PCIHeader1 +typedef struct { - PCIDeviceHeader Header; - uint32_t BAR0; - uint32_t BAR1; - uint8_t PrimaryBusNumber; - uint8_t SecondaryBusNumber; - uint8_t SubordinateBusNumber; - uint8_t SecondaryLatencyTimer; - uint8_t IOBase; - uint8_t IOLimit; - uint16_t SecondaryStatus; - uint16_t MemoryBase; - uint16_t MemoryLimit; - uint16_t PrefetchableMemoryBase; - uint16_t PrefetchableMemoryLimit; - uint32_t PrefetchableMemoryBaseUpper32; - uint32_t PrefetchableMemoryLimitUpper32; - uint16_t IOBaseUpper16; - uint16_t IOLimitUpper16; - uint8_t CapabilitiesPointer; - uint8_t Reserved0; - uint16_t Reserved1; - uint32_t ExpansionROMBaseAddress; - uint8_t InterruptLine; - uint8_t InterruptPin; - uint16_t BridgeControl; -}; + PCIDeviceHeader Header; + uint32_t BAR0; + uint32_t BAR1; + uint8_t PrimaryBusNumber; + uint8_t SecondaryBusNumber; + uint8_t SubordinateBusNumber; + uint8_t SecondaryLatencyTimer; + uint8_t IOBase; + uint8_t IOLimit; + uint16_t SecondaryStatus; + uint16_t MemoryBase; + uint16_t MemoryLimit; + uint16_t PrefetchableMemoryBase; + uint16_t PrefetchableMemoryLimit; + uint32_t PrefetchableMemoryBaseUpper32; + uint32_t PrefetchableMemoryLimitUpper32; + uint16_t IOBaseUpper16; + uint16_t IOLimitUpper16; + uint8_t CapabilitiesPointer; + uint8_t Reserved0; + uint16_t Reserved1; + uint32_t ExpansionROMBaseAddress; + uint8_t InterruptLine; + uint8_t InterruptPin; + uint16_t BridgeControl; +} __attribute__((packed)) PCIHeader1; -struct PCIHeader2 +typedef struct { - PCIDeviceHeader Header; - uint32_t CardbusSocketRegistersBaseAddress; - uint8_t CapabilitiesPointer; - uint8_t Reserved0; - uint16_t SecondaryStatus; - uint8_t PCIbusNumber; - uint8_t CardbusBusNumber; - uint8_t SubordinateBusNumber; - uint8_t CardbusLatencyTimer; - uint32_t MemoryBase0; - uint32_t MemoryLimit0; - uint32_t MemoryBase1; - uint32_t MemoryLimit1; - uint32_t IOBase0; - uint32_t IOLimit0; - uint32_t IOBase1; - uint32_t IOLimit1; - uint8_t InterruptLine; - uint8_t InterruptPin; - uint16_t BridgeControl; - uint16_t SubsystemVendorID; - uint16_t SubsystemID; - uint32_t LegacyBaseAddress; -}; + PCIDeviceHeader Header; + uint32_t CardbusSocketRegistersBaseAddress; + uint8_t CapabilitiesPointer; + uint8_t Reserved0; + uint16_t SecondaryStatus; + uint8_t PCIbusNumber; + uint8_t CardbusBusNumber; + uint8_t SubordinateBusNumber; + uint8_t CardbusLatencyTimer; + uint32_t MemoryBase0; + uint32_t MemoryLimit0; + uint32_t MemoryBase1; + uint32_t MemoryLimit1; + uint32_t IOBase0; + uint32_t IOLimit0; + uint32_t IOBase1; + uint32_t IOLimit1; + uint8_t InterruptLine; + uint8_t InterruptPin; + uint16_t BridgeControl; + uint16_t SubsystemVendorID; + uint16_t SubsystemID; + uint32_t LegacyBaseAddress; +} __attribute__((packed)) PCIHeader2; + +typedef struct +{ + uint64_t BaseAddress; + uint16_t PCISegGroup; + uint8_t StartBus; + uint8_t EndBus; + uint32_t Reserved; +} __attribute__((packed)) DeviceConfig; + +typedef struct +{ + PCIDeviceHeader *Header; + DeviceConfig *Config; + uint32_t Bus; + uint32_t Device; + uint32_t Function; +} __attribute__((packed)) PCIDevice; + +typedef struct +{ + PCIDevice *Device; + /* PCIArray */ void *Next; +} __attribute__((packed)) PCIArray; + +#ifdef __cplusplus +extern "C" +{ +#endif + + PCIArray *FindPCIDevices(uint16_t Vendors[], uint16_t Devices[]); + void InitializePCI(PCIDevice *Device); + uint32_t GetBAR(uint8_t Index, PCIDevice *Device); + uint8_t iLine(PCIDevice *Device); + uint8_t iPin(PCIDevice *Device); + +#ifdef __cplusplus +} +#endif #endif // !__FENNIX_API_PCI_H__ diff --git a/include/regs.h b/include/regs.h new file mode 100644 index 0000000..c731529 --- /dev/null +++ b/include/regs.h @@ -0,0 +1,100 @@ +/* + 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 . +*/ + +#ifndef __FENNIX_API_REGISTERS_H__ +#define __FENNIX_API_REGISTERS_H__ + +#include + +#if defined(__amd64__) +typedef struct +{ + uint64_t r15; // General purpose + uint64_t r14; // General purpose + uint64_t r13; // General purpose + uint64_t r12; // General purpose + uint64_t r11; // General purpose + uint64_t r10; // General purpose + uint64_t r9; // General purpose + uint64_t r8; // General purpose + + uint64_t rbp; // Base Pointer (meant for stack frames) + uint64_t rdi; // Destination index for string operations + uint64_t rsi; // Source index for string operations + uint64_t rdx; // Data (commonly extends the A register) + uint64_t rcx; // Counter + uint64_t rbx; // Base + uint64_t rax; // Accumulator + + uint64_t InterruptNumber; // Interrupt Number + uint64_t ErrorCode; // Error code + + uint64_t rip; // Instruction Pointer + uint64_t cs; // Code Segment + uint64_t rflags; // Register Flags + uint64_t rsp; // Stack Pointer + uint64_t ss; // Stack Segment +} TrapFrame; +#elif defined(__i386__) +typedef struct TrapFrame +{ + uint32_t edi; // Destination index for string operations + uint32_t esi; // Source index for string operations + uint32_t ebp; // Base Pointer (meant for stack frames) + uint32_t esp; // Stack Pointer + uint32_t ebx; // Base + uint32_t edx; // Data (commonly extends the A register) + uint32_t ecx; // Counter + uint32_t eax; // Accumulator + + uint32_t InterruptNumber; // Interrupt Number + uint32_t ErrorCode; // Error code + + uint32_t eip; // Instruction Pointer + uint32_t cs; // Code Segment + uint32_t eflags; // Register Flags + + uint32_t r3_esp; // Stack Pointer + uint32_t r3_ss; // Stack Segment +} TrapFrame; +#elif defined(__aarch64__) +typedef struct TrapFrame +{ + uint64_t x19; // General purpose + uint64_t x20; // General purpose + uint64_t x21; // General purpose + uint64_t x22; // General purpose + uint64_t x23; // General purpose + uint64_t x24; // General purpose + uint64_t x25; // General purpose + uint64_t x26; // General purpose + + uint64_t x27; // Stack frame pointer + uint64_t x28; // Link register + uint64_t x29; // Frame pointer + uint64_t x30; // Program counter + + uint64_t sp_el0; // Stack pointer + uint64_t elr_el1; // Exception Link Register + uint64_t spsr_el1; // Saved Program Status Register + uint64_t ErrorCode /* esr_el1 */; // Exception Syndrome Register + + uint64_t InterruptNumber /* iar_el1 */; // Interrupt Acknowledge Register +} TrapFrame; +#endif + +#endif // !__FENNIX_API_REGISTERS_H__ diff --git a/include/syscall.h b/include/syscall.h new file mode 100644 index 0000000..1fdf92a --- /dev/null +++ b/include/syscall.h @@ -0,0 +1,231 @@ +/* + BSD 3-Clause License + + Copyright (c) 2023, EnderIce2 + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef __FENNIX_KERNEL_SYSCALLS_LIST_H__ +#define __FENNIX_KERNEL_SYSCALLS_LIST_H__ + +/* mmap */ + +#define sc_PROT_NONE 0 +#define sc_PROT_READ 1 +#define sc_PROT_WRITE 2 +#define sc_PROT_EXEC 4 + +#define sc_MAP_SHARED 1 +#define sc_MAP_PRIVATE 2 +#define sc_MAP_FIXED 4 +#define sc_MAP_ANONYMOUS 8 + +/* lseek */ + +#define sc_SEEK_SET 0 +#define sc_SEEK_CUR 1 +#define sc_SEEK_END 2 + +/** + * Enumeration of all the native syscalls + * available in the kernel + */ +typedef enum +{ + /** + * This syscall is used to exit the current + * process with the provided exit code. + * + * @fn void exit(int status); + * + * @param Code The exit code + * @return This syscall does not return + * + * @note No permissions are required to call + * this syscall + */ + sc_exit = 0, + + /** + * Map pages of memory + * + * @fn void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off); + */ + sc_mmap, + + /** + * Unmap pages of memory + * + * @fn int munmap(void *addr, size_t len); + */ + sc_munmap, + + /** + * Change the protection of a page of memory + * + * @fn int mprotect(void *addr, size_t len, int prot); + */ + sc_mprotect, + + /** + * Open a file + * + * @fn int open(const char *path, int oflag, ... ); + */ + sc_open, + + /** + * Close a file descriptor + * + * @fn int close(int fildes); + */ + sc_close, + + /** + * Read from a file descriptor + * + * @fn ssize_t read(int fildes, void *buf, size_t nbyte); + */ + sc_read, + + /** + * Write to a file descriptor + * + * @fn ssize_t write(int fildes, const void *buf, size_t nbyte); + */ + sc_write, + + /** + * Seek to a position in a file descriptor + * + * @fn off_t lseek(int fildes, off_t offset, int whence); + */ + sc_lseek, + + /** + * Create a new process + * + * @fn pid_t fork(void); + */ + sc_fork, + + /** Not a real syscall */ + sc_MaxSyscall +} NativeSyscalls; + +#ifndef syscall0 +static inline long syscall0(long syscall) +{ + long ret; + __asm__ __volatile__("syscall" + : "=a"(ret) + : "a"(syscall) + : "rcx", "r11", "memory"); + return ret; +} +#endif + +#ifndef syscall1 +static inline long syscall1(long syscall, long arg1) +{ + long ret; + __asm__ __volatile__("syscall" + : "=a"(ret) + : "a"(syscall), "D"(arg1) + : "rcx", "r11", "memory"); + return ret; +} +#endif + +#ifndef syscall2 +static inline long syscall2(long syscall, long arg1, long arg2) +{ + long ret; + __asm__ __volatile__("syscall" + : "=a"(ret) + : "a"(syscall), "D"(arg1), "S"(arg2) + : "rcx", "r11", "memory"); + return ret; +} +#endif + +#ifndef syscall3 +static inline long syscall3(long syscall, long arg1, long arg2, long arg3) +{ + long ret; + __asm__ __volatile__("syscall" + : "=a"(ret) + : "a"(syscall), "D"(arg1), "S"(arg2), "d"(arg3) + : "rcx", "r11", "memory"); + return ret; +} +#endif + +#ifndef syscall4 +static inline long syscall4(long syscall, long arg1, long arg2, long arg3, long arg4) +{ + long ret; + register long r10 __asm__("r10") = arg4; + __asm__ __volatile__("syscall" + : "=a"(ret) + : "a"(syscall), "D"(arg1), "S"(arg2), "d"(arg3), "r"(r10) + : "rcx", "r11", "memory"); + return ret; +} +#endif + +#ifndef syscall5 +static inline long syscall5(long syscall, long arg1, long arg2, long arg3, long arg4, long arg5) +{ + long ret; + register long r10 __asm__("r10") = arg4; + register long r8 __asm__("r8") = arg5; + __asm__ __volatile__("syscall" + : "=a"(ret) + : "a"(syscall), "D"(arg1), "S"(arg2), "d"(arg3), "r"(r10), "r"(r8) + : "rcx", "r11", "memory"); + return ret; +} +#endif + +#ifndef syscall6 +static inline long syscall6(long syscall, long arg1, long arg2, long arg3, long arg4, long arg5, long arg6) +{ + long ret; + register long r10 __asm__("r10") = arg4; + register long r8 __asm__("r8") = arg5; + register long r9 __asm__("r9") = arg6; + __asm__ __volatile__("syscall" + : "=a"(ret) + : "a"(syscall), "D"(arg1), "S"(arg2), "d"(arg3), "r"(r10), "r"(r8), "r"(r9) + : "rcx", "r11", "memory"); + return ret; +} +#endif + +#endif // !__FENNIX_KERNEL_SYSCALLS_LIST_H__ diff --git a/include/types.h b/include/types.h index b0eecc2..f9a717d 100644 --- a/include/types.h +++ b/include/types.h @@ -1,3 +1,20 @@ +/* + 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 . +*/ + #ifndef __FENNIX_API_TYPES_H__ #define __FENNIX_API_TYPES_H__ @@ -37,6 +54,58 @@ typedef __UINTMAX_TYPE__ uintmax_t; typedef __PTRDIFF_TYPE__ ptrdiff_t; typedef __SIZE_TYPE__ size_t; +typedef __SIZE_TYPE__ dev_t; +typedef __SIZE_TYPE__ off_t; +typedef __INT32_TYPE__ mode_t; +typedef int pid_t; + #define UNUSED(x) (void)(x) +#ifdef __cplusplus +#define EXTERNC extern "C" +#define NULL 0 +#else // __cplusplus +#define NULL ((void *)0) +#define bool _Bool +#define EXTERNC +#endif // __cplusplus + +#define true 1 +#define false 0 + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +#define MAX(a, b) \ + ({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a > _b ? _a : _b; \ + }) + +#define MIN(a, b) \ + ({ \ + __typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a < _b ? _a : _b; \ + }) + +#ifndef __va_list__ +typedef __builtin_va_list va_list; +#endif + +#define asm __asm__ +#define asmv __asm__ volatile + +#if __STDC_VERSION__ >= 201112L && !defined(__cplusplus) +#define static_assert _Static_assert +#endif + +#define va_start(v, l) __builtin_va_start(v, l) +#define va_end(v) __builtin_va_end(v) +#define va_arg(v, l) __builtin_va_arg(v, l) + +#define TO_PAGES(d) (((d) + PAGE_SIZE - 1) / PAGE_SIZE) +#define FROM_PAGES(d) ((d) * PAGE_SIZE) + #endif // !__FENNIX_API_TYPES_H__ diff --git a/include/vm.h b/include/vm.h new file mode 100644 index 0000000..a90ad18 --- /dev/null +++ b/include/vm.h @@ -0,0 +1,25 @@ +/* + 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 . +*/ + +#ifndef __FENNIX_API_VM_H__ +#define __FENNIX_API_VM_H__ + +#include + +bool IsVMwareBackdoorAvailable(); + +#endif // !__FENNIX_API_VM_H__ diff --git a/input/Makefile b/input/Makefile new file mode 100644 index 0000000..4279619 --- /dev/null +++ b/input/Makefile @@ -0,0 +1,5 @@ +build: + make -C aip build + +clean: + make -C aip clean diff --git a/input/aip/Makefile b/input/aip/Makefile new file mode 100644 index 0000000..0c529af --- /dev/null +++ b/input/aip/Makefile @@ -0,0 +1,83 @@ +# Config file +include ../../../Makefile.conf + +FILENAME = aip.drv + +CC = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)gcc +CPP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)g++ +LD = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)ld +AS = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)as +OBJDUMP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)objdump + +S_SOURCES = $(shell find ./ -type f -name '*.S') +C_SOURCES = $(shell find ./ -type f -name '*.c') +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp') +HEADERS = $(sort $(dir $(wildcard ../../include/*))) +OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o) +STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CPP_SOURCES:.cpp=.su) +INCLUDE_DIR = ../../include + +LIBS := ../../out/dcrt0.o -L../../out -ldriver + +LDFLAGS := \ + -fPIC -fPIE -pie \ + -Wl,--no-dynamic-linker,-ztext,--no-warn-rwx-segment \ + -nostdlib -nodefaultlibs -nolibc \ + -zmax-page-size=0x1000 \ + -Wl,-Map file.map -shared -fvisibility=hidden + +WARNCFLAG = -Wall -Wextra + +CFLAGS := -I$(INCLUDE_DIR) -fvisibility=hidden + +ifeq ($(OSARCH), amd64) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 \ + -march=x86-64 -pipe -ffunction-sections \ + -msoft-float -fno-builtin + +else ifeq ($(OSARCH), i386) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 -ffunction-sections \ + -march=i386 -pipe -msoft-float -fno-builtin + +else ifeq ($(OSARCH), aarch64) + +CFLAGS += -pipe -fno-builtin -fPIC + +endif + +ifeq ($(DEBUG), 1) + CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fstack-usage +ifeq ($(OSARCH), amd64) + CFLAGS += -fverbose-asm +endif +ifneq ($(OSARCH), aarch64) + CFLAGS += -fstack-check +endif + LDFLAGS += -ggdb3 -O0 +endif + +build: $(FILENAME) + mv $(FILENAME) ../../out/$(FILENAME) + +$(FILENAME): $(OBJ) + $(info Linking $@) + $(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@ + +%.o: %.c $(HEADERS) + $(info Compiling $<) + $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ + +%.o: %.cpp $(HEADERS) + $(info Compiling $<) + $(CPP) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fno-exceptions -fno-rtti -c $< -o $@ + +%.o: %.S + $(info Compiling $<) + $(AS) -o $@ $< + +clean: + rm -f file.map $(OBJ) $(STACK_USAGE_OBJ) diff --git a/input/aip/aip.h b/input/aip/aip.h new file mode 100644 index 0000000..95ea010 --- /dev/null +++ b/input/aip/aip.h @@ -0,0 +1,38 @@ +/* + 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 . +*/ + +#ifndef __FENNIX_DRIVER_AIP_H__ +#define __FENNIX_DRIVER_AIP_H__ + +#include +#include +#include + +extern uint8_t Device1ID[]; +extern uint8_t Device2ID[]; + +void PS2KbdInterruptHandler(TrapFrame *); +int InitializeKeyboard(); +int FinalizeKeyboard(); +int DetectPS2Keyboard(); + +void PS2MouseInterruptHandler(TrapFrame *); +int InitializeMouse(); +int FinalizeMouse(); +int DetectPS2Mouse(); + +#endif // !__FENNIX_DRIVER_AIP_H__ diff --git a/input/aip/keyboard.c b/input/aip/keyboard.c new file mode 100644 index 0000000..7f2c165 --- /dev/null +++ b/input/aip/keyboard.c @@ -0,0 +1,309 @@ +/* + 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.h" + +#include +#include +#include +#include +#include + +uint8_t KeyboardScanCodeSet = 0; +dev_t KeyboardDevID = -1; + +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}; + +bool IsE0 = false; +bool IsE1 = false; +void PS2KbdInterruptHandler(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; + Log("Unknown PS/2 Keyboard Scan Code Set: %#x", KeyboardScanCodeSet); + break; + } + } +} + +int InitializeKeyboard() +{ + // PS2WriteData(PS2_KBD_CMD_RESET); + // uint8_t test = PS2ReadData(); + // if (test != PS2_KBD_RESP_TEST_PASSED && + // test != PS2_KBD_RESP_ACK) + // { + // Log("PS/2 keyboard reset failed (%#x)", test); + // return -EFAULT; + // } + + PS2WriteData(PS2_KBD_CMD_DEFAULTS); + if (PS2ACKTimeout() != 0) + Log("PS/2 keyboard failed to set defaults"); + + PS2WriteData(PS2_KBD_CMD_SCAN_CODE_SET); + if (PS2ACKTimeout() != 0) + Log("PS/2 keyboard failed to set scan code set"); + + /* We want Scan Code Set 1 */ + PS2WriteData(PS2_KBD_SCAN_CODE_SET_2); /* It will set to 1 but with translation? */ + if (PS2ACKTimeout() != 0) + Log("PS/2 keyboard failed to set scan code set 2"); + + PS2WriteData(PS2_KBD_CMD_SCAN_CODE_SET); + if (PS2ACKTimeout() != 0) + Log("PS/2 keyboard failed to set scan code set"); + + PS2WriteData(PS2_KBD_SCAN_CODE_GET_CURRENT); + if (PS2ACKTimeout() != 0) + Log("PS/2 keyboard failed to get current scan code set"); + + KeyboardScanCodeSet = PS2ReadAfterACK(); + Log("PS/2 Keyboard Scan Code Set: 0x%X", KeyboardScanCodeSet); + PS2ClearOutputBuffer(); + + PS2WriteData(PS2_KBD_CMD_ENABLE_SCANNING); + + RegisterInterruptHandler(1, PS2KbdInterruptHandler); + KeyboardDevID = RegisterInputDevice(ddt_Keyboard); + return 0; +} + +int FinalizeKeyboard() +{ + UnregisterInputDevice(KeyboardDevID, ddt_Keyboard); + return 0; +} + +int DetectPS2Keyboard() +{ + PS2WriteData(PS2_KBD_CMD_DISABLE_SCANNING); + if (PS2ACKTimeout() != 0) + Log("PS/2 keyboard failed to disable scanning"); + + PS2WriteData(PS2_KBD_CMD_IDENTIFY); + if (PS2ACKTimeout() != 0) + Log("PS/2 keyboard failed to identify"); + + uint8_t recByte; + int timeout = 1000000; + while (timeout--) + { + recByte = PS2ReadData(); + if (recByte != PS2_ACK) + break; + } + Device1ID[0] = recByte; + + timeout = 1000000; + while (timeout--) + { + recByte = PS2ReadData(); + if (recByte != PS2_ACK) + break; + } + if (timeout == 0) + Log("PS/2 keyboard second byte timed out"); + else + Device1ID[1] = recByte; + + Log("PS2 Keyboard Device: 0x%X 0x%X", Device1ID[0], Device1ID[1]); + return 0; +} diff --git a/input/aip/main.c b/input/aip/main.c new file mode 100644 index 0000000..a63a513 --- /dev/null +++ b/input/aip/main.c @@ -0,0 +1,234 @@ +/* + 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.h" + +#include +#include + +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; + +int DriverEntry() +{ + PS2WriteCommand(PS2_CMD_DISABLE_PORT_1); + PS2WriteCommand(PS2_CMD_DISABLE_PORT_2); + PS2ClearOutputBuffer(); + + PS2WriteCommand(PS2_CMD_READ_CONFIG); + PS2_CONFIGURATION cfg = {.Raw = PS2ReadData()}; + + DualChannel = cfg.Port2Clock; + if (DualChannel) + Log("Dual channel PS/2 controller detected"); + cfg.Port1Interrupt = 1; + cfg.Port2Interrupt = 1; + cfg.Port1Translation = 1; + + PS2WriteCommand(PS2_CMD_WRITE_CONFIG); + PS2WriteData(cfg.Raw); + + PS2WriteCommand(PS2_CMD_TEST_CONTROLLER); + uint8_t test = PS2ReadData(); + if (test != PS2_TEST_PASSED) + { + Log("PS/2 controller self test failed (%#x)", test); + return -EFAULT; + } + + PS2WriteCommand(PS2_CMD_WRITE_CONFIG); + PS2WriteData(cfg.Raw); + + // bool port2avail = false; + // if (DualChannel) + // { + // PS2WriteCommand(PS2_CMD_ENABLE_PORT_1); + // PS2WriteCommand(PS2_CMD_READ_CONFIG); + // cfg.Raw = PS2ReadData(); + // port2avail = cfg.Port2Clock; + // PS2WriteCommand(PS2_CMD_DISABLE_PORT_1); + // } + + PS2WriteCommand(PS2_CMD_TEST_PORT_1); + test = PS2ReadData(); + if (test != 0x00) + { + Log("PS/2 Port 1 self test failed (%#x)", test); + return -EFAULT; + } + + if (DualChannel) + { + PS2WriteCommand(PS2_CMD_TEST_PORT_2); + test = PS2ReadData(); + if (test != 0x00) + { + Log("PS/2 Port 2 self test failed (%#x)", test); + return -EFAULT; + } + } + + PS2WriteCommand(PS2_CMD_ENABLE_PORT_1); + if (DualChannel) + PS2WriteCommand(PS2_CMD_ENABLE_PORT_2); + + int errK = InitializeKeyboard(); + + int errM = 0; + if (DualChannel) + errM = InitializeMouse(); + + /** A device may fail, but if the other one works, + * we can still use it. + */ + if (errK != 0 && errM != 0) + return -ENODEV; + return 0; +} + +int DriverFinal() +{ + FinalizeKeyboard(); + FinalizeMouse(); + PS2WriteCommand(PS2_CMD_DISABLE_PORT_1); + PS2WriteCommand(PS2_CMD_DISABLE_PORT_2); + return 0; +} + +int DriverPanic() +{ + PS2WriteCommand(PS2_CMD_DISABLE_PORT_1); + PS2WriteCommand(PS2_CMD_DISABLE_PORT_2); + return 0; +} + +void __intStub() {} +int DriverProbe() +{ + RegisterInterruptHandler(1, __intStub); + RegisterInterruptHandler(12, __intStub); + + int kbd = DetectPS2Keyboard(); + int mouse = DetectPS2Mouse(); + + UnregisterAllInterruptHandlers(__intStub); + + if (kbd != 0 && mouse != 0) + return -ENODEV; + + if (kbd == 0) + { + if (!IsKeyboard(Device1ID[0])) + { + Log("PS/2 Port 1 is not a keyboard"); + // return -EINVAL; + } + } + + if (mouse == 0) + { + if (!IsMouse(Device2ID[0])) + { + Log("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; +} + +DriverInfo("aip", + "Advanced Integrated Peripheral Driver", + "EnderIce2", + "0.1", + "GPLv3"); diff --git a/input/aip/mouse.c b/input/aip/mouse.c new file mode 100644 index 0000000..2ac8337 --- /dev/null +++ b/input/aip/mouse.c @@ -0,0 +1,233 @@ +/* + 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.h" + +#include +#include +#include + +dev_t MouseDevID = -1; +bool PacketReady = false; +bool FourPackets = false; +bool MouseButton45 = false; +uint8_t Cycle = 0; +PS2_MOUSE_PACKET Packet = {0}; + +void PS2MouseInterruptHandler(TrapFrame *) +{ + uint8_t data = PS2ReadData(); + 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') + + DebugLog("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; + + MouseReport mr = { + .LeftButton = Packet.Base.LeftButton, + .RightButton = Packet.Base.RightButton, + .MiddleButton = Packet.Base.MiddleButton, + .Button4 = Packet.ZMovement.Button4, + .Button5 = Packet.ZMovement.Button5, + .X = X, + .Y = -Y, + .Z = Packet.ZMovement.Z, + }; + ReportRelativeMouseEvent(MouseDevID, mr); + + PacketReady = false; +} + +void MouseSampleRate(uint8_t SampleRate) +{ + PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT); + PS2WriteData(PS2_MOUSE_CMD_SET_SAMPLE_RATE); + PS2ReadData(); + + PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT); + PS2WriteData(SampleRate); + PS2ReadData(); +} + +int InitializeMouse() +{ + PS2WriteData(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT); + PS2WriteData(PS2_MOUSE_CMD_RESET); + uint8_t test = PS2ReadData(); + if (test != PS2_MOUSE_RESP_TEST_PASSED && + test != PS2_MOUSE_RESP_ACK) + { + Log("PS/2 mouse reset failed! (%#x)", test); + return -EFAULT; + } + + RegisterInterruptHandler(12, PS2MouseInterruptHandler); + MouseDevID = RegisterInputDevice(ddt_Mouse); + + PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT); + PS2WriteData(PS2_MOUSE_CMD_SET_DEFAULTS); + PS2ReadData(); + + PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT); + PS2WriteData(PS2_MOUSE_CMD_ENABLE_DATA_REPORTING); + + MouseSampleRate(200); + MouseSampleRate(100); + MouseSampleRate(80); + + PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT); + PS2WriteData(PS2_MOUSE_CMD_READ_ID); + uint8_t Device2ID = PS2ReadData(); + Log("PS/2 Mouse ID: %#x", Device2ID); + + MouseSampleRate(200); + MouseSampleRate(200); + MouseSampleRate(80); + + PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT); + PS2WriteData(PS2_MOUSE_CMD_READ_ID); + Device2ID = PS2ReadData(); + Log("PS/2 Mouse ID: %#x", Device2ID); + + if (Device2ID >= 3 && Device2ID <= 4) + FourPackets = true; + if (Device2ID == 4) + MouseButton45 = true; + + return 0; +} + +int FinalizeMouse() +{ + UnregisterInputDevice(MouseDevID, ddt_Mouse); + + PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT); + PS2WriteData(PS2_MOUSE_CMD_DISABLE_DATA_REPORTING); + return 0; +} + +int DetectPS2Mouse() +{ + PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT); + PS2WriteData(PS2_MOUSE_CMD_DISABLE_DATA_REPORTING); + if (PS2ACKTimeout() != 0) + Log("PS/2 mouse failed to disable data reporting!"); + + PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT); + PS2WriteData(PS2_MOUSE_CMD_READ_ID); + if (PS2ACKTimeout() != 0) + Log("PS/2 mouse failed to read ID!"); + + uint8_t recByte; + int timeout = 1000000; + while (timeout--) + { + recByte = PS2ReadData(); + if (recByte != PS2_ACK) + break; + } + Device2ID[0] = recByte; + + timeout = 1000000; + while (timeout--) + { + recByte = PS2ReadData(); + if (recByte != PS2_ACK) + break; + } + Device2ID[1] = recByte; + + Log("PS2 Mouse Device: 0x%X 0x%X", Device2ID[0], Device2ID[1]); + return 0; +} diff --git a/library/Makefile b/library/Makefile new file mode 100644 index 0000000..752d3a5 --- /dev/null +++ b/library/Makefile @@ -0,0 +1,82 @@ +# Config file +include ../../Makefile.conf + +FILENAME = libdriver.a + +CC = ../../$(COMPILER_PATH)/$(COMPILER_ARCH)gcc +CPP = ../../$(COMPILER_PATH)/$(COMPILER_ARCH)g++ +LD = ../../$(COMPILER_PATH)/$(COMPILER_ARCH)ld +AS = ../../$(COMPILER_PATH)/$(COMPILER_ARCH)as +AR = ../../$(COMPILER_PATH)/$(COMPILER_ARCH)ar + +S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./crt/*") +C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./crt/*") +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./crt/*") +HEADERS = $(sort $(dir $(wildcard ../include/*))) +OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o) +STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CPP_SOURCES:.cpp=.su) +INCLUDE_DIR = ../include + +LDFLAGS := \ + -fPIC -fPIE -pie \ + -nostdlib -nodefaultlibs -nolibc \ + -zmax-page-size=0x1000 \ + -Wl,-Map driver.map -static -fvisibility=hidden + +WARNCFLAG = -Wall -Wextra + +CFLAGS := -I$(INCLUDE_DIR) -fvisibility=hidden + +ifeq ($(OSARCH), amd64) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 \ + -march=x86-64 -pipe -ffunction-sections \ + -msoft-float -fno-builtin + +else ifeq ($(OSARCH), i386) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 -ffunction-sections \ + -march=i386 -pipe -msoft-float -fno-builtin + +else ifeq ($(OSARCH), aarch64) + +CFLAGS += -pipe -fno-builtin -fPIC + +endif + +ifeq ($(DEBUG), 1) + CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fstack-usage +ifeq ($(OSARCH), amd64) + CFLAGS += -fverbose-asm +endif +ifneq ($(OSARCH), aarch64) + CFLAGS += -fstack-check +endif + LDFLAGS += -ggdb3 -O0 +endif + +build: $(FILENAME) + $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -shared -c crt/crt0.c -o dcrt0.o + mv dcrt0.o ../out/dcrt0.o + +$(FILENAME): $(OBJ) + $(info Linking $@) + $(AR) rcs $@ $(OBJ) + mv $(FILENAME) ../out/$(FILENAME) + +%.o: %.c $(HEADERS) + $(info Compiling $<) + $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ + +%.o: %.cpp $(HEADERS) + $(info Compiling $<) + $(CPP) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fexceptions -c $< -o $@ + +%.o: %.S + $(info Compiling $<) + $(AS) -o $@ $< + +clean: + rm -f driver.map $(OBJ) $(FILENAME) $(STACK_USAGE_OBJ) dcrt0.su diff --git a/library/aip.c b/library/aip.c new file mode 100644 index 0000000..6314983 --- /dev/null +++ b/library/aip.c @@ -0,0 +1,146 @@ +/* + 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 + +extern void Log(const char *Format, ...); + +void PIC_EOI(uint8_t IRQ) +{ + if (IRQ >= 8) + outb(PIC2_CMD, _PIC_EOI); + outb(PIC1_CMD, _PIC_EOI); +} + +void IRQ_MASK(uint8_t IRQ) +{ + uint16_t port; + uint8_t value; + + if (IRQ < 8) + port = PIC1_DATA; + else + { + port = PIC2_DATA; + IRQ -= 8; + } + + value = inb(port) | (1 << IRQ); + outb(port, value); +} + +void IRQ_UNMASK(uint8_t IRQ) +{ + uint16_t port; + uint8_t value; + + if (IRQ < 8) + port = PIC1_DATA; + else + { + port = PIC2_DATA; + IRQ -= 8; + } + + value = inb(port) & ~(1 << IRQ); + outb(port, value); +} + +void PS2Wait(const bool Output) +{ + int Timeout = 100000; + PS2_STATUSES Status = {.Raw = inb(PS2_STATUS)}; + while (Timeout--) + { + if (!Output) /* FIXME: Reverse? */ + { + if (Status.OutputBufferFull == 0) + return; + } + else + { + if (Status.InputBufferFull == 0) + return; + } + Status.Raw = inb(PS2_STATUS); + } + + Log("PS/2 controller timeout! (Status: %#x, %d)", Status, Output); +} + +void PS2WriteCommand(uint8_t Command) +{ + WaitInput; + outb(PS2_CMD, Command); +} + +void PS2WriteData(uint8_t Data) +{ + WaitInput; + outb(PS2_DATA, Data); +} + +uint8_t PS2ReadData() +{ + WaitOutput; + return inb(PS2_DATA); +} + +uint8_t PS2ReadStatus() +{ + WaitOutput; + return inb(PS2_STATUS); +} + +uint8_t PS2ReadAfterACK() +{ + uint8_t ret = PS2ReadData(); + while (ret == PS2_ACK) + { + WaitOutput; + ret = inb(PS2_DATA); + } + return ret; +} + +void PS2ClearOutputBuffer() +{ + PS2_STATUSES Status; + int timeout = 0x500; + while (timeout--) + { + Status.Raw = inb(PS2_STATUS); + if (Status.OutputBufferFull == 0) + return; + inb(PS2_DATA); + } +} + +int PS2ACKTimeout() +{ + int timeout = 0x500; + while (timeout > 0) + { + if (PS2ReadData() == PS2_ACK) + return 0; + timeout--; + } + return -ETIMEDOUT; +} diff --git a/library/audio.c b/library/audio.c new file mode 100644 index 0000000..ef51d46 --- /dev/null +++ b/library/audio.c @@ -0,0 +1,31 @@ +/* + 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 + +extern __driverAPI *API; + +dev_t RegisterAudioDevice(DeviceDriverType Type, drvOpen_t Open, drvClose_t Close, + drvRead_t Read, drvWrite_t Write, drvIoctl_t Ioctl) +{ + return API->RegisterAudioDevice(API->MajorID, Type, Open, Close, Read, Write, Ioctl); +} + +int UnregisterAudioDevice(dev_t DeviceID, DeviceDriverType Type) +{ + return API->UnregisterAudioDevice(API->MajorID, DeviceID, Type); +} diff --git a/library/base.c b/library/base.c new file mode 100644 index 0000000..6b661aa --- /dev/null +++ b/library/base.c @@ -0,0 +1,182 @@ +/* + 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 + +__driverAPI *API = NULL; + +int RegisterInterruptHandler(uint8_t IRQ, void *Handler) +{ + if (Handler == NULL) + return -EINVAL; + + return API->RegisterInterruptHandler(API->MajorID, + IRQ, + Handler); +} + +int OverrideInterruptHandler(uint8_t IRQ, void *Handler) +{ + if (Handler == NULL) + return -EINVAL; + + return API->OverrideInterruptHandler(API->MajorID, + IRQ, + Handler); +} + +int UnregisterInterruptHandler(uint8_t IRQ, void *Handler) +{ + if (Handler == NULL) + return -EINVAL; + + return API->UnregisterInterruptHandler(API->MajorID, + IRQ, + Handler); +} + +int UnregisterAllInterruptHandlers(void *Handler) +{ + if (Handler == NULL) + return -EINVAL; + + return API->UnregisterAllInterruptHandlers(API->MajorID, + Handler); +} + +void *AllocateMemory(size_t Pages) +{ + if (Pages == 0) + return NULL; + + return API->RequestPages(API->MajorID, + Pages); +} + +void FreeMemory(void *Pointer, size_t Pages) +{ + if (Pointer == NULL || Pages == 0) + return; + + API->FreePages(API->MajorID, + Pointer, + Pages); +} + +void AppendMapFlag(void *Address, PageMapFlags Flag) +{ + API->AppendMapFlag(API->MajorID, + Address, + Flag); +} + +void RemoveMapFlag(void *Address, PageMapFlags Flag) +{ + API->RemoveMapFlag(API->MajorID, + Address, + Flag); +} + +void MapPages(void *PhysicalAddress, void *VirtualAddress, size_t Pages, uint32_t Flags) +{ + API->MapPages(API->MajorID, + PhysicalAddress, + VirtualAddress, + Pages, + Flags); +} + +void UnmapPages(void *VirtualAddress, size_t Pages) +{ + API->UnmapPages(API->MajorID, + VirtualAddress, + Pages); +} + +void KPrint(const char *Format, ...) +{ + va_list Args; + va_start(Args, Format); + + API->KPrint(API->MajorID, + Format, + Args); + + va_end(Args); +} + +void Log(const char *Format, ...) +{ + va_list Args; + va_start(Args, Format); + + API->KernelLog(API->MajorID, + Format, + Args); + + va_end(Args); +} + +CriticalState EnterCriticalSection() +{ + CriticalState cs; + +#if defined(__i386__) || defined(__x86_64__) + + uintptr_t Flags; +#if defined(__x86_64__) + asmv("pushfq"); + asmv("popq %0" + : "=r"(Flags)); +#else + asmv("pushfl"); + asmv("popl %0" + : "=r"(Flags)); +#endif + cs = Flags & (1 << 9); + asmv("cli"); + +#elif defined(__arm__) || defined(__aarch64__) + + uintptr_t Flags; + asmv("mrs %0, cpsr" + : "=r"(Flags)); + cs = Flags & (1 << 7); + asmv("cpsid i"); + +#endif + + return cs; +} + +void LeaveCriticalSection(CriticalState PreviousState) +{ +#if defined(__i386__) || defined(__x86_64__) + + if (PreviousState) + asmv("sti"); + +#elif defined(__arm__) || defined(__aarch64__) + + if (PreviousState) + asmv("cpsie i"); + +#endif +} diff --git a/library/block.c b/library/block.c new file mode 100644 index 0000000..10dd2db --- /dev/null +++ b/library/block.c @@ -0,0 +1,31 @@ +/* + 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 + +extern __driverAPI *API; + +dev_t RegisterBlockDevice(DeviceDriverType Type, drvOpen_t Open, drvClose_t Close, + drvRead_t Read, drvWrite_t Write, drvIoctl_t Ioctl) +{ + return API->RegisterBlockDevice(API->MajorID, Type, Open, Close, Read, Write, Ioctl); +} + +int UnregisterBlockDevice(dev_t DeviceID, DeviceDriverType Type) +{ + return API->UnregisterBlockDevice(API->MajorID, DeviceID, Type); +} diff --git a/library/crt/crt0.c b/library/crt/crt0.c new file mode 100644 index 0000000..509492c --- /dev/null +++ b/library/crt/crt0.c @@ -0,0 +1,85 @@ +/* + 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 + +extern __driverAPI *API; + +int DriverEntry(); +int DriverFinal(); +int DriverPanic(); +int DriverProbe(); +void __IdentifyDriver( + dev_t ID, + int (*GetDriverInfo)(dev_t, const char *, const char *, + const char *, const char *, const char *)); + +typedef void (*CallPtr)(void); +extern CallPtr __init_array_start[0], __init_array_end[0]; +extern CallPtr __fini_array_start[0], __fini_array_end[0]; + +int _entry() +{ + for (CallPtr *func = __init_array_start; func != __init_array_end; func++) + (*func)(); + + return DriverEntry(); +} + +int _final() +{ + int err = DriverFinal(); + + for (CallPtr *func = __fini_array_start; func != __fini_array_end; func++) + (*func)(); + + return err; +} + +#define API_MajorRequiredVersion 0 +#define API_MinorRequiredVersion 0 +#define API_PatchRequiredVersion 0 + +int _start(__driverAPI *__API) +{ + if (unlikely(__API == NULL)) + return -EINVAL; + + if (unlikely(__API->APIVersion.Major != API_MajorRequiredVersion || + __API->APIVersion.Minor != API_MinorRequiredVersion || + __API->APIVersion.Patch != API_PatchRequiredVersion)) + return -EPROTO; + + if (unlikely(__API->RegisterFunction == NULL)) + return -ENOSYS; + + if (unlikely(DriverEntry == NULL || + DriverFinal == NULL || + DriverPanic == NULL || + DriverProbe == NULL || + __IdentifyDriver == NULL)) + return -EFAULT; + + API = __API; + __API->RegisterFunction(__API->MajorID, _entry, _drf_Entry); + __API->RegisterFunction(__API->MajorID, _final, _drf_Final); + __API->RegisterFunction(__API->MajorID, DriverPanic, _drf_Panic); + __API->RegisterFunction(__API->MajorID, DriverProbe, _drf_Probe); + __IdentifyDriver(__API->MajorID, __API->GetDriverInfo); + return 0; +} diff --git a/library/input.c b/library/input.c new file mode 100644 index 0000000..19bc1d0 --- /dev/null +++ b/library/input.c @@ -0,0 +1,61 @@ +/* + 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 + +extern __driverAPI *API; + +dev_t RegisterInputDevice(DeviceDriverType Type) +{ + return API->RegisterInputDevice(API->MajorID, Type); +} + +int UnregisterInputDevice(dev_t DeviceID, DeviceDriverType Type) +{ + return API->UnregisterInputDevice(API->MajorID, DeviceID, Type); +} + +int ReportKeyboardEvent(dev_t DeviceID, KeyScanCodes ScanCode, uint8_t Pressed) +{ + if (Pressed) + ScanCode |= KEY_PRESSED; + return API->ReportKeyboardEvent(API->MajorID, DeviceID, ScanCode); +} + +int ReportRelativeMouseEvent(dev_t DeviceID, MouseReport Report) +{ + __MouseButtons mb = { + .LeftButton = Report.LeftButton, + .RightButton = Report.RightButton, + .MiddleButton = Report.MiddleButton, + .Button4 = Report.Button4, + .Button5 = Report.Button5, + }; + return API->ReportRelativeMouseEvent(API->MajorID, DeviceID, mb, Report.X, Report.Y, Report.Z); +} + +int ReportAbsoluteMouseEvent(dev_t DeviceID, MouseReport Report, uintptr_t X, uintptr_t Y) +{ + __MouseButtons mb = { + .LeftButton = Report.LeftButton, + .RightButton = Report.RightButton, + .MiddleButton = Report.MiddleButton, + .Button4 = Report.Button4, + .Button5 = Report.Button5, + }; + return API->ReportAbsoluteMouseEvent(API->MajorID, DeviceID, mb, X, Y, Report.Z); +} diff --git a/library/net.c b/library/net.c new file mode 100644 index 0000000..fa37a70 --- /dev/null +++ b/library/net.c @@ -0,0 +1,36 @@ +/* + 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 + +extern __driverAPI *API; + +dev_t RegisterNetDevice(DeviceDriverType Type, drvOpen_t Open, drvClose_t Close, + drvRead_t Read, drvWrite_t Write, drvIoctl_t Ioctl) +{ + return API->RegisterNetDevice(API->MajorID, Type, Open, Close, Read, Write, Ioctl); +} + +int UnregisterNetDevice(dev_t DeviceID, DeviceDriverType Type) +{ + return API->UnregisterNetDevice(API->MajorID, DeviceID, Type); +} + +int ReportNetworkPacket(dev_t DeviceID, void *Buffer, size_t Size) +{ + return API->ReportNetworkPacket(API->MajorID, DeviceID, Buffer, Size); +} diff --git a/library/pci.c b/library/pci.c new file mode 100644 index 0000000..7bea853 --- /dev/null +++ b/library/pci.c @@ -0,0 +1,57 @@ +/* + 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 + +extern __driverAPI *API; + +static_assert(sizeof(PCIArray) == sizeof(__PCIArray), + "PCIArray size mismatch"); + +PCIArray *FindPCIDevices(uint16_t Vendors[], uint16_t Devices[]) +{ + return (PCIArray *)API->GetPCIDevices(API->MajorID, + Vendors, + Devices); +} + +void InitializePCI(PCIDevice *Device) +{ + API->InitializePCI(API->MajorID, + (void *)Device->Header); +} + +uint32_t GetBAR(uint8_t Index, PCIDevice *Device) +{ + return API->GetBAR(API->MajorID, + Index, + (void *)Device->Header); +} + +uint8_t iLine(PCIDevice *Device) +{ + PCIHeader0 *Header = (PCIHeader0 *)Device->Header; + return Header->InterruptLine; +} + +uint8_t iPin(PCIDevice *Device) +{ + PCIHeader0 *Header = (PCIHeader0 *)Device->Header; + return Header->InterruptPin; +} diff --git a/library/std.c b/library/std.c new file mode 100644 index 0000000..3cbe942 --- /dev/null +++ b/library/std.c @@ -0,0 +1,51 @@ +/* + 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 + +#undef memcpy +#undef memset +#undef memmove +#undef strlen + +extern __driverAPI *API; + +void *MemoryCopy(void *Destination, const void *Source, size_t Length) +{ + return API->memcpy(API->MajorID, Destination, Source, Length); +} + +void *MemorySet(void *Destination, int Value, size_t Length) +{ + return API->memset(API->MajorID, Destination, Value, Length); +} + +void *MemoryMove(void *Destination, const void *Source, size_t Length) +{ + return API->memmove(API->MajorID, Destination, Source, Length); +} + +size_t StringLength(const char String[]) +{ + return API->strlen(API->MajorID, String); +} + +char *strstr(const char *Haystack, const char *Needle) +{ + return API->strstr(API->MajorID, Haystack, Needle); +} diff --git a/library/stdcpp.cpp b/library/stdcpp.cpp new file mode 100644 index 0000000..ecf7751 --- /dev/null +++ b/library/stdcpp.cpp @@ -0,0 +1,73 @@ +/* + 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 + +extern __driverAPI *API; + +/** + * TODO: memory allocator + * we can't really call kernel's malloc because the + * kernel keep track of memory usage for each driver + * + * maybe implement the allocator in vma? + */ + +void *__new_alloc_page(std::size_t Size) +{ + /* Do not allow allocations larger than 4 KiB */ + if (Size > PAGE_SIZE) + asmv("ud2"); + + return API->RequestPages(API->MajorID, 1); +} + +void __delete_alloc_page(void *Pointer) +{ + API->FreePages(API->MajorID, Pointer, 1); +} + +void *operator new(std::size_t Size) +{ + return __new_alloc_page(Size); +} + +void *operator new[](std::size_t Size) +{ + return __new_alloc_page(Size); +} + +void operator delete(void *Pointer) +{ + __delete_alloc_page(Pointer); +} + +void operator delete[](void *Pointer) +{ + __delete_alloc_page(Pointer); +} + +void operator delete(void *Pointer, std::size_t) +{ + __delete_alloc_page(Pointer); +} + +void operator delete[](void *Pointer, std::size_t) +{ + __delete_alloc_page(Pointer); +} diff --git a/library/task.c b/library/task.c new file mode 100644 index 0000000..de7e65d --- /dev/null +++ b/library/task.c @@ -0,0 +1,59 @@ +/* + 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 + +extern __driverAPI *API; + +pid_t CreateKernelProcess(const char *Name) +{ + return API->CreateKernelProcess(API->MajorID, + Name); +} + +pid_t CreateKernelThread(pid_t pId, const char *Name, void *EntryPoint, void *Argument) +{ + return API->CreateKernelThread(API->MajorID, + pId, + Name, + EntryPoint, + Argument); +} + +int KillProcess(pid_t pId, int ExitCode) +{ + return API->KillProcess(API->MajorID, + pId, ExitCode); +} + +int KillThread(pid_t tId, int ExitCode) +{ + return API->KillThread(API->MajorID, + tId, ExitCode); +} + +void Yield() +{ + API->Yield(API->MajorID); +} + +void Sleep(uint64_t Milliseconds) +{ + API->Sleep(API->MajorID, + Milliseconds); +} diff --git a/library/vm.c b/library/vm.c new file mode 100644 index 0000000..f0cd2fc --- /dev/null +++ b/library/vm.c @@ -0,0 +1,114 @@ +/* + 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 + +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; +} diff --git a/misc/Makefile b/misc/Makefile new file mode 100644 index 0000000..f90db13 --- /dev/null +++ b/misc/Makefile @@ -0,0 +1,7 @@ +build: + make -C example build + make -C vmware build + +clean: + make -C example clean + make -C vmware clean diff --git a/misc/example/Makefile b/misc/example/Makefile new file mode 100644 index 0000000..b15790f --- /dev/null +++ b/misc/example/Makefile @@ -0,0 +1,83 @@ +# Config file +include ../../../Makefile.conf + +FILENAME = example.drv + +CC = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)gcc +CPP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)g++ +LD = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)ld +AS = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)as +OBJDUMP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)objdump + +S_SOURCES = $(shell find ./ -type f -name '*.S') +C_SOURCES = $(shell find ./ -type f -name '*.c') +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp') +HEADERS = $(sort $(dir $(wildcard ../../include/*))) +OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o) +STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CPP_SOURCES:.cpp=.su) +INCLUDE_DIR = ../../include + +LIBS := ../../out/dcrt0.o -L../../out -ldriver + +LDFLAGS := \ + -fPIC -fPIE -pie \ + -Wl,--no-dynamic-linker,-ztext,--no-warn-rwx-segment \ + -nostdlib -nodefaultlibs -nolibc \ + -zmax-page-size=0x1000 \ + -Wl,-Map file.map -shared -fvisibility=hidden + +WARNCFLAG = -Wall -Wextra + +CFLAGS := -I$(INCLUDE_DIR) -fvisibility=hidden + +ifeq ($(OSARCH), amd64) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 \ + -march=x86-64 -pipe -ffunction-sections \ + -msoft-float -fno-builtin + +else ifeq ($(OSARCH), i386) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 -ffunction-sections \ + -march=i386 -pipe -msoft-float -fno-builtin + +else ifeq ($(OSARCH), aarch64) + +CFLAGS += -pipe -fno-builtin -fPIC + +endif + +ifeq ($(DEBUG), 1) + CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fstack-usage +ifeq ($(OSARCH), amd64) + CFLAGS += -fverbose-asm +endif +ifneq ($(OSARCH), aarch64) + CFLAGS += -fstack-check +endif + LDFLAGS += -ggdb3 -O0 +endif + +build: $(FILENAME) + mv $(FILENAME) ../../out/$(FILENAME) + +$(FILENAME): $(OBJ) + $(info Linking $@) + $(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@ + +%.o: %.c $(HEADERS) + $(info Compiling $<) + $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ + +%.o: %.cpp $(HEADERS) + $(info Compiling $<) + $(CPP) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fno-exceptions -fno-rtti -c $< -o $@ + +%.o: %.S + $(info Compiling $<) + $(AS) -o $@ $< + +clean: + rm -f file.map $(OBJ) $(STACK_USAGE_OBJ) diff --git a/misc/example/main.c b/misc/example/main.c new file mode 100644 index 0000000..b4f363b --- /dev/null +++ b/misc/example/main.c @@ -0,0 +1,87 @@ +/* + 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 + +int DriverEntry() +{ + /** This is the main function of the driver. + * This function is called when the driver is loaded. + * This function should be used to initialize the PCI device + * and allocate resources. + */ + + /* Print a message to the screen */ + KPrint("Hello World from Example Driver!"); + + /* Print a message to the kernel log */ + Log("Hello World from Example Driver!"); + + /* Print a message only if DEBUG is set */ + DebugLog("Hello World from Example Driver!"); + + /* Return 0 to indicate success */ + return 0; +} + +int DriverFinal() +{ + /** This function is called when the driver is unloaded. + * This function should be used to stop the PCI device and + * free any resources. + */ + + return 0; +} + +int DriverPanic() +{ + /** This function is called when the kernel panics. + * This function should be used to stop the driver from + * receiving interrupts or anything else that is not + * safe to do when the kernel panics. + */ + + return 0; +} + +int DriverProbe() +{ + /** This is the first function that is called when the + * driver is loaded. + * We can use this function to test if the driver is + * compatible with the hardware. + * Like if we have a specific PCI device or if we have + * a specific CPU feature. + * + * Return 0 if the driver is compatible with the hardware. + * Otherwise, we return a value from the errno.h header. + * + * Note: In this function you cannot use variables that + * have constructors or destructors. Before DriverEntry, + * the constructors are called and after DriverFinalize, + * the destructors are called. + */ + + return 0; +} + +DriverInfo("example", + "Example Driver", + "EnderIce2", + "0.1", + "GPLv3"); diff --git a/misc/vmware/Makefile b/misc/vmware/Makefile new file mode 100644 index 0000000..171f3b4 --- /dev/null +++ b/misc/vmware/Makefile @@ -0,0 +1,83 @@ +# Config file +include ../../../Makefile.conf + +FILENAME = vmware.drv + +CC = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)gcc +CPP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)g++ +LD = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)ld +AS = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)as +OBJDUMP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)objdump + +S_SOURCES = $(shell find ./ -type f -name '*.S') +C_SOURCES = $(shell find ./ -type f -name '*.c') +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp') +HEADERS = $(sort $(dir $(wildcard ../../include/*))) +OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o) +STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CPP_SOURCES:.cpp=.su) +INCLUDE_DIR = ../../include + +LIBS := ../../out/dcrt0.o -L../../out -ldriver + +LDFLAGS := \ + -fPIC -fPIE -pie \ + -Wl,--no-dynamic-linker,-ztext,--no-warn-rwx-segment \ + -nostdlib -nodefaultlibs -nolibc \ + -zmax-page-size=0x1000 \ + -Wl,-Map file.map -shared -fvisibility=hidden + +WARNCFLAG = -Wall -Wextra + +CFLAGS := -I$(INCLUDE_DIR) -fvisibility=hidden + +ifeq ($(OSARCH), amd64) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 \ + -march=x86-64 -pipe -ffunction-sections \ + -msoft-float -fno-builtin + +else ifeq ($(OSARCH), i386) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 -ffunction-sections \ + -march=i386 -pipe -msoft-float -fno-builtin + +else ifeq ($(OSARCH), aarch64) + +CFLAGS += -pipe -fno-builtin -fPIC + +endif + +ifeq ($(DEBUG), 1) + CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fstack-usage +ifeq ($(OSARCH), amd64) + CFLAGS += -fverbose-asm +endif +ifneq ($(OSARCH), aarch64) + CFLAGS += -fstack-check +endif + LDFLAGS += -ggdb3 -O0 +endif + +build: $(FILENAME) + mv $(FILENAME) ../../out/$(FILENAME) + +$(FILENAME): $(OBJ) + $(info Linking $@) + $(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@ + +%.o: %.c $(HEADERS) + $(info Compiling $<) + $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ + +%.o: %.cpp $(HEADERS) + $(info Compiling $<) + $(CPP) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fno-exceptions -fno-rtti -c $< -o $@ + +%.o: %.S + $(info Compiling $<) + $(AS) -o $@ $< + +clean: + rm -f file.map $(OBJ) $(STACK_USAGE_OBJ) diff --git a/misc/vmware/main.c b/misc/vmware/main.c new file mode 100644 index 0000000..515e1a7 --- /dev/null +++ b/misc/vmware/main.c @@ -0,0 +1,753 @@ +/* + 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 +#include +#include +#include +#include +#include + +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) + +#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") + +/* TODO: + - use vmcall or vmmcall instead of out or in if available +*/ + +typedef struct +{ + int TCLOChannel; + uint16_t ChannelID; + uint32_t CookieHigh; + uint32_t CookieLow; +} ToolboxContext; + +dev_t MouseDevID = -1; + +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) + { + Log("Failed to open message channel %#lx", Protocol); + return -EINVAL; + } + + DebugLog("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); + + DebugLog("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 = StringLength(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 = StringLength(Message); + while (Size && + (HighWord(cx) & STATUS_SUCCESS)) + { + uint32_t TotalBytes = MIN((uint32_t)Size, (uint32_t)4); + uint32_t Word = 0; + MemoryCopy(&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; + + MemoryCopy(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 = StringLength(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) + { + Log("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) + { + DebugLog("Message \"%s\" sent", Message); + return 0; + } + else if ((status & STATUS_CPT) == 0) + { + Log("Checkpoint occurred for message \"%s\"", Message); + continue; + } + else + break; + } + + Log("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) + { + Log("Failed to receive message size: %d", cx); + return -EINVAL; + } + else if ((HighWord(cx) & STATUS_DORECV) == 0) + { + // Log("No message to receive"); + return -EAGAIN; + } + + ReplySize = bx; + + if (ReplyBuf != NULL) + FreeMemory(ReplyBuf, ReplyBufPages); + ReplyBufPages = ReplySize / 0x1000 + 1; + ReplyBuf = AllocateMemory(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) + { + Log("Checkpoint occurred for message payload"); + continue; + } + + Log("Failed to receive message payload: %d", HighWord(bx)); + FreeMemory(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) + { + Log("Retrying message receive"); + continue; + } + + Log("Failed to receive message status: %d", HighWord(cx)); + FreeMemory(ReplyBuf, ReplyBufPages); + return -EINVAL; + } + + break; + } + + if (ReplyBuf == NULL) + { + Log("Failed to receive message"); + return -EINVAL; + } + + *Buffer = ReplyBuf; + *BufferSize = ReplySize; + DebugLog("Received message \"%s\"", ReplyBuf); + return 0; +} + +static int SendRPCI(ToolboxContext *, const char *Request) +{ + ToolboxContext rpci_ctx = {0}; + int status = OpenMessageChannel(&rpci_ctx, MESSAGE_RPCI); + if (status < 0) + { + Log("Failed to open RPCI channel: %d", status); + return status; + } + + status = MessageSend(&rpci_ctx, Request); + if (status < 0) + { + Log("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) + { + Sleep(1000); + continue; + } + else if (status < 0) + { + Log("Failed to receive message"); + return 1; + } + + buf[StringLength(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")) + { + DebugLog("%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; +ToolboxContext *tb_ctx = NULL; +void DisplayScaleThread() +{ + /* sizeof ToolboxContext */ + tb_ctx = AllocateMemory(1); + Sleep(2000); + + while (true) + { + if (DisplayGetSize(tb_ctx) != 0) + Log("Failed to scale display"); + Sleep(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 = {0}; + + /* 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 = {0}; + cmd.bx = ABSPOINTER_RELATIVE; + cmd.command = CMD_ABSPOINTER_COMMAND; + CommandSend(&cmd); +} + +void InterruptHandler(TrapFrame *) +{ + uint8_t Data = inb(0x60); + (void)Data; + + VMwareCommand cmd = {0}; + cmd.bx = 0; + cmd.command = CMD_ABSPOINTER_STATUS; + CommandSend(&cmd); + + if (cmd.ax == 0xFFFF0000) + { + Log("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); + + MouseReport mr = { + .LeftButton = Buttons & 0x20, + .RightButton = Buttons & 0x10, + .MiddleButton = Buttons & 0x08, + .Button4 = 0x0, + .Button5 = 0x0, + .X = 0, + .Y = 0, + .Z = (int8_t)cmd.dx, + }; + + /** + * 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; + ReportAbsoluteMouseEvent(MouseDevID, mr, AbsoluteX, AbsoluteY); +} + +bool ToolboxSupported = false; +int DriverEntry() +{ + ToolboxContext tb_ctx = {0}; + /* Test if it's supported */ + int status = OpenMessageChannel(&tb_ctx, MESSAGE_TCLO); + if (status == 0) + { + ToolboxSupported = true; + MessageClose(&tb_ctx); + dst_id = CreateKernelThread(0, "VMware Display Scale", + (void *)DisplayScaleThread, NULL); + } + + PS2WriteCommand(PS2_CMD_ENABLE_PORT_2); + PS2WriteCommand(PS2_CMD_READ_CONFIG); + PS2_CONFIGURATION config = {.Raw = PS2ReadData()}; + config.Port2Interrupt = 1; + PS2WriteCommand(PS2_CMD_WRITE_CONFIG); + PS2WriteData(config.Raw); + + PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT); + PS2WriteData(PS2_MOUSE_CMD_SET_DEFAULTS); + PS2ReadData(); + + PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT); + PS2WriteData(PS2_MOUSE_CMD_ENABLE_DATA_REPORTING); + PS2ReadData(); + Absolute(); + + /** + * If we have another driver using the PS/2 mouse, we need to + * override its interrupt handler. + */ + OverrideInterruptHandler(12, InterruptHandler); + MouseDevID = RegisterInputDevice(ddt_Mouse); + return 0; +} + +int DriverFinal() +{ + PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT); + PS2WriteData(PS2_MOUSE_CMD_DISABLE_DATA_REPORTING); + + Relative(); + UnregisterInputDevice(MouseDevID, ddt_Mouse); + + if (ToolboxSupported) + { + KillThread(dst_id, 0); + if (tb_ctx->TCLOChannel != -1) + MessageClose(tb_ctx); + FreeMemory(tb_ctx, 1); + } + return 0; +} + +int DriverPanic() +{ + Relative(); + PS2WriteCommand(PS2_CMD_WRITE_NEXT_BYTE_TO_PS2_PORT_2_INPUT); + PS2WriteData(PS2_MOUSE_CMD_DISABLE_DATA_REPORTING); + return 0; +} + +int DriverProbe() +{ + if (!IsVMwareBackdoorAvailable()) + return -ENODEV; + return 0; +} + +DriverInfo("vmware", + "VMware Tools Driver", + "EnderIce2", + "0.1", + "GPLv3"); diff --git a/network/Makefile b/network/Makefile new file mode 100644 index 0000000..1e9340b --- /dev/null +++ b/network/Makefile @@ -0,0 +1,7 @@ +build: + make -C e1000 build + make -C rtl8139 build + +clean: + make -C e1000 clean + make -C rtl8139 clean diff --git a/network/e1000/Makefile b/network/e1000/Makefile new file mode 100644 index 0000000..cd5cbb9 --- /dev/null +++ b/network/e1000/Makefile @@ -0,0 +1,83 @@ +# Config file +include ../../../Makefile.conf + +FILENAME = e1000.drv + +CC = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)gcc +CPP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)g++ +LD = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)ld +AS = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)as +OBJDUMP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)objdump + +S_SOURCES = $(shell find ./ -type f -name '*.S') +C_SOURCES = $(shell find ./ -type f -name '*.c') +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp') +HEADERS = $(sort $(dir $(wildcard ../../include/*))) +OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o) +STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CPP_SOURCES:.cpp=.su) +INCLUDE_DIR = ../../include + +LIBS := ../../out/dcrt0.o -L../../out -ldriver + +LDFLAGS := \ + -fPIC -fPIE -pie \ + -Wl,--no-dynamic-linker,-ztext,--no-warn-rwx-segment \ + -nostdlib -nodefaultlibs -nolibc \ + -zmax-page-size=0x1000 \ + -Wl,-Map file.map -shared -fvisibility=hidden + +WARNCFLAG = -Wall -Wextra + +CFLAGS := -I$(INCLUDE_DIR) -fvisibility=hidden + +ifeq ($(OSARCH), amd64) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 \ + -march=x86-64 -pipe -ffunction-sections \ + -msoft-float -fno-builtin + +else ifeq ($(OSARCH), i386) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 -ffunction-sections \ + -march=i386 -pipe -msoft-float -fno-builtin + +else ifeq ($(OSARCH), aarch64) + +CFLAGS += -pipe -fno-builtin -fPIC + +endif + +ifeq ($(DEBUG), 1) + CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fstack-usage +ifeq ($(OSARCH), amd64) + CFLAGS += -fverbose-asm +endif +ifneq ($(OSARCH), aarch64) + CFLAGS += -fstack-check +endif + LDFLAGS += -ggdb3 -O0 +endif + +build: $(FILENAME) + mv $(FILENAME) ../../out/$(FILENAME) + +$(FILENAME): $(OBJ) + $(info Linking $@) + $(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@ + +%.o: %.c $(HEADERS) + $(info Compiling $<) + $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ + +%.o: %.cpp $(HEADERS) + $(info Compiling $<) + $(CPP) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fno-exceptions -fno-rtti -c $< -o $@ + +%.o: %.S + $(info Compiling $<) + $(AS) -o $@ $< + +clean: + rm -f file.map $(OBJ) $(STACK_USAGE_OBJ) diff --git a/network/e1000/e1000.cpp b/network/e1000/e1000.cpp new file mode 100644 index 0000000..43f57c2 --- /dev/null +++ b/network/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 +#include +#include + +#include "e1000.hpp" + +class E1000Device +{ +private: + PCIHeader0 *Header; + uint16_t DeviceID; + bool Initialized = false; + + bool EEPROMAvailable; + + 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() + { + DebugLog("Initializing RX..."); + uintptr_t Ptr = (uintptr_t)AllocateMemory(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)AllocateMemory(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() + { + DebugLog("Initializing TX..."); + uintptr_t Ptr = (uintptr_t)AllocateMemory(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)) + Yield(); + 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 + { + Log("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(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); + + 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(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: + { + Log("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()) + { + Log("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: + { + Log("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: + { + Log("Unimplemented E1000 device."); + return; + } + } + } +}; + +E1000Device *Drivers[4] = {nullptr}; +dev_t AudioID[4] = {0}; + +#define OIR(x) OIR_##x +#define CREATE_OIR(x) \ + void OIR_##x(TrapFrame *f) { Drivers[x]->OnInterruptReceived(f); } + +CREATE_OIR(0); +CREATE_OIR(1); +CREATE_OIR(2); +CREATE_OIR(3); + +int drvOpen(dev_t, dev_t, int, mode_t) { return 0; } +int drvClose(dev_t, dev_t) { return 0; } +size_t drvRead(dev_t, dev_t, uint8_t *, size_t, off_t) { return 0; } + +size_t drvWrite(dev_t, dev_t min, uint8_t *Buffer, size_t Size, off_t) +{ + return Drivers[AudioID[min]]->write(Buffer, Size); +} + +int drvIoctl(dev_t, dev_t min, unsigned long Request, void *Argp) +{ + return Drivers[AudioID[min]]->ioctl((NetIoctl)Request, Argp); +} + +PCIArray *Devices; +EXTERNC int cxx_Panic() +{ + PCIArray *ctx = Devices; + short Count = 0; + while (ctx != nullptr) + { + if (Drivers[Count] != nullptr) + Drivers[Count]->Panic(); + Count++; + ctx = (PCIArray *)ctx->Next; + } + + return 0; +} + +EXTERNC int cxx_Probe() +{ + uint16_t VendorIDs[] = {0x8086, /* Intel */ + PCI_END}; + uint16_t DeviceIDs[] = {0x100E, /* 82540EM */ + 0x100F, /* 82545EM */ + 0x10D3, /* 82574L */ + 0x10EA, /* I217-LM */ + 0x153A, /* 82577LM */ + PCI_END}; + Devices = FindPCIDevices(VendorIDs, DeviceIDs); + if (Devices == nullptr) + { + Log("No E1000 device found."); + return -ENODEV; + } + return 0; +} + +EXTERNC int cxx_Initialize() +{ + PCIArray *ctx = Devices; + size_t Count = 0; + while (ctx != nullptr) + { + if (Count > sizeof(Drivers) / sizeof(E1000Device *)) + break; + + InitializePCI(ctx->Device); + + Drivers[Count] = new E1000Device((PCIHeader0 *)ctx->Device->Header, + ctx->Device->Header->DeviceID); + + if (Drivers[Count]->IsInitialized()) + { + dev_t ret = RegisterNetDevice(ddt_Network, + drvOpen, drvClose, + drvRead, drvWrite, + drvIoctl); + AudioID[Count] = ret; + Drivers[Count]->ID = ret; + + /* FIXME: bad code */ + switch (Count) + { + case 0: + RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(0)); + break; + case 1: + RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(1)); + break; + case 2: + RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(2)); + break; + case 3: + RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(3)); + break; + default: + break; + } + + Count++; + } + ctx = (PCIArray *)ctx->Next; + } + + if (Count == 0) + { + Log("No valid E1000 device found."); + return -EINVAL; + } + + return 0; +} + +EXTERNC int cxx_Finalize() +{ + PCIArray *ctx = Devices; + size_t Count = 0; + while (ctx != nullptr) + { + if (Count++ > sizeof(Drivers) / sizeof(E1000Device *)) + break; + + delete Drivers[Count++]; + ctx->Device->Header->Command |= PCI_COMMAND_INTX_DISABLE; + ctx = (PCIArray *)ctx->Next; + } + + return 0; +} diff --git a/network/e1000/e1000.hpp b/network/e1000/e1000.hpp new file mode 100644 index 0000000..73c6848 --- /dev/null +++ b/network/e1000/e1000.hpp @@ -0,0 +1,163 @@ +/* + 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 . +*/ + +#pragma once + +#include + +#ifdef __cplusplus +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)); +#endif + +EXTERNC int cxx_Panic(); +EXTERNC int cxx_Probe(); +EXTERNC int cxx_Initialize(); +EXTERNC int cxx_Finalize(); diff --git a/network/e1000/main.c b/network/e1000/main.c new file mode 100644 index 0000000..9f3a46e --- /dev/null +++ b/network/e1000/main.c @@ -0,0 +1,31 @@ +/* + 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 "e1000.hpp" + +int DriverEntry() { return cxx_Initialize(); } +int DriverFinal() { return cxx_Finalize(); } +int DriverPanic() { return cxx_Panic(); } +int DriverProbe() { return cxx_Probe(); } + +DriverInfo("e1000", + "Intel(R) PRO/1000 Network Driver", + "EnderIce2", + "0.1", + "GPLv3"); diff --git a/network/rtl8139/Makefile b/network/rtl8139/Makefile new file mode 100644 index 0000000..1a41216 --- /dev/null +++ b/network/rtl8139/Makefile @@ -0,0 +1,83 @@ +# Config file +include ../../../Makefile.conf + +FILENAME = rtl8139.drv + +CC = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)gcc +CPP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)g++ +LD = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)ld +AS = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)as +OBJDUMP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)objdump + +S_SOURCES = $(shell find ./ -type f -name '*.S') +C_SOURCES = $(shell find ./ -type f -name '*.c') +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp') +HEADERS = $(sort $(dir $(wildcard ../../include/*))) +OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o) +STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CPP_SOURCES:.cpp=.su) +INCLUDE_DIR = ../../include + +LIBS := ../../out/dcrt0.o -L../../out -ldriver + +LDFLAGS := \ + -fPIC -fPIE -pie \ + -Wl,--no-dynamic-linker,-ztext,--no-warn-rwx-segment \ + -nostdlib -nodefaultlibs -nolibc \ + -zmax-page-size=0x1000 \ + -Wl,-Map file.map -shared -fvisibility=hidden + +WARNCFLAG = -Wall -Wextra + +CFLAGS := -I$(INCLUDE_DIR) -fvisibility=hidden + +ifeq ($(OSARCH), amd64) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 \ + -march=x86-64 -pipe -ffunction-sections \ + -msoft-float -fno-builtin + +else ifeq ($(OSARCH), i386) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 -ffunction-sections \ + -march=i386 -pipe -msoft-float -fno-builtin + +else ifeq ($(OSARCH), aarch64) + +CFLAGS += -pipe -fno-builtin -fPIC + +endif + +ifeq ($(DEBUG), 1) + CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fstack-usage +ifeq ($(OSARCH), amd64) + CFLAGS += -fverbose-asm +endif +ifneq ($(OSARCH), aarch64) + CFLAGS += -fstack-check +endif + LDFLAGS += -ggdb3 -O0 +endif + +build: $(FILENAME) + mv $(FILENAME) ../../out/$(FILENAME) + +$(FILENAME): $(OBJ) + $(info Linking $@) + $(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@ + +%.o: %.c $(HEADERS) + $(info Compiling $<) + $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ + +%.o: %.cpp $(HEADERS) + $(info Compiling $<) + $(CPP) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fno-exceptions -fno-rtti -c $< -o $@ + +%.o: %.S + $(info Compiling $<) + $(AS) -o $@ $< + +clean: + rm -f file.map $(OBJ) $(STACK_USAGE_OBJ) diff --git a/network/rtl8139/main.c b/network/rtl8139/main.c new file mode 100644 index 0000000..0e6e7da --- /dev/null +++ b/network/rtl8139/main.c @@ -0,0 +1,31 @@ +/* + 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 "rtl8139.hpp" + +int DriverEntry() { return cxx_Initialize(); } +int DriverFinal() { return cxx_Finalize(); } +int DriverPanic() { return cxx_Panic(); } +int DriverProbe() { return cxx_Probe(); } + +DriverInfo("rtl8139", + "Realtek RTL8139 Network Driver", + "EnderIce2", + "0.1", + "GPLv3"); diff --git a/network/rtl8139/rtl8139.cpp b/network/rtl8139/rtl8139.cpp new file mode 100644 index 0000000..8f4e413 --- /dev/null +++ b/network/rtl8139/rtl8139.cpp @@ -0,0 +1,298 @@ +/* + 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 +#include + +#include "rtl8139.hpp" + +class RTL8139Device +{ +private: + 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(TrapFrame *) + { + /* Acknowledge interrupt */ + uint16_t status = inw(RegISR); + DebugLog("%#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); + + /* 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(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 *)AllocateMemory(TO_PAGES(BufferSize)); + + /* Power on */ + outb(RegCONFIG1, 0x0); + + /* Software Reset */ + outb(RegCMD, 0x10); + while (inb(RegCMD) & 0x10) + Yield(); + + /* 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; + + /* FIXME: Shutdown code */ + } +}; + +RTL8139Device *Drivers[4] = {nullptr}; +dev_t AudioID[4] = {0}; + +#define OIR(x) OIR_##x +#define CREATE_OIR(x) \ + void OIR_##x(TrapFrame *f) { Drivers[x]->OnInterruptReceived(f); } + +CREATE_OIR(0); +CREATE_OIR(1); +CREATE_OIR(2); +CREATE_OIR(3); + +int drvOpen(dev_t, dev_t, int, mode_t) { return 0; } +int drvClose(dev_t, dev_t) { return 0; } +size_t drvRead(dev_t, dev_t, uint8_t *, size_t, off_t) { return 0; } + +size_t drvWrite(dev_t, dev_t min, uint8_t *Buffer, size_t Size, off_t) +{ + return Drivers[AudioID[min]]->write(Buffer, Size); +} + +int drvIoctl(dev_t, dev_t min, unsigned long Request, void *Argp) +{ + return Drivers[AudioID[min]]->ioctl((NetIoctl)Request, Argp); +} + +PCIArray *Devices; +EXTERNC int cxx_Panic() +{ + PCIArray *ctx = Devices; + short Count = 0; + while (ctx != nullptr) + { + if (Drivers[Count] != nullptr) + Drivers[Count]->Panic(); + Count++; + ctx = (PCIArray *)ctx->Next; + } + + return 0; +} + +EXTERNC int cxx_Probe() +{ + uint16_t VendorIDs[] = {0x10EC, /* Realtek */ + PCI_END}; + uint16_t DeviceIDs[] = {0x8139, /* RTL8139 */ + PCI_END}; + Devices = FindPCIDevices(VendorIDs, DeviceIDs); + if (Devices == nullptr) + { + Log("No RTL8139 device found."); + return -ENODEV; + } + return 0; +} + +EXTERNC int cxx_Initialize() +{ + PCIArray *ctx = Devices; + size_t Count = 0; + while (ctx != nullptr) + { + if (Count > sizeof(Drivers) / sizeof(RTL8139Device *)) + break; + + InitializePCI(ctx->Device); + + Drivers[Count] = new RTL8139Device((PCIHeader0 *)ctx->Device->Header); + + if (Drivers[Count]->IsInitialized()) + { + dev_t ret = RegisterNetDevice(ddt_Network, + drvOpen, drvClose, + drvRead, drvWrite, + drvIoctl); + AudioID[Count] = ret; + Drivers[Count]->ID = ret; + + /* FIXME: bad code */ + switch (Count) + { + case 0: + RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(0)); + break; + case 1: + RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(1)); + break; + case 2: + RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(2)); + break; + case 3: + RegisterInterruptHandler(iLine(ctx->Device), (void *)OIR(3)); + break; + default: + break; + } + + Count++; + } + ctx = (PCIArray *)ctx->Next; + } + + if (Count == 0) + { + Log("No valid RTL8139 device found."); + return -EINVAL; + } + + return 0; +} + +EXTERNC int cxx_Finalize() +{ + PCIArray *ctx = Devices; + size_t Count = 0; + while (ctx != nullptr) + { + if (Count++ > sizeof(Drivers) / sizeof(RTL8139Device *)) + break; + + delete Drivers[Count++]; + ctx->Device->Header->Command |= PCI_COMMAND_INTX_DISABLE; + ctx = (PCIArray *)ctx->Next; + } + + return 0; +} diff --git a/network/rtl8139/rtl8139.hpp b/network/rtl8139/rtl8139.hpp new file mode 100644 index 0000000..1f0e78d --- /dev/null +++ b/network/rtl8139/rtl8139.hpp @@ -0,0 +1,93 @@ +/* + 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 . +*/ + +#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, +}; + +EXTERNC int cxx_Panic(); +EXTERNC int cxx_Probe(); +EXTERNC int cxx_Initialize(); +EXTERNC int cxx_Finalize(); diff --git a/storage/Makefile b/storage/Makefile new file mode 100644 index 0000000..89dbd5f --- /dev/null +++ b/storage/Makefile @@ -0,0 +1,7 @@ +build: + make -C ahci build + make -C ata build + +clean: + make -C ahci clean + make -C ata clean diff --git a/storage/ahci/Makefile b/storage/ahci/Makefile new file mode 100644 index 0000000..fd47515 --- /dev/null +++ b/storage/ahci/Makefile @@ -0,0 +1,83 @@ +# Config file +include ../../../Makefile.conf + +FILENAME = ahci.drv + +CC = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)gcc +CPP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)g++ +LD = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)ld +AS = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)as +OBJDUMP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)objdump + +S_SOURCES = $(shell find ./ -type f -name '*.S') +C_SOURCES = $(shell find ./ -type f -name '*.c') +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp') +HEADERS = $(sort $(dir $(wildcard ../../include/*))) +OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o) +STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CPP_SOURCES:.cpp=.su) +INCLUDE_DIR = ../../include + +LIBS := ../../out/dcrt0.o -L../../out -ldriver + +LDFLAGS := \ + -fPIC -fPIE -pie \ + -Wl,--no-dynamic-linker,-ztext,--no-warn-rwx-segment \ + -nostdlib -nodefaultlibs -nolibc \ + -zmax-page-size=0x1000 \ + -Wl,-Map file.map -shared -fvisibility=hidden + +WARNCFLAG = -Wall -Wextra + +CFLAGS := -I$(INCLUDE_DIR) -fvisibility=hidden + +ifeq ($(OSARCH), amd64) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 \ + -march=x86-64 -pipe -ffunction-sections \ + -msoft-float -fno-builtin + +else ifeq ($(OSARCH), i386) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 -ffunction-sections \ + -march=i386 -pipe -msoft-float -fno-builtin + +else ifeq ($(OSARCH), aarch64) + +CFLAGS += -pipe -fno-builtin -fPIC + +endif + +ifeq ($(DEBUG), 1) + CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fstack-usage +ifeq ($(OSARCH), amd64) + CFLAGS += -fverbose-asm +endif +ifneq ($(OSARCH), aarch64) + CFLAGS += -fstack-check +endif + LDFLAGS += -ggdb3 -O0 +endif + +build: $(FILENAME) + mv $(FILENAME) ../../out/$(FILENAME) + +$(FILENAME): $(OBJ) + $(info Linking $@) + $(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@ + +%.o: %.c $(HEADERS) + $(info Compiling $<) + $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ + +%.o: %.cpp $(HEADERS) + $(info Compiling $<) + $(CPP) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fno-exceptions -fno-rtti -c $< -o $@ + +%.o: %.S + $(info Compiling $<) + $(AS) -o $@ $< + +clean: + rm -f file.map $(OBJ) $(STACK_USAGE_OBJ) diff --git a/storage/ahci/ahci.cpp b/storage/ahci/ahci.cpp new file mode 100644 index 0000000..54be554 --- /dev/null +++ b/storage/ahci/ahci.cpp @@ -0,0 +1,517 @@ +/* + 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 +#include +#include + +#define ATA_DEV_BUSY 0x80 +#define ATA_DEV_DRQ 0x08 +#define ATA_CMD_WRITE_DMA_EX 0x35 +#define ATA_CMD_READ_DMA_EX 0x25 +#define HBA_PxIS_TFES (1 << 30) + +#define HBA_PORT_DEV_PRESENT 0x3 +#define HBA_PORT_IPM_ACTIVE 0x1 +#define SATA_SIG_ATAPI 0xEB140101 +#define SATA_SIG_ATA 0x00000101 +#define SATA_SIG_SEMB 0xC33C0101 +#define SATA_SIG_PM 0x96690101 + +#define HBA_PxCMD_CR 0x8000 +#define HBA_PxCMD_FRE 0x0010 +#define HBA_PxCMD_ST 0x0001 +#define HBA_PxCMD_FR 0x4000 + +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]; +}; + +class Port +{ +public: + PortType AHCIPortType; + HBAPort *HBAPortPtr; + uint8_t *Buffer; + uint8_t PortNumber; + + Port(PortType Type, HBAPort *PortPtr, uint8_t PortNumber) + { + this->AHCIPortType = Type; + this->HBAPortPtr = PortPtr; + this->Buffer = static_cast(AllocateMemory(1)); + MemorySet(this->Buffer, 0, PAGE_SIZE); + this->PortNumber = PortNumber; + } + + ~Port() + { + FreeMemory(this->Buffer, 1); + } + + void StartCMD() + { + while (HBAPortPtr->CommandStatus & HBA_PxCMD_CR) + Yield(); + 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 = AllocateMemory(1); + HBAPortPtr->CommandListBase = (uint32_t)(uint64_t)CmdBase; + HBAPortPtr->CommandListBaseUpper = (uint32_t)((uint64_t)CmdBase >> 32); + MemorySet(reinterpret_cast(HBAPortPtr->CommandListBase), 0, 1024); + + void *FISBase = AllocateMemory(1); + HBAPortPtr->FISBaseAddress = (uint32_t)(uint64_t)FISBase; + HBAPortPtr->FISBaseAddressUpper = (uint32_t)((uint64_t)FISBase >> 32); + MemorySet(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 = AllocateMemory(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); + MemorySet(CommandTableAddress, 0, 256); + } + this->StartCMD(); + + Log("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, uint8_t *Buffer, bool Write) + { + if (this->AHCIPortType == PortType::SATAPI && + Write == true) + { + Log("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); + MemorySet(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 Spin = 0; + + while ((HBAPortPtr->TaskFileData & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && Spin < 1000000) + Spin++; + + if (Spin == 1000000) + { + Log("Port not responding."); + return false; + } + + HBAPortPtr->CommandIssue = 1; + + Spin = 0; + int TryCount = 0; + + while (true) + { + if (Spin > 100000000) + { + Log("Port %d not responding. (%d)", + this->PortNumber, TryCount); + + Spin = 0; + TryCount++; + if (TryCount > 10) + return false; + } + + if (HBAPortPtr->CommandIssue == 0) + break; + Spin++; + + if (HBAPortPtr->InterruptStatus & HBA_PxIS_TFES) + { + Log("Error reading/writing (%d).", Write); + return false; + } + } + + return true; + } +}; + +Port *Ports[64]; +int PortCount = 0; + +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; + } +} + +size_t drvRead(dev_t, dev_t min, + uint8_t *Buffer, size_t Size, off_t Offset) +{ + bool ok = Ports[min]->ReadWrite(Offset / 512, + uint32_t(Size / 512), + Buffer, + false); + return ok ? Size : 0; +} + +size_t drvWrite(dev_t, dev_t min, + uint8_t *Buffer, size_t Size, off_t Offset) +{ + bool ok = Ports[min]->ReadWrite(Offset / 512, + uint32_t(Size / 512), + Buffer, + true); + return ok ? Size : 0; +} + +void OnInterruptReceived(TrapFrame *) +{ +} + +EXTERNC int cxx_Panic() +{ + for (int i = 0; i < PortCount; i++) + Ports[i]->StopCMD(); + return 0; +} + +PCIArray *Devices; +EXTERNC int cxx_Probe() +{ + uint16_t VendorIDs[] = {0x8086, /* Intel */ + 0x15AD, /* VMware */ + PCI_END}; + uint16_t DeviceIDs[] = {0x2922, /* ICH9 */ + 0x2829, /* ICH8 */ + 0x07E0, /* SATA AHCI (VMware) */ + PCI_END}; + Devices = FindPCIDevices(VendorIDs, DeviceIDs); + if (Devices == nullptr) + { + Log("No AHCI device found."); + return -ENODEV; + } + return 0; +} + +EXTERNC int cxx_Initialize() +{ + PCIArray *ctx = Devices; + + /** + * We loop through all the devices and initialize them + */ + while (ctx != nullptr) + { + /* We don't use the interrupt handler now... maybe we will in the future */ + // RegisterInterruptHandler(iLine(ctx->Device), (void *)OnInterruptReceived); + + InitializePCI(ctx->Device); + HBAMemory *HBA = (HBAMemory *)(uintptr_t)GetBAR(5, ctx->Device); + + uint32_t PortsImplemented = HBA->PortsImplemented; + Log("AHCI ports implemented: %x", PortsImplemented); + for (int i = 0; i < 32; i++) + { + if (PortCount > 64) + { + Log("There are more than 64 AHCI ports implemented"); + break; + } + + if (PortsImplemented & (1 << i)) + { + Log("Port %d implemented", i); + PortType portType = CheckPortType(&HBA->Ports[i]); + if (portType == PortType::SATA || portType == PortType::SATAPI) + { + KPrint("%s drive found at port %d", PortTypeName[portType], i); + Ports[PortCount] = new Port(portType, &HBA->Ports[i], PortCount); + dev_t ret = RegisterBlockDevice(ddt_SATA, + nullptr, nullptr, + drvRead, drvWrite, + nullptr); + if (ret != (dev_t)PortCount) + { + KPrint("Failed to register block device %d", ret); + return -EBADSLT; + } + + PortCount++; + Ports[PortCount] = nullptr; + } + else + { + if (portType != PortType::None) + { + KPrint("Unsupported drive type %s found at port %d", + PortTypeName[portType], i); + } + } + } + } + + ctx = (PCIArray *)ctx->Next; + } + + Log("Initializing AHCI ports"); + for (int i = 0; i < PortCount; i++) + Ports[i]->Configure(); + + return PortCount > 0 ? 0 : -ENODEV; +} + +EXTERNC int cxx_Finalize() +{ + for (int i = 0; i < PortCount; i++) + { + Ports[i]->StopCMD(); + delete Ports[i]; + } + + PortCount = 0; + + do + { + UnregisterBlockDevice(PortCount, ddt_SATA); + PortCount--; + } while (PortCount >= 0); + + PCIArray *ctx = Devices; + + while (ctx != nullptr) + { + ctx->Device->Header->Command |= PCI_COMMAND_INTX_DISABLE; + ctx = (PCIArray *)ctx->Next; + } + + // std::list Devices = PCIManager->FindPCIDevice(VendorIDs, DeviceIDs); + // foreach (auto dev in Devices) + // Interrupts::RemoveHandler(OnInterruptReceived, iLine(dev)); + return 0; +} diff --git a/storage/ahci/ahci.hpp b/storage/ahci/ahci.hpp new file mode 100644 index 0000000..99242b9 --- /dev/null +++ b/storage/ahci/ahci.hpp @@ -0,0 +1,25 @@ +/* + 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 . +*/ + +#pragma once + +#include + +EXTERNC int cxx_Panic(); +EXTERNC int cxx_Probe(); +EXTERNC int cxx_Initialize(); +EXTERNC int cxx_Finalize(); diff --git a/storage/ahci/main.c b/storage/ahci/main.c new file mode 100644 index 0000000..8bac95a --- /dev/null +++ b/storage/ahci/main.c @@ -0,0 +1,31 @@ +/* + 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 "ahci.hpp" + +int DriverEntry() { return cxx_Initialize(); } +int DriverFinal() { return cxx_Finalize(); } +int DriverPanic() { return cxx_Panic(); } +int DriverProbe() { return cxx_Probe(); } + +DriverInfo("ahci", + "Advanced Host Controller Interface Driver", + "EnderIce2", + "0.1", + "GPLv3"); diff --git a/storage/ata/Makefile b/storage/ata/Makefile new file mode 100644 index 0000000..f712d0a --- /dev/null +++ b/storage/ata/Makefile @@ -0,0 +1,83 @@ +# Config file +include ../../../Makefile.conf + +FILENAME = ata.drv + +CC = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)gcc +CPP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)g++ +LD = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)ld +AS = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)as +OBJDUMP = ../../../$(COMPILER_PATH)/$(COMPILER_ARCH)objdump + +S_SOURCES = $(shell find ./ -type f -name '*.S') +C_SOURCES = $(shell find ./ -type f -name '*.c') +CPP_SOURCES = $(shell find ./ -type f -name '*.cpp') +HEADERS = $(sort $(dir $(wildcard ../../include/*))) +OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o) +STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CPP_SOURCES:.cpp=.su) +INCLUDE_DIR = ../../include + +LIBS := ../../out/dcrt0.o -L../../out -ldriver + +LDFLAGS := \ + -fPIC -fPIE -pie \ + -Wl,--no-dynamic-linker,-ztext,--no-warn-rwx-segment \ + -nostdlib -nodefaultlibs -nolibc \ + -zmax-page-size=0x1000 \ + -Wl,-Map file.map -shared -fvisibility=hidden + +WARNCFLAG = -Wall -Wextra + +CFLAGS := -I$(INCLUDE_DIR) -fvisibility=hidden + +ifeq ($(OSARCH), amd64) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 \ + -march=x86-64 -pipe -ffunction-sections \ + -msoft-float -fno-builtin + +else ifeq ($(OSARCH), i386) + +CFLAGS += -fPIC -fPIE -pie -mno-80387 -mno-mmx -mno-3dnow \ + -mno-red-zone -mno-sse -mno-sse2 -ffunction-sections \ + -march=i386 -pipe -msoft-float -fno-builtin + +else ifeq ($(OSARCH), aarch64) + +CFLAGS += -pipe -fno-builtin -fPIC + +endif + +ifeq ($(DEBUG), 1) + CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fstack-usage +ifeq ($(OSARCH), amd64) + CFLAGS += -fverbose-asm +endif +ifneq ($(OSARCH), aarch64) + CFLAGS += -fstack-check +endif + LDFLAGS += -ggdb3 -O0 +endif + +build: $(FILENAME) + mv $(FILENAME) ../../out/$(FILENAME) + +$(FILENAME): $(OBJ) + $(info Linking $@) + $(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@ + +%.o: %.c $(HEADERS) + $(info Compiling $<) + $(CC) $(CFLAGS) $(WARNCFLAG) -std=c17 -c $< -o $@ + +%.o: %.cpp $(HEADERS) + $(info Compiling $<) + $(CPP) $(CFLAGS) $(WARNCFLAG) -std=c++20 -fno-exceptions -fno-rtti -c $< -o $@ + +%.o: %.S + $(info Compiling $<) + $(AS) -o $@ $< + +clean: + rm -f file.map $(OBJ) $(STACK_USAGE_OBJ) diff --git a/storage/ata/main.c b/storage/ata/main.c new file mode 100644 index 0000000..300dab1 --- /dev/null +++ b/storage/ata/main.c @@ -0,0 +1,76 @@ +/* + 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 +#include + +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; +} + +void MasterInterruptHandler(TrapFrame *) +{ +} + +void SlaveInterruptHandler(TrapFrame *) +{ +} + +int DriverEntry() +{ + RegisterInterruptHandler(14, MasterInterruptHandler); + RegisterInterruptHandler(15, SlaveInterruptHandler); + + return 0; +} + +int DriverFinal() +{ + UnregisterInterruptHandler(14, MasterInterruptHandler); + UnregisterInterruptHandler(15, SlaveInterruptHandler); + + return 0; +} + +int DriverPanic() +{ + return 0; +} + +int DriverProbe() +{ + if (!IsATAPresent()) + return -ENODEV; + + return 0; +} + +DriverInfo("ata", + "Advanced Technology Attachment Driver", + "EnderIce2", + "0.1", + "GPLv3");