mirror of
https://github.com/EnderIce2/Fennix.git
synced 2025-05-25 22:14:34 +00:00
Merge remote-tracking branch 'Kernel/master'
This commit is contained in:
commit
682c84b2af
1
Kernel
1
Kernel
@ -1 +0,0 @@
|
||||
Subproject commit 19f2a78d357f48ec2c2cf2bd46ac35408b3965ec
|
91
Kernel/.gdbinit
Normal file
91
Kernel/.gdbinit
Normal file
@ -0,0 +1,91 @@
|
||||
# Usage: add-symbol-file-all <filename> [<offset>]
|
||||
# remove-symbol-file-all <filename> [<offset>]
|
||||
#
|
||||
# Credit: https://stackoverflow.com/a/33087762/9352057
|
||||
# CC BY-SA 4.0
|
||||
|
||||
python
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
def relocatesections(filename, addr):
|
||||
p = subprocess.Popen(["readelf", "-S", filename], stdout = subprocess.PIPE)
|
||||
|
||||
sections = []
|
||||
textaddr = '0'
|
||||
for line in p.stdout.readlines():
|
||||
line = line.decode("utf-8").strip()
|
||||
if not line.startswith('[') or line.startswith('[Nr]'):
|
||||
continue
|
||||
|
||||
line = re.sub(r' +', ' ', line)
|
||||
line = re.sub(r'\[ *(\d+)\]', '\g<1>', line)
|
||||
fieldsvalue = line.split(' ')
|
||||
fieldsname = ['number', 'name', 'type', 'addr', 'offset', 'size', 'entsize', 'flags', 'link', 'info', 'addralign']
|
||||
sec = dict(zip(fieldsname, fieldsvalue))
|
||||
|
||||
if sec['number'] == '0':
|
||||
continue
|
||||
|
||||
sections.append(sec)
|
||||
|
||||
if sec['name'] == '.text':
|
||||
textaddr = sec['addr']
|
||||
|
||||
return (textaddr, sections)
|
||||
|
||||
|
||||
class AddSymbolFileAll(gdb.Command):
|
||||
"""The right version for add-symbol-file"""
|
||||
|
||||
def __init__(self):
|
||||
super(AddSymbolFileAll, self).__init__("add-symbol-file-all", gdb.COMMAND_USER)
|
||||
self.dont_repeat()
|
||||
|
||||
def invoke(self, arg, from_tty):
|
||||
argv = gdb.string_to_argv(arg)
|
||||
filename = argv[0]
|
||||
|
||||
if len(argv) > 1:
|
||||
offset = int(str(gdb.parse_and_eval(argv[1])), 0)
|
||||
else:
|
||||
offset = 0
|
||||
|
||||
(textaddr, sections) = relocatesections(filename, offset)
|
||||
|
||||
cmd = "add-symbol-file %s 0x%08x" % (filename, int(textaddr, 16) + offset)
|
||||
|
||||
for s in sections:
|
||||
addr = int(s['addr'], 16)
|
||||
if s['name'] == '.text' or addr == 0:
|
||||
continue
|
||||
|
||||
cmd += " -s %s 0x%08x" % (s['name'], addr + offset)
|
||||
|
||||
gdb.execute(cmd)
|
||||
|
||||
class RemoveSymbolFileAll(gdb.Command):
|
||||
"""The right version for remove-symbol-file"""
|
||||
|
||||
def __init__(self):
|
||||
super(RemoveSymbolFileAll, self).__init__("remove-symbol-file-all", gdb.COMMAND_USER)
|
||||
self.dont_repeat()
|
||||
|
||||
def invoke(self, arg, from_tty):
|
||||
argv = gdb.string_to_argv(arg)
|
||||
filename = argv[0]
|
||||
|
||||
if len(argv) > 1:
|
||||
offset = int(str(gdb.parse_and_eval(argv[1])), 0)
|
||||
else:
|
||||
offset = 0
|
||||
|
||||
(textaddr, _) = relocatesections(filename, offset)
|
||||
|
||||
cmd = "remove-symbol-file -a 0x%08x" % (int(textaddr, 16) + offset)
|
||||
gdb.execute(cmd)
|
||||
|
||||
|
||||
AddSymbolFileAll()
|
||||
RemoveSymbolFileAll()
|
||||
end
|
36
Kernel/.github/workflows/flawfinder.yml
vendored
Normal file
36
Kernel/.github/workflows/flawfinder.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
# This workflow uses actions that are not certified by GitHub.
|
||||
# They are provided by a third-party and are governed by
|
||||
# separate terms of service, privacy policy, and support
|
||||
# documentation.
|
||||
|
||||
name: flawfinder
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
flawfinder:
|
||||
name: Flawfinder
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: flawfinder_scan
|
||||
uses: david-a-wheeler/flawfinder@8e4a779ad59dbfaee5da586aa9210853b701959c
|
||||
with:
|
||||
arguments: '--sarif ./'
|
||||
output: 'flawfinder_results.sarif'
|
||||
|
||||
- name: Upload analysis results to GitHub Security tab
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
with:
|
||||
sarif_file: ${{github.workspace}}/flawfinder_results.sarif
|
6
Kernel/.gitignore
vendored
Normal file
6
Kernel/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
*.o
|
||||
*.su
|
||||
*.gcno
|
||||
*.map
|
||||
fennix.elf
|
||||
*.log
|
100
Kernel/.vscode/c_boilerplates.code-snippets
vendored
Normal file
100
Kernel/.vscode/c_boilerplates.code-snippets
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
{
|
||||
"Fennix Kernel C++ Header": {
|
||||
"prefix": [
|
||||
"head",
|
||||
],
|
||||
"body": [
|
||||
"/*",
|
||||
"\tThis file is part of Fennix Kernel.",
|
||||
"",
|
||||
"\tFennix Kernel is free software: you can redistribute it and/or",
|
||||
"\tmodify it under the terms of the GNU General Public License as",
|
||||
"\tpublished by the Free Software Foundation, either version 3 of",
|
||||
"\tthe License, or (at your option) any later version.",
|
||||
"",
|
||||
"\tFennix Kernel is distributed in the hope that it will be useful,",
|
||||
"\tbut WITHOUT ANY WARRANTY; without even the implied warranty of",
|
||||
"\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the",
|
||||
"\tGNU General Public License for more details.",
|
||||
"",
|
||||
"\tYou should have received a copy of the GNU General Public License",
|
||||
"\talong with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.",
|
||||
"*/",
|
||||
"",
|
||||
"#pragma once",
|
||||
"",
|
||||
"$0",
|
||||
"",
|
||||
"$1",
|
||||
""
|
||||
],
|
||||
"description": "Create kernel header."
|
||||
},
|
||||
"Fennix Kernel C Header": {
|
||||
"prefix": [
|
||||
"headc",
|
||||
],
|
||||
"body": [
|
||||
"/*",
|
||||
"\tThis file is part of Fennix Kernel.",
|
||||
"",
|
||||
"\tFennix Kernel is free software: you can redistribute it and/or",
|
||||
"\tmodify it under the terms of the GNU General Public License as",
|
||||
"\tpublished by the Free Software Foundation, either version 3 of",
|
||||
"\tthe License, or (at your option) any later version.",
|
||||
"",
|
||||
"\tFennix Kernel is distributed in the hope that it will be useful,",
|
||||
"\tbut WITHOUT ANY WARRANTY; without even the implied warranty of",
|
||||
"\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the",
|
||||
"\tGNU General Public License for more details.",
|
||||
"",
|
||||
"\tYou should have received a copy of the GNU General Public License",
|
||||
"\talong with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.",
|
||||
"*/",
|
||||
"",
|
||||
"#ifndef __FENNIX_KERNEL_${1:header}_H__",
|
||||
"#define __FENNIX_KERNEL_${1:header}_H__",
|
||||
"",
|
||||
"#include <types.h>",
|
||||
"",
|
||||
"$0",
|
||||
"",
|
||||
"#endif // !__FENNIX_KERNEL_${1:header}_H__",
|
||||
""
|
||||
],
|
||||
"description": "Create kernel header."
|
||||
},
|
||||
"Fennix Kernel brief": {
|
||||
"prefix": [
|
||||
"brief",
|
||||
],
|
||||
"body": [
|
||||
"/** @brief $0 */"
|
||||
],
|
||||
"description": "Create kernel documentation brief."
|
||||
},
|
||||
"License": {
|
||||
"prefix": [
|
||||
"license",
|
||||
],
|
||||
"body": [
|
||||
"/*",
|
||||
"\tThis file is part of Fennix Kernel.",
|
||||
"",
|
||||
"\tFennix Kernel is free software: you can redistribute it and/or",
|
||||
"\tmodify it under the terms of the GNU General Public License as",
|
||||
"\tpublished by the Free Software Foundation, either version 3 of",
|
||||
"\tthe License, or (at your option) any later version.",
|
||||
"",
|
||||
"\tFennix Kernel is distributed in the hope that it will be useful,",
|
||||
"\tbut WITHOUT ANY WARRANTY; without even the implied warranty of",
|
||||
"\tMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the",
|
||||
"\tGNU General Public License for more details.",
|
||||
"",
|
||||
"\tYou should have received a copy of the GNU General Public License",
|
||||
"\talong with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.",
|
||||
"*/"
|
||||
],
|
||||
"description": "Create kernel license."
|
||||
}
|
||||
}
|
206
Kernel/.vscode/c_cpp_properties.json
vendored
Normal file
206
Kernel/.vscode/c_cpp_properties.json
vendored
Normal file
@ -0,0 +1,206 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Fennix x64 (Linux, GCC, debug)",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/include",
|
||||
"${workspaceFolder}/include/**",
|
||||
"${workspaceFolder}/include_std",
|
||||
"${workspaceFolder}/include_std/**",
|
||||
"${workspaceFolder}/arch/amd64/include"
|
||||
],
|
||||
"defines": [
|
||||
"a64",
|
||||
"a86",
|
||||
"DEBUG=\"1\""
|
||||
],
|
||||
"forcedInclude": [
|
||||
"${workspaceFolder}/.vscode/preinclude.h"
|
||||
],
|
||||
"compilerPath": "${workspaceFolder}/../tools/cross/bin/x86_64-fennix-gcc",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++20",
|
||||
"intelliSenseMode": "gcc-x64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools",
|
||||
"compilerArgs": [
|
||||
// Compiler flags
|
||||
"-fno-pic",
|
||||
"-fno-pie",
|
||||
"-mno-red-zone",
|
||||
"-march=core2",
|
||||
"-pipe",
|
||||
"-mcmodel=kernel",
|
||||
"-fno-builtin",
|
||||
"-m64",
|
||||
"-fcoroutines",
|
||||
// Warnings
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wfloat-equal",
|
||||
"-Wpointer-arith",
|
||||
"-Wcast-align",
|
||||
"-Wredundant-decls",
|
||||
"-Winit-self",
|
||||
"-Wswitch-default",
|
||||
"-Wstrict-overflow=5",
|
||||
"-Wconversion",
|
||||
// C++ flags
|
||||
"-fno-rtti",
|
||||
"-fno-exceptions",
|
||||
// Linker flags
|
||||
"-T${workspaceFolder}/arch/amd64/linker.ld",
|
||||
"-Wl,-static,--no-dynamic-linker,-ztext",
|
||||
"-nostdlib",
|
||||
"-nodefaultlibs",
|
||||
"-nolibc",
|
||||
"-zmax-page-size=0x1000",
|
||||
"-shared",
|
||||
// Debug flags
|
||||
"-ggdb3",
|
||||
"-O0",
|
||||
"-fdiagnostics-color=always",
|
||||
"-fverbose-asm",
|
||||
"-fstack-usage",
|
||||
"-fstack-check",
|
||||
"-fsanitize=undefined",
|
||||
// VSCode flags
|
||||
"-ffreestanding",
|
||||
"-nostdinc",
|
||||
"-nostdinc++"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Fennix x32 (Linux, GCC, debug)",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/include",
|
||||
"${workspaceFolder}/include/**",
|
||||
"${workspaceFolder}/include_std",
|
||||
"${workspaceFolder}/include_std/**",
|
||||
"${workspaceFolder}/arch/i386/include"
|
||||
],
|
||||
"forcedInclude": [
|
||||
"${workspaceFolder}/.vscode/preinclude.h"
|
||||
],
|
||||
"defines": [
|
||||
"a32",
|
||||
"a86",
|
||||
"DEBUG=\"1\""
|
||||
],
|
||||
"compilerPath": "${workspaceFolder}/../tools/cross/bin/i386-fennix-gcc",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++20",
|
||||
"intelliSenseMode": "gcc-x86",
|
||||
"configurationProvider": "ms-vscode.makefile-tools",
|
||||
"compilerArgs": [
|
||||
// Compiler flags
|
||||
"-fno-pic",
|
||||
"-fno-pie",
|
||||
"-mno-80387",
|
||||
"-mno-mmx",
|
||||
"-mno-3dnow",
|
||||
"-mno-red-zone",
|
||||
"-march=pentium",
|
||||
"-pipe",
|
||||
"-msoft-float",
|
||||
"-fno-builtin",
|
||||
"-m32",
|
||||
"-fcoroutines",
|
||||
// Warnings
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wfloat-equal",
|
||||
"-Wpointer-arith",
|
||||
"-Wcast-align",
|
||||
"-Wredundant-decls",
|
||||
"-Winit-self",
|
||||
"-Wswitch-default",
|
||||
"-Wstrict-overflow=5",
|
||||
"-Wconversion",
|
||||
// C++ flags
|
||||
"-fno-rtti",
|
||||
"-fno-exceptions",
|
||||
// Linker flags
|
||||
"-T${workspaceFolder}/arch/i386/linker.ld",
|
||||
"-Wl,-static,--no-dynamic-linker,-ztext",
|
||||
"-nostdlib",
|
||||
"-nodefaultlibs",
|
||||
"-nolibc",
|
||||
"-zmax-page-size=0x1000",
|
||||
"-shared",
|
||||
// Debug flags
|
||||
"-ggdb3",
|
||||
"-O0",
|
||||
"-fdiagnostics-color=always",
|
||||
"-fverbose-asm",
|
||||
"-fstack-usage",
|
||||
"-fstack-check",
|
||||
"-fsanitize=undefined",
|
||||
// VSCode flags
|
||||
"-ffreestanding",
|
||||
"-nostdinc",
|
||||
"-nostdinc++"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Fennix Aarch64 (Linux, GCC, debug)",
|
||||
"includePath": [
|
||||
"${workspaceFolder}/include",
|
||||
"${workspaceFolder}/include/**",
|
||||
"${workspaceFolder}/include_std",
|
||||
"${workspaceFolder}/include_std/**",
|
||||
"${workspaceFolder}/arch/aarch64/include"
|
||||
],
|
||||
"forcedInclude": [
|
||||
"${workspaceFolder}/.vscode/preinclude.h"
|
||||
],
|
||||
"defines": [
|
||||
"aa64",
|
||||
"DEBUG=\"1\""
|
||||
],
|
||||
"compilerPath": "${workspaceFolder}/../tools/cross/bin/aarch64-fennix-gcc",
|
||||
"cStandard": "c17",
|
||||
"cppStandard": "c++20",
|
||||
"intelliSenseMode": "linux-gcc-arm64",
|
||||
"configurationProvider": "ms-vscode.makefile-tools",
|
||||
"compilerArgs": [
|
||||
// Compiler flags
|
||||
"-pipe",
|
||||
"-fno-builtin",
|
||||
"-msoft-float",
|
||||
"-fPIC",
|
||||
"-Wstack-protector",
|
||||
"-fcoroutines",
|
||||
// Warnings
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wfloat-equal",
|
||||
"-Wpointer-arith",
|
||||
"-Wcast-align",
|
||||
"-Wredundant-decls",
|
||||
"-Winit-self",
|
||||
"-Wswitch-default",
|
||||
"-Wstrict-overflow=5",
|
||||
"-Wconversion",
|
||||
// C++ flags
|
||||
"-fno-rtti",
|
||||
"-fno-exceptions",
|
||||
// Linker flags
|
||||
"-T${workspaceFolder}/arch/aarch64/linker.ld",
|
||||
"-fPIC",
|
||||
// Debug flags
|
||||
"-ggdb3",
|
||||
"-O0",
|
||||
"-fdiagnostics-color=always",
|
||||
"-fverbose-asm",
|
||||
"-fstack-usage",
|
||||
"-fstack-check",
|
||||
"-fsanitize=undefined",
|
||||
// VSCode flags
|
||||
"-ffreestanding",
|
||||
"-nostdinc",
|
||||
"-nostdinc++"
|
||||
]
|
||||
}
|
||||
],
|
||||
"version": 4
|
||||
}
|
14
Kernel/.vscode/extensions.json
vendored
Normal file
14
Kernel/.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"jeff-hykin.better-cpp-syntax",
|
||||
"aaron-bond.better-comments",
|
||||
"ms-vscode.cpptools",
|
||||
"maziac.hex-hover-converter",
|
||||
"zixuanwang.linkerscript",
|
||||
"webfreak.debug",
|
||||
"ibm.output-colorizer",
|
||||
"wayou.vscode-todo-highlight",
|
||||
"gruntfuggly.todo-tree",
|
||||
"maziac.asm-code-lens"
|
||||
]
|
||||
}
|
175
Kernel/.vscode/launch.json
vendored
Normal file
175
Kernel/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,175 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch QEMU and debug the kernel",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/fennix.elf",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"args": [],
|
||||
"targetArchitecture": "x64",
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerPath": "${workspaceFolder}/../tools/cross/bin/x86_64-fennix-gdb",
|
||||
"miDebuggerArgs": "",
|
||||
"externalConsole": false,
|
||||
"additionalSOLibSearchPath": "${workspaceFolder}",
|
||||
"customLaunchSetupCommands": [
|
||||
{
|
||||
"text": "target remote localhost:1234",
|
||||
"description": "Connect to QEMU remote debugger"
|
||||
}
|
||||
],
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"text": "set breakpoint pending on",
|
||||
"description": "Make breakpoint pending on future shared library load."
|
||||
},
|
||||
{
|
||||
"text": "file ${workspaceFolder}/fennix.elf",
|
||||
"description": "Load binary."
|
||||
},
|
||||
{
|
||||
"text": "source ${workspaceFolder}/.gdbinit"
|
||||
}
|
||||
],
|
||||
"preLaunchTask": "launch-qemu"
|
||||
},
|
||||
{
|
||||
"name": "Attach to a running VM instance",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/fennix.elf",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"args": [],
|
||||
"targetArchitecture": "x64",
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerPath": "${workspaceFolder}/../tools/cross/bin/x86_64-fennix-gdb",
|
||||
"miDebuggerArgs": "",
|
||||
"externalConsole": false,
|
||||
"additionalSOLibSearchPath": "${workspaceFolder}",
|
||||
"customLaunchSetupCommands": [
|
||||
{
|
||||
"text": "target remote localhost:1234",
|
||||
"description": "Connect to QEMU remote debugger"
|
||||
}
|
||||
],
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"text": "set breakpoint pending on",
|
||||
"description": "Make breakpoint pending on future shared library load."
|
||||
},
|
||||
{
|
||||
"text": "file ${workspaceFolder}/fennix.elf",
|
||||
"description": "Load binary."
|
||||
},
|
||||
{
|
||||
"text": "source ${workspaceFolder}/.gdbinit"
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"name": "Attach to VM w/utest",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/fennix.elf",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"args": [],
|
||||
"targetArchitecture": "x64",
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerPath": "${workspaceFolder}/../tools/cross/bin/x86_64-fennix-gdb",
|
||||
"miDebuggerArgs": "",
|
||||
"externalConsole": false,
|
||||
"additionalSOLibSearchPath": "${workspaceFolder}",
|
||||
"customLaunchSetupCommands": [
|
||||
{
|
||||
"text": "target remote localhost:1234",
|
||||
"description": "Connect to QEMU remote debugger"
|
||||
}
|
||||
],
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"text": "set breakpoint pending on",
|
||||
"description": "Make breakpoint pending on future shared library load."
|
||||
},
|
||||
{
|
||||
"text": "file ${workspaceFolder}/fennix.elf",
|
||||
"description": "Load binary."
|
||||
},
|
||||
{
|
||||
"text": "add-symbol-file ${workspaceFolder}/../initrd_tmp_data/bin/utest",
|
||||
"description": "Load /bin/utest.",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"text": "source ${workspaceFolder}/.gdbinit"
|
||||
}
|
||||
],
|
||||
"preLaunchTask": "launch-qemu",
|
||||
},
|
||||
{
|
||||
"name": "Attach to VM w/utest_linux",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "${workspaceFolder}/fennix.elf",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"args": [],
|
||||
"targetArchitecture": "x64",
|
||||
"MIMode": "gdb",
|
||||
"miDebuggerPath": "${workspaceFolder}/../tools/cross/bin/x86_64-fennix-gdb",
|
||||
"miDebuggerArgs": "",
|
||||
"externalConsole": false,
|
||||
"additionalSOLibSearchPath": "${workspaceFolder}",
|
||||
"customLaunchSetupCommands": [
|
||||
{
|
||||
"text": "target remote localhost:1234",
|
||||
"description": "Connect to QEMU remote debugger"
|
||||
}
|
||||
],
|
||||
"setupCommands": [
|
||||
{
|
||||
"description": "Enable pretty-printing for gdb",
|
||||
"text": "-enable-pretty-printing",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"text": "set breakpoint pending on",
|
||||
"description": "Make breakpoint pending on future shared library load."
|
||||
},
|
||||
{
|
||||
"text": "file ${workspaceFolder}/fennix.elf",
|
||||
"description": "Load binary."
|
||||
},
|
||||
{
|
||||
"text": "set debug-file-directory /usr/lib/debug",
|
||||
"description": "Set debug-file-directory to /usr/lib/debug.",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"text": "add-symbol-file ${workspaceFolder}/../initrd_tmp_data/bin/utest_linux",
|
||||
"description": "Load /bin/utest_linux.",
|
||||
"ignoreFailures": true
|
||||
},
|
||||
{
|
||||
"text": "source ${workspaceFolder}/.gdbinit"
|
||||
}
|
||||
],
|
||||
"preLaunchTask": "launch-qemu",
|
||||
},
|
||||
]
|
||||
}
|
14
Kernel/.vscode/preinclude.h
vendored
Normal file
14
Kernel/.vscode/preinclude.h
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
#undef __linux__
|
||||
#undef __WIN32__
|
||||
#undef __WIN64__
|
||||
#undef _WIN32
|
||||
#undef _WIN64
|
||||
#undef __APPLE__
|
||||
#undef __clang__
|
||||
#define __vscode__ 1
|
||||
#define __kernel__ 1
|
||||
#define KERNEL_NAME "Fennix"
|
||||
#define KERNEL_ARCH "amd64"
|
||||
#define KERNEL_VERSION "1.0"
|
||||
#define GIT_COMMIT "0000000000000000000000000000000000000000"
|
||||
#define GIT_COMMIT_SHORT "0000000"
|
23
Kernel/.vscode/settings.json
vendored
Normal file
23
Kernel/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"C_Cpp.errorSquiggles": "enabled",
|
||||
"C_Cpp.autocompleteAddParentheses": true,
|
||||
"C_Cpp.codeAnalysis.clangTidy.enabled": true,
|
||||
"C_Cpp.clang_format_style": "Visual Studio",
|
||||
"C_Cpp.default.intelliSenseMode": "gcc-x64",
|
||||
"C_Cpp.default.cStandard": "c17",
|
||||
"C_Cpp.default.cppStandard": "c++20",
|
||||
"C_Cpp.intelliSenseMemoryLimit": 16384,
|
||||
"editor.smoothScrolling": true,
|
||||
"editor.cursorSmoothCaretAnimation": "on",
|
||||
"C_Cpp.codeAnalysis.clangTidy.checks.disabled": [
|
||||
"clang-analyzer-security.insecureAPI.strcpy",
|
||||
"clang-diagnostic-unknown-warning-option",
|
||||
"clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling",
|
||||
"clang-diagnostic-implicit-exception-spec-mismatch",
|
||||
"clang-diagnostic-unknown-attributes",
|
||||
"clang-diagnostic-user-defined-literals",
|
||||
"clang-diagnostic-non-pod-varargs",
|
||||
"clang-diagnostic-non-pod-varargs",
|
||||
"clang-diagnostic-non-pod-varargs"
|
||||
]
|
||||
}
|
26
Kernel/.vscode/tasks.json
vendored
Normal file
26
Kernel/.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "launch-qemu",
|
||||
"type": "shell",
|
||||
"command": "make -j$(shell nproc) build && make -C ../ build_image && make -C ../ vscode_debug_only",
|
||||
"isBackground": true,
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "shared"
|
||||
},
|
||||
"options": {
|
||||
"shell": {
|
||||
"executable": "bash",
|
||||
"args": ["-c"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
125
Kernel/CREDITS.md
Normal file
125
Kernel/CREDITS.md
Normal file
@ -0,0 +1,125 @@
|
||||
# Credits and References
|
||||
|
||||
This project has been influenced and inspired by other projects and resources.
|
||||
|
||||
License information can be found in the [LICENSES.md](LICENSES.md) file.
|
||||
|
||||
## General
|
||||
- [OSDev Wiki](https://wiki.osdev.org/Main_Page)
|
||||
- [GCC x86 Built-in Functions](https://gcc.gnu.org/onlinedocs/gcc/x86-Built-in-Functions.html#x86-Built-in-Functions)
|
||||
- [GCC Common Function Attributes](https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes)
|
||||
- [LemonOS Project](https://github.com/LemonOSProject/LemonOS)
|
||||
- [ToaruOS](https://github.com/klange/toaruos)
|
||||
- [Various SIMD functions](https://wiki.osdev.org/User:01000101/optlib/)
|
||||
|
||||
## C++ STL
|
||||
- [cppreference.com](https://cppreference.com)
|
||||
|
||||
## Virtual terminal
|
||||
- [vtconsole](https://github.com/sleepy-monax/vtconsole)
|
||||
|
||||
## Font
|
||||
- [Tamsyn Font](http://www.fial.com/~scott/tamsyn-font/)
|
||||
|
||||
## CPU XCR0 Structure
|
||||
- [CPU Registers x86 - XCR0](https://wiki.osdev.org/CPU_Registers_x86#XCR0)
|
||||
|
||||
## CPUID 0x7
|
||||
- [CPUID](https://en.wikipedia.org/wiki/CPUID)
|
||||
|
||||
## Network
|
||||
- [Beej's Guide to Network Programming](https://web.archive.org/web/20051210132103/http://users.pcnet.ro/dmoroian/beej/Beej.html)
|
||||
- [UDP Socket Programming](https://web.archive.org/web/20060229214053/http://www.cs.rutgers.edu/~pxk/417/notes/sockets/udp.html)
|
||||
- [EtherType](https://en.wikipedia.org/wiki/EtherType)
|
||||
- [Linux Network Packet Reception](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/performance_tuning_guide/s-network-packet-reception)
|
||||
- [Linux Kernel Networking Labs](https://linux-kernel-labs.github.io/refs/heads/master/labs/networking.html)
|
||||
- [smoltcp](https://github.com/smoltcp-rs/smoltcp)
|
||||
- [Understanding Linux Network Internals](https://www.cs.unh.edu/~cruse/cs326f04/RTL8139D_DataSheet.pdf)
|
||||
- [Address Resolution Protocol (ARP)](https://en.wikipedia.org/wiki/Address_Resolution_Protocol)
|
||||
- [C++ Operators](https://en.cppreference.com/w/cpp/language/operators)
|
||||
- [Dynamic Host Configuration Protocol (DHCP)](https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol)
|
||||
- [RTL8139 Programmer's Guide](https://www.cs.usfca.edu/~cruse/cs326f04/RTL8139_ProgrammersGuide.pdf)
|
||||
- [RTL8139CP Datasheet](http://realtek.info/pdf/rtl8139cp.pdf)
|
||||
- [IPv4](https://en.wikipedia.org/wiki/IPv4)
|
||||
- [ICMP Parameters](https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml)
|
||||
|
||||
## Loading ELF Shared Libraries and Dynamic Linking
|
||||
- [How To Write Shared Libraries](https://www.akkadia.org/drepper/dsohowto.pdf)
|
||||
- [Dynamic Linker](https://wiki.osdev.org/Dynamic_Linker)
|
||||
- [Nightingale OS](https://github.com/tyler569/nightingale)
|
||||
- [PLT and GOT: The Key to Code Sharing and Dynamic Libraries](https://www.technovelty.org/linux/plt-and-got-the-key-to-code-sharing-and-dynamic-libraries.html)
|
||||
- [YouTube Video on Dynamic Linking](https://www.youtube.com/watch?v=kUk5pw4w0h4)
|
||||
- [Oracle: Position Independent Code](https://docs.oracle.com/cd/E19683-01/817-3677/chapter6-42444/index.html)
|
||||
- [PLT and GOT Explained](https://ir0nstone.gitbook.io/notes/types/stack/aslr/plt_and_got)
|
||||
|
||||
## Inter-Process Communication (IPC)
|
||||
- [Oracle IPC](https://docs.oracle.com/cd/E19048-01/chorus5/806-6897/architecture-103/index.html)
|
||||
- [Inter-Process Communication in OS](https://www.scaler.com/topics/operating-system/inter-process-communication-in-os/)
|
||||
- [IPC on Wikipedia](https://en.wikipedia.org/wiki/Inter-process_communication)
|
||||
- [GeeksforGeeks IPC Guide](https://www.geeksforgeeks.org/inter-process-communication-ipc/)
|
||||
|
||||
## PCI (Peripheral Component Interconnect)
|
||||
- [OSDev PCI](https://wiki.osdev.org/PCI)
|
||||
- [PCI Configuration Space](https://en.wikipedia.org/wiki/PCI_configuration_space)
|
||||
|
||||
## Audio
|
||||
- [FFmpeg Audio Types](https://trac.ffmpeg.org/wiki/audio%20types)
|
||||
- [AC97 on OSDev](https://wiki.osdev.org/AC97)
|
||||
- [AC97 Revision 2.3 Specification](https://inst.eecs.berkeley.edu//~cs150/Documents/ac97_r23.pdf)
|
||||
|
||||
## Intrinsics (x86)
|
||||
- [Microsoft x86 Intrinsics](https://learn.microsoft.com/en-us/cpp/intrinsics/x86-intrinsics-list)
|
||||
- [Intel Intrinsics Guide](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html)
|
||||
|
||||
## CPUID Information
|
||||
- [AMD CPUID Instruction](https://www.amd.com/system/files/TechDocs/40332.pdf)
|
||||
- [CPUID Instruction Note](https://www.scss.tcd.ie/~jones/CS4021/processor-identification-cpuid-instruction-note.pdf)
|
||||
|
||||
## SMBIOS (System Management BIOS)
|
||||
- [DMTF DSP0134](https://www.dmtf.org/dsp/DSP0134)
|
||||
- [DSP0134 Version 3.6.0](https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.6.0.pdf)
|
||||
- [OSDev SMBIOS](https://wiki.osdev.org/System_Management_BIOS)
|
||||
|
||||
## EDBA (Effective Direct Bus Access)
|
||||
- [Memory Map (x86)](https://wiki.osdev.org/Memory_Map_(x86))
|
||||
|
||||
## UMIP, SMAP, and SMEP
|
||||
- [Control Register on Wikipedia](https://en.wikipedia.org/wiki/Control_register)
|
||||
- [Control Register and UMIP](https://web.archive.org/web/20160312223150/http://ncsi.com/nsatc11/presentations/wednesday/emerging_technologies/fischer.pdf)
|
||||
- [Supervisor Mode Access Prevention (SMEP)](https://en.wikipedia.org/wiki/Supervisor_Mode_Access_Prevention)
|
||||
|
||||
## Atomic Operations
|
||||
- [C++ Atomic Operations](https://en.cppreference.com/w/cpp/atomic/atomic)
|
||||
|
||||
## ELF (Executable and Linkable Format)
|
||||
- [ELF Header Format](https://www.sco.com/developers/gabi/latest/ch4.eheader.html)
|
||||
- [ELF File Format Specification](https://refspecs.linuxfoundation.org/elf/elf.pdf)
|
||||
- [Oracle: Executable and Linkable Format](https://docs.oracle.com/cd/E19683-01/817-3677/chapter6-42444/index.html)
|
||||
- [Oracle: ELF Program Headers](https://docs.oracle.com/cd/E19683-01/816-1386/chapter6-83432/index.html)
|
||||
- [YouTube Video on ELF](https://www.youtube.com/watch?v=nC1U1LJQL8o)
|
||||
- [Stevens' UNIX Network Programming](https://stevens.netmeister.org/631/elf.html)
|
||||
- [Linux Kernel ELF Header](https://github.com/torvalds/linux/blob/master/include/uapi/linux/elf.h)
|
||||
|
||||
## C++ ABI (Application Binary Interface)
|
||||
- [GCC libstdc++ Source](https://github.com/gcc-mirror/gcc/tree/master/libstdc%2B%2B-v3)
|
||||
- [Itanium C++ ABI](https://itanium-cxx-abi.github.io/cxx-abi/abi.html)
|
||||
|
||||
## signal.h
|
||||
- [POSIX signal.h](https://pubs.opengroup.org/onlinepubs/009695399/basedefs/signal.h.html)
|
||||
- [Linux signal(7) Manual](https://man7.org/linux/man-pages/man7/signal.7.html)
|
||||
|
||||
## PS/2
|
||||
- [Scan Codes](https://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html)
|
||||
- [PS/2 Keyboard on OSDev](https://wiki.osdev.org/PS2_Keyboard)
|
||||
- [PS/2 Mouse on OSDev](https://wiki.osdev.org/PS2_Mouse)
|
||||
- [Mouse Input on OSDev](https://wiki.osdev.org/Mouse_Input)
|
||||
- [I/O Ports on OSDev](https://wiki.osdev.org/I/O_ports)
|
||||
- [PS/2 Controller on OSDev](https://wiki.osdev.org/%228042%22_PS/2_Controller)
|
||||
- [AIP on OSDev](https://wiki.osdev.org/Advanced_Integrated_Peripheral)
|
||||
|
||||
## UART
|
||||
- [Interfacing the Serial / RS232 Port V5.0](http://www.senet.com.au/~cpeacock)
|
||||
|
||||
---
|
||||
|
||||
Special thanks to all contributors and the creators of the referenced projects and resources!
|
2659
Kernel/Doxyfile
Normal file
2659
Kernel/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
7
Kernel/ISSUES.md
Normal file
7
Kernel/ISSUES.md
Normal file
@ -0,0 +1,7 @@
|
||||
- [x] Kernel stack is smashed when an interrupt occurs. (this bug it occurs when an interrupt like IRQ1 or IRQ12 occurs)
|
||||
- [x] After setting the new stack pointer, the kernel crashes with an invalid opcode.
|
||||
- [ ] Somewhere in the kernel, the memory is wrongly freed or memcpy/memset.
|
||||
- [ ] GlobalDescriptorTable::SetKernelStack() is not working properly.
|
||||
- [ ] Sometimes while the kernel is inside BeforeShutdown(), we end up in a deadlock.
|
||||
- [ ] CPU usage is not working properly.
|
||||
- [x] fork() syscall is not working.
|
674
Kernel/LICENSE.md
Normal file
674
Kernel/LICENSE.md
Normal file
@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
54
Kernel/LICENSES.md
Normal file
54
Kernel/LICENSES.md
Normal file
@ -0,0 +1,54 @@
|
||||
# Licenses in the project
|
||||
|
||||
This project uses code from other projects, each with its own licenses.
|
||||
Below are the licenses associated with these components.
|
||||
Make sure to read and comply with these licenses before using or redistributing this software.
|
||||
|
||||
## printf
|
||||
|
||||
- **License:** The MIT License (MIT)
|
||||
- **Location:** [library/printf.c](library/printf.c) [include/printf.h](include/printf.h)
|
||||
|
||||
## stb_image
|
||||
|
||||
- **License:** The MIT License (MIT) and Public Domain
|
||||
- **Location:** [include/stb/image.h](include/stb/image.h)
|
||||
|
||||
## stb_image_resize
|
||||
|
||||
- **License:** The MIT License (MIT) and Public Domain
|
||||
- **Location:** [include/stb/image_resize.h](include/stb/image_resize.h)
|
||||
|
||||
## cargs
|
||||
|
||||
- **License:** The MIT License (MIT)
|
||||
- **Location:** [library/cargs.c](library/cargs.c) [include/cargs.h](include/cargs.h)
|
||||
|
||||
## cwalk
|
||||
|
||||
- **License:** The MIT License (MIT)
|
||||
- **Location:** [library/cwalk.c](library/cwalk.c) [include/cwalk.h](include/cwalk.h)
|
||||
|
||||
## Tamsyn Font (v1.11)
|
||||
|
||||
- **License:** Unknown
|
||||
- **Location:** [files/tamsyn-font-1.11/LICENSE](files/tamsyn-font-1.11/LICENSE)
|
||||
|
||||
## liballoc
|
||||
|
||||
- **License:** Public Domain
|
||||
- **Location:** [https://raw.githubusercontent.com/blanham/liballoc/master/LICENSE](https://raw.githubusercontent.com/blanham/liballoc/master/LICENSE)
|
||||
|
||||
## rpmalloc
|
||||
|
||||
- **License:** The MIT License (MIT) and Public Domain
|
||||
- **Location:** [https://raw.githubusercontent.com/mjansson/rpmalloc/develop/LICENSE](https://raw.githubusercontent.com/mjansson/rpmalloc/develop/LICENSE)
|
||||
|
||||
## ini.h
|
||||
|
||||
- **License:** The MIT License (MIT) and Public Domain
|
||||
- **Location:** [include/ini.h](include/ini.h)
|
||||
|
||||
---
|
||||
|
||||
Please refer to the respective license files for the full text of each license.
|
181
Kernel/Makefile
Normal file
181
Kernel/Makefile
Normal file
@ -0,0 +1,181 @@
|
||||
# Config file
|
||||
include ../Makefile.conf
|
||||
|
||||
KERNEL_FILENAME = fennix.elf
|
||||
|
||||
CC = ../$(COMPILER_PATH)/$(COMPILER_ARCH)gcc
|
||||
CPP = ../$(COMPILER_PATH)/$(COMPILER_ARCH)g++
|
||||
LD = ../$(COMPILER_PATH)/$(COMPILER_ARCH)ld
|
||||
AS = ../$(COMPILER_PATH)/$(COMPILER_ARCH)as
|
||||
NM = ../$(COMPILER_PATH)/$(COMPILER_ARCH)nm
|
||||
OBJCOPY = ../$(COMPILER_PATH)/$(COMPILER_ARCH)objcopy
|
||||
OBJDUMP = ../$(COMPILER_PATH)/$(COMPILER_ARCH)objdump
|
||||
GDB = ../$(COMPILER_PATH)/$(COMPILER_ARCH)gdb
|
||||
|
||||
RUST_TARGET_PATH = arch/$(OSARCH)/rust-target.json
|
||||
|
||||
GIT_COMMIT = $(shell git rev-parse HEAD)
|
||||
GIT_COMMIT_SHORT = $(shell git rev-parse --short HEAD)
|
||||
|
||||
HEADERS = $(sort $(dir $(wildcard ./include/*))) $(sort $(dir $(wildcard ./include_std/*)))
|
||||
INCLUDE_DIR = -I./include -I./include_std
|
||||
|
||||
BMP_SOURCES = $(shell find ./ -type f -name '*.bmp')
|
||||
PSF_SOURCES = $(shell find ./ -type f -name '*.psf')
|
||||
ifeq ($(OSARCH), amd64)
|
||||
S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*")
|
||||
s_SOURCES = $(shell find ./ -type f -name '*.s' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*")
|
||||
C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*")
|
||||
CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/i386/*" -not -path "./arch/aarch64/*")
|
||||
HEADERS += $(sort $(dir $(wildcard ./arch/amd64/include/*)))
|
||||
INCLUDE_DIR += -I./arch/amd64/include
|
||||
else ifeq ($(OSARCH), i386)
|
||||
S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*")
|
||||
s_SOURCES = $(shell find ./ -type f -name '*.s' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*")
|
||||
C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*")
|
||||
CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/amd64/*" -not -path "./arch/aarch64/*")
|
||||
HEADERS += $(sort $(dir $(wildcard ./arch/i386/include/*)))
|
||||
INCLUDE_DIR += -I./arch/i386/include
|
||||
else ifeq ($(OSARCH), aarch64)
|
||||
S_SOURCES = $(shell find ./ -type f -name '*.S' -not -path "./arch/amd64/*" -not -path "./arch/i386/*")
|
||||
s_SOURCES = $(shell find ./ -type f -name '*.s' -not -path "./arch/amd64/*" -not -path "./arch/i386/*")
|
||||
C_SOURCES = $(shell find ./ -type f -name '*.c' -not -path "./arch/amd64/*" -not -path "./arch/i386/*")
|
||||
CPP_SOURCES = $(shell find ./ -type f -name '*.cpp' -not -path "./arch/amd64/*" -not -path "./arch/i386/*")
|
||||
HEADERS += $(sort $(dir $(wildcard ./arch/aarch64/include/*)))
|
||||
INCLUDE_DIR += -I./arch/aarch64/include
|
||||
endif
|
||||
OBJ = $(C_SOURCES:.c=.o) $(CPP_SOURCES:.cpp=.o) $(ASM_SOURCES:.asm=.o) $(S_SOURCES:.S=.o) $(s_SOURCES:.s=.o) $(PSF_SOURCES:.psf=.o) $(BMP_SOURCES:.bmp=.o)
|
||||
STACK_USAGE_OBJ = $(C_SOURCES:.c=.su) $(CPP_SOURCES:.cpp=.su)
|
||||
GCNO_OBJ = $(C_SOURCES:.c=.gcno) $(CPP_SOURCES:.cpp=.gcno)
|
||||
|
||||
LDFLAGS := -Wl,-Map kernel.map -static -nostdlib -nodefaultlibs -nolibc
|
||||
|
||||
# Disable all warnings by adding "-w" in WARNCFLAG and if you want to treat the warnings as errors, add "-Werror"
|
||||
# -Wconversion this may be re-added later
|
||||
WARNCFLAG = -Wall -Wextra \
|
||||
-Wfloat-equal -Wpointer-arith -Wcast-align \
|
||||
-Wredundant-decls -Winit-self -Wswitch-default \
|
||||
-Wstrict-overflow=5 -Wno-error=cpp -Werror \
|
||||
-Wno-unused-parameter
|
||||
|
||||
# https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html
|
||||
CFLAGS := \
|
||||
$(INCLUDE_DIR) \
|
||||
-D__kernel__='1' \
|
||||
-DKERNEL_NAME='"$(OSNAME)"' \
|
||||
-DKERNEL_ARCH='"$(OSARCH)"' \
|
||||
-DKERNEL_VERSION='"$(KERNEL_VERSION)"' \
|
||||
-DGIT_COMMIT='"$(GIT_COMMIT)"' \
|
||||
-DGIT_COMMIT_SHORT='"$(GIT_COMMIT_SHORT)"'
|
||||
|
||||
ifeq ($(OSARCH), amd64)
|
||||
|
||||
CFLAGS += -fno-pic -fno-pie -mno-red-zone -march=core2 \
|
||||
-mcmodel=kernel -fno-builtin -Da64 -Da86 -m64
|
||||
CFLAG_STACK_PROTECTOR := -fstack-protector-all
|
||||
LDFLAGS += -Tarch/amd64/linker.ld \
|
||||
-fno-pic -fno-pie \
|
||||
-Wl,-static,--no-dynamic-linker,-ztext \
|
||||
-zmax-page-size=0x1000 \
|
||||
-Wl,-Map kernel.map
|
||||
|
||||
else ifeq ($(OSARCH), i386)
|
||||
|
||||
CFLAGS += -fno-pic -fno-pie -mno-red-zone -march=pentium \
|
||||
-fno-builtin -Da32 -Da86 -m32
|
||||
CFLAG_STACK_PROTECTOR := -fstack-protector-all
|
||||
LDFLAGS += -Tarch/i386/linker.ld \
|
||||
-fno-pic -fno-pie \
|
||||
-Wl,-static,--no-dynamic-linker,-ztext \
|
||||
-zmax-page-size=0x1000 \
|
||||
-Wl,-Map kernel.map
|
||||
|
||||
else ifeq ($(OSARCH), aarch64)
|
||||
|
||||
CFLAGS += -fno-builtin -Wstack-protector -Daa64 -fPIC -mno-outline-atomics
|
||||
CFLAG_STACK_PROTECTOR := -fstack-protector-all
|
||||
LDFLAGS += -Tarch/aarch64/linker.ld -fPIC -pie \
|
||||
-Wl,-static,--no-dynamic-linker,-ztext \
|
||||
-zmax-page-size=0x1000 \
|
||||
-Wl,-Map kernel.map
|
||||
|
||||
endif
|
||||
|
||||
# -finstrument-functions for __cyg_profile_func_enter & __cyg_profile_func_exit. Used for profiling and debugging.
|
||||
ifeq ($(DEBUG), 1)
|
||||
# CFLAGS += --coverage
|
||||
# CFLAGS += -pg
|
||||
# CFLAGS += -finstrument-functions
|
||||
CFLAGS += -DDEBUG -ggdb3 -O0 -fdiagnostics-color=always -fstack-usage -fsanitize=undefined
|
||||
ifeq ($(OSARCH), amd64)
|
||||
CFLAGS += -fverbose-asm
|
||||
endif
|
||||
ifneq ($(OSARCH), aarch64)
|
||||
CFLAGS += -fstack-check
|
||||
endif
|
||||
LDFLAGS += -ggdb3 -O0
|
||||
ASFLAGS += -g --gstabs+ --gdwarf-5 -D
|
||||
WARNCFLAG += -Wno-unused-function -Wno-maybe-uninitialized -Wno-builtin-declaration-mismatch -Wno-unknown-pragmas -Wno-unused-parameter -Wno-unused-variable
|
||||
endif
|
||||
|
||||
default:
|
||||
$(error Please specify a target)
|
||||
|
||||
prepare:
|
||||
$(info Nothing to prepare)
|
||||
|
||||
build: $(KERNEL_FILENAME)
|
||||
|
||||
dump:
|
||||
ifeq (,$(wildcard $(KERNEL_FILENAME)))
|
||||
$(error $(KERNEL_FILENAME) does not exist)
|
||||
endif
|
||||
$(info Dumping $(KERNEL_FILENAME) in AT T syntax...)
|
||||
$(OBJDUMP) -D -g -s -d $(KERNEL_FILENAME) > kernel_dump.map
|
||||
$(info Dumping $(KERNEL_FILENAME) in Intel syntax...)
|
||||
$(OBJDUMP) -M intel -D -g -s -d $(KERNEL_FILENAME) > kernel_dump_intel.map
|
||||
|
||||
$(KERNEL_FILENAME): $(OBJ)
|
||||
$(info Linking $@)
|
||||
$(CC) $(LDFLAGS) $(OBJ) -o $@
|
||||
# $(CC) $(LDFLAGS) $(OBJ) -mno-red-zone -lgcc -o $@
|
||||
|
||||
%.o: %.c $(HEADERS)
|
||||
$(info Compiling $<)
|
||||
$(CC) $(CFLAGS) $(CFLAG_STACK_PROTECTOR) $(WARNCFLAG) -std=c17 -c $< -o $@
|
||||
|
||||
# https://gcc.gnu.org/projects/cxx-status.html
|
||||
%.o: %.cpp $(HEADERS)
|
||||
$(info Compiling $<)
|
||||
$(CPP) $(CFLAGS) -fcoroutines $(CFLAG_STACK_PROTECTOR) $(WARNCFLAG) -std=c++20 -c $< -o $@ -fno-rtti
|
||||
|
||||
%.o: %.S
|
||||
$(info Compiling $<)
|
||||
$(AS) $(ASFLAGS) -c $< -o $@
|
||||
|
||||
%.o: %.s
|
||||
$(info Compiling $<)
|
||||
$(AS) $(ASFLAGS) -c $< -o $@
|
||||
|
||||
%.o: %.psf
|
||||
ifeq ($(OSARCH), amd64)
|
||||
$(OBJCOPY) -O elf64-x86-64 -I binary $< $@
|
||||
else ifeq ($(OSARCH), i386)
|
||||
$(OBJCOPY) -O elf32-i386 -I binary $< $@
|
||||
else ifeq ($(OSARCH), aarch64)
|
||||
$(OBJCOPY) -O elf64-littleaarch64 -I binary $< $@
|
||||
endif
|
||||
$(NM) $@
|
||||
|
||||
%.o: %.bmp
|
||||
ifeq ($(OSARCH), amd64)
|
||||
$(OBJCOPY) -O elf64-x86-64 -I binary $< $@
|
||||
else ifeq ($(OSARCH), i386)
|
||||
$(OBJCOPY) -O elf32-i386 -I binary $< $@
|
||||
else ifeq ($(OSARCH), aarch64)
|
||||
$(OBJCOPY) -O elf64-littlearch64 -I binary $< $@
|
||||
endif
|
||||
$(NM) $@
|
||||
|
||||
clean:
|
||||
rm -f kernel.map kernel_dump.map kernel_dump_intel.map $(OBJ) $(STACK_USAGE_OBJ) $(GCNO_OBJ) $(KERNEL_FILENAME)
|
11
Kernel/README.md
Normal file
11
Kernel/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Kernel
|
||||
|
||||
The core of the operating system.
|
||||
|
||||
---
|
||||
|
||||
Use `Fennix` repo to build the operating system.
|
||||
|
||||
```bash
|
||||
git clone --recurse-submodules https://github.com/Fennix-Project/Fennix.git
|
||||
```
|
51
Kernel/TODO.md
Normal file
51
Kernel/TODO.md
Normal file
@ -0,0 +1,51 @@
|
||||
# TODO
|
||||
|
||||
---
|
||||
|
||||
- [x] Optimize SMP.
|
||||
- [ ] Support IPv6.
|
||||
- [ ] ~~Endianess of the network stack (currently: [HOST](LSB)<=>[NETWORK](MSB)). Not sure if this is a standard or not.~~ (???)
|
||||
- [ ] Support 32-bit applications (ELF, PE, etc).
|
||||
- [ ] ~~Do not map the entire memory. Map only the needed memory address at allocation time.~~ (we just copy the pages for userland, see `Fork()` inside [core/memory/page_table.cpp](core/memory/page_table.cpp))
|
||||
- [ ] Implementation of logging (beside serial) with log rotation.
|
||||
- [x] Implement a better task manager. (replace struct P/TCB with classes)
|
||||
- [ ] Rewrite virtual file system.
|
||||
- [ ] Colors in crash screen are not following the kernel color scheme.
|
||||
- [x] ~~Find a way to add intrinsics.~~ (not feasible, use inline assembly)
|
||||
- [ ] Rework PSF1 font loader.
|
||||
- [x] ~~The cleanup should be done by a thread (tasking). This is done to avoid a deadlock.~~ (not needed, everything is done by the scheduler)
|
||||
- [ ] Implement a better Display::SetBrightness() function.
|
||||
- [ ] Fix memcpy, memset and memcmp functions (they are not working properly with SIMD).
|
||||
- [ ] Fully support i386.
|
||||
- [ ] Support Aarch64.
|
||||
- [ ] ~~SMP trampoline shouldn't be hardcoded at 0x2000.~~ (0x2000 is in the conventional memory, it's fine)
|
||||
- [ ] Rework the stack guard.
|
||||
- [x] Mutex implementation.
|
||||
- [ ] Update SMBIOS functions to support newer versions and actually use it.
|
||||
- [x] COW (Copy On Write) for the virtual memory. (https://en.wikipedia.org/wiki/Copy-on-write)
|
||||
- [x] Implement lazy allocation. (page faults)
|
||||
- [ ] Bootstrap should have a separate bss section + PHDR.
|
||||
- [ ] Reimplement the driver conflict detection.
|
||||
- [x] Elf loader shouldn't create a full copy of the elf binary. Copy only the needed sections.
|
||||
- [ ] Use NX-bit.
|
||||
- [ ] Fix std::string.
|
||||
- [x] Rewrite PS/2 drivers.
|
||||
- [ ] Improve signal handling.
|
||||
|
||||
- [ ] Improve the way the kernel crashes.
|
||||
- Add panic() function.
|
||||
- Handle assertion failures.
|
||||
|
||||
- [ ] Optimize screen printing.
|
||||
- On real hardware it's very slow, a solution is dirty printing.
|
||||
|
||||
- [x] Thread ids should follow the POSIX standard.
|
||||
- When a new process is created, the first thread should have the same id as the process id.
|
||||
- If pid is 400, the first thread should have the id 400. The second thread should have the id 401, etc.
|
||||
|
||||
- [ ] Optimize the scheduler
|
||||
- Create a separate list for processes that are waiting for a resource or a signal, etc.
|
||||
- Use all cores to schedule threads.
|
||||
|
||||
- [x] Improve Remap() function.
|
||||
- Remove Unmap & Map logic. Remove all flags directly.
|
36
Kernel/arch/aarch64/bootstrap/boot.S
Normal file
36
Kernel/arch/aarch64/bootstrap/boot.S
Normal file
@ -0,0 +1,36 @@
|
||||
/* Based on this tutorial:
|
||||
https://github.com/s-matyukevich/raspberry-pi-os */
|
||||
|
||||
.section ".text.boot", "a"
|
||||
|
||||
.extern _bss_start
|
||||
.extern _bss_end
|
||||
|
||||
.extern arm64Entry
|
||||
memzero:
|
||||
str xzr, [x0], #8
|
||||
subs x1, x1, #8
|
||||
b.gt memzero
|
||||
ret
|
||||
|
||||
.global _start
|
||||
_start:
|
||||
mrs x0, mpidr_el1
|
||||
and x0, x0, #0xFF
|
||||
cbz x0, _start2
|
||||
b CPU_Loop
|
||||
|
||||
_start2:
|
||||
adr x0, _bss_start
|
||||
adr x1, _bss_end
|
||||
sub x1, x1, x0
|
||||
bl memzero
|
||||
mov sp, #0x200000
|
||||
bl arm64Entry
|
||||
|
||||
Halt:
|
||||
wfe
|
||||
b Halt
|
||||
|
||||
CPU_Loop:
|
||||
b CPU_Loop
|
59
Kernel/arch/aarch64/cpu/smp.cpp
Normal file
59
Kernel/arch/aarch64/cpu/smp.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <smp.hpp>
|
||||
|
||||
#include <ints.hpp>
|
||||
#include <memory.hpp>
|
||||
#include <cpu.hpp>
|
||||
|
||||
#include "../../../kernel.h"
|
||||
|
||||
volatile bool CPUEnabled = false;
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
static __aligned(0x1000) CPUData CPUs[MAX_CPU] = {0};
|
||||
|
||||
CPUData *GetCPU(uint64_t id) { return &CPUs[id]; }
|
||||
|
||||
CPUData *GetCurrentCPU()
|
||||
{
|
||||
uint64_t ret = 0;
|
||||
|
||||
if (!CPUs[ret].IsActive)
|
||||
{
|
||||
error("CPU %d is not active!", ret);
|
||||
return &CPUs[0];
|
||||
}
|
||||
|
||||
if (CPUs[ret].Checksum != CPU_DATA_CHECKSUM)
|
||||
{
|
||||
error("CPU %d data is corrupted!", ret);
|
||||
return &CPUs[0];
|
||||
}
|
||||
return &CPUs[ret];
|
||||
}
|
||||
|
||||
namespace SMP
|
||||
{
|
||||
int CPUCores = 0;
|
||||
|
||||
void Initialize(void *madt)
|
||||
{
|
||||
fixme("SMP::Initialize() is not implemented!");
|
||||
}
|
||||
}
|
27
Kernel/arch/aarch64/entry.cpp
Normal file
27
Kernel/arch/aarch64/entry.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
|
||||
#include <debug.h>
|
||||
#include <cpu.hpp>
|
||||
|
||||
EXTERNC void arm64Entry(uint64_t dtb_ptr32, uint64_t x1, uint64_t x2, uint64_t x3)
|
||||
{
|
||||
trace("Hello, World!");
|
||||
CPU::Halt(true);
|
||||
}
|
0
Kernel/arch/aarch64/include/.gitkeep
Normal file
0
Kernel/arch/aarch64/include/.gitkeep
Normal file
90
Kernel/arch/aarch64/linker.ld
Normal file
90
Kernel/arch/aarch64/linker.ld
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
_bootstrap_start = .;
|
||||
.text.boot :
|
||||
{
|
||||
*(.text.boot)
|
||||
. += CONSTANT(MAXPAGESIZE);
|
||||
_bss_start = .;
|
||||
*(.text.bss)
|
||||
_bss_end = .;
|
||||
}
|
||||
_bootstrap_end = .;
|
||||
|
||||
_kernel_start = .;
|
||||
_kernel_text_start = .;
|
||||
.text :
|
||||
{
|
||||
KEEP(*(.text.boot))
|
||||
*(.text .text.*)
|
||||
}
|
||||
. = ALIGN(4096);
|
||||
_kernel_text_end = .;
|
||||
|
||||
_kernel_data_start = .;
|
||||
.data :
|
||||
{
|
||||
*(.data .data.*)
|
||||
}
|
||||
. = ALIGN(4096);
|
||||
_kernel_data_end = .;
|
||||
|
||||
_kernel_rodata_start = .;
|
||||
.rodata :
|
||||
{
|
||||
*(.rodata .rodata.*)
|
||||
}
|
||||
. = ALIGN(4096);
|
||||
|
||||
.init_array :
|
||||
{
|
||||
PROVIDE_HIDDEN(__init_array_start = .);
|
||||
KEEP(*(.init_array .ctors))
|
||||
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
}
|
||||
|
||||
.fini_array :
|
||||
{
|
||||
PROVIDE_HIDDEN(__fini_array_start = .);
|
||||
KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
|
||||
KEEP(*(.fini_array .dtors))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
}
|
||||
_kernel_rodata_end = .;
|
||||
|
||||
_kernel_bss_start = .;
|
||||
.bss :
|
||||
{
|
||||
*(.bss .bss.*)
|
||||
}
|
||||
. = ALIGN(4096);
|
||||
_kernel_bss_end = .;
|
||||
_kernel_end = .;
|
||||
_bss_size = _kernel_end - _kernel_rodata_end;
|
||||
|
||||
/DISCARD/ :
|
||||
{
|
||||
*(.comment*)
|
||||
*(.note*)
|
||||
}
|
||||
}
|
40
Kernel/arch/aarch64/memory/vmm.cpp
Normal file
40
Kernel/arch/aarch64/memory/vmm.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <memory.hpp>
|
||||
|
||||
#include <convert.h>
|
||||
#include <debug.h>
|
||||
|
||||
namespace Memory
|
||||
{
|
||||
bool Virtual::Check(void *VirtualAddress, PTFlag Flag, MapType Type)
|
||||
{
|
||||
}
|
||||
|
||||
void *Virtual::GetPhysical(void *VirtualAddress)
|
||||
{
|
||||
}
|
||||
|
||||
void Virtual::Map(void *VirtualAddress, void *PhysicalAddress, uint64_t Flags, MapType Type)
|
||||
{
|
||||
}
|
||||
|
||||
void Virtual::Unmap(void *VirtualAddress, MapType Type)
|
||||
{
|
||||
}
|
||||
}
|
31
Kernel/arch/aarch64/syscalls.cpp
Normal file
31
Kernel/arch/aarch64/syscalls.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <syscalls.hpp>
|
||||
|
||||
#include <cpu.hpp>
|
||||
|
||||
extern "C" __naked __used __no_stack_protector void SystemCallHandlerStub()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
extern "C" uint64_t SystemCallsHandler(SyscallsFrame *regs);
|
||||
|
||||
void InitializeSystemCalls()
|
||||
{
|
||||
}
|
38
Kernel/arch/amd64/bootstrap/_start.s
Normal file
38
Kernel/arch/amd64/bootstrap/_start.s
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.code32
|
||||
.extern Multiboot_start
|
||||
|
||||
.section .bootstrap.text, "a"
|
||||
|
||||
.global _start
|
||||
_start:
|
||||
/* Check for multiboot */
|
||||
cmp $0x2BADB002, %eax
|
||||
je .Multiboot
|
||||
|
||||
/* Unkown bootloader */
|
||||
.Hang:
|
||||
cli
|
||||
hlt
|
||||
jmp .Hang
|
||||
|
||||
/* Multiboot */
|
||||
.Multiboot:
|
||||
call Multiboot_start
|
||||
jmp .Hang
|
317
Kernel/arch/amd64/bootstrap/limine/limine.c
Normal file
317
Kernel/arch/amd64/bootstrap/limine/limine.c
Normal file
@ -0,0 +1,317 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <boot/binfo.h>
|
||||
#include <types.h>
|
||||
#include <debug.h>
|
||||
#include <convert.h>
|
||||
|
||||
#include "../../../../../tools/limine/limine.h"
|
||||
#include "../../../../kernel.h"
|
||||
|
||||
void InitLimine();
|
||||
|
||||
static volatile struct limine_entry_point_request EntryPointRequest = {
|
||||
.id = LIMINE_ENTRY_POINT_REQUEST,
|
||||
.revision = 0,
|
||||
.response = NULL,
|
||||
.entry = InitLimine};
|
||||
static volatile struct limine_bootloader_info_request BootloaderInfoRequest = {
|
||||
.id = LIMINE_BOOTLOADER_INFO_REQUEST,
|
||||
.revision = 0};
|
||||
static volatile struct limine_framebuffer_request FramebufferRequest = {
|
||||
.id = LIMINE_FRAMEBUFFER_REQUEST,
|
||||
.revision = 0};
|
||||
static volatile struct limine_memmap_request MemmapRequest = {
|
||||
.id = LIMINE_MEMMAP_REQUEST,
|
||||
.revision = 0};
|
||||
static volatile struct limine_kernel_address_request KernelAddressRequest = {
|
||||
.id = LIMINE_KERNEL_ADDRESS_REQUEST,
|
||||
.revision = 0};
|
||||
static volatile struct limine_rsdp_request RsdpRequest = {
|
||||
.id = LIMINE_RSDP_REQUEST,
|
||||
.revision = 0};
|
||||
static volatile struct limine_kernel_file_request KernelFileRequest = {
|
||||
.id = LIMINE_KERNEL_FILE_REQUEST,
|
||||
.revision = 0};
|
||||
static volatile struct limine_module_request ModuleRequest = {
|
||||
.id = LIMINE_MODULE_REQUEST,
|
||||
.revision = 0};
|
||||
static volatile struct limine_smbios_request SmbiosRequest = {
|
||||
.id = LIMINE_SMBIOS_REQUEST,
|
||||
.revision = 0};
|
||||
|
||||
void *TempStackPtr = NULL;
|
||||
__naked __used __no_stack_protector void InitLimine()
|
||||
{
|
||||
asmv("mov %%rsp, %0"
|
||||
: "=r"(TempStackPtr));
|
||||
|
||||
asmv("mov %0, %%rsp"
|
||||
:
|
||||
: "r"((uintptr_t)TempStackPtr - 0xFFFF800000000000));
|
||||
|
||||
asmv("mov $0, %rax\n"
|
||||
"mov $0, %rbx\n"
|
||||
"mov $0, %rcx\n"
|
||||
"mov $0, %rdx\n"
|
||||
"mov $0, %rsi\n"
|
||||
"mov $0, %rdi\n"
|
||||
"mov $0, %rbp\n"
|
||||
"mov $0, %r8\n"
|
||||
"mov $0, %r9\n"
|
||||
"mov $0, %r10\n"
|
||||
"mov $0, %r11\n"
|
||||
"mov $0, %r12\n"
|
||||
"mov $0, %r13\n"
|
||||
"mov $0, %r14\n"
|
||||
"mov $0, %r15");
|
||||
|
||||
asmv("jmp InitLimineAfterStack");
|
||||
}
|
||||
|
||||
nsa NIF void InitLimineAfterStack()
|
||||
{
|
||||
struct BootInfo binfo = {};
|
||||
struct limine_bootloader_info_response *BootloaderInfoResponse = BootloaderInfoRequest.response;
|
||||
info("Bootloader: %s %s", BootloaderInfoResponse->name, BootloaderInfoResponse->version);
|
||||
|
||||
struct limine_framebuffer_response *FrameBufferResponse = FramebufferRequest.response;
|
||||
struct limine_memmap_response *MemmapResponse = MemmapRequest.response;
|
||||
struct limine_kernel_address_response *KernelAddressResponse = KernelAddressRequest.response;
|
||||
struct limine_rsdp_response *RsdpResponse = RsdpRequest.response;
|
||||
struct limine_kernel_file_response *KernelFileResponse = KernelFileRequest.response;
|
||||
struct limine_module_response *ModuleResponse = ModuleRequest.response;
|
||||
struct limine_smbios_response *SmbiosResponse = SmbiosRequest.response;
|
||||
|
||||
if (FrameBufferResponse == NULL || FrameBufferResponse->framebuffer_count < 1)
|
||||
{
|
||||
error("No framebuffer available [%#lx;%ld]", FrameBufferResponse,
|
||||
(FrameBufferResponse == NULL) ? 0 : FrameBufferResponse->framebuffer_count);
|
||||
inf_loop asmv("hlt");
|
||||
}
|
||||
|
||||
if (MemmapResponse == NULL || MemmapResponse->entry_count < 1)
|
||||
{
|
||||
error("No memory map available [%#lx;%ld]", MemmapResponse,
|
||||
(MemmapResponse == NULL) ? 0 : MemmapResponse->entry_count);
|
||||
inf_loop asmv("hlt");
|
||||
}
|
||||
|
||||
if (KernelAddressResponse == NULL)
|
||||
{
|
||||
error("No kernel address available [%#lx]", KernelAddressResponse);
|
||||
inf_loop asmv("hlt");
|
||||
}
|
||||
|
||||
if (RsdpResponse == NULL || RsdpResponse->address == 0)
|
||||
{
|
||||
error("No RSDP address available [%#lx;%#lx]", RsdpResponse,
|
||||
(RsdpResponse == NULL) ? 0 : RsdpResponse->address);
|
||||
inf_loop asmv("hlt");
|
||||
}
|
||||
|
||||
if (KernelFileResponse == NULL || KernelFileResponse->kernel_file == NULL)
|
||||
{
|
||||
error("No kernel file available [%#lx;%#lx]", KernelFileResponse,
|
||||
(KernelFileResponse == NULL) ? 0 : KernelFileResponse->kernel_file);
|
||||
inf_loop asmv("hlt");
|
||||
}
|
||||
|
||||
/* Actual parsing starts here */
|
||||
|
||||
for (uint64_t i = 0; i < FrameBufferResponse->framebuffer_count; i++)
|
||||
{
|
||||
struct limine_framebuffer *framebuffer = FrameBufferResponse->framebuffers[i];
|
||||
switch (framebuffer->memory_model)
|
||||
{
|
||||
case LIMINE_FRAMEBUFFER_RGB:
|
||||
binfo.Framebuffer[i].Type = RGB;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
error("Unsupported framebuffer memory model %d", framebuffer->memory_model);
|
||||
inf_loop asmv("hlt");
|
||||
}
|
||||
}
|
||||
binfo.Framebuffer[i].BaseAddress = (void *)((uintptr_t)framebuffer->address - 0xFFFF800000000000);
|
||||
binfo.Framebuffer[i].Width = (uint32_t)framebuffer->width;
|
||||
binfo.Framebuffer[i].Height = (uint32_t)framebuffer->height;
|
||||
binfo.Framebuffer[i].Pitch = (uint32_t)framebuffer->pitch;
|
||||
binfo.Framebuffer[i].BitsPerPixel = framebuffer->bpp;
|
||||
binfo.Framebuffer[i].RedMaskSize = framebuffer->red_mask_size;
|
||||
binfo.Framebuffer[i].RedMaskShift = framebuffer->red_mask_shift;
|
||||
binfo.Framebuffer[i].GreenMaskSize = framebuffer->green_mask_size;
|
||||
binfo.Framebuffer[i].GreenMaskShift = framebuffer->green_mask_shift;
|
||||
binfo.Framebuffer[i].BlueMaskSize = framebuffer->blue_mask_size;
|
||||
binfo.Framebuffer[i].BlueMaskShift = framebuffer->blue_mask_shift;
|
||||
binfo.Framebuffer[i].ExtendedDisplayIdentificationData = framebuffer->edid;
|
||||
binfo.Framebuffer[i].EDIDSize = framebuffer->edid_size;
|
||||
|
||||
debug("Framebuffer %d: %dx%d %d bpp", i,
|
||||
binfo.Framebuffer[i].Width,
|
||||
binfo.Framebuffer[i].Height,
|
||||
binfo.Framebuffer[i].BitsPerPixel);
|
||||
|
||||
debug("More info:\nAddress: %#lx\nPitch: %ld\nType: %d\nRedMaskSize: %d\nRedMaskShift: %d\nGreenMaskSize: %d\nGreenMaskShift: %d\nBlueMaskSize: %d\nBlueMaskShift: %d\nEDID: %#lx\nEDIDSize: %d",
|
||||
binfo.Framebuffer[i].BaseAddress,
|
||||
binfo.Framebuffer[i].Pitch,
|
||||
binfo.Framebuffer[i].Type,
|
||||
binfo.Framebuffer[i].RedMaskSize,
|
||||
binfo.Framebuffer[i].RedMaskShift,
|
||||
binfo.Framebuffer[i].GreenMaskSize,
|
||||
binfo.Framebuffer[i].GreenMaskShift,
|
||||
binfo.Framebuffer[i].BlueMaskSize,
|
||||
binfo.Framebuffer[i].BlueMaskShift,
|
||||
binfo.Framebuffer[i].ExtendedDisplayIdentificationData,
|
||||
binfo.Framebuffer[i].EDIDSize);
|
||||
}
|
||||
|
||||
binfo.Memory.Entries = MemmapResponse->entry_count;
|
||||
for (uint64_t i = 0; i < MemmapResponse->entry_count; i++)
|
||||
{
|
||||
if (MemmapResponse->entry_count > MAX_MEMORY_ENTRIES)
|
||||
{
|
||||
warn("Too many memory entries, skipping the rest...");
|
||||
break;
|
||||
}
|
||||
|
||||
struct limine_memmap_entry *entry = MemmapResponse->entries[i];
|
||||
if (!entry)
|
||||
{
|
||||
warn("Null memory entry %ld (%#lx), skipping...", i, entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
binfo.Memory.Size += entry->length;
|
||||
switch (entry->type)
|
||||
{
|
||||
case LIMINE_MEMMAP_USABLE:
|
||||
binfo.Memory.Entry[i].BaseAddress = (void *)entry->base;
|
||||
binfo.Memory.Entry[i].Length = entry->length;
|
||||
binfo.Memory.Entry[i].Type = Usable;
|
||||
break;
|
||||
case LIMINE_MEMMAP_RESERVED:
|
||||
binfo.Memory.Entry[i].BaseAddress = (void *)entry->base;
|
||||
binfo.Memory.Entry[i].Length = entry->length;
|
||||
binfo.Memory.Entry[i].Type = Reserved;
|
||||
break;
|
||||
case LIMINE_MEMMAP_ACPI_RECLAIMABLE:
|
||||
binfo.Memory.Entry[i].BaseAddress = (void *)entry->base;
|
||||
binfo.Memory.Entry[i].Length = entry->length;
|
||||
binfo.Memory.Entry[i].Type = ACPIReclaimable;
|
||||
break;
|
||||
case LIMINE_MEMMAP_ACPI_NVS:
|
||||
binfo.Memory.Entry[i].BaseAddress = (void *)entry->base;
|
||||
binfo.Memory.Entry[i].Length = entry->length;
|
||||
binfo.Memory.Entry[i].Type = ACPINVS;
|
||||
break;
|
||||
case LIMINE_MEMMAP_BAD_MEMORY:
|
||||
binfo.Memory.Entry[i].BaseAddress = (void *)entry->base;
|
||||
binfo.Memory.Entry[i].Length = entry->length;
|
||||
binfo.Memory.Entry[i].Type = BadMemory;
|
||||
break;
|
||||
case LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE:
|
||||
binfo.Memory.Entry[i].BaseAddress = (void *)entry->base;
|
||||
binfo.Memory.Entry[i].Length = entry->length;
|
||||
binfo.Memory.Entry[i].Type = BootloaderReclaimable;
|
||||
break;
|
||||
case LIMINE_MEMMAP_KERNEL_AND_MODULES:
|
||||
binfo.Memory.Entry[i].BaseAddress = (void *)entry->base;
|
||||
binfo.Memory.Entry[i].Length = entry->length;
|
||||
binfo.Memory.Entry[i].Type = KernelAndModules;
|
||||
break;
|
||||
case LIMINE_MEMMAP_FRAMEBUFFER:
|
||||
binfo.Memory.Entry[i].BaseAddress = (void *)entry->base;
|
||||
binfo.Memory.Entry[i].Length = entry->length;
|
||||
binfo.Memory.Entry[i].Type = Framebuffer;
|
||||
break;
|
||||
default:
|
||||
binfo.Memory.Entry[i].BaseAddress = (void *)entry->base;
|
||||
binfo.Memory.Entry[i].Length = entry->length;
|
||||
binfo.Memory.Entry[i].Type = Unknown;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ModuleResponse != NULL && ModuleResponse->module_count > 0)
|
||||
{
|
||||
for (uint64_t i = 0; i < ModuleResponse->module_count; i++)
|
||||
{
|
||||
if (i > MAX_MODULES)
|
||||
{
|
||||
warn("Too many modules, skipping the rest...");
|
||||
break;
|
||||
}
|
||||
|
||||
binfo.Modules[i].Address = (void *)((uint64_t)ModuleResponse->modules[i]->address - 0xFFFF800000000000);
|
||||
binfo.Modules[i].Size = ModuleResponse->modules[i]->size;
|
||||
|
||||
strncpy(binfo.Modules[i].Path,
|
||||
ModuleResponse->modules[i]->path,
|
||||
strlen(ModuleResponse->modules[i]->path) + 1);
|
||||
|
||||
strncpy(binfo.Modules[i].CommandLine,
|
||||
ModuleResponse->modules[i]->cmdline,
|
||||
strlen(ModuleResponse->modules[i]->cmdline) + 1);
|
||||
|
||||
debug("Module %d:\nAddress: %#lx\nPath: \"%s\"\nCommand Line: \"%s\"\nSize: %ld",
|
||||
i,
|
||||
binfo.Modules[i].Address,
|
||||
binfo.Modules[i].Path,
|
||||
binfo.Modules[i].CommandLine,
|
||||
binfo.Modules[i].Size);
|
||||
}
|
||||
}
|
||||
|
||||
binfo.RSDP = (struct RSDPInfo *)((uintptr_t)RsdpResponse->address - 0xFFFF800000000000);
|
||||
debug("RSDP: %#lx [Signature: \"%.8s\"] [OEM: \"%.6s\"]",
|
||||
binfo.RSDP, binfo.RSDP->Signature, binfo.RSDP->OEMID);
|
||||
|
||||
if (SmbiosResponse->entry_64 != NULL)
|
||||
binfo.SMBIOSPtr = (void *)((uintptr_t)SmbiosResponse->entry_64 - 0xFFFF800000000000);
|
||||
else if (SmbiosResponse->entry_32 != NULL)
|
||||
binfo.SMBIOSPtr = (void *)((uintptr_t)SmbiosResponse->entry_32 - 0xFFFF800000000000);
|
||||
else
|
||||
binfo.SMBIOSPtr = NULL;
|
||||
debug("SMBIOS: %#lx %#lx (binfo: %#lx)",
|
||||
SmbiosResponse->entry_32,
|
||||
SmbiosResponse->entry_64,
|
||||
binfo.SMBIOSPtr);
|
||||
|
||||
binfo.Kernel.PhysicalBase = (void *)KernelAddressResponse->physical_base;
|
||||
binfo.Kernel.VirtualBase = (void *)KernelAddressResponse->virtual_base;
|
||||
binfo.Kernel.FileBase = (void *)((uintptr_t)KernelFileResponse->kernel_file->address - 0xFFFF800000000000);
|
||||
binfo.Kernel.Size = KernelFileResponse->kernel_file->size;
|
||||
|
||||
strncpy(binfo.Kernel.CommandLine,
|
||||
KernelFileResponse->kernel_file->cmdline,
|
||||
strlen(KernelFileResponse->kernel_file->cmdline) + 1);
|
||||
|
||||
debug("Kernel physical address: %#lx", binfo.Kernel.PhysicalBase);
|
||||
debug("Kernel virtual address: %#lx", binfo.Kernel.VirtualBase);
|
||||
|
||||
strncpy(binfo.Bootloader.Name,
|
||||
BootloaderInfoResponse->name,
|
||||
strlen(BootloaderInfoResponse->name) + 1);
|
||||
|
||||
strncpy(binfo.Bootloader.Version,
|
||||
BootloaderInfoResponse->version,
|
||||
strlen(BootloaderInfoResponse->version) + 1);
|
||||
|
||||
Entry(&binfo);
|
||||
}
|
81
Kernel/arch/amd64/bootstrap/multiboot/Helper/detect.s
Normal file
81
Kernel/arch/amd64/bootstrap/multiboot/Helper/detect.s
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.intel_syntax noprefix
|
||||
|
||||
.code32
|
||||
.section .bootstrap.text, "a"
|
||||
|
||||
.global DetectCPUID
|
||||
DetectCPUID:
|
||||
pushfd
|
||||
pop eax
|
||||
mov ecx, eax
|
||||
xor eax, 0x200000
|
||||
push eax
|
||||
popfd
|
||||
pushfd
|
||||
pop eax
|
||||
push ecx
|
||||
popfd
|
||||
xor eax, ecx
|
||||
jz .NoCPUID
|
||||
mov eax, 0x1
|
||||
ret
|
||||
.NoCPUID:
|
||||
xor eax, eax
|
||||
ret
|
||||
|
||||
.global Detect64Bit
|
||||
Detect64Bit:
|
||||
mov eax, 0x80000000
|
||||
cpuid
|
||||
cmp eax, 0x80000001
|
||||
jb .NoLongMode
|
||||
mov eax, 0x80000001
|
||||
cpuid
|
||||
test edx, 0x20000000
|
||||
jz .NoLongMode
|
||||
mov eax, 0x1
|
||||
ret
|
||||
.NoLongMode:
|
||||
xor eax, eax
|
||||
ret
|
||||
|
||||
.global DetectPSE
|
||||
DetectPSE:
|
||||
mov eax, 0x00000001
|
||||
cpuid
|
||||
test edx, 0x00000008
|
||||
jz .NoPSE
|
||||
mov eax, 0x1
|
||||
ret
|
||||
.NoPSE:
|
||||
xor eax, eax
|
||||
ret
|
||||
|
||||
.global DetectPAE
|
||||
DetectPAE:
|
||||
mov eax, 0x00000001
|
||||
cpuid
|
||||
test edx, 0x00000040
|
||||
jz .NoPAE
|
||||
mov eax, 0x1
|
||||
ret
|
||||
.NoPAE:
|
||||
xor eax, eax
|
||||
ret
|
64
Kernel/arch/amd64/bootstrap/multiboot/Helper/gdt32.s
Normal file
64
Kernel/arch/amd64/bootstrap/multiboot/Helper/gdt32.s
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.code32
|
||||
.section .bootstrap.text, "a"
|
||||
|
||||
.align 32
|
||||
.global gdtr
|
||||
gdtr:
|
||||
.word GDT32_END - GDT32 - 1
|
||||
.long GDT32
|
||||
|
||||
.align 32
|
||||
GDT32:
|
||||
.quad 0x0
|
||||
|
||||
.word 0xFFFF
|
||||
.word 0x0000
|
||||
.byte 0x00
|
||||
.word 0xCF9A
|
||||
.byte 0x00
|
||||
|
||||
.word 0xFFFF
|
||||
.word 0x0000
|
||||
.byte 0x00
|
||||
.word 0xCF92
|
||||
.byte 0x00
|
||||
|
||||
.word 0x0100
|
||||
.word 0x1000
|
||||
.byte 0x00
|
||||
.word 0x4092
|
||||
.byte 0x00
|
||||
GDT32_END:
|
||||
nop
|
||||
|
||||
.global LoadGDT32
|
||||
LoadGDT32:
|
||||
lgdt [gdtr]
|
||||
ljmp $0x8, $ActivateGDT
|
||||
|
||||
ActivateGDT:
|
||||
mov $0x10, %cx
|
||||
mov %cx, %ss
|
||||
mov %cx, %ds
|
||||
mov %cx, %es
|
||||
mov %cx, %fs
|
||||
mov $0x18, %cx
|
||||
mov %cx, %gs
|
||||
ret
|
62
Kernel/arch/amd64/bootstrap/multiboot/Helper/gdt64.s
Normal file
62
Kernel/arch/amd64/bootstrap/multiboot/Helper/gdt64.s
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.code64
|
||||
.section .bootstrap.data, "a"
|
||||
|
||||
/* Access bits */
|
||||
A = 0x1
|
||||
RW = 0x2
|
||||
DC = 0x4
|
||||
E = 0x8
|
||||
S = 0x10
|
||||
DPL0 = 0x0 /* 0 << 5 ???? */
|
||||
DPL1 = 0x20
|
||||
P = 0x80
|
||||
|
||||
/* Flags bits */
|
||||
LONG_MODE = 0x20
|
||||
SZ_32 = 0x40
|
||||
GRAN_4K = 0x80
|
||||
|
||||
.global GDT64.Null
|
||||
.global GDT64.Code
|
||||
.global GDT64.Data
|
||||
.global GDT64.Tss
|
||||
.global GDT64.Ptr
|
||||
|
||||
GDT64:
|
||||
GDT64.Null = . - GDT64
|
||||
.quad 0
|
||||
GDT64.Code = . - GDT64
|
||||
.long 0xFFFF
|
||||
.byte 0
|
||||
.byte P | S | E | RW
|
||||
.byte GRAN_4K | LONG_MODE | 0xF
|
||||
.byte 0
|
||||
GDT64.Data = . - GDT64
|
||||
.long 0xFFFF
|
||||
.byte 0
|
||||
.byte P | S | RW
|
||||
.byte GRAN_4K | SZ_32 | 0xF
|
||||
.byte 0
|
||||
GDT64.Tss = . - GDT64
|
||||
.long 0x00000068
|
||||
.long 0x00CF8900
|
||||
GDT64.Ptr:
|
||||
.word . - GDT64 - 1
|
||||
.quad GDT64
|
301
Kernel/arch/amd64/bootstrap/multiboot/Paging/mb_64bit_map.cpp
Normal file
301
Kernel/arch/amd64/bootstrap/multiboot/Paging/mb_64bit_map.cpp
Normal file
@ -0,0 +1,301 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
|
||||
union __attribute__((packed)) PageTableEntry
|
||||
{
|
||||
struct
|
||||
{
|
||||
bool Present : 1; // 0
|
||||
bool ReadWrite : 1; // 1
|
||||
bool UserSupervisor : 1; // 2
|
||||
bool WriteThrough : 1; // 3
|
||||
bool CacheDisable : 1; // 4
|
||||
bool Accessed : 1; // 5
|
||||
bool Dirty : 1; // 6
|
||||
bool PageAttributeTable : 1; // 7
|
||||
bool Global : 1; // 8
|
||||
uint8_t Available0 : 3; // 9-11
|
||||
uint64_t Address : 40; // 12-51
|
||||
uint32_t Available1 : 7; // 52-58
|
||||
uint8_t ProtectionKey : 4; // 59-62
|
||||
bool ExecuteDisable : 1; // 63
|
||||
};
|
||||
uint64_t raw;
|
||||
|
||||
__always_inline inline nsa NIF void SetAddress(uintptr_t _Address)
|
||||
{
|
||||
_Address &= 0x000000FFFFFFFFFF;
|
||||
this->raw &= 0xFFF0000000000FFF;
|
||||
this->raw |= (_Address << 12);
|
||||
}
|
||||
|
||||
__always_inline inline nsa NIF uintptr_t GetAddress() { return (this->raw & 0x000FFFFFFFFFF000) >> 12; }
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) PageTableEntryPtr
|
||||
{
|
||||
PageTableEntry Entries[512];
|
||||
};
|
||||
|
||||
union __attribute__((packed)) PageDirectoryEntry
|
||||
{
|
||||
struct
|
||||
{
|
||||
bool Present : 1; // 0
|
||||
bool ReadWrite : 1; // 1
|
||||
bool UserSupervisor : 1; // 2
|
||||
bool WriteThrough : 1; // 3
|
||||
bool CacheDisable : 1; // 4
|
||||
bool Accessed : 1; // 5
|
||||
bool Available0 : 1; // 6
|
||||
bool PageSize : 1; // 7
|
||||
uint8_t Available1 : 4; // 8-11
|
||||
uint64_t Address : 40; // 12-51
|
||||
uint32_t Available2 : 11; // 52-62
|
||||
bool ExecuteDisable : 1; // 63
|
||||
};
|
||||
uint64_t raw;
|
||||
|
||||
__always_inline inline nsa NIF void SetAddress(uintptr_t _Address)
|
||||
{
|
||||
_Address &= 0x000000FFFFFFFFFF;
|
||||
this->raw &= 0xFFF0000000000FFF;
|
||||
this->raw |= (_Address << 12);
|
||||
}
|
||||
|
||||
__always_inline inline nsa NIF uintptr_t GetAddress() { return (this->raw & 0x000FFFFFFFFFF000) >> 12; }
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) PageDirectoryEntryPtr
|
||||
{
|
||||
PageDirectoryEntry Entries[512];
|
||||
};
|
||||
|
||||
union __attribute__((packed)) PageDirectoryPointerTableEntry
|
||||
{
|
||||
struct
|
||||
{
|
||||
bool Present : 1; // 0
|
||||
bool ReadWrite : 1; // 1
|
||||
bool UserSupervisor : 1; // 2
|
||||
bool WriteThrough : 1; // 3
|
||||
bool CacheDisable : 1; // 4
|
||||
bool Accessed : 1; // 5
|
||||
bool Available0 : 1; // 6
|
||||
bool PageSize : 1; // 7
|
||||
uint8_t Available1 : 4; // 8-11
|
||||
uint64_t Address : 40; // 12-51
|
||||
uint32_t Available2 : 11; // 52-62
|
||||
bool ExecuteDisable : 1; // 63
|
||||
};
|
||||
uint64_t raw;
|
||||
|
||||
__always_inline inline nsa NIF void SetAddress(uintptr_t _Address)
|
||||
{
|
||||
_Address &= 0x000000FFFFFFFFFF;
|
||||
this->raw &= 0xFFF0000000000FFF;
|
||||
this->raw |= (_Address << 12);
|
||||
}
|
||||
|
||||
__always_inline inline nsa NIF uintptr_t GetAddress() { return (this->raw & 0x000FFFFFFFFFF000) >> 12; }
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) PageDirectoryPointerTableEntryPtr
|
||||
{
|
||||
PageDirectoryPointerTableEntry Entries[512];
|
||||
};
|
||||
|
||||
union __attribute__((packed)) PageMapLevel4
|
||||
{
|
||||
struct
|
||||
{
|
||||
bool Present : 1; // 0
|
||||
bool ReadWrite : 1; // 1
|
||||
bool UserSupervisor : 1; // 2
|
||||
bool WriteThrough : 1; // 3
|
||||
bool CacheDisable : 1; // 4
|
||||
bool Accessed : 1; // 5
|
||||
bool Available0 : 1; // 6
|
||||
bool Reserved0 : 1; // 7
|
||||
uint8_t Available1 : 4; // 8-11
|
||||
uint64_t Address : 40; // 12-51
|
||||
uint32_t Available2 : 11; // 52-62
|
||||
bool ExecuteDisable : 1; // 63
|
||||
};
|
||||
uint64_t raw;
|
||||
|
||||
__always_inline inline nsa NIF void SetAddress(uintptr_t _Address)
|
||||
{
|
||||
_Address &= 0x000000FFFFFFFFFF;
|
||||
this->raw &= 0xFFF0000000000FFF;
|
||||
this->raw |= (_Address << 12);
|
||||
}
|
||||
|
||||
__always_inline inline nsa NIF uintptr_t GetAddress() { return (this->raw & 0x000FFFFFFFFFF000) >> 12; }
|
||||
};
|
||||
|
||||
struct PageTable4
|
||||
{
|
||||
PageMapLevel4 Entries[512];
|
||||
} __attribute__((aligned(0x1000)));
|
||||
|
||||
extern "C" char BootPageTable[];
|
||||
extern uintptr_t _kernel_start, _kernel_end;
|
||||
|
||||
__attribute__((section(".bootstrap.data"))) static PageTable4 *BPTable = (PageTable4 *)BootPageTable;
|
||||
__attribute__((section(".bootstrap.data"))) static size_t BPT_Allocated = 0x4000;
|
||||
|
||||
__always_inline inline nsa NIF void *RequestPage()
|
||||
{
|
||||
void *Page = (void *)(BootPageTable + BPT_Allocated);
|
||||
BPT_Allocated += 0x1000;
|
||||
if (BPT_Allocated >= 0x10000) /* The length of BootPageTable */
|
||||
{
|
||||
while (true)
|
||||
;
|
||||
}
|
||||
return Page;
|
||||
}
|
||||
|
||||
class PageMapIndexer
|
||||
{
|
||||
public:
|
||||
uintptr_t PMLIndex = 0;
|
||||
uintptr_t PDPTEIndex = 0;
|
||||
uintptr_t PDEIndex = 0;
|
||||
uintptr_t PTEIndex = 0;
|
||||
__always_inline inline nsa NIF PageMapIndexer(uintptr_t VirtualAddress)
|
||||
{
|
||||
uintptr_t Address = VirtualAddress;
|
||||
Address >>= 12;
|
||||
this->PTEIndex = Address & 0x1FF;
|
||||
Address >>= 9;
|
||||
this->PDEIndex = Address & 0x1FF;
|
||||
Address >>= 9;
|
||||
this->PDPTEIndex = Address & 0x1FF;
|
||||
Address >>= 9;
|
||||
this->PMLIndex = Address & 0x1FF;
|
||||
}
|
||||
};
|
||||
|
||||
__attribute__((section(".bootstrap.text"))) nsa NIF void MB2_64_Map(void *VirtualAddress, void *PhysicalAddress, uint64_t Flags)
|
||||
{
|
||||
PageMapIndexer Index = PageMapIndexer((uintptr_t)VirtualAddress);
|
||||
// Clear any flags that are not 1 << 0 (Present) - 1 << 5 (Accessed) because rest are for page table entries only
|
||||
uint64_t DirectoryFlags = Flags & 0x3F;
|
||||
|
||||
PageMapLevel4 PML4 = BPTable->Entries[Index.PMLIndex];
|
||||
PageDirectoryPointerTableEntryPtr *PDPTEPtr = nullptr;
|
||||
if (!PML4.Present)
|
||||
{
|
||||
PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)RequestPage();
|
||||
if (PDPTEPtr == nullptr)
|
||||
return;
|
||||
{
|
||||
void *ptr = PDPTEPtr;
|
||||
uint8_t value = 0;
|
||||
size_t num = 0x1000;
|
||||
uint8_t *p = (uint8_t *)ptr;
|
||||
for (size_t i = 0; i < num; i++)
|
||||
p[i] = value;
|
||||
}
|
||||
PML4.Present = true;
|
||||
PML4.SetAddress((uintptr_t)PDPTEPtr >> 12);
|
||||
}
|
||||
else
|
||||
PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4.GetAddress() << 12);
|
||||
PML4.raw |= DirectoryFlags;
|
||||
BPTable->Entries[Index.PMLIndex] = PML4;
|
||||
|
||||
PageDirectoryPointerTableEntry PDPTE = PDPTEPtr->Entries[Index.PDPTEIndex];
|
||||
PageDirectoryEntryPtr *PDEPtr = nullptr;
|
||||
if (!PDPTE.Present)
|
||||
{
|
||||
PDEPtr = (PageDirectoryEntryPtr *)RequestPage();
|
||||
if (PDEPtr == nullptr)
|
||||
return;
|
||||
{
|
||||
void *ptr = PDEPtr;
|
||||
uint8_t value = 0;
|
||||
size_t num = 0x1000;
|
||||
uint8_t *p = (uint8_t *)ptr;
|
||||
for (size_t i = 0; i < num; i++)
|
||||
p[i] = value;
|
||||
}
|
||||
PDPTE.Present = true;
|
||||
PDPTE.SetAddress((uintptr_t)PDEPtr >> 12);
|
||||
}
|
||||
else
|
||||
PDEPtr = (PageDirectoryEntryPtr *)((uintptr_t)PDPTE.GetAddress() << 12);
|
||||
PDPTE.raw |= DirectoryFlags;
|
||||
PDPTEPtr->Entries[Index.PDPTEIndex] = PDPTE;
|
||||
|
||||
PageDirectoryEntry PDE = PDEPtr->Entries[Index.PDEIndex];
|
||||
PageTableEntryPtr *PTEPtr = nullptr;
|
||||
if (!PDE.Present)
|
||||
{
|
||||
PTEPtr = (PageTableEntryPtr *)RequestPage();
|
||||
if (PTEPtr == nullptr)
|
||||
return;
|
||||
{
|
||||
void *ptr = PTEPtr;
|
||||
uint8_t value = 0;
|
||||
size_t num = 0x1000;
|
||||
uint8_t *p = (uint8_t *)ptr;
|
||||
for (size_t i = 0; i < num; i++)
|
||||
p[i] = value;
|
||||
}
|
||||
PDE.Present = true;
|
||||
PDE.SetAddress((uintptr_t)PTEPtr >> 12);
|
||||
}
|
||||
else
|
||||
PTEPtr = (PageTableEntryPtr *)((uintptr_t)PDE.GetAddress() << 12);
|
||||
PDE.raw |= DirectoryFlags;
|
||||
PDEPtr->Entries[Index.PDEIndex] = PDE;
|
||||
|
||||
PageTableEntry PTE = PTEPtr->Entries[Index.PTEIndex];
|
||||
PTE.Present = true;
|
||||
PTE.raw |= Flags;
|
||||
PTE.SetAddress((uintptr_t)PhysicalAddress >> 12);
|
||||
PTEPtr->Entries[Index.PTEIndex] = PTE;
|
||||
asmv("invlpg (%0)"
|
||||
:
|
||||
: "r"(VirtualAddress)
|
||||
: "memory");
|
||||
}
|
||||
|
||||
EXTERNC __attribute__((section(".bootstrap.text"))) nsa NIF __attribute__((section(".bootstrap.text"))) void UpdatePageTable64()
|
||||
{
|
||||
BPTable = (PageTable4 *)BootPageTable;
|
||||
|
||||
uintptr_t KernelStart = (uintptr_t)&_kernel_start;
|
||||
uintptr_t KernelEnd = (uintptr_t)&_kernel_end;
|
||||
uintptr_t PhysicalStart = KernelStart - 0xFFFFFFFF80000000;
|
||||
for (uintptr_t i = KernelStart; i < KernelEnd; i += 0x1000)
|
||||
{
|
||||
MB2_64_Map((void *)i, (void *)PhysicalStart, 0x3);
|
||||
PhysicalStart += 0x1000;
|
||||
}
|
||||
|
||||
asmv("mov %%cr3, %%rax\n"
|
||||
"mov %%rax, %%cr3\n"
|
||||
:
|
||||
:
|
||||
: "rax");
|
||||
}
|
62
Kernel/arch/amd64/bootstrap/multiboot/Paging/mb_pt.s
Normal file
62
Kernel/arch/amd64/bootstrap/multiboot/Paging/mb_pt.s
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
PAGE_TABLE_SIZE = 0x4
|
||||
|
||||
.code32
|
||||
.section .bootstrap.data, "a"
|
||||
.align 0x1000
|
||||
.global BootPageTable
|
||||
BootPageTable:
|
||||
.space 0x10000 /* 0x4000 bytes will be used in UpdatePageTable */
|
||||
|
||||
.section .bootstrap.text, "a"
|
||||
.global UpdatePageTable
|
||||
UpdatePageTable:
|
||||
mov $(BootPageTable + 0x0000), %edi /* First PML4E */
|
||||
mov $(BootPageTable + 0x1000), %eax /* First PDPTE */
|
||||
or $0x3, %eax /* Bitwise OR on eax (PDPTE) with 11b (Present, Write) */
|
||||
mov %eax, (%edi) /* Write 11b to PML4E */
|
||||
|
||||
mov $(BootPageTable + 0x1000), %edi /* First PDPTE */
|
||||
mov $(BootPageTable + 0x2000), %eax /* First PDE */
|
||||
or $0x3, %eax /* Bitwise OR on eax (PDE) with 11b (Present, Write) */
|
||||
|
||||
mov $PAGE_TABLE_SIZE, %ecx /* For loop instruction */
|
||||
mov $0x0, %ebx /* Value to store in the next 4 bytes */
|
||||
.FillPageTableLevel3:
|
||||
mov %eax, (%edi) /* Store modified PDE in PDPTE */
|
||||
mov %ebx, 0x4(%edi) /* Store the ebx value in the next 4 bytes */
|
||||
add $0x1000, %eax /* Increment (page size) */
|
||||
adc $0x0, %ebx /* Add 0 to carry flag */
|
||||
add $0x8, %edi /* Add 8 to edi (next PDE) */
|
||||
loop .FillPageTableLevel3 /* Loop until ecx is 0 */
|
||||
|
||||
mov $(BootPageTable + 0x2000), %edi /* First PDE */
|
||||
mov $0x83, %eax /* Present, Write, Large Page */
|
||||
|
||||
mov $(512 * PAGE_TABLE_SIZE), %ecx /* For loop instruction */
|
||||
mov $0x0, %ebx /* Value to store in the next 4 bytes */
|
||||
.FillPageTableLevel2:
|
||||
mov %eax, (%edi) /* Store modified PDE in PDPTE */
|
||||
mov %ebx, 0x4(%edi) /* Store the ebx value in the next 4 bytes */
|
||||
add $0x200000, %eax /* Increment (page size) */
|
||||
adc $0x0, %ebx /* Add 0 (carry flag) to ebx to increment if there was a carry */
|
||||
add $0x8, %edi /* Add 8 to edi (next PDE) */
|
||||
loop .FillPageTableLevel2 /* Loop until ecx is 0 */
|
||||
|
||||
ret
|
38
Kernel/arch/amd64/bootstrap/multiboot/headers/header1.s
Normal file
38
Kernel/arch/amd64/bootstrap/multiboot/headers/header1.s
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.code32
|
||||
.extern Multiboot_start
|
||||
|
||||
.section .multiboot, "a"
|
||||
.align 4
|
||||
|
||||
MULTIBOOT_HEADER:
|
||||
.long 0x1BADB002
|
||||
.long 0x1 | 0x2 | 0x4
|
||||
.long -(0x1BADB002 + (0x1 | 0x2 | 0x4))
|
||||
/* KLUDGE */
|
||||
.long 0
|
||||
.long 0
|
||||
.long 0
|
||||
.long 0
|
||||
.long 0
|
||||
/* VIDEO MODE */
|
||||
.long 0
|
||||
.long 0
|
||||
.long 0
|
||||
.long 0
|
92
Kernel/arch/amd64/bootstrap/multiboot/headers/header2.s
Normal file
92
Kernel/arch/amd64/bootstrap/multiboot/headers/header2.s
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.code32
|
||||
.extern Multiboot_start
|
||||
|
||||
/* https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html */
|
||||
.section .multiboot2, "a"
|
||||
.align 0x1000
|
||||
MULTIBOOT2_HEADER_START:
|
||||
.long 0xE85250D6
|
||||
.long 0
|
||||
.long (MULTIBOOT2_HEADER_END - MULTIBOOT2_HEADER_START)
|
||||
.long 0x100000000 - (MULTIBOOT2_HEADER_END - MULTIBOOT2_HEADER_START) - 0 - 0xE85250D6
|
||||
.align 8
|
||||
InfoRequestTag_Start:
|
||||
.word 1
|
||||
.word 0
|
||||
.long InfoRequestTag_End - InfoRequestTag_Start
|
||||
.long 1 /* Command Line */
|
||||
.long 2 /* Boot Loader Name */
|
||||
.long 3 /* Module */
|
||||
.long 4 /* Basic Memory Information */
|
||||
.long 5 /* BIOS Boot Device */
|
||||
.long 6 /* Memory Map */
|
||||
.long 7 /* VBE */
|
||||
.long 8 /* Framebuffer */
|
||||
.long 9 /* ELF Sections */
|
||||
.long 10 /* APM Table */
|
||||
.long 11 /* EFI 32-bit System Table Pointer */
|
||||
.long 12 /* EFI 64-bit System Table Pointer */
|
||||
/* .long 13 */ /* SMBIOS */
|
||||
.long 14 /* ACPI Old */
|
||||
.long 15 /* ACPI New */
|
||||
.long 16 /* Network */
|
||||
.long 17 /* EFI Memory Map */
|
||||
.long 18 /* EFI Boot Services Notifier */
|
||||
.long 19 /* EFI 32-bit Image Handle Pointer */
|
||||
.long 20 /* EFI 64-bit Image Handle Pointer */
|
||||
.long 21 /* Load Base Address */
|
||||
InfoRequestTag_End:
|
||||
.align 8
|
||||
FramebufferTag_Start:
|
||||
.word 5
|
||||
.word 1
|
||||
.long FramebufferTag_End - FramebufferTag_Start
|
||||
.long 0
|
||||
.long 0
|
||||
.long 32
|
||||
FramebufferTag_End:
|
||||
.align 8
|
||||
EGATextSupportTag_Start:
|
||||
.word 4
|
||||
.word 0
|
||||
.long EGATextSupportTag_End - EGATextSupportTag_Start
|
||||
.long 0 /* https://www.gnu.org/software/grub/manual/multiboot2/html_node/Console-header-tags.html */
|
||||
EGATextSupportTag_End:
|
||||
.align 8
|
||||
AlignedModulesTag_Start:
|
||||
.word 6
|
||||
.word 0
|
||||
.long AlignedModulesTag_End - AlignedModulesTag_Start
|
||||
AlignedModulesTag_End:
|
||||
.align 8
|
||||
EntryAddressTag_Start:
|
||||
.word 3
|
||||
.word 0
|
||||
.long EntryAddressTag_End - EntryAddressTag_Start
|
||||
.long Multiboot_start
|
||||
EntryAddressTag_End:
|
||||
.align 8
|
||||
EndTag_Start:
|
||||
.word 0
|
||||
.word 0
|
||||
.long EndTag_End - EndTag_Start
|
||||
EndTag_End:
|
||||
MULTIBOOT2_HEADER_END:
|
||||
nop
|
210
Kernel/arch/amd64/bootstrap/multiboot/mb1_parse.cpp
Normal file
210
Kernel/arch/amd64/bootstrap/multiboot/mb1_parse.cpp
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
|
||||
#include <boot/protocol/multiboot.h>
|
||||
#include <memory.hpp>
|
||||
|
||||
#include "../../../../kernel.h"
|
||||
|
||||
void multiboot_parse(BootInfo &mb2binfo, uintptr_t Magic, uintptr_t Info)
|
||||
{
|
||||
multiboot_info *InfoAddress = r_cst(multiboot_info *, Info);
|
||||
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_MEMORY)
|
||||
{
|
||||
fixme("mem_lower: %#x, mem_upper: %#x",
|
||||
InfoAddress->mem_lower, InfoAddress->mem_upper);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_BOOTDEV)
|
||||
{
|
||||
fixme("boot_device: %#x",
|
||||
InfoAddress->boot_device);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_CMDLINE)
|
||||
{
|
||||
strncpy(mb2binfo.Kernel.CommandLine,
|
||||
r_cst(const char *, InfoAddress->cmdline),
|
||||
strlen(r_cst(const char *, InfoAddress->cmdline)));
|
||||
debug("Kernel command line: %s", mb2binfo.Kernel.CommandLine);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_MODS)
|
||||
{
|
||||
multiboot_mod_list *module = r_cst(multiboot_mod_list *, InfoAddress->mods_addr);
|
||||
for (size_t i = 0; i < InfoAddress->mods_count; i++)
|
||||
{
|
||||
if (i > MAX_MODULES)
|
||||
{
|
||||
warn("Too many modules, skipping the rest...");
|
||||
break;
|
||||
}
|
||||
mb2binfo.Modules[i].Address = (void *)(uint64_t)module[i].mod_start;
|
||||
mb2binfo.Modules[i].Size = module[i].mod_end - module[i].mod_start;
|
||||
strncpy(mb2binfo.Modules[i].Path, "(null)", 6);
|
||||
strncpy(mb2binfo.Modules[i].CommandLine, r_cst(const char *, module[i].cmdline),
|
||||
strlen(r_cst(const char *, module[i].cmdline)));
|
||||
debug("Module: %s", mb2binfo.Modules[i].Path);
|
||||
}
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_AOUT_SYMS)
|
||||
{
|
||||
fixme("aout_sym: [tabsize: %#x, strsize: %#x, addr: %#x, reserved: %#x]",
|
||||
InfoAddress->u.aout_sym.tabsize, InfoAddress->u.aout_sym.strsize,
|
||||
InfoAddress->u.aout_sym.addr, InfoAddress->u.aout_sym.reserved);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_ELF_SHDR)
|
||||
{
|
||||
mb2binfo.Kernel.Symbols.Num = InfoAddress->u.elf_sec.num;
|
||||
mb2binfo.Kernel.Symbols.EntSize = InfoAddress->u.elf_sec.size;
|
||||
mb2binfo.Kernel.Symbols.Shndx = InfoAddress->u.elf_sec.shndx;
|
||||
mb2binfo.Kernel.Symbols.Sections = s_cst(uintptr_t, InfoAddress->u.elf_sec.addr);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_MEM_MAP)
|
||||
{
|
||||
mb2binfo.Memory.Entries = InfoAddress->mmap_length / sizeof(multiboot_mmap_entry);
|
||||
for (uint32_t i = 0; i < mb2binfo.Memory.Entries; i++)
|
||||
{
|
||||
if (i > MAX_MEMORY_ENTRIES)
|
||||
{
|
||||
warn("Too many memory entries, skipping the rest...");
|
||||
break;
|
||||
}
|
||||
multiboot_mmap_entry entry = r_cst(multiboot_mmap_entry *, InfoAddress->mmap_addr)[i];
|
||||
mb2binfo.Memory.Size += entry.len;
|
||||
switch (entry.type)
|
||||
{
|
||||
case MULTIBOOT_MEMORY_AVAILABLE:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = Usable;
|
||||
break;
|
||||
case MULTIBOOT_MEMORY_RESERVED:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = Reserved;
|
||||
break;
|
||||
case MULTIBOOT_MEMORY_ACPI_RECLAIMABLE:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = ACPIReclaimable;
|
||||
break;
|
||||
case MULTIBOOT_MEMORY_NVS:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = ACPINVS;
|
||||
break;
|
||||
case MULTIBOOT_MEMORY_BADRAM:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = BadMemory;
|
||||
break;
|
||||
default:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = Unknown;
|
||||
break;
|
||||
}
|
||||
debug("Memory entry: [BaseAddress: %#x, Length: %#x, Type: %d]",
|
||||
mb2binfo.Memory.Entry[i].BaseAddress,
|
||||
mb2binfo.Memory.Entry[i].Length,
|
||||
mb2binfo.Memory.Entry[i].Type);
|
||||
}
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_DRIVE_INFO)
|
||||
{
|
||||
fixme("drives_length: %d, drives_addr: %#x",
|
||||
InfoAddress->drives_length, InfoAddress->drives_addr);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_CONFIG_TABLE)
|
||||
{
|
||||
fixme("config_table: %#x",
|
||||
InfoAddress->config_table);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_BOOT_LOADER_NAME)
|
||||
{
|
||||
strncpy(mb2binfo.Bootloader.Name,
|
||||
r_cst(const char *, InfoAddress->boot_loader_name),
|
||||
strlen(r_cst(const char *, InfoAddress->boot_loader_name)));
|
||||
debug("Bootloader name: %s", mb2binfo.Bootloader.Name);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_APM_TABLE)
|
||||
{
|
||||
fixme("apm_table: %#x",
|
||||
InfoAddress->apm_table);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_VBE_INFO)
|
||||
{
|
||||
fixme("vbe_control_info: %#x, vbe_mode_info: %#x, vbe_mode: %#x, vbe_interface_seg: %#x, vbe_interface_off: %#x, vbe_interface_len: %#x",
|
||||
InfoAddress->vbe_control_info, InfoAddress->vbe_mode_info,
|
||||
InfoAddress->vbe_mode, InfoAddress->vbe_interface_seg,
|
||||
InfoAddress->vbe_interface_off, InfoAddress->vbe_interface_len);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_FRAMEBUFFER_INFO)
|
||||
{
|
||||
static int fb_count = 0;
|
||||
mb2binfo.Framebuffer[fb_count].BaseAddress = (void *)InfoAddress->framebuffer_addr;
|
||||
mb2binfo.Framebuffer[fb_count].Width = InfoAddress->framebuffer_width;
|
||||
mb2binfo.Framebuffer[fb_count].Height = InfoAddress->framebuffer_height;
|
||||
mb2binfo.Framebuffer[fb_count].Pitch = InfoAddress->framebuffer_pitch;
|
||||
mb2binfo.Framebuffer[fb_count].BitsPerPixel = InfoAddress->framebuffer_bpp;
|
||||
switch (InfoAddress->framebuffer_type)
|
||||
{
|
||||
case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED:
|
||||
{
|
||||
mb2binfo.Framebuffer[fb_count].Type = Indexed;
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_FRAMEBUFFER_TYPE_RGB:
|
||||
{
|
||||
mb2binfo.Framebuffer[fb_count].Type = RGB;
|
||||
mb2binfo.Framebuffer[fb_count].RedMaskSize = InfoAddress->framebuffer_red_mask_size;
|
||||
mb2binfo.Framebuffer[fb_count].RedMaskShift = InfoAddress->framebuffer_red_field_position;
|
||||
mb2binfo.Framebuffer[fb_count].GreenMaskSize = InfoAddress->framebuffer_green_mask_size;
|
||||
mb2binfo.Framebuffer[fb_count].GreenMaskShift = InfoAddress->framebuffer_green_field_position;
|
||||
mb2binfo.Framebuffer[fb_count].BlueMaskSize = InfoAddress->framebuffer_blue_mask_size;
|
||||
mb2binfo.Framebuffer[fb_count].BlueMaskShift = InfoAddress->framebuffer_blue_field_position;
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT:
|
||||
{
|
||||
mb2binfo.Framebuffer[fb_count].Type = EGA;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
mb2binfo.Framebuffer[fb_count].Type = Unknown_Framebuffer_Type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
debug("Framebuffer %d: %dx%d %d bpp",
|
||||
fb_count, InfoAddress->framebuffer_width,
|
||||
InfoAddress->framebuffer_height,
|
||||
InfoAddress->framebuffer_bpp);
|
||||
debug("More info:\nAddress: %p\nPitch: %d\nMemoryModel: %d\nRedMaskSize: %d\nRedMaskShift: %d\nGreenMaskSize: %d\nGreenMaskShift: %d\nBlueMaskSize: %d\nBlueMaskShift: %d",
|
||||
InfoAddress->framebuffer_addr, InfoAddress->framebuffer_pitch, InfoAddress->framebuffer_type,
|
||||
InfoAddress->framebuffer_red_mask_size, InfoAddress->framebuffer_red_field_position, InfoAddress->framebuffer_green_mask_size,
|
||||
InfoAddress->framebuffer_green_field_position, InfoAddress->framebuffer_blue_mask_size, InfoAddress->framebuffer_blue_field_position);
|
||||
}
|
||||
|
||||
mb2binfo.Kernel.PhysicalBase = (void *)&_bootstrap_start;
|
||||
mb2binfo.Kernel.VirtualBase = (void *)(uint64_t)((uint64_t)&_bootstrap_start + 0xFFFFFFFF80000000);
|
||||
mb2binfo.Kernel.Size = ((uint64_t)&_kernel_end - (uint64_t)&_kernel_start) + ((uint64_t)&_bootstrap_end - (uint64_t)&_bootstrap_start);
|
||||
debug("Kernel base: %p (physical) %p (virtual)", mb2binfo.Kernel.PhysicalBase, mb2binfo.Kernel.VirtualBase);
|
||||
|
||||
Entry(&mb2binfo);
|
||||
}
|
284
Kernel/arch/amd64/bootstrap/multiboot/mb2_parse.cpp
Normal file
284
Kernel/arch/amd64/bootstrap/multiboot/mb2_parse.cpp
Normal file
@ -0,0 +1,284 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
|
||||
#include <boot/protocol/multiboot2.h>
|
||||
#include <memory.hpp>
|
||||
|
||||
#include "../../../../kernel.h"
|
||||
|
||||
void multiboot2_parse(BootInfo &mb2binfo, uintptr_t Magic, uintptr_t Info)
|
||||
{
|
||||
auto InfoAddress = Info;
|
||||
for (auto Tag = (struct multiboot_tag *)((uint8_t *)InfoAddress + 8);
|
||||
;
|
||||
Tag = (struct multiboot_tag *)((multiboot_uint8_t *)Tag + ((Tag->size + 7) & ~7)))
|
||||
{
|
||||
if (Tag->type == MULTIBOOT_TAG_TYPE_END)
|
||||
{
|
||||
debug("End of multiboot2 tags");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (Tag->type)
|
||||
{
|
||||
case MULTIBOOT_TAG_TYPE_CMDLINE:
|
||||
{
|
||||
strncpy(mb2binfo.Kernel.CommandLine,
|
||||
((multiboot_tag_string *)Tag)->string,
|
||||
strlen(((multiboot_tag_string *)Tag)->string));
|
||||
debug("Kernel command line: %s", mb2binfo.Kernel.CommandLine);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME:
|
||||
{
|
||||
strncpy(mb2binfo.Bootloader.Name,
|
||||
((multiboot_tag_string *)Tag)->string,
|
||||
strlen(((multiboot_tag_string *)Tag)->string));
|
||||
debug("Bootloader name: %s", mb2binfo.Bootloader.Name);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_MODULE:
|
||||
{
|
||||
multiboot_tag_module *module = (multiboot_tag_module *)Tag;
|
||||
static int module_count = 0;
|
||||
mb2binfo.Modules[module_count].Address = (void *)(uint64_t)module->mod_start;
|
||||
mb2binfo.Modules[module_count].Size = module->mod_end - module->mod_start;
|
||||
strncpy(mb2binfo.Modules[module_count].Path, "(null)", 6);
|
||||
strncpy(mb2binfo.Modules[module_count].CommandLine, module->cmdline,
|
||||
strlen(module->cmdline));
|
||||
debug("Module: %s", mb2binfo.Modules[module_count].Path);
|
||||
module_count++;
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO:
|
||||
{
|
||||
multiboot_tag_basic_meminfo *meminfo = (multiboot_tag_basic_meminfo *)Tag;
|
||||
fixme("basic_meminfo->[mem_lower: %#x, mem_upper: %#x]",
|
||||
meminfo->mem_lower, meminfo->mem_upper);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_BOOTDEV:
|
||||
{
|
||||
multiboot_tag_bootdev *bootdev = (multiboot_tag_bootdev *)Tag;
|
||||
fixme("bootdev->[biosdev: %#x, slice: %#x, part: %#x]",
|
||||
bootdev->biosdev, bootdev->slice, bootdev->part);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_MMAP:
|
||||
{
|
||||
multiboot_tag_mmap *mmap = (multiboot_tag_mmap *)Tag;
|
||||
size_t EntryCount = mmap->size / sizeof(multiboot_mmap_entry);
|
||||
mb2binfo.Memory.Entries = EntryCount;
|
||||
for (uint32_t i = 0; i < EntryCount; i++)
|
||||
{
|
||||
if (i > MAX_MEMORY_ENTRIES)
|
||||
{
|
||||
warn("Too many memory entries, skipping the rest...");
|
||||
break;
|
||||
}
|
||||
multiboot_mmap_entry entry = mmap->entries[i];
|
||||
mb2binfo.Memory.Size += entry.len;
|
||||
switch (entry.type)
|
||||
{
|
||||
case MULTIBOOT_MEMORY_AVAILABLE:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = Usable;
|
||||
break;
|
||||
case MULTIBOOT_MEMORY_RESERVED:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = Reserved;
|
||||
break;
|
||||
case MULTIBOOT_MEMORY_ACPI_RECLAIMABLE:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = ACPIReclaimable;
|
||||
break;
|
||||
case MULTIBOOT_MEMORY_NVS:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = ACPINVS;
|
||||
break;
|
||||
case MULTIBOOT_MEMORY_BADRAM:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = BadMemory;
|
||||
break;
|
||||
default:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = Unknown;
|
||||
break;
|
||||
}
|
||||
debug("Memory entry: [BaseAddress: %#x, Length: %#x, Type: %d]",
|
||||
mb2binfo.Memory.Entry[i].BaseAddress,
|
||||
mb2binfo.Memory.Entry[i].Length,
|
||||
mb2binfo.Memory.Entry[i].Type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_VBE:
|
||||
{
|
||||
multiboot_tag_vbe *vbe = (multiboot_tag_vbe *)Tag;
|
||||
fixme("vbe->[vbe_mode: %#x, vbe_interface_seg: %#x, vbe_interface_off: %#x, vbe_interface_len: %#x]",
|
||||
vbe->vbe_mode, vbe->vbe_interface_seg, vbe->vbe_interface_off, vbe->vbe_interface_len);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_FRAMEBUFFER:
|
||||
{
|
||||
multiboot_tag_framebuffer *fb = (multiboot_tag_framebuffer *)Tag;
|
||||
static int fb_count = 0;
|
||||
mb2binfo.Framebuffer[fb_count].BaseAddress = (void *)fb->common.framebuffer_addr;
|
||||
mb2binfo.Framebuffer[fb_count].Width = fb->common.framebuffer_width;
|
||||
mb2binfo.Framebuffer[fb_count].Height = fb->common.framebuffer_height;
|
||||
mb2binfo.Framebuffer[fb_count].Pitch = fb->common.framebuffer_pitch;
|
||||
mb2binfo.Framebuffer[fb_count].BitsPerPixel = fb->common.framebuffer_bpp;
|
||||
switch (fb->common.framebuffer_type)
|
||||
{
|
||||
case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED:
|
||||
{
|
||||
mb2binfo.Framebuffer[fb_count].Type = Indexed;
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_FRAMEBUFFER_TYPE_RGB:
|
||||
{
|
||||
mb2binfo.Framebuffer[fb_count].Type = RGB;
|
||||
mb2binfo.Framebuffer[fb_count].RedMaskSize = fb->framebuffer_red_mask_size;
|
||||
mb2binfo.Framebuffer[fb_count].RedMaskShift = fb->framebuffer_red_field_position;
|
||||
mb2binfo.Framebuffer[fb_count].GreenMaskSize = fb->framebuffer_green_mask_size;
|
||||
mb2binfo.Framebuffer[fb_count].GreenMaskShift = fb->framebuffer_green_field_position;
|
||||
mb2binfo.Framebuffer[fb_count].BlueMaskSize = fb->framebuffer_blue_mask_size;
|
||||
mb2binfo.Framebuffer[fb_count].BlueMaskShift = fb->framebuffer_blue_field_position;
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT:
|
||||
{
|
||||
mb2binfo.Framebuffer[fb_count].Type = EGA;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
mb2binfo.Framebuffer[fb_count].Type = Unknown_Framebuffer_Type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
debug("Framebuffer %d: %dx%d %d bpp", fb_count, fb->common.framebuffer_width, fb->common.framebuffer_height, fb->common.framebuffer_bpp);
|
||||
debug("More info:\nAddress: %p\nPitch: %d\nMemoryModel: %d\nRedMaskSize: %d\nRedMaskShift: %d\nGreenMaskSize: %d\nGreenMaskShift: %d\nBlueMaskSize: %d\nBlueMaskShift: %d",
|
||||
fb->common.framebuffer_addr, fb->common.framebuffer_pitch, fb->common.framebuffer_type,
|
||||
fb->framebuffer_red_mask_size, fb->framebuffer_red_field_position, fb->framebuffer_green_mask_size,
|
||||
fb->framebuffer_green_field_position, fb->framebuffer_blue_mask_size, fb->framebuffer_blue_field_position);
|
||||
fb_count++;
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_ELF_SECTIONS:
|
||||
{
|
||||
multiboot_tag_elf_sections *elf = (multiboot_tag_elf_sections *)Tag;
|
||||
mb2binfo.Kernel.Symbols.Num = elf->num;
|
||||
mb2binfo.Kernel.Symbols.EntSize = elf->entsize;
|
||||
mb2binfo.Kernel.Symbols.Shndx = elf->shndx;
|
||||
mb2binfo.Kernel.Symbols.Sections = r_cst(uintptr_t, elf->sections);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_APM:
|
||||
{
|
||||
multiboot_tag_apm *apm = (multiboot_tag_apm *)Tag;
|
||||
fixme("apm->[version: %d, cseg: %d, offset: %d, cseg_16: %d, dseg: %d, flags: %d, cseg_len: %d, cseg_16_len: %d, dseg_len: %d]",
|
||||
apm->version, apm->cseg, apm->offset, apm->cseg_16, apm->dseg, apm->flags, apm->cseg_len, apm->cseg_16_len, apm->dseg_len);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_EFI32:
|
||||
{
|
||||
multiboot_tag_efi32 *efi32 = (multiboot_tag_efi32 *)Tag;
|
||||
fixme("efi32->[pointer: %p, size: %d]", efi32->pointer, efi32->size);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_EFI64:
|
||||
{
|
||||
multiboot_tag_efi64 *efi64 = (multiboot_tag_efi64 *)Tag;
|
||||
fixme("efi64->[pointer: %p, size: %d]", efi64->pointer, efi64->size);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_SMBIOS:
|
||||
{
|
||||
multiboot_tag_smbios *smbios = (multiboot_tag_smbios *)Tag;
|
||||
fixme("smbios->[major: %d, minor: %d]", smbios->major, smbios->minor);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_ACPI_OLD:
|
||||
{
|
||||
mb2binfo.RSDP = (BootInfo::RSDPInfo *)((multiboot_tag_old_acpi *)Tag)->rsdp;
|
||||
debug("OLD ACPI RSDP: %p", mb2binfo.RSDP);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_ACPI_NEW:
|
||||
{
|
||||
mb2binfo.RSDP = (BootInfo::RSDPInfo *)((multiboot_tag_new_acpi *)Tag)->rsdp;
|
||||
debug("NEW ACPI RSDP: %p", mb2binfo.RSDP);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_NETWORK:
|
||||
{
|
||||
multiboot_tag_network *net = (multiboot_tag_network *)Tag;
|
||||
fixme("network->[dhcpack: %p]", net->dhcpack);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_EFI_MMAP:
|
||||
{
|
||||
multiboot_tag_efi_mmap *efi_mmap = (multiboot_tag_efi_mmap *)Tag;
|
||||
fixme("efi_mmap->[descr_size: %d, descr_vers: %d, efi_mmap: %p]",
|
||||
efi_mmap->descr_size, efi_mmap->descr_vers, efi_mmap->efi_mmap);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_EFI_BS:
|
||||
{
|
||||
fixme("efi_bs->[%p] (unknown structure)", Tag);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_EFI32_IH:
|
||||
{
|
||||
multiboot_tag_efi32_ih *efi32_ih = (multiboot_tag_efi32_ih *)Tag;
|
||||
fixme("efi32_ih->[pointer: %p]", efi32_ih->pointer);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_EFI64_IH:
|
||||
{
|
||||
multiboot_tag_efi64_ih *efi64_ih = (multiboot_tag_efi64_ih *)Tag;
|
||||
fixme("efi64_ih->[pointer: %p]", efi64_ih->pointer);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR:
|
||||
{
|
||||
multiboot_tag_load_base_addr *load_base_addr = (multiboot_tag_load_base_addr *)Tag;
|
||||
mb2binfo.Kernel.PhysicalBase = (void *)(uint64_t)load_base_addr->load_base_addr;
|
||||
mb2binfo.Kernel.VirtualBase = (void *)(uint64_t)(load_base_addr->load_base_addr + 0xFFFFFFFF80000000);
|
||||
mb2binfo.Kernel.Size = ((uint64_t)&_kernel_end - (uint64_t)&_kernel_start) + ((uint64_t)&_bootstrap_end - (uint64_t)&_bootstrap_start);
|
||||
debug("Kernel base: %p (physical) %p (virtual)", mb2binfo.Kernel.PhysicalBase, mb2binfo.Kernel.VirtualBase);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
error("Unknown multiboot2 tag type: %d", Tag->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Entry(&mb2binfo);
|
||||
}
|
53
Kernel/arch/amd64/bootstrap/multiboot/multiboot.cpp
Normal file
53
Kernel/arch/amd64/bootstrap/multiboot/multiboot.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
|
||||
#include <memory.hpp>
|
||||
|
||||
#include "../../../../kernel.h"
|
||||
|
||||
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
|
||||
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
|
||||
#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6
|
||||
#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289
|
||||
|
||||
void multiboot_parse(BootInfo &mb2binfo, uintptr_t Magic, uintptr_t Info);
|
||||
void multiboot2_parse(BootInfo &mb2binfo, uintptr_t Magic, uintptr_t Info);
|
||||
|
||||
EXTERNC void multiboot_main(uintptr_t Magic, uintptr_t Info)
|
||||
{
|
||||
BootInfo mb2binfo{};
|
||||
|
||||
if (Info == NULL || Magic == NULL)
|
||||
{
|
||||
if (Magic == NULL)
|
||||
error("Multiboot magic is NULL");
|
||||
if (Info == NULL)
|
||||
error("Multiboot info is NULL");
|
||||
CPU::Stop();
|
||||
}
|
||||
else if (Magic == MULTIBOOT_BOOTLOADER_MAGIC)
|
||||
multiboot_parse(mb2binfo, Magic, Info);
|
||||
else if (Magic == MULTIBOOT2_BOOTLOADER_MAGIC)
|
||||
multiboot2_parse(mb2binfo, Magic, Info);
|
||||
else
|
||||
{
|
||||
error("Unknown multiboot magic %#x", Magic);
|
||||
CPU::Stop();
|
||||
}
|
||||
}
|
124
Kernel/arch/amd64/bootstrap/multiboot/start.s
Normal file
124
Kernel/arch/amd64/bootstrap/multiboot/start.s
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.code32
|
||||
KERNEL_STACK_SIZE = 0x4000 /* 16KB */
|
||||
|
||||
.extern DetectCPUID
|
||||
.extern Detect64Bit
|
||||
.extern DetectPSE
|
||||
.extern DetectPAE
|
||||
.extern multiboot_main
|
||||
.extern LoadGDT32
|
||||
.extern BootPageTable
|
||||
.extern UpdatePageTable
|
||||
.extern GDT64.Ptr
|
||||
.extern GDT64.Code
|
||||
.extern GDT64.Data
|
||||
|
||||
.section .bootstrap.data, "a"
|
||||
MB_HeaderMagic:
|
||||
.quad 0
|
||||
|
||||
MB_HeaderInfo:
|
||||
.quad 0
|
||||
|
||||
.section .bootstrap.text, "a"
|
||||
|
||||
x32Hang:
|
||||
cli
|
||||
hlt
|
||||
jmp x32Hang
|
||||
|
||||
.global Multiboot_start
|
||||
Multiboot_start:
|
||||
cli
|
||||
|
||||
mov %eax, [MB_HeaderMagic]
|
||||
mov %ebx, [MB_HeaderInfo]
|
||||
|
||||
call DetectCPUID
|
||||
cmp $0, %eax
|
||||
je x32Hang
|
||||
|
||||
call Detect64Bit
|
||||
cmp $0, %eax
|
||||
je x32Hang
|
||||
|
||||
call DetectPSE
|
||||
cmp $0, %eax
|
||||
je x32Hang
|
||||
|
||||
call DetectPAE
|
||||
cmp $0, %eax
|
||||
je x32Hang
|
||||
|
||||
mov %cr4, %ecx
|
||||
or $0x00000010, %ecx /* PSE */
|
||||
or $0x00000020, %ecx /* PAE */
|
||||
mov %ecx, %cr4
|
||||
|
||||
call LoadGDT32
|
||||
call UpdatePageTable
|
||||
|
||||
mov $BootPageTable, %ecx
|
||||
mov %ecx, %cr3
|
||||
|
||||
mov $0xC0000080, %ecx /* EFER */
|
||||
rdmsr
|
||||
or $0x800, %eax /* LME */
|
||||
or $0x100, %eax /* LMA */
|
||||
or $0x1, %eax /* SCE */
|
||||
wrmsr
|
||||
|
||||
mov %cr0, %ecx
|
||||
or $0x80000000, %ecx /* PG */
|
||||
or $0x1, %ecx /* PE */
|
||||
mov %ecx, %cr0
|
||||
|
||||
lgdt [GDT64.Ptr]
|
||||
ljmp $GDT64.Code, $HigherHalfStart
|
||||
|
||||
.extern UpdatePageTable64
|
||||
|
||||
.code64
|
||||
HigherHalfStart:
|
||||
mov $GDT64.Data, %ax
|
||||
mov %ax, %ds
|
||||
mov %ax, %es
|
||||
mov %ax, %fs
|
||||
mov %ax, %gs
|
||||
mov %ax, %ss
|
||||
|
||||
call UpdatePageTable64
|
||||
|
||||
mov $(KernelStack + KERNEL_STACK_SIZE), %rsp
|
||||
mov $0x0, %rbp
|
||||
|
||||
mov [MB_HeaderMagic], %rdi
|
||||
mov [MB_HeaderInfo], %rsi
|
||||
push %rsi
|
||||
push %rdi
|
||||
call multiboot_main
|
||||
.Hang:
|
||||
hlt
|
||||
jmp .Hang
|
||||
|
||||
.section .bootstrap.bss, "a"
|
||||
.align 16
|
||||
KernelStack:
|
||||
.space KERNEL_STACK_SIZE
|
487
Kernel/arch/amd64/cpu/apic.cpp
Normal file
487
Kernel/arch/amd64/cpu/apic.cpp
Normal file
@ -0,0 +1,487 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "apic.hpp"
|
||||
|
||||
#include <memory.hpp>
|
||||
#include <acpi.hpp>
|
||||
#include <uart.hpp>
|
||||
#include <lock.hpp>
|
||||
#include <cpu.hpp>
|
||||
#include <smp.hpp>
|
||||
#include <io.h>
|
||||
|
||||
#include "../../../kernel.h"
|
||||
|
||||
NewLock(APICLock);
|
||||
|
||||
using namespace CPU::x64;
|
||||
using namespace CPU::x86;
|
||||
|
||||
/*
|
||||
In constructor 'APIC::APIC::APIC(int)':
|
||||
warning: left shift count >= width of type
|
||||
| APICBaseAddress = BaseStruct.ApicBaseLo << 12u | BaseStruct.ApicBaseHi << 32u;
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~^~~~~~
|
||||
*/
|
||||
#pragma GCC diagnostic ignored "-Wshift-count-overflow"
|
||||
|
||||
namespace APIC
|
||||
{
|
||||
// headache
|
||||
// https://www.amd.com/system/files/TechDocs/24593.pdf
|
||||
// https://www.naic.edu/~phil/software/intel/318148.pdf
|
||||
|
||||
uint32_t APIC::Read(uint32_t Register)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (Register != APIC_ICRLO &&
|
||||
Register != APIC_ICRHI &&
|
||||
Register != APIC_ID)
|
||||
debug("APIC::Read(%#lx) [x2=%d]",
|
||||
Register, x2APICSupported ? 1 : 0);
|
||||
#endif
|
||||
if (unlikely(x2APICSupported))
|
||||
assert(!"x2APIC is not supported");
|
||||
|
||||
CPU::MemBar::Barrier();
|
||||
uint32_t ret = *((volatile uint32_t *)((uintptr_t)APICBaseAddress + Register));
|
||||
CPU::MemBar::Barrier();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void APIC::Write(uint32_t Register, uint32_t Value)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (Register != APIC_EOI &&
|
||||
Register != APIC_TDCR &&
|
||||
Register != APIC_TIMER &&
|
||||
Register != APIC_TICR &&
|
||||
Register != APIC_ICRLO &&
|
||||
Register != APIC_ICRHI)
|
||||
debug("APIC::Write(%#lx, %#lx) [x2=%d]",
|
||||
Register, Value, x2APICSupported ? 1 : 0);
|
||||
#endif
|
||||
if (unlikely(x2APICSupported))
|
||||
assert(!"x2APIC is not supported");
|
||||
|
||||
CPU::MemBar::Barrier();
|
||||
*((volatile uint32_t *)(((uintptr_t)APICBaseAddress) + Register)) = Value;
|
||||
CPU::MemBar::Barrier();
|
||||
}
|
||||
|
||||
void APIC::IOWrite(uint64_t Base, uint32_t Register, uint32_t Value)
|
||||
{
|
||||
debug("APIC::IOWrite(%#lx, %#lx, %#lx)", Base, Register, Value);
|
||||
CPU::MemBar::Barrier();
|
||||
*((volatile uint32_t *)(((uintptr_t)Base))) = Register;
|
||||
CPU::MemBar::Barrier();
|
||||
*((volatile uint32_t *)(((uintptr_t)Base + 16))) = Value;
|
||||
CPU::MemBar::Barrier();
|
||||
}
|
||||
|
||||
uint32_t APIC::IORead(uint64_t Base, uint32_t Register)
|
||||
{
|
||||
debug("APIC::IORead(%#lx, %#lx)", Base, Register);
|
||||
CPU::MemBar::Barrier();
|
||||
*((volatile uint32_t *)(((uintptr_t)Base))) = Register;
|
||||
CPU::MemBar::Barrier();
|
||||
uint32_t ret = *((volatile uint32_t *)(((uintptr_t)Base + 16)));
|
||||
CPU::MemBar::Barrier();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void APIC::EOI()
|
||||
{
|
||||
Memory::SwapPT swap =
|
||||
Memory::SwapPT(KernelPageTable, thisPageTable);
|
||||
if (this->x2APICSupported)
|
||||
wrmsr(MSR_X2APIC_EOI, 0);
|
||||
else
|
||||
this->Write(APIC_EOI, 0);
|
||||
}
|
||||
|
||||
void APIC::WaitForIPI()
|
||||
{
|
||||
if (this->x2APICSupported)
|
||||
{
|
||||
ErrorStatusRegister esr{};
|
||||
esr.raw = uint32_t(rdmsr(MSR_X2APIC_ESR));
|
||||
UNUSED(esr);
|
||||
/* FIXME: Not sure if this is required or
|
||||
how to implement it. */
|
||||
}
|
||||
else
|
||||
{
|
||||
InterruptCommandRegister icr{};
|
||||
do
|
||||
{
|
||||
icr.split.Low = this->Read(APIC_ICRLO);
|
||||
CPU::Pause();
|
||||
} while (icr.DS != Idle);
|
||||
}
|
||||
}
|
||||
|
||||
void APIC::ICR(InterruptCommandRegister icr)
|
||||
{
|
||||
SmartCriticalSection(APICLock);
|
||||
if (x2APICSupported)
|
||||
{
|
||||
assert(icr.MT != LowestPriority);
|
||||
assert(icr.MT != DeliveryMode);
|
||||
assert(icr.MT != ExtINT);
|
||||
wrmsr(MSR_X2APIC_ICR, icr.raw);
|
||||
this->WaitForIPI();
|
||||
}
|
||||
else
|
||||
{
|
||||
this->Write(APIC_ICRHI, icr.split.High);
|
||||
this->Write(APIC_ICRLO, icr.split.Low);
|
||||
this->WaitForIPI();
|
||||
}
|
||||
}
|
||||
|
||||
void APIC::SendInitIPI(int CPU)
|
||||
{
|
||||
SmartCriticalSection(APICLock);
|
||||
InterruptCommandRegister icr{};
|
||||
|
||||
if (x2APICSupported)
|
||||
{
|
||||
icr.x2.MT = INIT;
|
||||
icr.x2.L = Assert;
|
||||
icr.x2.DES = uint8_t(CPU);
|
||||
|
||||
wrmsr(MSR_X2APIC_ICR, icr.raw);
|
||||
this->WaitForIPI();
|
||||
}
|
||||
else
|
||||
{
|
||||
icr.MT = INIT;
|
||||
icr.L = Assert;
|
||||
icr.DES = uint8_t(CPU);
|
||||
|
||||
this->Write(APIC_ICRHI, icr.split.High);
|
||||
this->Write(APIC_ICRLO, icr.split.Low);
|
||||
this->WaitForIPI();
|
||||
}
|
||||
}
|
||||
|
||||
void APIC::SendStartupIPI(int CPU, uint64_t StartupAddress)
|
||||
{
|
||||
SmartCriticalSection(APICLock);
|
||||
InterruptCommandRegister icr{};
|
||||
|
||||
if (x2APICSupported)
|
||||
{
|
||||
icr.x2.VEC = s_cst(uint8_t, StartupAddress >> 12);
|
||||
icr.x2.MT = Startup;
|
||||
icr.x2.L = Assert;
|
||||
icr.x2.DES = uint8_t(CPU);
|
||||
|
||||
wrmsr(MSR_X2APIC_ICR, icr.raw);
|
||||
this->WaitForIPI();
|
||||
}
|
||||
else
|
||||
{
|
||||
icr.VEC = s_cst(uint8_t, StartupAddress >> 12);
|
||||
icr.MT = Startup;
|
||||
icr.L = Assert;
|
||||
icr.DES = uint8_t(CPU);
|
||||
|
||||
this->Write(APIC_ICRHI, icr.split.High);
|
||||
this->Write(APIC_ICRLO, icr.split.Low);
|
||||
this->WaitForIPI();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t APIC::IOGetMaxRedirect(uint32_t APICID)
|
||||
{
|
||||
ACPI::MADT::MADTIOApic *ioapic = ((ACPI::MADT *)PowerManager->GetMADT())->ioapic[APICID];
|
||||
uint32_t TableAddress = (this->IORead(ioapic->Address, GetIOAPICVersion));
|
||||
IOAPICVersion ver = {.raw = TableAddress};
|
||||
return ver.MLE + 1;
|
||||
}
|
||||
|
||||
void APIC::RawRedirectIRQ(uint8_t Vector, uint32_t GSI, uint16_t Flags, uint8_t CPU, int Status)
|
||||
{
|
||||
int64_t IOAPICTarget = -1;
|
||||
ACPI::MADT *madt = (ACPI::MADT *)PowerManager->GetMADT();
|
||||
for (size_t i = 0; i < madt->ioapic.size(); i++)
|
||||
{
|
||||
if (madt->ioapic[i]->GSIBase <= GSI)
|
||||
{
|
||||
if (madt->ioapic[i]->GSIBase + IOGetMaxRedirect(uint32_t(i)) > GSI)
|
||||
{
|
||||
IOAPICTarget = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IOAPICTarget == -1)
|
||||
{
|
||||
error("No ISO table found for I/O APIC");
|
||||
return;
|
||||
}
|
||||
|
||||
IOAPICRedirectEntry Entry{};
|
||||
Entry.VEC = Vector;
|
||||
Entry.DES = CPU;
|
||||
|
||||
if (Flags & ActiveHighLow)
|
||||
Entry.IPP = 1;
|
||||
|
||||
if (Flags & EdgeLevel)
|
||||
Entry.TGM = 1;
|
||||
|
||||
if (!Status)
|
||||
Entry.M = 1;
|
||||
|
||||
uint32_t IORegister = (GSI - madt->ioapic[IOAPICTarget]->GSIBase) * 2 + 16;
|
||||
this->IOWrite(madt->ioapic[IOAPICTarget]->Address,
|
||||
IORegister, Entry.split.Low);
|
||||
this->IOWrite(madt->ioapic[IOAPICTarget]->Address,
|
||||
IORegister + 1, Entry.split.High);
|
||||
}
|
||||
|
||||
void APIC::RedirectIRQ(uint8_t CPU, uint8_t IRQ, int Status)
|
||||
{
|
||||
ACPI::MADT *madt = (ACPI::MADT *)PowerManager->GetMADT();
|
||||
for (uint64_t i = 0; i < madt->iso.size(); i++)
|
||||
if (madt->iso[i]->IRQSource == IRQ)
|
||||
{
|
||||
debug("[ISO %d] Mapping to source IRQ%#d GSI:%#lx on CPU %d",
|
||||
i, madt->iso[i]->IRQSource, madt->iso[i]->GSI, CPU);
|
||||
|
||||
this->RawRedirectIRQ(madt->iso[i]->IRQSource + 0x20,
|
||||
madt->iso[i]->GSI,
|
||||
madt->iso[i]->Flags,
|
||||
CPU, Status);
|
||||
return;
|
||||
}
|
||||
debug("Mapping IRQ%d on CPU %d", IRQ, CPU);
|
||||
this->RawRedirectIRQ(IRQ + 0x20, IRQ, 0, CPU, Status);
|
||||
}
|
||||
|
||||
void APIC::RedirectIRQs(uint8_t CPU)
|
||||
{
|
||||
SmartCriticalSection(APICLock);
|
||||
debug("Redirecting IRQs...");
|
||||
for (uint8_t i = 0; i < 16; i++)
|
||||
this->RedirectIRQ(CPU, i, 1);
|
||||
debug("Redirecting IRQs completed.");
|
||||
}
|
||||
|
||||
APIC::APIC(int Core)
|
||||
{
|
||||
SmartCriticalSection(APICLock);
|
||||
APIC_BASE BaseStruct = {.raw = rdmsr(MSR_APIC_BASE)};
|
||||
uint64_t BaseLow = BaseStruct.ABALow;
|
||||
uint64_t BaseHigh = BaseStruct.ABAHigh;
|
||||
this->APICBaseAddress = BaseLow << 12u | BaseHigh << 32u;
|
||||
trace("APIC Address: %#lx", this->APICBaseAddress);
|
||||
Memory::Virtual().Map((void *)this->APICBaseAddress,
|
||||
(void *)this->APICBaseAddress,
|
||||
Memory::RW | Memory::PCD);
|
||||
|
||||
if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0)
|
||||
{
|
||||
CPU::x86::AMD::CPUID0x00000001 cpuid;
|
||||
if (cpuid.ECX.x2APIC)
|
||||
{
|
||||
this->x2APICSupported = cpuid.ECX.x2APIC;
|
||||
debug("x2APIC is supported");
|
||||
}
|
||||
}
|
||||
else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0)
|
||||
{
|
||||
CPU::x86::Intel::CPUID0x00000001 cpuid;
|
||||
if (cpuid.ECX.x2APIC)
|
||||
{
|
||||
this->x2APICSupported = cpuid.ECX.x2APIC;
|
||||
debug("x2APIC is supported");
|
||||
}
|
||||
}
|
||||
|
||||
BaseStruct.AE = 1;
|
||||
wrmsr(MSR_APIC_BASE, BaseStruct.raw);
|
||||
|
||||
if (this->x2APICSupported)
|
||||
{
|
||||
BaseStruct.EXTD = 1;
|
||||
wrmsr(MSR_APIC_BASE, BaseStruct.raw);
|
||||
}
|
||||
|
||||
if (!this->x2APICSupported)
|
||||
{
|
||||
this->Write(APIC_TPR, 0x0);
|
||||
this->Write(APIC_DFR, 0xF0000000);
|
||||
this->Write(APIC_LDR, this->Read(APIC_ID));
|
||||
}
|
||||
else
|
||||
{
|
||||
wrmsr(MSR_X2APIC_TPR, 0x0);
|
||||
}
|
||||
|
||||
ACPI::MADT *madt = (ACPI::MADT *)PowerManager->GetMADT();
|
||||
|
||||
for (size_t i = 0; i < madt->nmi.size(); i++)
|
||||
{
|
||||
if (madt->nmi[i]->processor != 0xFF &&
|
||||
Core != madt->nmi[i]->processor)
|
||||
break;
|
||||
|
||||
uint32_t nmi = 0x402;
|
||||
if (madt->nmi[i]->flags & 2)
|
||||
nmi |= 1 << 13;
|
||||
if (madt->nmi[i]->flags & 8)
|
||||
nmi |= 1 << 15;
|
||||
if (madt->nmi[i]->lint == 0)
|
||||
{
|
||||
if (this->x2APICSupported)
|
||||
wrmsr(MSR_X2APIC_LVT_LINT0, nmi);
|
||||
else
|
||||
this->Write(APIC_LINT0, nmi);
|
||||
}
|
||||
else if (madt->nmi[i]->lint == 1)
|
||||
{
|
||||
if (this->x2APICSupported)
|
||||
wrmsr(MSR_X2APIC_LVT_LINT1, nmi);
|
||||
else
|
||||
this->Write(APIC_LINT1, nmi);
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup the spurious interrupt vector */
|
||||
Spurious svr{};
|
||||
if (this->x2APICSupported)
|
||||
svr.raw = uint32_t(rdmsr(MSR_X2APIC_SIVR));
|
||||
else
|
||||
svr.raw = this->Read(APIC_SVR);
|
||||
|
||||
svr.VEC = IRQ223;
|
||||
svr.ASE = 1;
|
||||
if (this->x2APICSupported)
|
||||
wrmsr(MSR_X2APIC_SIVR, svr.raw);
|
||||
else
|
||||
this->Write(APIC_SVR, svr.raw);
|
||||
|
||||
static int once = 0;
|
||||
if (!once++)
|
||||
{
|
||||
// Disable PIT
|
||||
outb(0x43, 0x28);
|
||||
outb(0x40, 0x0);
|
||||
|
||||
// Disable PIC
|
||||
outb(0x21, 0xFF);
|
||||
outb(0xA1, 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
APIC::~APIC() {}
|
||||
|
||||
void Timer::OnInterruptReceived(CPU::TrapFrame *) {}
|
||||
|
||||
void Timer::OneShot(uint32_t Vector, uint64_t Miliseconds)
|
||||
{
|
||||
/* FIXME: Sometimes APIC stops firing when debugging, why? */
|
||||
LVTTimer timer{};
|
||||
timer.VEC = uint8_t(Vector);
|
||||
timer.TMM = LVTTimerMode::OneShot;
|
||||
|
||||
LVTTimerDivide Divider = DivideBy8;
|
||||
|
||||
SmartCriticalSection(APICLock);
|
||||
if (this->lapic->x2APIC)
|
||||
{
|
||||
// wrmsr(MSR_X2APIC_DIV_CONF, Divider); <- gpf on real hardware
|
||||
wrmsr(MSR_X2APIC_INIT_COUNT, uint32_t(Ticks * Miliseconds));
|
||||
wrmsr(MSR_X2APIC_LVT_TIMER, uint32_t(timer.raw));
|
||||
}
|
||||
else
|
||||
{
|
||||
this->lapic->Write(APIC_TDCR, Divider);
|
||||
this->lapic->Write(APIC_TICR, uint32_t(Ticks * Miliseconds));
|
||||
this->lapic->Write(APIC_TIMER, uint32_t(timer.raw));
|
||||
}
|
||||
}
|
||||
|
||||
Timer::Timer(APIC *apic) : Interrupts::Handler(0) /* IRQ0 */
|
||||
{
|
||||
SmartCriticalSection(APICLock);
|
||||
this->lapic = apic;
|
||||
LVTTimerDivide Divider = DivideBy8;
|
||||
|
||||
trace("Initializing APIC timer on CPU %d",
|
||||
GetCurrentCPU()->ID);
|
||||
|
||||
if (this->lapic->x2APIC)
|
||||
{
|
||||
wrmsr(MSR_X2APIC_DIV_CONF, Divider);
|
||||
wrmsr(MSR_X2APIC_INIT_COUNT, 0xFFFFFFFF);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->lapic->Write(APIC_TDCR, Divider);
|
||||
this->lapic->Write(APIC_TICR, 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
TimeManager->Sleep(1, Time::Units::Milliseconds);
|
||||
|
||||
// Mask the timer
|
||||
if (this->lapic->x2APIC)
|
||||
{
|
||||
wrmsr(MSR_X2APIC_LVT_TIMER, 0x10000 /* LVTTimer.Mask flag */);
|
||||
Ticks = 0xFFFFFFFF - rdmsr(MSR_X2APIC_CUR_COUNT);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->lapic->Write(APIC_TIMER, 0x10000 /* LVTTimer.Mask flag */);
|
||||
Ticks = 0xFFFFFFFF - this->lapic->Read(APIC_TCCR);
|
||||
}
|
||||
|
||||
// Config for IRQ0 timer
|
||||
LVTTimer timer{};
|
||||
timer.VEC = IRQ0;
|
||||
timer.M = Unmasked;
|
||||
timer.TMM = LVTTimerMode::OneShot;
|
||||
|
||||
// Initialize APIC timer
|
||||
if (this->lapic->x2APIC)
|
||||
{
|
||||
wrmsr(MSR_X2APIC_DIV_CONF, Divider);
|
||||
wrmsr(MSR_X2APIC_INIT_COUNT, Ticks);
|
||||
wrmsr(MSR_X2APIC_LVT_TIMER, timer.raw);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->lapic->Write(APIC_TDCR, Divider);
|
||||
this->lapic->Write(APIC_TICR, uint32_t(Ticks));
|
||||
this->lapic->Write(APIC_TIMER, uint32_t(timer.raw));
|
||||
}
|
||||
|
||||
trace("%d APIC Timer %d ticks in.",
|
||||
GetCurrentCPU()->ID, Ticks);
|
||||
KPrint("APIC Timer: \x1b[1;32m%ld\x1b[0m ticks.", Ticks);
|
||||
}
|
||||
|
||||
Timer::~Timer()
|
||||
{
|
||||
}
|
||||
}
|
386
Kernel/arch/amd64/cpu/apic.hpp
Normal file
386
Kernel/arch/amd64/cpu/apic.hpp
Normal file
@ -0,0 +1,386 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __FENNIX_KERNEL_APIC_H__
|
||||
#define __FENNIX_KERNEL_APIC_H__
|
||||
|
||||
#include <types.h>
|
||||
|
||||
#include <ints.hpp>
|
||||
#include <cpu.hpp>
|
||||
|
||||
namespace APIC
|
||||
{
|
||||
enum APICRegisters
|
||||
{
|
||||
/* APIC ID Register */
|
||||
APIC_ID = 0x20,
|
||||
/* APIC Version Register */
|
||||
APIC_VER = 0x30,
|
||||
/* Task Priority Register (TPR) */
|
||||
APIC_TPR = 0x80,
|
||||
/* Arbitration Priority Register (APR) */
|
||||
APIC_APR = 0x90,
|
||||
/* Processor Priority Register (PPR) */
|
||||
APIC_PPR = 0xA0,
|
||||
/* End of Interrupt Register (EOI) */
|
||||
APIC_EOI = 0xB0,
|
||||
/* Remote Read Register */
|
||||
APIC_RRD = 0xC0,
|
||||
/* Logical Destination Register (LDR) */
|
||||
APIC_LDR = 0xD0,
|
||||
/* Destination Format Register (DFR) */
|
||||
APIC_DFR = 0xE0,
|
||||
/* Spurious Interrupt Vector Register */
|
||||
APIC_SVR = 0xF0,
|
||||
/* In-Service Register (ISR) */
|
||||
APIC_ISR = 0x100,
|
||||
/* Trigger Mode Register (TMR) */
|
||||
APIC_TMR = 0x180,
|
||||
/* Interrupt Request Register (IRR) */
|
||||
APIC_IRR = 0x200,
|
||||
/* Error Status Register (ESR) */
|
||||
APIC_ESR = 0x280,
|
||||
/* Interrupt Command Register Low (bits 31:0) */
|
||||
APIC_ICRLO = 0x300,
|
||||
/* Interrupt Command Register High (bits 63:32) */
|
||||
APIC_ICRHI = 0x310,
|
||||
/* Timer Local Vector Table Entry */
|
||||
APIC_TIMER = 0x320,
|
||||
/* Thermal Local Vector Table Entry */
|
||||
APIC_THERMAL = 0x330,
|
||||
/* Performance Counter Local Vector Table Entry */
|
||||
APIC_PERF = 0x340,
|
||||
/* Local Interrupt 0 Vector Table Entry */
|
||||
APIC_LINT0 = 0x350,
|
||||
/* Local Interrupt 1 Vector Table Entry */
|
||||
APIC_LINT1 = 0x360,
|
||||
/* Error Vector Table Entry */
|
||||
APIC_ERROR = 0x370,
|
||||
/* Timer Initial Count Register */
|
||||
APIC_TICR = 0x380,
|
||||
/* Timer Current Count Register */
|
||||
APIC_TCCR = 0x390,
|
||||
/* Timer Divide Configuration Register */
|
||||
APIC_TDCR = 0x3E0,
|
||||
/* Extended APIC Feature Register */
|
||||
APIC_EFR = 0x400,
|
||||
/* Extended APIC Control Register */
|
||||
APIC_ECR = 0x410,
|
||||
/* Specific End of Interrupt Register (SEOI) */
|
||||
APIC_SEOI = 0x420,
|
||||
/* Interrupt Enable Registers (IER) */
|
||||
APIC_IER0 = 0x480,
|
||||
/* Extended Interrupt [3:0] Local Vector Table Registers */
|
||||
APIC_EILVT0 = 0x500,
|
||||
};
|
||||
|
||||
enum IOAPICRegisters
|
||||
{
|
||||
GetIOAPICVersion = 0x1
|
||||
};
|
||||
|
||||
enum IOAPICFlags
|
||||
{
|
||||
ActiveHighLow = 2,
|
||||
EdgeLevel = 8
|
||||
};
|
||||
|
||||
enum APICMessageType
|
||||
{
|
||||
Fixed = 0b000,
|
||||
LowestPriority = 0b001, /* Reserved */
|
||||
SMI = 0b010,
|
||||
DeliveryMode = 0b011, /* Reserved */
|
||||
NMI = 0b100,
|
||||
INIT = 0b101,
|
||||
Startup = 0b110,
|
||||
ExtINT = 0b111 /* Reserved */
|
||||
};
|
||||
|
||||
enum APICDestinationMode
|
||||
{
|
||||
Physical = 0b0,
|
||||
Logical = 0b1
|
||||
};
|
||||
|
||||
enum APICDeliveryStatus
|
||||
{
|
||||
Idle = 0b0,
|
||||
SendPending = 0b1
|
||||
};
|
||||
|
||||
enum APICLevel
|
||||
{
|
||||
DeAssert = 0b0,
|
||||
Assert = 0b1
|
||||
};
|
||||
|
||||
enum APICTriggerMode
|
||||
{
|
||||
Edge = 0b0,
|
||||
Level = 0b1
|
||||
};
|
||||
|
||||
enum APICDestinationShorthand
|
||||
{
|
||||
NoShorthand = 0b00,
|
||||
Self = 0b01,
|
||||
AllIncludingSelf = 0b10,
|
||||
AllExcludingSelf = 0b11
|
||||
};
|
||||
|
||||
enum LVTTimerDivide
|
||||
{
|
||||
DivideBy2 = 0b000,
|
||||
DivideBy4 = 0b001,
|
||||
DivideBy8 = 0b010,
|
||||
DivideBy16 = 0b011,
|
||||
DivideBy32 = 0b100,
|
||||
DivideBy64 = 0b101,
|
||||
DivideBy128 = 0b110,
|
||||
DivideBy1 = 0b111
|
||||
};
|
||||
|
||||
enum LVTTimerMask
|
||||
{
|
||||
Unmasked = 0b0,
|
||||
Masked = 0b1
|
||||
};
|
||||
|
||||
enum LVTTimerMode
|
||||
{
|
||||
OneShot = 0b00,
|
||||
Periodic = 0b01,
|
||||
TSCDeadline = 0b10
|
||||
};
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
/** Vector */
|
||||
uint64_t VEC : 8;
|
||||
/** Reserved */
|
||||
uint64_t Reserved0 : 4;
|
||||
/** Delivery Status */
|
||||
uint64_t DS : 1;
|
||||
/** Reserved */
|
||||
uint64_t Reserved1 : 3;
|
||||
/** Mask */
|
||||
uint64_t M : 1;
|
||||
/** Timer Mode */
|
||||
uint64_t TMM : 1;
|
||||
/** Reserved */
|
||||
uint64_t Reserved2 : 14;
|
||||
};
|
||||
uint32_t raw;
|
||||
} __packed LVTTimer;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
/** Vector */
|
||||
uint64_t VEC : 8;
|
||||
/** APIC Software Enable */
|
||||
uint64_t ASE : 1;
|
||||
/** Focus CPU Core Checking */
|
||||
uint64_t FCC : 1;
|
||||
/** Reserved */
|
||||
uint64_t Reserved0 : 22;
|
||||
};
|
||||
uint32_t raw;
|
||||
} __packed Spurious;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
/** Vector */
|
||||
uint64_t VEC : 8;
|
||||
/** Message Type */
|
||||
uint64_t MT : 3;
|
||||
/** Destination Mode */
|
||||
uint64_t DM : 1;
|
||||
/** Delivery Status */
|
||||
uint64_t DS : 1;
|
||||
/** Reserved */
|
||||
uint64_t Reserved0 : 1;
|
||||
/** Level */
|
||||
uint64_t L : 1;
|
||||
/** Trigger Mode */
|
||||
uint64_t TGM : 1;
|
||||
/** Remote Read Status */
|
||||
uint64_t RSS : 2;
|
||||
/** Destination Shorthand */
|
||||
uint64_t DSH : 2;
|
||||
/** Reserved */
|
||||
uint64_t Reserved2 : 36;
|
||||
/** Destination */
|
||||
uint64_t DES : 8;
|
||||
};
|
||||
struct
|
||||
{
|
||||
/** Vector */
|
||||
uint64_t VEC : 8;
|
||||
/** Message Type */
|
||||
uint64_t MT : 3;
|
||||
/** Destination Mode */
|
||||
uint64_t DM : 1;
|
||||
/** Reserved */
|
||||
uint64_t Reserved0 : 2;
|
||||
/** Level */
|
||||
uint64_t L : 1;
|
||||
/** Trigger Mode */
|
||||
uint64_t TGM : 1;
|
||||
/** Reserved */
|
||||
uint64_t Reserved1 : 2;
|
||||
/** Destination Shorthand */
|
||||
uint64_t DSH : 2;
|
||||
/** Reserved */
|
||||
uint64_t Reserved2 : 12;
|
||||
/** Destination */
|
||||
uint64_t DES : 32;
|
||||
} x2;
|
||||
struct
|
||||
{
|
||||
uint32_t Low;
|
||||
uint32_t High;
|
||||
} split;
|
||||
uint64_t raw;
|
||||
} __packed InterruptCommandRegister;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
/** Reserved */
|
||||
uint64_t Reserved0 : 2;
|
||||
/** Sent Accept Error */
|
||||
uint64_t SAE : 1;
|
||||
/** Receive Accept Error */
|
||||
uint64_t RAE : 1;
|
||||
/** Reserved */
|
||||
uint64_t Reserved1 : 1;
|
||||
/** Sent Illegal Vector */
|
||||
uint64_t SIV : 1;
|
||||
/** Received Illegal Vector */
|
||||
uint64_t RIV : 1;
|
||||
/** Illegal Register Address */
|
||||
uint64_t IRA : 1;
|
||||
/** Reserved */
|
||||
uint64_t Reserved2 : 24;
|
||||
};
|
||||
uint32_t raw;
|
||||
} ErrorStatusRegister;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
/** Interrupt Vector */
|
||||
uint64_t VEC : 8;
|
||||
/** Delivery Mode */
|
||||
uint64_t MT : 3;
|
||||
/** Destination Mode */
|
||||
uint64_t DM : 1;
|
||||
/** Delivery Status */
|
||||
uint64_t DS : 1;
|
||||
/** Interrupt Input Pin Polarity */
|
||||
uint64_t IPP : 1;
|
||||
/** Remote IRR */
|
||||
uint64_t RIR : 1;
|
||||
/** Trigger Mode */
|
||||
uint64_t TGM : 1;
|
||||
/** Mask */
|
||||
uint64_t M : 1;
|
||||
/** Reserved */
|
||||
uint64_t Reserved0 : 15;
|
||||
/** Reserved */
|
||||
uint64_t Reserved1 : 24;
|
||||
/** Destination */
|
||||
uint64_t DES : 8;
|
||||
};
|
||||
struct
|
||||
{
|
||||
uint32_t Low;
|
||||
uint32_t High;
|
||||
} split;
|
||||
uint64_t raw;
|
||||
} __packed IOAPICRedirectEntry;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
/** Version */
|
||||
uint64_t VER : 8;
|
||||
/** Reserved */
|
||||
uint64_t Reserved0 : 8;
|
||||
/** Max LVT Entries */
|
||||
uint64_t MLE : 8;
|
||||
/** Reserved */
|
||||
uint64_t Reserved1 : 7;
|
||||
/** Extended APIC Register Space Present */
|
||||
uint64_t EAS : 1;
|
||||
};
|
||||
uint32_t raw;
|
||||
} __packed IOAPICVersion;
|
||||
|
||||
class APIC
|
||||
{
|
||||
private:
|
||||
bool x2APICSupported = false;
|
||||
uint64_t APICBaseAddress = 0;
|
||||
|
||||
public:
|
||||
decltype(x2APICSupported) &x2APIC = x2APICSupported;
|
||||
|
||||
uint32_t Read(uint32_t Register);
|
||||
void Write(uint32_t Register, uint32_t Value);
|
||||
void IOWrite(uint64_t Base, uint32_t Register, uint32_t Value);
|
||||
uint32_t IORead(uint64_t Base, uint32_t Register);
|
||||
void EOI();
|
||||
void RedirectIRQs(uint8_t CPU = 0);
|
||||
void WaitForIPI();
|
||||
void ICR(InterruptCommandRegister icr);
|
||||
void SendInitIPI(int CPU);
|
||||
void SendStartupIPI(int CPU, uint64_t StartupAddress);
|
||||
uint32_t IOGetMaxRedirect(uint32_t APICID);
|
||||
void RawRedirectIRQ(uint8_t Vector, uint32_t GSI, uint16_t Flags, uint8_t CPU, int Status);
|
||||
void RedirectIRQ(uint8_t CPU, uint8_t IRQ, int Status);
|
||||
APIC(int Core);
|
||||
~APIC();
|
||||
};
|
||||
|
||||
class Timer : public Interrupts::Handler
|
||||
{
|
||||
private:
|
||||
APIC *lapic;
|
||||
uint64_t Ticks = 0;
|
||||
void OnInterruptReceived(CPU::TrapFrame *Frame);
|
||||
|
||||
public:
|
||||
uint64_t GetTicks() { return Ticks; }
|
||||
void OneShot(uint32_t Vector, uint64_t Miliseconds);
|
||||
Timer(APIC *apic);
|
||||
~Timer();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // !__FENNIX_KERNEL_APIC_H__
|
215
Kernel/arch/amd64/cpu/gdt.cpp
Normal file
215
Kernel/arch/amd64/cpu/gdt.cpp
Normal file
@ -0,0 +1,215 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gdt.hpp"
|
||||
|
||||
#include <memory.hpp>
|
||||
#include <smp.hpp>
|
||||
#include <cpu.hpp>
|
||||
#include <debug.h>
|
||||
|
||||
namespace GlobalDescriptorTable
|
||||
{
|
||||
static GlobalDescriptorTableEntries GDTEntriesTemplate = {
|
||||
.Null = 0,
|
||||
.Code = {
|
||||
.SegmentLimitLow = 0xFFFF,
|
||||
.BaseAddressLow = 0x0,
|
||||
.BaseAddressHigh = 0x0,
|
||||
.Accessed = 0,
|
||||
.Readable = 1,
|
||||
.Conforming = 0,
|
||||
.Executable = 1,
|
||||
.Type = 1,
|
||||
.DescriptorPrivilegeLevel = 0,
|
||||
.Present = 1,
|
||||
.SegmentLimitHigh = 0xF,
|
||||
.Available = 0,
|
||||
.Long = 1,
|
||||
.Default = 0,
|
||||
.Granularity = 1,
|
||||
.BaseAddressHigher = 0x0,
|
||||
},
|
||||
|
||||
.Data = {
|
||||
.SegmentLimitLow = 0xFFFF,
|
||||
.BaseAddressLow = 0x0,
|
||||
.BaseAddressHigh = 0x0,
|
||||
.Accessed = 0,
|
||||
.Writable = 1,
|
||||
.ExpandDown = 0,
|
||||
.Executable = 0,
|
||||
.Type = 1,
|
||||
.DescriptorPrivilegeLevel = 0,
|
||||
.Present = 1,
|
||||
.SegmentLimitHigh = 0xF,
|
||||
.Available = 0,
|
||||
.Reserved = 0,
|
||||
.Default = 0,
|
||||
.Granularity = 1,
|
||||
.BaseAddressHigher = 0x0,
|
||||
},
|
||||
|
||||
.UserData = {
|
||||
.SegmentLimitLow = 0xFFFF,
|
||||
.BaseAddressLow = 0x0,
|
||||
.BaseAddressHigh = 0x0,
|
||||
.Accessed = 0,
|
||||
.Writable = 1,
|
||||
.ExpandDown = 1,
|
||||
.Executable = 0,
|
||||
.Type = 1,
|
||||
.DescriptorPrivilegeLevel = 3,
|
||||
.Present = 1,
|
||||
.SegmentLimitHigh = 0xF,
|
||||
.Available = 0,
|
||||
.Reserved = 0,
|
||||
.Default = 0,
|
||||
.Granularity = 1,
|
||||
.BaseAddressHigher = 0x0,
|
||||
},
|
||||
|
||||
.UserCode = {
|
||||
.SegmentLimitLow = 0xFFFF,
|
||||
.BaseAddressLow = 0x0,
|
||||
.BaseAddressHigh = 0x0,
|
||||
.Accessed = 0,
|
||||
.Readable = 1,
|
||||
.Conforming = 0,
|
||||
.Executable = 1,
|
||||
.Type = 1,
|
||||
.DescriptorPrivilegeLevel = 3,
|
||||
.Present = 1,
|
||||
.SegmentLimitHigh = 0xF,
|
||||
.Available = 0,
|
||||
.Long = 1,
|
||||
.Default = 0,
|
||||
.Granularity = 1,
|
||||
.BaseAddressHigher = 0x0,
|
||||
},
|
||||
|
||||
.TaskStateSegment{},
|
||||
};
|
||||
|
||||
GlobalDescriptorTableEntries GDTEntries[MAX_CPU] __aligned(16);
|
||||
GlobalDescriptorTableDescriptor gdt[MAX_CPU] __aligned(16);
|
||||
|
||||
TaskStateSegment tss[MAX_CPU] = {
|
||||
0,
|
||||
{0, 0, 0},
|
||||
0,
|
||||
{0, 0, 0, 0, 0, 0, 0},
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
};
|
||||
|
||||
void *CPUStackPointer[MAX_CPU];
|
||||
|
||||
nsa void Init(int Core)
|
||||
{
|
||||
GDTEntries[Core] = GDTEntriesTemplate;
|
||||
gdt[Core] =
|
||||
{
|
||||
.Limit = sizeof(GlobalDescriptorTableEntries) - 1,
|
||||
.BaseAddress = &GDTEntries[Core],
|
||||
};
|
||||
|
||||
debug("GDT: %#lx", &gdt[Core]);
|
||||
debug("GDT KERNEL CODE %#lx", GDT_KERNEL_CODE);
|
||||
debug("GDT KERNEL DATA %#lx", GDT_KERNEL_DATA);
|
||||
debug("GDT USER CODE %#lx", GDT_USER_CODE);
|
||||
debug("GDT USER DATA %#lx", GDT_USER_DATA);
|
||||
debug("GDT TSS %#lx", GDT_TSS);
|
||||
|
||||
CPU::x64::lgdt(&gdt[Core]);
|
||||
|
||||
asmv("movq %%rsp, %%rax\n"
|
||||
"pushq $16\n"
|
||||
"pushq %%rax\n"
|
||||
"pushfq\n"
|
||||
"pushq $8\n"
|
||||
"pushq $1f\n"
|
||||
"iretq\n"
|
||||
"1:\n"
|
||||
"movw $16, %%ax\n"
|
||||
"movw %%ax, %%ds\n"
|
||||
"movw %%ax, %%es\n" ::
|
||||
: "memory", "rax");
|
||||
|
||||
CPUStackPointer[Core] = StackManager.Allocate(STACK_SIZE);
|
||||
memset(CPUStackPointer[Core], 0, STACK_SIZE);
|
||||
debug("CPU %d Stack Pointer: %#lx-%#lx (%d pages)", Core,
|
||||
CPUStackPointer[Core], (uintptr_t)CPUStackPointer[Core] + STACK_SIZE,
|
||||
TO_PAGES(STACK_SIZE + 1));
|
||||
|
||||
uintptr_t Base = (uintptr_t)&tss[Core];
|
||||
size_t Limit = Base + sizeof(TaskStateSegment);
|
||||
SystemSegmentDescriptor *tssDesc = &gdt[Core].BaseAddress->TaskStateSegment;
|
||||
tssDesc->SegmentLimitLow = Limit & 0xFFFF;
|
||||
tssDesc->BaseAddressLow = Base & 0xFFFF;
|
||||
tssDesc->BaseAddressMiddle = (Base >> 16) & 0xFF;
|
||||
tssDesc->Type = AVAILABLE_64BIT_TSS;
|
||||
tssDesc->Zero0 = 0;
|
||||
tssDesc->DescriptorPrivilegeLevel = 0;
|
||||
tssDesc->Present = 1;
|
||||
tssDesc->Available = 0;
|
||||
tssDesc->Reserved0 = 0;
|
||||
tssDesc->Granularity = 0;
|
||||
tssDesc->BaseAddressHigh = (Base >> 24) & 0xFF;
|
||||
tssDesc->BaseAddressHigher = s_cst(uint32_t, (Base >> 32) & 0xFFFFFFFF);
|
||||
tssDesc->Reserved1 = 0;
|
||||
tssDesc->Zero1 = 0;
|
||||
tssDesc->Reserved2 = 0;
|
||||
|
||||
tss[Core].IOMapBaseAddressOffset = sizeof(TaskStateSegment);
|
||||
tss[Core].StackPointer[0] = (uint64_t)CPUStackPointer[Core] + STACK_SIZE;
|
||||
tss[Core].StackPointer[1] = 0x0;
|
||||
tss[Core].StackPointer[2] = 0x0;
|
||||
|
||||
for (size_t i = 0; i < sizeof(tss[Core].InterruptStackTable) / sizeof(tss[Core].InterruptStackTable[7]); i++)
|
||||
{
|
||||
void *NewStack = StackManager.Allocate(STACK_SIZE);
|
||||
tss[Core].InterruptStackTable[i] = (uint64_t)NewStack + STACK_SIZE;
|
||||
memset((void *)(tss[Core].InterruptStackTable[i] - STACK_SIZE), 0, STACK_SIZE);
|
||||
debug("IST-%d: %#lx-%#lx", i, NewStack, (uintptr_t)NewStack + STACK_SIZE);
|
||||
}
|
||||
|
||||
CPU::x64::ltr(GDT_TSS);
|
||||
debug("Global Descriptor Table initialized");
|
||||
}
|
||||
|
||||
nsa void SetKernelStack(void *Stack)
|
||||
{
|
||||
long CPUID = GetCurrentCPU()->ID;
|
||||
if (Stack != nullptr)
|
||||
tss[CPUID].StackPointer[0] = (uint64_t)Stack;
|
||||
else
|
||||
tss[CPUID].StackPointer[0] = (uint64_t)CPUStackPointer[CPUID] + STACK_SIZE;
|
||||
|
||||
/*
|
||||
FIXME: There's a bug in kernel which if
|
||||
we won't update "tss[CPUID].StackPointer[0]"
|
||||
with the current stack pointer, the kernel
|
||||
will crash.
|
||||
*/
|
||||
asmv("mov %%rsp, %0"
|
||||
: "=r"(tss[CPUID].StackPointer[0]));
|
||||
}
|
||||
|
||||
void *GetKernelStack() { return (void *)tss[GetCurrentCPU()->ID].StackPointer[0]; }
|
||||
}
|
67
Kernel/arch/amd64/cpu/gdt.hpp
Normal file
67
Kernel/arch/amd64/cpu/gdt.hpp
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __FENNIX_KERNEL_GDT_H__
|
||||
#define __FENNIX_KERNEL_GDT_H__
|
||||
|
||||
#include <types.h>
|
||||
#include <cpu/x86/x64/SegmentDescriptors.hpp>
|
||||
|
||||
namespace GlobalDescriptorTable
|
||||
{
|
||||
|
||||
struct TaskStateSegment
|
||||
{
|
||||
uint32_t Reserved0 __aligned(16);
|
||||
uint64_t StackPointer[3];
|
||||
uint64_t Reserved1;
|
||||
uint64_t InterruptStackTable[7];
|
||||
uint64_t Reserved2;
|
||||
uint16_t Reserved3;
|
||||
uint16_t IOMapBaseAddressOffset;
|
||||
} __packed;
|
||||
|
||||
struct GlobalDescriptorTableEntries
|
||||
{
|
||||
uint64_t Null;
|
||||
CodeSegmentDescriptor Code;
|
||||
DataSegmentDescriptor Data;
|
||||
DataSegmentDescriptor UserData;
|
||||
CodeSegmentDescriptor UserCode;
|
||||
SystemSegmentDescriptor TaskStateSegment;
|
||||
} __packed;
|
||||
|
||||
struct GlobalDescriptorTableDescriptor
|
||||
{
|
||||
uint16_t Limit;
|
||||
GlobalDescriptorTableEntries *BaseAddress;
|
||||
} __packed;
|
||||
|
||||
extern void *CPUStackPointer[];
|
||||
extern TaskStateSegment tss[];
|
||||
void Init(int Core);
|
||||
void SetKernelStack(void *Stack);
|
||||
void *GetKernelStack();
|
||||
}
|
||||
|
||||
#define GDT_KERNEL_CODE offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, Code)
|
||||
#define GDT_KERNEL_DATA offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, Data)
|
||||
#define GDT_USER_CODE (offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, UserCode) | 3)
|
||||
#define GDT_USER_DATA (offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, UserData) | 3)
|
||||
#define GDT_TSS (offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, TaskStateSegment) | 3)
|
||||
|
||||
#endif // !__FENNIX_KERNEL_GDT_H__
|
916
Kernel/arch/amd64/cpu/idt.cpp
Normal file
916
Kernel/arch/amd64/cpu/idt.cpp
Normal file
@ -0,0 +1,916 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "idt.hpp"
|
||||
|
||||
#include <memory.hpp>
|
||||
#include <cpu.hpp>
|
||||
#include <debug.h>
|
||||
#include <io.h>
|
||||
|
||||
#include "gdt.hpp"
|
||||
#include "../../../kernel.h"
|
||||
|
||||
/* conversion from 'uint64_t' {aka 'long unsigned int'} to 'unsigned char:2' may change value */
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
|
||||
extern "C" void MainInterruptHandler(void *Data);
|
||||
extern "C" void SchedulerInterruptHandler(void *Data);
|
||||
extern "C" void ExceptionHandler(void *Data);
|
||||
|
||||
#define __stub_handler \
|
||||
__naked __used __no_stack_protector __aligned(16)
|
||||
|
||||
namespace InterruptDescriptorTable
|
||||
{
|
||||
__aligned(8) static IDTGateDescriptor Entries[0x100];
|
||||
|
||||
__aligned(8) IDTRegister IDTr = {
|
||||
.Limit = sizeof(Entries) - 1,
|
||||
.BaseAddress = Entries,
|
||||
};
|
||||
|
||||
void SetEntry(uint8_t Index, void (*Base)(),
|
||||
InterruptStackTableType InterruptStackTable,
|
||||
GateType Gate, PrivilegeLevelType Ring,
|
||||
bool Present, uint16_t SegmentSelector)
|
||||
{
|
||||
switch (Gate)
|
||||
{
|
||||
case CALL_GATE_64BIT:
|
||||
{
|
||||
CallGate gate{
|
||||
.TargetOffsetLow = s_cst(uint16_t, ((uint64_t)Base & 0xFFFF)),
|
||||
.TargetSelector = SegmentSelector,
|
||||
.Reserved0 = 0,
|
||||
.Type = Gate,
|
||||
.Zero0 = 0,
|
||||
.DescriptorPrivilegeLevel = Ring,
|
||||
.Present = Present,
|
||||
.TargetOffsetMiddle = s_cst(uint16_t, ((uint64_t)Base >> 16)),
|
||||
.TargetOffsetHigh = s_cst(uint32_t, ((uint64_t)Base >> 32)),
|
||||
.Reserved1 = 0,
|
||||
.Zero1 = 0,
|
||||
.Reserved2 = 0,
|
||||
};
|
||||
Entries[Index].Call = gate;
|
||||
break;
|
||||
}
|
||||
case INTERRUPT_GATE_64BIT:
|
||||
{
|
||||
InterruptGate gate{
|
||||
.TargetOffsetLow = s_cst(uint16_t, ((uint64_t)Base & 0xFFFF)),
|
||||
.TargetSelector = SegmentSelector,
|
||||
.InterruptStackTable = InterruptStackTable,
|
||||
.Reserved0 = 0,
|
||||
.Type = Gate,
|
||||
.Zero = 0,
|
||||
.DescriptorPrivilegeLevel = Ring,
|
||||
.Present = Present,
|
||||
.TargetOffsetMiddle = s_cst(uint16_t, ((uint64_t)Base >> 16)),
|
||||
.TargetOffsetHigh = s_cst(uint32_t, ((uint64_t)Base >> 32)),
|
||||
.Reserved1 = 0,
|
||||
};
|
||||
Entries[Index].Interrupt = gate;
|
||||
break;
|
||||
}
|
||||
case TRAP_GATE_64BIT:
|
||||
{
|
||||
TrapGate gate{
|
||||
.TargetOffsetLow = s_cst(uint16_t, ((uint64_t)Base & 0xFFFF)),
|
||||
.TargetSelector = SegmentSelector,
|
||||
.InterruptStackTable = InterruptStackTable,
|
||||
.Reserved0 = 0,
|
||||
.Type = Gate,
|
||||
.Zero = 0,
|
||||
.DescriptorPrivilegeLevel = Ring,
|
||||
.Present = Present,
|
||||
.TargetOffsetMiddle = s_cst(uint16_t, ((uint64_t)Base >> 16)),
|
||||
.TargetOffsetHigh = s_cst(uint32_t, ((uint64_t)Base >> 32)),
|
||||
.Reserved1 = 0,
|
||||
};
|
||||
Entries[Index].Trap = gate;
|
||||
break;
|
||||
}
|
||||
case LDT_64BIT:
|
||||
case AVAILABLE_64BIT_TSS:
|
||||
case BUSY_64BIT_TSS:
|
||||
default:
|
||||
{
|
||||
assert(!"Unsupported gate type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __stub_handler void ExceptionHandlerStub()
|
||||
{
|
||||
asm("cld\n"
|
||||
"cli\n"
|
||||
|
||||
"pushq %rax\n"
|
||||
"pushq %rbx\n"
|
||||
"pushq %rcx\n"
|
||||
"pushq %rdx\n"
|
||||
"pushq %rsi\n"
|
||||
"pushq %rdi\n"
|
||||
"pushq %rbp\n"
|
||||
|
||||
"pushq %r8\n"
|
||||
"pushq %r9\n"
|
||||
"pushq %r10\n"
|
||||
"pushq %r11\n"
|
||||
"pushq %r12\n"
|
||||
"pushq %r13\n"
|
||||
"pushq %r14\n"
|
||||
"pushq %r15\n"
|
||||
|
||||
"movq %ds, %rax\n pushq %rax\n"
|
||||
"movq %es, %rax\n pushq %rax\n"
|
||||
"movq %fs, %rax\n pushq %rax\n"
|
||||
"movq %gs, %rax\n pushq %rax\n"
|
||||
|
||||
"movq %dr7, %rax\n pushq %rax\n"
|
||||
"movq %dr6, %rax\n pushq %rax\n"
|
||||
"movq %dr3, %rax\n pushq %rax\n"
|
||||
"movq %dr2, %rax\n pushq %rax\n"
|
||||
"movq %dr1, %rax\n pushq %rax\n"
|
||||
"movq %dr0, %rax\n pushq %rax\n"
|
||||
|
||||
"movq %cr8, %rax\n pushq %rax\n"
|
||||
"movq %cr4, %rax\n pushq %rax\n"
|
||||
"movq %cr3, %rax\n pushq %rax\n"
|
||||
"movq %cr2, %rax\n pushq %rax\n"
|
||||
"movq %cr0, %rax\n pushq %rax\n"
|
||||
|
||||
"movq %rsp, %rdi\n"
|
||||
"call ExceptionHandler\n"
|
||||
|
||||
"popq %rax\n movq %rax, %cr0\n"
|
||||
"popq %rax\n movq %rax, %cr2\n"
|
||||
"popq %rax\n movq %rax, %cr3\n"
|
||||
"popq %rax\n movq %rax, %cr4\n"
|
||||
"popq %rax\n movq %rax, %cr8\n"
|
||||
|
||||
"popq %rax\n movq %rax, %dr0\n"
|
||||
"popq %rax\n movq %rax, %dr1\n"
|
||||
"popq %rax\n movq %rax, %dr2\n"
|
||||
"popq %rax\n movq %rax, %dr3\n"
|
||||
"popq %rax\n movq %rax, %dr6\n"
|
||||
"popq %rax\n movq %rax, %dr7\n"
|
||||
|
||||
"popq %rax\n movq %rax, %gs\n"
|
||||
"popq %rax\n movq %rax, %fs\n"
|
||||
"popq %rax\n movq %rax, %es\n"
|
||||
"popq %rax\n movq %rax, %ds\n"
|
||||
|
||||
"popq %r15\n"
|
||||
"popq %r14\n"
|
||||
"popq %r13\n"
|
||||
"popq %r12\n"
|
||||
"popq %r11\n"
|
||||
"popq %r10\n"
|
||||
"popq %r9\n"
|
||||
"popq %r8\n"
|
||||
|
||||
"popq %rbp\n"
|
||||
"popq %rdi\n"
|
||||
"popq %rsi\n"
|
||||
"popq %rdx\n"
|
||||
"popq %rcx\n"
|
||||
"popq %rbx\n"
|
||||
"popq %rax\n"
|
||||
|
||||
"addq $16, %rsp\n"
|
||||
|
||||
"iretq"); // pop CS RIP RFLAGS SS RSP
|
||||
}
|
||||
|
||||
extern "C" __stub_handler void InterruptHandlerStub()
|
||||
{
|
||||
asm("cld\n"
|
||||
"cli\n"
|
||||
|
||||
"pushq %rax\n"
|
||||
"pushq %rbx\n"
|
||||
"pushq %rcx\n"
|
||||
"pushq %rdx\n"
|
||||
"pushq %rsi\n"
|
||||
"pushq %rdi\n"
|
||||
"pushq %rbp\n"
|
||||
|
||||
"pushq %r8\n"
|
||||
"pushq %r9\n"
|
||||
"pushq %r10\n"
|
||||
"pushq %r11\n"
|
||||
"pushq %r12\n"
|
||||
"pushq %r13\n"
|
||||
"pushq %r14\n"
|
||||
"pushq %r15\n"
|
||||
|
||||
"movq %rsp, %rdi\n"
|
||||
"call MainInterruptHandler\n"
|
||||
|
||||
"popq %r15\n"
|
||||
"popq %r14\n"
|
||||
"popq %r13\n"
|
||||
"popq %r12\n"
|
||||
"popq %r11\n"
|
||||
"popq %r10\n"
|
||||
"popq %r9\n"
|
||||
"popq %r8\n"
|
||||
|
||||
"popq %rbp\n"
|
||||
"popq %rdi\n"
|
||||
"popq %rsi\n"
|
||||
"popq %rdx\n"
|
||||
"popq %rcx\n"
|
||||
"popq %rbx\n"
|
||||
"popq %rax\n"
|
||||
|
||||
"addq $16, %rsp\n"
|
||||
|
||||
"sti\n"
|
||||
"iretq"); // pop CS RIP RFLAGS SS RSP
|
||||
}
|
||||
|
||||
extern "C" __stub_handler void SchedulerHandlerStub()
|
||||
{
|
||||
asm("cld\n"
|
||||
"cli\n"
|
||||
|
||||
"pushq %rax\n"
|
||||
"pushq %rbx\n"
|
||||
"pushq %rcx\n"
|
||||
"pushq %rdx\n"
|
||||
"pushq %rsi\n"
|
||||
"pushq %rdi\n"
|
||||
"pushq %rbp\n"
|
||||
|
||||
"pushq %r8\n"
|
||||
"pushq %r9\n"
|
||||
"pushq %r10\n"
|
||||
"pushq %r11\n"
|
||||
"pushq %r12\n"
|
||||
"pushq %r13\n"
|
||||
"pushq %r14\n"
|
||||
"pushq %r15\n"
|
||||
|
||||
/* TODO: Add advanced check so we won't update the cr3 when not needed */
|
||||
|
||||
"movq %cr3, %rax\n pushq %rax\n" /* Push opt */
|
||||
"pushq %rax\n" /* Push ppt */
|
||||
|
||||
"movq %rsp, %rdi\n"
|
||||
"call SchedulerInterruptHandler\n"
|
||||
|
||||
"popq %rax\n movq %rax, %cr3\n" /* Restore to ppt */
|
||||
"popq %rax\n" /* Pop opt */
|
||||
|
||||
"popq %r15\n"
|
||||
"popq %r14\n"
|
||||
"popq %r13\n"
|
||||
"popq %r12\n"
|
||||
"popq %r11\n"
|
||||
"popq %r10\n"
|
||||
"popq %r9\n"
|
||||
"popq %r8\n"
|
||||
|
||||
"popq %rbp\n"
|
||||
"popq %rdi\n"
|
||||
"popq %rsi\n"
|
||||
"popq %rdx\n"
|
||||
"popq %rcx\n"
|
||||
"popq %rbx\n"
|
||||
"popq %rax\n"
|
||||
|
||||
"addq $16, %rsp\n"
|
||||
|
||||
"sti\n"
|
||||
"iretq"); // pop CS RIP RFLAGS SS RSP
|
||||
}
|
||||
|
||||
#pragma region Interrupt Macros
|
||||
|
||||
#define EXCEPTION_HANDLER(num) \
|
||||
__stub_handler static void InterruptHandler_##num() \
|
||||
{ \
|
||||
asm("pushq $0\n" \
|
||||
"pushq $" #num "\n" \
|
||||
"jmp ExceptionHandlerStub"); \
|
||||
}
|
||||
|
||||
#define EXCEPTION_ERROR_HANDLER(num) \
|
||||
__stub_handler static void InterruptHandler_##num() \
|
||||
{ \
|
||||
asm("pushq $" #num "\n" \
|
||||
"jmp ExceptionHandlerStub"); \
|
||||
}
|
||||
|
||||
#define INTERRUPT_HANDLER(num) \
|
||||
__stub_handler void InterruptHandler_##num() \
|
||||
{ \
|
||||
asm("pushq $0\n" \
|
||||
"pushq $" #num "\n" \
|
||||
"jmp InterruptHandlerStub\n"); \
|
||||
}
|
||||
|
||||
#define SCHEDULER_HANDLER(num) \
|
||||
__stub_handler void InterruptHandler_##num() \
|
||||
{ \
|
||||
asm("pushq $0\n" \
|
||||
"pushq $" #num "\n" \
|
||||
"jmp SchedulerHandlerStub\n"); \
|
||||
}
|
||||
|
||||
/* ISR */
|
||||
|
||||
EXCEPTION_HANDLER(0x0);
|
||||
EXCEPTION_HANDLER(0x1);
|
||||
EXCEPTION_HANDLER(0x2);
|
||||
EXCEPTION_HANDLER(0x3);
|
||||
EXCEPTION_HANDLER(0x4);
|
||||
EXCEPTION_HANDLER(0x5);
|
||||
EXCEPTION_HANDLER(0x6);
|
||||
EXCEPTION_HANDLER(0x7);
|
||||
EXCEPTION_ERROR_HANDLER(0x8);
|
||||
EXCEPTION_HANDLER(0x9);
|
||||
EXCEPTION_ERROR_HANDLER(0xa);
|
||||
EXCEPTION_ERROR_HANDLER(0xb);
|
||||
EXCEPTION_ERROR_HANDLER(0xc);
|
||||
EXCEPTION_ERROR_HANDLER(0xd);
|
||||
EXCEPTION_ERROR_HANDLER(0xe);
|
||||
EXCEPTION_HANDLER(0xf);
|
||||
EXCEPTION_ERROR_HANDLER(0x10);
|
||||
EXCEPTION_HANDLER(0x11);
|
||||
EXCEPTION_HANDLER(0x12);
|
||||
EXCEPTION_HANDLER(0x13);
|
||||
EXCEPTION_HANDLER(0x14);
|
||||
EXCEPTION_HANDLER(0x15);
|
||||
EXCEPTION_HANDLER(0x16);
|
||||
EXCEPTION_HANDLER(0x17);
|
||||
EXCEPTION_HANDLER(0x18);
|
||||
EXCEPTION_HANDLER(0x19);
|
||||
EXCEPTION_HANDLER(0x1a);
|
||||
EXCEPTION_HANDLER(0x1b);
|
||||
EXCEPTION_HANDLER(0x1c);
|
||||
EXCEPTION_HANDLER(0x1d);
|
||||
EXCEPTION_HANDLER(0x1e);
|
||||
EXCEPTION_HANDLER(0x1f);
|
||||
|
||||
/* IRQ */
|
||||
|
||||
INTERRUPT_HANDLER(0x20)
|
||||
INTERRUPT_HANDLER(0x21)
|
||||
INTERRUPT_HANDLER(0x22)
|
||||
INTERRUPT_HANDLER(0x23)
|
||||
INTERRUPT_HANDLER(0x24)
|
||||
INTERRUPT_HANDLER(0x25)
|
||||
INTERRUPT_HANDLER(0x26)
|
||||
INTERRUPT_HANDLER(0x27)
|
||||
INTERRUPT_HANDLER(0x28)
|
||||
INTERRUPT_HANDLER(0x29)
|
||||
INTERRUPT_HANDLER(0x2a)
|
||||
INTERRUPT_HANDLER(0x2b)
|
||||
INTERRUPT_HANDLER(0x2c)
|
||||
INTERRUPT_HANDLER(0x2d)
|
||||
INTERRUPT_HANDLER(0x2e)
|
||||
INTERRUPT_HANDLER(0x2f)
|
||||
|
||||
/* Reserved by OS */
|
||||
|
||||
SCHEDULER_HANDLER(0x30)
|
||||
INTERRUPT_HANDLER(0x31)
|
||||
INTERRUPT_HANDLER(0x32)
|
||||
INTERRUPT_HANDLER(0x33)
|
||||
INTERRUPT_HANDLER(0x34)
|
||||
INTERRUPT_HANDLER(0x35)
|
||||
INTERRUPT_HANDLER(0x36)
|
||||
INTERRUPT_HANDLER(0x37)
|
||||
INTERRUPT_HANDLER(0x38)
|
||||
INTERRUPT_HANDLER(0x39)
|
||||
INTERRUPT_HANDLER(0x3a)
|
||||
INTERRUPT_HANDLER(0x3b)
|
||||
INTERRUPT_HANDLER(0x3c)
|
||||
INTERRUPT_HANDLER(0x3d)
|
||||
|
||||
/* Free */
|
||||
|
||||
INTERRUPT_HANDLER(0x3e)
|
||||
INTERRUPT_HANDLER(0x3f)
|
||||
INTERRUPT_HANDLER(0x40)
|
||||
INTERRUPT_HANDLER(0x41)
|
||||
INTERRUPT_HANDLER(0x42)
|
||||
INTERRUPT_HANDLER(0x43)
|
||||
INTERRUPT_HANDLER(0x44)
|
||||
INTERRUPT_HANDLER(0x45)
|
||||
INTERRUPT_HANDLER(0x46)
|
||||
INTERRUPT_HANDLER(0x47)
|
||||
INTERRUPT_HANDLER(0x48)
|
||||
INTERRUPT_HANDLER(0x49)
|
||||
INTERRUPT_HANDLER(0x4a)
|
||||
INTERRUPT_HANDLER(0x4b)
|
||||
INTERRUPT_HANDLER(0x4c)
|
||||
INTERRUPT_HANDLER(0x4d)
|
||||
INTERRUPT_HANDLER(0x4e)
|
||||
INTERRUPT_HANDLER(0x4f)
|
||||
INTERRUPT_HANDLER(0x50)
|
||||
INTERRUPT_HANDLER(0x51)
|
||||
INTERRUPT_HANDLER(0x52)
|
||||
INTERRUPT_HANDLER(0x53)
|
||||
INTERRUPT_HANDLER(0x54)
|
||||
INTERRUPT_HANDLER(0x55)
|
||||
INTERRUPT_HANDLER(0x56)
|
||||
INTERRUPT_HANDLER(0x57)
|
||||
INTERRUPT_HANDLER(0x58)
|
||||
INTERRUPT_HANDLER(0x59)
|
||||
INTERRUPT_HANDLER(0x5a)
|
||||
INTERRUPT_HANDLER(0x5b)
|
||||
INTERRUPT_HANDLER(0x5c)
|
||||
INTERRUPT_HANDLER(0x5d)
|
||||
INTERRUPT_HANDLER(0x5e)
|
||||
INTERRUPT_HANDLER(0x5f)
|
||||
INTERRUPT_HANDLER(0x60)
|
||||
INTERRUPT_HANDLER(0x61)
|
||||
INTERRUPT_HANDLER(0x62)
|
||||
INTERRUPT_HANDLER(0x63)
|
||||
INTERRUPT_HANDLER(0x64)
|
||||
INTERRUPT_HANDLER(0x65)
|
||||
INTERRUPT_HANDLER(0x66)
|
||||
INTERRUPT_HANDLER(0x67)
|
||||
INTERRUPT_HANDLER(0x68)
|
||||
INTERRUPT_HANDLER(0x69)
|
||||
INTERRUPT_HANDLER(0x6a)
|
||||
INTERRUPT_HANDLER(0x6b)
|
||||
INTERRUPT_HANDLER(0x6c)
|
||||
INTERRUPT_HANDLER(0x6d)
|
||||
INTERRUPT_HANDLER(0x6e)
|
||||
INTERRUPT_HANDLER(0x6f)
|
||||
INTERRUPT_HANDLER(0x70)
|
||||
INTERRUPT_HANDLER(0x71)
|
||||
INTERRUPT_HANDLER(0x72)
|
||||
INTERRUPT_HANDLER(0x73)
|
||||
INTERRUPT_HANDLER(0x74)
|
||||
INTERRUPT_HANDLER(0x75)
|
||||
INTERRUPT_HANDLER(0x76)
|
||||
INTERRUPT_HANDLER(0x77)
|
||||
INTERRUPT_HANDLER(0x78)
|
||||
INTERRUPT_HANDLER(0x79)
|
||||
INTERRUPT_HANDLER(0x7a)
|
||||
INTERRUPT_HANDLER(0x7b)
|
||||
INTERRUPT_HANDLER(0x7c)
|
||||
INTERRUPT_HANDLER(0x7d)
|
||||
INTERRUPT_HANDLER(0x7e)
|
||||
INTERRUPT_HANDLER(0x7f)
|
||||
INTERRUPT_HANDLER(0x80)
|
||||
INTERRUPT_HANDLER(0x81)
|
||||
INTERRUPT_HANDLER(0x82)
|
||||
INTERRUPT_HANDLER(0x83)
|
||||
INTERRUPT_HANDLER(0x84)
|
||||
INTERRUPT_HANDLER(0x85)
|
||||
INTERRUPT_HANDLER(0x86)
|
||||
INTERRUPT_HANDLER(0x87)
|
||||
INTERRUPT_HANDLER(0x88)
|
||||
INTERRUPT_HANDLER(0x89)
|
||||
INTERRUPT_HANDLER(0x8a)
|
||||
INTERRUPT_HANDLER(0x8b)
|
||||
INTERRUPT_HANDLER(0x8c)
|
||||
INTERRUPT_HANDLER(0x8d)
|
||||
INTERRUPT_HANDLER(0x8e)
|
||||
INTERRUPT_HANDLER(0x8f)
|
||||
INTERRUPT_HANDLER(0x90)
|
||||
INTERRUPT_HANDLER(0x91)
|
||||
INTERRUPT_HANDLER(0x92)
|
||||
INTERRUPT_HANDLER(0x93)
|
||||
INTERRUPT_HANDLER(0x94)
|
||||
INTERRUPT_HANDLER(0x95)
|
||||
INTERRUPT_HANDLER(0x96)
|
||||
INTERRUPT_HANDLER(0x97)
|
||||
INTERRUPT_HANDLER(0x98)
|
||||
INTERRUPT_HANDLER(0x99)
|
||||
INTERRUPT_HANDLER(0x9a)
|
||||
INTERRUPT_HANDLER(0x9b)
|
||||
INTERRUPT_HANDLER(0x9c)
|
||||
INTERRUPT_HANDLER(0x9d)
|
||||
INTERRUPT_HANDLER(0x9e)
|
||||
INTERRUPT_HANDLER(0x9f)
|
||||
INTERRUPT_HANDLER(0xa0)
|
||||
INTERRUPT_HANDLER(0xa1)
|
||||
INTERRUPT_HANDLER(0xa2)
|
||||
INTERRUPT_HANDLER(0xa3)
|
||||
INTERRUPT_HANDLER(0xa4)
|
||||
INTERRUPT_HANDLER(0xa5)
|
||||
INTERRUPT_HANDLER(0xa6)
|
||||
INTERRUPT_HANDLER(0xa7)
|
||||
INTERRUPT_HANDLER(0xa8)
|
||||
INTERRUPT_HANDLER(0xa9)
|
||||
INTERRUPT_HANDLER(0xaa)
|
||||
INTERRUPT_HANDLER(0xab)
|
||||
INTERRUPT_HANDLER(0xac)
|
||||
INTERRUPT_HANDLER(0xad)
|
||||
INTERRUPT_HANDLER(0xae)
|
||||
INTERRUPT_HANDLER(0xaf)
|
||||
INTERRUPT_HANDLER(0xb0)
|
||||
INTERRUPT_HANDLER(0xb1)
|
||||
INTERRUPT_HANDLER(0xb2)
|
||||
INTERRUPT_HANDLER(0xb3)
|
||||
INTERRUPT_HANDLER(0xb4)
|
||||
INTERRUPT_HANDLER(0xb5)
|
||||
INTERRUPT_HANDLER(0xb6)
|
||||
INTERRUPT_HANDLER(0xb7)
|
||||
INTERRUPT_HANDLER(0xb8)
|
||||
INTERRUPT_HANDLER(0xb9)
|
||||
INTERRUPT_HANDLER(0xba)
|
||||
INTERRUPT_HANDLER(0xbb)
|
||||
INTERRUPT_HANDLER(0xbc)
|
||||
INTERRUPT_HANDLER(0xbd)
|
||||
INTERRUPT_HANDLER(0xbe)
|
||||
INTERRUPT_HANDLER(0xbf)
|
||||
INTERRUPT_HANDLER(0xc0)
|
||||
INTERRUPT_HANDLER(0xc1)
|
||||
INTERRUPT_HANDLER(0xc2)
|
||||
INTERRUPT_HANDLER(0xc3)
|
||||
INTERRUPT_HANDLER(0xc4)
|
||||
INTERRUPT_HANDLER(0xc5)
|
||||
INTERRUPT_HANDLER(0xc6)
|
||||
INTERRUPT_HANDLER(0xc7)
|
||||
INTERRUPT_HANDLER(0xc8)
|
||||
INTERRUPT_HANDLER(0xc9)
|
||||
INTERRUPT_HANDLER(0xca)
|
||||
INTERRUPT_HANDLER(0xcb)
|
||||
INTERRUPT_HANDLER(0xcc)
|
||||
INTERRUPT_HANDLER(0xcd)
|
||||
INTERRUPT_HANDLER(0xce)
|
||||
INTERRUPT_HANDLER(0xcf)
|
||||
INTERRUPT_HANDLER(0xd0)
|
||||
INTERRUPT_HANDLER(0xd1)
|
||||
INTERRUPT_HANDLER(0xd2)
|
||||
INTERRUPT_HANDLER(0xd3)
|
||||
INTERRUPT_HANDLER(0xd4)
|
||||
INTERRUPT_HANDLER(0xd5)
|
||||
INTERRUPT_HANDLER(0xd6)
|
||||
INTERRUPT_HANDLER(0xd7)
|
||||
INTERRUPT_HANDLER(0xd8)
|
||||
INTERRUPT_HANDLER(0xd9)
|
||||
INTERRUPT_HANDLER(0xda)
|
||||
INTERRUPT_HANDLER(0xdb)
|
||||
INTERRUPT_HANDLER(0xdc)
|
||||
INTERRUPT_HANDLER(0xdd)
|
||||
INTERRUPT_HANDLER(0xde)
|
||||
INTERRUPT_HANDLER(0xdf)
|
||||
INTERRUPT_HANDLER(0xe0)
|
||||
INTERRUPT_HANDLER(0xe1)
|
||||
INTERRUPT_HANDLER(0xe2)
|
||||
INTERRUPT_HANDLER(0xe3)
|
||||
INTERRUPT_HANDLER(0xe4)
|
||||
INTERRUPT_HANDLER(0xe5)
|
||||
INTERRUPT_HANDLER(0xe6)
|
||||
INTERRUPT_HANDLER(0xe7)
|
||||
INTERRUPT_HANDLER(0xe8)
|
||||
INTERRUPT_HANDLER(0xe9)
|
||||
INTERRUPT_HANDLER(0xea)
|
||||
INTERRUPT_HANDLER(0xeb)
|
||||
INTERRUPT_HANDLER(0xec)
|
||||
INTERRUPT_HANDLER(0xed)
|
||||
INTERRUPT_HANDLER(0xee)
|
||||
INTERRUPT_HANDLER(0xef)
|
||||
INTERRUPT_HANDLER(0xf0)
|
||||
INTERRUPT_HANDLER(0xf1)
|
||||
INTERRUPT_HANDLER(0xf2)
|
||||
INTERRUPT_HANDLER(0xf3)
|
||||
INTERRUPT_HANDLER(0xf4)
|
||||
INTERRUPT_HANDLER(0xf5)
|
||||
INTERRUPT_HANDLER(0xf6)
|
||||
INTERRUPT_HANDLER(0xf7)
|
||||
INTERRUPT_HANDLER(0xf8)
|
||||
INTERRUPT_HANDLER(0xf9)
|
||||
INTERRUPT_HANDLER(0xfa)
|
||||
INTERRUPT_HANDLER(0xfb)
|
||||
INTERRUPT_HANDLER(0xfc)
|
||||
INTERRUPT_HANDLER(0xfd)
|
||||
INTERRUPT_HANDLER(0xfe)
|
||||
INTERRUPT_HANDLER(0xff)
|
||||
|
||||
#pragma endregion Interrupt Macros
|
||||
|
||||
void Init(int Core)
|
||||
{
|
||||
if (Core == 0) /* Disable PIC using BSP */
|
||||
{
|
||||
// PIC
|
||||
outb(0x20, 0x10 | 0x1);
|
||||
outb(0x80, 0);
|
||||
outb(0xA0, 0x10 | 0x10);
|
||||
outb(0x80, 0);
|
||||
|
||||
outb(0x21, 0x20);
|
||||
outb(0x80, 0);
|
||||
outb(0xA1, 0x28);
|
||||
outb(0x80, 0);
|
||||
|
||||
outb(0x21, 0x04);
|
||||
outb(0x80, 0);
|
||||
outb(0xA1, 0x02);
|
||||
outb(0x80, 0);
|
||||
|
||||
outb(0x21, 1);
|
||||
outb(0x80, 0);
|
||||
outb(0xA1, 1);
|
||||
outb(0x80, 0);
|
||||
|
||||
// Masking and disabling PIC
|
||||
outb(0x21, 0xff);
|
||||
outb(0x80, 0);
|
||||
outb(0xA1, 0xff);
|
||||
}
|
||||
|
||||
bool EnableISRs = true;
|
||||
#ifdef DEBUG
|
||||
EnableISRs = !DebuggerIsAttached;
|
||||
if (!EnableISRs)
|
||||
KPrint("\x1b[34mThe debugger is attached, disabling all ISRs.");
|
||||
#endif
|
||||
|
||||
/* ISR */
|
||||
SetEntry(0x0, InterruptHandler_0x0, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x1, InterruptHandler_0x1, IST1, INTERRUPT_GATE_64BIT, RING3, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x2, InterruptHandler_0x2, IST2, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x3, InterruptHandler_0x3, IST1, INTERRUPT_GATE_64BIT, RING3, (!DebuggerIsAttached), GDT_KERNEL_CODE); /* Do not handle breakpoints if we are debugging the kernel. */
|
||||
SetEntry(0x4, InterruptHandler_0x4, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x5, InterruptHandler_0x5, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x6, InterruptHandler_0x6, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x7, InterruptHandler_0x7, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x8, InterruptHandler_0x8, IST3, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x9, InterruptHandler_0x9, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa, InterruptHandler_0xa, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb, InterruptHandler_0xb, IST1, INTERRUPT_GATE_64BIT, RING0, (!DebuggerIsAttached), GDT_KERNEL_CODE);
|
||||
SetEntry(0xc, InterruptHandler_0xc, IST3, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd, InterruptHandler_0xd, IST3, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe, InterruptHandler_0xe, IST3, INTERRUPT_GATE_64BIT, RING0, EnableISRs /* FIXME: CoW? */, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf, InterruptHandler_0xf, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x10, InterruptHandler_0x10, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x11, InterruptHandler_0x11, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x12, InterruptHandler_0x12, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x13, InterruptHandler_0x13, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x14, InterruptHandler_0x14, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x15, InterruptHandler_0x15, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x16, InterruptHandler_0x16, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x17, InterruptHandler_0x17, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x18, InterruptHandler_0x18, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x19, InterruptHandler_0x19, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x1a, InterruptHandler_0x1a, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x1b, InterruptHandler_0x1b, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x1c, InterruptHandler_0x1c, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x1d, InterruptHandler_0x1d, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x1e, InterruptHandler_0x1e, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x1f, InterruptHandler_0x1f, IST1, INTERRUPT_GATE_64BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
|
||||
/* IRQ */
|
||||
|
||||
SetEntry(0x20, InterruptHandler_0x20, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x21, InterruptHandler_0x21, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x22, InterruptHandler_0x22, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x23, InterruptHandler_0x23, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x24, InterruptHandler_0x24, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x25, InterruptHandler_0x25, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x26, InterruptHandler_0x26, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x27, InterruptHandler_0x27, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x28, InterruptHandler_0x28, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x29, InterruptHandler_0x29, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x2a, InterruptHandler_0x2a, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x2b, InterruptHandler_0x2b, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x2c, InterruptHandler_0x2c, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x2d, InterruptHandler_0x2d, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x2e, InterruptHandler_0x2e, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x2f, InterruptHandler_0x2f, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
|
||||
/* Reserved by OS */
|
||||
|
||||
SetEntry(0x30, InterruptHandler_0x30, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x31, InterruptHandler_0x31, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x32, InterruptHandler_0x32, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x33, InterruptHandler_0x33, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x34, InterruptHandler_0x34, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x35, InterruptHandler_0x35, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x36, InterruptHandler_0x36, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x37, InterruptHandler_0x37, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x38, InterruptHandler_0x38, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x39, InterruptHandler_0x39, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x3a, InterruptHandler_0x3a, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x3b, InterruptHandler_0x3b, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x3c, InterruptHandler_0x3c, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x3d, InterruptHandler_0x3d, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
|
||||
/* Free */
|
||||
|
||||
SetEntry(0x3e, InterruptHandler_0x3e, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x3f, InterruptHandler_0x3f, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x40, InterruptHandler_0x40, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x41, InterruptHandler_0x41, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x42, InterruptHandler_0x42, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x43, InterruptHandler_0x43, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x44, InterruptHandler_0x44, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x45, InterruptHandler_0x45, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x46, InterruptHandler_0x46, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x47, InterruptHandler_0x47, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x48, InterruptHandler_0x48, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x49, InterruptHandler_0x49, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x4a, InterruptHandler_0x4a, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x4b, InterruptHandler_0x4b, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x4c, InterruptHandler_0x4c, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x4d, InterruptHandler_0x4d, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x4e, InterruptHandler_0x4e, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x4f, InterruptHandler_0x4f, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x50, InterruptHandler_0x50, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x51, InterruptHandler_0x51, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x52, InterruptHandler_0x52, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x53, InterruptHandler_0x53, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x54, InterruptHandler_0x54, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x55, InterruptHandler_0x55, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x56, InterruptHandler_0x56, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x57, InterruptHandler_0x57, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x58, InterruptHandler_0x58, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x59, InterruptHandler_0x59, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x5a, InterruptHandler_0x5a, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x5b, InterruptHandler_0x5b, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x5c, InterruptHandler_0x5c, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x5d, InterruptHandler_0x5d, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x5e, InterruptHandler_0x5e, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x5f, InterruptHandler_0x5f, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x60, InterruptHandler_0x60, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x61, InterruptHandler_0x61, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x62, InterruptHandler_0x62, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x63, InterruptHandler_0x63, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x64, InterruptHandler_0x64, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x65, InterruptHandler_0x65, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x66, InterruptHandler_0x66, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x67, InterruptHandler_0x67, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x68, InterruptHandler_0x68, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x69, InterruptHandler_0x69, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x6a, InterruptHandler_0x6a, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x6b, InterruptHandler_0x6b, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x6c, InterruptHandler_0x6c, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x6d, InterruptHandler_0x6d, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x6e, InterruptHandler_0x6e, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x6f, InterruptHandler_0x6f, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x70, InterruptHandler_0x70, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x71, InterruptHandler_0x71, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x72, InterruptHandler_0x72, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x73, InterruptHandler_0x73, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x74, InterruptHandler_0x74, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x75, InterruptHandler_0x75, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x76, InterruptHandler_0x76, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x77, InterruptHandler_0x77, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x78, InterruptHandler_0x78, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x79, InterruptHandler_0x79, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x7a, InterruptHandler_0x7a, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x7b, InterruptHandler_0x7b, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x7c, InterruptHandler_0x7c, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x7d, InterruptHandler_0x7d, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x7e, InterruptHandler_0x7e, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x7f, InterruptHandler_0x7f, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x80, InterruptHandler_0x80, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x81, InterruptHandler_0x81, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x82, InterruptHandler_0x82, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x83, InterruptHandler_0x83, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x84, InterruptHandler_0x84, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x85, InterruptHandler_0x85, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x86, InterruptHandler_0x86, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x87, InterruptHandler_0x87, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x88, InterruptHandler_0x88, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x89, InterruptHandler_0x89, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x8a, InterruptHandler_0x8a, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x8b, InterruptHandler_0x8b, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x8c, InterruptHandler_0x8c, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x8d, InterruptHandler_0x8d, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x8e, InterruptHandler_0x8e, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x8f, InterruptHandler_0x8f, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x90, InterruptHandler_0x90, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x91, InterruptHandler_0x91, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x92, InterruptHandler_0x92, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x93, InterruptHandler_0x93, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x94, InterruptHandler_0x94, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x95, InterruptHandler_0x95, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x96, InterruptHandler_0x96, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x97, InterruptHandler_0x97, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x98, InterruptHandler_0x98, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x99, InterruptHandler_0x99, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x9a, InterruptHandler_0x9a, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x9b, InterruptHandler_0x9b, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x9c, InterruptHandler_0x9c, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x9d, InterruptHandler_0x9d, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x9e, InterruptHandler_0x9e, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x9f, InterruptHandler_0x9f, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa0, InterruptHandler_0xa0, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa1, InterruptHandler_0xa1, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa2, InterruptHandler_0xa2, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa3, InterruptHandler_0xa3, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa4, InterruptHandler_0xa4, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa5, InterruptHandler_0xa5, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa6, InterruptHandler_0xa6, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa7, InterruptHandler_0xa7, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa8, InterruptHandler_0xa8, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa9, InterruptHandler_0xa9, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xaa, InterruptHandler_0xaa, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xab, InterruptHandler_0xab, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xac, InterruptHandler_0xac, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xad, InterruptHandler_0xad, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xae, InterruptHandler_0xae, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xaf, InterruptHandler_0xaf, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb0, InterruptHandler_0xb0, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb1, InterruptHandler_0xb1, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb2, InterruptHandler_0xb2, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb3, InterruptHandler_0xb3, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb4, InterruptHandler_0xb4, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb5, InterruptHandler_0xb5, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb6, InterruptHandler_0xb6, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb7, InterruptHandler_0xb7, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb8, InterruptHandler_0xb8, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb9, InterruptHandler_0xb9, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xba, InterruptHandler_0xba, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xbb, InterruptHandler_0xbb, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xbc, InterruptHandler_0xbc, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xbd, InterruptHandler_0xbd, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xbe, InterruptHandler_0xbe, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xbf, InterruptHandler_0xbf, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc0, InterruptHandler_0xc0, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc1, InterruptHandler_0xc1, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc2, InterruptHandler_0xc2, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc3, InterruptHandler_0xc3, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc4, InterruptHandler_0xc4, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc5, InterruptHandler_0xc5, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc6, InterruptHandler_0xc6, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc7, InterruptHandler_0xc7, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc8, InterruptHandler_0xc8, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc9, InterruptHandler_0xc9, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xca, InterruptHandler_0xca, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xcb, InterruptHandler_0xcb, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xcc, InterruptHandler_0xcc, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xcd, InterruptHandler_0xcd, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xce, InterruptHandler_0xce, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xcf, InterruptHandler_0xcf, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd0, InterruptHandler_0xd0, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd1, InterruptHandler_0xd1, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd2, InterruptHandler_0xd2, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd3, InterruptHandler_0xd3, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd4, InterruptHandler_0xd4, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd5, InterruptHandler_0xd5, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd6, InterruptHandler_0xd6, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd7, InterruptHandler_0xd7, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd8, InterruptHandler_0xd8, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd9, InterruptHandler_0xd9, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xda, InterruptHandler_0xda, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xdb, InterruptHandler_0xdb, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xdc, InterruptHandler_0xdc, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xdd, InterruptHandler_0xdd, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xde, InterruptHandler_0xde, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xdf, InterruptHandler_0xdf, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe0, InterruptHandler_0xe0, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe1, InterruptHandler_0xe1, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe2, InterruptHandler_0xe2, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe3, InterruptHandler_0xe3, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe4, InterruptHandler_0xe4, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe5, InterruptHandler_0xe5, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe6, InterruptHandler_0xe6, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe7, InterruptHandler_0xe7, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe8, InterruptHandler_0xe8, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe9, InterruptHandler_0xe9, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xea, InterruptHandler_0xea, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xeb, InterruptHandler_0xeb, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xec, InterruptHandler_0xec, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xed, InterruptHandler_0xed, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xee, InterruptHandler_0xee, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xef, InterruptHandler_0xef, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf0, InterruptHandler_0xf0, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf1, InterruptHandler_0xf1, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf2, InterruptHandler_0xf2, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf3, InterruptHandler_0xf3, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf4, InterruptHandler_0xf4, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf5, InterruptHandler_0xf5, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf6, InterruptHandler_0xf6, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf7, InterruptHandler_0xf7, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf8, InterruptHandler_0xf8, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf9, InterruptHandler_0xf9, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xfa, InterruptHandler_0xfa, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xfb, InterruptHandler_0xfb, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xfc, InterruptHandler_0xfc, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xfd, InterruptHandler_0xfd, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xfe, InterruptHandler_0xfe, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xff, InterruptHandler_0xff, IST0, INTERRUPT_GATE_64BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
CPU::x64::lidt(&IDTr);
|
||||
}
|
||||
}
|
51
Kernel/arch/amd64/cpu/idt.hpp
Normal file
51
Kernel/arch/amd64/cpu/idt.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __FENNIX_KERNEL_IDT_H__
|
||||
#define __FENNIX_KERNEL_IDT_H__
|
||||
|
||||
#include <types.h>
|
||||
#include <cpu/x86/x64/SegmentDescriptors.hpp>
|
||||
|
||||
namespace InterruptDescriptorTable
|
||||
{
|
||||
|
||||
union IDTGateDescriptor
|
||||
{
|
||||
InterruptGate Interrupt;
|
||||
TrapGate Trap;
|
||||
CallGate Call;
|
||||
};
|
||||
|
||||
struct IDTRegister
|
||||
{
|
||||
uint16_t Limit;
|
||||
IDTGateDescriptor *BaseAddress;
|
||||
} __packed;
|
||||
|
||||
void SetEntry(uint8_t Index,
|
||||
void (*Base)(),
|
||||
InterruptStackTableType InterruptStackTable,
|
||||
GateType Gate,
|
||||
PrivilegeLevelType Ring,
|
||||
bool Present,
|
||||
uint16_t SegmentSelector);
|
||||
|
||||
void Init(int Core);
|
||||
}
|
||||
|
||||
#endif // !__FENNIX_KERNEL_IDT_H__
|
198
Kernel/arch/amd64/cpu/smp.cpp
Normal file
198
Kernel/arch/amd64/cpu/smp.cpp
Normal file
@ -0,0 +1,198 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <smp.hpp>
|
||||
|
||||
#include <memory.hpp>
|
||||
#include <acpi.hpp>
|
||||
#include <ints.hpp>
|
||||
#include <assert.h>
|
||||
#include <cpu.hpp>
|
||||
#include <atomic>
|
||||
|
||||
#include "../../../kernel.h"
|
||||
#include "apic.hpp"
|
||||
|
||||
extern "C" uint64_t _trampoline_start, _trampoline_end;
|
||||
|
||||
/* https://wiki.osdev.org/Memory_Map_(x86) */
|
||||
enum SMPTrampolineAddress
|
||||
{
|
||||
PAGE_TABLE = 0x500,
|
||||
START_ADDR = 0x520,
|
||||
STACK = 0x570,
|
||||
GDT = 0x580,
|
||||
IDT = 0x590,
|
||||
CORE = 0x600,
|
||||
TRAMPOLINE_START = 0x2000
|
||||
};
|
||||
|
||||
std::atomic_bool CPUEnabled = false;
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
static __aligned(PAGE_SIZE) CPUData CPUs[MAX_CPU] = {0};
|
||||
|
||||
nsa CPUData *GetCPU(long id) { return &CPUs[id]; }
|
||||
|
||||
nsa CPUData *GetCurrentCPU()
|
||||
{
|
||||
if (unlikely(!Interrupts::apic[0]))
|
||||
return &CPUs[0]; /* No APIC means we are on the BSP. */
|
||||
|
||||
APIC::APIC *apic = (APIC::APIC *)Interrupts::apic[0];
|
||||
int CoreID = 0;
|
||||
if (CPUEnabled.load(std::memory_order_acquire) == true)
|
||||
{
|
||||
Memory::SwapPT swap =
|
||||
Memory::SwapPT(KernelPageTable, thisPageTable);
|
||||
|
||||
if (apic->x2APIC)
|
||||
CoreID = int(CPU::x64::rdmsr(CPU::x64::MSR_X2APIC_APICID));
|
||||
else
|
||||
CoreID = apic->Read(APIC::APIC_ID) >> 24;
|
||||
}
|
||||
|
||||
if (unlikely((&CPUs[CoreID])->IsActive != true))
|
||||
{
|
||||
error("CPU %d is not active!", CoreID);
|
||||
assert((&CPUs[0])->IsActive == true); /* We can't continue without the BSP. */
|
||||
return &CPUs[0];
|
||||
}
|
||||
|
||||
assert((&CPUs[CoreID])->Checksum == CPU_DATA_CHECKSUM); /* This should never happen. */
|
||||
return &CPUs[CoreID];
|
||||
}
|
||||
|
||||
extern "C" void StartCPU()
|
||||
{
|
||||
CPU::Interrupts(CPU::Disable);
|
||||
int CoreID = (int)*reinterpret_cast<int *>(CORE);
|
||||
CPU::InitializeFeatures(CoreID);
|
||||
// Initialize GDT and IDT
|
||||
Interrupts::Initialize(CoreID);
|
||||
Interrupts::Enable(CoreID);
|
||||
Interrupts::InitializeTimer(CoreID);
|
||||
asmv("mov %0, %%rsp" ::"r"((&CPUs[CoreID])->Stack));
|
||||
|
||||
CPU::Interrupts(CPU::Enable);
|
||||
KPrint("CPU %d is online", CoreID);
|
||||
CPUEnabled.store(true, std::memory_order_release);
|
||||
CPU::Halt(true);
|
||||
}
|
||||
|
||||
namespace SMP
|
||||
{
|
||||
int CPUCores = 0;
|
||||
|
||||
void Initialize(void *_madt)
|
||||
{
|
||||
if (!_madt)
|
||||
{
|
||||
error("MADT is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
ACPI::MADT *madt = (ACPI::MADT *)_madt;
|
||||
|
||||
if (madt->lapic.size() < 1)
|
||||
{
|
||||
error("No CPUs found!");
|
||||
return;
|
||||
}
|
||||
|
||||
int Cores = madt->CPUCores + 1;
|
||||
|
||||
if (Config.Cores > madt->CPUCores + 1)
|
||||
KPrint("More cores requested than available. Using %d cores",
|
||||
madt->CPUCores + 1);
|
||||
else if (Config.Cores != 0)
|
||||
Cores = Config.Cores;
|
||||
|
||||
CPUCores = Cores;
|
||||
|
||||
uint64_t TrampolineLength = (uintptr_t)&_trampoline_end -
|
||||
(uintptr_t)&_trampoline_start;
|
||||
Memory::Virtual().Map(0x0, 0x0, Memory::PTFlag::RW);
|
||||
/* We reserved the TRAMPOLINE_START address inside Physical class. */
|
||||
Memory::Virtual().Map((void *)TRAMPOLINE_START,
|
||||
(void *)TRAMPOLINE_START,
|
||||
TrampolineLength, Memory::PTFlag::RW);
|
||||
memcpy((void *)TRAMPOLINE_START, &_trampoline_start, TrampolineLength);
|
||||
debug("Trampoline address: %#lx-%#lx",
|
||||
TRAMPOLINE_START,
|
||||
TRAMPOLINE_START + TrampolineLength);
|
||||
|
||||
void *CPUTmpStack = KernelAllocator.RequestPages(TO_PAGES(STACK_SIZE + 1));
|
||||
asmv("sgdt [0x580]");
|
||||
asmv("sidt [0x590]");
|
||||
VPOKE(uintptr_t, STACK) = (uintptr_t)CPUTmpStack + STACK_SIZE;
|
||||
VPOKE(uintptr_t, PAGE_TABLE) = (uintptr_t)KernelPageTable;
|
||||
VPOKE(uintptr_t, START_ADDR) = (uintptr_t)&StartCPU;
|
||||
|
||||
for (int i = 0; i < Cores; i++)
|
||||
{
|
||||
ACPI::MADT::LocalAPIC *lapic = madt->lapic[i];
|
||||
APIC::APIC *apic = (APIC::APIC *)Interrupts::apic[0];
|
||||
|
||||
debug("Initializing CPU %d", lapic->APICId);
|
||||
uint8_t APIC_ID = 0;
|
||||
if (apic->x2APIC)
|
||||
APIC_ID = uint8_t(CPU::x64::rdmsr(CPU::x64::MSR_X2APIC_APICID));
|
||||
else
|
||||
APIC_ID = uint8_t(apic->Read(APIC::APIC_ID) >> 24);
|
||||
|
||||
if (APIC_ID != lapic->APICId)
|
||||
{
|
||||
VPOKE(int, CORE) = i;
|
||||
if (!apic->x2APIC)
|
||||
{
|
||||
APIC::InterruptCommandRegister icr{};
|
||||
icr.MT = APIC::INIT;
|
||||
icr.DES = lapic->APICId;
|
||||
apic->ICR(icr);
|
||||
}
|
||||
|
||||
apic->SendInitIPI(lapic->APICId);
|
||||
TimeManager->Sleep(20, Time::Units::Milliseconds);
|
||||
apic->SendStartupIPI(lapic->APICId, TRAMPOLINE_START);
|
||||
debug("Waiting for CPU %d to load...", lapic->APICId);
|
||||
|
||||
uint64_t Timeout = TimeManager->CalculateTarget(2, Time::Units::Seconds);
|
||||
while (CPUEnabled.load(std::memory_order_acquire) == false)
|
||||
{
|
||||
if (TimeManager->GetCounter() > Timeout)
|
||||
{
|
||||
error("CPU %d failed to load!", lapic->APICId);
|
||||
KPrint("\x1b[1;37;41mCPU %d failed to load!",
|
||||
lapic->APICId);
|
||||
break;
|
||||
}
|
||||
CPU::Pause();
|
||||
}
|
||||
trace("CPU %d loaded.", lapic->APICId);
|
||||
CPUEnabled.store(false, std::memory_order_release);
|
||||
}
|
||||
else
|
||||
KPrint("CPU %d is the BSP", lapic->APICId);
|
||||
}
|
||||
|
||||
KernelAllocator.FreePages(CPUTmpStack, TO_PAGES(STACK_SIZE + 1));
|
||||
/* We are going to unmap the page after we are done with it. */
|
||||
Memory::Virtual().Unmap(0x0);
|
||||
CPUEnabled.store(true, std::memory_order_release);
|
||||
}
|
||||
}
|
179
Kernel/arch/amd64/cpu/smp_trampoline.s
Normal file
179
Kernel/arch/amd64/cpu/smp_trampoline.s
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* This has to be the same as enum SMPTrampolineAddress. */
|
||||
TRAMPOLINE_PAGE_TABLE = 0x500
|
||||
TRAMPOLINE_START_ADDR = 0x520
|
||||
TRAMPOLINE_STACK = 0x570
|
||||
TRAMPOLINE_GDT = 0x580
|
||||
TRAMPOLINE_IDT = 0x590
|
||||
TRAMPOLINE_CORE = 0x600
|
||||
TRAMPOLINE_START = 0x2000
|
||||
|
||||
.section .rodata
|
||||
|
||||
/* ========== 16-bit ========== */
|
||||
|
||||
.code16
|
||||
.global _trampoline_start
|
||||
_trampoline_start:
|
||||
cli
|
||||
cld
|
||||
call Trampoline16
|
||||
|
||||
Trampoline16:
|
||||
mov $0x0, %ax
|
||||
mov %ax, %ds
|
||||
mov %ax, %es
|
||||
mov %ax, %fs
|
||||
mov %ax, %gs
|
||||
mov %ax, %ss
|
||||
|
||||
/* Load Protected Mode GDT */
|
||||
lgdt [ProtectedMode_gdtr - _trampoline_start + TRAMPOLINE_START]
|
||||
|
||||
/* Enable Protected Mode */
|
||||
mov %cr0, %eax
|
||||
or $0x1, %al
|
||||
mov %eax, %cr0
|
||||
|
||||
/* Jump to Protected Mode */
|
||||
ljmp $0x8, $(Trampoline32 - _trampoline_start + TRAMPOLINE_START)
|
||||
|
||||
/* ========== 32-bit ========== */
|
||||
|
||||
.code32
|
||||
Trampoline32:
|
||||
mov $0x10, %bx
|
||||
mov %bx, %ds
|
||||
mov %bx, %es
|
||||
mov %bx, %ss
|
||||
|
||||
/* Set a page table */
|
||||
mov [TRAMPOLINE_PAGE_TABLE], %eax
|
||||
mov %eax, %cr3
|
||||
|
||||
/* Enable PAE and PSE */
|
||||
mov %cr4, %eax
|
||||
or $0x20, %eax /* PAE */
|
||||
or $0x80, %eax /* PSE */
|
||||
mov %eax, %cr4
|
||||
|
||||
/* Enable Long Mode */
|
||||
mov $0xC0000080, %ecx
|
||||
rdmsr
|
||||
or $0x100, %eax /* LME */
|
||||
wrmsr
|
||||
|
||||
/* Enable paging */
|
||||
mov %cr0, %eax
|
||||
or $0x80000000, %eax /* PG */
|
||||
mov %eax, %cr0
|
||||
|
||||
/* Load Long Mode GDT */
|
||||
lgdt [LongMode_gdtr - _trampoline_start + TRAMPOLINE_START]
|
||||
|
||||
/* Jump to Long Mode */
|
||||
ljmp $0x8, $(Trampoline64 - _trampoline_start + TRAMPOLINE_START)
|
||||
|
||||
/* ========== 64-bit ========== */
|
||||
|
||||
.code64
|
||||
Trampoline64:
|
||||
mov $0x10, %ax
|
||||
mov %ax, %ds
|
||||
mov %ax, %es
|
||||
mov %ax, %ss
|
||||
|
||||
mov $0x0, %ax
|
||||
mov %ax, %fs
|
||||
mov %ax, %gs
|
||||
|
||||
/* Set custom GDT & IDT */
|
||||
lgdt [TRAMPOLINE_GDT]
|
||||
lidt [TRAMPOLINE_IDT]
|
||||
|
||||
/* Set up stack */
|
||||
mov [TRAMPOLINE_STACK], %rsp
|
||||
mov $0x0, %rbp
|
||||
|
||||
/* Reset RFLAGS */
|
||||
push $0x0
|
||||
popf
|
||||
|
||||
/* Jump to TrampolinePrepareExit */
|
||||
call TrampolineExit
|
||||
|
||||
.extern StartCPU
|
||||
TrampolineExit:
|
||||
mov $StartCPU, %rax
|
||||
call *%rax
|
||||
|
||||
.align 16
|
||||
ProtectedMode_gdtr:
|
||||
.word ProtectedModeGDTEnd - ProtectedModeGDTStart - 1
|
||||
.long ProtectedModeGDTStart - _trampoline_start + TRAMPOLINE_START
|
||||
|
||||
.align 16
|
||||
ProtectedModeGDTStart:
|
||||
/* NULL segment */
|
||||
.quad 0x0
|
||||
|
||||
/* Code segment */
|
||||
.word 0xFFFF
|
||||
.word 0x0000
|
||||
.byte 0x00
|
||||
.word 0xCF9A
|
||||
.byte 0x00
|
||||
|
||||
/* Data segment */
|
||||
.word 0xFFFF
|
||||
.word 0x0000
|
||||
.byte 0x00
|
||||
.word 0xCF92
|
||||
.byte 0x00
|
||||
ProtectedModeGDTEnd:
|
||||
nop
|
||||
|
||||
.align 16
|
||||
LongMode_gdtr:
|
||||
.word LongModeGDTEnd - LongModeGDTStart - 1
|
||||
.quad LongModeGDTStart - _trampoline_start + TRAMPOLINE_START
|
||||
|
||||
.align 16
|
||||
LongModeGDTStart:
|
||||
/* NULL segment */
|
||||
.quad 0x0
|
||||
|
||||
/* Code segment */
|
||||
.word 0xFFFF
|
||||
.word 0x0000
|
||||
.byte 0x00
|
||||
.word 0xAF98
|
||||
.byte 0x00
|
||||
|
||||
/* Data segment */
|
||||
.word 0xFFFF
|
||||
.word 0x0000
|
||||
.byte 0x00
|
||||
.word 0xCF92
|
||||
.byte 0x00
|
||||
LongModeGDTEnd:
|
||||
nop
|
||||
|
||||
.global _trampoline_end
|
||||
_trampoline_end:
|
0
Kernel/arch/amd64/include/.gitkeep
Normal file
0
Kernel/arch/amd64/include/.gitkeep
Normal file
129
Kernel/arch/amd64/linker.ld
Normal file
129
Kernel/arch/amd64/linker.ld
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
OUTPUT_FORMAT(elf64-x86-64)
|
||||
OUTPUT_ARCH(i386:x86-64)
|
||||
|
||||
ENTRY(_start)
|
||||
|
||||
PF_R = 0x4;
|
||||
PF_W = 0x2;
|
||||
PF_X = 0x1;
|
||||
|
||||
PHDRS
|
||||
{
|
||||
bootstrap PT_LOAD FLAGS( PF_R | PF_W /*| PF_X*/ );
|
||||
text PT_LOAD FLAGS( PF_R | PF_X );
|
||||
data PT_LOAD FLAGS( PF_R | PF_W );
|
||||
rodata PT_LOAD FLAGS( PF_R );
|
||||
bss PT_LOAD FLAGS( PF_R | PF_W );
|
||||
}
|
||||
|
||||
KERNEL_VMA = 0xFFFFFFFF80000000;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x100000;
|
||||
_bootstrap_start = .;
|
||||
.bootstrap ALIGN(CONSTANT(MAXPAGESIZE)) :
|
||||
{
|
||||
*(.multiboot)
|
||||
*(.multiboot2)
|
||||
*(.bootstrap .bootstrap.*)
|
||||
} :bootstrap
|
||||
_bootstrap_end = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
. += KERNEL_VMA;
|
||||
|
||||
_kernel_start = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
_kernel_text_start = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
.text ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.text) - KERNEL_VMA)
|
||||
{
|
||||
*(.text .text.*)
|
||||
} :text
|
||||
_kernel_text_end = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
_kernel_data_start = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
.data ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.data) - KERNEL_VMA)
|
||||
{
|
||||
*(.data .data.*)
|
||||
} :data
|
||||
|
||||
.eh_frame : AT(ADDR(.eh_frame) - KERNEL_VMA) ONLY_IF_RW
|
||||
{
|
||||
KEEP (*(.eh_frame .eh_frame.*))
|
||||
} :data
|
||||
|
||||
.gcc_except_table : AT(ADDR(.gcc_except_table) - KERNEL_VMA) ONLY_IF_RW
|
||||
{
|
||||
KEEP (*(.gcc_except_table .gcc_except_table.*))
|
||||
} :data
|
||||
_kernel_data_end = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
_kernel_rodata_start = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
.rodata ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.rodata) - KERNEL_VMA)
|
||||
{
|
||||
*(.rodata .rodata.*)
|
||||
} :rodata
|
||||
|
||||
.init_array ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.init_array) - KERNEL_VMA)
|
||||
{
|
||||
PROVIDE_HIDDEN(__init_array_start = .);
|
||||
KEEP(*(.init_array .ctors))
|
||||
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
} :rodata
|
||||
|
||||
.fini_array ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.fini_array) - KERNEL_VMA)
|
||||
{
|
||||
PROVIDE_HIDDEN(__fini_array_start = .);
|
||||
KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
|
||||
KEEP(*(.fini_array .dtors))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
} :rodata
|
||||
|
||||
.eh_frame_hdr : AT(ADDR(.eh_frame_hdr) - KERNEL_VMA)
|
||||
{
|
||||
*(.eh_frame_hdr .eh_frame_hdr.*)
|
||||
} :rodata
|
||||
|
||||
.eh_frame : AT(ADDR(.eh_frame) - KERNEL_VMA) ONLY_IF_RO
|
||||
{
|
||||
KEEP (*(.eh_frame .eh_frame.*))
|
||||
} :rodata
|
||||
|
||||
.gcc_except_table : AT(ADDR(.gcc_except_table) - KERNEL_VMA) ONLY_IF_RO
|
||||
{
|
||||
KEEP (*(.gcc_except_table .gcc_except_table.*))
|
||||
} :rodata
|
||||
_kernel_rodata_end = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
_kernel_bss_start = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
.bss ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.bss) - KERNEL_VMA)
|
||||
{
|
||||
*(COMMON)
|
||||
*(.bss .bss.*)
|
||||
} :bss
|
||||
_kernel_bss_end = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
_kernel_end = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
/DISCARD/ :
|
||||
{
|
||||
*(.comment*)
|
||||
*(.note*)
|
||||
}
|
||||
}
|
97
Kernel/arch/amd64/madt.cpp
Normal file
97
Kernel/arch/amd64/madt.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "acpi.hpp"
|
||||
|
||||
#include <memory.hpp>
|
||||
#include <debug.h>
|
||||
|
||||
#include "../../kernel.h"
|
||||
|
||||
namespace ACPI
|
||||
{
|
||||
MADT::MADT(ACPI::MADTHeader *madt)
|
||||
{
|
||||
trace("Initializing MADT");
|
||||
if (!madt)
|
||||
{
|
||||
error("MADT is NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
CPUCores = 0;
|
||||
LAPICAddress = (LAPIC *)(uintptr_t)madt->LocalControllerAddress;
|
||||
for (uint8_t *ptr = (uint8_t *)(madt->Entries);
|
||||
(uintptr_t)(ptr) < (uintptr_t)(madt) + madt->Header.Length;
|
||||
ptr += *(ptr + 1))
|
||||
{
|
||||
switch (*(ptr))
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (ptr[4] & 1)
|
||||
{
|
||||
lapic.push_back((LocalAPIC *)ptr);
|
||||
KPrint("Local APIC %d (APIC %d) found.", lapic.back()->ACPIProcessorId, lapic.back()->APICId);
|
||||
CPUCores++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
ioapic.push_back((MADTIOApic *)ptr);
|
||||
KPrint("I/O APIC %d (Address %#lx) found.", ioapic.back()->APICID, ioapic.back()->Address);
|
||||
Memory::Virtual(KernelPageTable).Map((void *)(uintptr_t)ioapic.back()->Address, (void *)(uintptr_t)ioapic.back()->Address, Memory::PTFlag::RW | Memory::PTFlag::PCD); // Make sure that the address is mapped.
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
iso.push_back((MADTIso *)ptr);
|
||||
KPrint("ISO (IRQ:%#lx, BUS:%#lx, GSI:%#lx, %s/%s) found.",
|
||||
iso.back()->IRQSource, iso.back()->BuSSource, iso.back()->GSI,
|
||||
iso.back()->Flags & 0x00000004 ? "Active High" : "Active Low",
|
||||
iso.back()->Flags & 0x00000100 ? "Edge Triggered" : "Level Triggered");
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
nmi.push_back((MADTNmi *)ptr);
|
||||
KPrint("NMI %#lx (lint:%#lx) found.", nmi.back()->processor, nmi.back()->lint);
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
LAPICAddress = (LAPIC *)ptr;
|
||||
KPrint("APIC found at %#lx", LAPICAddress);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
KPrint("Unknown MADT entry %#lx", *(ptr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
Memory::Virtual(KernelPageTable).Map((void *)LAPICAddress, (void *)LAPICAddress, Memory::PTFlag::RW | Memory::PTFlag::PCD); // I should map more than one page?
|
||||
}
|
||||
CPUCores--; // We start at 0 (BSP) and end at 11 (APs), so we have 12 cores.
|
||||
KPrint("Total CPU cores: %d", CPUCores + 1);
|
||||
}
|
||||
|
||||
MADT::~MADT()
|
||||
{
|
||||
}
|
||||
}
|
518
Kernel/arch/amd64/memory/vmm.cpp
Normal file
518
Kernel/arch/amd64/memory/vmm.cpp
Normal file
@ -0,0 +1,518 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <memory.hpp>
|
||||
|
||||
#include <convert.h>
|
||||
#include <debug.h>
|
||||
|
||||
namespace Memory
|
||||
{
|
||||
bool Virtual::Check(void *VirtualAddress, PTFlag Flag)
|
||||
{
|
||||
uintptr_t Address = (uintptr_t)VirtualAddress;
|
||||
Address &= 0xFFFFFFFFFFFFF000;
|
||||
|
||||
PageMapIndexer Index = PageMapIndexer(Address);
|
||||
|
||||
PageDirectoryPointerTableEntryPtr *PDPTE = nullptr;
|
||||
PageDirectoryEntryPtr *PDE = nullptr;
|
||||
PageTableEntryPtr *PTE = nullptr;
|
||||
|
||||
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
|
||||
if (!PML4->Present)
|
||||
{
|
||||
debug("PML4 not present for %#lx", VirtualAddress);
|
||||
return false;
|
||||
}
|
||||
|
||||
PDPTE = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4->GetAddress() << 12);
|
||||
if (!PDPTE)
|
||||
{
|
||||
debug("Failed to get PDPTE for %#lx", VirtualAddress);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PDPTE->Entries[Index.PDPTEIndex].PageSize)
|
||||
{
|
||||
bool result = PDPTE->Entries[Index.PDPTEIndex].raw & Flag;
|
||||
if (!result)
|
||||
{
|
||||
debug("Failed to check %#lx for %#lx (raw: %#lx)", VirtualAddress, Flag,
|
||||
PDPTE->Entries[Index.PDPTEIndex].raw);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
PDE = (PageDirectoryEntryPtr *)((uintptr_t)PDPTE->Entries[Index.PDPTEIndex].GetAddress() << 12);
|
||||
if (!PDE)
|
||||
{
|
||||
debug("Failed to get PDE for %#lx", VirtualAddress);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PDE->Entries[Index.PDEIndex].PageSize)
|
||||
{
|
||||
bool result = PDE->Entries[Index.PDEIndex].raw & Flag;
|
||||
if (!result)
|
||||
{
|
||||
debug("Failed to check %#lx for %#lx (raw: %#lx)", VirtualAddress, Flag,
|
||||
PDE->Entries[Index.PDEIndex].raw);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
PTE = (PageTableEntryPtr *)((uintptr_t)PDE->Entries[Index.PDEIndex].GetAddress() << 12);
|
||||
if (!PTE)
|
||||
{
|
||||
debug("Failed to get PTE for %#lx", VirtualAddress);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = PTE->Entries[Index.PTEIndex].raw & Flag;
|
||||
if (!result)
|
||||
{
|
||||
debug("Failed to check %#lx for %#lx (raw: %#lx)", VirtualAddress, Flag,
|
||||
PTE->Entries[Index.PTEIndex].raw);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void *Virtual::GetPhysical(void *VirtualAddress)
|
||||
{
|
||||
uintptr_t Address = (uintptr_t)VirtualAddress;
|
||||
Address &= 0xFFFFFFFFFFFFF000;
|
||||
|
||||
PageMapIndexer Index = PageMapIndexer(Address);
|
||||
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
|
||||
|
||||
PageDirectoryPointerTableEntryPtr *PDPTE = nullptr;
|
||||
PageDirectoryEntryPtr *PDE = nullptr;
|
||||
PageTableEntryPtr *PTE = nullptr;
|
||||
|
||||
if (PML4->Present)
|
||||
{
|
||||
PDPTE = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4->GetAddress() << 12);
|
||||
if (PDPTE)
|
||||
{
|
||||
if (PDPTE->Entries[Index.PDPTEIndex].Present)
|
||||
{
|
||||
if (PDPTE->Entries[Index.PDPTEIndex].PageSize)
|
||||
return (void *)((uintptr_t)PDPTE->Entries[Index.PDPTEIndex].GetAddress() << 12);
|
||||
|
||||
PDE = (PageDirectoryEntryPtr *)((uintptr_t)PDPTE->Entries[Index.PDPTEIndex].GetAddress() << 12);
|
||||
if (PDE)
|
||||
{
|
||||
if (PDE->Entries[Index.PDEIndex].Present)
|
||||
{
|
||||
if (PDE->Entries[Index.PDEIndex].PageSize)
|
||||
return (void *)((uintptr_t)PDE->Entries[Index.PDEIndex].GetAddress() << 12);
|
||||
|
||||
PTE = (PageTableEntryPtr *)((uintptr_t)PDE->Entries[Index.PDEIndex].GetAddress() << 12);
|
||||
if (PTE)
|
||||
{
|
||||
if (PTE->Entries[Index.PTEIndex].Present)
|
||||
return (void *)((uintptr_t)PTE->Entries[Index.PTEIndex].GetAddress() << 12);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Virtual::MapType Virtual::GetMapType(void *VirtualAddress)
|
||||
{
|
||||
uintptr_t Address = (uintptr_t)VirtualAddress;
|
||||
Address &= 0xFFFFFFFFFFFFF000;
|
||||
|
||||
PageMapIndexer Index = PageMapIndexer(Address);
|
||||
|
||||
PageDirectoryPointerTableEntryPtr *PDPTE = nullptr;
|
||||
PageDirectoryEntryPtr *PDE = nullptr;
|
||||
PageTableEntryPtr *PTE = nullptr;
|
||||
|
||||
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
|
||||
if (!PML4->Present)
|
||||
goto ReturnLogError;
|
||||
|
||||
PDPTE = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4->GetAddress() << 12);
|
||||
if (!PDPTE || !PDPTE->Entries[Index.PDPTEIndex].Present)
|
||||
goto ReturnLogError;
|
||||
|
||||
if (PDPTE->Entries[Index.PDPTEIndex].PageSize)
|
||||
return MapType::OneGiB;
|
||||
|
||||
PDE = (PageDirectoryEntryPtr *)((uintptr_t)PDPTE->Entries[Index.PDPTEIndex].GetAddress() << 12);
|
||||
if (!PDE || !PDE->Entries[Index.PDEIndex].Present)
|
||||
goto ReturnLogError;
|
||||
|
||||
if (PDE->Entries[Index.PDEIndex].PageSize)
|
||||
return MapType::TwoMiB;
|
||||
|
||||
PTE = (PageTableEntryPtr *)((uintptr_t)PDE->Entries[Index.PDEIndex].GetAddress() << 12);
|
||||
if (!PTE)
|
||||
goto ReturnLogError;
|
||||
|
||||
if (PTE->Entries[Index.PTEIndex].Present)
|
||||
return MapType::FourKiB;
|
||||
|
||||
ReturnLogError:
|
||||
return MapType::NoMapType;
|
||||
}
|
||||
|
||||
PageMapLevel5 *Virtual::GetPML5(void *VirtualAddress, MapType Type)
|
||||
{
|
||||
UNUSED(VirtualAddress);
|
||||
UNUSED(Type);
|
||||
stub; /* TODO */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PageMapLevel4 *Virtual::GetPML4(void *VirtualAddress, MapType Type)
|
||||
{
|
||||
UNUSED(Type);
|
||||
uintptr_t Address = (uintptr_t)VirtualAddress;
|
||||
Address &= 0xFFFFFFFFFFFFF000;
|
||||
|
||||
PageMapIndexer Index = PageMapIndexer(Address);
|
||||
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
|
||||
if (PML4->Present)
|
||||
return PML4;
|
||||
|
||||
debug("PML4 not present for %#lx", VirtualAddress);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PageDirectoryPointerTableEntry *Virtual::GetPDPTE(void *VirtualAddress, MapType Type)
|
||||
{
|
||||
UNUSED(Type);
|
||||
uintptr_t Address = (uintptr_t)VirtualAddress;
|
||||
Address &= 0xFFFFFFFFFFFFF000;
|
||||
|
||||
PageMapIndexer Index = PageMapIndexer(Address);
|
||||
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
|
||||
if (!PML4->Present)
|
||||
{
|
||||
debug("PML4 not present for %#lx", VirtualAddress);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PageDirectoryPointerTableEntryPtr *PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4->Address << 12);
|
||||
PageDirectoryPointerTableEntry *PDPTE = &PDPTEPtr->Entries[Index.PDPTEIndex];
|
||||
if (PDPTE->Present)
|
||||
return PDPTE;
|
||||
|
||||
debug("PDPTE not present for %#lx", VirtualAddress);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PageDirectoryEntry *Virtual::GetPDE(void *VirtualAddress, MapType Type)
|
||||
{
|
||||
UNUSED(Type);
|
||||
uintptr_t Address = (uintptr_t)VirtualAddress;
|
||||
Address &= 0xFFFFFFFFFFFFF000;
|
||||
|
||||
PageMapIndexer Index = PageMapIndexer(Address);
|
||||
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
|
||||
if (!PML4->Present)
|
||||
{
|
||||
debug("PML4 not present for %#lx", VirtualAddress);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PageDirectoryPointerTableEntryPtr *PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4->Address << 12);
|
||||
PageDirectoryPointerTableEntry *PDPTE = &PDPTEPtr->Entries[Index.PDPTEIndex];
|
||||
if (!PDPTE->Present)
|
||||
{
|
||||
debug("PDPTE not present for %#lx", VirtualAddress);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PageDirectoryEntryPtr *PDEPtr = (PageDirectoryEntryPtr *)(PDPTE->GetAddress() << 12);
|
||||
PageDirectoryEntry *PDE = &PDEPtr->Entries[Index.PDEIndex];
|
||||
if (PDE->Present)
|
||||
return PDE;
|
||||
|
||||
debug("PDE not present for %#lx", VirtualAddress);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PageTableEntry *Virtual::GetPTE(void *VirtualAddress, MapType Type)
|
||||
{
|
||||
UNUSED(Type);
|
||||
uintptr_t Address = (uintptr_t)VirtualAddress;
|
||||
Address &= 0xFFFFFFFFFFFFF000;
|
||||
|
||||
PageMapIndexer Index = PageMapIndexer(Address);
|
||||
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
|
||||
if (!PML4->Present)
|
||||
{
|
||||
debug("PML4 not present for %#lx", VirtualAddress);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PageDirectoryPointerTableEntryPtr *PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4->Address << 12);
|
||||
PageDirectoryPointerTableEntry *PDPTE = &PDPTEPtr->Entries[Index.PDPTEIndex];
|
||||
if (!PDPTE->Present)
|
||||
{
|
||||
debug("PDPTE not present for %#lx", VirtualAddress);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PageDirectoryEntryPtr *PDEPtr = (PageDirectoryEntryPtr *)(PDPTE->GetAddress() << 12);
|
||||
PageDirectoryEntry *PDE = &PDEPtr->Entries[Index.PDEIndex];
|
||||
if (!PDE->Present)
|
||||
{
|
||||
debug("PDE not present for %#lx", VirtualAddress);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PageTableEntryPtr *PTEPtr = (PageTableEntryPtr *)(PDE->GetAddress() << 12);
|
||||
PageTableEntry *PTE = &PTEPtr->Entries[Index.PTEIndex];
|
||||
if (PTE->Present)
|
||||
return PTE;
|
||||
|
||||
debug("PTE not present for %#lx", VirtualAddress);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Virtual::Map(void *VirtualAddress, void *PhysicalAddress, uint64_t Flags, MapType Type)
|
||||
{
|
||||
SmartLock(this->MemoryLock);
|
||||
if (unlikely(!this->pTable))
|
||||
{
|
||||
error("No page table");
|
||||
return;
|
||||
}
|
||||
|
||||
Flags |= PTFlag::P;
|
||||
|
||||
PageMapIndexer Index = PageMapIndexer((uintptr_t)VirtualAddress);
|
||||
// Clear any flags that are not 1 << 0 (Present) - 1 << 5 (Accessed) because rest are for page table entries only
|
||||
uint64_t DirectoryFlags = Flags & 0x3F;
|
||||
|
||||
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
|
||||
PageDirectoryPointerTableEntryPtr *PDPTEPtr = nullptr;
|
||||
if (!PML4->Present)
|
||||
{
|
||||
PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)KernelAllocator.RequestPages(TO_PAGES(sizeof(PageDirectoryPointerTableEntryPtr) + 1));
|
||||
memset(PDPTEPtr, 0, sizeof(PageDirectoryPointerTableEntryPtr));
|
||||
PML4->Present = true;
|
||||
PML4->SetAddress((uintptr_t)PDPTEPtr >> 12);
|
||||
}
|
||||
else
|
||||
PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)(PML4->GetAddress() << 12);
|
||||
PML4->raw |= DirectoryFlags;
|
||||
|
||||
PageDirectoryPointerTableEntry *PDPTE = &PDPTEPtr->Entries[Index.PDPTEIndex];
|
||||
if (Type == MapType::OneGiB)
|
||||
{
|
||||
PDPTE->raw |= Flags;
|
||||
PDPTE->PageSize = true;
|
||||
PDPTE->SetAddress((uintptr_t)PhysicalAddress >> 12);
|
||||
debug("Mapped 1GB page at %p to %p", VirtualAddress, PhysicalAddress);
|
||||
return;
|
||||
}
|
||||
|
||||
PageDirectoryEntryPtr *PDEPtr = nullptr;
|
||||
if (!PDPTE->Present)
|
||||
{
|
||||
PDEPtr = (PageDirectoryEntryPtr *)KernelAllocator.RequestPages(TO_PAGES(sizeof(PageDirectoryEntryPtr) + 1));
|
||||
memset(PDEPtr, 0, sizeof(PageDirectoryEntryPtr));
|
||||
PDPTE->Present = true;
|
||||
PDPTE->SetAddress((uintptr_t)PDEPtr >> 12);
|
||||
}
|
||||
else
|
||||
PDEPtr = (PageDirectoryEntryPtr *)(PDPTE->GetAddress() << 12);
|
||||
PDPTE->raw |= DirectoryFlags;
|
||||
|
||||
PageDirectoryEntry *PDE = &PDEPtr->Entries[Index.PDEIndex];
|
||||
if (Type == MapType::TwoMiB)
|
||||
{
|
||||
PDE->raw |= Flags;
|
||||
PDE->PageSize = true;
|
||||
PDE->SetAddress((uintptr_t)PhysicalAddress >> 12);
|
||||
debug("Mapped 2MB page at %p to %p", VirtualAddress, PhysicalAddress);
|
||||
return;
|
||||
}
|
||||
|
||||
PageTableEntryPtr *PTEPtr = nullptr;
|
||||
if (!PDE->Present)
|
||||
{
|
||||
PTEPtr = (PageTableEntryPtr *)KernelAllocator.RequestPages(TO_PAGES(sizeof(PageTableEntryPtr) + 1));
|
||||
memset(PTEPtr, 0, sizeof(PageTableEntryPtr));
|
||||
PDE->Present = true;
|
||||
PDE->SetAddress((uintptr_t)PTEPtr >> 12);
|
||||
}
|
||||
else
|
||||
PTEPtr = (PageTableEntryPtr *)(PDE->GetAddress() << 12);
|
||||
PDE->raw |= DirectoryFlags;
|
||||
|
||||
PageTableEntry *PTE = &PTEPtr->Entries[Index.PTEIndex];
|
||||
PTE->Present = true;
|
||||
PTE->raw |= Flags;
|
||||
PTE->SetAddress((uintptr_t)PhysicalAddress >> 12);
|
||||
CPU::x64::invlpg(VirtualAddress);
|
||||
|
||||
#ifdef DEBUG
|
||||
/* https://stackoverflow.com/a/3208376/9352057 */
|
||||
#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
|
||||
#define BYTE_TO_BINARY(byte) \
|
||||
(byte & 0x80 ? '1' : '0'), \
|
||||
(byte & 0x40 ? '1' : '0'), \
|
||||
(byte & 0x20 ? '1' : '0'), \
|
||||
(byte & 0x10 ? '1' : '0'), \
|
||||
(byte & 0x08 ? '1' : '0'), \
|
||||
(byte & 0x04 ? '1' : '0'), \
|
||||
(byte & 0x02 ? '1' : '0'), \
|
||||
(byte & 0x01 ? '1' : '0')
|
||||
|
||||
if (!this->Check(VirtualAddress, (PTFlag)Flags)) // quick workaround just to see where it fails
|
||||
warn("Failed to map v:%#lx p:%#lx with flags: " BYTE_TO_BINARY_PATTERN, VirtualAddress, PhysicalAddress, BYTE_TO_BINARY(Flags));
|
||||
#endif
|
||||
}
|
||||
|
||||
void Virtual::Unmap(void *VirtualAddress, MapType Type)
|
||||
{
|
||||
SmartLock(this->MemoryLock);
|
||||
if (!this->pTable)
|
||||
{
|
||||
error("No page table");
|
||||
return;
|
||||
}
|
||||
|
||||
PageMapIndexer Index = PageMapIndexer((uintptr_t)VirtualAddress);
|
||||
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
|
||||
if (!PML4->Present)
|
||||
return;
|
||||
|
||||
PageDirectoryPointerTableEntryPtr *PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)((uintptr_t)PML4->Address << 12);
|
||||
PageDirectoryPointerTableEntry *PDPTE = &PDPTEPtr->Entries[Index.PDPTEIndex];
|
||||
if (!PDPTE->Present)
|
||||
return;
|
||||
|
||||
if (Type == MapType::OneGiB && PDPTE->PageSize)
|
||||
{
|
||||
PDPTE->Present = false;
|
||||
return;
|
||||
}
|
||||
|
||||
PageDirectoryEntryPtr *PDEPtr = (PageDirectoryEntryPtr *)((uintptr_t)PDPTE->Address << 12);
|
||||
PageDirectoryEntry *PDE = &PDEPtr->Entries[Index.PDEIndex];
|
||||
if (!PDE->Present)
|
||||
return;
|
||||
|
||||
if (Type == MapType::TwoMiB && PDE->PageSize)
|
||||
{
|
||||
PDE->Present = false;
|
||||
return;
|
||||
}
|
||||
|
||||
PageTableEntryPtr *PTEPtr = (PageTableEntryPtr *)((uintptr_t)PDE->Address << 12);
|
||||
PageTableEntry PTE = PTEPtr->Entries[Index.PTEIndex];
|
||||
if (!PTE.Present)
|
||||
return;
|
||||
|
||||
PTE.Present = false;
|
||||
PTEPtr->Entries[Index.PTEIndex] = PTE;
|
||||
CPU::x64::invlpg(VirtualAddress);
|
||||
}
|
||||
|
||||
void Virtual::Remap(void *VirtualAddress, void *PhysicalAddress, uint64_t Flags, MapType Type)
|
||||
{
|
||||
SmartLock(this->MemoryLock);
|
||||
if (unlikely(!this->pTable))
|
||||
{
|
||||
error("No page table");
|
||||
return;
|
||||
}
|
||||
|
||||
Flags |= PTFlag::P;
|
||||
|
||||
PageMapIndexer Index = PageMapIndexer((uintptr_t)VirtualAddress);
|
||||
// Clear any flags that are not 1 << 0 (Present) - 1 << 5 (Accessed) because rest are for page table entries only
|
||||
uint64_t DirectoryFlags = Flags & 0x3F;
|
||||
|
||||
PageMapLevel4 *PML4 = &this->pTable->Entries[Index.PMLIndex];
|
||||
PageDirectoryPointerTableEntryPtr *PDPTEPtr = nullptr;
|
||||
if (!PML4->Present)
|
||||
{
|
||||
PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)KernelAllocator.RequestPages(TO_PAGES(sizeof(PageDirectoryPointerTableEntryPtr) + 1));
|
||||
memset(PDPTEPtr, 0, sizeof(PageDirectoryPointerTableEntryPtr));
|
||||
PML4->Present = true;
|
||||
PML4->SetAddress((uintptr_t)PDPTEPtr >> 12);
|
||||
}
|
||||
else
|
||||
PDPTEPtr = (PageDirectoryPointerTableEntryPtr *)(PML4->GetAddress() << 12);
|
||||
PML4->raw |= DirectoryFlags;
|
||||
|
||||
PageDirectoryPointerTableEntry *PDPTE = &PDPTEPtr->Entries[Index.PDPTEIndex];
|
||||
if (Type == MapType::OneGiB)
|
||||
{
|
||||
PDPTE->raw &= 0xFFF;
|
||||
PDPTE->raw |= Flags;
|
||||
PDPTE->PageSize = true;
|
||||
PDPTE->SetAddress((uintptr_t)PhysicalAddress >> 12);
|
||||
debug("Mapped 1GB page at %p to %p", VirtualAddress, PhysicalAddress);
|
||||
return;
|
||||
}
|
||||
|
||||
PageDirectoryEntryPtr *PDEPtr = nullptr;
|
||||
if (!PDPTE->Present)
|
||||
{
|
||||
PDEPtr = (PageDirectoryEntryPtr *)KernelAllocator.RequestPages(TO_PAGES(sizeof(PageDirectoryEntryPtr) + 1));
|
||||
memset(PDEPtr, 0, sizeof(PageDirectoryEntryPtr));
|
||||
PDPTE->Present = true;
|
||||
PDPTE->SetAddress((uintptr_t)PDEPtr >> 12);
|
||||
}
|
||||
else
|
||||
PDEPtr = (PageDirectoryEntryPtr *)(PDPTE->GetAddress() << 12);
|
||||
PDPTE->raw |= DirectoryFlags;
|
||||
|
||||
PageDirectoryEntry *PDE = &PDEPtr->Entries[Index.PDEIndex];
|
||||
if (Type == MapType::TwoMiB)
|
||||
{
|
||||
PDE->raw &= 0xFFF;
|
||||
PDE->raw |= Flags;
|
||||
PDE->PageSize = true;
|
||||
PDE->SetAddress((uintptr_t)PhysicalAddress >> 12);
|
||||
debug("Mapped 2MB page at %p to %p", VirtualAddress, PhysicalAddress);
|
||||
return;
|
||||
}
|
||||
|
||||
PageTableEntryPtr *PTEPtr = nullptr;
|
||||
if (!PDE->Present)
|
||||
{
|
||||
PTEPtr = (PageTableEntryPtr *)KernelAllocator.RequestPages(TO_PAGES(sizeof(PageTableEntryPtr) + 1));
|
||||
memset(PTEPtr, 0, sizeof(PageTableEntryPtr));
|
||||
PDE->Present = true;
|
||||
PDE->SetAddress((uintptr_t)PTEPtr >> 12);
|
||||
}
|
||||
else
|
||||
PTEPtr = (PageTableEntryPtr *)(PDE->GetAddress() << 12);
|
||||
PDE->raw |= DirectoryFlags;
|
||||
|
||||
PageTableEntry *PTE = &PTEPtr->Entries[Index.PTEIndex];
|
||||
PTE->raw &= 0xFFF;
|
||||
PTE->raw |= Flags;
|
||||
PTE->Present = true;
|
||||
PTE->SetAddress((uintptr_t)PhysicalAddress >> 12);
|
||||
CPU::x64::invlpg(VirtualAddress);
|
||||
}
|
||||
}
|
99
Kernel/arch/amd64/syscalls.cpp
Normal file
99
Kernel/arch/amd64/syscalls.cpp
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <syscalls.hpp>
|
||||
|
||||
#include <cpu.hpp>
|
||||
|
||||
#include "cpu/gdt.hpp"
|
||||
|
||||
// https://supercip971.github.io/02-wingos-syscalls.html
|
||||
using namespace CPU::x64;
|
||||
|
||||
// "core/SystemCalls.cpp"
|
||||
extern "C" uint64_t SystemCallsHandler(SyscallsFrame *regs);
|
||||
|
||||
extern "C" void SystemCallHandlerStub();
|
||||
|
||||
extern "C" __naked __used __no_stack_protector __aligned(16) void SystemCallHandlerStub()
|
||||
{
|
||||
asmv("swapgs\n"); /* Swap GS to get the gsTCB */
|
||||
asmv("mov %rsp, %gs:0x8\n"); /* We save the current rsp to gsTCB->TempStack */
|
||||
asmv("mov %gs:0x0, %rsp\n"); /* Get gsTCB->SystemCallStack and set it as rsp */
|
||||
asmv("push $0x1b\n"); /* Push user data segment for SyscallsFrame */
|
||||
asmv("push %gs:0x8\n"); /* Push gsTCB->TempStack (old rsp) for SyscallsFrame */
|
||||
asmv("push %r11\n"); /* Push the flags for SyscallsFrame */
|
||||
asmv("push $0x23\n"); /* Push user code segment for SyscallsFrame */
|
||||
asmv("push %rcx\n"); /* Push the return address for SyscallsFrame + sysretq (https://www.felixcloutier.com/x86/sysret) */
|
||||
|
||||
/* Push registers */
|
||||
asmv("push %rax\n"
|
||||
"push %rbx\n"
|
||||
"push %rcx\n"
|
||||
"push %rdx\n"
|
||||
"push %rsi\n"
|
||||
"push %rdi\n"
|
||||
"push %rbp\n"
|
||||
"push %r8\n"
|
||||
"push %r9\n"
|
||||
"push %r10\n"
|
||||
"push %r11\n"
|
||||
"push %r12\n"
|
||||
"push %r13\n"
|
||||
"push %r14\n"
|
||||
"push %r15\n");
|
||||
|
||||
/* Set the first argument to the SyscallsFrame pointer */
|
||||
asmv("mov %rsp, %rdi\n");
|
||||
asmv("mov $0, %rbp\n");
|
||||
asmv("call SystemCallsHandler\n");
|
||||
|
||||
/* Pop registers except rax */
|
||||
asmv("pop %r15\n"
|
||||
"pop %r14\n"
|
||||
"pop %r13\n"
|
||||
"pop %r12\n"
|
||||
"pop %r11\n"
|
||||
"pop %r10\n"
|
||||
"pop %r9\n"
|
||||
"pop %r8\n"
|
||||
"pop %rbp\n"
|
||||
"pop %rdi\n"
|
||||
"pop %rsi\n"
|
||||
"pop %rdx\n"
|
||||
"pop %rcx\n"
|
||||
"pop %rbx\n");
|
||||
|
||||
/* Restore rsp from gsTCB->TempStack */
|
||||
asmv("mov %gs:0x8, %rsp\n");
|
||||
#ifdef DEBUG
|
||||
/* Easier to debug stacks */
|
||||
asmv("movq $0, %gs:0x8\n");
|
||||
#endif
|
||||
|
||||
asmv("swapgs\n"); /* Swap GS back to the user GS */
|
||||
asmv("sti\n"); /* Enable interrupts */
|
||||
asmv("sysretq\n"); /* Return to rcx address in user mode */
|
||||
}
|
||||
|
||||
void InitializeSystemCalls()
|
||||
{
|
||||
wrmsr(MSR_EFER, rdmsr(MSR_EFER) | 1);
|
||||
wrmsr(MSR_STAR, ((uint64_t)(GDT_KERNEL_CODE) << 32) | ((uint64_t)(GDT_KERNEL_DATA | 3) << 48));
|
||||
wrmsr(MSR_LSTAR, (uint64_t)SystemCallHandlerStub);
|
||||
wrmsr(MSR_SYSCALL_MASK, (uint64_t)(1 << 9));
|
||||
}
|
38
Kernel/arch/amd64/tasking/signal_trampoline.s
Normal file
38
Kernel/arch/amd64/tasking/signal_trampoline.s
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.code64
|
||||
|
||||
.global _sig_native_trampoline_start
|
||||
_sig_native_trampoline_start:
|
||||
int $0x3
|
||||
|
||||
.global _sig_native_trampoline_end
|
||||
_sig_native_trampoline_end:
|
||||
|
||||
.global _sig_linux_trampoline_start
|
||||
_sig_linux_trampoline_start:
|
||||
movq %rsp, %rbp
|
||||
movq (%rbp), %rax
|
||||
call *%rax
|
||||
mov %rbp, %rsp
|
||||
/* rt_sigreturn = 15 */
|
||||
movq $15, %rax
|
||||
syscall
|
||||
|
||||
.global _sig_linux_trampoline_end
|
||||
_sig_linux_trampoline_end:
|
379
Kernel/arch/i386/arithmetic_operations.c
Normal file
379
Kernel/arch/i386/arithmetic_operations.c
Normal file
@ -0,0 +1,379 @@
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#pragma GCC diagnostic ignored "-Wsign-conversion"
|
||||
#pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||
|
||||
/* Source: https://github.com/glitchub/arith64 */
|
||||
#define arith64_u64 unsigned long long int
|
||||
#define arith64_s64 signed long long int
|
||||
#define arith64_u32 unsigned int
|
||||
#define arith64_s32 int
|
||||
|
||||
typedef union
|
||||
{
|
||||
arith64_u64 u64;
|
||||
arith64_s64 s64;
|
||||
struct
|
||||
{
|
||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
arith64_u32 hi;
|
||||
arith64_u32 lo;
|
||||
#else
|
||||
arith64_u32 lo;
|
||||
arith64_u32 hi;
|
||||
#endif
|
||||
} u32;
|
||||
struct
|
||||
{
|
||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
arith64_s32 hi;
|
||||
arith64_s32 lo;
|
||||
#else
|
||||
arith64_s32 lo;
|
||||
arith64_s32 hi;
|
||||
#endif
|
||||
} s32;
|
||||
} arith64_word;
|
||||
|
||||
#define arith64_hi(n) (arith64_word){.u64 = n}.u32.hi
|
||||
#define arith64_lo(n) (arith64_word){.u64 = n}.u32.lo
|
||||
#define arith64_neg(a, b) (((a) ^ ((((arith64_s64)(b)) >= 0) - 1)) + (((arith64_s64)(b)) < 0))
|
||||
#define arith64_abs(a) arith64_neg(a, a)
|
||||
|
||||
arith64_s64 __absvdi2(arith64_s64 a)
|
||||
{
|
||||
return arith64_abs(a);
|
||||
}
|
||||
|
||||
arith64_s64 __ashldi3(arith64_s64 a, int b)
|
||||
{
|
||||
arith64_word w = {.s64 = a};
|
||||
|
||||
b &= 63;
|
||||
|
||||
if (b >= 32)
|
||||
{
|
||||
w.u32.hi = w.u32.lo << (b - 32);
|
||||
w.u32.lo = 0;
|
||||
}
|
||||
else if (b)
|
||||
{
|
||||
w.u32.hi = (w.u32.lo >> (32 - b)) | (w.u32.hi << b);
|
||||
w.u32.lo <<= b;
|
||||
}
|
||||
return w.s64;
|
||||
}
|
||||
|
||||
arith64_s64 __ashrdi3(arith64_s64 a, int b)
|
||||
{
|
||||
arith64_word w = {.s64 = a};
|
||||
|
||||
b &= 63;
|
||||
|
||||
if (b >= 32)
|
||||
{
|
||||
w.s32.lo = w.s32.hi >> (b - 32);
|
||||
w.s32.hi >>= 31; // 0xFFFFFFFF or 0
|
||||
}
|
||||
else if (b)
|
||||
{
|
||||
w.u32.lo = (w.u32.hi << (32 - b)) | (w.u32.lo >> b);
|
||||
w.s32.hi >>= b;
|
||||
}
|
||||
return w.s64;
|
||||
}
|
||||
|
||||
int __clzsi2(arith64_u32 a)
|
||||
{
|
||||
int b, n = 0;
|
||||
b = !(a & 0xffff0000) << 4;
|
||||
n += b;
|
||||
a <<= b;
|
||||
b = !(a & 0xff000000) << 3;
|
||||
n += b;
|
||||
a <<= b;
|
||||
b = !(a & 0xf0000000) << 2;
|
||||
n += b;
|
||||
a <<= b;
|
||||
b = !(a & 0xc0000000) << 1;
|
||||
n += b;
|
||||
a <<= b;
|
||||
return n + !(a & 0x80000000);
|
||||
}
|
||||
|
||||
int __clzdi2(arith64_u64 a)
|
||||
{
|
||||
int b, n = 0;
|
||||
b = !(a & 0xffffffff00000000ULL) << 5;
|
||||
n += b;
|
||||
a <<= b;
|
||||
b = !(a & 0xffff000000000000ULL) << 4;
|
||||
n += b;
|
||||
a <<= b;
|
||||
b = !(a & 0xff00000000000000ULL) << 3;
|
||||
n += b;
|
||||
a <<= b;
|
||||
b = !(a & 0xf000000000000000ULL) << 2;
|
||||
n += b;
|
||||
a <<= b;
|
||||
b = !(a & 0xc000000000000000ULL) << 1;
|
||||
n += b;
|
||||
a <<= b;
|
||||
return n + !(a & 0x8000000000000000ULL);
|
||||
}
|
||||
|
||||
int __ctzsi2(arith64_u32 a)
|
||||
{
|
||||
int b, n = 0;
|
||||
b = !(a & 0x0000ffff) << 4;
|
||||
n += b;
|
||||
a >>= b;
|
||||
b = !(a & 0x000000ff) << 3;
|
||||
n += b;
|
||||
a >>= b;
|
||||
b = !(a & 0x0000000f) << 2;
|
||||
n += b;
|
||||
a >>= b;
|
||||
b = !(a & 0x00000003) << 1;
|
||||
n += b;
|
||||
a >>= b;
|
||||
return n + !(a & 0x00000001);
|
||||
}
|
||||
|
||||
int __ctzdi2(arith64_u64 a)
|
||||
{
|
||||
int b, n = 0;
|
||||
b = !(a & 0x00000000ffffffffULL) << 5;
|
||||
n += b;
|
||||
a >>= b;
|
||||
b = !(a & 0x000000000000ffffULL) << 4;
|
||||
n += b;
|
||||
a >>= b;
|
||||
b = !(a & 0x00000000000000ffULL) << 3;
|
||||
n += b;
|
||||
a >>= b;
|
||||
b = !(a & 0x000000000000000fULL) << 2;
|
||||
n += b;
|
||||
a >>= b;
|
||||
b = !(a & 0x0000000000000003ULL) << 1;
|
||||
n += b;
|
||||
a >>= b;
|
||||
return n + !(a & 0x0000000000000001ULL);
|
||||
}
|
||||
|
||||
arith64_u64 __divmoddi4(arith64_u64 a, arith64_u64 b, arith64_u64 *c)
|
||||
{
|
||||
if (b > a)
|
||||
{
|
||||
if (c)
|
||||
*c = a;
|
||||
return 0;
|
||||
}
|
||||
if (!arith64_hi(b))
|
||||
{
|
||||
if (b == 0)
|
||||
{
|
||||
volatile char x = 0;
|
||||
x = 1 / x;
|
||||
}
|
||||
if (b == 1)
|
||||
{
|
||||
if (c)
|
||||
*c = 0;
|
||||
return a;
|
||||
}
|
||||
if (!arith64_hi(a))
|
||||
{
|
||||
if (c)
|
||||
*c = arith64_lo(a) % arith64_lo(b);
|
||||
return arith64_lo(a) / arith64_lo(b);
|
||||
}
|
||||
}
|
||||
|
||||
char bits = __clzdi2(b) - __clzdi2(a) + 1;
|
||||
arith64_u64 rem = a >> bits;
|
||||
a <<= 64 - bits;
|
||||
arith64_u64 wrap = 0;
|
||||
while (bits-- > 0)
|
||||
{
|
||||
rem = (rem << 1) | (a >> 63);
|
||||
a = (a << 1) | (wrap & 1);
|
||||
wrap = ((arith64_s64)(b - rem - 1) >> 63);
|
||||
rem -= b & wrap;
|
||||
}
|
||||
if (c)
|
||||
*c = rem;
|
||||
return (a << 1) | (wrap & 1);
|
||||
}
|
||||
|
||||
arith64_s64 __divdi3(arith64_s64 a, arith64_s64 b)
|
||||
{
|
||||
arith64_u64 q = __divmoddi4(arith64_abs(a), arith64_abs(b), (void *)0);
|
||||
return arith64_neg(q, a ^ b);
|
||||
}
|
||||
|
||||
int __ffsdi2(arith64_u64 a) { return a ? __ctzdi2(a) + 1 : 0; }
|
||||
|
||||
arith64_u64 __lshrdi3(arith64_u64 a, int b)
|
||||
{
|
||||
arith64_word w = {.u64 = a};
|
||||
|
||||
b &= 63;
|
||||
|
||||
if (b >= 32)
|
||||
{
|
||||
w.u32.lo = w.u32.hi >> (b - 32);
|
||||
w.u32.hi = 0;
|
||||
}
|
||||
else if (b)
|
||||
{
|
||||
w.u32.lo = (w.u32.hi << (32 - b)) | (w.u32.lo >> b);
|
||||
w.u32.hi >>= b;
|
||||
}
|
||||
return w.u64;
|
||||
}
|
||||
|
||||
arith64_s64 __moddi3(arith64_s64 a, arith64_s64 b)
|
||||
{
|
||||
arith64_u64 r;
|
||||
__divmoddi4(arith64_abs(a), arith64_abs(b), &r);
|
||||
return arith64_neg(r, a);
|
||||
}
|
||||
|
||||
int __popcountsi2(arith64_u32 a)
|
||||
{
|
||||
|
||||
a = a - ((a >> 1) & 0x55555555);
|
||||
a = ((a >> 2) & 0x33333333) + (a & 0x33333333);
|
||||
a = (a + (a >> 4)) & 0x0F0F0F0F;
|
||||
a = (a + (a >> 16));
|
||||
|
||||
return (a + (a >> 8)) & 63;
|
||||
}
|
||||
|
||||
int __popcountdi2(arith64_u64 a)
|
||||
{
|
||||
|
||||
a = a - ((a >> 1) & 0x5555555555555555ULL);
|
||||
a = ((a >> 2) & 0x3333333333333333ULL) + (a & 0x3333333333333333ULL);
|
||||
a = (a + (a >> 4)) & 0x0F0F0F0F0F0F0F0FULL;
|
||||
a = (a + (a >> 32));
|
||||
a = (a + (a >> 16));
|
||||
|
||||
return (a + (a >> 8)) & 127;
|
||||
}
|
||||
|
||||
arith64_u64 __udivdi3(arith64_u64 a, arith64_u64 b) { return __divmoddi4(a, b, (void *)0); }
|
||||
|
||||
arith64_u64 __umoddi3(arith64_u64 a, arith64_u64 b)
|
||||
{
|
||||
arith64_u64 r;
|
||||
__divmoddi4(a, b, &r);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Good documentation: https://splichal.eu/scripts/sphinx/gccint/_build/html/the-gcc-low-level-runtime-library/routines-for-floating-point-emulation.html */
|
||||
|
||||
double __adddf3(double a, double b) { return a + b; }
|
||||
double __muldf3(double a, double b) { return a * b; }
|
||||
double __floatsidf(int i) { return (double)i; }
|
||||
int __ltdf2(double a, double b) { return a < b; }
|
||||
int __gtdf2(double a, double b) { return a > b; }
|
||||
int __nedf2(double a, double b) { return a != b; }
|
||||
int __eqdf2(double a, double b) { return a == b; }
|
||||
double __floatdidf(long i) { return (double)i; }
|
||||
double __divdf3(double a, double b) { return a / b; }
|
||||
double __subdf3(double a, double b) { return a - b; }
|
||||
int __gedf2(double a, double b) { return a >= b; }
|
||||
int __fixdfsi(double a) { return (int)a; }
|
||||
long __fixdfdi(double a) { return (long)a; }
|
||||
int __ledf2(double a, double b) { return a <= b; }
|
||||
|
||||
/* FIXME: Check if these functions are implemented correctly */
|
||||
|
||||
typedef long long int64_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef struct
|
||||
{
|
||||
uint64_t value;
|
||||
} atomic_uint64_t;
|
||||
|
||||
/* No longer needed? */
|
||||
|
||||
// uint64_t __atomic_load_8(const atomic_uint64_t *p)
|
||||
// {
|
||||
// uint64_t value;
|
||||
// __asm__ volatile("lock cmpxchg8b %1"
|
||||
// : "=A"(value)
|
||||
// : "m"(*p)
|
||||
// : "memory");
|
||||
// return value;
|
||||
// }
|
||||
|
||||
// void __atomic_store_8(atomic_uint64_t *p, uint64_t value)
|
||||
// {
|
||||
// __asm__ volatile("lock cmpxchg8b %0"
|
||||
// : "=m"(p->value)
|
||||
// : "a"((uint32_t)value), "d"((uint32_t)(value >> 32)), "m"(*p)
|
||||
// : "memory");
|
||||
// }
|
||||
|
||||
int __fixsfsi(float a)
|
||||
{
|
||||
return (int)a;
|
||||
}
|
||||
|
||||
int __ltsf2(float a, float b)
|
||||
{
|
||||
return -(a < b);
|
||||
}
|
||||
|
||||
int __nesf2(float a, float b)
|
||||
{
|
||||
return a == b;
|
||||
}
|
||||
|
||||
int __eqsf2(float a, float b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
float __divsf3(float a, float b)
|
||||
{
|
||||
return (a / b);
|
||||
}
|
||||
|
||||
double __extendsfdf2(float a)
|
||||
{
|
||||
return (double)a;
|
||||
}
|
||||
|
||||
float __truncdfsf2(double a)
|
||||
{
|
||||
return (float)a;
|
||||
}
|
||||
|
||||
float __subsf3(float a, float b)
|
||||
{
|
||||
return (a - b);
|
||||
}
|
||||
|
||||
float __floatsisf(int a)
|
||||
{
|
||||
return (float)a;
|
||||
}
|
||||
|
||||
int __fixunssfsi(float a)
|
||||
{
|
||||
return (int)a;
|
||||
}
|
||||
|
||||
float __mulsf3(float a, float b)
|
||||
{
|
||||
return (a * b);
|
||||
}
|
||||
|
||||
float __addsf3(float a, float b)
|
||||
{
|
||||
return (a + b);
|
||||
}
|
51
Kernel/arch/i386/bootstrap/Multiboot/Paging/mb_pt.s
Normal file
51
Kernel/arch/i386/bootstrap/Multiboot/Paging/mb_pt.s
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.code32
|
||||
KERNEL_VIRTUAL_BASE = 0xC0000000 /* 3GB */
|
||||
KERNEL_PAGE_NUMBER = 768 /* KERNEL_VIRTUAL_BASE >> 22 */
|
||||
|
||||
.section .bootstrap.data, "a"
|
||||
.align 0x1000
|
||||
.global BootPageTable
|
||||
BootPageTable:
|
||||
.long 0x00000083
|
||||
.long 0x00400083
|
||||
.long 0x00800083
|
||||
.long 0x00C00083
|
||||
.long 0x01000083
|
||||
.long 0x01400083
|
||||
.long 0x01800083
|
||||
.long 0x01C00083
|
||||
.long 0x02000083
|
||||
.long 0x02400083
|
||||
.rept (KERNEL_PAGE_NUMBER - 10)
|
||||
.long 0
|
||||
.endr
|
||||
.long 0x00000083
|
||||
.long 0x00400083
|
||||
.long 0x00800083
|
||||
.long 0x00C00083
|
||||
.long 0x01000083
|
||||
.long 0x01400083
|
||||
.long 0x01800083
|
||||
.long 0x01C00083
|
||||
.long 0x02000083
|
||||
.long 0x02400083
|
||||
.rept (1024 - KERNEL_PAGE_NUMBER - 10)
|
||||
.long 0
|
||||
.endr
|
38
Kernel/arch/i386/bootstrap/Multiboot/_start.s
Normal file
38
Kernel/arch/i386/bootstrap/Multiboot/_start.s
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.code32
|
||||
.extern Multiboot_start
|
||||
|
||||
.section .bootstrap.text, "a"
|
||||
|
||||
.global _start
|
||||
_start:
|
||||
/* Check for multiboot */
|
||||
cmp $0x2BADB002, %eax
|
||||
je .Multiboot
|
||||
|
||||
/* Unkown bootloader */
|
||||
.Hang:
|
||||
cli
|
||||
hlt
|
||||
jmp .Hang
|
||||
|
||||
/* Multiboot */
|
||||
.Multiboot:
|
||||
call Multiboot_start
|
||||
jmp .Hang
|
27
Kernel/arch/i386/bootstrap/Multiboot/headers/header1.s
Normal file
27
Kernel/arch/i386/bootstrap/Multiboot/headers/header1.s
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.intel_syntax noprefix
|
||||
|
||||
.code32
|
||||
.section .multiboot, "a"
|
||||
.align 4
|
||||
|
||||
MULTIBOOT_HEADER:
|
||||
.long 0x1BADB002
|
||||
.long 1 << 0 | 1 << 1
|
||||
.long -(0x1BADB002 + (1 << 0 | 1 << 1))
|
93
Kernel/arch/i386/bootstrap/Multiboot/headers/header2.s
Normal file
93
Kernel/arch/i386/bootstrap/Multiboot/headers/header2.s
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.intel_syntax noprefix
|
||||
|
||||
.code32
|
||||
.extern Multiboot_start
|
||||
|
||||
/* https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html */
|
||||
.section .multiboot2, "a"
|
||||
.align 0x1000
|
||||
MULTIBOOT2_HEADER_START:
|
||||
.long 0xE85250D6
|
||||
.long 0
|
||||
.long (MULTIBOOT2_HEADER_END - MULTIBOOT2_HEADER_START)
|
||||
.long 0x100000000 - (MULTIBOOT2_HEADER_END - MULTIBOOT2_HEADER_START) - 0 - 0xE85250D6
|
||||
.align 8
|
||||
InfoRequestTag_Start:
|
||||
.word 1
|
||||
.word 0
|
||||
.long InfoRequestTag_End - InfoRequestTag_Start
|
||||
.long 1 /* Command Line */
|
||||
.long 2 /* Boot Loader Name */
|
||||
.long 3 /* Module */
|
||||
.long 4 /* Basic Memory Information */
|
||||
.long 5 /* BIOS Boot Device */
|
||||
.long 6 /* Memory Map */
|
||||
.long 7 /* VBE */
|
||||
.long 8 /* Framebuffer */
|
||||
.long 9 /* ELF Sections */
|
||||
.long 10 /* APM Table */
|
||||
.long 11 /* EFI 32-bit System Table Pointer */
|
||||
.long 12 /* EFI 64-bit System Table Pointer */
|
||||
/* .long 13 */ /* SMBIOS */
|
||||
.long 14 /* ACPI Old */
|
||||
.long 15 /* ACPI New */
|
||||
.long 16 /* Network */
|
||||
.long 17 /* EFI Memory Map */
|
||||
.long 18 /* EFI Boot Services Notifier */
|
||||
.long 19 /* EFI 32-bit Image Handle Pointer */
|
||||
.long 20 /* EFI 64-bit Image Handle Pointer */
|
||||
.long 21 /* Load Base Address */
|
||||
InfoRequestTag_End:
|
||||
.align 8
|
||||
FramebufferTag_Start:
|
||||
.word 5
|
||||
.word 1
|
||||
.long FramebufferTag_End - FramebufferTag_Start
|
||||
.long 0
|
||||
.long 0
|
||||
.long 32
|
||||
FramebufferTag_End:
|
||||
.align 8
|
||||
EGATextSupportTag_Start:
|
||||
.word 4
|
||||
.word 0
|
||||
.long EGATextSupportTag_End - EGATextSupportTag_Start
|
||||
.long 0 /* https://www.gnu.org/software/grub/manual/multiboot2/html_node/Console-header-tags.html */
|
||||
EGATextSupportTag_End:
|
||||
.align 8
|
||||
AlignedModulesTag_Start:
|
||||
.word 6
|
||||
.word 0
|
||||
.long AlignedModulesTag_End - AlignedModulesTag_Start
|
||||
AlignedModulesTag_End:
|
||||
.align 8
|
||||
EntryAddressTag_Start:
|
||||
.word 3
|
||||
.word 0
|
||||
.long EntryAddressTag_End - EntryAddressTag_Start
|
||||
.long Multiboot_start
|
||||
EntryAddressTag_End:
|
||||
.align 8
|
||||
EndTag_Start:
|
||||
.word 0
|
||||
.word 0
|
||||
.long EndTag_End - EndTag_Start
|
||||
EndTag_End:
|
||||
MULTIBOOT2_HEADER_END:
|
81
Kernel/arch/i386/bootstrap/Multiboot/helper/detect.s
Normal file
81
Kernel/arch/i386/bootstrap/Multiboot/helper/detect.s
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.intel_syntax noprefix
|
||||
|
||||
.code32
|
||||
.section .bootstrap.text, "a"
|
||||
|
||||
.global DetectCPUID
|
||||
DetectCPUID:
|
||||
pushfd
|
||||
pop eax
|
||||
mov ecx, eax
|
||||
xor eax, 1 << 21
|
||||
push eax
|
||||
popfd
|
||||
pushfd
|
||||
pop eax
|
||||
push ecx
|
||||
popfd
|
||||
xor eax, ecx
|
||||
jz .NoCPUID
|
||||
mov eax, 1
|
||||
ret
|
||||
.NoCPUID:
|
||||
xor eax, eax
|
||||
ret
|
||||
|
||||
.global Detect64Bit
|
||||
Detect64Bit:
|
||||
mov eax, 0x80000000
|
||||
cpuid
|
||||
cmp eax, 0x80000001
|
||||
jb .NoLongMode
|
||||
mov eax, 0x80000001
|
||||
cpuid
|
||||
test edx, 1 << 29
|
||||
jz .NoLongMode
|
||||
mov eax, 1
|
||||
ret
|
||||
.NoLongMode:
|
||||
xor eax, eax
|
||||
ret
|
||||
|
||||
.global DetectPSE
|
||||
DetectPSE:
|
||||
mov eax, 0x00000001
|
||||
cpuid
|
||||
test edx, 0x00000008
|
||||
jz .NoPSE
|
||||
mov eax, 1
|
||||
ret
|
||||
.NoPSE:
|
||||
xor eax, eax
|
||||
ret
|
||||
|
||||
.global DetectPAE
|
||||
DetectPAE:
|
||||
mov eax, 0x00000001
|
||||
cpuid
|
||||
test edx, 0x00000040
|
||||
jz .NoPAE
|
||||
mov eax, 1
|
||||
ret
|
||||
.NoPAE:
|
||||
xor eax, eax
|
||||
ret
|
64
Kernel/arch/i386/bootstrap/Multiboot/helper/gdt32.s
Normal file
64
Kernel/arch/i386/bootstrap/Multiboot/helper/gdt32.s
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.code32
|
||||
.section .bootstrap.text, "a"
|
||||
|
||||
.align 32
|
||||
.global gdtr
|
||||
gdtr:
|
||||
.word GDT32_END - GDT32 - 1
|
||||
.long GDT32
|
||||
|
||||
.align 32
|
||||
GDT32:
|
||||
.quad 0x0
|
||||
|
||||
.word 0xFFFF
|
||||
.word 0x0000
|
||||
.byte 0x00
|
||||
.word 0xCF9A
|
||||
.byte 0x00
|
||||
|
||||
.word 0xFFFF
|
||||
.word 0x0000
|
||||
.byte 0x00
|
||||
.word 0xCF92
|
||||
.byte 0x00
|
||||
|
||||
.word 0x0100
|
||||
.word 0x1000
|
||||
.byte 0x00
|
||||
.word 0x4092
|
||||
.byte 0x00
|
||||
GDT32_END:
|
||||
nop
|
||||
|
||||
.global LoadGDT32
|
||||
LoadGDT32:
|
||||
lgdt [gdtr]
|
||||
ljmp $0x8, $ActivateGDT
|
||||
|
||||
ActivateGDT:
|
||||
mov $0x10, %cx
|
||||
mov %cx, %ss
|
||||
mov %cx, %ds
|
||||
mov %cx, %es
|
||||
mov %cx, %fs
|
||||
mov $0x18, %cx
|
||||
mov %cx, %gs
|
||||
ret
|
210
Kernel/arch/i386/bootstrap/Multiboot/mb1_parse.cpp
Normal file
210
Kernel/arch/i386/bootstrap/Multiboot/mb1_parse.cpp
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
|
||||
#include <boot/protocol/multiboot.h>
|
||||
#include <memory.hpp>
|
||||
|
||||
#include "../../../../kernel.h"
|
||||
|
||||
void multiboot_parse(BootInfo &mb2binfo, uintptr_t Magic, uintptr_t Info)
|
||||
{
|
||||
multiboot_info *InfoAddress = r_cst(multiboot_info *, Info);
|
||||
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_MEMORY)
|
||||
{
|
||||
fixme("mem_lower: %#x, mem_upper: %#x",
|
||||
InfoAddress->mem_lower, InfoAddress->mem_upper);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_BOOTDEV)
|
||||
{
|
||||
fixme("boot_device: %#x",
|
||||
InfoAddress->boot_device);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_CMDLINE)
|
||||
{
|
||||
strncpy(mb2binfo.Kernel.CommandLine,
|
||||
r_cst(const char *, InfoAddress->cmdline),
|
||||
strlen(r_cst(const char *, InfoAddress->cmdline)));
|
||||
debug("Kernel command line: %s", mb2binfo.Kernel.CommandLine);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_MODS)
|
||||
{
|
||||
multiboot_mod_list *module = r_cst(multiboot_mod_list *, InfoAddress->mods_addr);
|
||||
for (size_t i = 0; i < InfoAddress->mods_count; i++)
|
||||
{
|
||||
if (i > MAX_MODULES)
|
||||
{
|
||||
warn("Too many modules, skipping the rest...");
|
||||
break;
|
||||
}
|
||||
mb2binfo.Modules[i].Address = (void *)(uint32_t)module[i].mod_start;
|
||||
mb2binfo.Modules[i].Size = module[i].mod_end - module[i].mod_start;
|
||||
strncpy(mb2binfo.Modules[i].Path, "(null)", 6);
|
||||
strncpy(mb2binfo.Modules[i].CommandLine, r_cst(const char *, module[i].cmdline),
|
||||
strlen(r_cst(const char *, module[i].cmdline)));
|
||||
debug("Module: %s", mb2binfo.Modules[i].Path);
|
||||
}
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_AOUT_SYMS)
|
||||
{
|
||||
fixme("aout_sym: [tabsize: %#x, strsize: %#x, addr: %#x, reserved: %#x]",
|
||||
InfoAddress->u.aout_sym.tabsize, InfoAddress->u.aout_sym.strsize,
|
||||
InfoAddress->u.aout_sym.addr, InfoAddress->u.aout_sym.reserved);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_ELF_SHDR)
|
||||
{
|
||||
mb2binfo.Kernel.Symbols.Num = InfoAddress->u.elf_sec.num;
|
||||
mb2binfo.Kernel.Symbols.EntSize = InfoAddress->u.elf_sec.size;
|
||||
mb2binfo.Kernel.Symbols.Shndx = InfoAddress->u.elf_sec.shndx;
|
||||
mb2binfo.Kernel.Symbols.Sections = s_cst(uintptr_t, InfoAddress->u.elf_sec.addr);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_MEM_MAP)
|
||||
{
|
||||
mb2binfo.Memory.Entries = InfoAddress->mmap_length / sizeof(multiboot_mmap_entry);
|
||||
for (uint32_t i = 0; i < mb2binfo.Memory.Entries; i++)
|
||||
{
|
||||
if (i > MAX_MEMORY_ENTRIES)
|
||||
{
|
||||
warn("Too many memory entries, skipping the rest...");
|
||||
break;
|
||||
}
|
||||
multiboot_mmap_entry entry = r_cst(multiboot_mmap_entry *, InfoAddress->mmap_addr)[i];
|
||||
mb2binfo.Memory.Size += (__SIZE_TYPE__)entry.len;
|
||||
switch (entry.type)
|
||||
{
|
||||
case MULTIBOOT_MEMORY_AVAILABLE:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = (__SIZE_TYPE__)entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = Usable;
|
||||
break;
|
||||
case MULTIBOOT_MEMORY_RESERVED:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = (__SIZE_TYPE__)entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = Reserved;
|
||||
break;
|
||||
case MULTIBOOT_MEMORY_ACPI_RECLAIMABLE:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = (__SIZE_TYPE__)entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = ACPIReclaimable;
|
||||
break;
|
||||
case MULTIBOOT_MEMORY_NVS:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = (__SIZE_TYPE__)entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = ACPINVS;
|
||||
break;
|
||||
case MULTIBOOT_MEMORY_BADRAM:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = (__SIZE_TYPE__)entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = BadMemory;
|
||||
break;
|
||||
default:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = (__SIZE_TYPE__)entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = Unknown;
|
||||
break;
|
||||
}
|
||||
debug("Memory entry: [BaseAddress: %#x, Length: %#x, Type: %d]",
|
||||
mb2binfo.Memory.Entry[i].BaseAddress,
|
||||
mb2binfo.Memory.Entry[i].Length,
|
||||
mb2binfo.Memory.Entry[i].Type);
|
||||
}
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_DRIVE_INFO)
|
||||
{
|
||||
fixme("drives_length: %d, drives_addr: %#x",
|
||||
InfoAddress->drives_length, InfoAddress->drives_addr);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_CONFIG_TABLE)
|
||||
{
|
||||
fixme("config_table: %#x",
|
||||
InfoAddress->config_table);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_BOOT_LOADER_NAME)
|
||||
{
|
||||
strncpy(mb2binfo.Bootloader.Name,
|
||||
r_cst(const char *, InfoAddress->boot_loader_name),
|
||||
strlen(r_cst(const char *, InfoAddress->boot_loader_name)));
|
||||
debug("Bootloader name: %s", mb2binfo.Bootloader.Name);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_APM_TABLE)
|
||||
{
|
||||
fixme("apm_table: %#x",
|
||||
InfoAddress->apm_table);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_VBE_INFO)
|
||||
{
|
||||
fixme("vbe_control_info: %#x, vbe_mode_info: %#x, vbe_mode: %#x, vbe_interface_seg: %#x, vbe_interface_off: %#x, vbe_interface_len: %#x",
|
||||
InfoAddress->vbe_control_info, InfoAddress->vbe_mode_info,
|
||||
InfoAddress->vbe_mode, InfoAddress->vbe_interface_seg,
|
||||
InfoAddress->vbe_interface_off, InfoAddress->vbe_interface_len);
|
||||
}
|
||||
if (InfoAddress->flags & MULTIBOOT_INFO_FRAMEBUFFER_INFO)
|
||||
{
|
||||
static int fb_count = 0;
|
||||
mb2binfo.Framebuffer[fb_count].BaseAddress = (void *)InfoAddress->framebuffer_addr;
|
||||
mb2binfo.Framebuffer[fb_count].Width = InfoAddress->framebuffer_width;
|
||||
mb2binfo.Framebuffer[fb_count].Height = InfoAddress->framebuffer_height;
|
||||
mb2binfo.Framebuffer[fb_count].Pitch = InfoAddress->framebuffer_pitch;
|
||||
mb2binfo.Framebuffer[fb_count].BitsPerPixel = InfoAddress->framebuffer_bpp;
|
||||
switch (InfoAddress->framebuffer_type)
|
||||
{
|
||||
case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED:
|
||||
{
|
||||
mb2binfo.Framebuffer[fb_count].Type = Indexed;
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_FRAMEBUFFER_TYPE_RGB:
|
||||
{
|
||||
mb2binfo.Framebuffer[fb_count].Type = RGB;
|
||||
mb2binfo.Framebuffer[fb_count].RedMaskSize = InfoAddress->framebuffer_red_mask_size;
|
||||
mb2binfo.Framebuffer[fb_count].RedMaskShift = InfoAddress->framebuffer_red_field_position;
|
||||
mb2binfo.Framebuffer[fb_count].GreenMaskSize = InfoAddress->framebuffer_green_mask_size;
|
||||
mb2binfo.Framebuffer[fb_count].GreenMaskShift = InfoAddress->framebuffer_green_field_position;
|
||||
mb2binfo.Framebuffer[fb_count].BlueMaskSize = InfoAddress->framebuffer_blue_mask_size;
|
||||
mb2binfo.Framebuffer[fb_count].BlueMaskShift = InfoAddress->framebuffer_blue_field_position;
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT:
|
||||
{
|
||||
mb2binfo.Framebuffer[fb_count].Type = EGA;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
mb2binfo.Framebuffer[fb_count].Type = Unknown_Framebuffer_Type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
debug("Framebuffer %d: %dx%d %d bpp",
|
||||
fb_count, InfoAddress->framebuffer_width,
|
||||
InfoAddress->framebuffer_height,
|
||||
InfoAddress->framebuffer_bpp);
|
||||
debug("More info:\nAddress: %p\nPitch: %d\nMemoryModel: %d\nRedMaskSize: %d\nRedMaskShift: %d\nGreenMaskSize: %d\nGreenMaskShift: %d\nBlueMaskSize: %d\nBlueMaskShift: %d",
|
||||
InfoAddress->framebuffer_addr, InfoAddress->framebuffer_pitch, InfoAddress->framebuffer_type,
|
||||
InfoAddress->framebuffer_red_mask_size, InfoAddress->framebuffer_red_field_position, InfoAddress->framebuffer_green_mask_size,
|
||||
InfoAddress->framebuffer_green_field_position, InfoAddress->framebuffer_blue_mask_size, InfoAddress->framebuffer_blue_field_position);
|
||||
}
|
||||
|
||||
mb2binfo.Kernel.PhysicalBase = (void *)&_bootstrap_start;
|
||||
mb2binfo.Kernel.VirtualBase = (void *)(uint32_t)((uint32_t)&_bootstrap_start + 0xC0000000);
|
||||
mb2binfo.Kernel.Size = ((uint32_t)&_kernel_end - (uint32_t)&_kernel_start) + ((uint32_t)&_bootstrap_end - (uint32_t)&_bootstrap_start);
|
||||
debug("Kernel base: %p (physical) %p (virtual)", mb2binfo.Kernel.PhysicalBase, mb2binfo.Kernel.VirtualBase);
|
||||
|
||||
Entry(&mb2binfo);
|
||||
}
|
300
Kernel/arch/i386/bootstrap/Multiboot/mb2_parse.cpp
Normal file
300
Kernel/arch/i386/bootstrap/Multiboot/mb2_parse.cpp
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
|
||||
#include <memory.hpp>
|
||||
|
||||
#include <boot/protocol/multiboot2.h>
|
||||
#include "../../../../kernel.h"
|
||||
|
||||
void multiboot2_parse(BootInfo &mb2binfo, uintptr_t Magic, uintptr_t Info)
|
||||
{
|
||||
if (Info == NULL || Magic == NULL)
|
||||
{
|
||||
if (Magic == NULL)
|
||||
error("Multiboot magic is NULL");
|
||||
if (Info == NULL)
|
||||
error("Multiboot info is NULL");
|
||||
CPU::Stop();
|
||||
}
|
||||
else if (Magic != MULTIBOOT2_BOOTLOADER_MAGIC)
|
||||
{
|
||||
error("Multiboot magic is invalid (%#x != %#x)", Magic, MULTIBOOT2_BOOTLOADER_MAGIC);
|
||||
CPU::Stop();
|
||||
}
|
||||
|
||||
{
|
||||
auto InfoAddress = Info;
|
||||
for (auto Tag = (struct multiboot_tag *)((uint8_t *)InfoAddress + 8);
|
||||
;
|
||||
Tag = (struct multiboot_tag *)((multiboot_uint8_t *)Tag + ((Tag->size + 7) & ~7)))
|
||||
{
|
||||
if (Tag->type == MULTIBOOT_TAG_TYPE_END)
|
||||
{
|
||||
debug("End of multiboot2 tags");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (Tag->type)
|
||||
{
|
||||
case MULTIBOOT_TAG_TYPE_CMDLINE:
|
||||
{
|
||||
strncpy(mb2binfo.Kernel.CommandLine,
|
||||
((multiboot_tag_string *)Tag)->string,
|
||||
strlen(((multiboot_tag_string *)Tag)->string));
|
||||
debug("Kernel command line: %s", mb2binfo.Kernel.CommandLine);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME:
|
||||
{
|
||||
strncpy(mb2binfo.Bootloader.Name,
|
||||
((multiboot_tag_string *)Tag)->string,
|
||||
strlen(((multiboot_tag_string *)Tag)->string));
|
||||
debug("Bootloader name: %s", mb2binfo.Bootloader.Name);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_MODULE:
|
||||
{
|
||||
multiboot_tag_module *module = (multiboot_tag_module *)Tag;
|
||||
static int module_count = 0;
|
||||
mb2binfo.Modules[module_count].Address = (void *)(uint32_t)module->mod_start;
|
||||
mb2binfo.Modules[module_count].Size = module->mod_end - module->mod_start;
|
||||
strncpy(mb2binfo.Modules[module_count].Path, "(null)", 6);
|
||||
strncpy(mb2binfo.Modules[module_count].CommandLine, module->cmdline,
|
||||
strlen(module->cmdline));
|
||||
debug("Module: %s", mb2binfo.Modules[module_count].Path);
|
||||
module_count++;
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_BASIC_MEMINFO:
|
||||
{
|
||||
multiboot_tag_basic_meminfo *meminfo = (multiboot_tag_basic_meminfo *)Tag;
|
||||
fixme("basic_meminfo->[mem_lower: %#x, mem_upper: %#x]",
|
||||
meminfo->mem_lower, meminfo->mem_upper);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_BOOTDEV:
|
||||
{
|
||||
multiboot_tag_bootdev *bootdev = (multiboot_tag_bootdev *)Tag;
|
||||
fixme("bootdev->[biosdev: %#x, slice: %#x, part: %#x]",
|
||||
bootdev->biosdev, bootdev->slice, bootdev->part);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_MMAP:
|
||||
{
|
||||
multiboot_tag_mmap *mmap = (multiboot_tag_mmap *)Tag;
|
||||
size_t EntryCount = mmap->size / sizeof(multiboot_mmap_entry);
|
||||
mb2binfo.Memory.Entries = EntryCount;
|
||||
for (uint32_t i = 0; i < EntryCount; i++)
|
||||
{
|
||||
if (i > MAX_MEMORY_ENTRIES)
|
||||
{
|
||||
warn("Too many memory entries, skipping the rest...");
|
||||
break;
|
||||
}
|
||||
multiboot_mmap_entry entry = mmap->entries[i];
|
||||
mb2binfo.Memory.Size += (size_t)entry.len;
|
||||
switch (entry.type)
|
||||
{
|
||||
case MULTIBOOT_MEMORY_AVAILABLE:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = (size_t)entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = Usable;
|
||||
break;
|
||||
case MULTIBOOT_MEMORY_RESERVED:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = (size_t)entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = Reserved;
|
||||
break;
|
||||
case MULTIBOOT_MEMORY_ACPI_RECLAIMABLE:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = (size_t)entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = ACPIReclaimable;
|
||||
break;
|
||||
case MULTIBOOT_MEMORY_NVS:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = (size_t)entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = ACPINVS;
|
||||
break;
|
||||
case MULTIBOOT_MEMORY_BADRAM:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = (size_t)entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = BadMemory;
|
||||
break;
|
||||
default:
|
||||
mb2binfo.Memory.Entry[i].BaseAddress = (void *)entry.addr;
|
||||
mb2binfo.Memory.Entry[i].Length = (size_t)entry.len;
|
||||
mb2binfo.Memory.Entry[i].Type = Unknown;
|
||||
break;
|
||||
}
|
||||
debug("Memory entry: [BaseAddress: %#x, Length: %#x, Type: %d]",
|
||||
mb2binfo.Memory.Entry[i].BaseAddress,
|
||||
mb2binfo.Memory.Entry[i].Length,
|
||||
mb2binfo.Memory.Entry[i].Type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_VBE:
|
||||
{
|
||||
multiboot_tag_vbe *vbe = (multiboot_tag_vbe *)Tag;
|
||||
fixme("vbe->[vbe_mode: %#x, vbe_interface_seg: %#x, vbe_interface_off: %#x, vbe_interface_len: %#x]",
|
||||
vbe->vbe_mode, vbe->vbe_interface_seg, vbe->vbe_interface_off, vbe->vbe_interface_len);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_FRAMEBUFFER:
|
||||
{
|
||||
multiboot_tag_framebuffer *fb = (multiboot_tag_framebuffer *)Tag;
|
||||
static int fb_count = 0;
|
||||
mb2binfo.Framebuffer[fb_count].BaseAddress = (void *)fb->common.framebuffer_addr;
|
||||
mb2binfo.Framebuffer[fb_count].Width = fb->common.framebuffer_width;
|
||||
mb2binfo.Framebuffer[fb_count].Height = fb->common.framebuffer_height;
|
||||
mb2binfo.Framebuffer[fb_count].Pitch = fb->common.framebuffer_pitch;
|
||||
mb2binfo.Framebuffer[fb_count].BitsPerPixel = fb->common.framebuffer_bpp;
|
||||
switch (fb->common.framebuffer_type)
|
||||
{
|
||||
case MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED:
|
||||
{
|
||||
mb2binfo.Framebuffer[fb_count].Type = Indexed;
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_FRAMEBUFFER_TYPE_RGB:
|
||||
{
|
||||
mb2binfo.Framebuffer[fb_count].Type = RGB;
|
||||
mb2binfo.Framebuffer[fb_count].RedMaskSize = fb->framebuffer_red_mask_size;
|
||||
mb2binfo.Framebuffer[fb_count].RedMaskShift = fb->framebuffer_red_field_position;
|
||||
mb2binfo.Framebuffer[fb_count].GreenMaskSize = fb->framebuffer_green_mask_size;
|
||||
mb2binfo.Framebuffer[fb_count].GreenMaskShift = fb->framebuffer_green_field_position;
|
||||
mb2binfo.Framebuffer[fb_count].BlueMaskSize = fb->framebuffer_blue_mask_size;
|
||||
mb2binfo.Framebuffer[fb_count].BlueMaskShift = fb->framebuffer_blue_field_position;
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT:
|
||||
{
|
||||
mb2binfo.Framebuffer[fb_count].Type = EGA;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
mb2binfo.Framebuffer[fb_count].Type = Unknown_Framebuffer_Type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
debug("Framebuffer %d: %dx%d %d bpp", fb_count, fb->common.framebuffer_width, fb->common.framebuffer_height, fb->common.framebuffer_bpp);
|
||||
debug("More info:\nAddress: %p\nPitch: %d\nMemoryModel: %d\nRedMaskSize: %d\nRedMaskShift: %d\nGreenMaskSize: %d\nGreenMaskShift: %d\nBlueMaskSize: %d\nBlueMaskShift: %d",
|
||||
fb->common.framebuffer_addr, fb->common.framebuffer_pitch, fb->common.framebuffer_type,
|
||||
fb->framebuffer_red_mask_size, fb->framebuffer_red_field_position, fb->framebuffer_green_mask_size,
|
||||
fb->framebuffer_green_field_position, fb->framebuffer_blue_mask_size, fb->framebuffer_blue_field_position);
|
||||
fb_count++;
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_ELF_SECTIONS:
|
||||
{
|
||||
multiboot_tag_elf_sections *elf = (multiboot_tag_elf_sections *)Tag;
|
||||
mb2binfo.Kernel.Symbols.Num = elf->num;
|
||||
mb2binfo.Kernel.Symbols.EntSize = elf->entsize;
|
||||
mb2binfo.Kernel.Symbols.Shndx = elf->shndx;
|
||||
mb2binfo.Kernel.Symbols.Sections = (uintptr_t)&elf->sections;
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_APM:
|
||||
{
|
||||
multiboot_tag_apm *apm = (multiboot_tag_apm *)Tag;
|
||||
fixme("apm->[version: %d, cseg: %d, offset: %d, cseg_16: %d, dseg: %d, flags: %d, cseg_len: %d, cseg_16_len: %d, dseg_len: %d]",
|
||||
apm->version, apm->cseg, apm->offset, apm->cseg_16, apm->dseg, apm->flags, apm->cseg_len, apm->cseg_16_len, apm->dseg_len);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_EFI32:
|
||||
{
|
||||
multiboot_tag_efi32 *efi32 = (multiboot_tag_efi32 *)Tag;
|
||||
fixme("efi32->[pointer: %p, size: %d]", efi32->pointer, efi32->size);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_EFI64:
|
||||
{
|
||||
multiboot_tag_efi64 *efi64 = (multiboot_tag_efi64 *)Tag;
|
||||
fixme("efi64->[pointer: %p, size: %d]", efi64->pointer, efi64->size);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_SMBIOS:
|
||||
{
|
||||
multiboot_tag_smbios *smbios = (multiboot_tag_smbios *)Tag;
|
||||
fixme("smbios->[major: %d, minor: %d]", smbios->major, smbios->minor);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_ACPI_OLD:
|
||||
{
|
||||
mb2binfo.RSDP = (BootInfo::RSDPInfo *)((multiboot_tag_old_acpi *)Tag)->rsdp;
|
||||
debug("OLD ACPI RSDP: %p", mb2binfo.RSDP);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_ACPI_NEW:
|
||||
{
|
||||
mb2binfo.RSDP = (BootInfo::RSDPInfo *)((multiboot_tag_new_acpi *)Tag)->rsdp;
|
||||
debug("NEW ACPI RSDP: %p", mb2binfo.RSDP);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_NETWORK:
|
||||
{
|
||||
multiboot_tag_network *net = (multiboot_tag_network *)Tag;
|
||||
fixme("network->[dhcpack: %p]", net->dhcpack);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_EFI_MMAP:
|
||||
{
|
||||
multiboot_tag_efi_mmap *efi_mmap = (multiboot_tag_efi_mmap *)Tag;
|
||||
fixme("efi_mmap->[descr_size: %d, descr_vers: %d, efi_mmap: %p]",
|
||||
efi_mmap->descr_size, efi_mmap->descr_vers, efi_mmap->efi_mmap);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_EFI_BS:
|
||||
{
|
||||
fixme("efi_bs->[%p] (unknown structure)", Tag);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_EFI32_IH:
|
||||
{
|
||||
multiboot_tag_efi32_ih *efi32_ih = (multiboot_tag_efi32_ih *)Tag;
|
||||
fixme("efi32_ih->[pointer: %p]", efi32_ih->pointer);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_EFI64_IH:
|
||||
{
|
||||
multiboot_tag_efi64_ih *efi64_ih = (multiboot_tag_efi64_ih *)Tag;
|
||||
fixme("efi64_ih->[pointer: %p]", efi64_ih->pointer);
|
||||
break;
|
||||
}
|
||||
case MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR:
|
||||
{
|
||||
multiboot_tag_load_base_addr *load_base_addr = (multiboot_tag_load_base_addr *)Tag;
|
||||
mb2binfo.Kernel.PhysicalBase = (void *)(uint32_t)load_base_addr->load_base_addr;
|
||||
mb2binfo.Kernel.VirtualBase = (void *)(uint32_t)(load_base_addr->load_base_addr + 0xC0000000);
|
||||
mb2binfo.Kernel.Size = (size_t)(((uint32_t)&_kernel_end - (uint32_t)&_kernel_start) + ((uint32_t)&_bootstrap_end - (uint32_t)&_bootstrap_start));
|
||||
debug("Kernel base: %p (physical) %p (virtual)", mb2binfo.Kernel.PhysicalBase, mb2binfo.Kernel.VirtualBase);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
error("Unknown multiboot2 tag type: %d", Tag->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Entry(&mb2binfo);
|
||||
}
|
53
Kernel/arch/i386/bootstrap/Multiboot/multiboot.cpp
Normal file
53
Kernel/arch/i386/bootstrap/Multiboot/multiboot.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <types.h>
|
||||
|
||||
#include <memory.hpp>
|
||||
|
||||
#include "../../../../kernel.h"
|
||||
|
||||
#define MULTIBOOT_HEADER_MAGIC 0x1BADB002
|
||||
#define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002
|
||||
#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6
|
||||
#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289
|
||||
|
||||
void multiboot_parse(BootInfo &mb2binfo, uintptr_t Magic, uintptr_t Info);
|
||||
void multiboot2_parse(BootInfo &mb2binfo, uintptr_t Magic, uintptr_t Info);
|
||||
|
||||
EXTERNC void multiboot_main(uintptr_t Magic, uintptr_t Info)
|
||||
{
|
||||
BootInfo mb2binfo{};
|
||||
|
||||
if (Info == NULL || Magic == NULL)
|
||||
{
|
||||
if (Magic == NULL)
|
||||
error("Multiboot magic is NULL");
|
||||
if (Info == NULL)
|
||||
error("Multiboot info is NULL");
|
||||
CPU::Stop();
|
||||
}
|
||||
else if (Magic == MULTIBOOT_BOOTLOADER_MAGIC)
|
||||
multiboot_parse(mb2binfo, Magic, Info);
|
||||
else if (Magic == MULTIBOOT2_BOOTLOADER_MAGIC)
|
||||
multiboot2_parse(mb2binfo, Magic, Info);
|
||||
else
|
||||
{
|
||||
error("Unknown multiboot magic %#x", Magic);
|
||||
CPU::Stop();
|
||||
}
|
||||
}
|
79
Kernel/arch/i386/bootstrap/Multiboot/start.s
Normal file
79
Kernel/arch/i386/bootstrap/Multiboot/start.s
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.code32
|
||||
KERNEL_STACK_SIZE = 0x4000 /* 16KB */
|
||||
|
||||
.extern DetectCPUID
|
||||
.extern DetectPSE
|
||||
.extern multiboot_main
|
||||
.extern LoadGDT32
|
||||
.extern BootPageTable
|
||||
|
||||
.section .bootstrap.data, "a"
|
||||
MB_HeaderMagic:
|
||||
.quad 0
|
||||
|
||||
MB_HeaderInfo:
|
||||
.quad 0
|
||||
|
||||
.section .bootstrap.text, "a"
|
||||
|
||||
.global Multiboot_start
|
||||
Multiboot_start:
|
||||
cli
|
||||
|
||||
mov %eax, [MB_HeaderMagic]
|
||||
mov %ebx, [MB_HeaderInfo]
|
||||
|
||||
call DetectCPUID
|
||||
cmp $0, %eax
|
||||
je .
|
||||
|
||||
call DetectPSE
|
||||
cmp $0, %eax
|
||||
je .
|
||||
|
||||
mov %cr4, %ecx
|
||||
or $0x00000010, %ecx /* PSE */
|
||||
mov %ecx, %cr4
|
||||
|
||||
call LoadGDT32
|
||||
|
||||
mov $BootPageTable, %ecx
|
||||
mov %ecx, %cr3
|
||||
|
||||
mov %cr0, %ecx
|
||||
or $0x80000000, %ecx /* PG */
|
||||
mov %ecx, %cr0
|
||||
|
||||
mov $(KernelStack + KERNEL_STACK_SIZE), %esp
|
||||
mov $0x0, %ebp
|
||||
|
||||
mov [MB_HeaderMagic], %eax
|
||||
mov [MB_HeaderInfo], %ebx
|
||||
push %ebx
|
||||
push %eax
|
||||
call multiboot_main
|
||||
.Hang:
|
||||
hlt
|
||||
jmp .Hang
|
||||
|
||||
.section .bootstrap.bss, "a"
|
||||
.align 16
|
||||
KernelStack:
|
||||
.space KERNEL_STACK_SIZE
|
409
Kernel/arch/i386/cpu/apic.cpp
Normal file
409
Kernel/arch/i386/cpu/apic.cpp
Normal file
@ -0,0 +1,409 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "apic.hpp"
|
||||
|
||||
#include <memory.hpp>
|
||||
#include <uart.hpp>
|
||||
#include <lock.hpp>
|
||||
#include <acpi.hpp>
|
||||
#include <cpu.hpp>
|
||||
#include <smp.hpp>
|
||||
#include <io.h>
|
||||
|
||||
#include "../../../kernel.h"
|
||||
|
||||
NewLock(APICLock);
|
||||
|
||||
using namespace CPU::x32;
|
||||
using namespace CPU::x86;
|
||||
|
||||
/*
|
||||
In constructor 'APIC::APIC::APIC(int)':
|
||||
warning: left shift count >= width of type
|
||||
| APICBaseAddress = BaseStruct.ApicBaseLo << 12u | BaseStruct.ApicBaseHi << 32u;
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~^~~~~~
|
||||
*/
|
||||
#pragma GCC diagnostic ignored "-Wshift-count-overflow"
|
||||
|
||||
namespace APIC
|
||||
{
|
||||
// headache
|
||||
// https://www.amd.com/system/files/TechDocs/24593.pdf
|
||||
// https://www.naic.edu/~phil/software/intel/318148.pdf
|
||||
|
||||
uint32_t APIC::Read(uint32_t Register)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (Register != APIC_ICRLO &&
|
||||
Register != APIC_ICRHI &&
|
||||
Register != APIC_ID)
|
||||
debug("APIC::Read(%#lx) [x2=%d]", Register, x2APICSupported ? 1 : 0);
|
||||
#endif
|
||||
if (x2APICSupported)
|
||||
{
|
||||
if (Register != APIC_ICRHI)
|
||||
return s_cst(uint32_t, rdmsr((Register >> 4) + 0x800));
|
||||
else
|
||||
return s_cst(uint32_t, rdmsr(0x30 + 0x800));
|
||||
}
|
||||
else
|
||||
{
|
||||
CPU::MemBar::Barrier();
|
||||
uint32_t ret = *((volatile uint32_t *)((uintptr_t)APICBaseAddress + Register));
|
||||
CPU::MemBar::Barrier();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void APIC::Write(uint32_t Register, uint32_t Value)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (Register != APIC_EOI &&
|
||||
Register != APIC_TDCR &&
|
||||
Register != APIC_TIMER &&
|
||||
Register != APIC_TICR &&
|
||||
Register != APIC_ICRLO &&
|
||||
Register != APIC_ICRHI)
|
||||
debug("APIC::Write(%#lx, %#lx) [x2=%d]", Register, Value, x2APICSupported ? 1 : 0);
|
||||
#endif
|
||||
if (x2APICSupported)
|
||||
{
|
||||
if (Register != APIC_ICRHI)
|
||||
wrmsr((Register >> 4) + 0x800, Value);
|
||||
else
|
||||
wrmsr(MSR_X2APIC_ICR, Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
CPU::MemBar::Barrier();
|
||||
*((volatile uint32_t *)(((uintptr_t)APICBaseAddress) + Register)) = Value;
|
||||
CPU::MemBar::Barrier();
|
||||
}
|
||||
}
|
||||
|
||||
void APIC::IOWrite(uint64_t Base, uint32_t Register, uint32_t Value)
|
||||
{
|
||||
debug("APIC::IOWrite(%#lx, %#lx, %#lx)", Base, Register, Value);
|
||||
CPU::MemBar::Barrier();
|
||||
*((volatile uint32_t *)(((uintptr_t)Base))) = Register;
|
||||
CPU::MemBar::Barrier();
|
||||
*((volatile uint32_t *)(((uintptr_t)Base + 16))) = Value;
|
||||
CPU::MemBar::Barrier();
|
||||
}
|
||||
|
||||
uint32_t APIC::IORead(uint64_t Base, uint32_t Register)
|
||||
{
|
||||
debug("APIC::IORead(%#lx, %#lx)", Base, Register);
|
||||
CPU::MemBar::Barrier();
|
||||
*((volatile uint32_t *)(((uintptr_t)Base))) = Register;
|
||||
CPU::MemBar::Barrier();
|
||||
uint32_t ret = *((volatile uint32_t *)(((uintptr_t)Base + 16)));
|
||||
CPU::MemBar::Barrier();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void APIC::EOI() { this->Write(APIC_EOI, 0); }
|
||||
|
||||
void APIC::WaitForIPI()
|
||||
{
|
||||
InterruptCommandRegisterLow icr = {.raw = 0};
|
||||
do
|
||||
{
|
||||
icr.raw = this->Read(APIC_ICRLO);
|
||||
CPU::Pause();
|
||||
} while (icr.DeliveryStatus != Idle);
|
||||
}
|
||||
|
||||
void APIC::IPI(uint8_t CPU, InterruptCommandRegisterLow icr)
|
||||
{
|
||||
SmartCriticalSection(APICLock);
|
||||
if (x2APICSupported)
|
||||
{
|
||||
wrmsr(MSR_X2APIC_ICR, s_cst(uint32_t, icr.raw));
|
||||
this->WaitForIPI();
|
||||
}
|
||||
else
|
||||
{
|
||||
this->Write(APIC_ICRHI, (CPU << 24));
|
||||
this->Write(APIC_ICRLO, s_cst(uint32_t, icr.raw));
|
||||
this->WaitForIPI();
|
||||
}
|
||||
}
|
||||
|
||||
void APIC::SendInitIPI(uint8_t CPU)
|
||||
{
|
||||
SmartCriticalSection(APICLock);
|
||||
if (x2APICSupported)
|
||||
{
|
||||
InterruptCommandRegisterLow icr = {.raw = 0};
|
||||
icr.DeliveryMode = INIT;
|
||||
icr.Level = Assert;
|
||||
wrmsr(MSR_X2APIC_ICR, s_cst(uint32_t, icr.raw));
|
||||
this->WaitForIPI();
|
||||
}
|
||||
else
|
||||
{
|
||||
InterruptCommandRegisterLow icr = {.raw = 0};
|
||||
icr.DeliveryMode = INIT;
|
||||
icr.Level = Assert;
|
||||
this->Write(APIC_ICRHI, (CPU << 24));
|
||||
this->Write(APIC_ICRLO, s_cst(uint32_t, icr.raw));
|
||||
this->WaitForIPI();
|
||||
}
|
||||
}
|
||||
|
||||
void APIC::SendStartupIPI(uint8_t CPU, uint64_t StartupAddress)
|
||||
{
|
||||
SmartCriticalSection(APICLock);
|
||||
if (x2APICSupported)
|
||||
{
|
||||
InterruptCommandRegisterLow icr = {.raw = 0};
|
||||
icr.Vector = s_cst(uint8_t, StartupAddress >> 12);
|
||||
icr.DeliveryMode = Startup;
|
||||
icr.Level = Assert;
|
||||
wrmsr(MSR_X2APIC_ICR, s_cst(uint32_t, icr.raw));
|
||||
this->WaitForIPI();
|
||||
}
|
||||
else
|
||||
{
|
||||
InterruptCommandRegisterLow icr = {.raw = 0};
|
||||
icr.Vector = s_cst(uint8_t, StartupAddress >> 12);
|
||||
icr.DeliveryMode = Startup;
|
||||
icr.Level = Assert;
|
||||
this->Write(APIC_ICRHI, (CPU << 24));
|
||||
this->Write(APIC_ICRLO, s_cst(uint32_t, icr.raw));
|
||||
this->WaitForIPI();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t APIC::IOGetMaxRedirect(uint32_t APICID)
|
||||
{
|
||||
uint32_t TableAddress = (this->IORead((((ACPI::MADT *)PowerManager->GetMADT())->ioapic[APICID]->Address), GetIOAPICVersion));
|
||||
return ((IOAPICVersion *)&TableAddress)->MaximumRedirectionEntry;
|
||||
}
|
||||
|
||||
void APIC::RawRedirectIRQ(uint16_t Vector, uint32_t GSI, uint16_t Flags, int CPU, int Status)
|
||||
{
|
||||
uint64_t Value = Vector;
|
||||
|
||||
int64_t IOAPICTarget = -1;
|
||||
for (uint64_t i = 0; ((ACPI::MADT *)PowerManager->GetMADT())->ioapic[std::size_t(i)] != 0; i++)
|
||||
if (((ACPI::MADT *)PowerManager->GetMADT())->ioapic[std::size_t(i)]->GSIBase <= GSI)
|
||||
if (((ACPI::MADT *)PowerManager->GetMADT())->ioapic[std::size_t(i)]->GSIBase + IOGetMaxRedirect(s_cst(uint32_t, i)) > GSI)
|
||||
{
|
||||
IOAPICTarget = i;
|
||||
break;
|
||||
}
|
||||
|
||||
if (IOAPICTarget == -1)
|
||||
{
|
||||
error("No ISO table found for I/O APIC");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: IOAPICRedirectEntry Entry = {.raw = 0};
|
||||
|
||||
if (Flags & ActiveHighLow)
|
||||
Value |= (1 << 13);
|
||||
|
||||
if (Flags & EdgeLevel)
|
||||
Value |= (1 << 15);
|
||||
|
||||
if (!Status)
|
||||
Value |= (1 << 16);
|
||||
|
||||
Value |= (((uintptr_t)CPU) << 56);
|
||||
uint32_t IORegister = (GSI - ((ACPI::MADT *)PowerManager->GetMADT())->ioapic[std::size_t(IOAPICTarget)]->GSIBase) * 2 + 16;
|
||||
|
||||
this->IOWrite(((ACPI::MADT *)PowerManager->GetMADT())->ioapic[std::size_t(IOAPICTarget)]->Address,
|
||||
IORegister, (uint32_t)Value);
|
||||
this->IOWrite(((ACPI::MADT *)PowerManager->GetMADT())->ioapic[std::size_t(IOAPICTarget)]->Address,
|
||||
IORegister + 1, (uint32_t)(Value >> 32));
|
||||
}
|
||||
|
||||
void APIC::RedirectIRQ(int CPU, uint16_t IRQ, int Status)
|
||||
{
|
||||
for (uint64_t i = 0; i < ((ACPI::MADT *)PowerManager->GetMADT())->iso.size(); i++)
|
||||
if (((ACPI::MADT *)PowerManager->GetMADT())->iso[std::size_t(i)]->IRQSource == IRQ)
|
||||
{
|
||||
debug("[ISO %d] Mapping to source IRQ%#d GSI:%#lx on CPU %d",
|
||||
i, ((ACPI::MADT *)PowerManager->GetMADT())->iso[std::size_t(i)]->IRQSource,
|
||||
((ACPI::MADT *)PowerManager->GetMADT())->iso[std::size_t(i)]->GSI,
|
||||
CPU);
|
||||
|
||||
this->RawRedirectIRQ(((ACPI::MADT *)PowerManager->GetMADT())->iso[std::size_t(i)]->IRQSource + 0x20,
|
||||
((ACPI::MADT *)PowerManager->GetMADT())->iso[std::size_t(i)]->GSI,
|
||||
((ACPI::MADT *)PowerManager->GetMADT())->iso[std::size_t(i)]->Flags,
|
||||
CPU, Status);
|
||||
return;
|
||||
}
|
||||
debug("Mapping IRQ%d on CPU %d", IRQ, CPU);
|
||||
this->RawRedirectIRQ(IRQ + 0x20, IRQ, 0, CPU, Status);
|
||||
}
|
||||
|
||||
void APIC::RedirectIRQs(int CPU)
|
||||
{
|
||||
SmartCriticalSection(APICLock);
|
||||
debug("Redirecting IRQs...");
|
||||
for (uint8_t i = 0; i < 16; i++)
|
||||
this->RedirectIRQ(CPU, i, 1);
|
||||
debug("Redirecting IRQs completed.");
|
||||
}
|
||||
|
||||
APIC::APIC(int Core)
|
||||
{
|
||||
SmartCriticalSection(APICLock);
|
||||
APIC_BASE BaseStruct = {.raw = rdmsr(MSR_APIC_BASE)};
|
||||
uint64_t BaseLow = BaseStruct.ApicBaseLo;
|
||||
uint64_t BaseHigh = BaseStruct.ApicBaseHi;
|
||||
this->APICBaseAddress = BaseLow << 12u | BaseHigh << 32u;
|
||||
trace("APIC Address: %#lx", this->APICBaseAddress);
|
||||
Memory::Virtual().Map((void *)this->APICBaseAddress, (void *)this->APICBaseAddress, Memory::PTFlag::RW | Memory::PTFlag::PCD);
|
||||
|
||||
bool x2APICSupported = false;
|
||||
if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0)
|
||||
{
|
||||
CPU::x86::AMD::CPUID0x00000001 cpuid;
|
||||
if (cpuid.ECX.x2APIC)
|
||||
{
|
||||
// x2APICSupported = cpuid.ECX.x2APIC;
|
||||
fixme("x2APIC is supported");
|
||||
}
|
||||
}
|
||||
else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0)
|
||||
{
|
||||
CPU::x86::Intel::CPUID0x00000001 cpuid;
|
||||
if (cpuid.ECX.x2APIC)
|
||||
{
|
||||
// x2APICSupported = cpuid.ECX.x2APIC;
|
||||
fixme("x2APIC is supported");
|
||||
}
|
||||
}
|
||||
|
||||
if (x2APICSupported)
|
||||
{
|
||||
this->x2APICSupported = true;
|
||||
wrmsr(MSR_APIC_BASE, (rdmsr(MSR_APIC_BASE) | (1 << 11)) & ~(1 << 10));
|
||||
BaseStruct.EN = 1;
|
||||
wrmsr(MSR_APIC_BASE, BaseStruct.raw);
|
||||
}
|
||||
else
|
||||
{
|
||||
BaseStruct.EN = 1;
|
||||
wrmsr(MSR_APIC_BASE, BaseStruct.raw);
|
||||
}
|
||||
|
||||
this->Write(APIC_TPR, 0x0);
|
||||
// this->Write(APIC_SVR, this->Read(APIC_SVR) | 0x100); // 0x1FF or 0x100 ? on https://wiki.osdev.org/APIC is 0x100
|
||||
|
||||
if (!this->x2APICSupported)
|
||||
{
|
||||
this->Write(APIC_DFR, 0xF0000000);
|
||||
this->Write(APIC_LDR, this->Read(APIC_ID));
|
||||
}
|
||||
|
||||
ACPI::MADT *madt = (ACPI::MADT *)PowerManager->GetMADT();
|
||||
|
||||
for (size_t i = 0; i < madt->nmi.size(); i++)
|
||||
{
|
||||
if (madt->nmi[std::size_t(i)]->processor != 0xFF && Core != madt->nmi[std::size_t(i)]->processor)
|
||||
return;
|
||||
|
||||
uint32_t nmi = 0x402;
|
||||
if (madt->nmi[std::size_t(i)]->flags & 2)
|
||||
nmi |= 1 << 13;
|
||||
if (madt->nmi[std::size_t(i)]->flags & 8)
|
||||
nmi |= 1 << 15;
|
||||
if (madt->nmi[std::size_t(i)]->lint == 0)
|
||||
this->Write(APIC_LINT0, nmi);
|
||||
else if (madt->nmi[std::size_t(i)]->lint == 1)
|
||||
this->Write(APIC_LINT1, nmi);
|
||||
}
|
||||
|
||||
// Setup the spurrious interrupt vector
|
||||
Spurious Spurious = {.raw = this->Read(APIC_SVR)};
|
||||
Spurious.Vector = IRQ223; // TODO: Should I map the IRQ to something?
|
||||
Spurious.Software = 1;
|
||||
this->Write(APIC_SVR, s_cst(uint32_t, Spurious.raw));
|
||||
|
||||
static int once = 0;
|
||||
if (!once++)
|
||||
{
|
||||
// Disable PIT
|
||||
outb(0x43, 0x28);
|
||||
outb(0x40, 0x0);
|
||||
|
||||
// Disable PIC
|
||||
outb(0x21, 0xFF);
|
||||
outb(0xA1, 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
APIC::~APIC() {}
|
||||
|
||||
void Timer::OnInterruptReceived(CPU::TrapFrame *Frame) { UNUSED(Frame); }
|
||||
|
||||
void Timer::OneShot(uint32_t Vector, uint64_t Miliseconds)
|
||||
{
|
||||
SmartCriticalSection(APICLock);
|
||||
LVTTimer timer = {.raw = 0};
|
||||
timer.Vector = s_cst(uint8_t, Vector);
|
||||
timer.TimerMode = 0;
|
||||
if (strcmp(CPU::Hypervisor(), x86_CPUID_VENDOR_TCG) != 0)
|
||||
this->lapic->Write(APIC_TDCR, DivideBy128);
|
||||
else
|
||||
this->lapic->Write(APIC_TDCR, DivideBy16);
|
||||
this->lapic->Write(APIC_TICR, s_cst(uint32_t, Ticks * Miliseconds));
|
||||
this->lapic->Write(APIC_TIMER, s_cst(uint32_t, timer.raw));
|
||||
}
|
||||
|
||||
Timer::Timer(APIC *apic) : Interrupts::Handler(0) /* IRQ0 */
|
||||
{
|
||||
SmartCriticalSection(APICLock);
|
||||
this->lapic = apic;
|
||||
LVTTimerDivide Divider = DivideBy16;
|
||||
|
||||
trace("Initializing APIC timer on CPU %d", GetCurrentCPU()->ID);
|
||||
|
||||
this->lapic->Write(APIC_TDCR, Divider);
|
||||
this->lapic->Write(APIC_TICR, 0xFFFFFFFF);
|
||||
|
||||
TimeManager->Sleep(1, Time::Units::Milliseconds);
|
||||
|
||||
// Mask the timer
|
||||
this->lapic->Write(APIC_TIMER, 0x10000 /* LVTTimer.Mask flag */);
|
||||
Ticks = 0xFFFFFFFF - this->lapic->Read(APIC_TCCR);
|
||||
|
||||
// Config for IRQ0 timer
|
||||
LVTTimer timer = {.raw = 0};
|
||||
timer.Vector = IRQ0;
|
||||
timer.Mask = Unmasked;
|
||||
timer.TimerMode = LVTTimerMode::OneShot;
|
||||
|
||||
// Initialize APIC timer
|
||||
this->lapic->Write(APIC_TDCR, Divider);
|
||||
this->lapic->Write(APIC_TICR, s_cst(uint32_t, Ticks));
|
||||
this->lapic->Write(APIC_TIMER, s_cst(uint32_t, timer.raw));
|
||||
trace("%d APIC Timer %d ticks in.", GetCurrentCPU()->ID, Ticks);
|
||||
KPrint("APIC Timer: \x1b[1;32m%ld\x1b[0m ticks.", Ticks);
|
||||
}
|
||||
|
||||
Timer::~Timer()
|
||||
{
|
||||
}
|
||||
}
|
356
Kernel/arch/i386/cpu/apic.hpp
Normal file
356
Kernel/arch/i386/cpu/apic.hpp
Normal file
@ -0,0 +1,356 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __FENNIX_KERNEL_APIC_H__
|
||||
#define __FENNIX_KERNEL_APIC_H__
|
||||
|
||||
#include <types.h>
|
||||
|
||||
#include <ints.hpp>
|
||||
#include <cpu.hpp>
|
||||
|
||||
namespace APIC
|
||||
{
|
||||
enum APICRegisters
|
||||
{
|
||||
// source from: https://github.com/pdoane/osdev/blob/master/intr/local_apic.c
|
||||
APIC_ID = 0x20, // Local APIC ID
|
||||
APIC_VER = 0x30, // Local APIC Version
|
||||
APIC_TPR = 0x80, // Task Priority
|
||||
APIC_APR = 0x90, // Arbitration Priority
|
||||
APIC_PPR = 0xA0, // Processor Priority
|
||||
APIC_EOI = 0xB0, // EOI
|
||||
APIC_RRD = 0xC0, // Remote Read
|
||||
APIC_LDR = 0xD0, // Logical Destination
|
||||
APIC_DFR = 0xE0, // Destination Format
|
||||
APIC_SVR = 0xF0, // Spurious Interrupt Vector
|
||||
APIC_ISR = 0x100, // In-Service (8 registers)
|
||||
APIC_TMR = 0x180, // Trigger Mode (8 registers)
|
||||
APIC_IRR = 0x200, // Interrupt Request (8 registers)
|
||||
APIC_ESR = 0x280, // Error Status
|
||||
APIC_ICRLO = 0x300, // Interrupt Command
|
||||
APIC_ICRHI = 0x310, // Interrupt Command [63:32]
|
||||
APIC_TIMER = 0x320, // LVT Timer
|
||||
APIC_THERMAL = 0x330, // LVT Thermal Sensor
|
||||
APIC_PERF = 0x340, // LVT Performance Counter
|
||||
APIC_LINT0 = 0x350, // LVT LINT0
|
||||
APIC_LINT1 = 0x360, // LVT LINT1
|
||||
APIC_ERROR = 0x370, // LVT Error
|
||||
APIC_TICR = 0x380, // Initial Count (for Timer)
|
||||
APIC_TCCR = 0x390, // Current Count (for Timer)
|
||||
APIC_TDCR = 0x3E0, // Divide Configuration (for Timer)
|
||||
};
|
||||
|
||||
enum IOAPICRegisters
|
||||
{
|
||||
GetIOAPICVersion = 0x1
|
||||
};
|
||||
|
||||
enum IOAPICFlags
|
||||
{
|
||||
ActiveHighLow = 2,
|
||||
EdgeLevel = 8
|
||||
};
|
||||
|
||||
enum APICDeliveryMode
|
||||
{
|
||||
Fixed = 0b000,
|
||||
LowestPriority = 0b001, /* Reserved */
|
||||
SMI = 0b010,
|
||||
APIC_DELIVERY_MODE_RESERVED0 = 0b011, /* Reserved */
|
||||
NMI = 0b100,
|
||||
INIT = 0b101,
|
||||
Startup = 0b110,
|
||||
ExtINT = 0b111 /* Reserved */
|
||||
};
|
||||
|
||||
enum APICDestinationMode
|
||||
{
|
||||
Physical = 0b0,
|
||||
Logical = 0b1
|
||||
};
|
||||
|
||||
enum APICDeliveryStatus
|
||||
{
|
||||
Idle = 0b0,
|
||||
SendPending = 0b1
|
||||
};
|
||||
|
||||
enum APICLevel
|
||||
{
|
||||
DeAssert = 0b0,
|
||||
Assert = 0b1
|
||||
};
|
||||
|
||||
enum APICTriggerMode
|
||||
{
|
||||
Edge = 0b0,
|
||||
Level = 0b1
|
||||
};
|
||||
|
||||
enum APICDestinationShorthand
|
||||
{
|
||||
NoShorthand = 0b00,
|
||||
Self = 0b01,
|
||||
AllIncludingSelf = 0b10,
|
||||
AllExcludingSelf = 0b11
|
||||
};
|
||||
|
||||
enum LVTTimerDivide
|
||||
{
|
||||
DivideBy2 = 0b000,
|
||||
DivideBy4 = 0b001,
|
||||
DivideBy8 = 0b010,
|
||||
DivideBy16 = 0b011,
|
||||
DivideBy32 = 0b100,
|
||||
DivideBy64 = 0b101,
|
||||
DivideBy128 = 0b110,
|
||||
DivideBy1 = 0b111
|
||||
};
|
||||
|
||||
enum LVTTimerMask
|
||||
{
|
||||
Unmasked = 0b0,
|
||||
Masked = 0b1
|
||||
};
|
||||
|
||||
enum LVTTimerMode
|
||||
{
|
||||
OneShot = 0b00,
|
||||
Periodic = 0b01,
|
||||
TSCDeadline = 0b10
|
||||
};
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
/** @brief Interrupt Vector */
|
||||
uint64_t Vector : 8;
|
||||
/** @brief Reserved */
|
||||
uint64_t Reserved0 : 4;
|
||||
/**
|
||||
* @brief Delivery Status
|
||||
*
|
||||
* 0: Idle
|
||||
* 1: Send Pending
|
||||
*/
|
||||
uint64_t DeliveryStatus : 1;
|
||||
/** @brief Reserved */
|
||||
uint64_t Reserved1 : 3;
|
||||
/**
|
||||
* @brief Mask
|
||||
*
|
||||
* 0: Not masked
|
||||
* 1: Masked
|
||||
*/
|
||||
uint64_t Mask : 1;
|
||||
/** @brief Timer Mode
|
||||
*
|
||||
* 0: One-shot
|
||||
* 1: Periodic
|
||||
* 2: TSC-Deadline
|
||||
*/
|
||||
uint64_t TimerMode : 1;
|
||||
/** @brief Reserved */
|
||||
uint64_t Reserved2 : 14;
|
||||
};
|
||||
uint64_t raw;
|
||||
} __packed LVTTimer;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
/** @brief Spurious Vector */
|
||||
uint64_t Vector : 8;
|
||||
/** @brief Enable or disable APIC software */
|
||||
uint64_t Software : 1;
|
||||
/** @brief Focus Processor Checking */
|
||||
uint64_t FocusProcessorChecking : 1;
|
||||
/** @brief Reserved */
|
||||
uint64_t Reserved : 2;
|
||||
/** @brief Disable EOI Broadcast */
|
||||
uint64_t DisableEOIBroadcast : 1;
|
||||
/** @brief Reserved */
|
||||
uint64_t Reserved1 : 19;
|
||||
};
|
||||
uint64_t raw;
|
||||
} __packed Spurious;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
/** @brief Interrupt Vector */
|
||||
uint64_t Vector : 8;
|
||||
/** @brief Delivery Mode */
|
||||
uint64_t DeliveryMode : 3;
|
||||
/** @brief Destination Mode
|
||||
*
|
||||
* 0: Physical
|
||||
* 1: Logical
|
||||
*/
|
||||
uint64_t DestinationMode : 1;
|
||||
/** @brief Delivery Status
|
||||
*
|
||||
* @note Reserved when in x2APIC mode
|
||||
*/
|
||||
uint64_t DeliveryStatus : 1;
|
||||
/** @brief Reserved */
|
||||
uint64_t Reserved0 : 1;
|
||||
/** @brief Level
|
||||
*
|
||||
* 0: Deassert
|
||||
* 1: Assert
|
||||
*/
|
||||
uint64_t Level : 1;
|
||||
/** @brief Trigger Mode
|
||||
*
|
||||
* 0: Edge
|
||||
* 1: Level
|
||||
*/
|
||||
uint64_t TriggerMode : 1;
|
||||
/** @brief Reserved */
|
||||
uint64_t Reserved1 : 2;
|
||||
/** @brief Destination Shorthand
|
||||
*
|
||||
* 0: No shorthand
|
||||
* 1: Self
|
||||
* 2: All including self
|
||||
* 3: All excluding self
|
||||
*/
|
||||
uint64_t DestinationShorthand : 2;
|
||||
/** @brief Reserved */
|
||||
uint64_t Reserved2 : 12;
|
||||
};
|
||||
uint64_t raw;
|
||||
} __packed InterruptCommandRegisterLow;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
/** @brief Reserved */
|
||||
uint64_t Reserved0 : 24;
|
||||
/** @brief Destination */
|
||||
uint64_t Destination : 8;
|
||||
};
|
||||
uint64_t raw;
|
||||
} __packed InterruptCommandRegisterHigh;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
/** @brief Interrupt Vector */
|
||||
uint64_t Vector : 8;
|
||||
/** @brief Delivery Mode */
|
||||
uint64_t DeliveryMode : 3;
|
||||
/** @brief Destination Mode
|
||||
*
|
||||
* 0: Physical
|
||||
* 1: Logical
|
||||
*/
|
||||
uint64_t DestinationMode : 1;
|
||||
/** @brief Delivery Status */
|
||||
uint64_t DeliveryStatus : 1;
|
||||
/** @brief Interrupt Input Pin Polarity
|
||||
*
|
||||
* 0: Active High
|
||||
* 1: Active Low
|
||||
*/
|
||||
uint64_t Polarity : 1;
|
||||
/** @brief Remote IRR */
|
||||
uint64_t RemoteIRR : 1;
|
||||
/** @brief Trigger Mode
|
||||
*
|
||||
* 0: Edge
|
||||
* 1: Level
|
||||
*/
|
||||
uint64_t TriggerMode : 1;
|
||||
/** @brief Mask */
|
||||
uint64_t Mask : 1;
|
||||
/** @brief Reserved */
|
||||
uint64_t Reserved0 : 15;
|
||||
/** @brief Reserved */
|
||||
uint64_t Reserved1 : 24;
|
||||
/** @brief Destination */
|
||||
uint64_t DestinationID : 8;
|
||||
};
|
||||
struct
|
||||
{
|
||||
uint64_t Low;
|
||||
uint64_t High;
|
||||
} split;
|
||||
uint64_t raw;
|
||||
} __packed IOAPICRedirectEntry;
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint64_t Version : 8;
|
||||
uint64_t Reserved : 8;
|
||||
uint64_t MaximumRedirectionEntry : 8;
|
||||
uint64_t Reserved2 : 8;
|
||||
};
|
||||
uint64_t raw;
|
||||
} __packed IOAPICVersion;
|
||||
|
||||
class APIC
|
||||
{
|
||||
private:
|
||||
bool x2APICSupported = false;
|
||||
uint64_t APICBaseAddress = 0;
|
||||
|
||||
public:
|
||||
decltype(x2APICSupported) &x2APIC = x2APICSupported;
|
||||
|
||||
uint32_t Read(uint32_t Register);
|
||||
void Write(uint32_t Register, uint32_t Value);
|
||||
void IOWrite(uint64_t Base, uint32_t Register, uint32_t Value);
|
||||
uint32_t IORead(uint64_t Base, uint32_t Register);
|
||||
void EOI();
|
||||
void RedirectIRQs(int CPU = 0);
|
||||
void WaitForIPI();
|
||||
void IPI(uint8_t CPU, InterruptCommandRegisterLow icr);
|
||||
void SendInitIPI(uint8_t CPU);
|
||||
void SendStartupIPI(uint8_t CPU, uint64_t StartupAddress);
|
||||
uint32_t IOGetMaxRedirect(uint32_t APICID);
|
||||
void RawRedirectIRQ(uint16_t Vector, uint32_t GSI, uint16_t Flags, int CPU, int Status);
|
||||
void RedirectIRQ(int CPU, uint16_t IRQ, int Status);
|
||||
APIC(int Core);
|
||||
~APIC();
|
||||
};
|
||||
|
||||
class Timer : public Interrupts::Handler
|
||||
{
|
||||
private:
|
||||
APIC *lapic;
|
||||
uint64_t Ticks = 0;
|
||||
void OnInterruptReceived(CPU::TrapFrame *Frame);
|
||||
|
||||
public:
|
||||
uint64_t GetTicks() { return Ticks; }
|
||||
void OneShot(uint32_t Vector, uint64_t Miliseconds);
|
||||
Timer(APIC *apic);
|
||||
~Timer();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // !__FENNIX_KERNEL_APIC_H__
|
262
Kernel/arch/i386/cpu/gdt.cpp
Normal file
262
Kernel/arch/i386/cpu/gdt.cpp
Normal file
@ -0,0 +1,262 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gdt.hpp"
|
||||
|
||||
#include <memory.hpp>
|
||||
#include <smp.hpp>
|
||||
#include <cpu.hpp>
|
||||
#include <debug.h>
|
||||
|
||||
namespace GlobalDescriptorTable
|
||||
{
|
||||
static GlobalDescriptorTableEntries GDTEntriesTemplate = {
|
||||
.Null =
|
||||
{
|
||||
.Limit0 = 0x0,
|
||||
.BaseLow = 0x0,
|
||||
.BaseMiddle = 0x0,
|
||||
.Access = {.Raw = 0x0},
|
||||
// .Limit1 = 0x0,
|
||||
.Flags = {.Raw = 0x0},
|
||||
.BaseHigh = 0x0,
|
||||
},
|
||||
|
||||
.Code =
|
||||
{
|
||||
.Limit0 = 0xFFFF,
|
||||
.BaseLow = 0x0,
|
||||
.BaseMiddle = 0x0,
|
||||
.Access = {
|
||||
.A = 0,
|
||||
.RW = 1,
|
||||
.DC = 0,
|
||||
.E = 1,
|
||||
.S = 1,
|
||||
.DPL = 0,
|
||||
.P = 1,
|
||||
},
|
||||
// .Limit1 = 0xF,
|
||||
.Flags = {
|
||||
.Reserved = 0xF, /* Workaround for Limit1 */
|
||||
|
||||
.AVL = 0,
|
||||
.L = 0,
|
||||
.DB = 1,
|
||||
.G = 1,
|
||||
},
|
||||
.BaseHigh = 0x0,
|
||||
},
|
||||
|
||||
.Data = {
|
||||
.Limit0 = 0xFFFF,
|
||||
.BaseLow = 0x0,
|
||||
.BaseMiddle = 0x0,
|
||||
.Access = {
|
||||
.A = 0,
|
||||
.RW = 1,
|
||||
.DC = 0,
|
||||
.E = 0,
|
||||
.S = 1,
|
||||
.DPL = 0,
|
||||
.P = 1,
|
||||
},
|
||||
// .Limit1 = 0xF,
|
||||
.Flags = {
|
||||
.Reserved = 0xF, /* Workaround for Limit1 */
|
||||
|
||||
.AVL = 0,
|
||||
.L = 0,
|
||||
.DB = 1,
|
||||
.G = 1,
|
||||
},
|
||||
.BaseHigh = 0x0,
|
||||
},
|
||||
|
||||
.UserData = {
|
||||
.Limit0 = 0xFFFF,
|
||||
.BaseLow = 0x0,
|
||||
.BaseMiddle = 0x0,
|
||||
.Access = {
|
||||
.A = 0,
|
||||
.RW = 1,
|
||||
.DC = 0,
|
||||
.E = 0,
|
||||
.S = 1,
|
||||
.DPL = 3,
|
||||
.P = 1,
|
||||
},
|
||||
// .Limit1 = 0xF,
|
||||
.Flags = {
|
||||
.Reserved = 0xF, /* Workaround for Limit1 */
|
||||
|
||||
.AVL = 0,
|
||||
.L = 0,
|
||||
.DB = 1,
|
||||
.G = 1,
|
||||
},
|
||||
.BaseHigh = 0x0,
|
||||
},
|
||||
|
||||
.UserCode = {
|
||||
.Limit0 = 0xFFFF,
|
||||
.BaseLow = 0x0,
|
||||
.BaseMiddle = 0x0,
|
||||
.Access = {
|
||||
.A = 0,
|
||||
.RW = 1,
|
||||
.DC = 0,
|
||||
.E = 1,
|
||||
.S = 1,
|
||||
.DPL = 3,
|
||||
.P = 1,
|
||||
},
|
||||
// .Limit1 = 0xF,
|
||||
.Flags = {
|
||||
.Reserved = 0xF, /* Workaround for Limit1 */
|
||||
|
||||
.AVL = 0,
|
||||
.L = 0,
|
||||
.DB = 1,
|
||||
.G = 1,
|
||||
},
|
||||
.BaseHigh = 0x0,
|
||||
},
|
||||
|
||||
.TaskStateSegment = {},
|
||||
};
|
||||
|
||||
GlobalDescriptorTableEntries GDTEntries[MAX_CPU] __aligned(16);
|
||||
GlobalDescriptorTableDescriptor gdt[MAX_CPU] __aligned(16);
|
||||
|
||||
TaskStateSegment tss[MAX_CPU] = {
|
||||
0,
|
||||
{0, 0, 0},
|
||||
0,
|
||||
{0, 0, 0, 0, 0, 0, 0},
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
};
|
||||
|
||||
void *CPUStackPointer[MAX_CPU];
|
||||
|
||||
nsa void Init(int Core)
|
||||
{
|
||||
memcpy(&GDTEntries[Core], &GDTEntriesTemplate, sizeof(GlobalDescriptorTableEntries));
|
||||
gdt[Core] = {.Length = sizeof(GlobalDescriptorTableEntries) - 1, .Entries = &GDTEntries[Core]};
|
||||
|
||||
debug("GDT: %#lx", &gdt[Core]);
|
||||
debug("GDT KERNEL: CODE %#lx: Limit0: 0x%X, BaseLow: 0x%X, BaseMiddle: 0x%X, Access: 0x%X, Limit1: 0x%X, Flags: 0x%X, BaseHigh: 0x%X",
|
||||
GDT_KERNEL_CODE,
|
||||
GDTEntries[Core].Code.Limit0,
|
||||
GDTEntries[Core].Code.BaseLow,
|
||||
GDTEntries[Core].Code.BaseMiddle,
|
||||
GDTEntries[Core].Code.Access.Raw,
|
||||
GDTEntries[Core].Code.Flags.Reserved,
|
||||
GDTEntries[Core].Code.Flags.Raw & ~0xF,
|
||||
GDTEntries[Core].Code.BaseHigh);
|
||||
|
||||
debug("GDT KERNEL: DATA %#lx: Limit0: 0x%X, BaseLow: 0x%X, BaseMiddle: 0x%X, Access: 0x%X, Limit1: 0x%X, Flags: 0x%X, BaseHigh: 0x%X",
|
||||
GDT_KERNEL_DATA,
|
||||
GDTEntries[Core].Data.Limit0,
|
||||
GDTEntries[Core].Data.BaseLow,
|
||||
GDTEntries[Core].Data.BaseMiddle,
|
||||
GDTEntries[Core].Data.Access.Raw,
|
||||
GDTEntries[Core].Data.Flags.Reserved,
|
||||
GDTEntries[Core].Data.Flags.Raw & ~0xF,
|
||||
GDTEntries[Core].Data.BaseHigh);
|
||||
|
||||
debug("GDT USER: CODE %#lx: Limit0: 0x%X, BaseLow: 0x%X, BaseMiddle: 0x%X, Access: 0x%X, Limit1: 0x%X, Flags: 0x%X, BaseHigh: 0x%X",
|
||||
GDT_USER_CODE,
|
||||
GDTEntries[Core].UserCode.Limit0,
|
||||
GDTEntries[Core].UserCode.BaseLow,
|
||||
GDTEntries[Core].UserCode.BaseMiddle,
|
||||
GDTEntries[Core].UserCode.Access.Raw,
|
||||
GDTEntries[Core].UserCode.Flags.Reserved,
|
||||
GDTEntries[Core].UserCode.Flags.Raw & ~0xF,
|
||||
GDTEntries[Core].UserCode.BaseHigh);
|
||||
|
||||
debug("GDT USER: DATA %#lx: Limit0: 0x%X, BaseLow: 0x%X, BaseMiddle: 0x%X, Access: 0x%X, Limit1: 0x%X, Flags: 0x%X, BaseHigh: 0x%X",
|
||||
GDT_USER_DATA,
|
||||
GDTEntries[Core].UserData.Limit0,
|
||||
GDTEntries[Core].UserData.BaseLow,
|
||||
GDTEntries[Core].UserData.BaseMiddle,
|
||||
GDTEntries[Core].UserData.Access.Raw,
|
||||
GDTEntries[Core].UserData.Flags.Reserved,
|
||||
GDTEntries[Core].UserData.Flags.Raw & ~0xF,
|
||||
GDTEntries[Core].UserData.BaseHigh);
|
||||
|
||||
CPU::x32::lgdt(&gdt[Core]);
|
||||
|
||||
asmv("mov %%esp, %%eax\n"
|
||||
"push $16\n"
|
||||
"push %%eax\n"
|
||||
"pushf\n"
|
||||
"push $8\n"
|
||||
"push $1f\n"
|
||||
"iret\n"
|
||||
"1:\n"
|
||||
"movw $16, %%ax\n"
|
||||
"movw %%ax, %%ds\n"
|
||||
"movw %%ax, %%es\n" ::
|
||||
: "memory", "eax");
|
||||
|
||||
CPUStackPointer[Core] = StackManager.Allocate(STACK_SIZE);
|
||||
memset(CPUStackPointer[Core], 0, STACK_SIZE);
|
||||
debug("CPU %d Stack Pointer: %#lx-%#lx (%d pages)", Core,
|
||||
CPUStackPointer[Core], (uintptr_t)CPUStackPointer[Core] + STACK_SIZE,
|
||||
TO_PAGES(STACK_SIZE + 1));
|
||||
|
||||
uintptr_t Base = (uintptr_t)&tss[Core];
|
||||
size_t Limit = Base + sizeof(TaskStateSegment);
|
||||
gdt[Core].Entries->TaskStateSegment.Limit = Limit & 0xFFFF;
|
||||
gdt[Core].Entries->TaskStateSegment.BaseLow = Base & 0xFFFF;
|
||||
gdt[Core].Entries->TaskStateSegment.BaseMiddle = uint8_t((Base >> 16) & 0xFF);
|
||||
gdt[Core].Entries->TaskStateSegment.BaseHigh = uint8_t((Base >> 24) & 0xFF);
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wshift-count-overflow"
|
||||
gdt[Core].Entries->TaskStateSegment.BaseUpper = s_cst(uint32_t, (Base >> 32) & 0xFFFFFFFF);
|
||||
|
||||
gdt[Core].Entries->TaskStateSegment.Access = {.A = 1, .RW = 0, .DC = 0, .E = 1, .S = 0, .DPL = 0, .P = 1};
|
||||
gdt[Core].Entries->TaskStateSegment.Granularity = (0 << 4) | ((Limit >> 16) & 0xF);
|
||||
|
||||
tss[Core].IOMapBaseAddressOffset = sizeof(TaskStateSegment);
|
||||
tss[Core].StackPointer[0] = (uint32_t)CPUStackPointer[Core] + STACK_SIZE;
|
||||
tss[Core].StackPointer[1] = 0x0;
|
||||
tss[Core].StackPointer[2] = 0x0;
|
||||
|
||||
for (size_t i = 0; i < sizeof(tss[Core].InterruptStackTable) / sizeof(tss[Core].InterruptStackTable[7]); i++)
|
||||
{
|
||||
void *NewStack = StackManager.Allocate(STACK_SIZE);
|
||||
|
||||
tss[Core].InterruptStackTable[i] = (uint32_t)NewStack + STACK_SIZE;
|
||||
memset((void *)(tss[Core].InterruptStackTable[i] - STACK_SIZE), 0, STACK_SIZE);
|
||||
debug("IST-%d: %#lx-%#lx", i, NewStack, (uintptr_t)NewStack + STACK_SIZE);
|
||||
}
|
||||
|
||||
CPU::x32::ltr(GDT_TSS);
|
||||
debug("Global Descriptor Table initialized");
|
||||
}
|
||||
|
||||
nsa void SetKernelStack(void *Stack)
|
||||
{
|
||||
stub;
|
||||
}
|
||||
|
||||
void *GetKernelStack() { return (void *)nullptr; }
|
||||
}
|
224
Kernel/arch/i386/cpu/gdt.hpp
Normal file
224
Kernel/arch/i386/cpu/gdt.hpp
Normal file
@ -0,0 +1,224 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __FENNIX_KERNEL_GDT_H__
|
||||
#define __FENNIX_KERNEL_GDT_H__
|
||||
|
||||
#include <types.h>
|
||||
|
||||
namespace GlobalDescriptorTable
|
||||
{
|
||||
struct TaskStateSegmentEntry
|
||||
{
|
||||
/* LOW */
|
||||
uint16_t Limit;
|
||||
uint16_t BaseLow;
|
||||
uint8_t BaseMiddle;
|
||||
union GlobalDescriptorTableAccess
|
||||
{
|
||||
struct
|
||||
{
|
||||
/** @brief Access bit.
|
||||
* @note The CPU sets this bit to 1 when the segment is accessed.
|
||||
*/
|
||||
uint8_t A : 1;
|
||||
|
||||
/** @brief Readable bit for code segments, writable bit for data segments.
|
||||
* @details For code segments, this bit must be 1 for the segment to be readable.
|
||||
* @details For data segments, this bit must be 1 for the segment to be writable.
|
||||
*/
|
||||
uint8_t RW : 1;
|
||||
|
||||
/** @brief Direction bit for data segments, conforming bit for code segments.
|
||||
* @details For data segments, this bit must be 1 for the segment to grow up (higher addresses).
|
||||
* @details For code segments, this bit must be 1 for code in the segment to be able to be executed from an equal or lower privilege level.
|
||||
*/
|
||||
uint8_t DC : 1;
|
||||
|
||||
/** @brief Executable bit.
|
||||
* @details This bit must be 1 for code-segment descriptors.
|
||||
* @details This bit must be 0 for data-segment and system descriptors.
|
||||
*/
|
||||
uint8_t E : 1;
|
||||
|
||||
/** @brief Descriptor type.
|
||||
* @details This bit must be 0 for system descriptors.
|
||||
* @details This bit must be 1 for code or data segment descriptor.
|
||||
*/
|
||||
uint8_t S : 1;
|
||||
|
||||
/** @brief Descriptor privilege level.
|
||||
* @details This field determines the privilege level of the segment.
|
||||
* @details 0 = kernel mode, 3 = user mode.
|
||||
*/
|
||||
uint8_t DPL : 2;
|
||||
|
||||
/** @brief Present bit.
|
||||
* @details This bit must be 1 for all valid descriptors.
|
||||
*/
|
||||
uint8_t P : 1;
|
||||
} __packed;
|
||||
uint8_t Raw : 8;
|
||||
} Access;
|
||||
uint8_t Granularity;
|
||||
uint8_t BaseHigh;
|
||||
/* HIGH */
|
||||
uint32_t BaseUpper;
|
||||
uint32_t Reserved;
|
||||
} __packed;
|
||||
|
||||
struct TaskStateSegment
|
||||
{
|
||||
uint32_t Reserved0 __aligned(16);
|
||||
uint64_t StackPointer[3];
|
||||
uint64_t Reserved1;
|
||||
uint64_t InterruptStackTable[7];
|
||||
uint64_t Reserved2;
|
||||
uint16_t Reserved3;
|
||||
uint16_t IOMapBaseAddressOffset;
|
||||
} __packed;
|
||||
|
||||
struct GlobalDescriptorTableEntry
|
||||
{
|
||||
/** @brief Limit 0:15 */
|
||||
uint16_t Limit0 : 16;
|
||||
|
||||
/** @brief Low Base 0:15 */
|
||||
uint16_t BaseLow : 16;
|
||||
|
||||
/** @brief Middle Base 16:23 */
|
||||
uint8_t BaseMiddle : 8;
|
||||
|
||||
/** @brief Access */
|
||||
union GlobalDescriptorTableAccess
|
||||
{
|
||||
struct
|
||||
{
|
||||
/** @brief Access bit.
|
||||
* @note The CPU sets this bit to 1 when the segment is accessed.
|
||||
*/
|
||||
uint8_t A : 1;
|
||||
|
||||
/** @brief Readable bit for code segments, writable bit for data segments.
|
||||
* @details For code segments, this bit must be 1 for the segment to be readable.
|
||||
* @details For data segments, this bit must be 1 for the segment to be writable.
|
||||
*/
|
||||
uint8_t RW : 1;
|
||||
|
||||
/** @brief Direction bit for data segments, conforming bit for code segments.
|
||||
* @details For data segments, this bit must be 1 for the segment to grow up (higher addresses).
|
||||
* @details For code segments, this bit must be 1 for code in the segment to be able to be executed from an equal or lower privilege level.
|
||||
*/
|
||||
uint8_t DC : 1;
|
||||
|
||||
/** @brief Executable bit.
|
||||
* @details This bit must be 1 for code-segment descriptors.
|
||||
* @details This bit must be 0 for data-segment and system descriptors.
|
||||
*/
|
||||
uint8_t E : 1;
|
||||
|
||||
/** @brief Descriptor type.
|
||||
* @details This bit must be 0 for system descriptors.
|
||||
* @details This bit must be 1 for code or data segment descriptor.
|
||||
*/
|
||||
uint8_t S : 1;
|
||||
|
||||
/** @brief Descriptor privilege level.
|
||||
* @details This field determines the privilege level of the segment.
|
||||
* @details 0 = kernel mode, 3 = user mode.
|
||||
*/
|
||||
uint8_t DPL : 2;
|
||||
|
||||
/** @brief Present bit.
|
||||
* @details This bit must be 1 for all valid descriptors.
|
||||
*/
|
||||
uint8_t P : 1;
|
||||
} __packed;
|
||||
uint8_t Raw : 8;
|
||||
} Access;
|
||||
|
||||
// /** @brief Limit 16:19 */
|
||||
// uint16_t Limit1 : 4;
|
||||
|
||||
/** @brief Flags */
|
||||
union GlobalDescriptorTableFlags
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint8_t Reserved : 4; /* FIXME: Without this, the kernel crashes. */
|
||||
/** @brief Available bit.
|
||||
* @details This bit is available for use by system software.
|
||||
*/
|
||||
uint8_t AVL : 1;
|
||||
|
||||
/** @brief Long mode.
|
||||
* @details If the long mode bit is clear, the segment is in 32-bit protected mode.
|
||||
* @details If the long mode bit is set, the segment is in 64-bit long mode.
|
||||
*/
|
||||
uint8_t L : 1;
|
||||
|
||||
/** @brief Size flag.
|
||||
* @details If the size bit is clear, the segment is in 16-bit protected mode.
|
||||
* @details If the size bit is set, the segment is in 32-bit protected mode.
|
||||
*/
|
||||
uint8_t DB : 1;
|
||||
|
||||
/** @brief Granularity bit.
|
||||
* @details If the granularity bit is clear, the segment limit is in 1 B blocks.
|
||||
* @details If the granularity bit is set, the segment limit is in 4 KiB blocks.
|
||||
*/
|
||||
uint8_t G : 1;
|
||||
} __packed;
|
||||
uint8_t Raw : 8;
|
||||
} Flags;
|
||||
|
||||
/** @brief High Base 24:31 */
|
||||
uint8_t BaseHigh : 8;
|
||||
} __packed;
|
||||
|
||||
struct GlobalDescriptorTableEntries
|
||||
{
|
||||
GlobalDescriptorTableEntry Null;
|
||||
GlobalDescriptorTableEntry Code;
|
||||
GlobalDescriptorTableEntry Data;
|
||||
GlobalDescriptorTableEntry UserData;
|
||||
GlobalDescriptorTableEntry UserCode;
|
||||
TaskStateSegmentEntry TaskStateSegment;
|
||||
} __packed;
|
||||
|
||||
struct GlobalDescriptorTableDescriptor
|
||||
{
|
||||
/** @brief GDT entries length */
|
||||
uint16_t Length;
|
||||
/** @brief GDT entries address */
|
||||
GlobalDescriptorTableEntries *Entries;
|
||||
} __packed;
|
||||
|
||||
extern void *CPUStackPointer[];
|
||||
extern TaskStateSegment tss[];
|
||||
void Init(int Core);
|
||||
void SetKernelStack(void *Stack);
|
||||
void *GetKernelStack();
|
||||
}
|
||||
|
||||
#define GDT_KERNEL_CODE offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, Code)
|
||||
#define GDT_KERNEL_DATA offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, Data)
|
||||
#define GDT_USER_CODE (offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, UserCode) | 3)
|
||||
#define GDT_USER_DATA (offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, UserData) | 3)
|
||||
#define GDT_TSS (offsetof(GlobalDescriptorTable::GlobalDescriptorTableEntries, TaskStateSegment) | 3)
|
||||
|
||||
#endif // !__FENNIX_KERNEL_GDT_H__
|
737
Kernel/arch/i386/cpu/idt.cpp
Normal file
737
Kernel/arch/i386/cpu/idt.cpp
Normal file
@ -0,0 +1,737 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "idt.hpp"
|
||||
|
||||
#include <memory.hpp>
|
||||
#include <cpu.hpp>
|
||||
#include <debug.h>
|
||||
#include <io.h>
|
||||
|
||||
#include "gdt.hpp"
|
||||
#include "../../../kernel.h"
|
||||
|
||||
/* conversion from 'uint64_t' {aka 'long unsigned int'} to 'unsigned char:2' may change value */
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
|
||||
extern "C" void MainInterruptHandler(void *Data);
|
||||
extern "C" void ExceptionHandler(void *Data);
|
||||
|
||||
namespace InterruptDescriptorTable
|
||||
{
|
||||
__aligned(8) static IDTGateDescriptor Entries[0x100];
|
||||
|
||||
__aligned(8) IDTRegister IDTr = {
|
||||
.Limit = sizeof(Entries) - 1,
|
||||
.BaseAddress = Entries,
|
||||
};
|
||||
|
||||
void SetEntry(uint8_t Index,
|
||||
void (*Base)(),
|
||||
GateType Gate,
|
||||
PrivilegeLevelType Ring,
|
||||
bool Present,
|
||||
uint16_t SegmentSelector)
|
||||
{
|
||||
switch (Gate)
|
||||
{
|
||||
case INTERRUPT_GATE_32BIT:
|
||||
{
|
||||
InterruptGate gate{
|
||||
.TargetCodeSegmentOffsetLow = s_cst(uint16_t, ((uint32_t)Base & 0xFFFF)),
|
||||
.TargetCodeSegmentSelector = SegmentSelector,
|
||||
.Reserved0 = 0,
|
||||
.Type = Gate,
|
||||
.Zero = 0,
|
||||
.DescriptorPrivilegeLevel = Ring,
|
||||
.Present = Present,
|
||||
.TargetCodeSegmentOffsetHigh = s_cst(uint16_t, ((uint32_t)Base >> 16)),
|
||||
};
|
||||
Entries[Index].Interrupt = gate;
|
||||
break;
|
||||
}
|
||||
case TRAP_GATE_32BIT:
|
||||
{
|
||||
TrapGate gate{
|
||||
.TargetCodeSegmentOffsetLow = s_cst(uint16_t, ((uint32_t)Base & 0xFFFF)),
|
||||
.TargetCodeSegmentSelector = SegmentSelector,
|
||||
.Reserved0 = 0,
|
||||
.Type = Gate,
|
||||
.Zero = 0,
|
||||
.DescriptorPrivilegeLevel = Ring,
|
||||
.Present = Present,
|
||||
.TargetCodeSegmentOffsetHigh = s_cst(uint16_t, ((uint32_t)Base >> 16)),
|
||||
};
|
||||
Entries[Index].Trap = gate;
|
||||
break;
|
||||
}
|
||||
case AVAILABLE_16BIT_TSS:
|
||||
case LDT:
|
||||
case BUSY_16BIT_TSS:
|
||||
case CALL_GATE_16BIT:
|
||||
case TASK_GATE:
|
||||
case INTERRUPT_GATE_16BIT:
|
||||
case TRAP_GATE_16BIT:
|
||||
case AVAILABLE_32BIT_TSS:
|
||||
case BUSY_32BIT_TSS:
|
||||
case CALL_GATE_32BIT:
|
||||
default:
|
||||
{
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __naked __used __no_stack_protector __aligned(16) void ExceptionHandlerStub()
|
||||
{
|
||||
asm("cld\n"
|
||||
"cli\n"
|
||||
|
||||
"pusha\n"
|
||||
|
||||
"push %esp\n"
|
||||
"call ExceptionHandler\n"
|
||||
"pop %esp\n"
|
||||
|
||||
"popa\n"
|
||||
|
||||
"add $8, %esp\n"
|
||||
|
||||
"iret");
|
||||
}
|
||||
|
||||
extern "C" __naked __used __no_stack_protector __aligned(16) void InterruptHandlerStub()
|
||||
{
|
||||
asm("cld\n"
|
||||
"cli\n"
|
||||
|
||||
"pusha\n"
|
||||
|
||||
"push %esp\n"
|
||||
"call MainInterruptHandler\n"
|
||||
"pop %esp\n"
|
||||
|
||||
"popa\n"
|
||||
|
||||
"add $8, %esp\n"
|
||||
|
||||
"sti\n"
|
||||
"iret");
|
||||
}
|
||||
|
||||
#pragma region Exceptions
|
||||
|
||||
#define EXCEPTION_HANDLER(num) \
|
||||
__naked __used __no_stack_protector __aligned(16) static void InterruptHandler_##num() \
|
||||
{ \
|
||||
asm("push $0\npush $" #num "\n" \
|
||||
"jmp ExceptionHandlerStub"); \
|
||||
}
|
||||
|
||||
#define EXCEPTION_ERROR_HANDLER(num) \
|
||||
__naked __used __no_stack_protector __aligned(16) static void InterruptHandler_##num() \
|
||||
{ \
|
||||
asm("push $" #num "\n" \
|
||||
"jmp ExceptionHandlerStub"); \
|
||||
}
|
||||
|
||||
#define INTERRUPT_HANDLER(num) \
|
||||
__naked __used __no_stack_protector __aligned(16) void InterruptHandler_##num() \
|
||||
{ \
|
||||
asm("push $0\npush $" #num "\n" \
|
||||
"jmp InterruptHandlerStub\n"); \
|
||||
}
|
||||
|
||||
/* ISR */
|
||||
|
||||
EXCEPTION_HANDLER(0x0);
|
||||
EXCEPTION_HANDLER(0x1);
|
||||
EXCEPTION_HANDLER(0x2);
|
||||
EXCEPTION_HANDLER(0x3);
|
||||
EXCEPTION_HANDLER(0x4);
|
||||
EXCEPTION_HANDLER(0x5);
|
||||
EXCEPTION_HANDLER(0x6);
|
||||
EXCEPTION_HANDLER(0x7);
|
||||
EXCEPTION_ERROR_HANDLER(0x8);
|
||||
EXCEPTION_HANDLER(0x9);
|
||||
EXCEPTION_ERROR_HANDLER(0xa);
|
||||
EXCEPTION_ERROR_HANDLER(0xb);
|
||||
EXCEPTION_ERROR_HANDLER(0xc);
|
||||
EXCEPTION_ERROR_HANDLER(0xd);
|
||||
EXCEPTION_ERROR_HANDLER(0xe);
|
||||
EXCEPTION_HANDLER(0xf);
|
||||
EXCEPTION_ERROR_HANDLER(0x10);
|
||||
EXCEPTION_HANDLER(0x11);
|
||||
EXCEPTION_HANDLER(0x12);
|
||||
EXCEPTION_HANDLER(0x13);
|
||||
EXCEPTION_HANDLER(0x14);
|
||||
EXCEPTION_HANDLER(0x15);
|
||||
EXCEPTION_HANDLER(0x16);
|
||||
EXCEPTION_HANDLER(0x17);
|
||||
EXCEPTION_HANDLER(0x18);
|
||||
EXCEPTION_HANDLER(0x19);
|
||||
EXCEPTION_HANDLER(0x1a);
|
||||
EXCEPTION_HANDLER(0x1b);
|
||||
EXCEPTION_HANDLER(0x1c);
|
||||
EXCEPTION_HANDLER(0x1d);
|
||||
EXCEPTION_HANDLER(0x1e);
|
||||
EXCEPTION_HANDLER(0x1f);
|
||||
|
||||
/* IRQ */
|
||||
|
||||
INTERRUPT_HANDLER(0x20)
|
||||
INTERRUPT_HANDLER(0x21)
|
||||
INTERRUPT_HANDLER(0x22)
|
||||
INTERRUPT_HANDLER(0x23)
|
||||
INTERRUPT_HANDLER(0x24)
|
||||
INTERRUPT_HANDLER(0x25)
|
||||
INTERRUPT_HANDLER(0x26)
|
||||
INTERRUPT_HANDLER(0x27)
|
||||
INTERRUPT_HANDLER(0x28)
|
||||
INTERRUPT_HANDLER(0x29)
|
||||
INTERRUPT_HANDLER(0x2a)
|
||||
INTERRUPT_HANDLER(0x2b)
|
||||
INTERRUPT_HANDLER(0x2c)
|
||||
INTERRUPT_HANDLER(0x2d)
|
||||
INTERRUPT_HANDLER(0x2e)
|
||||
INTERRUPT_HANDLER(0x2f)
|
||||
|
||||
/* Reserved by OS */
|
||||
|
||||
INTERRUPT_HANDLER(0x30)
|
||||
INTERRUPT_HANDLER(0x31)
|
||||
INTERRUPT_HANDLER(0x32)
|
||||
INTERRUPT_HANDLER(0x33)
|
||||
INTERRUPT_HANDLER(0x34)
|
||||
INTERRUPT_HANDLER(0x35)
|
||||
INTERRUPT_HANDLER(0x36)
|
||||
INTERRUPT_HANDLER(0x37)
|
||||
INTERRUPT_HANDLER(0x38)
|
||||
INTERRUPT_HANDLER(0x39)
|
||||
INTERRUPT_HANDLER(0x3a)
|
||||
INTERRUPT_HANDLER(0x3b)
|
||||
INTERRUPT_HANDLER(0x3c)
|
||||
INTERRUPT_HANDLER(0x3d)
|
||||
|
||||
/* Free */
|
||||
|
||||
INTERRUPT_HANDLER(0x3e)
|
||||
INTERRUPT_HANDLER(0x3f)
|
||||
INTERRUPT_HANDLER(0x40)
|
||||
INTERRUPT_HANDLER(0x41)
|
||||
INTERRUPT_HANDLER(0x42)
|
||||
INTERRUPT_HANDLER(0x43)
|
||||
INTERRUPT_HANDLER(0x44)
|
||||
INTERRUPT_HANDLER(0x45)
|
||||
INTERRUPT_HANDLER(0x46)
|
||||
INTERRUPT_HANDLER(0x47)
|
||||
INTERRUPT_HANDLER(0x48)
|
||||
INTERRUPT_HANDLER(0x49)
|
||||
INTERRUPT_HANDLER(0x4a)
|
||||
INTERRUPT_HANDLER(0x4b)
|
||||
INTERRUPT_HANDLER(0x4c)
|
||||
INTERRUPT_HANDLER(0x4d)
|
||||
INTERRUPT_HANDLER(0x4e)
|
||||
INTERRUPT_HANDLER(0x4f)
|
||||
INTERRUPT_HANDLER(0x50)
|
||||
INTERRUPT_HANDLER(0x51)
|
||||
INTERRUPT_HANDLER(0x52)
|
||||
INTERRUPT_HANDLER(0x53)
|
||||
INTERRUPT_HANDLER(0x54)
|
||||
INTERRUPT_HANDLER(0x55)
|
||||
INTERRUPT_HANDLER(0x56)
|
||||
INTERRUPT_HANDLER(0x57)
|
||||
INTERRUPT_HANDLER(0x58)
|
||||
INTERRUPT_HANDLER(0x59)
|
||||
INTERRUPT_HANDLER(0x5a)
|
||||
INTERRUPT_HANDLER(0x5b)
|
||||
INTERRUPT_HANDLER(0x5c)
|
||||
INTERRUPT_HANDLER(0x5d)
|
||||
INTERRUPT_HANDLER(0x5e)
|
||||
INTERRUPT_HANDLER(0x5f)
|
||||
INTERRUPT_HANDLER(0x60)
|
||||
INTERRUPT_HANDLER(0x61)
|
||||
INTERRUPT_HANDLER(0x62)
|
||||
INTERRUPT_HANDLER(0x63)
|
||||
INTERRUPT_HANDLER(0x64)
|
||||
INTERRUPT_HANDLER(0x65)
|
||||
INTERRUPT_HANDLER(0x66)
|
||||
INTERRUPT_HANDLER(0x67)
|
||||
INTERRUPT_HANDLER(0x68)
|
||||
INTERRUPT_HANDLER(0x69)
|
||||
INTERRUPT_HANDLER(0x6a)
|
||||
INTERRUPT_HANDLER(0x6b)
|
||||
INTERRUPT_HANDLER(0x6c)
|
||||
INTERRUPT_HANDLER(0x6d)
|
||||
INTERRUPT_HANDLER(0x6e)
|
||||
INTERRUPT_HANDLER(0x6f)
|
||||
INTERRUPT_HANDLER(0x70)
|
||||
INTERRUPT_HANDLER(0x71)
|
||||
INTERRUPT_HANDLER(0x72)
|
||||
INTERRUPT_HANDLER(0x73)
|
||||
INTERRUPT_HANDLER(0x74)
|
||||
INTERRUPT_HANDLER(0x75)
|
||||
INTERRUPT_HANDLER(0x76)
|
||||
INTERRUPT_HANDLER(0x77)
|
||||
INTERRUPT_HANDLER(0x78)
|
||||
INTERRUPT_HANDLER(0x79)
|
||||
INTERRUPT_HANDLER(0x7a)
|
||||
INTERRUPT_HANDLER(0x7b)
|
||||
INTERRUPT_HANDLER(0x7c)
|
||||
INTERRUPT_HANDLER(0x7d)
|
||||
INTERRUPT_HANDLER(0x7e)
|
||||
INTERRUPT_HANDLER(0x7f)
|
||||
INTERRUPT_HANDLER(0x80)
|
||||
INTERRUPT_HANDLER(0x81)
|
||||
INTERRUPT_HANDLER(0x82)
|
||||
INTERRUPT_HANDLER(0x83)
|
||||
INTERRUPT_HANDLER(0x84)
|
||||
INTERRUPT_HANDLER(0x85)
|
||||
INTERRUPT_HANDLER(0x86)
|
||||
INTERRUPT_HANDLER(0x87)
|
||||
INTERRUPT_HANDLER(0x88)
|
||||
INTERRUPT_HANDLER(0x89)
|
||||
INTERRUPT_HANDLER(0x8a)
|
||||
INTERRUPT_HANDLER(0x8b)
|
||||
INTERRUPT_HANDLER(0x8c)
|
||||
INTERRUPT_HANDLER(0x8d)
|
||||
INTERRUPT_HANDLER(0x8e)
|
||||
INTERRUPT_HANDLER(0x8f)
|
||||
INTERRUPT_HANDLER(0x90)
|
||||
INTERRUPT_HANDLER(0x91)
|
||||
INTERRUPT_HANDLER(0x92)
|
||||
INTERRUPT_HANDLER(0x93)
|
||||
INTERRUPT_HANDLER(0x94)
|
||||
INTERRUPT_HANDLER(0x95)
|
||||
INTERRUPT_HANDLER(0x96)
|
||||
INTERRUPT_HANDLER(0x97)
|
||||
INTERRUPT_HANDLER(0x98)
|
||||
INTERRUPT_HANDLER(0x99)
|
||||
INTERRUPT_HANDLER(0x9a)
|
||||
INTERRUPT_HANDLER(0x9b)
|
||||
INTERRUPT_HANDLER(0x9c)
|
||||
INTERRUPT_HANDLER(0x9d)
|
||||
INTERRUPT_HANDLER(0x9e)
|
||||
INTERRUPT_HANDLER(0x9f)
|
||||
INTERRUPT_HANDLER(0xa0)
|
||||
INTERRUPT_HANDLER(0xa1)
|
||||
INTERRUPT_HANDLER(0xa2)
|
||||
INTERRUPT_HANDLER(0xa3)
|
||||
INTERRUPT_HANDLER(0xa4)
|
||||
INTERRUPT_HANDLER(0xa5)
|
||||
INTERRUPT_HANDLER(0xa6)
|
||||
INTERRUPT_HANDLER(0xa7)
|
||||
INTERRUPT_HANDLER(0xa8)
|
||||
INTERRUPT_HANDLER(0xa9)
|
||||
INTERRUPT_HANDLER(0xaa)
|
||||
INTERRUPT_HANDLER(0xab)
|
||||
INTERRUPT_HANDLER(0xac)
|
||||
INTERRUPT_HANDLER(0xad)
|
||||
INTERRUPT_HANDLER(0xae)
|
||||
INTERRUPT_HANDLER(0xaf)
|
||||
INTERRUPT_HANDLER(0xb0)
|
||||
INTERRUPT_HANDLER(0xb1)
|
||||
INTERRUPT_HANDLER(0xb2)
|
||||
INTERRUPT_HANDLER(0xb3)
|
||||
INTERRUPT_HANDLER(0xb4)
|
||||
INTERRUPT_HANDLER(0xb5)
|
||||
INTERRUPT_HANDLER(0xb6)
|
||||
INTERRUPT_HANDLER(0xb7)
|
||||
INTERRUPT_HANDLER(0xb8)
|
||||
INTERRUPT_HANDLER(0xb9)
|
||||
INTERRUPT_HANDLER(0xba)
|
||||
INTERRUPT_HANDLER(0xbb)
|
||||
INTERRUPT_HANDLER(0xbc)
|
||||
INTERRUPT_HANDLER(0xbd)
|
||||
INTERRUPT_HANDLER(0xbe)
|
||||
INTERRUPT_HANDLER(0xbf)
|
||||
INTERRUPT_HANDLER(0xc0)
|
||||
INTERRUPT_HANDLER(0xc1)
|
||||
INTERRUPT_HANDLER(0xc2)
|
||||
INTERRUPT_HANDLER(0xc3)
|
||||
INTERRUPT_HANDLER(0xc4)
|
||||
INTERRUPT_HANDLER(0xc5)
|
||||
INTERRUPT_HANDLER(0xc6)
|
||||
INTERRUPT_HANDLER(0xc7)
|
||||
INTERRUPT_HANDLER(0xc8)
|
||||
INTERRUPT_HANDLER(0xc9)
|
||||
INTERRUPT_HANDLER(0xca)
|
||||
INTERRUPT_HANDLER(0xcb)
|
||||
INTERRUPT_HANDLER(0xcc)
|
||||
INTERRUPT_HANDLER(0xcd)
|
||||
INTERRUPT_HANDLER(0xce)
|
||||
INTERRUPT_HANDLER(0xcf)
|
||||
INTERRUPT_HANDLER(0xd0)
|
||||
INTERRUPT_HANDLER(0xd1)
|
||||
INTERRUPT_HANDLER(0xd2)
|
||||
INTERRUPT_HANDLER(0xd3)
|
||||
INTERRUPT_HANDLER(0xd4)
|
||||
INTERRUPT_HANDLER(0xd5)
|
||||
INTERRUPT_HANDLER(0xd6)
|
||||
INTERRUPT_HANDLER(0xd7)
|
||||
INTERRUPT_HANDLER(0xd8)
|
||||
INTERRUPT_HANDLER(0xd9)
|
||||
INTERRUPT_HANDLER(0xda)
|
||||
INTERRUPT_HANDLER(0xdb)
|
||||
INTERRUPT_HANDLER(0xdc)
|
||||
INTERRUPT_HANDLER(0xdd)
|
||||
INTERRUPT_HANDLER(0xde)
|
||||
INTERRUPT_HANDLER(0xdf)
|
||||
INTERRUPT_HANDLER(0xe0)
|
||||
INTERRUPT_HANDLER(0xe1)
|
||||
INTERRUPT_HANDLER(0xe2)
|
||||
INTERRUPT_HANDLER(0xe3)
|
||||
INTERRUPT_HANDLER(0xe4)
|
||||
INTERRUPT_HANDLER(0xe5)
|
||||
INTERRUPT_HANDLER(0xe6)
|
||||
INTERRUPT_HANDLER(0xe7)
|
||||
INTERRUPT_HANDLER(0xe8)
|
||||
INTERRUPT_HANDLER(0xe9)
|
||||
INTERRUPT_HANDLER(0xea)
|
||||
INTERRUPT_HANDLER(0xeb)
|
||||
INTERRUPT_HANDLER(0xec)
|
||||
INTERRUPT_HANDLER(0xed)
|
||||
INTERRUPT_HANDLER(0xee)
|
||||
INTERRUPT_HANDLER(0xef)
|
||||
INTERRUPT_HANDLER(0xf0)
|
||||
INTERRUPT_HANDLER(0xf1)
|
||||
INTERRUPT_HANDLER(0xf2)
|
||||
INTERRUPT_HANDLER(0xf3)
|
||||
INTERRUPT_HANDLER(0xf4)
|
||||
INTERRUPT_HANDLER(0xf5)
|
||||
INTERRUPT_HANDLER(0xf6)
|
||||
INTERRUPT_HANDLER(0xf7)
|
||||
INTERRUPT_HANDLER(0xf8)
|
||||
INTERRUPT_HANDLER(0xf9)
|
||||
INTERRUPT_HANDLER(0xfa)
|
||||
INTERRUPT_HANDLER(0xfb)
|
||||
INTERRUPT_HANDLER(0xfc)
|
||||
INTERRUPT_HANDLER(0xfd)
|
||||
INTERRUPT_HANDLER(0xfe)
|
||||
INTERRUPT_HANDLER(0xff)
|
||||
|
||||
#pragma endregion Exceptions
|
||||
|
||||
void Init(int Core)
|
||||
{
|
||||
if (Core == 0) /* Remap PIC using BSP */
|
||||
{
|
||||
// PIC
|
||||
outb(0x20, 0x10 | 0x1);
|
||||
outb(0x80, 0);
|
||||
outb(0xA0, 0x10 | 0x10);
|
||||
outb(0x80, 0);
|
||||
|
||||
outb(0x21, 0x20);
|
||||
outb(0x80, 0);
|
||||
outb(0xA1, 0x28);
|
||||
outb(0x80, 0);
|
||||
|
||||
outb(0x21, 0x04);
|
||||
outb(0x80, 0);
|
||||
outb(0xA1, 0x02);
|
||||
outb(0x80, 0);
|
||||
|
||||
outb(0x21, 1);
|
||||
outb(0x80, 0);
|
||||
outb(0xA1, 1);
|
||||
outb(0x80, 0);
|
||||
|
||||
// Masking and disabling PIC
|
||||
outb(0x21, 0xff);
|
||||
outb(0x80, 0);
|
||||
outb(0xA1, 0xff);
|
||||
}
|
||||
|
||||
/* ISR */
|
||||
|
||||
bool EnableISRs = true;
|
||||
// #ifdef DEBUG
|
||||
EnableISRs = !DebuggerIsAttached;
|
||||
if (!EnableISRs)
|
||||
KPrint("\x1b[34mThe debugger is attached, disabling all ISRs.");
|
||||
// #endif
|
||||
|
||||
SetEntry(0x0, InterruptHandler_0x0, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x1, InterruptHandler_0x1, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x2, InterruptHandler_0x2, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x3, InterruptHandler_0x3, TRAP_GATE_32BIT, RING3, (!DebuggerIsAttached), GDT_KERNEL_CODE); /* Do not handle breakpoints if we are debugging the kernel. */
|
||||
SetEntry(0x4, InterruptHandler_0x4, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x5, InterruptHandler_0x5, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x6, InterruptHandler_0x6, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x7, InterruptHandler_0x7, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x8, InterruptHandler_0x8, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x9, InterruptHandler_0x9, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa, InterruptHandler_0xa, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb, InterruptHandler_0xb, TRAP_GATE_32BIT, RING0, (!DebuggerIsAttached), GDT_KERNEL_CODE);
|
||||
SetEntry(0xc, InterruptHandler_0xc, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd, InterruptHandler_0xd, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe, InterruptHandler_0xe, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf, InterruptHandler_0xf, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x10, InterruptHandler_0x10, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x11, InterruptHandler_0x11, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x12, InterruptHandler_0x12, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x13, InterruptHandler_0x13, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x14, InterruptHandler_0x14, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x15, InterruptHandler_0x15, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x16, InterruptHandler_0x16, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x17, InterruptHandler_0x17, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x18, InterruptHandler_0x18, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x19, InterruptHandler_0x19, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x1a, InterruptHandler_0x1a, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x1b, InterruptHandler_0x1b, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x1c, InterruptHandler_0x1c, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x1d, InterruptHandler_0x1d, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x1e, InterruptHandler_0x1e, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
SetEntry(0x1f, InterruptHandler_0x1f, TRAP_GATE_32BIT, RING0, EnableISRs, GDT_KERNEL_CODE);
|
||||
|
||||
/* IRQ */
|
||||
|
||||
SetEntry(0x20, InterruptHandler_0x20, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x21, InterruptHandler_0x21, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x22, InterruptHandler_0x22, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x23, InterruptHandler_0x23, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x24, InterruptHandler_0x24, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x25, InterruptHandler_0x25, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x26, InterruptHandler_0x26, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x27, InterruptHandler_0x27, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x28, InterruptHandler_0x28, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x29, InterruptHandler_0x29, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x2a, InterruptHandler_0x2a, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x2b, InterruptHandler_0x2b, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x2c, InterruptHandler_0x2c, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x2d, InterruptHandler_0x2d, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x2e, InterruptHandler_0x2e, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x2f, InterruptHandler_0x2f, INTERRUPT_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
|
||||
/* Reserved by OS */
|
||||
|
||||
SetEntry(0x30, InterruptHandler_0x30, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x31, InterruptHandler_0x31, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x32, InterruptHandler_0x32, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x33, InterruptHandler_0x33, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x34, InterruptHandler_0x34, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x35, InterruptHandler_0x35, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x36, InterruptHandler_0x36, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x37, InterruptHandler_0x37, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x38, InterruptHandler_0x38, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x39, InterruptHandler_0x39, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x3a, InterruptHandler_0x3a, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x3b, InterruptHandler_0x3b, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x3c, InterruptHandler_0x3c, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x3d, InterruptHandler_0x3d, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
|
||||
/* Free */
|
||||
|
||||
SetEntry(0x3e, InterruptHandler_0x3e, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x3f, InterruptHandler_0x3f, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x40, InterruptHandler_0x40, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x41, InterruptHandler_0x41, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x42, InterruptHandler_0x42, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x43, InterruptHandler_0x43, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x44, InterruptHandler_0x44, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x45, InterruptHandler_0x45, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x46, InterruptHandler_0x46, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x47, InterruptHandler_0x47, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x48, InterruptHandler_0x48, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x49, InterruptHandler_0x49, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x4a, InterruptHandler_0x4a, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x4b, InterruptHandler_0x4b, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x4c, InterruptHandler_0x4c, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x4d, InterruptHandler_0x4d, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x4e, InterruptHandler_0x4e, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x4f, InterruptHandler_0x4f, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x50, InterruptHandler_0x50, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x51, InterruptHandler_0x51, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x52, InterruptHandler_0x52, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x53, InterruptHandler_0x53, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x54, InterruptHandler_0x54, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x55, InterruptHandler_0x55, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x56, InterruptHandler_0x56, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x57, InterruptHandler_0x57, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x58, InterruptHandler_0x58, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x59, InterruptHandler_0x59, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x5a, InterruptHandler_0x5a, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x5b, InterruptHandler_0x5b, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x5c, InterruptHandler_0x5c, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x5d, InterruptHandler_0x5d, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x5e, InterruptHandler_0x5e, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x5f, InterruptHandler_0x5f, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x60, InterruptHandler_0x60, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x61, InterruptHandler_0x61, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x62, InterruptHandler_0x62, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x63, InterruptHandler_0x63, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x64, InterruptHandler_0x64, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x65, InterruptHandler_0x65, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x66, InterruptHandler_0x66, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x67, InterruptHandler_0x67, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x68, InterruptHandler_0x68, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x69, InterruptHandler_0x69, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x6a, InterruptHandler_0x6a, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x6b, InterruptHandler_0x6b, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x6c, InterruptHandler_0x6c, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x6d, InterruptHandler_0x6d, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x6e, InterruptHandler_0x6e, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x6f, InterruptHandler_0x6f, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x70, InterruptHandler_0x70, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x71, InterruptHandler_0x71, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x72, InterruptHandler_0x72, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x73, InterruptHandler_0x73, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x74, InterruptHandler_0x74, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x75, InterruptHandler_0x75, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x76, InterruptHandler_0x76, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x77, InterruptHandler_0x77, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x78, InterruptHandler_0x78, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x79, InterruptHandler_0x79, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x7a, InterruptHandler_0x7a, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x7b, InterruptHandler_0x7b, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x7c, InterruptHandler_0x7c, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x7d, InterruptHandler_0x7d, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x7e, InterruptHandler_0x7e, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x7f, InterruptHandler_0x7f, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x80, InterruptHandler_0x80, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x81, InterruptHandler_0x81, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x82, InterruptHandler_0x82, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x83, InterruptHandler_0x83, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x84, InterruptHandler_0x84, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x85, InterruptHandler_0x85, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x86, InterruptHandler_0x86, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x87, InterruptHandler_0x87, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x88, InterruptHandler_0x88, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x89, InterruptHandler_0x89, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x8a, InterruptHandler_0x8a, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x8b, InterruptHandler_0x8b, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x8c, InterruptHandler_0x8c, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x8d, InterruptHandler_0x8d, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x8e, InterruptHandler_0x8e, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x8f, InterruptHandler_0x8f, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x90, InterruptHandler_0x90, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x91, InterruptHandler_0x91, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x92, InterruptHandler_0x92, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x93, InterruptHandler_0x93, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x94, InterruptHandler_0x94, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x95, InterruptHandler_0x95, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x96, InterruptHandler_0x96, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x97, InterruptHandler_0x97, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x98, InterruptHandler_0x98, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x99, InterruptHandler_0x99, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x9a, InterruptHandler_0x9a, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x9b, InterruptHandler_0x9b, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x9c, InterruptHandler_0x9c, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x9d, InterruptHandler_0x9d, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x9e, InterruptHandler_0x9e, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0x9f, InterruptHandler_0x9f, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa0, InterruptHandler_0xa0, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa1, InterruptHandler_0xa1, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa2, InterruptHandler_0xa2, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa3, InterruptHandler_0xa3, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa4, InterruptHandler_0xa4, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa5, InterruptHandler_0xa5, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa6, InterruptHandler_0xa6, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa7, InterruptHandler_0xa7, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa8, InterruptHandler_0xa8, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xa9, InterruptHandler_0xa9, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xaa, InterruptHandler_0xaa, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xab, InterruptHandler_0xab, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xac, InterruptHandler_0xac, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xad, InterruptHandler_0xad, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xae, InterruptHandler_0xae, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xaf, InterruptHandler_0xaf, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb0, InterruptHandler_0xb0, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb1, InterruptHandler_0xb1, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb2, InterruptHandler_0xb2, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb3, InterruptHandler_0xb3, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb4, InterruptHandler_0xb4, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb5, InterruptHandler_0xb5, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb6, InterruptHandler_0xb6, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb7, InterruptHandler_0xb7, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb8, InterruptHandler_0xb8, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xb9, InterruptHandler_0xb9, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xba, InterruptHandler_0xba, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xbb, InterruptHandler_0xbb, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xbc, InterruptHandler_0xbc, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xbd, InterruptHandler_0xbd, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xbe, InterruptHandler_0xbe, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xbf, InterruptHandler_0xbf, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc0, InterruptHandler_0xc0, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc1, InterruptHandler_0xc1, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc2, InterruptHandler_0xc2, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc3, InterruptHandler_0xc3, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc4, InterruptHandler_0xc4, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc5, InterruptHandler_0xc5, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc6, InterruptHandler_0xc6, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc7, InterruptHandler_0xc7, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc8, InterruptHandler_0xc8, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xc9, InterruptHandler_0xc9, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xca, InterruptHandler_0xca, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xcb, InterruptHandler_0xcb, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xcc, InterruptHandler_0xcc, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xcd, InterruptHandler_0xcd, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xce, InterruptHandler_0xce, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xcf, InterruptHandler_0xcf, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd0, InterruptHandler_0xd0, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd1, InterruptHandler_0xd1, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd2, InterruptHandler_0xd2, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd3, InterruptHandler_0xd3, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd4, InterruptHandler_0xd4, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd5, InterruptHandler_0xd5, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd6, InterruptHandler_0xd6, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd7, InterruptHandler_0xd7, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd8, InterruptHandler_0xd8, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xd9, InterruptHandler_0xd9, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xda, InterruptHandler_0xda, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xdb, InterruptHandler_0xdb, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xdc, InterruptHandler_0xdc, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xdd, InterruptHandler_0xdd, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xde, InterruptHandler_0xde, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xdf, InterruptHandler_0xdf, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe0, InterruptHandler_0xe0, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe1, InterruptHandler_0xe1, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe2, InterruptHandler_0xe2, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe3, InterruptHandler_0xe3, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe4, InterruptHandler_0xe4, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe5, InterruptHandler_0xe5, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe6, InterruptHandler_0xe6, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe7, InterruptHandler_0xe7, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe8, InterruptHandler_0xe8, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xe9, InterruptHandler_0xe9, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xea, InterruptHandler_0xea, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xeb, InterruptHandler_0xeb, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xec, InterruptHandler_0xec, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xed, InterruptHandler_0xed, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xee, InterruptHandler_0xee, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xef, InterruptHandler_0xef, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf0, InterruptHandler_0xf0, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf1, InterruptHandler_0xf1, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf2, InterruptHandler_0xf2, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf3, InterruptHandler_0xf3, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf4, InterruptHandler_0xf4, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf5, InterruptHandler_0xf5, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf6, InterruptHandler_0xf6, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf7, InterruptHandler_0xf7, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf8, InterruptHandler_0xf8, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xf9, InterruptHandler_0xf9, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xfa, InterruptHandler_0xfa, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xfb, InterruptHandler_0xfb, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xfc, InterruptHandler_0xfc, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xfd, InterruptHandler_0xfd, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xfe, InterruptHandler_0xfe, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
SetEntry(0xff, InterruptHandler_0xff, TRAP_GATE_32BIT, RING0, true, GDT_KERNEL_CODE);
|
||||
CPU::x32::lidt(&IDTr);
|
||||
}
|
||||
}
|
144
Kernel/arch/i386/cpu/idt.hpp
Normal file
144
Kernel/arch/i386/cpu/idt.hpp
Normal file
@ -0,0 +1,144 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __FENNIX_KERNEL_IDT_H__
|
||||
#define __FENNIX_KERNEL_IDT_H__
|
||||
|
||||
#include <types.h>
|
||||
|
||||
namespace InterruptDescriptorTable
|
||||
{
|
||||
/**
|
||||
* Manual: AMD Architecture Programmer's Manual Volume 2: System Programming
|
||||
* Subsection: 4.7.4 System Descriptors
|
||||
* Table: 4-5
|
||||
*
|
||||
* @note Reserved values are not listed in the table.
|
||||
*/
|
||||
enum GateType
|
||||
{
|
||||
AVAILABLE_16BIT_TSS = 0b0001,
|
||||
LDT = 0b0010,
|
||||
BUSY_16BIT_TSS = 0b0011,
|
||||
CALL_GATE_16BIT = 0b0100,
|
||||
TASK_GATE = 0b0101,
|
||||
INTERRUPT_GATE_16BIT = 0b0110,
|
||||
TRAP_GATE_16BIT = 0b0111,
|
||||
AVAILABLE_32BIT_TSS = 0b1001,
|
||||
BUSY_32BIT_TSS = 0b1011,
|
||||
CALL_GATE_32BIT = 0b1100,
|
||||
INTERRUPT_GATE_32BIT = 0b1110,
|
||||
TRAP_GATE_32BIT = 0b1111,
|
||||
};
|
||||
|
||||
enum PrivilegeLevelType
|
||||
{
|
||||
RING0 = 0b0,
|
||||
RING1 = 0b1,
|
||||
RING2 = 0b10,
|
||||
RING3 = 0b11,
|
||||
};
|
||||
|
||||
struct LDTDescriptor
|
||||
{
|
||||
/* +0 */
|
||||
uint32_t SegmentLimitLow : 16;
|
||||
uint32_t BaseAddressLow : 16;
|
||||
/* +4 */
|
||||
uint32_t BaseAddressMiddle : 8;
|
||||
uint32_t Type : 4;
|
||||
uint32_t Zero : 1;
|
||||
uint32_t DescriptorPrivilegeLevel : 2;
|
||||
uint32_t Present : 1;
|
||||
uint32_t SegmentLimitHigh : 4;
|
||||
uint32_t Available : 1;
|
||||
uint32_t Zero1 : 2;
|
||||
uint32_t Granularity : 1;
|
||||
uint32_t BaseAddressHigh : 8;
|
||||
} __packed;
|
||||
|
||||
typedef LDTDescriptor TSSDescriptor;
|
||||
|
||||
struct CallGate
|
||||
{
|
||||
/* +0 */
|
||||
uint32_t TargetCodeSegmentOffsetLow : 16;
|
||||
uint32_t TargetCodeSegmentSelector : 16;
|
||||
/* +4 */
|
||||
uint32_t ParameterCount : 4;
|
||||
uint32_t Reserved0 : 3;
|
||||
uint32_t Type : 4;
|
||||
uint32_t Zero : 1;
|
||||
uint32_t DescriptorPrivilegeLevel : 2;
|
||||
uint32_t Present : 1;
|
||||
uint32_t TargetCodeSegmentOffsetHigh : 16;
|
||||
} __packed;
|
||||
|
||||
struct InterruptGate
|
||||
{
|
||||
/* +0 */
|
||||
uint32_t TargetCodeSegmentOffsetLow : 16;
|
||||
uint32_t TargetCodeSegmentSelector : 16;
|
||||
/* +4 */
|
||||
uint32_t Reserved0 : 8;
|
||||
uint32_t Type : 4;
|
||||
uint32_t Zero : 1;
|
||||
uint32_t DescriptorPrivilegeLevel : 2;
|
||||
uint32_t Present : 1;
|
||||
uint32_t TargetCodeSegmentOffsetHigh : 16;
|
||||
} __packed;
|
||||
|
||||
typedef InterruptGate TrapGate;
|
||||
|
||||
struct TaskGate
|
||||
{
|
||||
/* +0 */
|
||||
uint32_t Reserved0 : 16;
|
||||
uint32_t TSSSelector : 16;
|
||||
/* +4 */
|
||||
uint32_t Reserved1 : 8;
|
||||
uint32_t Type : 4;
|
||||
uint32_t Zero : 1;
|
||||
uint32_t DescriptorPrivilegeLevel : 2;
|
||||
uint32_t Present : 1;
|
||||
uint32_t Reserved2 : 16;
|
||||
} __packed;
|
||||
|
||||
union IDTGateDescriptor
|
||||
{
|
||||
InterruptGate Interrupt;
|
||||
TrapGate Trap;
|
||||
CallGate Call;
|
||||
};
|
||||
|
||||
struct IDTRegister
|
||||
{
|
||||
uint16_t Limit;
|
||||
IDTGateDescriptor *BaseAddress;
|
||||
} __packed;
|
||||
|
||||
void SetEntry(uint8_t Index,
|
||||
void (*Base)(),
|
||||
GateType Gate,
|
||||
PrivilegeLevelType Ring,
|
||||
bool Present,
|
||||
uint16_t SegmentSelector);
|
||||
|
||||
void Init(int Core);
|
||||
}
|
||||
|
||||
#endif // !__FENNIX_KERNEL_IDT_H__
|
93
Kernel/arch/i386/cpu/smp.cpp
Normal file
93
Kernel/arch/i386/cpu/smp.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <smp.hpp>
|
||||
|
||||
#include <memory.hpp>
|
||||
#include <acpi.hpp>
|
||||
#include <ints.hpp>
|
||||
#include <assert.h>
|
||||
#include <cpu.hpp>
|
||||
#include <atomic>
|
||||
|
||||
#include "../../../kernel.h"
|
||||
#include "apic.hpp"
|
||||
|
||||
enum SMPTrampolineAddress
|
||||
{
|
||||
PAGE_TABLE = 0x500,
|
||||
START_ADDR = 0x520,
|
||||
STACK = 0x570,
|
||||
GDT = 0x580,
|
||||
IDT = 0x590,
|
||||
CORE = 0x600,
|
||||
TRAMPOLINE_START = 0x2000
|
||||
};
|
||||
|
||||
std::atomic_bool CPUEnabled = false;
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
static __aligned(PAGE_SIZE) CPUData CPUs[MAX_CPU] = {0};
|
||||
|
||||
nsa CPUData *GetCPU(long id) { return &CPUs[id]; }
|
||||
|
||||
nsa CPUData *GetCurrentCPU()
|
||||
{
|
||||
if (unlikely(!Interrupts::apic[0]))
|
||||
return &CPUs[0]; /* No APIC means we are on the BSP. */
|
||||
|
||||
APIC::APIC *apic = (APIC::APIC *)Interrupts::apic[0];
|
||||
int CoreID = 0;
|
||||
if (CPUEnabled.load(std::memory_order_acquire) == true)
|
||||
{
|
||||
if (apic->x2APIC)
|
||||
CoreID = int(CPU::x32::rdmsr(CPU::x32::MSR_X2APIC_APICID));
|
||||
else
|
||||
CoreID = apic->Read(APIC::APIC_ID) >> 24;
|
||||
}
|
||||
|
||||
if (unlikely((&CPUs[CoreID])->IsActive != true))
|
||||
{
|
||||
error("CPU %d is not active!", CoreID);
|
||||
assert((&CPUs[0])->IsActive == true); /* We can't continue without the BSP. */
|
||||
return &CPUs[0];
|
||||
}
|
||||
|
||||
assert((&CPUs[CoreID])->Checksum == CPU_DATA_CHECKSUM); /* This should never happen. */
|
||||
return &CPUs[CoreID];
|
||||
}
|
||||
|
||||
namespace SMP
|
||||
{
|
||||
int CPUCores = 0;
|
||||
|
||||
void Initialize(void *_madt)
|
||||
{
|
||||
ACPI::MADT *madt = (ACPI::MADT *)_madt;
|
||||
|
||||
int Cores = madt->CPUCores + 1;
|
||||
|
||||
if (Config.Cores > madt->CPUCores + 1)
|
||||
KPrint("More cores requested than available. Using %d cores", madt->CPUCores + 1);
|
||||
else if (Config.Cores != 0)
|
||||
Cores = Config.Cores;
|
||||
|
||||
CPUCores = Cores;
|
||||
|
||||
fixme("SMP::Initialize() is not implemented!");
|
||||
}
|
||||
}
|
0
Kernel/arch/i386/include/.gitkeep
Normal file
0
Kernel/arch/i386/include/.gitkeep
Normal file
140
Kernel/arch/i386/interrupts/8259PIC.cpp
Normal file
140
Kernel/arch/i386/interrupts/8259PIC.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pic.hpp"
|
||||
|
||||
#include <io.h>
|
||||
|
||||
namespace PIC
|
||||
{
|
||||
PIC::PIC(uint8_t MasterCommandPort, uint8_t MasterDataPort, uint8_t SlaveCommandPort, uint8_t SlaveDataPort, uint8_t MasterOffset, uint8_t SlaveOffset)
|
||||
{
|
||||
this->MasterCommandPort = MasterCommandPort;
|
||||
this->MasterDataPort = MasterDataPort;
|
||||
this->SlaveCommandPort = SlaveCommandPort;
|
||||
this->SlaveDataPort = SlaveDataPort;
|
||||
this->MasterOffset = MasterOffset;
|
||||
this->SlaveOffset = SlaveOffset;
|
||||
|
||||
MasterMask = 0xFF;
|
||||
SlaveMask = 0xFF;
|
||||
|
||||
// ICW1
|
||||
outb(MasterCommandPort, 0x11);
|
||||
outb(SlaveCommandPort, 0x11);
|
||||
|
||||
// ICW2
|
||||
outb(MasterDataPort, MasterOffset);
|
||||
outb(SlaveDataPort, SlaveOffset);
|
||||
|
||||
// ICW3
|
||||
outb(MasterDataPort, 0x04);
|
||||
outb(SlaveDataPort, 0x02);
|
||||
|
||||
// ICW4
|
||||
outb(MasterDataPort, 0x01);
|
||||
outb(SlaveDataPort, 0x01);
|
||||
|
||||
// OCW1
|
||||
outb(MasterDataPort, MasterMask);
|
||||
outb(SlaveDataPort, SlaveMask);
|
||||
}
|
||||
|
||||
PIC::~PIC()
|
||||
{
|
||||
outb(MasterDataPort, 0xFF);
|
||||
outb(SlaveDataPort, 0xFF);
|
||||
}
|
||||
|
||||
void PIC::Mask(uint8_t IRQ)
|
||||
{
|
||||
uint16_t Port;
|
||||
uint8_t Value;
|
||||
|
||||
if (IRQ < 8)
|
||||
{
|
||||
Port = MasterDataPort;
|
||||
Value = (uint8_t)(MasterMask & ~(1 << IRQ));
|
||||
MasterMask = Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
Port = SlaveDataPort;
|
||||
Value = (uint8_t)(SlaveMask & ~(1 << (IRQ - 8)));
|
||||
SlaveMask = Value;
|
||||
}
|
||||
|
||||
outb(Port, Value);
|
||||
}
|
||||
|
||||
void PIC::Unmask(uint8_t IRQ)
|
||||
{
|
||||
uint16_t Port;
|
||||
uint8_t Value;
|
||||
|
||||
if (IRQ < 8)
|
||||
{
|
||||
Port = MasterDataPort;
|
||||
Value = MasterMask | (1 << IRQ);
|
||||
MasterMask = Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
Port = SlaveDataPort;
|
||||
Value = SlaveMask | (1 << (IRQ - 8));
|
||||
SlaveMask = Value;
|
||||
}
|
||||
|
||||
outb(Port, Value);
|
||||
}
|
||||
|
||||
void PIC::SendEOI(uint8_t IRQ)
|
||||
{
|
||||
if (IRQ >= 8)
|
||||
outb(SlaveCommandPort, 0x20);
|
||||
|
||||
outb(MasterCommandPort, 0x20);
|
||||
}
|
||||
|
||||
PIT::PIT(uint16_t Port, uint16_t Frequency)
|
||||
{
|
||||
this->Port = Port;
|
||||
this->Frequency = Frequency;
|
||||
}
|
||||
|
||||
PIT::~PIT()
|
||||
{
|
||||
}
|
||||
|
||||
void PIT::PrepareSleep(uint32_t Milliseconds)
|
||||
{
|
||||
uint16_t Divisor = (uint16_t)(1193182 / Frequency);
|
||||
uint8_t Low = (uint8_t)(Divisor & 0xFF);
|
||||
uint8_t High = (uint8_t)((Divisor >> 8) & 0xFF);
|
||||
|
||||
outb(Port + 3, 0x36);
|
||||
outb(Port + 0, Low);
|
||||
outb(Port + 1, High);
|
||||
}
|
||||
|
||||
void PIT::PerformSleep()
|
||||
{
|
||||
uint8_t Value = inb(Port + 0);
|
||||
while (Value != 0)
|
||||
Value = inb(Port + 0);
|
||||
}
|
||||
}
|
59
Kernel/arch/i386/interrupts/pic.hpp
Normal file
59
Kernel/arch/i386/interrupts/pic.hpp
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __FENNIX_KERNEL_8259PIC_H__
|
||||
#define __FENNIX_KERNEL_8259PIC_H__
|
||||
|
||||
#include <types.h>
|
||||
|
||||
namespace PIC
|
||||
{
|
||||
class PIC
|
||||
{
|
||||
private:
|
||||
uint8_t MasterCommandPort;
|
||||
uint8_t MasterDataPort;
|
||||
uint8_t SlaveCommandPort;
|
||||
uint8_t SlaveDataPort;
|
||||
uint8_t MasterOffset;
|
||||
uint8_t SlaveOffset;
|
||||
uint8_t MasterMask;
|
||||
uint8_t SlaveMask;
|
||||
|
||||
public:
|
||||
PIC(uint8_t MasterCommandPort, uint8_t MasterDataPort, uint8_t SlaveCommandPort, uint8_t SlaveDataPort, uint8_t MasterOffset, uint8_t SlaveOffset);
|
||||
~PIC();
|
||||
void Mask(uint8_t IRQ);
|
||||
void Unmask(uint8_t IRQ);
|
||||
void SendEOI(uint8_t IRQ);
|
||||
};
|
||||
|
||||
class PIT
|
||||
{
|
||||
private:
|
||||
uint16_t Port;
|
||||
uint16_t Frequency;
|
||||
|
||||
public:
|
||||
PIT(uint16_t Port, uint16_t Frequency);
|
||||
~PIT();
|
||||
void PrepareSleep(uint32_t Milliseconds);
|
||||
void PerformSleep();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // !__FENNIX_KERNEL_8259PIC_H__
|
129
Kernel/arch/i386/linker.ld
Normal file
129
Kernel/arch/i386/linker.ld
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
OUTPUT_FORMAT(elf32-i386)
|
||||
OUTPUT_ARCH(i386)
|
||||
|
||||
ENTRY(_start)
|
||||
|
||||
PF_R = 0x4;
|
||||
PF_W = 0x2;
|
||||
PF_X = 0x1;
|
||||
|
||||
PHDRS
|
||||
{
|
||||
bootstrap PT_LOAD FLAGS( PF_R | PF_W /*| PF_X*/ );
|
||||
text PT_LOAD FLAGS( PF_R | PF_X );
|
||||
data PT_LOAD FLAGS( PF_R | PF_W );
|
||||
rodata PT_LOAD FLAGS( PF_R );
|
||||
bss PT_LOAD FLAGS( PF_R | PF_W );
|
||||
}
|
||||
|
||||
KERNEL_VMA = 0xC0000000;
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x100000;
|
||||
_bootstrap_start = .;
|
||||
.bootstrap ALIGN(CONSTANT(MAXPAGESIZE)) :
|
||||
{
|
||||
*(.multiboot)
|
||||
*(.multiboot2)
|
||||
*(.bootstrap .bootstrap.*)
|
||||
} :bootstrap
|
||||
_bootstrap_end = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
. += KERNEL_VMA;
|
||||
|
||||
_kernel_start = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
_kernel_text_start = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
.text ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.text) - KERNEL_VMA)
|
||||
{
|
||||
*(.text .text.*)
|
||||
} :text
|
||||
_kernel_text_end = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
_kernel_data_start = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
.data ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.data) - KERNEL_VMA)
|
||||
{
|
||||
*(.data .data.*)
|
||||
} :data
|
||||
|
||||
.eh_frame : AT(ADDR(.eh_frame) - KERNEL_VMA) ONLY_IF_RW
|
||||
{
|
||||
KEEP (*(.eh_frame .eh_frame.*))
|
||||
} :data
|
||||
|
||||
.gcc_except_table : AT(ADDR(.gcc_except_table) - KERNEL_VMA) ONLY_IF_RW
|
||||
{
|
||||
KEEP (*(.gcc_except_table .gcc_except_table.*))
|
||||
} :data
|
||||
_kernel_data_end = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
_kernel_rodata_start = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
.rodata ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.rodata) - KERNEL_VMA)
|
||||
{
|
||||
*(.rodata .rodata.*)
|
||||
} :rodata
|
||||
|
||||
.init_array ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.init_array) - KERNEL_VMA)
|
||||
{
|
||||
PROVIDE_HIDDEN(__init_array_start = .);
|
||||
KEEP(*(.init_array .ctors))
|
||||
KEEP(*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
} :rodata
|
||||
|
||||
.fini_array ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.fini_array) - KERNEL_VMA)
|
||||
{
|
||||
PROVIDE_HIDDEN(__fini_array_start = .);
|
||||
KEEP(*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
|
||||
KEEP(*(.fini_array .dtors))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
} :rodata
|
||||
|
||||
.eh_frame_hdr : AT(ADDR(.eh_frame_hdr) - KERNEL_VMA)
|
||||
{
|
||||
*(.eh_frame_hdr .eh_frame_hdr.*)
|
||||
} :rodata
|
||||
|
||||
.eh_frame : AT(ADDR(.eh_frame) - KERNEL_VMA) ONLY_IF_RO
|
||||
{
|
||||
KEEP (*(.eh_frame .eh_frame.*))
|
||||
} :rodata
|
||||
|
||||
.gcc_except_table : AT(ADDR(.gcc_except_table) - KERNEL_VMA) ONLY_IF_RO
|
||||
{
|
||||
KEEP (*(.gcc_except_table .gcc_except_table.*))
|
||||
} :rodata
|
||||
_kernel_rodata_end = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
_kernel_bss_start = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
.bss ALIGN(CONSTANT(MAXPAGESIZE)) : AT(ADDR(.bss) - KERNEL_VMA)
|
||||
{
|
||||
*(COMMON)
|
||||
*(.bss .bss.*)
|
||||
} :bss
|
||||
_kernel_bss_end = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
_kernel_end = ALIGN(CONSTANT(MAXPAGESIZE));
|
||||
|
||||
/DISCARD/ :
|
||||
{
|
||||
*(.comment*)
|
||||
*(.note*)
|
||||
}
|
||||
}
|
91
Kernel/arch/i386/madt.cpp
Normal file
91
Kernel/arch/i386/madt.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "acpi.hpp"
|
||||
|
||||
#include <memory.hpp>
|
||||
#include <debug.h>
|
||||
|
||||
#include "../../kernel.h"
|
||||
|
||||
namespace ACPI
|
||||
{
|
||||
MADT::MADT(ACPI::MADTHeader *madt)
|
||||
{
|
||||
trace("Initializing MADT");
|
||||
CPUCores = 0;
|
||||
LAPICAddress = (LAPIC *)(uintptr_t)madt->LocalControllerAddress;
|
||||
for (uint8_t *ptr = (uint8_t *)(madt->Entries);
|
||||
(uintptr_t)(ptr) < (uintptr_t)(madt) + madt->Header.Length;
|
||||
ptr += *(ptr + 1))
|
||||
{
|
||||
switch (*(ptr))
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (ptr[4] & 1)
|
||||
{
|
||||
lapic.push_back((LocalAPIC *)ptr);
|
||||
KPrint("Local APIC %d (APIC %d) found.", lapic.back()->ACPIProcessorId, lapic.back()->APICId);
|
||||
CPUCores++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
ioapic.push_back((MADTIOApic *)ptr);
|
||||
KPrint("I/O APIC %d (Address %#lx) found.", ioapic.back()->APICID, ioapic.back()->Address);
|
||||
Memory::Virtual(KernelPageTable).Map((void *)(uintptr_t)ioapic.back()->Address, (void *)(uintptr_t)ioapic.back()->Address, Memory::PTFlag::RW | Memory::PTFlag::PCD); // Make sure that the address is mapped.
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
iso.push_back((MADTIso *)ptr);
|
||||
KPrint("ISO (IRQ:%#lx, BUS:%#lx, GSI:%#lx, %s/%s) found.",
|
||||
iso.back()->IRQSource, iso.back()->BuSSource, iso.back()->GSI,
|
||||
iso.back()->Flags & 0x00000004 ? "Active High" : "Active Low",
|
||||
iso.back()->Flags & 0x00000100 ? "Edge Triggered" : "Level Triggered");
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
nmi.push_back((MADTNmi *)ptr);
|
||||
KPrint("NMI %#lx (lint:%#lx) found.", nmi.back()->processor, nmi.back()->lint);
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
LAPICAddress = (LAPIC *)ptr;
|
||||
KPrint("APIC found at %#lx", LAPICAddress);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
KPrint("Unknown MADT entry %#lx", *(ptr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
Memory::Virtual(KernelPageTable).Map((void *)LAPICAddress, (void *)LAPICAddress, Memory::PTFlag::RW | Memory::PTFlag::PCD); // I should map more than one page?
|
||||
}
|
||||
CPUCores--; // We start at 0 (BSP) and end at 11 (APs), so we have 12 cores.
|
||||
KPrint("Total CPU cores: %d", CPUCores + 1);
|
||||
}
|
||||
|
||||
MADT::~MADT()
|
||||
{
|
||||
}
|
||||
}
|
226
Kernel/arch/i386/memory/vmm.cpp
Normal file
226
Kernel/arch/i386/memory/vmm.cpp
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <memory.hpp>
|
||||
|
||||
#include <convert.h>
|
||||
#include <debug.h>
|
||||
|
||||
namespace Memory
|
||||
{
|
||||
bool Virtual::Check(void *VirtualAddress, PTFlag Flag, MapType Type)
|
||||
{
|
||||
// 0x1000 aligned
|
||||
uintptr_t Address = (uintptr_t)VirtualAddress;
|
||||
Address &= 0xFFFFF000;
|
||||
|
||||
PageMapIndexer Index = PageMapIndexer(Address);
|
||||
PageDirectoryEntry *PDE = &this->Table->Entries[Index.PDEIndex];
|
||||
PageTableEntryPtr *PTE = nullptr;
|
||||
|
||||
if ((PDE->raw & Flag) > 0)
|
||||
{
|
||||
if (Type == MapType::FourMiB && PDE->PageSize)
|
||||
return true;
|
||||
|
||||
PTE = (PageTableEntryPtr *)((uintptr_t)PDE->GetAddress() << 12);
|
||||
if (PTE)
|
||||
{
|
||||
if ((PTE->Entries[Index.PTEIndex].Present))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void *Virtual::GetPhysical(void *VirtualAddress)
|
||||
{
|
||||
// 0x1000 aligned
|
||||
uintptr_t Address = (uintptr_t)VirtualAddress;
|
||||
Address &= 0xFFFFF000;
|
||||
|
||||
PageMapIndexer Index = PageMapIndexer(Address);
|
||||
PageDirectoryEntry *PDE = &this->Table->Entries[Index.PDEIndex];
|
||||
PageTableEntryPtr *PTE = nullptr;
|
||||
|
||||
if (PDE->Present)
|
||||
{
|
||||
if (PDE->PageSize)
|
||||
return (void *)((uintptr_t)PDE->GetAddress() << 12);
|
||||
|
||||
PTE = (PageTableEntryPtr *)((uintptr_t)PDE->GetAddress() << 12);
|
||||
if (PTE)
|
||||
{
|
||||
if (PTE->Entries[Index.PTEIndex].Present)
|
||||
return (void *)((uintptr_t)PTE->Entries[Index.PTEIndex].GetAddress() << 12);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Virtual::MapType Virtual::GetMapType(void *VirtualAddress)
|
||||
{
|
||||
// 0x1000 aligned
|
||||
uintptr_t Address = (uintptr_t)VirtualAddress;
|
||||
Address &= 0xFFFFF000;
|
||||
|
||||
PageMapIndexer Index = PageMapIndexer(Address);
|
||||
|
||||
PageDirectoryEntry *PDE = &this->Table->Entries[Index.PDEIndex];
|
||||
PageTableEntryPtr *PTE = nullptr;
|
||||
|
||||
if (PDE->Present)
|
||||
{
|
||||
if (PDE->PageSize)
|
||||
return MapType::FourMiB;
|
||||
|
||||
PTE = (PageTableEntryPtr *)((uintptr_t)PDE->GetAddress() << 12);
|
||||
if (PTE)
|
||||
{
|
||||
if (PTE->Entries[Index.PTEIndex].Present)
|
||||
return MapType::FourKiB;
|
||||
}
|
||||
}
|
||||
return MapType::NoMapType;
|
||||
}
|
||||
|
||||
PageDirectoryEntry *Virtual::GetPDE(void *VirtualAddress, MapType Type)
|
||||
{
|
||||
uintptr_t Address = (uintptr_t)VirtualAddress;
|
||||
Address &= 0xFFFFF000;
|
||||
|
||||
PageMapIndexer Index = PageMapIndexer(Address);
|
||||
PageDirectoryEntry *PDE = &this->Table->Entries[Index.PDEIndex];
|
||||
if (PDE->Present)
|
||||
return PDE;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PageTableEntry *Virtual::GetPTE(void *VirtualAddress, MapType Type)
|
||||
{
|
||||
uintptr_t Address = (uintptr_t)VirtualAddress;
|
||||
Address &= 0xFFFFF000;
|
||||
|
||||
PageMapIndexer Index = PageMapIndexer(Address);
|
||||
PageDirectoryEntry *PDE = &this->Table->Entries[Index.PDEIndex];
|
||||
if (!PDE->Present)
|
||||
return nullptr;
|
||||
|
||||
PageTableEntryPtr *PTEPtr = (PageTableEntryPtr *)(PDE->GetAddress() << 12);
|
||||
PageTableEntry *PTE = &PTEPtr->Entries[Index.PTEIndex];
|
||||
if (PTE->Present)
|
||||
return PTE;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Virtual::Map(void *VirtualAddress, void *PhysicalAddress, uint64_t Flags, MapType Type)
|
||||
{
|
||||
SmartLock(this->MemoryLock);
|
||||
if (unlikely(!this->Table))
|
||||
{
|
||||
error("No page table");
|
||||
return;
|
||||
}
|
||||
|
||||
Flags |= PTFlag::P;
|
||||
|
||||
PageMapIndexer Index = PageMapIndexer((uintptr_t)VirtualAddress);
|
||||
// Clear any flags that are not 1 << 0 (Present) - 1 << 5 (Accessed) because rest are for page table entries only
|
||||
uint64_t DirectoryFlags = Flags & 0x3F;
|
||||
|
||||
PageDirectoryEntry *PDE = &this->Table->Entries[Index.PDEIndex];
|
||||
if (Type == MapType::FourMiB)
|
||||
{
|
||||
PDE->raw |= (uintptr_t)Flags;
|
||||
PDE->PageSize = true;
|
||||
PDE->SetAddress((uintptr_t)PhysicalAddress >> 12);
|
||||
debug("Mapped 4MB page at %p to %p", VirtualAddress, PhysicalAddress);
|
||||
return;
|
||||
}
|
||||
|
||||
PageTableEntryPtr *PTEPtr = nullptr;
|
||||
if (!PDE->Present)
|
||||
{
|
||||
PTEPtr = (PageTableEntryPtr *)KernelAllocator.RequestPages(TO_PAGES(sizeof(PageTableEntryPtr) + 1));
|
||||
memset(PTEPtr, 0, sizeof(PageTableEntryPtr));
|
||||
PDE->Present = true;
|
||||
PDE->SetAddress((uintptr_t)PTEPtr >> 12);
|
||||
}
|
||||
else
|
||||
PTEPtr = (PageTableEntryPtr *)(PDE->GetAddress() << 12);
|
||||
PDE->raw |= (uintptr_t)DirectoryFlags;
|
||||
|
||||
PageTableEntry *PTE = &PTEPtr->Entries[Index.PTEIndex];
|
||||
PTE->Present = true;
|
||||
PTE->raw |= (uintptr_t)Flags;
|
||||
PTE->SetAddress((uintptr_t)PhysicalAddress >> 12);
|
||||
CPU::x32::invlpg(VirtualAddress);
|
||||
|
||||
#ifdef DEBUG
|
||||
/* https://stackoverflow.com/a/3208376/9352057 */
|
||||
#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
|
||||
#define BYTE_TO_BINARY(byte) \
|
||||
(byte & 0x80 ? '1' : '0'), \
|
||||
(byte & 0x40 ? '1' : '0'), \
|
||||
(byte & 0x20 ? '1' : '0'), \
|
||||
(byte & 0x10 ? '1' : '0'), \
|
||||
(byte & 0x08 ? '1' : '0'), \
|
||||
(byte & 0x04 ? '1' : '0'), \
|
||||
(byte & 0x02 ? '1' : '0'), \
|
||||
(byte & 0x01 ? '1' : '0')
|
||||
|
||||
if (!this->Check(VirtualAddress, (PTFlag)Flags, Type)) // quick workaround just to see where it fails
|
||||
warn("Failed to map v:%#lx p:%#lx with flags: " BYTE_TO_BINARY_PATTERN, VirtualAddress, PhysicalAddress, BYTE_TO_BINARY(Flags));
|
||||
#endif
|
||||
}
|
||||
|
||||
void Virtual::Unmap(void *VirtualAddress, MapType Type)
|
||||
{
|
||||
SmartLock(this->MemoryLock);
|
||||
if (!this->Table)
|
||||
{
|
||||
error("No page table");
|
||||
return;
|
||||
}
|
||||
|
||||
PageMapIndexer Index = PageMapIndexer((uintptr_t)VirtualAddress);
|
||||
PageDirectoryEntry *PDE = &this->Table->Entries[Index.PDEIndex];
|
||||
if (!PDE->Present)
|
||||
{
|
||||
warn("Page %#lx not present", PDE->GetAddress());
|
||||
return;
|
||||
}
|
||||
|
||||
if (Type == MapType::FourMiB && PDE->PageSize)
|
||||
{
|
||||
PDE->Present = false;
|
||||
return;
|
||||
}
|
||||
|
||||
PageTableEntryPtr *PTEPtr = (PageTableEntryPtr *)((uintptr_t)PDE->Address << 12);
|
||||
PageTableEntry PTE = PTEPtr->Entries[Index.PTEIndex];
|
||||
if (!PTE.Present)
|
||||
{
|
||||
warn("Page %#lx not present", PTE.GetAddress());
|
||||
return;
|
||||
}
|
||||
|
||||
PTE.Present = false;
|
||||
PTEPtr->Entries[Index.PTEIndex] = PTE;
|
||||
CPU::x32::invlpg(VirtualAddress);
|
||||
}
|
||||
}
|
30
Kernel/arch/i386/syscalls.cpp
Normal file
30
Kernel/arch/i386/syscalls.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <syscalls.hpp>
|
||||
|
||||
#include <cpu.hpp>
|
||||
|
||||
#include "cpu/gdt.hpp"
|
||||
|
||||
using namespace CPU::x32;
|
||||
|
||||
extern "C" uint32_t SystemCallsHandler(SyscallsFrame *regs);
|
||||
|
||||
void InitializeSystemCalls()
|
||||
{
|
||||
}
|
207
Kernel/core/acpi.cpp
Normal file
207
Kernel/core/acpi.cpp
Normal file
@ -0,0 +1,207 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <acpi.hpp>
|
||||
|
||||
#include <debug.h>
|
||||
#include <io.h>
|
||||
|
||||
#include "../kernel.h"
|
||||
|
||||
namespace ACPI
|
||||
{
|
||||
__no_sanitize("alignment") void *ACPI::FindTable(ACPI::ACPIHeader *ACPIHeader, char *Signature)
|
||||
{
|
||||
for (uint64_t t = 0; t < ((ACPIHeader->Length - sizeof(ACPI::ACPIHeader)) / (XSDTSupported ? 8 : 4)); t++)
|
||||
{
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
|
||||
|
||||
// TODO: Should I be concerned about unaligned memory access?
|
||||
ACPI::ACPIHeader *SDTHdr = nullptr;
|
||||
if (XSDTSupported)
|
||||
SDTHdr = (ACPI::ACPIHeader *)(*(uint64_t *)((uint64_t)ACPIHeader + sizeof(ACPI::ACPIHeader) + (t * 8)));
|
||||
else
|
||||
SDTHdr = (ACPI::ACPIHeader *)(*(uint32_t *)((uint64_t)ACPIHeader + sizeof(ACPI::ACPIHeader) + (t * 4)));
|
||||
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
size_t signLength = strlen(Signature);
|
||||
for (size_t i = 0; i < signLength; i++)
|
||||
{
|
||||
if (SDTHdr->Signature[i] != Signature[i])
|
||||
break;
|
||||
if (i == 3)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
KPrint("ACPI: %.4s [%.6s:%.8s] found at address %#lx",
|
||||
Signature,
|
||||
SDTHdr->OEMID,
|
||||
SDTHdr->OEMTableID,
|
||||
(uintptr_t)SDTHdr);
|
||||
#endif
|
||||
trace("%s found at address %#lx", Signature, (uintptr_t)SDTHdr);
|
||||
|
||||
Tables[Signature] = SDTHdr;
|
||||
return SDTHdr;
|
||||
}
|
||||
}
|
||||
}
|
||||
// warn("%s not found!", Signature);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ACPI::SearchTables(ACPIHeader *Header)
|
||||
{
|
||||
if (!Header)
|
||||
return;
|
||||
|
||||
HPET = (HPETHeader *)FindTable(Header, (char *)"HPET");
|
||||
FADT = (FADTHeader *)FindTable(Header, (char *)"FACP");
|
||||
MCFG = (MCFGHeader *)FindTable(Header, (char *)"MCFG");
|
||||
BGRT = (BGRTHeader *)FindTable(Header, (char *)"BGRT");
|
||||
SRAT = (SRATHeader *)FindTable(Header, (char *)"SRAT");
|
||||
TPM2 = (TPM2Header *)FindTable(Header, (char *)"TPM2");
|
||||
TCPA = (TCPAHeader *)FindTable(Header, (char *)"TCPA");
|
||||
WAET = (WAETHeader *)FindTable(Header, (char *)"WAET");
|
||||
MADT = (MADTHeader *)FindTable(Header, (char *)"APIC");
|
||||
HEST = (HESTHeader *)FindTable(Header, (char *)"HEST");
|
||||
SSDT = (SSDTHeader *)FindTable(Header, (char *)"SSDT");
|
||||
DBGP = (DBGPHeader *)FindTable(Header, (char *)"DBGP");
|
||||
DBG2 = (DBG2Header *)FindTable(Header, (char *)"DBG2");
|
||||
FindTable(Header, (char *)"BERT");
|
||||
FindTable(Header, (char *)"CPEP");
|
||||
FindTable(Header, (char *)"DSDT");
|
||||
FindTable(Header, (char *)"ECDT");
|
||||
FindTable(Header, (char *)"EINJ");
|
||||
FindTable(Header, (char *)"ERST");
|
||||
FindTable(Header, (char *)"FACS");
|
||||
FindTable(Header, (char *)"MSCT");
|
||||
FindTable(Header, (char *)"MPST");
|
||||
FindTable(Header, (char *)"OEMx");
|
||||
FindTable(Header, (char *)"PMTT");
|
||||
FindTable(Header, (char *)"PSDT");
|
||||
FindTable(Header, (char *)"RASF");
|
||||
FindTable(Header, (char *)"RSDT");
|
||||
FindTable(Header, (char *)"SBST");
|
||||
FindTable(Header, (char *)"SLIT");
|
||||
FindTable(Header, (char *)"XSDT");
|
||||
FindTable(Header, (char *)"DRTM");
|
||||
FindTable(Header, (char *)"FPDT");
|
||||
FindTable(Header, (char *)"GTDT");
|
||||
FindTable(Header, (char *)"PCCT");
|
||||
FindTable(Header, (char *)"S3PT");
|
||||
FindTable(Header, (char *)"MATR");
|
||||
FindTable(Header, (char *)"MSDM");
|
||||
FindTable(Header, (char *)"WPBT");
|
||||
FindTable(Header, (char *)"OSDT");
|
||||
FindTable(Header, (char *)"RSDP");
|
||||
FindTable(Header, (char *)"NFIT");
|
||||
FindTable(Header, (char *)"ASF!");
|
||||
FindTable(Header, (char *)"BOOT");
|
||||
FindTable(Header, (char *)"CSRT");
|
||||
FindTable(Header, (char *)"BDAT");
|
||||
FindTable(Header, (char *)"CDAT");
|
||||
FindTable(Header, (char *)"DMAR");
|
||||
FindTable(Header, (char *)"IBFT");
|
||||
FindTable(Header, (char *)"IORT");
|
||||
FindTable(Header, (char *)"IVRS");
|
||||
FindTable(Header, (char *)"LPIT");
|
||||
FindTable(Header, (char *)"MCHI");
|
||||
FindTable(Header, (char *)"MTMR");
|
||||
FindTable(Header, (char *)"SLIC");
|
||||
FindTable(Header, (char *)"SPCR");
|
||||
FindTable(Header, (char *)"SPMI");
|
||||
FindTable(Header, (char *)"UEFI");
|
||||
FindTable(Header, (char *)"VRTC");
|
||||
FindTable(Header, (char *)"WDAT");
|
||||
FindTable(Header, (char *)"WDDT");
|
||||
FindTable(Header, (char *)"WDRT");
|
||||
FindTable(Header, (char *)"ATKG");
|
||||
FindTable(Header, (char *)"GSCI");
|
||||
FindTable(Header, (char *)"IEIT");
|
||||
FindTable(Header, (char *)"HMAT");
|
||||
FindTable(Header, (char *)"CEDT");
|
||||
FindTable(Header, (char *)"AEST");
|
||||
FindTable(Header, (char *)"AGDI");
|
||||
FindTable(Header, (char *)"APMT");
|
||||
FindTable(Header, (char *)"ETDT");
|
||||
FindTable(Header, (char *)"MPAM");
|
||||
FindTable(Header, (char *)"PDTT");
|
||||
FindTable(Header, (char *)"PPTT");
|
||||
FindTable(Header, (char *)"RAS2");
|
||||
FindTable(Header, (char *)"SDEI");
|
||||
FindTable(Header, (char *)"STAO");
|
||||
FindTable(Header, (char *)"XENV");
|
||||
FindTable(Header, (char *)"PHAT");
|
||||
FindTable(Header, (char *)"SVKL");
|
||||
FindTable(Header, (char *)"UUID");
|
||||
FindTable(Header, (char *)"CCEL");
|
||||
FindTable(Header, (char *)"WSMT");
|
||||
FindTable(Header, (char *)"PRMT");
|
||||
FindTable(Header, (char *)"NBFT");
|
||||
FindTable(Header, (char *)"RSD PTR");
|
||||
FindTable(Header, (char *)"TDX");
|
||||
FindTable(Header, (char *)"CXL");
|
||||
FindTable(Header, (char *)"DSD");
|
||||
FindTable(Header, (char *)"BSA");
|
||||
}
|
||||
|
||||
ACPI::ACPI()
|
||||
{
|
||||
trace("Initializing ACPI");
|
||||
if (!bInfo.RSDP)
|
||||
{
|
||||
error("RSDP not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bInfo.RSDP->Revision >= 2 && bInfo.RSDP->XSDTAddress)
|
||||
{
|
||||
debug("XSDT supported");
|
||||
XSDTSupported = true;
|
||||
XSDT = (ACPIHeader *)(bInfo.RSDP->XSDTAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("RSDT supported");
|
||||
XSDT = (ACPIHeader *)(uintptr_t)bInfo.RSDP->RSDTAddress;
|
||||
}
|
||||
|
||||
if (!Memory::Virtual().Check(XSDT))
|
||||
{
|
||||
warn("%s is not mapped!",
|
||||
XSDTSupported ? "XSDT" : "RSDT");
|
||||
debug("XSDT: %p", XSDT);
|
||||
Memory::Virtual().Map(XSDT, XSDT, Memory::RW);
|
||||
}
|
||||
|
||||
this->SearchTables(XSDT);
|
||||
|
||||
if (FADT)
|
||||
{
|
||||
outb(s_cst(uint16_t, FADT->SMI_CommandPort), FADT->AcpiEnable);
|
||||
/* TODO: Sleep for ~5 seconds before polling PM1a CB? */
|
||||
while (!(inw(s_cst(uint16_t, FADT->PM1aControlBlock)) & 1))
|
||||
CPU::Pause();
|
||||
}
|
||||
}
|
||||
|
||||
ACPI::~ACPI()
|
||||
{
|
||||
}
|
||||
}
|
348
Kernel/core/console.cpp
Normal file
348
Kernel/core/console.cpp
Normal file
@ -0,0 +1,348 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <kcon.hpp>
|
||||
|
||||
#include <memory.hpp>
|
||||
#include <stropts.h>
|
||||
#include <string.h>
|
||||
#include <ini.h>
|
||||
|
||||
#include "../kernel.h"
|
||||
|
||||
namespace KernelConsole
|
||||
{
|
||||
int TermColors[] = {
|
||||
[TerminalColor::BLACK] = 0x000000,
|
||||
[TerminalColor::RED] = 0xAA0000,
|
||||
[TerminalColor::GREEN] = 0x00AA00,
|
||||
[TerminalColor::YELLOW] = 0xAA5500,
|
||||
[TerminalColor::BLUE] = 0x0000AA,
|
||||
[TerminalColor::MAGENTA] = 0xAA00AA,
|
||||
[TerminalColor::CYAN] = 0x00AAAA,
|
||||
[TerminalColor::GREY] = 0xAAAAAA,
|
||||
};
|
||||
|
||||
int TermBrightColors[] = {
|
||||
[TerminalColor::BLACK] = 0x858585,
|
||||
[TerminalColor::RED] = 0xFF5555,
|
||||
[TerminalColor::GREEN] = 0x55FF55,
|
||||
[TerminalColor::YELLOW] = 0xFFFF55,
|
||||
[TerminalColor::BLUE] = 0x5555FF,
|
||||
[TerminalColor::MAGENTA] = 0xFF55FF,
|
||||
[TerminalColor::CYAN] = 0x55FFFF,
|
||||
[TerminalColor::GREY] = 0xFFFFFF,
|
||||
};
|
||||
|
||||
__no_sanitize("undefined") char FontRenderer::Paint(long CellX, long CellY, char Char, uint32_t Foreground, uint32_t Background)
|
||||
{
|
||||
uint64_t x = CellX * CurrentFont->GetInfo().Width;
|
||||
uint64_t y = CellY * CurrentFont->GetInfo().Height;
|
||||
|
||||
switch (CurrentFont->GetInfo().Type)
|
||||
{
|
||||
case Video::FontType::PCScreenFont1:
|
||||
{
|
||||
uint32_t *PixelPtr = (uint32_t *)Display->GetBuffer;
|
||||
char *FontPtr = (char *)CurrentFont->GetInfo().PSF1Font->GlyphBuffer + (Char * CurrentFont->GetInfo().PSF1Font->Header->charsize);
|
||||
for (uint64_t Y = 0; Y < 16; Y++)
|
||||
{
|
||||
for (uint64_t X = 0; X < 8; X++)
|
||||
{
|
||||
if ((*FontPtr & (0b10000000 >> X)) > 0)
|
||||
*(unsigned int *)(PixelPtr + (x + X) + ((y + Y) * Display->GetWidth)) = Foreground;
|
||||
else
|
||||
*(unsigned int *)(PixelPtr + (x + X) + ((y + Y) * Display->GetWidth)) = Background;
|
||||
}
|
||||
FontPtr++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Video::FontType::PCScreenFont2:
|
||||
{
|
||||
Video::FontInfo fInfo = CurrentFont->GetInfo();
|
||||
|
||||
int BytesPerLine = (fInfo.PSF2Font->Header->width + 7) / 8;
|
||||
char *FontAddress = (char *)fInfo.StartAddress;
|
||||
uint32_t FontHeaderSize = fInfo.PSF2Font->Header->headersize;
|
||||
uint32_t FontCharSize = fInfo.PSF2Font->Header->charsize;
|
||||
uint32_t FontLength = fInfo.PSF2Font->Header->length;
|
||||
char *FontPtr = FontAddress + FontHeaderSize + (Char > 0 && (uint32_t)Char < FontLength ? Char : 0) * FontCharSize;
|
||||
|
||||
uint32_t FontHdrWidth = fInfo.PSF2Font->Header->width;
|
||||
uint32_t FontHdrHeight = fInfo.PSF2Font->Header->height;
|
||||
|
||||
for (uint32_t Y = 0; Y < FontHdrHeight; Y++)
|
||||
{
|
||||
for (uint32_t X = 0; X < FontHdrWidth; X++)
|
||||
{
|
||||
void *FramebufferAddress = (void *)((uintptr_t)Display->GetBuffer +
|
||||
((y + Y) * Display->GetWidth + (x + X)) *
|
||||
(Display->GetFramebufferStruct().BitsPerPixel / 8));
|
||||
|
||||
if ((*FontPtr & (0b10000000 >> (X % 8))) > 0)
|
||||
*(uint32_t *)FramebufferAddress = Foreground;
|
||||
else
|
||||
*(uint32_t *)FramebufferAddress = Background;
|
||||
}
|
||||
FontPtr += BytesPerLine;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
warn("Unsupported font type");
|
||||
break;
|
||||
}
|
||||
return Char;
|
||||
}
|
||||
|
||||
FontRenderer Renderer;
|
||||
|
||||
void paint_callback(TerminalCell *cell, long x, long y)
|
||||
{
|
||||
if (cell->attr.Bright)
|
||||
Renderer.Paint(x, y, cell->c, TermBrightColors[cell->attr.Foreground], TermColors[cell->attr.Background]);
|
||||
else
|
||||
Renderer.Paint(x, y, cell->c, TermColors[cell->attr.Foreground], TermColors[cell->attr.Background]);
|
||||
}
|
||||
|
||||
void cursor_callback(TerminalCursor *cur)
|
||||
{
|
||||
Renderer.Cursor = {cur->X, cur->Y};
|
||||
}
|
||||
|
||||
VirtualTerminal *Terminals[16] = {nullptr};
|
||||
std::atomic<VirtualTerminal *> CurrentTerminal = nullptr;
|
||||
|
||||
bool SetTheme(std::string Theme)
|
||||
{
|
||||
FileNode *rn = fs->GetByPath("/etc/term", thisProcess->Info.RootNode);
|
||||
if (rn == nullptr)
|
||||
return false;
|
||||
|
||||
kstat st{};
|
||||
rn->Stat(&st);
|
||||
|
||||
char *sh = new char[st.Size];
|
||||
rn->Read(sh, st.Size, 0);
|
||||
|
||||
ini_t *ini = ini_load(sh, NULL);
|
||||
int themeSection, c0, c1, c2, c3, c4, c5, c6, c7, colorsIdx;
|
||||
const char *colors[8];
|
||||
|
||||
debug("Loading terminal theme: \"%s\"", Theme.c_str());
|
||||
themeSection = ini_find_section(ini, Theme.c_str(), NULL);
|
||||
if (themeSection == INI_NOT_FOUND)
|
||||
{
|
||||
ini_destroy(ini);
|
||||
delete[] sh;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto getColorComponent = [](const char *str, int &index) -> int
|
||||
{
|
||||
int value = 0;
|
||||
while (str[index] >= '0' && str[index] <= '9')
|
||||
{
|
||||
value = value * 10 + (str[index] - '0');
|
||||
++index;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
auto parseColor = [getColorComponent](const char *colorStr) -> unsigned int
|
||||
{
|
||||
int index = 0;
|
||||
int r = getColorComponent(colorStr, index);
|
||||
if (colorStr[index] == ',')
|
||||
++index;
|
||||
int g = getColorComponent(colorStr, index);
|
||||
if (colorStr[index] == ',')
|
||||
++index;
|
||||
int b = getColorComponent(colorStr, index);
|
||||
return (r << 16) | (g << 8) | b;
|
||||
};
|
||||
|
||||
c0 = ini_find_property(ini, themeSection, "color0", NULL);
|
||||
c1 = ini_find_property(ini, themeSection, "color1", NULL);
|
||||
c2 = ini_find_property(ini, themeSection, "color2", NULL);
|
||||
c3 = ini_find_property(ini, themeSection, "color3", NULL);
|
||||
c4 = ini_find_property(ini, themeSection, "color4", NULL);
|
||||
c5 = ini_find_property(ini, themeSection, "color5", NULL);
|
||||
c6 = ini_find_property(ini, themeSection, "color6", NULL);
|
||||
c7 = ini_find_property(ini, themeSection, "color7", NULL);
|
||||
|
||||
colors[0] = ini_property_value(ini, themeSection, c0);
|
||||
colors[1] = ini_property_value(ini, themeSection, c1);
|
||||
colors[2] = ini_property_value(ini, themeSection, c2);
|
||||
colors[3] = ini_property_value(ini, themeSection, c3);
|
||||
colors[4] = ini_property_value(ini, themeSection, c4);
|
||||
colors[5] = ini_property_value(ini, themeSection, c5);
|
||||
colors[6] = ini_property_value(ini, themeSection, c6);
|
||||
colors[7] = ini_property_value(ini, themeSection, c7);
|
||||
|
||||
colorsIdx = 0;
|
||||
for (auto color : colors)
|
||||
{
|
||||
colorsIdx++;
|
||||
if (color == 0)
|
||||
continue;
|
||||
|
||||
char *delimiterPos = strchr(color, ':');
|
||||
if (delimiterPos == NULL)
|
||||
continue;
|
||||
|
||||
char colorStr[20], colorBrightStr[20];
|
||||
strncpy(colorStr, color, delimiterPos - color);
|
||||
colorStr[delimiterPos - color] = '\0';
|
||||
strcpy(colorBrightStr, delimiterPos + 1);
|
||||
|
||||
TermColors[colorsIdx - 1] = parseColor(colorStr);
|
||||
TermBrightColors[colorsIdx - 1] = parseColor(colorBrightStr);
|
||||
}
|
||||
|
||||
ini_destroy(ini);
|
||||
delete[] sh;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void __test_themes()
|
||||
{
|
||||
printf("\x1b[H\x1b[2J");
|
||||
auto testTheme = [](const char *theme)
|
||||
{
|
||||
KernelConsole::SetTheme("vga");
|
||||
printf("== Theme: \"%s\" ==\n", theme);
|
||||
KernelConsole::SetTheme(theme);
|
||||
const char *txtColors[] = {
|
||||
"30", "31", "32", "33", "34", "35", "36", "37", "39"};
|
||||
const char *bgColors[] = {
|
||||
"40", "41", "42", "43", "44", "45", "46", "47", "49"};
|
||||
const int numTxtColors = sizeof(txtColors) / sizeof(txtColors[0]);
|
||||
const int numBgColors = sizeof(bgColors) / sizeof(bgColors[0]);
|
||||
|
||||
const char *msg = "H";
|
||||
for (int i = 0; i < numTxtColors; i++)
|
||||
for (int j = 0; j < numBgColors; j++)
|
||||
printf("\x1b[%sm\x1b[%sm%s\x1b[0m", txtColors[i], bgColors[j], msg);
|
||||
|
||||
for (int i = 0; i < numTxtColors; i++)
|
||||
for (int j = 0; j < numBgColors; j++)
|
||||
printf("\x1b[1;%sm\x1b[1;%sm%s\x1b[0m", txtColors[i], bgColors[j], msg);
|
||||
|
||||
KernelConsole::SetTheme("vga");
|
||||
printf("\n");
|
||||
};
|
||||
testTheme("vga");
|
||||
testTheme("breeze");
|
||||
testTheme("coolbreeze");
|
||||
testTheme("softlight");
|
||||
testTheme("calmsea");
|
||||
testTheme("warmember");
|
||||
// CPU::Stop();
|
||||
}
|
||||
#endif
|
||||
|
||||
void EarlyInit()
|
||||
{
|
||||
Renderer.CurrentFont = new Video::Font(&_binary_files_tamsyn_font_1_11_Tamsyn7x14r_psf_start,
|
||||
&_binary_files_tamsyn_font_1_11_Tamsyn7x14r_psf_end,
|
||||
Video::FontType::PCScreenFont2);
|
||||
|
||||
size_t Rows = Display->GetWidth / Renderer.CurrentFont->GetInfo().Width;
|
||||
size_t Cols = Display->GetHeight / Renderer.CurrentFont->GetInfo().Height;
|
||||
debug("Terminal size: %ux%u", Rows, Cols);
|
||||
Terminals[0] = new VirtualTerminal(Rows, Cols, Display->GetWidth, Display->GetHeight, paint_callback, cursor_callback);
|
||||
Terminals[0]->Clear(0, 0, Rows, Cols - 1);
|
||||
CurrentTerminal.store(Terminals[0], std::memory_order_release);
|
||||
}
|
||||
|
||||
void LateInit()
|
||||
{
|
||||
FileNode *rn = fs->GetByPath("/etc/term", thisProcess->Info.RootNode);
|
||||
if (rn == nullptr)
|
||||
return;
|
||||
|
||||
kstat st{};
|
||||
rn->Stat(&st);
|
||||
|
||||
char *sh = new char[st.Size];
|
||||
rn->Read(sh, st.Size, 0);
|
||||
|
||||
ini_t *ini = ini_load(sh, NULL);
|
||||
int general = ini_find_section(ini, "general", NULL);
|
||||
int cursor = ini_find_section(ini, "cursor", NULL);
|
||||
assert(general != INI_NOT_FOUND && cursor != INI_NOT_FOUND);
|
||||
|
||||
int themeIndex = ini_find_property(ini, general, "theme", NULL);
|
||||
assert(themeIndex != INI_NOT_FOUND);
|
||||
|
||||
int cursorColor = ini_find_property(ini, cursor, "color", NULL);
|
||||
int cursorBlink = ini_find_property(ini, cursor, "blink", NULL);
|
||||
assert(cursorColor != INI_NOT_FOUND && cursorBlink != INI_NOT_FOUND);
|
||||
|
||||
const char *colorThemeStr = ini_property_value(ini, general, themeIndex);
|
||||
const char *cursorColorStr = ini_property_value(ini, cursor, cursorColor);
|
||||
const char *cursorBlinkStr = ini_property_value(ini, cursor, cursorBlink);
|
||||
debug("colorThemeStr=%s", colorThemeStr);
|
||||
debug("cursorColorStr=%s", cursorColorStr);
|
||||
debug("cursorBlinkStr=%s", cursorBlinkStr);
|
||||
|
||||
auto getColorComponent = [](const char *str, int &index) -> int
|
||||
{
|
||||
int value = 0;
|
||||
while (str[index] >= '0' && str[index] <= '9')
|
||||
{
|
||||
value = value * 10 + (str[index] - '0');
|
||||
++index;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
auto parseColor = [getColorComponent](const char *colorStr) -> unsigned int
|
||||
{
|
||||
int index = 0;
|
||||
int r = getColorComponent(colorStr, index);
|
||||
if (colorStr[index] == ',')
|
||||
++index;
|
||||
int g = getColorComponent(colorStr, index);
|
||||
if (colorStr[index] == ',')
|
||||
++index;
|
||||
int b = getColorComponent(colorStr, index);
|
||||
return (r << 16) | (g << 8) | b;
|
||||
};
|
||||
|
||||
if (colorThemeStr != 0)
|
||||
SetTheme(colorThemeStr);
|
||||
|
||||
if (cursorBlinkStr != 0 && strncmp(cursorBlinkStr, "true", 4) == 0)
|
||||
{
|
||||
uint32_t blinkColor = 0xFFFFFF;
|
||||
if (cursorColorStr != 0)
|
||||
blinkColor = parseColor(cursorColorStr);
|
||||
fixme("cursor blink with colors %X", blinkColor);
|
||||
}
|
||||
|
||||
ini_destroy(ini);
|
||||
delete[] sh;
|
||||
|
||||
#ifdef DEBUG
|
||||
// __test_themes();
|
||||
#endif
|
||||
}
|
||||
}
|
551
Kernel/core/cpu.cpp
Normal file
551
Kernel/core/cpu.cpp
Normal file
@ -0,0 +1,551 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <cpu.hpp>
|
||||
|
||||
#include <memory.hpp>
|
||||
#include <convert.h>
|
||||
#include <debug.h>
|
||||
#include <smp.hpp>
|
||||
|
||||
#include "../kernel.h"
|
||||
|
||||
#if defined(a64)
|
||||
using namespace CPU::x64;
|
||||
#elif defined(a32)
|
||||
using namespace CPU::x32;
|
||||
#elif defined(aa64)
|
||||
#endif
|
||||
|
||||
namespace CPU
|
||||
{
|
||||
static bool SSEEnabled = false;
|
||||
|
||||
const char *Vendor()
|
||||
{
|
||||
static char Vendor[13] = {0};
|
||||
if (Vendor[0] != 0)
|
||||
return Vendor;
|
||||
#if defined(a64)
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
x64::cpuid(0x0, &eax, &ebx, &ecx, &edx);
|
||||
memcpy(Vendor + 0, &ebx, 4);
|
||||
memcpy(Vendor + 4, &edx, 4);
|
||||
memcpy(Vendor + 8, &ecx, 4);
|
||||
#elif defined(a32)
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
x32::cpuid(0x0, &eax, &ebx, &ecx, &edx);
|
||||
memcpy(Vendor + 0, &ebx, 4);
|
||||
memcpy(Vendor + 4, &edx, 4);
|
||||
memcpy(Vendor + 8, &ecx, 4);
|
||||
#elif defined(aa64)
|
||||
#error "Not implemented"
|
||||
#endif
|
||||
return Vendor;
|
||||
}
|
||||
|
||||
const char *Name()
|
||||
{
|
||||
static char Name[49] = {0};
|
||||
if (Name[0] != 0)
|
||||
return Name;
|
||||
#if defined(a64)
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
x64::cpuid(0x80000002, &eax, &ebx, &ecx, &edx);
|
||||
memcpy(Name + 0, &eax, 4);
|
||||
memcpy(Name + 4, &ebx, 4);
|
||||
memcpy(Name + 8, &ecx, 4);
|
||||
memcpy(Name + 12, &edx, 4);
|
||||
x64::cpuid(0x80000003, &eax, &ebx, &ecx, &edx);
|
||||
memcpy(Name + 16, &eax, 4);
|
||||
memcpy(Name + 20, &ebx, 4);
|
||||
memcpy(Name + 24, &ecx, 4);
|
||||
memcpy(Name + 28, &edx, 4);
|
||||
x64::cpuid(0x80000004, &eax, &ebx, &ecx, &edx);
|
||||
memcpy(Name + 32, &eax, 4);
|
||||
memcpy(Name + 36, &ebx, 4);
|
||||
memcpy(Name + 40, &ecx, 4);
|
||||
memcpy(Name + 44, &edx, 4);
|
||||
#elif defined(a32)
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
x32::cpuid(0x80000002, &eax, &ebx, &ecx, &edx);
|
||||
memcpy(Name + 0, &eax, 4);
|
||||
memcpy(Name + 4, &ebx, 4);
|
||||
memcpy(Name + 8, &ecx, 4);
|
||||
memcpy(Name + 12, &edx, 4);
|
||||
x32::cpuid(0x80000003, &eax, &ebx, &ecx, &edx);
|
||||
memcpy(Name + 16, &eax, 4);
|
||||
memcpy(Name + 20, &ebx, 4);
|
||||
memcpy(Name + 24, &ecx, 4);
|
||||
memcpy(Name + 28, &edx, 4);
|
||||
x32::cpuid(0x80000004, &eax, &ebx, &ecx, &edx);
|
||||
memcpy(Name + 32, &eax, 4);
|
||||
memcpy(Name + 36, &ebx, 4);
|
||||
memcpy(Name + 40, &ecx, 4);
|
||||
memcpy(Name + 44, &edx, 4);
|
||||
#elif defined(aa64)
|
||||
#error "Not implemented"
|
||||
#endif
|
||||
return Name;
|
||||
}
|
||||
|
||||
const char *Hypervisor()
|
||||
{
|
||||
static char Hypervisor[13] = {0};
|
||||
if (Hypervisor[0] != 0)
|
||||
return Hypervisor;
|
||||
#if defined(a64)
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
x64::cpuid(0x1, &eax, &ebx, &ecx, &edx);
|
||||
if (!(ecx & (1 << 31))) /* Intel & AMD are the same */
|
||||
{
|
||||
Hypervisor[0] = 'N';
|
||||
Hypervisor[1] = 'o';
|
||||
Hypervisor[2] = 'n';
|
||||
Hypervisor[3] = 'e';
|
||||
return Hypervisor;
|
||||
}
|
||||
|
||||
x64::cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
|
||||
memcpy(Hypervisor + 0, &ebx, 4);
|
||||
memcpy(Hypervisor + 4, &ecx, 4);
|
||||
memcpy(Hypervisor + 8, &edx, 4);
|
||||
#elif defined(a32)
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
x32::cpuid(0x1, &eax, &ebx, &ecx, &edx);
|
||||
if (!(ecx & (1 << 31))) /* Intel & AMD are the same */
|
||||
{
|
||||
Hypervisor[0] = 'N';
|
||||
Hypervisor[1] = 'o';
|
||||
Hypervisor[2] = 'n';
|
||||
Hypervisor[3] = 'e';
|
||||
return Hypervisor;
|
||||
}
|
||||
|
||||
x32::cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
|
||||
memcpy(Hypervisor + 0, &ebx, 4);
|
||||
memcpy(Hypervisor + 4, &ecx, 4);
|
||||
memcpy(Hypervisor + 8, &edx, 4);
|
||||
#elif defined(aa64)
|
||||
#error "Not implemented"
|
||||
#endif
|
||||
return Hypervisor;
|
||||
}
|
||||
|
||||
bool Interrupts(InterruptsType Type)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case Check:
|
||||
{
|
||||
uintptr_t Flags;
|
||||
#if defined(a64)
|
||||
asmv("pushfq");
|
||||
asmv("popq %0"
|
||||
: "=r"(Flags));
|
||||
return Flags & (1 << 9);
|
||||
#elif defined(a32)
|
||||
asmv("pushfl");
|
||||
asmv("popl %0"
|
||||
: "=r"(Flags));
|
||||
return Flags & (1 << 9);
|
||||
#elif defined(aa64)
|
||||
asmv("mrs %0, cpsr"
|
||||
: "=r"(Flags));
|
||||
return Flags & (1 << 7);
|
||||
#endif
|
||||
}
|
||||
case Enable:
|
||||
{
|
||||
#if defined(a86)
|
||||
asmv("sti");
|
||||
#elif defined(aa64)
|
||||
asmv("cpsie i");
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
case Disable:
|
||||
{
|
||||
#if defined(a86)
|
||||
asmv("cli");
|
||||
#elif defined(aa64)
|
||||
asmv("cpsid i");
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
assert(!"Unknown InterruptsType");
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void *PageTable(void *PT)
|
||||
{
|
||||
void *ret;
|
||||
#if defined(a64)
|
||||
asmv("movq %%cr3, %0"
|
||||
: "=r"(ret));
|
||||
|
||||
if (PT)
|
||||
{
|
||||
asmv("movq %0, %%cr3"
|
||||
:
|
||||
: "r"(PT)
|
||||
: "memory");
|
||||
}
|
||||
#elif defined(a32)
|
||||
asmv("movl %%cr3, %0"
|
||||
: "=r"(ret));
|
||||
|
||||
if (PT)
|
||||
{
|
||||
asmv("movl %0, %%cr3"
|
||||
:
|
||||
: "r"(PT)
|
||||
: "memory");
|
||||
}
|
||||
#elif defined(aa64)
|
||||
asmv("mrs %0, ttbr0_el1"
|
||||
: "=r"(ret));
|
||||
|
||||
if (PT)
|
||||
{
|
||||
asmv("msr ttbr0_el1, %0"
|
||||
:
|
||||
: "r"(PT)
|
||||
: "memory");
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct SupportedFeat
|
||||
{
|
||||
bool PGE = false;
|
||||
bool SSE = false;
|
||||
bool UMIP = false;
|
||||
bool SMEP = false;
|
||||
bool SMAP = false;
|
||||
};
|
||||
|
||||
SupportedFeat GetCPUFeat()
|
||||
{
|
||||
SupportedFeat feat{};
|
||||
|
||||
if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0)
|
||||
{
|
||||
CPU::x86::AMD::CPUID0x00000001 cpuid1;
|
||||
CPU::x86::AMD::CPUID0x00000007_ECX_0 cpuid7;
|
||||
|
||||
feat.PGE = cpuid1.EDX.PGE;
|
||||
feat.SSE = cpuid1.EDX.SSE;
|
||||
feat.SMEP = cpuid7.EBX.SMEP;
|
||||
feat.SMAP = cpuid7.EBX.SMAP;
|
||||
feat.UMIP = cpuid7.ECX.UMIP;
|
||||
}
|
||||
else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0)
|
||||
{
|
||||
CPU::x86::Intel::CPUID0x00000001 cpuid1;
|
||||
CPU::x86::Intel::CPUID0x00000007_0 cpuid7_0;
|
||||
|
||||
feat.PGE = cpuid1.EDX.PGE;
|
||||
feat.SSE = cpuid1.EDX.SSE;
|
||||
feat.SMEP = cpuid7_0.EBX.SMEP;
|
||||
feat.SMAP = cpuid7_0.EBX.SMAP;
|
||||
feat.UMIP = cpuid7_0.ECX.UMIP;
|
||||
}
|
||||
|
||||
return feat;
|
||||
}
|
||||
|
||||
void InitializeFeatures(int Core)
|
||||
{
|
||||
static int BSP = 0;
|
||||
SupportedFeat feat = GetCPUFeat();
|
||||
|
||||
CR0 cr0 = readcr0();
|
||||
CR4 cr4 = readcr4();
|
||||
|
||||
if (Config.SIMD == false)
|
||||
{
|
||||
debug("Disabling SSE support...");
|
||||
feat.SSE = false;
|
||||
}
|
||||
|
||||
if (feat.PGE)
|
||||
{
|
||||
debug("Enabling global pages support...");
|
||||
if (!BSP)
|
||||
KPrint("Global Pages is supported.");
|
||||
cr4.PGE = true;
|
||||
}
|
||||
|
||||
bool SSEEnableAfter = false;
|
||||
|
||||
/* Not sure if my code is not working properly or something else is the issue. */
|
||||
if ((strcmp(Hypervisor(), x86_CPUID_VENDOR_VIRTUALBOX) != 0) &&
|
||||
feat.SSE)
|
||||
{
|
||||
debug("Enabling SSE support...");
|
||||
if (!BSP)
|
||||
KPrint("SSE is supported.");
|
||||
cr0.EM = false;
|
||||
cr0.MP = true;
|
||||
cr4.OSFXSR = true;
|
||||
cr4.OSXMMEXCPT = true;
|
||||
|
||||
CPUData *CoreData = GetCPU(Core);
|
||||
CoreData->Data.FPU.mxcsr = 0b0001111110000000;
|
||||
CoreData->Data.FPU.mxcsrmask = 0b1111111110111111;
|
||||
CoreData->Data.FPU.fcw = 0b0000001100111111;
|
||||
fxrstor(&CoreData->Data.FPU);
|
||||
|
||||
SSEEnableAfter = true;
|
||||
}
|
||||
|
||||
/* More info in AMD64 Architecture Programmer's Manual
|
||||
Volume 2: 3.1.1 CR0 Register */
|
||||
|
||||
/* Not Write-Through
|
||||
This is ignored on recent processors.
|
||||
*/
|
||||
cr0.NW = false;
|
||||
|
||||
/* Cache Disable
|
||||
Wether the CPU should cache memory or not.
|
||||
If it's enabled, PWT and PCD are ignored.
|
||||
*/
|
||||
cr0.CD = false;
|
||||
|
||||
/* Write Protect
|
||||
When set, the supervisor can't write to read-only pages.
|
||||
*/
|
||||
cr0.WP = true;
|
||||
|
||||
/* Alignment check
|
||||
The CPU checks the alignment of memory operands
|
||||
and generates #AC if the alignment is incorrect.
|
||||
|
||||
The condition for an alignment check is:
|
||||
- The AM flag in CR0 is set.
|
||||
- The AC flag in the RFLAGS register is set.
|
||||
- CPL is 3.
|
||||
*/
|
||||
cr0.AM = true;
|
||||
|
||||
debug("Updating CR0...");
|
||||
writecr0(cr0);
|
||||
debug("Updated CR0.");
|
||||
|
||||
debug("CPU Prevention Features:%s%s%s",
|
||||
feat.SMEP ? " SMEP" : "",
|
||||
feat.SMAP ? " SMAP" : "",
|
||||
feat.UMIP ? " UMIP" : "");
|
||||
/* User-Mode Instruction Prevention
|
||||
This prevents user-mode code from executing these instructions:
|
||||
SGDT, SIDT, SLDT, SMSW, STR
|
||||
If any of these instructions are executed with CPL > 0, a #GP is generated.
|
||||
*/
|
||||
// cr4.UMIP = feat.UMIP;
|
||||
|
||||
/* Supervisor Mode Execution Prevention
|
||||
This prevents user-mode code from executing code in the supervisor mode.
|
||||
*/
|
||||
cr4.SMEP = feat.SMEP;
|
||||
|
||||
/* Supervisor Mode Access Prevention
|
||||
This prevents supervisor-mode code from accessing user-mode pages.
|
||||
*/
|
||||
cr4.SMAP = feat.SMAP;
|
||||
|
||||
debug("Updating CR4...");
|
||||
writecr4(cr4);
|
||||
debug("Updated CR4.");
|
||||
|
||||
debug("Enabling PAT support...");
|
||||
wrmsr(MSR_CR_PAT, 0x6 | (0x0 << 8) | (0x1 << 16));
|
||||
if (!BSP++)
|
||||
trace("Features for BSP initialized.");
|
||||
if (SSEEnableAfter)
|
||||
{
|
||||
SSEEnabled = true;
|
||||
debug("SSE support enabled.");
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Counter()
|
||||
{
|
||||
// TODO: Get the counter from the x2APIC or any other timer that is available. (TSC is not available on all CPUs)
|
||||
uint64_t Counter;
|
||||
#if defined(a86)
|
||||
uint32_t eax, edx;
|
||||
asmv("rdtsc"
|
||||
: "=a"(eax),
|
||||
"=d"(edx));
|
||||
Counter = ((uint64_t)eax) | (((uint64_t)edx) << 32);
|
||||
#elif defined(aa64)
|
||||
asmv("mrs %0, cntvct_el0"
|
||||
: "=r"(Counter));
|
||||
#endif
|
||||
return Counter;
|
||||
}
|
||||
|
||||
uint64_t CheckSIMD()
|
||||
{
|
||||
if (unlikely(!SSEEnabled))
|
||||
return SIMD_NONE;
|
||||
|
||||
#warning "TODO: Proper SIMD support"
|
||||
return SIMD_NONE;
|
||||
|
||||
#if defined(a86)
|
||||
static uint64_t SIMDType = SIMD_NONE;
|
||||
|
||||
if (likely(SIMDType != SIMD_NONE))
|
||||
return SIMDType;
|
||||
|
||||
if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0)
|
||||
{
|
||||
CPU::x86::AMD::CPUID0x00000001 cpuid;
|
||||
asmv("cpuid"
|
||||
: "=a"(cpuid.EAX.raw), "=b"(cpuid.EBX.raw),
|
||||
"=c"(cpuid.ECX.raw), "=d"(cpuid.EDX.raw)
|
||||
: "a"(0x1));
|
||||
|
||||
if (cpuid.ECX.SSE42)
|
||||
SIMDType |= SIMD_SSE42;
|
||||
else if (cpuid.ECX.SSE41)
|
||||
SIMDType |= SIMD_SSE41;
|
||||
else if (cpuid.ECX.SSSE3)
|
||||
SIMDType |= SIMD_SSSE3;
|
||||
else if (cpuid.ECX.SSE3)
|
||||
SIMDType |= SIMD_SSE3;
|
||||
else if (cpuid.EDX.SSE2)
|
||||
SIMDType |= SIMD_SSE2;
|
||||
else if (cpuid.EDX.SSE)
|
||||
SIMDType |= SIMD_SSE;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (cpuid.ECX.SSE42)
|
||||
debug("SSE4.2 is supported.");
|
||||
if (cpuid.ECX.SSE41)
|
||||
debug("SSE4.1 is supported.");
|
||||
if (cpuid.ECX.SSSE3)
|
||||
debug("SSSE3 is supported.");
|
||||
if (cpuid.ECX.SSE3)
|
||||
debug("SSE3 is supported.");
|
||||
if (cpuid.EDX.SSE2)
|
||||
debug("SSE2 is supported.");
|
||||
if (cpuid.EDX.SSE)
|
||||
debug("SSE is supported.");
|
||||
#endif
|
||||
|
||||
return SIMDType;
|
||||
}
|
||||
else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0)
|
||||
{
|
||||
CPU::x86::Intel::CPUID0x00000001 cpuid;
|
||||
asmv("cpuid"
|
||||
: "=a"(cpuid.EAX.raw), "=b"(cpuid.EBX.raw), "=c"(cpuid.ECX.raw), "=d"(cpuid.EDX.raw)
|
||||
: "a"(0x1));
|
||||
|
||||
if (cpuid.ECX.SSE4_2)
|
||||
SIMDType |= SIMD_SSE42;
|
||||
else if (cpuid.ECX.SSE4_1)
|
||||
SIMDType |= SIMD_SSE41;
|
||||
else if (cpuid.ECX.SSSE3)
|
||||
SIMDType |= SIMD_SSSE3;
|
||||
else if (cpuid.ECX.SSE3)
|
||||
SIMDType |= SIMD_SSE3;
|
||||
else if (cpuid.EDX.SSE2)
|
||||
SIMDType |= SIMD_SSE2;
|
||||
else if (cpuid.EDX.SSE)
|
||||
SIMDType |= SIMD_SSE;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (cpuid.ECX.SSE4_2)
|
||||
debug("SSE4.2 is supported.");
|
||||
if (cpuid.ECX.SSE4_1)
|
||||
debug("SSE4.1 is supported.");
|
||||
if (cpuid.ECX.SSSE3)
|
||||
debug("SSSE3 is supported.");
|
||||
if (cpuid.ECX.SSE3)
|
||||
debug("SSE3 is supported.");
|
||||
if (cpuid.EDX.SSE2)
|
||||
debug("SSE2 is supported.");
|
||||
if (cpuid.EDX.SSE)
|
||||
debug("SSE is supported.");
|
||||
#endif
|
||||
return SIMDType;
|
||||
}
|
||||
|
||||
debug("No SIMD support.");
|
||||
#endif // a64 || a32
|
||||
return SIMD_NONE;
|
||||
}
|
||||
|
||||
bool CheckSIMD(x86SIMDType Type)
|
||||
{
|
||||
if (unlikely(!SSEEnabled))
|
||||
return false;
|
||||
|
||||
#if defined(a86)
|
||||
if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0)
|
||||
{
|
||||
CPU::x86::AMD::CPUID0x00000001 cpuid;
|
||||
asmv("cpuid"
|
||||
: "=a"(cpuid.EAX.raw), "=b"(cpuid.EBX.raw), "=c"(cpuid.ECX.raw), "=d"(cpuid.EDX.raw)
|
||||
: "a"(0x1));
|
||||
|
||||
if (Type == SIMD_SSE42)
|
||||
return cpuid.ECX.SSE42;
|
||||
else if (Type == SIMD_SSE41)
|
||||
return cpuid.ECX.SSE41;
|
||||
else if (Type == SIMD_SSSE3)
|
||||
return cpuid.ECX.SSSE3;
|
||||
else if (Type == SIMD_SSE3)
|
||||
return cpuid.ECX.SSE3;
|
||||
else if (Type == SIMD_SSE2)
|
||||
return cpuid.EDX.SSE2;
|
||||
else if (Type == SIMD_SSE)
|
||||
return cpuid.EDX.SSE;
|
||||
}
|
||||
else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0)
|
||||
{
|
||||
CPU::x86::Intel::CPUID0x00000001 cpuid;
|
||||
asmv("cpuid"
|
||||
: "=a"(cpuid.EAX.raw), "=b"(cpuid.EBX.raw), "=c"(cpuid.ECX.raw), "=d"(cpuid.EDX.raw)
|
||||
: "a"(0x1));
|
||||
|
||||
if (Type == SIMD_SSE42)
|
||||
return cpuid.ECX.SSE4_2;
|
||||
else if (Type == SIMD_SSE41)
|
||||
return cpuid.ECX.SSE4_1;
|
||||
else if (Type == SIMD_SSSE3)
|
||||
return cpuid.ECX.SSSE3;
|
||||
else if (Type == SIMD_SSE3)
|
||||
return cpuid.ECX.SSE3;
|
||||
else if (Type == SIMD_SSE2)
|
||||
return cpuid.EDX.SSE2;
|
||||
else if (Type == SIMD_SSE)
|
||||
return cpuid.EDX.SSE;
|
||||
}
|
||||
#endif // a64 || a32
|
||||
return false;
|
||||
}
|
||||
}
|
236
Kernel/core/debugger.cpp
Normal file
236
Kernel/core/debugger.cpp
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <debug.h>
|
||||
|
||||
#include <printf.h>
|
||||
#include <lock.hpp>
|
||||
#include <io.h>
|
||||
|
||||
#include "../kernel.h"
|
||||
|
||||
NewLock(DebuggerLock);
|
||||
|
||||
extern bool serialports[8];
|
||||
|
||||
EXTERNC NIF void uart_wrapper(char c, void *unused)
|
||||
{
|
||||
static int once = 0;
|
||||
if (unlikely(!once++))
|
||||
{
|
||||
uint8_t com = inb(0x3F8);
|
||||
if (com != 0xFF)
|
||||
{
|
||||
outb(s_cst(uint16_t, 0x3F8 + 1), 0x00); // Disable all interrupts
|
||||
outb(s_cst(uint16_t, 0x3F8 + 3), 0x80); // Enable DLAB (set baud rate divisor)
|
||||
outb(s_cst(uint16_t, 0x3F8 + 0), 0x1); // Set divisor to 1 (lo byte) 115200 baud
|
||||
outb(s_cst(uint16_t, 0x3F8 + 1), 0x0); // (hi byte)
|
||||
outb(s_cst(uint16_t, 0x3F8 + 3), 0x03); // 8 bits, no parity, one stop bit
|
||||
outb(s_cst(uint16_t, 0x3F8 + 2), 0xC7); // Enable FIFO, clear them, with 14-byte threshold
|
||||
outb(s_cst(uint16_t, 0x3F8 + 4), 0x0B); // IRQs enabled, RTS/DSR set
|
||||
|
||||
/* FIXME https://wiki.osdev.org/Serial_Ports */
|
||||
// outb(s_cst(uint16_t, 0x3F8 + 0), 0x1E);
|
||||
// outb(s_cst(uint16_t, 0x3F8 + 0), 0xAE);
|
||||
// Check if the serial port is faulty.
|
||||
// if (inb(s_cst(uint16_t, 0x3F8 + 0)) != 0xAE)
|
||||
// {
|
||||
// static int once = 0;
|
||||
// if (!once++)
|
||||
// warn("Serial port %#llx is faulty.", 0x3F8);
|
||||
// // serialports[0x3F8] = false; // ignore for now
|
||||
// // return;
|
||||
// }
|
||||
|
||||
// Set to normal operation mode.
|
||||
outb(s_cst(uint16_t, 0x3F8 + 4), 0x0F);
|
||||
serialports[0] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (likely(serialports[0]))
|
||||
{
|
||||
while ((inb(s_cst(uint16_t, 0x3F8 + 5)) & 0x20) == 0)
|
||||
;
|
||||
outb(0x3F8, c);
|
||||
}
|
||||
UNUSED(unused);
|
||||
}
|
||||
|
||||
static inline NIF bool WritePrefix(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, va_list args)
|
||||
{
|
||||
const char *DbgLvlString;
|
||||
switch (Level)
|
||||
{
|
||||
case DebugLevelError:
|
||||
DbgLvlString = "ERROR";
|
||||
break;
|
||||
case DebugLevelWarning:
|
||||
DbgLvlString = "WARN ";
|
||||
break;
|
||||
case DebugLevelInfo:
|
||||
DbgLvlString = "INFO ";
|
||||
break;
|
||||
case DebugLevelDebug:
|
||||
DbgLvlString = "DEBUG";
|
||||
break;
|
||||
case DebugLevelTrace:
|
||||
DbgLvlString = "TRACE";
|
||||
break;
|
||||
case DebugLevelFixme:
|
||||
DbgLvlString = "FIXME";
|
||||
break;
|
||||
case DebugLevelStub:
|
||||
fctprintf(uart_wrapper, nullptr, "STUB | %s>%s() is stub\n", File, Function);
|
||||
return false;
|
||||
case DebugLevelFunction:
|
||||
fctprintf(uart_wrapper, nullptr, "FUNC | %s>%s( ", File, Function);
|
||||
vfctprintf(uart_wrapper, nullptr, Format, args);
|
||||
fctprintf(uart_wrapper, nullptr, " )\n");
|
||||
return false;
|
||||
case DebugLevelUbsan:
|
||||
{
|
||||
DbgLvlString = "UBSAN";
|
||||
fctprintf(uart_wrapper, nullptr, "%s| ", DbgLvlString);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
DbgLvlString = "UNKNW";
|
||||
break;
|
||||
}
|
||||
fctprintf(uart_wrapper, nullptr, "%s| %s>%s:%d: ", DbgLvlString, File, Function, Line);
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace SysDbg
|
||||
{
|
||||
NIF void Write(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, Format);
|
||||
if (!WritePrefix(Level, File, Line, Function, Format, args))
|
||||
{
|
||||
va_end(args);
|
||||
return;
|
||||
}
|
||||
vfctprintf(uart_wrapper, nullptr, Format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
NIF void WriteLine(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, Format);
|
||||
if (!WritePrefix(Level, File, Line, Function, Format, args))
|
||||
{
|
||||
va_end(args);
|
||||
return;
|
||||
}
|
||||
vfctprintf(uart_wrapper, nullptr, Format, args);
|
||||
va_end(args);
|
||||
uart_wrapper('\n', nullptr);
|
||||
}
|
||||
|
||||
NIF void LockedWrite(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...)
|
||||
{
|
||||
SmartTimeoutLock(DebuggerLock, 1000);
|
||||
va_list args;
|
||||
va_start(args, Format);
|
||||
if (!WritePrefix(Level, File, Line, Function, Format, args))
|
||||
{
|
||||
va_end(args);
|
||||
return;
|
||||
}
|
||||
vfctprintf(uart_wrapper, nullptr, Format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
NIF void LockedWriteLine(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...)
|
||||
{
|
||||
SmartTimeoutLock(DebuggerLock, 1000);
|
||||
va_list args;
|
||||
va_start(args, Format);
|
||||
if (!WritePrefix(Level, File, Line, Function, Format, args))
|
||||
{
|
||||
va_end(args);
|
||||
return;
|
||||
}
|
||||
vfctprintf(uart_wrapper, nullptr, Format, args);
|
||||
va_end(args);
|
||||
uart_wrapper('\n', nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// C compatibility
|
||||
extern "C" NIF void SysDbgWrite(enum DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, Format);
|
||||
if (!WritePrefix(Level, File, Line, Function, Format, args))
|
||||
{
|
||||
va_end(args);
|
||||
return;
|
||||
}
|
||||
vfctprintf(uart_wrapper, nullptr, Format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// C compatibility
|
||||
extern "C" NIF void SysDbgWriteLine(enum DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, Format);
|
||||
if (!WritePrefix(Level, File, Line, Function, Format, args))
|
||||
{
|
||||
va_end(args);
|
||||
return;
|
||||
}
|
||||
vfctprintf(uart_wrapper, nullptr, Format, args);
|
||||
va_end(args);
|
||||
uart_wrapper('\n', nullptr);
|
||||
}
|
||||
|
||||
// C compatibility
|
||||
extern "C" NIF void SysDbgLockedWrite(enum DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...)
|
||||
{
|
||||
SmartTimeoutLock(DebuggerLock, 1000);
|
||||
va_list args;
|
||||
va_start(args, Format);
|
||||
if (!WritePrefix(Level, File, Line, Function, Format, args))
|
||||
{
|
||||
va_end(args);
|
||||
return;
|
||||
}
|
||||
vfctprintf(uart_wrapper, nullptr, Format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// C compatibility
|
||||
extern "C" NIF void SysDbgLockedWriteLine(enum DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...)
|
||||
{
|
||||
SmartTimeoutLock(DebuggerLock, 1000);
|
||||
va_list args;
|
||||
va_start(args, Format);
|
||||
if (!WritePrefix(Level, File, Line, Function, Format, args))
|
||||
{
|
||||
va_end(args);
|
||||
return;
|
||||
}
|
||||
vfctprintf(uart_wrapper, nullptr, Format, args);
|
||||
va_end(args);
|
||||
uart_wrapper('\n', nullptr);
|
||||
}
|
193
Kernel/core/disk.cpp
Normal file
193
Kernel/core/disk.cpp
Normal file
@ -0,0 +1,193 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <disk.hpp>
|
||||
|
||||
#include <memory.hpp>
|
||||
#include <printf.h>
|
||||
|
||||
#include "../kernel.h"
|
||||
|
||||
namespace Disk
|
||||
{
|
||||
void Manager::FetchDisks(unsigned long modUniqueID)
|
||||
{
|
||||
/* KernelCallback */
|
||||
// KernelCallback callback{};
|
||||
// callback.Reason = QueryReason;
|
||||
// DriverManager->IOCB(modUniqueID, &callback);
|
||||
// this->AvailablePorts = callback.DiskCallback.Fetch.Ports;
|
||||
// this->BytesPerSector = callback.DiskCallback.Fetch.BytesPerSector;
|
||||
debug("AvailablePorts:%ld BytesPerSector:%ld", this->AvailablePorts, this->BytesPerSector);
|
||||
|
||||
if (this->AvailablePorts <= 0)
|
||||
return;
|
||||
|
||||
uint8_t *RWBuffer = (uint8_t *)KernelAllocator.RequestPages(TO_PAGES(this->BytesPerSector + 1));
|
||||
|
||||
for (unsigned char ItrPort = 0; ItrPort < this->AvailablePorts; ItrPort++)
|
||||
{
|
||||
Drive *drive = new Drive{};
|
||||
sprintf(drive->Name, "sd%ld", modUniqueID);
|
||||
debug("Drive Name: %s", drive->Name);
|
||||
// TODO: Implement disk type detection. Very useful in the future.
|
||||
drive->MechanicalDisk = true;
|
||||
|
||||
memset(RWBuffer, 0, this->BytesPerSector);
|
||||
/* KernelCallback */
|
||||
// callback.Reason = ReceiveReason;
|
||||
// callback.DiskCallback.RW = {
|
||||
// .Sector = 0,
|
||||
// .SectorCount = 2,
|
||||
// .Port = ItrPort,
|
||||
// .Buffer = RWBuffer,
|
||||
// .Write = false,
|
||||
// };
|
||||
// DriverManager->IOCB(modUniqueID, &callback);
|
||||
memcpy(&drive->Table, RWBuffer, sizeof(PartitionTable));
|
||||
|
||||
/*
|
||||
TODO: Add to devfs the disk
|
||||
*/
|
||||
|
||||
if (drive->Table.GPT.Signature == GPT_MAGIC)
|
||||
{
|
||||
drive->Style = GPT;
|
||||
uint32_t Entries = 512 / drive->Table.GPT.EntrySize;
|
||||
uint32_t Sectors = drive->Table.GPT.PartCount / Entries;
|
||||
for (uint32_t Block = 0; Block < Sectors; Block++)
|
||||
{
|
||||
memset(RWBuffer, 0, this->BytesPerSector);
|
||||
/* KernelCallback */
|
||||
// callback.Reason = ReceiveReason;
|
||||
// callback.DiskCallback.RW = {
|
||||
// .Sector = 2 + Block,
|
||||
// .SectorCount = 1,
|
||||
// .Port = ItrPort,
|
||||
// .Buffer = RWBuffer,
|
||||
// .Write = false,
|
||||
// };
|
||||
// DriverManager->IOCB(modUniqueID, &callback);
|
||||
|
||||
for (uint32_t e = 0; e < Entries; e++)
|
||||
{
|
||||
GUIDPartitionTableEntry GPTPartition = reinterpret_cast<GUIDPartitionTableEntry *>(RWBuffer)[e];
|
||||
if (memcmp(GPTPartition.PartitionType, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", sizeof(GPTPartition.PartitionType)) != 0)
|
||||
{
|
||||
debug("Partition Type: %02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
|
||||
GPTPartition.PartitionType[0], GPTPartition.PartitionType[1], GPTPartition.PartitionType[2], GPTPartition.PartitionType[3],
|
||||
GPTPartition.PartitionType[4], GPTPartition.PartitionType[5], GPTPartition.PartitionType[6], GPTPartition.PartitionType[7],
|
||||
GPTPartition.PartitionType[8], GPTPartition.PartitionType[9], GPTPartition.PartitionType[10], GPTPartition.PartitionType[11],
|
||||
GPTPartition.PartitionType[12], GPTPartition.PartitionType[13], GPTPartition.PartitionType[14], GPTPartition.PartitionType[15]);
|
||||
|
||||
debug("Unique Partition GUID: %02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
|
||||
GPTPartition.UniquePartitionGUID[0], GPTPartition.UniquePartitionGUID[1], GPTPartition.UniquePartitionGUID[2], GPTPartition.UniquePartitionGUID[3],
|
||||
GPTPartition.UniquePartitionGUID[4], GPTPartition.UniquePartitionGUID[5], GPTPartition.UniquePartitionGUID[6], GPTPartition.UniquePartitionGUID[7],
|
||||
GPTPartition.UniquePartitionGUID[8], GPTPartition.UniquePartitionGUID[9], GPTPartition.UniquePartitionGUID[10], GPTPartition.UniquePartitionGUID[11],
|
||||
GPTPartition.UniquePartitionGUID[12], GPTPartition.UniquePartitionGUID[13], GPTPartition.UniquePartitionGUID[14], GPTPartition.UniquePartitionGUID[15]);
|
||||
|
||||
Partition *partition = new Partition{};
|
||||
memset(partition->Label, '\0', sizeof(partition->Label));
|
||||
// TODO: Add support for UTF-16 partition names.
|
||||
/* Convert utf16 to utf8 */
|
||||
for (int i = 0; i < 36; i++)
|
||||
{
|
||||
uint16_t utf16 = GPTPartition.PartitionName[i];
|
||||
if (utf16 == 0)
|
||||
break;
|
||||
if (utf16 < 0x80)
|
||||
partition->Label[i] = (char)utf16;
|
||||
else if (utf16 < 0x800)
|
||||
{
|
||||
partition->Label[i] = (char)(0xC0 | (utf16 >> 6));
|
||||
partition->Label[i + 1] = (char)(0x80 | (utf16 & 0x3F));
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
partition->Label[i] = (char)(0xE0 | (utf16 >> 12));
|
||||
partition->Label[i + 1] = (char)(0x80 | ((utf16 >> 6) & 0x3F));
|
||||
partition->Label[i + 2] = (char)(0x80 | (utf16 & 0x3F));
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
partition->StartLBA = GPTPartition.FirstLBA;
|
||||
partition->EndLBA = GPTPartition.LastLBA;
|
||||
partition->Sectors = (size_t)(partition->EndLBA - partition->StartLBA);
|
||||
partition->Port = ItrPort;
|
||||
partition->Flags = Present;
|
||||
partition->Style = GPT;
|
||||
if (GPTPartition.Attributes & 1)
|
||||
partition->Flags |= EFISystemPartition;
|
||||
partition->Index = drive->Partitions.size();
|
||||
trace("GPT partition \"%s\" found with %lld sectors",
|
||||
partition->Label, partition->Sectors);
|
||||
drive->Partitions.push_back(partition);
|
||||
|
||||
char PartitionName[64];
|
||||
sprintf(PartitionName, "sd%ldp%ld", drives.size(), partition->Index);
|
||||
fixme("PartitionName: %s", PartitionName);
|
||||
|
||||
/*
|
||||
TODO: Add to devfs the disk
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
trace("%d GPT partitions found.", drive->Partitions.size());
|
||||
}
|
||||
else if (drive->Table.MBR.Signature[0] == MBR_MAGIC0 &&
|
||||
drive->Table.MBR.Signature[1] == MBR_MAGIC1)
|
||||
{
|
||||
drive->Style = MBR;
|
||||
for (size_t p = 0; p < 4; p++)
|
||||
if (drive->Table.MBR.Partitions[p].LBAFirst != 0)
|
||||
{
|
||||
Partition *partition = new Partition{};
|
||||
partition->StartLBA = drive->Table.MBR.Partitions[p].LBAFirst;
|
||||
partition->EndLBA = drive->Table.MBR.Partitions[p].LBAFirst + drive->Table.MBR.Partitions[p].Sectors;
|
||||
partition->Sectors = drive->Table.MBR.Partitions[p].Sectors;
|
||||
partition->Port = ItrPort;
|
||||
partition->Flags = Present;
|
||||
partition->Style = MBR;
|
||||
partition->Index = drive->Partitions.size();
|
||||
trace("MBR Partition %x found with %d sectors.",
|
||||
drive->Table.MBR.UniqueID, partition->Sectors);
|
||||
drive->Partitions.push_back(partition);
|
||||
|
||||
char PartitionName[64];
|
||||
sprintf(PartitionName, "sd%ldp%ld", drives.size(), partition->Index);
|
||||
fixme("PartitionName: %s", PartitionName);
|
||||
|
||||
/*
|
||||
TODO: Add to devfs the disk
|
||||
*/
|
||||
}
|
||||
trace("%d MBR partitions found.", drive->Partitions.size());
|
||||
}
|
||||
else
|
||||
warn("No partition table found on port %d!", ItrPort);
|
||||
|
||||
drives.push_back(drive);
|
||||
}
|
||||
|
||||
KernelAllocator.FreePages(RWBuffer, TO_PAGES(this->BytesPerSector + 1));
|
||||
}
|
||||
|
||||
Manager::Manager() {}
|
||||
Manager::~Manager() {}
|
||||
}
|
924
Kernel/core/driver/api.cpp
Normal file
924
Kernel/core/driver/api.cpp
Normal file
@ -0,0 +1,924 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <driver.hpp>
|
||||
#include <interface/driver.h>
|
||||
#include <interface/fs.h>
|
||||
#include <type_traits>
|
||||
#include <interface/aip.h>
|
||||
#include <interface/input.h>
|
||||
#include <interface/pci.h>
|
||||
|
||||
#include "../../kernel.h"
|
||||
|
||||
#define DEBUG_API
|
||||
|
||||
#ifdef DEBUG_API
|
||||
#define dbg_api(Format, ...) func(Format, ##__VA_ARGS__)
|
||||
#else
|
||||
#define dbg_api(Format, ...)
|
||||
#endif
|
||||
|
||||
namespace v0
|
||||
{
|
||||
typedef int CriticalState;
|
||||
|
||||
void KernelPrint(dev_t DriverID, const char *Format, va_list args)
|
||||
{
|
||||
dbg_api("%d, %s, %#lx", DriverID, Format, args);
|
||||
|
||||
_KPrint(Format, args);
|
||||
}
|
||||
|
||||
void KernelLog(dev_t DriverID, const char *Format, va_list args)
|
||||
{
|
||||
dbg_api("%d, %s, %#lx", DriverID, Format, args);
|
||||
|
||||
fctprintf(uart_wrapper, nullptr, "DRVER| %ld: ", DriverID);
|
||||
vfctprintf(uart_wrapper, nullptr, Format, args);
|
||||
uart_wrapper('\n', nullptr);
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
||||
CriticalState EnterCriticalSection(dev_t DriverID)
|
||||
{
|
||||
dbg_api("%d", DriverID);
|
||||
|
||||
CriticalState cs;
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
|
||||
uintptr_t Flags;
|
||||
#if defined(__x86_64__)
|
||||
asmv("pushfq");
|
||||
asmv("popq %0"
|
||||
: "=r"(Flags));
|
||||
#else
|
||||
asmv("pushfl");
|
||||
asmv("popl %0"
|
||||
: "=r"(Flags));
|
||||
#endif
|
||||
cs = Flags & (1 << 9);
|
||||
asmv("cli");
|
||||
|
||||
#elif defined(__arm__) || defined(__aarch64__)
|
||||
|
||||
uintptr_t Flags;
|
||||
asmv("mrs %0, cpsr"
|
||||
: "=r"(Flags));
|
||||
cs = Flags & (1 << 7);
|
||||
asmv("cpsid i");
|
||||
|
||||
#endif
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
void LeaveCriticalSection(dev_t DriverID, CriticalState PreviousState)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, PreviousState);
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
|
||||
if (PreviousState)
|
||||
asmv("sti");
|
||||
|
||||
#elif defined(__arm__) || defined(__aarch64__)
|
||||
|
||||
if (PreviousState)
|
||||
asmv("cpsie i");
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
int RegisterInterruptHandler(dev_t DriverID, uint8_t IRQ, void *Handler)
|
||||
{
|
||||
dbg_api("%d, %d, %#lx", DriverID, IRQ, Handler);
|
||||
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(DriverID);
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
if (drv->InterruptHandlers->contains(IRQ))
|
||||
return -EEXIST;
|
||||
|
||||
Interrupts::AddHandler((void (*)(CPU::TrapFrame *))Handler, IRQ);
|
||||
auto ih = drv->InterruptHandlers;
|
||||
ih->insert(std::pair<uint8_t, void *>(IRQ, Handler));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int OverrideInterruptHandler(dev_t DriverID, uint8_t IRQ, void *Handler)
|
||||
{
|
||||
dbg_api("%d, %d, %#lx", DriverID, IRQ, Handler);
|
||||
|
||||
debug("Overriding IRQ %d with %#lx", IRQ, Handler);
|
||||
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
|
||||
for (auto &var : drivers)
|
||||
{
|
||||
Driver::DriverObject *drv = &var.second;
|
||||
for (const auto &ih : *drv->InterruptHandlers)
|
||||
{
|
||||
if (ih.first != IRQ)
|
||||
continue;
|
||||
|
||||
debug("Removing IRQ %d: %#lx for %s", IRQ, (uintptr_t)ih.second, drv->Path.c_str());
|
||||
Interrupts::RemoveHandler((void (*)(CPU::TrapFrame *))ih.second, IRQ);
|
||||
drv->InterruptHandlers->erase(IRQ);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return RegisterInterruptHandler(DriverID, IRQ, Handler);
|
||||
}
|
||||
|
||||
int UnregisterInterruptHandler(dev_t DriverID, uint8_t IRQ, void *Handler)
|
||||
{
|
||||
dbg_api("%d, %d, %#lx", DriverID, IRQ, Handler);
|
||||
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(DriverID);
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
Interrupts::RemoveHandler((void (*)(CPU::TrapFrame *))Handler, IRQ);
|
||||
auto ih = drv->InterruptHandlers;
|
||||
ih->erase(IRQ);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int UnregisterAllInterruptHandlers(dev_t DriverID, void *Handler)
|
||||
{
|
||||
dbg_api("%d, %#lx", DriverID, Handler);
|
||||
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(DriverID);
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
for (auto &i : *drv->InterruptHandlers)
|
||||
{
|
||||
Interrupts::RemoveHandler((void (*)(CPU::TrapFrame *))Handler, i.first);
|
||||
debug("Removed IRQ %d: %#lx for %s", i.first, (uintptr_t)Handler, drv->Path.c_str());
|
||||
}
|
||||
auto ih = drv->InterruptHandlers;
|
||||
ih->clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
||||
dev_t RegisterFileSystem(dev_t DriverID, FileSystemInfo *Info, struct Inode *Root)
|
||||
{
|
||||
dbg_api("%d, %#lx, %#lx", DriverID, Info, Root);
|
||||
|
||||
return fs->RegisterFileSystem(Info, Root);
|
||||
}
|
||||
|
||||
int UnregisterFileSystem(dev_t DriverID, dev_t Device)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, Device);
|
||||
|
||||
return fs->UnregisterFileSystem(Device);
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
||||
pid_t CreateKernelProcess(dev_t DriverID, const char *Name)
|
||||
{
|
||||
dbg_api("%d, %s", DriverID, Name);
|
||||
|
||||
Tasking::PCB *pcb = TaskManager->CreateProcess(nullptr, Name, Tasking::System,
|
||||
true, 0, 0);
|
||||
|
||||
return pcb->ID;
|
||||
}
|
||||
|
||||
pid_t CreateKernelThread(dev_t DriverID, pid_t pId, const char *Name, void *EntryPoint, void *Argument)
|
||||
{
|
||||
dbg_api("%d, %d, %s, %#lx, %#lx", DriverID, pId, Name, EntryPoint, Argument);
|
||||
|
||||
Tasking::PCB *parent = TaskManager->GetProcessByID(pId);
|
||||
if (!parent)
|
||||
return -EINVAL;
|
||||
|
||||
CriticalSection cs;
|
||||
Tasking::TCB *tcb = TaskManager->CreateThread(parent, (Tasking::IP)EntryPoint);
|
||||
if (Argument)
|
||||
tcb->SYSV_ABI_Call((uintptr_t)Argument);
|
||||
tcb->Rename(Name);
|
||||
return tcb->ID;
|
||||
}
|
||||
|
||||
pid_t GetCurrentProcess(dev_t DriverID)
|
||||
{
|
||||
dbg_api("%d", DriverID);
|
||||
|
||||
return TaskManager->GetCurrentProcess()->ID;
|
||||
}
|
||||
|
||||
int KillProcess(dev_t DriverID, pid_t pId, int ExitCode)
|
||||
{
|
||||
dbg_api("%d, %d, %d", DriverID, pId, ExitCode);
|
||||
|
||||
Tasking::PCB *pcb = TaskManager->GetProcessByID(pId);
|
||||
if (!pcb)
|
||||
return -EINVAL;
|
||||
TaskManager->KillProcess(pcb, (Tasking::KillCode)ExitCode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int KillThread(dev_t DriverID, pid_t tId, pid_t pId, int ExitCode)
|
||||
{
|
||||
dbg_api("%d, %d, %d", DriverID, tId, ExitCode);
|
||||
|
||||
Tasking::TCB *tcb = TaskManager->GetThreadByID(tId, TaskManager->GetProcessByID(pId));
|
||||
if (!tcb)
|
||||
return -EINVAL;
|
||||
TaskManager->KillThread(tcb, (Tasking::KillCode)ExitCode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Yield(dev_t DriverID)
|
||||
{
|
||||
dbg_api("%d", DriverID);
|
||||
|
||||
TaskManager->Yield();
|
||||
}
|
||||
|
||||
void Sleep(dev_t DriverID, uint64_t Milliseconds)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, Milliseconds);
|
||||
|
||||
TaskManager->Sleep(Milliseconds);
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
||||
void PIC_EOI(dev_t DriverID, uint8_t IRQ)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, IRQ);
|
||||
|
||||
if (IRQ >= 8)
|
||||
outb(PIC2_CMD, _PIC_EOI);
|
||||
outb(PIC1_CMD, _PIC_EOI);
|
||||
}
|
||||
|
||||
void IRQ_MASK(dev_t DriverID, uint8_t IRQ)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, IRQ);
|
||||
|
||||
uint16_t port;
|
||||
uint8_t value;
|
||||
|
||||
if (IRQ < 8)
|
||||
port = PIC1_DATA;
|
||||
else
|
||||
{
|
||||
port = PIC2_DATA;
|
||||
IRQ -= 8;
|
||||
}
|
||||
|
||||
value = inb(port) | (1 << IRQ);
|
||||
outb(port, value);
|
||||
}
|
||||
|
||||
void IRQ_UNMASK(dev_t DriverID, uint8_t IRQ)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, IRQ);
|
||||
|
||||
uint16_t port;
|
||||
uint8_t value;
|
||||
|
||||
if (IRQ < 8)
|
||||
port = PIC1_DATA;
|
||||
else
|
||||
{
|
||||
port = PIC2_DATA;
|
||||
IRQ -= 8;
|
||||
}
|
||||
|
||||
value = inb(port) & ~(1 << IRQ);
|
||||
outb(port, value);
|
||||
}
|
||||
|
||||
void PS2Wait(dev_t DriverID, const bool Output)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, Output);
|
||||
|
||||
int Timeout = 100000;
|
||||
PS2_STATUSES Status = {.Raw = inb(PS2_STATUS)};
|
||||
while (Timeout--)
|
||||
{
|
||||
if (!Output) /* FIXME: Reverse? */
|
||||
{
|
||||
if (Status.OutputBufferFull == 0)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Status.InputBufferFull == 0)
|
||||
return;
|
||||
}
|
||||
Status.Raw = inb(PS2_STATUS);
|
||||
}
|
||||
|
||||
warn("PS/2 controller timeout! (Status: %#x, %d)", Status, Output);
|
||||
}
|
||||
|
||||
void PS2WriteCommand(dev_t DriverID, uint8_t Command)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, Command);
|
||||
|
||||
WaitInput;
|
||||
outb(PS2_CMD, Command);
|
||||
}
|
||||
|
||||
void PS2WriteData(dev_t DriverID, uint8_t Data)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, Data);
|
||||
|
||||
WaitInput;
|
||||
outb(PS2_DATA, Data);
|
||||
}
|
||||
|
||||
uint8_t PS2ReadData(dev_t DriverID)
|
||||
{
|
||||
dbg_api("%d", DriverID);
|
||||
|
||||
WaitOutput;
|
||||
return inb(PS2_DATA);
|
||||
}
|
||||
|
||||
uint8_t PS2ReadStatus(dev_t DriverID)
|
||||
{
|
||||
dbg_api("%d", DriverID);
|
||||
|
||||
WaitOutput;
|
||||
return inb(PS2_STATUS);
|
||||
}
|
||||
|
||||
uint8_t PS2ReadAfterACK(dev_t DriverID)
|
||||
{
|
||||
dbg_api("%d", DriverID);
|
||||
|
||||
uint8_t ret = PS2ReadData(DriverID);
|
||||
while (ret == PS2_ACK)
|
||||
{
|
||||
WaitOutput;
|
||||
ret = inb(PS2_DATA);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PS2ClearOutputBuffer(dev_t DriverID)
|
||||
{
|
||||
dbg_api("%d", DriverID);
|
||||
|
||||
PS2_STATUSES Status;
|
||||
int timeout = 0x500;
|
||||
while (timeout--)
|
||||
{
|
||||
Status.Raw = inb(PS2_STATUS);
|
||||
if (Status.OutputBufferFull == 0)
|
||||
return;
|
||||
inb(PS2_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
int PS2ACKTimeout(dev_t DriverID)
|
||||
{
|
||||
dbg_api("%d", DriverID);
|
||||
|
||||
int timeout = 0x500;
|
||||
while (timeout > 0)
|
||||
{
|
||||
if (PS2ReadData(DriverID) == PS2_ACK)
|
||||
return 0;
|
||||
timeout--;
|
||||
}
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
||||
void *AllocateMemory(dev_t DriverID, size_t Pages)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, Pages);
|
||||
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &Drivers =
|
||||
DriverManager->GetDrivers();
|
||||
|
||||
auto itr = Drivers.find(DriverID);
|
||||
assert(itr != Drivers.end());
|
||||
|
||||
void *ptr = itr->second.vma->RequestPages(Pages);
|
||||
memset(ptr, 0, FROM_PAGES(Pages));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void FreeMemory(dev_t DriverID, void *Pointer, size_t Pages)
|
||||
{
|
||||
dbg_api("%d, %#lx, %d", DriverID, Pointer, Pages);
|
||||
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &Drivers =
|
||||
DriverManager->GetDrivers();
|
||||
|
||||
auto itr = Drivers.find(DriverID);
|
||||
assert(itr != Drivers.end());
|
||||
|
||||
itr->second.vma->FreePages(Pointer, Pages);
|
||||
}
|
||||
|
||||
void *MemoryCopy(dev_t DriverID, void *Destination, const void *Source, size_t Length)
|
||||
{
|
||||
dbg_api("%d, %#lx, %#lx, %d", DriverID, Destination, Source, Length);
|
||||
|
||||
return memcpy(Destination, Source, Length);
|
||||
}
|
||||
|
||||
void *MemorySet(dev_t DriverID, void *Destination, int Value, size_t Length)
|
||||
{
|
||||
dbg_api("%d, %#lx, %d, %d", DriverID, Destination, Value, Length);
|
||||
|
||||
return memset(Destination, Value, Length);
|
||||
}
|
||||
|
||||
void *MemoryMove(dev_t DriverID, void *Destination, const void *Source, size_t Length)
|
||||
{
|
||||
dbg_api("%d, %#lx, %#lx, %d", DriverID, Destination, Source, Length);
|
||||
|
||||
return memmove(Destination, Source, Length);
|
||||
}
|
||||
|
||||
size_t StringLength(dev_t DriverID, const char String[])
|
||||
{
|
||||
dbg_api("%d, %s", DriverID, String);
|
||||
|
||||
return strlen(String);
|
||||
}
|
||||
|
||||
char *_strstr(dev_t DriverID, const char *Haystack, const char *Needle)
|
||||
{
|
||||
dbg_api("%d, %s, %s", DriverID, Haystack, Needle);
|
||||
|
||||
return (char *)strstr(Haystack, Needle);
|
||||
}
|
||||
|
||||
void MapPages(dev_t MajorID, void *PhysicalAddress, void *VirtualAddress, size_t Pages, uint32_t Flags)
|
||||
{
|
||||
dbg_api("%d, %#lx, %#lx, %d, %d", MajorID, PhysicalAddress, VirtualAddress, Pages, Flags);
|
||||
|
||||
Memory::Virtual vmm(KernelPageTable);
|
||||
vmm.Map(VirtualAddress, PhysicalAddress, Pages, Flags);
|
||||
}
|
||||
|
||||
void UnmapPages(dev_t MajorID, void *VirtualAddress, size_t Pages)
|
||||
{
|
||||
dbg_api("%d, %#lx, %d", MajorID, VirtualAddress, Pages);
|
||||
|
||||
Memory::Virtual vmm(KernelPageTable);
|
||||
vmm.Unmap(VirtualAddress, Pages);
|
||||
}
|
||||
|
||||
void AppendMapFlag(dev_t MajorID, void *Address, PageMapFlags Flag)
|
||||
{
|
||||
dbg_api("%d, %#lx, %d", MajorID, Address, Flag);
|
||||
|
||||
Memory::Virtual vmm(KernelPageTable);
|
||||
vmm.GetPTE(Address)->raw |= Flag;
|
||||
}
|
||||
|
||||
void RemoveMapFlag(dev_t MajorID, void *Address, PageMapFlags Flag)
|
||||
{
|
||||
dbg_api("%d, %#lx, %d", MajorID, Address, Flag);
|
||||
|
||||
Memory::Virtual vmm(KernelPageTable);
|
||||
vmm.GetPTE(Address)->raw &= ~Flag;
|
||||
}
|
||||
|
||||
void *Znwm(size_t Size)
|
||||
{
|
||||
dbg_api("%d", Size);
|
||||
|
||||
return malloc(Size);
|
||||
}
|
||||
|
||||
void ZdlPvm(void *Pointer, size_t Size)
|
||||
{
|
||||
dbg_api("%d, %#lx", Pointer, Size);
|
||||
|
||||
free(Pointer);
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
||||
__PCIArray *GetPCIDevices(dev_t DriverID, uint16_t _Vendors[], uint16_t _Devices[])
|
||||
{
|
||||
dbg_api("%d, %#lx, %#lx", DriverID, _Vendors, _Devices);
|
||||
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &Drivers =
|
||||
DriverManager->GetDrivers();
|
||||
|
||||
auto itr = Drivers.find(DriverID);
|
||||
if (itr == Drivers.end())
|
||||
return nullptr;
|
||||
|
||||
std::list<uint16_t> VendorIDs;
|
||||
for (int i = 0; _Vendors[i] != 0x0; i++)
|
||||
VendorIDs.push_back(_Vendors[i]);
|
||||
|
||||
std::list<uint16_t> DeviceIDs;
|
||||
for (int i = 0; _Devices[i] != 0x0; i++)
|
||||
DeviceIDs.push_back(_Devices[i]);
|
||||
|
||||
std::list<PCI::PCIDevice> Devices = PCIManager->FindPCIDevice(VendorIDs, DeviceIDs);
|
||||
if (Devices.empty())
|
||||
return nullptr;
|
||||
|
||||
Memory::VirtualMemoryArea *vma = itr->second.vma;
|
||||
__PCIArray *head = nullptr;
|
||||
__PCIArray *array = nullptr;
|
||||
|
||||
foreach (auto &dev in Devices)
|
||||
{
|
||||
/* TODO: optimize memory allocation */
|
||||
PCI::PCIDevice *dptr = (PCI::PCIDevice *)vma->RequestPages(TO_PAGES(sizeof(PCI::PCIDevice)));
|
||||
memcpy(dptr, &dev, sizeof(PCI::PCIDevice));
|
||||
|
||||
__PCIArray *newArray = (__PCIArray *)vma->RequestPages(TO_PAGES(sizeof(__PCIArray)));
|
||||
|
||||
if (unlikely(head == nullptr))
|
||||
{
|
||||
head = newArray;
|
||||
array = head;
|
||||
}
|
||||
else
|
||||
{
|
||||
array->Next = newArray;
|
||||
array = newArray;
|
||||
}
|
||||
|
||||
array->Device = dptr;
|
||||
array->Next = nullptr;
|
||||
|
||||
debug("Found %02x.%02x.%02x: %04x:%04x",
|
||||
dev.Bus, dev.Device, dev.Function,
|
||||
dev.Header->VendorID, dev.Header->DeviceID);
|
||||
}
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
void InitializePCI(dev_t DriverID, void *_Header)
|
||||
{
|
||||
dbg_api("%d, %#lx", DriverID, _Header);
|
||||
|
||||
PCI::PCIDevice *__device = (PCI::PCIDevice *)_Header;
|
||||
PCI::PCIDeviceHeader *Header = (PCI::PCIDeviceHeader *)__device->Header;
|
||||
|
||||
debug("Header Type: %d", Header->HeaderType);
|
||||
switch (Header->HeaderType)
|
||||
{
|
||||
case 128:
|
||||
warn("Unknown header type %d! Guessing PCI Header 0",
|
||||
Header->HeaderType);
|
||||
[[fallthrough]];
|
||||
case 0: /* PCI Header 0 */
|
||||
{
|
||||
PCI::PCIHeader0 *hdr0 = (PCI::PCIHeader0 *)Header;
|
||||
|
||||
uint32_t BAR[6];
|
||||
size_t BARsSize[6];
|
||||
|
||||
BAR[0] = hdr0->BAR0;
|
||||
BAR[1] = hdr0->BAR1;
|
||||
BAR[2] = hdr0->BAR2;
|
||||
BAR[3] = hdr0->BAR3;
|
||||
BAR[4] = hdr0->BAR4;
|
||||
BAR[5] = hdr0->BAR5;
|
||||
|
||||
debug("Type: %d; IOBase: %#lx; MemoryBase: %#lx",
|
||||
BAR[0] & 1, BAR[1] & (~3), BAR[0] & (~15));
|
||||
|
||||
/* BARs Size */
|
||||
for (short i = 0; i < 6; i++)
|
||||
{
|
||||
if (BAR[i] == 0)
|
||||
continue;
|
||||
|
||||
size_t size;
|
||||
if ((BAR[i] & 1) == 0) /* Memory Base */
|
||||
{
|
||||
hdr0->BAR0 = 0xFFFFFFFF;
|
||||
size = hdr0->BAR0;
|
||||
hdr0->BAR0 = BAR[i];
|
||||
BARsSize[i] = size & (~15);
|
||||
BARsSize[i] = ~BARsSize[i] + 1;
|
||||
BARsSize[i] = BARsSize[i] & 0xFFFFFFFF;
|
||||
debug("BAR%d %#lx size: %d",
|
||||
i, BAR[i], BARsSize[i]);
|
||||
}
|
||||
else if ((BAR[i] & 1) == 1) /* I/O Base */
|
||||
{
|
||||
hdr0->BAR1 = 0xFFFFFFFF;
|
||||
size = hdr0->BAR1;
|
||||
hdr0->BAR1 = BAR[i];
|
||||
BARsSize[i] = size & (~3);
|
||||
BARsSize[i] = ~BARsSize[i] + 1;
|
||||
BARsSize[i] = BARsSize[i] & 0xFFFF;
|
||||
debug("BAR%d %#lx size: %d",
|
||||
i, BAR[i], BARsSize[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Memory::Virtual vmm(KernelPageTable);
|
||||
|
||||
/* Mapping the BARs */
|
||||
for (short i = 0; i < 6; i++)
|
||||
{
|
||||
if (BAR[i] == 0)
|
||||
continue;
|
||||
|
||||
if ((BAR[i] & 1) == 0) /* Memory Base */
|
||||
{
|
||||
uintptr_t BARBase = BAR[i] & (~15);
|
||||
size_t BARSize = BARsSize[i];
|
||||
|
||||
debug("Mapping BAR%d %#lx-%#lx",
|
||||
i, BARBase, BARBase + BARSize);
|
||||
|
||||
if (BARSize == 0)
|
||||
{
|
||||
warn("BAR%d size is zero!", i);
|
||||
BARSize++;
|
||||
}
|
||||
|
||||
vmm.Map((void *)BARBase, (void *)BARBase,
|
||||
BARSize, Memory::RW | Memory::PWT | Memory::PCD);
|
||||
}
|
||||
else if ((BAR[i] & 1) == 1) /* I/O Base */
|
||||
{
|
||||
uintptr_t BARBase = BAR[i] & (~3);
|
||||
size_t BARSize = BARsSize[i];
|
||||
|
||||
debug("Mapping BAR%d %#x-%#x",
|
||||
i, BARBase, BARBase + BARSize);
|
||||
|
||||
if (BARSize == 0)
|
||||
{
|
||||
warn("BAR%d size is zero!", i);
|
||||
BARSize++;
|
||||
}
|
||||
|
||||
vmm.Map((void *)BARBase, (void *)BARBase,
|
||||
BARSize, Memory::RW | Memory::PWT | Memory::PCD);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: /* PCI Header 1 (PCI-to-PCI Bridge) */
|
||||
{
|
||||
fixme("PCI Header 1 (PCI-to-PCI Bridge) not implemented yet");
|
||||
break;
|
||||
}
|
||||
case 2: /* PCI Header 2 (PCI-to-CardBus Bridge) */
|
||||
{
|
||||
fixme("PCI Header 2 (PCI-to-CardBus Bridge) not implemented yet");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
error("Unknown header type %d", Header->HeaderType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Header->Command |= PCI_COMMAND_MASTER |
|
||||
PCI_COMMAND_IO |
|
||||
PCI_COMMAND_MEMORY;
|
||||
Header->Command &= ~PCI_COMMAND_INTX_DISABLE;
|
||||
}
|
||||
|
||||
uint32_t GetBAR(dev_t DriverID, uint8_t i, void *_Header)
|
||||
{
|
||||
dbg_api("%d, %d, %#lx", DriverID, i, _Header);
|
||||
|
||||
PCI::PCIDevice *__device = (PCI::PCIDevice *)_Header;
|
||||
PCI::PCIDeviceHeader *Header = (PCI::PCIDeviceHeader *)__device->Header;
|
||||
|
||||
switch (Header->HeaderType)
|
||||
{
|
||||
case 128:
|
||||
warn("Unknown header type %d! Guessing PCI Header 0",
|
||||
Header->HeaderType);
|
||||
[[fallthrough]];
|
||||
case 0: /* PCI Header 0 */
|
||||
{
|
||||
PCI::PCIHeader0 *hdr0 =
|
||||
(PCI::PCIHeader0 *)Header;
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
return hdr0->BAR0;
|
||||
case 1:
|
||||
return hdr0->BAR1;
|
||||
case 2:
|
||||
return hdr0->BAR2;
|
||||
case 3:
|
||||
return hdr0->BAR3;
|
||||
case 4:
|
||||
return hdr0->BAR4;
|
||||
case 5:
|
||||
return hdr0->BAR5;
|
||||
default:
|
||||
assert(!"Invalid BAR index");
|
||||
}
|
||||
}
|
||||
case 1: /* PCI Header 1 (PCI-to-PCI Bridge) */
|
||||
{
|
||||
PCI::PCIHeader1 *hdr1 =
|
||||
(PCI::PCIHeader1 *)Header;
|
||||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
return hdr1->BAR0;
|
||||
case 1:
|
||||
return hdr1->BAR1;
|
||||
default:
|
||||
assert(!"Invalid BAR index");
|
||||
}
|
||||
}
|
||||
case 2: /* PCI Header 2 (PCI-to-CardBus Bridge) */
|
||||
{
|
||||
assert(!"PCI-to-CardBus Bridge not supported");
|
||||
}
|
||||
default:
|
||||
assert(!"Invalid PCI header type");
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t iLine(dev_t DriverID, PCIDevice *Device)
|
||||
{
|
||||
dbg_api("%d, %#lx", DriverID, Device);
|
||||
|
||||
PCIHeader0 *Header = (PCIHeader0 *)Device->Header;
|
||||
return Header->InterruptLine;
|
||||
}
|
||||
|
||||
uint8_t iPin(dev_t DriverID, PCIDevice *Device)
|
||||
{
|
||||
dbg_api("%d, %#lx", DriverID, Device);
|
||||
|
||||
PCIHeader0 *Header = (PCIHeader0 *)Device->Header;
|
||||
return Header->InterruptPin;
|
||||
}
|
||||
|
||||
/* --------- */
|
||||
|
||||
dev_t RegisterDevice(dev_t DriverID, DeviceType Type, const InodeOperations *Operations)
|
||||
{
|
||||
dbg_api("%d, %d, %#lx", DriverID, Type, Operations);
|
||||
|
||||
return DriverManager->RegisterDevice(DriverID, Type, Operations);
|
||||
}
|
||||
|
||||
int UnregisterDevice(dev_t DriverID, dev_t Device)
|
||||
{
|
||||
dbg_api("%d, %d", DriverID, Device);
|
||||
|
||||
return DriverManager->UnregisterDevice(DriverID, Device);
|
||||
}
|
||||
|
||||
int ReportInputEvent(dev_t DriverID, InputReport *Report)
|
||||
{
|
||||
dbg_api("%d, %#lx", DriverID, Report);
|
||||
|
||||
return DriverManager->ReportInputEvent(DriverID, Report);
|
||||
}
|
||||
}
|
||||
|
||||
struct APISymbols
|
||||
{
|
||||
const char *Name;
|
||||
void *Function;
|
||||
};
|
||||
|
||||
static struct APISymbols APISymbols_v0[] = {
|
||||
{"__KernelPrint", (void *)v0::KernelPrint},
|
||||
{"__KernelLog", (void *)v0::KernelLog},
|
||||
|
||||
{"__EnterCriticalSection", (void *)v0::EnterCriticalSection},
|
||||
{"__LeaveCriticalSection", (void *)v0::LeaveCriticalSection},
|
||||
|
||||
{"__RegisterInterruptHandler", (void *)v0::RegisterInterruptHandler},
|
||||
{"__OverrideInterruptHandler", (void *)v0::OverrideInterruptHandler},
|
||||
{"__UnregisterInterruptHandler", (void *)v0::UnregisterInterruptHandler},
|
||||
{"__UnregisterAllInterruptHandlers", (void *)v0::UnregisterAllInterruptHandlers},
|
||||
|
||||
{"__RegisterFileSystem", (void *)v0::RegisterFileSystem},
|
||||
{"__UnregisterFileSystem", (void *)v0::UnregisterFileSystem},
|
||||
|
||||
{"__CreateKernelProcess", (void *)v0::CreateKernelProcess},
|
||||
{"__CreateKernelThread", (void *)v0::CreateKernelThread},
|
||||
{"__GetCurrentProcess", (void *)v0::GetCurrentProcess},
|
||||
{"__KillProcess", (void *)v0::KillProcess},
|
||||
{"__KillThread", (void *)v0::KillThread},
|
||||
{"__Yield", (void *)v0::Yield},
|
||||
{"__Sleep", (void *)v0::Sleep},
|
||||
|
||||
{"__PIC_EOI", (void *)v0::PIC_EOI},
|
||||
{"__IRQ_MASK", (void *)v0::IRQ_MASK},
|
||||
{"__IRQ_UNMASK", (void *)v0::IRQ_UNMASK},
|
||||
{"__PS2Wait", (void *)v0::PS2Wait},
|
||||
{"__PS2WriteCommand", (void *)v0::PS2WriteCommand},
|
||||
{"__PS2WriteData", (void *)v0::PS2WriteData},
|
||||
{"__PS2ReadData", (void *)v0::PS2ReadData},
|
||||
{"__PS2ReadStatus", (void *)v0::PS2ReadStatus},
|
||||
{"__PS2ReadAfterACK", (void *)v0::PS2ReadAfterACK},
|
||||
{"__PS2ClearOutputBuffer", (void *)v0::PS2ClearOutputBuffer},
|
||||
{"__PS2ACKTimeout", (void *)v0::PS2ACKTimeout},
|
||||
|
||||
{"__AllocateMemory", (void *)v0::AllocateMemory},
|
||||
{"__FreeMemory", (void *)v0::FreeMemory},
|
||||
{"__MemoryCopy", (void *)v0::MemoryCopy},
|
||||
{"__MemorySet", (void *)v0::MemorySet},
|
||||
{"__MemoryMove", (void *)v0::MemoryMove},
|
||||
{"__StringLength", (void *)v0::StringLength},
|
||||
{"__strstr", (void *)v0::_strstr},
|
||||
{"__MapPages", (void *)v0::MapPages},
|
||||
{"__UnmapPages", (void *)v0::UnmapPages},
|
||||
{"__AppendMapFlag", (void *)v0::AppendMapFlag},
|
||||
{"__RemoveMapFlag", (void *)v0::RemoveMapFlag},
|
||||
{"_Znwm", (void *)v0::Znwm},
|
||||
{"_ZdlPvm", (void *)v0::ZdlPvm},
|
||||
|
||||
{"__GetPCIDevices", (void *)v0::GetPCIDevices},
|
||||
{"__InitializePCI", (void *)v0::InitializePCI},
|
||||
{"__GetBAR", (void *)v0::GetBAR},
|
||||
{"__iLine", (void *)v0::iLine},
|
||||
{"__iPin", (void *)v0::iPin},
|
||||
|
||||
{"__RegisterDevice", (void *)v0::RegisterDevice},
|
||||
{"__UnregisterDevice", (void *)v0::UnregisterDevice},
|
||||
{"__ReportInputEvent", (void *)v0::ReportInputEvent},
|
||||
};
|
||||
|
||||
long __KernelUndefinedFunction(long arg0, long arg1, long arg2, long arg3,
|
||||
long arg4, long arg5, long arg6, long arg7)
|
||||
{
|
||||
debug("%#lx, %#lx, %#lx, %#lx, %#lx, %#lx, %#lx, %#lx",
|
||||
arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
|
||||
assert(!"Undefined kernel driver API function called!");
|
||||
CPU::Stop();
|
||||
}
|
||||
|
||||
void *GetSymbolByName(const char *Name, int Version)
|
||||
{
|
||||
switch (Version)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
for (auto sym : APISymbols_v0)
|
||||
{
|
||||
if (strcmp(Name, sym.Name) != 0)
|
||||
continue;
|
||||
|
||||
debug("Symbol %s found in API version %d", Name, Version);
|
||||
return sym.Function;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(!"Invalid API version");
|
||||
}
|
||||
|
||||
error("Symbol %s not found in API version %d", Name, Version);
|
||||
KPrint("Driver API symbol \"%s\" not found!", Name);
|
||||
return (void *)__KernelUndefinedFunction;
|
||||
}
|
890
Kernel/core/driver/daemon.cpp
Normal file
890
Kernel/core/driver/daemon.cpp
Normal file
@ -0,0 +1,890 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <driver.hpp>
|
||||
|
||||
#include <interface/driver.h>
|
||||
#include <interface/input.h>
|
||||
#include <memory.hpp>
|
||||
#include <stropts.h>
|
||||
#include <ints.hpp>
|
||||
#include <task.hpp>
|
||||
#include <printf.h>
|
||||
#include <exec.hpp>
|
||||
#include <rand.hpp>
|
||||
#include <cwalk.h>
|
||||
#include <md5.h>
|
||||
|
||||
#include "../../kernel.h"
|
||||
|
||||
using namespace vfs;
|
||||
|
||||
namespace Driver
|
||||
{
|
||||
/**
|
||||
* maj = 0
|
||||
* min:
|
||||
* 0 - <ROOT>
|
||||
* 1 - /proc/self
|
||||
* 2 - /dev/null
|
||||
* 3 - /dev/zero
|
||||
* 4 - /dev/random
|
||||
* 5 - /dev/mem
|
||||
* 6 - /dev/kcon
|
||||
* 7 - /dev/tty
|
||||
* 8 - /dev/ptmx
|
||||
*
|
||||
* maj = 1
|
||||
* min:
|
||||
* 0 - /dev/input/keyboard
|
||||
* 1 - /dev/input/mouse
|
||||
* ..- /dev/input/eventX
|
||||
*/
|
||||
|
||||
TTY::PTMXDevice *ptmx = nullptr;
|
||||
|
||||
int __fs_Lookup(struct Inode *_Parent, const char *Name, struct Inode **Result)
|
||||
{
|
||||
func("%#lx %s %#lx", _Parent, Name, Result);
|
||||
|
||||
assert(_Parent != nullptr);
|
||||
|
||||
const char *basename;
|
||||
size_t length;
|
||||
cwk_path_get_basename(Name, &basename, &length);
|
||||
if (basename == NULL)
|
||||
{
|
||||
error("Invalid name %s", Name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
auto Parent = (Manager::DeviceInode *)_Parent;
|
||||
for (const auto &child : Parent->Children)
|
||||
{
|
||||
debug("Comparing %s with %s", basename, child->Name.c_str());
|
||||
if (strcmp(child->Name.c_str(), basename) != 0)
|
||||
continue;
|
||||
|
||||
*Result = &child->Node;
|
||||
return 0;
|
||||
}
|
||||
|
||||
debug("Not found %s", Name);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int __fs_Create(struct Inode *_Parent, const char *Name, mode_t Mode, struct Inode **Result)
|
||||
{
|
||||
func("%#lx %s %d", _Parent, Name, Mode);
|
||||
|
||||
assert(_Parent != nullptr);
|
||||
|
||||
/* We expect to be /dev or children of it */
|
||||
auto Parent = (Manager::DeviceInode *)_Parent;
|
||||
auto _dev = new Manager::DeviceInode;
|
||||
_dev->Parent = nullptr;
|
||||
_dev->ParentInode = _Parent;
|
||||
_dev->Name = Name;
|
||||
_dev->Node.Mode = Mode;
|
||||
_dev->Node.Index = Parent->Node.Index + Parent->Children.size();
|
||||
Parent->Children.push_back(_dev);
|
||||
|
||||
*Result = &_dev->Node;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t __fs_Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
func("%#lx %d %d", Node, Size, Offset);
|
||||
switch (Node->GetMajor())
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 2: /* /dev/null */
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
case 3: /* /dev/zero */
|
||||
{
|
||||
if (Size <= 0)
|
||||
return 0;
|
||||
|
||||
memset(Buffer, 0, Size);
|
||||
return Size;
|
||||
}
|
||||
case 4: /* /dev/random */
|
||||
{
|
||||
if (Size <= 0)
|
||||
return 0;
|
||||
|
||||
if (Size < sizeof(uint64_t))
|
||||
{
|
||||
uint8_t *buf = (uint8_t *)Buffer;
|
||||
for (size_t i = 0; i < Size; i++)
|
||||
buf[i] = (uint8_t)(Random::rand16() & 0xFF);
|
||||
return Size;
|
||||
}
|
||||
|
||||
uint64_t *buf = (uint64_t *)Buffer;
|
||||
for (size_t i = 0; i < Size / sizeof(uint64_t); i++)
|
||||
buf[i] = Random::rand64();
|
||||
return Size;
|
||||
}
|
||||
case 5: /* /dev/mem */
|
||||
{
|
||||
stub;
|
||||
return 0;
|
||||
}
|
||||
case 6: /* /dev/kcon */
|
||||
return KernelConsole::CurrentTerminal.load()->Read(Buffer, Size, Offset);
|
||||
case 7: /* /dev/tty */
|
||||
{
|
||||
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
|
||||
if (tty == nullptr)
|
||||
return -ENOTTY;
|
||||
return tty->Read(Buffer, Size, Offset);
|
||||
}
|
||||
case 8: /* /dev/ptmx */
|
||||
return -ENOSYS;
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 0: /* /dev/input/keyboard */
|
||||
{
|
||||
if (Size < sizeof(KeyboardReport))
|
||||
return -EINVAL;
|
||||
|
||||
size_t nReads = Size / sizeof(KeyboardReport);
|
||||
|
||||
KeyboardReport *report = (KeyboardReport *)Buffer;
|
||||
|
||||
while (DriverManager->GlobalKeyboardInputReports.Count() == 0)
|
||||
TaskManager->Yield();
|
||||
|
||||
DriverManager->GlobalKeyboardInputReports.Read(report, nReads);
|
||||
return sizeof(KeyboardReport) * nReads;
|
||||
}
|
||||
case 1: /* /dev/input/mouse */
|
||||
{
|
||||
if (Size < sizeof(MouseReport))
|
||||
return -EINVAL;
|
||||
|
||||
size_t nReads = Size / sizeof(MouseReport);
|
||||
|
||||
MouseReport *report = (MouseReport *)Buffer;
|
||||
|
||||
while (DriverManager->GlobalMouseInputReports.Count() == 0)
|
||||
TaskManager->Yield();
|
||||
|
||||
DriverManager->GlobalMouseInputReports.Read(report, nReads);
|
||||
return sizeof(MouseReport) * nReads;
|
||||
}
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
}
|
||||
default:
|
||||
{
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(Node->GetMajor());
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", Node->GetMajor());
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
auto dop = drv->DeviceOperations;
|
||||
auto dOps = dop->find(Node->GetMinor());
|
||||
if (dOps == dop->end())
|
||||
ReturnLogError(-EINVAL, "Device %d not found", Node->GetMinor());
|
||||
AssertReturnError(dOps->second.Ops, -ENOTSUP);
|
||||
AssertReturnError(dOps->second.Ops->Read, -ENOTSUP);
|
||||
return dOps->second.Ops->Read(Node, Buffer, Size, Offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t __fs_Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
func("%#lx %p %d %d", Node, Buffer, Size, Offset);
|
||||
|
||||
switch (Node->GetMajor())
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 2: /* /dev/null */
|
||||
{
|
||||
return Size;
|
||||
}
|
||||
case 3: /* /dev/zero */
|
||||
{
|
||||
return Size;
|
||||
}
|
||||
case 4: /* /dev/random */
|
||||
{
|
||||
return Size;
|
||||
}
|
||||
case 5: /* /dev/mem */
|
||||
{
|
||||
stub;
|
||||
return 0;
|
||||
}
|
||||
case 6: /* /dev/kcon */
|
||||
return KernelConsole::CurrentTerminal.load()->Write(Buffer, Size, Offset);
|
||||
case 7: /* /dev/tty */
|
||||
{
|
||||
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
|
||||
if (tty == nullptr)
|
||||
return -ENOTTY;
|
||||
return tty->Write(Buffer, Size, Offset);
|
||||
}
|
||||
case 8: /* /dev/ptmx */
|
||||
return -ENOSYS;
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 0: /* /dev/input/keyboard */
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
case 1: /* /dev/input/mouse */
|
||||
{
|
||||
return -ENOTSUP;
|
||||
}
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
}
|
||||
default:
|
||||
{
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(Node->GetMajor());
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", Node->GetMajor());
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
auto dop = drv->DeviceOperations;
|
||||
auto dOps = dop->find(Node->GetMinor());
|
||||
if (dOps == dop->end())
|
||||
ReturnLogError(-EINVAL, "Device %d not found", Node->GetMinor());
|
||||
AssertReturnError(dOps->second.Ops, -ENOTSUP);
|
||||
AssertReturnError(dOps->second.Ops->Write, -ENOTSUP);
|
||||
return dOps->second.Ops->Write(Node, Buffer, Size, Offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int __fs_Open(struct Inode *Node, int Flags, mode_t Mode)
|
||||
{
|
||||
func("%#lx %d %d", Node, Flags, Mode);
|
||||
|
||||
switch (Node->GetMajor())
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 2: /* /dev/null */
|
||||
case 3: /* /dev/zero */
|
||||
case 4: /* /dev/random */
|
||||
case 5: /* /dev/mem */
|
||||
return -ENOSYS;
|
||||
case 6: /* /dev/kcon */
|
||||
return KernelConsole::CurrentTerminal.load()->Open(Flags, Mode);
|
||||
case 7: /* /dev/tty */
|
||||
{
|
||||
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
|
||||
if (tty == nullptr)
|
||||
return -ENOTTY;
|
||||
return tty->Open(Flags, Mode);
|
||||
}
|
||||
case 8: /* /dev/ptmx */
|
||||
return ptmx->Open();
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 0: /* /dev/input/keyboard */
|
||||
case 1: /* /dev/input/mouse */
|
||||
return -ENOTSUP;
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
}
|
||||
default:
|
||||
{
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(Node->GetMajor());
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", Node->GetMajor());
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
auto dop = drv->DeviceOperations;
|
||||
auto dOps = dop->find(Node->GetMinor());
|
||||
if (dOps == dop->end())
|
||||
ReturnLogError(-EINVAL, "Device %d not found", Node->GetMinor());
|
||||
AssertReturnError(dOps->second.Ops, -ENOTSUP);
|
||||
AssertReturnError(dOps->second.Ops->Open, -ENOTSUP);
|
||||
return dOps->second.Ops->Open(Node, Flags, Mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int __fs_Close(struct Inode *Node)
|
||||
{
|
||||
func("%#lx", Node);
|
||||
|
||||
switch (Node->GetMajor())
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 2: /* /dev/null */
|
||||
case 3: /* /dev/zero */
|
||||
case 4: /* /dev/random */
|
||||
case 5: /* /dev/mem */
|
||||
return -ENOSYS;
|
||||
case 6: /* /dev/kcon */
|
||||
return KernelConsole::CurrentTerminal.load()->Close();
|
||||
case 7: /* /dev/tty */
|
||||
{
|
||||
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
|
||||
if (tty == nullptr)
|
||||
return -ENOTTY;
|
||||
return tty->Close();
|
||||
}
|
||||
case 8: /* /dev/ptmx */
|
||||
return ptmx->Close();
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 0: /* /dev/input/keyboard */
|
||||
case 1: /* /dev/input/mouse */
|
||||
return -ENOTSUP;
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
}
|
||||
default:
|
||||
{
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(Node->GetMajor());
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", Node->GetMajor());
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
auto dop = drv->DeviceOperations;
|
||||
auto dOps = dop->find(Node->GetMinor());
|
||||
if (dOps == dop->end())
|
||||
ReturnLogError(-EINVAL, "Device %d not found", Node->GetMinor());
|
||||
AssertReturnError(dOps->second.Ops, -ENOTSUP);
|
||||
AssertReturnError(dOps->second.Ops->Close, -ENOTSUP);
|
||||
return dOps->second.Ops->Close(Node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int __fs_Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
|
||||
{
|
||||
func("%#lx %lu %#lx", Node, Request, Argp);
|
||||
|
||||
switch (Node->GetMajor())
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 2: /* /dev/null */
|
||||
case 3: /* /dev/zero */
|
||||
case 4: /* /dev/random */
|
||||
case 5: /* /dev/mem */
|
||||
return -ENOSYS;
|
||||
case 6: /* /dev/kcon */
|
||||
return KernelConsole::CurrentTerminal.load()->Ioctl(Request, Argp);
|
||||
case 7: /* /dev/tty */
|
||||
{
|
||||
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
|
||||
if (tty == nullptr)
|
||||
return -ENOTTY;
|
||||
return tty->Ioctl(Request, Argp);
|
||||
}
|
||||
case 8: /* /dev/ptmx */
|
||||
return -ENOSYS;
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 0: /* /dev/input/keyboard */
|
||||
case 1: /* /dev/input/mouse */
|
||||
return -ENOSYS;
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
}
|
||||
default:
|
||||
{
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(Node->GetMajor());
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", Node->GetMajor());
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
auto dop = drv->DeviceOperations;
|
||||
auto dOps = dop->find(Node->GetMinor());
|
||||
if (dOps == dop->end())
|
||||
ReturnLogError(-EINVAL, "Device %d not found", Node->GetMinor());
|
||||
AssertReturnError(dOps->second.Ops, -ENOTSUP);
|
||||
AssertReturnError(dOps->second.Ops->Ioctl, -ENOTSUP);
|
||||
return dOps->second.Ops->Ioctl(Node, Request, Argp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__no_sanitize("alignment")
|
||||
ssize_t __fs_Readdir(struct Inode *_Node, struct kdirent *Buffer, size_t Size, off_t Offset, off_t Entries)
|
||||
{
|
||||
func("%#lx %#lx %d %d %d", _Node, Buffer, Size, Offset, Entries);
|
||||
|
||||
auto Node = (Manager::DeviceInode *)_Node;
|
||||
|
||||
off_t realOffset = Offset;
|
||||
|
||||
size_t totalSize = 0;
|
||||
uint16_t reclen = 0;
|
||||
struct kdirent *ent = nullptr;
|
||||
|
||||
if (Offset == 0)
|
||||
{
|
||||
reclen = (uint16_t)(offsetof(struct kdirent, d_name) + strlen(".") + 1);
|
||||
if (totalSize + reclen >= Size)
|
||||
return -EINVAL;
|
||||
|
||||
ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
|
||||
ent->d_ino = Node->Node.Index;
|
||||
ent->d_off = Offset++;
|
||||
ent->d_reclen = reclen;
|
||||
ent->d_type = DT_DIR;
|
||||
strcpy(ent->d_name, ".");
|
||||
totalSize += reclen;
|
||||
}
|
||||
|
||||
if (Offset <= 1)
|
||||
{
|
||||
reclen = (uint16_t)(offsetof(struct kdirent, d_name) + strlen("..") + 1);
|
||||
if (totalSize + reclen >= Size)
|
||||
{
|
||||
if (realOffset == 1)
|
||||
return -EINVAL;
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
|
||||
|
||||
if (Node->Parent)
|
||||
ent->d_ino = Node->Parent->Node->Index;
|
||||
else if (Node->ParentInode)
|
||||
ent->d_ino = Node->ParentInode->Index;
|
||||
else
|
||||
{
|
||||
warn("Parent is null for %s", Node->Name.c_str());
|
||||
ent->d_ino = Node->Node.Index;
|
||||
}
|
||||
ent->d_off = Offset++;
|
||||
ent->d_reclen = reclen;
|
||||
ent->d_type = DT_DIR;
|
||||
strcpy(ent->d_name, "..");
|
||||
totalSize += reclen;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(Node->Node.Mode))
|
||||
return -ENOTDIR;
|
||||
|
||||
if ((Offset >= 2 ? (Offset - 2) : Offset) > (off_t)Node->Children.size())
|
||||
return -EINVAL;
|
||||
|
||||
off_t entries = 0;
|
||||
for (const auto &var : Node->Children)
|
||||
{
|
||||
debug("iterating \"%s\" inside \"%s\"", var->Name.c_str(), Node->Name.c_str());
|
||||
if (var->Node.Offset < realOffset)
|
||||
{
|
||||
debug("skipping \"%s\" (%d < %d)", var->Name.c_str(), var->Node.Offset, Offset);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entries >= Entries)
|
||||
break;
|
||||
|
||||
reclen = (uint16_t)(offsetof(struct kdirent, d_name) + strlen(var->Name.c_str()) + 1);
|
||||
|
||||
if (totalSize + reclen >= Size)
|
||||
break;
|
||||
|
||||
ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
|
||||
ent->d_ino = var->Node.Index;
|
||||
ent->d_off = var->Node.Offset;
|
||||
ent->d_reclen = reclen;
|
||||
ent->d_type = IFTODT(var->Node.Mode);
|
||||
strncpy(ent->d_name, var->Name.c_str(), strlen(var->Name.c_str()));
|
||||
|
||||
totalSize += reclen;
|
||||
entries++;
|
||||
}
|
||||
|
||||
if (totalSize + sizeof(struct kdirent) >= Size)
|
||||
return totalSize;
|
||||
|
||||
ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
|
||||
ent->d_ino = 0;
|
||||
ent->d_off = 0;
|
||||
ent->d_reclen = 0;
|
||||
ent->d_type = DT_UNKNOWN;
|
||||
ent->d_name[0] = '\0';
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
void ManagerDaemonWrapper() { DriverManager->Daemon(); }
|
||||
|
||||
void Manager::Daemon()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
TaskManager->Sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
dev_t Manager::RegisterInputDevice(std::unordered_map<dev_t, DriverHandlers> *dop,
|
||||
dev_t DriverID, size_t i, const InodeOperations *Operations)
|
||||
{
|
||||
std::string prefix = "event";
|
||||
for (size_t j = 0; j < 128; j++)
|
||||
{
|
||||
std::string deviceName = prefix + std::to_string(j);
|
||||
FileNode *node = fs->GetByPath(deviceName.c_str(), devInputNode);
|
||||
if (node)
|
||||
continue;
|
||||
|
||||
/* c rwx r-- r-- */
|
||||
mode_t mode = S_IRWXU |
|
||||
S_IRGRP |
|
||||
S_IROTH |
|
||||
S_IFCHR;
|
||||
|
||||
node = fs->ForceCreate(devInputNode, deviceName.c_str(), mode);
|
||||
node->Node->SetDevice(DriverID, i);
|
||||
|
||||
DriverHandlers dh{};
|
||||
dh.Ops = Operations;
|
||||
dh.Node = node->Node;
|
||||
dh.InputReports = new RingBuffer<InputReport>(16);
|
||||
dop->insert({i, std::move(dh)});
|
||||
return i;
|
||||
}
|
||||
|
||||
ReturnLogError(-1, "No available slots for device %d", DriverID);
|
||||
return -1; /* -Werror=return-type */
|
||||
}
|
||||
|
||||
dev_t Manager::RegisterBlockDevice(std::unordered_map<dev_t, DriverHandlers> *dop,
|
||||
dev_t DriverID, size_t i, const InodeOperations *Operations)
|
||||
{
|
||||
std::string prefix = "event";
|
||||
for (size_t j = 0; j < 128; j++)
|
||||
{
|
||||
std::string deviceName = prefix + std::to_string(j);
|
||||
FileNode *node = fs->GetByPath(deviceName.c_str(), devInputNode);
|
||||
if (node)
|
||||
continue;
|
||||
|
||||
/* c rwx r-- r-- */
|
||||
mode_t mode = S_IRWXU |
|
||||
S_IRGRP |
|
||||
S_IROTH |
|
||||
S_IFBLK;
|
||||
|
||||
node = fs->ForceCreate(devInputNode, deviceName.c_str(), mode);
|
||||
node->Node->SetDevice(DriverID, i);
|
||||
|
||||
DriverHandlers dh{};
|
||||
dh.Ops = Operations;
|
||||
dh.Node = node->Node;
|
||||
dh.InputReports = new RingBuffer<InputReport>(16);
|
||||
dop->insert({i, std::move(dh)});
|
||||
return i;
|
||||
}
|
||||
|
||||
ReturnLogError(-1, "No available slots for device %d", DriverID);
|
||||
return -1; /* -Werror=return-type */
|
||||
}
|
||||
|
||||
dev_t Manager::RegisterDevice(dev_t DriverID, DeviceType Type, const InodeOperations *Operations)
|
||||
{
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(DriverID);
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
auto dop = drv->DeviceOperations;
|
||||
for (size_t i = 0; i < 128; i++)
|
||||
{
|
||||
const auto dOps = dop->find(i);
|
||||
const auto dOpsEnd = dop->end();
|
||||
if (dOps != dOpsEnd)
|
||||
continue;
|
||||
|
||||
DeviceType devType = (DeviceType)(Type & DEVICE_TYPE_MASK);
|
||||
switch (devType)
|
||||
{
|
||||
case DEVICE_TYPE_INPUT:
|
||||
return RegisterInputDevice(dop, DriverID, i, Operations);
|
||||
case DEVICE_TYPE_BLOCK:
|
||||
return RegisterBlockDevice(dop, DriverID, i, Operations);
|
||||
default:
|
||||
ReturnLogError(-1, "Invalid device type %d", Type);
|
||||
}
|
||||
}
|
||||
|
||||
ReturnLogError(-1, "No available slots for device %d", DriverID);
|
||||
}
|
||||
|
||||
int Manager::UnregisterDevice(dev_t DriverID, dev_t Device)
|
||||
{
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(DriverID);
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
auto dop = drv->DeviceOperations;
|
||||
const auto dOps = dop->find(Device);
|
||||
if (dOps == dop->end())
|
||||
ReturnLogError(-EINVAL, "Device %d not found", Device);
|
||||
dop->erase(dOps);
|
||||
fixme("remove eventX from /dev/input");
|
||||
fixme("delete InputReports");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Manager::ReportInputEvent(dev_t DriverID, InputReport *Report)
|
||||
{
|
||||
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
|
||||
DriverManager->GetDrivers();
|
||||
const auto it = drivers.find(DriverID);
|
||||
if (it == drivers.end())
|
||||
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
|
||||
const Driver::DriverObject *drv = &it->second;
|
||||
|
||||
auto dop = drv->DeviceOperations;
|
||||
auto dOps = dop->find(Report->Device);
|
||||
if (dOps == dop->end())
|
||||
ReturnLogError(-EINVAL, "Device %d not found", Report->Device);
|
||||
|
||||
dOps->second.InputReports->Write(Report, 1);
|
||||
|
||||
switch (Report->Type)
|
||||
{
|
||||
case INPUT_TYPE_KEYBOARD:
|
||||
{
|
||||
KeyboardReport *kReport = &Report->Keyboard;
|
||||
GlobalKeyboardInputReports.Write(kReport, 1);
|
||||
break;
|
||||
}
|
||||
case INPUT_TYPE_MOUSE:
|
||||
{
|
||||
MouseReport *mReport = &Report->Mouse;
|
||||
GlobalMouseInputReports.Write(mReport, 1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(!"Invalid input type");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Manager::InitializeDaemonFS()
|
||||
{
|
||||
ptmx = new TTY::PTMXDevice;
|
||||
|
||||
dev_t MinorID = 0;
|
||||
DeviceInode *_dev = new DeviceInode;
|
||||
_dev->Name = "dev";
|
||||
|
||||
/* d rwx r-- r-- */
|
||||
mode_t mode = S_IRWXU |
|
||||
S_IRGRP |
|
||||
S_IROTH |
|
||||
S_IFDIR;
|
||||
Inode *dev = (Inode *)_dev;
|
||||
dev->Mode = mode;
|
||||
dev->Flags = I_FLAG_MOUNTPOINT | I_FLAG_CACHE_KEEP;
|
||||
|
||||
FileSystemInfo *fsi = new FileSystemInfo;
|
||||
fsi->Name = "Device Virtual File System";
|
||||
fsi->RootName = "dev";
|
||||
fsi->Flags = I_FLAG_ROOT | I_FLAG_MOUNTPOINT | I_FLAG_CACHE_KEEP;
|
||||
fsi->SuperOps = {};
|
||||
fsi->Ops.Lookup = __fs_Lookup;
|
||||
fsi->Ops.Create = __fs_Create;
|
||||
fsi->Ops.Read = __fs_Read;
|
||||
fsi->Ops.Write = __fs_Write;
|
||||
fsi->Ops.Open = __fs_Open;
|
||||
fsi->Ops.Close = __fs_Close;
|
||||
fsi->Ops.Ioctl = __fs_Ioctl;
|
||||
fsi->Ops.ReadDir = __fs_Readdir;
|
||||
|
||||
dev->Device = fs->RegisterFileSystem(fsi, dev);
|
||||
dev->SetDevice(0, MinorID++);
|
||||
MinorID++; /* 1 = /proc/self */
|
||||
|
||||
devNode = fs->Mount(fs->GetRoot(0), dev, "/dev");
|
||||
_dev->Parent = devNode->Parent;
|
||||
_dev->ParentInode = devNode->Parent->Node;
|
||||
|
||||
/* d rwx r-- r-- */
|
||||
mode = S_IRWXU |
|
||||
S_IRGRP |
|
||||
S_IROTH |
|
||||
S_IFDIR;
|
||||
DeviceInode *input = new DeviceInode;
|
||||
input->Parent = devNode;
|
||||
input->ParentInode = devNode->Node;
|
||||
input->Name = "input";
|
||||
input->Node.Device = dev->Device;
|
||||
input->Node.Mode = mode;
|
||||
input->Node.Flags = I_FLAG_CACHE_KEEP;
|
||||
input->Node.Offset = _dev->Children.size();
|
||||
_dev->Children.push_back(input);
|
||||
devInputNode = fs->GetByPath("input", devNode);
|
||||
|
||||
auto createDevice = [](DeviceInode *p1, FileNode *p2, const std::string &name, dev_t maj, dev_t min, mode_t mode)
|
||||
{
|
||||
DeviceInode *device = new DeviceInode;
|
||||
device->Parent = p2;
|
||||
device->ParentInode = p2->Node;
|
||||
device->Name = name;
|
||||
device->Node.Device = p2->Node->Device;
|
||||
device->Node.Mode = mode;
|
||||
device->Node.SetDevice(maj, min);
|
||||
device->Node.Flags = I_FLAG_CACHE_KEEP;
|
||||
device->Node.Offset = p1->Children.size();
|
||||
p1->Children.push_back(device);
|
||||
};
|
||||
|
||||
/* c rw- rw- rw- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP | S_IWGRP |
|
||||
S_IROTH | S_IWOTH |
|
||||
S_IFCHR;
|
||||
createDevice(_dev, devNode, "null", 0, MinorID++, mode);
|
||||
|
||||
/* c rw- rw- rw- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP | S_IWGRP |
|
||||
S_IROTH | S_IWOTH |
|
||||
S_IFCHR;
|
||||
createDevice(_dev, devNode, "zero", 0, MinorID++, mode);
|
||||
|
||||
/* c rw- rw- rw- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP | S_IWGRP |
|
||||
S_IROTH | S_IWOTH |
|
||||
S_IFCHR;
|
||||
createDevice(_dev, devNode, "random", 0, MinorID++, mode);
|
||||
|
||||
/* c rw- r-- --- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP |
|
||||
|
||||
S_IFCHR;
|
||||
createDevice(_dev, devNode, "mem", 0, MinorID++, mode);
|
||||
|
||||
/* c rw- r-- --- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP |
|
||||
|
||||
S_IFCHR;
|
||||
createDevice(_dev, devNode, "kcon", 0, MinorID++, mode);
|
||||
|
||||
/* c rw- rw- rw- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP | S_IWGRP |
|
||||
S_IRUSR | S_IWUSR |
|
||||
S_IFCHR;
|
||||
createDevice(_dev, devNode, "tty", 0, MinorID++, mode);
|
||||
|
||||
/* c rw- rw- rw- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP | S_IWGRP |
|
||||
S_IRUSR | S_IWUSR |
|
||||
S_IFCHR;
|
||||
createDevice(_dev, devNode, "ptmx", 0, MinorID++, mode);
|
||||
|
||||
/* ------------------------------------------------------ */
|
||||
|
||||
MinorID = 0;
|
||||
|
||||
/* c rw- r-- --- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP |
|
||||
|
||||
S_IFCHR;
|
||||
createDevice(input, devInputNode, "keyboard", 1, MinorID++, mode);
|
||||
|
||||
/* c rw- r-- --- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP |
|
||||
|
||||
S_IFCHR;
|
||||
createDevice(input, devInputNode, "mouse", 1, MinorID++, mode);
|
||||
}
|
||||
}
|
524
Kernel/core/driver/driver.cpp
Normal file
524
Kernel/core/driver/driver.cpp
Normal file
@ -0,0 +1,524 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <driver.hpp>
|
||||
|
||||
#include <interface/driver.h>
|
||||
#include <interface/input.h>
|
||||
#include <memory.hpp>
|
||||
#include <ints.hpp>
|
||||
#include <task.hpp>
|
||||
#include <printf.h>
|
||||
#include <exec.hpp>
|
||||
#include <rand.hpp>
|
||||
#include <cwalk.h>
|
||||
#include <md5.h>
|
||||
|
||||
#include "../../kernel.h"
|
||||
|
||||
using namespace vfs;
|
||||
|
||||
namespace Driver
|
||||
{
|
||||
void Manager::PreloadDrivers()
|
||||
{
|
||||
debug("Initializing driver manager");
|
||||
const char *DriverDirectory = Config.DriverDirectory;
|
||||
FileNode *drvDirNode = fs->GetByPath(DriverDirectory, nullptr);
|
||||
if (!drvDirNode)
|
||||
{
|
||||
error("Failed to open driver directory %s", DriverDirectory);
|
||||
KPrint("Failed to open driver directory %s", DriverDirectory);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (const auto &drvNode in drvDirNode->Children)
|
||||
{
|
||||
debug("Checking driver %s", drvNode->Path.c_str());
|
||||
if (!drvNode->IsRegularFile())
|
||||
continue;
|
||||
|
||||
if (Execute::GetBinaryType(drvNode->Path) != Execute::BinTypeELF)
|
||||
{
|
||||
error("Driver %s is not an ELF binary", drvNode->Path.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
DriverObject drvObj = {.BaseAddress = 0,
|
||||
.EntryPoint = 0,
|
||||
.vma = new Memory::VirtualMemoryArea(thisProcess->PageTable),
|
||||
.Path = drvNode->Path,
|
||||
.InterruptHandlers = new std::unordered_map<uint8_t, void *>(),
|
||||
.DeviceOperations = new std::unordered_map<dev_t, DriverHandlers>(),
|
||||
.ID = DriverIDCounter};
|
||||
|
||||
int err = this->LoadDriverFile(drvObj, drvNode);
|
||||
debug("err = %d (%s)", err, strerror(err));
|
||||
if (err != 0)
|
||||
{
|
||||
error("Failed to load driver %s: %s",
|
||||
drvNode->Path.c_str(), strerror(err));
|
||||
|
||||
delete drvObj.vma;
|
||||
delete drvObj.InterruptHandlers;
|
||||
delete drvObj.DeviceOperations;
|
||||
continue;
|
||||
}
|
||||
|
||||
debug("gdb: \"0x%lX\" %s", drvObj.BaseAddress, drvObj.Name);
|
||||
|
||||
Drivers.insert({DriverIDCounter++, drvObj});
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::LoadAllDrivers()
|
||||
{
|
||||
if (Drivers.empty())
|
||||
{
|
||||
KPrint("\x1b[1;31;41mNo drivers to load");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (auto &var in Drivers)
|
||||
{
|
||||
DriverObject &Drv = var.second;
|
||||
|
||||
debug("Calling driver %s at %#lx", Drv.Path.c_str(), Drv.EntryPoint);
|
||||
int (*DrvInit)(dev_t) = (int (*)(dev_t))Drv.EntryPoint;
|
||||
Drv.ErrorCode = DrvInit(Drv.ID);
|
||||
if (Drv.ErrorCode < 0)
|
||||
{
|
||||
KPrint("FATAL: _start() failed for %s: %s",
|
||||
Drv.Name, strerror(Drv.ErrorCode));
|
||||
error("Failed to load driver %s: %s",
|
||||
Drv.Path.c_str(), strerror(Drv.ErrorCode));
|
||||
|
||||
Drv.vma->FreeAllPages();
|
||||
continue;
|
||||
}
|
||||
|
||||
KPrint("Loading driver %s", Drv.Name);
|
||||
|
||||
debug("Calling Probe()=%#lx on driver %s",
|
||||
Drv.Probe, Drv.Path.c_str());
|
||||
Drv.ErrorCode = Drv.Probe();
|
||||
if (Drv.ErrorCode < 0)
|
||||
{
|
||||
KPrint("Probe() failed for %s: %s",
|
||||
Drv.Name, strerror(Drv.ErrorCode));
|
||||
error("Failed to probe driver %s: %s",
|
||||
Drv.Path.c_str(), strerror(Drv.ErrorCode));
|
||||
|
||||
Drv.vma->FreeAllPages();
|
||||
continue;
|
||||
}
|
||||
|
||||
debug("Calling driver Entry()=%#lx function on driver %s",
|
||||
Drv.Entry, Drv.Path.c_str());
|
||||
Drv.ErrorCode = Drv.Entry();
|
||||
if (Drv.ErrorCode < 0)
|
||||
{
|
||||
KPrint("Entry() failed for %s: %s",
|
||||
Drv.Name, strerror(Drv.ErrorCode));
|
||||
error("Failed to initialize driver %s: %s",
|
||||
Drv.Path.c_str(), strerror(Drv.ErrorCode));
|
||||
|
||||
Drv.vma->FreeAllPages();
|
||||
continue;
|
||||
}
|
||||
|
||||
debug("Loaded driver %s", Drv.Path.c_str());
|
||||
Drv.Initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::UnloadAllDrivers()
|
||||
{
|
||||
foreach (auto &var in Drivers)
|
||||
{
|
||||
DriverObject *Drv = &var.second;
|
||||
if (!Drv->Initialized)
|
||||
continue;
|
||||
|
||||
debug("Unloading driver %s", Drv->Name);
|
||||
int err = Drv->Final();
|
||||
if (err < 0)
|
||||
{
|
||||
warn("Failed to unload driver %s: %s",
|
||||
Drv->Name, strerror(err));
|
||||
}
|
||||
|
||||
if (!Drv->InterruptHandlers->empty())
|
||||
{
|
||||
foreach (auto &rInt in * Drv->InterruptHandlers)
|
||||
{
|
||||
Interrupts::RemoveHandler((void (*)(CPU::TrapFrame *))rInt.second);
|
||||
}
|
||||
Drv->InterruptHandlers->clear();
|
||||
}
|
||||
}
|
||||
Drivers.clear();
|
||||
}
|
||||
|
||||
void Manager::Panic()
|
||||
{
|
||||
Memory::Virtual vmm;
|
||||
if (Drivers.size() == 0)
|
||||
return;
|
||||
|
||||
foreach (auto Driver in Drivers)
|
||||
{
|
||||
if (!Driver.second.Initialized)
|
||||
continue;
|
||||
|
||||
trace("Panic on driver %s", Driver.second.Name);
|
||||
debug("%#lx", Driver.second.Panic);
|
||||
|
||||
/* Crash while probing? */
|
||||
if (Driver.second.Panic && vmm.Check((void *)Driver.second.Panic))
|
||||
Driver.second.Panic();
|
||||
else
|
||||
error("No panic function for driver %s",
|
||||
Driver.second.Name);
|
||||
}
|
||||
}
|
||||
|
||||
int Manager::LoadDriverFile(DriverObject &Drv, FileNode *File)
|
||||
{
|
||||
trace("Loading driver %s in memory", File->Name.c_str());
|
||||
|
||||
Elf_Ehdr ELFHeader{};
|
||||
File->Read(&ELFHeader, sizeof(Elf_Ehdr), 0);
|
||||
|
||||
AssertReturnError(ELFHeader.e_ident[EI_CLASS] == ELFCLASS64, -ENOEXEC);
|
||||
AssertReturnError(ELFHeader.e_ident[EI_DATA] == ELFDATA2LSB, -ENOEXEC);
|
||||
AssertReturnError(ELFHeader.e_ident[EI_OSABI] == ELFOSABI_SYSV, -ENOEXEC);
|
||||
AssertReturnError(ELFHeader.e_ident[EI_ABIVERSION] == 0, -ENOEXEC);
|
||||
AssertReturnError(ELFHeader.e_type == ET_DYN, -ENOEXEC);
|
||||
AssertReturnError(ELFHeader.e_machine == EM_X86_64, -ENOEXEC);
|
||||
AssertReturnError(ELFHeader.e_version == EV_CURRENT, -ENOEXEC);
|
||||
AssertReturnError(ELFHeader.e_entry != 0x0, -ENOEXEC);
|
||||
AssertReturnError(ELFHeader.e_shstrndx != SHN_UNDEF, -ENOEXEC);
|
||||
Drv.EntryPoint = ELFHeader.e_entry;
|
||||
|
||||
size_t segSize = 0;
|
||||
Elf_Phdr phdr{};
|
||||
for (Elf_Half i = 0; i < ELFHeader.e_phnum; i++)
|
||||
{
|
||||
File->Read(&phdr, sizeof(Elf_Phdr), ELFHeader.e_phoff + (i * sizeof(Elf_Phdr)));
|
||||
if (phdr.p_type == PT_LOAD || phdr.p_type == PT_DYNAMIC)
|
||||
{
|
||||
if (segSize < phdr.p_vaddr + phdr.p_memsz)
|
||||
segSize = phdr.p_vaddr + phdr.p_memsz;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (phdr.p_type == PT_INTERP)
|
||||
{
|
||||
char interp[17];
|
||||
File->Read(interp, sizeof(interp), phdr.p_offset);
|
||||
if (strncmp(interp, "/boot/fennix.elf", sizeof(interp)) != 0)
|
||||
{
|
||||
error("Interpreter is not /boot/fennix.elf");
|
||||
return -ENOEXEC;
|
||||
}
|
||||
}
|
||||
}
|
||||
debug("segSize: %ld", segSize);
|
||||
|
||||
Drv.BaseAddress = (uintptr_t)Drv.vma->RequestPages(TO_PAGES(segSize) + 1);
|
||||
Drv.EntryPoint += Drv.BaseAddress;
|
||||
debug("Driver %s has entry point %#lx and base %#lx",
|
||||
File->Name.c_str(), Drv.EntryPoint, Drv.BaseAddress);
|
||||
|
||||
Elf64_Shdr sht_strtab{};
|
||||
Elf64_Shdr sht_symtab{};
|
||||
Elf_Shdr shstrtab{};
|
||||
Elf_Shdr shdr{};
|
||||
__DriverInfo driverInfo{};
|
||||
File->Read(&shstrtab, sizeof(Elf_Shdr), ELFHeader.e_shoff + (ELFHeader.e_shstrndx * ELFHeader.e_shentsize));
|
||||
for (Elf_Half i = 0; i < ELFHeader.e_shnum; i++)
|
||||
{
|
||||
if (i == ELFHeader.e_shstrndx)
|
||||
continue;
|
||||
|
||||
File->Read(&shdr, ELFHeader.e_shentsize, ELFHeader.e_shoff + (i * ELFHeader.e_shentsize));
|
||||
|
||||
switch (shdr.sh_type)
|
||||
{
|
||||
case SHT_PROGBITS:
|
||||
break;
|
||||
case SHT_SYMTAB:
|
||||
sht_symtab = shdr;
|
||||
continue;
|
||||
case SHT_STRTAB:
|
||||
sht_strtab = shdr;
|
||||
continue;
|
||||
case SHT_NULL:
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
char symName[16];
|
||||
File->Read(symName, sizeof(symName), shstrtab.sh_offset + shdr.sh_name);
|
||||
if (strcmp(symName, ".driver.info") != 0)
|
||||
continue;
|
||||
|
||||
File->Read(&driverInfo, sizeof(__DriverInfo), shdr.sh_offset);
|
||||
|
||||
/* Perform relocations */
|
||||
driverInfo.Name = (const char *)(Drv.BaseAddress + (uintptr_t)driverInfo.Name);
|
||||
driverInfo.Description = (const char *)(Drv.BaseAddress + (uintptr_t)driverInfo.Description);
|
||||
driverInfo.Author = (const char *)(Drv.BaseAddress + (uintptr_t)driverInfo.Author);
|
||||
driverInfo.License = (const char *)(Drv.BaseAddress + (uintptr_t)driverInfo.License);
|
||||
}
|
||||
|
||||
for (size_t h = 0; h < (sht_symtab.sh_size / sizeof(Elf64_Sym)); h++)
|
||||
{
|
||||
Elf64_Sym symEntry{};
|
||||
uintptr_t symOffset = sht_symtab.sh_offset + (h * sizeof(Elf64_Sym));
|
||||
File->Read(&symEntry, sizeof(Elf64_Sym), symOffset);
|
||||
|
||||
if (symEntry.st_name == 0)
|
||||
continue;
|
||||
|
||||
char symName[16];
|
||||
File->Read(symName, sizeof(symName), sht_strtab.sh_offset + symEntry.st_name);
|
||||
|
||||
switch (symEntry.st_shndx)
|
||||
{
|
||||
case SHN_UNDEF:
|
||||
case SHN_ABS:
|
||||
case SHN_LOPROC /* , SHN_LORESERVE and SHN_BEFORE */:
|
||||
case SHN_AFTER:
|
||||
case SHN_HIPROC:
|
||||
case SHN_COMMON:
|
||||
case SHN_HIRESERVE:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
debug("shndx: %d", symEntry.st_shndx);
|
||||
if (strcmp(symName, "DriverEntry") == 0)
|
||||
Drv.Entry = (int (*)())(Drv.BaseAddress + symEntry.st_value);
|
||||
else if (strcmp(symName, "DriverFinal") == 0)
|
||||
Drv.Final = (int (*)())(Drv.BaseAddress + symEntry.st_value);
|
||||
else if (strcmp(symName, "DriverPanic") == 0)
|
||||
Drv.Panic = (int (*)())(Drv.BaseAddress + symEntry.st_value);
|
||||
else if (strcmp(symName, "DriverProbe") == 0)
|
||||
Drv.Probe = (int (*)())(Drv.BaseAddress + symEntry.st_value);
|
||||
|
||||
debug("Found %s at %#lx", symName, symEntry.st_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Elf_Half i = 0; i < ELFHeader.e_phnum; i++)
|
||||
{
|
||||
File->Read(&phdr, sizeof(Elf_Phdr), ELFHeader.e_phoff + (i * sizeof(Elf_Phdr)));
|
||||
|
||||
switch (phdr.p_type)
|
||||
{
|
||||
case PT_LOAD:
|
||||
case PT_DYNAMIC:
|
||||
{
|
||||
if (phdr.p_memsz == 0)
|
||||
continue;
|
||||
|
||||
uintptr_t dest = Drv.BaseAddress + phdr.p_vaddr;
|
||||
debug("Copying PHDR %#lx to %#lx-%#lx (%ld file bytes, %ld mem bytes)",
|
||||
phdr.p_type, dest, dest + phdr.p_memsz,
|
||||
phdr.p_filesz, phdr.p_memsz);
|
||||
|
||||
if (phdr.p_filesz > 0)
|
||||
File->Read(dest, phdr.p_filesz, phdr.p_offset);
|
||||
|
||||
if (phdr.p_memsz - phdr.p_filesz > 0)
|
||||
{
|
||||
void *zero = (void *)(dest + phdr.p_filesz);
|
||||
memset(zero, 0, phdr.p_memsz - phdr.p_filesz);
|
||||
}
|
||||
|
||||
if (phdr.p_type != PT_DYNAMIC)
|
||||
break;
|
||||
|
||||
Elf64_Dyn *dyn = (Elf64_Dyn *)(Drv.BaseAddress + phdr.p_vaddr);
|
||||
Elf64_Dyn *relaSize = nullptr;
|
||||
Elf64_Dyn *pltrelSize = nullptr;
|
||||
|
||||
while (dyn->d_tag != DT_NULL)
|
||||
{
|
||||
switch (dyn->d_tag)
|
||||
{
|
||||
case DT_PLTRELSZ:
|
||||
{
|
||||
pltrelSize = dyn;
|
||||
break;
|
||||
}
|
||||
case DT_PLTGOT:
|
||||
{
|
||||
Elf_Addr *got = (Elf_Addr *)(Drv.BaseAddress + dyn->d_un.d_ptr);
|
||||
got[1] = 0;
|
||||
got[2] = 0;
|
||||
break;
|
||||
}
|
||||
case DT_RELASZ:
|
||||
{
|
||||
relaSize = dyn;
|
||||
break;
|
||||
}
|
||||
case DT_PLTREL:
|
||||
{
|
||||
AssertReturnError(dyn->d_un.d_val == DT_RELA, -ENOEXEC);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
dyn++;
|
||||
}
|
||||
|
||||
dyn = (Elf64_Dyn *)(Drv.BaseAddress + phdr.p_vaddr);
|
||||
while (dyn->d_tag != DT_NULL)
|
||||
{
|
||||
switch (dyn->d_tag)
|
||||
{
|
||||
case DT_RELA: /* .rela.dyn */
|
||||
{
|
||||
AssertReturnError(relaSize != nullptr, -ENOEXEC);
|
||||
|
||||
Elf64_Rela *rela = (Elf64_Rela *)(Drv.BaseAddress + dyn->d_un.d_ptr);
|
||||
for (size_t i = 0; i < (relaSize->d_un.d_val / sizeof(Elf64_Rela)); i++)
|
||||
{
|
||||
Elf64_Rela *r = &rela[i];
|
||||
uintptr_t *reloc = (uintptr_t *)(Drv.BaseAddress + r->r_offset);
|
||||
uintptr_t relocTarget = 0;
|
||||
|
||||
switch (ELF64_R_TYPE(r->r_info))
|
||||
{
|
||||
case R_X86_64_GLOB_DAT:
|
||||
case R_X86_64_JUMP_SLOT:
|
||||
{
|
||||
relocTarget = Drv.BaseAddress;
|
||||
break;
|
||||
}
|
||||
case R_X86_64_RELATIVE:
|
||||
case R_X86_64_64:
|
||||
{
|
||||
relocTarget = Drv.BaseAddress + r->r_addend;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
fixme("Unhandled relocation type: %#lx",
|
||||
ELF64_R_TYPE(r->r_info));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*reloc = relocTarget;
|
||||
|
||||
debug("Relocated %#lx to %#lx",
|
||||
r->r_offset, *reloc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DT_JMPREL: /* .rela.plt */
|
||||
{
|
||||
AssertReturnError(pltrelSize != nullptr, -ENOEXEC);
|
||||
|
||||
std::vector<Elf64_Dyn> symtab = Execute::ELFGetDynamicTag_x86_64(File, DT_SYMTAB);
|
||||
Elf64_Sym *symbols = (Elf64_Sym *)((uintptr_t)Drv.BaseAddress + symtab[0].d_un.d_ptr);
|
||||
|
||||
std::vector<Elf64_Dyn> StrTab = Execute::ELFGetDynamicTag_x86_64(File, DT_STRTAB);
|
||||
char *DynStr = (char *)((uintptr_t)Drv.BaseAddress + StrTab[0].d_un.d_ptr);
|
||||
|
||||
Elf64_Rela *rela = (Elf64_Rela *)(Drv.BaseAddress + dyn->d_un.d_ptr);
|
||||
for (size_t i = 0; i < (pltrelSize->d_un.d_val / sizeof(Elf64_Rela)); i++)
|
||||
{
|
||||
Elf64_Rela *r = &rela[i];
|
||||
uintptr_t *reloc = (uintptr_t *)(Drv.BaseAddress + r->r_offset);
|
||||
|
||||
switch (ELF64_R_TYPE(r->r_info))
|
||||
{
|
||||
case R_X86_64_JUMP_SLOT:
|
||||
{
|
||||
Elf64_Xword symIndex = ELF64_R_SYM(r->r_info);
|
||||
Elf64_Sym *sym = symbols + symIndex;
|
||||
|
||||
const char *symName = DynStr + sym->st_name;
|
||||
debug("Resolving symbol %s", symName);
|
||||
|
||||
*reloc = (uintptr_t)GetSymbolByName(symName, driverInfo.Version.APIVersion);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
fixme("Unhandled relocation type: %#lx",
|
||||
ELF64_R_TYPE(r->r_info));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DT_PLTGOT:
|
||||
case DT_PLTRELSZ:
|
||||
case DT_RELASZ:
|
||||
case DT_PLTREL:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
fixme("Unhandled dynamic tag: %#lx", dyn->d_tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
dyn++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PT_PHDR:
|
||||
case PT_INTERP:
|
||||
break;
|
||||
default:
|
||||
{
|
||||
fixme("Unhandled program header type: %#lx", phdr.p_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AssertReturnError(driverInfo.Name != nullptr, -EFAULT);
|
||||
strncpy(Drv.Name, driverInfo.Name, sizeof(Drv.Name));
|
||||
strncpy(Drv.Description, driverInfo.Description, sizeof(Drv.Description));
|
||||
strncpy(Drv.Author, driverInfo.Author, sizeof(Drv.Author));
|
||||
Drv.Version.Major = driverInfo.Version.Major;
|
||||
Drv.Version.Minor = driverInfo.Version.Minor;
|
||||
Drv.Version.Patch = driverInfo.Version.Patch;
|
||||
strncpy(Drv.License, driverInfo.License, sizeof(Drv.License));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Manager::Manager() { this->InitializeDaemonFS(); }
|
||||
|
||||
Manager::~Manager()
|
||||
{
|
||||
debug("Unloading drivers");
|
||||
UnloadAllDrivers();
|
||||
}
|
||||
}
|
368
Kernel/core/driver/scancode.cpp
Normal file
368
Kernel/core/driver/scancode.cpp
Normal file
@ -0,0 +1,368 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <interface/driver.h>
|
||||
#include <driver.hpp>
|
||||
|
||||
static char ScanCodeConversionTableLower[] = {
|
||||
[KEY_1] = '1',
|
||||
[KEY_2] = '2',
|
||||
[KEY_3] = '3',
|
||||
[KEY_4] = '4',
|
||||
[KEY_5] = '5',
|
||||
[KEY_6] = '6',
|
||||
[KEY_7] = '7',
|
||||
[KEY_8] = '8',
|
||||
[KEY_9] = '9',
|
||||
[KEY_0] = '0',
|
||||
|
||||
[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_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_Z] = 'z',
|
||||
[KEY_X] = 'x',
|
||||
[KEY_C] = 'c',
|
||||
[KEY_V] = 'v',
|
||||
[KEY_B] = 'b',
|
||||
[KEY_N] = 'n',
|
||||
[KEY_M] = 'm',
|
||||
|
||||
[KEY_F1] = 0x00,
|
||||
[KEY_F2] = 0x00,
|
||||
[KEY_F3] = 0x00,
|
||||
[KEY_F4] = 0x00,
|
||||
[KEY_F5] = 0x00,
|
||||
[KEY_F6] = 0x00,
|
||||
[KEY_F7] = 0x00,
|
||||
[KEY_F8] = 0x00,
|
||||
[KEY_F9] = 0x00,
|
||||
[KEY_F10] = 0x00,
|
||||
[KEY_F11] = 0x00,
|
||||
[KEY_F12] = 0x00,
|
||||
|
||||
[KEYPAD_7] = '7',
|
||||
[KEYPAD_8] = '8',
|
||||
[KEYPAD_9] = '9',
|
||||
[KEYPAD_MINUS] = '-',
|
||||
[KEYPAD_4] = '4',
|
||||
[KEYPAD_5] = '5',
|
||||
[KEYPAD_6] = '6',
|
||||
[KEYPAD_PLUS] = '+',
|
||||
[KEYPAD_1] = '1',
|
||||
[KEYPAD_2] = '2',
|
||||
[KEYPAD_3] = '3',
|
||||
[KEYPAD_0] = '0',
|
||||
[KEYPAD_PERIOD] = '.',
|
||||
[KEYPAD_RETURN] = '\n',
|
||||
[KEYPAD_ASTERISK] = '*',
|
||||
[KEYPAD_SLASH] = '/',
|
||||
|
||||
[KEY_LEFT_CTRL] = 0x00,
|
||||
[KEY_RIGHT_CTRL] = 0x00,
|
||||
[KEY_LEFT_SHIFT] = 0x00,
|
||||
[KEY_RIGHT_SHIFT] = 0x00,
|
||||
[KEY_LEFT_ALT] = 0x00,
|
||||
[KEY_RIGHT_ALT] = 0x00,
|
||||
[KEY_ESCAPE] = '\e',
|
||||
[KEY_MINUS] = '-',
|
||||
[KEY_EQUAL] = '=',
|
||||
[KEY_BACKSPACE] = '\b',
|
||||
[KEY_TAB] = '\t',
|
||||
[KEY_LEFT_BRACKET] = '[',
|
||||
[KEY_RIGHT_BRACKET] = ']',
|
||||
[KEY_RETURN] = '\n',
|
||||
[KEY_SEMICOLON] = ';',
|
||||
[KEY_APOSTROPHE] = '\'',
|
||||
[KEY_BACK_TICK] = '`',
|
||||
[KEY_BACKSLASH] = '\\',
|
||||
[KEY_COMMA] = ',',
|
||||
[KEY_PERIOD] = '.',
|
||||
[KEY_SLASH] = '/',
|
||||
[KEY_SPACE] = ' ',
|
||||
[KEY_CAPS_LOCK] = 0x00,
|
||||
[KEY_NUM_LOCK] = 0x00,
|
||||
[KEY_SCROLL_LOCK] = 0x00,
|
||||
[KEY_PRINT_SCREEN] = 0x00,
|
||||
|
||||
[KEY_HOME] = 0x00,
|
||||
[KEY_UP_ARROW] = 0x00,
|
||||
[KEY_LEFT_ARROW] = 0x00,
|
||||
[KEY_RIGHT_ARROW] = 0x00,
|
||||
[KEY_DOWN_ARROW] = 0x00,
|
||||
[KEY_PAGE_UP] = 0x00,
|
||||
[KEY_PAGE_DOWN] = 0x00,
|
||||
[KEY_END] = 0x00,
|
||||
[KEY_INSERT] = 0x00,
|
||||
[KEY_DELETE] = 0x00,
|
||||
[KEY_LEFT_GUI] = 0x00,
|
||||
[KEY_RIGHT_GUI] = 0x00,
|
||||
[KEY_APPS] = 0x00,
|
||||
|
||||
[KEY_MULTIMEDIA_PREV_TRACK] = 0x00,
|
||||
[KEY_MULTIMEDIA_NEXT_TRACK] = 0x00,
|
||||
[KEY_MULTIMEDIA_MUTE] = 0x00,
|
||||
[KEY_MULTIMEDIA_CALCULATOR] = 0x00,
|
||||
[KEY_MULTIMEDIA_PLAY] = 0x00,
|
||||
[KEY_MULTIMEDIA_STOP] = 0x00,
|
||||
[KEY_MULTIMEDIA_VOL_DOWN] = 0x00,
|
||||
[KEY_MULTIMEDIA_VOL_UP] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_HOME] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_SEARCH] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_FAVORITES] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_REFRESH] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_STOP] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_FORWARD] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_BACK] = 0x00,
|
||||
[KEY_MULTIMEDIA_MY_COMPUTER] = 0x00,
|
||||
[KEY_MULTIMEDIA_EMAIL] = 0x00,
|
||||
[KEY_MULTIMEDIA_MEDIA_SELECT] = 0x00,
|
||||
|
||||
[KEY_ACPI_POWER] = 0x00,
|
||||
[KEY_ACPI_SLEEP] = 0x00,
|
||||
[KEY_ACPI_WAKE] = 0x00};
|
||||
|
||||
static char ScanCodeConversionTableUpper[] = {
|
||||
[KEY_1] = '!',
|
||||
[KEY_2] = '@',
|
||||
[KEY_3] = '#',
|
||||
[KEY_4] = '$',
|
||||
[KEY_5] = '%',
|
||||
[KEY_6] = '^',
|
||||
[KEY_7] = '&',
|
||||
[KEY_8] = '*',
|
||||
[KEY_9] = '(',
|
||||
[KEY_0] = ')',
|
||||
|
||||
[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_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_Z] = 'Z',
|
||||
[KEY_X] = 'X',
|
||||
[KEY_C] = 'C',
|
||||
[KEY_V] = 'V',
|
||||
[KEY_B] = 'B',
|
||||
[KEY_N] = 'N',
|
||||
[KEY_M] = 'M',
|
||||
|
||||
[KEY_F1] = 0x00,
|
||||
[KEY_F2] = 0x00,
|
||||
[KEY_F3] = 0x00,
|
||||
[KEY_F4] = 0x00,
|
||||
[KEY_F5] = 0x00,
|
||||
[KEY_F6] = 0x00,
|
||||
[KEY_F7] = 0x00,
|
||||
[KEY_F8] = 0x00,
|
||||
[KEY_F9] = 0x00,
|
||||
[KEY_F10] = 0x00,
|
||||
[KEY_F11] = 0x00,
|
||||
[KEY_F12] = 0x00,
|
||||
|
||||
[KEYPAD_7] = '7',
|
||||
[KEYPAD_8] = '8',
|
||||
[KEYPAD_9] = '9',
|
||||
[KEYPAD_MINUS] = '-',
|
||||
[KEYPAD_4] = '4',
|
||||
[KEYPAD_5] = '5',
|
||||
[KEYPAD_6] = '6',
|
||||
[KEYPAD_PLUS] = '+',
|
||||
[KEYPAD_1] = '1',
|
||||
[KEYPAD_2] = '2',
|
||||
[KEYPAD_3] = '3',
|
||||
[KEYPAD_0] = '0',
|
||||
[KEYPAD_PERIOD] = '.',
|
||||
[KEYPAD_RETURN] = '\n',
|
||||
[KEYPAD_ASTERISK] = '*',
|
||||
[KEYPAD_SLASH] = '/',
|
||||
|
||||
[KEY_LEFT_CTRL] = 0x00,
|
||||
[KEY_RIGHT_CTRL] = 0x00,
|
||||
[KEY_LEFT_SHIFT] = 0x00,
|
||||
[KEY_RIGHT_SHIFT] = 0x00,
|
||||
[KEY_LEFT_ALT] = 0x00,
|
||||
[KEY_RIGHT_ALT] = 0x00,
|
||||
[KEY_ESCAPE] = '\e',
|
||||
[KEY_MINUS] = '_',
|
||||
[KEY_EQUAL] = '+',
|
||||
[KEY_BACKSPACE] = '\b',
|
||||
[KEY_TAB] = '\t',
|
||||
[KEY_LEFT_BRACKET] = '{',
|
||||
[KEY_RIGHT_BRACKET] = '}',
|
||||
[KEY_RETURN] = '\n',
|
||||
[KEY_SEMICOLON] = ':',
|
||||
[KEY_APOSTROPHE] = '\"',
|
||||
[KEY_BACK_TICK] = '~',
|
||||
[KEY_BACKSLASH] = '|',
|
||||
[KEY_COMMA] = '<',
|
||||
[KEY_PERIOD] = '>',
|
||||
[KEY_SLASH] = '?',
|
||||
[KEY_SPACE] = ' ',
|
||||
[KEY_CAPS_LOCK] = 0x00,
|
||||
[KEY_NUM_LOCK] = 0x00,
|
||||
[KEY_SCROLL_LOCK] = 0x00,
|
||||
[KEY_PRINT_SCREEN] = 0x00,
|
||||
|
||||
[KEY_HOME] = 0x00,
|
||||
[KEY_UP_ARROW] = 0x00,
|
||||
[KEY_LEFT_ARROW] = 0x00,
|
||||
[KEY_RIGHT_ARROW] = 0x00,
|
||||
[KEY_DOWN_ARROW] = 0x00,
|
||||
[KEY_PAGE_UP] = 0x00,
|
||||
[KEY_PAGE_DOWN] = 0x00,
|
||||
[KEY_END] = 0x00,
|
||||
[KEY_INSERT] = 0x00,
|
||||
[KEY_DELETE] = 0x00,
|
||||
[KEY_LEFT_GUI] = 0x00,
|
||||
[KEY_RIGHT_GUI] = 0x00,
|
||||
[KEY_APPS] = 0x00,
|
||||
|
||||
[KEY_MULTIMEDIA_PREV_TRACK] = 0x00,
|
||||
[KEY_MULTIMEDIA_NEXT_TRACK] = 0x00,
|
||||
[KEY_MULTIMEDIA_MUTE] = 0x00,
|
||||
[KEY_MULTIMEDIA_CALCULATOR] = 0x00,
|
||||
[KEY_MULTIMEDIA_PLAY] = 0x00,
|
||||
[KEY_MULTIMEDIA_STOP] = 0x00,
|
||||
[KEY_MULTIMEDIA_VOL_DOWN] = 0x00,
|
||||
[KEY_MULTIMEDIA_VOL_UP] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_HOME] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_SEARCH] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_FAVORITES] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_REFRESH] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_STOP] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_FORWARD] = 0x00,
|
||||
[KEY_MULTIMEDIA_WWW_BACK] = 0x00,
|
||||
[KEY_MULTIMEDIA_MY_COMPUTER] = 0x00,
|
||||
[KEY_MULTIMEDIA_EMAIL] = 0x00,
|
||||
[KEY_MULTIMEDIA_MEDIA_SELECT] = 0x00,
|
||||
|
||||
[KEY_ACPI_POWER] = 0x00,
|
||||
[KEY_ACPI_SLEEP] = 0x00,
|
||||
[KEY_ACPI_WAKE] = 0x00};
|
||||
|
||||
#ifdef DEBUG
|
||||
static const char *ScanCodeDebugNames[] = {
|
||||
"KEY_1", "KEY_2", "KEY_3", "KEY_4", "KEY_5", "KEY_6", "KEY_7", "KEY_8",
|
||||
"KEY_9", "KEY_0", "KEY_Q", "KEY_W", "KEY_E", "KEY_R", "KEY_T", "KEY_Y",
|
||||
"KEY_U", "KEY_I", "KEY_O", "KEY_P", "KEY_A", "KEY_S", "KEY_D", "KEY_F",
|
||||
"KEY_G", "KEY_H", "KEY_J", "KEY_K", "KEY_L", "KEY_Z", "KEY_X", "KEY_C",
|
||||
"KEY_V", "KEY_B", "KEY_N", "KEY_M", "KEY_F1", "KEY_F2", "KEY_F3", "KEY_F4",
|
||||
"KEY_F5", "KEY_F6", "KEY_F7", "KEY_F8", "KEY_F9", "KEY_F10", "KEY_F11",
|
||||
"KEY_F12", "KEYPAD_7", "KEYPAD_8", "KEYPAD_9", "KEYPAD_MINUS", "KEYPAD_4",
|
||||
"KEYPAD_5", "KEYPAD_6", "KEYPAD_PLUS", "KEYPAD_1", "KEYPAD_2", "KEYPAD_3",
|
||||
"KEYPAD_0", "KEYPAD_PERIOD", "KEYPAD_RETURN", "KEYPAD_ASTERISK", "KEYPAD_SLASH",
|
||||
"KEY_LEFT_CTRL", "KEY_RIGHT_CTRL", "KEY_LEFT_SHIFT", "KEY_RIGHT_SHIFT",
|
||||
"KEY_LEFT_ALT", "KEY_RIGHT_ALT", "KEY_ESCAPE", "KEY_MINUS", "KEY_EQUAL",
|
||||
"KEY_BACKSPACE", "KEY_TAB", "KEY_LEFT_BRACKET", "KEY_RIGHT_BRACKET",
|
||||
"KEY_RETURN", "KEY_SEMICOLON", "KEY_APOSTROPHE", "KEY_BACK_TICK",
|
||||
"KEY_BACKSLASH", "KEY_COMMA", "KEY_PERIOD", "KEY_SLASH", "KEY_SPACE",
|
||||
"KEY_CAPS_LOCK", "KEY_NUM_LOCK", "KEY_SCROLL_LOCK", "KEY_PRINT_SCREEN",
|
||||
"KEY_HOME", "KEY_UP_ARROW", "KEY_LEFT_ARROW", "KEY_RIGHT_ARROW",
|
||||
"KEY_DOWN_ARROW", "KEY_PAGE_UP", "KEY_PAGE_DOWN", "KEY_END", "KEY_INSERT",
|
||||
"KEY_DELETE", "KEY_LEFT_GUI", "KEY_RIGHT_GUI", "KEY_APPS",
|
||||
"KEY_MULTIMEDIA_PREV_TRACK", "KEY_MULTIMEDIA_NEXT_TRACK", "KEY_MULTIMEDIA_MUTE",
|
||||
"KEY_MULTIMEDIA_CALCULATOR", "KEY_MULTIMEDIA_PLAY", "KEY_MULTIMEDIA_STOP",
|
||||
"KEY_MULTIMEDIA_VOL_DOWN", "KEY_MULTIMEDIA_VOL_UP", "KEY_MULTIMEDIA_WWW_HOME",
|
||||
"KEY_MULTIMEDIA_WWW_SEARCH", "KEY_MULTIMEDIA_WWW_FAVORITES",
|
||||
"KEY_MULTIMEDIA_WWW_REFRESH", "KEY_MULTIMEDIA_WWW_STOP",
|
||||
"KEY_MULTIMEDIA_WWW_FORWARD", "KEY_MULTIMEDIA_WWW_BACK",
|
||||
"KEY_MULTIMEDIA_MY_COMPUTER", "KEY_MULTIMEDIA_EMAIL",
|
||||
"KEY_MULTIMEDIA_MEDIA_SELECT", "KEY_ACPI_POWER", "KEY_ACPI_SLEEP", "KEY_ACPI_WAKE"};
|
||||
#endif
|
||||
|
||||
namespace Driver
|
||||
{
|
||||
char GetScanCode(uint8_t ScanCode, bool Upper)
|
||||
{
|
||||
ScanCode &= 0x7F; /* Remove KEY_PRESSED bit */
|
||||
if (ScanCode >= sizeof(ScanCodeConversionTableLower))
|
||||
{
|
||||
warn("Unknown scancode %x", ScanCode);
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
// debug("Scancode %x (%s)", ScanCode, ScanCodeDebugNames[ScanCode]);
|
||||
return Upper
|
||||
? ScanCodeConversionTableUpper[ScanCode]
|
||||
: ScanCodeConversionTableLower[ScanCode];
|
||||
}
|
||||
|
||||
bool IsValidChar(uint8_t ScanCode)
|
||||
{
|
||||
ScanCode &= 0x7F; /* Remove KEY_PRESSED bit */
|
||||
if (ScanCode >= sizeof(ScanCodeConversionTableLower))
|
||||
return false;
|
||||
|
||||
if (ScanCode > KEY_M)
|
||||
{
|
||||
if (ScanCode < KEYPAD_7)
|
||||
return false; /* F1 - F12 */
|
||||
|
||||
switch (ScanCode)
|
||||
{
|
||||
case KEY_MINUS:
|
||||
case KEY_EQUAL:
|
||||
case KEY_LEFT_BRACKET:
|
||||
case KEY_RIGHT_BRACKET:
|
||||
case KEY_RETURN:
|
||||
case KEY_SEMICOLON:
|
||||
case KEY_APOSTROPHE:
|
||||
case KEY_BACK_TICK:
|
||||
case KEY_BACKSLASH:
|
||||
case KEY_COMMA:
|
||||
case KEY_PERIOD:
|
||||
case KEY_SLASH:
|
||||
case KEY_SPACE:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
334
Kernel/core/dsdt.cpp
Normal file
334
Kernel/core/dsdt.cpp
Normal file
@ -0,0 +1,334 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <acpi.hpp>
|
||||
|
||||
#include <time.hpp>
|
||||
#include <debug.h>
|
||||
#include <smp.hpp>
|
||||
#include <io.h>
|
||||
|
||||
#if defined(a64)
|
||||
#include "../arch/amd64/cpu/apic.hpp"
|
||||
#elif defined(a32)
|
||||
#include "../arch/i386/cpu/apic.hpp"
|
||||
#endif
|
||||
#include "../kernel.h"
|
||||
|
||||
#define ACPI_TIMER 0x0001
|
||||
#define ACPI_BUSMASTER 0x0010
|
||||
#define ACPI_GLOBAL 0x0020
|
||||
#define ACPI_POWER_BUTTON 0x0100
|
||||
#define ACPI_SLEEP_BUTTON 0x0200
|
||||
#define ACPI_RTC_ALARM 0x0400
|
||||
#define ACPI_PCIE_WAKE 0x4000
|
||||
#define ACPI_WAKE 0x8000
|
||||
|
||||
extern std::atomic<bool> ExceptionLock;
|
||||
|
||||
namespace ACPI
|
||||
{
|
||||
__always_inline inline bool IsCanonical(uint64_t Address)
|
||||
{
|
||||
return ((Address <= 0x00007FFFFFFFFFFF) ||
|
||||
((Address >= 0xFFFF800000000000) &&
|
||||
(Address <= 0xFFFFFFFFFFFFFFFF)));
|
||||
}
|
||||
|
||||
#define ACPI_ENABLED 0x0001
|
||||
#define ACPI_SLEEP 0x2000
|
||||
|
||||
#define ACPI_GAS_MMIO 0
|
||||
#define ACPI_GAS_IO 1
|
||||
#define ACPI_GAS_PCI 2
|
||||
|
||||
void DSDT::OnInterruptReceived(CPU::TrapFrame *)
|
||||
{
|
||||
debug("SCI Handle Triggered");
|
||||
uint16_t Event = 0;
|
||||
{
|
||||
uint16_t a = 0, b = 0;
|
||||
if (acpi->FADT->PM1aEventBlock)
|
||||
{
|
||||
a = inw(s_cst(uint16_t, acpi->FADT->PM1aEventBlock));
|
||||
outw(s_cst(uint16_t, acpi->FADT->PM1aEventBlock), a);
|
||||
}
|
||||
if (acpi->FADT->PM1bEventBlock)
|
||||
{
|
||||
b = inw(s_cst(uint16_t, acpi->FADT->PM1bEventBlock));
|
||||
outw(s_cst(uint16_t, acpi->FADT->PM1bEventBlock), b);
|
||||
}
|
||||
Event = a | b;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
char dbgEvStr[128];
|
||||
dbgEvStr[0] = '\0';
|
||||
if (Event & ACPI_BUSMASTER)
|
||||
strcat(dbgEvStr, "BUSMASTER ");
|
||||
if (Event & ACPI_GLOBAL)
|
||||
strcat(dbgEvStr, "GLOBAL ");
|
||||
if (Event & ACPI_POWER_BUTTON)
|
||||
strcat(dbgEvStr, "POWER_BUTTON ");
|
||||
if (Event & ACPI_SLEEP_BUTTON)
|
||||
strcat(dbgEvStr, "SLEEP_BUTTON ");
|
||||
if (Event & ACPI_RTC_ALARM)
|
||||
strcat(dbgEvStr, "RTC_ALARM ");
|
||||
if (Event & ACPI_PCIE_WAKE)
|
||||
strcat(dbgEvStr, "PCIE_WAKE ");
|
||||
if (Event & ACPI_WAKE)
|
||||
strcat(dbgEvStr, "WAKE ");
|
||||
if (Event & ACPI_TIMER)
|
||||
strcat(dbgEvStr, "ACPI_TIMER ");
|
||||
KPrint("SCI Event: %s", dbgEvStr);
|
||||
#endif
|
||||
if (Event & ACPI_BUSMASTER)
|
||||
{
|
||||
fixme("ACPI Busmaster");
|
||||
}
|
||||
else if (Event & ACPI_GLOBAL)
|
||||
{
|
||||
fixme("ACPI Global");
|
||||
}
|
||||
else if (Event & ACPI_POWER_BUTTON)
|
||||
{
|
||||
if (ExceptionLock.load())
|
||||
{
|
||||
this->Shutdown();
|
||||
CPU::Stop();
|
||||
}
|
||||
|
||||
Tasking::PCB *pcb = thisProcess;
|
||||
if (pcb && !pcb->GetContext()->IsPanic())
|
||||
{
|
||||
Tasking::Task *ctx = pcb->GetContext();
|
||||
ctx->CreateThread(ctx->GetKernelProcess(),
|
||||
Tasking::IP(KST_Shutdown))
|
||||
->Rename("Shutdown");
|
||||
}
|
||||
else
|
||||
KernelShutdownThread(false);
|
||||
}
|
||||
else if (Event & ACPI_SLEEP_BUTTON)
|
||||
{
|
||||
fixme("ACPI Sleep Button");
|
||||
}
|
||||
else if (Event & ACPI_RTC_ALARM)
|
||||
{
|
||||
fixme("ACPI RTC Alarm");
|
||||
}
|
||||
else if (Event & ACPI_PCIE_WAKE)
|
||||
{
|
||||
fixme("ACPI PCIe Wake");
|
||||
}
|
||||
else if (Event & ACPI_WAKE)
|
||||
{
|
||||
fixme("ACPI Wake");
|
||||
}
|
||||
else if (Event & ACPI_TIMER)
|
||||
{
|
||||
fixme("ACPI Timer");
|
||||
}
|
||||
else
|
||||
{
|
||||
error("ACPI unknown event %#lx on CPU %d",
|
||||
Event, GetCurrentCPU()->ID);
|
||||
KPrint("ACPI unknown event %#lx on CPU %d",
|
||||
Event, GetCurrentCPU()->ID);
|
||||
}
|
||||
}
|
||||
|
||||
void DSDT::Shutdown()
|
||||
{
|
||||
trace("Shutting down...");
|
||||
if (SCI_EN != 1)
|
||||
{
|
||||
error("ACPI Shutdown not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
if (inw(s_cst(uint16_t, PM1a_CNT) & SCI_EN) == 0)
|
||||
{
|
||||
KPrint("ACPI was disabled, enabling...");
|
||||
if (SMI_CMD == 0 || ACPI_ENABLE == 0)
|
||||
{
|
||||
error("ACPI Shutdown not supported");
|
||||
KPrint("ACPI Shutdown not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
outb(s_cst(uint16_t, SMI_CMD), ACPI_ENABLE);
|
||||
|
||||
uint16_t Timeout = 3000;
|
||||
while ((inw(s_cst(uint16_t, PM1a_CNT)) & SCI_EN) == 0 && Timeout-- > 0)
|
||||
;
|
||||
|
||||
if (Timeout == 0)
|
||||
{
|
||||
error("ACPI Shutdown not supported");
|
||||
KPrint("ACPI Shutdown not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
if (PM1b_CNT)
|
||||
{
|
||||
Timeout = 3000;
|
||||
while ((inw(s_cst(uint16_t, PM1b_CNT)) & SCI_EN) == 0 && Timeout-- > 0)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
outw(s_cst(uint16_t, PM1a_CNT), SLP_TYPa | SLP_EN);
|
||||
if (PM1b_CNT)
|
||||
outw(s_cst(uint16_t, PM1b_CNT), SLP_TYPb | SLP_EN);
|
||||
}
|
||||
|
||||
void DSDT::Reboot()
|
||||
{
|
||||
trace("Rebooting...");
|
||||
switch (acpi->FADT->ResetReg.AddressSpace)
|
||||
{
|
||||
case ACPI_GAS_MMIO:
|
||||
{
|
||||
*(uint8_t *)(acpi->FADT->ResetReg.Address) = acpi->FADT->ResetValue;
|
||||
break;
|
||||
}
|
||||
case ACPI_GAS_IO:
|
||||
{
|
||||
outb(s_cst(uint16_t, acpi->FADT->ResetReg.Address), acpi->FADT->ResetValue);
|
||||
break;
|
||||
}
|
||||
case ACPI_GAS_PCI:
|
||||
{
|
||||
fixme("ACPI_GAS_PCI not supported.");
|
||||
/*
|
||||
seg - 0
|
||||
bus - 0
|
||||
dev - (FADT->ResetReg.Address >> 32) & 0xFFFF
|
||||
function - (FADT->ResetReg.Address >> 16) & 0xFFFF
|
||||
offset - FADT->ResetReg.Address & 0xFFFF
|
||||
value - FADT->ResetValue
|
||||
*/
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
error("Unknown reset register address space: %d", acpi->FADT->ResetReg.AddressSpace);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DSDT::DSDT(ACPI *acpi) : Interrupts::Handler(acpi->FADT->SCI_Interrupt, true)
|
||||
{
|
||||
/* TODO: AML Interpreter */
|
||||
|
||||
this->acpi = acpi;
|
||||
uint64_t Address = ((IsCanonical(acpi->FADT->X_Dsdt) && acpi->XSDTSupported) ? acpi->FADT->X_Dsdt : acpi->FADT->Dsdt);
|
||||
uint8_t *S5Address = (uint8_t *)(Address) + 36;
|
||||
ACPI::ACPI::ACPIHeader *Header = (ACPI::ACPI::ACPIHeader *)Address;
|
||||
Memory::Virtual vmm;
|
||||
if (!vmm.Check(Header))
|
||||
{
|
||||
warn("DSDT is not mapped");
|
||||
debug("DSDT: %#lx", Address);
|
||||
vmm.Map(Header, Header, Memory::RW);
|
||||
}
|
||||
|
||||
size_t Length = Header->Length;
|
||||
vmm.Map(Header, Header, Length, Memory::RW);
|
||||
|
||||
while (Length-- > 0)
|
||||
{
|
||||
if (!memcmp(S5Address, "_S5_", 4))
|
||||
break;
|
||||
S5Address++;
|
||||
}
|
||||
|
||||
if (Length <= 0)
|
||||
{
|
||||
warn("_S5_ not present in ACPI");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((*(S5Address - 1) == 0x08 ||
|
||||
(*(S5Address - 2) == 0x08 &&
|
||||
*(S5Address - 1) == '\\')) &&
|
||||
*(S5Address + 4) == 0x12)
|
||||
{
|
||||
S5Address += 5;
|
||||
S5Address += ((*S5Address & 0xC0) >> 6) + 2;
|
||||
|
||||
if (*S5Address == 0x0A)
|
||||
S5Address++;
|
||||
|
||||
SLP_TYPa = s_cst(uint16_t, *(S5Address) << 10);
|
||||
S5Address++;
|
||||
|
||||
if (*S5Address == 0x0A)
|
||||
S5Address++;
|
||||
|
||||
SLP_TYPb = s_cst(uint16_t, *(S5Address) << 10);
|
||||
SMI_CMD = acpi->FADT->SMI_CommandPort;
|
||||
ACPI_ENABLE = acpi->FADT->AcpiEnable;
|
||||
ACPI_DISABLE = acpi->FADT->AcpiDisable;
|
||||
PM1a_CNT = acpi->FADT->PM1aControlBlock;
|
||||
PM1b_CNT = acpi->FADT->PM1bControlBlock;
|
||||
PM1_CNT_LEN = acpi->FADT->PM1ControlLength;
|
||||
SLP_EN = 1 << 13;
|
||||
SCI_EN = 1;
|
||||
KPrint("ACPI Shutdown is supported");
|
||||
ACPIShutdownSupported = true;
|
||||
|
||||
{
|
||||
const uint16_t value = /*ACPI_TIMER |*/ ACPI_BUSMASTER | ACPI_GLOBAL |
|
||||
ACPI_POWER_BUTTON | ACPI_SLEEP_BUTTON | ACPI_RTC_ALARM |
|
||||
ACPI_PCIE_WAKE | ACPI_WAKE;
|
||||
uint16_t a = s_cst(uint16_t, acpi->FADT->PM1aEventBlock + (acpi->FADT->PM1EventLength / 2));
|
||||
uint16_t b = s_cst(uint16_t, acpi->FADT->PM1bEventBlock + (acpi->FADT->PM1EventLength / 2));
|
||||
debug("SCI Event: %#x [a:%#x b:%#x]", value, a, b);
|
||||
if (acpi->FADT->PM1aEventBlock)
|
||||
outw(a, value);
|
||||
if (acpi->FADT->PM1bEventBlock)
|
||||
outw(b, value);
|
||||
}
|
||||
|
||||
{
|
||||
uint16_t a = 0, b = 0;
|
||||
if (acpi->FADT->PM1aEventBlock)
|
||||
{
|
||||
a = inw(s_cst(uint16_t, acpi->FADT->PM1aEventBlock));
|
||||
outw(s_cst(uint16_t, acpi->FADT->PM1aEventBlock), a);
|
||||
}
|
||||
if (acpi->FADT->PM1bEventBlock)
|
||||
{
|
||||
b = inw(s_cst(uint16_t, acpi->FADT->PM1bEventBlock));
|
||||
outw(s_cst(uint16_t, acpi->FADT->PM1bEventBlock), b);
|
||||
}
|
||||
}
|
||||
|
||||
((APIC::APIC *)Interrupts::apic[0])->RedirectIRQ(0, uint8_t(acpi->FADT->SCI_Interrupt), 1);
|
||||
return;
|
||||
}
|
||||
warn("Failed to parse _S5_ in ACPI");
|
||||
SCI_EN = 0;
|
||||
}
|
||||
|
||||
DSDT::~DSDT()
|
||||
{
|
||||
}
|
||||
}
|
497
Kernel/core/interrupts_manager.cpp
Normal file
497
Kernel/core/interrupts_manager.cpp
Normal file
@ -0,0 +1,497 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <ints.hpp>
|
||||
|
||||
#include <syscalls.hpp>
|
||||
#include <acpi.hpp>
|
||||
#include <smp.hpp>
|
||||
#include <vector>
|
||||
#include <io.h>
|
||||
|
||||
#if defined(a64)
|
||||
#include "../arch/amd64/cpu/apic.hpp"
|
||||
#include "../arch/amd64/cpu/gdt.hpp"
|
||||
#include "../arch/amd64/cpu/idt.hpp"
|
||||
#elif defined(a32)
|
||||
#include "../arch/i386/cpu/apic.hpp"
|
||||
#include "../arch/i386/cpu/gdt.hpp"
|
||||
#include "../arch/i386/cpu/idt.hpp"
|
||||
#elif defined(aa64)
|
||||
#endif
|
||||
|
||||
#include "../kernel.h"
|
||||
|
||||
void HandleException(CPU::ExceptionFrame *Frame);
|
||||
|
||||
extern "C" nsa void ExceptionHandler(void *Frame)
|
||||
{
|
||||
HandleException((CPU::ExceptionFrame *)Frame);
|
||||
}
|
||||
|
||||
namespace Interrupts
|
||||
{
|
||||
struct Event
|
||||
{
|
||||
/** Interrupt number */
|
||||
int IRQ;
|
||||
|
||||
/** Raw pointer to the Handler */
|
||||
void *Data;
|
||||
|
||||
/** Is this a handler? */
|
||||
bool IsHandler;
|
||||
|
||||
/**
|
||||
* Function to call if this is not a Handler
|
||||
*
|
||||
* Used for C-style callbacks.
|
||||
*/
|
||||
void (*Callback)(CPU::TrapFrame *);
|
||||
|
||||
/**
|
||||
* Context for the callback
|
||||
*
|
||||
* Used for C-style callbacks if the callback needs a context.
|
||||
* (e.g. a driver)
|
||||
*/
|
||||
void *Context;
|
||||
|
||||
/**
|
||||
* Priority of the event
|
||||
*
|
||||
* Used for sorting the events.
|
||||
*
|
||||
* This is incremented every time the event is called.
|
||||
*
|
||||
* This will improve performance by reducing the time
|
||||
* spent on searching for the event.
|
||||
*/
|
||||
unsigned long Priority;
|
||||
|
||||
/**
|
||||
* If this is true, the event is critical.
|
||||
*
|
||||
* This will make sure that the event will not be
|
||||
* removed by the kernel.
|
||||
*
|
||||
* This is used to prevent the kernel from removing
|
||||
* ACPI related handlers. (SCI interrupts)
|
||||
*/
|
||||
bool Critical;
|
||||
};
|
||||
std::list<Event> RegisteredEvents;
|
||||
|
||||
#ifdef DEBUG
|
||||
#define SORT_DIVIDER 10
|
||||
#else
|
||||
#define SORT_DIVIDER 1
|
||||
#endif
|
||||
|
||||
#define SORT_START 10000
|
||||
std::atomic_uint SortEvents = SORT_START / SORT_DIVIDER;
|
||||
constexpr uint32_t SORT_ITR = (SORT_START * 100) / SORT_DIVIDER;
|
||||
|
||||
#if defined(a86)
|
||||
/* APIC::APIC */ void *apic[MAX_CPU] = {nullptr};
|
||||
/* APIC::Timer */ void *apicTimer[MAX_CPU] = {nullptr};
|
||||
#elif defined(aa64)
|
||||
#endif
|
||||
|
||||
void Initialize(int Core)
|
||||
{
|
||||
#if defined(a64)
|
||||
GlobalDescriptorTable::Init(Core);
|
||||
InterruptDescriptorTable::Init(Core);
|
||||
CPUData *CoreData = GetCPU(Core);
|
||||
CoreData->Checksum = CPU_DATA_CHECKSUM;
|
||||
CPU::x64::wrmsr(CPU::x64::MSR_GS_BASE, (uint64_t)CoreData);
|
||||
CPU::x64::wrmsr(CPU::x64::MSR_SHADOW_GS_BASE, (uint64_t)CoreData);
|
||||
CoreData->ID = Core;
|
||||
CoreData->IsActive = true;
|
||||
CoreData->Stack = (uintptr_t)StackManager.Allocate(STACK_SIZE) + STACK_SIZE;
|
||||
if (CoreData->Checksum != CPU_DATA_CHECKSUM)
|
||||
{
|
||||
KPrint("CPU %d checksum mismatch! %x != %x",
|
||||
Core, CoreData->Checksum, CPU_DATA_CHECKSUM);
|
||||
CPU::Stop();
|
||||
}
|
||||
debug("Stack for core %d is %#lx (Address: %#lx)",
|
||||
Core, CoreData->Stack, CoreData->Stack - STACK_SIZE);
|
||||
InitializeSystemCalls();
|
||||
#elif defined(a32)
|
||||
GlobalDescriptorTable::Init(Core);
|
||||
InterruptDescriptorTable::Init(Core);
|
||||
CPUData *CoreData = GetCPU(Core);
|
||||
CoreData->Checksum = CPU_DATA_CHECKSUM;
|
||||
CPU::x32::wrmsr(CPU::x32::MSR_GS_BASE, (uint64_t)CoreData);
|
||||
CPU::x32::wrmsr(CPU::x32::MSR_SHADOW_GS_BASE, (uint64_t)CoreData);
|
||||
CoreData->ID = Core;
|
||||
CoreData->IsActive = true;
|
||||
CoreData->Stack = (uintptr_t)StackManager.Allocate(STACK_SIZE) + STACK_SIZE;
|
||||
if (CoreData->Checksum != CPU_DATA_CHECKSUM)
|
||||
{
|
||||
KPrint("CPU %d checksum mismatch! %x != %x",
|
||||
Core, CoreData->Checksum, CPU_DATA_CHECKSUM);
|
||||
CPU::Stop();
|
||||
}
|
||||
debug("Stack for core %d is %#lx (Address: %#lx)",
|
||||
Core, CoreData->Stack, CoreData->Stack - STACK_SIZE);
|
||||
#elif defined(aa64)
|
||||
warn("aarch64 is not supported yet");
|
||||
#endif
|
||||
}
|
||||
|
||||
void Enable(int Core)
|
||||
{
|
||||
#if defined(a86)
|
||||
if (((ACPI::MADT *)PowerManager->GetMADT())->LAPICAddress != nullptr)
|
||||
{
|
||||
// TODO: This function is called by SMP too. Do not initialize timers that doesn't support multiple cores.
|
||||
apic[Core] = new APIC::APIC(Core);
|
||||
if (Core == Config.IOAPICInterruptCore) // Redirect IRQs to the specified core.
|
||||
((APIC::APIC *)apic[Core])->RedirectIRQs(uint8_t(Core));
|
||||
}
|
||||
else
|
||||
{
|
||||
error("LAPIC not found");
|
||||
// TODO: PIC
|
||||
}
|
||||
#elif defined(aa64)
|
||||
warn("aarch64 is not supported yet");
|
||||
#endif
|
||||
CPU::Interrupts(CPU::Enable);
|
||||
}
|
||||
|
||||
void InitializeTimer(int Core)
|
||||
{
|
||||
// TODO: This function is called by SMP too. Do not initialize timers that doesn't support multiple cores.
|
||||
#if defined(a86)
|
||||
if (apic[Core] != nullptr)
|
||||
apicTimer[Core] = new APIC::Timer((APIC::APIC *)apic[Core]);
|
||||
else
|
||||
{
|
||||
fixme("apic not found");
|
||||
}
|
||||
#elif defined(aa64)
|
||||
warn("aarch64 is not supported yet");
|
||||
#endif
|
||||
}
|
||||
|
||||
nsa void RemoveAll()
|
||||
{
|
||||
forItr(itr, RegisteredEvents)
|
||||
{
|
||||
if (itr->Critical)
|
||||
continue;
|
||||
RegisteredEvents.erase(itr);
|
||||
}
|
||||
}
|
||||
|
||||
void AddHandler(void (*Callback)(CPU::TrapFrame *),
|
||||
int InterruptNumber,
|
||||
void *ctx, bool Critical)
|
||||
{
|
||||
/* Just log a warning if the interrupt is already registered. */
|
||||
foreach (auto ev in RegisteredEvents)
|
||||
{
|
||||
if (ev.IRQ == InterruptNumber &&
|
||||
ev.Callback == Callback)
|
||||
{
|
||||
warn("IRQ%d is already registered.",
|
||||
InterruptNumber);
|
||||
}
|
||||
}
|
||||
|
||||
Event newEvent =
|
||||
{InterruptNumber, /* IRQ */
|
||||
nullptr, /* Data */
|
||||
false, /* IsHandler */
|
||||
Callback, /* Callback */
|
||||
ctx, /* Context */
|
||||
0, /* Priority */
|
||||
Critical}; /* Critical */
|
||||
RegisteredEvents.push_back(newEvent);
|
||||
debug("Registered interrupt handler for IRQ%d to %#lx",
|
||||
InterruptNumber, Callback);
|
||||
}
|
||||
|
||||
void RemoveHandler(void (*Callback)(CPU::TrapFrame *), int InterruptNumber)
|
||||
{
|
||||
forItr(itr, RegisteredEvents)
|
||||
{
|
||||
if (itr->IRQ == InterruptNumber &&
|
||||
itr->Callback == Callback)
|
||||
{
|
||||
RegisteredEvents.erase(itr);
|
||||
debug("Unregistered interrupt handler for IRQ%d to %#lx",
|
||||
InterruptNumber, Callback);
|
||||
return;
|
||||
}
|
||||
}
|
||||
warn("Event %d not found.", InterruptNumber);
|
||||
}
|
||||
|
||||
void RemoveHandler(void (*Callback)(CPU::TrapFrame *))
|
||||
{
|
||||
forItr(itr, RegisteredEvents)
|
||||
{
|
||||
if (itr->Callback == Callback)
|
||||
{
|
||||
debug("Removing handle %d %#lx", itr->IRQ,
|
||||
itr->IsHandler
|
||||
? itr->Data
|
||||
: (void *)itr->Callback);
|
||||
|
||||
RegisteredEvents.erase(itr);
|
||||
}
|
||||
}
|
||||
warn("Handle not found.");
|
||||
}
|
||||
|
||||
void RemoveHandler(int InterruptNumber)
|
||||
{
|
||||
forItr(itr, RegisteredEvents)
|
||||
{
|
||||
if (itr->IRQ == InterruptNumber)
|
||||
{
|
||||
debug("Removing handle %d %#lx", itr->IRQ,
|
||||
itr->IsHandler
|
||||
? itr->Data
|
||||
: (void *)itr->Callback);
|
||||
|
||||
RegisteredEvents.erase(itr);
|
||||
}
|
||||
}
|
||||
warn("IRQ%d not found.", InterruptNumber);
|
||||
}
|
||||
|
||||
nsa inline void ReturnFromInterrupt()
|
||||
{
|
||||
CPUData *CoreData = GetCurrentCPU();
|
||||
int Core = CoreData->ID;
|
||||
|
||||
/* TODO: This should be done when the os is idle */
|
||||
if (SortEvents++ > SORT_ITR)
|
||||
{
|
||||
debug("Sorting events");
|
||||
SortEvents = 0;
|
||||
RegisteredEvents.sort([](const Event &a, const Event &b)
|
||||
{ return a.Priority < b.Priority; });
|
||||
|
||||
#ifdef DEBUG
|
||||
foreach (auto ev in RegisteredEvents)
|
||||
{
|
||||
void *fct = ev.IsHandler
|
||||
? ev.Data
|
||||
: (void *)ev.Callback;
|
||||
const char *symbol = ev.IsHandler
|
||||
? "class"
|
||||
: KernelSymbolTable->GetSymbol((uintptr_t)fct);
|
||||
|
||||
debug("Event IRQ%d [%#lx %s] has priority %ld",
|
||||
ev.IRQ, fct, symbol, ev.Priority);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (likely(apic[Core]))
|
||||
{
|
||||
APIC::APIC *this_apic = (APIC::APIC *)apic[Core];
|
||||
this_apic->EOI();
|
||||
return;
|
||||
}
|
||||
else
|
||||
fixme("APIC not found for core %d", Core);
|
||||
// TODO: Handle PIC too
|
||||
|
||||
assert(!"EOI not handled.");
|
||||
CPU::Stop();
|
||||
}
|
||||
|
||||
extern "C" nsa void MainInterruptHandler(void *Data)
|
||||
{
|
||||
class AutoSwitchPageTable
|
||||
{
|
||||
private:
|
||||
void *Original;
|
||||
|
||||
public:
|
||||
AutoSwitchPageTable()
|
||||
{
|
||||
#if defined(a86)
|
||||
asmv("mov %%cr3, %0" : "=r"(Original));
|
||||
#endif
|
||||
if (likely(Original == KernelPageTable))
|
||||
return;
|
||||
#if defined(a86)
|
||||
asmv("mov %0, %%cr3" : : "r"(KernelPageTable));
|
||||
#endif
|
||||
}
|
||||
|
||||
~AutoSwitchPageTable()
|
||||
{
|
||||
if (likely(Original == KernelPageTable))
|
||||
return;
|
||||
#if defined(a86)
|
||||
asmv("mov %0, %%cr3" : : "r"(Original));
|
||||
#endif
|
||||
}
|
||||
} SwitchPageTable;
|
||||
|
||||
CPU::TrapFrame *Frame = (CPU::TrapFrame *)Data;
|
||||
// debug("IRQ%ld", Frame->InterruptNumber - 32);
|
||||
|
||||
/* Halt core interrupt */
|
||||
if (unlikely(Frame->InterruptNumber == CPU::x86::IRQ31))
|
||||
CPU::Stop();
|
||||
assert(Frame->InterruptNumber <= CPU::x86::IRQ223);
|
||||
|
||||
auto it = RegisteredEvents.begin();
|
||||
while (it != RegisteredEvents.end())
|
||||
{
|
||||
int iEvNum = it->IRQ;
|
||||
#if defined(a86)
|
||||
iEvNum += CPU::x86::IRQ0;
|
||||
#endif
|
||||
if (iEvNum == s_cst(int, Frame->InterruptNumber))
|
||||
break;
|
||||
++it;
|
||||
}
|
||||
|
||||
if (it == RegisteredEvents.end())
|
||||
{
|
||||
warn("IRQ%d is not registered.", Frame->InterruptNumber - 32);
|
||||
ReturnFromInterrupt();
|
||||
return;
|
||||
}
|
||||
|
||||
it->Priority++;
|
||||
|
||||
if (it->IsHandler)
|
||||
{
|
||||
Handler *hnd = (Handler *)it->Data;
|
||||
hnd->OnInterruptReceived(Frame);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (it->Context != nullptr)
|
||||
it->Callback((CPU::TrapFrame *)it->Context);
|
||||
else
|
||||
it->Callback(Frame);
|
||||
}
|
||||
|
||||
ReturnFromInterrupt();
|
||||
}
|
||||
|
||||
extern "C" nsa void SchedulerInterruptHandler(void *Data)
|
||||
{
|
||||
KernelPageTable->Update();
|
||||
CPU::SchedulerFrame *Frame = (CPU::SchedulerFrame *)Data;
|
||||
#if defined(a86)
|
||||
assert(Frame->InterruptNumber == CPU::x86::IRQ16);
|
||||
#else
|
||||
assert(Frame->InterruptNumber == 16);
|
||||
#endif
|
||||
|
||||
auto it = std::find_if(RegisteredEvents.begin(), RegisteredEvents.end(),
|
||||
[](const Event &ev)
|
||||
{
|
||||
return ev.IRQ == 16;
|
||||
});
|
||||
|
||||
if (it == RegisteredEvents.end())
|
||||
{
|
||||
warn("Scheduler interrupt is not registered.");
|
||||
ReturnFromInterrupt();
|
||||
Frame->ppt = Frame->opt;
|
||||
debug("opt = %#lx", Frame->opt);
|
||||
return;
|
||||
}
|
||||
assert(it->IsHandler);
|
||||
|
||||
it->Priority++;
|
||||
|
||||
Handler *hnd = (Handler *)it->Data;
|
||||
hnd->OnInterruptReceived(Frame);
|
||||
ReturnFromInterrupt();
|
||||
}
|
||||
|
||||
Handler::Handler(int InterruptNumber, bool Critical)
|
||||
{
|
||||
foreach (auto ev in RegisteredEvents)
|
||||
{
|
||||
if (ev.IRQ == InterruptNumber)
|
||||
{
|
||||
warn("IRQ%d is already registered.",
|
||||
InterruptNumber);
|
||||
}
|
||||
}
|
||||
|
||||
this->InterruptNumber = InterruptNumber;
|
||||
|
||||
Event newEvent =
|
||||
{InterruptNumber, /* IRQ */
|
||||
this, /* Data */
|
||||
true, /* IsHandler */
|
||||
nullptr, /* Callback */
|
||||
nullptr, /* Context */
|
||||
0, /* Priority */
|
||||
Critical}; /* Critical */
|
||||
RegisteredEvents.push_back(newEvent);
|
||||
debug("Registered interrupt handler for IRQ%d.",
|
||||
InterruptNumber);
|
||||
}
|
||||
|
||||
Handler::Handler(PCI::PCIDevice Device, bool Critical)
|
||||
{
|
||||
PCI::PCIHeader0 *hdr0 =
|
||||
(PCI::PCIHeader0 *)Device.Header;
|
||||
Handler(hdr0->InterruptLine, Critical);
|
||||
}
|
||||
|
||||
Handler::Handler()
|
||||
{
|
||||
debug("Empty interrupt handler.");
|
||||
}
|
||||
|
||||
Handler::~Handler()
|
||||
{
|
||||
debug("Unregistering interrupt handler for IRQ%d.",
|
||||
this->InterruptNumber);
|
||||
|
||||
forItr(itr, RegisteredEvents)
|
||||
{
|
||||
if (itr->IRQ == this->InterruptNumber)
|
||||
{
|
||||
RegisteredEvents.erase(itr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
warn("Event %d not found.", this->InterruptNumber);
|
||||
}
|
||||
|
||||
void Handler::OnInterruptReceived(CPU::TrapFrame *Frame)
|
||||
{
|
||||
debug("Unhandled interrupt %d", Frame->InterruptNumber);
|
||||
}
|
||||
|
||||
void Handler::OnInterruptReceived(CPU::SchedulerFrame *Frame)
|
||||
{
|
||||
debug("Unhandled scheduler interrupt %d", Frame->InterruptNumber);
|
||||
}
|
||||
}
|
256
Kernel/core/lock.cpp
Normal file
256
Kernel/core/lock.cpp
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <lock.hpp>
|
||||
|
||||
#include <debug.h>
|
||||
#include <smp.hpp>
|
||||
|
||||
#include "../kernel.h"
|
||||
|
||||
/* This might end up in a deadlock in the deadlock handler.
|
||||
Nobody can escape the deadlock, not even the
|
||||
deadlock handler itself. */
|
||||
// #define PRINT_BACKTRACE 1
|
||||
|
||||
#ifdef PRINT_BACKTRACE
|
||||
#pragma GCC diagnostic ignored "-Wframe-address"
|
||||
|
||||
void PrintStacktrace(LockClass::SpinLockData *Lock)
|
||||
{
|
||||
if (!KernelSymbolTable)
|
||||
{
|
||||
warn("Symbol table not available.");
|
||||
return;
|
||||
}
|
||||
|
||||
struct StackFrame
|
||||
{
|
||||
uintptr_t BasePointer;
|
||||
uintptr_t ReturnAddress;
|
||||
};
|
||||
|
||||
// char DbgAttempt[1024] = "\0";
|
||||
// char DbgHolder[1024] = "\0";
|
||||
std::string DbgAttempt = "\0";
|
||||
std::string DbgHolder = "\0";
|
||||
|
||||
StackFrame *FrameAttempt = (StackFrame *)Lock->StackPointerAttempt.load();
|
||||
StackFrame *FrameHolder = (StackFrame *)Lock->StackPointerHolder.load();
|
||||
|
||||
while (Memory::Virtual().Check(FrameAttempt))
|
||||
{
|
||||
DbgAttempt.concat(KernelSymbolTable->GetSymbol(FrameAttempt->ReturnAddress));
|
||||
DbgAttempt.concat("<-");
|
||||
FrameAttempt = (StackFrame *)FrameAttempt->BasePointer;
|
||||
}
|
||||
warn("Attempt: %s", DbgAttempt.c_str());
|
||||
|
||||
while (Memory::Virtual().Check(FrameHolder))
|
||||
{
|
||||
DbgHolder.concat(KernelSymbolTable->GetSymbol(FrameHolder->ReturnAddress));
|
||||
DbgHolder.concat("<-");
|
||||
FrameHolder = (StackFrame *)FrameHolder->BasePointer;
|
||||
}
|
||||
|
||||
warn("Holder: %s", DbgHolder.c_str());
|
||||
|
||||
// warn("\t\t%s<-%s<-%s<-%s<-%s<-%s<-%s<-%s<-%s<-%s",
|
||||
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(0)),
|
||||
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(1)),
|
||||
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(2)),
|
||||
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(3)),
|
||||
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(4)),
|
||||
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(5)),
|
||||
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(6)),
|
||||
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(7)),
|
||||
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(8)),
|
||||
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(9)));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DEADLOCK_TIMEOUT 0x1000
|
||||
#else
|
||||
#define DEADLOCK_TIMEOUT 0x10000000
|
||||
#endif
|
||||
|
||||
bool ForceUnlock = false;
|
||||
std::atomic_size_t LocksCount = 0;
|
||||
|
||||
size_t GetLocksCount() { return LocksCount.load(); }
|
||||
|
||||
void LockClass::Yield()
|
||||
{
|
||||
if (CPU::Interrupts(CPU::Check) && TaskManager &&
|
||||
!TaskManager->IsPanic())
|
||||
{
|
||||
TaskManager->Yield();
|
||||
}
|
||||
|
||||
CPU::Pause();
|
||||
}
|
||||
|
||||
void LockClass::DeadLock(SpinLockData &Lock)
|
||||
{
|
||||
if (ForceUnlock)
|
||||
{
|
||||
warn("Unlocking lock '%s' which it was held by '%s'...",
|
||||
Lock.AttemptingToGet, Lock.CurrentHolder);
|
||||
this->DeadLocks = 0;
|
||||
this->Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
CPUData *CoreData = GetCurrentCPU();
|
||||
long CCore = 0xdead;
|
||||
if (CoreData != nullptr)
|
||||
CCore = CoreData->ID;
|
||||
|
||||
warn("Potential deadlock in lock '%s' held by '%s'! %ld %s in queue. Interrupts are %s. Core %ld held by %ld. (%ld times happened)",
|
||||
Lock.AttemptingToGet, Lock.CurrentHolder, Lock.Count, Lock.Count > 1 ? "locks" : "lock",
|
||||
CPU::Interrupts(CPU::Check) ? "enabled" : "disabled", CCore, Lock.Core, this->DeadLocks);
|
||||
|
||||
#ifdef PRINT_BACKTRACE
|
||||
PrintStacktrace(&Lock);
|
||||
#endif
|
||||
|
||||
// TODO: Print on screen too.
|
||||
|
||||
this->DeadLocks++;
|
||||
|
||||
if (Config.UnlockDeadLock && this->DeadLocks.load() > 10)
|
||||
{
|
||||
warn("Unlocking lock '%s' to prevent deadlock. (this is enabled in the kernel config)", Lock.AttemptingToGet);
|
||||
this->DeadLocks = 0;
|
||||
this->Unlock();
|
||||
}
|
||||
|
||||
this->Yield();
|
||||
}
|
||||
|
||||
int LockClass::Lock(const char *FunctionName)
|
||||
{
|
||||
LockData.AttemptingToGet = FunctionName;
|
||||
LockData.StackPointerAttempt = (uintptr_t)__builtin_frame_address(0);
|
||||
|
||||
Retry:
|
||||
int i = 0;
|
||||
while (IsLocked.exchange(true, std::memory_order_acquire) &&
|
||||
++i < DEADLOCK_TIMEOUT)
|
||||
{
|
||||
this->Yield();
|
||||
}
|
||||
|
||||
if (i >= DEADLOCK_TIMEOUT)
|
||||
{
|
||||
DeadLock(LockData);
|
||||
goto Retry;
|
||||
}
|
||||
|
||||
LockData.Count.fetch_add(1);
|
||||
LockData.CurrentHolder.store(FunctionName);
|
||||
LockData.StackPointerHolder.store((uintptr_t)__builtin_frame_address(0));
|
||||
|
||||
CPUData *CoreData = GetCurrentCPU();
|
||||
if (CoreData != nullptr)
|
||||
LockData.Core.store(CoreData->ID);
|
||||
|
||||
LocksCount.fetch_add(1);
|
||||
|
||||
__sync;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LockClass::Unlock()
|
||||
{
|
||||
__sync;
|
||||
|
||||
IsLocked.store(false, std::memory_order_release);
|
||||
LockData.Count.fetch_sub(1);
|
||||
LocksCount.fetch_sub(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LockClass::TimeoutDeadLock(SpinLockData &Lock, uint64_t Timeout)
|
||||
{
|
||||
CPUData *CoreData = GetCurrentCPU();
|
||||
long CCore = 0xdead;
|
||||
|
||||
if (CoreData != nullptr)
|
||||
CCore = CoreData->ID;
|
||||
|
||||
uint64_t Counter = TimeManager->GetCounter();
|
||||
|
||||
warn("Potential deadlock in lock '%s' held by '%s'! %ld %s in queue. Interrupts are %s. Core %ld held by %ld. Timeout in %ld (%ld ticks remaining).",
|
||||
Lock.AttemptingToGet, Lock.CurrentHolder, Lock.Count, Lock.Count > 1 ? "locks" : "lock",
|
||||
CPU::Interrupts(CPU::Check) ? "enabled" : "disabled", CCore, Lock.Core, Timeout, Timeout - Counter);
|
||||
|
||||
#ifdef PRINT_BACKTRACE
|
||||
PrintStacktrace(&Lock);
|
||||
#endif
|
||||
|
||||
if (Timeout < Counter)
|
||||
{
|
||||
warn("Unlocking lock '%s' because of timeout. (%ld < %ld)",
|
||||
Lock.AttemptingToGet, Timeout, Counter);
|
||||
this->Unlock();
|
||||
}
|
||||
|
||||
this->Yield();
|
||||
}
|
||||
|
||||
int LockClass::TimeoutLock(const char *FunctionName, uint64_t Timeout)
|
||||
{
|
||||
if (!TimeManager)
|
||||
return Lock(FunctionName);
|
||||
|
||||
LockData.AttemptingToGet.store(FunctionName);
|
||||
LockData.StackPointerAttempt.store((uintptr_t)__builtin_frame_address(0));
|
||||
|
||||
std::atomic_uint64_t Target = 0;
|
||||
Retry:
|
||||
int i = 0;
|
||||
while (IsLocked.exchange(true, std::memory_order_acquire) &&
|
||||
++i < DEADLOCK_TIMEOUT)
|
||||
{
|
||||
this->Yield();
|
||||
}
|
||||
|
||||
if (i >= DEADLOCK_TIMEOUT)
|
||||
{
|
||||
if (Target.load() == 0)
|
||||
Target.store(TimeManager->CalculateTarget(Timeout,
|
||||
Time::Units::Milliseconds));
|
||||
TimeoutDeadLock(LockData, Target.load());
|
||||
goto Retry;
|
||||
}
|
||||
|
||||
LockData.Count.fetch_add(1);
|
||||
LockData.CurrentHolder.store(FunctionName);
|
||||
LockData.StackPointerHolder.store((uintptr_t)__builtin_frame_address(0));
|
||||
|
||||
CPUData *CoreData = GetCurrentCPU();
|
||||
if (CoreData != nullptr)
|
||||
LockData.Core.store(CoreData->ID);
|
||||
|
||||
LocksCount.fetch_add(1);
|
||||
|
||||
__sync;
|
||||
return 0;
|
||||
}
|
104
Kernel/core/memory/brk.cpp
Normal file
104
Kernel/core/memory/brk.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <memory/brk.hpp>
|
||||
#include <memory/virtual.hpp>
|
||||
#include <memory/vma.hpp>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <debug.h>
|
||||
|
||||
namespace Memory
|
||||
{
|
||||
void *ProgramBreak::brk(void *Address)
|
||||
{
|
||||
if (HeapStart == 0x0 || Break == 0x0)
|
||||
{
|
||||
error("HeapStart or Break is 0x0");
|
||||
return (void *)-EAGAIN;
|
||||
}
|
||||
|
||||
/* Get the current program break. */
|
||||
if (Address == nullptr)
|
||||
return (void *)Break;
|
||||
|
||||
/* Check if the address is valid. */
|
||||
if ((uintptr_t)Address < HeapStart)
|
||||
{
|
||||
debug("Address %#lx is less than HeapStart %#lx", Address, HeapStart);
|
||||
return (void *)-ENOMEM;
|
||||
}
|
||||
|
||||
Virtual vmm(this->Table);
|
||||
|
||||
if ((uintptr_t)Address > Break)
|
||||
{
|
||||
/* Allocate more memory. */
|
||||
ssize_t Pages = TO_PAGES(uintptr_t(Address) - Break);
|
||||
void *Allocated = vma->RequestPages(Pages);
|
||||
if (Allocated == nullptr)
|
||||
return (void *)-ENOMEM;
|
||||
|
||||
/* Map the allocated pages. */
|
||||
for (ssize_t i = 0; i < Pages; i++)
|
||||
{
|
||||
void *VirtAddr = (void *)(Break + (i * PAGE_SIZE));
|
||||
void *PhysAddr = (void *)(uintptr_t(Allocated) + (i * PAGE_SIZE));
|
||||
debug("Mapping %#lx to %#lx", VirtAddr, PhysAddr);
|
||||
vmm.Map(VirtAddr, PhysAddr, RW | US);
|
||||
}
|
||||
|
||||
Break = ROUND_UP(uintptr_t(Address), PAGE_SIZE);
|
||||
debug("Round up %#lx to %#lx", Address, Break);
|
||||
return Address;
|
||||
}
|
||||
|
||||
/* Free memory. */
|
||||
ssize_t Pages = TO_PAGES(Break - uintptr_t(Address));
|
||||
vma->FreePages((void *)Break, Pages);
|
||||
|
||||
/* Unmap the freed pages. */
|
||||
for (ssize_t i = 0; i < Pages; i++)
|
||||
{
|
||||
uint64_t Page = Break - (i * 0x1000);
|
||||
vmm.Remap((void *)Page, (void *)Page, RW);
|
||||
debug("Unmapping %#lx", Page);
|
||||
}
|
||||
|
||||
Break = (uint64_t)Address;
|
||||
return (void *)Break;
|
||||
}
|
||||
|
||||
ProgramBreak::ProgramBreak(PageTable *Table, VirtualMemoryArea *vma)
|
||||
{
|
||||
assert(Table != nullptr);
|
||||
assert(vma != nullptr);
|
||||
|
||||
debug("+ %#lx", this);
|
||||
|
||||
this->Table = Table;
|
||||
this->vma = vma;
|
||||
}
|
||||
|
||||
ProgramBreak::~ProgramBreak()
|
||||
{
|
||||
debug("- %#lx", this);
|
||||
|
||||
/* Do nothing because VirtualMemoryArea
|
||||
will be destroyed later. */
|
||||
}
|
||||
}
|
212
Kernel/core/memory/find_bitmap_region.cpp
Normal file
212
Kernel/core/memory/find_bitmap_region.cpp
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <memory.hpp>
|
||||
|
||||
#include <acpi.hpp>
|
||||
#include <debug.h>
|
||||
#include <elf.h>
|
||||
#ifdef DEBUG
|
||||
#include <uart.hpp>
|
||||
#endif
|
||||
|
||||
#include "../../kernel.h"
|
||||
|
||||
namespace Memory
|
||||
{
|
||||
__no_sanitize("alignment") void Physical::FindBitmapRegion(uintptr_t &BitmapAddress,
|
||||
size_t &BitmapAddressSize)
|
||||
{
|
||||
size_t BitmapSize = (size_t)(bInfo.Memory.Size / PAGE_SIZE) / 8 + 1;
|
||||
|
||||
uintptr_t KernelStart = (uintptr_t)bInfo.Kernel.PhysicalBase;
|
||||
uintptr_t KernelEnd = (uintptr_t)bInfo.Kernel.PhysicalBase + bInfo.Kernel.Size;
|
||||
|
||||
uintptr_t SectionsStart = 0x0;
|
||||
uintptr_t SectionsEnd = 0x0;
|
||||
|
||||
uintptr_t Symbols = 0x0;
|
||||
uintptr_t StringAddress = 0x0;
|
||||
size_t SymbolSize = 0;
|
||||
size_t StringSize = 0;
|
||||
|
||||
uintptr_t RSDPStart = 0x0;
|
||||
uintptr_t RSDPEnd = 0x0;
|
||||
|
||||
if (bInfo.Kernel.Symbols.Num &&
|
||||
bInfo.Kernel.Symbols.EntSize &&
|
||||
bInfo.Kernel.Symbols.Shndx)
|
||||
{
|
||||
char *sections = r_cst(char *, bInfo.Kernel.Symbols.Sections);
|
||||
|
||||
SectionsStart = (uintptr_t)sections;
|
||||
SectionsEnd = (uintptr_t)sections + bInfo.Kernel.Symbols.EntSize *
|
||||
bInfo.Kernel.Symbols.Num;
|
||||
|
||||
for (size_t i = 0; i < bInfo.Kernel.Symbols.Num; ++i)
|
||||
{
|
||||
Elf_Shdr *sym = (Elf_Shdr *)§ions[bInfo.Kernel.Symbols.EntSize * i];
|
||||
Elf_Shdr *str = (Elf_Shdr *)§ions[bInfo.Kernel.Symbols.EntSize *
|
||||
sym->sh_link];
|
||||
|
||||
if (sym->sh_type == SHT_SYMTAB &&
|
||||
str->sh_type == SHT_STRTAB)
|
||||
{
|
||||
Symbols = (uintptr_t)sym->sh_addr;
|
||||
StringAddress = (uintptr_t)str->sh_addr;
|
||||
SymbolSize = (size_t)sym->sh_size;
|
||||
StringSize = (size_t)str->sh_size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(a86)
|
||||
if (bInfo.RSDP)
|
||||
{
|
||||
RSDPStart = (uintptr_t)bInfo.RSDP;
|
||||
RSDPEnd = (uintptr_t)bInfo.RSDP + sizeof(BootInfo::RSDPInfo);
|
||||
|
||||
#ifdef DEBUG
|
||||
ACPI::ACPI::ACPIHeader *ACPIPtr;
|
||||
bool XSDT = false;
|
||||
|
||||
if (bInfo.RSDP->Revision >= 2 && bInfo.RSDP->XSDTAddress)
|
||||
{
|
||||
ACPIPtr = (ACPI::ACPI::ACPIHeader *)bInfo.RSDP->XSDTAddress;
|
||||
XSDT = true;
|
||||
}
|
||||
else
|
||||
ACPIPtr = (ACPI::ACPI::ACPIHeader *)(uintptr_t)bInfo.RSDP->RSDTAddress;
|
||||
|
||||
if (Memory::Virtual().Check(ACPIPtr))
|
||||
{
|
||||
size_t TableSize = ((ACPIPtr->Length - sizeof(ACPI::ACPI::ACPIHeader)) /
|
||||
(XSDT ? 8 : 4));
|
||||
debug("There are %d ACPI tables", TableSize);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#elif defined(aa64)
|
||||
#endif
|
||||
|
||||
for (uint64_t i = 0; i < bInfo.Memory.Entries; i++)
|
||||
{
|
||||
if (bInfo.Memory.Entry[i].Type == Usable)
|
||||
{
|
||||
uintptr_t RegionAddress = (uintptr_t)bInfo.Memory.Entry[i].BaseAddress;
|
||||
uintptr_t RegionSize = bInfo.Memory.Entry[i].Length;
|
||||
|
||||
/* We don't want to use the first 1MB of memory. */
|
||||
if (RegionAddress <= 0xFFFFF)
|
||||
continue;
|
||||
|
||||
if ((BitmapSize + 0x100) > RegionSize)
|
||||
{
|
||||
debug("Region %p-%p (%d MiB) is too small for bitmap.",
|
||||
(void *)RegionAddress,
|
||||
(void *)(RegionAddress + RegionSize),
|
||||
TO_MiB(RegionSize));
|
||||
continue;
|
||||
}
|
||||
|
||||
BitmapAddress = RegionAddress;
|
||||
BitmapAddressSize = RegionSize;
|
||||
|
||||
struct AddrRange
|
||||
{
|
||||
uintptr_t Start;
|
||||
uintptr_t End;
|
||||
};
|
||||
|
||||
auto SortAddresses = [](AddrRange *Array, size_t n)
|
||||
{
|
||||
size_t MinimumIndex;
|
||||
for (size_t i = 0; i < n - 1; i++)
|
||||
{
|
||||
MinimumIndex = i;
|
||||
for (size_t j = i + 1; j < n; j++)
|
||||
if (Array[j].Start < Array[MinimumIndex].Start)
|
||||
MinimumIndex = j;
|
||||
|
||||
AddrRange tmp = Array[MinimumIndex];
|
||||
Array[MinimumIndex] = Array[i];
|
||||
Array[i] = tmp;
|
||||
}
|
||||
};
|
||||
|
||||
AddrRange PtrArray[] =
|
||||
{
|
||||
{KernelStart,
|
||||
KernelEnd},
|
||||
{SectionsStart,
|
||||
SectionsEnd},
|
||||
{Symbols,
|
||||
Symbols + SymbolSize},
|
||||
{StringAddress,
|
||||
StringAddress + StringSize},
|
||||
{RSDPStart,
|
||||
RSDPEnd},
|
||||
{(uintptr_t)bInfo.Kernel.FileBase,
|
||||
(uintptr_t)bInfo.Kernel.FileBase + bInfo.Kernel.Size},
|
||||
{(uintptr_t)bInfo.Modules[0].Address,
|
||||
(uintptr_t)bInfo.Modules[0].Address + bInfo.Modules[0].Size},
|
||||
{(uintptr_t)bInfo.Modules[1].Address,
|
||||
(uintptr_t)bInfo.Modules[1].Address + bInfo.Modules[1].Size},
|
||||
{(uintptr_t)bInfo.Modules[2].Address,
|
||||
(uintptr_t)bInfo.Modules[2].Address + bInfo.Modules[2].Size},
|
||||
{(uintptr_t)bInfo.Modules[3].Address,
|
||||
(uintptr_t)bInfo.Modules[3].Address + bInfo.Modules[3].Size},
|
||||
/* MAX_MODULES == 4 */
|
||||
};
|
||||
|
||||
SortAddresses(PtrArray, sizeof(PtrArray) / sizeof(PtrArray[0]));
|
||||
|
||||
for (size_t i = 0; i < sizeof(PtrArray) / sizeof(PtrArray[0]); i++)
|
||||
{
|
||||
if (PtrArray[i].Start == 0x0)
|
||||
continue;
|
||||
|
||||
uintptr_t Start = PtrArray[i].Start;
|
||||
uintptr_t End = PtrArray[i].End;
|
||||
debug("%#lx - %#lx", Start, End);
|
||||
|
||||
if (RegionAddress >= Start &&
|
||||
End <= (RegionAddress + RegionSize))
|
||||
{
|
||||
BitmapAddress = End;
|
||||
BitmapAddressSize = RegionSize - (End - RegionAddress);
|
||||
}
|
||||
}
|
||||
|
||||
if ((BitmapSize + 0x100) > BitmapAddressSize)
|
||||
{
|
||||
debug("Region %p-%p (%d MiB) is too small for bitmap.",
|
||||
(void *)BitmapAddress,
|
||||
(void *)(BitmapAddress + BitmapAddressSize),
|
||||
TO_MiB(BitmapAddressSize));
|
||||
continue;
|
||||
}
|
||||
|
||||
debug("Found free memory for bitmap: %p (%d MiB)",
|
||||
(void *)BitmapAddress,
|
||||
TO_MiB(BitmapAddressSize));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
154
Kernel/core/memory/heap_allocators/Xalloc/README.md
Normal file
154
Kernel/core/memory/heap_allocators/Xalloc/README.md
Normal file
@ -0,0 +1,154 @@
|
||||
# Xalloc
|
||||
|
||||
Xalloc is a custom memory allocator designed for hobby operating systems.
|
||||
Written in C++ and provides a simple and efficient way to manage memory in your hobby OS.
|
||||
|
||||
#### ❗ This project is still in development and is not ready for use in production environments. ❗
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Simple API** - Simple API for allocating and freeing memory.
|
||||
|
||||
- **Efficient** - Uses a free-list to manage memory and is designed to be fast.
|
||||
|
||||
- **No dependencies** - No dependencies and is designed to be easy to integrate into your OS.
|
||||
|
||||
---
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Implementing missing functions
|
||||
|
||||
You will need to implement the following functions in your OS:
|
||||
|
||||
##### Wrapper.cpp
|
||||
```cpp
|
||||
extern "C" void *Xalloc_REQUEST_PAGES(Xsize_t Pages)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
extern "C" void Xalloc_FREE_PAGES(void *Address, Xsize_t Pages)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
/* Mandatory only if Xalloc_MapPages is set to true */
|
||||
extern "C" void Xalloc_MAP_MEMORY(void *VirtualAddress, void *PhysicalAddress, Xsize_t Flags)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
/* Mandatory only if Xalloc_MapPages is set to true */
|
||||
extern "C" void Xalloc_UNMAP_MEMORY(void *VirtualAddress)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
##### Xalloc.hpp
|
||||
```cpp
|
||||
#define Xalloc_StopOnFail <bool> /* Infinite loop on failure */
|
||||
#define Xalloc_MapPages <bool> /* Map pages on allocation */
|
||||
#define Xalloc_PAGE_SIZE <page size> /* <-- Replace with your page size */
|
||||
#define Xalloc_trace(m, ...) <trace function>
|
||||
#define Xalloc_warn(m, ...) <warning function>
|
||||
#define Xalloc_err(m, ...) <error function>
|
||||
|
||||
#define XallocV1_def <define a lock> /* eg. std::mutex Xalloc_lock; */
|
||||
#define XallocV1_lock <lock function>
|
||||
#define XallocV1_unlock <unlock function>
|
||||
|
||||
/* Same as above */
|
||||
#define XallocV2_def <define a lock>
|
||||
#define XallocV2_lock <lock function>
|
||||
#define XallocV2_unlock <unlock function>
|
||||
```
|
||||
|
||||
### Typical usage
|
||||
|
||||
```cpp
|
||||
#include "Xalloc.hpp"
|
||||
|
||||
Xalloc::V1 *XallocV1Allocator = nullptr;
|
||||
|
||||
int main()
|
||||
{
|
||||
/* Virtual Base User SMAP */
|
||||
XallocV1Allocator = new Xalloc::V1((void *)0xFFFFA00000000000, false, false);
|
||||
|
||||
void *p = XallocV1Allocator->malloc(1234);
|
||||
/* ... */
|
||||
XallocV1Allocator->free(p);
|
||||
delete XallocV1Allocator;
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```cpp
|
||||
#include "Xalloc.hpp"
|
||||
|
||||
int main()
|
||||
{
|
||||
/* Virtual Base User SMAP */
|
||||
Xalloc::V1 XallocV1Allocator((void *)0xFFFFA00000000000, false, false);
|
||||
|
||||
void *p = XallocV1Allocator.malloc(1234);
|
||||
/* ... */
|
||||
XallocV1Allocator.free(p);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API
|
||||
|
||||
### Xalloc::V1
|
||||
|
||||
```cpp
|
||||
void *malloc(Xsize_t Size);
|
||||
```
|
||||
Allocates a block of memory of size `Size` bytes.
|
||||
If `Size` is 0, then `nullptr` is returned.
|
||||
- `Size` - The size of the block to allocate in bytes.
|
||||
|
||||
<br><br>
|
||||
|
||||
```cpp
|
||||
void free(void *Address);
|
||||
```
|
||||
Frees the memory block pointed to by `Address`.
|
||||
If `Address` is `nullptr`, then no operation is performed.
|
||||
- `Address` - The address of the memory block to free.
|
||||
|
||||
<br><br>
|
||||
|
||||
```cpp
|
||||
void *calloc(Xsize_t NumberOfBlocks, Xsize_t Size);
|
||||
```
|
||||
Allocates a block of memory for an array of `NumberOfBlocks` elements, each of them `Size` bytes long.
|
||||
If `NumberOfBlocks` or `Size` is 0, then `nullptr` is returned.
|
||||
- `NumberOfBlocks` - The number of elements to allocate.
|
||||
- `Size` - The size of each element in bytes.
|
||||
|
||||
<br><br>
|
||||
|
||||
```cpp
|
||||
void *realloc(void *Address, Xsize_t Size);
|
||||
```
|
||||
Changes the size of the memory block pointed to by `Address` to `Size` bytes.
|
||||
If `Address` is `nullptr`, then the call is equivalent to `malloc(Size)`.
|
||||
If `Size` is equal to zero, and `Address` is not `nullptr`, then the call is equivalent to `free(Address)`.
|
||||
- `Address` - The address of the memory block to resize.
|
||||
- `Size` - The new size of the memory block in bytes.
|
||||
|
||||
---
|
||||
|
||||
## To-do
|
||||
|
||||
- [ ] Multiple free-lists for different block sizes
|
40
Kernel/core/memory/heap_allocators/Xalloc/Wrapper.cpp
Normal file
40
Kernel/core/memory/heap_allocators/Xalloc/Wrapper.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Xalloc.hpp"
|
||||
|
||||
#include <memory.hpp>
|
||||
|
||||
extern "C" void *Xalloc_REQUEST_PAGES(Xsize_t Pages)
|
||||
{
|
||||
return KernelAllocator.RequestPages(Pages);
|
||||
}
|
||||
|
||||
extern "C" void Xalloc_FREE_PAGES(void *Address, Xsize_t Pages)
|
||||
{
|
||||
KernelAllocator.FreePages(Address, Pages);
|
||||
}
|
||||
|
||||
extern "C" void Xalloc_MAP_MEMORY(void *VirtualAddress, void *PhysicalAddress, Xsize_t Flags)
|
||||
{
|
||||
Memory::Virtual(KernelPageTable).Map(VirtualAddress, PhysicalAddress, Flags);
|
||||
}
|
||||
|
||||
extern "C" void Xalloc_UNMAP_MEMORY(void *VirtualAddress)
|
||||
{
|
||||
Memory::Virtual(KernelPageTable).Unmap(VirtualAddress);
|
||||
}
|
236
Kernel/core/memory/heap_allocators/Xalloc/Xalloc.hpp
Normal file
236
Kernel/core/memory/heap_allocators/Xalloc/Xalloc.hpp
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __FENNIX_KERNEL_Xalloc_H__
|
||||
#define __FENNIX_KERNEL_Xalloc_H__
|
||||
|
||||
#include <memory.hpp>
|
||||
#include <lock.hpp>
|
||||
#include <debug.h>
|
||||
|
||||
typedef __UINT8_TYPE__ Xuint8_t;
|
||||
typedef __SIZE_TYPE__ Xsize_t;
|
||||
typedef __UINTPTR_TYPE__ Xuintptr_t;
|
||||
|
||||
#define Xalloc_StopOnFail true
|
||||
#define Xalloc_MapPages false
|
||||
#define Xalloc_PAGE_SIZE PAGE_SIZE
|
||||
#define Xalloc_trace(m, ...) trace(m, ##__VA_ARGS__)
|
||||
#define Xalloc_warn(m, ...) warn(m, ##__VA_ARGS__)
|
||||
#define Xalloc_err(m, ...) error(m, ##__VA_ARGS__)
|
||||
|
||||
#define XallocV1_def NewLock(XallocV1Lock)
|
||||
#define XallocV1_lock XallocV1Lock.Lock(__FUNCTION__)
|
||||
#define XallocV1_unlock XallocV1Lock.Unlock()
|
||||
|
||||
#define XallocV2_def NewLock(XallocV2Lock)
|
||||
#define XallocV2_lock XallocV2Lock.Lock(__FUNCTION__)
|
||||
#define XallocV2_unlock XallocV2Lock.Unlock()
|
||||
|
||||
namespace Xalloc
|
||||
{
|
||||
class V1
|
||||
{
|
||||
private:
|
||||
void *BaseVirtualAddress = nullptr;
|
||||
void *FirstBlock = nullptr;
|
||||
void *LastBlock = nullptr;
|
||||
|
||||
bool UserMapping = false;
|
||||
bool SMAPUsed = false;
|
||||
|
||||
public:
|
||||
/** @brief Execute "stac" instruction if the kernel has SMAP enabled */
|
||||
void Xstac();
|
||||
|
||||
/** @brief Execute "clac" instruction if the kernel has SMAP enabled */
|
||||
void Xclac();
|
||||
|
||||
/**
|
||||
* @brief Arrange the blocks to optimize the memory usage
|
||||
* The allocator is not arranged by default
|
||||
* to avoid performance issues.
|
||||
* This function will defragment the memory
|
||||
* and free the unused blocks.
|
||||
*
|
||||
* You should call this function when the
|
||||
* kernel is idle or when is not using
|
||||
* the allocator.
|
||||
*/
|
||||
void Arrange();
|
||||
|
||||
/**
|
||||
* @brief Allocate a new memory block
|
||||
*
|
||||
* @param Size Size of the block to allocate.
|
||||
* @return void* Pointer to the allocated block.
|
||||
*/
|
||||
void *malloc(Xsize_t Size);
|
||||
|
||||
/**
|
||||
* @brief Free a previously allocated block
|
||||
*
|
||||
* @param Address Address of the block to free.
|
||||
*/
|
||||
void free(void *Address);
|
||||
|
||||
/**
|
||||
* @brief Allocate a new memory block
|
||||
*
|
||||
* @param NumberOfBlocks Number of blocks to allocate.
|
||||
* @param Size Size of the block to allocate.
|
||||
* @return void* Pointer to the allocated block.
|
||||
*/
|
||||
void *calloc(Xsize_t NumberOfBlocks, Xsize_t Size);
|
||||
|
||||
/**
|
||||
* @brief Reallocate a previously allocated block
|
||||
*
|
||||
* @param Address Address of the block to reallocate.
|
||||
* @param Size New size of the block.
|
||||
* @return void* Pointer to the reallocated block.
|
||||
*/
|
||||
void *realloc(void *Address, Xsize_t Size);
|
||||
|
||||
/**
|
||||
* @brief Construct a new Allocator object
|
||||
*
|
||||
* @param BaseVirtualAddress Virtual address to map the pages.
|
||||
* @param UserMode Map the new pages with USER flag?
|
||||
* @param SMAPEnabled Does the kernel has Supervisor Mode Access Prevention enabled?
|
||||
*/
|
||||
V1(void *BaseVirtualAddress, bool UserMode, bool SMAPEnabled);
|
||||
|
||||
/**
|
||||
* @brief Destroy the Allocator object
|
||||
*
|
||||
*/
|
||||
~V1();
|
||||
};
|
||||
|
||||
class V2
|
||||
{
|
||||
private:
|
||||
class Block
|
||||
{
|
||||
public:
|
||||
int Sanity = 0xA110C;
|
||||
Block *Next = nullptr;
|
||||
bool IsFree = true;
|
||||
V2 *ctx = nullptr;
|
||||
|
||||
Xuint8_t *Data = nullptr;
|
||||
Xsize_t DataSize = 0;
|
||||
|
||||
void Check();
|
||||
Block(Xsize_t Size, V2 *ctx);
|
||||
~Block();
|
||||
void *operator new(Xsize_t);
|
||||
void operator delete(void *Address);
|
||||
} __attribute__((packed, aligned((16))));
|
||||
|
||||
/* The base address of the virtual memory */
|
||||
Xuintptr_t BaseVirtualAddress = 0x0;
|
||||
|
||||
/* The size of the heap */
|
||||
Xsize_t HeapSize = 0x0;
|
||||
|
||||
/* The used size of the heap */
|
||||
Xsize_t HeapUsed = 0x0;
|
||||
|
||||
Block *FirstBlock = nullptr;
|
||||
|
||||
Xuint8_t *AllocateHeap(Xsize_t Size);
|
||||
void FreeHeap(Xuint8_t *At, Xsize_t Size);
|
||||
|
||||
Xsize_t Align(Xsize_t Size);
|
||||
void *FindFreeBlock(Xsize_t Size,
|
||||
Block *&CurrentBlock);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Arrange the blocks to optimize the memory
|
||||
* usage.
|
||||
* The allocator is not arranged by default
|
||||
* to avoid performance issues.
|
||||
* This function will defragment the memory
|
||||
* and free the unused blocks.
|
||||
*
|
||||
* You should call this function when the
|
||||
* kernel is idle or when is not using the
|
||||
* allocator.
|
||||
*/
|
||||
void Arrange();
|
||||
|
||||
/**
|
||||
* Allocate a new memory block
|
||||
*
|
||||
* @param Size Size of the block to allocate.
|
||||
* @return void* Pointer to the allocated
|
||||
* block.
|
||||
*/
|
||||
void *malloc(Xsize_t Size);
|
||||
|
||||
/**
|
||||
* Free a previously allocated block
|
||||
*
|
||||
* @param Address Address of the block to
|
||||
* free.
|
||||
*/
|
||||
void free(void *Address);
|
||||
|
||||
/**
|
||||
* Allocate a new memory block
|
||||
*
|
||||
* @param NumberOfBlocks Number of blocks
|
||||
* to allocate.
|
||||
* @param Size Size of the block to allocate.
|
||||
* @return void* Pointer to the allocated
|
||||
* block.
|
||||
*/
|
||||
void *calloc(Xsize_t NumberOfBlocks,
|
||||
Xsize_t Size);
|
||||
|
||||
/**
|
||||
* Reallocate a previously allocated block
|
||||
*
|
||||
* @param Address Address of the block
|
||||
* to reallocate.
|
||||
* @param Size New size of the block.
|
||||
* @return void* Pointer to the reallocated
|
||||
* block.
|
||||
*/
|
||||
void *realloc(void *Address, Xsize_t Size);
|
||||
|
||||
/**
|
||||
* Construct a new Allocator object
|
||||
*
|
||||
* @param VirtualBase Virtual address
|
||||
* to map the pages.
|
||||
*/
|
||||
V2(void *VirtualBase);
|
||||
|
||||
/**
|
||||
* Destroy the Allocator object
|
||||
*/
|
||||
~V2();
|
||||
|
||||
friend class Block;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // !__FENNIX_KERNEL_Xalloc_H__
|
290
Kernel/core/memory/heap_allocators/Xalloc/XallocV1.cpp
Normal file
290
Kernel/core/memory/heap_allocators/Xalloc/XallocV1.cpp
Normal file
@ -0,0 +1,290 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Xalloc.hpp"
|
||||
|
||||
XallocV1_def;
|
||||
|
||||
#define XALLOC_CONCAT(x, y) x##y
|
||||
#define XStoP(d) (((d) + PAGE_SIZE - 1) / PAGE_SIZE)
|
||||
#define XPtoS(d) ((d)*PAGE_SIZE)
|
||||
#define Xalloc_BlockSanityKey 0xA110C
|
||||
|
||||
extern "C" void *Xalloc_REQUEST_PAGES(Xsize_t Pages);
|
||||
extern "C" void Xalloc_FREE_PAGES(void *Address, Xsize_t Pages);
|
||||
extern "C" void Xalloc_MAP_MEMORY(void *VirtualAddress, void *PhysicalAddress, Xsize_t Flags);
|
||||
extern "C" void Xalloc_UNMAP_MEMORY(void *VirtualAddress);
|
||||
|
||||
// TODO: Change memcpy with an optimized version
|
||||
void *Xmemcpy(void *__restrict__ Destination, const void *__restrict__ Source, Xsize_t Length)
|
||||
{
|
||||
unsigned char *dst = (unsigned char *)Destination;
|
||||
const unsigned char *src = (const unsigned char *)Source;
|
||||
for (Xsize_t i = 0; i < Length; i++)
|
||||
dst[i] = src[i];
|
||||
return Destination;
|
||||
}
|
||||
|
||||
// TODO: Change memset with an optimized version
|
||||
void *Xmemset(void *__restrict__ Destination, int Data, Xsize_t Length)
|
||||
{
|
||||
unsigned char *Buffer = (unsigned char *)Destination;
|
||||
for (Xsize_t i = 0; i < Length; i++)
|
||||
Buffer[i] = (unsigned char)Data;
|
||||
return Destination;
|
||||
}
|
||||
|
||||
namespace Xalloc
|
||||
{
|
||||
class Block
|
||||
{
|
||||
public:
|
||||
void *Address = nullptr;
|
||||
|
||||
int Sanity = Xalloc_BlockSanityKey;
|
||||
Xsize_t Size = 0;
|
||||
Block *Next = nullptr;
|
||||
Block *Last = nullptr;
|
||||
bool IsFree = true;
|
||||
|
||||
bool Check()
|
||||
{
|
||||
if (this->Sanity != Xalloc_BlockSanityKey)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Block(Xsize_t Size)
|
||||
{
|
||||
this->Address = Xalloc_REQUEST_PAGES(XStoP(Size + 1));
|
||||
this->Size = Size;
|
||||
Xmemset(this->Address, 0, Size);
|
||||
}
|
||||
|
||||
~Block()
|
||||
{
|
||||
Xalloc_FREE_PAGES(this->Address, XStoP(this->Size + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Overload new operator to allocate memory from the heap
|
||||
* @param Size Unused
|
||||
* @return void* Pointer to the allocated memory
|
||||
*/
|
||||
void *operator new(Xsize_t Size)
|
||||
{
|
||||
void *ptr = Xalloc_REQUEST_PAGES(XStoP(sizeof(Block)));
|
||||
return ptr;
|
||||
(void)(Size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Overload delete operator to free memory from the heap
|
||||
* @param Address Pointer to the memory to free
|
||||
*/
|
||||
void operator delete(void *Address)
|
||||
{
|
||||
Xalloc_FREE_PAGES(Address, XStoP(sizeof(Block)));
|
||||
}
|
||||
} __attribute__((packed, aligned((16))));
|
||||
|
||||
class SmartSMAPClass
|
||||
{
|
||||
private:
|
||||
V1 *allocator = nullptr;
|
||||
|
||||
public:
|
||||
SmartSMAPClass(V1 *allocator)
|
||||
{
|
||||
this->allocator = allocator;
|
||||
this->allocator->Xstac();
|
||||
}
|
||||
~SmartSMAPClass() { this->allocator->Xclac(); }
|
||||
};
|
||||
#define SmartSMAP SmartSMAPClass XALLOC_CONCAT(SmartSMAP##_, __COUNTER__)(this)
|
||||
|
||||
void V1::Xstac()
|
||||
{
|
||||
if (this->SMAPUsed)
|
||||
{
|
||||
#if defined(a86)
|
||||
asm volatile("stac" ::
|
||||
: "cc");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void V1::Xclac()
|
||||
{
|
||||
if (this->SMAPUsed)
|
||||
{
|
||||
#if defined(a86)
|
||||
asm volatile("clac" ::
|
||||
: "cc");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void V1::Arrange()
|
||||
{
|
||||
Xalloc_err("Arrange() is not implemented yet!");
|
||||
}
|
||||
|
||||
void *V1::malloc(Xsize_t Size)
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
Xalloc_warn("Attempted to allocate 0 bytes!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SmartSMAP;
|
||||
XallocV1_lock;
|
||||
|
||||
if (this->FirstBlock == nullptr)
|
||||
{
|
||||
this->FirstBlock = new Block(Size);
|
||||
((Block *)this->FirstBlock)->IsFree = false;
|
||||
XallocV1_unlock;
|
||||
return ((Block *)this->FirstBlock)->Address;
|
||||
}
|
||||
|
||||
Block *CurrentBlock = ((Block *)this->FirstBlock);
|
||||
while (CurrentBlock != nullptr)
|
||||
{
|
||||
if (!CurrentBlock->Check())
|
||||
{
|
||||
Xalloc_err("Block %#lx has an invalid sanity key! (%#x != %#x)",
|
||||
(Xsize_t)CurrentBlock, CurrentBlock->Sanity, Xalloc_BlockSanityKey);
|
||||
while (Xalloc_StopOnFail)
|
||||
;
|
||||
}
|
||||
else if (CurrentBlock->IsFree && CurrentBlock->Size >= Size)
|
||||
{
|
||||
CurrentBlock->IsFree = false;
|
||||
Xmemset(CurrentBlock->Address, 0, Size);
|
||||
XallocV1_unlock;
|
||||
return CurrentBlock->Address;
|
||||
}
|
||||
CurrentBlock = CurrentBlock->Next;
|
||||
}
|
||||
|
||||
CurrentBlock = ((Block *)this->FirstBlock);
|
||||
while (CurrentBlock->Next != nullptr)
|
||||
CurrentBlock = CurrentBlock->Next;
|
||||
|
||||
CurrentBlock->Next = new Block(Size);
|
||||
((Block *)CurrentBlock->Next)->Last = CurrentBlock;
|
||||
((Block *)CurrentBlock->Next)->IsFree = false;
|
||||
XallocV1_unlock;
|
||||
return ((Block *)CurrentBlock->Next)->Address;
|
||||
}
|
||||
|
||||
void V1::free(void *Address)
|
||||
{
|
||||
if (Address == nullptr)
|
||||
{
|
||||
Xalloc_warn("Attempted to free a null pointer!");
|
||||
return;
|
||||
}
|
||||
|
||||
SmartSMAP;
|
||||
XallocV1_lock;
|
||||
|
||||
Block *CurrentBlock = ((Block *)this->FirstBlock);
|
||||
while (CurrentBlock != nullptr)
|
||||
{
|
||||
if (!CurrentBlock->Check())
|
||||
{
|
||||
Xalloc_err("Block %#lx has an invalid sanity key! (%#x != %#x)",
|
||||
(Xsize_t)CurrentBlock, CurrentBlock->Sanity, Xalloc_BlockSanityKey);
|
||||
while (Xalloc_StopOnFail)
|
||||
;
|
||||
}
|
||||
else if (CurrentBlock->Address == Address)
|
||||
{
|
||||
if (CurrentBlock->IsFree)
|
||||
{
|
||||
Xalloc_warn("Attempted to free an already freed pointer!");
|
||||
XallocV1_unlock;
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentBlock->IsFree = true;
|
||||
XallocV1_unlock;
|
||||
return;
|
||||
}
|
||||
CurrentBlock = CurrentBlock->Next;
|
||||
}
|
||||
|
||||
Xalloc_err("Invalid address %#lx.", Address);
|
||||
XallocV1_unlock;
|
||||
}
|
||||
|
||||
void *V1::calloc(Xsize_t NumberOfBlocks, Xsize_t Size)
|
||||
{
|
||||
if (NumberOfBlocks == 0 || Size == 0)
|
||||
{
|
||||
Xalloc_warn("The %s%s%s is 0!",
|
||||
NumberOfBlocks == 0 ? "NumberOfBlocks" : "",
|
||||
NumberOfBlocks == 0 && Size == 0 ? " and " : "",
|
||||
Size == 0 ? "Size" : "");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return this->malloc(NumberOfBlocks * Size);
|
||||
}
|
||||
|
||||
void *V1::realloc(void *Address, Xsize_t Size)
|
||||
{
|
||||
if (Address == nullptr)
|
||||
return this->malloc(Size);
|
||||
|
||||
if (Size == 0)
|
||||
{
|
||||
this->free(Address);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// SmartSMAP;
|
||||
// XallocV1_lock;
|
||||
// ...
|
||||
// XallocV1_unlock;
|
||||
|
||||
// TODO: Implement realloc
|
||||
this->free(Address);
|
||||
return this->malloc(Size);
|
||||
}
|
||||
|
||||
V1::V1(void *BaseVirtualAddress, bool UserMode, bool SMAPEnabled)
|
||||
{
|
||||
SmartSMAP;
|
||||
XallocV1_lock;
|
||||
this->SMAPUsed = SMAPEnabled;
|
||||
this->UserMapping = UserMode;
|
||||
this->BaseVirtualAddress = BaseVirtualAddress;
|
||||
XallocV1_unlock;
|
||||
}
|
||||
|
||||
V1::~V1()
|
||||
{
|
||||
SmartSMAP;
|
||||
XallocV1_lock;
|
||||
Xalloc_trace("Destructor not implemented yet.");
|
||||
XallocV1_unlock;
|
||||
}
|
||||
}
|
281
Kernel/core/memory/heap_allocators/Xalloc/XallocV2.cpp
Normal file
281
Kernel/core/memory/heap_allocators/Xalloc/XallocV2.cpp
Normal file
@ -0,0 +1,281 @@
|
||||
/*
|
||||
This file is part of Fennix Kernel.
|
||||
|
||||
Fennix Kernel is free software: you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
Fennix Kernel is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Xalloc.hpp"
|
||||
|
||||
XallocV2_def;
|
||||
|
||||
#define XALLOC_CONCAT(x, y) x##y
|
||||
#define XStoP(d) (((d) + PAGE_SIZE - 1) / PAGE_SIZE)
|
||||
#define XPtoS(d) ((d)*PAGE_SIZE)
|
||||
|
||||
extern "C" void *Xalloc_REQUEST_PAGES(Xsize_t Pages);
|
||||
extern "C" void Xalloc_FREE_PAGES(void *Address, Xsize_t Pages);
|
||||
extern "C" void Xalloc_MAP_MEMORY(void *VirtualAddress,
|
||||
void *PhysicalAddress,
|
||||
Xsize_t Flags);
|
||||
extern "C" void Xalloc_UNMAP_MEMORY(void *VirtualAddress);
|
||||
|
||||
#define Xalloc_BlockSanityKey 0xA110C
|
||||
|
||||
/*
|
||||
[ IN DEVELOPMENT ]
|
||||
*/
|
||||
|
||||
namespace Xalloc
|
||||
{
|
||||
void V2::Block::Check()
|
||||
{
|
||||
if (unlikely(this->Sanity != Xalloc_BlockSanityKey))
|
||||
{
|
||||
Xalloc_err("Block %#lx has an invalid sanity key! (%#x != %#x)",
|
||||
this, this->Sanity, Xalloc_BlockSanityKey);
|
||||
|
||||
while (Xalloc_StopOnFail)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
V2::Block::Block(Xsize_t Size, V2 *ctx)
|
||||
{
|
||||
this->ctx = ctx;
|
||||
this->Data = ctx->AllocateHeap(Size);
|
||||
this->DataSize = Size;
|
||||
}
|
||||
|
||||
V2::Block::~Block()
|
||||
{
|
||||
}
|
||||
|
||||
void *V2::Block::operator new(Xsize_t)
|
||||
{
|
||||
constexpr Xsize_t bPgs = XStoP(sizeof(Block));
|
||||
void *ptr = Xalloc_REQUEST_PAGES(bPgs);
|
||||
/* TODO: Do something with the rest of
|
||||
the allocated memory */
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void V2::Block::operator delete(void *Address)
|
||||
{
|
||||
constexpr Xsize_t bPgs = XStoP(sizeof(Block));
|
||||
Xalloc_FREE_PAGES(Address, bPgs);
|
||||
}
|
||||
|
||||
/* ========================================= */
|
||||
|
||||
Xuint8_t *V2::AllocateHeap(Xsize_t Size)
|
||||
{
|
||||
Size = this->Align(Size);
|
||||
Xsize_t Pages = XStoP(Size);
|
||||
|
||||
Xuint8_t *FinalAddress = 0x0;
|
||||
if (this->HeapUsed + Size >= this->HeapSize)
|
||||
{
|
||||
void *Address = Xalloc_REQUEST_PAGES(Pages);
|
||||
void *VirtualAddress = (void *)(this->BaseVirtualAddress + this->HeapSize);
|
||||
if (Xalloc_MapPages)
|
||||
{
|
||||
for (Xsize_t i = 0; i < Pages; i++)
|
||||
{
|
||||
Xuintptr_t Page = i * Xalloc_PAGE_SIZE;
|
||||
void *vAddress = (void *)((Xuintptr_t)VirtualAddress + Page);
|
||||
Xalloc_MAP_MEMORY(vAddress, (void *)((Xuintptr_t)Address + Page), 0x3);
|
||||
}
|
||||
}
|
||||
|
||||
this->HeapSize += XPtoS(Pages);
|
||||
FinalAddress = (Xuint8_t *)VirtualAddress;
|
||||
}
|
||||
else
|
||||
FinalAddress = (Xuint8_t *)(this->BaseVirtualAddress + this->HeapUsed);
|
||||
|
||||
this->HeapUsed += Size;
|
||||
return (uint8_t *)FinalAddress;
|
||||
}
|
||||
|
||||
void V2::FreeHeap(Xuint8_t *At, Xsize_t Size)
|
||||
{
|
||||
Xsize_t Pages = XStoP(Size);
|
||||
|
||||
if (Xalloc_MapPages)
|
||||
{
|
||||
for (Xsize_t i = 0; i < Pages; i++)
|
||||
{
|
||||
Xuintptr_t Page = i * Xalloc_PAGE_SIZE;
|
||||
void *VirtualAddress = (void *)((Xuintptr_t)At + Page);
|
||||
Xalloc_UNMAP_MEMORY(VirtualAddress);
|
||||
}
|
||||
}
|
||||
|
||||
Xalloc_FREE_PAGES(At, Pages);
|
||||
this->HeapUsed -= Size;
|
||||
}
|
||||
|
||||
Xsize_t V2::Align(Xsize_t Size)
|
||||
{
|
||||
return (Size + 0xF) & ~0xF;
|
||||
}
|
||||
|
||||
void *V2::FindFreeBlock(Xsize_t Size, Block *&CurrentBlock)
|
||||
{
|
||||
if (this->FirstBlock == nullptr)
|
||||
{
|
||||
this->FirstBlock = new Block(Size, this);
|
||||
this->FirstBlock->IsFree = false;
|
||||
return this->FirstBlock->Data;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
CurrentBlock->Check();
|
||||
|
||||
/* FIXME: This will waste a lot of space
|
||||
need better algorithm */
|
||||
if (CurrentBlock->IsFree &&
|
||||
CurrentBlock->DataSize >= Size)
|
||||
{
|
||||
CurrentBlock->IsFree = false;
|
||||
return CurrentBlock->Data;
|
||||
}
|
||||
|
||||
if (CurrentBlock->Next == nullptr)
|
||||
break;
|
||||
|
||||
CurrentBlock = CurrentBlock->Next;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void V2::Arrange()
|
||||
{
|
||||
Xalloc_err("Arrange() is not implemented yet!");
|
||||
}
|
||||
|
||||
void *V2::malloc(Xsize_t Size)
|
||||
{
|
||||
if (Size == 0)
|
||||
{
|
||||
Xalloc_warn("Attempted to allocate 0 bytes!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
XallocV2_lock;
|
||||
Block *CurrentBlock = this->FirstBlock;
|
||||
void *ret = this->FindFreeBlock(Size, CurrentBlock);
|
||||
if (ret)
|
||||
{
|
||||
XallocV2_unlock;
|
||||
return ret;
|
||||
}
|
||||
|
||||
CurrentBlock->Next = new Block(Size, this);
|
||||
CurrentBlock->Next->IsFree = false;
|
||||
XallocV2_unlock;
|
||||
return CurrentBlock->Next->Data;
|
||||
}
|
||||
|
||||
void V2::free(void *Address)
|
||||
{
|
||||
if (Address == nullptr)
|
||||
{
|
||||
Xalloc_warn("Attempted to free a null pointer!");
|
||||
return;
|
||||
}
|
||||
|
||||
XallocV2_lock;
|
||||
|
||||
Block *CurrentBlock = ((Block *)this->FirstBlock);
|
||||
while (CurrentBlock != nullptr)
|
||||
{
|
||||
CurrentBlock->Check();
|
||||
|
||||
if (CurrentBlock->Data == Address)
|
||||
{
|
||||
if (CurrentBlock->IsFree)
|
||||
Xalloc_warn("Attempted to free an already freed block! %#lx", Address);
|
||||
|
||||
CurrentBlock->IsFree = true;
|
||||
XallocV2_unlock;
|
||||
return;
|
||||
}
|
||||
CurrentBlock = CurrentBlock->Next;
|
||||
}
|
||||
|
||||
Xalloc_err("Invalid address %#lx.", Address);
|
||||
XallocV2_unlock;
|
||||
}
|
||||
|
||||
void *V2::calloc(Xsize_t NumberOfBlocks, Xsize_t Size)
|
||||
{
|
||||
if (NumberOfBlocks == 0 || Size == 0)
|
||||
{
|
||||
Xalloc_warn("The %s%s%s is 0!",
|
||||
NumberOfBlocks == 0 ? "NumberOfBlocks" : "",
|
||||
NumberOfBlocks == 0 && Size == 0 ? " and " : "",
|
||||
Size == 0 ? "Size" : "");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return this->malloc(NumberOfBlocks * Size);
|
||||
}
|
||||
|
||||
void *V2::realloc(void *Address, Xsize_t Size)
|
||||
{
|
||||
if (Address == nullptr && Size != 0)
|
||||
return this->malloc(Size);
|
||||
|
||||
if (Size == 0)
|
||||
{
|
||||
this->free(Address);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// XallocV2_lock;
|
||||
// ...
|
||||
// XallocV2_unlock;
|
||||
|
||||
// TODO: Implement realloc
|
||||
static int once = 0;
|
||||
if (!once++)
|
||||
Xalloc_trace("realloc is stub!");
|
||||
this->free(Address);
|
||||
return this->malloc(Size);
|
||||
}
|
||||
|
||||
V2::V2(void *VirtualBase)
|
||||
{
|
||||
if (VirtualBase == 0x0 && Xalloc_MapPages)
|
||||
{
|
||||
Xalloc_err("VirtualBase is 0x0 and Xalloc_MapPages is true!");
|
||||
while (true)
|
||||
;
|
||||
}
|
||||
|
||||
XallocV2_lock;
|
||||
this->BaseVirtualAddress = Xuintptr_t(VirtualBase);
|
||||
XallocV2_unlock;
|
||||
}
|
||||
|
||||
V2::~V2()
|
||||
{
|
||||
XallocV2_lock;
|
||||
Xalloc_trace("Destructor not implemented yet.");
|
||||
XallocV2_unlock;
|
||||
}
|
||||
}
|
796
Kernel/core/memory/heap_allocators/liballoc_1_1/liballoc_1_1.c
Normal file
796
Kernel/core/memory/heap_allocators/liballoc_1_1/liballoc_1_1.c
Normal file
@ -0,0 +1,796 @@
|
||||
#include "liballoc_1_1.h"
|
||||
#include <convert.h>
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wconversion"
|
||||
#pragma GCC diagnostic ignored "-Wsign-conversion"
|
||||
|
||||
/** Durand's Amazing Super Duper Memory functions. */
|
||||
|
||||
#define VERSION "1.1"
|
||||
#define ALIGNMENT 16ul // 4ul ///< This is the byte alignment that memory must be allocated on. IMPORTANT for GTK and other stuff.
|
||||
|
||||
#define ALIGN_TYPE char /// unsigned char[16] /// unsigned short
|
||||
#define ALIGN_INFO sizeof(ALIGN_TYPE) * 16 ///< Alignment information is stored right before the pointer. This is the number of bytes of information stored there.
|
||||
|
||||
#define USE_CASE1
|
||||
#define USE_CASE2
|
||||
#define USE_CASE3
|
||||
#define USE_CASE4
|
||||
#define USE_CASE5
|
||||
|
||||
/** This macro will conveniently align our pointer upwards */
|
||||
#define ALIGN(ptr) \
|
||||
if (ALIGNMENT > 1) \
|
||||
{ \
|
||||
uintptr_t diff; \
|
||||
ptr = (void *)((uintptr_t)ptr + ALIGN_INFO); \
|
||||
diff = (uintptr_t)ptr & (ALIGNMENT - 1); \
|
||||
if (diff != 0) \
|
||||
{ \
|
||||
diff = ALIGNMENT - diff; \
|
||||
ptr = (void *)((uintptr_t)ptr + diff); \
|
||||
} \
|
||||
*((ALIGN_TYPE *)((uintptr_t)ptr - ALIGN_INFO)) = \
|
||||
diff + ALIGN_INFO; \
|
||||
}
|
||||
|
||||
#define UNALIGN(ptr) \
|
||||
if (ALIGNMENT > 1) \
|
||||
{ \
|
||||
uintptr_t diff = *((ALIGN_TYPE *)((uintptr_t)ptr - ALIGN_INFO)); \
|
||||
if (diff < (ALIGNMENT + ALIGN_INFO)) \
|
||||
{ \
|
||||
ptr = (void *)((uintptr_t)ptr - diff); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define LIBALLOC_MAGIC 0xc001c0de
|
||||
#define LIBALLOC_DEAD 0xdeaddead
|
||||
|
||||
// #define LIBALLOCDEBUG 1
|
||||
#define LIBALLOCINFO 1
|
||||
|
||||
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
|
||||
// #include <stdio.h>
|
||||
// #include <stdlib.h>
|
||||
#include <debug.h>
|
||||
|
||||
// #define FLUSH() fflush(stdout)
|
||||
#define FLUSH()
|
||||
#define atexit(x)
|
||||
#define printf(m, ...) trace(m, ##__VA_ARGS__)
|
||||
|
||||
#endif
|
||||
|
||||
/** A structure found at the top of all system allocated
|
||||
* memory blocks. It details the usage of the memory block.
|
||||
*/
|
||||
struct liballoc_major
|
||||
{
|
||||
struct liballoc_major *prev; ///< Linked list information.
|
||||
struct liballoc_major *next; ///< Linked list information.
|
||||
unsigned int pages; ///< The number of pages in the block.
|
||||
unsigned int size; ///< The number of pages in the block.
|
||||
unsigned int usage; ///< The number of bytes used in the block.
|
||||
struct liballoc_minor *first; ///< A pointer to the first allocated memory in the block.
|
||||
};
|
||||
|
||||
/** This is a structure found at the beginning of all
|
||||
* sections in a major block which were allocated by a
|
||||
* malloc, calloc, realloc call.
|
||||
*/
|
||||
struct liballoc_minor
|
||||
{
|
||||
struct liballoc_minor *prev; ///< Linked list information.
|
||||
struct liballoc_minor *next; ///< Linked list information.
|
||||
struct liballoc_major *block; ///< The owning block. A pointer to the major structure.
|
||||
unsigned int magic; ///< A magic number to idenfity correctness.
|
||||
unsigned int size; ///< The size of the memory allocated. Could be 1 byte or more.
|
||||
unsigned int req_size; ///< The size of memory requested.
|
||||
};
|
||||
|
||||
static struct liballoc_major *l_memRoot = NULL; ///< The root memory block acquired from the system.
|
||||
static struct liballoc_major *l_bestBet = NULL; ///< The major with the most free memory.
|
||||
|
||||
static unsigned int l_pageSize = 4096; ///< The size of an individual page. Set up in liballoc_init.
|
||||
static unsigned int l_pageCount = 16; ///< The number of pages to request per chunk. Set up in liballoc_init.
|
||||
static unsigned long long l_allocated = 0; ///< Running total of allocated memory.
|
||||
static unsigned long long l_inuse = 0; ///< Running total of used memory.
|
||||
|
||||
static long long l_warningCount = 0; ///< Number of warnings encountered
|
||||
static long long l_errorCount = 0; ///< Number of actual errors
|
||||
static long long l_possibleOverruns = 0; ///< Number of possible overruns
|
||||
|
||||
// *********** HELPER FUNCTIONS *******************************
|
||||
|
||||
__no_sanitize("undefined") static void *liballoc_memset(void *s, int c, size_t n)
|
||||
{
|
||||
return memset(s, c, n);
|
||||
unsigned int i;
|
||||
for (i = 0; i < n; i++)
|
||||
((char *)s)[i] = c;
|
||||
|
||||
return s;
|
||||
}
|
||||
__no_sanitize("undefined") static void *liballoc_memcpy(void *s1, const void *s2, size_t n)
|
||||
{
|
||||
return memcpy(s1, s2, n);
|
||||
char *cdest;
|
||||
char *csrc;
|
||||
unsigned int *ldest = (unsigned int *)s1;
|
||||
unsigned int *lsrc = (unsigned int *)s2;
|
||||
|
||||
while (n >= sizeof(unsigned int))
|
||||
{
|
||||
*ldest++ = *lsrc++;
|
||||
n -= sizeof(unsigned int);
|
||||
}
|
||||
|
||||
cdest = (char *)ldest;
|
||||
csrc = (char *)lsrc;
|
||||
|
||||
while (n > 0)
|
||||
{
|
||||
*cdest++ = *csrc++;
|
||||
n -= 1;
|
||||
}
|
||||
|
||||
return s1;
|
||||
}
|
||||
|
||||
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
|
||||
__no_sanitize("undefined") static void liballoc_dump()
|
||||
{
|
||||
#ifdef LIBALLOCDEBUG
|
||||
struct liballoc_major *maj = l_memRoot;
|
||||
struct liballoc_minor *min = NULL;
|
||||
#endif
|
||||
|
||||
printf("liballoc: ------ Memory data ---------------\n");
|
||||
printf("liballoc: System memory allocated: %i bytes\n", l_allocated);
|
||||
printf("liballoc: Memory in used (malloc'ed): %i bytes\n", l_inuse);
|
||||
printf("liballoc: Warning count: %i\n", l_warningCount);
|
||||
printf("liballoc: Error count: %i\n", l_errorCount);
|
||||
printf("liballoc: Possible overruns: %i\n", l_possibleOverruns);
|
||||
|
||||
#ifdef LIBALLOCDEBUG
|
||||
while (maj != NULL)
|
||||
{
|
||||
printf("liballoc: %lx: total = %i, used = %i\n",
|
||||
maj,
|
||||
maj->size,
|
||||
maj->usage);
|
||||
|
||||
min = maj->first;
|
||||
while (min != NULL)
|
||||
{
|
||||
printf("liballoc: %lx: %i bytes\n",
|
||||
min,
|
||||
min->size);
|
||||
min = min->next;
|
||||
}
|
||||
|
||||
maj = maj->next;
|
||||
}
|
||||
#endif
|
||||
|
||||
FLUSH();
|
||||
}
|
||||
#endif
|
||||
|
||||
// ***************************************************************
|
||||
|
||||
__no_sanitize("undefined") static struct liballoc_major *allocate_new_page(unsigned int size)
|
||||
{
|
||||
unsigned int st;
|
||||
struct liballoc_major *maj;
|
||||
|
||||
// This is how much space is required.
|
||||
st = size + sizeof(struct liballoc_major);
|
||||
st += sizeof(struct liballoc_minor);
|
||||
|
||||
// Perfect amount of space?
|
||||
if ((st % l_pageSize) == 0)
|
||||
st = st / (l_pageSize);
|
||||
else
|
||||
st = st / (l_pageSize) + 1;
|
||||
// No, add the buffer.
|
||||
|
||||
// Make sure it's >= the minimum size.
|
||||
if (st < l_pageCount)
|
||||
st = l_pageCount;
|
||||
|
||||
maj = (struct liballoc_major *)liballoc_alloc(st);
|
||||
|
||||
if (maj == NULL)
|
||||
{
|
||||
l_warningCount += 1;
|
||||
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
|
||||
printf("liballoc: WARNING: liballoc_alloc( %i ) return NULL\n", st);
|
||||
FLUSH();
|
||||
#endif
|
||||
return NULL; // uh oh, we ran out of memory.
|
||||
}
|
||||
|
||||
maj->prev = NULL;
|
||||
maj->next = NULL;
|
||||
maj->pages = st;
|
||||
maj->size = st * l_pageSize;
|
||||
maj->usage = sizeof(struct liballoc_major);
|
||||
maj->first = NULL;
|
||||
|
||||
l_allocated += maj->size;
|
||||
|
||||
#ifdef LIBALLOCDEBUG
|
||||
printf("liballoc: Resource allocated %lx of %i pages (%i bytes) for %i size.\n", maj, st, maj->size, size);
|
||||
|
||||
printf("liballoc: Total memory usage = %i KB\n", (int)((l_allocated / (1024))));
|
||||
FLUSH();
|
||||
#endif
|
||||
|
||||
return maj;
|
||||
}
|
||||
|
||||
__no_sanitize("undefined") void *PREFIX(malloc)(size_t req_size)
|
||||
{
|
||||
int startedBet = 0;
|
||||
unsigned long long bestSize = 0;
|
||||
void *p = NULL;
|
||||
uintptr_t diff;
|
||||
struct liballoc_major *maj;
|
||||
struct liballoc_minor *min;
|
||||
struct liballoc_minor *new_min;
|
||||
unsigned long size = req_size;
|
||||
|
||||
// For alignment, we adjust size so there's enough space to align.
|
||||
if (ALIGNMENT > 1)
|
||||
{
|
||||
size += ALIGNMENT + ALIGN_INFO;
|
||||
}
|
||||
// So, ideally, we really want an alignment of 0 or 1 in order
|
||||
// to save space.
|
||||
|
||||
liballoc_lock();
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
l_warningCount += 1;
|
||||
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
|
||||
printf("liballoc: WARNING: alloc( 0 ) called from %lx\n",
|
||||
__builtin_return_address(0));
|
||||
FLUSH();
|
||||
#endif
|
||||
liballoc_unlock();
|
||||
return PREFIX(malloc)(1);
|
||||
}
|
||||
|
||||
if (l_memRoot == NULL)
|
||||
{
|
||||
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
|
||||
#ifdef LIBALLOCDEBUG
|
||||
printf("liballoc: initialization of liballoc " VERSION "\n");
|
||||
#endif
|
||||
atexit(liballoc_dump);
|
||||
FLUSH();
|
||||
#endif
|
||||
|
||||
// This is the first time we are being used.
|
||||
l_memRoot = allocate_new_page(size);
|
||||
if (l_memRoot == NULL)
|
||||
{
|
||||
liballoc_unlock();
|
||||
#ifdef LIBALLOCDEBUG
|
||||
printf("liballoc: initial l_memRoot initialization failed\n", p);
|
||||
FLUSH();
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef LIBALLOCDEBUG
|
||||
printf("liballoc: set up first memory major %lx\n", l_memRoot);
|
||||
FLUSH();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef LIBALLOCDEBUG
|
||||
printf("liballoc: %lx PREFIX(malloc)( %i ): ",
|
||||
__builtin_return_address(0),
|
||||
size);
|
||||
FLUSH();
|
||||
#endif
|
||||
|
||||
// Now we need to bounce through every major and find enough space....
|
||||
|
||||
maj = l_memRoot;
|
||||
startedBet = 0;
|
||||
|
||||
// Start at the best bet....
|
||||
if (l_bestBet != NULL)
|
||||
{
|
||||
bestSize = l_bestBet->size - l_bestBet->usage;
|
||||
|
||||
if (bestSize > (size + sizeof(struct liballoc_minor)))
|
||||
{
|
||||
maj = l_bestBet;
|
||||
startedBet = 1;
|
||||
}
|
||||
}
|
||||
|
||||
while (maj != NULL)
|
||||
{
|
||||
diff = maj->size - maj->usage;
|
||||
// free memory in the block
|
||||
|
||||
if (bestSize < diff)
|
||||
{
|
||||
// Hmm.. this one has more memory then our bestBet. Remember!
|
||||
l_bestBet = maj;
|
||||
bestSize = diff;
|
||||
}
|
||||
|
||||
#ifdef USE_CASE1
|
||||
|
||||
// CASE 1: There is not enough space in this major block.
|
||||
if (diff < (size + sizeof(struct liballoc_minor)))
|
||||
{
|
||||
#ifdef LIBALLOCDEBUG
|
||||
printf("CASE 1: Insufficient space in block %lx\n", maj);
|
||||
FLUSH();
|
||||
#endif
|
||||
|
||||
// Another major block next to this one?
|
||||
if (maj->next != NULL)
|
||||
{
|
||||
maj = maj->next; // Hop to that one.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (startedBet == 1) // If we started at the best bet,
|
||||
{ // let's start all over again.
|
||||
maj = l_memRoot;
|
||||
startedBet = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create a new major block next to this one and...
|
||||
maj->next = allocate_new_page(size); // next one will be okay.
|
||||
if (maj->next == NULL)
|
||||
break; // no more memory.
|
||||
maj->next->prev = maj;
|
||||
maj = maj->next;
|
||||
|
||||
// .. fall through to CASE 2 ..
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_CASE2
|
||||
|
||||
// CASE 2: It's a brand new block.
|
||||
if (maj->first == NULL)
|
||||
{
|
||||
maj->first = (struct liballoc_minor *)((uintptr_t)maj + sizeof(struct liballoc_major));
|
||||
|
||||
maj->first->magic = LIBALLOC_MAGIC;
|
||||
maj->first->prev = NULL;
|
||||
maj->first->next = NULL;
|
||||
maj->first->block = maj;
|
||||
maj->first->size = size;
|
||||
maj->first->req_size = req_size;
|
||||
maj->usage += size + sizeof(struct liballoc_minor);
|
||||
|
||||
l_inuse += size;
|
||||
|
||||
p = (void *)((uintptr_t)(maj->first) + sizeof(struct liballoc_minor));
|
||||
|
||||
ALIGN(p);
|
||||
|
||||
#ifdef LIBALLOCDEBUG
|
||||
printf("CASE 2: returning %lx\n", p);
|
||||
FLUSH();
|
||||
#endif
|
||||
liballoc_unlock(); // release the lock
|
||||
return p;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_CASE3
|
||||
|
||||
// CASE 3: Block in use and enough space at the start of the block.
|
||||
diff = (uintptr_t)(maj->first);
|
||||
diff -= (uintptr_t)maj;
|
||||
diff -= sizeof(struct liballoc_major);
|
||||
|
||||
if (diff >= (size + sizeof(struct liballoc_minor)))
|
||||
{
|
||||
// Yes, space in front. Squeeze in.
|
||||
maj->first->prev = (struct liballoc_minor *)((uintptr_t)maj + sizeof(struct liballoc_major));
|
||||
maj->first->prev->next = maj->first;
|
||||
maj->first = maj->first->prev;
|
||||
|
||||
maj->first->magic = LIBALLOC_MAGIC;
|
||||
maj->first->prev = NULL;
|
||||
maj->first->block = maj;
|
||||
maj->first->size = size;
|
||||
maj->first->req_size = req_size;
|
||||
maj->usage += size + sizeof(struct liballoc_minor);
|
||||
|
||||
l_inuse += size;
|
||||
|
||||
p = (void *)((uintptr_t)(maj->first) + sizeof(struct liballoc_minor));
|
||||
ALIGN(p);
|
||||
|
||||
#ifdef LIBALLOCDEBUG
|
||||
printf("CASE 3: returning %lx\n", p);
|
||||
FLUSH();
|
||||
#endif
|
||||
liballoc_unlock(); // release the lock
|
||||
return p;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_CASE4
|
||||
|
||||
// CASE 4: There is enough space in this block. But is it contiguous?
|
||||
min = maj->first;
|
||||
|
||||
// Looping within the block now...
|
||||
while (min != NULL)
|
||||
{
|
||||
// CASE 4.1: End of minors in a block. Space from last and end?
|
||||
if (min->next == NULL)
|
||||
{
|
||||
// the rest of this block is free... is it big enough?
|
||||
diff = (uintptr_t)(maj) + maj->size;
|
||||
diff -= (uintptr_t)min;
|
||||
diff -= sizeof(struct liballoc_minor);
|
||||
diff -= min->size;
|
||||
// minus already existing usage..
|
||||
|
||||
if (diff >= (size + sizeof(struct liballoc_minor)))
|
||||
{
|
||||
// yay....
|
||||
min->next = (struct liballoc_minor *)((uintptr_t)min + sizeof(struct liballoc_minor) + min->size);
|
||||
min->next->prev = min;
|
||||
min = min->next;
|
||||
min->next = NULL;
|
||||
min->magic = LIBALLOC_MAGIC;
|
||||
min->block = maj;
|
||||
min->size = size;
|
||||
min->req_size = req_size;
|
||||
maj->usage += size + sizeof(struct liballoc_minor);
|
||||
|
||||
l_inuse += size;
|
||||
|
||||
p = (void *)((uintptr_t)min + sizeof(struct liballoc_minor));
|
||||
ALIGN(p);
|
||||
|
||||
#ifdef LIBALLOCDEBUG
|
||||
printf("CASE 4.1: returning %lx\n", p);
|
||||
FLUSH();
|
||||
#endif
|
||||
liballoc_unlock(); // release the lock
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
// CASE 4.2: Is there space between two minors?
|
||||
if (min->next != NULL)
|
||||
{
|
||||
// is the difference between here and next big enough?
|
||||
diff = (uintptr_t)(min->next);
|
||||
diff -= (uintptr_t)min;
|
||||
diff -= sizeof(struct liballoc_minor);
|
||||
diff -= min->size;
|
||||
// minus our existing usage.
|
||||
|
||||
if (diff >= (size + sizeof(struct liballoc_minor)))
|
||||
{
|
||||
// yay......
|
||||
new_min = (struct liballoc_minor *)((uintptr_t)min + sizeof(struct liballoc_minor) + min->size);
|
||||
|
||||
new_min->magic = LIBALLOC_MAGIC;
|
||||
new_min->next = min->next;
|
||||
new_min->prev = min;
|
||||
new_min->size = size;
|
||||
new_min->req_size = req_size;
|
||||
new_min->block = maj;
|
||||
min->next->prev = new_min;
|
||||
min->next = new_min;
|
||||
maj->usage += size + sizeof(struct liballoc_minor);
|
||||
|
||||
l_inuse += size;
|
||||
|
||||
p = (void *)((uintptr_t)new_min + sizeof(struct liballoc_minor));
|
||||
ALIGN(p);
|
||||
|
||||
#ifdef LIBALLOCDEBUG
|
||||
printf("CASE 4.2: returning %lx\n", p);
|
||||
FLUSH();
|
||||
#endif
|
||||
|
||||
liballoc_unlock(); // release the lock
|
||||
return p;
|
||||
}
|
||||
} // min->next != NULL
|
||||
|
||||
min = min->next;
|
||||
} // while min != NULL ...
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef USE_CASE5
|
||||
|
||||
// CASE 5: Block full! Ensure next block and loop.
|
||||
if (maj->next == NULL)
|
||||
{
|
||||
#ifdef LIBALLOCDEBUG
|
||||
printf("CASE 5: block full\n");
|
||||
FLUSH();
|
||||
#endif
|
||||
|
||||
if (startedBet == 1)
|
||||
{
|
||||
maj = l_memRoot;
|
||||
startedBet = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
// we've run out. we need more...
|
||||
maj->next = allocate_new_page(size); // next one guaranteed to be okay
|
||||
if (maj->next == NULL)
|
||||
break; // uh oh, no more memory.....
|
||||
maj->next->prev = maj;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
maj = maj->next;
|
||||
} // while (maj != NULL)
|
||||
|
||||
liballoc_unlock(); // release the lock
|
||||
|
||||
#ifdef LIBALLOCDEBUG
|
||||
printf("All cases exhausted. No memory available.\n");
|
||||
FLUSH();
|
||||
#endif
|
||||
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
|
||||
printf("liballoc: WARNING: PREFIX(malloc)( %i ) returning NULL.\n", size);
|
||||
liballoc_dump();
|
||||
FLUSH();
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__no_sanitize("undefined") void PREFIX(free)(void *ptr)
|
||||
{
|
||||
struct liballoc_minor *min;
|
||||
struct liballoc_major *maj;
|
||||
|
||||
if (ptr == NULL)
|
||||
{
|
||||
l_warningCount += 1;
|
||||
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
|
||||
printf("liballoc: WARNING: PREFIX(free)( NULL ) called from %lx\n",
|
||||
__builtin_return_address(0));
|
||||
FLUSH();
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
UNALIGN(ptr);
|
||||
|
||||
liballoc_lock(); // lockit
|
||||
|
||||
min = (struct liballoc_minor *)((uintptr_t)ptr - sizeof(struct liballoc_minor));
|
||||
|
||||
if (min->magic != LIBALLOC_MAGIC)
|
||||
{
|
||||
l_errorCount += 1;
|
||||
|
||||
// Check for overrun errors. For all bytes of LIBALLOC_MAGIC
|
||||
if (
|
||||
((min->magic & 0xFFFFFF) == (LIBALLOC_MAGIC & 0xFFFFFF)) ||
|
||||
((min->magic & 0xFFFF) == (LIBALLOC_MAGIC & 0xFFFF)) ||
|
||||
((min->magic & 0xFF) == (LIBALLOC_MAGIC & 0xFF)))
|
||||
{
|
||||
l_possibleOverruns += 1;
|
||||
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
|
||||
printf("liballoc: ERROR: Possible 1-3 byte overrun for magic %lx != %lx\n",
|
||||
min->magic,
|
||||
LIBALLOC_MAGIC);
|
||||
FLUSH();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (min->magic == LIBALLOC_DEAD)
|
||||
{
|
||||
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
|
||||
printf("liballoc: ERROR: multiple PREFIX(free)() attempt on %lx from %lx.\n",
|
||||
ptr,
|
||||
__builtin_return_address(0));
|
||||
FLUSH();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
|
||||
printf("liballoc: ERROR: Bad PREFIX(free)( %lx ) called from %lx\n",
|
||||
ptr,
|
||||
__builtin_return_address(0));
|
||||
FLUSH();
|
||||
#endif
|
||||
}
|
||||
|
||||
// being lied to...
|
||||
liballoc_unlock(); // release the lock
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef LIBALLOCDEBUG
|
||||
printf("liballoc: %lx PREFIX(free)( %lx ): ",
|
||||
__builtin_return_address(0),
|
||||
ptr);
|
||||
FLUSH();
|
||||
#endif
|
||||
|
||||
maj = min->block;
|
||||
|
||||
l_inuse -= min->size;
|
||||
|
||||
maj->usage -= (min->size + sizeof(struct liballoc_minor));
|
||||
min->magic = LIBALLOC_DEAD; // No mojo.
|
||||
|
||||
if (min->next != NULL)
|
||||
min->next->prev = min->prev;
|
||||
if (min->prev != NULL)
|
||||
min->prev->next = min->next;
|
||||
|
||||
if (min->prev == NULL)
|
||||
maj->first = min->next;
|
||||
// Might empty the block. This was the first
|
||||
// minor.
|
||||
|
||||
// We need to clean up after the majors now....
|
||||
|
||||
if (maj->first == NULL) // Block completely unused.
|
||||
{
|
||||
if (l_memRoot == maj)
|
||||
l_memRoot = maj->next;
|
||||
if (l_bestBet == maj)
|
||||
l_bestBet = NULL;
|
||||
if (maj->prev != NULL)
|
||||
maj->prev->next = maj->next;
|
||||
if (maj->next != NULL)
|
||||
maj->next->prev = maj->prev;
|
||||
l_allocated -= maj->size;
|
||||
|
||||
liballoc_free(maj, maj->pages);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (l_bestBet != NULL)
|
||||
{
|
||||
int bestSize = l_bestBet->size - l_bestBet->usage;
|
||||
int majSize = maj->size - maj->usage;
|
||||
|
||||
if (majSize > bestSize)
|
||||
l_bestBet = maj;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LIBALLOCDEBUG
|
||||
printf("OK\n");
|
||||
FLUSH();
|
||||
#endif
|
||||
|
||||
liballoc_unlock(); // release the lock
|
||||
}
|
||||
|
||||
__no_sanitize("undefined") void *PREFIX(calloc)(size_t nobj, size_t size)
|
||||
{
|
||||
int real_size;
|
||||
void *p;
|
||||
|
||||
real_size = nobj * size;
|
||||
|
||||
p = PREFIX(malloc)(real_size);
|
||||
|
||||
liballoc_memset(p, 0, real_size);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
__no_sanitize("undefined") void *PREFIX(realloc)(void *p, size_t size)
|
||||
{
|
||||
void *ptr;
|
||||
struct liballoc_minor *min;
|
||||
unsigned int real_size;
|
||||
|
||||
// Honour the case of size == 0 => free old and return NULL
|
||||
if (size == 0)
|
||||
{
|
||||
PREFIX(free)
|
||||
(p);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// In the case of a NULL pointer, return a simple malloc.
|
||||
if (p == NULL)
|
||||
return PREFIX(malloc)(size);
|
||||
|
||||
// Unalign the pointer if required.
|
||||
ptr = p;
|
||||
UNALIGN(ptr);
|
||||
|
||||
liballoc_lock(); // lockit
|
||||
|
||||
min = (struct liballoc_minor *)((uintptr_t)ptr - sizeof(struct liballoc_minor));
|
||||
|
||||
// Ensure it is a valid structure.
|
||||
if (min->magic != LIBALLOC_MAGIC)
|
||||
{
|
||||
l_errorCount += 1;
|
||||
|
||||
// Check for overrun errors. For all bytes of LIBALLOC_MAGIC
|
||||
if (
|
||||
((min->magic & 0xFFFFFF) == (LIBALLOC_MAGIC & 0xFFFFFF)) ||
|
||||
((min->magic & 0xFFFF) == (LIBALLOC_MAGIC & 0xFFFF)) ||
|
||||
((min->magic & 0xFF) == (LIBALLOC_MAGIC & 0xFF)))
|
||||
{
|
||||
l_possibleOverruns += 1;
|
||||
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
|
||||
printf("liballoc: ERROR: Possible 1-3 byte overrun for magic %lx != %lx\n",
|
||||
min->magic,
|
||||
LIBALLOC_MAGIC);
|
||||
FLUSH();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (min->magic == LIBALLOC_DEAD)
|
||||
{
|
||||
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
|
||||
printf("liballoc: ERROR: multiple PREFIX(free)() attempt on %lx from %lx.\n",
|
||||
ptr,
|
||||
__builtin_return_address(0));
|
||||
FLUSH();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
|
||||
printf("liballoc: ERROR: Bad PREFIX(free)( %lx ) called from %lx\n",
|
||||
ptr,
|
||||
__builtin_return_address(0));
|
||||
FLUSH();
|
||||
#endif
|
||||
}
|
||||
|
||||
// being lied to...
|
||||
liballoc_unlock(); // release the lock
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Definitely a memory block.
|
||||
|
||||
real_size = min->req_size;
|
||||
|
||||
if (real_size >= size)
|
||||
{
|
||||
min->req_size = size;
|
||||
liballoc_unlock();
|
||||
return p;
|
||||
}
|
||||
|
||||
liballoc_unlock();
|
||||
|
||||
// If we got here then we're reallocating to a block bigger than us.
|
||||
ptr = PREFIX(malloc)(size); // We need to allocate new memory
|
||||
liballoc_memcpy(ptr, p, real_size);
|
||||
PREFIX(free)
|
||||
(p);
|
||||
|
||||
return ptr;
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
#ifndef _LIBALLOC_H
|
||||
#define _LIBALLOC_H
|
||||
|
||||
#include <types.h>
|
||||
|
||||
/** \defgroup ALLOCHOOKS liballoc hooks
|
||||
*
|
||||
* These are the OS specific functions which need to
|
||||
* be implemented on any platform that the library
|
||||
* is expected to work on.
|
||||
*/
|
||||
|
||||
/** @{ */
|
||||
|
||||
// If we are told to not define our own size_t, then we skip the define.
|
||||
// #define _HAVE_UINTPTR_T
|
||||
// typedef unsigned long uintptr_t;
|
||||
|
||||
// This lets you prefix malloc and friends
|
||||
#define PREFIX(func) kliballoc_##func
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/** This function is supposed to lock the memory data structures. It
|
||||
* could be as simple as disabling interrupts or acquiring a spinlock.
|
||||
* It's up to you to decide.
|
||||
*
|
||||
* \return 0 if the lock was acquired successfully. Anything else is
|
||||
* failure.
|
||||
*/
|
||||
extern int liballoc_lock();
|
||||
|
||||
/** This function unlocks what was previously locked by the liballoc_lock
|
||||
* function. If it disabled interrupts, it enables interrupts. If it
|
||||
* had acquiried a spinlock, it releases the spinlock. etc.
|
||||
*
|
||||
* \return 0 if the lock was successfully released.
|
||||
*/
|
||||
extern int liballoc_unlock();
|
||||
|
||||
/** This is the hook into the local system which allocates pages. It
|
||||
* accepts an integer parameter which is the number of pages
|
||||
* required. The page size was set up in the liballoc_init function.
|
||||
*
|
||||
* \return NULL if the pages were not allocated.
|
||||
* \return A pointer to the allocated memory.
|
||||
*/
|
||||
extern void *liballoc_alloc(size_t);
|
||||
|
||||
/** This frees previously allocated memory. The void* parameter passed
|
||||
* to the function is the exact same value returned from a previous
|
||||
* liballoc_alloc call.
|
||||
*
|
||||
* The integer value is the number of pages to free.
|
||||
*
|
||||
* \return 0 if the memory was successfully freed.
|
||||
*/
|
||||
extern int liballoc_free(void *, size_t);
|
||||
|
||||
extern void *PREFIX(malloc)(size_t); ///< The standard function.
|
||||
extern void *PREFIX(realloc)(void *, size_t); ///< The standard function.
|
||||
extern void *PREFIX(calloc)(size_t, size_t); ///< The standard function.
|
||||
extern void PREFIX(free)(void *); ///< The standard function.
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif
|
@ -0,0 +1,29 @@
|
||||
#include <types.h>
|
||||
#include <lock.hpp>
|
||||
#include <memory.hpp>
|
||||
|
||||
NewLock(liballocLock);
|
||||
|
||||
EXTERNC int liballoc_lock()
|
||||
{
|
||||
return liballocLock.Lock(__FUNCTION__);
|
||||
}
|
||||
|
||||
EXTERNC int liballoc_unlock()
|
||||
{
|
||||
return liballocLock.Unlock();
|
||||
}
|
||||
|
||||
EXTERNC void *liballoc_alloc(size_t Pages)
|
||||
{
|
||||
void *ret = KernelAllocator.RequestPages(Pages);
|
||||
debug("(%d) = %#lx", Pages, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXTERNC int liballoc_free(void *Address, size_t Pages)
|
||||
{
|
||||
debug("(%#lx, %d)", Address, Pages);
|
||||
KernelAllocator.FreePages(Address, Pages);
|
||||
return 0;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user