├── .clang-format ├── .editorconfig ├── .github └── PULL_REQUEST_TEMPLATE ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json └── tasks.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CONTRIBUTORS ├── COPYING ├── Doxyfile ├── Makefile ├── README.md ├── arch └── i386 │ ├── include │ └── kernel │ │ ├── cpu │ │ ├── idt.h │ │ └── isrdef.h │ │ └── mem │ │ ├── heap.h │ │ └── pmm.h │ └── kernel │ ├── cpu │ ├── idt.c │ └── lidt.S │ └── mem │ ├── alloc.c │ ├── heap.c │ └── pmm.c ├── conf ├── I386 ├── Makefile.i386 ├── files ├── files.i386 └── gdbinit.I386 ├── kernel ├── device │ ├── kbd.c │ ├── null.c │ ├── pctimer.c │ ├── rtclock.c │ ├── uart8250.c │ ├── vgafb.c │ ├── vgafb.h │ └── vtcon │ │ ├── scancodes.h │ │ └── vtcon.c ├── fs │ └── tarfs │ │ ├── tar.c │ │ └── tar.h ├── i386 │ ├── conf │ │ └── kernel.ld │ ├── i386 │ │ ├── locore.S │ │ ├── multiboot.S │ │ ├── paging.c │ │ ├── port.c │ │ └── spinlock.c │ └── include │ │ ├── cpu.h │ │ ├── multiboot.h │ │ └── paging.h ├── kern │ ├── fs_devfs.c │ ├── fs_fsops.c │ ├── fs_path.c │ ├── fs_vfs.c │ └── kern_main.c ├── stdkern │ ├── list.c │ ├── memcpy.c │ ├── memset.c │ ├── ringbuf.c │ ├── strcat.c │ ├── strchr.c │ ├── strcmp.c │ ├── strcpy.c │ ├── strdup.c │ ├── strlen.c │ └── strsep.c └── sys │ ├── device.h │ ├── list.h │ ├── ringbuf.h │ ├── spinlock.h │ ├── stdkern.h │ └── vfs.h ├── ramdisk ├── DOC │ └── COPYING └── SYSTEM │ └── MOTD.TXT └── tools ├── .gitignore ├── buildno.sh ├── cdrom └── boot │ └── grub │ └── grub.cfg ├── gdbscripts ├── devices.gdb ├── heap.gdb └── vfs.gdb └── kcons /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AlignAfterOpenBracket: Align 4 | AlignConsecutiveMacros: false 5 | AlignConsecutiveAssignments: false 6 | AlignConsecutiveBitFields: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: DontAlign 9 | AlignOperands: Align 10 | AlignTrailingComments: false 11 | AllowAllArgumentsOnNextLine: false 12 | AllowAllConstructorInitializersOnNextLine: false 13 | AllowAllParametersOfDeclarationOnNextLine: false 14 | AllowShortEnumsOnASingleLine: false 15 | AllowShortBlocksOnASingleLine: false 16 | AllowShortCaseLabelsOnASingleLine: false 17 | AllowShortFunctionsOnASingleLine: None 18 | AllowShortLambdasOnASingleLine: None 19 | AllowShortIfStatementsOnASingleLine: Never 20 | AllowShortLoopsOnASingleLine: false 21 | AlwaysBreakAfterDefinitionReturnType: All 22 | AlwaysBreakAfterReturnType: TopLevelDefinitions 23 | AlwaysBreakBeforeMultilineStrings: false 24 | AlwaysBreakTemplateDeclarations: Yes 25 | BinPackArguments: false 26 | BinPackParameters: false 27 | BreakBeforeBinaryOperators: NonAssignment 28 | BreakBeforeBraces: Linux 29 | BreakBeforeTernaryOperators: true 30 | BreakStringLiterals: true 31 | ColumnLimit: 80 32 | CommentPragmas: '^ IWYU pragma:' 33 | ContinuationIndentWidth: 4 34 | Cpp11BracedListStyle: true 35 | DeriveLineEnding: true 36 | DerivePointerAlignment: false 37 | DisableFormat: false 38 | FixNamespaceComments: true 39 | ForEachMacros: 40 | - foreach 41 | - Q_FOREACH 42 | - BOOST_FOREACH 43 | IncludeBlocks: Preserve 44 | IncludeCategories: 45 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 46 | Priority: 2 47 | SortPriority: 0 48 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 49 | Priority: 3 50 | SortPriority: 0 51 | - Regex: '.*' 52 | Priority: 1 53 | SortPriority: 0 54 | IncludeIsMainRegex: '(Test)?$' 55 | IncludeIsMainSourceRegex: '' 56 | IndentCaseLabels: false 57 | IndentCaseBlocks: false 58 | IndentGotoLabels: true 59 | IndentPPDirectives: None 60 | IndentExternBlock: AfterExternBlock 61 | IndentWidth: 8 62 | IndentWrappedFunctionNames: false 63 | KeepEmptyLinesAtTheStartOfBlocks: false 64 | MacroBlockBegin: '' 65 | MacroBlockEnd: '' 66 | MaxEmptyLinesToKeep: 1 67 | NamespaceIndentation: None 68 | PenaltyBreakAssignment: 2 69 | PenaltyBreakBeforeFirstCallParameter: 19 70 | PenaltyBreakComment: 300 71 | PenaltyBreakFirstLessLess: 120 72 | PenaltyBreakString: 1000 73 | PenaltyBreakTemplateDeclaration: 10 74 | PenaltyExcessCharacter: 1000000 75 | PenaltyReturnTypeOnItsOwnLine: 60 76 | PointerAlignment: Right 77 | ReflowComments: true 78 | SortIncludes: true 79 | SortUsingDeclarations: true 80 | SpaceAfterCStyleCast: true 81 | SpaceAfterLogicalNot: false 82 | SpaceAfterTemplateKeyword: true 83 | SpaceBeforeAssignmentOperators: true 84 | SpaceBeforeCpp11BracedList: false 85 | SpaceBeforeCtorInitializerColon: true 86 | SpaceBeforeInheritanceColon: true 87 | SpaceBeforeParens: ControlStatements 88 | SpaceBeforeRangeBasedForLoopColon: true 89 | SpaceInEmptyBlock: false 90 | SpaceInEmptyParentheses: false 91 | SpacesBeforeTrailingComments: 1 92 | SpacesInAngles: false 93 | SpacesInConditionalStatement: false 94 | SpacesInContainerLiterals: true 95 | SpacesInCStyleCastParentheses: false 96 | SpacesInParentheses: false 97 | SpacesInSquareBrackets: false 98 | SpaceBeforeSquareBrackets: false 99 | Standard: Latest 100 | TabWidth: 8 101 | UseCRLF: false 102 | UseTab: AlignWithSpaces 103 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lr 5 | insert_final_newline = true 6 | 7 | [*.{S,c,h}] 8 | charset = utf-8 9 | indent_style = tab 10 | tab_width = 8 11 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | Please, do not send pull requests at this moment. 2 | 3 | Read CONTRIBUTING.md for more information about pull requests. 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | compile/ 2 | dist/ 3 | docs/html -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "llvm-vs-code-extensions.vscode-clangd", 4 | "editorconfig.editorconfig", 5 | "webfreak.debug", 6 | "13xforever.language-x86-64-assembly" 7 | ] 8 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "gdb", 9 | "request": "attach", 10 | "name": "gdb (I386)", 11 | "executable": "${workspaceFolder}/compile/I386/kernel", 12 | "target": "localhost:1234", 13 | "remote": true, 14 | "cwd": "${workspaceRoot}", 15 | "autorun": [ 16 | "break Bootstrap" 17 | ] 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "problemMatcher": [], 6 | "tasks": [ 7 | { 8 | "label": "kcons (I386)", 9 | "type": "shell", 10 | "command": "${workspaceFolder}/tools/kcons conf/I386", 11 | }, 12 | { 13 | "label": "rebuild compile_commands.json (I386)", 14 | "type": "shell", 15 | "dependsOn": [ 16 | "kcons (I386)" 17 | ], 18 | "command": "bear -- make -C ${workspaceFolder}/compile/I386 clean kernel", 19 | }, 20 | { 21 | "label": "build (I386)", 22 | "type": "shell", 23 | "dependsOn": [ 24 | "kcons (I386)" 25 | ], 26 | "command": "make -C ${workspaceFolder}/compile/I386 kernel", 27 | "group": { 28 | "kind": "build", 29 | "isDefault": true 30 | } 31 | }, 32 | { 33 | "label": "clean (I386)", 34 | "type": "shell", 35 | "dependsOn": [ 36 | "kcons (I386)" 37 | ], 38 | "command": "make -C ${workspaceFolder}/compile/I386 clean", 39 | }, 40 | { 41 | "label": "qemu (I386)", 42 | "type": "shell", 43 | "dependsOn": [ 44 | "kcons (I386)" 45 | ], 46 | "command": "make -C ${workspaceFolder}/compile/I386 qemu", 47 | "group": { 48 | "kind": "test", 49 | "isDefault": true 50 | } 51 | }, 52 | { 53 | "label": "qemu-gdb (I386)", 54 | "type": "shell", 55 | "dependsOn": [ 56 | "kcons (I386)" 57 | ], 58 | "command": "make -C ${workspaceFolder}/compile/I386 qemu-gdb", 59 | } 60 | ] 61 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. 8 | 9 | ## Our Standards 10 | 11 | Examples of behavior that contributes to a positive environment for our community include: 12 | 13 | * Demonstrating empathy and kindness toward other people 14 | * Being respectful of differing opinions, viewpoints, and experiences 15 | * Giving and gracefully accepting constructive feedback 16 | * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience 17 | * Focusing on what is best not just for us as individuals, but for the overall community 18 | 19 | Examples of unacceptable behavior include: 20 | 21 | * The use of sexualized language or imagery, and sexual attention or 22 | advances of any kind 23 | * Trolling, insulting or derogatory comments, and personal or political attacks 24 | * Public or private harassment 25 | * Publishing others' private information, such as a physical or email 26 | address, without their explicit permission 27 | * Other conduct which could reasonably be considered inappropriate in a 28 | professional setting 29 | 30 | ## Enforcement Responsibilities 31 | 32 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. 33 | 34 | Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. 35 | 36 | ## Scope 37 | 38 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 39 | 40 | ## Enforcement 41 | 42 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [INSERT CONTACT METHOD]. All complaints will be reviewed and investigated promptly and fairly. 43 | 44 | All community leaders are obligated to respect the privacy and security of the reporter of any incident. 45 | 46 | ## Enforcement Guidelines 47 | 48 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 49 | 50 | ### 1. Correction 51 | 52 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. 53 | 54 | **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. 55 | 56 | ### 2. Warning 57 | 58 | **Community Impact**: A violation through a single incident or series of actions. 59 | 60 | **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. 61 | 62 | ### 3. Temporary Ban 63 | 64 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. 65 | 66 | **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 67 | 68 | ### 4. Permanent Ban 69 | 70 | **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. 71 | 72 | **Consequence**: A permanent ban from any sort of public interaction within the project community. 73 | 74 | ## Attribution 75 | 76 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, 77 | available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 78 | 79 | Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). 80 | 81 | [homepage]: https://www.contributor-covenant.org 82 | 83 | For answers to common questions about this code of conduct, see the FAQ at 84 | https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. 85 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to this project 2 | 3 | Please, read the complete contents of this document in order to avoid 4 | miscomunication or wasting your own free time. This document declares how 5 | to contribute to this project. 6 | 7 | ## Roadmap 8 | 9 | **This project has no public roadmap**. This means that I'm not disclosing 10 | what I'm working on when I'm working on this project. This is intended at 11 | this moment for this project because the project is not mature or stable 12 | enough and is still under development. 13 | 14 | This also means that the code can break, change or be rewritten at any 15 | time without further notice; therefore, don't let your contributions stale, 16 | keep them fresh. 17 | 18 | ## Issue reporting 19 | 20 | Use the issue tracker to file bug reports on existing features or to propose 21 | new features for the software. 22 | 23 | * Bug reports should include enough information for making them reproducible. 24 | This is the best way for making sure that the issue can be fixed in first 25 | place. Provide as much context as you can: what was happening, what is your 26 | computer, your OS, your compiler... please note that bug reports without 27 | enough context could be sadly closed as invalid or not reproducible. 28 | 29 | * Technical suggestions should include an explanation on why is it a good 30 | idea to have this added into the project. Please note that, because of 31 | time constraints and because of having my own roadmap, I still reserve the 32 | right for rejecting suggestions that I consider don't fit within my project 33 | goals. 34 | 35 | ## Pull requests 36 | 37 | **Please, do not send pull requests at this moment.** 38 | 39 | * Because this code is done as a research and not as a serious project, there 40 | is some self-challenging factor in there that makes accepting external code 41 | look like cheating. I'm doing this project because I want to learn. Having 42 | other people do the project for me defeats this purpose. 43 | 44 | * As I said before, I have my own roadmap, and unexpected new features may 45 | disrupt my workflow. You might have used code that was soon to be deprecated 46 | or that already had been removed by the time you submitted your PR, for 47 | instance. 48 | 49 | ## Forking this project 50 | 51 | This code is still open source. Therefore, you are free to fork this project 52 | and modify it provided you do it on a way that doesn't conflicts with the 53 | open source license used by this project. For instance, if you want to use 54 | this research repository as a starting point for your own experiments. 55 | 56 | Please note that most of the conventions for this repository don't apply for 57 | forks. This means that you are free to accept pull requests in your own fork, 58 | change the code of conduct and such. 59 | -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | This project has also received patches and general contributions from the 2 | following contributors. They remain the copyright of their contributions. 3 | 4 | * Izan Beltrán 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # This file is part of NativeOS 2 | # Copyright (C) 2015-2018 The NativeOS contributors 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License 15 | # along with this program. If not, see . 16 | 17 | .PHONY: build-kernel \ 18 | cdrom \ 19 | check-profile \ 20 | clean \ 21 | qemu \ 22 | qemu-gdb \ 23 | usage 24 | 25 | GRUB_MKRESCUE ?= grub-mkrescue 26 | GRUB_ROOT = $(shell dirname `which ${GRUB_MKRESCUE}`) 27 | QEMU = qemu-system-i386 28 | 29 | usage: 30 | @echo "Targets:" 31 | @echo " build-kernel builds a kernel image" 32 | @echo " cdrom builds a CD-ROM image" 33 | @echo " qemu runs the built CD-ROM in QEMU" 34 | @echo " qemu-gdb runs the built CD-ROM in QEMU using GDB" 35 | @echo 36 | @echo "All the targets require passing a variable called PROFILE with" 37 | @echo "the name of a kernel profile first. For instance," 38 | @echo 39 | @echo " make build-kernel PROFILE=I386" 40 | @echo 41 | 42 | check-profile: 43 | ifeq ($(PROFILE),) 44 | $(error Please, set the PROFILE variable when calling this target.) 45 | endif 46 | true 47 | 48 | build-kernel: check-profile 49 | tools/kcons conf/${PROFILE} 50 | make -C compile/${PROFILE} 51 | 52 | dist/ramdisk.tar: check-profile 53 | tar -C ramdisk -cf dist/ramdisk.tar . 54 | 55 | cdrom: check-profile dist/nativeos-${PROFILE}.iso 56 | 57 | dist/nativeos-${PROFILE}.iso: compile/${PROFILE}/kernel dist/ramdisk.tar 58 | rm -rf dist/${PROFILE} 59 | mkdir -p dist/${PROFILE} 60 | cp -R tools/cdrom/* dist/${PROFILE} 61 | cp compile/${PROFILE}/kernel dist/${PROFILE}/boot/nativeos.exe 62 | cp dist/ramdisk.tar dist/${PROFILE}/ramdisk.tar 63 | ${GRUB_MKRESCUE} -d ${GRUB_ROOT}/../lib/grub/i386-pc -o dist/nativeos-${PROFILE}.iso dist/${PROFILE} 64 | 65 | 66 | qemu: check-profile dist/nativeos-${PROFILE}.iso 67 | $(QEMU) -m 16 -cpu 486 -cdrom dist/nativeos-${PROFILE}.iso -serial stdio -display sdl 68 | 69 | qemu-gdb: check-profile dist/nativeos-${PROFILE}.iso 70 | $(QEMU) -m 16 -cpu 486 -cdrom dist/nativeos-${PROFILE}.iso -serial stdio -s -S -display sdl 71 | 72 | clean: 73 | rm -rf compile 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NativeOS 2 | 3 | NativeOS is a hobbyist kernel and operating system for the x86 4 | platform. It is designed as a project to learn more about how the x86 5 | processor work and how operating systems work. My intention is purely 6 | educational. The source code is open in the hope that it helps other 7 | people too. 8 | 9 | ## Roadmap and contributing to the project 10 | 11 | **This project has no public roadmap**. This means that I'm not disclosing 12 | what I'm working on when I'm working on this project. This is intended at 13 | this moment for this project because the project is not mature or stable 14 | enough and is still under development. 15 | 16 | My issue and pull request policy is clarified on [CONTRIBUTING.md][1]. I'm not 17 | accepting pull requests at this moment. See the document to read why. 18 | Issues are still open and covered by the Code of Conduct. See 19 | [CODE\_OF\_CONDUCT.md][2] for details. 20 | 21 | ## Building the NativeOS Kernel 22 | 23 | To build the kernel image (in other words, the program that will run 24 | when the computer starts up), you will need to install the LLVM compiler 25 | if you don't have it already. NativeOS switched to LLVM because it has 26 | support for foreign ABIs (such as i386-elf in an x86-64 macOS) out of 27 | the box without requiring to setup a cross-compiler. 28 | 29 | GNU Make is also required to run the Makefile file. 30 | 31 | Python 3 is also required to run the kcons tool, which is used to 32 | generate the Makefiles. 33 | 34 | Windows NT is not a supported platform and there may be bugs. Users 35 | of Windows NT are encouraged to setup a POSIX system such as MSYS or 36 | Cygwin, or use the Windows Subsystem for Linux if available. 37 | 38 | ### Picking a profile 39 | 40 | Profiles are kernel compilation settings. A profile causes a specific 41 | subset of source code files to be compiled into the kernel image, and 42 | some specific optional settings to be enabled. 43 | 44 | At the moment, the repository provides the I386 profile, to build a 45 | basic 32 bit kernel image for the i386 architecture. The settings file 46 | for this profile is at conf/I386. 47 | 48 | ### Building a profile 49 | 50 | To build a profile, you can use the kcons script. kcons will parse the 51 | profile and the associated files and generate a Makefile that can be 52 | used to build the kernel. Call `tools/kcons` providing the path to a 53 | profile file as a parameter. 54 | 55 | $ tools/kcons conf/I386 56 | 57 | This will generate a subdirectory named after the profile name inside 58 | the `compile/` directory. In this directory, a Makefile will be present. 59 | Use it to build the kernel image: 60 | 61 | $ cd compile/I386 62 | 63 | Generating a depfile is recommended in order to have the object files 64 | recompiled whenever the imported source code changes: 65 | 66 | $ make .depend 67 | 68 | Then, proceed to build the kernel. 69 | 70 | $ make kernel 71 | 72 | Or, to make things faster, 73 | 74 | $ make -C compile/I386 .depend kernel 75 | 76 | The NativeOS kernel file is a binary in the ELF format. It supports the 77 | Multiboot specification, so it can be deployed in any machine using a 78 | bootloader capable of loading Multiboot binaries, such as GNU GRUB, 79 | SYSLINUX or Limine. 80 | 81 | ### Running the profile in QEMU 82 | 83 | For the i386 platform, QEMU supports a special flag called `-kernel`, 84 | where the path to a Multiboot executable file can be given. QEMU will 85 | treat the given file as if it was booted via a CD-ROM. This allows to 86 | quickly test the executable file without having to build CD-ROMs: 87 | 88 | $ make -C compile/I386 qemu 89 | qemu-system-i386 -kernel kernel -serial stdio 90 | 91 | ### Debugging the profile in QEMU 92 | 93 | QEMU also supports using the gdbserver protocol, so it is possible to 94 | connect QEMU with GDB in order to debug the running processor. There 95 | is a gdbinit file in the conf directory to help with the process. 96 | 97 | $ make -C compile/I386 qemu-gdb 98 | 99 | (In another terminal:) 100 | 101 | $ gdb -x conf/gdbinit.I386 102 | > continue 103 | 104 | ### Shortcut 105 | 106 | It is also possible to configure and build a kernel image profile using 107 | the root Makefile. Provide the profile name as the `PROFILE` parameter 108 | and it will run all the commands above. 109 | 110 | $ make build-kernel PROFILE=I386 111 | 112 | ## Creating a CD image 113 | 114 | You can use some of the GRUB Command Line Tools to generate ISO files. 115 | There is already a rule in the Makefile file to generate a CD-ROM using 116 | GRUB. 117 | 118 | You need the GRUB Command Line Tools installed with support for the 119 | i386-elf platform. If you are using GNU/Linux, you probably already have 120 | support for that platform, but otherwise you might have to install it to 121 | your system. Take protections to prevent GRUB to running on your host 122 | system if that is not practical for you. The `grub-mkrescue` program 123 | should be able to run in your command line. 124 | 125 | GNU xorriso is additionally required to generate the ISO file. 126 | 127 | To create a NativeOS ISO image, run the following command, specifying 128 | the name of a **previously built** profile as the `PROFILE` variable: 129 | 130 | $ make cdrom PROFILE=I386 131 | 132 | Windows NT, macOS and BSD users can install the GRUB Command Line tools 133 | by following the instructions given in the following address: 134 | . 135 | (Instructions are the same for Windows). 136 | 137 | NOTE: You'll be getting the GRUB Command Line Tools here. **This is 138 | definitely NOT the same as installing the GRUB Bootloader on your host 139 | PC**, although the command line tools allow you to do that, so care must 140 | be taken in order to avoid running any dangerous command. 141 | 142 | ## Running NativeOS 143 | 144 | A virtual machine is the safest way for running and testing NativeOS. 145 | I don't recommend running this on your host PC unless you know what 146 | you are doing and you have inspected the source code. Also, take a 147 | look at the disclaimer below before attempting to run anything that 148 | you could regret later. 149 | 150 | The Makefile already has some rules to run the NativeOS ISO file with 151 | QEMU. It is even possible to run QEMU in debug mode and to remotely 152 | attach GDB in order to debug the kernel image. 153 | 154 | Run the following commands: 155 | 156 | $ make qemu PROFILE=I386 # Run qemu-system-i386 157 | $ make qemu-gdb PROFILE=I386 # Run qemu-system-i386 in debug mode 158 | 159 | For more information on GDB debugging with QEMU, see the QEMU 160 | manual or the following article: . 161 | 162 | ## License and disclaimer 163 | 164 | Copyright (C) 2015-2021 Dani Rodríguez 165 | 166 | NativeOS is licensed under the terms of the GNU General Public License v3. 167 | See the COPYING file for more information. Old NativeOS files will 168 | probably not have the GPL header on it. I'm working on that. 169 | 170 | NativeOS is provided AS IS with no extra support. Working with kernels 171 | and OS and bootloaders is fun, but I'm not resposible for anything wrong 172 | you do with it. If you break your hard drive or your computer because 173 | of using the wrong command, that is not my fault. 174 | 175 | [1]: https://github.com/danirod/nativeos/blob/master/CONTRIBUTING.md 176 | [2]: https://github.com/danirod/nativeos/blob/master/CODE_OF_CONDUCT.md 177 | -------------------------------------------------------------------------------- /arch/i386/include/kernel/cpu/idt.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2018 The NativeOS contributors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef ARCH_X86_IDT_H_ 20 | #define ARCH_X86_IDT_H_ 21 | 22 | struct idt_table { 23 | unsigned short limit; /* Size of the IDT table. */ 24 | unsigned int base; /* Base address of the IDT table. */ 25 | } __attribute__((packed)); 26 | 27 | struct idt_entry { 28 | unsigned short offset1; /* 15..0 bytes for the offset address. */ 29 | unsigned short segment; /* GDT segment for this interrupt. */ 30 | unsigned char zeros; /* Must be zero. */ 31 | unsigned char attributes; /* IDT entry attributes. */ 32 | unsigned short offset2; /* 31..16 bytes for the offset address. */ 33 | } __attribute__((packed)); 34 | 35 | struct idt_data { 36 | unsigned int edi, esi, ebp, esp; 37 | unsigned int eax, ebx, ecx, edx; 38 | unsigned int int_no, err_code; 39 | } __attribute__((packed)); 40 | 41 | /* This function will set up the IDT table. */ 42 | void idt_init(void); 43 | 44 | /* This function will handle an interrupt. */ 45 | void idt_handler(struct idt_data* data); 46 | 47 | /* This is the prototype function for local IDT handlers. */ 48 | typedef void (*local_idt_handler_t)(struct idt_data*); 49 | 50 | /* This function is used to modify the handler associated to a interrupt. */ 51 | void idt_set_handler(unsigned int interrupt_code, local_idt_handler_t handler); 52 | 53 | #endif // ARCH_X86_IDT_H_ 54 | -------------------------------------------------------------------------------- /arch/i386/include/kernel/cpu/isrdef.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2018 The NativeOS contributors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #ifndef ARCH_X86_ISRDEF_H_ 20 | #define ARCH_X86_ISRDEF_H_ 21 | 22 | /* 23 | * How many interrupts are supported at this moment: 24 | * o Interrupts 0-31 are reserved for the system. 25 | * o Interrupts 32-48 are reserved for hardware (PIC, keyboard, timer...) 26 | * o Still have to decide an interrupt for system calls. 27 | */ 28 | #define INTERRUPTS 48 29 | 30 | /* 31 | * These are my interrupt entrypoints. This is ugly, but it has to be done 32 | * since every interrupt is actually a different function (or memory address) 33 | * that has to be given to the IDT Table. 34 | */ 35 | extern void isr_0(void); 36 | extern void isr_1(void); 37 | extern void isr_2(void); 38 | extern void isr_3(void); 39 | extern void isr_4(void); 40 | extern void isr_5(void); 41 | extern void isr_6(void); 42 | extern void isr_7(void); 43 | extern void isr_8(void); 44 | extern void isr_9(void); 45 | extern void isr_10(void); 46 | extern void isr_11(void); 47 | extern void isr_12(void); 48 | extern void isr_13(void); 49 | extern void isr_14(void); 50 | extern void isr_15(void); 51 | extern void isr_16(void); 52 | extern void isr_17(void); 53 | extern void isr_18(void); 54 | extern void isr_19(void); 55 | extern void isr_20(void); 56 | extern void isr_21(void); 57 | extern void isr_22(void); 58 | extern void isr_23(void); 59 | extern void isr_24(void); 60 | extern void isr_25(void); 61 | extern void isr_26(void); 62 | extern void isr_27(void); 63 | extern void isr_28(void); 64 | extern void isr_29(void); 65 | extern void isr_30(void); 66 | extern void isr_31(void); 67 | extern void isr_32(void); 68 | extern void isr_33(void); 69 | extern void isr_34(void); 70 | extern void isr_35(void); 71 | extern void isr_36(void); 72 | extern void isr_37(void); 73 | extern void isr_38(void); 74 | extern void isr_39(void); 75 | extern void isr_40(void); 76 | extern void isr_41(void); 77 | extern void isr_42(void); 78 | extern void isr_43(void); 79 | extern void isr_44(void); 80 | extern void isr_45(void); 81 | extern void isr_46(void); 82 | extern void isr_47(void); 83 | 84 | /* Interrupt table. */ 85 | extern unsigned int isr_vector[INTERRUPTS]; 86 | 87 | #endif // ARCH_X86_ISRDEF_H_ 88 | -------------------------------------------------------------------------------- /arch/i386/include/kernel/mem/heap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2018 The NativeOS contributors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #pragma once 19 | 20 | /** 21 | * \file heap.h 22 | * \brief Kernel Heap Allocator 23 | * 24 | * The kernel heap allocator is used by the kernel to allocate objects in a 25 | * dynamic way during runtime. Note that the kernel heap is only used by the 26 | * kernel, as the name implies. It is used for allocate objects that are 27 | * required during the kernel operation, such as temporal handling of inodes or 28 | * other driver stuff. 29 | */ 30 | 31 | #include 32 | 33 | /** 34 | * \brief Initialises the kernel heap. 35 | * 36 | * This function must be called at least once before actually using the heap 37 | * for anything, because it will initialise internal data structures that the 38 | * heap requires for normal operation. 39 | */ 40 | void heap_init(); 41 | 42 | /** 43 | * \brief Allocate a memory region in the heap. 44 | * 45 | * This function is used by the kernel to allocate something in the kernel 46 | * heap. If the requested amount of memory is successfully allocated in the 47 | * kernel, a pointer to the first byte of such memory buffer will be returned. 48 | * If there is an error, such as an exhausted heap, NULL will be returned. 49 | * 50 | * \param size the amount of bytes that the kernel wants to have allocated. 51 | * \return either a pointer to a memory region, or NULL in case of error. 52 | */ 53 | void * heap_alloc(size_t size); 54 | 55 | /** 56 | * \brief Free a memory region from the heap. 57 | * 58 | * This function is used by the kernel to free a memory region that previously 59 | * has been allocated by the heap_alloc function. The given memory region must 60 | * be the pointer that heap_alloc would return; i.e. the first byte of memory 61 | * from the memory region. After calling heap_free, the pointer should not be 62 | * used because it's not a safe region and may be reallocated later. 63 | * 64 | * \param ptr a pointer to the memory region to free. 65 | */ 66 | void heap_free(void * ptr); 67 | -------------------------------------------------------------------------------- /arch/i386/include/kernel/mem/pmm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2020 The NativeOS contributors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | #pragma once 19 | 20 | typedef unsigned int physaddr_t; 21 | 22 | /** 23 | * \brief Initialize the physical memory manager subsystem. 24 | */ 25 | void pmm_init(); 26 | 27 | /** 28 | * \brief Allocate a 4 KB page in the physical memory of the computer. 29 | * \return The allocated physical memory address, 0 if no free page is found. 30 | */ 31 | physaddr_t pmm_alloc_page(); 32 | 33 | /** 34 | * \brief Free a 4 KB page from the physical memory of the computer. 35 | * \param page the memory address of the page to free up from memory. 36 | */ 37 | void pmm_free_page(physaddr_t page); 38 | -------------------------------------------------------------------------------- /arch/i386/kernel/cpu/idt.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2018 The NativeOS contributors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | /* Table of contents for the IDT structure. */ 24 | struct idt_table idt_toc; 25 | 26 | /* Actual IDT data. */ 27 | struct idt_entry idt_entries[INTERRUPTS]; 28 | 29 | /* This is the function that actually loads the IDT table. */ 30 | extern void idt_load(void); 31 | 32 | /* Halts the system. */ 33 | extern void kernel_die(void); 34 | 35 | /* This function will modify one entry in the IDT table. */ 36 | static void idt_set_entry(int pos, unsigned int offset, 37 | unsigned short segment, unsigned char attributes) 38 | { 39 | idt_entries[pos].offset1 = offset & 0xFFFF; 40 | idt_entries[pos].offset2 = (offset >> 16) & 0xFFFF; 41 | idt_entries[pos].zeros = 0; 42 | idt_entries[pos].segment = segment; 43 | idt_entries[pos].attributes = attributes; 44 | } 45 | 46 | /* Remaps the PIC device to use the given interrupt vector. */ 47 | static void remap_pic(unsigned int start_int) 48 | { 49 | /* Tell the PIC the party is about to begin. */ 50 | port_out_byte(0x20, 0x11); 51 | port_out_byte(0xA0, 0x11); 52 | 53 | /* Tell the PIC where the interrupt vectors start. */ 54 | port_out_byte(0x21, start_int); 55 | port_out_byte(0xA1, start_int + 8); 56 | 57 | /* Tell each PIC which one is PIC1 and which one is PIC2. */ 58 | port_out_byte(0x21, 0x04); 59 | port_out_byte(0xA1, 0x02); 60 | 61 | /* Tell the PICs that this is x86. */ 62 | port_out_byte(0x21, 0x01); 63 | port_out_byte(0xA1, 0x01); 64 | 65 | /* Unmask the PICs. */ 66 | port_out_byte(0x21, 0x00); 67 | port_out_byte(0xA1, 0x00); 68 | } 69 | 70 | /* These are the handlers. */ 71 | static local_idt_handler_t idt_handlers[INTERRUPTS]; 72 | 73 | /* 74 | * This function can be used to modify which handler is associated with 75 | * a particular type of interrupt. For instance, the keyboard driver 76 | * could attach his own handler to the keyboard interrupt in order to 77 | * receive events when a key is pressed or released. 78 | */ 79 | void idt_set_handler(unsigned int interrupt_code, local_idt_handler_t handler) 80 | { 81 | if (interrupt_code >= INTERRUPTS) return; 82 | idt_handlers[interrupt_code] = handler; 83 | } 84 | 85 | void idt_init() 86 | { 87 | /* Create the IDT table. */ 88 | idt_toc.base = (unsigned int) &idt_entries; 89 | idt_toc.limit = (sizeof (struct idt_entry) * INTERRUPTS) - 1; 90 | 91 | /* Remap the PIC. */ 92 | remap_pic(0x20); 93 | 94 | /* Fill the IDT table and the handler table. */ 95 | int i; 96 | for (i = 0; i < INTERRUPTS; i++) { 97 | idt_set_entry(i, isr_vector[i], 0x08, 0x8E); 98 | idt_handlers[i] = 0; /* Will use default handler. */ 99 | } 100 | 101 | /* Actually load the IDT table. */ 102 | idt_load(); 103 | } 104 | 105 | /* Fallback handler for interrupts. */ 106 | static void fallback_handler(struct idt_data* data) 107 | { 108 | // NOP 109 | } 110 | 111 | /* This function is invoked when an interrupt is received. */ 112 | void idt_handler(struct idt_data* data) 113 | { 114 | /* Use the given interrupt handler if exists or use the fallback. */ 115 | if (idt_handlers[data->int_no] != 0) { 116 | idt_handlers[data->int_no](data); 117 | } else { 118 | fallback_handler(data); 119 | } 120 | 121 | /* If this is an exception, I have to halt the system (I think?) */ 122 | if (data->int_no < 16) 123 | kernel_die(); 124 | 125 | /* 126 | * Acknowledge the interrupt to PIC1. If the interrupt came from, 127 | * PIC2, then PIC1 will forward the interrupt acknowledge to PIC2. 128 | */ 129 | if (data->int_no >= 0x28 && data->int_no < 0x30) 130 | port_out_byte(0xA0, 0x20); 131 | if (data->int_no >= 0x20 && data->int_no < 0x30) 132 | port_out_byte(0x20, 0x20); 133 | } 134 | 135 | /* Vector interrupt table begins here. */ 136 | unsigned int isr_vector[] = { 137 | (unsigned int) &isr_0, 138 | (unsigned int) &isr_1, 139 | (unsigned int) &isr_2, 140 | (unsigned int) &isr_3, 141 | (unsigned int) &isr_4, 142 | (unsigned int) &isr_5, 143 | (unsigned int) &isr_6, 144 | (unsigned int) &isr_7, 145 | (unsigned int) &isr_8, 146 | (unsigned int) &isr_9, 147 | (unsigned int) &isr_10, 148 | (unsigned int) &isr_11, 149 | (unsigned int) &isr_12, 150 | (unsigned int) &isr_13, 151 | (unsigned int) &isr_14, 152 | (unsigned int) &isr_15, 153 | (unsigned int) &isr_16, 154 | (unsigned int) &isr_17, 155 | (unsigned int) &isr_18, 156 | (unsigned int) &isr_19, 157 | (unsigned int) &isr_20, 158 | (unsigned int) &isr_21, 159 | (unsigned int) &isr_22, 160 | (unsigned int) &isr_23, 161 | (unsigned int) &isr_24, 162 | (unsigned int) &isr_25, 163 | (unsigned int) &isr_26, 164 | (unsigned int) &isr_27, 165 | (unsigned int) &isr_28, 166 | (unsigned int) &isr_29, 167 | (unsigned int) &isr_30, 168 | (unsigned int) &isr_31, 169 | (unsigned int) &isr_32, 170 | (unsigned int) &isr_33, 171 | (unsigned int) &isr_34, 172 | (unsigned int) &isr_35, 173 | (unsigned int) &isr_36, 174 | (unsigned int) &isr_37, 175 | (unsigned int) &isr_38, 176 | (unsigned int) &isr_39, 177 | (unsigned int) &isr_40, 178 | (unsigned int) &isr_41, 179 | (unsigned int) &isr_42, 180 | (unsigned int) &isr_43, 181 | (unsigned int) &isr_44, 182 | (unsigned int) &isr_45, 183 | (unsigned int) &isr_46, 184 | (unsigned int) &isr_47 185 | }; 186 | -------------------------------------------------------------------------------- /arch/i386/kernel/cpu/lidt.S: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2018 The NativeOS contributors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | .text 20 | .align 4 21 | 22 | .global idt_load 23 | idt_load: 24 | lidt (idt_toc) 25 | sti 26 | ret 27 | 28 | /* Non erratic IDT handlers. */ 29 | .macro NON_ERROR_INTERRUPT num 30 | .global isr_\num 31 | isr_\num: 32 | pushl $0 33 | pushl $\num 34 | jmp idt_common_prehandler 35 | .endm 36 | 37 | /* Error IDT handlers. */ 38 | .macro ERROR_INTERRUPT num 39 | .global isr_\num 40 | isr_\num: 41 | pushl $\num 42 | jmp idt_common_prehandler 43 | .endm 44 | 45 | /* IDT declarations. */ 46 | NON_ERROR_INTERRUPT 0 47 | NON_ERROR_INTERRUPT 1 48 | NON_ERROR_INTERRUPT 2 49 | NON_ERROR_INTERRUPT 3 50 | NON_ERROR_INTERRUPT 4 51 | NON_ERROR_INTERRUPT 5 52 | NON_ERROR_INTERRUPT 6 53 | NON_ERROR_INTERRUPT 7 54 | ERROR_INTERRUPT 8 55 | NON_ERROR_INTERRUPT 9 56 | ERROR_INTERRUPT 10 57 | ERROR_INTERRUPT 11 58 | ERROR_INTERRUPT 12 59 | ERROR_INTERRUPT 13 60 | ERROR_INTERRUPT 14 61 | NON_ERROR_INTERRUPT 15 62 | NON_ERROR_INTERRUPT 16 63 | NON_ERROR_INTERRUPT 17 64 | NON_ERROR_INTERRUPT 18 65 | NON_ERROR_INTERRUPT 19 66 | NON_ERROR_INTERRUPT 20 67 | NON_ERROR_INTERRUPT 21 68 | NON_ERROR_INTERRUPT 22 69 | NON_ERROR_INTERRUPT 23 70 | NON_ERROR_INTERRUPT 24 71 | NON_ERROR_INTERRUPT 25 72 | NON_ERROR_INTERRUPT 26 73 | NON_ERROR_INTERRUPT 27 74 | NON_ERROR_INTERRUPT 28 75 | NON_ERROR_INTERRUPT 29 76 | NON_ERROR_INTERRUPT 30 77 | NON_ERROR_INTERRUPT 31 78 | NON_ERROR_INTERRUPT 32 79 | NON_ERROR_INTERRUPT 33 80 | NON_ERROR_INTERRUPT 34 81 | NON_ERROR_INTERRUPT 35 82 | NON_ERROR_INTERRUPT 36 83 | NON_ERROR_INTERRUPT 37 84 | NON_ERROR_INTERRUPT 38 85 | NON_ERROR_INTERRUPT 39 86 | NON_ERROR_INTERRUPT 40 87 | NON_ERROR_INTERRUPT 41 88 | NON_ERROR_INTERRUPT 42 89 | NON_ERROR_INTERRUPT 43 90 | NON_ERROR_INTERRUPT 44 91 | NON_ERROR_INTERRUPT 45 92 | NON_ERROR_INTERRUPT 46 93 | NON_ERROR_INTERRUPT 47 94 | 95 | .global idt_common_prehandler 96 | idt_common_prehandler: 97 | pusha 98 | 99 | /* Save ESP in the stack so we can access other registers. */ 100 | mov %esp, %eax 101 | push %eax 102 | 103 | call idt_handler 104 | 105 | /* Remove ESP */ 106 | pop %eax 107 | 108 | popa 109 | 110 | /* Removes error code and interrupt routine number. */ 111 | add $8, %esp 112 | iret 113 | -------------------------------------------------------------------------------- /arch/i386/kernel/mem/alloc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2018 The NativeOS contributors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * \file alloc.c 21 | * \brief i386 alloc implementation for stdlib.h 22 | * 23 | * This file has the implementation of several platform dependent functions 24 | * present in stdlib.h that need to be defined in the kernel image. Examples 25 | * of such functions are malloc or free, which allows to allocat and deallocate 26 | * objects from the kernel heap. 27 | */ 28 | #include 29 | #include 30 | 31 | void * 32 | malloc (size_t size) 33 | { 34 | return heap_alloc(size); 35 | } 36 | 37 | void 38 | free (void * ptr) 39 | { 40 | heap_free(ptr); 41 | } 42 | -------------------------------------------------------------------------------- /arch/i386/kernel/mem/heap.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2018 The NativeOS contributors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * \file heap.c 21 | * \brief x86 kernel heap 22 | * 23 | * This file implements a heap allocator designed to be used by the kernel. 24 | * The heap is a big chunk of memory where an application may allocate objects 25 | * dynamically. 26 | * 27 | * The current kernel heap implementation is a linked list of memory regions. 28 | * Each memory region has a control block as header (including fields such as 29 | * the size of the memory region or whether it's allocated or not), and a data 30 | * region as body. The data region is the space where the application may 31 | * write to or read from. 32 | * 33 | * Whenever a new buffer is allocated, a free memory region is selected, and 34 | * it's split in two segments. One of the segments has the requested amount of 35 | * bytes on its data region; the remaining part of the original memory region 36 | * is marked with a new control block in order to have it free for future use. 37 | * 38 | * Whenever a buffer is freed, the memory segment is marked as free. Further 39 | * allocations may use this memory buffer again, even split it again. This is 40 | * a fragmentation source because there may be a lot of small regions marked as 41 | * active, with small free gaps in between that canont be used to allocate 42 | * bigger objects. 43 | * 44 | * In order to reduce fragmentation, every time a memory region is deallocated, 45 | * the bounds of the memory region are checked to see if there are other free 46 | * regions adjacent to the one that's being freed. If two consecutive free 47 | * regions are found, those regions are merged into one consecutive and big 48 | * memory region. 49 | */ 50 | 51 | #include 52 | #include 53 | 54 | /** Magic number that indicates that a heap control block follows. */ 55 | #define HEAP_MAGIC_HEAD 0x51514949 56 | 57 | /** Magic value that indicates that the current memory block is free. */ 58 | #define HEAP_MAGIC_FREE 0x40404040 59 | 60 | /** Magic value that indicates that the current memory block is allocated. */ 61 | #define HEAP_MAGIC_USED 0xEFEFEFEF 62 | 63 | /** Points to a memory address related to the heap. */ 64 | typedef unsigned char * HEAP_ADDR; 65 | 66 | /** Used to indicate the size of a memory region in the heap. */ 67 | typedef size_t HEAP_SIZE; 68 | 69 | /** 70 | * \brief Header control block 71 | * 72 | * The header control block is a structure that's present at the beginning of a 73 | * memory segment in the heap. It's used to mark metadata about the memory 74 | * region itself, such as whether the memory region is free and can be 75 | * allocated, or if it's currently in use. 76 | */ 77 | typedef struct heap_block { 78 | int magic, status; 79 | HEAP_ADDR next, prev; 80 | HEAP_SIZE size; 81 | } heap_block_t; 82 | 83 | /* This points to the root control block of the heap once it has been 84 | * initialised. Control blocks are connected through a linked list. The 85 | * allocator will traverse the linked list when looking for blocks marked as 86 | * free. */ 87 | static volatile heap_block_t * heap_root; 88 | 89 | /* Defined in ldscript. This symbol is located at the bottom of the kernel 90 | * heap. Heap allocations should always start at a memory address that is 91 | * equal or greater than the memory address of this symbol. */ 92 | extern volatile const unsigned char heap_bottom; 93 | 94 | /* Defined in ldscript. This symbol is located at the top of the kernel heap. 95 | * Heap allocations should never touch any memory address whose value is equal 96 | * or greater than the memory address of this symbol. */ 97 | extern volatile const unsigned char heap_top; 98 | 99 | /* Forbids multiple processors of allocating memory at the same time. */ 100 | static struct spinlock heap_allocator_spinlock; 101 | 102 | /** 103 | * \brief Attempt to merge the given heap control blocks. 104 | * \param head the first memory block to merge. 105 | * \param tail the second memory block to merge. 106 | * \return pointer to the merged block, or NULL if no merge was made. 107 | */ 108 | static inline heap_block_t * 109 | heap_merge (heap_block_t * head, heap_block_t * tail) 110 | { 111 | HEAP_ADDR head_addr = (HEAP_ADDR) head; 112 | HEAP_ADDR tail_addr = (HEAP_ADDR) tail; 113 | heap_block_t * bound; 114 | 115 | if (head->magic != HEAP_MAGIC_HEAD || head->magic != tail->magic) { 116 | /* Both headers must be valid. */ 117 | return 0; 118 | } 119 | if (head->status != HEAP_MAGIC_FREE || head->status != tail->status) { 120 | /* Both headers must be marked as free. */ 121 | return 0; 122 | } 123 | if (head->next != (HEAP_ADDR) tail || tail->prev != (HEAP_ADDR) head) { 124 | /* Memory regions must be linked. */ 125 | return 0; 126 | } 127 | if (head_addr + sizeof(heap_block_t) + head->size != tail_addr) { 128 | /* Memory regions must be consecutive. */ 129 | return 0; 130 | } 131 | 132 | /* Unlink tail from chain. */ 133 | if (tail->next) { 134 | bound = (heap_block_t *) tail->next; 135 | bound->prev = (HEAP_ADDR) head; 136 | } 137 | head->next = tail->next; 138 | 139 | /* Assignate the space for the control block and buffer to head. */ 140 | head->size += (sizeof(heap_block_t) + tail->size); 141 | 142 | return head; 143 | } 144 | 145 | void 146 | heap_init (void) 147 | { 148 | HEAP_SIZE total; 149 | HEAP_ADDR top, bottom; 150 | 151 | /* Extract the heap size. Just remember that heap_top and heap_bottom 152 | * are symbols defined in the linkerscript, therefore we should only 153 | * access the memory address, not the values themselves. Also, top is 154 | * at the end of the heap because memory maps go bottom to up. */ 155 | bottom = (HEAP_ADDR) &heap_bottom; 156 | top = (HEAP_ADDR) &heap_top; 157 | total = (HEAP_SIZE) (top - bottom); 158 | 159 | /* Create the root control block for the heap. */ 160 | heap_root = (heap_block_t *) bottom; 161 | heap_root->magic = HEAP_MAGIC_HEAD; 162 | heap_root->status = HEAP_MAGIC_FREE; 163 | heap_root->size = total - sizeof(heap_block_t); 164 | heap_root->prev = 0; 165 | heap_root->next = 0; 166 | 167 | spinlock_init(&heap_allocator_spinlock); 168 | } 169 | 170 | void * 171 | heap_alloc (size_t size) 172 | { 173 | heap_block_t * root, * block, * next_block; 174 | HEAP_ADDR blockptr, buffer, after_buffer; 175 | 176 | root = (heap_block_t *) heap_root; 177 | buffer = 0; 178 | 179 | /* Make sure we are the only allocator in the neighbourhood. */ 180 | spinlock_lock(&heap_allocator_spinlock); 181 | 182 | for (block = root; block; block = (heap_block_t *) block->next) { 183 | blockptr = (HEAP_ADDR) block; 184 | 185 | if (block->magic != HEAP_MAGIC_HEAD) { 186 | /* This is not a heap control block! Ackchyually, 187 | * yeah, nothing guarantees us that this is a rogue 188 | * control block with valid magic numbers. */ 189 | goto _cleanup; 190 | } 191 | 192 | if (block->status != HEAP_MAGIC_FREE) { 193 | /* Block cannot be used because it's allocated. */ 194 | continue; 195 | } 196 | 197 | if (block->size < size) { 198 | /* Block is not big enough for this allocation. */ 199 | continue; 200 | } 201 | 202 | /* Okay, so if we are here, we can allocate. */ 203 | block->status = HEAP_MAGIC_USED; 204 | buffer = (HEAP_ADDR) (blockptr + sizeof(heap_block_t)); 205 | 206 | /* Early return if there is not enough space for split. */ 207 | if (block->size <= (size + sizeof(heap_block_t) + 1)) { 208 | goto _cleanup; /* No. */ 209 | } 210 | 211 | after_buffer = (HEAP_ADDR) (buffer + size); 212 | next_block = (heap_block_t *) after_buffer; 213 | 214 | /* Mark the bounds of the buffer as a new block. */ 215 | next_block->magic = HEAP_MAGIC_HEAD; 216 | next_block->status = HEAP_MAGIC_FREE; 217 | next_block->size = block->size - size - sizeof(heap_block_t); 218 | next_block->prev = (HEAP_ADDR) block; 219 | next_block->next = block->next; 220 | 221 | /* Shrink the current block. */ 222 | block->size = size; 223 | block->next = (HEAP_ADDR) next_block; 224 | 225 | /* No iterations required anymore. */ 226 | break; 227 | } 228 | _cleanup: 229 | /* Make sure to unlock the spinlock or the system will collapse. */ 230 | spinlock_release(&heap_allocator_spinlock); 231 | return buffer; 232 | } 233 | 234 | void 235 | heap_free (void * ptr) 236 | { 237 | heap_block_t * bufheader, * nextblock, * prevblock; 238 | 239 | /* Lock before doing anything useful. */ 240 | spinlock_lock(&heap_allocator_spinlock); 241 | 242 | /* If this is an allocated buffer, it should have a header. */ 243 | bufheader = (heap_block_t *) (ptr - sizeof(heap_block_t)); 244 | if (bufheader->magic != HEAP_MAGIC_HEAD) { 245 | /* Hey wait a second! */ 246 | goto _cleanup; 247 | } 248 | 249 | /* It should be used. */ 250 | if (bufheader->status != HEAP_MAGIC_USED) { 251 | goto _cleanup; 252 | } 253 | 254 | /* Mark the block as free. */ 255 | bufheader->status = HEAP_MAGIC_FREE; 256 | 257 | /* Test for merge. */ 258 | if (bufheader->next) { 259 | heap_merge(bufheader, (heap_block_t *) bufheader->next); 260 | } 261 | if (bufheader->prev) { 262 | heap_merge((heap_block_t *) bufheader->prev, bufheader); 263 | } 264 | _cleanup: 265 | /* Make sure to unlock the spinlock or things will collapse. */ 266 | spinlock_release(&heap_allocator_spinlock); 267 | } 268 | -------------------------------------------------------------------------------- /arch/i386/kernel/mem/pmm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2020 The NativeOS contributors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | /** 20 | * \file pmm.c 21 | * \brief Physical Memory Manager 22 | * 23 | * The physical memory manager allocates pages. A page is an aligned 4 KB 24 | * memory region (in other words, a memory block that is always 4096 bytes 25 | * long, whose first byte has 0x000 as their 12 least significant bits). 26 | * 27 | * The virtual memory manager depends on the physical memory manager in order 28 | * to allocate physical memory pages that will be put in the frames in use by 29 | * the virtual memory address range. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | /** 37 | * \brief Frame index. 38 | * 39 | * This abstraction provides a little more context to variables that hold a 40 | * frame number, to make them stand out among other kind of unsigned integer 41 | * variables that we hold in this module. 42 | */ 43 | typedef unsigned int frame_idx_t; 44 | 45 | /** 46 | * \brief Frame allocation bitmap 47 | * 48 | * Each bit of this bitmap represents one of the 4 KB frames the memory is 49 | * split by. Bit i is set to 1 when frame i, spanning memory addresses 50 | * (i * 0x4000) up to (i * 0x4000 + 0x3FFF) is already taken. 51 | */ 52 | static unsigned int * frames_map; 53 | 54 | /** 55 | * \brief Number of allocatable frames 56 | * 57 | * This is the length of the bitmap phys_frame_bitmap. So, this variable will 58 | * hold the total amount of frames that exist in the memory of this machine. 59 | * Up to 2^20 frames can exist on a machine fully loaded with 4 GB of RAM. 60 | * For a system with less than 4 GB of RAM, less frames will exist. 61 | */ 62 | static frame_idx_t frames_count; 63 | 64 | #define FRAME_NUMBER(memaddr) (memaddr >> 12) /* divide by 4096 */ 65 | #define PHYSICAL_ADDR(idx) (idx << 12) /* multiply by 4096 */ 66 | 67 | #define BIT_INDEX(frame_idx) (frame_idx >> 5) /* divide by 32 */ 68 | #define BIT_OFFSET(frame_idx) (frame_idx & 0x1F) /* modulus 32 */ 69 | 70 | #define OFFSET_MASK(bit) (((unsigned int) 0x1) << bit) 71 | 72 | static inline void 73 | frame_set (frame_idx_t idx) 74 | { 75 | frames_map[BIT_INDEX(idx)] |= OFFSET_MASK(BIT_OFFSET(idx)); 76 | } 77 | 78 | static inline unsigned int 79 | frame_test (frame_idx_t idx) 80 | { 81 | return frames_map[BIT_INDEX(idx)] & OFFSET_MASK(BIT_OFFSET(idx)); 82 | } 83 | 84 | static inline void 85 | frame_clear (frame_idx_t idx) 86 | { 87 | frames_map[BIT_INDEX(idx)] &= ~OFFSET_MASK(BIT_OFFSET(idx)); 88 | } 89 | 90 | /** 91 | * \brief Return the index of a free frame. 92 | * 93 | * Note that this implementation can be improved, because it will take more 94 | * time to allocate frames as the system grows the in use memory. However, 95 | * this is a simple implementation that will make things kick in the meantime. 96 | * 97 | * \return either -1 if no free frames are found, or the frame index otherwise. 98 | */ 99 | static frame_idx_t 100 | frame_find_free () 101 | { 102 | frame_idx_t idx, offset; 103 | unsigned int map; 104 | 105 | for (idx = 0; idx < frames_count; idx += 32) { 106 | if (frames_map[BIT_INDEX(idx)] != (unsigned int) -1) { 107 | map = frames_map[BIT_INDEX(idx)]; 108 | for (offset = 0; offset < 32; offset++) { 109 | if (!(map & OFFSET_MASK(BIT_OFFSET(offset)))) { 110 | return idx | offset; 111 | } 112 | } 113 | } 114 | } 115 | 116 | return -1; 117 | } 118 | 119 | static void 120 | allocate_frames () 121 | { 122 | /* mem_upper contains the size of the extended memory area. Note that 123 | * because multiboot respects the original IBM PC memory map, the 124 | * extended memory starts at 1 MB, so I have to add 1024 kB to whatever 125 | * I have here. Also note that this variable is expressed in kBs. */ 126 | unsigned int mapsize, memsize = (multiboot_info->mem_upper + 1) << 10; 127 | 128 | frames_count = FRAME_NUMBER(memsize); 129 | 130 | mapsize = BIT_INDEX(frames_count); 131 | if (BIT_OFFSET(frames_count) != 0) { 132 | mapsize++; 133 | } 134 | frames_map = (unsigned int *) heap_alloc(mapsize); 135 | } 136 | 137 | /** 138 | * \brief Mark frames in use for a specific memory block. 139 | * \param mblock a memory region declared by multiboot as in use. 140 | */ 141 | static void 142 | reserve_system_block (multiboot_mmap_t * mblock) 143 | { 144 | physaddr_t page; 145 | unsigned int f, frame_offset = FRAME_NUMBER(mblock->base_addr); 146 | 147 | for (f = frame_offset, page = 0; 148 | f < frames_count && page < mblock->length; 149 | f++, page += 0x1000) { 150 | frame_set(f); 151 | } 152 | } 153 | 154 | /** 155 | * \brief Mark frames in use by the system as in use. 156 | * 157 | * Multiboot may report that some memory areas are reserved (hardware mappings, 158 | * ACPI tables, or damaged memory regions). In such cases, it may be desired 159 | * to mark such frames as permanently in use so that no further pages are 160 | * allocated into these frames. 161 | */ 162 | static void 163 | reserve_system () 164 | { 165 | multiboot_mmap_t * mblock; 166 | physaddr_t mmap_end; 167 | 168 | if (multiboot_info->flags & 0x40) { 169 | mblock = (multiboot_mmap_t *) multiboot_info->mmap_addr; 170 | mmap_end = (physaddr_t) mblock + multiboot_info->mmap_length; 171 | 172 | while ((physaddr_t) mblock < mmap_end) { 173 | /* If the block is reserved, mark the pages. */ 174 | if (mblock->type != 1) { 175 | reserve_system_block(mblock); 176 | } 177 | 178 | /* Skips to the next block (forgive weird math). */ 179 | mblock = (multiboot_mmap_t *) ((unsigned int) mblock + 180 | mblock->size + sizeof(mblock->size)); 181 | } 182 | } 183 | } 184 | 185 | /** 186 | * \brief Mark the memory pages in use by the kernel as in use. 187 | * 188 | * This subroutine will scan the memory pages in use by the kernel binary image 189 | * as in use, so that the physical memory manager never tries to allocate pages 190 | * inside the kernel image. 191 | */ 192 | static void 193 | reserve_kernel () 194 | { 195 | extern char kernel_start, kernel_after; 196 | physaddr_t addr; 197 | frame_idx_t frame; 198 | 199 | /* Start counting at the start of the page. */ 200 | addr = ((physaddr_t) &kernel_start) & 0xFFFFF000; 201 | 202 | /* Mark pages in use by the kernel. */ 203 | for (frame = FRAME_NUMBER(addr); 204 | addr < (physaddr_t) &kernel_after; 205 | addr += 4096, frame++) { 206 | frame_set(frame); 207 | } 208 | } 209 | 210 | void 211 | pmm_init () 212 | { 213 | allocate_frames(); 214 | reserve_system(); 215 | reserve_kernel(); 216 | // TODO: reserve_modules(); 217 | } 218 | 219 | physaddr_t 220 | pmm_alloc_page () 221 | { 222 | frame_idx_t frame = frame_find_free(); 223 | if (frame == (unsigned int) -1) { 224 | return 0; 225 | } else { 226 | frame_set(frame); 227 | return PHYSICAL_ADDR(frame); 228 | } 229 | } 230 | 231 | void 232 | pmm_free_page (physaddr_t page) 233 | { 234 | frame_clear(FRAME_NUMBER(page)); 235 | } 236 | -------------------------------------------------------------------------------- /conf/I386: -------------------------------------------------------------------------------- 1 | # The main configuration file for the i386 architecture. 2 | # Duplicate this file if you want to derive additional profiles 3 | # based on this one. 4 | 5 | arch i386 6 | 7 | # Version information 8 | define VERSION_NAME="\"NativeOS Preview\"" 9 | 10 | # Turn on useful tools for debug 11 | makeoption CFLAGS+="-g -O0 -march=i386" 12 | option debug 13 | 14 | define KERNEL_STACK_SIZE=0x4000 15 | 16 | # Enable support for the multiboot standard 17 | option multiboot 18 | define MULTIBOOT 19 | -------------------------------------------------------------------------------- /conf/Makefile.i386: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | 3 | CC = clang 4 | AS = clang 5 | LD = ld.lld 6 | 7 | CFLAGS = -c -nostdlib -ffreestanding -fno-builtin -target i386-elf -m32 8 | CFLAGS += -I. 9 | CFLAGS += -I../../arch/i386/include 10 | CFLAGS += -I../../kernel 11 | 12 | DEPENDFLAGS = -E -MM 13 | DEPENDFLAGS += -I. 14 | DEPENDFLAGS += -I../../arch/i386/include 15 | DEPENDFLAGS += -I../../kernel 16 | 17 | LDFLAGS = -nostdlib -T ../../kernel/i386/conf/kernel.ld 18 | 19 | ROOT=../.. 20 | 21 | #### MAKEOPTIONS start here 22 | %%MAKEOPTIONS 23 | #### MAKEOPTIONS end here 24 | 25 | .PHONY: clean 26 | 27 | #### OBJS start here 28 | %%OBJS 29 | #### OBJS end here 30 | 31 | #### SRCS start here 32 | %%SRCS 33 | #### SRCS end here 34 | 35 | kernel: ${OBJS} 36 | ${LD} ${LDFLAGS} -o kernel ${OBJS} 37 | 38 | # If the .depend file is available, rules will be imported. 39 | -include .depend 40 | 41 | # Rebuild the .depend file using the rules database 42 | .depend: ${SRCS} 43 | ${CC} ${DEPENDFLAGS} ${SRCS} > .depend 44 | 45 | #### RULES start here 46 | %%RULES 47 | #### RULES end here 48 | 49 | qemu: kernel 50 | qemu-system-i386 -kernel kernel -serial stdio 51 | 52 | qemu-gdb: kernel 53 | qemu-system-i386 -kernel kernel -serial stdio -s -S 54 | 55 | clean: 56 | rm -f kernel ${OBJS} 57 | -------------------------------------------------------------------------------- /conf/files: -------------------------------------------------------------------------------- 1 | kernel/device/null.c standard 2 | kernel/fs/tarfs/tar.c standard 3 | kernel/kern/fs_devfs.c standard 4 | kernel/kern/fs_fsops.c standard 5 | kernel/kern/fs_path.c standard 6 | kernel/kern/fs_vfs.c standard 7 | kernel/kern/kern_main.c standard 8 | kernel/stdkern/list.c standard 9 | kernel/stdkern/memcpy.c standard 10 | kernel/stdkern/memset.c standard 11 | kernel/stdkern/ringbuf.c standard 12 | kernel/stdkern/strcat.c standard 13 | kernel/stdkern/strchr.c standard 14 | kernel/stdkern/strcmp.c standard 15 | kernel/stdkern/strcpy.c standard 16 | kernel/stdkern/strdup.c standard 17 | kernel/stdkern/strlen.c standard 18 | kernel/stdkern/strsep.c standard 19 | -------------------------------------------------------------------------------- /conf/files.i386: -------------------------------------------------------------------------------- 1 | arch/i386/kernel/cpu/idt.c standard 2 | arch/i386/kernel/cpu/lidt.S standard 3 | arch/i386/kernel/mem/alloc.c standard 4 | arch/i386/kernel/mem/heap.c standard 5 | arch/i386/kernel/mem/pmm.c standard 6 | kernel/device/vgafb.c standard 7 | kernel/device/vtcon/vtcon.c standard 8 | kernel/device/kbd.c standard 9 | kernel/device/pctimer.c standard 10 | kernel/device/rtclock.c standard 11 | kernel/device/uart8250.c standard 12 | kernel/i386/i386/locore.S standard 13 | kernel/i386/i386/multiboot.S optional multiboot 14 | kernel/i386/i386/paging.c standard 15 | kernel/i386/i386/port.c standard 16 | kernel/i386/i386/spinlock.c standard 17 | -------------------------------------------------------------------------------- /conf/gdbinit.I386: -------------------------------------------------------------------------------- 1 | # This is the gdbscript used to debug a kernel image compiled using 2 | # the I386 profile. Note that it requires the kernel image to have 3 | # been compiled first, in order to have access to the symbol table. 4 | # It will work if QEMU is launched using the `make qemu-gdb` target. 5 | 6 | # Load useful gdbscripts 7 | source tools/gdbscripts/devices.gdb 8 | source tools/gdbscripts/heap.gdb 9 | source tools/gdbscripts/vfs.gdb 10 | 11 | target remote localhost:1234 12 | 13 | file compile/I386/kernel 14 | 15 | # Set a breakpoint in kernel_main to debug the main function. At this 16 | # stage of kernel development, this is probably the most interesting 17 | # thing to catch. 18 | break kernel_main 19 | continue 20 | -------------------------------------------------------------------------------- /kernel/device/kbd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | static int keyboard_init(void); 7 | static int keyboard_open(unsigned int flags); 8 | static int keyboard_close(void); 9 | static unsigned int keyboard_read(unsigned char *buf, unsigned int len); 10 | 11 | static driver_t keyboard_driver = { 12 | .drv_name = "keyboard", 13 | .drv_flags = DV_FCHARDEV, 14 | .drv_init = &keyboard_init, 15 | }; 16 | 17 | static device_t keyboard_device = { 18 | .dev_family = &keyboard_driver, 19 | .dev_open = &keyboard_open, 20 | .dev_read_chr = &keyboard_read, 21 | .dev_close = &keyboard_close, 22 | }; 23 | 24 | static ringbuf_t *keyboard_rbuf; 25 | 26 | static int 27 | keyboard_open(unsigned int flags) 28 | { 29 | return 0; 30 | } 31 | 32 | static int 33 | keyboard_close(void) 34 | { 35 | return 0; 36 | } 37 | 38 | static unsigned int 39 | keyboard_read(unsigned char *buf, unsigned int len) 40 | { 41 | unsigned int total = 0; 42 | unsigned char scancode; 43 | 44 | while (total <= len && ringbuf_test_ready(keyboard_rbuf)) { 45 | scancode = ringbuf_read(keyboard_rbuf); 46 | *buf++ = scancode; 47 | total++; 48 | } 49 | 50 | return total; 51 | } 52 | 53 | static void 54 | keyboard_key_handler(struct idt_data *idt) 55 | { 56 | int scancode = port_in_byte(0x60); 57 | ringbuf_write(keyboard_rbuf, (char) scancode); 58 | } 59 | 60 | static int 61 | keyboard_init(void) 62 | { 63 | keyboard_rbuf = ringbuf_alloc(1024); 64 | idt_set_handler(0x21, &keyboard_key_handler); 65 | device_install(&keyboard_device, "kbd"); 66 | return 0; 67 | } 68 | 69 | DEVICE_DESCRIPTOR(kbd, keyboard_driver); 70 | -------------------------------------------------------------------------------- /kernel/device/null.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static int null_init(void); 4 | static int null_dev_open(unsigned int flags); 5 | static unsigned int null_dev_read(unsigned char *buf, unsigned int len); 6 | static unsigned int null_dev_write(unsigned char *buf, unsigned int len); 7 | static unsigned int zero_dev_read(unsigned char *buf, unsigned int len); 8 | static int null_dev_close(); 9 | 10 | static driver_t null_driver = { 11 | .drv_name = "null", 12 | .drv_flags = DV_FCHARDEV, 13 | .drv_init = &null_init, 14 | }; 15 | 16 | static device_t null_device = { 17 | .dev_family = &null_driver, 18 | .dev_open = &null_dev_open, 19 | .dev_read_chr = &null_dev_read, 20 | .dev_write_chr = &null_dev_write, 21 | .dev_close = &null_dev_close, 22 | }; 23 | 24 | static device_t zero_device = { 25 | .dev_family = &null_driver, 26 | .dev_open = &null_dev_open, 27 | .dev_read_chr = &zero_dev_read, 28 | .dev_write_chr = &null_dev_write, 29 | .dev_close = &null_dev_close, 30 | }; 31 | 32 | static int 33 | null_dev_open(unsigned int flags) 34 | { 35 | return 0; 36 | } 37 | 38 | static int 39 | null_dev_close() 40 | { 41 | return 0; 42 | } 43 | 44 | static unsigned int 45 | null_dev_read(unsigned char *buf, unsigned int len) 46 | { 47 | /* Read zero bytes. */ 48 | return 0; 49 | } 50 | 51 | static unsigned int 52 | null_dev_write(unsigned char *buf, unsigned int len) 53 | { 54 | /* Should act as if it could write every byte. */ 55 | return len; 56 | } 57 | 58 | static unsigned int 59 | zero_dev_read(unsigned char *buf, unsigned int len) 60 | { 61 | unsigned i; 62 | for (i = 0; i < len; i++) 63 | buf[i] = 0; 64 | return len; 65 | } 66 | 67 | static int 68 | null_init(void) 69 | { 70 | device_install(&null_device, "null"); 71 | device_install(&zero_device, "zero"); 72 | return 0; 73 | } 74 | 75 | DEVICE_DESCRIPTOR(null, null_driver); 76 | -------------------------------------------------------------------------------- /kernel/device/pctimer.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * \brief Intel 8253 Programmable Interrupt Timer Driver 4 | * 5 | * The PIT is a simple device that generates timed signals a specific amount 6 | * of times per second. One of the uses of this timed signal is to make 7 | * preemptive multitasking systems force a context switch if the currently 8 | * running task has been using the CPU for an excessive amount of time. 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | static unsigned long next_ticks = 0; 15 | 16 | static void pctimer_handler(struct idt_data *data); 17 | static int pctimer_init(void); 18 | static int pctimer_open(unsigned int flags); 19 | static int pctimer_close(void); 20 | static unsigned int pctimer_read(unsigned char *buf, unsigned int len); 21 | static unsigned int pctimer_write(unsigned char *buf, unsigned int len); 22 | 23 | static driver_t pctimer_driver = { 24 | .drv_name = "pctimer", 25 | .drv_flags = DV_FCHARDEV, 26 | .drv_init = &pctimer_init, 27 | }; 28 | 29 | static device_t pctimer_device = { 30 | .dev_family = &pctimer_driver, 31 | .dev_open = &pctimer_open, 32 | .dev_close = &pctimer_close, 33 | .dev_read_chr = &pctimer_read, 34 | }; 35 | 36 | static void 37 | pctimer_handler(struct idt_data *data) 38 | { 39 | next_ticks++; 40 | } 41 | 42 | static int 43 | pctimer_init(void) 44 | { 45 | idt_set_handler(0x20, &pctimer_handler); 46 | device_install(&pctimer_device, "pit"); 47 | return 0; 48 | } 49 | 50 | static int 51 | pctimer_open(unsigned int flags) 52 | { 53 | return 0; 54 | } 55 | 56 | static int 57 | pctimer_close() 58 | { 59 | return 0; 60 | } 61 | 62 | static unsigned int 63 | pctimer_read(unsigned char *buf, unsigned int len) 64 | { 65 | static char *letters = "0123456789ABCDEF"; 66 | unsigned int i, last, read = 0; 67 | unsigned long ticks = next_ticks; 68 | char conversion[8] = {0, 0, 0, 0, 0, 0, 0, 0}; 69 | for (i = 0; i < 8; i++) { 70 | last = ticks & 0xF; 71 | conversion[i] = letters[last]; 72 | ticks >>= 4; 73 | } 74 | for (i = 7; i >= 0 && len--; --i) { 75 | *buf++ = conversion[i]; 76 | read++; 77 | } 78 | return read; 79 | } 80 | 81 | DEVICE_DESCRIPTOR(pctimer, pctimer_driver); 82 | -------------------------------------------------------------------------------- /kernel/device/rtclock.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * \brief Clock 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | #define REG_SECONDS 0 10 | #define REG_MINUTES 2 11 | #define REG_HOURS 4 12 | #define REG_DAY 7 13 | #define REG_MONTH 8 14 | #define REG_YEAR 9 15 | #define REG_CENTURY 0x32 16 | #define REG_STATUS_B 0xB 17 | 18 | /** Converts from RTC-BCD to BIN. */ 19 | #define BCD_TO_BIN(bcd) ((bcd / 16) * 10) + (bcd & 0xF) 20 | 21 | struct rtclock { 22 | unsigned char is_binary; 23 | unsigned short year; 24 | unsigned char seconds, minutes, hours; 25 | unsigned char month, day; 26 | }; 27 | 28 | static struct rtclock rtc_clock; 29 | 30 | static int 31 | sameclock(struct rtclock *a, struct rtclock *b) 32 | { 33 | return a->year == b->year && a->month == b->month && a->day == b->day 34 | && a->hours == b->hours && a->minutes == b->minutes 35 | && a->seconds == b->seconds; 36 | } 37 | 38 | static void 39 | copyclock(struct rtclock *clock) 40 | { 41 | rtc_clock.year = clock->year; 42 | rtc_clock.month = clock->month; 43 | rtc_clock.day = clock->day; 44 | rtc_clock.hours = clock->hours; 45 | rtc_clock.minutes = clock->minutes; 46 | rtc_clock.seconds = clock->seconds; 47 | } 48 | 49 | static unsigned char 50 | read_cmos(unsigned int reg) 51 | { 52 | unsigned char value; 53 | 54 | /* We do not want interrupts while we are working here. */ 55 | __asm__("cli"); 56 | port_out_byte(0x70, reg); 57 | value = port_in_byte(0x71); 58 | __asm__("sti"); 59 | return value; 60 | } 61 | 62 | static void 63 | readclock(struct rtclock *nuclear_somali) 64 | { 65 | unsigned int century; 66 | 67 | nuclear_somali->seconds = read_cmos(REG_SECONDS); 68 | nuclear_somali->minutes = read_cmos(REG_MINUTES); 69 | nuclear_somali->hours = read_cmos(REG_HOURS); 70 | nuclear_somali->year = read_cmos(REG_YEAR); 71 | nuclear_somali->month = read_cmos(REG_MONTH); 72 | nuclear_somali->day = read_cmos(REG_DAY); 73 | nuclear_somali->day = read_cmos(REG_DAY); 74 | century = read_cmos(REG_CENTURY); 75 | if (!rtc_clock.is_binary) { 76 | nuclear_somali->seconds = BCD_TO_BIN(nuclear_somali->seconds); 77 | nuclear_somali->minutes = BCD_TO_BIN(nuclear_somali->minutes); 78 | nuclear_somali->hours = BCD_TO_BIN(nuclear_somali->hours); 79 | nuclear_somali->year = BCD_TO_BIN(nuclear_somali->year); 80 | nuclear_somali->month = BCD_TO_BIN(nuclear_somali->month); 81 | nuclear_somali->day = BCD_TO_BIN(nuclear_somali->day); 82 | century = BCD_TO_BIN(century); 83 | } 84 | nuclear_somali->year = (century * 100) + nuclear_somali->year; 85 | } 86 | 87 | static void 88 | update_clock(void) 89 | { 90 | /* 91 | * The clock could change while we are reading it. Therefore, we are 92 | * going to read the clock twice. If the values are not the same, we 93 | * must assume that the clock changed while we was reading it, so 94 | * we should read it again. 95 | */ 96 | struct rtclock clock_a, clock_b; 97 | 98 | do { 99 | readclock(&clock_a); 100 | readclock(&clock_b); 101 | } while (!sameclock(&clock_a, &clock_b)); 102 | copyclock(&clock_b); 103 | } 104 | 105 | static int clock_init(void); 106 | static int clock_open(unsigned int flags); 107 | static int clock_close(void); 108 | static unsigned int clock_read(unsigned char *buf, unsigned int len); 109 | 110 | static driver_t clock_driver = { 111 | .drv_name = "clock", 112 | .drv_init = &clock_init, 113 | .drv_flags = DV_FCHARDEV, 114 | }; 115 | 116 | DEVICE_DESCRIPTOR(clock, clock_driver); 117 | 118 | static device_t clock_device = { 119 | .dev_family = &clock_driver, 120 | .dev_close = &clock_close, 121 | .dev_open = &clock_open, 122 | .dev_read_chr = &clock_read, 123 | }; 124 | 125 | static int 126 | clock_init(void) 127 | { 128 | unsigned char van_damme = read_cmos(REG_STATUS_B); 129 | rtc_clock.is_binary = (van_damme & 0x4) != 0; 130 | device_install(&clock_device, "clock"); 131 | return 0; 132 | } 133 | 134 | static int 135 | clock_open(unsigned int flags) 136 | { 137 | return 0; 138 | } 139 | 140 | static void 141 | write_number(unsigned char *buf, unsigned int num, unsigned int len) 142 | { 143 | char tmp[16]; 144 | int i = 0; 145 | 146 | while (num > 0) { 147 | tmp[i++] = '0' + (num % 10); 148 | num /= 10; 149 | } 150 | while (i < len) { 151 | tmp[i++] = '0'; 152 | } 153 | while (i >= 0) { 154 | *buf++ = tmp[--i]; 155 | } 156 | } 157 | 158 | static unsigned int 159 | clock_read(unsigned char *buf, unsigned int len) 160 | { 161 | // YYYYMMDDHHMMSS 162 | if (len < 15) { 163 | return 0; 164 | } 165 | 166 | update_clock(); 167 | 168 | write_number(&buf[0], rtc_clock.year, 4); 169 | write_number(&buf[4], rtc_clock.month, 2); 170 | write_number(&buf[6], rtc_clock.day, 2); 171 | write_number(&buf[8], rtc_clock.hours, 2); 172 | write_number(&buf[10], rtc_clock.minutes, 2); 173 | write_number(&buf[12], rtc_clock.seconds, 2); 174 | buf[14] = 0; 175 | return 15; 176 | } 177 | 178 | static int 179 | clock_close(void) 180 | { 181 | return 0; 182 | } 183 | -------------------------------------------------------------------------------- /kernel/device/uart8250.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * \brief UART 8250 driver 4 | * 5 | * UART 8250 is the basic serial port for the PC platform. While the COM port 6 | * is deprecated and most computers don't have a COM port these days, it is 7 | * still a extremely popular debug tool, because most virtual machines will 8 | * provide a way to emulate COM ports, allowing to be used as a log or as a 9 | * quick shell. 10 | * 11 | * The PC architecture supports up to four serial ports. At the moment, 12 | * NativeOS will only support one serial port. Communication between the PC 13 | * and the UART is handled via IO ports. There are eight ports per UART. 14 | * For instance, for the first UART device these ports will be 0x3F8 to 0x3FF. 15 | * 16 | * Use cases for the UART port: 17 | * 18 | * \li The most important thing about the UART is the RX/TX (receive/transmit). 19 | * OUT a byte to the 0x3F8 port, it will be send to the serial port. 20 | * IN a byte from the 0x3F8 port, it will be received from the serial port. 21 | * \li Then there is the line status register. It is in PORT+5 and you read 22 | * the bits, you can have information about the status of the port, such as 23 | * whether there is data waiting to be read. 24 | * \li Settings are configured using the Line Control Register, which is in 25 | * PORT+3. Update the value of this port to tweak the behaviour of the 26 | * serial port. Should be used to tweak the preferences like baud rate or 27 | * parity. 28 | * 29 | * Settings that can be configured realisticly: 30 | * 31 | * \li The baud rate. Turn the DLAB bit ON in the line control register, send 32 | * the divisor for 115200 to use through PORT (LSB) and PORT+1 (MSB), turn 33 | * off the DLAB bit. 34 | * \li The data bits. Use bits 1 and 0 of the line control register to set it 35 | * to 5 (00), 6 (01), 7 (10) or 8 (11). Nowadays 8 is the standard, and 36 | * also the only accepted configuration at the moment. 37 | * \li The stop bits, which are used for sync the bits of each byte. Set the 38 | * bit 2 of the line control register to 0 for 1 bit, or 1 for 1.5 or 2 39 | * bits (this use case depends on even bits per character). These days, 40 | * standard is 1 bit. 41 | * \li The parity, used for basic checksums to detect transmission errors. 42 | * Use bit 3 to enable parity (0 = OFF, 1 = ON), and if ON, use the bits 43 | * 4 and 5 to set the parity mode (00 = ODD, 01 = EVEN, 10 = MARK, 44 | * 11 = SPACE). These days, standard is NO PARITY. 45 | * \li Interrupts. With DLAB off, write a value to PORT+1 to configure the 46 | * interrupt mode. Depending on the bits that are set to 1, you will 47 | * receive more or less interruptions. Bit 0 = data available, bit 1 = 48 | * transmitter is empty, bit 2 = error or break, bit 3 = status changed. 49 | * 50 | * Interrupts for the serial port will come from IRQ 12 for the first port, 51 | * and IRQ 11 for the second port. Assuming that protected mode is enabled, 52 | * which will be. 53 | */ 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | 62 | #define UART_PORT 0x3F8 63 | 64 | #define BAUD_RATE_115200 0 65 | #define BAUD_RATE_38400 1 66 | #define BAUD_RATE_19200 2 67 | #define BAUD_RATE_9600 3 68 | #define BAUD_RATE_7200 4 69 | #define BAUD_RATE_4800 5 70 | #define BAUD_RATE_3600 6 71 | #define BAUD_RATE_2400 7 72 | #define BAUD_RATE_2000 8 73 | #define BAUD_RATE_1800 9 74 | #define BAUD_RATE_1200 10 75 | #define BAUD_RATE_600 11 76 | #define BAUD_RATE_300 12 77 | #define BAUD_RATE_150 13 78 | #define BAUD_RATE_110 14 79 | #define BAUD_RATE_50 15 80 | 81 | /** 82 | * \brief List of divisor values 83 | * These are the accepted divisor values that can be set through the DLAB port 84 | * when the baud rate is being configured. It is possible to pick one of these 85 | * through code by using the BAUD_RATE constant definitions. 86 | */ 87 | static unsigned short uart8250_baud_divisors[] = { 88 | 0x001, // 115200 89 | 0x003, // 38400 90 | 0x006, // 19200 91 | 0x00C, // 9600 92 | 0x010, // 7200 93 | 0x018, // 4800 94 | 0x020, // 3600 95 | 0x030, // 2400 96 | 0x03A, // 2000 97 | 0x040, // 1800 98 | 0x060, // 1200 99 | 0x0C0, // 600 100 | 0x180, // 300 101 | 0x300, // 150 102 | 0x417, // 110 103 | 0x900, // 50 104 | 0x000, // NUL 105 | }; 106 | 107 | static struct { 108 | uint32_t flags; /**< Holds the flags of the current open mode. */ 109 | ringbuf_t *rx_buf; /**< Ringbuffer to store the bytes until read. */ 110 | } uart8250_context; 111 | 112 | /** 113 | * \brief Reconfigure the UART device 114 | * Based on the current configuration of the context, set the device 115 | */ 116 | static void 117 | uart8250_reconfigure(void) 118 | { 119 | unsigned short divisor = uart8250_baud_divisors[BAUD_RATE_19200]; 120 | port_out_byte(UART_PORT + 1, 0x00); // turn off interrupts 121 | 122 | // Set the baud rate. 123 | port_out_byte(UART_PORT + 3, 0x80); // DLAB = on 124 | port_out_byte(UART_PORT + 0, divisor & 0xFF); 125 | port_out_byte(UART_PORT + 1, divisor >> 8); 126 | 127 | // Configure interrupts and status line. 128 | // WHY DO I WASTE SO MANY HOURS DEBUGGING THIS PIECE OF ANCIENT SHIT 129 | port_out_byte(UART_PORT + 3, 0x03); // 8N1 Mode 130 | // port_outb(UART_PORT + 2, 0xC7); // FIFO and all this shit 131 | port_out_byte(UART_PORT + 4, 0x07); // Modem, RTS, DTR, Interrupts ON 132 | port_out_byte(UART_PORT + 1, 0x01); // Actually turn on ready interrupts 133 | } 134 | 135 | static void 136 | acknowledge(void) 137 | { 138 | port_in_byte(UART_PORT + 2); 139 | } 140 | 141 | static int uart8250_init(void); 142 | static void uart8250_tini(void); 143 | static int uart8250_open(unsigned int flags); 144 | static int uart8250_close(void); 145 | static uint32_t uart8250_read(unsigned char *buf, uint32_t len); 146 | static uint32_t uart8250_write(unsigned char *buf, uint32_t len); 147 | 148 | static driver_t uart8250_driver = { 149 | .drv_name = "uart8250", 150 | .drv_flags = DV_FCHARDEV, 151 | .drv_init = &uart8250_init, 152 | .drv_tini = &uart8250_tini, 153 | }; 154 | 155 | static device_t uart8250_device = { 156 | .dev_family = &uart8250_driver, 157 | .dev_open = &uart8250_open, 158 | .dev_read_chr = &uart8250_read, 159 | .dev_write_chr = &uart8250_write, 160 | .dev_close = &uart8250_close, 161 | }; 162 | 163 | static void 164 | uart8250_interrupt(struct idt_data *idt) 165 | { 166 | uint8_t value; 167 | 168 | /* Assert there is data. */ 169 | while (port_in_byte(UART_PORT + 5) & 1) { 170 | value = port_in_byte(UART_PORT); 171 | if (uart8250_context.flags & VO_FWRITE) { 172 | ringbuf_write(uart8250_context.rx_buf, value); 173 | } 174 | } 175 | acknowledge(); 176 | } 177 | 178 | static int 179 | uart8250_init(void) 180 | { 181 | device_install(&uart8250_device, "uart"); 182 | uart8250_reconfigure(); 183 | idt_set_handler(0x24, &uart8250_interrupt); 184 | acknowledge(); 185 | return 0; 186 | } 187 | 188 | static void 189 | uart8250_tini(void) 190 | { 191 | device_remove("uart"); 192 | } 193 | 194 | static int 195 | uart8250_open(unsigned int flags) 196 | { 197 | /* Device is already opened. */ 198 | if (uart8250_context.flags) { 199 | return -1; 200 | } 201 | 202 | /* Reserve the device. */ 203 | uart8250_context.flags = flags; 204 | uart8250_context.rx_buf = ringbuf_alloc(4096); 205 | 206 | return 0; 207 | } 208 | 209 | static int 210 | uart8250_close(void) 211 | { 212 | /* Is this possible? Do I need a spinlock? I don't want to know. */ 213 | if (!uart8250_context.flags) { 214 | return -1; 215 | } else { 216 | ringbuf_free(uart8250_context.rx_buf); 217 | uart8250_context.flags = 0; 218 | return 0; 219 | } 220 | } 221 | 222 | static uint32_t 223 | uart8250_read(unsigned char *buf, uint32_t len) 224 | { 225 | size_t read_bytes = 0; 226 | while (read_bytes <= len 227 | && ringbuf_test_ready(uart8250_context.rx_buf)) { 228 | *buf++ = ringbuf_read(uart8250_context.rx_buf); 229 | read_bytes++; 230 | } 231 | return read_bytes; 232 | } 233 | 234 | static uint32_t 235 | uart8250_write_binary(unsigned char *buf, uint32_t len) 236 | { 237 | unsigned int write_bytes = 0; 238 | while (len--) { 239 | port_out_byte(UART_PORT, *buf++); 240 | write_bytes++; 241 | } 242 | return write_bytes; 243 | } 244 | 245 | static uint32_t 246 | uart8250_write_non_binary(unsigned char *buf, uint32_t len) 247 | { 248 | unsigned int write_bytes = 0; 249 | while (len--) { 250 | switch (*buf) { 251 | case '\r': 252 | port_out_byte(UART_PORT, '\r'); 253 | port_out_byte(UART_PORT, '\n'); 254 | write_bytes += 2; 255 | buf++; 256 | break; 257 | case 127: 258 | port_out_byte(UART_PORT, '\b'); 259 | write_bytes++; 260 | buf++; 261 | break; 262 | default: 263 | port_out_byte(UART_PORT, *buf++); 264 | write_bytes++; 265 | break; 266 | } 267 | } 268 | return write_bytes; 269 | } 270 | 271 | static uint32_t 272 | uart8250_write(unsigned char *buf, uint32_t len) 273 | { 274 | if (!buf || !*buf) { 275 | return 0; 276 | } 277 | if (uart8250_context.flags & VO_FBINARY) { 278 | return uart8250_write_binary(buf, len); 279 | } else { 280 | return uart8250_write_non_binary(buf, len); 281 | } 282 | } 283 | 284 | DEVICE_DESCRIPTOR(uart8250, uart8250_driver); 285 | -------------------------------------------------------------------------------- /kernel/device/vgafb.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * \brief VGA Framebuffer 4 | * 5 | * The VGA Framebuffer is a block device that exposes the memory region 6 | * reserved to the framebuffer, so that other code can use VFS operations 7 | * to get or change the contents of the screen. 8 | * 9 | * Applications will usually not use this file directly, but rather delegate 10 | * changing the contents of the screen to the virtual console device, which 11 | * also will make additional transformations, such as changing attributes. 12 | */ 13 | 14 | #include "vgafb.h" 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #define VGA_BASE 0xB8000 21 | #define VGA_COLS 80 22 | #define VGA_ROWS 25 23 | #define VGA_CHARS (VGA_COLS * VGA_ROWS) 24 | #define VGA_SIZE (VGA_CHARS * 2) 25 | #define VGA_LIMIT (VGA_BASE + VGA_SIZE) 26 | 27 | #define VGA_IOR_ADDR 0x3D4 28 | #define VGA_IOR_DATA 0x3D5 29 | 30 | static int vgafb_init(void); 31 | static int vgafb_open(unsigned int flags); 32 | static int vgafb_close(void); 33 | static unsigned int 34 | vgafb_read(unsigned char *buf, unsigned int off, unsigned int len); 35 | static unsigned int 36 | vgafb_write(unsigned char *buf, unsigned int off, unsigned int len); 37 | static int vgafb_ioctl(int iorq, void *argp); 38 | 39 | static driver_t vgafb_driver = { 40 | .drv_name = "vgafb", 41 | .drv_flags = DV_FBLCKDEV, 42 | .drv_init = &vgafb_init, 43 | }; 44 | 45 | static device_t vgafb_device = { 46 | .dev_family = &vgafb_driver, 47 | .dev_open = &vgafb_open, 48 | .dev_close = &vgafb_close, 49 | .dev_read_blk = &vgafb_read, 50 | .dev_write_blk = &vgafb_write, 51 | .dev_ioctl = &vgafb_ioctl, 52 | }; 53 | 54 | static int 55 | vgafb_init(void) 56 | { 57 | unsigned int i; 58 | unsigned short *vga = (unsigned short *) VGA_BASE; 59 | 60 | if (device_install(&vgafb_device, "fb") < 0) { 61 | return -1; 62 | } 63 | for (i = 0; i < VGA_CHARS; i++) 64 | vga[i] = 0x700; 65 | return 0; 66 | } 67 | 68 | static int 69 | vgafb_open(unsigned int flags) 70 | { 71 | return 0; 72 | } 73 | 74 | static int 75 | vgafb_close(void) 76 | { 77 | return 0; 78 | } 79 | 80 | static unsigned int 81 | vgafb_read(unsigned char *buf, unsigned int offt, unsigned int len) 82 | { 83 | unsigned char *start, *end, *ptr; 84 | unsigned int read_bytes = 0; 85 | 86 | if (offt < VGA_SIZE) { 87 | start = (unsigned char *) VGA_BASE + offt; 88 | end = start + len; 89 | if (end >= (unsigned char *) VGA_LIMIT) { 90 | end = (unsigned char *) VGA_LIMIT; 91 | } 92 | 93 | for (ptr = start; ptr < end; ptr++) { 94 | *buf++ = *ptr; 95 | read_bytes++; 96 | } 97 | } 98 | 99 | return read_bytes; 100 | } 101 | 102 | static unsigned int 103 | vgafb_write(unsigned char *buf, unsigned int offt, unsigned int len) 104 | { 105 | unsigned char *start, *end, *ptr; 106 | unsigned int write_bytes = 0; 107 | 108 | if (offt < VGA_SIZE) { 109 | start = (unsigned char *) VGA_BASE + offt; 110 | end = start + len; 111 | if (end >= (unsigned char *) VGA_LIMIT) { 112 | end = (unsigned char *) VGA_LIMIT; 113 | } 114 | 115 | for (ptr = start; ptr < end; ptr++) { 116 | *ptr = *buf++; 117 | write_bytes++; 118 | } 119 | } 120 | 121 | return write_bytes; 122 | } 123 | 124 | static void 125 | enablecursor(int enabled) 126 | { 127 | unsigned char in; 128 | if (enabled) { 129 | port_out_byte(VGA_IOR_ADDR, 0xA); 130 | in = port_in_byte(VGA_IOR_DATA) & 0x1F; 131 | port_out_byte(VGA_IOR_DATA, in | 0xD); 132 | port_out_byte(VGA_IOR_ADDR, 0xB); 133 | in = port_in_byte(VGA_IOR_DATA) & 0xC0; 134 | port_out_byte(VGA_IOR_DATA, in | 0xF); 135 | } else { 136 | port_out_byte(VGA_IOR_ADDR, 0xA); 137 | port_out_byte(VGA_IOR_DATA, 0x20); 138 | } 139 | } 140 | 141 | static void 142 | movecursor(unsigned short abspos) 143 | { 144 | port_out_byte(VGA_IOR_ADDR, 0xE); 145 | port_out_byte(VGA_IOR_DATA, (abspos >> 8) & 0xFF); 146 | port_out_byte(VGA_IOR_ADDR, 0xF); 147 | port_out_byte(VGA_IOR_DATA, abspos & 0xFF); 148 | } 149 | 150 | static int 151 | vgafb_ioctl(int iorq, void *arg) 152 | { 153 | switch (iorq) { 154 | case VGAFB_IOCTL_SETCUR: 155 | enablecursor(((int) arg) != 0); 156 | return 0; 157 | case VGAFB_IOCTL_MOVECUR: 158 | movecursor(*(unsigned short *) arg); 159 | return 0; 160 | } 161 | 162 | return -1; 163 | } 164 | 165 | DEVICE_DESCRIPTOR(vgafb, vgafb_driver); -------------------------------------------------------------------------------- /kernel/device/vgafb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define VGAFB_IOCTL_SETCUR 0x0 4 | #define VGAFB_IOCTL_MOVECUR 0x1 -------------------------------------------------------------------------------- /kernel/device/vtcon/scancodes.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * \brief Scancodes 4 | * 5 | * Not the prettiest way to do this at the moment, but this file contains 6 | * the translation tables used to convert between a scancode and a virtual 7 | * key in the different keyboard layouts that are supported at the moment. 8 | */ 9 | #pragma once 10 | 11 | /* Non printable characters from the ASCII table. */ 12 | #define VK_NUL 0 13 | #define VK_SOH 1 14 | #define VK_STX 2 15 | #define VK_ETX 3 16 | #define VK_EOT 4 17 | #define VK_ENQ 5 18 | #define VK_ACK 6 19 | #define VK_BEL 7 20 | #define VK_BS 8 21 | #define VK_HT 9 22 | #define VK_LF 10 23 | #define VK_VT 11 24 | #define VK_FF 12 25 | #define VK_CR 13 26 | #define VK_SO 14 27 | #define VK_SI 15 28 | #define VK_DLE 16 29 | #define VK_DC1 17 30 | #define VK_DC2 18 31 | #define VK_DC3 19 32 | #define VK_DC4 20 33 | #define VK_NAK 21 34 | #define VK_SYN 22 35 | #define VK_ETB 23 36 | #define VK_CAN 24 37 | #define VK_EM 25 38 | #define VK_SUB 26 39 | #define VK_ESC 27 40 | #define VK_FS 28 41 | #define VK_GS 29 42 | #define VK_RS 30 43 | #define VK_US 31 44 | #define VK_SP 32 45 | #define VK_DEL 127 46 | #define VK_CUP 224 47 | #define VK_CLFT 225 48 | #define VK_CRGT 226 49 | #define VK_CDWN 227 50 | #define VK_PGUP 228 51 | #define VK_PGDN 229 52 | #define VK_INSERT 230 53 | #define VK_HOME 231 54 | #define VK_END 232 55 | #define VK_LCTL 233 56 | #define VK_RCTL 234 57 | #define VK_LSHF 235 58 | #define VK_RSHF 236 59 | #define VK_LALT 237 60 | #define VK_RALT 238 61 | #define VK_CAPL 239 62 | #define VK_NUML 240 63 | #define VK_SCRL 241 64 | #define VK_BTAB 242 65 | #define VK_F1 243 66 | #define VK_F2 244 67 | #define VK_F3 245 68 | #define VK_F4 246 69 | #define VK_F5 247 70 | #define VK_F6 248 71 | #define VK_F7 249 72 | #define VK_F8 250 73 | #define VK_F9 251 74 | #define VK_F10 252 75 | #define VK_F11 253 76 | #define VK_F12 254 77 | 78 | typedef struct kbdev { 79 | unsigned short vk; 80 | unsigned short flags; 81 | } kbdev_t; 82 | 83 | typedef struct scancode { 84 | unsigned char nomod; /**< Mapping for no mod is down. */ 85 | unsigned char shift; /**< Mapping for SHIFT is on. */ 86 | unsigned char ctrl; /**< Mapping for CTRL is on. */ 87 | unsigned char ctrlshift; /**< Mapping for CTRL-SHIFT is on. */ 88 | unsigned char alt; /**< Mapping for ALT is on. */ 89 | unsigned char altshift; /**< Mapping for SHIFT-ALT is on. */ 90 | unsigned char altctrl; /**< Mapping for CTRL-ALT is on. */ 91 | unsigned char altctrlshift; /**< Mapping for CTRL-SHIFT-ALT is on. */ 92 | } scancode_t; 93 | 94 | static struct scancode us_scancodes_1[] = { 95 | {VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL}, 96 | {VK_ESC, VK_ESC, VK_ESC, VK_ESC, VK_ESC, VK_ESC, VK_ESC, VK_ESC}, 97 | {'1', '!', VK_NUL, VK_NUL, '1', '!', VK_NUL, VK_NUL}, 98 | {'2', '@', VK_NUL, VK_NUL, '1', '@', VK_NUL, VK_NUL}, 99 | {'3', '#', VK_NUL, VK_NUL, '1', '#', VK_NUL, VK_NUL}, 100 | {'4', '$', VK_NUL, VK_NUL, '1', '$', VK_NUL, VK_NUL}, 101 | {'5', '%', VK_NUL, VK_NUL, '1', '%', VK_NUL, VK_NUL}, 102 | {'6', '^', VK_NUL, VK_NUL, '1', '^', VK_NUL, VK_NUL}, 103 | {'7', '&', VK_NUL, VK_NUL, '1', '&', VK_NUL, VK_NUL}, 104 | {'8', '*', VK_NUL, VK_NUL, '1', '*', VK_NUL, VK_NUL}, 105 | {'9', '(', VK_NUL, VK_NUL, '1', '(', VK_NUL, VK_NUL}, 106 | {'0', ')', VK_NUL, VK_NUL, '1', ')', VK_NUL, VK_NUL}, 107 | {'-', '_', VK_US, VK_US, '-', '_', VK_US, VK_US}, 108 | {'=', '+', VK_NUL, VK_NUL, '=', '+', VK_NUL, VK_NUL}, 109 | {VK_BS, VK_BS, VK_DEL, VK_DEL, VK_BS, VK_BS, VK_DEL, VK_DEL}, 110 | {VK_HT, VK_BTAB, VK_NUL, VK_NUL, VK_VT, VK_BTAB, VK_NUL, VK_NUL}, 111 | {'q', 'Q', VK_DC1, VK_DC1, 'q', 'Q', VK_DC1, VK_DC1}, 112 | {'w', 'W', VK_ETB, VK_ETB, 'w', 'W', VK_ETB, VK_ETB}, 113 | {'e', 'E', VK_ENQ, VK_ENQ, 'e', 'E', VK_ENQ, VK_ENQ}, 114 | {'r', 'R', VK_DC2, VK_DC2, 'r', 'R', VK_DC2, VK_DC2}, 115 | {'t', 'T', VK_DC4, VK_DC4, 't', 'T', VK_DC4, VK_DC4}, 116 | {'y', 'Y', VK_EM, VK_EM, 'y', 'Y', VK_EM, VK_EM}, 117 | {'u', 'U', VK_NAK, VK_NAK, 'u', 'U', VK_NAK, VK_NAK}, 118 | {'i', 'I', VK_HT, VK_HT, 'i', 'I', VK_HT, VK_HT}, 119 | {'o', 'O', VK_SI, VK_SI, 'o', 'O', VK_SI, VK_SI}, 120 | {'p', 'P', VK_DLE, VK_DLE, 'p', 'P', VK_DLE, VK_DLE}, 121 | {'[', '{', VK_ESC, VK_ESC, '[', '{', VK_ESC, VK_ESC}, 122 | {']', '}', VK_GS, VK_GS, ']', '}', VK_GS, VK_GS}, 123 | {VK_LF, VK_LF, VK_CR, VK_CR, VK_LF, VK_LF, VK_CR, VK_CR}, 124 | {VK_LCTL, VK_LCTL, VK_LCTL, VK_LCTL, VK_LCTL, VK_LCTL, VK_LCTL, VK_LCTL}, 125 | {'a', 'A', VK_SOH, VK_SOH, 'a', 'A', VK_SOH, VK_SOH}, 126 | {'s', 'S', VK_DC3, VK_DC3, 's', 'S', VK_DC3, VK_DC3}, 127 | {'d', 'D', VK_EOT, VK_EOT, 'd', 'D', VK_EOT, VK_EOT}, 128 | {'f', 'F', VK_ACK, VK_ACK, 'f', 'F', VK_ACK, VK_ACK}, 129 | {'g', 'G', VK_BEL, VK_BEL, 'g', 'G', VK_BEL, VK_BEL}, 130 | {'h', 'H', VK_BS, VK_BS, 'h', 'H', VK_BS, VK_BS}, 131 | {'j', 'J', VK_LF, VK_LF, 'j', 'J', VK_LF, VK_LF}, 132 | {'k', 'K', VK_VT, VK_VT, 'k', 'K', VK_VT, VK_VT}, 133 | {'l', 'L', VK_FF, VK_FF, 'l', 'L', VK_FF, VK_FF}, 134 | {';', ':', VK_NUL, VK_NUL, ';', ':', VK_NUL, VK_NUL}, 135 | {'\'', '"', VK_NUL, VK_NUL, '\'', '"', VK_NUL, VK_NUL}, 136 | {'`', '~', VK_NUL, VK_NUL, '`', '~', VK_NUL, VK_NUL}, 137 | {VK_LSHF, VK_LSHF, VK_LSHF, VK_LSHF, VK_LSHF, VK_LSHF, VK_LSHF, VK_LSHF}, 138 | {'\\', '|', VK_FS, VK_FS, '\\', '|', VK_FS, VK_FS}, 139 | {'z', 'Z', VK_SUB, VK_SUB, 'z', 'Z', VK_SUB, VK_SUB}, 140 | {'x', 'X', VK_CAN, VK_CAN, 'x', 'X', VK_CAN, VK_CAN}, 141 | {'c', 'C', VK_ETX, VK_ETX, 'c', 'C', VK_ETX, VK_ETX}, 142 | {'v', 'V', VK_SYN, VK_SYN, 'v', 'V', VK_SYN, VK_SYN}, 143 | {'b', 'B', VK_STX, VK_STX, 'b', 'B', VK_STX, VK_STX}, 144 | {'n', 'N', VK_SO, VK_SO, 'n', 'N', VK_SO, VK_SO}, 145 | {'m', 'M', VK_CR, VK_CR, 'm', 'M', VK_CR, VK_CR}, 146 | {',', '<', VK_NUL, VK_NUL, ',', '<', VK_NUL, VK_NUL}, 147 | {'.', '>', VK_NUL, VK_NUL, '.', '>', VK_NUL, VK_NUL}, 148 | {'/', '?', VK_NUL, VK_NUL, '/', '?', VK_NUL, VK_NUL}, 149 | {VK_RSHF, VK_RSHF, VK_RSHF, VK_RSHF, VK_RSHF, VK_RSHF, VK_RSHF, VK_RSHF}, 150 | {'*', '*', '*', '*', '*', '*', '*', '*'}, 151 | {VK_LALT, VK_LALT, VK_LALT, VK_LALT, VK_LALT, VK_LALT, VK_LALT, VK_LALT}, 152 | {' ', ' ', VK_NUL, ' ', ' ', ' ', VK_NUL, ' '}, 153 | {VK_CAPL, VK_CAPL, VK_CAPL, VK_CAPL, VK_CAPL, VK_CAPL, VK_CAPL, VK_CAPL}, 154 | {VK_F1, VK_F1, VK_F1, VK_F1, VK_F1, VK_F1, VK_F1, VK_F1}, 155 | {VK_F2, VK_F2, VK_F2, VK_F2, VK_F2, VK_F2, VK_F2, VK_F2}, 156 | {VK_F3, VK_F3, VK_F3, VK_F3, VK_F3, VK_F3, VK_F3, VK_F3}, 157 | {VK_F4, VK_F4, VK_F4, VK_F4, VK_F4, VK_F4, VK_F4, VK_F4}, 158 | {VK_F5, VK_F5, VK_F5, VK_F5, VK_F5, VK_F5, VK_F5, VK_F5}, 159 | {VK_F6, VK_F6, VK_F6, VK_F6, VK_F6, VK_F6, VK_F6, VK_F6}, 160 | {VK_F7, VK_F7, VK_F7, VK_F7, VK_F7, VK_F7, VK_F7, VK_F7}, 161 | {VK_F8, VK_F8, VK_F8, VK_F8, VK_F8, VK_F8, VK_F8, VK_F8}, 162 | {VK_F9, VK_F9, VK_F9, VK_F9, VK_F9, VK_F9, VK_F9, VK_F9}, 163 | {VK_F10, VK_F10, VK_F10, VK_F10, VK_F10, VK_F10, VK_F10, VK_F10}, 164 | {VK_NUML, VK_NUML, VK_NUML, VK_NUML, VK_NUML, VK_NUML, VK_NUML, VK_NUML}, 165 | {VK_SCRL, VK_SCRL, VK_SCRL, VK_SCRL, VK_SCRL, VK_SCRL, VK_SCRL, VK_SCRL}, 166 | {'7', '7', '7', '7', '7', '7', '7', '7'}, 167 | {'8', '8', '8', '8', '8', '8', '8', '8'}, 168 | {'9', '9', '9', '9', '9', '9', '9', '9'}, 169 | {'-', '-', '-', '-', '-', '-', '-', '-'}, 170 | {'4', '4', '4', '4', '4', '4', '4', '4'}, 171 | {'5', '5', '5', '5', '5', '5', '5', '5'}, 172 | {'6', '6', '6', '6', '6', '6', '6', '6'}, 173 | {'+', '+', '+', '+', '+', '+', '+', '+'}, 174 | {'1', '1', '1', '1', '1', '1', '1', '1'}, 175 | {'2', '2', '2', '2', '2', '2', '2', '2'}, 176 | {'3', '3', '3', '3', '3', '3', '3', '3'}, 177 | {'0', '0', '0', '0', '0', '0', '0', '0'}, 178 | {'.', '.', '.', '.', '.', '.', '.', '.'}, 179 | {VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL}, 180 | {VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL}, 181 | {VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL}, 182 | {VK_F11, VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL}, 183 | {VK_F12, VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL, VK_NUL}, 184 | {0, 0, 0, 0, 0, 0, 0, 0}, 185 | /* TODO: Add 0xE0 and 0xF0. */ 186 | }; 187 | -------------------------------------------------------------------------------- /kernel/device/vtcon/vtcon.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * \brief Virtual Console 4 | * 5 | * The Virtual Console is a character device that abstracts the framebuffer 6 | * as a character device, so that the higher level code doesn't have to do 7 | * manually dangerous things - such as manipulating the framebuffer directly, 8 | * in case the protocol ever changes from VGA/CGA to something like VESA. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | static int vtcon_init(void); 19 | 20 | #define VGA_COLS 80 21 | #define VGA_ROWS 25 22 | #define VGA_SIZE (VGA_COLS * VGA_ROWS) 23 | #define VGA_ENTRY(char, fg, bg) (char | fg << 8 | bg << 12) 24 | 25 | #define KBD_MOD_SHIFT 0x01 26 | #define KBD_MOD_CTRL 0x02 27 | #define KBD_MOD_ALT 0x04 28 | #define KBD_MOD_META 0x08 29 | #define KBD_CAP_UP 0x10 30 | #define KBD_LOCK_CAPS 0x100 31 | #define KBD_MODS (KBD_MOD_SHIFT | KBD_MOD_CTRL | KBD_MOD_ALT) 32 | 33 | struct vtcontext { 34 | unsigned int cx, cy; 35 | unsigned char fg, bg; 36 | unsigned int kbd_status; 37 | unsigned short buffer[VGA_COLS * VGA_ROWS]; 38 | }; 39 | 40 | static vfs_node_t *con_fb, *con_kbd; 41 | static struct vtcontext contexts[8]; 42 | static unsigned int current_context = 0; 43 | static struct vtcontext *context = &contexts[0]; 44 | static vfs_node_t *clock = 0; 45 | 46 | static void 47 | drawstatus() 48 | { 49 | char text[80], date[15]; 50 | unsigned char fg, bg; 51 | unsigned short buffer[80]; 52 | unsigned int i; 53 | 54 | /* Compose status bar text. */ 55 | memset(text, 0, 80); 56 | text[1] = '1' + current_context; 57 | strncpy(&text[4], VERSION_NAME, 70); 58 | fs_read(clock, 0, &date, 15); 59 | 60 | /* Render status bar. */ 61 | for (i = 0; i < VGA_COLS; i++) { 62 | fg = i < 3 ? 0x1 : 0x7; 63 | bg = fg == 0x7 ? 0x1 : 0x7; 64 | buffer[i] = VGA_ENTRY(text[i], fg, bg); 65 | } 66 | 67 | /* TODO: Add a sprintf function to never do this again. */ 68 | buffer[VGA_COLS - 21] = VGA_ENTRY(' ', 0x1, 0x7); 69 | buffer[VGA_COLS - 20] = VGA_ENTRY(date[0], 0x1, 0x7); 70 | buffer[VGA_COLS - 19] = VGA_ENTRY(date[1], 0x1, 0x7); 71 | buffer[VGA_COLS - 18] = VGA_ENTRY(date[2], 0x1, 0x7); 72 | buffer[VGA_COLS - 17] = VGA_ENTRY(date[3], 0x1, 0x7); 73 | buffer[VGA_COLS - 16] = VGA_ENTRY('/', 0x1, 0x7); 74 | buffer[VGA_COLS - 15] = VGA_ENTRY(date[4], 0x1, 0x7); 75 | buffer[VGA_COLS - 14] = VGA_ENTRY(date[5], 0x1, 0x7); 76 | buffer[VGA_COLS - 13] = VGA_ENTRY('/', 0x1, 0x7); 77 | buffer[VGA_COLS - 12] = VGA_ENTRY(date[6], 0x1, 0x7); 78 | buffer[VGA_COLS - 11] = VGA_ENTRY(date[7], 0x1, 0x7); 79 | buffer[VGA_COLS - 10] = VGA_ENTRY(' ', 0x1, 0x7); 80 | buffer[VGA_COLS - 9] = VGA_ENTRY(date[8], 0x1, 0x7); 81 | buffer[VGA_COLS - 8] = VGA_ENTRY(date[9], 0x1, 0x7); 82 | buffer[VGA_COLS - 7] = VGA_ENTRY(':', 0x1, 0x7); 83 | buffer[VGA_COLS - 6] = VGA_ENTRY(date[10], 0x1, 0x7); 84 | buffer[VGA_COLS - 5] = VGA_ENTRY(date[11], 0x1, 0x7); 85 | buffer[VGA_COLS - 4] = VGA_ENTRY(':', 0x1, 0x7); 86 | buffer[VGA_COLS - 3] = VGA_ENTRY(date[12], 0x1, 0x7); 87 | buffer[VGA_COLS - 2] = VGA_ENTRY(date[13], 0x1, 0x7); 88 | buffer[VGA_COLS - 1] = VGA_ENTRY(' ', 0x1, 0x7); 89 | 90 | fs_write(con_fb, 2 * VGA_COLS * (VGA_ROWS - 1), buffer, sizeof(buffer)); 91 | } 92 | 93 | static void 94 | resetcontext(unsigned int i) 95 | { 96 | unsigned int pos; 97 | 98 | contexts[i].cx = 0; 99 | contexts[i].cy = 0; 100 | contexts[i].fg = 7; 101 | contexts[i].bg = 0; 102 | for (pos = 0; pos < VGA_ROWS * VGA_COLS; pos++) { 103 | contexts[i].buffer[pos] = VGA_ENTRY(' ', 0x7, 0x0); 104 | } 105 | } 106 | 107 | static void 108 | resetcontexts() 109 | { 110 | unsigned int i; 111 | for (i = 0; i < 8; i++) { 112 | resetcontext(i); 113 | } 114 | } 115 | 116 | static inline void 117 | syncfbcursor() 118 | { 119 | unsigned short abspos = context->cy * VGA_COLS + context->cx; 120 | fs_ioctl(con_fb, VGAFB_IOCTL_MOVECUR, &abspos); 121 | } 122 | 123 | static void 124 | switchcontext(unsigned int to) 125 | { 126 | if (to < 8) { 127 | fs_read(con_fb, 0, context->buffer, sizeof(context->buffer)); 128 | current_context = to; 129 | context = &contexts[current_context]; 130 | fs_write(con_fb, 0, context->buffer, sizeof(context->buffer)); 131 | drawstatus(); 132 | syncfbcursor(); 133 | } 134 | } 135 | 136 | static void 137 | clearscreen() 138 | { 139 | int x, y; 140 | unsigned int pos; 141 | unsigned short entry; 142 | 143 | for (y = 0; y < VGA_ROWS; y++) { 144 | for (x = 0; x < VGA_COLS; x++) { 145 | pos = y * VGA_COLS + x; 146 | entry = VGA_ENTRY(' ', context->fg, context->bg); 147 | fs_write(con_fb, 2 * pos, &entry, 2); 148 | } 149 | } 150 | 151 | context->cx = 0; 152 | context->cy = 0; 153 | drawstatus(); 154 | syncfbcursor(); 155 | } 156 | 157 | static inline void 158 | copyrow(unsigned int dst, unsigned int src) 159 | { 160 | unsigned char buf[VGA_COLS * 2]; 161 | unsigned int dstofft, srcofft, read; 162 | if (dst < VGA_ROWS && src < VGA_ROWS) { 163 | srcofft = VGA_COLS * src * 2; 164 | dstofft = VGA_COLS * dst * 2; 165 | read = fs_read(con_fb, srcofft, buf, VGA_COLS * 2); 166 | fs_write(con_fb, dstofft, buf, read); 167 | } 168 | } 169 | 170 | static inline void 171 | clearrow(unsigned int row) 172 | { 173 | unsigned int i; 174 | unsigned int rowofft = VGA_COLS * row * 2; 175 | unsigned short value = context->fg << 8; 176 | for (i = 0; i < VGA_COLS; i++) 177 | fs_write(con_fb, rowofft + 2 * i, &value, 2); 178 | } 179 | 180 | static inline void 181 | moveline() 182 | { 183 | unsigned int row; 184 | if (++context->cy == VGA_ROWS - 1) { 185 | for (row = 1; row < VGA_ROWS - 1; row++) 186 | copyrow(row - 1, row); 187 | clearrow(VGA_ROWS - 2); 188 | context->cy = VGA_ROWS - 2; 189 | } 190 | } 191 | 192 | static inline void 193 | movecursor() 194 | { 195 | if (++context->cx == VGA_COLS) { 196 | context->cx = 0; 197 | moveline(); 198 | } 199 | } 200 | 201 | static void 202 | putchar(unsigned int ch) 203 | { 204 | int pos; 205 | short entry; 206 | switch (ch) { 207 | case '\b': 208 | if (context->cx > 0) { 209 | context->cx--; 210 | } 211 | break; 212 | case '\t': 213 | do { 214 | context->cx++; 215 | } while (context->cx % 7); 216 | if (context->cx >= VGA_COLS) { 217 | context->cx = 0; 218 | moveline(); 219 | } 220 | break; 221 | case '\n': 222 | moveline(); 223 | case '\r': 224 | context->cx = 0; 225 | break; 226 | default: 227 | pos = context->cy * VGA_COLS + context->cx; 228 | entry = VGA_ENTRY(ch, context->fg, context->bg); 229 | fs_write(con_fb, 2 * pos, &entry, 2); 230 | movecursor(); 231 | break; 232 | } 233 | drawstatus(); 234 | syncfbcursor(); 235 | } 236 | 237 | static void 238 | putstr(char *str) 239 | { 240 | if (str) { 241 | while (*str) { 242 | putchar(*str++); 243 | } 244 | } 245 | } 246 | 247 | static int 248 | get_single_scancode(unsigned char key) 249 | { 250 | scancode_t *scancode; 251 | char idx, *scancode_bytes; 252 | 253 | if (key > 0x58) { 254 | return VK_NUL; 255 | } 256 | scancode = &us_scancodes_1[key]; 257 | scancode_bytes = (char *) scancode; 258 | idx = context->kbd_status & KBD_MODS; 259 | return scancode_bytes[idx]; 260 | } 261 | 262 | static inline void 263 | releasebit(unsigned short bit, unsigned int rel) 264 | { 265 | if (rel) { 266 | context->kbd_status &= ~bit; 267 | } else { 268 | context->kbd_status |= bit; 269 | } 270 | } 271 | 272 | static inline void 273 | togglekbdbit(unsigned short bit) 274 | { 275 | context->kbd_status ^= bit; 276 | } 277 | 278 | static int 279 | decode_scancode(kbdev_t *kbdev, unsigned char *buf, unsigned int len) 280 | { 281 | unsigned int i; 282 | unsigned char keycode, rel, status, vk; 283 | 284 | if (len == 1) { 285 | keycode = buf[0] & 0x7F; 286 | rel = buf[0] & 0x80; 287 | vk = get_single_scancode(keycode); 288 | 289 | switch (vk) { 290 | case VK_LCTL: 291 | releasebit(KBD_MOD_CTRL, rel); 292 | break; 293 | case VK_LSHF: 294 | case VK_RSHF: 295 | releasebit(KBD_MOD_SHIFT, rel); 296 | break; 297 | case VK_LALT: 298 | releasebit(KBD_MOD_ALT, rel); 299 | break; 300 | case VK_CAPL: 301 | if (!rel) 302 | togglekbdbit(KBD_LOCK_CAPS); 303 | break; 304 | } 305 | 306 | kbdev->flags = context->kbd_status; 307 | if (rel) { 308 | kbdev->flags |= KBD_CAP_UP; 309 | } 310 | kbdev->vk = vk; 311 | return 0; 312 | } else if (len == 2) { 313 | /* One of those extended keys. */ 314 | if (buf[0] != 0xE0) { 315 | /* Unless I'm using layer 2, I have no idea. */ 316 | return -1; 317 | } 318 | keycode = buf[1] & 0x7F; 319 | rel = buf[1] & 0x80; 320 | 321 | switch (keycode) { 322 | case 0x1C: 323 | vk = VK_LF; 324 | break; 325 | case 0x1D: 326 | vk = VK_RCTL; 327 | releasebit(KBD_MOD_CTRL, rel); 328 | break; 329 | case 0x35: 330 | vk = '/'; 331 | break; 332 | case 0x38: 333 | vk = VK_RALT; 334 | releasebit(KBD_MOD_ALT, rel); 335 | break; 336 | case 0x47: 337 | vk = VK_HOME; 338 | break; 339 | case 0x48: 340 | vk = VK_CUP; 341 | break; 342 | case 0x49: 343 | vk = VK_PGUP; 344 | break; 345 | case 0x4B: 346 | vk = VK_CLFT; 347 | break; 348 | case 0x4D: 349 | vk = VK_CRGT; 350 | break; 351 | case 0x4F: 352 | vk = VK_END; 353 | break; 354 | case 0x50: 355 | vk = VK_CDWN; 356 | break; 357 | case 0x51: 358 | vk = VK_PGDN; 359 | break; 360 | case 0x52: 361 | vk = VK_INSERT; 362 | break; 363 | case 0x53: 364 | vk = VK_DEL; 365 | break; 366 | case 0x5B: 367 | case 0x59: 368 | releasebit(KBD_MOD_META, rel); 369 | break; 370 | } 371 | kbdev->flags = context->kbd_status; 372 | if (rel) { 373 | kbdev->flags |= KBD_CAP_UP; 374 | } 375 | kbdev->vk = vk; 376 | return 0; 377 | } 378 | 379 | return -1; 380 | } 381 | 382 | static void 383 | echo_scancode(kbdev_t *kbdev) 384 | { 385 | if (kbdev->flags & KBD_CAP_UP) { 386 | return; 387 | } 388 | switch (kbdev->vk) { 389 | case VK_NUL: 390 | putstr("^@"); 391 | break; 392 | case VK_SOH: 393 | putstr("^A"); 394 | break; 395 | case VK_STX: 396 | putstr("^B"); 397 | break; 398 | case VK_ETX: 399 | putstr("^C"); 400 | break; 401 | case VK_EOT: 402 | putstr("^D"); 403 | break; 404 | case VK_ENQ: 405 | putstr("^E"); 406 | break; 407 | case VK_ACK: 408 | putstr("^F"); 409 | break; 410 | case VK_BEL: 411 | putstr("^G"); 412 | break; 413 | case VK_BS: 414 | putchar('\b'); 415 | break; 416 | case VK_HT: 417 | putchar('\t'); 418 | break; 419 | case VK_LF: 420 | putchar('\n'); 421 | break; 422 | case VK_VT: 423 | putstr("^K"); 424 | break; 425 | case VK_FF: 426 | clearscreen(); 427 | break; 428 | case VK_CR: 429 | putchar('\r'); 430 | break; 431 | case VK_SO: 432 | putstr("^N"); 433 | break; 434 | case VK_SI: 435 | putstr("^O"); 436 | break; 437 | case VK_DLE: 438 | putstr("^P"); 439 | break; 440 | case VK_DC1: 441 | putstr("^Q"); 442 | break; 443 | case VK_DC2: 444 | putstr("^R"); 445 | break; 446 | case VK_DC3: 447 | putstr("^S"); 448 | break; 449 | case VK_DC4: 450 | putstr("^T"); 451 | break; 452 | case VK_NAK: 453 | putstr("^U"); 454 | break; 455 | case VK_SYN: 456 | putstr("^V"); 457 | break; 458 | case VK_ETB: 459 | putstr("^W"); 460 | break; 461 | case VK_CAN: 462 | putstr("^X"); 463 | break; 464 | case VK_EM: 465 | putstr("^Y"); 466 | break; 467 | case VK_SUB: 468 | putstr("^Z"); 469 | break; 470 | case VK_ESC: 471 | putstr("^["); 472 | break; 473 | case VK_FS: 474 | putstr("^\\"); 475 | break; 476 | case VK_GS: 477 | putstr("^]"); 478 | break; 479 | case VK_RS: 480 | putstr("^^"); 481 | break; 482 | case VK_US: 483 | putstr("^_"); 484 | break; 485 | case VK_CUP: 486 | putstr("^[[A"); 487 | break; 488 | case VK_CDWN: 489 | putstr("^[[B"); 490 | break; 491 | case VK_CRGT: 492 | putstr("^[[C"); 493 | break; 494 | case VK_CLFT: 495 | putstr("^[[D"); 496 | break; 497 | case VK_HOME: 498 | putstr("^[[H"); 499 | break; 500 | case VK_END: 501 | putstr("^[[F"); 502 | break; 503 | case VK_INSERT: 504 | putstr("^[[2~"); 505 | break; 506 | case VK_DEL: 507 | putstr("^[[3~"); 508 | break; 509 | case VK_PGUP: 510 | putstr("^[[5~"); 511 | break; 512 | case VK_PGDN: 513 | putstr("^[[6~"); 514 | break; 515 | case VK_F1: 516 | switchcontext(0); 517 | break; 518 | case VK_F2: 519 | switchcontext(1); 520 | break; 521 | case VK_F3: 522 | switchcontext(2); 523 | break; 524 | case VK_F4: 525 | switchcontext(3); 526 | break; 527 | case VK_F5: 528 | switchcontext(4); 529 | break; 530 | case VK_F6: 531 | switchcontext(5); 532 | break; 533 | case VK_F7: 534 | switchcontext(6); 535 | break; 536 | case VK_F8: 537 | switchcontext(7); 538 | break; 539 | case VK_F9: 540 | putstr("^[[20~"); 541 | break; 542 | case VK_F10: 543 | putstr("^[[21~"); 544 | break; 545 | case VK_F11: 546 | putstr("^[[23~"); 547 | break; 548 | case VK_F12: 549 | putstr("^[[24~"); 550 | break; 551 | default: 552 | if (kbdev->vk >= 27 && kbdev->vk < 127) { 553 | putchar(kbdev->vk); 554 | } 555 | break; 556 | } 557 | } 558 | 559 | static unsigned int 560 | vtcon_read(unsigned char *buf, unsigned int len) 561 | { 562 | unsigned int kbd_len, read_bytes = 0; 563 | unsigned char kbd_buf[16]; 564 | kbdev_t kbdev; 565 | 566 | /* TODO: I'm going to hell for doing this in this function. */ 567 | drawstatus(); 568 | 569 | while (len >= sizeof(kbdev_t)) { 570 | kbd_len = fs_read(con_kbd, 0, kbd_buf, 16); 571 | if (kbd_len == 0) { 572 | break; 573 | } 574 | if (decode_scancode(&kbdev, kbd_buf, kbd_len) == 0) { 575 | echo_scancode(&kbdev); 576 | memcpy(buf, &kbdev, sizeof(kbdev_t)); 577 | len -= sizeof(kbdev_t); 578 | read_bytes += sizeof(kbdev_t); 579 | } 580 | } 581 | return read_bytes; 582 | } 583 | 584 | static unsigned int 585 | vtcon_write(unsigned char *buf, unsigned int len) 586 | { 587 | unsigned int rem = len; 588 | while (rem--) { 589 | putchar(*buf); 590 | buf++; 591 | } 592 | return len; 593 | } 594 | 595 | static int 596 | try_open_fb() 597 | { 598 | con_fb = fs_resolve("DEV:/fb"); 599 | if (con_fb && fs_open(con_fb, VO_FWRITE) == 0) { 600 | return 0; 601 | } 602 | return -1; 603 | } 604 | 605 | static int 606 | try_open_kbd() 607 | { 608 | con_kbd = fs_resolve("DEV:/kbd"); 609 | if (con_kbd && fs_open(con_kbd, VO_FREAD) == 0) { 610 | return 0; 611 | } 612 | return -1; 613 | } 614 | 615 | static int 616 | try_open_clock() 617 | { 618 | clock = fs_resolve("DEV:/clock"); 619 | if (clock && fs_open(clock, VO_FREAD) == 0) { 620 | return 0; 621 | } 622 | return -1; 623 | } 624 | 625 | static int 626 | try_close_kbd() 627 | { 628 | if (fs_close(con_fb) != 0) { 629 | return -1; 630 | } 631 | return 0; 632 | } 633 | 634 | static int 635 | try_close_clock() 636 | { 637 | if (fs_close(clock) != 0) { 638 | return -1; 639 | } 640 | return 0; 641 | } 642 | 643 | static int 644 | try_close_fb() 645 | { 646 | if (fs_close(con_fb) != 0) { 647 | return -1; 648 | } 649 | return 0; 650 | } 651 | 652 | static int 653 | vtcon_open(unsigned int flags) 654 | { 655 | if (flags & VO_FREAD) 656 | /* This is a write only device. */ 657 | return -1; 658 | if (con_fb) 659 | /* Device is already opened. */ 660 | return -1; 661 | if (try_open_fb() < 0) { 662 | return -1; 663 | } 664 | if (try_open_kbd() < 0) { 665 | try_close_fb(); 666 | return -1; 667 | } 668 | if (try_open_clock() < 0) { 669 | try_close_kbd(); 670 | try_close_fb(); 671 | return -1; 672 | } 673 | drawstatus(); 674 | syncfbcursor(); 675 | return 0; 676 | } 677 | 678 | static int 679 | vtcon_ioctl(int op, void *argp) 680 | { 681 | if (op == 0) { 682 | clearscreen(); 683 | return 0; 684 | } 685 | return -1; 686 | } 687 | 688 | static int 689 | vtcon_close() 690 | { 691 | try_close_clock(); 692 | try_close_kbd(); 693 | try_close_fb(); 694 | resetcontexts(); 695 | return 0; 696 | } 697 | 698 | static driver_t vtcon_driver = { 699 | .drv_name = "vgacon", 700 | .drv_flags = DV_FCHARDEV, 701 | .drv_init = &vtcon_init, 702 | }; 703 | 704 | static device_t vtcon_device = { 705 | .dev_family = &vtcon_driver, 706 | .dev_open = &vtcon_open, 707 | .dev_read_chr = &vtcon_read, 708 | .dev_write_chr = &vtcon_write, 709 | .dev_ioctl = &vtcon_ioctl, 710 | .dev_close = &vtcon_close, 711 | }; 712 | 713 | static int 714 | vtcon_init(void) 715 | { 716 | resetcontexts(); 717 | device_install(&vtcon_device, "vtcon"); 718 | return 0; 719 | } 720 | 721 | DEVICE_DESCRIPTOR(vtcon, vtcon_driver); 722 | -------------------------------------------------------------------------------- /kernel/fs/tarfs/tar.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /** 8 | * An internal TAR file node, both as a header block reference for the TAR file 9 | * and as the VFS node that is exposed as the file system. 10 | */ 11 | struct tarfs_node { 12 | vfs_node_t *node; 13 | tar_header_block_t *block; 14 | }; 15 | 16 | /** The internals associated with a TARFS volume. */ 17 | struct tarfs_payload { 18 | /** A pointer to the memory buffer of the TAR file. */ 19 | unsigned char *buf; 20 | 21 | /** A linked list with all the tarfs_node for all the files. */ 22 | list_t *nodes; 23 | 24 | /** Points back to the mounted VFS volume backing this payload. */ 25 | vfs_volume_t *volume; 26 | 27 | /** Points to the root node of the mounted TAR. */ 28 | vfs_node_t *root; 29 | }; 30 | 31 | static unsigned int 32 | octal2int(char *octstring) 33 | { 34 | unsigned int acc = 0; 35 | while (*octstring) { 36 | acc = acc * 8 + (*octstring - '0'); 37 | octstring++; 38 | } 39 | return acc; 40 | } 41 | 42 | static int 43 | decode_block(struct tarfs_payload *tar, tar_header_block_t *block) 44 | { 45 | struct tarfs_node *tnode; 46 | 47 | if ((tnode = malloc(sizeof(struct tarfs_node))) != 0) { 48 | tnode->block = block; 49 | tnode->node = 0; 50 | list_append(tar->nodes, tnode); 51 | return 0; 52 | } else { 53 | return -1; 54 | } 55 | } 56 | 57 | static void 58 | split_basename(char *path, char **dirname, char **basename) 59 | { 60 | char *ptr = path, *slash = 0; 61 | 62 | while (*ptr) { 63 | if (*ptr == '/') { 64 | slash = ptr; 65 | } 66 | ptr++; 67 | } 68 | 69 | if (slash) { 70 | *slash = 0; 71 | *basename = slash + 1; 72 | *dirname = path; 73 | } else { 74 | *basename = path; 75 | *dirname = ""; 76 | } 77 | } 78 | 79 | static void reassemble_nodes(struct tarfs_payload *tarfile, 80 | struct tarfs_node *node); 81 | 82 | static vfs_node_t * 83 | lookup_vnode(struct tarfs_payload *tarfile, const char *path) 84 | { 85 | listnode_t *listnode; 86 | struct tarfs_node *tarnode; 87 | char *kiwi; 88 | 89 | list_foreach(tarfile->nodes, listnode) 90 | { 91 | unsigned int last_char; 92 | tarnode = (struct tarfs_node *) listnode->data; 93 | kiwi = tarnode->block->metadata.name; 94 | /* If file name is ./, remove trailing dot. */ 95 | if (*kiwi == '.' && *(kiwi + 1) == '/') { 96 | kiwi++; 97 | } 98 | /* If starts with slash, remove it. */ 99 | if (*kiwi == '/') { 100 | kiwi++; 101 | } 102 | /* Remove possible trailing slash. */ 103 | last_char = strlen(kiwi) - 1; 104 | if (*kiwi && kiwi[last_char] == '/') { 105 | kiwi[last_char] = 0; 106 | } 107 | if (!strncmp(kiwi, path, 256)) { 108 | reassemble_nodes(tarfile, tarnode); 109 | return tarnode->node; 110 | } 111 | } 112 | 113 | return NULL; 114 | } 115 | 116 | static void 117 | reassemble_nodes(struct tarfs_payload *tarfile, struct tarfs_node *node) 118 | { 119 | char *orig_path, *path, *dirname, *basename; 120 | unsigned int last_char; 121 | vfs_node_t *vnode; 122 | 123 | if (!node->node) { 124 | orig_path = strdup(node->block->metadata.name); 125 | path = orig_path; 126 | 127 | /* Normalize paths removing absolute dir slashes and dots. */ 128 | if (*path == '.' && *(path + 1) == '/') { 129 | path++; 130 | } 131 | if (*path == '/') { 132 | path++; 133 | } 134 | 135 | /* Remove possible trailing slash. */ 136 | last_char = strlen(path) - 1; 137 | if (*path && path[last_char] == '/') { 138 | path[last_char] = 0; 139 | } 140 | 141 | /* Divide my path in dirname and basename. */ 142 | 143 | vnode = malloc(sizeof(vfs_node_t)); 144 | vnode->vn_flags = 0; 145 | if (*path) { 146 | split_basename(path, &dirname, &basename); 147 | strcpy(vnode->vn_name, basename); 148 | vnode->vn_parent = lookup_vnode(tarfile, dirname); 149 | } else { 150 | strcpy(vnode->vn_name, ""); 151 | vnode->vn_parent = NULL; 152 | if (!tarfile->root) { 153 | tarfile->root = vnode; 154 | } 155 | } 156 | vnode->vn_flags = 0; 157 | switch (node->block->metadata.type) { 158 | case '0': 159 | vnode->vn_flags |= VN_FREGFILE; 160 | break; 161 | case '5': 162 | vnode->vn_flags |= VN_FDIR; 163 | break; 164 | } 165 | vnode->vn_volume = tarfile->volume; 166 | vnode->vn_payload = node; 167 | node->node = vnode; 168 | 169 | free(orig_path); 170 | } 171 | } 172 | 173 | static void 174 | init_nodes(struct tarfs_payload *tar) 175 | { 176 | unsigned int bx = 0, file_length; 177 | tar_header_block_t *block = (tar_header_block_t *) tar->buf; 178 | listnode_t *lnode; 179 | 180 | /* First we decode all the files in this TAR. */ 181 | while (*block[bx].metadata.name) { 182 | decode_block(tar, &block[bx]); 183 | file_length = octal2int(block[bx].metadata.size); 184 | bx += (file_length / 512) + 1; 185 | if ((file_length % 512)) { 186 | bx++; 187 | } 188 | } 189 | 190 | /* Then we reassemble the vfs_node_t data structures. */ 191 | list_foreach(tar->nodes, lnode) 192 | { 193 | reassemble_nodes(tar, (struct tarfs_node *) lnode->data); 194 | } 195 | } 196 | 197 | static int tarfs_mount(vfs_volume_t *volume); 198 | static int tarfs_open(vfs_node_t *node, unsigned int flags); 199 | static unsigned int tarfs_read(vfs_node_t *, unsigned, void *, unsigned); 200 | static unsigned int tarfs_write(vfs_node_t *, unsigned, void *, unsigned); 201 | static int tarfs_close(vfs_node_t *node); 202 | static vfs_node_t *tarfs_readdir(vfs_node_t *node, unsigned int index); 203 | static vfs_node_t *tarfs_finddir(vfs_node_t *node, char *name); 204 | 205 | static vfs_ops_t tarfs_ops = { 206 | .vfs_close = &tarfs_close, 207 | .vfs_open = &tarfs_open, 208 | .vfs_read = &tarfs_read, 209 | .vfs_readdir = &tarfs_readdir, 210 | .vfs_finddir = &tarfs_finddir, 211 | }; 212 | 213 | static vfs_filesys_t tarfs_driver = { 214 | .fsd_ident = "tarfs", 215 | .fsd_name = "TAR File System", 216 | .fsd_mount = &tarfs_mount, 217 | .fsd_ops = &tarfs_ops, 218 | }; 219 | 220 | FS_DESCRIPTOR(tarfs, tarfs_driver); 221 | 222 | static int 223 | tarfs_mount(vfs_volume_t *luna) 224 | { 225 | struct tarfs_payload *payload; 226 | 227 | if ((payload = malloc(sizeof(struct tarfs_payload)))) { 228 | payload->buf = luna->vv_payload; 229 | payload->nodes = list_alloc(); 230 | payload->volume = luna; 231 | init_nodes(payload); 232 | luna->vv_root = payload->root; 233 | luna->vv_payload = payload; 234 | return 0; 235 | } else { 236 | return -1; 237 | } 238 | } 239 | 240 | static int 241 | tarfs_open(vfs_node_t *node, unsigned int flags) 242 | { 243 | /* TODO: Should check the flags, and mark the file as opened. */ 244 | return 0; 245 | } 246 | 247 | static unsigned int 248 | tarfs_read(vfs_node_t *node, unsigned int offt, void *buf, unsigned int len) 249 | { 250 | unsigned char *data, *dst; 251 | struct tarfs_node *tar = (struct tarfs_node *) node->vn_payload; 252 | unsigned int read = 0, size = octal2int(tar->block->metadata.size); 253 | 254 | data = (unsigned char *) &tar->block->metadata + 512; 255 | dst = (unsigned char *) buf; 256 | while (len > 0 && offt < size) { 257 | dst[read++] = data[offt++]; 258 | len--; 259 | } 260 | return read; 261 | } 262 | 263 | static unsigned int 264 | tarfs_write(vfs_node_t *node, unsigned int offt, void *buf, unsigned int len) 265 | { 266 | return -1; 267 | } 268 | 269 | static int 270 | tarfs_close(vfs_node_t *node) 271 | { 272 | return 0; 273 | } 274 | 275 | static vfs_node_t * 276 | tarfs_readdir(vfs_node_t *klairm_cocayketa_voltereta, 277 | unsigned int clank_will_not_die) 278 | { 279 | listnode_t *esta_variable_es_del_mejor_mod_de_discord; 280 | struct tarfs_payload *dia_de_pago; 281 | struct tarfs_node *kiwi; 282 | 283 | dia_de_pago = (struct tarfs_payload *) 284 | klairm_cocayketa_voltereta->vn_volume->vv_payload; 285 | 286 | list_foreach(dia_de_pago->nodes, 287 | esta_variable_es_del_mejor_mod_de_discord) 288 | { 289 | kiwi = (struct tarfs_node *) 290 | esta_variable_es_del_mejor_mod_de_discord->data; 291 | if (kiwi->node->vn_parent == klairm_cocayketa_voltereta) { 292 | if (clank_will_not_die == 0) { 293 | return kiwi->node; 294 | } else { 295 | --clank_will_not_die; 296 | } 297 | } 298 | } 299 | 300 | return NULL; 301 | } 302 | 303 | static vfs_node_t * 304 | tarfs_finddir(vfs_node_t *node, char *name) 305 | { 306 | struct tarfs_payload *payload; 307 | struct tarfs_node *tarnode; 308 | listnode_t *listnode; 309 | 310 | payload = node->vn_volume->vv_payload; 311 | list_foreach(payload->nodes, listnode) 312 | { 313 | tarnode = (struct tarfs_node *) listnode->data; 314 | if (tarnode->node->vn_parent == node 315 | && !strcmp(name, tarnode->node->vn_name)) { 316 | return tarnode->node; 317 | } 318 | } 319 | 320 | return NULL; 321 | } 322 | -------------------------------------------------------------------------------- /kernel/fs/tarfs/tar.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct tar_metadata { 4 | char name[100]; 5 | char mode[8]; 6 | char uid[8]; 7 | char gid[8]; 8 | char size[12]; 9 | char mktime[12]; 10 | char checksum[8]; 11 | char type; 12 | char linkname[100]; 13 | char magic[6]; 14 | char version[2]; 15 | char uname[32]; 16 | char gname[32]; 17 | char devmajor[8]; 18 | char devminor[8]; 19 | char prefix[155]; 20 | } tar_metadata_t; 21 | 22 | typedef struct tar_header_block { 23 | tar_metadata_t metadata; 24 | char reserved[12]; 25 | } tar_header_block_t; 26 | -------------------------------------------------------------------------------- /kernel/i386/conf/kernel.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH(i386); 2 | OUTPUT_FORMAT(elf32-i386); 3 | 4 | ENTRY (kernel_bootstrap) 5 | 6 | /* 7 | * Size of the kernel heap, used to allocate kernel objects. This is not the 8 | * allocator that will be used by the userland. This value should be set to 9 | * something big enough to let the kernel do its work. 10 | */ 11 | heap_size = 0x100000; 12 | 13 | SECTIONS 14 | { 15 | . = 0x00100000; 16 | kernel_start = .; 17 | 18 | .multiboot ALIGN(0x1000) : 19 | { 20 | KEEP(*(.multiboot)) 21 | } 22 | 23 | .text : ALIGN(0x1000) 24 | { 25 | *(.text) 26 | } 27 | 28 | .text.driver : ALIGN(0x1000) 29 | { 30 | devices_start = .; 31 | *(.text.driver) 32 | devices_end = .; 33 | } 34 | 35 | .text.fs : ALIGN(0x1000) 36 | { 37 | fs_descriptor__start = .; 38 | *(.text.fs); 39 | fs_descriptor__end = .; 40 | } 41 | 42 | .note.gnu.build-id : ALIGN(0x1000) 43 | { 44 | *(.note.gnu.build-id) 45 | } 46 | 47 | .rodata ALIGN (0x1000) : 48 | { 49 | *(.rodata*) 50 | } 51 | 52 | .data ALIGN (0x1000) : 53 | { 54 | *(.data) 55 | } 56 | 57 | .heap ALIGN (0x1000) : 58 | { 59 | heap_bottom = .; 60 | . = . + heap_size; 61 | heap_top = .; 62 | } 63 | 64 | .bss : 65 | { 66 | sbss = .; 67 | *(COMMON) 68 | *(.bss) 69 | ebss = .; 70 | } 71 | 72 | kernel_after = .; 73 | 74 | /DISCARD/ : 75 | { 76 | *(.interp) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /kernel/i386/i386/locore.S: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2018 The NativeOS contributors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | .text 22 | 23 | .global kernel_bootstrap 24 | .type kernel_bootstrap, @function 25 | .global kernel_die 26 | .type kernel_die, @function 27 | 28 | .extern idt_init 29 | .extern heap_init 30 | .extern pmm_init 31 | .extern multiboot_init 32 | .extern kernel_main 33 | .extern virtual_memory_init 34 | 35 | /** 36 | * This procedure is the actual kernel entrypoint as executed by the bootloader 37 | * when the system starts. Assembly code is umcomfortable to read and write, 38 | * so this function should do its stuff as soon as possible and pass control 39 | * to the portable C code to perform more advanced things. 40 | */ 41 | kernel_bootstrap: 42 | /* Set up the stack. */ 43 | movl $(kernel_stack + KERNEL_STACK_SIZE), %esp 44 | 45 | #ifdef MULTIBOOT 46 | /* Initialise the multiboot parameters given by the bootloader. */ 47 | call multiboot_init 48 | #endif 49 | 50 | /* Set up GDT */ 51 | lgdt gdt_toc 52 | ljmp $0x8, $.after_gdt_setup 53 | .after_gdt_setup: 54 | /* Platform specific initialisation. */ 55 | call idt_init 56 | call heap_init 57 | call pmm_init 58 | call virtual_memory_init 59 | 60 | /* Execute the kernel. */ 61 | call kernel_main 62 | 63 | /** 64 | * This procedure enters an infinite loop of fully halting the system. There 65 | * is no way this function will return unless the computer is physically 66 | * switched off or rebooted first. 67 | */ 68 | kernel_die: 69 | cli 70 | hlt 71 | jmp kernel_die 72 | 73 | .rodata 74 | gdt_table: 75 | /* Entry 0: NULL */ 76 | .long 0 /* (sid = 0, access = 0x00, granularity = 0x00) */ 77 | .long 0 /* (base = 0x00000000, limit = 0x00000000) */ 78 | 79 | /* Entry 1: Code Segment */ 80 | .long 0x0000ffff /* (sid = 1, access = 0x9A, granularity = 0xCF) */ 81 | .long 0x00cf9a00 /* (base = 0x00000000, limit = 0xFFFFFFFF) */ 82 | 83 | /* Entry 2: Data Segment */ 84 | .long 0x0000ffff /* (sid = 2, access = 0x92, granularity = 0xCF) */ 85 | .long 0x00cf9300 /* (base = 0x00000000, limit = 0xFFFFFFFF) */ 86 | gdt_toc: 87 | .word 0x17 88 | .long gdt_table 89 | 90 | .bss 91 | .lcomm kernel_stack, KERNEL_STACK_SIZE 92 | -------------------------------------------------------------------------------- /kernel/i386/i386/multiboot.S: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2022 The NativeOS contributors 4 | * SPDX-License-Identifier: GPL-3.0-only 5 | */ 6 | 7 | /** 8 | * \file 9 | * \brief Multiboot entrypoints 10 | * 11 | * If the kernel is built with support for multiboot, this file contains 12 | * the multiboot header as expected to be present by the bootloader in the 13 | * final executable, and some routines used to test and initialise the 14 | * multiboot information. 15 | */ 16 | 17 | /* Magic number used by the multiboot bootloader. */ 18 | #define MULTIBOOT_MAGIC_NUMBER 0x1BADB002 19 | 20 | /* Ask for memory alignment and memory information. */ 21 | #define MULTIBOOT_FLAGS 0x00000003 22 | 23 | /* Mark the section as allocatable and executable for clang. */ 24 | .section .multiboot, "a" 25 | multiboot_header: 26 | .int MULTIBOOT_MAGIC_NUMBER 27 | .int MULTIBOOT_FLAGS 28 | .int -(MULTIBOOT_MAGIC_NUMBER + MULTIBOOT_FLAGS) 29 | 30 | .text 31 | 32 | .global multiboot_init 33 | .type multiboot_init, @function 34 | 35 | .extern kernel_die 36 | 37 | /** 38 | * This subroutine should be called as soon as possible because it depends 39 | * on the proper parameters placed in the CPU registers to succeed. This 40 | * subroutine will store the multiboot data structure in multiboot_info if 41 | * the magic number is valid. If the magic number is not valid, the system 42 | * will halt for your safety. 43 | * 44 | * Registers: 45 | * - EAX: the multiboot magic number (should be set by the bootloader) 46 | * - EBX: a pointer to the multiboot info (should be set by the bootloader) 47 | * 48 | * Returns: nothing, but it will not return if the magic number is not valid. 49 | */ 50 | multiboot_init: 51 | push %ebp 52 | mov %esp, %ebp 53 | 54 | /* Test that the magic number is correct as soon as possible. */ 55 | cmpl $0x2badb002, %eax 56 | je .valid_magic_number 57 | jmp kernel_die 58 | .valid_magic_number: 59 | /* The number is valid, the multiboot_info can be trusted. */ 60 | movl %ebx, multiboot_info 61 | 62 | mov %ebp, %esp 63 | pop %ebp 64 | ret 65 | 66 | .data 67 | .global multiboot_info 68 | multiboot_info: 69 | .int 0 70 | -------------------------------------------------------------------------------- /kernel/i386/i386/paging.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // The page directory in use by the kernel itself. 4 | static unsigned int kernel_page_directory[1024] __attribute__((aligned(4096))); 5 | 6 | void 7 | virtual_memory_init() 8 | { 9 | unsigned int i; 10 | 11 | // Mark all pages as PS=1, but they are not present. 12 | for (i = 0; i < 512; i++) { 13 | kernel_page_directory[i] = (i << 22) | 0x83; 14 | } 15 | for (i = 512; i < 1024; i++) { 16 | kernel_page_directory[i] = ((i - 512) << 2) | 0x83; 17 | } 18 | } 19 | 20 | void 21 | enable_paging() 22 | { 23 | register unsigned int cr; 24 | 25 | // Load the memory address of the kernel page directory. 26 | __asm__("movl %0, %%cr3" : : "r"(kernel_page_directory)); 27 | 28 | // Enable CR4 PSE 29 | __asm__("movl %%cr4, %0" : "=r"(cr)); 30 | cr |= 0x10; 31 | __asm__("movl %0, %%cr4" : : "r"(cr)); 32 | 33 | // Set pagination in CR0. 34 | __asm__("movl %%cr0, %0" : "=r"(cr)); 35 | cr |= 0x80000001; 36 | __asm__("movl %0, %%cr0" : : "r"(cr)); 37 | } 38 | -------------------------------------------------------------------------------- /kernel/i386/i386/port.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void 4 | port_out_byte(uint16_t port, uint8_t value) 5 | { 6 | __asm__("outb %0, %1" : : "a"(value), "d"(port)); 7 | } 8 | 9 | void 10 | port_out_word(uint16_t port, uint16_t value) 11 | { 12 | __asm__("outw %0, %1" : : "a"(value), "d"(port)); 13 | } 14 | 15 | void 16 | port_out_long(uint16_t port, uint32_t value) 17 | { 18 | __asm__("outl %0, %1" : : "a"(value), "d"(port)); 19 | } 20 | 21 | uint8_t 22 | port_in_byte(uint16_t port) 23 | { 24 | uint8_t value; 25 | __asm__("inb %1, %0" : "=a"(value) : "d"(port)); 26 | return value; 27 | } 28 | 29 | uint16_t 30 | port_in_word(uint16_t port) 31 | { 32 | uint16_t value; 33 | __asm__("inw %1, %0" : "=a"(value) : "d"(port)); 34 | return value; 35 | } 36 | 37 | uint32_t 38 | port_in_long(uint16_t port) 39 | { 40 | uint32_t value; 41 | __asm__("inl %1, %0" : "=a"(value) : "d"(port)); 42 | return value; 43 | } -------------------------------------------------------------------------------- /kernel/i386/i386/spinlock.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * \brief i386 implementation of Spinlock API 4 | * 5 | * This function contains the i386 internal implementation of the spinlocks 6 | * for NativeOS. A spinlock is a simple resource lock that allows a process 7 | * to mark a resource as busy. If a different thread tries to lock a locked 8 | * spinlock, it will loop (spin) until the lock can be locked. 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | /** 15 | * \brief Replace the contents of the memory address with the given value. 16 | * \param memaddr the pointer whose value must be updated. 17 | * \param val the new value to place in the pointed memory address. 18 | * \return the old value for the memory address pointed here. 19 | * \internal 20 | */ 21 | static inline int 22 | x86_xchg_dword(int *memaddr, int val) 23 | { 24 | int prev; 25 | __asm__("xchgl %0, %1" : "=a"(prev) : "m"(*memaddr), "a"(val)); 26 | return prev; 27 | } 28 | 29 | void 30 | spinlock_init(struct spinlock *lock) 31 | { 32 | lock->locked = 0; 33 | } 34 | 35 | void 36 | spinlock_lock(struct spinlock *lock) 37 | { 38 | /* If locked was already set to 1, this function will return 1. */ 39 | while (x86_xchg_dword(&lock->locked, 1)) 40 | ; 41 | } 42 | 43 | void 44 | spinlock_release(struct spinlock *lock) 45 | { 46 | x86_xchg_dword(&lock->locked, 0); 47 | } 48 | -------------------------------------------------------------------------------- /kernel/i386/include/cpu.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void port_out_byte(uint16_t port, uint8_t value); 6 | void port_out_word(uint16_t port, uint16_t value); 7 | void port_out_long(uint16_t port, uint32_t value); 8 | uint8_t port_in_byte(uint16_t port); 9 | uint16_t port_in_word(uint16_t port); 10 | uint32_t port_in_long(uint16_t port); -------------------------------------------------------------------------------- /kernel/i386/include/multiboot.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2022 The NativeOS contributors 4 | * SPDX-License-Identifier: GPL-3.0-only 5 | */ 6 | 7 | /** 8 | * \file 9 | * \brief Multiboot Data Structures 10 | * 11 | * This file defines the different structs associated with the buffers provided 12 | * by the Multiboot bootloader when the system starts. One of the first things 13 | * the kernel has to do is to preserve this pointer into a variable so that 14 | * we can process it later. 15 | */ 16 | #pragma once 17 | 18 | /** 19 | * Describes a module. Multiboot bootloaders provide a way to load arbitrary 20 | * files into memory, known as modules. The operating system will receive a 21 | * list of modules. 22 | */ 23 | typedef struct multiboot_module { 24 | /** Physical memory address where the module starts. */ 25 | unsigned int mod_start; 26 | /** Physical memory address where the module ends. */ 27 | unsigned int mod_end; 28 | /** A string associated with the module (usually command line). */ 29 | unsigned int string; 30 | unsigned int reserved; 31 | } multiboot_module_t; 32 | 33 | /** 34 | * Information about the section header table of the ELF kernel. This is the 35 | * executable itself as booted by the bootloader. Is useful in order to parse 36 | * the sections of the executable. 37 | */ 38 | typedef struct multiboot_elf { 39 | /** Number of sections in the section header table (e_shnum). */ 40 | unsigned int num; 41 | /** Size of each section in the section header table (e_shentsize). */ 42 | unsigned int size; 43 | /** Memory address of the section header table. */ 44 | unsigned int addr; 45 | /** Index used for the string table (e_shstrndx). */ 46 | unsigned int shndx; 47 | } multiboot_elf_t; 48 | 49 | /** 50 | * Computer memory map, as reported by the bootloader. Each memory slice 51 | * contains information about the type of kind of memory block this is. 52 | */ 53 | typedef struct multiboot_mmap { 54 | /** Size of this entry. */ 55 | unsigned int size; 56 | /** Base address where the memory block starts. */ 57 | unsigned long long base_addr; 58 | /** Length in bytes of the memory block. */ 59 | unsigned long long length; 60 | /** Kind of memory block. 1 = RAM, 3 = ACPI, 5 = Defective. */ 61 | unsigned int type; 62 | } multiboot_mmap_t; 63 | 64 | /* Information about the drives that are accessible on the computer. */ 65 | /** 66 | * A drive as reported by the bootloader. Note that the actual size of the 67 | * data structure is tricky to get because there is an entry per port. 68 | */ 69 | typedef struct multiboot_drive { 70 | /** Size of the structure */ 71 | unsigned int size; 72 | /** Drive number as defined by the system. */ 73 | unsigned char drive_number; 74 | /** Access mode used by this device. */ 75 | unsigned char drive_mode; 76 | /** Number of cylinders. */ 77 | unsigned char drive_cylinders; 78 | /** Number of heads. */ 79 | unsigned short drive_heads; 80 | /** Number of sectors. */ 81 | unsigned char drive_sectors; 82 | /** Zero-terminated array of ports for this drive. */ 83 | unsigned short *drive_ports; 84 | } multiboot_drive_t; 85 | 86 | /** The device uses CHS addressing. */ 87 | #define MULTIBOOT_DRIVE_CHS 0 88 | /** The device uses LBA addressing. */ 89 | #define MULTIBOOT_DRIVE_LBA 1 90 | 91 | /** 92 | * Advanced Power Managemnt (APM) table. The spec is tricky to find because 93 | * Microsoft dropped the support for it long time ago, and they were the 94 | * creators of the spec. There is more information in the Wikipedia page: 95 | * https://en.wikipedia.org/wiki/Advanced_Power_Management. 96 | */ 97 | typedef struct multiboot_apm_table { 98 | /** Version number. */ 99 | unsigned short version; 100 | /** 32 bit code segment. */ 101 | unsigned short cseg; 102 | /** Entrypoint offset. */ 103 | unsigned short offset; 104 | /** Protected mode 16 bit code segment. */ 105 | unsigned short cseg_16; 106 | /** Protected mode 16 bit data segment. */ 107 | unsigned short dseg; 108 | /** Flags. */ 109 | unsigned short flags; 110 | /** Length of the 32 bit code segment. */ 111 | unsigned short cseg_len; 112 | /** Length of the 16 bit code segment. */ 113 | unsigned short cseg_16_len; 114 | /** Length of the 16 bit data segment. */ 115 | unsigned short dseg_len; 116 | } multiboot_apm_table_t; 117 | 118 | /** 119 | * VBE info, if requested by the operating system. When VBE is requested and 120 | * a graphical mode is set by the bootloader, information about the graphical 121 | * mode set will be placed here. 122 | */ 123 | typedef struct multiboot_vbe_info { 124 | /** Memory address of the VBE control information */ 125 | unsigned int vbe_control_info; 126 | /** VBE mode as returned by the system. */ 127 | unsigned int vbe_mode_info; 128 | /** Video mode in the VBE 3.0 format. */ 129 | unsigned short vbe_mode; 130 | /** VBE Protected Mode Interface segment. */ 131 | unsigned short vbe_interface_segment; 132 | /** VBE Protected Mode Interface offset. */ 133 | unsigned short vbe_interface_offset; 134 | /** VBE Protected Mode Interface length. */ 135 | unsigned short vbe_interface_length; 136 | } multiboot_vbe_info_t; 137 | 138 | /** 139 | * Multiboot structure. A multiboot bootloader will provide during boot a 140 | * pointer to a memory buffer that contains a data structure using this format 141 | * so that the target operating system can get information about the system. 142 | */ 143 | typedef struct multiboot_info { 144 | /** Which features are supported by the OS loader (and version). */ 145 | unsigned int flags; 146 | /** Lower bounds of the computer memory (flag bit 0). */ 147 | unsigned int mem_lower; 148 | /** Upper bounds of the computer memory (flag bit 0). */ 149 | unsigned int mem_upper; 150 | /** Attributes related to boot device (flag 1) */ 151 | unsigned int boot_device; 152 | /** Attributes related to command line (flag 2) */ 153 | unsigned int command_line; 154 | /** Number of modules (flag 3) */ 155 | unsigned int mods_count; 156 | /** Memory address of the module data structure (flag 3). */ 157 | unsigned int mods_addr; 158 | /** ELF section table information (flag 5). */ 159 | multiboot_elf_t elf; 160 | /** Size of the bytearray holding the memory map info (flag 6). */ 161 | unsigned int mmap_length; 162 | /** Memory address of the memory map info (flag 6). */ 163 | unsigned int mmap_addr; 164 | /** Total number of drive information entries (flag 7). */ 165 | unsigned int drives_length; 166 | /** Memory address of the first drive information entry (flag 7). */ 167 | unsigned int drives_addr; 168 | /** Attributes related to the config table (flag 8). */ 169 | unsigned int config_table; 170 | /** Attributes related to the boot laoder name (flag 9). */ 171 | unsigned int boot_loader_name; 172 | /** Attributes related to the APM table (flag 10). */ 173 | unsigned int apm_table; 174 | /** Attributes related to VBE data (flag 11). */ 175 | struct multiboot_vbe_info vbe_info; 176 | } multiboot_info_t; 177 | 178 | /** 179 | * Global kernel multiboot information. On kernels built with support for 180 | * Multiboot, the kernel entrypoint will take this information from the 181 | * bootloader handoff and expose it through this variable. 182 | */ 183 | extern multiboot_info_t *multiboot_info; 184 | -------------------------------------------------------------------------------- /kernel/i386/include/paging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void virtual_memory_init(void); 4 | 5 | void enable_paging(); 6 | -------------------------------------------------------------------------------- /kernel/kern/fs_devfs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2021 The NativeOS contributors 4 | * SPDX-License-Identifier: GPL-3.0-only 5 | */ 6 | 7 | /** 8 | * \file kern/hw_devices.h 9 | * \brief Hardware device manager 10 | * 11 | * This system module maintains a list of currently mounted devices, and 12 | * operations to install and remove devices from the list. While a device 13 | * is installed, it can be retrieved in order to use it. 14 | * 15 | * Until the VFS is implemented, some functions are added for accessing 16 | * the devices. These functions are device_open, device_read and device_write. 17 | * Note that these functions are unstable and they might be removed in the 18 | * future once there is a fully functional DEVFS running on top of the VFS. 19 | * I'm adding a warning here because, as usual, time has a different speed 20 | * inside this repository. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | static int devfs_mount(vfs_volume_t *vol); 29 | static int devfs_open(vfs_node_t *node, unsigned int flags); 30 | static unsigned int devfs_read(vfs_node_t *, unsigned, void *, unsigned); 31 | static unsigned int devfs_write(vfs_node_t *, unsigned, void *, unsigned); 32 | static int devfs_ioctl(vfs_node_t *, int iorq, void *args); 33 | static int devfs_close(vfs_node_t *node); 34 | static vfs_node_t *devfs_readdir(vfs_node_t *node, unsigned int index); 35 | static vfs_node_t *devfs_finddir(vfs_node_t *node, char *name); 36 | 37 | static vfs_ops_t devfs_ops = { 38 | .vfs_open = devfs_open, 39 | .vfs_read = devfs_read, 40 | .vfs_write = devfs_write, 41 | .vfs_ioctl = devfs_ioctl, 42 | .vfs_close = devfs_close, 43 | .vfs_readdir = devfs_readdir, 44 | .vfs_finddir = devfs_finddir, 45 | }; 46 | 47 | static list_t *devmgr_list; 48 | static vfs_node_t devfs_rootdir = { 49 | .vn_name = {0}, 50 | .vn_flags = VN_FDIR, 51 | }; 52 | 53 | static vfs_filesys_t devfs_fs = { 54 | .fsd_ident = "devfs", 55 | .fsd_name = "Device FS", 56 | .fsd_init = 0, // Will be manually initialised 57 | .fsd_mount = &devfs_mount, 58 | .fsd_ops = &devfs_ops, 59 | }; 60 | 61 | FS_DESCRIPTOR(devfs, devfs_fs); 62 | 63 | void 64 | device_init(void) 65 | { 66 | extern char devices_start, devices_end; 67 | driver_t **driver_start, **driver_end, **driver; 68 | 69 | /* Init the data structures. */ 70 | devmgr_list = list_alloc(); 71 | 72 | vfs_mount("devfs", "DEV", 0); 73 | 74 | /* Mount the devices. */ 75 | driver_start = (driver_t **) &devices_start; 76 | driver_end = (driver_t **) &devices_end; 77 | for (driver = driver_start; driver < driver_end; driver++) { 78 | (*driver)->drv_init(); 79 | } 80 | } 81 | 82 | int 83 | device_install(device_t *dev, char *mtname) 84 | { 85 | vfs_node_t *node; 86 | if ((node = devfs_finddir(0, mtname)) != 0) 87 | return -2; /* node name is taken. */ 88 | if ((node = (vfs_node_t *) malloc(sizeof(vfs_node_t))) == 0) 89 | return -1; /* cannot allocate. */ 90 | strncpy(node->vn_name, mtname, 64); 91 | node->vn_flags = 0; 92 | switch (dev->dev_family->drv_flags & (DV_FBLCKDEV | DV_FCHARDEV)) { 93 | case DV_FBLCKDEV: 94 | node->vn_flags |= VN_FBLOCKDEV; 95 | break; 96 | case DV_FCHARDEV: 97 | node->vn_flags |= VN_FCHARDEV; 98 | break; 99 | } 100 | node->vn_volume = devfs_rootdir.vn_volume; 101 | node->vn_payload = dev; 102 | node->vn_parent = &devfs_rootdir; 103 | list_append(devmgr_list, node); 104 | return 0; 105 | } 106 | 107 | void 108 | device_remove(char *mtname) 109 | { 110 | vfs_node_t *node = devfs_finddir(0, mtname); 111 | if (node) { 112 | list_delete(devmgr_list, node); 113 | free(node); 114 | } 115 | } 116 | 117 | static int 118 | devfs_mount(vfs_volume_t *vol) 119 | { 120 | if (devfs_rootdir.vn_volume) { 121 | return -1; 122 | } else { 123 | devfs_rootdir.vn_volume = vol; 124 | vol->vv_root = &devfs_rootdir; 125 | return 0; 126 | } 127 | } 128 | 129 | static int 130 | devfs_open(vfs_node_t *node, unsigned int flags) 131 | { 132 | device_t *dev = (device_t *) node->vn_payload; 133 | if (dev && dev->dev_open) { 134 | return dev->dev_open(flags); 135 | } 136 | return -1; 137 | } 138 | 139 | static unsigned int 140 | devfs_read(vfs_node_t *node, unsigned int offt, void *buf, unsigned int len) 141 | { 142 | device_t *dev = (device_t *) node->vn_payload; 143 | unsigned char *chbuf = (unsigned char *) buf; 144 | unsigned int flags; 145 | 146 | if (dev && dev->dev_family) { 147 | flags = dev->dev_family->drv_flags; 148 | if ((flags & DV_FCHARDEV) != 0 && dev->dev_read_chr) { 149 | return dev->dev_read_chr(chbuf, len); 150 | } else if ((flags & DV_FBLCKDEV) != 0 && dev->dev_read_blk) { 151 | return dev->dev_read_blk(chbuf, offt, len); 152 | } else { 153 | return -1; 154 | } 155 | } 156 | 157 | return -1; 158 | } 159 | 160 | static unsigned int 161 | devfs_write(vfs_node_t *node, unsigned int offt, void *buf, unsigned int len) 162 | { 163 | device_t *dev = (device_t *) node->vn_payload; 164 | unsigned char *chbuf = (unsigned char *) buf; 165 | unsigned int flags; 166 | 167 | if (dev && dev->dev_family) { 168 | flags = dev->dev_family->drv_flags; 169 | if ((flags & DV_FCHARDEV) != 0 && dev->dev_write_chr) { 170 | return dev->dev_write_chr(chbuf, len); 171 | } else if ((flags & DV_FBLCKDEV) != 0 && dev->dev_write_blk) { 172 | return dev->dev_write_blk(chbuf, offt, len); 173 | } else { 174 | return -1; 175 | } 176 | } 177 | return -1; 178 | } 179 | 180 | static int 181 | devfs_ioctl(vfs_node_t *node, int iorq, void *args) 182 | { 183 | device_t *dev = (device_t *) node->vn_payload; 184 | if (dev && dev->dev_ioctl) { 185 | return dev->dev_ioctl(iorq, args); 186 | } 187 | return -1; 188 | } 189 | 190 | static int 191 | devfs_close(vfs_node_t *node) 192 | { 193 | device_t *cdev = (device_t *) node->vn_payload; 194 | if (cdev && cdev->dev_close) { 195 | return cdev->dev_close(); 196 | } 197 | return -1; 198 | } 199 | 200 | static vfs_node_t * 201 | devfs_readdir(vfs_node_t *node, unsigned int index) 202 | { 203 | /* TODO: Take into account node, which must be the root. */ 204 | if (index < devmgr_list->count) { 205 | return (vfs_node_t *) list_at(devmgr_list, index); 206 | } 207 | return 0; 208 | } 209 | 210 | static vfs_node_t * 211 | devfs_finddir(vfs_node_t *node, char *name) 212 | { 213 | listnode_t *lnode; 214 | vfs_node_t *vnode; 215 | list_foreach(devmgr_list, lnode) 216 | { 217 | vnode = (vfs_node_t *) lnode->data; 218 | if (!strncmp(vnode->vn_name, name, 64)) { 219 | return vnode; 220 | } 221 | } 222 | return 0; 223 | } 224 | -------------------------------------------------------------------------------- /kernel/kern/fs_fsops.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int 4 | fs_open(vfs_node_t *node, unsigned int flags) 5 | { 6 | vfs_ops_t *ops = NODE_OPS(node); 7 | if (ops && ops->vfs_open) { 8 | switch (node->vn_flags) { 9 | case VN_FREGFILE: 10 | case VN_FCHARDEV: 11 | case VN_FBLOCKDEV: 12 | return ops->vfs_open(node, flags); 13 | } 14 | } 15 | return -1; 16 | } 17 | 18 | int 19 | fs_close(vfs_node_t *node) 20 | { 21 | vfs_ops_t *ops = NODE_OPS(node); 22 | if (ops && ops->vfs_close) { 23 | switch (node->vn_flags) { 24 | case VN_FREGFILE: 25 | case VN_FCHARDEV: 26 | case VN_FBLOCKDEV: 27 | return ops->vfs_close(node); 28 | } 29 | } 30 | return -1; 31 | } 32 | 33 | unsigned int 34 | fs_read(vfs_node_t *node, unsigned int offt, void *buf, unsigned int len) 35 | { 36 | vfs_ops_t *ops = NODE_OPS(node); 37 | if (ops && ops->vfs_read) { 38 | switch (node->vn_flags) { 39 | case VN_FREGFILE: 40 | case VN_FCHARDEV: 41 | case VN_FBLOCKDEV: 42 | return ops->vfs_read(node, offt, buf, len); 43 | } 44 | } 45 | return -1; 46 | } 47 | 48 | unsigned int 49 | fs_write(vfs_node_t *node, unsigned int offt, void *buf, unsigned int len) 50 | { 51 | vfs_ops_t *ops = NODE_OPS(node); 52 | if (ops && ops->vfs_write) { 53 | switch (node->vn_flags) { 54 | case VN_FREGFILE: 55 | case VN_FCHARDEV: 56 | case VN_FBLOCKDEV: 57 | return ops->vfs_write(node, offt, buf, len); 58 | } 59 | } 60 | return -1; 61 | } 62 | 63 | int 64 | fs_ioctl(vfs_node_t *node, int iorq, void *args) 65 | { 66 | vfs_ops_t *ops = NODE_OPS(node); 67 | if (ops && ops->vfs_ioctl) { 68 | switch (node->vn_flags) { 69 | case VN_FCHARDEV: 70 | case VN_FBLOCKDEV: 71 | return ops->vfs_ioctl(node, iorq, args); 72 | } 73 | } 74 | return -1; 75 | } 76 | 77 | vfs_node_t * 78 | fs_readdir(vfs_node_t *node, unsigned int index) 79 | { 80 | vfs_ops_t *ops = NODE_OPS(node); 81 | if (ops && ops->vfs_readdir) { 82 | if (node->vn_flags == VN_FDIR) { 83 | return ops->vfs_readdir(node, index); 84 | } 85 | } 86 | return 0; 87 | } 88 | 89 | vfs_node_t * 90 | fs_finddir(vfs_node_t *node, char *name) 91 | { 92 | vfs_ops_t *ops = NODE_OPS(node); 93 | if (ops && ops->vfs_finddir) { 94 | if (node->vn_flags == VN_FDIR) { 95 | return ops->vfs_finddir(node, name); 96 | } 97 | } 98 | return 0; 99 | } 100 | -------------------------------------------------------------------------------- /kernel/kern/fs_path.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /** 5 | * Recursively locate the given path in the given directory VFS node. If 6 | * the path points to a file name, such as "hello.txt", it will lookup for 7 | * the given file in the directory. Otherwise, it will lookup for nested 8 | * directories if present. In case of error or if the file is not found, 9 | * this function returns NULL. 10 | */ 11 | static vfs_node_t * 12 | fs_follow_path(vfs_node_t *root, char *path) 13 | { 14 | char *out; 15 | vfs_node_t *node; 16 | 17 | /* Get the next path component and locate it in the file system. */ 18 | out = strsep(&path, "/"); 19 | node = fs_finddir(root, out); 20 | 21 | /* This was the last element in the chain, so return it. */ 22 | if (!path) { 23 | return node; 24 | } 25 | 26 | /* There are more components, but this is not a directory. */ 27 | if ((node->vn_flags & VN_FDIR) == 0) { 28 | return 0; 29 | } 30 | 31 | /* This is a directory, and node is a proper directory. */ 32 | return fs_follow_path(node, path); 33 | } 34 | 35 | vfs_node_t * 36 | fs_resolve(const char *path) 37 | { 38 | vfs_node_t *descriptor = 0; 39 | char *strsep_orig, *strsep_in, *strsep_out; 40 | 41 | /* 42 | * We do not want strsep to modify the given path parameter, so we 43 | * duplicate it. Also, we inmediately keep an _orig reference to do 44 | * the free. strsep will be modifying _in value. 45 | */ 46 | strsep_orig = strdup(path); 47 | strsep_in = strsep_orig; 48 | 49 | /* Strip the volume separator and try to lookup the proper volume. */ 50 | strsep_out = strsep(&strsep_in, ":"); 51 | if (!strsep_in) { 52 | goto defer; 53 | } 54 | descriptor = vfs_get_volume(strsep_out); 55 | 56 | /* 57 | * There are some early fail preconditions here: if the volume is not 58 | * found, it is an invalid path. If the path does not contain a slash 59 | * right after the /, it is also a path error. 60 | */ 61 | if (!descriptor || *strsep_in != '/') { 62 | goto defer; 63 | } 64 | 65 | /* Then start processing the path to look for the file in the device. */ 66 | strsep_in++; 67 | if (*strsep_in != 0) { 68 | descriptor = fs_follow_path(descriptor, strsep_in); 69 | } 70 | 71 | defer: 72 | free(strsep_orig); 73 | return descriptor; 74 | } 75 | -------------------------------------------------------------------------------- /kernel/kern/fs_vfs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static list_t *vfs_volumes; 6 | static list_t *vfs_drivers; 7 | 8 | static int rootfs_mount(vfs_volume_t *vol); 9 | static int rootfs_open(struct vfs_node *node, unsigned int flags); 10 | static int rootfs_close(struct vfs_node *node); 11 | static unsigned int rootfs_read(struct vfs_node *node, 12 | unsigned int offt, 13 | void *buf, 14 | unsigned int len); 15 | static vfs_node_t *rootfs_readdir(struct vfs_node *node, unsigned int index); 16 | static vfs_node_t *rootfs_finddir(struct vfs_node *node, char *name); 17 | static void rootfs_register_volume(vfs_volume_t *vol); 18 | static void rootfs_unregister_volume(vfs_volume_t *vol); 19 | 20 | static vfs_ops_t rootfs_ops = { 21 | .vfs_open = rootfs_open, 22 | .vfs_read = rootfs_read, 23 | .vfs_close = rootfs_close, 24 | .vfs_readdir = rootfs_readdir, 25 | .vfs_finddir = rootfs_finddir, 26 | }; 27 | 28 | /** 29 | * \brief Root File System 30 | * 31 | * The Root File System is a special file system that is mounted when the 32 | * VFS system is being initialised. It allows to expose the VFS mountpoints 33 | * using the VFS itself. 34 | */ 35 | static vfs_node_t rootfs_root = { 36 | .vn_name = {0}, 37 | .vn_flags = VN_FDIR, 38 | .vn_volume = 0, 39 | }; 40 | 41 | static vfs_filesys_t rootfs_fs = { 42 | .fsd_ident = "rootfs", 43 | .fsd_name = "Root FS", 44 | .fsd_init = 0, // Manually initialised 45 | .fsd_mount = &rootfs_mount, 46 | .fsd_ops = &rootfs_ops, 47 | }; 48 | 49 | FS_DESCRIPTOR(rootfs, rootfs_fs); 50 | 51 | static inline vfs_volume_t * 52 | find_mountpoint_by_name(char *mtname) 53 | { 54 | listnode_t *node; 55 | vfs_volume_t *mountpoint; 56 | list_foreach(vfs_volumes, node) 57 | { 58 | mountpoint = (vfs_volume_t *) node->data; 59 | if (!strncmp(mountpoint->vv_name, mtname, 16)) { 60 | return mountpoint; 61 | } 62 | } 63 | return 0; 64 | } 65 | 66 | void 67 | vfs_init(void) 68 | { 69 | extern char fs_descriptor__start, fs_descriptor__end; 70 | vfs_filesys_t **fs_start, **fs_end, **fs; 71 | 72 | vfs_volumes = list_alloc(); 73 | vfs_drivers = list_alloc(); 74 | 75 | /* TODO: This shouldn't happen. */ 76 | list_append(vfs_drivers, &rootfs_fs); 77 | 78 | /* TODO: This should happen after adding the drivers. */ 79 | vfs_mount("rootfs", "ROOT", 0); 80 | 81 | /* Register the file system drivers. */ 82 | fs_start = (vfs_filesys_t **) &fs_descriptor__start; 83 | fs_end = (vfs_filesys_t **) &fs_descriptor__end; 84 | for (fs = fs_start; fs < fs_end; fs++) { 85 | if ((*fs)->fsd_init) { 86 | (*fs)->fsd_init(); 87 | } 88 | list_append(vfs_drivers, *fs); 89 | } 90 | } 91 | 92 | vfs_filesys_t * 93 | get_driver_by_name(char *name) 94 | { 95 | vfs_filesys_t *fs; 96 | listnode_t *node; 97 | 98 | list_foreach(vfs_drivers, node) 99 | { 100 | fs = (vfs_filesys_t *) node->data; 101 | if (!strcmp(name, fs->fsd_ident)) { 102 | return fs; 103 | } 104 | } 105 | return NULL; 106 | } 107 | 108 | int 109 | vfs_mount(char *driver, char *name, void *argp) 110 | { 111 | vfs_volume_t *volume; 112 | vfs_filesys_t *family; 113 | 114 | if (find_mountpoint_by_name(name) != 0) { 115 | return -1; 116 | } 117 | if ((family = get_driver_by_name(driver)) == 0) { 118 | return -1; 119 | } 120 | if ((volume = (vfs_volume_t *) malloc(sizeof(vfs_volume_t))) == 0) { 121 | return -1; 122 | } 123 | volume->vv_name = strdup(name); 124 | volume->vv_payload = argp; 125 | volume->vv_family = family; 126 | volume->vv_root = NULL; 127 | if (family->fsd_mount(volume) < 0) { 128 | free(volume->vv_name); 129 | free(volume); 130 | return -1; 131 | } 132 | list_append(vfs_volumes, volume); 133 | rootfs_register_volume(volume); 134 | return 0; 135 | } 136 | 137 | int 138 | vfs_umount(char *mountname) 139 | { 140 | vfs_volume_t *vol = find_mountpoint_by_name(mountname); 141 | if (vol) { 142 | rootfs_unregister_volume(vol); 143 | list_delete(vfs_volumes, vol); 144 | free(vol->vv_name); 145 | free(vol); 146 | return 0; 147 | } 148 | return -1; 149 | } 150 | 151 | vfs_node_t * 152 | vfs_get_volume(char *mountname) 153 | { 154 | vfs_volume_t *mtpoint = find_mountpoint_by_name(mountname); 155 | if (mtpoint) 156 | return mtpoint->vv_root; 157 | else 158 | return 0; 159 | } 160 | 161 | static list_t *rootfs_nodes; 162 | 163 | static void 164 | rootfs_register_volume(vfs_volume_t *vol) 165 | { 166 | vfs_node_t *mati_stop_using_haskell; 167 | 168 | mati_stop_using_haskell = (vfs_node_t *) malloc(sizeof(vfs_node_t)); 169 | strcpy(mati_stop_using_haskell->vn_name, vol->vv_name); 170 | mati_stop_using_haskell->vn_flags = VN_FREGFILE; 171 | mati_stop_using_haskell->vn_volume = rootfs_root.vn_volume; 172 | mati_stop_using_haskell->vn_parent = &rootfs_root; 173 | mati_stop_using_haskell->vn_payload = vol; 174 | list_append(rootfs_nodes, mati_stop_using_haskell); 175 | } 176 | 177 | static void 178 | rootfs_unregister_volume(vfs_volume_t *vol) 179 | { 180 | listnode_t *node; 181 | vfs_node_t *vfs_node; 182 | 183 | list_foreach(rootfs_nodes, node) 184 | { 185 | vfs_node = (vfs_node_t *) node->data; 186 | if (!strcmp(vfs_node->vn_name, vol->vv_name)) { 187 | list_delete(rootfs_nodes, vfs_node); 188 | return; 189 | } 190 | } 191 | } 192 | 193 | static int 194 | rootfs_mount(vfs_volume_t *vol) 195 | { 196 | if (rootfs_nodes) { 197 | return -1; 198 | } 199 | 200 | rootfs_nodes = list_alloc(); 201 | rootfs_root.vn_volume = vol; 202 | vol->vv_root = &rootfs_root; 203 | return 0; 204 | } 205 | 206 | static int 207 | rootfs_open(struct vfs_node *node, unsigned int flags) 208 | { 209 | if ((flags & VO_FREAD) == 0 || (flags & VO_FWRITE) != 0) { 210 | return -1; 211 | } 212 | return 0; 213 | } 214 | 215 | static int 216 | rootfs_close(struct vfs_node *node) 217 | { 218 | return 0; 219 | } 220 | 221 | static unsigned int 222 | rootfs_read(struct vfs_node *node, 223 | unsigned int offt, 224 | void *buf, 225 | unsigned int len) 226 | { 227 | return 0; 228 | } 229 | 230 | static vfs_node_t * 231 | rootfs_readdir(struct vfs_node *node, unsigned int index) 232 | { 233 | vfs_node_t *vfs_node; 234 | if (index >= list_count(rootfs_nodes)) { 235 | return 0; 236 | } 237 | return list_at(rootfs_nodes, index); 238 | } 239 | 240 | static vfs_node_t * 241 | rootfs_finddir(struct vfs_node *node, char *name) 242 | { 243 | listnode_t *l_node; 244 | vfs_node_t *vfs_node; 245 | 246 | list_foreach(rootfs_nodes, l_node) 247 | { 248 | vfs_node = (vfs_node_t *) l_node->data; 249 | if (!strcmp(vfs_node->vn_name, name)) { 250 | return vfs_node; 251 | } 252 | } 253 | return 0; 254 | } 255 | -------------------------------------------------------------------------------- /kernel/kern/kern_main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2021 The NativeOS contributors 4 | * SPDX-License-Identifier: GPL-3.0-only 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | /** 14 | * \file kern/kern_main.c 15 | * \brief Kernel main entrypoint 16 | * 17 | * This function contains code executed right after the platform loader has 18 | * finished with the platform-dependent stuff. Code in this module should 19 | * finish with the system initialisation and delegate to the init process 20 | * to have a proper userland switch. (Or until this is programmed, at least 21 | * do something useful). 22 | */ 23 | 24 | static void ramdisk_init(void); 25 | static void kernel_welcome(void); 26 | 27 | /** 28 | * \brief Kernel main 29 | * 30 | * Virtually the main function, although you should know that the platform 31 | * loader may have done additional stuff before reaching here. Check out 32 | * code preceding a call to kernel_main in the loader code for each platform. 33 | */ 34 | void 35 | kernel_main(void) 36 | { 37 | vfs_init(); 38 | device_init(); 39 | ramdisk_init(); 40 | enable_paging(); 41 | kernel_welcome(); 42 | } 43 | 44 | static void 45 | ramdisk_init(void) 46 | { 47 | unsigned int i; 48 | multiboot_module_t *multiboot_mods; 49 | unsigned char *tar; 50 | 51 | multiboot_mods = (multiboot_module_t *) multiboot_info->mods_addr; 52 | for (i = 0; i < multiboot_info->mods_count; i++) { 53 | /* We can afford to strcmp because "ramdisk" is static. */ 54 | if (!strcmp("ramdisk", (char *) multiboot_mods[0].string)) { 55 | /* We found the ramdisk. */ 56 | tar = (unsigned char *) multiboot_mods[i].mod_start; 57 | vfs_mount("tarfs", "INITRD", tar); 58 | return; 59 | } 60 | } 61 | } 62 | 63 | static vfs_node_t * 64 | fs_resolve_and_open(const char *path, unsigned int args) 65 | { 66 | vfs_node_t *node = fs_resolve(path); 67 | if (node) { 68 | if (fs_open(node, args) != 0) { 69 | node = NULL; 70 | } 71 | } 72 | return node; 73 | } 74 | 75 | static unsigned int 76 | fs_write_string(vfs_node_t *node, unsigned int offt, const char *str) 77 | { 78 | return fs_write(node, offt, (unsigned char *) str, strlen(str)); 79 | } 80 | 81 | static void 82 | kernel_welcome(void) 83 | { 84 | vfs_node_t *vtcon, *motd; 85 | int read, offt; 86 | char buffer[64]; 87 | 88 | vtcon = fs_resolve_and_open("DEV:/vtcon", VO_FWRITE); 89 | 90 | motd = fs_resolve_and_open("INITRD:/SYSTEM/MOTD.TXT", VO_FREAD); 91 | if (motd) { 92 | offt = 0; 93 | while ((read = fs_read(motd, offt, buffer, 64)) > 0) { 94 | fs_write(vtcon, 0, buffer, read); 95 | offt += read; 96 | } 97 | fs_write_string(vtcon, 0, "\n"); 98 | fs_close(motd); 99 | } 100 | for (;;) { 101 | fs_read(vtcon, 0, buffer, 64); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /kernel/stdkern/list.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2021 The NativeOS contributors 4 | * SPDX-License-Identifier: GPL-3.0-only 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | static inline void 11 | chain(listnode_t *car, listnode_t *cdr) 12 | { 13 | if (car) 14 | car->next = cdr; 15 | if (cdr) 16 | cdr->prev = car; 17 | } 18 | 19 | static listnode_t * 20 | listnode_alloc(void *ptr) 21 | { 22 | listnode_t *node = (listnode_t *) malloc(sizeof(listnode_t)); 23 | if (node) { 24 | node->prev = NULL; 25 | node->next = NULL; 26 | node->data = ptr; 27 | } 28 | return node; 29 | } 30 | 31 | static void * 32 | listnode_free(listnode_t *node) 33 | { 34 | void *ptr = node->data; 35 | node->prev = NULL; 36 | node->next = NULL; 37 | node->data = NULL; 38 | free(node); 39 | return ptr; 40 | } 41 | 42 | list_t * 43 | list_alloc() 44 | { 45 | list_t *list = (list_t *) malloc(sizeof(list_t)); 46 | if (list) { 47 | list->count = 0; 48 | list->head = NULL; 49 | list->tail = NULL; 50 | } 51 | return list; 52 | } 53 | 54 | unsigned int 55 | list_count(list_t *list) 56 | { 57 | return list->count; 58 | } 59 | 60 | listnode_t * 61 | list_append(list_t *list, void *ptr) 62 | { 63 | listnode_t *node = listnode_alloc(ptr); 64 | if (node) { 65 | chain(list->tail, node); 66 | list->tail = node; 67 | if (!list->head) 68 | list->head = node; 69 | list->count++; 70 | } 71 | return node; 72 | } 73 | 74 | listnode_t * 75 | list_prepend(list_t *list, void *ptr) 76 | { 77 | listnode_t *node = listnode_alloc(ptr); 78 | if (node) { 79 | chain(node, list->head); 80 | list->head = node; 81 | if (!list->tail) 82 | list->tail = node; 83 | list->count++; 84 | } 85 | return node; 86 | } 87 | 88 | listnode_t * 89 | list_insert(list_t *list, void *ptr, unsigned int before) 90 | { 91 | listnode_t *cur = list->head, *newnode, *prevnode; 92 | 93 | /* 94 | * Shortcircuit head and tail of the list. I dislike having multiple 95 | * algorithms in the same function, but it is useful to filter out 96 | * all the "prev is null", "next is null", "assign node to head" and 97 | * "assign node to tail" stuff. 98 | */ 99 | if (!before) /* head */ 100 | return list_prepend(list, ptr); 101 | if (before >= list->count) /* tail */ 102 | return list_append(list, ptr); 103 | 104 | /* Inserts the new node before cur. */ 105 | while (before--) 106 | cur = cur->next; 107 | newnode = listnode_alloc(ptr); 108 | if (newnode) { 109 | prevnode = cur->prev; 110 | chain(prevnode, newnode); 111 | chain(newnode, cur); 112 | list->count++; 113 | } 114 | return newnode; 115 | } 116 | 117 | void * 118 | list_first(list_t *list) 119 | { 120 | if (list->head) 121 | return list->head->data; 122 | else 123 | return NULL; 124 | } 125 | 126 | void * 127 | list_last(list_t *list) 128 | { 129 | if (list->tail) 130 | return list->tail->data; 131 | else 132 | return NULL; 133 | } 134 | 135 | void * 136 | list_at(list_t *list, unsigned int idx) 137 | { 138 | listnode_t *cur = list->head; 139 | if (idx >= list->count) 140 | return NULL; 141 | while (idx--) { 142 | cur = cur->next; 143 | } 144 | return cur->data; 145 | } 146 | 147 | unsigned int 148 | list_index(list_t *list, void *ptr) 149 | { 150 | listnode_t *cur = list->head; 151 | unsigned int i = 0; 152 | while (cur) { 153 | if (cur->data == ptr) 154 | return i; 155 | cur = cur->next; 156 | i++; 157 | } 158 | return (unsigned int) -1; 159 | } 160 | 161 | void 162 | list_empty(list_t *list) 163 | { 164 | listnode_t *cur = list->head; 165 | if (list->count) { 166 | while (cur) { 167 | listnode_free(cur); 168 | cur = cur->next; 169 | } 170 | list->head = NULL; 171 | list->tail = NULL; 172 | list->count = 0; 173 | } 174 | } 175 | 176 | static void * 177 | list_unchain(list_t *list, listnode_t *node) 178 | { 179 | listnode_t *prev = node->prev, *next = node->next; 180 | if (prev) 181 | prev->next = next; 182 | else 183 | list->head = node->next; 184 | if (next) 185 | next->prev = prev; 186 | else 187 | list->tail = node->prev; 188 | list->count--; 189 | return listnode_free(node); 190 | } 191 | 192 | void * 193 | list_remove(list_t *list, unsigned int at) 194 | { 195 | listnode_t *cur = list->head; 196 | if (!list->head || at >= list->count) 197 | return NULL; 198 | while (at--) { 199 | cur = cur->next; 200 | if (!cur) { 201 | return NULL; 202 | } 203 | } 204 | return list_unchain(list, cur); 205 | } 206 | 207 | void * 208 | list_delete(list_t *list, void *ptr) 209 | { 210 | listnode_t *cur = list->head; 211 | while (cur) { 212 | if (cur->data == ptr) { 213 | return list_unchain(list, cur); 214 | } 215 | cur = cur->next; 216 | } 217 | return NULL; 218 | } 219 | 220 | void * 221 | list_pop_head(list_t *list) 222 | { 223 | if (list->head) 224 | return list_unchain(list, list->head); 225 | else 226 | return NULL; 227 | } 228 | 229 | void * 230 | list_pop_tail(list_t *list) 231 | { 232 | if (list->tail) 233 | return list_unchain(list, list->tail); 234 | else 235 | return NULL; 236 | } 237 | 238 | void 239 | list_free(list_t *list) 240 | { 241 | list_empty(list); 242 | free(list); 243 | } 244 | -------------------------------------------------------------------------------- /kernel/stdkern/memcpy.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2018 The NativeOS contributors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | void * 22 | memcpy(void *dst, const void *src, size_t count) 23 | { 24 | unsigned char *pdst = (unsigned char *) dst; 25 | unsigned char *psrc = (unsigned char *) src; 26 | while (count--) { 27 | *pdst++ = *psrc++; 28 | } 29 | return dst; 30 | } 31 | -------------------------------------------------------------------------------- /kernel/stdkern/memset.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void * 4 | memset(void *dest, int byte, size_t count) 5 | { 6 | unsigned char *ptr = (unsigned char *) dest; 7 | while (count--) 8 | *ptr++ = byte; 9 | return dest; 10 | } -------------------------------------------------------------------------------- /kernel/stdkern/ringbuf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2021 The NativeOS contributors 4 | * SPDX-License-Identifier: GPL-3.0-only 5 | */ 6 | 7 | #include 8 | #include 9 | 10 | ringbuf_t * 11 | ringbuf_alloc(unsigned int size) 12 | { 13 | ringbuf_t *ringbuf = (ringbuf_t *) malloc(sizeof(ringbuf_t)); 14 | if (ringbuf) { 15 | ringbuf->size = size; 16 | ringbuf->writeptr = 0; 17 | ringbuf->readptr = 0; 18 | ringbuf->buffer = (unsigned char *) malloc(size); 19 | /* TODO: Fill the buffer with zeros. */ 20 | if (!ringbuf->buffer) { 21 | free(ringbuf); 22 | ringbuf = 0; 23 | } 24 | } 25 | return ringbuf; 26 | } 27 | 28 | void 29 | ringbuf_write(ringbuf_t *buf, unsigned char byte) 30 | { 31 | if (buf->writeptr == buf->readptr && buf->status & RINGBUF_FUL) { 32 | /* We are writing in a full buffer, so it's an overflow. */ 33 | buf->status |= RINGBUF_OVF; 34 | if (++buf->readptr == buf->size) 35 | buf->readptr = 0; 36 | } 37 | buf->buffer[buf->writeptr] = byte; 38 | if (++buf->writeptr == buf->size) 39 | buf->writeptr = 0; 40 | if (buf->writeptr == buf->readptr) 41 | buf->status |= RINGBUF_FUL; 42 | } 43 | 44 | int 45 | ringbuf_test_overflow(ringbuf_t *buf) 46 | { 47 | return buf->status & RINGBUF_OVF; 48 | } 49 | 50 | int 51 | ringbuf_test_ready(ringbuf_t *buf) 52 | { 53 | return buf->writeptr != buf->readptr || buf->status & RINGBUF_FUL; 54 | } 55 | 56 | unsigned char 57 | ringbuf_read(ringbuf_t *buf) 58 | { 59 | unsigned char byte = buf->buffer[buf->readptr]; 60 | if (++buf->readptr == buf->size) 61 | buf->readptr = 0; 62 | buf->status = 0; /* clear bytes */ 63 | return byte; 64 | } 65 | 66 | void 67 | ringbuf_free(ringbuf_t *buf) 68 | { 69 | if (buf) { 70 | free(buf->buffer); 71 | } 72 | free(buf); 73 | } -------------------------------------------------------------------------------- /kernel/stdkern/strcat.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * \brief Implementation of strcat and strncat 4 | */ 5 | 6 | #include 7 | 8 | char * 9 | strcat(char *s1, const char *s2) 10 | { 11 | char *ptr1 = s1; 12 | char *ptr2 = (char *) s2; 13 | 14 | if (ptr1) { 15 | /* Make ptr1 point to the \0 at the end of s1. */ 16 | while (*ptr1) 17 | ptr1++; 18 | if (ptr2) { 19 | while (*ptr2) { 20 | *ptr1++ = *ptr2++; 21 | } 22 | *ptr1++ = '\0'; 23 | } 24 | } 25 | return s1; 26 | } 27 | 28 | char * 29 | strncat(char *s1, const char *s2, size_t count) 30 | { 31 | char *ptr1 = s1; 32 | char *ptr2 = (char *) s2; 33 | if (ptr1) { 34 | /* Make ptr1 point to the \0 at the end of s1. */ 35 | while (*ptr1) 36 | ptr1++; 37 | if (ptr2 && count) { 38 | while (count-- && *ptr2) { 39 | *ptr1++ = *ptr2++; 40 | } 41 | *ptr1++ = '\0'; 42 | } 43 | } 44 | return s1; 45 | } 46 | -------------------------------------------------------------------------------- /kernel/stdkern/strchr.c: -------------------------------------------------------------------------------- 1 | char * 2 | strchr(const char *s, int c) 3 | { 4 | char *ptr = (char *) s; 5 | if (ptr) { 6 | while (*ptr) { 7 | if (*ptr == c) { 8 | return ptr; 9 | } 10 | ptr++; 11 | } 12 | } 13 | return 0; 14 | } 15 | 16 | char * 17 | strrchr(const char *s, int c) 18 | { 19 | char *ptr = (char *) s; 20 | char *target = 0; 21 | if (ptr) { 22 | while (*ptr) { 23 | if (*ptr == c) { 24 | target = ptr; 25 | } 26 | ptr++; 27 | } 28 | } 29 | return target; 30 | } 31 | -------------------------------------------------------------------------------- /kernel/stdkern/strcmp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2018 The NativeOS contributors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | int 22 | strcmp(const char *s1, const char *s2) 23 | { 24 | unsigned char *cmp1 = (unsigned char *) s1; 25 | unsigned char *cmp2 = (unsigned char *) s2; 26 | 27 | while (*cmp1 && *cmp2) { 28 | if (*cmp1 != *cmp2) { 29 | return *cmp1 - *cmp2; 30 | } 31 | cmp1++; 32 | cmp2++; 33 | } 34 | 35 | return *cmp1 - *cmp2; 36 | } 37 | 38 | int 39 | strncmp(const char *s1, const char *s2, size_t n) 40 | { 41 | unsigned char *cmp1 = (unsigned char *) s1; 42 | unsigned char *cmp2 = (unsigned char *) s2; 43 | 44 | if (n == 0) { 45 | /* Early bail out. */ 46 | return 0; 47 | } 48 | 49 | /* Bail as soon as any string ends. */ 50 | while (*cmp1 && *cmp2 && n--) { 51 | if (*cmp1 != *cmp2) { 52 | return *cmp1 - *cmp2; 53 | } 54 | cmp1++; 55 | cmp2++; 56 | } 57 | 58 | return *cmp1 - *cmp2; 59 | } 60 | -------------------------------------------------------------------------------- /kernel/stdkern/strcpy.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2018 The NativeOS contributors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | char * 22 | strcpy(char *dst, const char *src) 23 | { 24 | char *ptr = dst; 25 | if (src && *src) { 26 | while (*src) { 27 | *ptr++ = *src++; 28 | } 29 | *ptr++ = 0; 30 | } 31 | return dst; 32 | } 33 | 34 | char * 35 | strncpy(char *dst, const char *src, size_t len) 36 | { 37 | char *ptr = dst; 38 | 39 | if (len) { 40 | do { 41 | // When src reaches ends, assigns \0 before fail. 42 | if (!(*ptr++ = *src++)) { 43 | // Did you know that the spec says you must 44 | // fill the remaining of the string with \0? 45 | // Because you are supposed to copy len 46 | // bytes. 47 | while (len--) 48 | *ptr++ = 0; 49 | break; 50 | } 51 | } while(--len); 52 | } 53 | return dst; 54 | } 55 | -------------------------------------------------------------------------------- /kernel/stdkern/strdup.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | char *strdup(const char *s) 4 | { 5 | unsigned int nchar; 6 | char *copystr; 7 | 8 | nchar = strlen(s) + 1; 9 | copystr = malloc(nchar); 10 | if (copystr) { 11 | strcpy(copystr, s); 12 | } 13 | return copystr; 14 | } 15 | -------------------------------------------------------------------------------- /kernel/stdkern/strlen.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2018 The NativeOS contributors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | 21 | size_t 22 | strlen(const char *s) 23 | { 24 | const char *ptr; 25 | for (ptr = s; *ptr; ptr++) 26 | ; 27 | return ptr - s; 28 | } 29 | 30 | size_t 31 | strnlen(const char *s, size_t maxlen) 32 | { 33 | if (maxlen == 0) { 34 | return 0; 35 | } 36 | const char *ptr; 37 | for (ptr = s; *ptr && maxlen--; ptr++) 38 | ; 39 | return ptr - s; 40 | } 41 | -------------------------------------------------------------------------------- /kernel/stdkern/strsep.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static inline int 4 | matches(char chr, char *delims) 5 | { 6 | while (*delims) { 7 | if (chr == *delims) 8 | return 1; 9 | delims++; 10 | } 11 | return 0; 12 | } 13 | 14 | /* 15 | * This function does a lot of things. 16 | * - The original value pointed by strptr must be returned. 17 | * - The pointer in strptr must change to the location of the 18 | * coincidence in the string. 19 | */ 20 | char * 21 | strsep(char **strptr, const char *delimiter) 22 | { 23 | char *delim, *strcur, *origstrptr; 24 | 25 | if (!strptr || !*strptr) { 26 | /* Shortcircuit to NULL if given strptr is NULL. */ 27 | return 0; 28 | } 29 | 30 | strcur = *strptr; 31 | origstrptr = *strptr; 32 | while (*strcur) { 33 | delim = (char *) delimiter; 34 | while (*delim) { 35 | if (*delim == *strcur) { 36 | *strcur = 0; 37 | *strptr = (strcur + 1); 38 | return origstrptr; 39 | } 40 | delim++; 41 | } 42 | strcur++; 43 | } 44 | 45 | /* When nothing matched, it will return NULL via the pointer. */ 46 | *strptr = 0; 47 | return origstrptr; 48 | } 49 | -------------------------------------------------------------------------------- /kernel/sys/device.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * \brief Declare a device descriptor 5 | * 6 | * Calling this macro providing a proper device_t structure will create a 7 | * marker pointer in a different section of the ELF binary image, so that 8 | * the device manager will be able to dynamically load it when the kernel 9 | * starts up. 10 | * 11 | * \param name the name to use for the exported symbol 12 | * \param dev the device_t structure to add to the table 13 | */ 14 | #define DEVICE_DESCRIPTOR(name, dev) \ 15 | driver_t *device_descriptor_##name \ 16 | __attribute__((section(".text.driver"), used)) = &dev 17 | 18 | struct device; 19 | struct driver; 20 | 21 | typedef int (*device_open)(unsigned int flags); 22 | typedef unsigned int (*device_read_chr)(unsigned char *buf, unsigned int size); 23 | typedef unsigned int (*device_write_chr)(unsigned char *buf, unsigned int size); 24 | typedef unsigned int (*device_read_blk)(unsigned char *buf, 25 | unsigned int offt, 26 | unsigned int size); 27 | typedef unsigned int (*device_write_blk)(unsigned char *buf, 28 | unsigned int offt, 29 | unsigned int size); 30 | typedef int (*device_ioctl)(int iorq, void *args); 31 | typedef int (*device_close)(); 32 | 33 | typedef struct device { 34 | struct driver *dev_family; 35 | device_open dev_open; 36 | device_read_blk dev_read_blk; 37 | device_write_blk dev_write_blk; 38 | device_read_chr dev_read_chr; 39 | device_write_chr dev_write_chr; 40 | device_ioctl dev_ioctl; 41 | device_close dev_close; 42 | } device_t; 43 | 44 | typedef struct driver { 45 | char *drv_name; 46 | unsigned int drv_flags; 47 | #define DV_FCHARDEV 1 48 | #define DV_FBLCKDEV 2 49 | int (*drv_init)(void); 50 | void (*drv_tini)(void); 51 | } driver_t; 52 | 53 | /** 54 | * \brief Init the device manager. 55 | * 56 | * This function must be called prior to attempting to mount any kind of 57 | * device in order to initialize the system data structures required to 58 | * maintain the device manager itself. 59 | */ 60 | void device_init(void); 61 | 62 | /** 63 | * \brief Install a device. 64 | * 65 | * This function should be called by drivers when attempting to expose a 66 | * specific device through the device manager. A name to identify the device 67 | * should be given, and the device will be accessible through that name. 68 | * 69 | * The acceptable return values for this function are negative values for 70 | * unsuccessful installations, non-negatives otherwise. The current list of 71 | * status codes this function may return are: 72 | * 73 | * 0: device mounted successfully 74 | * -1: kernel error 75 | * -2: device name already exists 76 | * 77 | * \param dev the device to make accessible through the device manager. 78 | * \param mtname the name to use when refering to this device. 79 | * \return status code with the outcome of the mount operation. 80 | */ 81 | int device_install(device_t *dev, char *mtname); 82 | 83 | /** 84 | * \brief Uninstall a device. 85 | * 86 | * This function should be called by drivers when attempting to remove a 87 | * previously exposed driver, using its identifier. This function will 88 | * silently drop requests to remove devices whose name does not match the 89 | * name of any mounted device. 90 | * 91 | * \param mtname the name of the device to remove from the device manager. 92 | */ 93 | void device_remove(char *mtname); 94 | -------------------------------------------------------------------------------- /kernel/sys/list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2021 The NativeOS contributors 4 | * SPDX-License-Identifier: GPL-3.0-only 5 | */ 6 | 7 | #pragma once 8 | 9 | typedef struct listnode listnode_t; 10 | typedef struct list list_t; 11 | 12 | struct listnode { 13 | void *data; 14 | listnode_t *prev; 15 | listnode_t *next; 16 | }; 17 | 18 | struct list { 19 | listnode_t *head; 20 | listnode_t *tail; 21 | int count; 22 | }; 23 | 24 | list_t *list_alloc(); 25 | 26 | unsigned int list_count(list_t *list); 27 | 28 | listnode_t *list_append(list_t *list, void *ptr); 29 | 30 | listnode_t *list_prepend(list_t *list, void *ptr); 31 | 32 | listnode_t *list_insert(list_t *list, void *ptr, unsigned int before); 33 | 34 | void *list_first(list_t *list); 35 | 36 | void *list_last(list_t *list); 37 | 38 | void *list_at(list_t *list, unsigned int idx); 39 | 40 | unsigned int list_index(list_t *list, void *ptr); 41 | 42 | void list_empty(list_t *list); 43 | 44 | void *list_remove(list_t *list, unsigned int at); 45 | 46 | void *list_delete(list_t *list, void *ptr); 47 | 48 | void *list_pop_head(list_t *list); 49 | 50 | void *list_pop_tail(list_t *list); 51 | 52 | void list_free(list_t *list); 53 | 54 | #define list_foreach(list, var) for (var = list->head; var; var = var->next) 55 | -------------------------------------------------------------------------------- /kernel/sys/ringbuf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2021 The NativeOS contributors 4 | * SPDX-License-Identifier: GPL-3.0-only 5 | */ 6 | #pragma once 7 | 8 | #define RINGBUF_FUL 1 9 | #define RINGBUF_OVF 2 10 | 11 | typedef struct ringbuf { 12 | unsigned char *buffer; 13 | unsigned int size; 14 | unsigned int writeptr; 15 | unsigned int readptr; 16 | 17 | unsigned int status; 18 | } ringbuf_t; 19 | 20 | /** 21 | * \brief Allocates a new ring buffer. 22 | * \param size the amount of bytes for the buffer of this ring buffer. 23 | * \return a pointer to the ring buffer. 24 | */ 25 | ringbuf_t *ringbuf_alloc(unsigned int size); 26 | 27 | /** 28 | * \brief Writes a byte into the ring buffer. 29 | * 30 | * As a side effect, if writing this byte overwrites a byte still pending to 31 | * be read, the buffer will be marked as overflow, in order to signal that a 32 | * previous write operation overlapped an unread section of the buffer. 33 | * 34 | * \param buf the buffer to write into 35 | * \param byte the byte to place into the ring buffer. 36 | */ 37 | void ringbuf_write(ringbuf_t *buf, unsigned char byte); 38 | 39 | /** 40 | * \brief Tests whether the buffer was overflowed. 41 | * \param buf the buffer to test for overflow. 42 | * \return a non-zero value if a previous write operation did overflow. 43 | */ 44 | int ringbuf_test_overflow(ringbuf_t *buf); 45 | 46 | /** 47 | * \brief Tests whether the buffer contains data pending to be read. 48 | * \param buf the buffer to test for readiness. 49 | * \return a non-zero value if the buffer contains data waiting to be read. 50 | */ 51 | int ringbuf_test_ready(ringbuf_t *buf); 52 | 53 | /** 54 | * \brief Reads a byte from the given ring buffer. 55 | * Note that calling this function in a buffer that is not ready to be read 56 | * has undefined side effects and it is possible that junk will be retrieved. 57 | * \param buf the buffer where the data should be read 58 | * \return the byte read from the ring buffer. 59 | */ 60 | unsigned char ringbuf_read(ringbuf_t *buf); 61 | 62 | /** 63 | * \brief Deallocates a previously allocated ring buffer. 64 | * \param buf the ring buffer to deallocate. 65 | */ 66 | void ringbuf_free(ringbuf_t *buf); -------------------------------------------------------------------------------- /kernel/sys/spinlock.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * \file 5 | * \brief Spinlock API 6 | */ 7 | 8 | /** 9 | * A spinlock. This data structure is open and not opaque because in order to 10 | * statically allocate spinlocks in the kernel source code, it is required to 11 | * provide an implementation for the kernel modules. However, the internal 12 | * fields of a spinlock should never be modified without using the functions 13 | * in the spinlocks API, which are atomic and safe to use. 14 | * 15 | * \ingroup spinlock. 16 | */ 17 | struct spinlock { 18 | /** The current state of the spinlock. */ 19 | int locked; 20 | }; 21 | 22 | /** 23 | * \defgroup spinlock 24 | * \brief Spinlock API 25 | * 26 | * A spinlock is a concurrent data structure that guarantees control to a 27 | * resource that cannot be shared at the same time among multiple threads 28 | * or processes. 29 | * 30 | * The spinlock has to primitives: lock and release. A process will lock 31 | * a resource before trying to access it, and it will release the lock when 32 | * it is done. If a concurrent process tries to lock a locked spinlock, 33 | * the nature of the spinlock makes the process loop (spin) until the other 34 | * process releases the lock. 35 | */ 36 | 37 | /** 38 | * Initialise and reset a spinlock to the unlocked state. 39 | * \ingroup spinlock 40 | * \param lock the spinlock to initialise 41 | */ 42 | void spinlock_init(struct spinlock *lock); 43 | 44 | /** 45 | * Memory barrier that returns once the spinlock is acquired. 46 | * \ingroup spinlock 47 | * \param lock the spinlock to acquire 48 | */ 49 | void spinlock_lock(struct spinlock *lock); 50 | 51 | /** 52 | * Releases a previously acquired spinlock. 53 | * \ingroup spinlock 54 | * \param lock the spinlock to release. 55 | */ 56 | void spinlock_release(struct spinlock *lock); 57 | -------------------------------------------------------------------------------- /kernel/sys/stdkern.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of NativeOS 3 | * Copyright (C) 2015-2018 The NativeOS contributors 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #pragma once 20 | 21 | #include 22 | 23 | /** 24 | * @brief Allocate some memory buffer in the heap. 25 | * @param size the amount of bytes to allocate in the heap. 26 | * @return a pointer to the allocated buffer or NULL if there was an error. 27 | */ 28 | void *malloc(size_t size); 29 | 30 | /** 31 | * @brief Deallocate a memory buffer from the heap. 32 | * @param ptr a pointer to the memory region to be deallocated from heap. 33 | */ 34 | void free(void *ptr); 35 | 36 | /** 37 | * @brief Copies the given number of characters from one buffer to other. 38 | * @param dst the target buffer to copy data to. 39 | * @param src the source buffer to copy data from. 40 | * @param count the amount of bytes to transfer from source to dest. 41 | */ 42 | void *memcpy(void *dst, const void *src, size_t count); 43 | 44 | /** 45 | * @brief Sets every byte in the given buffer region to a value. 46 | * @param dest the target buffer where to set the values in. 47 | * @param byte the value to place in the given buffer. 48 | * @param count the amount of bytes to overwrite in the given buffer. 49 | * @return a pointer to the target buffer. 50 | */ 51 | void *memset(void *dest, int byte, size_t count); 52 | 53 | /** 54 | * @brief Appends a copy of the string s2 into s1. 55 | * @param s1 the string where to append 56 | * @param s2 the string to append 57 | * @return a pointer to s1 58 | */ 59 | char *strcat(char *s1, const char *s2); 60 | 61 | /** 62 | * @brief Appends a slice of the string s2 into s1. 63 | * @param s1 the string where to append 64 | * @param s2 the string to append 65 | * @param count how many characters to append as a maximum 66 | * @return a pointer to s1. 67 | */ 68 | char *strncat(char *s1, const char *s2, size_t count); 69 | 70 | /** 71 | * @brief Locates the first ocurrence of the character c in the string s. 72 | * @param s the string where the search has to be performed. 73 | * @param c the character to locate in the string. 74 | * @return either a pointer to the in-string character, or NULL if not found. 75 | */ 76 | char *strchr(const char *s, int c); 77 | 78 | /** 79 | * @brief Locates the last ocurrence of the character c in the string s. 80 | * @param s the string where the search has to be performed. 81 | * @param c the character to locate in the string. 82 | * @return either a pointer to the in-string character, or NULL if not found. 83 | */ 84 | char *strrchr(const char *s, int c); 85 | 86 | /** 87 | * @brief Compare lexicographically both strings. 88 | * @param s1 the first string to compare 89 | * @param s2 the second string to compare 90 | * @return <0 if s1 > s2, >0 if s1 < s2, 0 if s1 == s2 91 | */ 92 | int strcmp(const char *s1, const char *s2); 93 | 94 | /** 95 | * @brief Compare lexicographically no more than n characters from both strings. 96 | * @param s1 the first string to compare 97 | * @param s2 the second string to compare 98 | * @param n the maximum number of characters to compare 99 | * @return <0 if s1 > s2, >0 if s1 < s2, 0 if s1 == s2 100 | */ 101 | int strncmp(const char *s1, const char *s2, size_t n); 102 | 103 | /** 104 | * @brief Copies the string src into dst. 105 | * @param dst the target string where to put the copied characters. 106 | * @param src the source string to copy characters form. 107 | * @return a pointer to dst string. 108 | */ 109 | char *strcpy(char *dst, const char *src); 110 | 111 | /** 112 | * @brief Copies at most len characters from src into dst. 113 | * @param dst the target string where to put the copied characters. 114 | * @param src the source string to copy characters from. 115 | * @param len the maximum amount of characters to copy. 116 | * @return a pointer to dst string. 117 | */ 118 | char *strncpy(char *dst, const char *src, size_t len); 119 | 120 | /** 121 | * @brief Computes the length of the string s. 122 | * @param s the string whose length we want to compute. 123 | * @return the computed length of the string s. 124 | */ 125 | size_t strlen(const char *s); 126 | 127 | /** 128 | * @brief Computes the length of the string s as long as doesn't exceed maxlen. 129 | * @param s the string whose length we want to compute. 130 | * @param maxlen the maximum amount of characters to compute. 131 | * @return the computed length of the string s or maxlen if it's larger. 132 | */ 133 | size_t strnlen(const char *s, size_t maxlen); 134 | 135 | /** 136 | * @brief Duplicates a string into the heap and returns a copy. 137 | * @param s the string to copy into the heap 138 | * @return a pointer to the heap area where the string was copied. 139 | */ 140 | char *strdup(const char *s); 141 | 142 | /** 143 | * @brief Separates strings 144 | * @param strptr A pointer to the string to be separated. 145 | * @param delimiter The delimiter to break the string 146 | * @return a pointer to the original strptr value. 147 | */ 148 | char *strsep(char **strptr, const char *delimiter); 149 | -------------------------------------------------------------------------------- /kernel/sys/vfs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct vfs_node; 4 | struct vfs_filesys; 5 | 6 | #define VO_FREAD 0x0001 /**< Open the file to read from it. */ 7 | #define VO_FWRITE 0x0002 /**< Open the file to write into it. */ 8 | #define VO_FBINARY 0x004 /**< Open the file in binary mode. */ 9 | 10 | #define VN_FREGFILE 0x1 11 | #define VN_FDIR 0x2 12 | #define VN_FCHARDEV 0x3 13 | #define VN_FBLOCKDEV 0x4 14 | 15 | /** 16 | * \brief Virtual File System Operations 17 | * 18 | * A file system implementation will implement some of the hooks in this 19 | * data structure. If the operating system receives a hook request on behalf 20 | * of a specific node, it will call the hook implementation as long as the 21 | * node operations table implements the given function. 22 | */ 23 | typedef struct vfs_ops { 24 | /** 25 | * \brief Open a VFS node for further access. 26 | * 27 | * This hook can only be implemented by regular files and devices. 28 | * 29 | * The file system implementation should allocate a way for the system 30 | * to send further VFS requests to the node, such as reading or 31 | * writing. 32 | * 33 | * \param node the VFS node that will be opened 34 | * \param flags a flags mask with the node open mode. 35 | * \return status code: zero on success, non-zero on failure. 36 | */ 37 | int (*vfs_open)(struct vfs_node *node, unsigned int flags); 38 | 39 | /** 40 | * \brief Read from a VFS node. 41 | * 42 | * This hook can only be implemented by regular files and devices. 43 | * 44 | * A file system implementation that uses this hook should copy into 45 | * the given buffer \p buf up to \p len bytes from the underlying 46 | * container mapped to the VFS node, starting at offset \p offt. 47 | * 48 | * \param node the VFS node that will be read 49 | * \param offt the offset to apply within the container 50 | * \param buf the buffer where the read bytes should be copied 51 | * \param len the number of bytes to copy into the buffer 52 | * \return the actual number of bytes that were copied to the buffer 53 | */ 54 | unsigned int (*vfs_read)(struct vfs_node *node, 55 | unsigned int offt, 56 | void *buf, 57 | unsigned int len); 58 | 59 | /** 60 | * \brief Write to a VFS node. 61 | * 62 | * This hook can only be implemented by regular files and devices. 63 | * 64 | * A file system implementation that uses this hook should copy from 65 | * the given buffer \p buf up to \p len bytes into the underlying 66 | * container mapped to the VFS node, starting at offset \p offt. 67 | * 68 | * \param node the VFS node that will be written 69 | * \param offt the offset to apply within the container 70 | * \param buf the buffer where the bytes to copy are located 71 | * \param len the number of bytes to copy into the container 72 | * \return the actual number of bytes that were copied from the buffer 73 | */ 74 | unsigned int (*vfs_write)(struct vfs_node *node, 75 | unsigned int offt, 76 | void *buf, 77 | unsigned int len); 78 | 79 | /** 80 | * \brief IO Control for VFS nodes. 81 | * 82 | * This hook can only be implemented by devices. 83 | * 84 | * ioctl is a special hack that has been in use for a very long time 85 | * to execute side requests into device driver files. It can only be 86 | * used by devices, and the semantics of the request depend in the 87 | * device driver itself. 88 | * 89 | * \param node the VFS node where the request will be sent 90 | * \param iorq the request code number to send to the device 91 | * \param args additional iorq-dependent parameter payload 92 | * \return the outcome of the ioctl request as defined by the driver 93 | */ 94 | int (*vfs_ioctl)(struct vfs_node *node, int iorq, void *args); 95 | 96 | /** 97 | * \brief Close an VFS node 98 | * 99 | * This hook can only be implemented by regular files and devices. 100 | * 101 | * This hook is used by the file system implementation to mark the 102 | * node as closed. The file system implementation should assume that 103 | * after this hook is called, no further requests to the given VFS 104 | * node will be received until another open request is received. 105 | * 106 | * \param node the VFS node to close. 107 | * \return zero if successful, non-zero if not successful 108 | */ 109 | int (*vfs_close)(struct vfs_node *node); 110 | 111 | /** 112 | * \brief Read a directory entry 113 | * 114 | * This hook can only be implemented by directories. 115 | * 116 | * This hook is used to traverse the VFS node representation of a 117 | * directory in order to look for child VFS nodes that are contained 118 | * inside the directory. To iterate, an \p index number is given, 119 | * which should return a proper node if it points to a valid node 120 | * inside this directory. Otherwise, it should return NULL to mark 121 | * that the index is out of the bounds of the directory children. 122 | * 123 | * \todo This is not a directory entry, this is an vfs_node. 124 | * \param node the VFS node to iterate 125 | * \param index the index number of the child VFS node to find. 126 | * \return a pointer to the child VFS node or NULL. 127 | */ 128 | struct vfs_node *(*vfs_readdir)(struct vfs_node *node, 129 | unsigned int index); 130 | 131 | /** 132 | * \brief Find a directory entry 133 | * 134 | * This hook can only be implemented by directories. 135 | * 136 | * This hook is used to locate a specific node inside the given 137 | * VFS node by its name. It is a way to lookup a specific entry 138 | * given the name of the entry. It should return a pointer to the 139 | * VFS node if the given directory contains a child VFS node 140 | * that matches this name. Otherwise, it should return NULL. 141 | * 142 | * \todo This is not a directory entry, this is an vfs_node. 143 | * \param node the VFS node to lookup into 144 | * \param nodename the name of the child node to lookup 145 | * \return a pointer to the child VFS ndoe or NULL. 146 | */ 147 | struct vfs_node *(*vfs_finddir)(struct vfs_node *node, char *nodename); 148 | } vfs_ops_t; 149 | 150 | typedef struct vfs_node { 151 | char vn_name[64]; 152 | unsigned int vn_flags; 153 | struct vfs_node *vn_parent; 154 | struct vfs_volume *vn_volume; 155 | void *vn_payload; 156 | } vfs_node_t; 157 | 158 | typedef struct vfs_volume { 159 | char *vv_name; 160 | struct vfs_filesys *vv_family; 161 | struct vfs_node *vv_root; 162 | void *vv_payload; 163 | } vfs_volume_t; 164 | 165 | typedef struct vfs_filesys { 166 | /** The identifier name of the file system driver. */ 167 | char *fsd_ident; 168 | 169 | /** The public name of the file system driver. */ 170 | char *fsd_name; 171 | 172 | /** A hook to tell the FS Driver it is being initialised. */ 173 | void (*fsd_init)(void); 174 | 175 | /** A hook to tell the FS Driver it is being destructed. */ 176 | void (*fsd_tini)(void); 177 | 178 | /** A hook used to tell the FS Driver that a volume is mounted. */ 179 | int (*fsd_mount)(vfs_volume_t *vol); 180 | 181 | /** A pointer to the operations table for this FS Driver. */ 182 | vfs_ops_t *fsd_ops; 183 | } vfs_filesys_t; 184 | 185 | /** 186 | * \brief Get the file system operations table for a VFS node. 187 | * \param node the file system node to retrieve the operations table. 188 | * \return the operations table for the given node. 189 | */ 190 | #define NODE_OPS(node) \ 191 | ((node && node->vn_volume && node->vn_volume \ 192 | && node->vn_volume->vv_family) \ 193 | ? node->vn_volume->vv_family->fsd_ops \ 194 | : 0) 195 | 196 | #define FS_DESCRIPTOR(name, fs) \ 197 | vfs_filesys_t *fs_descriptor_##name \ 198 | __attribute__((section(".text.fs"), used)) = &fs 199 | 200 | void vfs_init(void); 201 | 202 | int vfs_mount(char *driver, char *name, void *argp); 203 | int vfs_umount(char *mountname); 204 | 205 | vfs_node_t *vfs_get_volume(char *mountname); 206 | 207 | /** 208 | * @brief Given a path, locate the node in the mounted filesystems 209 | * @param path the path to the node we want to find 210 | * @return a pointer to the found node or NULL if not found 211 | */ 212 | vfs_node_t *fs_resolve(const char *path); 213 | 214 | int fs_open(vfs_node_t *node, unsigned int flags); 215 | unsigned int 216 | fs_read(vfs_node_t *node, unsigned int offset, void *buf, unsigned int len); 217 | unsigned int 218 | fs_write(vfs_node_t *node, unsigned int offset, void *buf, unsigned int len); 219 | int fs_ioctl(vfs_node_t *node, int iorq, void *args); 220 | int fs_close(vfs_node_t *node); 221 | vfs_node_t *fs_readdir(vfs_node_t *node, unsigned int index); 222 | vfs_node_t *fs_finddir(vfs_node_t *node, char *name); 223 | -------------------------------------------------------------------------------- /ramdisk/SYSTEM/MOTD.TXT: -------------------------------------------------------------------------------- 1 | Welcome to NativeOS Preview. 2 | 3 | Thank you for your interest in testing NativeOS. NativeOS is a toy 4 | OS for the i386 system architecture to help me learn more about how 5 | computers work. 6 | 7 | This is a work in progress. Expect bugs. Please, keep an eye on the 8 | repository and the issue tracker for future work and bug fixes. 9 | 10 | * Repository: https://git.danirod.es/nativeos.git 11 | * Issue tracker: https://redmine.danirod.es/projects/nativeos 12 | * GitHub Mirror: https://github.com/danirod/nativeos 13 | 14 | 15 | This is the virtual console test. 16 | Press keys to type them into the screen. 17 | Press keys F1 to F8 to switch context. 18 | -------------------------------------------------------------------------------- /tools/.gitignore: -------------------------------------------------------------------------------- 1 | # This is where the i386-elf toolchain gets downloaded to. 2 | toolchain/ 3 | i386-elf-*.zip 4 | i386-elf-*.tar.bz2 5 | 6 | -------------------------------------------------------------------------------- /tools/buildno.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if ! git status >/dev/null 2>/dev/null ; then 4 | exit 1 5 | fi 6 | 7 | git log --oneline --first-parent | wc -l | awk '{ print "Build " $1 }' 8 | -------------------------------------------------------------------------------- /tools/cdrom/boot/grub/grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=0 2 | 3 | menuentry 'NativeOS' { 4 | multiboot /boot/nativeos.exe 5 | module /ramdisk.tar ramdisk 6 | boot 7 | } 8 | -------------------------------------------------------------------------------- /tools/gdbscripts/devices.gdb: -------------------------------------------------------------------------------- 1 | # This file defines macros that make testing the devmgr a little bit easier. 2 | 3 | # dump-devices 4 | # Prints to the console output the list of currently available devices, 5 | # including information about the device family. This list is looked up 6 | # using information exposed from the DEVFS file system. 7 | define dump-dev-devices 8 | set $node = devmgr_list->head 9 | while $node != 0 10 | set $devfile = (vfs_node_t *) $node->data 11 | set $device = (device_t *) $devfile->vn_payload 12 | set $family = (driver_t *) $device->dev_family 13 | 14 | printf "Device: %s. ", $devfile->vn_name 15 | printf "(Family: %s", $family->drv_name 16 | if ($family->drv_flags & 3) == 1 17 | printf ", char" 18 | else 19 | if ($family->drv_flags & 3) == 2 20 | printf ", block" 21 | end 22 | end 23 | printf ")\n" 24 | 25 | set $node = $node->next 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /tools/gdbscripts/heap.gdb: -------------------------------------------------------------------------------- 1 | # This file defines macros that make testing the heap a little bit easier. 2 | 3 | # dump-heap-block $block 4 | # Prints to the debugger console information about the given block. The 5 | # block must be a pointer of type heap_block_t * (defined in heap.c). 6 | # Will print in a single line information and memory addresses. 7 | # Format: "heap block 0x[mem] [used/free] [ok/invl] [size] [start] [end]" 8 | define dump-heap-block 9 | # Print memory address of the block 10 | printf "heap block 0x%08x", (int) $arg0 11 | 12 | # Print status of the block 13 | if $arg0->status == 0x40404040 14 | printf " free" 15 | else 16 | printf " used" 17 | end 18 | 19 | # Test the magic number (to detect corruption) 20 | if $arg0->magic == 0x51514949 21 | printf " ok " 22 | else 23 | printf " INVL" 24 | end 25 | 26 | # Print memory region start and end address 27 | set $start = ((int) $arg0) + sizeof(heap_block_t) 28 | set $end = $start + $arg0->size 29 | printf " 0x%08x < 0x%08x", $start, $end 30 | 31 | # Print size 32 | printf ", %d bytes\n", $arg0->size 33 | end 34 | 35 | # dump-heap 36 | # Prints all the current slices present in the heap linked list. 37 | define dump-heap 38 | set $block = heap_root 39 | while $block != 0 40 | dump-heap-block $block 41 | set $block = (heap_block_t *) $block->next 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /tools/gdbscripts/vfs.gdb: -------------------------------------------------------------------------------- 1 | # This file defines macros that make testing the VFS a little bit easier. 2 | 3 | # dump-fs-drivers 4 | # Prints to the debugger console information about the list of available 5 | # file system drivers that can be used when mounting volumes and other 6 | # VFS entities. 7 | define dump-fs-drivers 8 | set $node = vfs_drivers->head 9 | while $node != 0 10 | set $data = (vfs_filesys_t *) $node->data 11 | printf "* filesys: %s (%s)\n", $data->fsd_ident, $data->fsd_name 12 | set $node = $node->next 13 | end 14 | end 15 | 16 | # dump-fs-volumes 17 | # Prints to the debugger console information about the list of currently 18 | # mounted volumes, showing the name of the volume, and the file system 19 | # driver backing the volume. 20 | define dump-fs-volumes 21 | set $node = vfs_volumes->head 22 | while $node != 0 23 | set $data = (vfs_volume_t *) $node->data 24 | printf "* %s (%s)\n", $data->vv_name, $data->vv_family->fsd_ident 25 | set $node = $node->next 26 | end 27 | end 28 | --------------------------------------------------------------------------------