diff --git a/.clang-format b/.clang-format index db7b4db..fcc0cd7 100644 --- a/.clang-format +++ b/.clang-format @@ -11,23 +11,24 @@ --- AccessModifierOffset: -4 AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: false +AlignConsecutiveAssignments: true AlignConsecutiveDeclarations: false +AlignConsecutiveMacros: true #AlignEscapedNewlines: Left # Unknown to clang-format-4.0 -AlignOperands: true -AlignTrailingComments: false +AlignOperands: AlignAfterOperator +AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: false -AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: None -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: true +AllowShortBlocksOnASingleLine: Always AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: false +AlwaysBreakTemplateDeclarations: Yes BinPackArguments: true -BinPackParameters: true +BinPackParameters: false BraceWrapping: AfterClass: false AfterControlStatement: false @@ -52,7 +53,7 @@ BreakConstructorInitializersBeforeComma: false #BreakConstructorInitializers: BeforeComma # Unknown to clang-format-4.0 BreakAfterJavaFieldAnnotations: false BreakStringLiterals: false -ColumnLimit: 80 +ColumnLimit: 0 CommentPragmas: '^ IWYU pragma:' #CompactNamespaces: false # Unknown to clang-format-4.0 ConstructorInitializerAllOnOneLineOrOnePerLine: false @@ -463,7 +464,7 @@ SpacesInContainerLiterals: false SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false -Standard: Cpp03 +Standard: Auto TabWidth: 4 -UseTab: Always +UseTab: Never ... diff --git a/.gitignore b/.gitignore index 54484f9..fa3360d 100644 --- a/.gitignore +++ b/.gitignore @@ -99,3 +99,6 @@ src/initscp/initfscp # OS generated files .DS_Store + +# OS filesystem files. +files/bin/** \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index e6c3768..88509f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,111 +1,158 @@ -# ------------------------------------------------------------------------------ +# ============================================================================= +# Set the minimum required version of cmake. +cmake_minimum_required(VERSION 2.8) # Initialize the project. project(MentOs) -cmake_minimum_required(VERSION 2.8.4) -message(STATUS "Crosscompiling: ${CMAKE_CROSSCOMPILING}") - -# ------------------------------------------------------------------------------ -# Add operating system specific option. -message(STATUS "System name: ${CMAKE_HOST_SYSTEM_NAME}") -message(STATUS "Kernel version: ${CMAKE_SYSTEM_VERSION}") -if ((${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin") OR APPLE) - # Apple MacOSx -elseif ((${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows") OR WIN32) - # Windows - set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -sdl) -else() - # Generic Unix System - find_program(LSB_RELEASE_EXEC lsb_release) - execute_process(COMMAND "${LSB_RELEASE_EXEC}" --short --release - OUTPUT_VARIABLE LSB_RELEASE_VERSION_SHORT - OUTPUT_STRIP_TRAILING_WHITESPACE) - message(STATUS "LSB version: ${LSB_RELEASE_VERSION_SHORT}") - if (${LSB_RELEASE_VERSION_SHORT} MATCHES "^18") - # Ubuntu 18 - set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -sdl) - elseif (${LSB_RELEASE_VERSION_SHORT} MATCHES "^19") - # Ubuntu 19 - set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -display gtk) - elseif (${LSB_RELEASE_VERSION_SHORT} MATCHES "^20") - # Ubuntu 20 - set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -display gtk) - else() - set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -sdl) - endif() +# ============================================================================= +# Set the default build type to Debug. +if(NOT CMAKE_BUILD_TYPE) + message(STATUS "Setting build type to 'Debug' as none was specified.") + set(CMAKE_BUILD_TYPE + "Debug" + CACHE STRING "Choose the type of build." FORCE) endif() -# ------------------------------------------------------------------------------ -# Add the debugging option. -set(DEBUGGING_TYPE "DEBUG_STDIO" CACHE STRING - "Chose the type of debugging: DEBUG_STDIO DEBUG_LOG") -set_property( - CACHE DEBUGGING_TYPE PROPERTY STRINGS - DEBUG_STDIO - DEBUG_LOG) -if ("${DEBUGGING_TYPE}" STREQUAL "DEBUG_STDIO" OR - "${DEBUGGING_TYPE}" STREQUAL "DEBUG_LOG") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D${DEBUGGING_TYPE}") - message(STATUS "Setting debugging type to ${DEBUGGING_TYPE}.") -else () - message(FATAL_ERROR "Debugging type ${DEBUGGING_TYPE} is not valid.") -endif () +# ============================================================================= +# Add operating system specific option. +message(STATUS "Crosscompiling : ${CMAKE_CROSSCOMPILING}") +message(STATUS "System name : ${CMAKE_HOST_SYSTEM_NAME}") +message(STATUS "Kernel version : ${CMAKE_SYSTEM_VERSION}") +if((${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin") OR APPLE) + # Set the Apple MacOSx compilers. + if(CMAKE_VERSION VERSION_LESS "3.6.0") + include(CMakeForceCompiler) + cmake_force_c_compiler(i386-elf-gcc Clang) + cmake_force_cxx_compiler(i386-elf-g++ Clang) + else() + set(CMAKE_C_COMPILER i386-elf-gcc) + set(CMAKE_CXX_COMPILER i386-elf-g++) + set(CMAKE_AR i386-elf-ar) + endif() + # Speicfy the linker. + set(CMAKE_LINKER i386-elf-ld) + # Specify the linker flags. + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -nostdlib") -# ------------------------------------------------------------------------------ +elseif((${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows") OR WIN32) + # Windows set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -sdl) + +else() + # Generic Unix System + find_program(LSB_RELEASE_EXEC lsb_release) + execute_process( + COMMAND "${LSB_RELEASE_EXEC}" --short --release + OUTPUT_VARIABLE LSB_RELEASE_VERSION_SHORT + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "LSB version : ${LSB_RELEASE_VERSION_SHORT}") + if(${LSB_RELEASE_VERSION_SHORT} MATCHES "^18") + # Ubuntu 18 set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -sdl) + elseif(${LSB_RELEASE_VERSION_SHORT} MATCHES "^19") + # Ubuntu 19 + set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -display gtk) + elseif(${LSB_RELEASE_VERSION_SHORT} MATCHES "^20") + # Ubuntu 20 + set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -display gtk) + else() + # set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -sdl) + endif() +endif() + +# ============================================================================= +# Add the debugging option. +set(DEBUGGING_TYPE + "DEBUG_STDIO" + CACHE STRING "Chose the type of debugging: DEBUG_STDIO DEBUG_LOG") +set_property(CACHE DEBUGGING_TYPE PROPERTY STRINGS DEBUG_STDIO DEBUG_LOG) +if("${DEBUGGING_TYPE}" STREQUAL "DEBUG_STDIO" OR "${DEBUGGING_TYPE}" STREQUAL "DEBUG_LOG") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D${DEBUGGING_TYPE}") + message(STATUS "Setting debugging type to ${DEBUGGING_TYPE}.") +else() + message(FATAL_ERROR "Debugging type ${DEBUGGING_TYPE} is not valid.") +endif() + +# ============================================================================= # Add the sub-directories. +add_subdirectory(programs) +add_subdirectory(programs/tests) add_subdirectory(mentos) add_subdirectory(initscp) +add_subdirectory(libc) +add_subdirectory(doc) -# ------------------------------------------------------------------------------ -# Generate the initrd filesystem. +# ============================================================================= +# Target to generate the initrd filesystem. add_custom_target( - initfs - COMMAND echo "---------------------------------------------" - COMMAND echo "Initializing 'initfs'..." - COMMAND echo "---------------------------------------------" - COMMAND ./initscp/initfscp -s ${CMAKE_SOURCE_DIR}/files -t ${CMAKE_BINARY_DIR}/initfs -m /dev - COMMAND echo "---------------------------------------------" - COMMAND echo "Done!" - COMMAND echo "---------------------------------------------" - DEPENDS initfscp -) + initrd + COMMAND echo "---------------------------------------------" + COMMAND echo "Initializing 'initrd'..." + COMMAND echo "---------------------------------------------" + COMMAND initfscp -s ${CMAKE_SOURCE_DIR}/files -t ${CMAKE_BINARY_DIR}/initrd -m /dev + COMMAND echo "---------------------------------------------" + COMMAND echo "Done!" + COMMAND echo "---------------------------------------------" + DEPENDS initfscp + DEPENDS all_programs all_tests) -# ------------------------------------------------------------------------------ +# ============================================================================= +# Update GDB symbol file. +add_custom_target( + gdb_file + COMMAND echo "symbol-file ${CMAKE_BINARY_DIR}/mentos/kernel-bootloader.bin" >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND echo "exec-file ${CMAKE_BINARY_DIR}/mentos/kernel-bootloader.bin" >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND bash ${CMAKE_SOURCE_DIR}/scripts/get_text_address.sh ${CMAKE_BINARY_DIR}/mentos/kernel.bin > ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND bash ${CMAKE_SOURCE_DIR}/scripts/get_text_address.sh ${CMAKE_BINARY_DIR}/mentos/kernel-bootloader.bin >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND bash ${CMAKE_SOURCE_DIR}/scripts/get_text_address.sh ${CMAKE_BINARY_DIR}/programs/tests/test_* >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND bash ${CMAKE_SOURCE_DIR}/scripts/get_text_address.sh ${CMAKE_BINARY_DIR}/programs/prog_* >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND echo "break boot.c: boot_main" >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND echo "break kernel.c: kmain" >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND echo "target remote localhost:1234" >> ${CMAKE_BINARY_DIR}/.gdbinit + DEPENDS ${PROJECT_NAME}_bootloader + DEPENDS all_programs all_tests + DEPENDS libc) + +# ============================================================================= +# Update GDB symbol file. +add_custom_target( + gdb_file_clion_embedded + COMMAND echo "symbol-file ${CMAKE_BINARY_DIR}/mentos/kernel-bootloader.bin" >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND echo "exec-file ${CMAKE_BINARY_DIR}/mentos/kernel-bootloader.bin" >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND bash ${CMAKE_SOURCE_DIR}/scripts/get_text_address.sh ${CMAKE_BINARY_DIR}/mentos/kernel.bin > ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND bash ${CMAKE_SOURCE_DIR}/scripts/get_text_address.sh ${CMAKE_BINARY_DIR}/mentos/kernel-bootloader.bin >> ${CMAKE_BINARY_DIR}/.gdbinit + COMMAND bash ${CMAKE_SOURCE_DIR}/scripts/get_text_address.sh ${CMAKE_BINARY_DIR}/programs/prog_* >> ${CMAKE_BINARY_DIR}/.gdbinit + DEPENDS ${PROJECT_NAME}_bootloader + DEPENDS all_programs all_tests + DEPENDS libc) + +# ============================================================================= # Set memory size. -SET(MEMORY_SIZE 1096M) +set(MEMORY_SIZE 1096M) -# ------------------------------------------------------------------------------ +# ============================================================================= # Builds the code and runs qemu with the built Os. -SET(EMULATOR qemu-system-i386) -if (${DEBUGGING_TYPE} STREQUAL DEBUG_LOG) - SET(EMULATOR_FLAGS ${EMULATOR_FLAGS} -serial file:serial.log) -elseif (${DEBUGGING_TYPE} STREQUAL DEBUG_STDIO) - SET(EMULATOR_FLAGS ${EMULATOR_FLAGS} -serial stdio) -endif () -SET(EMULATOR_FLAGS ${EMULATOR_FLAGS} -vga std) -SET(EMULATOR_FLAGS ${EMULATOR_FLAGS} -k it) -SET(EMULATOR_FLAGS ${EMULATOR_FLAGS} -m ${MEMORY_SIZE}) -SET(EMULATOR_KERNEL -kernel mentos/kernel.bin) -SET(EMULATOR_FS -initrd initfs) +set(EMULATOR qemu-system-i386) +if(${DEBUGGING_TYPE} STREQUAL DEBUG_LOG) + set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -serial file:serial.log) +elseif(${DEBUGGING_TYPE} STREQUAL DEBUG_STDIO) + set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -serial stdio) +endif(${DEBUGGING_TYPE} STREQUAL DEBUG_LOG) +set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -vga std) +set(EMULATOR_FLAGS ${EMULATOR_FLAGS} -m ${MEMORY_SIZE}) +set(EMULATOR_KERNEL -kernel mentos/kernel-bootloader.bin) +set(EMULATOR_FS -initrd initrd) add_custom_target( - qemu - COMMAND ${EMULATOR} ${EMULATOR_FLAGS} ${EMULATOR_KERNEL} ${EMULATOR_FS} - DEPENDS MentOs - DEPENDS initfs -) + qemu + COMMAND ${EMULATOR} ${EMULATOR_FLAGS} ${EMULATOR_KERNEL} ${EMULATOR_FS} + DEPENDS kernel-bootloader.bin initrd all_programs all_tests libc) -# ------------------------------------------------------------------------------ +# ============================================================================= # Builds the code and runs qemu with the built Os. -SET(EMULATOR_FLAGS_GDB ${EMULATOR_FLAGS} -s -S) -SET(GDB_PROG cgdb) -SET(GDB_ARGS -x ${CMAKE_SOURCE_DIR}/.gdb.debug) +set(EMULATOR_FLAGS_GDB ${EMULATOR_FLAGS} -s -S) add_custom_target( - qemu-gdb - COMMAND xterm -geometry 160x30-0-0 -e ${EMULATOR} ${EMULATOR_FLAGS_GDB} ${EMULATOR_KERNEL} ${EMULATOR_FS} & - COMMAND xterm -geometry 160x30-0+0 -e ${GDB_PROG} ${GDB_ARGS} & - DEPENDS MentOs - DEPENDS initfs -) + qemu-gdb + COMMAND ${EMULATOR} ${EMULATOR_FLAGS_GDB} ${EMULATOR_KERNEL} ${EMULATOR_FS} & + DEPENDS gdb_file kernel-bootloader.bin initrd) +add_custom_target(all-execs-clion-embedded DEPENDS gdb_file_clion_embedded kernel-bootloader.bin initrd) diff --git a/CODING_STYLE.md b/CODING_STYLE.md index fe053ac..d8b7271 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -1,7 +1,4 @@ -Code Styles for this project -============================ - - +# Coding Style If you'd like to contribute you are welcome, thus refer to the following coding styles please: diff --git a/README.md b/README.md index b3b8604..1b9b584 100644 --- a/README.md +++ b/README.md @@ -1,95 +1,93 @@ -MentOS -====== +# MentOS [![forthebadge](https://forthebadge.com/images/badges/built-with-love.svg)](https://forthebadge.com) [![forthebadge](https://forthebadge.com/images/badges/made-with-c.svg)](https://forthebadge.com) [![forthebadge](https://forthebadge.com/images/badges/for-you.svg)](https://forthebadge.com) -What is MentOS ------------------ +## 1. What is MentOS -MentOS (Mentoring Operating system) is an open source educational operating -system. -The goal of MentOS is to provide a project environment that is realistic -enough to show how a real Operating System work, yet simple enough that -students can understand and modify it in significant ways. +MentOS (Mentoring Operating System) is an open source educational operating +system. The goal of MentOS is to provide a project environment that is realistic +enough to show how a real Operating System work, yet simple enough that students +can understand and modify it in significant ways. -There are so many operating systems, why did we write MentOS? -It is true, there are a lot of education operating system, BUT -how many of them follow the guideline de fined by Linux? +There are so many operating systems, why did we write MentOS? It is true, there +are a lot of education operating system, BUT how many of them follow the +guideline de fined by Linux? + +MentOS aims to have the same Linux's data structures and algorithms. It has a +well-documented source code, and you can compile it on your laptop in a few +seconds! -MentOS aims to have the same Linux's data structures and algorithms. It -has a well-documented source code, and you can compile it on your laptop -in a few seconds! If you are a beginner in Operating-System developing, perhaps MentOS is the right operating system to start with. +Parts of MentOS are inherited or inspired by a similar educational operating +system called [DreamOs](https://github.com/dreamos82/DreamOs) written by Ivan +Gualandri. -Developers ----------------- -Main Developers: +## 2. Prerequisites - * [Enrico Fraccaroli](https://github.com/Galfurian) - * [Alessandro Danese](https://github.com/alessandroDanese88) - * [Luigi Capogrosso](https://github.com/luigicapogrosso) - * [Mirco De Marchi](https://github.com/mircodemarchi) +MentOS is compatible with the main **unix-based** operating systems. It has been +tested with *Ubuntu*, *WSL1*, *WSL2*, and *MacOS*. -Prerequisites ------------------ +### 2.1. Generic Prerequisites -MentOS is compatible with the main Unix distribution operating systems. It has been tested with *Ubuntu* and *MacOS*, but specifically tested on *Ubuntu 18.04*. +#### 3.1.1. Compile -For compiling the main system: +For compiling the system: - * nasm - * gcc - * g++ - * make - * cmake - * git + - nasm + - gcc + - make + - cmake + - git + - ccmake (suggested) -To run and try: +Under **MacOS**, for compiling, you have additional dependencies: - * qemu-system-i386 + - i386-elf-binutils + - i386-elf-gcc -For debugging: +#### 3.1.2. Execute - * ccmake - * cgdb - * xterm +To execute the operating system, you need to install: -For MacOS, you have additional dependencies: + - qemu-system-i386 (or qemu-system-x86) - * i386-elf-binutils (from [i386-elf-toolchain](https://github.com/nativeos/homebrew-i386-elf-toolchain)) - * i386-elf-gcc (from [i386-elf-toolchain](https://github.com/nativeos/homebrew-i386-elf-toolchain)) +#### 3.1.3. Debug -Prerequisites installation commands ------------------ +For debugging we suggest using: -For Ubuntu: + - cgdb + - xterm -``` +### 2.2. installation Prerequisites + +Under **Ubuntu**, you can type the following commands: + +```bash sudo apt-get update && sudo apt-get upgrade -y sudo apt-get install -y build-essential git cmake qemu-system-i386 -sudo apt-get install -y cgdb xterm #<- for debug only +sudo apt-get install -y cgdb xterm ``` -For MacOS: +Under **MacOS** you also need to install the i386-elf cross-compiler. The +simplest installation method is through Homebrew package manager. +Install [Homebrew](https://brew.sh/index_it) if you don't already have it, and +then type the following commands: -You need to install additionally the i386-elf cross-compiler. The simplest installation method is through Homebrew package manager. Install [Homebrew](https://brew.sh/index_it) if you don't already have and exec the following commands: - -``` +```bash brew update && brew upgrade -brew tap nativeos/i386-elf-toolchain brew install i386-elf-binutils i386-elf-gcc git cmake qemu nasm brew install cgdb xterm #<- for debug only ``` -Compiling MentOS ------------------ -Compile and boot MentOS with qemu in Unix systems: +## 3. Compiling MentOS -``` +Compile and boot MentOS with qemu: + +```bash cd mkdir build cd build @@ -98,26 +96,20 @@ make make qemu ``` -If you want to access to the shell, use one of the usernames listed in files/passwd. +To login, use one of the usernames listed in `files/etc/passwd`. -**For MacOS**, the steps are the same but instead of `cmake ..`, you have to put an additional argument in order to use the i386-elf cross-compiler: - -``` -cmake -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake .. -``` - -Change the scheduling algorithm ------------------ +## 4. Change the scheduling algorithm MentOS provides three different scheduling algorithms: -* Round-Robin -* Priority -* Completely Fair Scheduling + - Round-Robin + - Priority + - Completely Fair Scheduling If you want to change the scheduling algorithm: -``` +```bash + cd build # Round Robin scheduling algorithm @@ -133,7 +125,7 @@ make qemu Otherwise you can use `ccmake`: -``` +```bash cd build cmake .. ccmake .. @@ -151,72 +143,20 @@ SCHEDULER_TYPE SCHEDULER_RR ``` Select SCHEDULER_TYPE, and type Enter to scroll the three available algorithms -(SCHEDULER_RR, SCHEDULER_PRIORITY, SCHEDULER_CFS). -Afterwards, -``` -type c -type g +(SCHEDULER_RR, SCHEDULER_PRIORITY, SCHEDULER_CFS). Afterwards, + +```bash + + make make qemu ``` -Enable to Buddy System ------------------ +## 5. Use Debugger -MentOS provides a Buddy System to manage the allocation and deallocation of -page frames in the physical memory. - -If you want to enable the MentOS's Buddy System: - -``` -cd build -cmake -DENABLE_BUDDY_SYSTEM=ON .. -make -make qemu -``` - -Otherwise you can use `ccmake`: - -``` -cd build -cmake .. -ccmake .. -``` - -Now you should see something like this: - -``` -BUILD_DOCUMENTATION ON -CMAKE_BUILD_TYPE -CMAKE_INSTALL_PREFIX /usr/local -DEBUGGING_TYPE DEBUG_STDIO -ENABLE_BUDDY_SYSTEM OFF -SCHEDULER_TYPE SCHEDULER_RR -``` - -Select ENABLE_BUDDY_SYSTEM, and type Enter. -You should see something like this: -``` -BUILD_DOCUMENTATION ON -CMAKE_BUILD_TYPE -CMAKE_INSTALL_PREFIX /usr/local -DEBUGGING_TYPE DEBUG_STDIO -ENABLE_BUDDY_SYSTEM ON -SCHEDULER_TYPE SCHEDULER_RR -``` - -Afterwards, -``` -type c -type g -make -make qemu -``` - -Use Debugger ------------------ If you want to use GDB to debug MentOS: -``` + +```bash cd build cmake .. make @@ -224,8 +164,29 @@ make qemu-gdb ``` If you did everything correctly, you should have 3 windows with: -``` -1) - Kernel Booting on qemu -2) - Shell with video printing of statistics previously discussed -3) - Debugger cgdb with code screening -``` + +1. Kernel Booting on qemu +2. Shell with video printing of statistics previously discussed +3. Debugger cgdb with code screening + +## Developers + +Main Developers: + +* [Enrico Fraccaroli](https://github.com/Galfurian) + - Mr. Wolf +* [Alessandro Danese](https://github.com/alessandroDanese88), [Luigi Capogrosso](https://github.com/luigicapogrosso), [Mirco De Marchi](https://github.com/mircodemarchi) + - Protection ring + - libc +* Andrea Cracco + - Buddy System, Heap, Paging, Slab, Caching, Zone + - Process Image, ELF + - VFS: procfs + - Bootloader +* Linda Sacchetto, Marco Berti + - Real time scheduler +* Daniele Nicoletti, Filippo Ziche + - Real time scheduler (Asynchronous EDF) + - Soft IRQs + - Timer + - Signals diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..da8887d --- /dev/null +++ b/TODO.md @@ -0,0 +1 @@ +# TODOs diff --git a/doc/CMakeCache.txt b/doc/CMakeCache.txt deleted file mode 100644 index 59c17fd..0000000 --- a/doc/CMakeCache.txt +++ /dev/null @@ -1,38 +0,0 @@ -# This is the CMakeCache file. -# For build in directory: /home/enrico/Repositories/University/DreamOs/doc -# It was generated by CMake: /usr/bin/cmake -# You can edit this file to change values found and used by cmake. -# If you do not want to change any of the values, simply exit the editor. -# If you do want to change a value, simply edit, save, and exit the editor. -# The syntax for the file is as follows: -# KEY:TYPE=VALUE -# KEY is the name of a variable in the cache. -# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!. -# VALUE is the current value for the KEY. - -######################## -# EXTERNAL cache entries -######################## - - -######################## -# INTERNAL cache entries -######################## - -//This is the directory where this CMakeCache.txt was created -CMAKE_CACHEFILE_DIR:INTERNAL=/home/enrico/Repositories/University/DreamOs/doc -//Major version of cmake used to create the current loaded cache -CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3 -//Minor version of cmake used to create the current loaded cache -CMAKE_CACHE_MINOR_VERSION:INTERNAL=5 -//Patch version of cmake used to create the current loaded cache -CMAKE_CACHE_PATCH_VERSION:INTERNAL=1 -//Path to CMake executable. -CMAKE_COMMAND:INTERNAL=/usr/bin/cmake -//Path to cpack program executable. -CMAKE_CPACK_COMMAND:INTERNAL=/usr/bin/cpack -//Path to ctest program executable. -CMAKE_CTEST_COMMAND:INTERNAL=/usr/bin/ctest -//Path to CMake installation. -CMAKE_ROOT:INTERNAL=/usr/share/cmake-3.5 - diff --git a/doc/CMakeFiles/cmake.check_cache b/doc/CMakeFiles/cmake.check_cache deleted file mode 100644 index 3dccd73..0000000 --- a/doc/CMakeFiles/cmake.check_cache +++ /dev/null @@ -1 +0,0 @@ -# This file is generated by cmake for dependency checking of the CMakeCache.txt file diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt new file mode 100644 index 0000000..c5d3905 --- /dev/null +++ b/doc/CMakeLists.txt @@ -0,0 +1,37 @@ +# Find Doxygen +find_package(Doxygen) + +if (DOXYGEN_FOUND) + set(DOXYGEN_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/doc_doxygen) + set(DOXYGEN_INDEX_FILE ${DOXYGEN_OUTPUT_DIR}/html/index.html) + + # Replace variables inside @@ with the current values. + set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxygen) + set(DOXYFILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxygen) + configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY) + + # Copy the files needd by the documentation. + set(DOXYFILE_CSS ${CMAKE_CURRENT_SOURCE_DIR}/doxygen.css) + set(DOXYFILE_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/header.html) + set(DOXYFILE_FOOTER ${CMAKE_CURRENT_SOURCE_DIR}/footer.html) + + # Doxygen won't create this for us. + file(MAKE_DIRECTORY ${DOXYGEN_OUTPUT_DIR}) + + add_custom_command( + OUTPUT ${DOXYGEN_INDEX_FILE} + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYFILE_OUT} + + MAIN_DEPENDENCY ${DOXYFILE_OUT} ${DOXYFILE_IN} + COMMENT "Generating docs" + ) + add_custom_target( + Doxygen + DEPENDS + ${DOXYGEN_INDEX_FILE} + ${DOXYFILE_IN} + ${DOXYFILE_CSS} + ${DOXYFILE_HEADER} + ${DOXYFILE_FOOTER} + ) +endif (DOXYGEN_FOUND) diff --git a/doc/dreamos.doxyfile b/doc/Doxygen similarity index 91% rename from doc/dreamos.doxyfile rename to doc/Doxygen index dded7d7..bc4c7bb 100644 --- a/doc/dreamos.doxyfile +++ b/doc/Doxygen @@ -1,4 +1,4 @@ -# Doxyfile 1.8.11 +# Doxyfile 1.8.17 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -17,11 +17,11 @@ # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 @@ -32,19 +32,19 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "MentOS" +PROJECT_NAME = MentOS # 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 # control system is used. -PROJECT_NUMBER = +PROJECT_NUMBER = 0.3.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # 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 = +PROJECT_BRIEF = "The Mentoring Operating System" # 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 @@ -58,7 +58,7 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = +OUTPUT_DIRECTORY = "@DOXYGEN_OUTPUT_DIR@" # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and @@ -93,6 +93,14 @@ ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. @@ -152,7 +160,7 @@ FULL_PATH_NAMES = YES # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = +STRIP_FROM_PATH = @CMAKE_SOURCE_DIR@ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which @@ -179,6 +187,16 @@ SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus @@ -226,7 +244,12 @@ TAB_SIZE = 4 # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) ALIASES = @@ -242,7 +265,7 @@ TCL_SUBST = # members will be omitted, etc. # The default value is: NO. -OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored @@ -264,17 +287,26 @@ OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: -# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: -# Fortran. In the later case the parser tries to guess whether the code is fixed -# or free formatted code, this is the default for Fortran type files), VHDL. For -# instance to make doxygen treat .inc files as Fortran files (default is PHP), -# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files), VHDL, tcl. For instance to make doxygen treat +# .inc files as Fortran files (default is PHP), and .f files as C (default is +# Fortran), use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # @@ -285,7 +317,7 @@ EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. +# documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. @@ -293,6 +325,15 @@ EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or @@ -318,7 +359,7 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. @@ -424,6 +465,12 @@ EXTRACT_ALL = NO EXTRACT_PRIVATE = NO +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. @@ -478,8 +525,8 @@ HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO, these declarations will be -# included in the documentation. +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO @@ -502,7 +549,7 @@ INTERNAL_DOCS = NO # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. +# (including Cygwin) ands Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES @@ -689,7 +736,7 @@ LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. @@ -734,10 +781,11 @@ WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. -WARN_NO_PARAMDOC = NO +WARN_NO_PARAMDOC = YES # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. @@ -759,7 +807,7 @@ WARN_FORMAT = "$file:$line: $text" # messages should be written. If left blank the output is written to standard # error (stderr). -WARN_LOGFILE = mentos.log +WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files @@ -771,12 +819,19 @@ WARN_LOGFILE = mentos.log # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = . ../README.md ../src/mentos +INPUT = @CMAKE_SOURCE_DIR@/README.md \ + @CMAKE_SOURCE_DIR@/CODING_STYLE.md \ + @CMAKE_SOURCE_DIR@/LICENSE.md \ + @CMAKE_SOURCE_DIR@/TODO.md \ + @CMAKE_SOURCE_DIR@/doc/signal.md \ + @CMAKE_SOURCE_DIR@/doc/syscall.md \ + @CMAKE_SOURCE_DIR@/mentos \ + @CMAKE_SOURCE_DIR@/libc # 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 # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of # possible encodings. # The default value is: UTF-8. @@ -793,10 +848,12 @@ INPUT_ENCODING = UTF-8 # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl, -# *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js. +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen +# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. -FILE_PATTERNS = +FILE_PATTERNS = *.c *.h # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. @@ -864,7 +921,7 @@ EXAMPLE_RECURSIVE = NO # that contain images that are to be included in the documentation (see the # \image command). -IMAGE_PATH = +IMAGE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@/resources" # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program @@ -920,7 +977,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 = ../README.md +USE_MDFILE_AS_MAINPAGE = @CMAKE_SOURCE_DIR@/README.md #--------------------------------------------------------------------------- # Configuration options related to source browsing @@ -933,7 +990,7 @@ USE_MDFILE_AS_MAINPAGE = ../README.md # also VERBATIM_HEADERS is set to NO. # The default value is: NO. -SOURCE_BROWSER = NO +SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. @@ -949,7 +1006,7 @@ INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. +# entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO @@ -981,12 +1038,12 @@ SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version +# (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # @@ -1014,7 +1071,7 @@ VERBATIM_HEADERS = YES # rich C++ code for which doxygen's built-in parser lacks the necessary type # information. # Note: The availability of this option depends on whether or not doxygen was -# generated with the -Duse-libclang=ON option for CMake. +# generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO @@ -1027,6 +1084,16 @@ CLANG_ASSISTED_PARSING = NO CLANG_OPTIONS = +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files +# were built. This is equivalent to specifying the "-p" option to a clang tool, +# such as clang-check. These options will then be passed to the parser. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1095,7 +1162,7 @@ HTML_FILE_EXTENSION = .html # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_HEADER = +HTML_HEADER = "@CMAKE_CURRENT_SOURCE_DIR@/header.html" # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard @@ -1105,7 +1172,7 @@ HTML_HEADER = # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_FOOTER = +HTML_FOOTER = "@CMAKE_CURRENT_SOURCE_DIR@/footer.html" # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of @@ -1117,7 +1184,7 @@ HTML_FOOTER = # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_STYLESHEET = +HTML_STYLESHEET = "@CMAKE_CURRENT_SOURCE_DIR@/doxygen.css" # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets @@ -1145,7 +1212,7 @@ HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. @@ -1181,6 +1248,17 @@ HTML_COLORSTYLE_GAMMA = 80 HTML_TIMESTAMP = NO +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. @@ -1204,13 +1282,13 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# environment (see: https://developer.apple.com/xcode/), introduced with OSX +# 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1249,7 +1327,7 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output @@ -1325,7 +1403,7 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1333,7 +1411,7 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1342,7 +1420,7 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1350,7 +1428,7 @@ QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1358,7 +1436,7 @@ QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = @@ -1451,7 +1529,7 @@ EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # @@ -1462,8 +1540,14 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering +# https://www.mathjax.org) which uses client side JavaScript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path @@ -1490,8 +1574,8 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest @@ -1533,7 +1617,7 @@ MATHJAX_CODEFILE = SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. There +# implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching depending on the EXTERNAL_SEARCH # setting. When disabled, doxygen will generate a PHP script for searching and # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing @@ -1552,7 +1636,7 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). +# Xapian (see: https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1565,7 +1649,7 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). See the section "External Indexing and +# Xapian (see: https://xapian.org/). See the section "External Indexing and # Searching" for details. # This tag requires that the tag SEARCHENGINE is set to YES. @@ -1617,21 +1701,35 @@ LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. # -# Note that when enabling USE_PDFLATEX this option is only used for generating -# bitmaps for formulas in the HTML output, but not in the Makefile that is -# written to the output directory. -# The default file is: latex. +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate # index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). # The default file is: makeindex. # This tag requires that the tag GENERATE_LATEX is set to YES. MAKEINDEX_CMD_NAME = makeindex +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. In case there is no backslash (\) as first character +# it will be automatically added in the LaTeX code. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = makeindex + # If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. @@ -1752,7 +1850,7 @@ LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See -# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. # The default value is: plain. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1766,6 +1864,14 @@ LATEX_BIB_STYLE = plain LATEX_TIMESTAMP = NO +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- @@ -1805,9 +1911,9 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's config -# file, i.e. a series of assignments. You only have to provide replacements, -# missing definitions are set to their default value. +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the # default style sheet that doxygen normally uses. @@ -1816,8 +1922,8 @@ RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's config file. A template extensions file can be generated -# using doxygen -e rtf extensionFile. +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_EXTENSIONS_FILE = @@ -1903,6 +2009,13 @@ XML_OUTPUT = xml XML_PROGRAMLISTING = YES +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- @@ -1935,9 +2048,9 @@ DOCBOOK_PROGRAMLISTING = NO #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an -# AutoGen Definitions (see http://autogen.sf.net) file that captures the -# structure of the code including all documentation. Note that this feature is -# still experimental and incomplete at the moment. +# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO @@ -1997,7 +2110,7 @@ ENABLE_PREPROCESSING = YES # The default value is: NO. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -MACRO_EXPANSION = NO +MACRO_EXPANSION = YES # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then # the macro expansion is limited to the macros specified with the PREDEFINED and @@ -2005,7 +2118,7 @@ MACRO_EXPANSION = NO # The default value is: NO. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -EXPAND_ONLY_PREDEF = NO +EXPAND_ONLY_PREDEF = YES # If the SEARCH_INCLUDES tag is set to YES, the include files in the # INCLUDE_PATH will be searched if a #include is found. @@ -2037,7 +2150,7 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = +PREDEFINED = __attribute__(x)= # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The @@ -2104,12 +2217,6 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of 'which perl'). -# The default file (with absolute path) is: /usr/bin/perl. - -PERL_PATH = /usr/bin/perl - #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- @@ -2123,15 +2230,6 @@ PERL_PATH = /usr/bin/perl CLASS_DIAGRAMS = YES -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see: -# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. @@ -2162,7 +2260,7 @@ HAVE_DOT = YES # Minimum value: 0, maximum value: 32, default value: 0. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_NUM_THREADS = 0 +DOT_NUM_THREADS = 2 # When you want a differently looking font in the dot files that doxygen # generates you can specify the font name using DOT_FONTNAME. You need to make @@ -2361,6 +2459,11 @@ DIAFILE_DIRS = PLANTUML_JAR_PATH = +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + # When using plantuml, the specified paths are searched for files specified by # the !include statement in a plantuml block. diff --git a/doc/HOWTO_use_grub_script.md b/doc/HOWTO_use_grub_script.md deleted file mode 100644 index 1f41fb2..0000000 --- a/doc/HOWTO_use_grub_script.md +++ /dev/null @@ -1,17 +0,0 @@ -* * * -### Come usare lo script per generare un'entry di GRUB # -* * * - -#### 1. Inserire la partizione dei sorgenti -Questo significa dire al programma in quale partizione si trova la vostra directory dei sorgenti. -Ad esempio se siete su **/dev/hda2**, dovrete inserire **hda2** - -#### 2. Inserire il mountpoint -Tale partizione ha un mountpoint (se non lo conoscete, date da shell il comando mount senza argomenti oppure date uno sguardo in **/etc/fstab**). Ad esempio potrà essere **/** o anche **/home/vostroutente** se tenete la home directory in una partizione separata. - -#### 3. Attenzione al path -Il path dell'immagine di MentOS sarà calcolato automaticamente, ma per fare questo lo script dev'essere nella stessa directory dell'immagine. -Dopodiché vi basta confermare ed avere la vostra entry nel file **menu.lst** - -**ATTENZIONE: Usatelo solo se sapete cosa state facendo. Lo script e' stato testato e funziona, ma non rispondiamo di eventuali blocchi del vostro computer.** - diff --git a/doc/doxygen.css b/doc/doxygen.css new file mode 100644 index 0000000..1595128 --- /dev/null +++ b/doc/doxygen.css @@ -0,0 +1,1604 @@ +@charset "UTF-8"; +/* The standard CSS for doxygen 1.8.16 */ +/* The following are overrides over hard-coded styles in Doxygen */ +.sm-dox { + background: #4b5263; } + +.sm-dox a, +.sm-dox a:focus, +.sm-dox a:active, +.sm-dox a:hover, +.sm-dox a.highlighted { + color: #A9B7C6; + background: none; } + +.sm-dox a, +.sm-dox a:focus, +.sm-dox a:hover, +.sm-dox a:active { + text-shadow: none; + outline: none; } + +.memberdecls tr:not(:first-child) { + background-color: #333842; } + +/* Stylesheet generated by Doxygen */ +body, +table, +div, +p, +dl { + font: 400 14px/22px Roboto, sans-serif; + display: block; + margin-block-start: 0em; + margin-block-end: 0em; + margin-inline-start: 0px; + margin-inline-end: 0px; } + +ul { + display: block; + list-style-type: disc; + margin-block-start: 0em; + margin-block-end: 0em; + margin-inline-start: 0px; + margin-inline-end: 0px; + padding-inline-start: 30px; } + +p.reference, +p.definition { + font: 400 14px/22px Roboto, sans-serif; } + +/* @group Heading Levels */ +h1.groupheader { + font-size: 150%; } + +.title { + font: 400 14px/28px Roboto, sans-serif; + font-size: 150%; + font-weight: bold; + margin: 10px 2px; } + +h2.groupheader { + border-radius: 4px; + background-color: #49515f; + font-size: 150%; + font-weight: normal; + padding: 0.5em; } + +h3.groupheader { + font-size: 100%; } + +h1, +h2, +h3, +h4, +h5, +h6 { + margin-right: 15px; + display: block; + margin-block-start: 0.25em; + margin-block-end: 0.25em; + margin-inline-start: 0px; + margin-inline-end: 0px; + font-weight: bold; + color: #d7dee4; } + +h1.glow, +h2.glow, +h3.glow, +h4.glow, +h5.glow, +h6.glow { + text-shadow: 0 0 15px cyan; } + +dt { + font-weight: bold; } + +ul.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; + column-count: 3; } + +p.startli, +p.startdd { + margin-top: 2px; } + +p.starttd { + margin-top: 0px; } + +p.endli { + margin-bottom: 0px; } + +p.enddd { + margin-bottom: 4px; } + +p.endtd { + margin-bottom: 2px; } + +/* @end */ +caption { + font-weight: bold; } + +span.legend { + font-size: 70%; + text-align: center; } + +span.arrow { + /* width: 32px; */ + padding-left: 0px; } + +h3.version { + font-size: 90%; + text-align: center; } + +div.qindex, +div.navtab { + background-color: #333842; + text-align: center; } + +div.qindex, +div.navpath { + width: 100%; + line-height: 140%; } + +div.navtab { + margin-right: 15px; } + +/* @group Link Styling */ +a { + color: #728dc2; + font-weight: normal; + text-decoration: none; } + +.contents a:visited { + color: #607fbb; } + +a:hover { + color: #96aad2; + text-decoration: underline; } + +a.qindex { + font-weight: bold; } + +a.qindexHL { + font-weight: bold; + background-color: #9CAFD4; + color: #FFFFFF; + border: 1px double #869DCA; } + +.contents a.qindexHL:visited { + color: #FFFFFF; } + +a.el { + font-weight: bold; } + +a.line, +a.line:visited { + color: #A9B7C6; } + +a.codeRef, +a.codeRef:visited, +a.lineRef, +a.lineRef:visited { + color: #4665A2; } + +/* @end */ +dl.el { + margin-left: -1cm; } + +ul { + overflow: hidden; + /*Fixed: list item bullets overlap floating elements*/ } + +#side-nav ul { + overflow: visible; + /* reset ul rule for scroll bar in GENERATE_TREEVIEW window */ } + +#main-nav ul { + overflow: visible; + /* reset ul rule for the navigation bar drop down lists */ } + +#main-nav li { + overflow: visible; } + +.fragment { + text-align: left; + direction: ltr; + overflow-x: auto; + /*Fixed: fragment lines overlap floating elements*/ + overflow-y: hidden; } + +pre.fragment { + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + line-height: 125%; + font-family: monaco, Consolas, "Lucida Console", monospace; + font-size: 105%; + color: #A9B7C6; } + +div.fragment { + padding: 0.5em; + background-color: #333842; + border: 1px solid #333842; + color: #A9B7C6; } + +div.line { + font-family: monospace, fixed; + font-size: 13px; + min-height: 13px; + line-height: 1.0; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; + /* Moz */ + white-space: -pre-wrap; + /* Opera 4-6 */ + white-space: -o-pre-wrap; + /* Opera 7 */ + white-space: pre-wrap; + /* CSS3 */ + word-wrap: break-word; + /* IE 5.5+ */ + text-indent: -53px; + padding-left: 53px; + padding-bottom: 0px; + margin: 0px; + transition: 0.5s; } + +div.line.glow { + background-color: cyan; + box-shadow: 0 0 10px cyan; } + +span.lineno { + padding-right: 4px; + text-align: right; + white-space: pre; } + +span.lineno a:hover { + background-color: #565e72; } + +.lineno { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } + +div.ah, +span.ah { + background-color: #16181d; + font-weight: bold; + color: #FFFFFF; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border-radius: 0.5em; } + +div.classindex ul { + list-style: none; + padding-left: 0; } + +div.classindex span.ai { + display: inline-block; } + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; } + +div.groupText { + margin-left: 16px; + font-style: italic; } + +body { + background-color: #282c34; + color: #A9B7C6; + margin: 0; } + +td.indexkey { + background-color: #EBEFF6; + font-weight: bold; + border: 1px solid #C4CFE5; + margin: 2px 0px 2px 0; + padding: 2px 10px; + white-space: nowrap; + vertical-align: top; } + +td.indexvalue { + background-color: #EBEFF6; + border: 1px solid #C4CFE5; + padding: 2px 10px; + margin: 2px 0px; } + +tr.memlist { + background-color: #EEF1F7; } + +p.formulaDsp { + text-align: center; } + +img.formulaInl, +img.inline { + vertical-align: middle; } + +div.center { + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; } + +div.center img { + border: 0px; } + +address.footer { + font-style: normal; + padding-right: 12px; } + +hr.footer { + flex: 1 1 auto; } + +img.footer { + height: 22px; + border: 0px; + vertical-align: middle; } + +div.footer ul { + list-style-type: none; + list-style-image: none; + margin: 0; + padding: 0; } + +/* @group Code Colorization */ +span.keyword { + color: #CC7832; } + +span.keywordtype { + color: #CC7832; } + +span.keywordflow { + color: #CC7832; } + +span.comment { + color: #808080; } + +span.preprocessor { + color: #BBB529; } + +span.stringliteral { + color: #6A8759; } + +span.charliteral { + color: #008080; } + +span.vhdldigit { + color: #ff00ff; } + +span.vhdlchar { + color: #000000; } + +span.vhdlkeyword { + color: #700070; } + +span.vhdllogic { + color: #ff0000; } + +blockquote { + background-color: #F7F8FB; + border-left: 2px solid #9CAFD4; + margin: 0 24px 0 4px; + padding: 0 12px 0 16px; } + +blockquote.DocNodeRTL { + border-left: 0; + border-right: 2px solid #9CAFD4; + margin: 0 4px 0 24px; + padding: 0 16px 0 12px; } + +/* @end */ +/* +.search { + color: #003399; + font-weight:bold; + +} + +form.search { + margin-bottom: 0px; + margin-top: 0px; + +} + +input.search { + font-size: 75%; + color: #000080; + font-weight:normal; + background-color: #e8eef2; + +} +* / + +td.tiny { + font-size: 75%; + +} + +.dirtab { + padding: 4px; + border-collapse:collapse; + border: 1px solid #A3B4D7; + +} + +th.dirtab { + background: #EBEFF6; + font-weight:bold; + +} + +hr { + height: 0px; + border:none; + border-top: 1px solid #4A6AAA; + +} + +hr.footer { + display:none; + +} + +div.footer { + display:flex; + align-items:center; + justify-content:space-between; + padding: 1em; + background-color: darken($content-bg-color, 5%); + +} + +/* @group Member Descriptions */ +table.memberdecls { + border-spacing: 0px; + padding: 0px; } + +.memberdecls td.glow, +.fieldtable tr.glow { + background-color: cyan; + box-shadow: 0 0 15px cyan; } + +.memItemLeft, +.memItemRight { + font-family: monaco, Consolas, "Lucida Console", monospace; } + +.mdescLeft, +.mdescRight, +.memItemLeft, +.memItemRight, +.memTemplItemLeft, +.memTemplItemRight, +.memTemplParams { + border: none; + margin: 4px; + padding: 1px 0 0 8px; } + +.mdescLeft, +.mdescRight { + padding: 0px 8px 4px 8px; } + +.memSeparator { + background-color: #282c34; + line-height: 1px; } + +.memItemLeft, +.memTemplItemLeft { + white-space: nowrap; } + +.memItemRight { + width: 100%; } + +.memTemplParams { + color: #4665A2; + white-space: nowrap; + font-size: 80%; } + +/* @end */ +/* @group Member Details */ +/* Styles for detailed member documentation */ +.memtitle { + border-top-right-radius: 4px; + border-top-left-radius: 4px; + margin: 0; + background-color: #333842; + padding: 0.5em; + font-weight: 300; + display: flex; + align-items: center; + float: left; + clear: both; + display: block; } + +.permalink { + font-size: 65%; + display: inline-block; + vertical-align: middle; } + +.permalink a:hover { + text-decoration: none; } + +.memtemplate { + font-size: 80%; + color: #4665A2; + font-weight: normal; + margin-left: 9px; } + +.memnav { + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; } + +.mempage { + width: 100%; } + +.memitem { + background-color: #333842; + clear: both; + margin-bottom: 1em; } + +.memitem.glow { + box-shadow: 0 0 15px cyan; } + +.memname { + font-family: monaco, Consolas, "Lucida Console", monospace; + font-weight: 400; + margin-left: 6px; } + +.memname td { + vertical-align: bottom; } + +.memproto, +dl.reflist dt { + padding: 0.5em 0; } + +.overload { + font-family: "courier new", courier, monospace; + font-size: 65%; } + +.memdoc, +dl.reflist dd { + padding: 1em; } + +dl.reflist dt { + padding: 5px; } + +dl.reflist dd { + margin: 0px 0px 10px 0px; + padding: 5px; } + +.paramkey { + text-align: right; } + +.paramtype { + white-space: nowrap; } + +.paramname { + color: #e5c07b; + white-space: nowrap; } + +.paramname em { + font-style: normal; + font-weight: bold; } + +.paramname code { + line-height: 14px; } + +.params, +.retval, +.exception, +.tparams { + margin-left: 0px; + padding-left: 0px; } + +.params .paramname, +.retval .paramname, +.tparams .paramname, +.exception .paramname { + font-weight: bold; + vertical-align: top; } + +.params .paramtype, +.tparams .paramtype { + font-style: italic; + vertical-align: top; } + +.params .paramdir, +.tparams .paramdir { + font-family: "courier new", courier, monospace; + vertical-align: top; } + +table.mlabels { + border-spacing: 0px; } + +td.mlabels-left { + width: 100%; + padding: 0px; } + +td.mlabels-right { + vertical-align: bottom; + padding: 0px; + white-space: nowrap; } + +span.mlabels { + margin-left: 8px; } + +span.mlabel { + background-color: #728DC1; + border-top: 1px solid #5373B4; + border-left: 1px solid #5373B4; + border-right: 1px solid #C4CFE5; + border-bottom: 1px solid #C4CFE5; + text-shadow: none; + color: white; + margin-right: 4px; + padding: 2px 3px; + border-radius: 3px; + font-size: 7pt; + white-space: nowrap; + vertical-align: middle; } + +/* @end */ +/* these are for tree view inside a (index) page */ +div.directory { + width: 100%; } + +.directory table { + border-collapse: collapse; } + +.directory td { + margin: 0px; + padding: 0px; + vertical-align: top; } + +.directory td.entry { + white-space: nowrap; + padding-right: 6px; + padding-top: 3px; + padding-bottom: 3px; } + +.directory td.entry a { + outline: none; } + +.directory td.entry a img { + border: none; } + +.directory td.desc { + width: 100%; + padding-left: 6px; + padding-right: 6px; } + +.directory img { + vertical-align: -30%; } + +.directory .levels { + white-space: nowrap; + width: 100%; + text-align: right; + font-size: 9pt; + background-color: #333842; } + +.directory .levels span { + cursor: pointer; + padding-left: 2px; + padding-right: 2px; + color: #728dc2; } + +.directory .levels span:hover { + color: #96aad2; } + +.arrow { + color: #9CAFD4; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; + font-size: 80%; + display: inline-block; + vertical-align: top; } + +.icon { + font-family: Arial, Helvetica; + font-weight: bold; + font-size: 12px; + height: 14px; + width: 16px; + display: inline-block; + background-color: #728DC1; + color: white; + text-align: center; + border-radius: 4px; + margin-left: 2px; + margin-right: 2px; } + +.icona { + width: 24px; + height: 22px; + display: inline-block; } + +.iconfopen { + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image: url("folderopen.png"); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align: top; + display: inline-block; } + +.iconfclosed { + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image: url("folderclosed.png"); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align: top; + display: inline-block; } + +.icondoc { + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image: url("doc.png"); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align: top; + display: inline-block; } + +table.directory { + font: 400 14px Roboto, sans-serif; } + +/* @end */ +div.dynheader { + margin-top: 8px; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } + +table.doxtable caption { + caption-side: top; } + +table.doxtable { + border-collapse: collapse; + margin-top: 4px; + margin-bottom: 4px; } + +table.doxtable td, +table.doxtable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; } + +table.doxtable th { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; } + +table.fieldtable { + /*width: 100%;*/ + margin-bottom: 10px; + border: 1px solid #A8B8D9; + border-spacing: 0px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); } + +.fieldtable td, +.fieldtable th { + padding: 3px 7px 2px; } + +.fieldtable td.fieldtype, +.fieldtable td.fieldname { + white-space: nowrap; + border-right: 1px solid #A8B8D9; + border-bottom: 1px solid #A8B8D9; + vertical-align: top; } + +.fieldtable td.fieldname { + padding-top: 3px; } + +.fieldtable td.fielddoc { + border-bottom: 1px solid #A8B8D9; + /*width: 100%;*/ } + +.fieldtable td.fielddoc p:first-child { + margin-top: 0px; } + +.fieldtable td.fielddoc p:last-child { + margin-bottom: 2px; } + +.fieldtable tr:last-child td { + border-bottom: none; } + +.fieldtable th { + background-image: url("nav_f.png"); + background-repeat: repeat-x; + background-color: #E2E8F2; + font-size: 90%; + color: #253555; + padding-bottom: 4px; + padding-top: 5px; + text-align: left; + font-weight: 400; + -moz-border-radius-topleft: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: 1px solid #A8B8D9; } + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + background-image: url("tab_b.png"); + z-index: 101; + overflow: hidden; + font-size: 13px; } + +.navpath ul { + font-size: 11px; + background-color: #3e4451; + color: #8AA0CC; + overflow: hidden; + margin: 0px; + padding: 0px; + display: flex; + align-items: center; } + +.navpath li { + list-style-type: none; + padding-top: 0.25em; + padding-bottom: 0.25em; + padding-right: 0.25em; + display: flex; + padding: 0.25em; + color: #a9b7c6; } + +.navpath li:not(:first-child)::before { + content: "〉"; + display: block; + font-size: 1em; + font-weight: bold; + width: 1em; } + +.navpath li.navelem a { + display: block; + text-decoration: none; + outline: none; + color: #a9b7c6; + font-family: 'Lucida Grande', Geneva, Helvetica, Arial, sans-serif; + text-decoration: none; } + +.navpath li.navelem a:hover { + color: #d7dee4; } + +.navpath li.footer { + list-style-type: none; + float: right; + padding-left: 10px; + padding-right: 15px; + background-image: none; + background-repeat: no-repeat; + background-position: right; + color: #364D7C; + font-size: 8pt; } + +div.summary { + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; } + +div.summary a { + white-space: nowrap; } + +table.classindex { + margin: 10px; + white-space: nowrap; + margin-left: 3%; + margin-right: 3%; + width: 94%; + border: 0; + border-spacing: 0; + padding: 0; } + +div.ingroups { + font-size: 8pt; + width: 50%; + text-align: left; } + +div.ingroups a { + white-space: nowrap; } + +div.header { + background-color: #282c34; + margin: 0px; + color: #A9B7C6; } + +div.headertitle { + padding: 5px 5px 5px 10px; + border-bottom: 1px solid #C4CFE5; } + +.PageDocRTL-title div.headertitle { + text-align: right; + direction: rtl; } + +dl { + padding: 0 0 0 0; } + +/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug, dl.examples */ +dl.section { + margin-left: 0px; + padding-left: 0px; } + +dl.section.DocNodeRTL { + margin-right: 0px; + padding-right: 0px; } + +dl.note { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #D0C000; } + +dl.note.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #D0C000; } + +dl.warning, +dl.attention { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #FF0000; } + +dl.warning.DocNodeRTL, +dl.attention.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #FF0000; } + +dl.pre, +dl.post, +dl.invariant { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #00D000; } + +dl.pre.DocNodeRTL, +dl.post.DocNodeRTL, +dl.invariant.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #00D000; } + +dl.deprecated { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #505050; } + +dl.deprecated.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #505050; } + +dl.todo { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #00C0E0; } + +dl.todo.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #00C0E0; } + +dl.test { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #3030E0; } + +dl.test.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #3030E0; } + +dl.bug { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #C08050; } + +dl.bug.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #C08050; } + +dl.section dd { + margin-bottom: 6px; } + +#projectlogo { + text-align: center; + vertical-align: bottom; + border-collapse: separate; } + +#projectlogo img { + border: 0px none; + height: 8em; + padding: 1em; } + +#projectalign { + vertical-align: middle; } + +#projectname { + font: 300% Tahoma, Arial, sans-serif; + margin: 0px; + padding: 2px 0px; } + +#projectbrief { + font: 120% Tahoma, Arial, sans-serif; + margin: 0px; + padding: 0px; } + +#projectnumber { + font: 50% Tahoma, Arial, sans-serif; + margin: 0px; + padding: 0px; } + +#titlearea { + padding: 0px; + margin: 0px; + width: 100%; + color: #A9B7C6; + background-color: #282c34; + border-bottom: 3px solid #A9B7C6; } + +.image { + text-align: center; + background-color: #5f697c; + margin-left: 5em; + margin-right: 5em; + padding: 0.5em; + border-radius: 4px; } + +.dotgraph { + text-align: center; } + +.mscgraph { + text-align: center; } + +.plantumlgraph { + text-align: center; } + +.diagraph { + text-align: center; } + +.caption { + font-weight: bold; } + +div.zoom { + border: 1px solid #90A5CE; } + +dl.citelist { + margin-bottom: 50px; } + +dl.citelist dt { + color: #334975; + float: left; + font-weight: bold; + margin-right: 10px; + padding: 5px; } + +dl.citelist dd { + margin: 2px 0; + padding: 5px 0; } + +div.toc { + padding: 14px 25px; + background-color: #F4F6FA; + border: 1px solid #D8DFEE; + border-radius: 7px 7px 7px 7px; + float: right; + height: auto; + margin: 0 8px 10px 10px; + width: 200px; } + +.PageDocRTL-title div.toc { + float: left !important; + text-align: right; } + +div.toc li { + background: url("bdwn.png") no-repeat scroll 0 5px transparent; + font: 10px/1.2 Verdana, DejaVu Sans, Geneva, sans-serif; + margin-top: 5px; + padding-left: 10px; + padding-top: 2px; } + +.PageDocRTL-title div.toc li { + background-position-x: right !important; + padding-left: 0 !important; + padding-right: 10px; } + +div.toc h3 { + font: bold 12px/1.2 Arial, FreeSans, sans-serif; + color: #4665A2; + border-bottom: 0 none; + margin: 0; } + +div.toc ul { + list-style: none outside none; + border: medium none; + padding: 0px; } + +div.toc li.level1 { + margin-left: 0px; } + +div.toc li.level2 { + margin-left: 15px; } + +div.toc li.level3 { + margin-left: 30px; } + +div.toc li.level4 { + margin-left: 45px; } + +.PageDocRTL-title div.toc li.level1 { + margin-left: 0 !important; + margin-right: 0; } + +.PageDocRTL-title div.toc li.level2 { + margin-left: 0 !important; + margin-right: 15px; } + +.PageDocRTL-title div.toc li.level3 { + margin-left: 0 !important; + margin-right: 30px; } + +.PageDocRTL-title div.toc li.level4 { + margin-left: 0 !important; + margin-right: 45px; } + +.inherit_header { + font-weight: bold; + color: gray; + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } + +.inherit_header td { + padding: 6px 0px 2px 5px; } + +.inherit { + display: none; } + +/* tooltip related style info */ +.ttc { + position: absolute; + display: none; } + +#powerTip { + cursor: default; + white-space: nowrap; + background-color: white; + border: 1px solid gray; + border-radius: 4px 4px 4px 4px; + box-shadow: 1px 1px 7px gray; + display: none; + font-size: smaller; + max-width: 80%; + opacity: 0.9; + padding: 1ex 1em 1em; + position: absolute; + z-index: 2147483647; } + +#powerTip div.ttdoc { + color: grey; + font-style: italic; } + +#powerTip div.ttname a { + font-weight: bold; } + +#powerTip div.ttname { + font-weight: bold; } + +#powerTip div.ttdeci { + color: #006318; } + +#powerTip div { + margin: 0px; + padding: 0px; + font: 12px/16px Roboto, sans-serif; } + +#powerTip:before, +#powerTip:after { + content: ""; + position: absolute; + margin: 0px; } + +#powerTip.n:after, +#powerTip.n:before, +#powerTip.s:after, +#powerTip.s:before, +#powerTip.w:after, +#powerTip.w:before, +#powerTip.e:after, +#powerTip.e:before, +#powerTip.ne:after, +#powerTip.ne:before, +#powerTip.se:after, +#powerTip.se:before, +#powerTip.nw:after, +#powerTip.nw:before, +#powerTip.sw:after, +#powerTip.sw:before { + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; } + +#powerTip.n:after, +#powerTip.s:after, +#powerTip.w:after, +#powerTip.e:after, +#powerTip.nw:after, +#powerTip.ne:after, +#powerTip.sw:after, +#powerTip.se:after { + border-color: rgba(255, 255, 255, 0); } + +#powerTip.n:before, +#powerTip.s:before, +#powerTip.w:before, +#powerTip.e:before, +#powerTip.nw:before, +#powerTip.ne:before, +#powerTip.sw:before, +#powerTip.se:before { + border-color: rgba(128, 128, 128, 0); } + +#powerTip.n:after, +#powerTip.n:before, +#powerTip.ne:after, +#powerTip.ne:before, +#powerTip.nw:after, +#powerTip.nw:before { + top: 100%; } + +#powerTip.n:after, +#powerTip.ne:after, +#powerTip.nw:after { + border-top-color: #FFFFFF; + border-width: 10px; + margin: 0px -10px; } + +#powerTip.n:before { + border-top-color: #808080; + border-width: 11px; + margin: 0px -11px; } + +#powerTip.n:after, +#powerTip.n:before { + left: 50%; } + +#powerTip.nw:after, +#powerTip.nw:before { + right: 14px; } + +#powerTip.ne:after, +#powerTip.ne:before { + left: 14px; } + +#powerTip.s:after, +#powerTip.s:before, +#powerTip.se:after, +#powerTip.se:before, +#powerTip.sw:after, +#powerTip.sw:before { + bottom: 100%; } + +#powerTip.s:after, +#powerTip.se:after, +#powerTip.sw:after { + border-bottom-color: #FFFFFF; + border-width: 10px; + margin: 0px -10px; } + +#powerTip.s:before, +#powerTip.se:before, +#powerTip.sw:before { + border-bottom-color: #808080; + border-width: 11px; + margin: 0px -11px; } + +#powerTip.s:after, +#powerTip.s:before { + left: 50%; } + +#powerTip.sw:after, +#powerTip.sw:before { + right: 14px; } + +#powerTip.se:after, +#powerTip.se:before { + left: 14px; } + +#powerTip.e:after, +#powerTip.e:before { + left: 100%; } + +#powerTip.e:after { + border-left-color: #FFFFFF; + border-width: 10px; + top: 50%; + margin-top: -10px; } + +#powerTip.e:before { + border-left-color: #808080; + border-width: 11px; + top: 50%; + margin-top: -11px; } + +#powerTip.w:after, +#powerTip.w:before { + right: 100%; } + +#powerTip.w:after { + border-right-color: #FFFFFF; + border-width: 10px; + top: 50%; + margin-top: -10px; } + +#powerTip.w:before { + border-right-color: #808080; + border-width: 11px; + top: 50%; + margin-top: -11px; } + +@media print { + #top { + display: none; } + #side-nav { + display: none; } + #nav-path { + display: none; } + body { + overflow: visible; } + h1, + h2, + h3, + h4, + h5, + h6 { + page-break-after: avoid; } + .summary { + display: none; } + .memitem { + page-break-inside: avoid; } + #doc-content { + margin-left: 0 !important; + height: auto !important; + width: auto !important; + overflow: inherit; + display: inline; } } + +/* @group Markdown */ +/* +table.markdownTable { + + border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; + +} + +table.markdownTable td, table.markdownTable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; + +} + +table.markdownTableHead tr { + +} + +table.markdownTableBodyLeft td, table.markdownTable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; + +} + +th.markdownTableHeadLeft th.markdownTableHeadRight th.markdownTableHeadCenter th.markdownTableHeadNone { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; + +} + +th.markdownTableHeadLeft { + text-align: left + +} + +th.markdownTableHeadRight { + text-align: right + +} + +th.markdownTableHeadCenter { + text-align: center + +} +* / + +table.markdownTable { + + border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; + +} + +table.markdownTable td, table.markdownTable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; + +} + +table.markdownTable tr { + +} + +th.markdownTableHeadLeft, th.markdownTableHeadRight, th.markdownTableHeadCenter, th.markdownTableHeadNone { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; + +} + +th.markdownTableHeadLeft, td.markdownTableBodyLeft { + text-align: left + +} + +th.markdownTableHeadRight, td.markdownTableBodyRight { + text-align: right + +} + +th.markdownTableHeadCenter, td.markdownTableBodyCenter { + text-align: center + +} + +.DocNodeRTL { + text-align:right; + direction:rtl; + +} + +.DocNodeLTR { + text-align:left; + direction:ltr; + +} + +table.DocNodeRTL { + width:auto; + margin-right: 0; + margin-left:auto; + +} + +table.DocNodeLTR { + width:auto; + margin-right:auto; + margin-left: 0; + +} + +tt, code, kbd, samp + +{ +display:inline-block; +direction:ltr; + +} +/* @end */ +u { + text-decoration: underline; } + +/* +#nav-tree { + padding: 0px 0px; + font-size: 14px; + overflow: auto; +} +*/ +/* +.ui-resizable-e { + background: $white-color; + width: 3px; +} + +#nav-tree { + padding: 0px 0px; + background-color: $black-color; + background-image: none; + font-size: 14px; + overflow: auto; +} + +#nav-tree .selected { + background-color: $code-bg-color; + background-image: none; + color: $white-color; +} +*/ diff --git a/doc/doxygen.scss b/doc/doxygen.scss new file mode 100644 index 0000000..cb1235a --- /dev/null +++ b/doc/doxygen.scss @@ -0,0 +1,2061 @@ +/* The standard CSS for doxygen 1.8.16 */ + +$black-color: #282c34; +$white-color: #A9B7C6; +$magenta-color: #c678dd; +$cyan-color: #56b6c2; +$yellow-color: #e5c07b; +$blue-color: #61afef; +$gutter-color: #4b5263; +// $code-font: "Courier New", Courier, "Lucida Sans Typewriter", "Lucida Typewriter", monospace; +$code-font: monaco, +Consolas, +"Lucida Console", +monospace; +$title-fg-color: $white-color; +$title-bg-color: $black-color; +$menu-bg-color: $gutter-color; +$breadcrumb-fg-color: lighten($white-color, 0%); +$breadcrumb-bg-color: lighten($black-color, 10%); +$breadcrumb-arrow-color: $white-color; +$content-fg-color: $white-color; +$content-bg-color: $black-color; +$headers-fg-color: lighten($white-color, 15%); +$header-bg-color: lighten($black-color, 15%); +$table-bg-color: lighten($black-color, 5%); +$image-bg-color: lighten($black-color, 25%); +$code-border-color: $gutter-color; +$code-fg-color: $white-color; +$code-bg-color: lighten($black-color, 5%); +$link-color: lighten(#4665A2, 15%); +$link-visited-color: lighten(#4665A2, 10%); +$link-hover-color: lighten(#4665A2, 25%); +$footer-fg-color: darken($white-color, 10%); +$footer-bg-color: darken($black-color, 10%); +$ah-bg-color: darken($black-color, 8%); + +/* The following are overrides over hard-coded styles in Doxygen */ + +.sm-dox { + background: $menu-bg-color; +} + +.sm-dox a, +.sm-dox a:focus, +.sm-dox a:active, +.sm-dox a:hover, +.sm-dox a.highlighted { + color: $white-color; + background: none; +} + +.sm-dox a, +.sm-dox a:focus, +.sm-dox a:hover, +.sm-dox a:active { + text-shadow: none; + outline: none; +} + +.memberdecls tr:not(:first-child) { + background-color: $table-bg-color; +} + + +/* Stylesheet generated by Doxygen */ + +body, +table, +div, +p, +dl { + font: 400 14px/22px Roboto, sans-serif; + display: block; + margin-block-start: 0em; + margin-block-end: 0em; + margin-inline-start: 0px; + margin-inline-end: 0px; +} + +ul { + display: block; + list-style-type: disc; + margin-block-start: 0em; + margin-block-end: 0em; + margin-inline-start: 0px; + margin-inline-end: 0px; + padding-inline-start: 30px; +} + +p.reference, +p.definition { + font: 400 14px/22px Roboto, sans-serif; +} + + +/* @group Heading Levels */ + +h1.groupheader { + font-size: 150%; +} + +.title { + font: 400 14px/28px Roboto, sans-serif; + font-size: 150%; + font-weight: bold; + margin: 10px 2px; +} + +h2.groupheader { + // border-bottom: 1px solid $tabel-fg-color; + // color: #354C7B; + border-radius: 4px; + background-color: $header-bg-color; + font-size: 150%; + font-weight: normal; + //margin-top: 1.75em; + padding: 0.5em; + // width: 100%; +} + +h3.groupheader { + font-size: 100%; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + // -webkit-transition: text-shadow 0.5s linear; + // -moz-transition: text-shadow 0.5s linear; + // -ms-transition: text-shadow 0.5s linear; + // -o-transition: text-shadow 0.5s linear; + // transition: text-shadow 0.5s linear; + margin-right: 15px; + display: block; + margin-block-start: 0.25em; + margin-block-end: 0.25em; + margin-inline-start: 0px; + margin-inline-end: 0px; + font-weight: bold; + color: $headers-fg-color; +} + +h1.glow, +h2.glow, +h3.glow, +h4.glow, +h5.glow, +h6.glow { + text-shadow: 0 0 15px cyan; +} + +dt { + font-weight: bold; +} + +ul.multicol { + -moz-column-gap: 1em; + -webkit-column-gap: 1em; + column-gap: 1em; + -moz-column-count: 3; + -webkit-column-count: 3; + column-count: 3; +} + +p.startli, +p.startdd { + margin-top: 2px; +} + +p.starttd { + margin-top: 0px; +} + +p.endli { + margin-bottom: 0px; +} + +p.enddd { + margin-bottom: 4px; +} + +p.endtd { + margin-bottom: 2px; +} + +p.interli {} + +p.interdd {} + +p.intertd {} + + +/* @end */ + +caption { + font-weight: bold; +} + +span.legend { + font-size: 70%; + text-align: center; +} + +span.arrow { + /* width: 32px; */ + padding-left: 0px; +} + +h3.version { + font-size: 90%; + text-align: center; +} + +div.qindex, +div.navtab { + background-color: $table-bg-color; + // border: 1px solid #A3B4D7; + text-align: center; +} + +div.qindex, +div.navpath { + width: 100%; + line-height: 140%; +} + +div.navtab { + margin-right: 15px; +} + + +/* @group Link Styling */ + +a { + color: $link-color; + font-weight: normal; + text-decoration: none; +} + +.contents a:visited { + color: $link-visited-color; +} + +a:hover { + color: $link-hover-color; + text-decoration: underline; +} + +a.qindex { + font-weight: bold; +} + +a.qindexHL { + font-weight: bold; + background-color: #9CAFD4; + color: #FFFFFF; + border: 1px double #869DCA; +} + +.contents a.qindexHL:visited { + color: #FFFFFF; +} + +a.el { + font-weight: bold; +} + +a.elRef {} + +a.line, +a.line:visited { + color: $white-color; +} + +// a.code { +// color: $link-color; +// } +a.codeRef, +a.codeRef:visited, +a.lineRef, +a.lineRef:visited { + color: #4665A2; +} + + +/* @end */ + +dl.el { + margin-left: -1cm; +} + +ul { + overflow: hidden; + /*Fixed: list item bullets overlap floating elements*/ +} + +#side-nav ul { + overflow: visible; + /* reset ul rule for scroll bar in GENERATE_TREEVIEW window */ +} + +#main-nav ul { + overflow: visible; + /* reset ul rule for the navigation bar drop down lists */ +} + +#main-nav li { + overflow: visible; +} + +.fragment { + text-align: left; + direction: ltr; + overflow-x: auto; + /*Fixed: fragment lines overlap floating elements*/ + overflow-y: hidden; +} + +pre.fragment { + // border: 1px solid $code-border-color; + // background-color: $code-bg-color; + padding: 4px 6px; + margin: 4px 8px 4px 2px; + overflow: auto; + word-wrap: break-word; + line-height: 125%; + font-family: $code-font; + font-size: 105%; + color: $code-fg-color; +} + +div.fragment { + // padding: 0 0 1px 0; [>Fixed: last line underline overlap border<] + // margin: 4px 8px 4px 2px; + padding: 0.5em; + background-color: $code-bg-color; + border: 1px solid $code-bg-color; + color: $code-fg-color; +} + +div.line { + font-family: monospace, fixed; + font-size: 13px; + min-height: 13px; + line-height: 1.0; + text-wrap: unrestricted; + white-space: -moz-pre-wrap; + /* Moz */ + white-space: -pre-wrap; + /* Opera 4-6 */ + white-space: -o-pre-wrap; + /* Opera 7 */ + white-space: pre-wrap; + /* CSS3 */ + word-wrap: break-word; + /* IE 5.5+ */ + text-indent: -53px; + padding-left: 53px; + padding-bottom: 0px; + margin: 0px; + transition: 0.5s; +} + +// div.line:hover { +// background-color: lighten($code-bg-color, 10%); +// } +div.line.glow { + background-color: cyan; + box-shadow: 0 0 10px cyan; +} + +span.lineno { + padding-right: 4px; + text-align: right; + white-space: pre; + //background-color: $gutter-color; + //border-right: 2px solid darken($gutter-color, 10%); +} + +span.lineno a { + //background-color: lighten($gutter-color, 5%); +} + +span.lineno a:hover { + background-color: lighten($gutter-color, 5%); +} + +.lineno { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +div.ah, +span.ah { + background-color: $ah-bg-color; + font-weight: bold; + color: #FFFFFF; + margin-bottom: 3px; + margin-top: 3px; + padding: 0.2em; + border-radius: 0.5em; +} + +div.classindex ul { + list-style: none; + padding-left: 0; +} + +div.classindex span.ai { + display: inline-block; +} + +div.groupHeader { + margin-left: 16px; + margin-top: 12px; + font-weight: bold; +} + +div.groupText { + margin-left: 16px; + font-style: italic; +} + +body { + background-color: $content-bg-color; + color: $content-fg-color; + margin: 0; +} + +div.contents { + //margin: 1em; + //margin-top: 2em; + //margin-left: 0.5em; + //margin-right: 0.5em; + //margin-bottom: 2em; + // margin-top: 10px; + // margin-left: 12px; + // margin-right: 8px; + //margin-bottom: 1em; +} + +td.indexkey { + background-color: #EBEFF6; + font-weight: bold; + border: 1px solid #C4CFE5; + margin: 2px 0px 2px 0; + padding: 2px 10px; + white-space: nowrap; + vertical-align: top; +} + +td.indexvalue { + background-color: #EBEFF6; + border: 1px solid #C4CFE5; + padding: 2px 10px; + margin: 2px 0px; +} + +tr.memlist { + background-color: #EEF1F7; +} + +p.formulaDsp { + text-align: center; +} + +img.formulaDsp {} + +img.formulaInl, +img.inline { + vertical-align: middle; +} + +div.center { + text-align: center; + margin-top: 0px; + margin-bottom: 0px; + padding: 0px; +} + +div.center img { + border: 0px; +} + +address.footer { + font-style: normal; + padding-right: 12px; +} + +hr.footer { + flex: 1 1 auto; +} + +img.footer { + height: 22px; + border: 0px; + vertical-align: middle; +} + +div.footer ul { + list-style-type: none; + list-style-image: none; + margin: 0; + padding: 0; +} + + +/* @group Code Colorization */ + +span.keyword { + color: #CC7832 //#008000 +} + +span.keywordtype { + color: #CC7832 //#aa6e32 +} + +span.keywordflow { + color: #CC7832 +} + +span.comment { + color: #808080 //#5c6370 +} + +span.preprocessor { + color: #BBB529 +} + +span.stringliteral { + color: #6A8759 +} + +span.charliteral { + color: #008080 +} + +span.vhdldigit { + color: #ff00ff +} + +span.vhdlchar { + color: #000000 +} + +span.vhdlkeyword { + color: #700070 +} + +span.vhdllogic { + color: #ff0000 +} + +blockquote { + background-color: #F7F8FB; + border-left: 2px solid #9CAFD4; + margin: 0 24px 0 4px; + padding: 0 12px 0 16px; +} + +blockquote.DocNodeRTL { + border-left: 0; + border-right: 2px solid #9CAFD4; + margin: 0 4px 0 24px; + padding: 0 16px 0 12px; +} + + +/* @end */ + + +/* +.search { + color: #003399; + font-weight:bold; + +} + +form.search { + margin-bottom: 0px; + margin-top: 0px; + +} + +input.search { + font-size: 75%; + color: #000080; + font-weight:normal; + background-color: #e8eef2; + +} +* / + +td.tiny { + font-size: 75%; + +} + +.dirtab { + padding: 4px; + border-collapse:collapse; + border: 1px solid #A3B4D7; + +} + +th.dirtab { + background: #EBEFF6; + font-weight:bold; + +} + +hr { + height: 0px; + border:none; + border-top: 1px solid #4A6AAA; + +} + +hr.footer { + display:none; + +} + +div.footer { + display:flex; + align-items:center; + justify-content:space-between; + padding: 1em; + background-color: darken($content-bg-color, 5%); + +} + +/* @group Member Descriptions */ + +table.memberdecls { + border-spacing: 0px; + padding: 0px; +} + +.memberdecls td.glow, +.fieldtable tr.glow { + background-color: cyan; + box-shadow: 0 0 15px cyan; +} + +.memItemLeft, +.memItemRight { + font-family: $code-font; +} + +.mdescLeft, +.mdescRight, +.memItemLeft, +.memItemRight, +.memTemplItemLeft, +.memTemplItemRight, +.memTemplParams { + border: none; + margin: 4px; + padding: 1px 0 0 8px; +} + +.mdescLeft, +.mdescRight { + padding: 0px 8px 4px 8px; + // color: $table-fg-color; +} + +.memSeparator { + // display: none; + background-color: $content-bg-color; + line-height: 1px; + // border-bottom: 1px solid transparent; + // line-height: 1px; + // margin: 0px; + // padding: 0px; +} + +.memItemLeft, +.memTemplItemLeft { + white-space: nowrap; +} + +.memItemRight { + width: 100%; +} + +.memTemplParams { + color: #4665A2; + white-space: nowrap; + font-size: 80%; +} + + +/* @end */ + + +/* @group Member Details */ + + +/* Styles for detailed member documentation */ + +.memtitle { + // padding: 8px; + // border-top: 1px solid #A8B8D9; + // border-left: 1px solid #A8B8D9; + // border-right: 1px solid #A8B8D9; + // margin-bottom: -1px; + // background-image: url('nav_f.png'); + // background-repeat: repeat-x; + // background-color: #E2E8F2; + border-top-right-radius: 4px; + border-top-left-radius: 4px; + margin: 0; + background-color: $table-bg-color; + padding: 0.5em; + font-weight: 300; + display: flex; + align-items: center; + float: left; + clear: both; + display: block; +} + +.permalink { + font-size: 65%; + display: inline-block; + vertical-align: middle; +} + +.permalink a:hover { + text-decoration: none; +} + +.memtemplate { + font-size: 80%; + color: #4665A2; + font-weight: normal; + margin-left: 9px; +} + +.memnav { + background-color: #EBEFF6; + border: 1px solid #A3B4D7; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} + +.mempage { + width: 100%; +} + +.memitem { + background-color: $table-bg-color; + clear: both; + margin-bottom: 1em; + // padding: 0; + // margin-bottom: 10px; + // margin-right: 5px; + // -webkit-transition: box-shadow 0.5s linear; + // -moz-transition: box-shadow 0.5s linear; + // -ms-transition: box-shadow 0.5s linear; + // -o-transition: box-shadow 0.5s linear; + // transition: box-shadow 0.5s linear; + // display: table !important; + // width: 100%; +} + +.memitem.glow { + box-shadow: 0 0 15px cyan; +} + +.memname { + font-family: $code-font; + font-weight: 400; + margin-left: 6px; +} + +.memname td { + vertical-align: bottom; +} + +.memproto, +dl.reflist dt { + padding: 0.5em 0; + // background-color: darken(#d19a66, 40%); + // border-top: 1px solid #A8B8D9; + // border-left: 1px solid #A8B8D9; + // border-right: 1px solid #A8B8D9; + // padding: 6px 0px 6px 0px; + // color: #253555; + // font-weight: bold; + // text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + // background-color: #DFE5F1; + // [> opera specific markup <] + // box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + // border-top-right-radius: 4px; + // [> firefox specific markup <] + // -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + // -moz-border-radius-topright: 4px; + // [> webkit specific markup <] + // -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + // -webkit-border-top-right-radius: 4px; +} + +.overload { + font-family: "courier new", courier, monospace; + font-size: 65%; +} + +.memdoc, +dl.reflist dd { + padding: 1em; + // border-bottom: 1px solid #A8B8D9; + // border-left: 1px solid #A8B8D9; + // border-right: 1px solid #A8B8D9; + // padding: 6px 10px 2px 10px; + // background-color: #FBFCFD; + // border-top-width: 0; + // background-image:url('nav_g.png'); + // background-repeat:repeat-x; + // background-color: #FFFFFF; + // [> opera specific markup <] + // border-bottom-left-radius: 4px; + // border-bottom-right-radius: 4px; + // box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); + // [> firefox specific markup <] + // -moz-border-radius-bottomleft: 4px; + // -moz-border-radius-bottomright: 4px; + // -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; + // [> webkit specific markup <] + // -webkit-border-bottom-left-radius: 4px; + // -webkit-border-bottom-right-radius: 4px; + // -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); +} + +dl.reflist dt { + padding: 5px; +} + +dl.reflist dd { + margin: 0px 0px 10px 0px; + padding: 5px; +} + +.paramkey { + text-align: right; +} + +.paramtype { + white-space: nowrap; +} + +.paramname { + color: $yellow-color; + white-space: nowrap; +} + +.paramname em { + font-style: normal; + font-weight: bold; +} + +.paramname code { + line-height: 14px; +} + +.params, +.retval, +.exception, +.tparams { + margin-left: 0px; + padding-left: 0px; +} + +.params .paramname, +.retval .paramname, +.tparams .paramname, +.exception .paramname { + font-weight: bold; + vertical-align: top; +} + +.params .paramtype, +.tparams .paramtype { + font-style: italic; + vertical-align: top; +} + +.params .paramdir, +.tparams .paramdir { + font-family: "courier new", courier, monospace; + vertical-align: top; +} + +table.mlabels { + border-spacing: 0px; +} + +td.mlabels-left { + width: 100%; + padding: 0px; +} + +td.mlabels-right { + vertical-align: bottom; + padding: 0px; + white-space: nowrap; +} + +span.mlabels { + margin-left: 8px; +} + +span.mlabel { + background-color: #728DC1; + border-top: 1px solid #5373B4; + border-left: 1px solid #5373B4; + border-right: 1px solid #C4CFE5; + border-bottom: 1px solid #C4CFE5; + text-shadow: none; + color: white; + margin-right: 4px; + padding: 2px 3px; + border-radius: 3px; + font-size: 7pt; + white-space: nowrap; + vertical-align: middle; +} + + +/* @end */ + + +/* these are for tree view inside a (index) page */ + +div.directory { + //margin: 10px 0px; + // border-top: 1px solid #9CAFD4; + // border-bottom: 1px solid #9CAFD4; + width: 100%; +} + +.directory table { + border-collapse: collapse; +} + +.directory td { + margin: 0px; + padding: 0px; + vertical-align: top; +} + +.directory td.entry { + white-space: nowrap; + padding-right: 6px; + padding-top: 3px; + padding-bottom: 3px; +} + +.directory td.entry a { + outline: none; +} + +.directory td.entry a img { + border: none; +} + +.directory td.desc { + width: 100%; + padding-left: 6px; + padding-right: 6px; + // border-left: 1px solid rgba(0,0,0,0.05); +} + +.directory img { + vertical-align: -30%; +} + +.directory .levels { + white-space: nowrap; + width: 100%; + text-align: right; + font-size: 9pt; + background-color: $table-bg-color; +} + +.directory .levels span { + cursor: pointer; + padding-left: 2px; + padding-right: 2px; + color: $link-color; +} + +.directory .levels span:hover { + color: $link-hover-color; +} + +.arrow { + color: #9CAFD4; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + cursor: pointer; + font-size: 80%; + display: inline-block; + //width: 16px; + //height: 22px; + vertical-align: top; +} + +.icon { + font-family: Arial, Helvetica; + font-weight: bold; + font-size: 12px; + height: 14px; + width: 16px; + display: inline-block; + background-color: #728DC1; + color: white; + text-align: center; + border-radius: 4px; + margin-left: 2px; + margin-right: 2px; +} + +.icona { + width: 24px; + height: 22px; + display: inline-block; +} + +.iconfopen { + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image: url('folderopen.png'); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align: top; + display: inline-block; +} + +.iconfclosed { + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image: url('folderclosed.png'); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align: top; + display: inline-block; +} + +.icondoc { + width: 24px; + height: 18px; + margin-bottom: 4px; + background-image: url('doc.png'); + background-position: 0px -4px; + background-repeat: repeat-y; + vertical-align: top; + display: inline-block; +} + +table.directory { + font: 400 14px Roboto, sans-serif; +} + + +/* @end */ + +div.dynheader { + margin-top: 8px; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +table.doxtable caption { + caption-side: top; +} + +table.doxtable { + border-collapse: collapse; + margin-top: 4px; + margin-bottom: 4px; +} + +table.doxtable td, +table.doxtable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; +} + +table.doxtable th { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; +} + +table.fieldtable { + /*width: 100%;*/ + margin-bottom: 10px; + border: 1px solid #A8B8D9; + border-spacing: 0px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; + -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); +} + +.fieldtable td, +.fieldtable th { + padding: 3px 7px 2px; +} + +.fieldtable td.fieldtype, +.fieldtable td.fieldname { + white-space: nowrap; + border-right: 1px solid #A8B8D9; + border-bottom: 1px solid #A8B8D9; + vertical-align: top; +} + +.fieldtable td.fieldname { + padding-top: 3px; +} + +.fieldtable td.fielddoc { + border-bottom: 1px solid #A8B8D9; + /*width: 100%;*/ +} + +.fieldtable td.fielddoc p:first-child { + margin-top: 0px; +} + +.fieldtable td.fielddoc p:last-child { + margin-bottom: 2px; +} + +.fieldtable tr:last-child td { + border-bottom: none; +} + +.fieldtable th { + background-image: url('nav_f.png'); + background-repeat: repeat-x; + background-color: #E2E8F2; + font-size: 90%; + color: #253555; + padding-bottom: 4px; + padding-top: 5px; + text-align: left; + font-weight: 400; + -moz-border-radius-topleft: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + border-bottom: 1px solid #A8B8D9; +} + +.tabsearch { + top: 0px; + left: 10px; + height: 36px; + background-image: url('tab_b.png'); + z-index: 101; + overflow: hidden; + font-size: 13px; +} + +.navpath ul { + font-size: 11px; + background-color: $breadcrumb-bg-color; + color: #8AA0CC; + // border:solid 1px #C2CDE4; + overflow: hidden; + margin: 0px; + padding: 0px; + display: flex; + align-items: center; +} + +.navpath li { + list-style-type: none; + // float:left; + // padding-left:10px; + padding-top: 0.25em; + padding-bottom: 0.25em; + padding-right: 0.25em; + display: flex; + padding: 0.25em; + color: $breadcrumb-fg-color; +} + +.navpath li:not(:first-child)::before { + content: "〉"; + display: block; + font-size: 1em; + font-weight: bold; + width: 1em; +} + +.navpath li.navelem a { + display: block; + text-decoration: none; + outline: none; + color: $breadcrumb-fg-color; + font-family: 'Lucida Grande', Geneva, Helvetica, Arial, sans-serif; + // text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); + text-decoration: none; +} + +.navpath li.navelem a:hover { + color: lighten($breadcrumb-fg-color, 15%); +} + +.navpath li.footer { + list-style-type: none; + float: right; + padding-left: 10px; + padding-right: 15px; + background-image: none; + background-repeat: no-repeat; + background-position: right; + color: #364D7C; + font-size: 8pt; +} + +div.summary { + float: right; + font-size: 8pt; + padding-right: 5px; + width: 50%; + text-align: right; +} + +div.summary a { + white-space: nowrap; +} + +table.classindex { + margin: 10px; + white-space: nowrap; + margin-left: 3%; + margin-right: 3%; + width: 94%; + border: 0; + border-spacing: 0; + padding: 0; +} + +div.ingroups { + font-size: 8pt; + width: 50%; + text-align: left; +} + +div.ingroups a { + white-space: nowrap; +} + +div.header { + background-color: $content-bg-color; + margin: 0px; + // border-bottom: 1px solid #C4CFE5; + color: $content-fg-color; +} + +div.headertitle { + padding: 5px 5px 5px 10px; + border-bottom: 1px solid #C4CFE5; +} + +.PageDocRTL-title div.headertitle { + text-align: right; + direction: rtl; +} + +dl { + padding: 0 0 0 0; +} + + +/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug, dl.examples */ + +dl.section { + margin-left: 0px; + padding-left: 0px; +} + +dl.section.DocNodeRTL { + margin-right: 0px; + padding-right: 0px; +} + +dl.note { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #D0C000; +} + +dl.note.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #D0C000; +} + +dl.warning, +dl.attention { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #FF0000; +} + +dl.warning.DocNodeRTL, +dl.attention.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #FF0000; +} + +dl.pre, +dl.post, +dl.invariant { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #00D000; +} + +dl.pre.DocNodeRTL, +dl.post.DocNodeRTL, +dl.invariant.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #00D000; +} + +dl.deprecated { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #505050; +} + +dl.deprecated.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #505050; +} + +dl.todo { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #00C0E0; +} + +dl.todo.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #00C0E0; +} + +dl.test { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #3030E0; +} + +dl.test.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #3030E0; +} + +dl.bug { + margin-left: -7px; + padding-left: 3px; + border-left: 4px solid; + border-color: #C08050; +} + +dl.bug.DocNodeRTL { + margin-left: 0; + padding-left: 0; + border-left: 0; + margin-right: -7px; + padding-right: 3px; + border-right: 4px solid; + border-color: #C08050; +} + +dl.section dd { + margin-bottom: 6px; +} + +#projectlogo { + text-align: center; + vertical-align: bottom; + border-collapse: separate; +} + +#projectlogo img { + border: 0px none; + height: 8em; + padding: 1em; +} + +#projectalign { + vertical-align: middle; +} + +#projectname { + font: 300% Tahoma, Arial, sans-serif; + margin: 0px; + padding: 2px 0px; +} + +#projectbrief { + font: 120% Tahoma, Arial, sans-serif; + margin: 0px; + padding: 0px; +} + +#projectnumber { + font: 50% Tahoma, Arial, sans-serif; + margin: 0px; + padding: 0px; +} + +#titlearea { + padding: 0px; + margin: 0px; + width: 100%; + color: $title-fg-color; + background-color: $title-bg-color; + border-bottom: 3px solid $white-color; +} + +.image { + text-align: center; + background-color: $image-bg-color; + margin-left: 5em; + margin-right: 5em; + padding: 0.5em; + border-radius: 4px; +} + +.dotgraph { + text-align: center; +} + +.mscgraph { + text-align: center; +} + +.plantumlgraph { + text-align: center; +} + +.diagraph { + text-align: center; +} + +.caption { + font-weight: bold; +} + +div.zoom { + border: 1px solid #90A5CE; +} + +dl.citelist { + margin-bottom: 50px; +} + +dl.citelist dt { + color: #334975; + float: left; + font-weight: bold; + margin-right: 10px; + padding: 5px; +} + +dl.citelist dd { + margin: 2px 0; + padding: 5px 0; +} + +div.toc { + padding: 14px 25px; + background-color: #F4F6FA; + border: 1px solid #D8DFEE; + border-radius: 7px 7px 7px 7px; + float: right; + height: auto; + margin: 0 8px 10px 10px; + width: 200px; +} + +.PageDocRTL-title div.toc { + float: left !important; + text-align: right; +} + +div.toc li { + background: url("bdwn.png") no-repeat scroll 0 5px transparent; + font: 10px/1.2 Verdana, DejaVu Sans, Geneva, sans-serif; + margin-top: 5px; + padding-left: 10px; + padding-top: 2px; +} + +.PageDocRTL-title div.toc li { + background-position-x: right !important; + padding-left: 0 !important; + padding-right: 10px; +} + +div.toc h3 { + font: bold 12px/1.2 Arial, FreeSans, sans-serif; + color: #4665A2; + border-bottom: 0 none; + margin: 0; +} + +div.toc ul { + list-style: none outside none; + border: medium none; + padding: 0px; +} + +div.toc li.level1 { + margin-left: 0px; +} + +div.toc li.level2 { + margin-left: 15px; +} + +div.toc li.level3 { + margin-left: 30px; +} + +div.toc li.level4 { + margin-left: 45px; +} + +.PageDocRTL-title div.toc li.level1 { + margin-left: 0 !important; + margin-right: 0; +} + +.PageDocRTL-title div.toc li.level2 { + margin-left: 0 !important; + margin-right: 15px; +} + +.PageDocRTL-title div.toc li.level3 { + margin-left: 0 !important; + margin-right: 30px; +} + +.PageDocRTL-title div.toc li.level4 { + margin-left: 0 !important; + margin-right: 45px; +} + +.inherit_header { + font-weight: bold; + color: gray; + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.inherit_header td { + padding: 6px 0px 2px 5px; +} + +.inherit { + display: none; +} + +tr.heading h2 { + //margin: 0.5em; + //margin: 0; + //margin-bottom: 0.5em; +} + + +/* tooltip related style info */ + +.ttc { + position: absolute; + display: none; +} + +#powerTip { + cursor: default; + white-space: nowrap; + background-color: white; + border: 1px solid gray; + border-radius: 4px 4px 4px 4px; + box-shadow: 1px 1px 7px gray; + display: none; + font-size: smaller; + max-width: 80%; + opacity: 0.9; + padding: 1ex 1em 1em; + position: absolute; + z-index: 2147483647; +} + +#powerTip div.ttdoc { + color: grey; + font-style: italic; +} + +#powerTip div.ttname a { + font-weight: bold; +} + +#powerTip div.ttname { + font-weight: bold; +} + +#powerTip div.ttdeci { + color: #006318; +} + +#powerTip div { + margin: 0px; + padding: 0px; + font: 12px/16px Roboto, sans-serif; +} + +#powerTip:before, +#powerTip:after { + content: ""; + position: absolute; + margin: 0px; +} + +#powerTip.n:after, +#powerTip.n:before, +#powerTip.s:after, +#powerTip.s:before, +#powerTip.w:after, +#powerTip.w:before, +#powerTip.e:after, +#powerTip.e:before, +#powerTip.ne:after, +#powerTip.ne:before, +#powerTip.se:after, +#powerTip.se:before, +#powerTip.nw:after, +#powerTip.nw:before, +#powerTip.sw:after, +#powerTip.sw:before { + border: solid transparent; + content: " "; + height: 0; + width: 0; + position: absolute; +} + +#powerTip.n:after, +#powerTip.s:after, +#powerTip.w:after, +#powerTip.e:after, +#powerTip.nw:after, +#powerTip.ne:after, +#powerTip.sw:after, +#powerTip.se:after { + border-color: rgba(255, 255, 255, 0); +} + +#powerTip.n:before, +#powerTip.s:before, +#powerTip.w:before, +#powerTip.e:before, +#powerTip.nw:before, +#powerTip.ne:before, +#powerTip.sw:before, +#powerTip.se:before { + border-color: rgba(128, 128, 128, 0); +} + +#powerTip.n:after, +#powerTip.n:before, +#powerTip.ne:after, +#powerTip.ne:before, +#powerTip.nw:after, +#powerTip.nw:before { + top: 100%; +} + +#powerTip.n:after, +#powerTip.ne:after, +#powerTip.nw:after { + border-top-color: #FFFFFF; + border-width: 10px; + margin: 0px -10px; +} + +#powerTip.n:before { + border-top-color: #808080; + border-width: 11px; + margin: 0px -11px; +} + +#powerTip.n:after, +#powerTip.n:before { + left: 50%; +} + +#powerTip.nw:after, +#powerTip.nw:before { + right: 14px; +} + +#powerTip.ne:after, +#powerTip.ne:before { + left: 14px; +} + +#powerTip.s:after, +#powerTip.s:before, +#powerTip.se:after, +#powerTip.se:before, +#powerTip.sw:after, +#powerTip.sw:before { + bottom: 100%; +} + +#powerTip.s:after, +#powerTip.se:after, +#powerTip.sw:after { + border-bottom-color: #FFFFFF; + border-width: 10px; + margin: 0px -10px; +} + +#powerTip.s:before, +#powerTip.se:before, +#powerTip.sw:before { + border-bottom-color: #808080; + border-width: 11px; + margin: 0px -11px; +} + +#powerTip.s:after, +#powerTip.s:before { + left: 50%; +} + +#powerTip.sw:after, +#powerTip.sw:before { + right: 14px; +} + +#powerTip.se:after, +#powerTip.se:before { + left: 14px; +} + +#powerTip.e:after, +#powerTip.e:before { + left: 100%; +} + +#powerTip.e:after { + border-left-color: #FFFFFF; + border-width: 10px; + top: 50%; + margin-top: -10px; +} + +#powerTip.e:before { + border-left-color: #808080; + border-width: 11px; + top: 50%; + margin-top: -11px; +} + +#powerTip.w:after, +#powerTip.w:before { + right: 100%; +} + +#powerTip.w:after { + border-right-color: #FFFFFF; + border-width: 10px; + top: 50%; + margin-top: -10px; +} + +#powerTip.w:before { + border-right-color: #808080; + border-width: 11px; + top: 50%; + margin-top: -11px; +} + +@media print { + #top { + display: none; + } + #side-nav { + display: none; + } + #nav-path { + display: none; + } + body { + overflow: visible; + } + h1, + h2, + h3, + h4, + h5, + h6 { + page-break-after: avoid; + } + .summary { + display: none; + } + .memitem { + page-break-inside: avoid; + } + #doc-content { + margin-left: 0 !important; + height: auto !important; + width: auto !important; + overflow: inherit; + display: inline; + } +} + + +/* @group Markdown */ + + +/* +table.markdownTable { + + border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; + +} + +table.markdownTable td, table.markdownTable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; + +} + +table.markdownTableHead tr { + +} + +table.markdownTableBodyLeft td, table.markdownTable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; + +} + +th.markdownTableHeadLeft th.markdownTableHeadRight th.markdownTableHeadCenter th.markdownTableHeadNone { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; + +} + +th.markdownTableHeadLeft { + text-align: left + +} + +th.markdownTableHeadRight { + text-align: right + +} + +th.markdownTableHeadCenter { + text-align: center + +} +* / + +table.markdownTable { + + border-collapse:collapse; + margin-top: 4px; + margin-bottom: 4px; + +} + +table.markdownTable td, table.markdownTable th { + border: 1px solid #2D4068; + padding: 3px 7px 2px; + +} + +table.markdownTable tr { + +} + +th.markdownTableHeadLeft, th.markdownTableHeadRight, th.markdownTableHeadCenter, th.markdownTableHeadNone { + background-color: #374F7F; + color: #FFFFFF; + font-size: 110%; + padding-bottom: 4px; + padding-top: 5px; + +} + +th.markdownTableHeadLeft, td.markdownTableBodyLeft { + text-align: left + +} + +th.markdownTableHeadRight, td.markdownTableBodyRight { + text-align: right + +} + +th.markdownTableHeadCenter, td.markdownTableBodyCenter { + text-align: center + +} + +.DocNodeRTL { + text-align:right; + direction:rtl; + +} + +.DocNodeLTR { + text-align:left; + direction:ltr; + +} + +table.DocNodeRTL { + width:auto; + margin-right: 0; + margin-left:auto; + +} + +table.DocNodeLTR { + width:auto; + margin-right:auto; + margin-left: 0; + +} + +tt, code, kbd, samp + +{ +display:inline-block; +direction:ltr; + +} +/* @end */ + +u { + text-decoration: underline; +} + + +/* +#nav-tree { + padding: 0px 0px; + font-size: 14px; + overflow: auto; +} +*/ + + +/* +.ui-resizable-e { + background: $white-color; + width: 3px; +} + +#nav-tree { + padding: 0px 0px; + background-color: $black-color; + background-image: none; + font-size: 14px; + overflow: auto; +} + +#nav-tree .selected { + background-color: $code-bg-color; + background-image: none; + color: $white-color; +} +*/ \ No newline at end of file diff --git a/doc/footer.html b/doc/footer.html new file mode 100644 index 0000000..94684c9 --- /dev/null +++ b/doc/footer.html @@ -0,0 +1,38 @@ + + + + + + + + + + diff --git a/doc/header.html b/doc/header.html new file mode 100644 index 0000000..d46710a --- /dev/null +++ b/doc/header.html @@ -0,0 +1,63 @@ + + + + + + + + + $projectname: $title + + $title + + + + $treeview + $search + $mathjax + + $extrastylesheet + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + +
+
$projectname +  $projectnumber + +
+ +
$projectbrief
+ +
+
$projectbrief
+
$searchbox
+
+ + \ No newline at end of file diff --git a/doc/resources/gdt_bits.png b/doc/resources/gdt_bits.png new file mode 100644 index 0000000..b326c18 Binary files /dev/null and b/doc/resources/gdt_bits.png differ diff --git a/doc/signal.md b/doc/signal.md new file mode 100644 index 0000000..ff79493 --- /dev/null +++ b/doc/signal.md @@ -0,0 +1,49 @@ +# Signal + +## Legenda + +The **first column** of the table represent the implementation status, +specifically: + +1. Implemented +2. Tested + +If the column is empty it means that it's not implemented yet. + +## Implementation Status + +``` + S | Number | Signal | Standard | Action | Comment +---+--------+-----------+----------+--------+------------------------------ + | 1 | SIGHUP | P1990 | Term | Hangup detected on controlling terminal or death of controlling process + | 2 | SIGINT | P1990 | Term | Interrupt from keyboard + | 3 | SIGQUIT | P1990 | Core | Quit from keyboard + | 4 | SIGILL | P1990 | Core | Illegal Instruction + | 5 | SIGTRAP | P2001 | Core | Trace/breakpoint trap + 2 | 6 | SIGABRT | P1990 | Core | Abort signal from abort(3) + | 7 | SIGEMT | - | Term | Emulator trap + 2 | 8 | SIGFPE | P1990 | Core | Floating-point exception + 2 | 9 | SIGKILL | P1990 | Term | Kill signal + | 10 | SIGBUS | P2001 | Core | Bus error (bad memory access) + | 11 | SIGSEGV | P1990 | Core | Invalid memory reference + | 12 | SIGSYS | P2001 | Core | Bad system call (SVr4); see also seccomp(2) + | 13 | SIGPIPE | P1990 | Term | Broken pipe: write to pipe with no readers; see pipe(7) + 2 | 14 | SIGALRM | P1990 | Term | Timer signal from alarm(2) + 2 | 15 | SIGTERM | P1990 | Term | Termination signal + 2 | 16 | SIGUSR1 | P1990 | Term | User-defined signal 1 + 2 | 17 | SIGUSR2 | P1990 | Term | User-defined signal 2 + 1 | 18 | SIGCHLD | P1990 | Ign | Child stopped or terminated + | 19 | SIGPWR | - | Term | Power failure (System V) + | 20 | SIGWINCH | - | Ign | Window resize signal (4.3BSD, Sun) + | 21 | SIGURG | P2001 | Ign | Urgent condition on socket (4.2BSD) + | 22 | SIGPOLL | P2001 | Term | Pollable event (Sys V); synonym for SIGIO + 2 | 23 | SIGSTOP | P1990 | Stop | Stop process + | 24 | SIGTSTP | P1990 | Stop | Stop typed at terminal + 2 | 25 | SIGCONT | P1990 | Cont | Continue if stopped + | 26 | SIGTTIN | P1990 | Stop | Terminal input for background process + | 27 | SIGTTOU | P1990 | Stop | Terminal output for background process + | 28 | SIGVTALRM | P2001 | Term | Virtual alarm clock (4.2BSD) + 2 | 29 | SIGPROF | P2001 | Term | Profiling timer expired + | 30 | SIGXCPU | P2001 | Core | CPU time limit exceeded (4.2BSD); see setrlimit(2) + | 31 | SIGXFSZ | P2001 | Core | File size limit exceeded (4.2BSD); see setrlimit(2) +``` \ No newline at end of file diff --git a/doc/syscall.md b/doc/syscall.md new file mode 100644 index 0000000..3a01c3c --- /dev/null +++ b/doc/syscall.md @@ -0,0 +1,207 @@ +# System Call + +## Legenda + +The **first column** of the table represent the implementation status, +specifically: + +1. Implemented +2. Tested + +If the column is empty it means that it's not implemented yet. + +## Implementation Status + +``` + S | EAX | Name | Source | EBX | ECX | EDX | ESX | EDI | +---+-----+----------------------------+-----------------------------+--------------------------+------------------------------+-------------------------+-----------------+------------------| + 2 | 1 | sys_exit | kernel/exit.c | int | - | - | - | - | + 2 | 2 | sys_fork | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | + 2 | 3 | sys_read | fs/read_write.c | unsigned int | char * | size_t | - | - | + 2 | 4 | sys_write | fs/read_write.c | unsigned int | const char * | size_t | - | - | + 2 | 5 | sys_open | fs/open.c | const char * | int | int | - | - | + 2 | 6 | sys_close | fs/open.c | int | - | - | - | - | + 2 | 7 | sys_waitpid | kernel/exit.c | pid_t | unsigned int * | int | - | - | + | 8 | sys_creat | fs/open.c | const char * | int | - | - | - | + | 9 | sys_link | fs/namei.c | const char * | const char * | - | - | - | + 2 | 10 | sys_unlink | fs/namei.c | const char * | - | - | - | - | + 2 | 11 | sys_execve | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | + 2 | 12 | sys_chdir | fs/open.c | const char * | - | - | - | - | + 2 | 13 | sys_time | kernel/time.c | int * | - | - | - | - | + | 14 | sys_mknod | fs/namei.c | const char * | int | dev_t | - | - | + | 15 | sys_chmod | fs/open.c | const char * | mode_t | - | - | - | + | 16 | sys_lchown | fs/open.c | const char * | uid_t | gid_t | - | - | + 2 | 18 | sys_stat | fs/stat.c | char * | struct __old_kernel_stat * | - | - | - | + 2 | 19 | sys_lseek | fs/read_write.c | unsigned int | off_t | unsigned int | - | - | + 2 | 20 | sys_getpid | kernel/sched.c | - | - | - | - | - | + | 21 | sys_mount | fs/super.c | char * | char * | char * | - | - | + | 22 | sys_oldumount | fs/super.c | char * | - | - | - | - | + | 23 | sys_setuid | kernel/sys.c | uid_t | - | - | - | - | + | 24 | sys_getuid | kernel/sched.c | - | - | - | - | - | + | 25 | sys_stime | kernel/time.c | int * | - | - | - | - | + | 26 | sys_ptrace | arch/i386/kernel/ptrace.c | long | long | long | long | - | + | 27 | sys_alarm | kernel/sched.c | unsigned int | - | - | - | - | + 2 | 28 | sys_fstat | fs/stat.c | unsigned int | struct __old_kernel_stat * | - | - | - | + | 29 | sys_pause | arch/i386/kernel/sys_i386.c | - | - | - | - | - | + | 30 | sys_utime | fs/open.c | char * | struct utimbuf * | - | - | - | + | 33 | sys_access | fs/open.c | const char * | int | - | - | - | + 1 | 34 | sys_nice | kernel/sched.c | int | - | - | - | - | + | 36 | sys_sync | fs/buffer.c | - | - | - | - | - | + 1 | 37 | sys_kill | kernel/signal.c | int | int | - | - | - | + | 38 | sys_rename | fs/namei.c | const char * | const char * | - | - | - | + 2 | 39 | sys_mkdir | fs/namei.c | const char * | int | - | - | - | + 2 | 40 | sys_rmdir | fs/namei.c | const char * | - | - | - | - | + | 41 | sys_dup | fs/fcntl.c | unsigned int | - | - | - | - | + | 42 | sys_pipe | arch/i386/kernel/sys_i386.c | unsigned long * | - | - | - | - | + | 43 | sys_times | kernel/sys.c | struct tms * | - | - | - | - | + 2 | 45 | sys_brk | mm/mmap.c | unsigned long | - | - | - | - | + | 46 | sys_setgid | kernel/sys.c | gid_t | - | - | - | - | + | 47 | sys_getgid | kernel/sched.c | - | - | - | - | - | + 1 | 48 | sys_signal | kernel/signal.c | int | __sighandler_t | - | - | - | + | 49 | sys_geteuid | kernel/sched.c | - | - | - | - | - | + | 50 | sys_getegid | kernel/sched.c | - | - | - | - | - | + | 51 | sys_acct | kernel/acct.c | const char * | - | - | - | - | + | 52 | sys_umount | fs/super.c | char * | int | - | - | - | + 1 | 54 | sys_ioctl | fs/ioctl.c | unsigned int | unsigned int | unsigned long | - | - | + | 55 | sys_fcntl | fs/fcntl.c | unsigned int | unsigned int | unsigned long | - | - | + | 57 | sys_setpgid | kernel/sys.c | pid_t | pid_t | - | - | - | + | 59 | sys_olduname | arch/i386/kernel/sys_i386.c | struct oldold_utsname * | - | - | - | - | + | 60 | sys_umask | kernel/sys.c | int | - | - | - | - | + | 61 | sys_chroot | fs/open.c | const char * | - | - | - | - | + | 62 | sys_ustat | fs/super.c | dev_t | struct ustat * | - | - | - | + | 63 | sys_dup2 | fs/fcntl.c | unsigned int | unsigned int | - | - | - | + 2 | 64 | sys_getppid | kernel/sched.c | - | - | - | - | - | + | 65 | sys_getpgrp | kernel/sys.c | - | - | - | - | - | + | 66 | sys_setsid | kernel/sys.c | - | - | - | - | - | + 1 | 67 | sys_sigaction | arch/i386/kernel/signal.c | int | const struct old_sigaction * | struct old_sigaction * | - | - | + | 68 | sys_sgetmask | kernel/signal.c | - | - | - | - | - | + | 69 | sys_ssetmask | kernel/signal.c | int | - | - | - | - | + | 70 | sys_setreuid | kernel/sys.c | uid_t | uid_t | - | - | - | + | 71 | sys_setregid | kernel/sys.c | gid_t | gid_t | - | - | - | + | 72 | sys_sigsuspend | arch/i386/kernel/signal.c | int | int | old_sigset_t | - | - | + | 73 | sys_sigpending | kernel/signal.c | old_sigset_t * | - | - | - | - | + | 74 | sys_sethostname | kernel/sys.c | char * | int | - | - | - | + | 75 | sys_setrlimit | kernel/sys.c | unsigned int | struct rlimit * | - | - | - | + | 76 | sys_getrlimit | kernel/sys.c | unsigned int | struct rlimit * | - | - | - | + | 77 | sys_getrusage | kernel/sys.c | int | struct rusage * | - | - | - | + | 78 | sys_gettimeofday | kernel/time.c | struct timeval * | struct timezone * | - | - | - | + | 79 | sys_settimeofday | kernel/time.c | struct timeval * | struct timezone * | - | - | - | + | 80 | sys_getgroups | kernel/sys.c | int | gid_t * | - | - | - | + | 81 | sys_setgroups | kernel/sys.c | int | gid_t * | - | - | - | + | 83 | sys_symlink | fs/namei.c | const char * | const char * | - | - | - | + | 84 | sys_lstat | fs/stat.c | char * | struct __old_kernel_stat * | - | - | - | + | 85 | sys_readlink | fs/stat.c | const char * | char * | int | - | - | + | 86 | sys_uselib | fs/exec.c | const char * | - | - | - | - | + | 87 | sys_swapon | mm/swapfile.c | const char * | int | - | - | - | + 1 | 88 | sys_reboot | kernel/sys.c | int | int | int | void * | - | + 1 | 89 | old_readdir | fs/readdir.c | unsigned int | void * | unsigned int | - | - | + | 90 | old_mmap | arch/i386/kernel/sys_i386.c | struct mmap_arg_struct * | - | - | - | - | + | 91 | sys_munmap | mm/mmap.c | unsigned long | size_t | - | - | - | + | 92 | sys_truncate | fs/open.c | const char * | unsigned long | - | - | - | + | 93 | sys_ftruncate | fs/open.c | unsigned int | unsigned long | - | - | - | + | 94 | sys_fchmod | fs/open.c | unsigned int | mode_t | - | - | - | + | 95 | sys_fchown | fs/open.c | unsigned int | uid_t | gid_t | - | - | + | 96 | sys_getpriority | kernel/sys.c | int | int | - | - | - | + | 97 | sys_setpriority | kernel/sys.c | int | int | int | - | - | + | 99 | sys_statfs | fs/open.c | const char * | struct statfs * | - | - | - | + | 100 | sys_fstatfs | fs/open.c | unsigned int | struct statfs * | - | - | - | + | 101 | sys_ioperm | arch/i386/kernel/ioport.c | unsigned long | unsigned long | int | - | - | + | 102 | sys_socketcall | net/socket.c | int | unsigned long * | - | - | - | + | 103 | sys_syslog | kernel/printk.c | int | char * | int | - | - | + | 104 | sys_setitimer | kernel/itimer.c | int | struct itimerval * | struct itimerval * | - | - | + | 105 | sys_getitimer | kernel/itimer.c | int | struct itimerval * | - | - | - | + | 106 | sys_newstat | fs/stat.c | char * | struct stat * | - | - | - | + | 107 | sys_newlstat | fs/stat.c | char * | struct stat * | - | - | - | + | 108 | sys_newfstat | fs/stat.c | unsigned int | struct stat * | - | - | - | + 1 | 109 | sys_uname | arch/i386/kernel/sys_i386.c | struct old_utsname * | - | - | - | - | + | 110 | sys_iopl | arch/i386/kernel/ioport.c | unsigned long | - | - | - | - | + | 111 | sys_vhangup | fs/open.c | - | - | - | - | - | + | 112 | sys_idle | arch/i386/kernel/process.c | - | - | - | - | - | + | 113 | sys_vm86old | arch/i386/kernel/vm86.c | unsigned long | struct vm86plus_struct * | - | - | - | + | 114 | sys_wait4 | kernel/exit.c | pid_t | unsigned long * | int options | struct rusage * | - | + | 115 | sys_swapoff | mm/swapfile.c | const char * | - | - | - | - | + | 116 | sys_sysinfo | kernel/info.c | struct sysinfo * | - | - | - | - | + * | 117 | sys_ipc(*Note) | arch/i386/kernel/sys_i386.c | uint | int | int | int | void * | + | 118 | sys_fsync | fs/buffer.c | unsigned int | - | - | - | - | + | 119 | sys_sigreturn | arch/i386/kernel/signal.c | unsigned long | - | - | - | - | + | 120 | sys_clone | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | + | 121 | sys_setdomainname | kernel/sys.c | char * | int | - | - | - | + | 122 | sys_newuname | kernel/sys.c | struct new_utsname * | - | - | - | - | + | 123 | sys_modify_ldt | arch/i386/kernel/ldt.c | int | void * | unsigned long | - | - | + | 124 | sys_adjtimex | kernel/time.c | struct timex * | - | - | - | - | + | 125 | sys_mprotect | mm/mprotect.c | unsigned long | size_t | unsigned long | - | - | + 1 | 126 | sys_sigprocmask | kernel/signal.c | int | old_sigset_t * | old_sigset_t * | - | - | + | 127 | sys_create_module | kernel/module.c | const char * | size_t | - | - | - | + | 128 | sys_init_module | kernel/module.c | const char * | struct module * | - | - | - | + | 129 | sys_delete_module | kernel/module.c | const char * | - | - | - | - | + | 130 | sys_get_kernel_syms | kernel/module.c | struct kernel_sym * | - | - | - | - | + | 131 | sys_quotactl | fs/dquot.c | int | const char * | int | caddr_t | - | + | 132 | sys_getpgid | kernel/sys.c | pid_t | - | - | - | - | + 1 | 133 | sys_fchdir | fs/open.c | unsigned int | - | - | - | - | + | 134 | sys_bdflush | fs/buffer.c | int | long | - | - | - | + | 135 | sys_sysfs | fs/super.c | int | unsigned long | unsigned long | - | - | + | 136 | sys_personality | kernel/exec_domain.c | unsigned long | - | - | - | - | + | 138 | sys_setfsuid | kernel/sys.c | uid_t | - | - | - | - | + | 139 | sys_setfsgid | kernel/sys.c | gid_t | - | - | - | - | + | 140 | sys_llseek | fs/read_write.c | unsigned int | unsigned long | unsigned long | loff_t * | unsigned int | + 2 | 141 | sys_getdents | fs/readdir.c | unsigned int | void * | unsigned int | - | - | + | 142 | sys_select | fs/select.c | int | fd_set * | fd_set * | fd_set * | struct timeval * | + | 143 | sys_flock | fs/locks.c | unsigned int | unsigned int | - | - | - | + | 144 | sys_msync | mm/filemap.c | unsigned long | size_t | int | - | - | + | 145 | sys_readv | fs/read_write.c | unsigned long | const struct iovec * | unsigned long | - | - | + | 146 | sys_writev | fs/read_write.c | unsigned long | const struct iovec * | unsigned long | - | - | + | 147 | sys_getsid | kernel/sys.c | pid_t | - | - | - | - | + | 148 | sys_fdatasync | fs/buffer.c | unsigned int | - | - | - | - | + | 149 | sys_sysctl | kernel/sysctl.c | struct __sysctl_args * | - | - | - | - | + | 150 | sys_mlock | mm/mlock.c | unsigned long | size_t | - | - | - | + | 151 | sys_munlock | mm/mlock.c | unsigned long | size_t | - | - | - | + | 152 | sys_mlockall | mm/mlock.c | int | - | - | - | - | + | 153 | sys_munlockall | mm/mlock.c | - | - | - | - | - | + 1 | 154 | sys_sched_setparam | kernel/sched.c | pid_t | struct sched_param * | - | - | - | + 1 | 155 | sys_sched_getparam | kernel/sched.c | pid_t | struct sched_param * | - | - | - | + | 156 | sys_sched_setscheduler | kernel/sched.c | pid_t | int | struct sched_param * | - | - | + | 157 | sys_sched_getscheduler | kernel/sched.c | pid_t | - | - | - | - | + | 158 | sys_sched_yield | kernel/sched.c | - | - | - | - | - | + | 159 | sys_sched_get_priority_max | kernel/sched.c | int | - | - | - | - | + | 160 | sys_sched_get_priority_min | kernel/sched.c | int | - | - | - | - | + | 161 | sys_sched_rr_get_interval | kernel/sched.c | pid_t | struct timespec * | - | - | - | + | 162 | sys_nanosleep | kernel/sched.c | struct timespec * | struct timespec * | - | - | - | + | 163 | sys_mremap | mm/mremap.c | unsigned long | unsigned long | unsigned long | unsigned long | - | + | 164 | sys_setresuid | kernel/sys.c | uid_t | uid_t | uid_t | - | - | + | 165 | sys_getresuid | kernel/sys.c | uid_t * | uid_t * | uid_t * | - | - | + | 166 | sys_vm86 | arch/i386/kernel/vm86.c | struct vm86_struct * | - | - | - | - | + | 167 | sys_query_module | kernel/module.c | const char * | int | char * | size_t | size_t * | + | 168 | sys_poll | fs/select.c | struct pollfd * | unsigned int | long | - | - | + | 169 | sys_nfsservctl | fs/filesystems.c | int | void * | void * | - | - | + | 170 | sys_setresgid | kernel/sys.c | gid_t | gid_t | gid_t | - | - | + | 171 | sys_getresgid | kernel/sys.c | gid_t * | gid_t * | gid_t * | - | - | + | 172 | sys_prctl | kernel/sys.c | int | unsigned long | unsigned long | unsigned long | unsigned long | + | 173 | sys_rt_sigreturn | arch/i386/kernel/signal.c | unsigned long | - | - | - | - | + | 174 | sys_rt_sigaction | kernel/signal.c | int | const struct sigaction * | struct sigaction * | size_t | - | + | 175 | sys_rt_sigprocmask | kernel/signal.c | int | sigset_t * | sigset_t * | size_t | - | + | 176 | sys_rt_sigpending | kernel/signal.c | sigset_t * | size_t | - | - | - | + | 177 | sys_rt_sigtimedwait | kernel/signal.c | const sigset_t * | siginfo_t * | const struct timespec * | size_t | - | + | 178 | sys_rt_sigqueueinfo | kernel/signal.c | int | int | siginfo_t * | - | - | + | 179 | sys_rt_sigsuspend | arch/i386/kernel/signal.c | sigset_t * | size_t | - | - | - | + | 180 | sys_pread | fs/read_write.c | unsigned int | char * | size_t | loff_t | - | + | 181 | sys_pwrite | fs/read_write.c | unsigned int | const char * | size_t | loff_t | - | + | 182 | sys_chown | fs/open.c | const char * | uid_t | gid_t | - | - | + 1 | 183 | sys_getcwd | fs/dcache.c | char * | unsigned long | - | - | - | + | 184 | sys_capget | kernel/capability.c | cap_user_header_t | cap_user_data_t | - | - | - | + | 185 | sys_capset | kernel/capability.c | cap_user_header_t | const cap_user_data_t | - | - | - | + | 186 | sys_sigaltstack | arch/i386/kernel/signal.c | const stack_t * | stack_t * | - | - | - | + | 187 | sys_sendfile | mm/filemap.c | int | int | off_t * | size_t | - | + | | | | | | | | | + 2 | 188 | sys_waitperiod | kernel/sched.c | | | | | | + | 189 | sys_msgctl | | int | | | | | + | 190 | sys_msgget | | | | | | | + | 191 | sys_msgrcv | | | | | | | + | 192 | sys_msgsnd | | | | | | | + | 193 | sys_semctl | | | | | | | + | 194 | sys_semget | | | | | | | + | 195 | sys_semop | | | | | | | + | 196 | sys_shmat | | | | | | | + | 197 | sys_shmctl | | | | | | | + | 198 | sys_shmdt | | | | | | | + | 199 | sys_shmget | | | | | | | +``` \ No newline at end of file diff --git a/doc/syscall.txt b/doc/syscall.txt deleted file mode 100644 index 6c8b52e..0000000 --- a/doc/syscall.txt +++ /dev/null @@ -1,181 +0,0 @@ -| EAX | Name | Source | EBX | ECX | EDX | ESX | EDI | -|:----|:---------------------------|:----------------------------|:-------------------------|:-----------------------------|:------------------------|:----------------|:-----------------| -| 1 | sys_exit | kernel/exit.c | int | - | - | - | - | -| 2 | sys_fork | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | -| 3 | sys_read | fs/read_write.c | unsigned int | char * | size_t | - | - | -| 4 | sys_write | fs/read_write.c | unsigned int | const char * | size_t | - | - | -| 5 | sys_open | fs/open.c | const char * | int | int | - | - | -| 6 | sys_close | fs/open.c | unsigned int | - | - | - | - | -| 7 | sys_waitpid | kernel/exit.c | pid_t | unsigned int * | int | - | - | -| 8 | sys_creat | fs/open.c | const char * | int | - | - | - | -| 9 | sys_link | fs/namei.c | const char * | const char * | - | - | - | -| 10 | sys_unlink | fs/namei.c | const char * | - | - | - | - | -| 11 | sys_execve | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | -| 12 | sys_chdir | fs/open.c | const char * | - | - | - | - | -| 13 | sys_time | kernel/time.c | int * | - | - | - | - | -| 14 | sys_mknod | fs/namei.c | const char * | int | dev_t | - | - | -| 15 | sys_chmod | fs/open.c | const char * | mode_t | - | - | - | -| 16 | sys_lchown | fs/open.c | const char * | uid_t | gid_t | - | - | -| 18 | sys_stat | fs/stat.c | char * | struct __old_kernel_stat * | - | - | - | -| 19 | sys_lseek | fs/read_write.c | unsigned int | off_t | unsigned int | - | - | -| 20 | sys_getpid | kernel/sched.c | - | - | - | - | - | -| 21 | sys_mount | fs/super.c | char * | char * | char * | - | - | -| 22 | sys_oldumount | fs/super.c | char * | - | - | - | - | -| 23 | sys_setuid | kernel/sys.c | uid_t | - | - | - | - | -| 24 | sys_getuid | kernel/sched.c | - | - | - | - | - | -| 25 | sys_stime | kernel/time.c | int * | - | - | - | - | -| 26 | sys_ptrace | arch/i386/kernel/ptrace.c | long | long | long | long | - | -| 27 | sys_alarm | kernel/sched.c | unsigned int | - | - | - | - | -| 28 | sys_fstat | fs/stat.c | unsigned int | struct __old_kernel_stat * | - | - | - | -| 29 | sys_pause | arch/i386/kernel/sys_i386.c | - | - | - | - | - | -| 30 | sys_utime | fs/open.c | char * | struct utimbuf * | - | - | - | -| 33 | sys_access | fs/open.c | const char * | int | - | - | - | -| 34 | sys_nice | kernel/sched.c | int | - | - | - | - | -| 36 | sys_sync | fs/buffer.c | - | - | - | - | - | -| 37 | sys_kill | kernel/signal.c | int | int | - | - | - | -| 38 | sys_rename | fs/namei.c | const char * | const char * | - | - | - | -| 39 | sys_mkdir | fs/namei.c | const char * | int | - | - | - | -| 40 | sys_rmdir | fs/namei.c | const char * | - | - | - | - | -| 41 | sys_dup | fs/fcntl.c | unsigned int | - | - | - | - | -| 42 | sys_pipe | arch/i386/kernel/sys_i386.c | unsigned long * | - | - | - | - | -| 43 | sys_times | kernel/sys.c | struct tms * | - | - | - | - | -| 45 | sys_brk | mm/mmap.c | unsigned long | - | - | - | - | -| 46 | sys_setgid | kernel/sys.c | gid_t | - | - | - | - | -| 47 | sys_getgid | kernel/sched.c | - | - | - | - | - | -| 48 | sys_signal | kernel/signal.c | int | __sighandler_t | - | - | - | -| 49 | sys_geteuid | kernel/sched.c | - | - | - | - | - | -| 50 | sys_getegid | kernel/sched.c | - | - | - | - | - | -| 51 | sys_acct | kernel/acct.c | const char * | - | - | - | - | -| 52 | sys_umount | fs/super.c | char * | int | - | - | - | -| 54 | sys_ioctl | fs/ioctl.c | unsigned int | unsigned int | unsigned long | - | - | -| 55 | sys_fcntl | fs/fcntl.c | unsigned int | unsigned int | unsigned long | - | - | -| 57 | sys_setpgid | kernel/sys.c | pid_t | pid_t | - | - | - | -| 59 | sys_olduname | arch/i386/kernel/sys_i386.c | struct oldold_utsname * | - | - | - | - | -| 60 | sys_umask | kernel/sys.c | int | - | - | - | - | -| 61 | sys_chroot | fs/open.c | const char * | - | - | - | - | -| 62 | sys_ustat | fs/super.c | dev_t | struct ustat * | - | - | - | -| 63 | sys_dup2 | fs/fcntl.c | unsigned int | unsigned int | - | - | - | -| 64 | sys_getppid | kernel/sched.c | - | - | - | - | - | -| 65 | sys_getpgrp | kernel/sys.c | - | - | - | - | - | -| 66 | sys_setsid | kernel/sys.c | - | - | - | - | - | -| 67 | sys_sigaction | arch/i386/kernel/signal.c | int | const struct old_sigaction * | struct old_sigaction * | - | - | -| 68 | sys_sgetmask | kernel/signal.c | - | - | - | - | - | -| 69 | sys_ssetmask | kernel/signal.c | int | - | - | - | - | -| 70 | sys_setreuid | kernel/sys.c | uid_t | uid_t | - | - | - | -| 71 | sys_setregid | kernel/sys.c | gid_t | gid_t | - | - | - | -| 72 | sys_sigsuspend | arch/i386/kernel/signal.c | int | int | old_sigset_t | - | - | -| 73 | sys_sigpending | kernel/signal.c | old_sigset_t * | - | - | - | - | -| 74 | sys_sethostname | kernel/sys.c | char * | int | - | - | - | -| 75 | sys_setrlimit | kernel/sys.c | unsigned int | struct rlimit * | - | - | - | -| 76 | sys_getrlimit | kernel/sys.c | unsigned int | struct rlimit * | - | - | - | -| 77 | sys_getrusage | kernel/sys.c | int | struct rusage * | - | - | - | -| 78 | sys_gettimeofday | kernel/time.c | struct timeval * | struct timezone * | - | - | - | -| 79 | sys_settimeofday | kernel/time.c | struct timeval * | struct timezone * | - | - | - | -| 80 | sys_getgroups | kernel/sys.c | int | gid_t * | - | - | - | -| 81 | sys_setgroups | kernel/sys.c | int | gid_t * | - | - | - | - -| 83 | sys_symlink | fs/namei.c | const char * | const char * | - | - | - | -| 84 | sys_lstat | fs/stat.c | char * | struct __old_kernel_stat * | - | - | - | -| 85 | sys_readlink | fs/stat.c | const char * | char * | int | - | - | -| 86 | sys_uselib | fs/exec.c | const char * | - | - | - | - | -| 87 | sys_swapon | mm/swapfile.c | const char * | int | - | - | - | -| 88 | sys_reboot | kernel/sys.c | int | int | int | void * | - | -| 89 | old_readdir | fs/readdir.c | unsigned int | void * | unsigned int | - | - | -| 90 | old_mmap | arch/i386/kernel/sys_i386.c | struct mmap_arg_struct * | - | - | - | - | -| 91 | sys_munmap | mm/mmap.c | unsigned long | size_t | - | - | - | -| 92 | sys_truncate | fs/open.c | const char * | unsigned long | - | - | - | -| 93 | sys_ftruncate | fs/open.c | unsigned int | unsigned long | - | - | - | -| 94 | sys_fchmod | fs/open.c | unsigned int | mode_t | - | - | - | -| 95 | sys_fchown | fs/open.c | unsigned int | uid_t | gid_t | - | - | -| 96 | sys_getpriority | kernel/sys.c | int | int | - | - | - | -| 97 | sys_setpriority | kernel/sys.c | int | int | int | - | - | -| 99 | sys_statfs | fs/open.c | const char * | struct statfs * | - | - | - | -| 100 | sys_fstatfs | fs/open.c | unsigned int | struct statfs * | - | - | - | -| 101 | sys_ioperm | arch/i386/kernel/ioport.c | unsigned long | unsigned long | int | - | - | -| 102 | sys_socketcall | net/socket.c | int | unsigned long * | - | - | - | -| 103 | sys_syslog | kernel/printk.c | int | char * | int | - | - | -| 104 | sys_setitimer | kernel/itimer.c | int | struct itimerval * | struct itimerval * | - | - | -| 105 | sys_getitimer | kernel/itimer.c | int | struct itimerval * | - | - | - | -| 106 | sys_newstat | fs/stat.c | char * | struct stat * | - | - | - | -| 107 | sys_newlstat | fs/stat.c | char * | struct stat * | - | - | - | -| 108 | sys_newfstat | fs/stat.c | unsigned int | struct stat * | - | - | - | -| 109 | sys_uname | arch/i386/kernel/sys_i386.c | struct old_utsname * | - | - | - | - | -| 110 | sys_iopl | arch/i386/kernel/ioport.c | unsigned long | - | - | - | - | -| 111 | sys_vhangup | fs/open.c | - | - | - | - | - | -| 112 | sys_idle | arch/i386/kernel/process.c | - | - | - | - | - | -| 113 | sys_vm86old | arch/i386/kernel/vm86.c | unsigned long | struct vm86plus_struct * | - | - | - | -| 114 | sys_wait4 | kernel/exit.c | pid_t | unsigned long * | int options | struct rusage * | - | -| 115 | sys_swapoff | mm/swapfile.c | const char * | - | - | - | - | -| 116 | sys_sysinfo | kernel/info.c | struct sysinfo * | - | - | - | - | -| 117 | sys_ipc(*Note) | arch/i386/kernel/sys_i386.c | uint | int | int | int | void * | -| 118 | sys_fsync | fs/buffer.c | unsigned int | - | - | - | - | -| 119 | sys_sigreturn | arch/i386/kernel/signal.c | unsigned long | - | - | - | - | -| 120 | sys_clone | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | -| 121 | sys_setdomainname | kernel/sys.c | char * | int | - | - | - | -| 122 | sys_newuname | kernel/sys.c | struct new_utsname * | - | - | - | - | -| 123 | sys_modify_ldt | arch/i386/kernel/ldt.c | int | void * | unsigned long | - | - | -| 124 | sys_adjtimex | kernel/time.c | struct timex * | - | - | - | - | -| 125 | sys_mprotect | mm/mprotect.c | unsigned long | size_t | unsigned long | - | - | -| 126 | sys_sigprocmask | kernel/signal.c | int | old_sigset_t * | old_sigset_t * | - | - | -| 127 | sys_create_module | kernel/module.c | const char * | size_t | - | - | - | -| 128 | sys_init_module | kernel/module.c | const char * | struct module * | - | - | - | -| 129 | sys_delete_module | kernel/module.c | const char * | - | - | - | - | -| 130 | sys_get_kernel_syms | kernel/module.c | struct kernel_sym * | - | - | - | - | -| 131 | sys_quotactl | fs/dquot.c | int | const char * | int | caddr_t | - | -| 132 | sys_getpgid | kernel/sys.c | pid_t | - | - | - | - | -| 133 | sys_fchdir | fs/open.c | unsigned int | - | - | - | - | -| 134 | sys_bdflush | fs/buffer.c | int | long | - | - | - | -| 135 | sys_sysfs | fs/super.c | int | unsigned long | unsigned long | - | - | -| 136 | sys_personality | kernel/exec_domain.c | unsigned long | - | - | - | - | -| 138 | sys_setfsuid | kernel/sys.c | uid_t | - | - | - | - | -| 139 | sys_setfsgid | kernel/sys.c | gid_t | - | - | - | - | -| 140 | sys_llseek | fs/read_write.c | unsigned int | unsigned long | unsigned long | loff_t * | unsigned int | -| 141 | sys_getdents | fs/readdir.c | unsigned int | void * | unsigned int | - | - | -| 142 | sys_select | fs/select.c | int | fd_set * | fd_set * | fd_set * | struct timeval * | -| 143 | sys_flock | fs/locks.c | unsigned int | unsigned int | - | - | - | -| 144 | sys_msync | mm/filemap.c | unsigned long | size_t | int | - | - | -| 145 | sys_readv | fs/read_write.c | unsigned long | const struct iovec * | unsigned long | - | - | -| 146 | sys_writev | fs/read_write.c | unsigned long | const struct iovec * | unsigned long | - | - | -| 147 | sys_getsid | kernel/sys.c | pid_t | - | - | - | - | -| 148 | sys_fdatasync | fs/buffer.c | unsigned int | - | - | - | - | -| 149 | sys_sysctl | kernel/sysctl.c | struct __sysctl_args * | - | - | - | - | -| 150 | sys_mlock | mm/mlock.c | unsigned long | size_t | - | - | - | -| 151 | sys_munlock | mm/mlock.c | unsigned long | size_t | - | - | - | -| 152 | sys_mlockall | mm/mlock.c | int | - | - | - | - | -| 153 | sys_munlockall | mm/mlock.c | - | - | - | - | - | -| 154 | sys_sched_setparam | kernel/sched.c | pid_t | struct sched_param * | - | - | - | -| 155 | sys_sched_getparam | kernel/sched.c | pid_t | struct sched_param * | - | - | - | -| 156 | sys_sched_setscheduler | kernel/sched.c | pid_t | int | struct sched_param * | - | - | -| 157 | sys_sched_getscheduler | kernel/sched.c | pid_t | - | - | - | - | -| 158 | sys_sched_yield | kernel/sched.c | - | - | - | - | - | -| 159 | sys_sched_get_priority_max | kernel/sched.c | int | - | - | - | - | -| 160 | sys_sched_get_priority_min | kernel/sched.c | int | - | - | - | - | -| 161 | sys_sched_rr_get_interval | kernel/sched.c | pid_t | struct timespec * | - | - | - | -| 162 | sys_nanosleep | kernel/sched.c | struct timespec * | struct timespec * | - | - | - | -| 163 | sys_mremap | mm/mremap.c | unsigned long | unsigned long | unsigned long | unsigned long | - | -| 164 | sys_setresuid | kernel/sys.c | uid_t | uid_t | uid_t | - | - | -| 165 | sys_getresuid | kernel/sys.c | uid_t * | uid_t * | uid_t * | - | - | -| 166 | sys_vm86 | arch/i386/kernel/vm86.c | struct vm86_struct * | - | - | - | - | -| 167 | sys_query_module | kernel/module.c | const char * | int | char * | size_t | size_t * | -| 168 | sys_poll | fs/select.c | struct pollfd * | unsigned int | long | - | - | -| 169 | sys_nfsservctl | fs/filesystems.c | int | void * | void * | - | - | -| 170 | sys_setresgid | kernel/sys.c | gid_t | gid_t | gid_t | - | - | -| 171 | sys_getresgid | kernel/sys.c | gid_t * | gid_t * | gid_t * | - | - | -| 172 | sys_prctl | kernel/sys.c | int | unsigned long | unsigned long | unsigned long | unsigned long | -| 173 | sys_rt_sigreturn | arch/i386/kernel/signal.c | unsigned long | - | - | - | - | -| 174 | sys_rt_sigaction | kernel/signal.c | int | const struct sigaction * | struct sigaction * | size_t | - | -| 175 | sys_rt_sigprocmask | kernel/signal.c | int | sigset_t * | sigset_t * | size_t | - | -| 176 | sys_rt_sigpending | kernel/signal.c | sigset_t * | size_t | - | - | - | -| 177 | sys_rt_sigtimedwait | kernel/signal.c | const sigset_t * | siginfo_t * | const struct timespec * | size_t | - | -| 178 | sys_rt_sigqueueinfo | kernel/signal.c | int | int | siginfo_t * | - | - | -| 179 | sys_rt_sigsuspend | arch/i386/kernel/signal.c | sigset_t * | size_t | - | - | - | -| 180 | sys_pread | fs/read_write.c | unsigned int | char * | size_t | loff_t | - | -| 181 | sys_pwrite | fs/read_write.c | unsigned int | const char * | size_t | loff_t | - | -| 182 | sys_chown | fs/open.c | const char * | uid_t | gid_t | - | - | -| 183 | sys_getcwd | fs/dcache.c | char * | unsigned long | - | - | - | -| 184 | sys_capget | kernel/capability.c | cap_user_header_t | cap_user_data_t | - | - | - | -| 185 | sys_capset | kernel/capability.c | cap_user_header_t | const cap_user_data_t | - | - | - | -| 186 | sys_sigaltstack | arch/i386/kernel/signal.c | const stack_t * | stack_t * | - | - | - | -| 187 | sys_sendfile | mm/filemap.c | int | int | off_t * | size_t | - | -| 190 | sys_vfork | arch/i386/kernel/process.c | struct pt_regs | - | - | - | - | -| | | | | | | | | \ No newline at end of file diff --git a/files/etc/group b/files/etc/group new file mode 100644 index 0000000..1fb11d4 --- /dev/null +++ b/files/etc/group @@ -0,0 +1,2 @@ +root:x:0:user,root,marco +user:x:1000:banana,marco \ No newline at end of file diff --git a/files/etc/hostname b/files/etc/hostname new file mode 100644 index 0000000..db49e60 --- /dev/null +++ b/files/etc/hostname @@ -0,0 +1 @@ +avalon \ No newline at end of file diff --git a/files/etc/passwd b/files/etc/passwd new file mode 100644 index 0000000..6f1c914 --- /dev/null +++ b/files/etc/passwd @@ -0,0 +1,2 @@ +root:root:0:0:Root User:/:/bin/shell +user:user:1000:1000:Normal User:/home/user:/bin/shell \ No newline at end of file diff --git a/files/passwd b/files/passwd deleted file mode 100644 index 4af77b6..0000000 --- a/files/passwd +++ /dev/null @@ -1,3 +0,0 @@ -root:password -user:computer -asd:asd diff --git a/files/prog/more.c b/files/prog/more.c deleted file mode 100644 index fa67ac4..0000000 --- a/files/prog/more.c +++ /dev/null @@ -1,28 +0,0 @@ -#include -#include - -void main(int argc, char ** argv) -{ - if (argc == 2) - { - // Try to open the file. - int fd = open(argv[1], O_RDONLY, 42); - if (fd > -1) - { - char c; - // Put on the standard output the characters. - while (read(fd, &c, 1)) putchar(c); - putchar('\n'); - putchar('\n'); - close(fd); - } - else - { - printf("%s: cannot find the file.\n\n", argv[1]); - } - } - else - { - printf("Usage: more file\n\n"); - } -} \ No newline at end of file diff --git a/files/test2.txt b/files/test2.txt index 111264b..783b6fc 100644 --- a/files/test2.txt +++ b/files/test2.txt @@ -1 +1,3 @@ come va ? +AAA + diff --git a/initscp/CMakeLists.txt b/initscp/CMakeLists.txt index cb8563e..3dfcec9 100644 --- a/initscp/CMakeLists.txt +++ b/initscp/CMakeLists.txt @@ -1,6 +1,6 @@ # ------------------------------------------------------------------------------ # Initialize the project. -project(initfscp C ASM) +project(initfscp C) # ------------------------------------------------------------------------------ # Set the project name. @@ -19,10 +19,6 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") -# ------------------------------------------------------------------------------ -# Add the includes. -include_directories(inc) - # ------------------------------------------------------------------------------ # Add the source files. set(SOURCE_FILES src/initfscp.c) diff --git a/initscp/inc/initfscp.h b/initscp/inc/initfscp.h deleted file mode 100644 index 87157e4..0000000 --- a/initscp/inc/initfscp.h +++ /dev/null @@ -1,42 +0,0 @@ -/// @file initfscp.h -/// @brief - -#pragma once - -#define MAX_FILENAME_LENGTH 64 -#define MAX_FILES 32 -#define INITFSCP_VER "0.3.0" - -#define FS_FILE 0x01 ///< Identifies a file. -#define FS_DIRECTORY 0x02 ///< Identifies a directory. -#define FS_CHARDEVICE 0x04 ///< Identifies a character devies. -#define FS_BLOCKDEVICE 0x08 ///< Identifies a block devies. -#define FS_PIPE 0x10 ///< Identifies a pipe. -#define FS_SYMLINK 0x20 ///< Identifies a symbolic link. -#define FS_MOUNTPOINT 0x40 ///< Identifies a mount-point. - -#define RESET "\033[00m" -#define BLACK "\033[30m" -#define RED "\033[31m" -#define GREEN "\033[32m" -#define YELLOW "\033[33m" -#define BLUE "\033[34m" -#define MAGENTA "\033[35m" -#define CYAN "\033[36m" -#define WHITE "\033[37m" - -/// @brief Information concerning a file. -typedef struct initrd_file_t { - /// Number used as delimiter, it must be set to 0xBF. - int magic; - /// The name of the file. - char fileName[MAX_FILENAME_LENGTH]; - /// The type of the file. - short int file_type; - /// The uid of the owner. - int uid; - /// Offset of the starting address. - unsigned int offset; - /// Dimension of the file. - unsigned int length; -} initrd_file_t; diff --git a/initscp/src/initfscp.c b/initscp/src/initfscp.c index 4b7ec7e..086e129 100755 --- a/initscp/src/initfscp.c +++ b/initscp/src/initfscp.c @@ -4,321 +4,401 @@ #include #include #include -#include -#include #include #include #include #include +#include +#include +#include +#include +#include +#include -static FILE *target_fs = NULL; -static initrd_file_t headers[MAX_FILES]; -static char mount_points[MAX_FILES][MAX_FILENAME_LENGTH]; -static int mountpoint_idx = 0; -static int header_idx = 0; -static int header_offset = 0; +#define INITFSCP_VER "0.3.0" + +#define INITRD_NAME_MAX 255 +#define INITRD_MAX_FILES 128 +#define INITRD_MAX_FS_SIZE 1048576 + +#define RESET "\033[00m" +#define BLACK "\033[30m" +#define RED "\033[31m" +#define GREEN "\033[32m" +#define YELLOW "\033[33m" +#define BLUE "\033[34m" +#define MAGENTA "\033[35m" +#define CYAN "\033[36m" +#define WHITE "\033[37m" + +static const char dt_char_array[] = { + '?', // DT_UNKNOWN = 0, + 'p', //DT_FIFO = 1, + 'c', //DT_CHR = 2, + '*', + 'd', //DT_DIR = 4, + '*', + 'b', //DT_BLK = 6, + '*', + '-', //DT_REG = 8, + '*', + 'l', //DT_LNK = 10, + '*', + 's', //DT_SOCK = 12, + '*', + '?', //DT_WHT = 14 +}; + +/// @brief Information concerning a file. +typedef struct initrd_file_t { + /// Number used as delimiter, it must be set to 0xBF. + int magic; + /// The inode of the file. + unsigned int inode; + /// The name of the file. + char name[INITRD_NAME_MAX]; + /// The type of the file. + short int file_type; + /// The permissions mask. + unsigned int mask; + /// The id of the owner. + unsigned int uid; + /// The id of the group. + unsigned int gid; + /// Time of last access. + unsigned int atime; + /// Time of last data modification. + unsigned int mtime; + /// Time of last status change. + unsigned int ctime; + /// Offset of the starting address. + unsigned int offset; + /// Dimension of the file. + unsigned int length; +} __attribute__((aligned(16))) initrd_file_t; + +/// @brief The details regarding the filesystem. +/// @brief Contains the number of files inside the initrd filesystem. +typedef struct initrd_t { + /// Number of files. + unsigned int nfiles; + /// List of headers. + initrd_file_t headers[INITRD_MAX_FILES]; +} __attribute__((aligned(16))) initrd_t; + +// Prepare a variable to keep track of the offsets inside the FS. By +// default skip the initial space for the FS details structure. +static int fs_offset = sizeof(initrd_t); static inline void usage(char *prgname) { - printf("Usage:\n"); - printf(" %s --help For this screen\n", prgname); - printf(" %s --source [-s] The source directory.\n", prgname); - printf(" %s --target [-t] The target file for the initfs.\n", prgname); + printf("Usage:\n"); + printf(" %s --help For this screen\n", prgname); + printf(" %s --source [-s] The source directory.\n", prgname); + printf(" %s --target [-t] The target file for the initrd.\n", prgname); } static inline void version(char *prgname) { - printf("%s version: %s\n", prgname, INITFSCP_VER); + printf("%s version: %s\n", prgname, INITFSCP_VER); } -static inline bool is_option_mount_point(char *arg) +static inline int is_option_mount_point(char *arg) { - if (arg == NULL) - return false; - return ((strcmp(arg, "-m") == 0) || (strcmp(arg, "--mountpoint") == 0)); + return ((strcmp(arg, "-m") == 0) || (strcmp(arg, "--mountpoint") == 0)); } -static inline bool is_option_source(char *arg) +static inline int is_option_source(char *arg) { - if (arg == NULL) - return false; - return ((strcmp(arg, "-s") == 0) || (strcmp(arg, "--source") == 0)); + return ((strcmp(arg, "-s") == 0) || (strcmp(arg, "--source") == 0)); } -static inline bool is_option_target(char *arg) +static inline int is_option_target(char *arg) { - if (arg == NULL) - return false; - return ((strcmp(arg, "-t") == 0) || (strcmp(arg, "--target") == 0)); + return ((strcmp(arg, "-t") == 0) || (strcmp(arg, "--target") == 0)); } -static inline bool is_mount_point(char *name) +static int open_target_fs(int argc, char *argv[]) { - if (name == NULL) - return false; - // Check if the name matches with a mount-point. - for (int i = 0; i < mountpoint_idx; ++i) { - if (strcmp(name, mount_points[i]) == 0) { - return true; - } - } - return false; + printf("%-64s", "Opening target filesystem..."); + int fd = -1; + for (int i = 1; i < argc; ++i) { + if (is_option_target(argv[i]) && ((i + 1) < argc)) { + fd = open(argv[i + 1], O_CREAT | O_RDWR, S_IRWXU | S_IRWXG | S_IRWXO); + break; + } + } + if (fd == -1) + printf("[" RED "FAILED" RESET "]\n\n"); + else + printf("[" GREEN "DONE" RESET "]\n\n"); + return fd; } -static FILE *openfile(const char *dirname, struct dirent *dir, const char *mode) +static void init_headers(initrd_t *initrd) { - char pathname[1024]; - FILE *fp; - sprintf(pathname, "%s/%s", dirname, dir->d_name); - fp = fopen(pathname, mode); - return fp; + printf("%-64s", "Initializing headers structures..."); + for (size_t i = 0; i < INITRD_MAX_FILES; i++) { + initrd->headers[i].magic = 0xBF; + initrd->headers[i].inode = i; + initrd->headers[i].file_type = 0; + initrd->headers[i].mask = 0; + initrd->headers[i].uid = 0; + initrd->headers[i].gid = 0; + initrd->headers[i].atime = time(NULL); + initrd->headers[i].mtime = time(NULL); + initrd->headers[i].ctime = time(NULL); + initrd->headers[i].offset = 0; + initrd->headers[i].length = 0; + + memset(initrd->headers[i].name, 0, INITRD_NAME_MAX); + } + printf("[" GREEN "DONE" RESET "]\n\n"); } -static bool open_target_fs(int argc, char *argv[]) +static int create_file_headers(initrd_t *initrd, char *mountpoint, char *directory) { - printf("%-64s", "Opening target filesystem..."); - for (int i = 1; i < argc; ++i) { - if (is_option_target(argv[i]) && ((i + 1) < argc)) { - target_fs = fopen(argv[i + 1], "w"); - printf("[" GREEN "DONE" RESET "]\n\n"); - return true; - } - } - printf("[" RED "FAILED" RESET "]\n\n"); - return false; + assert(mountpoint && "Received null mountpoint."); + assert(directory && "Received null directory."); + + // ------------------------------------------------------------------------ + char directory_path_abs[PATH_MAX], entry_path_abs[PATH_MAX], fs_abs_path[PATH_MAX]; + memset(directory_path_abs, 0, PATH_MAX); + memset(entry_path_abs, 0, PATH_MAX); + memset(fs_abs_path, 0, PATH_MAX); + strcpy(directory_path_abs, mountpoint); + strcat(directory_path_abs, directory); + + int dir_fd = open(directory_path_abs, O_RDONLY); + if (dir_fd == -1) { + printf("[" RED "FAILED" RESET "]\n"); + printf("Could not open source directory %s.\n", directory_path_abs); + return 0; + } + + // ------------------------------------------------------------------------ + char buffer[BUFSIZ], *ebuf; + off_t basep; + int nbytes; + while ((nbytes = getdirentries(dir_fd, buffer, BUFSIZ, &basep)) > 0) { + ebuf = buffer + nbytes; + char *cp = buffer; + while (cp < ebuf) { + struct dirent *entry = (struct dirent *)cp; + if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) { + cp += entry->d_reclen; + continue; + } + // Create the host machine absolute path. + strcpy(entry_path_abs, directory_path_abs); + strcat(entry_path_abs, "/"); + strcat(entry_path_abs, entry->d_name); + // Create the filesystem absolute path. + strcpy(fs_abs_path, directory); + strcat(fs_abs_path, "/"); + strcat(fs_abs_path, entry->d_name); + // Print the entry. + printf("[%3d] ENTRY: %-52s", initrd->nfiles, fs_abs_path); + // Stat the file. + struct stat _stat; + if (stat(entry_path_abs, &_stat) == -1) { + printf("[" RED "FAILED" RESET "]\n"); + printf("Error while executing stat on file : %s\n", entry_path_abs); + return 0; + } + printf("[%c%c%c%c%c%c%c%c%c%c]", + dt_char_array[entry->d_type], + (_stat.st_mode & S_IRUSR) != 0 ? 'r' : '-', + (_stat.st_mode & S_IWUSR) != 0 ? 'w' : '-', + (_stat.st_mode & S_IXUSR) != 0 ? 'x' : '-', + (_stat.st_mode & S_IRGRP) != 0 ? 'r' : '-', + (_stat.st_mode & S_IWGRP) != 0 ? 'w' : '-', + (_stat.st_mode & S_IXGRP) != 0 ? 'x' : '-', + (_stat.st_mode & S_IROTH) != 0 ? 'r' : '-', + (_stat.st_mode & S_IWOTH) != 0 ? 'w' : '-', + (_stat.st_mode & S_IXOTH) != 0 ? 'x' : '-'); + + if (entry->d_type != DT_DIR) { + // ---------------------------------------------------------------- + // Copy the name and the main info. + strcpy(initrd->headers[initrd->nfiles].name, fs_abs_path); + initrd->headers[initrd->nfiles].file_type = DT_REG; + initrd->headers[initrd->nfiles].length = _stat.st_size; + initrd->headers[initrd->nfiles].offset = fs_offset; + initrd->headers[initrd->nfiles].mask = _stat.st_mode; + initrd->headers[initrd->nfiles].uid = _stat.st_uid; + initrd->headers[initrd->nfiles].gid = _stat.st_gid; + initrd->headers[initrd->nfiles].atime = _stat.st_atim.tv_sec; + initrd->headers[initrd->nfiles].mtime = _stat.st_mtim.tv_sec; + initrd->headers[initrd->nfiles].ctime = _stat.st_ctim.tv_sec; + + // ---------------------------------------------------------------- + printf("[OFFSET:%6d]", initrd->headers[initrd->nfiles].offset); + printf("[SIZE:%6d]", initrd->headers[initrd->nfiles].length); + printf("[" GREEN "DONE" RESET "]\n"); + + fs_offset += initrd->headers[initrd->nfiles].length; + ++initrd->nfiles; + } else { + // ---------------------------------------------------------------- + strcpy(initrd->headers[initrd->nfiles].name, fs_abs_path); + initrd->headers[initrd->nfiles].file_type = DT_DIR; + initrd->headers[initrd->nfiles].length = _stat.st_size; + initrd->headers[initrd->nfiles].offset = fs_offset; + initrd->headers[initrd->nfiles].mask = _stat.st_mode; + initrd->headers[initrd->nfiles].uid = _stat.st_uid; + initrd->headers[initrd->nfiles].gid = _stat.st_gid; + initrd->headers[initrd->nfiles].atime = _stat.st_atim.tv_sec; + initrd->headers[initrd->nfiles].mtime = _stat.st_mtim.tv_sec; + initrd->headers[initrd->nfiles].ctime = _stat.st_ctim.tv_sec; + + // ---------------------------------------------------------------- + printf("[OFFSET:%6d]", initrd->headers[initrd->nfiles].offset); + printf("[SIZE:%6d]", initrd->headers[initrd->nfiles].length); + printf("[" GREEN "DONE" RESET "]\n"); + + // ---------------------------------------------------------------- + ++initrd->nfiles; + + // ---------------------------------------------------------------- + if (!create_file_headers(initrd, mountpoint, fs_abs_path)) { + break; + } + } + cp += entry->d_reclen; + } + } + close(dir_fd); + return 1; } -static bool init_headers() +static int write_file_system(initrd_t *initrd, int target_fd, char *mountpoint) { - printf("%-64s", "Initializing headers structures..."); - for (size_t i = 0; i < MAX_FILES; i++) { - headers[i].magic = 0xBF; - memset(headers[i].fileName, 0, MAX_FILENAME_LENGTH); - headers[i].file_type = 0; - headers[i].uid = 0; - headers[i].offset = 0; - headers[i].length = 0; - } - printf("[" GREEN "DONE" RESET "]\n\n"); - return true; -} + printf("Copying data to filesystem...\n"); + for (int i = 0; i < INITRD_MAX_FILES; i++) { + // -------------------------------------------------------------------- + char absolute_path[256]; + memset(absolute_path, 0, 256); + strcpy(absolute_path, mountpoint); + strcat(absolute_path, initrd->headers[i].name); -static bool init_mount_points(int argc, char *argv[]) -{ - printf("Initializing mount points...\n"); - for (int i = 1; i < argc; ++i) { - if (is_option_mount_point(argv[i]) && ((i + 1) < argc)) { - strcpy(mount_points[mountpoint_idx], argv[i + 1]); - printf("[%3d] MPNT: %-52s", mountpoint_idx, - mount_points[mountpoint_idx]); - printf("[" CYAN "DONE" RESET "]\n"); - ++mountpoint_idx; - } - } - printf("[" GREEN "DONE" RESET "]\n\n"); - return true; -} + // -------------------------------------------------------------------- + if (initrd->headers[i].file_type == DT_REG) { + FILE *fd2 = fopen(absolute_path, "r+"); + if (fd2 == NULL) { + continue; + } + printf("[%3d] FILE: %-92s", i, absolute_path); + char *buffer = (char *)malloc(initrd->headers[i].length); + fread(buffer, 1, initrd->headers[i].length, fd2); + // Write the content. + write(target_fd, buffer, initrd->headers[i].length); -static bool create_file_headers(char *mountpoint, char *directory) -{ - assert(mountpoint && "Received null mountpoint."); - assert(directory && "Received null directory."); - - // ------------------------------------------------------------------------ - char absolute_path[256]; - memset(absolute_path, 0, 256); - strcpy(absolute_path, mountpoint); - strcat(absolute_path, directory); - - // ------------------------------------------------------------------------ - DIR *source_dir = opendir(absolute_path); - if (source_dir == NULL) { - printf("[" RED "FAILED" RESET "]\n"); - printf("Could not open source directory %s.\n", absolute_path); - return false; - } - struct dirent *entry; - while ((entry = readdir(source_dir)) != NULL) { - if ((strcmp(entry->d_name, ".") == 0) || - (strcmp(entry->d_name, "..") == 0)) { - continue; - } - if (entry->d_type != DT_DIR) { - char relative_filename[256]; - strcpy(relative_filename, directory); - strcat(relative_filename, "/"); - strcat(relative_filename, entry->d_name); - - // ---------------------------------------------------------------- - FILE *fd = openfile(absolute_path, entry, "r+"); - printf("[%3d] FILE: %-52s", header_idx, relative_filename); - if (fd == NULL) { - printf("[" RED "FAILED" RESET "]\n"); - printf("Error while opening file : %s\n", relative_filename); - } - fseek(fd, 0, SEEK_END); - long length = ftell(fd); - fclose(fd); - - // ---------------------------------------------------------------- - strcpy(headers[header_idx].fileName, relative_filename); - headers[header_idx].file_type = FS_FILE; - headers[header_idx].length = length; - headers[header_idx].offset = header_offset; - - // ---------------------------------------------------------------- - printf("[" BLUE "OPEN" RESET "]"); - printf("[OFFSET:%6d]", headers[header_idx].offset); - printf("[SIZE:%6d]", headers[header_idx].length); - printf("[" GREEN "DONE" RESET "]\n"); - - header_offset += headers[header_idx].length; - ++header_idx; - } else { - // ---------------------------------------------------------------- - char sub_directory[256]; - memset(sub_directory, 0, 256); - strcpy(sub_directory, directory); - strcat(sub_directory, "/"); - strcat(sub_directory, entry->d_name); - - // ---------------------------------------------------------------- - strcpy(headers[header_idx].fileName, sub_directory); - headers[header_idx].file_type = FS_DIRECTORY; - headers[header_idx].length = 0; - headers[header_idx].offset = 0 /*++header_offset*/; - - // ---------------------------------------------------------------- - // Check if the directory is a mountpoint. - if (is_mount_point(headers[header_idx].fileName)) { - headers[header_idx].file_type = FS_MOUNTPOINT; - } - - // ---------------------------------------------------------------- - printf("[%3d] %3s : %-52s", header_idx, - (headers[header_idx].file_type == FS_DIRECTORY) ? "DIR" : - "MPT", - sub_directory); - printf("[" BLUE "OPEN" RESET "]"); - printf("[OFFSET:%6d]", headers[header_idx].offset); - printf("[SIZE:%6d]", headers[header_idx].length); - printf("[" GREEN "DONE" RESET "]\n"); - - // ---------------------------------------------------------------- - ++header_idx; - - // ---------------------------------------------------------------- - create_file_headers(mountpoint, sub_directory); - } - } - closedir(source_dir); - return true; -} - -static bool write_file_system(char *mountpoint) -{ - printf("Copying data to filesystem...\n"); - for (int i = 0; i < MAX_FILES; i++) { - // -------------------------------------------------------------------- - char absolute_path[256]; - memset(absolute_path, 0, 256); - strcpy(absolute_path, mountpoint); - strcat(absolute_path, headers[i].fileName); - - // -------------------------------------------------------------------- - if (headers[i].file_type == FS_FILE) { - FILE *fd2 = fopen(absolute_path, "r+"); - if (fd2 == NULL) { - continue; - } - printf("[%3d] FILE: %-92s", i, absolute_path); - char *buffer = (char *)malloc(headers[i].length); - fread(buffer, 1, headers[i].length, fd2); - fwrite(buffer, 1, headers[i].length, target_fs); - printf("[" GREEN "DONE" RESET "]\n"); - fclose(fd2); - free(buffer); - } - } - printf("[" GREEN "DONE" RESET "]\n\n"); - return true; + printf("[" GREEN "DONE" RESET "]\n"); + fclose(fd2); + free(buffer); + } + } + printf("[" GREEN "DONE" RESET "]\n\n"); + return 1; } int main(int argc, char *argv[]) { - printf("Welcome to MentOS initfs file copier tool\n\n"); - if (argc <= 1) { - if (argv[0][0] == '.') { - version(argv[0] + 2); - } - usage(argv[0]); - return EXIT_FAILURE; - } - if (!strcmp(argv[1], "--version") || !strcmp(argv[1], "-v")) { - version(argv[0] + 2); - return EXIT_SUCCESS; - } - if (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { - usage(argv[0]); - return EXIT_SUCCESS; - } + printf("Welcome to MentOS initrd file copier tool\n\n"); + if (argc <= 1) { + if (argv[0][0] == '.') { + version(argv[0] + 2); + } + usage(argv[0]); + return EXIT_FAILURE; + } + if (!strcmp(argv[1], "--version") || !strcmp(argv[1], "-v")) { + version(argv[0] + 2); + return EXIT_SUCCESS; + } + if (!strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { + usage(argv[0]); + return EXIT_SUCCESS; + } - // ------------------------------------------------------------------------ - // Open the fs. - if (!open_target_fs(argc, argv)) { - printf("Could not open target FileSystem.\n"); - return EXIT_FAILURE; - } + // Create the filesystem details structure. + initrd_t initrd; + // Target fs file descriptor. + int target_fd; - // ------------------------------------------------------------------------ - // Initialize the headers. - if (!init_headers()) { - printf("Could not initialize headers.\n"); - fclose(target_fs); - return EXIT_FAILURE; - } + // ------------------------------------------------------------------------ + // Open the fs. + if ((target_fd = open_target_fs(argc, argv)) == -1) { + printf("Could not open target FileSystem.\n"); + return EXIT_FAILURE; + } - // ------------------------------------------------------------------------ - // Initialize the mountpoints. - if (!init_mount_points(argc, argv)) { - printf("Could not initialize mount points.\n"); - fclose(target_fs); - return EXIT_FAILURE; - } + // ------------------------------------------------------------------------ + // Initialize the headers. + init_headers(&initrd); - // ------------------------------------------------------------------------ - // Create file headers. - header_offset = sizeof(struct initrd_file_t) * MAX_FILES + sizeof(int); - printf("Creating headers...\n"); - for (uint32_t i = 1; i < argc; ++i) { - if (is_option_source(argv[i]) && ((i + 1) < argc)) { - if (!create_file_headers(argv[i + 1], "")) { - printf("Could not create file headers.\n"); - fclose(target_fs); - return EXIT_FAILURE; - } - } - } - printf("[" GREEN "DONE" RESET "]\n\n"); + // ------------------------------------------------------------------------ + // Create file headers. + printf("Creating headers...\n"); + for (unsigned int i = 1; i < argc; ++i) { + if (is_option_source(argv[i]) && ((i + 1) < argc)) { + // ---------------------------------------------------------------- + strcpy(initrd.headers[initrd.nfiles].name, "/"); + initrd.headers[initrd.nfiles].file_type = DT_DIR; + initrd.headers[initrd.nfiles].length = 0; + initrd.headers[initrd.nfiles].offset = fs_offset; + initrd.headers[initrd.nfiles].mask = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; + initrd.headers[initrd.nfiles].uid = 0; + initrd.headers[initrd.nfiles].gid = 0; + initrd.headers[initrd.nfiles].atime = time(NULL); + initrd.headers[initrd.nfiles].mtime = time(NULL); + initrd.headers[initrd.nfiles].ctime = time(NULL); - // ------------------------------------------------------------------------ - // Copying information about headers on filesystem. - printf("%-64s", "Copying information about headers to filesystem..."); - fwrite(&header_idx, sizeof(int), 1, target_fs); - fwrite(headers, sizeof(struct initrd_file_t), 32, target_fs); - printf("[" GREEN "DONE" RESET "]\n\n"); + printf("[%3d] DIR : %-52s", initrd.nfiles, initrd.headers[initrd.nfiles].name); + printf("[" BLUE "OPEN" RESET "]"); + printf("[OFFSET:%6d]", initrd.headers[initrd.nfiles].offset); + printf("[SIZE:%6d]", initrd.headers[initrd.nfiles].length); + printf("[" GREEN "DONE" RESET "]\n"); + ++initrd.nfiles; - // ------------------------------------------------------------------------ - // Write headers on filesystem. - for (uint32_t i = 1; i < argc; ++i) { - if (is_option_source(argv[i]) && ((i + 1) < argc)) { - if (!write_file_system(argv[i + 1])) { - printf("Could not write on filesystem.\n"); - fclose(target_fs); - return EXIT_FAILURE; - } - } - } - fclose(target_fs); - return 0; + // ---------------------------------------------------------------- + if (!create_file_headers(&initrd, argv[i + 1], "")) { + printf("Could not create file headers.\n"); + close(target_fd); + return EXIT_FAILURE; + } + } + } + printf("[" GREEN "DONE" RESET "]\n\n"); + + // ------------------------------------------------------------------------ + // Copying information about headers on filesystem. + printf("%-64s", "Copying the filesystem's details structure..."); + write(target_fd, &initrd, sizeof(initrd_t)); + printf("[" GREEN "DONE" RESET "]\n\n"); + + // ------------------------------------------------------------------------ + // Write headers on filesystem. + for (unsigned int i = 1; i < argc; ++i) { + if (is_option_source(argv[i]) && ((i + 1) < argc)) { + if (!write_file_system(&initrd, target_fd, argv[i + 1])) { + printf("Could not write on filesystem.\n"); + close(target_fd); + return EXIT_FAILURE; + } + } + } + + // ------------------------------------------------------------------------ + lseek(target_fd, 0L, SEEK_SET); + size_t fs_size = lseek(target_fd, 0L, SEEK_END); + printf("FS size : %ld\n", fs_size); + if (fs_size < INITRD_MAX_FS_SIZE - 1) { + printf("Extend FS size to : %d\n", INITRD_MAX_FS_SIZE - 1); + lseek(target_fd, INITRD_MAX_FS_SIZE - 1, SEEK_SET); + write(target_fd, 0, 1); + } + close(target_fd); + return 0; } diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt new file mode 100644 index 0000000..22e668c --- /dev/null +++ b/libc/CMakeLists.txt @@ -0,0 +1,140 @@ +# ============================================================================= +# Author: Enrico Fraccaroli +# ============================================================================= +# Set the minimum required version of cmake. +cmake_minimum_required(VERSION 2.8) +# Initialize the project. +project(libc C ASM) + +# ============================================================================= +# Set the default build type to Debug. +if(NOT CMAKE_BUILD_TYPE) + message(STATUS "Setting build type to 'Debug' as none was specified.") + set(CMAKE_BUILD_TYPE + "Debug" + CACHE STRING "Choose the type of build." FORCE) +endif() + +# ============================================================================= +# Enable the assembly language. +enable_language(ASM) +# Find the NASM compiler. +find_program(ASM_COMPILER + NAMES nasm + HINTS /usr/bin/ ${CMAKE_SOURCE_DIR}/third_party/nasm/bin +) +# Check that we have found the compiler. +if(NOT ASM_COMPILER) + message(FATAL_ERROR "ASM compiler not found!") +endif(NOT ASM_COMPILER) +# Set the asm compiler. +set(CMAKE_ASM_COMPILER ${ASM_COMPILER}) +# Set the assembly compiler flags. +set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -f elf") +set(CMAKE_ASM_COMPILE_OBJECT " -o ") +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -g") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -O0") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -F dwarf") +elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -g") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -O3") +endif(CMAKE_BUILD_TYPE STREQUAL "Debug") + +# ============================================================================= +# Warning flags. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wpedantic") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic-errors") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-function") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-variable") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unknown-pragmas") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-but-set-variable") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") +# Set the C compiler options. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdlib") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-builtin") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0") +elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") +endif(CMAKE_BUILD_TYPE STREQUAL "Debug") + +# ============================================================================= +# Add the library. +add_library( + ${PROJECT_NAME} + + ${PROJECT_SOURCE_DIR}/src/stdio.c + ${PROJECT_SOURCE_DIR}/src/ctype.c + ${PROJECT_SOURCE_DIR}/src/string.c + ${PROJECT_SOURCE_DIR}/src/stdlib.c + ${PROJECT_SOURCE_DIR}/src/math.c + ${PROJECT_SOURCE_DIR}/src/fcvt.c + ${PROJECT_SOURCE_DIR}/src/time.c + ${PROJECT_SOURCE_DIR}/src/strerror.c + ${PROJECT_SOURCE_DIR}/src/termios.c + ${PROJECT_SOURCE_DIR}/src/libgen.c + ${PROJECT_SOURCE_DIR}/src/vsprintf.c + ${PROJECT_SOURCE_DIR}/src/vscanf.c + ${PROJECT_SOURCE_DIR}/src/pwd.c + ${PROJECT_SOURCE_DIR}/src/grp.c + ${PROJECT_SOURCE_DIR}/src/sched.c + ${PROJECT_SOURCE_DIR}/src/setenv.c + ${PROJECT_SOURCE_DIR}/src/assert.c + ${PROJECT_SOURCE_DIR}/src/abort.c + + ${PROJECT_SOURCE_DIR}/src/io/mm_io.c + ${PROJECT_SOURCE_DIR}/src/io/port_io.c + + ${PROJECT_SOURCE_DIR}/src/ipc/ipc.c + + ${PROJECT_SOURCE_DIR}/src/sys/unistd.c + ${PROJECT_SOURCE_DIR}/src/sys/errno.c + ${PROJECT_SOURCE_DIR}/src/sys/utsname.c + ${PROJECT_SOURCE_DIR}/src/sys/ioctl.c + + ${PROJECT_SOURCE_DIR}/src/unistd/getppid.c + ${PROJECT_SOURCE_DIR}/src/unistd/getpid.c + ${PROJECT_SOURCE_DIR}/src/unistd/exit.c + ${PROJECT_SOURCE_DIR}/src/unistd/setsid.c + ${PROJECT_SOURCE_DIR}/src/unistd/getsid.c + ${PROJECT_SOURCE_DIR}/src/unistd/setgid.c + ${PROJECT_SOURCE_DIR}/src/unistd/getgid.c + ${PROJECT_SOURCE_DIR}/src/unistd/fork.c + ${PROJECT_SOURCE_DIR}/src/unistd/read.c + ${PROJECT_SOURCE_DIR}/src/unistd/write.c + ${PROJECT_SOURCE_DIR}/src/unistd/exec.c + ${PROJECT_SOURCE_DIR}/src/unistd/nice.c + ${PROJECT_SOURCE_DIR}/src/unistd/open.c + ${PROJECT_SOURCE_DIR}/src/unistd/reboot.c + ${PROJECT_SOURCE_DIR}/src/unistd/waitpid.c + ${PROJECT_SOURCE_DIR}/src/unistd/chdir.c + ${PROJECT_SOURCE_DIR}/src/unistd/getcwd.c + ${PROJECT_SOURCE_DIR}/src/unistd/close.c + ${PROJECT_SOURCE_DIR}/src/unistd/stat.c + ${PROJECT_SOURCE_DIR}/src/unistd/rmdir.c + ${PROJECT_SOURCE_DIR}/src/unistd/mkdir.c + ${PROJECT_SOURCE_DIR}/src/unistd/unlink.c + ${PROJECT_SOURCE_DIR}/src/unistd/getdents.c + ${PROJECT_SOURCE_DIR}/src/unistd/lseek.c + ${PROJECT_SOURCE_DIR}/src/unistd/kill.c + ${PROJECT_SOURCE_DIR}/src/unistd/signal.c + ${PROJECT_SOURCE_DIR}/src/unistd/interval.c + ${PROJECT_SOURCE_DIR}/src/libc_start.c + ${PROJECT_SOURCE_DIR}/src/debug.c + + ${PROJECT_SOURCE_DIR}/src/crt0.S +) + +# Add the includes. +target_include_directories(${PROJECT_NAME} PUBLIC inc) + +# Remove the 'lib' prefix. +set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "") diff --git a/libc/inc/assert.h b/libc/inc/assert.h new file mode 100644 index 0000000..8b285a0 --- /dev/null +++ b/libc/inc/assert.h @@ -0,0 +1,20 @@ +/// MentOS, The Mentoring Operating system project +/// @file assert.h +/// @brief Defines the function and pre-processor macro for assertions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// @brief Function used to log the information of a failed assertion. +/// @param assertion The failed assertion. +/// @param file The file where the assertion is located. +/// @param function The function where the assertion is. +/// @param line The line inside the file. +void __assert_fail(const char *assertion, const char *file, const char *function, unsigned int line); + +/// @brief Extract the filename from the full path provided by __FILE__. +#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__) + +/// @brief Assert function. +#define assert(expression) ((expression) ? (void)0 : __assert_fail(#expression, __FILENAME__, __func__, __LINE__)) diff --git a/libc/inc/bits/ioctls.h b/libc/inc/bits/ioctls.h new file mode 100644 index 0000000..2226e89 --- /dev/null +++ b/libc/inc/bits/ioctls.h @@ -0,0 +1,9 @@ +/// @file ioctls.h +/// @brief Input/Output ConTroL (IOCTL) numbers. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#define TCGETS 0x5401U ///< Get the current serial port settings. +#define TCSETS 0x5402U ///< Set the current serial port settings. diff --git a/libc/inc/bits/stat.h b/libc/inc/bits/stat.h new file mode 100644 index 0000000..2ff288b --- /dev/null +++ b/libc/inc/bits/stat.h @@ -0,0 +1,36 @@ +/// MentOS, The Mentoring Operating system project +/// @file stat.h +/// @brief Defines the structure used by the functiosn fstat(), lstat(), and stat(). +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#if !defined(__SYS_STAT_H) && !defined(__KERNEL__) +#error "Never include directly; use instead." +#endif + +#include "stddef.h" +#include "time.h" + +/// @brief Data structure which contains information about a file. +typedef struct stat_t { + /// ID of device containing file. + dev_t st_dev; + /// File serial number. + ino_t st_ino; + /// Mode of file. + mode_t st_mode; + /// File user id. + uid_t st_uid; + /// File group id. + gid_t st_gid; + /// File Size. + off_t st_size; + /// Time of last access. + time_t st_atime; + /// Time of last data modification. + time_t st_mtime; + /// Time of last status change. + time_t st_ctime; +} stat_t; diff --git a/libc/inc/bits/termios-struct.h b/libc/inc/bits/termios-struct.h new file mode 100644 index 0000000..88e16a4 --- /dev/null +++ b/libc/inc/bits/termios-struct.h @@ -0,0 +1,45 @@ +/// MentOS, The Mentoring Operating system project +/// @file termios-struct.h +/// @brief Definition of the `termios` structure. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// Type for control characters. +typedef unsigned char cc_t; +/// Type for speed. +typedef unsigned int speed_t; +/// Type for flags. +typedef unsigned int tcflag_t; +/// The number of control characters. +#define NCCS 32 + +/// @brief Stores information about a terminal IOs. +typedef struct termios { + tcflag_t c_iflag; ///< input mode flags + tcflag_t c_oflag; ///< output mode flags + tcflag_t c_cflag; ///< control mode flags + tcflag_t c_lflag; ///< local mode flags + cc_t c_line; ///< line discipline + cc_t c_cc[NCCS]; ///< control characters + speed_t c_ispeed; ///< input speed + speed_t c_ospeed; ///< output speed +} termios; + +/// @brief These flags generally control higher-level aspects of input processing than the input +/// modes flags described in Input Modes, such as echoing, signals, and the choice of canonical +/// or noncanonical input. +enum { + ISIG = 000001U, ///< Controls whether the INTR, QUIT, and SUSP characters are recognized. + ICANON = 000002U, ///< Enables canonical input processing mode. + ECHO = 000010U, ///< Echo input characters. + ECHOE = 000020U, ///< If ICANON is set, the ERASE character erases the preceding character. + ECHOK = 000040U, ///< If ICANON is set, the KILL character erases the current line. + ECHONL = 000100U, ///< If ICANON is set, echo the NL character even if ECHO is not set. + NOFLSH = 000200U, ///< Do not clear in/out queues when receiving INTR, QUIT, and SUSP. + TOSTOP = 000400U, ///< Allows SIGTTOU signals generated by background processes. + ECHOCTL = 001000U, ///< If this and ECHO are set, control characters with ‘^’ are echoed. + ECHOKE = 004000U, ///< If ICANON is set, KILL is echoed by erasing each character on the line. + IEXTEN = 100000U, ///< Enables implementation-defined input processing. +}; \ No newline at end of file diff --git a/mentos/inc/libc/ctype.h b/libc/inc/ctype.h similarity index 53% rename from mentos/inc/libc/ctype.h rename to libc/inc/ctype.h index 2e5dccf..0471672 100644 --- a/mentos/inc/libc/ctype.h +++ b/libc/inc/ctype.h @@ -1,34 +1,52 @@ /// MentOS, The Mentoring Operating system project /// @file ctype.h /// @brief Functions related to character handling. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once /// @brief Check if the given value is a digit. +/// @param c The input character. +/// @return 1 on success, 0 otherwise. int isdigit(int c); /// @brief Check if the given value is a letter. +/// @param c The input character. +/// @return 1 on success, 0 otherwise. int isalpha(int c); /// @brief Check if the given value is either a letter or a digit. +/// @param c The input character. +/// @return 1 on success, 0 otherwise. int isalnum(int c); /// @brief Check if the given value is an hexadecimal digit. +/// @param c The input character. +/// @return 1 on success, 0 otherwise. int isxdigit(int c); /// @brief Check if the given value is a lower case letter. +/// @param c The input character. +/// @return 1 on success, 0 otherwise. int islower(int c); /// @brief Check if the given value is an upper case letter. +/// @param c The input character. +/// @return 1 on success, 0 otherwise. int isupper(int c); /// @brief Transforms the given value into a lower case letter. +/// @param c The input letter. +/// @return The input letter turned into a lower case letter. int tolower(int c); /// @brief Transforms the given value into an upper case letter. +/// @param c The input letter. +/// @return The input letter turned into an upper case letter. int toupper(int c); /// @brief Check if the given value is a whitespace. +/// @param c The input character. +/// @return 1 on success, 0 otherwise. int isspace(int c); diff --git a/libc/inc/debug.h b/libc/inc/debug.h new file mode 100644 index 0000000..7e515fe --- /dev/null +++ b/libc/inc/debug.h @@ -0,0 +1,44 @@ +/// MentOS, The Mentoring Operating system project +/// @file debug.h +/// @brief Debugging primitives. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#ifndef __DEBUG_HEADER__ +/// Header for identifying outputs coming from a mechanism. +#define __DEBUG_HEADER__ +#endif + +/// @brief Extract the filename from the full path provided by __FILE__. +#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__) + +/// @brief Prints the given character to debug output. +/// @param c The character to print. +void dbg_putchar(char c); + +/// @brief Prints the given string to debug output. +/// @param s The string to print. +void dbg_puts(const char *s); + +/// @brief Prints the given string to the debug output. +/// @param file The name of the file. +/// @param fun The name of the function. +/// @param line The line inside the file. +/// @param format The format to used, see printf. +/// @param ... The list of arguments. +void dbg_printf(const char *file, const char *fun, int line, const char *format, ...); + +/// @brief Transforms the given amount of bytes to a readable string. +/// @param bytes The bytes to turn to string. +/// @return String representing the bytes in human readable form. +const char *to_human_size(unsigned long bytes); + +/// @brief Transforms the given value to a binary string. +/// @param value The decimal value. +/// @return String representing the binary value. +const char *dec_to_binary(unsigned long value); + +/// Prints a debugging message. +#define pr_debug(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__ __VA_ARGS__) diff --git a/libc/inc/fcntl.h b/libc/inc/fcntl.h new file mode 100644 index 0000000..255ffd3 --- /dev/null +++ b/libc/inc/fcntl.h @@ -0,0 +1,46 @@ +/// MentOS, The Mentoring Operating system project +/// @file fcntl.h +/// @brief Headers of functions fcntl() and open(). +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#define O_RDONLY 00000000U ///< Open for reading only. +#define O_WRONLY 00000001U ///< Open for writing only. +#define O_RDWR 00000002U ///< Open for reading and writing. +#define O_CREAT 00000100U ///< Create if nonexistant. +#define O_EXCL 00000200U ///< Error if already exists. +#define O_TRUNC 00001000U ///< Truncate to zero length. +#define O_APPEND 00002000U ///< Set append mode. +#define O_NONBLOCK 00004000U ///< No delay. +#define O_DIRECTORY 00200000U ///< If file exists has no effect. Otherwise, the file is created. + +/// @defgroup ModeBitsAccessPermission Mode Bits for Access Permission +/// @brief The file modes. +/// @{ + +#define S_IRWXU 0000700U ///< RWX mask for user. +#define S_IRUSR 0000400U ///< R for user. +#define S_IWUSR 0000200U ///< W for user. +#define S_IXUSR 0000100U ///< X for user. + +#define S_IRWXG 0000070U ///< RWX mask for group. +#define S_IRGRP 0000040U ///< R for group. +#define S_IWGRP 0000020U ///< W for group. +#define S_IXGRP 0000010U ///< X for group. + +#define S_IRWXO 0000007U ///< RWX mask for group. +#define S_IROTH 0000004U ///< R for group. +#define S_IWOTH 0000002U ///< W for group. +#define S_IXOTH 0000001U ///< X for group. + +#define S_ISDIR(m) (((m)&0170000) == 0040000) ///< directory. +#define S_ISCHR(m) (((m)&0170000) == 0020000) ///< char special +#define S_ISBLK(m) (((m)&0170000) == 0060000) ///< block special +#define S_ISREG(m) (((m)&0170000) == 0100000) ///< regular file +#define S_ISFIFO(m) (((m)&0170000) == 0010000) ///< fifo +#define S_ISLNK(m) (((m)&0170000) == 0120000) ///< symbolic link +#define S_ISSOCK(m) (((m)&0170000) == 0140000) ///< socket + +/// @} diff --git a/libc/inc/fcvt.h b/libc/inc/fcvt.h new file mode 100644 index 0000000..017d3ef --- /dev/null +++ b/libc/inc/fcvt.h @@ -0,0 +1,37 @@ +/// MentOS, The Mentoring Operating system project +/// @file fcvt.h +/// @brief Declare the functions required to turn double values into a string. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// @brief Thif function transforms `value` into a string of digits inside `buf`, +/// representing the whole part followed by the decimal part. +/// @details +/// For instance, 512.765 will result in: +/// decpt = 3 +/// sign = 0 +/// buf = "512765" +/// @param arg The argument to turn into string. +/// @param chars The total number of digits. +/// @param decpt The position of the decimal point. +/// @param sign The sign of the number. +/// @param buf Buffer where the digits should be placed. +/// @param buf_size Dimension of the buffer. +void ecvtbuf(double arg, int chars, int *decpt, int *sign, char *buf, unsigned buf_size); + +/// @brief Thif function transforms `value` into a string of digits inside `buf`, +/// representing the whole part followed by the decimal part. +/// @details +/// For instance, 512.765 will result in: +/// decpt = 3 +/// sign = 0 +/// buf = "512765" +/// @param arg The argument to turn into string. +/// @param decimals The total number of digits to write after the decimal point. +/// @param decpt The position of the decimal point. +/// @param sign The sign of the number. +/// @param buf Buffer where the digits should be placed. +/// @param buf_size Dimension of the buffer. +void fcvtbuf(double arg, int decimals, int *decpt, int *sign, char *buf, unsigned buf_size); diff --git a/libc/inc/grp.h b/libc/inc/grp.h new file mode 100644 index 0000000..42d2eb5 --- /dev/null +++ b/libc/inc/grp.h @@ -0,0 +1,76 @@ +/// MentOS, The Mentoring Operating system project +/// @file grp.h +/// @brief Defines the structures and functions for managing groups. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stddef.h" + +/// Maximum number of users per group +#define MAX_MEMBERS_PER_GROUP 64 + +///@brief Contains user group informations +typedef struct group{ + + char* gr_name; // the name of the group + gid_t gr_gid; // numerical group ID + char* gr_passwd; // password + + //pointer to a null-terminated array of character pointers to member names + char* gr_mem[MAX_MEMBERS_PER_GROUP + 1]; + +} group; + +///@brief Provides access to the group database entry for `uid`. +///@param gid The gid to search inside the database. +///@return A pointer to the structure containing the database entry. +struct group* getgrgid(gid_t gid); + +///@brief Provides access to the group database entry for `name`. +///@param name The name to search inside the database. +///@return A pointer to the structure containing the database entry. +struct group* getgrnam(const char* name); + +///@brief Provides the same information as getgrgid but it stores the +/// results inside group, and the string information are store store +/// inside `buf`. +///@param gid The uid to search inside the database. +///@param group The structure containing pointers to the entry fields. +///@param buf The buffer where the strings should be stored. +///@param buflen The lenght of the buffer. +///@param result A pointer to the result or NULL is stored here. +///@return If the entry was found returns zero and set *result to group, +/// if the entry was not found returns zero and set *result to NULL, +/// on failure returns a number and sets and set *result to NULL. +int getgrgid_r(gid_t gid, struct group* group, char* buf, size_t buflen, struct group ** result); + +///@brief Provides the same information as getgrnam but it stores the +/// results inside group, and the string information are store store +/// inside `buf`. +///@param name The name to search inside the database. +///@param group The structure containing pointers to the entry fields. +///@param buf The buffer where the strings should be stored. +///@param buflen The lenght of the buffer. +///@param result A pointer to the result or NULL is stored here. +///@return If the entry was found returns zero and set *result to group, +/// if the entry was not found returns zero and set *result to NULL, +/// on failure returns a number and sets and set *result to NULL. +int getgrnam_r(const char* name, struct group* group, char* buf, size_t buflen, struct group** result); + + +///@brief Returns a pointer to a structure containing the broken-out +/// fields of an entry in the group database. +/// When first called returns a pointer to a group structure +/// containing the first entry in the group database. +/// Thereafter, it returns a pointer to a group structure +/// containing the next group structure in the group database, +/// so successive calls may be used to search the entire database. +struct group* getgrent(void); + +///@brief Rewinds the group database to allow repeated searches. +void endgrent(void); + +///@brief May be called to close the group database when processing is complete. +void setgrent(void); \ No newline at end of file diff --git a/libc/inc/io/mm_io.h b/libc/inc/io/mm_io.h new file mode 100644 index 0000000..c81dce1 --- /dev/null +++ b/libc/inc/io/mm_io.h @@ -0,0 +1,27 @@ +/// MentOS, The Mentoring Operating system project +/// @file mm_io.h +/// @brief Memory Mapped IO functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stdint.h" + +/// @brief Reads a 8-bit value from the given address. +uint8_t in_memb(uint32_t addr); + +/// @brief Reads a 16-bit value from the given address. +uint16_t in_mems(uint32_t addr); + +/// @brief Reads a 32-bit value from the given address. +uint32_t in_meml(uint32_t addr); + +/// @brief Writes a 8-bit value at the given address. +void out_memb(uint32_t addr, uint8_t value); + +/// @brief Writes a 16-bit value at the given address. +void out_mems(uint32_t addr, uint16_t value); + +/// @brief Writes a 32-bit value at the given address. +void out_meml(uint32_t addr, uint32_t value); diff --git a/libc/inc/io/port_io.h b/libc/inc/io/port_io.h new file mode 100644 index 0000000..fe20b5a --- /dev/null +++ b/libc/inc/io/port_io.h @@ -0,0 +1,43 @@ +/// MentOS, The Mentoring Operating system project +/// @file port_io.h +/// @brief Byte I/O on ports prototypes. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stdint.h" + +/// @brief Used for reading from the I/O ports. +/// @param port The input port. +/// @return The read value. +uint8_t inportb(uint16_t port); + +/// @brief Used for reading 2 bytes from the I/O ports. +/// @param port The input port. +/// @return The read value. +uint16_t inports(uint16_t port); + +void inportsm(uint16_t port, uint8_t *data, unsigned long size); + +/// @brief Used for reading 4 bytes from the I/O ports. +/// @param port The input port. +/// @return The read value. +uint32_t inportl(uint16_t port); + +/// @brief Use this to write to I/O ports to send bytes to devices. +/// @param port The output port. +/// @param data The data to write. +void outportb(uint16_t port, uint8_t data); + +/// @brief Use this to write to I/O ports to send 2 bytes to devices. +/// @param port The output port. +/// @param data The data to write. +void outports(uint16_t port, uint16_t data); + +void outportsm(uint16_t port, uint8_t *data, uint16_t size); + +/// @brief Use this to write to I/O ports to send 4 bytes to devices. +/// @param port The output port. +/// @param data The data to write. +void outportl(uint16_t port, uint32_t data); diff --git a/libc/inc/ipc/ipc.h b/libc/inc/ipc/ipc.h new file mode 100644 index 0000000..f0867c5 --- /dev/null +++ b/libc/inc/ipc/ipc.h @@ -0,0 +1,25 @@ +/// MentOS, The Mentoring Operating system project +/// @file ipc.h +/// @brief Inter-Process Communication (IPC) structures. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// @brief Permission details of an IPC object. +struct ipc_perm { + /// Key supplied to msgget(2). + key_t __key; + /// Effective UID of owner. + uid_t uid; + /// Effective GID of owner. + gid_t gid; + /// Effective UID of creator. + uid_t cuid; + /// Effective GID of creator. + gid_t cgid; + /// Permissions. + unsigned short mode; + /// Sequence number. + unsigned short __seq; +}; \ No newline at end of file diff --git a/libc/inc/ipc/msg.h b/libc/inc/ipc/msg.h new file mode 100644 index 0000000..b832c0b --- /dev/null +++ b/libc/inc/ipc/msg.h @@ -0,0 +1,70 @@ +/// MentOS, The Mentoring Operating system project +/// @file msg.h +/// @brief Definition of structure for managing message queues. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "sys/types.h" +#include "stddef.h" +#include "time.h" +#include "ipc.h" + +/// Type for storing the number of messages in a message queue. +typedef unsigned int msgqnum_t; + +/// Type for storing the number of bytes in a message queue. +typedef unsigned int msglen_t; + +/// @brief Buffer to use with the message queue IPC. +struct msgbuf { + /// Type of the message. + long mtype; + /// Text of the message. + char mtext[1]; +}; + +/// @brief Message queue data structure. +struct msqid_ds { + /// Ownership and permissions. + struct ipc_perm msg_perm; + /// Time of last msgsnd(2). + time_t msg_stime; + /// Time of last msgrcv(2). + time_t msg_rtime; + /// Time of creation or last modification by msgctl(). + time_t msg_ctime; + /// Number of bytes in queue. + unsigned long msg_cbytes; + /// Number of messages in queue. + msgqnum_t msg_qnum; + /// Maximum number of bytes in queue. + msglen_t msg_qbytes; + /// PID of last msgsnd(2). + pid_t msg_lspid; + /// PID of last msgrcv(2). + pid_t msg_lrpid; +}; + +#ifdef __KERNEL__ + +long sys_msgget(key_t key, int msgflg); + +long sys_msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg); + +long sys_msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg); + +long sys_msgctl(int msqid, int cmd, struct msqid_ds *buf); + +#else + +long msgget(key_t key, int msgflg); + +long msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg); + +long msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg); + +long msgctl(int msqid, int cmd, struct msqid_ds *buf); + +#endif \ No newline at end of file diff --git a/libc/inc/ipc/sem.h b/libc/inc/ipc/sem.h new file mode 100644 index 0000000..e683e58 --- /dev/null +++ b/libc/inc/ipc/sem.h @@ -0,0 +1,40 @@ +/// MentOS, The Mentoring Operating system project +/// @file sem.h +/// @brief Definition of structure for managing semaphores. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "sys/types.h" +#include "stddef.h" +#include "time.h" +#include "ipc.h" + +/// @brief Buffer to use with the semaphore IPC. +struct sembuf { + /// Semaphore index in array. + unsigned short sem_num; + /// Semaphore operation. + short sem_op; + /// Operation flags. + short sem_flg; +}; + +#ifdef __KERNEL__ + +long sys_semget(key_t key, int nsems, int semflg); + +long sys_semop(int semid, struct sembuf *sops, unsigned nsops); + +long sys_semctl(int semid, int semnum, int cmd, unsigned long arg); + +#else + +long semget(key_t key, int nsems, int semflg); + +long semop(int semid, struct sembuf *sops, unsigned nsops); + +long semctl(int semid, int semnum, int cmd, unsigned long arg); + +#endif \ No newline at end of file diff --git a/mentos/inc/sys/shm.h b/libc/inc/ipc/shm.h similarity index 60% rename from mentos/inc/sys/shm.h rename to libc/inc/ipc/shm.h index 8a80360..dec48bf 100644 --- a/mentos/inc/sys/shm.h +++ b/libc/inc/ipc/shm.h @@ -1,14 +1,48 @@ /// MentOS, The Mentoring Operating system project /// @file shm.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @brief Definition of structure for managing shared memories. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once +#include "sys/types.h" +#include "stddef.h" +#include "time.h" +#include "ipc.h" + +/// Data type for storing the number of attaches. +typedef unsigned long shmatt_t; + +/// @brief Shared memory data structure. +struct shmid_ds { + /// Operation permission struct. + struct ipc_perm shm_perm; + /// Size of segment in bytes. + size_t shm_segsz; + /// Time of last shmat(). + time_t shm_atime; + /// Time of last shmdt(). + time_t shm_dtime; + /// Time of last change by shmctl(). + time_t shm_ctime; + /// Pid of creator. + pid_t shm_cpid; + /// Pid of last shmop. + pid_t shm_lpid; + /// Number of current attaches. + shmatt_t shm_nattch; + /// Pointer to the next shared memory data structure. + struct shmid_ds *next; + /// Where shm created is memorized, should be a file. + void *shm_location; +}; + +#if 0 + #include "ipc.h" #include "debug.h" -#include "clock.h" +#include "time.h" #include "kheap.h" #include "stddef.h" #include "paging.h" @@ -17,10 +51,10 @@ //======== Permission flag for shmget ========================================== // or S_IRUGO from . -#define SHM_R 0400 +#define SHM_R 0400 // or S_IWUGO from . -#define SHM_W 0200 +#define SHM_W 0200 //============================================================================== //======== Flags for shmat ===================================================== @@ -28,76 +62,43 @@ #define SHM_RDONLY 010000 // Round attach address to SHMLBA. -#define SHM_RND 020000 +#define SHM_RND 020000 // Take-over region on attach. -#define SHM_REMAP 040000 +#define SHM_REMAP 040000 // Execution access. -#define SHM_EXEC 0100000 +#define SHM_EXEC 0100000 //============================================================================== //======== Commands for shmctl ================================================= // Lock segment (root only). -#define SHM_LOCK 11 +#define SHM_LOCK 11 // Unlock segment (root only). #define SHM_UNLOCK 12 //============================================================================== // Ipcs ctl commands. -#define SHM_STAT 13 +#define SHM_STAT 13 #define SHM_INFO 14 -#define SHM_STAT_ANY 15 +#define SHM_STAT_ANY 15 //======== shm_mode upper byte flags =========================================== // segment will be destroyed on last detach. -#define SHM_DEST 01000 +#define SHM_DEST 01000 // Segment will not be swapped. -#define SHM_LOCKED 02000 +#define SHM_LOCKED 02000 // Segment is mapped via hugetlb. -#define SHM_HUGETLB 04000 +#define SHM_HUGETLB 04000 // Don't check for reservations. #define SHM_NORESERVE 010000 -typedef unsigned long shmatt_t; - -struct shmid_ds { - // Operation permission struct. - struct ipc_perm shm_perm; - - // Size of segment in bytes. - size_t shm_segsz; - - // Time of last shmat(). - time_t shm_atime; - - // Time of last shmdt(). - time_t shm_dtime; - - // Time of last change by shmctl(). - time_t shm_ctime; - - // Pid of creator. - pid_t shm_cpid; - - // Pid of last shmop. - pid_t shm_lpid; - - // Number of current attaches. - shmatt_t shm_nattch; - - struct shmid_ds *next; - - // Where shm created is memorized, should be a file. - void *shm_location; -}; - /// @@brief Syscall Service Routine: Shared memory control operation. int syscall_shmctl(int *args); @@ -130,3 +131,27 @@ struct shmid_ds *find_shm_fromkey(key_t key); /// @@brief shmid_ds on list. struct shmid_ds *find_shm_fromvaddr(void *shmvaddr); + +#endif + +#ifdef __KERNEL__ + +long sys_shmat(int shmid, char *shmaddr, int shmflg); + +long sys_shmget(key_t key, size_t size, int flag); + +long sys_shmdt(char *shmaddr); + +long sys_shmctl(int shmid, int cmd, struct shmid_ds *buf); + +#else + +long shmat(int shmid, char *shmaddr, int shmflg); + +long shmget(key_t key, size_t size, int flag); + +long shmdt(char *shmaddr); + +long shmctl(int shmid, int cmd, struct shmid_ds *buf); + +#endif \ No newline at end of file diff --git a/libc/inc/libgen.h b/libc/inc/libgen.h new file mode 100644 index 0000000..508bfb2 --- /dev/null +++ b/libc/inc/libgen.h @@ -0,0 +1,41 @@ +/// MentOS, The Mentoring Operating system project +/// @file libgen.h +/// @brief String routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "stddef.h" + +// TODO: doxygen comment. +/// @brief +/// @param out +/// @param cur +/// @param sep +/// @param max +/// @result +int parse_path(char *out, char **cur, char sep, size_t max); + +// TODO: doxygen comment. +/// @brief +/// @param path +/// @result +char *dirname(const char *path); + +// TODO: doxygen comment. +/// @brief +/// @param path +/// @result +char *basename(const char *path); + +/// @brief Return the canonicalized absolute pathname. +/// @param path The path we are canonicalizing. +/// @param resolved The canonicalized path. +/// @return +/// If there is no error, realpath() returns a pointer to the resolved. +/// Otherwise, it returns NULL, the contents of the array resolved +/// are undefined, and errno is set to indicate the error. +/// @details +/// If resolved is NULL, then realpath() uses malloc +/// to allocate a buffer of up to PATH_MAX bytes to hold the +/// resolved pathname, and returns a pointer to this buffer. +char *realpath(const char *path, char *resolved); \ No newline at end of file diff --git a/libc/inc/limits.h b/libc/inc/limits.h new file mode 100644 index 0000000..b2222f6 --- /dev/null +++ b/libc/inc/limits.h @@ -0,0 +1,58 @@ +/// MentOS, The Mentoring Operating system project +/// @file limits.h +/// @brief OS numeric limits. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// Number of bits in a `char'. +#define CHAR_BIT 8 + +/// Minimum value a `signed char' can hold. +#define SCHAR_MIN -128 + +/// Maximum value a `signed char' can hold. +#define SCHAR_MAX +127 + +/// Minimum value a `signed char' can hold. +#define CHAR_MIN SCHAR_MIN + +/// Maximum value a `signed char' can hold. +#define CHAR_MAX SCHAR_MAX + +/// Maximum value a `char' can hold. +#define UCHAR_MAX 255 + +/// Minimum value a `signed short int' can hold. +#define SHRT_MIN (-32768) + +/// Maximum value a `signed short int' can hold. +#define SHRT_MAX (+32767) + +/// Maximum value a `unsigned short int' can hold. +#define USHRT_MAX 65535 + +/// Minimum value a `signed int' can hold. +#define INT_MIN (-2147483648) + +/// Maximum values a `signed int' can hold. +#define INT_MAX (+2147483647) + +/// Maximum value an `unsigned int' can hold. +#define UINT_MAX (+2147483647) + +/// Maximum value a `signed long int' can hold. +#define LONG_MIN (-2147483648L) + +/// Minimum value a `signed long int' can hold. +#define LONG_MAX (+2147483647L) + +/// Maximum number of characters in a file name. +#define NAME_MAX 255 + +/// Maximum number of characters in a path name. +#define PATH_MAX 4096 + +/// Maximum pid number. +#define PID_MAX_LIMIT 32768 diff --git a/libc/inc/math.h b/libc/inc/math.h new file mode 100644 index 0000000..fc99027 --- /dev/null +++ b/libc/inc/math.h @@ -0,0 +1,158 @@ +/// MentOS, The Mentoring Operating system project +/// @file math.h +/// @brief Mathematical constants and functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// @brief The absolute value. +#define abs(a) (((a) < 0) ? -(a) : (a)) + +/// @brief The max of the two values. +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +/// @brief The min of the two values. +#define min(a, b) (((a) < (b)) ? (a) : (b)) + +/// @brief The sign the the passed value. +#define sign(x) ((x < 0) ? -1 : ((x > 0) ? 1 : 0)) + +/// @brief e +#define M_E 2.7182818284590452354 + +/// @brief log_2 e +#define M_LOG2E 1.4426950408889634074 + +/// @brief log_10 e +#define M_LOG10E 0.43429448190325182765 + +/// @brief log_e 2 +#define M_LN2 0.69314718055994530942 + +/// @brief log_e 10 +#define M_LN10 2.30258509299404568402 + +/// @brief pi +#define M_PI 3.14159265358979323846 + +/// @brief pi / 2 +#define M_PI_2 1.57079632679489661923 + +/// @brief pi / 4 +#define M_PI_4 0.78539816339744830962 + +/// @brief 1 / pi +#define M_1_PI 0.31830988618379067154 + +/// @brief 2 / pi +#define M_2_PI 0.63661977236758134308 + +/// @brief 2 / sqrt(pi) +#define M_2_SQRTPI 1.12837916709551257390 + +/// @brief sqrt(2) +#define M_SQRT2 1.41421356237309504880 + +/// @brief 1 / sqrt(2) +#define M_SQRT1_2 0.70710678118654752440 + +/// @brief Returns the integral value that is nearest to x, with +/// halfway cases rounded away from zero. +/// @param x Value to round. +/// @result The value of x rounded to the nearest integral +/// (as a floating-point value). +double round(double x); + +/// @brief Rounds x upward, returning the smallest integral value +/// that is not less than x. +/// @param x Value to round up. +/// @return The smallest integral value that is not less than x +/// (as a floating-point value). +double ceil(double x); + +/// @brief Rounds x downward, returning the largest integral value +/// that is not greater than x. +/// @param x Value to round down. +/// @return The value of x rounded downward (as a floating-point value). +double floor(double x); + +/// @brief Returns base raised to the power exponent: +/// @param base Base value. +/// @param exponent Exponent value. +/// @result The result of raising base to the power exponent. +/// @details +/// If the base is finite negative and the exponent is finite but not an +/// integer value, it causes a domain error. +/// If both base and exponent are zero, it may also cause a domain error +/// on certain implementations. +/// If base is zero and exponent is negative, it may cause a domain error +/// or a pole error (or none, depending on the library implementation). +/// The function may also cause a range error if the result is too great +/// or too small to be represented by a value of the return type. +double pow(double base, double exponent); + +/// @brief Returns the base-e exponential function of x, which is e raised +/// to the power x: e^x. +/// @param x Value of the exponent. +/// @return Exponential value of x. +/// @details +/// If the magnitude of the result is too large to be represented by a value +/// of the return type, the function returns HUGE_VAL (or HUGE_VALF or +/// HUGE_VALL) with the proper sign, and an overflow range error occurs and +/// the global variable errno is set to ERANGE. +double exp(double x); + +/// @brief Returns the absolute value of x: |x|. +/// @param x Value whose absolute value is returned. +/// @result The absolute value of x. +double fabs(double x); + +/// @brief Returns the absolute value of x: |x|. +/// @param x Value whose absolute value is returned. +/// @result The absolute value of x. +float fabsf(float x); + +/// @brief Returns the square root of x. +/// @param x Value whose square root is computed. +/// @return Square root of x. If x is negative, the global variable errno +/// is set to EDOM. +double sqrt(double x); + +/// @brief Returns the square root of x. +/// @param x Value whose square root is computed. +/// @return Square root of x. If x is negative, the global variable errno +/// is set to EDOM. +float sqrtf(float x); + +/// @brief Checks if the input value is Infinite (INF). +/// @param x The value to check. +/// @return 1 if INF, 0 otherwise. +int isinf(double x); + +/// @brief Checks if the input value is Not A Number (NAN). +/// @param x The value to check. +/// @return 1 if NAN, 0 otherwise. +int isnan(double x); + +/// @brief Logarithm function in base 10. +/// @param x Topic of the logarithm function. +/// @return Return the result. +double log10(double x); + +/// @brief Natural logarithm function. +/// @param x Topic of the logarithm function. +/// @return Return the result. +double ln(double x); + +/// @brief Logarithm function in base x. +/// @param x Base of the logarithm. +/// @param y Topic of the logarithm function. +/// @return Return the result. +double logx(double x, double y); + +/// @brief Breaks x into an integral and a fractional part, both parts have the same sign as x. +/// @param x The value we want to break. +/// @param intpart Where we store the integer part. +/// @return the fractional part. +double modf(double x, double *intpart); diff --git a/libc/inc/pwd.h b/libc/inc/pwd.h new file mode 100644 index 0000000..29e9098 --- /dev/null +++ b/libc/inc/pwd.h @@ -0,0 +1,56 @@ +/// MentOS, The Mentoring Operating system project +/// @file pwd.h +/// @brief Contains the structure and functions for managing passwords. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stddef.h" + +/// @brief Stores user account information. +typedef struct passwd_t { + char *pw_name; ///< User's login name. + char *pw_passwd; ///< Encrypted password (not currently). + uid_t pw_uid; ///< User ID. + gid_t pw_gid; ///< Group ID. + char *pw_gecos; ///< User's full name. + char *pw_dir; ///< User's login directory. + char *pw_shell; ///< User's login shell. +} passwd_t; + +/// @brief Provides access to the password database entry for `name`. +/// @param name The name to search inside the database. +/// @return A pointer to the structure containing the database entry. +passwd_t *getpwnam(const char *name); + +/// @brief Provides access to the password database entry for `uid`. +/// @param uid The uid to search inside the database. +/// @return A pointer to the structure containing the database entry. +passwd_t *getpwuid(uid_t uid); + +/// @brief Provides the same information as getpwnam but it stores the +/// results inside pwd, and the string information are store store +/// inside `buf`. +/// @param name The name to search inside the database. +/// @param pwd The structure containing pointers to the entry fields. +/// @param buf The buffer where the strings should be stored. +/// @param buflen The lenght of the buffer. +/// @param result A pointer to the result or NULL is stored here. +/// @return If the entry was found returns zero and set *result to pwd, +/// if the entry was not found returns zero and set *result to NULL, +/// on failure returns a number and sets and set *result to NULL. +int getpwnam_r(const char *name, passwd_t *pwd, char *buf, size_t buflen, passwd_t **result); + +/// @brief Provides the same information as getpwuid but it stores the +/// results inside pwd, and the string information are store store +/// inside `buf`. +/// @param uid The uid to search inside the database. +/// @param pwd The structure containing pointers to the entry fields. +/// @param buf The buffer where the strings should be stored. +/// @param buflen The lenght of the buffer. +/// @param result A pointer to the result or NULL is stored here. +/// @return If the entry was found returns zero and set *result to pwd, +/// if the entry was not found returns zero and set *result to NULL, +/// on failure returns a number and sets and set *result to NULL. +int getpwuid_r(uid_t uid, passwd_t *pwd, char *buf, size_t buflen, passwd_t **result); diff --git a/libc/inc/sched.h b/libc/inc/sched.h new file mode 100644 index 0000000..d82546e --- /dev/null +++ b/libc/inc/sched.h @@ -0,0 +1,29 @@ +/// MentOS, The Mentoring Operating system project +/// @file sched.h +/// @brief Structures and functions for managing the scheduler. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/types.h" +#include "time.h" +#include "stdbool.h" + +/// @brief Structure that describes scheduling parameters. +typedef struct sched_param_t { + /// Static execution priority. + int sched_priority; + /// Expected period of the task + time_t period; + /// Absolute deadline + time_t deadline; + /// Absolute time of arrival of the task + time_t arrivaltime; + /// Is task periodic? + bool_t is_periodic; +} sched_param_t; + +int sched_setparam(pid_t pid, const sched_param_t *param); + +int sched_getparam(pid_t pid, sched_param_t *param); + +int waitperiod(); diff --git a/libc/inc/signal.h b/libc/inc/signal.h new file mode 100644 index 0000000..eebb828 --- /dev/null +++ b/libc/inc/signal.h @@ -0,0 +1,273 @@ +/// MentOS, The Mentoring Operating system project +/// @file signal.h +/// @brief Signals definition. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "sys/types.h" + +/// @brief List of signals. +typedef enum { + SIGHUP = 1, ///< Hang up detected on controlling terminal or death of controlling process. + SIGINT = 2, ///< Issued if the user sends an interrupt signal (Ctrl + C). + SIGQUIT = 3, ///< Issued if the user sends a quit signal (Ctrl + D). + SIGILL = 4, ///< Illegal Instruction. + SIGTRAP = 5, ///< Trace/breakpoint trap. + SIGABRT = 6, ///< Abort signal from abort(). + SIGEMT = 7, ///< Emulator trap. + SIGFPE = 8, ///< Floating-point arithmetic exception. + SIGKILL = 9, ///< If a process gets this signal it must quit immediately and will not perform any clean-up operations. + SIGBUS = 10, ///< Bus error (bad memory access). + SIGSEGV = 11, ///< Invalid memory reference. + SIGSYS = 12, ///< Bad system call (SVr4). + SIGPIPE = 13, ///< Broken pipe: write to pipe with no readers. + SIGALRM = 14, ///< Alarm clock signal (used for timers). + SIGTERM = 15, ///< Software termination signal (sent by kill by default). + SIGUSR1 = 16, ///< User-defined signal 1. + SIGUSR2 = 17, ///< User-defined signal 2. + SIGCHLD = 18, ///< Child stopped or terminated. + SIGPWR = 19, ///< Power failure. + SIGWINCH = 20, ///< Window resize signal. + SIGURG = 21, ///< Urgent condition on socket. + SIGPOLL = 22, ///< Pollable event. + SIGSTOP = 23, ///< Stop process. + SIGTSTP = 24, ///< Stop typed at terminal. + SIGCONT = 25, ///< Continue if stopped. + SIGTTIN = 26, ///< Terminal input for background process. + SIGTTOU = 27, ///< Terminal output for background process. + SIGVTALRM = 28, ///< Virtual alarm clock. + SIGPROF = 29, ///< Profiling timer expired. + SIGXCPU = 30, ///< CPU time limit exceeded. + SIGXFSZ = 31, ///< File size limit exceeded. + NSIG +} signal_type_t; + +/// @brief Codes that indentify the sender of a signal. +typedef enum { + SI_NOINFO, ///< Unable to determine complete signal information. + + // Signal : - + // Enabled fields : si_pid, si_uid + SI_USER, ///< Signal sent by kill(), pthread_kill(), raise(), abort() or alarm(). + + // Signal : - + // Enabled fields : - + SI_KERNEL, ///< Generic kernel function + + // Signal : - + // Enabled fields : si_pid, si_uid, si_value + SI_QUEUE, ///< Signal was sent by sigqueue(). + SI_TIMER, ///< Signal was generated by expiration of a timer set by timer_settimer(). + SI_ASYNCIO, ///< Signal was generated by completion of an asynchronous I/O request. + SI_MESGQ, ///< Signal was generated by arrival of a message on an empty message queue. + + // Signal : SIGILL + // Enabled fields : si_addr (address of failing instruction) + ILL_ILLOPC, ///< Illegal opcode. + ILL_ILLOPN, ///< Illegal operand. + ILL_ILLADR, ///< Illegal addressing mode. + ILL_ILLTRP, ///< Illegal trap. + ILL_PRVOPC, ///< Privileged opcode. + ILL_PRVREG, ///< Privileged register. + ILL_COPROC, ///< Coprocessor error. + ILL_BADSTK, ///< Internal stack error. + + // Signal : SIGFPE + // Enabled fields : si_addr (address of failing instruction) + FPE_INTDIV, ///< Integer divide-by-zero. + FPE_INTOVF, ///< Integer overflow. + FPE_FLTDIV, ///< Floating point divide-by-zero. + FPE_FLTOVF, ///< Floating point overflow. + FPE_FLTUND, ///< Floating point underflow. + FPE_FLTRES, ///< Floating point inexact result. + FPE_FLTINV, ///< Invalid floating point operation. + FPE_FLTSUB, ///< Subscript out of range. + + // Signal : SIGSEGV + // Enabled fields : si_addr (address of faulting memory reference) + SEGV_MAPERR, ///< Address not mapped. + SEGV_ACCERR, ///< Invalid permissions. + + // Signal : SIGBUS + // Enabled fields : si_addr (address of faulting memory reference) + BUS_ADRALN, ///< Invalid address alignment. + BUS_ADRERR, ///< Non-existent physical address. + BUS_OBJERR, ///< Object-specific hardware error. + + // Signal : SIGTRAP + // Enabled fields : - + TRAP_BRKPT, ///< Process breakpoint. + TRAP_TRACE, ///< Process trace trap. + + // Signal : SIGCHLD + // Enabled fields : si_pid (child process ID) + // si_uid (real user ID of process that sent the signal) + // si_status (exit value or signal) + CLD_EXITED, ///< Child has exited. + CLD_KILLED, ///< Child has terminated abnormally and did not create a core file. + CLD_DUMPED, ///< Child has terminated abnormally and created a core file. + CLD_TRAPPED, ///< Traced child has trapped. + CLD_STOPPED, ///< Child has stopped. + CLD_CONTINUED, ///< Stopped child has continued. + + // Signal : SIGIO/SIGPOLL + // Enabled fields : si_band + POLL_IN, ///< Data input available. + POLL_OUT, ///< Output buffers available. + POLL_MSG, ///< Input message available. + POLL_ERR, ///< I/O error. + POLL_PRI, ///< High priority input available. + POLL_HUP, ///< Device disconnected. +} signal_sender_code_t; + +/// @brief Defines what to do with the provided signal mask. +typedef enum { + /// @brief The set of blocked signals is the union of the current set + /// and the set argument. + SIG_BLOCK, + /// @brief The signals in set are removed from the current set of + /// blocked signals. It is permissible to attempt to unblock + /// a signal which is not blocked. + SIG_UNBLOCK, + /// @brief The set of blocked signals is set to the argument set. + SIG_SETMASK +} sigmask_how_t; + +/// @defgroup SigactionFlags Flags associated with a sigaction. +/// @{ + +#define SA_NOCLDSTOP 0x00000001U ///< Turn off SIGCHLD when children stop. +#define SA_NOCLDWAIT 0x00000002U ///< Flag on SIGCHLD to inhibit zombies. +#define SA_SIGINFO 0x00000004U ///< sa_sigaction specifies the signal-handling function for signum. +#define SA_ONSTACK 0x08000000U ///< Indicates that a registered stack_t will be used. +#define SA_RESTART 0x10000000U ///< Flag to get restarting signals (which were the default long ago) +#define SA_NODEFER 0x40000000U ///< Prevents the current signal from being masked in the handler. +#define SA_RESETHAND 0x80000000U ///< Clears the handler when the signal is delivered. + +/// @} + +/// Type of a signal handler. +typedef void (*sighandler_t)(int); + +#define SIG_DFL ((sighandler_t)0) ///< Default signal handling. +#define SIG_IGN ((sighandler_t)1) ///< Ignore signal. +#define SIG_ERR ((sighandler_t)-1) ///< Error return from signal. + +/// @brief Structure used to mask and unmask signals. +/// @details +/// Each unsigned long consists of 32 bits, thus, the maximum number of signals +/// that may be declared is 64. +/// Signals are divided into two cathegories, identified by the two unsigned longs: +/// [ 1, 31] corresponds to normal signals; +/// [32, 64] corresponds to real-time signals. +typedef struct sigset_t { + /// Signals divided into two cathegories. + unsigned long sig[2]; +} sigset_t; + +/// @brief Holds the information on how to handle a specific signal. +typedef struct sigaction_t { + /// This field specifies the type of action to be performed; its value can be a pointer + /// to the signal handler, SIG_DFL (that is, the value 0) to specify that the default + /// action is performed, or SIG_IGN (that is, the value 1) to specify that the signal is + /// ignored. + sighandler_t sa_handler; + /// This sigset_t variable specifies the signals to be masked when running the signal handler + sigset_t sa_mask; + /// This set of flags specifies how the signal must be handled; + unsigned int sa_flags; +} sigaction_t; + +/// @brief Data passed with signal info. +typedef union sigval { + int sival_int; ///< Integer value. + void *sival_ptr; ///< Pointer value. +} sigval_t; + +/// @brief Stores information about an occurrence of a specific signal. +typedef struct siginfo_t { + /// The signal number. + int si_signo; + /// A code identifying who raised the signal (see signal_sender_code_t). + int si_code; + /// Signal value. + sigval_t si_value; + /// The error code of the instruction that caused the signal to be raised, or 0 if there was no error. + int si_errno; + /// Process ID of sending process. + pid_t si_pid; + /// Real user ID of sending process. + uid_t si_uid; + /// Address at which fault occurred. + void *si_addr; + /// Exit value or signal for process termination. + int si_status; + /// Band event for SIGPOLL/SIGIO. + int si_band; +} siginfo_t; + +/// @brief Send signal to a process. +/// @param pid The pid of the process to which we send the signal. +/// @param sig The type of signal to send. +/// @return On success 0, on error -1 and errno is set appropriately. +int kill(pid_t pid, int sig); + +/// @brief Sets the disposition of the signal signum to handler. +/// @param signum The signal number. +/// @param handler The handler for the signal. +/// @return The previous value of the signal handler, or SIG_ERR on error. +sighandler_t signal(int signum, sighandler_t handler); + +/// @brief Examine and change a signal action. +/// @param signum Specifies the signal and can be any valid signal except SIGKILL and SIGSTOP. +/// @param act If non-NULL, the new action for signal signum is installed from act. +/// @param oldact If non-NULL, the previous action is saved in oldact. +/// @return returns 0 on success; on error, -1 is returned, and errno is set to indicate the error. +int sigaction(int signum, const sigaction_t *act, sigaction_t *oldact); + +/// @brief Examine and change blocked signals. +/// @param how Determines the behavior of the call. +/// @param set The set of signals to manage by the function. +/// @param oldset If non-NULL, the previous value of the signal mask is stored here. +/// @return returns 0 on success, and -1 on error (errno is set to indicate the cause). +/// @details +/// If set is NULL, then the signal mask is unchanged (i.e., how is +/// ignored), but the current value of the signal mask is +/// nevertheless returned in oldset (if it is not NULL). +int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); + +/// @brief Returns the string describing the given signal. +/// @param sig The signal to inquire. +/// @return String representing the signal. +const char *strsignal(int sig); + +/// @brief Prepare an empty set. +/// @param set The set to manipulate. +/// @return 0 on success and -1 on error. +int sigemptyset(sigset_t *set); + +/// @brief Prepare a full set. +/// @param set The set to manipulate. +/// @return 0 on success and -1 on error. +int sigfillset(sigset_t *set); + +/// @brief Adds the given signal to the correct set. +/// @param set The set to manipulate. +/// @param signum The signalt to handle. +/// @return 0 on success and -1 on error. +int sigaddset(sigset_t *set, int signum); + +/// @brief Removes the given signal to the correct set. +/// @param set The set to manipulate. +/// @param signum The signalt to handle. +/// @return 0 on success and -1 on error. +int sigdelset(sigset_t *set, int signum); + +/// @brief Checks if the given signal is part of the set. +/// @param set The set to manipulate. +/// @param signum The signalt to handle. +/// @return 1 if signum is a member of set, +/// 0 if signum is not a member, and -1 on error. +int sigismember(sigset_t *set, int signum); diff --git a/mentos/inc/libc/stdarg.h b/libc/inc/stdarg.h similarity index 83% rename from mentos/inc/libc/stdarg.h rename to libc/inc/stdarg.h index c36e914..097546e 100644 --- a/mentos/inc/libc/stdarg.h +++ b/libc/inc/stdarg.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file stdarg.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @brief Contains the macros required to manage variable number of arguments. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -16,7 +16,7 @@ typedef char *va_list; /// @brief Amount of space required in an argument list (ie. the stack) for an /// argument of type t. -#define va_size(t) \ +#define va_size(t) \ (((sizeof(t) + sizeof(va_item) - 1) / sizeof(va_item)) * sizeof(va_item)) /// @brief The start of a variadic list. diff --git a/libc/inc/stdbool.h b/libc/inc/stdbool.h new file mode 100644 index 0000000..97b48b5 --- /dev/null +++ b/libc/inc/stdbool.h @@ -0,0 +1,13 @@ +/// MentOS, The Mentoring Operating system project +/// @file stdbool.h +/// @brief Defines the boolean values. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// @brief Define boolean value. +typedef enum bool_t { + false, ///< [0] False. + true ///< [1] True. +} __attribute__((__packed__)) bool_t; diff --git a/libc/inc/stddef.h b/libc/inc/stddef.h new file mode 100644 index 0000000..ac2c81b --- /dev/null +++ b/libc/inc/stddef.h @@ -0,0 +1,71 @@ +/// MentOS, The Mentoring Operating system project +/// @file stddef.h +/// @brief Define basic data types. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#ifndef NULL +/// @brief Define NULL. +#define NULL ((void *)0) +#endif + +#ifndef EOF +/// @brief Define End-Of-File. +#define EOF (-1) +#endif + +/// @brief Define the size of a buffer. +#define BUFSIZ 512 + +/// Is the signed integer type of the result of subtracting two pointers. +typedef long signed int ptrdiff_t; + +/// Define the byte type. +typedef unsigned char byte_t; + +/// Define the generic size type. +typedef unsigned long size_t; + +/// Define the generic signed size type. +typedef long ssize_t; + +/// Define the type of an inode. +typedef unsigned int ino_t; + +/// Used for device IDs. +typedef unsigned int dev_t; + +/// The type of user-id. +typedef unsigned int uid_t; + +/// The type of group-id. +typedef unsigned int gid_t; + +/// The type of offset. +typedef long int off_t; + +/// The type of mode. +typedef unsigned int mode_t; + +/// This data-type is used to set protection bits of pages. +typedef unsigned int pgprot_t; + +/// It evaluates to the offset (in bytes) of a given member within +/// a struct or union type, an expression of type size_t. +#define offsetof(type, member) \ + ((size_t) & (((type *)0)->member)) + +/// Retrieve an enclosing structure from a pointer to a nested element. +#if 1 +#define container_of(ptr, type, member) \ + ((type *)((char *)(1 ? (ptr) : &((type *)0)->member) - offsetof(type, member))) +#else +#define container_of(ptr, type, member) \ + ((type *)((char *)ptr - offsetof(type, member))) +#endif + +/// Returns the alignment, in bytes, of the specified type. +#define alignof(type) offsetof( \ + struct { char c; type member; }, member) diff --git a/mentos/inc/libc/stdint.h b/libc/inc/stdint.h similarity index 88% rename from mentos/inc/libc/stdint.h rename to libc/inc/stdint.h index fa4b624..d2b6449 100644 --- a/mentos/inc/libc/stdint.h +++ b/libc/inc/stdint.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file stdint.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @brief Standard integer data-types. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once diff --git a/libc/inc/stdio.h b/libc/inc/stdio.h new file mode 100644 index 0000000..eceb086 --- /dev/null +++ b/libc/inc/stdio.h @@ -0,0 +1,129 @@ +/// MentOS, The Mentoring Operating system project +/// @file stdio.h +/// @brief Standard I/0 functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stdarg.h" +#include "stddef.h" + +/// @brief The maximum number of digits of an integer. +#define MAX_DIGITS_IN_INTEGER 11 +/// @brief The size of 'gets' buffer. +#define GETS_BUFFERSIZE 255 + +#ifndef EOF +/// @brief Define the End-Of-File. +#define EOF (-1) +#endif + +#define SEEK_SET 0 ///< The file offset is set to offset bytes. +#define SEEK_CUR 1 ///< The file offset is set to its current location plus offset bytes. +#define SEEK_END 2 ///< The file offset is set to the size of the file plus offset bytes. + +/// @brief Writes the given character to the standard output (stdout). +/// @param character The character to send to stdout. +void putchar(int character); + +/// @brief Writes the string pointed by str to the standard output (stdout) +/// and appends a newline character. +/// @param str The string to send to stdout. +void puts(char *str); + +/// @brief Returns the next character from the standard input (stdin). +/// @return The character received from stdin. +int getchar(); + +/// @brief Reads characters from the standard input (stdin). +/// @param str Where the characters are stored. +/// @return The string received from standard input. +char *gets(char *str); + +#ifndef __KERNEL__ +/// @brief Same as getchar but reads from the given file descriptor. +/// @param fd The file descriptor from which it reads. +/// @return The read character. +int fgetc(int fd); + +/// @brief Same as gets but reads from the given file descriptor. +/// @param buf The buffer where the string should be placed. +/// @param n The amount of characters to read. +/// @param fd The file descriptor from which it reads. +/// @return The read string. +char *fgets(char *buf, int n, int fd); +#endif + +/// @brief Convert the given string to an integer. +/// @param str The string to convert. +/// @return The integer contained inside the string. +int atoi(const char *str); + +/// @brief Converts the initial part of `str` to a long int value according to +/// the given base, which. +/// @param str This is the string containing the integral number. +/// @param endptr Set to the character after the numerical value. +/// @param base The base must be between 2 and 36 inclusive, or special 0. +/// @return Integral number as a long int value, else zero value is returned. +long strtol(const char *str, char **endptr, int base); + +/// @brief Write formatted output to stdout. +/// @param fmt The format string. +/// @param ... The list of arguments. +/// @return On success, the total number of characters written is returned. +/// On failure, a negative number is returned. +int printf(const char *fmt, ...); + +/// @brief Write formatted output to `str`. +/// @param str The buffer where the formatted string will be placed. +/// @param fmt Format string, following the same specifications as printf. +/// @param ... The list of arguments. +/// @return On success, the total number of characters written is returned. +/// On failure, a negative number is returned. +int sprintf(char *str, const char *fmt, ...); + +#ifndef __KERNEL__ +/// @brief The same as sprintf, but it putput on file. +/// @param fd The file descriptor associated with the file. +/// @param fmt Format string, following the same specifications as printf. +/// @param ... The list of arguments. +/// @return On success, the total number of characters written is returned. +/// On failure, a negative number is returned. +int fprintf(int fd, const char *fmt, ...); +#endif + +/// @brief Write formatted data from variable argument list to string. +/// @param str Pointer to a buffer where the resulting C-string is stored. +/// @param fmt Format string, following the same specifications as printf. +/// @param args A variable arguments list. +/// @return On success, the total number of characters written is returned. +/// On failure, a negative number is returned. +int vsprintf(char *str, const char *fmt, va_list args); + +#ifndef __KERNEL__ +/// @brief Read formatted input from stdin. +/// @param fmt Format string, following the same specifications as printf. +/// @param ... The list of arguments where the values are stored. +/// @return On success, the function returns the number of items of the +/// argument list successfully filled. EOF otherwise. +int scanf(const char *fmt, ...); +#endif + +/// @brief Read formatted data from string. +/// @param str String processed as source to retrieve the data. +/// @param fmt Format string, following the same specifications as printf. +/// @param ... The list of arguments where the values are stored. +/// @return On success, the function returns the number of items of the +/// argument list successfully filled. EOF otherwise. +int sscanf(const char *str, const char *fmt, ...); + +#ifndef __KERNEL__ +/// @brief The same as sscanf but the source is a file. +/// @param fd The file descriptor associated with the file. +/// @param fmt Format string, following the same specifications as printf. +/// @param ... The list of arguments where the values are stored. +/// @return On success, the function returns the number of items of the +/// argument list successfully filled. EOF otherwise. +int fscanf(int fd, const char *fmt, ...); +#endif \ No newline at end of file diff --git a/libc/inc/stdlib.h b/libc/inc/stdlib.h new file mode 100644 index 0000000..77be723 --- /dev/null +++ b/libc/inc/stdlib.h @@ -0,0 +1,71 @@ +/// MentOS, The Mentoring Operating system project +/// @file stdlib.h +/// @brief Useful generic functions and macros. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stddef.h" + +/// @brief Returns the number of usable bytes in the block pointed to by ptr. +/// @param ptr The pointer for which we want to retrieve the usable size. +/// @return The number of usable bytes in the block of allocated memory +/// pointed to by ptr. If ptr is not a valid pointer, 0 is returned. +size_t malloc_usable_size(void *ptr); + +/// @brief Provides dynamically allocated memory. +/// @param size The amount of memory to allocate. +/// @return A pointer to the allocated memory. +void *malloc(unsigned int size); + +/// @brief Allocates a block of memory for an array of num elements. +/// @param num The number of elements. +/// @param size The size of an element. +/// @return A pointer to the allocated memory. +void *calloc(size_t num, size_t size); + +/// @brief Reallocates the given area of memory. +/// @param ptr The pointer to the memory to reallocate. +/// @param size The new size for the memory. +/// @return A pointer to the new portion of memory. +/// @details +/// It must be previously allocated by malloc(), calloc() or realloc() and +/// not yet freed with a call to free or realloc. Otherwise, the results +/// are undefined. +void *realloc(void *ptr, size_t size); + +/// @brief Frees dynamically allocated memory. +/// @param ptr The pointer to the allocated memory. +void free(void *ptr); + +/// The maximum value returned by the rand function. +#define RAND_MAX ((1U << 31U) - 1U) + +/// @brief Allows to set the seed of the random value generator. +/// @param x The new seed. +void srand(int x); + +/// @brief Generates a random value. +/// @return The random value. +int rand(); + +/// @brief Cause an abnormal program termination with core-dump. +void abort(); + +/// @brief Tries to adds the variable to the environment. +/// @param name Name of the variable. +/// @param value Value of the variable. +/// @param overwrite Override existing variable value or not. +/// @return Zero on success, or -1 on error with errno indicating the cause. +int setenv(const char *name, const char *value, int overwrite); + +/// @brief Tries to remove the variable from the environment. +/// @param name Name of the variable. +/// @return Zero on success, or -1 on error with errno indicating the cause. +int unsetenv(const char *name); + +/// @brief Returns the value of the given variable. +/// @param name Name of the variable. +/// @return A pointer to the value, or NULL if there is no match. +char *getenv(const char *name); diff --git a/libc/inc/strerror.h b/libc/inc/strerror.h new file mode 100644 index 0000000..575de9b --- /dev/null +++ b/libc/inc/strerror.h @@ -0,0 +1,14 @@ +/// MentOS, The Mentoring Operating system project +/// @file strerror.h +/// @brief Contains the function that transfornms an errno into a string. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include + +/// @brief Returns the string representing the error number. +/// @param errnum The error number. +/// @return The string representing the error number. +char *strerror(int errnum); diff --git a/libc/inc/string.h b/libc/inc/string.h new file mode 100644 index 0000000..315bc13 --- /dev/null +++ b/libc/inc/string.h @@ -0,0 +1,311 @@ +/// MentOS, The Mentoring Operating system project +/// @file string.h +/// @brief String routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stddef.h" + +/// @brief Copies the first num characters of source to destination. +/// @param destination Pointer to the destination array where the content is to be copied. +/// @param source String to be copied. +/// @param num Maximum number of characters to be copied from source. +/// @return destination is returned. +char *strncpy(char *destination, const char *source, size_t num); + +/// @brief Compares up to n characters of s1 to those of s2. +/// @param s1 First string to be compared. +/// @param s2 Second string to be compared. +/// @param n Maximum number of characters to compare. +/// @return +/// Returns an integral value indicating the relationship between the strings: +/// <0 the first character that does not match has a lower +/// value in s1 than in s2 +/// 0 the contents of both strings are equal +/// >0 the first character that does not match has a greater +/// value in s1 than in s2 +int strncmp(const char *s1, const char *s2, size_t n); + +/// @brief Case insensitive string compare. +/// @param s1 First string to be compared. +/// @param s2 Second string to be compared. +/// @return +/// Returns an integral value indicating the relationship between the strings: +/// <0 the first character that does not match has a lower +/// value in s1 than in s2 +/// 0 the contents of both strings are equal +/// >0 the first character that does not match has a greater +/// value in s1 than in s2 +int stricmp(const char *s1, const char *s2); + +/// @brief Case-insensitively compare up to n characters of s1 to those of s2. +/// @param s1 First string to be compared. +/// @param s2 Second string to be compared. +/// @param n Maximum number of characters to compare. +/// @return +/// Returns an integral value indicating the relationship between the strings: +/// <0 the first character that does not match has a lower +/// value in s1 than in s2 +/// 0 the contents of both strings are equal +/// >0 the first character that does not match has a greater +/// value in s1 than in s2 +int strnicmp(const char *s1, const char *s2, size_t n); + +/// @brief Returns a pointer to the first occurrence of ch in str. +/// @param s The string where the search is performed. +/// @param ch Character to be located. +/// @return A pointer to the first occurrence of character in str. +char *strchr(const char *s, int ch); + +/// @brief Returns a pointer to the last occurrence of ch in str. +/// @param s The string where the search is performed. +/// @param ch Character to be located. +/// @return A pointer to the last occurrence of character in str. +char *strrchr(const char *s, int ch); + +/// @brief Returns a pointer to the first occurrence of s2 in s1, +/// or NULL if s2 is not part of s1. +/// @param s1 String to be scanned +/// @param s2 String containing the sequence of characters to match. +/// @return A pointer to the first occurrence in s1 of the entire +/// sequence of characters specified in s2, or a null pointer +/// if the sequence is not present in s1. +char *strstr(const char *s1, const char *s2); + +/// @brief Returns the length of the initial portion of string which consists +/// only of characters that are part of control. +/// @param string String to be scanned. +/// @param control String containing the characters to match. +/// @return The number of characters in the initial segment of string which +/// consist only of characters from control. +size_t strspn(const char *string, const char *control); + +/// @brief Calculates the length of the initial segment of string which +/// consists entirely of characters not in control. +/// @param string String to be scanned. +/// @param control String containing the characters to match. +/// @return The number of characters in the initial segment of string which +/// consist only of characters that are not inside control. +size_t strcspn(const char *string, const char *control); + +/// @brief Finds the first character in the string string that matches any +/// character specified in control. +/// @param string String to be scanned. +/// @param control String containing the characters to match. +/// @return +/// A pointer to the first occurrence in string of any of the characters +/// that are part of control, or a null pointer if none of the characters +/// of control is found in string before the terminating null-character. +char *strpbrk(const char *string, const char *control); + +/// @brief Make a copy of the given string. +/// @param s String to duplicate. +/// @return On success, returns a pointer to the duplicated string. +/// On failure, returns NULL with errno indicating the cause. +char *strdup(const char *s); + +/// @brief Make a copy of at most n bytes of the given string. +/// @param s String to duplicate. +/// @param n The number of character to duplicate. +/// @return On success, returns a pointer to the duplicated string. +/// On failure, returns NULL with errno indicating the cause. +char *strndup(const char *s, size_t n); + +/// @brief Appends a copy of the string src to the string dst. +/// @param dst Pointer to the destination array, which should be large enough +/// to contain the concatenated resulting string. +/// @param src String to be appended. This should not overlap dst. +/// @return destination is returned. +char *strcat(char *dst, const char *src); + +/// @brief Appends a copy of the string src to the string dst, up to n bytes. +/// @param dst Pointer to the destination array, which should be large enough +/// to contain the concatenated resulting string. +/// @param src String to be appended. This should not overlap dst. +/// @param n The number of bytes to copy. +/// @return destination is returned. +char *strncat(char *dst, const char *src, size_t n); + +/// @brief Fill the string s with the character c. +/// @param s The string that you want to fill. +/// @param c The character that you want to fill the string with. +/// @return The address of the string, s. +char *strset(char *s, int c); + +/// @brief Fill the string s with the character c, up to the given length n. +/// @param s The string that you want to fill. +/// @param c The character that you want to fill the string with. +/// @param n The maximum number of bytes to fill. +/// @return The address of the string, s. +char *strnset(char *s, int c, size_t n); + +/// @brief Reverse the string s. +/// @param s The given string which is needed to be reversed. +/// @return The address of the string, s. +char *strrev(char *s); + +/// @brief Splits string into tokens. +/// @param str String to truncate. +/// @param delim String containing the delimiter characters. +/// @return If a token is found, a pointer to the beginning of the token. +/// Otherwise, a null pointer. +/// @details +/// Notice that str is modified by being broken into smaller strings (tokens). +/// A null pointer may be specified, in which case the function continues +/// scanning where a previous successful call to the function ended. +char *strtok(char *str, const char *delim); + +/// @brief This function is a reentrant version strtok(). +/// @param str String to truncate. +/// @param delim String containing the delimiter characters. +/// @param saveptr Pointer used internally to maintain context between calls. +/// @return +/// @details +/// The saveptr argument is a pointer to a char * variable that is used +/// internally by strtok_r() in order to maintain context between successive +/// calls that parse the same string. +/// On the first call to strtok_r(), str should point to the string to be +/// parsed, and the value of saveptr is ignored. In subsequent calls, str +/// should be NULL, and saveptr should be unchanged since the previous call. +char *strtok_r(char *str, const char *delim, char **saveptr); + +/// @brief Copies the values of num bytes from the location pointed by source +/// to the memory block pointed by destination. +/// @param dst Pointer to the destination array where the content is to be +/// copied, type-casted to a pointer of type void*. +/// @param src Pointer to the source of data to be copied, type-casted to +/// a pointer of type const void*. +/// @param n Number of bytes to copy. +/// @return A pointer to dst is returned. +void *memmove(void *dst, const void *src, size_t n); + +/// @brief Searches for the first occurrence of the character c (an unsigned +/// char) in the first n bytes of the string pointed to, by the +/// argument str. +/// @param ptr Pointer to the block of memory where the search is performed. +/// @param c Value to be located. +/// @param n Number of bytes to be analyzed. +/// @return A pointer to the first occurrence of value in the block of memory. +void *memchr(const void *ptr, int c, size_t n); + +/// @brief Converts a given string into lowercase. +/// @param s String which we want to convert into lowercase. +/// @return A pointer to s. +char *strlwr(char *s); + +/// @brief Converts a given string into uppercase. +/// @param s String which we want to convert into uppercase. +/// @return A pointer to s. +char *strupr(char *s); + +/// @brief The memccpy function copies no more than n bytes from memory +/// area src to memory area dest, stopping when the character c is +/// found. +/// @param dst Points to the destination memory area. +/// @param src Points to the source memory area. +/// @param c The delimiter used to stop. +/// @param n The maximum number of copied bytes. +/// @return A pointer to the next character in dst after c, or NULL if c +/// was not found in the first n characters of src. +void *memccpy(void *dst, const void *src, int c, size_t n); + +/// @brief Copy a block of memory, handling overlap. +/// @param dst Pointer to the destination. +/// @param src Pointer to the source. +/// @param num Number of bytes to be copied. +/// @return Pointer to the destination. +void *memcpy(void *dst, const void *src, size_t num); + +/// @brief Compares the first n bytes of str1 and str2. +/// @param ptr1 First pointer to block of memory. +/// @param ptr2 Second pointer to block of memory. +/// @param n Number of bytes to compare. +/// @return +/// Returns an integral value indicating the relationship between +/// the memory blocks: +/// <0 the first byte that does not match has a lower +/// value in ptr1 than in ptr2 +/// 0 the contents of both memory blocks are equal +/// >0 the first byte that does not match has a greater +/// value in ptr1 than in ptr2 +int memcmp(const void *ptr1, const void *ptr2, size_t n); + +/// @brief Sets the first num bytes of the block of memory pointed by ptr +/// to the specified value. +/// @param ptr Pointer to the block of memory to set. +/// @param value Value to be set. +/// @param num Number of bytes to be set to the given value. +/// @return The same ptr. +void *memset(void *ptr, int value, size_t num); + +/// @brief Copy the string src into the array dst. +/// @param dst The destination array where the content is to be copied. +/// @param src String to be copied. +/// @return A pointer to dst is returned. +char *strcpy(char *dst, const char *src); + +/// @brief Checks if the two strings are equal. +/// @param s1 First string to be compared. +/// @param s2 Second string to be compared. +/// @return +/// Returns an integral value indicating the relationship between the strings: +/// <0 the first character that does not match has a lower +/// value in s1 than in s2 +/// 0 the contents of both strings are equal +/// >0 the first character that does not match has a greater +/// value in s1 than in s2 +int strcmp(const char *s1, const char *s2); + +/// @brief Returns the length of the string s. +/// @param s Pointer to the null-terminated byte string to be examined. +/// @return The length of the null-terminated string str. +size_t strlen(const char *s); + +/// @brief Returns the number of characters inside s, excluding the +/// terminating null byte ('\0'), but at most count. +/// @param s Pointer to the null-terminated byte string to be examined. +/// @param maxlen The upperbound on the length. +/// @return Returns strlen(s), if that is less than maxlen, or maxlen +/// if there is no null terminating ('\0') among the first maxlen +/// characters pointed to by s. +size_t strnlen(const char *s, size_t maxlen); + +/// @brief Removes any whitespace characters from the beginning and end of str. +/// @param str The string to trim. +/// @return A pointer to str. +char *trim(char *str); + +/// @brief Separate the given string based on a given delimiter. +/// @param stringp The string to separate. +/// @param delim The delimiter used to separate the string. +/// @return Returns a pointer to stringp. +/// @details +/// Finds the first token in stringp, that is delimited by one of the bytes in +/// the string delim. This token is terminated by overwriting the delimiter +/// with a null byte ('\0'), and *stringp is updated to point past the token. +/// In case no delimiter was found, the token is taken to be the entire string +/// *stringp, and *stringp is made NULL. +char *strsep(char **stringp, const char *delim); + +/// @brief Move the number "num" into a string. +/// @param buffer The string containing the number. +/// @param num The number to convert. +/// @param base The base used to convert. +/// @return A pointer to buffer. +char *itoa(char *buffer, unsigned int num, unsigned int base); + +/// @brief Replaces the occurrences of find with replace inside str. +/// @param str The string to manipulate. +/// @param find The character to replace. +/// @param replace The character used to replace. +/// @return A pointer to str. +char *replace_char(char *str, char find, char replace); + +/// @brief Converts a file mode (the type and permission information associated +/// with an inode) into a symbolic string which is stored in the location +/// referenced by p. +/// @param mode File mode that encodes access permissions and file type. +/// @param p Buffer used to hold the string representation of file mode m. +void strmode(mode_t mode, char *p); \ No newline at end of file diff --git a/libc/inc/sys/bitops.h b/libc/inc/sys/bitops.h new file mode 100644 index 0000000..1c65468 --- /dev/null +++ b/libc/inc/sys/bitops.h @@ -0,0 +1,41 @@ +/// MentOS, The Mentoring Operating system project +/// @file bitops.h +/// @brief Bitmasks functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#define bit_set(V, B) ((V) | (1U << (B))) ///< Sets the given bit. +#define bit_clear(V, B) ((V) & ~(1U << (B))) ///< Clears the given bit. +#define bit_flip(V, B) ((V) ^ (1U << (B))) ///< Flips the given bit. +#define bit_set_assign(V, B) ((V) |= (1U << (B))) ///< Sets the given bit, permanently. +#define bit_clear_assign(V, B) ((V) &= ~(1U << (B))) ///< Clears the given bit, permanently. +#define bit_flip_assign(V, B) ((V) ^= (1U << (B))) ///< Flips the given bit, permanently. +#define bit_check(V, B) ((V) & (1U << (B))) ///< Checks if the given bit is 1. + +#define bitmask_set(V, M) ((V) | (M)) ///< Sets the bits identified by the mask. +#define bitmask_clear(V, M) ((V) & ~(M)) ///< Clears the bits identified by the mask. +#define bitmask_flip(V, M) ((V) ^ (M)) ///< Flips the bits identified by the mask. +#define bitmask_set_assign(V, M) ((V) |= (M)) ///< Sets the bits identified by the mask, permanently. +#define bitmask_clear_assign(V, M) ((V) &= ~(M)) ///< Clears the bits identified by the mask, permanently. +#define bitmask_flip_assign(V, M) ((V) ^= (M)) ///< Flips the bits identified by the mask, permanently. +#define bitmask_check(V, M) ((V) & (M)) ///< Checks if the bits identified by the mask are all 1. + +/// @brief Finds the first bit at zero, starting from the less significative bit. +static inline int find_first_zero(unsigned long value) +{ + for (int i = 0; i < 32; ++i) + if (!bit_check(value, i)) + return i; + return 0; +} + +/// @brief Finds the first bit not zero, starting from the less significative bit. +static inline int find_first_non_zero(unsigned long value) +{ + for (int i = 0; i < 32; ++i) + if (bit_check(value, i)) + return i; + return 0; +} \ No newline at end of file diff --git a/libc/inc/sys/dirent.h b/libc/inc/sys/dirent.h new file mode 100644 index 0000000..1c6d185 --- /dev/null +++ b/libc/inc/sys/dirent.h @@ -0,0 +1,50 @@ +/// MentOS, The Mentoring Operating system project +/// @file dirent.h +/// @brief Functions used to manage directories. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "limits.h" +#include "stddef.h" + +/// File types for `d_type'. +enum { + DT_UNKNOWN = 0, + DT_FIFO = 1, + DT_CHR = 2, + DT_DIR = 4, + DT_BLK = 6, + DT_REG = 8, + DT_LNK = 10, + DT_SOCK = 12, + DT_WHT = 14 +}; + +static const char dt_char_array[] = { + '?', // DT_UNKNOWN = 0, + 'p', //DT_FIFO = 1, + 'c', //DT_CHR = 2, + '*', + 'd', //DT_DIR = 4, + '*', + 'b', //DT_BLK = 6, + '*', + '-', //DT_REG = 8, + '*', + 'l', //DT_LNK = 10, + '*', + 's', //DT_SOCK = 12, + '*', + '?', //DT_WHT = 14 +}; + +/// Directory entry. +typedef struct dirent_t { + ino_t d_ino; ///< Inode number. + off_t d_off; ///< Offset to next linux_dirent. + unsigned short d_reclen; ///< Length of this linux_dirent. + unsigned short d_type; ///< type of the directory entry. + char d_name[NAME_MAX]; ///< Filename (null-terminated) +} dirent_t; \ No newline at end of file diff --git a/libc/inc/sys/errno.h b/libc/inc/sys/errno.h new file mode 100644 index 0000000..1608340 --- /dev/null +++ b/libc/inc/sys/errno.h @@ -0,0 +1,138 @@ +/// MentOS, The Mentoring Operating system project +/// @file errno.h +/// @brief System call errors definition. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +extern int *__geterrno(); + +/// Provide easy access to the error number. +#define errno (*__geterrno()) + +#define EPERM 1 ///< Operation not permitted. +#define ENOENT 2 ///< No such file or directory. +#define ESRCH 3 ///< No such process. +#define EINTR 4 ///< Interrupted system call. +#define EIO 5 ///< I/O error. +#define ENXIO 6 ///< No such device or address. +#define E2BIG 7 ///< Arg list too long. +#define ENOEXEC 8 ///< Exec format error. +#define EBADF 9 ///< Bad file number. +#define ECHILD 10 ///< No child processes. +#define EAGAIN 11 ///< Try again. +#define ENOMEM 12 ///< Out of memory. +#define EACCES 13 ///< Permission denied. +#define EFAULT 14 ///< Bad address. +#define ENOTBLK 15 ///< Block device required. +#define EBUSY 16 ///< Device or resource busy. +#define EEXIST 17 ///< File exists. +#define EXDEV 18 ///< Cross-device link. +#define ENODEV 19 ///< No such device. +#define ENOTDIR 20 ///< Not a directory. +#define EISDIR 21 ///< Is a directory. +#define EINVAL 22 ///< Invalid argument. +#define ENFILE 23 ///< File table overflow. +#define EMFILE 24 ///< Too many open files. +#define ENOTTY 25 ///< Not a typewriter. +#define ETXTBSY 26 ///< Text file busy. +#define EFBIG 27 ///< File too large. +#define ENOSPC 28 ///< No space left on device. +#define ESPIPE 29 ///< Illegal seek. +#define EROFS 30 ///< Read-only file system. +#define EMLINK 31 ///< Too many links. +#define EPIPE 32 ///< Broken pipe. +#define EDOM 33 ///< Math argument out of domain of func. +#define ERANGE 34 ///< Math result not representable. +#define EDEADLK 35 ///< Resource deadlock would occur. +#define ENAMETOOLONG 36 ///< File name too long. +#define ENOLCK 37 ///< No record locks available. +#define ENOSYS 38 ///< Function not implemented. +#define ENOTEMPTY 39 ///< Directory not empty. +#define ELOOP 40 ///< Too many symbolic links encountered. +#define EWOULDBLOCK EAGAIN ///< Operation would block. +#define ENOMSG 42 ///< No message of desired type. +#define EIDRM 43 ///< Identifier removed. +#define ECHRNG 44 ///< Channel number out of range. +#define EL2NSYNC 45 ///< Level 2 not synchronized. +#define EL3HLT 46 ///< Level 3 halted. +#define EL3RST 47 ///< Level 3 reset. +#define ELNRNG 48 ///< Link number out of range. +#define EUNATCH 49 ///< Protocol driver not attached. +#define ENOCSI 50 ///< No CSI structure available. +#define EL2HLT 51 ///< Level 2 halted. +#define EBADE 52 ///< Invalid exchange. +#define EBADR 53 ///< Invalid request descriptor. +#define EXFULL 54 ///< Exchange full. +#define ENOANO 55 ///< No anode. +#define EBADRQC 56 ///< Invalid request code. +#define EBADSLT 57 ///< Invalid slot. +#define EDEADLOCK EDEADLK ///< Resource deadlock would occur. +#define EBFONT 59 ///< Bad font file format. +#define ENOSTR 60 ///< Device not a stream. +#define ENODATA 61 ///< No data available. +#define ETIME 62 ///< Timer expired. +#define ENOSR 63 ///< Out of streams resources. +#define ENONET 64 ///< Machine is not on the network. +#define ENOPKG 65 ///< Package not installed. +#define EREMOTE 66 ///< Object is remote. +#define ENOLINK 67 ///< Link has been severed. +#define EADV 68 ///< Advertise error. +#define ESRMNT 69 ///< Srmount error. +#define ECOMM 70 ///< Communication error on send. +#define EPROTO 71 ///< Protocol error. +#define EMULTIHOP 72 ///< Multihop attempted. +#define EDOTDOT 73 ///< RFS specific error. +#define EBADMSG 74 ///< Not a data message. +#define EOVERFLOW 75 ///< Value too large for defined data type. +#define ENOTUNIQ 76 ///< Name not unique on network. +#define EBADFD 77 ///< File descriptor in bad state. +#define EREMCHG 78 ///< Remote address changed. +#define ELIBACC 79 ///< Can not access a needed shared library. +#define ELIBBAD 80 ///< Accessing a corrupted shared library. +#define ELIBSCN 81 ///< .lib section in a.out corrupted. +#define ELIBMAX 82 ///< Attempting to link in too many shared libraries. +#define ELIBEXEC 83 ///< Cannot exec a shared library directly. +#define EILSEQ 84 ///< Illegal byte sequence. +#define ERESTART 85 ///< Interrupted system call should be restarted. +#define ESTRPIPE 86 ///< Streams pipe error. +#define EUSERS 87 ///< Too many users. +#define ENOTSOCK 88 ///< Socket operation on non-socket. +#define EDESTADDRREQ 89 ///< Destination address required. +#define EMSGSIZE 90 ///< Message too long. +#define EPROTOTYPE 91 ///< Protocol wrong type for socket. +#define ENOPROTOOPT 92 ///< Protocol not available. +#define EPROTONOSUPPORT 93 ///< Protocol not supported. +#define ESOCKTNOSUPPORT 94 ///< Socket type not supported. +#define EOPNOTSUPP 95 ///< Operation not supported on transport endpoint. +#define EPFNOSUPPORT 96 ///< Protocol family not supported. +#define EAFNOSUPPORT 97 ///< Address family not supported by protocol. +#define EADDRINUSE 98 ///< Address already in use. +#define EADDRNOTAVAIL 99 ///< Cannot assign requested address. +#define ENETDOWN 100 ///< Network is down. +#define ENETUNREACH 101 ///< Network is unreachable. +#define ENETRESET 102 ///< Network dropped connection because of reset. +#define ECONNABORTED 103 ///< Software caused connection abort. +#define ECONNRESET 104 ///< Connection reset by peer. +#define ENOBUFS 105 ///< No buffer space available. +#define EISCONN 106 ///< Transport endpoint is already connected. +#define ENOTCONN 107 ///< Transport endpoint is not connected. +#define ESHUTDOWN 108 ///< Cannot send after transport endpoint shutdown. +#define ETOOMANYREFS 109 ///< Too many references: cannot splice. +#define ETIMEDOUT 110 ///< Connection timed out. +#define ECONNREFUSED 111 ///< Connection refused. +#define EHOSTDOWN 112 ///< Host is down. +#define EHOSTUNREACH 113 ///< No route to host. +#define EALREADY 114 ///< Operation already in progress. +#define EINPROGRESS 115 ///< Operation now in progress. +#define ESTALE 116 ///< Stale NFS file handle. +#define EUCLEAN 117 ///< Structure needs cleaning. +#define ENOTNAM 118 ///< Not a XENIX named type file. +#define ENAVAIL 119 ///< No XENIX semaphores available. +#define EISNAM 120 ///< Is a named type file. +#define EREMOTEIO 121 ///< Remote I/O error. +#define EDQUOT 122 ///< Quota exceeded. +#define ENOMEDIUM 123 ///< No medium found. +#define EMEDIUMTYPE 124 ///< Wrong medium type. +#define ENOTSCHEDULABLE 125 ///< The periodc process cannot be scheduled. diff --git a/libc/inc/sys/ioctl.h b/libc/inc/sys/ioctl.h new file mode 100644 index 0000000..d039852 --- /dev/null +++ b/libc/inc/sys/ioctl.h @@ -0,0 +1,15 @@ +/// MentOS, The Mentoring Operating system project +/// @file ioctl.h +/// @brief Input/Output ConTroL (IOCTL) functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// @brief Perform the I/O control operation specified by REQUEST on FD. +/// One argument may follow; its presence and type depend on REQUEST. +/// @param fd Must be an open file descriptor. +/// @param request The device-dependent request code +/// @param data An untyped pointer to memory. +/// @return Return value depends on REQUEST. Usually -1 indicates error. +int ioctl(int fd, unsigned long int request, void *data); \ No newline at end of file diff --git a/libc/inc/sys/reboot.h b/libc/inc/sys/reboot.h new file mode 100644 index 0000000..4a6dd3d --- /dev/null +++ b/libc/inc/sys/reboot.h @@ -0,0 +1,36 @@ +/// MentOS, The Mentoring Operating system project +/// @file reboot.h +/// @brief Defines the values required to issue a reboot. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// Magic values required to use _reboot() system call. +#define LINUX_REBOOT_MAGIC1 0xfee1dead +/// Magic values required to use _reboot() system call. +#define LINUX_REBOOT_MAGIC2 672274793 +/// Magic values required to use _reboot() system call. +#define LINUX_REBOOT_MAGIC2A 85072278 +/// Magic values required to use _reboot() system call. +#define LINUX_REBOOT_MAGIC2B 369367448 +/// Magic values required to use _reboot() system call. +#define LINUX_REBOOT_MAGIC2C 537993216 + +// Commands accepted by the _reboot() system call. +/// Restart system using default command and mode. +#define LINUX_REBOOT_CMD_RESTART 0x01234567 +/// Stop OS and give system control to ROM monitor, if any. +#define LINUX_REBOOT_CMD_HALT 0xCDEF0123 +/// Ctrl-Alt-Del sequence causes RESTART command. +#define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF +/// Ctrl-Alt-Del sequence sends SIGINT to init task. +#define LINUX_REBOOT_CMD_CAD_OFF 0x00000000 +/// Stop OS and remove all power from system, if possible. +#define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC +/// Restart system using given command string. +#define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4 +/// Suspend system using software suspend if compiled in. +#define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2 +/// Restart system using a previously loaded Linux kernel +#define LINUX_REBOOT_CMD_KEXEC 0x45584543 diff --git a/libc/inc/sys/stat.h b/libc/inc/sys/stat.h new file mode 100644 index 0000000..39fb7b2 --- /dev/null +++ b/libc/inc/sys/stat.h @@ -0,0 +1,35 @@ +/// MentOS, The Mentoring Operating system project +/// @file stat.h +/// @brief Stat functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once +#define __SYS_STAT_H + +#include "bits/stat.h" +#include "stddef.h" +#include "time.h" + +/// @brief Retrieves information about the file at the given location. +/// @param path The path to the file that is being inquired. +/// @param buf A structure where data about the file will be stored. +/// @return Returns a negative value on failure. +int stat(const char *path, stat_t *buf); + +/// @brief Retrieves information about the file at the given location. +/// @param fd The file descriptor of the file that is being inquired. +/// @param buf A structure where data about the file will be stored. +/// @return Returns a negative value on failure. +int fstat(int fd, stat_t *buf); + +/// @brief Creates a new directory at the given path. +/// @param path The path of the new directory. +/// @param mode The permission of the new directory. +/// @return Returns a negative value on failure. +int mkdir(const char *path, mode_t mode); + +/// @brief Removes the given directory. +/// @param path The path to the directory to remove. +/// @return Returns a negative value on failure. +int rmdir(const char *path); \ No newline at end of file diff --git a/libc/inc/sys/types.h b/libc/inc/sys/types.h new file mode 100644 index 0000000..42336b2 --- /dev/null +++ b/libc/inc/sys/types.h @@ -0,0 +1,70 @@ +/// MentOS, The Mentoring Operating system project +/// @file types.h +/// @brief Collection of Kernel datatype +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// The type of process id. +typedef signed int pid_t; + +/// The type of process user variable. +typedef unsigned int user_t; + +/// The type of process status. +typedef unsigned int status_t; + +/// Type for system keys. +typedef int key_t; + +/// Defines the list of flags of a process. +typedef enum eflags_list { + /// Carry flag. + EFLAG_CF = (1 << 0), + + /// Parity flag. + EFLAG_PF = (1 << 2), + + /// Auxiliary carry flag. + EFLAG_AF = (1 << 4), + + /// Zero flag. + EFLAG_ZF = (1 << 6), + + /// Sign flag. + EFLAG_SF = (1 << 7), + + /// Trap flag. + EFLAG_TF = (1 << 8), + + /// Interrupt enable flag. + EFLAG_IF = (1 << 9), + + /// Direction flag. + EFLAG_DF = (1 << 10), + + /// Overflow flag. + EFLAG_OF = (1 << 11), + + /// Nested task flag. + EFLAG_NT = (1 << 14), + + /// Resume flag. + EFLAG_RF = (1 << 16), + + /// Virtual 8086 mode flag. + EFLAG_VM = (1 << 17), + + /// Alignment check flag (486+). + EFLAG_AC = (1 << 18), + + /// Virutal interrupt flag. + EFLAG_VIF = (1 << 19), + + /// Virtual interrupt pending flag. + EFLAG_VIP = (1 << 20), + + /// ID flag. + EFLAG_ID = (1 << 21), +} eflags_list; diff --git a/libc/inc/sys/unistd.h b/libc/inc/sys/unistd.h new file mode 100644 index 0000000..b305d0f --- /dev/null +++ b/libc/inc/sys/unistd.h @@ -0,0 +1,223 @@ +/// MentOS, The Mentoring Operating system project +/// @file unistd.h +/// @brief Functions used to manage files. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "sys/types.h" +#include "sys/dirent.h" +#include "stddef.h" + +#define STDIN_FILENO 0 ///< Standard input. +#define STDOUT_FILENO 1 ///< Standard output. +#define STDERR_FILENO 2 ///< Standard error output. + +/// @brief Read data from a file descriptor. +/// @param fd The file descriptor. +/// @param buf The buffer. +/// @param nbytes The number of bytes to read. +/// @return The number of read characters. +ssize_t read(int fd, void *buf, size_t nbytes); + +/// @brief Write data into a file descriptor. +/// @param fd The file descriptor. +/// @param buf The buffer collecting data to written. +/// @param nbytes The number of bytes to write. +/// @return The number of written bytes. +ssize_t write(int fd, void *buf, size_t nbytes); + +/// @brief Opens the file specified by pathname. +/// @param pathname A pathname for a file. +/// @param flags Used to set the file status flags and file access modes +/// of the open file description. +/// @param mode Specifies the file mode bits be applied when a new file +/// is created. +/// @return Returns a file descriptor, a small, nonnegative integer for +/// use in subsequent system calls. +int open(const char *pathname, int flags, mode_t mode); + +/// @brief Close a file descriptor. +/// @param fd The file descriptor. +/// @return The result of the operation. +int close(int fd); + +/// @brief Repositions the file offset inside a file. +/// @param fd The file descriptor of the file. +/// @param offset The offest to use for the operation. +/// @param whence The type of operation. +/// @return Upon successful completion, returns the resulting offset +/// location as measured in bytes from the beginning of the file. On +/// error, the value (off_t) -1 is returned and errno is set to +/// indicate the error. +off_t lseek(int fd, off_t offset, int whence); + +/// @brief Delete a name and possibly the file it refers to. +/// @param path The path to the file. +/// @return +int unlink(const char *path); + +/// @brief Wrapper for exit system call. +/// @param status The exit status. +extern void exit(int status); + +/// @brief Returns the process ID (PID) of the calling process. +/// @return pid_t process identifier. +extern pid_t getpid(); + +///@brief Return session id of the given process. +/// If pid == 0 return the SID of the calling process +/// If pid != 0 return the SID corresponding to the process having identifier == pid +///@param pid process identifier from wich we want the SID +///@return On success return SID of the session +/// Otherwise return -1 with errno set on: EPERM or ESRCH +extern pid_t getsid(pid_t pid); + +///@brief creates a new session if the calling process is not a +/// process group leader. The calling process is the leader of the +/// new session (i.e., its session ID is made the same as its process +/// ID). The calling process also becomes the process group leader +/// of a new process group in the session (i.e., its process group ID +/// is made the same as its process ID). +///@return On success return SID of the session just created +/// Otherwise return -1 with errno : EPERM +extern pid_t setsid(); + +///@brief returns the group ID of the calling process. +///@return GID of the current process +extern pid_t getgid(); + +///@brief sets the effective group ID of the calling process. +///@param pid process identifier to +///@return On success, zero is returned. +/// Otherwise returns -1 with errno set to :EINVAL or EPERM +extern int setgid(pid_t pid); + +/// @brief Returns the parent process ID (PPID) of the calling process. +/// @return pid_t parent process identifier. +extern pid_t getppid(); + +/// @brief Clone the calling process, but without copying the whole address space. +/// The calling process is suspended until the new process exits or is +/// replaced by a call to `execve'. +/// @return Return -1 for errors, 0 to the new process, and the process ID of +/// the new process to the old process. +extern pid_t fork(); + +/// @brief Replaces the current process image with a new process image (argument list). +/// @param path The absolute path to the binary file to execute. +/// @param arg A list of one or more pointers to null-terminated strings that represent +/// the argument list available to the executed program. +/// @param ... The argument list. +/// @return Returns -1 only if an error has occurred, and sets errno. +int execl(const char *path, const char *arg, ...); + +/// @brief Replaces the current process image with a new process image (argument list). +/// @param file The name of the binary file to execute, which is searched inside the +/// paths specified inside the PATH environmental variable. +/// @param arg A list of one or more pointers to null-terminated strings that represent +/// the argument list available to the executed program. +/// @param ... The argument list. +/// @return Returns -1 only if an error has occurred, and sets errno. +int execlp(const char *file, const char *arg, ...); + +/// @brief Replaces the current process image with a new process image (argument list). +/// @param path The absolute path to the binary file to execute. +/// @param arg A list of one or more pointers to null-terminated strings that represent +/// the argument list available to the executed program. +/// @param ... The argument list which contains as last argument the environment. +/// @return Returns -1 only if an error has occurred, and sets errno. +int execle(const char *path, const char *arg, ...); + +/// @brief Replaces the current process image with a new process image (argument list). +/// @param file The name of the binary file to execute, which is searched inside the +/// paths specified inside the PATH environmental variable. +/// @param arg A list of one or more pointers to null-terminated strings that represent +/// the argument list available to the executed program. +/// @param ... The argument list which contains as last argument the environment. +/// @return Returns -1 only if an error has occurred, and sets errno. +int execlpe(const char *file, const char *arg, ...); + +/// @brief Replaces the current process image with a new process image (argument vector). +/// @param path The absolute path to the binary file to execute. +/// @param argv A vector of one or more pointers to null-terminated strings that represent +/// the argument list available to the executed program. +/// @return Returns -1 only if an error has occurred, and sets errno. +int execv(const char *path, char *const argv[]); + +/// @brief Replaces the current process image with a new process image (argument vector). +/// @param file The name of the binary file to execute, which is searched inside the +/// paths specified inside the PATH environmental variable. +/// @param argv A vector of one or more pointers to null-terminated strings that represent +/// the argument list available to the executed program. +/// @return Returns -1 only if an error has occurred, and sets errno. +int execvp(const char *file, char *const argv[]); + +/// @brief Replaces the current process image with a new process +/// image (argument vector), allows the caller to specify +/// the environment of the executed program via `envp`. +/// @param path The absolute path to the binary file to execute. +/// @param argv A vector of one or more pointers to null-terminated strings that represent +/// the argument list available to the executed program. +/// @param envp A vector of one or more pointers to null-terminated strings that represent +/// the environment list available to the executed program. +/// @return Returns -1 only if an error has occurred, and sets errno. +int execve(const char *path, char *const argv[], char *const envp[]); + +/// @brief Replaces the current process image with a new process +/// image (argument vector), allows the caller to specify +/// the environment of the executed program via `envp`. +/// @param file The name of the binary file to execute, which is searched inside the +/// paths specified inside the PATH environmental variable. +/// @param argv A vector of one or more pointers to null-terminated strings that represent +/// the argument list available to the executed program. +/// @param envp A vector of one or more pointers to null-terminated strings that represent +/// the environment list available to the executed program. +/// @return Returns -1 only if an error has occurred, and sets errno. +int execvpe(const char *file, char *const argv[], char *const envp[]); + +/// @brief Adds inc to the nice value for the calling thread. +/// @param inc The value to add to the nice. +/// @return On success, the new nice value is returned. On error, -1 is +/// returned, and errno is set appropriately. +int nice(int inc); + +/// @brief Reboots the system, or enables/disables the reboot keystroke. +/// @param magic1 fails (with the error EINVAL) unless equals LINUX_REBOOT_MAGIC1. +/// @param magic2 fails (with the error EINVAL) unless equals LINUX_REBOOT_MAGIC2. +/// @param cmd The command to send to the reboot. +/// @param arg Argument passed with some specific commands. +/// @return For the values of cmd that stop or restart the system, a +/// successful call to reboot() does not return. For the other cmd +/// values, zero is returned on success. In all cases, -1 is +/// returned on failure, and errno is set appropriately. +int reboot(int magic1, int magic2, unsigned int cmd, void *arg); + +/// @brief Get current working directory. +/// @param buf The array where the CWD will be copied. +/// @param size The size of the array. +/// @return On success, returns the same pointer to buf. +/// On failure, returnr NULL, and errno is set to indicate the error. +char *getcwd(char *buf, size_t size); + +/// @brief Changes the current working directory to the given path. +/// @param path The new current working directory. +int chdir(char const *path); + +/// @brief Is identical to chdir(), the only difference is that the +/// directory is given as an open file descriptor. +/// @param fd The file descriptor of the open directory. +int fchdir(int fd); + +/// Provide access to the directory entries. +/// @param fd The fd pointing to the opened directory. +/// @param dirp The buffer where de data should be placed. +/// @param count The size of the buffer. +/// @return On success, the number of bytes read is returned. On end of +/// directory, 0 is returned. On error, -1 is returned, and errno is set +/// appropriately. +int getdents(int fd, dirent_t *dirp, unsigned int count); + +/// @brief Send signal to calling thread after desired seconds. +int alarm(int seconds); \ No newline at end of file diff --git a/libc/inc/sys/utsname.h b/libc/inc/sys/utsname.h new file mode 100644 index 0000000..c3330b9 --- /dev/null +++ b/libc/inc/sys/utsname.h @@ -0,0 +1,30 @@ +/// MentOS, The Mentoring Operating system project +/// @file utsname.h +/// @brief Functions used to provide information about the machine & OS. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// Maximum length of the string used by utsname. +#define SYS_LEN 257 + +/// @brief Holds information concerning the machine and the os. +typedef struct utsname_t { + /// The name of the system. + char sysname[SYS_LEN]; + /// The name of the node. + char nodename[SYS_LEN]; + /// Operating system release (e.g., "2.6.28"). + char release[SYS_LEN]; + /// The version of the OS. + char version[SYS_LEN]; + /// The name of the machine. + char machine[SYS_LEN]; +} utsname_t; + + +/// @brief Returns system information in the structure pointed to by buf. +/// @param buf Buffer where the info will be placed. +/// @return 0 on success, a negative value on failure. +int uname(utsname_t* buf); diff --git a/mentos/inc/sys/wait.h b/libc/inc/sys/wait.h similarity index 61% rename from mentos/inc/sys/wait.h rename to libc/inc/sys/wait.h index c82ae56..62bc49a 100644 --- a/mentos/inc/sys/wait.h +++ b/libc/inc/sys/wait.h @@ -1,13 +1,11 @@ /// MentOS, The Mentoring Operating system project /// @file wait.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @brief Event management functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "types.h" - /// @brief Return immediately if no child is there to be waited for. #define WNOHANG 0x00000001 @@ -43,10 +41,30 @@ /// This macro can only be evaluated if WIFSTOPPED() returned nonzero. #define WSTOPSIG(status) (WEXITSTATUS(status)) +//==== Task States ============================================================ +#define TASK_RUNNING 0x00 ///< The process is either: 1) running on CPU or 2) waiting in a run queue. +#define TASK_INTERRUPTIBLE (1 << 0) ///< The process is sleeping, waiting for some event to occur. +#define TASK_UNINTERRUPTIBLE (1 << 1) ///< Similar to TASK_INTERRUPTIBLE, but it doesn't process signals. +#define TASK_STOPPED (1 << 2) ///< Stopped, it's not running, and not able to run. +#define TASK_TRACED (1 << 3) ///< Is being monitored by other processes such as debuggers. +#define EXIT_ZOMBIE (1 << 4) ///< The process has terminated. +#define EXIT_DEAD (1 << 5) ///< The final state. +//============================================================================== + +/// @brief Suspends the execution of the calling thread until ANY child has +/// changed state. +/// @param status Variable where the new status of the child is stored. +/// @return On error, -1 is returned, otherwise it returns the pid of the +/// child that has unlocked the wait. extern pid_t wait(int *status); /// @brief Suspends the execution of the calling thread until a child /// specified by pid argument has changed state. +/// @param pid Se details below for more information. +/// @param status Variable where the new status of the child is stored. +/// @param options Waitpid options. +/// @return On error, -1 is returned, otherwise it returns the pid of the +/// child that has unlocked the wait. /// @details /// By default, waitpid() waits only for terminated children, but this /// behavior is modifiable via the options argument, as described below. @@ -56,5 +74,4 @@ extern pid_t wait(int *status); /// equal to that of the calling process. /// > 0 meaning wait for the child whose process ID is equal to the /// value of pid. -/// @return On error, -1 is returned. extern pid_t waitpid(pid_t pid, int *status, int options); diff --git a/libc/inc/system/syscall_types.h b/libc/inc/system/syscall_types.h new file mode 100644 index 0000000..98bf013 --- /dev/null +++ b/libc/inc/system/syscall_types.h @@ -0,0 +1,308 @@ +/// MentOS, The Mentoring Operating system project +/// @file syscall_types.h +/// @brief System Call numbers. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#define __NR_exit 1 ///< System-call number for `exit` +#define __NR_fork 2 ///< System-call number for `fork` +#define __NR_read 3 ///< System-call number for `read` +#define __NR_write 4 ///< System-call number for `write` +#define __NR_open 5 ///< System-call number for `open` +#define __NR_close 6 ///< System-call number for `close` +#define __NR_waitpid 7 ///< System-call number for `waitpid` +#define __NR_creat 8 ///< System-call number for `creat` +#define __NR_link 9 ///< System-call number for `link` +#define __NR_unlink 10 ///< System-call number for `unlink` +#define __NR_execve 11 ///< System-call number for `execve` +#define __NR_chdir 12 ///< System-call number for `chdir` +#define __NR_time 13 ///< System-call number for `time` +#define __NR_mknod 14 ///< System-call number for `mknod` +#define __NR_chmod 15 ///< System-call number for `chmod` +#define __NR_lchown 16 ///< System-call number for `lchown` +#define __NR_stat 18 ///< System-call number for `stat` +#define __NR_lseek 19 ///< System-call number for `lseek` +#define __NR_getpid 20 ///< System-call number for `getpid` +#define __NR_mount 21 ///< System-call number for `mount` +#define __NR_oldumount 22 ///< System-call number for `oldumount` +#define __NR_setuid 23 ///< System-call number for `setuid` +#define __NR_getuid 24 ///< System-call number for `getuid` +#define __NR_stime 25 ///< System-call number for `stime` +#define __NR_ptrace 26 ///< System-call number for `ptrace` +#define __NR_alarm 27 ///< System-call number for `alarm` +#define __NR_fstat 28 ///< System-call number for `fstat` +#define __NR_pause 29 ///< System-call number for `pause` +#define __NR_utime 30 ///< System-call number for `utime` +#define __NR_access 33 ///< System-call number for `access` +#define __NR_nice 34 ///< System-call number for `nice` +#define __NR_sync 36 ///< System-call number for `sync` +#define __NR_kill 37 ///< System-call number for `kill` +#define __NR_rename 38 ///< System-call number for `rename` +#define __NR_mkdir 39 ///< System-call number for `mkdir` +#define __NR_rmdir 40 ///< System-call number for `rmdir` +#define __NR_dup 41 ///< System-call number for `dup` +#define __NR_pipe 42 ///< System-call number for `pipe` +#define __NR_times 43 ///< System-call number for `times` +#define __NR_brk 45 ///< System-call number for `brk` +#define __NR_setgid 46 ///< System-call number for `setgid` +#define __NR_getgid 47 ///< System-call number for `getgid` +#define __NR_signal 48 ///< System-call number for `signal` +#define __NR_geteuid 49 ///< System-call number for `geteuid` +#define __NR_getegid 50 ///< System-call number for `getegid` +#define __NR_acct 51 ///< System-call number for `acct` +#define __NR_umount 52 ///< System-call number for `umount` +#define __NR_ioctl 54 ///< System-call number for `ioctl` +#define __NR_fcntl 55 ///< System-call number for `fcntl` +#define __NR_setpgid 57 ///< System-call number for `setpgid` +#define __NR_olduname 59 ///< System-call number for `olduname` +#define __NR_umask 60 ///< System-call number for `umask` +#define __NR_chroot 61 ///< System-call number for `chroot` +#define __NR_ustat 62 ///< System-call number for `ustat` +#define __NR_dup2 63 ///< System-call number for `dup2` +#define __NR_getppid 64 ///< System-call number for `getppid` +#define __NR_getpgrp 65 ///< System-call number for `getpgrp` +#define __NR_setsid 66 ///< System-call number for `setsid` +#define __NR_sigaction 67 ///< System-call number for `sigaction` +#define __NR_sgetmask 68 ///< System-call number for `sgetmask` +#define __NR_ssetmask 69 ///< System-call number for `ssetmask` +#define __NR_setreuid 70 ///< System-call number for `setreuid` +#define __NR_setregid 71 ///< System-call number for `setregid` +#define __NR_sigsuspend 72 ///< System-call number for `sigsuspend` +#define __NR_sigpending 73 ///< System-call number for `sigpending` +#define __NR_sethostname 74 ///< System-call number for `sethostname` +#define __NR_setrlimit 75 ///< System-call number for `setrlimit` +#define __NR_getrlimit 76 ///< System-call number for `getrlimit` +#define __NR_getrusage 77 ///< System-call number for `getrusage` +#define __NR_gettimeofday 78 ///< System-call number for `gettimeofday` +#define __NR_settimeofday 79 ///< System-call number for `settimeofday` +#define __NR_getgroups 80 ///< System-call number for `getgroups` +#define __NR_setgroups 81 ///< System-call number for `setgroups` +#define __NR_symlink 83 ///< System-call number for `symlink` +#define __NR_lstat 84 ///< System-call number for `lstat` +#define __NR_readlink 85 ///< System-call number for `readlink` +#define __NR_uselib 86 ///< System-call number for `uselib` +#define __NR_swapon 87 ///< System-call number for `swapon` +#define __NR_reboot 88 ///< System-call number for `reboot` +#define __NR_readdir 89 ///< System-call number for `readdir` +#define __NR_mmap 90 ///< System-call number for `mmap` +#define __NR_munmap 91 ///< System-call number for `munmap` +#define __NR_truncate 92 ///< System-call number for `truncate` +#define __NR_ftruncate 93 ///< System-call number for `ftruncate` +#define __NR_fchmod 94 ///< System-call number for `fchmod` +#define __NR_fchown 95 ///< System-call number for `fchown` +#define __NR_getpriority 96 ///< System-call number for `getpriority` +#define __NR_setpriority 97 ///< System-call number for `setpriority` +#define __NR_statfs 99 ///< System-call number for `statfs` +#define __NR_fstatfs 100 ///< System-call number for `fstatfs` +#define __NR_ioperm 101 ///< System-call number for `ioperm` +#define __NR_socketcall 102 ///< System-call number for `socketcall` +#define __NR_syslog 103 ///< System-call number for `syslog` +#define __NR_setitimer 104 ///< System-call number for `setitimer` +#define __NR_getitimer 105 ///< System-call number for `getitimer` +#define __NR_newstat 106 ///< System-call number for `newstat` +#define __NR_newlstat 107 ///< System-call number for `newlstat` +#define __NR_newfstat 108 ///< System-call number for `newfstat` +#define __NR_uname 109 ///< System-call number for `uname` +#define __NR_iopl 110 ///< System-call number for `iopl` +#define __NR_vhangup 111 ///< System-call number for `vhangup` +#define __NR_idle 112 ///< System-call number for `idle` +#define __NR_vm86old 113 ///< System-call number for `vm86old` +#define __NR_wait4 114 ///< System-call number for `wait4` +#define __NR_swapoff 115 ///< System-call number for `swapoff` +#define __NR_sysinfo 116 ///< System-call number for `sysinfo` +#define __NR_ipc 117 ///< System-call number for `ipc` +#define __NR_fsync 118 ///< System-call number for `fsync` +#define __NR_sigreturn 119 ///< System-call number for `sigreturn` +#define __NR_clone 120 ///< System-call number for `clone` +#define __NR_setdomainname 121 ///< System-call number for `setdomainname` +#define __NR_newuname 122 ///< System-call number for `newuname` +#define __NR_modify_ldt 123 ///< System-call number for `modify_ldt` +#define __NR_adjtimex 124 ///< System-call number for `adjtimex` +#define __NR_mprotect 125 ///< System-call number for `mprotect` +#define __NR_sigprocmask 126 ///< System-call number for `sigprocmask` +#define __NR_create_module 127 ///< System-call number for `create_module` +#define __NR_init_module 128 ///< System-call number for `init_module` +#define __NR_delete_module 129 ///< System-call number for `delete_module` +#define __NR_get_kernel_syms 130 ///< System-call number for `get_kernel_syms` +#define __NR_quotactl 131 ///< System-call number for `quotactl` +#define __NR_getpgid 132 ///< System-call number for `getpgid` +#define __NR_fchdir 133 ///< System-call number for `fchdir` +#define __NR_bdflush 134 ///< System-call number for `bdflush` +#define __NR_sysfs 135 ///< System-call number for `sysfs` +#define __NR_personality 136 ///< System-call number for `personality` +#define __NR_setfsuid 138 ///< System-call number for `setfsuid` +#define __NR_setfsgid 139 ///< System-call number for `setfsgid` +#define __NR_llseek 140 ///< System-call number for `llseek` +#define __NR_getdents 141 ///< System-call number for `getdents` +#define __NR_select 142 ///< System-call number for `select` +#define __NR_flock 143 ///< System-call number for `flock` +#define __NR_msync 144 ///< System-call number for `msync` +#define __NR_readv 145 ///< System-call number for `readv` +#define __NR_writev 146 ///< System-call number for `writev` +#define __NR_getsid 147 ///< System-call number for `getsid` +#define __NR_fdatasync 148 ///< System-call number for `fdatasync` +#define __NR_sysctl 149 ///< System-call number for `sysctl` +#define __NR_mlock 150 ///< System-call number for `mlock` +#define __NR_munlock 151 ///< System-call number for `munlock` +#define __NR_mlockall 152 ///< System-call number for `mlockall` +#define __NR_munlockall 153 ///< System-call number for `munlockall` +#define __NR_sched_setparam 154 ///< System-call number for `sched_setparam` +#define __NR_sched_getparam 155 ///< System-call number for `sched_getparam` +#define __NR_sched_setscheduler 156 ///< System-call number for `sched_setscheduler` +#define __NR_sched_getscheduler 157 ///< System-call number for `sched_getscheduler` +#define __NR_sched_yield 158 ///< System-call number for `sched_yield` +#define __NR_sched_get_priority_max 159 ///< System-call number for `sched_get_priority_max` +#define __NR_sched_get_priority_min 160 ///< System-call number for `sched_get_priority_min` +#define __NR_sched_rr_get_interval 161 ///< System-call number for `sched_rr_get_interval` +#define __NR_nanosleep 162 ///< System-call number for `nanosleep` +#define __NR_mremap 163 ///< System-call number for `mremap` +#define __NR_setresuid 164 ///< System-call number for `setresuid` +#define __NR_getresuid 165 ///< System-call number for `getresuid` +#define __NR_vm86 166 ///< System-call number for `vm86` +#define __NR_query_module 167 ///< System-call number for `query_module` +#define __NR_poll 168 ///< System-call number for `poll` +#define __NR_nfsservctl 169 ///< System-call number for `nfsservctl` +#define __NR_setresgid 170 ///< System-call number for `setresgid` +#define __NR_getresgid 171 ///< System-call number for `getresgid` +#define __NR_prctl 172 ///< System-call number for `prctl` +#define __NR_rt_sigreturn 173 ///< System-call number for `rt_sigreturn` +#define __NR_rt_sigaction 174 ///< System-call number for `rt_sigaction` +#define __NR_rt_sigprocmask 175 ///< System-call number for `rt_sigprocmask` +#define __NR_rt_sigpending 176 ///< System-call number for `rt_sigpending` +#define __NR_rt_sigtimedwait 177 ///< System-call number for `rt_sigtimedwait` +#define __NR_rt_sigqueueinfo 178 ///< System-call number for `rt_sigqueueinfo` +#define __NR_rt_sigsuspend 179 ///< System-call number for `rt_sigsuspend` +#define __NR_pread 180 ///< System-call number for `pread` +#define __NR_pwrite 181 ///< System-call number for `pwrite` +#define __NR_chown 182 ///< System-call number for `chown` +#define __NR_getcwd 183 ///< System-call number for `getcwd` +#define __NR_capget 184 ///< System-call number for `capget` +#define __NR_capset 185 ///< System-call number for `capset` +#define __NR_sigaltstack 186 ///< System-call number for `sigaltstack` +#define __NR_sendfile 187 ///< System-call number for `sendfile` +#define __NR_waitperiod 188 ///< System-call number for `waitperiod` +#define __NR_msgctl 189 ///< System-call number for `msgctl` +#define __NR_msgget 190 ///< System-call number for `msgget` +#define __NR_msgrcv 191 ///< System-call number for `msgrcv` +#define __NR_msgsnd 192 ///< System-call number for `msgsnd` +#define __NR_semctl 193 ///< System-call number for `semctl` +#define __NR_semget 194 ///< System-call number for `semget` +#define __NR_semop 195 ///< System-call number for `semop` +#define __NR_shmat 196 ///< System-call number for `shmat` +#define __NR_shmctl 197 ///< System-call number for `shmctl` +#define __NR_shmdt 198 ///< System-call number for `shmdt` +#define __NR_shmget 199 ///< System-call number for `shmget` +#define SYSCALL_NUMBER 200 ///< The total number of system-calls. + +/// @brief Handle the value returned from a system call. +/// @param type Specifies the type of the returned value. +/// @param res The name of the variable where the result of the SC is stored. +#define __syscall_return(type, res) \ + do { \ + if ((unsigned int)(res) >= (unsigned int)(-125)) { \ + errno = -(res); \ + (res) = -1; \ + } \ + return (type)(res); \ + } while (0) + +/// @brief Heart of the code that calls a system call with 0 parameters. +#define __inline_syscall0(res, name) \ + __asm__ __volatile__("int $0x80" \ + : "=a"(res) \ + : "0"(__NR_##name)) + +/// @brief Heart of the code that calls a system call with 1 parameter. +#define __inline_syscall1(res, name, arg1) \ + __asm__ __volatile__("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \ + : "=a"(res) \ + : "0"(__NR_##name), "ri"((int)(arg1)) \ + : "memory"); + +/// @brief Heart of the code that calls a system call with 2 parameters. +#define __inline_syscall2(res, name, arg1, arg2) \ + __asm__ __volatile__("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \ + : "=a"(res) \ + : "0"(__NR_##name), "ri"((int)(arg1)), "c"((int)(arg2)) \ + : "memory"); + +/// @brief Heart of the code that calls a system call with 3 parameters. +#define __inline_syscall3(res, name, arg1, arg2, arg3) \ + __asm__ __volatile__("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \ + : "=a"(res) \ + : "0"(__NR_##name), "ri"((int)(arg1)), "c"((int)(arg2)), \ + "d"((int)(arg3)) \ + : "memory"); + +/// @brief Heart of the code that calls a system call with 4 parameters. +#define __inline_syscall4(res, name, arg1, arg2, arg3, arg4) \ + __asm__ __volatile__("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx" \ + : "=a"(res) \ + : "0"(__NR_##name), "ri"((int)(arg1)), "c"((int)(arg2)), \ + "d"((int)(arg3)), "S"((int)(arg4)) \ + : "memory"); + +/// @brief Heart of the code that calls a system call with 5 parameters. +#define __inline_syscall5(res, name, arg1, arg2, arg3, arg4, arg5) \ + __asm__ __volatile__("push %%ebx ; movl %2,%%ebx ; movl %1,%%eax ; " \ + "int $0x80 ; pop %%ebx" \ + : "=a"(res) \ + : "i"(__NR_##name), "ri"((int)(arg1)), "c"((int)(arg2)), \ + "d"((int)(arg3)), "S"((int)(arg4)), "D"((int)(arg5)) \ + : "memory"); + +/// @brief System call with 0 parameters. +#define _syscall0(type, name) \ + type name(void) \ + { \ + long __res; \ + __inline_syscall0(__res, name); \ + __syscall_return(type, __res); \ + } + +/// @brief System call with 1 parameter. +#define _syscall1(type, name, type1, arg1) \ + type name(type1 arg1) \ + { \ + long __res; \ + __inline_syscall1(__res, name, arg1); \ + __syscall_return(type, __res); \ + } + +/// @brief System call with 2 parameters. +#define _syscall2(type, name, type1, arg1, type2, arg2) \ + type name(type1 arg1, type2 arg2) \ + { \ + long __res; \ + __inline_syscall2(__res, name, arg1, arg2); \ + __syscall_return(type, __res); \ + } + +/// @brief System call with 3 parameters. +#define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type name(type1 arg1, type2 arg2, type3 arg3) \ + { \ + long __res; \ + __inline_syscall3(__res, name, arg1, arg2, arg3); \ + __syscall_return(type, __res); \ + } + +/// @brief System call with 4 parameters. +#define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4) \ + type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \ + { \ + long __res; \ + __inline_syscall4(__res, name, arg1, arg2, arg3, arg4); \ + __syscall_return(type, __res); \ + } + +/// @brief System call with 5 parameters. +#define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, type4, arg4, type5, arg5) \ + type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \ + { \ + long __res; \ + __inline_syscall5(__res, name, arg1, arg2, arg3, arg4, arg5); \ + __syscall_return(type, __res); \ + } diff --git a/libc/inc/termios.h b/libc/inc/termios.h new file mode 100644 index 0000000..c573a31 --- /dev/null +++ b/libc/inc/termios.h @@ -0,0 +1,22 @@ +/// MentOS, The Mentoring Operating system project +/// @file termios.h +/// @brief Defines the termios functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "bits/termios-struct.h" + +/// @brief Put the state of FD into *TERMIOS_P. +/// @param fd +/// @param termios_p +/// @return +extern int tcgetattr(int fd, termios *termios_p); + +/// @brief Set the state of FD to *TERMIOS_P. +/// @param fd +/// @param optional_actions +/// @param termios_p +/// @return +extern int tcsetattr(int fd, int optional_actions, const termios *termios_p); diff --git a/libc/inc/time.h b/libc/inc/time.h new file mode 100644 index 0000000..b7e8072 --- /dev/null +++ b/libc/inc/time.h @@ -0,0 +1,110 @@ +/// MentOS, The Mentoring Operating system project +/// @file time.h +/// @brief Time-related functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stddef.h" + +/// Domains of interval timers +#define ITIMER_REAL 0 +#define ITIMER_VIRTUAL 1 +#define ITIMER_PROF 2 + +/// Used to store time values. +typedef unsigned int time_t; + +/// Used to get information about the current time. +typedef struct tm_t { + /// Seconds [0 to 59] + int tm_sec; + /// Minutes [0 to 59] + int tm_min; + /// Hours [0 to 23] + int tm_hour; + /// Day of the month [1 to 31] + int tm_mday; + /// Month [0 to 11] + int tm_mon; + /// Year [since 1900] + int tm_year; + /// Day of the week [0 to 6] + int tm_wday; + /// Day in the year [0 to 365] + int tm_yday; + /// Is Daylight Saving Time. + int tm_isdst; +} tm_t; + +/// Rappresents time +typedef struct _timeval { + time_t tv_sec; /* seconds */ + time_t tv_usec; /* microseconds */ +} timeval; + +/// Rappresents a time interval +typedef struct _itimerval { + timeval it_interval; /* next value */ + timeval it_value; /* current value */ +} itimerval; + +/// @brief Specify intervals of time with nanosecond precision. +typedef struct timespec { + time_t tv_sec; ///< Seconds. + long tv_nsec; ///< Nanoseconds. +} timespec; + +/// @brief Returns the current time. +/// @param t Where the time should be stored. +/// @return The current time. +time_t time(time_t *t); + +/// @brief Return the difference between the two time values. +/// @param time1 The first time value. +/// @param time2 The second time value. +/// @return The difference in terms of seconds. +time_t difftime(time_t time1, time_t time2); + +/// @brief The current time broken down into a tm_t structure. +/// @param timep A pointer to a variable holding the current time. +/// @return The time broken down. +tm_t *localtime(const time_t *timep); + +/// @brief Formats the time tm according to the format specification format +/// and places the result in the character array s of size max. +/// @param s The destination buffer. +/// @param max The maximum length of the buffer. +/// @param format The buffer used to generate the time. +/// @param tm The broken-down time. +/// @return The number of bytes (excluding the terminating null) placed in s. +size_t strftime(char *s, size_t max, const char *format, const tm_t *tm); + +/// @brief Suspends the execution of the calling thread. +/// @param req The amount of time we want to sleep. +/// @param rem The remaining time we did not sleep. +/// @return If the call is interrupted by a signal handler, nanosleep() +/// returns -1, sets errno to EINTR, and writes the remaining time +/// into the structure pointed to by rem unless rem is NULL. +/// @details +/// The execution is suspended until either at least the time specified +/// in *req has elapsed, or the delivery of a signal that triggers the +/// invocation of a handler in the calling thread or that terminates +/// the process. +int nanosleep(const timespec *req, timespec *rem); + +/// @brief Causes the calling thread to sleep either until the number of +/// real-time seconds specified in seconds have elapsed or +/// until a signal arrives which is not ignored. +/// @param seconds The number of seconds we want to sleep. +/// @return Zero if the requested time has elapsed, or the number of seconds +/// left to sleep, if the call was interrupted by a signal handler. +unsigned int sleep(unsigned int seconds); + +/// @brief Fills the structure pointed to by curr_value with the current setting for the timer specified by which. +int getitimer(int which, itimerval *curr_value); + +/// @brief The system provides each process with three interval timers, each decrementing in a distinct time domain. +/// When any timer expires, a signal is sent to the process, and the timer (potentially) restarts. +int setitimer(int which, const itimerval *new_value, itimerval *old_value); diff --git a/libc/src/abort.c b/libc/src/abort.c new file mode 100644 index 0000000..5b1181b --- /dev/null +++ b/libc/src/abort.c @@ -0,0 +1,81 @@ +/// MentOS, The Mentoring Operating system project +/// @file abort.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "string.h" +#include "signal.h" +#include "stdio.h" + +// Since there could be signal handlers listening for the abort, we +// need to keep track at which stage of the abort we are. +static int stage = 0; + +void abort(void) +{ + sigaction_t action; + sigset_t sigset; + + /* Unblock SIGABRT. */ + if (stage == 0) { + ++stage; + + sigemptyset(&sigset); + sigaddset(&sigset, SIGABRT); + sigprocmask(SIG_UNBLOCK, &sigset, 0); + } + + /* Send signal which possibly calls a user handler. */ + if (stage == 1) { + + // We must allow recursive calls of abort + int save_stage = stage; + + stage = 0; + kill(getpid(), SIGABRT); + stage = save_stage + 1; + } + + /* There was a handler installed. Now remove it. */ + if (stage == 2) { + ++stage; + + memset(&action, 0, sizeof(action)); + + action.sa_handler = SIG_DFL; + sigemptyset(&action.sa_mask); + action.sa_flags = 0; + + sigaction(SIGABRT, &action, NULL); + } + + /* Try again. */ + if (stage == 3) { + ++stage; + + kill(getpid(), SIGABRT); + } + + /* Now try to abort using the system specific command. */ + if (stage == 4) { + ++stage; + + __asm__ __volatile__("hlt"); + } + + /* If we can't signal ourselves and the abort instruction failed, exit. */ + if (stage == 5) { + ++stage; + + exit(127); + } + + // If even this fails try to use the provided instruction to crash + // or otherwise make sure we never return. +#pragma clang diagnostic push +#pragma ide diagnostic ignored "EndlessLoop" + while (1) __asm__ __volatile__("hlt"); +#pragma clang diagnostic pop +} diff --git a/libc/src/assert.c b/libc/src/assert.c new file mode 100644 index 0000000..5ceba4f --- /dev/null +++ b/libc/src/assert.c @@ -0,0 +1,19 @@ +/// MentOS, The Mentoring Operating system project +/// @file assert.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "assert.h" +#include "stdlib.h" +#include "stdio.h" + +void __assert_fail(const char *assertion, const char *file, const char *function, unsigned int line) +{ + printf("FILE: %s\n" + "FUNC: %s\n" + "LINE: %d\n\n" + "Assertion `%s` failed.\n", + file, (function ? function : "NO_FUN"), line, assertion); + abort(); +} diff --git a/libc/src/crt0.S b/libc/src/crt0.S new file mode 100644 index 0000000..665b352 --- /dev/null +++ b/libc/src/crt0.S @@ -0,0 +1,15 @@ +extern main +extern __libc_start_main +global _start + +; ----------------------------------------------------------------------------- +; SECTION (text) +; ----------------------------------------------------------------------------- +section .text +_start: ; _start is the entry point known to the linker + mov ebp, 0 ; Set ebp to 0 as x86 programs require + push main ; Push the pointer to `main` to the stack. + call __libc_start_main ; Call the libc initialization function. + mov ebx, eax ; Move `main` return value to ebx. + mov eax, 1 ; Call the `exit` function by using `int 80` (i.e., a system call) + int 0x80 diff --git a/libc/src/ctype.c b/libc/src/ctype.c new file mode 100644 index 0000000..88ce35f --- /dev/null +++ b/libc/src/ctype.c @@ -0,0 +1,63 @@ +/// MentOS, The Mentoring Operating system project +/// @file ctype.c +/// @brief Functions related to character handling. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "ctype.h" + +/// Distance from a uppercase character to the correspondent lowercase in ASCII. +#define OFFSET 32 + +int isdigit(int c) +{ + return (c >= 48 && c <= 57); +} + +int isalpha(int c) +{ + return ((c >= 65 && c <= 90) || (c >= 97 && c <= 122)); +} + +int isalnum(int c) +{ + return (isalpha(c) || isdigit(c)); +} + +int isxdigit(int c) +{ + return (isdigit(c) || (c >= 65 && c <= 70)); +} + +int islower(int c) +{ + return (c >= 97 && c <= 122); +} + +int isupper(int c) +{ + return (c >= 65 && c <= 90); +} + +int tolower(int c) +{ + if (isalpha(c) == 0 || islower(c)) { + return c; + } + + return c + OFFSET; +} + +int toupper(int c) +{ + if (isalpha(c) == 0 || isupper(c)) { + return c; + } + + return c - OFFSET; +} + +int isspace(int c) +{ + return (c == ' '); +} diff --git a/libc/src/debug.c b/libc/src/debug.c new file mode 100644 index 0000000..84ab2f5 --- /dev/null +++ b/libc/src/debug.c @@ -0,0 +1,105 @@ +/// MentOS, The Mentoring Operating system project +/// @file debug.c +/// @brief Debugging primitives. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include + +#include +#include +#include +#include +#include + +/// Serial port for QEMU. +#define SERIAL_COM1 (0x03F8) + +void dbg_putchar(char c) +{ +#if (defined(DEBUG_STDIO) || defined(DEBUG_LOG)) + outportb(SERIAL_COM1, (uint8_t)c); +#endif +} + +void dbg_puts(const char *s) +{ +#if (defined(DEBUG_STDIO) || defined(DEBUG_LOG)) + while ((*s) != 0) + dbg_putchar(*s++); +#endif +} + +static inline void __debug_print_header(const char *file, const char *fun, int line) +{ + static char tmp_prefix[BUFSIZ], final_prefix[BUFSIZ]; + dbg_puts("[ LB |"); + sprintf(tmp_prefix, "%s:%d", file, line); + sprintf(final_prefix, " %-20s ", tmp_prefix); + dbg_puts(final_prefix); + dbg_putchar('|'); + sprintf(final_prefix, " %-25s ] ", fun); + dbg_puts(final_prefix); +} + +void dbg_printf(const char *file, const char *fun, int line, const char *format, ...) +{ + // Define a buffer for the formatted string. + static char formatted[BUFSIZ]; + static short new_line = 1; + + // Stage 1: FORMAT + if (strlen(format) >= BUFSIZ) + return; + + // Start variabile argument's list. + va_list ap; + va_start(ap, format); + // Format the message. + vsprintf(formatted, format, ap); + // End the list of arguments. + va_end(ap); + + // Stage 2: SEND + if (new_line) { + __debug_print_header(file, fun, line); + new_line = 0; + } + for (size_t it = 0; (formatted[it] != 0) && (it < BUFSIZ); ++it) { + dbg_putchar(formatted[it]); + if (formatted[it] != '\n') { + continue; + } + if ((it + 1) >= BUFSIZ) { + continue; + } + if (formatted[it + 1] == 0) { + new_line = 1; + } else { + __debug_print_header(file, fun, line); + } + } +} + +const char *to_human_size(unsigned long bytes) +{ + static char output[200]; + const char *suffix[] = { "B", "KB", "MB", "GB", "TB" }; + char length = sizeof(suffix) / sizeof(suffix[0]); + int i = 0; + double dblBytes = bytes; + if (bytes > 1024) { + for (i = 0; (bytes / 1024) > 0 && i < length - 1; i++, bytes /= 1024) + dblBytes = bytes / 1024.0; + } + sprintf(output, "%.03lf %2s", dblBytes, suffix[i]); + return output; +} + +const char *dec_to_binary(unsigned long value) +{ + static char buffer[33]; + for (int i = 0; i < 32; ++i) + buffer[i] = bit_check(value, 31 - i) ? '1' : '0'; + return buffer; +} \ No newline at end of file diff --git a/libc/src/fcvt.c b/libc/src/fcvt.c new file mode 100644 index 0000000..750f564 --- /dev/null +++ b/libc/src/fcvt.c @@ -0,0 +1,104 @@ +/// MentOS, The Mentoring Operating system project +/// @file fcvt.c +/// @brief Define the functions required to turn double values into a string. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "fcvt.h" +#include "math.h" + +static void cvt(double arg, int ndigits, int *decpt, int *sign, char *buf, unsigned buf_size, int eflag) +{ + int r2; + double fi, fj; + char *p, *p1; + + char *buf_end = (buf + buf_size); + + if (ndigits < 0) { + ndigits = 0; + } + + if (ndigits >= buf_size - 1) { + ndigits = buf_size - 2; + } + + r2 = 0; + *sign = 0; + p = &buf[0]; + + if (arg < 0) { + *sign = 1; + arg = -arg; + } + + arg = modf(arg, &fi); + p1 = buf_end; + + if (fi != 0) { + p1 = buf_end; + while (fi != 0) { + fj = modf(fi / 10, &fi); + *--p1 = (int)((fj + .03) * 10) + '0'; + r2++; + } + while (p1 < buf_end) { + *p++ = *p1++; + } + } else if (arg > 0) { + while ((fj = arg * 10) < 1) { + arg = fj; + r2--; + } + } + + p1 = &buf[ndigits]; + if (eflag == 0) { + p1 += r2; + } + + *decpt = r2; + if (p1 < &buf[0]) { + buf[0] = '\0'; + return; + } + + while (p <= p1 && p < buf_end) { + arg *= 10; + arg = modf(arg, &fj); + *p++ = (int)fj + '0'; + } + + if (p1 >= buf_end) { + buf[buf_size - 1] = '\0'; + return; + } + + p = p1; + *p1 += 5; + while (*p1 > '9') { + *p1 = '0'; + if (p1 > buf) { + ++*--p1; + } else { + *p1 = '1'; + (*decpt)++; + if (eflag == 0) { + if (p > buf) + *p = '0'; + p++; + } + } + } + *p = '\0'; +} + +void ecvtbuf(double arg, int chars, int *decpt, int *sign, char *buf, unsigned buf_size) +{ + cvt(arg, chars, decpt, sign, buf, buf_size, 1); +} + +void fcvtbuf(double arg, int decimals, int *decpt, int *sign, char *buf, unsigned buf_size) +{ + cvt(arg, decimals, decpt, sign, buf, buf_size, 0); +} diff --git a/libc/src/grp.c b/libc/src/grp.c new file mode 100644 index 0000000..41c97f0 --- /dev/null +++ b/libc/src/grp.c @@ -0,0 +1,238 @@ +/// MentOS, The Mentoring Operating system project +/// @file grp.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "grp.h" +#include "sys/unistd.h" +#include "sys/errno.h" +#include "assert.h" +#include "string.h" +#include "stdio.h" +#include "debug.h" +#include "fcntl.h" + + +static inline void __parse_line(struct group* grp, char *buf) +{ + assert(grp && "Received null grp!"); + char *token; + // Parse the group name. + if ((token = strtok(buf, ":")) != NULL) + grp->gr_name = token; + // Parse the group passwd. + if ((token = strtok(NULL, ":")) != NULL) + grp->gr_passwd = token; + // Parse the group id. + if ((token = strtok(NULL, ":")) != NULL) + grp->gr_gid = atoi(token); + + size_t found_users = 0; + while((token = strtok(NULL, ",\n\0")) != NULL && found_users < MAX_MEMBERS_PER_GROUP) { + grp->gr_mem[found_users] = token; + found_users += 1; + } + + // Null terminate array + grp->gr_mem[found_users] = "\0"; +} + +static inline char *__search_entry(int fd, char *buf, int buflen, const char *name, gid_t gid) +{ + int ret; + char c; + int pos = 0; + while ((ret = read(fd, &c, 1U))) { + // Skip carriage return. + if (c == '\r') + continue; + if (pos >= buflen) { + errno = ERANGE; + return NULL; + } + // If we have found a newline or the EOF, parse the entry. + if ((c == '\n') || (ret == EOF)) { + // Close the buffer. + buf[pos] = 0; + // Check the entry. + if (name) { + if (strncmp(buf, name, strlen(name)) == 0) + return buf; + } else { + int gid_start = -1, col_count = 0; + for (int i = 0; i < pos; ++i) { + if (buf[i] == ':') { + if (++col_count == 2) { + gid_start = i + 1; + break; + } + } + } + if ((gid_start != -1) && (gid_start < pos)) { + // Parse the gid. + int found_gid = atoi(&buf[gid_start]); + // Check the gid. + if (found_gid == gid) + return buf; + } + } + // Reset the index. + pos = 0; + // If we have reached the EOF stop. + if (ret == EOF) + break; + } else { + buf[pos++] = c; + } + } + errno = ENOENT; + return NULL; +} + +struct group* getgrgid(gid_t gid) { + + static group grp; + static char buffer[BUFSIZ]; + + group *result; + if (!getgrgid_r(gid, &grp, buffer, BUFSIZ, &result)) + return NULL; + + return &grp; +} + +struct group *getgrnam(const char* name) { + + if (name == NULL) + return NULL; + + static group grp; + static char buffer[BUFSIZ]; + + group *result; + if (!getgrnam_r(name, &grp, buffer, BUFSIZ, &result)) + return NULL; + + return &grp; +} + +int getgrgid_r(gid_t gid, struct group* group, char* buf, size_t buflen, struct group ** result) { + + int fd = open("/etc/group", O_RDONLY, 0); + if (fd == -1) { + errno = ENOENT; + *result = NULL; + return 0; + } + + char *entry = __search_entry(fd, buf, buflen, NULL, gid); + if (entry != NULL) { + + // Close the file. + close(fd); + // Parse the line. + __parse_line(group, entry); + // Return success. + return 1; + } + + // Close the file. + close(fd); + // Return fail. + return 0; +} + +int getgrnam_r(const char* name, struct group* group, char* buf, size_t buflen, struct group** result) { + + int fd = open("/etc/group", O_RDONLY, 0); + if (fd == -1) { + errno = ENOENT; + *result = NULL; + return 0; + } + + char *entry = __search_entry(fd, buf, buflen, name, 0); + if (entry != NULL) { + + // Close the file. + close(fd); + // Parse the line. + __parse_line(group, entry); + // Return success. + return 1; + } + + // Close the file. + close(fd); + // Return fail. + return 0; +} + +static int fd = -1; +struct group* getgrent(void) { + + static group result; + + if (fd == -1) { + //pr_debug("Opening group file\n"); + fd = open("/etc/group", O_RDONLY, 0); + if (fd == -1) { + errno = ENOENT; + return 0; + } + } + + int ret; + char c; + int pos = 0; + + static char buffer[BUFSIZ]; + while ((ret = read(fd, &c, 1U))) { + + // Skip carriage return. + if (c == '\r') + continue; + + if (pos >= BUFSIZ) { + errno = ERANGE; + return NULL; + } + + // If we have found a newline or the EOF, parse the entry. + if ((c == '\n') || (ret == EOF)) { + // Close the buffer. + buffer[pos] = 0; + + // Check the entry. + if (strlen(buffer) != 0) { + //pr_debug("Found entry in group file: %s\n", buffer); + __parse_line(&result, buffer); + return &result; + } + + // If we have reached the EOF stop. + if (ret == EOF) + break; + + } else { + buffer[pos++] = c; + } + } + + errno = ENOENT; + return NULL; +} + +void endgrent(void) { + + //pr_debug("Closing group file\n"); + close(fd); + fd = -1; +} + +void setgrent(void) { + + //pr_debug("Resetting pointer to beginning of group file\n"); + lseek(fd, 0, SEEK_SET); +} \ No newline at end of file diff --git a/libc/src/io/mm_io.c b/libc/src/io/mm_io.c new file mode 100644 index 0000000..5bcdaa2 --- /dev/null +++ b/libc/src/io/mm_io.c @@ -0,0 +1,37 @@ +/// MentOS, The Mentoring Operating system project +/// @file mm_io.c +/// @brief Memory Mapped IO functions implementation. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "io/mm_io.h" + +uint8_t in_memb(uint32_t addr) +{ + return *((uint8_t *) (addr)); +} + +uint16_t in_mems(uint32_t addr) +{ + return *((uint16_t *) (addr)); +} + +uint32_t in_meml(uint32_t addr) +{ + return *((uint32_t *) (addr)); +} + +void out_memb(uint32_t addr, uint8_t value) +{ + (*((uint8_t *) (addr))) = (value); +} + +void out_mems(uint32_t addr, uint16_t value) +{ + (*((uint16_t *) (addr))) = (value); +} + +void out_meml(uint32_t addr, uint32_t value) +{ + (*((uint32_t *) (addr))) = (value); +} diff --git a/libc/src/io/port_io.c b/libc/src/io/port_io.c new file mode 100644 index 0000000..ebe5072 --- /dev/null +++ b/libc/src/io/port_io.c @@ -0,0 +1,59 @@ +/// MentOS, The Mentoring Operating system project +/// @file port_io.c +/// @brief Byte I/O on ports prototypes. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "io/port_io.h" + +inline uint8_t inportb(uint16_t port) +{ + unsigned char data = 0; + __asm__ __volatile__("inb %%dx, %%al" : "=a"(data) : "d"(port)); + + return data; +} + +inline uint16_t inports(uint16_t port) +{ + uint16_t rv; + __asm__ __volatile__("inw %1, %0" : "=a"(rv) : "dN"(port)); + + return rv; +} + +void inportsm(uint16_t port, uint8_t *data, unsigned long size) +{ + __asm__ __volatile__("rep insw" + : "+D"(data), "+c"(size) + : "d"(port) + : "memory"); +} + +inline uint32_t inportl(uint16_t port) +{ + uint32_t rv; + __asm__ __volatile__("inl %%dx, %%eax" : "=a"(rv) : "dN"(port)); + + return rv; +} + +inline void outportb(uint16_t port, uint8_t data) +{ + __asm__ __volatile__("outb %%al, %%dx" ::"a"(data), "d"(port)); +} + +inline void outports(uint16_t port, uint16_t data) +{ + __asm__ __volatile__("outw %1, %0" : : "dN"(port), "a"(data)); +} + +void outportsm(uint16_t port, uint8_t *data, uint16_t size) +{ + asm volatile("rep outsw" : "+S"(data), "+c"(size) : "d"(port)); +} + +inline void outportl(uint16_t port, uint32_t data) +{ + __asm__ __volatile__("outl %%eax, %%dx" : : "dN"(port), "a"(data)); +} diff --git a/libc/src/ipc/ipc.c b/libc/src/ipc/ipc.c new file mode 100644 index 0000000..eb8f082 --- /dev/null +++ b/libc/src/ipc/ipc.c @@ -0,0 +1,36 @@ +/// MentOS, The Mentoring Operating system project +/// @file ipc.c +/// @brief Inter-Process Communication (IPC) system call implementation. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" +#include "stddef.h" +#include "ipc/sem.h" +#include "ipc/shm.h" +#include "ipc/msg.h" + +_syscall3(long, shmat, int, shmid, char *, shmaddr, int, shmflg) + +_syscall3(long, shmget, key_t, key, size_t, size, int, flag) + +_syscall1(long, shmdt, char *, shmaddr) + +_syscall3(long, shmctl, int, shmid, int, cmd, struct shmid_ds *, buf) + +_syscall3(long, semget, key_t, key, int, nsems, int, semflg) + +_syscall3(long, semop, int, semid, struct sembuf *, sops, unsigned, nsops) + +_syscall4(long, semctl, int, semid, int, semnum, int, cmd, unsigned long, arg) + +_syscall2(long, msgget, key_t, key, int, msgflg) + +_syscall4(long, msgsnd, int, msqid, struct msgbuf *, msgp, size_t, msgsz, int, msgflg) + +_syscall5(long, msgrcv, int, msqid, struct msgbuf *, msgp, size_t, msgsz, long, msgtyp, int, msgflg) + +_syscall3(long, msgctl, int, msqid, int, cmd, struct msqid_ds *, buf) + diff --git a/libc/src/libc_start.c b/libc/src/libc_start.c new file mode 100644 index 0000000..87ddc00 --- /dev/null +++ b/libc/src/libc_start.c @@ -0,0 +1,40 @@ +/// @file libc_start.c +/// @brief Contains the programs initialization procedure. + +#include "sys/unistd.h" +#include "stdlib.h" +#include "string.h" +#include "assert.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +/// @brief Reference to the `environ` variable in `setenv.c`. +extern char **environ; + +/// @brief The entry point to every program. +/// @param main Pointer to the main function. +/// @param argc The number of arguments. +/// @param argv The pointer to the arguments. +/// @param envp The pointer to the environmental variables. +/// @return The return of the `main` function. +int __libc_start_main(int (*main)(int, char **, char **), int argc, char *argv[], char *envp[]) +{ + //dbg_print("== START %-30s =======================================\n", argv[0]); + //dbg_print("__libc_start_main(%p, %d, %p, %p)\n", main, argc, argv, envp); + assert(main && "There is no `main` function."); + assert(argv && "There is no `argv` array."); + assert(envp && "There is no `envp` array."); + //dbg_print("environ : %p\n", environ); + // Copy the environ. + environ = envp; + // Call the main function. + int result = main(argc, argv, envp); + // Free the environ. + //dbg_print("== END %-30s =======================================\n", argv[0]); + return result; +} + +// WARNING: This declaration must be here, because libc_start is compiled +// with all the programs, and all the programs NEED to have the `sigreturn` +// symbol. It must be this way, period. +_syscall0(int, sigreturn) diff --git a/libc/src/libgen.c b/libc/src/libgen.c new file mode 100644 index 0000000..50d03b5 --- /dev/null +++ b/libc/src/libgen.c @@ -0,0 +1,141 @@ +/// MentOS, The Mentoring Operating system project +/// @file libgen.c +/// @brief String routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include "libgen.h" +#include "string.h" +#include "limits.h" + +int parse_path(char *out, char **cur, char sep, size_t max) +{ + if (**cur == '\0') { + return 0; + } + + *out++ = **cur; + ++*cur; + --max; + + while ((max > 0) && (**cur != '\0') && (**cur != sep)) { + *out++ = **cur; + ++*cur; + --max; + } + + *out = '\0'; + + return 1; +} + +char *dirname(const char *path) +{ + static char s[PATH_MAX]; + + static char dot[2] = "."; + + // Check the input path. + if (path == NULL) { + return dot; + } + + // Copy the path to the support string. + strcpy(s, path); + + // Get the last occurrence of '/'. + char *last_slash = strrchr(s, '/'); + + if (last_slash == s) { + // If the slash is acutally the first character of the string, move the + // pointer to the last slash after it. + ++last_slash; + } else if ((last_slash != NULL) && (last_slash[1] == '\0')) { + // If the slash is the last character, we need to search before it. + last_slash = memchr(s, '/', last_slash - s); + } + + if (last_slash != NULL) { + // If we have found it, close the string. + last_slash[0] = '\0'; + } else { + // Otherwise, return '.'. + return dot; + } + + return s; +} + +char *basename(const char *path) +{ + char *p = strrchr(path, '/'); + + return p ? p + 1 : (char *)path; +} + +char *realpath(const char *path, char *resolved) +{ + assert(path && "Provided null path."); + if (resolved == NULL) + resolved = malloc(sizeof(char) * PATH_MAX); + char abspath[PATH_MAX]; + // Initialize the absolute path. + memset(abspath, '\0', PATH_MAX); + int remaining; + if (path[0] != '/') { + // Get the current task. + getcwd(abspath, PATH_MAX); + // Check the current task. + assert((strlen(abspath) > 0) && "There is no current task."); + // Check that the current working directory is an absolute path. + assert((abspath[0] == '/') && "Current working directory is not an absolute path."); + // Count the remaining space in the absolute path. + remaining = PATH_MAX - strlen(abspath) - 1; + // Add the separator to the end (se strncat for safety). + strncat(abspath, "/", remaining); + // Set the number of characters that should be copied, + // based on the current absolute path. + remaining = PATH_MAX - strlen(abspath) - 1; + // Append the path. + strncat(abspath, path, remaining); + } else { + // Copy the path into the absolute path. + strncpy(abspath, path, PATH_MAX - 1); + } + // Count the remaining space in the absolute path. + remaining = PATH_MAX - strlen(abspath) - 1; + // Add the separator to the end (se strncat for safety). + strncat(abspath, "/", remaining); + + int absidx = 0, pathidx = 0; + + while (abspath[absidx]) { + // Skip multiple consecutive / characters + if (!strncmp("//", abspath + absidx, 2)) { + absidx++; + } + // Go to previous directory if /../ is found + else if (!strncmp("/../", abspath + absidx, 4)) { + // Go to a valid path character (pathidx points to the next one) + if (pathidx) + pathidx--; + while (pathidx && resolved[pathidx] != '/') { + pathidx--; + } + absidx += 3; + } else if (!strncmp("/./", abspath + absidx, 3)) { + absidx += 2; + } else { + resolved[pathidx++] = abspath[absidx++]; + } + } + // Remove the last / + if (pathidx > 1) + resolved[pathidx - 1] = '\0'; + else + resolved[1] = '\0'; + return resolved; +} \ No newline at end of file diff --git a/libc/src/math.c b/libc/src/math.c new file mode 100644 index 0000000..35ffffd --- /dev/null +++ b/libc/src/math.c @@ -0,0 +1,180 @@ +/// MentOS, The Mentoring Operating system project +/// @file math.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "math.h" +#include "stdint.h" + +double round(double x) +{ + double out; + __asm__ __volatile__("fldln2; fldl %1; frndint" + : "=t"(out) + : "m"(x)); + return out; +} + +double floor(double x) +{ + if (x > -1.0 && x < 1.0) { + if (x >= 0) + return 0.0; + return -1.0; + } + int i = (int)x; + if (x < 0) + return (double)(i - 1); + return (double)i; +} + +double ceil(double x) +{ + if (x > -1.0 && x < 1.0) { + if (x <= 0) + return 0.0; + return 1.0; + } + int i = (int)x; + if (x > 0) + return (double)(i + 1); + return (double)i; +} + +double pow(double base, double exponent) +{ + double out; + __asm__ __volatile__("fyl2x;" + "fld %%st;" + "frndint;" + "fsub %%st,%%st(1);" + "fxch;" + "fchs;" + "f2xm1;" + "fld1;" + "faddp;" + "fxch;" + "fld1;" + "fscale;" + "fstp %%st(1);" + "fmulp;" + : "=t"(out) + : "0"(base), "u"(exponent) + : "st(1)"); + return out; +} + +double exp(double x) +{ + return pow(M_E, x); +} + +double fabs(double x) +{ + double out; + __asm__ __volatile__("fldln2; fldl %1; fabs" + : "=t"(out) + : "m"(x)); + return out; +} + +float fabsf(float x) +{ + float out; + __asm__ __volatile__("fldln2; fldl %1; fabs" + : "=t"(out) + : "m"(x)); + return out; +} + +double sqrt(double x) +{ + double out; + __asm__ __volatile__("fldln2; fldl %1; fsqrt" + : "=t"(out) + : "m"(x)); + return out; +} + +float sqrtf(float x) +{ + float out; + __asm__ __volatile__("fldln2; fldl %1; fsqrt" + : "=t"(out) + : "m"(x)); + return out; +} + +int isinf(double x) +{ + union { + unsigned long long u; + double f; + } ieee754; + ieee754.f = x; + return ((unsigned)(ieee754.u >> 32U) & 0x7fffffffU) == 0x7ff00000U && + ((unsigned)ieee754.u == 0); +} + +int isnan(double x) +{ + union { + unsigned long long u; + double f; + } ieee754; + ieee754.f = x; + return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) + + ((unsigned)ieee754.u != 0) > + 0x7ff00000; +} + +double log10(double x) +{ + return ln(x) / ln(10); +} + +double ln(double x) +{ + double out; + __asm__ __volatile__("fldln2; fldl %1; fyl2x" + : "=t"(out) + : "m"(x)); + return out; +} + +double logx(double x, double y) +{ + // Base may not equal 1 or be negative. + if (y == 1.f || y < 0.f || ln(y) == 0.f) + return 0.f; + return ln(x) / ln(y); +} + +/// Max power for forward and reverse projections. +#define MAXPOWTWO 4.503599627370496000E+15 + +double modf(double x, double *intpart) +{ + register double absvalue; + if ((absvalue = (x >= 0.0) ? x : -x) >= MAXPOWTWO) { + // It must be an integer. + (*intpart) = x; + } else { + // Shift fraction off right. + (*intpart) = absvalue + MAXPOWTWO; + // Shift back without fraction. + (*intpart) -= MAXPOWTWO; + + // Above arithmetic might round. + while ((*intpart) > absvalue) { + // Test again just to be sure. + (*intpart) -= 1.0; + } + if (x < 0.0) { + (*intpart) = -(*intpart); + } + } + // Signed fractional part. + return (x - (*intpart)); +} diff --git a/libc/src/pwd.c b/libc/src/pwd.c new file mode 100644 index 0000000..eb6684d --- /dev/null +++ b/libc/src/pwd.c @@ -0,0 +1,162 @@ +/// MentOS, The Mentoring Operating system project +/// @file pwd.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "pwd.h" +#include "sys/unistd.h" +#include "sys/errno.h" +#include "assert.h" +#include "string.h" +#include "stdio.h" +#include "fcntl.h" + +static inline void __parse_line(passwd_t *pwd, char *buf) +{ + assert(pwd && "Received null pwd!"); + char *token; + // Parse the username. + if ((token = strtok(buf, ":")) != NULL) + pwd->pw_name = token; + // Parse the password. + if ((token = strtok(NULL, ":")) != NULL) + pwd->pw_passwd = token; + // Parse the user ID. + if ((token = strtok(NULL, ":")) != NULL) + pwd->pw_uid = atoi(token); + // Parse the group ID. + if ((token = strtok(NULL, ":")) != NULL) + pwd->pw_gid = atoi(token); + // Parse the user information. + if ((token = strtok(NULL, ":")) != NULL) + pwd->pw_gecos = token; + // Parse the dir. + if ((token = strtok(NULL, ":")) != NULL) + pwd->pw_dir = token; + // Parse the shell. + if ((token = strtok(NULL, ":")) != NULL) + pwd->pw_shell = token; +} + +static inline char *__search_entry(int fd, char *buf, int buflen, const char *name, uid_t uid) +{ + int ret; + char c; + int pos = 0; + while ((ret = read(fd, &c, 1U))) { + // Skip carriage return. + if (c == '\r') + continue; + if (pos >= buflen) { + errno = ERANGE; + return NULL; + } + // If we have found a newline or the EOF, parse the entry. + if ((c == '\n') || (ret == EOF)) { + // Close the buffer. + buf[pos] = 0; + // Check the entry. + if (name) { + if (strncmp(buf, name, strlen(name)) == 0) + return buf; + } else { + int uid_start = -1, col_count = 0; + for (int i = 0; i < pos; ++i) { + if (buf[i] == ':') { + if (++col_count == 2) { + uid_start = i + 1; + break; + } + } + } + if ((uid_start != -1) && (uid_start < pos)) { + // Parse the uid. + int found_uid = atoi(&buf[uid_start]); + // Check the uid. + if (found_uid == uid) + return buf; + } + } + // Reset the index. + pos = 0; + // If we have reached the EOF stop. + if (ret == EOF) + break; + } else { + buf[pos++] = c; + } + } + errno = ENOENT; + return NULL; +} + +passwd_t *getpwnam(const char *name) +{ + if (name == NULL) + return NULL; + static passwd_t pwd; + static char buffer[BUFSIZ]; + passwd_t *result; + if (!getpwnam_r(name, &pwd, buffer, BUFSIZ, &result)) + return NULL; + return &pwd; +} + +passwd_t *getpwuid(uid_t uid) +{ + static passwd_t pwd; + static char buffer[BUFSIZ]; + passwd_t *result; + if (!getpwuid_r(uid, &pwd, buffer, BUFSIZ, &result)) + return NULL; + return &pwd; +} + +int getpwnam_r(const char *name, passwd_t *pwd, char *buf, size_t buflen, passwd_t **result) +{ + if (name == NULL) + return 0; + int fd = open("/etc/passwd", O_RDONLY, 0); + if (fd == -1) { + errno = ENOENT; + *result = NULL; + return 0; + } + char *entry = __search_entry(fd, buf, buflen, name, 0); + if (entry != NULL) { + // Close the file. + close(fd); + // Parse the line. + __parse_line(pwd, entry); + // Return success. + return 1; + } + // Close the file. + close(fd); + // Return fail. + return 0; +} + +int getpwuid_r(uid_t uid, passwd_t *pwd, char *buf, size_t buflen, passwd_t **result) +{ + int fd = open("/etc/passwd", O_RDONLY, 0); + if (fd == -1) { + errno = ENOENT; + *result = NULL; + return 0; + } + char *entry = __search_entry(fd, buf, buflen, NULL, uid); + if (entry != NULL) { + // Close the file. + close(fd); + // Parse the line. + __parse_line(pwd, entry); + // Return success. + return 1; + } + // Close the file. + close(fd); + // Return fail. + return 0; +} diff --git a/libc/src/sched.c b/libc/src/sched.c new file mode 100644 index 0000000..daddafa --- /dev/null +++ b/libc/src/sched.c @@ -0,0 +1,13 @@ +// +// Created by enryf on 24/09/2020. +// + +#include "system/syscall_types.h" +#include "sys/errno.h" +#include "sched.h" + +_syscall2(int, sched_setparam, pid_t, pid, const sched_param_t *, param) + +_syscall2(int, sched_getparam, pid_t, pid, sched_param_t *, param) + +_syscall0(int, waitperiod) diff --git a/libc/src/setenv.c b/libc/src/setenv.c new file mode 100644 index 0000000..08fbbba --- /dev/null +++ b/libc/src/setenv.c @@ -0,0 +1,146 @@ +/// @file setenv.c +/// @brief Defines the functions used to manipulate the environmental variables. + +#include +#include "sys/errno.h" +#include "string.h" +#include "stdlib.h" + +char **environ; + +static char **__environ = NULL; +static size_t __environ_size = 0; + +static inline int __find_entry(const char *name, const size_t name_len) +{ + if (environ) { + int index = 0; + for (char **ptr = environ; *ptr; ++ptr, ++index) + if (!strncmp((*ptr), name, name_len) && (*ptr)[name_len] == '=') + return index; + } + return -1; +} + +static void __clone_environ() +{ + if (environ) { + // Count the number of variables. + __environ_size = 0; + for (char **ptr = environ; *ptr; ++ptr) { ++__environ_size; } + // Allocate the space. + __environ = malloc(sizeof(char *) * (__environ_size + 2)); + for (int i = 0; i < __environ_size; ++i) { + size_t entry_len = strlen(environ[i]) + 1; + __environ[i] = malloc(sizeof(char) * entry_len); + memcpy(__environ[i], environ[i], entry_len); + } + __environ[__environ_size] = (char *)NULL; + __environ[__environ_size + 1] = (char *)NULL; + __environ_size += 2; + // Set the new environ. + environ = __environ; + } +} + +int setenv(const char *name, const char *value, int replace) +{ + // There must be always an environ variable set. + assert(environ && "There is no `environ` set."); + // Check the name. + if (name == NULL || *name == '\0' || strchr(name, '=') != NULL) { + errno = EINVAL; + return -1; + } + if (__environ == NULL) { + __clone_environ(); + environ = __environ; + } + const size_t name_len = strlen(name); + const size_t value_len = strlen(value) + 1; + const size_t total_len = name_len + value_len + 1; + //LOCK; + if (environ == NULL) { + return -1; + } + // Find the entry. + int index = __find_entry(name, name_len); + if (index >= 0) { + if (!replace) { + //UNLOCK; + return -1; + } + } else { + // Get the size of environ; + index = __environ_size - 2; + // Extend the environ dynamically allocated memory. + char **new_environ = (char **)realloc(environ, sizeof(char *) * (__environ_size + 1)); + // Check the pointer. + if (new_environ == NULL) { + //UNLOCK; + return -1; + } + // Increment the size. + __environ_size += 1; + // Close the environment. + new_environ[__environ_size - 2] = NULL; + new_environ[__environ_size - 1] = NULL; + // Update all the variables. + environ = __environ = new_environ; + } + // Free the previous entry. + if (environ[index]) + free(environ[index]); + // Allocate the new entry. + environ[index] = malloc(total_len); + // Memcopy because we do not want the null terminating character. + memcpy(environ[index], name, name_len); + // Set the equal. + environ[index][name_len] = '='; + // Add the value. + memcpy(&environ[index][name_len + 1], value, value_len); + //UNLOCK; + return 0; +} + +int unsetenv(const char *name) +{ + if (name == NULL || *name == '\0' || strchr(name, '=') != NULL) { + errno = EINVAL; + return -1; + } + if (__environ == NULL) { + __clone_environ(); + environ = __environ; + } + size_t len = strlen(name); + //LOCK; + char **ep = environ; + while (*ep != NULL) { + if (!strncmp(*ep, name, len) && (*ep)[len] == '=') { + /* Found it. Remove this pointer by moving later ones back. */ + char **dp = ep; + do dp[0] = dp[1]; + while (*dp++); + /* Continue the loop in case NAME appears again. */ + } else { + ++ep; + } + } + //UNLOCK; + return 0; +} + +char *getenv(const char *name) +{ + size_t name_len = strlen(name); + int index = __find_entry(name, name_len); + if (index < 0) { + return NULL; + } + size_t env_len = strlen(environ[index]); + if ((name_len + 1) < env_len) { + return &environ[index][name_len + 1]; + } + return NULL; +} diff --git a/libc/src/stdio.c b/libc/src/stdio.c new file mode 100644 index 0000000..02f74c2 --- /dev/null +++ b/libc/src/stdio.c @@ -0,0 +1,207 @@ +/// @file stdio.c +/// @brief Standard I/0 functions. +/// @date Apr 2019 + +#include +#include "stdio.h" +#include "ctype.h" +#include "string.h" +#include "stdbool.h" +#include "sys/unistd.h" + +void putchar(int character) +{ + write(STDOUT_FILENO, &character, 1); +} + +void puts(char *str) +{ + write(STDOUT_FILENO, str, strlen(str)); +} + +int getchar(void) +{ + char c; + while (read(STDIN_FILENO, &c, 1) == 0) + continue; + + return c; +} + +char *gets(char *str) +{ + // Check the input string. + if (str == NULL) + return NULL; + // Buffer for reading input. + char buffer[GETS_BUFFERSIZE]; + memset(buffer, '\0', GETS_BUFFERSIZE); + // Char pointer to the buffer. + char *cptr = buffer; + // Character storage and counter to prevent overflow. + int ch, counter = 0; + // Read until we find a newline or we exceed the buffer size. + while (((ch = getchar()) != '\n') && (counter++ < GETS_BUFFERSIZE)) { + // If we encounter EOF, stop. + if (ch == EOF) { + // EOF at start of line return NULL. + if (cptr == str) { + return NULL; + } + break; + } + // Backspace key + if (ch == '\b') { + if (counter > 0) { + counter--; + --cptr; + putchar('\b'); + } + } else { + // The character is stored at address, and the pointer is incremented. + *cptr++ = ch; + } + } + // Add the null-terminating character. + *cptr = '\0'; + // Copy the string we have read. + strcpy(str, buffer); + // Return a pointer to the original string. + return str; +} + +int atoi(const char *str) +{ + // Check the input string. + if (str == NULL) + return 0; + // Initialize sign, the result variable, and two indices. + int sign = (str[0] == '-') ? -1 : +1, result = 0, i; + // Find where the number ends. + for (i = (sign == -1) ? 1 : 0; (str[i] != '\0') && isdigit(str[i]); ++i) + result = (result * 10) + str[i] - '0'; + return sign * result; +} + +long strtol(const char *str, char **endptr, int base) +{ + const char *s; + long acc, cutoff; + int c; + int neg, any, cutlim; + /* + * Skip white space and pick up leading +/- sign if any. + * If base is 0, allow 0x for hex and 0 for octal, else + * assume decimal; if base is already 16, allow 0x. + */ + s = str; + do { + c = (unsigned char)*s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else { + neg = 0; + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + /* + * Compute the cutoff value between legal numbers and illegal + * numbers. That is the largest legal value, divided by the + * base. An input number that is greater than this value, if + * followed by a legal input character, is too big. One that + * is equal to this value may be valid or not; the limit + * between valid and invalid numbers is then based on the last + * digit. For instance, if the range for longs is + * [-2147483648..2147483647] and the input base is 10, + * cutoff will be set to 214748364 and cutlim to either + * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated + * a value > 214748364, or equal but the next digit is > 7 (or 8), + * the number is too big, and we will return a range error. + * + * Set any if any `digits' consumed; make it negative to indicate + * overflow. + */ + cutoff = neg ? LONG_MIN : LONG_MAX; + cutlim = cutoff % base; + cutoff /= base; + if (neg) { + if (cutlim > 0) { + cutlim -= base; + cutoff += 1; + } + cutlim = -cutlim; + } + for (acc = 0, any = 0;; c = (unsigned char)*s++) { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0) + continue; + if (neg) { + if (acc < cutoff || (acc == cutoff && c > cutlim)) { + any = -1; + acc = LONG_MIN; + errno = ERANGE; + } else { + any = 1; + acc *= base; + acc -= c; + } + } else { + if (acc > cutoff || (acc == cutoff && c > cutlim)) { + any = -1; + acc = LONG_MAX; + errno = ERANGE; + } else { + any = 1; + acc *= base; + acc += c; + } + } + } + if (endptr != 0) + *endptr = (char *)(any ? s - 1 : str); + return (acc); +} + +int fgetc(int fd) +{ + unsigned char c; + if (read(fd, &c, 1) <= 0) + return EOF; + return c; +} + +char *fgets(char *buf, int n, int fd) +{ + int c; + char *p; + + /* get max bytes or upto a newline */ + for (p = buf, n--; n > 0; n--) { + if ((c = fgetc(fd)) == EOF) + break; + *p++ = c; + if (c == '\n') + break; + } + *p = 0; + if (p == buf || c == EOF) + return NULL; + return (p); +} \ No newline at end of file diff --git a/libc/src/stdlib.c b/libc/src/stdlib.c new file mode 100644 index 0000000..5a6524a --- /dev/null +++ b/libc/src/stdlib.c @@ -0,0 +1,91 @@ +/// MentOS, The Mentoring Operating system project +/// @file stdlib.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "system/syscall_types.h" +#include "stdlib.h" +#include "string.h" +#include "assert.h" + +/// @brief Number which identifies a memory area allocated through a call to +/// malloc(), calloc() or realloc(). +#define MALLOC_MAGIC_NUMBER 0x600DC0DE + +static inline int __malloc_is_valid_ptr(void *ptr) +{ + return (ptr && (((size_t *)ptr)[-1] == MALLOC_MAGIC_NUMBER)); +} + +size_t malloc_usable_size(void *ptr) +{ + if (__malloc_is_valid_ptr(ptr)) + return ((size_t *)ptr)[-2]; + return 0; +} + +void *malloc(unsigned int size) +{ + size_t *_res, _size = 2 * sizeof(size_t) + size; + __inline_syscall1(_res, brk, _size); + _res[0] = size; + _res[1] = MALLOC_MAGIC_NUMBER; + return &_res[2]; +} + +void *calloc(size_t num, size_t size) +{ + void *ptr = malloc(num * size); + if (ptr) { + memset(ptr, 0, num * size); + } + return ptr; +} + +void *realloc(void *ptr, size_t size) +{ + // C standard implementation: When NULL is passed to realloc, + // simply malloc the requested size and return a pointer to that. + if (__builtin_expect(ptr == NULL, 0)) { + return malloc(size); + } + // C standard implementation: For a size of zero, free the + // pointer and return NULL, allocating no new memory. + if (__builtin_expect(size == 0, 0)) { + free(ptr); + return NULL; + } + // Get the old size. + size_t old_size = malloc_usable_size(ptr); + // Create the new pointer. + void *newp = malloc(size); + memset(newp, 0, size); + memcpy(newp, ptr, old_size); + free(ptr); + return newp; +} + +void free(void *ptr) +{ + int _res; + if (__malloc_is_valid_ptr(ptr)) { + size_t *_ptr = (size_t *)ptr - 2; + __inline_syscall1(_res, brk, _ptr); + } else { + __inline_syscall1(_res, brk, ptr); + } +} + +/// Seed used to generate random numbers. +static int rseed = 0; + +void srand(int x) +{ + rseed = x; +} + +int rand() +{ + return rseed = (rseed * 1103515245U + 12345U) & RAND_MAX; +} diff --git a/libc/src/strerror.c b/libc/src/strerror.c new file mode 100644 index 0000000..a67b78c --- /dev/null +++ b/libc/src/strerror.c @@ -0,0 +1,480 @@ +/// MentOS, The Mentoring Operating system project +/// @file strerror.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "strerror.h" +#include "string.h" + +char *strerror(int errnum) +{ + static char error[1024]; + + switch (errnum) { + case 0: + strcpy(error, "Success"); + break; +#ifdef ENOENT + case ENOENT: + strcpy(error, "No such file or directory"); + break; +#endif +#ifdef ESRCH + case ESRCH: + strcpy(error, "No such process"); + break; +#endif +#ifdef EINTR + case EINTR: + strcpy(error, "Interrupted system call"); + break; +#endif +#ifdef EIO + case EIO: + strcpy(error, "I/O error"); + break; +#endif +#if defined(ENXIO) && (!defined(ENODEV) || (ENXIO != ENODEV)) + case ENXIO: + strcpy(error, "No such device or address"); + break; +#endif +#ifdef E2BIG + case E2BIG: + strcpy(error, "Arg list too long"); + break; +#endif +#ifdef ENOEXEC + case ENOEXEC: + strcpy(error, "Exec format error"); + break; +#endif +#ifdef EALREADY + case EALREADY: + strcpy(error, "Socket already connected"); + break; +#endif +#ifdef EBADF + case EBADF: + strcpy(error, "Bad file number"); + break; +#endif +#ifdef ECHILD + case ECHILD: + strcpy(error, "No children"); + break; +#endif +#ifdef EDESTADDRREQ + case EDESTADDRREQ: + strcpy(error, "Destination address required"); + break; +#endif +#ifdef EAGAIN + case EAGAIN: + strcpy(error, "No more processes"); + break; +#endif +#ifdef ENOMEM + case ENOMEM: + strcpy(error, "Not enough space"); + break; +#endif +#ifdef EACCES + case EACCES: + strcpy(error, "Permission denied"); + break; +#endif +#ifdef EFAULT + case EFAULT: + strcpy(error, "Bad address"); + break; +#endif +#ifdef ENOTBLK + case ENOTBLK: + strcpy(error, "Block device required"); + break; +#endif +#ifdef EBUSY + case EBUSY: + strcpy(error, "Device or resource busy"); + break; +#endif +#ifdef EEXIST + case EEXIST: + strcpy(error, "File exists"); + break; +#endif +#ifdef EXDEV + case EXDEV: + strcpy(error, "Cross-device link"); + break; +#endif +#ifdef ENODEV + case ENODEV: + strcpy(error, "No such device"); + break; +#endif +#ifdef ENOTDIR + case ENOTDIR: + strcpy(error, "Not a directory"); + break; +#endif +#ifdef EHOSTDOWN + case EHOSTDOWN: + strcpy(error, "Host is down"); + break; +#endif +#ifdef EINPROGRESS + case EINPROGRESS: + strcpy(error, "Connection already in progress"); + break; +#endif +#ifdef EISDIR + case EISDIR: + strcpy(error, "Is a directory"); + break; +#endif +#ifdef EINVAL + case EINVAL: + strcpy(error, "Invalid argument"); + break; +#endif +#ifdef EISNAM + case EISNAM: + strcpy(error, "Is a named type file"); + break; +#endif +#ifdef ENETDOWN + case ENETDOWN: + strcpy(error, "Network interface is not configured"); + break; +#endif +#ifdef ENFILE + case ENFILE: + strcpy(error, "Too many open files in system"); + break; +#endif +#ifdef EMFILE + case EMFILE: + strcpy(error, "Too many open files"); + break; +#endif +#ifdef ENOTTY + case ENOTTY: + strcpy(error, "Not a character device"); + break; +#endif +#ifdef ETXTBSY + case ETXTBSY: + strcpy(error, "Text file busy"); + break; +#endif +#ifdef EFBIG + case EFBIG: + strcpy(error, "File too large"); + break; +#endif +#ifdef EHOSTUNREACH + case EHOSTUNREACH: + strcpy(error, "Host is unreachable"); + break; +#endif +#ifdef ENOSPC + case ENOSPC: + strcpy(error, "No space left on device"); + break; +#endif +#ifdef ENOTSUP + case ENOTSUP: + strcpy(error, "Not supported"); + break; +#endif +#ifdef ESPIPE + case ESPIPE: + strcpy(error, "Illegal seek"); + break; +#endif +#ifdef EROFS + case EROFS: + strcpy(error, "Read-only file system"); + break; +#endif +#ifdef EMLINK + case EMLINK: + strcpy(error, "Too many links"); + break; +#endif +#ifdef EPIPE + case EPIPE: + strcpy(error, "Broken pipe"); + break; +#endif +#ifdef EDOM + case EDOM: + strcpy(error, "Math argument"); + break; +#endif +#ifdef ERANGE + case ERANGE: + strcpy(error, "Result too large"); + break; +#endif +#ifdef ENOMSG + case ENOMSG: + strcpy(error, "No message of desired type"); + break; +#endif +#ifdef EIDRM + case EIDRM: + strcpy(error, "Identifier removed"); + break; +#endif +#ifdef EDEADLK + case EDEADLK: + strcpy(error, "Deadlock"); + break; +#endif +#ifdef ENETUNREACH + case ENETUNREACH: + strcpy(error, "Network is unreachable"); + break; +#endif +#ifdef ENOLCK + case ENOLCK: + strcpy(error, "No lock"); + break; +#endif +#ifdef ENOSTR + case ENOSTR: + strcpy(error, "Not a stream"); + break; +#endif +#ifdef ETIME + case ETIME: + strcpy(error, "Stream ioctl timeout"); + break; +#endif +#ifdef ENOSR + case ENOSR: + strcpy(error, "No stream resources"); + break; +#endif +#ifdef ENONET + case ENONET: + strcpy(error, "Machine is not on the network"); + break; +#endif +#ifdef ENOPKG + case ENOPKG: + strcpy(error, "No package"); + break; +#endif +#ifdef EREMOTE + case EREMOTE: + strcpy(error, "Resource is remote"); + break; +#endif +#ifdef ENOLINK + case ENOLINK: + strcpy(error, "Virtual circuit is gone"); + break; +#endif +#ifdef EADV + case EADV: + strcpy(error, "Advertise error"); + break; +#endif +#ifdef ESRMNT + case ESRMNT: + strcpy(error, "Srmount error"); + break; +#endif +#ifdef ECOMM + case ECOMM: + strcpy(error, "Communication error"); + break; +#endif +#ifdef EPROTO + case EPROTO: + strcpy(error, "Protocol error"); + break; +#endif +#ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: + strcpy(error, "Unknown protocol"); + break; +#endif +#ifdef EMULTIHOP + case EMULTIHOP: + strcpy(error, "Multihop attempted"); + break; +#endif +#ifdef EBADMSG + case EBADMSG: + strcpy(error, "Bad message"); + break; +#endif +#ifdef ELIBACC + case ELIBACC: + strcpy(error, "Cannot access a needed shared library"); + break; +#endif +#ifdef ELIBBAD + case ELIBBAD: + strcpy(error, "Accessing a corrupted shared library"); + break; +#endif +#ifdef ELIBSCN + case ELIBSCN: + strcpy(error, ".lib section in a.out corrupted"); + break; +#endif +#ifdef ELIBMAX + case ELIBMAX: + strcpy(error, + "Attempting to link in more shared libraries than system limit"); + break; +#endif +#ifdef ELIBEXEC + case ELIBEXEC: + strcpy(error, "Cannot exec a shared library directly"); + break; +#endif +#ifdef ENOSYS + case ENOSYS: + strcpy(error, "Function not implemented"); + break; +#endif +#ifdef ENMFILE + case ENMFILE: + strcpy(error, "No more files"); + break; +#endif +#ifdef ENOTEMPTY + case ENOTEMPTY: + strcpy(error, "Directory not empty"); + break; +#endif +#ifdef ENAMETOOLONG + case ENAMETOOLONG: + strcpy(error, "File or path name too long"); + break; +#endif +#ifdef ELOOP + case ELOOP: + strcpy(error, "Too many symbolic links"); + break; +#endif +#ifdef ENOBUFS + case ENOBUFS: + strcpy(error, "No buffer space available"); + break; +#endif +#ifdef EAFNOSUPPORT + case EAFNOSUPPORT: + strcpy(error, "Address family not supported by protocol family"); + break; +#endif +#ifdef EPROTOTYPE + case EPROTOTYPE: + strcpy(error, "Protocol wrong type for socket"); + break; +#endif +#ifdef ENOTSOCK + case ENOTSOCK: + strcpy(error, "Socket operation on non-socket"); + break; +#endif +#ifdef ENOPROTOOPT + case ENOPROTOOPT: + strcpy(error, "Protocol not available"); + break; +#endif +#ifdef ESHUTDOWN + case ESHUTDOWN: + strcpy(error, "Can't send after socket shutdown"); + break; +#endif +#ifdef ECONNREFUSED + case ECONNREFUSED: + strcpy(error, "Connection refused"); + break; +#endif +#ifdef EADDRINUSE + case EADDRINUSE: + strcpy(error, "Address already in use"); + break; +#endif +#ifdef ECONNABORTED + case ECONNABORTED: + strcpy(error, "Software caused connection abort"); + break; +#endif +#if (defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))) + case EWOULDBLOCK: + strcpy(error, "Operation would block"); + break; +#endif +#ifdef ENOTCONN + case ENOTCONN: + strcpy(error, "Socket is not connected"); + break; +#endif +#ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: + strcpy(error, "Socket type not supported"); + break; +#endif +#ifdef EISCONN + case EISCONN: + strcpy(error, "Socket is already connected"); + break; +#endif +#ifdef ECANCELED + case ECANCELED: + strcpy(error, "Operation canceled"); + break; +#endif +#ifdef ENOTRECOVERABLE + case ENOTRECOVERABLE: + strcpy(error, "State not recoverable"); + break; +#endif +#ifdef EOWNERDEAD + case EOWNERDEAD: + strcpy(error, "Previous owner died"); + break; +#endif +#ifdef ESTRPIPE + case ESTRPIPE: + strcpy(error, "Streams pipe error"); + break; +#endif +#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (ENOTSUP != EOPNOTSUPP)) + case EOPNOTSUPP: + strcpy(error, "Operation not supported on socket"); + break; +#endif +#ifdef EMSGSIZE + case EMSGSIZE: + strcpy(error, "Message too long"); + break; +#endif +#ifdef ETIMEDOUT + case ETIMEDOUT: + strcpy(error, "Connection timed out"); + break; +#endif +#ifdef ENOTSCHEDULABLE + case ENOTSCHEDULABLE: + strcpy(error, "The process cannot be scheduled"); + break; +#endif + default: + strcpy(error, "Unknown error"); + break; + } + + return error; +} diff --git a/libc/src/string.c b/libc/src/string.c new file mode 100644 index 0000000..37c66e6 --- /dev/null +++ b/libc/src/string.c @@ -0,0 +1,702 @@ +/// MentOS, The Mentoring Operating system project +/// @file string.c +/// @brief String routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include "string.h" +#include "ctype.h" +#include "stdio.h" +#include "stdlib.h" + +char *strncpy(char *destination, const char *source, size_t num) +{ + char *start = destination; + while (num && (*destination++ = *source++)) { + num--; + } + if (num) { + while (--num) { + *destination++ = '\0'; + } + } + + return start; +} + +int strncmp(const char *s1, const char *s2, size_t n) +{ + if (!n) + return 0; + while ((--n > 0) && (*s1) && (*s2) && (*s1 == *s2)) { + s1++; + s2++; + } + + return *(unsigned char *)s1 - *(unsigned char *)s2; +} + +int stricmp(const char *s1, const char *s2) +{ + while (*s2 != 0 && toupper(*s1) == toupper(*s2)) { + s1++, s2++; + } + + return (toupper(*s1) - toupper(*s2)); +} + +int strnicmp(const char *s1, const char *s2, size_t n) +{ + int f, l; + + do { + if (((f = (unsigned char)(*(s1++))) >= 'A') && (f <= 'Z')) { + f -= 'A' - 'a'; + } + if (((l = (unsigned char)(*(s2++))) >= 'A') && (l <= 'Z')) { + l -= 'A' - 'a'; + } + } while (--n && f && (f == l)); + + return f - l; +} + +char *strchr(const char *s, int ch) +{ + while (*s && *s != (char)ch) { + s++; + } + if (*s == (char)ch) + return (char *)s; + { + return NULL; + } +} + +char *strrchr(const char *s, int ch) +{ + char *start = (char *)s; + + while (*s++) {} + + while (--s != start && *s != (char)ch) {} + + if (*s == (char)ch) { + return (char *)s; + } + + return NULL; +} + +char *strstr(const char *str1, const char *str2) +{ + char *cp = (char *)str1; + char *s1, *s2; + + if (!*str2) { + return (char *)str1; + } + + while (*cp) { + s1 = cp; + s2 = (char *)str2; + + while (*s1 && *s2 && !(*s1 - *s2)) { + s1++, s2++; + } + if (!*s2) { + return cp; + } + cp++; + } + + return NULL; +} + +size_t strspn(const char *string, const char *control) +{ + const char *str = string; + const char *ctrl = control; + + char map[32]; + size_t n; + + // Clear out bit map. + for (n = 0; n < 32; n++) { + map[n] = 0; + } + + // Set bits in control map. + while (*ctrl) { + map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); + ctrl++; + } + + // 1st char NOT in control map stops search. + if (*str) { + n = 0; + while (map[*str >> 3] & (1 << (*str & 7))) { + n++; + str++; + } + + return n; + } + + return 0; +} + +size_t strcspn(const char *string, const char *control) +{ + const char *str = string; + const char *ctrl = control; + + char map[32]; + size_t n; + + // Clear out bit map. + for (n = 0; n < 32; n++) + map[n] = 0; + + // Set bits in control map. + while (*ctrl) { + map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); + ctrl++; + } + + // 1st char in control map stops search. + n = 0; + map[0] |= 1; + while (!(map[*str >> 3] & (1 << (*str & 7)))) { + n++; + str++; + } + + return n; +} + +char *strpbrk(const char *string, const char *control) +{ + const char *str = string; + const char *ctrl = control; + + char map[32]; + int n; + + // Clear out bit map. + for (n = 0; n < 32; n++) + map[n] = 0; + + // Set bits in control map. + while (*ctrl) { + map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); + ctrl++; + } + + // 1st char in control map stops search. + while (*str) { + if (map[*str >> 3] & (1 << (*str & 7))) { + return (char *)str; + } + str++; + } + + return NULL; +} + +void *memmove(void *dst, const void *src, size_t n) +{ + void *ret = dst; + + if (dst <= src || (char *)dst >= ((char *)src + n)) { + /* Non-overlapping buffers; copy from lower addresses to higher + * addresses. + */ + while (n--) { + *(char *)dst = *(char *)src; + dst = (char *)dst + 1; + src = (char *)src + 1; + } + } else { + // Overlapping buffers; copy from higher addresses to lower addresses. + dst = (char *)dst + n - 1; + src = (char *)src + n - 1; + + while (n--) { + *(char *)dst = *(char *)src; + dst = (char *)dst - 1; + src = (char *)src - 1; + } + } + + return ret; +} + +void *memchr(const void *ptr, int ch, size_t n) +{ + while (n && (*(unsigned char *)ptr != (unsigned char)ch)) { + ptr = (unsigned char *)ptr + 1; + n--; + } + + return (n ? (void *)ptr : NULL); +} + +char *strlwr(char *s) +{ + char *p = s; + + while (*p) { + *p = (char)tolower(*p); + p++; + } + + return s; +} + +char *strupr(char *s) +{ + char *p = s; + + while (*p) { + *p = (char)toupper(*p); + p++; + } + + return s; +} + +char *strncat(char *s1, const char *s2, size_t n) +{ + char *start = s1; + + while (*s1++) {} + + s1--; + + while (n--) { + if (!(*s1++ = *s2++)) + return start; + } + + *s1 = '\0'; + + return start; +} + +char *strnset(char *s, int c, size_t n) +{ + while (n-- && *s) { + *s++ = (char)c; + } + + return s; +} + +char *strrev(char *s) +{ + char *start = s; + char *left = s; + char ch; + + while (*s++) {} + s -= 2; + + while (left < s) { + ch = *left; + *left++ = *s; + *s-- = ch; + } + + return start; +} + +char *strtok_r(char *str, const char *delim, char **saveptr) +{ + char *s; + const char *ctrl = delim; + + char map[32]; + int n; + + // Clear delim map. + for (n = 0; n < 32; n++) { + map[n] = 0; + } + + // Set bits in delimiter table. + do { + map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); + } while (*ctrl++); + + /* Initialize s. If str is NULL, set s to the saved + * pointer (i.e., continue breaking tokens out of the str + * from the last strtok call). + */ + if (str) { + s = str; + } else { + s = *saveptr; + } + + /* Find beginning of token (skip over leading delimiters). Note that + * there is no token iff this loop sets s to point to the terminal + * null (*s == '\0'). + */ + while ((map[*s >> 3] & (1 << (*s & 7))) && *s) { + s++; + } + + str = s; + + /* Find the end of the token. If it is not the end of the str, + * put a null there. + */ + for (; *s; s++) { + if (map[*s >> 3] & (1 << (*s & 7))) { + *s++ = '\0'; + + break; + } + } + + // Update nexttoken. + *saveptr = s; + + // Determine if a token has been found. + if (str == (char *)s) { + return NULL; + } else { + return str; + } +} + +// Intrinsic functions. + +/* + * #pragma function(memset) + * #pragma function(memcmp) + * #pragma function(memcpy) + * #pragma function(strcpy) + * #pragma function(strlen) + * #pragma function(strcat) + * #pragma function(strcmp) + * #pragma function(strset) + */ + +void *memset(void *ptr, int value, size_t num) +{ + // Truncate c to 8 bits. + value = (value & 0xFF); + + char *dst = (char *)ptr; + + // Initialize the rest of the size. + while (num--) { + *dst++ = (char)value; + } + + return ptr; +} + +int memcmp(const void *ptr1, const void *ptr2, size_t n) +{ + if (!n) { + return 0; + } + + while (--n && *(char *)ptr1 == *(char *)ptr2) { + ptr1 = (char *)ptr1 + 1; + ptr2 = (char *)ptr2 + 1; + } + + return *((unsigned char *)ptr1) - *((unsigned char *)ptr2); +} + +void *memcpy(void *ptr1, const void *ptr2, size_t num) +{ + char *_dst = ptr1; + const char *_src = ptr2; + + while (num--) { + *_dst++ = *_src++; + } + + return ptr1; +} + +void *memccpy(void *ptr1, const void *ptr2, int c, size_t n) +{ + while (n && (*((char *)(ptr1 = (char *)ptr1 + 1) - 1) = + *((char *)(ptr2 = (char *)ptr2 + 1) - 1)) != (char)c) { + n--; + } + + return n ? ptr1 : NULL; +} + +char *strcpy(char *dst, const char *src) +{ + char *save = dst; + + while ((*dst++ = *src++) != '\0') {} + + return save; +} + +size_t strlen(const char *s) +{ + const char *eos; + + for (eos = s; *eos != 0; ++eos) {} + + long len = eos - s; + + return (len < 0) ? 0 : (size_t)len; +} + +size_t strnlen(const char *s, size_t count) +{ + const char *sc; + + for (sc = s; *sc != '\0' && count--; ++sc) {} + + long len = sc - s; + + return (len < 0) ? 0 : (size_t)len; +} + +int strcmp(const char *s1, const char *s2) +{ + int ret = 0; + const char *s1t = s1, *s2t = s2; + + for (; !(ret = *s1t - *s2t) && *s2t; ++s1t, ++s2t) {} + + return (ret < 0) ? -1 : (ret > 0) ? 1 : + 0; +} + +char *strcat(char *dst, const char *src) +{ + char *cp = dst; + + while (*cp) { + cp++; + } + + while ((*cp++ = *src++) != '\0') {} + + return dst; +} + +char *strset(char *s, int c) +{ + char *start = s; + + while (*s) { + *s++ = (char)c; + } + + return start; +} + +char *strtok(char *str, const char *delim) +{ + const char *spanp; + int c, sc; + char *tok; + static char *last; + + if (str == NULL && (str = last) == NULL) { + return (NULL); + } + +cont: + c = *str++; + for (spanp = delim; (sc = *spanp++) != 0;) { + if (c == sc) { + goto cont; + } + } + + if (c == 0) { + last = NULL; + + return (NULL); + } + tok = str - 1; + + for (;;) { + c = *str++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) { + str = NULL; + } else { + str[-1] = 0; + } + last = str; + + return (tok); + } + } while (sc != 0); + } +} + +char *trim(char *str) +{ + size_t len = 0; + char *frontp = str; + char *endp = NULL; + + if (str == NULL) { + return NULL; + } + if (str[0] == '\0') { + return str; + } + + len = strlen(str); + endp = str + len; + + /* Move the front and back pointers to address the first non-whitespace + * characters from each end. + */ + while (isspace((unsigned char)*frontp)) { + ++frontp; + } + if (endp != frontp) { + while (isspace((unsigned char)*(--endp)) && endp != frontp) {} + } + if (str + len - 1 != endp) { + *(endp + 1) = '\0'; + } else if (frontp != str && endp == frontp) { + *str = '\0'; + } + /* Shift the string so that it starts at str so that if it's dynamically + * allocated, we can still free it on the returned pointer. Note the reuse + * of endp to mean the front of the string buffer now. + */ + endp = str; + if (frontp != str) { + while (*frontp) { + *endp++ = *frontp++; + } + *endp = '\0'; + } + + return str; +} + +char *strdup(const char *s) +{ + size_t len = strlen(s) + 1; + char *new = malloc(len); + if (new == NULL) + return NULL; + new[len] = '\0'; + return (char *)memcpy(new, s, len); +} + +char *strndup(const char *s, size_t n) +{ + size_t len = strnlen(s, n); + char *new = malloc(len); + if (new == NULL) + return NULL; + new[len] = '\0'; + return (char *)memcpy(new, s, len); +} + +char *strsep(char **stringp, const char *delim) +{ + char *s; + const char *spanp; + int c, sc; + char *tok; + if ((s = *stringp) == NULL) { + return (NULL); + } + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) { + s = NULL; + } else { + s[-1] = 0; + } + *stringp = s; + + return (tok); + } + } while (sc != 0); + } +} + +char *itoa(char *buffer, unsigned int num, unsigned int base) +{ + // int numval; + char *p, *pbase; + + p = pbase = buffer; + + if (base == 16) { + sprintf(buffer, "%0x", num); + } else { + if (num == 0) { + *p++ = '0'; + } + while (num != 0) { + *p++ = (char)('0' + (num % base)); + num = num / base; + } + *p-- = 0; + + while (p > pbase) { + char tmp; + tmp = *p; + *p = *pbase; + *pbase = tmp; + + p--; + pbase++; + } + } + return buffer; +} + +char *replace_char(char *str, char find, char replace) +{ + char *current_pos = strchr(str, find); + + while (current_pos) { + *current_pos = replace; + current_pos = strchr(current_pos, find); + } + + return str; +} + +void strmode(mode_t mode, char *p) +{ + // Usr. + *p++ = mode & S_IRUSR ? 'r' : '-'; + *p++ = mode & S_IWUSR ? 'w' : '-'; + *p++ = mode & S_IXUSR ? 'x' : '-'; + // Group. + *p++ = mode & S_IRGRP ? 'r' : '-'; + *p++ = mode & S_IWGRP ? 'w' : '-'; + *p++ = mode & S_IXGRP ? 'x' : '-'; + // Other. + *p++ = mode & S_IROTH ? 'r' : '-'; + *p++ = mode & S_IWOTH ? 'w' : '-'; + *p++ = mode & S_IXOTH ? 'x' : '-'; + // Will be a '+' if ACL's implemented. + *p++ = ' '; + *p = '\0'; +} diff --git a/libc/src/sys/errno.c b/libc/src/sys/errno.c new file mode 100644 index 0000000..32e7e5f --- /dev/null +++ b/libc/src/sys/errno.c @@ -0,0 +1,15 @@ +/// MentOS, The Mentoring Operating system project +/// @file errno.c +/// @brief Stores the error number. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/errno.h" + +/// @brief Returns the error number for the current process. +/// @return Pointer to the error number. +int *__geterrno() +{ + static int _errno = 0; + return &_errno; +} diff --git a/libc/src/sys/ioctl.c b/libc/src/sys/ioctl.c new file mode 100644 index 0000000..3e4a461 --- /dev/null +++ b/libc/src/sys/ioctl.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file ioctl.c +/// @brief Input/Output ConTroL (IOCTL) functions implementation. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/ioctl.h" +#include "sys/errno.h" +#include "system/syscall_types.h" + +_syscall3(int, ioctl, int, fd, unsigned long int, request, void *, data) diff --git a/libc/src/sys/unistd.c b/libc/src/sys/unistd.c new file mode 100644 index 0000000..81b1185 --- /dev/null +++ b/libc/src/sys/unistd.c @@ -0,0 +1,37 @@ +/// MentOS, The Mentoring Operating system project +/// @file unistd.c +/// @brief Functions used to manage files. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "string.h" +#include "limits.h" +#include "stdio.h" + +#if 0 +#include "vfs.h" + +int rmdir(const char *path) +{ + char absolute_path[PATH_MAX]; + + get_absolute_normalized_path(path, absolute_path); + + int32_t mp_id = get_mountpoint_id(absolute_path); + if (mp_id < 0) { + printf("rmdir: failed to remove '%s':" + "Cannot find mount-point\n", + path); + + return -1; + } + + if (mountpoint_list[mp_id].dir_op.rmdir_f == NULL) { + return -1; + } + + return mountpoint_list[mp_id].dir_op.rmdir_f(absolute_path); +} + +#endif \ No newline at end of file diff --git a/libc/src/sys/utsname.c b/libc/src/sys/utsname.c new file mode 100644 index 0000000..8398d9b --- /dev/null +++ b/libc/src/sys/utsname.c @@ -0,0 +1,12 @@ +/// MentOS, The Mentoring Operating system project +/// @file utsname.c +/// @brief Functions used to provide information about the machine & OS. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/utsname.h" +#include "system/syscall_types.h" +#include "sys/errno.h" +#include "stddef.h" + +_syscall1(int, uname, utsname_t *, buf) diff --git a/libc/src/termios.c b/libc/src/termios.c new file mode 100644 index 0000000..074872f --- /dev/null +++ b/libc/src/termios.c @@ -0,0 +1,38 @@ +/// MentOS, The Mentoring Operating system project +/// @file termios.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "termios.h" +#include "system/syscall_types.h" +#include "sys/errno.h" +#include "bits/ioctls.h" + +int tcgetattr(int fd, termios *termios_p) +{ + int retval; + __asm__ volatile("int $0x80" + : "=a"(retval) + : "0"(__NR_ioctl), "b"((long)(fd)), "c"((long)(TCGETS)), + "d"((long)(termios_p))); + if (retval < 0) { + errno = -retval; + retval = -1; + } + return retval; +} + +int tcsetattr(int fd, int optional_actions, const termios *termios_p) +{ + int retval; + __asm__ volatile("int $0x80" + : "=a"(retval) + : "0"(__NR_ioctl), "b"((long)(fd)), "c"((long)(TCSETS)), + "d"((long)(termios_p))); + if (retval < 0) { + errno = -retval; + retval = -1; + } + return retval; +} \ No newline at end of file diff --git a/libc/src/time.c b/libc/src/time.c new file mode 100644 index 0000000..ce78f5b --- /dev/null +++ b/libc/src/time.c @@ -0,0 +1,399 @@ +/// MentOS, The Mentoring Operating system project +/// @file time.c +/// @brief Clock functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "time.h" +#include "system/syscall_types.h" +#include "sys/errno.h" +#include "string.h" +#include "stdio.h" + +static const char *weekdays[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" +}; + +static const char *months[] = { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" +}; + +_syscall1(time_t, time, time_t *, t) + +time_t difftime(time_t time1, time_t time2) +{ + return time1 - time2; +} + +/// @brief Computes day of week +/// @param y Year +/// @param m Month of year (in range 1 to 12) +/// @param d Day of month (in range 1 to 31) +/// @return Day of week (in range 1 to 7) +static inline int day_of_week(unsigned int y, unsigned int m, unsigned int d) +{ + int h, j, k; + // January and February are counted as months 13 and 14 of the previous year + if (m <= 2) { + m += 12; + y -= 1; + } + // J is the century + j = (int)(y / 100); + // K the year of the century + k = (int)(y % 100); + // Compute H using Zeller's congruence + h = (int)(d + (26 * (m + 1) / 10) + k + (k / 4) + (5 * j) + (j / 4)); + // Return the day of the week + return ((h + 5) % 7) + 1; +} + +tm_t *localtime(const time_t *time) +{ + static tm_t date; + unsigned int a, b, c, d, e, f; + time_t t = *time; + // Negative Unix time values are not supported + if (t < 1) { + t = 0; + } + //Retrieve hours, minutes and seconds + date.tm_sec = (int)(t % 60); + t /= 60; + date.tm_min = (int)(t % 60); + t /= 60; + date.tm_hour = (int)(t % 24); + t /= 24; + // Convert Unix time to date + a = (unsigned int)((4 * t + 102032) / 146097 + 15); + b = (unsigned int)(t + 2442113 + a - (a / 4)); + c = (20 * b - 2442) / 7305; + d = b - 365 * c - (c / 4); + e = d * 1000 / 30601; + f = d - e * 30 - e * 601 / 1000; + // January and February are counted as months 13 and 14 of the previous year + if (e <= 13) { + c -= 4716; + e -= 1; + } else { + c -= 4715; + e -= 13; + } + //Retrieve year, month and day + date.tm_year = (int)c; + date.tm_mon = (int)e; + date.tm_mday = (int)f; + // Calculate day of week. + date.tm_wday = day_of_week(date.tm_year, date.tm_mon, date.tm_mday); + return &date; +} + +size_t strftime(char *str, size_t maxsize, const char *format, const tm_t *timeptr) +{ + if (format == NULL || str == NULL || maxsize <= 0) { + return 0; + } + char *f = (char *)format; + char *s = str; + int value; + size_t ret = 0; + while ((*f) != '\0') { + if ((*f) != '%') { + (*s++) = (*f); + } else { + ++f; + switch (*f) { +#if 0 + case 'a': /* locale's abbreviated weekday name */ + PUT_STRN(weekdays[timeptr->tm_wday], 3); + break; + + case 'A': /* locale's full weekday name */ + PUT_STR(weekdays[timeptr->tm_wday]); + break; +#endif + case 'b': // Abbreviated month name + { + strncat(s, months[timeptr->tm_mon], 3); + break; + } + case 'B': // Full month name + { + unsigned int i = (unsigned int)timeptr->tm_mon; + strcat(s, months[i]); + break; + } + case 'd': /* day of month as decimal number (01-31) */ + { + value = timeptr->tm_mday; + (*s++) = (char)('0' + ((value / 10) % 10)); + (*s++) = (char)('0' + (value % 10)); + break; + } + case 'H': /* hour (24-hour clock) as decimal (00-23) */ + { + value = timeptr->tm_hour; + (*s++) = (char)('0' + ((value / 10) % 10)); + (*s++) = (char)('0' + (value % 10)); + break; + } +#if 0 + case 'I': /* hour (12-hour clock) as decimal (01-12) */ + SPRINTF("%02d", ((timeptr->tm_hour + 11) % 12) + 1); + break; +#endif + case 'j': // Day of year as decimal number (001-366) + { + value = timeptr->tm_yday; + (*s++) = (char)('0' + ((value / 10) % 10)); + (*s++) = (char)('0' + (value % 10)); + break; + } + case 'm': // Month as decimal number (01-12) + { + value = timeptr->tm_mon; + (*s++) = (char)('0' + ((value / 10) % 10)); + (*s++) = (char)('0' + (value % 10)); + break; + } +#if 0 + case 'M': /* month as decimal number (00-59) */ + SPRINTF("%02d", timeptr->tm_min); + break; + + case 'p': /* AM/PM designations (12-hour clock) */ + PUT_STR(timeptr->tm_hour < 12 ? "AM" : "PM"); + /* s.b. locale-specific */ + break; + + case 'S': /* second as decimal number (00-61) */ + SPRINTF("%02d", timeptr->tm_sec); + break; + + case 'U': /* week of year (first Sunday = 1) (00-53) */ + case 'W': /* week of year (first Monday = 1) (00-53) */ + { + int fudge = timeptr->tm_wday - timeptr->tm_yday % 7; + if (*fp == 'W') { + fudge--; + } + fudge = (fudge + 7) % 7; /* +7 so not negative */ + SPRINTF("%02d", (timeptr->tm_yday + fudge) / 7); + break; + } + + case 'w': /* weekday (0-6), Sunday is 0 */ + SPRINTF("%02d", timeptr->tm_wday); + break; + + case 'x': /* appropriate date representation */ + RECUR("%B %d, %Y"); /* s.b. locale-specific */ + break; + + case 'X': /* appropriate time representation */ + RECUR("%H:%M:%S"); /* s.b. locale-specific */ + break; + + case 'y': /* year without century as decimal (00-99) */ + SPRINTF("%02d", timeptr->tm_year % 100); + break; + + case 'Y': /* year with century as decimal */ + SPRINTF("%d", timeptr->tm_year + 1900); + break; + + case 'Z': /* time zone name or abbreviation */ + /* XXX %%% */ + break; + + case '%': + PUT_CH('%'); + break; +#endif + default: + (*s++) = '%'; + (*s++) = (*f); + } + } + ++f; + } + +#if 0 +#define PUT_CH(c) (ret < maxsize ? (*dp++ = (c), ret++) : 0) +#define PUT_STR(str) \ + p = (char *)(str); \ + goto dostr; +#define PUT_STRN(str, n) \ + p = (char *)(str); \ + len = (n); \ + goto dostrn; +#define SPRINTF(fmt, arg) \ + sprintf(tmpbuf, fmt, arg); \ + p = tmpbuf; \ + goto dostr; +#define RECUR(fmt) \ + { \ + size_t r = strftime(dp, maxsize - ret, fmt, timeptr); \ + if (r <= 0) \ + return r; \ + dp += r; \ + ret += r; \ + } + + char* fp = (char*) format; + char* dp = s; + size_t ret = 0; + + if (format == NULL || s == NULL || maxsize <= 0) { + return 0; + } + + while (*fp != '\0') { + char* p; + int len; + char tmpbuf[10]; + + if (*fp != '%') { + PUT_CH(*fp++); + continue; + } + + fp++; + + switch (*fp) { + + case 'a': /* locale's abbreviated weekday name */ + PUT_STRN(weekdays[timeptr->tm_wday], 3); + break; + + case 'A': /* locale's full weekday name */ + PUT_STR(weekdays[timeptr->tm_wday]); + break; + + case 'b': /* locale's abbreviated month name */ + PUT_STRN(months[timeptr->tm_mon], 3); + break; + + case 'B': /* locale's full month name */ + PUT_STR(months[timeptr->tm_mon]); + break; + + case 'c': /* appropriate date and time representation */ + RECUR("%X %x"); /* s.b. locale-specific */ + break; + + case 'd': /* day of month as decimal number (01-31) */ + SPRINTF("%02d", timeptr->tm_mday); + break; + + case 'H': /* hour (24-hour clock) as decimal (00-23) */ + SPRINTF("%02d", timeptr->tm_hour); + break; + + case 'I': /* hour (12-hour clock) as decimal (01-12) */ + SPRINTF("%02d", ((timeptr->tm_hour + 11) % 12) + 1); + break; + + case 'j': /* day of year as decimal number (001-366) */ + SPRINTF("%03d", timeptr->tm_yday + 1); + break; + + case 'm': /* month as decimal number (01-12) */ + SPRINTF("%02d", timeptr->tm_mon + 1); + break; + + case 'M': /* month as decimal number (00-59) */ + SPRINTF("%02d", timeptr->tm_min); + break; + + case 'p': /* AM/PM designations (12-hour clock) */ + PUT_STR(timeptr->tm_hour < 12 ? "AM" : "PM"); + /* s.b. locale-specific */ + break; + + case 'S': /* second as decimal number (00-61) */ + SPRINTF("%02d", timeptr->tm_sec); + break; + + case 'U': /* week of year (first Sunday = 1) (00-53) */ + case 'W': /* week of year (first Monday = 1) (00-53) */ + { + int fudge = timeptr->tm_wday - timeptr->tm_yday % 7; + if (*fp == 'W') { + fudge--; + } + fudge = (fudge + 7) % 7; /* +7 so not negative */ + SPRINTF("%02d", (timeptr->tm_yday + fudge) / 7); + break; + } + + case 'w': /* weekday (0-6), Sunday is 0 */ + SPRINTF("%02d", timeptr->tm_wday); + break; + + case 'x': /* appropriate date representation */ + RECUR("%B %d, %Y"); /* s.b. locale-specific */ + break; + + case 'X': /* appropriate time representation */ + RECUR("%H:%M:%S"); /* s.b. locale-specific */ + break; + + case 'y': /* year without century as decimal (00-99) */ + SPRINTF("%02d", timeptr->tm_year % 100); + break; + + case 'Y': /* year with century as decimal */ + SPRINTF("%d", timeptr->tm_year + 1900); + break; + + case 'Z': /* time zone name or abbreviation */ + /* XXX %%% */ + break; + + case '%': + PUT_CH('%'); + break; + + dostr: + while (*p != '\0') + PUT_CH(*p++); + break; + + dostrn: + while (len-- > 0) + PUT_CH(*p++); + break; + } + + ++fp; + } + if (ret >= maxsize) { + s[maxsize - 1] = '\0'; + return 0; + } + *dp = '\0'; +#endif + return ret; +} + +_syscall2(int, nanosleep, const timespec *, req, timespec *, rem) + +unsigned int sleep(unsigned int seconds) +{ + timespec req, rem; + req.tv_sec = seconds; + // Call the nanosleep. + int __ret = nanosleep(&req, &rem); + // If the call to the nanosleep is interrupted by a signal handler, + // then it returns -1 with errno set to EINTR. + if ((__ret == -1) && (errno == EINTR)) { + return rem.tv_sec; + } + return 0; +} + +_syscall2(int, getitimer, int, which, itimerval *, curr_value) + +_syscall3(int, setitimer, int, which, const itimerval *, new_value, itimerval *, old_value) \ No newline at end of file diff --git a/libc/src/unistd/chdir.c b/libc/src/unistd/chdir.c new file mode 100644 index 0000000..d93eb58 --- /dev/null +++ b/libc/src/unistd/chdir.c @@ -0,0 +1,13 @@ +/// MentOS, The Mentoring Operating system project +/// @file chdir.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall1(int, chdir, const char *, path) + +_syscall1(int, fchdir, int, fd) diff --git a/libc/src/unistd/close.c b/libc/src/unistd/close.c new file mode 100644 index 0000000..51013b6 --- /dev/null +++ b/libc/src/unistd/close.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file close.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall1(int, close, int, fd) diff --git a/libc/src/unistd/exec.c b/libc/src/unistd/exec.c new file mode 100644 index 0000000..3580d54 --- /dev/null +++ b/libc/src/unistd/exec.c @@ -0,0 +1,243 @@ +/// MentOS, The Mentoring Operating system project +/// @file exec.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +#include "sys/stat.h" +#include "stdlib.h" +#include "string.h" +#include "stdarg.h" +#include "fcntl.h" + +extern char **environ; + +/// @brief Default `PATH`. +#define DEFAULT_PATH "/bin:/usr/bin" + +/// @brief Finds an executable inside the PATH entries. +/// @param file The file to search. +/// @param buf The buffer where we will store the absolute path. +/// @param buf_len The length of the buffer. +/// @return 0 if we have found the file inside the entries of PATH, +/// -1 otherwise. +static inline int __find_in_path(const char *file, char *buf, size_t buf_len) +{ + // Determine the search path. + char *PATH_VAR = getenv("PATH"); + if (PATH_VAR == NULL) { + PATH_VAR = DEFAULT_PATH; + } + // Prepare a stat object for later. + stat_t stat_buf; + // Copy the path. + char *path = strdup(PATH_VAR); + // Iterate through the path entries. + char *token = strtok(path, ":"); + while (token != NULL) { + strcpy(buf, token); + strcat(buf, "/"); + strcat(buf, file); + if (stat(buf, &stat_buf) == 0) { + if (stat_buf.st_mode & S_IXUSR) { + // TODO: Check why `init` has problems with this free. + // To reproduce the problem use this in init.c: + // execvp("login", _argv); + free(path); + return 0; + } + } + token = strtok(NULL, ":"); + } + free(path); + // We did not find the file inside PATH. + errno = ENOENT; + return -1; +} + +_syscall3(int, execve, const char *, path, char *const *, argv, char *const *, envp) + +int execv(const char *path, char *const argv[]) +{ + return execve(path, argv, environ); +} + +int execvp(const char *file, char *const argv[]) +{ + if (!file || !argv || !environ) { + errno = ENOENT; + return -1; + } + if (file[0] == '/') { + return execve(file, argv, environ); + } + // Prepare a buffer for the absolute path. + char absolute_path[PATH_MAX] = { 0 }; + // Find the file inside the entries of the PATH variable. + if (__find_in_path(file, absolute_path, PATH_MAX) == 0) { + return execve(absolute_path, argv, environ); + } + errno = ENOENT; + return -1; +} + +int execvpe(const char *file, char *const argv[], char *const envp[]) +{ + if (!file || !argv || !envp) { + errno = ENOENT; + return -1; + } + if (file[0] == '/') { + return execve(file, argv, envp); + } + // Prepare a buffer for the absolute path. + char absolute_path[PATH_MAX]; + // Find the file inside the entries of the PATH variable. + if (__find_in_path(file, absolute_path, PATH_MAX) == 0) { + return execve(absolute_path, argv, envp); + } + errno = ENOENT; + return -1; +} + +int execl(const char *path, const char *arg, ...) +{ + va_list ap; + int argc; + + // Phase 1: Count the arguments. + va_start(ap, arg); + for (argc = 1; va_arg(ap, const char *); ++argc) { + if (argc == INT_MAX) { + va_end(ap); + errno = E2BIG; + return -1; + } + } + va_end(ap); + + // Phase 2: Store the arguments inside a vector. + char *argv[argc + 1]; + va_start(ap, arg); + argv[0] = (char *)arg; + for (int i = 1; i <= argc; ++i) + argv[i] = va_arg(ap, char *); + va_end(ap); + + // Now, we can call `execve` plain and simple. + return execve(path, argv, environ); +} + +int execlp(const char *file, const char *arg, ...) +{ + va_list ap; + int argc; + + // Phase 1: Count the arguments. + va_start(ap, arg); + for (argc = 1; va_arg(ap, const char *); ++argc) { + if (argc == INT_MAX) { + va_end(ap); + errno = E2BIG; + return -1; + } + } + va_end(ap); + + // Phase 2: Store the arguments inside a vector. + char *argv[argc + 1]; + va_start(ap, arg); + argv[0] = (char *)arg; + for (int i = 1; i < argc; ++i) + argv[i] = va_arg(ap, char *); + va_end(ap); + + // Close argv. + argv[argc] = NULL; + + // Now, we can call `execve` plain and simple. + return execvpe(file, argv, environ); +} + +int execle(const char *path, const char *arg, ...) +{ + va_list ap; + int argc; + + // Phase 1: Count the arguments. + va_start(ap, arg); + for (argc = 1; va_arg(ap, const char *); ++argc) { + if (argc == INT_MAX) { + va_end(ap); + errno = E2BIG; + return -1; + } + } + va_end(ap); + + argc -= 1; + if (argc < 0) { + errno = EINVAL; + return -1; + } + + // Phase 2: Store the arguments inside a vector. + char *argv[argc + 1]; + va_start(ap, arg); + argv[0] = (char *)arg; + for (int i = 1; i < argc; ++i) + argv[i] = va_arg(ap, char *); + // Close argv. + argv[argc] = NULL; + + // Phase 3: Store the pointer to the environ. + char **envp = va_arg(ap, char **); + va_end(ap); + + // Now, we can call `execve` plain and simple. + return execve(path, argv, envp); +} + +int execlpe(const char *file, const char *arg, ...) +{ + va_list ap; + int argc; + + // Phase 1: Count the arguments. + va_start(ap, arg); + for (argc = 1; va_arg(ap, const char *); ++argc) { + if (argc == INT_MAX) { + va_end(ap); + errno = E2BIG; + return -1; + } + } + va_end(ap); + + // We don't need to count `envp` among the arguments. + if ((argc -= 1) < 0) { + errno = EINVAL; + return -1; + } + + // Phase 2: Store the arguments inside a vector. + char *argv[argc + 1]; + va_start(ap, arg); + argv[0] = (char *)arg; + for (int i = 1; i < argc; ++i) + argv[i] = va_arg(ap, char *); + // Close argv. + argv[argc] = NULL; + + // Phase 3: Store the pointer to the environ. + char **envp = va_arg(ap, char **); + va_end(ap); + + // Now, we can call `execve` plain and simple. + return execvpe(file, argv, envp); +} diff --git a/libc/src/unistd/exit.c b/libc/src/unistd/exit.c new file mode 100644 index 0000000..0432e08 --- /dev/null +++ b/libc/src/unistd/exit.c @@ -0,0 +1,15 @@ +/// MentOS, The Mentoring Operating system project +/// @file exit.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" + +void exit(int status) +{ + long __res; + __inline_syscall1(__res, exit, status); + // The process never returns from this system call! +} diff --git a/libc/src/unistd/fork.c b/libc/src/unistd/fork.c new file mode 100644 index 0000000..a649f36 --- /dev/null +++ b/libc/src/unistd/fork.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file fork.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall0(pid_t, fork) diff --git a/libc/src/unistd/getcwd.c b/libc/src/unistd/getcwd.c new file mode 100644 index 0000000..af08b71 --- /dev/null +++ b/libc/src/unistd/getcwd.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file getcwd.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall2(char *, getcwd, char *, buf, size_t, size) diff --git a/libc/src/unistd/getdents.c b/libc/src/unistd/getdents.c new file mode 100644 index 0000000..29a2865 --- /dev/null +++ b/libc/src/unistd/getdents.c @@ -0,0 +1,12 @@ +/// MentOS, The Mentoring Operating system project +/// @file getdents.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + + +_syscall3(int, getdents, int, fd, dirent_t *, dirp, unsigned int, count) diff --git a/libc/src/unistd/getgid.c b/libc/src/unistd/getgid.c new file mode 100644 index 0000000..c27ee57 --- /dev/null +++ b/libc/src/unistd/getgid.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file getgid.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall0(pid_t, getgid) diff --git a/libc/src/unistd/getpid.c b/libc/src/unistd/getpid.c new file mode 100644 index 0000000..d9851e9 --- /dev/null +++ b/libc/src/unistd/getpid.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file getpid.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall0(pid_t, getpid) diff --git a/libc/src/unistd/getppid.c b/libc/src/unistd/getppid.c new file mode 100644 index 0000000..3cc612e --- /dev/null +++ b/libc/src/unistd/getppid.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file getppid.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall0(pid_t, getppid) diff --git a/libc/src/unistd/getsid.c b/libc/src/unistd/getsid.c new file mode 100644 index 0000000..7e8daf8 --- /dev/null +++ b/libc/src/unistd/getsid.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file getsid.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall1(pid_t, getsid, pid_t, pid) diff --git a/libc/src/unistd/interval.c b/libc/src/unistd/interval.c new file mode 100644 index 0000000..fc7ad63 --- /dev/null +++ b/libc/src/unistd/interval.c @@ -0,0 +1,6 @@ + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall1(int, alarm, int, seconds) diff --git a/libc/src/unistd/kill.c b/libc/src/unistd/kill.c new file mode 100644 index 0000000..14e7a95 --- /dev/null +++ b/libc/src/unistd/kill.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file kill.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall2(int, kill, pid_t, pid, int, sig) diff --git a/libc/src/unistd/lseek.c b/libc/src/unistd/lseek.c new file mode 100644 index 0000000..b7b4fe4 --- /dev/null +++ b/libc/src/unistd/lseek.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file open.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall3(off_t, lseek, int, fd, off_t, offset, int, whence) diff --git a/libc/src/unistd/mkdir.c b/libc/src/unistd/mkdir.c new file mode 100644 index 0000000..e05c430 --- /dev/null +++ b/libc/src/unistd/mkdir.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file mkdir.c +/// @brief Make directory functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall2(int, mkdir, const char *, path, mode_t, mode) diff --git a/libc/src/unistd/nice.c b/libc/src/unistd/nice.c new file mode 100644 index 0000000..d72815b --- /dev/null +++ b/libc/src/unistd/nice.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file nice.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall1(int, nice, int, inc) diff --git a/libc/src/unistd/open.c b/libc/src/unistd/open.c new file mode 100644 index 0000000..dfe1d8f --- /dev/null +++ b/libc/src/unistd/open.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file open.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall3(int, open, const char *, pathname, int, flags, mode_t, mode) diff --git a/libc/src/unistd/read.c b/libc/src/unistd/read.c new file mode 100644 index 0000000..211166f --- /dev/null +++ b/libc/src/unistd/read.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file read.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall3(ssize_t, read, int, fd, void *, buf, size_t, nbytes) diff --git a/libc/src/unistd/reboot.c b/libc/src/unistd/reboot.c new file mode 100644 index 0000000..d8a3a8c --- /dev/null +++ b/libc/src/unistd/reboot.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file reboot.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall4(int, reboot, int, magic1, int, magic2, unsigned int, cmd, void *, arg) diff --git a/libc/src/unistd/rmdir.c b/libc/src/unistd/rmdir.c new file mode 100644 index 0000000..3c5115e --- /dev/null +++ b/libc/src/unistd/rmdir.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file rmdir.c +/// @brief Make directory functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall1(int, rmdir, const char *, path) diff --git a/libc/src/unistd/setgid.c b/libc/src/unistd/setgid.c new file mode 100644 index 0000000..7ec415a --- /dev/null +++ b/libc/src/unistd/setgid.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file setgid.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall1(int, setgid, pid_t, pid) diff --git a/libc/src/unistd/setsid.c b/libc/src/unistd/setsid.c new file mode 100644 index 0000000..e988438 --- /dev/null +++ b/libc/src/unistd/setsid.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file setsid.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall0(pid_t, setsid) diff --git a/libc/src/unistd/signal.c b/libc/src/unistd/signal.c new file mode 100644 index 0000000..f00d5e5 --- /dev/null +++ b/libc/src/unistd/signal.c @@ -0,0 +1,103 @@ +/// MentOS, The Mentoring Operating system project +/// @file signal.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +#include "signal.h" +#include "sys/bitops.h" + +static const char *sys_siglist[] = { + "HUP", + "INT", + "QUIT", + "ILL", + "TRAP", + "ABRT", + "EMT", + "FPE", + "KILL", + "BUS", + "SEGV", + "SYS", + "PIPE", + "ALRM", + "TERM", + "USR1", + "USR2", + "CHLD", + "PWR", + "WINCH", + "URG", + "POLL", + "STOP", + "TSTP", + "CONT", + "TTIN", + "TTOU", + "VTALRM", + "PROF", + "XCPU", + "XFSZ", + NULL, +}; + +_syscall2(sighandler_t, signal, int, signum, sighandler_t, handler) + +_syscall3(int, sigaction, int, signum, const sigaction_t *, act, sigaction_t *, oldact) + +_syscall3(int, sigprocmask, int, how, const sigset_t *, set, sigset_t *, oldset) + +const char *strsignal(int sig) +{ + if ((sig >= SIGHUP) && (sig < NSIG)) + return sys_siglist[sig - 1]; + return NULL; +} + +int sigemptyset(sigset_t *set) +{ + if (set) { + set->sig[0] = 0; + return 0; + } + return -1; +} + +int sigfillset(sigset_t *set) +{ + if (set) { + set->sig[0] = ~0UL; + return 0; + } + return -1; +} + +int sigaddset(sigset_t *set, int signum) +{ + if (set && ((signum))) { + bit_set_assign(set->sig[(signum - 1) / 32], ((signum - 1) % 32)); + return 0; + } + return -1; +} + +int sigdelset(sigset_t *set, int signum) +{ + if (set) { + bit_clear_assign(set->sig[(signum - 1) / 32], ((signum - 1) % 32)); + return 0; + } + return -1; +} + +int sigismember(sigset_t *set, int signum) +{ + if (set) + return bit_check(set->sig[(signum - 1) / 32], (signum - 1) % 32); + return -1; +} \ No newline at end of file diff --git a/libc/src/unistd/stat.c b/libc/src/unistd/stat.c new file mode 100644 index 0000000..da0ae8c --- /dev/null +++ b/libc/src/unistd/stat.c @@ -0,0 +1,15 @@ +/// MentOS, The Mentoring Operating system project +/// @file stat.c +/// @brief Stat functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +#include "sys/stat.h" + +_syscall2(int, stat, const char *, path, stat_t *, buf) + +_syscall2(int, fstat, int, fd, stat_t *, buf) diff --git a/libc/src/unistd/unlink.c b/libc/src/unistd/unlink.c new file mode 100644 index 0000000..4508ec7 --- /dev/null +++ b/libc/src/unistd/unlink.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file unlink.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall1(int, unlink, const char *, path) diff --git a/libc/src/unistd/waitpid.c b/libc/src/unistd/waitpid.c new file mode 100644 index 0000000..ee7d233 --- /dev/null +++ b/libc/src/unistd/waitpid.c @@ -0,0 +1,37 @@ +/// MentOS, The Mentoring Operating system project +/// @file waitpid.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "sys/wait.h" +#include "sys/errno.h" +#include "system/syscall_types.h" + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +pid_t waitpid(pid_t pid, int *status, int options) +{ + pid_t __res; + int __status = 0; + do { + __inline_syscall3(__res, waitpid, pid, &__status, options); + if (__res < 0) + break; + if (__status == EXIT_ZOMBIE) + break; + if (options && WNOHANG) + break; + } while (1); + if (status) + *status = __status; + __syscall_return(pid_t, __res); +} + +pid_t wait(int *status) +{ + return waitpid(-1, status, 0); +} diff --git a/libc/src/unistd/write.c b/libc/src/unistd/write.c new file mode 100644 index 0000000..5f7e547 --- /dev/null +++ b/libc/src/unistd/write.c @@ -0,0 +1,11 @@ +/// MentOS, The Mentoring Operating system project +/// @file write.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/unistd.h" +#include "system/syscall_types.h" +#include "sys/errno.h" + +_syscall3(ssize_t, write, int, fd, void *, buf, size_t, nbytes) diff --git a/libc/src/vscanf.c b/libc/src/vscanf.c new file mode 100644 index 0000000..5082962 --- /dev/null +++ b/libc/src/vscanf.c @@ -0,0 +1,138 @@ +/// MentOS, The Mentoring Operating system project +/// @file vscanf.c +/// @brief Reading formatting routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +static int vsscanf(const char *buf, const char *s, va_list ap) +{ + int count = 0, noassign = 0, width = 0, base = 0; + const char *tc; + char tmp[BUFSIZ]; + + while (*s && *buf) { + while (isspace(*s)) + ++s; + if (*s == '%') { + ++s; + for (; *s; ++s) { + if (strchr("dibouxcsefg%", *s)) + break; + if (*s == '*') + noassign = 1; + else if (isdigit(*s)) { + for (tc = s; isdigit(*s); ++s) + {} + strncpy(tmp, tc, s - tc); + tmp[s - tc] = '\0'; + width = strtol(tmp, NULL, 10); + --s; + } + } + if (*s == 's') { + while (isspace(*buf)) + ++buf; + if (!width) + width = strcspn(buf, " \t\n\r\f\v"); + if (!noassign) { + char *string = va_arg(ap, char *); + strncpy(string, buf, width); + string[width] = '\0'; + } + buf += width; + } else if (*s == 'c') { + while (isspace(*buf)) + ++buf; + if (!width) + width = 1; + if (!noassign) { + strncpy(va_arg(ap, char *), buf, width); + } + buf += width; + } else if (strchr("duxob", *s)) { + while (isspace(*buf)) + ++buf; + if (*s == 'd' || *s == 'u') + base = 10; + else if (*s == 'x') + base = 16; + else if (*s == 'o') + base = 8; + else if (*s == 'b') + base = 2; + if (!width) { + if (isspace(*(s + 1)) || *(s + 1) == 0) + width = strcspn(buf, " \t\n\r\f\v"); + else + width = strchr(buf, *(s + 1)) - buf; + } + strncpy(tmp, buf, width); + tmp[width] = '\0'; + buf += width; + if (!noassign) + *va_arg(ap, unsigned int *) = strtol(tmp, NULL, base); + } + if (!noassign) + ++count; + width = noassign = 0; + ++s; + } else { + while (isspace(*buf)) + ++buf; + if (*s != *buf) + break; + else + ++s, ++buf; + } + } + return (count); +} + +static int vfscanf(int fd, const char *fmt, va_list ap) +{ + int count; + char buf[BUFSIZ + 1]; + + if (fgets(buf, BUFSIZ, fd) == 0) + return (-1); + count = vsscanf(buf, fmt, ap); + return (count); +} + +int scanf(const char *fmt, ...) +{ + int count; + va_list ap; + + va_start(ap, fmt); + count = vfscanf(STDIN_FILENO, fmt, ap); + va_end(ap); + return (count); +} + +int fscanf(int fd, const char *fmt, ...) +{ + int count; + va_list ap; + + va_start(ap, fmt); + count = vfscanf(fd, fmt, ap); + va_end(ap); + return (count); +} + +int sscanf(const char *buf, const char *fmt, ...) +{ + int count; + va_list ap; + + va_start(ap, fmt); + count = vsscanf(buf, fmt, ap); + va_end(ap); + return (count); +} diff --git a/libc/src/vsprintf.c b/libc/src/vsprintf.c new file mode 100644 index 0000000..7418095 --- /dev/null +++ b/libc/src/vsprintf.c @@ -0,0 +1,693 @@ +/// MentOS, The Mentoring Operating system project +/// @file vsprintf.c +/// @brief Print formatting routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include "fcvt.h" +#include "ctype.h" +#include "string.h" +#include "stdarg.h" +#include "stdint.h" +#include "stdio.h" +#include "sys/unistd.h" + +/// Size of the buffer used to call cvt functions. +#define CVTBUFSIZE 500 + +#define FLAGS_ZEROPAD (1U << 0U) ///< Fill zeros before the number. +#define FLAGS_LEFT (1U << 1U) ///< Left align the value. +#define FLAGS_PLUS (1U << 2U) ///< Print the plus sign. +#define FLAGS_SPACE (1U << 3U) ///< If positive add a space instead of the plus sign. +#define FLAGS_HASH (1U << 4U) ///< Preceed with 0x or 0X, %x or %X respectively. +#define FLAGS_UPPERCASE (1U << 5U) ///< Print uppercase. +#define FLAGS_SIGN (1U << 6U) ///< Print the sign. + +/// The list of digits. +static char *_digits = "0123456789abcdefghijklmnopqrstuvwxyz"; + +/// The list of uppercase digits. +static char *_upper_digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +/// @brief Returns the index of the first non-integer character. +static inline int skip_atoi(const char **s) +{ + int i = 0; + while (isdigit(**s)) + i = i * 10 + *((*s)++) - '0'; + return i; +} + +static char *number(char *str, long num, int base, int size, int32_t precision, unsigned flags) +{ + char c, tmp[66] = { 0 }; + char *dig = _digits; + + if (bitmask_check(flags, FLAGS_UPPERCASE)) { + dig = _upper_digits; + } + if (bitmask_check(flags, FLAGS_LEFT)) { + bitmask_clear_assign(flags, FLAGS_ZEROPAD); + } + if (base < 2 || base > 36) { + return 0; + } + + c = bitmask_check(flags, FLAGS_ZEROPAD) ? '0' : ' '; + + // -------------------------------- + // Set the sign. + // -------------------------------- + char sign = 0; + if (bitmask_check(flags, FLAGS_SIGN)) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (bitmask_check(flags, FLAGS_PLUS)) { + sign = '+'; + size--; + } else if (bitmask_check(flags, FLAGS_SPACE)) { + sign = ' '; + size--; + } + } + // Sice I've removed the sign (if negative), i can transform it to unsigned. + uint32_t uns_num = (uint32_t)num; + if (bitmask_check(flags, FLAGS_HASH)) { + if (base == 16) { + size -= 2; + } else if (base == 8) { + size--; + } + } + + int32_t i = 0; + if (uns_num == 0) { + tmp[i++] = '0'; + } else { + while (uns_num != 0) { + tmp[i++] = dig[((unsigned long)uns_num) % (unsigned)base]; + uns_num = ((unsigned long)uns_num) / (unsigned)base; + } + } + if (i > precision) { + precision = i; + } + size -= precision; + if (!bitmask_check(flags, FLAGS_ZEROPAD | FLAGS_LEFT)) { + while (size-- > 0) + *str++ = ' '; + } + if (sign) { + *str++ = sign; + } + if (bitmask_check(flags, FLAGS_HASH)) { + if (base == 8) + *str++ = '0'; + else if (base == 16) { + *str++ = '0'; + *str++ = _digits[33]; + } + } + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (size-- > 0) { + *str++ = c; + } + } + while (i < precision--) { + *str++ = '0'; + } + while (i-- > 0) { + *str++ = tmp[i]; + } + while (size-- > 0) { + *str++ = ' '; + } + return str; +} + +static char *eaddr(char *str, unsigned char *addr, int size, int precision, unsigned flags) +{ + (void)precision; + char tmp[24]; + char *dig = _digits; + int i, len; + + if (bitmask_check(flags, FLAGS_UPPERCASE)) { + dig = _upper_digits; + } + + len = 0; + for (i = 0; i < 6; i++) { + if (i != 0) { + tmp[len++] = ':'; + } + tmp[len++] = dig[addr[i] >> 4]; + tmp[len++] = dig[addr[i] & 0x0F]; + } + + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (len < size--) { + *str++ = ' '; + } + } + + for (i = 0; i < len; ++i) { + *str++ = tmp[i]; + } + + while (len < size--) { + *str++ = ' '; + } + + return str; +} + +static char *iaddr(char *str, unsigned char *addr, int size, int precision, unsigned flags) +{ + (void)precision; + char tmp[24]; + int i, n, len; + + len = 0; + for (i = 0; i < 4; i++) { + if (i != 0) { + tmp[len++] = '.'; + } + n = addr[i]; + + if (n == 0) { + tmp[len++] = _digits[0]; + } else { + if (n >= 100) { + tmp[len++] = _digits[n / 100]; + n = n % 100; + tmp[len++] = _digits[n / 10]; + n = n % 10; + } else if (n >= 10) { + tmp[len++] = _digits[n / 10]; + n = n % 10; + } + + tmp[len++] = _digits[n]; + } + } + + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (len < size--) { + *str++ = ' '; + } + } + + for (i = 0; i < len; ++i) { + *str++ = tmp[i]; + } + + while (len < size--) { + *str++ = ' '; + } + + return str; +} + +static void cfltcvt(double value, char *buffer, char fmt, int precision) +{ + int decpt, sign, exp, pos; + char cvtbuf[CVTBUFSIZE]; + char *digits = cvtbuf; + int capexp = 0; + int magnitude; + + if (fmt == 'G' || fmt == 'E') { + capexp = 1; + fmt += 'a' - 'A'; + } + + if (fmt == 'g') { + ecvtbuf(value, precision, &decpt, &sign, cvtbuf, CVTBUFSIZE); + magnitude = decpt - 1; + if (magnitude < -4 || magnitude > precision - 1) { + fmt = 'e'; + precision -= 1; + } else { + fmt = 'f'; + precision -= decpt; + } + } + + if (fmt == 'e') { + ecvtbuf(value, precision + 1, &decpt, &sign, cvtbuf, CVTBUFSIZE); + + if (sign) { + *buffer++ = '-'; + } + *buffer++ = *digits; + if (precision > 0) { + *buffer++ = '.'; + } + memcpy(buffer, digits + 1, precision); + buffer += precision; + *buffer++ = capexp ? 'E' : 'e'; + + if (decpt == 0) { + if (value == 0.0) { + exp = 0; + } else { + exp = -1; + } + } else { + exp = decpt - 1; + } + + if (exp < 0) { + *buffer++ = '-'; + exp = -exp; + } else { + *buffer++ = '+'; + } + + buffer[2] = (char)((exp % 10) + '0'); + exp = exp / 10; + buffer[1] = (char)((exp % 10) + '0'); + exp = exp / 10; + buffer[0] = (char)((exp % 10) + '0'); + buffer += 3; + } else if (fmt == 'f') { + fcvtbuf(value, precision, &decpt, &sign, cvtbuf, CVTBUFSIZE); + if (sign) { + *buffer++ = '-'; + } + if (*digits) { + if (decpt <= 0) { + *buffer++ = '0'; + *buffer++ = '.'; + for (pos = 0; pos < -decpt; pos++) { + *buffer++ = '0'; + } + while (*digits) { + *buffer++ = *digits++; + } + } else { + pos = 0; + while (*digits) { + if (pos++ == decpt) { + *buffer++ = '.'; + } + *buffer++ = *digits++; + } + } + } else { + *buffer++ = '0'; + if (precision > 0) { + *buffer++ = '.'; + for (pos = 0; pos < precision; pos++) { + *buffer++ = '0'; + } + } + } + } + + *buffer = '\0'; +} + +static void forcdecpt(char *buffer) +{ + while (*buffer) { + if (*buffer == '.') { + return; + } + if (*buffer == 'e' || *buffer == 'E') { + break; + } + + buffer++; + } + + if (*buffer) { + long n = (long)strlen(buffer); + while (n > 0) { + buffer[n + 1] = buffer[n]; + n--; + } + *buffer = '.'; + } else { + *buffer++ = '.'; + *buffer = '\0'; + } +} + +static void cropzeros(char *buffer) +{ + char *stop; + + while (*buffer && *buffer != '.') { + buffer++; + } + + if (*buffer++) { + while (*buffer && *buffer != 'e' && *buffer != 'E') { + buffer++; + } + stop = buffer--; + while (*buffer == '0') { + buffer--; + } + if (*buffer == '.') { + buffer--; + } + while ((*++buffer = *stop++)) {} + } +} + +static char *flt(char *str, double num, int size, int precision, char fmt, unsigned flags) +{ + char tmp[80]; + char c, sign; + int n, i; + + // Left align means no zero padding. + if (bitmask_check(flags, FLAGS_LEFT)) + bitmask_clear_assign(flags, FLAGS_ZEROPAD); + + // Determine padding and sign char. + c = bitmask_check(flags, FLAGS_ZEROPAD) ? '0' : ' '; + sign = 0; + if (bitmask_check(flags, FLAGS_SIGN)) { + if (num < 0.0) { + sign = '-'; + num = -num; + size--; + } else if (bitmask_check(flags, FLAGS_PLUS)) { + sign = '+'; + size--; + } else if (bitmask_check(flags, FLAGS_SPACE)) { + sign = ' '; + size--; + } + } + + // Compute the precision value. + if (precision < 0) { + // Default precision: 6. + precision = 6; + } else if (precision == 0 && fmt == 'g') { + // ANSI specified. + precision = 1; + } + + // Convert floating point number to text. + cfltcvt(num, tmp, fmt, precision); + + // '#' and precision == 0 means force a decimal point. + if (bitmask_check(flags, FLAGS_HASH) && (precision == 0)) { + forcdecpt(tmp); + } + + // 'g' format means crop zero unless '#' given. + if ((fmt == 'g') && !bitmask_check(flags, FLAGS_HASH)) { + cropzeros(tmp); + } + + n = strlen(tmp); + + // Output number with alignment and padding. + size -= n; + if (!bitmask_check(flags, FLAGS_ZEROPAD | FLAGS_LEFT)) { + while (size-- > 0) { + *str++ = ' '; + } + } + + if (sign) { + *str++ = sign; + } + + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (size-- > 0) { + *str++ = c; + } + } + + for (i = 0; i < n; i++) { + *str++ = tmp[i]; + } + + while (size-- > 0) { + *str++ = ' '; + } + + return str; +} + +int vsprintf(char *str, const char *fmt, va_list args) +{ + int base; + char *tmp; + char *s; + + // Flags to number(). + unsigned flags; + + // 'h', 'l', or 'L' for integer fields. + char qualifier; + + for (tmp = str; *fmt; fmt++) { + if (*fmt != '%') { + *tmp++ = *fmt; + + continue; + } + + // Process flags- + flags = 0; + repeat: + // This also skips first '%'. + fmt++; + switch (*fmt) { + case '-': + bitmask_set_assign(flags, FLAGS_LEFT); + goto repeat; + case '+': + bitmask_set_assign(flags, FLAGS_PLUS); + goto repeat; + case ' ': + bitmask_set_assign(flags, FLAGS_SPACE); + goto repeat; + case '#': + bitmask_set_assign(flags, FLAGS_HASH); + goto repeat; + case '0': + bitmask_set_assign(flags, FLAGS_ZEROPAD); + goto repeat; + } + + // Get the width of the output field. + int32_t field_width; + field_width = -1; + + if (isdigit(*fmt)) { + field_width = skip_atoi(&fmt); + } else if (*fmt == '*') { + fmt++; + field_width = va_arg(args, int32_t); + if (field_width < 0) { + field_width = -field_width; + bitmask_set_assign(flags, FLAGS_LEFT); + } + } + + /* Get the precision, thus the minimum number of digits for + * integers; max number of chars for from string. + */ + int32_t precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) { + precision = skip_atoi(&fmt); + } else if (*fmt == '*') { + ++fmt; + precision = va_arg(args, int); + } + if (precision < 0) { + precision = 0; + } + } + + // Get the conversion qualifier. + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + fmt++; + } + + // Default base. + base = 10; + + switch (*fmt) { + case 'c': + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (--field_width > 0) { + *tmp++ = ' '; + } + } + *tmp++ = va_arg(args, char); + while (--field_width > 0) { + *tmp++ = ' '; + } + continue; + + case 's': + s = va_arg(args, char *); + if (!s) { + s = ""; + } + + int32_t len = (int32_t)strnlen(s, (uint32_t)precision); + if (!bitmask_check(flags, FLAGS_LEFT)) { + while (len < field_width--) { + *tmp++ = ' '; + } + } + + int32_t it; + for (it = 0; it < len; ++it) { + *tmp++ = *s++; + } + while (len < field_width--) { + *tmp++ = ' '; + } + continue; + + case 'p': + if (field_width == -1) { + field_width = 2 * sizeof(void *); + bitmask_set_assign(flags, FLAGS_ZEROPAD); + } + tmp = number(tmp, (unsigned long)va_arg(args, void *), 16, field_width, precision, flags); + continue; + case 'n': + if (qualifier == 'l') { + long *ip = va_arg(args, long *); + *ip = (tmp - str); + } else { + int *ip = va_arg(args, int *); + *ip = (tmp - str); + } + continue; + case 'A': + bitmask_set_assign(flags, FLAGS_UPPERCASE); + break; + case 'a': + if (qualifier == 'l') + tmp = eaddr(tmp, va_arg(args, unsigned char *), field_width, precision, flags); + else + tmp = iaddr(tmp, va_arg(args, unsigned char *), field_width, precision, flags); + continue; + // Integer number formats - set up the flags and "break". + case 'o': + base = 8; + break; + + case 'X': + bitmask_set_assign(flags, FLAGS_UPPERCASE); + break; + + case 'x': + base = 16; + break; + + case 'd': + case 'i': + bitmask_set_assign(flags, FLAGS_SIGN); + + case 'u': + break; + case 'E': + case 'G': + case 'e': + case 'f': + case 'g': + tmp = flt(tmp, va_arg(args, double), field_width, precision, *fmt, bitmask_set(flags, FLAGS_SIGN)); + continue; + default: + if (*fmt != '%') + *tmp++ = '%'; + if (*fmt) + *tmp++ = *fmt; + else + --fmt; + continue; + } + + if (bitmask_check(flags, FLAGS_SIGN)) { + long num; + if (qualifier == 'l') { + num = va_arg(args, long); + } else if (qualifier == 'h') { + num = va_arg(args, short); + } else { + num = va_arg(args, int); + } + tmp = number(tmp, num, base, field_width, precision, flags); + } else { + unsigned long num; + if (qualifier == 'l') { + num = va_arg(args, unsigned long); + } else if (qualifier == 'h') { + num = va_arg(args, unsigned short); + } else { + num = va_arg(args, unsigned int); + } + tmp = number(tmp, num, base, field_width, precision, flags); + } + } + + *tmp = '\0'; + return tmp - str; +} + +int printf(const char *fmt, ...) +{ + char buffer[4096]; + va_list ap; + + // Start variabile argument's list. + va_start(ap, fmt); + int len = vsprintf(buffer, fmt, ap); + va_end(ap); + + // Write the contento to standard output. + puts(buffer); + + return len; +} + +int sprintf(char *str, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + int len = vsprintf(str, fmt, ap); + va_end(ap); + + return len; +} + +int fprintf(int fd, const char *fmt, ...) +{ + char buffer[4096]; + va_list ap; + + va_start(ap, fmt); + int len = vsprintf(buffer, fmt, ap); + va_end(ap); + + if (len > 0) { + if (write(fd, buffer, len) <= 0) + return EOF; + return len; + } + return -1; +} \ No newline at end of file diff --git a/mentos/CMakeLists.txt b/mentos/CMakeLists.txt index d546568..3dd47a5 100644 --- a/mentos/CMakeLists.txt +++ b/mentos/CMakeLists.txt @@ -1,340 +1,324 @@ -# ------------------------------------------------------------------------------ +# ============================================================================= +# Set the minimum required version of cmake. +cmake_minimum_required(VERSION 2.8) # Initialize the project. -project(MentOs C ASM) -set(PROJECT_NAME MentOs) +project(mentos C ASM) -# ------------------------------------------------------------------------------ -# Add operating system specific option. -if((${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Darwin") OR APPLE) # Apple MacOSx - set(CMAKE_ASM_COMPILER nasm) -elseif ((${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows") OR WIN32) # Windows - set(CMAKE_ASM_COMPILER ${CMAKE_SOURCE_DIR}/third_party/nasm/bin/nasm) -else () # Generic Unix System - set(CMAKE_ASM_COMPILER ${CMAKE_SOURCE_DIR}/third_party/nasm/bin/nasm) -endif () +# ============================================================================= +# Set the main file names. +set(KERNEL_NAME ${PROJECT_NAME}_kernel) +set(BOOTLOADER_NAME ${PROJECT_NAME}_bootloader) +set(BUDDY_SYSTEM_NAME ${PROJECT_NAME}_memory) +set(BUDDY_SYSTEM_FILE ${PROJECT_SOURCE_DIR}/lib${PROJECT_NAME}_memory.a) -# ------------------------------------------------------------------------------ -# Find doxygen -find_package(Doxygen) +# ============================================================================= +# Set the default build type to Debug. +if(NOT CMAKE_BUILD_TYPE) + message(STATUS "Setting build type to 'Debug' as none was specified.") + set(CMAKE_BUILD_TYPE + "Debug" + CACHE STRING "Choose the type of build." FORCE) +endif() -# Add option to generate documentation. -option(BUILD_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND}) +# ============================================================================= +# Define that this code is kernel code. +add_definitions(-D__KERNEL__) -if (BUILD_DOCUMENTATION) - if (NOT DOXYGEN_FOUND) - message(FATAL_ERROR "Doxygen is needed to build the documentation.") - endif (NOT DOXYGEN_FOUND) +# ============================================================================= +# Add the memory option. +option(USE_BUDDY_SYSTEM "Build using the buddysystem written by the user." OFF) - set(doxyfile_in ${CMAKE_SOURCE_DIR}/doc/dreamos.doxyfile) - set(doxyfile ${CMAKE_BINARY_DIR}/doxyfile) - - configure_file(${doxyfile_in} ${doxyfile} @ONLY) - - add_custom_target( - doc - COMMAND ${DOXYGEN_EXECUTABLE} ${doxyfile} - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMENT "Generating API documentation with Doxygen" - VERBATIM - ) - install(DIRECTORY ${CMAKE_BINARY_DIR}/html DESTINATION share/doc) -endif (BUILD_DOCUMENTATION) - -# ------------------------------------------------------------------------------ +# ============================================================================= # Add the scheduling option. set(SCHEDULER_TYPE "SCHEDULER_RR" - CACHE STRING - "Chose the type of scheduler: SCHEDULER_RR SCHEDULER_PRIORITY SCHEDULER_CFS") -set_property( - CACHE SCHEDULER_TYPE PROPERTY STRINGS - SCHEDULER_RR - SCHEDULER_PRIORITY - SCHEDULER_CFS) -if ("${SCHEDULER_TYPE}" STREQUAL "SCHEDULER_RR" OR - "${SCHEDULER_TYPE}" STREQUAL "SCHEDULER_PRIORITY" OR - "${SCHEDULER_TYPE}" STREQUAL "SCHEDULER_CFS") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D${SCHEDULER_TYPE}") - message(STATUS "Setting scheduler type to ${SCHEDULER_TYPE}.") -else () - message(FATAL_ERROR "Scheduler type ${SCHEDULER_TYPE} is not valid.") -endif () - -# ------------------------------------------------------------------------------ -# Add the option to enable buddy system. -option(ENABLE_BUDDY_SYSTEM "Enables the buddy system" OFF) -if (ENABLE_BUDDY_SYSTEM) - message(STATUS "Enabling buddy system.") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_BUDDYSYSTEM") -else (ENABLE_BUDDY_SYSTEM) - message(STATUS "Buddy system disabled.") -endif (ENABLE_BUDDY_SYSTEM) - -# ------------------------------------------------------------------------------ -# Set the compiler options (original). -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdlib") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fomit-frame-pointer") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-builtin") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-stack-protector") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=i686") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") - -# ------------------------------------------------------------------------------ -# Set the compiler options (extra). -set(CMAKE_C_FLAGS"${CMAKE_C_FLAGS} -Wall") -#set(CMAKE_C_FLAGS"${CMAKE_C_FLAGS} -Wpedantic") -#set(CMAKE_C_FLAGS"${CMAKE_C_FLAGS} -pedantic-errors") -#set(CMAKE_C_FLAGS"${CMAKE_C_FLAGS} -Wextra") -#set(CMAKE_C_FLAGS"${CMAKE_C_FLAGS} -Werror") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wconversion") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wcast-align") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wcast-qual") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wdisabled-optimization") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wfloat-equal") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wfloat-conversion") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat=2") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat-nonliteral") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat-security") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wformat-y2k") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wimport") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Winit-self") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Winline") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Winvalid-pch") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-long-long") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-field-initializers") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-format-attribute") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wmissing-include-dirs") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wpacked") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wpointer-arith") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wredundant-decls") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wshadow") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstack-protector") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wstrict-aliasing=2") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wswitch") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wswitch-default") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wswitch-enum") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunreachable-code") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused-function") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused-label") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused-parameter") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused-value") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wunused-variable") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wvariadic-macros") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wwrite-strings") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wsign-compare") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wsign-conversion") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wuninitialized") -#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fmessage-length=0") - -if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcommon") + CACHE STRING "Chose the type of scheduler: SCHEDULER_RR SCHEDULER_PRIORITY SCHEDULER_CFS SCHEDULER_EDF SCHEDULER_RM SCHEDULER_AEDF") +# List of schedulers. +set_property(CACHE SCHEDULER_TYPE PROPERTY STRINGS SCHEDULER_RR SCHEDULER_PRIORITY SCHEDULER_CFS SCHEDULER_EDF SCHEDULER_RM) +# Check which scheduler is currently active and export the related macro. +if("${SCHEDULER_TYPE}" STREQUAL "SCHEDULER_RR" + OR "${SCHEDULER_TYPE}" STREQUAL "SCHEDULER_PRIORITY" + OR "${SCHEDULER_TYPE}" STREQUAL "SCHEDULER_CFS" + OR "${SCHEDULER_TYPE}" STREQUAL "SCHEDULER_EDF" + OR "${SCHEDULER_TYPE}" STREQUAL "SCHEDULER_RM" + OR "${SCHEDULER_TYPE}" STREQUAL "SCHEDULER_AEDF") + # Add the define stating which scheduler is currently active. + add_definitions(-D${SCHEDULER_TYPE}) + # Notify the type of scheduler. + message(STATUS "Setting scheduler type to ${SCHEDULER_TYPE}.") +else() + message(FATAL_ERROR "Scheduler type ${SCHEDULER_TYPE} is not valid.") endif() -# ------------------------------------------------------------------------------ +# ============================================================================= +# Add the video option. +set(VIDEO_TYPE + "VGA_TEXT_MODE" + CACHE STRING "Chose the type of video: VGA_TEXT_MODE VGA_MODE_320_200_256 VGA_MODE_640_480_16 VGA_MODE_720_480_16") +# List of video tpes. +set_property(CACHE VIDEO_TYPE PROPERTY STRINGS VGA_TEXT_MODE VGA_MODE_320_200_256 VGA_MODE_640_480_16 VGA_MODE_720_480_16) +# Check which VIDEO is currently active and export the related macro. +if("${VIDEO_TYPE}" STREQUAL "VGA_TEXT_MODE" + OR "${VIDEO_TYPE}" STREQUAL "VGA_MODE_320_200_256" + OR "${VIDEO_TYPE}" STREQUAL "VGA_MODE_640_480_16" + OR "${VIDEO_TYPE}" STREQUAL "VGA_MODE_720_480_16") + add_definitions(-D${VIDEO_TYPE}) + message(STATUS "Setting video type to ${VIDEO_TYPE}.") +else() + message(FATAL_ERROR "Video type ${VIDEO_TYPE} is not valid.") +endif() -if (CMAKE_BUILD_TYPE MATCHES "Release") -elseif (CMAKE_BUILD_TYPE MATCHES "Debug") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDEBUG") -endif (CMAKE_BUILD_TYPE MATCHES "Release") +# ============================================================================= +# Warning flags. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wpedantic") +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic-errors") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-function") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-variable") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unknown-pragmas") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-but-set-variable") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") +# Set the compiler options. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdlib") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fomit-frame-pointer") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-builtin") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-pic") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-stack-protector") +if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcommon") +endif() +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=i686") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") -# ------------------------------------------------------------------------------ -# Set the assembly flags. -set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -g") -set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -O0") +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g3") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0") + add_definitions(-DDEBUG) + +elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") + add_definitions(-DRELEASE) + +endif(CMAKE_BUILD_TYPE STREQUAL "Debug") + +# ============================================================================= +# Enable specific compilers for ASM. +enable_language(ASM) +# Find the NASM compiler. +find_program( + ASM_COMPILER + NAMES nasm + HINTS /usr/bin/ ${CMAKE_SOURCE_DIR}/third_party/nasm/bin) +# Check that we have found the compiler. +if(NOT ASM_COMPILER) + message(FATAL_ERROR "ASM compiler not found!") +endif(NOT ASM_COMPILER) +# Set the asm compiler. +set(CMAKE_ASM_COMPILER ${ASM_COMPILER}) +# Set the assembly compiler flags. set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -f elf") -set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -F dwarf") -set(CMAKE_ASM_COMPILE_OBJECT " -o ") +set(CMAKE_ASM_COMPILE_OBJECT " -o ") +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -g") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -O0") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -F dwarf") +elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -g") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -O3") +endif(CMAKE_BUILD_TYPE STREQUAL "Debug") -# ------------------------------------------------------------------------------ +# ============================================================================= # Add the includes. -include_directories( - inc - inc/descriptor_tables - inc/devices - inc/drivers - inc/drivers/keyboard - inc/fs - inc/hardware - inc/io - inc/kernel - inc/libc - inc/lng - inc/mem - inc/misc - inc/sys - inc/system - inc/process - inc/elf - inc/ui - inc/ui/shell - inc/ui/command - inc/ui/init -) - -# ------------------------------------------------------------------------------ -# Add the source files. -set(SOURCE_FILES - boot.asm +include_directories(inc ../libc/inc) +# ============================================================================= +# Add kernel library. +if(USE_BUDDY_SYSTEM) + add_library( + ${KERNEL_NAME} src/kernel.c src/multiboot.c - src/devices/pci.c src/devices/fpu.c - src/drivers/ata.c + src/drivers/rtc.c src/drivers/fdc.c src/drivers/mouse.c src/drivers/keyboard/keyboard.c src/drivers/keyboard/keymap.c - - src/fs/fcntl.c src/fs/initrd.c src/fs/vfs.c src/fs/read_write.c - - src/sys/module.c - src/sys/unistd.c + src/fs/open.c + src/fs/stat.c + src/fs/readdir.c + src/fs/procfs.c + src/fs/ioctl.c + src/fs/namei.c src/hardware/timer.c src/hardware/cpuid.c src/hardware/pic8259.c src/io/port_io.c src/io/mm_io.c src/io/video.c - + src/io/stdio.c + src/io/proc_video.c + src/io/proc_running.c + src/io/proc_system.c + src/io/vga/vga.c + src/ipc/msg.c + src/ipc/sem.c + src/ipc/shm.c src/kernel/sys.c - - src/libc/ctype.c - src/libc/stdio.c - src/libc/string.c - src/libc/vsprintf.c - src/libc/queue.c - src/libc/stdlib.c - src/libc/spinlock.c - src/libc/list.c - src/libc/ordered_array.c - src/libc/mutex.c - src/libc/libgen.c - src/libc/strerror.c - src/libc/tree.c - src/libc/bitset.c - src/libc/fcvt.c - src/libc/rbtree.c - src/libc/math.c - src/libc/hashmap.c - - src/libc/unistd/getppid.c - src/libc/unistd/getpid.c - src/libc/unistd/exit.c - src/libc/unistd/vfork.c - src/libc/unistd/read.c - src/libc/unistd/write.c - src/libc/unistd/execve.c - src/libc/unistd/nice.c - src/libc/unistd/open.c - src/libc/unistd/reboot.c - src/libc/unistd/waitpid.c - src/libc/unistd/chdir.c - src/libc/unistd/getcwd.c - + src/klib/assert.c + src/klib/ctype.c + src/klib/mutex.c + src/klib/string.c + src/klib/vsprintf.c + src/klib/vscanf.c + src/klib/time.c + src/klib/libgen.c + src/klib/strerror.c + src/klib/math.c + src/klib/fcvt.c + src/klib/spinlock.c + src/klib/rbtree.c + src/klib/ndtree.c + src/klib/hashmap.c + src/klib/list.c src/mem/kheap.c src/mem/paging.c + src/mem/slab.c + src/mem/vmem_map.c src/mem/zone_allocator.c - - src/misc/bitops.c - src/misc/clock.c + src/mem/buddysystem.c src/misc/debug.c - src/sys/dirent.c - src/sys/stat.c - src/sys/utsname.c - #src/sys/shm.c src/elf/elf.c - src/descriptor_tables/gdt.c - src/descriptor_tables/gdt.asm + src/descriptor_tables/gdt.S src/descriptor_tables/interrupt.c src/descriptor_tables/exception.c - src/descriptor_tables/interrupt.asm - src/descriptor_tables/exception.asm + src/descriptor_tables/interrupt.S + src/descriptor_tables/exception.S src/descriptor_tables/idt.c - src/descriptor_tables/idt.asm + src/descriptor_tables/idt.S src/descriptor_tables/tss.c - src/descriptor_tables/tss.asm - - src/process/scheduler.c + src/descriptor_tables/tss.S src/process/scheduler_algorithm.c + src/process/scheduler.c src/process/process.c - src/process/user.asm - + src/process/wait.c + src/process/user.S + src/sys/utsname.c + src/sys/module.c src/system/errno.c src/system/panic.c - src/system/syscall.c src/system/printk.c + src/system/signal.c + src/system/syscall.c) +else(USE_BUDDY_SYSTEM) + add_library( + ${KERNEL_NAME} + src/kernel.c + src/multiboot.c + src/devices/pci.c + src/devices/fpu.c + src/drivers/ata.c + src/drivers/rtc.c + src/drivers/fdc.c + src/drivers/mouse.c + src/drivers/keyboard/keyboard.c + src/drivers/keyboard/keymap.c + src/fs/initrd.c + src/fs/vfs.c + src/fs/read_write.c + src/fs/open.c + src/fs/stat.c + src/fs/readdir.c + src/fs/procfs.c + src/fs/ioctl.c + src/fs/namei.c + src/hardware/timer.c + src/hardware/cpuid.c + src/hardware/pic8259.c + src/io/port_io.c + src/io/mm_io.c + src/io/video.c + src/io/stdio.c + src/io/proc_video.c + src/io/proc_running.c + src/io/proc_system.c + src/io/vga/vga.c + src/ipc/msg.c + src/ipc/sem.c + src/ipc/shm.c + src/kernel/sys.c + src/klib/assert.c + src/klib/ctype.c + src/klib/mutex.c + src/klib/string.c + src/klib/vsprintf.c + src/klib/vscanf.c + src/klib/time.c + src/klib/libgen.c + src/klib/strerror.c + src/klib/math.c + src/klib/fcvt.c + src/klib/spinlock.c + src/klib/rbtree.c + src/klib/ndtree.c + src/klib/hashmap.c + src/klib/list.c + src/mem/kheap.c + src/mem/paging.c + src/mem/slab.c + src/mem/vmem_map.c + src/mem/zone_allocator.c + src/misc/debug.c + src/elf/elf.c + src/descriptor_tables/gdt.c + src/descriptor_tables/gdt.S + src/descriptor_tables/interrupt.c + src/descriptor_tables/exception.c + src/descriptor_tables/interrupt.S + src/descriptor_tables/exception.S + src/descriptor_tables/idt.c + src/descriptor_tables/idt.S + src/descriptor_tables/tss.c + src/descriptor_tables/tss.S + src/process/scheduler_algorithm.c + src/process/scheduler.c + src/process/process.c + src/process/wait.c + src/process/user.S + src/sys/utsname.c + src/sys/module.c + src/system/errno.c + src/system/panic.c + src/system/printk.c + src/system/signal.c + src/system/syscall.c) + # Link the exercise libraries. + target_link_libraries(${KERNEL_NAME} ${BUDDY_SYSTEM_FILE}) +endif(USE_BUDDY_SYSTEM) +# ============================================================================= +# Add bootloader library. +add_library(${BOOTLOADER_NAME} src/boot.c boot.S) - src/ui/command/cmd_cd.c - src/ui/command/cmd_clear.c - src/ui/command/cmd_cpuid.c - src/ui/command/cmd_credits.c - src/ui/command/cmd_date.c - src/ui/command/cmd_drv_load.c - src/ui/command/cmd_echo.c - src/ui/command/cmd_logo.c - src/ui/command/cmd_ls.c - src/ui/command/cmd_mkdir.c - src/ui/command/cmd_more.c - src/ui/command/cmd_newfile.c - src/ui/command/cmd_poweroff.c - src/ui/command/cmd_ps.c - src/ui/command/cmd_pwd.c - src/ui/command/cmd_rm.c - src/ui/command/cmd_rmdir.c - src/ui/command/cmd_showpid.c - src/ui/command/cmd_sleep.c - #src/ui/command/cmd_tester.c - src/ui/command/cmd_touch.c - src/ui/command/cmd_uname.c - src/ui/command/cmd_whoami.c - src/ui/command/cmd_ipcs.c - src/ui/command/cmd_ipcrm.c - src/ui/command/cmd_nice.c - - src/ui/init/init.c - - src/ui/shell/shell.c - src/ui/shell/shell_login.c - ) - -if (ENABLE_BUDDY_SYSTEM MATCHES "ON") - set(SOURCE_FILES - ${SOURCE_FILES} - src/mem/buddysystem.c) -endif (ENABLE_BUDDY_SYSTEM MATCHES "ON") - -# ------------------------------------------------------------------------------ -# Add the executable. -add_library(${PROJECT_NAME} ${SOURCE_FILES}) - -# ------------------------------------------------------------------------------ +# ============================================================================= # Build the kernel. -add_custom_command( - TARGET MentOs - POST_BUILD - COMMAND ${CMAKE_LINKER} - -melf_i386 - -static - --oformat elf32-i386 - -Ttext 0x100000 - --output=${CMAKE_CURRENT_BINARY_DIR}/kernel.bin - --script=${CMAKE_CURRENT_SOURCE_DIR}/kernel.lds - ${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.a - -Map ${CMAKE_CURRENT_BINARY_DIR}/kernel.map -) +add_custom_target( + kernel-bootloader.bin ALL + COMMAND mkdir -p ${PROJECT_BINARY_DIR}/../debug_sym + COMMAND ${CMAKE_LINKER} -melf_i386 -static --oformat elf32-i386 --output kernel.bin --script ${CMAKE_CURRENT_SOURCE_DIR}/kernel.lds -Map kernel.map -u kmain + $ ${BUDDY_SYSTEM_FILE} + COMMAND objcopy -I binary -O elf32-i386 -B i386 kernel.bin kernel.bin.o + COMMAND ${CMAKE_LINKER} -melf_i386 -static --oformat elf32-i386 --output kernel-bootloader.bin --script ${CMAKE_CURRENT_SOURCE_DIR}/boot.lds -Map kernel-bootloader.map + kernel.bin.o $ + DEPENDS ${KERNEL_NAME} + DEPENDS ${BOOTLOADER_NAME}) diff --git a/mentos/boot.asm b/mentos/boot.S similarity index 50% rename from mentos/boot.asm rename to mentos/boot.S index e5bc0de..fffbe63 100644 --- a/mentos/boot.asm +++ b/mentos/boot.S @@ -1,26 +1,32 @@ ; MentOS, The Mentoring Operating system project ; @file boot.asm ; @brief Kernel start location, multiboot header -; @copyright (c) 2019 This file is distributed under the MIT License. +; @copyright (c) 2014-2021 This file is distributed under the MIT License. ; See LICENSE.md for details. -[BITS 32] ; All instructions should be 32-bit. -[EXTERN kmain] ; The start point of our C code +bits 32 ; All instructions should be 32-bit. +extern boot_main ; The start point of our C code + +; The magic field should contain this. +MULTIBOOT_HEADER_MAGIC equ 0x1BADB002 +; This should be in %eax. +MULTIBOOT_BOOTLOADER_MAGIC equ 0x2BADB002 + +; = Specify what GRUB should PROVIDE ========================================= +; Align the kernel and kernel modules on i386 page (4KB) boundaries. +MULTIBOOT_PAGE_ALIGN equ 0x00000001 +; Provide the kernel with memory information. +MULTIBOOT_MEMORY_INFO equ 0x00000002 +; Must pass video information to OS. +MULTIBOOT_VIDEO_MODE equ 0x00000004 +; ----------------------------------------------------------------------------- -; Grub is informed with this flag to load -; the kernel and kernel modules on a page boundary. -MBOOT_PAGE_ALIGN equ 1<<0 -; Grub is informed with this flag to provide the kernel -; with memory information. -MBOOT_MEM_INFO equ 1<<1 -; This is the multiboot magic value. -MBOOT_HEADER_MAGIC equ 0x1BADB002 ; This is the flag combination that we prepare for Grub ; to read at kernel load time. -MBOOT_HEADER_FLAGS equ MBOOT_PAGE_ALIGN | MBOOT_MEM_INFO +MULTIBOOT_HEADER_FLAGS equ (MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_VIDEO_MODE) ; Grub reads this value to make sure it loads a kernel ; and not just garbage. -MBOOT_CHECKSUM equ - (MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS) +MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS) LOAD_MEMORY_ADDRESS equ 0x00000000 ; reserve (1024*1024) for the stack on a doubleword boundary @@ -33,57 +39,65 @@ section .multiboot_header align 4 ; This is the GRUB Multiboot header. multiboot_header: - dd MBOOT_HEADER_MAGIC - dd MBOOT_HEADER_FLAGS - dd MBOOT_CHECKSUM + ; magic + dd MULTIBOOT_HEADER_MAGIC + ; flags + dd MULTIBOOT_HEADER_FLAGS + ; checksum + dd MULTIBOOT_CHECKSUM ; ----------------------------------------------------------------------------- ; SECTION (data) ; ----------------------------------------------------------------------------- -section .data, nobits -align 4096 -[GLOBAL boot_page_dir] -boot_page_dir: - resb 0x1000 -boot_page_tabl: - resb 0x1000 +; section .data nobits +; align 4096 ; ----------------------------------------------------------------------------- ; SECTION (text) ; ----------------------------------------------------------------------------- section .text -[GLOBAL kernel_entry] -kernel_entry: +global boot_entry +boot_entry: ; Clear interrupt flag [IF = 0]; 0xFA cli - ; To set up a stack, we simply set the esp register to point to the top of - ; our stack (as it grows downwards). + ; To set up a stack, we simply set the esp register to point to the top of + ; our stack (as it grows downwards). mov esp, stack_top ; pass the initial ESP push esp - ;mov ebp, esp ; pass Multiboot info structure push ebx ; pass Multiboot magic number push eax - ; Call the kmain() function inside kernel.c - call kmain + ; Call the boot_main() function inside boot.c + call boot_main ; Set interrupt flag [IF = 1]; 0xFA - ; Clear interrupts and hang if we return from kmain + ; Clear interrupts and hang if we return from boot_main cli hang: hlt jmp hang +global boot_kernel +boot_kernel: + mov edx, [esp + 4] ; stack_pointer + mov ebx, [esp + 8] ; entry + mov eax, [esp + 12] ; boot info + mov esp, edx ; set stack pointer + push eax ; push the boot info + call ebx ; call the kernel main + ; ----------------------------------------------------------------------------- ; SECTION (bss) ; ----------------------------------------------------------------------------- section .bss -[GLOBAL stack_bottom] -[GLOBAL stack_top] align 16 + +global stack_bottom stack_bottom: resb KERNEL_STACK_SIZE + +global stack_top stack_top: ; the top of the stack is the bottom because the stack counts down diff --git a/mentos/boot.lds b/mentos/boot.lds new file mode 100644 index 0000000..c99c023 --- /dev/null +++ b/mentos/boot.lds @@ -0,0 +1,59 @@ +OUTPUT_FORMAT("elf") +OUTPUT_ARCH(i386) + +ENTRY(boot_entry) + +BOOTLOADER_PHYSICAL_ADDRESS = 0x00100000; + +MEMORY { + BOOTLOADER_MEM : ORIGIN = 0x00000000, LENGTH = 128M +} + +SECTIONS +{ + . = BOOTLOADER_PHYSICAL_ADDRESS; + _bootloader_start = .; + .multiboot . : AT(ADDR(.multiboot)) + { + . = ALIGN(4); + _multiboot_header_start = .; + *(.multiboot_header) + _multiboot_header_end = .; + } > BOOTLOADER_MEM + + /* Put the .text section. */ + .text . : AT(ADDR(.text)) + { + _text_start = .; + *(.text) + _text_end = .; + } > BOOTLOADER_MEM + + /* Read-only data. */ + .rodata ALIGN(4K) : AT(ADDR(.rodata)) + { + _rodata_start = .; + *(.rodata) + _rodata_end = .; + } > BOOTLOADER_MEM + + /* Read-write data (initialized) */ + .data ALIGN(4K) : AT(ADDR(.data)) + { + _data_start = .; + *(.data) + _data_end = .; + } > BOOTLOADER_MEM + + /* Read-write data (uninitialized) and stack */ + .bss ALIGN(4K) : AT(ADDR(.bss)) + { + _bss_start = .; + *(.bss*) + _bss_end = .; + } > BOOTLOADER_MEM + + /* Put a symbol end here, it tells us where all the kernel code/data ends, + it means everything after 'end' can be used for something else. */ + _bootloader_end = .; +} diff --git a/mentos/inc/boot.h b/mentos/inc/boot.h new file mode 100644 index 0000000..cce5be3 --- /dev/null +++ b/mentos/inc/boot.h @@ -0,0 +1,85 @@ +/// MentOS, The Mentoring Operating system project +/// @file boot.h +/// @brief Bootloader structures +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#ifndef MENTOS_BOOT_H +#define MENTOS_BOOT_H + +#include "multiboot.h" + +/// @brief Mentos structure to communicate bootloader info to the kernel +typedef struct boot_info_t { + /// Boot magic number. + uint32_t magic; + + /* + * Bootloader physical range + * it can be overwritten to do other things + * */ + + /// bootloader code start + uint32_t bootloader_phy_start; + /// bootloader code end + uint32_t bootloader_phy_end; + + /* + * Kernel virtual and physical range + * must not be overwritten + * it's assumed to be contiguous, so section holes are just wasted space + * */ + + /// kernel physical code start + uint32_t kernel_phy_start; + /// kernel physical code end + uint32_t kernel_phy_end; + + /// kernel code start + uint32_t kernel_start; + /// kernel code end + uint32_t kernel_end; + + /// kernel size. + uint32_t kernel_size; + + /// Address after the modules. + uint32_t module_end; + + /* + * Range of addressable lowmemory, is memory that is available + * (not used by the kernel executable nor by the bootloader) + * and can be accessed by the kernel at any time + * */ + + /// lowmem physical addressable start + uint32_t lowmem_phy_start; + /// lowmem physical addressable end + uint32_t lowmem_phy_end; + + /// lowmem addressable start + uint32_t lowmem_start; + /// lowmem addressable end + uint32_t lowmem_end; + + /// stack end (comes after lowmem_end, and is the end of the low mapped memory) + uint32_t stack_end; + + /* + * Range of non-addressable highmemory, is memory that can be + * accessed by the kernel only if previously mapped + * */ + + /// highmem addressable start + uint32_t highmem_phy_start; + /// highmem addressable end + uint32_t highmem_phy_end; + + /// multiboot info + multiboot_info_t *multiboot_header; + + /// stack suggested start address (also set by the bootloader) + uint32_t stack_base; +} boot_info_t; + +#endif //MENTOS_BOOT_H diff --git a/mentos/inc/descriptor_tables/gdt.h b/mentos/inc/descriptor_tables/gdt.h index a7728e4..9cb385d 100644 --- a/mentos/inc/descriptor_tables/gdt.h +++ b/mentos/inc/descriptor_tables/gdt.h @@ -1,40 +1,122 @@ /// MentOS, The Mentoring Operating system project -/// @file gdt.h +/// @file gdt.h /// @brief Data structures concerning the Global Descriptor Table (GDT). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "stdint.h" -/// @brief Access flags, determines what ring this segment can be used in. -typedef enum gdt_access_option_t -{ - /// Identifies a kernel segment. - KERNEL = 0x00, - /// Identifies a user segment. - USER = 0x03, - /// Identifies a code segment. - CODE = 0x10, - /// Identifies a data segment. - DATA = 0x10, - /// Segment is present. - PRESENT = 0x80, -} __attribute__ ((__packed__)) gdt_access_option_t; +/// @defgroup gdt_bits List of GDT Bits. +/// @brief Bitmasks used to access to specific bits of the GDT. +/// @details +/// @image html gdt_bits.png +/// #### PR (Present bit) +/// This must be 1 for all valid selectors. +/// +/// #### PRIV (Privilege bits) +/// Contains the ring level, specifically: +/// - `00 (0)` = highest (kernel), +/// - `11 (3)` = lowest (user applications). +/// +/// #### S (Descriptor type) +/// This bit should be set for code or data segments and should be cleared +/// for system segments (eg. a Task State Segment). +/// +/// #### EX (Executable bit) +/// - If 1 code in this segment can be executed, ie. a code selector. +/// - If 0 it is a data selector. +/// +/// #### DC (Direction bit/Conforming bit) +/// Direction bit for data selectors: Tells the direction. 0 the segment grows up. +/// 1 the segment grows down, ie., the offset has to be greater than the limit. +/// +/// Conforming bit for code selectors: +/// - If 1 code in this segment can be executed from an equal or lower privilege +/// level. For example, code in ring 3 can far-jump to conforming code in a ring 2 segment. +/// The privl-bits represent the highest privilege level that is allowed to execute +/// the segment. For example, code in ring 0 cannot far-jump to a conforming code segment +/// with privl==0x2, while code in ring 2 and 3 can. Note that the privilege level remains +/// the same, ie. a far-jump form ring 3 to a privl==2-segment remains in ring 3 +/// after the jump. +/// - If 0 code in this segment can only be executed from the ring set in privl. +/// +/// #### RW (Readable bit/Writable bit) +/// - Readable bit for code selectors: +/// - Whether read access for this segment is allowed. +/// - Write access is never allowed for code segments. +/// - Writable bit for data selectors: +/// - Whether write access for this segment is allowed. +/// - Read access is always allowed for data segments. +/// +/// #### AC (Accessed bit) +/// Just set to 0. The CPU sets this to 1 when the segment is accessed. +/// +/// #### GR (Granularity bit) +/// - If 0 the limit is in 1 B blocks (byte granularity); +/// - if 1 the limit is in 4 KiB blocks (page granularity). +/// +/// #### SZ (Size bit) +/// - If 0 the selector defines 16 bit protected mode; +/// - if 1 it defines 32 bit protected mode. +/// +/// You can have both 16 bit and 32 bit selectors at once. +/// +/// @{ -/// @brief Options for the second option. -typedef enum gdt_granularity_option_t -{ - /// Granularity. - GRANULARITY = 0x80, - /// Szbits. - SZBITS = 0x40 -} __attribute__ ((__packed__)) gdt_granularity_option_t; +/// @brief Present bit. +/// This must be 1 for all valid selectors. +#define GDT_PRESENT 128U // 0b10000000U + +/// @brief Sets the 2 privilege bits (ring level) to 0 = highest (kernel). +#define GDT_KERNEL 0U // 0b00000000U + +/// @brief Sets the 2 privilege bits (ring level) to 3 = lowest (user applications). +#define GDT_USER 96U // 0b01100000U + +/// @brief Descriptor type. +/// This bit should be set for code or data segments and should be cleared for system segments (eg. a Task State Segment) +#define GDT_S 16U // 0b00010000U + +/// @brief Executable bit. +/// If 1 code in this segment can be executed, ie. a code selector. If 0 it is a data selector. +#define GDT_EX 8U // 0b00001000U + +/// @brief Direction bit/Conforming bit. +#define GDT_DC 4U // 0b00000100U + +/// @brief Readable bit/Writable bit. +#define GDT_RW 2U // 0b00000010U + +/// @brief Accessed bit. +/// Just set to 0. The CPU sets this to 1 when the segment is accessed. +#define GDT_AC 1U // 0b00000001U + +/// @brief Identifies an executable code segment. +#define GDT_CODE (GDT_S | GDT_EX) + +/// @brief Identifies a writable data segment. +#define GDT_DATA (GDT_S | GDT_RW) + +/// @brief Granularity bit. +/// If 0 the limit is in 1 B blocks (byte granularity), +/// if 1 the limit is in 4 KiB blocks (page granularity). +#define GDT_GRANULARITY 128U // 0b10000000U + +/// @brief Size bit. +/// If 0 the selector defines 16 bit protected mode. +/// If 1 it defines 32 bit protected mode. +/// You can have both 16 bit and 32 bit selectors at once. +#define GDT_OPERAND_SIZE 64U // 0b01000000U + +/// @} + +/// @brief Used in IDT for padding. +#define IDT_PADDING 14U // 0b00001110U /// @brief Data structure representing a GDT descriptor. -typedef struct gdt_descriptor_t -{ +typedef struct gdt_descriptor_t { /// The lower 16 bits of the limit. uint16_t limit_low; /// The lower 16 bits of the base. @@ -50,8 +132,7 @@ typedef struct gdt_descriptor_t } __attribute__((packed)) gdt_descriptor_t; /// @brief Data structure used to load the GDT into the GDTR. -typedef struct gdt_pointer_t -{ +typedef struct gdt_pointer_t { /// The size of the GDT (entry number). uint16_t limit; /// The starting address of the GDT. @@ -70,10 +151,6 @@ void init_gdt(); /// @param index The index inside the GDT. /// @param base Memory address where the segment we are defining starts. /// @param limit The memory address which determines the end of the segnment. -/// @param _access Type (4bit) - S (1) bit -DPL (2 bit) - P(1 bit). -/// @param _granul SegLimit_hi(4 bit) AVL(1 bit) L(1 bit) D/B(1 bit) G(1bit). -void gdt_set_gate(uint8_t index, - uint32_t base, - uint32_t limit, - uint8_t _access, - uint8_t _granul); +/// @param access Type (4bit) - S (1) bit -DPL (2 bit) - P(1 bit). +/// @param granul SegLimit_hi(4 bit) AVL(1 bit) L(1 bit) D/B(1 bit) G(1bit). +void gdt_set_gate(uint8_t index, uint32_t base, uint32_t limit, uint8_t access, uint8_t granul); diff --git a/mentos/inc/descriptor_tables/idt.h b/mentos/inc/descriptor_tables/idt.h index ee388c3..18185d4 100644 --- a/mentos/inc/descriptor_tables/idt.h +++ b/mentos/inc/descriptor_tables/idt.h @@ -1,32 +1,25 @@ /// MentOS, The Mentoring Operating system project /// @file idt.h /// @brief Data structures concerning the Interrupt Descriptor Table (IDT). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "isr.h" #include "stdint.h" -#include /// The maximum dimension of the IDT. #define IDT_SIZE 256 - /// When an exception occurs whose entry is a Task Gate, a task switch results. -#define TASK_GATE 0x5 - +#define TASK_GATE 0x5 /// Used to specify an interrupt service routine (16-bit). -#define INT16_GATE 0x6 - +#define INT16_GATE 0x6 /// @brief Similar to an Interrupt gate (16-bit). -#define TRAP16_GATE 0x7 - +#define TRAP16_GATE 0x7 /// Used to specify an interrupt service routine (32-bit). -#define INT32_GATE 0xE - +#define INT32_GATE 0xE /// @brief Similar to an Interrupt gate (32-bit). -#define TRAP32_GATE 0xF +#define TRAP32_GATE 0xF /* * Trap and Interrupt gates are similar, and their descriptors are @@ -36,21 +29,23 @@ */ /// @brief This structure describes one interrupt gate. -typedef struct idt_descriptor_t -{ +typedef struct idt_descriptor_t { + /// TODO: Comment. unsigned short offset_low; + /// Segment selector. unsigned short seg_selector; - // This will ALWAYS be set to 0. + /// This will ALWAYS be set to 0. unsigned char null_par; - // |P|DPL|01110|. - // P present, DPL required Ring (2bits). + /// @brief IDT descriptor options: + /// |P|DPL|01110|. + /// P present, DPL required Ring (2bits). unsigned char options; + /// TODO: Comment. unsigned short offset_high; } __attribute__((packed)) idt_descriptor_t; /// @brief A pointer structure used for informing the CPU about our IDT. -typedef struct idt_pointer_t -{ +typedef struct idt_pointer_t { /// The size of the IDT (entry number). unsigned short int limit; /// The start address of the IDT. @@ -60,17 +55,9 @@ typedef struct idt_pointer_t /// @brief Initialise the interrupt descriptor table. void init_idt(); -/// @brief Use this function to set an entry in the IDT. -/// @param index Indice della IDT. -/// @param handler Puntatore alla funzione che gestira' l'interrupt/Eccezione -/// @param options Le opzioni del descrittore (PRESENT,NOTPRESENT,KERNEL,USER) -/// @param seg_sel Il selettore del segmento della GDT. -void idt_set_gate(uint8_t index, - interrupt_handler_t handler, - uint16_t options, - uint8_t seg_sel); +// == List of exceptions generated internally by the CPU ====================== +//! @cond Doxygen_Suppress -//==== List of exceptions generated internally by the CPU ====================== extern void INT_0(); extern void INT_1(); @@ -136,9 +123,8 @@ extern void INT_30(); extern void INT_31(); extern void INT_80(); -//============================================================================== -//==== List of interrupts generated by PIC ===================================== +// == List of interrupts generated by PIC ===================================== extern void IRQ_0(); extern void IRQ_1(); @@ -170,4 +156,6 @@ extern void IRQ_13(); extern void IRQ_14(); extern void IRQ_15(); -//============================================================================== + +//! @endcond +// ============================================================================ diff --git a/mentos/inc/descriptor_tables/isr.h b/mentos/inc/descriptor_tables/isr.h index c8c5217..9c36db0 100644 --- a/mentos/inc/descriptor_tables/isr.h +++ b/mentos/inc/descriptor_tables/isr.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file isr.h /// @brief Data structures concerning the Interrupt Service Routines (ISRs). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -19,9 +19,9 @@ typedef void (*interrupt_handler_t)(pt_regs *f); /// prints the rose exceptions and stops kernel execution. void isrs_init(); -/// @brief For each interrupt irqs_init sets a default handler which +/// @brief For each interrupt irq_init sets a default handler which /// prints the rose IRQ line and stops kernel execution. -void irqs_init(); +void irq_init(); /* Even if an interrupt service routine is called for exceptions and * interrupts, we use two distinct methods to logically setup an ISR to @@ -31,99 +31,52 @@ void irqs_init(); * for a same IRQ. */ -/// @brief Installs an ISR to handle an exception. -/// @param i Exception identifier. -/// @param handler Exception handler. -/// @return 0 On success, -1 otherwise. -int isr_install_handler(uint32_t i, interrupt_handler_t handler, - char *description); +/// @brief Installs an ISR to handle an exception. +/// @param i Exception identifier. +/// @param handler Exception handler. +/// @param description Exception description. +/// @return 0 on success, -1 otherwise. +int isr_install_handler(uint32_t i, interrupt_handler_t handler, char *description); -/// @brief Installs an ISR to handle an interrupt. -/// @param i Interrupt identifier. -/// @param handler Interrupt handler. -/// @return 0 On success, -1 otherwise. -int irq_install_handler(uint32_t i, interrupt_handler_t handler, - char *description); +/// @brief Installs an ISR to handle an interrupt. +/// @param i Interrupt identifier. +/// @param handler Interrupt handler. +/// @param description Interrupt description. +/// @return 0 on success, -1 otherwise. +int irq_install_handler(unsigned i, interrupt_handler_t handler, char *description); -/// @brief Method called by CPU to handle interrupts. -/// @param f The interrupt stack frame. +/// @brief Method called by CPU to handle interrupts. +/// @param f The interrupt stack frame. extern void irq_handler(pt_regs *f); -/// @brief Method called by CPU to handle exceptions. -/// @param f The interrupt stack frame. +/// @brief Method called by CPU to handle exceptions. +/// @param f The interrupt stack frame. extern void isq_handler(pt_regs *f); //==== List of exceptions generated internally by the CPU ====================== -/// @brief DE Divide Error. -#define DIVIDE_ERROR 0 - -/// @brief DB Debug. -#define DEBUG_EXC 1 - -/// @brief Non Mascable Interrupt. -#define NMI_INTERRUPT 2 - -/// @brief BP Breakpoint. -#define BREAKPOINT 3 - -/// @brief OF Overflow. -#define OVERFLOW 4 - -/// @brief BR Bound Range Exception. -#define BOUND_RANGE_EXCEED 5 - -/// @brief UD Invalid OpCode Exception. -#define INVALID_OPCODE 6 - -/// @brief NM Device Not Available. -#define DEV_NOT_AVL 7 - -/// @brief DF Double Fault. -#define DOUBLE_FAULT 8 - -/// @brief Coprocessor Segment Overrun. -#define COPROC_SEG_OVERRUN 9 - -/// @brief TS Invalid TSS. -#define INVALID_TSS 10 - -/// @brief NP Segment Not Present. -#define SEGMENT_NOT_PRESENT 11 - -/// @brief SS Stack Segment Fault. -#define STACK_SEGMENT_FAULT 12 - -/// @brief GP General Protection. -#define GENERAL_PROTECTION 13 - -/// @brief PF Page Fault. -#define PAGE_FAULT 14 - -/// @brief XX Reserverd. -#define INT_RSV 15 - -/// @brief MF Floating Point. -#define FLOATING_POINT_ERR 16 - -/// @brief AC Alignment Check. -#define ALIGNMENT_CHECK 17 - -/// @brief MC Machine Check. -#define MACHINE_CHECK 18 - -/// @brief XF Streaming SIMD Exception. -#define SIMD_FP_EXC 19 - -/// @brief Virtualization Exception. -#define VIRT_EXC 20 - -/// @brief Reserved [21-29]. -/// @brief Security Exception. -#define SECURITY_EXC 30 - -/// @brief Triple Fault -#define TRIPLE_FAULT 31 - -/// @brief System call interrupt. -#define SYSTEM_CALL 80 +#define DIVIDE_ERROR 0 ///< DE Divide Error. +#define DEBUG_EXC 1 ///< DB Debug. +#define NMI_INTERRUPT 2 ///< Non Mascable Interrupt. +#define BREAKPOINT 3 ///< BP Breakpoint. +#define OVERFLOW 4 ///< OF Overflow. +#define BOUND_RANGE_EXCEED 5 ///< BR Bound Range Exception. +#define INVALID_OPCODE 6 ///< UD Invalid OpCode Exception. +#define DEV_NOT_AVL 7 ///< NM Device Not Available. +#define DOUBLE_FAULT 8 ///< DF Double Fault. +#define COPROC_SEG_OVERRUN 9 ///< Coprocessor Segment Overrun. +#define INVALID_TSS 10 ///< TS Invalid TSS. +#define SEGMENT_NOT_PRESENT 11 ///< NP Segment Not Present. +#define STACK_SEGMENT_FAULT 12 ///< SS Stack Segment Fault. +#define GENERAL_PROTECTION 13 ///< GP General Protection. +#define PAGE_FAULT 14 ///< PF Page Fault. +#define INT_RSV 15 ///< XX Reserverd. +#define FLOATING_POINT_ERR 16 ///< MF Floating Point. +#define ALIGNMENT_CHECK 17 ///< AC Alignment Check. +#define MACHINE_CHECK 18 ///< MC Machine Check. +#define SIMD_FP_EXC 19 ///< XF Streaming SIMD Exception. +#define VIRT_EXC 20 ///< Virtualization Exception. +// Reserved [21-29]. +#define SECURITY_EXC 30 ///< Security Exception. +#define TRIPLE_FAULT 31 ///< Triple Fault +#define SYSTEM_CALL 80 ///< System call interrupt. //============================================================================== diff --git a/mentos/inc/descriptor_tables/tss.h b/mentos/inc/descriptor_tables/tss.h index ea5acdb..cb265ff 100644 --- a/mentos/inc/descriptor_tables/tss.h +++ b/mentos/inc/descriptor_tables/tss.h @@ -1,61 +1,55 @@ /// MentOS, The Mentoring Operating system project /// @file tss.h /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "gdt.h" -#include "stdio.h" -#include "kernel.h" +#include "stdint.h" -// TODO: doxygen comment. +/// @brief Task state segment entry. typedef struct tss_entry { - uint32_t prevTss; - uint32_t esp0; - uint32_t ss0; - uint32_t esp1; - uint32_t ss1; - uint32_t esp2; - uint32_t ss2; - uint32_t cr3; - uint32_t eip; - uint32_t eflags; - uint32_t eax; - uint32_t ecx; - uint32_t edx; - uint32_t ebx; - uint32_t esp; - uint32_t ebp; - uint32_t esi; - uint32_t edi; - uint32_t es; - uint32_t cs; - uint32_t ss; - uint32_t ds; - uint32_t fs; - uint32_t gs; - uint32_t ldt; - uint16_t trap; - uint16_t iomap; + uint32_t prev_tss; ///< If we used hardware task switching this would form a linked list. + uint32_t esp0; ///< The stack pointer to load when we change to kernel mode. + uint32_t ss0; ///< The stack segment to load when we change to kernel mode. + uint32_t esp1; ///< everything below here is unusued now. + uint32_t ss1; ///< TODO: Comment. + uint32_t esp2; ///< TODO: Comment. + uint32_t ss2; ///< TODO: Comment. + uint32_t cr3; ///< TODO: Comment. + uint32_t eip; ///< TODO: Comment. + uint32_t eflags; ///< TODO: Comment. + uint32_t eax; ///< TODO: Comment. + uint32_t ecx; ///< TODO: Comment. + uint32_t edx; ///< TODO: Comment. + uint32_t ebx; ///< TODO: Comment. + uint32_t esp; ///< TODO: Comment. + uint32_t ebp; ///< TODO: Comment. + uint32_t esi; ///< TODO: Comment. + uint32_t edi; ///< TODO: Comment. + uint32_t es; ///< TODO: Comment. + uint32_t cs; ///< TODO: Comment. + uint32_t ss; ///< TODO: Comment. + uint32_t ds; ///< TODO: Comment. + uint32_t fs; ///< TODO: Comment. + uint32_t gs; ///< TODO: Comment. + uint32_t ldt; ///< TODO: Comment. + uint16_t trap; ///< TODO: Comment. + uint16_t iomap; ///< TODO: Comment. } tss_entry_t; +/// @brief Flushes the Task State Segment. extern void tss_flush(); /// @brief We don't need tss to assist task switching, but it's required to /// have one tss for switching back to kernel mode(system call for /// example). -/// @param idx -/// @param kss -/// @param kesp -void tss_init(uint8_t idx, uint32_t kss, uint32_t kesp); +/// @param idx Index. +/// @param ss0 Kernel data segment. +void tss_init(uint8_t idx, uint32_t ss0); -// esp the kernel should be using. -/// @brief This function is used to set the tss's esp, so that CPU knows what. -/// @param kss -/// @param kesp +/// @brief This function is used to set the esp the kernel should be using. +/// @param kss Kernel data segment. +/// @param kesp Kernel stack address. void tss_set_stack(uint32_t kss, uint32_t kesp); - -// TODO: doxygen comment. -void print_tss(); diff --git a/mentos/inc/devices/fpu.h b/mentos/inc/devices/fpu.h index 5edd9c5..06bee5e 100644 --- a/mentos/inc/devices/fpu.h +++ b/mentos/inc/devices/fpu.h @@ -1,21 +1,126 @@ /// MentOS, The Mentoring Operating system project /// @file fpu.h /// @brief Floating Point Unit (FPU). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "stdint.h" -#include "stdbool.h" +#pragma once -// TODO: doxygen comment. -/// @brief +#include "stdint.h" + +/// @brief Environment information of floating point unit. +typedef struct env87 { + /// Control word (16bits). + long en_cw; + /// Status word (16bits). + long en_sw; + /// Tag word (16bits). + long en_tw; + /// Floating point instruction pointer. + long en_fip; + /// Floating code segment selector. + unsigned short en_fcs; + /// Opcode last executed (11 bits). + unsigned short en_opcode; + /// Floating operand offset. + long en_foo; + /// Floating operand segment selector. + long en_fos; +} env87; + +/// @brief Contents of each floating point accumulator. +typedef struct fpacc87 { + /// Easy to access bytes. + unsigned char fp_bytes[10]; +} fpacc87; + +/// @brief Floating point context. +typedef struct save87 { + /// Floating point control/status. + env87 sv_env; + /// Accumulator contents, 0-7. + fpacc87 sv_ac[8]; + /// Padding for (now unused) saved status word. + unsigned char sv_pad0[4]; + /* + * Bogus padding for emulators. Emulators should use their own + * struct and arrange to store into this struct (ending here) + * before it is inspected for ptracing or for core dumps. Some + * emulators overwrite the whole struct. We have no good way of + * knowing how much padding to leave. Leave just enough for the + * GPL emulator's i387_union (176 bytes total). + */ + /// Padding used by emulators. + unsigned char sv_pad[64]; +} save87; + +/// @brief Stores the XMM environment. +typedef struct envxmm { + /// Control word (16bits). + uint16_t en_cw; + /// Status word (16bits). + uint16_t en_sw; + /// Tag word (16bits). + uint16_t en_tw; + /// Opcode last executed (11 bits). + uint16_t en_opcode; + /// Floating point instruction pointer. + uint32_t en_fip; + /// Floating code segment selector. + uint16_t en_fcs; + /// Padding. + uint16_t en_pad0; + /// Floating operand offset. + uint32_t en_foo; + /// Floating operand segment selector. + uint16_t en_fos; + /// Padding. + uint16_t en_pad1; + /// SSE sontorol/status register. + uint32_t en_mxcsr; + /// Valid bits in mxcsr. + uint32_t en_mxcsr_mask; +} envxmm; + +/// @brief Contents of each SSE extended accumulator. +typedef struct xmmacc { + /// TODO: Comment. + unsigned char xmm_bytes[16]; +} xmmacc; + +/// @brief Stores the XMM context. +typedef struct savexmm { + /// TODO: Comment. + envxmm sv_env; + /// TODO: Comment. + struct { + /// TODO: Comment. + fpacc87 fp_acc; + /// Padding. + unsigned char fp_pad[6]; + } sv_fp[8]; + /// TODO: Comment. + xmmacc sv_xmm[8]; + /// Padding. + unsigned char sv_pad[224]; +} __attribute__((__aligned__(16))) savexmm; + +/// @brief Stores FPU registers details. +typedef union savefpu { + /// Stores the floating point context. + save87 sv_87; + /// Stores the XMM context. + savexmm sv_xmm; +} savefpu; + +/// @brief Called during a context switch to save the FPU registers status +/// of the currently running thread. void switch_fpu(); -// TODO: doxygen comment. -/// @brief +/// @brief Called during a context switch to load the FPU registers status +/// of the currently running thread inside the FPU. void unswitch_fpu(); -// TODO: doxygen comment. -/// @brief -/// @return -bool_t fpu_install(); +/// @brief Enable the FPU context handling. +/// @return 0 if fails, 1 if succeed. +int fpu_install(); diff --git a/mentos/inc/devices/pci.h b/mentos/inc/devices/pci.h index aa05b6d..404cfe1 100644 --- a/mentos/inc/devices/pci.h +++ b/mentos/inc/devices/pci.h @@ -1,8 +1,9 @@ /// MentOS, The Mentoring Operating system project /// @file pci.h /// @brief Routines for PCI initialization. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +///! @cond Doxygen_Suppress #pragma once @@ -82,116 +83,95 @@ /// register, and reading it back. Only 1 bits are decoded. /// @{ -#define PCI_BASE_ADDRESS_0 0x10 - -#define PCI_BASE_ADDRESS_1 0x14 - -#define PCI_BASE_ADDRESS_2 0x18 - -#define PCI_BASE_ADDRESS_3 0x1c - -#define PCI_BASE_ADDRESS_4 0x20 - -#define PCI_BASE_ADDRESS_5 0x24 +#define PCI_BASE_ADDRESS_0 0x10 ///< Location of base address 0. +#define PCI_BASE_ADDRESS_1 0x14 ///< Location of base address 1. +#define PCI_BASE_ADDRESS_2 0x18 ///< Location of base address 2. +#define PCI_BASE_ADDRESS_3 0x1c ///< Location of base address 3. +#define PCI_BASE_ADDRESS_4 0x20 ///< Location of base address 4. +#define PCI_BASE_ADDRESS_5 0x24 ///< Location of base address 5. /// @} pci_base_addresses /// Points to the Card Information Structure and is used by devices that /// share silicon between CardBus and PCI. #define PCI_CARDBUS_CIS 0x28 - +/// Points to the Subsystem Vendor ID #define PCI_SUBSYSTEM_VENDOR_ID 0x2c - +/// Points to the Subsystem Device ID #define PCI_SUBSYSTEM_ID 0x2e - +/// Bits 31..11 are address, 10..1 reserved #define PCI_ROM_ADDRESS 0x30 - /// Points to a linked list of new capabilities implemented by the device. /// Used if bit 4 of the status register (Capabilities List bit) is set to 1. /// The bottom two bits are reserved and should be masked before the Pointer /// is used to access the Configuration Space. #define PCI_CAPABILITY_LIST 0x34 - /// Specifies which input of the system interrupt controllers the device's /// interrupt pin is connected to and is implemented by any device that makes /// use of an interrupt pin. For the x86 architecture this register /// corresponds to the PIC IRQ numbers 0-15 (and not I/O APIC IRQ numbers) and /// a value of 0xFF defines no connection. #define PCI_INTERRUPT_LINE 0x3c - /// Specifies which interrupt pin the device uses. Where a value of 0x01 is /// INTA#, 0x02 is INTB#, 0x03 is INTC#, 0x04 is INTD#, and 0x00 means the /// device does not use an interrupt pin. #define PCI_INTERRUPT_PIN 0x3d - /// A read-only register that specifies the burst period length, in 1/4 /// microsecond units, that the device needs (assuming a 33 MHz clock rate). #define PCI_MIN_GNT 0x3e - /// A read-only register that specifies how often the device needs access to /// the PCI bus (in 1/4 microsecond units). #define PCI_MAX_LAT 0x3f /// @} pci_configuration_space -#define PCI_SECONDARY_BUS 0x19 +#define PCI_PRIMARY_BUS 0x18 ///< Primary bus number. +#define PCI_SECONDARY_BUS 0x19 ///< Secondary bus number. -#define PCI_HEADER_TYPE_DEVICE 0 +#define PCI_HEADER_TYPE_NORMAL 0 ///< TODO: Document. +#define PCI_HEADER_TYPE_BRIDGE 1 ///< TODO: Document. +#define PCI_HEADER_TYPE_CARDBUS 2 ///< TODO: Document. -#define PCI_HEADER_TYPE_BRIDGE 1 +#define PCI_TYPE_BRIDGE 0x060400 ///< TODO: Document. +#define PCI_TYPE_SATA 0x010600 ///< TODO: Document. -#define PCI_HEADER_TYPE_CARDBUS 2 +#define PCI_ADDRESS_PORT 0xCF8 ///< TODO: Document. +#define PCI_VALUE_PORT 0xCFC ///< TODO: Document. +#define PCI_NONE 0xFFFF ///< TODO: Document. -#define PCI_TYPE_BRIDGE 0x060400 +/// @brief PIC scan function. +typedef void (*pci_scan_func_t)(uint32_t device, uint16_t vendor_id, uint16_t device_id, void *extra); -#define PCI_TYPE_SATA 0x010600 - -#define PCI_ADDRESS_PORT 0xCF8 - -#define PCI_VALUE_PORT 0xCFC - -#define PCI_NONE 0xFFFF - -// TODO: doxygen comment. -/// @brief -typedef void (*pci_func_t)(uint32_t device, uint16_t vendor_id, - uint16_t device_id, void *extra); - -// TODO: doxygen comment. -/// @brief +/// @brief Extract the `bus` from the device. static inline int pci_extract_bus(uint32_t device) { - return (uint8_t)((device >> 16)); + return (uint8_t)((device >> 16)); } -// TODO: doxygen comment. -/// @brief +/// @brief Extract the `slot` from the device. static inline int pci_extract_slot(uint32_t device) { - return (uint8_t)((device >> 8)); + return (uint8_t)((device >> 8)); } -// TODO: doxygen comment. -/// @brief +/// @brief Extract the `func` from the device. static inline int pci_extract_func(uint32_t device) { - return (uint8_t)(device); + return (uint8_t)(device); } -// TODO: doxygen comment. -/// @brief +/// @brief TODO: doxygen comment. static inline uint32_t pci_get_addr(uint32_t device, int field) { - return 0x80000000 | (pci_extract_bus(device) << 16) | - (pci_extract_slot(device) << 11) | - (pci_extract_func(device) << 8) | ((field)&0xFC); + return 0x80000000 | (pci_extract_bus(device) << 16) | + (pci_extract_slot(device) << 11) | (pci_extract_func(device) << 8) | + ((field)&0xFC); } -// TODO: doxygen comment. -/// @brief +/// @brief TODO: doxygen comment. static inline uint32_t pci_box_device(int bus, int slot, int func) { - return (uint32_t)((bus << 16) | (slot << 8) | func); + return (uint32_t)((bus << 16) | (slot << 8) | func); } /// @brief Reads a field from the given PCI device. @@ -212,27 +192,26 @@ const char *pci_vendor_lookup(unsigned short vendor_id); // TODO: doxygen comment. /// @brief const char *pci_device_lookup(unsigned short vendor_id, - unsigned short device_id); + unsigned short device_id); // TODO: doxygen comment. /// @brief -void pci_scan_hit(pci_func_t f, uint32_t dev, void *extra); +void pci_scan_hit(pci_scan_func_t f, uint32_t dev, void *extra); // TODO: doxygen comment. /// @brief -void pci_scan_func(pci_func_t f, int type, int bus, int slot, int func, - void *extra); +void pci_scan_func(pci_scan_func_t f, int type, int bus, int slot, int func, void *extra); // TODO: doxygen comment. /// @brief -void pci_scan_slot(pci_func_t f, int type, int bus, int slot, void *extra); +void pci_scan_slot(pci_scan_func_t f, int type, int bus, int slot, void *extra); // TODO: doxygen comment. /// @brief -void pci_scan_bus(pci_func_t f, int type, int bus, void *extra); +void pci_scan_bus(pci_scan_func_t f, int type, int bus, void *extra); // TODO: doxygen comment. /// @brief -void pci_scan(pci_func_t f, int type, void *extra); +void pci_scan(pci_scan_func_t f, int type, void *extra); // TODO: doxygen comment. /// @brief @@ -245,3 +224,5 @@ int pci_get_interrupt(uint32_t device); // TODO: doxygen comment. /// @brief void pci_debug_scan(); + +///! @endcond \ No newline at end of file diff --git a/mentos/inc/drivers/ata.h b/mentos/inc/drivers/ata.h index 6920636..a8241b5 100644 --- a/mentos/inc/drivers/ata.h +++ b/mentos/inc/drivers/ata.h @@ -1,8 +1,16 @@ /// MentOS, The Mentoring Operating system project -// @file ata.h +/// @file ata.h /// @brief ATA values and data structures. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @details +/// IDE is a keyword which refers to the electrical specification of the cables +/// which connect ATA drives (like hard drives) to another device. The drives +/// use the ATA (Advanced Technology Attachment) interface. An IDE cable also +/// can terminate at an IDE card connected to PCI. +/// ATAPI is an extension to ATA (recently renamed to PATA) which adds support +/// for the SCSI command set. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +///! @cond Doxygen_Suppress #pragma once @@ -11,189 +19,187 @@ /// @defgroup ata_defines The ATA configuration registers. /// @{ -/// @defgroup status_register Status register bits +/// @defgroup status_register Status register bits. /// @{ -/// Device Busy -#define ATA_STAT_BUSY 0x80 -/// Device Ready -#define ATA_STAT_READY 0x40 -/// Device Fault -#define ATA_STAT_FAULT 0x20 -/// Device Seek Complete -#define ATA_STAT_SEEK 0x10 -/// Data Request (ready) -#define ATA_STAT_DRQ 0x08 -/// Corrected Data Error -#define ATA_STAT_CORR 0x04 -/// Vendor specific -#define ATA_STAT_INDEX 0x02 -/// Error -#define ATA_STAT_ERR 0x01 +#define ATA_STAT_BUSY 0x80 /// Device Busy +#define ATA_STAT_READY 0x40 ///< Device Ready +#define ATA_STAT_FAULT 0x20 ///< Device Fault +#define ATA_STAT_SEEK 0x10 ///< Device Seek Complete +#define ATA_STAT_DRQ 0x08 ///< Data Request (ready) +#define ATA_STAT_CORR 0x04 ///< Corrected Data Error +#define ATA_STAT_INDEX 0x02 ///< Vendor specific +#define ATA_STAT_ERR 0x01 ///< Error -/// @} - -/// @defgroup device_registers Device / Head Register Bits -/// @{ - -#define ATA_DEVICE(x) ((x & 1) << 4) -#define ATA_LBA 0xE0 - -/// @} +///< @} /// @defgroup ata_commands ATA Commands /// @{ -/// Read Sectors (with retries) -#define ATA_CMD_READ 0x20 -/// Read Sectors (no retries) -#define ATA_CMD_READN 0x21 -/// Write Sectores (with retries) -#define ATA_CMD_WRITE 0x30 -/// Write Sectors (no retries) -#define ATA_CMD_WRITEN 0x31 -/// Read Verify (with retries) -#define ATA_CMD_VRFY 0x40 -/// Read verify (no retries) -#define ATA_CMD_VRFYN 0x41 -/// Seek -#define ATA_CMD_SEEK 0x70 -/// Execute Device Diagnostic -#define ATA_CMD_DIAG 0x90 -/// Initialize Device Parameters -#define ATA_CMD_INIT 0x91 -/// Read Multiple -#define ATA_CMD_RD_MULT 0xC4 -/// Write Multiple -#define ATA_CMD_WR_MULT 0xC5 -/// Set Multiple Mode -#define ATA_CMD_SETMULT 0xC6 -/// Read DMA (with retries) -#define ATA_CMD_RD_DMA 0xC8 -/// Read DMS (no retries) -#define ATA_CMD_RD_DMAN 0xC9 -/// Write DMA (with retries) -#define ATA_CMD_WR_DMA 0xCA -/// Write DMA (no retires) -#define ATA_CMD_WR_DMAN 0xCB -/// Identify Device -#define ATA_CMD_IDENT 0xEC -#define ATA_CMD_CH_FLSH 0xE7 -/// Set Features -#define ATA_CMD_SETF 0xEF -/// Check Power Mode -#define ATA_CMD_CHK_PWR 0xE5 +#define ATA_CMD_READ 0x20 ///< Read Sectors (with retries) +#define ATA_CMD_READN 0x21 ///< Read Sectors (no retries) +#define ATA_CMD_WRITE 0x30 ///< Write Sectores (with retries) +#define ATA_CMD_WRITEN 0x31 ///< Write Sectors (no retries) +#define ATA_CMD_VRFY 0x40 ///< Read Verify (with retries) +#define ATA_CMD_VRFYN 0x41 ///< Read verify (no retries) +#define ATA_CMD_SEEK 0x70 ///< Seek +#define ATA_CMD_DIAG 0x90 ///< Execute Device Diagnostic +#define ATA_CMD_INIT 0x91 ///< Initialize Device Parameters +#define ATA_CMD_RD_MULT 0xC4 ///< Read Multiple +#define ATA_CMD_WR_MULT 0xC5 ///< Write Multiple +#define ATA_CMD_SETMULT 0xC6 ///< Set Multiple Mode +#define ATA_CMD_RD_DMA 0xC8 ///< Read DMA (with retries) +#define ATA_CMD_RD_DMAN 0xC9 ///< Read DMS (no retries) +#define ATA_CMD_WR_DMA 0xCA ///< Write DMA (with retries) +#define ATA_CMD_WR_DMAN 0xCB ///< Write DMA (no retires) +#define ATA_CMD_IDENT 0xEC ///< Identify Device +#define ATA_CMD_CH_FLSH 0xE7 ///< Cache flush. +#define ATA_CMD_SETF 0xEF ///< Set Features +#define ATA_CMD_CHK_PWR 0xE5 ///< Check Power Mode -/// @} +///< @} /// @defgroup atapi_commands ATAPI Commands /// @{ -#define ATAPI_CMD_PACKET 0xA0 +#define ATAPI_CMD_PACKET 0xA0 #define ATAPI_CMD_ID_PCKT 0xA1 /// @} -#define ATA_IDENT_DEVICETYPE 0 -#define ATA_IDENT_CYLINDERS 2 -#define ATA_IDENT_HEADS 6 -#define ATA_IDENT_SECTORS 12 -#define ATA_IDENT_SERIAL 20 -#define ATA_IDENT_MODEL 54 +/// @defgroup ata_ident_brits Identification Space Bits +/// @brief Definitions used to read information from the identification space. +/// @{ + +#define ATA_IDENT_DEVICETYPE 0 +#define ATA_IDENT_CYLINDERS 2 +#define ATA_IDENT_HEADS 6 +#define ATA_IDENT_SECTORS 12 +#define ATA_IDENT_SERIAL 20 +#define ATA_IDENT_MODEL 54 #define ATA_IDENT_CAPABILITIES 98 -#define ATA_IDENT_FIELDVALID 106 -#define ATA_IDENT_MAX_LBA 120 -#define ATA_IDENT_COMMANDSETS 164 -#define ATA_IDENT_MAX_LBA_EXT 200 - -#define IDE_ATA 0x00 -#define IDE_ATAPI 0x01 - -#define ATA_MASTER 0x00 -#define ATA_SLAVE 0x01 - -#define ATA_REG_DATA 0x00 -#define ATA_REG_ERROR 0x01 -#define ATA_REG_FEATURES 0x01 -#define ATA_REG_SECCOUNT0 0x02 -#define ATA_REG_LBA0 0x03 -#define ATA_REG_LBA1 0x04 -#define ATA_REG_LBA2 0x05 -#define ATA_REG_HDDEVSEL 0x06 -#define ATA_REG_COMMAND 0x07 -#define ATA_REG_STATUS 0x07 -#define ATA_REG_SECCOUNT1 0x08 -#define ATA_REG_LBA3 0x09 -#define ATA_REG_LBA4 0x0A -#define ATA_REG_LBA5 0x0B -#define ATA_REG_CONTROL 0x0C -#define ATA_REG_ALTSTATUS 0x0C -#define ATA_REG_DEVADDRESS 0x0D - -// Channels: -#define ATA_PRIMARY 0x00 -#define ATA_SECONDARY 0x01 - -// Directions: -#define ATA_READ 0x00 -#define ATA_WRITE 0x01 +#define ATA_IDENT_FIELDVALID 106 +#define ATA_IDENT_MAX_LBA 120 +#define ATA_IDENT_COMMANDSETS 164 +#define ATA_IDENT_MAX_LBA_EXT 200 /// @} -typedef struct { - uint16_t base; - uint16_t ctrl; - uint16_t bmide; - uint16_t nien; +/// @defgroup ata_interface Interface Type +/// @{ + +#define IDE_ATA 0x00 ///< ATA (Advanced Technology Attachment) interface. +#define IDE_ATAPI 0x01 ///< Extended ATA with support for SCSI command set. + +/// @} + +/// @defgroup ata_priority Interface Priority +/// @{ + +#define ATA_DEVICE_0 0x00 +#define ATA_DEVICE_1 0x01 + +/// @} + +/// @defgroup ata_command_block_registers Command block registers +/// @{ + +#define ATA_REG_DATA 0x00 ///< Read/Write PIO data bytes. +#define ATA_REG_ERROR 0x01 ///< Error generated by the last ATA command. +#define ATA_REG_FEATURES 0x01 ///< Control command specific interface features. +#define ATA_REG_SECCOUNT0 0x02 ///< Number of sectors to read/write (0 is a special value). +#define ATA_REG_LBA0 0x03 ///< Sector Number Register. +#define ATA_REG_LBA1 0x04 ///< Cylinder Low Register. +#define ATA_REG_LBA2 0x05 ///< Cylinder High Register. +#define ATA_REG_HDDEVSEL 0x06 ///< Used to select a drive and/or head. +#define ATA_REG_COMMAND 0x07 ///< Used to send ATA commands to the device. +#define ATA_REG_STATUS 0x07 ///< Used to read the current status. +#define ATA_REG_SECCOUNT1 0x08 ///< +#define ATA_REG_LBA3 0x09 ///< +#define ATA_REG_LBA4 0x0A ///< +#define ATA_REG_LBA5 0x0B ///< +#define ATA_REG_CONTROL 0x0C ///< +#define ATA_REG_ALTSTATUS 0x0C ///< +#define ATA_REG_DEVADDRESS 0x0D ///< + +/// @} + +/// @defgroup ata_channels Channels +/// @{ + +#define ATA_PRIMARY 0x00 ///< Primary channel. +#define ATA_SECONDARY 0x01 ///< Secondary channel. +/// @} + +/// @defgroup ata_directions Directions +/// @{ + +#define ATA_READ 0x00 ///< Read direction. +#define ATA_WRITE 0x01 ///< Write direction. + +/// @} +/// @} + +/// @brief Stores information of a channel. +typedef struct ide_channel_regs_t{ + uint16_t base; ///< I/O Base. + uint16_t ctrl; ///< Control Base + uint16_t bmide; ///< Bus Master IDE + uint16_t nien; ///< nIEN (No Interrupt); } ide_channel_regs_t; -typedef struct { - uint8_t reserved; - uint8_t channel; - uint8_t drive; - uint16_t type; - uint16_t signature; - uint16_t capabilities; - uint32_t command_sets; - uint32_t size; - uint8_t model[41]; +/// @brief Stores information of a device. +typedef struct ide_device_t{ + uint8_t reserved; ///< 0 (Empty) or 1 (This Drive really exists). + uint8_t channel; ///< 0 (Primary Channel) or 1 (Secondary Channel). + uint8_t drive; ///< 0 (Drive 0) or 1 (Drive 1). + uint16_t type; ///< 0: ATA, 1:ATAPI. + uint16_t signature; ///< Drive Signature. + uint16_t capabilities; ///< Features. + uint32_t command_sets; ///< Command Sets Supported. + uint32_t size; ///< Size in Sectors. + uint8_t model[41]; ///< Model in string. } ide_device_t; -typedef struct { - uint8_t status; - uint8_t chs_first_sector[3]; - uint8_t type; - uint8_t chs_last_sector[3]; - uint32_t lba_first_sector; - uint32_t sector_count; +/// @brief +typedef struct partition_t{ + uint8_t status; ///< + uint8_t chs_first_sector[3]; ///< + uint8_t type; ///< + uint8_t chs_last_sector[3]; ///< + uint32_t lba_first_sector; ///< + uint32_t sector_count; ///< } partition_t; -typedef struct { - uint16_t flags; - uint16_t unused1[9]; - char serial[20]; - uint16_t unused2[3]; - char firmware[8]; - char model[40]; - uint16_t sectors_per_int; - uint16_t unused3; - uint16_t capabilities[2]; - uint16_t unused4[2]; - uint16_t valid_ext_data; - uint16_t unused5[5]; - uint16_t size_of_rw_mult; - uint32_t sectors_28; - uint16_t unused6[38]; - uint64_t sectors_48; - uint16_t unused7[152]; +typedef struct ata_identify_t{ + uint16_t flags; + uint16_t unused1[9]; + char serial[20]; + uint16_t unused2[3]; + char firmware[8]; + char model[40]; + uint16_t sectors_per_int; + uint16_t unused3; + uint16_t capabilities[2]; + uint16_t unused4[2]; + uint16_t valid_ext_data; + uint16_t unused5[5]; + uint16_t size_of_rw_mult; + uint32_t sectors_28; + uint16_t unused6[38]; + uint64_t sectors_48; + uint16_t unused7[152]; } __attribute__((packed)) ata_identify_t; -typedef struct { - uint8_t boostrap[446]; - partition_t partitions[4]; - uint8_t signature[2]; +/// @brief Master Boot Record. +typedef struct mbr_t{ + uint8_t boostrap[446]; + partition_t partitions[4]; + uint8_t signature[2]; } __attribute__((packed)) mbr_t; int ata_initialize(); int ata_finalize(); + +///! @endcond \ No newline at end of file diff --git a/mentos/inc/drivers/fdc.h b/mentos/inc/drivers/fdc.h index 608ecec..c037c25 100644 --- a/mentos/inc/drivers/fdc.h +++ b/mentos/inc/drivers/fdc.h @@ -1,38 +1,37 @@ /// MentOS, The Mentoring Operating system project /// @file fdc.h /// @brief Definitions about the floppy. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once /// @brief Floppy Disk Controller (FDC) registers. -typedef enum fdc_registers_t -{ +typedef enum fdc_registers_t { STATUS_REGISTER_A = 0x3F0, ///< This register is read-only and monitors the state of the ///< interrupt pin and several disk interface pins. - STATUS_REGISTER_B = 0x3F1, + STATUS_REGISTER_B = 0x3F1, ///< This register is read-only and monitors the state of several ///< isk interface pins. - DOR = 0x3F2, + DOR = 0x3F2, ///< The Digital Output Register contains the drive select and ///< motor enable bits, a reset bit and a DMA GATE bit. - TAPE_DRIVE_REGISTER = 0x3F3, + TAPE_DRIVE_REGISTER = 0x3F3, ///< This register allows the user to assign tape support to a ///< particular drive during initialization. - MAIN_STATUS_REGISTER = 0x3F4, + MAIN_STATUS_REGISTER = 0x3F4, ///< The Main Status Register is a read-only register and is used ///< for controlling command input and result output for all commands. - DATARATE_SELECT_REGISTER = 0x3F4, + DATARATE_SELECT_REGISTER = 0x3F4, ///< This register is included for compatibility with the 82072 ///< floppy controller and is write-only. - DATA_FIFO = 0x3F5, + DATA_FIFO = 0x3F5, ///< All command parameter information and disk data transfers go ///< through the FIFO. - DIGITAL_INPUT_REGISTER = 0x3F7, + DIGITAL_INPUT_REGISTER = 0x3F7, ///< This register is read only in all modes. - CONFIGURATION_CONTROL_REGISTER = 0x3F7 + CONFIGURATION_CONTROL_REGISTER = 0x3F7 ///< This register sets the datarate and is write only. } fdc_registers_t; diff --git a/mentos/inc/drivers/keyboard/keyboard.h b/mentos/inc/drivers/keyboard/keyboard.h index 6436794..c3a84f5 100644 --- a/mentos/inc/drivers/keyboard/keyboard.h +++ b/mentos/inc/drivers/keyboard/keyboard.h @@ -1,14 +1,12 @@ /// MentOS, The Mentoring Operating system project /// @file keyboard.h /// @brief Definitions about the keyboard. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "kernel.h" -#include "stdint.h" -#include "stdbool.h" /// @brief The dimension of the circular buffer used to store video history. #define BUFSIZE 256 @@ -16,8 +14,9 @@ /// @brief Function used to install the keyboard. void keyboard_install(); -/// @brief The keyboard handler. -void keyboard_isr(pt_regs *r); +/// @brief The interrupt service routine of the keyboard. +/// @param f The interrupt stack frame. +void keyboard_isr(pt_regs *f); /// @brief Enable the keyboard. void keyboard_enable(); @@ -32,20 +31,3 @@ void keyboard_update_leds(); /// @details It loops until there is something new to read. /// @return The read character. int keyboard_getc(); - -/// @brief Set Keyboard echo Shadow. -/// @param value 1 if you want enable shadow, 0 otherwise. -void keyboard_set_shadow(const bool_t value); - -/// @brief Set Keyboard echo Shadow character. -/// @param value 1 if you want enable shadow, 0 otherwise. -void keyboard_set_shadow_character(const char _shadow_character); - -/// @brief Get Keyboard Shadow information. -bool_t keyboard_get_shadow(); - -/// @brief Get ctrl status. -bool_t keyboard_is_ctrl_pressed(); - -/// @brief Get shift status. -bool_t keyboard_is_shifted(); diff --git a/mentos/inc/drivers/keyboard/keymap.h b/mentos/inc/drivers/keyboard/keymap.h index 9ce71c7..c55fefb 100644 --- a/mentos/inc/drivers/keyboard/keymap.h +++ b/mentos/inc/drivers/keyboard/keymap.h @@ -1,242 +1,151 @@ /// MentOS, The Mentoring Operating system project /// @file keymap.h /// @brief Keymap for keyboard. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "stdint.h" +/// @brief The list of supported keyboard layouts. +typedef enum { + KEYMAP_IT, ///< Identifies the IT keyboard mapping. + KEYMAP_US, ///< Identifies the US keyboard mapping. + KEYMAP_TYPE_MAX ///< The delimiter for the keyboard types. +} keymap_type_t; + +/// @brief Defines a set of arrays used to map key to characters. +typedef struct keymap_t { + /// The basic mapping. + int32_t base[65536]; + /// The mapping when shifted. + int32_t shift[65536]; + /// The mapping when numlock is active. + uint32_t numlock[65536]; +} keymap_t; + +/// @brief Returns the current keymap type. +/// @return The current keymap type. +keymap_type_t get_keymap_type(); + +/// @brief Changes the current keymap type. +/// @param type The type to set. +void set_keymap_type(keymap_type_t type); + +/// @brief Returns the current keymap. +/// @return Pointer to the current keymap. +const keymap_t *get_keymap(); + +/// @brief Initializes the supported keymaps. +void init_keymaps(); + /// @defgroup keyboardcodes Keyboard Codes /// @brief This is the list of keyboard codes. /// @{ -/// Escape character. -#define KEY_ESCAPE 0x01 -/// 1. -#define KEY_ONE 0x02 -/// 2. -#define KEY_TWO 0x03 -/// 3. -#define KEY_THREE 0x04 -/// 4. -#define KEY_FOUR 0x05 -/// 5. -#define KEY_FIVE 0x06 -/// 6. -#define KEY_SIX 0x07 -/// 7. -#define KEY_SEVEN 0x08 -/// 8. -#define KEY_EIGHT 0x09 -/// 9. -#define KEY_NINE 0x0A -/// 0. -#define KEY_ZERO 0x0B -/// '. -#define KEY_APOSTROPHE 0x0C -/// i'. -#define KEY_I_ACC 0x0D -/// Backspace. -#define KEY_BACKSPACE 0x0E -/// Tabular. -#define KEY_TAB 0x0F -/// q. -#define KEY_Q 0x10 -/// w. -#define KEY_W 0x11 -/// e. -#define KEY_E 0x12 -/// r. -#define KEY_R 0x13 -/// t. -#define KEY_T 0x14 -/// y. -#define KEY_Y 0x15 -/// u. -#define KEY_U 0x16 -/// i. -#define KEY_I 0x17 -/// o. -#define KEY_O 0x18 -/// p. -#define KEY_P 0x19 -/// (. -#define KEY_LEFT_BRAKET 0x1A -/// ). -#define KEY_RIGHT_BRAKET 0x1B -/// Enter. -#define KEY_ENTER 0x1C -/// Left-ctrl. -#define KEY_LEFT_CONTROL 0x1D -/// a. -#define KEY_A 0x1E -/// s. -#define KEY_S 0x1F -/// d. -#define KEY_D 0x20 -/// f. -#define KEY_F 0x21 -/// g. -#define KEY_G 0x22 -/// h. -#define KEY_H 0x23 -/// j. -#define KEY_J 0x24 -/// k. -#define KEY_K 0x25 -/// l. -#define KEY_L 0x26 -/// '. -#define KEY_SEMICOLON 0x27 -/// ". -#define KEY_DOUBLE_QUOTES 0x28 -///` or ~. -#define KEY_GRAVE 0x29 -/// LShift. -#define KEY_LEFT_SHIFT 0x2A -/// \ or |. -#define KEY_BACKSLASH 0x2B -/// Z. -#define KEY_Z 0x2c -/// X. -#define KEY_X 0x2d -/// C. -#define KEY_C 0x2e -/// V. -#define KEY_V 0x2f -/// B. -#define KEY_B 0x30 -/// N. -#define KEY_N 0x31 -/// M. -#define KEY_M 0x32 -/// , or <. -#define KEY_COMMA 0x33 -/// . or >. -#define KEY_PERIOD 0x34 -/// - or _. -#define KEY_MINUS 0x35 -/// RShift. -#define KEY_RIGHT_SHIFT 0x36 -/// NP - *. -#define KEY_KP_MUL 0x37 -/// LAlt. -#define KEY_LEFT_ALT 0x38 -/// Space. -#define KEY_SPACE 0x39 -/// Caps Lock. -#define KEY_CAPS_LOCK 0x3a -/// Function 1. -#define KEY_F1 0x3B -/// Function 2. -#define KEY_F2 0x3C -/// Function 3. -#define KEY_F3 0x3D -/// Function 4. -#define KEY_F4 0x3E -/// Function 5. -#define KEY_F5 0x3F -/// Function 6. -#define KEY_F6 0x40 -/// Function 7. -#define KEY_F7 0x41 -/// Function 8. -#define KEY_F8 0x42 -/// Function 9. -#define KEY_F9 0x43 -/// Function 10. -#define KEY_F10 0x44 -/// Num Lock. -#define KEY_NUM_LOCK 0x45 -/// Scroll Lock. -#define KEY_SCROLL_LOCK 0x46 -/// NP - Home. -#define KEY_KP7 0x47 -/// NP - UpArrow. -#define KEY_KP8 0x48 -/// NP - Pgup. -#define KEY_KP9 0x49 -/// NP - Grey. -#define KEY_KP_SUB 0x4A -/// NP - LArrow. -#define KEY_KP4 0x4B -/// NP - Center. -#define KEY_KP5 0x4C -/// NP - RArrow. -#define KEY_KP6 0x4D -/// NP - Grey +. -#define KEY_KP_ADD 0x4E -/// NP - End. -#define KEY_KP1 0x4F -/// NP - DArrow. -#define KEY_KP2 0x50 -/// NP - Pgdn. -#define KEY_KP3 0x51 -/// NP - Ins. -#define KEY_KP0 0x52 -/// NP - Del. -#define KEY_KP_DEC 0x53 -/// NP - Del. -#define KEY_KP_LESS 0x56 -/// NP - Enter 57372. -#define KEY_KP_RETURN 0xe01c -/// Right Ctrl 57373. -#define KEY_RIGHT_CONTROL 0xE01D -/// Divide 57397. -#define KEY_KP_DIV 0xE035 -/// Right Alt 57400. -#define KEY_RIGHT_ALT 0xe038 -/// F11 57431. -#define KEY_F11 0xe057 -/// F12 57432. -#define KEY_F12 0xe058 -/// Left Winkey 57435. -#define KEY_LEFT_WIN 0xe05b -/// Right Winkey 57436. -#define KEY_RIGHT_WIN 0xe05c -/// Ins 57426. -#define KEY_INSERT 0xe052 -/// Home 57415. -#define KEY_HOME 0xe047 -/// Up Arrow 57416. -#define KEY_UP_ARROW 0xe048 -/// Pgup 57417. -#define KEY_PAGE_UP 0xe049 -/// Left Arrow 57419. -#define KEY_LEFT_ARROW 0xe04b -/// Del 57427. -#define KEY_DELETE 0xe053 -/// End 57423. -#define KEY_END 0xe04f -/// Pgdn 57425. -#define KEY_PAGE_DOWN 0xe051 -/// Right Arrow 57421. -#define KEY_RIGHT_ARROW 0xe04d -/// Down Arrow 57424. -#define KEY_DOWN_ARROW 0xe050 -///< Code break code. -#define CODE_BREAK 0x80 +#define KEY_ESCAPE 0x0001U ///< Escape character +#define KEY_ONE 0x0002U ///< 1 +#define KEY_TWO 0x0003U ///< 2 +#define KEY_THREE 0x0004U ///< 3 +#define KEY_FOUR 0x0005U ///< 4 +#define KEY_FIVE 0x0006U ///< 5 +#define KEY_SIX 0x0007U ///< 6 +#define KEY_SEVEN 0x0008U ///< 7 +#define KEY_EIGHT 0x0009U ///< 8 +#define KEY_NINE 0x000AU ///< 9 +#define KEY_ZERO 0x000BU ///< 0 +#define KEY_APOSTROPHE 0x000CU ///< ' +#define KEY_I_ACC 0x000DU ///< i' +#define KEY_BACKSPACE 0x000EU ///< Backspace +#define KEY_TAB 0x000FU ///< Tabular +#define KEY_Q 0x0010U ///< q +#define KEY_W 0x0011U ///< w +#define KEY_E 0x0012U ///< e +#define KEY_R 0x0013U ///< r +#define KEY_T 0x0014U ///< t +#define KEY_Y 0x0015U ///< y +#define KEY_U 0x0016U ///< u +#define KEY_I 0x0017U ///< i +#define KEY_O 0x0018U ///< o +#define KEY_P 0x0019U ///< p +#define KEY_LEFT_BRAKET 0x001AU ///< ( +#define KEY_RIGHT_BRAKET 0x001BU ///< ) +#define KEY_ENTER 0x001CU ///< Enter +#define KEY_LEFT_CONTROL 0x001DU ///< Left-ctrl +#define KEY_A 0x001EU ///< a +#define KEY_S 0x001FU ///< s +#define KEY_D 0x0020U ///< d +#define KEY_F 0x0021U ///< f +#define KEY_G 0x0022U ///< g +#define KEY_H 0x0023U ///< h +#define KEY_J 0x0024U ///< j +#define KEY_K 0x0025U ///< k +#define KEY_L 0x0026U ///< l +#define KEY_SEMICOLON 0x0027U ///< ' +#define KEY_DOUBLE_QUOTES 0x0028U ///< " +#define KEY_GRAVE 0x0029U ///< Either ` or ~ +#define KEY_LEFT_SHIFT 0x002AU ///< LShift +#define KEY_BACKSLASH 0x002BU ///< Either \ or | +#define KEY_Z 0x002cU ///< Z +#define KEY_X 0x002dU ///< X +#define KEY_C 0x002eU ///< C +#define KEY_V 0x002fU ///< V +#define KEY_B 0x0030U ///< B +#define KEY_N 0x0031U ///< N +#define KEY_M 0x0032U ///< M +#define KEY_COMMA 0x0033U ///< Either , or < +#define KEY_PERIOD 0x0034U ///< Either . or > +#define KEY_MINUS 0x0035U ///< Either - and _ +#define KEY_RIGHT_SHIFT 0x0036U ///< RShift +#define KEY_KP_MUL 0x0037U ///< NP - * +#define KEY_LEFT_ALT 0x0038U ///< LAlt +#define KEY_SPACE 0x0039U ///< Space +#define KEY_CAPS_LOCK 0x003aU ///< Caps Lock +#define KEY_F1 0x003BU ///< Function 1 +#define KEY_F2 0x003CU ///< Function 2 +#define KEY_F3 0x003DU ///< Function 3 +#define KEY_F4 0x003EU ///< Function 4 +#define KEY_F5 0x003FU ///< Function 5 +#define KEY_F6 0x0040U ///< Function 6 +#define KEY_F7 0x0041U ///< Function 7 +#define KEY_F8 0x0042U ///< Function 8 +#define KEY_F9 0x0043U ///< Function 9 +#define KEY_F10 0x0044U ///< Function 10 +#define KEY_NUM_LOCK 0x0045U ///< Num Lock +#define KEY_SCROLL_LOCK 0x0046U ///< Scroll Lock +#define KEY_KP7 0x0047U ///< NP - Home +#define KEY_KP8 0x0048U ///< NP - UpArrow +#define KEY_KP9 0x0049U ///< NP - Pgup +#define KEY_KP_SUB 0x004AU ///< NP - Grey +#define KEY_KP4 0x004BU ///< NP - LArrow +#define KEY_KP5 0x004CU ///< NP - Center +#define KEY_KP6 0x004DU ///< NP - RArrow +#define KEY_KP_ADD 0x004EU ///< NP - Grey + +#define KEY_KP1 0x004FU ///< NP - End +#define KEY_KP2 0x0050U ///< NP - DArrow +#define KEY_KP3 0x0051U ///< NP - Pgdn +#define KEY_KP0 0x0052U ///< NP - Ins +#define KEY_KP_DEC 0x0053U ///< NP - Del +#define KEY_KP_LESS 0x0056U ///< NP - Del +#define KEY_KP_RETURN 0xe01cU ///< NP - Enter 57372 +#define KEY_RIGHT_CONTROL 0xE01DU ///< Right Ctrl 57373 +#define KEY_KP_DIV 0xE035U ///< Divide 57397 +#define KEY_RIGHT_ALT 0xe038U ///< Right Alt 57400 +#define KEY_F11 0xe057U ///< F11 57431 +#define KEY_F12 0xe058U ///< F12 57432 +#define KEY_LEFT_WIN 0xe05bU ///< Left Winkey 57435 +#define KEY_RIGHT_WIN 0xe05cU ///< Right Winkey 57436 +#define KEY_INSERT 0xe052U ///< Ins 57426 +#define KEY_HOME 0xe047U ///< Home 57415 +#define KEY_UP_ARROW 0xe048U ///< Up Arrow 57416 +#define KEY_PAGE_UP 0xe049U ///< Pgup 57417 +#define KEY_LEFT_ARROW 0xe04bU ///< Left Arrow 57419 +#define KEY_DELETE 0xe053U ///< Del 57427 +#define KEY_END 0xe04fU ///< End 57423 +#define KEY_PAGE_DOWN 0xe051U ///< Pgdn 57425 +#define KEY_RIGHT_ARROW 0xe04dU ///< Right Arrow 57421 +#define KEY_DOWN_ARROW 0xe050U ///< Down Arrow 57424 +#define CODE_BREAK 0x0080U ///< Code break code /// @} - -/// Num Pad Led. -#define NUM_LED 0x45 -/// Scroll Lock Led. -#define SCROLL_LED 0x46 -/// Caps Lock Led. -#define CAPS_LED 0x3a - -/// @brief Defines a set of arrays used to map key to characters. -typedef struct keymap_t { - /// The basic mapping. - int32_t base[65536]; - /// The mapping when shifted. - int32_t shift[65536]; - /// The mapping when numlock is active. - uint32_t numlock[65536]; -} keymap_t; - -/// @brief Italian keymap. -extern const keymap_t keymap_it; diff --git a/mentos/inc/drivers/mouse.h b/mentos/inc/drivers/mouse.h index 0f20a34..8ed7154 100644 --- a/mentos/inc/drivers/mouse.h +++ b/mentos/inc/drivers/mouse.h @@ -1,24 +1,25 @@ /// MentOS, The Mentoring Operating system project /// @file mouse.h /// @brief Driver for *PS2* Mouses. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +///! @cond Doxygen_Suppress #pragma once /* The mouse starts sending automatic packets when the mouse moves or is * clicked. */ -#include +#include "kernel.h" -#define MOUSE_ENABLE_PACKET 0xF4 +#define MOUSE_ENABLE_PACKET 0xF4 /// The mouse stops sending automatic packets. -#define MOUSE_DISABLE_PACKET 0xF5 +#define MOUSE_DISABLE_PACKET 0xF5 /// Disables streaming, sets the packet rate to 100 per second, and /// resolution to 4 pixels per mm. -#define MOUSE_USE_DEFAULT_SETTINGS 0xF6 +#define MOUSE_USE_DEFAULT_SETTINGS 0xF6 /// @brief Sets up the mouse by installing the mouse handler into IRQ12. void mouse_install(); @@ -41,5 +42,8 @@ void mouse_write(unsigned char data); /// @return The data received from mouse. unsigned char mouse_read(); -/// @brief The mouse handler. -void mouse_isr(register_t *r); +/// @brief The interrupt service routine of the mouse. +/// @param f The interrupt stack frame. +void mouse_isr(pt_regs *f); + +///! @endcond \ No newline at end of file diff --git a/mentos/inc/drivers/rtc.h b/mentos/inc/drivers/rtc.h new file mode 100644 index 0000000..d0dd81f --- /dev/null +++ b/mentos/inc/drivers/rtc.h @@ -0,0 +1,16 @@ +/// MentOS, The Mentoring Operating system project +/// @file rtc.h +/// @brief Real Time Clock (RTC) driver. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "time.h" + +/// @brief Copies the global time inside the provided variable. +/// @param time Pointer where we store the global time. +extern void gettime(tm_t *time); + +/// @brief Installs the Real Time Clock. +extern void rtc_install(void); \ No newline at end of file diff --git a/mentos/inc/elf/elf.h b/mentos/inc/elf/elf.h index b3bad2e..af303ea 100644 --- a/mentos/inc/elf/elf.h +++ b/mentos/inc/elf/elf.h @@ -1,60 +1,292 @@ /// MentOS, The Mentoring Operating system project /// @file elf.h /// @brief Function for multiboot support. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once +#include "process/process.h" #include "stdint.h" -#include "multiboot.h" -// Used to get the symbol type. -#define ELF32_ST_TYPE(i) ((i)&0xf) +/// @defgroup header_segment_types Program Header Segment Types +/// @brief List of numeric defines which identify segment entries types. +/// @{ -// List of symbol types. -#define ELF32_TYPE_FUNCTION (0x02) +/// @brief Unused. +#define PT_NULL 0 +/// @brief Specifies a loadable segment, described by p_filesz and p_memsz. +/// The bytes from the file are mapped to the beginning of the memory segment. +/// If the segment's memory size (p_memsz) is larger than the file +/// size (p_filesz), the extra bytes are defined to hold the value 0 +/// and to follow the segment's initialized area. The file size can not +/// be larger than the memory size. Loadable segment entries in the program +/// header table appear in ascending order, sorted on the p_vaddr member. +#define PT_LOAD 1 +/// @brief Specifies dynamic linking information. +#define PT_DYNAMIC 2 +/// @brief Specifies the location and size of a null-terminated path name +/// to invoke as an interpreter. This segment type is mandatory for dynamic +/// executable files and can occur in shared objects. It cannot occur more +/// than once in a file. This type, if present, it must precede any loadable +/// segment entry. +#define PT_INTERP 3 +/// @brief Specifies the location and size of auxiliary information. +#define PT_NOTE 4 +/// @brief Reserved but has unspecified semantics. +#define PT_SHLIB 5 +/// @brief Specifies the location and size of the program header table +/// itself, both in the file and in the memory image of the program. +/// This segment type cannot occur more than once in a file. Moreover, +/// it can occur only if the program header table is part of the memory +/// image of the program. This type, if present, must precede any loadable +/// segment entry. +#define PT_PHDR 6 +/// @brief Section for supporting exception handling routines. +/// @details +/// The .eh_frame section has the same structure with .debug_frame, +/// which follows DWARF format. It represents the table that describes +/// how to set registers to restore the previous call frame at runtime. +#define PT_EH_FRAME 0x6474E550 +/// @brief Is a program header which tells the system how to control +/// the stack when the ELF is loaded into memory. +#define PT_GNU_STACK 0x6474E551 +/// @brief This segment indicates the memory region which should be made +/// Read-Only after relocation is done. This segment usually appears in a +/// dynamic link library and it contains .ctors, .dtors, .dynamic, .got s +/// ections. See paragraph below. +#define PT_GNU_RELRO 0x6474E552 +/// @brief TODO: Document. +#define PT_LOPROC 0x70000000 +/// @brief TODO: Document. +#define PT_HIPROC 0x7FFFFFFF -/// @brief A section header with all kinds of useful information. -typedef struct elf_section_header -{ - uint32_t name_offset_in_shstrtab; - uint32_t type; +/// @} + +/// Elf header ident size. +#define EI_NIDENT 16 + +/// @brief The elf starting section. +typedef struct elf_header { + /// Elf header identity bits. + uint8_t ident[EI_NIDENT]; + /// Identifies object file type. + uint16_t type; + /// Specifies target instruction set architecture. + uint16_t machine; + /// Set to 1 for the original version of ELF. + uint32_t version; + /// This is the memory address of the entry point from where the process starts executing. + uint32_t entry; + /// Points to the start of the program header table. + uint32_t phoff; + /// TODO: Comment. + uint32_t shoff; + /// TODO: Comment. uint32_t flags; - uint32_t addr; + /// TODO: Comment. + uint16_t ehsize; + /// TODO: Comment. + uint16_t phentsize; + /// TODO: Comment. + uint16_t phnum; + /// TODO: Comment. + uint16_t shentsize; + /// TODO: Comment. + uint16_t shnum; + /// TODO: Comment. + uint16_t shstrndx; +} elf_header_t; + +/// @brief The elf program header, holding program layout information +typedef struct elf_program_header { + /// Identifies the type of the segment. + uint32_t type; + /// Offset of the segment in the file image. uint32_t offset; + /// Virtual address of the segment in memory. + uint32_t vaddr; + /// On systems where physical address is relevant, reserved for + /// segment's physical address. + uint32_t paddr; + /// Size in bytes of the segment in the file image. May be 0. + uint32_t filesz; + /// Size in bytes of the segment in memory. May be 0. + uint32_t memsz; + /// Segment-dependent flags. + uint32_t flags; + /// Values of 0 or 1 specify no alignment. Otherwise should be a positive, + /// integral power of 2, with p_vaddr equating p_offset modulus p_align. + uint32_t align; +} elf_program_header_t; + +/// @brief A section header with all kinds of useful information +typedef struct elf_section_header { + /// TODO: Comment. + uint32_t name; + /// TODO: Comment. + uint32_t type; + /// TODO: Comment. + uint32_t flags; + /// TODO: Comment. + uint32_t addr; + /// TODO: Comment. + uint32_t offset; + /// TODO: Comment. uint32_t size; + /// TODO: Comment. uint32_t link; + /// TODO: Comment. uint32_t info; + /// TODO: Comment. uint32_t addralign; + /// TODO: Comment. uint32_t entsize; -} __attribute__((packed)) elf_section_header_t; +} elf_section_header_t; /// @brief A symbol itself. -typedef struct elf_symbol -{ - uint32_t name_offset_in_strtab; +typedef struct elf_symbol { + /// TODO: Comment. + uint32_t name; + /// TODO: Comment. uint32_t value; + /// TODO: Comment. uint32_t size; - uint8_t info; - uint8_t other; - uint16_t shndx; -} __attribute__((packed)) elf_symbol_t; + /// TODO: Comment. + uint8_t info; + /// TODO: Comment. + uint8_t other; + /// TODO: Comment. + uint16_t ndx; +} elf_symbol_t; -// Will hold the array of symbols and their names for us. -typedef struct elf_symbols -{ - elf_symbol_t *symtab; - uint32_t symtab_size; - const char *strtab; - uint32_t strtab_size; -} elf_symbols_t; +/// @brief Holds information about relocation object (that do not need an addend). +typedef struct elf_rel_t { + /// TODO: Comment. + uint32_t r_offset; + /// TODO: Comment. + uint32_t r_info; +} elf_rel_t; +/// @brief Holds information about relocation object (that need an addend). +typedef struct elf_rela_t { + /// TODO: Comment. + uint32_t r_offset; + /// TODO: Comment. + uint32_t r_info; + /// TODO: Comment. + int32_t r_addend; +} elf_rela_t; -/// @brief Builds as the set of elf symbols from a multiboot scructure. -void build_elf_symbols_from_multiboot (multiboot_info_t* mb); +/// @brief Fields index of ELF_IDENT. +enum Elf_Ident { + EI_MAG0 = 0, ///< 0x7F + EI_MAG1 = 1, ///< 'E' + EI_MAG2 = 2, ///< 'L' + EI_MAG3 = 3, ///< 'F' + EI_CLASS = 4, ///< Architecture (32/64) + EI_DATA = 5, ///< Set to either 1 or 2 to signify little or big endianness. + EI_VERSION = 6, ///< ELF Version + EI_OSABI = 7, ///< OS Specific + EI_ABIVERSION = 8, ///< OS Specific + EI_PAD = 9 ///< Padding +}; -/* Locate a symbol (only functions) in the following elf symbols - * Note that, for now, we'll only use this for the kernel (no other elf things..) - */ -const char *elf_lookup_symbol (uint32_t addr, elf_symbols_t *elf); +#define ELFMAG0 0x7F ///< e_ident[EI_MAG0] +#define ELFMAG1 'E' ///< e_ident[EI_MAG1] +#define ELFMAG2 'L' ///< e_ident[EI_MAG2] +#define ELFMAG3 'F' ///< e_ident[EI_MAG3] + +#define ELFDATA2LSB 1 ///< Little Endian +#define ELFCLASS32 1 ///< 32-bit Architecture + +/// @brief Type of ELF files. +typedef enum Elf_Type { + ET_NONE = 0, ///< Unkown Type + ET_REL = 1, ///< Relocatable File + ET_EXEC = 2 ///< Executable File +} Elf_Type; + +#define EM_386 3 ///< x86 Machine Type. +#define EV_CURRENT 1 ///< ELF Current Version. + +/// @brief Defines a number of different types of sections, which correspond +/// to values stored in the field sh_type in the section header +typedef enum ShT_Types { + SHT_NULL = 0, ///< Null section + SHT_PROGBITS = 1, ///< Program information + SHT_SYMTAB = 2, ///< Symbol table + SHT_STRTAB = 3, ///< String table + SHT_RELA = 4, ///< Relocation (w/ addend) + SHT_NOBITS = 8, ///< Not present in file + SHT_REL = 9, ///< Relocation (no addend) +} ShT_Types; + +/// @brief ShT_Attributes corresponds to the field sh_flags, but are bit +/// flags rather than stand-alone values. +enum ShT_Attributes { + SHF_WRITE = 0x01, ///< Writable section + SHF_ALLOC = 0x02 ///< Exists in memory +}; + +/// @brief Provide access to teh symbol biding. +#define ELF32_ST_BIND(INFO) ((INFO) >> 4) +/// @brief Provide access to teh symbol type. +#define ELF32_ST_TYPE(INFO) ((INFO)&0x0F) + +/// @brief Provides possible symbol bindings. +enum StT_Bindings { + STB_LOCAL = 0, ///< Local scope + STB_GLOBAL = 1, ///< Global scope + STB_WEAK = 2 ///< Weak, (ie. __attribute__((weak))) +}; + +/// @brief Provides a number of possible symbol types. +enum StT_Types { + STT_NOTYPE = 0, ///< No type + STT_OBJECT = 1, ///< Variables, arrays, etc. + STT_FUNC = 2 ///< Methods or functions +}; + +/// @brief Loads an ELF file into the memory of task. +/// @param task The task for which we load the ELF. +/// @param file The ELF file. +/// @param entry The ELF binary entry. +/// @return 0 if fails, 1 if succeed. +int elf_load_file(task_struct *task, vfs_file_t *file, uint32_t *entry); + +/// @brief Checks if the file is a valid ELF. +/// @param file The file to check. +/// @param type The type of ELF file we expect. +/// @return 0 if fails, 1 if succeed. +int elf_check_file_type(vfs_file_t *file, Elf_Type type); + +/// @brief Checks the correctness of the ELF header. +/// @param hdr The header to check. +/// @return 0 if fails, 1 if succeed. +int elf_check_file_header(elf_header_t *hdr); + +/// @brief Checks the correctness of the ELF header magic number. +/// @param hdr The header to check. +/// @return 0 if fails, 1 if succeed. +int elf_check_magic_number(elf_header_t *hdr); + +/// @brief Transforms the passed ELF type to string. +/// @param type The integer representing the ELF type. +/// @return The string representing the ELF type. +const char *elf_type_to_string(int type); + +/// @brief Transforms the passed ELF section header type to string. +/// @param type The integer representing the ELF section header type. +/// @return The string representing the ELF section header type. +const char *elf_section_header_type_to_string(int type); + +/// @brief Transforms the passed ELF symbol type to string. +/// @param type The integer representing the ELF symbol type. +/// @return The string representing the ELF symbol type. +const char *elf_symbol_type_to_string(int type); + +/// @brief Transforms the passed ELF symbol bind to string. +/// @param bind The integer representing the ELF symbol bind. +/// @return The string representing the ELF symbol bind. +const char *elf_symbol_bind_to_string(int bind); \ No newline at end of file diff --git a/mentos/inc/fs/fcntl.h b/mentos/inc/fs/fcntl.h deleted file mode 100644 index 15f9324..0000000 --- a/mentos/inc/fs/fcntl.h +++ /dev/null @@ -1,42 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file fcntl.h -/// @brief Headers of functions fcntl() and open(). -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" - -/// Open for reading only. -#define O_RDONLY 0x00 - -/// Open for writing only. -#define O_WRONLY 0x01 - -/// Open for reading and writing. -#define O_RDWR 0x02 - -/// If the file exists, this flag has no effect. Otherwise, the file is created. -#define O_CREAT 0x40 - -/// The file offset will be set to the end of the file prior to each write. -#define O_APPEND 0x400 - -/// @brief Given a pathname for a file, open() returns a file -/// descriptor, a small, nonnegative integer for use in -/// subsequent system calls. -/// @param pathname A pathname for a file. -/// @param flags Used to set the file status flags and file access modes -/// of the open file description. -/// @param mode Specifies the file mode bits be applied when a new file -/// is created. -/// @return Returns a file descriptor, a small, nonnegative integer -/// for use in subsequent system calls. -int sys_open(const char *pathname, int flags, mode_t mode); - -/// @brief Deletes the file whose name is specified in filename. -/// @param pathname A path to a file. -/// @return On success, zero is returned. On error, -1 is returned, -/// and errno is set appropriately. -int remove(const char *pathname); diff --git a/mentos/inc/fs/initrd.h b/mentos/inc/fs/initrd.h index 636df7f..d95cac4 100644 --- a/mentos/inc/fs/initrd.h +++ b/mentos/inc/fs/initrd.h @@ -1,131 +1,15 @@ /// MentOS, The Mentoring Operating system project /// @file initrd.h /// @brief Headers of functions for initrd filesystem. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "stat.h" -#include "dirent.h" -#include "unistd.h" -#include "stdint.h" -#include "kernel.h" +/// @brief Initialize the initrd filesystem. +/// @return 0 if fails, 1 if succeed. +int initrd_init_module(void); -/// The maximum number of files. -#define MAX_FILES 32 - -/// The maximum number of file descriptors. -#define MAX_INITRD_DESCRIPTORS MAX_OPEN_FD - -/// @brief Contains the number of files inside the initrd filesystem. -typedef struct initrd_t { - /// Number of files. - uint32_t nfiles; -} initrd_t; - -/// @brief Information concerning a file. -typedef struct initrd_file_t { - /// Number used as delimiter, it must be set to 0xBF. - int magic; - /// The name of the file. - char fileName[MAX_FILENAME_LENGTH]; - /// The type of the file. - short int file_type; - /// The uid of the owner. - int uid; - /// Offset of the starting address. - unsigned int offset; - /// Dimension of the file. - unsigned int length; -} initrd_file_t; - -/// @brief Descriptor linked to open files. -typedef struct initrd_fd { - /// Id of the open file inside the file system. More precisely, its index - /// inside the vector of files. - int file_descriptor; - /// The current position inside the file. Used for writing/reading - /// opeations. - int cur_pos; -} initrd_fd; - -// TODO: doxygen comment. -/// @brief -extern initrd_t *fs_specs; -/// @brief File system headers. -extern initrd_file_t *fs_headers; -// TODO: doxygen comment. -/// @brief -extern unsigned int fs_end; - -/// Initializes the initrd file system. -uint32_t initfs_init(); - -/// @brief Opens a directory at the given path. -/// @param path The path where the directory resides. -/// @return Structure used to access the directory. -DIR *initfs_opendir(const char *path); - -/// @brief Closes the directory stream associated with dirp. -/// @param dirp The directory handler. -/// @return 0 on success. On error, -1 is returned, and errno is set. -int initfs_closedir(DIR *dirp); - -/// @brief Moves the position of the currently readed entry inside the -/// directory. -/// @param dirp The directory handler. -/// @return A pointer to the next entry inside the directory. -dirent_t *initrd_readdir(DIR *dirp); - -/// @brief Creates a new directory. -/// @param path The path to the new directory. -/// @param mode The file mode. -/// @return 0 if success. -int initrd_mkdir(const char *path, mode_t mode); - -/// @brief Removes a directory. -/// @param path The path to the directory. -/// @return 0 if success. -int initrd_rmdir(const char *path); - -/// @brief Open the file at the given path and returns its file descriptor. -/// @param path The path to the file. -/// @param flags The flags used to determine the behavior of the function. -/// @return The file descriptor of the opened file, otherwise returns -1. -int initfs_open(const char *path, int flags, ...); - -/// @brief Deletes the file at the given path. -/// @param path The path to the file. -/// @return On success, zero is returned. On error, -1 is returned. -int initfs_remove(const char *path); - -/// @brief Reads from the file identified by the file descriptor. -/// @param fildes The file descriptor of the file. -/// @param buf Buffer where the read content must be placed. -/// @param nbyte The number of bytes to read. -/// @return T The number of red bytes. -ssize_t initfs_read(int fildes, char *buf, size_t nbyte); - -/// @brief Retrieves information concerning the file at the given position. -/// @param path The path where the file resides. -/// @param stat The structure where the information are stored. -/// @return 0 if success. -int initrd_stat(const char *path, stat_t *stat); - -/// @brief Writes the given content inside the file. -/// @param fildes The file descriptor of the file. -/// @param buf The content to write. -/// @param nbyte The number of bytes to write. -/// @return T The number of written bytes. -ssize_t initrd_write(int fildes, const void *buf, size_t nbyte); - -/// @brief Closes the given file. -/// @param fildes The file descriptor of the file. -int initrd_close(int fildes); - -// TODO: doxygen comment. -size_t initrd_nfiles(); - -// TODO: doxygen comment. -void dump_initrd_fs(); +/// @brief Clean up the initrd filesystem. +/// @return 0 if fails, 1 if succeed. +int initrd_cleanup_module(void); diff --git a/mentos/inc/fs/ioctl.h b/mentos/inc/fs/ioctl.h new file mode 100644 index 0000000..cab5e05 --- /dev/null +++ b/mentos/inc/fs/ioctl.h @@ -0,0 +1,13 @@ +/// MentOS, The Mentoring Operating system project +/// @file ioctl.h +/// @brief Declares device controlling operations. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +/// @brief Manipulates the underlying device parameters of special files, or operating +/// characteristics of character special files (e.g., terminals). +/// @param fd Must be an open file descriptor. +/// @param request The device-dependent request code +/// @param data An untyped pointer to memory. +/// @return On success zero is returned. +int sys_ioctl(int fd, int request, void *data); \ No newline at end of file diff --git a/mentos/inc/fs/procfs.h b/mentos/inc/fs/procfs.h new file mode 100644 index 0000000..039e9d9 --- /dev/null +++ b/mentos/inc/fs/procfs.h @@ -0,0 +1,69 @@ +/// MentOS, The Mentoring Operating system project +/// @file procfs.h +/// @brief Proc file system public functions and structures. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "process/process.h" + +/// @brief Stores information about a procfs directory entry. +typedef struct proc_dir_entry_t { + /// Generic system operations. + vfs_sys_operations_t *sys_operations; + /// Files operations. + vfs_file_operations_t *fs_operations; + /// Data associated with the dir_entry. + void *data; + /// Name of the entry. + const char *name; +} proc_dir_entry_t; + +/// @brief Initialize the procfs filesystem. +/// @return 0 if fails, 1 if succeed. +int procfs_module_init(); + +/// @brief Clean up the procfs filesystem. +/// @return 0 if fails, 1 if succeed. +int procfs_cleanup_module(); + +/// @brief Finds the direntry inside `/proc` or under the given `parent`. +/// @param name The name of the entry we are searching. +/// @param parent The parent (optional). +/// @return A pointer to the entry, or NULL. +proc_dir_entry_t *proc_dir_entry_get(const char *name, proc_dir_entry_t *parent); + +/// @brief Creates a new directory inside the procfs filesystem. +/// @param name The name of the entry we are creating. +/// @param parent The parent (optional). +/// @return A pointer to the entry, or NULL if fails. +proc_dir_entry_t *proc_mkdir(const char *name, proc_dir_entry_t *parent); + +/// @brief Removes a directory from the procfs filesystem. +/// @param name The name of the entry we are removing. +/// @param parent The parent (optional). +/// @return 0 if succeed, or -errno in case of error. +int proc_rmdir(const char *name, proc_dir_entry_t *parent); + +/// @brief Creates a new entry inside the procfs filesystem. +/// @param name The name of the entry we are creating. +/// @param parent The parent (optional). +/// @return A pointer to the entry, or NULL if fails. +proc_dir_entry_t *proc_create_entry(const char *name, proc_dir_entry_t *parent); + +/// @brief Removes an entry from the procfs filesystem. +/// @param name The name of the entry we are removing. +/// @param parent The parent (optional). +/// @return 0 if succeed, or -errno in case of error. +int proc_destroy_entry(const char *name, proc_dir_entry_t *parent); + +/// @brief Create the entire procfs entry tree for the give process. +/// @param entry Pointer to the task_struct of the process. +/// @return 0 if succeed, or -errno in case of error. +int proc_create_entry_pid(task_struct *entry); + +/// @brief Destroy the entire procfs entry tree for the give process. +/// @param entry Pointer to the task_struct of the process. +/// @return 0 if succeed, or -errno in case of error. +int proc_destroy_entry_pid(task_struct *entry); \ No newline at end of file diff --git a/mentos/inc/fs/read_write.h b/mentos/inc/fs/read_write.h deleted file mode 100644 index 1e7a403..0000000 --- a/mentos/inc/fs/read_write.h +++ /dev/null @@ -1,23 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file read_write.c -/// @brief Read and write functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" - -/// @brief Read data from a file descriptor. -/// @param fd The file descriptor. -/// @param buf The buffer. -/// @param nbytes The number of bytes to read. -/// @return The number of read characters. -ssize_t sys_read(int fd, void *buf, size_t nbytes); - -/// @brief Write data into a file descriptor. -/// @param fd The file descriptor. -/// @param buf The buffer collecting data to written. -/// @param nbytes The number of bytes to write. -/// @return The number of written bytes. -ssize_t sys_write(int fd, void *buf, size_t nbytes); diff --git a/mentos/inc/fs/vfs.h b/mentos/inc/fs/vfs.h index 360ebec..1a2a5e1 100644 --- a/mentos/inc/fs/vfs.h +++ b/mentos/inc/fs/vfs.h @@ -1,52 +1,169 @@ /// MentOS, The Mentoring Operating system project /// @file vfs.h /// @brief Headers for Virtual File System (VFS). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "unistd.h" -#include "vfs_types.h" +#include "fs/vfs_types.h" +#include "mem/slab.h" -/// The maximum number of mount points. -#define MAX_MOUNTPOINT 10 +#define MAX_OPEN_FD 16 ///< Maximum number of opened file. -/// The currently opened file descriptor. -extern int current_fd; +#define STDIN_FILENO 0 ///< Standard input. +#define STDOUT_FILENO 1 ///< Standard output. +#define STDERR_FILENO 2 ///< Standard error output. -/// The list of file descriptors. -extern file_descriptor_t fd_list[MAX_OPEN_FD]; +extern kmem_cache_t *vfs_file_cache; -/// The list of mount points. -extern mountpoint_t mountpoint_list[MAX_MOUNTPOINT]; +/// @brief Forward declaration of task_struct. +struct task_struct; + +/// @brief Searches for the mountpoint of the given path. +/// @param absolute_path Path for which we want to search the mountpoint. +/// @return Pointer to the vfs_file of the mountpoint. +super_block_t *vfs_get_superblock(const char *absolute_path); /// @brief Initialize the Virtual File System (VFS). void vfs_init(); -/// @brief Retrieves the id of the mount point where the path resides. -/// @param path The path to the mountpoint. -/// @return The id of the mountpoint. -int32_t get_mountpoint_id(const char *path); +/// @brief Register a new filesystem. +/// @param fs A pointer to the information concerning the new filesystem. +/// @return The outcome of the operation, 0 if fails. +int vfs_register_filesystem(file_system_type *fs); -// TODO: doxigen comment. -mountpoint_t * get_mountpoint(const char *path); +/// @brief Unregister a new filesystem. +/// @param fs A pointer to the information concerning the filesystem. +/// @return The outcome of the operation, 0 if fails. +int vfs_unregister_filesystem(file_system_type *fs); -// TODO: doxigen comment. -mountpoint_t * get_mountpoint_from_id(int32_t mp_id); +/// @brief Given a pathname for a file, kopen() returns a file struct, used to access the file. +/// @param pathname A pathname for a file. +/// @param flags Used to set the file status flags and file access modes of the open file description. +/// @param mode Specifies the file mode bits be applied when a new file is created. +/// @return Returns a file struct, or NULL. +vfs_file_t *vfs_open(const char *pathname, int flags, mode_t mode); -/// @brief A path is extracted from the relative path, excluding the -/// mountpoint. -/// @param mp_id Id of the mountpoint point of the file. -/// @param path Path to the file to be opened. -/// @return Path without the mountpoint part. -int get_relative_path(uint32_t mp_id, char *path); +/// @brief Decreases the number of references to a given file, if the +/// references number reaches 0, close the file. +/// @param file A pointer to the file structure. +/// @return The result of the call. +int vfs_close(vfs_file_t *file); -/// @brief Given a path, it extracts its absolute path (starting from -/// the current one). -/// @param path Path to the file to be opened. -/// @return Error code. -int get_absolute_path(char *path); +/// @brief Read data from a file. +/// @param file The file structure used to reference a file. +/// @param buf The buffer. +/// @param offset The offset from which the function starts to read. +/// @param nbytes The number of bytes to read. +/// @return The number of read characters. +ssize_t vfs_read(vfs_file_t *file, void *buf, size_t offset, size_t nbytes); -/// @brief Dumps the list of file descriptors. -void vfs_dump(); +/// @brief Write data to a file. +/// @param file The file structure used to reference a file. +/// @param buf The buffer. +/// @param offset The offset from which the function starts to write. +/// @param nbytes The number of bytes to write. +/// @return The number of written characters. +ssize_t vfs_write(vfs_file_t *file, void *buf, size_t offset, size_t nbytes); + +/// @brief Repositions the file offset inside a file. +/// @param file The file for which we reposition the offest. +/// @param offset The offest to use for the operation. +/// @param whence The type of operation. +/// @return Upon successful completion, returns the resulting offset +/// location as measured in bytes from the beginning of the file. On +/// error, the value (off_t) -1 is returned and errno is set to +/// indicate the error. +off_t vfs_lseek(vfs_file_t *file, off_t offset, int whence); + +/// Provide access to the directory entries. +/// @param file The directory for which we accessing the entries. +/// @param dirp The buffer where de data should be placed. +/// @param off The offset from which we start reading the entries. +/// @param count The size of the buffer. +/// @return On success, the number of bytes read is returned. On end of +/// directory, 0 is returned. On error, -1 is returned, and errno is set +/// appropriately. +int vfs_getdents(vfs_file_t *file, dirent_t *dirp, off_t off, size_t count); + +/// @brief Perform the I/O control operation specified by REQUEST on FD. +/// One argument may follow; its presence and type depend on REQUEST. +/// @param file The file for which we are executing the operations. +/// @param request The device-dependent request code +/// @param data An untyped pointer to memory. +/// @return Return value depends on REQUEST. Usually -1 indicates error. +int vfs_ioctl(vfs_file_t *file, int request, void *data); + +/// @brief Delete a name and possibly the file it refers to. +/// @param path The path to the file. +/// @return On success, zero is returned. On error, -1 is returned, and +/// errno is set appropriately. +int vfs_unlink(const char *path); + +/// @brief Creates a new directory at the given path. +/// @param path The path of the new directory. +/// @param mode The permission of the new directory. +/// @return Returns a negative value on failure. +int vfs_mkdir(const char *path, mode_t mode); + +/// @brief Removes the given directory. +/// @param path The path to the directory to remove. +/// @return Returns a negative value on failure. +int vfs_rmdir(const char *path); + +/// @brief Stat the file at the given path. +/// @param path Path to the file for which we are retrieving the statistics. +/// @param buf Buffer where we are storing the statistics. +/// @return 0 on success, a negative number if fails and errno is set. +int vfs_stat(const char *path, stat_t *buf); + +/// @brief Stat the given file. +/// @param file Pointer to the file for which we are retrieving the statistics. +/// @param buf Buffer where we are storing the statistics. +/// @return 0 on success, a negative number if fails and errno is set. +int vfs_fstat(vfs_file_t *file, stat_t *buf); + +/// @brief Mount a file system to the specified path. +/// @param path Path where we want to map the filesystem. +/// @param fs_root Root node of the filesystem. +/// @return 1 on success, 0 on fail. +/// @details +/// For example, if we have an EXT2 filesystem with a root node +/// of ext2_root and we want to mount it to /, we would run +/// vfs_mount("/", ext2_root); - or, if we have a procfs node, +/// we could mount that to /dev/procfs. Individual files can also +/// be mounted. +int vfs_mount(const char *path, vfs_file_t *fs_root); + +/// @brief Mount the path as a filesystem of the given type. +/// @param type The type of filesystem +/// @param path The path to where it should be mounter. +/// @param args The arguments passed to the filesystem mount callback. +/// @return 0 on success, a negative number if fails and errno is set. +int do_mount(const char *type, const char *path, const char *args); + +/// @brief Locks the access to the given file. +/// @param file The file to lock. +void vfs_lock(vfs_file_t *file); + +/// @brief Extends the file descriptor list for the given task. +/// @param task The task for which we extend the file descriptor list. +/// @return 0 on fail, 1 on success. +int vfs_extend_task_fd_list(struct task_struct *task); + +/// @brief Initialize the file descriptor list for the given task. +/// @param task The task for which we initialize the file descriptor list. +/// @return 0 on fail, 1 on success. +int vfs_init_task(struct task_struct *task); + +/// @brief Duplicate the file descriptor list of old_task into new_task. +/// @param new_task The task where we clone the file descriptor list. +/// @param old_task The task from which we clone the file descriptor list. +/// @return 0 on fail, 1 on success. +int vfs_dup_task(struct task_struct *new_task, struct task_struct *old_task); + +/// @brief Destroy the file descriptor list for the given task. +/// @param task The task for which we destroy the file descriptor list. +/// @return 0 on fail, 1 on success. +int vfs_destroy_task(struct task_struct *task); diff --git a/mentos/inc/fs/vfs_types.h b/mentos/inc/fs/vfs_types.h index ef8ef9a..4e9893e 100644 --- a/mentos/inc/fs/vfs_types.h +++ b/mentos/inc/fs/vfs_types.h @@ -1,137 +1,151 @@ /// MentOS, The Mentoring Operating system project /// @file vfs_types.h /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "stat.h" -#include "kernel.h" -#include "dirent.h" +#include "klib/list_head.h" +#include "sys/dirent.h" +#include "bits/stat.h" +#include "stdint.h" -/// Identifies a file. -#define FS_FILE 0x01 +#define PATH_SEPARATOR '/' ///< The character used as path separator. +#define PATH_SEPARATOR_STRING "/" ///< The string used as path separator. +#define PATH_UP ".." ///< The path to the parent. +#define PATH_DOT "." ///< The path to the current directory. -/// Identifies a directory. -#define FS_DIRECTORY 0x02 - -/// Identifies a character devies. -#define FS_CHARDEVICE 0x04 - -/// Identifies a block devies. -#define FS_BLOCKDEVICE 0x08 - -/// Identifies a pipe. -#define FS_PIPE 0x10 - -/// Identifies a symbolic link. -#define FS_SYMLINK 0x20 - -/// Identifies a mount-point. -#define FS_MOUNTPOINT 0x40 - -/// Function used to open a directory. -typedef DIR *(*opendir_callback)(const char *); - -/// Function used to close a directory. -typedef int (*closedir_callback)(DIR *); +/// Forward declaration of the VFS file. +typedef struct vfs_file_t vfs_file_t; /// Function used to create a directory. -typedef int (*mkdir_callback)(const char *, mode_t); - +typedef int (*vfs_mkdir_callback)(const char *, mode_t); /// Function used to remove a directory. -typedef int (*rmdir_callback)(const char *); - -/// Function used to read the next entry of a directory. -typedef dirent_t *(*readdir_callback)(DIR *); - -/// Function used to open a file. -typedef int (*open_callback)(const char *, int, ...); - +typedef int (*vfs_rmdir_callback)(const char *); +/// Function used to read the entries of a directory. +typedef int (*vfs_getdents_callback)(vfs_file_t *, dirent_t *, off_t, size_t); +/// Function used to open a file (or directory). +typedef vfs_file_t *(*vfs_open_callback)(const char *, int, mode_t); /// Function used to remove a file. -typedef int (*remove_callback)(const char *); - +typedef int (*vfs_unlink_callback)(const char *); /// Function used to close a file. -typedef int (*close_callback)(int); - +typedef int (*vfs_close_callback)(vfs_file_t *); /// Function used to read from a file. -typedef ssize_t (*read_callback)(int, char *, size_t); - +typedef ssize_t (*vfs_read_callback)(vfs_file_t *, char *, off_t, size_t); /// Function used to write inside a file. -typedef ssize_t (*write_callback)(int, const void *, size_t); - +typedef ssize_t (*vfs_write_callback)(vfs_file_t *, const void *, off_t, size_t); +/// Function used to reposition the file offset inside a file. +typedef off_t (*vfs_lseek_callback)(vfs_file_t *, off_t, int); /// Function used to stat fs entries. -typedef int (*stat_callback)(const char *, stat_t *); +typedef int (*vfs_stat_callback)(const char *, stat_t *); +/// Function used to stat files. +typedef int (*vfs_fstat_callback)(vfs_file_t *, stat_t *); +/// Function used to perform ioctl on files. +typedef int (*vfs_ioctl_callback)(vfs_file_t *, int, void *); -/// @brief Set of functions used to perform operations on directories. -typedef struct directory_operations_t { - /// Identifies a mount-point. - opendir_callback opendir_f; - /// Closes a directory. - closedir_callback closedir_f; - /// Creates a directory. - mkdir_callback mkdir_f; - /// Removes a directory. - rmdir_callback rmdir_f; - /// Read next entry inside the directory. - readdir_callback readdir_f; -} directory_operations_t; +/// @brief Filesystem information. +typedef struct file_system_type { + /// Name of the filesystem. + const char *name; + /// Flags of the filesystem. + int fs_flags; + /// Mount function. + vfs_file_t *(*mount)(const char *, const char *); +} file_system_type; + +/// @brief Set of functions used to perform operations on filesystem. +typedef struct vfs_sys_operations_t { + /// Creates a directory. + vfs_mkdir_callback mkdir_f; + /// Removes a directory. + vfs_rmdir_callback rmdir_f; + /// Stat function. + vfs_stat_callback stat_f; +} vfs_sys_operations_t; /// @brief Set of functions used to perform operations on files. -typedef struct super_node_operations_t { - /// Open a file. - open_callback open_f; - /// Remove a file. - remove_callback remove_f; - /// Close a file. - close_callback close_f; - /// Read from a file. - read_callback read_f; - /// Write inside a file. - write_callback write_f; -} super_node_operations_t; - -/// @brief Stat operations. -typedef struct stat_operations_t { - /// Stat function. - stat_callback stat_f; -} stat_operations_t; +typedef struct vfs_file_operations_t { + /// Open a file. + vfs_open_callback open_f; + /// Remove a file. + vfs_unlink_callback unlink_f; + /// Close a file. + vfs_close_callback close_f; + /// Read from a file. + vfs_read_callback read_f; + /// Write inside a file. + vfs_write_callback write_f; + /// Reposition the file offset inside a file. + vfs_lseek_callback lseek_f; + /// Stat the file. + vfs_fstat_callback stat_f; + /// Perform ioctl on file. + vfs_ioctl_callback ioctl_f; + /// Read entries inside the directory. + vfs_getdents_callback getdents_f; +} vfs_file_operations_t; /// @brief Data structure that contains information about the mounted filesystems. -typedef struct mountpoint_t { - /// The id of the mountpoint. - int32_t mp_id; - /// Name of the mountpoint. - char mountpoint[MAX_FILENAME_LENGTH]; - /// Maschera dei permessi. - unsigned int pmask; - /// User ID. - unsigned int uid; - /// Group ID. - unsigned int gid; - /// Starting address of the FileSystem. - unsigned int start_address; - /// Ending address of the FileSystem. - unsigned int end_address; - /// Device ID. - int dev_id; - /// Operations on files. - super_node_operations_t operations; - /// Operations on directories. - directory_operations_t dir_op; - /// Stat operations. - stat_operations_t stat_op; -} mountpoint_t; +struct vfs_file_t { + /// The filename. + char name[NAME_MAX]; + /// Device object (optional). + void *device; + /// The permissions mask. + uint32_t mask; + /// The owning user. + uint32_t uid; + /// The owning group. + uint32_t gid; + /// Flags (node type, etc). + uint32_t flags; + /// Inode number. + uint32_t ino; + /// Size of the file, in byte. + uint32_t length; + /// Used to keep track which fs it belongs to. + uint32_t impl; + /// Flags passed to open (read/write/append, etc.) + uint32_t open_flags; + /// Number of file descriptors associated with this file + int count; + /// Accessed (time). + uint32_t atime; + /// Modified (time). + uint32_t mtime; + /// Created (time). + uint32_t ctime; + /// Generic system operations. + vfs_sys_operations_t *sys_operations; + /// Files operations. + vfs_file_operations_t *fs_operations; + /// Offset for read operations. + size_t f_pos; + /// The number of links. + uint32_t nlink; + /// List to hold all active files associated with a specific entry in a filesystem. + list_head siblings; + /// TODO: Comment. + int32_t refcount; +}; + +/// @brief A structure that represents an instance of a filesystem, i.e., a mounted filesystem. +typedef struct super_block_t { + /// Name of the superblock. + char name[NAME_MAX]; + /// Pointer to the root file of the given filesystem. + vfs_file_t *root; + /// Pointer to the information regarding the filesystem. + file_system_type *type; + /// List to hold all active mounting points. + list_head mounts; +} super_block_t; /// @brief Data structure containing information about an open file. -typedef struct file_descriptor_t { - /// The descriptor of the internal file of the FileSystem. - int fs_spec_id; - /// The id of the mountpoint where the. - int mountpoint_id; - /// Offset for file reading, for the next read. - int offset; - /// Flags for file opening modes. - int flags_mask; -} file_descriptor_t; +typedef struct vfs_file_descriptor_t { + /// the underlying file structure + vfs_file_t *file_struct; + /// Flags for file opening modes. + int flags_mask; +} vfs_file_descriptor_t; diff --git a/mentos/inc/hardware/cmd_cpuid.h b/mentos/inc/hardware/cmd_cpuid.h deleted file mode 100644 index 742a698..0000000 --- a/mentos/inc/hardware/cmd_cpuid.h +++ /dev/null @@ -1,86 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cpuid.h -/// @brief CPUID definitions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" -#include "stdint.h" -#include "kernel.h" - -/// Dimension of the exc flags. -#define ECX_FLAGS_SIZE 24 - -/// Dimension of the edx flags. -#define EDX_FLAGS_SIZE 32 - -/// @brief Contains the information concerning the CPU. -typedef struct cpuinfo_t { - /// The name of the vendor. - char cpu_vendor[13]; - /// The type of the CPU. - char *cpu_type; - /// The family of the CPU. - uint32_t cpu_family; - /// The model of the CPU. - uint32_t cpu_model; - /// Identifier for individual cores when the CPU is interrogated by the - /// CPUID instruction. - uint32_t apic_id; - /// Ecx flags. - uint32_t cpuid_ecx_flags[ECX_FLAGS_SIZE]; - /// Edx flags. - uint32_t cpuid_edx_flags[EDX_FLAGS_SIZE]; - // TODO: doxygen comment. - int is_brand_string; - // TODO: doxygen comment. - char *brand_string; -} cpuinfo_t; - -/// This will be populated with the information concerning the CPU. -cpuinfo_t sinfo; - -/// @brief Main CPUID procedure. -/// @param cpuinfo Structure to fill with CPUID information. -void get_cpuid(cpuinfo_t *cpuinfo); - -/// @brief Actual CPUID call. -/// @param registers The registers to fill with the result of the call. -void call_cpuid(register_t *registers); - -/// @brief Extract vendor string. -/// @param cpuinfo The struct containing the CPUID infos. -/// @param registers The registers. -void cpuid_write_vendor(cpuinfo_t *cpuinfo, register_t *registers); - -// TODO: doxygen documentation. -/// @brief CPUID is called with EAX=1 -/// EAX contains Type, Family, Model and Stepping ID -/// EBX contains the Brand Index if supported, and the APIC ID -/// ECX/EDX contains feature information -/// @param cpuinfo -/// @param registers -void cpuid_write_proctype(cpuinfo_t *cpuinfo, register_t *registers); - -/// @brief EAX=1, ECX contains a list of supported features. -void cpuid_feature_ecx(cpuinfo_t *, uint32_t); - -/// @brief EAX=1, EDX contains a list of supported features. -void cpuid_feature_edx(cpuinfo_t *, uint32_t); - -// TODO: doxygen documentation. -/// @brief Extract single byte from a register. -/// @param reg -/// @param position -/// @param value -/// @return -uint32_t cpuid_get_byte(const uint32_t reg, const uint32_t position, - const uint32_t value); - -/// @brief Index of brand strings. -char *cpuid_brand_index(register_t *); - -/// @brief Brand string is contained in EAX, EBX, ECX and EDX. -char *cpuid_brand_string(register_t *); diff --git a/mentos/inc/hardware/cpuid.h b/mentos/inc/hardware/cpuid.h new file mode 100644 index 0000000..65313ff --- /dev/null +++ b/mentos/inc/hardware/cpuid.h @@ -0,0 +1,96 @@ +/// MentOS, The Mentoring Operating system project +/// @file cpuid.h +/// @brief CPUID definitions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stdint.h" +#include "kernel.h" + +/// Dimension of the exc flags. +#define ECX_FLAGS_SIZE 24 + +/// Dimension of the edx flags. +#define EDX_FLAGS_SIZE 32 + +/// @brief Contains the information concerning the CPU. +typedef struct cpuinfo_t { + /// The name of the vendor. + char cpu_vendor[13]; + /// The type of the CPU. + char *cpu_type; + /// The family of the CPU. + uint32_t cpu_family; + /// The model of the CPU. + uint32_t cpu_model; + /// Identifier for individual cores when the CPU is interrogated by the + /// CPUID instruction. + uint32_t apic_id; + /// Ecx flags. + uint32_t cpuid_ecx_flags[ECX_FLAGS_SIZE]; + /// Edx flags. + uint32_t cpuid_edx_flags[EDX_FLAGS_SIZE]; + /// TODO: Comment. + int is_brand_string; + /// TODO: Comment. + char *brand_string; +} cpuinfo_t; + +/// This will be populated with the information concerning the CPU. +cpuinfo_t sinfo; + +/// @brief Main CPUID procedure. +/// @param cpuinfo Structure to fill with CPUID information. +void get_cpuid(cpuinfo_t *cpuinfo); + +/// @brief Actual CPUID call. +/// @param registers The registers to fill with the result of the call. +void call_cpuid(pt_regs *registers); + +/// @brief Extract vendor string. +/// @param cpuinfo The struct containing the CPUID infos. +/// @param registers The registers. +void cpuid_write_vendor(cpuinfo_t *cpuinfo, pt_regs *registers); + +// TODO: doxygen documentation. +/// @brief +/// @param cpuinfo +/// @param registers +/// @details +/// CPUID is called with EAX=1 +/// EAX contains Type, Family, Model and Stepping ID +/// EBX contains the Brand Index if supported, and the APIC ID +/// ECX/EDX contains feature information +void cpuid_write_proctype(cpuinfo_t *cpuinfo, pt_regs *registers); + +// TODO: doxygen documentation. +/// @brief EAX=1, ECX contains a list of supported features. +/// @param cpuinfo +/// @param ecx +void cpuid_feature_ecx(cpuinfo_t *cpuinfo, uint32_t ecx); + +// TODO: doxygen documentation. +/// @brief EAX=1, EDX contains a list of supported features. +/// @param cpuinfo +/// @param edx +void cpuid_feature_edx(cpuinfo_t *cpuinfo, uint32_t edx); + +// TODO: doxygen documentation. +/// @brief Extract single byte from a register. +/// @param reg +/// @param position +/// @param value +/// @return +uint32_t cpuid_get_byte(uint32_t reg, uint32_t position, uint32_t value); + +/// @brief Index of brand strings. TODO: Document +/// @param f Stack frame. +/// @return The brand string. +char *cpuid_brand_index(pt_regs *f); + +/// @brief Brand string is contained in EAX, EBX, ECX and EDX. +/// @param f Stack frame. +/// @return The brand string. +char *cpuid_brand_string(pt_regs *f); diff --git a/mentos/inc/hardware/pic8259.h b/mentos/inc/hardware/pic8259.h index 7d409c8..068c902 100644 --- a/mentos/inc/hardware/pic8259.h +++ b/mentos/inc/hardware/pic8259.h @@ -1,20 +1,12 @@ /// MentOS, The Mentoring Operating system project /// @file pic8259.h /// @brief Pic8259 definitions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "idt.h" -#include "kheap.h" -#include "debug.h" -#include "stddef.h" -#include "kernel.h" -#include "bitops.h" -#include "port_io.h" -#include "irqflags.h" -#include "scheduler.h" +#include "stdint.h" /// The total number of IRQs. #define IRQ_NUM 16 diff --git a/mentos/inc/hardware/timer.h b/mentos/inc/hardware/timer.h index a3a1860..0faa231 100644 --- a/mentos/inc/hardware/timer.h +++ b/mentos/inc/hardware/timer.h @@ -1,85 +1,211 @@ /// MentOS, The Mentoring Operating system project /// @file timer.h /// @brief Programmable Interval Timer (PIT) definitions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "kernel.h" #include "stdint.h" +#include "klib/list_head.h" +#include "klib/spinlock.h" +#include "process/process.h" +#include "time.h" -/// @defgroup picregs Programmable Interval Timer Registers -/// @brief The list of registers used to set the PIT. -/// @{ +/// This enables the dynamic timer system use an hierarchical timing wheel, +/// an optimized data structure that allows O(1) insertion, O(1) deletion and +/// ammortized O(1) tick update. +#define ENABLE_REAL_TIMER_SYSTEM -/// Channel 0 data port (read/write). -#define PIT_DATAREG0 0x40 +/// This enables the system dump tvec_base timer vectors content on +/// the console. +#define ENABLE_REAL_TIMER_SYSTEM_DUMP -/// Channel 1 data port (read/write). -#define PIT_DATAREG1 0x41 - -/// Channel 2 data port (read/write). -#define PIT_DATAREG2 0x42 - -/// Mode/Command register (write only, a read is ignored). -#define PIT_COMREG 0x43 - -/// @} - -/// @brief Frequency divider value (1.193182 MHz). -#define PIT_DIVISOR 1193180 - -/// @brief Command used to configure the PIT. -/// @details -/// 0x36 = 00110110B. -/// Channel | 00 | Select Channel 0. -/// Access mode | 11 | lobyte/hibyte. -/// Operating mode | 011 | Mode 3 (square wave generator). -/// BCD/Binary mode | 0 | 16-bit binary. -#define PIT_CONFIGURATION 0x36 - -#define PIT_MASK 0xFF - -/// @brief Pointer to a functionality to wake up. -typedef void (*wakeup_callback_t)(); - -/// @brief Holds the information about a wake-up functionality. -typedef struct wakeup_info { - /// Pointer to the functionality. - wakeup_callback_t func; - /// The tick, in the future, when the functionality must be triggered. - __volatile__ uint32_t wakeup_at_jiffy; - /// The period in seconds. - uint32_t period; -} wakeup_info_t; +/// Counts down in real (i.e., wall clock) time. +#define ITIMER_REAL 0 +/// Counts down against the user-mode CPU time consumed by the process. +#define ITIMER_VIRTUAL 1 +/// This timer counts down against the total (i.e., both user and system) CPU +/// time consumed by the process. +#define ITIMER_PROF 2 /// @brief Handles the timer. -/// @details In this case, it's very simple: We -/// increment the 'timer_ticks' variable every time the -/// timer fires. By default, the timer fires 18.222 times -/// per second. Why 18.222Hz? Some engineer at IBM must've -/// been smoking something funky -void timer_handler(pt_regs *reg); +/// @param f The interrupt stack frame. +/// @details +/// In this case, it's very simple: We increment the 'timer_ticks' variable +/// every time the timer fires. By default, the timer fires 18.222 times +/// per second. Why 18.222Hz? Some engineer at IBM must've been smoking +/// something funky. +void timer_handler(pt_regs *f); /// @brief Sets up the system clock by installing the timer handler into IRQ0. void timer_install(); -/// @brief Returns the number of ticks since the system is running. -uint64_t timer_get_ticks(); +/// @brief Returns the number of seconds since the system started its execution. +/// @return Value in seconds. +uint64_t timer_get_seconds(); -/// @brief Returns the number of ticks since the system is running. -uint64_t timer_get_subticks(); - -/// @brief Registers a function which will be waken up at each tick. -/// @param func The functionality which must be triggered. -/// @param period The period in second. -void timer_register_wakeup_call(wakeup_callback_t func, uint32_t period); - -/// @brief Makes the process sleep for the given ammount of time. -/// @param seconds The ammount of seconds. -void sleep(unsigned int seconds); +/// @brief Returns the number of ticks since the system started its execution. +/// @return Value in ticks. +unsigned long timer_get_ticks(); /// @brief Allows to set the timer phase to the given frequency. /// @param hz The frequency to set. void timer_phase(const uint32_t hz); + +// =============================================================================== +// Per-CPU timer vectors + +#ifdef ENABLE_REAL_TIMER_SYSTEM + +/// Number of bits for the normal timer vector. +#define TVN_BITS 6 +/// Number of bits for the root timer vector. +#define TVR_BITS 8 +/// Number of headers in a normal timer vector. +#define TVN_SIZE (1 << TVN_BITS) +/// Number of headers in a root timer vector. +#define TVR_SIZE (1 << TVR_BITS) +/// A mask with all 1s for the normal timer vector (0b00111111) +#define TVN_MASK (TVN_SIZE - 1) +/// A mask with all 1s for the root timer vector (0b11111111) +#define TVR_MASK (TVR_SIZE - 1) +/// A shift used to calculate a timer position inside the tvec_base structure +#define TIMER_TICKS_BITS(tv) (TVR_BITS + TVN_BITS * (tv)) +/// Expiration ticks of timer based on position inside tvec_base structure +#define TIMER_TICKS(tv) (1 << TIMER_TICKS_BITS(tv)) + +/// @brief Root timer vector. +typedef struct timer_vec_root { + /// Array of lists of timers + list_head vec[TVR_SIZE]; +} timer_vec_root; + +/// @brief Normal timer vector. +typedef struct timer_vec { + /// Array of lists of timers + list_head vec[TVN_SIZE]; +} timer_vec; + +#endif + +/// @brief Contains all the timers of a single CPU +typedef struct tvec_base_s { + /// Lock for the timer data structure + spinlock_t lock; + /// Points to the dynamic timer that is currently handled by the CPU. + struct timer_list *running_timer; +#ifdef ENABLE_REAL_TIMER_SYSTEM + /// The earliest expiration time of the dynamic timers yet to be checked + unsigned long timer_ticks; + + /// Lists of timers that will expires in the next 255 ticks + struct timer_vec_root tv1; + /// Lists of timers that will expires in the next 2^14 - 1 ticks + struct timer_vec tv2; + /// Lists of timers that will expires in the next 2^20 - 1 ticks + struct timer_vec tv3; + /// Lists of timers that will expires in the next 2^26 - 1 ticks + struct timer_vec tv4; + /// Lists of timers with extremely large expires fields (2^32 - 1 ticks) + struct timer_vec tv5; + +#else + /// List of all the timers + struct list_head list; +#endif + +} tvec_base_t; + +/// @brief Represents the request to execute a function in the future, also +/// known as timer. +struct timer_list { + /// Protects the access to the timer. + spinlock_t lock; + /// Lists of timers are mantained using the list_head. + struct list_head entry; + /// Ticks value when the timer has to expire + unsigned long expires; + /// Functions to be executed when the timer expires + void (*function)(unsigned long); + /// Custom data to be passed to the timer function + unsigned long data; + /// Pointer to the structure containing all the other related timers. + tvec_base_t *base; +}; + +/// @brief Initialize dynamic timer system +void dynamic_timers_install(); + +/// @brief Initializes a new timer struct. +/// @param timer The timer to initialize. +void init_timer(struct timer_list *timer); + +/// @brief Updates the timer data structures +void run_timer_softirq(); + +/// @brief Add a new timer to the current CPU. +/// @param timer The timer to add. +void add_timer(struct timer_list *timer); + +/// @brief Removes a timer from the current CPU. +/// @param timer The timer to remove. +void del_timer(struct timer_list *timer); + +/// @brief Updates and executes dynamics timers +void run_timer_softirq(); + +/// @brief Suspends the execution of the calling thread. +/// @param req The amount of time we want to sleep. +/// @param rem The remaining time we did not sleep. +/// @return If the call is interrupted by a signal handler, nanosleep() +/// returns -1, sets errno to EINTR, and writes the remaining time +/// into the structure pointed to by rem unless rem is NULL. +/// @details +/// The execution is suspended until either at least the time specified +/// in *req has elapsed, or the delivery of a signal that triggers the +/// invocation of a handler in the calling thread or that terminates +/// the process. +int sys_nanosleep(const timespec *req, timespec *rem); + +/// @brief Send signal to calling thread after desired seconds. +/// @param seconds The number of seconds in the interval +/// @return the number of seconds remaining until any previously scheduled +/// alarm was due to be delivered, or zero if there was no previously +/// scheduled alarm. +int sys_alarm(int seconds); + +/// @brief Rappresents a time value. +struct timeval { + time_t tv_sec; ///< Seconds. + time_t tv_usec; ///< Microseconds. +}; + +/// @brief Rappresents a time interval. +struct itimerval { + struct timeval it_interval; ///< Next value. + struct timeval it_value; ///< Current value. +}; + +/// @brief Fills the structure pointed to by curr_value with the current setting for the timer specified by which. +/// @param which The time domain (i.e., ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF) +/// @param curr_value There the values must be stored. +/// @return Zero on success, or a negative value indicating the error. +int sys_getitimer(int which, struct itimerval *curr_value); + +/// @brief Arms or disarms the timer specified by which, by setting the timer +/// to the value specified by new_value. +/// @details +/// The system provides each process with three interval timers, each +/// decrementing in a distinct time domain. When any timer expires, +/// a signal is sent to the process, and the timer (potentially) restarts. +/// @param which The time domain (i.e., ITIMER_REAL, ITIMER_VIRTUAL, ITIMER_PROF) +/// @param new_value The new value to set. +/// @param old_value Where the old value must be stored. +/// @return Zero on success, or a negative value indicating the error. +int sys_setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value); + +/// @brief Update the profiling timer and generate SIGPROF if it has expired. +/// @param proc The process for which we must update the profiling. +void update_process_profiling_timer(task_struct *proc); \ No newline at end of file diff --git a/mentos/inc/io/mm_io.h b/mentos/inc/io/mm_io.h index 6f3bc3a..152886e 100644 --- a/mentos/inc/io/mm_io.h +++ b/mentos/inc/io/mm_io.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file mm_io.h /// @brief Memory Mapped IO functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -9,19 +9,31 @@ #include "stdint.h" /// @brief Reads a 8-bit value from the given address. +/// @param addr The address from which we read. +/// @return The content read from the address. uint8_t in_memb(uint32_t addr); /// @brief Reads a 16-bit value from the given address. +/// @param addr The address from which we read. +/// @return The content read from the address. uint16_t in_mems(uint32_t addr); /// @brief Reads a 32-bit value from the given address. +/// @param addr The address from which we read. +/// @return The content read from the address. uint32_t in_meml(uint32_t addr); /// @brief Writes a 8-bit value at the given address. +/// @param addr The address where we want to write. +/// @param value The value we want to write. void out_memb(uint32_t addr, uint8_t value); /// @brief Writes a 16-bit value at the given address. +/// @param addr The address where we want to write. +/// @param value The value we want to write. void out_mems(uint32_t addr, uint16_t value); /// @brief Writes a 32-bit value at the given address. +/// @param addr The address where we want to write. +/// @param value The value we want to write. void out_meml(uint32_t addr, uint32_t value); diff --git a/mentos/inc/io/port_io.h b/mentos/inc/io/port_io.h index 270ad71..1ec56dd 100644 --- a/mentos/inc/io/port_io.h +++ b/mentos/inc/io/port_io.h @@ -1,43 +1,51 @@ /// MentOS, The Mentoring Operating system project /// @file port_io.h /// @brief Byte I/O on ports prototypes. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "stdint.h" -/// @brief Used for reading from the I/O ports. +/// @brief Used for reading from the I/O ports. /// @param port The input port. -/// @return The read value. +/// @return The read value. uint8_t inportb(uint16_t port); -/// @brief Used for reading 2 bytes from the I/O ports. +/// @brief Used for reading 2 bytes from the I/O ports. /// @param port The input port. -/// @return The read value. +/// @return The read value. uint16_t inports(uint16_t port); +/// @brief Read from the given port and store what we read in `data`. +/// @param port The input port. +/// @param data The buffer where we store what we read. +/// @param size The dimension of the buffer. void inportsm(uint16_t port, uint8_t *data, unsigned long size); -/// @brief Used for reading 4 bytes from the I/O ports. +/// @brief Used for reading 4 bytes from the I/O ports. /// @param port The input port. -/// @return The read value. +/// @return The read value. uint32_t inportl(uint16_t port); -/// @brief Use this to write to I/O ports to send bytes to devices. +/// @brief Use this to write to I/O ports to send bytes to devices. /// @param port The output port. /// @param data The data to write. void outportb(uint16_t port, uint8_t data); -/// @brief Use this to write to I/O ports to send 2 bytes to devices. +/// @brief Use this to write to I/O ports to send 2 bytes to devices. /// @param port The output port. /// @param data The data to write. void outports(uint16_t port, uint16_t data); +/// @brief Write to the given port what it is stored inside `data`. +/// @param port The output port. +/// @param data The buffer where we have the data. +/// @param size The dimension of the buffer. void outportsm(uint16_t port, uint8_t *data, uint16_t size); -/// @brief Use this to write to I/O ports to send 4 bytes to devices. +/// @brief Use this to write to I/O ports to send 4 bytes to devices. /// @param port The output port. /// @param data The data to write. void outportl(uint16_t port, uint32_t data); diff --git a/mentos/inc/io/proc_modules.h b/mentos/inc/io/proc_modules.h new file mode 100644 index 0000000..6dbd4a2 --- /dev/null +++ b/mentos/inc/io/proc_modules.h @@ -0,0 +1,17 @@ +/// MentOS, The Mentoring Operating system project +/// @file proc_modules.h +/// @brief Contains functions for managing procfs filesystems. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "fs/vfs.h" + +/// @brief Initialize the procfs video files. +/// @return 0 on success, 1 on failure. +int procv_module_init(); + +/// @brief Initialize the procfs system files. +/// @return 0 on success, 1 on failure. +int procs_module_init(); diff --git a/mentos/inc/io/vga/vga.h b/mentos/inc/io/vga/vga.h new file mode 100644 index 0000000..db8dfd9 --- /dev/null +++ b/mentos/inc/io/vga/vga.h @@ -0,0 +1,24 @@ + +#pragma once + +void vga_initialize(); +void vga_finalize(); + +int vga_is_enabled(); + +int vga_width(); +int vga_height(); + +void vga_clear_screen(); + +void vga_draw_char(unsigned int x, unsigned int y, unsigned char c, unsigned char color); + +void vga_draw_string(int x, int y, char *str, unsigned char color); + +void vga_draw_line(unsigned int x0, unsigned int y0, unsigned int x1, unsigned int y1, unsigned char color); + +void vga_draw_rectangle(unsigned int sx, unsigned int sy, unsigned int ex, unsigned int ey, unsigned char fill); + +void vga_draw_circle(unsigned int xc, unsigned int yc, unsigned int r, unsigned char col); + +void vga_draw_triangle(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, unsigned int x3, unsigned int y3, unsigned char col); \ No newline at end of file diff --git a/mentos/inc/io/vga/vga_font.h b/mentos/inc/io/vga/vga_font.h new file mode 100644 index 0000000..33bbf68 --- /dev/null +++ b/mentos/inc/io/vga/vga_font.h @@ -0,0 +1,919 @@ +/// MentOS, The Mentoring Operating system project +/// @file vga_font.h +/// @brief VGA fonts. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// Font 8x16. +unsigned char arr_8x16_font[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7E, 0x81, 0xA5, 0x81, 0x81, 0xBD, 0x99, 0x81, 0x81, 0x7E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7E, 0xFF, 0xDB, 0xFF, 0xFF, 0xC3, 0xE7, 0xFF, 0xFF, 0x7E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6C, 0xFE, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3C, 0x3C, 0xE7, 0xE7, 0xE7, 0x99, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x7E, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xC3, 0xC3, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x42, 0x42, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x99, 0xBD, 0xBD, 0x99, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x1E, 0x0E, 0x1A, 0x32, 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0x33, 0x3F, 0x30, 0x30, 0x30, 0x30, 0x70, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0x63, 0x7F, 0x63, 0x63, 0x63, 0x63, 0x67, 0xE7, 0xE6, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xDB, 0x3C, 0xE7, 0x3C, 0xDB, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFE, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x06, 0x0E, 0x1E, 0x3E, 0xFE, 0x3E, 0x1E, 0x0E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0xDB, 0xDB, 0xDB, 0x7B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7C, 0xC6, 0x60, 0x38, 0x6C, 0xC6, 0xC6, 0x6C, 0x38, 0x0C, 0xC6, 0x7C, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x7E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0C, 0xFE, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xFE, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x6C, 0xFE, 0x6C, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7C, 0x7C, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x7C, 0x7C, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x3C, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7C, 0xC6, 0xC2, 0xC0, 0x7C, 0x06, 0x86, 0xC6, 0x7C, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC2, 0xC6, 0x0C, 0x18, 0x30, 0x60, 0xC6, 0x86, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xCE, 0xD6, 0xD6, 0xE6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x06, 0x3C, 0x06, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xC0, 0xC0, 0xC0, 0xFC, 0x0E, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x60, 0xC0, 0xC0, 0xFC, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xC6, 0x06, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0x06, 0x0C, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0x0C, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xDE, 0xDE, 0xDE, 0xDC, 0xC0, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xC0, 0xC0, 0xC2, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xDE, 0xC6, 0xC6, 0x66, 0x3A, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE6, 0x66, 0x6C, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xE6, 0xF6, 0xFE, 0xDE, 0xCE, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0xDE, 0x7C, 0x0C, 0x0E, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0x60, 0x38, 0x0C, 0x06, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7E, 0x7E, 0x5A, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0xD6, 0xFE, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0x6C, 0x6C, 0x38, 0x38, 0x6C, 0x6C, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xC6, 0x86, 0x0C, 0x18, 0x30, 0x60, 0xC2, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE0, 0x60, 0x60, 0x78, 0x6C, 0x66, 0x66, 0x66, 0x66, 0xDC, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC0, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x0C, 0x0C, 0x3C, 0x6C, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6C, 0x64, 0x60, 0xF0, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xCC, 0x78, 0x00, + 0x00, 0x00, 0xE0, 0x60, 0x60, 0x6C, 0x76, 0x66, 0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x00, 0x0E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3C, 0x00, + 0x00, 0x00, 0xE0, 0x60, 0x60, 0x66, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xFE, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0x0C, 0x1E, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x76, 0x62, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0x60, 0x38, 0x0C, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x30, 0x30, 0xFC, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xD6, 0xD6, 0xFE, 0x6C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x38, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x0C, 0xF8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xCC, 0x18, 0x30, 0x60, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xC0, 0xC2, 0x66, 0x3C, 0x0C, 0x06, 0x7C, 0x00, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0C, 0x18, 0x30, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6C, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6C, 0x38, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x60, 0x60, 0x66, 0x3C, 0x0C, 0x06, 0x3C, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6C, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3C, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xC6, 0xC6, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6C, 0x38, 0x00, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xFE, 0x66, 0x60, 0x7C, 0x60, 0x60, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x76, 0x36, 0x7E, 0xD8, 0xD8, 0x6E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3E, 0x6C, 0xCC, 0xCC, 0xFE, 0xCC, 0xCC, 0xCC, 0xCC, 0xCE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6C, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x78, 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x0C, 0x78, 0x00, + 0x00, 0xC6, 0xC6, 0x00, 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xC6, 0xC6, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6C, 0x64, 0x60, 0xF0, 0x60, 0x60, 0x60, 0x60, 0xE6, 0xFC, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xF8, 0xCC, 0xCC, 0xF8, 0xC4, 0xCC, 0xDE, 0xCC, 0xCC, 0xCC, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0E, 0x1B, 0x18, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0xD8, 0x70, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0C, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xDC, 0x00, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x76, 0xDC, 0x00, 0xC6, 0xE6, 0xF6, 0xFE, 0xDE, 0xCE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3C, 0x6C, 0x6C, 0x3E, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xC0, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xC0, 0xC0, 0xC2, 0xC6, 0xCC, 0x18, 0x30, 0x60, 0xCE, 0x93, 0x06, 0x0C, 0x1F, 0x00, 0x00, + 0x00, 0xC0, 0xC0, 0xC2, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xCE, 0x9A, 0x3F, 0x06, 0x0F, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3C, 0x3C, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, + 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xF6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xF6, 0x06, 0xF6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x06, 0xF6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xF6, 0x06, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xF7, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xF7, 0x00, 0xF7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xFF, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDC, 0xD8, 0xD8, 0xD8, 0xDC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xC6, 0xFC, 0xC6, 0xC6, 0xFC, 0xC0, 0xC0, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xC6, 0xC6, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0xFE, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFE, 0xC6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xDC, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7E, 0x18, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0x6C, 0x6C, 0x6C, 0x6C, 0xEE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1E, 0x30, 0x18, 0x0C, 0x3E, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xDB, 0xDB, 0xDB, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x06, 0x7E, 0xCF, 0xDB, 0xF3, 0x7E, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1C, 0x30, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x30, 0x1C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0E, 0x1B, 0x1B, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xD8, 0xD8, 0xD8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDC, 0x00, 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0F, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xEC, 0x6C, 0x6C, 0x3C, 0x1C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xD8, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0x98, 0x30, 0x60, 0xC8, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/// Font 8x8. +unsigned char arr_8x8_font[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7E, 0x81, 0xA5, 0x81, 0xBD, 0x99, 0x81, 0x7E, + 0x7E, 0xFF, 0xDB, 0xFF, 0xC3, 0xE7, 0xFF, 0x7E, + 0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, + 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, 0x00, + 0x38, 0x7C, 0x38, 0xFE, 0xFE, 0x92, 0x10, 0x7C, + 0x00, 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x7C, + 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x00, 0x00, + 0xFF, 0xFF, 0xE7, 0xC3, 0xC3, 0xE7, 0xFF, 0xFF, + 0x00, 0x3C, 0x66, 0x42, 0x42, 0x66, 0x3C, 0x00, + 0xFF, 0xC3, 0x99, 0xBD, 0xBD, 0x99, 0xC3, 0xFF, + 0x0F, 0x07, 0x0F, 0x7D, 0xCC, 0xCC, 0xCC, 0x78, + 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18, + 0x3F, 0x33, 0x3F, 0x30, 0x30, 0x70, 0xF0, 0xE0, + 0x7F, 0x63, 0x7F, 0x63, 0x63, 0x67, 0xE6, 0xC0, + 0x99, 0x5A, 0x3C, 0xE7, 0xE7, 0x3C, 0x5A, 0x99, + 0x80, 0xE0, 0xF8, 0xFE, 0xF8, 0xE0, 0x80, 0x00, + 0x02, 0x0E, 0x3E, 0xFE, 0x3E, 0x0E, 0x02, 0x00, + 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x7E, 0x3C, 0x18, + 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, + 0x7F, 0xDB, 0xDB, 0x7B, 0x1B, 0x1B, 0x1B, 0x00, + 0x3E, 0x63, 0x38, 0x6C, 0x6C, 0x38, 0x86, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x7E, 0x00, + 0x18, 0x3C, 0x7E, 0x18, 0x7E, 0x3C, 0x18, 0xFF, + 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00, + 0x00, 0x18, 0x0C, 0xFE, 0x0C, 0x18, 0x00, 0x00, + 0x00, 0x30, 0x60, 0xFE, 0x60, 0x30, 0x00, 0x00, + 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xFE, 0x00, 0x00, + 0x00, 0x24, 0x66, 0xFF, 0x66, 0x24, 0x00, 0x00, + 0x00, 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x7E, 0x3C, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00, + 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6C, 0x6C, 0xFE, 0x6C, 0xFE, 0x6C, 0x6C, 0x00, + 0x18, 0x7E, 0xC0, 0x7C, 0x06, 0xFC, 0x18, 0x00, + 0x00, 0xC6, 0xCC, 0x18, 0x30, 0x66, 0xC6, 0x00, + 0x38, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0x76, 0x00, + 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, + 0x60, 0x30, 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, + 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, + 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, + 0x7C, 0xCE, 0xDE, 0xF6, 0xE6, 0xC6, 0x7C, 0x00, + 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x00, + 0x78, 0xCC, 0x0C, 0x38, 0x60, 0xCC, 0xFC, 0x00, + 0x78, 0xCC, 0x0C, 0x38, 0x0C, 0xCC, 0x78, 0x00, + 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, 0x0C, 0x1E, 0x00, + 0xFC, 0xC0, 0xF8, 0x0C, 0x0C, 0xCC, 0x78, 0x00, + 0x38, 0x60, 0xC0, 0xF8, 0xCC, 0xCC, 0x78, 0x00, + 0xFC, 0xCC, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00, + 0x78, 0xCC, 0xCC, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x78, 0xCC, 0xCC, 0x7C, 0x0C, 0x18, 0x70, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, + 0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x00, + 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00, + 0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0x00, + 0x3C, 0x66, 0x0C, 0x18, 0x18, 0x00, 0x18, 0x00, + 0x7C, 0xC6, 0xDE, 0xDE, 0xDC, 0xC0, 0x7C, 0x00, + 0x30, 0x78, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0x00, + 0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xFC, 0x00, + 0x3C, 0x66, 0xC0, 0xC0, 0xC0, 0x66, 0x3C, 0x00, + 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, + 0xFE, 0x62, 0x68, 0x78, 0x68, 0x62, 0xFE, 0x00, + 0xFE, 0x62, 0x68, 0x78, 0x68, 0x60, 0xF0, 0x00, + 0x3C, 0x66, 0xC0, 0xC0, 0xCE, 0x66, 0x3A, 0x00, + 0xCC, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0xCC, 0x00, + 0x78, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x1E, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00, + 0xE6, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0xE6, 0x00, + 0xF0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00, + 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00, + 0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, 0xC6, 0x00, + 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00, + 0xFC, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, + 0x7C, 0xC6, 0xC6, 0xC6, 0xD6, 0x7C, 0x0E, 0x00, + 0xFC, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0xE6, 0x00, + 0x7C, 0xC6, 0xE0, 0x78, 0x0E, 0xC6, 0x7C, 0x00, + 0xFC, 0xB4, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xFC, 0x00, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, + 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0x6C, 0x00, + 0xC6, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0xC6, 0x00, + 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x30, 0x78, 0x00, + 0xFE, 0xC6, 0x8C, 0x18, 0x32, 0x66, 0xFE, 0x00, + 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, 0x00, + 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00, + 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x00, + 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0xE0, 0x60, 0x60, 0x7C, 0x66, 0x66, 0xDC, 0x00, + 0x00, 0x00, 0x78, 0xCC, 0xC0, 0xCC, 0x78, 0x00, + 0x1C, 0x0C, 0x0C, 0x7C, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, + 0x38, 0x6C, 0x64, 0xF0, 0x60, 0x60, 0xF0, 0x00, + 0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, + 0xE0, 0x60, 0x6C, 0x76, 0x66, 0x66, 0xE6, 0x00, + 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x0C, 0x00, 0x1C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, + 0xE0, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0xE6, 0x00, + 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x00, 0xCC, 0xFE, 0xFE, 0xD6, 0xD6, 0x00, + 0x00, 0x00, 0xB8, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, + 0x00, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0x00, 0xDC, 0x66, 0x66, 0x7C, 0x60, 0xF0, + 0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0x1E, + 0x00, 0x00, 0xDC, 0x76, 0x62, 0x60, 0xF0, 0x00, + 0x00, 0x00, 0x7C, 0xC0, 0x70, 0x1C, 0xF8, 0x00, + 0x10, 0x30, 0xFC, 0x30, 0x30, 0x34, 0x18, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, + 0x00, 0x00, 0xC6, 0xC6, 0xD6, 0xFE, 0x6C, 0x00, + 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, + 0x00, 0x00, 0xFC, 0x98, 0x30, 0x64, 0xFC, 0x00, + 0x1C, 0x30, 0x30, 0xE0, 0x30, 0x30, 0x1C, 0x00, + 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, + 0xE0, 0x30, 0x30, 0x1C, 0x30, 0x30, 0xE0, 0x00, + 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0x00, + 0x7C, 0xC6, 0xC0, 0xC6, 0x7C, 0x0C, 0x06, 0x7C, + 0x00, 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x1C, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, + 0x7E, 0x81, 0x3C, 0x06, 0x3E, 0x66, 0x3B, 0x00, + 0xCC, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0xE0, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0x30, 0x30, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0xC0, 0x78, 0x0C, 0x38, + 0x7E, 0x81, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00, + 0xCC, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, + 0xE0, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, + 0xCC, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x7C, 0x82, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00, + 0xE0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0xC6, 0x10, 0x7C, 0xC6, 0xFE, 0xC6, 0xC6, 0x00, + 0x30, 0x30, 0x00, 0x78, 0xCC, 0xFC, 0xCC, 0x00, + 0x1C, 0x00, 0xFC, 0x60, 0x78, 0x60, 0xFC, 0x00, + 0x00, 0x00, 0x7F, 0x0C, 0x7F, 0xCC, 0x7F, 0x00, + 0x3E, 0x6C, 0xCC, 0xFE, 0xCC, 0xCC, 0xCE, 0x00, + 0x78, 0x84, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0xCC, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0xE0, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x78, 0x84, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0xE0, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0xCC, 0x00, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, + 0xC3, 0x18, 0x3C, 0x66, 0x66, 0x3C, 0x18, 0x00, + 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00, + 0x18, 0x18, 0x7E, 0xC0, 0xC0, 0x7E, 0x18, 0x18, + 0x38, 0x6C, 0x64, 0xF0, 0x60, 0xE6, 0xFC, 0x00, + 0xCC, 0xCC, 0x78, 0x30, 0xFC, 0x30, 0xFC, 0x30, + 0xF8, 0xCC, 0xCC, 0xFA, 0xC6, 0xCF, 0xC6, 0xC3, + 0x0E, 0x1B, 0x18, 0x3C, 0x18, 0x18, 0xD8, 0x70, + 0x1C, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0x38, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, + 0x00, 0x1C, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0x1C, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, + 0x00, 0xF8, 0x00, 0xB8, 0xCC, 0xCC, 0xCC, 0x00, + 0xFC, 0x00, 0xCC, 0xEC, 0xFC, 0xDC, 0xCC, 0x00, + 0x3C, 0x6C, 0x6C, 0x3E, 0x00, 0x7E, 0x00, 0x00, + 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x7C, 0x00, 0x00, + 0x18, 0x00, 0x18, 0x18, 0x30, 0x66, 0x3C, 0x00, + 0x00, 0x00, 0x00, 0xFC, 0xC0, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFC, 0x0C, 0x0C, 0x00, 0x00, + 0xC6, 0xCC, 0xD8, 0x36, 0x6B, 0xC2, 0x84, 0x0F, + 0xC3, 0xC6, 0xCC, 0xDB, 0x37, 0x6D, 0xCF, 0x03, + 0x18, 0x00, 0x18, 0x18, 0x3C, 0x3C, 0x18, 0x00, + 0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00, + 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00, + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, + 0xDB, 0xF6, 0xDB, 0x6F, 0xDB, 0x7E, 0xD7, 0xED, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0xF6, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0xFE, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, + 0x36, 0x36, 0xF6, 0x06, 0xF6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xFE, 0x06, 0xF6, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xF6, 0x06, 0xFE, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xFE, 0x00, 0x00, 0x00, + 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xF8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1F, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x3F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0xF7, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, + 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x36, 0x36, 0xF7, 0x00, 0xF7, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x3F, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0xFF, 0x36, 0x36, 0x36, + 0x18, 0x18, 0xFF, 0x18, 0xFF, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xF8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x18, 0x18, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xDC, 0xC8, 0xDC, 0x76, 0x00, + 0x00, 0x78, 0xCC, 0xF8, 0xCC, 0xF8, 0xC0, 0xC0, + 0x00, 0xFC, 0xCC, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, + 0x00, 0x00, 0xFE, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, + 0xFC, 0xCC, 0x60, 0x30, 0x60, 0xCC, 0xFC, 0x00, + 0x00, 0x00, 0x7E, 0xD8, 0xD8, 0xD8, 0x70, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0xC0, + 0x00, 0x76, 0xDC, 0x18, 0x18, 0x18, 0x18, 0x00, + 0xFC, 0x30, 0x78, 0xCC, 0xCC, 0x78, 0x30, 0xFC, + 0x38, 0x6C, 0xC6, 0xFE, 0xC6, 0x6C, 0x38, 0x00, + 0x38, 0x6C, 0xC6, 0xC6, 0x6C, 0x6C, 0xEE, 0x00, + 0x1C, 0x30, 0x18, 0x7C, 0xCC, 0xCC, 0x78, 0x00, + 0x00, 0x00, 0x7E, 0xDB, 0xDB, 0x7E, 0x00, 0x00, + 0x06, 0x0C, 0x7E, 0xDB, 0xDB, 0x7E, 0x60, 0xC0, + 0x38, 0x60, 0xC0, 0xF8, 0xC0, 0x60, 0x38, 0x00, + 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, + 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, + 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x7E, 0x00, + 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xFC, 0x00, + 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, 0xFC, 0x00, + 0x0E, 0x1B, 0x1B, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xD8, 0xD8, 0x70, + 0x18, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x18, 0x00, + 0x00, 0x76, 0xDC, 0x00, 0x76, 0xDC, 0x00, 0x00, + 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x0F, 0x0C, 0x0C, 0x0C, 0xEC, 0x6C, 0x3C, 0x1C, + 0x58, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, + 0x70, 0x98, 0x30, 0x60, 0xF8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/// Contains an 8x8 font map for unicode points U+0000 - U+007F (basic latin) +unsigned char arr_8x8_basic_font[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0000 (nul) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0001 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0002 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0003 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0004 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0005 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0006 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0007 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0008 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0009 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000A + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000C + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+000F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0010 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0011 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0012 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0013 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0014 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0015 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0016 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0017 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0018 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0019 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001A + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001B + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001C + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001D + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001E + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+001F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0020 (space) + 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00, // U+0021 (!) + 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0022 (") + 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00, // U+0023 (#) + 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00, // U+0024 ($) + 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00, // U+0025 (%) + 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00, // U+0026 (&) + 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0027 (') + 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00, // U+0028 (() + 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00, // U+0029 ()) + 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, // U+002A (*) + 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00, // U+002B (+) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06, // U+002C (,) + 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, // U+002D (-) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, // U+002E (.) + 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00, // U+002F (/) + 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00, // U+0030 (0) + 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00, // U+0031 (1) + 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00, // U+0032 (2) + 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00, // U+0033 (3) + 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00, // U+0034 (4) + 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00, // U+0035 (5) + 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00, // U+0036 (6) + 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00, // U+0037 (7) + 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00, // U+0038 (8) + 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00, // U+0039 (9) + 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00, // U+003A (:) + 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06, // U+003B (;) + 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00, // U+003C (<) + 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00, // U+003D (=) + 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00, // U+003E (>) + 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00, // U+003F (?) + 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00, // U+0040 (@) + 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00, // U+0041 (A) + 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00, // U+0042 (B) + 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00, // U+0043 (C) + 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00, // U+0044 (D) + 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00, // U+0045 (E) + 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00, // U+0046 (F) + 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00, // U+0047 (G) + 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00, // U+0048 (H) + 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, // U+0049 (I) + 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00, // U+004A (J) + 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00, // U+004B (K) + 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00, // U+004C (L) + 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00, // U+004D (M) + 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00, // U+004E (N) + 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00, // U+004F (O) + 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00, // U+0050 (P) + 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00, // U+0051 (Q) + 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00, // U+0052 (R) + 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00, // U+0053 (S) + 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, // U+0054 (T) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00, // U+0055 (U) + 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00, // U+0056 (V) + 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00, // U+0057 (W) + 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00, // U+0058 (X) + 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00, // U+0059 (Y) + 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00, // U+005A (Z) + 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00, // U+005B ([) + 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00, // U+005C (\) + 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00, // U+005D (]) + 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00, // U+005E (^) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, // U+005F (_) + 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, // U+0060 (`) + 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00, // U+0061 (a) + 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00, // U+0062 (b) + 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00, // U+0063 (c) + 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00, // U+0064 (d) + 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00, // U+0065 (e) + 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00, // U+0066 (f) + 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F, // U+0067 (g) + 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00, // U+0068 (h) + 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, // U+0069 (i) + 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, // U+006A (j) + 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00, // U+006B (k) + 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00, // U+006C (l) + 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00, // U+006D (m) + 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00, // U+006E (n) + 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00, // U+006F (o) + 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F, // U+0070 (p) + 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78, // U+0071 (q) + 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00, // U+0072 (r) + 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00, // U+0073 (s) + 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00, // U+0074 (t) + 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00, // U+0075 (u) + 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00, // U+0076 (v) + 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00, // U+0077 (w) + 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00, // U+0078 (x) + 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F, // U+0079 (y) + 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00, // U+007A (z) + 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00, // U+007B ({) + 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, // U+007C (|) + 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00, // U+007D (}) + 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // U+007E (~) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // U+007F +}; + +/// Font 16x16. +unsigned char vga_font[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7e, 0xff, 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x1e, 0x0e, 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0x63, 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0x06, 0x0e, 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7f, 0xdb, 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc3, 0xc3, 0xdb, 0xdb, 0xc3, 0xc3, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xde, 0xde, 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, 0x0c, 0x0e, 0x00, 0x00, + 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, + 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, + 0x00, 0x00, 0xe0, 0x60, 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x38, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, 0x3c, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x3c, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6c, 0x38, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x6c, 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc6, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, + 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x66, 0x66, 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0c, 0x18, 0x30, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x76, 0xdc, 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, 0x0c, 0x1f, 0x00, 0x00, + 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, + 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, + 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x6c, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; \ No newline at end of file diff --git a/mentos/inc/io/vga/vga_mode.h b/mentos/inc/io/vga/vga_mode.h new file mode 100644 index 0000000..9213789 --- /dev/null +++ b/mentos/inc/io/vga/vga_mode.h @@ -0,0 +1,422 @@ +/// Structure that holds the information about a VGA mode. +typedef struct { + unsigned char misc; ///< 00h -- + /// @brief The Sequencer Registers. + struct { + unsigned char reset; ///< 00h -- + unsigned char clocking_mode; ///< 01h -- + unsigned char map_mask; ///< 02h -- + unsigned char character_map_select; ///< 03h -- + unsigned char sequencer_memory_mode; ///< 04h -- + } sc; + /// @brief CRT Controller (CRTC) Registers + struct { + unsigned char horizontal_total; ///< 00h -- + unsigned char end_horizontal_display; ///< 01h -- + unsigned char start_horizontal_blanking; ///< 02h -- + unsigned char end_horizontal_blanking; ///< 03h -- + unsigned char start_horizontal_retrace; ///< 04h -- + unsigned char end_horizontal_retrace; ///< 05h -- + unsigned char vertical_total; ///< 06h -- + unsigned char overflow; ///< 07h -- + unsigned char preset_row_scan; ///< 08h -- + unsigned char maximum_scan_line; ///< 09h -- + unsigned char cursor_start; ///< 0Ah -- + unsigned char cursor_end; ///< 0Bh -- + unsigned char start_address_high; ///< 0Ch -- + unsigned char start_address_low; ///< 0Dh -- + unsigned char cursor_location_high; ///< 0Eh -- + unsigned char cursor_location_low; ///< 0Fh -- + unsigned char vertical_retrace_start; ///< 10h -- + unsigned char vertical_retrace_end; ///< 11h -- + unsigned char vertical_display_end; ///< 12h -- + unsigned char offset; ///< 13h -- + unsigned char underline_location; ///< 14h -- + unsigned char start_vertical_blanking; ///< 15h -- + unsigned char end_vertical_blanking; ///< 16h -- + unsigned char crtc_mode_control; ///< 17h -- + unsigned char line_compare; ///< 18h -- + } crtc; + /// @brief The Graphics Registers. + struct { + unsigned char set_reset; ///< 00h -- + unsigned char enable_set_reset; ///< 01h -- + unsigned char color_compare; ///< 02h -- + unsigned char data_rotate; ///< 03h -- + unsigned char read_map; ///< 04h -- + unsigned char graphics_mode; ///< 05h -- + unsigned char misc_graphics; ///< 06h -- + unsigned char color_dont_care; ///< 07h -- + unsigned char bit_mask; ///< 08h -- + } gc; + /// @brief The Attribute Controller Registers. + struct { + unsigned char internal_palette_registers[16]; ///< 00h-0Fh -- + unsigned char attribute_mode_control; ///< 10h -- + unsigned char overscan_color; ///< 11h -- + unsigned char color_plane_enable; ///< 12h -- + unsigned char horizontal_pixel_panning; ///< 13h -- + unsigned char color_select; ///< 14h -- + } ac; +} vga_mode_t; + +vga_mode_t _mode_80_25_text = { + // 3C2h (W): Miscellaneous Output Register + // bit 0 If set Color Emulation: + // Base Address = 3Dxh else Mono Emulation. + // Base Address = 3Bxh. + // 1 Enable CPU Access to video memory if set + // 2-3 Clock Select: + // 00: 25MHz (used for 320/640 pixel wide modes), + // 01: 28MHz (used for 360/720 pixel wide modes) + // 5 When in Odd/Even modes Select High 64k bank if set + // 6 Horizontal Sync Polarity. Negative if set + // 7 Vertical Sync Polarity. Negative if set + // Bit 6-7 indicates the number of lines on the display: + // 1: 400, 2: 350, 3: 480 + // Note: Set to all zero on a hardware reset. + // Note: This register can be read from port 3CCh. + .misc = 0x67, // 0110 0011 + .sc = { + .reset = 0x03, // 0000 0011 - Bits 1 and 0 must be 1 to allow the sequencer to operate. + .clocking_mode = 0x00, // 0000 0001 - Selects 8 dots per character. + .map_mask = 0x03, // 0000 1111 - Write operations affect all the planes. + .character_map_select = 0x00, // 0000 0000 - No Character Map Select + .sequencer_memory_mode = 0x02, // 0000 0110 - System addresses sequentially access data within a bit map. + // Enables the video memory from 64KB to 256KB + }, + .crtc = { + .horizontal_total = 0x5F, // 95 (+5) - Number of character clocks per scan line. + .end_horizontal_display = 0x4F, // + .start_horizontal_blanking = 0x50, + .end_horizontal_blanking = 0x82, + .start_horizontal_retrace = 0x55, + .end_horizontal_retrace = 0x81, + .vertical_total = 0xBF, + .overflow = 0x1F, + .preset_row_scan = 0x00, + .maximum_scan_line = 0x4F, + .cursor_start = 0x0D, + .cursor_end = 0x0E, + .start_address_high = 0x00, + .start_address_low = 0x00, + .cursor_location_high = 0x00, + .cursor_location_low = 0x50, + .vertical_retrace_start = 0x9C, + .vertical_retrace_end = 0x0E, + .vertical_display_end = 0x8F, + .offset = 0x28, + .underline_location = 0x1F, + .start_vertical_blanking = 0x96, + .end_vertical_blanking = 0xB9, + .crtc_mode_control = 0xA3, // 0xA3 1010 0011 + .line_compare = 0xFF, + }, + .gc = { + .set_reset = 0x00, + .enable_set_reset = 0x00, + .color_compare = 0x00, + .data_rotate = 0x00, + .read_map = 0x00, + .graphics_mode = 0x10, + .misc_graphics = 0x0E, + .color_dont_care = 0x00, + .bit_mask = 0xFF, + }, + .ac = { + .internal_palette_registers = { + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x14, + 0x07, + 0x38, + 0x39, + 0x3A, + 0x3B, + 0x3C, + 0x3D, + 0x3E, + 0x3F, + }, + .attribute_mode_control = 0x0C, + .overscan_color = 0x00, + .color_plane_enable = 0x0F, + .horizontal_pixel_panning = 0x08, + .color_select = 0x00, + } +}; + +vga_mode_t _mode_320_200_256 = { + // 3C2h (W): Miscellaneous Output Register + // bit 0 If set Color Emulation: + // Base Address = 3Dxh else Mono Emulation. + // Base Address = 3Bxh. + // 1 Enable CPU Access to video memory if set + // 2-3 Clock Select: + // 00: 25MHz (used for 320/640 pixel wide modes), + // 01: 28MHz (used for 360/720 pixel wide modes) + // 5 When in Odd/Even modes Select High 64k bank if set + // 6 Horizontal Sync Polarity. Negative if set + // 7 Vertical Sync Polarity. Negative if set + // Bit 6-7 indicates the number of lines on the display: + // 1: 400, 2: 350, 3: 480 + // Note: Set to all zero on a hardware reset. + // Note: This register can be read from port 3CCh. + .misc = 0x63, // 0110 0011 + .sc = { + .reset = 0x03, // 0000 0011 - Bits 1 and 0 must be 1 to allow the sequencer to operate. + .clocking_mode = 0x01, // 0000 0001 - Selects 8 dots per character. + .map_mask = 0x0F, // 0000 1111 - Write operations affect all the planes. + .character_map_select = 0x00, // 0000 0000 - No Character Map Select + .sequencer_memory_mode = 0x06, // 0000 0110 - System addresses sequentially access data within a bit map. + // Enables the video memory from 64KB to 256KB + }, + .crtc = { + .horizontal_total = 0x5F, // 95 (+5) - Number of character clocks per scan line. + .end_horizontal_display = 0x4F, // + .start_horizontal_blanking = 0x50, + .end_horizontal_blanking = 0x82, + .start_horizontal_retrace = 0x54, + .end_horizontal_retrace = 0x80, + .vertical_total = 0xBF, + .overflow = 0x1F, + .preset_row_scan = 0x00, + .maximum_scan_line = 0x41, + .cursor_start = 0x00, + .cursor_end = 0x00, + .start_address_high = 0x00, + .start_address_low = 0x00, + .cursor_location_high = 0x00, + .cursor_location_low = 0x00, + .vertical_retrace_start = 0x9C, + .vertical_retrace_end = 0xE3, + .vertical_display_end = 0x8F, + .offset = 0x28, + .underline_location = 0x40, + .start_vertical_blanking = 0x96, + .end_vertical_blanking = 0xB9, + .crtc_mode_control = 0xA3, // 0xA3 1010 0011 + .line_compare = 0xFF, + }, + .gc = { + .set_reset = 0x00, + .enable_set_reset = 0x00, + .color_compare = 0x00, + .data_rotate = 0x00, + .read_map = 0x00, + .graphics_mode = 0x40, + .misc_graphics = 0x05, + .color_dont_care = 0x0F, + .bit_mask = 0xFF, + }, + .ac = { + .internal_palette_registers = { + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0A, + 0x0B, + 0x0C, + 0x0D, + 0x0E, + 0x0F, + }, + .attribute_mode_control = 0x41, + .overscan_color = 0x00, + .color_plane_enable = 0x0F, + .horizontal_pixel_panning = 0x00, + .color_select = 0x00, + } +}; + + +vga_mode_t _mode_640_480_16 = { + // 3C2h (W): Miscellaneous Output Register + // bit 0 If set Color Emulation: + // Base Address = 3Dxh else Mono Emulation. + // Base Address = 3Bxh. + // 1 Enable CPU Access to video memory if set + // 2-3 Clock Select: + // 00: 25MHz (used for 320/640 pixel wide modes), + // 01: 28MHz (used for 360/720 pixel wide modes) + // 5 When in Odd/Even modes Select High 64k bank if set + // 6 Horizontal Sync Polarity. Negative if set + // 7 Vertical Sync Polarity. Negative if set + // Bit 6-7 indicates the number of lines on the display: + // 1: 400, 2: 350, 3: 480 + // Note: Set to all zero on a hardware reset. + // Note: This register can be read from port 3CCh. + .misc = 0xE3, // 1110 0011 + .sc = { + .reset = 0x03, // 0000 0011 - Bits 1 and 0 must be 1 to allow the sequencer to operate. + .clocking_mode = 0x01, // 0000 0001 - Selects 8 dots per character. + .map_mask = 0x0F, // 0000 1111 - Write operations affect all the planes. + .character_map_select = 0x00, // 0000 0000 - No Character Map Select + .sequencer_memory_mode = 0x06, // 0000 0110 - System addresses sequentially access data within a bit map. + // Enables the video memory from 64KB to 256KB + }, + .crtc = { + .horizontal_total = 0x5F, // 95 (+5) - Number of character clocks per scan line. + .end_horizontal_display = 0x4F, // + .start_horizontal_blanking = 0x50, + .end_horizontal_blanking = 0x82, + .start_horizontal_retrace = 0x54, + .end_horizontal_retrace = 0x80, + .vertical_total = 0x0B, + .overflow = 0x3E, + .preset_row_scan = 0x00, + .maximum_scan_line = 0x40, + .cursor_start = 0x00, + .cursor_end = 0x00, + .start_address_high = 0x00, + .start_address_low = 0x00, + .cursor_location_high = 0x00, + .cursor_location_low = 0x00, + .vertical_retrace_start = 0xEA, + .vertical_retrace_end = 0x0C, + .vertical_display_end = 0xDF, + .offset = 0x28, + .underline_location = 0x00, + .start_vertical_blanking = 0xE7, + .end_vertical_blanking = 0x04, + .crtc_mode_control = 0xE3, + .line_compare = 0xFF, + }, + .gc = { + .set_reset = 0x00, + .enable_set_reset = 0x00, + .color_compare = 0x00, + .data_rotate = 0x00, + .read_map = 0x03, + .graphics_mode = 0x00, + .misc_graphics = 0x05, + .color_dont_care = 0x0F, + .bit_mask = 0xFF, + }, + .ac = { + .internal_palette_registers = { + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x14, + 0x07, + 0x38, + 0x39, + 0x3A, + 0x3B, + 0x3C, + 0x3D, + 0x3E, + 0x3F, + }, + .attribute_mode_control = 0x01, // 0b01000001, // 0x41, + .overscan_color = 0x00, + .color_plane_enable = 0x0F, + .horizontal_pixel_panning = 0x00, + .color_select = 0x00, + } +}; + +vga_mode_t _mode_720_480_16 = { + // 3C2h (W): Miscellaneous Output Register + // bit 0 If set Color Emulation: + // Base Address = 3Dxh else Mono Emulation. + // Base Address = 3Bxh. + // 1 Enable CPU Access to video memory if set + // 2-3 Clock Select: + // 00: 25MHz (used for 320/640 pixel wide modes), + // 01: 28MHz (used for 360/720 pixel wide modes) + // 5 When in Odd/Even modes Select High 64k bank if set + // 6 Horizontal Sync Polarity. Negative if set + // 7 Vertical Sync Polarity. Negative if set + // Bit 6-7 indicates the number of lines on the display: + // 1: 400, 2: 350, 3: 480 + // Note: Set to all zero on a hardware reset. + // Note: This register can be read from port 3CCh. + .misc = 0xE7, // 1110 0011 + .sc = { + .reset = 0x03, // 0000 0011 - Bits 1 and 0 must be 1 to allow the sequencer to operate. + .clocking_mode = 0x01, // 0000 0001 - Selects 8 dots per character. + .map_mask = 0x08, // 0000 1111 - Write operations affect all the planes. + .character_map_select = 0x00, // 0000 0000 - No Character Map Select + .sequencer_memory_mode = 0x06, // 0000 0110 - System addresses sequentially access data within a bit map. + // Enables the video memory from 64KB to 256KB + }, + .crtc = { + .horizontal_total = 0x6B, // 95 (+5) - Number of character clocks per scan line. + .end_horizontal_display = 0x59, // + .start_horizontal_blanking = 0x5A, + .end_horizontal_blanking = 0x82, + .start_horizontal_retrace = 0x60, + .end_horizontal_retrace = 0x8D, + .vertical_total = 0x0B, + .overflow = 0x3E, + .preset_row_scan = 0x00, + .maximum_scan_line = 0x40, + .cursor_start = 0x06, + .cursor_end = 0x07, + .start_address_high = 0x00, + .start_address_low = 0x00, + .cursor_location_high = 0x00, + .cursor_location_low = 0x00, + .vertical_retrace_start = 0xEA, + .vertical_retrace_end = 0x0C, + .vertical_display_end = 0xDF, + .offset = 0x2D, + .underline_location = 0x08, + .start_vertical_blanking = 0xE8, + .end_vertical_blanking = 0x05, + .crtc_mode_control = 0xE3, + .line_compare = 0xFF, + }, + .gc = { + .set_reset = 0x00, + .enable_set_reset = 0x00, + .color_compare = 0x00, + .data_rotate = 0x00, + .read_map = 0x03, + .graphics_mode = 0x00, + .misc_graphics = 0x05, + .color_dont_care = 0x0F, + .bit_mask = 0xFF, + }, + .ac = { + .internal_palette_registers = { + 0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0A, + 0x0B, + 0x0C, + 0x0D, + 0x0E, + 0x0F, + }, + .attribute_mode_control = 0x01, // 0b01000001, // 0x41, + .overscan_color = 0x00, + .color_plane_enable = 0x0F, + .horizontal_pixel_panning = 0x00, + .color_select = 0x00, + } +}; diff --git a/mentos/inc/io/vga/vga_palette.h b/mentos/inc/io/vga/vga_palette.h new file mode 100644 index 0000000..010c052 --- /dev/null +++ b/mentos/inc/io/vga/vga_palette.h @@ -0,0 +1,294 @@ +/// MentOS, The Mentoring Operating system project +/// @file vga_palette.h +/// @brief VGA color palettes. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// Structure that simplifies defining a palette. +typedef struct { + unsigned char red; ///< Red value. + unsigned char green; ///< Green value. + unsigned char blue; ///< Blue value. +} palette_entry_t; + +/// 16 color palette. +palette_entry_t ansi_16_palette[17] = { + { 0x00, 0x00, 0x00 }, // Black + { 0xAA, 0x00, 0x00 }, // Red + { 0x00, 0xAA, 0x00 }, // Green + { 0xAA, 0xAA, 0x00 }, // Yellow + { 0x00, 0x00, 0xAA }, // Blue + { 0xAA, 0x00, 0xAA }, // Magenta + { 0x00, 0xAA, 0xAA }, // Cyan + { 0xAA, 0xAA, 0xAA }, // White + { 0x55, 0x55, 0x55 }, // Black + { 0xFF, 0x55, 0x55 }, // Red + { 0x55, 0xFF, 0x55 }, // Green + { 0xFF, 0xFF, 0x55 }, // Yellow + { 0x55, 0x55, 0xFF }, // Blue + { 0xFF, 0x55, 0xFF }, // Magenta + { 0x55, 0xFF, 0xFF }, // Cyan + { 0xFF, 0xFF, 0xFF }, // White +}; + +/// 256 color palette. +palette_entry_t ansi_256_palette[256] = { + { 0, 0, 0 }, // Black + { 128, 0, 0 }, // Maroon + { 0, 128, 0 }, // Green + { 128, 128, 0 }, // Olive + { 0, 0, 128 }, // Navy + { 128, 0, 128 }, // Purple + { 0, 128, 128 }, // Teal + { 192, 192, 192 }, // Silver + { 128, 128, 128 }, // Grey + { 255, 0, 0 }, // Red + { 0, 255, 0 }, // Lime + { 255, 255, 0 }, // Yellow + { 0, 0, 255 }, // Blue + { 255, 0, 255 }, // Fuchsia + { 0, 255, 255 }, // Aqua + { 255, 255, 255 }, // White + { 0, 0, 55 }, // ExtremelyDarkBlue + { 0, 0, 95 }, // NavyBlue + { 0, 0, 135 }, // DarkBlue + { 0, 0, 175 }, // Blue3 + { 0, 0, 215 }, // Blue3 + { 0, 0, 255 }, // Blue1 + { 0, 95, 0 }, // DarkGreen + { 0, 95, 95 }, // DeepSkyBlue4 + { 0, 95, 135 }, // DeepSkyBlue4 + { 0, 95, 175 }, // DeepSkyBlue4 + { 0, 95, 215 }, // DodgerBlue3 + { 0, 95, 255 }, // DodgerBlue2 + { 0, 135, 0 }, // Green4 + { 0, 135, 95 }, // SpringGreen4 + { 0, 135, 135 }, // Turquoise4 + { 0, 135, 175 }, // DeepSkyBlue3 + { 0, 135, 215 }, // DeepSkyBlue3 + { 0, 135, 255 }, // DodgerBlue1 + { 0, 175, 0 }, // Green3 + { 0, 175, 95 }, // SpringGreen3 + { 0, 175, 135 }, // DarkCyan + { 0, 175, 175 }, // LightSeaGreen + { 0, 175, 215 }, // DeepSkyBlue2 + { 0, 175, 255 }, // DeepSkyBlue1 + { 0, 215, 0 }, // Green3 + { 0, 215, 95 }, // SpringGreen3 + { 0, 215, 135 }, // SpringGreen2 + { 0, 215, 175 }, // Cyan3 + { 0, 215, 215 }, // DarkTurquoise + { 0, 215, 255 }, // Turquoise2 + { 0, 255, 0 }, // Green1 + { 0, 255, 95 }, // SpringGreen2 + { 0, 255, 135 }, // SpringGreen1 + { 0, 255, 175 }, // MediumSpringGreen + { 0, 255, 215 }, // Cyan2 + { 0, 255, 255 }, // Cyan1 + { 95, 0, 0 }, // DarkRed + { 95, 0, 95 }, // DeepPink4 + { 95, 0, 135 }, // Purple4 + { 95, 0, 175 }, // Purple4 + { 95, 0, 215 }, // Purple3 + { 95, 0, 255 }, // BlueViolet + { 95, 95, 0 }, // Orange4 + { 95, 95, 95 }, // Grey37 + { 95, 95, 135 }, // MediumPurple4 + { 95, 95, 175 }, // SlateBlue3 + { 95, 95, 215 }, // SlateBlue3 + { 95, 95, 255 }, // RoyalBlue1 + { 95, 135, 0 }, // Chartreuse4 + { 95, 135, 95 }, // DarkSeaGreen4 + { 95, 135, 135 }, // PaleTurquoise4 + { 95, 135, 175 }, // SteelBlue + { 95, 135, 215 }, // SteelBlue3 + { 95, 135, 255 }, // CornflowerBlue + { 95, 175, 0 }, // Chartreuse3 + { 95, 175, 95 }, // DarkSeaGreen4 + { 95, 175, 135 }, // CadetBlue + { 95, 175, 175 }, // CadetBlue + { 95, 175, 215 }, // SkyBlue3 + { 95, 175, 255 }, // SteelBlue1 + { 95, 215, 0 }, // Chartreuse3 + { 95, 215, 95 }, // PaleGreen3 + { 95, 215, 135 }, // SeaGreen3 + { 95, 215, 175 }, // Aquamarine3 + { 95, 215, 215 }, // MediumTurquoise + { 95, 215, 255 }, // SteelBlue1 + { 95, 255, 0 }, // Chartreuse2 + { 95, 255, 95 }, // SeaGreen2 + { 95, 255, 135 }, // SeaGreen1 + { 95, 255, 175 }, // SeaGreen1 + { 95, 255, 215 }, // Aquamarine1 + { 95, 255, 255 }, // DarkSlateGray2 + { 135, 0, 0 }, // DarkRed + { 135, 0, 95 }, // DeepPink4 + { 135, 0, 135 }, // DarkMagenta + { 135, 0, 175 }, // DarkMagenta + { 135, 0, 215 }, // DarkViolet + { 135, 0, 255 }, // Purple + { 135, 95, 0 }, // Orange4 + { 135, 95, 95 }, // LightPink4 + { 135, 95, 135 }, // Plum4 + { 135, 95, 175 }, // MediumPurple3 + { 135, 95, 215 }, // MediumPurple3 + { 135, 95, 255 }, // SlateBlue1 + { 135, 135, 0 }, // Yellow4 + { 135, 135, 95 }, // Wheat4 + { 135, 135, 135 }, // Grey53 + { 135, 135, 175 }, // LightSlateGrey + { 135, 135, 215 }, // MediumPurple + { 135, 135, 255 }, // LightSlateBlue + { 135, 175, 0 }, // Yellow4 + { 135, 175, 95 }, // DarkOliveGreen3 + { 135, 175, 135 }, // DarkSeaGreen + { 135, 175, 175 }, // LightSkyBlue3 + { 135, 175, 215 }, // LightSkyBlue3 + { 135, 175, 255 }, // SkyBlue2 + { 135, 215, 0 }, // Chartreuse2 + { 135, 215, 95 }, // DarkOliveGreen3 + { 135, 215, 135 }, // PaleGreen3 + { 135, 215, 175 }, // DarkSeaGreen3 + { 135, 215, 215 }, // DarkSlateGray3 + { 135, 215, 255 }, // SkyBlue1 + { 135, 255, 0 }, // Chartreuse1 + { 135, 255, 95 }, // LightGreen + { 135, 255, 135 }, // LightGreen + { 135, 255, 175 }, // PaleGreen1 + { 135, 255, 215 }, // Aquamarine1 + { 135, 255, 255 }, // DarkSlateGray1 + { 175, 0, 0 }, // Red3 + { 175, 0, 95 }, // DeepPink4 + { 175, 0, 135 }, // MediumVioletRed + { 175, 0, 175 }, // Magenta3 + { 175, 0, 215 }, // DarkViolet + { 175, 0, 255 }, // Purple + { 175, 95, 0 }, // DarkOrange3 + { 175, 95, 95 }, // IndianRed + { 175, 95, 135 }, // HotPink3 + { 175, 95, 175 }, // MediumOrchid3 + { 175, 95, 215 }, // MediumOrchid + { 175, 95, 255 }, // MediumPurple2 + { 175, 135, 0 }, // DarkGoldenrod + { 175, 135, 95 }, // LightSalmon3 + { 175, 135, 135 }, // RosyBrown + { 175, 135, 175 }, // Grey63 + { 175, 135, 215 }, // MediumPurple2 + { 175, 135, 255 }, // MediumPurple1 + { 175, 175, 0 }, // Gold3 + { 175, 175, 95 }, // DarkKhaki + { 175, 175, 135 }, // NavajoWhite3 + { 175, 175, 175 }, // Grey69 + { 175, 175, 215 }, // LightSteelBlue3 + { 175, 175, 255 }, // LightSteelBlue + { 175, 215, 0 }, // Yellow3 + { 175, 215, 95 }, // DarkOliveGreen3 + { 175, 215, 135 }, // DarkSeaGreen3 + { 175, 215, 175 }, // DarkSeaGreen2 + { 175, 215, 215 }, // LightCyan3 + { 175, 215, 255 }, // LightSkyBlue1 + { 175, 255, 0 }, // GreenYellow + { 175, 255, 95 }, // DarkOliveGreen2 + { 175, 255, 135 }, // PaleGreen1 + { 175, 255, 175 }, // DarkSeaGreen2 + { 175, 255, 215 }, // DarkSeaGreen1 + { 175, 255, 255 }, // PaleTurquoise1 + { 215, 0, 0 }, // Red3 + { 215, 0, 95 }, // DeepPink3 + { 215, 0, 135 }, // DeepPink3 + { 215, 0, 175 }, // Magenta3 + { 215, 0, 215 }, // Magenta3 + { 215, 0, 255 }, // Magenta2 + { 215, 95, 0 }, // DarkOrange3 + { 215, 95, 95 }, // IndianRed + { 215, 95, 135 }, // HotPink3 + { 215, 95, 175 }, // HotPink2 + { 215, 95, 215 }, // Orchid + { 215, 95, 255 }, // MediumOrchid1 + { 215, 135, 0 }, // Orange3 + { 215, 135, 95 }, // LightSalmon3 + { 215, 135, 135 }, // LightPink3 + { 215, 135, 175 }, // Pink3 + { 215, 135, 215 }, // Plum3 + { 215, 135, 255 }, // Violet + { 215, 175, 0 }, // Gold3 + { 215, 175, 95 }, // LightGoldenrod3 + { 215, 175, 135 }, // Tan + { 215, 175, 175 }, // MistyRose3 + { 215, 175, 215 }, // Thistle3 + { 215, 175, 255 }, // Plum2 + { 215, 215, 0 }, // Yellow3 + { 215, 215, 95 }, // Khaki3 + { 215, 215, 135 }, // LightGoldenrod2 + { 215, 215, 175 }, // LightYellow3 + { 215, 215, 215 }, // Grey84 + { 215, 215, 255 }, // LightSteelBlue1 + { 215, 255, 0 }, // Yellow2 + { 215, 255, 95 }, // DarkOliveGreen1 + { 215, 255, 135 }, // DarkOliveGreen1 + { 215, 255, 175 }, // DarkSeaGreen1 + { 215, 255, 215 }, // Honeydew2 + { 215, 255, 255 }, // LightCyan1 + { 255, 0, 0 }, // Red1 + { 255, 0, 95 }, // DeepPink2 + { 255, 0, 135 }, // DeepPink1 + { 255, 0, 175 }, // DeepPink1 + { 255, 0, 215 }, // Magenta2 + { 255, 0, 255 }, // Magenta1 + { 255, 95, 0 }, // OrangeRed1 + { 255, 95, 95 }, // IndianRed1 + { 255, 95, 135 }, // IndianRed1 + { 255, 95, 175 }, // HotPink + { 255, 95, 215 }, // HotPink + { 255, 95, 255 }, // MediumOrchid1 + { 255, 135, 0 }, // DarkOrange + { 255, 135, 95 }, // Salmon1 + { 255, 135, 135 }, // LightCoral + { 255, 135, 175 }, // PaleVioletRed1 + { 255, 135, 215 }, // Orchid2 + { 255, 135, 255 }, // Orchid1 + { 255, 175, 0 }, // Orange1 + { 255, 175, 95 }, // SandyBrown + { 255, 175, 135 }, // LightSalmon1 + { 255, 175, 175 }, // LightPink1 + { 255, 175, 215 }, // Pink1 + { 255, 175, 255 }, // Plum1 + { 255, 215, 0 }, // Gold1 + { 255, 215, 95 }, // LightGoldenrod2 + { 255, 215, 135 }, // LightGoldenrod2 + { 255, 215, 175 }, // NavajoWhite1 + { 255, 215, 215 }, // MistyRose1 + { 255, 215, 255 }, // Thistle1 + { 255, 255, 0 }, // Yellow1 + { 255, 255, 95 }, // LightGoldenrod1 + { 255, 255, 135 }, // Khaki1 + { 255, 255, 175 }, // Wheat1 + { 255, 255, 215 }, // Cornsilk1 + { 255, 255, 255 }, // Grey100 + { 8, 8, 8 }, // Grey3 + { 18, 18, 18 }, // Grey7 + { 28, 28, 28 }, // Grey11 + { 38, 38, 38 }, // Grey15 + { 48, 48, 48 }, // Grey19 + { 58, 58, 58 }, // Grey23 + { 68, 68, 68 }, // Grey27 + { 78, 78, 78 }, // Grey30 + { 88, 88, 88 }, // Grey35 + { 98, 98, 98 }, // Grey39 + { 108, 108, 108 }, // Grey42 + { 118, 118, 118 }, // Grey46 + { 128, 128, 128 }, // Grey50 + { 138, 138, 138 }, // Grey54 + { 148, 148, 148 }, // Grey58 + { 158, 158, 158 }, // Grey62 + { 168, 168, 168 }, // Grey66 + { 178, 178, 178 }, // Grey70 + { 188, 188, 188 }, // Grey74 + { 198, 198, 198 }, // Grey78 + { 208, 208, 208 }, // Grey82 + { 218, 218, 218 }, // Grey85 + { 228, 228, 228 }, // Grey89 + { 238, 238, 238 }, // Grey93 +}; \ No newline at end of file diff --git a/mentos/inc/io/video.h b/mentos/inc/io/video.h index b619214..ab03277 100644 --- a/mentos/inc/io/video.h +++ b/mentos/inc/io/video.h @@ -1,78 +1,87 @@ /// MentOS, The Mentoring Operating system project /// @file video.h /// @brief Video functions and costants. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "stdint.h" -/// @brief A set of colors. -typedef enum video_color_t { - ///0 : Black - BLACK, - /// 1 : Blue - BLUE, - /// 2 : Green - GREEN, - /// 3 : Cyan - CYAN, - /// 4 : Red - RED, - /// 5 : Magenta - MAGENTA, - /// 6 : Brown - BROWN, - /// 7 : Grey - GREY, - /// 8 : Dark Grey - DARK_GREY, - /// 9 : Bright Blue - BRIGHT_BLUE, - /// 10 : Bright Green - BRIGHT_GREEN, - /// 11 : Bright Cyan - BRIGHT_CYAN, - /// 12 : Bright Red - BRIGHT_RED, - /// 13 : Bright Magenta - BRIGHT_MAGENTA, - /// 14 : Yellow - YELLOW, - /// 15 : White - WHITE, -} video_color_t; +#define FG_RESET "\033[0m" ///< ANSI code for resetting. + +#define FG_BLACK "\033[30m" ///< ANSI code for setting a BLACK foreground. +#define FG_RED "\033[31m" ///< ANSI code for setting a RED foreground. +#define FG_GREEN "\033[32m" ///< ANSI code for setting a GREEN foreground. +#define FG_YELLOW "\033[33m" ///< ANSI code for setting a YELLOW foreground. +#define FG_BLUE "\033[34m" ///< ANSI code for setting a BLUE foreground. +#define FG_MAGENTA "\033[35m" ///< ANSI code for setting a MAGENTA foreground. +#define FG_CYAN "\033[36m" ///< ANSI code for setting a CYAN foreground. +#define FG_WHITE "\033[37m" ///< ANSI code for setting a WHITE foreground. + +#define FG_BLACK_BOLD "\033[1;30m" ///< ANSI code for setting a BLACK foreground. +#define FG_RED_BOLD "\033[1;31m" ///< ANSI code for setting a RED foreground. +#define FG_GREEN_BOLD "\033[1;32m" ///< ANSI code for setting a GREEN foreground. +#define FG_YELLOW_BOLD "\033[1;33m" ///< ANSI code for setting a YELLOW foreground. +#define FG_BLUE_BOLD "\033[1;34m" ///< ANSI code for setting a BLUE foreground. +#define FG_MAGENTA_BOLD "\033[1;35m" ///< ANSI code for setting a MAGENTA foreground. +#define FG_CYAN_BOLD "\033[1;36m" ///< ANSI code for setting a CYAN foreground. +#define FG_WHITE_BOLD "\033[1;37m" ///< ANSI code for setting a WHITE foreground. + +#define FG_BLACK_BRIGHT "\033[90m" ///< ANSI code for setting a BRIGHT_BLACK foreground. +#define FG_RED_BRIGHT "\033[91m" ///< ANSI code for setting a BRIGHT_RED foreground. +#define FG_GREEN_BRIGHT "\033[92m" ///< ANSI code for setting a BRIGHT_GREEN foreground. +#define FG_YELLOW_BRIGHT "\033[93m" ///< ANSI code for setting a BRIGHT_YELLOW foreground. +#define FG_BLUE_BRIGHT "\033[94m" ///< ANSI code for setting a BRIGHT_BLUE foreground. +#define FG_MAGENTA_BRIGHT "\033[95m" ///< ANSI code for setting a BRIGHT_MAGENTA foreground. +#define FG_CYAN_BRIGHT "\033[96m" ///< ANSI code for setting a BRIGHT_CYAN foreground. +#define FG_WHITE_BRIGHT "\033[97m" ///< ANSI code for setting a BRIGHT_WHITE foreground. + +#define FG_BLACK_BRIGHT_BOLD "\033[1;90m" ///< ANSI code for setting a BRIGHT_BLACK foreground. +#define FG_RED_BRIGHT_BOLD "\033[1;91m" ///< ANSI code for setting a BRIGHT_RED foreground. +#define FG_GREEN_BRIGHT_BOLD "\033[1;92m" ///< ANSI code for setting a BRIGHT_GREEN foreground. +#define FG_YELLOW_BRIGHT_BOLD "\033[1;93m" ///< ANSI code for setting a BRIGHT_YELLOW foreground. +#define FG_BLUE_BRIGHT_BOLD "\033[1;94m" ///< ANSI code for setting a BRIGHT_BLUE foreground. +#define FG_MAGENTA_BRIGHT_BOLD "\033[1;95m" ///< ANSI code for setting a BRIGHT_MAGENTA foreground. +#define FG_CYAN_BRIGHT_BOLD "\033[1;96m" ///< ANSI code for setting a BRIGHT_CYAN foreground. +#define FG_WHITE_BRIGHT_BOLD "\033[1;97m" ///< ANSI code for setting a BRIGHT_WHITE foreground. + +#define BG_BLACK "\033[40m" ///< ANSI code for setting a BLACK background. +#define BG_RED "\033[41m" ///< ANSI code for setting a RED background. +#define BG_GREEN "\033[42m" ///< ANSI code for setting a GREEN background. +#define BG_YELLOW "\033[43m" ///< ANSI code for setting a YELLOW background. +#define BG_BLUE "\033[44m" ///< ANSI code for setting a BLUE background. +#define BG_MAGENTA "\033[45m" ///< ANSI code for setting a MAGENTA background. +#define BG_CYAN "\033[46m" ///< ANSI code for setting a CYAN background. +#define BG_WHITE "\033[47m" ///< ANSI code for setting a WHITE background. + +#define BG_BRIGHT_BLACK "\033[100m" ///< ANSI code for setting a BRIGHT_BLACK background. +#define BG_BRIGHT_RED "\033[101m" ///< ANSI code for setting a BRIGHT_RED background. +#define BG_BRIGHT_GREEN "\033[102m" ///< ANSI code for setting a BRIGHT_GREEN background. +#define BG_BRIGHT_YELLOW "\033[103m" ///< ANSI code for setting a BRIGHT_YELLOW background. +#define BG_BRIGHT_BLUE "\033[104m" ///< ANSI code for setting a BRIGHT_BLUE background. +#define BG_BRIGHT_MAGENTA "\033[105m" ///< ANSI code for setting a BRIGHT_MAGENTA background. +#define BG_BRIGHT_CYAN "\033[106m" ///< ANSI code for setting a BRIGHT_CYAN background. +#define BG_BRIGHT_WHITE "\033[107m" ///< ANSI code for setting a BRIGHT_WHITE background. /// @brief Initialize the video. void video_init(); /// @brief Print the given character on the screen. -void video_putc(int); +/// @param c The character to print. +void video_putc(int c); /// @brief Prints the given string on the screen. +/// @param str The string to print. void video_puts(const char *str); -/// @brief Change foreground colour. -void video_set_color(const video_color_t foreground); - -/// @brief Change background colour. -void video_set_background(const video_color_t background); - -/// @brief Deletes the last inserted character. -void video_delete_last_character(); - -/// @brief Move the cursor to the given position. -void video_set_cursor(const unsigned int x, const unsigned int y); - /// @brief When something is written in another position, update the cursor. void video_set_cursor_auto(); /// @brief Move the cursor at the position x, y on the screen. -void video_move_cursor(int, int); - -/// @brief Prints a tab on the screen. -void video_put_tab(); +/// @param x The x coordinate. +/// @param y The y coordinate. +void video_move_cursor(unsigned int x, unsigned int y); /// @brief Clears the screen. void video_clear(); @@ -84,49 +93,19 @@ void video_new_line(); void video_cartridge_return(); /// @brief Get the current column number. -uint32_t video_get_column(); +/// @return The column number. +uint32_t video_get_x(); /// @brief Get the current row number. -uint32_t video_get_line(); +/// @return The row number. +uint32_t video_get_y(); /// @brief The whole screen is shifted up by one line. Used when the cursor /// reaches the last position of the screen. -void video_shift_one_line(); +void video_shift_one_line_up(); -/// @brief The scrolling buffer is updated to contain the screen up the -/// current one. The oldest line is lost to make space for the new one. -void video_rotate_scroll_buffer(); +/// @brief The whole screen is shifted up by one page. +void video_shift_one_page_up(); -/// @brief Called by the pression of the PAGEUP key. -/// The screen aboce the current one is printed and the current one is -/// saved in downbuffer, ready to be restored in future. -void video_scroll_up(); - -/// @brief Called by the pression of the PAGEDOWN key. -/// The content of downbuffer (that is, the screen present when you -/// pressed PAGEUP) is printed again. -void video_scroll_down(); - -/// Determines the lower-bound on the x axis for the video. -uint32_t lower_bound_x; - -/// Determines the lower-bound on the y axis for the video. -uint32_t lower_bound_y; - -/// Determines the current position of the shell cursor on the x axis. -uint32_t shell_current_x; - -/// Determines the current position of the shell cursor on the y axis. -uint32_t shell_current_y; - -/// Determines the lower-bound on the x axis for the shell. -uint32_t shell_lower_bound_x; - -/// Determines the lower-bound on the y axis for the shell. -uint32_t shell_lower_bound_y; - -/// @brief Prints [OK] at the current row and column 60. -void video_print_ok(); - -/// @brief Prints [FAIL] at the current row and column 60. -void video_print_fail(); +/// @brief The whole screen is shifted down by one page. +void video_shift_one_page_down(); diff --git a/mentos/inc/kernel.h b/mentos/inc/kernel.h index 190b5ca..52238c6 100644 --- a/mentos/inc/kernel.h +++ b/mentos/inc/kernel.h @@ -1,287 +1,67 @@ /// MentOS, The Mentoring Operating system project /// @file kernel.h /// @brief Kernel generic data structure and functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "elf.h" -#include "stddef.h" #include "stdint.h" -#include "stdbool.h" -#include "multiboot.h" -/// The maximum length of a file name. -#define MAX_FILENAME_LENGTH 64 - -/// The maximum length of a path. -#define MAX_PATH_LENGTH 256 - -/// The maximum number of modules. -#define MAX_MODULES 10 - -/// This should be in %eax. -#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 - -/// Our kernel now loads at 0xC0000000, so what low memory address such as -/// 0xb800 you used to access, should be LOAD_MEMORY_ADDRESS + 0xb800 -#define LOAD_MEMORY_ADDRESS 0x00000000 - -// TODO: doxygen comment. +/// @brief The initial stack pointer. extern uintptr_t initial_esp; -/// @brief Halt. -inline static void halt() -{ - __asm__ __volatile__("hlt" ::: "memory"); -} - -/// @brief Pause. -inline static void pause() -{ - __asm__ __volatile__("pause" ::: "memory"); -} +/// @brief Push the `item` of a given `type` on the `stack`. +#define PUSH_ARG(stack, type, item) (*((type *)((stack) -= sizeof(type))) = (item)) +/// Kilobytes. #define K 1024 - +/// Megabytes. #define M (1024 * K) - +/// Gigabytes. #define G (1024 * M) -/// Pointer the beging of the module. -extern char *module_start[MAX_MODULES]; - -/// Address to the end of the module. -extern char *module_end[MAX_MODULES]; - -/// Elf symbols of the kernel. -extern elf_symbols_t kernel_elf; - -/// @brief Entry point of the kernel. -/// @param boot_informations Information concerning the boot. -/// @return The exit status of the kernel. -int kmain(uint32_t magic, multiboot_info_t *header, uintptr_t esp); - -/// @brief Register structs for interrupt/exception. -typedef struct register_t { - /// FS and GS have no hardware-assigned uses. - uint32_t gs; - /// FS and GS have no hardware-assigned uses. - uint32_t fs; - /// Extra Segment determined by the programmer. - uint32_t es; - /// Data Segment. - uint32_t ds; - /// 32-bit destination register. - uint32_t edi; - /// 32-bit source register. - uint32_t esi; - /// 32-bit base pointer register. - uint32_t ebp; - /// 32-bit stack pointer register. - uint32_t esp; - /// 32-bit base register. - uint32_t ebx; - /// 32-bit data register. - uint32_t edx; - /// 32-bit counter. - uint32_t ecx; - /// 32-bit accumulator register. - uint32_t eax; - /// Interrupt number. - uint32_t int_no; - /// Error code. - uint32_t err_code; - /// Instruction Pointer Register. - uint32_t eip; - /// Code Segment. - uint32_t cs; - /// 32-bit flag register. - uint32_t eflags; - // TODO: Check meaning! - uint32_t useresp; - /// Stack Segment. - uint32_t ss; -} register_t; - -/* - * /// @brief Register structs for bios service. - * typedef struct register16_t - * { - * /// Destination Index. - * uint16_t di; - * /// Source Index. - * uint16_t si; - * /// Base Pointer. - * uint16_t bp; - * /// Stack Pointer. - * uint16_t sp; - * /// Also known as the base register. - * uint16_t bx; - * /// Also known as the data register. - * uint16_t dx; - * /// Also known as the count register. - * uint16_t cx; - * /// Is the primary accumulator. - * uint16_t ax; - * /// Data Segment. - * uint16_t ds; - * /// Extra Segment determined by the programmer. - * uint16_t es; - * /// FS and GS have no hardware-assigned uses. - * uint16_t fs; - * /// FS and GS have no hardware-assigned uses. - * uint16_t gs; - * /// Stack Segment. - * uint16_t ss; - * /// 32-bit flag register. - * uint16_t eflags; - * } register16_t; - */ - -//==== Interrupt stack frame =================================================== -// Interrupt stack frame. When the CPU moves from Ring3 to Ring0 because of -// an interrupt, the following registes/values are moved into the kernel's stack -// TODO: doxygen comment. +/// @brief Interrupt stack frame. +/// @details +/// When the CPU moves from Ring3 to Ring0 because of an interrupt, +/// the following registes/values are moved into the kernel's stack typedef struct pt_regs { - /// FS and GS have no hardware-assigned uses. - uint32_t gs; - /// FS and GS have no hardware-assigned uses. - uint32_t fs; - /// Extra Segment determined by the programmer. - uint32_t es; - /// Data Segment. - uint32_t ds; - /// 32-bit destination register. - uint32_t edi; - /// 32-bit source register. - uint32_t esi; - /// 32-bit base pointer register. - uint32_t ebp; - /// 32-bit stack pointer register. - uint32_t esp; - /// 32-bit base register. - uint32_t ebx; - /// 32-bit data register. - uint32_t edx; - /// 32-bit counter. - uint32_t ecx; - /// 32-bit accumulator register. - uint32_t eax; - /// Interrupt number. - uint32_t int_no; - /// Error code. - uint32_t err_code; - /// Instruction Pointer Register. - uint32_t eip; - /// Code Segment. - uint32_t cs; - /// 32-bit flag register. - uint32_t eflags; - // TODO: Check meaning! - uint32_t useresp; - /// Stack Segment. - uint32_t ss; + /// FS and GS have no hardware-assigned uses. + uint32_t gs; + /// FS and GS have no hardware-assigned uses. + uint32_t fs; + /// Extra Segment determined by the programmer. + uint32_t es; + /// Data Segment. + uint32_t ds; + /// 32-bit destination register. + uint32_t edi; + /// 32-bit source register. + uint32_t esi; + /// 32-bit base pointer register. + uint32_t ebp; + /// 32-bit stack pointer register. + uint32_t esp; + /// 32-bit base register. + uint32_t ebx; + /// 32-bit data register. + uint32_t edx; + /// 32-bit counter. + uint32_t ecx; + /// 32-bit accumulator register. + uint32_t eax; + /// Interrupt number. + uint32_t int_no; + /// Error code. + uint32_t err_code; + /// Instruction Pointer Register. + uint32_t eip; + /// Code Segment. + uint32_t cs; + /// 32-bit flag register. + uint32_t eflags; + /// User application ESP. + uint32_t useresp; + /// Stack Segment. + uint32_t ss; } pt_regs; -//============================================================================== - -//==== Floating Point Unit (FPU) Register ====================================== -// Data structure used to save FPU registers. -/// @brief Environment information of floating point unit. -typedef struct { - /// Control word (16bits). - long en_cw; - /// Status word (16bits). - long en_sw; - /// Tag word (16bits). - long en_tw; - /// Floating point instruction pointer. - long en_fip; - /// Floating code segment selector. - unsigned short en_fcs; - /// Opcode last executed (11 bits). - unsigned short en_opcode; - /// Floating operand offset. - long en_foo; - /// Floating operand segment selector. - long en_fos; -} env87; - -/// @brief Contents of each floating point accumulator. -typedef struct { - unsigned char fp_bytes[10]; -} fpacc87; - -/// @brief Floating point context. -typedef struct { - /// Floating point control/status. - env87 sv_env; - /// Accumulator contents, 0-7. - fpacc87 sv_ac[8]; - /// Padding for (now unused) saved status word. - unsigned char sv_pad0[4]; - /* - * Bogus padding for emulators. Emulators should use their own - * struct and arrange to store into this struct (ending here) - * before it is inspected for ptracing or for core dumps. Some - * emulators overwrite the whole struct. We have no good way of - * knowing how much padding to leave. Leave just enough for the - * GPL emulator's i387_union (176 bytes total). - */ - unsigned char sv_pad[64]; // Padding; used by emulators -} save87; - -// TODO: doxygen comment. -typedef struct { - /// Control word (16bits). - uint16_t en_cw; - /// Status word (16bits). - uint16_t en_sw; - /// Tag word (16bits). - uint16_t en_tw; - /// Opcode last executed (11 bits). - uint16_t en_opcode; - /// Floating point instruction pointer. - uint32_t en_fip; - /// Floating code segment selector. - uint16_t en_fcs; - /// Padding. - uint16_t en_pad0; - /// Floating operand offset. - uint32_t en_foo; - /// Floating operand segment selector. - uint16_t en_fos; - /// Padding. - uint16_t en_pad1; - /// SSE sontorol/status register. - uint32_t en_mxcsr; - /// Valid bits in mxcsr. - uint32_t en_mxcsr_mask; -} envxmm; - -/// @brief Contents of each SSE extended accumulator. -typedef struct { - unsigned char xmm_bytes[16]; -} xmmacc; - -// TODO: doxygen comment. -typedef struct { - envxmm sv_env; - struct { - fpacc87 fp_acc; - /// Padding. - unsigned char fp_pad[6]; - } sv_fp[8]; - xmmacc sv_xmm[8]; - - /// Padding. - unsigned char sv_pad[224]; -} __attribute__((__aligned__(16))) savexmm; - -// TODO: doxygen comment. -typedef union { - save87 sv_87; - savexmm sv_xmm; -} savefpu; -//============================================================================== diff --git a/mentos/inc/kernel/sys.h b/mentos/inc/kernel/sys.h deleted file mode 100644 index 6b51478..0000000 --- a/mentos/inc/kernel/sys.h +++ /dev/null @@ -1,15 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file sys.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -// TODO: doxygen comment. -/// @brief -/// @param magic1 -/// @param magic2 -/// @param cmd -/// @param arg -int sys_reboot(int magic1, int magic2, unsigned int cmd, void *arg); diff --git a/mentos/inc/libc/compiler.h b/mentos/inc/klib/compiler.h similarity index 90% rename from mentos/inc/libc/compiler.h rename to mentos/inc/klib/compiler.h index dd661bd..6fb9c27 100644 --- a/mentos/inc/libc/compiler.h +++ b/mentos/inc/klib/compiler.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file compiler.h /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once diff --git a/mentos/inc/klib/hashmap.h b/mentos/inc/klib/hashmap.h new file mode 100644 index 0000000..392a0a3 --- /dev/null +++ b/mentos/inc/klib/hashmap.h @@ -0,0 +1,136 @@ +/// MentOS, The Mentoring Operating system project +/// @file hashmap.h +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "klib/list.h" + +// == OPAQUE TYPES ============================================================ +/// @brief Stores information of an entry of the hashmap. +typedef struct hashmap_entry_t hashmap_entry_t; +/// @brief Stores information of a hashmap. +typedef struct hashmap_t hashmap_t; + +// == HASHMAP FUNCTIONS ======================================================= +/// @brief Hashing function, used to generate hash keys. +typedef unsigned int (*hashmap_hash_t)(const void *key); +/// @brief Comparison function, used to compare hash keys. +typedef int (*hashmap_comp_t)(const void *a, const void *b); +/// @brief Key duplication function, used to duplicate hash keys. +typedef void *(*hashmap_dupe_t)(const void *); +/// @brief Key deallocation function, used to free the memory occupied by hash keys. +typedef void (*hashmap_free_t)(void *); + +// == HASHMAP KEY MANAGEMENT FUNCTIONS ======================================== +/// @brief Transforms an integer key into a hash key. +/// @param key The integer key. +/// @return The resulting hash key. +unsigned int hashmap_int_hash(const void *key); + +/// @brief Compares two integer hash keys. +/// @param a The first hash key. +/// @param b The second hash key. +/// @return Result of the comparison. +int hashmap_int_comp(const void *a, const void *b); + +/// @brief Transforms a string key into a hash key. +/// @param key The string key. +/// @return The resulting hash key. +unsigned int hashmap_str_hash(const void *key); + +/// @brief Compares two string hash keys. +/// @param a The first hash key. +/// @param b The second hash key. +/// @return Result of the comparison. +int hashmap_str_comp(const void *a, const void *b); + +/// @brief This function can be passed as hashmap_dupe_t, it does nothing. +/// @param value The value to duplicate. +/// @return The duplicated value. +void *hashmap_do_not_duplicate(const void *value); + +/// @brief This function can be passed as hashmap_free_t, it does nothing. +/// @param value The value to free. +void hashmap_do_not_free(void *value); + +// == HASHMAP CREATION AND DESTRUCTION ======================================== +/// @brief User-defined hashmap. +/// @param size Dimension of the hashmap. +/// @param hash_fun The hashing function. +/// @param comp_fun The hash compare function. +/// @param dupe_fun The key duplication function. +/// @param key_free_fun The function used to free memory of keys. +/// @return A pointer to the hashmap. +/// @details +/// (key_free_fun) : No free function. +/// (val_free_fun) : Standard `free` function. +hashmap_t *hashmap_create( + unsigned int size, + hashmap_hash_t hash_fun, + hashmap_comp_t comp_fun, + hashmap_dupe_t dupe_fun, + hashmap_free_t key_free_fun); + +/// @brief Standard hashmap with keys of type (char *). +/// @param size Dimension of the hashmap. +/// @return A pointer to the hashmap. +/// @details +/// (key_free_fun) : Standard `free` function. +/// (val_free_fun) : Standard `free` function. +hashmap_t *hashmap_create_str(unsigned int size); + +/// @brief Standard hashmap with keys of type (char *). +/// @param size Dimension of the hashmap. +/// @return A pointer to the hashmap. +/// @details +/// (key_free_fun) : No free function. +/// (val_free_fun) : Standard `free` function. +hashmap_t *hashmap_create_int(unsigned int size); + +/// @brief Frees the memory of the hashmap. +/// @param map A pointer to the hashmap. +void hashmap_free(hashmap_t *map); + +// == HASHMAP ACCESS FUNCTIONS ================================================ +/// @brief Sets the `value` for the given `key` in the hashmap `map`. +/// @param map The hashmap. +/// @param key The entry key. +/// @param value The entry value. +/// @return NULL on success, a pointer to an already existing entry if fails. +void *hashmap_set(hashmap_t *map, const void *key, void *value); + +/// @brief Access the value for the given key. +/// @param map The hashmap. +/// @param key The key of the entry we are searching. +/// @return The value on success, or NULL on failure. +void *hashmap_get(hashmap_t *map, const void *key); + +/// @brief Removes the entry with the given key. +/// @param map The hashmap. +/// @param key The key of the entry we are searching. +/// @return The value on success, or NULL on failure. +void *hashmap_remove(hashmap_t *map, const void *key); + +/// @brief Checks if the hashmap is empty. +/// @param map The hashmap. +/// @return 1 if empty, 0 otherwise. +int hashmap_is_empty(hashmap_t *map); + +/// @brief Checks if the hashmap contains an entry with the given key. +/// @param map The hashmap. +/// @param key The key of the entry we are searching. +/// @return 1 if the entry is present, 0 otherwise. +int hashmap_has(hashmap_t *map, const void *key); + +/// @brief Provides access to all the keys. +/// @param map The hashmap. +/// @return A list with all the keys, remember to destroy the list. +list_t *hashmap_keys(hashmap_t *map); + +/// @brief Provides access to all the values. +/// @param map The hashmap. +/// @return A list with all the values, remember to destroy the list. +list_t *hashmap_values(hashmap_t *map); diff --git a/mentos/inc/klib/irqflags.h b/mentos/inc/klib/irqflags.h new file mode 100644 index 0000000..23b3ad2 --- /dev/null +++ b/mentos/inc/klib/irqflags.h @@ -0,0 +1,52 @@ +/// MentOS, The Mentoring Operating system project +/// @file irqflags.h +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "proc_access.h" +#include "stddef.h" +#include "stdint.h" + +/// @brief Enable IRQs (nested). +/// @details If called after calling irq_nested_disable, this function will +/// not activate IRQs if they were not active before. +inline static void irq_nested_enable(uint8_t flags) +{ + if (flags) { + sti(); + } +} + +/// @brief Disable IRQs (nested). +/// @details Disable IRQs when unsure if IRQs were enabled at all. +/// This function together with irq_nested_enable can be used in +/// situations when interrupts shouldn't be activated if they were not +/// activated before calling this function. +inline static uint8_t irq_nested_disable() +{ + size_t flags; + __asm__ __volatile__("pushf; cli; pop %0" + : "=r"(flags) + : + : "memory"); + if (flags & (1 << 9)) + return 1; + return 0; +} + +/// @brief Determines, if the interrupt flags (IF) is set. +inline static uint8_t is_irq_enabled() +{ + size_t flags; + __asm__ __volatile__("pushf; pop %0" + : "=r"(flags) + : + : "memory"); + if (flags & (1 << 9)) { + return 1; + } + return 0; +} diff --git a/mentos/inc/klib/list.h b/mentos/inc/klib/list.h new file mode 100644 index 0000000..22dacff --- /dev/null +++ b/mentos/inc/klib/list.h @@ -0,0 +1,139 @@ +/// MentOS, The Mentoring Operating system project +/// @file list.h +/// @brief An implementation for generic list. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +/// @brief Represent the node of a list. +typedef struct listnode_t { + /// A pointer to the value. + void *value; + /// The previous node. + struct listnode_t *prev; + /// The next node. + struct listnode_t *next; +} listnode_t; + +/// @brief Represent the list. +typedef struct list_t { + /// The first element of the list. + listnode_t *head; + /// The last element of the list. + listnode_t *tail; + /// The size of the list. + unsigned int size; +} list_t; + +/// @brief Macro used to iterate through a list. +#define listnode_foreach(it, list) \ + for (listnode_t * (it) = (list)->head; (it) != NULL; (it) = (it)->next) + +/// @brief Create a list and set head, tail to NULL, and size to 0. +/// @return The newly created list. +list_t *list_create(); + +/// @brief Get list size. +/// @param list The list. +/// @return The size of the list. +unsigned int list_size(list_t *list); + +/// @brief Checks if the list is empty. +/// @param list The list. +/// @return 1 if empty, 0 otherwise. +int list_empty(list_t *list); + +/// @brief Insert a value at the front of list. +/// @param list The list. +/// @param value The value to insert. +/// @return The node associated with the inserted value. +listnode_t *list_insert_front(list_t *list, void *value); + +/// @brief Insert a value at the back of list. +/// @param list The list. +/// @param value The value to insert. +/// @return The node associated with the inserted value. +listnode_t *list_insert_back(list_t *list, void *value); + +/// @brief Given a listnode, remove it from list. +/// @param list The list. +/// @param node The node that has to be removed. +/// @return The value associated with the removed node. +void *list_remove_node(list_t *list, listnode_t *node); + +/// @brief Remove a value at the front of list. +/// @param list The list. +/// @return The value associated with the removed node. +void *list_remove_front(list_t *list); + +/// @brief Remove a value at the back of list. +/// @param list The list. +/// @return The value associated with the removed node. +void *list_remove_back(list_t *list); + +/// @brief Searches the node of the list which points at the given value. +/// @param list The list. +/// @param value The value that has to be searched. +/// @return The node associated with the value. +listnode_t *list_find(list_t *list, void *value); + +/// @brief Insert after tail of list(same as insert back). +/// @param list The list. +/// @param value The value to insert. +void list_push_back(list_t *list, void *value); + +/// @brief Remove and return the tail of the list. +/// @param list The list. +/// @return The node that has been removed. +/// @details User is responsible for freeing the returned node and the value. +listnode_t *list_pop_back(list_t *list); + +/// @brief Insert before head of list(same as insert front). +/// @param list The list. +/// @param value The value to insert. +void list_push_front(list_t *list, void *value); + +/// @brief Remove and return the head of the list. +/// @param list The list. +/// @return The node that has been removed. +/// @details User is responsible for freeing the returned node and the value. +listnode_t *list_pop_front(list_t *list); + +/// @brief Get the value of the first element but not remove it. +/// @param list The list. +/// @return The value associated with the first node. +void *list_peek_front(list_t *list); + +/// @brief Get the value of the last element but not remove it. +/// @param list The list. +/// @return The value associated with the first node. +void *list_peek_back(list_t *list); + +/// @brief Destroy a list. +/// @param list The list. +void list_destroy(list_t *list); + +/// @brief Checks if the given value is contained inside the list. +/// @param list The list. +/// @param value The value to search. +/// @return -1 if list element is not found, the index otherwise. +int list_get_index_of_value(list_t *list, void *value); + +/// @brief Returns the node at the given index. +/// @param list The list. +/// @param index The index of the desired node. +/// @return A pointer to the node, or NULL otherwise. +listnode_t *list_get_node_by_index(list_t *list, unsigned int index); + +/// @brief Removes a node from the list at the given index. +/// @param list The list. +/// @param index The index of the node we need to remove. +/// @return The value contained inside the node, NULL otherwise. +void *list_remove_by_index(list_t *list, unsigned int index); + +/// @brief Append source at the end of target. +/// @param target Where the element are added. +/// @param source Where the element are removed. +/// @details Beware, source is destroyed. +void list_merge(list_t *target, list_t *source); diff --git a/mentos/inc/libc/list_head.h b/mentos/inc/klib/list_head.h similarity index 53% rename from mentos/inc/libc/list_head.h rename to mentos/inc/klib/list_head.h index f3a0641..1620b33 100644 --- a/mentos/inc/libc/list_head.h +++ b/mentos/inc/klib/list_head.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file list_head.h /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -9,52 +9,65 @@ #include "stddef.h" /// @brief Structure used to implement the list_head data structure. -typedef struct list_head -{ +typedef struct list_head { /// @brief The previous element. - struct list_head * prev; + struct list_head *prev; /// @brief The subsequent element. - struct list_head * next; + struct list_head *next; } list_head; /// @brief Get the struct for this entry. -/// @param ptr The &struct list_head pointer. +/// @param ptr The &list_head pointer. /// @param type The type of the struct this is embedded in. /// @param member The name of the list_head within the struct. #define list_entry(ptr, type, member) \ - ((type *) ((char *) (ptr) - (unsigned long) (&((type *) 0)->member))) + container_of(ptr, type, member) /// @brief Iterates over a list. -/// @param pos The &struct list_head to use as a loop cursor. +/// @param pos The &list_head to use as a loop cursor. /// @param head The head for your list. #define list_for_each(pos, head) \ for ((pos) = (head)->next; (pos) != (head); (pos) = (pos)->next) /// @brief Iterates over a list backwards. -/// @param pos The &struct list_head to use as a loop cursor. +/// @param pos The &list_head to use as a loop cursor. /// @param head The head for your list. #define list_for_each_prev(pos, head) \ for ((pos) = (head)->prev; (pos) != (head); (pos) = (pos)->prev) /// @brief Iterates over a list safe against removal of list entry. -/// @param pos The &struct list_head to use as a loop cursor. -/// @param store Another &struct list_head to use as temporary storage. +/// @param pos The &list_head to use as a loop cursor. +/// @param store Another &list_head to use as temporary storage. /// @param head The head for your list. -#define list_for_each_safe(pos, store, head) \ +#define list_for_each_safe(pos, store, head) \ for ((pos) = (head)->next, (store) = (pos)->next; (pos) != (head); \ - (pos) = (store), (store) = (pos)->next) + (pos) = (store), (store) = (pos)->next) + +/// @brief Iterates over a list. +/// @param pos The &list_head to use as a loop cursor. +/// @param head The head for your list. +#define list_for_each_decl(pos, head) \ + for (list_head * (pos) = (head)->next; (pos) != (head); (pos) = (pos)->next) /// @brief Initializes the list_head. /// @param head The head for your list. -#define list_head_init(head) \ - (head)->next = (head)->prev = (head) +#define list_head_init(head) (head)->next = (head)->prev = (head) + +/// @brief Initializes the list_head. +/// @param head The head for your list. +#define list_head_size(head) \ + ({ \ + unsigned __list_head_size = 0; \ + list_for_each_decl(it, head) __list_head_size += 1; \ + __list_head_size; \ + }) /// @brief Insert element l2 after l1. -static inline void list_head_insert_after(list_head * l1, list_head * l2) +static inline void list_head_insert_after(list_head *l1, list_head *l2) { // [La]->l1 La<-[l1]->Lb <-[l2]-> l1<-[Lb] - list_head * l1_next = l1->next; + list_head *l1_next = l1->next; // [La]->l1 La<-[l1]->l2 <-[l2]-> l1<-[Lb] l1->next = l2; // [La]->l1 La<-[l1]->l2 l1<-[l2]-> l1<-[Lb] @@ -66,11 +79,11 @@ static inline void list_head_insert_after(list_head * l1, list_head * l2) } /// @brief Insert element l2 before l1. -static inline void list_head_insert_before(list_head * l1, list_head * l2) +static inline void list_head_insert_before(list_head *l1, list_head *l2) { // [La]->l1 [l2] La<-[l1]->Lb l1<-[Lb] - list_head * l1_prev = l1->prev; + list_head *l1_prev = l1->prev; // [La]->l2 [l2] La<-[l1]->Lb l1<-[Lb] l1_prev->next = l2; // [La]->l2 La<-[l2] La<-[l1]->Lb l1<-[Lb] @@ -83,7 +96,7 @@ static inline void list_head_insert_before(list_head * l1, list_head * l2) /// @brief Remove l from the list. /// @param l The element to remove. -static inline void list_head_del(list_head * l) +static inline void list_head_del(list_head *l) { // [La]->l La<-[l]->Lb l<-[Lb] @@ -98,15 +111,13 @@ static inline void list_head_del(list_head * l) /// @brief Tests whether the given list is empty. /// @param head The list to check. /// @return 1 if empty, 0 otherwise. -static inline int list_head_empty(list_head const * head) +static inline int list_head_empty(list_head const *head) { return head->next == head; } /// Insert a new entry between two known consecutive entries. -static inline void __list_add(list_head * new, - list_head * prev, - list_head * next) +static inline void __list_add(list_head *new, list_head *prev, list_head *next) { // [prev]-> <-[new]-> <-[next] @@ -121,13 +132,42 @@ static inline void __list_add(list_head * new, } /// @brief Insert element l2 before l1. -static inline void list_head_add(list_head * new, list_head * head) +static inline void list_head_add(list_head *new, list_head *head) { __list_add(new, head, head->next); } /// @brief Insert element l2 before l1. -static inline void list_head_add_tail(list_head * new, list_head * head) +static inline void list_head_add_tail(list_head *new, list_head *head) { __list_add(new, head->prev, head); } + +/// @brief Removes an element from the list pointer, it's used when we have a possibly +/// null list pointer and want to pop an element from it +static inline list_head *list_head_pop(list_head *listp) +{ + if (list_head_empty(listp)) + return NULL; + + list_head *value = listp->next; + list_head_del(listp->next); + + return value; +} + +static inline list_head *list_head_front(list_head *listp) +{ + return listp->next; +} + +/// Merges the elements of l2, into the elements of l1. +static inline void list_head_merge(list_head *l1, list_head *l2) +{ + l1->prev->next = l2->next; + l2->next->prev = l1->prev; + l2->prev->next = l1; + l1->prev = l2->prev; + // Initialize the second list. + list_head_init(l2); +} \ No newline at end of file diff --git a/mentos/inc/libc/mutex.h b/mentos/inc/klib/mutex.h similarity index 74% rename from mentos/inc/libc/mutex.h rename to mentos/inc/klib/mutex.h index a32927d..e6aafe1 100644 --- a/mentos/inc/libc/mutex.h +++ b/mentos/inc/klib/mutex.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file mutex.h /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -10,10 +10,10 @@ /// @brief Structure of a mutex. typedef struct mutex_t { - /// The state of the mutex. - uint8_t state; - /// The owner of the mutex. - uint32_t owner; + /// The state of the mutex. + uint8_t state; + /// The owner of the mutex. + uint32_t owner; } mutex_t; /// @brief Allows to lock a mutex. diff --git a/mentos/inc/klib/ndtree.h b/mentos/inc/klib/ndtree.h new file mode 100644 index 0000000..55a6536 --- /dev/null +++ b/mentos/inc/klib/ndtree.h @@ -0,0 +1,198 @@ +/// MentOS, The Mentoring Operating system project +/// @file ndtree.h +/// @brief N-Dimensional tree. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +// ============================================================================ +// Opaque types. + +/// @brief Node of the tree. +typedef struct ndtree_node_t ndtree_node_t; +/// @brief The tree itself. +typedef struct ndtree_t ndtree_t; +/// @brief Iterator for traversing the tree. +typedef struct ndtree_iter_t ndtree_iter_t; + +// ============================================================================ +// Comparison functions. + +/// @brief Function for comparing elements in the tree. +typedef int (*ndtree_tree_cmp_f)(ndtree_t *tree, void *lhs, void *rhs); +/// @brief Callback to call on elements of the tree. +typedef void (*ndtree_tree_node_f)(ndtree_t *tree, ndtree_node_t *node); + +// ============================================================================ +// Node management functions. + +/// @brief Allocate memory for a node. +/// @return Pointer to the allocated node. +ndtree_node_t *ndtree_node_alloc(); + +/// @brief Allocate memory for a node and sets its value. +/// @param value Value to associated node. +/// @return Pointer to the allocated node. +ndtree_node_t *ndtree_node_create(void *value); + +/// @brief Initializes the already allocated node. +/// @param node The node itself. +/// @param value The value associated to the node. +/// @return Pointer to the node itself. +ndtree_node_t *ndtree_node_init(ndtree_node_t *node, void *value); + +/// @brief Sets the value of the given node. +/// @param node The node to manipulate. +/// @param value The value associated to the node. +void ndtree_node_set_value(ndtree_node_t *node, void *value); + +/// @brief Provides access to the value associated to a node. +/// @param node The node itself. +/// @return The value associated to the node. +void *ndtree_node_get_value(ndtree_node_t *node); + +/// @brief Sets the given node as root of the tree. +/// @param tree The tree. +/// @param node The node to set as root. +void ndtree_set_root(ndtree_t *tree, ndtree_node_t *node); + +/// @brief Creates a new node and assigns it as root of the tree. +/// @param tree The tree. +/// @param value The value associated to the node. +/// @return The newly created node. +ndtree_node_t *ndtree_create_root(ndtree_t *tree, void *value); + +/// @brief Provides access to the root of the tree. +/// @param tree The tree. +/// @return Pointer to the node. +ndtree_node_t *ndtree_get_root(ndtree_t *tree); + +/// @brief Adds the given `child` as child of `parent`. +/// @param tree The tree. +/// @param parent The `parent` node. +/// @param child The new `child` node. +void ndtree_add_child_to_node(ndtree_t *tree, ndtree_node_t *parent, ndtree_node_t *child); + +/// @brief Creates a new node and sets it as child of `parent`. +/// @param tree The tree. +/// @param parent The `parent` node. +/// @param value Value associated with the new child. +/// @return Pointer to the newly created child node. +ndtree_node_t *ndtree_create_child_of_node(ndtree_t *tree, ndtree_node_t *parent, void *value); + +/// @brief Counts the number of children of the given node. +/// @param node The node of which we count the children. +/// @return The number of children. +unsigned int ndtree_node_count_children(ndtree_node_t *node); + +/// @brief Deallocate a node. +/// @param node The node to destroy. +void ndtree_node_dealloc(ndtree_node_t *node); + +// ============================================================================ +// Tree management functions. + +/// @brief Allocate memory for a tree. +/// @return Pointer to the allocated tree. +ndtree_t *ndtree_tree_alloc(); + +/// @brief Allocate memory for a tree and sets the function used to compare nodes. +/// @param cmp Function used to compare elements of the tree. +/// @return Pointer to the allocated tree. +ndtree_t *ndtree_tree_create(ndtree_tree_cmp_f cmp); + +/// @brief Deallocate a node. +/// @param tree The tree to destroy. +/// @param node_cb The function called on each element of the tree before destroying the tree. +void ndtree_tree_dealloc(ndtree_t *tree, ndtree_tree_node_f node_cb); + +/// @brief Initializes the tree. +/// @param tree The tree to initialize. +/// @param cmp The compare function to associate to the tree. +/// @return Pointer to tree itself. +ndtree_t *ndtree_tree_init(ndtree_t *tree, ndtree_tree_cmp_f cmp); + +/// @brief Searches the node inside the tree with the given value. +/// @param tree The tree. +/// @param cmp The node compare function. +/// @param value The value to search. +/// @return Node associated with the value. +ndtree_node_t *ndtree_tree_find(ndtree_t *tree, ndtree_tree_cmp_f cmp, void *value); + +/// @brief Searches the given value among the children of node. +/// @param tree The tree. +/// @param node The node under which we search. +/// @param cmp The node compare function. +/// @param value The value to search. +/// @return Node associated with the value. +ndtree_node_t *ndtree_node_find(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_cmp_f cmp, void *value); + +/// @brief Returns the size of the tree. +/// @param tree The tree. +/// @return The size of the tree. +unsigned int ndtree_tree_size(ndtree_t *tree); + +/// @brief Removes the node from the given tree. +/// @param tree The tree. +/// @param node The node to remove. +/// @param node_cb The function called on the node before removing it. +/// @return Returns 1 if the node was removed, 0 otherwise. +/// @details +/// Optionally the node callback can be provided to dealloc node and/or +/// user data. Use ndtree_tree_node_dealloc default callback to deallocate +/// node created by ndtree_tree_insert(...). +int ndtree_tree_remove_node_with_cb(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f node_cb); + +/// @brief Removes the node from the given tree. +/// @param tree The tree. +/// @param value The value to search. +/// @param node_cb The function called on the node before removing it. +/// @return Returns 1 if the value was removed, 0 otherwise. +/// @details +/// Optionally the node callback can be provided to dealloc node and/or +/// user data. Use ndtree_tree_node_dealloc default callback to deallocate +/// node created by ndtree_tree_insert(...). +int ndtree_tree_remove_with_cb(ndtree_t *tree, void *value, ndtree_tree_node_f node_cb); + +// ============================================================================ +// Iterators. + +/// @brief Allocate the memory for the iterator. +/// @return Pointer to the allocated iterator. +ndtree_iter_t *ndtree_iter_alloc(); + +/// @brief Deallocate the memory for the iterator. +/// @param iter Pointer to the allocated iterator. +void ndtree_iter_dealloc(ndtree_iter_t *iter); + +/// @brief Initializes the iterator the the first child of the node. +/// @param node The node of which we want to iterate the children. +/// @param iter The iterator we want to initialize. +/// @return Pointer to the first node of the list. +ndtree_node_t *ndtree_iter_first(ndtree_node_t *node, ndtree_iter_t *iter); + +/// @brief Initializes the iterator the the last child of the node. +/// @param node The node of which we want to iterate the children. +/// @param iter The iterator we want to initialize. +/// @return Pointer to the last node of the list. +ndtree_node_t *ndtree_iter_last(ndtree_node_t *node, ndtree_iter_t *iter); + +/// @brief Moves the iterator to the next element. +/// @param iter The iterator. +/// @return Pointer to the next element. +ndtree_node_t *ndtree_iter_next(ndtree_iter_t *iter); + +/// @brief Moves the iterator to the previous element. +/// @param iter The iterator. +/// @return Pointer to the previous element. +ndtree_node_t *ndtree_iter_prev(ndtree_iter_t *iter); + +// ============================================================================ +// Tree visiting functions. + +/// @brief Run a visit of the tree (DFS). +/// @param tree The tree to visit. +/// @param enter_fun Function to call when entering a node. +/// @param exit_fun Function to call when exiting a node. +void ndtree_tree_visitor(ndtree_t *tree, ndtree_tree_node_f enter_fun, ndtree_tree_node_f exit_fun); diff --git a/mentos/inc/klib/rbtree.h b/mentos/inc/klib/rbtree.h new file mode 100644 index 0000000..ae8732b --- /dev/null +++ b/mentos/inc/klib/rbtree.h @@ -0,0 +1,180 @@ +/// MentOS, The Mentoring Operating system project +/// @file rbtree.h +/// @brief Red/Black tree. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#ifndef RBTREE_ITER_MAX_HEIGHT +/// Tallest allowable tree to iterate. +#define RBTREE_ITER_MAX_HEIGHT 64 +#endif + +// ============================================================================ +// Opaque types. + +/// @brief Node of the tree. +typedef struct rbtree_node_t rbtree_node_t; +/// @brief The tree itself. +typedef struct rbtree_t rbtree_t; +/// @brief Iterator for traversing the tree. +typedef struct rbtree_iter_t rbtree_iter_t; + +// ============================================================================ +// Comparison functions. +/// @brief Function for comparing elements in the tree. +typedef int (*rbtree_tree_cmp_f)(rbtree_t *tree, rbtree_node_t *a, void *arg); +/// @brief Function for comparing elements in the tree. +typedef int (*rbtree_tree_node_cmp_f)(rbtree_t *tree, rbtree_node_t *a, rbtree_node_t *b); +/// @brief Callback to call on elements of the tree. +typedef void (*rbtree_tree_node_f)(rbtree_t *tree, rbtree_node_t *node); + +// ============================================================================ +// Node management functions. + +/// @brief Allocate memory for a node. +/// @return Pointer to the allocated node. +rbtree_node_t *rbtree_node_alloc(); + +/// @brief Allocate memory for a node and sets its value. +/// @param value Value to associated node. +/// @return Pointer to the allocated node. +rbtree_node_t *rbtree_node_create(void *value); + +/// @brief Initializes the already allocated node. +/// @param node The node itself. +/// @param value The value associated to the node. +/// @return Pointer to the node itself. +rbtree_node_t *rbtree_node_init(rbtree_node_t *node, void *value); + +/// @brief Provides access to the value associated to a node. +/// @param node The node itself. +/// @return The value associated to the node. +void *rbtree_node_get_value(rbtree_node_t *node); + +/// @brief Deallocate a node. +/// @param node The node to destroy. +void rbtree_node_dealloc(rbtree_node_t *node); + +// ============================================================================ +// Tree management functions. + +/// @brief Allocate memory for a tree. +/// @return Pointer to the allocated tree. +rbtree_t *rbtree_tree_alloc(); + +/// @brief Allocate memory for a tree and sets the function used to compare nodes. +/// @param cmp Function used to compare elements of the tree. +/// @return Pointer to the allocated tree. +rbtree_t *rbtree_tree_create(rbtree_tree_node_cmp_f cmp); + +/// @brief Initializes the tree. +/// @param tree The tree to initialize. +/// @param cmp The compare function to associate to the tree. +/// @return Pointer to tree itself. +rbtree_t *rbtree_tree_init(rbtree_t *tree, rbtree_tree_node_cmp_f cmp); + +/// @brief Deallocate a node. +/// @param tree The tree to destroy. +/// @param node_cb The function called on each element of the tree before destroying the tree. +void rbtree_tree_dealloc(rbtree_t *tree, rbtree_tree_node_f node_cb); + +/// @brief Searches the node inside the tree with the given value. +/// @param tree The tree. +/// @param value The value to search. +/// @return Pointer to the value itself. +void *rbtree_tree_find(rbtree_t *tree, void *value); + +/// @brief Searches the node inside the tree with the given value. +/// @param tree The tree. +/// @param cmp_fun The node compare function. +/// @param value The value to search. +/// @return Pointer to the value itself. +void *rbtree_tree_find_by_value(rbtree_t *tree, rbtree_tree_cmp_f cmp_fun, void *value); + +/// @brief Interts the value inside the tree. +/// @param tree The tree. +/// @param value The value to insert. +/// @return 1 on success, 0 on failure. +int rbtree_tree_insert(rbtree_t *tree, void *value); + +/// @brief Removes the value from the given tree. +/// @param tree The tree. +/// @param value The value to search. +/// @return Returns 1 if the value was removed, 0 otherwise. +int rbtree_tree_remove(rbtree_t *tree, void *value); + +/// @brief Returns the size of the tree. +/// @param tree The tree. +/// @return The size of the tree. +unsigned int rbtree_tree_size(rbtree_t *tree); + +/// @brief Interts the value inside the tree. +/// @param tree The tree. +/// @param node The node to insert. +/// @return 1 on success, 0 on failure. +int rbtree_tree_insert_node(rbtree_t *tree, rbtree_node_t *node); + +/// @brief Removes the value from the tree. +/// @param tree The tree. +/// @param value The value to remove. +/// @param node_cb The callback to call on the node before removing the node. +/// @return 1 on success, 0 on failure. +int rbtree_tree_remove_with_cb(rbtree_t *tree, void *value, rbtree_tree_node_f node_cb); + +// ============================================================================ +// Iterators. + +/// @brief Allocate the memory for the iterator. +/// @return Pointer to the allocated iterator. +rbtree_iter_t *rbtree_iter_alloc(); + +/// @brief Deallocate the memory for the iterator. +/// @param iter Pointer to the allocated iterator. +/// @return Pointer to the iterator itself. +rbtree_iter_t *rbtree_iter_init(rbtree_iter_t *iter); + +/// @brief Allocate the memory for the iterator and initializes it. +/// @return Pointer to the allocated iterator. +rbtree_iter_t *rbtree_iter_create(); + +/// @brief Deallocate the memory for the iterator. +/// @param iter Pointer to the allocated iterator. +void rbtree_iter_dealloc(rbtree_iter_t *iter); + +/// @brief Initializes the iterator the the first element of the tree. +/// @param iter The iterator we want to initialize. +/// @param tree The tree. +/// @return Pointer to the first value of the tree. +void *rbtree_iter_first(rbtree_iter_t *iter, rbtree_t *tree); + +/// @brief Initializes the iterator the the last element of the tree. +/// @param iter The iterator we want to initialize. +/// @param tree The tree. +/// @return Pointer to the last value of the tree. +void *rbtree_iter_last(rbtree_iter_t *iter, rbtree_t *tree); + +/// @brief Moves the iterator to the next element. +/// @param iter The iterator. +/// @return Pointer to the next element. +void *rbtree_iter_next(rbtree_iter_t *iter); + +/// @brief Moves the iterator to the previous element. +/// @param iter The iterator. +/// @return Pointer to the previous element. +void *rbtree_iter_prev(rbtree_iter_t *iter); + +// ============================================================================ +// Tree debugging functions. + +/// @brief Checks the tree. +/// @param tree The tree. +/// @param root The root of the tree. +/// @return 1 on failure, 0 on success. +int rbtree_tree_test(rbtree_t *tree, rbtree_node_t *root); + +/// @brief Prints the tree using the provided callback. +/// @param tree The tree. +/// @param fun The print callback. +void rbtree_tree_print(rbtree_t *tree, rbtree_tree_node_f fun); diff --git a/mentos/inc/libc/spinlock.h b/mentos/inc/klib/spinlock.h similarity index 54% rename from mentos/inc/libc/spinlock.h rename to mentos/inc/klib/spinlock.h index 5976c70..f9f3c4d 100644 --- a/mentos/inc/libc/spinlock.h +++ b/mentos/inc/klib/spinlock.h @@ -1,32 +1,34 @@ /// MentOS, The Mentoring Operating system project /// @file spinlock.h /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "debug.h" -#include "stddef.h" -#include "irqflags.h" -#include "stdatomic.h" -#include "scheduler.h" +#include "klib/stdatomic.h" +/// Determines if the spinlock is free. #define SPINLOCK_FREE 0 - +/// Determines if the spinlock is busy. #define SPINLOCK_BUSY 1 /// @brief Spinlock structure. typedef atomic_t spinlock_t; /// @brief Initialize the spinlock. +/// @param spinlock The spinlock we initialize. void spinlock_init(spinlock_t *spinlock); /// @brief Try to lock the spinlock. +/// @param spinlock The spinlock we lock. void spinlock_lock(spinlock_t *spinlock); /// @brief Try to unlock the spinlock. +/// @param spinlock The spinlock we unlock. void spinlock_unlock(spinlock_t *spinlock); /// @brief Try to unlock the spinlock. -bool_t spinlock_trylock(spinlock_t *spinlock); +/// @param spinlock The spinlock we try to block. +/// @return 1 if succeeded, 0 otherwise. +int spinlock_trylock(spinlock_t *spinlock); diff --git a/mentos/inc/klib/stdatomic.h b/mentos/inc/klib/stdatomic.h new file mode 100644 index 0000000..03234d4 --- /dev/null +++ b/mentos/inc/klib/stdatomic.h @@ -0,0 +1,166 @@ +/// MentOS, The Mentoring Operating system project +/// @file stdatomic.h +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stdint.h" +#include "stdbool.h" +#include "klib/compiler.h" + +/// @brief Standard structure for atomic operations (see below +/// for volatile explanation). +typedef volatile unsigned atomic_t; + +/// @brief The prefix used to lock. +#define LOCK_PREFIX "\n\tlock; " + +/// @brief Compile read-write barrier. +#define barrier() __asm__ __volatile__("" \ + : \ + : \ + : "memory") + +/// @brief Pause instruction to prevent excess processor bus usage. +#define cpu_relax() __asm__ __volatile__("pause\n" \ + : \ + : \ + : "memory") + +/// @brief Atomically sets the value of ptr with the value of x. +/// +/// @param ptr Pointer to the atomic variable to set. +/// @param x The value to set. +/// @return The final value of the atomic variable. +inline static int32_t atomic_set_and_test(atomic_t *ptr, int32_t x) +{ + __asm__ __volatile__("xchgl %0,%1" // instruction + : "=r"(x) // outputs + : "m"(*ptr), "0"(x) // inputs + : "memory"); // side effects + return x; +} + +/// @brief Atomically set ptr equal to i. +inline static void atomic_set(atomic_t *ptr, int32_t i) +{ + atomic_set_and_test(ptr, i); +} + +/// @brief Atomically read the integer value of ptr. +inline static int32_t atomic_read(const atomic_t *ptr) +{ + return READ_ONCE(*ptr); +} + +/// @brief Atomically add i to ptr. +inline static int32_t atomic_add(atomic_t const *ptr, int32_t i) +{ + int result = i; + __asm__ __volatile__(LOCK_PREFIX "xaddl %0, %1" + : "=r"(i) + : "m"(ptr), "0"(i) + : "memory", "cc"); + return result + i; +} + +/// @brief Atomically subtract i from ptr. +inline static int32_t atomic_sub(atomic_t *ptr, int32_t i) +{ + return atomic_add(ptr, -i); +} + +/// @brief Atomically add one to ptr. +inline static int32_t atomic_inc(atomic_t *ptr) +{ + return atomic_add(ptr, 1); +} + +/// @brief Atomically subtract one from ptr. +inline static int32_t atomic_dec(atomic_t *ptr) +{ + return atomic_add(ptr, -1); +} + +/// @brief Atomically add i to ptr and return true if the result is negative; +/// otherwise false. +inline static bool_t atomic_add_negative(atomic_t *ptr, int32_t i) +{ + return (bool_t)(atomic_add(ptr, i) < 0); +} + +/// @brief Atomically subtract i from ptr and return true if the result is +/// zero; otherwise false. +inline static bool_t atomic_sub_and_test(atomic_t *ptr, int32_t i) +{ + return (bool_t)(atomic_sub(ptr, i) == 0); +} + +/// @brief Atomically increment ptr by one and return true if the result is +/// zero; false otherwise. +inline static int32_t atomic_inc_and_test(atomic_t *ptr) +{ + return (bool_t)(atomic_inc(ptr) == 0); +} + +/// @brief Atomically decrement ptr by one and return true if zero; false +/// otherwise. +inline static int32_t atomic_dec_and_test(atomic_t *ptr) +{ + return (bool_t)(atomic_dec(ptr) == 0); +} + +/// @brief Atomically sets a bit in memory, using Bit Test And Set (bts). +/// @param offset The offset to the bit. +/// @param base The base address. +static inline void set_bit(int offset, volatile unsigned long *base) +{ + __asm__ __volatile__("btsl %[offset],%[base]" + : [base] "=m"(*(volatile long *)base) + : [offset] "Ir"(offset)); +} + +/// @brief Atomically clears a bit in memory. +/// @param offset The offset to the bit. +/// @param base The base address. +static inline void clear_bit(int offset, volatile unsigned long *base) +{ + __asm__ __volatile__("btrl %[offset],%[base]" + : [base] "=m"(*(volatile long *)base) + : [offset] "Ir"(offset)); +} + +/// @brief Atomically tests a bit in memory. +/// @param offset The offset to the bit. +/// @param base The base address. +/// @return 1 if the bit is set, 0 otherwise. +static inline int test_bit(int offset, volatile unsigned long *base) +{ + int old = 0; + __asm__ __volatile__("btl %[offset],%[base]\n" // Bit Test + "sbbl %[old],%[old]\n" // Return the previous value. + : [old] "=r"(old) + : [base] "m"(*(volatile long *)base), + [offset] "Ir"(offset)); + return old; +} + +// == Volatile Variable ======================================================= +// In C, and consequently C++, the volatile keyword was intended to: +// - allow access to memory-mapped I/O devices +// - allow uses of variables between setjmp and longjmp +// - allow uses of sig_atomic_t variables in signal handlers. +// +// Operations on volatile variables are not atomic, nor do they establish +// a proper happens-before relationship for threading like with the +// `__asm__` inline blocks. +// This is specified in the relevant standards (C, C++, POSIX, WIN32), and +// volatile variables are not thread-safe in the vast majority of current +// implementations. +// Thus, the usage of volatile keyword as a portable synchronization mechanism +// is discouraged by many C/C++ groups. + +// == xchg/xchgl ============================================================== +// \ No newline at end of file diff --git a/mentos/inc/libc/assert.h b/mentos/inc/libc/assert.h deleted file mode 100644 index f5812e1..0000000 --- a/mentos/inc/libc/assert.h +++ /dev/null @@ -1,34 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file assert.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stdio.h" -#include "panic.h" -#include "stdarg.h" - -/// @brief Function used to log the information of a failed assertion. -/// @param assertion The failed assertion. -/// @param file The file where the assertion is located. -/// @param line The line inside the file. -/// @param function The function where the assertion is. -static void __assert_fail(const char *assertion, const char *file, - unsigned int line, const char *function) -{ - char message[1024]; - sprintf(message, - "FILE: %s\n" - "LINE: %d\n" - "FUNC: %s\n\n" - "Assertion `%s` failed.\n", - file, line, (function ? function : "NO_FUN"), assertion); - kernel_panic(message); -} - -/// @brief Assert function. -#define assert(expression) \ - ((expression) ? (void)0 : \ - __assert_fail(#expression, __FILE__, __LINE__, __func__)) diff --git a/mentos/inc/libc/bitset.h b/mentos/inc/libc/bitset.h deleted file mode 100644 index 094150f..0000000 --- a/mentos/inc/libc/bitset.h +++ /dev/null @@ -1,42 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file bitset.h -/// @brief Bitset data structure. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" -#include "stdbool.h" - -/// @brief Bitset structure. -typedef struct { - /// The internal data. - unsigned char *data; - /// The size of the bitset. - size_t size; -} bitset_t; - -// TODO: doxygen comment. -/// @brief -void bitset_init(bitset_t *set, size_t size); - -// TODO: doxygen comment. -/// @brief -void bitset_free(bitset_t *set); - -// TODO: doxygen comment. -/// @brief -void bitset_set(bitset_t *set, size_t bit); - -// TODO: doxygen comment. -/// @brief -void bitset_clear(bitset_t *set, size_t bit); - -// TODO: doxygen comment. -/// @brief -bool_t bitset_test(bitset_t *set, size_t bit); - -// TODO: doxygen comment. -/// @brief -signed long bitset_find_first_unset_bit(bitset_t *set); diff --git a/mentos/inc/libc/fcvt.h b/mentos/inc/libc/fcvt.h deleted file mode 100644 index a645dfc..0000000 --- a/mentos/inc/libc/fcvt.h +++ /dev/null @@ -1,27 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file fcvt.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -// TODO: doxygen comment. -/// @brief -/// @param arg -/// @param ndigits -/// @param decpt -/// @param sign -/// @param buf -/// @result -char *ecvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf); - -// TODO: doxygen comment. -/// @brief -/// @param arg -/// @param ndigits -/// @param decpt -/// @param sign -/// @param buf -/// @result -char *fcvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf); diff --git a/mentos/inc/libc/hashmap.h b/mentos/inc/libc/hashmap.h deleted file mode 100644 index 32b94cf..0000000 --- a/mentos/inc/libc/hashmap.h +++ /dev/null @@ -1,80 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file hashmap.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "list.h" -#include "stdbool.h" - -//============================================================================== -// Opaque types. -typedef struct hashmap_entry_t hashmap_entry_t; -typedef struct hashmap_t hashmap_t; - -//============================================================================= -// Hashmap functions. - -// TODO: doxygen comment. -typedef size_t (*hashmap_hash_t)(void *key); - -// TODO: doxygen comment. -typedef bool_t (*hashmap_comp_t)(void *a, void *b); - -// TODO: doxygen comment. -typedef void (*hashmap_free_t)(void *); - -// TODO: doxygen comment. -typedef void *(*hashmap_dupe_t)(void *); - -//============================================================================== -// Hashmap creation and destruction. - -// TODO: doxygen comment. -extern hashmap_t *hashmap_create(size_t size); - -// TODO: doxygen comment. -extern hashmap_t *hashmap_create_int(size_t size); - -// TODO: doxygen comment. -extern void hashmap_free(hashmap_t *map); - -//============================================================================== -// Hashmap management. - -// TODO: doxygen comment. -extern void *hashmap_set(hashmap_t *map, void *key, void *value); - -// TODO: doxygen comment. -extern void *hashmap_get(hashmap_t *map, void *key); - -// TODO: doxygen comment. -extern void *hashmap_remove(hashmap_t *map, void *key); - -//============================================================================== -// Hashmap search. - -// TODO: doxygen comment. -extern bool_t hashmap_is_empty(hashmap_t *map); - -// TODO: doxygen comment. -extern bool_t hashmap_has(hashmap_t *map, void *key); - -// TODO: doxygen comment. -extern list_t *hashmap_keys(hashmap_t *map); - -// TODO: doxygen comment. -extern list_t *hashmap_values(hashmap_t *map); - -// TODO: doxygen comment. -extern size_t hashmap_string_hash(void *key); - -// TODO: doxygen comment. -extern bool_t hashmap_string_comp(void *a, void *b); - -// TODO: doxygen comment. -extern void *hashmap_string_dupe(void *key); - -//============================================================================== diff --git a/mentos/inc/libc/irqflags.h b/mentos/inc/libc/irqflags.h deleted file mode 100644 index b7145db..0000000 --- a/mentos/inc/libc/irqflags.h +++ /dev/null @@ -1,72 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file irqflags.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" -#include "stdint.h" - -/// @brief Enable IRQs. -inline static void irq_enable() -{ - __asm__ __volatile__("sti" ::: "memory"); -} - -// TODO: doxygen comment. -inline unsigned long get_eflags() -{ - unsigned long eflags; - /* "=rm" is safe here, because "pop" adjusts the stack before - * it evaluates its effective address -- this is part of the - * documented behavior of the "pop" instruction. - */ - __asm__ __volatile__("pushf ; pop %0" - : "=rm"(eflags) - : /* no input */ - : "memory"); - return eflags; -} - -/// @brief Enable IRQs (nested). -/// @details If called after calling irq_nested_disable, this function will -/// not activate IRQs if they were not active before. -inline static void irq_nested_enable(uint8_t flags) -{ - if (flags) { - irq_enable(); - } -} - -/// @brief Disable IRQs. -inline static void irq_disable() -{ - __asm__ __volatile__("cli" ::: "memory"); -} - -/// @brief Disable IRQs (nested). -/// @details Disable IRQs when unsure if IRQs were enabled at all. -/// This function together with irq_nested_enable can be used in -/// situations when interrupts shouldn't be activated if they were not -/// activated before calling this function. -inline static uint8_t irq_nested_disable() -{ - size_t flags; - __asm__ __volatile__("pushf; cli; pop %0" : "=r"(flags) : : "memory"); - if (flags & (1 << 9)) - return 1; - return 0; -} - -/// @brief Determines, if the interrupt flags (IF) is set. -inline static uint8_t is_irq_enabled() -{ - size_t flags; - __asm__ __volatile__("pushf; pop %0" : "=r"(flags) : : "memory"); - if (flags & (1 << 9)) { - return 1; - } - return 0; -} diff --git a/mentos/inc/libc/libgen.h b/mentos/inc/libc/libgen.h deleted file mode 100644 index 5f9bc61..0000000 --- a/mentos/inc/libc/libgen.h +++ /dev/null @@ -1,28 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file libgen.h -/// @brief String routines. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "stddef.h" - -// TODO: doxygen comment. -/// @brief -/// @param out -/// @param cur -/// @param sep -/// @param max -/// @result -int parse_path(char *out, char **cur, char sep, size_t max); - -// TODO: doxygen comment. -/// @brief -/// @param path -/// @result -char *dirname(const char *path); - -// TODO: doxygen comment. -/// @brief -/// @param path -/// @result -char *basename(const char *path); diff --git a/mentos/inc/libc/limits.h b/mentos/inc/libc/limits.h deleted file mode 100644 index 9b09ccf..0000000 --- a/mentos/inc/libc/limits.h +++ /dev/null @@ -1,41 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file limits.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -/// Number of bits in a `char'. -#define CHAR_BIT 8 - -/// Minimum value a `signed char' can hold. - -#define SCHAR_MIN (-128) - -/// Maximum value a `signed char' can hold. -#define SCHAR_MAX 127 - -/// Maximum value a `char' can hold. (Minimum is 0.) -#define CHAR_MAX 255 - -/// Minimum value a `signed short int' can hold. -#define SHRT_MIN (-32768) - -/// Maximum value a `signed short int' can hold. -#define SHRT_MAX 32767 - -/// Minimum value a `signed int' can hold. -#define INT_MIN (-INT_MAX - 1) - -/// Maximum values a `signed int' can hold. -#define INT_MAX 2147483647 - -/// Maximum value an `unsigned int' can hold. (Minimum is 0.) -#define UINT_MAX 4294967295U - -/// Minimum value a `signed long int' can hold. -#define LONG_MAX 2147483647L - -/// Maximum value a `signed long int' can hold. -#define LONG_MIN (-LONG_MAX - 1L) diff --git a/mentos/inc/libc/list.h b/mentos/inc/libc/list.h deleted file mode 100644 index c8852ab..0000000 --- a/mentos/inc/libc/list.h +++ /dev/null @@ -1,107 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file list.h -/// @brief An implementation for generic list. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stdint.h" -#include "stddef.h" -#include "stdbool.h" - -/// @brief Represent the node of a list. -typedef struct listnode_t { - /// A pointer to the value. - void *value; - /// The previous node. - struct listnode_t *prev; - /// The next node. - struct listnode_t *next; -} listnode_t; - -/// @brief Represent the list. -typedef struct list_t { - /// The first element of the list. - listnode_t *head; - /// The last element of the list. - listnode_t *tail; - /// The size of the list. - size_t size; -} list_t; - -/// @brief Macro used to iterate through a list. -#define listnode_foreach(it, list) \ - for (listnode_t * (it) = (list)->head; (it) != NULL; (it) = (it)->next) - -/// @brief Create a list and set head, tail to NULL, and size to 0. -list_t *list_create(); - -/// @brief Get list size. -size_t list_size(list_t *list); - -/// @brief Checks if the list is empty. -bool_t list_empty(list_t *list); - -/// @brief Insert a value at the front of list. -listnode_t *list_insert_front(list_t *list, void *value); - -/// @brief Insert a value at the back of list. -listnode_t *list_insert_back(list_t *list, void *value); - -/// @brief Insert a value at the back of list. -void list_insert_node_back(list_t *list, listnode_t *item); - -/// @brief Given a listnode, remove it from lis. -void *list_remove_node(list_t *list, listnode_t *node); - -/// @brief Remove a value at the front of list. -void *list_remove_front(list_t *list); - -/// @brief Remove a value at the back of list. -void *list_remove_back(list_t *list); - -/// @brief Searches the node of the list which points at the given value. -listnode_t *list_find(list_t *list, void *value); - -/// @brief Insert after tail of list(same as insert back). -void list_push(list_t *list, void *value); - -/// @brief Remove and return the tail of the list. -/// @details User is responsible for freeing the returned node and the value. -listnode_t *list_pop_back(list_t *list); - -/// @brief Remove and return the head of the list. -/// @details User is responsible for freeing the returned node and the value. -listnode_t *list_pop_front(list_t *list); - -/// @brief Insert before head of list(same as insert front). -void list_enqueue(list_t *list, void *value); - -/// @brief Remove and return tail of list(same as list_pop). -listnode_t *list_dequeue(list_t *list); - -/// @brief Get the value of the first element but not remove it. -void *list_peek_front(list_t *list); - -/// @brief Get the value of the last element but not remove it. -void *list_peek_back(list_t *list); - -/// @brief Destory a list. -void list_destroy(list_t *list); - -/// @brief Destroy a node of the list. -void listnode_destroy(listnode_t *node); - -/// @brief Does the list contain a value (Return -1 if list element is not -/// found). -int list_contain(list_t *list, void *value); - -/// @brief Returns the node at the given index. -listnode_t *list_get_node_by_index(list_t *list, size_t index); - -/// @brief Removes a node from the list at the given index. -void *list_remove_by_index(list_t *list, size_t index); - -/// @brief Append source at the end of target and DESTROY source. -void list_merge(list_t *target, list_t *source); diff --git a/mentos/inc/libc/math.h b/mentos/inc/libc/math.h deleted file mode 100644 index e41a9cc..0000000 --- a/mentos/inc/libc/math.h +++ /dev/null @@ -1,131 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file math.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -/// @brief The absolute value. -#define abs(a) (((a) < 0) ? -(a) : (a)) - -/// @brief The max of the two values. -#define max(a, b) (((a) > (b)) ? (a) : (b)) - -/// @brief The min of the two values. -#define min(a, b) (((a) < (b)) ? (a) : (b)) - -/// @brief The sign the the passed value. -#define sign(x) ((x < 0) ? -1 : ((x > 0) ? 1 : 0)) - -/// @brief Returns a rounded up, away from zero, to the nearest multiple of b. -#define ceil(NUMBER, BASE) (((NUMBER) + (BASE)-1) & ~((BASE)-1)) - -/// @brief e -#define M_E 2.7182818284590452354 - -/// @brief log_2 e -#define M_LOG2E 1.4426950408889634074 - -/// @brief log_10 e -#define M_LOG10E 0.43429448190325182765 - -/// @brief log_e 2 -#define M_LN2 0.69314718055994530942 - -/// @brief log_e 10 -#define M_LN10 2.30258509299404568402 - -/// @brief pi -#define M_PI 3.14159265358979323846 - -/// @brief pi / 2 -#define M_PI_2 1.57079632679489661923 - -/// @brief pi / 4 -#define M_PI_4 0.78539816339744830962 - -/// @brief 1 / pi -#define M_1_PI 0.31830988618379067154 - -/// @brief 2 / pi -#define M_2_PI 0.63661977236758134308 - -/// @brief 2 / sqrt(pi) -#define M_2_SQRTPI 1.12837916709551257390 - -/// @brief sqrt(2) -#define M_SQRT2 1.41421356237309504880 - -/// @brief 1 / sqrt(2) -#define M_SQRT1_2 0.70710678118654752440 - -// TODO: doxygen comment. -/// @brief -/// @param x -/// @result -double round(double x); - -// TODO: doxygen comment. -/// @brief -/// @param x -/// @result -double floor(double x); - -/// @brief Power function. -/// @param x First number. -/// @param y Second number. -/// @result Power between number x and y. -double pow(double x, double y); - -// TODO: doxygen comment. -/// @brief -/// @param base -/// @param value -/// @result -long find_nearest_pow_greater(double base, double value); - -/// @brief Exponential function. -/// @param x Value of the exponent. -double exp(double x); - -// TODO: doxygen comment. -/// @brief -/// @param x -/// @result -double fabs(double x); - -/// @brief Square root function. -/// @param x Topic of the square root. -double sqrt(double x); - -// TODO: doxygen comment. -/// @brief -/// @param x -/// @result -int isinf(double x); - -// TODO: doxygen comment. -/// @brief -/// @param x -/// @result -int isnan(double x); - -/// @brief Logarithm function in base 10. -/// @param x Topic of the logarithm function. -double log10(double x); - -/// @brief Natural logarithm function. -/// @param x Topic of the logarithm function. -double ln(double x); - -/// @brief Logarithm function in base x. -/// @brief x Base of the logarithm. -/// @param y Topic of the logarithm function. -double logx(double x, double y); - -/// @brief Breaks x into an integral and a fractional part. -/// The integer part is stored in the object pointed by intpart, and the -/// fractional part is returned by the function. Both parts have the same -/// sign as x. -double modf(double x, double *intpart); diff --git a/mentos/inc/libc/ordered_array.h b/mentos/inc/libc/ordered_array.h deleted file mode 100644 index b4cf225..0000000 --- a/mentos/inc/libc/ordered_array.h +++ /dev/null @@ -1,53 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file ordered_array.h -/// @brief Interface for creating, inserting and deleting from ordered arrays. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stdint.h" - -/// @brief This array is insertion sorted - it always remains in a sorted -/// state (between calls). It can store anything that can be cast to a -/// void* -- so a uint32_t, or any pointer. -typedef void *array_type_t; - -/// @brief A predicate should return nonzero if the first argument is less -/// than the second. Else it should return zero. -typedef int8_t (*lessthan_predicate_t)(array_type_t, array_type_t); - -/// @brief Structure which holds information concerning an ordered array. -typedef struct ordered_array_t { - /// Pointer to the array. - array_type_t *array; - /// The size of the array. - uint32_t size; - /// The maximum size of the array. - uint32_t max_size; - /// Ordering fucntion. - lessthan_predicate_t less_than; -} ordered_array_t; - -/// @brief A standard less than predicate. -int8_t standard_lessthan_predicate(array_type_t a, array_type_t b); - -/// @brief Create an ordered array. -ordered_array_t create_ordered_array(uint32_t max_size, - lessthan_predicate_t less_than); - -/// @brief Set the ordered array. -ordered_array_t place_ordered_array(void *addr, uint32_t max_size, - lessthan_predicate_t less_than); - -/// @brief Destroy an ordered array. -void destroy_ordered_array(ordered_array_t *array); - -/// @brief Add an item into the array. -void insert_ordered_array(array_type_t item, ordered_array_t *array); - -/// @brief Lookup the item at index i. -array_type_t lookup_ordered_array(uint32_t i, ordered_array_t *array); - -/// @brief Deletes the item at location i from the array. -void remove_ordered_array(uint32_t i, ordered_array_t *array); diff --git a/mentos/inc/libc/queue.h b/mentos/inc/libc/queue.h deleted file mode 100644 index ca7a1f9..0000000 --- a/mentos/inc/libc/queue.h +++ /dev/null @@ -1,78 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file queue.h -/// @brief Implementation of queue data structure. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" -#include "stdbool.h" - -/// @brief A node of the queue. -typedef struct queue_node_t -{ - /// The wrapped data. - void *data; - /// The next node of the queue. - struct queue_node_t *next; -} queue_node_t; - -/// @brief The queue. -typedef struct queue_t -{ - /// The front of the queue. - queue_node_t *front; - /// The back of the queue. - queue_node_t *back; - /// The size of the data contained inside the queue. - size_t data_size; -} *queue_t; - -/// @brief Creates a queue. -/// @param data_size The size of the stored elements. -/// @return The created queue. -queue_t queue_create(size_t data_size); - -/// @brief Destroys the queue. -/// @param queue The queue to be destroyed. -/// @return If the queue is destroyed. -bool_t queue_destroy(queue_t queue); - -/// @brief Returns if the queue is empty. -/// @param queue The queue. -bool_t queue_is_empty(queue_t queue); - -/// @brief Allows to add data to the queue. -/// @param queue The queue. -/// @param data The data to add. -/// @return If the data has been pushed inside the queue. -bool_t queue_enqueue(queue_t queue, void *data); - -/// @brief Removes the first element of the queue. -/// @param queue The queue. -/// @return If the data has been removed. -bool_t queue_dequeue(queue_t queue); - -/// @brief Returns the first element of the queue. -/// @param queue The queue. -/// @param data The data. -/// @return If the data has been correctly retrieved. -bool_t queue_front(queue_t queue, void *data); - -/// @brief Returns the last element of the queue. -/// @param queue The queue. -/// @param data The data. -/// @return If the data has been correctly retrieved. -bool_t queue_back(queue_t queue, void *data); - -/// @brief Returns the first element of the queue and removes it. -/// @param queue The queue. -/// @param data The data. -/// @return If the data has been correctly retrieved. -bool_t queue_front_and_dequeue(queue_t queue, void *data); - -/// @brief Deletes all the elements inside the queue. -/// @param queue The queue. -/// @return If the queue has been cleared. -bool_t queue_clear(queue_t queue); diff --git a/mentos/inc/libc/rbtree.h b/mentos/inc/libc/rbtree.h deleted file mode 100644 index b2c9806..0000000 --- a/mentos/inc/libc/rbtree.h +++ /dev/null @@ -1,134 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file rbtree.h -/// @brief Implementation of red black tree data structure. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" - -#ifndef RBTREE_ITER_MAX_HEIGHT - -/// Tallest allowable tree to iterate. -#define RBTREE_ITER_MAX_HEIGHT 64 -#endif - -//============================================================================== -// Opaque types. - -// TODO: doxygen comment. -typedef struct rbtree_t rbtree_t; - -// TODO: doxygen comment. -typedef struct rbtree_node_t rbtree_node_t; - -// TODO: doxygen comment. -typedef struct rbtree_iter_t rbtree_iter_t; - -//============================================================================== -// Comparison functions. - -// TODO: doxygen comment. -typedef int (*rbtree_tree_cmp_f)(rbtree_t *self, rbtree_node_t *a, void *arg); - -// TODO: doxygen comment. -typedef int (*rbtree_tree_node_cmp_f)(rbtree_t *self, rbtree_node_t *a, - rbtree_node_t *b); - -// TODO: doxygen comment. -typedef void (*rbtree_tree_node_f)(rbtree_t *self, rbtree_node_t *node); - -//============================================================================== -// Node management functions. - -// TODO: doxygen comment. -rbtree_node_t *rbtree_node_alloc(); - -// TODO: doxygen comment. -rbtree_node_t *rbtree_node_create(void *value); - -// TODO: doxygen comment. -rbtree_node_t *rbtree_node_init(rbtree_node_t *self, void *value); - -// TODO: doxygen comment. -void *rbtree_node_get_value(rbtree_node_t *self); - -// TODO: doxygen comment. -void rbtree_node_dealloc(rbtree_node_t *self); - -//============================================================================== -// Tree management functions. - -// TODO: doxygen comment. -rbtree_t *rbtree_tree_alloc(); - -// TODO: doxygen comment. -rbtree_t *rbtree_tree_create(rbtree_tree_node_cmp_f cmp); - -// TODO: doxygen comment. -rbtree_t *rbtree_tree_init(rbtree_t *self, rbtree_tree_node_cmp_f cmp); - -// TODO: doxygen comment. -void rbtree_tree_dealloc(rbtree_t *self, rbtree_tree_node_f node_cb); - -// TODO: doxygen comment. -void *rbtree_tree_find(rbtree_t *self, void *value); - -// TODO: doxygen comment. -void *rbtree_tree_find_by_value(rbtree_t *self, rbtree_tree_cmp_f cmp_fun, - void *value); - -// TODO: doxygen comment. -int rbtree_tree_insert(rbtree_t *self, void *value); - -// TODO: doxygen comment. -int rbtree_tree_remove(rbtree_t *self, void *value); - -// TODO: doxygen comment. -size_t rbtree_tree_size(rbtree_t *self); - -// TODO: doxygen comment. -int rbtree_tree_insert_node(rbtree_t *self, rbtree_node_t *node); - -// TODO: doxygen comment. -int rbtree_tree_remove_with_cb(rbtree_t *self, void *value, - rbtree_tree_node_f node_cb); - -//============================================================================== -// Iterators. - -// TODO: doxygen comment. -rbtree_iter_t *rbtree_iter_alloc(); - -// TODO: doxygen comment. -rbtree_iter_t *rbtree_iter_init(rbtree_iter_t *self); - -// TODO: doxygen comment. -rbtree_iter_t *rbtree_iter_create(); - -// TODO: doxygen comment. -void rbtree_iter_dealloc(rbtree_iter_t *self); - -// TODO: doxygen comment. -void *rbtree_iter_first(rbtree_iter_t *self, rbtree_t *tree); - -// TODO: doxygen comment. -void *rbtree_iter_last(rbtree_iter_t *self, rbtree_t *tree); - -// TODO: doxygen comment. -void *rbtree_iter_next(rbtree_iter_t *self); - -// TODO: doxygen comment. -void *rbtree_iter_prev(rbtree_iter_t *self); - -//============================================================================== -// Tree debugging functions. - -// TODO: doxygen comment. -int rbtree_tree_test(rbtree_t *self, rbtree_node_t *root); - -// TODO: doxygen comment. -void rbtree_tree_print(rbtree_t *self, rbtree_tree_node_f fun); - -//============================================================================== diff --git a/mentos/inc/libc/stdatomic.h b/mentos/inc/libc/stdatomic.h deleted file mode 100644 index 8ec33ff..0000000 --- a/mentos/inc/libc/stdatomic.h +++ /dev/null @@ -1,109 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file stdatomic.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stdint.h" -#include "stdbool.h" -#include "compiler.h" - -/// @brief Standard structure for atomic operations. -typedef volatile unsigned atomic_t; - -/// @brief At declaration, initialize an atomic_t to i. -#define ATOMIC_INIT(i) \ - { \ - (i) \ - } - -/// @brief The prefix used to lock. -#define LOCK_PREFIX "\n\tlock; " - -/// @brief Compile read-write barrier. -#define barrier() asm volatile("" : : : "memory") - -/// @brief Pause instruction to prevent excess processor bus usage. -#define cpu_relax() asm volatile("pause\n" : : : "memory") - -// TODO: doxygen comment. -inline static int32_t atomic_set_and_test(atomic_t const *ptr, int32_t i) -{ - __asm__ __volatile__("xchgl %0,%1" - : "=r"(i) - : "m"(*(volatile unsigned *)ptr), "0"(i) - : "memory"); - // __asm__ __volatile__("xchgl %0, %1" : "=r"(i) : "m"(ptr), "0"(i) : "memory"); - return i; -} - -/// @brief Atomically set ptr equal to i. -inline static void atomic_set(atomic_t *ptr, int32_t i) -{ - atomic_set_and_test(ptr, i); -} - -/// @brief Atomically read the integer value of ptr. -inline static int32_t atomic_read(const atomic_t *ptr) -{ - return READ_ONCE(*ptr); -} - -/// @brief Atomically add i to ptr. -inline static int32_t atomic_add(atomic_t const *ptr, int32_t i) -{ - int result = i; - asm volatile(LOCK_PREFIX "xaddl %0, %1" - : "=r"(i) - : "m"(ptr), "0"(i) - : "memory", "cc"); - return result + i; -} - -/// @brief Atomically subtract i from ptr. -inline static int32_t atomic_sub(atomic_t *ptr, int32_t i) -{ - return atomic_add(ptr, -i); -} - -/// @brief Atomically add one to ptr. -inline static int32_t atomic_inc(atomic_t *ptr) -{ - return atomic_add(ptr, 1); -} - -/// @brief Atomically subtract one from ptr. -inline static int32_t atomic_dec(atomic_t *ptr) -{ - return atomic_add(ptr, -1); -} - -/// @brief Atomically add i to ptr and return true if the result is negative; -/// otherwise false. -inline static bool_t atomic_add_negative(atomic_t *ptr, int32_t i) -{ - return (bool_t)(atomic_add(ptr, i) < 0); -} - -/// @brief Atomically subtract i from ptr and return true if the result is -/// zero; otherwise false. -inline static bool_t atomic_sub_and_test(atomic_t *ptr, int32_t i) -{ - return (bool_t)(atomic_sub(ptr, i) == 0); -} - -/// @brief Atomically increment ptr by one and return true if the result is -/// zero; false otherwise. -inline static int32_t atomic_inc_and_test(atomic_t *ptr) -{ - return (bool_t)(atomic_inc(ptr) == 0); -} - -/// @brief Atomically decrement ptr by one and return true if zero; false -/// otherwise. -inline static int32_t atomic_dec_and_test(atomic_t *ptr) -{ - return (bool_t)(atomic_dec(ptr) == 0); -} diff --git a/mentos/inc/libc/stdbool.h b/mentos/inc/libc/stdbool.h deleted file mode 100644 index 9a52976..0000000 --- a/mentos/inc/libc/stdbool.h +++ /dev/null @@ -1,16 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file stdbool.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -/// @brief Define boolean value. -typedef enum bool_t -{ - /// [0] False. - false, - /// [1] True. - true -} __attribute__ ((__packed__)) bool_t; diff --git a/mentos/inc/libc/stddef.h b/mentos/inc/libc/stddef.h deleted file mode 100644 index 04a4a03..0000000 --- a/mentos/inc/libc/stddef.h +++ /dev/null @@ -1,87 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file stddef.h -/// @brief Define basic data types. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#ifndef NULL - -/// @brief Define NULL. -#define NULL ((void*)0) - -#endif - -#ifndef EOF - -/// @brief Define End-Of-File. -#define EOF (-1) - -#endif - -/// @brief Define the size of a buffer. -#define BUFSIZ 512 - -/// @brief Define the size of the kernel. -#define KERNEL_SIZE 0x200 - -#ifndef TRUE - -/// @brief Define the value of true. -#define TRUE 1 - -#endif - -#ifndef FALSE - -/// @brief Define the value of false. -#define FALSE 0 - -#endif - -/// @brief Define the byte type. -typedef unsigned char byte_t; - -/// @brief Define the generic size type. -typedef unsigned long size_t; - -/// @brief Define the generic signed size type. -typedef long ssize_t; - -/// @brief Define the type of an inode. -typedef unsigned int ino_t; - -// TODO: doxygen comment. -typedef unsigned int dev_t; - -/// @brief The type of user-id. -typedef unsigned int uid_t; - -/// @brief The type of group-id. -typedef unsigned int gid_t; - -/// @brief The type of offset. -typedef unsigned int off_t; - -/// @brief The type of mode. -typedef unsigned int mode_t; - -// TODO: doxygen comment. -typedef mode_t pgprot_t; - -// TODO: doxygen comment. -typedef unsigned long int ulong; - -// TODO: doxygen comment. -typedef unsigned short int ushort; - -// TODO: doxygen comment. -typedef unsigned int uint; - -// TODO: doxygen comment. -typedef int key_t; - -// TODO: doxygen comment. -// Check with clock.h -typedef long __time_t; diff --git a/mentos/inc/libc/stdio.h b/mentos/inc/libc/stdio.h deleted file mode 100644 index 8300319..0000000 --- a/mentos/inc/libc/stdio.h +++ /dev/null @@ -1,48 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file stdio.h -/// @brief Standard I/0 functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stdarg.h" -#include "stddef.h" - -/// @brief The maximum number of digits of an integer. -#define MAX_DIGITS_IN_INTEGER 11 - -#ifndef EOF -/// @brief Define the End-Of-File. -#define EOF (-1) -#endif - -/// @brief Writes the given character to the standard output (stdout). -void putchar(int character); - -/// @brief Writes the string pointed by str to the standard output (stdout) -/// and appends a newline character ('\n'). -void puts(char *str); - -/// @brief Returns the next character from the standard input (stdin). -int getchar(void); - -/// @brief Reads characters from the standard input (stdin) and stores them -/// as a C string into str until a newline character or the end-of-file is -/// reached. -char *gets(char *str); - -/// @brief Convert the given string to an integer. -int atoi(const char *str); - -/// @brief Write formatted output to stdout. -int printf(const char *, ...); - -/// @brief Read formatted input from stdin. -size_t scanf(const char *, ...); - -/// @brief Write formatted output to the string str from argument list ARG. -int vsprintf(char *str, const char *fmt, va_list args); - -/// @brief Write formatted output to the string str. -int sprintf(char *str, const char *fmt, ...); diff --git a/mentos/inc/libc/stdlib.h b/mentos/inc/libc/stdlib.h deleted file mode 100644 index 1142c18..0000000 --- a/mentos/inc/libc/stdlib.h +++ /dev/null @@ -1,39 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file stdlib.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" - -/// @brief Malloc based on the number of elements. -void *calloc(size_t element_number, size_t element_size); - -/// @brief Allows to set the seed of the random value generator. -void srand(int x); - -void *malloc(unsigned int size); - -void free(void * p); - - -#ifndef MS_RAND - -/// @brief The maximum value of the random. -#define RAND_MAX ((1U << 31) - 1) - -/// @brief Generates a random value. -int rand(); - -// MS rand -#else - -#define RAND_MAX_32 ((1U << 31) - 1) - -#define RAND_MAX ((1U << 15) - 1) - -int rand(); - -#endif diff --git a/mentos/inc/libc/strerror.h b/mentos/inc/libc/strerror.h deleted file mode 100644 index 631db12..0000000 --- a/mentos/inc/libc/strerror.h +++ /dev/null @@ -1,12 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file strerror.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "errno.h" - -// TODO: doxygen comment. -char *strerror(int errnum); diff --git a/mentos/inc/libc/string.h b/mentos/inc/libc/string.h deleted file mode 100644 index c3a4fdf..0000000 --- a/mentos/inc/libc/string.h +++ /dev/null @@ -1,159 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file string.h -/// @brief String routines. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "list.h" -#include "stddef.h" - -/// @brief Copies the first num characters of source to destination. -char *strncpy(char *destination, const char *source, size_t num); - -/// @brief Compares up to n characters of s1 to those of s2. -int strncmp(const char *s1, const char *s2, size_t n); - -/// @brief Case insensitive string compare. -int stricmp(const char *s1, const char *s2); - -/// @brief Case-insensitively compare up to n characters of s1 to those of s2. -int strnicmp(const char *s1, const char *s2, size_t n); - -/// @brief Returns a pointer to the first occurrence of ch in str. -char *strchr(const char *s, int ch); - -/// @brief Returns a pointer to the last occurrence of ch in str. -char *strrchr(const char *s, int ch); - -/// @brief Returns a pointer to the first occurrence of s2 in s1, or NULL if -/// s2 is not part of s1. -char *strstr(const char *s1, const char *s2); - -/// @brief Returns the length of the initial portion of string which consists -/// only of characters that are part of control. -size_t strspn(const char *string, const char *control); - -/// @brief Calculates the length of the initial segment of string which -/// consists entirely of characters not in control. -size_t strcspn(const char *string, const char *control); - -/// @brief Finds the first character in the string string that matches any -/// character specified in control. -char *strpbrk(const char *string, const char *control); - -/// @brief Make a copy of the given string. -char *strdup(const char *s); - -/// @brief Make a copy of the given string. -char *kstrdup(const char *s); - -/// @brief Appends the string pointed to, by s2 to the end of the string -/// pointed to, by s1 up to n characters long. -char *strncat(char *s1, const char *s2, size_t n); - -/// @brief Fill the string s with the character c, to the given length n. -char *strnset(char *s, int c, size_t n); - -/// @brief Fill the string s with the character c. -char *strset(char *s, int c); - -/// @brief Reverse the string s. -char *strrev(char *s); - -// TODO: Check behaviour & doxygen comment. -char *strtok(char *str, const char *delim); - -// TODO: Check behaviour & doxygen comment. -char *strtok_r(char *string, const char *control, char **lasts); - -/// @brief Another function to copy n characters from str2 to str1. -void *memmove(void *dst, const void *src, size_t n); - -/// @brief Searches for the first occurrence of the character c (an unsigned -/// char) in the first n bytes of the string pointed to, by the -/// argument str. -void *memchr(const void *str, int c, size_t n); - -char *strlwr(char *s); - -char *strupr(char *s); - -/// @brief Copies the first n bytes from memory area src to memory area dest, -/// stopping when the character c is found. -void *memccpy(void *dst, const void *src, int c, size_t n); - -/// @brief Copy a block of memory, handling overlap. -/// @param _dst Pointer to the destination. -/// @param _src Pointer to the source. -/// @param num Number of bytes to be copied. -/// @return Pointer to the destination. -void *memcpy(void *_dst, const void *_src, size_t num); - -/// @brief Compares the first n bytes of str1 and str2. -int memcmp(const void *str1, const void *str2, size_t n); - -/// @brief Sets the first num bytes of the block of memory pointed by ptr -/// to the specified value. -/// @param ptr Pointer to the block of memory to set. -/// @param value Value to be set. -/// @param num Number of bytes to be set to the given value. -/// @return The same ptr. -void *memset(void *ptr, int value, size_t num); - -/// @brief Copy the string src into the array dst. -char *strcpy(char *dst, const char *src); - -/// @brief Appends a copy of the string src to the string dst. -char *strcat(char *dst, const char *src); - -/// @brief Checks if the two strings are equal. -int strcmp(const char *s1, const char *s2); - -/// @brief Returns the lenght of the string s. -size_t strlen(const char *s); - -/// @brief Returns the number of characters inside s, excluding the -/// terminating null byte ('\0'), but at most count. -size_t strnlen(const char *s, size_t count); - -/// @brief Separate a string in token according to the delimiter. -/// If str is NULL, the scanning will continue for the previous string. -/// It can be bettered. -char *strtok(char *str, const char *delim); - -/// @brief Compare num characters of s2 and s1. -int _kstrncmp(const char *s1, const char *s2, size_t num); - -/// @brief Removes the whitespaces in from of str. -char *trim(char *str); - -/// @brief Create a copy of str. -char *strdup(const char *src); - -/// @brief Separate stringp based on delim. -char *strsep(char **stringp, const char *delim); - -/// @brief Split a string into list of strings. -list_t *str_split(const char *str, const char *delim, unsigned int *num); - -/// @brief Reconstruct a string from list using delim as delimiter. -char *list2str(list_t *list, const char *delim); - -/// @brief Move the number "num" into a string. -/// @param buffer The string containing the number. -/// @param num The number to convert. -/// @param base The base used to convert. -void int_to_str(char *buffer, unsigned int num, unsigned int base); - -/// @brief Transforms num into a string. -void _knntos(char *buffer, int num, int base); - -/// @brief Replaces the occurences of find with replace inside str. -char *replace_char(char *str, char find, char replace); - -/// @brief Converts a file mode (the type and permission information associated -/// with an inode) into a symbolic string which is stored in the location -/// referenced by p. -void strmode(mode_t mode, char *p); diff --git a/mentos/inc/libc/tree.h b/mentos/inc/libc/tree.h deleted file mode 100644 index 097a36a..0000000 --- a/mentos/inc/libc/tree.h +++ /dev/null @@ -1,77 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file tree.h -/// @brief General-purpose tree implementation. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "list.h" - -/// @brief A node of the tree. -typedef struct tree_node_t { - /// The value stored by the node. - void *value; - /// Pointer to the list of childrens. - list_t *children; - /// Pointer to the parent. - struct tree_node_t *parent; -} tree_node_t; - -/// @brief Represents the tree. -typedef struct tree_t { - /// Number of nodes inside the tree. - size_t nodes; - /// Pointer to the root of the tree. - tree_node_t *root; -} tree_t; - -typedef uint8_t (*tree_comparator_t)(void *, void *); - -/// @brief Creates a new tree. -tree_t *tree_create(); - -/// @brief Sets the root node for a new tree. -tree_node_t *tree_set_root(tree_t *tree, void *value); - -/// @brief Free the contents of a node and its children, -/// but not the nodes themselves. -void tree_node_destroy(tree_node_t *node); - -/// @brief Free the contents of a tree, but not the nodes. -void tree_destroy(tree_t *tree); - -/// @brief Free all of the nodes in a tree, but not their contents. -void tree_free(tree_t *tree); - -/// @brief Create a new tree node pointing to the given value. -tree_node_t *tree_node_create(void *value); - -/// @brief Insert a node as a child of parent. -void tree_node_insert_child_node(tree_t *tree, tree_node_t *parent, - tree_node_t *node); - -/// @brief Insert a (fresh) node as a child of parent. -tree_node_t *tree_node_insert_child(tree_t *tree, tree_node_t *parent, - void *value); - -/// @brief Recursive node part of tree_find_parent. -tree_node_t *tree_node_find_parent(tree_node_t *haystack, tree_node_t *needle); - -/// @brief Remove a node when we know its parent; update node counts for the -/// tree. -void tree_node_parent_remove(tree_t *tree, tree_node_t *parent, - tree_node_t *node); - -/// @brief Remove an entire branch given its root. -void tree_node_remove(tree_t *tree, tree_node_t *node); - -/// @brief Remove this node and move its children into its parent's -/// list of children. -void tree_remove(tree_t *tree, tree_node_t *node); - -// TODO: doxygen comment. -void tree_break_off(tree_t *tree, tree_node_t *node); - -/// @brief Searches the given value inside the tree. -tree_node_t *tree_find(tree_t *tree, void *value, tree_comparator_t comparator); diff --git a/mentos/inc/link_access.h b/mentos/inc/link_access.h new file mode 100644 index 0000000..76df631 --- /dev/null +++ b/mentos/inc/link_access.h @@ -0,0 +1,45 @@ +/// MentOS, The Mentoring Operating system project +/// @file link_access.h +/// @brief Set of macros that provide access to linking symbols. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#ifdef __APPLE__ +#include + +/// @brief Define variables pointing to external symbols, specifically +/// to the .data section of a linked object file. +#define EXTLD(NAME) extern const unsigned char _section$__DATA__##NAME[]; +/// Provide access to the .data of the linked object. +#define LDVAR(NAME) _section$__DATA__##NAME +/// Provides access to the length of the .data section of the linked object. +#define LDLEN(NAME) (getsectbyname("__DATA", "__" #NAME)->size) + +#elif (defined __WIN32__) // mingw + +/// @brief Define variables pointing to external symbols, specifically +/// to the .data section of a linked object file. +#define EXTLD(NAME) \ + extern const unsigned char binary_##NAME##_start[]; \ + extern const unsigned char binary_##NAME##_end[]; \ + extern const unsigned char binary_##NAME##_size[]; +/// Provide access to the .data of the linked object. +#define LDVAR(NAME) binary_##NAME##_start +/// Provides access to the length of the .data section of the linked object. +#define LDLEN(NAME) binary_##NAME##_size + +#else // gnu/linux ld + +/// @brief Define variables pointing to external symbols, specifically +/// to the .data section of a linked object file. +#define EXTLD(NAME) \ + extern const unsigned char _binary_##NAME##_start[]; \ + extern const unsigned char _binary_##NAME##_end[]; \ + extern const unsigned char _binary_##NAME##_size[]; +/// Provides access to the .data of the linked object. +#define LDVAR(NAME) _binary_##NAME##_start +/// Provides access to the length of the .data section of the linked object. +#define LDLEN(NAME) _binary_##NAME##_size +#endif diff --git a/mentos/inc/mem/buddysystem.h b/mentos/inc/mem/buddysystem.h index 160d0d1..2b311d1 100644 --- a/mentos/inc/mem/buddysystem.h +++ b/mentos/inc/mem/buddysystem.h @@ -1,25 +1,124 @@ /// MentOS, The Mentoring Operating system project /// @file buddysystem.h /// @brief Buddy System. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "zone_allocator.h" +#include "klib/list_head.h" +#include "klib/stdatomic.h" +#include "stdint.h" -/// @brief Allocate a block of page frames in a zone of size 2^{order. -/// @param zone A memory zone. -/// @param order The logarithm of the size of the block. -/// @return The address of the first page descriptor of the block, or NULL. -page_t *bb_alloc_pages(zone_t *zone, unsigned int order); +/// @brief Max gfp pages order of buddysystem blocks. +#define MAX_BUDDYSYSTEM_GFP_ORDER 14 -/// @brief Free a block of page frames in a zone of size 2^{order. -/// @param zone A memory zone. -/// @param page The address of the first page descriptor of the block. -/// @param order The logarithm of the size of the block. -void bb_free_pages(zone_t *zone, page_t *page, unsigned int order); +/// @brief Provide the offset of the element inside the given type of page. +#define BBSTRUCT_OFFSET(page, element) \ + ((uint32_t) & (((page *)NULL)->element)) -/// @brief Print the size of free_list of each free_area. -/// @param zone A memory zone. -void buddy_system_dump(zone_t *zone); +/// @brief Returns the address of the given element of a given type of page, +/// based on the provided bbstruct. +#define PG_FROM_BBSTRUCT(bbstruct, page, element) \ + ((page *)(((uint32_t)(bbstruct)) - BBSTRUCT_OFFSET(page, element))) + +/// The base structure representing a bb page +typedef struct bb_page_t { + /// The flags of the page. + volatile unsigned long flags; + /// The current page order. + uint32_t order; + /// Keep track of where the page is located. + union { + /// The page siblings when not allocated. + list_head siblings; + /// The cache list pointer when allocated but on cache. + list_head cache; + } location; +} bb_page_t; + +/// @brief Buddy system descriptor: collection of free page blocks. +/// Each block represents 2^k free contiguous page. +typedef struct bb_free_area_t { + /// free_list collectes the first page descriptors of a blocks of 2^k frames + list_head free_list; + /// nr_free specifies the number of blocks of free pages. + int nr_free; +} bb_free_area_t; + +/// @brief Buddy system instance, +/// that represents a memory area managed by the buddy system +typedef struct bb_instance_t { + /// Name of this bb instance + const char *name; + /// List of buddy system pages grouped by level. + bb_free_area_t free_area[MAX_BUDDYSYSTEM_GFP_ORDER]; + /// Pointer to start of free pages cache + list_head free_pages_cache_list; + /// Size of the current cache + unsigned long free_pages_cache_size; + /// Buddysystem instance size in number of pages. + unsigned long size; + /// Address of the first managed page + bb_page_t *base_page; + /// Size of the (padded) wrapper page structure + unsigned long pgs_size; + /// Offset of the bb_page_t struct from the start of the whole structure + unsigned long bbpg_offset; +} bb_instance_t; + +/// @brief Allocate a block of page frames of size 2^order. +/// @param instance A buddy system instance. +/// @param order The logarithm of the size of the block. +/// @return The address of the first page descriptor of the block, or NULL. +bb_page_t *bb_alloc_pages(bb_instance_t *instance, unsigned int order); + +/// @brief Free a block of page frames of size 2^order. +/// @param instance A buddy system instance. +/// @param page The address of the first page descriptor of the block. +void bb_free_pages(bb_instance_t *instance, bb_page_t *page); + +/// @brief Alloc a page using bb cache. +/// @param instance Buddy system instance. +/// @return An allocated page. +bb_page_t *bb_alloc_page_cached(bb_instance_t *instance); + +/// @brief Free a page allocated with bb_alloc_page_cached. +/// @param instance Buddy system instance. +/// @param page The address of the first page descriptor of the block. +void bb_free_page_cached(bb_instance_t *instance, bb_page_t *page); + +/// @brief Initialize Buddy System. +/// @param instance A buddysystem instance. +/// @param name The name of the current instance (for debug purposes) +/// @param pages_start The start address of the page structures +/// @param bbpage_offset The offset from the start of the whole page of the +/// bb_page_t struct. +/// @param pages_stride The (padded) size of the whole page structure +/// @param pages_count The number of pages in this region +void buddy_system_init( + bb_instance_t *instance, + const char *name, + void *pages_start, + uint32_t bbpage_offset, + uint32_t pages_stride, + uint32_t pages_count); + +/// @brief Print the size of free_list of each free_area. +/// @param instance A buddy system instance. +void buddy_system_dump(bb_instance_t *instance); + +/// @brief Returns the total space for the given instance. +/// @param instance A buddy system instance. +/// @return The requested total sapce. +unsigned long buddy_system_get_total_space(bb_instance_t *instance); + +/// @brief Returns the free space for the given instance. +/// @param instance A buddy system instance. +/// @return The requested total sapce. +unsigned long buddy_system_get_free_space(bb_instance_t *instance); + +/// @brief Returns the cached space for the given instance. +/// @param instance A buddy system instance. +/// @return The requested total sapce. +unsigned long buddy_system_get_cached_space(bb_instance_t *instance); diff --git a/mentos/inc/mem/gfp.h b/mentos/inc/mem/gfp.h index 602745e..c1785fe 100644 --- a/mentos/inc/mem/gfp.h +++ b/mentos/inc/mem/gfp.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file gfp.h -/// @brief List of GFP Flags. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @brief List of Get Free Pages (GFP) Flags. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -9,159 +9,145 @@ /// Type used for GFP_FLAGS. typedef unsigned int gfp_t; -// Plain integer GFP bitmasks. Do not use this directly. -#define ___GFP_DMA 0x01u +/// @defgroup GFP The Get Free Pages (GFP) flags +/// @brief Sets of GFP defines. +/// @{ -#define ___GFP_HIGHMEM 0x02u +/// @defgroup gfp_bitmasks Bitmasks +/// @brief Plain integer GFP bitmasks. Do not use this directly. +/// @{ -#define ___GFP_DMA32 0x04u +#define ___GFP_DMA 0x001U ///< DMA +#define ___GFP_HIGHMEM 0x002U ///< HIGHMEM +#define ___GFP_DMA32 0x004U ///< DMA32 +#define ___GFP_RECLAIMABLE 0x010U ///< RECLAIMABLE +#define ___GFP_HIGH 0x020U ///< HIGH +#define ___GFP_IO 0x040U ///< IO +#define ___GFP_FS 0x080U ///< FS +#define ___GFP_ZERO 0x100U ///< ZERO +#define ___GFP_ATOMIC 0x200U ///< ATOMIC +#define ___GFP_DIRECT_RECLAIM 0x400U ///< DIRECT_RECLAIM +#define ___GFP_KSWAPD_RECLAIM 0x800U ///< KSWAPD_RECLAIM -#define ___GFP_RECLAIMABLE 0x10u +/// @} -#define ___GFP_HIGH 0x20u - -#define ___GFP_IO 0x40u - -#define ___GFP_FS 0x80u - -#define ___GFP_ZERO 0x100u - -#define ___GFP_ATOMIC 0x200u - -#define ___GFP_DIRECT_RECLAIM 0x400u - -#define ___GFP_KSWAPD_RECLAIM 0x800u - -/* - * Physical address zone modifiers (see linux/mmzone.h - low four bits) - * - * Do not put any conditional on these. If necessary modify the definitions - * without the underscores and use them consistently. The definitions here may - * be used in bit comparisons. - */ -#define __GFP_DMA ___GFP_DMA - -#define __GFP_HIGHMEM ___GFP_HIGHMEM - -#define __GFP_DMA32 ___GFP_DMA32 +/// @defgroup zone_modifiers Zone Modifiers +/// @brief Physical address zone modifiers (see linux/mmzone.h - low four bits) +/// @details +/// Do not put any conditional on these. If necessary modify the definitions +/// without the underscores and use them consistently. The definitions here may +/// be used in bit comparisons. +/// @{ +#define __GFP_DMA ___GFP_DMA ///< DMA +#define __GFP_HIGHMEM ___GFP_HIGHMEM ///< HIGHMEM +#define __GFP_DMA32 ___GFP_DMA32 ///< DMA32 +/// All of the above. #define GFP_ZONEMASK (__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32) -/** - * DOC: Watermark modifiers - * - * Watermark modifiers -- controls access to emergency reserves - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * %__GFP_HIGH indicates that the caller is high-priority and that granting - * the request is necessary before the system can make forward progress. - * For example, creating an IO context to clean pages. - * - * %__GFP_ATOMIC indicates that the caller cannot reclaim or sleep and is - * high priority. Users are typically interrupt handlers. This may be - * used in conjunction with %__GFP_HIGH - */ +/// @} + +/// @defgroup WatermarkModifiers Watermark Modifiers +/// @brief Controls access to emergency reserves. +/// @{ + +/// @brief Indicates that the caller cannot reclaim or sleep and is +/// high priority. Users are typically interrupt handlers. This may be +/// used in conjunction with %__GFP_HIGH. #define __GFP_ATOMIC ___GFP_ATOMIC +/// @brief Indicates that the caller is high-priority and that granting +/// the request is necessary before the system can make forward progress. +/// For example, creating an IO context to clean pages. #define __GFP_HIGH ___GFP_HIGH -/** - * DOC: Reclaim modifiers - * - * Reclaim modifiers - * ~~~~~~~~~~~~~~~~~ - * - * %__GFP_IO can start physical IO. - * - * %__GFP_FS can call down to the low-level FS. Clearing the flag avoids the - * allocator recursing into the filesystem which might already be holding - * locks. - * - * %__GFP_DIRECT_RECLAIM indicates that the caller may enter direct reclaim. - * This flag can be cleared to avoid unnecessary delays when a fallback - * option is available. - * - * %__GFP_KSWAPD_RECLAIM indicates that the caller wants to wake kswapd when - * the low watermark is reached and have it reclaim pages until the high - * watermark is reached. A caller may wish to clear this flag when fallback - * options are available and the reclaim is likely to disrupt the system. The - * canonical example is THP allocation where a fallback is cheap but - * reclaim/compaction may cause indirect stalls. - * - * %__GFP_RECLAIM is shorthand to allow/forbid both direct and kswapd reclaim. - */ +/// @} + +/// @defgroup ReclaimModifiers Reclaim Modifiers +/// @brief Enable reclaim operations on a specific region of memory. +/// @{ + +/// @brief Can start physical IO. #define __GFP_IO ___GFP_IO +/// @brief Can call down to the low-level FS. Clearing the flag avoids the +/// allocator recursing into the filesystem which might already be holding +/// locks. #define __GFP_FS ___GFP_FS -// Caller can reclaim. + +/// @brief Indicates that the caller may enter direct reclaim. +/// This flag can be cleared to avoid unnecessary delays when a fallback +/// option is available. #define __GFP_DIRECT_RECLAIM ___GFP_DIRECT_RECLAIM -// Kswapd can wake. +/// @brief Indicates that the caller wants to wake kswapd when +/// the low watermark is reached and have it reclaim pages until the high +/// watermark is reached. A caller may wish to clear this flag when fallback +/// options are available and the reclaim is likely to disrupt the system. The +/// canonical example is THP allocation where a fallback is cheap but +/// reclaim/compaction may cause indirect stalls. #define __GFP_KSWAPD_RECLAIM ___GFP_KSWAPD_RECLAIM +/// @brief Is shorthand to allow/forbid both direct and kswapd reclaim. #define __GFP_RECLAIM (___GFP_DIRECT_RECLAIM | ___GFP_KSWAPD_RECLAIM) -/** - * DOC: Useful GFP flag combinations - * - * Useful GFP flag combinations - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * Useful GFP flag combinations that are commonly used. It is recommended - * that subsystems start with one of these combinations and then set/clear - * %__GFP_FOO flags as necessary. - * - * %GFP_ATOMIC users can not sleep and need the allocation to succeed. A lower - * watermark is applied to allow access to "atomic reserves" - * - * %GFP_KERNEL is typical for kernel-internal allocations. The caller requires - * %ZONE_NORMAL or a lower zone for direct access but can direct reclaim. - * - * %GFP_NOWAIT is for kernel allocations that should not stall for direct - * reclaim, start physical IO or use any filesystem callback. - * - * %GFP_NOIO will use direct reclaim to discard clean pages or slab pages - * that do not require the starting of any physical IO. - * Please try to avoid using this flag directly and instead use - * memalloc_noio_{save,restore} to mark the whole scope which cannot - * perform any IO with a short explanation why. All allocation requests - * will inherit GFP_NOIO implicitly. - * - * %GFP_NOFS will use direct reclaim but will not use any filesystem interfaces. - * Please try to avoid using this flag directly and instead use - * memalloc_nofs_{save,restore} to mark the whole scope which cannot/shouldn't - * recurse into the FS layer with a short explanation why. All allocation - * requests will inherit GFP_NOFS implicitly. - * - * %GFP_USER is for userspace allocations that also need to be directly - * accessibly by the kernel or hardware. It is typically used by hardware - * for buffers that are mapped to userspace (e.g. graphics) that hardware - * still must DMA to. cpuset limits are enforced for these allocations. - * - * %GFP_DMA exists for historical reasons and should be avoided where possible. - * The flags indicates that the caller requires that the lowest zone be - * used (%ZONE_DMA or 16M on x86-64). Ideally, this would be removed but - * it would require careful auditing as some users really require it and - * others use the flag to avoid lowmem reserves in %ZONE_DMA and treat the - * lowest zone as a type of emergency reserve. - * - * %GFP_HIGHUSER is for userspace allocations that may be mapped to userspace, - * do not need to be directly accessible by the kernel but that cannot - * move once in use. An example may be a hardware allocation that maps - * data directly into userspace but has no addressing limitations. - */ +/// @} + +/// @defgroup gfp_flag_combinations Flag Combinations +/// @brief Useful GFP flag combinations. +/// @details +/// Useful GFP flag combinations that are commonly used. It is recommended +/// that subsystems start with one of these combinations and then set/clear +/// the flags as necessary. +/// @{ + +/// @brief Users can not sleep and need the allocation to succeed. A lower +/// watermark is applied to allow access to "atomic reserves" #define GFP_ATOMIC (__GFP_HIGH | __GFP_ATOMIC | __GFP_KSWAPD_RECLAIM) +/// @brief is typical for kernel-internal allocations. The caller requires +/// %ZONE_NORMAL or a lower zone for direct access but can direct reclaim. #define GFP_KERNEL (__GFP_RECLAIM | __GFP_IO | __GFP_FS) +/// @brief is for kernel allocations that should not stall for direct +/// reclaim, start physical IO or use any filesystem callback. #define GFP_NOWAIT (__GFP_KSWAPD_RECLAIM) +/// @brief will use direct reclaim to discard clean pages or slab pages +/// that do not require the starting of any physical IO. +/// Please try to avoid using this flag directly and instead use +/// memalloc_noio_{save,restore} to mark the whole scope which cannot +/// perform any IO with a short explanation why. All allocation requests +/// will inherit GFP_NOIO implicitly. #define GFP_NOIO (__GFP_RECLAIM) +/// @brief will use direct reclaim but will not use any filesystem interfaces. +/// Please try to avoid using this flag directly and instead use +/// memalloc_nofs_{save,restore} to mark the whole scope which cannot/shouldn't +/// recurse into the FS layer with a short explanation why. All allocation +/// requests will inherit GFP_NOFS implicitly. #define GFP_NOFS (__GFP_RECLAIM | __GFP_IO) +/// @brief is for userspace allocations that also need to be directly +/// accessibly by the kernel or hardware. It is typically used by hardware +/// for buffers that are mapped to userspace (e.g. graphics) that hardware +/// still must DMA to. cpuset limits are enforced for these allocations. #define GFP_USER (__GFP_RECLAIM | __GFP_IO | __GFP_FS) +/// @brief exists for historical reasons and should be avoided where possible. +/// The flags indicates that the caller requires that the lowest zone be +/// used (%ZONE_DMA or 16M on x86-64). Ideally, this would be removed but +/// it would require careful auditing as some users really require it and +/// others use the flag to avoid lowmem reserves in %ZONE_DMA and treat the +/// lowest zone as a type of emergency reserve. #define GFP_DMA (__GFP_DMA) +/// @brief is for userspace allocations that may be mapped to userspace, +/// do not need to be directly accessible by the kernel but that cannot +/// move once in use. An example may be a hardware allocation that maps +/// data directly into userspace but has no addressing limitations. #define GFP_HIGHUSER (GFP_USER | __GFP_HIGHMEM) + +/// @} + +/// @} diff --git a/mentos/inc/mem/kheap.h b/mentos/inc/mem/kheap.h index 299904f..309b74f 100644 --- a/mentos/inc/mem/kheap.h +++ b/mentos/inc/mem/kheap.h @@ -2,43 +2,41 @@ /// @file kheap.h /// @brief Interface for kernel heap functions, also provides a placement /// malloc() for use before the heap is initialised. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "kernel.h" -#include "scheduler.h" +#include "process/scheduler.h" -//#define KHEAP_START (void*)(LOAD_MEMORY_ADDRESS + 0x00800000) // 0x00800000 = 8M -//#define KHEAP_MAX_ADDRESS (void*)(LOAD_MEMORY_ADDRESS + 0x0FFFFFFF) // 0x10000000 = 256 M -// M = 1024 * 1024 = 1MB -#define KHEAP_INITIAL_SIZE (4 * M) -#define UHEAP_INITIAL_SIZE (1 * M) - -/** - * @brief Structure of block_t - * size: | 31 bit | 1 bit | - * | first bits of real size | free/alloc | - * To calculate the real size, set to zero the last bit - * prev: -> block_t - * next: -> block_t - **/ -typedef struct block_t { - unsigned int size; - struct block_t *nextfree; - struct block_t *next; -} block_t; - -/// @brief Initialize heap. +/// @brief Initialize heap. /// @param initial_size Starting size. void kheap_init(size_t initial_size); -/// @brief Returns if the kheap is enabled. -bool_t kheap_is_enabled(); - +/// @brief Increase the heap of the kernel. +/// @param increment The amount to increment. +/// @return Pointer to a ready-to-used memory area. void *ksbrk(int increment); +/// @brief Increase the heap of the current process. +/// @param increment The amount to increment. +/// @return Pointer to a ready-to-used memory area. +void *usbrk(int increment); + +/// @brief User malloc. +/// @param addr This argument is treated as an address of a dynamically +/// allocated memory if falls inside the process heap area. +/// Otherwise, it is treated as an amount of memory that +/// should be allocated. +/// @return NULL if there is no more memory available or we were freeing +/// a previously allocated memory area, the address of the +/// allocated space otherwise. +void *sys_brk(void *addr); + +/// @brief Prints the heap visually, for debug. +void kheap_dump(); + #if 0 /// @brief Kmalloc wrapper. /// @details When heap is not created, use a placement memory allocator, when @@ -47,7 +45,7 @@ void *ksbrk(int increment); /// @param align Return a page-aligned memory block. /// @param phys Return the physical address of the memory block. /// @return -uint32_t kmalloc_int(size_t size, bool_t align, uint32_t *phys); +uint32_t kmalloc_int(size_t size, int align, uint32_t *phys); /// @brief Wrapper for kmalloc, get physical address. /// @param sz Size of memory to allocate. @@ -60,51 +58,38 @@ void *kmalloc_p(unsigned int sz, unsigned int *phys); /// @param phys Pointer to the variable where to place the physical address. /// @return void *kmalloc_ap(unsigned int sz, unsigned int *phys); -#endif /// @brief Dynamically allocates memory of the given size. /// @param sz Size of memory to allocate. /// @return -void *kmalloc(unsigned int sz); +//void *kmalloc(unsigned int sz); /// @brief Wrapper for aligned kmalloc. /// @param size Size of memory to allocate. /// @return -void *kmalloc_align(size_t size); +//void *kmalloc_align(size_t size); /// @brief Dynamically allocates memory for (size * num) and memset it. /// @param num Multiplier. /// @param size Size of memory to allocate. /// @return -void *kcalloc(unsigned int num, unsigned int size); +//void *kcalloc(unsigned int num, unsigned int size); /// @brief Wrapper function for realloc. /// @param ptr /// @param size /// @return -void *krealloc(void *ptr, unsigned int size); +//void *krealloc(void *ptr, unsigned int size); /// @brief Wrapper function for free. /// @param p -void kfree(void *p); +//void kfree(void *p); /// @brief Allocates size bytes of uninitialized storage. //void *malloc(unsigned int size); -// We can't do a user heap brk if we don't enable paging... -void *usbrk(int increment); - -/// @brief User malloc -/// @param size The size requested in byte. -/// @return The address of the allocated space. -void *umalloc(unsigned int size); - -/// @brief User free -/// @param p Pointer to the space to free. -void ufree(void *p); - /// @brief Allocates size bytes of uninitialized storage with block align. -//void *malloc_align(struct vm_area_struct *heap, unsigned int size); +//void *malloc_align(vm_area_struct_t *heap, unsigned int size); /// @brief Deallocates previously allocated space. //void free(void *ptr); @@ -116,13 +101,4 @@ void ufree(void *p); /// @return //void *realloc(void *ptr, unsigned int size); -/// @brief Prints the heap visually, for debug. -void kheap_dump(); - -/// @brief Get the pointer to the kernel heap start. -/// @return The kernel heap start pointer. -uint32_t get_kheap_start(); - -/// @brief Get the pointer to the kernel heap curr. -/// @return The kernel heap current pointer. -uint32_t get_kheap_curr(); +#endif \ No newline at end of file diff --git a/mentos/inc/mem/paging.h b/mentos/inc/mem/paging.h index d8e12b9..1298602 100644 --- a/mentos/inc/mem/paging.h +++ b/mentos/inc/mem/paging.h @@ -1,106 +1,255 @@ /// MentOS, The Mentoring Operating system project /// @file paging.h /// @brief Implementation of a memory paging management. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "zone_allocator.h" +#include "mem/zone_allocator.h" +#include "proc_access.h" #include "kernel.h" #include "stddef.h" +#include "boot.h" /// Size of a page. -#define PAGE_SIZE 4096 +#define PAGE_SIZE 4096U +/// The start of the process area. +#define PROCAREA_START_ADDR 0x00000000 +/// The end of the process area (and start of the kernel area). +#define PROCAREA_END_ADDR 0xC0000000 -/// @brief Virtual Memory Area. -/// Process segments. -struct vm_area_struct { - /// Memory descriptor associated. - struct mm_struct *vm_mm; +/// @brief An entry of a page directory. +typedef struct page_dir_entry_t { + unsigned int present : 1; ///< TODO: Comment. + unsigned int rw : 1; ///< TODO: Comment. + unsigned int user : 1; ///< TODO: Comment. + unsigned int w_through : 1; ///< TODO: Comment. + unsigned int cache : 1; ///< TODO: Comment. + unsigned int accessed : 1; ///< TODO: Comment. + unsigned int reserved : 1; ///< TODO: Comment. + unsigned int page_size : 1; ///< TODO: Comment. + unsigned int global : 1; ///< TODO: Comment. + unsigned int available : 3; ///< TODO: Comment. + unsigned int frame : 20; ///< TODO: Comment. +} page_dir_entry_t; - /// Start address of the segment. - uint32_t vm_start; +/// @brief An entry of a page table. +typedef struct page_table_entry_t { + unsigned int present : 1; ///< TODO: Comment. + unsigned int rw : 1; ///< TODO: Comment. + unsigned int user : 1; ///< TODO: Comment. + unsigned int w_through : 1; ///< TODO: Comment. + unsigned int cache : 1; ///< TODO: Comment. + unsigned int accessed : 1; ///< TODO: Comment. + unsigned int dirty : 1; ///< TODO: Comment. + unsigned int zero : 1; ///< TODO: Comment. + unsigned int global : 1; ///< TODO: Comment. + unsigned int kernel_cow : 1; ///< TODO: Comment. + unsigned int available : 2; ///< TODO: Comment. + unsigned int frame : 20; ///< TODO: Comment. +} page_table_entry_t; - /// End address of the segment. - uint32_t vm_end; - - /// Next memory area of the list. - struct vm_area_struct *vm_next; - - /// Permissions. - pgprot_t vm_page_prot; - - /// Flags. - unsigned short vm_flags; - - /// rbtree node. - // struct rb_node vm_rb; +/// @brief Flags associated with virtual memory areas. +enum MEMMAP_FLAGS { + MM_USER = 0x1, ///< Area belongs to user. + MM_GLOBAL = 0x2, ///< Area is global. + MM_RW = 0x4, ///< Area has user read/write perm. + MM_PRESENT = 0x8, ///< Area is valid. + // Kernel flags + MM_COW = 0x10, ///< Area is copy on write. + MM_UPDADDR = 0x20, ///< Check? }; -/// @brief Memory Descriptor. -/// Memory description of user process. -struct mm_struct { - /// List of memory area. - struct vm_area_struct *mmap; - // /// rbtree of memory area. - // struct rb_root mm_rb; +/// @brief A page table. +/// @details +/// It contains 1024 entries which can be addressed by 10 bits (log_2(1024)). +typedef struct page_table_t { + page_table_entry_t pages[1024]; ///< Array of pages. +} __attribute__((aligned(PAGE_SIZE))) page_table_t; - /// Last memory area used. - struct vm_area_struct *mmap_cache; +/// @brief A page directory. +/// @details In the two-level paging, this is the first level. +typedef struct page_directory_t { + /// We need a table that contains virtual address, so that we can + /// actually get to the tables (size: 1024 * 4 = 4096 byte). + page_dir_entry_t entries[1024]; +} __attribute__((aligned(PAGE_SIZE))) page_directory_t; - // ///< Process page directory. - // page_directory_t * pgd; +/// @brief Virtual Memory Area, used to store details of a process segment. +typedef struct vm_area_struct_t { + /// Memory descriptor associated. + struct mm_struct_t *vm_mm; + /// Start address of the segment, inclusive. + uint32_t vm_start; + /// End address of the segment, exclusive. + uint32_t vm_end; + /// List of memory areas. + list_head vm_list; + /// Permissions. + pgprot_t vm_page_prot; + /// Flags. + unsigned short vm_flags; + /// rbtree node. + // struct rb_node vm_rb; +} vm_area_struct_t; - /// Number of memory area. - int map_count; +/// @brief Memory Descriptor, used to store details about the memory of a user process. +typedef struct mm_struct_t { + /// List of memory area (vm_area_struct reference). + list_head mmap_list; + // /// rbtree of memory area. + // struct rb_root mm_rb; + /// Last memory area used. + vm_area_struct_t *mmap_cache; + /// Process page directory. + page_directory_t *pgd; + /// Number of memory area. + int map_count; + /// List of mm_struct. + list_head mm_list; + /// CODE start. + uint32_t start_code; + /// CODE end. + uint32_t end_code; + /// DATA start. + uint32_t start_data; + /// DATA end. + uint32_t end_data; + /// HEAP start. + uint32_t start_brk; + /// HEAP end. + uint32_t brk; + /// STACK start. + uint32_t start_stack; + /// ARGS start. + uint32_t arg_start; + /// ARGS end. + uint32_t arg_end; + /// ENVIRONMENT start. + uint32_t env_start; + /// ENVIRONMENT end. + uint32_t env_end; + /// Number of mapped pages. + unsigned int total_vm; +} mm_struct_t; - /// List of mm_struct. - list_head mmlist; +/// @brief Cache used to store page tables. +extern kmem_cache_t *pgtbl_cache; - /// CODE. - uint32_t start_code, end_code; +/// @brief Initializes paging +/// @param info Information coming from bootloader. +void paging_init(boot_info_t *info); - /// DATA. - uint32_t start_data, end_data; +/// @brief Provide access to the main page directory. +/// @return A pointer to the main page directory. +page_directory_t *paging_get_main_directory(); - /// HEAP. - uint32_t start_brk, brk; +/// @brief Provide access to the current paging directory. +/// @return A pointer to the current page directory. +static inline page_directory_t *paging_get_current_directory() +{ + return (page_directory_t *)get_cr3(); +} - /// STACK. - uint32_t start_stack; +/// @brief Switches paging directory, the pointer must be a physical address. +/// @param dir A pointer to the new page directory. +static inline void paging_switch_directory(page_directory_t *dir) +{ + set_cr3((uintptr_t)dir); +} - /// ARGS. - uint32_t arg_start, arg_end; +/// @brief Switches paging directory, the pointer can be a lowmem address. +/// @param dir A pointer to the new page directory. +void paging_switch_directory_va(page_directory_t *dir); - /// ENVIRONMENT. - uint32_t env_start, env_end; +/// @brief Invalidate a single tlb page (the one that maps the specified virtual address) +/// @param addr The address of the page table. +void paging_flush_tlb_single(unsigned long addr); - /// Number of mapped pages. - unsigned int total_vm; -}; +/// @brief Enables paging. +static inline void paging_enable() +{ + // Clear the PSE bit from cr4. + set_cr4(bitmask_clear(get_cr4(), CR4_PSE)); + // Set the PG bit in cr0. + set_cr0(bitmask_set(get_cr0(), CR0_PG)); +} /// @brief Returns if paging is enabled. -bool_t paging_is_enabled(); +/// @return 1 if paging is enables, 0 otherwise. +static inline int paging_is_enabled() +{ + return bitmask_check(get_cr0(), CR0_PG); +} -/// @brief Create a Memory Descriptor. +/// @brief Handles a page fault. +/// @param f The interrupt stack frame. +void page_fault_handler(pt_regs *f); + +/// @brief Gets a page from a virtual address +/// @param pgdir The target page directory. +/// @param virt_start The virtual address to query +/// @param size A pointer to the requested size of the data, size is updated if physical memory is not contiguous +/// @return Pointer to the page. +page_t *mem_virtual_to_page(page_directory_t *pgdir, uint32_t virt_start, size_t *size); + +/// @brief Creates a virtual to physical mapping, incrementing pages usage counters. +/// @param pgd The target page directory. +/// @param virt_start The virtual address to map to. +/// @param phy_start The physical address to map. +/// @param size The size of the segment. +/// @param flags The flags for the memory range. +void mem_upd_vm_area(page_directory_t *pgd, uint32_t virt_start, uint32_t phy_start, size_t size, uint32_t flags); + +/// @brief Clones a range of pages between two distinct page tables +/// @param src_pgd The source page directory. +/// @param dst_pgd The dest page directory. +/// @param src_start The source virtual address for the clone. +/// @param dst_start The destination virtual address for the clone. +/// @param size The size of the segment. +/// @param flags The flags for the new dst memory range. +void mem_clone_vm_area(page_directory_t *src_pgd, + page_directory_t *dst_pgd, + uint32_t src_start, + uint32_t dst_start, + size_t size, + uint32_t flags); + +/// @brief Create a virtual memory area. +/// @param mm The memory descriptor which will contain the new segment. +/// @param virt_start The virtual address to map to. +/// @param size The size of the segment. +/// @param pgflags The flags for the new memory area. +/// @param gfpflags The Get Free Pages flags. +/// @return The virtual address of the starting point of the segment. +uint32_t create_vm_area(mm_struct_t *mm, + uint32_t virt_start, + size_t size, + uint32_t pgflags, + uint32_t gfpflags); + +/// @brief Clone a virtual memory area, using copy on write if specified +/// @param mm The memory descriptor which will contain the new segment. +/// @param area The area to clone +/// @param cow Whether to use copy-on-write or just copy everything. +/// @param gfpflags The Get Free Pages flags. +/// @return Zero on success. +uint32_t clone_vm_area(mm_struct_t *mm, + vm_area_struct_t *area, + int cow, + uint32_t gfpflags); + +/// @brief Creates the main memory descriptor. /// @param stack_size The size of the stack in byte. -/// @return The Memory Descriptor created. -struct mm_struct *create_process_image(size_t stack_size); +/// @return The Memory Descriptor created. +mm_struct_t *create_blank_process_image(size_t stack_size); -/// @brief Create a virtual memory area. -/// @param mm The memory descriptor which will contain the new segment. -/// @param size The size of the segment. -/// @return The virtual address of the starting point of the segment. -uint32_t create_segment(struct mm_struct *mm, size_t size); +/// @brief Create a Memory Descriptor. +/// @param mmp The memory map to clone +/// @return The Memory Descriptor created. +mm_struct_t *clone_process_image(mm_struct_t *mmp); -/// @brief Free Memory Descriptor with all the memory segment contained. +/// @brief Free Memory Descriptor with all the memory segment contained. /// @param mm The Memory Descriptor to free. -void destroy_process_image(struct mm_struct *mm); - -/* - * /// @brief Free a virtual memory area. - * /// @param mm The Memory Descriptor that contains the segment. - * /// @param segment The segment to free. - * void destroy_segment(struct mm_struct *mm, struct vm_area_struct *segment); - */ +void destroy_process_image(mm_struct_t *mm); diff --git a/mentos/inc/mem/slab.h b/mentos/inc/mem/slab.h new file mode 100644 index 0000000..5d99ea1 --- /dev/null +++ b/mentos/inc/mem/slab.h @@ -0,0 +1,169 @@ +/// MentOS, The Mentoring Operating system project +/// @file slab.h +/// @brief Functions and structures for managing memory slabs. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "klib/list_head.h" +#include "stddef.h" +#include "mem/gfp.h" + +//#define ENABLE_CACHE_TRACE +//#define ENABLE_ALLOC_TRACE + +/// @brief Type for slab flags. +typedef unsigned int slab_flags_t; + +/// Create a new cache. +#define KMEM_CREATE(objtype) kmem_cache_create(#objtype, \ + sizeof(objtype), \ + alignof(objtype), \ + GFP_KERNEL, \ + NULL, \ + NULL) + +/// Creates a new cache and allows to specify the constructor. +#define KMEM_CREATE_CTOR(objtype, ctor) kmem_cache_create(#objtype, \ + sizeof(objtype), \ + alignof(objtype), \ + GFP_KERNEL, \ + ((void (*)(void *))(ctor)), \ + NULL) + +/// @brief Stores the information of a cache. +typedef struct kmem_cache_t { + /// Handler for placing it inside a lists of caches. + list_head cache_list; + /// Name of the cache. + const char *name; + /// Size of the cache. + unsigned int size; + /// Size of the objects contained in the cache. + unsigned int object_size; + /// Alignment requirement of the type of objects. + unsigned int align; + /// The total number of slabs. + unsigned int total_num; + /// The number of free slabs. + unsigned int free_num; + /// The Get Free Pages (GFP) flags. + slab_flags_t flags; + /// The order for getting free pages. + unsigned int gfp_order; + /// Constructor for the elements. + void (*ctor)(void *); + /// Destructor for the elements. + void (*dtor)(void *); + /// Handler for the full slabs list. + list_head slabs_full; + /// Handler for the partial slabs list. + list_head slabs_partial; + /// Handler for the free slabs list. + list_head slabs_free; +} kmem_cache_t; + +/// Initialize the slab system +void kmem_cache_init(); + +/// @brief Creates a new slab cache. +/// @param name Name of the cache. +/// @param size Size of the objects contained inside the cache. +/// @param align Memory alignment for the objects inside the cache. +/// @param flags Flags used to define the properties of the cache. +/// @param ctor Constructor for initializing the cache elements. +/// @param dtor Destructor for finalizing the cache elements. +/// @return Pointer to the object used to manage the cache. +kmem_cache_t *kmem_cache_create( + const char *name, + unsigned int size, + unsigned int align, + slab_flags_t flags, + void (*ctor)(void *), + void (*dtor)(void *)); + +/// @brief Deletes the given cache. +/// @param cachep Pointer to the cache. +void kmem_cache_destroy(kmem_cache_t *cachep); + +#ifdef ENABLE_CACHE_TRACE + +#ifndef __FILENAME__ +#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__) +#endif + +/// @brief Allocs a new object using the provided cache. +/// @param file File where the object is allocated. +/// @param fun Function where the object is allocated. +/// @param line Line inside the file. +/// @param cachep The cache used to allocate the object. +/// @param flags Flags used to define where we are going to Get Free Pages (GFP). +/// @return Pointer to the allocated space. +void *pr_kmem_cache_alloc(const char *file, const char *fun, int line, kmem_cache_t *cachep, gfp_t flags); + +/// @brief Frees an cache allocated object. +/// @param file File where the object is deallocated. +/// @param fun Function where the object is deallocated. +/// @param line Line inside the file. +/// @param addr Address of the object. +void pr_kmem_cache_free(const char *file, const char *fun, int line, void *addr); + +/// Wrapper that provides the filename, the function and line where the alloc is happening. +#define kmem_cache_alloc(...) pr_kmem_cache_alloc(__FILENAME__, __func__, __LINE__, __VA_ARGS__) + +/// Wrapper that provides the filename, the function and line where the free is happening. +#define kmem_cache_free(...) pr_kmem_cache_free(__FILENAME__, __func__, __LINE__, __VA_ARGS__) + +#else +/// @brief Allocs a new object using the provided cache. +/// @param cachep The cache used to allocate the object. +/// @param flags Flags used to define where we are going to Get Free Pages (GFP). +/// @return Pointer to the allocated space. +void *kmem_cache_alloc(kmem_cache_t *cachep, gfp_t flags); + +/// @brief Frees an cache allocated object. +/// @param addr Address of the object. +void kmem_cache_free(void *addr); + +#endif + +#ifdef ENABLE_ALLOC_TRACE + +#ifndef __FILENAME__ +#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__) +#endif + +/// @brief Provides dynamically allocated memory in kernel space. +/// @param file File where the object is allocated. +/// @param fun Function where the object is allocated. +/// @param line Line inside the file. +/// @param size The amount of memory to allocate. +/// @return A pointer to the allocated memory. +void *pr_kmalloc(const char *file, const char *fun, int line, unsigned int size); + +/// @brief Frees dynamically allocated memory in kernel space. +/// @param file File where the object is deallocated. +/// @param fun Function where the object is deallocated. +/// @param line Line inside the file. +/// @param ptr The pointer to the allocated memory. +void pr_kfree(const char *file, const char *fun, int line, void *addr); + +/// Wrapper that provides the filename, the function and line where the alloc is happening. +#define kmalloc(...) pr_kmalloc(__FILENAME__, __func__, __LINE__, __VA_ARGS__) + +/// Wrapper that provides the filename, the function and line where the free is happening. +#define kfree(...) pr_kfree(__FILENAME__, __func__, __LINE__, __VA_ARGS__) + +#else + +/// @brief Provides dynamically allocated memory in kernel space. +/// @param size The amount of memory to allocate. +/// @return A pointer to the allocated memory. +void *kmalloc(unsigned int size); + +/// @brief Frees dynamically allocated memory in kernel space. +/// @param ptr The pointer to the allocated memory. +void kfree(void *ptr); + +#endif diff --git a/mentos/inc/mem/vmem_map.h b/mentos/inc/mem/vmem_map.h new file mode 100644 index 0000000..b8e65b6 --- /dev/null +++ b/mentos/inc/mem/vmem_map.h @@ -0,0 +1,77 @@ +/// MentOS, The Mentoring Operating system project +/// @file vmem_map.h +/// @brief Virtual memory mapping routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "mem/buddysystem.h" +#include "mem/zone_allocator.h" +#include "mem/paging.h" + +/// Size of the virtual memory. +#define VIRTUAL_MEMORY_SIZE_MB 128 + +/// @brief Virtual mapping manager. +typedef struct virt_map_page_manager_t { + /// The buddy system used to manage the pages. + bb_instance_t bb_instance; +} virt_map_page_manager_t; + +/// @brief Virtual mapping. +typedef struct virt_map_page_t { + /// A buddy system page. + bb_page_t bbpage; +} virt_map_page_t; + +/// @brief Initialize the virtual memory mapper +void virt_init(void); + +/// @brief Map a page range to virtual memory +/// @param page The start page of the mapping +/// @param pfn_count The number of pages to map +/// @return The virtual address of the mapping +uint32_t virt_map_physical_pages(page_t *page, int pfn_count); + +/// @brief Allocate a virtual page range of the specified size. +/// @param size The required amount. +/// @return Pointer to the allocated memory. +virt_map_page_t *virt_map_alloc(uint32_t size); + +/// @brief Map a page to a memory area portion. +/// @param mm The memory descriptor. +/// @param vpage Pointer to the virtual page. +/// @param vaddr The starting address of the are. +/// @param size The size of the area. +/// @return Address of the mapped area. +uint32_t virt_map_vaddress(mm_struct_t *mm, + virt_map_page_t *vpage, + uint32_t vaddr, + uint32_t size); + +/// @brief Checks if an address belongs to the virtual memory mapping. +/// @param addr The address to check. +/// @return 1 if it belongs to the virtual memory mapping, 0 otherwise. +int virtual_check_address(uint32_t addr); + +/// @brief Unmap an address. +/// @param addr The address to unmap. +void virt_unmap(uint32_t addr); + +/// @brief Unmap a page. +/// @param page Pointer to the page to unmap. +void virt_unmap_pg(virt_map_page_t *page); + +/// @brief Memcpy from different processes virtual addresses +/// @param dst_mm The destination memory struct +/// @param dst_vaddr The destination memory address +/// @param src_mm The source memory struct +/// @param src_vaddr The source memory address +/// @param size The size in bytes of the copy +void virt_memcpy( + mm_struct_t *dst_mm, + uint32_t dst_vaddr, + mm_struct_t *src_mm, + uint32_t src_vaddr, + uint32_t size); diff --git a/mentos/inc/mem/zone_allocator.h b/mentos/inc/mem/zone_allocator.h index c2cd4f0..8fb57a5 100644 --- a/mentos/inc/mem/zone_allocator.h +++ b/mentos/inc/mem/zone_allocator.h @@ -1,45 +1,58 @@ /// MentOS, The Mentoring Operating system project /// @file zone_allocator.h /// @brief Implementation of the Zone Allocator -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "gfp.h" +#include "mem/gfp.h" #include "math.h" #include "stdint.h" -#include "stdbool.h" -#include "list_head.h" +#include "klib/list_head.h" +#include "sys/bitops.h" +#include "klib/stdatomic.h" +#include "boot.h" +#include "mem/buddysystem.h" +#include "mem/slab.h" -/// Max order of buddysystem blocks. -#define MAX_ORDER 11 - -/// @brief Buddy system descriptor: collection of free page blocks. -/// Each block represents 2^k free contiguous page. -typedef struct { - /// free_list collectes the first page descriptors of a blocks of 2^k frames - list_head free_list; - /// nr_free specifies the number of blocks of free pages. - int nr_free; -} free_area_t; +#define page_count(p) atomic_read(&(p)->count) ///< Reads the page count. +#define set_page_count(p, v) atomic_set(&(p)->count, v) ///< Sets the page count. +#define page_inc(p) atomic_inc(&(p)->count) ///< Increments the counter for the given page. +#define page_dec(p) atomic_dec(&(p)->count) ///< Decrements the counter for the given page. /// @brief Page descriptor. Use as a bitmap to understand /// the order of the block and if it is free or allocated. -typedef struct { - /// Array of flags encoding also the zone number to which the page frame belongs. - unsigned long flags; - /// Page frame’s reference counter. -1 free, >= 0 used - int _count; - /// If the page is free, this field is used by the buddy system. - unsigned long private; - /// Contains pointers to the least recently used doubly linked list of pages. - struct list_head lru; +typedef struct page_t { + /// Array of flags encoding also the zone number to which the page frame belongs. + unsigned long flags; + /// Page frame’s reference counter. 0 free, 1 used, 2+ copy on write + atomic_t count; + /// Buddy system page definition + bb_page_t bbpage; + /// Contains pointers to the slabs doubly linked list of pages. + list_head slabs; + + // Slab allocator variables + /// Contains the total number of objects in this page, 0 if not managed by the slub + unsigned int slab_objcnt; + /// Tracks the number of free objects in the current page + unsigned int slab_objfree; + /// Holds the first free object (if slab_objfree is > 0) + list_head slab_freelist; + /// @brief This union can either contain the pointer to the slab main page + /// that handles this page, or the cache that contains it. + union { + /// Holds the slab page used to handle this memory region (root page) + struct page_t *slab_main_page; + /// Holds the slab cache pointer on the main page + kmem_cache_t *slab_cache; + } container; } page_t; /// @brief Enumeration for zone_t. enum zone_type { - /* + /* * ZONE_DMA is used when there are devices that are not able * to do DMA to all of addressable memory (ZONE_NORMAL). Then we * carve out the portion of memory that is needed for these devices. @@ -58,18 +71,18 @@ enum zone_type { * <16M. */ - // ///< Direct memory access. - // ZONE_DMA, + // /// Direct memory access. + //ZONE_DMA, - /* + /* * Normal addressable memory is in ZONE_NORMAL. DMA operations can be * performed on pages in ZONE_NORMAL if the DMA devices support * transfers to all addressable memory. */ - /// Direct mapping. Used by the kernel. - ZONE_NORMAL, + /// Direct mapping. Used by the kernel. + ZONE_NORMAL, - /* + /* * A memory area that is only addressable by the kernel through * mapping portions into its own address space. This is for example * used by i386 to allow the kernel to address the memory beyond @@ -77,87 +90,153 @@ enum zone_type { * table entries on i386) for each page that the kernel needs to * access. */ - /// Page tables mapping. Used by user processes. - ZONE_HIGHMEM, + /// Page tables mapping. Used by user processes. + ZONE_HIGHMEM, - __MAX_NR_ZONES + /// The maximum number of zones. + __MAX_NR_ZONES }; /// @brief Data structure to differentiate memory zone. -typedef struct { - /// Number of free pages in the zone. - unsigned long free_pages; - /// BuddySystem structure for the zone. - free_area_t free_area[MAX_ORDER]; - /// Pointer to first page descriptor of the zone. - page_t *zone_mem_map; - /// Index of the first page frame of the zone. - uint32_t zone_start_pfn; - /// Zone's name. - char *name; - /// Zone's size in number of pages. - unsigned long size; +typedef struct zone_t { + /// Number of free pages in the zone. + unsigned long free_pages; + /// Buddy system managing this zone + bb_instance_t buddy_system; + /// Pointer to first page descriptor of the zone. + page_t *zone_mem_map; + /// Index of the first page frame of the zone. + uint32_t zone_start_pfn; + /// Zone's name. + char *name; + /// Zone's size in number of pages. + unsigned long size; } zone_t; /// @brief Data structure to rapresent a memory node. /// In UMA Architecture there is only one node called /// contig_page_data. -typedef struct { - /// Zones of the node. - zone_t node_zones[__MAX_NR_ZONES]; - /// Number of zones in the node. - int nr_zones; - /// Array of pages of the node. - page_t *node_mem_map; - /// Physical address of the first page of the node. - unsigned long node_start_paddr; - /// Index on global mem_map for node_mem_map. - unsigned long node_start_mapnr; - /// Node's size in number of pages. - unsigned long node_size; - /// NID. - int node_id; - /// Pointer to the next node. - struct pglist_data *node_next; +typedef struct pg_data_t { + /// Zones of the node. + zone_t node_zones[__MAX_NR_ZONES]; + /// Number of zones in the node. + int nr_zones; + /// Array of pages of the node. + page_t *node_mem_map; + /// Physical address of the first page of the node. + unsigned long node_start_paddr; + /// Index on global mem_map for node_mem_map. + unsigned long node_start_mapnr; + /// Node's size in number of pages. + unsigned long node_size; + /// NID. + int node_id; + /// Next item in the memory node list. + struct pg_data_t *node_next; } pg_data_t; extern page_t *mem_map; extern pg_data_t *contig_page_data; -/// @brief Find the nearest block's order of size greater -/// than the amount of byte. -/// @param amount The amount of byte which we want to calculate order. -/// @return The block's order greater and nearest than amount. -uint32_t find_nearest_order_greater(uint32_t amount); +/// @brief Find the nearest block's order of size greater than the +/// amount of byte. +/// @param base_addr The start address, used to handle extra page +/// calculation in case of not page aligned addresses. +/// @param amount The amount of byte which we want to calculate order. +/// @return The block's order greater and nearest than amount. +uint32_t find_nearest_order_greater(uint32_t base_addr, uint32_t amount); -/// @brief Physical memory manager initialization (page_t, zones) -/// @param mem_size Size of the memory. -bool_t pmmngr_init(uint32_t mem_size); +/// @brief Physical memory manager initialization. +/// @param boot_info Information coming from the booloader. +/// @return Outcome of the operation. +int pmmngr_init(boot_info_t *boot_info); -/// @brief Find the first free page frame, set it allocated -/// and return the memory address of the page frame. -/// @param gfp_mask GFP_FLAGS to decide the zone allocation. -/// @return Memory address of the first free block. -uint32_t __alloc_page(gfp_t gfp_mask); +/// @brief Alloc a single cached page. +/// @param gfp_mask The GetFreePage mask. +/// @return Pointer to the page. +page_t *alloc_page_cached(gfp_t gfp_mask); -/// @brief Frees the given page frame address. +/// @brief Free a page allocated with alloc_page_cached. +/// @param page Pointer to the page to free. +void free_page_cached(page_t *page); + +/// @brief Find the first free page frame, set it allocated and return the +/// memory address of the page frame. +/// @param gfp_mask GFP_FLAGS to decide the zone allocation. +/// @return Memory address of the first free block. +uint32_t __alloc_page_lowmem(gfp_t gfp_mask); + +/// @brief Frees the given page frame address. /// @param addr The block address. -void free_page(uint32_t addr); +void free_page_lowmem(uint32_t addr); -/// @brief Find the first free 2^order amount of page frames, -/// set it allocated and return the memory address of the first -/// page frame allocated. +/// @brief Find the first free 2^order amount of page frames, set it allocated +/// and return the memory address of the first page frame allocated. /// @param gfp_mask GFP_FLAGS to decide the zone allocation. /// @param order The logarithm of the size of the page frame. -/// @return Memory address of the first free page frame allocated. -uint32_t __alloc_pages(gfp_t gfp_mask, uint32_t order); +/// @return Memory address of the first free page frame allocated. +uint32_t __alloc_pages_lowmem(gfp_t gfp_mask, uint32_t order); -/// @brief Frees from the given page frame address up to -/// 2^order amount of page frames. -/// @param addr The page frame address. -/// @param order The logarithm of the size of the block. -void free_pages(uint32_t addr, uint32_t order); +/// @brief Find the first free 2^order amount of page frames, set it allocated +/// and return the memory address of the first page frame allocated. +/// @param gfp_mask GFP_FLAGS to decide the zone allocation. +/// @param order The logarithm of the size of the page frame. +/// @return Memory address of the first free page frame allocated. +page_t *_alloc_pages(gfp_t gfp_mask, uint32_t order); -/// @brief Get the pointer of the last byte allocated with pmmngr_init() -/// @return The pointer to the memory. -uint32_t get_memory_start(); +/// @brief Get the start address of the corresponding page. +/// @param page A page structure. +/// @return The address that corresponds to the page. +uint32_t get_lowmem_address_from_page(page_t *page); + +/// @brief Get the start physical address of the corresponding page. +/// @param page A page structure +/// @return The physical address that corresponds to the page. +uint32_t get_physical_address_from_page(page_t *page); + +/// @brief Get the page from it's physical address. +/// @param phy_addr The physical address +/// @return The page that corresponds to the physical address. +page_t *get_page_from_physical_address(uint32_t phy_addr); + +/// @brief Get the page that contains the specified address. +/// @param addr A phisical address. +/// @return The page that corresponds to the address. +page_t *get_lowmem_page_from_address(uint32_t addr); + +/// @brief Frees from the given page frame address up to 2^order amount +/// of page frames. +/// @param addr The page frame address. +void free_pages_lowmem(uint32_t addr); + +/// @brief Frees from the given page frame address up to 2^order amount +/// of page frames. +/// @param page The page. +void __free_pages(page_t *page); + +/// @brief Returns the total space for the given zone. +/// @param gfp_mask GFP_FLAGS to decide the zone. +/// @return Total space of the given zone. +unsigned long get_zone_total_space(gfp_t gfp_mask); + +/// @brief Returns the total free space for the given zone. +/// @param gfp_mask GFP_FLAGS to decide the zone. +/// @return Total free space of the given zone. +unsigned long get_zone_free_space(gfp_t gfp_mask); + +/// @brief Returns the total cached space for the given zone. +/// @param gfp_mask GFP_FLAGS to decide the zone. +/// @return Total cached space of the given zone. +unsigned long get_zone_cached_space(gfp_t gfp_mask); + +/// @brief Checks if the specified address points to a page_t (or field) +/// that belongs to lowmem. +/// @param addr The address to check. +/// @return 1 if it belongs to lowmem, 0 otherwise. +static inline int is_lowmem_page_struct(void *addr) +{ + uint32_t start_lowm_map = (uint32_t)contig_page_data->node_zones[ZONE_NORMAL].zone_mem_map; + uint32_t lowmem_map_size = sizeof(page_t) * contig_page_data->node_zones[ZONE_NORMAL].size; + uint32_t map_index = (uint32_t)addr - start_lowm_map; + return map_index < lowmem_map_size; +} diff --git a/mentos/inc/misc/bitops.h b/mentos/inc/misc/bitops.h deleted file mode 100644 index 3268375..0000000 --- a/mentos/inc/misc/bitops.h +++ /dev/null @@ -1,30 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file bitops.h -/// @brief Bitmasks functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stdint.h" -#include "stdbool.h" - -/// @brief Finds the first bit set in the given irq mask. -int find_first_bit(unsigned short int irq_mask); - -/// @brief Check if the passed value has the given flag set. -/// @param flags The value to check. -/// @param flag The flag to search. -/// @return True If the value contain the flag,
-/// False otherwise. -bool_t has_flag(uint32_t flags, uint32_t flag); - -/// @brief Set the passed flag to the value. -/// @param flags The destination value. -/// @param flag The flag to set. -void set_flag(uint32_t *flags, uint32_t flag); - -/// @brief Clear the passed flag to the value. -/// @param flags The destination value. -/// @param flag The flag to clear. -void clear_flag(uint32_t *flags, uint32_t flag); diff --git a/mentos/inc/misc/clock.h b/mentos/inc/misc/clock.h deleted file mode 100644 index d7e7b64..0000000 --- a/mentos/inc/misc/clock.h +++ /dev/null @@ -1,98 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file clock.h -/// @brief Clock functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -/// Used to store time values. -typedef unsigned int time_t; - -/// Used to get information about the current time. -typedef struct tm_t { - /// Seconds [0 to 59] - int tm_sec; - /// Minutes [0 to 59] - int tm_min; - /// Hours [0 to 23] - int tm_hour; - /// Day of the month [1 to 31] - int tm_mday; - /// Month [0 to 11] - int tm_mon; - /// Year [since 1900] - int tm_year; - /// Day of the week [0 to 6] - int tm_wday; - /// Day in the year [0 to 365] - int tm_yday; - /// Is Daylight Saving Time. - int tm_isdst; -} tm_t; - -/// Value used to retrieve the seconds. -#define SECOND_RTC 0x00 - -/// Value used to retrieve the minutes. -#define MINUTE_RTC 0x02 - -/// Value used to retrieve the hours. -#define HOUR_RTC 0x04 - -/// Value used to retrieve the day of the week. -#define DAY_W_RTC 0x06 - -/// Value used to retrieve the day of the month. -#define DAY_M_RTC 0x07 - -/// Value used to retrieve the month. -#define MONTH_RTC 0x08 - -/// Value used to retrieve the year. -#define YEAR_RTC 0x09 - -/// @brief Returns the milliseconds. -time_t get_millisecond(); - -/// @brief Returns the seconds. -time_t get_second(); - -/// @brief Returns the hours. -time_t get_hour(); - -/// @brief Returns the minutes. -time_t get_minute(); - -/// @brief Returns the day of the month. -time_t get_day_m(); - -/// @brief returns the month. -time_t get_month(); - -/// @brief Returns the year. -time_t get_year(); - -/// @brief Returns the day of the week. -time_t get_day_w(); - -/// @brief Returns the name of the month. -char *get_month_lng(); - -/// @brief Returns the name of the day. -char *get_day_lng(); - -/// @brief Returns the current time. -time_t time(time_t *timer); - -// TODO: doxygen comment. -time_t difftime(time_t time1, time_t time2); - -// TODO: doxygen comment. -void strhourminutesecond(char *dst); - -// TODO: doxygen comment. -void strdaymonthyear(char *dst); - -// TODO: doxygen comment. -void strdatehour(char *dst); diff --git a/mentos/inc/misc/debug.h b/mentos/inc/misc/debug.h index 9bb2505..6383626 100644 --- a/mentos/inc/misc/debug.h +++ b/mentos/inc/misc/debug.h @@ -1,67 +1,114 @@ /// MentOS, The Mentoring Operating system project /// @file debug.h /// @brief Debugging primitives. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "kernel.h" -#include "port_io.h" +#include "sys/kernel_levels.h" -/// @brief Used to enable the device. Any I/O to the debug module before this -/// command is sent will simply be ignored. -#define DBG_ENABLE 0x8A00 +struct pt_regs; -/// @brief Disable the I/O interface to the debugger and the memory -/// monitoring functions. -#define DBG_DISABLE 0x8AFF +#ifndef __DEBUG_LEVEL__ +/// Defines the debug level. +#define __DEBUG_LEVEL__ LOGLEVEL_NOTICE +#endif -/// @brief Selects register 0: Memory monitoring range start address -/// (inclusive). -#define SELECTS_REG_0 0x8A01 +#ifndef __DEBUG_HEADER__ +/// Header for identifying outputs coming from a mechanism. +#define __DEBUG_HEADER__ 0 +#endif -/// @brief Selects register 1: Memory monitoring range end address (exclusive). -#define SELECTS_REG_1 0x8A02 +/// @brief Extract the filename from the full path provided by __FILE__. +#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__) -/// @brief Enable address range memory monitoring as indicated by register 0 -/// and 1 and clears both registers. -#define ENABLE_ADDR_RANGE_MEM_MONITOR 0x8A80 +/// @brief Sets the loglevel. +/// @param level The new loglevel. +void set_log_level(int level); -/// @brief If the debugger is enabled (via --enable-debugger), sending 0x8AE2 -/// to port 0x8A00 after the device has been enabled will enable instruction -/// tracing. -#define INSTRUCTION_TRACE_ENABLE 0x8AE3 +/// @brief Returns the current loglevel. +/// @return The current loglevel +int get_log_level(); -/// @brief If the debugger is enabled (via --enable-debugger), sending 0x8AE2 -/// to port 0x8A00 after the device has been enabled will disable instruction -/// tracing. -#define INSTRUCTION_TRACE_DISABLE 0x8AE2 +/// @brief Prints the given character to debug output. +/// @param c The character to print. +void dbg_putchar(char c); -/// @brief If the debugger is enabled (via --enable-debugger), sending 0x8AE5 -/// to port 0x8A00 after the device has been enabled will enable register -/// tracing. This currently output the value of all the registers for each -/// instruction traced. Note: instruction tracing must be enabled to view -/// the register tracing. -#define REGISTER_TRACE_ENABLE 0x8AE5 - -/// @brief If the debugger is enabled (via --enable-debugger), sending 0x8AE4 -/// to port 0x8A00 after the device has been enabled will disable register -/// tracing. -#define REGISTER_TRACE_DISABLE 0x8AE4 +/// @brief Prints the given string to debug output. +/// @param s The string to print. +void dbg_puts(const char *s); /// @brief Prints the given string to the debug output. -void _dbg_print(const char *file, const char *fun, int line, const char *msg, - ...); +/// @param file The name of the file. +/// @param fun The name of the function. +/// @param line The line inside the file. +/// @param header The header to print. +/// @param format The format to used, see printf. +/// @param ... The list of arguments. +void dbg_printf(const char *file, const char *fun, int line, char *header, const char *format, ...); -#define __FILENAME__ \ - (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : \ - __FILE__) +/// @brief Prints the registers on debug output. +/// @param frame Pointer to the register. +void dbg_print_regs(struct pt_regs *frame); -#define dbg_print(...) _dbg_print(__FILENAME__, __func__, __LINE__, __VA_ARGS__) +/// @brief Transforms the given amount of bytes to a readable string. +/// @param bytes The bytes to turn to string. +/// @return String representing the bytes in human readable form. +const char *to_human_size(unsigned long bytes); -/// @brief Prints the given registers to the debug output. -void print_reg(register_t *reg); +/// @brief Transforms the given value to a binary string. +/// @param value The decimal value. +/// @return String representing the binary value. +const char *dec_to_binary(unsigned long value); -/// @brief Prints the given intrframe to the debug output. -void print_intrframe(pt_regs *frame); +/// Prints a KERN_DEFAULT. +#define pr_default(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, KERN_DEFAULT __VA_ARGS__) +/// Prints a KERN_EMERG. +#if __DEBUG_LEVEL__ >= LOGLEVEL_EMERG +#define pr_emerg(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, KERN_EMERG __VA_ARGS__) +#else +#define pr_emerg(...) +#endif +/// Prints a KERN_ALERT. +#if __DEBUG_LEVEL__ >= LOGLEVEL_ALERT +#define pr_alert(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, KERN_ALERT __VA_ARGS__) +#else +#define pr_alert(...) +#endif +/// Prints a KERN_CRIT. +#if __DEBUG_LEVEL__ >= LOGLEVEL_CRIT +#define pr_crit(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, KERN_CRIT __VA_ARGS__) +#else +#define pr_crit(...) +#endif +/// Prints a KERN_ERR. +#if __DEBUG_LEVEL__ >= LOGLEVEL_ERR +#define pr_err(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, KERN_ERR __VA_ARGS__) +#else +#define pr_err(...) +#endif +/// Prints a KERN_WARNING. +#if __DEBUG_LEVEL__ >= LOGLEVEL_WARNING +#define pr_warning(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, KERN_WARNING __VA_ARGS__) +#else +#define pr_warning(...) +#endif +/// Prints a KERN_NOTICE. +#if __DEBUG_LEVEL__ >= LOGLEVEL_NOTICE +#define pr_notice(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, KERN_NOTICE __VA_ARGS__) +#else +#define pr_notice(...) +#endif +/// Prints a KERN_INFO. +#if __DEBUG_LEVEL__ >= LOGLEVEL_INFO +#define pr_info(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, KERN_INFO __VA_ARGS__) +#else +#define pr_info(...) +#endif +/// Prints a KERN_DEBUG. +#if __DEBUG_LEVEL__ >= LOGLEVEL_DEBUG +#define pr_debug(...) dbg_printf(__FILENAME__, __func__, __LINE__, __DEBUG_HEADER__, KERN_DEBUG __VA_ARGS__) +#else +#define pr_debug(...) +#endif diff --git a/mentos/inc/multiboot.h b/mentos/inc/multiboot.h index 237f5b2..3dac645 100644 --- a/mentos/inc/multiboot.h +++ b/mentos/inc/multiboot.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file multiboot.h /// @brief Data structures used for multiboot. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. // Do not inc here in boot.s. @@ -10,43 +10,25 @@ #include "stdint.h" -#define MULTIBOOT_FLAG_MEM 0x001 +#define MULTIBOOT_HEADER_MAGIC 0x1BADB002U ///< The magic field should contain this. +#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002U ///< This should be in %eax. -#define MULTIBOOT_FLAG_DEVICE 0x002 +#define MULTIBOOT_FLAG_MEM 0x00000001U ///< Is there basic lower/upper memory information? +#define MULTIBOOT_FLAG_DEVICE 0x00000002U ///< Is there a boot device set? +#define MULTIBOOT_FLAG_CMDLINE 0x00000004U ///< Is the command-line defined? +#define MULTIBOOT_FLAG_MODS 0x00000008U ///< Are there modules to do something with? +#define MULTIBOOT_FLAG_AOUT 0x00000010U ///< Is there a symbol table loaded? +#define MULTIBOOT_FLAG_ELF 0x00000020U ///< Is there an ELF section header table? +#define MULTIBOOT_FLAG_MMAP 0x00000040U ///< Is there a full memory map? +#define MULTIBOOT_FLAG_DRIVE_INFO 0x00000080U ///< Is there drive info? +#define MULTIBOOT_FLAG_CONFIG_TABLE 0x00000100U ///< Is there a config table? +#define MULTIBOOT_FLAG_BOOT_LOADER_NAME 0x00000200U ///< Is there a boot loader name? +#define MULTIBOOT_FLAG_APM_TABLE 0x00000400U ///< Is there a APM table? +#define MULTIBOOT_FLAG_VBE_INFO 0x00000800U ///< Is there video information? +#define MULTIBOOT_FLAG_FRAMEBUFFER_INFO 0x00001000U ///< Is there a framebuffer table? -#define MULTIBOOT_FLAG_CMDLINE 0x004 - -#define MULTIBOOT_FLAG_MODS 0x008 - -#define MULTIBOOT_FLAG_AOUT 0x010 - -#define MULTIBOOT_FLAG_ELF 0x020 - -#define MULTIBOOT_FLAG_MMAP 0x040 - -#define MULTIBOOT_FLAG_CONFIG 0x080 - -#define MULTIBOOT_FLAG_LOADER 0x100 - -#define MULTIBOOT_FLAG_APM 0x200 - -#define MULTIBOOT_FLAG_VBE 0x400 - -#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 - -#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 - -#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 - -#define MULTIBOOT_MEMORY_AVAILABLE 1 - -#define MULTIBOOT_MEMORY_RESERVED 2 - -#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 - -#define MULTIBOOT_MEMORY_NVS 4 - -#define MULTIBOOT_MEMORY_BADRAM 5 +#define MULTIBOOT_MEMORY_AVAILABLE 1 ///< The memory is available. +#define MULTIBOOT_MEMORY_RESERVED 2 ///< The memory is reserved. // +-------------------+ // 0 | flags | (required) @@ -93,131 +75,186 @@ // +-------------------+ /// The symbol table for a.out. -typedef struct multiboot_aout_symbol_table -{ +typedef struct multiboot_aout_symbol_table_t { + /// TODO: Comment. uint32_t tabsize; + /// TODO: Comment. uint32_t strsize; + /// TODO: Comment. uint32_t addr; + /// TODO: Comment. uint32_t reserved; } multiboot_aout_symbol_table_t; /// The section header table for ELF. -typedef struct multiboot_elf_section_header_table -{ +typedef struct multiboot_elf_section_header_table_t { + /// TODO: Comment. uint32_t num; + /// TODO: Comment. uint32_t size; + /// TODO: Comment. uint32_t addr; + /// TODO: Comment. uint32_t shndx; } multiboot_elf_section_header_table_t; -// TODO: doxygen comment. -typedef struct multiboot_mod_list -{ +/// @brief Stores information about a module. +typedef struct multiboot_module_t { // The memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive. + /// The starting address of the modules. uint32_t mod_start; + /// The ending address of the modules. uint32_t mod_end; - // Module command line. + /// Module command line. uint32_t cmdline; - // Padding to take it to 16 bytes (must be zero). + /// Padding to take it to 16 bytes (must be zero). uint32_t pad; } multiboot_module_t; -// TODO: doxygen comment. -typedef struct multiboot_mmap_entry -{ +/// @brief Stores information about memory mapping. +typedef struct multiboot_memory_map_t { + /// Size of this entry. uint32_t size; - uint64_t addr; - uint64_t len; + /// Lower bytes of the base address. + uint32_t base_addr_low; + /// Higher bytes of the base address. + uint32_t base_addr_high; + /// Lower bytes of the length. + uint32_t length_low; + /// Higher bytes of the length. + uint32_t length_high; + /// Memory type. uint32_t type; -} __attribute__((packed)) multiboot_memory_map_t; +} multiboot_memory_map_t; -// TODO: doxygen comment. -typedef struct multiboot_info -{ +/// @brief Multiboot information structure. +typedef struct multiboot_info_t { /// Multiboot info version number. uint32_t flags; - - /// Available memory from BIOS. + /// Lower memory available from the BIOS. uint32_t mem_lower; + /// Upper memory available from the BIOS. uint32_t mem_upper; - - /// "root" partition. + /// Boot device ID. uint32_t boot_device; - - /// Kernel command line. + /// Pointer to the boot command line. uint32_t cmdline; - - /// Boot-Module list. + /// Amount of modules loaded. uint32_t mods_count; + /// Address to the first module structure. uint32_t mods_addr; - - union - { + /// Contains data either of an aout or elf file. + union { + /// Symbol table for `aout` file. multiboot_aout_symbol_table_t aout_sym; + /// Elf section header table for `elf` file. multiboot_elf_section_header_table_t elf_sec; } u; - - /// Memory Mapping buffer. + /// Memory map length. uint32_t mmap_length; + /// Memory map address. uint32_t mmap_addr; - - /// Drive Info buffer. + /// Drive map length. uint32_t drives_length; + /// Drive map address. uint32_t drives_addr; - /// ROM configuration table. uint32_t config_table; - /// Boot Loader Name. uint32_t boot_loader_name; - /// APM table. uint32_t apm_table; - - /// Video. + /// Pointer to the VBE control info structure. uint32_t vbe_control_info; + /// Pointer to the VBE mode info structure. uint32_t vbe_mode_info; + /// Current VBE mode. uint32_t vbe_mode; + /// VBE3.0 interface segment. uint32_t vbe_interface_seg; + /// VBE3.0 interface segment offset. uint32_t vbe_interface_off; + /// VBE3.0 interface segment length. uint32_t vbe_interface_len; - + /// TODO: Comment. uint32_t framebuffer_addr; + /// TODO: Comment. uint32_t framebuffer_pitch; + /// TODO: Comment. uint32_t framebuffer_width; + /// TODO: Comment. uint32_t framebuffer_height; + /// TODO: Comment. uint32_t framebuffer_bpp; + /// TODO: Comment. uint32_t framebuffer_type; - union - { - struct - { + /// TODO: Comment. + union { + /// TODO: Comment. + struct { + /// TODO: Comment. uint32_t framebuffer_palette_addr; + /// TODO: Comment. uint16_t framebuffer_palette_num_colors; - }; - struct - { + } palette_field; + /// TODO: Comment. + struct { + /// TODO: Comment. uint8_t framebuffer_red_field_position; + /// TODO: Comment. uint8_t framebuffer_red_mask_size; + /// TODO: Comment. uint8_t framebuffer_green_field_position; + /// TODO: Comment. uint8_t framebuffer_green_mask_size; + /// TODO: Comment. uint8_t framebuffer_blue_field_position; + /// TODO: Comment. uint8_t framebuffer_blue_mask_size; - }; - }; -} __attribute__((packed)) multiboot_info_t; + } rgb_field; + } framebuffer_info; +} multiboot_info_t; -// Be careful that the offset 0 is base_addr_low but no size. -/// @brief The memory map. -typedef struct memory_map -{ - uint32_t size; - uint32_t base_addr_low; - uint32_t base_addr_high; - uint32_t length_low; - uint32_t length_high; - uint32_t type; -} memory_map_t; +/// @brief Returns the first mmap entry. +/// @param info The multiboot info from which we extract the entry. +/// @return The first entry. +multiboot_memory_map_t *mmap_first_entry(multiboot_info_t *info); -// TODO: doxygen comment. +/// @brief The first entry of the given type. +/// @param info The multiboot info from which we extract the entry. +/// @param type The type of entry we are looking for. +/// @return The first entry of the given type. +multiboot_memory_map_t *mmap_first_entry_of_type(multiboot_info_t *info, uint32_t type); + +/// @brief Returns the next mmap entry. +/// @param info The multiboot info from which we extract the entry. +/// @param entry The current entry. +/// @return The next entry. +multiboot_memory_map_t *mmap_next_entry(multiboot_info_t *info, multiboot_memory_map_t *entry); + +/// @brief Returns the next mmap entry of the given type. +/// @param info The multiboot info from which we extract the entry. +/// @param entry The current entry. +/// @param type The type of entry we are looking for. +/// @return The next entry of the given type. +multiboot_memory_map_t *mmap_next_entry_of_type(multiboot_info_t *info, multiboot_memory_map_t *entry, uint32_t type); + +/// @brief Returns the type of the entry as string. +/// @param entry The current entry. +/// @return String representing the type of entry. +char *mmap_type_name(multiboot_memory_map_t *entry); + +/// @brief Finds the first module. +/// @param info The multiboot info from which we extract the information. +/// @return Pointer to the first module. +multiboot_module_t *first_module(multiboot_info_t *info); + +/// @brief Finds the next module after the one we provide. +/// @param info The multiboot info from which we extract the information. +/// @param mod The current module. +/// @return Pointer to the next module. +multiboot_module_t *next_module(multiboot_info_t *info, multiboot_module_t *mod); + +/// @brief Prints as debugging output the info regarding the multiboot. +/// @param mboot_ptr The pointer to the multiboot information. void dump_multiboot(multiboot_info_t *mboot_ptr); diff --git a/mentos/inc/proc_access.h b/mentos/inc/proc_access.h new file mode 100644 index 0000000..05a1e3f --- /dev/null +++ b/mentos/inc/proc_access.h @@ -0,0 +1,272 @@ +/// MentOS, The Mentoring Operating system project +/// @file proc_access.h +/// @brief Set of functions and flags used to manage processors registers. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "stdint.h" + +/// Macro that encapsulate asm and volatile directives. +#define ASM(a) __asm__ __volatile__(a) + +#define CR0_PE 0x00000001u ///< Protected mode Enable. +#define CR0_MP 0x00000002u ///< "Math" Present (e.g. npx), wait for it. +#define CR0_EM 0x00000004u ///< EMulate NPX, e.g. trap, don't execute code. +#define CR0_TS 0x00000008u ///< Process has done Task Switch, do NPX save. +#define CR0_ET 0x00000010u ///< 32 bit (if set) vs 16 bit (387 vs 287). +#define CR0_PG 0x80000000u ///< Paging Enable. + +#define CR4_SEE 0x00008000u ///< Secure Enclave Enable XXX. +#define CR4_SMAP 0x00200000u ///< Supervisor-Mode Access Protect. +#define CR4_SMEP 0x00100000u ///< Supervisor-Mode Execute Protect. +#define CR4_OSXSAVE 0x00040000u ///< OS supports XSAVE. +#define CR4_PCIDE 0x00020000u ///< PCID Enable. +#define CR4_RDWRFSGS 0x00010000u ///< RDWRFSGS Enable. +#define CR4_SMXE 0x00004000u ///< Enable SMX operation. +#define CR4_VMXE 0x00002000u ///< Enable VMX operation. +#define CR4_OSXMM 0x00000400u ///< SSE/SSE2 exception support in OS. +#define CR4_OSFXS 0x00000200u ///< SSE/SSE2 OS supports FXSave. +#define CR4_PCE 0x00000100u ///< Performance-Monitor Count Enable. +#define CR4_PGE 0x00000080u ///< Page Global Enable. +#define CR4_MCE 0x00000040u ///< Machine Check Exceptions. +#define CR4_PAE 0x00000020u ///< Physical Address Extensions. +#define CR4_PSE 0x00000010u ///< Page Size Extensions. +#define CR4_DE 0x00000008u ///< Debugging Extensions. +#define CR4_TSD 0x00000004u ///< Time Stamp Disable. +#define CR4_PVI 0x00000002u ///< Protected-mode Virtual Interrupts. +#define CR4_VME 0x00000001u ///< Virtual-8086 Mode Extensions. + +static inline uint16_t get_es(void) +{ + uint16_t es; + ASM("mov %%es, %0" + : "=r"(es)); + return es; +} + +static inline void set_es(uint16_t es) +{ + ASM("mov %0, %%es" + : + : "r"(es)); +} + +static inline uint16_t get_ds(void) +{ + uint16_t ds; + ASM("mov %%ds, %0" + : "=r"(ds)); + return ds; +} + +static inline void set_ds(uint16_t ds) +{ + ASM("mov %0, %%ds" + : + : "r"(ds)); +} + +static inline uint16_t get_fs(void) +{ + uint16_t fs; + ASM("mov %%fs, %0" + : "=r"(fs)); + return fs; +} + +static inline void set_fs(uint16_t fs) +{ + ASM("mov %0, %%fs" + : + : "r"(fs)); +} + +static inline uint16_t get_gs(void) +{ + uint16_t gs; + ASM("mov %%gs, %0" + : "=r"(gs)); + return gs; +} + +static inline void set_gs(uint16_t gs) +{ + ASM("mov %0, %%gs" + : + : "r"(gs)); +} + +static inline uint16_t get_ss(void) +{ + uint16_t ss; + ASM("mov %%ss, %0" + : "=r"(ss)); + return ss; +} + +static inline void set_ss(uint16_t ss) +{ + ASM("mov %0, %%ss" + : + : "r"(ss)); +} + +static inline uintptr_t get_cr0(void) +{ + uintptr_t cr0; + ASM("mov %%cr0, %0" + : "=r"(cr0)); + return (cr0); +} + +static inline void set_cr0(uintptr_t cr0) +{ + ASM("mov %0, %%cr0" + : + : "r"(cr0)); +} + +static inline uintptr_t get_cr3(void) +{ + uintptr_t cr3; + ASM("mov %%cr3, %0" + : "=r"(cr3)); + return (cr3); +} + +static inline void set_cr3(uintptr_t cr3) +{ + ASM("mov %0, %%cr3" + : + : "r"(cr3)); +} + +static inline uintptr_t get_cr4(void) +{ + uintptr_t cr4; + ASM("mov %%cr4, %0" + : "=r"(cr4)); + return (cr4); +} + +static inline void set_cr4(uintptr_t cr4) +{ + ASM("mov %0, %%cr4" + : + : "r"(cr4) + : "memory"); +} + +static inline uintptr_t get_eflags(void) +{ + uintptr_t eflags; + /* "=rm" is safe here, because "pop" adjusts the stack before + * it evaluates its effective address -- this is part of the + * documented behavior of the "pop" instruction. + */ + ASM("pushf ; pop %0" + : "=rm"(eflags) + : /* no input */ + : "memory"); + return eflags; +} + +static inline void clear_ts(void) +{ + ASM("clts"); +} + +static inline unsigned short get_tr(void) +{ + unsigned short seg; + ASM("str %0" + : "=rm"(seg)); + return (seg); +} + +static inline void set_tr(unsigned int seg) +{ + ASM("ltr %0" + : + : "rm"((unsigned short)(seg))); +} + +static inline unsigned short sldt(void) +{ + unsigned short seg; + ASM("sldt %0" + : "=rm"(seg)); + return (seg); +} + +static inline void lldt(unsigned int seg) +{ + ASM("lldt %0" + : + : "rm"((unsigned short)(seg))); +} + +static inline void lgdt(uintptr_t *desc) +{ + ASM("lgdt %0" + : + : "m"(*desc)); +} + +static inline void lidt(uintptr_t *desc) +{ + ASM("lidt %0" + : + : "m"(*desc)); +} + +/// @brief Enable IRQs. +static inline void sti() +{ + ASM("sti" :: + : "memory"); +} + +/// @brief Disable IRQs. +static inline void cli() +{ + ASM("cli" :: + : "memory"); +} + +static inline void swapgs(void) +{ + ASM("swapgs"); +} + +static inline void hlt(void) +{ + ASM("hlt"); +} + +/// @brief Pause. +static inline void pause() +{ + ASM("pause"); +} + +// == Memory clobbers ========================================================= +// Memory clobber implies a fence, and it also impacts how the compiler treats +// potential data aliases. A memory clobber says that the asm block modifies +// memory that is not otherwise mentioned in the asm instructions. +// So, for example, a correct use of memory clobbers would be when using an +// instruction that clears a cache line. The compiler will assume that +// virtually any data may be aliased with the memory changed by that +// instruction. As a result, all required data used after the asm block +// will be reloaded from memory after the asm completes. This is much more +// expensive than the simple fence implied by the "volatile" attribute. + +// == Volatile Block ========================================================== +// Making an inline asm block "volatile" as in this example, ensures that, +// as it optimizes, the compiler does not move any instructions above or +// below the block of asm statements. +// ASM(" addic. %0,%1,%2\n" : "=r"(res): "=r"(a),"r"(a)) +// This can be particularly important in cases when the code is accessing +// shared memory. \ No newline at end of file diff --git a/mentos/inc/process/prio.h b/mentos/inc/process/prio.h index b5fbfe2..843dae5 100644 --- a/mentos/inc/process/prio.h +++ b/mentos/inc/process/prio.h @@ -1,15 +1,9 @@ /// MentOS, The Mentoring Operating system project /// @file prio.h /// @brief Defines processes priority value. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#define MAX_NICE +19 - -#define MIN_NICE -20 - -#define NICE_WIDTH (MAX_NICE - MIN_NICE + 1) - // Priority of a process goes from 0..MAX_PRIO-1, valid RT // priority is 0..MAX_RT_PRIO-1, and SCHED_NORMAL/SCHED_BATCH // tasks are in the range MAX_RT_PRIO..MAX_PRIO-1. Priority @@ -21,35 +15,58 @@ // priority to a value higher than any user task. // Note: MAX_RT_PRIO must not be smaller than MAX_USER_RT_PRIO. +/// @brief Max niceness value. +#define MAX_NICE +19 + +/// @brief Min niceness value. +#define MIN_NICE -20 + +/// @brief Niceness range. +#define NICE_WIDTH (MAX_NICE - MIN_NICE + 1) + +/// @brief Maximum real-time priority. #define MAX_RT_PRIO 100 -#define MAX_PRIO (MAX_RT_PRIO + NICE_WIDTH) +/// @brief Maximum priority. +#define MAX_PRIO (MAX_RT_PRIO + NICE_WIDTH) +/// @brief Default priority. #define DEFAULT_PRIO (MAX_RT_PRIO + NICE_WIDTH / 2) -// Convert user-nice values [ -20 ... 0 ... 19 ] -// to static priority [ MAX_RT_PRIO..MAX_PRIO-1 ], -// and back. +/// @brief Converts user-nice values [ -20 ... 0 ... 19 ] +/// to static priority [ MAX_RT_PRIO..MAX_PRIO-1 ]. #define NICE_TO_PRIO(nice) ((nice) + DEFAULT_PRIO) -#define PRIO_TO_NICE(prio) ((prio) - DEFAULT_PRIO) +/// @brief Converts static priority [ MAX_RT_PRIO..MAX_PRIO-1 ] +/// to user-nice values [ -20 ... 0 ... 19 ]. +#define PRIO_TO_NICE(prio) ((prio)-DEFAULT_PRIO) -// 'User priority' is the nice value converted to something we -// can work with better when scaling various scheduler parameters, -// it's a [ 0 ... 39 ] range. -#define USER_PRIO(p) ((p)-MAX_RT_PRIO) +/// @brief 'User priority' is the nice value converted to something we +/// can work with better when scaling various scheduler parameters, +/// it's a [ 0 ... 39 ] range. +#define USER_PRIO(p) ((p)-MAX_RT_PRIO) +/// @brief Provide easy access to the priority value of a task_struct. #define TASK_USER_PRIO(p) USER_PRIO((p)->static_prio) +/// @brief The maximum priority for a user process. #define MAX_USER_PRIO (USER_PRIO(MAX_PRIO)) +/// @brief Table that transforms the priority into a weight, used for +/// computing the virtual runtime. static const int prio_to_weight[NICE_WIDTH] = { - /* 100 */ 88761, 71755, 56483, 46273, 36291, - /* 105 */ 29154, 23254, 18705, 14949, 11916, - /* 110 */ 9548, 7620, 6100, 4904, 3906, - /* 115 */ 3121, 2501, 1991, 1586, 1277, - /* 120 */ 1024, 820, 655, 526, 423, - /* 125 */ 335, 272, 215, 172, 137, - /* 130 */ 110, 87, 70, 56, 45, - /* 135 */ 36, 29, 23, 18, 15 + /* 100 */ 88761, 71755, 56483, 46273, 36291, + /* 105 */ 29154, 23254, 18705, 14949, 11916, + /* 110 */ 9548, 7620, 6100, 4904, 3906, + /* 115 */ 3121, 2501, 1991, 1586, 1277, + /* 120 */ 1024, 820, 655, 526, 423, + /* 125 */ 335, 272, 215, 172, 137, + /* 130 */ 110, 87, 70, 56, 45, + /* 135 */ 36, 29, 23, 18, 15 }; + +/// @brief Transforms the priority to weight. +#define GET_WEIGHT(prio) prio_to_weight[USER_PRIO((prio))] + +/// @brief Weight of a default priority. +#define NICE_0_LOAD GET_WEIGHT(DEFAULT_PRIO) diff --git a/mentos/inc/process/process.h b/mentos/inc/process/process.h index e7ae4a6..39a455d 100644 --- a/mentos/inc/process/process.h +++ b/mentos/inc/process/process.h @@ -1,22 +1,14 @@ /// MentOS, The Mentoring Operating system project /// @file process.h /// @brief Process data structures and functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "list.h" -#include "tree.h" -#include "clock.h" -#include "types.h" -#include "stdint.h" -#include "stddef.h" -#include "paging.h" -#include "kernel.h" -#include "unistd.h" -#include "list_head.h" -#include "signal_defs.h" +#include "mem/paging.h" +#include "system/signal.h" +#include "devices/fpu.h" /// The maximum length of a name for a task_struct. #define TASK_NAME_MAX_LENGTH 100 @@ -24,191 +16,163 @@ /// The default dimension of the stack of a process (1 MByte). #define DEFAULT_STACK_SIZE 0x100000 -//==== Task state ============================================================== -// Used in tsk->state -/// The task is running. -#define TASK_RUNNING 0x0000 - -/// The task is interruptible. -#define TASK_INTERRUPTIBLE 0x0001 - -/// The task is uninterruptible. -#define TASK_UNINTERRUPTIBLE 0x0002 - -// Used in tsk->exit_state -/// The task is dead. -#define EXIT_DEAD 0x0010 - -/// The task is zombie. -#define EXIT_ZOMBIE 0x0020 -//============================================================================== - -/// @brief Every task_struck has a sched_entity. This structure is used to track -/// the statistics of a process. While the other variables also play a role in +/// @brief This structure is used to track the statistics of a process. +/// @details +/// While the other variables also play a role in /// CFS decisions'algorithm, vruntime is by far the core variable which needs /// more attention as to understand the scheduling decision process. -/// @details +/// /// The nice value is a user-space and priority 'prio' is the process's actual /// priority that use by Linux kernel. In linux system priorities are 0 to 139 /// in which 0 to 99 for real time and 100 to 139 for users. /// The nice value range is -20 to +19 where -20 is highest, 0 default and +19 /// is lowest. relation between nice value and priority is : PR = 20 + NI. -typedef struct sched_entity { - /// Static execution priority. - int prio; +typedef struct sched_entity_t { + /// Static execution priority. + int prio; - /// Start execution time. - time_t start_runtime; + /// Start execution time. + time_t start_runtime; + /// Last context switch time. + time_t exec_start; + /// Last execution time. + time_t exec_runtime; + /// Overall execution time. + time_t sum_exec_runtime; + /// Weighted execution time. + time_t vruntime; - /// Last context switch time. - time_t exec_start; + /// Expected period of the task + time_t period; + /// Absolute deadline + time_t deadline; + /// Absolute time of arrival of the task + time_t arrivaltime; + /// Has already executed + bool_t executed; + /// Determines if it is a periodic task. + bool_t is_periodic; + /// Determines if we need to analyze the WCET of the process. + bool_t is_under_analysis; + /// Beginning of next period + time_t next_period; + /// Worst case execution time + time_t worst_case_exec; + /// Processor utilization factor + double utilization_factor; +} sched_entity_t; - /// Overall execution time. - time_t sum_exec_runtime; - - /// weighted execution time. - time_t vruntime; - -} sched_entity; - -// x86 thread (x86 and FPU registers). -typedef struct thread_struct { - /// 32-bit base pointer register. - uint32_t ebp; - - /// 32-bit stack pointer register. - uint32_t useresp; - - /// 32-bit base register. - uint32_t ebx; - - /// 32-bit data register. - uint32_t edx; - - /// 32-bit counter. - uint32_t ecx; - - /// 32-bit accumulator register. - uint32_t eax; - - /// Instruction Pointer Register. - uint32_t eip; - - /// 32-bit flag register. - uint32_t eflags; - - /// FS and GS have no hardware-assigned uses. - uint32_t gs; - - /// FS and GS have no hardware-assigned uses. - uint32_t fs; - - /// Extra Segment determined by the programmer. - uint32_t es; - - /// Data Segment. - uint32_t ds; - - /// 32-bit destination register. - uint32_t edi; - - /// 32-bit source register. - uint32_t esi; - - // ///< Code Segment. - // uint32_t cs; - - // ///< Stack Segment. - // uint32_t ss; - - /// Determines if the FPU is enabled. - bool_t fpu_enabled; - - /// Data structure used to save FPU registers. - savefpu fpu_register; -} thread_struct; +/// @brief Stores the status of CPU and FPU registers. +typedef struct thread_struct_t { + /// Stored status of registers. + pt_regs regs; + /// Stored status of registers befor jumping into a signal handler. + pt_regs signal_regs; + /// Determines if the FPU is enabled. + bool_t fpu_enabled; + /// Data structure used to save FPU registers. + savefpu fpu_register; +} thread_struct_t; /// @brief this is our task object. Every process in the system has this, and /// it holds a lot of information. It’ll hold mm information, it’s name, /// statistics, etc.. typedef struct task_struct { - /// The pid of the process. - pid_t pid; + /// The pid of the process. + pid_t pid; + /// The session id of the process + pid_t sid; + /// The Group Id of the process + pid_t gid; + /// The uid of the user owning the process. + pid_t uid; + // -1 unrunnable, 0 runnable, >0 stopped. + /// The current state of the process: + __volatile__ long state; + /// The current opened file descriptors + vfs_file_descriptor_t *fd_list; + /// The maximum supported number of file descriptors + int max_fd; + /// Pointer to process's parent. + struct task_struct *parent; + /// List head for scheduling purposes. + list_head run_list; + /// List of children traced by the process. + list_head children; + /// List of siblings. + list_head sibling; + /// The context of the processors. + thread_struct_t thread; + /// For scheduling algorithms. + sched_entity_t se; + /// Exit code of the process. (parameter of _exit() system call). + int exit_code; + /// The name of the task (Added for debug purpose). + char name[TASK_NAME_MAX_LENGTH]; + /// Task's segments. + mm_struct_t *mm; + /// Task's specific error number. + int error_no; + /// The current working directory. + char cwd[PATH_MAX]; - // -1 unrunnable, 0 runnable, >0 stopped. - /// The current state of the process: - __volatile__ long state; + // struct signal_struct *signal; + /// Instruction Pointer of the LIBC Signal Handler. + uint32_t sigreturn_eip; + /// Pointer to the process’s signal handler descriptor + sighand_t sighand; + /// Mask of blocked signals. + sigset_t blocked; + /// Temporary mask of blocked signals (used by the rt_sigtimedwait() system call) + sigset_t real_blocked; + /// The previous sig mask. + sigset_t saved_sigmask; + /// Data structure storing the private pending signals + sigpending_t pending; - /// Pointer to process's parent. - struct task_struct *parent; + /// Timer for alarm syscall. + struct timer_list* real_timer; - /// List head for scheduling purposes. - list_head run_list; + /// Next value for the real timer (ITIMER_REAL). + unsigned long it_real_incr; + /// Current value for the real timer (ITIMER_REAL). + unsigned long it_real_value; + /// Next value for the virtual timer (ITIMER_VIRTUAL). + unsigned long it_virt_incr; + /// Current value for the virtual timer (ITIMER_VIRTUAL). + unsigned long it_virt_value; + /// Next value for the profiling timer (ITIMER_PROF). + unsigned long it_prof_incr; + /// Current value for the profiling timer (ITIMER_PROF). + unsigned long it_prof_value; - /// List of children traced by the process. - list_head children; + //==== Future work ========================================================= + // - task's attributes: + // struct task_struct __rcu *real_parent; + // int exit_state; + // int exit_signal; + // struct thread_info thread_info; + // List of sibling, namely processes created by parent process + // list_head sibling; - /// List of siblings. - list_head sibling; - - /// The context of the processors. - thread_struct thread; - - /// For scheduling algorithms. - sched_entity se; - - /// Exit code of the process. (parameter of _exit() system call). - int exit_code; - - /// The name of the task (Added for debug purpose). - char name[TASK_NAME_MAX_LENGTH]; - - /// Task's segments. - struct mm_struct *mm; - - /// Task's specific error number. - int error_no; - - /// The current working directory. - char cwd[MAX_PATH_LENGTH]; - - //==== Future work ========================================================= - // - task's attributes: - // struct task_struct __rcu *real_parent; - // int exit_state; - // int exit_signal; - // struct thread_info thread_info; - // List of sibling, namely processes created by parent process - // struct list_head sibling; - - // - task's file descriptor: - // struct files_struct *files; - - // - task's signal handlers: - // struct signal_struct *signal; - // struct sighand_struct *sighand; - // sigset_t blocked; - // sigset_t real_blocked; - // sigset_t saved_sigmask; - // struct sigpending pending; - //========================================================================== + // - task's file descriptor: + // struct files_struct *files; + // - task's signal handlers: + // struct signal_struct *signal; + // struct sighand_struct_t *sighand; + // sigset_t blocked; + // sigset_t real_blocked; + // sigset_t saved_sigmask; + // struct sigpending pending; + //========================================================================== } task_struct; -/// @brief Create a child process. -pid_t sys_vfork(pt_regs *r); - -// TODO: doxygen comment. -char *get_current_dir_name(); - -// TODO: doxygen comment. -void sys_getcwd(char *path, size_t size); - -// TODO: doxygen comment.s -void sys_chdir(char const *path); - -/// @brief Replaces the current process image with a new process image. -int sys_execve(pt_regs *r); +/// @brief Initialize the task management. +/// @return 1 success, 0 failure. +int init_tasking(); /// @brief Create and spawn the init process. -struct task_struct *create_init_process(); +/// @param path Path of the `init` program. +/// @return Pointer to init process. +task_struct *process_create_init(const char *path); diff --git a/mentos/inc/process/scheduler.h b/mentos/inc/process/scheduler.h index 0b1ca60..1fc6596 100644 --- a/mentos/inc/process/scheduler.h +++ b/mentos/inc/process/scheduler.h @@ -1,82 +1,115 @@ /// MentOS, The Mentoring Operating system project /// @file scheduler.h /// @brief Scheduler structures and functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -#include "clock.h" -#include "kernel.h" -#include "stdint.h" -#include "process.h" +#include "klib/list_head.h" +#include "process/process.h" +#include "stddef.h" -typedef struct { - /// Number of queued processes. - size_t num_active; - - /// Queue of processes. - list_head queue; - - /// The current running process. - task_struct *curr; +/// @brief Structure that contains information about live processes. +typedef struct runqueue_t { + /// Number of queued processes. + size_t num_active; + /// Number of queued periodic processes. + size_t num_periodic; + /// Queue of processes. + list_head queue; + /// The current running process. + task_struct *curr; } runqueue_t; +/// @brief Structure that describes scheduling parameters. +typedef struct sched_param_t { + /// Static execution priority. + int sched_priority; + /// Expected period of the task + time_t period; + /// Absolute deadline + time_t deadline; + /// Absolute time of arrival of the task + time_t arrivaltime; + /// Is task periodic? + bool_t is_periodic; +} sched_param_t; + +/// @brief Initialize the scheduler. +void scheduler_initialize(); + /// @brief Returns a non-decreasing unique process id. /// @return Process identifier (PID). -uint32_t get_new_pid(); - -/// @brief Initialize the scheduler. -void kernel_initialize_scheduler(); - -/// @brief Activate the given process. -/// @param process Process that has to be activated. -void enqueue_task(task_struct *process); - -/// @brief Removes the given process from the queue. -/// @param process Process that has to be activated. -void dequeue_task(task_struct *process); - -/// @brief Returns the number of active processes. -size_t kernel_get_active_processes(); +uint32_t scheduler_getpid(); /// @brief Returns the pointer to the current active process. -task_struct *kernel_get_current_process(); +/// @return Pointer to the current process. +task_struct *scheduler_get_current_process(); + +/// @brief Returns the maximum vruntime of all the processes in running state. +/// @return A maximum vruntime value. +time_t scheduler_get_maximum_vruntime(); + +/// @brief Returns the number of active processes. +/// @return Number of processes. +size_t scheduler_get_active_processes(); /// @brief Returns a pointer to the process with the given pid. -task_struct *kernel_get_running_process(pid_t pid); +/// @param pid The pid of the process we are looking for. +/// @return Pointer to the process, or NULL if we cannot find it. +task_struct *scheduler_get_running_process(pid_t pid); -task_struct *pick_next_task(runqueue_t *runqueue, time_t delta_exec); +/// @brief Activate the given process. +/// @param process Process that has to be activated. +void scheduler_enqueue_task(task_struct *process); -/// @brief The RR implementation of the scheduler. +/// @brief Removes the given process from the queue. +/// @param process Process that has to be activated. +void scheduler_dequeue_task(task_struct *process); + +/// @brief The RR implementation of the scheduler. /// @param f The context of the process. -void kernel_schedule(pt_regs *f); +void scheduler_run(pt_regs *f); -/// @birief Values from pt_regs to task_struct process. -void update_context(pt_regs *f, task_struct *process); +/// @brief Values from pt_regs to task_struct process. +/// @param f The set of registers we are saving. +/// @param process The process for which we are saving the CPU registers status. +void scheduler_store_context(pt_regs *f, task_struct *process); /// @brief Values from task_struct process to pt_regs. -void do_switch(task_struct *process, pt_regs *f); +/// @param process The process for which we are restoring the registers in CPU . +/// @param f The set of registers we are restoring. +void scheduler_restore_context(task_struct *process, pt_regs *f); -/// @brief Switch CPU to user mode and start running that given process. -/// @param process The process that has to be executed -void enter_user_jmp(uintptr_t location, uintptr_t stack); +/// @brief Switch CPU to user mode and start running that given process. +/// @param location The instruction pointer of the process we are starting. +/// @param stack Address of the stack of that process. +void scheduler_enter_user_jmp(uintptr_t location, uintptr_t stack); -/// Returns the process ID (PID) of the calling process. -pid_t sys_getpid(); +/// @brief Picks the next task (in scheduler_algorithm.c). +/// @param runqueue Pointer to the runqueue. +/// @return The next task to execute. +task_struct *scheduler_pick_next_task(runqueue_t *runqueue); -/// Returns the parent process ID (PPID) of the calling process. -pid_t sys_getppid(); +/// @brief Set new scheduling settings for the given process. +/// @param pid ID of the process we are manipulating. +/// @param param New parameters. +/// @return 1 on success, -1 on error. +int sys_sched_setparam(pid_t pid, const sched_param_t *param); -/// @brief Sets the priority value of the given task. -int set_user_nice(task_struct *p, long nice); +/// @brief Gets the scheduling settings for the given process. +/// @param pid ID of the process we are manipulating. +/// @param param Where we store the parameters. +/// @return 1 on success, -1 on error. +int sys_sched_getparam(pid_t pid, sched_param_t *param); -/// @brief Adds the increment to the priority value of the task. -int sys_nice(int increment); +/// @brief Puts the process on wait until its next period starts. +/// @return 0 on success, a negative value on failure. +int sys_waitperiod(); -/// @brief Suspends execution of the calling thread until a child specified -/// by pid argument has changed state. -pid_t sys_waitpid(pid_t pid, int *status, int options); - -/// The exit() function causes normal process termination. -void sys_exit(int exit_code); +/// @brief Returns 1 if the given group is orphaned, the session leader of the group +/// is no longer alive. +/// @param gid ID of the group +/// @return 1 if the group is orphan, 0 otherwise. +int is_orphaned_pgrp(pid_t gid); \ No newline at end of file diff --git a/mentos/inc/process/wait.h b/mentos/inc/process/wait.h new file mode 100644 index 0000000..05afbba --- /dev/null +++ b/mentos/inc/process/wait.h @@ -0,0 +1,111 @@ +/// MentOS, The Mentoring Operating system project +/// @file wait.h +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "klib/list_head.h" +#include "klib/spinlock.h" + +/// @brief Return immediately if no child is there to be waited for. +#define WNOHANG 0x00000001 +/// @brief Return for children that are stopped, and whose status has not +/// been reported. +#define WUNTRACED 0x00000002 +/// @brief returns true if the child process exited because of a signal that +/// was not caught. +#define WIFSIGNALED(status) (!WIFSTOPPED(status) && !WIFEXITED(status)) +/// @brief returns true if the child process that caused the return is +/// currently stopped; this is only possible if the call was done using +/// WUNTRACED(). +#define WIFSTOPPED(status) (((status)&0xff) == 0x7f) +/// @brief evaluates to the least significant eight bits of the return code +/// of the child that terminated, which may have been set as the argument +/// to a call to exit() or as the argument for a return statement in the +/// main program. This macro can only be evaluated if WIFEXITED() +/// returned nonzero. +#define WEXITSTATUS(status) (((status)&0xff00) >> 8) +/// @brief returns the number of the signal that caused the child process to +/// terminate. This macro can only be evaluated if WIFSIGNALED() returned +/// nonzero. +#define WTERMSIG(status) ((status)&0x7f) +/// @brief Is nonzero if the child exited normally. +#define WIFEXITED(status) (WTERMSIG(status) == 0) +/// @brief returns the number of the signal that caused the child to stop. +/// This macro can only be evaluated if WIFSTOPPED() returned nonzero. +#define WSTOPSIG(status) (WEXITSTATUS(status)) + +//==== Task States ============================================================ +#define TASK_RUNNING 0x00 ///< The process is either: 1) running on CPU or 2) waiting in a run queue. +#define TASK_INTERRUPTIBLE (1 << 0) ///< The process is sleeping, waiting for some event to occur. +#define TASK_UNINTERRUPTIBLE (1 << 1) ///< Similar to TASK_INTERRUPTIBLE, but it doesn't process signals. +#define TASK_STOPPED (1 << 2) ///< Stopped, it's not running, and not able to run. +#define TASK_TRACED (1 << 3) ///< Is being monitored by other processes such as debuggers. +#define EXIT_ZOMBIE (1 << 4) ///< The process has terminated. +#define EXIT_DEAD (1 << 5) ///< The final state. +//============================================================================== + +/// @defgroup WaitQueueFlags Wait Queue Flags +/// @{ + +/// @brief When an entry has this flag is added to the end of the wait queue. +/// Entries without that flag are, instead, added to the beginning. +#define WQ_FLAG_EXCLUSIVE 0x01 +//#define WQ_FLAG_WOKEN 0x02 +//#define WQ_FLAG_BOOKMARK 0x04 +//#define WQ_FLAG_CUSTOM 0x08 +//#define WQ_FLAG_DONE 0x10 + +/// @} + +/// @brief Head of the waiting queue. +typedef struct wait_queue_head_t { + /// Locking element for the waiting queque. + spinlock_t lock; + /// Head of the waiting queue, it contains wait_queue_entry_t elements. + struct list_head task_list; +} wait_queue_head_t; + +/// @brief Entry of the waiting queue. +typedef struct wait_queue_entry_t { + /// Flags of the type WaitQueueFlags. + unsigned int flags; + /// Task associated with the wait queue entry. + struct task_struct *task; + /// Function associated with the wait queue entry. + int (*func)(struct wait_queue_entry_t *wait, unsigned mode, int sync); + /// Handler for placing the entry inside a waiting queue double linked-list. + struct list_head task_list; +} wait_queue_entry_t; + +/// @brief Initialize the waiting queue entry. +/// @param wq The entry we initialize. +/// @param task The task associated with the entry. +void init_waitqueue_entry(wait_queue_entry_t *wq, struct task_struct *task); + +/// @brief Adds the element to the waiting queue. +/// @param head The head of the waiting queue. +/// @param wq The entry we insert inside the waiting queue. +void add_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *wq); + +/// @brief Removes the element from the waiting queue. +/// @param head The head of the waiting queue. +/// @param wq The entry we remove from the waiting queue. +void remove_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *wq); + +/// @brief The default wake function, a wrapper for try_to_wake_up. +/// @param wait The pointer to the wait queue. +/// @param mode The type of wait (TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE). +/// @param sync Specifies if the wakeup should be synchronous. +/// @return 1 on success, 0 on failure. +int default_wake_function(wait_queue_entry_t *wait, unsigned mode, int sync); + +/// @brief Sets the state of the current process to TASK_UNINTERRUPTIBLE +/// and inserts it into the specified wait queue. +/// +/// @param wq Waitqueue where to sleep. +/// @return Pointer to the entry inside the wq representing the +/// sleeping process. +wait_queue_entry_t *sleep_on(wait_queue_head_t *wq); \ No newline at end of file diff --git a/mentos/inc/sys/dirent.h b/mentos/inc/sys/dirent.h deleted file mode 100644 index ae2a3ae..0000000 --- a/mentos/inc/sys/dirent.h +++ /dev/null @@ -1,55 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file dirent.h -/// @brief Functions used to manage directories. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stddef.h" - -/// The maximum length for a name. -#define NAME_MAX 30 - -/// @brief Contains the entries of a directory. -typedef struct dirent_t -{ - /// The inode of the entry. - ino_t d_ino; - - /// The type of the entry. - int d_type; - - /// The nam of the entry. - char d_name[NAME_MAX + 1]; -} dirent_t; - -/// @brief Contains information concerning a directory. -typedef struct DIR -{ - /// Filesystem directory handle. - int handle; - - /// The currently opened entry. - ino_t cur_entry; - - /// Path to the directory. - char path[NAME_MAX + 1]; - - /// Next directory item. - dirent_t entry; -} DIR; - -/// @brief Opens a directory and returns a handler to it. -/// @param path The path of the directory. -/// @return Pointer to the directory. Otherwise, NULL. -DIR *opendir(const char *path); - -/// @brief Given a pointer to a directory, it closes it. -int closedir(DIR *dirp); - -/// @brief At each call of this function, it returns a pointer to the next -/// element inside the directory. -/// @return A pointer to the next element. If there are no more elments, it -/// returns NULL. -dirent_t *readdir(DIR *dirp); diff --git a/mentos/inc/sys/errno.h b/mentos/inc/sys/errno.h index eeff0de..f7b4530 100644 --- a/mentos/inc/sys/errno.h +++ b/mentos/inc/sys/errno.h @@ -1,383 +1,138 @@ /// MentOS, The Mentoring Operating system project -/// @file process.c -/// @brief System call errors definition. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @file errno.h +/// @brief Errors definition. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once extern int *__geterrno(); +/// Provide easy access to the error number. #define errno (*__geterrno()) -/// 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 - -/// Arg 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 - -/// Operation would block. -#define EWOULDBLOCK EAGAIN - -/// 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 - -// TODO: doxygen comment. -#define EDEADLOCK EDEADLK - -/// 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 +#define EPERM 1 ///< Operation not permitted. +#define ENOENT 2 ///< No such file or directory. +#define ESRCH 3 ///< No such process. +#define EINTR 4 ///< Interrupted system call. +#define EIO 5 ///< I/O error. +#define ENXIO 6 ///< No such device or address. +#define E2BIG 7 ///< Arg list too long. +#define ENOEXEC 8 ///< Exec format error. +#define EBADF 9 ///< Bad file number. +#define ECHILD 10 ///< No child processes. +#define EAGAIN 11 ///< Try again. +#define ENOMEM 12 ///< Out of memory. +#define EACCES 13 ///< Permission denied. +#define EFAULT 14 ///< Bad address. +#define ENOTBLK 15 ///< Block device required. +#define EBUSY 16 ///< Device or resource busy. +#define EEXIST 17 ///< File exists. +#define EXDEV 18 ///< Cross-device link. +#define ENODEV 19 ///< No such device. +#define ENOTDIR 20 ///< Not a directory. +#define EISDIR 21 ///< Is a directory. +#define EINVAL 22 ///< Invalid argument. +#define ENFILE 23 ///< File table overflow. +#define EMFILE 24 ///< Too many open files. +#define ENOTTY 25 ///< Not a typewriter. +#define ETXTBSY 26 ///< Text file busy. +#define EFBIG 27 ///< File too large. +#define ENOSPC 28 ///< No space left on device. +#define ESPIPE 29 ///< Illegal seek. +#define EROFS 30 ///< Read-only file system. +#define EMLINK 31 ///< Too many links. +#define EPIPE 32 ///< Broken pipe. +#define EDOM 33 ///< Math argument out of domain of func. +#define ERANGE 34 ///< Math result not representable. +#define EDEADLK 35 ///< Resource deadlock would occur. +#define ENAMETOOLONG 36 ///< File name too long. +#define ENOLCK 37 ///< No record locks available. +#define ENOSYS 38 ///< Function not implemented. +#define ENOTEMPTY 39 ///< Directory not empty. +#define ELOOP 40 ///< Too many symbolic links encountered. +#define EWOULDBLOCK EAGAIN ///< Operation would block. +#define ENOMSG 42 ///< No message of desired type. +#define EIDRM 43 ///< Identifier removed. +#define ECHRNG 44 ///< Channel number out of range. +#define EL2NSYNC 45 ///< Level 2 not synchronized. +#define EL3HLT 46 ///< Level 3 halted. +#define EL3RST 47 ///< Level 3 reset. +#define ELNRNG 48 ///< Link number out of range. +#define EUNATCH 49 ///< Protocol driver not attached. +#define ENOCSI 50 ///< No CSI structure available. +#define EL2HLT 51 ///< Level 2 halted. +#define EBADE 52 ///< Invalid exchange. +#define EBADR 53 ///< Invalid request descriptor. +#define EXFULL 54 ///< Exchange full. +#define ENOANO 55 ///< No anode. +#define EBADRQC 56 ///< Invalid request code. +#define EBADSLT 57 ///< Invalid slot. +#define EDEADLOCK EDEADLK ///< Resource deadlock would occur. +#define EBFONT 59 ///< Bad font file format. +#define ENOSTR 60 ///< Device not a stream. +#define ENODATA 61 ///< No data available. +#define ETIME 62 ///< Timer expired. +#define ENOSR 63 ///< Out of streams resources. +#define ENONET 64 ///< Machine is not on the network. +#define ENOPKG 65 ///< Package not installed. +#define EREMOTE 66 ///< Object is remote. +#define ENOLINK 67 ///< Link has been severed. +#define EADV 68 ///< Advertise error. +#define ESRMNT 69 ///< Srmount error. +#define ECOMM 70 ///< Communication error on send. +#define EPROTO 71 ///< Protocol error. +#define EMULTIHOP 72 ///< Multihop attempted. +#define EDOTDOT 73 ///< RFS specific error. +#define EBADMSG 74 ///< Not a data message. +#define EOVERFLOW 75 ///< Value too large for defined data type. +#define ENOTUNIQ 76 ///< Name not unique on network. +#define EBADFD 77 ///< File descriptor in bad state. +#define EREMCHG 78 ///< Remote address changed. +#define ELIBACC 79 ///< Can not access a needed shared library. +#define ELIBBAD 80 ///< Accessing a corrupted shared library. +#define ELIBSCN 81 ///< .lib section in a.out corrupted. +#define ELIBMAX 82 ///< Attempting to link in too many shared libraries. +#define ELIBEXEC 83 ///< Cannot exec a shared library directly. +#define EILSEQ 84 ///< Illegal byte sequence. +#define ERESTART 85 ///< Interrupted system call should be restarted. +#define ESTRPIPE 86 ///< Streams pipe error. +#define EUSERS 87 ///< Too many users. +#define ENOTSOCK 88 ///< Socket operation on non-socket. +#define EDESTADDRREQ 89 ///< Destination address required. +#define EMSGSIZE 90 ///< Message too long. +#define EPROTOTYPE 91 ///< Protocol wrong type for socket. +#define ENOPROTOOPT 92 ///< Protocol not available. +#define EPROTONOSUPPORT 93 ///< Protocol not supported. +#define ESOCKTNOSUPPORT 94 ///< Socket type not supported. +#define EOPNOTSUPP 95 ///< Operation not supported on transport endpoint. +#define EPFNOSUPPORT 96 ///< Protocol family not supported. +#define EAFNOSUPPORT 97 ///< Address family not supported by protocol. +#define EADDRINUSE 98 ///< Address already in use. +#define EADDRNOTAVAIL 99 ///< Cannot assign requested address. +#define ENETDOWN 100 ///< Network is down. +#define ENETUNREACH 101 ///< Network is unreachable. +#define ENETRESET 102 ///< Network dropped connection because of reset. +#define ECONNABORTED 103 ///< Software caused connection abort. +#define ECONNRESET 104 ///< Connection reset by peer. +#define ENOBUFS 105 ///< No buffer space available. +#define EISCONN 106 ///< Transport endpoint is already connected. +#define ENOTCONN 107 ///< Transport endpoint is not connected. +#define ESHUTDOWN 108 ///< Cannot send after transport endpoint shutdown. +#define ETOOMANYREFS 109 ///< Too many references: cannot splice. +#define ETIMEDOUT 110 ///< Connection timed out. +#define ECONNREFUSED 111 ///< Connection refused. +#define EHOSTDOWN 112 ///< Host is down. +#define EHOSTUNREACH 113 ///< No route to host. +#define EALREADY 114 ///< Operation already in progress. +#define EINPROGRESS 115 ///< Operation now in progress. +#define ESTALE 116 ///< Stale NFS file handle. +#define EUCLEAN 117 ///< Structure needs cleaning. +#define ENOTNAM 118 ///< Not a XENIX named type file. +#define ENAVAIL 119 ///< No XENIX semaphores available. +#define EISNAM 120 ///< Is a named type file. +#define EREMOTEIO 121 ///< Remote I/O error. +#define EDQUOT 122 ///< Quota exceeded. +#define ENOMEDIUM 123 ///< No medium found. +#define EMEDIUMTYPE 124 ///< Wrong medium type. +#define ENOTSCHEDULABLE 125 ///< The periodc process cannot be scheduled. diff --git a/mentos/inc/sys/ipc.h b/mentos/inc/sys/ipc.h index 4bd6eba..6f774e6 100644 --- a/mentos/inc/sys/ipc.h +++ b/mentos/inc/sys/ipc.h @@ -1,53 +1,35 @@ /// MentOS, The Mentoring Operating system project /// @file ipc.h /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once #include "stddef.h" -/// Create key if key does not exist. -#define IPC_CREAT 01000 +#define IPC_CREAT 01000 ///< Create key if key does not exist. +#define IPC_EXCL 02000 ///< Fail if key exists. +#define IPC_NOWAIT 04000 ///< Return error on wait. +#define IPC_RMID 0 ///< Remove identifier. +#define IPC_SET 1 ///< Set `ipc_perm' options. +#define IPC_STAT 2 ///< Get `ipc_perm' options. +#define IPC_INFO 3 ///< See ipcs. -/// Fail if key exists. -#define IPC_EXCL 02000 - -/// Return error on wait. -#define IPC_NOWAIT 04000 - -/// Remove identifier. -#define IPC_RMID 0 - -/// Set `ipc_perm' options. -#define IPC_SET 1 - -/// Get `ipc_perm' options. -#define IPC_STAT 2 - -/// See ipcs. -#define IPC_INFO 3 - -struct ipc_perm { +/// @brief Holds details about IPCs. +typedef struct ipc_perm_t { /// Creator user id. - uid_t cuid; - + uid_t cuid; /// Creator group id. - gid_t cgid; - + gid_t cgid; /// User id. - uid_t uid; - + uid_t uid; /// Group id. - gid_t gid; - + gid_t gid; /// r/w permission. - ushort mode; - + ushort mode; /// Sequence # (to generate unique msg/sem/shm id). - ushort seq; - + ushort seq; /// User specified msg/sem/shm key. - key_t key; -}; + key_t key; +} ipc_perm_t; diff --git a/mentos/inc/sys/kernel_levels.h b/mentos/inc/sys/kernel_levels.h new file mode 100644 index 0000000..5611219 --- /dev/null +++ b/mentos/inc/sys/kernel_levels.h @@ -0,0 +1,31 @@ +/// MentOS, The Mentoring Operating system project +/// @file kernel_levels.h +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#define KERN_SOH "\001" ///< ASCII Start Of Header. +#define KERN_SOH_ASCII '\001' ///< Character version of the SOH. + +#define KERN_EMERG KERN_SOH "0" ///< Emergency messages (precede a crash) +#define KERN_ALERT KERN_SOH "1" ///< Error requiring immediate attention +#define KERN_CRIT KERN_SOH "2" ///< Critical error (hardware or software) +#define KERN_ERR KERN_SOH "3" ///< Error conditions (common in drivers) +#define KERN_WARNING KERN_SOH "4" ///< Warning conditions (could lead to errors) +#define KERN_NOTICE KERN_SOH "5" ///< Not an error but a significant condition +#define KERN_INFO KERN_SOH "6" ///< Informational message +#define KERN_DEBUG KERN_SOH "7" ///< Used only for debug messages +#define KERN_DEFAULT "" ///< Default kernel logging level + +// Integer equivalents of KERN_ +#define LOGLEVEL_DEFAULT (-1) ///< default-level messages. +#define LOGLEVEL_EMERG 0 ///< system is unusable. +#define LOGLEVEL_ALERT 1 ///< action must be taken immediately. +#define LOGLEVEL_CRIT 2 ///< critical conditions. +#define LOGLEVEL_ERR 3 ///< error conditions. +#define LOGLEVEL_WARNING 4 ///< warning conditions. +#define LOGLEVEL_NOTICE 5 ///< normal but significant condition. +#define LOGLEVEL_INFO 6 ///< informational. +#define LOGLEVEL_DEBUG 7 ///< debug-level messages. diff --git a/mentos/inc/sys/module.h b/mentos/inc/sys/module.h index 02386d7..87e110b 100644 --- a/mentos/inc/sys/module.h +++ b/mentos/inc/sys/module.h @@ -1,7 +1,29 @@ /// MentOS, The Mentoring Operating system project /// @file module.h /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once + +#include "multiboot.h" +#include "stdint.h" + +/// The maximum number of modules. +#define MAX_MODULES 10 + +extern multiboot_module_t modules[MAX_MODULES]; + +/// @brief Ininitialize the modules. +/// @param header Multiboot info used to initialize the modules. +/// @return 1 on success, 0 on error. +int init_modules(multiboot_info_t *header); + +/// @brief Relocates modules to virtual mapped low memory, to allow physical +/// unmapping of the first part of the ram. +/// @return 1 on success, 0 on failure. +int relocate_modules(); + +/// @brief Returns the address where the modules end. +/// @return Address after the modules. +uintptr_t get_address_after_modules(); \ No newline at end of file diff --git a/mentos/inc/sys/reboot.h b/mentos/inc/sys/reboot.h index 8547eff..3fd404e 100644 --- a/mentos/inc/sys/reboot.h +++ b/mentos/inc/sys/reboot.h @@ -1,43 +1,36 @@ /// MentOS, The Mentoring Operating system project /// @file reboot.h /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once -// Magic values required to use _reboot() system call. +/// Magic values required to use _reboot() system call. #define LINUX_REBOOT_MAGIC1 0xfee1dead - +/// Magic values required to use _reboot() system call. #define LINUX_REBOOT_MAGIC2 672274793 - +/// Magic values required to use _reboot() system call. #define LINUX_REBOOT_MAGIC2A 85072278 - +/// Magic values required to use _reboot() system call. #define LINUX_REBOOT_MAGIC2B 369367448 - +/// Magic values required to use _reboot() system call. #define LINUX_REBOOT_MAGIC2C 537993216 // Commands accepted by the _reboot() system call. /// Restart system using default command and mode. #define LINUX_REBOOT_CMD_RESTART 0x01234567 - /// Stop OS and give system control to ROM monitor, if any. #define LINUX_REBOOT_CMD_HALT 0xCDEF0123 - /// Ctrl-Alt-Del sequence causes RESTART command. #define LINUX_REBOOT_CMD_CAD_ON 0x89ABCDEF - /// Ctrl-Alt-Del sequence sends SIGINT to init task. #define LINUX_REBOOT_CMD_CAD_OFF 0x00000000 - /// Stop OS and remove all power from system, if possible. #define LINUX_REBOOT_CMD_POWER_OFF 0x4321FEDC - /// Restart system using given command string. #define LINUX_REBOOT_CMD_RESTART2 0xA1B2C3D4 - /// Suspend system using software suspend if compiled in. #define LINUX_REBOOT_CMD_SW_SUSPEND 0xD000FCE2 - /// Restart system using a previously loaded Linux kernel #define LINUX_REBOOT_CMD_KEXEC 0x45584543 diff --git a/mentos/inc/sys/stat.h b/mentos/inc/sys/stat.h deleted file mode 100644 index 2037030..0000000 --- a/mentos/inc/sys/stat.h +++ /dev/null @@ -1,42 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file stat.h -/// @brief Stat functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "clock.h" -#include "stddef.h" - -/// @brief Data structure which contains information about a file. -typedef struct stat_t -{ - /// ID of device containing file. - dev_t st_dev; - /// Fle serial number. - ino_t st_ino; - /// Mode of file. - mode_t st_mode; - /// User id del file. - uid_t st_uid; - /// Group id del file. - gid_t st_gid; - /// Dimensione del file. - off_t st_size; - /// Time of last access. - time_t st_atime; - /// Time of last data modification. - time_t st_mtime; - /// Time of last status change. - time_t st_ctime; -} stat_t; - -/// @brief Retrieves information about the file at the given location. -/// @param path The file descriptor of the file that is being inquired. -/// @param buf A structure where data about the file will be stored. -/// @return Returns a negative value on failure. -int stat(const char *path, stat_t *buf); - -// TODO: doxygen comment. -int mkdir(const char *path, mode_t mode); diff --git a/mentos/inc/sys/types.h b/mentos/inc/sys/types.h index 4457700..42336b2 100644 --- a/mentos/inc/sys/types.h +++ b/mentos/inc/sys/types.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file types.h /// @brief Collection of Kernel datatype -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -15,53 +15,56 @@ typedef unsigned int user_t; /// The type of process status. typedef unsigned int status_t; +/// Type for system keys. +typedef int key_t; + /// Defines the list of flags of a process. typedef enum eflags_list { - /// Carry flag. - EFLAG_CF = (1 << 0), + /// Carry flag. + EFLAG_CF = (1 << 0), - /// Parity flag. - EFLAG_PF = (1 << 2), + /// Parity flag. + EFLAG_PF = (1 << 2), - /// Auxiliary carry flag. - EFLAG_AF = (1 << 4), + /// Auxiliary carry flag. + EFLAG_AF = (1 << 4), - /// Zero flag. - EFLAG_ZF = (1 << 6), + /// Zero flag. + EFLAG_ZF = (1 << 6), - /// Sign flag. - EFLAG_SF = (1 << 7), + /// Sign flag. + EFLAG_SF = (1 << 7), - /// Trap flag. - EFLAG_TF = (1 << 8), + /// Trap flag. + EFLAG_TF = (1 << 8), - /// Interrupt enable flag. - EFLAG_IF = (1 << 9), + /// Interrupt enable flag. + EFLAG_IF = (1 << 9), - /// Direction flag. - EFLAG_DF = (1 << 10), + /// Direction flag. + EFLAG_DF = (1 << 10), - /// Overflow flag. - EFLAG_OF = (1 << 11), + /// Overflow flag. + EFLAG_OF = (1 << 11), - /// Nested task flag. - EFLAG_NT = (1 << 14), + /// Nested task flag. + EFLAG_NT = (1 << 14), - /// Resume flag. - EFLAG_RF = (1 << 16), + /// Resume flag. + EFLAG_RF = (1 << 16), - /// Virtual 8086 mode flag. - EFLAG_VM = (1 << 17), + /// Virtual 8086 mode flag. + EFLAG_VM = (1 << 17), - /// Alignment check flag (486+). - EFLAG_AC = (1 << 18), + /// Alignment check flag (486+). + EFLAG_AC = (1 << 18), - /// Virutal interrupt flag. - EFLAG_VIF = (1 << 19), + /// Virutal interrupt flag. + EFLAG_VIF = (1 << 19), - /// Virtual interrupt pending flag. - EFLAG_VIP = (1 << 20), + /// Virtual interrupt pending flag. + EFLAG_VIP = (1 << 20), - /// ID flag. - EFLAG_ID = (1 << 21), + /// ID flag. + EFLAG_ID = (1 << 21), } eflags_list; diff --git a/mentos/inc/sys/unistd.h b/mentos/inc/sys/unistd.h deleted file mode 100644 index 9b3dc3a..0000000 --- a/mentos/inc/sys/unistd.h +++ /dev/null @@ -1,95 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file unistd.h -/// @brief Functions used to manage files. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "types.h" -#include "stddef.h" - -//======== Standard file descriptors =========================================== -/// Standard input. -#define STDIN_FILENO -3 - -/// Standard output. -#define STDOUT_FILENO -2 - -/// Standard error output. -#define STDERR_FILENO -1 -//============================================================================== - -/// Maximum number of opened file. -#define MAX_OPEN_FD 4 - -/// @brief Read data from a file descriptor. -/// @param fd The file descriptor. -/// @param buf The buffer. -/// @param nbytes The number of bytes to read. -/// @return The number of read characters. -ssize_t read(int fd, void *buf, size_t nbytes); - -/// @brief Write data into a file descriptor. -/// @param fd The file descriptor. -/// @param buf The buffer collecting data to written. -/// @param nbytes The number of bytes to write. -/// @return The number of written bytes. -ssize_t write(int fd, void *buf, size_t nbytes); - -/// @brief Opens the file specified by pathname. -/// @param pathname A pathname for a file. -/// @param flags Used to set the file status flags and file access modes -/// of the open file description. -/// @param mode Specifies the file mode bits be applied when a new file -/// is created. -/// @return Returns a file descriptor, a small, nonnegative integer for -/// use in subsequent system calls. -int open(const char *pathname, int flags, mode_t mode); - -/// @brief Close a file descriptor. -/// @param fildes The file descriptor. -/// @return The result of the operation. -int close(int fildes); - -/// @brief Removes the given directory. -/// @param path The path to the directory to remove. -/// @return -int rmdir(const char *path); - -/// @brief Wrapper for exit system call. -extern void exit(int status); - -/// @brief Return the process identifier of the calling process. -/// @return pid_t process identifier. -extern pid_t getpid(); - -/// @brief Clone the calling process, but without copying the whole address space. -/// The calling process is suspended until the new process exits or is -/// replaced by a call to `execve'. Return -1 for errors, 0 to the new -/// process, and the process ID of the new process to the old process. -/// @return -extern pid_t vfork(); - -/// @brief Replaces the current process image with a new process image. -/// @param path The path to the binary file to execute. -/// @param argv The list of arguments. -/// @param envp -/// @return -extern int execve(const char *path, char *const argv[], char *const envp[]); - -/// @brief Adds inc to the nice value for the calling thread. -/// @param inc The value to add to the nice. -/// @return On success, the new nice value is returned. On error, -1 is -/// returned, and errno is set appropriately. -int nice(int inc); - -/// @brief Reboot system call. -/// @param cmd -/// @param arg -/// @return -int reboot(int magic1, int magic2, unsigned int cmd, void *arg); - -void getcwd(char *path, size_t size); - -void chdir(char const *path); diff --git a/mentos/inc/sys/utsname.h b/mentos/inc/sys/utsname.h index 461ebd6..48b945a 100644 --- a/mentos/inc/sys/utsname.h +++ b/mentos/inc/sys/utsname.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file utsname.h /// @brief Functions used to provide information about the machine & OS. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -10,20 +10,20 @@ #define SYS_LEN 257 /// @brief Holds information concerning the machine and the os. -typedef struct utsname_t -{ +typedef struct utsname_t { /// The name of the system. char sysname[SYS_LEN]; - /// The name of the node. char nodename[SYS_LEN]; - + /// Operating system release (e.g., "2.6.28"). + char release[SYS_LEN]; /// The version of the OS. char version[SYS_LEN]; - /// The name of the machine. char machine[SYS_LEN]; } utsname_t; -/// @brief Sets the values of os_infos. -int uname(utsname_t *os_infos); +/// @brief Returns system information in the structure pointed to by buf. +/// @param buf Buffer where the info will be placed. +/// @return 0 on success, a negative value on failure. +int sys_uname(utsname_t *buf); diff --git a/mentos/inc/system/panic.h b/mentos/inc/system/panic.h index eb7b2ce..e1cbaf6 100644 --- a/mentos/inc/system/panic.h +++ b/mentos/inc/system/panic.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file panic.h /// @brief Functions used to manage kernel panic. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -10,3 +10,5 @@ /// the kernel. /// @param msg The message that has to be shown. void kernel_panic(const char *msg); + +#define TODO(msg) kernel_panic(#msg); \ No newline at end of file diff --git a/mentos/inc/system/printk.h b/mentos/inc/system/printk.h index c14ed1c..b3adc71 100644 --- a/mentos/inc/system/printk.h +++ b/mentos/inc/system/printk.h @@ -1,10 +1,13 @@ /// MentOS, The Mentoring Operating system project -/// @file printk.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @file printk.h +/// @brief Functions for managing the kernel messages. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once /// @brief Write formatted output to stdout. -void printk(const char *, ...); +/// @param format Output formatted as for printf. +/// @param ... List of arguments. +/// @return The number of bytes written in syslog. +int sys_syslog(const char *format, ...); diff --git a/mentos/inc/system/signal.h b/mentos/inc/system/signal.h new file mode 100644 index 0000000..4e360b1 --- /dev/null +++ b/mentos/inc/system/signal.h @@ -0,0 +1,325 @@ +/// MentOS, The Mentoring Operating system project +/// @file signal.h +/// @brief Signals definition. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#pragma once + +#include "klib/stdatomic.h" +#include "klib/spinlock.h" +#include "klib/list_head.h" +#include "system/syscall.h" + +/// @brief Signal codes. +typedef enum { + SIGHUP = 1, ///< Hang up detected on controlling terminal or death of controlling process. + SIGINT = 2, ///< Issued if the user sends an interrupt signal (Ctrl + C). + SIGQUIT = 3, ///< Issued if the user sends a quit signal (Ctrl + D). + SIGILL = 4, ///< Illegal Instruction. + SIGTRAP = 5, ///< Trace/breakpoint trap. + SIGABRT = 6, ///< Abort signal from abort(). + SIGEMT = 7, ///< Emulator trap. + SIGFPE = 8, ///< Floating-point arithmetic exception. + SIGKILL = 9, ///< If a process gets this signal it must quit immediately and will not perform any clean-up operations. + SIGBUS = 10, ///< Bus error (bad memory access). + SIGSEGV = 11, ///< Invalid memory reference. + SIGSYS = 12, ///< Bad system call (SVr4). + SIGPIPE = 13, ///< Broken pipe: write to pipe with no readers. + SIGALRM = 14, ///< Alarm clock signal (used for timers). + SIGTERM = 15, ///< Software termination signal (sent by kill by default). + SIGUSR1 = 16, ///< User-defined signal 1. + SIGUSR2 = 17, ///< User-defined signal 2. + SIGCHLD = 18, ///< Child stopped or terminated. + SIGPWR = 19, ///< Power failure. + SIGWINCH = 20, ///< Window resize signal. + SIGURG = 21, ///< Urgent condition on socket. + SIGPOLL = 22, ///< Pollable event. + SIGSTOP = 23, ///< Stop process. + SIGTSTP = 24, ///< Stop typed at terminal. + SIGCONT = 25, ///< Continue if stopped. + SIGTTIN = 26, ///< Terminal input for background process. + SIGTTOU = 27, ///< Terminal output for background process. + SIGVTALRM = 28, ///< Virtual alarm clock. + SIGPROF = 29, ///< Profiling timer expired. + SIGXCPU = 30, ///< CPU time limit exceeded. + SIGXFSZ = 31, ///< File size limit exceeded. + NSIG +} signal_type_t; + +/// @brief Codes that indentify the sender of a signal. +typedef enum { + SI_NOINFO, ///< Unable to determine complete signal information. + + // Signal : - + // Enabled fields : si_pid, si_uid + SI_USER, ///< Signal sent by kill(), pthread_kill(), raise(), abort() or alarm(). + + // Signal : - + // Enabled fields : - + SI_KERNEL, ///< Generic kernel function + + // Signal : - + // Enabled fields : si_pid, si_uid, si_value + SI_QUEUE, ///< Signal was sent by sigqueue(). + SI_TIMER, ///< Signal was generated by expiration of a timer set by timer_settimer(). + SI_ASYNCIO, ///< Signal was generated by completion of an asynchronous I/O request. + SI_MESGQ, ///< Signal was generated by arrival of a message on an empty message queue. + + // Signal : SIGILL + // Enabled fields : si_addr (address of failing instruction) + ILL_ILLOPC, ///< Illegal opcode. + ILL_ILLOPN, ///< Illegal operand. + ILL_ILLADR, ///< Illegal addressing mode. + ILL_ILLTRP, ///< Illegal trap. + ILL_PRVOPC, ///< Privileged opcode. + ILL_PRVREG, ///< Privileged register. + ILL_COPROC, ///< Coprocessor error. + ILL_BADSTK, ///< Internal stack error. + + // Signal : SIGFPE + // Enabled fields : si_addr (address of failing instruction) + FPE_INTDIV, ///< Integer divide-by-zero. + FPE_INTOVF, ///< Integer overflow. + FPE_FLTDIV, ///< Floating point divide-by-zero. + FPE_FLTOVF, ///< Floating point overflow. + FPE_FLTUND, ///< Floating point underflow. + FPE_FLTRES, ///< Floating point inexact result. + FPE_FLTINV, ///< Invalid floating point operation. + FPE_FLTSUB, ///< Subscript out of range. + + // Signal : SIGSEGV + // Enabled fields : si_addr (address of faulting memory reference) + SEGV_MAPERR, ///< Address not mapped. + SEGV_ACCERR, ///< Invalid permissions. + + // Signal : SIGBUS + // Enabled fields : si_addr (address of faulting memory reference) + BUS_ADRALN, ///< Invalid address alignment. + BUS_ADRERR, ///< Non-existent physical address. + BUS_OBJERR, ///< Object-specific hardware error. + + // Signal : SIGTRAP + // Enabled fields : - + TRAP_BRKPT, ///< Process breakpoint. + TRAP_TRACE, ///< Process trace trap. + + // Signal : SIGCHLD + // Enabled fields : si_pid (child process ID) + // si_uid (real user ID of process that sent the signal) + // si_status (exit value or signal) + CLD_EXITED, ///< Child has exited. + CLD_KILLED, ///< Child has terminated abnormally and did not create a core file. + CLD_DUMPED, ///< Child has terminated abnormally and created a core file. + CLD_TRAPPED, ///< Traced child has trapped. + CLD_STOPPED, ///< Child has stopped. + CLD_CONTINUED, ///< Stopped child has continued. + + // Signal : SIGIO/SIGPOLL + // Enabled fields : si_band + POLL_IN, ///< Data input available. + POLL_OUT, ///< Output buffers available. + POLL_MSG, ///< Input message available. + POLL_ERR, ///< I/O error. + POLL_PRI, ///< High priority input available. + POLL_HUP, ///< Device disconnected. +} signal_sender_code_t; + +/// @brief Defines what to do with the provided signal mask. +typedef enum { + /// @brief The set of blocked signals is the union of the current set + /// and the set argument. + SIG_BLOCK, + /// @brief The signals in set are removed from the current set of + /// blocked signals. It is permissible to attempt to unblock + /// a signal which is not blocked. + SIG_UNBLOCK, + /// @brief The set of blocked signals is set to the argument set. + SIG_SETMASK +} sigmask_how_t; + +/// @defgroup SigactionFlags Flags associated with a sigaction. +/// @{ + +#define SA_NOCLDSTOP 0x00000001U ///< Turn off SIGCHLD when children stop. +#define SA_NOCLDWAIT 0x00000002U ///< Flag on SIGCHLD to inhibit zombies. +#define SA_SIGINFO 0x00000004U ///< sa_sigaction specifies the signal-handling function for signum. +#define SA_ONSTACK 0x08000000U ///< Indicates that a registered stack_t will be used. +#define SA_RESTART 0x10000000U ///< Flag to get restarting signals (which were the default long ago) +#define SA_NODEFER 0x40000000U ///< Prevents the current signal from being masked in the handler. +#define SA_RESETHAND 0x80000000U ///< Clears the handler when the signal is delivered. + +/// @} + +/// Type of a signal handler. +typedef void (*sighandler_t)(int); + +#define SIG_DFL ((sighandler_t)0) ///< Default signal handling. +#define SIG_IGN ((sighandler_t)1) ///< ignore signal. +#define SIG_ERR ((sighandler_t)-1) ///< error return from signal. + +/// @brief Structure used to mask and unmask signals. +/// @details +/// Each unsigned long consists of 32 bits, thus, the maximum number of signals +/// that may be declared is 64. +/// Signals are divided into two cathegories, identified by the two unsigned longs: +/// [ 1, 31] corresponds to normal signals; +/// [32, 64] corresponds to real-time signals. +typedef struct sigset_t { + /// Signals divided into two cathegories. + unsigned long sig[2]; +} sigset_t; + +/// @brief Holds the information on how to handle a specific signal. +typedef struct sigaction_t { + /// This field specifies the type of action to be performed; its value can be a pointer + /// to the signal handler, SIG_DFL (that is, the value 0) to specify that the default + /// action is performed, or SIG_IGN (that is, the value 1) to specify that the signal is + /// ignored. + sighandler_t sa_handler; + /// This sigset_t variable specifies the signals to be masked when running the signal handler + sigset_t sa_mask; + /// This set of flags specifies how the signal must be handled; + unsigned int sa_flags; +} sigaction_t; + +/// @brief Describes how each signal must be handled. +typedef struct sighand_t { + /// Usage counter of the signal handler descriptor. + atomic_t count; + /// Array of structures specifying the actions to be performed upon delivering the signals + sigaction_t action[NSIG]; + /// Spinlock protecting both the signal descriptor and the signal handler descriptor. + spinlock_t siglock; +} sighand_t; + +/// @brief Data passed with signal info. +typedef union sigval { + int sival_int; ///< Integer value. + void *sival_ptr; ///< Pointer value. +} sigval_t; + +/// @brief Stores information about an occurrence of a specific signal. +typedef struct siginfo_t { + /// The signal number. + int si_signo; + /// A code identifying who raised the signal (see signal_sender_code_t). + int si_code; + /// Signal value. + sigval_t si_value; + /// The error code of the instruction that caused the signal to be raised, or 0 if there was no error. + int si_errno; + /// Process ID of sending process. + pid_t si_pid; + /// Real user ID of sending process. + uid_t si_uid; + /// Address at which fault occurred. + void *si_addr; + /// Exit value or signal for process termination. + int si_status; + /// Band event for SIGPOLL/SIGIO. + int si_band; +} siginfo_t; + +/// @brief An entry of the signal queue. +typedef struct sigqueue_t { + /// Links for the pending signal queue’s list. + list_head list; + /// Flags associated with the queued signal. + int flags; + /// Describes the event that raised the signal. + siginfo_t info; + // Pointer to the user data structure of the process’s owner. + //struct user_struct *user; +} sigqueue_t; + +/// @brief Keeps information of pending signals. +typedef struct sigpending_t { + /// Head of the list of pending signals. + list_head list; + /// The mask which can be queried to know which signals are pending. + sigset_t signal; +} sigpending_t; + +/// These can be the second arg to send_sig_info/send_group_sig_info. +#define SEND_SIG_NOINFO ((siginfo_t *)0) + +/// @brief Handle the return from a signal handler. +/// @param f The stack frame when returning from a signal handler. +/// @return never. +long sys_sigreturn(struct pt_regs *f); + +/// @brief Handles the signals of the current process. +/// @param f The address of the stack area where the User Mode register +/// contents of the current process are saved. +/// @return If we are handling a signal, thus, `regs` have been modified +/// to handle it (e.g., eip is now poiting at the handler). +int do_signal(struct pt_regs *f); + +/// @brief Initialize the signals. +/// @return 1 on success, 0 on failure. +int signals_init(); + +/// @brief Send signal to one specific process. +/// @param pid The PID of the process. +/// @param sig The signal to be sent. +/// @return +int sys_kill(pid_t pid, int sig); + +/// @brief Sets the disposition of the signal signum to handler. +/// @param signum The signal number. +/// @param handler The handler for the signal. +/// @return The previous value of the signal handler, or SIG_ERR on error. +sighandler_t sys_signal(int signum, sighandler_t handler); + +/// @brief Examine and change a signal action. +/// @param signum Specifies the signal and can be any valid signal except SIGKILL and SIGSTOP. +/// @param act If non-NULL, the new action for signal signum is installed from act. +/// @param oldact If non-NULL, the previous action is saved in oldact. +/// @return returns 0 on success; on error, -1 is returned, and errno is set to indicate the error. +int sys_sigaction(int signum, const sigaction_t *act, sigaction_t *oldact); + +/// @brief Examine and change blocked signals. +/// @param how Determines the behavior of the call. +/// @param set The set of signals to manage by the function. +/// @param oldset If non-NULL, the previous value of the signal mask is stored here. +/// @return returns 0 on success, and -1 on error (errno is set to indicate the cause). +/// @details +/// If set is NULL, then the signal mask is unchanged (i.e., how is +/// ignored), but the current value of the signal mask is +/// nevertheless returned in oldset (if it is not NULL). +int sys_sigprocmask(int how, const sigset_t *set, sigset_t *oldset); + +/// @brief Returns the string describing the given signal. +/// @param sig The signal to inquire. +/// @return String representing the signal. +const char *strsignal(int sig); + +/// @brief Prepare an empty set. +/// @param set The set to manipulate. +/// @return 0 on success and -1 on error. +int sigemptyset(sigset_t *set); + +/// @brief Prepare a full set. +/// @param set The set to manipulate. +/// @return 0 on success and -1 on error. +int sigfillset(sigset_t *set); + +/// @brief Adds the given signal to the correct set. +/// @param set The set to manipulate. +/// @param signum The signalt to handle. +/// @return 0 on success and -1 on error. +int sigaddset(sigset_t *set, int signum); + +/// @brief Removes the given signal to the correct set. +/// @param set The set to manipulate. +/// @param signum The signalt to handle. +/// @return 0 on success and -1 on error. +int sigdelset(sigset_t *set, int signum); + +/// @brief Checks if the given signal is part of the set. +/// @param set The set to manipulate. +/// @param signum The signalt to handle. +/// @return 1 if signum is a member of set, +/// 0 if signum is not a member, and -1 on error. +int sigismember(sigset_t *set, int signum); \ No newline at end of file diff --git a/mentos/inc/system/signal_defs.h b/mentos/inc/system/signal_defs.h deleted file mode 100644 index 3057c07..0000000 --- a/mentos/inc/system/signal_defs.h +++ /dev/null @@ -1,121 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file signal_defs.h -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -// Signal names (from the Unix specification on signals) - -/// Hangup. -#define SIGHUP 1 - -/// Interupt. -#define SIGINT 2 - -/// Quit. -#define SIGQUIT 3 - -/// Illegal instruction. -#define SIGILL 4 - -/// A breakpoint or trace instruction has been reached. -#define SIGTRAP 5 - -/// Another process has requested that you abort. -#define SIGABRT 6 - -/// Emulation trap XXX. -#define SIGEMT 7 - -/// Floating-point arithmetic exception. -#define SIGFPE 8 - -/// You have been stabbed repeated with a large knife. -#define SIGKILL 9 - -/// Bus error (device error). -#define SIGBUS 10 - -/// Segmentation fault. -#define SIGSEGV 11 - -/// Bad system call. -#define SIGSYS 12 - -/// Attempted to read or write from a broken pipe. -#define SIGPIPE 13 - -/// This is your wakeup call. -#define SIGALRM 14 - -/// You have been Schwarzenegger'd. -#define SIGTERM 15 - -/// User Defined Signal #1. -#define SIGUSR1 16 - -/// User Defined Signal #2. -#define SIGUSR2 17 - -/// Child status report. -#define SIGCHLD 18 - -/// We need moar powah!. -#define SIGPWR 19 - -/// Your containing terminal has changed size. -#define SIGWINCH 20 - -/// An URGENT! event (On a socket). -#define SIGURG 21 - -/// XXX OBSOLETE; socket i/o possible. -#define SIGPOLL 22 - -/// Stopped (signal). -#define SIGSTOP 23 - -/// ^Z (suspend). -#define SIGTSTP 24 - -/// Unsuspended (please, continue). -#define SIGCONT 25 - -/// TTY input has stopped. -#define SIGTTIN 26 - -/// TTY output has stopped. -#define SIGTTOUT 27 - -/// Virtual timer has expired. -#define SIGVTALRM 28 - -/// Profiling timer expired. -#define SIGPROF 29 - -/// CPU time limit exceeded. -#define SIGXCPU 30 - -/// File size limit exceeded. -#define SIGXFSZ 31 - -/// Herp. -#define SIGWAITING 32 - -/// Die in a fire. -#define SIGDIAF 33 - -/// The sending process does not like you. -#define SIGHATE 34 - -/// Window server event. -#define SIGWINEVENT 35 - -/// Everybody loves cats. -#define SIGCAT 36 - -#define SIGTTOU 37 - -#define NUMSIGNALS 38 - -#define NSIG NUMSIGNALS diff --git a/mentos/inc/system/syscall.h b/mentos/inc/system/syscall.h index be57315..f52ccde 100644 --- a/mentos/inc/system/syscall.h +++ b/mentos/inc/system/syscall.h @@ -1,413 +1,201 @@ /// MentOS, The Mentoring Operating system project /// @file syscall.h /// @brief System Call handler definition. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once +#include "system/syscall_types.h" +#include "fs/vfs_types.h" #include "kernel.h" +#include "sys/dirent.h" +#include "sys/types.h" /// @brief Initialize the system calls. void syscall_init(); /// @brief Handler for the system calls. -void syscall_handler(pt_regs *r); - -// Return result in eax ("=a"). -// System call number in eax ("a"). -#define DEFN_SYSCALL0(__res, num) \ - __asm__ __volatile__("INT $0x80" : "=a"(__res) : "a"(num)) - -// Return result in eax ("=a"). -// System call number in eax ("a"). -// p1 in ebx ("+b"). -#define DEFN_SYSCALL1(__res, num, p1) \ - __asm__ __volatile__("INT $0x80" : "=a"(__res), "+b"(p1) : "a"(num)) - -#define DEFN_SYSCALL2(__res, num, p1, p2) \ - __asm__ __volatile__("INT $0x80" \ - : "=a"(__res), "+b"(p1), "+c"(p2) \ - : "a"(num)); - -#define DEFN_SYSCALL3(__res, num, p1, p2, p3) \ - __asm__ __volatile__("INT $0x80" \ - : "=a"(__res), "+b"(p1), "+c"(p2), "+d"(p3) \ - : "a"(num)) - -#define DEFN_SYSCALL4(__res, num, p1, p2, p3, p4) \ - __asm__ __volatile__("INT $0x80" \ - : "=a"(__res), "+b"(p1), "+c"(p2), "+d"(p3), "+S"(p4) \ - : "a"(num)) - -#define DEFN_SYSCALL5(fn, num, P1, P2, P3, P4, P5) \ - int syscall_##fn(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) \ - { \ - int __res; \ - __asm__ __volatile__("push eax\n movl %2,%%ebx; INT 0x80; pop %%ebx" \ - : "=a"(__res) \ - : "0"(num), "r"((int)(p1)), "c"((int)(p2)), \ - "d"((int)(p3)), "S"((int)(p4)), \ - "D"((int)(p5))); \ - return __res; \ - } - -#define __NR_exit 1 - -#define __NR_fork 2 - -#define __NR_read 3 - -#define __NR_write 4 - -#define __NR_open 5 - -#define __NR_close 6 - -#define __NR_waitpid 7 - -#define __NR_creat 8 - -#define __NR_link 9 - -#define __NR_unlink 10 - -#define __NR_execve 11 - -#define __NR_chdir 12 - -#define __NR_time 13 - -#define __NR_mknod 14 - -#define __NR_chmod 15 - -#define __NR_lchown 16 - -#define __NR_stat 18 - -#define __NR_lseek 19 - -#define __NR_getpid 20 - -#define __NR_mount 21 - -#define __NR_oldumount 22 - -#define __NR_setuid 23 - -#define __NR_getuid 24 - -#define __NR_stime 25 - -#define __NR_ptrace 26 - -#define __NR_alarm 27 - -#define __NR_fstat 28 - -#define __NR_pause 29 - -#define __NR_utime 30 - -#define __NR_access 33 - -#define __NR_nice 34 - -#define __NR_sync 36 - -#define __NR_kill 37 - -#define __NR_rename 38 - -#define __NR_mkdir 39 - -#define __NR_rmdir 40 - -#define __NR_dup 41 - -#define __NR_pipe 42 - -#define __NR_times 43 - -#define __NR_brk 45 - -#define __NR_setgid 46 - -#define __NR_getgid 47 - -#define __NR_signal 48 - -#define __NR_geteuid 49 - -#define __NR_getegid 50 - -#define __NR_acct 51 - -#define __NR_umount 52 - -#define __NR_ioctl 54 - -#define __NR_fcntl 55 - -#define __NR_setpgid 57 - -#define __NR_olduname 59 - -#define __NR_umask 60 - -#define __NR_chroot 61 - -#define __NR_ustat 62 - -#define __NR_dup2 63 - -#define __NR_getppid 64 - -#define __NR_getpgrp 65 - -#define __NR_setsid 66 - -#define __NR_sigaction 67 - -#define __NR_sgetmask 68 - -#define __NR_ssetmask 69 - -#define __NR_setreuid 70 - -#define __NR_setregid 71 - -#define __NR_sigsuspend 72 - -#define __NR_sigpending 73 - -#define __NR_sethostname 74 - -#define __NR_setrlimit 75 - -#define __NR_getrlimit 76 - -#define __NR_getrusage 77 - -#define __NR_gettimeofday 78 - -#define __NR_settimeofday 79 - -#define __NR_getgroups 80 - -#define __NR_setgroups 81 - -#define __NR_symlink 83 - -#define __NR_lstat 84 - -#define __NR_readlink 85 - -#define __NR_uselib 86 - -#define __NR_swapon 87 - -#define __NR_reboot 88 - -#define __NR_readdir 89 - -#define __NR_mmap 90 - -#define __NR_munmap 91 - -#define __NR_truncate 92 - -#define __NR_ftruncate 93 - -#define __NR_fchmod 94 - -#define __NR_fchown 95 - -#define __NR_getpriority 96 - -#define __NR_setpriority 97 - -#define __NR_statfs 99 - -#define __NR_fstatfs 100 - -#define __NR_ioperm 101 - -#define __NR_socketcall 102 - -#define __NR_syslog 103 - -#define __NR_setitimer 104 - -#define __NR_getitimer 105 - -#define __NR_newstat 106 - -#define __NR_newlstat 107 - -#define __NR_newfstat 108 - -#define __NR_uname 109 - -#define __NR_iopl 110 - -#define __NR_vhangup 111 - -#define __NR_idle 112 - -#define __NR_vm86old 113 - -#define __NR_wait4 114 - -#define __NR_swapoff 115 - -#define __NR_sysinfo 116 - -#define __NR_ipc 117 - -#define __NR_fsync 118 - -#define __NR_sigreturn 119 - -#define __NR_clone 120 - -#define __NR_setdomainname 121 - -#define __NR_newuname 122 - -#define __NR_modify_ldt 123 - -#define __NR_adjtimex 124 - -#define __NR_mprotect 125 - -#define __NR_sigprocmask 126 - -#define __NR_create_module 127 - -#define __NR_init_module 128 - -#define __NR_delete_module 129 - -#define __NR_get_kernel_syms 130 - -#define __NR_quotactl 131 - -#define __NR_getpgid 132 - -#define __NR_fchdir 133 - -#define __NR_bdflush 134 - -#define __NR_sysfs 135 - -#define __NR_personality 136 - -#define __NR_setfsuid 138 - -#define __NR_setfsgid 139 - -#define __NR_llseek 140 - -#define __NR_getdents 141 - -#define __NR_select 142 - -#define __NR_flock 143 - -#define __NR_msync 144 - -#define __NR_readv 145 - -#define __NR_writev 146 - -#define __NR_getsid 147 - -#define __NR_fdatasync 148 - -#define __NR_sysctl 149 - -#define __NR_mlock 150 - -#define __NR_munlock 151 - -#define __NR_mlockall 152 - -#define __NR_munlockall 153 - -#define __NR_sched_setparam 154 - -#define __NR_sched_getparam 155 - -#define __NR_sched_setscheduler 156 - -#define __NR_sched_getscheduler 157 - -#define __NR_sched_yield 158 - -#define __NR_sched_get_priority_max 159 - -#define __NR_sched_get_priority_min 160 - -#define __NR_sched_rr_get_interval 161 - -#define __NR_nanosleep 162 - -#define __NR_mremap 163 - -#define __NR_setresuid 164 - -#define __NR_getresuid 165 - -#define __NR_vm86 166 - -#define __NR_query_module 167 - -#define __NR_poll 168 - -#define __NR_nfsservctl 169 - -#define __NR_setresgid 170 - -#define __NR_getresgid 171 - -#define __NR_prctl 172 - -#define __NR_rt_sigreturn 173 - -#define __NR_rt_sigaction 174 - -#define __NR_rt_sigprocmask 175 - -#define __NR_rt_sigpending 176 - -#define __NR_rt_sigtimedwait 177 - -#define __NR_rt_sigqueueinfo 178 - -#define __NR_rt_sigsuspend 179 - -#define __NR_pread 180 - -#define __NR_pwrite 181 - -#define __NR_chown 182 - -#define __NR_getcwd 183 - -#define __NR_capget 184 - -#define __NR_capset 185 - -#define __NR_sigaltstack 186 - -#define __NR_sendfile 187 - -#define __NR_vfork 188 - -#define __NR_free 189 // TODO: remove me! - -#define SYSCALL_NUMBER 190 - -//----------------------------------------------------------------------------- +/// @param f The interrupt stack frame. +void syscall_handler(pt_regs *f); + +/// @brief Returns the current interrupt stack frame. +/// @return Pointer to the stack frame. +pt_regs* get_current_interrupt_stack_frame(); + +/// The exit() function causes normal process termination. +/// @param exit_code The exit code. +void sys_exit(int exit_code); + +/// @brief Read data from a file descriptor. +/// @param fd The file descriptor. +/// @param buf The buffer. +/// @param nbytes The number of bytes to read. +/// @return The number of read characters. +ssize_t sys_read(int fd, void *buf, size_t nbytes); + +/// @brief Write data into a file descriptor. +/// @param fd The file descriptor. +/// @param buf The buffer collecting data to written. +/// @param nbytes The number of bytes to write. +/// @return The number of written bytes. +ssize_t sys_write(int fd, void *buf, size_t nbytes); + +/// @brief Repositions the file offset inside a file. +/// @param fd The file descriptor of the file. +/// @param offset The offest to use for the operation. +/// @param whence The type of operation. +/// @return Upon successful completion, returns the resulting offset +/// location as measured in bytes from the beginning of the file. On +/// error, the value (off_t) -1 is returned and errno is set to +/// indicate the error. +off_t sys_lseek(int fd, off_t offset, int whence); + +/// @brief Given a pathname for a file, open() returns a file +/// descriptor, a small, nonnegative integer for use in +/// subsequent system calls. +/// @param pathname A pathname for a file. +/// @param flags Used to set the file status flags and file access modes +/// of the open file description. +/// @param mode Specifies the file mode bits be applied when a new file +/// is created. +/// @return Returns a file descriptor, a small, nonnegative integer +/// for use in subsequent system calls. +int sys_open(const char *pathname, int flags, mode_t mode); + +/// @brief +/// @param fd +/// @return +int sys_close(int fd); + +/// @brief Delete a name and possibly the file it refers to. +/// @param path A pathname for a file. +/// @return On success, zero is returned. On error, -1 is returned, and errno is set appropriately. +int sys_unlink(const char *path); + +/// @brief Suspends execution of the calling thread until a child specified +/// by pid argument has changed state. +/// @param pid The pid to wait. +/// @param status If not NULL, store status information here. +/// @param options Determines the wait behaviour. +/// @return on success, returns the process ID of the terminated +/// child; on error, -1 is returned. +pid_t sys_waitpid(pid_t pid, int *status, int options); + +/// @brief Replaces the current process image with a new process image. +/// @param f CPU registers whe calling this function. +/// @return 0 on success, -1 on error. +int sys_execve(pt_regs *f); + +/// @brief Changes the working directory. +/// @param path The new working directory. +void sys_chdir(char const *path); + +/// @brief Changes the working directory. +/// @param fd File descriptor of the new working directory. +void sys_fchdir(int fd); + +/// @brief Returns the process ID (PID) of the calling process. +/// @return The process ID. +pid_t sys_getpid(); + +///@brief Return session id of the given process. +/// If pid == 0 return the SID of the calling process +/// If pid != 0 return the SID corresponding to the process having identifier == pid +///@param pid process identifier from wich we want the SID +///@return On success return SID of the session +/// Otherwise return -1 with errno set on: EPERM or ESRCH +pid_t sys_getsid(pid_t pid); + +///@brief creates a new session if the calling process is not a +/// process group leader. The calling process is the leader of the +/// new session (i.e., its session ID is made the same as its process +/// ID). The calling process also becomes the process group leader +/// of a new process group in the session (i.e., its process group ID +/// is made the same as its process ID). +///@return On success return SID of the session just created +/// Otherwise return -1 with errno : EPERM +pid_t sys_setsid(); + +///@brief returns the group ID of the calling process. +///@return GID of the current process +pid_t sys_getgid(); + +///@brief sets the effective group ID of the calling process. +///@param pid process identifier to +///@return On success, zero is returned. +/// Otherwise returns -1 with errno set to :EINVAL or EPERM +int sys_setgid(pid_t pid); + +/// @brief Returns the parent process ID (PPID) of the calling process. +/// @return The parent process ID. +pid_t sys_getppid(); + +/// @brief Adds the increment to the priority value of the task. +/// @param increment The modifier to apply to the nice value. +/// @return The new nice value. +int sys_nice(int increment); + +/// @brief Reboots the system, or enables/disables the reboot keystroke. +/// @param magic1 fails (with the error EINVAL) unless equals LINUX_REBOOT_MAGIC1. +/// @param magic2 fails (with the error EINVAL) unless equals LINUX_REBOOT_MAGIC2. +/// @param cmd The command to send to the reboot. +/// @param arg Argument passed with some specific commands. +/// @return For the values of cmd that stop or restart the system, a +/// successful call to reboot() does not return. For the other cmd +/// values, zero is returned on success. In all cases, -1 is +/// returned on failure, and errno is set appropriately. +int sys_reboot(int magic1, int magic2, unsigned int cmd, void *arg); + +/// @brief Get current working directory. +/// @param buf The array where the CWD will be copied. +/// @param size The size of the array. +/// @return On success, returns the same pointer to buf. +/// On failure, returnr NULL, and errno is set to indicate the error. +char *sys_getcwd(char *buf, size_t size); + +/// @brief Clone the calling process, but without copying the whole address space. +/// The calling process is suspended until the new process exits or is +/// replaced by a call to `execve'. +/// @param f CPU registers whe calling this function. +/// @return Return -1 for errors, 0 to the new process, and the process ID of +/// the new process to the old process. +pid_t sys_fork(pt_regs *f); + +/// @brief Stat the file at the given path. +/// @param path Path to the file for which we are retrieving the statistics. +/// @param buf Buffer where we are storing the statistics. +/// @return 0 on success, a negative number if fails and errno is set. +int sys_stat(const char *path, stat_t *buf); + +/// @brief Retrieves information about the file at the given location. +/// @param fd The file descriptor of the file that is being inquired. +/// @param buf A structure where data about the file will be stored. +/// @return Returns a negative value on failure. +int sys_fstat(int fd, stat_t *buf); + +/// @brief Creates a new directory at the given path. +/// @param path The path of the new directory. +/// @param mode The permission of the new directory. +/// @return Returns a negative value on failure. +int sys_mkdir(const char *path, mode_t mode); + +/// @brief Removes the given directory. +/// @param path The path to the directory to remove. +/// @return Returns a negative value on failure. +int sys_rmdir(const char *path); + +/// Provide access to the directory entries. +/// @param fd The file descriptor of the directory for which we accessing +/// the entries. +/// @param dirp The buffer where de data should be placed. +/// @param count The size of the buffer. +/// @return On success, the number of bytes read is returned. On end of +/// directory, 0 is returned. On error, -1 is returned, and errno is set +/// appropriately. +int sys_getdents(int fd, dirent_t *dirp, unsigned int count); + +/// @brief Returns the current time. +/// @param time Where the time should be stored. +/// @return The current time. +time_t sys_time(time_t *time); diff --git a/mentos/inc/ui/command/commands.h b/mentos/inc/ui/command/commands.h deleted file mode 100644 index cbca97a..0000000 --- a/mentos/inc/ui/command/commands.h +++ /dev/null @@ -1,93 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file commands.h -/// @brief Prototypes of functions for the Shell. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "shell.h" - -/// @brief Prints the logo. -void cmd_logo(int argc, char **argv); - -/// @brief Returns the list of commands. -void cmd_help(int argc, char **argv); - -/// @brief Echos the given message. -void cmd_echo(int argc, char **argv); - -/// @brief Power off the machine. -void cmd_poweroff(int argc, char **argv); - -/// @brief Call the uname function. -void cmd_uname(int argc, char **argv); - -/// @brief Prints the credits. -void cmd_credits(int argc, char **argv); - -/// @brief Sleeps the thread of the shell for a given period of time. -void cmd_sleep(int argc, char **argv); - -/// @brief Shows the output of the cpuid function. -void cmd_cpuid(int argc, char **argv); - -/// @brief Lists the directory. -void cmd_ls(int argc, char **argv); - -/// @brief Move to another directory. -void cmd_cd(int argc, char **argv); - -/// @brief Creates a new directory. -void cmd_mkdir(int argc, char **argv); - -/// @brief Removes a file. -void cmd_rm(int argc, char **argv); - -/// @brief Removes a directory. -void cmd_rmdir(int argc, char **argv); - -/// @brief Show the current user name. -void cmd_whoami(int argc, char **argv); - -/// @brief Allows to test some functionalities of the kernel. -void cmd_tester(int argc, char **argv); - -/// @brief Print current working directory. -void cmd_pwd(int argc, char **argv); - -/// @brief Read content of a file. -void cmd_more(int argc, char **argv); - -/// @brief Create a new file. -void cmd_touch(int argc, char **argv); - -/// @brief Create a new file. -void cmd_newfile(int argc, char **argv); - -/// @brief Show task list. -void cmd_ps(int argc, char **argv); - -/// @brief Show date and time. -void cmd_date(int argc, char **argv); - -/// @brief Clears the screen. -void cmd_clear(int argc, char **argv); - -/// @brief Shows the PID of the shell. -void cmd_showpid(int argc, char **argv); - -/// @brief Prints the history. -void cmd_show_history(int argc, char **argv); - -/// @brief Loads the drivers. -void cmd_drv_load(int argc, char **argv); - -/// @brief Show IPC state. -void cmd_ipcs(int argc, char **argv); - -/// @brief Remove IPC resorce from id. -void cmd_ipcrm(int argc, char **argv); - -/// @brief Change the nice value. -void cmd_nice(int argc, char **argv); diff --git a/mentos/inc/ui/init/init.h b/mentos/inc/ui/init/init.h deleted file mode 100644 index 70230d3..0000000 --- a/mentos/inc/ui/init/init.h +++ /dev/null @@ -1,10 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file init.h -/// @brief Scheduler structures and functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -// TODO: doxygen comment. -int main_init(); diff --git a/mentos/inc/ui/shell/shell.h b/mentos/inc/ui/shell/shell.h deleted file mode 100644 index b6d2015..0000000 --- a/mentos/inc/ui/shell/shell.h +++ /dev/null @@ -1,77 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file shell.h -/// @brief Data structure used to implement the Shell. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "stdint.h" -#include "kernel.h" - -/// Maximum length of credentials. -#define CREDENTIALS_LENGTH 50 - -/// Maximum length of commands. -#define CMD_LEN 256 - -/// Maximum length of descriptions. -#define DESC_LEN 256 - -/// Maximum number of saved commands. -#define MAX_NUM_COM 50 - -/// Maximum length of history. -#define HST_LEN 10 - -#define KEY_UP 72 - -#define KEY_DOWN 80 - -#define KEY_LEFT 75 - -#define KEY_RIGHT 77 - -/// Pointer to the function of a commmand. -typedef void (* CommandFunction)(int argc, char **argv); - -/// @brief Holds information about a command. -typedef struct command_t -{ - /// The name of the command. - char cmdname[CMD_LEN]; - - /// The function pointer to the command. - CommandFunction function; - - /// The description of the command. - char cmddesc[DESC_LEN]; -} command_t; - -/// @brief Holds information about the user. -typedef struct userenv_t -{ - /// The username. - char username[CREDENTIALS_LENGTH]; - - /// The current path. - char cur_path[MAX_PATH_LENGTH]; - - /// The user identifier. - unsigned int uid; - - /// The group identifier. - unsigned int gid; -} userenv_t; - -/// Contains the information about the current user. -extern userenv_t current_user; - -/// @brief The shell. -int shell(int argc, char **argv, char **envp); - -/// @brief Moves the cursor left. -void move_cursor_left(void); - -/// @brief Moves the cursor right. -void move_cursor_right(void); diff --git a/mentos/inc/ui/shell/shell_login.h b/mentos/inc/ui/shell/shell_login.h deleted file mode 100644 index 3d0b458..0000000 --- a/mentos/inc/ui/shell/shell_login.h +++ /dev/null @@ -1,14 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file shell_login.h -/// @brief Functions used to manage login. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#pragma once - -#include "shell.h" -#include "stdbool.h" - -/// @brief Function used to perform login. -void shell_login(); - diff --git a/mentos/inc/version.h b/mentos/inc/version.h index b79875f..d8fc1d1 100644 --- a/mentos/inc/version.h +++ b/mentos/inc/version.h @@ -1,7 +1,7 @@ /// MentOS, The Mentoring Operating system project /// @file version.h /// @brief Version information. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #pragma once @@ -10,7 +10,10 @@ #define OS_NAME "MentOS" /// The site of the operating system. -#define OS_SITEURL "..." +#define OS_SITEURL "https://mentos-team.github.io/MentOS" + +/// The email of the reference developer. +#define OS_REF_EMAIL "enry.frak@gmail.com" /// Major version of the operating system. #define OS_MAJOR_VERSION 0 @@ -28,7 +31,6 @@ #define OS_STR(x) OS_STR_HELPER(x) /// Complete version of the operating system. -#define OS_VERSION \ - OS_STR(OS_MAJOR_VERSION) "." \ - OS_STR(OS_MINOR_VERSION) "." \ - OS_STR(OS_MICRO_VERSION) +#define OS_VERSION \ + OS_STR(OS_MAJOR_VERSION) \ + "." OS_STR(OS_MINOR_VERSION) "." OS_STR(OS_MICRO_VERSION) diff --git a/mentos/kernel.lds b/mentos/kernel.lds index db0112d..3716d68 100644 --- a/mentos/kernel.lds +++ b/mentos/kernel.lds @@ -1,52 +1,53 @@ OUTPUT_FORMAT("elf") OUTPUT_ARCH(i386) -ENTRY(kernel_entry) +ENTRY(kmain) + +KERNEL_VIRTUAL_ADDRESS = 0xC0000000; + +MEMORY { + USER_SPACE : ORIGIN = 0x00000000, LENGTH = 3072M + KERNEL_LOWMEM : ORIGIN = KERNEL_VIRTUAL_ADDRESS, LENGTH = 896M + KERNEL_HIGHMEM : ORIGIN = 0xF8000000, LENGTH = 128M +} -KERNEL_PHYSICAL_ADDRESS = 0x00100000; -LOAD_MEMORY_ADDRESS = 0x00000000; SECTIONS { - . = KERNEL_PHYSICAL_ADDRESS; - - /* Put the .multiboot_header and .text section. */ - .text : AT(ADDR(.text)) + . = KERNEL_VIRTUAL_ADDRESS; + /* Put the .text section. */ + .text . : AT(ADDR(.text)) { - _multiboot_header_start = .; - *(.multiboot_header) - _multiboot_header_end = .; - _text_start = .; - *(.text) + EXCLUDE_FILE(*boot.*.o) *(.text) _text_end = .; - } + } > KERNEL_LOWMEM /* Read-only data. */ .rodata ALIGN(4K) : AT(ADDR(.rodata)) { _rodata_start = .; - *(.rodata) + EXCLUDE_FILE(*boot.*.o) *(.rodata) _rodata_end = .; - } + } > KERNEL_LOWMEM /* Read-write data (initialized) */ .data ALIGN(4K) : AT(ADDR(.data)) { _data_start = .; - *(.data) + EXCLUDE_FILE(*boot.*.o) *(.data) _data_end = .; - } + } > KERNEL_LOWMEM /* Read-write data (uninitialized) and stack */ .bss ALIGN(4K) : AT(ADDR(.bss)) { _bss_start = .; - *(.bss) + *(.bss*) _bss_end = .; - } + } > KERNEL_LOWMEM /* Put a symbol end here, it tells us where all the kernel code/data ends, it means everything after 'end' can be used for something else. */ - end = .; + _kernel_end = .; } diff --git a/mentos/libmentos_memory.a b/mentos/libmentos_memory.a new file mode 100644 index 0000000..16ccc97 Binary files /dev/null and b/mentos/libmentos_memory.a differ diff --git a/mentos/src/boot.c b/mentos/src/boot.c new file mode 100644 index 0000000..aad9189 --- /dev/null +++ b/mentos/src/boot.c @@ -0,0 +1,299 @@ +/// MentOS, The Mentoring Operating system project +/// @file boot.c +/// @brief Bootloader. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "link_access.h" +#include "multiboot.h" +#include "mem/paging.h" +#include "sys/module.h" +#include "stdint.h" +#include "elf/elf.h" + +/// @defgroup bootloader Bootloader +/// @brief Set of functions and variables for booting the kernel. +/// @{ + +/// @brief External function implemented in `boot.S`. +/// @param stack_pointer The stack base pointer, usually at the end of the lowmem. +/// @param entry +/// @param boot_info +extern void boot_kernel(uint32_t stack_pointer, uint32_t entry, struct boot_info_t *boot_info); + +/// @brief Size of the kernel's stack. +#define KERNEL_STACK_SIZE 0x100000 + +/// Serial port for QEMU. +#define SERIAL_COM1 (0x03F8) + +/// @brief Linker symbols for where the .data section of `kernel.bin.o`. +EXTLD(kernel_bin) + +/// @brief Linker symbol for where the bootloader starts. +extern char _bootloader_start[]; +/// @brief Linker symbol for where the bootloader ends. +extern char _bootloader_end[]; + +/// @brief Boot info provided to the kmain function. +static boot_info_t boot_info; +/// @brief Boot page directory. +static page_directory_t boot_pgdir; +/// @brief Boot page tables. +static page_table_t boot_pgtables[1024]; + +static inline void __outportb(uint16_t port, uint8_t data) +{ + __asm__ __volatile__("outb %%al, %%dx" ::"a"(data), "d"(port)); +} + +static inline void __debug_putchar(char c) +{ +#if (defined(DEBUG_STDIO) || defined(DEBUG_LOG)) + __outportb(SERIAL_COM1, c); +#endif +} + +static inline void __debug_puts(char *s) +{ +#if (defined(DEBUG_STDIO) || defined(DEBUG_LOG)) + while ((*s) != 0) + __outportb(SERIAL_COM1, *s++); +#endif +} + +/// @brief Align memory to the specified value (round up). +static inline uint32_t __align_rup(uint32_t addr, uint32_t value) +{ + uint32_t reminder = (addr % value); + return addr + (reminder ? (value - reminder) : 0); +} + +/// @brief Align memory to the specified value (round down). +static inline uint32_t __align_rdown(uint32_t addr, uint32_t value) +{ + return addr - (addr % value); +} + +static void __setup_pages(uint32_t pfn_virt_start, uint32_t pfn_phys_start, uint32_t pfn_count) +{ + uint32_t base_pgtable = pfn_virt_start / 1024; + uint32_t base_pgentry = pfn_virt_start % 1024; + + uint32_t pg_offset = 0; + for (int i = base_pgtable; i < 1024 && pfn_count; i++) { + page_table_t *table = boot_pgtables + i; + + uint32_t pgentry_start = (i == base_pgtable) ? base_pgentry : 0; + + for (int j = pgentry_start; j < 1024 && pfn_count; j++, pfn_count--) { + table->pages[j].frame = pfn_phys_start + pg_offset++; + table->pages[j].rw = 1; + table->pages[j].present = 1; + table->pages[j].global = 0; + table->pages[j].user = 0; + } + + boot_pgdir.entries[i].rw = 1; + boot_pgdir.entries[i].present = 1; + boot_pgdir.entries[i].available = 1; + boot_pgdir.entries[i].frame = ((uint32_t)table) >> 12u; + } +} + +/* + * Setup paging mapping all the low memory to two places: + * one is the physical address of the memory itself + * the other is in the virtual kernel address space + * */ +static inline void __setup_boot_paging() +{ + uint32_t kernel_base_phy_page = boot_info.kernel_phy_start >> 12U; + uint32_t kernel_base_virt_page = boot_info.kernel_start >> 12U; + + uint32_t lowmem_last_phy_page = ((uint32_t)(boot_info.lowmem_phy_end - 1)) >> 12U; + + uint32_t num_pages = lowmem_last_phy_page - kernel_base_phy_page + 1; + + // Map lowmem physical pages also to their physical address (to keep bootloader working) + __setup_pages(0, 0, lowmem_last_phy_page); + + // Setup kernel virtual address space + lowmem + __setup_pages(kernel_base_virt_page, kernel_base_phy_page, num_pages); +} + +/// @brief Extract the starting and ending address of the kernel. +/// @param elf_hdr The elf header of the kernel. +/// @param virt_low Output variable where we store the lowest address of the kernel. +/// @param virt_high Output variable where we store the highest address of the kernel. +static void __get_kernel_low_high(elf_header_t *elf_hdr, + uint32_t *virt_low, + uint32_t *virt_high) +{ + // Prepare a pointer to a program header. + elf_program_header_t *program_header; + // Compute the offset for accessing the program headers. + uint32_t offset = (uint32_t)elf_hdr + (uint32_t)elf_hdr->phoff; + // In this two variables we will store the start and end addresses of the segment. + uint32_t segment_start, segment_end; + // Iterate for each program header. + for (int i = 0; i < elf_hdr->phnum; i++) { + program_header = (elf_program_header_t *)(offset + elf_hdr->phentsize * i); + if (program_header->type == PT_LOAD) { + // Take the start and end addresses of the segment from the program header. + segment_start = program_header->vaddr; + segment_end = segment_start + program_header->memsz; + // Take the lowest and highest virtual address. + *virt_low = min(*virt_low, segment_start); + *virt_high = max(*virt_high, segment_end); + } + } +} + +/// @brief Returns the first address after the modules. +/// @param header The multiboot info structure from which we extract the info. +/// @return The address after the modules. +static inline uint32_t __get_address_after_modules(multiboot_info_t *header) +{ + // We set by default the address to the ending physical address + // of the bootloader. + uint32_t addr = boot_info.bootloader_phy_end; + // Get the pointer to the mods. + multiboot_module_t *mod = (multiboot_module_t *)header->mods_addr; + for (int i = 0; (i < header->mods_count) && (i < MAX_MODULES); ++i, ++mod) { + addr = max(max(addr, mod->mod_start), mod->mod_end); + } + return addr; +} + +/// @brief Relocate the kernel image. +/// @param elf_hdr The elf header of the kernel. +static inline void __relocate_kernel_image(elf_header_t *elf_hdr) +{ + // Support variables. + elf_program_header_t *program_header; + char *kernel_start, *virtual_address, *physical_address; + uint32_t offset, valid_size; + + // Get the elf file starting address. + kernel_start = (char *)elf_hdr; + // Compute the offset for accessing the program headers. + offset = (uint32_t)kernel_start + (uint32_t)elf_hdr->phoff; + // Iterate over the program headers. + for (int i = 0; i < elf_hdr->phnum; i++) { + // Get the program header. + program_header = (elf_program_header_t *)(offset + elf_hdr->phentsize * i); + // Get the virtual address of the program header. + virtual_address = (char *)program_header->vaddr; + // Get the physical address of the program header. + physical_address = (char *)(kernel_start + program_header->offset); + // Move only the loadable segments. + if (program_header->type == PT_LOAD) { + // Get the valid size of the segment by taking the minimum between + // the size in bytes of the segment in the file image, in memory. + valid_size = min(program_header->filesz, program_header->memsz); + // Copy the physical data of the image to the corresponding virtual address. + for (int j = 0; j < valid_size; j++) + virtual_address[j] = physical_address[j]; + // Set to 0 parts not present in memory! + for (int j = valid_size; j < program_header->memsz; j++) + virtual_address[j] = 0; + } + } +} + +/// @brief Entry point of the bootloader. +/// @param magic The magic number coming from the multiboot assembly code. +/// @param header Multiboot header provided by the bootloader. +/// @param esp The initial stack pointer. +void boot_main(uint32_t magic, multiboot_info_t *header, uint32_t esp) +{ + __debug_puts("\nbootloader: Start...\n"); + elf_header_t *elf_hdr = (elf_header_t *)LDVAR(kernel_bin); + + // Get the physical addresses of where the kernel starts and ends. + uint32_t boot_start = (uint32_t)_bootloader_start; + uint32_t boot_end = (uint32_t)_bootloader_end; + + // Extract the lowest and highest address of the kernel. + uint32_t kernel_virt_low = 0xFFFFFFFF; + uint32_t kernel_virt_high = 0; + __get_kernel_low_high(elf_hdr, &kernel_virt_low, &kernel_virt_high); + + // Initialize the boot_info_t structure. + __debug_puts("bootloader: Initializing the boot_info structure...\n"); + boot_info.magic = magic; + boot_info.bootloader_phy_start = boot_start; + boot_info.bootloader_phy_end = boot_end; + boot_info.kernel_start = kernel_virt_low; + boot_info.kernel_end = kernel_virt_high; + boot_info.kernel_size = kernel_virt_high - kernel_virt_low; + boot_info.multiboot_header = header; + + // Get the address after the modules. + boot_info.module_end = __get_address_after_modules(header); + + // Get the starting address of the physical pages at the end of the modules. + uint32_t kernel_phy_page_start = __align_rup(boot_info.module_end, PAGE_SIZE); + // Get the starting address of the virtual pages. + uint32_t kernel_virt_page_start = __align_rdown(kernel_virt_low, PAGE_SIZE); + + // Compute the absolute offset of the first virtual page, by subtracting + // the starting address of the virtual pages and the lowest virtual address + // of the kernel. + uint32_t kernel_page_offset = kernel_virt_page_start - kernel_virt_low; + + // If we add the offset we computed earlier to the physical address where + // the modules ends, we obtain the starting address of the physical memory. + boot_info.kernel_phy_start = kernel_phy_page_start + kernel_page_offset; + // The ending address of the physical memory is just the start plus the + // size of the kernel (virt_high - virt_low). + boot_info.kernel_phy_end = boot_info.kernel_phy_start + boot_info.kernel_size; + + boot_info.lowmem_phy_start = __align_rup(boot_info.kernel_phy_end, PAGE_SIZE); + boot_info.lowmem_phy_end = 896 * 1024 * 1024; // 896 MB of low memory max + + uint32_t lowmem_size = boot_info.lowmem_phy_end - boot_info.lowmem_phy_start; + + boot_info.lowmem_start = __align_rup(boot_info.kernel_end, PAGE_SIZE); + boot_info.lowmem_end = boot_info.lowmem_start + lowmem_size; + + boot_info.highmem_phy_start = boot_info.lowmem_phy_end; + boot_info.highmem_phy_end = header->mem_upper * 1024; + boot_info.stack_end = boot_info.lowmem_end; + + // Setup the page directory and page tables for the boot. + __debug_puts("bootloader: Setting up paging...\n"); + __setup_boot_paging(); + + // Switch to the newly created page directory. + __debug_puts("bootloader: Switching page directory...\n"); + paging_switch_directory(&boot_pgdir); + + // Enable paging. + __debug_puts("bootloader: Enabling paging...\n"); + paging_enable(); + + // Reserve space for the kernel stack at the end of lowmem. + boot_info.stack_base = boot_info.lowmem_end; + boot_info.lowmem_phy_end = boot_info.lowmem_phy_end - KERNEL_STACK_SIZE; + boot_info.lowmem_end = boot_info.lowmem_end - KERNEL_STACK_SIZE; + + __debug_puts("bootloader: Relocating kernel image...\n"); + __relocate_kernel_image(elf_hdr); + +#if 0 + for (int i = 0; i < elf_hdr->shnum; i++) { + struct elf_section_header *section_header = + (elf_section_header_t *)(LDVAR(kernel_bin) + elf_hdr->shoff + elf_hdr->shentsize * i); + for (int j = 0; j < section_header->; j++) { + ((char *)section_header->vaddr)[j] = (LDVAR(kernel_bin) + section_header->offset)[j]; + } + } +#endif + + __debug_puts("bootloader: Calling `boot_kernel`...\n\n"); + boot_kernel(boot_info.stack_base, elf_hdr->entry, &boot_info); +} + +/// @} \ No newline at end of file diff --git a/mentos/src/descriptor_tables/exception.asm b/mentos/src/descriptor_tables/exception.S similarity index 86% rename from mentos/src/descriptor_tables/exception.asm rename to mentos/src/descriptor_tables/exception.S index ba386cc..9ac39eb 100644 --- a/mentos/src/descriptor_tables/exception.asm +++ b/mentos/src/descriptor_tables/exception.S @@ -1,12 +1,14 @@ ; MentOS, The Mentoring Operating system project ; @file exception.asm ; @brief -; @copyright (c) 2019 This file is distributed under the MIT License. +; @copyright (c) 2014-2021 This file is distributed under the MIT License. ; See LICENSE.md for details. +extern isr_handler + ; Macro used to define a ISR which does not push an error code. %macro ISR_NOERR 1 - [GLOBAL INT_%1] + global INT_%1 INT_%1: cli ; A normal ISR stub that pops a dummy error code to keep a @@ -18,13 +20,18 @@ ; Macro used to define a ISR which pushes an error code. %macro ISR_ERR 1 - [GLOBAL INT_%1] + global INT_%1 INT_%1: cli push %1 jmp isr_common %endmacro +; ----------------------------------------------------------------------------- +; SECTION (text) +; ----------------------------------------------------------------------------- +section .text + ; Standard X86 interrupt service routines ISR_NOERR 0 ISR_NOERR 1 @@ -46,7 +53,6 @@ ISR_NOERR 16 ISR_NOERR 17 ISR_NOERR 18 ISR_NOERR 19 - ISR_NOERR 20 ISR_NOERR 21 ISR_NOERR 22 @@ -62,8 +68,6 @@ ISR_NOERR 31 ISR_NOERR 80 -[EXTERN isr_handler] - isr_common: ; Save all registers (eax, ecx, edx, ebx, esp, ebp, esi, edi) pusha @@ -91,7 +95,7 @@ isr_common: cld ; Call the interrupt handler. - push esp; + push esp call isr_handler add esp, 0x4 @@ -107,5 +111,6 @@ isr_common: ; Cleanup error code and IRQ # add esp, 0x8 + iret ; pops 5 things at once: ; CS, EIP, EFLAGS, SS, and ESP diff --git a/mentos/src/descriptor_tables/exception.c b/mentos/src/descriptor_tables/exception.c index e346ebc..571790a 100644 --- a/mentos/src/descriptor_tables/exception.c +++ b/mentos/src/descriptor_tables/exception.c @@ -1,93 +1,101 @@ /// MentOS, The Mentoring Operating system project -/// @file isr.c +/// @file exception.c /// @brief Functions which manage the Interrupt Service Routines (ISRs). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "isr.h" -#include "idt.h" +#include "system/panic.h" +#include "descriptor_tables/isr.h" +#include "descriptor_tables/idt.h" #include "stdio.h" -#include "debug.h" +#include "misc/debug.h" // Default error messages for exceptions. -static const char *exception_messages[32] = { "Division by zero", - "Debug", - "Non-maskable interrupt", - "Breakpoint", - "Detected overflow", - "Out-of-bounds", - "Invalid opcode", - "No coprocessor", - "Double fault", - "Coprocessor segment overrun", - "Bad TSS", - "Segment not present", - "Stack fault", - "General protection fault", - "Page fault", - "Unknown interrupt", - "Coprocessor fault", - "Alignment check", - "Machine check", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Reserved", - "Security exception", - "Triple fault" }; +static const char *exception_messages[32] = { + "Division by zero", + "Debug", + "Non-maskable interrupt", + "Breakpoint", + "Detected overflow", + "Out-of-bounds", + "Invalid opcode", + "No coprocessor", + "Double fault", + "Coprocessor segment overrun", + "Bad TSS", + "Segment not present", + "Stack fault", + "General protection fault", + "Page fault", + "Unknown interrupt", + "Coprocessor fault", + "Alignment check", + "Machine check", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Security exception", + "Triple fault" +}; -// Array of interrupt service routines for execptions and interrupts. +/// @brief Array of interrupt service routines for execptions and interrupts. static interrupt_handler_t isr_routines[IDT_SIZE]; +/// @brief Descriptions of routines. static char *isr_routines_description[IDT_SIZE]; -// Default handler for exceptions. -void default_isr_handler(pt_regs *f) +/// @brief Default handler for exceptions. +/// @param f CPU registers when calling this function. +static inline void default_isr_handler(pt_regs *f) { - uint32_t irq_line = f->int_no; + uint32_t irq_line = f->int_no; - printf("Kernel PANIC!\n no handler for execption [%d]\n", irq_line); - printf("Description: %s\n", (irq_line < 32) ? - exception_messages[irq_line] : - "no description"); - // Stop kernel execution. - while (1) - ; + dbg_print_regs(f); - // TODO: call the kernel panic method! + printf("Kernel PANIC!\n no handler for execption [%d]\n", irq_line); + printf("Description: %s\n", (irq_line < 32) ? exception_messages[irq_line] : "no description"); + // Stop kernel execution. + + kernel_panic("Kernel PANIC!\n no handler for execption"); + + while (1) {} +} + +/// @brief Interrupt Service Routines handler called from the assembly. +/// @param f CPU registers when calling this function. +void isr_handler(pt_regs *f) +{ + uint32_t isr_number = f->int_no; + if (isr_number != 80) { + // pr_default("calling ISR %d\n", isr_number); + } + // pr_default("calling ISR %d\n", isr_number); + isr_routines[isr_number](f); + // pr_default("end calling ISR %d\n", isr_number); } void isrs_init() { - // Setting the default_isr_handler as default handler. - for (uint32_t i = 0; i < IDT_SIZE; ++i) { - isr_routines[i] = default_isr_handler; - } + // Setting the default_isr_handler as default handler. + for (uint32_t i = 0; i < IDT_SIZE; ++i) { + isr_routines[i] = default_isr_handler; + } } -int isr_install_handler(uint32_t i, interrupt_handler_t handler, - char *description) +int isr_install_handler(uint32_t i, interrupt_handler_t handler, char *description) { - // Sanity check. - if (!(i >= 0 && i <= 31) && i != 80) { - return -1; - } - isr_routines[i] = handler; - isr_routines_description[i] = description; - return 0; -} - -void isr_handler(pt_regs *f) -{ - uint32_t isr_number = f->int_no; - if (isr_number != 80) { - dbg_print("calling ISR %d\n", isr_number); - } - isr_routines[isr_number](f); + // Sanity check. + if (i > 31 && i != 80) { + return -1; + } + isr_routines[i] = handler; + isr_routines_description[i] = description; + return 0; } diff --git a/mentos/src/descriptor_tables/gdt.S b/mentos/src/descriptor_tables/gdt.S new file mode 100644 index 0000000..ea5c6de --- /dev/null +++ b/mentos/src/descriptor_tables/gdt.S @@ -0,0 +1,30 @@ +; MentOS, The Mentoring Operating system project +; @file gdt.asm +; @brief +; @copyright (c) 2014-2021 This file is distributed under the MIT License. +; See LICENSE.md for details. + +global gdt_flush ; Allows the C code to call gdt_flush(). + +; ----------------------------------------------------------------------------- +; SECTION (text) +; ----------------------------------------------------------------------------- +section .text + +gdt_flush: + mov eax, [esp+4] ; Get the pointer to the GDT, passed as a parameter. + lgdt [eax] ; Load the new GDT pointer + + ; The data segments selectors (registers), can be easily modified using + ; simple mov instruction, but the cs can't be used with mov, so you use: + jmp 0x08:flush + ; to load the segment configurations into the the code segment selector. + +flush: + mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment + mov ds, ax ; Load all data segment selectors + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + ret \ No newline at end of file diff --git a/mentos/src/descriptor_tables/gdt.asm b/mentos/src/descriptor_tables/gdt.asm deleted file mode 100644 index df68538..0000000 --- a/mentos/src/descriptor_tables/gdt.asm +++ /dev/null @@ -1,23 +0,0 @@ -; MentOS, The Mentoring Operating system project -; @file gdt.asm -; @brief -; @copyright (c) 2019 This file is distributed under the MIT License. -; See LICENSE.md for details. - -[GLOBAL gdt_flush] ; Allows the C code to call gdt_flush(). - -gdt_flush: - mov eax, [esp+4] ; Get the pointer to the GDT, passed as a parameter. - lgdt [eax] ; Load the new GDT pointer - - mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment - mov ds, ax ; Load all data segment selectors - mov es, ax - mov fs, ax - mov gs, ax - mov ss, ax - - jmp 0x08:.flush ; 0x08 is the offset to our code segment: Far jump! - -.flush: - ret \ No newline at end of file diff --git a/mentos/src/descriptor_tables/gdt.c b/mentos/src/descriptor_tables/gdt.c index 16edba2..039a0bc 100644 --- a/mentos/src/descriptor_tables/gdt.c +++ b/mentos/src/descriptor_tables/gdt.c @@ -1,11 +1,12 @@ /// MentOS, The Mentoring Operating system project /// @file gdt.c /// @brief Functions which manage the Global Descriptor Table (GDT). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "gdt.h" -#include "tss.h" +#include "misc/debug.h" +#include "descriptor_tables/gdt.h" +#include "descriptor_tables/tss.h" /// The maximum dimension of the GDT. #define GDT_SIZE 10 @@ -23,79 +24,166 @@ gdt_pointer_t gdt_pointer; void init_gdt() { - // Prepare GDT vector. - for (uint32_t it = 0; it < GDT_SIZE; ++it) { - gdt[it].limit_low = 0; - gdt[it].base_low = 0; - gdt[it].base_middle = 0; - gdt[it].access = 0; - gdt[it].granularity = 0; - gdt[it].base_high = 0; - } + // BEWARE: Look below for a deeper explanation. - // Setup the GDT pointer and limit. - // We have six entries in the GDT: - // - Two for kernel mode. - // - Two for user mode. - // - The NULL descriptor. - // - And one for the TSS (task state segment). - // The limit is the last valid byte from the start of the GDT. - // i.e. the size of the GDT - 1. - gdt_pointer.limit = sizeof(gdt_descriptor_t) * 6 - 1; - gdt_pointer.base = (uint32_t)&gdt; + // Prepare GDT vector. + for (uint32_t it = 0; it < GDT_SIZE; ++it) { + gdt[it].limit_low = 0; + gdt[it].base_low = 0; + gdt[it].base_middle = 0; + gdt[it].access = 0; + gdt[it].granularity = 0; + gdt[it].base_high = 0; + } - // ------------------------------------------------------------------------ - // NULL - // ------------------------------------------------------------------------ - gdt_set_gate(0, 0, 0, 0, 0); + // Setup the GDT pointer and limit. + // We have six entries in the GDT: + // - Two for kernel mode. + // - Two for user mode. + // - The NULL descriptor. + // - And one for the TSS (task state segment). + // The limit is the last valid byte from the start of the GDT. + // i.e. the size of the GDT - 1. + gdt_pointer.limit = sizeof(gdt_descriptor_t) * 6 - 1; + gdt_pointer.base = (uint32_t)&gdt; - // ------------------------------------------------------------------------ - // CODE - // ------------------------------------------------------------------------ - // The base address is 0, the limit is 4GBytes, it uses 4KByte - // granularity, uses 32-bit opcodes, and is a Code Segment descriptor. - gdt_set_gate(1, 0, 0xFFFFFFFF, PRESENT | KERNEL | CODE | 0x0A, - GRANULARITY | SZBITS | 0x0F); + // ------------------------------------------------------------------------ + // NULL + // ------------------------------------------------------------------------ + gdt_set_gate(0, 0, 0, 0, 0); - // ------------------------------------------------------------------------ - // DATA - // ------------------------------------------------------------------------ - // It's EXACTLY the same as our code segment, but the descriptor type in - // this entry's access byte says it's a Data Segment. - gdt_set_gate(2, 0, 0xFFFFFFFF, PRESENT | KERNEL | DATA | 0x02, - GRANULARITY | SZBITS | 0x0F); + // ------------------------------------------------------------------------ + // CODE + // ------------------------------------------------------------------------ + // The base address is 0, the limit is 4GBytes, it uses 4KByte + // granularity, uses 32-bit opcodes, and is a Code Segment descriptor. + gdt_set_gate( + 1, + 0, + 0xFFFFFFFF, + GDT_PRESENT | GDT_KERNEL | GDT_CODE | GDT_RW, + GDT_GRANULARITY | GDT_OPERAND_SIZE); - // ------------------------------------------------------------------------ - // USER MODE CODE - // ------------------------------------------------------------------------ - gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, GRANULARITY | SZBITS | 0x0F); + // ------------------------------------------------------------------------ + // DATA + // ------------------------------------------------------------------------ + // It's EXACTLY the same as our code segment, but the descriptor type in + // this entry's access byte says it's a Data Segment. + gdt_set_gate( + 2, + 0, + 0xFFFFFFFF, + GDT_PRESENT | GDT_KERNEL | GDT_DATA, + GDT_GRANULARITY | GDT_OPERAND_SIZE); - // ------------------------------------------------------------------------ - // USER MODE DATA - // ------------------------------------------------------------------------ - gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, GRANULARITY | SZBITS | 0x0F); + // ------------------------------------------------------------------------ + // USER MODE CODE + // ------------------------------------------------------------------------ + gdt_set_gate( + 3, + 0, + 0xFFFFFFFF, + GDT_PRESENT | GDT_USER | GDT_CODE | GDT_RW, + GDT_GRANULARITY | GDT_OPERAND_SIZE); - // Initialize the TSS - tss_init(5, 0x10, 0x0); + // ------------------------------------------------------------------------ + // USER MODE DATA + // ------------------------------------------------------------------------ + gdt_set_gate( + 4, + 0, + 0xFFFFFFFF, + GDT_PRESENT | GDT_USER | GDT_DATA, + GDT_GRANULARITY | GDT_OPERAND_SIZE); - // Inform the CPU about the changes on the GDT. - gdt_flush((uint32_t)&gdt_pointer); - tss_flush(); + // Initialize the TSS + tss_init(5, 0x10); + + // Inform the CPU about the changes on the GDT. + gdt_flush((uint32_t)&gdt_pointer); + + // Inform the CPU about the changes on the TSS. + tss_flush(); } -void gdt_set_gate(uint8_t index, uint32_t base, uint32_t limit, uint8_t _access, - uint8_t _granul) +void gdt_set_gate(uint8_t index, uint32_t base, uint32_t limit, uint8_t access, uint8_t granul) { - // Setup the descriptor base address. - gdt[index].base_low = (base & 0xFFFF); - gdt[index].base_middle = (base >> 16) & 0xFF; - gdt[index].base_high = (base >> 24) & 0xFF; + // Setup the descriptor base address. + gdt[index].base_low = (base & 0xFFFFU); + gdt[index].base_middle = (base >> 16U) & 0xFFU; + gdt[index].base_high = (base >> 24U) & 0xFFU; - // Setup the descriptor limits. - gdt[index].limit_low = (limit & 0xFFFF); - gdt[index].granularity = (limit >> 16) & 0x0F; + // Setup the descriptor limits. + gdt[index].limit_low = (limit & 0xFFFFU); + gdt[index].granularity = (limit >> 16U) & 0x0FU; - // Finally, set up the granularity and access flags. - gdt[index].access = _access; - gdt[index].granularity |= _granul & 0xF0; + // Finally, set up the granularity and access flags. + gdt[index].granularity |= granul & 0xF0U; + gdt[index].access = access; + pr_debug( + "gdt[%2d] = {.low=0x%x, .mid=0x%x, .high=0x%x, .access=0x%x, .granul=0x%x}\n", + index, gdt[index].base_low, gdt[index].base_middle, + gdt[index].base_high, gdt[index].access, gdt[index].granularity); } + +// +// == VIRTUAL MEMORY SCHEMES ================================================== +// x86 supports two virtual memory schemes: +// segmentation (mandatory): managed using the segment table, GDT. +// paging (optional) : managed using the page table, PDT. +// Most operating systems want to to use paging and don't want the +// segmentation, but its mandatory and can't just be disabled. +// +// So the trick is to disable its effect as it wasn't there. This can usually +// be done by creating 4 large overlapped segments descriptors (beside the +// null segment): +// segment index 0 : null segment descriptor +// segment index 1 : CODE segment desc. for the privileged (kernel) mode +// segment index 2 : DATA segment desc. for the privileged (kernel) mode +// segment index 3 : CODE segment desc. for the non-privileged (user) mode +// segment index 4 : DATA segment desc. for the non-privileged (user) mode +// +// all these segments starts from 0x00000000 up to 0xffffffff, so you end +// up with overlapped large segments that is privileged code and data, and +// non-privileged code and data in the same time. This should open up the +// virtual memory and disable the segmentation effect. +// +// The processor uses the segment selectors (segment registers cs, ds, ss ...) +// to find out the right segment (once again, the segmentation is must). +// +// == SEGMENT SELECTOR ======================================================== +// Every segment selector is 16 bit size and has the following layout (source): +// |15 3| 2|1 0| +// |----- Index (13-bit) ----- | TI | RPL | +// where TI is the Table Indicator: +// 0 - GDT +// 1 - LDT +// and RPL encodes in 2 bits the Requestor Privilege Level (RPL): +// 00 - Highest +// 01 +// 10 +// 11 - Lowest +// Regarding the `privilege level`, x86 supports 4 levels, but only two of them +// are actually used (00 highest, and 11 lowest). +// +// The remaining 13 bits indicates the segment index. +// +// == GDT_FLUSH =============================================================== +// If you look in gdt.S, you will see a `jmp 0x08` at the end of the gdt_flush. +// Now, if you interpret the 0x08 that is loaded in cs, it will be in binary: +// | 0000000000001| 0| 11| +// | index 3 (code) | GDT| privileged| +// +// and the 0x10 that is loaded in ds, ss, ... : +// | 0000000000010| 0| 11| +// | index 3 (code) | GDT| privileged| +// +// == SS of a USER MODE PROGRAM =============================================== +// If you read the segment selectors of any user mode program you should see +// that the cs value is 27 (0x1b) which means: +// | 0000000000011| 0| 11| +// | index 3 (code) | GDT| non-privileged| +// and the data selectors ds, ss, ..., should store 35 (0x23): +// | 0000000000100| 0| 11| +// | index 4 (data) | GDT| non-privileged| +// \ No newline at end of file diff --git a/mentos/src/descriptor_tables/idt.S b/mentos/src/descriptor_tables/idt.S new file mode 100644 index 0000000..3c4db2f --- /dev/null +++ b/mentos/src/descriptor_tables/idt.S @@ -0,0 +1,17 @@ +; MentOS, The Mentoring Operating system project +; @file idt.asm +; @brief +; @copyright (c) 2014-2021 This file is distributed under the MIT License. +; See LICENSE.md for details. + +global idt_flush ; Allows the C code to call idt_flush(). + +; ----------------------------------------------------------------------------- +; SECTION (text) +; ----------------------------------------------------------------------------- +section .text + +idt_flush: + mov eax, [esp+4] ; Get the pointer to the IDT, passed as a parameter. + lidt [eax] ; Load the IDT pointer. + ret \ No newline at end of file diff --git a/mentos/src/descriptor_tables/idt.asm b/mentos/src/descriptor_tables/idt.asm deleted file mode 100644 index 66cb14c..0000000 --- a/mentos/src/descriptor_tables/idt.asm +++ /dev/null @@ -1,12 +0,0 @@ -; MentOS, The Mentoring Operating system project -; @file idt.asm -; @brief -; @copyright (c) 2019 This file is distributed under the MIT License. -; See LICENSE.md for details. - -[GLOBAL idt_flush] ; Allows the C code to call idt_flush(). - -idt_flush: - mov eax, [esp+4] ; Get the pointer to the IDT, passed as a parameter. - lidt [eax] ; Load the IDT pointer. - ret \ No newline at end of file diff --git a/mentos/src/descriptor_tables/idt.c b/mentos/src/descriptor_tables/idt.c index 3823834..a4ae9de 100644 --- a/mentos/src/descriptor_tables/idt.c +++ b/mentos/src/descriptor_tables/idt.c @@ -1,14 +1,15 @@ /// MentOS, The Mentoring Operating system project /// @file idt.c /// @brief Functions which manage the Interrupt Descriptor Table (IDT). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "idt.h" -#include "gdt.h" -#include "debug.h" +#include "descriptor_tables/idt.h" +#include "descriptor_tables/gdt.h" +#include "descriptor_tables/isr.h" /// @brief This function is in idt.asm. +/// @param idt_pointer Address of the idt. extern void idt_flush(uint32_t idt_pointer); /// The IDT itself. @@ -17,113 +18,108 @@ static idt_descriptor_t idt_table[IDT_SIZE]; /// Pointer structure to give to the CPU. idt_pointer_t idt_pointer; -// 1000 0000 -#define IDT_PRESENT 0x80 -// 0000 0000 -#define IDT_KERNEL 0x00 -// 0110 0000 -#define IDT_USER 0x60 -// 0000 1110 -#define IDT_PADDING 0x0E +/// @brief Use this function to set an entry in the IDT. +/// @param index Indice della IDT. +/// @param handler Puntatore alla funzione che gestira' l'interrupt/Eccezione +/// @param options Le opzioni del descrittore (PRESENT,NOTPRESENT,KERNEL,USER) +/// @param seg_sel Il selettore del segmento della GDT. +static inline void __idt_set_gate(uint8_t index, interrupt_handler_t handler, uint16_t options, uint8_t seg_sel) +{ + uintptr_t base_prt = (uintptr_t)handler; + + // Assign the base values. + idt_table[index].offset_low = (base_prt & 0xFFFFu); + idt_table[index].offset_high = (base_prt >> 16u) & 0xFFFFu; + + // Set the other fields. + idt_table[index].null_par = 0x00; + idt_table[index].seg_selector = seg_sel; + idt_table[index].options = options | IDT_PADDING; +} void init_idt() { - // Prepare IDT vector. - for (uint32_t it = 0; it < IDT_SIZE; ++it) { - idt_table[it].offset_low = 0; - idt_table[it].seg_selector = 0; - idt_table[it].null_par = 0; - idt_table[it].options = 0; - idt_table[it].offset_high = 0; - } + // Prepare IDT vector. + for (uint32_t it = 0; it < IDT_SIZE; ++it) { + idt_table[it].offset_low = 0; + idt_table[it].seg_selector = 0; + idt_table[it].null_par = 0; + idt_table[it].options = 0; + idt_table[it].offset_high = 0; + } - // Just like the GDT, the IDT has a "limit" field that is set to the last - // valid byte in the IDT, after adding in the start position (i.e. size-1). - idt_pointer.limit = sizeof(idt_descriptor_t) * IDT_SIZE - 1; - idt_pointer.base = (uint32_t)&idt_table; + // Just like the GDT, the IDT has a "limit" field that is set to the last + // valid byte in the IDT, after adding in the start position (i.e. size-1). + idt_pointer.limit = sizeof(idt_descriptor_t) * IDT_SIZE - 1; + idt_pointer.base = (uint32_t)&idt_table; - // Initialize ISR for CPU execptions. - isrs_init(); + // Initialize ISR for CPU execptions. + isrs_init(); - // Initialize ISR for PIC interrupts. - irqs_init(); + // Initialize ISR for PIC interrupts. + irq_init(); - // Register ISR [0-31] + 80, interrupts generated by CPU. - // These interrupts will be initially managed by isr_handler. - // The appropriate handler will be called by looking at the vector - // isr_routines. - idt_set_gate(0, INT_0, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(1, INT_1, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(2, INT_2, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(3, INT_3, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(4, INT_4, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(5, INT_5, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(6, INT_6, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(7, INT_7, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(8, INT_8, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(9, INT_9, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(10, INT_10, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(11, INT_11, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(12, INT_12, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(13, INT_13, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(14, INT_14, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(15, INT_15, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(16, INT_16, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(17, INT_17, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(18, INT_18, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(19, INT_19, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(20, INT_20, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(21, INT_21, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(22, INT_22, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(23, INT_23, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(24, INT_24, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(25, INT_25, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(26, INT_26, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(27, INT_27, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(28, INT_28, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(29, INT_29, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(30, INT_30, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(31, INT_31, IDT_PRESENT | IDT_KERNEL, 0x8); + // Register ISR [0-31] + 80, interrupts generated by CPU. + // These interrupts will be initially managed by isr_handler. + // The appropriate handler will be called by looking at the vector + // isr_routines. + __idt_set_gate(0, INT_0, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(1, INT_1, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(2, INT_2, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(3, INT_3, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(4, INT_4, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(5, INT_5, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(6, INT_6, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(7, INT_7, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(8, INT_8, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(9, INT_9, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(10, INT_10, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(11, INT_11, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(12, INT_12, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(13, INT_13, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(14, INT_14, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(15, INT_15, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(16, INT_16, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(17, INT_17, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(18, INT_18, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(19, INT_19, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(20, INT_20, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(21, INT_21, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(22, INT_22, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(23, INT_23, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(24, INT_24, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(25, INT_25, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(26, INT_26, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(27, INT_27, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(28, INT_28, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(29, INT_29, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(30, INT_30, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(31, INT_31, GDT_PRESENT | GDT_KERNEL, 0x8); - // Registers ISR [32-47] (irq [0-15]), interrupts generated by PIC. - // These interrupts will be initially managed by irq_handler. - // The appropriate handler will be called by looking at the vector - // isr_routines. - idt_set_gate(32, IRQ_0, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(33, IRQ_1, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(34, IRQ_2, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(35, IRQ_3, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(36, IRQ_4, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(37, IRQ_5, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(38, IRQ_6, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(39, IRQ_7, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(40, IRQ_8, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(41, IRQ_9, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(42, IRQ_10, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(43, IRQ_11, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(44, IRQ_12, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(45, IRQ_13, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(46, IRQ_14, IDT_PRESENT | IDT_KERNEL, 0x8); - idt_set_gate(47, IRQ_15, IDT_PRESENT | IDT_KERNEL, 0x8); + // Registers ISR [32-47] (irq [0-15]), interrupts generated by PIC. + // These interrupts will be initially managed by irq_handler. + // The appropriate handler will be called by looking at the vector + // isr_routines. + __idt_set_gate(32, IRQ_0, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(33, IRQ_1, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(34, IRQ_2, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(35, IRQ_3, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(36, IRQ_4, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(37, IRQ_5, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(38, IRQ_6, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(39, IRQ_7, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(40, IRQ_8, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(41, IRQ_9, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(42, IRQ_10, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(43, IRQ_11, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(44, IRQ_12, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(45, IRQ_13, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(46, IRQ_14, GDT_PRESENT | GDT_KERNEL, 0x8); + __idt_set_gate(47, IRQ_15, GDT_PRESENT | GDT_KERNEL, 0x8); - // System call! - idt_set_gate(128, INT_80, IDT_PRESENT | IDT_USER, 0x8); + // System call! + __idt_set_gate(128, INT_80, GDT_PRESENT | GDT_USER, 0x8); - // Points the processor's internal register to the new IDT. - idt_flush((uint32_t)&idt_pointer); -} - -void idt_set_gate(uint8_t index, interrupt_handler_t handler, uint16_t options, - uint8_t seg_sel) -{ - uint32_t base_prt = (uint32_t)handler; - - // Assign the base values. - idt_table[index].offset_low = (base_prt & 0xFFFF); - idt_table[index].offset_high = (base_prt >> 16) & 0xFFFF; - - // Set the other fields. - idt_table[index].null_par = 0x00; - idt_table[index].seg_selector = seg_sel; - idt_table[index].options = options | IDT_PADDING; + // Points the processor's internal register to the new IDT. + idt_flush((uint32_t)&idt_pointer); } diff --git a/mentos/src/descriptor_tables/interrupt.asm b/mentos/src/descriptor_tables/interrupt.S similarity index 87% rename from mentos/src/descriptor_tables/interrupt.asm rename to mentos/src/descriptor_tables/interrupt.S index b095447..0097083 100644 --- a/mentos/src/descriptor_tables/interrupt.asm +++ b/mentos/src/descriptor_tables/interrupt.S @@ -1,11 +1,13 @@ ; MentOS, The Mentoring Operating system project ; @file interrupt.asm ; @brief -; @copyright (c) 2019 This file is distributed under the MIT License. +; @copyright (c) 2014-2021 This file is distributed under the MIT License. ; See LICENSE.md for details. +extern irq_handler + %macro IRQ 2 - [GLOBAL IRQ_%1] + global IRQ_%1 IRQ_%1: cli ; disable interrupt line ; A normal ISR stub that pops a dummy error code to keep a @@ -14,6 +16,12 @@ push %2 ; irq number jmp irq_common %endmacro + +; ----------------------------------------------------------------------------- +; SECTION (text) +; ----------------------------------------------------------------------------- +section .text + ; 32 is the first irq, 47 is the last one. DO NOT CHANGE THESE NUMBERS. IRQ 0, 32 IRQ 1, 33 @@ -32,8 +40,6 @@ IRQ 13, 45 IRQ 14, 46 IRQ 15, 47 -[EXTERN irq_handler] - irq_common: ;==== Save CPU registers =================================================== ; when an irq occurs, the following registers are already pushed on stack: diff --git a/mentos/src/descriptor_tables/interrupt.c b/mentos/src/descriptor_tables/interrupt.c index 15f0161..8b67c91 100644 --- a/mentos/src/descriptor_tables/interrupt.c +++ b/mentos/src/descriptor_tables/interrupt.c @@ -1,113 +1,99 @@ /// MentOS, The Mentoring Operating system project -/// @file isr.c +/// @file interrupt.c /// @brief Functions which manage the Interrupt Service Routines (ISRs). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "isr.h" -#include "idt.h" -#include "printk.h" -#include "pic8259.h" -#include "pic8259.h" -#include "scheduler.h" +#include "descriptor_tables/isr.h" -// TODO: lists, double-linked lists should be a Kernel data struture! -/// @brief shared interrupt handlers are stored into a linked list. -/// irq_struct_t is a node of the list +#include "process/scheduler.h" +#include "hardware/pic8259.h" +#include "system/printk.h" +#include "assert.h" +#include "stdio.h" +#include "misc/debug.h" +#include "descriptor_tables/idt.h" + +/// @brief Shared interrupt handlers, stored into a double-linked list. typedef struct irq_struct_t { - /// Puntatore alla funzione handler di un IRQ. - interrupt_handler_t handler; - - /// Puntatore alla descrizione dell'handler. - char *description; - - /// Prossimo handler per questo IRQ - struct irq_struct_t *next; + /// Pointer to the IRQ handler. + interrupt_handler_t handler; + /// Pointer to the description of the handler. + char *description; + /// List handler. + list_head siblings; } irq_struct_t; -// For each IRQ, a chain of handlers. -static irq_struct_t *shared_interrupt_handlers[IRQ_NUM]; +/// For each IRQ, a chain of handlers. +static list_head shared_interrupt_handlers[IRQ_NUM]; +/// Cache where we will store the data regarding an irq service. +static kmem_cache_t *irq_cache; -// Default handler for interrupts and exceptions. -void default_irq_handler(pt_regs *f) +/// @brief Creates a new irq struct. +static inline irq_struct_t *__irq_struct_alloc() { - uint32_t irq_line = f->int_no - 32; - - printk("Kernel PANIC!\n no handler for IRQ [%d]\n", irq_line); - - // Stop kernel execution. - while (1) - ; - // TODO: call the kernel panic method! + // Allocate the structure. + irq_struct_t *irq_struct = kmem_cache_alloc(irq_cache, GFP_KERNEL); + assert(irq_struct && "Failed to allocate memory for IRQ structure."); + // Initialize its fields. + irq_struct->description = NULL; + irq_struct->handler = NULL; + list_head_init(&irq_struct->siblings); + return irq_struct; } -void irqs_init() +/// @brief Destroys an irq struct. +static inline void __irq_struct_dealloc(irq_struct_t *irq_struct) { - // Setting the default_irq_handler as default handler for each IRQ line. - irq_struct_t *irq_struct = NULL; - for (uint32_t i = 0; i < IRQ_NUM; ++i) { - irq_struct = (irq_struct_t *)kmalloc(sizeof(irq_struct_t)); - irq_struct->handler = default_irq_handler; - irq_struct->next = NULL; - shared_interrupt_handlers[i] = irq_struct; - } + list_head_del(&irq_struct->siblings); + kmem_cache_free(irq_struct); } -int irq_install_handler(uint32_t i, interrupt_handler_t handler, - char *description) +void irq_init() { - // We have maximun IRQ_NUM IRQ lines. - if (i > IRQ_NUM) { - return -1; - } + // Initialize the cache. + irq_cache = KMEM_CREATE(irq_struct_t); + // Initializing the list for each irq number. + for (uint32_t i = 0; i < IRQ_NUM; ++i) { + list_head_init(&shared_interrupt_handlers[i]); + } +} - // The current handler for this IRQ line. - irq_struct_t *current_irq_struct = shared_interrupt_handlers[i]; - - // Is the current handler the default one? - if (current_irq_struct->handler == default_irq_handler) { - // Set the given handler as a new handler for i-th IRQ. - current_irq_struct->handler = handler; - // Return. Nothing else to do. - return 0; - } - - // Move current_irq_struct to get the last handler for this IRQ line. - while (current_irq_struct->next != NULL) { - current_irq_struct = current_irq_struct->next; - } - - // Create a new irq_struct_t to save the given handler. - irq_struct_t *irq_struct = - (irq_struct_t *)kmalloc(sizeof(irq_struct_t)); - irq_struct->next = NULL; - irq_struct->description = description; - irq_struct->handler = handler; - - // Store the given handler. - current_irq_struct->next = irq_struct; - return 0; +int irq_install_handler(unsigned i, interrupt_handler_t handler, char *description) +{ + // We have maximun IRQ_NUM IRQ lines. + assert((i < IRQ_NUM) && "Unidentified IRQ number."); + // Create a new irq_struct_t to save the given handler. + irq_struct_t *irq_struct = __irq_struct_alloc(); + irq_struct->description = description; + irq_struct->handler = handler; + // Add the handler to the list of his siblings. + list_head_add_tail(&irq_struct->siblings, &shared_interrupt_handlers[i]); + return 0; } void irq_handler(pt_regs *f) { - // Keep in mind, - // because of irq mapping, the first PIC's irq line is shifted by 32. - uint32_t irq_line = f->int_no - 32; - - // Actually, we may have several handlers for a same irq line. - // The Kernel should provide the dev_id to each handler in order to - // let it know if its own device generated the interrupt. - // TODO: get dev_id - - irq_struct_t *irq_struct = shared_interrupt_handlers[irq_line]; - do { - // Call the interrupt function. - irq_struct->handler(f); - // Move to the next interrupt function. - irq_struct = irq_struct->next; - } while (irq_struct != NULL); - - // Send the end-of-interrupt to PIC. - pic8259_send_eoi(irq_line); + // Keep in mind, + // because of irq mapping, the first PIC's irq line is shifted by 32. + unsigned irq_line = f->int_no - 32; + assert((irq_line < IRQ_NUM) && "Unidentified IRQ number."); + // Actually, we may have several handlers for a same irq line. + // The Kernel should provide the dev_id to each handler in order to + // let it know if its own device generated the interrupt. + // TODO: get dev_id + if (list_head_empty(&shared_interrupt_handlers[irq_line])) { + pr_err("Thre are no handler for IRQ `%d`\n", irq_line); + } else { + list_for_each_decl(it, &shared_interrupt_handlers[irq_line]) + { + // Get the interrupt structure. + irq_struct_t *irq_struct = list_entry(it, irq_struct_t, siblings); + assert(irq_struct && "Something went wrong."); + // Call the interrupt function. + irq_struct->handler(f); + } + } + // Send the end-of-interrupt to PIC. + pic8259_send_eoi(irq_line); } diff --git a/mentos/src/descriptor_tables/tss.S b/mentos/src/descriptor_tables/tss.S new file mode 100644 index 0000000..df9fdd0 --- /dev/null +++ b/mentos/src/descriptor_tables/tss.S @@ -0,0 +1,17 @@ +; MentOS, The Mentoring Operating system project +; @file tss.asm +; @brief +; @copyright (c) 2014-2021 This file is distributed under the MIT License. +; See LICENSE.md for details. + +global tss_flush + +; ----------------------------------------------------------------------------- +; SECTION (text) +; ----------------------------------------------------------------------------- +section .text + +tss_flush: + mov ax, 0x28 + ltr ax + ret diff --git a/mentos/src/descriptor_tables/tss.asm b/mentos/src/descriptor_tables/tss.asm deleted file mode 100644 index 84275dc..0000000 --- a/mentos/src/descriptor_tables/tss.asm +++ /dev/null @@ -1,12 +0,0 @@ -; MentOS, The Mentoring Operating system project -; @file tss.asm -; @brief -; @copyright (c) 2019 This file is distributed under the MIT License. -; See LICENSE.md for details. - -[GLOBAL tss_flush] - -tss_flush: - mov ax, 0x28 - ltr ax - ret diff --git a/mentos/src/descriptor_tables/tss.c b/mentos/src/descriptor_tables/tss.c index 8cbf775..d1c0484 100644 --- a/mentos/src/descriptor_tables/tss.c +++ b/mentos/src/descriptor_tables/tss.c @@ -1,59 +1,54 @@ /// MentOS, The Mentoring Operating system project /// @file tss.c /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "tss.h" -#include "debug.h" +#include "descriptor_tables/tss.h" + #include "string.h" +#include "misc/debug.h" +#include "descriptor_tables/gdt.h" static tss_entry_t kernel_tss; -void tss_init(uint8_t idx, uint32_t kss, uint32_t kesp) +void tss_init(uint8_t idx, uint32_t ss0) { - uint32_t base = (uint32_t) &kernel_tss; + uint32_t base = (uint32_t)&kernel_tss; uint32_t limit = base + sizeof(tss_entry_t); // Add the TSS descriptor to the GDT. // Kernel tss, access(E9 = 1 11 0 1 0 0 1) // 1 present - // 11 ring 3 - // 0 should always be 1, why 0? may be this value doesn't matter at all - // 1 code? + // 11 ring 3 (kernel) + // 0 always 0 when dealing with system segments + // 1 execution // 0 can not be executed by ring lower or equal to DPL, // 0 not readable - // 1 access bit, always 0, cpu set this to 1 when accessing this - // sector(why 0 now?) - gdt_set_gate(idx, base, limit, 0xE9, 0x0); + // 1 access bit, always 0, cpu set this to 1 when accessing this sector + gdt_set_gate(idx, base, limit, GDT_PRESENT | GDT_USER | GDT_EX | GDT_AC, 0x0); - // init. tss entry to zero - memset(&kernel_tss, 0x0, sizeof(tss_entry_t)); - - kernel_tss.ss0 = kss; // Note that we usually set tss's esp to 0 when booting our os, however, // we need to set it to the real esp when we've switched to usermode // because the CPU needs to know what esp to use when usermode app is // calling a kernel function(aka system call), that's why we have a // function below called tss_set_stack. - kernel_tss.esp0 = kesp; - kernel_tss.cs = 0x0b; - kernel_tss.ds = 0x13; - kernel_tss.es = 0x13; - kernel_tss.fs = 0x13; - kernel_tss.gs = 0x13; - kernel_tss.ss = 0x13; + memset(&kernel_tss, 0x0, sizeof(tss_entry_t)); + kernel_tss.ss0 = ss0; + kernel_tss.esp0 = 0x0; + kernel_tss.cs = 0x0b; + kernel_tss.ds = 0x13; + kernel_tss.es = 0x13; + kernel_tss.fs = 0x13; + kernel_tss.gs = 0x13; + kernel_tss.ss = 0x13; kernel_tss.iomap = sizeof(tss_entry_t); } -void print_tss() { - printf("TSS: SSO(%p) ESP0(%p)\n", kernel_tss.ss0, kernel_tss.esp0); -} - void tss_set_stack(uint32_t kss, uint32_t kesp) { // Kernel data segment. - kernel_tss.ss0 = kss; + kernel_tss.ss0 = kss; // Kernel stack address. kernel_tss.esp0 = kesp; } diff --git a/mentos/src/devices/fpu.c b/mentos/src/devices/fpu.c index c9a0b7d..36b3f7a 100644 --- a/mentos/src/devices/fpu.c +++ b/mentos/src/devices/fpu.c @@ -1,177 +1,177 @@ /// MentOS, The Mentoring Operating system project /// @file fpu.c /// @brief Floating Point Unit (FPU). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "fpu.h" -#include "isr.h" -#include "debug.h" +#include "devices/fpu.h" +#include "descriptor_tables/isr.h" +#include "misc/debug.h" #include "string.h" #include "assert.h" -#include "process.h" -#include "scheduler.h" +#include "process/scheduler.h" +#include "math.h" +#include "process/process.h" +#include "system/signal.h" -#define NO_LAZY_FPU - -struct task_struct *fpu_thread = NULL; +/// Pointerst to the current thread using the FPU. +task_struct *thread_using_fpu = NULL; +/// Temporary aligned buffer for copying around FPU contexts. +uint8_t saves[512] __attribute__((aligned(16))); /// @brief Set the FPU control word. /// @param cw What to set the control word to. -void set_fpu_cw(const uint16_t cw) +static inline void __set_fpu_cw(const uint16_t cw) { - asm volatile("fldcw %0" ::"m"(cw)); + asm volatile("fldcw %0" ::"m"(cw)); } /// @brief Enable the FPU and SSE. -void enable_fpu() +static inline void __enable_fpu() { - asm volatile("clts"); - - size_t t; - - asm volatile("mov %%cr0, %0" : "=r"(t)); - - t &= ~(1 << 2); - - t |= (1 << 1); - - asm volatile("mov %0, %%cr0" ::"r"(t)); - - asm volatile("mov %%cr4, %0" : "=r"(t)); - - t |= 3 << 9; - - asm volatile("mov %0, %%cr4" ::"r"(t)); + asm volatile("clts"); + size_t t; + asm volatile("mov %%cr0, %0" + : "=r"(t)); + t &= ~(1U << 2U); + t |= (1U << 1U); + asm volatile("mov %0, %%cr0" ::"r"(t)); + asm volatile("mov %%cr4, %0" + : "=r"(t)); + t |= 3U << 9U; + asm volatile("mov %0, %%cr4" ::"r"(t)); } /// Disable FPU and SSE so it traps to the kernel. -void disable_fpu() +static inline void __disable_fpu() { - size_t t; + size_t t; - asm volatile("mov %%cr0, %0" : "=r"(t)); + asm volatile("mov %%cr0, %0" + : "=r"(t)); - t |= 1 << 3; + t |= 1U << 3U; - asm volatile("mov %0, %%cr0" ::"r"(t)); + asm volatile("mov %0, %%cr0" ::"r"(t)); } -// Temporary aligned buffer for copying around FPU contexts. -uint8_t saves[512] __attribute__((aligned(16))); - /// @brief Restore the FPU for a process. -void restore_fpu(struct task_struct *proc) +static inline void __restore_fpu(task_struct *proc) { - assert(proc && "Trying to restore FPU of NULL process."); + assert(proc && "Trying to restore FPU of NULL process."); - memcpy(&saves, (uint8_t *)&proc->thread.fpu_register, 512); + memcpy(&saves, (uint8_t *)&proc->thread.fpu_register, 512); - asm volatile("fxrstor (%0)" ::"r"(saves)); + asm volatile("fxrstor (%0)" ::"r"(saves)); } /// Save the FPU for a process. -void save_fpu(struct task_struct *proc) +static inline void __save_fpu(task_struct *proc) { - assert(proc && "Trying to save FPU of NULL process."); + assert(proc && "Trying to save FPU of NULL process."); - asm volatile("fxsave (%0)" ::"r"(saves)); + asm volatile("fxsave (%0)" ::"r"(saves)); - memcpy((uint8_t *)&proc->thread.fpu_register, &saves, 512); + memcpy((uint8_t *)&proc->thread.fpu_register, &saves, 512); } /// Initialize the FPU. -void init_fpu() +static inline void __init_fpu() { - asm volatile("fninit"); + asm volatile("fninit"); } /// Kernel trap for FPU usage when FPU is disabled. -void invalid_op(pt_regs *r) +/// @param f The interrupt stack frame. +static inline void __invalid_op(pt_regs *f) { - // First, turn the FPU on. - enable_fpu(); - if (fpu_thread == kernel_get_current_process()) { - // If this is the thread that last used the FPU, do nothing. - return; - } - if (fpu_thread) { - // If there is a thread that was using the FPU, save its state. - save_fpu(fpu_thread); - } - fpu_thread = kernel_get_current_process(); - if (!fpu_thread->thread.fpu_enabled) { - /* + pr_debug("__invalid_op(%p)\n", f); + // First, turn the FPU on. + __enable_fpu(); + if (thread_using_fpu == scheduler_get_current_process()) { + // If this is the thread that last used the FPU, do nothing. + return; + } + if (thread_using_fpu) { + // If there is a thread that was using the FPU, save its state. + __save_fpu(thread_using_fpu); + } + thread_using_fpu = scheduler_get_current_process(); + if (!thread_using_fpu->thread.fpu_enabled) { + /* * If the FPU has not been used in this thread previously, * we need to initialize it. */ - init_fpu(); - fpu_thread->thread.fpu_enabled = true; - return; - } - // Otherwise we restore the context for this thread. - restore_fpu(fpu_thread); + __init_fpu(); + thread_using_fpu->thread.fpu_enabled = true; + return; + } + // Otherwise we restore the context for this thread. + __restore_fpu(thread_using_fpu); +} + +/// Kernel trap for various integer and floating-point errors +/// @param f The interrupt stack frame. +static inline void __sigfpe_handler(pt_regs* f) +{ + pr_debug("__sigfpe_handler(%p)\n", f); + + // Notifies current process + thread_using_fpu = scheduler_get_current_process(); + sys_kill(thread_using_fpu->pid, SIGFPE); +} + + +/// @brief Ensure basic FPU functionality works. +/// @details +/// For processors without a FPU, this tests that maths libraries link +/// correctly. +static int __fpu_test() +{ + double a = M_PI; + // First test. + for (int i = 0; i < 10000; i++) { + a = a * 1.123 + (a / 3); + a /= 1.111; + while (a > 100.0) + a /= 3.1234563212; + while (a < 2.0) + a += 1.1232132131; + } + if (a != 50.11095685350556294679336133413) + return 0; + // Second test. + a = M_PI; + for (int i = 0; i < 100; i++) + a = a * 3 + (a / 3); + return (a == 60957114488184560000000000000000000000000000000000000.0); } -// Called during a context switch; disable the FPU. void switch_fpu() { -#ifdef NO_LAZY_FPU - save_fpu(kernel_get_current_process()); -#else - disable_fpu(); -#endif + __save_fpu(scheduler_get_current_process()); } void unswitch_fpu() { -#ifdef NO_LAZY_FPU - restore_fpu(kernel_get_current_process()); -#endif + __restore_fpu(scheduler_get_current_process()); } -static bool_t fpu_test_1() +int fpu_install() { - double a = M_PI; - for (int i = 0; i < 10000; i++) { - a = a * 1.123 + (a / 3); - a /= 1.111; - while (a > 100.0) { - a /= 3.1234563212; - } - while (a < 2.0) { - a += 1.1232132131; - } - } - return (a == 50.11095685350556294679336133413); -} + __enable_fpu(); + __init_fpu(); + __save_fpu(scheduler_get_current_process()); -/* - * Ensure basic FPU functionality works. - * - * For processors without a FPU, this tests that maths libraries link - * correctly. - */ -static bool_t fpu_test_2() -{ - double a = M_PI; - for (int i = 0; i < 100; i++) { - a = a * 3 + (a / 3); - } - return (a == 60957114488184560000000000000000000000000000000000000.0); -} + // Install the handler for device missing + isr_install_handler(DEV_NOT_AVL, &__invalid_op, "fpu: device missing"); -// Enable the FPU context handling. -bool_t fpu_install() -{ -#ifdef NO_LAZY_FPU - enable_fpu(); - init_fpu(); - save_fpu(kernel_get_current_process()); -#else - enable_fpu(); - disable_fpu(); -#endif - isr_install_handler(7, &invalid_op, "fpu"); - return fpu_test_1() & fpu_test_2(); + // Install handlers for floating points and integers errors + isr_install_handler(DIVIDE_ERROR, &__sigfpe_handler, "divide error"); + + // NB: The exceptions bolow don't seems to ever trigger + //isr_install_handler(OVERFLOW, &__sigfpe_handler, "overflow"); + //isr_install_handler(FLOATING_POINT_ERR, &__sigfpe_handler, "floating point error"); + + return __fpu_test(); } diff --git a/mentos/src/devices/pci.c b/mentos/src/devices/pci.c index 6074f26..578b3d7 100644 --- a/mentos/src/devices/pci.c +++ b/mentos/src/devices/pci.c @@ -1,20 +1,19 @@ /// MentOS, The Mentoring Operating system project /// @file pci.c /// @brief Routines for PCI initialization. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +///! @cond Doxygen_Suppress -#include "pci.h" -#include "debug.h" -#include "stdio.h" -#include "kheap.h" +#include "devices/pci.h" +#include "misc/debug.h" #include "string.h" -#include "port_io.h" +#include "io/port_io.h" void pci_write_field(uint32_t device, int field, int size, uint32_t value) { - (void)size; - outportl(PCI_ADDRESS_PORT, pci_get_addr(device, field)); + (void)size; + outportl(PCI_ADDRESS_PORT, pci_get_addr(device, field)); #if 0 if (size == 4) outportl(PCI_VALUE_PORT, value); @@ -23,507 +22,472 @@ void pci_write_field(uint32_t device, int field, int size, uint32_t value) else if (size == 1) outportb(PCI_VALUE_PORT, value); #else - outportl(PCI_VALUE_PORT, value); + outportl(PCI_VALUE_PORT, value); #endif } uint32_t pci_read_field(uint32_t device, int field, int size) { - outportl(PCI_ADDRESS_PORT, pci_get_addr(device, field)); + outportl(PCI_ADDRESS_PORT, pci_get_addr(device, field)); - if (size == 4) { - return inportl(PCI_VALUE_PORT); - } else if (size == 2) { - return inports(PCI_VALUE_PORT + (field & 2)); - } else if (size == 1) { - return inportb(PCI_VALUE_PORT + (field & 3)); - } - return 0xFFFF; + if (size == 4) { + return inportl(PCI_VALUE_PORT); + } else if (size == 2) { + return inports(PCI_VALUE_PORT + (field & 2)); + } else if (size == 1) { + return inportb(PCI_VALUE_PORT + (field & 3)); + } + return 0xFFFF; } uint32_t pci_find_type(uint32_t device) { - return pci_read_field(device, PCI_CLASS, 1) << 16 | - pci_read_field(device, PCI_SUBCLASS, 1) << 8 | - pci_read_field(device, PCI_PROG_IF, 1); + return pci_read_field(device, PCI_CLASS, 1) << 16 | + pci_read_field(device, PCI_SUBCLASS, 1) << 8 | + pci_read_field(device, PCI_PROG_IF, 1); } struct { - uint16_t id; - const char *name; + uint16_t id; + const char *name; } _pci_vendors[] = { - { 0x1022, "AMD" }, { 0x106b, "Apple, Inc." }, - { 0x1234, "Bochs/QEMU" }, { 0x1274, "Ensoniq" }, - { 0x15ad, "VMWare" }, { 0x8086, "Intel Corporation" }, - { 0x80EE, "VirtualBox" }, + { 0x1022, "AMD" }, + { 0x106b, "Apple, Inc." }, + { 0x1234, "Bochs/QEMU" }, + { 0x1274, "Ensoniq" }, + { 0x15ad, "VMWare" }, + { 0x8086, "Intel Corporation" }, + { 0x80EE, "VirtualBox" }, }; struct { - uint16_t ven_id; - uint16_t dev_id; - const char *name; + uint16_t ven_id; + uint16_t dev_id; + const char *name; } _pci_devices[] = { - { 0x1022, 0x2000, "PCNet Ethernet Controller (pcnet)" }, - { 0x106b, 0x003f, "OHCI Controller" }, - { 0x1234, 0x1111, "VGA BIOS Graphics Extensions" }, - { 0x1274, 0x1371, "Creative Labs CT2518 (ensoniq audio)" }, - { 0x15ad, 0x0740, "VM Communication Interface" }, - { 0x15ad, 0x0405, "SVGA II Adapter" }, - { 0x15ad, 0x0790, "PCI bridge" }, - { 0x15ad, 0x07a0, "PCI Express Root Port" }, - { 0x8086, 0x100e, "Gigabit Ethernet Controller (e1000)" }, - { 0x8086, 0x100f, "Gigabit Ethernet Controller (e1000)" }, - { 0x8086, 0x1237, "PCI & Memory" }, - { 0x8086, 0x2415, "AC'97 Audio Chipset" }, - { 0x8086, 0x7000, "PCI-to-ISA Bridge" }, - { 0x8086, 0x7010, "IDE Interface" }, - { 0x8086, 0x7110, "PIIX4 ISA" }, - { 0x8086, 0x7111, "PIIX4 IDE" }, - { 0x8086, 0x7113, "Power Management Controller" }, - { 0x8086, 0x7190, "Host Bridge" }, - { 0x8086, 0x7191, "AGP Bridge" }, - { 0x80EE, 0xBEEF, "Bochs/QEMU-compatible Graphics Adapter" }, - { 0x80EE, 0xCAFE, "Guest Additions Device" }, + { 0x1022, 0x2000, "PCNet Ethernet Controller (pcnet)" }, + { 0x106b, 0x003f, "OHCI Controller" }, + { 0x1234, 0x1111, "VGA BIOS Graphics Extensions" }, + { 0x1274, 0x1371, "Creative Labs CT2518 (ensoniq audio)" }, + { 0x15ad, 0x0740, "VM Communication Interface" }, + { 0x15ad, 0x0405, "SVGA II Adapter" }, + { 0x15ad, 0x0790, "PCI bridge" }, + { 0x15ad, 0x07a0, "PCI Express Root Port" }, + { 0x8086, 0x100e, "Gigabit Ethernet Controller (e1000)" }, + { 0x8086, 0x100f, "Gigabit Ethernet Controller (e1000)" }, + { 0x8086, 0x1237, "PCI & Memory" }, + { 0x8086, 0x2415, "AC'97 Audio Chipset" }, + { 0x8086, 0x7000, "PCI-to-ISA Bridge" }, + { 0x8086, 0x7010, "IDE Interface" }, + { 0x8086, 0x7110, "PIIX4 ISA" }, + { 0x8086, 0x7111, "PIIX4 IDE" }, + { 0x8086, 0x7113, "Power Management Controller" }, + { 0x8086, 0x7190, "Host Bridge" }, + { 0x8086, 0x7191, "AGP Bridge" }, + { 0x80EE, 0xBEEF, "Bochs/QEMU-compatible Graphics Adapter" }, + { 0x80EE, 0xCAFE, "Guest Additions Device" }, }; struct { - uint32_t id; - const char *name; + uint32_t id; + const char *name; } _pci_classes[] = { - { 0x000000, "Legacy Device" }, - { 0x000100, "VGA-Compatible Device" }, + { 0x000000, "Legacy Device" }, + { 0x000100, "VGA-Compatible Device" }, - { 0x010000, "SCSI bus controller" }, - { 0x010100, "ISA Compatibility mode-only controller" }, - { 0x010105, "PCI native mode-only controller" }, - { 0x01010a, "ISA Compatibility mode controller, supports both channels " - "switched to PCI native mode" }, - { 0x01010f, - "PCI native mode controller, supports both channels switched " - "to ISA compatibility mode" }, - { 0x010180, - "ISA Compatibility mode-only controller, supports bus mastering" }, - { 0x010185, "PCI native mode-only controller, supports bus mastering" }, - { 0x01018a, "ISA Compatibility mode controller, supports both channels " - "switched to PCI native mode, supports bus mastering" }, - { 0x01018f, - "PCI native mode controller, supports both channels switched " - "\to ISA compatibility mode, supports bus mastering" }, + { 0x010000, "SCSI bus controller" }, + { 0x010100, "ISA Compatibility mode-only controller" }, + { 0x010105, "PCI native mode-only controller" }, + { 0x01010a, "ISA Compatibility mode controller, supports both channels " + "switched to PCI native mode" }, + { 0x01010f, "PCI native mode controller, supports both channels switched " + "to ISA compatibility mode" }, + { 0x010180, + "ISA Compatibility mode-only controller, supports bus mastering" }, + { 0x010185, "PCI native mode-only controller, supports bus mastering" }, + { 0x01018a, "ISA Compatibility mode controller, supports both channels " + "switched to PCI native mode, supports bus mastering" }, + { 0x01018f, "PCI native mode controller, supports both channels switched " + "\to ISA compatibility mode, supports bus mastering" }, - { 0x010200, "Floppy disk controller" }, - { 0x010300, "IPI bus controller" }, - { 0x010400, "RAID controller" }, - { 0x010520, "ATA controller, single stepping" }, - { 0x010530, "ATA controller, continuous" }, - { 0x010600, "Serial ATA controller - vendor specific interface" }, - { 0x010601, "Serial ATA controller - AHCI 1.0 interface" }, - { 0x010700, "Serial Attached SCSI controller" }, - { 0x018000, "Mass Storage controller" }, + { 0x010200, "Floppy disk controller" }, + { 0x010300, "IPI bus controller" }, + { 0x010400, "RAID controller" }, + { 0x010520, "ATA controller, single stepping" }, + { 0x010530, "ATA controller, continuous" }, + { 0x010600, "Serial ATA controller - vendor specific interface" }, + { 0x010601, "Serial ATA controller - AHCI 1.0 interface" }, + { 0x010700, "Serial Attached SCSI controller" }, + { 0x018000, "Mass Storage controller" }, - { 0x020000, "Ethernet controller" }, - { 0x020100, "Token Ring controller" }, - { 0x020200, "FDDI controller" }, - { 0x020300, "ATM controller" }, - { 0x020400, "ISDN controller" }, - { 0x020500, "WorldFip controller" }, - // { 0x0206xx , "PICMG 2.14 Multi Computing" }, - { 0x028000, "Network controller" }, + { 0x020000, "Ethernet controller" }, + { 0x020100, "Token Ring controller" }, + { 0x020200, "FDDI controller" }, + { 0x020300, "ATM controller" }, + { 0x020400, "ISDN controller" }, + { 0x020500, "WorldFip controller" }, + // { 0x0206xx , "PICMG 2.14 Multi Computing" }, + { 0x028000, "Network controller" }, - { 0x030000, "VGA Display controller" }, - { 0x030001, "8514-compatible Display controller" }, - { 0x030100, "XGA Display controller" }, - { 0x030200, "3D Display controller" }, - { 0x038000, "Display controller" }, + { 0x030000, "VGA Display controller" }, + { 0x030001, "8514-compatible Display controller" }, + { 0x030100, "XGA Display controller" }, + { 0x030200, "3D Display controller" }, + { 0x038000, "Display controller" }, - { 0x040000, "Video device" }, - { 0x040100, "Audio device" }, - { 0x040200, "Computer Telephony device" }, - { 0x048000, "Multimedia device" }, + { 0x040000, "Video device" }, + { 0x040100, "Audio device" }, + { 0x040200, "Computer Telephony device" }, + { 0x048000, "Multimedia device" }, - { 0x050000, "RAM memory controller" }, - { 0x050100, "Flash memory controller" }, - { 0x058000, "Memory controller" }, + { 0x050000, "RAM memory controller" }, + { 0x050100, "Flash memory controller" }, + { 0x058000, "Memory controller" }, - { 0x060000, "Host bridge" }, - { 0x060100, "ISA bridge" }, - { 0x060200, "EISA bridge" }, - { 0x060300, "MCA bridge" }, - { 0x060400, "PCI-to-PCI bridge" }, - { 0x060401, "PCI-to-PCI bridge (subtractive decoding)" }, - { 0x060500, "PCMCIA bridge" }, - { 0x060600, "NuBus bridge" }, - { 0x060700, "CardBus bridge" }, - // { 0x0608xx , "RACEway bridge" }, - { 0x060940, - "PCI-to-PCI bridge, Semi-transparent, primary facing Host" }, - { 0x060980, - "PCI-to-PCI bridge, Semi-transparent, secondary facing Host" }, - { 0x060A00, "InfiniBand-to-PCI host bridge" }, - { 0x068000, "Bridge device" }, + { 0x060000, "Host bridge" }, + { 0x060100, "ISA bridge" }, + { 0x060200, "EISA bridge" }, + { 0x060300, "MCA bridge" }, + { 0x060400, "PCI-to-PCI bridge" }, + { 0x060401, "PCI-to-PCI bridge (subtractive decoding)" }, + { 0x060500, "PCMCIA bridge" }, + { 0x060600, "NuBus bridge" }, + { 0x060700, "CardBus bridge" }, + // { 0x0608xx , "RACEway bridge" }, + { 0x060940, "PCI-to-PCI bridge, Semi-transparent, primary facing Host" }, + { 0x060980, "PCI-to-PCI bridge, Semi-transparent, secondary facing Host" }, + { 0x060A00, "InfiniBand-to-PCI host bridge" }, + { 0x068000, "Bridge device" }, - { 0x070000, "Generic XT-compatible serial controller" }, - { 0x070001, "16450-compatible serial controller" }, - { 0x070002, "16550-compatible serial controller" }, - { 0x070003, "16650-compatible serial controller" }, - { 0x070004, "16750-compatible serial controller" }, - { 0x070005, "16850-compatible serial controller" }, - { 0x070006, "16950-compatible serial controller" }, + { 0x070000, "Generic XT-compatible serial controller" }, + { 0x070001, "16450-compatible serial controller" }, + { 0x070002, "16550-compatible serial controller" }, + { 0x070003, "16650-compatible serial controller" }, + { 0x070004, "16750-compatible serial controller" }, + { 0x070005, "16850-compatible serial controller" }, + { 0x070006, "16950-compatible serial controller" }, - { 0x070100, "Parallel port" }, - { 0x070101, "Bi-directional parallel port" }, - { 0x070102, "ECP 1.X compliant parallel port" }, - { 0x070103, "IEEE1284 controller" }, - { 0x0701FE, "IEEE1284 target device" }, - { 0x070200, "Multiport serial controller" }, + { 0x070100, "Parallel port" }, + { 0x070101, "Bi-directional parallel port" }, + { 0x070102, "ECP 1.X compliant parallel port" }, + { 0x070103, "IEEE1284 controller" }, + { 0x0701FE, "IEEE1284 target device" }, + { 0x070200, "Multiport serial controller" }, - { 0x070300, "Generic modem" }, - { 0x070301, "Hayes 16450-compatible modem" }, - { 0x070302, "Hayes 16550-compatible modem" }, - { 0x070303, "Hayes 16650-compatible modem" }, - { 0x070304, "Hayes 16750-compatible modem" }, - { 0x070400, "GPIB (IEEE 488.1/2) controller" }, - { 0x070500, "Smart Card" }, - { 0x078000, "Communications device" }, + { 0x070300, "Generic modem" }, + { 0x070301, "Hayes 16450-compatible modem" }, + { 0x070302, "Hayes 16550-compatible modem" }, + { 0x070303, "Hayes 16650-compatible modem" }, + { 0x070304, "Hayes 16750-compatible modem" }, + { 0x070400, "GPIB (IEEE 488.1/2) controller" }, + { 0x070500, "Smart Card" }, + { 0x078000, "Communications device" }, - { 0x080000, "Generic 8259 PIC" }, - { 0x080001, "ISA PIC" }, - { 0x080002, "EISA PIC" }, - { 0x080010, "I/O APIC interrupt controller" }, - { 0x080020, "I/O(x) APIC interrupt controller" }, + { 0x080000, "Generic 8259 PIC" }, + { 0x080001, "ISA PIC" }, + { 0x080002, "EISA PIC" }, + { 0x080010, "I/O APIC interrupt controller" }, + { 0x080020, "I/O(x) APIC interrupt controller" }, - { 0x080100, "Generic 8237 DMA controller" }, - { 0x080101, "ISA DMA controller" }, - { 0x080102, "EISA DMA controller" }, + { 0x080100, "Generic 8237 DMA controller" }, + { 0x080101, "ISA DMA controller" }, + { 0x080102, "EISA DMA controller" }, - { 0x080200, "Generic 8254 system timer" }, - { 0x080201, "ISA system timer" }, - { 0x080202, "EISA system timer-pair" }, + { 0x080200, "Generic 8254 system timer" }, + { 0x080201, "ISA system timer" }, + { 0x080202, "EISA system timer-pair" }, - { 0x080300, "Generic RTC controller" }, - { 0x080301, "ISA RTC controller" }, + { 0x080300, "Generic RTC controller" }, + { 0x080301, "ISA RTC controller" }, - { 0x080400, "Generic PCI Hot-Plug controller" }, - { 0x080500, "SD Host controller" }, - { 0x088000, "System peripheral" }, + { 0x080400, "Generic PCI Hot-Plug controller" }, + { 0x080500, "SD Host controller" }, + { 0x088000, "System peripheral" }, - { 0x090000, "Keyboard controller" }, - { 0x090100, "Digitizer (pen)" }, - { 0x090200, "Mouse controller" }, - { 0x090300, "Scanner controller" }, - { 0x090400, "Generic Gameport controller" }, - { 0x090410, "Legacy Gameport controller" }, - { 0x098000, "Input controller" }, + { 0x090000, "Keyboard controller" }, + { 0x090100, "Digitizer (pen)" }, + { 0x090200, "Mouse controller" }, + { 0x090300, "Scanner controller" }, + { 0x090400, "Generic Gameport controller" }, + { 0x090410, "Legacy Gameport controller" }, + { 0x098000, "Input controller" }, - { 0x0a0000, "Generic docking station" }, - { 0x0a8000, "Docking station" }, + { 0x0a0000, "Generic docking station" }, + { 0x0a8000, "Docking station" }, - { 0x0b0000, "386 Processor" }, - { 0x0b0100, "486 Processor" }, - { 0x0b0200, "Pentium Processor" }, - { 0x0b1000, "Alpha Processor" }, - { 0x0b2000, "PowerPC Processor" }, - { 0x0b3000, "MIPS Processor" }, - { 0x0b4000, "Co-processor" }, + { 0x0b0000, "386 Processor" }, + { 0x0b0100, "486 Processor" }, + { 0x0b0200, "Pentium Processor" }, + { 0x0b1000, "Alpha Processor" }, + { 0x0b2000, "PowerPC Processor" }, + { 0x0b3000, "MIPS Processor" }, + { 0x0b4000, "Co-processor" }, - { 0x0c0000, "IEEE 1394 (FireWire)" }, - { 0x0c0010, "IEEE 1394 -- OpenHCI spec" }, - { 0x0c0100, "ACCESS.bus" }, - { 0x0c0200, "SSA" }, - { 0x0c0300, "Universal Serial Bus (UHC spec)" }, - { 0x0c0310, "Universal Serial Bus (Open Host spec)" }, - { 0x0c0320, "USB2 Host controller (Intel Enhanced HCI spec)" }, - { 0x0c0380, "Universal Serial Bus (no PI spec)" }, - { 0x0c03FE, "USB Target Device" }, - { 0x0c0400, "Fibre Channel" }, - { 0x0c0500, "System Management Bus" }, - { 0x0c0600, "InfiniBand" }, - { 0x0c0700, "IPMI SMIC Interface" }, - { 0x0c0701, "IPMI Kybd Controller Style Interface" }, - { 0x0c0702, "IPMI Block Transfer Interface" }, - // { 0x0c08xx , "SERCOS Interface" }, - { 0x0c0900, "CANbus" }, + { 0x0c0000, "IEEE 1394 (FireWire)" }, + { 0x0c0010, "IEEE 1394 -- OpenHCI spec" }, + { 0x0c0100, "ACCESS.bus" }, + { 0x0c0200, "SSA" }, + { 0x0c0300, "Universal Serial Bus (UHC spec)" }, + { 0x0c0310, "Universal Serial Bus (Open Host spec)" }, + { 0x0c0320, "USB2 Host controller (Intel Enhanced HCI spec)" }, + { 0x0c0380, "Universal Serial Bus (no PI spec)" }, + { 0x0c03FE, "USB Target Device" }, + { 0x0c0400, "Fibre Channel" }, + { 0x0c0500, "System Management Bus" }, + { 0x0c0600, "InfiniBand" }, + { 0x0c0700, "IPMI SMIC Interface" }, + { 0x0c0701, "IPMI Kybd Controller Style Interface" }, + { 0x0c0702, "IPMI Block Transfer Interface" }, + // { 0x0c08xx , "SERCOS Interface" }, + { 0x0c0900, "CANbus" }, - { 0x0d100, "iRDA compatible controller" }, - { 0x0d100, "Consumer IR controller" }, - { 0x0d100, "RF controller" }, - { 0x0d100, "Bluetooth controller" }, - { 0x0d100, "Broadband controller" }, - { 0x0d100, "Ethernet (802.11a 5 GHz) controller" }, - { 0x0d100, "Ethernet (802.11b 2.4 GHz) controller" }, - { 0x0d100, "Wireless controller" }, + { 0x0d100, "iRDA compatible controller" }, + { 0x0d100, "Consumer IR controller" }, + { 0x0d100, "RF controller" }, + { 0x0d100, "Bluetooth controller" }, + { 0x0d100, "Broadband controller" }, + { 0x0d100, "Ethernet (802.11a 5 GHz) controller" }, + { 0x0d100, "Ethernet (802.11b 2.4 GHz) controller" }, + { 0x0d100, "Wireless controller" }, - // { 0x0e00xx , "I2O Intelligent I/O, spec 1.0" }, - { 0x0e0000, "Message FIFO at offset 040h" }, + // { 0x0e00xx , "I2O Intelligent I/O, spec 1.0" }, + { 0x0e0000, "Message FIFO at offset 040h" }, - { 0x0f0100, "TV satellite comm. controller" }, - { 0x0f0200, "Audio satellite comm. controller" }, - { 0x0f0300, "Voice satellite comm. controller" }, - { 0x0f0400, "Data satellite comm. controller" }, + { 0x0f0100, "TV satellite comm. controller" }, + { 0x0f0200, "Audio satellite comm. controller" }, + { 0x0f0300, "Voice satellite comm. controller" }, + { 0x0f0400, "Data satellite comm. controller" }, - { 0x100000, "Network and computing en/decryption" }, - { 0x101000, "Entertainment en/decryption" }, - { 0x108000, "En/Decryption" }, + { 0x100000, "Network and computing en/decryption" }, + { 0x101000, "Entertainment en/decryption" }, + { 0x108000, "En/Decryption" }, - { 0x110000, "DPIO modules" }, - { 0x110100, "Perf. counters" }, - { 0x111000, "Comm. synch., time and freq. test" }, - { 0x112000, "Management card" }, - { 0x118000, "Data acq./Signal proc." }, + { 0x110000, "DPIO modules" }, + { 0x110100, "Perf. counters" }, + { 0x111000, "Comm. synch., time and freq. test" }, + { 0x112000, "Management card" }, + { 0x118000, "Data acq./Signal proc." }, }; const char *pci_vendor_lookup(unsigned short vendor_id) { - for (int i = 0; i < sizeof(_pci_vendors) / sizeof(_pci_vendors[0]); - ++i) { - if (_pci_vendors[i].id == vendor_id) { - return _pci_vendors[i].name; - } - } + for (int i = 0; i < sizeof(_pci_vendors) / sizeof(_pci_vendors[0]); ++i) { + if (_pci_vendors[i].id == vendor_id) { + return _pci_vendors[i].name; + } + } - return "Unknown"; + return "Unknown"; } -const char *pci_device_lookup(unsigned short vendor_id, - unsigned short device_id) +const char *pci_device_lookup(unsigned short vendor_id, unsigned short device_id) { - for (int i = 0; i < sizeof(_pci_devices) / sizeof(_pci_devices[0]); - ++i) { - if (_pci_devices[i].ven_id == vendor_id && - _pci_devices[i].dev_id == device_id) { - return _pci_devices[i].name; - } - } + for (int i = 0; i < sizeof(_pci_devices) / sizeof(_pci_devices[0]); ++i) { + if (_pci_devices[i].ven_id == vendor_id && + _pci_devices[i].dev_id == device_id) { + return _pci_devices[i].name; + } + } - return "Unknown"; + return "Unknown"; } const char *pci_class_lookup(uint32_t class_code) { - for (int i = 0; i < sizeof(_pci_classes) / sizeof(_pci_classes[0]); - ++i) { - if (_pci_classes[i].id == class_code) { - return _pci_classes[i].name; - } - } + for (int i = 0; i < sizeof(_pci_classes) / sizeof(_pci_classes[0]); ++i) { + if (_pci_classes[i].id == class_code) { + return _pci_classes[i].name; + } + } - return "Unknown"; + return "Unknown"; } -void pci_scan_hit(pci_func_t f, uint32_t dev, void *extra) +void pci_scan_hit(pci_scan_func_t f, uint32_t dev, void *extra) { - uint16_t dev_vend = (uint16_t)pci_read_field(dev, PCI_VENDOR_ID, 2); - uint16_t dev_dvid = (uint16_t)pci_read_field(dev, PCI_DEVICE_ID, 2); - f(dev, dev_vend, dev_dvid, extra); + uint16_t dev_vend = (uint16_t)pci_read_field(dev, PCI_VENDOR_ID, 2); + uint16_t dev_dvid = (uint16_t)pci_read_field(dev, PCI_DEVICE_ID, 2); + f(dev, dev_vend, dev_dvid, extra); } -void pci_scan_func(pci_func_t f, int type, int bus, int slot, int func, - void *extra) +void pci_scan_func(pci_scan_func_t f, int type, int bus, int slot, int func, void *extra) { - uint32_t dev = pci_box_device(bus, slot, func); + uint32_t dev = pci_box_device(bus, slot, func); - if ((type == -1) || (type == pci_find_type(dev))) { - pci_scan_hit(f, dev, extra); - } - if (pci_find_type(dev) == PCI_TYPE_BRIDGE) { - pci_scan_bus(f, type, pci_read_field(dev, PCI_SECONDARY_BUS, 1), - extra); - } + if ((type == -1) || (type == pci_find_type(dev))) { + pci_scan_hit(f, dev, extra); + } + if (pci_find_type(dev) == PCI_TYPE_BRIDGE) { + pci_scan_bus(f, type, pci_read_field(dev, PCI_SECONDARY_BUS, 1), extra); + } } -void pci_scan_slot(pci_func_t f, int type, int bus, int slot, void *extra) +void pci_scan_slot(pci_scan_func_t f, int type, int bus, int slot, void *extra) { - uint32_t dev = pci_box_device(bus, slot, 0); + uint32_t dev = pci_box_device(bus, slot, 0); - if (pci_read_field(dev, PCI_VENDOR_ID, 2) == PCI_NONE) { - return; - } + if (pci_read_field(dev, PCI_VENDOR_ID, 2) == PCI_NONE) { + return; + } - pci_scan_func(f, type, bus, slot, 0, extra); + pci_scan_func(f, type, bus, slot, 0, extra); - if (!pci_read_field(dev, PCI_HEADER_TYPE, 1)) { - return; - } + if (!pci_read_field(dev, PCI_HEADER_TYPE, 1)) { + return; + } - for (int func = 1; func < 8; func++) { - dev = pci_box_device(bus, slot, func); + for (int func = 1; func < 8; func++) { + dev = pci_box_device(bus, slot, func); - if (pci_read_field(dev, PCI_VENDOR_ID, 2) != PCI_NONE) { - pci_scan_func(f, type, bus, slot, func, extra); - } - } + if (pci_read_field(dev, PCI_VENDOR_ID, 2) != PCI_NONE) { + pci_scan_func(f, type, bus, slot, func, extra); + } + } } -void pci_scan_bus(pci_func_t f, int type, int bus, void *extra) +void pci_scan_bus(pci_scan_func_t f, int type, int bus, void *extra) { - for (int slot = 0; slot < 32; ++slot) { - pci_scan_slot(f, type, bus, slot, extra); - } + for (int slot = 0; slot < 32; ++slot) { + pci_scan_slot(f, type, bus, slot, extra); + } } -void pci_scan(pci_func_t f, int type, void *extra) +void pci_scan(pci_scan_func_t f, int type, void *extra) { - if ((pci_read_field(0, PCI_HEADER_TYPE, 1) & 0x80) == 0) { - pci_scan_bus(f, type, 0, extra); - return; - } + if ((pci_read_field(0, PCI_HEADER_TYPE, 1) & 0x80) == 0) { + pci_scan_bus(f, type, 0, extra); + return; + } - for (int func = 0; func < 8; ++func) { - uint32_t dev = pci_box_device(0, 0, func); + for (int func = 0; func < 8; ++func) { + uint32_t dev = pci_box_device(0, 0, func); - if (pci_read_field(dev, PCI_VENDOR_ID, 2) == PCI_NONE) { - break; - } - pci_scan_bus(f, type, func, extra); - } + if (pci_read_field(dev, PCI_VENDOR_ID, 2) == PCI_NONE) { + break; + } + pci_scan_bus(f, type, func, extra); + } } -static void find_isa_bridge(uint32_t device, uint16_t vendorid, - uint16_t deviceid, void *extra) +static void find_isa_bridge(uint32_t device, uint16_t vendorid, uint16_t deviceid, void *extra) { - if (vendorid == 0x8086 && (deviceid == 0x7000 || deviceid == 0x7110)) { - *((uint32_t *)extra) = device; - } + if (vendorid == 0x8086 && (deviceid == 0x7000 || deviceid == 0x7110)) { + *((uint32_t *)extra) = device; + } } -static uint32_t pci_isa = 0; +static uint32_t pci_isa = 0; static uint32_t pci_remaps[4] = { 0 }; void pci_remap() { - pci_scan(&find_isa_bridge, -1, &pci_isa); + pci_scan(&find_isa_bridge, -1, &pci_isa); - if (pci_isa) { - dbg_print("PCI-to-ISA interrupt mappings by line:\n"); + if (pci_isa) { + pr_default("PCI-to-ISA interrupt mappings by line:\n"); - for (int i = 0; i < 4; ++i) { - pci_remaps[i] = pci_read_field(pci_isa, 0x60 + i, 1); - dbg_print("\tLine %d: 0x%2x\n", i + 1, pci_remaps[i]); - } + for (int i = 0; i < 4; ++i) { + pci_remaps[i] = pci_read_field(pci_isa, 0x60 + i, 1); + pr_default("\tLine %d: 0x%2x\n", i + 1, pci_remaps[i]); + } - uint32_t out = 0; - memcpy(&out, &pci_remaps, 4); - pci_write_field(pci_isa, 0x60, 4, out); - } + uint32_t out = 0; + memcpy(&out, &pci_remaps, 4); + pci_write_field(pci_isa, 0x60, 4, out); + } } int pci_get_interrupt(uint32_t device) { - if (pci_isa == 0) { - return pci_read_field(device, PCI_INTERRUPT_LINE, 1); - } + if (pci_isa == 0) { + return pci_read_field(device, PCI_INTERRUPT_LINE, 1); + } - uint32_t irq_pin = pci_read_field(device, PCI_INTERRUPT_PIN, 1); + uint32_t irq_pin = pci_read_field(device, PCI_INTERRUPT_PIN, 1); - if (irq_pin == 0) { - dbg_print("PCI device does not specific interrupt line\n"); - return pci_read_field(device, PCI_INTERRUPT_LINE, 1); - } + if (irq_pin == 0) { + pr_default("PCI device does not specific interrupt line\n"); + return pci_read_field(device, PCI_INTERRUPT_LINE, 1); + } - int pirq = (irq_pin + pci_extract_slot(device) - 2) % 4; + int pirq = (irq_pin + pci_extract_slot(device) - 2) % 4; - uint32_t int_line = pci_read_field(device, PCI_INTERRUPT_LINE, 1); + uint32_t int_line = pci_read_field(device, PCI_INTERRUPT_LINE, 1); - dbg_print( - "Slot is %d, irq pin is %d, so pirq is %d and that maps to %d?" - "int_line=%d\n", - pci_extract_slot(device), irq_pin, pirq, pci_remaps[pirq], - int_line); + pr_default("Slot is %d, irq pin is %d, so pirq is %d and that maps to %d?" + "int_line=%d\n", + pci_extract_slot(device), irq_pin, pirq, pci_remaps[pirq], + int_line); - if (pci_remaps[pirq] == 0x80) { - dbg_print("Not mapped, remapping?\n"); - pci_remaps[pirq] = int_line; - uint32_t out = 0; - memcpy(&out, &pci_remaps, 4); - pci_write_field(pci_isa, 0x60, 4, out); - return pci_read_field(device, PCI_INTERRUPT_LINE, 1); - } + if (pci_remaps[pirq] == 0x80) { + pr_default("Not mapped, remapping?\n"); + pci_remaps[pirq] = int_line; + uint32_t out = 0; + memcpy(&out, &pci_remaps, 4); + pci_write_field(pci_isa, 0x60, 4, out); + return pci_read_field(device, PCI_INTERRUPT_LINE, 1); + } - return pci_remaps[pirq]; + return pci_remaps[pirq]; } -static void scan_count(uint32_t device, uint16_t vendorid, uint16_t deviceid, - void *extra) +static void __scan_count(uint32_t device, uint16_t vendorid, uint16_t deviceid, void *extra) { - (void)device; - (void)vendorid; - (void)deviceid; - size_t *count = extra; - ++(*count); + (void)device; + (void)vendorid; + (void)deviceid; + size_t *count = extra; + ++(*count); } -static void scan_hit_list(uint32_t device, uint16_t vendorid, uint16_t deviceid, - void *extra) +static void __scan_hit_list(uint32_t device, uint16_t vendorid, uint16_t deviceid, void *extra) { - (void)extra; - dbg_print("%2x:%2x.%d\n", pci_extract_bus(device), - pci_extract_slot(device), pci_extract_func(device), deviceid); - dbg_print(" Vendor : [0x%06x] %s\n", vendorid, - pci_vendor_lookup(vendorid)); - dbg_print(" Device : [0x%06x] %s\n", deviceid, - pci_device_lookup(vendorid, deviceid)); - dbg_print(" Type : [0x%06x] %s\n", pci_find_type(device), - pci_class_lookup(pci_find_type(device))); - dbg_print(" Status : 0x%4x\n", - pci_read_field(device, PCI_STATUS, 2)); - dbg_print(" Command : 0x%4x\n", - pci_read_field(device, PCI_COMMAND, 2)); - dbg_print(" Revision : %2d\n", - pci_read_field(device, PCI_REVISION_ID, 1)); - dbg_print(" Cache Line Size : %2d\n", - pci_read_field(device, PCI_CACHE_LINE_SIZE, 1)); - dbg_print(" Latency Timer : %2d\n", - pci_read_field(device, PCI_LATENCY_TIMER, 1)); - dbg_print(" Header Type : %2d\n", - pci_read_field(device, PCI_HEADER_TYPE, 1)); - dbg_print(" BIST : %2d\n", - pci_read_field(device, PCI_BIST, 1)); - dbg_print(" BAR0 : 0x%08x", - pci_read_field(device, PCI_BASE_ADDRESS_0, 4)); - dbg_print(" BAR1 : 0x%08x", - pci_read_field(device, PCI_BASE_ADDRESS_1, 4)); - dbg_print(" BAR2 : 0x%08x\n", - pci_read_field(device, PCI_BASE_ADDRESS_2, 4)); - dbg_print(" BAR3 : 0x%08x", - pci_read_field(device, PCI_BASE_ADDRESS_3, 4)); - dbg_print(" BAR4 : 0x%08x", - pci_read_field(device, PCI_BASE_ADDRESS_4, 4)); - dbg_print(" BAR6 : 0x%08x\n", - pci_read_field(device, PCI_BASE_ADDRESS_5, 4)); - dbg_print(" Cardbus CIS : 0x%08x\n", - pci_read_field(device, PCI_CARDBUS_CIS, 4)); - dbg_print(" Subsystem V. ID : 0x%08x\n", - pci_read_field(device, PCI_SUBSYSTEM_VENDOR_ID, 2)); - dbg_print(" Subsystem ID : 0x%08x\n", - pci_read_field(device, PCI_SUBSYSTEM_ID, 2)); - dbg_print(" ROM Base Address: 0x%08x\n", - pci_read_field(device, PCI_ROM_ADDRESS, 4)); - dbg_print(" PCI Cp. LinkList: 0x%08x\n", - pci_read_field(device, PCI_CAPABILITY_LIST, 1)); - dbg_print(" Max Latency : 0x%08x\n", - pci_read_field(device, PCI_MAX_LAT, 1)); - dbg_print(" Min Grant : 0x%08x\n", - pci_read_field(device, PCI_MIN_GNT, 1)); - dbg_print(" Interrupt Pin : %2d\n", - pci_read_field(device, PCI_INTERRUPT_PIN, 1)); - dbg_print(" Interrupt Line : %2d\n", - pci_read_field(device, PCI_INTERRUPT_LINE, 1)); - dbg_print(" Interrupt Number: %2d\n", pci_get_interrupt(device)); - dbg_print("\n"); + (void)extra; + pr_default("%2x:%2x.%d\n", pci_extract_bus(device), pci_extract_slot(device), pci_extract_func(device), deviceid); + pr_default(" Vendor : [0x%06x] %s\n", vendorid, pci_vendor_lookup(vendorid)); + pr_default(" Device : [0x%06x] %s\n", deviceid, pci_device_lookup(vendorid, deviceid)); + pr_default(" Type : [0x%06x] %s\n", pci_find_type(device), pci_class_lookup(pci_find_type(device))); + pr_default(" Status : 0x%4x\n", pci_read_field(device, PCI_STATUS, 2)); + pr_default(" Command : 0x%4x\n", pci_read_field(device, PCI_COMMAND, 2)); + pr_default(" Revision : %2d\n", pci_read_field(device, PCI_REVISION_ID, 1)); + pr_default(" Cache Line Size : %2d\n", pci_read_field(device, PCI_CACHE_LINE_SIZE, 1)); + pr_default(" Latency Timer : %2d\n", pci_read_field(device, PCI_LATENCY_TIMER, 1)); + pr_default(" Header Type : %2d\n", pci_read_field(device, PCI_HEADER_TYPE, 1)); + pr_default(" BIST : %2d\n", pci_read_field(device, PCI_BIST, 1)); + pr_default(" BAR0 : 0x%08x\n", pci_read_field(device, PCI_BASE_ADDRESS_0, 4)); + pr_default(" BAR1 : 0x%08x\n", pci_read_field(device, PCI_BASE_ADDRESS_1, 4)); + pr_default(" BAR2 : 0x%08x\n", pci_read_field(device, PCI_BASE_ADDRESS_2, 4)); + pr_default(" BAR3 : 0x%08x\n", pci_read_field(device, PCI_BASE_ADDRESS_3, 4)); + pr_default(" BAR4 : 0x%08x\n", pci_read_field(device, PCI_BASE_ADDRESS_4, 4)); + pr_default(" BAR6 : 0x%08x\n", pci_read_field(device, PCI_BASE_ADDRESS_5, 4)); + pr_default(" Cardbus CIS : 0x%08x\n", pci_read_field(device, PCI_CARDBUS_CIS, 4)); + pr_default(" Subsystem V. ID : 0x%08x\n", pci_read_field(device, PCI_SUBSYSTEM_VENDOR_ID, 2)); + pr_default(" Subsystem ID : 0x%08x\n", pci_read_field(device, PCI_SUBSYSTEM_ID, 2)); + pr_default(" ROM Base Address: 0x%08x\n", pci_read_field(device, PCI_ROM_ADDRESS, 4)); + pr_default(" PCI Cp. LinkList: 0x%08x\n", pci_read_field(device, PCI_CAPABILITY_LIST, 1)); + pr_default(" Max Latency : 0x%08x\n", pci_read_field(device, PCI_MAX_LAT, 1)); + pr_default(" Min Grant : 0x%08x\n", pci_read_field(device, PCI_MIN_GNT, 1)); + pr_default(" Interrupt Pin : %2d\n", pci_read_field(device, PCI_INTERRUPT_PIN, 1)); + pr_default(" Interrupt Line : %2d\n", pci_read_field(device, PCI_INTERRUPT_LINE, 1)); + pr_default(" Interrupt Number: %2d\n", pci_get_interrupt(device)); + pr_default("\n"); } void pci_debug_scan() { - dbg_print("\n--------------------------------------------------\n"); - dbg_print("Counting PCI entities...\n"); - size_t count = 0; - pci_scan(&scan_count, -1, &count); - dbg_print("Total PCI entities: %d\n", count); + pr_default("\n--------------------------------------------------\n"); + pr_default("Counting PCI entities...\n"); + size_t count = 0; + pci_scan(&__scan_count, -1, &count); + pr_default("Total PCI entities: %d\n", count); - dbg_print("Scanning PCI entities...\n"); - pci_scan(&scan_hit_list, -1, NULL); + pr_default("Scanning PCI entities...\n"); + pci_scan(&__scan_hit_list, -1, NULL); - dbg_print("Mapping PCI entities...\n"); - pci_remap(); + pr_default("Mapping PCI entities...\n"); + pci_remap(); - dbg_print("--------------------------------------------------\n"); + pr_default("--------------------------------------------------\n"); } + +///! @endcond \ No newline at end of file diff --git a/mentos/src/drivers/ata.c b/mentos/src/drivers/ata.c index f59a706..647040b 100644 --- a/mentos/src/drivers/ata.c +++ b/mentos/src/drivers/ata.c @@ -1,74 +1,76 @@ /// MentOS, The Mentoring Operating system project /// @file ata.c /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +///! @cond Doxygen_Suppress -#include "ata.h" -#include "isr.h" -#include "vfs.h" -#include "pci.h" -#include "list.h" -#include "debug.h" +#include "drivers/ata.h" +#include "klib/spinlock.h" +#include "fcntl.h" +#include "mem/vmem_map.h" +#include "klib/list.h" #include "stdio.h" -#include "kheap.h" +#include "descriptor_tables/isr.h" +#include "fs/vfs.h" +#include "devices/pci.h" +#include "misc/debug.h" +#include "mem/kheap.h" #include "assert.h" #include "string.h" #include "kernel.h" -#include "pic8259.h" -#include "spinlock.h" +#include "hardware/pic8259.h" +#include "io/port_io.h" +#include "time.h" -// #define COMPLETE_VFS // #define COMPLETE_SCHEDULER -uint32_t ata_pci = 0x00000000; +static char ata_drive_char = 'a'; +static int cdrom_number = 0; +static uint32_t ata_pci = 0x00000000; +static int atapi_in_progress = 0; +static list_t *atapi_waiter = NULL; -list_t *atapi_waiter; - -bool_t atapi_in_progress = false; - -typedef union { - uint8_t command_bytes[12]; - uint16_t command_words[6]; +typedef union atapi_command_t { + uint8_t command_bytes[12]; + uint16_t command_words[6]; } atapi_command_t; -typedef struct { - uintptr_t offset; - uint16_t bytes; - uint16_t last; +/// @brief Physical Region Descriptor Tables (PRDT). +typedef struct prdt_t { + uintptr_t offset; + uint16_t bytes; + uint16_t last; } prdt_t; -typedef struct { - char name[256]; - int io_base; - int control; - int slave; - bool_t is_atapi; - ata_identify_t identity; - prdt_t *dma_prdt; - uintptr_t dma_prdt_phys; - uint8_t *dma_start; - uintptr_t dma_start_phys; - uint32_t bar4; - uint32_t atapi_lba; - uint32_t atapi_sector_size; +/// @brief Stores information about an ATA device. +typedef struct ata_device_t { + char name[256]; + int io_base; + int control; + int slave; + int is_atapi; + ata_identify_t identity; + /// Physical Region Descriptor Table (PRDT). + prdt_t *dma_prdt; + /// Stores the physical address of the current PRDT in the Bus Master Register, + /// of the Bus Mastering ATA Disk Controller on the PCI bus. + uintptr_t dma_prdt_phys; + uint8_t *dma_start; + uintptr_t dma_start_phys; + uint32_t bar4; + uint32_t atapi_lba; + uint32_t atapi_sector_size; + /// Device root file. + vfs_file_t *fs_root; } ata_device_t; -ata_device_t ata_primary_master = { .io_base = 0x1F0, - .control = 0x3F6, - .slave = 0 }; -ata_device_t ata_primary_slave = { .io_base = 0x1F0, - .control = 0x3F6, - .slave = 1 }; -ata_device_t ata_secondary_master = { .io_base = 0x170, - .control = 0x376, - .slave = 0 }; -ata_device_t ata_secondary_slave = { .io_base = 0x170, - .control = 0x376, - .slave = 1 }; +ata_device_t ata_primary_master = { .io_base = 0x1F0, .control = 0x3F6, .slave = 0 }; +ata_device_t ata_primary_slave = { .io_base = 0x1F0, .control = 0x3F6, .slave = 1 }; +ata_device_t ata_secondary_master = { .io_base = 0x170, .control = 0x376, .slave = 0 }; +ata_device_t ata_secondary_slave = { .io_base = 0x170, .control = 0x376, .slave = 1 }; -// volatile uint8_t ata_lock = 0; -spinlock_t ata_lock = { 0 }; +spinlock_t ata_lock; // TODO: support other sector sizes. #define ATA_SECTOR_SIZE 512 @@ -77,730 +79,728 @@ spinlock_t ata_lock = { 0 }; /// @param dev void ata_io_wait(ata_device_t *dev) { - inportb(dev->io_base + ATA_REG_ALTSTATUS); - inportb(dev->io_base + ATA_REG_ALTSTATUS); - inportb(dev->io_base + ATA_REG_ALTSTATUS); - inportb(dev->io_base + ATA_REG_ALTSTATUS); - inportb(dev->io_base + ATA_REG_ALTSTATUS); + inportb(dev->io_base + ATA_REG_ALTSTATUS); + inportb(dev->io_base + ATA_REG_ALTSTATUS); + inportb(dev->io_base + ATA_REG_ALTSTATUS); + inportb(dev->io_base + ATA_REG_ALTSTATUS); + inportb(dev->io_base + ATA_REG_ALTSTATUS); } -uint8_t ata_status_wait(ata_device_t *dev, int timeout) -{ - uint8_t status; - if (timeout > 0) { - for (int i = 0; (status = inportb(dev->io_base + ATA_REG_STATUS)) & - ATA_STAT_BUSY && - (i < timeout); - ++i) - ; - } else { - while ((status = inportb(dev->io_base + ATA_REG_STATUS)) & - ATA_STAT_BUSY) - ; - } +static void ata_device_read_sector(ata_device_t *, uint64_t, uint8_t *); +static void ata_device_read_sector_atapi(ata_device_t *, uint64_t, uint8_t *); +static void ata_device_write_sector_retry(ata_device_t *, uint64_t, uint8_t *); - return status; +static vfs_file_t *ata_open(const char *, int, mode_t); +static int ata_close(vfs_file_t *); +static ssize_t ata_read(vfs_file_t *, char *, off_t, size_t); +static ssize_t atapi_read(vfs_file_t *, char *, off_t, size_t); +static ssize_t ata_write(vfs_file_t *, const void *, off_t, size_t); +static int ata_fstat(vfs_file_t *file, stat_t *stat); +static int ata_stat(const char *path, stat_t *stat); + +// == SUPPORT FUNCTIONS ======================================================= +static uint8_t ata_status_wait(ata_device_t *dev, int timeout) +{ + uint8_t status; + if (timeout > 0) { + for (int i = 0; (status = inportb(dev->io_base + ATA_REG_STATUS)) & ATA_STAT_BUSY && (i < timeout); ++i) {} + } else { + while ((status = inportb(dev->io_base + ATA_REG_STATUS)) & ATA_STAT_BUSY) {} + } + return status; +} +static int ata_wait(ata_device_t *dev, bool_t advanced) +{ + ata_io_wait(dev); + ata_status_wait(dev, 0); + if (advanced) { + uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); + if (status & ATA_STAT_ERR) + return 1; + if (status & ATA_STAT_FAULT) + return 1; + if (!(status & ATA_STAT_DRQ)) + return 1; + } + return 0; +} +static void ata_device_select(ata_device_t *dev) +{ + outportb(dev->io_base + 1, 1); + outportb(dev->control, 0); + outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4); + ata_io_wait(dev); +} +static uint64_t ata_max_offset(ata_device_t *dev) +{ + uint64_t sectors = dev->identity.sectors_48; + if (!sectors) { + // Fall back to sectors_28. + sectors = dev->identity.sectors_28; + } + + return sectors * ATA_SECTOR_SIZE; +} +static uint64_t atapi_max_offset(ata_device_t *dev) +{ + uint64_t max_sector = dev->atapi_lba; + if (!max_sector) { + return 0; + } + return (max_sector + 1) * dev->atapi_sector_size; +} +static int buffer_compare(uint32_t *ptr1, uint32_t *ptr2, size_t size) +{ + assert(!(size % 4)); + + size_t i = 0; + + while (i < size) { + if (*ptr1 != *ptr2) { + return 1; + } + + ptr1++; + ptr2++; + i += sizeof(uint32_t); + } + return 0; } -int ata_wait(ata_device_t *dev, bool_t advanced) -{ - ata_io_wait(dev); - uint8_t status = ata_status_wait(dev, 0); - if (advanced) { - status = inportb(dev->io_base + ATA_REG_STATUS); - if (status & ATA_STAT_ERR) - return 1; - if (status & ATA_STAT_FAULT) - return 1; - if (!(status & ATA_STAT_DRQ)) - return 1; - } +// == VFS ENTRY GENERATION ==================================================== +/// Filesystem general operations. +static vfs_sys_operations_t ata_sys_operations = { + .mkdir_f = NULL, + .rmdir_f = NULL, + .stat_f = ata_stat +}; - return 0; +/// ATA filesystem file operations. +static vfs_file_operations_t ata_fs_operations = { + .open_f = ata_open, + .unlink_f = NULL, + .close_f = ata_close, + .read_f = ata_read, + .write_f = ata_write, + .lseek_f = NULL, + .stat_f = ata_fstat, + .ioctl_f = NULL, + .getdents_f = NULL +}; + +/// ATAPI filesystem file operations. +static vfs_file_operations_t atapi_fs_operations = { + .open_f = ata_open, + .unlink_f = NULL, + .close_f = ata_close, + .read_f = atapi_read, + .write_f = NULL, + .lseek_f = NULL, + .stat_f = ata_fstat, + .ioctl_f = NULL, + .getdents_f = NULL +}; + +static vfs_file_t *atapi_device_create(ata_device_t *device) +{ + char path[PATH_MAX]; + sprintf(path, "/dev/%s", device->name); + // Create the file. + vfs_file_t *file = vfs_open(path, O_RDONLY | O_CREAT, 0); + // Set the device. + file->device = device; + // Set the length + file->length = atapi_max_offset(device); + // Re-set the flags. + file->flags = DT_BLK; + // Change the operations. + file->sys_operations = &ata_sys_operations; + file->fs_operations = &atapi_fs_operations; + return file; +} +static vfs_file_t *ata_device_create(ata_device_t *device) +{ + char path[PATH_MAX]; + sprintf(path, "/dev/%s", device->name); + // Create the file. + vfs_file_t *file = vfs_open(path, O_RDWR | O_CREAT, 0); + // Set the device. + file->device = device; + // Set the length + file->length = ata_max_offset(device); + // Re-set the flags. + file->flags = DT_BLK; + // Change the operations. + file->sys_operations = &ata_sys_operations; + file->fs_operations = &ata_fs_operations; + return file; } -void ata_device_select(ata_device_t *dev) +// == VFS CALLBACKS =========================================================== +static vfs_file_t *ata_open(const char *path, int flags, mode_t mode) { - outportb(dev->io_base + 1, 1); - outportb(dev->control, 0); - outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4); - ata_io_wait(dev); + pr_default("ata_open(%s, %d, %d)\n", path, flags, mode); + if (ata_primary_master.fs_root && (strcmp(path, ata_primary_master.fs_root->name) == 0)) { + ++ata_primary_master.fs_root->count; + return ata_primary_master.fs_root; + } + if (ata_primary_slave.fs_root && (strcmp(path, ata_primary_slave.fs_root->name) == 0)) { + ++ata_primary_slave.fs_root->count; + return ata_primary_slave.fs_root; + } + if (ata_secondary_master.fs_root && (strcmp(path, ata_secondary_master.fs_root->name) == 0)) { + ++ata_secondary_master.fs_root->count; + return ata_secondary_master.fs_root; + } + if (ata_secondary_slave.fs_root && (strcmp(path, ata_secondary_slave.fs_root->name) == 0)) { + ++ata_secondary_slave.fs_root->count; + return ata_secondary_slave.fs_root; + } + return NULL; +} +static int ata_close(vfs_file_t *file) +{ + pr_default("ata_close(%p)\n", file); + if (ata_primary_master.fs_root == file) { + --ata_primary_master.fs_root->count; + } + if (ata_primary_slave.fs_root == file) { + --ata_primary_slave.fs_root->count; + } + if (ata_secondary_master.fs_root == file) { + --ata_secondary_master.fs_root->count; + } + if (ata_secondary_slave.fs_root == file) { + --ata_secondary_slave.fs_root->count; + } + return 0; +} +static ssize_t ata_read(vfs_file_t *file, char *buffer, off_t offset, size_t size) +{ + pr_default("ata_read(%p, %p, %d, %d)\n", file, buffer, offset, size); + ata_device_t *dev = (ata_device_t *)file->device; + assert(dev); + + unsigned int start_block = offset / ATA_SECTOR_SIZE; + unsigned int end_block = (offset + size - 1) / ATA_SECTOR_SIZE; + + unsigned int x_offset = 0; + + if (offset > ata_max_offset(dev)) { + return 0; + } + + if (offset + size > ata_max_offset(dev)) { + unsigned int i = ata_max_offset(dev) - offset; + size = i; + } + + if (offset % ATA_SECTOR_SIZE) { + unsigned int prefix_size = (ATA_SECTOR_SIZE - (offset % ATA_SECTOR_SIZE)); + char *tmp = kmalloc(ATA_SECTOR_SIZE); + ata_device_read_sector(dev, start_block, (uint8_t *)tmp); + memcpy(buffer, (void *)((uintptr_t)tmp + (offset % ATA_SECTOR_SIZE)), prefix_size); + kfree(tmp); + x_offset += prefix_size; + start_block++; + } + if ((offset + size) % ATA_SECTOR_SIZE && start_block <= end_block) { + unsigned int postfix_size = (offset + size) % ATA_SECTOR_SIZE; + char *tmp = kmalloc(ATA_SECTOR_SIZE); + ata_device_read_sector(dev, end_block, (uint8_t *)tmp); + memcpy((void *)((uintptr_t)buffer + size - postfix_size), tmp, postfix_size); + kfree(tmp); + end_block--; + } + while (start_block <= end_block) { + ata_device_read_sector(dev, start_block, (uint8_t *)((uintptr_t)buffer + x_offset)); + x_offset += ATA_SECTOR_SIZE; + start_block++; + } + return size; +} +static ssize_t atapi_read(vfs_file_t *file, char *buffer, off_t offset, size_t size) +{ + pr_default("atapi_read(%p, %p, %d, %d)\n", file, buffer, offset, size); + ata_device_t *dev = (ata_device_t *)file->device; + unsigned int start_block = offset / dev->atapi_sector_size; + unsigned int end_block = (offset + size - 1) / dev->atapi_sector_size; + unsigned int x_offset = 0; + if (offset > atapi_max_offset(dev)) { + return 0; + } + if (offset + size > atapi_max_offset(dev)) { + unsigned int i = atapi_max_offset(dev) - offset; + size = i; + } + if (offset % dev->atapi_sector_size) { + unsigned int prefix_size = (dev->atapi_sector_size - (offset % dev->atapi_sector_size)); + char *tmp = kmalloc(dev->atapi_sector_size); + ata_device_read_sector_atapi(dev, start_block, (uint8_t *)tmp); + memcpy(buffer, (void *)((uintptr_t)tmp + (offset % dev->atapi_sector_size)), prefix_size); + kfree(tmp); + x_offset += prefix_size; + start_block++; + } + if ((offset + size) % dev->atapi_sector_size && start_block <= end_block) { + unsigned int postfix_size = (offset + size) % dev->atapi_sector_size; + char *tmp = kmalloc(dev->atapi_sector_size); + ata_device_read_sector_atapi(dev, end_block, (uint8_t *)tmp); + memcpy((void *)((uintptr_t)buffer + size - postfix_size), tmp, postfix_size); + kfree(tmp); + end_block--; + } + while (start_block <= end_block) { + ata_device_read_sector_atapi(dev, start_block, (uint8_t *)((uintptr_t)buffer + x_offset)); + x_offset += dev->atapi_sector_size; + start_block++; + } + return size; +} +static ssize_t ata_write(vfs_file_t *file, const void *buffer, off_t offset, size_t size) +{ + ata_device_t *dev = (ata_device_t *)file->device; + unsigned int start_block = offset / ATA_SECTOR_SIZE; + unsigned int end_block = (offset + size - 1) / ATA_SECTOR_SIZE; + unsigned int x_offset = 0; + if (offset > ata_max_offset(dev)) { + return 0; + } + if (offset + size > ata_max_offset(dev)) { + unsigned int i = ata_max_offset(dev) - offset; + size = i; + } + if (offset % ATA_SECTOR_SIZE) { + unsigned int prefix_size = (ATA_SECTOR_SIZE - (offset % ATA_SECTOR_SIZE)); + char *tmp = kmalloc(ATA_SECTOR_SIZE); + ata_device_read_sector(dev, start_block, (uint8_t *)tmp); + pr_default("Writing first block"); + memcpy((void *)((uintptr_t)tmp + (offset % ATA_SECTOR_SIZE)), buffer, prefix_size); + ata_device_write_sector_retry(dev, start_block, (uint8_t *)tmp); + kfree(tmp); + x_offset += prefix_size; + start_block++; + } + if ((offset + size) % ATA_SECTOR_SIZE && start_block <= end_block) { + unsigned int postfix_size = (offset + size) % ATA_SECTOR_SIZE; + char *tmp = kmalloc(ATA_SECTOR_SIZE); + ata_device_read_sector(dev, end_block, (uint8_t *)tmp); + pr_default("Writing last block"); + memcpy(tmp, (void *)((uintptr_t)buffer + size - postfix_size), postfix_size); + ata_device_write_sector_retry(dev, end_block, (uint8_t *)tmp); + kfree(tmp); + end_block--; + } + while (start_block <= end_block) { + ata_device_write_sector_retry(dev, start_block, (uint8_t *)((uintptr_t)buffer + x_offset)); + x_offset += ATA_SECTOR_SIZE; + start_block++; + } + return size; +} +static int _ata_stat(const ata_device_t *device, stat_t *stat) +{ + if (device) { + stat->st_dev = 0; + stat->st_ino = 0; + stat->st_mode = 0; + stat->st_uid = 0; + stat->st_gid = 0; + stat->st_atime = sys_time(NULL); + stat->st_mtime = sys_time(NULL); + stat->st_ctime = sys_time(NULL); + stat->st_size = 0; + } + return 0; } -void ata_device_read_sector(ata_device_t *dev, uint32_t lba, uint8_t *buf); - -void ata_device_read_sector_atapi(ata_device_t *dev, uint32_t lba, - uint8_t *buf); - -void ata_device_write_sector_retry(ata_device_t *dev, uint32_t lba, - uint8_t *buf); - -#ifdef COMPLETE_VFS -uint32_t read_ata(fs_node_t *node, uint32_t offset, uint32_t size, - uint8_t *buffer); -uint32_t write_ata(fs_node_t *node, uint32_t offset, uint32_t size, - uint8_t *buffer); -void open_ata(fs_node_t *node, unsigned int flags); -void close_ata(fs_node_t *node); -#endif - -uint64_t ata_max_offset(ata_device_t *dev) +/// @brief Retrieves information concerning the file at the given position. +/// @param fid The file struct. +/// @param stat The structure where the information are stored. +/// @return 0 if success. +static int ata_fstat(vfs_file_t *file, stat_t *stat) { - uint64_t sectors = dev->identity.sectors_48; - if (!sectors) { - // Fall back to sectors_28. - sectors = dev->identity.sectors_28; - } - - return sectors * ATA_SECTOR_SIZE; + return _ata_stat(file->device, stat); } -uint64_t atapi_max_offset(ata_device_t *dev) +/// @brief Retrieves information concerning the file at the given position. +/// @param path The path where the file resides. +/// @param stat The structure where the information are stored. +/// @return 0 if success. +static int ata_stat(const char *path, stat_t *stat) { - uint64_t max_sector = dev->atapi_lba; - - if (!max_sector) { - return 0; - } - - return (max_sector + 1) * dev->atapi_sector_size; + super_block_t *sb = vfs_get_superblock(path); + if (sb && sb->root) { + return _ata_stat(sb->root->device, stat); + } + return -1; } -#ifdef COMPLETE_VFS -uint32_t read_ata(fs_node_t *node, uint32_t offset, uint32_t size, - uint8_t *buffer) +// == ATA DEVICE MANAGEMENT =================================================== +static bool_t ata_device_init(ata_device_t *dev) { - ata_device_t *dev = (ata_device_t *)node->device; - - unsigned int start_block = offset / ATA_SECTOR_SIZE; - unsigned int end_block = (offset + size - 1) / ATA_SECTOR_SIZE; - - unsigned int x_offset = 0; - - if (offset > ata_max_offset(dev)) { - return 0; - } - - if (offset + size > ata_max_offset(dev)) { - unsigned int i = ata_max_offset(dev) - offset; - size = i; - } - - if (offset % ATA_SECTOR_SIZE) { - unsigned int prefix_size = - (ATA_SECTOR_SIZE - (offset % ATA_SECTOR_SIZE)); - char *tmp = malloc(ATA_SECTOR_SIZE); - ata_device_read_sector(dev, start_block, (uint8_t *)tmp); - - memcpy(buffer, (void *)((uintptr_t)tmp + (offset % ATA_SECTOR_SIZE)), - prefix_size); - - free(tmp); - - x_offset += prefix_size; - start_block++; - } - - if ((offset + size) % ATA_SECTOR_SIZE && start_block <= end_block) { - unsigned int postfix_size = (offset + size) % ATA_SECTOR_SIZE; - char *tmp = malloc(ATA_SECTOR_SIZE); - ata_device_read_sector(dev, end_block, (uint8_t *)tmp); - - memcpy((void *)((uintptr_t)buffer + size - postfix_size), tmp, - postfix_size); - - free(tmp); - - end_block--; - } - - while (start_block <= end_block) { - ata_device_read_sector(dev, start_block, - (uint8_t *)((uintptr_t)buffer + x_offset)); - x_offset += ATA_SECTOR_SIZE; - start_block++; - } - - return size; -} -#endif - -#ifdef COMPLETE_VFS -uint32_t read_atapi(fs_node_t *node, uint32_t offset, uint32_t size, - uint8_t *buffer) -{ - ata_device_t *dev = (ata_device_t *)node->device; - - unsigned int start_block = offset / dev->atapi_sector_size; - unsigned int end_block = (offset + size - 1) / dev->atapi_sector_size; - - unsigned int x_offset = 0; - - if (offset > atapi_max_offset(dev)) { - return 0; - } - - if (offset + size > atapi_max_offset(dev)) { - unsigned int i = atapi_max_offset(dev) - offset; - size = i; - } - - if (offset % dev->atapi_sector_size) { - unsigned int prefix_size = - (dev->atapi_sector_size - (offset % dev->atapi_sector_size)); - char *tmp = malloc(dev->atapi_sector_size); - ata_device_read_sector_atapi(dev, start_block, (uint8_t *)tmp); - - memcpy(buffer, - (void *)((uintptr_t)tmp + (offset % dev->atapi_sector_size)), - prefix_size); - - free(tmp); - - x_offset += prefix_size; - start_block++; - } - - if ((offset + size) % dev->atapi_sector_size && start_block <= end_block) { - unsigned int postfix_size = (offset + size) % dev->atapi_sector_size; - char *tmp = malloc(dev->atapi_sector_size); - ata_device_read_sector_atapi(dev, end_block, (uint8_t *)tmp); - - memcpy((void *)((uintptr_t)buffer + size - postfix_size), tmp, - postfix_size); - - free(tmp); - - end_block--; - } - - while (start_block <= end_block) { - ata_device_read_sector_atapi(dev, start_block, - (uint8_t *)((uintptr_t)buffer + x_offset)); - x_offset += dev->atapi_sector_size; - start_block++; - } - - return size; -} -#endif - -#ifdef COMPLETE_VFS -uint32_t write_ata(fs_node_t *node, uint32_t offset, uint32_t size, - uint8_t *buffer) -{ - ata_device_t *dev = (ata_device_t *)node->device; - - unsigned int start_block = offset / ATA_SECTOR_SIZE; - unsigned int end_block = (offset + size - 1) / ATA_SECTOR_SIZE; - - unsigned int x_offset = 0; - - if (offset > ata_max_offset(dev)) { - return 0; - } - - if (offset + size > ata_max_offset(dev)) { - unsigned int i = ata_max_offset(dev) - offset; - size = i; - } - - if (offset % ATA_SECTOR_SIZE) { - unsigned int prefix_size = - (ATA_SECTOR_SIZE - (offset % ATA_SECTOR_SIZE)); - - char *tmp = malloc(ATA_SECTOR_SIZE); - ata_device_read_sector(dev, start_block, (uint8_t *)tmp); - - dbg_print("Writing first block"); - - memcpy((void *)((uintptr_t)tmp + (offset % ATA_SECTOR_SIZE)), buffer, - prefix_size); - ata_device_write_sector_retry(dev, start_block, (uint8_t *)tmp); - - free(tmp); - x_offset += prefix_size; - start_block++; - } - - if ((offset + size) % ATA_SECTOR_SIZE && start_block <= end_block) { - unsigned int postfix_size = (offset + size) % ATA_SECTOR_SIZE; - - char *tmp = malloc(ATA_SECTOR_SIZE); - ata_device_read_sector(dev, end_block, (uint8_t *)tmp); - - dbg_print("Writing last block"); - - memcpy(tmp, (void *)((uintptr_t)buffer + size - postfix_size), - postfix_size); - - ata_device_write_sector_retry(dev, end_block, (uint8_t *)tmp); - - free(tmp); - end_block--; - } - - while (start_block <= end_block) { - ata_device_write_sector_retry( - dev, start_block, (uint8_t *)((uintptr_t)buffer + x_offset)); - x_offset += ATA_SECTOR_SIZE; - start_block++; - } - - return size; -} -#endif - -#ifdef COMPLETE_VFS -void open_ata(fs_node_t *node, unsigned int flags) -{ - return; -} -#endif - -#ifdef COMPLETE_VFS -void close_ata(fs_node_t *node) -{ - return; -} -#endif - -#ifdef COMPLETE_VFS -fs_node_t *atapi_device_create(ata_device_t *device) -{ - fs_node_t *fnode = malloc(sizeof(fs_node_t)); - memset(fnode, 0x00, sizeof(fs_node_t)); - fnode->inode = 0; - sprintf(fnode->name, "cdrom%d", cdrom_number); - fnode->device = device; - fnode->uid = 0; - fnode->gid = 0; - fnode->mask = 0660; - fnode->length = atapi_max_offset(device); - fnode->flags = FS_BLOCKDEVICE; - fnode->read = read_atapi; - // No write support. - fnode->write = NULL; - fnode->open = open_ata; - fnode->close = close_ata; - fnode->readdir = NULL; - fnode->finddir = NULL; - // TODO, identify: etc? - fnode->ioctl = NULL; - return fnode; -} -#endif - -#ifdef COMPLETE_VFS -fs_node_t *ata_device_create(ata_device_t *device) -{ - fs_node_t *fnode = malloc(sizeof(fs_node_t)); - memset(fnode, 0x00, sizeof(fs_node_t)); - fnode->inode = 0; - sprintf(fnode->name, "atadev%d", ata_drive_char - 'a'); - fnode->device = device; - fnode->uid = 0; - fnode->gid = 0; - fnode->mask = 0660; - // TODO - fnode->length = ata_max_offset(device); - fnode->flags = FS_BLOCKDEVICE; - fnode->read = read_ata; - fnode->write = write_ata; - fnode->open = open_ata; - fnode->close = close_ata; - fnode->readdir = NULL; - fnode->finddir = NULL; - // TODO: identify, etc? - fnode->ioctl = NULL; - return fnode; -} -#endif - -void ata_soft_reset(ata_device_t *dev) -{ - outportb(dev->control, 0x04); - ata_io_wait(dev); - outportb(dev->control, 0x00); -} - -void ata_irq_handler(pt_regs *r) -{ - inportb(ata_primary_master.io_base + ATA_REG_STATUS); - - if (atapi_in_progress) { - // wakeup_queue(atapi_waiter); - } - - // irq_ack(14); - pic8259_send_eoi(14); -} - -void ata_irq_handler_s(pt_regs *r) -{ - inportb(ata_secondary_master.io_base + ATA_REG_STATUS); - - if (atapi_in_progress) { - // wakeup_queue(atapi_waiter); - } - - // irq_ack(15); - pic8259_send_eoi(15); -} - -bool_t ata_device_init(ata_device_t *dev) -{ - dbg_print("Detected IDE device on bus 0x%3x\n", dev->io_base); - dbg_print("Device name: %s\n", dev->name); - ata_device_select(dev); - outportb(dev->io_base + ATA_REG_COMMAND, ATA_CMD_IDENT); - ata_io_wait(dev); - uint8_t status = inportb(dev->io_base + ATA_REG_COMMAND); - dbg_print("Device status: %d\n", status); - - ata_wait(dev, false); - - uint16_t *buf = (uint16_t *)&dev->identity; - for (int i = 0; i < 256; ++i) { - buf[i] = inports(dev->io_base); - } - - uint8_t *ptr = (uint8_t *)&dev->identity.model; - for (int i = 0; i < 39; i += 2) { - char tmp = ptr[i + 1]; - ptr[i + 1] = ptr[i]; - ptr[i] = tmp; - } - ptr[39] = 0; - - dbg_print("Device Model: %s\n", dev->identity.model); - dbg_print("Sectors (48): %d\n", (uint32_t)dev->identity.sectors_48); - dbg_print("Sectors (24): %d\n", dev->identity.sectors_28); - - dbg_print("Setting up DMA...\n"); - // TODO: Ale. - // dev->dma_prdt = kmalloc_p(sizeof(prdt_t) * 1, &dev->dma_prdt_phys); - // dev->dma_start = kmalloc_p(4096, &dev->dma_start_phys); - - dbg_print("Putting prdt at 0x%x (0x%x phys)\n", dev->dma_prdt, - dev->dma_prdt_phys); - dbg_print("Putting prdt[0] at 0x%x (0x%x phys)\n", dev->dma_start, - dev->dma_start_phys); - - dev->dma_prdt[0].offset = dev->dma_start_phys; - dev->dma_prdt[0].bytes = 512; - dev->dma_prdt[0].last = 0x8000; - - dbg_print("ATA PCI device ID: 0x%x\n", ata_pci); - - uint16_t command_reg = pci_read_field(ata_pci, PCI_COMMAND, 4); - dbg_print("COMMAND register before: 0x%4x\n", command_reg); - if (command_reg & (1U << 2U)) { - dbg_print("Bus mastering already enabled.\n"); - } else { - // bit 2. - command_reg |= (1U << 2U); - dbg_print("Enabling bus mastering...\n"); - pci_write_field(ata_pci, PCI_COMMAND, 4, command_reg); - command_reg = pci_read_field(ata_pci, PCI_COMMAND, 4); - dbg_print("COMMAND register after: 0x%4x\n", command_reg); - } - - dev->bar4 = pci_read_field(ata_pci, PCI_BASE_ADDRESS_4, 4); - dbg_print("BAR4: 0x%x\n", dev->bar4); - - if (dev->bar4 & 0x00000001U) { - dev->bar4 = dev->bar4 & 0xFFFFFFFC; - } else { - dbg_print("? ATA bus master registers are 'usually' I/O ports.\n"); - - // No DMA because we're not sure what to do here- - return false; - } -#if 0 - pci_write_field(ata_pci, PCI_INTERRUPT_LINE, 1, 0xFE); - if (pci_read_field(ata_pci, PCI_INTERRUPT_LINE, 1) == 0xFE) + pr_default("Detected IDE device on bus 0x%3x\n", dev->io_base); + pr_default("Device name: %s\n", dev->name); + ata_device_select(dev); + outportb(dev->io_base + ATA_REG_COMMAND, ATA_CMD_IDENT); + ata_io_wait(dev); + uint8_t status = inportb(dev->io_base + ATA_REG_COMMAND); + pr_default("Device status: %d\n", status); + + ata_wait(dev, false); + + uint16_t *buf = (uint16_t *)&dev->identity; + for (int i = 0; i < 256; ++i) { + buf[i] = inports(dev->io_base); + } + + uint8_t *ptr = (uint8_t *)&dev->identity.model; + for (int i = 0; i < 39; i += 2) { + char tmp = ptr[i + 1]; + ptr[i + 1] = ptr[i]; + ptr[i] = tmp; + } + ptr[39] = 0; + + pr_default("Device Model: %s\n", dev->identity.model); + pr_default("Sectors (48): %d\n", (uint32_t)dev->identity.sectors_48); + pr_default("Sectors (24): %d\n", dev->identity.sectors_28); + + pr_default("Setting up DMA...\n"); + // dev->dma_prdt = kmalloc_p(sizeof(prdt_t) * 1, &dev->dma_prdt_phys); + // dev->dma_start = kmalloc_p(4096, &dev->dma_start_phys); + // TODO: Check correctness. { + uint32_t order = find_nearest_order_greater(0, sizeof(prdt_t)); + page_t *page = _alloc_pages(GFP_KERNEL, order); + dev->dma_prdt = (void *)get_lowmem_address_from_page(page); + dev->dma_prdt_phys = get_physical_address_from_page(page); + } + { + uint32_t order = find_nearest_order_greater(0, 4096); + page_t *page = _alloc_pages(GFP_KERNEL, order); + dev->dma_start = (void *)get_lowmem_address_from_page(page); + dev->dma_start_phys = get_physical_address_from_page(page); + } + pr_default("Putting prdt at 0x%x (0x%x phys)\n", dev->dma_prdt, dev->dma_prdt_phys); + pr_default("Putting prdt[0] at 0x%x (0x%x phys)\n", dev->dma_start, dev->dma_start_phys); + + dev->dma_prdt[0].offset = dev->dma_start_phys; + dev->dma_prdt[0].bytes = 512; + dev->dma_prdt[0].last = 0x8000; + + pr_default("ATA PCI device ID: 0x%x\n", ata_pci); + + uint16_t command_reg = pci_read_field(ata_pci, PCI_COMMAND, 4); + pr_default("COMMAND register before: 0x%4x\n", command_reg); + if (command_reg & (1U << 2U)) { + pr_default("Bus mastering already enabled.\n"); + } else { + // bit 2. + command_reg |= (1U << 2U); + pr_default("Enabling bus mastering...\n"); + pci_write_field(ata_pci, PCI_COMMAND, 4, command_reg); + command_reg = pci_read_field(ata_pci, PCI_COMMAND, 4); + pr_default("COMMAND register after: 0x%4x\n", command_reg); + } + + dev->bar4 = pci_read_field(ata_pci, PCI_BASE_ADDRESS_4, 4); + pr_default("BAR4: 0x%x\n", dev->bar4); + + if (dev->bar4 & 0x00000001U) { + dev->bar4 = dev->bar4 & 0xFFFFFFFC; + } else { + pr_default("? ATA bus master registers are 'usually' I/O ports.\n"); + // No DMA because we're not sure what to do here- + return 1; + } + pci_write_field(ata_pci, PCI_INTERRUPT_LINE, 1, 0xFE); + if (pci_read_field(ata_pci, PCI_INTERRUPT_LINE, 1) == 0xFE) { // Needs assignment. pci_write_field(ata_pci, PCI_INTERRUPT_LINE, 1, 14); } -#endif - - return true; + return 0; } - -int atapi_device_init(ata_device_t *dev) +static int atapi_device_init(ata_device_t *dev) { - dbg_print("Detected ATAPI device at io-base 0x%3x, ctrl 0x%3x, slave %d\n", - dev->io_base, dev->control, dev->slave); - dbg_print("Device name: %s\n", dev->name); - ata_device_select(dev); - outportb(dev->io_base + ATA_REG_COMMAND, ATAPI_CMD_ID_PCKT); - ata_io_wait(dev); - uint8_t status = inportb(dev->io_base + ATA_REG_COMMAND); - dbg_print("Device status: %d\n", status); + pr_default("Detected ATAPI device at io-base 0x%3x, ctrl 0x%3x, slave %d\n", + dev->io_base, dev->control, dev->slave); + pr_default("Device name: %s\n", dev->name); + ata_device_select(dev); + outportb(dev->io_base + ATA_REG_COMMAND, ATAPI_CMD_ID_PCKT); + ata_io_wait(dev); + uint8_t status = inportb(dev->io_base + ATA_REG_COMMAND); + pr_default("Device status: %d\n", status); - ata_wait(dev, false); + ata_wait(dev, false); - uint16_t *buf = (uint16_t *)&dev->identity; + uint16_t *buf = (uint16_t *)&dev->identity; - for (int i = 0; i < 256; ++i) { - buf[i] = inports(dev->io_base); - } + for (int i = 0; i < 256; ++i) { + buf[i] = inports(dev->io_base); + } - uint8_t *ptr = (uint8_t *)&dev->identity.model; - for (int i = 0; i < 39; i += 2) { - char tmp = ptr[i + 1]; - ptr[i + 1] = ptr[i]; - ptr[i] = tmp; - } - ptr[39] = 0; + uint8_t *ptr = (uint8_t *)&dev->identity.model; + for (int i = 0; i < 39; i += 2) { + char tmp = ptr[i + 1]; + ptr[i + 1] = ptr[i]; + ptr[i] = tmp; + } + ptr[39] = 0; - dbg_print("Device Model: %s\n", dev->identity.model); + pr_default("Device Model: %s\n", dev->identity.model); - // Detect medium. - atapi_command_t command; - command.command_bytes[0] = 0x25; - command.command_bytes[1] = 0; - command.command_bytes[2] = 0; - command.command_bytes[3] = 0; - command.command_bytes[4] = 0; - command.command_bytes[5] = 0; - command.command_bytes[6] = 0; - command.command_bytes[7] = 0; - // Bit 0 = PMI (0, last sector). - command.command_bytes[8] = 0; - // Control. - command.command_bytes[9] = 0; - command.command_bytes[10] = 0; - command.command_bytes[11] = 0; + // Detect medium. + atapi_command_t command; + command.command_bytes[0] = 0x25; + command.command_bytes[1] = 0; + command.command_bytes[2] = 0; + command.command_bytes[3] = 0; + command.command_bytes[4] = 0; + command.command_bytes[5] = 0; + command.command_bytes[6] = 0; + command.command_bytes[7] = 0; + // Bit 0 = PMI (0, last sector). + command.command_bytes[8] = 0; + // Control. + command.command_bytes[9] = 0; + command.command_bytes[10] = 0; + command.command_bytes[11] = 0; - uint16_t bus = dev->io_base; + uint16_t bus = dev->io_base; - outportb(bus + ATA_REG_FEATURES, 0x00); - outportb(bus + ATA_REG_LBA1, 0x08); - outportb(bus + ATA_REG_LBA2, 0x08); - outportb(bus + ATA_REG_COMMAND, ATAPI_CMD_PACKET); + outportb(bus + ATA_REG_FEATURES, 0x00); + outportb(bus + ATA_REG_LBA1, 0x08); + outportb(bus + ATA_REG_LBA2, 0x08); + outportb(bus + ATA_REG_COMMAND, ATAPI_CMD_PACKET); - // Poll. - while (1) { - status = inportb(dev->io_base + ATA_REG_STATUS); - if ((status & ATA_STAT_ERR)) { - goto atapi_error; - } - if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { - break; - } - } + // Poll. + while (1) { + status = inportb(dev->io_base + ATA_REG_STATUS); + if ((status & ATA_STAT_ERR)) { + goto atapi_error; + } + if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { + break; + } + } - for (int i = 0; i < 6; ++i) - outports(bus, command.command_words[i]); + for (int i = 0; i < 6; ++i) + outports(bus, command.command_words[i]); - // Poll. - while (1) { - status = inportb(dev->io_base + ATA_REG_STATUS); - if ((status & ATA_STAT_ERR)) { - goto atapi_error_read; - } - if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { - break; - } - if ((status & ATA_STAT_DRQ)) { - break; - } - } + // Poll. + while (1) { + status = inportb(dev->io_base + ATA_REG_STATUS); + if ((status & ATA_STAT_ERR)) { + goto atapi_error_read; + } + if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { + break; + } + if ((status & ATA_STAT_DRQ)) { + break; + } + } - uint16_t data[4]; + uint16_t data[4]; - for (int i = 0; i < 4; ++i) { - data[i] = inports(bus); - } + for (int i = 0; i < 4; ++i) { + data[i] = inports(bus); + } -#define htonl(l) \ - ((((l)&0xFF) << 24) | (((l)&0xFF00) << 8) | (((l)&0xFF0000) >> 8) | \ - (((l)&0xFF000000) >> 24)) - uint32_t lba, blocks; - ; +#define htonl(l) \ + ((((l)&0xFF) << 24) | (((l)&0xFF00) << 8) | (((l)&0xFF0000) >> 8) | \ + (((l)&0xFF000000) >> 24)) + uint32_t lba, blocks; - memcpy(&lba, &data[0], sizeof(uint32_t)); + memcpy(&lba, &data[0], sizeof(uint32_t)); - lba = htonl(lba); + lba = htonl(lba); - memcpy(&blocks, &data[2], sizeof(uint32_t)); + memcpy(&blocks, &data[2], sizeof(uint32_t)); - blocks = htonl(blocks); + blocks = htonl(blocks); - dev->atapi_lba = lba; - dev->atapi_sector_size = blocks; + dev->atapi_lba = lba; + dev->atapi_sector_size = blocks; - if (!lba) { - return false; - } + if (!lba) { + return false; + } - dbg_print("Finished! LBA = %x; block length = %x\n", lba, blocks); - return true; + pr_default("Finished! LBA = %x; block length = %x\n", lba, blocks); + return true; atapi_error_read: - dbg_print("ATAPI error; no medium?\n"); - return false; + pr_default("ATAPI error; no medium?\n"); + return false; atapi_error: - dbg_print("ATAPI early error; unsure\n"); - return false; + pr_default("ATAPI early error; unsure\n"); + return false; +} +static void ata_soft_reset(ata_device_t *dev) +{ + outportb(dev->control, 0x04); + ata_io_wait(dev); + outportb(dev->control, 0x00); +} +static int ata_device_detect(ata_device_t *dev) +{ + ata_soft_reset(dev); + ata_io_wait(dev); + outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4); + ata_io_wait(dev); + ata_status_wait(dev, 10000); + + pr_default("Probing cylinder registers...\n"); + uint8_t cl = inportb(dev->io_base + ATA_REG_LBA1); + uint8_t ch = inportb(dev->io_base + ATA_REG_LBA2); + if ((cl == 0xFF) && (ch == 0xFF)) { + pr_default("No drive(s) present\n"); + return 1; + } + + pr_default("Waiting while busy...\n"); + uint8_t status = ata_status_wait(dev, 5000); + if (status & ATA_STAT_BUSY) { + pr_default("No drive(s) present\n"); + return 1; + } + + pr_default("Device detected: 0x%2x 0x%2x\n", cl, ch); + if ((cl == 0x00 && ch == 0x00) || (cl == 0x3C && ch == 0xC3)) { + // The device is not an ATAPI. + dev->is_atapi = false; + // Parallel ATA device, or emulated SATA + sprintf(dev->name, "hd%c", ata_drive_char); + + dev->fs_root = ata_device_create(dev); + if (!dev->fs_root) { + pr_default("Failed to create ata device!\n"); + return 1; + } + if (!vfs_mount(dev->fs_root->name, dev->fs_root)) { + pr_default("Failed to mount ata device!\n"); + return 1; + } + if (ata_device_init(dev)) { + pr_default("Failed to initialize ata device!\n"); + return 1; + } + ++ata_drive_char; + } else if ((cl == 0x14 && ch == 0xEB) || (cl == 0x69 && ch == 0x96)) { + // The device is an ATAPI. + dev->is_atapi = true; + sprintf(dev->name, "cdrom%d", cdrom_number); + + dev->fs_root = atapi_device_create(dev); + if (!dev->fs_root) { + pr_default("Failed to create atapi device!\n"); + return 1; + } + if (!vfs_mount(dev->fs_root->name, dev->fs_root)) { + pr_default("Failed to mount atapi device!\n"); + return 1; + } + if (atapi_device_init(dev)) { + pr_default("Failed to initialize atapi device!\n"); + return 1; + } + ++cdrom_number; + } + pr_default("\n"); + return 0; } -int ata_device_detect(ata_device_t *dev) +// == ATA SECTOR READ/WRITE FUNCTIONS ========================================= +static void ata_device_read_sector(ata_device_t *dev, uint32_t lba, uint8_t *buffer) { - static char ata_drive_char = 'a'; - static int cdrom_number = 0; + pr_default("ata_device_read_sector(%p, %d, %p)\n", dev, lba, buffer); - ata_soft_reset(dev); - ata_io_wait(dev); - outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4); - ata_io_wait(dev); - ata_status_wait(dev, 10000); + uint16_t bus = dev->io_base; - dbg_print("Probing cylinder registers...\n"); - uint8_t cl = inportb(dev->io_base + ATA_REG_LBA1); - uint8_t ch = inportb(dev->io_base + ATA_REG_LBA2); - if ((cl == 0xFF) && (ch == 0xFF)) { - dbg_print("No drive(s) present\n"); - return false; - } + uint8_t slave = dev->slave; - dbg_print("Waiting while busy...\n"); - uint8_t status = ata_status_wait(dev, 5000); - if (status & ATA_STAT_BUSY) { - dbg_print("No drive(s) present\n"); + if (dev->is_atapi) { + return; + } - return false; - } + spinlock_lock(&ata_lock); - dbg_print("Device detected: 0x%2x 0x%2x\n", cl, ch); - if ((cl == 0x00 && ch == 0x00) || (cl == 0x3C && ch == 0xC3)) { - // The device is not an ATAPI. - dev->is_atapi = false; - // Parallel ATA device, or emulated SATA - sprintf(dev->name, "/dev/hd%c", ata_drive_char); - -#ifdef COMPLETE_VFS - fs_node_t *node = ata_device_create(dev); - vfs_mount(devname, node); -#endif - - ++ata_drive_char; - if (!ata_device_init(dev)) { - return 0; - } - - return 1; - } else if ((cl == 0x14 && ch == 0xEB) || (cl == 0x69 && ch == 0x96)) { - // The device is an ATAPI. - dev->is_atapi = true; - sprintf(dev->name, "/dev/cdrom%d", cdrom_number); - -#ifdef COMPLETE_VFS - fs_node_t *node = atapi_device_create(dev); - vfs_mount(devname, node); -#endif - - ++cdrom_number; - if (!atapi_device_init(dev)) { - return 0; - } - return 2; - } - dbg_print("\n"); - - // TODO: ATAPI, SATA, SATAPI. - - return 0; -} - -void ata_device_read_sector(ata_device_t *dev, uint32_t lba, uint8_t *buf) -{ - uint16_t bus = dev->io_base; - - uint8_t slave = dev->slave; - - if (dev->is_atapi) { - return; - } - - spinlock_lock(&ata_lock); - -#if 0 - int errors = 0; - try_again: -#endif - - ata_wait(dev, false); - - // Stop. - outportb(dev->bar4, 0x00); - - // Set the PRDT. - outportl(dev->bar4 + 0x04, dev->dma_prdt_phys); - - // Enable error, irq status. - outportb(dev->bar4 + 0x2, inportb(dev->bar4 + 0x02) | 0x04 | 0x02); - - // Set read. - outportb(dev->bar4, 0x08); - - irq_enable(); - - while (1) { - uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); - if (!(status & ATA_STAT_BUSY)) { - break; - } - } - - outportb(bus + ATA_REG_CONTROL, 0x00); - outportb(bus + ATA_REG_HDDEVSEL, - 0xe0 | slave << 4 | (lba & 0x0f000000) >> 24); - ata_io_wait(dev); - outportb(bus + ATA_REG_FEATURES, 0x00); - outportb(bus + ATA_REG_SECCOUNT0, 1); - outportb(bus + ATA_REG_LBA0, (lba & 0x000000ff) >> 0); - outportb(bus + ATA_REG_LBA1, (lba & 0x0000ff00) >> 8); - outportb(bus + ATA_REG_LBA2, (lba & 0x00ff0000) >> 16); - // outportb(bus + ATA_REG_COMMAND, ATA_CMD_READ); #if 1 - while (1) { - uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); - if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { - break; - } - } + int errors = 0; +try_again: #endif - outportb(bus + ATA_REG_COMMAND, ATA_CMD_RD_DMA); - ata_io_wait(dev); + pr_default("ata_wait\n"); + ata_wait(dev, false); - outportb(dev->bar4, 0x08 | 0x01); + // Stop. + outportb(dev->bar4, 0x00); - while (1) { - int status = inportb(dev->bar4 + 0x02); - int dstatus = inportb(dev->io_base + ATA_REG_STATUS); - if (!(status & 0x04)) { - continue; - } - if (!(dstatus & ATA_STAT_BUSY)) { - break; - } - } - irq_disable(); + pr_default("Set the PRDT.\n"); + // Set the PRDT. + outportl(dev->bar4 + 0x04, dev->dma_prdt_phys); -#if 0 + pr_default("Enable error, irq status.\n"); + // Enable error, irq status. + outportb(dev->bar4 + 0x2, inportb(dev->bar4 + 0x02) | 0x04 | 0x02); + + // Set read. + outportb(dev->bar4, 0x08); + + pr_default("irq_enable...\n"); + //sti(); + + while (1) { + pr_default("Wait busy...\n"); + uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); + if (!(status & ATA_STAT_BUSY)) { + break; + } + } + + pr_default("Read.\n"); + outportb(bus + ATA_REG_CONTROL, 0x00); + outportb(bus + ATA_REG_HDDEVSEL, 0xe0 | slave << 4 | (lba & 0x0f000000) >> 24); + ata_io_wait(dev); + outportb(bus + ATA_REG_FEATURES, 0x00); + outportb(bus + ATA_REG_SECCOUNT0, 1); + outportb(bus + ATA_REG_LBA0, (lba & 0x000000ff) >> 0); + outportb(bus + ATA_REG_LBA1, (lba & 0x0000ff00) >> 8); + outportb(bus + ATA_REG_LBA2, (lba & 0x00ff0000) >> 16); + // outportb(bus + ATA_REG_COMMAND, ATA_CMD_READ); + while (1) { + uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); + if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { + break; + } + } + outportb(bus + ATA_REG_COMMAND, ATA_CMD_RD_DMA); + + ata_io_wait(dev); + + outportb(dev->bar4, 0x08 | 0x01); + + while (1) { + int status = inportb(dev->bar4 + 0x02); + int dstatus = inportb(dev->io_base + ATA_REG_STATUS); + if (!(status & 0x04)) { + continue; + } + if (!(dstatus & ATA_STAT_BUSY)) { + break; + } + } + //cli(); + +#if 1 if (ata_wait(dev, true)) { - dbg_print("Error during ATA read of lba block %d\n", lba); + pr_default("Error during ATA read of lba block %d\n", lba); errors++; - if (errors > 4) - { - dbg_print( - "-- Too many errors trying to read this block. Bailing.\n"); + if (errors > 4) { + pr_default("-- Too many errors trying to read this block. Bailing.\n"); spinlock_unlock(&ata_lock); return; } @@ -808,11 +808,12 @@ void ata_device_read_sector(ata_device_t *dev, uint32_t lba, uint8_t *buf) } #endif - // Copy from DMA buffer to output buffer. - memcpy(buf, dev->dma_start, 512); + pr_default("Copy from DMA buffer to output buffer.\n"); + // Copy from DMA buffer to output buffer. + memcpy(buffer, dev->dma_start, 512); - // Inform device we are done. - outportb(dev->bar4 + 0x2, inportb(dev->bar4 + 0x02) | 0x04 | 0x02); + // Inform device we are done. + outportb(dev->bar4 + 0x2, inportb(dev->bar4 + 0x02) | 0x04 | 0x02); #if 0 int size = 256; @@ -820,190 +821,200 @@ void ata_device_read_sector(ata_device_t *dev, uint32_t lba, uint8_t *buf) ata_wait(dev, false); outportb(bus + ATA_REG_CONTROL, 0x02); #endif - spinlock_unlock(&ata_lock); + spinlock_unlock(&ata_lock); } - -void ata_device_read_sector_atapi(ata_device_t *dev, uint32_t lba, uint8_t *buf) +static void ata_device_read_sector_atapi(ata_device_t *dev, uint32_t lba, uint8_t *buffer) { - if (!dev->is_atapi) { - return; - } + if (!dev->is_atapi) { + return; + } - uint16_t bus = dev->io_base; - spinlock_lock(&ata_lock); + uint16_t bus = dev->io_base; + spinlock_lock(&ata_lock); - outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4); - ata_io_wait(dev); + outportb(dev->io_base + ATA_REG_HDDEVSEL, 0xA0 | dev->slave << 4); + ata_io_wait(dev); - outportb(bus + ATA_REG_FEATURES, 0x00); - outportb(bus + ATA_REG_LBA1, dev->atapi_sector_size & 0xFF); - outportb(bus + ATA_REG_LBA2, dev->atapi_sector_size >> 8); - outportb(bus + ATA_REG_COMMAND, ATAPI_CMD_PACKET); + outportb(bus + ATA_REG_FEATURES, 0x00); + outportb(bus + ATA_REG_LBA1, dev->atapi_sector_size & 0xFF); + outportb(bus + ATA_REG_LBA2, dev->atapi_sector_size >> 8); + outportb(bus + ATA_REG_COMMAND, ATAPI_CMD_PACKET); - // Poll. - while (1) { - uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); - if ((status & ATA_STAT_ERR)) { - goto atapi_error_on_read_setup; - } - if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_DRQ)) { - break; - } - } + // Poll. + while (1) { + uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); + if ((status & ATA_STAT_ERR)) { + goto atapi_error_on_read_setup; + } + if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_DRQ)) { + break; + } + } - atapi_in_progress = true; + atapi_in_progress = true; - atapi_command_t command; - command.command_bytes[0] = 0xA8; - command.command_bytes[1] = 0; - command.command_bytes[2] = (lba >> 0x18) & 0xFF; - command.command_bytes[3] = (lba >> 0x10) & 0xFF; - command.command_bytes[4] = (lba >> 0x08) & 0xFF; - command.command_bytes[5] = (lba >> 0x00) & 0xFF; - command.command_bytes[6] = 0; - command.command_bytes[7] = 0; - // Bit 0 = PMI (0, last sector). - command.command_bytes[8] = 0; - // Control. - command.command_bytes[9] = 1; - command.command_bytes[10] = 0; - command.command_bytes[11] = 0; + atapi_command_t command; + command.command_bytes[0] = 0xA8; + command.command_bytes[1] = 0; + command.command_bytes[2] = (lba >> 0x18) & 0xFF; + command.command_bytes[3] = (lba >> 0x10) & 0xFF; + command.command_bytes[4] = (lba >> 0x08) & 0xFF; + command.command_bytes[5] = (lba >> 0x00) & 0xFF; + command.command_bytes[6] = 0; + command.command_bytes[7] = 0; + // Bit 0 = PMI (0, last sector). + command.command_bytes[8] = 0; + // Control. + command.command_bytes[9] = 1; + command.command_bytes[10] = 0; + command.command_bytes[11] = 0; - for (int i = 0; i < 6; ++i) { - outports(bus, command.command_words[i]); - } + for (int i = 0; i < 6; ++i) { + outports(bus, command.command_words[i]); + } - // Wait. + // Wait. #ifdef COMPLETE_SCHEDULER - sleep_on(atapi_waiter); + sleep_on(atapi_waiter); #endif - atapi_in_progress = false; + atapi_in_progress = false; - while (1) { - uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); - if ((status & ATA_STAT_ERR)) { - goto atapi_error_on_read_setup; - } - if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_DRQ)) { - break; - } - } + while (1) { + uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); + if ((status & ATA_STAT_ERR)) { + goto atapi_error_on_read_setup; + } + if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_DRQ)) { + break; + } + } - uint16_t size_to_read = inportb(bus + ATA_REG_LBA2) << 8; - size_to_read = size_to_read | inportb(bus + ATA_REG_LBA1); + uint16_t size_to_read = inportb(bus + ATA_REG_LBA2) << 8; + size_to_read = size_to_read | inportb(bus + ATA_REG_LBA1); - inportsm(bus, buf, size_to_read / 2); + inportsm(bus, buffer, size_to_read / 2); - while (1) { - uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); - if ((status & ATA_STAT_ERR)) { - goto atapi_error_on_read_setup; - } - if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { - break; - } - } + while (1) { + uint8_t status = inportb(dev->io_base + ATA_REG_STATUS); + if ((status & ATA_STAT_ERR)) { + goto atapi_error_on_read_setup; + } + if (!(status & ATA_STAT_BUSY) && (status & ATA_STAT_READY)) { + break; + } + } -atapi_error_on_read_setup: - spinlock_unlock(&ata_lock); +atapi_error_on_read_setup:; + spinlock_unlock(&ata_lock); } - -void ata_device_write_sector(ata_device_t *dev, uint32_t lba, uint8_t *buf) +static void ata_device_write_sector(ata_device_t *dev, uint32_t lba, uint8_t *buffer) { - uint16_t bus = dev->io_base; - uint8_t slave = dev->slave; + uint16_t bus = dev->io_base; + uint8_t slave = dev->slave; - spinlock_lock(&ata_lock); + spinlock_lock(&ata_lock); - outportb(bus + ATA_REG_CONTROL, 0x02); + outportb(bus + ATA_REG_CONTROL, 0x02); - ata_wait(dev, false); - outportb(bus + ATA_REG_HDDEVSEL, - 0xe0 | slave << 4 | (lba & 0x0f000000) >> 24); - ata_wait(dev, false); + ata_wait(dev, false); + outportb(bus + ATA_REG_HDDEVSEL, 0xe0 | slave << 4 | (lba & 0x0f000000) >> 24); + ata_wait(dev, false); - outportb(bus + ATA_REG_FEATURES, 0x00); - outportb(bus + ATA_REG_SECCOUNT0, 0x01); - outportb(bus + ATA_REG_LBA0, (lba & 0x000000ff) >> 0); - outportb(bus + ATA_REG_LBA1, (lba & 0x0000ff00) >> 8); - outportb(bus + ATA_REG_LBA2, (lba & 0x00ff0000) >> 16); - outportb(bus + ATA_REG_COMMAND, ATA_CMD_WRITE); - ata_wait(dev, false); - int size = ATA_SECTOR_SIZE / 2; - outportsm(bus, buf, size); - outportb(bus + 0x07, ATA_CMD_CH_FLSH); - ata_wait(dev, false); - spinlock_unlock(&ata_lock); + outportb(bus + ATA_REG_FEATURES, 0x00); + outportb(bus + ATA_REG_SECCOUNT0, 0x01); + outportb(bus + ATA_REG_LBA0, (lba & 0x000000ff) >> 0); + outportb(bus + ATA_REG_LBA1, (lba & 0x0000ff00) >> 8); + outportb(bus + ATA_REG_LBA2, (lba & 0x00ff0000) >> 16); + outportb(bus + ATA_REG_COMMAND, ATA_CMD_WRITE); + ata_wait(dev, false); + int size = ATA_SECTOR_SIZE / 2; + outportsm(bus, buffer, size); + outportb(bus + 0x07, ATA_CMD_CH_FLSH); + ata_wait(dev, false); + spinlock_unlock(&ata_lock); } - -int buffer_compare(uint32_t *ptr1, uint32_t *ptr2, size_t size) +static void ata_device_write_sector_retry(ata_device_t *dev, uint32_t lba, uint8_t *buffer) { - assert(!(size % 4)); - - size_t i = 0; - - while (i < size) { - if (*ptr1 != *ptr2) { - return 1; - } - - ptr1++; - ptr2++; - i += sizeof(uint32_t); - } - return 0; + uint8_t *read_buf = kmalloc(ATA_SECTOR_SIZE); + do { + ata_device_write_sector(dev, lba, buffer); + ata_device_read_sector(dev, lba, read_buf); + } while ( + buffer_compare((uint32_t *)buffer, (uint32_t *)read_buf, ATA_SECTOR_SIZE)); + kfree(read_buf); } -void ata_device_write_sector_retry(ata_device_t *dev, uint32_t lba, - uint8_t *buf) +// == IRQ HANDLERS ============================================================ +/// @param f The interrupt stack frame. +static void ata_irq_handler_master(pt_regs *f) { - uint8_t *read_buf = kmalloc(ATA_SECTOR_SIZE); - do { - ata_device_write_sector(dev, lba, buf); - ata_device_read_sector(dev, lba, read_buf); - } while ( - buffer_compare((uint32_t *)buf, (uint32_t *)read_buf, ATA_SECTOR_SIZE)); - kfree(read_buf); + inportb(ata_primary_master.io_base + ATA_REG_STATUS); + + if (atapi_in_progress) { + // wakeup_queue(atapi_waiter); + } + + // irq_ack(14); + pic8259_send_eoi(14); } -void find_ata_pci(uint32_t dev, uint16_t vid, uint16_t did, void *extra) +/// @param f The interrupt stack frame. +static void ata_irq_handler_slave(pt_regs *f) { - if ((vid == 0x8086) && (did == 0x7010 || did == 0x7111)) { - *((uint32_t *)extra) = dev; - } + inportb(ata_secondary_master.io_base + ATA_REG_STATUS); + + if (atapi_in_progress) { + // wakeup_queue(atapi_waiter); + } + + // irq_ack(15); + pic8259_send_eoi(15); } +// == PCI FUNCTIONS =========================================================== +static void pci_find_ata(uint32_t dev, uint16_t vid, uint16_t did, void *extra) +{ + if ((vid == 0x8086) && (did == 0x7010 || did == 0x7111)) { + *((uint32_t *)extra) = dev; + } +} + +// == INITIALIZE/FINALIZE ATA ================================================= int ata_initialize() { - // Detect drives and mount them. - // Locate ATA device via PCI. - pci_scan(&find_ata_pci, -1, &ata_pci); + spinlock_init(&ata_lock); - irq_install_handler(14, ata_irq_handler, "ide master"); - irq_install_handler(15, ata_irq_handler_s, "ide slave"); + // Detect drives and mount them. + // Locate ATA device via PCI. + pci_scan(&pci_find_ata, -1, &ata_pci); - atapi_waiter = list_create(); + //irq_install_handler(14, ata_irq_handler_master, "ide master"); + //irq_install_handler(15, ata_irq_handler_slave, "ide slave"); - dbg_print("Detecteing devices...\n"); - dbg_print("Detecteing Primary Master...\n"); - ata_device_detect(&ata_primary_master); - dbg_print("\n"); - dbg_print("Detecteing Primary Slave...\n"); - ata_device_detect(&ata_primary_slave); - dbg_print("\n"); - dbg_print("Detecteing Secondary Master...\n"); - ata_device_detect(&ata_secondary_master); - dbg_print("\n"); - dbg_print("Detecteing Secondary Slave...\n"); - ata_device_detect(&ata_secondary_slave); - dbg_print("\n"); - dbg_print("Done\n"); + // atapi_waiter = list_create(); - return 0; + pr_default("Detecteing devices...\n"); + pr_default("Detecteing Primary Master...\n"); + ata_device_detect(&ata_primary_master); + pr_default("\n"); + pr_default("Detecteing Primary Slave...\n"); + ata_device_detect(&ata_primary_slave); + pr_default("\n"); + pr_default("Detecteing Secondary Master...\n"); + ata_device_detect(&ata_secondary_master); + pr_default("\n"); + pr_default("Detecteing Secondary Slave...\n"); + ata_device_detect(&ata_secondary_slave); + pr_default("\n"); + pr_default("Done\n"); + + return 0; } int ata_finalize() { - return 0; + return 0; } + +///! @endcond \ No newline at end of file diff --git a/mentos/src/drivers/fdc.c b/mentos/src/drivers/fdc.c index 5dbe1a5..9eb0f3d 100644 --- a/mentos/src/drivers/fdc.c +++ b/mentos/src/drivers/fdc.c @@ -1,12 +1,12 @@ /// MentOS, The Mentoring Operating system project /// @file fdc.c /// @brief Floppy driver controller handling. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "fdc.h" -#include "video.h" -#include "port_io.h" +#include "drivers/fdc.h" +#include "io/video.h" +#include "io/port_io.h" void fdc_disable_motor() { diff --git a/mentos/src/drivers/keyboard/keyboard.c b/mentos/src/drivers/keyboard/keyboard.c index 49452f9..18691f6 100644 --- a/mentos/src/drivers/keyboard/keyboard.c +++ b/mentos/src/drivers/keyboard/keyboard.c @@ -1,271 +1,275 @@ /// MentOS, The Mentoring Operating system project /// @file keyboard.c /// @brief Keyboard handling. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "keyboard.h" -#include "isr.h" -#include "video.h" -#include "stdio.h" -#include "keymap.h" -#include "bitops.h" -#include "port_io.h" -#include "pic8259.h" +#include "drivers/keyboard/keyboard.h" + +#include "io/port_io.h" +#include "hardware/pic8259.h" +#include "drivers/keyboard/keymap.h" +#include "sys/bitops.h" +#include "io/video.h" +#include "misc/debug.h" +#include "ctype.h" +#include "descriptor_tables/isr.h" +#include "process/scheduler.h" /// A macro from Ivan to update buffer indexes. -#define STEP(x) (((x) == BUFSIZE - 1) ? 0 : (x + 1)) +#define STEP(x) (((x) == BUFSIZE - 1) ? 0 : ((x) + 1)) /// Circular Buffer where the pressed keys are stored. -static int circular_buffer[BUFSIZE]; - +static int32_t circular_buffer[BUFSIZE] = { 0 }; /// Index inside the buffer... static long buf_r = 0; - /// Index inside the buffer... static long buf_w = 0; - /// Tracks the state of the leds. static uint8_t ledstate = 0; - -/// The shadow option is active. -static bool_t shadow = false; - -/// The shadow character. -static char shadow_character = 0; - /// The flags concerning the keyboard. -static uint32_t keyboard_flags = 0; +static uint32_t kflags = 0; -/// Flag which identifies the left shift. -#define KBD_LEFT_SHIFT 1 +#define KBD_LEFT_SHIFT (1 << 0) ///< Flag which identifies the left shift. +#define KBD_RIGHT_SHIFT (1 << 1) ///< Flag which identifies the right shift. +#define KBD_CAPS_LOCK (1 << 2) ///< Flag which identifies the caps lock. +#define KBD_NUM_LOCK (1 << 3) ///< Flag which identifies the num lock. +#define KBD_SCROLL_LOCK (1 << 4) ///< Flag which identifies the scroll lock. +#define KBD_LEFT_CONTROL (1 << 5) ///< Flag which identifies the left control. +#define KBD_RIGHT_CONTROL (1 << 6) ///< Flag which identifies the right control. +#define KBD_LEFT_ALT (1 << 7) ///< Flag which identifies the left alt. +#define KBD_RIGHT_ALT (1 << 8) ///< Flag which identifies the right alt. -/// Flag which identifies the right shift. -#define KBD_RIGHT_SHIFT 2 - -/// Flag which identifies the caps lock. -#define KBD_CAPS_LOCK 4 - -/// Flag which identifies the num lock. -#define KBD_NUM_LOCK 8 - -/// Flag which identifies the scroll lock. -#define KBD_SCROLL_LOCK 16 - -/// Flag which identifies the left control. -#define KBD_LEFT_CONTROL 32 - -/// Flag which identifies the right control. -#define KBD_RIGHT_CONTROL 64 - -static inline void push_character(char c) +static inline void push_character(uint16_t c) { - // Update buffer. - if (STEP(buf_w) == buf_r) { - buf_r = STEP(buf_r); - } - circular_buffer[buf_w] = c; - buf_w = STEP(buf_w); + // Update buffer. + if (STEP(buf_w) == buf_r) { + buf_r = STEP(buf_r); + } + + circular_buffer[buf_w] = c; + + buf_w = STEP(buf_w); +} + +static inline int read_character() +{ + char c = -1; + if (buf_r != buf_w) { + c = circular_buffer[buf_r]; + buf_r = STEP(buf_r); + } + return c; } void keyboard_install() { - // Install the IRQ. - irq_install_handler(IRQ_KEYBOARD, keyboard_isr, "keyboard"); - // Enable the IRQ. - pic8259_irq_enable(IRQ_KEYBOARD); + // Initialize the keymaps. + init_keymaps(); + // Install the IRQ. + irq_install_handler(IRQ_KEYBOARD, keyboard_isr, "keyboard"); + // Enable the IRQ. + pic8259_irq_enable(IRQ_KEYBOARD); } -void keyboard_isr(pt_regs *r) +void keyboard_isr(pt_regs *f) { - (void)r; + (void)f; - if (!(inportb(0x64) & 1)) { - return; - } + if (!(inportb(0x64U) & 1U)) { + return; + } - // Take scancode from the port. - uint32_t scancode = inportb(0x60); - if (scancode == 0xE0) { - scancode = (scancode << 8) | inportb(0x60); - } + // Take scancode from the port. + uint32_t scancode = inportb(0x60); + if (scancode == 0xE0) { + scancode = (scancode << 8U) | inportb(0x60); + } - // If the key has just been released. - if (scancode & 0x80) { - if (scancode == (KEY_LEFT_SHIFT | CODE_BREAK)) { - clear_flag(&keyboard_flags, KBD_LEFT_SHIFT); - } else if (scancode == (KEY_RIGHT_SHIFT | CODE_BREAK)) { - clear_flag(&keyboard_flags, KBD_RIGHT_SHIFT); - } else if (scancode == (KEY_LEFT_CONTROL | CODE_BREAK)) { - clear_flag(&keyboard_flags, KBD_LEFT_CONTROL); - } else if (scancode == (KEY_RIGHT_CONTROL | CODE_BREAK)) { - clear_flag(&keyboard_flags, KBD_RIGHT_CONTROL); - } - } else { - int32_t character = 0; + // If the key has just been released. + if (scancode & 0x80U) { + if (scancode == (KEY_LEFT_SHIFT | CODE_BREAK)) { + bitmask_clear_assign(kflags, KBD_LEFT_SHIFT); + } else if (scancode == (KEY_RIGHT_SHIFT | CODE_BREAK)) { + bitmask_clear_assign(kflags, KBD_RIGHT_SHIFT); + } else if (scancode == (KEY_LEFT_CONTROL | CODE_BREAK)) { + bitmask_clear_assign(kflags, KBD_LEFT_CONTROL); + } else if (scancode == (KEY_RIGHT_CONTROL | CODE_BREAK)) { + bitmask_clear_assign(kflags, KBD_RIGHT_CONTROL); + } else if (scancode == (KEY_LEFT_ALT | CODE_BREAK)) { + bitmask_clear_assign(kflags, KBD_LEFT_ALT); + } else if (scancode == (KEY_RIGHT_ALT | CODE_BREAK)) { + bitmask_clear_assign(kflags, KBD_RIGHT_ALT); + } + } else { + int32_t character = 0; - // Parse the key. - switch (scancode) { - case KEY_LEFT_SHIFT: - set_flag(&keyboard_flags, KBD_LEFT_SHIFT); - break; - case KEY_RIGHT_SHIFT: - set_flag(&keyboard_flags, KBD_RIGHT_SHIFT); - break; - case KEY_LEFT_CONTROL: - set_flag(&keyboard_flags, KBD_LEFT_CONTROL); - break; - case KEY_RIGHT_CONTROL: - set_flag(&keyboard_flags, KBD_RIGHT_CONTROL); - break; - case KEY_CAPS_LOCK: - keyboard_flags ^= KBD_CAPS_LOCK; - keyboard_update_leds(); - break; - case KEY_NUM_LOCK: - keyboard_flags ^= KBD_NUM_LOCK; - keyboard_update_leds(); - break; - case KEY_SCROLL_LOCK: - keyboard_flags ^= KBD_SCROLL_LOCK; - keyboard_update_leds(); - break; - case KEY_PAGE_UP: - video_scroll_up(); - break; - case KEY_PAGE_DOWN: - video_scroll_down(); - break; - case KEY_ESCAPE: - break; - case KEY_LEFT_ALT: - break; - case KEY_RIGHT_ALT: - break; - case KEY_BACKSPACE: - push_character('\b'); - video_delete_last_character(); - video_set_cursor_auto(); - break; - case KEY_KP_RETURN: - case KEY_ENTER: - push_character('\n'); - video_new_line(); - video_set_cursor_auto(); - pic8259_send_eoi(IRQ_KEYBOARD); - break; - case KEY_UP_ARROW: - push_character('\033'); - push_character('['); - push_character(72); - break; - case KEY_DOWN_ARROW: - push_character('\033'); - push_character('['); - push_character(80); - break; - case KEY_LEFT_ARROW: - push_character('\033'); - push_character('['); - push_character(75); - break; - case KEY_RIGHT_ARROW: - push_character('\033'); - push_character('['); - push_character(77); - break; - default: - if (has_flag(keyboard_flags, KBD_NUM_LOCK)) { - character = keymap_it.numlock[scancode]; - } - if (character <= 0) { - // Apply shift modifier. - if (has_flag(keyboard_flags, - (KBD_LEFT_SHIFT | KBD_RIGHT_SHIFT))) { - character = keymap_it.shift[scancode]; - } else { - character = keymap_it.base[scancode]; - } - } - if (character > 0) { - // Apply caps lock modifier. - if (has_flag(keyboard_flags, KBD_CAPS_LOCK)) { - if (character >= 'a' && character <= 'z') { - character += 'A' - 'a'; - } else if (character >= 'A' && character <= 'Z') { - character += 'a' - 'A'; - } - } - if (!shadow) { - video_putc(character); - } else if (shadow_character != 0) { - video_putc(shadow_character); - } - // Update buffer. - push_character(character); - } - break; - } - } - pic8259_send_eoi(IRQ_KEYBOARD); + // Parse the key. + switch (scancode) { + case KEY_LEFT_SHIFT: + bitmask_set_assign(kflags, KBD_LEFT_SHIFT); + break; + case KEY_RIGHT_SHIFT: + bitmask_set_assign(kflags, KBD_RIGHT_SHIFT); + break; + case KEY_LEFT_CONTROL: + bitmask_set_assign(kflags, KBD_LEFT_CONTROL); + break; + case KEY_RIGHT_CONTROL: + bitmask_set_assign(kflags, KBD_RIGHT_CONTROL); + break; + case KEY_CAPS_LOCK: + bitmask_flip_assign(kflags, KBD_CAPS_LOCK); + keyboard_update_leds(); + break; + case KEY_NUM_LOCK: + bitmask_flip_assign(kflags, KBD_NUM_LOCK); + keyboard_update_leds(); + break; + case KEY_SCROLL_LOCK: + bitmask_flip_assign(kflags, KBD_SCROLL_LOCK); + keyboard_update_leds(); + break; + case KEY_PAGE_UP: + video_shift_one_page_down(); + break; + case KEY_PAGE_DOWN: + video_shift_one_page_up(); + break; + case KEY_ESCAPE: + break; + case KEY_LEFT_ALT: + bitmask_set_assign(kflags, KBD_LEFT_ALT); + push_character(scancode << 16u); + break; + case KEY_RIGHT_ALT: + bitmask_set_assign(kflags, KBD_RIGHT_ALT); + push_character(scancode << 16u); + break; + case KEY_BACKSPACE: + push_character('\b'); + break; + case KEY_DELETE: + push_character(127); + break; + case KEY_KP_RETURN: + case KEY_ENTER: + push_character('\n'); + break; + case KEY_UP_ARROW: + push_character('\033'); + push_character('['); + push_character('A'); + break; + case KEY_DOWN_ARROW: + push_character('\033'); + push_character('['); + push_character('B'); + break; + case KEY_RIGHT_ARROW: + push_character('\033'); + push_character('['); + push_character('C'); + break; + case KEY_LEFT_ARROW: + push_character('\033'); + push_character('['); + push_character('D'); + break; + case KEY_HOME: + push_character('\033'); + push_character('['); + push_character('H'); + break; + case KEY_END: + push_character('\033'); + push_character('['); + push_character('F'); + break; + default: { + // Get the current keymap. + const keymap_t *keymap = get_keymap(); + + if (bitmask_check(kflags, KBD_NUM_LOCK)) { + character = keymap->numlock[scancode]; + } + if ((character <= 0) && bitmask_check(kflags, KBD_RIGHT_ALT) && (bitmask_check(kflags, KBD_LEFT_SHIFT | KBD_RIGHT_SHIFT))) { + if (scancode == KEY_LEFT_BRAKET) { + character = '{'; + } else if (scancode == KEY_RIGHT_BRAKET) { + character = '}'; + } + } + if ((character <= 0) && bitmask_check(kflags, KBD_RIGHT_ALT)) { + if (scancode == KEY_LEFT_BRAKET) { + character = '['; + } else if (scancode == KEY_RIGHT_BRAKET) { + character = ']'; + } else if (scancode == 0x27) { + character = '@'; + } else if (scancode == 0x28) { + character = '#'; + } + } + if ((character <= 0) && (bitmask_check(kflags, KBD_LEFT_SHIFT | KBD_RIGHT_SHIFT))) { + character = keymap->shift[scancode]; + } + if (character <= 0) { + character = keymap->base[scancode]; + } + // We have failed to retrieve the character. + if (character <= 0) { + break; + } + // Apply caps lock modifier. + if (bitmask_check(kflags, KBD_CAPS_LOCK)) { + if (character >= 'a' && character <= 'z') { + character += 'A' - 'a'; + } else if (character >= 'A' && character <= 'Z') { + character += 'a' - 'A'; + } + } + if (bitmask_check(kflags, KBD_LEFT_CONTROL | KBD_RIGHT_CONTROL)) { + push_character('\033'); + push_character('^'); + character = toupper(character); + + // Qui ci arrivi quando stai tenendo premuto ctrl+. + // Qui devi scatenare il SIGINT (ctrl+c). + + // signal(); + } + // Update buffer. + push_character(character); + } + } + } + pic8259_send_eoi(IRQ_KEYBOARD); } void keyboard_update_leds() { - // Handle scroll_loc & num_loc & caps_loc. - (keyboard_flags & KBD_SCROLL_LOCK) ? (ledstate |= 1) : (ledstate ^= 1); - (keyboard_flags & KBD_NUM_LOCK) ? (ledstate |= 2) : (ledstate ^= 2); - (keyboard_flags & KBD_CAPS_LOCK) ? (ledstate |= 4) : (ledstate ^= 4); + // Handle scroll_loc & num_loc & caps_loc. + bitmask_check(kflags, KBD_SCROLL_LOCK) ? (ledstate |= 1) : (ledstate ^= 1); + bitmask_check(kflags, KBD_NUM_LOCK) ? (ledstate |= 2) : (ledstate ^= 2); + bitmask_check(kflags, KBD_CAPS_LOCK) ? (ledstate |= 4) : (ledstate ^= 4); - // Write on the port. - outportb(0x60, 0xED); - outportb(0x60, ledstate); + // Write on the port. + outportb(0x60, 0xED); + outportb(0x60, ledstate); } void keyboard_enable() { - outportb(0x60, 0xF4); + outportb(0x60, 0xF4); } void keyboard_disable() { - outportb(0x60, 0xF5); + outportb(0x60, 0xF5); } -int keyboard_getc(void) +int keyboard_getc() { - int c = -1; - if (buf_r != buf_w) { - c = circular_buffer[buf_r]; - buf_r = STEP(buf_r); - } - return c; -} - -void keyboard_set_shadow(const bool_t value) -{ - shadow = value; - if (shadow == false) { - shadow_character = 0; - } -} - -void keyboard_set_shadow_character(const char _shadow_character) -{ - shadow_character = _shadow_character; -} - -bool_t keyboard_get_shadow() -{ - return shadow; -} - -bool_t keyboard_is_ctrl_pressed() -{ - return (bool_t)(keyboard_flags & (KBD_LEFT_CONTROL)); -} - -bool_t keyboard_is_shifted() -{ - return (bool_t)(keyboard_flags & (KBD_LEFT_SHIFT | KBD_RIGHT_SHIFT)); + return read_character(); } diff --git a/mentos/src/drivers/keyboard/keymap.c b/mentos/src/drivers/keyboard/keymap.c index 79c5c74..9a2438b 100644 --- a/mentos/src/drivers/keyboard/keymap.c +++ b/mentos/src/drivers/keyboard/keymap.c @@ -1,207 +1,284 @@ /// MentOS, The Mentoring Operating system project /// @file keymap.c /// @brief Keymap for keyboard. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "keymap.h" +#include "drivers/keyboard/keymap.h" +#include "string.h" -const keymap_t keymap_it = { - .base = { - [0] = -1, - [KEY_ESCAPE] = 27, - [KEY_ONE] = '1', - [KEY_TWO] = '2', - [KEY_THREE] = '3', - [KEY_FOUR] = '4', - [KEY_FIVE] = '5', - [KEY_SIX] = '6', - [KEY_SEVEN] = '7', - [KEY_EIGHT] = '8', - [KEY_NINE] = '9', - [KEY_ZERO] = '0', - [KEY_APOSTROPHE] = '\'', - [KEY_I_ACC] = 141, - [KEY_BACKSPACE] = '\b', - [KEY_TAB] = '\t', - [KEY_Q] = 'q', - [KEY_W] = 'w', - [KEY_E] = 'e', - [KEY_R] = 'r', - [KEY_T] = 't', - [KEY_Y] = 'y', - [KEY_U] = 'u', - [KEY_I] = 'i', - [KEY_O] = 'o', - [KEY_P] = 'p', - [KEY_LEFT_BRAKET] = 138, - [KEY_RIGHT_BRAKET] = '+', - [KEY_ENTER] = 13, - [KEY_LEFT_CONTROL] = -1, - [KEY_A] = 'a', - [KEY_S] = 's', - [KEY_D] = 'd', - [KEY_F] = 'f', - [KEY_G] = 'g', - [KEY_H] = 'h', - [KEY_J] = 'j', - [KEY_K] = 'k', - [KEY_L] = 'l', - [KEY_SEMICOLON] = 149, - [KEY_DOUBLE_QUOTES] = 133, - [KEY_GRAVE] = '\\', - [KEY_LEFT_SHIFT] = -1, - [KEY_BACKSLASH] = 151, - [KEY_Z] = 'z', - [KEY_X] = 'x', - [KEY_C] = 'c', - [KEY_V] = 'v', - [KEY_B] = 'b', - [KEY_N] = 'n', - [KEY_M] = 'm', - [KEY_COMMA] = ',', - [KEY_PERIOD] = '.', - [KEY_MINUS] = '-', - [KEY_RIGHT_SHIFT] = -1, - [KEY_KP_MUL] = '*', - [KEY_LEFT_ALT] = -1, - [KEY_SPACE] = ' ', - [KEY_CAPS_LOCK] = -1, - [KEY_F1] = -1, - [KEY_F2] = -1, - [KEY_F3] = -1, - [KEY_F4] = -1, - [KEY_F5] = -1, - [KEY_F6] = -1, - [KEY_F7] = -1, - [KEY_F8] = -1, - [KEY_F9] = -1, - [KEY_F10] = -1, - [KEY_NUM_LOCK] = -1, - [KEY_SCROLL_LOCK] = -1, - [KEY_KP7] = -1, - [KEY_KP8] = -1, - [KEY_KP9] = -1, - [KEY_KP_SUB] = '-', - [KEY_KP4] = -1, - [KEY_KP5] = -1, - [KEY_KP6] = -1, - [KEY_KP_ADD] = '+', - [KEY_KP1] = -1, - [KEY_KP2] = -1, - [KEY_KP3] = -1, - [KEY_KP0] = -1, - [KEY_KP_DEC] = -1, - [84] = -1, - [85] = -1, - [KEY_KP_LESS] = '<', - [87] = -1, - [88] = -1, - [KEY_KP_DIV] = '/' - }, - .shift = { - [0] = -1, - [KEY_ESCAPE] = -1, - [KEY_ONE] = '!', - [KEY_TWO] = '"', - [KEY_THREE] = 156, - [KEY_FOUR] = '$', - [KEY_FIVE] = '%', - [KEY_SIX] = '&', - [KEY_SEVEN] = '/', - [KEY_EIGHT] = '(', - [KEY_NINE] = ')', - [KEY_ZERO] = '=', - [KEY_APOSTROPHE] = '?', - [KEY_I_ACC] = '^', - [KEY_BACKSPACE] = -1, - [KEY_TAB] = -1, - [KEY_Q] = 'Q', - [KEY_W] = 'W', - [KEY_E] = 'E', - [KEY_R] = 'R', - [KEY_T] = 'T', - [KEY_Y] = 'Y', - [KEY_U] = 'U', - [KEY_I] = 'I', - [KEY_O] = 'O', - [KEY_P] = 'P', - [KEY_LEFT_BRAKET] = 130, - [KEY_RIGHT_BRAKET] = '*', - [KEY_ENTER] = -1, - [KEY_LEFT_CONTROL] = -1, - [KEY_A] = 'A', - [KEY_S] = 'S', - [KEY_D] = 'D', - [KEY_F] = 'F', - [KEY_G] = 'G', - [KEY_H] = 'H', - [KEY_J] = 'J', - [KEY_K] = 'K', - [KEY_L] = 'L', - [KEY_SEMICOLON] = 128, - [KEY_DOUBLE_QUOTES] = 167, - [KEY_GRAVE] = '|', - [KEY_LEFT_SHIFT] = -1, - [KEY_BACKSLASH] = -1, - [KEY_Z] = 'Z', - [KEY_X] = 'X', - [KEY_C] = 'C', - [KEY_V] = 'V', - [KEY_B] = 'B', - [KEY_N] = 'N', - [KEY_M] = 'M', - [KEY_COMMA] = ';', - [KEY_PERIOD] = ':', - [KEY_MINUS] = '_', - [KEY_RIGHT_SHIFT] = -1, - [KEY_KP_MUL] = '*', - [KEY_LEFT_ALT] = -1, - [KEY_SPACE] = ' ', - [KEY_CAPS_LOCK] = -1, - [KEY_F1] = -1, - [KEY_F2] = -1, - [KEY_F3] = -1, - [KEY_F4] = -1, - [KEY_F5] = -1, - [KEY_F6] = -1, - [KEY_F7] = -1, - [KEY_F8] = -1, - [KEY_F9] = -1, - [KEY_F10] = -1, - [KEY_NUM_LOCK] = -1, - [KEY_SCROLL_LOCK] = -1, - [KEY_KP7] = -1, - [KEY_KP8] = -1, - [KEY_KP9] = -1, - [KEY_KP_SUB] = '-', - [KEY_KP4] = -1, - [KEY_KP5] = -1, - [KEY_KP6] = -1, - [KEY_KP_ADD] = '+', - [KEY_KP1] = -1, - [KEY_KP2] = -1, - [KEY_KP3] = -1, - [KEY_KP0] = -1, - [KEY_KP_DEC] = -1, - [84] = -1, - [85] = -1, - [KEY_KP_LESS] = '>', - [87] = -1, - [88] = -1, - [KEY_KP_DIV] = '/' - }, - .numlock = { - [KEY_KP_DEC] = '.', - [KEY_KP0] = '0', - [KEY_KP1] = '1', - [KEY_KP2] = '2', - [KEY_KP3] = '3', - [KEY_KP4] = '4', - [KEY_KP5] = '5', - [KEY_KP6] = '6', - [KEY_KP7] = '7', - [KEY_KP8] = '8', - [KEY_KP9] = '9', - }, -}; +/// Identifies the current keymap type. +keymap_type_t keymap_type = KEYMAP_IT; +/// Contains the different keymaps. +keymap_t keymaps[KEYMAP_TYPE_MAX]; + +keymap_type_t get_keymap_type() +{ + return keymap_type; +} + +void set_keymap_type(keymap_type_t type) +{ + if (type != keymap_type) + keymap_type = type; +} + +const keymap_t *get_keymap() +{ + return &keymaps[keymap_type]; +} + +void init_keymaps() +{ + for (int i = 0; i < KEYMAP_TYPE_MAX; ++i) + memset(&keymaps[i], -1, sizeof(keymap_t)); + + // == ITALIAN KEY MAPPING ================================================= + keymaps[KEYMAP_IT].base[KEY_ESCAPE] = 27; + keymaps[KEYMAP_IT].base[KEY_ONE] = '1'; + keymaps[KEYMAP_IT].base[KEY_TWO] = '2'; + keymaps[KEYMAP_IT].base[KEY_THREE] = '3'; + keymaps[KEYMAP_IT].base[KEY_FOUR] = '4'; + keymaps[KEYMAP_IT].base[KEY_FIVE] = '5'; + keymaps[KEYMAP_IT].base[KEY_SIX] = '6'; + keymaps[KEYMAP_IT].base[KEY_SEVEN] = '7'; + keymaps[KEYMAP_IT].base[KEY_EIGHT] = '8'; + keymaps[KEYMAP_IT].base[KEY_NINE] = '9'; + keymaps[KEYMAP_IT].base[KEY_ZERO] = '0'; + keymaps[KEYMAP_IT].base[KEY_APOSTROPHE] = '\''; + keymaps[KEYMAP_IT].base[KEY_I_ACC] = 141; + keymaps[KEYMAP_IT].base[KEY_BACKSPACE] = '\b'; + keymaps[KEYMAP_IT].base[KEY_TAB] = '\t'; + keymaps[KEYMAP_IT].base[KEY_Q] = 'q'; + keymaps[KEYMAP_IT].base[KEY_W] = 'w'; + keymaps[KEYMAP_IT].base[KEY_E] = 'e'; + keymaps[KEYMAP_IT].base[KEY_R] = 'r'; + keymaps[KEYMAP_IT].base[KEY_T] = 't'; + keymaps[KEYMAP_IT].base[KEY_Y] = 'y'; + keymaps[KEYMAP_IT].base[KEY_U] = 'u'; + keymaps[KEYMAP_IT].base[KEY_I] = 'i'; + keymaps[KEYMAP_IT].base[KEY_O] = 'o'; + keymaps[KEYMAP_IT].base[KEY_P] = 'p'; + keymaps[KEYMAP_IT].base[KEY_LEFT_BRAKET] = 138; + keymaps[KEYMAP_IT].base[KEY_RIGHT_BRAKET] = '+'; + keymaps[KEYMAP_IT].base[KEY_ENTER] = 13; + keymaps[KEYMAP_IT].base[KEY_A] = 'a'; + keymaps[KEYMAP_IT].base[KEY_S] = 's'; + keymaps[KEYMAP_IT].base[KEY_D] = 'd'; + keymaps[KEYMAP_IT].base[KEY_F] = 'f'; + keymaps[KEYMAP_IT].base[KEY_G] = 'g'; + keymaps[KEYMAP_IT].base[KEY_H] = 'h'; + keymaps[KEYMAP_IT].base[KEY_J] = 'j'; + keymaps[KEYMAP_IT].base[KEY_K] = 'k'; + keymaps[KEYMAP_IT].base[KEY_L] = 'l'; + keymaps[KEYMAP_IT].base[KEY_SEMICOLON] = 149; + keymaps[KEYMAP_IT].base[KEY_DOUBLE_QUOTES] = 133; + keymaps[KEYMAP_IT].base[KEY_GRAVE] = '\\'; + keymaps[KEYMAP_IT].base[KEY_BACKSLASH] = 151; + keymaps[KEYMAP_IT].base[KEY_Z] = 'z'; + keymaps[KEYMAP_IT].base[KEY_X] = 'x'; + keymaps[KEYMAP_IT].base[KEY_C] = 'c'; + keymaps[KEYMAP_IT].base[KEY_V] = 'v'; + keymaps[KEYMAP_IT].base[KEY_B] = 'b'; + keymaps[KEYMAP_IT].base[KEY_N] = 'n'; + keymaps[KEYMAP_IT].base[KEY_M] = 'm'; + keymaps[KEYMAP_IT].base[KEY_COMMA] = ','; + keymaps[KEYMAP_IT].base[KEY_PERIOD] = '.'; + keymaps[KEYMAP_IT].base[KEY_MINUS] = '-'; + keymaps[KEYMAP_IT].base[KEY_KP_MUL] = '*'; + keymaps[KEYMAP_IT].base[KEY_SPACE] = ' '; + keymaps[KEYMAP_IT].base[KEY_KP_SUB] = '-'; + keymaps[KEYMAP_IT].base[KEY_KP_ADD] = '+'; + keymaps[KEYMAP_IT].base[KEY_KP_LESS] = '<'; + keymaps[KEYMAP_IT].base[KEY_KP_DIV] = '/'; + + keymaps[KEYMAP_IT].shift[KEY_ONE] = '!'; + keymaps[KEYMAP_IT].shift[KEY_TWO] = '"'; + keymaps[KEYMAP_IT].shift[KEY_THREE] = 156; + keymaps[KEYMAP_IT].shift[KEY_FOUR] = '$'; + keymaps[KEYMAP_IT].shift[KEY_FIVE] = '%'; + keymaps[KEYMAP_IT].shift[KEY_SIX] = '&'; + keymaps[KEYMAP_IT].shift[KEY_SEVEN] = '/'; + keymaps[KEYMAP_IT].shift[KEY_EIGHT] = '('; + keymaps[KEYMAP_IT].shift[KEY_NINE] = ')'; + keymaps[KEYMAP_IT].shift[KEY_ZERO] = '='; + keymaps[KEYMAP_IT].shift[KEY_APOSTROPHE] = '?'; + keymaps[KEYMAP_IT].shift[KEY_I_ACC] = '^'; + keymaps[KEYMAP_IT].shift[KEY_Q] = 'Q'; + keymaps[KEYMAP_IT].shift[KEY_W] = 'W'; + keymaps[KEYMAP_IT].shift[KEY_E] = 'E'; + keymaps[KEYMAP_IT].shift[KEY_R] = 'R'; + keymaps[KEYMAP_IT].shift[KEY_T] = 'T'; + keymaps[KEYMAP_IT].shift[KEY_Y] = 'Y'; + keymaps[KEYMAP_IT].shift[KEY_U] = 'U'; + keymaps[KEYMAP_IT].shift[KEY_I] = 'I'; + keymaps[KEYMAP_IT].shift[KEY_O] = 'O'; + keymaps[KEYMAP_IT].shift[KEY_P] = 'P'; + keymaps[KEYMAP_IT].shift[KEY_LEFT_BRAKET] = 130; + keymaps[KEYMAP_IT].shift[KEY_RIGHT_BRAKET] = '*'; + keymaps[KEYMAP_IT].shift[KEY_A] = 'A'; + keymaps[KEYMAP_IT].shift[KEY_S] = 'S'; + keymaps[KEYMAP_IT].shift[KEY_D] = 'D'; + keymaps[KEYMAP_IT].shift[KEY_F] = 'F'; + keymaps[KEYMAP_IT].shift[KEY_G] = 'G'; + keymaps[KEYMAP_IT].shift[KEY_H] = 'H'; + keymaps[KEYMAP_IT].shift[KEY_J] = 'J'; + keymaps[KEYMAP_IT].shift[KEY_K] = 'K'; + keymaps[KEYMAP_IT].shift[KEY_L] = 'L'; + keymaps[KEYMAP_IT].shift[KEY_SEMICOLON] = 128; + keymaps[KEYMAP_IT].shift[KEY_DOUBLE_QUOTES] = 167; + keymaps[KEYMAP_IT].shift[KEY_GRAVE] = '|'; + keymaps[KEYMAP_IT].shift[KEY_Z] = 'Z'; + keymaps[KEYMAP_IT].shift[KEY_X] = 'X'; + keymaps[KEYMAP_IT].shift[KEY_C] = 'C'; + keymaps[KEYMAP_IT].shift[KEY_V] = 'V'; + keymaps[KEYMAP_IT].shift[KEY_B] = 'B'; + keymaps[KEYMAP_IT].shift[KEY_N] = 'N'; + keymaps[KEYMAP_IT].shift[KEY_M] = 'M'; + keymaps[KEYMAP_IT].shift[KEY_COMMA] = ';'; + keymaps[KEYMAP_IT].shift[KEY_PERIOD] = ':'; + keymaps[KEYMAP_IT].shift[KEY_MINUS] = '_'; + keymaps[KEYMAP_IT].shift[KEY_KP_MUL] = '*'; + keymaps[KEYMAP_IT].shift[KEY_SPACE] = ' '; + keymaps[KEYMAP_IT].shift[KEY_KP_SUB] = '-'; + keymaps[KEYMAP_IT].shift[KEY_KP_ADD] = '+'; + keymaps[KEYMAP_IT].shift[KEY_KP_LESS] = '>'; + keymaps[KEYMAP_IT].shift[KEY_KP_DIV] = '/'; + + keymaps[KEYMAP_IT].numlock[KEY_KP_DEC] = '.'; + keymaps[KEYMAP_IT].numlock[KEY_KP0] = '0'; + keymaps[KEYMAP_IT].numlock[KEY_KP1] = '1'; + keymaps[KEYMAP_IT].numlock[KEY_KP2] = '2'; + keymaps[KEYMAP_IT].numlock[KEY_KP3] = '3'; + keymaps[KEYMAP_IT].numlock[KEY_KP4] = '4'; + keymaps[KEYMAP_IT].numlock[KEY_KP5] = '5'; + keymaps[KEYMAP_IT].numlock[KEY_KP6] = '6'; + keymaps[KEYMAP_IT].numlock[KEY_KP7] = '7'; + keymaps[KEYMAP_IT].numlock[KEY_KP8] = '8'; + keymaps[KEYMAP_IT].numlock[KEY_KP9] = '9'; + + // == US KEY MAPPING ====================================================== + + keymaps[KEYMAP_US].base[KEY_ESCAPE] = 27; + keymaps[KEYMAP_US].base[KEY_ONE] = '1'; + keymaps[KEYMAP_US].base[KEY_TWO] = '2'; + keymaps[KEYMAP_US].base[KEY_THREE] = '3'; + keymaps[KEYMAP_US].base[KEY_FOUR] = '4'; + keymaps[KEYMAP_US].base[KEY_FIVE] = '5'; + keymaps[KEYMAP_US].base[KEY_SIX] = '6'; + keymaps[KEYMAP_US].base[KEY_SEVEN] = '7'; + keymaps[KEYMAP_US].base[KEY_EIGHT] = '8'; + keymaps[KEYMAP_US].base[KEY_NINE] = '9'; + keymaps[KEYMAP_US].base[KEY_ZERO] = '0'; + keymaps[KEYMAP_US].base[KEY_APOSTROPHE] = '-'; + keymaps[KEYMAP_US].base[KEY_I_ACC] = '='; + keymaps[KEYMAP_US].base[KEY_BACKSPACE] = '\b'; + keymaps[KEYMAP_US].base[KEY_TAB] = '\t'; + keymaps[KEYMAP_US].base[KEY_Q] = 'q'; + keymaps[KEYMAP_US].base[KEY_W] = 'w'; + keymaps[KEYMAP_US].base[KEY_E] = 'e'; + keymaps[KEYMAP_US].base[KEY_R] = 'r'; + keymaps[KEYMAP_US].base[KEY_T] = 't'; + keymaps[KEYMAP_US].base[KEY_Y] = 'y'; + keymaps[KEYMAP_US].base[KEY_U] = 'u'; + keymaps[KEYMAP_US].base[KEY_I] = 'i'; + keymaps[KEYMAP_US].base[KEY_O] = 'o'; + keymaps[KEYMAP_US].base[KEY_P] = 'p'; + keymaps[KEYMAP_US].base[KEY_LEFT_BRAKET] = '['; + keymaps[KEYMAP_US].base[KEY_RIGHT_BRAKET] = ']'; + keymaps[KEYMAP_US].base[KEY_ENTER] = 13; + keymaps[KEYMAP_US].base[KEY_A] = 'a'; + keymaps[KEYMAP_US].base[KEY_S] = 's'; + keymaps[KEYMAP_US].base[KEY_D] = 'd'; + keymaps[KEYMAP_US].base[KEY_F] = 'f'; + keymaps[KEYMAP_US].base[KEY_G] = 'g'; + keymaps[KEYMAP_US].base[KEY_H] = 'h'; + keymaps[KEYMAP_US].base[KEY_J] = 'j'; + keymaps[KEYMAP_US].base[KEY_K] = 'k'; + keymaps[KEYMAP_US].base[KEY_L] = 'l'; + keymaps[KEYMAP_US].base[KEY_SEMICOLON] = ';'; + keymaps[KEYMAP_US].base[KEY_DOUBLE_QUOTES] = '\''; + keymaps[KEYMAP_US].base[KEY_GRAVE] = '`'; + keymaps[KEYMAP_US].base[KEY_BACKSLASH] = '\\'; + keymaps[KEYMAP_US].base[KEY_Z] = 'z'; + keymaps[KEYMAP_US].base[KEY_X] = 'x'; + keymaps[KEYMAP_US].base[KEY_C] = 'c'; + keymaps[KEYMAP_US].base[KEY_V] = 'v'; + keymaps[KEYMAP_US].base[KEY_B] = 'b'; + keymaps[KEYMAP_US].base[KEY_N] = 'n'; + keymaps[KEYMAP_US].base[KEY_M] = 'm'; + keymaps[KEYMAP_US].base[KEY_COMMA] = ','; + keymaps[KEYMAP_US].base[KEY_PERIOD] = '.'; + keymaps[KEYMAP_US].base[KEY_MINUS] = '/'; + keymaps[KEYMAP_US].base[KEY_KP_MUL] = '*'; + keymaps[KEYMAP_US].base[KEY_SPACE] = ' '; + keymaps[KEYMAP_US].base[KEY_KP_SUB] = '-'; + keymaps[KEYMAP_US].base[KEY_KP_ADD] = '+'; + keymaps[KEYMAP_US].base[KEY_KP_LESS] = '<'; + keymaps[KEYMAP_US].base[KEY_KP_DIV] = '/'; + + keymaps[KEYMAP_US].shift[KEY_ONE] = '!'; + keymaps[KEYMAP_US].shift[KEY_TWO] = '@'; + keymaps[KEYMAP_US].shift[KEY_THREE] = '#'; + keymaps[KEYMAP_US].shift[KEY_FOUR] = '$'; + keymaps[KEYMAP_US].shift[KEY_FIVE] = '%'; + keymaps[KEYMAP_US].shift[KEY_SIX] = '^'; + keymaps[KEYMAP_US].shift[KEY_SEVEN] = '&'; + keymaps[KEYMAP_US].shift[KEY_EIGHT] = '*'; + keymaps[KEYMAP_US].shift[KEY_NINE] = '('; + keymaps[KEYMAP_US].shift[KEY_ZERO] = ')'; + keymaps[KEYMAP_US].shift[KEY_APOSTROPHE] = '_'; + keymaps[KEYMAP_US].shift[KEY_I_ACC] = '+'; + keymaps[KEYMAP_US].shift[KEY_Q] = 'Q'; + keymaps[KEYMAP_US].shift[KEY_W] = 'W'; + keymaps[KEYMAP_US].shift[KEY_E] = 'E'; + keymaps[KEYMAP_US].shift[KEY_R] = 'R'; + keymaps[KEYMAP_US].shift[KEY_T] = 'T'; + keymaps[KEYMAP_US].shift[KEY_Y] = 'Y'; + keymaps[KEYMAP_US].shift[KEY_U] = 'U'; + keymaps[KEYMAP_US].shift[KEY_I] = 'I'; + keymaps[KEYMAP_US].shift[KEY_O] = 'O'; + keymaps[KEYMAP_US].shift[KEY_P] = 'P'; + keymaps[KEYMAP_US].shift[KEY_LEFT_BRAKET] = '{'; + keymaps[KEYMAP_US].shift[KEY_RIGHT_BRAKET] = '}'; + keymaps[KEYMAP_US].shift[KEY_A] = 'A'; + keymaps[KEYMAP_US].shift[KEY_S] = 'S'; + keymaps[KEYMAP_US].shift[KEY_D] = 'D'; + keymaps[KEYMAP_US].shift[KEY_F] = 'F'; + keymaps[KEYMAP_US].shift[KEY_G] = 'G'; + keymaps[KEYMAP_US].shift[KEY_H] = 'H'; + keymaps[KEYMAP_US].shift[KEY_J] = 'J'; + keymaps[KEYMAP_US].shift[KEY_K] = 'K'; + keymaps[KEYMAP_US].shift[KEY_L] = 'L'; + keymaps[KEYMAP_US].shift[KEY_SEMICOLON] = ':'; + keymaps[KEYMAP_US].shift[KEY_DOUBLE_QUOTES] = '"'; + keymaps[KEYMAP_US].shift[KEY_GRAVE] = '~'; + keymaps[KEYMAP_US].shift[KEY_Z] = 'Z'; + keymaps[KEYMAP_US].shift[KEY_X] = 'X'; + keymaps[KEYMAP_US].shift[KEY_C] = 'C'; + keymaps[KEYMAP_US].shift[KEY_V] = 'V'; + keymaps[KEYMAP_US].shift[KEY_B] = 'B'; + keymaps[KEYMAP_US].shift[KEY_N] = 'N'; + keymaps[KEYMAP_US].shift[KEY_M] = 'M'; + keymaps[KEYMAP_US].shift[KEY_COMMA] = '<'; + keymaps[KEYMAP_US].shift[KEY_PERIOD] = '>'; + keymaps[KEYMAP_US].shift[KEY_MINUS] = '?'; + keymaps[KEYMAP_US].shift[KEY_KP_MUL] = '*'; + keymaps[KEYMAP_US].shift[KEY_SPACE] = ' '; + keymaps[KEYMAP_US].shift[KEY_KP_SUB] = '-'; + keymaps[KEYMAP_US].shift[KEY_KP_ADD] = '+'; + keymaps[KEYMAP_US].shift[KEY_KP_LESS] = '>'; + keymaps[KEYMAP_US].shift[KEY_KP_DIV] = '/'; + + keymaps[KEYMAP_US].numlock[KEY_KP_DEC] = '.'; + keymaps[KEYMAP_US].numlock[KEY_KP0] = '0'; + keymaps[KEYMAP_US].numlock[KEY_KP1] = '1'; + keymaps[KEYMAP_US].numlock[KEY_KP2] = '2'; + keymaps[KEYMAP_US].numlock[KEY_KP3] = '3'; + keymaps[KEYMAP_US].numlock[KEY_KP4] = '4'; + keymaps[KEYMAP_US].numlock[KEY_KP5] = '5'; + keymaps[KEYMAP_US].numlock[KEY_KP6] = '6'; + keymaps[KEYMAP_US].numlock[KEY_KP7] = '7'; + keymaps[KEYMAP_US].numlock[KEY_KP8] = '8'; + keymaps[KEYMAP_US].numlock[KEY_KP9] = '9'; +} \ No newline at end of file diff --git a/mentos/src/drivers/mouse.c b/mentos/src/drivers/mouse.c index 1073d79..83acc45 100644 --- a/mentos/src/drivers/mouse.c +++ b/mentos/src/drivers/mouse.c @@ -1,11 +1,14 @@ /// MentOS, The Mentoring Operating system project /// @file mouse.c /// @brief Driver for *PS2* Mouses. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details.is distributed under the MIT License. +///// See LICENSE.md for details. +/////! @cond Doxygen_Suppress -#include "mouse.h" -#include "pic8259.h" +#include "drivers/mouse.h" +#include "hardware/pic8259.h" +#include "io/port_io.h" static uint8_t mouse_cycle = 0; @@ -17,159 +20,161 @@ static int32_t mouse_y = (600 / 2); void mouse_install() { - // Enable the auxiliary mouse device. - mouse_waitcmd(1); - outportb(0x64, 0xA8); + // Enable the auxiliary mouse device. + mouse_waitcmd(1); + outportb(0x64, 0xA8); - // Enable the interrupts. - mouse_waitcmd(1); - outportb(0x64, 0x20); - mouse_waitcmd(0); - uint8_t status_byte = (inportb(0x60) | 2); - mouse_waitcmd(1); - outportb(0x64, 0x60); - mouse_waitcmd(1); - outportb(0x60, status_byte); + // Enable the interrupts. + mouse_waitcmd(1); + outportb(0x64, 0x20); + mouse_waitcmd(0); + uint8_t status_byte = (inportb(0x60) | 2); + mouse_waitcmd(1); + outportb(0x64, 0x60); + mouse_waitcmd(1); + outportb(0x60, status_byte); - // Tell the mouse to use default settings. - mouse_write(MOUSE_USE_DEFAULT_SETTINGS); - // Acknowledge. - mouse_read(); + // Tell the mouse to use default settings. + mouse_write(MOUSE_USE_DEFAULT_SETTINGS); + // Acknowledge. + mouse_read(); - // Setup the mouse handler. - // pic8259_irq_install_handler(IRQ_MOUSE, mouse_isr); + // Setup the mouse handler. + // pic8259_irq_install_handler(IRQ_MOUSE, mouse_isr); - mouse_enable(); + mouse_enable(); } void mouse_enable() { - // Enable the mouse interrupts. - pic8259_irq_enable(IRQ_MOUSE); - // Disable the mouse. - mouse_write(MOUSE_ENABLE_PACKET); - // Acknowledge. - mouse_read(); + // Enable the mouse interrupts. + pic8259_irq_enable(IRQ_MOUSE); + // Disable the mouse. + mouse_write(MOUSE_ENABLE_PACKET); + // Acknowledge. + mouse_read(); } void mouse_disable() { - // Disable the mouse interrupts. - pic8259_irq_disable(IRQ_MOUSE); - // Disable the mouse. - mouse_write(MOUSE_DISABLE_PACKET); - // Acknowledge. - mouse_read(); + // Disable the mouse interrupts. + pic8259_irq_disable(IRQ_MOUSE); + // Disable the mouse. + mouse_write(MOUSE_DISABLE_PACKET); + // Acknowledge. + mouse_read(); } void mouse_waitcmd(unsigned char type) { - register unsigned int _time_out = 100000; - if (type == 0) { - // DATA. - while (_time_out--) { - if ((inportb(0x64) & 1) == 1) { - return; - } - } - return; - } else { - while (_time_out--) // SIGNALS - { - if ((inportb(0x64) & 2) == 0) { - return; - } - } - return; - } + register unsigned int _time_out = 100000; + if (type == 0) { + // DATA. + while (_time_out--) { + if ((inportb(0x64) & 1) == 1) { + return; + } + } + return; + } else { + while (_time_out--) // SIGNALS + { + if ((inportb(0x64) & 2) == 0) { + return; + } + } + return; + } } void mouse_write(unsigned char data) { - mouse_waitcmd(1); - outportb(0x64, 0xD4); - mouse_waitcmd(1); - outportb(0x60, data); + mouse_waitcmd(1); + outportb(0x64, 0xD4); + mouse_waitcmd(1); + outportb(0x60, data); } unsigned char mouse_read() { - mouse_waitcmd(0); - return inportb(0x60); + mouse_waitcmd(0); + return inportb(0x60); } -void mouse_isr(register_t *r) +void mouse_isr(pt_regs *f) { - (void)r; - // Get the input bytes. - mouse_bytes[mouse_cycle++] = (char)inportb(0x60); - if (mouse_cycle == 3) { - // Reset the mouse cycle. - mouse_cycle = 0; - // ---------------------------- - // Get the X coordinates. - // ---------------------------- - if ((mouse_bytes[0] & 0x40) == 0) { - // Bit number 4 of the first byte (value 0x10) indicates that - // delta X (the 2nd byte) is a negative number, if it is set. - if ((mouse_bytes[0] & 0x10) == 0) { - mouse_x -= mouse_bytes[1]; - } else { - mouse_x += mouse_bytes[1]; - } - } else { - // Overflow. - mouse_x += mouse_bytes[1] / 2; - } - // ---------------------------- - // Get the Y coordinates. - // ---------------------------- - if ((mouse_bytes[0] & 0x80) == 0) { - // Bit number 5 of the first byte (value 0x20) indicates that - // delta Y (the 3rd byte) is a negative number, if it is set. - if ((mouse_bytes[0] & 0x20) == 0) { - mouse_y -= mouse_bytes[2]; - } else { - mouse_y += mouse_bytes[2]; - } - } else { - // Overflow. - mouse_y -= mouse_bytes[2] / 2; - } - // ---------------------------- - // Apply cursor constraint (800x600). - // ---------------------------- - if (mouse_x <= 0) { - mouse_x = 0; - } else if (mouse_x >= (800 - 16)) { - mouse_x = 800 - 16; - } - if (mouse_y <= 0) { - mouse_y = 0; - } else if (mouse_y >= (600 - 24)) { - mouse_y = 600 - 24; - } - // Print the position. - // dbg_print("\rX: %d | Y: %d\n", mouse_x, mouse_y); + (void)f; + // Get the input bytes. + mouse_bytes[mouse_cycle++] = (char)inportb(0x60); + if (mouse_cycle == 3) { + // Reset the mouse cycle. + mouse_cycle = 0; + // ---------------------------- + // Get the X coordinates. + // ---------------------------- + if ((mouse_bytes[0] & 0x40) == 0) { + // Bit number 4 of the first byte (value 0x10) indicates that + // delta X (the 2nd byte) is a negative number, if it is set. + if ((mouse_bytes[0] & 0x10) == 0) { + mouse_x -= mouse_bytes[1]; + } else { + mouse_x += mouse_bytes[1]; + } + } else { + // Overflow. + mouse_x += mouse_bytes[1] / 2; + } + // ---------------------------- + // Get the Y coordinates. + // ---------------------------- + if ((mouse_bytes[0] & 0x80) == 0) { + // Bit number 5 of the first byte (value 0x20) indicates that + // delta Y (the 3rd byte) is a negative number, if it is set. + if ((mouse_bytes[0] & 0x20) == 0) { + mouse_y -= mouse_bytes[2]; + } else { + mouse_y += mouse_bytes[2]; + } + } else { + // Overflow. + mouse_y -= mouse_bytes[2] / 2; + } + // ---------------------------- + // Apply cursor constraint (800x600). + // ---------------------------- + if (mouse_x <= 0) { + mouse_x = 0; + } else if (mouse_x >= (800 - 16)) { + mouse_x = 800 - 16; + } + if (mouse_y <= 0) { + mouse_y = 0; + } else if (mouse_y >= (600 - 24)) { + mouse_y = 600 - 24; + } + // Print the position. + // pr_default("\rX: %d | Y: %d\n", mouse_x, mouse_y); - // Move the cursor. - // video_set_cursor(mouse_x, mouse_y); + // Move the cursor. + // video_set_cursor(mouse_x, mouse_y); - // Here a problem is detected, if the mouse moves - // Pressed keys are detected. - // Detecting keystrokes. - // Center pressed. - if ((mouse_bytes[0] & 0x04) == 0) { - // dbg_print(LNG_MOUSE_MID); - } - // Right pressed. - if ((mouse_bytes[0] & 0x02) == 0) { - // dbg_print(LNG_MOUSE_RIGHT); - } - // Left pressed. - if ((mouse_bytes[0] & 0x01) == 0) { - // dbg_print(LNG_MOUSE_LEFT); - } - } - pic8259_send_eoi(IRQ_MOUSE); + // Here a problem is detected, if the mouse moves + // Pressed keys are detected. + // Detecting keystrokes. + // Center pressed. + if ((mouse_bytes[0] & 0x04) == 0) { + // pr_default(LNG_MOUSE_MID); + } + // Right pressed. + if ((mouse_bytes[0] & 0x02) == 0) { + // pr_default(LNG_MOUSE_RIGHT); + } + // Left pressed. + if ((mouse_bytes[0] & 0x01) == 0) { + // pr_default(LNG_MOUSE_LEFT); + } + } + pic8259_send_eoi(IRQ_MOUSE); } + +///! @endcond \ No newline at end of file diff --git a/mentos/src/drivers/rtc.c b/mentos/src/drivers/rtc.c new file mode 100644 index 0000000..153043e --- /dev/null +++ b/mentos/src/drivers/rtc.c @@ -0,0 +1,138 @@ +/// MentOS, The Mentoring Operating system project +/// @file rtc.c +/// @brief Real Time Clock (RTC) driver. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "drivers/rtc.h" + +#include "hardware/pic8259.h" +#include "string.h" +#include "io/port_io.h" +#include "kernel.h" +#include "descriptor_tables/isr.h" + +#define CMOS_ADDR 0x70 ///< Addess where we need to write the Address. +#define CMOS_DATA 0x71 ///< Addess where we need to write the Data. + +/// Current global time. +tm_t global_time; +/// Previous global time. +tm_t previous_global_time; +/// Data type is BCD. +int is_bcd; + +static inline unsigned int rtc_are_different(tm_t *t0, tm_t *t1) +{ + if (t0->tm_sec != t1->tm_sec) + return 1; + if (t0->tm_min != t1->tm_min) + return 1; + if (t0->tm_hour != t1->tm_hour) + return 1; + if (t0->tm_mon != t1->tm_mon) + return 1; + if (t0->tm_year != t1->tm_year) + return 1; + if (t0->tm_wday != t1->tm_wday) + return 1; + if (t0->tm_mday != t1->tm_mday) + return 1; + return 0; +} + +/// @brief Check if rtc is updating time currently. +static inline unsigned int is_updating_rtc() +{ + outportb(CMOS_ADDR, 0x0A); + uint32_t status = inportb(CMOS_DATA); + return (status & 0x80U); +} + +static inline unsigned char read_register(unsigned char reg) +{ + outportb(CMOS_ADDR, reg); + return inportb(CMOS_DATA); +} + +static inline void write_register(unsigned char reg, unsigned char value) +{ + outportb(CMOS_ADDR, reg); + outportb(CMOS_DATA, value); +} + +static inline unsigned char bcd2bin(unsigned char bcd) +{ + return ((bcd >> 4u) * 10) + (bcd & 0x0Fu); +} + +static inline void rtc_read_datetime() +{ + if (read_register(0x0Cu) & 0x10u) { + if (is_bcd) { + global_time.tm_sec = bcd2bin(read_register(0x00)); + global_time.tm_min = bcd2bin(read_register(0x02)); + global_time.tm_hour = bcd2bin(read_register(0x04)) + 2; + global_time.tm_mon = bcd2bin(read_register(0x08)); + global_time.tm_year = bcd2bin(read_register(0x09)) + 2000; + global_time.tm_wday = bcd2bin(read_register(0x06)); + global_time.tm_mday = bcd2bin(read_register(0x07)); + } else { + global_time.tm_sec = read_register(0x00); + global_time.tm_min = read_register(0x02); + global_time.tm_hour = read_register(0x04) + 2; + global_time.tm_mon = read_register(0x08); + global_time.tm_year = read_register(0x09) + 2000; + global_time.tm_wday = read_register(0x06); + global_time.tm_mday = read_register(0x07); + } + } +} + +static inline void rtc_handler_isr(pt_regs *f) +{ + static unsigned int first_update = 1; + // Wait until rtc is not updating. + while (is_updating_rtc()) + ; + // Read the values. + rtc_read_datetime(); + if (first_update) { + do { + // Save the previous global time. + previous_global_time = global_time; + // Wait until rtc is not updating. + while (is_updating_rtc()) + ; + // Read the values. + rtc_read_datetime(); + } while (!rtc_are_different(&previous_global_time, &global_time)); + first_update = 0; + } +} + +void gettime(tm_t *time) +{ + // Copy the update time. + memcpy(time, &global_time, sizeof(tm_t)); +} + +void rtc_install(void) +{ + unsigned char status; + + status = read_register(0x0B); + status |= 0x02u; // 24 hour clock + status |= 0x10u; // update ended interrupts + status &= ~0x20u; // no alarm interrupts + status &= ~0x40u; // no periodic interrupt + is_bcd = !(status & 0x04u); // check if data type is BCD + write_register(0x0B, status); + + read_register(0x0C); + + // Install the IRQ. + irq_install_handler(IRQ_REAL_TIME_CLOCK, rtc_handler_isr, "Real Time Clock (RTC)"); + // Enable the IRQ. + pic8259_irq_enable(IRQ_REAL_TIME_CLOCK); +} \ No newline at end of file diff --git a/mentos/src/elf/elf.c b/mentos/src/elf/elf.c index 372b0c4..77024a2 100644 --- a/mentos/src/elf/elf.c +++ b/mentos/src/elf/elf.c @@ -1,74 +1,401 @@ /// MentOS, The Mentoring Operating system project /// @file elf.c /// @brief Function for multiboot support. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "elf.h" -#include "debug.h" +/// Change the header. +#define __DEBUG_HEADER__ "[ELF ]" + +#include "elf/elf.h" + +#include "process/scheduler.h" +#include "mem/vmem_map.h" +#include "process/process.h" #include "string.h" -#include "multiboot.h" +#include "stddef.h" +#include "misc/debug.h" +#include "stdio.h" +#include "mem/slab.h" +#include "fs/vfs.h" -/// @brief Data structure containg information about the kernel. -elf_symbols_t kernel_elf; - -/* - * This function grabs a pointer to the array of section headers. - * It then grabs a pointer to the section where all the strings are (.shstrtab) - * (remember that each section header has an offset to this .shstrtab) - * - * Then it's just a matter of iterating sections (checking their names via - * indexing shstrtab) until we find strtab and symtab, which is what we're - * looking for - */ -void build_elf_symbols_from_multiboot(multiboot_info_t *mb) +/// @brief Reads the program header from file. +/// @param file The file from which we extract the program header. +/// @param hdr A pointer to the ELF header. +/// @param idx The index of the program header. +/// @param phdr Where we store the content we read. +/// @return The amount of bytes we read. +static inline ssize_t read_elf_program_header(vfs_file_t *file, elf_header_t *hdr, unsigned idx, elf_program_header_t *phdr) { - uint32_t i; - elf_section_header_t *sh = (elf_section_header_t *)mb->u.elf_sec.addr; - - /* - * .shstrtab has the names of the sections, - * and sh is an array of sections, which themselves contain - * an index to .shstrtab (for their names) - */ - uint32_t shstrtab = sh[mb->u.elf_sec.shndx].addr; - for (i = 0; i < mb->u.elf_sec.num; i++) { - const char *name = - (const char *)(shstrtab + sh[i].name_offset_in_shstrtab); - if (!strcmp(name, ".strtab")) { - kernel_elf.strtab = (const char *)sh[i].addr; - kernel_elf.strtab_size = sh[i].size; - } - if (!strcmp(name, ".symtab")) { - kernel_elf.symtab = (elf_symbol_t *)sh[i].addr; - kernel_elf.symtab_size = sh[i].size; - } - } + return vfs_read(file, phdr, hdr->phoff + hdr->phentsize * idx, sizeof(elf_program_header_t)); } -/* - * Iterate through all the symbols and look for functions... - * Then, as we find functions, check if the symbol is within that - * function's range (given by value and size) - */ -const char *elf_lookup_symbol(uint32_t addr, elf_symbols_t *elf) +/// @brief Reads the section header from file. +/// @param file The file from which we extract the section header. +/// @param hdr A pointer to the ELF header. +/// @param idx The index of the section header. +/// @param shdr Where we store the content we read. +/// @return The amount of bytes we read. +static inline ssize_t read_elf_section_header(vfs_file_t *file, elf_header_t *hdr, unsigned idx, elf_section_header_t *shdr) { - int i; - int num_symbols = elf->symtab_size / sizeof(elf_symbol_t); - - for (i = 0; i < num_symbols; i++) { - if (ELF32_ST_TYPE(elf->symtab[i].info) != ELF32_TYPE_FUNCTION) { - continue; - } - - if ((addr >= elf->symtab[i].value) && - (addr < (elf->symtab[i].value + elf->symtab[i].size))) { - const char *name = - (const char *)((uint32_t)elf->strtab + - elf->symtab[i].name_offset_in_strtab); - return name; - } - } - - return NULL; + return vfs_read(file, shdr, hdr->shoff + hdr->shentsize * idx, sizeof(elf_program_header_t)); } + +/// @brief Reads the symbol from file. +/// @param file The file from which we extract the symbol. +/// @param shdr A pointer to the ELF symbol table header. +/// @param idx The index of the symbol. +/// @param symbol Where we store the content we read. +/// @return The amount of bytes we read. +static inline ssize_t read_elf_symbol(vfs_file_t *file, elf_section_header_t *shdr, unsigned idx, elf_symbol_t *symbol) +{ + // TODO: Here it should use `shdr->entsize`. + return vfs_read(file, symbol, shdr->offset + sizeof(elf_symbol_t) * idx, sizeof(elf_symbol_t)); +} + +/// @brief Reads the symbol from file. +/// @param file The file from which we extract the symbol. +/// @param shdr A pointer to the ELF symbol table header. +/// @param idx The index of the symbol. +/// @param symbol Where we store the content we read. +/// @return The amount of bytes we read. +static inline ssize_t read_elf_symbol_name(vfs_file_t *file, elf_section_header_t *shdr, unsigned offset, char *name, size_t name_len) +{ + return vfs_read(file, name, shdr->offset + offset, name_len); +} + +static inline int elf_find_section_header(vfs_file_t *file, elf_header_t *hdr, int type, elf_section_header_t *shdr) +{ + for (int i = 0; i < hdr->shnum; ++i) { + if (read_elf_section_header(file, hdr, i, shdr) == -1) { + pr_err("Failed to read section header at index %d.\n", i); + return -1; + } + if (shdr->type == type) + return 0; + memset(shdr, 0, sizeof(elf_section_header_t)); + } + return -1; +} + +static inline char *elf_get_strtable(vfs_file_t *file, elf_header_t *hdr, int ndx) +{ + if (ndx == SHT_NULL) + return NULL; + elf_section_header_t shdr; + if (read_elf_section_header(file, hdr, ndx, &shdr) == -1) { + pr_err("Failed to read section header at index %d.\n", ndx); + return NULL; + } + char *strtable = kmalloc(shdr.size); + memset(strtable, 0, shdr.size); + if (vfs_read(file, strtable, shdr.offset, shdr.size) == -1) { + pr_err("Failed to read the string table at %d.\n", shdr.offset); + return NULL; + } +#if 0 + dbg_putchar('{'); + for (int i = 0; i < shdr.size; ++i) { + pr_debug("[%4d] `%c`", i, strtable[i]); + if (strtable[i] == 0) + pr_debug("\n"); + } + dbg_putchar('}'); + dbg_putchar('\n'); +#endif + return strtable; +} + +static inline int elf_load_sigreturn(task_struct *task, vfs_file_t *file, elf_header_t *hdr) +{ + elf_section_header_t shdr; + if (elf_find_section_header(file, hdr, SHT_SYMTAB, &shdr) == -1) + return -1; + char *strtable = elf_get_strtable(file, hdr, shdr.link); + if (strtable == NULL) + return -1; + uint32_t symtab_entries = shdr.size / sizeof(elf_symbol_t); + elf_symbol_t symbol; + for (int i = 0; i < symtab_entries; ++i) { + if (read_elf_symbol(file, &shdr, i, &symbol) == -1) { + pr_err("Failed to read the elf symbol at index %d.\n", i); + break; + } + if (strcmp(strtable + symbol.name, "sigreturn") == 0) { + task->sigreturn_eip = symbol.value; + pr_debug("Found `sigreturn` at index %d with EIP = %p.\n", i, symbol.value); + kfree(strtable); + return 0; + } + } + pr_emerg("Failed to find `sigreturn`!\n"); + kfree(strtable); + return -1; +} + +/// @brief Loads an ELF executable. +/// @param task The task for which we load the ELF. +/// @param file The ELF file. +/// @param hdr The header of the ELF file. +/// @return The ELF entry. +static inline int elf_load_exec(task_struct *task, vfs_file_t *file, elf_header_t *hdr) +{ + elf_program_header_t phdr; + pr_debug(" Type | Mem. Size | File Size | VADDR\n"); + for (int idx = 0; idx < hdr->phnum; ++idx) { + // Get the header. + if (read_elf_program_header(file, hdr, idx, &phdr) == -1) { + pr_err("Failed to read program header at index %d.\n", idx); + return -1; + } + pr_debug(" %-9s | %9s | %9s | 0x%08x - 0x%08x\n", + elf_type_to_string(phdr.type), + to_human_size(phdr.memsz), + to_human_size(phdr.filesz), + phdr.vaddr, phdr.vaddr + phdr.memsz); + if (phdr.type == PT_LOAD) { + uint32_t virt_addr = create_vm_area(task->mm, phdr.vaddr, phdr.memsz, MM_USER | MM_RW | MM_COW, GFP_KERNEL); + virt_map_page_t *vpage = virt_map_alloc(phdr.memsz); + uint32_t dst_addr = virt_map_vaddress(task->mm, vpage, virt_addr, phdr.memsz); + + // Load the memory area. + vfs_read(file, (void *)dst_addr, phdr.offset, phdr.filesz); + + if (phdr.memsz > phdr.filesz) { + uint32_t zmem_sz = phdr.memsz - phdr.filesz; + memset((void *)(dst_addr + phdr.filesz), 0, zmem_sz); + } + virt_unmap_pg(vpage); + } + } + return 0; +} + +static inline void dump_elf_section_headers(vfs_file_t *file, elf_header_t *hdr) +{ + char *strtable = elf_get_strtable(file, hdr, hdr->shstrndx); + if (strtable == NULL) + return; + pr_debug("[Nr] Name Type Addr Off Size ES Flg Lk Inf Al\n"); + elf_section_header_t shdr; + for (int i = 0; i < hdr->shnum; ++i) { + if (read_elf_section_header(file, hdr, i, &shdr) == -1) { + pr_err("Failed to read section header at index %d.\n", i); + } + pr_debug("[%2d] %-20s %-15s %08x %06x %06x %2u %3u %2u %3u %2u\n", + i, strtable + shdr.name, elf_section_header_type_to_string(shdr.type), + shdr.addr, shdr.offset, shdr.size, + shdr.entsize, shdr.flags, shdr.link, shdr.info, shdr.addralign); + } + kfree(strtable); +} + +static inline void dump_elf_symbol_table(vfs_file_t *file, elf_header_t *hdr) +{ + elf_section_header_t shdr; + if (elf_find_section_header(file, hdr, SHT_SYMTAB, &shdr) == -1) + return; + + char *strtable = elf_get_strtable(file, hdr, shdr.link); + if (strtable == NULL) + return; + + // Count the number of entries. + uint32_t symtab_entries = shdr.size / sizeof(elf_symbol_t); + pr_debug("Symbol table '.symtab' contains %d entries (%d/%d):\n", symtab_entries, shdr.size, sizeof(elf_symbol_t)); + pr_debug("[ Nr ] Value Size Type Bind Vis Ndx Name\n"); + elf_symbol_t symbol; + for (int i = 0; i < symtab_entries; ++i) { + if (read_elf_symbol(file, &shdr, i, &symbol) == -1) { + pr_err("Failed to read the elf symbol at index %d.\n", i); + } + pr_debug("[%4d] %08x %5d %-7s %-6s %-8s %3d %s\n", i, symbol.value, symbol.size, + elf_symbol_type_to_string(ELF32_ST_TYPE(symbol.info)), + elf_symbol_bind_to_string(ELF32_ST_BIND(symbol.info)), + "-", + symbol.ndx, + strtable + symbol.name); + } + kfree(strtable); +} + +int elf_load_file(task_struct *task, vfs_file_t *file, uint32_t *entry) +{ + // Open the file. + if (file == NULL) { + pr_err("Cannot find executable!"); + return 0; + } + elf_header_t hdr; + // Set the reading position at the beginning of the file. + vfs_lseek(file, 0, SEEK_SET); + // Read the header. + if (vfs_read(file, &hdr, 0, sizeof(elf_header_t)) != -1) { + if (elf_check_file_header(&hdr)) { + pr_debug("Version : 0x%x\n", hdr.version); + pr_debug("Entry : 0x%x\n", hdr.entry); + pr_debug("Headers offset : 0x%x\n", hdr.phoff); + pr_debug("Headers count : %d\n", hdr.phnum); + //dump_elf_section_headers(file, &hdr); + //dump_elf_symbol_table(file, &hdr); + if (hdr.type == ET_EXEC) { + if (elf_load_sigreturn(task, file, &hdr) == -1) { + return 0; + } + if (elf_load_exec(task, file, &hdr) == -1) { + return 0; + } + // Set the entry. + (*entry) = hdr.entry; + return 1; + } else { + pr_err("ELF type not supported.\n"); + } + } else { + pr_err("ELF file cannot be loaded.\n"); + } + } else { + pr_err("Filed to read ELF header.\n"); + } + return 0; +} + +int elf_check_file_type(vfs_file_t *file, Elf_Type type) +{ + // Open the file. + if (file == NULL) { + pr_err("Cannot find executable!"); + return 0; + } + // Set the reading position at the beginning of the file. + vfs_lseek(file, 0, SEEK_SET); + // Prepare the elf header. + elf_header_t hdr; + // By default we return failure. + int ret = 0; + // Read the header and check the file type. + if (vfs_read(file, &hdr, 0, sizeof(elf_header_t)) != -1) + if (elf_check_file_header(&hdr)) + ret = hdr.type == type; + // Set the reading position at the beginning of the file. + vfs_lseek(file, 0, SEEK_SET); + return ret; +} + +int elf_check_file_header(elf_header_t *hdr) +{ + if (!elf_check_magic_number(hdr)) { + pr_err("Invalid ELF File.\n"); + return 0; + } + if (hdr->ident[EI_CLASS] != ELFCLASS32) { + pr_err("Unsupported ELF File Class.\n"); + return 0; + } + if (hdr->ident[EI_DATA] != ELFDATA2LSB) { + pr_err("Unsupported ELF File byte order.\n"); + return 0; + } + if (hdr->machine != EM_386) { + pr_err("Unsupported ELF File target.\n"); + return 0; + } + if (hdr->ident[EI_VERSION] != EV_CURRENT) { + pr_err("Unsupported ELF File version.\n"); + return 0; + } + if (hdr->type != ET_EXEC) { + pr_err("Unsupported ELF File type.\n"); + return 0; + } + return 1; +} + +int elf_check_magic_number(elf_header_t *hdr) +{ + if (!hdr) + return 0; + if (hdr->ident[EI_MAG0] != ELFMAG0) { + pr_err("ELF Header EI_MAG0 incorrect.\n"); + return 0; + } + if (hdr->ident[EI_MAG1] != ELFMAG1) { + pr_err("ELF Header EI_MAG1 incorrect.\n"); + return 0; + } + if (hdr->ident[EI_MAG2] != ELFMAG2) { + pr_err("ELF Header EI_MAG2 incorrect.\n"); + return 0; + } + if (hdr->ident[EI_MAG3] != ELFMAG3) { + pr_err("ELF Header EI_MAG3 incorrect.\n"); + return 0; + } + return 1; +} + +const char *elf_type_to_string(int type) +{ + if (type == PT_LOAD) + return "LOAD"; + if (type == PT_DYNAMIC) + return "DYNAMIC"; + if (type == PT_INTERP) + return "INTERP"; + if (type == PT_NOTE) + return "NOTE"; + if (type == PT_SHLIB) + return "SHLIB"; + if (type == PT_PHDR) + return "PHDR"; + if (type == PT_EH_FRAME) + return "EH_FRAME"; + if (type == PT_GNU_STACK) + return "GNU_STACK"; + if (type == PT_GNU_RELRO) + return "GNU_RELRO"; + if (type == PT_LOPROC) + return "LOPROC"; + if (type == PT_HIPROC) + return "HIPROC"; + return "NULL"; +} + +const char *elf_section_header_type_to_string(int type) +{ + if (type == SHT_PROGBITS) + return "PROGBITS"; + if (type == SHT_SYMTAB) + return "SYMTAB"; + if (type == SHT_STRTAB) + return "STRTAB"; + if (type == SHT_RELA) + return "RELA"; + if (type == SHT_NOBITS) + return "NOBITS"; + if (type == SHT_REL) + return "REL"; + return "NULL"; +} + +const char *elf_symbol_type_to_string(int type) +{ + if (type == STT_NOTYPE) + return "NOTYPE"; + if (type == STT_OBJECT) + return "OBJECT"; + if (type == STT_FUNC) + return "FUNC"; + return "-1"; +} + +const char *elf_symbol_bind_to_string(int bind) +{ + if (bind == STB_LOCAL) + return "LOCAL"; + if (bind == STB_GLOBAL) + return "GLOBAL"; + if (bind == STB_WEAK) + return "WEAK"; + return "-1"; +} \ No newline at end of file diff --git a/mentos/src/fs/fcntl.c b/mentos/src/fs/fcntl.c deleted file mode 100644 index a06e84f..0000000 --- a/mentos/src/fs/fcntl.c +++ /dev/null @@ -1,112 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file fcntl.c -/// @brief Implementation of functions fcntl() and open(). -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "vfs.h" -#include "debug.h" -#include "kheap.h" -#include "shell.h" -#include "errno.h" -#include "fcntl.h" -#include "string.h" -#include "kernel.h" - -int sys_open(const char *pathname, int flags, mode_t mode) -{ - // Allocate a variable for the path. - char absolute_path[MAX_PATH_LENGTH]; - - // Copy the path to the working variable. - strcpy(absolute_path, pathname); - - // If the first character is not the '/' then get the absolute path. - if (absolute_path[0] != '/') - { - if (!get_absolute_path(absolute_path)) - { - dbg_print("Cannot get the absolute path.\n"); - return -1; - } - } - - // Search for an unused fd. - for (current_fd = 0; current_fd < MAX_OPEN_FD; ++current_fd) - { - if (fd_list[current_fd].mountpoint_id == -1) - { - break; - } - } - - // Check if there is not fd available. - if (current_fd == MAX_OPEN_FD) - { - //errno = EMFILE; - return -1; - } - - // Get the mountpoint. - mountpoint_t *mp = get_mountpoint(absolute_path); - if (mp == NULL) - { - //errno = ENODEV; - return -1; - } - - // Check if the function is implemented. - if (mp->operations.open_f == NULL) - { - //errno = ENOSYS; - // Reset the file descriptor. - close(current_fd); - return -1; - } - - int32_t fs_spec_id = mp->operations.open_f(absolute_path, flags, mode); - if (fs_spec_id == -1) - { - // Reset the file descriptor. - close(current_fd); - return -1; - } - - // Set the file descriptor id. - fd_list[current_fd].fs_spec_id = fs_spec_id; - - // Set the mount point id. - fd_list[current_fd].mountpoint_id = mp->mp_id; - - // Reset the offset. - fd_list[current_fd].offset = 0; - - // Set the flags. - fd_list[current_fd].flags_mask = flags; - - // Return the file descriptor and increment it. - return (current_fd++); -} - -int remove(const char *pathname) -{ - char absolute_path[MAX_PATH_LENGTH]; - strcpy(absolute_path, pathname); - if (pathname[0] != '/') - { - get_absolute_path(absolute_path); - } - - int32_t mp_id = get_mountpoint_id(absolute_path); - if (mp_id < 0) - { - return -1; - } - - if (mountpoint_list[mp_id].operations.remove_f == NULL) - { - return -1; - } - - return mountpoint_list[mp_id].operations.remove_f(absolute_path); -} diff --git a/mentos/src/fs/initrd.c b/mentos/src/fs/initrd.c index 9ac4fe4..3b6848d 100644 --- a/mentos/src/fs/initrd.c +++ b/mentos/src/fs/initrd.c @@ -1,455 +1,681 @@ /// MentOS, The Mentoring Operating system project /// @file initrd.c /// @brief Headers of functions for initrd filesystem. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "stdio.h" -#include "vfs.h" -#include "errno.h" -#include "debug.h" -#include "shell.h" -#include "kheap.h" +#include "assert.h" +#include "system/syscall.h" +#include "sys/module.h" +#include "system/panic.h" +#include "fs/vfs.h" +#include "sys/errno.h" +#include "misc/debug.h" +#include "mem/kheap.h" #include "fcntl.h" -#include "libgen.h" -#include "bitops.h" -#include "initrd.h" +#include "sys/bitops.h" +#include "fs/initrd.h" #include "string.h" +#include "stdio.h" +#include "libgen.h" +#include "fcntl.h" -char *module_start[MAX_MODULES]; +/// Maximum length of name in INITRD. +#define INITRD_NAME_MAX 255U +/// Maximum number of files in INITRD. +#define INITRD_MAX_FILES 128U +/// Maximum size of files in INITRD. +#define INITRD_MAX_FS_SIZE 1048576 -initrd_t *fs_specs; -initrd_file_t *fs_headers; -unsigned int fs_end; +/// @brief Information concerning a file. +typedef struct initrd_file_t { + /// Number used as delimiter, it must be set to 0xBF. + int magic; + /// The inode of the file. + unsigned int inode; + /// The name of the file. + char fileName[INITRD_NAME_MAX]; + /// The type of the file. + short int file_type; + /// The permissions mask. + unsigned int mask; + /// The id of the owner. + unsigned int uid; + /// The id of the group. + unsigned int gid; + /// Time of last access. + unsigned int atime; + /// Time of last data modification. + unsigned int mtime; + /// Time of last status change. + unsigned int ctime; + /// Offset of the starting address. + unsigned int offset; + /// Dimension of the file. + unsigned int length; +} __attribute__((aligned(16))) initrd_file_t; -/// The list of file descriptors. -initrd_fd ird_descriptors[MAX_INITRD_DESCRIPTORS]; +/// @brief The details regarding the filesystem. +/// @brief Contains the number of files inside the initrd filesystem. +static struct initrd_t { + /// Number of files. + unsigned int nfiles; + /// List of headers. + initrd_file_t headers[INITRD_MAX_FILES]; +} fs_specs __attribute__((aligned(16))); -/// The currently opened file descriptor. -unsigned int cur_irdfd; +static vfs_file_t *initrd_create_file_struct( + ino_t ino, + const char *name, + size_t size, + int flags); -static inline initrd_file_t *get_initrd_file(const char *path) +/// @brief Searches for the file at the given path. +/// @param path The path where to search the file. +/// @return The file if found, NULL otherwise. +static inline initrd_file_t *initrd_find_file(const char *path) { - for (uint32_t i = 0; i < MAX_FILES; ++i) { - // Discard the headers which has a different name. - if (strcmp(path, fs_headers[i].fileName) == 0) { - return &(fs_headers[i]); - } - } - - return NULL; + for (unsigned int i = 0; i < INITRD_MAX_FILES; ++i) + if (strcmp(path, fs_specs.headers[i].fileName) == 0) + return &(fs_specs.headers[i]); + return NULL; } -static inline size_t get_free_header(initrd_file_t **free_header) +/// @brief Searches for the file at the given path. +/// @param path The path where to search the file. +/// @return The file if found, NULL otherwise. +static inline ino_t initrd_find_inode(const char *path) { - for (size_t i = 0; i < MAX_FILES; ++i) { - // TODO: If the header has type 0, then I suppose it is not used. - if (fs_headers[i].file_type == 0) { - (*free_header) = &(fs_headers[i]); - return i; - } - } - - return 0; + for (unsigned int i = 0; i < INITRD_MAX_FILES; ++i) + if (strcmp(path, fs_specs.headers[i].fileName) == 0) + return fs_specs.headers[i].inode; + return -1; } -uint32_t initfs_init() +static inline initrd_file_t *get_free_header() { - fs_end = 0; - fs_specs = (initrd_t *)module_start[0]; - fs_headers = (initrd_file_t *)(module_start[0] + sizeof(initrd_t)); - - for (int i = 0; i < MAX_INITRD_DESCRIPTORS; ++i) { - ird_descriptors[i].file_descriptor = -1; - ird_descriptors[i].cur_pos = 0; - } - cur_irdfd = 0; - printf(" * Number of Files: %d\n", fs_specs->nfiles); - fs_end = fs_headers[(fs_specs->nfiles) - 1].offset + - fs_headers[(fs_specs->nfiles) - 1].length; - printf(" * Filesystem end : %d\n", fs_end); - //dump_initrd_fs(); - - return fs_specs->nfiles; + for (size_t i = 0; i < INITRD_MAX_FILES; ++i) + if (fs_specs.headers[i].file_type == 0) + return &(fs_specs.headers[i]); + return NULL; } -DIR *initfs_opendir(const char *path) +static inline bool_t check_if_occupied(size_t offset) { - initrd_file_t *direntry = get_initrd_file(path); - - if ((direntry == NULL) && (strcmp(path, "/") != 0)) { - dbg_print("Cannot find '%s'\n", path); - - return NULL; - } - - DIR *pdir = kmalloc(sizeof(DIR)); - pdir->handle = -1; - pdir->cur_entry = 0; - strcpy(pdir->path, path); - - return pdir; + for (size_t i = 0; i < INITRD_MAX_FILES; ++i) { + initrd_file_t *h = &fs_specs.headers[i]; + if ((h->file_type != 0) && (offset >= h->offset) && (offset <= (h->offset + h->length))) { + return true; + } + } + return false; } -int initfs_closedir(DIR *dirp) +static inline int get_free_slot_offset() { - if (dirp != NULL) { - kfree(dirp); - } - - return 0; + int offset = sizeof(struct initrd_t); + for (size_t i = 0; i < INITRD_MAX_FILES; ++i) { + initrd_file_t *h = &fs_specs.headers[i]; + if ((h->file_type != 0) && (offset >= h->offset) && (offset <= (h->offset + h->length))) { + offset = (int)(h->offset + h->length); + continue; + } + return offset; + } + return -1; } -dirent_t *initrd_readdir(DIR *dirp) +// TODO: doxygen comment. +static void dump_initrd_fs(void) { - if (dirp->cur_entry >= MAX_FILES) { - return NULL; - } - - for (; dirp->cur_entry < MAX_FILES; ++dirp->cur_entry) { - initrd_file_t *entry = &fs_headers[dirp->cur_entry]; - if (entry->fileName[0] == '\0') { - continue; - } - // Get the directory of the file. - char *filedir = dirname(entry->fileName); - - // Check if directory path and file directory are the same, or if - // the directory is the root and the file directory is dot. - if (strcmp(dirp->path, filedir) == 0) { - dirp->entry.d_ino = dirp->cur_entry; - dirp->entry.d_type = entry->file_type; - strcpy(dirp->entry.d_name, entry->fileName); - ++dirp->cur_entry; - - return &(dirp->entry); - } - } - - return NULL; + for (size_t i = 0; i < INITRD_MAX_FILES; ++i) { + initrd_file_t *file = &fs_specs.headers[i]; + pr_debug("[%3d][%c%c%c%c%c%c%c%c%c%c] %s\n", + i, + dt_char_array[file->file_type], + (file->mask & S_IRUSR) != 0 ? 'r' : '-', + (file->mask & S_IWUSR) != 0 ? 'w' : '-', + (file->mask & S_IXUSR) != 0 ? 'x' : '-', + (file->mask & S_IRGRP) != 0 ? 'r' : '-', + (file->mask & S_IWGRP) != 0 ? 'w' : '-', + (file->mask & S_IXGRP) != 0 ? 'x' : '-', + (file->mask & S_IROTH) != 0 ? 'r' : '-', + (file->mask & S_IWOTH) != 0 ? 'w' : '-', + (file->mask & S_IXOTH) != 0 ? 'x' : '-', + file->fileName); + } } -int initrd_mkdir(const char *path, mode_t mode) +/// @brief Reads contents of the directories to a dirent buffer, updating +/// the offset and returning the number of written bytes in the buffer, +/// it assumes that all paths are well-formed. +/// @param file The directory handler. +/// @param dirp The buffer where the data should be written. +/// @param doff The offset inside the buffer where the data should be written. +/// @param count The maximum length of the buffer. +/// @return The number of written bytes in the buffer. +static int initrd_getdents(vfs_file_t *file, dirent_t *dirp, off_t doff, size_t count) { - (void)mode; - initrd_file_t *direntry = NULL; + if (file->ino >= INITRD_MAX_FILES) { + return -1; + } + memset(dirp, 0, count); - // Check if the directory already exists. - direntry = get_initrd_file(path); - if (direntry != NULL) { - printf("initrd_mkdir: cannot create directory '%s': " - "File exists\n\n", - path); - return -1; - } - // Check if the directories before it exist. - if ((strcmp(dirname(path), ".") != 0) && - (strcmp(dirname(path), "/") != 0)) { - dbg_print("initrd_mkdir: %s\n", dirname(path)); - direntry = get_initrd_file(dirname(path)); - if (direntry == NULL) { - printf("initrd_mkdir: cannot create directory '%s': " - "No such file or directory\n\n", - path); + initrd_file_t *tdir = &fs_specs.headers[file->ino]; + int len = strlen(tdir->fileName); + size_t written = 0; + off_t current = 0; - return -1; - } - if ((direntry->file_type != FS_DIRECTORY) && - (direntry->file_type != FS_MOUNTPOINT)) { - printf("initrd_mkdir: cannot create directory '%s': " - "Not a directory\n\n", - path); - - return -1; - } - } - // Get a free header. - initrd_file_t *free_header = NULL; - get_free_header(&free_header); - if (free_header == NULL) { - printf("initrd_mkdir: cannot create directory '%s': " - "Maximum number of headers reached\n\n", - path); - - return -1; - } - // Create the directory. - free_header->magic = 0xBF; - strcpy(free_header->fileName, path); - free_header->file_type = FS_DIRECTORY; - free_header->uid = current_user.uid; - free_header->offset = ++fs_end; - free_header->length = 0; - // Increase the number of files. - ++fs_specs->nfiles; - - return 1; + char *parent = NULL; + for (off_t it = 0; (it < INITRD_MAX_FILES) && (written < count); ++it) { + initrd_file_t *entry = &fs_specs.headers[it]; + if (entry->fileName[0] == '\0') { + continue; + } + // If the entry is the directory itself, skip. + if (strcmp(tdir->fileName, entry->fileName) == 0) { + continue; + } + // Get the parent directory. + parent = dirname(entry->fileName); + // Check if the entry is inside the directory. + if (strcmp(tdir->fileName, parent) != 0) { + continue; + } + // Skip if already provided. + if (current++ < doff) { + continue; + } + if (*(entry->fileName + len) == '/') + ++len; + // Write on current dirp. + dirp->d_ino = it; + dirp->d_type = entry->file_type; + strcpy(dirp->d_name, entry->fileName + len); + dirp->d_off = sizeof(dirent_t); + dirp->d_reclen = sizeof(dirent_t); + // Increment the written counter. + written += sizeof(dirent_t); + // Move to next writing position. + dirp += 1; + } + return written; } -int initrd_rmdir(const char *path) +/// @brief Creates a new directory. +/// @param path The path to the new directory. +/// @param mode The file mode. +/// @return 0 if success. +static int initrd_mkdir(const char *path, mode_t mode) { - initrd_file_t *direntry = NULL; - - // Check if the directory exists. - direntry = get_initrd_file(path); - if (direntry == NULL) { - //errno = ENOENT; - return -1; - } - - if ((direntry->file_type != FS_DIRECTORY)) { - //errno = ENOTDIR; - return -1; - } - - for (int i = 0; i < MAX_FILES; ++i) { - initrd_file_t *entry = &fs_headers[i]; - if (entry->fileName[0] == '\0') { - continue; - } - // Get the directory of the file. - char *filedir = dirname(entry->fileName); - // Check if directory path and file directory are the same. - if (strcmp(direntry->fileName, filedir) == 0) { - //errno = ENOTEMPTY; - return -1; - } - } - // Remove the directory. - direntry->magic = 0; - memset(direntry->fileName, 0, MAX_FILENAME_LENGTH); - direntry->file_type = 0; - direntry->uid = 0; - direntry->offset = 0; - direntry->length = 0; - // Decrease the number of files. - --fs_specs->nfiles; - - return 0; + if ((strcmp(path, ".") == 0) || (strcmp(path, "..") == 0)) { + return -EPERM; + } + initrd_file_t *direntry = initrd_find_file(path); + if (direntry != NULL) { + return -EEXIST; + } + // Check if the directories before it exist. + char *parent = dirname(path); + if ((strcmp(parent, ".") != 0) && (strcmp(parent, "/") != 0)) { + direntry = initrd_find_file(parent); + if (direntry == NULL) { + return -ENOENT; + } + if (direntry->file_type != DT_DIR) { + return -ENOTDIR; + } + } + // Get a free header. + initrd_file_t *initrd_file = get_free_header(); + if (!initrd_file) { + pr_err("Cannot create initrd_file for `%s`...\n", path); + return -ENFILE; + } + int offset = get_free_slot_offset(); + if (offset < 0) { + pr_err("There are no free slot available for `%s`...\n", path); + return -ENFILE; + } + // Create the file. + initrd_file->magic = 0xBF; + strcpy(initrd_file->fileName, path); + initrd_file->file_type = DT_DIR; + initrd_file->uid = scheduler_get_current_process()->uid; + initrd_file->gid = scheduler_get_current_process()->uid; + initrd_file->offset = offset; + initrd_file->length = 0; + initrd_file->atime = sys_time(NULL); + initrd_file->mtime = sys_time(NULL); + initrd_file->ctime = sys_time(NULL); + // Increase the number of files. + ++fs_specs.nfiles; + return 0; } -int initfs_open(const char *path, int flags, ...) +/// @brief Removes a directory. +/// @param path The path to the directory. +/// @return 0 if success. +static int initrd_rmdir(const char *path) { - // If we have reached the maximum number of descriptors, just try to find - // a non-used one. - if (cur_irdfd >= MAX_INITRD_DESCRIPTORS) { - // Reset the current ird file descriptor. - cur_irdfd = 0; - while ((ird_descriptors[cur_irdfd].file_descriptor != -1) && - (cur_irdfd < MAX_INITRD_DESCRIPTORS)) { - ++cur_irdfd; - } - } - - for (uint32_t it = 0; it < fs_specs->nfiles; ++it) { - // Discard the headers which has a different name. - if (strcmp(path, fs_headers[it].fileName) != 0) { - continue; - } - - // However, if the name is the same, but the file type is different, - // stop the function and return failure value. - if ((fs_headers[it].file_type == FS_DIRECTORY) || - (fs_headers[it].file_type == FS_MOUNTPOINT)) { - //errno = EISDIR; - return -1; - } - - if (has_flag(flags, O_CREAT)) { - //errno = EEXIST; - return -1; - } - - // Otherwise, if the file is correct, update - ird_descriptors[cur_irdfd].file_descriptor = it; - ird_descriptors[cur_irdfd].cur_pos = 0; - if (has_flag(flags, O_APPEND)) { - ird_descriptors[cur_irdfd].cur_pos = fs_headers[it].length; - } - - return cur_irdfd++; - } - if (has_flag(flags, O_CREAT)) { - // Check if the directories before it exist. - if ((strcmp(dirname(path), ".") != 0) && - (strcmp(dirname(path), "/") != 0)) { - initrd_file_t *direntry = get_initrd_file(dirname(path)); - if (direntry == NULL) { - //errno = ENOENT; - return -1; - } - } - // Get a free header. - initrd_file_t *free_header = NULL; - size_t fd = get_free_header(&free_header); - if (free_header == NULL) { - //errno = ENFILE; - return -1; - } - // Create the file. - free_header->magic = 0xBF; - strcpy(free_header->fileName, path); - free_header->file_type = FS_FILE; - free_header->uid = current_user.uid; - free_header->offset = ++fs_end; - free_header->length = 0; - // Set the descriptor. - ird_descriptors[cur_irdfd].file_descriptor = fd; - ird_descriptors[cur_irdfd].cur_pos = 0; - // Increase the number of files. - ++fs_specs->nfiles; - - return cur_irdfd++; - } - - //errno = ENOENT; - return -1; + if ((strcmp(path, ".") == 0) || (strcmp(path, "..") == 0)) { + pr_err("initrd_rmdir(%s): Cannot remove `.` or `..`.\n", path); + return -EPERM; + } + // Check if the directory exists. + initrd_file_t *direntry = initrd_find_file(path); + if (direntry == NULL) { + pr_err("initrd_rmdir(%s): Cannot find the directory.\n", path); + return -ENOENT; + } + // Check the type. + if (direntry->file_type != DT_DIR) { + pr_err("initrd_rmdir(%s): The entry is not a directory.\n", path); + return -ENOTDIR; + } + for (int i = 0; i < INITRD_MAX_FILES; ++i) { + initrd_file_t *entry = &fs_specs.headers[i]; + if (entry->fileName[0] == '\0') { + continue; + } + // Get the directory of the file. + char *filedir = dirname(entry->fileName); + // Check if directory path and file directory are the same. + if (strcmp(direntry->fileName, filedir) == 0) { + pr_err("initrd_rmdir(%s): The directory is not empty.\n", path); + return -ENOTEMPTY; + } + } + // Remove the directory. + direntry->magic = 0; + memset(direntry->fileName, 0, NAME_MAX); + direntry->file_type = 0; + direntry->uid = 0; + direntry->offset = 0; + direntry->length = 0; + // Decrease the number of files. + --fs_specs.nfiles; + return 0; } -int initfs_remove(const char *path) +/// @brief Open the file at the given path and returns its file descriptor. +/// @param path The path to the file. +/// @param flags The flags used to determine the behavior of the function. +/// @param mode The mode with which we open the file. +/// @return The file descriptor of the opened file, otherwise returns -1. +static vfs_file_t *initrd_open(const char *path, int flags, mode_t mode) { - initrd_file_t *file = get_initrd_file(path); - - if (file == NULL) { - return -1; - } - - if (file->file_type != FS_FILE) { - return -1; - } - - // Remove the directory. - file->magic = 0; - memset(file->fileName, 0, MAX_FILENAME_LENGTH); - file->file_type = 0; - file->uid = 0; - file->offset = 0; - file->length = 0; - // Decrease the number of files. - --fs_specs->nfiles; - - return 0; + initrd_file_t *initrd_file = initrd_find_file(path); + if (initrd_file != NULL) { + // Check if it is a directory. + if (flags == (O_RDONLY | O_DIRECTORY)) { + if (initrd_file->file_type != DT_DIR) { + pr_err("Is not a directory `%s`...\n", path); + errno = ENOTDIR; + return NULL; + } + // Create the file structure. + vfs_file_t *vfs_file = initrd_create_file_struct( + initrd_file->inode, + initrd_file->fileName, + initrd_file->length, + DT_DIR); + if (!vfs_file) { + pr_err("Cannot create vfs file for opening directory `%s`...\n", path); + errno = ENOMEM; + return NULL; + } + // Update file access. + initrd_file->atime = sys_time(NULL); + return vfs_file; + } else if (initrd_file->file_type == DT_DIR) { + pr_err("Is a directory `%s`...\n", path); + errno = EISDIR; + return NULL; + } + // Check if the open has to create. + if (flags & O_CREAT) { + pr_err("Cannot create, it exists `%s`...\n", path); + errno = EEXIST; + return NULL; + } + // Create the file structure. + vfs_file_t *vfs_file = initrd_create_file_struct( + initrd_file->inode, + initrd_file->fileName, + initrd_file->length, + DT_REG); + if (!vfs_file) { + pr_err("Cannot create vfs file for opening file `%s`...\n", path); + errno = ENOMEM; + return NULL; + } + // Update file access. + initrd_file->atime = sys_time(NULL); + return vfs_file; + } + if (flags & O_CREAT) { + // Check if the parent directory exists. + char *dir = dirname(path); + if ((strcmp(dir, ".") != 0) && (strcmp(dir, "/") != 0)) { + if (initrd_find_file(dir) == NULL) { + errno = ENOENT; + return NULL; + } + } + // Get a free header. + initrd_file = get_free_header(); + if (!initrd_file) { + pr_err("Cannot create initrd_file for `%s`...\n", path); + errno = ENFILE; + return NULL; + } + int offset = get_free_slot_offset(); + if (offset < 0) { + pr_err("There are no free slot available for `%s`...\n", path); + errno = ENFILE; + return NULL; + } + // Create the file. + initrd_file->magic = 0xBF; + strcpy(initrd_file->fileName, path); + initrd_file->file_type = DT_REG; + initrd_file->mask = S_IRWXU; + initrd_file->uid = scheduler_get_current_process()->uid; + initrd_file->gid = scheduler_get_current_process()->uid; + initrd_file->offset = offset; + initrd_file->length = 0; + initrd_file->atime = sys_time(NULL); + initrd_file->mtime = sys_time(NULL); + initrd_file->ctime = sys_time(NULL); + // Increase the number of files. + ++fs_specs.nfiles; + // Create the file structure. + vfs_file_t *vfs_file = initrd_create_file_struct( + initrd_file->inode, + initrd_file->fileName, + initrd_file->length, + DT_REG); + if (!vfs_file) { + pr_err("Cannot create vfs file for opening file `%s`...\n", path); + errno = ENOMEM; + return NULL; + } + return vfs_file; + } + errno = ENOENT; + return NULL; } -ssize_t initfs_read(int fildes, char *buf, size_t nbyte) +/// @brief Deletes the file at the given path. +/// @param path The path to the file. +/// @return On success, zero is returned. On error, -1 is returned. +static int initrd_unlink(const char *path) { - // If the number of byte to read is zero, skip. - if (nbyte == 0) { - return 0; - } - - // Get the file descriptor of the file. - int lfd = ird_descriptors[fildes].file_descriptor; - - // Get the current position. - int read_pos = ird_descriptors[fildes].cur_pos; - - // Get the legnth of the file. - int file_size = fs_headers[lfd].length; - - // Get the begin of the file. - char *file_start = (module_start[0] + fs_headers[lfd].offset); - - // Declare an iterator. - size_t it = 0; - - while ((it < nbyte) && (read_pos < file_size)) { - *buf++ = file_start[read_pos]; - ++read_pos; - ++it; - } - - ird_descriptors[fildes].cur_pos = read_pos; - if (read_pos == file_size) { - return EOF; - } - - return nbyte; + if ((strcmp(path, ".") == 0) || (strcmp(path, "..") == 0)) { + return -EPERM; + } + // Check if the directory exists. + initrd_file_t *file = initrd_find_file(path); + if (file == NULL) { + pr_err("initrd_unlink(%s): Cannot find the file.\n", path); + return -ENOENT; + } + if (file->file_type != DT_REG) { + if (file->file_type == DT_DIR) { + pr_err("initrd_unlink(%s): The file is a directory.\n", path); + return -EISDIR; + } + pr_err("initrd_unlink(%s): The file is not a regular file.\n", path); + return -EACCES; + } + // Remove the directory. + file->magic = 0; + memset(file->fileName, 0, NAME_MAX); + file->file_type = 0; + file->uid = 0; + file->offset = 0; + file->length = 0; + // Decrease the number of files. + --fs_specs.nfiles; + return 0; } -int initrd_stat(const char *path, stat_t *stat) +/// @brief Reads from the file identified by the file descriptor. +/// @param file The file. +/// @param buf Buffer where the read content must be placed. +/// @param offset Offset from which we start reading from the file. +/// @param nbyte The number of bytes to read. +/// @return The number of red bytes. +static ssize_t initrd_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyte) { - int i; - i = 0; - - while (i < MAX_FILES) { - if (!strcmp(path, fs_headers[i].fileName)) { - stat->st_uid = fs_headers[i].uid; - stat->st_size = fs_headers[i].length; - - break; - } - i++; - } - //dbg_print("Initrd stat function\n"); - //buf->st_uid = 33; - if (i == MAX_FILES) { - return -1; - } else { - return 0; - } + // If the number of byte to read is zero, skip. + if (nbyte == 0) { + return 0; + } + // Get the file descriptor of the file. + int lfd = file->ino; + // Get the current position. + int read_pos = offset; + // Get the length of the file. + int file_size = (int)fs_specs.headers[lfd].length; + // If we have reached the end of the file, return. + if (read_pos == file_size) { + return EOF; + } + // Get the begin of the file. + char *file_start = (char *)(modules[0].mod_start + fs_specs.headers[lfd].offset); + // Declare an iterator, used afterward to return the number of bytes read. + ssize_t it = 0; + while ((it < nbyte) && (read_pos < file_size)) { + *buf++ = file_start[read_pos]; + ++read_pos; + ++it; + } + return it; } -ssize_t initrd_write(int fildes, const void *buf, size_t nbyte) +static int _initrd_stat(const initrd_file_t *file, stat_t *stat) { - // If the number of byte to write is zero, skip. - if (nbyte == 0) { - return 0; - } - - // Make a copy of the buffer. - char *tmp = (char *)kmalloc(strlen(buf) * sizeof(char)); - strcpy(tmp, buf); - - // Get the file descriptor of the file. - int lfd = ird_descriptors[fildes].file_descriptor; - - printf("Please wait, im writing the world...\n"); - printf("And the world begun with those words: %s and his mark his: %d\n", - tmp, lfd); - // Get the begin of the file. - char *file_start = (module_start[0] + fs_headers[lfd].offset + - ird_descriptors[fildes].cur_pos); - // Declare an iterator. - size_t it = 0; - while (it <= nbyte) { - file_start[it] = tmp[it]; - ++it; - } - - // Increment the length of the file. - fs_headers[lfd].length = fs_headers[lfd].length + it; - // Free the memory of the temporary file. - kfree(tmp); - // Return the number of written bytes. - - return it; + stat->st_dev = 0; + stat->st_ino = file - fs_specs.headers; + stat->st_mode = file->mask; + stat->st_uid = file->uid; + stat->st_gid = file->gid; + stat->st_atime = file->atime; + stat->st_mtime = file->mtime; + stat->st_ctime = file->ctime; + stat->st_size = file->length; + return 0; } -int initrd_close(int fildes) +/// @brief Retrieves information concerning the file at the given position. +/// @param file The file struct. +/// @param stat The structure where the information are stored. +/// @return 0 if success. +static int initrd_fstat(vfs_file_t *file, stat_t *stat) { - ird_descriptors[fildes].file_descriptor = -1; - ird_descriptors[fildes].cur_pos = 0; - - return 0; + return _initrd_stat(&fs_specs.headers[file->ino], stat); } -size_t initrd_nfiles() +/// @brief Retrieves information concerning the file at the given position. +/// @param path The path where the file resides. +/// @param stat The structure where the information are stored. +/// @return 0 if success. +static int initrd_stat(const char *path, stat_t *stat) { - size_t nfiles = 0; + int i; + i = 0; - for (size_t i = 0; i < MAX_FILES; ++i) { - if (fs_headers[i].file_type != 0) { - ++nfiles; - } - } + while (i < INITRD_MAX_FILES) { + if (!strcmp(path, fs_specs.headers[i].fileName)) { + stat->st_uid = fs_specs.headers[i].uid; + stat->st_size = fs_specs.headers[i].length; + break; + } + i++; + } - return nfiles; + if (i == INITRD_MAX_FILES) { + return -ENOENT; + } else { + return _initrd_stat(&fs_specs.headers[i], stat); + } } -void dump_initrd_fs() +/// @brief Writes the given content inside the file. +/// @param file The file descriptor of the file. +/// @param buf The content to write. +/// @param offset Offset from which we start writing in the file. +/// @param nbyte The number of bytes to write. +/// @return The number of written bytes. +static ssize_t initrd_write(vfs_file_t *file, const void *buf, off_t offset, size_t nbyte) { - for (uint32_t i = 0; i < MAX_FILES; ++i) { - dbg_print("[%2d] %s\n", i, fs_headers[i].fileName); - } + // Get the header. + initrd_file_t *header = &fs_specs.headers[file->ino]; + // If the number of byte to write is zero, skip. + if (nbyte == 0) { + return 0; + } + if (check_if_occupied(offset + nbyte)) { + pr_emerg("We need to move the file.\n"); + TODO("Implement file movement."); + } + // Prepare pointers to the contents. + char *dest = (char *)(&fs_specs + header->offset + offset), *src = (char *)buf; + // Copy the content. + int num = 0; + while ((num < nbyte) && (*dest++ = *src++)) { + ++num; + } + dest[num] = '\0'; + dest[num + 1] = EOF; + // Increment the length of the file. + header->length += num; + return num; } + +static off_t initrd_lseek(vfs_file_t *file, off_t offset, int whence) +{ + // Get the header. + initrd_file_t *header = &fs_specs.headers[file->ino]; + + switch (whence) { + case SEEK_END: + offset += header->length; + break; + case SEEK_CUR: + if (offset == 0) { + return file->f_pos; + } + offset += file->f_pos; + break; + case SEEK_SET: + break; + default: + return -EINVAL; + } + if (offset >= 0) { + if (offset != file->f_pos) { + file->f_pos = offset; + } + return offset; + } + return -EINVAL; +} + +/// @brief Closes the given file. +/// @param file The file structure. +static int initrd_close(vfs_file_t *file) +{ + assert(file && "Received null file."); + // Remove the file from the list of the corresponding entry inside the `header_files`. + list_head_del(&file->siblings); + // Free the memory of the file. + kmem_cache_free(file); + return 0; +} + +/// Filesystem general operations. +static vfs_sys_operations_t initrd_sys_operations = { + .mkdir_f = initrd_mkdir, + .rmdir_f = initrd_rmdir, + .stat_f = initrd_stat +}; + +/// Filesystem file operations. +static vfs_file_operations_t initrd_fs_operations = { + .open_f = initrd_open, + .unlink_f = initrd_unlink, + .close_f = initrd_close, + .read_f = initrd_read, + .write_f = initrd_write, + .lseek_f = initrd_lseek, + .stat_f = initrd_fstat, + .ioctl_f = NULL, + .getdents_f = initrd_getdents +}; + +static vfs_file_t *initrd_create_file_struct( + ino_t ino, + const char *name, + size_t size, + int flags) +{ + vfs_file_t *file = kmem_cache_alloc(vfs_file_cache, GFP_KERNEL); + if (!file) { + pr_err("Failed to allocation memory for the file."); + return NULL; + } + memset(file, 0, sizeof(vfs_file_t)); + + strcpy(file->name, name); + file->device = (void *)file; + file->ino = ino; + file->uid = 0; + file->gid = 0; + file->mask = S_IRUSR | S_IRGRP | S_IROTH; + file->length = size; + file->flags = flags; + file->sys_operations = &initrd_sys_operations; + file->fs_operations = &initrd_fs_operations; + + return file; +} + +static vfs_file_t *initrd_mount_callback(const char *path, const char *device) +{ + dump_initrd_fs(); + // Create the associated file. + vfs_file_t *vfs_file = initrd_create_file_struct(0, path, 0, DT_DIR); + assert(vfs_file && "Failed to create vfs_file."); + // Initialize the proc_root. + return vfs_file; +} + +/// Filesystem information. +static file_system_type initrd_file_system_type = { + .name = "initrd", + .fs_flags = 0, + .mount = initrd_mount_callback +}; + +int initrd_init_module(void) +{ + for (int i = 0; i < MAX_MODULES; ++i) { + if (strcmp((char *)modules[i].cmdline, "initrd") == 0) { + assert(sizeof(struct initrd_t) <= (modules[i].mod_end - modules[i].mod_start)); + // Copy the FS specification. + memcpy(&fs_specs, (void *)modules[i].mod_start, sizeof(struct initrd_t)); + // Register the filesystem. + vfs_register_filesystem(&initrd_file_system_type); + } + } + return 0; +} + +int initrd_cleanup_module(void) +{ + vfs_unregister_filesystem(&initrd_file_system_type); + return 0; +} \ No newline at end of file diff --git a/mentos/src/fs/ioctl.c b/mentos/src/fs/ioctl.c new file mode 100644 index 0000000..d2e6b87 --- /dev/null +++ b/mentos/src/fs/ioctl.c @@ -0,0 +1,35 @@ +/// MentOS, The Mentoring Operating system project +/// @file ioctl.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "fs/ioctl.h" +#include "process/scheduler.h" +#include "system/printk.h" +#include "stdio.h" +#include "sys/errno.h" +#include "fs/vfs.h" + +int sys_ioctl(int fd, int request, void *data) +{ + // Get the current task. + task_struct *task = scheduler_get_current_process(); + + // Check the current FD. + if (fd < 0 || fd >= task->max_fd) { + return -EMFILE; + } + + // Get the file descriptor. + vfs_file_descriptor_t *vfd = &task->fd_list[fd]; + + // Get the file. + vfs_file_t *file = vfd->file_struct; + if (file == NULL) { + return -ENOSYS; + } + + // Perform the ioctl. + return vfs_ioctl(file, request, data); +} \ No newline at end of file diff --git a/mentos/src/fs/namei.c b/mentos/src/fs/namei.c new file mode 100644 index 0000000..8ffb0f9 --- /dev/null +++ b/mentos/src/fs/namei.c @@ -0,0 +1,24 @@ +/// MentOS, The Mentoring Operating system project +/// @file namei.c +/// @brief Implementation of functions fcntl() and open(). +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/errno.h" +#include "fs/vfs.h" +#include "process/scheduler.h" + +int sys_unlink(const char *path) +{ + return vfs_unlink(path); +} + +int sys_mkdir(const char *path, mode_t mode) +{ + return vfs_mkdir(path, mode); +} + +int sys_rmdir(const char *path) +{ + return vfs_rmdir(path); +} diff --git a/mentos/src/fs/open.c b/mentos/src/fs/open.c new file mode 100644 index 0000000..9200609 --- /dev/null +++ b/mentos/src/fs/open.c @@ -0,0 +1,93 @@ +/// MentOS, The Mentoring Operating system project +/// @file open.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "process/scheduler.h" +#include "process/process.h" +#include "system/printk.h" +#include "fcntl.h" +#include "system/syscall.h" +#include "string.h" +#include "limits.h" +#include "misc/debug.h" +#include "sys/errno.h" +#include "stdio.h" +#include "fs/vfs.h" + +int sys_open(const char *pathname, int flags, mode_t mode) +{ + // Get the current task. + task_struct *task = scheduler_get_current_process(); + + // Search for an unused fd. + int fd; + for (fd = 0; fd < task->max_fd; ++fd) { + if (!task->fd_list[fd].file_struct) { + break; + } + } + + // Check if there is not fd available. + if (fd >= MAX_OPEN_FD) { + return -EMFILE; + } + + // If fd limit is reached, try to allocate more + if (fd == task->max_fd) { + if (!vfs_extend_task_fd_list(task)) { + pr_err("Failed to extend the file descriptor list.\n"); + return -EMFILE; + } + } + + // Try to open the file. + vfs_file_t *file = vfs_open(pathname, flags, mode); + if (file == NULL) { + return -errno; + } + + // Set the file descriptor id. + task->fd_list[fd].file_struct = file; + + if (!bitmask_check(flags, O_APPEND)) { + // Reset the offset. + task->fd_list[fd].file_struct->f_pos = 0; + } else { + stat_t stat; + // Stat the file. + file->fs_operations->stat_f(file, &stat); + // Point at the last character + task->fd_list[fd].file_struct->f_pos = stat.st_size; + } + + // Set the flags. + task->fd_list[fd].flags_mask = flags; + + // Return the file descriptor and increment it. + return fd; +} + +int sys_close(int fd) +{ + // Get the current task. + task_struct *task = scheduler_get_current_process(); + + // Check the current FD. + if (fd < 0 || fd >= task->max_fd) { + return -EMFILE; + } + + // Get the file. + vfs_file_t *file = task->fd_list[fd].file_struct; + if (file == NULL) { + return -1; + } + + // Remove the reference to the file. + task->fd_list[fd].file_struct = NULL; + + // Call the close function. + return vfs_close(file); +} diff --git a/mentos/src/fs/procfs.c b/mentos/src/fs/procfs.c new file mode 100644 index 0000000..2de01e6 --- /dev/null +++ b/mentos/src/fs/procfs.c @@ -0,0 +1,779 @@ +/// MentOS, The Mentoring Operating system project +/// @file procfs.c +/// @brief Proc file system implementation. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +/// Change the header. +#define __DEBUG_HEADER__ "[PROCFS]" + +#include "fs/procfs.h" +#include "fs/vfs.h" +#include "string.h" +#include "sys/errno.h" +#include "misc/debug.h" +#include "fcntl.h" +#include "libgen.h" +#include "assert.h" +#include "time.h" + +/// Maximum length of name in PROCFS. +#define PROCFS_NAME_MAX 255U +/// Maximum number of files in PROCFS. +#define PROCFS_MAX_FILES 512U +/// The magic number used to check if the procfs file is valid. +#define PROCFS_MAGIC_NUMBER 0xBF + +/// @brief Information concerning a file. +typedef struct procfs_file_t { + /// Number used as delimiter, it must be set to 0xBF. + int magic; + /// The file inode. + int inode; + /// Flags. + unsigned flags; + /// The name of the file. + char name[PROCFS_NAME_MAX]; + /// Associated files. + list_head files; + /// User id of the file. + uid_t uid; + /// Group id of the file. + gid_t gid; + /// Time of last access. + time_t atime; + /// Time of last data modification. + time_t mtime; + /// Time of last status change. + time_t ctime; + /// Pointer to the associated proc_dir_entry_t. + proc_dir_entry_t dir_entry; +} procfs_file_t; + +/// @brief The details regarding the filesystem. +/// @brief Contains the number of files inside the initrd filesystem. +static struct procfs_t { + /// Number of files. + unsigned int nfiles; + /// List of headers. + procfs_file_t headers[PROCFS_MAX_FILES]; +} __attribute__((aligned(16))) fs_specs; + +static inline procfs_file_t *procfs_create_file(const char *path, unsigned flags); + +static inline int procfs_destroy_file(procfs_file_t *procfs_file); + +static inline vfs_file_t *procfs_create_file_struct(procfs_file_t *procfs_file); + +static inline int procfs_get_free_inode() +{ + for (int i = 0; i < PROCFS_MAX_FILES; ++i) { + assert(fs_specs.headers[i].magic == PROCFS_MAGIC_NUMBER); + if (fs_specs.headers[i].inode == -1) { + return i; + } + } + return -1; +} + +static inline procfs_file_t *procfs_get_free_entry() +{ + int free_inode = procfs_get_free_inode(); + if (free_inode != -1) { + procfs_file_t *free_entry = &fs_specs.headers[free_inode]; + free_entry->inode = free_inode; + return free_entry; + } else { + pr_err("There are no more free inodes (%d/%d).\n", fs_specs.nfiles, PROCFS_MAX_FILES); + } + return NULL; +} + +static inline procfs_file_t *procfs_find_entry_path(const char *path) +{ + for (int i = 0; i < PROCFS_MAX_FILES; ++i) { + if (strcmp(fs_specs.headers[i].name, path) == 0) { + return &fs_specs.headers[i]; + } + } + return NULL; +} + +static inline procfs_file_t *procfs_find_entry_inode(int inode) +{ + for (int i = 0; i < PROCFS_MAX_FILES; ++i) { + if (fs_specs.headers[i].inode == inode) { + return &fs_specs.headers[i]; + } + } + return NULL; +} + +static inline int procfs_find_inode(const char *path) +{ + procfs_file_t *file = procfs_find_entry_path(path); + if (file) + return file->inode; + return -1; +} + +static inline int procfs_check_if_empty(const char *path) +{ + for (int i = 0; i < PROCFS_MAX_FILES; ++i) { + procfs_file_t *entry = &fs_specs.headers[i]; + // There is nothing here. + if (entry->inode == -1) { + continue; + } + // It's the directory itself. + if (strcmp(path, entry->name) == 0) { + continue; + } + // Get the directory of the file. + char *filedir = dirname(entry->name); + // Check if directory path and file directory are the same. + if (strcmp(path, filedir) == 0) { + return 1; + } + } + return 0; +} + +static void dump_procfs() +{ + for (int i = 0; i < PROCFS_MAX_FILES; ++i) { + procfs_file_t *file = &fs_specs.headers[i]; + pr_debug("[%3d]ino:%3d, name:`%s`\n", i, file->inode, file->name); + } +} + +static void procfs_init() +{ + // Initialize the procfs. + memset(&fs_specs, 0, sizeof(struct procfs_t)); + for (int i = 0; i < PROCFS_MAX_FILES; ++i) { + fs_specs.headers[i].magic = PROCFS_MAGIC_NUMBER; + fs_specs.headers[i].inode = -1; + } +} + +/// @brief Creates a new directory. +/// @param path The path to the new directory. +/// @param mode The file mode. +/// @return 0 if success. +static int procfs_mkdir(const char *path, mode_t mode) +{ + if ((strcmp(path, ".") == 0) || (strcmp(path, "..") == 0)) { + pr_err("procfs_mkdir(%s): Cannot create `.` or `..`.\n", path); + return -EPERM; + } + if (procfs_find_entry_path(path) != NULL) { + return -EEXIST; + } + // Check if the directories before it exist. + procfs_file_t *parent_file = NULL; + char *parent = dirname(path); + if ((strcmp(parent, ".") != 0) && (strcmp(parent, "/") != 0)) { + parent_file = procfs_find_entry_path(parent); + if (parent_file == NULL) { + return -ENOENT; + } + if ((parent_file->flags & DT_DIR) == 0) { + return -ENOTDIR; + } + } + // Create the new procfs file. + procfs_file_t *procfs_file = procfs_create_file(path, DT_DIR); + if (!procfs_file) { + pr_err("procfs_mkdir(%s): Cannot create the procfs file.\n", path); + return -ENFILE; + } + return 0; +} + +/// @brief Removes a directory. +/// @param path The path to the directory. +/// @return 0 if success. +static int procfs_rmdir(const char *path) +{ + if ((strcmp(path, ".") == 0) || (strcmp(path, "..") == 0)) { + pr_err("procfs_rmdir(%s): Cannot remove `.` or `..`.\n", path); + return -EPERM; + } + // Get the file. + procfs_file_t *procfs_file = procfs_find_entry_path(path); + if (procfs_file == NULL) { + pr_err("procfs_rmdir(%s): Cannot find the file.\n", path); + return -ENOENT; + } + // Check the type. + if ((procfs_file->flags & DT_DIR) == 0) { + pr_err("procfs_rmdir(%s): The entry is not a directory.\n", path); + return -ENOTDIR; + } + // Check if the directory is currently opened. + if (!list_head_empty(&procfs_file->files)) { + pr_err("procfs_rmdir(%s): The directory is opened by someone.\n", path); + return -EBUSY; + } + // Check if its empty. + if (procfs_check_if_empty(procfs_file->name)) { + pr_err("procfs_rmdir(%s): The directory is not empty.\n", path); + return -ENOTEMPTY; + } + if (procfs_destroy_file(procfs_file)) { + pr_err("procfs_rmdir(%s): Failed to remove directory.\n", path); + } + return 0; +} + +static vfs_file_t *procfs_open(const char *path, int flags, mode_t mode) +{ + procfs_file_t *procfs_file = procfs_find_entry_path(path); + if (procfs_file != NULL) { + // Check if it is a directory. + if (flags == (O_RDONLY | O_DIRECTORY)) { + if ((procfs_file->flags & DT_DIR) == 0) { + pr_err("Is not a directory `%s`...\n", path); + errno = ENOTDIR; + return NULL; + } + // Create the associated file. + vfs_file_t *vfs_file = procfs_create_file_struct(procfs_file); + if (!vfs_file) { + pr_err("Cannot create vfs file for opening directory `%s`...\n", path); + errno = ENFILE; + return NULL; + } + // Update file access. + procfs_file->atime = sys_time(NULL); + // Add the vfs_file to the list of associated files. + list_head_add_tail(&vfs_file->siblings, &procfs_file->files); + return vfs_file; + } else if ((procfs_file->flags & DT_DIR) != 0) { + pr_err("Is a directory `%s`...\n", path); + errno = EISDIR; + return NULL; + } + // Check if the open has to create. + if (flags & O_CREAT) { + pr_err("Cannot create, it exists `%s`...\n", path); + errno = EEXIST; + return NULL; + } + // Create the associated file. + vfs_file_t *vfs_file = procfs_create_file_struct(procfs_file); + if (!vfs_file) { + pr_err("Cannot create vfs file for opening file `%s`...\n", path); + errno = ENFILE; + return NULL; + } + // Update file access. + procfs_file->atime = sys_time(NULL); + // Add the vfs_file to the list of associated files. + list_head_add_tail(&vfs_file->siblings, &procfs_file->files); + return vfs_file; + } + if (flags & O_CREAT) { + // Check if the directories before it exist. + procfs_file_t *parent_file = NULL; + char *parent_path = dirname(path); + if ((strcmp(parent_path, ".") != 0) && (strcmp(parent_path, "/") != 0)) { + parent_file = procfs_find_entry_path(parent_path); + if (parent_file == NULL) { + pr_err("Cannot find parent `%s`...\n", parent_path); + errno = ENOENT; + return NULL; + } + if ((parent_file->flags & DT_DIR) == 0) { + pr_err("Parent `%s` the parent is not a directory...\n", parent_path); + errno = ENOTDIR; + return NULL; + } + } + // Create the new procfs file. + procfs_file = procfs_create_file(path, DT_REG); + if (!procfs_file) { + pr_err("Cannot create procfs_file for `%s`...\n", path); + errno = ENFILE; + return NULL; + } + // Create the associated file. + vfs_file_t *vfs_file = procfs_create_file_struct(procfs_file); + if (!vfs_file) { + pr_err("Cannot create vfs file for opening file `%s`...\n", path); + errno = ENFILE; + return NULL; + } + // Add the vfs_file to the list of associated files. + list_head_add_tail(&vfs_file->siblings, &procfs_file->files); + return vfs_file; + } + errno = ENOENT; + return NULL; +} + +static int procfs_close(vfs_file_t *file) +{ + assert(file && "Received null file."); + //pr_debug("procfs_close(%p): VFS file : %p\n", file, file); + // Remove the file from the list of `files` inside its corresponding `procfs_file_t`. + list_head_del(&file->siblings); + // Free the memory of the file. + kmem_cache_free(file); + return 0; +} + +/// @brief Deletes the file at the given path. +/// @param path The path to the file. +/// @return On success, zero is returned. On error, -1 is returned. +static inline int procfs_unlink(const char *path) +{ + if ((strcmp(path, ".") == 0) || (strcmp(path, "..") == 0)) { + return -EPERM; + } + procfs_file_t *procfs_file = procfs_find_entry_path(path); + if (procfs_file != NULL) { + return -EEXIST; + } + // Check the type. + if ((procfs_file->flags & DT_REG) == 0) { + if ((procfs_file->flags & DT_DIR) != 0) { + pr_err("procfs_unlink(%s): The file is a directory.\n", path); + return -EISDIR; + } + pr_err("procfs_unlink(%s): The file is not a regular file.\n", path); + return -EACCES; + } + // Check if the procfs file has still some file associated. + if (!list_head_empty(&procfs_file->files)) { + pr_err("procfs_unlink(%s): The file is opened by someone.\n", path); + return -EACCES; + } + if (procfs_destroy_file(procfs_file)) { + pr_err("procfs_unlink(%s): Failed to remove file.\n", path); + } + return 0; +} + +static ssize_t procfs_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyte) +{ + if (file) { + procfs_file_t *procfs_file = procfs_find_entry_inode(file->ino); + if (procfs_file && procfs_file->dir_entry.fs_operations) + if (procfs_file->dir_entry.fs_operations->read_f) + return procfs_file->dir_entry.fs_operations->read_f(file, buf, offset, nbyte); + } + return -ENOSYS; +} + +static ssize_t procfs_write(vfs_file_t *file, const void *buf, off_t offset, size_t nbyte) +{ + if (file) { + procfs_file_t *procfs_file = procfs_find_entry_inode(file->ino); + if (procfs_file && procfs_file->dir_entry.fs_operations) + if (procfs_file->dir_entry.fs_operations->write_f) + return procfs_file->dir_entry.fs_operations->write_f(file, buf, offset, nbyte); + } + return -ENOSYS; +} + +static int __procfs_stat(procfs_file_t *file, stat_t *stat) +{ + stat->st_uid = file->uid; + stat->st_gid = file->gid; + stat->st_dev = 0; + stat->st_ino = file->inode; + stat->st_mode = file->flags; + stat->st_size = 0; + stat->st_atime = file->atime; + stat->st_mtime = file->mtime; + stat->st_ctime = file->ctime; + return 0; +} + +/// @brief Retrieves information concerning the file at the given position. +/// @param file The file struct. +/// @param stat The structure where the information are stored. +/// @return 0 if success. +static int procfs_fstat(vfs_file_t *file, stat_t *stat) +{ + if (file && stat) { + procfs_file_t *procfs_file = procfs_find_entry_inode(file->ino); + if (procfs_file) { + if (procfs_file->dir_entry.fs_operations && procfs_file->dir_entry.fs_operations->stat_f) + return procfs_file->dir_entry.fs_operations->stat_f(file, stat); + else + return __procfs_stat(procfs_file, stat); + } + } + return -ENOSYS; +} + +/// @brief Retrieves information concerning the file at the given position. +/// @param path The path to the file. +/// @param stat The structure where the information are stored. +/// @return 0 if success. +static int procfs_stat(const char *path, stat_t *stat) +{ + if (path && stat) { + procfs_file_t *procfs_file = procfs_find_entry_path(path); + if (procfs_file) { + if (procfs_file->dir_entry.sys_operations && procfs_file->dir_entry.sys_operations->stat_f) + return procfs_file->dir_entry.sys_operations->stat_f(path, stat); + else + return __procfs_stat(procfs_file, stat); + } + } + return -1; +} + +static int procfs_ioctl(vfs_file_t *file, int request, void *data) +{ + if (file) { + procfs_file_t *procfs_file = procfs_find_entry_inode(file->ino); + if (procfs_file) + if (procfs_file->dir_entry.fs_operations && procfs_file->dir_entry.fs_operations->ioctl_f) + return procfs_file->dir_entry.fs_operations->ioctl_f(file, request, data); + } + return -1; +} + +/// @brief Reads contents of the directories to a dirent buffer, updating +/// the offset and returning the number of written bytes in the buffer, +/// it assumes that all paths are well-formed. +/// @param file The directory handler. +/// @param dirp The buffer where the data should be written. +/// @param doff The offset inside the buffer where the data should be written. +/// @param count The maximum length of the buffer. +/// @return The number of written bytes in the buffer. +static inline int procfs_getdents(vfs_file_t *file, dirent_t *dirp, off_t doff, size_t count) +{ + if (!file || !dirp) + return -1; + procfs_file_t *direntry = procfs_find_entry_inode(file->ino); + if (direntry == NULL) { + return -ENOENT; + } + if ((direntry->flags & DT_DIR) == 0) + return -ENOTDIR; + memset(dirp, 0, count); + int len = strlen(direntry->name); + size_t written = 0; + off_t current = 0; + char *parent = NULL; + for (off_t it = 0; (it < PROCFS_MAX_FILES) && (written < count); ++it) { + procfs_file_t *entry = &fs_specs.headers[it]; + if (entry->inode == -1) { + continue; + } + // If the entry is the directory itself, skip. + if (strcmp(direntry->name, entry->name) == 0) { + continue; + } + // Get the parent directory. + parent = dirname(entry->name); + // Check if the entry is inside the directory. + if (strcmp(direntry->name, parent) != 0) { + continue; + } + // Skip if already provided. + if (current++ < doff) { + continue; + } + if (*(entry->name + len) == '/') + ++len; + // Write on current dirp. + dirp->d_ino = it; + dirp->d_type = entry->flags; + strcpy(dirp->d_name, entry->name + len); + dirp->d_off = sizeof(dirent_t); + dirp->d_reclen = sizeof(dirent_t); + // Increment the written counter. + written += sizeof(dirent_t); + // Move to next writing position. + dirp += 1; + } + return written; +} + +/// Filesystem general operations. +static vfs_sys_operations_t procfs_sys_operations = { + .mkdir_f = procfs_mkdir, + .rmdir_f = procfs_rmdir, + .stat_f = procfs_stat +}; + +/// Filesystem file operations. +static vfs_file_operations_t procfs_fs_operations = { + .open_f = procfs_open, + .unlink_f = procfs_unlink, + .close_f = procfs_close, + .read_f = procfs_read, + .write_f = procfs_write, + .lseek_f = NULL, + .stat_f = procfs_fstat, + .ioctl_f = procfs_ioctl, + .getdents_f = procfs_getdents +}; + +static inline procfs_file_t *procfs_create_file(const char *path, unsigned flags) +{ + procfs_file_t *file = procfs_get_free_entry(); + if (!file) { + pr_err("Failed to get free entry (%p).\n", file); + return NULL; + } + // Number used as delimiter, it must be set to 0xBF. + assert(file->magic == PROCFS_MAGIC_NUMBER); + // The file inode. + assert(file->inode != -1); + // Flags. + file->flags = flags; + // The name of the file. + strcpy(file->name, path); + // Associated files. + list_head_init(&file->files); + // Time of last access. + file->atime = sys_time(NULL); + // Time of last data modification. + file->mtime = file->atime; + // Time of last status change. + file->ctime = file->atime; + // Initialize the dir_entry. + file->dir_entry.name = basename(file->name); + file->dir_entry.data = NULL; + file->dir_entry.sys_operations = NULL; + file->dir_entry.fs_operations = NULL; + // Increase the number of files. + ++fs_specs.nfiles; + return file; +} + +static inline int procfs_destroy_file(procfs_file_t *procfs_file) +{ + if (!procfs_file) { + pr_err("Received a null entry (%p).\n", procfs_file); + return 1; + } + // Check the number used as delimiter, it must be set to 0xBF. + assert(procfs_file->magic == PROCFS_MAGIC_NUMBER); + + // Reset the inode. + procfs_file->inode = -1; + // Reset the flags. + procfs_file->flags = 0; + // Reset the name of the file. + memset(procfs_file->name, 0, PROCFS_NAME_MAX); + // Reset the list of associated files. + list_head_init(&procfs_file->files); + // Reset the time of last access. + procfs_file->atime = sys_time(NULL); + // Reset the time of last data modification. + procfs_file->mtime = procfs_file->atime; + // Reset the time of last status change. + procfs_file->ctime = procfs_file->atime; + + // Decrease the number of files. + --fs_specs.nfiles; + return 0; +} + +static inline vfs_file_t *procfs_create_file_struct(procfs_file_t *procfs_file) +{ + if (!procfs_file) { + pr_err("procfs_create_file_struct(%p): Procfs file not valid!\n", procfs_file); + return NULL; + } + vfs_file_t *file = kmem_cache_alloc(vfs_file_cache, GFP_KERNEL); + if (!file) { + pr_err("procfs_create_file_struct(%p): Failed to allocate memory for VFS file!\n", procfs_file); + return NULL; + } + memset(file, 0, sizeof(vfs_file_t)); + + strcpy(file->name, procfs_file->name); + file->device = &procfs_file->dir_entry; + file->ino = procfs_file->inode; + file->uid = 0; + file->gid = 0; + file->mask = S_IRUSR | S_IRGRP | S_IROTH; + file->length = 0; + file->flags = procfs_file->flags; + file->sys_operations = &procfs_sys_operations; + file->fs_operations = &procfs_fs_operations; + //pr_debug("procfs_create_file_struct(%p): VFS file : %p\n", procfs_file, file); + return file; +} + +static vfs_file_t *procfs_mount_callback(const char *path, const char *device) +{ + // Create the new procfs file. + procfs_file_t *procfs_file = procfs_create_file(path, DT_DIR); + assert(procfs_file && "Failed to create procfs_file."); + // Create the associated file. + vfs_file_t *vfs_file = procfs_create_file_struct(procfs_file); + assert(vfs_file && "Failed to create vfs_file."); + // Add the vfs_file to the list of associated files. + list_head_add_tail(&vfs_file->siblings, &procfs_file->files); + // Initialize the proc_root. + return vfs_file; +} + +/// Filesystem information. +static file_system_type procfs_file_system_type = { + .name = "procfs", + .fs_flags = 0, + .mount = procfs_mount_callback +}; + +int procfs_module_init() +{ + // Initialize the procfs. + procfs_init(); + // Register the filesystem. + vfs_register_filesystem(&procfs_file_system_type); + return 0; +} + +int procfs_cleanup_module() +{ + // Unregister the filesystem. + vfs_register_filesystem(&procfs_file_system_type); + return 0; +} + +proc_dir_entry_t *proc_dir_entry_get(const char *name, proc_dir_entry_t *parent) +{ + char entry_path[PATH_MAX]; + strcpy(entry_path, "/proc/"); + if (parent) { + strcat(entry_path, parent->name); + strcat(entry_path, "/"); + } + strcat(entry_path, name); + // Get the procfs entry. + procfs_file_t *procfs_file = procfs_find_entry_path(entry_path); + if (procfs_file == NULL) { + pr_err("proc_dir_entry_get(%s): Cannot find proc entry.\n", entry_path); + return NULL; + } + return &procfs_file->dir_entry; +} + +proc_dir_entry_t *proc_mkdir(const char *name, proc_dir_entry_t *parent) +{ + char entry_path[PATH_MAX]; + strcpy(entry_path, "/proc/"); + if (parent) { + strcat(entry_path, parent->name); + strcat(entry_path, "/"); + } + strcat(entry_path, name); + // Check if the entry exists. + if (procfs_find_entry_path(entry_path) != NULL) { + pr_err("proc_destroy_entry(%s): Proc entry already exists.\n", entry_path); + errno = EEXIST; + return NULL; + } + // Create the new procfs file. + procfs_file_t *procfs_file = procfs_create_file(entry_path, DT_DIR); + if (!procfs_file) { + pr_err("proc_destroy_entry(%s): Cannot create proc entry.\n", entry_path); + errno = ENFILE; + return NULL; + } + return &procfs_file->dir_entry; +} + +int proc_rmdir(const char *name, proc_dir_entry_t *parent) +{ + char entry_path[PATH_MAX]; + strcpy(entry_path, "/proc/"); + if (parent) { + strcat(entry_path, parent->name); + strcat(entry_path, "/"); + } + strcat(entry_path, name); + // Check if the entry exists. + procfs_file_t *procfs_file = procfs_find_entry_path(entry_path); + if (procfs_file == NULL) { + pr_err("proc_destroy_entry(%s): Cannot find proc entry.\n", entry_path); + return -ENOENT; + } + if ((procfs_file->flags & DT_DIR) == 0) { + pr_err("proc_destroy_entry(%s): Proc entry is not a directory.\n", entry_path); + return -ENOTDIR; + } + // Check if its empty. + if (procfs_check_if_empty(procfs_file->name)) { + pr_err("procfs_rmdir(%s): The directory is not empty.\n", entry_path); + return -ENOTEMPTY; + } + // Check if the procfs file has still some file associated. + if (!list_head_empty(&procfs_file->files)) { + pr_err("proc_destroy_entry(%s): Proc entry is busy.\n", entry_path); + return -EBUSY; + } + if (procfs_destroy_file(procfs_file)) { + pr_err("proc_destroy_entry(%s): Failed to remove file.\n", entry_path); + return -ENOENT; + } + return 0; +} + +proc_dir_entry_t *proc_create_entry(const char *name, proc_dir_entry_t *parent) +{ + char entry_path[PATH_MAX]; + strcpy(entry_path, "/proc/"); + if (parent) { + strcat(entry_path, parent->name); + strcat(entry_path, "/"); + } + strcat(entry_path, name); + // Check if the entry exists. + if (procfs_find_entry_path(entry_path) != NULL) { + pr_err("proc_destroy_entry(%s): Proc entry already exists.\n", entry_path); + errno = EEXIST; + return NULL; + } + // Create the new procfs file. + procfs_file_t *procfs_file = procfs_create_file(entry_path, DT_REG); + if (!procfs_file) { + pr_err("proc_destroy_entry(%s): Cannot create proc entry.\n", entry_path); + errno = ENFILE; + return NULL; + } + return &procfs_file->dir_entry; +} + +int proc_destroy_entry(const char *name, proc_dir_entry_t *parent) +{ + char entry_path[PATH_MAX]; + strcpy(entry_path, "/proc/"); + if (parent) { + strcat(entry_path, parent->name); + strcat(entry_path, "/"); + } + strcat(entry_path, name); + // Check if the entry exists. + procfs_file_t *procfs_file = procfs_find_entry_path(entry_path); + if (procfs_file == NULL) { + pr_err("proc_destroy_entry(%s): Cannot find proc entry.\n", entry_path); + return -ENOENT; + } + if ((procfs_file->flags & DT_REG) == 0) { + pr_err("proc_destroy_entry(%s): Proc entry is not a regular file.\n", entry_path); + return -ENOENT; + } + // Check if the procfs file has still some file associated. + if (!list_head_empty(&procfs_file->files)) { + pr_err("proc_destroy_entry(%s): Proc entry is busy.\n", entry_path); + return -EBUSY; + } + if (procfs_destroy_file(procfs_file)) { + pr_err("proc_destroy_entry(%s): Failed to remove file.\n", entry_path); + return -ENOENT; + } + return 0; +} \ No newline at end of file diff --git a/mentos/src/fs/read_write.c b/mentos/src/fs/read_write.c index fcf2531..23673f4 100644 --- a/mentos/src/fs/read_write.c +++ b/mentos/src/fs/read_write.c @@ -1,64 +1,97 @@ /// MentOS, The Mentoring Operating system project /// @file read_write.c /// @brief Read and write functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include -#include "read_write.h" -#include "vfs.h" -#include "stdio.h" +#include "process/scheduler.h" +#include "fs/vfs_types.h" +#include "system/panic.h" +#include "sys/errno.h" #include "fcntl.h" -#include "unistd.h" -#include "keyboard.h" -#include "video.h" +#include "stdio.h" +#include "fs/vfs.h" ssize_t sys_read(int fd, void *buf, size_t nbytes) { - if (fd == STDIN_FILENO) { - *((char *)buf) = keyboard_getc(); - return 1; - } + // Get the current task. + task_struct *task = scheduler_get_current_process(); - int mp_id = fd_list[fd].mountpoint_id; - int fs_fd = fd_list[fd].fs_spec_id; + // Check the current FD. + if (fd < 0 || fd >= task->max_fd) { + return -EMFILE; + } - if (mountpoint_list[mp_id].operations.read_f != NULL) { - return mountpoint_list[mp_id].operations.read_f(fs_fd, buf, nbytes); - } else { - printf("No READ Found for that file system\n"); - } + // Get the file descriptor. + vfs_file_descriptor_t *vfd = &task->fd_list[fd]; - return 0; + // Check the permissions. +#if 0 + if (!(vfd->flags_mask & O_RDONLY)) { + return -EROFS; + } +#endif + + // Check the file. + if (vfd->file_struct == NULL) { + return -ENOSYS; + } + + // Perform the read. + int read = vfs_read(vfd->file_struct, buf, vfd->file_struct->f_pos, nbytes); + + // Update the offset. + if (read > 0) { + vfd->file_struct->f_pos += read; + } + return read; } ssize_t sys_write(int fd, void *buf, size_t nbytes) { - if ((fd == STDOUT_FILENO) || (fd == STDERR_FILENO)) { - for (size_t i = 0; (i < nbytes); ++i) - video_putc(((char *)buf)[i]); - return nbytes; - } + // Get the current task. + task_struct *task = scheduler_get_current_process(); - if (fd > MAX_OPEN_FD) { - //errno = EBADF; - return -1; - } + // Check the current FD. + if (fd < 0 || fd >= task->max_fd) { + return -EMFILE; + } - if (!(fd_list[fd].flags_mask & O_RDWR)) { - //errno = EROFS; - return -1; - } + // Get the file descriptor. + vfs_file_descriptor_t *vfd = &task->fd_list[fd]; - mountpoint_t *mp = &mountpoint_list[fd_list[fd].mountpoint_id]; - if (mp == NULL) { - //errno = ENODEV; - return -1; - } - if (mp->operations.write_f == NULL) { - //errno = ENOSYS; - return -1; - } + // Check the permissions. + if (!(vfd->flags_mask & O_WRONLY)) { + return -EROFS; + } - return mp->operations.write_f(fd_list[fd].fs_spec_id, buf, nbytes); + // Check the file. + if (vfd->file_struct == NULL) { + return -ENOSYS; + } + + // Perform the write. + int written = vfs_write(vfd->file_struct, buf, vfd->file_struct->f_pos, nbytes); + + // Update the offset. + if (written > 0) { + vfd->file_struct->f_pos += written; + } + return written; } + +off_t sys_lseek(int fd, off_t offset, int whence) +{ + task_struct *task = scheduler_get_current_process(); + if (fd < 0 || fd >= task->max_fd) { + return -1; + } + // Get the file descriptor. + vfs_file_descriptor_t *vfd = &task->fd_list[fd]; + // Check the file. + if (vfd->file_struct == NULL) { + return -ENOSYS; + } + // Perform the lseek. + return vfs_lseek(vfd->file_struct, offset, whence); +} \ No newline at end of file diff --git a/mentos/src/fs/readdir.c b/mentos/src/fs/readdir.c new file mode 100644 index 0000000..54f74f0 --- /dev/null +++ b/mentos/src/fs/readdir.c @@ -0,0 +1,54 @@ +/// MentOS, The Mentoring Operating system project +/// @file readdir.c +/// @brief Function for accessing directory entries. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "sys/dirent.h" +#include "process/scheduler.h" +#include "system/syscall.h" +#include "system/printk.h" +#include "sys/errno.h" +#include "stdio.h" +#include "fs/vfs.h" + +int sys_getdents(int fd, dirent_t *dirp, unsigned int count) +{ + if (dirp == NULL) { + printf("getdents: cannot read directory :" + "Directory pointer is not valid\n"); + return 0; + } + // Get the current task. + task_struct *task = scheduler_get_current_process(); + + // Check the current FD. + if (fd < 0 || fd >= task->max_fd) { + return -EMFILE; + } + + // Get the file descriptor. + vfs_file_descriptor_t *vfd = &task->fd_list[fd]; + + // Check the permissions. +#if 0 + if (!(task->fd_list[fd].flags_mask & O_RDONLY)) { + return -EROFS; + } +#endif + + // Get the file. + vfs_file_t *file = vfd->file_struct; + if (file == NULL) { + return -ENOSYS; + } + + // Perform the read. + int actual_read = vfs_getdents(file, dirp, vfd->file_struct->f_pos, count); + + // Update the offset. + if (actual_read > 0) { + vfd->file_struct->f_pos += (actual_read / sizeof(dirent_t)); + } + return actual_read; +} diff --git a/mentos/src/fs/stat.c b/mentos/src/fs/stat.c new file mode 100644 index 0000000..becfdb1 --- /dev/null +++ b/mentos/src/fs/stat.c @@ -0,0 +1,47 @@ +/// MentOS, The Mentoring Operating system project +/// @file stat.c +/// @brief Stat functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "misc/debug.h" +#include "sys/errno.h" +#include "fs/vfs.h" +#include "mem/kheap.h" +#include "stdio.h" +#include "string.h" +#include "fs/initrd.h" +#include "limits.h" + +int sys_stat(const char *path, stat_t *buf) +{ + return vfs_stat(path, buf); +} + +int sys_fstat(int fd, stat_t *buf) +{ + // Get the current task. + task_struct *task = scheduler_get_current_process(); + + // Check the current FD. + if (fd < 0 || fd >= task->max_fd) { + return -EMFILE; + } + + // Get the file descriptor. + vfs_file_descriptor_t *vfd = &task->fd_list[fd]; + + // Check the permissions. +#if 0 + if (!(vfd->flags_mask & O_RDONLY)) { + return -EROFS; + } +#endif + + // Check the file. + if (vfd->file_struct == NULL) { + return -ENOSYS; + } + + return vfs_fstat(vfd->file_struct, buf); +} \ No newline at end of file diff --git a/mentos/src/fs/vfs.c b/mentos/src/fs/vfs.c index a358375..f6e310e 100644 --- a/mentos/src/fs/vfs.c +++ b/mentos/src/fs/vfs.c @@ -1,168 +1,504 @@ /// MentOS, The Mentoring Operating system project /// @file vfs.c /// @brief Headers for Virtual File System (VFS). -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include -#include -#include "video.h" -#include "vfs.h" -#include "debug.h" -#include "shell.h" -#include "string.h" -#include "initrd.h" +#include "fs/vfs.h" -int current_fd; -char *module_start[MAX_MODULES]; -file_descriptor_t fd_list[MAX_OPEN_FD]; -mountpoint_t mountpoint_list[MAX_MOUNTPOINT]; +#include "process/scheduler.h" +#include "klib/spinlock.h" +#include "strerror.h" +#include "system/syscall.h" +#include "klib/hashmap.h" +#include "string.h" +#include "fs/procfs.h" +#include "assert.h" +#include "libgen.h" +#include "misc/debug.h" +#include "system/panic.h" +#include "stdio.h" + +/// The hashmap that associates a type of Filesystem `name` to its `mount` function; +static hashmap_t *vfs_filesystems; +/// The list of superblocks. +static list_head vfs_super_blocks; +/// The maximum number of filesystem types. +static const unsigned vfs_filesystems_max = 10; +/// Lock for refcount field. +static spinlock_t vfs_spinlock_refcount; +/// Spinlock for the entire virtual filesystem. +static spinlock_t vfs_spinlock; +/// VFS memory cache for superblocks. +static kmem_cache_t *vfs_superblock_cache; + +/// VFS memory cache for files. +kmem_cache_t *vfs_file_cache; void vfs_init() { - current_fd = 0; - - for (int32_t i = 0; i < MAX_MOUNTPOINT; ++i) { - strcpy(mountpoint_list[i].mountpoint, ""); - mountpoint_list[i].mp_id = i; - mountpoint_list[i].uid = 0; - mountpoint_list[i].gid = 0; - mountpoint_list[i].pmask = 0; - mountpoint_list[i].dev_id = 0; - mountpoint_list[i].start_address = 0; - mountpoint_list[i].dir_op.opendir_f = NULL; - mountpoint_list[i].operations.open_f = NULL; - mountpoint_list[i].operations.close_f = NULL; - mountpoint_list[i].operations.read_f = NULL; - mountpoint_list[i].operations.write_f = NULL; - mountpoint_list[i].stat_op.stat_f = NULL; - } - - for (int32_t i = 0; i < MAX_OPEN_FD; ++i) { - fd_list[i].fs_spec_id = -1; - fd_list[i].mountpoint_id = -1; - fd_list[i].offset = 0; - fd_list[i].flags_mask = 0; - } - - strcpy(mountpoint_list[0].mountpoint, "/"); - mountpoint_list[0].uid = 0; - mountpoint_list[0].gid = 0; - mountpoint_list[0].pmask = 0; - mountpoint_list[0].dev_id = 0; - mountpoint_list[0].start_address = (unsigned int)module_start[0]; - mountpoint_list[0].end_address = (unsigned int)module_end[0]; - mountpoint_list[0].dir_op.opendir_f = initfs_opendir; - mountpoint_list[0].dir_op.closedir_f = initfs_closedir; - mountpoint_list[0].dir_op.readdir_f = initrd_readdir; - mountpoint_list[0].dir_op.mkdir_f = initrd_mkdir; - mountpoint_list[0].dir_op.rmdir_f = initrd_rmdir; - mountpoint_list[0].operations.open_f = initfs_open; - mountpoint_list[0].operations.remove_f = initfs_remove; - mountpoint_list[0].operations.close_f = initrd_close; - mountpoint_list[0].operations.read_f = initfs_read; - mountpoint_list[0].operations.write_f = initrd_write; - mountpoint_list[0].stat_op.stat_f = initrd_stat; + // Initialize the list of superblocks. + list_head_init(&vfs_super_blocks); + // Initialize the caches for superblocks and files. + vfs_superblock_cache = KMEM_CREATE(super_block_t); + vfs_file_cache = KMEM_CREATE(vfs_file_t); + // Allocate the hashmap for the different filesystems. + vfs_filesystems = hashmap_create( + vfs_filesystems_max, + hashmap_str_hash, + hashmap_str_comp, + hashmap_do_not_duplicate, + hashmap_do_not_free); + // Initialize the spinlock. + spinlock_init(&vfs_spinlock); + spinlock_init(&vfs_spinlock_refcount); } -int32_t get_mountpoint_id(const char *path) +int vfs_register_filesystem(file_system_type *fs) { - size_t last_mpl = 0; - int32_t last_mp_id = -1; - - for (uint32_t it = 0; it < MAX_MOUNTPOINT; ++it) { - size_t mpl = strlen(mountpoint_list[it].mountpoint); - - if (!strncmp(path, mountpoint_list[it].mountpoint, mpl)) { - if (((mpl > last_mpl) && it > 0) || (it == 0)) { - last_mpl = mpl; - last_mp_id = it; - } - } - } - - return last_mp_id; + if (hashmap_set(vfs_filesystems, (void *)fs->name, (void *)fs) != NULL) { + pr_err("Filesystem already registered.\n"); + return 0; + } + pr_debug("vfs_register_filesystem(`%s`) : %p\n", fs->name, fs); + return 1; } -mountpoint_t *get_mountpoint(const char *path) +int vfs_unregister_filesystem(file_system_type *fs) { - int32_t mp_id = get_mountpoint_id(path); - - if (mp_id < 0) { - return NULL; - } - - return &mountpoint_list[mp_id]; + if (hashmap_remove(vfs_filesystems, (void *)fs->name) != NULL) { + pr_err("Filesystem not present to unregister.\n"); + return 0; + } + return 1; } -mountpoint_t *get_mountpoint_from_id(int32_t mp_id) +super_block_t *vfs_get_superblock(const char *absolute_path) { - if (mp_id >= MAX_MOUNTPOINT) { - return NULL; - } - - return &mountpoint_list[mp_id]; + size_t last_sb_len = 0; + super_block_t *last_sb = NULL, *superblock = NULL; + list_head *it; + list_for_each (it, &vfs_super_blocks) { + superblock = list_entry(it, super_block_t, mounts); + int len = strlen(superblock->name); + if (!strncmp(absolute_path, superblock->name, len)) { + size_t sbl = strlen(superblock->name); + if (sbl > last_sb_len) { + last_sb_len = sbl; + last_sb = superblock; + } + } + } + return last_sb; } -int get_relative_path(uint32_t mp_id, char *path) +vfs_file_t *vfs_open(const char *path, int flags, mode_t mode) { - char relative_path[MAX_PATH_LENGTH]; - - // Get the size of the path. - size_t path_size = strlen(path); - - // Get the size of the mount-point. - size_t mnpt_size = strlen(mountpoint_list[mp_id].mountpoint); - - // Get the size of the relative path. - size_t rltv_size = path_size - mnpt_size; - - // Copy the relative path. - if (rltv_size > 0) { - size_t i; - for (i = 0; i < rltv_size; ++i) { - relative_path[i] = path[mnpt_size + i]; - } - relative_path[i] = '\0'; - strcpy(path, relative_path); - } - - return strlen(path); + // Allocate a variable for the path. + char absolute_path[PATH_MAX]; + // If the first character is not the '/' then get the absolute path. + if (!realpath(path, absolute_path)) { + pr_err("vfs_open(%s): Cannot get the absolute path!\n", path); + errno = ENODEV; + return NULL; + } + super_block_t *sb = vfs_get_superblock(absolute_path); + if (sb == NULL) { + pr_err("vfs_open(%s): Cannot find the superblock!\n", path); + errno = ENOENT; + return NULL; + } + vfs_file_t *sb_root = sb->root; + if (sb_root == NULL) { + pr_err("vfs_open(%s): Cannot find the superblock root!\n", path); + errno = ENOENT; + return NULL; + } + // Rebase the absolute path. + //size_t name_offset = (strcmp(mp->name, "/") == 0) ? 0 : strlen(mp->name); + // Check if the function is implemented. + if (sb_root->fs_operations->open_f == NULL) { + pr_err("vfs_open(%s): Function not supported in current filesystem.", path); + errno = ENOSYS; + return NULL; + } + // Retrieve the file. + vfs_file_t *file = sb_root->fs_operations->open_f(absolute_path, flags, mode); + if (file == NULL) { + pr_err("vfs_open(%s): Cannot find the given file (%s)!\n", path, strerror(errno)); + errno = ENOENT; + return NULL; + } + // Increment file reference counter. + ++file->count; + // Return the file. + return file; } -int get_absolute_path(char *path) +int vfs_close(vfs_file_t *file) { - if (path[0] != '/') { - char abspath[MAX_PATH_LENGTH]; - memset(abspath, '\0', MAX_PATH_LENGTH); - getcwd(abspath, MAX_PATH_LENGTH); - int abs_size = strlen(abspath); - if (abspath[abs_size - 1] == '/') { - strncat(abspath, path, strlen(path)); - } else { - strncat(abspath, "/", strlen(path)); - strncat(abspath, path, strlen(path)); - } - strcpy(path, abspath); + // Decrement file reference counter. + file->count--; - return strlen(path); - } - - return 0; + // Close file if it's the last reference. + if (file->count == 0) { + // Check if the filesystem has the close function. + if (file->fs_operations->close_f == NULL) { + return -ENOSYS; + } + file->fs_operations->close_f(file); + } + return 0; } -void vfs_dump() +ssize_t vfs_read(vfs_file_t *file, void *buf, size_t offset, size_t nbytes) { - dbg_print("File Descriptors:\n"); - int i = 0; - - while (i < MAX_OPEN_FD) { - if (fd_list[i].mountpoint_id != -1) { - dbg_print("[%d] %s\n", i, - mountpoint_list[fd_list[i].mountpoint_id].mountpoint); - } else { - dbg_print("[%d] Free\n", i); - } - ++i; - } + if (file->fs_operations->read_f == NULL) { + pr_err("No READ function found for the current filesystem.\n"); + return -ENOSYS; + } + return file->fs_operations->read_f(file, buf, offset, nbytes); +} + +ssize_t vfs_write(vfs_file_t *file, void *buf, size_t offset, size_t nbytes) +{ + if (file->fs_operations->write_f == NULL) { + pr_err("No WRITE function found for the current filesystem.\n"); + return -ENOSYS; + } + return file->fs_operations->write_f(file, buf, offset, nbytes); +} + +off_t vfs_lseek(vfs_file_t *file, off_t offset, int whence) +{ + if (file->fs_operations->lseek_f == NULL) { + pr_err("No WRITE function found for the current filesystem.\n"); + return -ENOSYS; + } + return file->fs_operations->lseek_f(file, offset, whence); +} + +int vfs_getdents(vfs_file_t *file, dirent_t *dirp, off_t off, size_t count) +{ + if (file->fs_operations->getdents_f == NULL) { + pr_err("No GETDENTS function found for the current filesystem.\n"); + return -ENOSYS; + } + return file->fs_operations->getdents_f(file, dirp, off, count); +} + +int vfs_ioctl(vfs_file_t *file, int request, void *data) +{ + if (file->fs_operations->ioctl_f == NULL) { + pr_err("No IOCTL function found for the current filesystem.\n"); + return -ENOSYS; + } + return file->fs_operations->ioctl_f(file, request, data); +} + +int vfs_unlink(const char *path) +{ + // Allocate a variable for the path. + char absolute_path[PATH_MAX]; + // If the first character is not the '/' then get the absolute path. + if (!realpath(path, absolute_path)) { + pr_err("vfs_unlink(%s): Cannot get the absolute path.", path); + return -ENODEV; + } + super_block_t *sb = vfs_get_superblock(absolute_path); + if (sb == NULL) { + pr_err("vfs_unlink(%s): Cannot find the superblock!\n"); + return -ENODEV; + } + vfs_file_t *sb_root = sb->root; + if (sb_root == NULL) { + pr_err("vfs_unlink(%s): Cannot find the superblock root.", path); + return -ENOENT; + } + // Check if the function is implemented. + if (sb_root->fs_operations->unlink_f == NULL) { + pr_err("vfs_unlink(%s): Function not supported in current filesystem.", path); + return -ENOSYS; + } + return sb_root->fs_operations->unlink_f(absolute_path); +} + +int vfs_mkdir(const char *path, mode_t mode) +{ + // Allocate a variable for the path. + char absolute_path[PATH_MAX]; + // If the first character is not the '/' then get the absolute path. + if (!realpath(path, absolute_path)) { + pr_err("vfs_mkdir(%s): Cannot get the absolute path.", path); + return -ENODEV; + } + super_block_t *sb = vfs_get_superblock(absolute_path); + if (sb == NULL) { + pr_err("vfs_mkdir(%s): Cannot find the superblock!\n"); + return -ENODEV; + } + vfs_file_t *sb_root = sb->root; + if (sb_root == NULL) { + pr_err("vfs_mkdir(%s): Cannot find the superblock root.", path); + return -ENOENT; + } + // Check if the function is implemented. + if (sb_root->sys_operations->mkdir_f == NULL) { + pr_err("vfs_mkdir(%s): Function not supported in current filesystem.", path); + return -ENOSYS; + } + return sb_root->sys_operations->mkdir_f(absolute_path, mode); +} + +int vfs_rmdir(const char *path) +{ + // Allocate a variable for the path. + char absolute_path[PATH_MAX]; + // If the first character is not the '/' then get the absolute path. + if (!realpath(path, absolute_path)) { + pr_err("vfs_rmdir(%s): Cannot get the absolute path.", path); + return -ENODEV; + } + super_block_t *sb = vfs_get_superblock(absolute_path); + if (sb == NULL) { + pr_err("vfs_rmdir(%s): Cannot find the superblock!\n"); + return -ENODEV; + } + vfs_file_t *sb_root = sb->root; + if (sb_root == NULL) { + pr_err("vfs_rmdir(%s): Cannot find the superblock root.", path); + return -ENOENT; + } + // Check if the function is implemented. + if (sb_root->sys_operations->rmdir_f == NULL) { + pr_err("vfs_rmdir(%s): Function not supported in current filesystem.", path); + return -ENOSYS; + } + // Remove the file. + return sb_root->sys_operations->rmdir_f(absolute_path); +} + +int vfs_stat(const char *path, stat_t *buf) +{ + // Allocate a variable for the path. + char absolute_path[PATH_MAX]; + // If the first character is not the '/' then get the absolute path. + if (!realpath(path, absolute_path)) { + pr_err("vfs_stat(%s): Cannot get the absolute path.", path); + return -ENODEV; + } + super_block_t *sb = vfs_get_superblock(absolute_path); + if (sb == NULL) { + pr_err("vfs_stat(%s): Cannot find the superblock!\n"); + return -ENODEV; + } + vfs_file_t *sb_root = sb->root; + if (sb_root == NULL) { + pr_err("vfs_stat(%s): Cannot find the superblock root.", path); + return -ENOENT; + } + // Check if the function is implemented. + if (sb_root->sys_operations->stat_f == NULL) { + pr_err("vfs_rmdir(%s): Function not supported in current filesystem.", path); + return -ENOSYS; + } + // Reset the structure. + buf->st_dev = 0; + buf->st_ino = 0; + buf->st_mode = 0; + buf->st_uid = 0; + buf->st_gid = 0; + buf->st_size = 0; + buf->st_atime = 0; + buf->st_mtime = 0; + buf->st_ctime = 0; + // Retrieve the file. + return sb_root->sys_operations->stat_f(absolute_path, buf); +} + +int vfs_fstat(vfs_file_t *file, stat_t *buf) +{ + if (file->fs_operations->stat_f == NULL) { + pr_err("No FSTAT function found for the current filesystem.\n"); + return -ENOSYS; + } + // Reset the structure. + buf->st_dev = 0; + buf->st_ino = 0; + buf->st_mode = 0; + buf->st_uid = 0; + buf->st_gid = 0; + buf->st_size = 0; + buf->st_atime = 0; + buf->st_mtime = 0; + buf->st_ctime = 0; + return file->fs_operations->stat_f(file, buf); +} + +int vfs_mount(const char *path, vfs_file_t *new_fs_root) +{ + if (!path || path[0] != '/') { + pr_err("vfs_mount(%s): Path must be absolute for superblock.\n", path); + return 0; + } + if (new_fs_root == NULL) { + pr_err("vfs_mount(%s): You must provide a valid file!\n", path); + return 0; + } + // Lock the vfs spinlock. + spinlock_lock(&vfs_spinlock); + pr_debug("Mounting file with path `%s` as root '%s'...\n", new_fs_root->name, path); + // Create the superblock. + super_block_t *sb = kmem_cache_alloc(vfs_superblock_cache, GFP_KERNEL); + if (!sb) { + pr_debug("Cannot allocate memory for the superblock.\n"); + } else { + // Copy the name. + strcpy(sb->name, new_fs_root->name); + // Set the pointer. + sb->root = new_fs_root; + // Add to the list. + list_head_add(&sb->mounts, &vfs_super_blocks); + } + spinlock_unlock(&vfs_spinlock); + pr_debug("Correctly mounted '%s' on '%s'...\n", path, new_fs_root->name); + return 1; +} + +int do_mount(const char *type, const char *path, const char *args) +{ + file_system_type *fst = (file_system_type *)hashmap_get(vfs_filesystems, type); + if (fst == NULL) { + pr_err("Unknown filesystem type: %s\n", type); + return -ENODEV; + } + vfs_file_t *file = fst->mount(path, args); + if (file == NULL) { + pr_err("Mount callback return a null pointer: %s\n", type); + return -ENODEV; + } + if (!vfs_mount(path, file)) { + pr_err("do_mount(`%s`, `%s`, `%s`) : failed to mount.\n", type, path, args); + return -ENODEV; + } + super_block_t *sb = vfs_get_superblock(path); + if (sb == NULL) { + pr_err("do_mount(`%s`, `%s`, `%s`) : Cannot find the superblock.\n", type, path, args); + return -ENODEV; + } + // Set the filesystem type. + sb->type = fst; + pr_debug("Mounted %s[%s] to `%s`: file = %p\n", type, args, path, file); + return 0; +} + +void vfs_lock(vfs_file_t *file) +{ + spinlock_lock(&vfs_spinlock_refcount); + file->refcount = -1; + spinlock_unlock(&vfs_spinlock_refcount); +} + +int vfs_extend_task_fd_list(struct task_struct *task) +{ + if (!task) { + pr_err("Null process.\n"); + errno = ESRCH; + return 0; + } + // Set the max number of file descriptors. + int new_max_fd = (task->fd_list) ? task->max_fd * 2 + 1 : MAX_OPEN_FD; + // Allocate the memory for the list. + void *new_fd_list = kmalloc(new_max_fd * sizeof(vfs_file_descriptor_t)); + // Check the new list. + if (!new_fd_list) { + pr_err("Failed to allocate memory for `fd_list`.\n"); + errno = EMFILE; + return 0; + } + // Clear the memory of the new list. + memset(new_fd_list, 0, task->max_fd * sizeof(vfs_file_descriptor_t)); + // Deal with pre-existing list. + if (task->fd_list) { + // Copy the old entries. + memcpy(new_fd_list, task->fd_list, task->max_fd * sizeof(vfs_file_descriptor_t)); + // Free the memory of the old list. + kfree(task->fd_list); + } + // Set the new maximum number of file descriptors. + task->max_fd = new_max_fd; + // Set the new list. + task->fd_list = new_fd_list; + return 1; +} + +int vfs_init_task(task_struct *task) +{ + if (!task) { + pr_err("Null process.\n"); + errno = ESRCH; + return 0; + } + // Initialize the file descriptor list. + if (!vfs_extend_task_fd_list(task)) { + pr_err("Error while trying to initialize the `fd_list` for process `%d`: %s\n", task->pid, strerror(errno)); + return 0; + } + // Create the proc entry. + if (proc_create_entry_pid(task)) { + pr_err("Error while trying to create proc entry for process `%d`: %s\n", task->pid, strerror(errno)); + return 0; + } + return 1; +} + +int vfs_dup_task(task_struct *task, task_struct *old_task) +{ + // Copy the maximum number of file descriptors. + task->max_fd = old_task->max_fd; + // Allocate the memory for the new list. + task->fd_list = kmalloc(task->max_fd * sizeof(vfs_file_descriptor_t)); + // Copy the old list. + memcpy(task->fd_list, old_task->fd_list, task->max_fd * sizeof(vfs_file_descriptor_t)); + // Increase the counters to the open files. + for (int fd = 0; fd < task->max_fd; fd++) { + // Check if the file descriptor is associated with a file. + if (task->fd_list[fd].file_struct) { + // Increase the counter. + ++task->fd_list[fd].file_struct->count; + } + } + // Create the proc entry. + if (proc_create_entry_pid(task)) { + pr_err("Error while trying to create proc entry for '%d': %s\n", task->pid, strerror(errno)); + return 0; + } + return 1; +} + +int vfs_destroy_task(task_struct *task) +{ + // Decrease the counters to the open files. + for (int fd = 0; fd < task->max_fd; fd++) { + // Check if the file descriptor is associated with a file. + if (task->fd_list[fd].file_struct) { + // Decrease the counter. + --task->fd_list[fd].file_struct->count; + // If counter is zero, close the file. + if (task->fd_list[fd].file_struct->count == 0) + task->fd_list[fd].file_struct->fs_operations->close_f(task->fd_list[fd].file_struct); + // Clear the pointer to the file structure. + task->fd_list[fd].file_struct = NULL; + } + } + // Set the maximum file descriptors to 0. + task->max_fd = 0; + // Free the memory of the list. + kfree(task->fd_list); + // Remove the proc entry. + if (proc_destroy_entry_pid(task)) { + pr_err("Error while trying to remove proc entry for '%d': %s\n", task->pid, strerror(errno)); + return 0; + } + return 1; } diff --git a/mentos/src/hardware/cpuid.c b/mentos/src/hardware/cpuid.c index 4735565..a157016 100644 --- a/mentos/src/hardware/cpuid.c +++ b/mentos/src/hardware/cpuid.c @@ -1,15 +1,15 @@ /// MentOS, The Mentoring Operating system project /// @file cpuid.c /// @brief CPUID-based function to detect CPU type. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "cmd_cpuid.h" +#include "hardware/cpuid.h" #include "string.h" void get_cpuid(cpuinfo_t *cpuinfo) { - register_t ereg; + pt_regs ereg; ereg.eax = ereg.ebx = ereg.ecx = ereg.edx = 0; cpuid_write_vendor(cpuinfo, &ereg); @@ -19,70 +19,65 @@ void get_cpuid(cpuinfo_t *cpuinfo) cpuid_write_proctype(cpuinfo, &ereg); } -void call_cpuid(register_t *registers) +void call_cpuid(pt_regs *registers) { - __asm__ ("cpuid\n\t" : - "=a" (registers->eax), - "=b" (registers->ebx), - "=c" (registers->ecx), - "=d" (registers->edx) : - "a" (registers->eax)); + __asm__("cpuid\n\t" + : "=a"(registers->eax), "=b"(registers->ebx), "=c"(registers->ecx), + "=d"(registers->edx) + : "a"(registers->eax)); } -void cpuid_write_vendor(cpuinfo_t *cpuinfo, register_t *registers) +void cpuid_write_vendor(cpuinfo_t *cpuinfo, pt_regs *registers) { call_cpuid(registers); - cpuinfo->cpu_vendor[0] = (char) ((registers->ebx & 0x000000FF)); - cpuinfo->cpu_vendor[1] = (char) ((registers->ebx & 0x0000FF00) >> 8); - cpuinfo->cpu_vendor[2] = (char) ((registers->ebx & 0x00FF0000) >> 16); - cpuinfo->cpu_vendor[3] = (char) ((registers->ebx & 0xFF000000) >> 24); - cpuinfo->cpu_vendor[4] = (char) ((registers->edx & 0x000000FF)); - cpuinfo->cpu_vendor[5] = (char) ((registers->edx & 0x0000FF00) >> 8); - cpuinfo->cpu_vendor[6] = (char) ((registers->edx & 0x00FF0000) >> 16); - cpuinfo->cpu_vendor[7] = (char) ((registers->edx & 0xFF000000) >> 24); - cpuinfo->cpu_vendor[8] = (char) ((registers->ecx & 0x000000FF)); - cpuinfo->cpu_vendor[9] = (char) ((registers->ecx & 0x0000FF00) >> 8); - cpuinfo->cpu_vendor[10] = (char) ((registers->ecx & 0x00FF0000) >> 16); - cpuinfo->cpu_vendor[11] = (char) ((registers->ecx & 0xFF000000) >> 24); + cpuinfo->cpu_vendor[0] = (char)((registers->ebx & 0x000000FF)); + cpuinfo->cpu_vendor[1] = (char)((registers->ebx & 0x0000FF00) >> 8); + cpuinfo->cpu_vendor[2] = (char)((registers->ebx & 0x00FF0000) >> 16); + cpuinfo->cpu_vendor[3] = (char)((registers->ebx & 0xFF000000) >> 24); + cpuinfo->cpu_vendor[4] = (char)((registers->edx & 0x000000FF)); + cpuinfo->cpu_vendor[5] = (char)((registers->edx & 0x0000FF00) >> 8); + cpuinfo->cpu_vendor[6] = (char)((registers->edx & 0x00FF0000) >> 16); + cpuinfo->cpu_vendor[7] = (char)((registers->edx & 0xFF000000) >> 24); + cpuinfo->cpu_vendor[8] = (char)((registers->ecx & 0x000000FF)); + cpuinfo->cpu_vendor[9] = (char)((registers->ecx & 0x0000FF00) >> 8); + cpuinfo->cpu_vendor[10] = (char)((registers->ecx & 0x00FF0000) >> 16); + cpuinfo->cpu_vendor[11] = (char)((registers->ecx & 0xFF000000) >> 24); cpuinfo->cpu_vendor[12] = '\0'; } -void cpuid_write_proctype(cpuinfo_t *cpuinfo, register_t *registers) +void cpuid_write_proctype(cpuinfo_t *cpuinfo, pt_regs *registers) { call_cpuid(registers); uint32_t type = cpuid_get_byte(registers->eax, 0xB, 0x3); - switch (type) - { - case 0: - cpuinfo->cpu_type = "Original OEM Processor"; - break; - case 1: - cpuinfo->cpu_type = "Intel Overdrive Processor"; - break; - case 2: - cpuinfo->cpu_type = "Dual processor"; - break; - case 3: - cpuinfo->cpu_type = "(Intel reserved bit)"; - break; + switch (type) { + case 0: + cpuinfo->cpu_type = "Original OEM Processor"; + break; + case 1: + cpuinfo->cpu_type = "Intel Overdrive Processor"; + break; + case 2: + cpuinfo->cpu_type = "Dual processor"; + break; + case 3: + cpuinfo->cpu_type = "(Intel reserved bit)"; + break; } - uint32_t familyID = cpuid_get_byte(registers->eax, 0x7, 0xE); + uint32_t familyID = cpuid_get_byte(registers->eax, 0x7, 0xE); cpuinfo->cpu_family = familyID; - if (familyID == 0x0F) - { + if (familyID == 0x0F) { cpuinfo->cpu_family += cpuid_get_byte(registers->eax, 0x13, 0xFF); } - uint32_t model = cpuid_get_byte(registers->eax, 0x3, 0xE); + uint32_t model = cpuid_get_byte(registers->eax, 0x3, 0xE); cpuinfo->cpu_model = model; - if (familyID == 0x06 || familyID == 0x0F) - { + if (familyID == 0x06 || familyID == 0x0F) { uint32_t ext_model = cpuid_get_byte(registers->eax, 0xF, 0xE); cpuinfo->cpu_model += (ext_model << 4); } @@ -92,12 +87,9 @@ void cpuid_write_proctype(cpuinfo_t *cpuinfo, register_t *registers) cpuid_feature_edx(cpuinfo, registers->edx); /* Get brand string to identify the processor */ - if (familyID >= 0x0F && model >= 0x03) - { + if (familyID >= 0x0F && model >= 0x03) { cpuinfo->brand_string = cpuid_brand_string(registers); - } - else - { + } else { cpuinfo->brand_string = cpuid_brand_index(registers); } } @@ -107,11 +99,10 @@ void cpuid_feature_ecx(cpuinfo_t *cpuinfo, uint32_t ecx) uint32_t temp = ecx; uint32_t i; - for (i = 0; i < ECX_FLAGS_SIZE; ++i) - { - temp = cpuid_get_byte(temp, i, 1); + for (i = 0; i < ECX_FLAGS_SIZE; ++i) { + temp = cpuid_get_byte(temp, i, 1); cpuinfo->cpuid_ecx_flags[i] = temp; - temp = ecx; + temp = ecx; } } @@ -120,74 +111,66 @@ void cpuid_feature_edx(cpuinfo_t *cpuinfo, uint32_t edx) uint32_t temp = edx; uint32_t i; - for (i = 0; i < EDX_FLAGS_SIZE; ++i) - { - temp = cpuid_get_byte(temp, i, 1); + for (i = 0; i < EDX_FLAGS_SIZE; ++i) { + temp = cpuid_get_byte(temp, i, 1); cpuinfo->cpuid_edx_flags[i] = temp; - temp = edx; + temp = edx; } } -inline uint32_t cpuid_get_byte(const uint32_t reg, - const uint32_t position, - const uint32_t value) +inline uint32_t cpuid_get_byte(uint32_t reg, uint32_t position, uint32_t value) { return ((reg >> position) & value); } -char *cpuid_brand_index(register_t *r) +char *cpuid_brand_index(pt_regs *f) { - char *indexes[21] = {"Reserved", - "Intel Celeron", - "Intel Pentium III", - "Intel Pentium III Xeon", - "Mobile Intel Pentium III", - "Mobile Intel Celeron", - "Intel Pentium 4", - "Intel Pentium 4", - "Intel Celeron", - "Intel Xeon MP", - "Intel Xeon MP", - "Mobile Intel Pentium 4", - "Mobile Intel Celeron", - "Mobile Genuine Intel", - "Intel Celeron M", - "Mobile Intel Celeron", - "Intel Celeron", - "Mobile Genuine Intel", - "Intel Pentium M", - "Mobile Intel Celeron", - NULL}; + char *indexes[21] = { "Reserved", + "Intel Celeron", + "Intel Pentium III", + "Intel Pentium III Xeon", + "Mobile Intel Pentium III", + "Mobile Intel Celeron", + "Intel Pentium 4", + "Intel Pentium 4", + "Intel Celeron", + "Intel Xeon MP", + "Intel Xeon MP", + "Mobile Intel Pentium 4", + "Mobile Intel Celeron", + "Mobile Genuine Intel", + "Intel Celeron M", + "Mobile Intel Celeron", + "Intel Celeron", + "Mobile Genuine Intel", + "Intel Pentium M", + "Mobile Intel Celeron", + NULL }; - int bx = (r->ebx & 0xFF); + int bx = (f->ebx & 0xFF); - if (bx > 0x17) - { + if (bx > 0x17) { bx = 0; } return indexes[bx]; } -/// -/// @param r -/// @return -char *cpuid_brand_string(register_t *r) +char *cpuid_brand_string(pt_regs *f) { char *temp = ""; - for (r->eax = 0x80000002; r->eax <= 0x80000004; (r->eax)++) - { - r->ebx = r->ecx = r->edx = 0; - call_cpuid(r); - temp = strncat(temp, (const char *) r->eax, - strlen((const char *) r->eax)); - temp = strncat(temp, (const char *) r->ebx, - strlen((const char *) r->ebx)); - temp = strncat(temp, (const char *) r->ecx, - strlen((const char *) r->ecx)); - temp = strncat(temp, (const char *) r->edx, - strlen((const char *) r->edx)); + for (f->eax = 0x80000002; f->eax <= 0x80000004; (f->eax)++) { + f->ebx = f->ecx = f->edx = 0; + call_cpuid(f); + temp = + strncat(temp, (const char *)f->eax, strlen((const char *)f->eax)); + temp = + strncat(temp, (const char *)f->ebx, strlen((const char *)f->ebx)); + temp = + strncat(temp, (const char *)f->ecx, strlen((const char *)f->ecx)); + temp = + strncat(temp, (const char *)f->edx, strlen((const char *)f->edx)); } return temp; diff --git a/mentos/src/hardware/pic8259.c b/mentos/src/hardware/pic8259.c index 11cc46e..9dac083 100644 --- a/mentos/src/hardware/pic8259.c +++ b/mentos/src/hardware/pic8259.c @@ -1,10 +1,12 @@ /// MentOS, The Mentoring Operating system project /// @file pic8259.c /// @brief pic8259 definitions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "pic8259.h" +#include "hardware/pic8259.h" +#include "io/port_io.h" +#include "stddef.h" /// End-of-interrupt command code. #define EOI 0x20 @@ -65,94 +67,94 @@ static byte_t slave_cur_mask; void pic8259_init_irq() { - // Set the masks for the master and slave. - master_cur_mask = 0xFF; - slave_cur_mask = 0xFF; + // Set the masks for the master and slave. + master_cur_mask = 0xFF; + slave_cur_mask = 0xFF; - // Starts the initialization sequence (in cascade mode). - outportb(MASTER_PORT_COMMAND, ICW1_INIT + ICW1_ICW4); - outportb(SLAVE_PORT_COMMAND, ICW1_INIT + ICW1_ICW4); + // Starts the initialization sequence (in cascade mode). + outportb(MASTER_PORT_COMMAND, ICW1_INIT + ICW1_ICW4); + outportb(SLAVE_PORT_COMMAND, ICW1_INIT + ICW1_ICW4); - // ICW2: Master PIC vector offset. - outportb(MASTER_PORT_DATA, 0x20); - // ICW2: Slave PIC vector offset. - outportb(SLAVE_PORT_DATA, 0x28); + // ICW2: Master PIC vector offset. + outportb(MASTER_PORT_DATA, 0x20); + // ICW2: Slave PIC vector offset. + outportb(SLAVE_PORT_DATA, 0x28); - // ICW3: Tell Master PIC that there is a slave PIC at IRQ2 (0000 0100). - outportb(MASTER_PORT_DATA, 4); - // ICW3: Tell Slave PIC its cascade identity (0000 0010). - outportb(SLAVE_PORT_DATA, 2); + // ICW3: Tell Master PIC that there is a slave PIC at IRQ2 (0000 0100). + outportb(MASTER_PORT_DATA, 4); + // ICW3: Tell Slave PIC its cascade identity (0000 0010). + outportb(SLAVE_PORT_DATA, 2); - outportb(MASTER_PORT_DATA, ICW4_8086); - outportb(SLAVE_PORT_DATA, ICW4_8086); + outportb(MASTER_PORT_DATA, ICW4_8086); + outportb(SLAVE_PORT_DATA, ICW4_8086); - // Restore saved masks. - outportb(MASTER_PORT_DATA, master_cur_mask); - outportb(SLAVE_PORT_DATA, slave_cur_mask); + // Restore saved masks. + outportb(MASTER_PORT_DATA, master_cur_mask); + outportb(SLAVE_PORT_DATA, slave_cur_mask); - pic8259_irq_enable(IRQ_TO_SLAVE_PIC); + pic8259_irq_enable(IRQ_TO_SLAVE_PIC); - outportb(0xFF, MASTER_PORT_DATA); - outportb(0xFF, SLAVE_PORT_DATA); + outportb(0xFF, MASTER_PORT_DATA); + outportb(0xFF, SLAVE_PORT_DATA); } int pic8259_irq_enable(uint32_t irq) { - if (irq >= IRQ_NUM) { - return -1; - } + if (irq >= IRQ_NUM) { + return -1; + } - byte_t cur_mask; - byte_t new_mask; + byte_t cur_mask; + byte_t new_mask; - if (irq < 8) { - new_mask = ~(1 << irq); - cur_mask = inportb(MASTER_PORT_DATA); - outportb(MASTER_PORT_DATA, (new_mask & cur_mask)); - master_cur_mask = (new_mask & cur_mask); - } else { - irq -= 8; - new_mask = ~(1 << irq); - cur_mask = inportb(SLAVE_PORT_DATA); - outportb(SLAVE_PORT_DATA, (new_mask & cur_mask)); - slave_cur_mask = (new_mask & cur_mask); - } + if (irq < 8) { + new_mask = ~(1 << irq); + cur_mask = inportb(MASTER_PORT_DATA); + outportb(MASTER_PORT_DATA, (new_mask & cur_mask)); + master_cur_mask = (new_mask & cur_mask); + } else { + irq -= 8; + new_mask = ~(1 << irq); + cur_mask = inportb(SLAVE_PORT_DATA); + outportb(SLAVE_PORT_DATA, (new_mask & cur_mask)); + slave_cur_mask = (new_mask & cur_mask); + } - return 0; + return 0; } int pic8259_irq_disable(uint32_t irq) { - if (irq >= IRQ_NUM) { - return -1; - } + if (irq >= IRQ_NUM) { + return -1; + } - uint8_t cur_mask; - if (irq < 8) { - cur_mask = inportb(MASTER_PORT_DATA); - cur_mask |= (1 << irq); - outportb(MASTER_PORT_DATA, cur_mask & 0xFF); - } else { - irq = irq - 8; - cur_mask = inportb(SLAVE_PORT_DATA); - cur_mask |= (1 << irq); - outportb(SLAVE_PORT_DATA, cur_mask & 0xFF); - } - return 0; + uint8_t cur_mask; + if (irq < 8) { + cur_mask = inportb(MASTER_PORT_DATA); + cur_mask |= (1 << irq); + outportb(MASTER_PORT_DATA, cur_mask & 0xFF); + } else { + irq = irq - 8; + cur_mask = inportb(SLAVE_PORT_DATA); + cur_mask |= (1 << irq); + outportb(SLAVE_PORT_DATA, cur_mask & 0xFF); + } + return 0; } void pic8259_send_eoi(uint32_t irq) { - // Perhaps the most common command issued to the PIC chips is the end of - // interrupt (EOI) command (code 0x20). This is issued to the PIC chips - // at the end of an IRQ-based interrupt routine. If the IRQ came from the - // Master PIC, it is sufficient to issue this command only to the Master - // PIC; however if the IRQ came from the Slave PIC, it is necessary to - // issue the command to both PIC chips. - if (irq >= 8) { - outportb(SLAVE_PORT_COMMAND, EOI); - } - outportb(MASTER_PORT_COMMAND, EOI); + // Perhaps the most common command issued to the PIC chips is the end of + // interrupt (EOI) command (code 0x20). This is issued to the PIC chips + // at the end of an IRQ-based interrupt routine. If the IRQ came from the + // Master PIC, it is sufficient to issue this command only to the Master + // PIC; however if the IRQ came from the Slave PIC, it is necessary to + // issue the command to both PIC chips. + if (irq >= 8) { + outportb(SLAVE_PORT_COMMAND, EOI); + } + outportb(MASTER_PORT_COMMAND, EOI); } /* diff --git a/mentos/src/hardware/timer.c b/mentos/src/hardware/timer.c index 5a4b795..117c02e 100644 --- a/mentos/src/hardware/timer.c +++ b/mentos/src/hardware/timer.c @@ -1,115 +1,747 @@ /// MentOS, The Mentoring Operating system project /// @file timer.c /// @brief Timer implementation. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "timer.h" -#include "list.h" -#include "kheap.h" -#include "debug.h" +#include "hardware/timer.h" + +#include "klib/irqflags.h" +#include "process/scheduler.h" +#include "hardware/pic8259.h" +#include "io/port_io.h" #include "stdint.h" -#include "pic8259.h" -#include "port_io.h" -#include "irqflags.h" +#include "mem/kheap.h" +#include "misc/debug.h" +#include "process/wait.h" +#include "drivers/rtc.h" +#include "descriptor_tables/isr.h" +#include "devices/fpu.h" +#include "system/signal.h" +#include "assert.h" +#include "sys/errno.h" -#define SUBTICKS_PER_TICK 1000 +/// Number of ticks per seconds. +#define TICKS_PER_SECOND 1193 -/// This will keep track of how many ticks the system has been running for. -static __volatile__ uint64_t timer_subticks = 0; -/// This will keep track of how many seconds the system has been running for. -static __volatile__ uint64_t timer_ticks = 0; +/// @defgroup picregs Programmable Interval Timer Registers +/// @brief The list of registers used to set the PIT. +/// @{ -/// The list of wakeup functions. -// static list_t *wakeup_list; +/// Channel 0 data port (read/write). +#define PIT_DATAREG0 0x40u + +/// Channel 1 data port (read/write). +#define PIT_DATAREG1 0x41u + +/// Channel 2 data port (read/write). +#define PIT_DATAREG2 0x42u + +/// Mode/Command register (write only, a read is ignored). +#define PIT_COMREG 0x43u + +/// @} + +/// @brief Frequency divider value (1.193182 MHz). +#define PIT_DIVISOR 1193182 + +/// @brief Command used to configure the PIT. +/// @details +/// Bits Usage +/// 6 and 7 [Select channel] +/// 0 0 = Channel 0 +/// 0 1 = Channel 1 +/// 1 0 = Channel 2 +/// 1 1 = Read-back command (8254 only) +/// 4 and 5 [Access mode] +/// 0 0 = Latch count value command +/// 0 1 = Access mode: lobyte only +/// 1 0 = Access mode: hibyte only +/// 1 1 = Access mode: lobyte/hibyte +/// 1 to 3 [Operating mode] +/// 0 0 0 = Mode 0 (interrupt on terminal count) +/// 0 0 1 = Mode 1 (hardware re-triggerable one-shot) +/// 0 1 0 = Mode 2 (rate generator) +/// 0 1 1 = Mode 3 (square wave generator) +/// 1 0 0 = Mode 4 (software triggered strobe) +/// 1 0 1 = Mode 5 (hardware triggered strobe) +/// 1 1 0 = Mode 2 (rate generator, same as 010b) +/// 1 1 1 = Mode 3 (square wave generator, same as 011b) +/// 0 [BCD/Binary mode] +/// 0 = 16-bit binary +/// 1 = four-digit BCD +/// +/// Examples: +/// 0x36 = 00|11|011|0 +/// 0x34 = 00|11|010|0 +#define PIT_CONFIGURATION 0x34u + +/// Mask used to set the divisor. +#define PIT_MASK 0xFFu + +/// The number of ticks since the system started its execution. +static __volatile__ unsigned long timer_ticks = 0; void timer_phase(const uint32_t hz) { - // Calculate our divisor. - uint32_t divisor = PIT_DIVISOR / hz; - // Set our command byte 0x36. - outportb(PIT_COMREG, PIT_CONFIGURATION); - // Set low byte of divisor. - outportb(PIT_DATAREG0, divisor & PIT_MASK); - // Set high byte of divisor. - outportb(PIT_DATAREG0, (divisor >> 8) & PIT_MASK); + // Calculate our divisor. + unsigned int divisor = PIT_DIVISOR / hz; + // Set our command byte 0x36. + outportb(PIT_COMREG, PIT_CONFIGURATION); + // Set low byte of divisor. + outportb(PIT_DATAREG0, divisor & PIT_MASK); + // Set high byte of divisor. + outportb(PIT_DATAREG0, (divisor >> 8u) & PIT_MASK); } void timer_handler(pt_regs *reg) { - (void)reg; - // Check if a second has passed. - if ((++timer_subticks % SUBTICKS_PER_TICK) == 0) { - ++timer_ticks; - } - /* - * if (!list_empty(wakeup_list)) - * { - * listnode_foreach(t, wakeup_list) - * { - * wakeup_info_t *wakeupInfo = (wakeup_info_t *) t->value; - * if (timer_subticks >= wakeupInfo->wakeup_at_jiffy) - * { - * // Call the function. - * wakeupInfo->func(); - * // Reset the timer. - * wakeupInfo->wakeup_at_jiffy = - * timer_subticks + wakeupInfo->period * timer_hz; - * } - * { - * } - */ - - kernel_schedule(reg); - // The ack is sent to PIC only when all handlers terminated! - pic8259_send_eoi(IRQ_TIMER); + // Save current process fpu state. + switch_fpu(); + // Check if a second has passed. + ++timer_ticks; + // Update all timers + run_timer_softirq(); + // Perform the schedule. + scheduler_run(reg); + // Restore fpu state. + unswitch_fpu(); + // The ack is sent to PIC only when all handlers terminated! + pic8259_send_eoi(IRQ_TIMER); } void timer_install() { - // Intialize the wakeup list (before installing the timer). - // wakeup_list = list_create(); - // Set the timer phase. - timer_phase(SUBTICKS_PER_TICK); - // Installs 'timer_handler' to IRQ0. - irq_install_handler(IRQ_TIMER, timer_handler, "timer"); - // Enable the IRQ of the itemer. - pic8259_irq_enable(IRQ_TIMER); + dynamic_timers_install(); + + // Set the timer phase. + timer_phase(TICKS_PER_SECOND); + // Installs 'timer_handler' to IRQ0. + irq_install_handler(IRQ_TIMER, timer_handler, "timer"); + // Enable the IRQ of the timer. + pic8259_irq_enable(IRQ_TIMER); } -uint64_t timer_get_ticks() +uint64_t timer_get_seconds() { - return timer_ticks; + return timer_ticks / TICKS_PER_SECOND; } -uint64_t timer_get_subticks() +unsigned long timer_get_ticks() { - return timer_subticks; + return timer_ticks; } -/* - * void timer_register_wakeup_call(wakeup_callback_t func, uint32_t period) - * { - * - * // Save the function sec, and jiffy to a list, when timer hits that - * // func's jiffy it will call the func, and update next jiffies. - * - * wakeup_info_t *wakeupInfo = kmalloc(sizeof(wakeup_info_t)); - * - * wakeupInfo->func = func; - * - * wakeupInfo->wakeup_at_jiffy = timer_subticks + period * timer_hz; - * - * wakeupInfo->period = period; - * - * list_insert_front(wakeup_list, wakeupInfo); - * } - */ +//====================================================================================== +// Dynamics timers -void sleep(unsigned int seconds) +/// Contains timer for each CPU (for now only one) +static tvec_base_t cpu_base = { 0 }; + +/// Contains all process waiting for a sleep +static wait_queue_head_t sleep_queue; + +/// @brief Initialize dynamic timer system +void dynamic_timers_install() { - uint32_t future_time = timer_ticks + seconds; +#ifndef ENABLE_REAL_TIMER_SYSTEM + list_head_init(&cpu_base.list); +#endif - while (timer_ticks <= future_time) - ; + // Initialize tvec_base structure + tvec_base_t *base = &cpu_base; + base->timer_ticks = 0; + + for(int i = 0; i < TVR_SIZE; ++i) + list_head_init(base->tv1.vec + i); + + for(int i = 0; i < TVN_SIZE; ++i) { + + list_head_init(base->tv2.vec + i); + list_head_init(base->tv3.vec + i); + list_head_init(base->tv4.vec + i); + list_head_init(base->tv5.vec + i); + } + + // Initialize sleeping process list + list_head_init(&sleep_queue.task_list); + spinlock_init(&sleep_queue.lock); } + +/// Prints used slots of timer vector +static void __print_tvec_slots(tvec_base_t *base, int tv_index) { + + if (tv_index < 0 || tv_index > 5) + return; + + // Write buffer + char result[TVN_SIZE + 1]; + result[TVN_SIZE] = '\0'; + + struct timer_vec* tv = NULL; + switch(tv_index) { + + // Root + case 1: { + + pr_debug("base->tv1.vec:"); + for(int i = 0; i < TVR_SIZE; ++i) { + + // New line in order to not clutter the screen + int index = i % TVN_SIZE; + if (i != 0 && index == 0) + pr_debug("\n\t%s", result); + + if (!list_head_empty(base->tv1.vec + i)) + result[index] = '1'; + else + result[index] = '0'; + } + + // The last line + pr_debug("\n\t%s\n", result); + return; + + } break; + + // Normal + case 2: tv = &base->tv2; break; + case 3: tv = &base->tv3; break; + case 4: tv = &base->tv4; break; + case 5: tv = &base->tv5; break; + } + + for(int i = 0; i < TVN_SIZE; ++i) { + if (list_head_empty(tv->vec + i)) + result[i] = '0'; + else + result[i] = '1'; + } + + pr_debug("base->tv%d.vec:\n\t%s\n", tv_index, result); +} + +/// Dump all timer vector in base +static inline void __dump_all_tvec_slots(tvec_base_t *base) { + __print_tvec_slots(base, 1); + __print_tvec_slots(base, 2); + __print_tvec_slots(base, 3); + __print_tvec_slots(base, 4); + __print_tvec_slots(base, 5); +} + +/// Select correct timer vector and position inside of it for the input timer +/// index contains the position inside of the tv_index timer vector +static void __find_tvec(tvec_base_t *base, struct timer_list *timer, int* index, int* tv_index) +{ + assert(index && "index is NULL"); + assert(tv_index && "tv_index is NULL"); + + unsigned long expires = timer->expires; + unsigned long ticks = expires - base->timer_ticks; + + unsigned long tv1_ticks = TIMER_TICKS(0); + unsigned long tv2_ticks = TIMER_TICKS(1); + unsigned long tv3_ticks = TIMER_TICKS(2); + unsigned long tv4_ticks = TIMER_TICKS(3); + + // Can happen if you add a timer with expires == ticks, or in the past + if ((signed long)ticks < 0) { + *index = base->timer_ticks & TVR_MASK; + *tv_index = 1; + } + // tv1 + else if (ticks < tv1_ticks) { + *index = expires & TVR_MASK; + *tv_index = 1; + } + // tv2 + else if (ticks < tv2_ticks) { + *index = (expires >> TIMER_TICKS_BITS(0)) & TVN_MASK; + *tv_index = 2; + } + // tv3 + else if (ticks < tv3_ticks) { + *index = (expires >> TIMER_TICKS_BITS(1)) & TVN_MASK; + *tv_index = 3; + } + // tv4 + else if (ticks < tv4_ticks) { + *index = (expires >> TIMER_TICKS_BITS(2)) & TVN_MASK; + *tv_index = 4; + } + // tv5 + else { + *index = (expires >> TIMER_TICKS_BITS(3)) & TVN_MASK; + *tv_index = 5; + } +} + +/// Add timers into different lists based on their expire time +static void __add_timer_tvec_base(tvec_base_t *base, struct timer_list *timer) { + + int index = 0, tv_index = 0; + __find_tvec(base, timer, &index, &tv_index); + + struct list_head* vec; + switch(tv_index) { + case 1: vec = base->tv1.vec + index; break; + case 2: vec = base->tv2.vec + index; break; + case 3: vec = base->tv3.vec + index; break; + case 4: vec = base->tv4.vec + index; break; + case 5: vec = base->tv5.vec + index; break; + } + + pr_debug("Adding timer at time_index: %d in tv%d\n", index, tv_index); + list_head_add_tail(&timer->entry, vec); + +#ifdef ENABLE_REAL_TIMER_SYSTEM_DUMP + __dump_all_tvec_slots(base); +#endif + +} + +/// Remove timer from tvec_base +static void __rem_timer_tvec_base(tvec_base_t *base, struct timer_list *timer) { + + int index = 0, tv_index = 0; + __find_tvec(base, timer, &index, &tv_index); + + struct list_head* vec; + switch(tv_index) { + case 1: vec = base->tv1.vec + index; break; + case 2: vec = base->tv2.vec + index; break; + case 3: vec = base->tv3.vec + index; break; + case 4: vec = base->tv4.vec + index; break; + case 5: vec = base->tv5.vec + index; break; + } + + pr_debug("Removing timer at time_index: %d in tv%d\n", index, tv_index); + list_head_del(&timer->entry); + +#ifdef ENABLE_REAL_TIMER_SYSTEM_DUMP + __dump_all_tvec_slots(base); +#endif + +} + +/// Move all timers from tv up one level +static int cascate(tvec_base_t* base, timer_vec* tv, int time_index, int tv_index) { + + if (!list_head_empty(tv->vec + time_index)) { + pr_debug("Relocating timers in tv%d.vec[%d]\n", tv_index, time_index); + + // Reinsert all timers into base in the new correct list + struct list_head *it, *tmp; + list_for_each_safe (it, tmp, tv->vec + time_index) { + struct timer_list *timer = list_entry(it, struct timer_list, entry); + list_head_del(it); + + __add_timer_tvec_base(base, timer); + } + + } + return time_index; +} + +void run_timer_softirq() +{ + tvec_base_t *base = &cpu_base; + spinlock_lock(&base->lock); + +#ifdef ENABLE_REAL_TIMER_SYSTEM + + // While we are not up to date with current ticks + unsigned long current_ticks = timer_get_ticks(); + while (base->timer_ticks <= current_ticks) { + + // Index of the current timer to execute + int current_time_index = base->timer_ticks & TVR_MASK; + + // If the index is zero then all lists in base->tv1 have been checked, so they are empty + if (!current_time_index) { + + // Consider the first invocation of the cascade() function: it receives as arguments + // the address in base, the address of base->tv2, and the index of the list + // in base->tv2 including the timers that will decay in the next 256 ticks. This + // index is determined by looking at the proper bits of the base->timer_ticks value. + // cascade() moves all dynamic timers in the base->tv2 list into the + // proper lists of base->tv1; then, it returns a positive value, unless all base->tv2 + // lists are now empty. If so, cascade() is invoked once more to replenish + // base->tv2 with the timers included in a list of base->tv3, and so on. + + int tv2_index = (base->timer_ticks >> TIMER_TICKS_BITS(0)) & TVN_MASK; + int tv3_index = (base->timer_ticks >> TIMER_TICKS_BITS(1)) & TVN_MASK; + int tv4_index = (base->timer_ticks >> TIMER_TICKS_BITS(2)) & TVN_MASK; + int tv5_index = (base->timer_ticks >> TIMER_TICKS_BITS(3)) & TVN_MASK; + + if (!cascate(base, &base->tv2, tv2_index, 2) && + !cascate(base, &base->tv3, tv3_index, 3) && + !cascate(base, &base->tv4, tv4_index, 4) && + !cascate(base, &base->tv5, tv5_index, 5)); + } + + // If there are timers to execute in this instant + if (!list_head_empty(&base->tv1.vec[current_time_index])) { + pr_notice("Executing dynamic timers at %d ticks from start inside of tv1.vec[%d]\n", + base->timer_ticks, current_time_index); + + // Trigger all timers + struct list_head *it, *tmp; + list_for_each_safe (it, tmp, &base->tv1.vec[current_time_index]) { + struct timer_list *timer = list_entry(it, struct timer_list, entry); + + // Executes timer function + spinlock_unlock(&base->lock); + pr_notice("Executing dynamic timer function...\n"); + timer->function(timer->data); + spinlock_lock(&base->lock); + + // Removes timer from list + list_head_del(it); + kfree(timer); + } + } + + // Advance timer check + ++base->timer_ticks; + } + + base->running_timer = NULL; + +#else + + struct list_head *it, *tmp; + list_for_each_safe (it, tmp, &base->list) { + struct timer_list *timer = list_entry(it, struct timer_list, entry); + if (timer->expires <= timer_get_ticks()) { + base->running_timer = timer; + timer->base = NULL; + + // Executes timer function + spinlock_unlock(&base->lock); + pr_notice("Executing dynamic timer function...\n"); + timer->function(timer->data); + spinlock_lock(&base->lock); + + // Removes timer from list + pr_notice("Removing dynamic timer...\n"); + list_head_del(it); + kfree(timer); + } + } + +#endif + + base->running_timer = NULL; + spinlock_unlock(&base->lock); +} + +void init_timer(struct timer_list *timer) +{ + timer->base = NULL; + list_head_init(&timer->entry); + spinlock_unlock(&timer->lock); +} + +void add_timer(struct timer_list *timer) +{ + tvec_base_t *base = &cpu_base; + timer->base = base; + +#ifdef ENABLE_REAL_TIMER_SYSTEM + __add_timer_tvec_base(base, timer); +#else + list_head_add_tail(&timer->entry, &base->list); +#endif + +} + +void del_timer(struct timer_list *timer) +{ + tvec_base_t *base = &cpu_base; + timer->base = NULL; + +#ifdef ENABLE_REAL_TIMER_SYSTEM + __rem_timer_tvec_base(base, timer); +#else + list_head_del(&timer->entry); +#endif + +} + +//====================================================================================== +// Sleep + +/// @brief Debugging function. +/// @param data The data. +static inline void debug_timeout(unsigned long data) +{ + pr_notice("Il timer è stato attivato con successo: %d, ticks: %d, seconds: %d\n", + data, timer_ticks, timer_get_seconds()); +} + +/// @brief Contains the entry of a wait queue and timespec which keeps trakc of +/// the remaining time. +typedef struct sleep_data_t { + /// POinter to the entry of a wait queue. + wait_queue_entry_t *entry; + /// Keeps track of the remaining time. + timespec *rem; +} sleep_data_t; + +/// @brief Callback for when a sleep timer expires +/// @param data Custom data stored in the timer +void sleep_timeout(unsigned long data) +{ + // NOTE: We could modify the sleep_on and make it return the wait_queue_entry_t + // and then store it in the dynamic timer data member instead of the task pid, + // this would remove the need to iterate the sleep queue list. + + sleep_data_t *sleep_data = (sleep_data_t *)data; + + wait_queue_entry_t *entry = sleep_data->entry; + task_struct *task = entry->task; + + // Executed entry's wakeup test function + int res = entry->func(entry, 0, 0); + if (res == 1) { + // Removes entry from list and memory + remove_wait_queue(&sleep_queue, entry); + kfree(entry); + + pr_debug("Process (pid: %d) restored from sleep\n", task->pid); + } +} + +int sys_nanosleep(const timespec *req, timespec *rem) +{ + // Probabilmente devi salvare rem da qualche parte, perche' dentro ci va + // messo quanto tempo mancava allo scadere del timer nel caso in cui il + // timer venga interrotto prima da un segnale. + pr_debug("sys_nanosleep([s:%d; ns:%d],...)\n", req->tv_sec, req->tv_nsec); + + // Saves pid and rem timespec + sleep_data_t *data = kmalloc(sizeof(sleep_data_t)); + data->rem = rem; + + // Create a dinamic timer to wake up the process after some time + struct timer_list *sleep_timer = kmalloc(sizeof(struct timer_list)); + init_timer(sleep_timer); + + sleep_timer->expires = timer_get_ticks() + TICKS_PER_SECOND * req->tv_sec; + sleep_timer->function = &sleep_timeout; + sleep_timer->data = (unsigned long)data; + + // Removes current process from runqueue and stores it in the waiting queue, + // this must be done at the end, because it changes the current active page + // and invalidates the req and rem pointers (?) + wait_queue_entry_t *entry = sleep_on(&sleep_queue); + data->entry = entry; + + add_timer(sleep_timer); + return -1; +} + +/// @brief Function executed when the real_timer of a process expires, sends SIGALRM to process. +/// @param pid PID of the process whos associated timer has expired +void alarm_timeout(unsigned long pid) +{ + sys_kill(pid, SIGALRM); + + struct task_struct *cur = scheduler_get_current_process(); + cur->real_timer = NULL; +} + +int sys_alarm(int seconds) +{ + pr_debug("sys_alarm(seconds:%d)\n", seconds); + + struct task_struct *current = scheduler_get_current_process(); + struct timer_list *timer; + + // If there is already a timer running + int result = 0; + if (current->real_timer != NULL) { + del_timer(current->real_timer); + result = (current->real_timer->expires - timer_get_ticks()) / TICKS_PER_SECOND; + timer = current->real_timer; + + // Returns only the amount of seconds remaining + if (seconds == 0) { + kfree(current->real_timer); + current->real_timer = NULL; + return result; + } + } else { + if (seconds == 0) + return 0; + + // Allocate new timer + timer = (struct timer_list *)kmalloc(sizeof(struct timer_list)); + } + + current->real_timer = timer; + init_timer(timer); + + timer->expires = timer_get_ticks() + TICKS_PER_SECOND * seconds; + timer->function = &alarm_timeout; + timer->data = current->pid; + + add_timer(timer); + + return result; +} + +static void calc_itimerval(unsigned long incr, unsigned long value, struct itimerval *result) +{ + result->it_interval.tv_sec = incr / TICKS_PER_SECOND; + result->it_interval.tv_usec = incr / TICKS_PER_SECOND * 1000; + + result->it_value.tv_sec = value / TICKS_PER_SECOND; + result->it_value.tv_usec = value / TICKS_PER_SECOND * 1000; +} + +static void update_task_itimerval(int which, const struct itimerval *val) +{ + unsigned long interval_ticks = val->it_interval.tv_sec * TICKS_PER_SECOND; + interval_ticks += val->it_interval.tv_usec * TICKS_PER_SECOND / 1000; + + unsigned long value_ticks = val->it_value.tv_sec * TICKS_PER_SECOND; + value_ticks += val->it_value.tv_usec * TICKS_PER_SECOND / 1000; + + struct task_struct *curr = scheduler_get_current_process(); + switch (which) { + case ITIMER_REAL: + curr->it_real_incr = interval_ticks; + curr->it_real_value = value_ticks; + break; + + case ITIMER_VIRTUAL: + curr->it_virt_incr = interval_ticks; + curr->it_virt_value = value_ticks; + break; + + case ITIMER_PROF: + curr->it_prof_incr = interval_ticks; + curr->it_prof_value = value_ticks; + break; + } +} + +int sys_getitimer(int which, struct itimerval *curr_value) +{ + // Invalid time domain + if (which < 0 || which > 3) + return EINVAL; + + struct task_struct *curr = scheduler_get_current_process(); + switch (which) { + case ITIMER_REAL: { + // Extract remaining time in dynamic timer + unsigned long value = curr->real_timer->expires - timer_get_ticks(); + curr->it_real_value = value; + calc_itimerval(curr->it_real_incr, curr->it_real_value, curr_value); + } break; + case ITIMER_VIRTUAL: + calc_itimerval(curr->it_virt_incr, curr->it_virt_value, curr_value); + break; + case ITIMER_PROF: + calc_itimerval(curr->it_prof_incr, curr->it_prof_value, curr_value); + break; + } + return 0; +} + +// Real timer interval timemout +static void it_real_fn(unsigned long pid) +{ + struct task_struct *cur = scheduler_get_running_process(pid); + sys_kill(pid, SIGALRM); + + // If the real incr is not 0 then restart + if (cur->it_real_incr != 0) { + // Create new timer for process + struct timer_list *real_timer = (struct timer_list *)kmalloc(sizeof(struct timer_list)); + cur->real_timer = real_timer; + init_timer(real_timer); + + real_timer->expires = timer_get_ticks() + cur->it_real_incr; + real_timer->function = &it_real_fn; + real_timer->data = cur->pid; + + add_timer(real_timer); + return; + } + + // No more timer + cur->real_timer = NULL; +} + +int sys_setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value) +{ + // Invalid time domain + if (which < 0 || which > 3) + return EINVAL; + + // Returns old timer interval + if (old_value != NULL) + sys_getitimer(which, old_value); + + // Get ticks of interval + unsigned long interval_ticks = new_value->it_interval.tv_sec * TICKS_PER_SECOND; + interval_ticks += new_value->it_interval.tv_usec * TICKS_PER_SECOND / 1000; + + // If interval is 0 removes timer + struct task_struct *cur = scheduler_get_current_process(); + if (interval_ticks == 0) { + // Removes real_timer + if (which == ITIMER_REAL && cur->real_timer != NULL) + cur->real_timer = NULL; + + update_task_itimerval(which, new_value); + return -1; + } + + switch (which) { + // Uses Dynamic Timers + case ITIMER_REAL: { + // Remove real_timer if already in use + struct timer_list *timer = cur->real_timer; + if (timer != NULL) { + del_timer(timer); // Recycle memory + } else { + // Alloc new timer + timer = (struct timer_list *)kmalloc(sizeof(struct timer_list)); + } + + init_timer(timer); + + timer->expires = timer_get_ticks() + interval_ticks; + timer->function = &it_real_fn; + timer->data = cur->pid; + + add_timer(timer); + + } break; + + case ITIMER_VIRTUAL: + case ITIMER_PROF: + break; + } + + update_task_itimerval(which, new_value); + return -1; +} + +void update_process_profiling_timer(task_struct *proc) +{ + // If the timer is active + if (proc->it_prof_incr != 0) { + proc->it_prof_value += proc->se.exec_runtime; + if (proc->it_prof_value >= proc->it_prof_incr) { + sys_kill(proc->pid, SIGPROF); + proc->it_prof_value = 0; + } + } +} \ No newline at end of file diff --git a/mentos/src/io/mm_io.c b/mentos/src/io/mm_io.c index b39caa1..219f776 100644 --- a/mentos/src/io/mm_io.c +++ b/mentos/src/io/mm_io.c @@ -1,37 +1,37 @@ /// MentOS, The Mentoring Operating system project /// @file mm_io.c /// @brief Memory Mapped IO functions implementation. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "mm_io.h" +#include "io/mm_io.h" uint8_t in_memb(uint32_t addr) { - return *((uint8_t *) (addr)); + return *((uint8_t *)(addr)); } uint16_t in_mems(uint32_t addr) { - return *((uint16_t *) (addr)); + return *((uint16_t *)(addr)); } uint32_t in_meml(uint32_t addr) { - return *((uint32_t *) (addr)); + return *((uint32_t *)(addr)); } void out_memb(uint32_t addr, uint8_t value) { - (*((uint8_t *) (addr))) = (value); + (*((uint8_t *)(addr))) = (value); } void out_mems(uint32_t addr, uint16_t value) { - (*((uint16_t *) (addr))) = (value); + (*((uint16_t *)(addr))) = (value); } void out_meml(uint32_t addr, uint32_t value) { - (*((uint32_t *) (addr))) = (value); + (*((uint32_t *)(addr))) = (value); } diff --git a/mentos/src/io/port_io.c b/mentos/src/io/port_io.c index edfb4bd..9dfe3ff 100644 --- a/mentos/src/io/port_io.c +++ b/mentos/src/io/port_io.c @@ -1,15 +1,17 @@ /// MentOS, The Mentoring Operating system project /// @file port_io.c /// @brief Byte I/O on ports prototypes. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "port_io.h" +#include "io/port_io.h" inline uint8_t inportb(uint16_t port) { unsigned char data = 0; - __asm__ __volatile__("inb %%dx, %%al" : "=a" (data) : "d" (port)); + __asm__ __volatile__("inb %%dx, %%al" + : "=a"(data) + : "d"(port)); return data; } @@ -17,40 +19,53 @@ inline uint8_t inportb(uint16_t port) inline uint16_t inports(uint16_t port) { uint16_t rv; - __asm__ __volatile__("inw %1, %0" : "=a" (rv) : "dN" (port)); + __asm__ __volatile__("inw %1, %0" + : "=a"(rv) + : "dN"(port)); return rv; } -void inportsm(uint16_t port, uint8_t * data, unsigned long size) +void inportsm(uint16_t port, uint8_t *data, unsigned long size) { - __asm__ __volatile__("rep insw" : "+D" (data), "+c" (size) : "d" (port) : "memory"); + __asm__ __volatile__("rep insw" + : "+D"(data), "+c"(size) + : "d"(port) + : "memory"); } inline uint32_t inportl(uint16_t port) { uint32_t rv; - __asm__ __volatile__("inl %%dx, %%eax" : "=a" (rv) : "dN" (port)); + __asm__ __volatile__("inl %%dx, %%eax" + : "=a"(rv) + : "dN"(port)); return rv; } inline void outportb(uint16_t port, uint8_t data) { - __asm__ __volatile__("outb %%al, %%dx"::"a" (data), "d" (port)); + __asm__ __volatile__("outb %%al, %%dx" ::"a"(data), "d"(port)); } inline void outports(uint16_t port, uint16_t data) { - __asm__ __volatile__("outw %1, %0" : : "dN" (port), "a" (data)); + __asm__ __volatile__("outw %1, %0" + : + : "dN"(port), "a"(data)); } void outportsm(uint16_t port, uint8_t *data, uint16_t size) { - asm volatile ("rep outsw" : "+S" (data), "+c" (size) : "d" (port)); + asm volatile("rep outsw" + : "+S"(data), "+c"(size) + : "d"(port)); } inline void outportl(uint16_t port, uint32_t data) { - __asm__ __volatile__("outl %%eax, %%dx" : : "dN" (port), "a" (data)); + __asm__ __volatile__("outl %%eax, %%dx" + : + : "dN"(port), "a"(data)); } diff --git a/mentos/src/io/proc_running.c b/mentos/src/io/proc_running.c new file mode 100644 index 0000000..916fd70 --- /dev/null +++ b/mentos/src/io/proc_running.c @@ -0,0 +1,488 @@ +// +// Created by andrea on 02/05/20. +// + +#include "fs/procfs.h" + +#include "process/process.h" +#include "string.h" +#include "libgen.h" +#include "stdio.h" +#include "sys/errno.h" +#include "misc/debug.h" +#include "process/prio.h" + +/// @brief Returns the character identifying the process state. +/// @details +/// R Running +/// S Sleeping in an interruptible wait +/// D Waiting in uninterruptible disk sleep +/// Z Zombie +/// T Stopped +/// t Tracing stop +/// X Dead +static inline char get_task_state_char(int state) +{ + if (state == 0x00) // TASK_RUNNING + return 'R'; + if (state == (1 << 0)) // TASK_INTERRUPTIBLE + return 'S'; + if (state == (1 << 1)) // TASK_UNINTERRUPTIBLE + return 'D'; + if (state == (1 << 2)) // TASK_STOPPED + return 'T'; + if (state == (1 << 3)) // TASK_TRACED + return 't'; + if (state == (1 << 4)) // EXIT_ZOMBIE + return 'Z'; + if (state == (1 << 5)) // EXIT_DEAD + return 'X'; + return '?'; +} + +static ssize_t procr_do_cmdline(char *buffer, size_t bufsize, task_struct *task); + +static ssize_t procr_do_stat(char *buffer, size_t bufsize, task_struct *task); + +static ssize_t procr_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyte) +{ + if (file == NULL) + return -EFAULT; + // Get the entry. + proc_dir_entry_t *entry = (proc_dir_entry_t *)file->device; + if (entry == NULL) + return -EFAULT; + // Get the task. + task_struct *task = (task_struct *)entry->data; + if (task == NULL) + return -EFAULT; + // Prepare a buffer. + char buffer[BUFSIZ]; + memset(buffer, 0, BUFSIZ); + // Call the specific function. + int ret = 0; + if (strcmp(entry->name, "cmdline") == 0) + ret = procr_do_cmdline(buffer, BUFSIZ, task); + else if (strcmp(entry->name, "stat") == 0) + ret = procr_do_stat(buffer, BUFSIZ, task); + // Perform read. + ssize_t it = 0; + if (ret == 1) { + size_t name_len = strlen(buffer); + size_t read_pos = offset; + if (read_pos < name_len) { + while ((it < nbyte) && (read_pos < name_len)) { + *buf++ = buffer[read_pos]; + ++read_pos; + ++it; + } + } + } + return it; +} + +/// Filesystem general operations. +static vfs_sys_operations_t procv_sys_operations = { + .mkdir_f = NULL, + .rmdir_f = NULL, + .stat_f = NULL +}; + +/// Filesystem file operations. +static vfs_file_operations_t procv_fs_operations = { + .open_f = NULL, + .unlink_f = NULL, + .close_f = NULL, + .read_f = procr_read, + .write_f = NULL, + .lseek_f = NULL, + .stat_f = NULL, + .ioctl_f = NULL, + .getdents_f = NULL +}; + +int proc_create_entry_pid(task_struct *entry) +{ + char path[PATH_MAX]; + proc_dir_entry_t *proc_dir = NULL, *proc_entry = NULL; + { + // Create `/proc/[PID]`. + sprintf(path, "%d", entry->pid); + // Create the proc entry root directory. + if ((proc_dir = proc_mkdir(path, NULL)) == NULL) { + pr_err("[task: %d] Cannot create proc root directory `%s`.\n", entry->pid, path); + return -ENOENT; + } + proc_dir->data = entry; + } + { + // Create `/proc/[PID]/cmdline`. + if ((proc_entry = proc_create_entry("cmdline", proc_dir)) == NULL) { + pr_err("[task: %d] Cannot create proc entry `%s`.\n", entry->pid, path); + return -ENOENT; + } + proc_entry->sys_operations = &procv_sys_operations; + proc_entry->fs_operations = &procv_fs_operations; + proc_entry->data = entry; + } + { + // Create `/proc/[PID]/stat`. + if ((proc_entry = proc_create_entry("stat", proc_dir)) == NULL) { + pr_err("[task: %d] Cannot create proc entry `%s`.\n", entry->pid, path); + return -ENOENT; + } + proc_entry->sys_operations = &procv_sys_operations; + proc_entry->fs_operations = &procv_fs_operations; + proc_entry->data = entry; + } + return 0; +} + +int proc_destroy_entry_pid(task_struct *entry) +{ + // Turn the pid into string. The maximum pid is 32768, thus entry pid is at most 6 chars. + char pid_str[6]; + sprintf(pid_str, "%d", entry->pid); + // Get the root directory. + proc_dir_entry_t *proc_dir = proc_dir_entry_get(pid_str, NULL); + if (proc_dir == NULL) { + pr_err("[task: %d] Cannot find proc root directory `%s`.\n", entry->pid, pid_str); + return -ENOENT; + } + // Destroy `/proc/[PID]/cmdline`. + if (proc_destroy_entry("cmdline", proc_dir)) { + pr_err("[task: %d] Cannot destroy proc cmdline.\n", entry->pid); + return -ENOENT; + } + // Destroy `/proc/[PID]/stat`. + if (proc_destroy_entry("stat", proc_dir)) { + pr_err("[task: %d] Cannot destroy proc stat.\n", entry->pid); + return -ENOENT; + } + // Destroy `/proc/[PID]`. + if (proc_rmdir(pid_str, NULL)) { + pr_err("[task: %d] Cannot remove proc root directory `%s`.\n", entry->pid, pid_str); + return -ENOENT; + } + return 0; +} + +static ssize_t procr_do_cmdline(char *buffer, size_t bufsize, task_struct *task) +{ + strcpy(buffer, task->name); + return 1; +} + +static ssize_t procr_do_stat(char *buffer, size_t bufsize, task_struct *task) +{ + //(1) pid %d + // The process ID. + // + sprintf(buffer, "%d", task->pid); + //(2) comm %s + // The filename of the executable, in parentheses. + // Strings longer than TASK_COMM_LEN (16) characters (in‐ + // cluding the terminating null byte) are silently trun‐ + // cated. This is visible whether or not the executable + // is swapped out. + // + sprintf(buffer, "%s (%s)", buffer, basename(task->name)); + //(3) state %c + // One of the following characters, indicating process state: + // R Running + // S Sleeping in an interruptible wait + // D Waiting in uninterruptible disk sleep + // Z Zombie + // T Stopped + // t Tracing stop + // X Dead + sprintf(buffer, "%s %c", buffer, get_task_state_char(task->state)); + //(4) ppid %d + // The PID of the parent of this process. + // + if (task->parent) + sprintf(buffer, "%s %d", buffer, task->parent->pid); + else + strcat(buffer, " 0"); + //(5) TODO: pgrp %d + // The process group ID of the process. + // + strcat(buffer, " 0"); + //(6) TODO: session %d + // The session ID of the process. + // + strcat(buffer, " 0"); + //(7) TODO: tty_nr %d + // The controlling terminal of the process. (The minor + // device number is contained in the combination of bits + // 31 to 20 and 7 to 0; the major device number is in bits + // 15 to 8.) + // + strcat(buffer, " 0"); + //(8) TODO: tpgid %d + // The ID of the foreground process group of the control‐ + // ling terminal of the process. + // + strcat(buffer, " 0"); + //(9) TODO: flags %u + // The kernel flags word of the process. For bit mean‐ + // ings, see the PF_* defines in the Linux kernel source + // file include/linux/sched.h. Details depend on the ker‐ + // nel version. + // The format for this field was %lu before Linux 2.6. + // + strcat(buffer, " 0"); + //(10) TODO: minflt %lu + // The number of minor faults the process has made which + // have not required loading a memory page from disk. + // + strcat(buffer, " 0"); + //(11) TODO: cminflt %lu + // The number of minor faults that the process's waited- + // for children have made. + // + strcat(buffer, " 0"); + //(12) TODO: majflt %lu + // The number of major faults the process has made which + // have required loading a memory page from disk. + // + strcat(buffer, " 0"); + //(13) TODO: cmajflt %lu + // The number of major faults that the process's waited- + // for children have made. + // + strcat(buffer, " 0"); + //(14) TODO: utime %lu + // Amount of time that this process has been scheduled in + // user mode, measured in clock ticks (divide by + // sysconf(_SC_CLK_TCK)). This includes guest time, + // guest_time (time spent running a virtual CPU, see be‐ + // low), so that applications that are not aware of the + // guest time field do not lose that time from their cal‐ + // culations. + // + strcat(buffer, " 0"); + //(15) TODO: stime %lu + // Amount of time that this process has been scheduled in + // kernel mode, measured in clock ticks (divide by + // sysconf(_SC_CLK_TCK)). + // + strcat(buffer, " 0"); + //(16) TODO: cutime %ld + // Amount of time that this process's waited-for children + // have been scheduled in user mode, measured in clock + // ticks (divide by sysconf(_SC_CLK_TCK)). (See also + // times(2).) This includes guest time, cguest_time (time + // spent running a virtual CPU, see below). + // + strcat(buffer, " 0"); + //(17) TODO: cstime %ld + // Amount of time that this process's waited-for children + // have been scheduled in kernel mode, measured in clock + // ticks (divide by sysconf(_SC_CLK_TCK)). + // + strcat(buffer, " 0"); + //(18) priority %ld + // (Explanation for Linux 2.6) For processes running a + // real-time scheduling policy (policy below; see + // sched_setscheduler(2)), this is the negated scheduling + // priority, minus one; that is, a number in the range -2 + // to -100, corresponding to real-time priorities 1 to 99. + // For processes running under a non-real-time scheduling + // policy, this is the raw nice value (setpriority(2)) as + // represented in the kernel. The kernel stores nice val‐ + // ues as numbers in the range 0 (high) to 39 (low), cor‐ + // responding to the user-visible nice range of -20 to 19. + // + // Before Linux 2.6, this was a scaled value based on the + // scheduler weighting given to this process. + // + sprintf(buffer, "%s %ld", buffer, task->se.prio); + //(19) nice %ld + // The nice value (see setpriority(2)), a value in the + // range 19 (low priority) to -20 (high priority). + // + sprintf(buffer, "%s %ld", buffer, PRIO_TO_NICE(task->se.prio)); + //(20) TODO: num_threads %ld + // Number of threads in this process (since Linux 2.6). + // Before kernel 2.6, this field was hard coded to 0 as a + // placeholder for an earlier removed field. + // + strcat(buffer, " 0"); + //(21) TODO: itrealvalue %ld + // The time in jiffies before the next SIGALRM is sent to + // the process due to an interval timer. Since kernel + // 2.6.17, this field is no longer maintained, and is hard + // coded as 0. + // + strcat(buffer, " 0"); + //(22) starttime %llu + // The time the process started after system boot. In + // kernels before Linux 2.6, this value was expressed in + // jiffies. Since Linux 2.6, the value is expressed in + // clock ticks (divide by sysconf(_SC_CLK_TCK)). + // + // The format for this field was %lu before Linux 2.6. + // + sprintf(buffer, "%s %lu", buffer, task->se.exec_start); + //(23) vsize %lu + // Virtual memory size in bytes. + // + sprintf(buffer, "%s %lu", buffer, task->mm->total_vm); + //(24) TODO: rss %ld + // Resident Set Size: number of pages the process has in + // real memory. This is just the pages which count toward + // text, data, or stack space. This does not include + // pages which have not been demand-loaded in, or which + // are swapped out. This value is inaccurate; see + // /proc/[pid]/statm below. + // + strcat(buffer, " 0"); + //(25) TODO: rsslim %lu + // Current soft limit in bytes on the rss of the process; + // see the description of RLIMIT_RSS in getrlimit(2). + // + strcat(buffer, " 0"); + //(26) startcode %lu [PT] + // The address above which program text can run. + // + sprintf(buffer, "%s %lu", buffer, task->mm->start_code); + //(27) endcode %lu [PT] + // The address below which program text can run. + // + sprintf(buffer, "%s %lu", buffer, task->mm->end_code); + //(28) startstack %lu [PT] + // The address of the start (i.e., bottom) of the stack. + // + sprintf(buffer, "%s %lu", buffer, task->mm->start_stack); + //(29) kstkesp %lu [PT] + // The current value of ESP (stack pointer), as found in + // the kernel stack page for the process. + // + sprintf(buffer, "%s %lu", buffer, task->thread.regs.useresp); + //(30) kstkeip %lu [PT] + // The current EIP (instruction pointer). + // + sprintf(buffer, "%s %lu", buffer, task->thread.regs.eip); + //(31) TODO: signal %lu + // The bitmap of pending signals, displayed as a decimal + // number. Obsolete, because it does not provide informa‐ + // tion on real-time signals; use /proc/[pid]/status in‐ + // stead. + // + strcat(buffer, " 0"); + //(32) TODO: blocked %lu + // The bitmap of blocked signals, displayed as a decimal + // number. Obsolete, because it does not provide informa‐ + // tion on real-time signals; use /proc/[pid]/status in‐ + // stead. + // + strcat(buffer, " 0"); + //(33) TODO: sigignore %lu + // The bitmap of ignored signals, displayed as a decimal + // number. Obsolete, because it does not provide informa‐ + // tion on real-time signals; use /proc/[pid]/status in‐ + // stead. + // + strcat(buffer, " 0"); + //(34) TODO: sigcatch %lu + // The bitmap of caught signals, displayed as a decimal + // number. Obsolete, because it does not provide informa‐ + // tion on real-time signals; use /proc/[pid]/status in‐ + // stead. + // + strcat(buffer, " 0"); + //(35) TODO: wchan %lu [PT] + // This is the "channel" in which the process is waiting. + // It is the address of a location in the kernel where the + // process is sleeping. The corresponding symbolic name + // can be found in /proc/[pid]/wchan. + // + strcat(buffer, " 0"); + //(36) TODO: nswap %lu + // Number of pages swapped (not maintained). + // + strcat(buffer, " 0"); + //(37) TODO: cnswap %lu + // Cumulative nswap for child processes (not maintained). + // + strcat(buffer, " 0"); + //(38) TODO: exit_signal %d (since Linux 2.1.22) + // Signal to be sent to parent when we die. + // + strcat(buffer, " 0"); + //(39) TODO: processor %d (since Linux 2.2.8) + // CPU number last executed on. + // + strcat(buffer, " 0"); + //(40) TODO: rt_priority %u (since Linux 2.5.19) + // Real-time scheduling priority, a number in the range 1 + // to 99 for processes scheduled under a real-time policy, + // or 0, for non-real-time processes (see + // sched_setscheduler(2)). + // + if (task->se.prio >= 100) + strcat(buffer, " 0"); + else + sprintf(buffer, "%s %u", buffer, task->se.prio); + //(41) TODO: policy %u (since Linux 2.5.19) + // Scheduling policy (see sched_setscheduler(2)). Decode + // using the SCHED_* constants in linux/sched.h. + // The format for this field was %lu before Linux 2.6.22. + // + strcat(buffer, " 0"); + //(42) TODO: delayacct_blkio_ticks %llu (since Linux 2.6.18) + // Aggregated block I/O delays, measured in clock ticks + // (centiseconds). + // + strcat(buffer, " 0"); + //(43) TODO: guest_time %lu (since Linux 2.6.24) + // Guest time of the process (time spent running a virtual + // CPU for a guest operating system), measured in clock + // ticks (divide by sysconf(_SC_CLK_TCK)). + // + strcat(buffer, " 0"); + //(44) TODO: cguest_time %ld (since Linux 2.6.24) + // Guest time of the process's children, measured in clock + // ticks (divide by sysconf(_SC_CLK_TCK)). + // + strcat(buffer, " 0"); + //(45) start_data %lu (since Linux 3.3) [PT] + // Address above which program initialized and uninitial‐ + // ized (BSS) data are placed. + // + sprintf(buffer, "%s %lu", buffer, task->mm->start_data); + //(46) end_data %lu (since Linux 3.3) [PT] + // Address below which program initialized and uninitial‐ + // ized (BSS) data are placed. + // + sprintf(buffer, "%s %lu", buffer, task->mm->end_data); + //(47) start_brk %lu (since Linux 3.3) [PT] + // Address above which program heap can be expanded with + // brk(2). + // + sprintf(buffer, "%s %lu", buffer, task->mm->start_brk); + //(48) arg_start %lu (since Linux 3.5) [PT] + // Address above which program command-line arguments + // (argv) are placed. + // + sprintf(buffer, "%s %lu", buffer, task->mm->arg_start); + //(49) arg_end %lu (since Linux 3.5) [PT] + // Address below program command-line arguments (argv) are + // placed. + // + sprintf(buffer, "%s %lu", buffer, task->mm->arg_end); + //(50) env_start %lu (since Linux 3.5) [PT] + // Address above which program environment is placed. + // + sprintf(buffer, "%s %lu", buffer, task->mm->env_start); + //(51) env_end %lu (since Linux 3.5) [PT] + // Address below which program environment is placed. + // + sprintf(buffer, "%s %lu", buffer, task->mm->env_end); + //(52) exit_code %d (since Linux 3.5) [PT] + // The thread's exit status in the form reported by + // waitpid(2). + sprintf(buffer, "%s %d\n", buffer, task->exit_code); + return 1; +} \ No newline at end of file diff --git a/mentos/src/io/proc_system.c b/mentos/src/io/proc_system.c new file mode 100644 index 0000000..9e90927 --- /dev/null +++ b/mentos/src/io/proc_system.c @@ -0,0 +1,206 @@ +/// MentOS, The Mentoring Operating system project +/// @file proc_system.c +/// @brief Contains callbacks for procfs system files. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "fs/procfs.h" +#include "version.h" +#include "process/process.h" +#include "string.h" +#include "stdio.h" +#include "sys/errno.h" +#include "misc/debug.h" +#include "hardware/timer.h" + +static ssize_t procs_do_uptime(char *buffer, size_t bufsize); + +static ssize_t procs_do_version(char *buffer, size_t bufsize); + +static ssize_t procs_do_mounts(char *buffer, size_t bufsize); + +static ssize_t procs_do_cpuinfo(char *buffer, size_t bufsize); + +static ssize_t procs_do_meminfo(char *buffer, size_t bufsize); + +static ssize_t procs_do_stat(char *buffer, size_t bufsize); + +static ssize_t procs_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyte) +{ + if (file == NULL) + return -EFAULT; + proc_dir_entry_t *entry = (proc_dir_entry_t *)file->device; + if (entry == NULL) + return -EFAULT; + // Prepare a buffer. + char buffer[BUFSIZ]; + memset(buffer, 0, BUFSIZ); + // Call the specific function. + int ret = 0; + if (strcmp(entry->name, "uptime") == 0) + ret = procs_do_uptime(buffer, BUFSIZ); + else if (strcmp(entry->name, "version") == 0) + ret = procs_do_version(buffer, BUFSIZ); + else if (strcmp(entry->name, "mounts") == 0) + ret = procs_do_mounts(buffer, BUFSIZ); + else if (strcmp(entry->name, "cpuinfo") == 0) + ret = procs_do_cpuinfo(buffer, BUFSIZ); + else if (strcmp(entry->name, "meminfo") == 0) + ret = procs_do_meminfo(buffer, BUFSIZ); + else if (strcmp(entry->name, "stat") == 0) + ret = procs_do_stat(buffer, BUFSIZ); + // Perform read. + ssize_t it = 0; + if (ret == 0) { + size_t name_len = strlen(buffer); + size_t read_pos = offset; + if (read_pos < name_len) { + while ((it < nbyte) && (read_pos < name_len)) { + *buf++ = buffer[read_pos]; + ++read_pos; + ++it; + } + } + } + return it; +} + +/// Filesystem general operations. +static vfs_sys_operations_t procs_sys_operations = { + .mkdir_f = NULL, + .rmdir_f = NULL, + .stat_f = NULL +}; + +/// Filesystem file operations. +static vfs_file_operations_t procs_fs_operations = { + .open_f = NULL, + .unlink_f = NULL, + .close_f = NULL, + .read_f = procs_read, + .write_f = NULL, + .lseek_f = NULL, + .stat_f = NULL, + .ioctl_f = NULL, + .getdents_f = NULL +}; + +int procs_module_init() +{ + proc_dir_entry_t *system_entry; + // == /proc/uptime ======================================================== + if ((system_entry = proc_create_entry("uptime", NULL)) == NULL) { + pr_err("Cannot create `/proc/uptime`.\n"); + return 1; + } + pr_debug("Created `/proc/uptime` (%p)\n", system_entry); + // Set the specific operations. + system_entry->sys_operations = &procs_sys_operations; + system_entry->fs_operations = &procs_fs_operations; + + // == /proc/version ======================================================== + if ((system_entry = proc_create_entry("version", NULL)) == NULL) { + pr_err("Cannot create `/proc/version`.\n"); + return 1; + } + pr_debug("Created `/proc/version` (%p)\n", system_entry); + // Set the specific operations. + system_entry->sys_operations = &procs_sys_operations; + system_entry->fs_operations = &procs_fs_operations; + + // == /proc/mounts ======================================================== + if ((system_entry = proc_create_entry("mounts", NULL)) == NULL) { + pr_err("Cannot create `/proc/mounts`.\n"); + return 1; + } + pr_debug("Created `/proc/mounts` (%p)\n", system_entry); + // Set the specific operations. + system_entry->sys_operations = &procs_sys_operations; + system_entry->fs_operations = &procs_fs_operations; + + // == /proc/cpuinfo ======================================================== + if ((system_entry = proc_create_entry("cpuinfo", NULL)) == NULL) { + pr_err("Cannot create `/proc/cpuinfo`.\n"); + return 1; + } + pr_debug("Created `/proc/cpuinfo` (%p)\n", system_entry); + // Set the specific operations. + system_entry->sys_operations = &procs_sys_operations; + system_entry->fs_operations = &procs_fs_operations; + + // == /proc/meminfo ======================================================== + if ((system_entry = proc_create_entry("meminfo", NULL)) == NULL) { + pr_err("Cannot create `/proc/meminfo`.\n"); + return 1; + } + pr_debug("Created `/proc/meminfo` (%p)\n", system_entry); + // Set the specific operations. + system_entry->sys_operations = &procs_sys_operations; + system_entry->fs_operations = &procs_fs_operations; + + // == /proc/stat ======================================================== + if ((system_entry = proc_create_entry("stat", NULL)) == NULL) { + pr_err("Cannot create `/proc/stat`.\n"); + return 1; + } + pr_debug("Created `/proc/stat` (%p)\n", system_entry); + // Set the specific operations. + system_entry->sys_operations = &procs_sys_operations; + system_entry->fs_operations = &procs_fs_operations; + return 0; +} + +static ssize_t procs_do_uptime(char *buffer, size_t bufsize) +{ + sprintf(buffer, "%d", timer_get_seconds()); + return 0; +} + +static ssize_t procs_do_version(char *buffer, size_t bufsize) +{ + sprintf(buffer, + "%s version %s (site: %s) (email: %s)", + OS_NAME, + OS_VERSION, + OS_SITEURL, + OS_REF_EMAIL); + return 0; +} + +static ssize_t procs_do_mounts(char *buffer, size_t bufsize) +{ + return 0; +} + +static ssize_t procs_do_cpuinfo(char *buffer, size_t bufsize) +{ + return 0; +} + +static ssize_t procs_do_meminfo(char *buffer, size_t bufsize) +{ + double total_space = get_zone_total_space(GFP_KERNEL) + + get_zone_total_space(GFP_USER), + free_space = get_zone_free_space(GFP_KERNEL) + + get_zone_free_space(GFP_USER), + cached_space = get_zone_cached_space(GFP_KERNEL) + + get_zone_cached_space(GFP_USER), + used_space = total_space - free_space; + total_space /= (double)K; + free_space /= (double)K; + cached_space /= (double)K; + used_space /= (double)K; + sprintf( + buffer, + "MemTotal : %12.2f Kb\n" + "MemFree : %12.2f Kb\n" + "MemUsed : %12.2f Kb\n" + "Cached : %12.2f Kb\n", + total_space, free_space, used_space, cached_space); + return 0; +} + +static ssize_t procs_do_stat(char *buffer, size_t bufsize) +{ + return 0; +} \ No newline at end of file diff --git a/mentos/src/io/proc_video.c b/mentos/src/io/proc_video.c new file mode 100644 index 0000000..8937451 --- /dev/null +++ b/mentos/src/io/proc_video.c @@ -0,0 +1,106 @@ +// +// Created by andrea on 02/05/20. +// + +#include "bits/termios-struct.h" +#include "drivers/keyboard/keyboard.h" +#include "fs/procfs.h" +#include "bits/ioctls.h" +#include "sys/bitops.h" +#include "io/video.h" +#include "misc/debug.h" +#include "sys/errno.h" +#include "fcntl.h" +#include "fs/vfs.h" + +static termios ktermios = { + .c_cflag = 0, + .c_lflag = (ICANON | ECHO | ECHOE | ECHOK | ECHONL | ISIG), + .c_oflag = 0, + .c_iflag = 0 +}; + +static ssize_t procv_read(vfs_file_t *file, char *buf, off_t offset, size_t nbyte) +{ + if (buf == NULL) { + return -1; + } + // Read the character from the keyboard. + int c = keyboard_getc(); + if (c >= 0) { + // Return the character. + *((char *)buf) = c; + if (bitmask_check(ktermios.c_lflag, ECHO)) { + if ((c == '\b') && + (!bitmask_check(ktermios.c_lflag, ICANON) || !bitmask_check(ktermios.c_lflag, ECHOE))) { + return 1; + } + // Echo the character to video. + video_putc(c); + } + return 1; + } + return 0; +} + +static ssize_t procv_write(vfs_file_t *file, const void *buf, off_t offset, size_t nbyte) +{ + for (size_t i = 0; i < nbyte; ++i) { + video_putc(((char *)buf)[i]); + } + return nbyte; +} + +static int procv_fstat(vfs_file_t *file, stat_t *stat) +{ + return -ENOSYS; +} + +static int procv_ioctl(vfs_file_t *file, int request, void *data) +{ + switch (request) { + case TCGETS: + *((termios *)data) = ktermios; + break; + case TCSETS: + ktermios = *((termios *)data); + break; + default: + break; + } + return 0; +} + +/// Filesystem general operations. +static vfs_sys_operations_t procv_sys_operations = { + .mkdir_f = NULL, + .rmdir_f = NULL, + .stat_f = NULL +}; + +/// Filesystem file operations. +static vfs_file_operations_t procv_fs_operations = { + .open_f = NULL, + .unlink_f = NULL, + .close_f = NULL, + .read_f = procv_read, + .write_f = procv_write, + .lseek_f = NULL, + .stat_f = procv_fstat, + .ioctl_f = procv_ioctl, + .getdents_f = NULL +}; + +int procv_module_init() +{ + proc_dir_entry_t *video = proc_create_entry("video", NULL); + if (video == NULL) { + pr_err("Cannot create `/proc/video`.\n"); + return 1; + } + pr_debug("Created `/proc/video` (%p)\n", video); + // Set the specific operations. + video->sys_operations = &procv_sys_operations; + video->fs_operations = &procv_fs_operations; + return 0; +} \ No newline at end of file diff --git a/mentos/src/io/stdio.c b/mentos/src/io/stdio.c new file mode 100644 index 0000000..d28f4d4 --- /dev/null +++ b/mentos/src/io/stdio.c @@ -0,0 +1,177 @@ +/// @file stdio.c +/// @brief Standard I/0 functions. +/// @date Apr 2019 + +#include "system/syscall.h" +#include "sys/errno.h" +#include "stdio.h" +#include "io/video.h" +#include "ctype.h" +#include "drivers/keyboard/keyboard.h" +#include "string.h" + +void putchar(int character) +{ + video_putc(character); +} + +void puts(char *str) +{ + video_puts(str); +} + +int getchar() +{ + return keyboard_getc(); +} + +char *gets(char *str) +{ + // Check the input string. + if (str == NULL) + return NULL; + // Buffer for reading input. + char buffer[GETS_BUFFERSIZE]; + memset(buffer, '\0', GETS_BUFFERSIZE); + // Char pointer to the buffer. + char *cptr = buffer; + // Character storage and counter to prevent overflow. + int ch, counter = 0; + // Read until we find a newline or we exceed the buffer size. + while (((ch = getchar()) != '\n') && (counter++ < GETS_BUFFERSIZE)) { + // If we encounter EOF, stop. + if (ch == EOF) { + // EOF at start of line return NULL. + if (cptr == str) { + return NULL; + } + break; + } + // Backspace key + if (ch == '\b') { + if (counter > 0) { + counter--; + --cptr; + putchar('\b'); + } + } else { + // The character is stored at address, and the pointer is incremented. + *cptr++ = ch; + } + } + // Add the null-terminating character. + *cptr = '\0'; + // Copy the string we have read. + strcpy(str, buffer); + // Return a pointer to the original string. + return str; +} + +int atoi(const char *str) +{ + // Check the input string. + if (str == NULL) + return 0; + // Initialize sign, the result variable, and two indices. + int sign = (str[0] == '-') ? -1 : +1, result = 0, i; + // Find where the number ends. + for (i = (sign == -1) ? 1 : 0; (str[i] != '\0') && isdigit(str[i]); ++i) + result = (result * 10) + str[i] - '0'; + return sign * result; +} + +long strtol(const char *str, char **endptr, int base) +{ + const char *s; + long acc, cutoff; + int c; + int neg, any, cutlim; + /* + * Skip white space and pick up leading +/- sign if any. + * If base is 0, allow 0x for hex and 0 for octal, else + * assume decimal; if base is already 16, allow 0x. + */ + s = str; + do { + c = (unsigned char)*s++; + } while (isspace(c)); + if (c == '-') { + neg = 1; + c = *s++; + } else { + neg = 0; + if (c == '+') + c = *s++; + } + if ((base == 0 || base == 16) && + c == '0' && (*s == 'x' || *s == 'X')) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = c == '0' ? 8 : 10; + /* + * Compute the cutoff value between legal numbers and illegal + * numbers. That is the largest legal value, divided by the + * base. An input number that is greater than this value, if + * followed by a legal input character, is too big. One that + * is equal to this value may be valid or not; the limit + * between valid and invalid numbers is then based on the last + * digit. For instance, if the range for longs is + * [-2147483648..2147483647] and the input base is 10, + * cutoff will be set to 214748364 and cutlim to either + * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated + * a value > 214748364, or equal but the next digit is > 7 (or 8), + * the number is too big, and we will return a range error. + * + * Set any if any `digits' consumed; make it negative to indicate + * overflow. + */ + cutoff = neg ? LONG_MIN : LONG_MAX; + cutlim = cutoff % base; + cutoff /= base; + if (neg) { + if (cutlim > 0) { + cutlim -= base; + cutoff += 1; + } + cutlim = -cutlim; + } + for (acc = 0, any = 0;; c = (unsigned char)*s++) { + if (isdigit(c)) + c -= '0'; + else if (isalpha(c)) + c -= isupper(c) ? 'A' - 10 : 'a' - 10; + else + break; + if (c >= base) + break; + if (any < 0) + continue; + if (neg) { + if (acc < cutoff || (acc == cutoff && c > cutlim)) { + any = -1; + acc = LONG_MIN; + errno = ERANGE; + } else { + any = 1; + acc *= base; + acc -= c; + } + } else { + if (acc > cutoff || (acc == cutoff && c > cutlim)) { + any = -1; + acc = LONG_MAX; + errno = ERANGE; + } else { + any = 1; + acc *= base; + acc += c; + } + } + } + if (endptr != 0) + *endptr = (char *)(any ? s - 1 : str); + return (acc); +} diff --git a/mentos/src/io/vga/vga.c b/mentos/src/io/vga/vga.c new file mode 100644 index 0000000..0503d10 --- /dev/null +++ b/mentos/src/io/vga/vga.c @@ -0,0 +1,662 @@ +#include "io/vga/vga.h" + +#include "io/vga/vga_palette.h" +#include "io/vga/vga_mode.h" +#include "io/vga/vga_font.h" + +#include "io/port_io.h" +#include "stdbool.h" +#include "string.h" +#include "misc/debug.h" +#include "math.h" + +#define COUNT_OF(x) ((sizeof(x) / sizeof(0 [x])) / ((size_t)(!(sizeof(x) % sizeof(0 [x]))))) + +#define AC_INDEX 0x03C0 +#define AC_WRITE 0x03C0 +#define AC_READ 0x03C1 +#define MISC_WRITE 0x03C2 +#define MISC_READ 0x03CC +#define SC_INDEX 0x03C4 // VGA sequence controller. +#define SC_DATA 0x03C5 +#define PALETTE_MASK 0x03C6 +#define PALETTE_READ 0x03C7 +#define PALETTE_INDEX 0x03C8 // VGA digital-to-analog converter. +#define PALETTE_DATA 0x03C9 +#define GC_INDEX 0x03CE // VGA graphics controller. +#define GC_DATA 0x03CF +#define CRTC_INDEX 0x03D4 // VGA CRT controller. +#define CRTC_DATA 0x03D5 +#define INPUT_STATUS_READ 0x03DA + +/// VGA pointers for drawing operations. +typedef struct { + /// Writes a pixel. + void (*write_pixel)(unsigned int x, unsigned int y, unsigned char c); + /// Reads a pixel. + unsigned (*read_pixel)(unsigned int x, unsigned int y); + /// Draws a rectangle. + void (*draw_rect)(unsigned int x, unsigned int y, unsigned int wd, unsigned int ht, unsigned char c); + /// Fills a rectangle. + void (*fill_rect)(unsigned int x, unsigned int y, unsigned int wd, unsigned int ht, unsigned char c); +} vga_ops_t; + +/// VGA font details. +typedef struct { + unsigned char *font; ///< Pointer to the array holding the shape of each character. + unsigned width; ///< Width of the font. + unsigned height; ///< Height of the font. +} vga_font_t; + +/// VGA driver details. +typedef struct { + int width; ///< Screen's width. + int height; ///< Screen's height. + int bpp; ///< Bits per pixel (bpp). + char *address; ///< Starting address of the screen. + vga_ops_t *ops; ///< Writing operations. +} vga_driver_t; + +static bool_t vga_enable = false; +palette_entry_t stored_palette[256]; +char vidmem[262144]; + +static vga_driver_t *driver = NULL; +static vga_font_t *font; + +// ============================================================================ +// == VGA MODEs =============================================================== +#define MODE_NUM_SEQ_REGS 5 +#define MODE_NUM_CRTC_REGS 25 +#define MODE_NUM_GC_REGS 9 +#define MODE_NUM_AC_REGS (16 + 5) +#define MODE_NUM_REGS (1 + MODE_NUM_SEQ_REGS + MODE_NUM_CRTC_REGS + MODE_NUM_GC_REGS + MODE_NUM_AC_REGS) // 61 + +static inline char *__get_seg(void) +{ + unsigned int seg; + outportb(GC_INDEX, 6); + seg = (inportb(GC_DATA) >> 2) & 3; + if ((seg == 0) || (seg == 1)) + return (char *)0xA0000; + if (seg == 2) + return (char *)0xB0000; + if (seg == 3) + return (char *)0xB8000; + return (char *)seg; +} + +void __vga_set_color_map(unsigned int index, unsigned char r, unsigned char g, unsigned char b) +{ + outportb(PALETTE_MASK, 0xFF); + outportb(PALETTE_INDEX, index); + outportl(PALETTE_DATA, r); + outportl(PALETTE_DATA, g); + outportl(PALETTE_DATA, b); +} + +void __vga_get_color_map(unsigned int index, unsigned char *r, unsigned char *g, unsigned char *b) +{ + outportb(PALETTE_MASK, 0xFF); + outportb(PALETTE_READ, index); + *r = inportl(PALETTE_DATA); + *g = inportl(PALETTE_DATA); + *b = inportl(PALETTE_DATA); +} + +static void __save_palette(palette_entry_t *p, size_t size) +{ + outportb(PALETTE_MASK, 0xFF); + outportb(PALETTE_READ, 0x00); + for (int i = 0; i < size; i++) { + p[i].red = inportl(PALETTE_DATA); + p[i].green = inportl(PALETTE_DATA); + p[i].blue = inportl(PALETTE_DATA); + } +} + +static void __load_palette(palette_entry_t *p, size_t size) +{ + outportb(PALETTE_MASK, 0xFF); + outportb(PALETTE_INDEX, 0x00); + for (int i = 0; i < size; i++) { + outportl(PALETTE_DATA, p[i].red); + outportl(PALETTE_DATA, p[i].green); + outportl(PALETTE_DATA, p[i].blue); + } +} + +static inline void __set_plane(unsigned int plane) +{ + unsigned char pmask; + plane &= 3; + pmask = 1 << plane; + // Set read plane. + outportb(GC_INDEX, 4); + outportb(GC_DATA, plane); + // Set write plane. + outportb(SC_INDEX, 2); + outportb(SC_DATA, pmask); +} + +static unsigned char __read_byte(unsigned int offset) +{ + return (unsigned char)(*(driver->address + offset)); +} + +static void __write_byte(unsigned int offset, unsigned char value) +{ + *(char *)(driver->address + offset) = value; +} + +static void __set_mode(vga_mode_t *vga_mode) +{ + unsigned char *ptr = &vga_mode->misc; + + // Write SEQUENCER regs. + outportb(MISC_WRITE, *ptr); + ++ptr; + + // Write SEQUENCER regs. + for (unsigned i = 0; i < MODE_NUM_SEQ_REGS; i++) { + outportb(SC_INDEX, i); + outportb(SC_DATA, *ptr); + ++ptr; + } + + // Wnlock CRTC registers. + outportb(CRTC_INDEX, 0x03); + outportb(CRTC_DATA, inportb(CRTC_DATA) | 0x80); + outportb(CRTC_INDEX, 0x11); + outportb(CRTC_DATA, inportb(CRTC_DATA) & ~0x80); + + // Make sure they remain unlocked. + ptr[0x03] |= 0x80; + ptr[0x11] &= ~0x80; + + // write CRTC regs. + for (unsigned i = 0; i < MODE_NUM_CRTC_REGS; i++) { + outportb(CRTC_INDEX, i); + outportb(CRTC_DATA, *ptr); + ++ptr; + } + + // write GRAPHICS CONTROLLER regs. + for (unsigned i = 0; i < MODE_NUM_GC_REGS; i++) { + outportb(GC_INDEX, i); + outportb(GC_DATA, *ptr); + ++ptr; + } + + // write ATTRIBUTE CONTROLLER regs. + for (unsigned i = 0; i < MODE_NUM_AC_REGS; i++) { + inportb(INPUT_STATUS_READ); + outportb(AC_INDEX, i); + outportb(AC_WRITE, *ptr); + ++ptr; + } + + // lock 16-color palette and unblank display. + (void)inportb(INPUT_STATUS_READ); + outportb(AC_INDEX, 0x20); +} + +static void __read_registers(vga_mode_t *vga_mode) +{ + unsigned char *ptr = &vga_mode->misc; + + // read MISCELLANEOUS + *ptr = inportb(MISC_READ); + ptr++; + + // read SEQUENCER + for (unsigned i = 0; i < MODE_NUM_SEQ_REGS; i++) { + outportb(SC_INDEX, i); + *ptr = inportb(SC_DATA); + ptr++; + } + + // read CRTC + for (unsigned i = 0; i < MODE_NUM_CRTC_REGS; i++) { + outportb(CRTC_INDEX, i); + *ptr = inportb(CRTC_DATA); + ptr++; + } + + // read GRAPHICS CONTROLLER + for (unsigned i = 0; i < MODE_NUM_GC_REGS; i++) { + outportb(GC_INDEX, i); + *ptr = inportb(GC_DATA); + ptr++; + } + + // read ATTRIBUTE CONTROLLER + for (unsigned i = 0; i < MODE_NUM_AC_REGS; i++) { + (void)inportb(INPUT_STATUS_READ); + outportb(AC_INDEX, i); + *ptr = inportb(AC_READ); + ptr++; + } + + // lock 16-color palette and unblank display + (void)inportb(INPUT_STATUS_READ); + outportb(AC_INDEX, 0x20); +} + +static void __write_font(unsigned char *buf, unsigned font_height) +{ + unsigned char seq2, seq4, gc4, gc5, gc6; + unsigned i; + + /* save registers +__set_plane() modifies GC 4 and SEQ 2, so save them as well */ + outportb(SC_INDEX, 2); + seq2 = inportb(SC_DATA); + + outportb(SC_INDEX, 4); + seq4 = inportb(SC_DATA); + /* turn off even-odd addressing (set flat addressing) +assume: chain-4 addressing already off */ + outportb(SC_DATA, seq4 | 0x04); + + outportb(GC_INDEX, 4); + gc4 = inportb(GC_DATA); + + outportb(GC_INDEX, 5); + gc5 = inportb(GC_DATA); + /* turn off even-odd addressing */ + outportb(GC_DATA, gc5 & ~0x10); + + outportb(GC_INDEX, 6); + gc6 = inportb(GC_DATA); + /* turn off even-odd addressing */ + outportb(GC_DATA, gc6 & ~0x02); + /* write font to plane P4 */ + __set_plane(2); + /* write font 0 */ + for (i = 0; i < 256; i++) { + memcpy((char *)buf, (char *)(i * 32 + 0xA0000), font_height); + buf += font_height; + } + /* restore registers */ + outportb(SC_INDEX, 2); + outportb(SC_DATA, seq2); + outportb(SC_INDEX, 4); + outportb(SC_DATA, seq4); + outportb(GC_INDEX, 4); + outportb(GC_DATA, gc4); + outportb(GC_INDEX, 5); + outportb(GC_DATA, gc5); + outportb(GC_INDEX, 6); + outportb(GC_DATA, gc6); +} + +// ============================================================================ +// == WRITE PIXEL FUNCTIONS =================================================== + +static void __write_pixel_1(unsigned int x, unsigned int y, unsigned char c) +{ + unsigned wd_in_bytes; + unsigned off, mask; + + c = (c & 1) * 0xFF; + wd_in_bytes = driver->width / 8; + off = wd_in_bytes * y + x / 8; + x = (x & 7) * 1; + mask = 0x80 >> x; + __write_byte(off, (__read_byte(off) & ~mask) | (c & mask)); +} + +static void __write_pixel_2(unsigned int x, unsigned int y, unsigned char c) +{ + unsigned wd_in_bytes; + unsigned off, mask; + + c = (c & 3) * 0x55; + wd_in_bytes = driver->width / 4; + off = wd_in_bytes * y + x / 4; + x = (x & 3) * 2; + mask = 0xC0 >> x; + __write_byte(off, (__read_byte(off) & ~mask) | (c & mask)); +} + +static void __write_pixel_4(unsigned int x, unsigned int y, unsigned char color) +{ + int rotation = 0; + int16_t t; + switch (rotation) { + case 1: + t = x; + x = driver->width - 1 - y; + y = t; + break; + case 2: + x = driver->width - 1 - x; + y = driver->height - 1 - y; + break; + case 3: + t = x; + x = y; + y = driver->height - 1 - t; + break; + } + + unsigned wd_in_bytes, off, mask, p, pmask; + + wd_in_bytes = driver->width / 8; + off = wd_in_bytes * y + x / 8; + x = (x & 7) * 1; + mask = 0x80 >> x; + pmask = 1; + for (p = 0; p < 4; p++) { + __set_plane(p); + if (pmask & color) + __write_byte(off, __read_byte(off) | mask); + else + __write_byte(off, __read_byte(off) & ~mask); + pmask <<= 1; + } +} + +static inline void __write_pixel_8(unsigned int x, unsigned int y, unsigned char color) +{ + __set_plane(x); + __write_byte(((y * driver->width) + x) / 4, color); + // (y << 6) + (y << 4) + (x >> 2) +} + +unsigned int reverseBits(char num) +{ + unsigned int NO_OF_BITS = sizeof(num) * 8; + unsigned int reverse_num = 0; + int i; + for (i = 0; i < NO_OF_BITS; i++) { + if ((num & (1 << i))) + reverse_num |= 1 << ((NO_OF_BITS - 1) - i); + } + return reverse_num; +} + +int vga_is_enabled() +{ + return vga_enable; +} + +int vga_width() +{ + if (vga_enable) + return driver->width; + return 0; +} + +int vga_height() +{ + if (vga_enable) + return driver->height; + return 0; +} + +void vga_setfont(const vga_font_t *__font) +{ + font->font = __font->font; + font->width = __font->width; + font->height = __font->height; +} + +void vga_clear_screen() +{ + for (int p = 0; p < 4; p++) { + __set_plane(p); + memset(driver->address, 0, 64 * 1024); + } +} + +void vga_draw_char(unsigned int x, unsigned int y, unsigned char c, unsigned char color) +{ + static unsigned mask[] = { + 1u << 0u, // 1 + 1u << 1u, // 2 + 1u << 2u, // 4 + 1u << 3u, // 8 + 1u << 4u, // 16 + 1u << 5u, // 32 + 1u << 6u, // 64 + 1u << 7u, // 128 + 1u << 8u, // 256 + }; + unsigned char *glyph = font->font + c * font->height; + for (unsigned cy = 0; cy < font->height; ++cy) { + for (unsigned cx = 0; cx < font->width; ++cx) { + driver->ops->write_pixel(x + (font->width - cx), y + cy, glyph[cy] & mask[cx] ? color : 0x00u); + } + } +} + +void vga_draw_string(int x, int y, char *str, unsigned char color) +{ + char i = 0; + while (*str != '\0') { + vga_draw_char(x + i * 8, y, *str, color); + str++; + i++; + } +} + +void vga_draw_line(unsigned int x0, unsigned int y0, unsigned int x1, unsigned int y1, unsigned char color) +{ + bool_t steep = abs(y1 - y0) > abs(x1 - x0); + int tmp = 0; + if (steep) { + tmp = x0; + x0 = y0; + y0 = tmp; + + tmp = x1; + x1 = y1; + y1 = tmp; + } + if (x0 > x1) { + tmp = x0; + x0 = x1; + x1 = tmp; + + tmp = y0; + y0 = y1; + y1 = tmp; + } + int deltax = x1 - x0; + int deltay = abs(y1 - y0); + int error = deltax / 2; + int ystep; + int y = y0; + if (y0 < y1) + ystep = 1; + else + ystep = -1; + for (int x = x0; x < x1; x++) { + if (steep) + driver->ops->write_pixel(y, x, color); + else + driver->ops->write_pixel(x, y, color); + error = error - deltay; + if (error < 0) { + y = y + ystep; + error = error + deltax; + } + } +} + +void vga_draw_rectangle(unsigned int sx, unsigned int sy, unsigned int ex, unsigned int ey, unsigned char fill) +{ + vga_draw_line(sx, sy, ex, sy, fill); + vga_draw_line(sx, sy, sx, ey, fill); + vga_draw_line(sx, ey, ex, ey, fill); + vga_draw_line(ex, ey, ex, sy, fill); +} + +void vga_draw_circle(unsigned int xc, unsigned int yc, unsigned int r, unsigned char col) +{ + unsigned int x = 0; + unsigned int y = r; + unsigned int p = 3 - 2 * r; + if (!r) + return; + + while (y >= x) // only formulate 1/8 of circle + { + driver->ops->write_pixel(xc - x, yc - y, col); //upper left left + driver->ops->write_pixel(xc - y, yc - x, col); //upper upper left + driver->ops->write_pixel(xc + y, yc - x, col); //upper upper right + driver->ops->write_pixel(xc + x, yc - y, col); //upper right right + driver->ops->write_pixel(xc - x, yc + y, col); //lower left left + driver->ops->write_pixel(xc - y, yc + x, col); //lower lower left + driver->ops->write_pixel(xc + y, yc + x, col); //lower lower right + driver->ops->write_pixel(xc + x, yc + y, col); //lower right right + if (p < 0) + p += 4 * x++ + 6; + else + p += 4 * (x++ - y--) + 10; + } +} + +void vga_draw_triangle(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, unsigned int x3, unsigned int y3, unsigned char col) +{ + vga_draw_line(x1, y1, x2, y2, col); + vga_draw_line(x2, y2, x3, y3, col); + vga_draw_line(x3, y3, x1, y1, col); +} + +static void __test_vga(void) +{ + vga_clear_screen(); + vga_draw_rectangle(1, 1, driver->width - 1, driver->height - 1, 3); + for (unsigned i = 1; i < driver->height - 1; i++) { + driver->ops->write_pixel((driver->width - driver->height) / 2 + i, i, 1); + driver->ops->write_pixel((driver->height + driver->width) / 2 - i, i, 1); + } +} + +// == MODEs and DRIVERs ======================================================= + +static vga_ops_t ops_720_480_16 = { + .write_pixel = __write_pixel_4, + .read_pixel = NULL, + .draw_rect = NULL, + .fill_rect = NULL, +}; + +static vga_ops_t ops_640_480_16 = { + .write_pixel = __write_pixel_4, + .read_pixel = NULL, + .draw_rect = NULL, + .fill_rect = NULL, +}; + +static vga_ops_t ops_320_200_256 = { + .write_pixel = __write_pixel_8, + .read_pixel = NULL, + .draw_rect = NULL, + .fill_rect = NULL, +}; + +static vga_font_t font_8x8 = { + .font = arr_8x8_font, + .width = 8, + .height = 8, +}; + +static vga_font_t font_8x8_basic = { + .font = arr_8x8_basic_font, + .width = 8, + .height = 8, +}; + +static vga_font_t font_8x16 = { + .font = arr_8x16_font, + .width = 8, + .height = 16, +}; + +static vga_driver_t driver_720_480_16 = { + .width = 720, + .height = 480, + .bpp = 16, + .address = (char *)NULL, + .ops = &ops_720_480_16, +}; + +static vga_driver_t driver_640_480_16 = { + .width = 640, + .height = 480, + .bpp = 16, + .address = (char *)NULL, + .ops = &ops_640_480_16, +}; + +static vga_driver_t driver_320_200_256 = { + .width = 320, + .height = 200, + .bpp = 256, + .address = (char *)NULL, + .ops = &ops_320_200_256, +}; + +// == INITIALIZE and FINALIZE ================================================= + +void vga_initialize() +{ + // Initialize the desired mode. +#if defined(VGA_MODE_320_200_256) + // Save the current palette. + __save_palette(stored_palette, 256); + // Write the registers. + __set_mode(&_mode_320_200_256); + // Initialize the mode. + driver = &driver_320_200_256; + // Load the color palette. + __load_palette(ansi_256_palette, 256); +#elif defined(VGA_MODE_640_480_16) + // Save the current palette. + __save_palette(stored_palette, 256); + // Write the registers. + __set_mode(&_mode_640_480_16); + // Initialize the mode. + driver = &driver_640_480_16; + // Load the color palette. + __load_palette(ansi_16_palette, 16); +#elif defined(VGA_MODE_720_480_16) + // Save the current palette. + __save_palette(stored_palette, 256); + // Write the registers. + __set_mode(&_mode_720_480_16); + // Initialize the mode. + driver = &driver_720_480_16; + // Load the color palette. + __load_palette(ansi_16_palette, 16); +#else // VGA_TEXT_MODE + __set_mode(&_mode_80_25_text); + return; +#endif + // Set the address. + driver->address = __get_seg(); + + // Set the font. + vga_setfont(&font_8x8); + + // Save the content of the memory. + memcpy(vidmem, driver->address, 0x4000); + + // Clears the screen. + vga_clear_screen(); + + // Set the vga as enabled. + vga_enable = true; +} + +void vga_finalize() +{ + memcpy(driver->address, vidmem, 256 * 1024); + __set_mode(&_mode_80_25_text); + __load_palette(stored_palette, 256); + vga_enable = false; +} diff --git a/mentos/src/io/video.c b/mentos/src/io/video.c index 6a8760a..d160806 100644 --- a/mentos/src/io/video.c +++ b/mentos/src/io/video.c @@ -1,381 +1,349 @@ /// MentOS, The Mentoring Operating system project /// @file video.c /// @brief Video functions and costants. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "video.h" -#include "debug.h" +#include "io/port_io.h" +#include "io/video.h" #include "stdbool.h" +#include "ctype.h" +#include "string.h" +#include "stdio.h" -/// The height of the screen. -#define SCREEN_HEIGHT 25 +#define HEIGHT 25 ///< The height of the +#define WIDTH 80 ///< The width of the +#define W2 (WIDTH * 2) ///< The width of the +#define TOTAL_SIZE (HEIGHT * WIDTH * 2) ///< The total size of the screen. +#define ADDR (char *)0xB8000U ///< The address of the +#define STORED_PAGES 3 ///< The number of stored pages. -/// The width of the screen. -#define SCREEN_WIDTH 80 +/// @brief Stores the association between ANSI colors and pure VIDEO colors. +struct ansi_color_map_t { + /// The ANSI color number. + uint8_t ansi_color; + /// The VIDEO color number. + uint8_t video_color; +} +/// @brief The mapping. +ansi_color_map[] = { + { 30, 0 }, + { 31, 4 }, + { 32, 2 }, + { 33, 6 }, + { 34, 1 }, + { 35, 5 }, + { 36, 3 }, + { 37, 7 }, -/// @brief Structure used to hold information about the screen. -typedef struct screen_t { - /// The width of the screen. - uint32_t width; - /// The height of the screen. - uint32_t height; - /// Pointer to the memory of the screen. - char *memory; - /// Pointer to a position of the screen. - char *pointer; - /// The current foreground color. - char foreground_color; - /// The current background color. - char background_color; - /// The buffer used when scrolling upward. - char upbuffer[SCREEN_HEIGHT][SCREEN_WIDTH * 2]; - /// The buffer used when scrolling downward. - char downbuffer[SCREEN_HEIGHT][SCREEN_WIDTH * 2]; - /// The output has been scrolled. - bool_t is_scrolled; - /// If the output has been shifted at least once. - bool_t is_shifted_once; - /// Used to store the last x coordiantes (used when scrolling). - uint32_t stored_x; - /// Used to store the last y coordiantes (used when scrolling). - uint32_t stored_y; -} screen_t; + { 90, 8 }, + { 91, 12 }, + { 92, 10 }, + { 93, 14 }, + { 94, 9 }, + { 95, 13 }, + { 96, 11 }, + { 97, 15 }, -/// The information concerning the screen. -screen_t screen; + { 40, 0 }, + { 41, 4 }, + { 42, 2 }, + { 43, 6 }, + { 44, 1 }, + { 45, 5 }, + { 46, 3 }, + { 47, 7 }, + + { 100, 8 }, + { 101, 12 }, + { 102, 10 }, + { 103, 14 }, + { 104, 9 }, + { 105, 13 }, + { 106, 11 }, + { 107, 15 }, + + { 0, 0 } +}; + +/// Pointer to a position of the screen writer. +char *pointer = ADDR; +/// The current color. +char color = 7; +/// Used to write on the escape_buffer. If -1, we are not parsing an escape sequence. +int escape_index = -1; +/// Used to store an escape sequence. +char escape_buffer[256]; +/// Buffer where we store the upper scroll history. +char upper_buffer[STORED_PAGES * TOTAL_SIZE] = { 0 }; +/// Buffer where we store the lower scroll history. +char original_page[TOTAL_SIZE] = { 0 }; +/// Determines if the screen is currently scrolled. +int scrolled_page = 0; void video_init() { - // =================================== - screen.height = 25; - screen.width = 80; - screen.memory = (char *)0xb8000; - screen.pointer = (char *)0xb8000; - screen.foreground_color = GREY; - screen.background_color = BLACK; - screen.is_scrolled = false; - screen.is_shifted_once = false; - // =================================== - video_clear(); + video_clear(); } -static inline void _draw_char(char c) +/// @brief Draws the given character. +/// @param c The character to draw. +static inline void __draw_char(char c) { - *(screen.pointer++) = c; - *(screen.pointer++) = - (screen.foreground_color + 16 * screen.background_color); + for (char *ptr = (ADDR + TOTAL_SIZE + (WIDTH * 2)); ptr > pointer; ptr -= 2) { + *(ptr) = *(ptr - 2); + *(ptr + 1) = *(ptr - 1); + } + *(pointer++) = c; + *(pointer++) = color; +} + +/// @brief Sets the provided ansi code. +/// @param ansi_code The ansi code describing background and foreground color. +static inline void __set_color(uint8_t ansi_code) +{ + struct ansi_color_map_t *it = ansi_color_map; + while (it->ansi_color != 0) { + if (ansi_code == it->ansi_color) { + if (((ansi_code >= 30) && (ansi_code <= 37)) || ((ansi_code >= 90) && (ansi_code <= 97))) { + color = (color & 0xF0U) | it->video_color; + } else { + color = (color & 0x0FU) | (it->video_color << 4U); + } + break; + } + ++it; + } +} + +/// @brief Moves the cursor backward. +/// @param erase If 1 also erase the character. +/// @param amount How many times we move backward. +static inline void __move_cursor_backward(int erase, int amount) +{ + for (int i = 0; i < amount; ++i) { + // Bring back the pointer. + pointer -= 2; + if (erase) { + strcpy(pointer, pointer + 2); + } + } + video_set_cursor_auto(); +} + +/// @brief Moves the cursor forward. +/// @param erase If 1 also erase the character. +/// @param amount How many times we move forward. +static inline void __move_cursor_forward(int erase, int amount) +{ + for (int i = 0; i < amount; ++i) { + // Bring forward the pointer. + if (erase) { + __draw_char(' '); + } else { + pointer += 2; + } + } + video_set_cursor_auto(); +} + +/// @brief Issue the vide to move the cursor to the given position. +/// @param x The x coordinate. +/// @param y The y coordinate. +static inline void __video_set_cursor(unsigned int x, unsigned int y) +{ + uint32_t position = x * WIDTH + y; + // Cursor LOW port to vga INDEX register. + outportb(0x3D4, 0x0F); + outportb(0x3D5, (uint8_t)(position & 0xFFU)); + // Cursor HIGH port to vga INDEX register. + outportb(0x3D4, 0x0E); + outportb(0x3D5, (uint8_t)((position >> 8U) & 0xFFU)); } void video_putc(int c) { - // If we have stored the last coordinates, this means that we are - // currently scrolling upwards. Thus, we need to scroll down first. - if (screen.stored_x && screen.stored_y) { - video_scroll_down(); - } - // If the character is '\n' go the new line. - if (c == '\n') { - video_new_line(); - } else if (c == '\033') { - __asm__("nop"); - } else if (c == '\t') { - video_put_tab(); - } else if (c == '\b') { - video_delete_last_character(); - } else if (c == '\r') { - video_cartridge_return(); - } else { - _draw_char(c); - } - video_shift_one_line(); - video_set_cursor_auto(); + // == ESCAPE SEQUENCES ======================================================================== + if (c == '\033') { + escape_index = 0; + return; + } + if (escape_index >= 0) { + if ((escape_index == 0) && (c == '[')) { + return; + } + escape_buffer[escape_index++] = c; + escape_buffer[escape_index] = 0; + if (isalpha(c)) { + escape_buffer[--escape_index] = 0; + if (c == 'C') { + __move_cursor_forward(false, atoi(escape_buffer)); + } else if (c == 'D') { + __move_cursor_backward(false, atoi(escape_buffer)); + } else if (c == 'm') { + __set_color(atoi(escape_buffer)); + } else if (c == 'J') { + video_clear(); + } + escape_index = -1; + } + return; + } + + // == NORMAL CHARACTERS ======================================================================= + // If the character is '\n' go the new line. + if (c == '\n') { + video_new_line(); + //video_shift_one_line_down(); + } else if (c == '\b') { + __move_cursor_backward(true, 1); + } else if (c == '\r') { + video_cartridge_return(); + } else if (c == 127) { + strcpy(pointer, pointer + 2); + } else if ((c >= 0x20) && (c <= 0x7E)) { + __draw_char(c); + } else { + return; + } + + video_shift_one_line_up(); + video_set_cursor_auto(); } void video_puts(const char *str) { - while ((*str) != 0) { - if ((*str) == '\n') { - video_new_line(); - } else if ((*str) == '\033') { - video_set_color(*(++str)); - } else if ((*str) == '\t') { - video_put_tab(); - } else if ((*str) == '\b') { - video_delete_last_character(); - } else if ((*str) == '\r') { - video_cartridge_return(); - } else { - video_putc((*str)); - } - ++str; - } -} - -void video_set_color(const video_color_t foreground) -{ - screen.foreground_color = foreground; -} - -void video_set_background(const video_color_t background) -{ - screen.background_color = background; -} - -void video_delete_last_character() -{ - if (lower_bound_y != video_get_line() || - lower_bound_x < video_get_column()) { - video_set_color(WHITE); - video_set_background(BLACK); - // Delete any tabular. - int x = video_get_column(); - if ((*(screen.pointer - 2)) == '\t') { - for (int i = 0; (i < (4 - x % 4)); ++i) { - if ((*(screen.pointer - 2)) != '\t') - break; - // Bring back the pointer. - screen.pointer -= 2; - // Delete the character. - _draw_char(' '); - // Bring back the pointer. - screen.pointer -= 2; - } - } else { - // Bring back the pointer. - screen.pointer -= 2; - // Delete the character. - _draw_char(' '); - // Bring back the pointer. - screen.pointer -= 2; - } - } -} - -void video_set_cursor(const unsigned int x, const unsigned int y) -{ - uint32_t position = (x * 80) + y; - - // Cursor LOW port to vga INDEX register. - outportb(0x3D4, 0x0F); - outportb(0x3D5, (uint8_t)(position & 0xFF)); - - // Cursor HIGH port to vga INDEX register. - outportb(0x3D4, 0x0E); - outportb(0x3D5, (uint8_t)((position >> 8) & 0xFF)); + while ((*str) != 0) { + video_putc((*str++)); + } } void video_set_cursor_auto() { - long x = ((screen.pointer - screen.memory) / 2) / screen.width; - long y = ((screen.pointer - screen.memory) / 2) % screen.width; - - if (x < 0) { - dbg_print("Negative x while setting auto-cursor.\n"); - x = 0; - } - - if (y < 0) { - dbg_print("Negative x while setting auto-cursor.\n"); - y = 0; - } - - video_set_cursor((uint32_t)x, (uint32_t)y); + __video_set_cursor(((pointer - ADDR) / 2U) / WIDTH, ((pointer - ADDR) / 2U) % WIDTH); } -void video_move_cursor(int x, int y) +void video_move_cursor(unsigned int x, unsigned int y) { - screen.pointer = screen.memory + ((y * screen.width * 2) + (x * 2)); - video_set_cursor_auto(); -} - -void video_put_tab() -{ - int foreground = screen.foreground_color; - video_set_color(screen.background_color); - int x = video_get_column(); - for (int i = 0; i < (4 - x % 4); ++i) - _draw_char('\t'); - video_set_color(foreground); + pointer = ADDR + ((y * WIDTH * 2) + (x * 2)); + video_set_cursor_auto(); } void video_clear() { - screen.pointer = screen.memory; - - for (uint32_t y = 0; y < screen.height; y++) { - for (uint32_t x = 0; x < screen.width; x++) { - *(screen.pointer++) = ' '; - *(screen.pointer++) = 0x7; - } - } - - screen.pointer = screen.memory; + memset(upper_buffer, 0, STORED_PAGES * TOTAL_SIZE); + memset(ADDR, 0, TOTAL_SIZE); } void video_new_line() { - screen.pointer = - screen.memory + - ((((screen.pointer - screen.memory) / (screen.width * 2)) + 1) * - (screen.width * 2)); - - video_shift_one_line(); - video_set_cursor_auto(); + pointer = ADDR + ((pointer - ADDR) / W2 + 1) * W2; + video_shift_one_line_up(); + video_set_cursor_auto(); } void video_cartridge_return() { - screen.pointer = - screen.memory + - ((((screen.pointer - screen.memory) / (screen.width * 2)) - 1) * - (screen.width * 2)); - - video_new_line(); - - video_shift_one_line(); - - video_set_cursor_auto(); + pointer = ADDR + ((pointer - ADDR) / W2 - 1) * W2; + video_new_line(); + video_shift_one_line_up(); + video_set_cursor_auto(); } -uint32_t video_get_column() +uint32_t video_get_x() { - if (screen.pointer < screen.memory) { - dbg_print("Get negative value while getting video column.\n"); - - return 0; - } - - return ((screen.pointer - screen.memory) % (screen.width * 2)) / 2; + return ((pointer - ADDR) % (WIDTH * 2)) / 2; } -uint32_t video_get_line() +uint32_t video_get_y() { - if (screen.pointer < screen.memory) { - dbg_print("Get negative value while getting video line.\n"); - - return 0; - } - - return ((screen.pointer - screen.memory) / (screen.width * 2)); + return ((pointer - ADDR) / (WIDTH * 2)); } -void video_shift_one_line(void) +void video_shift_one_line_up(void) { - if (screen.pointer >= - screen.memory + ((screen.height) * screen.width * 2)) { - /* We save the line to be lost in a buffer, this will be useful for - * scrolling. - */ - video_rotate_scroll_buffer(); - uint32_t index; - - for (index = 0; index < screen.width * 2; index++) { - screen.upbuffer[screen.height - 1][index] = - *(screen.memory + index); - } - - for (char *i = screen.memory; - i <= (screen.memory + ((screen.height) * screen.width * 2) + - (screen.width * 2)); - ++i) { - *i = i[screen.width * 2]; - } - screen.pointer = - screen.memory + - ((((screen.pointer - screen.memory) / (screen.width * 2)) - 1) * - (screen.width * 2)); - // Set that the scroll has been shifted at least once. - screen.is_shifted_once = true; - } + if (pointer >= ADDR + TOTAL_SIZE) { + // Move the upper buffer up by one line. + for (int row = 0; row < (STORED_PAGES * HEIGHT); ++row) { + memcpy(upper_buffer + W2 * row, upper_buffer + W2 * (row + 1), 2 * WIDTH); + } + // Copy the first line on the screen inside the last line of the upper buffer. + memcpy(upper_buffer + (TOTAL_SIZE * STORED_PAGES - W2), ADDR, 2 * WIDTH); + // Move the screen up by one line. + for (int row = 0; row < HEIGHT; ++row) { + memcpy(ADDR + (W2 * row), ADDR + (W2 * (row + 1)), 2 * WIDTH); + } + // Update the pointer. + pointer = ADDR + ((pointer - ADDR) / W2 - 1) * W2; + } } -void video_rotate_scroll_buffer() +void video_shift_one_page_up() { - for (uint32_t y = 1; y < screen.height; y++) { - for (uint32_t x = 0; x < screen.width * 2; x++) { - screen.upbuffer[y - 1][x] = screen.upbuffer[y][x]; - } - } + if (scrolled_page > 0) { + // Decrese the number of scrolled pages, and compute which page must be loaded. + int page_to_load = (STORED_PAGES - (--scrolled_page)); + // If we have reached 0, restore the original page. + if (scrolled_page == 0) + memcpy(ADDR, original_page, TOTAL_SIZE); + else + memcpy(ADDR, upper_buffer + (page_to_load * TOTAL_SIZE), TOTAL_SIZE); + } } +void video_shift_one_page_down(void) +{ + if (scrolled_page < STORED_PAGES) { + // Increase the number of scrolled pages, and compute which page must be loaded. + int page_to_load = (STORED_PAGES - (++scrolled_page)); + // If we are loading the first history page, save the original. + if (scrolled_page == 1) + memcpy(original_page, ADDR, TOTAL_SIZE); + // Load the specific page. + memcpy(ADDR, upper_buffer + (page_to_load * TOTAL_SIZE), TOTAL_SIZE); + } +} +#if 0 void video_scroll_up() { - char *ptr = screen.memory; + if (is_scrolled) + return; + char *ptr = memory = pointer; + for (unsigned int it = 0; it < TOTAL_SIZE; it++) { + downbuffer[y][x] = *ptr; + *ptr++ = upbuffer[y][x]; + } - if (screen.is_scrolled || !screen.is_shifted_once) { - return; - } + is_scrolled = true; - for (uint32_t y = 0; y < screen.height; y++) { - for (uint32_t x = 0; x < screen.width * 2; x++) { - screen.downbuffer[y][x] = *ptr; - *ptr++ = screen.upbuffer[y][x]; - } - } + stored_x = video_get_column(); - screen.is_scrolled = true; + stored_y = video_get_line(); - screen.stored_x = video_get_column(); - - screen.stored_y = video_get_line(); - - video_move_cursor(screen.width, screen.height); + video_move_cursor(width, height); } void video_scroll_down() { - char *ptr = screen.memory; + char *ptr = memory; - // If PAGEUP hasn't been pressed, it's useless to go down, there is nothing. - if (!screen.is_scrolled) { - return; - } - for (uint32_t y = 0; y < screen.height; y++) { - for (uint32_t x = 0; x < screen.width * 2; x++) { - *ptr++ = screen.downbuffer[y][x]; - } - } + // If PAGEUP hasn't been pressed, it's useless to go down, there is nothing. + if (!is_scrolled) { + return; + } + for (uint32_t y = 0; y < height; y++) { + for (uint32_t x = 0; x < width * 2; x++) { + *ptr++ = downbuffer[y][x]; + } + } - screen.is_scrolled = false; + is_scrolled = false; - video_move_cursor(screen.stored_x, screen.stored_y); + video_move_cursor(stored_x, stored_y); - screen.stored_x = 0; + stored_x = 0; - screen.stored_y = 0; + stored_y = 0; } -void video_print_ok() -{ - video_move_cursor(60, video_get_line()); - - video_set_color(WHITE); - - video_putc('['); - - video_set_color(BRIGHT_GREEN); - - video_puts("OK"); - - video_set_color(WHITE); - - video_puts("]\n"); -} - -void video_print_fail() -{ - video_move_cursor(60, video_get_line()); - - video_set_color(WHITE); - - video_putc('['); - - video_set_color(BRIGHT_RED); - - video_puts("FAIL"); - - video_set_color(WHITE); - - video_puts("]\n"); -} +#endif \ No newline at end of file diff --git a/mentos/src/ipc/msg.c b/mentos/src/ipc/msg.c new file mode 100644 index 0000000..bbaa6d9 --- /dev/null +++ b/mentos/src/ipc/msg.c @@ -0,0 +1,35 @@ +/// MentOS, The Mentoring Operating system project +/// @file msg.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. +///! @cond Doxygen_Suppress + +#include "ipc/msg.h" +#include "system/panic.h" + +long sys_msgget(key_t key, int msgflg) +{ + TODO("Not implemented"); + return 0; +} + +long sys_msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg) +{ + TODO("Not implemented"); + return 0; +} + +long sys_msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg) +{ + TODO("Not implemented"); + return 0; +} + +long sys_msgctl(int msqid, int cmd, struct msqid_ds *buf) +{ + TODO("Not implemented"); + return 0; +} + +///! @endcond \ No newline at end of file diff --git a/mentos/src/ipc/sem.c b/mentos/src/ipc/sem.c new file mode 100644 index 0000000..238b40f --- /dev/null +++ b/mentos/src/ipc/sem.c @@ -0,0 +1,29 @@ +/// MentOS, The Mentoring Operating system project +/// @file sem.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. +///! @cond Doxygen_Suppress + +#include "ipc/sem.h" +#include "system/panic.h" + +long sys_semget(key_t key, int nsems, int semflg) +{ + TODO("Not implemented"); + return 0; +} + +long sys_semop(int semid, struct sembuf *sops, unsigned nsops) +{ + TODO("Not implemented"); + return 0; +} + +long sys_semctl(int semid, int semnum, int cmd, unsigned long arg) +{ + TODO("Not implemented"); + return 0; +} + +///! @endcond \ No newline at end of file diff --git a/mentos/src/ipc/shm.c b/mentos/src/ipc/shm.c new file mode 100644 index 0000000..4bab57c --- /dev/null +++ b/mentos/src/ipc/shm.c @@ -0,0 +1,396 @@ +/// MentOS, The Mentoring Operating system project +/// @file shm.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. +///! @cond Doxygen_Suppress + +#include "ipc/shm.h" +#include "system/panic.h" + +#if 0 +struct shmid_ds *head = NULL; +static ushort shm_descriptor = 0; + +int syscall_shmctl(int *args) +{ + int shmid = args[0]; + int cmd = args[1]; + + // TODO: for IPC_STAT + // struct shmid_ds * buf = (struct shmid_ds *) args[2]; + + struct shmid_ds *myshmid_ds = find_shm_fromid(shmid); + + if (myshmid_ds == NULL) { + return -1; + } + + // Upgrade shm info. + myshmid_ds->shm_lpid = scheduler_get_current_process()->pid; + myshmid_ds->shm_ctime = time(NULL); + + switch (cmd) { + case IPC_RMID: + if (myshmid_ds->shm_nattch == 0) { + kfree(myshmid_ds->shm_location); + + // Manage list. + if (myshmid_ds == head) { + head = head->next; + } else { + // Finding the previous shmid_ds. + struct shmid_ds *prev = head; + while (prev->next != myshmid_ds) { + prev = prev->next; + } + prev->next = myshmid_ds->next; + } + kfree(myshmid_ds); + } else { + (myshmid_ds->shm_perm).mode |= SHM_DEST; + } + + return 0; + + case IPC_STAT: + break; + case IPC_SET: + break; + case SHM_LOCK: + break; + case SHM_UNLOCK: + break; + default: + break; + } + + return -1; +} + +// Get shared memory segment. +int syscall_shmget(int *args) +{ + int flags = args[2]; + key_t key = (key_t)args[0]; + size_t size = (size_t)args[1]; + + struct shmid_ds *shmid_ds; + + if (flags & IPC_EXCL) { + return -1; + } + + if (flags & IPC_CREAT) { + shmid_ds = find_shm_fromkey(key); + + if (shmid_ds != NULL) { + return -1; + } + + shmid_ds = kmalloc(sizeof(struct shmid_ds)); + pr_default("\n[SHM] shmget() shmid_ds : 0x%p", shmid_ds); + + shmid_ds->shm_location = kmalloc_align(size); + pr_default("\n[SHM] shmget() Location : 0x%p", + shmid_ds->shm_location); + pr_default("\n[SHM] shmget() physLocation : 0x%p", + paging_virtual_to_physical(get_current_page_directory(), + shmid_ds->shm_location)); + + shmid_ds->next = head; + head = shmid_ds; + + shmid_ds->shm_segsz = size; + shmid_ds->shm_atime = 0; + shmid_ds->shm_dtime = 0; + shmid_ds->shm_ctime = 0; + shmid_ds->shm_cpid = scheduler_get_current_process()->pid; + shmid_ds->shm_lpid = scheduler_get_current_process()->pid; + shmid_ds->shm_nattch = 0; + + // No user implementation. + (shmid_ds->shm_perm).cuid = 0; + // No group implementation. + (shmid_ds->shm_perm).cgid = 0; + // No user implementation + (shmid_ds->shm_perm).uid = 0; + // No group implementation. + (shmid_ds->shm_perm).gid = 0; + (shmid_ds->shm_perm).mode = flags & 0777; + (shmid_ds->shm_perm).seq = shm_descriptor++; + (shmid_ds->shm_perm).key = key; + } else { + shmid_ds = find_shm_fromkey(key); + pr_default("\n[SHM] shmget() shmid_ds found : 0x%p", shmid_ds); + + if (shmid_ds == NULL) { + return -1; + } + + if ((flags & 0777) > ((shmid_ds->shm_perm).mode & 0777)) { + return -1; + } + shmid_ds->shm_lpid = scheduler_get_current_process()->pid; + } + + return (shmid_ds->shm_perm).seq; +} + +// Attach shared memory segment. +void *syscall_shmat(int *args) +{ + int shmid = args[0]; + void *shmaddr = (void *)args[1]; + + // TODO: for more settings + // int flags = args[2]; + + struct shmid_ds *myshmid_ds = find_shm_fromid(shmid); + pr_default("\n[SHM] shmat() shmid_ds found : 0x%p", myshmid_ds); + + if (myshmid_ds == NULL) { + return (void *)-1; + } + + void *shm_start = myshmid_ds->shm_location; + + if (shmaddr == NULL) { + void *ret = kmalloc_align(myshmid_ds->shm_segsz); + + uint32_t shm_vaddr_start = (uint32_t)ret & 0xfffff000; + uint32_t shm_vaddr_end = + ((uint32_t)ret + myshmid_ds->shm_segsz) & 0xfffff000; + + uint32_t shm_paddr_start = (uint32_t)paging_virtual_to_physical( + get_current_page_directory(), shm_start); + + free_map_region(get_current_page_directory(), shm_vaddr_start, + shm_vaddr_end, true); + + while (shm_vaddr_start <= shm_vaddr_end) { + paging_allocate_page(get_current_page_directory(), shm_vaddr_start, + shm_paddr_start / PAGE_SIZE, true, true); + shm_vaddr_start += PAGE_SIZE; + shm_paddr_start += PAGE_SIZE; + } + + pr_default("\n[SHM] shmat() vaddr : 0x%p", ret); + pr_default("\n[SHM] shmat() paddr : 0x%p", + (void *)shm_paddr_start); + pr_default("\n[SHM] shmat() paddr after map: 0x%p", + paging_virtual_to_physical(get_current_page_directory(), + ret)); + + // Upgrade shm info. + myshmid_ds->shm_lpid = scheduler_get_current_process()->pid; + (myshmid_ds->shm_nattch)++; + myshmid_ds->shm_atime = time(NULL); + + return ret; + } + + return (void *)-1; +} + +// Detach shared memory segment. +int syscall_shmdt(int *args) +{ + void *shmaddr = (void *)args[0]; + + if (shmaddr == NULL) { + return -1; + } + + struct shmid_ds *myshmid_ds = find_shm_fromvaddr(shmaddr); + pr_default("\n[SHM] shmdt() shmid_ds found : 0x%p", myshmid_ds); + + if (myshmid_ds == NULL) { + return -1; + } + + // ===== Test ============================================================== + uint32_t shm_vaddr_start = (uint32_t)shmaddr & 0xfffff000; + uint32_t shm_vaddr_end = + ((uint32_t)shmaddr + myshmid_ds->shm_segsz) & 0xfffff000; + + free_map_region(get_current_page_directory(), shm_vaddr_start, + shm_vaddr_end, false); + + while (shm_vaddr_start <= shm_vaddr_end) { + paging_allocate_page(get_current_page_directory(), shm_vaddr_start, + shm_vaddr_start / PAGE_SIZE, true, true); + shm_vaddr_start += PAGE_SIZE; + } + // ========================================================================= + + kfree(shmaddr); + + // Upgrade shm info. + myshmid_ds->shm_lpid = scheduler_get_current_process()->pid; + (myshmid_ds->shm_nattch)--; + myshmid_ds->shm_dtime = time(NULL); + + // Manage SHM_DEST flag on. + if (myshmid_ds->shm_nattch == 0 && (myshmid_ds->shm_perm).mode & SHM_DEST) { + kfree(myshmid_ds->shm_location); + + // Manage list. + if (myshmid_ds == head) { + head = head->next; + } else { + // Finding the previous shmid_ds. + struct shmid_ds *prev = head; + while (prev->next != myshmid_ds) { + prev = prev->next; + } + prev->next = myshmid_ds->next; + } + kfree(myshmid_ds); + } + + return 0; +} + +int shmctl(int shmid, int cmd, struct shmid_ds *buf) +{ + int error; + + __asm__("movl %0, %%ecx\n" + "movl %1, %%ebx\n" + "movl %2, %%edx\n" + "movl $6, %%eax\n" + "int $80\n" + : + : "r"(shmid), "r"(cmd), "r"(buf)); + __asm__("movl %%eax, %0\n\t" + : "=r"(error)); + + return error; +} + +int shmget(key_t key, size_t size, int flags) +{ + int id; + + __asm__("movl %0, %%ecx\n" + "movl %1, %%ebx\n" + "movl %2, %%edx\n" + "movl $3, %%eax\n" + "int $80\n" + : + : "r"(key), "r"(size), "r"(flags)); + __asm__("movl %%eax, %0\n\t" + : "=r"(id)); + + return id; +} + +void *shmat(int shmid, void *shmaddr, int flag) +{ + void *addr; + + __asm__("movl %0, %%ecx\n" + "movl %1, %%ebx\n" + "movl %2, %%edx\n" + "movl $4, %%eax\n" + "int $80\n" + : + : "r"(shmid), "r"(shmaddr), "r"(flag)); + // The kernel is serving my system call + + // Now I have the control + __asm__("movl %%eax, %0\n\t" + : "=r"(addr)); + + return addr; +} + +int shmdt(void *shmaddr) +{ + int error; + + __asm__("movl %0, %%ecx\n" + "movl $5, %%eax\n" + "int $80\n" + : + : "r"(shmaddr)); + __asm__("movl %%eax, %0\n\t" + : "=r"(error)); + + return error; +} + +struct shmid_ds *find_shm_fromid(int shmid) +{ + struct shmid_ds *res = head; + + while (res != NULL) { + if ((res->shm_perm).seq == shmid) { + return res; + } + res = res->next; + } + + return NULL; +} + +struct shmid_ds *find_shm_fromkey(key_t key) +{ + struct shmid_ds *res = head; + + while (res != NULL) { + if ((res->shm_perm).key == key) { + return res; + } + res = res->next; + } + + return NULL; +} + +struct shmid_ds *find_shm_fromvaddr(void *shmvaddr) +{ + void *shmpaddr = + paging_virtual_to_physical(get_current_page_directory(), shmvaddr); + void *paddr; + struct shmid_ds *res = head; + + while (res != NULL) { + paddr = paging_virtual_to_physical(get_current_page_directory(), + res->shm_location); + if (paddr == shmpaddr) { + return res; + } + res = res->next; + } + + return NULL; +} +#endif + +long sys_shmat(int shmid, char *shmaddr, int shmflg) +{ + TODO("Not implemented"); + return 0; +} + +long sys_shmget(key_t key, size_t size, int flag) +{ + TODO("Not implemented"); + return 0; +} + +long sys_shmdt(char *shmaddr) +{ + TODO("Not implemented"); + return 0; +} + +long sys_shmctl(int shmid, int cmd, struct shmid_ds *buf) +{ + TODO("Not implemented"); + return 0; +} + +///! @endcond diff --git a/mentos/src/kernel.c b/mentos/src/kernel.c index 87d6c28..6ec25bb 100644 --- a/mentos/src/kernel.c +++ b/mentos/src/kernel.c @@ -1,261 +1,349 @@ /// MentOS, The Mentoring Operating system project /// @file kernel.c /// @brief Kernel main function. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +#include "io/proc_modules.h" +#include "mem/vmem_map.h" +#include "fs/procfs.h" +#include "devices/pci.h" +#include "drivers/ata.h" +#include "descriptor_tables/idt.h" #include "kernel.h" -#include "zone_allocator.h" -#include "gdt.h" -#include "syscall.h" +#include "mem/zone_allocator.h" +#include "descriptor_tables/gdt.h" +#include "system/syscall.h" #include "version.h" -#include "video.h" - -#include "pic8259.h" - -#include "cmd_cpuid.h" -#include "debug.h" -#include "fdc.h" -#include "initrd.h" -#include "irqflags.h" -#include "keyboard.h" -#include "scheduler.h" -#include "shell.h" +#include "io/video.h" +#include "hardware/pic8259.h" +#include "misc/debug.h" +#include "drivers/fdc.h" +#include "fs/initrd.h" +#include "klib/irqflags.h" +#include "drivers/keyboard/keyboard.h" +#include "process/scheduler.h" +#include "hardware/timer.h" +#include "fs/vfs.h" +#include "devices/fpu.h" +#include "system/printk.h" +#include "sys/module.h" +#include "drivers/rtc.h" #include "stdio.h" -#include "timer.h" -#include "vfs.h" +#include "assert.h" +#include "io/vga/vga.h" -#include "kheap.h" - -#include "init.h" - -#include "pci.h" -#include "fpu.h" -#include "ata.h" - -#include "printk.h" - -/// Describe start and end address of grub multiboot modules +/// Describe start address of grub multiboot modules. char *module_start[MAX_MODULES]; +/// Describe end address of grub multiboot modules. char *module_end[MAX_MODULES]; -/// Defined in kernel.ld, points at the multiheader grub info. +// Everything is defined in kernel.ld. + +/// Points at the multiheader grub info, starting address. extern uint32_t _multiboot_header_start; +/// Points at the multiheader grub info, ending address. extern uint32_t _multiboot_header_end; -/// Defined in kernel.ld, points at the kernel code. +/// Points at the kernel code, starting address. extern uint32_t _text_start; +/// Points at the kernel code, ending address. extern uint32_t _text_end; -/// Defined in kernel.ld, points at the read-only kernel data. +/// Points at the read-only kernel data, starting address. extern uint32_t _rodata_start; +/// Points at the read-only kernel data, ending address. extern uint32_t _rodata_end; -/// Defined in kernel.ld, points at the read-write kernel data initialized. +/// Points at the read-write kernel data initialized, starting address. extern uint32_t _data_start; +/// Points at the read-write kernel data initialized, ending address. extern uint32_t _data_end; -/// Defined in kernel.ld, points at the read-write kernel data uninitialized an kernel stack. +/// Points at the read-write kernel data uninitialized an kernel stack, starting address. extern uint32_t _bss_start; +/// Points at the read-write kernel data uninitialized an kernel stack, ending address. extern uint32_t _bss_end; +/// Points at the top of the kernel stack. extern uint32_t stack_top; +/// Points at the bottom of the kernel stack. extern uint32_t stack_bottom; -/// Defined in kernel.ld, points at the end of kernel code/data. +/// Points at the end of kernel code/data. extern uint32_t end; +/// Initial ESP. uintptr_t initial_esp = 0; +/// The boot info. +boot_info_t boot_info; -int kmain(uint32_t magic, multiboot_info_t *header, uintptr_t esp) +/// @brief Prints [OK] at the current row and column 60. +static inline void print_ok() { - dbg_print("\n"); - dbg_print("--------------------------------------------------\n"); - dbg_print("- Booting...\n"); - dbg_print("--------------------------------------------------\n"); - - // Print kernel initial state - dbg_print("\n KERNEL STATE ON BOOTING"); - dbg_print("\nKernel multiboot header: [ 0x%p - 0x%p ] ", - &_multiboot_header_start, &_multiboot_header_end); - dbg_print("\nKernel code: [ 0x%p - 0x%p ] <- text ", - &_text_start, &_text_end); - dbg_print("\nKernel read-only data: [ 0x%p - 0x%p ] <- rodata ", - &_rodata_start, &_rodata_end); - dbg_print("\nKernel initialized data: [ 0x%p - 0x%p ] <- data ", - &_data_start, &_data_end); - dbg_print("\nKernel uninitialized data: [ 0x%p - 0x%p ] <- stack & bss", - &_bss_start, &_bss_end); - dbg_print("\n - Kernel stack: [ 0x%p - 0x%p ] ", - &stack_bottom, &stack_top); - dbg_print("\n - Kernel bss: [ 0x%p - 0x%p ] ", - &stack_top, &_bss_end); - dbg_print("\nKernel image end: 0x%p", &end); - - // Am I booted by a Multiboot-compliant boot loader? - if (magic != MULTIBOOT_BOOTLOADER_MAGIC) { - printk("Invalid magic number: 0x%x\n", (unsigned)magic); - return 1; - } - - // Set the initial esp. - initial_esp = esp; - - // Dump the multiboot structure. - dump_multiboot(header); - - //=== Initialize the video ================================================= - video_init(); - //-------------------------------------------------------------------------- - - //==== Show Operating System Version ======================================= - video_set_color(BRIGHT_GREEN); - printk(OS_NAME " " OS_VERSION); - video_set_color(WHITE); - printk("\nSite:"); - video_set_color(BRIGHT_BLUE); - printk(OS_SITEURL); - printk("\n"); - video_set_color(WHITE); - printk("\n"); - //-------------------------------------------------------------------------- - - //==== Set the starting point and end point of the module ================== - int i = 0; - multiboot_module_t *mod = (multiboot_module_t *)header->mods_addr; - for (; i < header->mods_count && i < MAX_MODULES; i++, mod++) { - module_start[i] = (char *)mod->mod_start; - module_end[i] = (char *)mod->mod_end; - } - //========================================================================== - - //==== Initialize memory =================================================== - printk("Initialize physical memory manager..."); - // Memoria fisica totale allocata: 1096 MB - if (!pmmngr_init(1096 * M + 512 * 4 * K)) { - video_print_fail(); - return 1; - } - video_print_ok(); - //========================================================================== - - //==== Initialize kernel heap ============================================== - dbg_print("Initialize kernel heap.\n"); - printk("Initialize heap..."); - kheap_init(KHEAP_INITIAL_SIZE); - video_print_ok(); - //========================================================================== - - //==== Initialize core modules ============================================= - // The Global Descriptor Table (GDT) is a data structure used by Intel - // x86-family processors starting with the 80286 in order to define the - // characteristics of the various memory areas used during program execution, - // including the base address, the size, and access privileges like - // executability and writability. These memory areas are called segments in - // Intel terminology. - printk("Initialize GDT..."); - init_gdt(); - video_print_ok(); - - // The IDT is used to show the processor what Interrupt Service Routine - // (ISR) to call to handle an exception. IDT entries are also called - // Interrupt requests whenever a device has completed a request and needs to - // be serviced. - // ISRs are used to save the current processor state and set up the - // appropriate segment registers needed for kernel mode before the kernel’s - // C-level interrupt handler is called. To handle the right exception, the - // correct entry in the IDT should be pointed to the correct ISR. - printk("Initialize IDT..."); - init_idt(); - video_print_ok(); - //-------------------------------------------------------------------------- - - // Initialize the system calls. - printk("Initialize system calls..."); - syscall_init(); - video_print_ok(); - - // Set the IRQs. - printk("Initialize IRQ..."); - pic8259_init_irq(); - video_print_ok(); - - //dbg_print("Initialize the paging.\n"); - // Initialize the paging. - //printk("Initialize the paging..."); - //paging_init(); - //video_print_ok(); - - dbg_print("Install the timer.\n"); - // Install the timer. - printk(" * Setting up timer..."); - timer_install(); - video_print_ok(); - - //dbg_print("Alloc and fill CPUID structure.\n"); - // Alloc and fill CPUID structure. - //printk(LNG_INIT_CPUID); - //get_cpuid(&sinfo); - //video_print_ok(); - - dbg_print("Initialize the filesystem.\n"); - // Initialize the filesystem. - printk("Initialize the fylesystem..."); - vfs_init(); - video_print_ok(); - initfs_init(); - - //dbg_print("Get the kernel image from the boot info.\n"); - // Get the kernel image from the boot info - //build_elf_symbols_from_multiboot(header); - - //==== Install/Uninstall devices =========================================== - // For debugging, show the list of PCI devices. - // pci_debug_scan(); - // Scan for ata devices. - // ata_initialize(); - - dbg_print("Install the keyboard.\n"); - printk(" * Setting up keyboard driver..."); - keyboard_install(); - video_print_ok(); - - // dbg_print("Install the mouse.\n"); - // printf(" * Setting up mouse driver..."); - // mouse_install(); // Install the mouse. - // video_print_ok(); - - dbg_print("Uninstall the floppy driver.\n"); - fdc_disable_motor(); // We disable floppy driver motor - //-------------------------------------------------------------------------- - - //==== Initialize the scheduler ============================================ - dbg_print("Initialize the scheduler.\n"); - printk("Initialize the scheduler..."); - kernel_initialize_scheduler(); - video_print_ok(); - //-------------------------------------------------------------------------- - - // create init process - task_struct *init_p = create_init_process(); - - //==== Initialize the FPU ================================================= - // Initialize the Floating Point Unit (FPU). - dbg_print("Initialize the Floating Point Unit (FPU).\n"); - printk("Initialize floating point unit..."); - if (!fpu_install()) { - video_print_fail(); - return 1; - } - video_print_ok(); - - dbg_print("--------------------------------------------------\n"); - dbg_print("- Booting Done\n"); - dbg_print("- Jumping into init process, hopefully...\n"); - dbg_print("--------------------------------------------------\n"); - - // Jump into init process. - enter_user_jmp( - // Entry point. - init_p->thread.eip, - // Stack pointer. - init_p->thread.useresp); - - dbg_print("Dear developer, we have to talk...\n"); - - return 1; + video_move_cursor(75, video_get_y()); + video_puts("[" FG_GREEN_BRIGHT "OK" FG_WHITE "]\n"); +} + +/// @brief Prints [FAIL] at the current row and column 60. +static inline void print_fail() +{ + video_move_cursor(75, video_get_y()); + video_puts("[" FG_RED_BRIGHT "FAIL" FG_WHITE "]\n"); +} + +/// @brief Entry point of the kernel. +/// @param boot_informations Information concerning the boot. +/// @return The exit status of the kernel. +int kmain(boot_info_t *boot_informations) +{ + pr_notice("Booting...\n"); + // Make a copy for when paging is enabled + boot_info = *boot_informations; + // Am I booted by a Multiboot-compliant boot loader? + if (boot_info.magic != MULTIBOOT_BOOTLOADER_MAGIC) { + printf("Invalid magic number: 0x%x\n", (unsigned)boot_info.magic); + return 1; + } + // Set the initial esp. + initial_esp = boot_info.stack_base; + // Dump the multiboot structure. + dump_multiboot(boot_info.multiboot_header); + //========================================================================== + pr_notice("Initialize the video...\n"); + vga_initialize(); + video_init(); + + //========================================================================== + printf(OS_NAME " " OS_VERSION); + printf("\nSite:"); + printf(OS_SITEURL); + printf("\n\n"); + + //========================================================================== + pr_notice("Initialize modules...\n"); + printf("Initialize modules..."); + if (!init_modules(boot_info.multiboot_header)) { + print_fail(); + return 1; + } + print_ok(); + pr_debug("End of modules: 0x%09p\n", get_address_after_modules()); + + //========================================================================== + pr_notice("Initialize physical memory manager...\n"); + printf("Initialize physical memory manager..."); + if (!pmmngr_init(&boot_info)) { + print_fail(); + return 1; + } + print_ok(); + + //========================================================================== + pr_notice("Initialize slab allocator.\n"); + printf("Initialize slab..."); + kmem_cache_init(); + print_ok(); + + //========================================================================== + // The Global Descriptor Table (GDT) is a data structure used by Intel + // x86-family processors starting with the 80286 in order to define the + // characteristics of the various memory areas used during program execution, + // including the base address, the size, and access privileges like + // executability and writability. These memory areas are called segments in + // Intel terminology. + pr_notice("Initialize Global Descriptor Table (GDT)...\n"); + printf("Initialize GDT..."); + init_gdt(); + print_ok(); + // The IDT is used to show the processor what Interrupt Service Routine + // (ISR) to call to handle an exception. IDT entries are also called + // Interrupt requests whenever a device has completed a request and needs to + // be serviced. + // ISRs are used to save the current processor state and set up the + // appropriate segment registers needed for kernel mode before the kernel’s + // C-level interrupt handler is called. To handle the right exception, the + // correct entry in the IDT should be pointed to the correct ISR. + pr_notice("Initialize Interrupt Service Routine(ISR)...\n"); + printf("Initialize IDT..."); + init_idt(); + print_ok(); + + //========================================================================== + pr_notice("Initialize system calls...\n"); + printf("Initialize system calls..."); + syscall_init(); + print_ok(); + + //========================================================================== + pr_notice("Initialize IRQ...\n"); + printf("Initialize IRQ..."); + pic8259_init_irq(); + print_ok(); + + //========================================================================== + pr_notice("Relocate modules.\n"); + printf("Relocate modules..."); + relocate_modules(); + print_ok(); + + //========================================================================== + pr_notice("Initialize paging.\n"); + printf("Initialize paging..."); + paging_init(&boot_info); + print_ok(); + + //========================================================================== + pr_notice("Initialize virtual memory mapping.\n"); + printf("Initialize virtual memory mapping..."); + virt_init(); + print_ok(); + + //========================================================================== + pr_notice("Install the timer.\n"); + printf("Setting up timer..."); + timer_install(); + print_ok(); + + //========================================================================== + pr_notice("Install RTC.\n"); + printf("Setting up RTC..."); + rtc_install(); + print_ok(); + + //========================================================================== + pr_notice("Initialize the filesystem.\n"); + printf("Initialize the filesystem..."); + vfs_init(); + print_ok(); + + //========================================================================== + pr_notice(" Initialize 'initrd'...\n"); + printf(" Initialize 'initrd'..."); + if (initrd_init_module()) { + print_fail(); + pr_emerg("Failed to register `initrd`!\n"); + return 1; + } + print_ok(); + if (do_mount("initrd", "/", "/dev/ram0")) { + pr_emerg("Failed to mount root `/`!\n"); + return 1; + } + + //========================================================================== + pr_notice(" Initialize 'procfs'...\n"); + printf(" Initialize 'procfs'..."); + if (procfs_module_init()) { + print_fail(); + pr_emerg("Failed to register `procfs`!\n"); + return 1; + } + print_ok(); + if (do_mount("procfs", "/proc", NULL)) { + pr_emerg("Failed to mount procfs at `/proc`!\n"); + return 1; + } + + //========================================================================== + pr_notice("Initialize video procfs file...\n"); + printf("Initialize video procfs file..."); + if (procv_module_init()) { + print_fail(); + pr_emerg("Failed to initialize `/proc/video`!\n"); + return 1; + } + print_ok(); + + //========================================================================== + pr_notice("Initialize system procfs file...\n"); + printf("Initialize system procfs file..."); + if (procs_module_init()) { + print_fail(); + pr_emerg("Failed to initialize proc system entries!\n"); + return 1; + } + print_ok(); + + //========================================================================== +#if 0 + // For debugging, show the list of PCI devices. + pci_debug_scan(); + // Scan for ata devices. + ata_initialize(); +#endif + + //========================================================================== + pr_notice("Setting up keyboard driver...\n"); + printf("Setting up keyboard driver..."); + keyboard_install(); + print_ok(); + + //========================================================================== +#if 0 + pr_notice("Install the mouse.\n"); + printf(" * Setting up mouse driver..."); + mouse_install(); // Install the mouse. + print_ok(); +#endif + + //========================================================================== + pr_notice("Uninstall the floppy driver.\n"); + fdc_disable_motor(); + + //========================================================================== + pr_notice("Initialize the scheduler.\n"); + printf("Initialize the scheduler..."); + scheduler_initialize(); + print_ok(); + + //========================================================================== + pr_notice("Init process management...\n"); + printf("Init process management..."); + if (!init_tasking()) { + print_fail(); + return 1; + } + print_ok(); + + //========================================================================== + pr_notice("Creating init process...\n"); + printf("Creating init process..."); + task_struct *init_p = process_create_init("/bin/init"); + if (!init_p) { + print_fail(); + return 1; + } + print_ok(); + + //========================================================================== + pr_notice("Initialize floating point unit...\n"); + printf("Initialize floating point unit..."); + if (!fpu_install()) { + print_fail(); + return 1; + } + print_ok(); + + //========================================================================== + pr_notice("Initialize signals...\n"); + printf("Initialize signals..."); + if (!signals_init()) { + print_fail(); + return 1; + } + print_ok(); + + // We have completed the booting procedure. + pr_notice("Booting done, jumping into init process.\n"); + // Print the welcome message. + printf("\n .: Welcome to MentOS :.\n\n"); + // Switch to the page directory of init. + paging_switch_directory_va(init_p->mm->pgd); + // Jump into init process. + scheduler_enter_user_jmp( + // Entry point. + init_p->thread.regs.eip, + // Stack pointer. + init_p->thread.regs.useresp); + // Enable interrupt requests. + sti(); + for (;;) {} + // We should not be here. + pr_emerg("Dear developer, we have to talk...\n"); + return 1; } diff --git a/mentos/src/kernel/sys.c b/mentos/src/kernel/sys.c index 3972c1c..a9e5581 100644 --- a/mentos/src/kernel/sys.c +++ b/mentos/src/kernel/sys.c @@ -1,20 +1,18 @@ /// MentOS, The Mentoring Operating system project /// @file sys.c /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "sys.h" #include "stdio.h" -#include "errno.h" -#include "mutex.h" -#include "reboot.h" -#include "stdatomic.h" +#include "sys/errno.h" +#include "sys/reboot.h" +#include "klib/stdatomic.h" +#include "klib/mutex.h" static void machine_power_off() { - while (true) - { + while (true) { cpu_relax(); } } @@ -22,15 +20,15 @@ static void machine_power_off() /// @brief Shutdown everything and perform a clean system power_off. static void kernel_power_off() { -// kernel_shutdown_prepare(SYSTEM_POWER_OFF); -// if (pm_power_off_prepare) -// { -// pm_power_off_prepare(); -// } -// migrate_to_reboot_cpu(); -// syscore_shutdown(); + // kernel_shutdown_prepare(SYSTEM_POWER_OFF); + // if (pm_power_off_prepare) + // { + // pm_power_off_prepare(); + // } + // migrate_to_reboot_cpu(); + // syscore_shutdown(); printf("Power down\n"); -// kmsg_dump(KMSG_DUMP_POWEROFF); + // kmsg_dump(KMSG_DUMP_POWEROFF); machine_power_off(); } @@ -39,38 +37,34 @@ int sys_reboot(int magic1, int magic2, unsigned int cmd, void *arg) static mutex_t reboot_mutex; // For safety, we require "magic" arguments. - if (magic1 != LINUX_REBOOT_MAGIC1 || - (magic2 != LINUX_REBOOT_MAGIC2 && - magic2 != LINUX_REBOOT_MAGIC2A && - magic2 != LINUX_REBOOT_MAGIC2B && - magic2 != LINUX_REBOOT_MAGIC2C)) - { + if (magic1 != LINUX_REBOOT_MAGIC1 || + (magic2 != LINUX_REBOOT_MAGIC2 && magic2 != LINUX_REBOOT_MAGIC2A && + magic2 != LINUX_REBOOT_MAGIC2B && magic2 != LINUX_REBOOT_MAGIC2C)) { return -EINVAL; } mutex_lock(&reboot_mutex, 0); - switch (cmd) - { - case LINUX_REBOOT_CMD_RESTART: - break; - case LINUX_REBOOT_CMD_CAD_ON: - break; - case LINUX_REBOOT_CMD_CAD_OFF: - break; - case LINUX_REBOOT_CMD_HALT: - break; - case LINUX_REBOOT_CMD_POWER_OFF: - kernel_power_off(); - break; - case LINUX_REBOOT_CMD_RESTART2: - break; - case LINUX_REBOOT_CMD_KEXEC: - break; - case LINUX_REBOOT_CMD_SW_SUSPEND: - break; - default: - return -EINVAL; + switch (cmd) { + case LINUX_REBOOT_CMD_RESTART: + break; + case LINUX_REBOOT_CMD_CAD_ON: + break; + case LINUX_REBOOT_CMD_CAD_OFF: + break; + case LINUX_REBOOT_CMD_HALT: + break; + case LINUX_REBOOT_CMD_POWER_OFF: + kernel_power_off(); + break; + case LINUX_REBOOT_CMD_RESTART2: + break; + case LINUX_REBOOT_CMD_KEXEC: + break; + case LINUX_REBOOT_CMD_SW_SUSPEND: + break; + default: + return -EINVAL; } mutex_unlock(&reboot_mutex); diff --git a/mentos/src/klib/assert.c b/mentos/src/klib/assert.c new file mode 100644 index 0000000..9621daf --- /dev/null +++ b/mentos/src/klib/assert.c @@ -0,0 +1,21 @@ +/// MentOS, The Mentoring Operating system project +/// @file assert.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "assert.h" +#include "stdio.h" +#include "system/panic.h" + +void __assert_fail(const char *assertion, const char *file, const char *function, unsigned int line) +{ + char message[1024]; + sprintf(message, + "FILE: %s\n" + "FUNC: %s\n" + "LINE: %d\n\n" + "Assertion `%s` failed.\n", + file, (function ? function : "NO_FUN"), line, assertion); + kernel_panic(message); +} diff --git a/mentos/src/klib/ctype.c b/mentos/src/klib/ctype.c new file mode 100644 index 0000000..88ce35f --- /dev/null +++ b/mentos/src/klib/ctype.c @@ -0,0 +1,63 @@ +/// MentOS, The Mentoring Operating system project +/// @file ctype.c +/// @brief Functions related to character handling. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "ctype.h" + +/// Distance from a uppercase character to the correspondent lowercase in ASCII. +#define OFFSET 32 + +int isdigit(int c) +{ + return (c >= 48 && c <= 57); +} + +int isalpha(int c) +{ + return ((c >= 65 && c <= 90) || (c >= 97 && c <= 122)); +} + +int isalnum(int c) +{ + return (isalpha(c) || isdigit(c)); +} + +int isxdigit(int c) +{ + return (isdigit(c) || (c >= 65 && c <= 70)); +} + +int islower(int c) +{ + return (c >= 97 && c <= 122); +} + +int isupper(int c) +{ + return (c >= 65 && c <= 90); +} + +int tolower(int c) +{ + if (isalpha(c) == 0 || islower(c)) { + return c; + } + + return c + OFFSET; +} + +int toupper(int c) +{ + if (isalpha(c) == 0 || isupper(c)) { + return c; + } + + return c - OFFSET; +} + +int isspace(int c) +{ + return (c == ' '); +} diff --git a/mentos/src/klib/fcvt.c b/mentos/src/klib/fcvt.c new file mode 100644 index 0000000..750f564 --- /dev/null +++ b/mentos/src/klib/fcvt.c @@ -0,0 +1,104 @@ +/// MentOS, The Mentoring Operating system project +/// @file fcvt.c +/// @brief Define the functions required to turn double values into a string. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "fcvt.h" +#include "math.h" + +static void cvt(double arg, int ndigits, int *decpt, int *sign, char *buf, unsigned buf_size, int eflag) +{ + int r2; + double fi, fj; + char *p, *p1; + + char *buf_end = (buf + buf_size); + + if (ndigits < 0) { + ndigits = 0; + } + + if (ndigits >= buf_size - 1) { + ndigits = buf_size - 2; + } + + r2 = 0; + *sign = 0; + p = &buf[0]; + + if (arg < 0) { + *sign = 1; + arg = -arg; + } + + arg = modf(arg, &fi); + p1 = buf_end; + + if (fi != 0) { + p1 = buf_end; + while (fi != 0) { + fj = modf(fi / 10, &fi); + *--p1 = (int)((fj + .03) * 10) + '0'; + r2++; + } + while (p1 < buf_end) { + *p++ = *p1++; + } + } else if (arg > 0) { + while ((fj = arg * 10) < 1) { + arg = fj; + r2--; + } + } + + p1 = &buf[ndigits]; + if (eflag == 0) { + p1 += r2; + } + + *decpt = r2; + if (p1 < &buf[0]) { + buf[0] = '\0'; + return; + } + + while (p <= p1 && p < buf_end) { + arg *= 10; + arg = modf(arg, &fj); + *p++ = (int)fj + '0'; + } + + if (p1 >= buf_end) { + buf[buf_size - 1] = '\0'; + return; + } + + p = p1; + *p1 += 5; + while (*p1 > '9') { + *p1 = '0'; + if (p1 > buf) { + ++*--p1; + } else { + *p1 = '1'; + (*decpt)++; + if (eflag == 0) { + if (p > buf) + *p = '0'; + p++; + } + } + } + *p = '\0'; +} + +void ecvtbuf(double arg, int chars, int *decpt, int *sign, char *buf, unsigned buf_size) +{ + cvt(arg, chars, decpt, sign, buf, buf_size, 1); +} + +void fcvtbuf(double arg, int decimals, int *decpt, int *sign, char *buf, unsigned buf_size) +{ + cvt(arg, decimals, decpt, sign, buf, buf_size, 0); +} diff --git a/mentos/src/klib/hashmap.c b/mentos/src/klib/hashmap.c new file mode 100644 index 0000000..3033aba --- /dev/null +++ b/mentos/src/klib/hashmap.c @@ -0,0 +1,254 @@ +/// MentOS, The Mentoring Operating system project +/// @file hashmap.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "klib/hashmap.h" +#include "assert.h" +#include "string.h" +#include "mem/slab.h" + +/// @brief Stores information of an entry of the hashmap. +struct hashmap_entry_t { + /// Key of the entry. + char *key; + /// Value of the entry. + void *value; + /// Pointer to the next entry. + struct hashmap_entry_t *next; +}; + +/// @brief Stores information of a hashmap. +struct hashmap_t { + /// Hashing function, used to generate hash keys. + hashmap_hash_t hash_func; + /// Comparison function, used to compare hash keys. + hashmap_comp_t hash_comp; + /// Key duplication function, used to duplicate hash keys. + hashmap_dupe_t hash_key_dup; + /// Key deallocation function, used to free the memory occupied by hash keys. + hashmap_free_t hash_key_free; + /// Size of the hashmap. + unsigned int size; + /// List of entries. + hashmap_entry_t **entries; +}; + +static inline hashmap_t *__alloc_hashmap() +{ + hashmap_t *hashmap = kmalloc(sizeof(hashmap_t)); + memset(hashmap, 0, sizeof(hashmap_t)); + return hashmap; +} + +static inline hashmap_entry_t *__alloc_entry() +{ + hashmap_entry_t *entry = kmalloc(sizeof(hashmap_entry_t)); + memset(entry, 0, sizeof(hashmap_entry_t)); + return entry; +} + +static inline void __dealloc_entry(hashmap_entry_t *entry) +{ + assert(entry && "Invalid pointer to an entry."); + kfree(entry); +} + +static inline hashmap_entry_t **__alloc_entries(unsigned int size) +{ + hashmap_entry_t **entries = kmalloc(sizeof(hashmap_entry_t *) * size); + memset(entries, 0, sizeof(hashmap_entry_t *) * size); + return entries; +} + +static inline void __dealloc_entries(hashmap_entry_t **entries) +{ + assert(entries && "Invalid pointer to entries."); + kfree(entries); +} + +unsigned int hashmap_int_hash(const void *key) +{ + return (unsigned int)key; +} + +int hashmap_int_comp(const void *a, const void *b) +{ + return (int)a == (int)b; +} + +unsigned int hashmap_str_hash(const void *_key) +{ + unsigned int hash = 0; + const char *key = (const char *)_key; + char c; + // This is the so-called "sdbm" hash. It comes from a piece of public + // domain code from a clone of ndbm. + while ((c = *key++)) + hash = c + (hash << 6) + (hash << 16) - hash; + return hash; +} + +int hashmap_str_comp(const void *a, const void *b) +{ + return !strcmp(a, b); +} + +void *hashmap_do_not_duplicate(const void *value) +{ + return (void *)value; +} + +void hashmap_do_not_free(void *value) +{ + (void)value; +} + +hashmap_t *hashmap_create( + unsigned int size, + hashmap_hash_t hash_fun, + hashmap_comp_t comp_fun, + hashmap_dupe_t dupe_fun, + hashmap_free_t key_free_fun) +{ + // Allocate the map. + hashmap_t *map = __alloc_hashmap(); + // Initialize the entries. + map->size = size; + map->entries = __alloc_entries(size); + // Initialize its functions. + map->hash_func = hash_fun; + map->hash_comp = comp_fun; + map->hash_key_dup = dupe_fun; + map->hash_key_free = key_free_fun; + return map; +} + +void hashmap_free(hashmap_t *map) +{ + for (unsigned int i = 0; i < map->size; ++i) { + hashmap_entry_t *x = map->entries[i], *p; + while (x) { + p = x; + x = x->next; + map->hash_key_free(p->key); + __dealloc_entry(p); + } + } + __dealloc_entries(map->entries); +} + +void *hashmap_set(hashmap_t *map, const void *key, void *value) +{ + unsigned int hash = map->hash_func(key) % map->size; + hashmap_entry_t *x = map->entries[hash]; + + if (x == NULL) { + hashmap_entry_t *e = __alloc_entry(); + e->key = map->hash_key_dup(key); + e->value = value; + e->next = NULL; + map->entries[hash] = e; + + return NULL; + } + + hashmap_entry_t *p = NULL; + + do { + if (map->hash_comp(x->key, key)) { + void *out = x->value; + x->value = value; + + return out; + } + p = x; + x = x->next; + } while (x); + + hashmap_entry_t *e = __alloc_entry(); + e->key = map->hash_key_dup(key); + e->value = value; + e->next = NULL; + p->next = e; + + return NULL; +} + +void *hashmap_get(hashmap_t *map, const void *key) +{ + unsigned int hash = map->hash_func(key) % map->size; + for (hashmap_entry_t *x = map->entries[hash]; x; x = x->next) + if (map->hash_comp(x->key, key)) + return x->value; + return NULL; +} + +void *hashmap_remove(hashmap_t *map, const void *key) +{ + unsigned int hash = map->hash_func(key) % map->size; + hashmap_entry_t *x = map->entries[hash]; + + if (x == NULL) { + return NULL; + } + if (map->hash_comp(x->key, key)) { + void *out = x->value; + map->entries[hash] = x->next; + map->hash_key_free(x->key); + __dealloc_entry(x); + return out; + } + + hashmap_entry_t *p = x; + x = x->next; + do { + if (map->hash_comp(x->key, key)) { + void *out = x->value; + p->next = x->next; + map->hash_key_free(x->key); + __dealloc_entry(x); + return out; + } + p = x; + x = x->next; + } while (x); + + return NULL; +} + +int hashmap_is_empty(hashmap_t *map) +{ + for (unsigned int i = 0; i < map->size; ++i) + if (map->entries[i]) + return 0; + return 1; +} + +int hashmap_has(hashmap_t *map, const void *key) +{ + unsigned int hash = map->hash_func(key) % map->size; + for (hashmap_entry_t *x = map->entries[hash]; x; x = x->next) + if (map->hash_comp(x->key, key)) + return 1; + return 0; +} + +list_t *hashmap_keys(hashmap_t *map) +{ + list_t *l = list_create(); + for (unsigned int i = 0; i < map->size; ++i) + for (hashmap_entry_t *x = map->entries[i]; x; x = x->next) + list_insert_back(l, x->key); + return l; +} + +list_t *hashmap_values(hashmap_t *map) +{ + list_t *l = list_create(); + for (unsigned int i = 0; i < map->size; ++i) + for (hashmap_entry_t *x = map->entries[i]; x; x = x->next) + list_insert_back(l, x->value); + return l; +} diff --git a/mentos/src/klib/libgen.c b/mentos/src/klib/libgen.c new file mode 100644 index 0000000..10283c6 --- /dev/null +++ b/mentos/src/klib/libgen.c @@ -0,0 +1,142 @@ +/// MentOS, The Mentoring Operating system project +/// @file libgen.c +/// @brief String routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "system/syscall.h" +#include "libgen.h" +#include "string.h" +#include "fs/initrd.h" +#include "limits.h" +#include "assert.h" +#include "mem/paging.h" + +int parse_path(char *out, char **cur, char sep, size_t max) +{ + if (**cur == '\0') { + return 0; + } + + *out++ = **cur; + ++*cur; + --max; + + while ((max > 0) && (**cur != '\0') && (**cur != sep)) { + *out++ = **cur; + ++*cur; + --max; + } + + *out = '\0'; + + return 1; +} + +char *dirname(const char *path) +{ + static char s[PATH_MAX]; + + static char dot[2] = "."; + + // Check the input path. + if (path == NULL) { + return dot; + } + + // Copy the path to the support string. + strcpy(s, path); + + // Get the last occurrence of '/'. + char *last_slash = strrchr(s, '/'); + + if (last_slash == s) { + // If the slash is acutally the first character of the string, move the + // pointer to the last slash after it. + ++last_slash; + } else if ((last_slash != NULL) && (last_slash[1] == '\0')) { + // If the slash is the last character, we need to search before it. + last_slash = memchr(s, '/', last_slash - s); + } + + if (last_slash != NULL) { + // If we have found it, close the string. + last_slash[0] = '\0'; + } else { + // Otherwise, return '.'. + return dot; + } + + return s; +} + +char *basename(const char *path) +{ + char *p = strrchr(path, '/'); + + return p ? p + 1 : (char *)path; +} + +char *realpath(const char *path, char *resolved) +{ + assert(path && "Provided null path."); + if (resolved == NULL) + resolved = kmalloc(sizeof(char) * PATH_MAX); + char abspath[PATH_MAX]; + // Initialize the absolute path. + memset(abspath, '\0', PATH_MAX); + int remaining; + if (path[0] != '/') { + // Get the current task. + sys_getcwd(abspath, PATH_MAX); + // Check the current task. + assert((strlen(abspath) > 0) && "There is no current task."); + // Check that the current working directory is an absolute path. + assert((abspath[0] == '/') && "Current working directory is not an absolute path."); + // Count the remaining space in the absolute path. + remaining = PATH_MAX - strlen(abspath) - 1; + // Add the separator to the end (se strncat for safety). + strncat(abspath, "/", remaining); + // Set the number of characters that should be copied, + // based on the current absolute path. + remaining = PATH_MAX - strlen(abspath) - 1; + // Append the path. + strncat(abspath, path, remaining); + } else { + // Copy the path into the absolute path. + strncpy(abspath, path, PATH_MAX - 1); + } + // Count the remaining space in the absolute path. + remaining = PATH_MAX - strlen(abspath) - 1; + // Add the separator to the end (se strncat for safety). + strncat(abspath, "/", remaining); + + int absidx = 0, pathidx = 0; + + while (abspath[absidx]) { + // Skip multiple consecutive / characters + if (!strncmp("//", abspath + absidx, 2)) { + absidx++; + } + // Go to previous directory if /../ is found + else if (!strncmp("/../", abspath + absidx, 4)) { + // Go to a valid path character (pathidx points to the next one) + if (pathidx) + pathidx--; + while (pathidx && resolved[pathidx] != '/') { + pathidx--; + } + absidx += 3; + } else if (!strncmp("/./", abspath + absidx, 3)) { + absidx += 2; + } else { + resolved[pathidx++] = abspath[absidx++]; + } + } + // Remove the last / + if (pathidx > 1) + resolved[pathidx - 1] = '\0'; + else + resolved[1] = '\0'; + return resolved; +} \ No newline at end of file diff --git a/mentos/src/klib/list.c b/mentos/src/klib/list.c new file mode 100644 index 0000000..836c6c6 --- /dev/null +++ b/mentos/src/klib/list.c @@ -0,0 +1,331 @@ +/// MentOS, The Mentoring Operating system project +/// @file list.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "klib/list.h" +#include "assert.h" +#include "string.h" +#include "mem/slab.h" + +static inline listnode_t *__node_alloc() +{ + listnode_t *node = kmalloc(sizeof(listnode_t)); + memset(node, 0, sizeof(listnode_t)); + return node; +} + +static inline void __node_dealloc(listnode_t *node) +{ + assert(node && "Invalid pointer to node."); + kfree(node); +} + +static inline void __list_dealloc(list_t *list) +{ + assert(list && "Invalid pointer to list."); + kfree(list); +} + +list_t *list_create() +{ + list_t *list = kmalloc(sizeof(list_t)); + memset(list, 0, sizeof(list_t)); + return list; +} + +unsigned int list_size(list_t *list) +{ + assert(list && "List is null."); + + return list->size; +} + +int list_empty(list_t *list) +{ + assert(list && "List is null."); + + if (list->size == 0) { + return 1; + } + + return 0; +} + +listnode_t *list_insert_front(list_t *list, void *value) +{ + assert(list && "List is null."); + assert(value && "Value is null."); + + // Create a new node. + listnode_t *node = __node_alloc(); + + list->head->prev = node; + node->next = list->head; + node->value = value; + + // If it's the first element, then it's both head and tail + if (!list->head) { + list->tail = node; + } + + list->head = node; + list->size++; + + return node; +} + +listnode_t *list_insert_back(list_t *list, void *value) +{ + assert(list && "List is null."); + assert(value && "Value is null."); + + // Create a new node. + listnode_t *node = __node_alloc(); + node->prev = list->tail; + + if (list->tail != NULL) { + list->tail->next = node; + } + node->value = value; + + if (list->head == NULL) { + list->head = node; + } + + list->tail = node; + list->size++; + return node; +} + +void *list_remove_node(list_t *list, listnode_t *node) +{ + assert(list && "List is null."); + assert(node && "Node is null."); + + if (list->head == node) { + return list_remove_front(list); + } else if (list->tail == node) { + return list_remove_back(list); + } + + void *value = node->value; + node->next->prev = node->prev; + node->prev->next = node->next; + list->size--; + __node_dealloc(node); + + return value; +} + +void *list_remove_front(list_t *list) +{ + assert(list && "List is null."); + + if (list->head == NULL) { + return NULL; + } + + listnode_t *node = list->head; + void *value = node->value; + list->head = node->next; + + if (list->head) { + list->head->prev = NULL; + } + __node_dealloc(node); + list->size--; + + return value; +} + +void *list_remove_back(list_t *list) +{ + assert(list && "List is null."); + + if (list->head == NULL) { + return NULL; + } + + listnode_t *node = list->tail; + void *value = node->value; + list->tail = node->prev; + + if (list->tail) { + list->tail->next = NULL; + } + __node_dealloc(node); + list->size--; + + return value; +} + +listnode_t *list_find(list_t *list, void *value) +{ + listnode_foreach(listnode, list) + { + if (listnode->value == value) { + return listnode; + } + } + + return NULL; +} + +void list_push_back(list_t *list, void *value) +{ + assert(list && "List is null."); + assert(value && "Value is null."); + + list_insert_back(list, value); +} + +listnode_t *list_pop_back(list_t *list) +{ + assert(list && "List is null."); + + if (!list->head) { + return NULL; + } + + listnode_t *node = list->tail; + list->tail = node->prev; + + if (list->tail) { + list->tail->next = NULL; + } + + list->size--; + + return node; +} + +listnode_t *list_pop_front(list_t *list) +{ + assert(list && "List is null."); + + if (!list->head) { + return NULL; + } + + listnode_t *node = list->head; + list->head = list->head->next; + list->size--; + + return node; +} + +void list_push_front(list_t *list, void *value) +{ + assert(list && "List is null."); + assert(value && "Value is null."); + + list_insert_front(list, value); +} + +void *list_peek_front(list_t *list) +{ + assert(list && "List is null."); + + if (!list->head) { + return NULL; + } + + return list->head->value; +} + +void *list_peek_back(list_t *list) +{ + assert(list && "List is null."); + + if (!list->tail) { + return NULL; + } + + return list->tail->value; +} + +int list_get_index_of_value(list_t *list, void *value) +{ + assert(list && "List is null."); + assert(value && "Value is null."); + + int idx = 0; + listnode_foreach(listnode, list) + { + if (listnode->value == value) { + return idx; + } + ++idx; + } + + return -1; +} + +listnode_t *list_get_node_by_index(list_t *list, unsigned int index) +{ + assert(list && "List is null."); + + if (index >= list_size(list)) { + return NULL; + } + + unsigned int curr = 0; + listnode_foreach(listnode, list) + { + if (index == curr) { + return listnode; + } + curr++; + } + return NULL; +} + +void *list_remove_by_index(list_t *list, unsigned int index) +{ + assert(list && "List is null."); + + listnode_t *node = list_get_node_by_index(list, index); + if (node != NULL) { + return list_remove_node(list, node); + } + + return NULL; +} + +void list_destroy(list_t *list) +{ + assert(list && "List is null."); + + // Deallocate each node. + listnode_t *node = list->head; + while (node != NULL) { + listnode_t *save = node; + node = node->next; + __node_dealloc(save); + } + + // Free the list. + __list_dealloc(list); +} + +void list_merge(list_t *target, list_t *source) +{ + assert(target && "Target list is null."); + assert(source && "Source list is null."); + + // Destructively merges source into target. + if (target->tail) { + target->tail->next = source->head; + } else { + target->head = source->head; + } + + if (source->tail) { + target->tail = source->tail; + } + + target->size += source->size; + __list_dealloc(source); +} diff --git a/mentos/src/klib/math.c b/mentos/src/klib/math.c new file mode 100644 index 0000000..35ffffd --- /dev/null +++ b/mentos/src/klib/math.c @@ -0,0 +1,180 @@ +/// MentOS, The Mentoring Operating system project +/// @file math.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "math.h" +#include "stdint.h" + +double round(double x) +{ + double out; + __asm__ __volatile__("fldln2; fldl %1; frndint" + : "=t"(out) + : "m"(x)); + return out; +} + +double floor(double x) +{ + if (x > -1.0 && x < 1.0) { + if (x >= 0) + return 0.0; + return -1.0; + } + int i = (int)x; + if (x < 0) + return (double)(i - 1); + return (double)i; +} + +double ceil(double x) +{ + if (x > -1.0 && x < 1.0) { + if (x <= 0) + return 0.0; + return 1.0; + } + int i = (int)x; + if (x > 0) + return (double)(i + 1); + return (double)i; +} + +double pow(double base, double exponent) +{ + double out; + __asm__ __volatile__("fyl2x;" + "fld %%st;" + "frndint;" + "fsub %%st,%%st(1);" + "fxch;" + "fchs;" + "f2xm1;" + "fld1;" + "faddp;" + "fxch;" + "fld1;" + "fscale;" + "fstp %%st(1);" + "fmulp;" + : "=t"(out) + : "0"(base), "u"(exponent) + : "st(1)"); + return out; +} + +double exp(double x) +{ + return pow(M_E, x); +} + +double fabs(double x) +{ + double out; + __asm__ __volatile__("fldln2; fldl %1; fabs" + : "=t"(out) + : "m"(x)); + return out; +} + +float fabsf(float x) +{ + float out; + __asm__ __volatile__("fldln2; fldl %1; fabs" + : "=t"(out) + : "m"(x)); + return out; +} + +double sqrt(double x) +{ + double out; + __asm__ __volatile__("fldln2; fldl %1; fsqrt" + : "=t"(out) + : "m"(x)); + return out; +} + +float sqrtf(float x) +{ + float out; + __asm__ __volatile__("fldln2; fldl %1; fsqrt" + : "=t"(out) + : "m"(x)); + return out; +} + +int isinf(double x) +{ + union { + unsigned long long u; + double f; + } ieee754; + ieee754.f = x; + return ((unsigned)(ieee754.u >> 32U) & 0x7fffffffU) == 0x7ff00000U && + ((unsigned)ieee754.u == 0); +} + +int isnan(double x) +{ + union { + unsigned long long u; + double f; + } ieee754; + ieee754.f = x; + return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) + + ((unsigned)ieee754.u != 0) > + 0x7ff00000; +} + +double log10(double x) +{ + return ln(x) / ln(10); +} + +double ln(double x) +{ + double out; + __asm__ __volatile__("fldln2; fldl %1; fyl2x" + : "=t"(out) + : "m"(x)); + return out; +} + +double logx(double x, double y) +{ + // Base may not equal 1 or be negative. + if (y == 1.f || y < 0.f || ln(y) == 0.f) + return 0.f; + return ln(x) / ln(y); +} + +/// Max power for forward and reverse projections. +#define MAXPOWTWO 4.503599627370496000E+15 + +double modf(double x, double *intpart) +{ + register double absvalue; + if ((absvalue = (x >= 0.0) ? x : -x) >= MAXPOWTWO) { + // It must be an integer. + (*intpart) = x; + } else { + // Shift fraction off right. + (*intpart) = absvalue + MAXPOWTWO; + // Shift back without fraction. + (*intpart) -= MAXPOWTWO; + + // Above arithmetic might round. + while ((*intpart) > absvalue) { + // Test again just to be sure. + (*intpart) -= 1.0; + } + if (x < 0.0) { + (*intpart) = -(*intpart); + } + } + // Signed fractional part. + return (x - (*intpart)); +} diff --git a/mentos/src/klib/mutex.c b/mentos/src/klib/mutex.c new file mode 100644 index 0000000..6a921ea --- /dev/null +++ b/mentos/src/klib/mutex.c @@ -0,0 +1,34 @@ +/// MentOS, The Mentoring Operating system project +/// @file mutex.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "klib/mutex.h" +#include "misc/debug.h" + +void mutex_lock(mutex_t *mutex, uint32_t owner) +{ + pr_debug("[%d] Trying to lock mutex...\n", owner); + int failure = 1; + + while (mutex->state == 0 || failure || mutex->owner != owner) { + failure = 1; + if (mutex->state == 0) { + asm("movl $0x01,%%eax\n\t" // move 1 to eax + "xchg %%eax,%0\n\t" // try to set the lock bit + "mov %%eax,%1\n\t" // export our result to a test var + : "=m"(mutex->state), "=r"(failure) + : "m"(mutex->state) + : "%eax"); + } + if (failure == 0) { + mutex->owner = owner; //test to see if we got the lock bit + } + } +} + +void mutex_unlock(mutex_t *mutex) +{ + mutex->state = 0; +} diff --git a/mentos/src/klib/ndtree.c b/mentos/src/klib/ndtree.c new file mode 100644 index 0000000..095d4d8 --- /dev/null +++ b/mentos/src/klib/ndtree.c @@ -0,0 +1,378 @@ +/// MentOS, The Mentoring Operating system project +/// @file ndtree.c +/// @brief Red/Black tree. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "misc/debug.h" +#include "klib/ndtree.h" +#include "assert.h" +#include "klib/list_head.h" +#include "mem/slab.h" + +// ============================================================================ +// Tree types. + +/// @brief Stores data about an NDTree node. +struct ndtree_node_t { + /// User provided, used indirectly via ndtree_tree_cmp_f. + void *value; + /// Pointer to the parent. + ndtree_node_t *parent; + /// List of siblings. + list_head siblings; + /// List of children. + list_head children; +}; + +/// @brief Stores data about an NDTree. +struct ndtree_t { + /// Comparison function. + ndtree_tree_cmp_f cmp; + /// Size of the tree. + size_t size; + /// Pointer to the root node. + ndtree_node_t *root; + /// List of orphans. + list_head orphans; +}; + +/// @brief Stores data about an NDTree iterator. +struct ndtree_iter_t { + /// Pointer to the head of the list. + list_head *head; + /// Pointer to the current element of the list. + list_head *current; +}; + +// ============================================================================ +// Default Comparison functions. +static inline int __ndtree_tree_node_cmp_ptr_cb(ndtree_t *self, void *a, void *b) +{ + return (a > b) - (a < b); +} + +// ============================================================================ +// Node management functions. + +ndtree_node_t *ndtree_node_alloc() +{ + return kmalloc(sizeof(ndtree_node_t)); +} + +ndtree_node_t *ndtree_node_create(void *value) +{ + ndtree_node_t *node = ndtree_node_alloc(); + node = ndtree_node_init(node, value); + return node; +} + +ndtree_node_t *ndtree_node_init(ndtree_node_t *node, void *value) +{ + if (node) { + node->value = value; + node->parent = NULL; + list_head_init(&node->siblings); + list_head_init(&node->children); + } + return node; +} + +void ndtree_node_set_value(ndtree_node_t *node, void *value) +{ + if (node && value) { + node->value = value; + } +} + +void *ndtree_node_get_value(ndtree_node_t *node) +{ + if (node) + return node->value; + return NULL; +} + +void ndtree_set_root(ndtree_t *tree, ndtree_node_t *node) +{ + tree->root = node; + ++tree->size; +} + +ndtree_node_t *ndtree_create_root(ndtree_t *tree, void *value) +{ + ndtree_node_t *node = ndtree_node_create(value); + ndtree_set_root(tree, node); + return node; +} + +ndtree_node_t *ndtree_get_root(ndtree_t *tree) +{ + return tree->root; +} + +void ndtree_add_child_to_node(ndtree_t *tree, ndtree_node_t *parent, ndtree_node_t *child) +{ + child->parent = parent; + list_head_add(&child->siblings, &parent->children); + ++tree->size; +} + +ndtree_node_t *ndtree_create_child_of_node(ndtree_t *tree, ndtree_node_t *parent, void *value) +{ + ndtree_node_t *child = ndtree_node_create(value); + ndtree_add_child_to_node(tree, parent, child); + return child; +} + +unsigned int ndtree_node_count_children(ndtree_node_t *node) +{ + unsigned int children = 0; + if (node) { + list_for_each_decl(it, &node->children) + { + ++children; + } + } + return children; +} + +void ndtree_node_dealloc(ndtree_node_t *node) +{ + if (node) + kfree(node); +} + +// ============================================================================ +// Tree management functions. +ndtree_t *ndtree_tree_alloc() +{ + return kmalloc(sizeof(ndtree_t)); +} + +ndtree_t *ndtree_tree_create(ndtree_tree_cmp_f cmp) +{ + return ndtree_tree_init(ndtree_tree_alloc(), cmp); +} + +ndtree_t *ndtree_tree_init(ndtree_t *tree, ndtree_tree_cmp_f node_cmp_cb) +{ + if (tree) { + tree->size = 0; + tree->cmp = node_cmp_cb ? node_cmp_cb : __ndtree_tree_node_cmp_ptr_cb; + tree->root = NULL; + } + return tree; +} + +static void __ndtree_tree_dealloc_rec(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f node_cb) +{ + if (node && node_cb) { + if (!list_head_empty(&node->children)) { + list_head *it_save; + list_for_each_decl(it, &node->children) + { + ndtree_node_t *entry = list_entry(it, ndtree_node_t, siblings); + it_save = it->prev; + list_head_del(it); + it = it_save; + __ndtree_tree_dealloc_rec(tree, entry, node_cb); + } + } + node_cb(tree, node); + kfree(node); + } +} + +void ndtree_tree_dealloc(ndtree_t *tree, ndtree_tree_node_f node_cb) +{ + if (tree && tree->root && node_cb) + __ndtree_tree_dealloc_rec(tree, tree->root, node_cb); + kfree(tree); +} + +static ndtree_node_t *__ndtree_tree_find_rec(ndtree_t *tree, ndtree_tree_cmp_f cmp, void *value, ndtree_node_t *node) +{ + ndtree_node_t *result = NULL; + if (tree && cmp && node && value) { + if (cmp(tree, node->value, value) == 0) { + result = node; + } else if (!list_head_empty(&node->children)) { + list_for_each_decl(it, &node->children) + { + ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings); + if ((result = __ndtree_tree_find_rec(tree, cmp, value, child)) != NULL) { + break; + } + } + } + } + return result; +} + +ndtree_node_t *ndtree_tree_find(ndtree_t *tree, ndtree_tree_cmp_f cmp, void *value) +{ + if (tree && tree->root && value) + return __ndtree_tree_find_rec(tree, cmp, value, tree->root); + return NULL; +} + +ndtree_node_t *ndtree_node_find(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_cmp_f cmp, void *value) +{ + if (tree && node && value) { + // Check only if the node has children. + if (!list_head_empty(&node->children)) { + // Check which compare function we need to use. + ndtree_tree_cmp_f cmp_fun = cmp ? cmp : tree->cmp; + // If neither the tree nor the function argument are valid, rollback to the + // default comparison function. + if (cmp_fun == NULL) + cmp_fun = __ndtree_tree_node_cmp_ptr_cb; + // Iterate throught the children. + list_for_each_decl(it, &node->children) + { + ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings); + if (cmp_fun(tree, child->value, value) == 0) + return child; + } + } + } + return NULL; +} + +unsigned int ndtree_tree_size(ndtree_t *tree) +{ + if (tree) + return tree->size; + return 0; +} + +int ndtree_tree_remove_node_with_cb(ndtree_t *tree, ndtree_node_t *node, ndtree_tree_node_f node_cb) +{ + if (tree && node) { + // Remove the node from the parent list. + list_head_del(&node->siblings); + // If the node has children, we need to migrate them. + if (!list_head_empty(&node->children)) { + // The new parent, by default it is NULL. + ndtree_node_t *new_parent = NULL; + // The new list, by default it is the list of orphans of the tree. + list_head *new_list = &tree->orphans; + // If the found node has a parent, we need to set the variables + // so that we can migrate the children. + if (node->parent) { + new_parent = node->parent; + new_list = &node->parent->children; + } + // Migrate the children. + list_for_each_decl(it, &node->children) + { + ndtree_node_t *child = list_entry(it, ndtree_node_t, siblings); + child->parent = new_parent; + } + // Merge the lists. + list_head_merge(new_list, &node->children); + } + if (node_cb) + node_cb(tree, node); + else + ndtree_node_dealloc(node); + --tree->size; + return 1; + } + return 0; +} + +int ndtree_tree_remove_with_cb(ndtree_t *tree, void *value, ndtree_tree_node_f node_cb) +{ + if (tree && value) { + ndtree_node_t *node = ndtree_tree_find(tree, tree->cmp, value); + return ndtree_tree_remove_node_with_cb(tree, node, node_cb); + } + return 0; +} + +// ============================================================================ +// Iterators. +ndtree_iter_t *ndtree_iter_alloc() +{ + ndtree_iter_t *iter = kmalloc(sizeof(ndtree_iter_t)); + iter->head = NULL; + iter->current = NULL; + return iter; +} + +void ndtree_iter_dealloc(ndtree_iter_t *iter) +{ + if (iter) + kfree(iter); +} + +ndtree_node_t *ndtree_iter_first(ndtree_node_t *node, ndtree_iter_t *iter) +{ + if (node && iter) { + if (!list_head_empty(&node->children)) { + iter->head = &node->children; + iter->current = iter->head->next; + return list_entry(iter->current, ndtree_node_t, siblings); + } + } + return NULL; +} + +ndtree_node_t *ndtree_iter_last(ndtree_node_t *node, ndtree_iter_t *iter) +{ + if (node && iter) { + if (!list_head_empty(&node->children)) { + iter->head = &node->children; + iter->current = iter->head->prev; + return list_entry(iter->current, ndtree_node_t, siblings); + } + } + return NULL; +} + +ndtree_node_t *ndtree_iter_next(ndtree_iter_t *iter) +{ + if (iter) { + if (iter->current->next != iter->head) { + iter->current = iter->current->next; + return list_entry(iter->current, ndtree_node_t, siblings); + } + } + return NULL; +} + +ndtree_node_t *ndtree_iter_prev(ndtree_iter_t *iter) +{ + if (iter) { + if (iter->current->next != iter->head) { + iter->current = iter->current->prev; + return list_entry(iter->current, ndtree_node_t, siblings); + } + } + return NULL; +} + +// ============================================================================ +// Tree debugging functions. +static void __ndtree_tree_visitor_iter(ndtree_t *tree, + ndtree_node_t *node, + ndtree_tree_node_f enter_fun, + ndtree_tree_node_f exit_fun) +{ + assert(tree); + assert(node); + if (enter_fun) + enter_fun(tree, node); + if (!list_head_empty(&node->children)) + list_for_each_decl(it, &node->children) + __ndtree_tree_visitor_iter(tree, list_entry(it, ndtree_node_t, siblings), enter_fun, exit_fun); + if (exit_fun) + exit_fun(tree, node); +} + +void ndtree_tree_visitor(ndtree_t *tree, ndtree_tree_node_f enter_fun, ndtree_tree_node_f exit_fun) +{ + if (tree && tree->root) + __ndtree_tree_visitor_iter(tree, tree->root, enter_fun, exit_fun); +} \ No newline at end of file diff --git a/mentos/src/klib/rbtree.c b/mentos/src/klib/rbtree.c new file mode 100644 index 0000000..7f6abe8 --- /dev/null +++ b/mentos/src/klib/rbtree.c @@ -0,0 +1,564 @@ +/// MentOS, The Mentoring Operating system project +/// @file rbtree.c +/// @brief Red/Black tree. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "klib/rbtree.h" + +#include "assert.h" +#include "misc/debug.h" +#include "mem/slab.h" + +/// @brief Stores information of a node. +struct rbtree_node_t { + /// Color red (1), black (0) + int red; + /// Link left [0] and right [1] + rbtree_node_t *link[2]; + /// User provided, used indirectly via rbtree_tree_node_cmp_f. + void *value; +}; + +/// @brief Stores information of a rbtree. +struct rbtree_t { + /// Root of the tree. + rbtree_node_t *root; + /// Comparison function for insertion. + rbtree_tree_node_cmp_f cmp; + /// Size of the tree. + unsigned int size; +}; + +/// @brief Stores information for iterating a rbtree. +struct rbtree_iter_t { + /// Pointer to the tree itself. + rbtree_t *tree; + /// Current node + rbtree_node_t *node; + /// Traversal path + rbtree_node_t *path[RBTREE_ITER_MAX_HEIGHT]; + /// Top of stack + unsigned int top; +}; + +rbtree_node_t *rbtree_node_alloc() +{ + return kmalloc(sizeof(rbtree_node_t)); +} + +rbtree_node_t *rbtree_node_init(rbtree_node_t *node, void *value) +{ + if (node) { + node->red = 1; + node->link[0] = node->link[1] = NULL; + node->value = value; + } + return node; +} + +rbtree_node_t *rbtree_node_create(void *value) +{ + return rbtree_node_init(rbtree_node_alloc(), value); +} + +void *rbtree_node_get_value(rbtree_node_t *node) +{ + if (node) + return node->value; + return NULL; +} + +void rbtree_node_dealloc(rbtree_node_t *node) +{ + if (node) + kfree(node); +} + +static int rbtree_node_is_red(const rbtree_node_t *node) +{ + return node ? node->red : 0; +} + +static rbtree_node_t *rbtree_node_rotate(rbtree_node_t *node, int dir) +{ + rbtree_node_t *result = NULL; + if (node) { + result = node->link[!dir]; + node->link[!dir] = result->link[dir]; + result->link[dir] = node; + node->red = 1; + result->red = 0; + } + return result; +} + +static rbtree_node_t *rbtree_node_rotate2(rbtree_node_t *node, int dir) +{ + rbtree_node_t *result = NULL; + if (node) { + node->link[!dir] = rbtree_node_rotate(node->link[!dir], !dir); + result = rbtree_node_rotate(node, dir); + } + return result; +} + +// rbtree_t - default callbacks + +static int rbtree_tree_node_cmp_ptr_cb( + rbtree_t *tree, + rbtree_node_t *a, + rbtree_node_t *b) +{ + (void)tree; + return (a->value > b->value) - (a->value < b->value); +} + +static void rbtree_tree_node_dealloc_cb(rbtree_t *tree, rbtree_node_t *node) +{ + if (tree) + if (node) + rbtree_node_dealloc(node); +} + +// rbtree_t + +rbtree_t *rbtree_tree_alloc() +{ + return kmalloc(sizeof(rbtree_t)); +} + +rbtree_t *rbtree_tree_init(rbtree_t *tree, rbtree_tree_node_cmp_f node_cmp_cb) +{ + if (tree) { + tree->root = NULL; + tree->size = 0; + tree->cmp = node_cmp_cb ? node_cmp_cb : rbtree_tree_node_cmp_ptr_cb; + } + return tree; +} + +rbtree_t *rbtree_tree_create(rbtree_tree_node_cmp_f node_cb) +{ + return rbtree_tree_init(rbtree_tree_alloc(), node_cb); +} + +void rbtree_tree_dealloc(rbtree_t *tree, rbtree_tree_node_f node_cb) +{ + assert(tree); + if (node_cb) { + rbtree_node_t *node = tree->root; + rbtree_node_t *save = NULL; + + // Rotate away the left links so that + // we can treat this like the destruction + // of a linked list + while (node) { + if (node->link[0] == NULL) { + // No left links, just kill the node and move on + save = node->link[1]; + node_cb(tree, node); + kfree(node); + node = NULL; + } else { + // Rotate away the left link and check again + save = node->link[0]; + node->link[0] = save->link[1]; + save->link[1] = node; + } + node = save; + } + } + kfree(tree); +} + +void *rbtree_tree_find(rbtree_t *tree, void *value) +{ + void *result = NULL; + if (tree) { + rbtree_node_t node = { .value = value }; + rbtree_node_t *it = tree->root; + int cmp = 0; + while (it) { + if ((cmp = tree->cmp(tree, it, &node))) { + // If the tree supports duplicates, they should be + // chained to the right subtree for this to work + it = it->link[cmp < 0]; + } else { + break; + } + } + result = it ? it->value : NULL; + } + return result; +} + +void *rbtree_tree_find_by_value(rbtree_t *tree, + rbtree_tree_cmp_f cmp_fun, + void *value) +{ + void *result = NULL; + if (tree) { + rbtree_node_t *it = tree->root; + int cmp = 0; + while (it) { + if ((cmp = cmp_fun(tree, it, value))) { + // If the tree supports duplicates, they should be + // chained to the right subtree for this to work + it = it->link[cmp < 0]; + } else { + break; + } + } + result = it ? it->value : NULL; + } + return result; +} + +// Creates (kmalloc'ates) +int rbtree_tree_insert(rbtree_t *tree, void *value) +{ + return rbtree_tree_insert_node(tree, rbtree_node_create(value)); +} + +// Returns 1 on success, 0 otherwise. +int rbtree_tree_insert_node(rbtree_t *tree, rbtree_node_t *node) +{ + if (tree && node) { + if (tree->root == NULL) { + tree->root = node; + } else { + rbtree_node_t head = { 0 }; // False tree root + rbtree_node_t *g, *t; // Grandparent & parent + rbtree_node_t *p, *q; // Iterator & parent + int dir = 0, last = 0; + + // Set up our helpers + t = &head; + g = p = NULL; + q = t->link[1] = tree->root; + + // Search down the tree for a place to insert + while (1) { + if (q == NULL) { + // Insert node at the first null link. + p->link[dir] = q = node; + } else if (rbtree_node_is_red(q->link[0]) && + rbtree_node_is_red(q->link[1])) { + // Simple red violation: color flip + q->red = 1; + q->link[0]->red = 0; + q->link[1]->red = 0; + } + + if (rbtree_node_is_red(q) && rbtree_node_is_red(p)) { + // Hard red violation: rotations necessary + int dir2 = t->link[1] == g; + if (q == p->link[last]) { + t->link[dir2] = rbtree_node_rotate(g, !last); + } else { + t->link[dir2] = rbtree_node_rotate2(g, !last); + } + } + + // Stop working if we inserted a node. This + // check also disallows duplicates in the tree + if (tree->cmp(tree, q, node) == 0) { + break; + } + + last = dir; + dir = tree->cmp(tree, q, node) < 0; + + // Move the helpers down + if (g != NULL) { + t = g; + } + + g = p, p = q; + q = q->link[dir]; + } + + // Update the root (it may be different) + tree->root = head.link[1]; + } + + // Make the root black for simplified logic + tree->root->red = 0; + ++tree->size; + } + + return 1; +} + +// Returns 1 if the value was removed, 0 otherwise. Optional node callback +// can be provided to dealloc node and/or user data. Use rbtree_tree_node_dealloc +// default callback to deallocate node created by rbtree_tree_insert(...). +int rbtree_tree_remove_with_cb(rbtree_t *tree, + void *value, + rbtree_tree_node_f node_cb) +{ + if (tree->root != NULL) { + rbtree_node_t head = { 0 }; // False tree root + rbtree_node_t node = { .value = value }; // Value wrapper node + rbtree_node_t *q, *p, *g; // Helpers + rbtree_node_t *f = NULL; // Found item + int dir = 1; + + // Set up our helpers + q = &head; + g = p = NULL; + q->link[1] = tree->root; + + // Search and push a red node down + // to fix red violations as we go + while (q->link[dir] != NULL) { + int last = dir; + + // Move the helpers down + g = p, p = q; + q = q->link[dir]; + dir = tree->cmp(tree, q, &node) < 0; + + // Save the node with matching value and keep + // going; we'll do removal tasks at the end + if (tree->cmp(tree, q, &node) == 0) { + f = q; + } + + // Push the red node down with rotations and color flips + if (!rbtree_node_is_red(q) && !rbtree_node_is_red(q->link[dir])) { + if (rbtree_node_is_red(q->link[!dir])) { + p = p->link[last] = rbtree_node_rotate(q, dir); + } else if (!rbtree_node_is_red(q->link[!dir])) { + rbtree_node_t *s = p->link[!last]; + if (s) { + if (!rbtree_node_is_red(s->link[!last]) && + !rbtree_node_is_red(s->link[last])) { + // Color flip + p->red = 0; + s->red = 1; + q->red = 1; + } else { + int dir2 = g->link[1] == p; + if (rbtree_node_is_red(s->link[last])) { + g->link[dir2] = rbtree_node_rotate2(p, last); + } else if (rbtree_node_is_red(s->link[!last])) { + g->link[dir2] = rbtree_node_rotate(p, last); + } + + // Ensure correct coloring + q->red = g->link[dir2]->red = 1; + g->link[dir2]->link[0]->red = 0; + g->link[dir2]->link[1]->red = 0; + } + } + } + } + } + + // Replace and remove the saved node + if (f) { + void *tmp = f->value; + f->value = q->value; + q->value = tmp; + + p->link[p->link[1] == q] = q->link[q->link[0] == NULL]; + + if (node_cb) { + node_cb(tree, q); + } + q = NULL; + } + + // Update the root (it may be different) + tree->root = head.link[1]; + + // Make the root black for simplified logic + if (tree->root != NULL) { + tree->root->red = 0; + } + + --tree->size; + } + return 1; +} + +int rbtree_tree_remove(rbtree_t *tree, void *value) +{ + int result = 0; + if (tree) { + result = rbtree_tree_remove_with_cb( + tree, value, rbtree_tree_node_dealloc_cb); + } + return result; +} + +unsigned int rbtree_tree_size(rbtree_t *tree) +{ + unsigned int result = 0; + if (tree) { + result = tree->size; + } + return result; +} + +// rbtree_iter_t + +rbtree_iter_t *rbtree_iter_alloc() +{ + return kmalloc(sizeof(rbtree_iter_t)); +} + +rbtree_iter_t *rbtree_iter_init(rbtree_iter_t *iter) +{ + if (iter) { + iter->tree = NULL; + iter->node = NULL; + iter->top = 0; + } + return iter; +} + +rbtree_iter_t *rbtree_iter_create() +{ + return rbtree_iter_init(rbtree_iter_alloc()); +} + +void rbtree_iter_dealloc(rbtree_iter_t *iter) +{ + if (iter) { + kfree(iter); + } +} + +// Internal function, init traversal object, dir determines whether +// to begin traversal at the smallest or largest valued node. +static void *rbtree_iter_start(rbtree_iter_t *iter, rbtree_t *tree, int dir) +{ + void *result = NULL; + if (iter) { + iter->tree = tree; + iter->node = tree->root; + iter->top = 0; + + // Save the path for later selfersal + if (iter->node != NULL) { + while (iter->node->link[dir] != NULL) { + iter->path[iter->top++] = iter->node; + iter->node = iter->node->link[dir]; + } + } + + result = iter->node == NULL ? NULL : iter->node->value; + } + return result; +} + +// Traverse a red black tree in the user-specified direction (0 asc, 1 desc) +static void *rbtree_iter_move(rbtree_iter_t *iter, int dir) +{ + if (iter->node->link[dir] != NULL) { + // Continue down this branch + iter->path[iter->top++] = iter->node; + iter->node = iter->node->link[dir]; + while (iter->node->link[!dir] != NULL) { + iter->path[iter->top++] = iter->node; + iter->node = iter->node->link[!dir]; + } + } else { + // Move to the next branch + rbtree_node_t *last = NULL; + do { + if (iter->top == 0) { + iter->node = NULL; + break; + } + last = iter->node; + iter->node = iter->path[--iter->top]; + } while (last == iter->node->link[dir]); + } + return iter->node == NULL ? NULL : iter->node->value; +} + +void *rbtree_iter_first(rbtree_iter_t *iter, rbtree_t *tree) +{ + return rbtree_iter_start(iter, tree, 0); +} + +void *rbtree_iter_last(rbtree_iter_t *iter, rbtree_t *tree) +{ + return rbtree_iter_start(iter, tree, 1); +} + +void *rbtree_iter_next(rbtree_iter_t *iter) +{ + return rbtree_iter_move(iter, 1); +} + +void *rbtree_iter_prev(rbtree_iter_t *iter) +{ + return rbtree_iter_move(iter, 0); +} + +int rbtree_tree_test(rbtree_t *tree, rbtree_node_t *root) +{ + int lh, rh; + + if (root == NULL) + return 1; + else { + rbtree_node_t *ln = root->link[0]; + rbtree_node_t *rn = root->link[1]; + + /* Consecutive red links */ + if (rbtree_node_is_red(root)) { + if (rbtree_node_is_red(ln) || rbtree_node_is_red(rn)) { + pr_err("Red violation"); + return 0; + } + } + + lh = rbtree_tree_test(tree, ln); + rh = rbtree_tree_test(tree, rn); + + /* Invalid binary search tree */ + if ((ln != NULL && tree->cmp(tree, ln, root) >= 0) || (rn != NULL && tree->cmp(tree, rn, root) <= 0)) { + pr_err("Binary tree violation"); + return 0; + } + + /* Black height mismatch */ + if (lh != 0 && rh != 0 && lh != rh) { + pr_err("Black violation"); + return 0; + } + + /* Only count black links */ + if (lh != 0 && rh != 0) + return rbtree_node_is_red(root) ? lh : lh + 1; + else + return 0; + } +} + +static void rbtree_tree_print_iter(rbtree_t *tree, + rbtree_node_t *node, + rbtree_tree_node_f fun) +{ + assert(tree); + assert(node); + assert(fun); + fun(tree, node); + if (node->link[0]) + rbtree_tree_print_iter(tree, node->link[0], fun); + if (node->link[1]) + rbtree_tree_print_iter(tree, node->link[1], fun); +} + +void rbtree_tree_print(rbtree_t *tree, rbtree_tree_node_f fun) +{ + assert(tree); + assert(fun); + rbtree_tree_print_iter(tree, tree->root, fun); +} diff --git a/mentos/src/klib/spinlock.c b/mentos/src/klib/spinlock.c new file mode 100644 index 0000000..5c59035 --- /dev/null +++ b/mentos/src/klib/spinlock.c @@ -0,0 +1,34 @@ +/// MentOS, The Mentoring Operating system project +/// @file spinlock.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "klib/spinlock.h" + +void spinlock_init(spinlock_t *spinlock) +{ + (*spinlock) = SPINLOCK_FREE; +} + +void spinlock_lock(spinlock_t *spinlock) +{ + while (1) { + if (atomic_set_and_test(spinlock, SPINLOCK_BUSY) == 0) { + break; + } + while (*spinlock) + cpu_relax(); + } +} + +void spinlock_unlock(spinlock_t *spinlock) +{ + barrier(); + atomic_set(spinlock, SPINLOCK_FREE); +} + +int spinlock_trylock(spinlock_t *spinlock) +{ + return atomic_set_and_test(spinlock, SPINLOCK_BUSY) == 0; +} diff --git a/mentos/src/klib/strerror.c b/mentos/src/klib/strerror.c new file mode 100644 index 0000000..a67b78c --- /dev/null +++ b/mentos/src/klib/strerror.c @@ -0,0 +1,480 @@ +/// MentOS, The Mentoring Operating system project +/// @file strerror.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "strerror.h" +#include "string.h" + +char *strerror(int errnum) +{ + static char error[1024]; + + switch (errnum) { + case 0: + strcpy(error, "Success"); + break; +#ifdef ENOENT + case ENOENT: + strcpy(error, "No such file or directory"); + break; +#endif +#ifdef ESRCH + case ESRCH: + strcpy(error, "No such process"); + break; +#endif +#ifdef EINTR + case EINTR: + strcpy(error, "Interrupted system call"); + break; +#endif +#ifdef EIO + case EIO: + strcpy(error, "I/O error"); + break; +#endif +#if defined(ENXIO) && (!defined(ENODEV) || (ENXIO != ENODEV)) + case ENXIO: + strcpy(error, "No such device or address"); + break; +#endif +#ifdef E2BIG + case E2BIG: + strcpy(error, "Arg list too long"); + break; +#endif +#ifdef ENOEXEC + case ENOEXEC: + strcpy(error, "Exec format error"); + break; +#endif +#ifdef EALREADY + case EALREADY: + strcpy(error, "Socket already connected"); + break; +#endif +#ifdef EBADF + case EBADF: + strcpy(error, "Bad file number"); + break; +#endif +#ifdef ECHILD + case ECHILD: + strcpy(error, "No children"); + break; +#endif +#ifdef EDESTADDRREQ + case EDESTADDRREQ: + strcpy(error, "Destination address required"); + break; +#endif +#ifdef EAGAIN + case EAGAIN: + strcpy(error, "No more processes"); + break; +#endif +#ifdef ENOMEM + case ENOMEM: + strcpy(error, "Not enough space"); + break; +#endif +#ifdef EACCES + case EACCES: + strcpy(error, "Permission denied"); + break; +#endif +#ifdef EFAULT + case EFAULT: + strcpy(error, "Bad address"); + break; +#endif +#ifdef ENOTBLK + case ENOTBLK: + strcpy(error, "Block device required"); + break; +#endif +#ifdef EBUSY + case EBUSY: + strcpy(error, "Device or resource busy"); + break; +#endif +#ifdef EEXIST + case EEXIST: + strcpy(error, "File exists"); + break; +#endif +#ifdef EXDEV + case EXDEV: + strcpy(error, "Cross-device link"); + break; +#endif +#ifdef ENODEV + case ENODEV: + strcpy(error, "No such device"); + break; +#endif +#ifdef ENOTDIR + case ENOTDIR: + strcpy(error, "Not a directory"); + break; +#endif +#ifdef EHOSTDOWN + case EHOSTDOWN: + strcpy(error, "Host is down"); + break; +#endif +#ifdef EINPROGRESS + case EINPROGRESS: + strcpy(error, "Connection already in progress"); + break; +#endif +#ifdef EISDIR + case EISDIR: + strcpy(error, "Is a directory"); + break; +#endif +#ifdef EINVAL + case EINVAL: + strcpy(error, "Invalid argument"); + break; +#endif +#ifdef EISNAM + case EISNAM: + strcpy(error, "Is a named type file"); + break; +#endif +#ifdef ENETDOWN + case ENETDOWN: + strcpy(error, "Network interface is not configured"); + break; +#endif +#ifdef ENFILE + case ENFILE: + strcpy(error, "Too many open files in system"); + break; +#endif +#ifdef EMFILE + case EMFILE: + strcpy(error, "Too many open files"); + break; +#endif +#ifdef ENOTTY + case ENOTTY: + strcpy(error, "Not a character device"); + break; +#endif +#ifdef ETXTBSY + case ETXTBSY: + strcpy(error, "Text file busy"); + break; +#endif +#ifdef EFBIG + case EFBIG: + strcpy(error, "File too large"); + break; +#endif +#ifdef EHOSTUNREACH + case EHOSTUNREACH: + strcpy(error, "Host is unreachable"); + break; +#endif +#ifdef ENOSPC + case ENOSPC: + strcpy(error, "No space left on device"); + break; +#endif +#ifdef ENOTSUP + case ENOTSUP: + strcpy(error, "Not supported"); + break; +#endif +#ifdef ESPIPE + case ESPIPE: + strcpy(error, "Illegal seek"); + break; +#endif +#ifdef EROFS + case EROFS: + strcpy(error, "Read-only file system"); + break; +#endif +#ifdef EMLINK + case EMLINK: + strcpy(error, "Too many links"); + break; +#endif +#ifdef EPIPE + case EPIPE: + strcpy(error, "Broken pipe"); + break; +#endif +#ifdef EDOM + case EDOM: + strcpy(error, "Math argument"); + break; +#endif +#ifdef ERANGE + case ERANGE: + strcpy(error, "Result too large"); + break; +#endif +#ifdef ENOMSG + case ENOMSG: + strcpy(error, "No message of desired type"); + break; +#endif +#ifdef EIDRM + case EIDRM: + strcpy(error, "Identifier removed"); + break; +#endif +#ifdef EDEADLK + case EDEADLK: + strcpy(error, "Deadlock"); + break; +#endif +#ifdef ENETUNREACH + case ENETUNREACH: + strcpy(error, "Network is unreachable"); + break; +#endif +#ifdef ENOLCK + case ENOLCK: + strcpy(error, "No lock"); + break; +#endif +#ifdef ENOSTR + case ENOSTR: + strcpy(error, "Not a stream"); + break; +#endif +#ifdef ETIME + case ETIME: + strcpy(error, "Stream ioctl timeout"); + break; +#endif +#ifdef ENOSR + case ENOSR: + strcpy(error, "No stream resources"); + break; +#endif +#ifdef ENONET + case ENONET: + strcpy(error, "Machine is not on the network"); + break; +#endif +#ifdef ENOPKG + case ENOPKG: + strcpy(error, "No package"); + break; +#endif +#ifdef EREMOTE + case EREMOTE: + strcpy(error, "Resource is remote"); + break; +#endif +#ifdef ENOLINK + case ENOLINK: + strcpy(error, "Virtual circuit is gone"); + break; +#endif +#ifdef EADV + case EADV: + strcpy(error, "Advertise error"); + break; +#endif +#ifdef ESRMNT + case ESRMNT: + strcpy(error, "Srmount error"); + break; +#endif +#ifdef ECOMM + case ECOMM: + strcpy(error, "Communication error"); + break; +#endif +#ifdef EPROTO + case EPROTO: + strcpy(error, "Protocol error"); + break; +#endif +#ifdef EPROTONOSUPPORT + case EPROTONOSUPPORT: + strcpy(error, "Unknown protocol"); + break; +#endif +#ifdef EMULTIHOP + case EMULTIHOP: + strcpy(error, "Multihop attempted"); + break; +#endif +#ifdef EBADMSG + case EBADMSG: + strcpy(error, "Bad message"); + break; +#endif +#ifdef ELIBACC + case ELIBACC: + strcpy(error, "Cannot access a needed shared library"); + break; +#endif +#ifdef ELIBBAD + case ELIBBAD: + strcpy(error, "Accessing a corrupted shared library"); + break; +#endif +#ifdef ELIBSCN + case ELIBSCN: + strcpy(error, ".lib section in a.out corrupted"); + break; +#endif +#ifdef ELIBMAX + case ELIBMAX: + strcpy(error, + "Attempting to link in more shared libraries than system limit"); + break; +#endif +#ifdef ELIBEXEC + case ELIBEXEC: + strcpy(error, "Cannot exec a shared library directly"); + break; +#endif +#ifdef ENOSYS + case ENOSYS: + strcpy(error, "Function not implemented"); + break; +#endif +#ifdef ENMFILE + case ENMFILE: + strcpy(error, "No more files"); + break; +#endif +#ifdef ENOTEMPTY + case ENOTEMPTY: + strcpy(error, "Directory not empty"); + break; +#endif +#ifdef ENAMETOOLONG + case ENAMETOOLONG: + strcpy(error, "File or path name too long"); + break; +#endif +#ifdef ELOOP + case ELOOP: + strcpy(error, "Too many symbolic links"); + break; +#endif +#ifdef ENOBUFS + case ENOBUFS: + strcpy(error, "No buffer space available"); + break; +#endif +#ifdef EAFNOSUPPORT + case EAFNOSUPPORT: + strcpy(error, "Address family not supported by protocol family"); + break; +#endif +#ifdef EPROTOTYPE + case EPROTOTYPE: + strcpy(error, "Protocol wrong type for socket"); + break; +#endif +#ifdef ENOTSOCK + case ENOTSOCK: + strcpy(error, "Socket operation on non-socket"); + break; +#endif +#ifdef ENOPROTOOPT + case ENOPROTOOPT: + strcpy(error, "Protocol not available"); + break; +#endif +#ifdef ESHUTDOWN + case ESHUTDOWN: + strcpy(error, "Can't send after socket shutdown"); + break; +#endif +#ifdef ECONNREFUSED + case ECONNREFUSED: + strcpy(error, "Connection refused"); + break; +#endif +#ifdef EADDRINUSE + case EADDRINUSE: + strcpy(error, "Address already in use"); + break; +#endif +#ifdef ECONNABORTED + case ECONNABORTED: + strcpy(error, "Software caused connection abort"); + break; +#endif +#if (defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))) + case EWOULDBLOCK: + strcpy(error, "Operation would block"); + break; +#endif +#ifdef ENOTCONN + case ENOTCONN: + strcpy(error, "Socket is not connected"); + break; +#endif +#ifdef ESOCKTNOSUPPORT + case ESOCKTNOSUPPORT: + strcpy(error, "Socket type not supported"); + break; +#endif +#ifdef EISCONN + case EISCONN: + strcpy(error, "Socket is already connected"); + break; +#endif +#ifdef ECANCELED + case ECANCELED: + strcpy(error, "Operation canceled"); + break; +#endif +#ifdef ENOTRECOVERABLE + case ENOTRECOVERABLE: + strcpy(error, "State not recoverable"); + break; +#endif +#ifdef EOWNERDEAD + case EOWNERDEAD: + strcpy(error, "Previous owner died"); + break; +#endif +#ifdef ESTRPIPE + case ESTRPIPE: + strcpy(error, "Streams pipe error"); + break; +#endif +#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (ENOTSUP != EOPNOTSUPP)) + case EOPNOTSUPP: + strcpy(error, "Operation not supported on socket"); + break; +#endif +#ifdef EMSGSIZE + case EMSGSIZE: + strcpy(error, "Message too long"); + break; +#endif +#ifdef ETIMEDOUT + case ETIMEDOUT: + strcpy(error, "Connection timed out"); + break; +#endif +#ifdef ENOTSCHEDULABLE + case ENOTSCHEDULABLE: + strcpy(error, "The process cannot be scheduled"); + break; +#endif + default: + strcpy(error, "Unknown error"); + break; + } + + return error; +} diff --git a/mentos/src/klib/string.c b/mentos/src/klib/string.c new file mode 100644 index 0000000..9cbd601 --- /dev/null +++ b/mentos/src/klib/string.c @@ -0,0 +1,701 @@ +/// MentOS, The Mentoring Operating system project +/// @file string.c +/// @brief String routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "mem/kheap.h" +#include "stdio.h" +#include "fcntl.h" +#include "string.h" +#include "ctype.h" + +char *strncpy(char *destination, const char *source, size_t num) +{ + char *start = destination; + while (num && (*destination++ = *source++)) { + num--; + } + if (num) { + while (--num) { + *destination++ = '\0'; + } + } + + return start; +} + +int strncmp(const char *s1, const char *s2, size_t n) +{ + if (!n) + return 0; + while ((--n > 0) && (*s1) && (*s2) && (*s1 == *s2)) { + s1++; + s2++; + } + + return *(unsigned char *)s1 - *(unsigned char *)s2; +} + +int stricmp(const char *s1, const char *s2) +{ + while (*s2 != 0 && toupper(*s1) == toupper(*s2)) { + s1++, s2++; + } + + return (toupper(*s1) - toupper(*s2)); +} + +int strnicmp(const char *s1, const char *s2, size_t n) +{ + int f, l; + + do { + if (((f = (unsigned char)(*(s1++))) >= 'A') && (f <= 'Z')) { + f -= 'A' - 'a'; + } + if (((l = (unsigned char)(*(s2++))) >= 'A') && (l <= 'Z')) { + l -= 'A' - 'a'; + } + } while (--n && f && (f == l)); + + return f - l; +} + +char *strchr(const char *s, int ch) +{ + while (*s && *s != (char)ch) { + s++; + } + if (*s == (char)ch) + return (char *)s; + { + return NULL; + } +} + +char *strrchr(const char *s, int ch) +{ + char *start = (char *)s; + + while (*s++) {} + + while (--s != start && *s != (char)ch) {} + + if (*s == (char)ch) { + return (char *)s; + } + + return NULL; +} + +char *strstr(const char *str1, const char *str2) +{ + char *cp = (char *)str1; + char *s1, *s2; + + if (!*str2) { + return (char *)str1; + } + + while (*cp) { + s1 = cp; + s2 = (char *)str2; + + while (*s1 && *s2 && !(*s1 - *s2)) { + s1++, s2++; + } + if (!*s2) { + return cp; + } + cp++; + } + + return NULL; +} + +size_t strspn(const char *string, const char *control) +{ + const char *str = string; + const char *ctrl = control; + + char map[32]; + size_t n; + + // Clear out bit map. + for (n = 0; n < 32; n++) { + map[n] = 0; + } + + // Set bits in control map. + while (*ctrl) { + map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); + ctrl++; + } + + // 1st char NOT in control map stops search. + if (*str) { + n = 0; + while (map[*str >> 3] & (1 << (*str & 7))) { + n++; + str++; + } + + return n; + } + + return 0; +} + +size_t strcspn(const char *string, const char *control) +{ + const char *str = string; + const char *ctrl = control; + + char map[32]; + size_t n; + + // Clear out bit map. + for (n = 0; n < 32; n++) + map[n] = 0; + + // Set bits in control map. + while (*ctrl) { + map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); + ctrl++; + } + + // 1st char in control map stops search. + n = 0; + map[0] |= 1; + while (!(map[*str >> 3] & (1 << (*str & 7)))) { + n++; + str++; + } + + return n; +} + +char *strpbrk(const char *string, const char *control) +{ + const char *str = string; + const char *ctrl = control; + + char map[32]; + int n; + + // Clear out bit map. + for (n = 0; n < 32; n++) + map[n] = 0; + + // Set bits in control map. + while (*ctrl) { + map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); + ctrl++; + } + + // 1st char in control map stops search. + while (*str) { + if (map[*str >> 3] & (1 << (*str & 7))) { + return (char *)str; + } + str++; + } + + return NULL; +} + +void *memmove(void *dst, const void *src, size_t n) +{ + void *ret = dst; + + if (dst <= src || (char *)dst >= ((char *)src + n)) { + /* Non-overlapping buffers; copy from lower addresses to higher + * addresses. + */ + while (n--) { + *(char *)dst = *(char *)src; + dst = (char *)dst + 1; + src = (char *)src + 1; + } + } else { + // Overlapping buffers; copy from higher addresses to lower addresses. + dst = (char *)dst + n - 1; + src = (char *)src + n - 1; + + while (n--) { + *(char *)dst = *(char *)src; + dst = (char *)dst - 1; + src = (char *)src - 1; + } + } + + return ret; +} + +void *memchr(const void *ptr, int ch, size_t n) +{ + while (n && (*(unsigned char *)ptr != (unsigned char)ch)) { + ptr = (unsigned char *)ptr + 1; + n--; + } + + return (n ? (void *)ptr : NULL); +} + +char *strlwr(char *s) +{ + char *p = s; + + while (*p) { + *p = (char)tolower(*p); + p++; + } + + return s; +} + +char *strupr(char *s) +{ + char *p = s; + + while (*p) { + *p = (char)toupper(*p); + p++; + } + + return s; +} + +char *strcat(char *dst, const char *src) +{ + char *cp = dst; + + while (*cp) { + cp++; + } + + while ((*cp++ = *src++) != '\0') {} + + return dst; +} + +char *strncat(char *s1, const char *s2, size_t n) +{ + char *start = s1; + + while (*s1++) {} + s1--; + + while (n--) { + if (!(*s1++ = *s2++)) + return start; + } + + *s1 = '\0'; + + return start; +} + +char *strrev(char *s) +{ + char *start = s; + char *left = s; + char ch; + + while (*s++) {} + s -= 2; + + while (left < s) { + ch = *left; + *left++ = *s; + *s-- = ch; + } + + return start; +} + +char *strtok_r(char *str, const char *delim, char **saveptr) +{ + char *s; + const char *ctrl = delim; + + char map[32]; + int n; + + // Clear delim map. + for (n = 0; n < 32; n++) { + map[n] = 0; + } + + // Set bits in delimiter table. + do { + map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); + } while (*ctrl++); + + /* Initialize s. If str is NULL, set s to the saved + * pointer (i.e., continue breaking tokens out of the str + * from the last strtok call). + */ + if (str) { + s = str; + } else { + s = *saveptr; + } + + /* Find beginning of token (skip over leading delimiters). Note that + * there is no token iff this loop sets s to point to the terminal + * null (*s == '\0'). + */ + while ((map[*s >> 3] & (1 << (*s & 7))) && *s) { + s++; + } + + str = s; + + /* Find the end of the token. If it is not the end of the str, + * put a null there. + */ + for (; *s; s++) { + if (map[*s >> 3] & (1 << (*s & 7))) { + *s++ = '\0'; + + break; + } + } + + // Update nexttoken. + *saveptr = s; + + // Determine if a token has been found. + if (str == (char *)s) { + return NULL; + } else { + return str; + } +} + +// Intrinsic functions. + +/* + * #pragma function(memset) + * #pragma function(memcmp) + * #pragma function(memcpy) + * #pragma function(strcpy) + * #pragma function(strlen) + * #pragma function(strcat) + * #pragma function(strcmp) + * #pragma function(strset) + */ + +void *memset(void *ptr, int value, size_t num) +{ + // Truncate c to 8 bits. + value = (value & 0xFF); + + char *dst = (char *)ptr; + + // Initialize the rest of the size. + while (num--) { + *dst++ = (char)value; + } + + return ptr; +} + +int memcmp(const void *dst, const void *src, size_t n) +{ + if (!n) { + return 0; + } + + while (--n && *(char *)dst == *(char *)src) { + dst = (char *)dst + 1; + src = (char *)src + 1; + } + + return *((unsigned char *)dst) - *((unsigned char *)src); +} + +void *memcpy(void *dst, const void *src, size_t num) +{ + char *_dst = dst; + const char *_src = src; + + while (num--) { + *_dst++ = *_src++; + } + + return dst; +} + +void *memccpy(void *dst, const void *src, int c, size_t n) +{ + while (n && (*((char *)(dst = (char *)dst + 1) - 1) = + *((char *)(src = (char *)src + 1) - 1)) != (char)c) { + n--; + } + + return n ? dst : NULL; +} + +char *strcpy(char *dst, const char *src) +{ + char *save = dst; + + while ((*dst++ = *src++) != '\0') {} + + return save; +} + +size_t strlen(const char *s) +{ + const char *eos; + + for (eos = s; *eos != 0; ++eos) {} + + long len = eos - s; + + return (len < 0) ? 0 : (size_t)len; +} + +size_t strnlen(const char *s, size_t count) +{ + const char *sc; + + for (sc = s; *sc != '\0' && count--; ++sc) {} + + long len = sc - s; + + return (len < 0) ? 0 : (size_t)len; +} + +int strcmp(const char *s1, const char *s2) +{ + int ret = 0; + const char *s1t = s1, *s2t = s2; + + for (; !(ret = *s1t - *s2t) && *s2t; ++s1t, ++s2t) {} + + return (ret < 0) ? -1 : (ret > 0) ? 1 : + 0; +} + +char *strset(char *s, int c) +{ + char *start = s; + + while (*s) { + *s++ = (char)c; + } + + return start; +} + +char *strnset(char *s, int c, size_t n) +{ + while (n-- && *s) { + *s++ = (char)c; + } + + return s; +} + +char *strtok(char *str, const char *delim) +{ + const char *spanp; + int c, sc; + char *tok; + static char *last; + + if (str == NULL && (str = last) == NULL) { + return (NULL); + } + +cont: + c = *str++; + for (spanp = delim; (sc = *spanp++) != 0;) { + if (c == sc) { + goto cont; + } + } + + if (c == 0) { + last = NULL; + + return (NULL); + } + tok = str - 1; + + for (;;) { + c = *str++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) { + str = NULL; + } else { + str[-1] = 0; + } + last = str; + + return (tok); + } + } while (sc != 0); + } +} + +char *trim(char *str) +{ + size_t len = 0; + char *frontp = str; + char *endp = NULL; + + if (str == NULL) { + return NULL; + } + if (str[0] == '\0') { + return str; + } + + len = strlen(str); + endp = str + len; + + /* Move the front and back pointers to address the first non-whitespace + * characters from each end. + */ + while (isspace((unsigned char)*frontp)) { + ++frontp; + } + if (endp != frontp) { + while (isspace((unsigned char)*(--endp)) && endp != frontp) {} + } + if (str + len - 1 != endp) { + *(endp + 1) = '\0'; + } else if (frontp != str && endp == frontp) { + *str = '\0'; + } + /* Shift the string so that it starts at str so that if it's dynamically + * allocated, we can still free it on the returned pointer. Note the reuse + * of endp to mean the front of the string buffer now. + */ + endp = str; + if (frontp != str) { + while (*frontp) { + *endp++ = *frontp++; + } + *endp = '\0'; + } + + return str; +} + +char *strdup(const char *s) +{ + size_t len = strlen(s) + 1; + char *new = kmalloc(len); + if (new == NULL) + return NULL; + new[len] = '\0'; + return (char *)memcpy(new, s, len); +} + +char *strndup(const char *s, size_t n) +{ + size_t len = strnlen(s, n); + char *new = kmalloc(len); + if (new == NULL) + return NULL; + new[len] = '\0'; + return (char *)memcpy(new, s, len); +} + +char *strsep(char **stringp, const char *delim) +{ + char *s; + const char *spanp; + int c, sc; + char *tok; + if ((s = *stringp) == NULL) { + return (NULL); + } + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) { + s = NULL; + } else { + s[-1] = 0; + } + *stringp = s; + + return (tok); + } + } while (sc != 0); + } +} + +char *itoa(char *buffer, unsigned int num, unsigned int base) +{ + // int numval; + char *p, *pbase; + + p = pbase = buffer; + + if (base == 16) { + sprintf(buffer, "%0x", num); + } else { + if (num == 0) { + *p++ = '0'; + } + while (num != 0) { + *p++ = (char)('0' + (num % base)); + num = num / base; + } + *p-- = 0; + + while (p > pbase) { + char tmp; + tmp = *p; + *p = *pbase; + *pbase = tmp; + + p--; + pbase++; + } + } + return buffer; +} + +char *replace_char(char *str, char find, char replace) +{ + char *current_pos = strchr(str, find); + + while (current_pos) { + *current_pos = replace; + current_pos = strchr(current_pos, find); + } + + return str; +} + +void strmode(mode_t mode, char *p) +{ + // Usr. + *p++ = mode & S_IRUSR ? 'r' : '-'; + *p++ = mode & S_IWUSR ? 'w' : '-'; + *p++ = mode & S_IXUSR ? 'x' : '-'; + // Group. + *p++ = mode & S_IRGRP ? 'r' : '-'; + *p++ = mode & S_IWGRP ? 'w' : '-'; + *p++ = mode & S_IXGRP ? 'x' : '-'; + // Other. + *p++ = mode & S_IROTH ? 'r' : '-'; + *p++ = mode & S_IWOTH ? 'w' : '-'; + *p++ = mode & S_IXOTH ? 'x' : '-'; + // Will be a '+' if ACL's implemented. + *p++ = ' '; + *p = '\0'; +} diff --git a/mentos/src/klib/time.c b/mentos/src/klib/time.c new file mode 100644 index 0000000..2b97404 --- /dev/null +++ b/mentos/src/klib/time.c @@ -0,0 +1,115 @@ +/// MentOS, The Mentoring Operating system project +/// @file time.c +/// @brief Clock functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "misc/debug.h" +#include "time.h" +#include "stdio.h" +#include "stddef.h" +#include "io/port_io.h" +#include "hardware/timer.h" +#include "drivers/rtc.h" + +static const char *str_weekdays[] = { "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" }; + +static const char *str_months[] = { "January", "February", "March", "April", + "May", "June", "July", "August", + "September", "October", "November", "December" }; + +time_t sys_time(time_t *time) +{ + tm_t curr_time; + gettime(&curr_time); + // January and February are counted as months 13 and 14 of the previous year. + if (curr_time.tm_mon <= 2) { + curr_time.tm_mon += 12; + curr_time.tm_year -= 1; + } + time_t t; + // Convert years to days + t = (365 * curr_time.tm_year) + (curr_time.tm_year / 4) - (curr_time.tm_year / 100) + + (curr_time.tm_year / 400); + // Convert months to days + t += (30 * curr_time.tm_mon) + (3 * (curr_time.tm_mon + 1) / 5) + curr_time.tm_mday; + // Unix time starts on January 1st, 1970 + t -= 719561; + // Convert days to seconds + t *= 86400; + // Add hours, minutes and seconds + t += (3600 * curr_time.tm_hour) + (60 * curr_time.tm_min) + curr_time.tm_sec; + if (time) { + (*time) = t; + } + return t; +} + +time_t difftime(time_t time1, time_t time2) +{ + return time1 - time2; +} + +/// @brief Computes day of week +/// @param y Year +/// @param m Month of year (in range 1 to 12) +/// @param d Day of month (in range 1 to 31) +/// @return Day of week (in range 1 to 7) +static inline int day_of_week(unsigned int y, unsigned int m, unsigned int d) +{ + int h, j, k; + // January and February are counted as months 13 and 14 of the previous year + if (m <= 2) { + m += 12; + y -= 1; + } + // J is the century + j = (int)(y / 100); + // K the year of the century + k = (int)(y % 100); + // Compute H using Zeller's congruence + h = (int)(d + (26 * (m + 1) / 10) + k + (k / 4) + (5 * j) + (j / 4)); + // Return the day of the week + return ((h + 5) % 7) + 1; +} + +tm_t *localtime(const time_t *time) +{ + static tm_t date; + unsigned int a, b, c, d, e, f; + time_t t = *time; + // Negative Unix time values are not supported + if (t < 1) { + t = 0; + } + //Retrieve hours, minutes and seconds + date.tm_sec = (int)(t % 60); + t /= 60; + date.tm_min = (int)(t % 60); + t /= 60; + date.tm_hour = (int)(t % 24); + t /= 24; + // Convert Unix time to date + a = (unsigned int)((4 * t + 102032) / 146097 + 15); + b = (unsigned int)(t + 2442113 + a - (a / 4)); + c = (20 * b - 2442) / 7305; + d = b - 365 * c - (c / 4); + e = d * 1000 / 30601; + f = d - e * 30 - e * 601 / 1000; + // January and February are counted as months 13 and 14 of the previous year + if (e <= 13) { + c -= 4716; + e -= 1; + } else { + c -= 4715; + e -= 13; + } + //Retrieve year, month and day + date.tm_year = (int)c; + date.tm_mon = (int)e; + date.tm_mday = (int)f; + // Calculate day of week. + date.tm_wday = day_of_week(date.tm_year, date.tm_mon, date.tm_mday); + return &date; +} \ No newline at end of file diff --git a/mentos/src/klib/vscanf.c b/mentos/src/klib/vscanf.c new file mode 100644 index 0000000..b811d6b --- /dev/null +++ b/mentos/src/klib/vscanf.c @@ -0,0 +1,105 @@ +/// MentOS, The Mentoring Operating system project +/// @file vscanf.c +/// @brief Reading formatting routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "fs/vfs.h" +#include "ctype.h" +#include "string.h" +#include "misc/debug.h" +#include "stdio.h" + +static int vsscanf(const char *buf, const char *s, va_list ap) +{ + int count = 0, noassign = 0, width = 0, base = 0; + const char *tc; + char tmp[BUFSIZ]; + + while (*s && *buf) { + while (isspace(*s)) + ++s; + if (*s == '%') { + ++s; + for (; *s; ++s) { + if (strchr("dibouxcsefg%", *s)) + break; + if (*s == '*') + noassign = 1; + else if (isdigit(*s)) { + for (tc = s; isdigit(*s); ++s) + ; + strncpy(tmp, tc, s - tc); + tmp[s - tc] = '\0'; + width = strtol(tmp, NULL, 10); + --s; + } + } + if (*s == 's') { + while (isspace(*buf)) + ++buf; + if (!width) + width = strcspn(buf, " \t\n\r\f\v"); + if (!noassign) { + char *string = va_arg(ap, char *); + strncpy(string, buf, width); + string[width] = '\0'; + } + buf += width; + } else if (*s == 'c') { + while (isspace(*buf)) + ++buf; + if (!width) + width = 1; + if (!noassign) { + strncpy(va_arg(ap, char *), buf, width); + } + buf += width; + } else if (strchr("duxob", *s)) { + while (isspace(*buf)) + ++buf; + if (*s == 'd' || *s == 'u') + base = 10; + else if (*s == 'x') + base = 16; + else if (*s == 'o') + base = 8; + else if (*s == 'b') + base = 2; + if (!width) { + if (isspace(*(s + 1)) || *(s + 1) == 0) + width = strcspn(buf, " \t\n\r\f\v"); + else + width = strchr(buf, *(s + 1)) - buf; + } + strncpy(tmp, buf, width); + tmp[width] = '\0'; + buf += width; + if (!noassign) + *va_arg(ap, unsigned int *) = strtol(tmp, NULL, base); + } + if (!noassign) + ++count; + width = noassign = 0; + ++s; + } else { + while (isspace(*buf)) + ++buf; + if (*s != *buf) + break; + else + ++s, ++buf; + } + } + return (count); +} + +int sscanf(const char *buf, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + int count = vsscanf(buf, fmt, ap); + va_end(ap); + return count; +} diff --git a/mentos/src/klib/vsprintf.c b/mentos/src/klib/vsprintf.c new file mode 100644 index 0000000..d44d4a7 --- /dev/null +++ b/mentos/src/klib/vsprintf.c @@ -0,0 +1,675 @@ +/// MentOS, The Mentoring Operating system project +/// @file vsprintf.c +/// @brief Print formatting routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "math.h" +#include "ctype.h" +#include "string.h" +#include "stdarg.h" +#include "stdbool.h" +#include "stdint.h" +#include "stdio.h" +#include "io/video.h" +#include "fcvt.h" + +/// Size of the buffer used to call cvt functions. +#define CVTBUFSIZE 500 + +#define FLAGS_ZEROPAD (1U << 0U) ///< Fill zeros before the number. +#define FLAGS_LEFT (1U << 1U) ///< Left align the value. +#define FLAGS_PLUS (1U << 2U) ///< Print the plus sign. +#define FLAGS_SPACE (1U << 3U) ///< If positive add a space instead of the plus sign. +#define FLAGS_HASH (1U << 4U) ///< Preceed with 0x or 0X, %x or %X respectively. +#define FLAGS_UPPERCASE (1U << 5U) ///< Print uppercase. +#define FLAGS_SIGN (1U << 6U) ///< Print the sign. + +/// The list of digits. +static char *_digits = "0123456789abcdefghijklmnopqrstuvwxyz"; + +/// The list of uppercase digits. +static char *_upper_digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +/// @brief Returns the index of the first non-integer character. +static inline int skip_atoi(const char **s) +{ + int i = 0; + while (isdigit(**s)) + i = i * 10 + *((*s)++) - '0'; + return i; +} + +static char *number(char *str, long num, int base, int size, int32_t precision, unsigned flags) +{ + char c, tmp[66] = { 0 }; + char *dig = _digits; + + if (flags & FLAGS_UPPERCASE) { + dig = _upper_digits; + } + if (flags & FLAGS_LEFT) { + flags &= ~FLAGS_ZEROPAD; + } + if (base < 2 || base > 36) { + return 0; + } + + c = (flags & FLAGS_ZEROPAD) ? '0' : ' '; + + // -------------------------------- + // Set the sign. + // -------------------------------- + char sign = 0; + if (flags & FLAGS_SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (flags & FLAGS_PLUS) { + sign = '+'; + size--; + } else if (flags & FLAGS_SPACE) { + sign = ' '; + size--; + } + } + // Sice I've removed the sign (if negative), i can transform it to unsigned. + uint32_t uns_num = (uint32_t)num; + if (flags & FLAGS_HASH) { + if (base == 16) { + size -= 2; + } else if (base == 8) { + size--; + } + } + + int32_t i = 0; + if (uns_num == 0) { + tmp[i++] = '0'; + } else { + while (uns_num != 0) { + tmp[i++] = dig[((unsigned long)uns_num) % (unsigned)base]; + uns_num = ((unsigned long)uns_num) / (unsigned)base; + } + } + if (i > precision) { + precision = i; + } + size -= precision; + if (!(flags & (FLAGS_ZEROPAD | FLAGS_LEFT))) { + while (size-- > 0) + *str++ = ' '; + } + if (sign) { + *str++ = sign; + } + if (flags & FLAGS_HASH) { + if (base == 8) + *str++ = '0'; + else if (base == 16) { + *str++ = '0'; + *str++ = _digits[33]; + } + } + if (!(flags & FLAGS_LEFT)) { + while (size-- > 0) { + *str++ = c; + } + } + while (i < precision--) { + *str++ = '0'; + } + while (i-- > 0) { + *str++ = tmp[i]; + } + while (size-- > 0) { + *str++ = ' '; + } + return str; +} + +static char *eaddr(char *str, unsigned char *addr, int size, int precision, unsigned flags) +{ + (void)precision; + char tmp[24]; + char *dig = _digits; + int i, len; + + if (flags & FLAGS_UPPERCASE) { + dig = _upper_digits; + } + + len = 0; + for (i = 0; i < 6; i++) { + if (i != 0) { + tmp[len++] = ':'; + } + tmp[len++] = dig[addr[i] >> 4]; + tmp[len++] = dig[addr[i] & 0x0F]; + } + + if (!(flags & FLAGS_LEFT)) { + while (len < size--) { + *str++ = ' '; + } + } + + for (i = 0; i < len; ++i) { + *str++ = tmp[i]; + } + + while (len < size--) { + *str++ = ' '; + } + + return str; +} + +static char *iaddr(char *str, unsigned char *addr, int size, int precision, unsigned flags) +{ + (void)precision; + char tmp[24]; + int i, n, len; + + len = 0; + for (i = 0; i < 4; i++) { + if (i != 0) { + tmp[len++] = '.'; + } + n = addr[i]; + + if (n == 0) { + tmp[len++] = _digits[0]; + } else { + if (n >= 100) { + tmp[len++] = _digits[n / 100]; + n = n % 100; + tmp[len++] = _digits[n / 10]; + n = n % 10; + } else if (n >= 10) { + tmp[len++] = _digits[n / 10]; + n = n % 10; + } + + tmp[len++] = _digits[n]; + } + } + + if (!(flags & FLAGS_LEFT)) { + while (len < size--) { + *str++ = ' '; + } + } + + for (i = 0; i < len; ++i) { + *str++ = tmp[i]; + } + + while (len < size--) { + *str++ = ' '; + } + + return str; +} + +static void cfltcvt(double value, char *buffer, char fmt, int precision) +{ + int decpt, sign, exp, pos; + char cvtbuf[CVTBUFSIZE]; + char *digits = cvtbuf; + int capexp = 0; + int magnitude; + + if (fmt == 'G' || fmt == 'E') { + capexp = 1; + fmt += 'a' - 'A'; + } + + if (fmt == 'g') { + ecvtbuf(value, precision, &decpt, &sign, cvtbuf, CVTBUFSIZE); + magnitude = decpt - 1; + if (magnitude < -4 || magnitude > precision - 1) { + fmt = 'e'; + precision -= 1; + } else { + fmt = 'f'; + precision -= decpt; + } + } + + if (fmt == 'e') { + ecvtbuf(value, precision + 1, &decpt, &sign, cvtbuf, CVTBUFSIZE); + + if (sign) { + *buffer++ = '-'; + } + *buffer++ = *digits; + if (precision > 0) { + *buffer++ = '.'; + } + memcpy(buffer, digits + 1, precision); + buffer += precision; + *buffer++ = capexp ? 'E' : 'e'; + + if (decpt == 0) { + if (value == 0.0) { + exp = 0; + } else { + exp = -1; + } + } else { + exp = decpt - 1; + } + + if (exp < 0) { + *buffer++ = '-'; + exp = -exp; + } else { + *buffer++ = '+'; + } + + buffer[2] = (char)((exp % 10) + '0'); + exp = exp / 10; + buffer[1] = (char)((exp % 10) + '0'); + exp = exp / 10; + buffer[0] = (char)((exp % 10) + '0'); + buffer += 3; + } else if (fmt == 'f') { + fcvtbuf(value, precision, &decpt, &sign, cvtbuf, CVTBUFSIZE); + if (sign) { + *buffer++ = '-'; + } + if (*digits) { + if (decpt <= 0) { + *buffer++ = '0'; + *buffer++ = '.'; + for (pos = 0; pos < -decpt; pos++) { + *buffer++ = '0'; + } + while (*digits) { + *buffer++ = *digits++; + } + } else { + pos = 0; + while (*digits) { + if (pos++ == decpt) { + *buffer++ = '.'; + } + *buffer++ = *digits++; + } + } + } else { + *buffer++ = '0'; + if (precision > 0) { + *buffer++ = '.'; + for (pos = 0; pos < precision; pos++) { + *buffer++ = '0'; + } + } + } + } + + *buffer = '\0'; +} + +static void forcdecpt(char *buffer) +{ + while (*buffer) { + if (*buffer == '.') { + return; + } + if (*buffer == 'e' || *buffer == 'E') { + break; + } + + buffer++; + } + + if (*buffer) { + long n = (long)strlen(buffer); + while (n > 0) { + buffer[n + 1] = buffer[n]; + n--; + } + *buffer = '.'; + } else { + *buffer++ = '.'; + *buffer = '\0'; + } +} + +static void cropzeros(char *buffer) +{ + char *stop; + + while (*buffer && *buffer != '.') { + buffer++; + } + + if (*buffer++) { + while (*buffer && *buffer != 'e' && *buffer != 'E') { + buffer++; + } + stop = buffer--; + while (*buffer == '0') { + buffer--; + } + if (*buffer == '.') { + buffer--; + } + while ((*++buffer = *stop++)) {} + } +} + +static char *flt(char *str, double num, int size, int precision, char fmt, unsigned flags) +{ + char tmp[80]; + char c, sign; + int n, i; + + // Left align means no zero padding. + if (flags & FLAGS_LEFT) + flags &= ~FLAGS_ZEROPAD; + + // Determine padding and sign char. + c = (flags & FLAGS_ZEROPAD) ? '0' : ' '; + sign = 0; + if (flags & FLAGS_SIGN) { + if (num < 0.0) { + sign = '-'; + num = -num; + size--; + } else if (flags & FLAGS_PLUS) { + sign = '+'; + size--; + } else if (flags & FLAGS_SPACE) { + sign = ' '; + size--; + } + } + + // Compute the precision value. + if (precision < 0) { + // Default precision: 6. + precision = 6; + } else if (precision == 0 && fmt == 'g') { + // ANSI specified. + precision = 1; + } + + // Convert floating point number to text. + cfltcvt(num, tmp, fmt, precision); + + // '#' and precision == 0 means force a decimal point. + if ((flags & FLAGS_HASH) && precision == 0) { + forcdecpt(tmp); + } + + // 'g' format means crop zero unless '#' given. + if (fmt == 'g' && !(flags & FLAGS_HASH)) { + cropzeros(tmp); + } + + n = strlen(tmp); + + // Output number with alignment and padding. + size -= n; + if (!(flags & (FLAGS_ZEROPAD | FLAGS_LEFT))) { + while (size-- > 0) { + *str++ = ' '; + } + } + + if (sign) { + *str++ = sign; + } + + if (!(flags & FLAGS_LEFT)) { + while (size-- > 0) { + *str++ = c; + } + } + + for (i = 0; i < n; i++) { + *str++ = tmp[i]; + } + + while (size-- > 0) { + *str++ = ' '; + } + + return str; +} + +int vsprintf(char *str, const char *fmt, va_list args) +{ + int base; + char *tmp; + char *s; + + // Flags to number(). + unsigned flags; + + // 'h', 'l', or 'L' for integer fields. + char qualifier; + + for (tmp = str; *fmt; fmt++) { + if (*fmt != '%') { + *tmp++ = *fmt; + + continue; + } + + // Process flags- + flags = 0; + repeat: + // This also skips first '%'. + fmt++; + switch (*fmt) { + case '-': + flags |= FLAGS_LEFT; + goto repeat; + case '+': + flags |= FLAGS_PLUS; + goto repeat; + case ' ': + flags |= FLAGS_SPACE; + goto repeat; + case '#': + flags |= FLAGS_HASH; + goto repeat; + case '0': + flags |= FLAGS_ZEROPAD; + goto repeat; + } + + // Get the width of the output field. + int32_t field_width; + field_width = -1; + + if (isdigit(*fmt)) { + field_width = skip_atoi(&fmt); + } else if (*fmt == '*') { + fmt++; + field_width = va_arg(args, int32_t); + if (field_width < 0) { + field_width = -field_width; + flags |= FLAGS_LEFT; + } + } + + /* Get the precision, thus the minimum number of digits for + * integers; max number of chars for from string. + */ + int32_t precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) { + precision = skip_atoi(&fmt); + } else if (*fmt == '*') { + ++fmt; + precision = va_arg(args, int); + } + if (precision < 0) { + precision = 0; + } + } + + // Get the conversion qualifier. + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + fmt++; + } + + // Default base. + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & FLAGS_LEFT)) { + while (--field_width > 0) { + *tmp++ = ' '; + } + } + *tmp++ = va_arg(args, char); + while (--field_width > 0) { + *tmp++ = ' '; + } + continue; + + case 's': + s = va_arg(args, char *); + if (!s) { + s = ""; + } + + int32_t len = (int32_t)strnlen(s, (uint32_t)precision); + if (!(flags & FLAGS_LEFT)) { + while (len < field_width--) { + *tmp++ = ' '; + } + } + + int32_t it; + for (it = 0; it < len; ++it) { + *tmp++ = *s++; + } + while (len < field_width--) { + *tmp++ = ' '; + } + continue; + + case 'p': + if (field_width == -1) { + field_width = 2 * sizeof(void *); + flags |= FLAGS_ZEROPAD; + } + tmp = number(tmp, (unsigned long)va_arg(args, void *), 16, field_width, precision, flags); + continue; + case 'n': + if (qualifier == 'l') { + long *ip = va_arg(args, long *); + *ip = (tmp - str); + } else { + int *ip = va_arg(args, int *); + *ip = (tmp - str); + } + continue; + case 'A': + flags |= FLAGS_UPPERCASE; + break; + case 'a': + if (qualifier == 'l') + tmp = eaddr(tmp, va_arg(args, unsigned char *), field_width, precision, flags); + else + tmp = iaddr(tmp, va_arg(args, unsigned char *), field_width, precision, flags); + continue; + // Integer number formats - set up the flags and "break". + case 'o': + base = 8; + break; + + case 'X': + flags |= FLAGS_UPPERCASE; + break; + + case 'x': + base = 16; + break; + + case 'd': + case 'i': + flags |= FLAGS_SIGN; + + case 'u': + break; + case 'E': + case 'G': + case 'e': + case 'f': + case 'g': + tmp = flt(tmp, va_arg(args, double), field_width, precision, *fmt, flags | FLAGS_SIGN); + continue; + default: + if (*fmt != '%') + *tmp++ = '%'; + if (*fmt) + *tmp++ = *fmt; + else + --fmt; + continue; + } + + if (flags & FLAGS_SIGN) { + long num; + if (qualifier == 'l') { + num = va_arg(args, long); + } else if (qualifier == 'h') { + num = va_arg(args, short); + } else { + num = va_arg(args, int); + } + tmp = number(tmp, num, base, field_width, precision, flags); + } else { + unsigned long num; + if (qualifier == 'l') { + num = va_arg(args, unsigned long); + } else if (qualifier == 'h') { + num = va_arg(args, unsigned short); + } else { + num = va_arg(args, unsigned int); + } + tmp = number(tmp, num, base, field_width, precision, flags); + } + } + + *tmp = '\0'; + return tmp - str; +} + +int printf(const char *format, ...) +{ + char buffer[4096]; + va_list ap; + int len; + // Start variabile argument's list. + va_start(ap, format); + len = vsprintf(buffer, format, ap); + va_end(ap); + video_puts(buffer); + return len; +} + +int sprintf(char *str, const char *fmt, ...) +{ + va_list args; + int len; + + va_start(args, fmt); + len = vsprintf(str, fmt, args); + va_end(args); + + return len; +} diff --git a/mentos/src/libc/assert.c b/mentos/src/libc/assert.c deleted file mode 100644 index 87038b0..0000000 --- a/mentos/src/libc/assert.c +++ /dev/null @@ -1,27 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file assert.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "assert.h" -#include "stdio.h" -#include "panic.h" - -void __assert_fail(const char *assertion, - const char *file, - unsigned int line, - const char *function) -{ - char message[1024]; - sprintf(message, - "FILE: %s\n" - "LINE: %d\n" - "FUNC: %s\n\n" - "Assertion `%s` failed.\n", - file, - line, - (function ? function : "NO_FUN"), - assertion); - kernel_panic(message); -} diff --git a/mentos/src/libc/bitset.c b/mentos/src/libc/bitset.c deleted file mode 100644 index b9f3112..0000000 --- a/mentos/src/libc/bitset.c +++ /dev/null @@ -1,120 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file bitset.c -/// @brief Bitset data structure. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "bitset.h" -#include "math.h" -#include "string.h" -#include "stdlib.h" -#include "assert.h" - -/// @brief -/// @param bit -/// @param index -/// @param mask -static void get_index_and_mask(size_t bit, size_t *index, size_t *mask) -{ - assert(index); - - assert(mask); - - (*index) = bit >> 3; - - bit = bit - (*index) * 8; - - size_t offset = bit & 7; - - (*mask) = 1UL << offset; -} - -/// @brief -/// @param set -/// @param size - -/* - * static void bitset_resize(bitset_t *set, size_t size) - * { - * assert(set && "Received NULL set."); - * - * if (set->size >= size) - * { - * return; - * } - * set->data = realloc(set->data, size); - * - * memset(set->data + set->size, 0, size - set->size); - * - * set->size = size; - * - * } - */ - -void bitset_init(bitset_t *set, size_t size) -{ - assert(set && "Received NULL set."); - - set->size = ceil(size, 8); - - set->data = calloc(set->size, 1); -} - -void bitset_free(bitset_t *set) -{ - assert(set && "Received NULL set."); - - free(set->data); -} - -/* - * void bitset_set(bitset_t *set, size_t bit) - * { - * assert(set && "Received NULL set."); - * size_t index, mask; - * - * get_index_and_mask(bit, &index, &mask); - * - * if (set->size <= index) - * { - * bitset_resize(set, set->size << 1); - * } - * - * set->data[index] |= mask; - * } - */ - -void bitset_clear(bitset_t *set, size_t bit) -{ - assert(set && "Received NULL set."); - - size_t index, mask; - - get_index_and_mask(bit, &index, &mask); - - set->data[index] &= ~mask; -} - -bool_t bitset_test(bitset_t *set, size_t bit) -{ - assert(set && "Received NULL set."); - - size_t index, mask; - - get_index_and_mask(bit, &index, &mask); - - return (mask & set->data[index]) != 0; -} - -signed long bitset_find_first_unset_bit(bitset_t *set) -{ - assert(set && "Received NULL set."); - - for (size_t i = 0; i < (set->size * 8); i++) { - if (!bitset_test(set, i)) { - return (signed long)i; - } - } - - return -1; -} diff --git a/mentos/src/libc/ctype.c b/mentos/src/libc/ctype.c deleted file mode 100644 index e1e6a50..0000000 --- a/mentos/src/libc/ctype.c +++ /dev/null @@ -1,63 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file ctype.c -/// @brief Functions related to character handling. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "ctype.h" - -/// Distance from a uppercase character to the correspondent lowercase in ASCII. -#define OFFSET 32 - -int isdigit(int c) -{ - return (c >= 48 && c <= 57); -} - -int isalpha(int c) -{ - return ((c >= 65 && c <= 90) || (c >= 97 && c <= 122)); -} - -int isalnum(int c) -{ - return (isalpha(c) || isdigit(c)); -} - -int isxdigit(int c) -{ - return (isdigit(c) || (c >= 65 && c <= 70)); -} - -int islower(int c) -{ - return (c >= 97 && c <= 122); -} - -int isupper(int c) -{ - return (c >= 65 && c <= 90); -} - -int tolower(int c) -{ - if (isalpha(c) == 0 || islower(c)) { - return c; - } - - return c + OFFSET; -} - -int toupper(int c) -{ - if (isalpha(c) == 0 || isupper(c)) { - return c; - } - - return c - OFFSET; -} - -int isspace(int c) -{ - return (c == ' '); -} diff --git a/mentos/src/libc/fcvt.c b/mentos/src/libc/fcvt.c deleted file mode 100644 index cb5eb67..0000000 --- a/mentos/src/libc/fcvt.c +++ /dev/null @@ -1,114 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file fcvt.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "fcvt.h" -#include - -/* - * cvt.c - IEEE floating point formatting routines for FreeBSD - * from GNU libc-4.6.27 - */ - -#define CVTBUFSIZE 500 - -static char *cvt(double arg, int ndigits, int *decpt, int *sign, char *buf, - int eflag) -{ - int r2; - double fi, fj; - char *p, *p1; - - if (ndigits < 0) { - ndigits = 0; - } - - if (ndigits >= CVTBUFSIZE - 1) { - ndigits = CVTBUFSIZE - 2; - } - - r2 = 0; - *sign = 0; - p = &buf[0]; - - if (arg < 0) { - *sign = 1; - arg = -arg; - } - - arg = modf(arg, &fi); - p1 = &buf[CVTBUFSIZE]; - - if (fi != 0) { - p1 = &buf[CVTBUFSIZE]; - while (fi != 0) { - fj = modf(fi / 10, &fi); - *--p1 = (int)((fj + .03) * 10) + '0'; - r2++; - } - while (p1 < &buf[CVTBUFSIZE]) { - *p++ = *p1++; - } - } else if (arg > 0) { - while ((fj = arg * 10) < 1) { - arg = fj; - r2--; - } - } - - p1 = &buf[ndigits]; - if (eflag == 0) { - p1 += r2; - } - - *decpt = r2; - if (p1 < &buf[0]) { - buf[0] = '\0'; - - return buf; - } - - while (p <= p1 && p < &buf[CVTBUFSIZE]) { - arg *= 10; - arg = modf(arg, &fj); - *p++ = (int)fj + '0'; - } - - if (p1 >= &buf[CVTBUFSIZE]) { - buf[CVTBUFSIZE - 1] = '\0'; - - return buf; - } - - p = p1; - *p1 += 5; - while (*p1 > '9') { - *p1 = '0'; - if (p1 > buf) { - ++*--p1; - } else { - *p1 = '1'; - (*decpt)++; - if (eflag == 0) { - if (p > buf) - *p = '0'; - p++; - } - } - } - *p = '\0'; - - return buf; -} - -char *ecvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf) -{ - return cvt(arg, ndigits, decpt, sign, buf, 1); -} - -char *fcvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf) -{ - return cvt(arg, ndigits, decpt, sign, buf, 0); -} diff --git a/mentos/src/libc/hashmap.c b/mentos/src/libc/hashmap.c deleted file mode 100644 index feb00a5..0000000 --- a/mentos/src/libc/hashmap.c +++ /dev/null @@ -1,294 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file hashmap.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "hashmap.h" -#include "string.h" -#include "stdlib.h" - -struct hashmap_entry_t -{ - char *key; - void *value; - struct hashmap_entry_t *next; -}; - -struct hashmap_t -{ - hashmap_hash_t hash_func; - hashmap_comp_t hash_comp; - hashmap_dupe_t hash_key_dup; - hashmap_free_t hash_key_free; - hashmap_free_t hash_val_free; - size_t size; - hashmap_entry_t **entries; -}; - -size_t hashmap_string_hash(void *_key) -{ - size_t hash = 0; - char *key = (char *) _key; - int c; - /* - * This is the so-called "sdbm" hash. It comes from a piece of public - * domain code from a clone of ndbm. - */ - while ((c = *key++)) - { - hash = c + (hash << 6) + (hash << 16) - hash; - } - - return hash; -} - -bool_t hashmap_string_comp(void *a, void *b) -{ - return !strcmp(a, b); -} - -void *hashmap_string_dupe(void *key) -{ - return strdup(key); -} - -static size_t hashmap_int_hash(void *key) -{ - return (size_t) key; -} - -static bool_t hashmap_int_comp(void *a, void *b) -{ - return (int) a == (int) b; -} - -static void *hashmap_int_dupe(void *key) -{ - return key; -} - -static void hashmap_int_free(void *ptr) -{ - (void) ptr; -} - -hashmap_t *hashmap_create(size_t size) -{ - hashmap_t *map = malloc(sizeof(hashmap_t)); - - map->hash_func = &hashmap_string_hash; - map->hash_comp = &hashmap_string_comp; - map->hash_key_dup = &hashmap_string_dupe; - map->hash_key_free = &free; - map->hash_val_free = &free; - - map->size = size; - map->entries = malloc(sizeof(hashmap_entry_t *) * size); - memset(map->entries, 0, sizeof(hashmap_entry_t *) * size); - - return map; -} - -hashmap_t *hashmap_create_int(size_t size) -{ - hashmap_t *map = malloc(sizeof(hashmap_t)); - - map->hash_func = &hashmap_int_hash; - map->hash_comp = &hashmap_int_comp; - map->hash_key_dup = &hashmap_int_dupe; - map->hash_key_free = &hashmap_int_free; - map->hash_val_free = &free; - - map->size = size; - map->entries = malloc(sizeof(hashmap_entry_t *) * size); - memset(map->entries, 0, sizeof(hashmap_entry_t *) * size); - - return map; -} - -void hashmap_free(hashmap_t *map) -{ - for (size_t i = 0; i < map->size; ++i) - { - hashmap_entry_t *x = map->entries[i], * p; - while (x) - { - p = x; - x = x->next; - map->hash_key_free(p->key); - map->hash_val_free(p); - } - } - - free(map->entries); -} - -void *hashmap_set(hashmap_t *map, void *key, void *value) -{ - size_t hash = map->hash_func(key) % map->size; - hashmap_entry_t *x = map->entries[hash]; - - if (x == NULL) - { - hashmap_entry_t *e = malloc(sizeof(hashmap_entry_t)); - e->key = map->hash_key_dup(key); - e->value = value; - e->next = NULL; - map->entries[hash] = e; - - return NULL; - } - - hashmap_entry_t *p = NULL; - - do - { - if (map->hash_comp(x->key, key)) - { - void *out = x->value; - x->value = value; - - return out; - } - p = x; - x = x->next; - } while (x); - - hashmap_entry_t *e = malloc(sizeof(hashmap_entry_t)); - e->key = map->hash_key_dup(key); - e->value = value; - e->next = NULL; - p->next = e; - - return NULL; -} - -void *hashmap_get(hashmap_t *map, void *key) -{ - size_t hash = map->hash_func(key) % map->size; - hashmap_entry_t *x = map->entries[hash]; - - if (x == NULL) - { - return NULL; - } - do - { - if (map->hash_comp(x->key, key)) - { - return x->value; - } - x = x->next; - } while (x); - - return NULL; -} - -void *hashmap_remove(hashmap_t *map, void *key) -{ - size_t hash = map->hash_func(key) % map->size; - hashmap_entry_t *x = map->entries[hash]; - - if (x == NULL) - { - return NULL; - } - if (map->hash_comp(x->key, key)) - { - void *out = x->value; - map->entries[hash] = x->next; - map->hash_key_free(x->key); - map->hash_val_free(x); - - return out; - } - - hashmap_entry_t * p = x; - x = x->next; - do - { - if (map->hash_comp(x->key, key)) - { - void *out = x->value; - p->next = x->next; - map->hash_key_free(x->key); - map->hash_val_free(x); - - return out; - } - p = x; - x = x->next; - } while (x); - - return NULL; -} - -bool_t hashmap_is_empty(hashmap_t *map) -{ - for (size_t i = 0; i < map->size; ++i) - { - if (map->entries[i]) - { - return false; - } - } - - return true; -} - -bool_t hashmap_has(hashmap_t *map, void *key) -{ - size_t hash = map->hash_func(key) % map->size; - hashmap_entry_t * x = map->entries[hash]; - - if (x == NULL) - { - return false; - } - do - { - if (map->hash_comp(x->key, key)) - { - return true; - } - x = x->next; - } while (x); - - return false; -} - -list_t *hashmap_keys(hashmap_t *map) -{ - list_t *l = list_create(); - - for (size_t i = 0; i < map->size; ++i) - { - hashmap_entry_t * x = map->entries[i]; - while (x) - { - list_insert_back(l, x->key); - x = x->next; - } - } - - return l; -} - -list_t *hashmap_values(hashmap_t *map) -{ - list_t *l = list_create(); - - for (size_t i = 0; i < map->size; ++i) - { - hashmap_entry_t *x = map->entries[i]; - - while (x) - { - list_insert_back(l, x->value); - x = x->next; - } - } - - return l; -} diff --git a/mentos/src/libc/libgen.c b/mentos/src/libc/libgen.c deleted file mode 100644 index d6827d8..0000000 --- a/mentos/src/libc/libgen.c +++ /dev/null @@ -1,80 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file libgen.c -/// @brief String routines. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "libgen.h" -#include "string.h" -#include "initrd.h" - -int parse_path(char *out, char **cur, char sep, size_t max) -{ - if (**cur == '\0') - { - return 0; - } - - *out++ = **cur; - ++*cur; - --max; - - while ((max > 0) && (**cur != '\0') && (**cur != sep)) - { - *out++ = **cur; - ++*cur; - --max; - } - - *out = '\0'; - - return 1; -} - -char *dirname(const char *path) -{ - static char s[MAX_PATH_LENGTH]; - - static char dot[2] = "."; - - // Check the input path. - if (path == NULL) return dot; - - // Copy the path to the support string. - strcpy(s, path); - - // Get the last occurrence of '/'. - char *last_slash = strrchr(s, '/'); - - if (last_slash == s) - { - // If the slash is acutally the first character of the string, move the - // pointer to the last slash after it. - ++last_slash; - } - else if ((last_slash != NULL) && (last_slash[1] == '\0')) - { - // If the slash is the last character, we need to search before it. - last_slash = memchr(s, '/', last_slash - s); - } - - if (last_slash != NULL) - { - // If we have found it, close the string. - last_slash[0] = '\0'; - } - else - { - // Otherwise, return '.'. - return dot; - } - - return s; -} - -char *basename(const char *path) -{ - char *p = strrchr(path, '/'); - - return p ? p + 1 : (char *) path; -} diff --git a/mentos/src/libc/list.c b/mentos/src/libc/list.c deleted file mode 100644 index 7f50731..0000000 --- a/mentos/src/libc/list.c +++ /dev/null @@ -1,344 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file list.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "list.h" -#include "assert.h" -#include "stdlib.h" - -list_t *list_create() -{ - list_t *list = calloc(sizeof(list_t), 1); - - return list; -} - -size_t list_size(list_t *list) -{ - assert(list && "List is null."); - - return list->size; -} - -bool_t list_empty(list_t *list) -{ - assert(list && "List is null."); - - if (list->size == 0) { - return true; - } - - return false; -} - -listnode_t *list_insert_front(list_t *list, void *value) -{ - assert(list && "List is null."); - assert(value && "Value is null."); - - // Create a new node. - listnode_t *node = calloc(sizeof(listnode_t), 1); - - list->head->prev = node; - node->next = list->head; - node->value = value; - - // If it's the first element, then it's both head and tail - if (!list->head) { - list->tail = node; - } - - list->head = node; - list->size++; - - return node; -} - -listnode_t *list_insert_back(list_t *list, void *value) -{ - assert(list && "List is null."); - assert(value && "Value is null."); - - // Create a new node. - listnode_t *node = calloc(sizeof(listnode_t), 1); - node->prev = list->tail; - - if (list->tail) { - list->tail->next = node; - } - node->value = value; - - if (!list->head) { - list->head = node; - } - - list->tail = node; - list->size++; - - return node; -} - -void list_insert_node_back(list_t *list, listnode_t *node) -{ - assert(list && "List is null."); - assert(node && "Node is null."); - - node->prev = list->tail; - if (list->tail) { - list->tail->next = node; - } - - if (!list->head) { - list->head = node; - } - - list->tail = node; - list->size++; -} - -void *list_remove_node(list_t *list, listnode_t *node) -{ - assert(list && "List is null."); - assert(node && "Node is null."); - - if (list->head == node) { - return list_remove_front(list); - } else if (list->tail == node) { - return list_remove_back(list); - } - - void *value = node->value; - node->next->prev = node->prev; - node->prev->next = node->next; - list->size--; - free(node); - - return value; -} - -void *list_remove_front(list_t *list) -{ - assert(list && "List is null."); - - if (list->head == NULL) { - return NULL; - } - - listnode_t *node = list->head; - void *value = node->value; - list->head = node->next; - - if (list->head) { - list->head->prev = NULL; - } - free(node); - list->size--; - - return value; -} - -void *list_remove_back(list_t *list) -{ - assert(list && "List is null."); - - if (list->head == NULL) { - return NULL; - } - - listnode_t *node = list->tail; - void *value = node->value; - list->tail = node->prev; - - if (list->tail) { - list->tail->next = NULL; - } - free(node); - list->size--; - - return value; -} - -listnode_t *list_find(list_t *list, void *value) -{ - listnode_foreach(listnode, list) - { - if (listnode->value == value) { - return listnode; - } - } - - return NULL; -} - -void list_push(list_t *list, void *value) -{ - assert(list && "List is null."); - assert(value && "Value is null."); - - list_insert_back(list, value); -} - -listnode_t *list_pop_back(list_t *list) -{ - assert(list && "List is null."); - - if (!list->head) { - return NULL; - } - - listnode_t *node = list->tail; - list->tail = node->prev; - - if (list->tail) { - list->tail->next = NULL; - } - - list->size--; - - return node; -} - -listnode_t *list_pop_front(list_t *list) -{ - assert(list && "List is null."); - - if (!list->head) { - return NULL; - } - - listnode_t *node = list->head; - list->head = list->head->next; - list->size--; - - return node; -} - -void list_enqueue(list_t *list, void *value) -{ - assert(list && "List is null."); - assert(value && "Value is null."); - - list_insert_front(list, value); -} - -listnode_t *list_dequeue(list_t *list) -{ - assert(list && "List is null."); - - return list_pop_back(list); -} - -void *list_peek_front(list_t *list) -{ - assert(list && "List is null."); - - if (!list->head) { - return NULL; - } - - return list->head->value; -} - -void *list_peek_back(list_t *list) -{ - assert(list && "List is null."); - - if (!list->tail) { - return NULL; - } - - return list->tail->value; -} - -int list_contain(list_t *list, void *value) -{ - assert(list && "List is null."); - assert(value && "Value is null."); - - int idx = 0; - listnode_foreach(listnode, list) - { - if (listnode->value == value) { - return idx; - } - ++idx; - } - - return -1; -} - -listnode_t *list_get_node_by_index(list_t *list, size_t index) -{ - assert(list && "List is null."); - - if (index >= list_size(list)) { - return NULL; - } - - size_t curr = 0; - listnode_foreach(listnode, list) - { - if (index == curr) { - return listnode; - } - curr++; - } - return NULL; -} - -void *list_remove_by_index(list_t *list, size_t index) -{ - assert(list && "List is null."); - - listnode_t *node = list_get_node_by_index(list, index); - if (node != NULL) { - return list_remove_node(list, node); - } - - return NULL; -} - -void list_destroy(list_t *list) -{ - assert(list && "List is null."); - - // Free each node's value and the node itself. - listnode_t *node = list->head; - while (node != NULL) { - listnode_t *save = node; - node = node->next; - free(save); - } - - // Free the list. - free(list); -} - -void listnode_destroy(listnode_t *node) -{ - assert(node && "Node is null."); - - free(node); -} - -void list_merge(list_t *target, list_t *source) -{ - assert(target && "Target list is null."); - assert(source && "Source list is null."); - - // Destructively merges source into target. - if (target->tail) { - target->tail->next = source->head; - } else { - target->head = source->head; - } - - if (source->tail) { - target->tail = source->tail; - } - - target->size += source->size; - free(source); -} diff --git a/mentos/src/libc/math.c b/mentos/src/libc/math.c deleted file mode 100644 index bac3b1a..0000000 --- a/mentos/src/libc/math.c +++ /dev/null @@ -1,201 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file math.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "math.h" -#include - -double round(double x) -{ - double out; - __asm__("fldln2; fldl %1; frndint" : "=t"(out) : "m"(x)); - - return out; -} - -double floor(double x) -{ - if (x > -1.0 && x < 1.0) { - if (x >= 0) { - return 0.0; - } else { - return -1.0; - } - } - - int x_i = (int)x; - - if (x < 0) { - return (double)(x_i - 1); - } else { - return (double)x_i; - } -} - -#if 0 -double pow(double base, double ex) -{ - // Power of 0. - if (ex == 0) - { - return 1; - } - // Negative exponenet. - else if (ex < 0) - { - return 1 / pow(base, -ex); - } - // Even exponenet. - else if ((int) ex % 2 == 0) - { - float half_pow = pow(base, ex / 2); - - return half_pow * half_pow; - } - // Integer exponenet. - else - { - return base * pow(base, ex - 1); - } -} -#else - -double pow(double x, double y) -{ - double out; - __asm__ __volatile__("fyl2x;" - "fld %%st;" - "frndint;" - "fsub %%st,%%st(1);" - "fxch;" - "fchs;" - "f2xm1;" - "fld1;" - "faddp;" - "fxch;" - "fld1;" - "fscale;" - "fstp %%st(1);" - "fmulp;" - : "=t"(out) - : "0"(x), "u"(y) - : "st(1)"); - return out; -} - -#endif - -long find_nearest_pow_greater(double base, double value) -{ - if (base <= 1) { - return -1; - } - - long pow_value = 0; - - while (pow(base, pow_value) < value) { - pow_value++; - } - - return pow_value; -} - -double exp(double x) -{ - return pow(M_E, x); -} - -double fabs(double x) -{ - double out; - __asm__("fldln2; fldl %1; fabs" : "=t"(out) : "m"(x)); - - return out; -} - -double sqrt(double x) -{ - double out; - __asm__("fldln2; fldl %1; fsqrt" : "=t"(out) : "m"(x)); - - return out; -} - -int isinf(double x) -{ - union { - unsigned long long u; - double f; - } ieee754; - ieee754.f = x; - - return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) == 0x7ff00000 && - ((unsigned)ieee754.u == 0); -} - -int isnan(double x) -{ - union { - unsigned long long u; - double f; - } ieee754; - ieee754.f = x; - - return ((unsigned)(ieee754.u >> 32) & 0x7fffffff) + - ((unsigned)ieee754.u != 0) > - 0x7ff00000; -} - -double log10(double x) -{ - return ln(x) / ln(10); -} - -double ln(double x) -{ - double out; - __asm__("fldln2; fldl %1; fyl2x" : "=t"(out) : "m"(x)); - - return out; -} - -double logx(double x, double y) -{ - // Base may not equal 1 or be negative. - if (y == 1.f || y < 0.f || ln(y) == 0.f) { - return 0.f; - } - - return ln(x) / ln(y); -} - -#define MAXPOWTWO 4.503599627370496000E+15 - -double modf(double x, double *intpart) -{ - register double absvalue; - - if ((absvalue = (x >= 0.0) ? x : -x) >= MAXPOWTWO) { - // It must be an integer. - (*intpart) = x; - } else { - // Shift fraction off right. - (*intpart) = absvalue + MAXPOWTWO; - // Shift back without fraction. - (*intpart) -= MAXPOWTWO; - - // Above arithmetic might round. - while ((*intpart) > absvalue) { - // Test again just to be sure. - (*intpart) -= 1.0; - } - if (x < 0.0) { - (*intpart) = -(*intpart); - } - } - - // Signed fractional part. - return (x - (*intpart)); -} diff --git a/mentos/src/libc/mutex.c b/mentos/src/libc/mutex.c deleted file mode 100644 index 59a5cc6..0000000 --- a/mentos/src/libc/mutex.c +++ /dev/null @@ -1,38 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file mutex.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "mutex.h" -#include "debug.h" - -void mutex_lock(mutex_t *mutex, uint32_t owner) -{ - dbg_print("[%d] Trying to lock mutex...\n", owner); - int failure = 1; - - while (mutex->state == 0 || failure || mutex->owner != owner) { - failure = 1; - if (mutex->state == 0) { - asm("movl $0x01,%%eax\n\t" // move 1 to eax - "xchg %%eax,%0\n\t" // try to set the lock bit - "mov %%eax,%1\n\t" // export our result to a test var - : "=m"(mutex->state), "=r"(failure) - : "m"(mutex->state) - : "%eax"); - } - - if (failure == 0) { - dbg_print("[%d] Acquired mutex...\n", owner); - mutex->owner = - owner; //test to see if we got the lock bit - } - } -} - -void mutex_unlock(mutex_t *mutex) -{ - dbg_print("[%d] Unlock mutex...\n", mutex->owner); - mutex->state = 0; -} diff --git a/mentos/src/libc/ordered_array.c b/mentos/src/libc/ordered_array.c deleted file mode 100644 index 1247cee..0000000 --- a/mentos/src/libc/ordered_array.c +++ /dev/null @@ -1,97 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file ordered_array.c -/// @brief Interface for creating, inserting and deleting from ordered arrays. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "ordered_array.h" -#include "string.h" -#include "assert.h" -#include "stdlib.h" - -int8_t standard_lessthan_predicate(array_type_t a, array_type_t b) -{ - return (a < b); -} - -ordered_array_t create_ordered_array(uint32_t max_size, - lessthan_predicate_t less_than) -{ - ordered_array_t to_ret; - to_ret.array = malloc(max_size * sizeof(array_type_t)); - memset(to_ret.array, 0, max_size * sizeof(array_type_t)); - to_ret.size = 0; - to_ret.max_size = max_size; - to_ret.less_than = less_than; - - return to_ret; -} - -ordered_array_t place_ordered_array(void * addr, - uint32_t max_size, - lessthan_predicate_t less_than) -{ - ordered_array_t to_ret; - to_ret.array = (array_type_t *) addr; - memset(to_ret.array, 0, max_size * sizeof(array_type_t)); - to_ret.size = 0; - to_ret.max_size = max_size; - to_ret.less_than = less_than; - - return to_ret; -} - -void destroy_ordered_array(ordered_array_t * array) -{ - free(array->array); -} - -void insert_ordered_array(array_type_t item, ordered_array_t * array) -{ - assert(array->less_than); - - uint32_t iterator = 0; - while (iterator < array->size && array->less_than(array->array[iterator], - item)) - { - iterator++; - } - - // Just add at the end of the array. - if (iterator == array->size) - { - array->array[array->size++] = item; - } - else - { - array_type_t tmp = array->array[iterator]; - array->array[iterator] = item; - - while (iterator < array->size) - { - iterator++; - array_type_t tmp2 = array->array[iterator]; - array->array[iterator] = tmp; - tmp = tmp2; - } - array->size++; - } -} - -array_type_t lookup_ordered_array(uint32_t i, ordered_array_t * array) -{ - assert(i < array->size); - - return array->array[i]; -} - -void remove_ordered_array(uint32_t i, ordered_array_t * array) -{ - while (i < array->size) - { - array->array[i] = array->array[i + 1]; - i++; - } - - array->size--; -} diff --git a/mentos/src/libc/queue.c b/mentos/src/libc/queue.c deleted file mode 100644 index c4e08dc..0000000 --- a/mentos/src/libc/queue.c +++ /dev/null @@ -1,162 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file queue.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "queue.h" -#include "stdio.h" -#include "stdlib.h" -#include "string.h" - -queue_t queue_create(size_t data_size) -{ - queue_t queue = malloc(sizeof(struct queue_t)); - if (queue == NULL) { - printf("Cannot create the queue.\n"); - return NULL; - } - queue->data_size = data_size; - queue->front = NULL; - queue->back = NULL; - - return queue; -} - -bool_t queue_destroy(queue_t queue) -{ - if (queue == NULL) { - printf("Queue is NULL.\n"); - return false; - } - queue_clear(queue); - free(queue); - - return true; -} - -bool_t queue_is_empty(queue_t queue) -{ - if (queue == NULL) { - printf("Queue is NULL.\n"); - - return false; - } - - return (bool_t)(queue->front == NULL); -} - -bool_t queue_enqueue(queue_t queue, void *data) -{ - if (queue == NULL) { - printf("Queue is NULL.\n"); - - return false; - } - if (data == NULL) { - printf("Data is NULL.\n"); - - return false; - } - queue_node_t *node = malloc(sizeof(struct queue_node_t)); - - if (node == NULL) { - printf("New node of the queue is NULL.\n"); - - return false; - } - // Initialize the new node. - node->data = data; - // Since the new node is the tail, nobody is behind it. - node->next = NULL; - // If the queue is empty, the data is placed also on the front. - if (queue->front == NULL) { - queue->front = node; - } else { - queue->back->next = node; - } - queue->back = node; - - return true; -} - -bool_t queue_dequeue(queue_t queue) -{ - struct queue_node_t *front_node = queue->front; - if (front_node == NULL) { - printf("Queue is Empty\n"); - - return false; - } - if (queue->front == queue->back) { - queue->front = queue->back = NULL; - } else { - queue->front = queue->front->next; - } - free(front_node); - - return true; -} - -bool_t queue_front(queue_t queue, void *data) -{ - if (queue == NULL) { - printf("Queue is NULL.\n"); - - return false; - } - if (queue->front == NULL) { - printf("Queue is empty\n"); - - return false; - } - memcpy(data, queue->front->data, queue->data_size); - - return true; -} - -bool_t queue_back(queue_t queue, void *data) -{ - if (queue == NULL) { - printf("Queue is NULL.\n"); - - return false; - } - if (queue->back == NULL) { - printf("Queue is empty\n"); - - return false; - } - memcpy(data, queue->back->data, queue->data_size); - - return true; -} - -bool_t queue_front_and_dequeue(queue_t queue, void *data) -{ - if (!queue_front(queue, data)) { - return false; - } - if (!queue_dequeue(queue)) { - return false; - } - - return true; -} - -bool_t queue_clear(queue_t queue) -{ - if (queue == NULL) { - printf("Queue is NULL.\n"); - - return false; - } - if (queue->front == NULL) { - return true; - } - while (queue->front) { - queue_dequeue(queue); - } - - return true; -} diff --git a/mentos/src/libc/rbtree.c b/mentos/src/libc/rbtree.c deleted file mode 100644 index 99fda45..0000000 --- a/mentos/src/libc/rbtree.c +++ /dev/null @@ -1,613 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file rbtree.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "rbtree.h" -#include "assert.h" -#include "stdlib.h" - -struct rbtree_node_t { - /// Color red (1), black (0) - int red; - /// Link left [0] and right [1] - rbtree_node_t *link[2]; - /// User provided, used indirectly via rbtree_tree_node_cmp_f. - void *value; -}; - -struct rbtree_t { - rbtree_node_t *root; - rbtree_tree_node_cmp_f cmp; - size_t size; -}; - -struct rbtree_iter_t { - /// Pointer to the tree itself. - rbtree_t *tree; - /// Current node - rbtree_node_t *node; - /// Traversal path - rbtree_node_t *path[RBTREE_ITER_MAX_HEIGHT]; - /// Top of stack - size_t top; -}; - -rbtree_node_t *rbtree_node_alloc() -{ - return malloc(sizeof(rbtree_node_t)); -} - -rbtree_node_t *rbtree_node_init(rbtree_node_t *self, void *value) -{ - if (self) { - self->red = 1; - self->link[0] = self->link[1] = NULL; - self->value = value; - } - - return self; -} - -rbtree_node_t *rbtree_node_create(void *value) -{ - return rbtree_node_init(rbtree_node_alloc(), value); -} - -void *rbtree_node_get_value(rbtree_node_t *self) -{ - if (self) { - return self->value; - } - - return NULL; -} - -void rbtree_node_dealloc(rbtree_node_t *self) -{ - if (self) { - free(self); - } -} - -static int rbtree_node_is_red(const rbtree_node_t *self) -{ - return self ? self->red : 0; -} - -static rbtree_node_t *rbtree_node_rotate(rbtree_node_t *self, int dir) -{ - rbtree_node_t *result = NULL; - if (self) { - result = self->link[!dir]; - self->link[!dir] = result->link[dir]; - result->link[dir] = self; - self->red = 1; - result->red = 0; - } - - return result; -} - -static rbtree_node_t *rbtree_node_rotate2(rbtree_node_t *self, int dir) -{ - rbtree_node_t *result = NULL; - if (self) { - self->link[!dir] = rbtree_node_rotate(self->link[!dir], !dir); - result = rbtree_node_rotate(self, dir); - } - - return result; -} - -// rbtree_t - default callbacks. - -static int rbtree_tree_node_cmp_ptr_cb(rbtree_t *self, rbtree_node_t *a, - rbtree_node_t *b) -{ - (void)self; - - return (a->value > b->value) - (a->value < b->value); -} - -static void rbtree_tree_node_dealloc_cb(rbtree_t *self, rbtree_node_t *node) -{ - if (self) { - if (node) { - rbtree_node_dealloc(node); - } - } -} - -// rbtree_t - -rbtree_t *rbtree_tree_alloc() -{ - return malloc(sizeof(rbtree_t)); -} - -rbtree_t *rbtree_tree_init(rbtree_t *self, rbtree_tree_node_cmp_f node_cmp_cb) -{ - if (self) { - self->root = NULL; - self->size = 0; - self->cmp = - node_cmp_cb ? node_cmp_cb : rbtree_tree_node_cmp_ptr_cb; - } - - return self; -} - -rbtree_t *rbtree_tree_create(rbtree_tree_node_cmp_f node_cb) -{ - return rbtree_tree_init(rbtree_tree_alloc(), node_cb); -} - -void rbtree_tree_dealloc(rbtree_t *self, rbtree_tree_node_f node_cb) -{ - assert(self); - if (node_cb) { - rbtree_node_t *node = self->root; - rbtree_node_t *save = NULL; - - /* Rotate away the left links so that - * we can treat this like the destruction - * of a linked list. - */ - while (node) { - if (node->link[0] == NULL) { - // No left links, just kill the node and move on. - save = node->link[1]; - node_cb(self, node); - free(node); - node = NULL; - } else { - // Rotate away the left link and check again. - save = node->link[0]; - node->link[0] = save->link[1]; - save->link[1] = node; - } - node = save; - } - } - - free(self); -} - -void *rbtree_tree_find(rbtree_t *self, void *value) -{ - void *result = NULL; - if (self) { - rbtree_node_t node = { .value = value }; - rbtree_node_t *it = self->root; - int cmp = 0; - while (it) { - if ((cmp = self->cmp(self, it, &node))) { - /* If the tree supports duplicates, they should be - * chained to the right subtree for this to work. - */ - it = it->link[cmp < 0]; - } else { - break; - } - } - result = it ? it->value : NULL; - } - - return result; -} - -void *rbtree_tree_find_by_value(rbtree_t *self, rbtree_tree_cmp_f cmp_fun, - void *value) -{ - void *result = NULL; - if (self) { - rbtree_node_t *it = self->root; - int cmp = 0; - while (it) { - if ((cmp = cmp_fun(self, it, value))) { - /* If the tree supports duplicates, they should be - * chained to the right subtree for this to work. - */ - it = it->link[cmp < 0]; - } else { - break; - } - } - result = it ? it->value : NULL; - } - - return result; -} - -// Creates (malloc'ates). -int rbtree_tree_insert(rbtree_t *self, void *value) -{ - return rbtree_tree_insert_node(self, rbtree_node_create(value)); -} - -// Returns 1 on success, 0 otherwise. -int rbtree_tree_insert_node(rbtree_t *self, rbtree_node_t *node) -{ - if (self && node) { - if (self->root == NULL) { - self->root = node; - } else { - // False tree root. - rbtree_node_t head = { 0 }; - // Grandparent & parent. - rbtree_node_t *g, *t; - // Iterator & parent. - rbtree_node_t *p, *q; - int dir = 0, last = 0; - - // Set up our helpers. - t = &head; - g = p = NULL; - q = t->link[1] = self->root; - - // Search down the tree for a place to insert. - while (1) { - if (q == NULL) { - // Insert node at the first null link. - p->link[dir] = q = node; - } else if (rbtree_node_is_red(q->link[0]) && - rbtree_node_is_red(q->link[1])) { - // Simple red violation: color flip. - q->red = 1; - q->link[0]->red = 0; - q->link[1]->red = 0; - } - - if (rbtree_node_is_red(q) && - rbtree_node_is_red(p)) { - // Hard red violation: rotations necessary. - int dir2 = t->link[1] == g; - - if (q == p->link[last]) { - t->link[dir2] = - rbtree_node_rotate( - g, !last); - } else { - t->link[dir2] = - rbtree_node_rotate2( - g, !last); - } - } - /* Stop working if we inserted a node. This - * check also disallows duplicates in the tree. - */ - if (self->cmp(self, q, node) == 0) { - break; - } - last = dir; - dir = self->cmp(self, q, node) < 0; - - // Move the helpers down. - if (g != NULL) { - t = g; - } - g = p, p = q; - q = q->link[dir]; - } - // Update the root (it may be different). - self->root = head.link[1]; - } - // Make the root black for simplified logic. - self->root->red = 0; - ++self->size; - } - - return 1; -} - -/* Returns 1 if the value was removed, 0 otherwise. Optional node callback - * can be provided to dealloc node and/or user data. Use - * rbtree_tree_node_dealloc - * default callback to deallocate node created by rbtree_tree_insert(...). - */ -int rbtree_tree_remove_with_cb(rbtree_t *self, void *value, - rbtree_tree_node_f node_cb) -{ - if (self->root != NULL) { - // False tree root. - rbtree_node_t head = { 0 }; - // Value wrapper node. - rbtree_node_t node = { .value = value }; - // Helpers. - rbtree_node_t *q, *p, *g; - // Found item. - rbtree_node_t *f = NULL; - int dir = 1; - - // Set up our helpers. - q = &head; - g = p = NULL; - q->link[1] = self->root; - - /* Search and push a red node down - * to fix red violations as we go. - */ - while (q->link[dir] != NULL) { - int last = dir; - - // Move the helpers down. - g = p, p = q; - q = q->link[dir]; - dir = self->cmp(self, q, &node) < 0; - - /* Save the node with matching value and keep - * going; we'll do removal tasks at the end. - */ - if (self->cmp(self, q, &node) == 0) { - f = q; - } - - // Push the red node down with rotations and color flips. - if (!rbtree_node_is_red(q) && - !rbtree_node_is_red(q->link[dir])) { - if (rbtree_node_is_red(q->link[!dir])) { - p = p->link[last] = - rbtree_node_rotate(q, dir); - } else if (!rbtree_node_is_red(q->link[!dir])) { - rbtree_node_t *s = p->link[!last]; - if (s) { - if (!rbtree_node_is_red( - s->link[!last]) && - !rbtree_node_is_red( - s->link[last])) { - // Color flip. - p->red = 0; - s->red = 1; - q->red = 1; - } else { - int dir2 = - g->link[1] == p; - if (rbtree_node_is_red( - s->link[last])) { - g->link[dir2] = rbtree_node_rotate2( - p, - last); - } else if ( - rbtree_node_is_red( - s->link[!last])) { - g->link[dir2] = rbtree_node_rotate( - p, - last); - } - // Ensure correct coloring. - q->red = g->link[dir2] - ->red = - 1; - g->link[dir2] - ->link[0] - ->red = 0; - g->link[dir2] - ->link[1] - ->red = 0; - } - } - } - } - } - // Replace and remove the saved node. - if (f) { - void *tmp = f->value; - f->value = q->value; - q->value = tmp; - - p->link[p->link[1] == q] = q->link[q->link[0] == NULL]; - - if (node_cb) { - node_cb(self, q); - } - q = NULL; - } - - // Update the root (it may be different). - self->root = head.link[1]; - - // Make the root black for simplified logic. - if (self->root != NULL) { - self->root->red = 0; - } - - --self->size; - } - - return 1; -} - -int rbtree_tree_remove(rbtree_t *self, void *value) -{ - int result = 0; - if (self) { - result = rbtree_tree_remove_with_cb( - self, value, rbtree_tree_node_dealloc_cb); - } - - return result; -} - -size_t rbtree_tree_size(rbtree_t *self) -{ - size_t result = 0; - if (self) { - result = self->size; - } - - return result; -} - -// rbtree_iter_t - -rbtree_iter_t *rbtree_iter_alloc() -{ - return malloc(sizeof(rbtree_iter_t)); -} - -rbtree_iter_t *rbtree_iter_init(rbtree_iter_t *self) -{ - if (self) { - self->tree = NULL; - self->node = NULL; - self->top = 0; - } - - return self; -} - -rbtree_iter_t *rbtree_iter_create() -{ - return rbtree_iter_init(rbtree_iter_alloc()); -} - -void rbtree_iter_dealloc(rbtree_iter_t *self) -{ - if (self) { - free(self); - } -} - -/* Internal function, init traversal object, dir determines whether - * to begin traversal at the smallest or largest valued node. - */ -static void *rbtree_iter_start(rbtree_iter_t *self, rbtree_t *tree, int dir) -{ - void *result = NULL; - if (self) { - self->tree = tree; - self->node = tree->root; - self->top = 0; - // Save the path for later selfersal. - if (self->node != NULL) { - while (self->node->link[dir] != NULL) { - self->path[self->top++] = self->node; - self->node = self->node->link[dir]; - } - } - - result = self->node == NULL ? NULL : self->node->value; - } - - return result; -} - -// Traverse a red black tree in the user-specified direction (0 asc, 1 desc). -static void *rbtree_iter_move(rbtree_iter_t *self, int dir) -{ - if (self->node->link[dir] != NULL) { - // Continue down this branch. - self->path[self->top++] = self->node; - self->node = self->node->link[dir]; - while (self->node->link[!dir] != NULL) { - self->path[self->top++] = self->node; - self->node = self->node->link[!dir]; - } - } else { - // Move to the next branch. - rbtree_node_t *last = NULL; - do { - if (self->top == 0) { - self->node = NULL; - - break; - } - last = self->node; - self->node = self->path[--self->top]; - } while (last == self->node->link[dir]); - } - - return self->node == NULL ? NULL : self->node->value; -} - -void *rbtree_iter_first(rbtree_iter_t *self, rbtree_t *tree) -{ - return rbtree_iter_start(self, tree, 0); -} - -void *rbtree_iter_last(rbtree_iter_t *self, rbtree_t *tree) -{ - return rbtree_iter_start(self, tree, 1); -} - -void *rbtree_iter_next(rbtree_iter_t *self) -{ - return rbtree_iter_move(self, 1); -} - -void *rbtree_iter_prev(rbtree_iter_t *self) -{ - return rbtree_iter_move(self, 0); -} - -int rbtree_tree_test(rbtree_t *self, rbtree_node_t *root) -{ - int lh, rh; - - if (root == NULL) { - return 1; - } else { - rbtree_node_t *ln = root->link[0]; - rbtree_node_t *rn = root->link[1]; - - // Consecutive red links. - if (rbtree_node_is_red(root)) { - if (rbtree_node_is_red(ln) || rbtree_node_is_red(rn)) { - printf("Red violation"); - - return 0; - } - } - - lh = rbtree_tree_test(self, ln); - rh = rbtree_tree_test(self, rn); - - // Invalid binary search tree. - if ((ln != NULL && self->cmp(self, ln, root) >= 0) || - (rn != NULL && self->cmp(self, rn, root) <= 0)) { - puts("Binary tree violation"); - - return 0; - } - - // Black height mismatch. - if (lh != 0 && rh != 0 && lh != rh) { - puts("Black violation"); - - return 0; - } - - // Only count black links. - if (lh != 0 && rh != 0) { - return rbtree_node_is_red(root) ? lh : lh + 1; - } else { - return 0; - } - } -} - -static void rbtree_tree_print_iter(rbtree_t *self, rbtree_node_t *node, - rbtree_tree_node_f fun) -{ - assert(self); - assert(node); - assert(fun); - - fun(self, node); - if (node->link[0]) { - rbtree_tree_print_iter(self, node->link[0], fun); - } - if (node->link[1]) { - rbtree_tree_print_iter(self, node->link[1], fun); - } -} - -void rbtree_tree_print(rbtree_t *self, rbtree_tree_node_f fun) -{ - assert(self); - assert(fun); - - rbtree_tree_print_iter(self, self->root, fun); -} diff --git a/mentos/src/libc/spinlock.c b/mentos/src/libc/spinlock.c deleted file mode 100644 index 659511c..0000000 --- a/mentos/src/libc/spinlock.c +++ /dev/null @@ -1,36 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file spinlock.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "spinlock.h" - -void spinlock_init(spinlock_t *spinlock) -{ - (*spinlock) = 0; -} - -void spinlock_lock(spinlock_t *spinlock) -{ - while (true) - { - if (atomic_set_and_test(spinlock, SPINLOCK_BUSY) == 0) - { - break; - } - while (*spinlock) cpu_relax(); - } -} - -void spinlock_unlock(spinlock_t *spinlock) -{ - barrier(); - - atomic_set(spinlock, SPINLOCK_FREE); -} - -bool_t spinlock_trylock(spinlock_t *spinlock) -{ - return (bool_t) (atomic_set_and_test(spinlock, SPINLOCK_BUSY) == 0); -} diff --git a/mentos/src/libc/stdio.c b/mentos/src/libc/stdio.c deleted file mode 100644 index d665896..0000000 --- a/mentos/src/libc/stdio.c +++ /dev/null @@ -1,181 +0,0 @@ -/// @file stdio.c -/// @brief Standard I/0 functions. -/// @date Apr 2019 - -#include "stdio.h" -#include "video.h" -#include "ctype.h" -#include "string.h" -#include "keyboard.h" -#include "unistd.h" -#include "debug.h" - -void putchar(int character) -{ - write(STDOUT_FILENO, &character, 1); -} - -void puts(char *str) -{ - write(STDOUT_FILENO, str, strlen(str)); -} - -int getchar(void) -{ -#if 1 - char c; - while (true) { - read(STDIN_FILENO, &c, 1); - if (c != -1) - break; - } - return c; -#else - int tmpchar; - while ((tmpchar = keyboard_getc()) == -1) - ; - - return tmpchar; -#endif -} - -char *gets(char *str) -{ - int count = 0; - char tmp[255]; - memset(tmp, '\0', 255); - - do { - int c = getchar(); - - // Return Key. - if (c == '\n') { - break; - } - // Backspace key. - - if (c == '\b') { - if (count > 0) { - count--; - } - } else { - tmp[count++] = (char)c; - } - } while (count < 255); - - tmp[count] = '\0'; - /* tmp cant simply be returned, it is allocated in this stack frame and - * it will be lost! - */ - strcpy(str, tmp); - - return str; -} - -int atoi(const char *str) -{ - // Initialize sign as positive. - int sign = 1; - - // Initialize the result. - int result = 0; - - // Initialize the index. - int i = 0; - - // If the number is negative, then update the sign. - if (str[0] == '-') { - sign = -1; - } - - // Check that the rest of the numbers are digits. - for (i = (sign == -1) ? 1 : 0; str[i] != '\0'; ++i) { - if (!isdigit(str[i])) { - return -1; - } - } - - // Iterate through all digits and update the result. - for (i = (sign == -1) ? 1 : 0; str[i] != '\0'; ++i) { - result = (result * 10) + str[i] - '0'; - } - - return sign * result; -} - -int printf(const char *format, ...) -{ - char buffer[4096]; - va_list ap; - - // Start variabile argument's list. - va_start(ap, format); - - int len = vsprintf(buffer, format, ap); - va_end(ap); - - // Write the contento to standard output. - write(STDOUT_FILENO, buffer, len); - - return len; -} - -size_t scanf(const char *format, ...) -{ - size_t count = 0; - va_list scan; - va_start(scan, format); - - for (; *format; format++) { - if (*format == '%') { - // Declare an input string. - char input[255]; - // Get the input string. - gets(input); - // Evaluate the length of the string. - size_t input_length = strlen(input); - // Add the length of the input to the counter. - count += input_length; - // Evaluate the maximum number of input characters. - size_t max_chars = 0; - if (isdigit(*++format)) { - char max_char_num[16]; - int i = 0; - - while (isdigit(*format)) { - max_char_num[i++] = *format; - format++; - } - max_char_num[i] = '\0'; - int number = atoi(max_char_num); - - if (number > 0) { - max_chars = (size_t)number; - } - } - switch (*format) { - case 's': { - char *s_ptr = va_arg(scan, char *); - if (max_chars == 0 || input_length <= max_chars) - strncpy(s_ptr, input, input_length); - else - strncpy(s_ptr, input, max_chars); - break; - } - case 'd': { - int *d_ptr = va_arg(scan, int *); - if (max_chars != 0 && input_length > max_chars) { - input[max_chars] = '\0'; - } - (*d_ptr) = atoi(input); - break; - } - default: - break; - } - } - } - va_end(scan); - - return count; -} diff --git a/mentos/src/libc/stdlib.c b/mentos/src/libc/stdlib.c deleted file mode 100644 index f064066..0000000 --- a/mentos/src/libc/stdlib.c +++ /dev/null @@ -1,78 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file stdlib.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "syscall.h" -#include "stdlib.h" -#include "string.h" - -/// Used to align the memory. -#define ALIGN(x) \ - (((x) + (sizeof(size_t) - 1)) & ~(sizeof(size_t) - 1)) - - -void *malloc(unsigned int size) -{ - void *_res; - DEFN_SYSCALL1(_res, __NR_brk, size); - - return _res; -} - -void free(void *p) -{ - int _res; - DEFN_SYSCALL1(_res, __NR_free, p); -} - - -void *calloc(size_t element_number, size_t element_size) -{ - void *ptr = malloc(element_number * element_size); - if (ptr) - { - memset(ptr, 0, element_number * element_size); - } - - return ptr; -} - - -/// Seed used to generate random numbers. -int rseed = 0; - -inline void srand(int x) -{ - rseed = x; -} - -#ifndef MS_RAND - -/// The maximum value returned by the rand function. -#define RAND_MAX ((1U << 31) - 1) - -/// @brief Returns a pseudo-random integral number in the range -/// between 0 and RAND_MAX. -inline int rand() -{ - return rseed = (rseed * 1103515245 + 12345) & RAND_MAX; -} - -// MS rand. -#else -/// The maximum 32bit value returned by the rand function. -#define RAND_MAX_32 ((1U << 31) - 1) - -/// The maximum value returned by the rand function. -#define RAND_MAX ((1U << 15) - 1) - -/// @brief Returns a pseudo-random integral number in the range -/// between 0 and RAND_MAX. -inline int rand() -{ - return (rseed = (rseed * 214013 + 2531011) & RAND_MAX_32) >> 16; -} -<<<<<<< HEAD -#endif diff --git a/mentos/src/libc/strerror.c b/mentos/src/libc/strerror.c deleted file mode 100644 index 668ea21..0000000 --- a/mentos/src/libc/strerror.c +++ /dev/null @@ -1,471 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file strerror.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "strerror.h" -#include "string.h" - -char *strerror(int errnum) -{ - static char error[1024]; - - switch (errnum) { - case 0: - strcpy(error, "Success"); - break; -#ifdef ENOENT - case ENOENT: - strcpy(error, "No such file or directory"); - break; -#endif -#ifdef ESRCH - case ESRCH: - strcpy(error, "No such process"); - break; -#endif -#ifdef EINTR - case EINTR: - strcpy(error, "Interrupted system call"); - break; -#endif -#ifdef EIO - case EIO: - strcpy(error, "I/O error"); - break; -#endif -#if defined(ENXIO) && (!defined(ENODEV) || (ENXIO != ENODEV)) - case ENXIO: - strcpy(error, "No such device or address"); - break; -#endif -#ifdef E2BIG - case E2BIG: - strcpy(error, "Arg list too long"); - break; -#endif -#ifdef ENOEXEC - case ENOEXEC: - strcpy(error, "Exec format error"); - break; -#endif -#ifdef EALREADY - case EALREADY: - strcpy(error, "Socket already connected"); - break; -#endif -#ifdef EBADF - case EBADF: - strcpy(error, "Bad file number"); - break; -#endif -#ifdef ECHILD - case ECHILD: - strcpy(error, "No children"); - break; -#endif -#ifdef EDESTADDRREQ - case EDESTADDRREQ: - strcpy(error, "Destination address required"); - break; -#endif -#ifdef EAGAIN - case EAGAIN: - strcpy(error, "No more processes"); - break; -#endif -#ifdef ENOMEM - case ENOMEM: - strcpy(error, "Not enough space"); - break; -#endif -#ifdef EACCES - case EACCES: - strcpy(error, "Permission denied"); - break; -#endif -#ifdef EFAULT - case EFAULT: - strcpy(error, "Bad address"); - break; -#endif -#ifdef ENOTBLK - case ENOTBLK: - strcpy(error, "Block device required"); - break; -#endif -#ifdef EBUSY - case EBUSY: - strcpy(error, "Device or resource busy"); - break; -#endif -#ifdef EEXIST - case EEXIST: - strcpy(error, "File exists"); - break; -#endif -#ifdef EXDEV - case EXDEV: - strcpy(error, "Cross-device link"); - break; -#endif -#ifdef ENODEV - case ENODEV: - strcpy(error, "No such device"); - break; -#endif -#ifdef ENOTDIR - case ENOTDIR: - strcpy(error, "Not a directory"); - break; -#endif -#ifdef EHOSTDOWN - case EHOSTDOWN: - strcpy(error, "Host is down"); - break; -#endif -#ifdef EINPROGRESS - case EINPROGRESS: - strcpy(error, "Connection already in progress"); - break; -#endif -#ifdef EISDIR - case EISDIR: - strcpy(error, "Is a directory"); - break; -#endif -#ifdef EINVAL - case EINVAL: - strcpy(error, "Invalid argument"); - break; -#endif -#ifdef ENETDOWN - case ENETDOWN: - strcpy(error, "Network interface is not configured"); - break; -#endif -#ifdef ENFILE - case ENFILE: - strcpy(error, "Too many open files in system"); - break; -#endif -#ifdef EMFILE - case EMFILE: - strcpy(error, "Too many open files"); - break; -#endif -#ifdef ENOTTY - case ENOTTY: - strcpy(error, "Not a character device"); - break; -#endif -#ifdef ETXTBSY - case ETXTBSY: - strcpy(error, "Text file busy"); - break; -#endif -#ifdef EFBIG - case EFBIG: - strcpy(error, "File too large"); - break; -#endif -#ifdef EHOSTUNREACH - case EHOSTUNREACH: - strcpy(error, "Host is unreachable"); - break; -#endif -#ifdef ENOSPC - case ENOSPC: - strcpy(error, "No space left on device"); - break; -#endif -#ifdef ENOTSUP - case ENOTSUP: - strcpy(error, "Not supported"); - break; -#endif -#ifdef ESPIPE - case ESPIPE: - strcpy(error, "Illegal seek"); - break; -#endif -#ifdef EROFS - case EROFS: - strcpy(error, "Read-only file system"); - break; -#endif -#ifdef EMLINK - case EMLINK: - strcpy(error, "Too many links"); - break; -#endif -#ifdef EPIPE - case EPIPE: - strcpy(error, "Broken pipe"); - break; -#endif -#ifdef EDOM - case EDOM: - strcpy(error, "Math argument"); - break; -#endif -#ifdef ERANGE - case ERANGE: - strcpy(error, "Result too large"); - break; -#endif -#ifdef ENOMSG - case ENOMSG: - strcpy(error, "No message of desired type"); - break; -#endif -#ifdef EIDRM - case EIDRM: - strcpy(error, "Identifier removed"); - break; -#endif -#ifdef EDEADLK - case EDEADLK: - strcpy(error, "Deadlock"); - break; -#endif -#ifdef ENETUNREACH - case ENETUNREACH: - strcpy(error, "Network is unreachable"); - break; -#endif -#ifdef ENOLCK - case ENOLCK: - strcpy(error, "No lock"); - break; -#endif -#ifdef ENOSTR - case ENOSTR: - strcpy(error, "Not a stream"); - break; -#endif -#ifdef ETIME - case ETIME: - strcpy(error, "Stream ioctl timeout"); - break; -#endif -#ifdef ENOSR - case ENOSR: - strcpy(error, "No stream resources"); - break; -#endif -#ifdef ENONET - case ENONET: - strcpy(error, "Machine is not on the network"); - break; -#endif -#ifdef ENOPKG - case ENOPKG: - strcpy(error, "No package"); - break; -#endif -#ifdef EREMOTE - case EREMOTE: - strcpy(error, "Resource is remote"); - break; -#endif -#ifdef ENOLINK - case ENOLINK: - strcpy(error, "Virtual circuit is gone"); - break; -#endif -#ifdef EADV - case EADV: - strcpy(error, "Advertise error"); - break; -#endif -#ifdef ESRMNT - case ESRMNT: - strcpy(error, "Srmount error"); - break; -#endif -#ifdef ECOMM - case ECOMM: - strcpy(error, "Communication error"); - break; -#endif -#ifdef EPROTO - case EPROTO: - strcpy(error, "Protocol error"); - break; -#endif -#ifdef EPROTONOSUPPORT - case EPROTONOSUPPORT: - strcpy(error, "Unknown protocol"); - break; -#endif -#ifdef EMULTIHOP - case EMULTIHOP: - strcpy(error, "Multihop attempted"); - break; -#endif -#ifdef EBADMSG - case EBADMSG: - strcpy(error, "Bad message"); - break; -#endif -#ifdef ELIBACC - case ELIBACC: - strcpy(error, "Cannot access a needed shared library"); - break; -#endif -#ifdef ELIBBAD - case ELIBBAD: - strcpy(error, "Accessing a corrupted shared library"); - break; -#endif -#ifdef ELIBSCN - case ELIBSCN: - strcpy(error, ".lib section in a.out corrupted"); - break; -#endif -#ifdef ELIBMAX - case ELIBMAX: - strcpy(error, - "Attempting to link in more shared libraries than system limit"); - break; -#endif -#ifdef ELIBEXEC - case ELIBEXEC: - strcpy(error, "Cannot exec a shared library directly"); - break; -#endif -#ifdef ENOSYS - case ENOSYS: - strcpy(error, "Function not implemented"); - break; -#endif -#ifdef ENMFILE - case ENMFILE: - strcpy(error, "No more files"); - break; -#endif -#ifdef ENOTEMPTY - case ENOTEMPTY: - strcpy(error, "Directory not empty"); - break; -#endif -#ifdef ENAMETOOLONG - case ENAMETOOLONG: - strcpy(error, "File or path name too long"); - break; -#endif -#ifdef ELOOP - case ELOOP: - strcpy(error, "Too many symbolic links"); - break; -#endif -#ifdef ENOBUFS - case ENOBUFS: - strcpy(error, "No buffer space available"); - break; -#endif -#ifdef EAFNOSUPPORT - case EAFNOSUPPORT: - strcpy(error, - "Address family not supported by protocol family"); - break; -#endif -#ifdef EPROTOTYPE - case EPROTOTYPE: - strcpy(error, "Protocol wrong type for socket"); - break; -#endif -#ifdef ENOTSOCK - case ENOTSOCK: - strcpy(error, "Socket operation on non-socket"); - break; -#endif -#ifdef ENOPROTOOPT - case ENOPROTOOPT: - strcpy(error, "Protocol not available"); - break; -#endif -#ifdef ESHUTDOWN - case ESHUTDOWN: - strcpy(error, "Can't send after socket shutdown"); - break; -#endif -#ifdef ECONNREFUSED - case ECONNREFUSED: - strcpy(error, "Connection refused"); - break; -#endif -#ifdef EADDRINUSE - case EADDRINUSE: - strcpy(error, "Address already in use"); - break; -#endif -#ifdef ECONNABORTED - case ECONNABORTED: - strcpy(error, "Software caused connection abort"); - break; -#endif -#if (defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))) - case EWOULDBLOCK: - strcpy(error, "Operation would block"); - break; -#endif -#ifdef ENOTCONN - case ENOTCONN: - strcpy(error, "Socket is not connected"); - break; -#endif -#ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: - strcpy(error, "Socket type not supported"); - break; -#endif -#ifdef EISCONN - case EISCONN: - strcpy(error, "Socket is already connected"); - break; -#endif -#ifdef ECANCELED - case ECANCELED: - strcpy(error, "Operation canceled"); - break; -#endif -#ifdef ENOTRECOVERABLE - case ENOTRECOVERABLE: - strcpy(error, "State not recoverable"); - break; -#endif -#ifdef EOWNERDEAD - case EOWNERDEAD: - strcpy(error, "Previous owner died"); - break; -#endif -#ifdef ESTRPIPE - case ESTRPIPE: - strcpy(error, "Streams pipe error"); - break; -#endif -#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (ENOTSUP != EOPNOTSUPP)) - case EOPNOTSUPP: - strcpy(error, "Operation not supported on socket"); - break; -#endif -#ifdef EMSGSIZE - case EMSGSIZE: - strcpy(error, "Message too long"); - break; -#endif -#ifdef ETIMEDOUT - case ETIMEDOUT: - strcpy(error, "Connection timed out"); - break; -#endif - default: - strcpy(error, "Unknown error"); - break; - } - - return error; -} diff --git a/mentos/src/libc/string.c b/mentos/src/libc/string.c deleted file mode 100644 index 196163d..0000000 --- a/mentos/src/libc/string.c +++ /dev/null @@ -1,850 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file string.c -/// @brief String routines. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include -#include "string.h" -#include "ctype.h" -#include "stdlib.h" -#include "stdio.h" - -char *strncpy(char *destination, const char *source, size_t num) -{ - char *start = destination; - while (num && (*destination++ = *source++)) { - num--; - } - if (num) { - while (--num) { - *destination++ = '\0'; - } - } - - return start; -} - -int strncmp(const char *s1, const char *s2, size_t n) -{ - if (!n) - return 0; - while ((--n > 0) && (*s1) && (*s2) && (*s1 == *s2)) { - s1++; - s2++; - } - - return *(unsigned char *)s1 - *(unsigned char *)s2; -} - -int stricmp(const char *s1, const char *s2) -{ - while (*s2 != 0 && toupper(*s1) == toupper(*s2)) { - s1++, s2++; - } - - return (toupper(*s1) - toupper(*s2)); -} - -int strnicmp(const char *s1, const char *s2, size_t n) -{ - int f, l; - - do { - if (((f = (unsigned char)(*(s1++))) >= 'A') && (f <= 'Z')) { - f -= 'A' - 'a'; - } - if (((l = (unsigned char)(*(s2++))) >= 'A') && (l <= 'Z')) { - l -= 'A' - 'a'; - } - } while (--n && f && (f == l)); - - return f - l; -} - -char *strchr(const char *s, int ch) -{ - while (*s && *s != (char)ch) { - s++; - } - if (*s == (char)ch) - return (char *)s; - { - return NULL; - } -} - -char *strrchr(const char *s, int ch) -{ - char *start = (char *)s; - - while (*s++) - ; - - while (--s != start && *s != (char)ch) - ; - - if (*s == (char)ch) { - return (char *)s; - } - - return NULL; -} - -char *strstr(const char *str1, const char *str2) -{ - char *cp = (char *)str1; - char *s1, *s2; - - if (!*str2) { - return (char *)str1; - } - - while (*cp) { - s1 = cp; - s2 = (char *)str2; - - while (*s1 && *s2 && !(*s1 - *s2)) { - s1++, s2++; - } - if (!*s2) { - return cp; - } - cp++; - } - - return NULL; -} - -size_t strspn(const char *string, const char *control) -{ - const char *str = string; - const char *ctrl = control; - - char map[32]; - size_t n; - - // Clear out bit map. - for (n = 0; n < 32; n++) { - map[n] = 0; - } - - // Set bits in control map. - while (*ctrl) { - map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); - ctrl++; - } - - // 1st char NOT in control map stops search. - if (*str) { - n = 0; - while (map[*str >> 3] & (1 << (*str & 7))) { - n++; - str++; - } - - return n; - } - - return 0; -} - -size_t strcspn(const char *string, const char *control) -{ - const char *str = string; - const char *ctrl = control; - - char map[32]; - size_t n; - - // Clear out bit map. - for (n = 0; n < 32; n++) - map[n] = 0; - - // Set bits in control map. - while (*ctrl) { - map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); - ctrl++; - } - - // 1st char in control map stops search. - n = 0; - map[0] |= 1; - while (!(map[*str >> 3] & (1 << (*str & 7)))) { - n++; - str++; - } - - return n; -} - -char *strpbrk(const char *string, const char *control) -{ - const char *str = string; - const char *ctrl = control; - - char map[32]; - int n; - - // Clear out bit map. - for (n = 0; n < 32; n++) - map[n] = 0; - - // Set bits in control map. - while (*ctrl) { - map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); - ctrl++; - } - - // 1st char in control map stops search. - while (*str) { - if (map[*str >> 3] & (1 << (*str & 7))) { - return (char *)str; - } - str++; - } - - return NULL; -} - -void *memmove(void *dst, const void *src, size_t n) -{ - void *ret = dst; - - if (dst <= src || (char *)dst >= ((char *)src + n)) { - /* Non-overlapping buffers; copy from lower addresses to higher - * addresses. - */ - while (n--) { - *(char *)dst = *(char *)src; - dst = (char *)dst + 1; - src = (char *)src + 1; - } - } else { - // Overlapping buffers; copy from higher addresses to lower addresses. - dst = (char *)dst + n - 1; - src = (char *)src + n - 1; - - while (n--) { - *(char *)dst = *(char *)src; - dst = (char *)dst - 1; - src = (char *)src - 1; - } - } - - return ret; -} - -void *memchr(const void *buf, int ch, size_t n) -{ - while (n && (*(unsigned char *)buf != (unsigned char)ch)) { - buf = (unsigned char *)buf + 1; - n--; - } - - return (n ? (void *)buf : NULL); -} - -char *strlwr(char *s) -{ - char *p = s; - - while (*p) { - *p = (char)tolower(*p); - p++; - } - - return s; -} - -char *strupr(char *s) -{ - char *p = s; - - while (*p) { - *p = (char)toupper(*p); - p++; - } - - return s; -} - -char *strncat(char *s1, const char *s2, size_t n) -{ - char *start = s1; - - while (*s1++) - ; - { - s1--; - } - - while (n--) { - if (!(*s1++ = *s2++)) - return start; - } - - *s1 = '\0'; - - return start; -} - -char *strnset(char *s, int c, size_t n) -{ - while (n-- && *s) { - *s++ = (char)c; - } - - return s; -} - -char *strrev(char *s) -{ - char *start = s; - char *left = s; - char ch; - - while (*s++) - ; - s -= 2; - - while (left < s) { - ch = *left; - *left++ = *s; - *s-- = ch; - } - - return start; -} - -char *strtok_r(char *string, const char *control, char **lasts) -{ - char *str; - const char *ctrl = control; - - char map[32]; - int n; - - // Clear control map. - for (n = 0; n < 32; n++) { - map[n] = 0; - } - - // Set bits in delimiter table. - do { - map[*ctrl >> 3] |= (char)(1 << (*ctrl & 7)); - } while (*ctrl++); - - /* Initialize str. If string is NULL, set str to the saved - * pointer (i.e., continue breaking tokens out of the string - * from the last strtok call). - */ - if (string) { - str = string; - } else { - str = *lasts; - } - - /* Find beginning of token (skip over leading delimiters). Note that - * there is no token iff this loop sets str to point to the terminal - * null (*str == '\0'). - */ - while ((map[*str >> 3] & (1 << (*str & 7))) && *str) { - str++; - } - - string = str; - - /* Find the end of the token. If it is not the end of the string, - * put a null there. - */ - for (; *str; str++) { - if (map[*str >> 3] & (1 << (*str & 7))) { - *str++ = '\0'; - - break; - } - } - - // Update nexttoken. - *lasts = str; - - // Determine if a token has been found. - if (string == (char *)str) { - return NULL; - } else { - return string; - } -} - -// Intrinsic functions. - -/* - * #pragma function(memset) - * #pragma function(memcmp) - * #pragma function(memcpy) - * #pragma function(strcpy) - * #pragma function(strlen) - * #pragma function(strcat) - * #pragma function(strcmp) - * #pragma function(strset) - */ - -void *memset(void *ptr, int value, size_t num) -{ - // Truncate c to 8 bits. - value = (value & 0xFF); - - char *dst = (char *)ptr; - - // Initialize the rest of the size. - while (num--) { - *dst++ = (char)value; - } - - return ptr; -} - -int memcmp(const void *dst, const void *src, size_t n) -{ - if (!n) { - return 0; - } - - while (--n && *(char *)dst == *(char *)src) { - dst = (char *)dst + 1; - src = (char *)src + 1; - } - - return *((unsigned char *)dst) - *((unsigned char *)src); -} - -void *memcpy(void *_dst, const void *_src, size_t num) -{ - char *dst = _dst; - const char *src = _src; - - while (num--) { - *dst++ = *src++; - } - - return _dst; -} - -void *memccpy(void *dst, const void *src, int c, size_t n) -{ - while (n && (*((char *)(dst = (char *)dst + 1) - 1) = - *((char *)(src = (char *)src + 1) - 1)) != (char)c) { - n--; - } - - return n ? dst : NULL; -} - -char *strcpy(char *dst, const char *src) -{ - char *save = dst; - - while ((*dst++ = *src++) != '\0') - ; - - return save; -} - -size_t strlen(const char *s) -{ - const char *eos; - - for (eos = s; *eos != 0; ++eos) - ; - - long len = eos - s; - - return (len < 0) ? 0 : (size_t)len; -} - -size_t strnlen(const char *s, size_t count) -{ - const char *sc; - - for (sc = s; *sc != '\0' && count--; ++sc) - ; - - long len = sc - s; - - return (len < 0) ? 0 : (size_t)len; -} - -int strcmp(const char *s1, const char *s2) -{ - int ret = 0; - const char *s1t = s1, *s2t = s2; - - for (; !(ret = *s1t - *s2t) && *s2t; ++s1t, ++s2t) - ; - - return (ret < 0) ? -1 : (ret > 0) ? 1 : 0; -} - -char *strcat(char *dst, const char *src) -{ - char *cp = dst; - - while (*cp) { - cp++; - } - - while ((*cp++ = *src++) != '\0') - ; - - return dst; -} - -char *strset(char *s, int c) -{ - char *start = s; - - while (*s) { - *s++ = (char)c; - } - - return start; -} - -char *strtok(char *str, const char *delim) -{ - const char *spanp; - int c, sc; - char *tok; - static char *last; - - if (str == NULL && (str = last) == NULL) { - return (NULL); - } - -cont: - c = *str++; - for (spanp = delim; (sc = *spanp++) != 0;) { - if (c == sc) { - goto cont; - } - } - - if (c == 0) { - last = NULL; - - return (NULL); - } - tok = str - 1; - - for (;;) { - c = *str++; - spanp = delim; - do { - if ((sc = *spanp++) == c) { - if (c == 0) { - str = NULL; - } else { - str[-1] = 0; - } - last = str; - - return (tok); - } - } while (sc != 0); - } -} - -int _kstrncmp(const char *s1, const char *s2, size_t num) -{ - /* If the number of characters that has to be checked is equal to zero, - * just return 0. - */ - if (num == 0) { - return 0; - } - size_t sn = 0; - - while ((*s1 == *s2) && (sn < (num - 1))) { - ++s1; - ++s2; - ++sn; - } - - if (*s1 > *s2) { - return 1; - } - - if (*s1 < *s2) { - return -1; - } - - return 0; -} - -char *trim(char *str) -{ - size_t len = 0; - char *frontp = str; - char *endp = NULL; - - if (str == NULL) { - return NULL; - } - if (str[0] == '\0') { - return str; - } - - len = strlen(str); - endp = str + len; - - /* Move the front and back pointers to address the first non-whitespace - * characters from each end. - */ - while (isspace((unsigned char)*frontp)) { - ++frontp; - } - if (endp != frontp) { - while (isspace((unsigned char)*(--endp)) && endp != frontp) - ; - } - if (str + len - 1 != endp) { - *(endp + 1) = '\0'; - } else if (frontp != str && endp == frontp) { - *str = '\0'; - } - /* Shift the string so that it starts at str so that if it's dynamically - * allocated, we can still free it on the returned pointer. Note the reuse - * of endp to mean the front of the string buffer now. - */ - endp = str; - if (frontp != str) { - while (*frontp) { - *endp++ = *frontp++; - } - *endp = '\0'; - } - - return str; -} - -char *strdup(const char *s) -{ - size_t len = strlen(s) + 1; - - return memcpy(malloc(len), s, len); -} - -char *kstrdup(const char *s) -{ - size_t len = strlen(s) + 1; - - return memcpy(kmalloc(len), s, len); -} - -char *strsep(char **stringp, const char *delim) -{ - char *s; - const char *spanp; - int c, sc; - char *tok; - if ((s = *stringp) == NULL) { - return (NULL); - } - for (tok = s;;) { - c = *s++; - spanp = delim; - do { - if ((sc = *spanp++) == c) { - if (c == 0) { - s = NULL; - } else { - s[-1] = 0; - } - *stringp = s; - - return (tok); - } - } while (sc != 0); - } -} - -list_t *str_split(const char *str, const char *delim, unsigned int *num) -{ - list_t *ret_list = list_create(); - char *s = strdup(str); - char *token, *rest = s; - - while ((token = strsep(&rest, delim)) != NULL) { - if (!strcmp(token, ".")) { - continue; - } - if (!strcmp(token, "..")) { - if (list_size(ret_list) > 0) - list_pop_back(ret_list); - - continue; - } - list_push(ret_list, strdup(token)); - - if (num) { - (*num)++; - } - } - free(s); - - return ret_list; -} - -char *list2str(list_t *list, const char *delim) -{ - char *ret = malloc(256); - memset(ret, 0, 256); - size_t len = 0; - size_t ret_len = 256; - - while (list_size(list) > 0) { - char *temp = list_pop_back(list)->value; - size_t len_temp = strlen(temp); - if (len + len_temp + 1 + 1 > ret_len) { - ret_len = ret_len * 2; - free(ret); - ret = malloc(ret_len); - len = len + len_temp + 1; - } - strcat(ret, delim); - strcat(ret, temp); - } - - return ret; -} - -void int_to_str(char *buffer, unsigned int num, unsigned int base) -{ - // int numval; - char *p, *pbase; - - p = pbase = buffer; - - if (base == 16) { - sprintf(buffer, "%0x", num); - } else { - if (num == 0) { - *p++ = '0'; - } - while (num != 0) { - *p++ = (char)('0' + (num % base)); - num = num / base; - } - *p-- = 0; - - while (p > pbase) { - char tmp; - tmp = *p; - *p = *pbase; - *pbase = tmp; - - p--; - pbase++; - } - } -} - -void _knntos(char *buffer, int num, int base) -{ - // int numval; - char *p, *pbase; - - p = pbase = buffer; - - if (num < 0) { - num = (~num) + 1; - *p++ = '-'; - pbase++; - } - while (num > 0) { - *p++ = (char)('0' + (num % base)); - num = num / base; - } - - *p-- = 0; - while (p > pbase) { - char tmp; - tmp = *p; - *p = *pbase; - *pbase = tmp; - - p--; - pbase++; - } -} - -char *replace_char(char *str, char find, char replace) -{ - char *current_pos = strchr(str, find); - - while (current_pos) { - *current_pos = replace; - current_pos = strchr(current_pos, find); - } - - return str; -} - -void strmode(mode_t mode, char *p) -{ - // Usr. - if (mode & 0400) { - *p++ = 'r'; - } else { - *p++ = '-'; - } - if (mode & 0200) { - *p++ = 'w'; - } else { - *p++ = '-'; - } - if (mode & 0100) { - *p++ = 'x'; - } else { - *p++ = '-'; - } - - // Group. - if (mode & 0040) { - *p++ = 'r'; - } else { - *p++ = '-'; - } - if (mode & 0020) { - *p++ = 'w'; - } else { - *p++ = '-'; - } - if (mode & 0010) { - *p++ = 'x'; - } else { - *p++ = '-'; - } - - // Other. - if (mode & 0004) { - *p++ = 'r'; - } else { - *p++ = '-'; - } - if (mode & 0002) { - *p++ = 'w'; - } else { - *p++ = '-'; - } - if (mode & 0001) { - *p++ = 'x'; - } else { - *p++ = '-'; - } - - // Will be a '+' if ACL's implemented. - *p++ = ' '; - *p = '\0'; -} diff --git a/mentos/src/libc/tree.c b/mentos/src/libc/tree.c deleted file mode 100644 index 3afd337..0000000 --- a/mentos/src/libc/tree.c +++ /dev/null @@ -1,217 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file tree.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "tree.h" -#include "list.h" -#include "stdlib.h" -#include "debug.h" -#include "panic.h" - -tree_t *tree_create() -{ - tree_t *out = malloc(sizeof(tree_t)); - out->nodes = 0; - out->root = NULL; - - return out; -} - -tree_node_t *tree_set_root(tree_t *tree, void *value) -{ - tree_node_t *root = tree_node_create(value); - tree->root = root; - tree->nodes = 1; - - return root; -} - -void tree_node_destroy(tree_node_t *node) -{ - listnode_foreach(child, node->children) - { - tree_node_destroy((tree_node_t *)child->value); - } - free(node->value); -} - -void tree_destroy(tree_t *tree) -{ - if (tree->root) { - tree_node_destroy(tree->root); - } -} - -/// @brief Free a node and its children, but not their contents. -static void tree_node_free(tree_node_t *node) -{ - if (!node) { - return; - } - listnode_foreach(child, node->children) - { - tree_node_free(child->value); - } - list_destroy(node->children); - free(node); -} - -void tree_free(tree_t *tree) -{ - tree_node_free(tree->root); -} - -tree_node_t *tree_node_create(void *value) -{ - tree_node_t *out = malloc(sizeof(tree_node_t)); - out->value = value; - out->children = list_create(); - out->parent = NULL; - - return out; -} - -void tree_node_insert_child_node(tree_t *tree, tree_node_t *parent, - tree_node_t *node) -{ - list_insert_back(parent->children, node); - node->parent = parent; - tree->nodes++; -} - -tree_node_t *tree_node_insert_child(tree_t *tree, tree_node_t *parent, - void *value) -{ - tree_node_t *out = tree_node_create(value); - tree_node_insert_child_node(tree, parent, out); - - return out; -} - -tree_node_t *tree_node_find_parent(tree_node_t *haystack, tree_node_t *needle) -{ - tree_node_t *found = NULL; - listnode_foreach(child, haystack->children) - { - if (child->value == needle) { - return haystack; - } - found = tree_node_find_parent((tree_node_t *)child->value, needle); - if (found) { - break; - } - } - - return found; -} - -/// @brief Return the parent of a node, inefficiently. -tree_node_t *tree_find_parent(tree_t *tree, tree_node_t *node) -{ - if (!tree->root) { - return NULL; - } - - return tree_node_find_parent(tree->root, node); -} - -/// @brief Return the number of children this node has. -static size_t tree_count_children(tree_node_t *node) -{ - if (!node) { - return 0; - } - - if (!node->children) { - return 0; - } - - size_t out = node->children->size; - listnode_foreach(child, node->children) - { - out += tree_count_children((tree_node_t *)child->value); - } - - return out; -} - -void tree_node_parent_remove(tree_t *tree, tree_node_t *parent, - tree_node_t *node) -{ - tree->nodes -= tree_count_children(node) + 1; - list_remove_node(parent->children, list_find(parent->children, node)); - tree_node_free(node); -} - -void tree_node_remove(tree_t *tree, tree_node_t *node) -{ - tree_node_t *parent = node->parent; - if (!parent) { - if (node == tree->root) { - tree->nodes = 0; - tree->root = NULL; - tree_node_free(node); - } else - kernel_panic("Found node with no parent which is not root."); - } - tree_node_parent_remove(tree, parent, node); -} - -void tree_remove(tree_t *tree, tree_node_t *node) -{ - tree_node_t *parent = node->parent; - /* This is something we just can't do. We don't know how to merge our - * children into our "parent" because then we'd have more than one root node. - * A good way to think about this is actually what this tree struct - * primarily exists for: processes. Trying to remove the root is equivalent - * to trying to kill init! Which is bad. We immediately fault on such - * a case anyway ("Tried to kill init, shutting down!"). - */ - if (!parent) { - return; - } - tree->nodes--; - list_remove_node(parent->children, list_find(parent->children, node)); - listnode_foreach(child, node->children) - { - // Reassign the parents. - ((tree_node_t *)child->value)->parent = parent; - } - list_merge(parent->children, node->children); - free(node); -} - -void tree_break_off(tree_t *tree, tree_node_t *node) -{ - tree_node_t *parent = node->parent; - if (!parent) { - return; - } - list_remove_node(parent->children, list_find(parent->children, node)); -} - -/// @brief Searches the item inside tree and returns the node which contains it. -tree_node_t *tree_node_find(tree_node_t *node, void *search, - tree_comparator_t comparator) -{ - if (comparator(node->value, search)) { - return node; - } - tree_node_t *found; - listnode_foreach(child, node->children) - { - found = tree_node_find((tree_node_t *)child->value, search, comparator); - if (found) { - return found; - } - } - - return NULL; -} - -tree_node_t *tree_find(tree_t *tree, void *value, tree_comparator_t comparator) -{ - return tree_node_find(tree->root, value, comparator); -} diff --git a/mentos/src/libc/unistd/chdir.c b/mentos/src/libc/unistd/chdir.c deleted file mode 100644 index 790f5e5..0000000 --- a/mentos/src/libc/unistd/chdir.c +++ /dev/null @@ -1,19 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file chdir.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "syscall.h" -#include "errno.h" - -void chdir(char const * path) -{ - ssize_t retval; - DEFN_SYSCALL1(retval, __NR_chdir, path); - if (retval < 0) - { - errno = -retval; - } -} diff --git a/mentos/src/libc/unistd/execve.c b/mentos/src/libc/unistd/execve.c deleted file mode 100644 index c0351ee..0000000 --- a/mentos/src/libc/unistd/execve.c +++ /dev/null @@ -1,24 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file execve.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "errno.h" -#include "syscall.h" - -int execve(const char *path, char *const argv[], char *const envp[]) -{ - ssize_t retval; - - DEFN_SYSCALL3(retval, __NR_execve, path, argv, envp); - - if (retval < 0) - { - errno = -retval; - retval = -1; - } - - return retval; -} diff --git a/mentos/src/libc/unistd/exit.c b/mentos/src/libc/unistd/exit.c deleted file mode 100644 index aa8a3f5..0000000 --- a/mentos/src/libc/unistd/exit.c +++ /dev/null @@ -1,17 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file exit.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "types.h" -#include "syscall.h" - -void exit(int status) { - int _res; - - DEFN_SYSCALL1(_res, __NR_exit, status); - - // The process never returns from this system call! -} diff --git a/mentos/src/libc/unistd/getcwd.c b/mentos/src/libc/unistd/getcwd.c deleted file mode 100644 index 2e2ad1b..0000000 --- a/mentos/src/libc/unistd/getcwd.c +++ /dev/null @@ -1,19 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file getcwd.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "syscall.h" -#include "errno.h" - -void getcwd(char * path, size_t size) -{ - ssize_t retval; - DEFN_SYSCALL2(retval, __NR_getcwd, path, size); - if (retval < 0) - { - errno = -retval; - } -} diff --git a/mentos/src/libc/unistd/getpid.c b/mentos/src/libc/unistd/getpid.c deleted file mode 100644 index 58616cb..0000000 --- a/mentos/src/libc/unistd/getpid.c +++ /dev/null @@ -1,19 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file getpid.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" - -#include "syscall.h" -#include "types.h" - -pid_t getpid() -{ - pid_t ret; - - DEFN_SYSCALL0(ret, __NR_getpid); - - return ret; -} diff --git a/mentos/src/libc/unistd/getppid.c b/mentos/src/libc/unistd/getppid.c deleted file mode 100644 index c881377..0000000 --- a/mentos/src/libc/unistd/getppid.c +++ /dev/null @@ -1,18 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file getppid.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "types.h" -#include "syscall.h" - -pid_t getppid() -{ - pid_t ret; - - DEFN_SYSCALL0(ret, __NR_getppid); - - return ret; -} diff --git a/mentos/src/libc/unistd/nice.c b/mentos/src/libc/unistd/nice.c deleted file mode 100644 index dd6d8c9..0000000 --- a/mentos/src/libc/unistd/nice.c +++ /dev/null @@ -1,17 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file nice.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "syscall.h" - -int nice(int inc) -{ - ssize_t _res; - - DEFN_SYSCALL1(_res, __NR_nice, inc); - - return _res; -} diff --git a/mentos/src/libc/unistd/open.c b/mentos/src/libc/unistd/open.c deleted file mode 100644 index f6c8f3a..0000000 --- a/mentos/src/libc/unistd/open.c +++ /dev/null @@ -1,24 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file open.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "errno.h" -#include "syscall.h" - -int open(const char *pathname, int flags, mode_t mode) -{ - ssize_t retval; - - DEFN_SYSCALL3(retval, __NR_open, pathname, flags, mode); - - if (retval < 0) - { - errno = -retval; - retval = -1; - - } - return retval; -} diff --git a/mentos/src/libc/unistd/read.c b/mentos/src/libc/unistd/read.c deleted file mode 100644 index f233796..0000000 --- a/mentos/src/libc/unistd/read.c +++ /dev/null @@ -1,24 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file read.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "errno.h" -#include "syscall.h" - -ssize_t read(int fd, void *buf, size_t nbytes) -{ - ssize_t retval; - - DEFN_SYSCALL3(retval, __NR_read, fd, buf, nbytes); - - if (retval < 0) - { - errno = -retval; - retval = -1; - } - - return retval; -} diff --git a/mentos/src/libc/unistd/reboot.c b/mentos/src/libc/unistd/reboot.c deleted file mode 100644 index f1e347d..0000000 --- a/mentos/src/libc/unistd/reboot.c +++ /dev/null @@ -1,23 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file reboot.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "errno.h" -#include "syscall.h" - -int reboot(int magic1, int magic2, unsigned int cmd, void *arg) -{ - ssize_t retval; - - DEFN_SYSCALL4(retval, __NR_reboot, magic1, magic2, cmd, arg); - - if (retval < 0) { - errno = -retval; - retval = -1; - } - - return retval; -} diff --git a/mentos/src/libc/unistd/vfork.c b/mentos/src/libc/unistd/vfork.c deleted file mode 100644 index f6c4f18..0000000 --- a/mentos/src/libc/unistd/vfork.c +++ /dev/null @@ -1,26 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file vfork.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "types.h" -#include "errno.h" -#include "syscall.h" -#include -#include -#include - -pid_t vfork() -{ - pid_t retval = 0; - - DEFN_SYSCALL0(retval, __NR_vfork); - - if (retval < 0) { - errno = -retval; - retval = -1; - } - return retval; -} diff --git a/mentos/src/libc/unistd/waitpid.c b/mentos/src/libc/unistd/waitpid.c deleted file mode 100644 index 04320ec..0000000 --- a/mentos/src/libc/unistd/waitpid.c +++ /dev/null @@ -1,41 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file waitpid.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "wait.h" -#include "errno.h" -#include "syscall.h" -#include "scheduler.h" - -pid_t wait(int *status) -{ - return waitpid(-1, status, 0); -} - -pid_t waitpid(pid_t pid, int *status, int options) -{ - pid_t retval; - - int _status = 0, *_status_ptr = &_status; - - do - { - DEFN_SYSCALL3(retval, __NR_waitpid, pid, _status_ptr, options); - } while (((*_status_ptr) != EXIT_ZOMBIE) && !(options && WNOHANG)); - - if (status != NULL) - { - (*status) = (*_status_ptr); - } - - if (retval < 0) - { - errno = -retval; - retval = -1; - } - - return retval; -} diff --git a/mentos/src/libc/unistd/write.c b/mentos/src/libc/unistd/write.c deleted file mode 100644 index 50c994f..0000000 --- a/mentos/src/libc/unistd/write.c +++ /dev/null @@ -1,25 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file write.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "unistd.h" -#include "errno.h" -#include "syscall.h" - -ssize_t write(int fd, void *buf, size_t nbytes) -{ - ssize_t retval; - - DEFN_SYSCALL3(retval, __NR_write, fd, buf, nbytes); - - if (retval < 0) - { - errno = -retval; - retval = -1; - - } - - return retval; -} diff --git a/mentos/src/libc/vsprintf.c b/mentos/src/libc/vsprintf.c deleted file mode 100644 index 92554d3..0000000 --- a/mentos/src/libc/vsprintf.c +++ /dev/null @@ -1,787 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file vsprintf.c -/// @brief Print formatting routines. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "math.h" -#include "fcvt.h" -#include "ctype.h" -#include "string.h" -#include "stdarg.h" -#include "stdbool.h" - -#define CVTBUFSIZE 500 - -#define FLAGS_ZEROPAD (1U << 0U) -#define FLAGS_LEFT (1U << 1U) -#define FLAGS_PLUS (1U << 2U) -#define FLAGS_SPACE (1U << 3U) -#define FLAGS_HASH (1U << 4U) -#define FLAGS_UPPERCASE (1U << 5U) -#define FLAGS_SIGN (1U << 6U) - -/* 'ftoa' conversion buffer size, this must be big enough to hold one converted - * float number including padded zeros (dynamically created on stack) - * default: 32 byte. - */ -#ifndef PRINTF_FTOA_BUFFER_SIZE -#define PRINTF_FTOA_BUFFER_SIZE 32U -#endif - -/// The list of digits. -static char *digits = "0123456789abcdefghijklmnopqrstuvwxyz"; - -/// The list of uppercase digits. -static char *upper_digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - -/// @brief Returns the index of the first non-integer character. -static int skip_atoi(const char **s) -{ - int i = 0; - do { - i = i * 10 + *((*s)++) - '0'; - } while (isdigit(**s)); - - return i; -} - -#if 0 -char *dtoa(char *str, - double num, - int32_t precision) -{ - // Handle special cases. - if (isnan(num)) - { - strcpy(str, "nan"); - } - else if (isinf(num)) - { - strcpy(str, "inf"); - } - else if (num == 0.0) - { - strcpy(str, "0"); - } - else - { - int digit, m, m1; - char *c = str; - int neg = (num < 0); - if (neg) - { - num = -num; - } - // Calculate magnitude. - m = log10(num); - int useExp = (m >= 14 || (neg && m >= 9) || m <= -9); - if (neg) - { - *(c++) = '-'; - } - // Set up for scientific notation. - if (useExp) - { - if (m < 0) - { - m -= 1.0; - } - num = num / pow(10.0, m); - m1 = m; - m = 0; - } - if (m < 1.0) - { - m = 0; - } - // Convert the number. - while (num > precision || m >= 0) - { - double weight = pow(10.0, m); - if (weight > 0 && !isinf(weight)) - { - digit = floor(num / weight); - num -= (digit * weight); - *(c++) = '0' + digit; - } - if (m == 0 && num > 0) - *(c++) = '.'; - m--; - } - if (useExp) - { - // Convert the exponent. - int i, j; - *(c++) = 'e'; - if (m1 > 0) - { - *(c++) = '+'; - } - else - { - *(c++) = '-'; - m1 = -m1; - } - m = 0; - while (m1 > 0) - { - *(c++) = '0' + m1 % 10; - m1 /= 10; - m++; - } - c -= m; - for (i = 0, j = m - 1; i < j; i++, j--) - { - // Swap without temporary. - c[i] ^= c[j]; - c[j] ^= c[i]; - c[i] ^= c[j]; - } - c += m; - } - *(c) = '\0'; - } - - return str; -} -#endif - -static char *number(char *str, long num, int base, int size, int32_t precision, - int type) -{ - char c, tmp[66]; - char *dig = digits; - - if (type & FLAGS_UPPERCASE) { - dig = upper_digits; - } - if (type & FLAGS_LEFT) { - type &= ~FLAGS_ZEROPAD; - } - if (base < 2 || base > 36) { - return 0; - } - - c = (type & FLAGS_ZEROPAD) ? '0' : ' '; - - // -------------------------------- - // Set the sign. - // -------------------------------- - char sign = 0; - if (type & FLAGS_SIGN) { - if (num < 0) { - sign = '-'; - num = -num; - size--; - } else if (type & FLAGS_PLUS) { - sign = '+'; - size--; - } else if (type & FLAGS_SPACE) { - sign = ' '; - size--; - } - } - // Sice I've removed the sign (if negative), i can transform it to unsigned. - uint32_t uns_num = (uint32_t)num; - - if (type & FLAGS_HASH) { - if (base == 16) { - size -= 2; - } else if (base == 8) { - size--; - } - } - - int32_t i = 0; - if (uns_num == 0) { - tmp[i++] = '0'; - } else { - while (uns_num != 0) { - tmp[i++] = dig[((unsigned long)uns_num) % (unsigned)base]; - uns_num = ((unsigned long)uns_num) / (unsigned)base; - } - } - - if (i > precision) { - precision = i; - } - size -= precision; - if (!(type & (FLAGS_ZEROPAD | FLAGS_LEFT))) { - while (size-- > 0) - *str++ = ' '; - } - if (sign) { - *str++ = sign; - } - - if (type & FLAGS_HASH) { - if (base == 8) - *str++ = '0'; - else if (base == 16) { - *str++ = '0'; - *str++ = digits[33]; - } - } - - if (!(type & FLAGS_LEFT)) { - while (size-- > 0) { - *str++ = c; - } - } - while (i < precision--) { - *str++ = '0'; - } - while (i-- > 0) { - *str++ = tmp[i]; - } - while (size-- > 0) { - *str++ = ' '; - } - - return str; -} - -static char *eaddr(char *str, unsigned char *addr, int size, int precision, - int type) -{ - (void)precision; - char tmp[24]; - char *dig = digits; - int i, len; - - if (type & FLAGS_UPPERCASE) { - dig = upper_digits; - } - - len = 0; - for (i = 0; i < 6; i++) { - if (i != 0) { - tmp[len++] = ':'; - } - tmp[len++] = dig[addr[i] >> 4]; - tmp[len++] = dig[addr[i] & 0x0F]; - } - - if (!(type & FLAGS_LEFT)) { - while (len < size--) { - *str++ = ' '; - } - } - - for (i = 0; i < len; ++i) { - *str++ = tmp[i]; - } - - while (len < size--) { - *str++ = ' '; - } - - return str; -} - -static char *iaddr(char *str, unsigned char *addr, int size, int precision, - int type) -{ - (void)precision; - char tmp[24]; - int i, n, len; - - len = 0; - for (i = 0; i < 4; i++) { - if (i != 0) { - tmp[len++] = '.'; - } - n = addr[i]; - - if (n == 0) { - tmp[len++] = digits[0]; - } else { - if (n >= 100) { - tmp[len++] = digits[n / 100]; - n = n % 100; - tmp[len++] = digits[n / 10]; - n = n % 10; - } else if (n >= 10) { - tmp[len++] = digits[n / 10]; - n = n % 10; - } - - tmp[len++] = digits[n]; - } - } - - if (!(type & FLAGS_LEFT)) { - while (len < size--) { - *str++ = ' '; - } - } - - for (i = 0; i < len; ++i) { - *str++ = tmp[i]; - } - - while (len < size--) { - *str++ = ' '; - } - - return str; -} - -static void cfltcvt(double value, char *buffer, char fmt, int precision) -{ - int decpt, sign, exp, pos; - char *digits = NULL; - char cvtbuf[CVTBUFSIZE]; - int capexp = 0; - int magnitude; - - if (fmt == 'G' || fmt == 'E') { - capexp = 1; - fmt += 'a' - 'A'; - } - - if (fmt == 'g') { - digits = ecvtbuf(value, precision, &decpt, &sign, cvtbuf); - magnitude = decpt - 1; - if (magnitude < -4 || magnitude > precision - 1) { - fmt = 'e'; - precision -= 1; - } else { - fmt = 'f'; - precision -= decpt; - } - } - - if (fmt == 'e') { - digits = ecvtbuf(value, precision + 1, &decpt, &sign, cvtbuf); - - if (sign) { - *buffer++ = '-'; - } - *buffer++ = *digits; - if (precision > 0) { - *buffer++ = '.'; - } - memcpy(buffer, digits + 1, precision); - buffer += precision; - *buffer++ = capexp ? 'E' : 'e'; - - if (decpt == 0) { - if (value == 0.0) { - exp = 0; - } else { - exp = -1; - } - } else { - exp = decpt - 1; - } - - if (exp < 0) { - *buffer++ = '-'; - exp = -exp; - } else { - *buffer++ = '+'; - } - - buffer[2] = (char)((exp % 10) + '0'); - exp = exp / 10; - buffer[1] = (char)((exp % 10) + '0'); - exp = exp / 10; - buffer[0] = (char)((exp % 10) + '0'); - buffer += 3; - } else if (fmt == 'f') { - digits = fcvtbuf(value, precision, &decpt, &sign, cvtbuf); - if (sign) { - *buffer++ = '-'; - } - if (*digits) { - if (decpt <= 0) { - *buffer++ = '0'; - *buffer++ = '.'; - for (pos = 0; pos < -decpt; pos++) { - *buffer++ = '0'; - } - while (*digits) { - *buffer++ = *digits++; - } - } else { - pos = 0; - while (*digits) { - if (pos++ == decpt) { - *buffer++ = '.'; - } - *buffer++ = *digits++; - } - } - } else { - *buffer++ = '0'; - if (precision > 0) { - *buffer++ = '.'; - for (pos = 0; pos < precision; pos++) { - *buffer++ = '0'; - } - } - } - } - - *buffer = '\0'; -} - -static void forcdecpt(char *buffer) -{ - while (*buffer) { - if (*buffer == '.') { - return; - } - if (*buffer == 'e' || *buffer == 'E') { - break; - } - - buffer++; - } - - if (*buffer) { - long n = (long)strlen(buffer); - while (n > 0) { - buffer[n + 1] = buffer[n]; - n--; - } - *buffer = '.'; - } else { - *buffer++ = '.'; - *buffer = '\0'; - } -} - -static void cropzeros(char *buffer) -{ - char *stop; - - while (*buffer && *buffer != '.') { - buffer++; - } - - if (*buffer++) { - while (*buffer && *buffer != 'e' && *buffer != 'E') { - buffer++; - } - stop = buffer--; - while (*buffer == '0') { - buffer--; - } - if (*buffer == '.') { - buffer--; - } - while ((*++buffer = *stop++)) - ; - } -} - -static char *flt(char *str, double num, int size, int precision, char fmt, - int flags) -{ - char tmp[80]; - char c, sign; - int n, i; - - // Left align means no zero padding. - if (flags & FLAGS_LEFT) - flags &= ~FLAGS_ZEROPAD; - - // Determine padding and sign char. - c = (flags & FLAGS_ZEROPAD) ? '0' : ' '; - sign = 0; - if (flags & FLAGS_SIGN) { - if (num < 0.0) { - sign = '-'; - num = -num; - size--; - } else if (flags & FLAGS_PLUS) { - sign = '+'; - size--; - } else if (flags & FLAGS_SPACE) { - sign = ' '; - size--; - } - } - - // Compute the precision value. - if (precision < 0) { - // Default precision: 6. - precision = 6; - } else if (precision == 0 && fmt == 'g') { - // ANSI specified. - precision = 1; - } - - // Convert floating point number to text. - cfltcvt(num, tmp, fmt, precision); - - // '#' and precision == 0 means force a decimal point. - if ((flags & FLAGS_HASH) && precision == 0) { - forcdecpt(tmp); - } - - // 'g' format means crop zero unless '#' given. - if (fmt == 'g' && !(flags & FLAGS_HASH)) { - cropzeros(tmp); - } - - n = strlen(tmp); - - // Output number with alignment and padding. - size -= n; - if (!(flags & (FLAGS_ZEROPAD | FLAGS_LEFT))) { - while (size-- > 0) { - *str++ = ' '; - } - } - - if (sign) { - *str++ = sign; - } - - if (!(flags & FLAGS_LEFT)) { - while (size-- > 0) { - *str++ = c; - } - } - - for (i = 0; i < n; i++) { - *str++ = tmp[i]; - } - - while (size-- > 0) { - *str++ = ' '; - } - - return str; -} - -int vsprintf(char *str, const char *fmt, va_list args) -{ - int base; - char *tmp; - char *s; - - // Flags to number(). - int flags; - - // 'h', 'l', or 'L' for integer fields. - int qualifier; - - for (tmp = str; *fmt; fmt++) { - if (*fmt != '%') { - *tmp++ = *fmt; - - continue; - } - - // Process flags- - flags = 0; - repeat: - // This also skips first '%'. - fmt++; - switch (*fmt) { - case '-': - flags |= FLAGS_LEFT; - goto repeat; - case '+': - flags |= FLAGS_PLUS; - goto repeat; - case ' ': - flags |= FLAGS_SPACE; - goto repeat; - case '#': - flags |= FLAGS_HASH; - goto repeat; - case '0': - flags |= FLAGS_ZEROPAD; - goto repeat; - } - - // Get the width of the output field. - int32_t field_width; - field_width = -1; - - if (isdigit(*fmt)) { - field_width = skip_atoi(&fmt); - } else if (*fmt == '*') { - fmt++; - field_width = va_arg(args, int32_t); - if (field_width < 0) { - field_width = -field_width; - flags |= FLAGS_LEFT; - } - } - - /* Get the precision, thus the minimum number of digits for - * integers; max number of chars for from string. - */ - int32_t precision = -1; - if (*fmt == '.') { - ++fmt; - if (isdigit(*fmt)) { - precision = skip_atoi(&fmt); - } else if (*fmt == '*') { - ++fmt; - precision = va_arg(args, int); - } - if (precision < 0) { - precision = 0; - } - } - - // Get the conversion qualifier. - qualifier = -1; - if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { - qualifier = *fmt; - fmt++; - } - - // Default base. - base = 10; - - switch (*fmt) { - case 'c': - if (!(flags & FLAGS_LEFT)) { - while (--field_width > 0) { - *tmp++ = ' '; - } - } - *tmp++ = va_arg(args, char); - while (--field_width > 0) { - *tmp++ = ' '; - } - continue; - - case 's': - s = va_arg(args, char *); - if (!s) { - s = ""; - } - - int32_t len = (int32_t)strnlen(s, (uint32_t)precision); - if (!(flags & FLAGS_LEFT)) { - while (len < field_width--) { - *tmp++ = ' '; - } - } - - int32_t it; - for (it = 0; it < len; ++it) { - *tmp++ = *s++; - } - while (len < field_width--) { - *tmp++ = ' '; - } - continue; - - case 'p': - if (field_width == -1) { - field_width = 2 * sizeof(void *); - flags |= FLAGS_ZEROPAD; - } - tmp = number(tmp, (unsigned long)va_arg(args, void *), 16, - field_width, precision, flags); - continue; - - case 'n': - if (qualifier == 'l') { - long *ip = va_arg(args, long *); - *ip = (tmp - str); - } else { - int *ip = va_arg(args, int *); - *ip = (tmp - str); - } - continue; - - case 'A': - flags |= FLAGS_UPPERCASE; - break; - - case 'a': - if (qualifier == 'l') - tmp = eaddr(tmp, va_arg(args, unsigned char *), field_width, - precision, flags); - else - tmp = iaddr(tmp, va_arg(args, unsigned char *), field_width, - precision, flags); - continue; - - // Integer number formats - set up the flags and "break". - case 'o': - base = 8; - break; - - case 'X': - flags |= FLAGS_UPPERCASE; - break; - - case 'x': - base = 16; - break; - - case 'd': - case 'i': - flags |= FLAGS_SIGN; - - case 'u': - break; - case 'E': - case 'G': - case 'e': - case 'f': - case 'g': - tmp = flt(tmp, va_arg(args, double), field_width, precision, *fmt, - flags | FLAGS_SIGN); - continue; - default: - if (*fmt != '%') - *tmp++ = '%'; - if (*fmt) - *tmp++ = *fmt; - else - --fmt; - continue; - } - - if (flags & FLAGS_SIGN) { - long num; - if (qualifier == 'l') { - num = va_arg(args, long); - } else if (qualifier == 'h') { - num = va_arg(args, short); - } else { - num = va_arg(args, int); - } - tmp = number(tmp, num, base, field_width, precision, flags); - } else { - unsigned long num; - if (qualifier == 'l') { - num = va_arg(args, unsigned long); - } else if (qualifier == 'h') { - num = va_arg(args, unsigned short); - } else { - num = va_arg(args, unsigned int); - } - tmp = number(tmp, num, base, field_width, precision, flags); - } - } - - *tmp = '\0'; - return tmp - str; -} - -int sprintf(char *str, const char *fmt, ...) -{ - va_list args; - int n; - - va_start(args, fmt); - n = vsprintf(str, fmt, args); - va_end(args); - - return n; -} diff --git a/mentos/src/mem/buddysystem.c b/mentos/src/mem/buddysystem.c index ad3dac2..d1cd2b1 100644 --- a/mentos/src/mem/buddysystem.c +++ b/mentos/src/mem/buddysystem.c @@ -1,173 +1,417 @@ +/// MentOS, The Mentoring Operating system project /// @file buddysystem.c /// @brief Buddy System. -/// @date Apr 2019. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. -#include "buddysystem.h" -#include "debug.h" +/// Change the header. +#define __DEBUG_HEADER__ "[BUDDY ]" + +#include "mem/buddysystem.h" +#include "mem/paging.h" #include "assert.h" +#include "misc/debug.h" +#include "system/panic.h" + +/// @brief Cache level low limit after which allocation starts. +#define LOW_WATERMARK_LEVEL 10 +/// @brief Cache level high limit, above it deallocation happens. +#define HIGH_WATERMARK_LEVEL 70 +/// @brief Cache level midway limit. +#define MID_WATERMARK_LEVEL ((LOW_WATERMARK_LEVEL + HIGH_WATERMARK_LEVEL) / 2) + +/// @brief Bitwise flags for identifying page types and statuses. +enum bb_flag { + FREE_PAGE = 0, ///< Bit position that identifies when a page is free or not. + ROOT_PAGE = 1 ///< Bit position that identifies when a page is the root page. +}; + +/// @brief Sets the given flag in the page. +/// @param page The page of which we want to modify the flag. +/// @param flag The flag we want to set. +static inline void __bb_set_flag(bb_page_t *page, int flag) +{ + set_bit(flag, &page->flags); +} + +/// @brief Clears the given flag from the page. +/// @param page The page of which we want to modify the flag. +/// @param flag The flag we want to clear. +static inline void __bb_clear_flag(bb_page_t *page, int flag) +{ + clear_bit(flag, &page->flags); +} + +/// @brief Gets the given flag from the page. +/// @param page The page of which we want to modify the flag. +/// @param flag The flag we want to test. +/// @return 1 if the bit is set, 0 otherwise. +static inline int __bb_test_flag(bb_page_t *page, int flag) +{ + return test_bit(flag, &page->flags); +} + +/// @brief Returns the page at the given index, starting from the given base. +/// @param instance The buddy system instance we are working with. +/// @param base The base page from which we move at the given index. +/// @param index The number of pages we want to move from the base. +/// @return The page we found. +static inline bb_page_t *__get_page_from_base(bb_instance_t *instance, bb_page_t *base, unsigned int index) +{ + return (bb_page_t *)(((uint32_t)base) + instance->pgs_size * index); +} + +/// @brief Returns the page at the given index, starting from the first page of the BB system. +/// @param instance The buddy system instance we are working with. +/// @param index The number of pages we want to move from the first page. +/// @return The page we found. +static inline bb_page_t *__get_page_at_index(bb_instance_t *instance, unsigned int index) +{ + return __get_page_from_base(instance, instance->base_page, index); +} + +/// @brief Computes the number of pages separating the two pages (begin, end). +/// @param instance The buddy system instance we are working with. +/// @param begin The first page. +/// @param end The second page. +/// @return The number of pages between begin and end. +static inline unsigned int __get_page_range(bb_instance_t *instance, bb_page_t *begin, bb_page_t *end) +{ + return (((uintptr_t)end) - ((uintptr_t)begin)) / instance->pgs_size; +} /// @brief Get the buddy index of a page. +/// @details +/// ----------------------- xor ----------------------- +/// | page_idx ^ (1UL << order) = buddy_idx | +/// | 1 1 0 | +/// | 0 1 1 | +/// --------------------------------------------------- +/// If the bit of page_idx that corresponds to the block +/// size, is 1, then we have to take the block on the +/// left (0), otherwise we have to take the block on the right (1). /// @param page_idx A page index. /// @param order The logarithm of the size of the block. /// @return The page index of the buddy of page. -static unsigned long get_buddy_idx(unsigned long page_idx, unsigned int order) +static inline unsigned long __get_buddy_at_index(unsigned long page_idx, unsigned int order) { - /* Get the index of the buddy block. - * - * ----------------------- xor ----------------------- - * | page_idx ^ (1UL << order) = buddy_idx | - * | 1 1 0 | - * | 0 1 1 | - * --------------------------------------------------- - * - * If the bit of page_idx that corresponds to the block - * size, is 1, then we have to take the block on the - * left (0), otherwise we have to take the block on the right (1). - */ - unsigned long buddy_idx = page_idx ^ (1UL << order); - - return buddy_idx; + return (page_idx ^ (1UL << order)); } - -page_t *bb_alloc_pages(zone_t *zone, unsigned int order) +static inline bool_t __page_is_buddy(bb_page_t *page, unsigned int order) { - page_t *page = NULL; - free_area_t *area = NULL; + return __bb_test_flag(page, FREE_PAGE) && (page->order == order); +} - // Search for a free_area_t with at least one available block of pages. - unsigned int current_order; - for (current_order = order; current_order < MAX_ORDER; ++current_order) { - // get the free_area_t at index 'current_order' - area = // ... +bb_page_t *bb_alloc_pages(bb_instance_t *instance, unsigned int order) +{ + bb_page_t *page = NULL; + bb_free_area_t *area = NULL; - // check if area is not empty (is there at least a block here?) - if (!list_head_empty(&/*...*/)) { - goto block_found; - } - } + // Cyclic search through each list for an available block, + // starting with the list for the requested order and + // continuing if necessary to larger orders. + unsigned int current_order; + for (current_order = order; current_order < MAX_BUDDYSYSTEM_GFP_ORDER; ++current_order) { + // Get the free_area_t at index 'current_order'. + area = /* ... */; + // Check if area is not empty, which means that there is at least a block inside the list. + if (!list_head_empty(&/* ... */)) { + goto block_found; + } + } - // No suitable free block has been found. - return NULL; + // No suitable free block has been found. + return NULL; block_found: - // Get a block of pages from the found free_area_t. - // Here we have to manage pages. Recall, free_area_t collects the first - // page_t of each free block of 2^order contiguous page frames. + // Get a block of pages from the found free_area_t. Here we have to manage + // pages. Recall, free_area_t collects the first page_t of each free block + // of 2^order contiguous page frames. + page = list_entry(/* ... */, /* ... */, /* ... */); - page = list_entry(/*...*/); + // Remove the descriptor of its first page frame. + list_head_del(&/* ... */); - // Remove page from the list_head in the found free_area_t. - list_head_del(/*...*/); + // Set the page as allocated, thus, remove the flag FREE_PAGE. + __bb_clear_flag(/* ... */, /* ... */); - // Set page as taken. - page->_count = // ... - page->private = 0; + // Check that the page is a ROOT_PAGE + assert(__bb_test_flag(/* ... */, /* ... */)); - // Decrease the number of free blocks in the found free_area_t. - // ... + // Decrease the number of free block of the free_area_t. + /* ... */ -= 1; - /* We found a block with 2^k page frames to satisfy a request - * of 2^h page frames. If h < k, then we can split the block with 2^k - * pages until it is large 2^h pages, namely k == h. - */ + // Found a block of 2^k page frames, to satisfy a request + // for 2^h page frames (h < k) the program allocates + // the first 2^h page frames and iteratively reassigns + // the last 2^k – 2^h page frames to the free_area lists + // that have indexes between h and k. + unsigned int size = 1UL << current_order; + while (current_order > order) { + // At each loop, we have to set free the right half of the found block. - // We can exploit size(=2^k) to have at each loop the address the page that - // resides in the midle of the found block. - unsigned int size = 1 << current_order; - while (current_order > order) { + // Refer to the lower free_area_t and order. + area--; + current_order--; - // At each loop, we have to set free the right half of the found block. + // Split the block and set free the second half. + size >>= /* ... */; - // Split the block size in half - size = // ... + // Get the address of the page in the midle of the found block. + bb_page_t *buddy = __get_page_from_base(/* ... */, /* ... */, /* ... */); - // get the address of the page in the midle of the found block. - page_t *buddy = // ... + // Check that the buddy is free and not a root page. + assert(/* ... */ &&!/* ... */); - // set the order of pages after the buddy page_t (the field 'private') - // ... + // Insert buddy as first element in the list of available blocks (free_list). + list_head_add(&/* ... */.siblings, &/* ... */); - // get the free_area_t collecting blocks with 2^(k-1) page frames - area = // ... + // Increase the number of free block of the free_area_t. + /* ... */ += 1; - // add the buddy block in its list of available blocks - // ... + // Save the order of the buddy. + /* ... */ = current_order; - // Increase the number of free blocks of the free_area_t. - // ... - } + // Set the buddy as a root page. + /* ... */; + } - buddy_system_dump(zone); + // Set the order of the page we are returning. + /* ... */; - return page; + // Set the page as root page. + /* ... */; + + // Clear the free-page status from the page. + /* ... */; + +#if 0 + buddy_system_dump(instance); +#endif + return page; } -void bb_free_pages(zone_t *zone, page_t *page, unsigned int order) +void bb_free_pages(bb_instance_t *instance, bb_page_t *page) { - // Take the first page descriptor of the zone. - page_t *base = zone->zone_mem_map; + // Take the first page descriptor of the zone. + bb_page_t *base = instance->base_page; + // Take the page frame index of page compared to the zone. + unsigned long page_idx = __get_page_range(instance, base, page); + // Set the page freed, but do not set the private + // field because we want to try to merge. FIXME: Add this line on zone page deallocate! + // set_page_count(page, -1); - // Take the page frame index of page compared to the zone. - unsigned long page_idx = page - base; + unsigned int order = page->order; - // Set the given page as free - page->_count = // ... + // Check that the page is free, or that it is not a root page. + if (/* ... */ || !/* ... */) { + kernel_panic("Double deallocation in buddy system!"); + } - // At each loop, check if the buddy block can be merged with page. - while (order < MAX_ORDER - 1) { - // Get the index of the buddy block of page. - unsigned long buddy_idx = get_buddy_idx(page_idx, order); - // Get the page_t of the buddy block, namely its first page frame. - page_t *buddy = base + buddy_idx; + // Performs a cycle that starts with the smallest + // order block and moves up to the top order. + while (order < MAX_BUDDYSYSTEM_GFP_ORDER - 1) { + // Get the base page index. + page = __get_page_from_base(instance, base, page_idx); - // If the buddy is free and it has the same size of page, then - // they can be merged. Otherwise, we can stop the while-loop and insert - // page in the list of free blocks. + // Get the index of the buddy. + unsigned long buddy_idx = __get_buddy_at_index(page_idx, order); - if (!(/*...it is free...*/ && /*...they have the same size...*/)) { - break; - } + // Return the page descriptor of the buddy. + bb_page_t *buddy = __get_page_from_base(instance, base, buddy_idx); - // we are here only if buddy is free and can be merged with page. + // If the page is not a buddy, stop the loop. So it should not be free + // and having the same size. + if (!(/* ... */ &&/* ... */)) + break; - // remove buddy from the list of available blocks in its free_area_t - // .... + // If buddy is free, remove buddy from the current free list, + // because then the coalesced block will be inserted on a + // upper order. + /* ... */; - // Decrease the number of free block of the current free_area_t. - // ... + // Decrease the number of free block of the current free_area_t. + /* ... */; - // buddy no longer represents a free block, so clear the private field. - buddy->private = 0; + // Get the page that gets forgotten, it's always the one on the right (the greatest) + bb_page_t *forgot_page = buddy > page ? buddy : page; + + // Clear the root flag from the forgotten page. + /* ... */; - // Update the page index with the index of the coalesced block. - // ... + // Set the forgotten page as a free page. + /* ... */; - order++; - } + // Update the page index with the index of the coalesced block. + /* ... */ &= /* ... */; - // The coalesced block is the result of the merging procedure. - page_t *coalesced = base + page_idx; + order++; + } - // Update the field private to set the size. - coalesced->private = // ... + // Take the coalesced block with the order reached up. + bb_page_t *coalesced = __get_page_from_base(instance, base, page_idx); - // Insert the coalesced block in the free_area as available block - // ... + // Update the order of the coalesced page. + /* ... */; + + // Set it to be a root page and a free page + /* ... */; + /* ... */; - // Increase the number of free blocks of the free_area. - // ... + // Insert coalesced as first element in the free list. + list_head_add(&/* ... */, &/* ... */); + + // Increase the number of free block of the free_area_t. + /* ... */; - buddy_system_dump(zone); +#if 0 + buddy_system_dump(instance); +#endif } -void buddy_system_dump(zone_t *zone) +void buddy_system_init(bb_instance_t *instance, + const char *name, + void *pages_start, + uint32_t bbpage_offset, + uint32_t pages_stride, + uint32_t pages_count) { - // Print free_list's size of each area of the zone. - dbg_print("Zone\t%s\t", zone->name); - for (int order = 0; order < MAX_ORDER; order++) { - free_area_t *area = zone->free_area + order; - dbg_print("%d\t", area->nr_free); - } - dbg_print("\n"); + // Compute the base bb page of the struct + instance->base_page = ((bb_page_t *)(((uint32_t)pages_start) + bbpage_offset)); + + // Save all needed page info + instance->bbpg_offset = bbpage_offset; + instance->pgs_size = pages_stride; + instance->size = pages_count; + instance->name = name; + + // Initialize all pages + for (uint32_t i = 0; i < pages_count; i++) { + bb_page_t *page = __get_page_at_index(instance, i); + page->flags = 0; + // Mark page as free + __bb_set_flag(page, FREE_PAGE); + // Initialize siblings list + list_head_init(&(page->location.siblings)); + } + + // Initialize the free_lists of each area of the zone. + for (unsigned int order = 0; order < MAX_BUDDYSYSTEM_GFP_ORDER; order++) { + bb_free_area_t *area = instance->free_area + order; + area->nr_free = 0; + list_head_init(&area->free_list); + } + + // Current base page descriptor of the zone. + bb_page_t *page = instance->base_page; + // Address of the last page descriptor of the zone. + bb_page_t *last_page = __get_page_from_base(instance, page, instance->size); + + // Get the free area collecting the larges block of page frames. + const unsigned int order = MAX_BUDDYSYSTEM_GFP_ORDER - 1; + bb_free_area_t *area = instance->free_area + order; + + // Add all zone's pages to the largest free area block. + uint32_t block_size = 1UL << order; + while ((page + block_size) <= last_page) { + // Save the order of the page. + page->order = order; + // Set the page as root + __bb_set_flag(page, ROOT_PAGE); + + // Insert page as first element in the list. + list_head_add_tail(&page->location.siblings, &area->free_list); + // Increase the number of free block of the free_area_t. + area->nr_free++; + + page = __get_page_from_base(instance, page, block_size); + } + + assert(page == last_page && + "Memory size is not aligned to MAX_ORDER size!"); +} + +void buddy_system_dump(bb_instance_t *instance) +{ + // Print free_list's size of each area of the zone. + pr_debug("Zone %-12s ", instance->name); + for (int order = 0; order < MAX_BUDDYSYSTEM_GFP_ORDER; order++) { + bb_free_area_t *area = instance->free_area + order; + pr_debug("%2d ", area->nr_free); + } + pr_debug(": %s\n", to_human_size(buddy_system_get_free_space(instance))); +} + +unsigned long buddy_system_get_total_space(bb_instance_t *instance) +{ + return instance->size * PAGE_SIZE; +} + +unsigned long buddy_system_get_free_space(bb_instance_t *instance) +{ + unsigned int size = 0; + for (int order = 0; order < MAX_BUDDYSYSTEM_GFP_ORDER; ++order) + size += instance->free_area[order].nr_free * (1UL << order) * PAGE_SIZE; + return size; +} + +unsigned long buddy_system_get_cached_space(bb_instance_t *instance) +{ + unsigned int size = 0; + for (int order = 0; order < MAX_BUDDYSYSTEM_GFP_ORDER; ++order) + size += instance->free_pages_cache_size * PAGE_SIZE; + return size; +} + +static void __cache_extend(bb_instance_t *instance, int count) +{ + for (int i = 0; i < count; i++) { + bb_page_t *page = bb_alloc_pages(instance, 0); + list_head_add(&page->location.cache, &instance->free_pages_cache_list); + instance->free_pages_cache_size++; + } +} + +static void __cache_shrink(bb_instance_t *instance, int count) +{ + for (int i = 0; i < count; i++) { + list_head *page_list = list_head_pop(&instance->free_pages_cache_list); + bb_page_t *page = list_entry(page_list, bb_page_t, location.cache); + bb_free_pages(instance, page); + instance->free_pages_cache_size--; + } +} + +static bb_page_t *__cached_alloc(bb_instance_t *instance) +{ + if (instance->free_pages_cache_size < LOW_WATERMARK_LEVEL) { + // Request pages from the buddy system + uint32_t pages_to_request = MID_WATERMARK_LEVEL - instance->free_pages_cache_size; + __cache_extend(instance, pages_to_request); + } + list_head *page_list = list_head_pop(&instance->free_pages_cache_list); + bb_page_t *page = list_entry(page_list, bb_page_t, location.cache); + return page; +} + +static void __cached_free(bb_instance_t *instance, bb_page_t *page) +{ + list_head_add(&page->location.cache, &instance->free_pages_cache_list); + + if (instance->free_pages_cache_size > HIGH_WATERMARK_LEVEL) { + // Free pages to the buddy system + uint32_t pages_to_free = instance->free_pages_cache_size - MID_WATERMARK_LEVEL; + __cache_shrink(instance, pages_to_free); + } +} + +bb_page_t *bb_alloc_page_cached(bb_instance_t *instance) +{ + return __cached_alloc(instance); +} + +void bb_free_page_cached(bb_instance_t *instance, bb_page_t *page) +{ + __cached_free(instance, page); } diff --git a/mentos/src/mem/kheap.c b/mentos/src/mem/kheap.c index 244e770..6f9ffe0 100644 --- a/mentos/src/mem/kheap.c +++ b/mentos/src/mem/kheap.c @@ -1,93 +1,77 @@ /// MentOS, The Mentoring Operating system project /// @file kheap.c /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "kheap.h" +/// Change the header. +#define __DEBUG_HEADER__ "[KHEAP ]" + +#include "mem/kheap.h" #include "math.h" -#include "debug.h" +#include "misc/debug.h" #include "string.h" -#include "paging.h" +#include "mem/paging.h" #include "assert.h" +#include "klib/list_head.h" -#define PAGE_SIZE 4096 +/// Overhead given by the block_t itself. +#define OVERHEAD sizeof(block_t) +/// Align the given address. +#define ADDR_ALIGN(addr) ((((uint32_t)(addr)) & 0xFFFFF000) + 0x1000) +/// Checks if the given address is aligned. +#define IS_ALIGN(addr) ((((uint32_t)(addr)) & 0x00000FFF) == 0) +/// Returns a rounded up, away from zero, to the nearest multiple of b. +#define CEIL(NUMBER, BASE) (((NUMBER) + (BASE)-1) & ~((BASE)-1)) +/// User heap initial size ( 1 Megabyte). +#define UHEAP_INITIAL_SIZE (1 * M) -// 16 byte. -// #define OVERHEAD (sizeof(unsigned int) + sizeof(block_t)) -#define OVERHEAD sizeof(block_t) -#define ADDR_ALIGN(addr) ((((uint32_t)(addr)) & 0xFFFFF000) + 0x1000) -#define IS_ALIGN(addr) ((((uint32_t)(addr)) & 0x00000FFF) == 0) +/// @brief Identifies a block of memory. +typedef struct block_t { + /// @brief Identifies the side of the block and also if it is free or allocated. + /// @details + /// | 31 bit | 1 bit | + /// | first bits of real size | free/alloc | + /// To calculate the real size, set to zero the last bit + unsigned int size; + /// Pointer to the next free block. + struct block_t *nextfree; + /// Pointer to the next block. + struct block_t *next; +} block_t; +/// Kernel heap section. +static vm_area_struct_t kernel_heap; +/// Top of the kernel heap. +static uint32_t kernel_heap_top; -//============= Heap memory descriptor ========================================= -// Kernel heap section. -static struct vm_area_struct kernel_heap; -// Top of heap. -static uint32_t kernel_heap_curr; -static bool_t kheap_enabled = false; -//============================================================================== - -static void *__do_brk(uint32_t *pointer_heap_curr, - struct vm_area_struct *heap, - int increment) -{ - assert(pointer_heap_curr && "Pointer to heap curr is NULL."); - assert(heap && "Heap do not exist."); - - uint32_t heap_curr = *pointer_heap_curr; - uint32_t old_heap_curr = heap_curr; - - uint32_t heap_end = heap->vm_end; - -// dbg_print("BRK> %s: heap_start: %p, free space: %d\n", -// (heap == &kernel_heap)? "KERNEL" : "USER", -// heap->vm_start, heap_end - heap_curr); - - if (increment > 0) - { - uint32_t new_boundary = heap_curr + increment; - /* If new size is smaller or equal to end, simply add size to - * kernel_heap_curr and return the old kernel_heap_curr. - */ - if (new_boundary <= heap_end) - { - *pointer_heap_curr = new_boundary; - - return (void *) old_heap_curr; - } - } - - return NULL; -} - -/// @brief Given the field size in a Block(which contain free/alloc -/// information), extract the size. +/// @brief Given the field size in a Block(which contain free/alloc +/// information), extract the size. /// @param size /// @return -static inline uint32_t get_real_size(uint32_t size) +static inline uint32_t blkmngr_get_real_size(uint32_t size) { return (size >> 1U) << 1U; } /// @brief Sets the free/alloc bit of the size field. -static inline void blkmngr_set_free(uint32_t *size, bool_t x) +static inline void blkmngr_set_free(uint32_t *size, int x) { - (*size) = (x) ? ((*size) | 1U) - : ((*size) & 0xFFFFFFFE); + (*size) = (x) ? ((*size) | 1U) : ((*size) & 0xFFFFFFFE); } /// @brief Checks if a block is freed or allocated. -static inline bool_t blkmngr_is_free(block_t *block) +static inline int blkmngr_is_free(block_t *block) { - if (block == NULL) return false; + if (block == NULL) + return 0; return (block->size & 1U); } /* * /// @brief Checks if it is the end block. - * static inline bool_t blkmngr_is_end(block_t * block) + * static inline int blkmngr_is_end(block_t * block) * { * assert(block && "Received null block."); * assert(tail && "Tail has not set."); @@ -100,11 +84,11 @@ static inline bool_t blkmngr_is_free(block_t *block) /// @param block The given block. /// @param size The size to check /// @return -static inline bool_t blkmngr_does_it_fit(block_t *block, uint32_t size) +static inline int blkmngr_does_it_fit(block_t *block, uint32_t size) { assert(block && "Received null block."); - return (block->size >= get_real_size(size)) && blkmngr_is_free(block); + return (block->size >= blkmngr_get_real_size(size)) && blkmngr_is_free(block); } /// @brief Removes the block from freelist. @@ -113,20 +97,16 @@ static inline void blkmngr_remove_from_freelist(block_t *block, uint32_t *freeli assert(block && "Received null block."); assert(freelist && "Freelist is a null pointer."); - block_t *first_free_block = (block_t *) *freelist; + block_t *first_free_block = (block_t *)*freelist; assert(first_free_block && "Freelist is empty."); - if (block == first_free_block) - { - *freelist = (uint32_t) block->nextfree; - } - else - { + if (block == first_free_block) { + *freelist = (uint32_t)block->nextfree; + } else { block_t *prev = first_free_block; while (prev != NULL && prev->nextfree != block) prev = prev->nextfree; - if (prev) - { + if (prev) { prev->nextfree = block->nextfree; } } @@ -139,30 +119,26 @@ static inline void blkmngr_add_to_freelist(block_t *block, uint32_t *freelist) { assert(block && "Received null block."); assert(freelist && "Freelist is a null pointer."); - block_t * first_free_block = (block_t *) *freelist; - block->nextfree = first_free_block; - *freelist = (uint32_t) block; + block_t *first_free_block = (block_t *)*freelist; + block->nextfree = first_free_block; + *freelist = (uint32_t)block; } /// @brief Find the best fitting block in the memory pool. static inline block_t *blkmngr_find_best_fitting(uint32_t size, uint32_t *freelist) { assert(freelist && "Freelist is a null pointer."); - block_t *first_free_block = (block_t *) *freelist; + block_t *first_free_block = (block_t *)*freelist; - if (first_free_block == NULL) - { + if (first_free_block == NULL) { return NULL; } block_t *best_fitting = NULL; - for (block_t * current = first_free_block; current; current = current->nextfree) - { - if (!blkmngr_does_it_fit(current, size)) - { + for (block_t *current = first_free_block; current; current = current->nextfree) { + if (!blkmngr_does_it_fit(current, size)) { continue; } - if ((best_fitting == NULL) || (current->size < best_fitting->size)) - { + if ((best_fitting == NULL) || (current->size < best_fitting->size)) { best_fitting = current; } } @@ -175,16 +151,16 @@ static inline block_t *blkmngr_get_previous_block(block_t *block, uint32_t *head assert(block && "Received null block."); assert(head && "The head of the list is not set."); - block_t *head_block = (block_t *) *head; + block_t *head_block = (block_t *)*head; assert(head_block && "The head of the list is not set."); - if (block == head_block) - { + if (block == head_block) { return NULL; } block_t *prev = head_block; - while (prev->next != block) - { + + // FIXME: Sometimes enters infinite loop! + while (prev->next != block) { prev = prev->next; } @@ -197,21 +173,87 @@ static inline block_t *blkmngr_get_next_block(block_t *block, uint32_t *tail) assert(block && "Received null block."); assert(tail && "The tail of the list is not set."); - block_t *tail_block = (block_t *) *tail; + block_t *tail_block = (block_t *)*tail; assert(tail_block && "The head of the list is not set."); - if (block == tail_block) - { + if (block == tail_block) { return NULL; } return block->next; } -/// @brief Allocates size bytes of uninitialized storage. -static void *__do_malloc(struct vm_area_struct *heap, size_t size) +/// @brief Find the current user heap. +/// @return The heap structure if heap exists, otherwise NULL. +static vm_area_struct_t *__find_user_heap() { - if (size == 0) return NULL; + // Get the memory descriptor of the current process. + task_struct *current_task = scheduler_get_current_process(); + if (current_task == NULL) { + pr_emerg("There is no current task!\n"); + return NULL; + } + mm_struct_t *current_mm = current_task->mm; + if (current_mm == NULL) { + pr_emerg("The mm_struct of the current task is not initialized!\n"); + return NULL; + } + // Get the starting address of the heap. + uint32_t start_heap = current_mm->start_brk; + // If not set return NULL. + if (start_heap == 0) { + return NULL; + } + // Otherwise find the respective heap segment. + vm_area_struct_t *segment = NULL; + list_for_each_decl(it, ¤t_mm->mmap_list) + { + segment = list_entry(it, vm_area_struct_t, vm_list); + if (segment->vm_start == start_heap) { + return segment; + } + } + return NULL; +} + +/// @brief Extends the provided heap of the given increment. +/// @param heap_top Current top of the heap. +/// @param heap Pointer to the heap. +/// @param increment Increment to the heap. +/// @return Pointer to the old top of the heap, ready to be used. +static void *__do_brk(uint32_t *heap_top, vm_area_struct_t *heap, int increment) +{ + assert(heap_top && "Pointer to the current top of the heap is NULL."); + assert(heap && "Pointer to the heap is NULL."); + // pr_default("BRK> %s: heap_start: %p, free space: %d\n", + // (heap == &kernel_heap)? "KERNEL" : "USER", + // heap->vm_start, heap_end - heap_curr); + if (increment > 0) { + // Compute the new boundary. + uint32_t new_boundary = *heap_top + increment; + // If new boundary is smaller or equal to end, simply + // update the heap_top to the new boundary and return + // the old heap_top. + if (new_boundary <= heap->vm_end) { + // Save the old top of the heap. + uint32_t old_heap_top = *heap_top; + // Overwrite the top of the heap. + *heap_top = new_boundary; + // Return the old top of the heap. + return (void *)old_heap_top; + } + } + return NULL; +} + +/// @brief Allocates size bytes of uninitialized storage. +/// @param heap Heap from which we get the unallocated memory. +/// @param size Size of the desired memory area. +/// @return Pointer to the allocated memory area. +static void *__do_malloc(vm_area_struct_t *heap, size_t size) +{ + if (size == 0) + return NULL; // Get: // 1) First memory block. @@ -222,18 +264,18 @@ static void *__do_malloc(struct vm_area_struct *heap, size_t size) // block_t *freelist = NULL; // We will use these in writing. - uint32_t *head = (uint32_t *) (heap->vm_start); - uint32_t *tail = (uint32_t *) (heap->vm_start + sizeof(block_t *)); - uint32_t *freelist = (uint32_t *) (heap->vm_start + 2 * sizeof(block_t *)); + uint32_t *head = (uint32_t *)(heap->vm_start); + uint32_t *tail = (uint32_t *)(heap->vm_start + sizeof(block_t *)); + uint32_t *freelist = (uint32_t *)(heap->vm_start + 2 * sizeof(block_t *)); // assert(head && tail && freelist && "Heap block lists point to null."); // We will use these others in reading. - block_t *head_block = (block_t *) *head; - block_t *tail_block = (block_t *) *tail; + block_t *head_block = (block_t *)*head; + block_t *tail_block = (block_t *)*tail; // block_t *first_free_block = (block_t *) *freelist; // Calculate real size that's used, round it to multiple of 16. - uint32_t rounded_size = ceil(size, 16); + uint32_t rounded_size = CEIL(size, 16); // The block size takes into account also the block_t overhead. uint32_t block_size = rounded_size + OVERHEAD; @@ -241,39 +283,33 @@ static void *__do_malloc(struct vm_area_struct *heap, size_t size) // best-fit node when there is more than one such node in tree. block_t *best_fitting = blkmngr_find_best_fitting(rounded_size, freelist); - if (best_fitting != NULL) - { + if (best_fitting != NULL) { // and! put a SIZE to the last four byte of the chunk - void *block_ptr = (void *) best_fitting; + char *block_ptr = (void *)best_fitting; // Store a pointer to the next block. void *stored_next_block = blkmngr_get_next_block(best_fitting, tail); // Get the size of the chunk. - uint32_t chunk_size = get_real_size(best_fitting->size) + OVERHEAD; + uint32_t chunk_size = blkmngr_get_real_size(best_fitting->size) + OVERHEAD; // Get what's left. uint32_t remaining_size = chunk_size - block_size; // Get the real size. - uint32_t real_size = (remaining_size < (8 + OVERHEAD)) ? chunk_size - : block_size; + uint32_t real_size = (remaining_size < (8 + OVERHEAD)) ? chunk_size : block_size; // Set the size of the best fitting block. best_fitting->size = real_size - OVERHEAD; // Set the content of the block as free. - blkmngr_set_free(&(best_fitting->size), false); + blkmngr_set_free(&(best_fitting->size), 0); // Store the base pointer. void *base_ptr = block_ptr; - block_ptr = (void *) (block_ptr + real_size); + block_ptr = (char *)block_ptr + real_size; - if (remaining_size < (8 + OVERHEAD)) - { + if (remaining_size < (8 + OVERHEAD)) { goto no_split; - } - else if (remaining_size >= (8 + OVERHEAD)) - { - if (blkmngr_is_free(stored_next_block)) - { + } else if (remaining_size >= (8 + OVERHEAD)) { + if (blkmngr_is_free(stored_next_block)) { // Choice b) merge! // Gather info about next block - void *nextblock = stored_next_block; + void *nextblock = stored_next_block; block_t *n_nextblock = nextblock; /* Remove next from list because it no longer exists(just * unlink it) @@ -281,78 +317,66 @@ static void *__do_malloc(struct vm_area_struct *heap, size_t size) blkmngr_remove_from_freelist(n_nextblock, freelist); // Merge! - block_t *t = block_ptr; - t->size = remaining_size + get_real_size(n_nextblock->size); - blkmngr_set_free(&(t->size), true); + block_t *t = (block_t *)block_ptr; + t->size = remaining_size + blkmngr_get_real_size(n_nextblock->size); + blkmngr_set_free(&(t->size), 1); t->next = blkmngr_get_next_block(stored_next_block, tail); - if (nextblock == tail_block) - { + if (nextblock == tail_block) { // I don't want to set it to tail now, instead, reclaim it - *tail = (uint32_t) t; - // int reclaimSize = get_real_size(t->size) + OVERHEAD; + *tail = (uint32_t)t; + // int reclaimSize = blkmngr_get_real_size(t->size) + OVERHEAD; // ksbrk(-reclaimSize); // goto no_split; } // then add merged one into the front of the list blkmngr_add_to_freelist(t, freelist); - } - else - { + } else { // Choice a) seperate! - block_t *putThisBack = block_ptr; - putThisBack->size = remaining_size - OVERHEAD; - blkmngr_set_free(&(putThisBack->size), true); + block_t *putThisBack = (block_t *)block_ptr; + putThisBack->size = remaining_size - OVERHEAD; + blkmngr_set_free(&(putThisBack->size), 1); putThisBack->next = stored_next_block; - if (base_ptr == tail_block) - { - *tail = (uint32_t) putThisBack; - // int reclaimSize = get_real_size(putThisBack->size) +OVERHEAD; + if (base_ptr == tail_block) { + *tail = (uint32_t)putThisBack; + // int reclaimSize = blkmngr_get_real_size(putThisBack->size) +OVERHEAD; // ksbrk(-reclaimSize); // goto no_split; } blkmngr_add_to_freelist(putThisBack, freelist); } - ((block_t *) base_ptr)->next = block_ptr; + ((block_t *)base_ptr)->next = (block_t *)block_ptr; } - no_split: + no_split: // Remove the block from the free list. blkmngr_remove_from_freelist(base_ptr, freelist); - return base_ptr + sizeof(block_t); - } - else - { + return (char *)base_ptr + sizeof(block_t); + } else { uint32_t realsize = block_size; block_t *ret; - if (heap == &kernel_heap) - { + if (heap == &kernel_heap) { ret = ksbrk(realsize); - } - else - { + } else { ret = usbrk(realsize); } assert(ret != NULL && "Heap is running out of space\n"); - if (!head_block) - { - *head = (uint32_t) ret; - } - else - { + if (!head_block) { + *head = (uint32_t)ret; + } else { tail_block->next = ret; } - ret->next = NULL; + ret->next = NULL; ret->nextfree = NULL; - *tail = (uint32_t) ret; + *tail = (uint32_t)ret; void *save = ret; @@ -361,282 +385,284 @@ static void *__do_malloc(struct vm_area_struct *heap, size_t size) */ ret->size = block_size - OVERHEAD; blkmngr_set_free(&(ret->size), - false); + 0); // Set the block allocated. // ptr = ptr + block_size - sizeof(uint32_t); // trailing_space = ptr; // *trailing_space = ret->size; // Now, return it! - return save + sizeof(block_t); - } -} - -/// @brief Allocates size bytes of uninitialized storage with block align. -static void *__do_malloc_align(struct vm_area_struct *heap, uint32_t size) -{ - if (size == 0) return NULL; - - // Get: - // 1) First memory block. - // static block_t *head = NULL; - // 2) Last memory block. - // static block_t *tail = NULL; - // 3) All the memory blocks that are freed. - // static block_t *freelist = NULL; - - // We will use these in writing. - uint32_t *head = (uint32_t *) (heap->vm_start); - uint32_t *tail = (uint32_t *) (heap->vm_start + sizeof(block_t *)); - uint32_t *freelist = (uint32_t *) (heap->vm_start + 2 * sizeof(block_t *)); - assert(head && tail && freelist && "Heap block lists point to null."); - - // We will use these others in reading. - block_t *head_block = (block_t *) *head; - block_t *tail_block = (block_t *) *tail; - // block_t *first_free_block = (block_t *) *freelist; - - // Calculate real size that's used, round it to multiple of 16. - uint32_t rounded_size = ceil(size, 16); - - /* Find bestfit in avl tree. This bestfit function will remove - * thebest-fit node when there is more than one such node in tree. - */ - block_t *best_fitting = blkmngr_find_best_fitting(rounded_size, freelist); - if (best_fitting != NULL && (IS_ALIGN(best_fitting + sizeof(block_t)))) - { - return kmalloc(size); - } - else - { - void *needed_addr = (void *) ADDR_ALIGN( - ((uint32_t) kernel_heap_curr + sizeof(block_t)) & 0xFFFFF000); - block_t *block_addr = needed_addr - sizeof(block_t); - - uint32_t realsize = - (uint32_t) block_addr - (uint32_t) (kernel_heap_curr) + OVERHEAD + - rounded_size; - block_t *ret; - if(heap == &kernel_heap) - { - ret = ksbrk(realsize); - } - else - { - ret = usbrk(realsize); - } - assert(ret != NULL && "Heap is running out of space\n"); - if (!head_block) - { - *head = (uint32_t) block_addr; - } - else - { - tail_block->next = block_addr; - } - - ret->next = NULL; - ret->nextfree = NULL; - *tail = (uint32_t) block_addr; - - /* After sbrk(), split the block into half [block_size | the rest], - * and put the rest into the tree. - */ - block_addr->size = rounded_size; - blkmngr_set_free(&(block_addr->size), - false); - // Set the block allocated. - // ptr = ptr + block_size - sizeof(uint32_t); - // trailing_space = ptr; - // *trailing_space = block_addr->size; - - // Now, return it! - return needed_addr; - } -} - -/// @brief Reallocates the given area of memory. It must be still allocated -/// and not yet freed with a call to free or realloc. -/// @param ptr -/// @param size -/// @return -static void *__do_realloc(struct vm_area_struct *heap, void *ptr, uint32_t size) -{ - // Get: - // 1) First memory block. - // static block_t *head = NULL; - // 2) Last memory block. - // static block_t *tail = NULL; - // 3) All the memory blocks that are freed. - // static block_t *freelist = NULL; - - // We will use these in writing. - uint32_t *head = (uint32_t *) (heap->vm_start); - uint32_t *tail = (uint32_t *) (heap->vm_start + sizeof(block_t *)); - uint32_t *freelist = (uint32_t *) (heap->vm_start + 2 * sizeof(block_t *)); - assert(head && tail && freelist && "Heap block lists point to null."); - - // We will use these others in reading. - block_t *head_block = (block_t *) *head; - block_t *tail_block = (block_t *) *tail; - // block_t *first_free_block = (block_t *) *freelist; - - uint32_t *trailing_space = NULL; - if (!ptr) - { - return kmalloc(size); - } - if (size == 0 && ptr != NULL) - { - kfree(ptr); - - return NULL; - } - uint32_t rounded_size = ceil(size, 16); - uint32_t block_size = rounded_size + OVERHEAD; - block_t *nextBlock; - block_t *prevBlock; - - /* Shrink or expand? - * - * Shrink: - * Now, we would just return the same address, later we may split this - * block. - * - * Expand: - * First, try if the actual size of the memory block is enough - * to hold the current size. - * Second, if not, try if merging the next block works. - * Third, if none of the above works, malloc another block, move all the - * data there, and then free the original block. - */ - block_t *nptr = ptr - sizeof(block_t); - nextBlock = blkmngr_get_next_block(nptr, tail); - prevBlock = blkmngr_get_previous_block(nptr, head); - if (nptr->size == size) - { - return ptr; - } - if (nptr->size < size) - { - // Expand, size of the block is just not enough. - if (tail_block != nptr && blkmngr_is_free(nextBlock) && - (get_real_size(nptr->size) + OVERHEAD + - get_real_size(nextBlock->size)) >= rounded_size) - { - // Merge with the next block, and return! - // Change size to curr's size + OVERHEAD + next's size. - blkmngr_remove_from_freelist(nextBlock, freelist); - nptr->size = get_real_size(nptr->size) + OVERHEAD + - get_real_size(nextBlock->size); - blkmngr_set_free(&(nptr->size), false); - trailing_space = - (void *) nptr + sizeof(block_t) + get_real_size(nptr->size); - *trailing_space = nptr->size; - if (tail_block == nextBlock) - { - // Set it to tail for now, or we can reclaim it. - *tail = (uint32_t) nptr; - } - return nptr + 1; - } - // Hey! Try merging with the previous block! - else if (head_block != nptr && blkmngr_is_free(prevBlock) && - (get_real_size(nptr->size) + OVERHEAD + - get_real_size(prevBlock->size)) >= rounded_size) - { - // db_print(); - uint32_t originalSize = get_real_size(nptr->size); - // Hey! one more thing to do , copy data over to new block. - blkmngr_remove_from_freelist(prevBlock, freelist); - prevBlock->size = - originalSize + OVERHEAD + get_real_size(prevBlock->size); - blkmngr_set_free(&(prevBlock->size), false); - trailing_space = (void *) prevBlock + sizeof(block_t) + - get_real_size(prevBlock->size); - *trailing_space = prevBlock->size; - if (tail_block == nptr) - { - *tail = (uint32_t) prevBlock; - } - memcpy(prevBlock + 1, ptr, originalSize); - - return prevBlock + 1; - } - - // Move to somewhere else. - void *newplace = kmalloc(size); - // Copy data over. - memcpy(newplace, ptr, get_real_size(nptr->size)); - // Free original one - kfree(ptr); - - return newplace; - } - else - { - /* Shrink/Do nothing, you can leave it as it's, but yeah... shrink - * it What's left after shrinking the original block. - */ - uint32_t rest = get_real_size(nptr->size) + OVERHEAD - block_size; - if (rest < 8 + OVERHEAD) return ptr; - - nptr->size = block_size - OVERHEAD; - blkmngr_set_free(&(nptr->size), false); - trailing_space = - (void *) nptr + sizeof(block_t) + get_real_size(nptr->size); - *trailing_space = nptr->size; - /* - * if(tail == nptr) - * { - * ksbrk(-reclaimSize); - * - * return ptr; - * } - */ - block_t *splitBlock = (void *) trailing_space + sizeof(uint32_t); - - /* Set the next, if the next of the next is also freed.. then merge!! - * Wait... what if after merge, I get a much much more bigger block - * than I even need? split again hahahahahah fuck! - * Instead of spliting after merge, let's give splitBlock. - */ - if (nextBlock && blkmngr_is_free(nextBlock)) - { - splitBlock->size = rest + get_real_size(nextBlock->size); - blkmngr_set_free(&(splitBlock->size), true); - trailing_space = (void *) splitBlock + sizeof(block_t) + - get_real_size(splitBlock->size); - *trailing_space = splitBlock->size; - - // Remove next block from freelist. - blkmngr_remove_from_freelist(nextBlock, freelist); - // This can be deleted when you correctly implemented malloc() - if (tail_block == nextBlock) - { - *tail = (uint32_t) splitBlock; - } - // Add splitblock to freelist. - blkmngr_add_to_freelist(splitBlock, freelist); - - return ptr; - } - // Separate! - splitBlock->size = rest - OVERHEAD; - blkmngr_set_free(&(splitBlock->size), true); - trailing_space = (void *) splitBlock + sizeof(block_t) + - get_real_size(splitBlock->size); - *trailing_space = splitBlock->size; - - // Add this mo** f**r to the freelist! - blkmngr_add_to_freelist(splitBlock, freelist); - - return ptr; + return (char *)save + sizeof(block_t); } } +// +///// @brief Allocates size bytes of uninitialized storage with block align. +//static void *__do_malloc_align(vm_area_struct_t *heap, uint32_t size) +//{ +// if (size == 0) return NULL; +// +// // Get: +// // 1) First memory block. +// // static block_t *head = NULL; +// // 2) Last memory block. +// // static block_t *tail = NULL; +// // 3) All the memory blocks that are freed. +// // static block_t *freelist = NULL; +// +// // We will use these in writing. +// uint32_t *head = (uint32_t *) (heap->vm_start); +// uint32_t *tail = (uint32_t *) (heap->vm_start + sizeof(block_t *)); +// uint32_t *freelist = (uint32_t *) (heap->vm_start + 2 * sizeof(block_t *)); +// assert(head && tail && freelist && "Heap block lists point to null."); +// +// // We will use these others in reading. +// block_t *head_block = (block_t *) *head; +// block_t *tail_block = (block_t *) *tail; +// // block_t *first_free_block = (block_t *) *freelist; +// +// // Calculate real size that's used, round it to multiple of 16. +// uint32_t rounded_size = CEIL(size, 16); +// +// /* Find bestfit in avl tree. This bestfit function will remove +// * thebest-fit node when there is more than one such node in tree. +// */ +// block_t *best_fitting = blkmngr_find_best_fitting(rounded_size, freelist); +// if (best_fitting != NULL && (IS_ALIGN(best_fitting + sizeof(block_t)))) +// { +// return kmalloc(size); +// } +// else +// { +// void *needed_addr = (void *) ADDR_ALIGN( +// ((uint32_t) kernel_heap_top + sizeof(block_t)) & 0xFFFFF000); +// block_t *block_addr = needed_addr - sizeof(block_t); +// +// uint32_t realsize = +// (uint32_t) block_addr - (uint32_t) (kernel_heap_top) + OVERHEAD + +// rounded_size; +// block_t *ret; +// if(heap == &kernel_heap) +// { +// ret = ksbrk(realsize); +// } +// else +// { +// ret = usbrk(realsize); +// } +// assert(ret != NULL && "Heap is running out of space\n"); +// if (!head_block) +// { +// *head = (uint32_t) block_addr; +// } +// else +// { +// tail_block->next = block_addr; +// } +// +// ret->next = NULL; +// ret->nextfree = NULL; +// *tail = (uint32_t) block_addr; +// +// /* After sbrk(), split the block into half [block_size | the rest], +// * and put the rest into the tree. +// */ +// block_addr->size = rounded_size; +// blkmngr_set_free(&(block_addr->size), +// 0); +// // Set the block allocated. +// // ptr = ptr + block_size - sizeof(uint32_t); +// // trailing_space = ptr; +// // *trailing_space = block_addr->size; +// +// // Now, return it! +// return needed_addr; +// } +//} +// +///// @brief Reallocates the given area of memory. It must be still allocated +///// and not yet freed with a call to free or realloc. +///// @param ptr +///// @param size +///// @return +//static void *__do_realloc(vm_area_struct_t *heap, void *ptr, uint32_t size) +//{ +// // Get: +// // 1) First memory block. +// // static block_t *head = NULL; +// // 2) Last memory block. +// // static block_t *tail = NULL; +// // 3) All the memory blocks that are freed. +// // static block_t *freelist = NULL; +// +// // We will use these in writing. +// uint32_t *head = (uint32_t *) (heap->vm_start); +// uint32_t *tail = (uint32_t *) (heap->vm_start + sizeof(block_t *)); +// uint32_t *freelist = (uint32_t *) (heap->vm_start + 2 * sizeof(block_t *)); +// assert(head && tail && freelist && "Heap block lists point to null."); +// +// // We will use these others in reading. +// block_t *head_block = (block_t *) *head; +// block_t *tail_block = (block_t *) *tail; +// // block_t *first_free_block = (block_t *) *freelist; +// +// uint32_t *trailing_space = NULL; +// if (!ptr) +// { +// return kmalloc(size); +// } +// if (size == 0 && ptr != NULL) +// { +// kfree(ptr); +// +// return NULL; +// } +// uint32_t rounded_size = CEIL(size, 16); +// uint32_t block_size = rounded_size + OVERHEAD; +// block_t *nextBlock; +// block_t *prevBlock; +// +// /* Shrink or expand? +// * +// * Shrink: +// * Now, we would just return the same address, later we may split this +// * block. +// * +// * Expand: +// * First, try if the actual size of the memory block is enough +// * to hold the current size. +// * Second, if not, try if merging the next block works. +// * Third, if none of the above works, malloc another block, move all the +// * data there, and then free the original block. +// */ +// block_t *nptr = ptr - sizeof(block_t); +// nextBlock = blkmngr_get_next_block(nptr, tail); +// prevBlock = blkmngr_get_previous_block(nptr, head); +// if (nptr->size == size) +// { +// return ptr; +// } +// if (nptr->size < size) +// { +// // Expand, size of the block is just not enough. +// if (tail_block != nptr && blkmngr_is_free(nextBlock) && +// (blkmngr_get_real_size(nptr->size) + OVERHEAD + +// blkmngr_get_real_size(nextBlock->size)) >= rounded_size) +// { +// // Merge with the next block, and return! +// // Change size to curr's size + OVERHEAD + next's size. +// blkmngr_remove_from_freelist(nextBlock, freelist); +// nptr->size = blkmngr_get_real_size(nptr->size) + OVERHEAD + +// blkmngr_get_real_size(nextBlock->size); +// blkmngr_set_free(&(nptr->size), 0); +// trailing_space = +// (void *) nptr + sizeof(block_t) + blkmngr_get_real_size(nptr->size); +// *trailing_space = nptr->size; +// if (tail_block == nextBlock) +// { +// // Set it to tail for now, or we can reclaim it. +// *tail = (uint32_t) nptr; +// } +// return nptr + 1; +// } +// // Hey! Try merging with the previous block! +// else if (head_block != nptr && blkmngr_is_free(prevBlock) && +// (blkmngr_get_real_size(nptr->size) + OVERHEAD + +// blkmngr_get_real_size(prevBlock->size)) >= rounded_size) +// { +// // db_print(); +// uint32_t originalSize = blkmngr_get_real_size(nptr->size); +// // Hey! one more thing to do , copy data over to new block. +// blkmngr_remove_from_freelist(prevBlock, freelist); +// prevBlock->size = +// originalSize + OVERHEAD + blkmngr_get_real_size(prevBlock->size); +// blkmngr_set_free(&(prevBlock->size), 0); +// trailing_space = (void *) prevBlock + sizeof(block_t) + +// blkmngr_get_real_size(prevBlock->size); +// *trailing_space = prevBlock->size; +// if (tail_block == nptr) +// { +// *tail = (uint32_t) prevBlock; +// } +// memcpy(prevBlock + 1, ptr, originalSize); +// +// return prevBlock + 1; +// } +// +// // Move to somewhere else. +// void *newplace = kmalloc(size); +// // Copy data over. +// memcpy(newplace, ptr, blkmngr_get_real_size(nptr->size)); +// // Free original one +// kfree(ptr); +// +// return newplace; +// } +// else +// { +// /* Shrink/Do nothing, you can leave it as it's, but yeah... shrink +// * it What's left after shrinking the original block. +// */ +// uint32_t rest = blkmngr_get_real_size(nptr->size) + OVERHEAD - block_size; +// if (rest < 8 + OVERHEAD) return ptr; +// +// nptr->size = block_size - OVERHEAD; +// blkmngr_set_free(&(nptr->size), 0); +// trailing_space = +// (void *) nptr + sizeof(block_t) + blkmngr_get_real_size(nptr->size); +// *trailing_space = nptr->size; +// /* +// * if(tail == nptr) +// * { +// * ksbrk(-reclaimSize); +// * +// * return ptr; +// * } +// */ +// block_t *splitBlock = (void *) trailing_space + sizeof(uint32_t); +// +// /* Set the next, if the next of the next is also freed.. then merge!! +// * Wait... what if after merge, I get a much much more bigger block +// * than I even need? split again hahahahahah fuck! +// * Instead of spliting after merge, let's give splitBlock. +// */ +// if (nextBlock && blkmngr_is_free(nextBlock)) +// { +// splitBlock->size = rest + blkmngr_get_real_size(nextBlock->size); +// blkmngr_set_free(&(splitBlock->size), 1); +// trailing_space = (void *) splitBlock + sizeof(block_t) + +// blkmngr_get_real_size(splitBlock->size); +// *trailing_space = splitBlock->size; +// +// // Remove next block from freelist. +// blkmngr_remove_from_freelist(nextBlock, freelist); +// // This can be deleted when you correctly implemented malloc() +// if (tail_block == nextBlock) +// { +// *tail = (uint32_t) splitBlock; +// } +// // Add splitblock to freelist. +// blkmngr_add_to_freelist(splitBlock, freelist); +// +// return ptr; +// } +// // Separate! +// splitBlock->size = rest - OVERHEAD; +// blkmngr_set_free(&(splitBlock->size), 1); +// trailing_space = (void *) splitBlock + sizeof(block_t) + +// blkmngr_get_real_size(splitBlock->size); +// *trailing_space = splitBlock->size; +// +// // Add this mo** f**r to the freelist! +// blkmngr_add_to_freelist(splitBlock, freelist); +// +// return ptr; +// } +//} /// @brief Deallocates previously allocated space. -static void __do_free(struct vm_area_struct * heap, void * ptr) +/// @param heap Heap to which we return the allocated memory. +/// @param ptr Pointer to the allocated memory. +static void __do_free(vm_area_struct_t *heap, void *ptr) { assert(ptr); @@ -649,92 +675,75 @@ static void __do_free(struct vm_area_struct * heap, void * ptr) // static block_t *freelist = NULL; // We will use these in writing. - uint32_t *head = (uint32_t *) (heap->vm_start); - uint32_t *tail = (uint32_t *) (heap->vm_start + sizeof(block_t *)); - uint32_t *freelist = (uint32_t *) (heap->vm_start + 2 * sizeof(block_t *)); + uint32_t *head = (uint32_t *)(heap->vm_start); + uint32_t *tail = (uint32_t *)(heap->vm_start + sizeof(block_t *)); + uint32_t *freelist = (uint32_t *)(heap->vm_start + 2 * sizeof(block_t *)); assert(head && tail && freelist && "Heap block lists point to null."); // We will use these others in reading. - block_t *tail_block = (block_t *) *tail; + block_t *tail_block = (block_t *)*tail; - block_t *curr = ptr - sizeof(block_t); + block_t *curr = (block_t *)((char *)ptr - sizeof(block_t)); block_t *prev = blkmngr_get_previous_block(curr, head); block_t *next = blkmngr_get_next_block(curr, tail); - if (blkmngr_is_free(prev) && blkmngr_is_free(next)) - { + if (blkmngr_is_free(prev) && blkmngr_is_free(next)) { prev->size = - get_real_size(prev->size) + 2 * OVERHEAD + - get_real_size(curr->size) + - get_real_size(next->size); - blkmngr_set_free(&(prev->size), true); + blkmngr_get_real_size(prev->size) + 2 * OVERHEAD + + blkmngr_get_real_size(curr->size) + + blkmngr_get_real_size(next->size); + blkmngr_set_free(&(prev->size), 1); prev->next = blkmngr_get_next_block(next, tail); // If next used to be tail, set prev = tail. - if (tail_block == next) - { - *tail = (uint32_t) prev; + if (tail_block == next) { + *tail = (uint32_t)prev; } blkmngr_remove_from_freelist(next, freelist); - } - else if (blkmngr_is_free(prev)) - { + } else if (blkmngr_is_free(prev)) { prev->size = - get_real_size(prev->size) + OVERHEAD + get_real_size(curr->size); - blkmngr_set_free(&(prev->size), true); + blkmngr_get_real_size(prev->size) + OVERHEAD + blkmngr_get_real_size(curr->size); + blkmngr_set_free(&(prev->size), 1); prev->next = next; - if (tail_block == curr) - { - *tail = (uint32_t) prev; + if (tail_block == curr) { + *tail = (uint32_t)prev; } - } - else if (blkmngr_is_free(next)) - { + } else if (blkmngr_is_free(next)) { // Change size to curr's size + OVERHEAD + next's size. curr->size = - get_real_size(curr->size) + OVERHEAD + get_real_size(next->size); - blkmngr_set_free(&(curr->size), true); + blkmngr_get_real_size(curr->size) + OVERHEAD + blkmngr_get_real_size(next->size); + blkmngr_set_free(&(curr->size), 1); curr->next = blkmngr_get_next_block(next, tail); - if (tail_block == next) - { - *tail = (uint32_t) curr; + if (tail_block == next) { + *tail = (uint32_t)curr; } blkmngr_remove_from_freelist(next, freelist); blkmngr_add_to_freelist(curr, freelist); - } - else - { + } else { // Just mark curr freed. - blkmngr_set_free(&(curr->size), true); + blkmngr_set_free(&(curr->size), 1); blkmngr_add_to_freelist(curr, freelist); } } void kheap_init(size_t initial_size) { - // Starting allocate kheap after paging. - if (paging_is_enabled()) - { - assert(0 && "Paging must not be enabled!"); - } - - unsigned int order = find_nearest_order_greater(initial_size); - + unsigned int order = find_nearest_order_greater(0, initial_size); // Kernel_heap_start. - kernel_heap.vm_start = __alloc_pages(GFP_KERNEL, order); - kernel_heap.vm_end = kernel_heap.vm_start + ((1UL << order) * PAGE_SIZE); - + kernel_heap.vm_start = __alloc_pages_lowmem(GFP_KERNEL, order); + kernel_heap.vm_end = kernel_heap.vm_start + ((1UL << order) * PAGE_SIZE); // Kernel_heap_start. - kernel_heap_curr = kernel_heap.vm_start; + kernel_heap_top = kernel_heap.vm_start; + // FIXME!! // Set kernel_heap vm_area_struct info: - kernel_heap.vm_next = NULL; - kernel_heap.vm_mm = NULL; + // kernel_heap.vm_next = NULL; + // kernel_heap.vm_mm = NULL; // Reserved space for: // 1) First memory block. @@ -743,100 +752,39 @@ void kheap_init(size_t initial_size) // static block_t *tail = NULL; // 3) All the memory blocks that are freed. // static block_t *freelist = NULL; - memset((void *)kernel_heap_curr, 0, 3 * sizeof(block_t *)); - kernel_heap_curr += 3 * sizeof(block_t *); - - kheap_enabled = true; -} - -bool_t kheap_is_enabled() -{ - return kheap_enabled; + memset((void *)kernel_heap_top, 0, 3 * sizeof(block_t *)); + kernel_heap_top += 3 * sizeof(block_t *); } void *ksbrk(int increment) { - return __do_brk(&kernel_heap_curr, &kernel_heap, increment); + return __do_brk(&kernel_heap_top, &kernel_heap, increment); } - - -void *kmalloc(uint32_t sz) +void *usbrk(int increment) { - assert(kheap_enabled && "KHEAP not initialized!"); + task_struct *current_task = scheduler_get_current_process(); + mm_struct_t *task_mm = current_task->mm; + uint32_t *heap_curr = &task_mm->brk; - return __do_malloc(&kernel_heap, sz); + vm_area_struct_t *heap_segment = __find_user_heap(); + + return __do_brk(heap_curr, heap_segment, increment); } -// TODO: check! -void *kmalloc_align(size_t size) -{ - assert(kheap_enabled && "KHEAP not initialized!"); - - return __do_malloc_align(&kernel_heap, size); -} - -void *kcalloc(uint32_t num, uint32_t size) -{ - void *ptr = kmalloc(num * size); - if (ptr) - { - memset(ptr, 0, num * size); - } - - return ptr; -} - -// TODO: check! -void *krealloc(void *ptr, uint32_t size) -{ - return __do_realloc(&kernel_heap, ptr, size); -} - -void kfree(void *ptr) -{ - __do_free(&kernel_heap, ptr); -} - -/// @brief Find the current user heap. -/// @return The heap structure if heap exists, otherwise NULL. -static struct vm_area_struct *find_user_heap() -{ - // Get the memory descriptor of the current process. - task_struct *current_task = kernel_get_current_process(); - struct mm_struct *current_mm = current_task->mm; - - // Get the starting address of the heap. - uint32_t start_heap = current_mm->start_brk; - // If not set return NULL. - if(start_heap == 0) - { - return NULL; - } - - // Otherwise find the respective heap segment. - struct vm_area_struct *segment = current_mm->mmap; - while(segment->vm_start != start_heap) - { - segment = segment->vm_next; - } - - return segment; -} - -// TODO: rename in sys_brk -void *umalloc(unsigned int size) +void *sys_brk(void *addr) { // Get user heap segment structure. - struct vm_area_struct *heap_segment = find_user_heap(); + vm_area_struct_t *heap_segment = __find_user_heap(); // Allocate the segment if don't exist. - if(heap_segment == NULL) - { - task_struct *current_task = kernel_get_current_process(); - struct mm_struct *current_mm = current_task->mm; - current_mm->start_brk = create_segment(current_mm, UHEAP_INITIAL_SIZE); - current_mm->brk = current_mm->start_brk; + if (heap_segment == NULL) { + task_struct *current_task = scheduler_get_current_process(); + mm_struct_t *current_mm = current_task->mm; + current_mm->start_brk = create_vm_area(current_mm, + 0x40000000 /*FIXME! stabilize this*/, + UHEAP_INITIAL_SIZE, MM_RW | MM_PRESENT | MM_USER | MM_UPDADDR, GFP_HIGHUSER); + current_mm->brk = current_mm->start_brk; // Reserved space for: // 1) First memory block. // static block_t *head = NULL; @@ -845,34 +793,16 @@ void *umalloc(unsigned int size) // 3) All the memory blocks that are freed. // static block_t *freelist = NULL; current_mm->brk += 3 * sizeof(block_t *); - heap_segment = find_user_heap(); + heap_segment = __find_user_heap(); } - - return __do_malloc(heap_segment, size); -} - -// TODO: rename in sys_brk -void ufree(void *p) -{ - // Get user heap segment structure. - struct vm_area_struct *heap_segment = find_user_heap(); - - // If the segment exists, user did a malloc previously. - if (heap_segment != NULL) - { - __do_free(heap_segment, p); + // If the address falls inside the memory region, call the free function, + // otherwise execute a malloc of the specified amount. + if (((uintptr_t)addr > heap_segment->vm_start) && + ((uintptr_t)addr < heap_segment->vm_end)) { + __do_free(heap_segment, addr); + return NULL; } -} - -void *usbrk(int increment) -{ - task_struct *current_task = kernel_get_current_process(); - struct mm_struct *task_mm = current_task->mm; - uint32_t *heap_curr = &task_mm->brk; - - struct vm_area_struct *heap_segment = find_user_heap(); - - return __do_brk(heap_curr, heap_segment, increment); + return __do_malloc(heap_segment, (uintptr_t)addr); } void kheap_dump() @@ -885,57 +815,43 @@ void kheap_dump() // static block_t *freelist = NULL; // We will use these in writing. - uint32_t *head = (uint32_t *) (kernel_heap.vm_start); - uint32_t *tail = (uint32_t *) (kernel_heap.vm_start + sizeof(block_t *)); - uint32_t *freelist = (uint32_t *) (kernel_heap.vm_start + 2 * sizeof(block_t *)); + uint32_t *head = (uint32_t *)(kernel_heap.vm_start); + uint32_t *tail = (uint32_t *)(kernel_heap.vm_start + sizeof(block_t *)); + uint32_t *freelist = (uint32_t *)(kernel_heap.vm_start + 2 * sizeof(block_t *)); assert(head && tail && freelist && "Heap block lists point to null."); // We will use these others in reading. - block_t *head_block = (block_t *) *head; + block_t *head_block = (block_t *)*head; // block_t *tail_block = (block_t *) *tail; - block_t *first_free_block = (block_t *) *freelist; - - if (!head_block) - { - dbg_print("your heap is empty now\n"); + block_t *first_free_block = (block_t *)*freelist; + if (!head_block) { + pr_debug("your heap is empty now\n"); return; } - // dbg_print("HEAP:\n"); - uint32_t total = 0; + // pr_debug("HEAP:\n"); + uint32_t total = 0; uint32_t total_overhead = 0; - block_t * it = head_block; - while (it) - { - dbg_print("[%c] %12u (%12u) from 0x%p to 0x%p\n", - (blkmngr_is_free(it)) ? 'F' : 'A', - get_real_size(it->size), - it->size, - it, - (void *) it + OVERHEAD + get_real_size(it->size)); - total += get_real_size(it->size); + block_t *it = head_block; + while (it) { + pr_debug("[%c] %12u (%12u) from 0x%p to 0x%p\n", + (blkmngr_is_free(it)) ? 'F' : 'A', + blkmngr_get_real_size(it->size), + it->size, + it, + (char *)it + OVERHEAD + blkmngr_get_real_size(it->size)); + total += blkmngr_get_real_size(it->size); total_overhead += OVERHEAD; it = it->next; } - dbg_print("\nTotal usable bytes : %d", total); - dbg_print("\nTotal overhead bytes : %d", total_overhead); - dbg_print("\nTotal bytes : %d", total + total_overhead); - dbg_print("\nFreelist: "); - for (it = first_free_block; it != NULL; it = it->nextfree) - { - dbg_print("(%p)->", it); + pr_debug("\nTotal usable bytes : %d", total); + pr_debug("\nTotal overhead bytes : %d", total_overhead); + pr_debug("\nTotal bytes : %d", total + total_overhead); + pr_debug("\nFreelist: "); + for (it = first_free_block; it != NULL; it = it->nextfree) { + pr_debug("(%p)->", it); } - dbg_print("\n\n"); -} - -uint32_t get_kheap_start() -{ - return kernel_heap.vm_start; -} - -uint32_t get_kheap_curr() -{ - return kernel_heap_curr; + pr_debug("\n\n"); } diff --git a/mentos/src/mem/paging.c b/mentos/src/mem/paging.c index e304439..11601dd 100644 --- a/mentos/src/mem/paging.c +++ b/mentos/src/mem/paging.c @@ -1,88 +1,626 @@ /// MentOS, The Mentoring Operating system project /// @file paging.c /// @brief Implementation of a memory paging management. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "paging.h" -#include "zone_allocator.h" -#include "kheap.h" -#include "debug.h" +/// Change the header. +#define __DEBUG_HEADER__ "[PAGING]" + +#include "mem/paging.h" +#include "descriptor_tables/isr.h" +#include "mem/vmem_map.h" +#include "mem/zone_allocator.h" +#include "mem/kheap.h" +#include "misc/debug.h" #include "assert.h" #include "string.h" +#include "system/panic.h" -bool_t paging_is_enabled() +/// Cache for storing mm_struct. +kmem_cache_t *mm_cache; +/// Cache for storing vm_area_struct. +kmem_cache_t *vm_area_cache; +/// Cache for storing page directories. +kmem_cache_t *pgdir_cache; +/// Cache for storing page tables. +kmem_cache_t *pgtbl_cache; + +/// The mm_struct of the kernel. +static mm_struct_t *main_mm; + +/// @brief Structure for iterating page directory entries. +typedef struct page_iterator_s { + /// Pointer to the entry. + page_dir_entry_t *entry; + /// Pointer to the page table. + page_table_t *table; + /// Page Frame Number (PFN). + uint32_t pfn; + /// Last PNF. + uint32_t last_pfn; + /// Contains MEMMAP_FLAGS flags. + uint32_t flags; +} page_iterator_t; + +/// @brief Structure for iterating page table entries. +typedef struct pg_iter_entry_s { + /// Pointer to the page table entry. + page_table_entry_t *entry; + /// Page Frame Number (PFN). + uint32_t pfn; +} pg_iter_entry_t; + +page_directory_t *paging_get_main_directory() { - return false; + return main_mm->pgd; } -struct mm_struct *create_process_image(size_t stack_size) +/// @brief Switches paging directory, the pointer can be a lowmem address +void paging_switch_directory_va(page_directory_t *dir) { - // Allocate the mm_struct. - struct mm_struct *mm = kmalloc(sizeof(struct mm_struct)); - memset(mm, 0, sizeof(struct mm_struct)); - - // Allocate the stack segment. - mm->start_stack = create_segment(mm, stack_size); - - return mm; + page_t *page = get_lowmem_page_from_address((uintptr_t)dir); + paging_switch_directory((page_directory_t *)get_physical_address_from_page(page)); } -uint32_t create_segment(struct mm_struct *mm, size_t size) +void paging_flush_tlb_single(unsigned long addr) { - // Allocate on kernel space the structure for the segment. - struct vm_area_struct *new_segment = kmalloc(sizeof(struct vm_area_struct)); - - // Allocate the space requested for the segment on user space. - if (paging_is_enabled()) { - assert(0 && "Paging must not be enabled"); - } - - unsigned int order = find_nearest_order_greater(size); - uint32_t vm_start = __alloc_pages(GFP_HIGHUSER, order); - - // Update vm_area_struct info. - new_segment->vm_start = vm_start; - new_segment->vm_end = vm_start + (1 << order) * PAGE_SIZE; - new_segment->vm_mm = mm; - - // Update memory descriptor list of vm_area_struct. - new_segment->vm_next = mm->mmap; - mm->mmap = new_segment; - mm->mmap_cache = new_segment; - - // Update memory descriptor info. - mm->map_count++; - - mm->total_vm += (1 << order); - - return vm_start; + ASM("invlpg (%0)" ::"r"(addr) + : "memory"); } -void destroy_process_image(struct mm_struct *mm) +uint32_t create_vm_area(mm_struct_t *mm, + uint32_t virt_start, + size_t size, + uint32_t pgflags, + uint32_t gfpflags) { - assert(mm != NULL); + // Allocate on kernel space the structure for the segment. + vm_area_struct_t *new_segment = kmem_cache_alloc(vm_area_cache, GFP_KERNEL); - // Free each segment inside mm. - struct vm_area_struct *segment = mm->mmap; - while (segment != NULL) { - // Free the pages represented by the segment. - if (paging_is_enabled()) { - assert(0 && "Paging must not be enabled"); - } + uint32_t order = find_nearest_order_greater(virt_start, size); - size_t size = segment->vm_end - segment->vm_start; - unsigned int order = find_nearest_order_greater(size); + uint32_t phy_vm_start; - free_pages(segment->vm_start, order); + if (pgflags & MM_COW) { + pgflags &= ~(MM_PRESENT | MM_UPDADDR); + phy_vm_start = 0; + } else { + pgflags |= MM_UPDADDR; + page_t *page = _alloc_pages(gfpflags, order); + phy_vm_start = get_physical_address_from_page(page); + } - struct vm_area_struct *tmp = segment; - segment = segment->vm_next; + mem_upd_vm_area(mm->pgd, virt_start, phy_vm_start, size, pgflags); - // Free the vm_area_struct. - kfree(tmp); - } + uint32_t vm_start = virt_start; - // Free the mm_struct. - kfree(mm); + // Update vm_area_struct info. + new_segment->vm_start = vm_start; + new_segment->vm_end = vm_start + size; + new_segment->vm_mm = mm; + + // Update memory descriptor list of vm_area_struct. + list_head_add(&new_segment->vm_list, &mm->mmap_list); + mm->mmap_cache = new_segment; + + // Update memory descriptor info. + mm->map_count++; + + mm->total_vm += (1U << order); + + return vm_start; +} + +uint32_t clone_vm_area(mm_struct_t *mm, vm_area_struct_t *area, int cow, uint32_t gfpflags) +{ + vm_area_struct_t *new_segment = kmem_cache_alloc(vm_area_cache, GFP_KERNEL); + memcpy(new_segment, area, sizeof(vm_area_struct_t)); + + new_segment->vm_mm = mm; + + uint32_t size = new_segment->vm_end - new_segment->vm_start; + uint32_t order = find_nearest_order_greater(area->vm_start, size); + + if (!cow) { + // If not copy-on-write, allocate directly the physical pages + page_t *dst_page = _alloc_pages(gfpflags, order); + uint32_t phy_vm_start = get_physical_address_from_page(dst_page); + + // Then update the virtual memory map + mem_upd_vm_area(mm->pgd, new_segment->vm_start, phy_vm_start, size, + MM_RW | MM_PRESENT | MM_UPDADDR | MM_USER); + + // Copy virtual memory of source area into dest area by using a virtual mapping + virt_memcpy(mm, area->vm_start, area->vm_mm, area->vm_start, size); + } else { + // If copy-on-write, set the original pages as read-only + mem_upd_vm_area(area->vm_mm->pgd, area->vm_start, 0, size, + MM_COW | MM_PRESENT | MM_USER); + + // Do a cow of the whole virtual memory area, handling fragmented physical memory + // and set it as read-only + mem_clone_vm_area(area->vm_mm->pgd, + mm->pgd, + area->vm_start, + new_segment->vm_start, + size, + MM_COW | MM_PRESENT | MM_UPDADDR | MM_USER); + } + + // Update memory descriptor list of vm_area_struct. + list_head_add(&new_segment->vm_list, &mm->mmap_list); + mm->mmap_cache = new_segment; + + // Update memory descriptor info. + mm->map_count++; + + mm->total_vm += (1U << order); + + return 0; +} + +static void __init_pagedir(page_directory_t *pdir) +{ + *pdir = (page_directory_t){ 0 }; +} + +static void __init_pagetable(page_table_t *ptable) +{ + *ptable = (page_table_t){ 0 }; +} + +void paging_init(boot_info_t *info) +{ + mm_cache = KMEM_CREATE(mm_struct_t); + vm_area_cache = KMEM_CREATE(vm_area_struct_t); + + pgdir_cache = KMEM_CREATE_CTOR(page_directory_t, __init_pagedir); + pgtbl_cache = KMEM_CREATE_CTOR(page_table_t, __init_pagetable); + + main_mm = kmem_cache_alloc(mm_cache, GFP_KERNEL); + + main_mm->pgd = kmem_cache_alloc(pgdir_cache, GFP_KERNEL); + + uint32_t lowkmem_size = info->stack_end - info->kernel_start; + + // Map the first 1MB of memory with physical mapping to access video memory and other bios stuff + mem_upd_vm_area(main_mm->pgd, 0, 0, 1024 * 1024, MM_RW | MM_PRESENT | MM_GLOBAL | MM_UPDADDR); + + mem_upd_vm_area(main_mm->pgd, info->kernel_start, info->kernel_phy_start, lowkmem_size, + MM_RW | MM_PRESENT | MM_GLOBAL | MM_UPDADDR); + + isr_install_handler(PAGE_FAULT, page_fault_handler, "page_fault_handler"); + + paging_switch_directory_va(main_mm->pgd); + paging_enable(); +} + +// Error code interpretation. +#define ERR_PRESENT 0x01 ///< Page not present. +#define ERR_RW 0x02 ///< Page is read only. +#define ERR_USER 0x04 ///< Page is privileged. +#define ERR_RESERVED 0x08 ///< Overwrote reserved bit. +#define ERR_INST 0x10 ///< Instruction fetch. + +static inline void __set_pg_table_flags(page_table_entry_t *table, uint32_t flags) +{ + table->rw = (flags & MM_RW) != 0; + table->present = (flags & MM_PRESENT) != 0; + table->kernel_cow = (flags & MM_COW) != 0; // Store the cow/not cow status + table->available = 1; // Future kernel data 2 bits + table->global = (flags & MM_GLOBAL) != 0; + table->user = (flags & MM_USER) != 0; +} + +/// @brief Prints stack frame data and calls kernel_panic. +/// @param f The interrupt stack frame. +/// @param addr The faulting address. +static void __page_fault_panic(pt_regs *f, uint32_t addr) +{ + asm volatile("cli"); + + // Gather fault info and print to screen + pr_err("Faulting address (cr2): 0x%p\n", addr); + + pr_err("EIP: 0x%p\n", f->eip); + + pr_err("Page fault: 0x%x\n", addr); + + pr_err("Possible causes: [ "); + if (!(f->err_code & ERR_PRESENT)) + pr_err("Page not present "); + if (f->err_code & ERR_RW) + pr_err("Page is read only "); + if (f->err_code & ERR_USER) + pr_err("Page is privileged "); + if (f->err_code & ERR_RESERVED) + pr_err("Overwrote reserved bits "); + if (f->err_code & ERR_INST) + pr_err("Instruction fetch "); + pr_err("]\n"); + dbg_print_regs(f); + + kernel_panic("Page fault!"); + + // Make directory accessible + // main_mm->pgd->entries[addr/(1024*4096)].user = 1; + // main_directory->entries[addr/(1024*4096)]. = 1; + + asm volatile("cli"); +} + +static void __page_handle_cow(page_table_entry_t *entry) +{ + // Check if the page is Copy On Write (COW). + if (entry->kernel_cow) { + // Set the entry is no longer COW. + entry->kernel_cow = 0; + // Check if the entry is not present (allocated). + if (!entry->present) { + // Allocate a new page. + page_t *page = _alloc_pages(GFP_HIGHUSER, 0); + // Clear the new page. + uint32_t vaddr = virt_map_physical_pages(page, 1); + memset((void *)vaddr, 0, PAGE_SIZE); + // Unmap the virtual address. + virt_unmap(vaddr); + // Set it as current table entry frame. + entry->frame = get_physical_address_from_page(page) >> 12U; + // Set it as allocated. + entry->present = 1; + return; + } + } + kernel_panic("Page not cow!"); +} + +static page_table_t *__mem_pg_entry_alloc(page_dir_entry_t *entry, uint32_t flags) +{ + if (!entry->present) { + // Alloc page table if not present + // Present should be always 1, to indicate that the page tables + // have been allocated and allow lazy physical pages allocation + entry->present = 1; + entry->rw = 1; + entry->global = (flags & MM_GLOBAL) != 0; + entry->user = (flags & MM_USER) != 0; + entry->accessed = 0; + entry->available = 1; + return kmem_cache_alloc(pgtbl_cache, GFP_KERNEL); + } else { + entry->present |= (flags & MM_PRESENT) != 0; + entry->rw |= (flags & MM_RW) != 0; + + // We should not remove a global flag from a page directory, + // if this happens there is probably a bug in the kernel + assert(!entry->global || (flags & MM_GLOBAL)); + + entry->global &= (flags & MM_GLOBAL) != 0; + entry->user |= (flags & MM_USER) != 0; + return (page_table_t *)get_lowmem_address_from_page( + get_page_from_physical_address(((uint32_t)entry->frame) << 12U)); + } +} + +static inline void __set_pg_entry_frame(page_dir_entry_t *entry, page_table_t *table) +{ + page_t *table_page = get_lowmem_page_from_address((uint32_t)table); + uint32_t phy_addr = get_physical_address_from_page(table_page); + entry->frame = phy_addr >> 12u; +} + +void page_fault_handler(pt_regs *f) +{ + // Here you will find the `Demand Paging` mechanism. + // From `Understanding The Linux Kernel 3rd Edition`: + // The term demand paging denotes a dynamic memory allocation + // technique that consists of deferring page frame allocation + // until the last possible moment—until the process attempts + // to address a page that is not present in RAM, thus causing + // a Page Fault exception. + + // First, read the linear address that caused the Page Fault. + // When the exception occurs, the CPU control unit stores that + // value in the cr2 control register. + uint32_t faulting_addr; + asm volatile("mov %%cr2, %0" + : "=r"(faulting_addr)); + // Get the physical address of the current page directory. + uint32_t phy_dir = (uint32_t)paging_get_current_directory(); + // Get the page directory. + page_directory_t *lowmem_dir = (page_directory_t *)get_lowmem_address_from_page(get_page_from_physical_address(phy_dir)); + // Get the directory entry. + page_dir_entry_t *direntry = &lowmem_dir->entries[faulting_addr / (1024U * PAGE_SIZE)]; + // TODO: Panic only if page is in kernel memory, else abort process with sigsegv + if (!direntry->present) { + __page_fault_panic(f, faulting_addr); + } + // Get the physical address of the page table. + uint32_t phy_table = direntry->frame << 12U; + // Get the page table. + page_table_t *lowmem_table = (page_table_t *)get_lowmem_address_from_page(get_page_from_physical_address(phy_table)); + // Get the entry inside the table that caused the fault. + uint32_t table_index = (faulting_addr / PAGE_SIZE) % 1024U; + // Get the corresponding page table entry. + page_table_entry_t *entry = &lowmem_table->pages[table_index]; + // There was a page fault on a virtual mapped address, + // so we must first update the original mapped page + if (virtual_check_address(faulting_addr)) { + // Get the original page table entry from the virtually mapped one. + page_table_entry_t *orig_entry = (page_table_entry_t *)(*(uint32_t *)entry); + // Check if the page is Copy on Write (CoW). + __page_handle_cow(orig_entry); + // Update the page table entry frame. + entry->frame = orig_entry->frame; + // Update the entry flags. + __set_pg_table_flags(entry, MM_PRESENT | MM_RW | MM_GLOBAL | MM_COW | MM_UPDADDR); + } else { + // Check if the page is Copy on Write (CoW). + __page_handle_cow(entry); + } + // Invalidate the page table entry. + paging_flush_tlb_single(faulting_addr); +} + +/// @brief Initialize a page iterator. +/// @param iter The iterator to initialize. +/// @param pgd The page directory to iterate. +/// @param addr_start The starting address. +/// @param size The total amount we want to iterate. +/// @param flags Allocation flags. +static void __pg_iter_init(page_iterator_t *iter, + page_directory_t *pgd, + uint32_t addr_start, + uint32_t size, + uint32_t flags) +{ + uint32_t start_pfn = addr_start / PAGE_SIZE; + + uint32_t end_pfn = (addr_start + size + PAGE_SIZE - 1) / PAGE_SIZE; + + uint32_t base_pgt = start_pfn / 1024; + iter->entry = pgd->entries + base_pgt; + iter->pfn = start_pfn; + iter->last_pfn = end_pfn; + iter->flags = flags; + + iter->table = __mem_pg_entry_alloc(iter->entry, flags); + __set_pg_entry_frame(iter->entry, iter->table); +} + +/// @brief Checks if the iterator has a next entry. +/// @param iter The iterator. +/// @return If we can continue the iteration. +static int __pg_iter_has_next(page_iterator_t *iter) +{ + return iter->pfn < iter->last_pfn; +} + +/// @brief Moves the iterator to the next entry. +/// @param iter The itetator. +/// @return The iterator after moving to the next entry. +static pg_iter_entry_t __pg_iter_next(page_iterator_t *iter) +{ + pg_iter_entry_t result = { + .entry = &iter->table->pages[iter->pfn % 1024], + .pfn = iter->pfn + }; + + if (++iter->pfn % 1024 == 0) { + // Create a new page only if we haven't reached the end + // The page directory is always aligned to page boundaries, + // so we can easily know when we've skipped the last page by checking + // if the address % PAGE_SIZE is equal to zero. + if (iter->pfn != iter->last_pfn && ((uint32_t)++iter->entry) % 4096 != 0) { + iter->table = __mem_pg_entry_alloc(iter->entry, iter->flags); + __set_pg_entry_frame(iter->entry, iter->table); + } + } + + return result; +} + +page_t *mem_virtual_to_page(page_directory_t *pgdir, uint32_t virt_start, size_t *size) +{ + uint32_t virt_pfn = virt_start / PAGE_SIZE; + uint32_t virt_pgt = virt_pfn / 1024; + uint32_t virt_pgt_offset = virt_pfn % 1024; + + page_t *pgd_page = mem_map + pgdir->entries[virt_pgt].frame; + + page_table_t *pgt_address = (page_table_t *)get_lowmem_address_from_page(pgd_page); + + uint32_t pfn = pgt_address->pages[virt_pgt_offset].frame; + + page_t *page = mem_map + pfn; + + // FIXME: handle unaligned page mapping + // to return the correct to-block-end size + // instead of 0 (1 page at a time) + if (size) { + uint32_t pfn_count = 1U << page->bbpage.order; + uint32_t bytes_count = pfn_count * PAGE_SIZE; + *size = min(*size, bytes_count); + } + + return page; +} + +void mem_upd_vm_area(page_directory_t *pgd, + uint32_t virt_start, + uint32_t phy_start, + size_t size, + uint32_t flags) +{ + page_iterator_t virt_iter; + __pg_iter_init(&virt_iter, pgd, virt_start, size, flags); + + uint32_t phy_pfn = phy_start / PAGE_SIZE; + + while (__pg_iter_has_next(&virt_iter)) { + pg_iter_entry_t it = __pg_iter_next(&virt_iter); + if (flags & MM_UPDADDR) { + it.entry->frame = phy_pfn++; + // Flush the tlb to allow address update + // TODO: Check if it's always needed (ex. when the pgdir is not the current one) + paging_flush_tlb_single(it.pfn * PAGE_SIZE); + } + __set_pg_table_flags(it.entry, flags); + } +} + +void mem_clone_vm_area(page_directory_t *src_pgd, + page_directory_t *dst_pgd, + uint32_t src_start, + uint32_t dst_start, + size_t size, + uint32_t flags) +{ + page_iterator_t src_iter; + page_iterator_t dst_iter; + + __pg_iter_init(&src_iter, src_pgd, src_start, size, flags); + __pg_iter_init(&dst_iter, dst_pgd, dst_start, size, flags); + + while (__pg_iter_has_next(&src_iter) && __pg_iter_has_next(&dst_iter)) { + pg_iter_entry_t src_it = __pg_iter_next(&src_iter); + pg_iter_entry_t dst_it = __pg_iter_next(&dst_iter); + + if (src_it.entry->kernel_cow) { + *(uint32_t *)dst_it.entry = (uint32_t)src_it.entry; + // This is to make it clear that the page is not present, + // can be omitted because the .entry address is aligned to 4 bytes boundary + // so it's first two bytes are always zero + dst_it.entry->present = 0; + } else { + dst_it.entry->frame = src_it.entry->frame; + __set_pg_table_flags(dst_it.entry, flags); + } + + // Flush the tlb to allow address update + // TODO: Check if it's always needed (ex. when the pgdir is not the current one) + paging_flush_tlb_single(dst_it.pfn * PAGE_SIZE); + } +} + +mm_struct_t *create_blank_process_image(size_t stack_size) +{ + // Allocate the mm_struct. + mm_struct_t *mm = kmem_cache_alloc(mm_cache, GFP_KERNEL); + memset(mm, 0, sizeof(mm_struct_t)); + + list_head_init(&mm->mmap_list); + + // TODO: Use this field + list_head_init(&mm->mm_list); + + page_directory_t *pdir_cpy = kmem_cache_alloc(pgdir_cache, GFP_KERNEL); + memcpy(pdir_cpy, paging_get_main_directory(), sizeof(page_directory_t)); + + mm->pgd = pdir_cpy; + + // Initialize vm areas list + list_head_init(&mm->mmap_list); + + // Allocate the stack segment. + mm->start_stack = create_vm_area(mm, PROCAREA_END_ADDR - stack_size, stack_size, + MM_PRESENT | MM_RW | MM_USER | MM_COW, GFP_HIGHUSER); + return mm; +} + +mm_struct_t *clone_process_image(mm_struct_t *mmp) +{ + // Allocate the mm_struct. + mm_struct_t *mm = kmem_cache_alloc(mm_cache, GFP_KERNEL); + memcpy(mm, mmp, sizeof(mm_struct_t)); + + // Initialize the process with the main directory, to avoid page tables data races. + // Pages from the old process are copied/cow when segments are cloned + page_directory_t *pdir_cpy = kmem_cache_alloc(pgdir_cache, GFP_KERNEL); + memcpy(pdir_cpy, paging_get_main_directory(), sizeof(page_directory_t)); + + mm->pgd = pdir_cpy; + + vm_area_struct_t *vm_area = NULL; + + // Reset vm areas to allow easy clone + list_head_init(&mm->mmap_list); + mm->map_count = 0; + mm->total_vm = 0; + + // Clone each memory area to the new process! + list_head *it; + list_for_each (it, &mmp->mmap_list) { + vm_area = list_entry(it, vm_area_struct_t, vm_list); + clone_vm_area(mm, vm_area, 0, GFP_HIGHUSER); + } + + // + // // Allocate the stack segment. + // mm->start_stack = create_segment(mm, stack_size); + + return mm; +} + +void destroy_process_image(mm_struct_t *mm) +{ + assert(mm != NULL); + + if ((uint32_t)paging_get_current_directory() == get_physical_address_from_page(get_lowmem_page_from_address((uint32_t)mm->pgd))) { + paging_switch_directory_va(paging_get_main_directory()); + } + + // Free each segment inside mm. + vm_area_struct_t *segment = NULL; + + list_head *it = mm->mmap_list.next; + while (!list_head_empty(it)) { + segment = list_entry(it, vm_area_struct_t, vm_list); + + size_t size = segment->vm_end - segment->vm_start; + + uint32_t area_start = segment->vm_start; + + while (size > 0) { + size_t area_size = size; + page_t *phy_page = mem_virtual_to_page(mm->pgd, area_start, &area_size); + + // If the pages are marked as copy-on-write, do not deallocate them! + if (page_count(phy_page) > 1) { + uint32_t order = phy_page->bbpage.order; + uint32_t block_size = 1UL << order; + for (int i = 0; i < block_size; i++) { + page_dec(phy_page + i); + } + } else { + __free_pages(phy_page); + } + + size -= area_size; + area_start += area_size; + } + // Free the vm_area_struct. + + // Delete segment from the mmap + it = segment->vm_list.next; + list_head_del(&segment->vm_list); + --mm->map_count; + + kmem_cache_free(segment); + } + + // Free all the page tables + for (int i = 0; i < 1024; i++) { + page_dir_entry_t *entry = &mm->pgd->entries[i]; + if (entry->present && !entry->global) { + page_t *pgt_page = get_page_from_physical_address(entry->frame * PAGE_SIZE); + uint32_t pgt_addr = get_lowmem_address_from_page(pgt_page); + kmem_cache_free((void *)pgt_addr); + } + } + kmem_cache_free((void *)mm->pgd); + + // Free the mm_struct. + kmem_cache_free(mm); } diff --git a/mentos/src/mem/slab.c b/mentos/src/mem/slab.c new file mode 100644 index 0000000..744c374 --- /dev/null +++ b/mentos/src/mem/slab.c @@ -0,0 +1,354 @@ +/// MentOS, The Mentoring Operating system project +/// @file mouse.h +/// @brief Driver for *PS2* Mouses. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +/// Change the header. +#define __DEBUG_HEADER__ "[SLAB ]" + +#include "mem/zone_allocator.h" +#include "mem/paging.h" +#include "assert.h" +#include "misc/debug.h" +#include "mem/slab.h" + +/// @brief Use it to manage cached pages. +typedef struct kmem_obj { + /// The list_head for this object. + list_head objlist; +} kmem_obj; + +/// Max order of kmalloc cache allocations, if greater raw page allocation is done. +#define MAX_KMALLOC_CACHE_ORDER 12 + +#define KMEM_OBJ_OVERHEAD sizeof(kmem_obj) +#define KMEM_START_OBJ_COUNT 8 +#define KMEM_MAX_REFILL_OBJ_COUNT 64 +#define KMEM_OBJ(cachep, addr) ((kmem_obj *)(addr)) +#define ADDR_FROM_KMEM_OBJ(cachep, kmem_obj) ((void *)(kmem_obj)) + +// The list of caches. +static list_head kmem_caches_list; +// Cache where we will store the data about caches. +static kmem_cache_t kmem_cache; +// Caches for each order of the malloc. +static kmem_cache_t *malloc_blocks[MAX_KMALLOC_CACHE_ORDER]; + +static int __alloc_slab_page(kmem_cache_t *cachep, gfp_t flags) +{ + page_t *page = _alloc_pages(flags, cachep->gfp_order); + if (!page) { + pr_crit("Failed to allocate a new page from slab.\n"); + return -1; + } + + list_head_init(&page->slabs); + + // Save in the root page the kmem_cache_t pointer, + // to allow freeing arbitrary pointers + page[0].container.slab_cache = cachep; + + // Update slab main pages of all child pages, to allow + // reconstructing which page handles a specified address + for (unsigned int i = 1; i < (1U << cachep->gfp_order); i++) { + page[i].container.slab_main_page = page; + } + + unsigned int slab_size = PAGE_SIZE * (1U << cachep->gfp_order); + + // Update the page objects counters + page->slab_objcnt = slab_size / cachep->size; + page->slab_objfree = page->slab_objcnt; + + unsigned int pg_addr = get_lowmem_address_from_page(page); + + list_head_init(&page->slab_freelist); + + // Build the objects structures + for (unsigned int i = 0; i < page->slab_objcnt; i++) { + kmem_obj *obj = KMEM_OBJ(cachep, pg_addr + cachep->size * i); + list_head_add(&obj->objlist, &page->slab_freelist); + } + + // Add the page to the slab list and update the counters + list_head_add(&page->slabs, &cachep->slabs_free); + cachep->total_num += page->slab_objcnt; + cachep->free_num += page->slab_objcnt; + + return 0; +} + +static void __kmem_cache_refill(kmem_cache_t *cachep, unsigned int free_num, gfp_t flags) +{ + while (cachep->free_num < free_num) { + if (__alloc_slab_page(cachep, flags) < 0) { + pr_warning("Cannot allocate a page, abort refill\n"); + break; + } + } +} + +static unsigned int __find_next_alignment(unsigned int size, unsigned int align) +{ + return (size / align + (size % align ? 1 : 0)) * align; +} + +static void __compute_size_and_order(kmem_cache_t *cachep) +{ + // Align the whole object to the required padding + cachep->size = __find_next_alignment( + max(cachep->object_size, KMEM_OBJ_OVERHEAD), + max(8, cachep->align)); + + // Compute the gfp order + unsigned int size = __find_next_alignment(cachep->size, PAGE_SIZE) / PAGE_SIZE; + while ((size /= 2) > 0) { + cachep->gfp_order++; + } +} + +static void __kmem_cache_create(kmem_cache_t *cachep, const char *name, unsigned int size, unsigned int align, slab_flags_t flags, void (*ctor)(void *), void (*dtor)(void *), unsigned int start_count) +{ + pr_info("Creating new cache `%s` with objects of size `%d`.\n", name, size); + + *cachep = (kmem_cache_t){ + .name = name, + .object_size = size, + .align = align, + .flags = flags, + .ctor = ctor, + .dtor = dtor + }; + + list_head_init(&cachep->slabs_free); + list_head_init(&cachep->slabs_partial); + list_head_init(&cachep->slabs_full); + + __compute_size_and_order(cachep); + + __kmem_cache_refill(cachep, start_count, flags); + + list_head_add(&cachep->cache_list, &kmem_caches_list); +} + +static inline void *__kmem_cache_alloc_slab(kmem_cache_t *cachep, page_t *slab_page) +{ + list_head *elem_listp = list_head_pop(&slab_page->slab_freelist); + if (!elem_listp) { + pr_warning("There are no FREE element inside the slab_freelist\n"); + return NULL; + } + slab_page->slab_objfree--; + cachep->free_num--; + + kmem_obj *obj = list_entry(elem_listp, kmem_obj, objlist); + + // Get the element from the kmem_obj object + void *elem = ADDR_FROM_KMEM_OBJ(cachep, obj); + + if (cachep->ctor) + cachep->ctor(elem); + + return elem; +} + +static inline void __kmem_cache_free_slab(kmem_cache_t *cachep, page_t *slab_page) +{ + cachep->free_num -= slab_page->slab_objfree; + cachep->total_num -= slab_page->slab_objcnt; + // Clear objcnt, used as a flag to check if the page belongs to the slab + slab_page->slab_objcnt = 0; + slab_page->container.slab_main_page = NULL; + + // Reset all non-root slab pages + for (unsigned int i = 1; i < (1U << cachep->gfp_order); i++) { + (slab_page + i)->container.slab_main_page = NULL; + } + + __free_pages(slab_page); +} + +void kmem_cache_init() +{ + // Initialize the list of caches. + list_head_init(&kmem_caches_list); + // Create a cache to store the data about caches. + __kmem_cache_create( + &kmem_cache, + "kmem_cache_t", + sizeof(kmem_cache_t), + alignof(kmem_cache_t), + GFP_KERNEL, + NULL, + NULL, 32); + for (unsigned int i = 0; i < MAX_KMALLOC_CACHE_ORDER; i++) { + malloc_blocks[i] = kmem_cache_create( + "kmalloc", + 1u << i, + 1u << i, + GFP_KERNEL, + NULL, + NULL); + } +} + +kmem_cache_t *kmem_cache_create(const char *name, unsigned int size, unsigned int align, slab_flags_t flags, void (*ctor)(void *), void (*dtor)(void *)) +{ + kmem_cache_t *cachep = (kmem_cache_t *)kmem_cache_alloc(&kmem_cache, GFP_KERNEL); + if (!cachep) + return cachep; + + __kmem_cache_create(cachep, name, size, align, flags, ctor, dtor, KMEM_START_OBJ_COUNT); + + return cachep; +} + +void kmem_cache_destroy(kmem_cache_t *cachep) +{ + while (!list_head_empty(&cachep->slabs_free)) { + list_head *slab_list = list_head_pop(&cachep->slabs_free); + __kmem_cache_free_slab(cachep, list_entry(slab_list, page_t, slabs)); + } + + while (!list_head_empty(&cachep->slabs_partial)) { + list_head *slab_list = list_head_pop(&cachep->slabs_partial); + __kmem_cache_free_slab(cachep, list_entry(slab_list, page_t, slabs)); + } + + while (!list_head_empty(&cachep->slabs_full)) { + list_head *slab_list = list_head_pop(&cachep->slabs_full); + __kmem_cache_free_slab(cachep, list_entry(slab_list, page_t, slabs)); + } + + kmem_cache_free(cachep); + list_head_del(&cachep->cache_list); +} + +#ifdef ENABLE_CACHE_TRACE +void *pr_kmem_cache_alloc(const char *file, const char *fun, int line, kmem_cache_t *cachep, gfp_t flags) +#else +void *kmem_cache_alloc(kmem_cache_t *cachep, gfp_t flags) +#endif +{ + if (list_head_empty(&cachep->slabs_partial)) { + if (list_head_empty(&cachep->slabs_free)) { + if (flags == 0) + flags = cachep->flags; + + // Refill the cache in an exponential fashion, capping at KMEM_MAX_REFILL_OBJ_COUNT to avoid + // too big allocations + __kmem_cache_refill(cachep, min(cachep->total_num, KMEM_MAX_REFILL_OBJ_COUNT), flags); + if (list_head_empty(&cachep->slabs_free)) { + pr_crit("Cannot allocate more slabs in `%s`\n", cachep->name); + return NULL; + } + } + + // Add a free slab to partial list because in any case an element will + // be removed before the function returns + list_head *free_slab = list_head_pop(&cachep->slabs_free); + list_head_add(free_slab, &cachep->slabs_partial); + } + + page_t *slab_page = list_entry(list_head_front(&cachep->slabs_partial), page_t, slabs); + void *ptr = __kmem_cache_alloc_slab(cachep, slab_page); + + // If the slab is now full, add it to the full slabs list + if (slab_page->slab_objfree == 0) { + list_head *slab_full_elem = list_head_pop(&cachep->slabs_partial); + list_head_add(slab_full_elem, &cachep->slabs_full); + } +#ifdef ENABLE_CACHE_TRACE + pr_notice("kmem_cache_alloc : (%-16s:%3d)[%-16s] : 0x%p\n", file, line, cachep->name, ptr); +#endif + return ptr; +} + +#ifdef ENABLE_CACHE_TRACE +void pr_kmem_cache_free(const char *file, const char *fun, int line, void *ptr) +#else +void kmem_cache_free(void *ptr) +#endif +{ + page_t *slab_page = get_lowmem_page_from_address((uint32_t)ptr); + + // If the slab main page is a lowmem page, change to it as it's the root page + if (is_lowmem_page_struct(slab_page->container.slab_main_page)) { + slab_page = slab_page->container.slab_main_page; + } + + kmem_cache_t *cachep = slab_page->container.slab_cache; + +#ifdef ENABLE_CACHE_TRACE + pr_notice("kmem_cache_free : (%-16s:%3d)[%-16s] : 0x%p\n", file, line, cachep->name, ptr); +#endif + if (cachep->dtor) + cachep->dtor(ptr); + + kmem_obj *obj = KMEM_OBJ(cachep, ptr); + + // Add object to the free list + list_head_add(&obj->objlist, &slab_page->slab_freelist); + slab_page->slab_objfree++; + cachep->free_num++; + + // Now page is completely free + if (slab_page->slab_objfree == slab_page->slab_objcnt) { + // Remove page from partial list + list_head_del(&slab_page->slabs); + // Add page to free list + list_head_add(&slab_page->slabs, &cachep->slabs_free); + } + // Now page is not full, so change its list + else if (slab_page->slab_objfree == 1) { + // Remove page from full list + list_head_del(&slab_page->slabs); + // Add page to partial list + list_head_add(&slab_page->slabs, &cachep->slabs_partial); + } +} + +#ifdef ENABLE_ALLOC_TRACE +void *pr_kmalloc(const char *file, const char *fun, int line, unsigned int size) +#else +void *kmalloc(unsigned int size) +#endif +{ + unsigned int order = 0; + while (size != 0) { + order++; + size /= 2; + } + + // If size does not fit in the maximum cache order, allocate raw pages + void *ptr; + if (order >= MAX_KMALLOC_CACHE_ORDER) { + ptr = (void *)__alloc_pages_lowmem(GFP_KERNEL, order - 12); + } else { + ptr = kmem_cache_alloc(malloc_blocks[order], GFP_KERNEL); + } +#ifdef ENABLE_ALLOC_TRACE + pr_notice("kmalloc : (%-16s:%3d) : 0x%p\n", file, line, ptr); +#endif + return ptr; +} + +#ifdef ENABLE_ALLOC_TRACE +void pr_kfree(const char *file, const char *fun, int line, void *ptr) +#else +void kfree(void *ptr) +#endif +{ +#ifdef ENABLE_ALLOC_TRACE + pr_notice("kfree : (%-16s:%3d) : 0x%p\n", file, line, ptr); +#endif + page_t *page = get_lowmem_page_from_address((uint32_t)ptr); + + // If the address is part of the cache + if (page->container.slab_main_page) { + kmem_cache_free(ptr); + } else { + free_pages_lowmem((uint32_t)ptr); + } +} diff --git a/mentos/src/mem/vmem_map.c b/mentos/src/mem/vmem_map.c new file mode 100644 index 0000000..2bed3cc --- /dev/null +++ b/mentos/src/mem/vmem_map.c @@ -0,0 +1,176 @@ +/// MentOS, The Mentoring Operating system project +/// @file vmem_map.c +/// @brief Virtual memory mapping routines. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +/// Change the header. +#define __DEBUG_HEADER__ "[VMEM ]" + +#include "mem/vmem_map.h" +#include "string.h" +#include "system/panic.h" + +/// Virtual addresses manager. +static virt_map_page_manager_t virt_default_mapping; + +/// TODO: check. +#define VIRTUAL_MEMORY_PAGES_COUNT (VIRTUAL_MEMORY_SIZE_MB * 256) +/// TODO: check. +#define VIRTUAL_MAPPING_BASE (PROCAREA_END_ADDR + 0x38000000) +/// TODO: check. +#define VIRT_PAGE_TO_ADDRESS(page) ((((page)-virt_pages) * PAGE_SIZE) + VIRTUAL_MAPPING_BASE) +/// TODO: check. +#define VIRT_ADDRESS_TO_PAGE(addr) ((((addr)-VIRTUAL_MAPPING_BASE) / PAGE_SIZE) + virt_pages) + +/// Array of virtual pages. +virt_map_page_t virt_pages[VIRTUAL_MEMORY_PAGES_COUNT]; + +void virt_init(void) +{ + buddy_system_init( + &virt_default_mapping.bb_instance, + "virt_manager", + virt_pages, + BBSTRUCT_OFFSET(virt_map_page_t, bbpage), + sizeof(virt_map_page_t), + VIRTUAL_MEMORY_PAGES_COUNT); + + page_directory_t *mainpgd = paging_get_main_directory(); + + uint32_t start_virt_pfn = VIRTUAL_MAPPING_BASE / PAGE_SIZE; + uint32_t start_virt_pgt = start_virt_pfn / 1024; + uint32_t start_virt_tbl_idx = start_virt_pfn % 1024; + + uint32_t pfn_num = VIRTUAL_MEMORY_PAGES_COUNT; + + // Alloc all page tables inside the main directory, so they will be shared across + // all page directories of processes + for (uint32_t i = start_virt_pgt; i < 1024 && (pfn_num > 0); i++) { + page_dir_entry_t *entry = mainpgd->entries + i; + + page_table_t *table; + + // Alloc virtual page table + entry->present = 1; + entry->rw = 0; + entry->global = 1; + entry->user = 0; + entry->accessed = 0; + entry->available = 1; + table = kmem_cache_alloc(pgtbl_cache, GFP_KERNEL); + + uint32_t start_page = (i == start_virt_pgt) ? start_virt_tbl_idx : 0; + + for (uint32_t j = start_page; j < 1024 && (pfn_num > 0); j++, pfn_num--) { + table->pages[j].frame = 0; + table->pages[j].rw = 0; + table->pages[j].present = 0; + table->pages[j].global = 1; + table->pages[j].user = 0; + } + + page_t *table_page = get_lowmem_page_from_address((uint32_t)table); + uint32_t phy_addr = get_physical_address_from_page(table_page); + entry->frame = phy_addr >> 12u; + } +} + +static virt_map_page_t *_alloc_virt_pages(uint32_t pfn_count) +{ + int order = find_nearest_order_greater(0, pfn_count << 12); + virt_map_page_t *vpage = PG_FROM_BBSTRUCT(bb_alloc_pages(&virt_default_mapping.bb_instance, order), virt_map_page_t, bbpage); + return vpage; +} + +uint32_t virt_map_physical_pages(page_t *page, int pfn_count) +{ + virt_map_page_t *vpage = _alloc_virt_pages(pfn_count); + if (!vpage) + return 0; + + uint32_t virt_address = VIRT_PAGE_TO_ADDRESS(vpage); + uint32_t phy_address = get_physical_address_from_page(page); + + mem_upd_vm_area(paging_get_main_directory(), virt_address, phy_address, + pfn_count * PAGE_SIZE, MM_PRESENT | MM_RW | MM_GLOBAL | MM_UPDADDR); + return virt_address; +} + +virt_map_page_t *virt_map_alloc(uint32_t size) +{ + uint32_t pages_count = (size + PAGE_SIZE - 1) / PAGE_SIZE; + return _alloc_virt_pages(pages_count); +} + +uint32_t virt_map_vaddress(mm_struct_t *mm, virt_map_page_t *vpage, uint32_t vaddr, uint32_t size) +{ + uint32_t start_map_virt_address = VIRT_PAGE_TO_ADDRESS(vpage); + + // Clone the source vaddr the the requested virtual memory portion + mem_clone_vm_area(mm->pgd, + paging_get_main_directory(), + vaddr, + start_map_virt_address, + size, + MM_PRESENT | MM_RW | MM_GLOBAL | MM_UPDADDR); + return start_map_virt_address; +} + +int virtual_check_address(uint32_t addr) +{ + return addr >= VIRTUAL_MAPPING_BASE; // && addr < VIRTUAL_MAPPING_BASE + VIRTUAL_MEMORY_PAGES_COUNT * PAGE_SIZE; +} + +void virt_unmap(uint32_t addr) +{ + virt_map_page_t *page = VIRT_ADDRESS_TO_PAGE(addr); + virt_unmap_pg(page); +} + +void virt_unmap_pg(virt_map_page_t *page) +{ + uint32_t addr = VIRT_PAGE_TO_ADDRESS(page); + + // Set all virtual pages as not present + mem_upd_vm_area(paging_get_main_directory(), addr, 0, + (1 << page->bbpage.order) * PAGE_SIZE, MM_GLOBAL); + + // and avoiding unwanted memory accesses by the kernel + bb_free_pages(&virt_default_mapping.bb_instance, &page->bbpage); +} + +// FIXME: Check if this function should support unaligned page-boundaries copy +void virt_memcpy(mm_struct_t *dst_mm, uint32_t dst_vaddr, mm_struct_t *src_mm, uint32_t src_vaddr, uint32_t size) +{ + const uint32_t VMEM_BUFFER_SIZE = 65536; + + uint32_t buffer_size = min(VMEM_BUFFER_SIZE, size); + + virt_map_page_t *src_vpage = virt_map_alloc(size); + virt_map_page_t *dst_vpage = virt_map_alloc(size); + + if (!src_vpage || !dst_vpage) { + kernel_panic("Cannot copy virtual memory address, unable to reserve vmem!"); + } + + for (;;) { + uint32_t src_map = virt_map_vaddress(src_mm, src_vpage, src_vaddr, buffer_size); + uint32_t dst_map = virt_map_vaddress(dst_mm, dst_vpage, dst_vaddr, buffer_size); + + uint32_t cpy_size = min(buffer_size, size); + + memcpy((void *)dst_map, (void *)src_map, cpy_size); + + if (size <= buffer_size) { + break; + } + + size -= cpy_size; + src_vaddr += cpy_size; + dst_vaddr += cpy_size; + } + + virt_unmap_pg(src_vpage); + virt_unmap_pg(dst_vpage); +} diff --git a/mentos/src/mem/zone_allocator.c b/mentos/src/mem/zone_allocator.c index db8b353..5c805d4 100644 --- a/mentos/src/mem/zone_allocator.c +++ b/mentos/src/mem/zone_allocator.c @@ -1,229 +1,204 @@ /// MentOS, The Mentoring Operating system project /// @file zone_allocator.c /// @brief Implementation of the Zone Allocator -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "zone_allocator.h" -#include "buddysystem.h" -#include "debug.h" -#include "kheap.h" +/// Change the header. +#define __DEBUG_HEADER__ "[PMM ]" + +#include "mem/zone_allocator.h" +#include "mem/buddysystem.h" +#include "klib/list_head.h" #include "kernel.h" #include "assert.h" -#include "paging.h" +#include "mem/paging.h" +#include "string.h" +#include "misc/debug.h" -#define MAX_ORDER_ALIGN(addr) \ - ((addr) & (~(PAGE_SIZE * (1 << (MAX_ORDER - 1)) - 1))) + \ - (PAGE_SIZE * (1 << (MAX_ORDER - 1))) - -/// Defined in kernel.ld, points at the end of kernel's data segment. -extern uint32_t end; - -/// End address of the kernel's data segment. -static uint8_t *_mmngr_memory_start = (uint8_t *)(&end); +/// TODO: Comment. +#define MIN_PAGE_ALIGN(addr) ((addr) & (~(PAGE_SIZE - 1))) +/// TODO: Comment. +#define MAX_PAGE_ALIGN(addr) (((addr) & (~(PAGE_SIZE - 1))) + PAGE_SIZE) +/// TODO: Comment. +#define MIN_ORDER_ALIGN(addr) ((addr) & (~((PAGE_SIZE << (MAX_BUDDYSYSTEM_GFP_ORDER - 1)) - 1))) +/// TODO: Comment. +#define MAX_ORDER_ALIGN(addr) \ + (((addr) & (~((PAGE_SIZE << (MAX_BUDDYSYSTEM_GFP_ORDER - 1)) - 1))) + \ + (PAGE_SIZE << (MAX_BUDDYSYSTEM_GFP_ORDER - 1))) /// Array of all physical blocks page_t *mem_map = NULL; - /// Memory node. pg_data_t *contig_page_data = NULL; +/// Low memory virtual base address. +uint32_t lowmem_virt_base = 0; +/// Low memory base address. +uint32_t lowmem_page_base = 0; -/// @brief Get the zone that contains a page frame. -/// @param page A page descriptor. -/// @return The zone requested. +page_t *get_lowmem_page_from_address(uint32_t addr) +{ + unsigned int offset = addr - lowmem_virt_base; + return mem_map + lowmem_page_base + (offset / PAGE_SIZE); +} + +uint32_t get_lowmem_address_from_page(page_t *page) +{ + unsigned int offset = (page - mem_map) - lowmem_page_base; + return lowmem_virt_base + offset * PAGE_SIZE; +} + +uint32_t get_physical_address_from_page(page_t *page) +{ + return (page - mem_map) * PAGE_SIZE; +} + +page_t *get_page_from_physical_address(uint32_t phy_addr) +{ + return mem_map + (phy_addr / PAGE_SIZE); +} + +/// @brief Get the zone that contains a page frame. +/// @param page A page descriptor. +/// @return The zone requested. static zone_t *get_zone_from_page(page_t *page) { - zone_t *zone = NULL; - int nr_zones = contig_page_data->nr_zones; - - for (int zone_index = 0; zone_index < nr_zones; zone_index++) { - zone = contig_page_data->node_zones + zone_index; - page_t *last_page = zone->zone_mem_map + zone->size; - - if (page < last_page) { - return zone; - } - } - - // Error: page is over memory size. - return (zone_t *)NULL; + zone_t *zone; + page_t *last_page; + // Iterate over all the zones. + for (int zone_index = 0; zone_index < contig_page_data->nr_zones; zone_index++) { + // Get the zone at the given index. + zone = contig_page_data->node_zones + zone_index; + assert(zone && "Failed to retrieve the zone."); + // Get the last page of the zone. + last_page = zone->zone_mem_map + zone->size; + assert(last_page && "Failed to retrieve the last page of the zone."); + // Check if the page is before the last page of the zone. + if (page < last_page) + return zone; + } + // Error: page is over memory size. + return (zone_t *)NULL; } -/// @brief Get a zone from gfp_mask -/// @param gfp_mask GFP_FLAG see gfp.h. -/// @return The zone requested. +/// @brief Get a zone from gfp_mask +/// @param gfp_mask GFP_FLAG see gfp.h. +/// @return The zone requested. static zone_t *get_zone_from_flags(gfp_t gfp_mask) { - switch (gfp_mask) { - case GFP_KERNEL: - case GFP_ATOMIC: - case GFP_NOFS: - case GFP_NOIO: - case GFP_NOWAIT: - return &contig_page_data->node_zones[ZONE_NORMAL]; - case GFP_HIGHUSER: - return &contig_page_data->node_zones[ZONE_HIGHMEM]; - default: - return (zone_t *)NULL; - } + switch (gfp_mask) { + case GFP_KERNEL: + case GFP_ATOMIC: + case GFP_NOFS: + case GFP_NOIO: + case GFP_NOWAIT: + return &contig_page_data->node_zones[ZONE_NORMAL]; + case GFP_HIGHUSER: + return &contig_page_data->node_zones[ZONE_HIGHMEM]; + default: + return (zone_t *)NULL; + } } -static bool_t is_memory_clean(gfp_t gfp_mask) +static int is_memory_clean(gfp_t gfp_mask) { - bool_t memory_clean = true; - zone_t *zone = get_zone_from_flags(gfp_mask); - assert((zone != NULL) && "Invalid zone flag!"); - -#ifdef ENABLE_BUDDYSYSTEM - /* Check every field nr_free of the buddy system - * descriptor of the zone that all blocks are - * allocated on the last free list. - */ - unsigned int order = 0; - for (; order < MAX_ORDER - 1; ++order) { - free_area_t *area = zone->free_area + order; - if (area->nr_free != 0) { - memory_clean = false; - - break; - } - } - - if (memory_clean && - (zone->free_area[order].nr_free != (zone->size / (1UL << order)))) { - memory_clean = false; - } - -#else - /* Check every field _count of the page descriptor - * of the zone as free. - */ - for (int i = 0; i < zone->size; ++i) { - page_t *page = zone->zone_mem_map + i; - if (page->_count != -1) { - memory_clean = false; - - break; - } - } -#endif - - return memory_clean; + // Get the corresponding zone. + zone_t *zone = get_zone_from_flags(gfp_mask); + assert(zone && "Failed to retrieve the zone given the gfp_mask!"); + // Get the last free area list of the buddy system. + bb_free_area_t *area = zone->buddy_system.free_area + (MAX_BUDDYSYSTEM_GFP_ORDER - 1); + assert(area && "Failed to retrieve the last free_area for the given zone!"); + // Compute the total size of the zone. + unsigned int total_size = (zone->size / (1UL << (MAX_BUDDYSYSTEM_GFP_ORDER - 1))); + // Check if the size of the zone is equal to the remaining pages inside the free area. + if (area->nr_free != total_size) { + pr_crit("Number of blocks of free pages is different than expected (%d vs %d).\n", area->nr_free, total_size); + buddy_system_dump(&zone->buddy_system); + return 0; + } + return 1; } /// @brief Checks if the physical memory manager is working properly. /// @return If the check was done correctly. -static bool_t pmm_check() +static int pmm_check() { - dbg_print( - "\n=================== ZONE ALLOCATOR TEST ==================== \n"); + pr_debug( + "\n=================== ZONE ALLOCATOR TEST ==================== \n"); - dbg_print("\t[STEP1] One page frame in kernel-space... "); - dbg_print("\n\t ===== [STEP1] One page frame in kernel-space ====\n"); - uint32_t ptr1 = __alloc_page(GFP_KERNEL); - free_page(ptr1); - if (!is_memory_clean(GFP_KERNEL)) { - return false; - } - - dbg_print("\t[STEP2] Five page frames in user-space... "); - dbg_print("\n\t ===== [STEP2] Five page frames in user-space ====\n"); - uint32_t ptr2[5]; - for (int i = 0; i < 5; i++) { - ptr2[i] = __alloc_page(GFP_HIGHUSER); - } - for (int i = 0; i < 5; i++) { - free_page(ptr2[i]); - } - if (!is_memory_clean(GFP_HIGHUSER)) { - return false; - } - - dbg_print("\t[STEP3] 2^{3} page frames in kernel-space... "); - dbg_print("\n\t ===== [STEP3] 2^{3} page frames in kernel-space ====\n"); - uint32_t ptr3 = __alloc_pages(GFP_KERNEL, 3); - free_pages(ptr3, 3); - if (!is_memory_clean(GFP_KERNEL)) { - return false; - } - - dbg_print("\t[STEP4] Five 2^{i} page frames in user-space... "); - dbg_print("\n\t ===== [STEP4] Five 2^{i} page frames in user-space ====\n"); - uint32_t ptr4[5]; - for (int i = 0; i < 5; i++) { - ptr4[i] = __alloc_pages(GFP_HIGHUSER, i); - } - for (int i = 0; i < 5; i++) { - free_pages(ptr4[i], i); - } - if (!is_memory_clean(GFP_HIGHUSER)) { - return false; - } - - dbg_print("\t[STEP5] Mixed page frames in kernel-space... "); - dbg_print("\n\t ===== [STEP5] Mixed page frames in kernel-space ====\n"); - int **ptr = (int **)__alloc_page(GFP_KERNEL); - int i = 0; - for (; i < 5; ++i) { - ptr[i] = (int *)__alloc_page(GFP_KERNEL); - } - for (; i < 20; ++i) { - ptr[i] = (int *)__alloc_pages(GFP_KERNEL, 2); - } - - int j = 0; - for (; j < 5; ++j) { - free_page((uint32_t)ptr[j]); - } - for (; j < 20; ++j) { - free_pages((uint32_t)ptr[j], 2); - } - free_page((uint32_t)ptr1); - - if (!is_memory_clean(GFP_KERNEL)) { - return false; - } - return true; -} - -/// @brief Initialize Buddy System. -/// @param zone A memory zone. -static void buddy_system_init(zone_t *zone) -{ - // Initialize the free_lists of each area of the zone. - for (unsigned int order = 0; order < MAX_ORDER; order++) { - free_area_t *area = zone->free_area + order; - area->nr_free = 0; - list_head_init(&area->free_list); + pr_debug("\t[STEP1] One page frame in kernel-space... "); + pr_debug("\n\t ===== [STEP1] One page frame in kernel-space ====\n"); + pr_debug("\n\t ----- ALLOC -------------------------------------\n"); + uint32_t ptr1 = __alloc_page_lowmem(GFP_KERNEL); + pr_debug("\n\t ----- FREE --------------------------------------\n"); + free_page_lowmem(ptr1); + if (!is_memory_clean(GFP_KERNEL)) { + pr_emerg("Test failed, memory is not clean.\n"); + return 0; } - // Current base page descriptor of the zone. - page_t *page = zone->zone_mem_map; - // Address of the last page descriptor of the zone. - page_t *last_page = page + zone->size; - - // Get the free area collecting the larges block of page frames. - const unsigned int order = MAX_ORDER - 1; - free_area_t *area = zone->free_area + order; - - // Add all zone's pages to the largest free area block. - uint32_t block_size = 1UL << order; - while ((page + block_size) <= last_page) { - /* page has already the _count field set to -1, - * therefore only save the order of the page. - */ - page->private = order; - - // Insert page as first element in the list. - list_head_add_tail(&page->lru, &area->free_list); - // Increase the number of free block of the free_area_t. - area->nr_free++; - - page += block_size; + pr_debug("\t[STEP2] Five page frames in user-space... "); + pr_debug("\n\t ===== [STEP2] Five page frames in user-space ====\n"); + page_t *ptr2[5]; + for (int i = 0; i < 5; i++) { + ptr2[i] = _alloc_pages(GFP_HIGHUSER, 0); + } + for (int i = 0; i < 5; i++) { + __free_pages(ptr2[i]); + } + if (!is_memory_clean(GFP_HIGHUSER)) { + pr_emerg("Test failed, memory is not clean.\n"); + return 0; } - assert(page == last_page && - "Memory size is not aligned to MAX_ORDER size!"); + pr_debug("\t[STEP3] 2^{3} page frames in kernel-space... "); + pr_debug("\n\t ===== [STEP3] 2^{3} page frames in kernel-space ====\n"); + uint32_t ptr3 = __alloc_pages_lowmem(GFP_KERNEL, 3); + free_pages_lowmem(ptr3); + if (!is_memory_clean(GFP_KERNEL)) { + pr_emerg("Test failed, memory is not clean.\n"); + return 0; + } + + pr_debug("\t[STEP4] Five 2^{i} page frames in user-space... "); + pr_debug("\n\t ===== [STEP4] Five 2^{i} page frames in user-space ====\n"); + page_t *ptr4[5]; + for (int i = 0; i < 5; i++) { + ptr4[i] = _alloc_pages(GFP_HIGHUSER, i); + } + for (int i = 0; i < 5; i++) { + __free_pages(ptr4[i]); + } + if (!is_memory_clean(GFP_HIGHUSER)) { + pr_emerg("Test failed, memory is not clean.\n"); + return 0; + } + + pr_debug("\t[STEP5] Mixed page frames in kernel-space... "); + pr_debug("\n\t ===== [STEP5] Mixed page frames in kernel-space ====\n"); + int **ptr = (int **)__alloc_page_lowmem(GFP_KERNEL); + int i = 0; + for (; i < 5; ++i) { + ptr[i] = (int *)__alloc_page_lowmem(GFP_KERNEL); + } + for (; i < 20; ++i) { + ptr[i] = (int *)__alloc_pages_lowmem(GFP_KERNEL, 2); + } + + int j = 0; + for (; j < 5; ++j) { + free_page_lowmem((uint32_t)ptr[j]); + } + for (; j < 20; ++j) { + free_pages_lowmem((uint32_t)ptr[j]); + } + free_page_lowmem((uint32_t)ptr1); + + if (!is_memory_clean(GFP_KERNEL)) { + pr_emerg("Test failed, memory is not clean.\n"); + return 0; + } + return 1; } /// @brief Initializes the memory attributes. @@ -231,244 +206,274 @@ static void buddy_system_init(zone_t *zone) /// @param zone_index Zone's index. /// @param adr_from the lowest address of the zone /// @param adr_to the highest address of the zone (not included!) -static void zone_init(char *name, int zone_index, uint32_t adr_from, - uint32_t adr_to) +static void zone_init(char *name, int zone_index, uint32_t adr_from, uint32_t adr_to) { - assert((adr_from < adr_to) && ((adr_from & 0xfffff000) == adr_from) && - ((adr_to & 0xfffff000) == adr_to) && - "Inserted bad block addresses!"); + assert((adr_from < adr_to) && "Inserted bad block addresses!"); + assert(((adr_from & 0xfffff000) == adr_from) && "Inserted bad block addresses!"); + assert(((adr_to & 0xfffff000) == adr_to) && "Inserted bad block addresses!"); + assert((zone_index < contig_page_data->nr_zones) && "The index is above the number of zones."); + // Take the zone_t structure that correspondes to the zone_index. + zone_t *zone = contig_page_data->node_zones + zone_index; + assert(zone && "Failed to retrieve the zone."); + // Number of page frames in the zone. + size_t num_page_frames = (adr_to - adr_from) / PAGE_SIZE; + // Index of the first page frame of the zone. + uint32_t first_page_frame = adr_from / PAGE_SIZE; + // Update zone info. + zone->name = name; + zone->size = num_page_frames; + zone->free_pages = num_page_frames; + zone->zone_mem_map = mem_map + first_page_frame; + zone->zone_start_pfn = first_page_frame; + // Dump the information. + pr_debug("ZONE %s, first page: %p, last page: %p, npages:%d\n", zone->name, + zone->zone_mem_map, zone->zone_mem_map + zone->size, zone->size); + // Set to zero all page structures. + memset(zone->zone_mem_map, 0, zone->size * sizeof(page_t)); + // Initialize the buddy system for the new zone. + buddy_system_init(&zone->buddy_system, + name, + zone->zone_mem_map, + BBSTRUCT_OFFSET(page_t, bbpage), + sizeof(page_t), + num_page_frames); + buddy_system_dump(&zone->buddy_system); +} - // Take the zone_t structure that correspondes to the zone_index. - zone_t *zone = contig_page_data->node_zones + zone_index; +/* + * AAAABBBBCCCC + * ZZZZZZ + * + * */ - // Number of page frames in the zone. - size_t num_page_frames = (adr_to - adr_from) / PAGE_SIZE; +unsigned int find_nearest_order_greater(uint32_t base_addr, uint32_t amount) +{ + uint32_t start_pfn = base_addr / PAGE_SIZE; + uint32_t end_pfn = (base_addr + amount + PAGE_SIZE - 1) / PAGE_SIZE; + // Get the number of pages. + uint32_t npages = end_pfn - start_pfn; + // Find the fitting order. + unsigned int order = 0; + while ((1UL << order) < npages) { + ++order; + } + return order; +} - // Index of the first page frame of the zone. - uint32_t first_page_frame = adr_from / PAGE_SIZE; +int pmmngr_init(boot_info_t *boot_info) +{ + //======================================================================= - // Update zone info. - zone->name = name; - zone->size = num_page_frames; - zone->free_pages = num_page_frames; - zone->zone_mem_map = mem_map + first_page_frame; - zone->zone_start_pfn = first_page_frame; + uint32_t lowmem_phy_start = boot_info->lowmem_phy_start; - dbg_print("ZONE %s, first page: %p, last page: %p, npages:%d\n", zone->name, - zone->zone_mem_map, zone->zone_mem_map + zone->size, zone->size); + // Now we have skipped all modules in physical space, is time to + // consider also virtual lowmem space! + uint32_t lowmem_virt_start = boot_info->lowmem_start + (lowmem_phy_start - boot_info->lowmem_phy_start); -#ifdef ENABLE_BUDDYSYSTEM - buddy_system_init(zone); - buddy_system_dump(zone); + pr_debug("Start memory address after skip modules (phy => virt) : 0x%p => 0x%p \n", + lowmem_phy_start, lowmem_virt_start); + //======================================================================= + + //==== Initialize array of page_t ======================================= + pr_debug("Initializing low memory map structure...\n"); + mem_map = (page_t *)lowmem_virt_start; + + uint32_t mem_size = boot_info->highmem_phy_end; + + // Total number of blocks (all lowmem+highmem RAM). + uint32_t mem_num_frames = mem_size / PAGE_SIZE; + + // Initialize each page_t. + for (int page_index = 0; page_index < mem_num_frames; ++page_index) { + page_t *page = mem_map + page_index; + // Mark page as free. + set_page_count(page, 0); + } + //======================================================================= + + //==== Skip memory space used for page_t[] ============================== + lowmem_phy_start += sizeof(page_t) * mem_num_frames; + lowmem_virt_start += sizeof(page_t) * mem_num_frames; + pr_debug("Size of mem_map : %i byte [0x%p - 0x%p]\n", + (char *)lowmem_virt_start - (char *)mem_map, mem_map, + lowmem_virt_start); + //======================================================================= + + //==== Initialize contig_page_data node ================================= + pr_debug("Initializing contig_page_data node...\n"); + contig_page_data = (pg_data_t *)lowmem_virt_start; + // ZONE_NORMAL and ZONE_HIGHMEM + contig_page_data->nr_zones = __MAX_NR_ZONES; + // NID start from 0. + contig_page_data->node_id = 0; + // Corresponds with mem_map. + contig_page_data->node_mem_map = mem_map; + // In UMA we have only one node. + contig_page_data->node_next = NULL; + // All the memory. + contig_page_data->node_size = mem_num_frames; + // mem_map[0]. + contig_page_data->node_start_mapnr = 0; + // The first physical page. + contig_page_data->node_start_paddr = 0x0; + //======================================================================= + + //==== Skip memory space used for pg_data_t ============================= + lowmem_phy_start += sizeof(pg_data_t); + lowmem_virt_start += sizeof(pg_data_t); + //======================================================================= + + //==== Initialize zones zone_t ========================================== + pr_debug("Initializing zones...\n"); + + // ZONE_NORMAL [ memory_start - mem_size/4 ] + uint32_t start_normal_addr = MAX_PAGE_ALIGN(lowmem_phy_start); + uint32_t stop_normal_addr = MIN_PAGE_ALIGN(boot_info->lowmem_phy_end); + + // Move the stop address so that the size is a multiple of max buddysystem order + uint32_t normal_area_size = MIN_ORDER_ALIGN(stop_normal_addr - start_normal_addr); + stop_normal_addr = start_normal_addr + normal_area_size; + + uint32_t phv_delta = start_normal_addr - lowmem_phy_start; + lowmem_virt_base = lowmem_virt_start + phv_delta; + lowmem_page_base = start_normal_addr / PAGE_SIZE; + zone_init("Normal", ZONE_NORMAL, start_normal_addr, stop_normal_addr); + + // ZONE_HIGHMEM [ mem_size/4 - mem_size ] + uint32_t start_high_addr = MAX_PAGE_ALIGN((uint32_t)boot_info->highmem_phy_start); + uint32_t stop_high_addr = MIN_PAGE_ALIGN(boot_info->highmem_phy_end); + + // Move the stop address so that the size is a multiple of max buddysystem order + uint32_t high_area_size = MIN_ORDER_ALIGN(stop_high_addr - start_high_addr); + stop_high_addr = start_high_addr + high_area_size; + + zone_init("HighMem", ZONE_HIGHMEM, start_high_addr, stop_high_addr); + //======================================================================= + + pr_debug("Memory Size : %u MB \n", mem_size / M); + pr_debug("Total page frames (MemorySize/4096) : %u \n", mem_num_frames); + pr_debug("mem_map address : 0x%p \n", mem_map); + pr_debug("Memory Start : 0x%p \n", lowmem_phy_start); + + // With the caching enabled, the pmm check is useless. + //return pmm_check(); + return 1; +} + +page_t *alloc_page_cached(gfp_t gfp_mask) +{ + zone_t *zone = get_zone_from_flags(gfp_mask); + return PG_FROM_BBSTRUCT(bb_alloc_page_cached(&zone->buddy_system), page_t, bbpage); +} + +void free_page_cached(page_t *page) +{ + zone_t *zone = get_zone_from_page(page); + bb_free_page_cached(&zone->buddy_system, &page->bbpage); +} + +uint32_t __alloc_page_lowmem(gfp_t gfp_mask) +{ + return get_lowmem_address_from_page(alloc_page_cached(gfp_mask)); +} + +void free_page_lowmem(uint32_t addr) +{ + page_t *page = get_lowmem_page_from_address(addr); + free_page_cached(page); +} + +uint32_t __alloc_pages_lowmem(gfp_t gfp_mask, uint32_t order) +{ + assert((order <= (MAX_BUDDYSYSTEM_GFP_ORDER - 1)) && gfp_mask == GFP_KERNEL && "Order is exceeding limit."); + + page_t *page = _alloc_pages(gfp_mask, order); + + // Get the index of the first page frame of the block. + uint32_t block_frame_adr = get_lowmem_address_from_page(page); + if (block_frame_adr == -1) { + pr_emerg("MEM. REQUEST FAILED"); + } +#if 0 + else { + pr_debug("BS-G: addr: %p (page: %p order: %d)\n", block_frame_adr, page, order); + } #endif + return block_frame_adr; } -unsigned int find_nearest_order_greater(uint32_t amount) +page_t *_alloc_pages(gfp_t gfp_mask, uint32_t order) { - amount = (amount + PAGE_SIZE - 1) / PAGE_SIZE; - unsigned int order = 0; - while ((1UL << order) < amount) { - ++order; - } + uint32_t block_size = 1UL << order; - return order; + zone_t *zone = get_zone_from_flags(gfp_mask); + page_t *page = NULL; + + // Search for a block of page frames by using the BuddySystem. + page = PG_FROM_BBSTRUCT(bb_alloc_pages(&zone->buddy_system, order), page_t, bbpage); + + // Set page counters + for (int i = 0; i < block_size; i++) { + set_page_count(&page[i], 1); + } + + assert(page && "Cannot allocate pages."); + + // Decrement the number of pages in the zone. + if (page) { + zone->free_pages -= block_size; + } + + return page; } -bool_t pmmngr_init(uint32_t mem_size) +void free_pages_lowmem(uint32_t addr) { - //==== Get RAM size ===================================================== - // Align mem_size to a page frame size, namely 4KB. - uint32_t _mmngr_memory_size = MAX_ORDER_ALIGN(mem_size); - - // Total number of blocks (all RAM). - uint32_t _mmngr_num_frames = _mmngr_memory_size / PAGE_SIZE; - //======================================================================= - - //==== Skip modules ===================================================== - dbg_print("[PMM] Start memory address previous skip modules : 0x%p \n", - _mmngr_memory_start); - - for (int i = 0; i < MAX_MODULES; i++) { - uint8_t *skip_addr = (uint8_t *)module_end[i]; - if (skip_addr != NULL && _mmngr_memory_start < skip_addr) { - _mmngr_memory_start = skip_addr; - } - } - - dbg_print("[PMM] Start memory address after skip modules : 0x%p \n", - _mmngr_memory_start); - //======================================================================= - - //==== Initialize array of page_t ======================================= - dbg_print("[PMM] Initializing memory map structure...\n"); - mem_map = (page_t *)_mmngr_memory_start; - - // Initialize each page_t. - for (int page_index = 0; page_index < _mmngr_num_frames; ++page_index) { - page_t *page = mem_map + page_index; - // Mark page as free. - page->_count = -1; - list_head_init(&(page->lru)); - } - //======================================================================= - - //==== Skip memory space used for page_t[] ============================== - _mmngr_memory_start += sizeof(page_t) * _mmngr_num_frames; - dbg_print( - "[PMM] Size of mem_map : %i byte [0x%p - 0x%p]\n", - (void *)_mmngr_memory_start - (void *)mem_map, mem_map, - _mmngr_memory_start); - //======================================================================= - - //==== Initialize contig_page_data node ================================= - dbg_print("[PMM] Initializing contig_page_data node...\n"); - contig_page_data = (pg_data_t *)_mmngr_memory_start; - // ZONE_NORMAL and ZONE_HIGHMEM - contig_page_data->nr_zones = __MAX_NR_ZONES; - // NID start from 0. - contig_page_data->node_id = 0; - // Corresponds with mem_map. - contig_page_data->node_mem_map = mem_map; - // In UMA we have only one node. - contig_page_data->node_next = NULL; - // All the memory. - contig_page_data->node_size = _mmngr_num_frames; - // mem_map[0]. - contig_page_data->node_start_mapnr = 0; - // The first physical page. - contig_page_data->node_start_paddr = 0x0; - //======================================================================= - - //==== Skip memory space used for pg_data_t ============================= - _mmngr_memory_start += sizeof(pg_data_t); - //======================================================================= - - //==== Initialize zones zone_t ========================================== - dbg_print("[PMM] Initializing zones...\n"); - - // ZONE_NORMAL [ memory_start - mem_size/4 ] - uint32_t start_normal_addr = MAX_ORDER_ALIGN((uint32_t)_mmngr_memory_start); - uint32_t stop_normal_addr = MAX_ORDER_ALIGN(_mmngr_memory_size >> 2); - zone_init("Normal", ZONE_NORMAL, start_normal_addr, stop_normal_addr); - - // ZONE_HIGHMEM [ mem_size/4 - mem_size ] - uint32_t start_high_addr = stop_normal_addr; - uint32_t stop_high_addr = _mmngr_memory_size; - zone_init("HighMem", ZONE_HIGHMEM, start_high_addr, stop_high_addr); - //======================================================================= - - dbg_print("[PMM] Memory Size : %u MB \n", - _mmngr_memory_size / M); - dbg_print("[PMM] Total page frames (MemorySize/4096) : %u \n", - _mmngr_num_frames); - dbg_print("[PMM] mem_map address : 0x%p \n", - mem_map); - dbg_print("[PMM] Memory Start : 0x%p \n", - _mmngr_memory_start); - - return pmm_check(); + page_t *page = get_lowmem_page_from_address(addr); + assert(page && "Page is over memory size."); + __free_pages(page); } -uint32_t __alloc_page(gfp_t gfp_mask) +void __free_pages(page_t *page) { - return __alloc_pages(gfp_mask, 0); -} + zone_t *zone = get_zone_from_page(page); + assert(zone && "Page is over memory size."); -void free_page(uint32_t addr) -{ - free_pages(addr, 0); -} + assert(zone->zone_mem_map <= page && "Page is below the selected zone!"); -uint32_t __alloc_pages(gfp_t gfp_mask, uint32_t order) -{ - zone_t *zone = get_zone_from_flags(gfp_mask); - assert((zone != NULL) && "Invalid zone flag!"); - assert((order <= (MAX_ORDER - 1)) && "Invalid order!"); + uint32_t order = page->bbpage.order; + uint32_t block_size = 1UL << order; - int block_size = 1UL << order; + for (int i = 0; i < block_size; i++) { + set_page_count(&page[i], 0); + } - page_t *page = NULL; -#ifdef ENABLE_BUDDYSYSTEM - // Search for a block of page frames by using the BuddySystem. - page = bb_alloc_pages(zone, order); + bb_free_pages(&zone->buddy_system, &page->bbpage); -#else - // First page of the zone. - page_t *block = zone->zone_mem_map; - // Last page of the zone. - page_t *last_frame = zone->zone_mem_map + zone->size - order + 1; - // When true, then we found a block of pages - int found = 0; - // Search for a block of pages - while (block < last_frame && found == 0) { - // Suppose this is the right block. - found = 1; - // Check if enough pages are available in current block. - for (unsigned int i = 0; i < block_size; ++i) { - found = ((block + i)->_count == -1); - if (!found) { - /* The block is not large enough. We have to skip it - * and restart to search for another one. - */ - block += i + 1; - - break; - } - } - - if (found) { - // Set the found block of pages as taken, and not available. - for (unsigned int i = 0; i < block_size; ++i) { - (block + i)->_count = 0; - } - } - } - page = (found == 1) ? block : NULL; + zone->free_pages += block_size; +#if 0 + pr_debug("BS-F: (page: %p order: %d)\n", page, order); #endif - - uint32_t block_frame_adr = -1; - if (page != NULL) { - // Decrement the number of pages in the zone. - zone->free_pages -= block_size; - // Get the index of the first page frame of the block. - uint32_t delta_block_frame = (uint32_t)(page - zone->zone_mem_map); - block_frame_adr = zone->zone_start_pfn + delta_block_frame; - block_frame_adr = block_frame_adr * PAGE_SIZE; - } - - if (block_frame_adr == -1) { - dbg_print("MEM. REQUEST FAILED"); - } else { - dbg_print("BS-G: addr: %p (page: %p order: %d)\n", block_frame_adr, - page, order); - } - - return block_frame_adr; + //buddy_system_dump(&zone->buddy_system); } -void free_pages(uint32_t addr, uint32_t order) +unsigned long get_zone_total_space(gfp_t gfp_mask) { - page_t *page = mem_map + (addr / PAGE_SIZE); - zone_t *zone = get_zone_from_page(page); - assert((zone != NULL) && "Page is over memory size"); - - int block_size = 1UL << order; - -#ifdef ENABLE_BUDDYSYSTEM - bb_free_pages(zone, page, order); -#else - // Set the given block of page frames as free, and available again. - for (unsigned int i = 0; i < block_size; ++i) { - (page + i)->_count = -1; - } -#endif - - zone->free_pages += block_size; - - dbg_print("BS-F: addr: %p (page: %p order: %d)\n", addr, page, order); + zone_t *zone = get_zone_from_flags(gfp_mask); + assert(zone && "Cannot retrieve the correct zone."); + return buddy_system_get_total_space(&zone->buddy_system); } -uint32_t get_memory_start() +unsigned long get_zone_free_space(gfp_t gfp_mask) { - return (uint32_t)_mmngr_memory_start; + zone_t *zone = get_zone_from_flags(gfp_mask); + assert(zone && "Cannot retrieve the correct zone."); + return buddy_system_get_free_space(&zone->buddy_system); } + +unsigned long get_zone_cached_space(gfp_t gfp_mask) +{ + zone_t *zone = get_zone_from_flags(gfp_mask); + assert(zone && "Cannot retrieve the correct zone."); + return buddy_system_get_cached_space(&zone->buddy_system); +} \ No newline at end of file diff --git a/mentos/src/misc/bitops.c b/mentos/src/misc/bitops.c deleted file mode 100644 index 7bcecbf..0000000 --- a/mentos/src/misc/bitops.c +++ /dev/null @@ -1,38 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file bitops.c -/// @brief Bitmasks functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "bitops.h" -#include "video.h" - -int find_first_bit(unsigned short int irq_mask) -{ - int i = 0; - if (irq_mask == 0) { - return 0; - } - for (i = 0; i < 8; i++) { - if ((1 << i) & irq_mask) { - break; - } - } - - return i; -} - -bool_t has_flag(uint32_t flags, uint32_t flag) -{ - return (bool_t)((flags & flag) != 0); -} - -void set_flag(uint32_t *flags, uint32_t flag) -{ - (*flags) |= flag; -} - -void clear_flag(uint32_t *flags, uint32_t flag) -{ - (*flags) &= ~flag; -} diff --git a/mentos/src/misc/clock.c b/mentos/src/misc/clock.c deleted file mode 100644 index cfb2918..0000000 --- a/mentos/src/misc/clock.c +++ /dev/null @@ -1,178 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file clock.c -/// @brief Clock functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "clock.h" -#include "timer.h" -#include "stdio.h" -#include "stddef.h" -#include "port_io.h" - -time_t get_millisecond() -{ - return timer_get_subticks(); -} - -time_t get_second() -{ - outportb(0x70, SECOND_RTC); - time_t c = inportb(0x71); - - return c; -} - -time_t get_minute() -{ - outportb(0x70, MINUTE_RTC); - time_t c = inportb(0x71); - - return c; -} - -time_t get_hour() -{ - outportb(0x70, HOUR_RTC); - time_t c = inportb(0x71); - - return c; -} - -time_t get_day_w() -{ - outportb(0x70, DAY_W_RTC); - time_t c = inportb(0x71); - - return c; -} - -time_t get_day_m() -{ - outportb(0x70, DAY_M_RTC); - time_t c = inportb(0x71); - - return c; -} - -time_t get_month() -{ - outportb(0x70, MONTH_RTC); - time_t c = inportb(0x71); - - return c; -} - -time_t get_year() -{ - outportb(0x70, YEAR_RTC); - time_t c = inportb(0x71); - - return c; -} - -char *get_month_lng() -{ - switch (get_month()) { - case 1: - return "January"; - case 2: - return "February"; - case 3: - return "March"; - case 4: - return "April"; - case 5: - return "May"; - case 6: - return "June"; - case 7: - return "July"; - case 8: - return "August"; - case 9: - return "September"; - case 10: - return "October"; - case 11: - return "November"; - case 12: - return "December"; - default: - break; - } - return ""; -} - -char *get_day_lng() -{ - switch (get_day_w()) { - case 1: - return "Sunday"; - case 2: - return "Monday"; - case 3: - return "Tuesday"; - case 4: - return "Wednesday"; - case 5: - return "Thursday"; - case 6: - return "Friday"; - case 7: - return "Saturday"; - default: - break; - } - return ""; -} - -time_t time(time_t *timer) -{ - // Jan 1, 1970 - time_t t = 0; - t += get_millisecond(); - t += get_second(); - t += get_minute() * 60; - t += get_hour() * 3600; - t += get_day_m() * 86400; - t += get_month() * 2629743; - t += (1970 - get_year()) * 31556926; - if (timer != NULL) { - (*timer) = t; - } - - return t; -} - -time_t difftime(time_t time1, time_t time2) -{ - return time1 - time2; -} - -void strhourminutesecond(char *dst) -{ - time_t s, m, h; - s = get_second(); - m = get_minute(); - h = get_hour(); - - sprintf(dst, "%i:%i:%i", h, m, s); -} - -void strdaymonthyear(char *dst) -{ - sprintf(dst, "%s %i %s %i", get_day_lng(), get_day_m(), get_month_lng(), - 1970 - get_year()); -} - -void strdatehour(char *dst) -{ - time_t s, m, h; - s = get_second(); - m = get_minute(); - h = get_hour(); - - sprintf(dst, "%s %i %s %i %i:%i:%i", get_day_lng(), get_day_m(), - get_month_lng(), 1970 - get_year(), h, m, s); -} diff --git a/mentos/src/misc/debug.c b/mentos/src/misc/debug.c index 2c4cd5e..c3e2be5 100644 --- a/mentos/src/misc/debug.c +++ b/mentos/src/misc/debug.c @@ -1,99 +1,194 @@ /// MentOS, The Mentoring Operating system project /// @file debug.c /// @brief Debugging primitives. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "debug.h" -#include "stdio.h" +#include "sys/bitops.h" +#include "misc/debug.h" +#include "klib/spinlock.h" +#include "io/port_io.h" +#include "kernel.h" #include "string.h" -#include "spinlock.h" +#include "stdio.h" +#include "io/video.h" /// Serial port for QEMU. #define SERIAL_COM1 (0x03F8) +/// Determines the log level. +static int max_log_level = LOGLEVEL_DEBUG; -#define DEBUG_BUFFER_SIZE 1024 - -static inline void dbg_putchar(char c) +void dbg_putchar(char c) { #if (defined(DEBUG_STDIO) || defined(DEBUG_LOG)) - outportb(SERIAL_COM1, c); + outportb(SERIAL_COM1, (uint8_t)c); #endif } -static inline void dbg_print_header(const char *file, const char *fun, int line) +void dbg_puts(const char *s) { - static char prefix[300], final_prefix[300]; - - sprintf(prefix, "[%s:%s:%d", file, fun, line); - - sprintf(final_prefix, "%-40s] ", prefix); - - for (register int it = 0; final_prefix[it] != 0; ++it) { - dbg_putchar(final_prefix[it]); - } +#if (defined(DEBUG_STDIO) || defined(DEBUG_LOG)) + while ((*s) != 0) + dbg_putchar(*s++); +#endif } -void _dbg_print(const char *file, const char *fun, int line, const char *msg, - ...) +static inline void __debug_print_header(const char *file, const char *fun, int line, int log_level, char *header) { - // Define a buffer for the formatted string. - static char formatted[DEBUG_BUFFER_SIZE]; - static bool_t new_line = true; - - // Stage 1: FORMAT - if (strlen(msg) >= 1024) { - return; - } - // Start variabile argument's list. - va_list ap; - va_start(ap, msg); - // Format the message. - vsprintf(formatted, msg, ap); - // End the list of arguments. - va_end(ap); - - // Stage 2: SEND - if (new_line) { - dbg_print_header(file, fun, line); - new_line = false; - } - for (int it = 0; (formatted[it] != 0) && (it < DEBUG_BUFFER_SIZE); ++it) { - dbg_putchar(formatted[it]); - if (formatted[it] != '\n') { - continue; - } - if ((it + 1) >= DEBUG_BUFFER_SIZE) { - continue; - } - if (formatted[it + 1] == 0) { - new_line = true; - } else { - dbg_print_header(file, fun, line); - } - } + static char tmp_prefix[BUFSIZ], final_prefix[BUFSIZ]; + if (log_level == LOGLEVEL_EMERG) + dbg_puts(FG_RED_BRIGHT); + else if (log_level == LOGLEVEL_ALERT) + dbg_puts(FG_RED_BRIGHT); + else if (log_level == LOGLEVEL_CRIT) + dbg_puts(FG_RED); + else if (log_level == LOGLEVEL_ERR) + dbg_puts(FG_RED); + else if (log_level == LOGLEVEL_WARNING) + dbg_puts(FG_YELLOW_BRIGHT); + else if (log_level == LOGLEVEL_DEBUG) + dbg_puts(FG_CYAN); + else if (log_level == LOGLEVEL_NOTICE) + dbg_puts(FG_RESET); + else if (log_level == LOGLEVEL_INFO) + dbg_puts(FG_RESET); + else + dbg_puts(FG_RESET); + dbg_putchar('['); + dbg_puts( + (log_level == LOGLEVEL_EMERG) ? " EM " /*"EMERG "*/ : + (log_level == LOGLEVEL_ALERT) ? " AL " /*"ALERT "*/ : + (log_level == LOGLEVEL_CRIT) ? " CR " /*"CRIT "*/ : + (log_level == LOGLEVEL_ERR) ? " ER " /*"ERR "*/ : + (log_level == LOGLEVEL_WARNING) ? " WR " /*"WARNING"*/ : + (log_level == LOGLEVEL_NOTICE) ? " NT " /*"NOTICE "*/ : + (log_level == LOGLEVEL_INFO) ? " IN " /*"INFO "*/ : + (log_level == LOGLEVEL_DEBUG) ? " DB " /*"DEBUG "*/ : + " DF " /*"DEFAULT*/); + dbg_putchar('|'); + sprintf(tmp_prefix, "%s:%d", file, line); + sprintf(final_prefix, " %-20s ", tmp_prefix); + dbg_puts(final_prefix); + dbg_putchar('|'); + sprintf(final_prefix, " %-25s ]", fun); + dbg_puts(final_prefix); + dbg_putchar(' '); + if (header) { + dbg_puts(header); + dbg_putchar(' '); + } } -void print_intrframe(pt_regs *frame) +void set_log_level(int level) { - dbg_print("Interrupt stack frame:\n"); - dbg_print("GS = 0x%-04x\n", frame->gs); - dbg_print("FS = 0x%-04x\n", frame->fs); - dbg_print("ES = 0x%-04x\n", frame->es); - dbg_print("DS = 0x%-04x\n", frame->ds); - dbg_print("EDI = 0x%-09x\n", frame->edi); - dbg_print("ESI = 0x%-09x\n", frame->esi); - dbg_print("EBP = 0x%-09x\n", frame->ebp); - dbg_print("ESP = 0x%-09x\n", frame->esp); - dbg_print("EBX = 0x%-09x\n", frame->ebx); - dbg_print("EDX = 0x%-09x\n", frame->edx); - dbg_print("ECX = 0x%-09x\n", frame->ecx); - dbg_print("EAX = 0x%-09x\n", frame->eax); - dbg_print("INT_NO = %-9d\n", frame->int_no); - dbg_print("ERR_CD = %-9d\n", frame->err_code); - dbg_print("EIP = 0x%-09x\n", frame->eip); - dbg_print("CS = 0x%-04x\n", frame->cs); - dbg_print("EFLAGS = 0x%-09x\n", frame->eflags); - dbg_print("UESP = 0x%-09x\n", frame->useresp); - dbg_print("SS = 0x%-04x\n", frame->ss); + if ((level >= LOGLEVEL_EMERG) && (level <= LOGLEVEL_DEBUG)) + max_log_level = level; +} + +int get_log_level() +{ + return max_log_level; +} + +void dbg_printf(const char *file, const char *fun, int line, char *header, const char *format, ...) +{ + // Define a buffer for the formatted string. + static char formatted[BUFSIZ]; + static short new_line = 1; + + // Stage 1: FORMAT + if (strlen(format) >= BUFSIZ) + return; + + // Check the log level. + int log_level = LOGLEVEL_DEFAULT; + if ((format[0] != '\0') && (format[0] == KERN_SOH_ASCII)) { + // Remove the Start Of Header. + ++format; + // Compute the log level. + log_level = (format[0] - '0'); + // Check the log_level. + if ((log_level < LOGLEVEL_EMERG) || (log_level > LOGLEVEL_DEBUG)) + log_level = 8; + // Remove the log_level; + ++format; + } + if (log_level > max_log_level) { + return; + } + + // Start variabile argument's list. + va_list ap; + va_start(ap, format); + // Format the message. + vsprintf(formatted, format, ap); + // End the list of arguments. + va_end(ap); + + // Stage 2: SEND + if (new_line) { + __debug_print_header(file, fun, line, log_level, header); + new_line = false; + } + for (int it = 0; (formatted[it] != 0) && (it < BUFSIZ); ++it) { + dbg_putchar(formatted[it]); + if (formatted[it] != '\n') { + continue; + } + if ((it + 1) >= BUFSIZ) { + continue; + } + if (formatted[it + 1] == 0) { + new_line = true; + } else { + __debug_print_header(file, fun, line, log_level, header); + } + } +} + +void dbg_print_regs(pt_regs *frame) +{ + pr_debug("Interrupt stack frame:\n"); + pr_debug("GS = 0x%-04x\n", frame->gs); + pr_debug("FS = 0x%-04x\n", frame->fs); + pr_debug("ES = 0x%-04x\n", frame->es); + pr_debug("DS = 0x%-04x\n", frame->ds); + pr_debug("EDI = 0x%-09x\n", frame->edi); + pr_debug("ESI = 0x%-09x\n", frame->esi); + pr_debug("EBP = 0x%-09x\n", frame->ebp); + pr_debug("ESP = 0x%-09x\n", frame->esp); + pr_debug("EBX = 0x%-09x\n", frame->ebx); + pr_debug("EDX = 0x%-09x\n", frame->edx); + pr_debug("ECX = 0x%-09x\n", frame->ecx); + pr_debug("EAX = 0x%-09x\n", frame->eax); + pr_debug("INT_NO = %-9d\n", frame->int_no); + pr_debug("ERR_CD = %-9d\n", frame->err_code); + pr_debug("EIP = 0x%-09x\n", frame->eip); + pr_debug("CS = 0x%-04x\n", frame->cs); + pr_debug("EFLAGS = 0x%-09x\n", frame->eflags); + pr_debug("UESP = 0x%-09x\n", frame->useresp); + pr_debug("SS = 0x%-04x\n", frame->ss); +} + +const char *to_human_size(unsigned long bytes) +{ + static char output[200]; + const char *suffix[] = { "B", "KB", "MB", "GB", "TB" }; + char length = sizeof(suffix) / sizeof(suffix[0]); + int i = 0; + double dblBytes = bytes; + if (bytes > 1024) { + for (i = 0; (bytes / 1024) > 0 && i < length - 1; i++, bytes /= 1024) + dblBytes = bytes / 1024.0; + } + sprintf(output, "%.02lf %2s", dblBytes, suffix[i]); + return output; +} + +const char *dec_to_binary(unsigned long value) +{ + static char buffer[33]; + for (int i = 0; i < 32; ++i) + buffer[i] = bit_check(value, 31 - i) ? '1' : '0'; + return buffer; } diff --git a/mentos/src/multiboot.c b/mentos/src/multiboot.c index 5fc60fa..59f18bc 100644 --- a/mentos/src/multiboot.c +++ b/mentos/src/multiboot.c @@ -1,80 +1,205 @@ /// MentOS, The Mentoring Operating system project /// @file multiboot.c /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. +/// Set the debug level to 0. +#define __DEBUG_LEVEL__ 0 + #include "multiboot.h" -#include "bitops.h" -#include "debug.h" +#include "kernel.h" +#include "sys/bitops.h" +#include "stddef.h" +#include "misc/debug.h" +#include "system/panic.h" +#include "stddef.h" + + +multiboot_memory_map_t *mmap_first_entry(multiboot_info_t *info) +{ + if (!bitmask_check(info->flags, MULTIBOOT_FLAG_MMAP)) + return NULL; + return (multiboot_memory_map_t *)((uintptr_t)info->mmap_addr); +} + +multiboot_memory_map_t *mmap_first_entry_of_type(multiboot_info_t *info, + uint32_t type) +{ + multiboot_memory_map_t *entry = mmap_first_entry(info); + if (entry && (entry->type == type)) + return entry; + return mmap_next_entry_of_type(info, entry, type); +} + +multiboot_memory_map_t *mmap_next_entry(multiboot_info_t *info, + multiboot_memory_map_t *entry) +{ + uintptr_t next = ((uintptr_t)entry) + entry->size + sizeof(entry->size); + if (next >= (info->mmap_addr + info->mmap_length)) + return NULL; + return (multiboot_memory_map_t *)next; +} + +multiboot_memory_map_t *mmap_next_entry_of_type(multiboot_info_t *info, + multiboot_memory_map_t *entry, + uint32_t type) +{ + do { + entry = mmap_next_entry(info, entry); + } while (entry && entry->type != type); + return entry; +} + +char *mmap_type_name(multiboot_memory_map_t *entry) +{ + if (entry->type == MULTIBOOT_MEMORY_AVAILABLE) + return "AVAILABLE"; + if (entry->type == MULTIBOOT_MEMORY_RESERVED) + return "RESERVED"; + return "NONE"; +} + +multiboot_module_t *first_module(multiboot_info_t *info) +{ + if (!bitmask_check(info->flags, MULTIBOOT_FLAG_MODS)) + return NULL; + if (!info->mods_count) + return NULL; + return (multiboot_module_t *)(uintptr_t)info->mods_addr; +} + +multiboot_module_t *next_module(multiboot_info_t *info, + multiboot_module_t *mod) +{ + multiboot_module_t *first = (multiboot_module_t *)((uintptr_t)info->mods_addr); + ++mod; + if ((mod - first) >= info->mods_count) + return NULL; + return mod; +} void dump_multiboot(multiboot_info_t *mbi) { - dbg_print("\n--------------------------------------------------\n"); - dbg_print("MULTIBOOT header at 0x%x:\n", mbi); - dbg_print("Flags : 0x%x\n", mbi->flags); - if (has_flag(mbi->flags, MULTIBOOT_FLAG_MEM)) { - dbg_print("Mem Lo: 0x%x\n", mbi->mem_lower * K); - dbg_print("Mem Hi: 0x%x (%dMB)\n", mbi->mem_upper * K, - (mbi->mem_upper / 1024)); - } - if (has_flag(mbi->flags, MULTIBOOT_FLAG_DEVICE)) { - dbg_print("Boot d: 0x%x\n", mbi->boot_device); - } - if (has_flag(mbi->flags, MULTIBOOT_FLAG_CMDLINE)) { - dbg_print("cmdlin: 0x%x (%s)\n", mbi->cmdline, (char *)mbi->cmdline); - } - if (has_flag(mbi->flags, MULTIBOOT_FLAG_MODS)) { - dbg_print("Mods : 0x%x\n", mbi->mods_count); + pr_debug("\n--------------------------------------------------\n"); + pr_debug("MULTIBOOT header at 0x%x:\n", mbi); - multiboot_module_t *mod = (multiboot_module_t *)mbi->mods_addr; + // Print out the flags. + pr_debug("%-16s = 0x%x\n", "flags", mbi->flags); - if (mbi->mods_count > 0) { - for (uint32_t i = 0; i < mbi->mods_count && i < MAX_MODULES; - i++, mod++) { - uint32_t start = mod->mod_start; - uint32_t end = mod->mod_end; - dbg_print("\tModule %d is at 0x%x:0x%x\n", i + 1, start, end); - } + // Are mem_* valid? + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_MEM)) { + pr_debug("%-16s = %u Kb (%u Mb)\n", "mem_lower", mbi->mem_lower, + mbi->mem_lower / K); + pr_debug("%-16s = %u Kb (%u Mb)\n", "mem_upper", mbi->mem_upper, + mbi->mem_upper / K); + pr_debug("%-16s = %u Kb (%u Mb)\n", "total", + mbi->mem_lower + mbi->mem_upper, + (mbi->mem_lower + mbi->mem_upper) / K); + } - /* Last implementation - for (uint32_t i = 0; i < mbi->mods_count; ++i) - { - // uint32_t start = *((uint32_t *) (mbi->mods_addr + 8 * i)); - uint32_t start = mbi->mods_addr + 8 * i; - // uint32_t end = *((uint32_t *) (mbi->mods_addr + 8 * i + 4)); - uint32_t end = mbi->mods_addr + 8 * i + 4; - dbg_print("\tModule %d is at 0x%x:0x%x\n", i + 1, start, end); - } - */ - } - } - if (has_flag(mbi->flags, MULTIBOOT_FLAG_AOUT)) { - dbg_print("AOUT t : 0x%x\n", mbi->u.aout_sym.tabsize); - dbg_print("AOUT s : 0x%x\n", mbi->u.aout_sym.strsize); - dbg_print("AOUT a : 0x%x\n", mbi->u.aout_sym.addr); - dbg_print("AOUT r : 0x%x\n", mbi->u.aout_sym.reserved); - } - if (has_flag(mbi->flags, MULTIBOOT_FLAG_ELF)) { - dbg_print("ELF n : 0x%x\n", mbi->u.elf_sec.num); - dbg_print("ELF s : 0x%x\n", mbi->u.elf_sec.size); - dbg_print("ELF a : 0x%x\n", mbi->u.elf_sec.addr); - dbg_print("ELF h : 0x%x\n", mbi->u.elf_sec.shndx); - } - dbg_print("MMap : 0x%x\n", mbi->mmap_length); - dbg_print("Addr : 0x%x\n", mbi->mmap_addr); - dbg_print("Drives: 0x%x\n", mbi->drives_length); - dbg_print("Addr : 0x%x\n", mbi->drives_addr); - dbg_print("Config: 0x%x\n", mbi->config_table); - dbg_print("Loader: 0x%x (%s)\n", mbi->boot_loader_name, - (char *)mbi->boot_loader_name); - dbg_print("APM : 0x%x\n", mbi->apm_table); - dbg_print("VBE Co: 0x%x\n", mbi->vbe_control_info); - dbg_print("VBE Mo: 0x%x\n", mbi->vbe_mode_info); - dbg_print("VBE In: 0x%x\n", mbi->vbe_mode); - dbg_print("VBE se: 0x%x\n", mbi->vbe_interface_seg); - dbg_print("VBE of: 0x%x\n", mbi->vbe_interface_off); - dbg_print("VBE le: 0x%x\n", mbi->vbe_interface_len); - dbg_print("--------------------------------------------------\n"); - dbg_print("\n"); + // Is boot_device valid? + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_DEVICE)) { + pr_debug("%-16s = 0x%x (0x%x)", "boot_device", mbi->boot_device); + switch ((mbi->boot_device) & 0xFF000000) { + case 0x00000000: + pr_debug("(floppy)\n"); + break; + case 0x80000000: + pr_debug("(disk)\n"); + break; + default: + pr_debug("(unknown)\n"); + } + } + + // Is the command line passed? + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_CMDLINE)) { + pr_debug("%-16s = %s\n", "cmdline", (char *)mbi->cmdline); + } + + // Are mods_* valid? + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_MODS)) { + pr_debug("%-16s = %d\n", "mods_count", mbi->mods_count); + pr_debug("%-16s = 0x%x\n", "mods_addr", mbi->mods_addr); + multiboot_module_t *mod = first_module(mbi); + for (int i = 0; mod; ++i, mod = next_module(mbi, mod)) { + pr_debug(" [%2d] " + "mod_start = 0x%x, " + "mod_end = 0x%x, " + "cmdline = %s\n", + i, mod->mod_start, mod->mod_end, (char *)mod->cmdline); + } + } + // Bits 4 and 5 are mutually exclusive! + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_AOUT) && + bitmask_check(mbi->flags, MULTIBOOT_FLAG_ELF)) { + kernel_panic("Both bits 4 and 5 are set.\n"); + return; + } + + // Is the symbol table of a.out valid? + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_AOUT)) { + multiboot_aout_symbol_table_t *multiboot_aout_sym = &(mbi->u.aout_sym); + pr_debug("multiboot_aout_symbol_table: tabsize = 0x%0x, " + "strsize = 0x%x, addr = 0x%x\n", + multiboot_aout_sym->tabsize, multiboot_aout_sym->strsize, + multiboot_aout_sym->addr); + } + + // Is the section header table of ELF valid? + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_ELF)) { + multiboot_elf_section_header_table_t *multiboot_elf_sec = + &(mbi->u.elf_sec); + pr_debug("multiboot_elf_sec: num = %u, size = 0x%x," + " addr = 0x%x, shndx = 0x%x\n", + multiboot_elf_sec->num, multiboot_elf_sec->size, + multiboot_elf_sec->addr, multiboot_elf_sec->shndx); + } + + // Are mmap_* valid? + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_MMAP)) { + pr_debug("%-16s = 0x%x\n", "mmap_addr", mbi->mmap_addr); + pr_debug("%-16s = 0x%x (%d entries)\n", "mmap_length", + mbi->mmap_length, + mbi->mmap_length / sizeof(multiboot_memory_map_t)); + multiboot_memory_map_t *mmap = mmap_first_entry(mbi); + for (int i = 0; mmap; ++i, mmap = mmap_next_entry(mbi, mmap)) { + pr_debug(" [%2d] " + "base_addr = 0x%09x%09x, " + "length = 0x%09x%09x, " + "type = 0x%x (%s)\n", + i, mmap->base_addr_high, mmap->base_addr_low, + mmap->length_high, mmap->length_low, mmap->type, + mmap_type_name(mmap)); + } + } + + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_DRIVE_INFO)) { + pr_debug("Drives: 0x%x\n", mbi->drives_length); + pr_debug("Addr : 0x%x\n", mbi->drives_addr); + } + + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_CONFIG_TABLE)) { + pr_debug("Config: 0x%x\n", mbi->config_table); + } + + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_BOOT_LOADER_NAME)) { + pr_debug("boot_loader_name: %s\n", (char *)mbi->boot_loader_name); + } + + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_APM_TABLE)) { + pr_debug("APM : 0x%x\n", mbi->apm_table); + } + + if (bitmask_check(mbi->flags, MULTIBOOT_FLAG_VBE_INFO)) { + pr_debug("VBE Co: 0x%x\n", mbi->vbe_control_info); + pr_debug("VBE Mo: 0x%x\n", mbi->vbe_mode_info); + pr_debug("VBE In: 0x%x\n", mbi->vbe_mode); + pr_debug("VBE se: 0x%x\n", mbi->vbe_interface_seg); + pr_debug("VBE of: 0x%x\n", mbi->vbe_interface_off); + pr_debug("VBE le: 0x%x\n", mbi->vbe_interface_len); + } + pr_debug("--------------------------------------------------\n"); + pr_debug("\n"); } diff --git a/mentos/src/process/process.c b/mentos/src/process/process.c index ae2eabc..b2eb124 100644 --- a/mentos/src/process/process.c +++ b/mentos/src/process/process.c @@ -1,308 +1,498 @@ /// MentOS, The Mentoring Operating system project /// @file process.c /// @brief Process data structures and functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "process.h" -#include "prio.h" -#include "init.h" -#include "panic.h" -#include "kheap.h" -#include "debug.h" -#include "unistd.h" + +#include "sys/kernel_levels.h" + +//#ifndef __DEBUG_LEVEL__ +//#define __DEBUG_LEVEL__ LOGLEVEL_DEBUG +//#endif + +#include "process/process.h" +#include "process/scheduler.h" +#include "assert.h" +#include "libgen.h" #include "string.h" -#include "list_head.h" -#include "stdatomic.h" -#include "scheduler.h" - -#define PUSH_ON_STACK(stack, type, item) \ - *((type *)(stack -= sizeof(type))) = item +#include "hardware/timer.h" +#include "sys/errno.h" +#include "fcntl.h" +#include "system/panic.h" +#include "misc/debug.h" +#include "process/wait.h" +#include "process/prio.h" +#include "fs/vfs.h" +#include "elf/elf.h" +/// Cache for creating the task structs. +static kmem_cache_t *task_struct_cache; /// @brief The task_struct of the init process. static task_struct *init_proc; -void exit_handler() +static inline int __push_args_on_stack(uintptr_t *esp, char *args[], char ***argsptr) { - exit(1); - kernel_panic("I should not be here.\n"); + int argc = 0; + char *args_ptr[256]; + // Count the number of arguments. + while (args[argc] != NULL) { + ++argc; + } + + // Prepare args with space for the terminating NULL. + for (int i = argc - 1; i >= 0; --i) { + for (int j = strlen(args[i]); j >= 0; --j) { + PUSH_ARG((*esp), char, args[i][j]); + } + args_ptr[i] = (char *)(*esp); + } + // Push terminating NULL. + PUSH_ARG((*esp), char *, (char *)NULL); + // Push array of pointers to the arguments. + for (int i = argc - 1; i >= 0; --i) { + PUSH_ARG((*esp), char *, args_ptr[i]); + } + (*argsptr) = (char **)(*esp); + + return argc; } -task_struct *create_init_process() +static int __reset_process(task_struct *task) { - dbg_print("Building init process...\n"); - // Create a new task_struct. - init_proc = kmalloc(sizeof(task_struct)); - // TODO: process is IN USER SPACE! it should be in KERNEL SPACE! - memset(init_proc, 0, sizeof(task_struct)); - // Set the id of the process. - init_proc->pid = get_new_pid(); - // Set the name of the process. - strcpy(init_proc->name, "init"); - // Set the statistics of the process. - init_proc->se.prio = DEFAULT_PRIO; - init_proc->se.start_runtime = 0; - init_proc->se.exec_start = 0; - init_proc->se.sum_exec_runtime = 0; - init_proc->se.vruntime = 0; - // Initialize the list_head. - list_head_init(&init_proc->run_list); - // Initialize the children list_head. - list_head_init(&init_proc->children); - // Initialize the sibling list_head. - list_head_init(&init_proc->sibling); - // Create a new stack segment. - init_proc->mm = create_process_image(DEFAULT_STACK_SIZE); - char *stack = (char *)init_proc->mm->start_stack; - // Clean stack space. - memset(stack, 0, DEFAULT_STACK_SIZE); - // Set the base address of the stack. - char *ebp = (char *)(stack + DEFAULT_STACK_SIZE); - // Create a pointer to keep track of the top of the stack. - char *esp = ebp; - // Set exit_handler as terminating function for init. - PUSH_ON_STACK(esp, uintptr_t, (uintptr_t)&exit_handler); - // Set the top address of the stack. - init_proc->thread.useresp = (uintptr_t)esp; - // Set the base address of the stack. - init_proc->thread.ebp = (uintptr_t)ebp; - // Set the program counter. - init_proc->thread.eip = (uintptr_t)&main_init; - // Enable the interrupts. - init_proc->thread.eflags = init_proc->thread.eflags | EFLAG_IF; - // Clear the current working directory. - memset(init_proc->cwd, '\0', MAX_PATH_LENGTH); - // Set the state of the process as running. - init_proc->state = TASK_RUNNING; - // Active the current process. - enqueue_task(init_proc); + pr_debug("__reset_process(%p `%s`)\n", task, task->name); + // Create a new stack segment. + task->mm = create_blank_process_image(DEFAULT_STACK_SIZE); + if (task->mm == NULL) { + pr_err("Failed to initialize process mm structure.\n"); + return 0; + } - dbg_print("--------------------------------------------------\n"); - dbg_print("- %s process (PID: %d, eflags: %d)\n", init_proc->name, - init_proc->pid, init_proc->thread.eflags); - dbg_print("\tStack: [0x%p - 0x%p]\n", init_proc->mm->start_stack, - init_proc->mm->start_stack + DEFAULT_STACK_SIZE); - dbg_print("\tebp: 0x%p\n", init_proc->thread.ebp); - dbg_print("\tesp: 0x%p\n", init_proc->thread.useresp); - dbg_print("\teip: 0x%p\n", init_proc->thread.eip); - dbg_print("--------------------------------------------------\n"); + // Save the current page directory. + page_directory_t *crtdir = paging_get_current_directory(); + // FIXME: Now to clear the stack a pgdir switch is made, it should be a kernel mmapping. + paging_switch_directory_va(task->mm->pgd); - return init_proc; + // Clean stack space. + memset((char *)task->mm->start_stack, 0, DEFAULT_STACK_SIZE); + // Set the base address of the stack. + task->thread.regs.ebp = (uintptr_t)(task->mm->start_stack + DEFAULT_STACK_SIZE); + // Set the top address of the stack. + task->thread.regs.useresp = task->thread.regs.ebp; + // Enable the interrupts. + task->thread.regs.eflags = task->thread.regs.eflags | EFLAG_IF; + + // Restore previous pgdir + paging_switch_directory(crtdir); + + return 1; } -char *get_current_dir_name() +static int __load_executable(const char *path, task_struct *task, uint32_t *entry) { - task_struct *current_process = kernel_get_current_process(); - if (current_process != NULL) { - return strdup(current_process->cwd); - } - - return kstrdup("/"); + pr_debug("__load_executable(`%s`, %p `%s`, %p)\n", path, task, task->name, entry); + vfs_file_t *file = vfs_open(path, O_RDONLY, 0); + if (file == NULL) { + pr_err("Cannot find executable!\n"); + return 0; + } + // Check that the file is actually an executable before destroying the `mm`. + if (!elf_check_file_type(file, ET_EXEC)) { + pr_err("This is not a valid ELF executable `%s`!\n", path); + return 0; + } + // FIXME: When threads will be implemented + // they should share the mm, so the destroy_process_image must be called + // only when all the threads are terminated. This can be accomplished by using + // an internal counter on the mm. + if (task->mm) + destroy_process_image(task->mm); + // Return code variable. + int ret = 0; + // Recreate the memory of the process. + if (__reset_process(task)) { + // Load the elf file, check if 0 is returned and print the error. + if (!(ret = elf_load_file(task, file, entry))) { + pr_err("Failed to load ELF file `%s`!\n", path); + } + } + // Close the file. + vfs_close(file); + return ret; } -void sys_getcwd(char *path, size_t size) +static inline int __count_args_bytes(char **args) { - task_struct *current_process = kernel_get_current_process(); - if ((current_process != NULL) && (path != NULL)) { - strncpy(path, current_process->cwd, size); - } + int argc = 0; + int bytes = 0; + + // Count the number of arguments. + while (args[argc] != NULL) { + ++argc; + } + + for (int i = 0; i < argc; i++) { + bytes += strlen(args[i]) + 1; + } + + return bytes + (argc + 1 /* The NULL terminator */) * sizeof(char *); +} + +static inline task_struct *__alloc_task(task_struct *source, task_struct *parent, const char *name) +{ + // Create a new task_struct. + task_struct *proc = kmem_cache_alloc(task_struct_cache, GFP_KERNEL); + // Clear the memory. + memset(proc, 0, sizeof(task_struct)); + // Set the id of the process. + proc->pid = scheduler_getpid(); + // Set the state of the process as running. + proc->state = TASK_RUNNING; + // Set the current opened file descriptors and the maximum number of file descriptors. + if (source) + vfs_dup_task(proc, source); + else + vfs_init_task(proc); + // Set the pointer to process's parent. + proc->parent = parent; + // Initialize the list_head. + list_head_init(&proc->run_list); + // Initialize the children list_head. + list_head_init(&proc->children); + // Initialize the sibling list_head. + list_head_init(&proc->sibling); + // If we have a parent, set the sibling child relation. + if (parent) { + // Set the new_process as child of current. + list_head_add_tail(&proc->sibling, &parent->children); + } + if (source) + memcpy(&proc->thread, &source->thread, sizeof(thread_struct_t)); + // Set the statistics of the process. + proc->uid = 0; + proc->sid = 0; + proc->gid = 0; + proc->se.prio = DEFAULT_PRIO; + proc->se.start_runtime = timer_get_ticks(); + proc->se.exec_start = timer_get_ticks(); + proc->se.exec_runtime = 0; + proc->se.sum_exec_runtime = 0; + proc->se.vruntime = 0; + proc->se.period = 0; + proc->se.deadline = 0; + proc->se.arrivaltime = timer_get_ticks(); + proc->se.executed = false; + proc->se.is_periodic = false; + proc->se.is_under_analysis = false; + proc->se.next_period = 0; + proc->se.worst_case_exec = 0; + proc->se.utilization_factor = 0; + // Initialize the exit code of the process. + proc->exit_code = 0; + // Copy the name. + if (name) + strcpy(proc->name, name); + // Do not touch the task's segments. + proc->mm = NULL; + // Initialize the error number. + proc->error_no = 0; + // Initialize the current working directory. + if (source) + strcpy(proc->cwd, source->cwd); + else + strcpy(proc->cwd, "/"); + // Clear the signal handler. + memset(&proc->sighand, 0x00, sizeof(sighand_t)); + spinlock_init(&proc->sighand.siglock); + atomic_set(&proc->sighand.count, 0); + for (int i = 0; i < NSIG; ++i) { + proc->sighand.action[i].sa_handler = SIG_DFL; + sigemptyset(&proc->sighand.action[i].sa_mask); + proc->sighand.action[i].sa_flags = 0; + } + // Clear the masks. + sigemptyset(&proc->blocked); + sigemptyset(&proc->real_blocked); + sigemptyset(&proc->saved_sigmask); + // Initialzie the data structure storing the pending signals. + list_head_init(&proc->pending.list); + sigemptyset(&proc->pending.signal); + + // Initalize real_timer for intervals + proc->real_timer = NULL; + + return proc; +} + +int init_tasking() +{ + if ((task_struct_cache = KMEM_CREATE(task_struct)) == NULL) { + return 0; + } + return 1; +} + +task_struct *process_create_init(const char *path) +{ + pr_debug("Building init process...\n"); + // Allocate the memory for the process. + init_proc = __alloc_task(NULL, NULL, "init"); + + // == INITIALIZE `/proc/video` ============================================ + // Check that the fd_list is initialized. + assert(init_proc->fd_list && "File descriptor list not initialized."); + assert((init_proc->max_fd > 3) && "File descriptor list cannot contain the standard IOs."); + + // Create STDIN descriptor. + vfs_file_t *stdin = vfs_open("/proc/video", O_RDONLY, 0); + stdin->count++; + init_proc->fd_list[STDIN_FILENO].file_struct = stdin; + init_proc->fd_list[STDIN_FILENO].flags_mask = O_RDONLY; + pr_debug("`/proc/video` stdin : %p\n", stdin); + + // Create STDOUT descriptor. + vfs_file_t *stdout = vfs_open("/proc/video", O_WRONLY, 0); + stdout->count++; + init_proc->fd_list[STDOUT_FILENO].file_struct = stdout; + init_proc->fd_list[STDOUT_FILENO].flags_mask = O_WRONLY; + pr_debug("`/proc/video` stdout : %p\n", stdout); + + // Create STDERR descriptor. + vfs_file_t *stderr = vfs_open("/proc/video", O_WRONLY, 0); + stderr->count++; + init_proc->fd_list[STDERR_FILENO].file_struct = stderr; + init_proc->fd_list[STDERR_FILENO].flags_mask = O_WRONLY; + pr_debug("`/proc/video` stderr : %p\n", stderr); + // ------------------------------------------------------------------------ + + // == INITIALIZE TASK MEMORY ============================================== + // Load the executable. + if (!__load_executable(path, init_proc, &init_proc->thread.regs.eip)) { + pr_err("Entry for init: %d\n", init_proc->thread.regs.eip); + kernel_panic("Init not valid (%d)!"); + } + // ------------------------------------------------------------------------ + + // == INITIALIZE PROGRAM ARGUMENTS ======================================== + // Save the current page directory. + page_directory_t *crtdir = paging_get_current_directory(); + // Switch to init page directory. + paging_switch_directory_va(init_proc->mm->pgd); + + // Prepare argv and envp for the init process. + char **argv_ptr, **envp_ptr; + int argc = 1; + static char *argv[] = { + "/bin/init", + (char *)NULL + }; + static char *envp[] = { + (char *)NULL + }; + // Save where the arguments start. + init_proc->mm->arg_start = init_proc->thread.regs.useresp; + // Push the arguments on the stack. + __push_args_on_stack(&init_proc->thread.regs.useresp, argv, &argv_ptr); + // Save where the arguments end. + init_proc->mm->arg_end = init_proc->thread.regs.useresp; + // Save where the environmental variables start. + init_proc->mm->env_start = init_proc->thread.regs.useresp; + // Push the environment on the stack. + __push_args_on_stack(&init_proc->thread.regs.useresp, envp, &envp_ptr); + // Save where the environmental variables end. + init_proc->mm->env_end = init_proc->thread.regs.useresp; + // Push the `main` arguments on the stack (argc, argv, envp). + PUSH_ARG(init_proc->thread.regs.useresp, char **, envp_ptr); + PUSH_ARG(init_proc->thread.regs.useresp, char **, argv_ptr); + PUSH_ARG(init_proc->thread.regs.useresp, int, argc); + + // Restore previous pgdir + paging_switch_directory(crtdir); + // ------------------------------------------------------------------------ + + // Active the current process. + scheduler_enqueue_task(init_proc); + + pr_debug("Executing '%s' (pid: %d)...\n", init_proc->name, init_proc->pid); + + return init_proc; +} + +char *sys_getcwd(char *buf, size_t size) +{ + task_struct *current_process = scheduler_get_current_process(); + if ((current_process != NULL) && (buf != NULL)) { + strncpy(buf, current_process->cwd, size); + return buf; + } + return (char *)-EACCES; } void sys_chdir(char const *path) { - task_struct *current_process = kernel_get_current_process(); - if ((current_process != NULL) && (path != NULL)) { - strcpy(current_process->cwd, path); - } + task_struct *current_process = scheduler_get_current_process(); + if ((current_process != NULL) && (path != NULL)) { + char absolute_path[PATH_MAX]; + realpath(path, absolute_path); + strcpy(current_process->cwd, absolute_path); + } } -pid_t sys_vfork(pt_regs *r) +void sys_fchdir(int fd) { - task_struct *current = kernel_get_current_process(); - if (current == NULL) { - kernel_panic("There is no current process!"); - } - - dbg_print("Forking '%s'(%d) process...\n", current->name, current->pid); - - // Create a new task_struct. - // TODO: process is IN USER SPACE! it should be in KERNEL SPACE! - task_struct *new_process = kmalloc(sizeof(task_struct)); - - // TODO: this is NOT a deep copy. should a deep copy be used here? - memcpy(new_process, current, sizeof(task_struct)); - - // Set the id of the process. - new_process->pid = get_new_pid(); - - // Set the statistics of the process. - new_process->se.prio = DEFAULT_PRIO; - new_process->se.start_runtime = 0; - new_process->se.exec_start = 0; - new_process->se.sum_exec_runtime = 0; - // TODO: vruntime should be the scheduled highest values so far. - new_process->se.vruntime = current->se.vruntime; - - // Create a new stack segment. - new_process->mm = create_process_image(DEFAULT_STACK_SIZE); - char *stack = (char *)new_process->mm->start_stack; - // Copy the father's stack. - memcpy((char *)new_process->mm->start_stack, - (char *)current->mm->start_stack, DEFAULT_STACK_SIZE); - // Set the base address of the stack. - char *ebp = stack + DEFAULT_STACK_SIZE; // TODO: da controllare - // Create a pointer to keep track of the top of the stack. - char *esp = stack + (r->useresp - current->mm->start_stack); - - // Set the top address of the stack. - new_process->thread.useresp = (uintptr_t)esp; - // Set the base address of the stack. - new_process->thread.ebp = (uintptr_t)ebp; - // Set the program counter. - new_process->thread.eip = r->eip; - - // Set the base registers. - new_process->thread.eax = 0; - new_process->thread.ebx = r->ebx; - new_process->thread.ecx = r->ecx; - new_process->thread.edx = r->edx; - - // Enable the interrupts. - new_process->thread.eflags = new_process->thread.eflags | EFLAG_IF; - - // Set the state of the process as running. - new_process->state = TASK_RUNNING; - - // Set current as parent for the new process - new_process->parent = current; - - // Initialize the list_head. - list_head_init(&new_process->run_list); - - // Initialize the children list_head. - list_head_init(&new_process->children); - - // Initialize the children list_head. - list_head_init(&new_process->sibling); - - // Set the new_process as child of current. - list_head_add_tail(¤t->children, &new_process->sibling); - - // Active the new process. - enqueue_task(new_process); - - dbg_print("--------------------------------------------------\n"); - dbg_print("- %s process (PID: %d, eflags: %d)\n", new_process->name, - new_process->pid, new_process->thread.eflags); - dbg_print("\teip : 0x%p\n", new_process->thread.eip); - dbg_print("\tebp : 0x%p\n", new_process->thread.ebp); - dbg_print("\tesp : 0x%p\n", new_process->thread.useresp); - dbg_print("\tStack : 0x%p\n", new_process->mm->start_stack); - dbg_print("\tRunList: 0x%p\n", &new_process->run_list); - dbg_print("--------------------------------------------------\n"); - - dbg_print("Fork of '%s' (child pid: %d) process completed.\n", - current->name, current->pid); - - // Return PID of child process to parent. - return new_process->pid; + // Get the current task. + task_struct *task = scheduler_get_current_process(); + // Check the current FD. + if (fd >= 0 && fd < task->max_fd) { + // Get the file descriptor. + vfs_file_descriptor_t *vfd = &task->fd_list[fd]; + // Check the file. + if (vfd->file_struct != NULL) { + char absolute_path[PATH_MAX]; + realpath(vfd->file_struct->name, absolute_path); + strcpy(task->cwd, absolute_path); + } + } } -static inline int push_args_on_stack(uintptr_t *esp, char *args[], - char ***argsptr) +pid_t sys_fork(pt_regs *f) { - int argc = 0; - char *args_ptr[256]; - // Count the number of arguments. - while (args[argc] != NULL) { - ++argc; - } - // Push terminating NULL. - PUSH_ON_STACK((*esp), char *, (char *)NULL); - // Prepare args with space for the terminating NULL. - for (int i = argc - 1; i >= 0; --i) { - for (int j = strlen(args[i]); j >= 0; --j) { - PUSH_ON_STACK((*esp), char, args[i][j]); - } - args_ptr[i] = (char *)(*esp); - } - // Push terminating NULL. - PUSH_ON_STACK((*esp), char *, (char *)NULL); - // Push array of pointers to the arguments. - for (int i = argc - 1; i >= 0; --i) { - PUSH_ON_STACK((*esp), char *, args_ptr[i]); - } - (*argsptr) = (char **)(*esp); + task_struct *current = scheduler_get_current_process(); + if (current == NULL) + kernel_panic("There is no current process!"); - return argc; + pr_debug("Forking '%s' (pid: %d)...\n", current->name, current->pid); + + // Update current process registers, they should be equal + // to the ones of the child process, except for eax. + scheduler_store_context(f, current); + // Allocate the memory for the process. + task_struct *proc = __alloc_task(current, current, current->name); + // Copy the father's stack, memory, heap etc... to the child process + proc->mm = clone_process_image(current->mm); + // Set the eax as 0, to indicate the child process + proc->thread.regs.eax = 0; + // Enable the interrupts. + proc->thread.regs.eflags = proc->thread.regs.eflags | EFLAG_IF; + + // Copy session and group id of the parent into the child + proc->sid = current->sid; + proc->gid = current->gid; + + // Active the new process. + scheduler_enqueue_task(proc); + + pr_debug("Forked '%s' (pid: %d, gid: %d, sid: %d)...\n", proc->name, proc->pid, proc->gid, proc->sid); + + // Return PID of child process to parent. + return proc->pid; } -int sys_execve(pt_regs *r) +int sys_execve(pt_regs *f) { - char **argv, **_argv, **envp, **_envp; - // Check the current process. - task_struct *current = kernel_get_current_process(); - if (current == NULL) { - kernel_panic("There is no current process!"); - } + // Check the current process. + task_struct *current = scheduler_get_current_process(); + if (current == NULL) + kernel_panic("There is no current process!"); - // Get the filename. - uintptr_t *filename = (uintptr_t *)r->ebx; - if (filename == NULL) { - return -1; - } + char **origin_argv, **saved_argv, **final_argv; + char **origin_envp, **saved_envp, **final_envp; + char name_buffer[NAME_MAX]; - // Get the arguments. - argv = (char **)r->ecx; - // Get the environment. - envp = (char **)r->edx; + // Get the filename. + char *filename = (char *)f->ebx; + if (filename == NULL) { + pr_err("Received NULL filename.\n"); + return -1; + } + // Get the arguments + origin_argv = (char **)f->ecx; + // Get the environment. + origin_envp = (char **)f->edx; + // Check the argument, the environment, and that at least the name is provided. + if (origin_argv == NULL) { + pr_err("sys_execve failed: must provide argv.\n"); + return -1; + } + if (origin_argv[0] == NULL) { + pr_err("sys_execve failed: must provide the name.\n"); + return -1; + } + if (origin_envp == NULL) { + pr_err("sys_execve failed: must provide the environment.\n"); + return -1; + } - // Check the argument and that at least the name is provided. - if ((argv == NULL) || (argv[0] == NULL)) { - return -1; - } + // Save the name of the process. + strcpy(name_buffer, origin_argv[0]); - // Check that the environment is provided. - if (envp == NULL) { - kernel_panic("You must provide at least an empty list for envp!"); - } + // == COPY PROGRAM ARGUMENTS ============================================== + // Copy argv and envp to kernel memory, because all the old process memory will be discarded. + int argv_bytes = __count_args_bytes(origin_argv); + int envp_bytes = __count_args_bytes(origin_envp); + if ((argv_bytes < 0) || (envp_bytes < 0)) { + pr_err("Failed to count required memory to store arguments and environment (%d + %d).\n", + argv_bytes, envp_bytes); + return -1; + } + void *args_mem = kmalloc(argv_bytes + envp_bytes); + if (!args_mem) { + pr_err("Failed to allocate memory for arguments and environment %d (%d + %d).\n", + argv_bytes + envp_bytes, argv_bytes, envp_bytes); + return -1; + } + // Copy the arguments. + uint32_t args_mem_ptr = (uint32_t)args_mem + (argv_bytes + envp_bytes); + __push_args_on_stack(&args_mem_ptr, origin_argv, &saved_argv); + __push_args_on_stack(&args_mem_ptr, origin_envp, &saved_envp); + // Check the memory pointer. + assert(args_mem_ptr == (uint32_t)args_mem); + // ------------------------------------------------------------------------ - // Set the name. - strcpy(current->name, argv[0]); + // == INITIALIZE TASK MEMORY ============================================== + if (!__load_executable(filename, current, ¤t->thread.regs.eip)) { + pr_err("Failed to load executable!\n"); + // Free the temporary args memory. + kfree(args_mem); + return -1; + } + // ------------------------------------------------------------------------ - // Set the top address of the stack. - current->thread.useresp = (uintptr_t)current->thread.ebp; + // == INITIALIZE PROGRAM ARGUMENTS ======================================== + // Save the current page directory. + page_directory_t *crtdir = paging_get_current_directory(); - // Set the program counter. - current->thread.eip = (uintptr_t)filename; + // Change the page directory to point to the newly created process + paging_switch_directory_va(current->mm->pgd); - int argc = push_args_on_stack(¤t->thread.useresp, argv, &_argv); - push_args_on_stack(¤t->thread.useresp, envp, &_envp); + // Save where the arguments start. + current->mm->arg_start = current->thread.regs.useresp; + // Push the arguments on the stack. + int argc = __push_args_on_stack(¤t->thread.regs.useresp, saved_argv, &final_argv); + // Save where the arguments end, and the env starts. + current->mm->env_start = current->mm->arg_end = current->thread.regs.useresp; + // Push the environment on the stack. + int envc = __push_args_on_stack(¤t->thread.regs.useresp, saved_envp, &final_envp); + // Save where the environmental variables end. + current->mm->env_end = current->thread.regs.useresp; + // Push the `main` arguments on the stack (argc, argv, envp). + PUSH_ARG(current->thread.regs.useresp, char **, final_envp); + PUSH_ARG(current->thread.regs.useresp, char **, final_argv); + PUSH_ARG(current->thread.regs.useresp, int, argc); - PUSH_ON_STACK(current->thread.useresp, char **, _envp); - PUSH_ON_STACK(current->thread.useresp, char **, _argv); - PUSH_ON_STACK(current->thread.useresp, int, argc); - PUSH_ON_STACK(current->thread.useresp, uintptr_t, (uintptr_t)exit_handler); + // Restore previous pgdir + paging_switch_directory(crtdir); + // ------------------------------------------------------------------------ -// dbg_print("_ARGV:0x%09x {\n", _argv); -// for (int i = 0; _argv[i] != NULL; ++i) { -// dbg_print("\t[%d][0x%09x]%s\n", i, _argv[i], _argv[i]); -// } -// dbg_print("}\n"); -// -// if (_envp != NULL) { -// dbg_print("_ENVP:0x%09x {\n", _envp); -// for (int i = 0; _envp[i] != NULL; ++i) { -// dbg_print("\t[%d][0x%09x]%s\n", i, _envp[i], _envp[i]); -// } -// dbg_print("}\n"); -// } + // Change the name of the process. + strcpy(current->name, name_buffer); - // Perform the switch to the new process. - do_switch(current, r); + // Free the temporary args memory. + kfree(args_mem); - dbg_print("Executing '0x%p' for process %d with %d arguments (0x%p)...\n", - filename, current->pid, argc, argv); + // Perform the switch to the new process. + scheduler_restore_context(current, f); - return 0; + pr_debug("Executing '%s' (pid: %d)...\n", current->name, current->pid); + return 0; } diff --git a/mentos/src/process/scheduler.c b/mentos/src/process/scheduler.c index feac995..accfd33 100644 --- a/mentos/src/process/scheduler.c +++ b/mentos/src/process/scheduler.c @@ -1,22 +1,30 @@ /// MentOS, The Mentoring Operating system project /// @file scheduler.c /// @brief Scheduler structures and functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "scheduler.h" -#include "tss.h" -#include "fpu.h" -#include "prio.h" -#include "wait.h" -#include "kheap.h" -#include "panic.h" -#include "debug.h" -#include "clock.h" -#include "errno.h" -#include "rbtree.h" -#include "stdlib.h" -#include "list_head.h" +/// Change the header. +#define __DEBUG_HEADER__ "[SCHED ]" + +#include "assert.h" +#include "strerror.h" +#include "fs/vfs.h" +#include "process/scheduler.h" +#include "descriptor_tables/tss.h" +#include "devices/fpu.h" +#include "process/prio.h" +#include "process/wait.h" +#include "mem/kheap.h" +#include "system/panic.h" +#include "misc/debug.h" +#include "time.h" +#include "sys/errno.h" +#include "klib/list_head.h" +#include "mem/paging.h" +#include "hardware/timer.h" +#include "math.h" +#include "stdio.h" /// @brief Assembly function setting the kernel stack to jump into /// location in Ring 3 mode (USER mode). @@ -27,357 +35,647 @@ extern void enter_userspace(uintptr_t location, uintptr_t stack); /// The list of processes. runqueue_t runqueue; -uint32_t get_new_pid(void) +void scheduler_initialize() { - /// The current unused PID. - static unsigned long int tid = 1; - - // Return the pid and increment. - return tid++; + // Initialize the runqueue list of tasks. + list_head_init(&runqueue.queue); + // Reset the current task. + runqueue.curr = NULL; + // Reset the number of active tasks. + runqueue.num_active = 0; } -task_struct *kernel_get_current_process() +uint32_t scheduler_getpid(void) { - return runqueue.curr; + /// The current unused PID. + static unsigned long int tid = 1; + + // Return the pid and increment. + return tid++; } -task_struct *kernel_get_running_process(pid_t pid) +task_struct *scheduler_get_current_process() { - list_head *it; - list_for_each (it, &runqueue.queue) { - task_struct *entry = list_entry(it, task_struct, run_list); - if (entry != NULL) { - if (entry->pid == pid) { - return entry; - } - } - } - return NULL; + return runqueue.curr; } -size_t kernel_get_active_processes() +time_t scheduler_get_maximum_vruntime() { - return runqueue.num_active; + time_t vruntime = 0; + task_struct *entry; + list_for_each_decl(it, &runqueue.queue) + { + // Check if we reached the head of list_head, and skip it. + if (it == &runqueue.queue) + continue; + // Get the current entry. + entry = list_entry(it, task_struct, run_list); + // Skip the process if it is a periodic one, we are issued to skip + // periodic tasks, and the entry is not a periodic task under + // analysis. + if (entry->se.is_periodic && !entry->se.is_under_analysis) + continue; + if (entry->se.vruntime > vruntime) + vruntime = entry->se.vruntime; + } + return vruntime; } -void kernel_initialize_scheduler() +size_t scheduler_get_active_processes() { - // Initialize the runqueue list of tasks. - list_head_init(&runqueue.queue); - // Reset the current task. - runqueue.curr = NULL; - // Reset the number of active tasks. - runqueue.num_active = 0; + return runqueue.num_active; } -void enqueue_task(task_struct *process) +task_struct *scheduler_get_running_process(pid_t pid) { - // If current_process is NULL, then process is the current process. - if (runqueue.curr == NULL) { - runqueue.curr = process; - } - // Add the new process at the end. - list_head_add_tail(&process->run_list, &runqueue.queue); - // Increment the number of active processes. - ++runqueue.num_active; + task_struct *entry; + list_for_each_decl(it, &runqueue.queue) + { + entry = list_entry(it, task_struct, run_list); + if (entry->pid == pid) + return entry; + } + return NULL; } -void dequeue_task(task_struct *process) +void scheduler_enqueue_task(task_struct *process) { - // Delete the process from the list of running processes. - list_head_del(&process->run_list); - // Decrement the number of active processes. - --runqueue.num_active; + // If current_process is NULL, then process is the current process. + if (runqueue.curr == NULL) { + runqueue.curr = process; + } + // Add the new process at the end. + list_head_add_tail(&process->run_list, &runqueue.queue); + // Increment the number of active processes. + ++runqueue.num_active; } -void kernel_schedule(pt_regs *f) +void scheduler_dequeue_task(task_struct *process) { - // Check if there is a running process. - if (runqueue.curr == NULL) { - return; - } - - //==== Update Statistics =================================================== - time_t delta_exec = get_millisecond() - runqueue.curr->se.exec_start; - // dbg_print("[%3d] %d = %d - %d\n", runqueue.curr->pid, delta_exec, - // get_millisecond(), runqueue.curr->se.exec_start); - // set the sum_exec_runtime - runqueue.curr->se.sum_exec_runtime += delta_exec; - //========================================================================== - - //==== Handle Zombies ====================================================== - task_struct *next_process = NULL; - if (runqueue.curr->state == EXIT_ZOMBIE) { - // get the next process after the current one - list_head *nNode = runqueue.curr->run_list.next; - // check if we reached the head of list_head - if (nNode == &runqueue.queue) - nNode = nNode->next; - // get the task_struct - next_process = list_entry(nNode, task_struct, run_list); - // Remove the zombie task. - dequeue_task(runqueue.curr); - } else { - //==== Scheduling ====================================================== - // Pointer to the next process to be executed. - next_process = pick_next_task(&runqueue, delta_exec); - //====================================================================== - } - //========================================================================== - - // Print, for debugging purpose, data about the current process. - if (runqueue.num_active > 2) { - dbg_print("PID:%3d, PRIO:%3d, VRUNTIME:%9d, SUM_EXEC:%9d\n", - next_process->pid, next_process->se.prio, - next_process->se.vruntime, next_process->se.sum_exec_runtime); - } - - //==== Context switch ====================================================== - // Update the context of the current process. - update_context(f, runqueue.curr); - // Check if the next and current processes are different. - if (next_process != runqueue.curr) { - // Copy into Kernel stack the next process's context. - do_switch(next_process, f); - runqueue.curr->se.sum_exec_runtime = get_millisecond(); - // Update the last context switch time of the next process. - next_process->se.exec_start = get_millisecond(); - } - //========================================================================== - - // Update the start execution time if it is executed for the first time - if (next_process->se.start_runtime == 0) - next_process->se.start_runtime = get_millisecond(); + // Delete the process from the list of running processes. + list_head_del(&process->run_list); + // Decrement the number of active processes. + --runqueue.num_active; + if (process->se.is_periodic) + runqueue.num_periodic--; } -void update_context(pt_regs *f, task_struct *process) +void scheduler_run(pt_regs *f) { - // Store the registers. - process->thread.gs = f->gs; - process->thread.fs = f->fs; - process->thread.es = f->es; - process->thread.ds = f->ds; - process->thread.edi = f->edi; - process->thread.esi = f->esi; - process->thread.ebp = f->ebp; - process->thread.ebx = f->ebx; - process->thread.edx = f->edx; - process->thread.ecx = f->ecx; - process->thread.eax = f->eax; - process->thread.eip = f->eip; - process->thread.eflags = f->eflags; - process->thread.useresp = f->useresp; - // TODO: Check if the following registers should be saved. - // process->thread.cs = f->cs; - // process->thread.ss = f->ss; - // Store the FPU. - switch_fpu(); + // Check if there is a running process. + if (runqueue.curr == NULL) + return; + + task_struct *next = NULL; + + // Update the context of the current process. + scheduler_store_context(f, runqueue.curr); + + // We check the existence of pending signals every time we finish + // handling an interrupt or an exception. + if (!do_signal(f)) { +#if 1 + if (runqueue.curr->state == EXIT_ZOMBIE) { + //==== Handle Zombies ================================================= + //pr_debug("Handle zombie %d\n", runqueue.curr->pid); + // get the next process after the current one + list_head *nNode = runqueue.curr->run_list.next; + // check if we reached the head of list_head + if (nNode == &runqueue.queue) { + nNode = nNode->next; + } + // get the task_struct + next = list_entry(nNode, task_struct, run_list); + // Remove the zombie task. + scheduler_dequeue_task(runqueue.curr); + assert(next && "No valid task selected after removing ZOMBIE."); + //===================================================================== + } else { +#endif + //==== Scheduling ===================================================== + // If we are currently executing a periodic process, and this process + // has yet to complete, keep executing it. +#ifdef SCHEDULER_EDF + if (runqueue.curr->se.is_periodic) + if (!runqueue.curr->se.executed) + return; +#endif + // Pointer to the next process to be executed. + next = scheduler_pick_next_task(&runqueue); + //===================================================================== + } + // Check if the next and current processes are different. + if (next != runqueue.curr) { + // Copy into Kernel stack the next process's context. + scheduler_restore_context(next, f); + } + } + //========================================================================== } -void do_switch(task_struct *process, pt_regs *f) +void scheduler_store_context(pt_regs *f, task_struct *process) { - // Switch to the next process. - runqueue.curr = process; - // Restore the registers. - f->gs = process->thread.gs; - f->fs = process->thread.fs; - f->es = process->thread.es; - f->ds = process->thread.ds; - f->edi = process->thread.edi; - f->esi = process->thread.esi; - f->ebp = process->thread.ebp; - f->ebx = process->thread.ebx; - f->edx = process->thread.edx; - f->ecx = process->thread.ecx; - f->eax = process->thread.eax; - f->eip = process->thread.eip; - f->eflags = process->thread.eflags; - f->useresp = process->thread.useresp; - // TODO: Check if the following registers should be restored. - // f->cs = process->thread.cs; - // f->ss = process->thread.ss; - // Restore the FPU. - unswitch_fpu(); + // Store the registers. + process->thread.regs = *f; } -int set_user_nice(task_struct *p, long nice) +void scheduler_restore_context(task_struct *process, pt_regs *f) { - if (PRIO_TO_NICE(p->se.prio) != nice && nice >= MIN_NICE && - nice <= MAX_NICE) { - p->se.prio = NICE_TO_PRIO(nice); - } - - return PRIO_TO_NICE(p->se.prio); + // Switch to the next process. + runqueue.curr = process; + // Restore the registers. + *f = process->thread.regs; + // TODO: Explain paging switch (ring 0 doesn't need page switching) + // Switch to process page directory + paging_switch_directory_va(process->mm->pgd); } -void enter_user_jmp(uintptr_t location, uintptr_t stack) +void scheduler_enter_user_jmp(uintptr_t location, uintptr_t stack) { - // Reset stack pointer for kernel. - tss_set_stack(0x10, initial_esp); + // Reset stack pointer for kernel. + tss_set_stack(0x10, initial_esp); - // update start execution time. - runqueue.curr->se.start_runtime = get_millisecond(); + // update start execution time. + runqueue.curr->se.start_runtime = timer_get_ticks(); - // last context switch time. - runqueue.curr->se.exec_start = get_millisecond(); + // last context switch time. + runqueue.curr->se.exec_start = timer_get_ticks(); - // Jump in location. - enter_userspace(location, stack); + // Jump in location. + enter_userspace(location, stack); +} + +/// @brief Awakens a sleeping process. +/// @param process The process that should be awakened +/// @param mode The type of wait (TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE). +/// @param sync Specifies if the wakeup should be synchronous. +/// @return 1 on success, 0 on failure. +static inline int try_to_wake_up(task_struct *process, int mode, int sync) +{ + // Only tasks in the state TASK_UNINTERRUPTIBLE can be woke up + if (process->state == TASK_UNINTERRUPTIBLE || process->state == TASK_STOPPED) { + //TODO: Recalc task priority + process->state = TASK_RUNNING; + return 1; + } + return 0; +} + +int default_wake_function(wait_queue_entry_t *wait, unsigned mode, int sync) +{ + task_struct *p = wait->task; + return try_to_wake_up(p, mode, sync); +} + +wait_queue_entry_t *sleep_on(wait_queue_head_t *wq) +{ + // Save the sleeping process registers state + task_struct *sleeping_task = scheduler_get_current_process(); + +#if 0 + pt_regs* f = get_current_interrupt_stack_frame(); + scheduler_store_context(f, sleeping_task); + + // Select next process in the runqueue as the current, restore it's context, + // we assume that the first process is init wich does not sleep (I hope). + // This is necessary to make the scheduler_run() in syscall_handler work. + task_struct *next = list_entry(runqueue.queue.next, task_struct, run_list); + assert((next != sleeping_task) && "The next selected process in the runqueue is the sleeping process"); + scheduler_restore_context(next, f); +#endif + + // Stops task from runqueue making it unrunnable + sleeping_task->state = TASK_UNINTERRUPTIBLE; + + // Add sleeping process to sleep wait queue + wait_queue_entry_t *wait_entry = kmalloc(sizeof(struct wait_queue_entry_t)); + init_waitqueue_entry(wait_entry, sleeping_task); + add_wait_queue(wq, wait_entry); + + return wait_entry; +} + +int is_orphaned_pgrp(pid_t gid) +{ + pid_t sid = 0; + + // Obtain SID of the group from a member + list_head *it; + list_for_each (it, &runqueue.queue) { + task_struct *task = list_entry(it, task_struct, run_list); + if (task->gid == gid) { + sid = task->sid; + break; + } + } + + // Check if the process leader of the session is alive + list_for_each (it, &runqueue.queue) { + task_struct *task = list_entry(it, task_struct, run_list); + if (task->pid == sid) { + return 0; + } + } + + return 1; } pid_t sys_getpid() { - // Get the current task. - if (runqueue.curr == NULL) { - kernel_panic("There is no current process!"); - } + // Get the current task. + if (runqueue.curr == NULL) { + kernel_panic("There is no current process!"); + } - // Return the process identifer of the process. - return runqueue.curr->pid; + // Return the process identifer of the process. + return runqueue.curr->pid; +} + +pid_t sys_getsid(pid_t pid) +{ + //If pid == 0 return SID of the calling process + if (pid == 0) { + if (runqueue.curr == NULL) { + kernel_panic("There is no current process!"); + } + // Return the session identifer of the process. + return runqueue.curr->sid; + } + //If != 0 get SID of the specified process + list_head *it; + list_for_each (it, &runqueue.queue) { + task_struct *task = list_entry(it, task_struct, run_list); + if (task->pid == pid) + { + if(runqueue.curr->sid != task->sid) + return -EPERM; + + return task->sid; + } + } + return -ESRCH; +} + +pid_t sys_setsid() +{ + task_struct *task = runqueue.curr; + if (task == NULL) { + kernel_panic("There is no current process!"); + } + if (task->sid == task->pid) + { + pr_debug("Process %d is already a session leader.", task->pid); + return -EPERM; + } + + task->sid = task->pid; + task->gid = task->pid; + + return task->sid; +} + +pid_t sys_getgid() +{ + task_struct *curr = runqueue.curr; + if (curr == NULL) { + kernel_panic("There is no current process!"); + } + + return curr->gid; +} + +int sys_setgid(pid_t gid) +{ + task_struct *curr = runqueue.curr; + if (curr == NULL) { + kernel_panic("There is no current process!"); + } + + if (curr->gid == curr->pid) + pr_debug("Process %d is already a session leader.", task->pid); + + curr->gid = curr->pid; + return 0; } pid_t sys_getppid() { - // Get the current task. - if (runqueue.curr == NULL) { - kernel_panic("There is no current process!"); - } - if (runqueue.curr->parent == NULL) { - return 0; - } + // Get the current task. + if (runqueue.curr == NULL) { + kernel_panic("There is no current process!"); + } + if (runqueue.curr->parent == NULL) { + return 0; + } - // Return the parent process identifer of the process. - return runqueue.curr->parent->pid; + // Return the parent process identifer of the process. + return runqueue.curr->parent->pid; } int sys_nice(int increment) { - // Get the current task. - if (runqueue.curr == NULL) { - kernel_panic("There is no current process!"); - } + // Get the current task. + if (runqueue.curr == NULL) { + kernel_panic("There is no current process!"); + } - if (increment < -40) { - increment = -40; - } - if (increment > 40) { - increment = 40; - } + if (increment < -40) { + increment = -40; + } + if (increment > 40) { + increment = 40; + } - int newNice = PRIO_TO_NICE(runqueue.curr->se.prio) + increment; - dbg_print("New nice value would be : %d\n", newNice); + int newNice = PRIO_TO_NICE(runqueue.curr->se.prio) + increment; + pr_debug("New nice value would be : %d\n", newNice); - if (newNice < MIN_NICE) { - newNice = MIN_NICE; - } - if (newNice > MAX_NICE) { - newNice = MAX_NICE; - } + if (newNice < MIN_NICE) { + newNice = MIN_NICE; + } + if (newNice > MAX_NICE) { + newNice = MAX_NICE; + } - int actualNice = set_user_nice(runqueue.curr, newNice); - dbg_print("Actual new nice value is: %d\n", actualNice); + if (PRIO_TO_NICE(runqueue.curr->se.prio) != newNice && newNice >= MIN_NICE && newNice <= MAX_NICE) { + runqueue.curr->se.prio = NICE_TO_PRIO(newNice); + } + int actualNice = PRIO_TO_NICE(runqueue.curr->se.prio); - return actualNice; + pr_debug("Actual new nice value is: %d\n", actualNice); + + return actualNice; } pid_t sys_waitpid(pid_t pid, int *status, int options) { - // Get the current task. - if (runqueue.curr == NULL) { - kernel_panic("There is no current process!"); - } + // Get the current task. + if (runqueue.curr == NULL) { + kernel_panic("There is no current process!"); + } - /* For now we do not support waiting for processes inside the given + /* For now we do not support waiting for processes inside the given * process group (pid < -1). */ - if ((pid < -1) || (pid == 0)) { - errno = ESRCH; - - return (-1); - } - if (pid == runqueue.curr->pid) { - errno = ECHILD; - - return (-1); - } - if (options != 0 && options != WNOHANG) { - errno = EINVAL; - - return (-1); - } - if (status == NULL) { - errno = EFAULT; - - return (-1); - } - list_head *it; - list_for_each (it, &runqueue.curr->children) { - task_struct *entry = list_entry(it, task_struct, sibling); - if (entry == NULL) { - continue; - } - if (entry->state != EXIT_ZOMBIE) { - continue; - } - if ((pid > 1) && (entry->pid != pid)) { - continue; - } - // Save the pid to return. - pid_t ppid = entry->pid; - // Save the state. - (*status) = entry->state; //TODO: da rivedere - // Remove entry from children of parent. - list_head_del(&entry->sibling); - // Delete the task_struct. - kfree(entry); - dbg_print("Freeing memory of process %d.\n", ppid); - - return ppid; - } - - return 0; + if ((pid < -1) || (pid == 0)) { + return -ESRCH; + } + if (pid == runqueue.curr->pid) { + return -ECHILD; + } + if (options != 0 && options != WNOHANG) { + return -EINVAL; + } +#if 0 + if (status == NULL) { + return -EFAULT; + } +#endif + if (list_head_empty(&runqueue.curr->children)) { + return -ECHILD; + } + list_head *it; + list_for_each (it, &runqueue.curr->children) { + task_struct *entry = list_entry(it, task_struct, sibling); + if (entry == NULL) { + continue; + } + if (entry->state != EXIT_ZOMBIE) { + continue; + } + if ((pid > 1) && (entry->pid != pid)) { + continue; + } + // Save the pid to return. + pid_t ppid = entry->pid; + // Save the state (TODO: Improve status set). + if (status) + (*status) = entry->state; + // Finalize the VFS structures. + vfs_destroy_task(entry); + // Remove entry from children of parent. + list_head_del(&entry->sibling); + // Remove entry from the scheduling queue. + scheduler_dequeue_task(entry); + // Delete the task_struct. + kmem_cache_free(entry); + pr_debug("Process %d is freeing memory of process %d.\n", runqueue.curr->pid, ppid); + return ppid; + } + return 0; } void sys_exit(int exit_code) { - // Get the current task. - if (runqueue.curr == NULL) { - kernel_panic("There is no current process!"); - } + // Get the current task. + if (runqueue.curr == NULL) { + kernel_panic("There is no current process!"); + } - task_struct *init_proc = kernel_get_running_process(1); - if (runqueue.curr == init_proc) { - kernel_panic("Init process cannot call sys_exit!"); - } - // Set the termination code of the process. - runqueue.curr->exit_code = (exit_code << 8) & 0xFF00; - // Set the state of the process to zombie. - runqueue.curr->state = EXIT_ZOMBIE; - // If it has children, then init process has to take care of them. - if (!list_head_empty(&runqueue.curr->children)) { - dbg_print("Moving children of %s(%d) to init(%d): {\n", - runqueue.curr->name, runqueue.curr->pid, init_proc->pid); - // TODO: Try to plug the list of children instead of iterating. - list_head *it; - list_for_each (it, &runqueue.curr->children) { - task_struct *entry = list_entry(it, task_struct, sibling); - dbg_print(" [%d] %s\n", entry->pid, entry->name); - it = entry->sibling.prev; - list_head_del(&entry->sibling); - list_head_add_tail(&init_proc->children, &entry->sibling); - entry->parent = init_proc; - } - dbg_print("}\n"); - dbg_print("Listing children of init(%d): {\n", init_proc->pid); - list_for_each (it, &init_proc->children) { - task_struct *entry = list_entry(it, task_struct, sibling); - dbg_print(" [%d] %s\n", entry->pid, entry->name); - } - dbg_print("}\n"); - } - // Free the space occupied by the stack. - destroy_process_image(runqueue.curr->mm); - // Debugging message. - dbg_print("Process %d exited with value %d\n", runqueue.curr->pid, - exit_code); + // Get the process. + task_struct *init_proc = scheduler_get_running_process(1); + if (runqueue.curr == init_proc) { + kernel_panic("Init process cannot call sys_exit!"); + } + + // Set the termination code of the process. + runqueue.curr->exit_code = (exit_code << 8) & 0xFF00; + // Set the state of the process to zombie. + runqueue.curr->state = EXIT_ZOMBIE; + // Send a SIGCHLD to the parent process. + if (runqueue.curr->parent) { + int ret = sys_kill(runqueue.curr->parent->pid, SIGCHLD); + if (ret == -1) { + printf("[%d] %5d failed sending signal %d : %s\n", ret, runqueue.curr->parent->pid, + SIGCHLD, strerror(errno)); + } + } + + // If it has children, then init process has to take care of them. + if (!list_head_empty(&runqueue.curr->children)) { + pr_debug("Moving children of %s(%d) to init(%d): {\n", + runqueue.curr->name, runqueue.curr->pid, init_proc->pid); + // Change the parent. + pr_debug("Moving children (%d): {\n", init_proc->pid); + list_for_each_decl(it, &runqueue.curr->children) + { + task_struct *entry = list_entry(it, task_struct, sibling); + pr_debug(" [%d] %s\n", entry->pid, entry->name); + entry->parent = init_proc; + } + pr_debug("}\n"); + // Plug the list of children. + list_head_merge(&init_proc->children, &runqueue.curr->children); + // Print the list of children. + pr_debug("New list of init children (%d): {\n", init_proc->pid); + list_for_each_decl(it, &init_proc->children) + { + task_struct *entry = list_entry(it, task_struct, sibling); + pr_debug(" [%d] %s\n", entry->pid, entry->name); + } + pr_debug("}\n"); + } + // Free the space occupied by the stack. + destroy_process_image(runqueue.curr->mm); + // Debugging message. + pr_debug("Process %d exited with value %d\n", runqueue.curr->pid, exit_code); } + +int sys_sched_setparam(pid_t pid, const sched_param_t *param) +{ + list_head *it; + // Iter over the runqueue to find the task + list_for_each (it, &runqueue.queue) { + task_struct *entry = list_entry(it, task_struct, run_list); + if (entry->pid == pid) { + if (!entry->se.is_periodic && param->is_periodic) + runqueue.num_periodic++; + else if (entry->se.is_periodic && !param->is_periodic) + runqueue.num_periodic--; + // Sets the parameters from param to the "se" struct parameters. + entry->se.prio = param->sched_priority; + entry->se.period = param->period; + entry->se.arrivaltime = param->arrivaltime; + entry->se.is_periodic = param->is_periodic; + entry->se.deadline = timer_get_ticks() + param->deadline; + entry->se.next_period = timer_get_ticks(); + + entry->se.is_under_analysis = true; + entry->se.executed = false; + return 1; + } + } + return -1; +} + +int sys_sched_getparam(pid_t pid, sched_param_t *param) +{ + list_head *it; + // Iter over the runqueue to find the task + list_for_each (it, &runqueue.queue) { + task_struct *entry = list_entry(it, task_struct, run_list); + if (entry->pid == pid) { + //Sets the parameters from the "se" struct to param + param->sched_priority = entry->se.prio; + param->period = entry->se.period; + param->deadline = entry->se.deadline; + param->arrivaltime = entry->se.arrivaltime; + return 1; + } + } + return -1; +} + +/// @brief Performs the response time analysis for the current list +/// of periodic processes. +/// @return 1 if scheduling periodic processes is feasable, 0 otherwise. +static int __response_time_analysis() +{ + task_struct *entry, *previous; + time_t r, previous_r = 0; + list_for_each_decl(it, &runqueue.queue) + { + entry = list_entry(it, task_struct, run_list); + if (entry->se.is_periodic) { + // Put r equal to worst case exec because is the first point in time that the task could possibly complete + r = entry->se.worst_case_exec; + previous_r = 0; + // The analysis can be completed either missing the deadline or reaching a fixed point + while (r < entry->se.deadline && r != previous_r) { + previous_r = r; + r = entry->se.worst_case_exec; + list_for_each_decl(it2, &runqueue.queue) + { + previous = list_entry(it2, task_struct, run_list); + // Check the interferences of higher priority processes + if (previous->se.is_periodic && previous->se.period < entry->se.period) { + r += (int)ceil((double)previous_r / (double)previous->se.period) * previous->se.worst_case_exec; + pr_debug("%d += (%.2f / %.2f) * %d\n", r, (double)previous_r, (double)previous->se.period, previous->se.worst_case_exec); + pr_debug("Response Time Analysis -> [%s]vs[%s] R = %d\n\n", entry->name, previous->name, r); + } + } + } + // Feasibility of scheduler is guaranteed if and only if response time analysis is lower than deadline. + if (r > entry->se.deadline) + return 1; + } + } + return 0; +} + +int sys_waitperiod() +{ + if (runqueue.curr) { + if (runqueue.curr->se.is_periodic) { + // Update the Worst Case Execution Time (WCET). + time_t wcet = timer_get_ticks() - runqueue.curr->se.exec_start; + if (runqueue.curr->se.worst_case_exec < wcet) + runqueue.curr->se.worst_case_exec = wcet; + // Update thye utilization factor. + runqueue.curr->se.utilization_factor = ((double)runqueue.curr->se.worst_case_exec / (double)runqueue.curr->se.period); + + // If the task is under analysis, we need to test if the process can be + // placed with the other periodic tasks. + if (runqueue.curr->se.is_under_analysis) { + runqueue.curr->se.worst_case_exec = runqueue.curr->se.sum_exec_runtime; + bool_t is_not_schedulable = false; +#if defined(SCHEDULER_EDF) + double u = 0; + task_struct *entry; + list_for_each_decl(it, &runqueue.queue) + { + entry = list_entry(it, task_struct, run_list); + // Sum the utilization factor of all periodic tasks. + if (entry->se.is_periodic) + u += entry->se.utilization_factor; + } + if (u > 1) { + is_not_schedulable = true; + } + pr_debug("utilization factor = %f\n", u); +#elif defined(SCHEDULER_RM) + // Calculating least upper bound of utilization factor. + // For large amount of processes ulub asymptotically should reach ln(2). + double ulub = (runqueue.num_periodic * (pow(2, (1.0 / runqueue.num_periodic)) - 1)); + double u = 0; + task_struct *entry; + list_for_each_decl(it, &runqueue.queue) + { + entry = list_entry(it, task_struct, run_list); + // Sum the utilization factor of all periodic tasks. + if (entry->se.is_periodic) + u += entry->se.utilization_factor; + } + // If the sum of utilization factor is bounded between ulub and 1 we need to calculate + // the response time analysis for each process. + if (u > 1) { + is_not_schedulable = true; + } else if (u <= ulub) + is_not_schedulable = false; + else + is_not_schedulable = __response_time_analysis(); +#endif + // If it is not schedulable, we need to tell it to the process. + if (is_not_schedulable) + return -ENOTSCHEDULABLE; + + // Otherwise, it is schedulable. + runqueue.curr->se.is_under_analysis = false; + + // The task has been executed as non-periodic process so that his deadline is not been updated + // by the scheduling algorithm of periodic tasks. We need to update it manually. + runqueue.curr->se.next_period = timer_get_ticks(); + runqueue.curr->se.deadline = timer_get_ticks() + runqueue.curr->se.period; + } + + if (timer_get_ticks() > runqueue.curr->se.deadline) + pr_warning("%d > %d Missing deadline...\n", timer_get_ticks(), runqueue.curr->se.deadline); + + // Tell the scheduler that we have executed the periodic process. + runqueue.curr->se.executed = true; + + } else + pr_warning("An aperiodic task is calling `waitperiod`, ignoring...\n"); + return 0; + } + return -ESRCH; +} \ No newline at end of file diff --git a/mentos/src/process/scheduler_algorithm.c b/mentos/src/process/scheduler_algorithm.c index eb5667d..9eec80b 100644 --- a/mentos/src/process/scheduler_algorithm.c +++ b/mentos/src/process/scheduler_algorithm.c @@ -1,73 +1,181 @@ +/// MentOS, The Mentoring Operating system project /// @file scheduler_algorithm.c /// @brief Round Robin algorithm. -/// @date Mar 2019. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. -#include "prio.h" -#include "debug.h" +#include "hardware/timer.h" +#include "process/prio.h" +#include "misc/debug.h" #include "assert.h" -#include "list_head.h" -#include "scheduler.h" +#include "klib/list_head.h" +#include "process/wait.h" +#include "process/scheduler.h" -#define GET_WEIGHT(prio) prio_to_weight[USER_PRIO((prio))] -#define NICE_0_LOAD GET_WEIGHT(DEFAULT_PRIO) +/// @brief Updates task execution statistics. +/// @param task the task to update. +static void __update_task_statistics(task_struct *task); -task_struct *pick_next_task(runqueue_t *runqueue, time_t delta_exec) +/// @brief Employs time-sharing, giving each job a timeslice, and is also +/// preemptive since the scheduler forces the task out of the CPU once +/// the timeslice expires. +/// @param runqueue list of all processes. +/// @param skip_periodic tells the algorithm if there are periodic processes +/// in the list, and in that case it needs to skip them. +/// @return the next task on success, NULL on failure. +static inline task_struct *__scheduler_rr(runqueue_t *runqueue, bool_t skip_periodic) { - // Pointer to the next task to schedule. - task_struct *next = NULL; + // If there is just one task, return it. + if ((runqueue->curr->run_list.next == &runqueue->queue) && + (runqueue->curr->run_list.prev == &runqueue->queue)) { + return runqueue->curr; + } + // By default, the next task is the current one. + task_struct *next = NULL, *entry = NULL; + // Search for the next task (BEWARE: We do not start from the head, so INSIDE skip the head). + list_for_each_decl(it, &runqueue->curr->run_list) + { + // Check if we reached the head of list_head, and skip it. + if (it == &runqueue->queue) + continue; + // Get the current entry. + entry = list_entry(it, task_struct, run_list); + // We consider only runnable processes + if (entry->state != TASK_RUNNING) + continue; + + // Skip the task if it is a periodic one, we are issued to skip + // periodic tasks, and the entry is not a periodic task under + // analysis. + if (entry->se.is_periodic && skip_periodic && !entry->se.is_under_analysis) + continue; + + // We have our next entry. + next = entry; + break; + } + return next; +} + +/// @brief Is a non-preemptive algorithm, where each task is assigned a +/// priority. Processes with highest priority are executed first, while +/// processes with same priority are executed on first-come/first-served +/// basis. Priority can be decided based on memory requirements, time +/// requirements or any other resource requirement. +/// @param runqueue list of all processes. +/// @param skip_periodic tells the algorithm if there are periodic processes +/// in the list, and in that case it needs to skip them. +/// @return the next task on success, NULL on failure. +static inline task_struct *__scheduler_priority(runqueue_t *runqueue, bool_t skip_periodic) +{ + return __scheduler_rr(runqueue, skip_periodic); +} + +/// @brief It aims at giving a fair share of CPU time to processes, and +/// achieves that by associating a virtual runtime to each of them. It always +/// tries to run the task with the smallest vruntime (i.e., the task which +/// executed least so far). It always tries to split up CPU time between +/// runnable tasks as close to "ideal multitasking hardware" as possible. +/// @param runqueue list of all processes. +/// @param skip_periodic tells the algorithm if there are periodic processes +/// in the list, and in that case it needs to skip them. +/// @return the next task on success, NULL on failure. +static inline task_struct *__scheduler_cfs(runqueue_t *runqueue, bool_t skip_periodic) +{ + return __scheduler_rr(runqueue, skip_periodic); +} + +/// @brief Executes the task with the earliest absolute deadline among all +/// the ready tasks. +/// @param runqueue list of all processes. +/// @return the next task on success, NULL on failure. +static inline task_struct *__scheduler_aedf(runqueue_t *runqueue) +{ + return __scheduler_rr(runqueue, false); +} + +/// @brief Executes the task with the earliest absolute DEADLINE among all +/// the ready tasks. When a task was executed, and its period is starting +/// again, it must be set as 'executable again', and its deadline and next_period +/// must be updated. +/// @param runqueue list of all processes. +/// @return the next task on success, NULL on failure. +static inline task_struct *__scheduler_edf(runqueue_t *runqueue) +{ + return __scheduler_rr(runqueue, false); +} + +/// @brief Executes the task with the earliest next PERIOD among all the +/// ready tasks. +/// @details When a task was executed, and its period is starting again, it +/// must be set as 'executable again', and its deadline and next_period must +/// be updated. +/// @param runqueue list of all processes. +/// @return the next task on success, NULL on failure. +static inline task_struct *__scheduler_rm(runqueue_t *runqueue) +{ + return __scheduler_rr(runqueue, false); +} + +task_struct *scheduler_pick_next_task(runqueue_t *runqueue) +{ + // Update task statistics. + __update_task_statistics(runqueue->curr); + + // Pointer to the next task to schedule. + task_struct *next = NULL; #if defined(SCHEDULER_RR) - //==== Implementatin of the Round-Robin Scheduling algorithm ============ - - - - //======================================================================= + next = __scheduler_rr(runqueue, false); #elif defined(SCHEDULER_PRIORITY) - //==== Implementatin of the Priority Scheduling algorithm =============== - - // get the first element of the list - next = list_entry(/*...*/); - - // Get its static priority. - time_t min = /*...*/ - - list_head *it; - // Inter over the runqueue to find the task with the smallest priority value - list_for_each (it, &runqueue->queue) { - task_struct *entry = list_entry(/*...*/); - // Check entry has a lower priority - if (/*...*/) { - /*...*/ - } - } - - //======================================================================= + next = __scheduler_priority(runqueue, false); #elif defined(SCHEDULER_CFS) - //==== Implementatin of the Completely Fair Scheduling ================== - - // Get the weight of the current process. - // (use GET_WEIGHT macro!) - int weight = /*...*/ - - if (weight != NICE_0_LOAD) { - // get the multiplicative factor for its delta_exec. - double factor = /*...*/ - - // weight the delta_exec with the multiplicative factor. - delta_exec = // ... - } - - // Update vruntime of the current process. - // ... - - // Inter over the runqueue to find the task with the smallest vruntime value - // ... - - //======================================================================== + next = __scheduler_cfs(runqueue, false); +#elif defined(SCHEDULER_EDF) + next = __scheduler_edf(runqueue); +#elif defined(SCHEDULER_RM) + next = __scheduler_rm(runqueue); +#elif defined(SCHEDULER_AEDF) + next = __scheduler_aedf(runqueue); #else #error "You should enable a scheduling algorithm!" #endif - assert(next && "No valid task selected. Have you implemented a scheduling algorithm?"); - return next; + assert(next && "No valid task selected by the scheduling algorithm."); + + // Update the last context switch time of the next task. + next->se.exec_start = timer_get_ticks(); + + return next; } + +static void __update_task_statistics(task_struct *task) +{ + assert(task && "Current task is not valid."); + + // While periodic task is under analysis is executed with aperiodic + // scheduler and can be preempted by a "true" periodic task. + // We need to sum all the execution spots to calculate the WCET even + // if is a more pessimistic evaluation. + // Update the delta exec. + task->se.exec_runtime = timer_get_ticks() - task->se.exec_start; + update_process_profiling_timer(task); + + // Set the sum_exec_runtime. + task->se.sum_exec_runtime += task->se.exec_runtime; + + // If the task is not a periodic task we have to update the virtual runtime. + if (!task->se.is_periodic) { + // Get the weight of the current task. + time_t weight = GET_WEIGHT(task->se.prio); + // If the weight is different from the default load, compute it. + if (weight != NICE_0_LOAD) { + // Get the multiplicative factor for its delta_exec. + double factor = ((double)NICE_0_LOAD) / ((double)weight); + // Weight the delta_exec with the multiplicative factor. + task->se.exec_runtime = (int)(((double)task->se.exec_runtime) * factor); + } + // Update vruntime of the current task. + task->se.vruntime += task->se.exec_runtime; + } +} \ No newline at end of file diff --git a/mentos/src/process/user.asm b/mentos/src/process/user.S similarity index 69% rename from mentos/src/process/user.asm rename to mentos/src/process/user.S index 4116e81..044fb0a 100644 --- a/mentos/src/process/user.asm +++ b/mentos/src/process/user.S @@ -1,23 +1,38 @@ ; MentOS, The Mentoring Operating system project ; @file user.asm ; @brief -; @copyright (c) 2019 This file is distributed under the MIT License. +; @copyright (c) 2014-2021 This file is distributed under the MIT License. ; See LICENSE.md for details. ; Enter userspace (ring3) (from Ring 0, namely Kernel) ; Usage: enter_userspace(uintptr_t location, uintptr_t stack); ; On stack -; | stack | -; | location | -; | return address | -; | EBP | EBP +; | stack | [ebp + 0x0C] ARG1 +; | location | [ebp + 0x08] ARG0 +; | return address | [ebp + 0x04] +; | EBP | [ebp + 0x00] ; | SS | ; | ESP | ; | EFLAGS | ; | CS | ; | EIP | +; We can use the following macros to access the arguments ONLY AFTER 0x23 is +; pushed onto the stack, in fact, the first argument is after 0x08 because +; we just pushed first `ebp` and then `0x23`. +%define ARG0 [ebp + 0x08] ; Argument 0 +%define ARG1 [ebp + 0x0C] ; Argument 1 +%define ARG2 [ebp + 0x10] ; Argument 2 +%define ARG3 [ebp + 0x14] ; Argument 3 +%define ARG4 [ebp + 0x18] ; Argument 4 + +; ----------------------------------------------------------------------------- +; SECTION (text) +; ----------------------------------------------------------------------------- +section .text + global enter_userspace ; Allows the C code to call enter_userspace(...). + enter_userspace: push ebp ; Save current ebp @@ -45,38 +60,28 @@ enter_userspace: ;--------------------------------------------------------------------------- ;==== (ESP) Stack address ================================================== - mov eax, [ebp + 0xC] ; get uintptr_t stack + mov eax, ARG1 ; get uintptr_t stack push eax ; push process's stack address on Kernel's stack ;--------------------------------------------------------------------------- ;==== (EFLAGS) ============================================================= pushf ; push EFLAGS into Kernel's stack pop eax ; pop EFLAGS into eax - or eax, 0x200 ; enable interrupt ?request ring3 + or eax, 0x200 ; enable interrupt push eax ; push new EFLAGS on Kernel's stack ;--------------------------------------------------------------------------- ;==== (CS) Code Segment ==================================================== - push 0x1B ; + push 0x1b ; ;--------------------------------------------------------------------------- ;==== (EIP) Entry point ==================================================== - mov eax, [ebp + 0x8] ; get uintptr_t location + mov eax, ARG0 ; get uintptr_t location push eax ; push uintptr_t location on Kernel's stack ;--------------------------------------------------------------------------- iret ; interrupt return + pop ebp + ret - ; WE SHOULD NOT STILL BE HERE! :( - - ;==== Reset segment selector =============================================== - mov ax, 0x10 - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - ;--------------------------------------------------------------------------- - - add esp, 0x14 ; reset stack pointer (20 bytes) - pop ebp ; reset value of ebp - ret ; return to kernel code + ; WE SHOULD NOT STILL BE HERE! :(p \ No newline at end of file diff --git a/mentos/src/process/wait.c b/mentos/src/process/wait.c new file mode 100644 index 0000000..9b37b82 --- /dev/null +++ b/mentos/src/process/wait.c @@ -0,0 +1,42 @@ +/// MentOS, The Mentoring Operating system project +/// @file wait.c +/// @brief wait functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +/// Change the header. +#define __DEBUG_HEADER__ "[WAIT ]" + +#include "process/wait.h" + +static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *wq) +{ + list_head_add_tail(&wq->task_list, &head->task_list); +} + +static inline void __remove_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *wq) +{ + list_head_del(&wq->task_list); +} + +void init_waitqueue_entry(wait_queue_entry_t *wq, struct task_struct *task) +{ + wq->flags = 0; + wq->task = task; + wq->func = default_wake_function; +} + +void add_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *wq) +{ + wq->flags &= ~WQ_FLAG_EXCLUSIVE; + spinlock_lock(&head->lock); + __add_wait_queue(head, wq); + spinlock_unlock(&head->lock); +} + +void remove_wait_queue(wait_queue_head_t *head, wait_queue_entry_t *wq) +{ + spinlock_lock(&head->lock); + __remove_wait_queue(head, wq); + spinlock_unlock(&head->lock); +} \ No newline at end of file diff --git a/mentos/src/sys/dirent.c b/mentos/src/sys/dirent.c deleted file mode 100644 index 971e12f..0000000 --- a/mentos/src/sys/dirent.c +++ /dev/null @@ -1,98 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file dirent.c -/// @brief Functions used to manage directories. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "dirent.h" -#include "string.h" -#include "vfs.h" -#include "kheap.h" -#include "stdio.h" -#include "initrd.h" -#include "debug.h" - -DIR *opendir(const char *path) -{ - char absolute_path[MAX_PATH_LENGTH]; - DIR *pdir = NULL; - - strcpy(absolute_path, path); - // If the first character is not the '/' then get the absolute path. - if (absolute_path[0] != '/') - { - if (!get_absolute_path(absolute_path)) - { - dbg_print("Cannot get the absolute path.\n"); - - return NULL; - } - } - - // Get the mount point id. - int32_t mp_id = get_mountpoint_id(absolute_path); - if (mp_id < 0) - { - printf("opendir: cannot open directory '%s':" - "Cannot find mount-point\n", absolute_path); - - return NULL; - } - - if (mountpoint_list[mp_id].dir_op.opendir_f == NULL) - { - printf("opendir: cannot open directory '%s':" - "No opendir function\n", absolute_path); - - return NULL; - } - - pdir = mountpoint_list[mp_id].dir_op.opendir_f(absolute_path); - // If the directiry is correctly open, set the handle. - if (pdir != NULL) - { - pdir->handle = mp_id; - } - - return pdir; -} - -int closedir(DIR *dirp) -{ - if (dirp == NULL) - { - printf("closedir: cannot close directory :" - "Directory pointer is not valid\n"); - - return -1; - } - if (mountpoint_list[dirp->handle].dir_op.closedir_f == NULL) - { - printf("closedir: cannot close directory '%s':" - "No closedir function\n", dirp->path); - - return -1; - } - - return mountpoint_list[dirp->handle].dir_op.closedir_f(dirp); -} - -dirent_t *readdir(DIR *dirp) -{ - if (dirp == NULL) - { - printf("readdir: cannot read directory :" - "Directory pointer is not valid\n"); - - return NULL; - } - if (mountpoint_list[dirp->handle].dir_op.readdir_f == NULL) - { - printf("readdir: cannot read directory '%s':" - "No readdir function\n", dirp->path); - - return NULL; - } - - return mountpoint_list[dirp->handle].dir_op.readdir_f(dirp); -} diff --git a/mentos/src/sys/module.c b/mentos/src/sys/module.c index 62bce4e..88d8b7b 100644 --- a/mentos/src/sys/module.c +++ b/mentos/src/sys/module.c @@ -1,7 +1,73 @@ /// MentOS, The Mentoring Operating system project /// @file module.c /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "module.h" +#include "mem/slab.h" +#include "sys/module.h" +#include "string.h" +#include "sys/bitops.h" +#include "misc/debug.h" + +/// Defined in kernel.ld, points at the end of kernel's data segment. +extern void *_kernel_end; +/// List of modules. +multiboot_module_t modules[MAX_MODULES]; + +int init_modules(multiboot_info_t *header) +{ + for (int i = 0; i < MAX_MODULES; ++i) { + modules[i].mod_start = 0; + modules[i].mod_end = 0; + modules[i].cmdline = 0; + modules[i].pad = 0; + } + if (!bitmask_check(header->flags, MULTIBOOT_FLAG_MODS)) + return 0; + multiboot_module_t *mod = first_module(header); + for (int i = 0; (mod != 0) && (i < MAX_MODULES); + ++i, mod = next_module(header, mod)) { + memcpy(&modules[i], mod, sizeof(multiboot_module_t)); + } + return 1; +} + +int relocate_modules() +{ + for (int i = 0; i < MAX_MODULES; ++i) { + // Exit if modules are finished + if (!modules[i].mod_start) + break; + + // Get module and cmdline sizes + uint32_t mod_size = modules[i].mod_end - modules[i].mod_start; + uint32_t cmdline_size = strlen((const char *)modules[i].cmdline) + 1; + + // Allocate needed memory, to copy both module and command line + uint32_t memory = (uint32_t)kmalloc(mod_size + cmdline_size); + + if (!memory) + return 0; + + // Copy module and its command line + memcpy((char *)memory, (char *)modules[i].mod_start, mod_size); + memcpy((char *)memory + mod_size, (char *)modules[i].cmdline, cmdline_size); + + // Change the module address to point to new allocated memory + modules[i].mod_start = memory; + modules[i].mod_end = modules[i].cmdline = memory + mod_size; + } + return 1; +} + +uintptr_t get_address_after_modules() +{ + // By default the first valid address is end. + uintptr_t address_after_modules = (uintptr_t)&_kernel_end; + for (int i = 0; i < MAX_MODULES; ++i) { + if (modules[i].mod_start != 0) + address_after_modules = modules[i].mod_end; + } + return address_after_modules; +} diff --git a/mentos/src/sys/shm.c b/mentos/src/sys/shm.c deleted file mode 100644 index a2c8d21..0000000 --- a/mentos/src/sys/shm.c +++ /dev/null @@ -1,362 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file shm.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "shm.h" - -struct shmid_ds *head = NULL; -static ushort shm_descriptor = 0; - -int syscall_shmctl(int *args) -{ - int shmid = args[0]; - int cmd = args[1]; - - // TODO: for IPC_STAT - // struct shmid_ds * buf = (struct shmid_ds *) args[2]; - - struct shmid_ds *myshmid_ds = find_shm_fromid(shmid); - - if (myshmid_ds == NULL) { - return -1; - } - - // Upgrade shm info. - myshmid_ds->shm_lpid = kernel_get_current_process()->pid; - myshmid_ds->shm_ctime = time(NULL); - - switch (cmd) { - case IPC_RMID: - if (myshmid_ds->shm_nattch == 0) { - kfree(myshmid_ds->shm_location); - - // Manage list. - if (myshmid_ds == head) { - head = head->next; - } else { - // Finding the previous shmid_ds. - struct shmid_ds *prev = head; - while (prev->next != myshmid_ds) { - prev = prev->next; - } - prev->next = myshmid_ds->next; - } - kfree(myshmid_ds); - } else { - (myshmid_ds->shm_perm).mode |= SHM_DEST; - } - - return 0; - - case IPC_STAT: - break; - case IPC_SET: - break; - case SHM_LOCK: - break; - case SHM_UNLOCK: - break; - default: - break; - } - - return -1; -} - -// Get shared memory segment. -int syscall_shmget(int *args) -{ - int flags = args[2]; - key_t key = (key_t)args[0]; - size_t size = (size_t)args[1]; - - struct shmid_ds *shmid_ds; - - if (flags & IPC_EXCL) { - return -1; - } - - if (flags & IPC_CREAT) { - shmid_ds = find_shm_fromkey(key); - - if (shmid_ds != NULL) { - return -1; - } - - shmid_ds = kmalloc(sizeof(struct shmid_ds)); - dbg_print("\n[SHM] shmget() shmid_ds : 0x%p", shmid_ds); - - shmid_ds->shm_location = kmalloc_align(size); - dbg_print("\n[SHM] shmget() Location : 0x%p", - shmid_ds->shm_location); - dbg_print("\n[SHM] shmget() physLocation : 0x%p", - paging_virtual_to_physical(get_current_page_directory(), - shmid_ds->shm_location)); - - shmid_ds->next = head; - head = shmid_ds; - - shmid_ds->shm_segsz = size; - shmid_ds->shm_atime = 0; - shmid_ds->shm_dtime = 0; - shmid_ds->shm_ctime = 0; - shmid_ds->shm_cpid = kernel_get_current_process()->pid; - shmid_ds->shm_lpid = kernel_get_current_process()->pid; - shmid_ds->shm_nattch = 0; - - // No user implementation. - (shmid_ds->shm_perm).cuid = 0; - // No group implementation. - (shmid_ds->shm_perm).cgid = 0; - // No user implementation - (shmid_ds->shm_perm).uid = 0; - // No group implementation. - (shmid_ds->shm_perm).gid = 0; - (shmid_ds->shm_perm).mode = flags & 0777; - (shmid_ds->shm_perm).seq = shm_descriptor++; - (shmid_ds->shm_perm).key = key; - } else { - shmid_ds = find_shm_fromkey(key); - dbg_print("\n[SHM] shmget() shmid_ds found : 0x%p", shmid_ds); - - if (shmid_ds == NULL) { - return -1; - } - - if ((flags & 0777) > ((shmid_ds->shm_perm).mode & 0777)) { - return -1; - } - shmid_ds->shm_lpid = kernel_get_current_process()->pid; - } - - return (shmid_ds->shm_perm).seq; -} - -// Attach shared memory segment. -void *syscall_shmat(int *args) -{ - int shmid = args[0]; - void *shmaddr = (void *)args[1]; - - // TODO: for more settings - // int flags = args[2]; - - struct shmid_ds *myshmid_ds = find_shm_fromid(shmid); - dbg_print("\n[SHM] shmat() shmid_ds found : 0x%p", myshmid_ds); - - if (myshmid_ds == NULL) { - return (void *)-1; - } - - void *shm_start = myshmid_ds->shm_location; - - if (shmaddr == NULL) { - void *ret = kmalloc_align(myshmid_ds->shm_segsz); - - uint32_t shm_vaddr_start = (uint32_t)ret & 0xfffff000; - uint32_t shm_vaddr_end = - ((uint32_t)ret + myshmid_ds->shm_segsz) & 0xfffff000; - - uint32_t shm_paddr_start = (uint32_t)paging_virtual_to_physical( - get_current_page_directory(), shm_start); - - free_map_region(get_current_page_directory(), shm_vaddr_start, - shm_vaddr_end, true); - - while (shm_vaddr_start <= shm_vaddr_end) { - paging_allocate_page(get_current_page_directory(), shm_vaddr_start, - shm_paddr_start / PAGE_SIZE, true, true); - shm_vaddr_start += PAGE_SIZE; - shm_paddr_start += PAGE_SIZE; - } - - dbg_print("\n[SHM] shmat() vaddr : 0x%p", ret); - dbg_print("\n[SHM] shmat() paddr : 0x%p", - (void *)shm_paddr_start); - dbg_print("\n[SHM] shmat() paddr after map: 0x%p", - paging_virtual_to_physical(get_current_page_directory(), - ret)); - - // Upgrade shm info. - myshmid_ds->shm_lpid = kernel_get_current_process()->pid; - (myshmid_ds->shm_nattch)++; - myshmid_ds->shm_atime = time(NULL); - - return ret; - } - - return (void *)-1; -} - -// Detach shared memory segment. -int syscall_shmdt(int *args) -{ - void *shmaddr = (void *)args[0]; - - if (shmaddr == NULL) { - return -1; - } - - struct shmid_ds *myshmid_ds = find_shm_fromvaddr(shmaddr); - dbg_print("\n[SHM] shmdt() shmid_ds found : 0x%p", myshmid_ds); - - if (myshmid_ds == NULL) { - return -1; - } - - // ===== Test ============================================================== - uint32_t shm_vaddr_start = (uint32_t)shmaddr & 0xfffff000; - uint32_t shm_vaddr_end = - ((uint32_t)shmaddr + myshmid_ds->shm_segsz) & 0xfffff000; - - free_map_region(get_current_page_directory(), shm_vaddr_start, - shm_vaddr_end, false); - - while (shm_vaddr_start <= shm_vaddr_end) { - paging_allocate_page(get_current_page_directory(), shm_vaddr_start, - shm_vaddr_start / PAGE_SIZE, true, true); - shm_vaddr_start += PAGE_SIZE; - } - // ========================================================================= - - kfree(shmaddr); - - // Upgrade shm info. - myshmid_ds->shm_lpid = kernel_get_current_process()->pid; - (myshmid_ds->shm_nattch)--; - myshmid_ds->shm_dtime = time(NULL); - - // Manage SHM_DEST flag on. - if (myshmid_ds->shm_nattch == 0 && (myshmid_ds->shm_perm).mode & SHM_DEST) { - kfree(myshmid_ds->shm_location); - - // Manage list. - if (myshmid_ds == head) { - head = head->next; - } else { - // Finding the previous shmid_ds. - struct shmid_ds *prev = head; - while (prev->next != myshmid_ds) { - prev = prev->next; - } - prev->next = myshmid_ds->next; - } - kfree(myshmid_ds); - } - - return 0; -} - -int shmctl(int shmid, int cmd, struct shmid_ds *buf) -{ - int error; - - __asm__("movl %0, %%ecx\n" - "movl %1, %%ebx\n" - "movl %2, %%edx\n" - "movl $6, %%eax\n" - "int $80\n" - : - : "r"(shmid), "r"(cmd), "r"(buf)); - __asm__("movl %%eax, %0\n\t" : "=r"(error)); - - return error; -} - -int shmget(key_t key, size_t size, int flags) -{ - int id; - - __asm__("movl %0, %%ecx\n" - "movl %1, %%ebx\n" - "movl %2, %%edx\n" - "movl $3, %%eax\n" - "int $80\n" - : - : "r"(key), "r"(size), "r"(flags)); - __asm__("movl %%eax, %0\n\t" : "=r"(id)); - - return id; -} - -void *shmat(int shmid, void *shmaddr, int flag) -{ - void *addr; - - __asm__("movl %0, %%ecx\n" - "movl %1, %%ebx\n" - "movl %2, %%edx\n" - "movl $4, %%eax\n" - "int $80\n" - : - : "r"(shmid), "r"(shmaddr), "r"(flag)); - // The kernel is serving my system call - - // Now I have the control - __asm__("movl %%eax, %0\n\t" : "=r"(addr)); - - return addr; -} - -int shmdt(void *shmaddr) -{ - int error; - - __asm__("movl %0, %%ecx\n" - "movl $5, %%eax\n" - "int $80\n" - : - : "r"(shmaddr)); - __asm__("movl %%eax, %0\n\t" : "=r"(error)); - - return error; -} - -struct shmid_ds *find_shm_fromid(int shmid) -{ - struct shmid_ds *res = head; - - while (res != NULL) { - if ((res->shm_perm).seq == shmid) { - return res; - } - res = res->next; - } - - return NULL; -} - -struct shmid_ds *find_shm_fromkey(key_t key) -{ - struct shmid_ds *res = head; - - while (res != NULL) { - if ((res->shm_perm).key == key) { - return res; - } - res = res->next; - } - - return NULL; -} - -struct shmid_ds *find_shm_fromvaddr(void *shmvaddr) -{ - void *shmpaddr = - paging_virtual_to_physical(get_current_page_directory(), shmvaddr); - void *paddr; - struct shmid_ds *res = head; - - while (res != NULL) { - paddr = paging_virtual_to_physical(get_current_page_directory(), - res->shm_location); - if (paddr == shmpaddr) { - return res; - } - res = res->next; - } - - return NULL; -} diff --git a/mentos/src/sys/stat.c b/mentos/src/sys/stat.c deleted file mode 100644 index 3fbb349..0000000 --- a/mentos/src/sys/stat.c +++ /dev/null @@ -1,84 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file stat.c -/// @brief Stat functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "vfs.h" -#include "kheap.h" -#include "stdio.h" -#include "string.h" -#include "initrd.h" - -int stat(const char *path, stat_t *buf) -{ - // Reset the structure. - buf->st_dev = 0; - buf->st_ino = 0; - buf->st_mode = 0; - buf->st_uid = 0; - buf->st_gid = 0; - buf->st_size = 0; - buf->st_atime = 0; - buf->st_mtime = 0; - buf->st_ctime = 0; - - char absolute_path[MAX_PATH_LENGTH]; - strcpy(absolute_path, path); - - if (path[0] != '/') - { - get_absolute_path(absolute_path); - } - - int32_t mp_id = get_mountpoint_id(absolute_path); - if (mp_id < 0) - { - printf("stat: cannot execute stat of '%s': Not exists\n", path); - return -1; - } - - buf->st_dev = (uint32_t) mp_id; - if (mountpoint_list[mp_id].stat_op.stat_f == NULL) - { - printf("stat: cannot execute stat of '%s': Not stat function\n", - path); - - return -1; - } - mountpoint_list[mp_id].stat_op.stat_f(absolute_path, buf); - - return 0; -} - -int mkdir(const char *path, mode_t mode) -{ - char absolute_path[MAX_PATH_LENGTH]; - strcpy(absolute_path, path); - int result = -1; - - if (path[0] != '/') - { - get_absolute_path(absolute_path); - } - - int32_t mp_id = get_mountpoint_id(absolute_path); - if (mp_id < 0) - { - printf("mkdir: cannot create directory '%s':" - "Cannot find mount-point\n", path); - - return result; - } - - if (mountpoint_list[mp_id].dir_op.mkdir_f == NULL) - { - printf("mkdir: cannot create directory '%s': " - "No mkdir function\n", path); - - return result; - } - result = mountpoint_list[mp_id].dir_op.mkdir_f(absolute_path, mode); - - return result; -} diff --git a/mentos/src/sys/unistd.c b/mentos/src/sys/unistd.c deleted file mode 100644 index d8b88a6..0000000 --- a/mentos/src/sys/unistd.c +++ /dev/null @@ -1,66 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file unistd.c -/// @brief Functions used to manage files. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "vfs.h" -#include "stdio.h" -#include "fcntl.h" -#include "kheap.h" -#include "errno.h" -#include "initrd.h" -#include "string.h" -#include "unistd.h" - -int close(int fildes) -{ - if (fildes < 0) - { - return -1; - } - if (fd_list[fildes].fs_spec_id >= -1) - { - int mp_id = fd_list[fildes].mountpoint_id; - if (mountpoint_list[mp_id].operations.close_f != NULL) - { - int fs_fd = fd_list[fildes].fs_spec_id; - mountpoint_list[mp_id].operations.close_f(fs_fd); - } - fd_list[fildes].fs_spec_id = -1; - fd_list[fildes].mountpoint_id = -1; - } - else - { - return -1; - } - - return 0; -} - -int rmdir(const char *path) -{ - char absolute_path[MAX_PATH_LENGTH]; - strcpy(absolute_path, path); - - if (path[0] != '/') - { - get_absolute_path(absolute_path); - } - - int32_t mp_id = get_mountpoint_id(absolute_path); - if (mp_id < 0) - { - printf("rmdir: failed to remove '%s':" - "Cannot find mount-point\n", path); - - return -1; - } - - if (mountpoint_list[mp_id].dir_op.rmdir_f == NULL) - { - return -1; - } - - return mountpoint_list[mp_id].dir_op.rmdir_f(absolute_path); -} diff --git a/mentos/src/sys/utsname.c b/mentos/src/sys/utsname.c index 007d539..9a10dc2 100644 --- a/mentos/src/sys/utsname.c +++ b/mentos/src/sys/utsname.c @@ -1,20 +1,54 @@ /// MentOS, The Mentoring Operating system project /// @file utsname.c /// @brief Functions used to provide information about the machine & OS. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. #include "string.h" -#include "utsname.h" +#include "sys/utsname.h" #include "version.h" +#include "misc/debug.h" +#include "sys/errno.h" +#include "fcntl.h" +#include "fs/vfs.h" -int uname(utsname_t *os_infos) +static inline int __gethostname(char *name, size_t len) { - // Uname code goes here. - strcpy(os_infos->sysname, OS_NAME); - strcpy(os_infos->version, OS_VERSION); - strcpy(os_infos->nodename, "testbed"); - strcpy(os_infos->machine, "i686"); - + // Check if name is an invalid address. + if (!name) + return -EFAULT; + // Check if len is negative. + if (len < 0) + return -EINVAL; + // Open the file. + vfs_file_t *file = vfs_open("/etc/hostname", O_RDONLY, 0); + if (file == NULL) { + pr_err("Cannot find `/etc/hostname`.\n"); + return -ENOENT; + } + // Clear the buffer. + memset(name, 0, len); + // Read the content of the file. + ssize_t ret = vfs_read(file, name, 0UL, len); + if (ret < 0) { + pr_err("Failed to read `/etc/hostname`.\n"); + return ret; + } + // Close the file. + vfs_close(file); + return 0; +} + +int sys_uname(utsname_t *buf) +{ + if (buf == NULL) { + return -EFAULT; + } + // Uname code goes here. + strcpy(buf->sysname, OS_NAME); + strcpy(buf->version, OS_VERSION); + strcpy(buf->release, OS_VERSION); + __gethostname(buf->nodename, SYS_LEN); + strcpy(buf->machine, "i686"); return 0; } diff --git a/mentos/src/system/errno.c b/mentos/src/system/errno.c index a48bdfc..c4bc9e1 100644 --- a/mentos/src/system/errno.c +++ b/mentos/src/system/errno.c @@ -1,22 +1,20 @@ /// MentOS, The Mentoring Operating system project /// @file errno.c /// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "errno.h" -#include "scheduler.h" +#include "sys/errno.h" +#include "process/scheduler.h" +/// @brief Returns the error number for the current process. +/// @return Pointer to the error number. int *__geterrno() { - static int _errno = 0; - - task_struct *current_process = kernel_get_current_process(); - - if (current_process == NULL) - { + static int _errno = 0; + task_struct *current_process = scheduler_get_current_process(); + if (current_process == NULL) { return &_errno; } - return ¤t_process->error_no; } diff --git a/mentos/src/system/panic.c b/mentos/src/system/panic.c index 57e0030..6693357 100644 --- a/mentos/src/system/panic.c +++ b/mentos/src/system/panic.c @@ -1,18 +1,16 @@ /// MentOS, The Mentoring Operating system project /// @file panic.c /// @brief Functions used to manage kernel panic. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "panic.h" -#include "elf.h" -#include "stdio.h" -#include "kernel.h" -#include "debug.h" +#include "system/panic.h" +#include "misc/debug.h" void kernel_panic(const char *msg) { - dbg_print("\nPANIC:\n%s\n\nWelcome to Kernel Debugging Land...\n\n", msg); - dbg_print("\n"); - for (;;); + pr_emerg("\nPANIC:\n%s\n\nWelcome to Kernel Debugging Land...\n\n", msg); + pr_emerg("\n"); + asm("cli"); // Disable interrupts + for (;;) asm("hlt"); // Decrease power consumption with hlt } diff --git a/mentos/src/system/printk.c b/mentos/src/system/printk.c index 9518a64..00e475e 100644 --- a/mentos/src/system/printk.c +++ b/mentos/src/system/printk.c @@ -1,23 +1,22 @@ /// MentOS, The Mentoring Operating system project /// @file printk.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @brief Functions for managing the kernel messages. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "printk.h" +#include "system/printk.h" #include "stdarg.h" #include "stdio.h" -#include "video.h" +#include "io/video.h" -void printk(const char * format, ...) +int sys_syslog(const char *format, ...) { char buffer[4096]; va_list ap; // Start variabile argument's list. - va_start (ap, format); + va_start(ap, format); int len = vsprintf(buffer, format, ap); - va_end (ap); - - for (size_t i = 0; (i < len); ++i) - video_putc(buffer[i]); + va_end(ap); + video_puts(buffer); + return len; } diff --git a/mentos/src/system/signal.c b/mentos/src/system/signal.c new file mode 100644 index 0000000..dfc8f7e --- /dev/null +++ b/mentos/src/system/signal.c @@ -0,0 +1,798 @@ +/// MentOS, The Mentoring Operating system project +/// @file signal.c +/// @brief Signals definition. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +/// Change the header. +#define __DEBUG_HEADER__ "[SIGNAL]" + +#include "system/signal.h" +#include "process/wait.h" +#include "process/scheduler.h" +#include "process/process.h" +#include "sys/errno.h" +#include "assert.h" +#include "misc/debug.h" +#include "string.h" +#include "klib/irqflags.h" + +/// SLAB caches for signal bits. +static kmem_cache_t *sigqueue_cachep; + +/// Contains all stopped process waiting for a continue signal +static struct wait_queue_head_t stopped_queue; + +static const char *sys_siglist[] = { + "HUP", + "INT", + "QUIT", + "ILL", + "TRAP", + "ABRT", + "EMT", + "FPE", + "KILL", + "BUS", + "SEGV", + "SYS", + "PIPE", + "ALRM", + "TERM", + "USR1", + "USR2", + "CHLD", + "PWR", + "WINCH", + "URG", + "POLL", + "STOP", + "TSTP", + "CONT", + "TTIN", + "TTOU", + "VTALRM", + "PROF", + "XCPU", + "XFSZ", + NULL, +}; + +static inline void __copy_siginfo(siginfo_t *to, const siginfo_t *from) +{ + memcpy(to, from, sizeof(*to)); +} + +static inline void __clear_siginfo(siginfo_t *info) +{ + memset(info, 0, sizeof(*info)); +} + +static inline void __lock_task_sighand(struct task_struct *t) +{ + assert(t && "Null task struct."); + spinlock_lock(&t->sighand.siglock); +} + +static inline void __unlock_task_sighand(struct task_struct *t) +{ + assert(t && "Null task struct."); + spinlock_unlock(&t->sighand.siglock); +} + +static sighandler_t __get_handler(struct task_struct *t, int sig) +{ + assert(t && "Null task struct."); + return t->sighand.action[sig - 1].sa_handler; +} + +static int __sig_is_ignored(struct task_struct *t, int sig) +{ + // Blocked signals are never ignored, since the + // signal handler may change by the time it is + // unblocked. + if (sigismember(&t->blocked, sig) || sigismember(&t->real_blocked, sig)) + return 0; + // Get the signal handler. + sighandler_t handler = __get_handler(t, sig); + // Check the type of the handler. + return (handler == SIG_IGN) && (sig != SIGCHLD); + // TODO: do_signal() specifically checks if the handler is IGN and the signal + // is SIGCHLD, in that case it forces a wait for the parent, that's why + // here I'm also accepting as not-ignored a SIG_IGN which is a SIGCHLD. +} + +/// @brief Allocate a new signal queue record. +/// @param t The task to which the signal belongs. +/// @param sig The signal to set. +/// @param flags Flags identifying from where we are going to take the memory. +static sigqueue_t *__sigqueue_alloc(struct task_struct *t, int sig, gfp_t flags) +{ + sigqueue_t *q = NULL; + if ((q = kmem_cache_alloc(sigqueue_cachep, flags)) == NULL) + return NULL; + // Initiliaze the values. + q->flags = 0; + list_head_init(&q->list); + return q; +} + +static void __sigqueue_free(sigqueue_t *q) +{ + if (q) + kmem_cache_free(q); +} + +/// @brief +/// @param sig Signal to be sent. +/// @param info The signal info +/// @param t The process to which we send the signal. +/// @return +static int __send_signal(int sig, siginfo_t *info, struct task_struct *t) +{ + // Lock the signal handling for the given task. + __lock_task_sighand(t); + pr_debug("Trying to add signal (%2d)`%s` to task (%2d)`%s`, currently pending `%d, %d`.\n", + sig, strsignal(sig), t->pid, t->name, t->pending.signal.sig[0], t->pending.signal.sig[1]); + // Check if the signal is ignored. + if (__sig_is_ignored(t, sig)) { + pr_debug("Trying to send signal (%2d)`%s` to task (%2d)`%s`: ignored.\n", + sig, strsignal(sig), t->pid, t->name); + __unlock_task_sighand(t); + return 0; + } + // Check if the process is in an invalid status. + if ((t->state == EXIT_ZOMBIE) || (t->state == EXIT_DEAD)) { + pr_debug("Trying to send signal (%2d)`%s` to task (%2d)`%s`: zombie or dead.\n", + sig, strsignal(sig), t->pid, t->name); + __unlock_task_sighand(t); + return -EINVAL; + } + sigqueue_t *q = __sigqueue_alloc(t, sig, GFP_KERNEL); + if (q == NULL) { + __unlock_task_sighand(t); + return -EAGAIN; + } + list_head_add_tail(&q->list, &t->pending.list); + if (info != SEND_SIG_NOINFO) + memcpy(&q->info, info, sizeof(siginfo_t)); + // Set that there is a signal pending. + sigaddset(&t->pending.signal, sig); + pr_debug("Added pending signal (%2d)`%s` to task (%2d)`%s`, pending `%d, %d`.\n", + sig, strsignal(sig), t->pid, t->name, t->pending.signal.sig[0], t->pending.signal.sig[1]); + __unlock_task_sighand(t); + return 0; +} + +static inline int __next_signal(sigpending_t *pending, sigset_t *mask) +{ + pr_debug("__next_signal(%p, %p)\n", pending, mask); + assert(pending && "Null `pending` structure."); + assert(mask && "Null `mask` structure."); + unsigned long x; + if ((x = bitmask_clear(pending->signal.sig[0], mask->sig[0])) != 0) + return 1 + find_first_non_zero(x); + if ((x = bitmask_clear(pending->signal.sig[1], mask->sig[1])) != 0) + return 33 + find_first_non_zero(x); + return 0; +} + +static inline void __collect_signal(int sig, sigpending_t *list, siginfo_t *info) +{ + pr_debug("__collect_signal(%d, %p, %p)\n", sig, list, info); + assert(list && "Null `list` structure."); + assert(info && "Null `info` structure."); + + sigqueue_t *queue_entry = NULL; + bool_t still_pending = false; + // Collect the siginfo appropriate to this signal. Check if + // there is another siginfo for the same signal. + list_for_each_decl(it, &list->list) + { + sigqueue_t *q = list_entry(it, sigqueue_t, list); + pr_debug("__collect_signal(%d, %p, %p) : Signal in queue : %p(%d : %s).\n", sig, list, info, + q, q->info.si_signo, strsignal(q->info.si_signo)); + if (q->info.si_signo == sig) { + // If the entry is already set, this means that there are several handlers + // pending for this particular signal. + if (queue_entry) { + pr_debug("__collect_signal(%d, %p, %p) : Still pending, do not remove from set.\n", sig, list, info); + still_pending = true; + break; + } + // Store the entry we encounter. + queue_entry = q; + } + } + // If there are no other signals pending of the same type, + // remove the signal from the set. + if (!still_pending) { + sigdelset(&list->signal, sig); + pr_debug("__collect_signal(%d, %p, %p) : Remove signal from set: %d.\n", sig, list, info, + list->signal.sig[0]); + } + // If we have found an entry. + if (queue_entry) { + pr_debug("__collect_signal(%d, %p, %p) : Remove and delete sigqueue entry : %p.\n", sig, list, info, queue_entry); + // Remove the entry from the queue. + list_head_del(&queue_entry->list); + // Copy the details about the entry inside the info structure. + __copy_siginfo(info, &queue_entry->info); + // Free the memory for the queue entry. + __sigqueue_free(queue_entry); + } else { + pr_debug("__collect_signal(%d, %p, %p) : Cannot find the signal in the queue.\n", sig, list, info); + // Ok, it wasn't in the queue, zero out the info. + __clear_siginfo(info); + // Get the current process. + struct task_struct *current = scheduler_get_current_process(); + assert(current && "There is no running process."); + // Initialize the info. + info->si_signo = sig; + info->si_code = SI_USER; + info->si_value.sival_int = 0; + info->si_errno = 0; + info->si_pid = current->pid; + info->si_uid = current->uid; + info->si_addr = NULL; + info->si_status = 0; + info->si_band = 0; + } +} + +static inline int __dequeue_signal(sigpending_t *pending, sigset_t *mask, siginfo_t *info) +{ + pr_debug("__dequeue_signal(%p, %p, %p)\n", pending, mask, info); + // The dequeue_signal( ) always considers the lowest-numbered pending signal. + // It updates the data structures to indicate that the signal is no longer + // pending and returns its number. + int sig = __next_signal(pending, mask); + if ((sig > 0) && (sig < NSIG)) { + __collect_signal(sig, pending, info); + } + return sig; +} + +static inline int __handle_signal(int signr, siginfo_t *info, sigaction_t *ka, struct pt_regs *regs) +{ + pr_debug("__handle_signal(%d, %p, %p, %p)\n", signr, info, ka, regs); + // The do_signal() function is usually only invoked when the CPU is going + // to return in User Mode. + struct task_struct *current = scheduler_get_current_process(); + assert(current && "There is no running process."); + // Skip the `init` process, always. + if (current->pid == 1) { + errno = ESRCH; + return 0; + } + // Save the previous signal mask. + memcpy(¤t->saved_sigmask, ¤t->blocked, sizeof(sigset_t)); + + // Add the signal to the list of blocked signals. + sigaddset(¤t->blocked, signr); + + // Store the registers before setting the ones required by the signal handling. + current->thread.signal_regs = *regs; + + // Restore the registers for the process that has set the signal. + *regs = current->thread.regs; + + // Set the instruction pointer. + regs->eip = (uintptr_t)ka->sa_handler; + + // If the user is also asking for the signal info, push it into the stack. + if (bitmask_check(ka->sa_flags, SA_SIGINFO)) { + // Move the stack so that we have space for storing the siginfo. + regs->useresp -= sizeof(siginfo_t); + // Save the pointer where the siginfo is stored. + siginfo_t *siginfo_addr = (siginfo_t *)regs->useresp; + // We push on the stack the entire siginfo. + __copy_siginfo(siginfo_addr, info); + // We push on the stack the pointer to the siginfo we copied on the stack. + PUSH_ARG(regs->useresp, siginfo_t *, siginfo_addr); + } + + // Push on the stack the signal number, first and only argument of the handler. + PUSH_ARG(regs->useresp, int, signr); + + // Push on the stack the function required to handle the signal return. + PUSH_ARG(regs->useresp, uint32_t, current->sigreturn_eip); + + return 1; +} + +long sys_sigreturn(struct pt_regs *f) +{ + pr_debug("sys_sigreturn(%p)\n", f); + struct task_struct *current = scheduler_get_current_process(); + assert(current && "There is no running process."); + // Restore the registers before the signal handling. + *f = current->thread.signal_regs; + // Restore the previous signal mask. + memcpy(¤t->blocked, ¤t->saved_sigmask, sizeof(sigset_t)); + // Switch to process page directory + paging_switch_directory_va(current->mm->pgd); + pr_debug("sys_sigreturn(%p) : done!\n", f); + return 0; +} + +// Send signal to parent +static int __notify_parent(struct task_struct *current, int signr) +{ + siginfo_t info; + info.si_signo = signr; + info.si_code = SI_KERNEL; + info.si_value.sival_int = 0; + info.si_errno = 0; + info.si_pid = current->pid; + info.si_uid = current->uid; + info.si_addr = NULL; + info.si_status = 0; + info.si_band = 0; + return __send_signal(signr, &info, current->parent); +} + +// Removes from the pending signal queue q the pending signals corresponding to +// the bit mask mask +static void __rm_from_queue(sigset_t *mask, sigpending_t *q) +{ + list_head *it, *tmp; + list_for_each_safe (it, tmp, &q->list) { + struct sigqueue_t *entry = list_entry(it, struct sigqueue_t, list); + int sig = entry->info.si_signo; + + if (sigismember(mask, sig)) { + list_head_del(it); + kfree(entry); + } + } +} + +// We do not consider group stopping because for now we don't have thread groups +static void __do_signal_stop(struct task_struct *current, struct pt_regs *f, int signr) +{ + // The do_signal( ) function also sends a SIGCHLD signal to + // the parent process of current, unless the parent has set + // the SA_NOCLDSTOP flag of SIGCHLD. + if (!(SA_NOCLDSTOP & current->parent->sighand.action[SIGCHLD - 1].sa_flags)) + if (__notify_parent(current, SIGCHLD) != 0) + pr_debug("Failed to notify parent with signal: %d", signr); + + // The state is now TASK_UNINTERRUPTABLE + sleep_on(&stopped_queue); + + current->state = TASK_STOPPED; + current->exit_code = signr; + + scheduler_run(f); +} + +int do_signal(struct pt_regs *f) +{ + // The do_signal() function is usually only invoked when the CPU is going + // to return in User Mode. + struct task_struct *current = scheduler_get_current_process(); + if (current == NULL) + return 0; + + // First, checks whether the function itself was triggered by an interrupt; + // if so, it simply returns. Otherwise, if the function was triggered by an + // exception that was raised while the process was running in User Mode, + // the function continues executing. + if ((f->cs & 3) != 3) + return 0; + + // Create a siginfo. + siginfo_t info; + // The return code of __dequeue_signal( ) is stored in signr. + int signr, exit_code; + + // Lock the signal handling for the given task. + __lock_task_sighand(current); + + // The heart of the do_signal( ) function consists of a loop that + // repeatedly invokes the __dequeue_signal( ) function until no + // non-blocked pending signals are left. + while (!list_head_empty(¤t->pending.list)) { + // Get the signal to deliver. + signr = exit_code = __dequeue_signal(¤t->pending, ¤t->blocked, &info); + + // Check the signal that we want to send. + if ((signr < 0) || (signr >= NSIG)) { + pr_err("Wrong signal number!\n"); + break; + } + + // If its value is 0, it means that all pending signals have been + // handled and do_signal( ) can finish. + if (signr == 0) { + pr_notice("There are no more signals to handle.\n"); + __unlock_task_sighand(current); + return 0; + } + + // Get the associated signal action. + sigaction_t *ka = ¤t->sighand.action[signr - 1]; + + // The only exception comes when the receiving process is init, in + // which case the signal is discarded. + if (current->pid == 1) + continue; + + // When a delivered signal is explicitly ignored, the do_signal( ) + // function normally just continues with a new execution of the loop + // and therefore considers another pending signal. + if (ka->sa_handler == SIG_IGN) { + if (signr == SIGCHLD) + while (sys_waitpid(-1, NULL, WNOHANG) > 0) {} + continue; + } + + // When a delivered signal is the default one, do_signal( ) must + // perform the default action of the signal. + if (ka->sa_handler == SIG_DFL) { + // For other processes, since the default action depends on the + // type of signal, the function executes a switch statement based + // on the value of signr. + switch (signr) { + // The signals whose default action is "ignore" are easily handled: + case SIGCONT: + case SIGCHLD: + case SIGURG: + case SIGWINCH: + continue; + // The signals whose default action is "stop" may stop the + // current process. To do this, do_signal( ) sets the state + // of current to TASK_STOPPED and then invokes the schedule( ) + // function (see Section 11.2.2). + // The difference between SIGSTOP and the other signals is: + // SIGSTOP always stops the process; + // The other signals stop the process only if it is not + // in an "orphaned process group." + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + if (is_orphaned_pgrp(current->gid)) + continue; + + case SIGSTOP: + __unlock_task_sighand(current); + __do_signal_stop(current, f, signr); + __lock_task_sighand(current); + + continue; + case SIGQUIT: + case SIGILL: + case SIGTRAP: + + case SIGABRT: + sys_exit(3); + + continue; + case SIGFPE: + case SIGSEGV: + case SIGBUS: + case SIGSYS: + case SIGXCPU: + case SIGXFSZ: +#if 0 + if (do_coredump(signr, f)) + exit_code |= 0x80; +#endif + default: +#if 0 + current->flags |= PF_SIGNALED; +#endif + sys_exit(exit_code); + __unlock_task_sighand(current); + return 1; + } + } + if (__handle_signal(signr, &info, ka, f) == 1) { + __unlock_task_sighand(current); + return 1; + } + pr_emerg("Failed to handle signal.\n"); + } + __unlock_task_sighand(current); + return 0; +} + +int signals_init() +{ + if ((sigqueue_cachep = KMEM_CREATE(sigqueue_t)) == NULL) { + pr_emerg("Failed to allocate cache for signals.\n"); + return 0; + } + + list_head_init(&stopped_queue.task_list); + return 1; +} + +/// @brief Checks for some types of signals that might nullify other pending +/// signals for the destination thread group +/// @param sig Signal number +/// @param info siginfo struct of the signal +/// @param p Target process of the signal +void handle_stop_signal(int sig, siginfo_t *info, struct task_struct *p) +{ + // remove the SIGCONT signal from the shared + // pending signal queue p->signal->shared_pending and from the private + // queues of all members of the thread group. + if (sig == SIGSTOP || sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU) { + // TODO: shared and thread group + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGCONT); + + __rm_from_queue(&mask, &p->pending); + } + + // remove any SIGSTOP, SIGTSTP, SIGTTIN, and SIGTTOU signal from the shared pending signal queue p->signal->shared_pending; + // then, removes the same signals from the private pending signal queues of the processes belonging to the thread + // group, and awakens them + if (sig == SIGCONT) { + sigset_t mask; + sigemptyset(&mask); + + sigaddset(&mask, SIGSTOP); + sigaddset(&mask, SIGTSTP); + sigaddset(&mask, SIGTTIN); + sigaddset(&mask, SIGTTOU); + + __rm_from_queue(&mask, &p->pending); + + struct list_head *it, *tmp; + list_for_each_safe (it, tmp, &stopped_queue.task_list) { + struct wait_queue_entry_t *entry = list_entry(it, struct wait_queue_entry_t, task_list); + + // Select only the waiting entry for the timer task pid + task_struct *task = entry->task; + if (task->pid == p->pid) { + // Executed entry's wakeup test function + int res = entry->func(entry, 0, 0); + if (res == 1) { + // Removes entry from list and memory + remove_wait_queue(&stopped_queue, entry); + kfree(entry); + + pr_debug("Process (pid: %d) restored from stop\n", p->pid); + } + break; + } + } + } +} + +/// @brief Send siginfo to target process +/// @param sig Signal number +/// @param info siginfo struct of the signal to be sent +/// @param p Target process where the signal will be sent +/// @return Returns 0 if there is no error, otherwise returns an error code +int __send_sig_info(int sig, siginfo_t *info, struct task_struct *p) +{ + if (sig < 0 || sig > NSIG) + return -EINVAL; + + // If the signal is being sent by a User Mode process, + // it checks whether the operation is allowed. + if (info->si_code == SI_USER) { + // TODO + } + + // If the sig parameter has the value 0, + // it returns immediately without generating any signal + if (!sig) + return 0; + + __lock_task_sighand(p); + + // Checks for some types of signals that might nullify other pending + // signals for the destination thread group + handle_stop_signal(sig, info, p); + +#if 0 + // Checks whether the signal is non-real-time and another occurrence of the same + // signal is already pending in the shared pending signal queue of the thread group + if (sig < 32 && sigismember(&p->signal->shared_pending.signal,sig)) + return 0; +#endif + + __unlock_task_sighand(p); + __send_signal(sig, info, p); + return 0; +} + +int sys_kill(pid_t pid, int sig) +{ + pr_debug("sys_kill(%d, %d)\n", pid, sig); + struct task_struct *current = scheduler_get_running_process(pid); + // Check the task associated with the pid. + if (!current) + return -ESRCH; + // Check the signal that we want to send. + if ((sig < 0) || (sig >= NSIG)) + return -EINVAL; + siginfo_t info; + info.si_signo = sig; + info.si_code = SI_USER; + info.si_value.sival_int = 0; + info.si_errno = 0; + info.si_pid = current->pid; + info.si_uid = current->uid; + info.si_addr = NULL; + info.si_status = 0; + info.si_band = 0; + return __send_sig_info(sig, &info, current); +} + +sighandler_t sys_signal(int signum, sighandler_t handler) +{ + pr_notice("sys_signal(%d, %p)\n", signum, handler); + // Check the signal that we want to send. + if ((signum < 0) || (signum >= NSIG)) { + pr_err("sys_signal(%d, %p): Wrong signal number!\n", signum, handler); + return SIG_ERR; + } + // The do_signal() function is usually only invoked when the CPU is going + // to return in User Mode. + struct task_struct *current = scheduler_get_current_process(); + assert(current && "There is no running process."); + // Skip the `init` process, always. + if (current->pid == 1) { + pr_err("sys_signal(%d, %p): Cannot signal number!\n", signum, handler); + return SIG_ERR; + } + // Create a new signal action. + sigaction_t new_sigaction; + // Set the handler. + new_sigaction.sa_handler = handler; + // Set the handler. + new_sigaction.sa_flags = SA_RESETHAND | SA_NODEFER; + // Reset the set for the signal action. + sigemptyset(&new_sigaction.sa_mask); + // Lock the signal handling for the given task. + __lock_task_sighand(current); + // Get the old sigaction. + sigaction_t *old_sigaction = ¤t->sighand.action[signum - 1]; + pr_err("sys_signal(%d, %p): Signal action ptr %p\n", signum, handler, old_sigaction); + pr_err("sys_signal(%d, %p): Old signal handler %p\n", signum, handler, old_sigaction->sa_handler); + // Get the old handler (to return). + sighandler_t old_handler = current->sighand.action[signum - 1].sa_handler; + // Set the new action. + memcpy(old_sigaction, &new_sigaction, sizeof(sigaction_t)); + // Unlock the signal handling for the given task. + __unlock_task_sighand(current); + // Return the old sighandler. + return old_handler; +} + +int sys_sigaction(int signum, const sigaction_t *act, sigaction_t *oldact) +{ + pr_debug("sys_sigaction(%d, %p, %p)\n", signum, act, oldact); + // Check the signal that we want to send. + if ((signum < 0) || (signum >= NSIG)) { + pr_debug("sys_sigaction(%d, %p, %p): Wrong signal number!\n", signum, act, oldact); + return -EINVAL; + } + // The do_signal() function is usually only invoked when the CPU is going + // to return in User Mode. + struct task_struct *current = scheduler_get_current_process(); + assert(current && "There is no running process."); + // Skip the `init` process, always. + if (current->pid == 1) { + pr_debug("sys_sigaction(%d, %p, %p): Cannot set signal for init!\n", signum, act, oldact); + return -EINVAL; + } + // Lock the signal handling for the given task. + __lock_task_sighand(current); + // Get a pointer to the entry in the sighand.action array. + sigaction_t *current_sigaction = ¤t->sighand.action[signum - 1]; + pr_debug("sys_sigaction(%d, %p, %p): : Signal old action ptr %p\n", signum, act, oldact, current_sigaction); + // If requested, get the old sigaction. + if (oldact) { + memcpy(oldact, current_sigaction, sizeof(sigaction_t)); + } + // Set the new action. + memcpy(current_sigaction, act, sizeof(sigaction_t)); + // Unlock the signal handling for the given task. + __unlock_task_sighand(current); + // Return the old sighandler. + return 0; +} + +int sys_sigprocmask(int how, const sigset_t *set, sigset_t *oldset) +{ + pr_notice("sys_sigprocmask(%d, %p, %p)\n", how, set, oldset); + if (!set && !oldset) { + return -EFAULT; + } + if ((how < SIG_BLOCK) || (how > SIG_SETMASK)) { + return -EINVAL; + } + // The do_signal() function is usually only invoked when the CPU is going + // to return in User Mode. + struct task_struct *current = scheduler_get_current_process(); + assert(current && "There is no running process."); + // Skip the `init` process, always. + if (current->pid == 1) { + pr_notice("sys_sigprocmask(%d, %p, %p): Cannot set signal for init!\n", how, set, oldset); + return -EINVAL; + } + // If `oldset` is not, return the old set. + if (oldset) { + oldset->sig[0] = current->blocked.sig[0]; + oldset->sig[1] = current->blocked.sig[1]; + } + // Set the new signal mask. + if (set) { + if (how == SIG_BLOCK) { + // The set of blocked signals is the union of the current set + // and the set argument. + current->blocked.sig[0] |= set->sig[0]; + current->blocked.sig[1] |= set->sig[1]; + } else if (how == SIG_UNBLOCK) { + // The signals in set are removed from the current set of + // blocked signals. It is permissible to attempt to unblock + // a signal which is not blocked. + current->blocked.sig[0] &= ~(set->sig[0]); + current->blocked.sig[1] &= ~(set->sig[1]); + } else if (how == SIG_SETMASK) { + // The set of blocked signals is set to the argument set. + current->blocked.sig[0] = set->sig[0]; + current->blocked.sig[1] = set->sig[1]; + } + } + return 0; +} + +const char *strsignal(int sig) +{ + if ((sig >= SIGHUP) && (sig < NSIG)) + return sys_siglist[sig - 1]; + return NULL; +} + +int sigemptyset(sigset_t *set) +{ + if (set) { + set->sig[0] = 0; + return 0; + } + return -1; +} + +int sigfillset(sigset_t *set) +{ + if (set) { + set->sig[0] = ~0UL; + return 0; + } + return -1; +} + +int sigaddset(sigset_t *set, int signum) +{ + if (set && ((signum))) { + bit_set_assign(set->sig[(signum - 1) / 32], ((signum - 1) % 32)); + return 0; + } + return -1; +} + +int sigdelset(sigset_t *set, int signum) +{ + if (set) { + bit_clear_assign(set->sig[(signum - 1) / 32], ((signum - 1) % 32)); + return 0; + } + return -1; +} + +int sigismember(sigset_t *set, int signum) +{ + if (set) + return bit_check(set->sig[(signum - 1) / 32], (signum - 1) % 32); + return -1; +} diff --git a/mentos/src/system/syscall.c b/mentos/src/system/syscall.c index ad9939e..b8931df 100644 --- a/mentos/src/system/syscall.c +++ b/mentos/src/system/syscall.c @@ -1,99 +1,153 @@ /// MentOS, The Mentoring Operating system project /// @file syscall.c /// @brief System Call management functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. /// See LICENSE.md for details. -#include "syscall.h" -#include "sys.h" -#include "shm.h" -#include "isr.h" -#include "errno.h" -#include "video.h" -#include "fcntl.h" +#include "devices/fpu.h" +#include "mem/kheap.h" +#include "system/syscall.h" +#include "descriptor_tables/isr.h" +#include "sys/errno.h" #include "kernel.h" -#include "unistd.h" -#include "process.h" -#include "process.h" -#include "irqflags.h" -#include "scheduler.h" -#include "read_write.h" +#include "process/process.h" +#include "process/scheduler.h" +#include "sys/utsname.h" +#include "fs/ioctl.h" +#include "hardware/timer.h" -/// @brief The signature of a function call. +#include "ipc/msg.h" +#include "ipc/sem.h" +#include "ipc/shm.h" + +/// The signature of a function call. typedef int (*SystemCall)(); -/// @brief The signature used to call the system call. -typedef uint32_t (*SystemCallFun)(uint32_t, ...); - -/// @brief The list of function call. +/// The list of function call. SystemCall sys_call_table[SYSCALL_NUMBER]; -// Linux provides a "not implemented" system call, sys_ni_syscall(), which does -// nothing except return ENOSYS, the error corresponding to an invalid -// system call. This function is used to "plug the hole" in the rare event that -// a syscall is removed or otherwise made unavailable. -int sys_ni_syscall() +/// Last interupt stack frame +static pt_regs *current_interrupt_stack_frame; + +/// @brief A Not Implemented (NI) system-call. +/// @return Always returns -ENOSYS. +/// @details +/// Linux provides a "not implemented" system call, sys_ni_syscall(), which does +/// nothing except return ENOSYS, the error corresponding to an invalid +/// system call. This function is used to "plug the hole" in the rare event that +/// a syscall is removed or otherwise made unavailable. +static inline int sys_ni_syscall() { - return ENOSYS; + return -ENOSYS; } void syscall_init() { - // Initialize the list of system calls. - for (uint32_t it = 0; it < SYSCALL_NUMBER; ++it) { - sys_call_table[it] = sys_ni_syscall; - } + // Initialize the list of system calls. + for (uint32_t it = 0; it < SYSCALL_NUMBER; ++it) { + sys_call_table[it] = sys_ni_syscall; + } - sys_call_table[__NR_exit] = (SystemCall)sys_exit; - sys_call_table[__NR_read] = (SystemCall)sys_read; - sys_call_table[__NR_write] = (SystemCall)sys_write; - sys_call_table[__NR_open] = (SystemCall)sys_open; - sys_call_table[__NR_getpid] = (SystemCall)sys_getpid; - sys_call_table[__NR_getppid] = (SystemCall)sys_getppid; - sys_call_table[__NR_vfork] = (SystemCall)sys_vfork; - sys_call_table[__NR_execve] = (SystemCall)sys_execve; - sys_call_table[__NR_nice] = (SystemCall)sys_nice; - sys_call_table[__NR_reboot] = (SystemCall)sys_reboot; - sys_call_table[__NR_waitpid] = (SystemCall)sys_waitpid; - sys_call_table[__NR_chdir] = (SystemCall)sys_chdir; - sys_call_table[__NR_getcwd] = (SystemCall)sys_getcwd; - sys_call_table[__NR_waitpid] = (SystemCall)sys_waitpid; - sys_call_table[__NR_brk] = (SystemCall)umalloc; // TODO: sys_brk - sys_call_table[__NR_free] = (SystemCall)ufree; // TODO: sys_brk + sys_call_table[__NR_exit] = (SystemCall)sys_exit; + sys_call_table[__NR_read] = (SystemCall)sys_read; + sys_call_table[__NR_write] = (SystemCall)sys_write; + sys_call_table[__NR_open] = (SystemCall)sys_open; + sys_call_table[__NR_close] = (SystemCall)sys_close; + sys_call_table[__NR_stat] = (SystemCall)sys_stat; + sys_call_table[__NR_fstat] = (SystemCall)sys_fstat; + sys_call_table[__NR_mkdir] = (SystemCall)sys_mkdir; + sys_call_table[__NR_rmdir] = (SystemCall)sys_rmdir; + sys_call_table[__NR_unlink] = (SystemCall)sys_unlink; + sys_call_table[__NR_getdents] = (SystemCall)sys_getdents; + sys_call_table[__NR_lseek] = (SystemCall)sys_lseek; + sys_call_table[__NR_getpid] = (SystemCall)sys_getpid; + sys_call_table[__NR_getsid] = (SystemCall)sys_getsid; + sys_call_table[__NR_setsid] = (SystemCall)sys_setsid; + sys_call_table[__NR_getgid] =(SystemCall)sys_getgid; + sys_call_table[__NR_setgid] =(SystemCall)sys_setgid; + sys_call_table[__NR_getppid] = (SystemCall)sys_getppid; + sys_call_table[__NR_sigaction] = (SystemCall)sys_sigaction; + sys_call_table[__NR_fork] = (SystemCall)sys_fork; + sys_call_table[__NR_execve] = (SystemCall)sys_execve; + sys_call_table[__NR_nice] = (SystemCall)sys_nice; + sys_call_table[__NR_kill] = (SystemCall)sys_kill; + sys_call_table[__NR_reboot] = (SystemCall)sys_reboot; + sys_call_table[__NR_uname] = (SystemCall)sys_uname; + sys_call_table[__NR_sigreturn] = (SystemCall)sys_sigreturn; + sys_call_table[__NR_waitpid] = (SystemCall)sys_waitpid; + sys_call_table[__NR_chdir] = (SystemCall)sys_chdir; + sys_call_table[__NR_fchdir] = (SystemCall)sys_fchdir; + sys_call_table[__NR_time] = (SystemCall)sys_time; + sys_call_table[__NR_sigprocmask] = (SystemCall)sys_sigprocmask; + sys_call_table[__NR_brk] = (SystemCall)sys_brk; + sys_call_table[__NR_signal] = (SystemCall)sys_signal; + sys_call_table[__NR_ioctl] = (SystemCall)sys_ioctl; + sys_call_table[__NR_sched_setparam] = (SystemCall)sys_sched_setparam; + sys_call_table[__NR_sched_getparam] = (SystemCall)sys_sched_getparam; + sys_call_table[__NR_nanosleep] = (SystemCall)sys_nanosleep; + sys_call_table[__NR_getcwd] = (SystemCall)sys_getcwd; + sys_call_table[__NR_waitperiod] = (SystemCall)sys_waitperiod; + sys_call_table[__NR_msgctl] = (SystemCall)sys_msgctl; + sys_call_table[__NR_msgget] = (SystemCall)sys_msgget; + sys_call_table[__NR_msgrcv] = (SystemCall)sys_msgrcv; + sys_call_table[__NR_msgsnd] = (SystemCall)sys_msgsnd; + sys_call_table[__NR_semctl] = (SystemCall)sys_semctl; + sys_call_table[__NR_semget] = (SystemCall)sys_semget; + sys_call_table[__NR_semop] = (SystemCall)sys_semop; + sys_call_table[__NR_shmat] = (SystemCall)sys_shmat; + sys_call_table[__NR_shmctl] = (SystemCall)sys_shmctl; + sys_call_table[__NR_shmdt] = (SystemCall)sys_shmdt; + sys_call_table[__NR_shmget] = (SystemCall)sys_shmget; + sys_call_table[__NR_alarm] = (SystemCall)sys_alarm; + sys_call_table[__NR_setitimer] = (SystemCall)sys_setitimer; + sys_call_table[__NR_getitimer] = (SystemCall)sys_getitimer; - isr_install_handler(SYSTEM_CALL, &syscall_handler, "syscall_handler"); + isr_install_handler(SYSTEM_CALL, &syscall_handler, "syscall_handler"); +} + +pt_regs *get_current_interrupt_stack_frame() +{ + return current_interrupt_stack_frame; } void syscall_handler(pt_regs *f) { - // print_intrframe(f); + // Saves current interrupt stack frame + current_interrupt_stack_frame = f; - // The index of the requested system call. - uint32_t sc_index = f->eax; + // dbg_print_regs(f); + // Save current process fpu state. + switch_fpu(); - // The result of the system call. - int ret; - if (sc_index >= SYSCALL_NUMBER) { - ret = ENOSYS; - } else { - uintptr_t ptr = (uintptr_t)sys_call_table[sc_index]; + // The index of the requested system call. + uint32_t sc_index = f->eax; - SystemCallFun func = (SystemCallFun)ptr; + // The result of the system call. + int ret; + if (sc_index >= SYSCALL_NUMBER) { + ret = ENOSYS; + } else { + uintptr_t ptr = (uintptr_t)sys_call_table[sc_index]; - uint32_t arg0 = f->ebx; - uint32_t arg1 = f->ecx; - uint32_t arg2 = f->edx; - uint32_t arg3 = f->esi; - uint32_t arg4 = f->edi; - if ((sc_index == __NR_vfork) || (sc_index == __NR_clone)) { - arg0 = (uintptr_t)f; - } else if (sc_index == __NR_execve) { - arg0 = (uintptr_t)f; - } - ret = func(arg0, arg1, arg2, arg3, arg4); - } - f->eax = ret; + SystemCall func = (SystemCall)ptr; - // Schedule next process. - kernel_schedule(f); + uint32_t arg0 = f->ebx; + uint32_t arg1 = f->ecx; + uint32_t arg2 = f->edx; + uint32_t arg3 = f->esi; + uint32_t arg4 = f->edi; + if ((sc_index == __NR_fork) || + (sc_index == __NR_clone) || + (sc_index == __NR_execve) || + (sc_index == __NR_sigreturn)) { + arg0 = (uintptr_t)f; + } + ret = func(arg0, arg1, arg2, arg3, arg4); + } + f->eax = ret; + + // Schedule next process. + scheduler_run(f); + // Restore fpu state. + unswitch_fpu(); } diff --git a/mentos/src/ui/command/cmd_cd.c b/mentos/src/ui/command/cmd_cd.c deleted file mode 100644 index bfb4cab..0000000 --- a/mentos/src/ui/command/cmd_cd.c +++ /dev/null @@ -1,71 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_cd.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include -#include -#include "commands.h" -#include "vfs.h" -#include "stdio.h" -#include "dirent.h" -#include "string.h" -#include "libgen.h" - -void cmd_cd(int argc, char **argv) -{ - DIR *dirp = NULL; - char path[MAX_PATH_LENGTH]; - memset(path, 0, MAX_PATH_LENGTH); - - char current_path[MAX_PATH_LENGTH]; - getcwd(current_path, MAX_PATH_LENGTH); - - if (argc <= 1) - { - strcpy(path, "/"); - } - else if (argc > 2) - { - printf("%s: too many arguments\n\n", argv[0]); - - return; - } - else if (strncmp(argv[1], "..", 2) == 0) - { - if (strcmp(current_path, dirname(current_path)) == 0) - { - return; - } - strcpy(path, dirname(current_path)); - } - else if (strncmp(argv[1], ".", 1) == 0) - { - return; - } - else - { - // Copy the current path. - strcpy(path, current_path); - // Get the absolute path. - get_absolute_path(path); - // If the current directory is not the root, add a '/'. - if (strcmp(path, "/") != 0) - { - strncat(path, "/", 1); - } - // Concatenate the input dir. - strncat(path, argv[1], strlen(argv[1])); - } - - dirp = opendir(path); - if (dirp == NULL) - { - printf("%s: no such file or directory: %s\n\n", argv[0], argv[1]); - - return; - } - chdir(path); - closedir(dirp); -} diff --git a/mentos/src/ui/command/cmd_clear.c b/mentos/src/ui/command/cmd_clear.c deleted file mode 100644 index fa2060b..0000000 --- a/mentos/src/ui/command/cmd_clear.c +++ /dev/null @@ -1,15 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_clear.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "video.h" - -void cmd_clear(int argc, char **argv) -{ - (void) argc; - (void) argv; - video_clear(); -} diff --git a/mentos/src/ui/command/cmd_cpuid.c b/mentos/src/ui/command/cmd_cpuid.c deleted file mode 100644 index fc9ab06..0000000 --- a/mentos/src/ui/command/cmd_cpuid.c +++ /dev/null @@ -1,118 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_cpuid.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" -#include "string.h" -#include "cmd_cpuid.h" - -void cmd_cpuid(int argc, char **argv) -{ - (void)argc; - (void)argv; - - // List of features. - const char *ecx_features[ECX_FLAGS_SIZE] = { - "SSE3", - "Reserved", - "Reserved", - "Monitor/MWAIT", - "CPL Debug Store", - "Virtual Machine", - "Safer Mode", - "Enhanced Intel SpeedStep Technology", - "Thermal Monitor 2", - "SSSE3", - "L1 Context ID", - "Reserved", - "Reserved", - "CMPXCHG16B", - "xTPR Update Control", - "Perfmon and Debug Capability", - "Reserved", - "Reserved", - "DCA", - "SSE4.1", - "SSE4.2", - "Reserved", - "Reserved", - "POPCNT" - }; - - const char *edx_features[EDX_FLAGS_SIZE] = { - "x87 FPU", - "Virtual 8086 Mode", - "Debugging Extensions", - "Page Size Extensions", - "Time Stamp Counter", - "RDMSR and WRMSR", - "Physical Address Extensions", - "Machine Check Exception", - "CMPXCHG8B", - "APIC On-chip", - "Reserved", - "SYSENTER and SYSEXIT", - "Memory Type Range Registers", - "PTE Global Bit", - "Machine Check Architecture", - "Conditional Move Instructions", - "Page Attribute Table", - "36-bit Page Size", - "Processor Serial Number", - "Reserved", - "Debug Store", - "Thermal Monitor and Clock Facilities", - "Intel MMX", - "FXSAVE and FXRSTOR", - "SSE", - "SSE2", - "Self Snoop", - "Multi-Threading", - "TTC", - "Reserved", - "Pending Break Enable" - }; - - int i; - int verbose = 0; - - // Examine possible options. - if (argv[1] != NULL) { - if (strcmp(argv[1], "-v") == 0) { - verbose = 1; - } else { - printf("Unknown option %s\n", argv[1]); - printf("CPUID help message\n" - "-v : shows verbose CPUID information\n"); - - return; - } - } - - printf("----- CPU ID Information -----\n"); - if (strcmp(sinfo.brand_string, "Reserved") != 0) { - printf("%s\n", sinfo.brand_string); - } - printf("Vendor: %s\n", sinfo.cpu_vendor); - printf("Type: %s, Family: %x, Model: %x\n", sinfo.cpu_type, - sinfo.cpu_family, sinfo.cpu_model); - printf("Apic ID: %d\n", sinfo.apic_id); - - if (verbose == 1) { - printf("\n--- Supported features ---\n"); - for (i = 0; i < ECX_FLAGS_SIZE; i++) { - if (sinfo.cpuid_ecx_flags[i] == 1) { - printf("%s\n", ecx_features[i]); - } - } - for (i = 0; i < EDX_FLAGS_SIZE; i++) { - if (sinfo.cpuid_edx_flags[i] == 1) { - printf("%s\n", edx_features[i]); - } - } - printf("---------------------------\n"); - } -} diff --git a/mentos/src/ui/command/cmd_credits.c b/mentos/src/ui/command/cmd_credits.c deleted file mode 100644 index b6d7cd8..0000000 --- a/mentos/src/ui/command/cmd_credits.c +++ /dev/null @@ -1,28 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file credits.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "video.h" -#include "stdio.h" -#include "version.h" - -void cmd_credits(int argc, char **argv) -{ - (void) argc; - (void) argv; - video_set_color(BRIGHT_BLUE); - printf(OS_NAME" Credits\n\n"); - printf("Main Developers:\n"); - video_set_color(GREEN); - printf("Enrico Fraccaroli (Galfurian)\n"); - printf("Alessando Danese\n"); - video_set_color(BRIGHT_BLUE); - printf("Developers:\n"); - video_set_color(GREEN); - printf("Luigi C.\n" - "Mirco D.\n\n"); - video_set_color(WHITE); -} diff --git a/mentos/src/ui/command/cmd_date.c b/mentos/src/ui/command/cmd_date.c deleted file mode 100644 index 3c96565..0000000 --- a/mentos/src/ui/command/cmd_date.c +++ /dev/null @@ -1,19 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_date.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" -#include "clock.h" -#include "irqflags.h" - -void cmd_date(int argc, char **argv) -{ - (void)argc; - (void)argv; - printf("It's %x:%x:%x of %s %02x %s %02x\n", get_hour(), - get_minute(), get_second(), get_day_lng(), - get_day_m(), get_month_lng(), 0x2000 + get_year()); -} diff --git a/mentos/src/ui/command/cmd_drv_load.c b/mentos/src/ui/command/cmd_drv_load.c deleted file mode 100644 index 549c681..0000000 --- a/mentos/src/ui/command/cmd_drv_load.c +++ /dev/null @@ -1,69 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_drv_load.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "mouse.h" -#include "string.h" -#include "stdio.h" - -void cmd_drv_load(int argc, char **argv) -{ - if (argc < 2) - { - printf( - "No driver inserted or bad usage! Type %s --help for the usage.\n", - argv[0]); - } - else - { - if ((_kstrncmp(argv[1], "-r", 2) == 0)) - { - if ((argv[2] != NULL)) - { - if (_kstrncmp(argv[2], "mouse", 5) == 0) - { - printf("Disattivamento %s in corso..\n", argv[2]); - mouse_disable(); - } - else - printf("FATAL: Driver %s not found.\n", argv[2]); - } - else - printf("Warning, no driver name inserted!\n"); - } - else if (_kstrncmp(argv[1], "mouse", 5) == 0) - { - // Enabling mouse. - mouse_install(); - } - else if ((_kstrncmp(argv[1], "--help", 6) == 0) || - (_kstrncmp(argv[1], "-h", 2) == 0)) - { - printf("---------------------------------------------------\n" - "Driver tool to load and kill driver\n" - "Simple to use, just type:\n" - "\n" - "Usage: %s - driver_name\n" - "\t-> %s module_name - to load driver\n" - "\t-> %s -r module_name - to kill driver\n" - "---------------------------------------------------\n", - argv[0], argv[0], argv[0]); - } - else - { - if ((_kstrncmp(argv[1], "-r", 2) == 0) && - (_kstrncmp(argv[2], "mouse", 5) == -1)) - { - printf("FATAL: Driver %s not found.\n", argv[2]); - } - - else - { - printf("FATAL: Driver %s not found.\n", argv[1]); - } - } - } -} diff --git a/mentos/src/ui/command/cmd_echo.c b/mentos/src/ui/command/cmd_echo.c deleted file mode 100644 index 69d8d5c..0000000 --- a/mentos/src/ui/command/cmd_echo.c +++ /dev/null @@ -1,26 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file echo.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" - -void cmd_echo(int argc, char **argv) -{ - int i = argc; - int j = 0; - if (argc == 1) - { - printf(""); - } - else - { - while (--i > 0) - { - printf("%s ", argv[++j]); - } - } - printf("\n"); -} diff --git a/mentos/src/ui/command/cmd_ipcrm.c b/mentos/src/ui/command/cmd_ipcrm.c deleted file mode 100644 index 2208de9..0000000 --- a/mentos/src/ui/command/cmd_ipcrm.c +++ /dev/null @@ -1,58 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_ipcrm.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "stdio.h" -#include "shm.h" -#include "string.h" - -extern struct shmid_ds *head; - -void cmd_ipcrm(int argc, char **argv){ - if (argc != 2) - { - printf("Bad arguments: you have to specify only IPC id, see ipcs.\n"); - - return; - } - - struct shmid_ds *shmid_ds = head; - struct shmid_ds *prev = NULL; - - while (shmid_ds != NULL) - { - char strid[10]; - int_to_str(strid, (shmid_ds ->shm_perm).seq, 10); - - if (strcmp(strid, argv[1]) == 0) - { - break; - } - - prev = shmid_ds; - shmid_ds = shmid_ds -> next; - } - - if (shmid_ds == NULL) - { - printf("No shared memory find. \n"); - } - else - { - kfree(shmid_ds -> shm_location); - - // shmid_ds = head. - if (prev == NULL) - { - head = head -> next; - } - else - { - prev -> next = shmid_ds -> next; - } - - kfree(shmid_ds); - } -} diff --git a/mentos/src/ui/command/cmd_ipcs.c b/mentos/src/ui/command/cmd_ipcs.c deleted file mode 100644 index f8fab60..0000000 --- a/mentos/src/ui/command/cmd_ipcs.c +++ /dev/null @@ -1,98 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_ipcs.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "shm.h" -#include "stdio.h" -#include "string.h" -#include "version.h" - -extern struct shmid_ds *head; - -static void print_sem_stat() -{ - printf("Semaphores: \n"); - printf("%-10s %-10s %-10s %-10s %-10s %-10s \n", "T", "ID", "KEY", "MODE", - "OWNER", "GROUP"); - printf("%-20s %-10s %-20s \n\n", "", "Semaphores not implemented", ""); -} - -static void print_shm_stat() -{ - struct shmid_ds *shm_list = head; - - printf("Shared Memory: \n"); - printf("%-10s %-10s %-10s %-10s %-10s %-10s \n", "T", "ID", "KEY", "MODE", - "OWNER", "GROUP"); - - while (shm_list != NULL) - { - char mode[12]; - strmode((shm_list->shm_perm).mode, mode); - - printf("%-10s %-10i %-10i %-10s %-10s %-10s \n", - "m", - (shm_list->shm_perm).seq, - (shm_list->shm_perm).key, - mode, - "-", "-"); - - shm_list = shm_list->next; - } - - printf("\n"); -} - -static void print_msg_stat() -{ - printf("Message Queues: \n"); - printf("%-10s %-10s %-10s %-10s %-10s %-10s \n", "T", "ID", "KEY", "MODE", - "OWNER", "GROUP"); - printf("%-20s %-10s %-20s \n\n", "", "Message Queues not implemented", ""); -} - -void cmd_ipcs(int argc, char **argv) -{ - if (argc > 2) - { - printf("Too much arguments.\n"); - - return; - } - - char datehour[100] = ""; - strdatehour(datehour); - - printf("IPC status from "OS_NAME" as of %s\n", datehour); - - if (argc == 2) - { - if (strcmp(argv[1], "-s") == 0) - { - print_sem_stat(); - } - else if (strcmp(argv[1], "-m") == 0) - { - print_shm_stat(); - } - else if (strcmp(argv[1], "-q") == 0) - { - print_msg_stat(); - } - else - { - printf("Option not recognize.\n"); - } - } - else - { - print_sem_stat(); - print_shm_stat(); - print_msg_stat(); - } - - return; -} diff --git a/mentos/src/ui/command/cmd_logo.c b/mentos/src/ui/command/cmd_logo.c deleted file mode 100644 index a1eb2e5..0000000 --- a/mentos/src/ui/command/cmd_logo.c +++ /dev/null @@ -1,38 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_logo.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "video.h" -#include "stdio.h" -#include "version.h" - -#define LOGO_TAB " " -void cmd_logo(int argc, char **argv) -{ - (void)argc; - (void)argv; - video_set_color(BRIGHT_GREEN); - printf(LOGO_TAB " __ __ _ ___ ____ \n"); - printf(LOGO_TAB "| \\/ | ___ _ __ | |_ / _ \\ / ___| \n"); - printf(LOGO_TAB "| |\\/| | / _ \\ | '_ \\ | __| | | | | \\___ \\ \n"); - printf(LOGO_TAB "| | | | | __/ | | | | | |_ | |_| | ___) |\n"); - printf(LOGO_TAB "|_| |_| \\___| |_| |_| \\__| \\___/ |____/ \n"); - video_set_color(BRIGHT_BLUE); - printf("\n"); - printf(LOGO_TAB " Welcome to "); - video_set_color(WHITE); - printf(OS_NAME "\n"); - video_set_color(BRIGHT_BLUE); - printf(LOGO_TAB " The "); - video_set_color(WHITE); - printf("Mentoring Operating System"); - video_set_color(BRIGHT_BLUE); - printf(" ver. "); - video_set_color(WHITE); - printf(OS_VERSION "\n"); - printf("\n"); - video_set_color(WHITE); -} diff --git a/mentos/src/ui/command/cmd_ls.c b/mentos/src/ui/command/cmd_ls.c deleted file mode 100644 index 8747243..0000000 --- a/mentos/src/ui/command/cmd_ls.c +++ /dev/null @@ -1,104 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file ls.c -/// @brief Command 'ls'. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include -#include -#include "commands.h" -#include "vfs.h" -#include "stdio.h" -#include "video.h" -#include "dirent.h" -#include "string.h" -#include "bitops.h" -#include "libgen.h" -#include "strerror.h" - -#define FLAG_L 1 - -static void print_ls(DIR *dirp, uint32_t flags) -{ - // If the directory is open. - if (dirp == NULL) { - return; - } - - size_t total_size = 0; - dirent_t *dirent = readdir(dirp); - while (dirent != NULL) { - if (dirent->d_type == FS_DIRECTORY) { - video_set_color(BRIGHT_CYAN); - } - if (dirent->d_type == FS_MOUNTPOINT) { - video_set_color(BRIGHT_GREEN); - } - if (has_flag(flags, FLAG_L)) { - stat_t entry_stat; - if (stat(dirent->d_name, &entry_stat) != -1) { - printf("%d %3d %3d %8d %s\n", dirent->d_type, entry_stat.st_uid, - entry_stat.st_gid, entry_stat.st_size, - basename(dirent->d_name)); - total_size += entry_stat.st_size; - } - } else { - printf("%s ", basename(dirent->d_name)); - } - video_set_color(WHITE); - dirent = readdir(dirp); - } - - closedir(dirp); - printf("\n"); - if (has_flag(flags, FLAG_L)) { - printf("Total: %d byte\n", total_size); - } - printf("\n"); -} - -void cmd_ls(int argc, char **argv) -{ - // Create a variable to store flags. - uint32_t flags = 0; - // Check the number of arguments. - for (int i = 1; i < argc; ++i) { - if (strcmp(argv[i], "--help") == 0) { - printf("List information about files inside a given directory.\n"); - printf("Usage:\n"); - printf(" ls [options] [directory]\n\n"); - - return; - } else if (strcmp(argv[i], "-l") == 0) { - set_flag(&flags, FLAG_L); - } - } - - bool_t no_directory = true; - for (int i = 1; i < argc; ++i) { - if (strcmp(argv[i], "-l") == 0) { - continue; - } - - no_directory = false; - DIR *dirp = opendir(argv[i]); - if (dirp == NULL) { - printf("%s: cannot access '%s': %s\n\n", argv[0], argv[i], - "unknown" /*strerror(errno)*/); - - continue; - } - printf("%s:\n", argv[i]); - print_ls(dirp, flags); - } - if (no_directory) { - char cwd[MAX_PATH_LENGTH]; - getcwd(cwd, MAX_PATH_LENGTH); - DIR *dirp = opendir(cwd); - if (dirp == NULL) { - printf("%s: cannot access '%s': %s\n\n", argv[0], cwd, "unknown"); - } else { - print_ls(dirp, flags); - } - } -} diff --git a/mentos/src/ui/command/cmd_mkdir.c b/mentos/src/ui/command/cmd_mkdir.c deleted file mode 100644 index f1fca69..0000000 --- a/mentos/src/ui/command/cmd_mkdir.c +++ /dev/null @@ -1,29 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_mkdir.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stat.h" -#include "stdio.h" -#include "string.h" - -void cmd_mkdir(int argc, char **argv) -{ - // Check the number of arguments. - if (argc != 2) { - printf("%s: missing operand.\n", argv[0]); - printf("Try '%s --help' for more information.\n\n", argv[0]); - - return; - } - if (strcmp(argv[1], "--help") == 0) { - printf("Creates a new directory.\n"); - printf("Usage:\n"); - printf(" %s \n", argv[0]); - - return; - } - mkdir(argv[1], 0); -} diff --git a/mentos/src/ui/command/cmd_more.c b/mentos/src/ui/command/cmd_more.c deleted file mode 100644 index 7dd0ddc..0000000 --- a/mentos/src/ui/command/cmd_more.c +++ /dev/null @@ -1,51 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_more.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "fcntl.h" -#include "stdio.h" -#include "string.h" -#include "unistd.h" -#include "strerror.h" - -void cmd_more(int argc, char **argv) -{ - if (argc != 2) - { - printf("%s: missing operand.\n", argv[0]); - printf("Try '%s --help' for more information.\n\n", argv[0]); - - return; - } - - if (strcmp(argv[1], "--help") == 0) - { - printf("Prints the content of the given file.\n"); - printf("Usage:\n"); - printf(" %s \n\n", argv[0]); - - return; - } - - int fd = open(argv[1], O_RDONLY, 42); - if (fd < 0) - { - printf("%s: Cannot stat file '%s': %s\n\n", - argv[0], argv[1],"unknown"/*strerror(errno)*/); - - return; - } - - char c; - // Put on the standard output the characters. - while (read(fd, &c, 1) > 0) - { - putchar(c); - } - putchar('\n'); - putchar('\n'); - close(fd); -} diff --git a/mentos/src/ui/command/cmd_newfile.c b/mentos/src/ui/command/cmd_newfile.c deleted file mode 100644 index 8cdc39c..0000000 --- a/mentos/src/ui/command/cmd_newfile.c +++ /dev/null @@ -1,62 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_newfile.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" -#include "fcntl.h" -#include "string.h" -#include "unistd.h" -#include "strerror.h" -#include - -void cmd_newfile(int argc, char **argv) -{ - if (argc != 2) - { - printf("%s: missing operand.\n", argv[0]); - printf("Try '%s --help' for more information.\n\n", argv[0]); - - return; - } - - if (strcmp(argv[1], "--help") == 0) - { - printf("Makes a new file, and prompt for it's content.\n"); - printf("Usage:\n"); - printf(" %s \n", argv[0]); - - return; - } - - char text[256]; - printf("Filename: %s\n", argv[1]); - int fd = open(argv[1], O_RDWR | O_CREAT | O_APPEND, 0); - if (fd < 0) - { - printf("%s: Cannot create file '%s': %s\n\n", - argv[0], argv[1], "unknown"/*strerror(errno)*/); - - return; - } - - printf("Type one line of text here (new line to complete):\n"); - scanf("%s", text); - if (write(fd, text, strlen(text)) == -1) - { - printf("%s: Cannot write on file '%s': %s\n\n", - argv[0], argv[1], "unknown"/*strerror(errno)*/); - - return; - } - - if (close(fd) == -1) - { - printf("%s: Cannot close file '%s': %s\n\n", - argv[0], argv[1], "unknown"/*strerror(errno)*/); - - return; - } -} diff --git a/mentos/src/ui/command/cmd_nice.c b/mentos/src/ui/command/cmd_nice.c deleted file mode 100644 index 497d366..0000000 --- a/mentos/src/ui/command/cmd_nice.c +++ /dev/null @@ -1,43 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_nice.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" -#include "unistd.h" -#include "string.h" - -void cmd_nice(int argc, char **argv) -{ - if (argc == 1) { - int current = nice(0); - printf("%d \n\n", current); - - return; - } - - if (argc != 2) { - printf("%s: missing operand.\n", argv[0]); - printf("Try '%s --help' for more information.\n\n", argv[0]); - - return; - } - - if (!strcmp(argv[1], "--help")) { - printf("Usage: %s \n\n", argv[0]); - - return; - } - - int increment = atoi(argv[1]); - if ((increment < -40) || (increment > 40)) { - printf("Error: You must provide a value between (-40,+40). \n\n", - increment); - - return; - } - int newNice = nice(increment); - printf("Your new nice value is %d.\n\n", newNice); -} diff --git a/mentos/src/ui/command/cmd_poweroff.c b/mentos/src/ui/command/cmd_poweroff.c deleted file mode 100644 index 4c9ea1d..0000000 --- a/mentos/src/ui/command/cmd_poweroff.c +++ /dev/null @@ -1,21 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file poweroff.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" -#include "reboot.h" -#include "unistd.h" - -void cmd_poweroff(int argc, char **argv) -{ - (void) argc; - (void) argv; - printf("Executing power-off...\n"); - reboot(LINUX_REBOOT_MAGIC1, - LINUX_REBOOT_MAGIC2, - LINUX_REBOOT_CMD_POWER_OFF, - NULL); -} diff --git a/mentos/src/ui/command/cmd_ps.c b/mentos/src/ui/command/cmd_ps.c deleted file mode 100644 index fd59ba8..0000000 --- a/mentos/src/ui/command/cmd_ps.c +++ /dev/null @@ -1,54 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_ps.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "list.h" -#include "stdio.h" -#include "string.h" -#include "bitops.h" -#include "scheduler.h" - -#define PS_OPT_F (1 << 1) - -void cmd_ps(int argc, char **argv) -{ - // Flag variable. - uint32_t flags = 0; - - // Check arguments. - for (int i = 0; i < argc; ++i) { - size_t optlen = strlen(argv[i]); - if (optlen == 0) { - continue; - } - - if (argv[i][0] != '-') { - continue; - } - - for (size_t j = 1; j < optlen; ++j) { - if (argv[i][j] == 'f') { - set_flag(&flags, PS_OPT_F); - } - } - } - - if (has_flag(flags, PS_OPT_F)) { - // Print the header. - printf("%-6s", "PID"); - printf("%-6s", "STATE"); - printf("%-50s", "COMMAND"); - printf("\n"); - // print_tree(task_structree->root, 0); - } else { - // Print the header. - printf("%-6s", "PID"); - printf("%-6s", "STATE"); - printf("%-50s", "COMMAND"); - printf("\n"); - } - printf("\n"); -} diff --git a/mentos/src/ui/command/cmd_pwd.c b/mentos/src/ui/command/cmd_pwd.c deleted file mode 100644 index e4f123f..0000000 --- a/mentos/src/ui/command/cmd_pwd.c +++ /dev/null @@ -1,19 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_pwd.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include -#include -#include "commands.h" -#include "stdio.h" - -void cmd_pwd(int argc, char **argv) -{ - (void) argc; - (void) argv; - char cwd[MAX_PATH_LENGTH]; - getcwd(cwd, MAX_PATH_LENGTH); - printf("%s\n", cwd); -} diff --git a/mentos/src/ui/command/cmd_rm.c b/mentos/src/ui/command/cmd_rm.c deleted file mode 100644 index 4e80b27..0000000 --- a/mentos/src/ui/command/cmd_rm.c +++ /dev/null @@ -1,51 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_rm.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "fcntl.h" -#include "stdio.h" -#include "string.h" -#include "unistd.h" -#include "strerror.h" - -void cmd_rm(int argc, char **argv) -{ - if (argc != 2) - { - printf("%s: missing operand.\n", argv[0]); - printf("Try '%s --help' for more information.\n\n", argv[0]); - - return; - } - - if (strcmp(argv[1], "--help") == 0) - { - printf("Remove (unlink) the FILE(s).\n"); - printf("Usage:\n"); - printf(" rm \n"); - - return; - } - - int fd = open(argv[1], O_RDONLY, 0); - if (fd < 0) - { - printf("%s: cannot remove '%s': %s\n\n", - argv[0], argv[1], "unknown"/*strerror(errno)*/); - - return; - } - - close(fd); - if (remove(argv[1]) != 0) - { - printf("rm: cannot remove '%s': Failed to remove file\n\n", - argv[1]); - - return; - } - printf("\n"); -} diff --git a/mentos/src/ui/command/cmd_rmdir.c b/mentos/src/ui/command/cmd_rmdir.c deleted file mode 100644 index 6c703a0..0000000 --- a/mentos/src/ui/command/cmd_rmdir.c +++ /dev/null @@ -1,40 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_rmdir.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" -#include "string.h" -#include "unistd.h" -#include "strerror.h" - -void cmd_rmdir(int argc, char **argv) -{ - // Check the number of arguments. - if (argc != 2) - { - printf("Bad usage.\n"); - printf("Try 'rmdir --help' for more information.\n"); - - return; - } - - if (strcmp(argv[1], "--help") == 0) - { - printf("Removes a directory.\n"); - printf("Usage:\n"); - printf(" rmdir \n"); - - return; - } - - if (rmdir(argv[1]) != 0) - { - printf("%s: failed to remove '%s': %s\n\n", - argv[0], argv[1], "unknown"/*strerror(errno)*/); - - return; - } -} diff --git a/mentos/src/ui/command/cmd_showpid.c b/mentos/src/ui/command/cmd_showpid.c deleted file mode 100644 index 30e2d87..0000000 --- a/mentos/src/ui/command/cmd_showpid.c +++ /dev/null @@ -1,15 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_showpid.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" -#include "unistd.h" -#include "scheduler.h" - -void cmd_showpid(int argc, char **argv) -{ - printf("pid %d\n", getpid()); -} diff --git a/mentos/src/ui/command/cmd_sleep.c b/mentos/src/ui/command/cmd_sleep.c deleted file mode 100644 index b588807..0000000 --- a/mentos/src/ui/command/cmd_sleep.c +++ /dev/null @@ -1,43 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file sleep.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "string.h" -#include "stdio.h" -#include "timer.h" -#include "clock.h" - -void cmd_sleep(int argc, char ** argv) -{ - if (argc != 2) - { - printf("%s: missing operand.\n", argv[0]); - printf("Try '%s --help' for more information.\n\n", argv[0]); - - return; - } - - if (!strcmp(argv[1], "--help")) - { - printf("Usage: %s \n\n", argv[0]); - - return; - } - - int seconds = atoi(argv[1]); - if (seconds <= 0) - { - printf("Error: You must provide a positive value (%d). \n\n", seconds); - - return; - } - - time_t t0 = get_hour() * 60 * 60 + get_minute() * 60 + get_second(); - printf("Start sleeping at '%d' for %ds...\n", t0, seconds); - sleep((time_t) seconds); - time_t t1 = get_hour() * 60 * 60 + get_minute() * 60 + get_second(); - printf("End sleeping at '%d' after %ds.\n", t1, t1 - t0); -} diff --git a/mentos/src/ui/command/cmd_tester.c b/mentos/src/ui/command/cmd_tester.c deleted file mode 100644 index 2921455..0000000 --- a/mentos/src/ui/command/cmd_tester.c +++ /dev/null @@ -1,353 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file testing.c -/// @brief Commands used to test OS functionalities. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "shm.h" -#include "stdio.h" -#include "timer.h" -#include "debug.h" -#include "shell.h" -#include "video.h" -#include "assert.h" -#include "stdlib.h" -#include "unistd.h" -#include "string.h" -#include "spinlock.h" - -//Function used to test vfork. -int task_test_function(int argc, char *argv[]) -{ - (void)argc; - (void)argv; - printf("Hey there, here is %s with pid %d!\n", argv[0], getpid()); - sleep(12); - printf("Here is %s with pid %d, I'm leaving.\n", argv[0], getpid()); - - return 0; -} - -void try_process(int argc, char **argv) -{ - printf("I'm %d, testing task creation functions...\n", getpid()); - pid_t cpid = vfork(); - - if (cpid == 0) { - char *_cmd[] = { "task_test_function", (char *)NULL }; - char *_env[] = { (char *)NULL }; - execve((const char *)task_test_function, _cmd, _env); - assert(false && "I should not be here."); - } - printf("Executed process with pid %d...\n", cpid); -} - -void try_stress_heap(int argc, char **argv) -{ - uint32_t max_element = 1000; - if (argc >= 1) { - int val = atoi(argv[0]); - if (val > 0) { - max_element = (uint32_t)val; - } - } - - dbg_print("Starting allocation of matrix of %d...\n", max_element); - uint32_t **elements = malloc(max_element * sizeof(uint32_t *)); - dbg_print("Starting allocation of each vector...\n"); - for (uint32_t i = 0; i < max_element; ++i) { - elements[i] = malloc(100 * sizeof(uint32_t)); - (*elements[i]) = i; - } - - dbg_print("Starting de-allocation of each vector...\n"); - for (uint32_t i = 0; i < max_element; ++i) { - free(elements[i]); - } - free(elements); - dbg_print("Done\n"); -} - -// Share memory keys used in shm test. -#define SHMKEY1 81 -#define SHMKEY2 82 - -// Spinlock used in the following shm test. -static spinlock_t shmspinlock1; -static spinlock_t shmspinlock2; - -// @brief Function used to test the shm. -int shm_test_1(void *args) -{ - (void)args; - - printf("[T1] I am the first process to be executed.\n"); - - int shmid = shmget(SHMKEY1, 128 * sizeof(char), 0777); - if (shmid == -1) { - printf("[T1] Error: shmget() failed!\n"); - - return -1; - } - printf("[T1] I have got a share memory with ID %i.\n", shmid); - - char *myshm = shmat(shmid, NULL, 0); - if (myshm == (void *)-1) { - printf("[T1] Error: shmat() failed!\n"); - - return -1; - } - printf("[T1] I attached the share memory in my virtual address. \n"); - printf("[T1] SHM VIRTUAL ADDRESS %p\n", myshm); - printf("[T1] SHM PHYSICAL ADDRESS %p\n", - paging_virtual_to_physical(get_current_page_directory(), myshm)); - - printf("[T1] Writing something on share memory.\n"); - memcpy(myshm, "Bella questa Share Memory!", 27); - - int ret = shmdt(myshm); - if (ret == -1) { - printf("[T1] Error: shmdt() failed!\n"); - - return -1; - } - printf("[T1] Share memory detached\n"); - - printf("[T2] Passing the control to Task 2.\n"); - spinlock_unlock(&shmspinlock1); - - return 0; -} - -// Function used to test the shm. -int shm_test_2(void *args) -{ - (void)args; - printf("[T2] I'm waiting that T1 finishes...\n"); - spinlock_lock(&shmspinlock1); - printf("[T2] Now it's my turn!\n"); - - int shmid = shmget(SHMKEY1, 128 * sizeof(char), 0777); - if (shmid == -1) { - printf("[T2] Error: shmget() failed!\n"); - - return -1; - } - printf("[T2] I have got a share memory with ID %i.\n", shmid); - - char *myshm = shmat(shmid, NULL, 0); - if (myshm == (void *)-1) { - printf("[T2] Error: shmat() failed!\n"); - - return -1; - } - printf("[T2] I attached the share memory in my virtual address. \n"); - printf("[T2] SHM VIRTUAL ADDRESS %p\n", myshm); - printf("[T2] SHM PHYSICAL ADDRESS %p\n", - paging_virtual_to_physical(get_current_page_directory(), myshm)); - - printf("[T2] I'm going to see what's inside this share memory...\n"); - printf(" << "); - - char *c = myshm; - while (*c != '\0') { - printf("%c", *c++); - } - printf(" >>\n"); - - int ret = shmdt(myshm); - if (ret == -1) { - printf("[T2] Error: shmdt() failed!\n"); - - return -1; - } - printf("[T2] Share memory detached\n"); - - printf("[T2] Passing the control to my father\n"); - spinlock_unlock(&shmspinlock2); - - return 0; -} - -void try_shm(int argc, char **argv) -{ - printf("Testing shm functions...\n"); - // printf("[F] I am the father process.\n"); - // printf("[F] Creating shm: shmget()\n"); - // - // size_t size = 128 *sizeof(char); - // - // int shmid = shmget(SHMKEY1, size, IPC_CREAT | 0777); - // if (shmid == -1) - // { - // printf( - // "[F] Error: attempt to create a shared memory already created!\n"); - // - // return; - // } - // printf("[F] Share memory %i with ID %i and SIZE %i byte. \n", SHMKEY1, - // shmid, size); - // - // spinlock_init(&shmspinlock1); - // spinlock_init(&shmspinlock2); - // - // spinlock_lock(&shmspinlock1); - // spinlock_lock(&shmspinlock2); - // - // int process1_id = execvp(shm_test_1, - // "shm_test_1", - // "shm_test_1"); - // printf("[F] I have created Task 1 with pid: %d\n", process1_id); - // int process2_id = execvp(shm_test_2, - // "shm_test_2", - // "shm_test_2"); - // printf("[F] I have created Task 2 with pid: %d\n", process2_id); - // - // printf("[F] Now I have to wait child finished processing.\n"); - // spinlock_lock(&shmspinlock2); - // - // int ret = shmctl(shmid, IPC_RMID, NULL); - // if (ret == -1) - // { - // printf("[F] Error: shmctl() failed!\n"); - // return; - // } - // - // printf("[F] Share memory removed... Finished! :)\n"); -} - -void try_badshm() -{ - // Attempt to create a Shared Memory. - size_t size = sizeof(int); - mode_t mode = 0777; - - int shmid = shmget(SHMKEY2, size, IPC_CREAT | mode); - if (shmid == -1) { - printf("Error: attempt to create a shared memory already created!\n"); - - return; - } - - printf("I created a Shared Memory with: \n"); - printf(" -> KEY: %5i \n", SHMKEY2); - printf(" -> ID: %5i \n", shmid); - printf(" -> SIZE: %5i \n", size); - printf(" -> FLAGS: %5o \n", IPC_CREAT | mode); - - char mode_str[100]; - strmode(mode, mode_str); - printf(" -> PERMISSIONS: %5s \n", mode_str); - printf("but I don't want to free it! \n"); - printf("Other process/functions can get this share memory. \n"); - printf("Try ipcs. \n"); -} - -int run_to_2(void *args) -{ - (void)args; - for (int i = 1; i <= 2; i++) { - sleep(i); - } - - return 0; -} - -int run_to_3(void *args) -{ - (void)args; - (void)args; - - for (int i = 1; i <= 3; i++) { - sleep(i); - } - - return 0; -} - -void try_scheduler() -{ - unsigned int start = timer_get_ticks(); - // // Disable the IRQs. - // irq_disable(); - // struct task_struct * process1 = kernel_create_process(run_to_2, - // "run_to_2", - // "run_to_2"); - // printf("I have created Task 1 with pid: %d\n", process1->id); - // struct task_struct * process2 = kernel_create_process(run_to_3, - // "run_to_3", - // "run_to_3"); - // printf("I have created Task 2 with pid: %d\n", process2->id); - // // Re-Enable the IRQs. - // irq_enable(); - // wait(process1); - // wait(process2); - unsigned int end = timer_get_ticks(); - printf("Total time of execution: %d\n", end - start); -} - -// The maximum number of tests. -#define MAX_TEST 20 - -/// @brief Define testing functions. -struct { - /// The name of the test. - char cmd_testname[CMD_LEN]; - /// A description of the test. - char cmd_description[DESC_LEN]; - - /// A pointer to the function. - void (*func)(int, char **); -} _testing_functions[MAX_TEST] = { - { "try_process", "Test multiple processes creation", try_process }, - { "try_stress_heap", "Tries to stress the heap", try_stress_heap }, - { "try_shm", "Test shared memory", try_shm }, - { "try_badshm", "Test shared memory without free it", try_badshm }, - { "try_scheduler", "Test the performance of different schduler", - try_scheduler } -}; - -void cmd_tester(int argc, char **argv) -{ - if (argc <= 1) { - printf("Bad usage. Try '%s --help' for more info about the usage.\n", - argv[0]); - - return; - } - if (!strcmp(argv[1], "--help")) { - printf("Testing functions.. "); - video_set_color(RED); - printf("Warning: for developers only!\n"); - video_set_color(GREY); - for (size_t i = 0; i < MAX_TEST; i++) { - if (_testing_functions[i].func == NULL) { - break; - } - printf(" [%-2d] %-20s%-20s\n", i, - _testing_functions[i].cmd_testname, - _testing_functions[i].cmd_description); - } - video_set_color(WHITE); - - return; - } - int testid = atoi(argv[1]); - for (size_t i = 0; i < MAX_TEST; i++) { - if (testid != i) { - continue; - } - if (_testing_functions[i].func == NULL) { - break; - } - printf("Running test %d...\n", testid); - ++argv; - ++argv; - (_testing_functions[i].func)(argc - 2, argv); - printf("Done running test %d...\n", testid); - - return; - } - printf("Error: Test %s not found.\n", argv[1]); - printf(" You have to provide the test id.\n"); -} diff --git a/mentos/src/ui/command/cmd_touch.c b/mentos/src/ui/command/cmd_touch.c deleted file mode 100644 index 860b5aa..0000000 --- a/mentos/src/ui/command/cmd_touch.c +++ /dev/null @@ -1,43 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_touch.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "fcntl.h" -#include "stdio.h" -#include "string.h" -#include "unistd.h" - -void cmd_touch(int argc, char **argv) -{ - if (argc != 2) - { - printf("%s: missing operand.\n", argv[0]); - printf("Try '%s --help' for more information.\n\n", argv[0]); - - return; - } - - if (strcmp(argv[1], "--help") == 0) - { - printf("Updates modification times of a given fine. If the does not" - "exists, it creates it.\n"); - printf("Usage:\n"); - printf(" touch \n"); - - return; - } - - int fd = open(argv[1], O_RDONLY, 0); - if (fd < 0) - { - fd = open(argv[1], O_CREAT, 0); - if (fd >= 0) - { - close(fd); - } - } - printf("\n"); -} diff --git a/mentos/src/ui/command/cmd_uname.c b/mentos/src/ui/command/cmd_uname.c deleted file mode 100644 index 8a069ab..0000000 --- a/mentos/src/ui/command/cmd_uname.c +++ /dev/null @@ -1,97 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file uname.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" -#include "video.h" -#include "string.h" -#include "utsname.h" -#include "version.h" -#include "cmd_cpuid.h" - -void cmd_uname(int argc, char **argv) -{ - utsname_t utsname; - uname(&utsname); - if (argc != 2) { - printf("%s\n", utsname.sysname); - - return; - } - - if (!(strcmp(argv[1], "-a")) || !(strcmp(argv[1], "--all"))) { - printf("%s %s #1 CEST 2013 %s\n", utsname.sysname, utsname.version, - sinfo.cpu_vendor); - } else if (!(strcmp(argv[1], "-r")) || !(strcmp(argv[1], "--rev"))) { - printf("%s\n", utsname.version); - } else if (!(strcmp(argv[1], "-h")) || !(strcmp(argv[1], "--help"))) { - printf( - "Uname function allow you to see the kernel and system information.\n"); - printf("Function avaibles:\n"); - printf("1) -a - Kernel version and processor type\n" - "2) -r - Only the kernel version\n" - "3) -i - All info of system and kernel\n"); - } else if (!(strcmp(argv[1], "-i")) || !(strcmp(argv[1], "--info"))) { - printf("\n:==========: :System info: :==========:\n\n"); - printf("Version: %s\n", OS_VERSION); - printf("Major: %d\n", OS_MAJOR_VERSION); - printf("Minor: %d\n", OS_MINOR_VERSION); - printf("Micro: %d\n", OS_MICRO_VERSION); - - // CPU Info. - printf("\nCPU:"); - video_set_color(BRIGHT_RED); - video_move_cursor(61, video_get_line()); - printf(sinfo.cpu_vendor); - video_set_color(WHITE); - printf("\n"); - - // Memory RAM Info. - /* - * printf("Memory RAM: "); - * video_move_cursor(60, video_get_line()); - * printf(" %d Kb\n", get_memsize()/1024); - * - * // Memory free RAM Info - * printf(LNG_FREERAM); - * video_move_cursor(60, video_get_line()); - * printf(" %d Kb\n", get_numpages()); - * - * printf("\n"); - * // Bitmap Info - * printf("Number bitmap's elements: "); - * video_move_cursor(60, video_get_line()); - * printf(" %d", get_bmpelements()); - * video_move_cursor(60, video_get_line()); - */ - - // Mem_area Info. - /* - * printf("\nSize of mem_area: "); - * video_move_cursor(60, video_get_line()); - * printf(" %d\n", sizeof(mem_area)); - */ - - // Page Dir Info. - /* - * printf("Page Dir Entry n.0 is: "); - * video_move_cursor(60, video_get_line()); - * printf(" %d\n", get_pagedir_entry(0)); - */ - - // Page Table Info. - /* - * printf("Page Table Entry n.4 in Page dir 0 is: "); - * video_move_cursor(60, video_get_line()); - * printf(" %d\n", get_pagetable_entry(0,4)); - */ - - printf("\n:==========: :===========: :==========:\n\n"); - } else { - printf("%s. For more info about this tool, please do 'uname --help'\n", - utsname.sysname); - } -} diff --git a/mentos/src/ui/command/cmd_whoami.c b/mentos/src/ui/command/cmd_whoami.c deleted file mode 100644 index 3be652f..0000000 --- a/mentos/src/ui/command/cmd_whoami.c +++ /dev/null @@ -1,15 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file cmd_whoami.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "commands.h" -#include "stdio.h" - -void cmd_whoami(int argc, char **argv) -{ - (void) argc; - (void) argv; - printf("%s\n", current_user.username); -} diff --git a/mentos/src/ui/init/init.c b/mentos/src/ui/init/init.c deleted file mode 100644 index d215ec0..0000000 --- a/mentos/src/ui/init/init.c +++ /dev/null @@ -1,40 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file init.c -/// @brief -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "init.h" -#include "wait.h" -#include "shell.h" -#include "errno.h" -#include "stdio.h" -#include "stdlib.h" -#include "unistd.h" -#include - -int main_init() -{ - pid_t cpid = vfork(); - - if (cpid == 0) - { - char *_argv[] = {"shell", "hello", (char *) NULL}; - char *_envp[] = {"/", (char *) NULL}; - - execve((const char *) shell, _argv, _envp); - - printf("This is bad, I should not be here! EXEC NOT WORKING\n"); - - return 1; - } - - int status; - while (true) - { - if ((cpid = wait(&status)) > 0) - dbg_print("Init has removed zombie children %d.\n", cpid); - } - - return 0; -} diff --git a/mentos/src/ui/shell/shell.c b/mentos/src/ui/shell/shell.c deleted file mode 100644 index 6d7c9de..0000000 --- a/mentos/src/ui/shell/shell.c +++ /dev/null @@ -1,479 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file shell.c -/// @brief Implement shell functions. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "wait.h" -#include "video.h" -#include "types.h" -#include "stdio.h" -#include "debug.h" -#include "panic.h" -#include "stdlib.h" -#include "unistd.h" -#include "string.h" -#include "version.h" -#include "process.h" -#include "keyboard.h" -#include "commands.h" -#include "shell_login.h" - -#define HISTORY_MAX 10 - -/// The current user. -userenv_t current_user; - -/// The input command. -static char cmd[CMD_LEN]; - -/// The index of the cursor. -static uint32_t cmd_cursor_index; - -/// History of commands. -char history[HISTORY_MAX][CMD_LEN]; - -/// -static int history_write_index = 0; - -/// -static int history_read_index = 0; - -static bool_t history_full = false; - -#define MAX_NUM_COM 50 ///< Maximum number of saved commands. -struct { - /// The name of the command. - char cmdname[CMD_LEN]; - /// The function pointer to the command. - CommandFunction function; - /// The description of the command. - char cmddesc[DESC_LEN]; -} _shell_commands[] = { - { "logo", cmd_logo, "Show an ascii art logo" }, - { "clear", cmd_clear, "Clear the screen" }, - { "echo", cmd_echo, "Print some lines of text" }, - { "poweroff", cmd_poweroff, "Turn off the machine" }, - { "uname", cmd_uname, - "Print kernel version, try uname --help for more info" }, - { "credits", cmd_credits, "Show " OS_NAME " credits" }, - { "sleep", cmd_sleep, "Pause the OS for a particular number of seconds" }, - { "cpuid", cmd_cpuid, "Show cpu identification informations" }, - { "help", cmd_help, "See the 'help' list to learn commands now available" }, - { "ls", cmd_ls, "Tool for listing dir - not complete-" }, - { "cd", cmd_cd, "Change dir - not complete-" }, - { "mkdir", cmd_mkdir, "Creates a new directory." }, - { "rm", cmd_rm, "Removes a file." }, - { "rmdir", cmd_rmdir, "Removes a directory." }, - { "whoami", cmd_whoami, "Show the current user name" }, - { "pwd", cmd_pwd, "Print current working directory" }, - { "more", cmd_more, "Read content of a file" }, - { "touch", cmd_touch, "Create a new file" }, - { "newfile", cmd_newfile, "Create a new file" }, - { "ps", cmd_ps, "Show task list" }, - { "date", cmd_date, "Show date and time" }, - { "clear", cmd_clear, "Clears the screen" }, - { "showpid", cmd_showpid, "Shows the PID of the shell" }, - { "history", cmd_show_history, "Shows the shell history" }, - { "nice", cmd_nice, "Change the nice value of the process" } -}; - -/// @brief Completely delete the current command. -static void shell_command_clear() -{ - for (size_t it = 0; it < cmd_cursor_index; ++it) { - putchar('\b'); - } - cmd_cursor_index = 0; -} - -/// -/// @brief -/// @param _cmd -static void shell_command_set(char *_cmd) -{ - // Outputs the command. - printf(_cmd); - // Moves the cursore. - cmd_cursor_index += strlen(_cmd); - // Copies the command. - strcpy(cmd, _cmd); -} - -static void shell_command_erase_char() -{ - if (cmd_cursor_index > 0) { - cmd[--cmd_cursor_index] = '\0'; - } -} - -static bool_t shell_command_append_char(char c) -{ - if ((cmd_cursor_index + 1) < CMD_LEN) { - cmd[cmd_cursor_index++] = c; - cmd[cmd_cursor_index] = '\0'; - - return true; - } - - return false; -} - -static inline void history_debug_print() -{ -#if 1 - // Prints the history stack with current indexes values. - dbg_print("------------------------------\n"); - for (size_t index = 0; index < HISTORY_MAX; ++index) { - dbg_print("[%d]%c%c: %s\n", index, - (index == history_write_index) ? 'w' : ' ', - (index == history_read_index) ? 'r' : ' ', history[index]); - } -#endif -} - -/// @brief Push the command inside the history. -static void history_push(char *_cmd) -{ - // Reset the read index. - history_read_index = history_write_index; - // Check if it is a duplicated entry. - if (history_write_index > 0) { - if (strcmp(history[history_write_index - 1], _cmd) == 0) { - return; - } - } - // Insert the node. - strcpy(history[history_write_index], _cmd); - if (++history_write_index >= HISTORY_MAX) { - history_write_index = 0; - history_full = true; - } - // Reset the read index. - history_read_index = history_write_index; - history_debug_print(); -} - -/// @brief Give the key allows to navigate through the history. -static char *history_fetch(const int key) -{ - if ((history_write_index == 0) && (history_full == false)) { - return NULL; - } - // If the history is empty do nothing. - char *_cmd = NULL; - int next_index = history_read_index; - // Update the position inside the history. - if (key == KEY_DOWN) { - ++next_index; - } else if (key == KEY_UP) { - --next_index; - } - // Check the next index. - if (history_full) { - if (next_index < 0) { - next_index = HISTORY_MAX - 1; - } else if (next_index >= HISTORY_MAX) { - next_index = 0; - } - // Do no read where ne will have to write next. - if (next_index == history_write_index) { - next_index = history_read_index; - } - } else { - if (next_index < 0) { - next_index = 0; - } else if (next_index >= history_write_index) { - next_index = history_read_index; - } - } - history_read_index = next_index; - _cmd = history[history_read_index]; - history_debug_print(); - // Return the command. - return _cmd; -} - -void cmd_show_history(int argc, char **argv) -{ - (void)argc; - (void)argv; - // Prints the history stack with current indexes values - printf("------------------------------\n"); - printf(" Debug history \n"); - printf("------------------------------\n"); - for (size_t index = 0; index < HISTORY_MAX; ++index) { - printf("[%d]%c%c: %s\n", index, - (index == history_write_index) ? 'w' : ' ', - (index == history_read_index) ? 'r' : ' ', history[index]); - } -} - -/// @brief Prints the prompt. -static void shell_print_prompt() -{ - video_set_color(BRIGHT_BLUE); - printf(current_user.username); - video_set_color(WHITE); - char cwd[MAX_PATH_LENGTH]; - getcwd(cwd, MAX_PATH_LENGTH); - printf("~:%s# ", cwd); - // Update the lower-bounds for the video. - lower_bound_x = video_get_column(); - lower_bound_y = video_get_line(); -} - -/// @brief Gets the inserted command. -static void shell_get_command() -{ - // Re-Initialize the cursor index. - cmd_cursor_index = 0; - //Initializing the current command line buffer - memset(cmd, '\0', CMD_LEN); - do { - int c = getchar(); - // Return Key - if (c == '\n') { - if (strlen(cmd) == 0) { - printf("\n"); - } - // Break the while loop. - break; - } else if (c == '\033') { - getchar(); // Skip '[' - c = getchar(); // Get the char. - char *_cmd = NULL; - if ((c == KEY_UP) || (c == KEY_DOWN)) { - _cmd = history_fetch(c); - } - if (_cmd != NULL) { - // Clear the current command. - shell_command_clear(); - // Sets the command. - shell_command_set(_cmd); - } - } else if (keyboard_is_ctrl_pressed() && (c == 'c')) { - // However, the ISR of the keyboard has already put the char. - // Thus, delete it by using backspace. - putchar('\b'); - // Re-set the index to the beginning. - cmd_cursor_index = 0; - // Go to the new line. - printf("\n\n"); - // Sets the command. - shell_command_set("\0"); - - // Break the while loop. - break; - } else if (c == '\b') { - shell_command_erase_char(); - } else { - if (!shell_command_append_char(c)) { - putchar('\b'); - } - } - } while (cmd_cursor_index < CMD_LEN); - - // Cleans all blanks at the beginning of the command. - trim(cmd); -} - -/// @brief Gets the inserted command. -static CommandFunction shell_find_command(char *command) -{ - if (command == NULL) { - return NULL; - } - // Matching and executing the command. - for (size_t it = 0; it < MAX_NUM_COM; ++it) { - // Skip commands with undefined functions. - if (_shell_commands[it].function == NULL) { - continue; - } - if (strcmp(command, _shell_commands[it].cmdname) != 0) { - continue; - } - - return _shell_commands[it].function; - } - - return NULL; -} - -static inline bool_t shell_is_separator(char c) -{ - return ((c == '\0') || (c == ' ') || (c == '\t') || (c == '\n') || - (c == '\r')); -} - -static int shell_count_words(const char *sentence) -{ - int result = 0; - bool_t inword = false; - const char *it = sentence; - do - if (shell_is_separator(*it)) { - if (inword) { - inword = false; - result++; - } - } else { - inword = true; - } - while (*it++); - - return result; -} - -/// @brief Gets the options from the command. -/// @param command The executed command. -static void shell_get_options(char *command, int *argc, char ***argv) -{ - // Get the number of arguments, return if zero. - if (((*argc) = shell_count_words(command)) == 0) { - return; - } - (*argv) = (char **)malloc(sizeof(char *) * ((*argc) + 1)); - bool_t inword = false; - const char *cit = command; - size_t argcIt = 0, argIt = 0; - do { - if (shell_is_separator(*cit)) { - if (inword) { - inword = false; - (*argv)[argcIt++][argIt] = '\0'; - argIt = 0; - } - } else { - // Allocate string for argument. - if (!inword) { - (*argv)[argcIt] = (char *)malloc(sizeof(char) * CMD_LEN); - } - inword = true; - (*argv)[argcIt][argIt++] = (*cit); - } - } while (*cit++); - (*argv)[argcIt] = NULL; -} - -void cmd_help(int argc, char **argv) -{ - if (argc > 2) { - printf("Too many arguments.\n\n"); - return; - } - if (argc == 1) { - printf("Available commands:\n"); - for (int i = 0, j = 0; i < MAX_NUM_COM; ++i) { - if (_shell_commands[i].function != NULL) { - printf("%-10s ", _shell_commands[i].cmdname); - if ((j++) == 3) { - printf("\n"); - j = 0; - } - } - } - printf("\n\n"); - return; - } - if (argc == 2) { - for (int i = 0; i < MAX_NUM_COM; ++i) { - if (strcmp(_shell_commands[i].cmdname, argv[1]) == 0) { - printf("%s\n\n", _shell_commands[i].cmddesc); - - return; - } - } - printf("Cannot find command: '%s'\n\n", argv[1]); - } - printf("\n"); -} - -int shell(int argc, char **argv, char **envp) -{ - dbg_print("I'm shell, I am the knight here...\n"); - - video_set_color(BRIGHT_BLUE); - printf("\t\t.: Welcome to MentOS :.\n\n"); - video_set_color(WHITE); - - dbg_print("I'm shell, I'll let my pawn, login, handle any intruder...\n"); - shell_login(); - - sys_chdir("/"); - current_user.uid = 1; - current_user.gid = 0; - - for (int i = 0; i < 50; ++i) { - putchar('\n'); - } - cmd_logo(1, NULL); - printf("\n\n\n\n"); - - dbg_print("I'm shell, let us begin...\n"); - - while (true) { - // First print the prompt. - shell_print_prompt(); - // Get the input command. - shell_get_command(); - // Check if the command is empty. - if (strlen(cmd) <= 0) { - continue; - } - // Retrieve the options from the command. - /// The current number of arguments. - int _argc = 1; - /// The vector of arguments. - char **_argv; - shell_get_options(cmd, &_argc, &_argv); - // Check if the command is empty. - if (_argc == 0) { - continue; - } - // Add the command to the history. - history_push(cmd); - // Find the command. - CommandFunction commandFunction = shell_find_command(_argv[0]); - if (commandFunction == NULL) { - printf("\nUnknown command: %s\n", _argv[0]); - } else if (strcmp(_argv[0], "cd") == 0) { - commandFunction(_argc, _argv); - } else { - int status; - pid_t cpid = vfork(); - if (cpid == 0) { - char *_envp[] = { (char *)NULL }; - execve((const char *)commandFunction, _argv, _envp); - kernel_panic("This is bad, I should not be here!\n"); - } - waitpid(cpid, &status, 0); - } - - // Free up the memory reserved for the arguments. - for (int it = 0; it < _argc; ++it) { - // Check if the argument is not empty. - if (_argv[it] != NULL) { - // Free up its memory. - free(_argv[it]); - } - } - free(_argv); - } - - return 0; -} - -void move_cursor_left(void) -{ - if (cmd_cursor_index > lower_bound_x) { - --cmd_cursor_index; - } -} - -void move_cursor_right(void) -{ - if (cmd_cursor_index < shell_lower_bound_x) { - ++cmd_cursor_index; - } -} diff --git a/mentos/src/ui/shell/shell_login.c b/mentos/src/ui/shell/shell_login.c deleted file mode 100644 index 7c1f124..0000000 --- a/mentos/src/ui/shell/shell_login.c +++ /dev/null @@ -1,152 +0,0 @@ -/// MentOS, The Mentoring Operating system project -/// @file shell_login.c -/// @brief Functions used to manage login. -/// @copyright (c) 2019 This file is distributed under the MIT License. -/// See LICENSE.md for details. - -#include "shell_login.h" -#include "vfs.h" -#include "stdio.h" -#include "fcntl.h" -#include "debug.h" -#include "video.h" -#include "string.h" -#include "keyboard.h" - -/// @brief Contains the credentials retrieved from the file. -typedef struct credentials_t { - /// The username. - char username[CREDENTIALS_LENGTH]; - /// The password. - char password[CREDENTIALS_LENGTH]; -} credentials_t; - -/// @brief Initialize the given credentials. -void init_credentials(credentials_t *credentials) -{ - if (credentials != NULL) { - memset(credentials->username, '\0', CREDENTIALS_LENGTH); - memset(credentials->password, '\0', CREDENTIALS_LENGTH); - } -} - -/// @brief -/// @param fd The FD of the file which contains the credentials. -/// @param credentials The structure which has to be filled with the -/// credentials. -/// @return If the credentials has been retrieved. -bool_t user_get(int fd, struct credentials_t *credentials) -{ - // Create a support array of char. - static char support[CREDENTIALS_LENGTH]; - // Cariable which will contain the number of bytes actually transferred. - ssize_t bytes_read = 0; - - // Get the USERNAME. - memset(support, '\0', CREDENTIALS_LENGTH); - for (int it = 0; it < CREDENTIALS_LENGTH; ++it) { - bytes_read = read(fd, &support[it], 1); - if (support[it] == ':') { - support[it] = '\0'; - - break; - } - } - replace_char(support, '\r', 0); - if ((bytes_read == 0) || (strlen(support) == 0)) { - return false; - } - strcpy(credentials->username, support); - - // Get the PASSWORD. - memset(support, '\0', CREDENTIALS_LENGTH); - for (int it = 0; it < CREDENTIALS_LENGTH; ++it) { - bytes_read = read(fd, &support[it], 1); - if ((support[it] == '\n') || (support[it] == EOF)) { - support[it] = '\0'; - - break; - } - } - - replace_char(support, '\r', 0); - if ((bytes_read == 0) || (strlen(support) == 0)) { - return false; - } - strcpy(credentials->password, support); - - return true; -} - -/// @brief Checks if the given credentials are correct. -bool_t check_credentials(credentials_t *credentials) -{ - // Initialize a variable for the return value. - bool_t status = false; - - /* Initialize the structure which will contain the username and the - * password. - */ - credentials_t existing; - init_credentials(&existing); - - // Open the file which contains the credentials. - // TODO: BUG: The first time the open is called, it fails. - int fd = open("/passwd", O_RDONLY, 0); - - // Check the file descriptor. - if (fd >= 0) { - // Get the next row inside the file containing the credentials. - while (user_get(fd, &existing) == true) { - if (!strcmp(credentials->username, existing.username) && - !strcmp(credentials->password, existing.password)) { - status = true; - - break; - } - } - // Close the file descriptor. - close(fd); - } else { - dbg_print("Can't open passwd file\n"); - } - return status; -} - -void shell_login() -{ - do { - // Initialize the credentials. - credentials_t credentials; - init_credentials(&credentials); - - // Ask the username. - printf("Username :"); - // Update the lower-bounds for the video. - lower_bound_x = video_get_column(); - lower_bound_y = video_get_line(); - // Get the username. - scanf("%50s", credentials.username); - - // Ask the password. - printf("Password :"); - // Update the lower-bounds for the video. - lower_bound_x = video_get_column(); - lower_bound_y = video_get_line(); - // Set the shadow option. - keyboard_set_shadow(true); - keyboard_set_shadow_character('*'); - // Get the password. - scanf("%50s", credentials.password); - // Disable the shadow option. - keyboard_set_shadow(false); - - // Check if the data are correct. - if (check_credentials(&credentials)) { - strcpy(current_user.username, credentials.username); - - break; - } - printf("Sorry, try again.\n"); - } while (true); -} diff --git a/programs/CMakeLists.txt b/programs/CMakeLists.txt new file mode 100644 index 0000000..8735a32 --- /dev/null +++ b/programs/CMakeLists.txt @@ -0,0 +1,128 @@ +# ============================================================================= +# Author: Enrico Fraccaroli +# ============================================================================= +# Set the minimum required version of cmake. +cmake_minimum_required(VERSION 2.8) +# Initialize the project. +project(programs C) + +# ============================================================================= +# Set the default build type to Debug. +if(NOT CMAKE_BUILD_TYPE) + message(STATUS "Setting build type to 'Debug' as none was specified.") + set(CMAKE_BUILD_TYPE + "Debug" + CACHE STRING "Choose the type of build." FORCE) +endif() + +# ============================================================================= +# Set the directory where the compiled binaries will be placed. +set(MENTOS_BIN_DIR ${CMAKE_SOURCE_DIR}/files/bin) + +# ============================================================================= +# Warning flags. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wpedantic") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic-errors") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-function") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-variable") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unknown-pragmas") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-but-set-variable") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") +# Set the compiler options. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -u_start") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdlib") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-builtin") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0") +elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") +endif(CMAKE_BUILD_TYPE STREQUAL "Debug") +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -no-pie -Wl,-z,norelro") + +# ============================================================================= +# Add the executables (manually). +set(PROGRAMS + init.c + cpuid.c + clear.c + date.c + ls.c + logo.c + nice.c + poweroff.c + rm.c + shell.c + uname.c + cat.c + echo.c + login.c + mkdir.c + pwd.c + rmdir.c + showpid.c + touch.c + env.c + ps.c + kill.c + ipcrm.c + ipcs.c + sleep.c + help.c) +foreach(PROGRAM ${PROGRAMS}) + # Prepare the program name. + string(REPLACE ".c" "" PROGRAM_NAME ${PROGRAM}) + + # Set the name of the target. + set(TARGET_NAME prog_${PROGRAM_NAME}) + + # Log the entry. + message(VERBOSE "Program ${PROGRAM_NAME} (source: ${PROGRAM}, make target: ${TARGET_NAME})") + + # Randomize .text section address so when debugging symbols don't clash The allowed range is from 256MB to 2.75GB Minimum allowed address: 0x10000000 Max allowed address: + # 0xB0000000 + string(MD5 RAND_HASH ${PROGRAM}) + string(SUBSTRING ${RAND_HASH} 1 3 TEXADDR_INFIX) + string( + RANDOM + LENGTH 1 + ALPHABET 0123456789AB + RANDOM_SEED ${RAND_HASH} TEXADDR_FIRST) + + # Create the target. + add_executable(${TARGET_NAME} ${PROGRAM}) + + # Add the includes. + target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/libc/inc) + + # Link the libc library. + target_link_libraries(${TARGET_NAME} ${CMAKE_BINARY_DIR}/libc/libc.a) + + # Add the dependency to libc. + add_dependencies(${TARGET_NAME} libc) + + # Add the linking properties. + set_target_properties(${TARGET_NAME} PROPERTIES LINK_FLAGS "-Wl,-Ttext=0x${TEXADDR_FIRST}${TEXADDR_INFIX}0000") + + # Add the final target that strips the debugging symbols from the program. + add_custom_target( + ${PROGRAM_NAME} + BYPRODUCTS ${MENTOS_BIN_DIR}/${PROGRAM_NAME} + COMMAND mkdir -p ${MENTOS_BIN_DIR} + # COMMAND cp ${PROJECT_BINARY_DIR}/${TARGET_NAME} + # ${MENTOS_BIN_DIR}/${PROGRAM_NAME} + COMMAND strip --strip-debug ${PROJECT_BINARY_DIR}/${TARGET_NAME} -o ${MENTOS_BIN_DIR}/${PROGRAM_NAME} + DEPENDS ${TARGET_NAME}) + + # Append the program name to the list of all the executables. + list(APPEND ALL_EXECUTABLES ${PROGRAM_NAME}) +endforeach() + +# Add the overall target that builds all the programs. +add_custom_target(all_programs ALL DEPENDS ${ALL_EXECUTABLES}) diff --git a/programs/cat.c b/programs/cat.c new file mode 100644 index 0000000..862e4d6 --- /dev/null +++ b/programs/cat.c @@ -0,0 +1,40 @@ +/// MentOS, The Mentoring Operating system project +/// @file cat.c +/// @brief `cat` program. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + if (argc != 2) { + printf("%s: missing operand.\n", argv[0]); + printf("Try '%s --help' for more information.\n\n", argv[0]); + return 1; + } + if (strcmp(argv[1], "--help") == 0) { + printf("Prints the content of the given file.\n"); + printf("Usage:\n"); + printf(" %s \n\n", argv[0]); + return 0; + } + int fd = open(argv[1], O_RDONLY, 42); + if (fd < 0) { + printf("%s: %s: %s\n\n", argv[0], strerror(errno), argv[1]); + return 1; + } + char buffer[BUFSIZ]; + // Put on the standard output the characters. + while (read(fd, buffer, BUFSIZ) > 0) { + puts(buffer); + } + putchar('\n'); + putchar('\n'); + close(fd); + return 0; +} diff --git a/programs/clear.c b/programs/clear.c new file mode 100644 index 0000000..910b13d --- /dev/null +++ b/programs/clear.c @@ -0,0 +1,13 @@ +/// MentOS, The Mentoring Operating system project +/// @file clear.c +/// @brief `clear` program. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include + +int main(int argc, char **argv) +{ + puts("\033[J"); + return 0; +} diff --git a/programs/cpuid.c b/programs/cpuid.c new file mode 100644 index 0000000..2a5f6aa --- /dev/null +++ b/programs/cpuid.c @@ -0,0 +1,118 @@ +/// MentOS, The Mentoring Operating system project +/// @file cpuid.c +/// @brief `cpuid` program. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include + +int main(int argc, char **argv) +{ +#if 0 + (void)argc; + (void)argv; + + // List of features. + const char *ecx_features[ECX_FLAGS_SIZE] = { + "SSE3", + "Reserved", + "Reserved", + "Monitor/MWAIT", + "CPL Debug Store", + "Virtual Machine", + "Safer Mode", + "Enhanced Intel SpeedStep Technology", + "Thermal Monitor 2", + "SSSE3", + "L1 Context ID", + "Reserved", + "Reserved", + "CMPXCHG16B", + "xTPR Update Control", + "Perfmon and Debug Capability", + "Reserved", + "Reserved", + "DCA", + "SSE4.1", + "SSE4.2", + "Reserved", + "Reserved", + "POPCNT" + }; + + const char *edx_features[EDX_FLAGS_SIZE] = { + "x87 FPU", + "Virtual 8086 Mode", + "Debugging Extensions", + "Page Size Extensions", + "Time Stamp Counter", + "RDMSR and WRMSR", + "Physical Address Extensions", + "Machine Check Exception", + "CMPXCHG8B", + "APIC On-chip", + "Reserved", + "SYSENTER and SYSEXIT", + "Memory Type Range Registers", + "PTE Global Bit", + "Machine Check Architecture", + "Conditional Move Instructions", + "Page Attribute Table", + "36-bit Page Size", + "Processor Serial Number", + "Reserved", + "Debug Store", + "Thermal Monitor and Clock Facilities", + "Intel MMX", + "FXSAVE and FXRSTOR", + "SSE", + "SSE2", + "Self Snoop", + "Multi-Threading", + "TTC", + "Reserved", + "Pending Break Enable" + }; + + int i; + int verbose = 0; + + // Examine possible options. + if (argv[1] != NULL) { + if (strcmp(argv[1], "-v") == 0) { + verbose = 1; + } else { + printf("Unknown option %s\n", argv[1]); + printf("CPUID help message\n" + "-v : shows verbose CPUID information\n"); + + return; + } + } + + printf("----- CPU ID Information -----\n"); + if (strcmp(sinfo.brand_string, "Reserved") != 0) { + printf("%s\n", sinfo.brand_string); + } + printf("Vendor: %s\n", sinfo.cpu_vendor); + printf("Type: %s, Family: %x, Model: %x\n", sinfo.cpu_type, + sinfo.cpu_family, sinfo.cpu_model); + printf("Apic ID: %d\n", sinfo.apic_id); + + if (verbose == 1) { + printf("\n--- Supported features ---\n"); + for (i = 0; i < ECX_FLAGS_SIZE; i++) { + if (sinfo.cpuid_ecx_flags[i] == 1) { + printf("%s\n", ecx_features[i]); + } + } + for (i = 0; i < EDX_FLAGS_SIZE; i++) { + if (sinfo.cpuid_edx_flags[i] == 1) { + printf("%s\n", edx_features[i]); + } + } + printf("---------------------------\n"); + } +#endif + return 0; +} diff --git a/programs/date.c b/programs/date.c new file mode 100644 index 0000000..7368d58 --- /dev/null +++ b/programs/date.c @@ -0,0 +1,22 @@ +/// MentOS, The Mentoring Operating system project +/// @file date.c +/// @brief `date` program. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include + +int main(int argc, char** argv) +{ + time_t rawtime = time(NULL); + printf("Seconds since 01/01/1970 : %u\n", rawtime); + tm_t* timeinfo = localtime(&rawtime); + printf( + "It's %2d:%2d:%2d, %d weekday, %02d/%02d/%4d\n", + timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, + timeinfo->tm_wday, + timeinfo->tm_mday, timeinfo->tm_mon, timeinfo->tm_year + ); + return 0; +} diff --git a/programs/echo.c b/programs/echo.c new file mode 100644 index 0000000..7d062fd --- /dev/null +++ b/programs/echo.c @@ -0,0 +1,117 @@ +/// MentOS, The Mentoring Operating system project +/// @file echo.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +#define ENV_NORM 1 +#define ENV_BRAK 2 +#define ENV_PROT 3 + +void expand_env(char *str, char *buf, size_t buf_len, int first_word, int last_word) +{ + // Buffer where we store the name of the variable. + char buffer[BUFSIZ] = { 0 }; + // Flags used to keep track of the special characters. + unsigned flags = 0; + // We keep track of where teh + char *env_start = NULL; + // Where we store the retrieved environmental variable value. + char *ENV = NULL; + // Get the length of the string. + size_t str_len = strlen(str); + // Position where we are writing on the buffer. + int b_pos = 0; + // Iterate the string. + for (int s_pos = 0; s_pos < str_len; ++s_pos) { + if (first_word && (s_pos == 0) && str[s_pos] == '"') + continue; + if (last_word && (s_pos == (str_len - 1)) && str[s_pos] == '"') + continue; + // If we find the backslash, we need to protect the next character. + if (str[s_pos] == '\\') { + if (bit_check(flags, ENV_PROT)) + buf[b_pos++] = '\\'; + else + bit_set_assign(flags, ENV_PROT); + continue; + } + // If we find the dollar, we need to catch the meaning. + if (str[s_pos] == '$') { + // If the previous character is a backslash, we just need to print the dollar. + if (bit_check(flags, ENV_PROT)) { + buf[b_pos++] = '$'; + } else if ((s_pos < (str_len - 2)) && ((str[s_pos + 1] == '{'))) { + // Toggle the open bracket method of accessing env variables. + bit_set_assign(flags, ENV_BRAK); + // We need to skip both the dollar and the open bracket `${`. + env_start = &str[s_pos + 2]; + } else { + // Toggle the normal method of accessing env variables. + bit_set_assign(flags, ENV_NORM); + // We need to skip the dollar `$`. + env_start = &str[s_pos + 1]; + } + continue; + } + if (bit_check(flags, ENV_BRAK)) { + if (str[s_pos] == '}') { + // Copy the environmental variable name. + strncpy(buffer, env_start, &str[s_pos] - env_start); + // Search for the environmental variable, and print it. + if ((ENV = getenv(buffer))) + for (int k = 0; k < strlen(ENV); ++k) + buf[b_pos++] = ENV[k]; + // Remove the flag. + bit_clear_assign(flags, ENV_BRAK); + } + continue; + } + if (bit_check(flags, ENV_NORM)) { + if (str[s_pos] == ':') { + // Copy the environmental variable name. + strncpy(buffer, env_start, &str[s_pos] - env_start); + // Search for the environmental variable, and print it. + if ((ENV = getenv(buffer))) + for (int k = 0; k < strlen(ENV); ++k) + buf[b_pos++] = ENV[k]; + // Copy the `:`. + buf[b_pos++] = str[s_pos]; + // Remove the flag. + bit_clear_assign(flags, ENV_NORM); + } + continue; + } + buf[b_pos++] = str[s_pos]; + } + if (bit_check(flags, ENV_NORM)) { + // Copy the environmental variable name. + strcpy(buffer, env_start); + // Search for the environmental variable, and print it. + if ((ENV = getenv(buffer))) + for (int k = 0; k < strlen(ENV); ++k) + buf[b_pos++] = ENV[k]; + // Remove the flag. + bit_clear_assign(flags, ENV_NORM); + } +} + +int main(int argc, char **argv) +{ + char buffer[BUFSIZ]; + // Iterate all the words. + for (int i = 1; i < argc; ++i) { + memset(buffer, 0, BUFSIZ); + expand_env(argv[i], buffer, BUFSIZ, (i == 1), (i == (argc - 1))); + puts(buffer); + if (i < (argc - 1)) + putchar(' '); + } + printf("\n\n"); + return 0; +} diff --git a/programs/env.c b/programs/env.c new file mode 100644 index 0000000..8ebf935 --- /dev/null +++ b/programs/env.c @@ -0,0 +1,17 @@ +/// MentOS, The Mentoring Operating system project +/// @file env.c +/// @brief `env` program. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include + +extern char **environ; + +int main(int argc, char *argv[]) +{ + for (char **ep = environ; *ep; ++ep) + printf("%s\n", *ep); + return 0; +} diff --git a/programs/help.c b/programs/help.c new file mode 100644 index 0000000..2fba54f --- /dev/null +++ b/programs/help.c @@ -0,0 +1,32 @@ +/// MentOS, The Mentoring Operating system project +/// @file help.c +/// @brief Shows the available commands. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + int fd = open("/bin", O_RDONLY | O_DIRECTORY, 0); + if (fd == -1) { + printf("%s: cannot access '/bin': %s\n\n", argv[0], strerror(errno)); + return 1; + } + dirent_t dent; + int per_line = 0; + while (getdents(fd, &dent, sizeof(dirent_t)) == sizeof(dirent_t)) { + printf("%10s ", dent.d_name); + if (++per_line == 6) { + per_line = 0; + putchar('\n'); + } + } + putchar('\n'); + close(fd); + return 0; +} diff --git a/programs/init.c b/programs/init.c new file mode 100644 index 0000000..45128a9 --- /dev/null +++ b/programs/init.c @@ -0,0 +1,28 @@ +/// MentOS, The Mentoring Operating system project +/// @file init.c +/// @brief `init` program. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char *argv[], char *envp[]) +{ + char *_argv[] = { "login", NULL }; + + if (fork() == 0) { + execv("/bin/login", _argv); + printf("This is bad, I should not be here! EXEC NOT WORKING\n"); + } + int status; +#pragma clang diagnostic push +#pragma ide diagnostic ignored "EndlessLoop" + while (1) { + wait(&status); + } +#pragma clang diagnostic pop + return 0; +} diff --git a/programs/ipcrm.c b/programs/ipcrm.c new file mode 100644 index 0000000..96aa5f2 --- /dev/null +++ b/programs/ipcrm.c @@ -0,0 +1,46 @@ +/// MentOS, The Mentoring Operating system project +/// @file ipcrm.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +int main(int argc, char **argv) +{ +#if 0 + if (argc != 2) { + printf("Bad arguments: you have to specify only IPC id, see ipcs.\n"); + + return; + } + + struct shmid_ds *shmid_ds = head; + struct shmid_ds *prev = NULL; + + while (shmid_ds != NULL) { + char strid[10]; + int_to_str(strid, (shmid_ds->shm_perm).seq, 10); + + if (strcmp(strid, argv[1]) == 0) { + break; + } + + prev = shmid_ds; + shmid_ds = shmid_ds->next; + } + + if (shmid_ds == NULL) { + printf("No shared memory find. \n"); + } else { + kfree(shmid_ds->shm_location); + + // shmid_ds = head. + if (prev == NULL) { + head = head->next; + } else { + prev->next = shmid_ds->next; + } + + kfree(shmid_ds); + } +#endif +} diff --git a/programs/ipcs.c b/programs/ipcs.c new file mode 100644 index 0000000..6e7d21f --- /dev/null +++ b/programs/ipcs.c @@ -0,0 +1,62 @@ +/// MentOS, The Mentoring Operating system project +/// @file ipcs.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#if 0 +static inline void print_sem_stat() +{ + printf("Semaphores: \n"); + printf("%-10s %-10s %-10s %-10s %-10s %-10s \n", "T", "ID", "KEY", "MODE", "OWNER", "GROUP"); + printf("%-20s %-10s %-20s \n\n", "", "Semaphores not implemented", ""); +} + +static inline void print_msg_stat() +{ + printf("Message Queues: \n"); + printf("%-10s %-10s %-10s %-10s %-10s %-10s \n", "T", "ID", "KEY", "MODE", "OWNER", "GROUP"); + printf("%-20s %-10s %-20s \n\n", "", "Message Queues not implemented", ""); +} + +static inline void print_shm_stat() +{ + printf("Shared Memory: \n"); + printf("%-10s %-10s %-10s %-10s %-10s %-10s \n", "T", "ID", "KEY", "MODE", "OWNER", "GROUP"); + printf("%-20s %-10s %-20s \n\n", "", "Shared Memory not implemented", ""); +} +#endif + +int main(int argc, char **argv) +{ +#if 0 + if (argc > 2) { + printf("Too much arguments.\n"); + + return; + } + + char datehour[100] = ""; + strdatehour(datehour); + + printf("IPC status from " OS_NAME " as of %s\n", datehour); + + if (argc == 2) { + if (strcmp(argv[1], "-s") == 0) { + print_sem_stat(); + } else if (strcmp(argv[1], "-m") == 0) { + print_shm_stat(); + } else if (strcmp(argv[1], "-q") == 0) { + print_msg_stat(); + } else { + printf("Option not recognize.\n"); + } + } else { + print_sem_stat(); + print_shm_stat(); + print_msg_stat(); + } + + return; +#endif +} diff --git a/programs/kill.c b/programs/kill.c new file mode 100644 index 0000000..e75cf31 --- /dev/null +++ b/programs/kill.c @@ -0,0 +1,107 @@ +/// MentOS, The Mentoring Operating system project +/// @file kill.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include + +static inline int is_number(char *s) +{ + size_t len = strlen(s); + for (size_t i = 0; i < len; ++i) + if (!isdigit(s[i])) + return 0; + return 1; +} + +static inline void print_signal_list() +{ + for (int it = 1; it < (NSIG - 1); ++it) { + printf("%6s ", strsignal(it)); + if ((it % 8) == 0) + putchar('\n'); + } +} + +static inline int get_signal(char *s) +{ + int signr = 0; + if (is_number(s)) + signr = atoi(s); + else { + ++s; + for (int it = 1; it < (NSIG - 1); ++it) { + if (strcmp(s, strsignal(it)) == 0) { + signr = it; + break; + } + } + } + if ((signr <= 0) || (signr >= NSIG)) + signr = 0; + return signr; +} + +static inline int get_pid(char *s) +{ + int pid = atoi(s); + return ((pid > 0) && (pid < PID_MAX_LIMIT)) ? pid : 0; +} + +static inline int send_signal(int pid, int signr) +{ + int ret = kill(pid, signr); + if (ret == -1) { + printf("[%d] %5d failed sending signal %d (%s) : %s\n", + ret, pid, signr, strsignal(signr), strerror(errno)); + return 0; + } + printf("[%d] %5d sent signal %d (%s).\n", + ret, pid, signr, strsignal(signr)); + return 1; +} + +int main(int argc, char **argv) +{ + int pid, signr; + + if (argc == 1) { + printf("%s: not enough arguments.\n", argv[0]); + puts("Type kill -l for a list of signals\n"); + return 0; + } + if (argc == 2) { + if (strcmp(argv[1], "-l") == 0) { + print_signal_list(); + } else if (is_number(argv[1])) { + if ((pid = get_pid(argv[1]))) { + send_signal(pid, SIGTERM); + } else { + printf("%s: not a valid pid `%s`\n", argv[0], argv[1]); + return 1; + } + } else { + printf("%s: unrecognized option `%s`\n", argv[0], argv[1]); + puts("Type kill -l for a list of signals\n"); + return 1; + } + } else { + if (!(signr = get_signal(argv[1]))) { + printf("%s: unrecognized signal `%s`.\n", argv[0], argv[1]); + return 1; + } + + for (int i = 2; i < argc; ++i) + if ((pid = get_pid(argv[i]))) + send_signal(pid, signr); + } + putchar('\n'); + return 0; +} diff --git a/programs/login.c b/programs/login.c new file mode 100644 index 0000000..c1685e4 --- /dev/null +++ b/programs/login.c @@ -0,0 +1,193 @@ +///// MentOS, The Mentoring Operating system project +/// @file login.c +/// @brief Functions used to manage login. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +/// Maximum length of credentials. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CREDENTIALS_LENGTH 50 + +#define FG_BLACK "\033[30m" +#define FG_WHITE "\033[37m" +#define FG_RED "\033[31m" +#define BG_WHITE "\033[47m" +#define BG_BLACK "\033[40m" + +void set_echo(bool_t active) +{ + struct termios _termios; + tcgetattr(STDIN_FILENO, &_termios); + if (active) + _termios.c_lflag |= (ICANON | ECHO); + else + _termios.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, 0, &_termios); +} + +void set_erase(bool_t active) +{ + struct termios _termios; + tcgetattr(STDIN_FILENO, &_termios); + if (active) + _termios.c_lflag |= ECHOE; + else + _termios.c_lflag &= ~ECHOE; + tcsetattr(STDIN_FILENO, 0, &_termios); +} + +/// @brief Gets the inserted command. +static bool_t get_input(char *input, size_t max_len, bool_t hide) +{ + size_t index = 0; + int c; + bool_t result = false; + + set_erase(false); + if (hide) { + set_echo(false); + } + + memset(input, 0, max_len); + do { + c = getchar(); + // Return Key + if (c == '\n') { + input[index] = 0; + result = true; + break; + } else if (c == '\033') { + c = getchar(); + if (c == '[') { + c = getchar(); // Get the char, and ignore it. + } else if (c == '^') { + c = getchar(); // Get the char. + if (c == 'C') { + // However, the ISR of the keyboard has already put the char. + // Thus, delete it by using backspace. + if (!hide) { + putchar('\b'); + putchar('\b'); + putchar('\n'); + } + result = false; + break; + } else if (c == 'U') { + if (!hide) { + // However, the ISR of the keyboard has already put the char. + // Thus, delete it by using backspace. + putchar('\b'); + putchar('\b'); + // Clear the current command. + for (size_t it = 0; it < index; ++it) { + putchar('\b'); + } + } + index = 0; + } + } + } else if (c == '\b') { + if (index > 0) { + if (!hide) { + putchar('\b'); + } + --index; + } + } else { + input[index++] = c; + if (index == (max_len - 1)) { + input[index] = 0; + result = true; + break; + } + } + } while (index < max_len); + + set_erase(true); + if (hide) { + set_echo(true); + putchar('\n'); + } + + return result; +} + +static inline int setup_env(passwd_t *pwd) +{ + // Set the HOME. + char env_buffer[BUFSIZ]; + // Set the USER. + if (setenv("USER", pwd->pw_name, 0) == -1) { + printf( "Failed to set env: `USER`\n"); + return 0; + } + // Set the SHELL. + if (setenv("SHELL", pwd->pw_shell, 0) == -1) { + printf( "Failed to set env: `SHELL`\n"); + return 0; + } + sprintf(env_buffer, "/home/%s", pwd->pw_name); + if (setenv("HOME", env_buffer, 0) == -1) { + printf("Failed to set env: `HOME`\n"); + return 0; + } + return 1; +} + +int main(int argc, char **argv) +{ + passwd_t *pwd; + char username[50], password[50]; + do { + // Get the username. + do { + printf("Username :"); + } while (!get_input(username, 50, false)); + do { + printf("Password :"); + } while (!get_input(password, 50, true)); + if ((pwd = getpwnam(username)) == NULL) { + if (errno == ENOENT) { + printf("The given name was not found.\n"); + } else if (errno == 0) { + printf("Cannot access passwd file.\n"); + } else { + printf("Unknown error (%s).\n", strerror(errno)); + } + continue; + } + if (strcmp(pwd->pw_passwd, password) != 0) { + printf("Wrong password.\n"); + continue; + } + break; + } while (true); + if (pwd->pw_shell == NULL) { + printf("%s: There is no shell set for the user `%s`.\n", argv[0], pwd->pw_name); + return 1; + } + + if (!setup_env(pwd)) { + printf("%s: Failed to setup the environmental variables.\n", argv[0]); + return 1; + } + + char *_argv[] = { pwd->pw_shell, (char *)NULL }; + if (execv(pwd->pw_shell, _argv) == -1) { + printf("%s: Failed to execute the shell.\n", argv[0]); + printf("%s: %s.\n", argv[0], strerror(errno)); + return 1; + } + return 0; +} \ No newline at end of file diff --git a/programs/logo.c b/programs/logo.c new file mode 100644 index 0000000..ff77725 --- /dev/null +++ b/programs/logo.c @@ -0,0 +1,18 @@ +/// MentOS, The Mentoring Operating system project +/// @file logo.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include + +int main(int argc, char **argv) +{ + printf(" __ __ _ ___ ____ \n"); + printf(" | \\/ | ___ _ __ | |_ / _ \\ / ___| \n"); + printf(" | |\\/| | / _ \\ | '_ \\ | __| | | | | \\___ \\ \n"); + printf(" | | | | | __/ | | | | | |_ | |_| | ___) |\n"); + printf(" |_| |_| \\___| |_| |_| \\__| \\___/ |____/ \n"); + return 0; +} diff --git a/programs/ls.c b/programs/ls.c new file mode 100644 index 0000000..0b233d3 --- /dev/null +++ b/programs/ls.c @@ -0,0 +1,152 @@ +/// MentOS, The Mentoring Operating system project +/// @file ls.c +/// @brief Command 'ls'. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FLAG_L (1U << 0U) +#define FLAG_A (1U << 1U) + +#define FG_BRIGHT_GREEN "\033[92m" +#define FG_BRIGHT_CYAN "\033[96m" +#define FG_BRIGHT_WHITE "\033[97m" +#define FG_BRIGHT_YELLOW "\033[93m" + +static void print_ls(int fd, const char *path, unsigned int flags) +{ + char relative_path[PATH_MAX], hidden = 0; + dirent_t dent; + stat_t dstat; + size_t total_size = 0; + tm_t *timeinfo; + while (getdents(fd, &dent, sizeof(dirent_t)) == sizeof(dirent_t)) { + // Check if the file starts with a dot (hidden), and we did not receive + // the `a` flag. + if ((dent.d_name[0] == '.') && !bitmask_check(flags, FLAG_A)) { + continue; + } + + // Prepare the relative path. + strcpy(relative_path, path); + if (strcmp(path, "/") != 0) + strcat(relative_path, "/"); + strcat(relative_path, dent.d_name); + + // Stat the file. + if (stat(relative_path, &dstat) == -1) { + continue; + } + + // Deal with the coloring. + if ((dent.d_type == DT_REG) && bitmask_check(dstat.st_mode, S_IXUSR)) { + puts(FG_BRIGHT_YELLOW); + } else if (dent.d_type == DT_DIR) { + puts(FG_BRIGHT_CYAN); + } else if (dent.d_type == DT_BLK) { + puts(FG_BRIGHT_GREEN); + } + + // Deal with the -l. + if (bitmask_check(flags, FLAG_L)) { + // Get the broken down time from the creation time of the file. + timeinfo = localtime(&dstat.st_ctime); + // Print the file type. + putchar(dt_char_array[dent.d_type]); + // Print the access permissions. + putchar(bitmask_check(dstat.st_mode, S_IRUSR) ? 'r' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IWUSR) ? 'w' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IXUSR) ? 'x' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IRGRP) ? 'r' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IWGRP) ? 'w' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IXGRP) ? 'x' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IROTH) ? 'r' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IWOTH) ? 'w' : '-'); + putchar(bitmask_check(dstat.st_mode, S_IXOTH) ? 'x' : '-'); + // Add a space. + putchar(' '); + // Print the rest. + printf("%4d %4d %11s %2d/%2d %2d:%2d %s\n", + dstat.st_uid, + dstat.st_gid, + to_human_size(dstat.st_size), + timeinfo->tm_mon, + timeinfo->tm_mday, + timeinfo->tm_hour, + timeinfo->tm_min, + dent.d_name); + total_size += dstat.st_size; + } else { + printf("%s ", dent.d_name); + } + + // Reset the color. + puts(FG_BRIGHT_WHITE); + } + printf("\n"); + if (bitmask_check(flags, FLAG_L)) { + printf("Total: %d byte\n", total_size); + } + printf("\n"); +} + +int main(int argc, char *argv[]) +{ + // Create a variable to store flags. + uint32_t flags = 0; + // Check the number of arguments. + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--help") == 0) { + printf("List information about files inside a given directory.\n"); + printf("Usage:\n"); + printf(" ls [options] [directory]\n\n"); + return 0; + } else if (argv[i][0] == '-') { + for (int j = 0; j < strlen(argv[i]); ++j) { + if (argv[i][j] == 'l') + bitmask_set_assign(flags, FLAG_L); + else if (argv[i][j] == 'a') + bitmask_set_assign(flags, FLAG_A); + } + } + } + + bool_t no_directory = true; + for (int i = 1; i < argc; ++i) { + if (argv[i][0] == '-') + continue; + no_directory = false; + int fd = open(argv[i], O_RDONLY | O_DIRECTORY, 0); + if (fd == -1) { + printf("%s: cannot access '%s': %s\n\n", argv[0], argv[i], strerror(errno)); + } else { + printf("%s:\n", argv[i]); + print_ls(fd, argv[i], flags); + close(fd); + } + } + if (no_directory) { + char cwd[PATH_MAX]; + getcwd(cwd, PATH_MAX); + int fd = open(cwd, O_RDONLY | O_DIRECTORY, 0); + if (fd == -1) { + printf("%s: cannot access '%s': %s\n\n", argv[0], cwd, strerror(errno)); + } else { + print_ls(fd, cwd, flags); + close(fd); + } + } + return 0; +} diff --git a/programs/mkdir.c b/programs/mkdir.c new file mode 100644 index 0000000..5f74eb3 --- /dev/null +++ b/programs/mkdir.c @@ -0,0 +1,30 @@ +/// MentOS, The Mentoring Operating system project +/// @file mkdir.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + // Check the number of arguments. + if (argc != 2) { + printf("%s: missing operand.\n", argv[0]); + printf("Try '%s --help' for more information.\n\n", argv[0]); + return 1; + } + if (strcmp(argv[1], "--help") == 0) { + printf("Creates a new directory.\n"); + printf("Usage:\n"); + printf(" %s \n", argv[0]); + return 0; + } + if (mkdir(argv[1], 0)) { + printf("%s: cannot create directory '%s': %s\n", argv[0], argv[1], strerror(errno)); + } + return 0; +} diff --git a/programs/nice.c b/programs/nice.c new file mode 100644 index 0000000..63bc58b --- /dev/null +++ b/programs/nice.c @@ -0,0 +1,35 @@ +/// MentOS, The Mentoring Operating system project +/// @file nice.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include + +int main(int argc, char** argv) +{ + if (argc == 1) { + int current = nice(0); + printf("%d \n\n", current); + return 0; + } + if (argc != 2) { + printf("%s: missing operand.\n", argv[0]); + printf("Try '%s --help' for more information.\n\n", argv[0]); + return 1; + } + if (!strcmp(argv[1], "--help")) { + printf("Usage: %s \n\n", argv[0]); + return 0; + } + int increment = atoi(argv[1]); + if ((increment < -40) || (increment > 40)) { + printf("Error: You must provide a value between (-40,+40). \n\n", increment); + return 1; + } + int newNice = nice(increment); + printf("Your new nice value is %d.\n\n", newNice); + return 0; +} diff --git a/programs/poweroff.c b/programs/poweroff.c new file mode 100644 index 0000000..ef34fa4 --- /dev/null +++ b/programs/poweroff.c @@ -0,0 +1,20 @@ +/// MentOS, The Mentoring Operating system project +/// @file poweroff.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include + +int main(int argc, char** argv) +{ + printf("Executing power-off...\n"); + reboot( + LINUX_REBOOT_MAGIC1, + LINUX_REBOOT_MAGIC2, + LINUX_REBOOT_CMD_POWER_OFF, + NULL); + return 0; +} diff --git a/programs/ps.c b/programs/ps.c new file mode 100644 index 0000000..4b90272 --- /dev/null +++ b/programs/ps.c @@ -0,0 +1,129 @@ +/// MentOS, The Mentoring Operating system project +/// @file ps.c +/// @brief Report a snapshot of the current processes. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include + +#define FORMAT_S "%5s %5s %6s %s\n" +#define FORMAT "%5d %5d %6c %s\n" + +static inline void __iterate_proc_dirs(int fd) +{ + char absolute_path[PATH_MAX] = "/proc/"; + // Holds the file descriptor of the stat file. + int stat_fd; + // Buffer used to read the stat file. + char stat_buffer[BUFSIZ] = { 0 }; + // Holds the number of bytes read from stat file. + ssize_t ret; + // Variables used to read the stat file. + // (1) pid %d + // (2) comm %s + // (3) state %c + // (4) ppid %d + pid_t pid; + char comm[BUFSIZ] = { 0 }; + char state; + pid_t ppid; + // The directory entry. + dirent_t dent; + while (getdents(fd, &dent, sizeof(dirent_t)) == sizeof(dirent_t)) { + // Skip non-directories. + if (dent.d_type != DT_DIR) + continue; + // Build the path to the stat file (i.e., `/proc//stat`). + strcpy(absolute_path + 6, dent.d_name); + strcat(absolute_path, "/stat"); + // Open the `/proc//stat` file. + if ((stat_fd = open(absolute_path, O_RDONLY, 0)) == -1) { + printf("Failed to open `%s`\n", absolute_path); + continue; + } + // Reset the stat buffer. + memset(stat_buffer, 0, BUFSIZ); + // Read the content of the stat file. + if ((ret = read(stat_fd, stat_buffer, BUFSIZ)) <= 0) { + printf("Cannot read `%s`\n", absolute_path); + close(stat_fd); + continue; + } + // Reset the comm buffer. + memset(comm, 0, BUFSIZ); + // Parse the content of the stat file. + sscanf(stat_buffer, "%d %s %c %d", &pid, comm, &state, &ppid); + // Print the stats concerning the process. + printf(FORMAT, pid, ppid, state, comm); + // Closing stat FD. + close(stat_fd); + } +} + +int main(int argc, char **argv) +{ + int fd = open("/proc", O_RDONLY | O_DIRECTORY, 0); + if (fd == -1) { + printf("ps: cannot access '/proc': %s\n\n", strerror(errno)); + } else { + printf(FORMAT_S, "PID", "PPID", "STATUS", "CMD"); + __iterate_proc_dirs(fd); + close(fd); + } + +#if 0 + + DIR *dir; + struct dirent_t *ent; + int i, fd_self, fd; + unsigned long time, stime; + char flag, *tty; + char cmd[256], tty_self[256], path[256], time_s[256]; + FILE *file; + + dir = opendir("/proc"); + fd_self = open("/proc/self/fd/0", O_RDONLY); + sprintf(tty_self, "%s", ttyname(fd_self)); + printf(FORMAT, "PID", "TTY", "TIME", "CMD"); + + while ((ent = readdir(dir)) != NULL) { + flag = 1; + for (i = 0; ent->d_name[i]; i++) + if (!isdigit(ent->d_name[i])) { + flag = 0; + break; + } + + if (flag) { + sprintf(path, "/proc/%s/fd/0", ent->d_name); + fd = open(path, O_RDONLY); + tty = ttyname(fd); + + if (tty && strcmp(tty, tty_self) == 0) { + sprintf(path, "/proc/%s/stat", ent->d_name); + file = fopen(path, "r"); + fscanf(file, "%d%s%c%c%c", &i, cmd, &flag, &flag, &flag); + cmd[strlen(cmd) - 1] = '\0'; + + for (i = 0; i < 11; i++) + fscanf(file, "%lu", &time); + fscanf(file, "%lu", &stime); + time = (int)((double)(time + stime) / sysconf(_SC_CLK_TCK)); + sprintf(time_s, "%02lu:%02lu:%02lu", + (time / 3600) % 3600, (time / 60) % 60, time % 60); + + printf(FORMAT, ent->d_name, tty + 5, time_s, cmd + 1); + fclose(file); + } + close(fd); + } + } + close(fd_self); +#endif + return 0; +} diff --git a/programs/pwd.c b/programs/pwd.c new file mode 100644 index 0000000..f57e020 --- /dev/null +++ b/programs/pwd.c @@ -0,0 +1,17 @@ +/// MentOS, The Mentoring Operating system project +/// @file pwd.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include + +int main(int argc, char** argv) +{ + char cwd[PATH_MAX]; + getcwd(cwd, PATH_MAX); + printf("%s\n", cwd); + return 0; +} diff --git a/programs/rm.c b/programs/rm.c new file mode 100644 index 0000000..fe320b2 --- /dev/null +++ b/programs/rm.c @@ -0,0 +1,78 @@ +/// MentOS, The Mentoring Operating system project +/// @file rm.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include + +bool_t has_option(int argc, char **argv, const char *first, ...) +{ + va_list ap; + const char *opt; + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], first) == 0) + return true; + va_start(ap, first); + while ((opt = va_arg(ap, const char *)) != NULL) { + if (strcmp(argv[i], opt) == 0) + return true; + } + va_end(ap); + } + return false; +} + +int main(int argc, char **argv) +{ + if (argc <= 1) { + printf("%s: missing operand.\n", argv[0]); + printf("Try '%s --help' for more information.\n\n", argv[0]); + return 1; + } + if (strcmp(argv[1], "--help") == 0) { + printf("Remove (unlink) the FILE(s).\n"); + printf("Usage:\n"); + printf(" rm \n"); + return 0; + } + if (strcmp(basename(argv[argc - 1]), "*") == 0) { + char directory[PATH_MAX], fullpath[PATH_MAX]; + int fd; + + if (strcmp(argv[argc - 1], "*") == 0) { + getcwd(directory, PATH_MAX); + } else { + strcpy(directory, dirname(argv[argc - 1])); + } + + if ((fd = open(directory, O_RDONLY | O_DIRECTORY, 0)) != -1) { + dirent_t dent; + while (getdents(fd, &dent, sizeof(dirent_t)) == sizeof(dirent_t)) { + strcpy(fullpath, directory); + strcat(fullpath, dent.d_name); + if (dent.d_type == DT_REG) { + if (unlink(fullpath) == 0) { + if (lseek(fd, -1, SEEK_CUR) != -1) { + printf("Failed to move back the getdents...\n"); + } + } + } + } + close(fd); + } + } else { + if (unlink(argv[argc - 1]) < 0) { + printf("%s: cannot remove '%s': %s\n\n", argv[0], argv[argc - 1], strerror(errno)); + return 1; + } + } + printf("\n"); + return 0; +} diff --git a/programs/rmdir.c b/programs/rmdir.c new file mode 100644 index 0000000..3ff4665 --- /dev/null +++ b/programs/rmdir.c @@ -0,0 +1,31 @@ +/// MentOS, The Mentoring Operating system project +/// @file rmdir.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + // Check the number of arguments. + if (argc != 2) { + printf("Bad usage.\n"); + printf("Try 'rmdir --help' for more information.\n"); + return 1; + } + if (strcmp(argv[1], "--help") == 0) { + printf("Removes a directory.\n"); + printf("Usage:\n"); + printf(" rmdir \n"); + return 0; + } + if (rmdir(argv[1]) == -1) { + printf("%s: failed to remove '%s': %s\n\n", argv[0], argv[1], strerror(errno)); + return 1; + } + return 0; +} diff --git a/programs/shell.c b/programs/shell.c new file mode 100644 index 0000000..24c70ad --- /dev/null +++ b/programs/shell.c @@ -0,0 +1,772 @@ +/// MentOS, The Mentoring Operating system project +/// @file shell.c +/// @brief Implement shell functions. +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include +#include +#include "stdbool.h" +#include "stddef.h" +#include "string.h" +#include "stdio.h" +#include "stdlib.h" +#include "strerror.h" +#include "termios.h" +#include "limits.h" +#include "sys/utsname.h" + +/// Maximum length of commands. +#define CMD_LEN 32 +/// Maximum lenght of the history. +#define HISTORY_MAX 10 + +/// @brief A set of colors. +#define FG_BLACK "\033[30m" +#define FG_RED "\033[31m" +#define FG_GREEN "\033[32m" +#define FG_YELLOW "\033[33m" +#define FG_BLUE "\033[34m" +#define FG_MAGENTA "\033[35m" +#define FG_CYAN "\033[36m" +#define FG_WHITE "\033[37m" +#define FG_BRIGHT_BLACK "\033[90m" +#define FG_BRIGHT_RED "\033[91m" +#define FG_BRIGHT_GREEN "\033[92m" +#define FG_BRIGHT_YELLOW "\033[93m" +#define FG_BRIGHT_BLUE "\033[94m" +#define FG_BRIGHT_MAGENTA "\033[95m" +#define FG_BRIGHT_CYAN "\033[96m" +#define FG_BRIGHT_WHITE "\033[97m" +#define BG_BLACK "\033[40m" +#define BG_RED "\033[41m" +#define BG_GREEN "\033[42m" +#define BG_YELLOW "\033[43m" +#define BG_BLUE "\033[44m" +#define BG_MAGENTA "\033[45m" +#define BG_CYAN "\033[46m" +#define BG_WHITE "\033[47m" +#define BG_BRIGHT_BLACK "\033[100m" +#define BG_BRIGHT_RED "\033[101m" +#define BG_BRIGHT_GREEN "\033[102m" +#define BG_BRIGHT_YELLOW "\033[103m" +#define BG_BRIGHT_BLUE "\033[104m" +#define BG_BRIGHT_MAGENTA "\033[105m" +#define BG_BRIGHT_CYAN "\033[106m" +#define BG_BRIGHT_WHITE "\033[107m" + +// Required by `export` +#define ENV_NORM 1 +#define ENV_BRAK 2 +#define ENV_PROT 3 + +// The input command. +static char cmd[CMD_LEN] = { 0 }; +// The index of the cursor. +static size_t cmd_cursor_index = 0; +// History of commands. +static char history[HISTORY_MAX][CMD_LEN] = { 0 }; +// The current write index inside the history. +static int history_write_index = 0; +// The current read index inside the history. +static int history_read_index = 0; +// Boolean used to check if the history is full. +static bool_t history_full = false; + +static inline int __is_separator(char c) +{ + return ((c == '\0') || (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r')); +} + +static inline int __count_words(const char *sentence) +{ + int result = 0; + bool_t inword = false; + const char *it = sentence; + do { + if (__is_separator(*it)) { + if (inword) { + inword = false; + result++; + } + } else { + inword = true; + } + } while (*it++); + return result; +} + +static inline void __set_echo(bool_t active) +{ + struct termios _termios; + tcgetattr(STDIN_FILENO, &_termios); + if (active) { + _termios.c_lflag |= (ICANON | ECHO); + } else { + _termios.c_lflag &= ~(ICANON | ECHO); + } + tcsetattr(STDIN_FILENO, 0, &_termios); +} + +static inline int __folder_contains( + const char *folder, + const char *entry, + int accepted_type, + dirent_t *result) +{ + int fd = open(folder, O_RDONLY | O_DIRECTORY, 0); + if (fd == -1) + return 0; + // Prepare the variables for the search. + dirent_t dent; + size_t entry_len = strlen(entry); + int found = 0; + while (getdents(fd, &dent, sizeof(dirent_t)) == sizeof(dirent_t)) { + if (accepted_type && (accepted_type != dent.d_type)) + continue; + if (strncmp(entry, dent.d_name, entry_len) == 0) { + *result = dent; + found = 1; + break; + } + } + close(fd); + return found; +} + +static inline int __search_in_path(const char *entry, dirent_t *result) +{ + // Determine the search path. + char *PATH_VAR = getenv("PATH"); + if (PATH_VAR == NULL) + PATH_VAR = "/bin:/usr/bin"; + // Copy the path. + char *path = strdup(PATH_VAR); + // Iterate through the path entries. + char *token = strtok(path, ":"); + if (token == NULL) { + free(path); + return 0; + } + do { + if (__folder_contains(token, entry, DT_REG, result)) + return 1; + } while ((token = strtok(NULL, ":"))); + free(path); + return 0; +} + +/// @brief Prints the prompt. +static inline void __prompt_print() +{ + // Get the current working directory. + char CWD[PATH_MAX]; + getcwd(CWD, PATH_MAX); + // If the current working directory is equal to HOME, show ~. + char *HOME = getenv("HOME"); + if (HOME != NULL) + if (strcmp(CWD, HOME) == 0) + strcpy(CWD, "~\0"); + // Get the user. + char *USER = getenv("USER"); + if (USER == NULL) { + USER = "error"; + } + time_t rawtime = time(NULL); + tm_t *timeinfo = localtime(&rawtime); + // Get the hostname. + char *HOSTNAME; + utsname_t buffer; + if (uname(&buffer) < 0) { + HOSTNAME = "error"; + } else { + HOSTNAME = buffer.nodename; + } + printf(FG_GREEN "%s" FG_WHITE "@" FG_CYAN "%s " FG_BRIGHT_BLUE "[%02d:%02d:%02d]" FG_WHITE " [%s] " FG_BRIGHT_WHITE "%% ", + USER, HOSTNAME, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, CWD); +} + +static void __expand_env(char *str, char *buf, size_t buf_len) +{ + // Buffer where we store the name of the variable. + char buffer[BUFSIZ] = { 0 }; + // Flags used to keep track of the special characters. + unsigned flags = 0; + // We keep track of where teh + char *env_start = NULL; + // Where we store the retrieved environmental variable value. + char *ENV = NULL; + // Get the length of the string. + size_t str_len = strlen(str); + // Position where we are writing on the buffer. + int b_pos = 0; + // Iterate the string. + for (int s_pos = 0; s_pos < str_len; ++s_pos) { + if ((s_pos == 0) && str[s_pos] == '"') + continue; + if ((s_pos == (str_len - 1)) && str[s_pos] == '"') + continue; + // If we find the backslash, we need to protect the next character. + if (str[s_pos] == '\\') { + if (bit_check(flags, ENV_PROT)) + buf[b_pos++] = '\\'; + else + bit_set_assign(flags, ENV_PROT); + continue; + } + // If we find the dollar, we need to catch the meaning. + if (str[s_pos] == '$') { + // If the previous character is a backslash, we just need to print the dollar. + if (bit_check(flags, ENV_PROT)) { + buf[b_pos++] = '$'; + } else if ((s_pos < (str_len - 2)) && ((str[s_pos + 1] == '{'))) { + // Toggle the open bracket method of accessing env variables. + bit_set_assign(flags, ENV_BRAK); + // We need to skip both the dollar and the open bracket `${`. + env_start = &str[s_pos + 2]; + } else { + // Toggle the normal method of accessing env variables. + bit_set_assign(flags, ENV_NORM); + // We need to skip the dollar `$`. + env_start = &str[s_pos + 1]; + } + continue; + } + if (bit_check(flags, ENV_BRAK)) { + if (str[s_pos] == '}') { + // Copy the environmental variable name. + strncpy(buffer, env_start, &str[s_pos] - env_start); + // Search for the environmental variable, and print it. + if ((ENV = getenv(buffer))) + for (int k = 0; k < strlen(ENV); ++k) + buf[b_pos++] = ENV[k]; + // Remove the flag. + bit_clear_assign(flags, ENV_BRAK); + } + continue; + } + if (bit_check(flags, ENV_NORM)) { + if (str[s_pos] == ':') { + // Copy the environmental variable name. + strncpy(buffer, env_start, &str[s_pos] - env_start); + // Search for the environmental variable, and print it. + if ((ENV = getenv(buffer))) + for (int k = 0; k < strlen(ENV); ++k) + buf[b_pos++] = ENV[k]; + // Copy the `:`. + buf[b_pos++] = str[s_pos]; + // Remove the flag. + bit_clear_assign(flags, ENV_NORM); + } + continue; + } + buf[b_pos++] = str[s_pos]; + } + if (bit_check(flags, ENV_NORM)) { + // Copy the environmental variable name. + strcpy(buffer, env_start); + // Search for the environmental variable, and print it. + if ((ENV = getenv(buffer))) + for (int k = 0; k < strlen(ENV); ++k) + buf[b_pos++] = ENV[k]; + // Remove the flag. + bit_clear_assign(flags, ENV_NORM); + } +} + +static int __export(int argc, char *argv[]) +{ + char name[BUFSIZ] = { 0 }, value[BUFSIZ] = { 0 }; + char *first, *last; + size_t name_len, value_start; + for (int i = 1; i < argc; ++i) { + // Get a pointer to the first and last occurrence of `=` inside the argument. + first = strchr(argv[i], '='), last = strrchr(argv[i], '='); + // Check validity of first and last, and check that they are the same. + if (!first || !last || (last < argv[i]) || (first != last)) + continue; + // Length of the name. + name_len = last - argv[i]; + // Set where the value starts. + value_start = (last + 1) - argv[i]; + // Copy the name. + strncpy(name, argv[i], name_len); + // Expand the environmental variables inside the argument. + __expand_env(&argv[i][value_start], value, BUFSIZ); + // Try to set the environmental variable. + if ((strlen(name) > 0) && (strlen(value) > 0)) { + if (setenv(name, value, 1) == -1) { + printf("Failed to set environmental variable.\n"); + return 1; + } + } + } + return 0; +} + +static int __cd(int argc, char *argv[]) +{ + if (argc > 2) { + printf("%s: too many arguments\n\n", argv[0]); + return 1; + } + const char *path = NULL; + if (argc == 2) { + path = argv[1]; + } else { + path = getenv("HOME"); + if (path == NULL) { + printf("cd: There is no home directory set.\n"); + return 1; + } + } + int fd = open(path, O_RDONLY | O_DIRECTORY, 0); + if (fd == -1) { + printf("cd: %s: %s\n\n", argv[0], strerror(errno), path); + return 1; + } + // Set current working directory. + fchdir(fd); + close(fd); + // Get the updated working directory. + char cwd[PATH_MAX]; + getcwd(cwd, PATH_MAX); + // Update the environmental variable. + if (setenv("PWD", cwd, 1) == -1) { + printf("cd: Failed to set current working directory.\n"); + return 1; + } + return 0; +} + +/// @brief Push the command inside the history. +static inline void __hst_push(char *_cmd) +{ + // Reset the read index. + history_read_index = history_write_index; + // Check if it is a duplicated entry. + if (history_write_index > 0) { + if (strcmp(history[history_write_index - 1], _cmd) == 0) { + return; + } + } + // Insert the node. + strcpy(history[history_write_index], _cmd); + if (++history_write_index >= HISTORY_MAX) { + history_write_index = 0; + history_full = true; + } + // Reset the read index. + history_read_index = history_write_index; +} + +/// @brief Give the key allows to navigate through the history. +static char *__hst_fetch(bool_t up) +{ + if ((history_write_index == 0) && (history_full == false)) { + return NULL; + } + // If the history is empty do nothing. + char *_cmd = NULL; + // Update the position inside the history. + int next_index = history_read_index + (up ? -1 : +1); + // Check the next index. + if (history_full) { + if (next_index < 0) { + next_index = HISTORY_MAX - 1; + } else if (next_index >= HISTORY_MAX) { + next_index = 0; + } + // Do no read where ne will have to write next. + if (next_index == history_write_index) { + next_index = history_read_index; + return NULL; + } + } else { + if (next_index < 0) { + next_index = 0; + } else if (next_index >= history_write_index) { + next_index = history_read_index; + return NULL; + } + } + history_read_index = next_index; + _cmd = history[history_read_index]; + // Return the command. + return _cmd; +} + +/// @brief Completely delete the current command. +static inline void __cmd_clr() +{ + // First we need to get back to the end of the line. + while (cmd[cmd_cursor_index] != 0) { + ++cmd_cursor_index; + puts("\033[1C"); + } + // Then we delete all the character. + for (size_t it = 0; it < cmd_cursor_index; ++it) { + putchar('\b'); + } + // Reset the index. + cmd_cursor_index = 0; +} + +/// @brief Sets the new command. +static inline void __cmd_set(char *_cmd) +{ + // Outputs the command. + printf(_cmd); + // Moves the cursore. + cmd_cursor_index += strlen(_cmd); + // Copies the command. + strcpy(cmd, _cmd); +} + +/// @brief Erases one character from the console. +static inline void __cmd_ers() +{ + if (cmd_cursor_index > 0) { + strcpy(cmd + cmd_cursor_index - 1, cmd + cmd_cursor_index); + putchar('\b'); + --cmd_cursor_index; + } +} + +/// @brief Appends the character `c` on the command. +static inline int __cmd_app(char c) +{ + if ((cmd_cursor_index + 1) < CMD_LEN) { + // If at the current index there is a character, shift the entire + // command ahead. + if (cmd[cmd_cursor_index] != 0) { + // Move forward the entire string. + for (unsigned long i = strlen(cmd); i > cmd_cursor_index; --i) { + cmd[i] = cmd[i - 1]; + } + } + // Place the new character. + cmd[cmd_cursor_index++] = c; + return 1; + } + return 0; +} + +static inline void __cmd_sug(dirent_t *suggestion, size_t starting_position) +{ + if (suggestion) { + for (size_t i = starting_position; i < strlen(suggestion->d_name); ++i) { + if (__cmd_app(suggestion->d_name[i])) { + putchar(suggestion->d_name[i]); + } + } + // If we suggested a directory, append a slash. + if (suggestion->d_type == DT_DIR) { + if (__cmd_app('/')) { + putchar('/'); + } + } + } +} + +/// @brief Gets the inserted command. +static void __cmd_get() +{ + // Re-Initialize the cursor index. + cmd_cursor_index = 0; + // Initializing the current command line buffer + memset(cmd, '\0', CMD_LEN); + char cwd[PATH_MAX]; + getcwd(cwd, PATH_MAX); + __set_echo(false); + do { + int c = getchar(); + // Return Key + if (c == '\n') { + putchar('\n'); + // Break the while loop. + break; + } else if (c == '\033') { + c = getchar(); + if (c == '[') { + c = getchar(); // Get the char. + if ((c == 'A') || (c == 'B')) { + char *old_cmd = __hst_fetch(c == 'A'); + if (old_cmd != NULL) { + // Clear the current command. + __cmd_clr(); + // Sets the command. + __cmd_set(old_cmd); + } + } else if (c == 'D') { + if (cmd_cursor_index > 0) { + --cmd_cursor_index; + puts("\033[1D"); + } + } else if (c == 'C') { + if ((cmd_cursor_index + 1) < CMD_LEN && (cmd_cursor_index + 1) <= strlen(cmd)) { + ++cmd_cursor_index; + puts("\033[1C"); + } + } else if (c == 'H') { + // Move the cursor back to the beginning. + printf("\033[%dD", cmd_cursor_index); + // Reset the cursor position. + cmd_cursor_index = 0; + } else if (c == 'F') { + // Compute the offest to the end of the line, and move only if necessary. + size_t offset = strlen(cmd) - cmd_cursor_index; + if (offset > 0) { + printf("\033[%dC", offset); + // Reset the cursor position. + cmd_cursor_index += offset; + } + } + } else if (c == '^') { + c = getchar(); // Get the char. + if (c == 'C') { + // Re-set the index to the beginning. + cmd_cursor_index = 0; + // Go to the new line. + printf("\n\n"); + // Sets the command. + __cmd_set("\0"); + // Break the while loop. + break; + } else if (c == 'U') { + // Clear the current command. + __cmd_clr(); + // Re-set the index to the beginning. + cmd_cursor_index = 0; + // Sets the command. + __cmd_set("\0"); + } else if (c == 'D') { + // Go to the new line. + printf("\n"); + exit(0); + } + } + } else if (c == '\b') { + __cmd_ers(); + } else if (c == '\t') { + // Get the lenght of the command. + size_t cmd_len = strlen(cmd); + // Count the number of words. + int words = __count_words(cmd); + // If there are no words, skip. + if (words == 0) + continue; + // Determines if we are at the beginning of a new argument, last character is space. + if (__is_separator(cmd[cmd_len - 1])) + continue; + // If the last two characters are two dots `..` append a slash `/`, + // and continue. + if ((cmd_len >= 2) && ((cmd[cmd_len - 2] == '.') && (cmd[cmd_len - 1] == '.'))) { + if (__cmd_app('/')) { + putchar('/'); + continue; + } + } + // Determines if we are executing a command from current directory. + int is_run_cmd = (words == 1) && (cmd[0] == '.') && (cmd_len > 3) && (cmd[1] == '/'); + // Determines if we are entering an absolute path. + int is_abs_path = (words == 1) && (cmd[0] == '/'); + // Prepare the dirent variable. + dirent_t dent; + // If there is only one word, we are searching for a command. + if (is_run_cmd) { + if (__folder_contains(cwd, cmd + 2, 0, &dent)) + __cmd_sug(&dent, cmd_len - 2); + } else if (is_abs_path) { + const char *_dirname = dirname(cmd), *_basename = basename(cmd); + if (!_dirname || !_basename) + continue; + if ((*_dirname == 0) || (*_basename == 0)) + continue; + if (__folder_contains(_dirname, _basename, 0, &dent)) + __cmd_sug(&dent, strlen(_basename)); + } else if (words == 1) { + if (__search_in_path(cmd, &dent)) + __cmd_sug(&dent, cmd_len); + } else { + // Search the last occurrence of a space, from there on + // we will have the last argument. + char *last_argument = strrchr(cmd, ' '); + // We need to move ahead of one character if we found the space. + last_argument = last_argument ? last_argument + 1 : NULL; + // If there is no last argument. + if (last_argument == NULL) + continue; + const char *_dirname = dirname(last_argument), *_basename = basename(last_argument); + if (!_dirname || !_basename) + continue; + if ((*_dirname != 0) && (*_basename != 0)) { + if (__folder_contains(_dirname, _basename, 0, &dent)) + __cmd_sug(&dent, strlen(_basename)); + } else if (*_basename != 0) { + if (__folder_contains(cwd, _basename, 0, &dent)) + __cmd_sug(&dent, strlen(_basename)); + } + } + } else if (c == 127) { + if ((cmd_cursor_index + 1) <= strlen(cmd)) { + strcpy(cmd + cmd_cursor_index, cmd + cmd_cursor_index + 1); + putchar(127); + } + } else if ((c > 0) && (c != '\n')) { + if (__cmd_app(c)) { + putchar(c); + } + } + } while (cmd_cursor_index < CMD_LEN); + + // Cleans all blanks at the beginning of the command. + trim(cmd); + __set_echo(true); +} + +/// @brief Gets the options from the command. +/// @param command The executed command. +static void __cmd_opt(char *command, int *argc, char ***argv) +{ + // Get the number of arguments, return if zero. + if (((*argc) = __count_words(command)) == 0) { + return; + } + (*argv) = (char **)malloc(sizeof(char *) * ((*argc) + 1)); + bool_t inword = false; + const char *cit = command; + size_t argcIt = 0, argIt = 0; + do { + if (__is_separator(*cit)) { + if (inword) { + inword = false; + (*argv)[argcIt++][argIt] = '\0'; + argIt = 0; + } + } else { + // Allocate string for argument. + if (!inword) { + (*argv)[argcIt] = (char *)malloc(sizeof(char) * CMD_LEN); + } + inword = true; + (*argv)[argcIt][argIt++] = (*cit); + } + } while (*cit++); + (*argv)[argcIt] = NULL; +} + +void wait_for_child(int signum) +{ + wait(NULL); +} + +int main(int argc, char *argv[]) +{ + setsid(); + + char *USER = getenv("USER"); + if (USER == NULL) { + printf("shell: There is no user set.\n"); + return 1; + } + if (getenv("PATH") == NULL) { + if (setenv("PATH", "/bin:/usr/bin", 0) == -1) { + printf("shell: Failed to set PATH.\n"); + return 1; + } + } + // Set the signal handler to handle the termination of the child. + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = wait_for_child; + if (sigaction(SIGCHLD, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGCHLD, strerror(errno)); + return 1; + } + // Clear the screen. + puts("\033[J"); + // Welcome the user. + puts(BG_WHITE FG_BLACK); + printf("Welcome " FG_RED "%s" FG_BLACK "...\n\n", USER); + puts(BG_BLACK FG_BRIGHT_WHITE); + // Move inside the home directory. + __cd(0, NULL); + +#pragma clang diagnostic push +#pragma ide diagnostic ignored "EndlessLoop" + while (true) { + // First print the prompt. + __prompt_print(); + + // Get the input command. + __cmd_get(); + + // Check if the command is empty. + if (strlen(cmd) <= 0) { + continue; + } + + // Retrieve the options from the command. + // The current number of arguments. + int _argc = 1; + // The vector of arguments. + char **_argv; + __cmd_opt(cmd, &_argc, &_argv); + // Check if the command is empty. + if (_argc == 0) { + continue; + } + + // Add the command to the history. + __hst_push(cmd); + if (!strcmp(_argv[0], "init")) { + } else if (!strcmp(_argv[0], "cd")) { + __cd(_argc, _argv); + } else if (!strcmp(_argv[0], "..")) { + const char *__argv[] = { "cd", "..", NULL }; + __cd(2, (char **)__argv); + } else if (!strcmp(_argv[0], "export")) { + __export(_argc, _argv); + } else { + bool_t blocking = true; + if (strcmp(_argv[_argc - 1], "&") == 0) { + free(_argv[_argc - 1]); + _argv[_argc - 1] = NULL; + blocking = false; + } + // Is a shell path, execute it! + int status; + pid_t cpid = fork(); + if (cpid == 0) { + // Makes the new process a group leader + pid_t pid = getpid(); + setgid(pid); + + if (execvp(_argv[0], _argv) == -1) { + printf("\nUnknown command: %s\n", _argv[0]); + exit(1); + } + } + if (blocking) { + waitpid(cpid, &status, 0); + } + } + // Free up the memory reserved for the arguments. + for (int it = 0; it < _argc; ++it) { + // Check if the argument is not empty. + if (_argv[it] != NULL) { + // Free up its memory. + free(_argv[it]); + } + } + } +#pragma clang diagnostic pop + return 0; +} diff --git a/programs/showpid.c b/programs/showpid.c new file mode 100644 index 0000000..a621164 --- /dev/null +++ b/programs/showpid.c @@ -0,0 +1,14 @@ +/// MentOS, The Mentoring Operating system project +/// @file showpid.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include + +int main(int argc, char **argv) +{ + printf("pid %d\n", getppid()); + return 0; +} diff --git a/programs/sleep.c b/programs/sleep.c new file mode 100644 index 0000000..446c013 --- /dev/null +++ b/programs/sleep.c @@ -0,0 +1,19 @@ +/// MentOS, The Mentoring Operating system project +/// @file sleep.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include + +int main(int argc, char **argv) +{ + if (argc == 2) { + int amount = atoi(argv[1]); + if (amount > 0) { + sleep(amount); + } + } + return 0; +} diff --git a/programs/tests/CMakeLists.txt b/programs/tests/CMakeLists.txt new file mode 100644 index 0000000..16f00fb --- /dev/null +++ b/programs/tests/CMakeLists.txt @@ -0,0 +1,126 @@ +# ============================================================================= +# Author: Enrico Fraccaroli +# ============================================================================= +# Set the minimum required version of cmake. +cmake_minimum_required(VERSION 2.8) +# Initialize the project. +project(tests C) + +# ============================================================================= +# Set the default build type to Debug. +if(NOT CMAKE_BUILD_TYPE) + message(STATUS "Setting build type to 'Debug' as none was specified.") + set(CMAKE_BUILD_TYPE + "Debug" + CACHE STRING "Choose the type of build." FORCE) +endif() + +# ============================================================================= +# Set the directory where the compiled binaries will be placed. +set(MENTOS_TESTS_DIR ${CMAKE_SOURCE_DIR}/files/bin/tests) + +# ============================================================================= +# Warning flags. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wpedantic") +#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic-errors") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-function") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-variable") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unknown-pragmas") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-but-set-variable") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") +# Set the compiler options. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -u_start") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdlib") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -nostdinc") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-builtin") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32") +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g3") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0") +elseif(CMAKE_BUILD_TYPE STREQUAL "Release") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3") +endif(CMAKE_BUILD_TYPE STREQUAL "Debug") +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -no-pie -Wl,-z,norelro") + +# ============================================================================= +# Add the executables (manually). +set(TESTS + t_mem.c + t_fork.c + # Real-time programs + t_periodic1.c + t_periodic2.c + t_periodic3.c + # Exec + t_exec.c + t_exec_callee.c + # Test environmental variables. + t_setenv.c + t_getenv.c + # Test: signals. + t_stopcont.c + t_sigusr.c + t_abort.c + t_sigfpe.c + t_siginfo.c + t_sleep.c + t_sigaction.c + t_sigmask.c + t_groups.c + t_alarm.c + t_kill.c + t_itimer.c + t_gid.c) + +foreach(TEST ${TESTS}) + # Prepare the program name. + string(REPLACE ".c" "" TEST_NAME ${TEST}) + # Set the name of the target. + set(TARGET_NAME test_${TEST_NAME}) + + # Log the entry. + message(VERBOSE "Test ${TEST_NAME} (source: ${TEST}, make target: ${TARGET_NAME})") + + # Randomize .text section address so when debugging symbols don't clash The allowed range is from 256MB to 2.75GB Minimum allowed address: 0x10000000 Max allowed address: + # 0xB0000000 + string(MD5 RAND_HASH ${TEST}) + string(SUBSTRING ${RAND_HASH} 1 3 TEXADDR_INFIX) + string( + RANDOM + LENGTH 1 + ALPHABET 0123456789AB + RANDOM_SEED ${RAND_HASH} TEXADDR_FIRST) + + # Create the target. + add_executable(${TARGET_NAME} ${TEST}) + + # Add the includes. + target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/libc/inc) + + # Link the libc library. + target_link_libraries(${TARGET_NAME} ${CMAKE_BINARY_DIR}/libc/libc.a) + + # Add the dependency to libc. + add_dependencies(${TARGET_NAME} libc) + + # Add the linking properties. + set_target_properties(${TARGET_NAME} PROPERTIES LINK_FLAGS "-Wl,-Ttext=0x${TEXADDR_FIRST}${TEXADDR_INFIX}0000,-e_start") + + # Add the final target that strips the debugging symbols from the program. + add_custom_target( + ${TEST_NAME} + BYPRODUCTS ${MENTOS_TESTS_DIR}/${TEST_NAME} + COMMAND mkdir -p ${MENTOS_TESTS_DIR} + COMMAND strip --strip-debug ${PROJECT_BINARY_DIR}/${TARGET_NAME} -o ${MENTOS_TESTS_DIR}/${TEST_NAME} + DEPENDS ${TARGET_NAME}) + + # Append the program name to the list of all the executables. + list(APPEND ALL_TESTS ${TEST_NAME}) +endforeach() + +# Add the overall target that builds all the programs. +add_custom_target(all_tests ALL DEPENDS ${ALL_TESTS}) diff --git a/programs/tests/t_abort.c b/programs/tests/t_abort.c new file mode 100644 index 0000000..518010b --- /dev/null +++ b/programs/tests/t_abort.c @@ -0,0 +1,46 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_abort.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include +#include + +void sig_handler(int sig) +{ + printf("handler(%d) : Starting handler.\n", sig); + if (sig == SIGABRT) { + + static int counter = 0; + counter += 1; + + printf("handler(%d) : Correct signal. ABRT (%d/3)\n", sig, counter); + if (counter < 3) + abort(); + + } else { + printf("handler(%d) : Wrong signal.\n", sig); + } + printf("handler(%d) : Ending handler.\n", sig); +} + +int main(int argc, char *argv[]) +{ + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = sig_handler; + + if (sigaction(SIGABRT, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGABRT, strerror(errno)); + return 1; + } + + abort(); +} \ No newline at end of file diff --git a/programs/tests/t_alarm.c b/programs/tests/t_alarm.c new file mode 100644 index 0000000..3633a9d --- /dev/null +++ b/programs/tests/t_alarm.c @@ -0,0 +1,51 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_alarm.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include + +void alarm_handler(int sig) +{ + printf("handler(%d) : Starting handler.\n", sig); + if (sig == SIGALRM) { + printf("handler(%d) : Correct signal.\n", sig); + + alarm(5); + int rest = alarm(5); + printf("handler(%d) : alarm(5) result: %d.\n", sig, rest); + + rest = alarm(0); + printf("handler(%d) : alarm(0) result: %d.\n", sig, rest); + + exit(0); + + } else { + printf("handler(%d) : Wrong signal.\n", sig); + } + printf("handler(%d) : Ending handler.\n", sig); +} + +int main(int argc, char *argv[]) +{ + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = alarm_handler; + if (sigaction(SIGALRM, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGALRM, strerror(errno)); + return 1; + } + + + alarm(5); + while(1) { } + + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_exec.c b/programs/tests/t_exec.c new file mode 100644 index 0000000..821680a --- /dev/null +++ b/programs/tests/t_exec.c @@ -0,0 +1,65 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_exec.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include + +static inline void __print_usage(int argc, char *argv[]) +{ + if (argc > 0) { + printf("%s: Usage: %s \n", argv[0], argv[0]); + printf("exec_type: execl, execlp, execle, execlpe, execv, execvp, execve, execvpe\n"); + } +} + +int main(int argc, char *argv[]) +{ + int status; + if (argc != 2) { + __print_usage(argc, argv); + return 1; + } + + if (setenv("ENV_VAR", "pwd0", 0) == -1) { + printf("Failed to set env: `ENV_VAR`\n"); + return 1; + } + + char *file = "echo"; + char *path = "/bin/echo"; + char *exec_argv[] = { "echo", "ENV_VAR: ${ENV_VAR}", NULL }; + char *exec_envp[] = { "PATH=/bin", "ENV_VAR=pwd1", NULL }; + + if (fork() == 0) { + if (strcmp(argv[1], "execl") == 0) { + execl(path, "echo", "ENV_VAR: ${ENV_VAR}", NULL); + } else if (strcmp(argv[1], "execlp") == 0) { + execlp(file, "echo", "ENV_VAR: ${ENV_VAR}", NULL); + } else if (strcmp(argv[1], "execle") == 0) { + execle(path, "echo", "ENV_VAR: ${ENV_VAR}", exec_envp, NULL); + } else if (strcmp(argv[1], "execlpe") == 0) { + execlpe(file, "echo", "ENV_VAR: ${ENV_VAR}", exec_envp, NULL); + } else if (strcmp(argv[1], "execv") == 0) { + execv(path, exec_argv); + } else if (strcmp(argv[1], "execvp") == 0) { + execvp(file, exec_argv); + } else if (strcmp(argv[1], "execve") == 0) { + execve(path, exec_argv, exec_envp); + } else if (strcmp(argv[1], "execvpe") == 0) { + execvpe(file, exec_argv, exec_envp); + } else { + __print_usage(argc, argv); + return 1; + } + printf("Exec failed.\n"); + return 1; + } + wait(&status); + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_exec_callee.c b/programs/tests/t_exec_callee.c new file mode 100644 index 0000000..1f31572 --- /dev/null +++ b/programs/tests/t_exec_callee.c @@ -0,0 +1,19 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_exec_callee.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include + +int main(int argc, char *argv[]) +{ + char *ENV_VAR = getenv("ENV_VAR"); + if (ENV_VAR == NULL) { + printf("Failed to get env: `ENV_VAR`\n"); + return 1; + } + printf("ENV_VAR = %s\n", ENV_VAR); + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_fork.c b/programs/tests/t_fork.c new file mode 100644 index 0000000..744fc4e --- /dev/null +++ b/programs/tests/t_fork.c @@ -0,0 +1,66 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_fork.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + if (argc != 2) { + return 1; + } + char *ptr; + int N; + N = strtol(argv[1], &ptr, 10); + pid_t cpid, mypid; +#if 0 + char buffer[256]; + + N = strtol(argv[1], &ptr, 10); + + char *_argv[] = { + "/bin/tests/t_fork", + itoa(buffer, N - 1, 10), + NULL + }; + + printf("N = %d\n", N); + if (N > 1) { + + printf("Keep caling (N = %d)\n", N); + + if ((cpid = fork()) == 0) { + + printf("I'm the child (pid = %d, N = %d)!\n", getpid(), N); + + execv(_argv[0], _argv); + + } + printf("Will wait for %d\n", N, cpid); + while (wait(NULL) != -1) continue; + } +#else + while (1) { + mypid = getpid(); + if (N > 0) { + if ((cpid = fork()) == 0) { + N -= 1; + continue; + } + printf("I'm %d and I will wait for %d (N = %d)!\n", mypid, cpid, N); + while (wait(NULL) != -1) continue; + printf("I'm %d and I waited for %d (N = %d)!\n", mypid, cpid, N); + } else { + printf("I'm %d and I will not wait!\n", mypid); + } + break; + } +#endif + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_getenv.c b/programs/tests/t_getenv.c new file mode 100644 index 0000000..5a068ab --- /dev/null +++ b/programs/tests/t_getenv.c @@ -0,0 +1,21 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_getenv.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + char *ENV_VAR = getenv("ENV_VAR"); + if (ENV_VAR == NULL) { + printf("Failed to get env: `ENV_VAR`\n"); + return 1; + } + printf("ENV_VAR = %s\n", ENV_VAR); + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_gid.c b/programs/tests/t_gid.c new file mode 100644 index 0000000..a03c6c8 --- /dev/null +++ b/programs/tests/t_gid.c @@ -0,0 +1,53 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_gid.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include "grp.h" +#include "stdlib.h" +#include "string.h" +#include + +static void list_groups() { + + group* iter; + while ((iter = getgrent()) != NULL) { + printf("Group\n\tname: %s\n\tpasswd: %s\n\tnames:\n",iter->gr_name, iter->gr_passwd); + + size_t count = 0; + while (iter->gr_mem[count] != NULL) { + printf("\t\t%s\n",iter->gr_mem[count]); + count += 1; + } + + printf("\n"); + } +} + +int main(int argc, char** argv) { + + printf("List of all groups:\n"); + + list_groups(); + setgrent(); + + printf("List all groups again:\n"); + + list_groups(); + endgrent(); + + group* root_group = getgrgid(0); + if (strcmp(root_group->gr_name, "root") != 0) { + printf("Error in getgrgid function."); + return 1; + } + + root_group = getgrnam("root"); + if (root_group->gr_gid != 0) { + printf("Error in getgrnam function."); + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_groups.c b/programs/tests/t_groups.c new file mode 100644 index 0000000..eba4f20 --- /dev/null +++ b/programs/tests/t_groups.c @@ -0,0 +1,39 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_groups.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + pid_t gid = getgid(); + pid_t pid = getpid(); + pid_t sid = getsid(0); + + printf("pid: %d, gid: %d, sid: %d\n\n", pid, gid, sid); + for (int i = 0; i < 5; ++i) { + if (fork() == 0) { + pid_t gid_child = getgid(); + pid_t pid_child = getpid(); + + pid_t ppid_child = getppid(); + pid_t sid_child = getsid(ppid_child); + + printf("%d) pid_child: %d, gid_child: %d, ppid_child: %d, sid_child: %d\n", + i, pid_child, gid_child, ppid_child, sid_child); + sleep(5); + exit(0); + } + } + //int status; + //while (wait(&status) > 0) + // ; + + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_itimer.c b/programs/tests/t_itimer.c new file mode 100644 index 0000000..6ca2b84 --- /dev/null +++ b/programs/tests/t_itimer.c @@ -0,0 +1,62 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_itimer.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include +#include + +void alarm_handler(int sig) +{ + printf("handler(%d) : Starting handler.\n", sig); + if (sig == SIGALRM) { + + itimerval val = { 0 }; + getitimer(ITIMER_REAL, &val); + printf("(sec: %d, usec: %d)\n", val.it_interval.tv_sec, val.it_interval.tv_usec); + + static int counter = 0; + counter += 1; + + printf("handler(%d) : Correct signal x%d\n", sig, counter); + if (counter == 4) + { + itimerval interval = { 0 }, prev = { 0 }; + interval.it_interval.tv_sec = 0; + + setitimer(ITIMER_REAL, &interval, &prev); + printf("prev: (sec: %d, usec: %d)", prev.it_interval.tv_sec, prev.it_interval.tv_usec); + + exit(0); + } + + } else { + printf("handler(%d) : Wrong signal.\n", sig); + } + printf("handler(%d) : Ending handler.\n", sig); +} + +int main(int argc, char *argv[]) +{ + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = alarm_handler; + if (sigaction(SIGALRM, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGALRM, strerror(errno)); + return 1; + } + + itimerval interval = { 0 }; + interval.it_interval.tv_sec = 4; + setitimer(ITIMER_REAL, &interval, NULL); + + while(1) { } + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_kill.c b/programs/tests/t_kill.c new file mode 100644 index 0000000..98d08a8 --- /dev/null +++ b/programs/tests/t_kill.c @@ -0,0 +1,66 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_kill.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include + +#define cpu_relax() asm volatile("pause\n" \ + : \ + : \ + : "memory") + +static inline void fake_sleep(int times) +{ + for (int j = 0; j < times; ++j) + for (long i = 1; i < 1024000; ++i) + cpu_relax(); +} + +void child_sigusr1_handler(int sig) +{ + printf("handler(sig: %d) : Starting handler (pid: %d).\n", sig, getpid()); + printf("handler(sig: %d) : Ending handler (pid: %d).\n", sig, getpid()); +} + +void child_process() +{ + while (1) { + printf("I'm the child (%d): I'm playing around!\n", getpid()); + fake_sleep(1); + } +} + +int main(int argc, char *argv[]) +{ + printf("main : Creating child!\n"); + pid_t ppid; + if ((ppid = fork()) == 0) { + printf("I'm the child (%d)!\n", ppid); + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = child_sigusr1_handler; + if (sigaction(SIGUSR1, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGUSR1, strerror(errno)); + return 1; + } + child_process(); + } else { + printf("I'm the parent (%d)!\n", ppid); + } + fake_sleep(9); + kill(ppid, SIGUSR1); + fake_sleep(9); + kill(ppid, SIGTERM); + int status; + wait(&status); + printf("main : end (%d)\n", status); + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_mem.c b/programs/tests/t_mem.c new file mode 100644 index 0000000..ab683ba --- /dev/null +++ b/programs/tests/t_mem.c @@ -0,0 +1,26 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_mem.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + if (argc != 2) { + return 1; + } + char *ptr; + int N = strtol(argv[1], &ptr, 10), *V; + for (int i = 0; i < N; ++i) { + for (int j = 1; j < N; ++j) { + V = (int *)malloc(j * sizeof(int)); + free(V); + } + } + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_periodic1.c b/programs/tests/t_periodic1.c new file mode 100644 index 0000000..07dfde3 --- /dev/null +++ b/programs/tests/t_periodic1.c @@ -0,0 +1,47 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_periodic1.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + pid_t cpid = getpid(); + sched_param_t param; + // Get current parameters. + sched_getparam(cpid, ¶m); + // Change parameters. + param.period = 5000; + param.deadline = 5000; + param.is_periodic = true; + // Set modified parameters. + sched_setparam(cpid, ¶m); + int counter = 0; + if (fork() == 0) { + char *_argv[] = { "/bin/periodic2", NULL }; + execv(_argv[0], _argv); + printf("This is bad, I should not be here! EXEC NOT WORKING\n"); + } + if (fork() == 0) { + char *_argv[] = { "/bin/periodic3", NULL }; + execv(_argv[0], _argv); + printf("This is bad, I should not be here! EXEC NOT WORKING\n"); + } + + while (1) { + if (++counter == 10) { + counter = 0; + } + printf("[periodic1]counter %d\n",counter); + if (waitperiod() == -1) { + printf("[%s] Error in waitperiod: %s\n", argv[0], strerror(errno)); + break; + } + } + return 0; +} diff --git a/programs/tests/t_periodic2.c b/programs/tests/t_periodic2.c new file mode 100644 index 0000000..61d4bdb --- /dev/null +++ b/programs/tests/t_periodic2.c @@ -0,0 +1,35 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_periodic2.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + pid_t cpid = getpid(); + sched_param_t param; + // Get current parameters. + sched_getparam(cpid, ¶m); + // Change parameters. + param.period = 4000; + param.deadline = 4000; + param.is_periodic = true; + // Set modified parameters. + sched_setparam(cpid, ¶m); + int counter = 0; + while (1) { + if (++counter == 10) + break; + printf("[priodic2] counter: %d\n", counter); + if (waitperiod() == -1) { + printf("[%s] Error in waitperiod: %s\n", argv[0], strerror(errno)); + break; + } + } + return 0; +} diff --git a/programs/tests/t_periodic3.c b/programs/tests/t_periodic3.c new file mode 100644 index 0000000..41e5cac --- /dev/null +++ b/programs/tests/t_periodic3.c @@ -0,0 +1,35 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_periodic3.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + pid_t cpid = getpid(); + sched_param_t param; + // Get current parameters. + sched_getparam(cpid, ¶m); + // Change parameters. + param.period = 3000; + param.deadline = 3000; + param.is_periodic = true; + // Set modified parameters. + sched_setparam(cpid, ¶m); + int counter = 0; + while (1) { + if (++counter == 10) + break; + printf("[periodic3] counter: %d\n", counter); + if (waitperiod() == -1) { + printf("[%s] Error in waitperiod: %s\n", argv[0], strerror(errno)); + break; + } + } + return 0; +} diff --git a/programs/tests/t_setenv.c b/programs/tests/t_setenv.c new file mode 100644 index 0000000..3eb0dea --- /dev/null +++ b/programs/tests/t_setenv.c @@ -0,0 +1,29 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_setenv.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + char *_argv[] = { "/bin/tests/t_getenv", NULL }; + int status; + + if (setenv("ENV_VAR", "pwd0", 0) == -1) { + printf("Failed to set env: `PWD`\n"); + return 1; + } + + if (fork() == 0) { + execv(_argv[0], _argv); + printf("This is bad, I should not be here! EXEC NOT WORKING\n"); + } + + wait(&status); + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_sigaction.c b/programs/tests/t_sigaction.c new file mode 100644 index 0000000..c263bd3 --- /dev/null +++ b/programs/tests/t_sigaction.c @@ -0,0 +1,49 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_sigaction.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include + +static int *values = NULL; + +void sigusr1_handler(int sig) +{ + printf("handler(sig: %d) : Starting handler.\n", sig); + printf("handler(sig: %d) : value : %d\n", sig, values); + values = malloc(sizeof(int) * 4); + for (int i = 0; i < 4; ++i) { + values[i] = i; + printf("values[%d] : `%d`\n", i, values[i]); + } + printf("handler(sig: %d) : value : %d\n", sig, values); + printf("handler(sig: %d) : Ending handler.\n", sig); +} + +int main(int argc, char *argv[]) +{ + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = sigusr1_handler; + if (sigaction(SIGUSR1, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGUSR1, strerror(errno)); + return 1; + } + + printf("main : Calling handler (%d).\n", SIGUSR1); + printf("main : value : %d\n", values); + int ret = kill(getpid(), SIGUSR1); + printf("main : Returning from handler (%d): %d.\n", SIGUSR1, ret); + printf("main : value : %d\n", values); + for (int i = 0; i < 4; ++i) + printf("values[%d] : `%d`\n", i, values[i]); + free(values); + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_sigfpe.c b/programs/tests/t_sigfpe.c new file mode 100644 index 0000000..26e4981 --- /dev/null +++ b/programs/tests/t_sigfpe.c @@ -0,0 +1,50 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_sigfpe.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include +#include + + +void sig_handler(int sig) +{ + printf("handler(%d) : Starting handler.\n", sig); + if (sig == SIGFPE) { + printf("handler(%d) : Correct signal. FPE\n", sig); + printf("handler(%d) : Exiting\n", sig); + exit(1); + + } else { + printf("handler(%d) : Wrong signal.\n", sig); + } + printf("handler(%d) : Ending handler.\n", sig); +} + +int main(int argc, char *argv[]) +{ + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = sig_handler; + + if (sigaction(SIGFPE, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGFPE, strerror(errno)); + return 1; + } + + printf("Diving by zero (unrecoverable)...\n"); + + // Should trigger ALU error, fighting the compiler... + int d = 1, e = 1; + while (1) { + d /= e; + e -= 1; + } +} \ No newline at end of file diff --git a/programs/tests/t_siginfo.c b/programs/tests/t_siginfo.c new file mode 100644 index 0000000..9479835 --- /dev/null +++ b/programs/tests/t_siginfo.c @@ -0,0 +1,51 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_siginfo.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include +#include + +void sig_handler_info(int sig, siginfo_t *siginfo) +{ + printf("handler(%d, %p) : Starting handler.\n", sig, siginfo); + if (sig == SIGFPE) { + printf("handler(%d, %p) : Correct signal.\n", sig, siginfo); + printf("handler(%d, %p) : Code : %d\n", sig, siginfo, siginfo->si_code); + printf("handler(%d, %p) : Exiting\n", sig, siginfo); + exit(1); + } else { + printf("handler(%d, %p) : Wrong signal.\n", sig, siginfo); + } + printf("handler(%d, %p) : Ending handler.\n", sig, siginfo); +} + +int main(int argc, char *argv[]) +{ + sigaction_t action; + memset(&action, 0, sizeof(action)); + + action.sa_handler = (sighandler_t)sig_handler_info; + action.sa_flags = SA_SIGINFO; + + if (sigaction(SIGFPE, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGFPE, strerror(errno)); + return 1; + } + + printf("Diving by zero (unrecoverable)...\n"); + + // Should trigger ALU error, fighting the compiler... + int d = 1, e = 1; + while (1) { + d /= e; + e -= 1; + } +} \ No newline at end of file diff --git a/programs/tests/t_sigmask.c b/programs/tests/t_sigmask.c new file mode 100644 index 0000000..24f37bb --- /dev/null +++ b/programs/tests/t_sigmask.c @@ -0,0 +1,50 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_sigmask.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include + +void sigusr1_handler(int sig) +{ + printf("handler(sig: %d) : Starting handler.\n", sig); + printf("handler(sig: %d) : Ending handler.\n", sig); +} + +int main(int argc, char *argv[]) +{ + int ret; + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = sigusr1_handler; + if (sigaction(SIGUSR1, &action, NULL) == -1) { + printf("Failed to set signal handler (%d, %s).\n", SIGUSR1, strerror(errno)); + return 1; + } + + printf("main : Blocking signal (%d).\n", SIGUSR1); + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + sigprocmask(SIG_BLOCK, &mask, NULL); + + printf("main : Calling handler (%d).\n", SIGUSR1); + ret = kill(getpid(), SIGUSR1); + printf("main : Returning from handler (%d): %d.\n", SIGUSR1, ret); + + printf("main : Unblocking signal (%d).\n", SIGUSR1); + sigprocmask(SIG_UNBLOCK, &mask, NULL); + + printf("main : Calling handler (%d).\n", SIGUSR1); + ret = kill(getpid(), SIGUSR1); + printf("main : Returning from handler (%d): %d.\n", SIGUSR1, ret); + + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_sigusr.c b/programs/tests/t_sigusr.c new file mode 100644 index 0000000..f9672b4 --- /dev/null +++ b/programs/tests/t_sigusr.c @@ -0,0 +1,56 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_sigusr.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include +#include + +void sig_handler(int sig) +{ + printf("handler(%d) : Starting handler.\n", sig); + + static int counter = 0; + if (sig == SIGUSR1 || sig == SIGUSR2) { + printf("handler(%d) : Correct signal. SIGUSER\n", sig); + counter += 1; + + if (counter == 2) + exit(1); + + } else { + printf("handler(%d) : Wrong signal.\n", sig); + } + printf("handler(%d) : Ending handler.\n", sig); +} + +int main(int argc, char *argv[]) +{ + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = sig_handler; + + if (sigaction(SIGUSR1, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGUSR1, strerror(errno)); + return 1; + } + + if (sigaction(SIGUSR2, &action, NULL) == -1) { + printf("Failed to set signal handler (%s).\n", SIGUSR2, strerror(errno)); + return 1; + } + + kill(getpid(), SIGUSR1); + sleep(2); + kill(getpid(), SIGUSR2); + + while(1) { }; + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_sleep.c b/programs/tests/t_sleep.c new file mode 100644 index 0000000..2193fc3 --- /dev/null +++ b/programs/tests/t_sleep.c @@ -0,0 +1,22 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_sleep.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + printf("Sleeping for 4 seconds... "); + sleep(4); + printf("COMPLETED.\n"); + + return 0; +} \ No newline at end of file diff --git a/programs/tests/t_stopcont.c b/programs/tests/t_stopcont.c new file mode 100644 index 0000000..f666e07 --- /dev/null +++ b/programs/tests/t_stopcont.c @@ -0,0 +1,60 @@ +/// MentOS, The Mentoring Operating system project +/// @file t_stopcont.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include +#include +#include +#include "stdlib.h" +#include + +int child_pid; + +void wait_for_child(int signr) +{ + printf("Signal received: %s\n", strsignal(signr)); + sleep(10); + + printf("Sending continue sig to child\n"); + if (kill(child_pid, SIGCONT) == -1) + printf("Error sending signal\n"); +} + +int main(int argc, char *argv[]) +{ + sigaction_t action; + memset(&action, 0, sizeof(action)); + action.sa_handler = wait_for_child; + if (sigaction(SIGCHLD, &action, NULL) == -1) { + printf("Failed to set signal handler. %d\n", SIGCHLD); + return 1; + } + + child_pid = fork(); + if (child_pid != 0) { + printf("Child PID: %d\n", child_pid); + + sleep(2); + printf("Sending stop sig to child\n"); + +#if 1 + if (kill(child_pid, SIGSTOP) == -1) + printf("Errore invio stop\n"); +#endif + + wait(NULL); + } else { + int c = 0; + for (;;) { + printf("c: %d\n", c++); + sleep(3); + } + } + + return 0; +} \ No newline at end of file diff --git a/programs/touch.c b/programs/touch.c new file mode 100644 index 0000000..8f56bb5 --- /dev/null +++ b/programs/touch.c @@ -0,0 +1,37 @@ +/// MentOS, The Mentoring Operating system project +/// @file touch.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + if (argc != 2) { + printf("%s: missing operand.\n", argv[0]); + printf("Try '%s --help' for more information.\n\n", argv[0]); + return 1; + } + if (strcmp(argv[1], "--help") == 0) { + printf( + "Updates modification times of a given fine. If the does not" + "exists, it creates it.\n" + ); + printf("Usage:\n"); + printf(" touch \n"); + return 0; + } + int fd = open(argv[1], O_RDONLY, 0); + if (fd < 0) { + fd = open(argv[1], O_CREAT, 0); + if (fd >= 0) { + close(fd); + } + } + printf("\n"); + return 0; +} diff --git a/programs/uname.c b/programs/uname.c new file mode 100644 index 0000000..580d9d9 --- /dev/null +++ b/programs/uname.c @@ -0,0 +1,40 @@ +/// MentOS, The Mentoring Operating system project +/// @file uname.c +/// @brief +/// @copyright (c) 2014-2021 This file is distributed under the MIT License. +/// See LICENSE.md for details. + +#include +#include +#include + +int main(int argc, char** argv) +{ + utsname_t utsname; + uname(&utsname); + if (argc != 2) { + printf("%s\n", utsname.sysname); + return -1; + } + + if (!(strcmp(argv[1], "-a")) || !(strcmp(argv[1], "--all"))) { + printf("%s %s \n", utsname.sysname, utsname.version); + } else if (!(strcmp(argv[1], "-r")) || !(strcmp(argv[1], "--rev"))) { + printf("%s\n", utsname.version); + } else if (!(strcmp(argv[1], "-i")) || !(strcmp(argv[1], "--info"))) { + + } else if (!(strcmp(argv[1], "-h")) || !(strcmp(argv[1], "--help"))) { + printf("Uname function allow you to see the kernel and system information.\n"); + printf("Function avaibles:\n"); + printf( + "1) -a - Kernel version and processor type\n" + "2) -r - Only the kernel version\n" + "3) -i - All info of system and kernel\n" + ); + } else { + printf( + "%s. For more info about this tool, please do 'uname --help'\n", utsname.sysname + ); + } + return 0; +} diff --git a/scripts/get_text_address.sh b/scripts/get_text_address.sh new file mode 100644 index 0000000..68df330 --- /dev/null +++ b/scripts/get_text_address.sh @@ -0,0 +1,6 @@ +#!/bin/bash +for obj_file in "$@" +do + ADDR=$(readelf -WS $obj_file | grep .text | awk '{ print "0x"$5 }') + echo "add-symbol-file $obj_file ${ADDR}" +done \ No newline at end of file diff --git a/scripts/replace_links.sh b/scripts/replace_links.sh new file mode 100644 index 0000000..7a6d452 --- /dev/null +++ b/scripts/replace_links.sh @@ -0,0 +1,3 @@ +#!/bin/bash +find -type l -exec git update-index --assume-unchanged {} \; +find -type l -exec bash -c 'ln -f "$(readlink -m "$0")" "$0"' {} \; \ No newline at end of file