├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── boot ├── Makefile ├── boot.asm ├── include │ ├── bios.inc │ ├── gdt.inc │ ├── globals.inc │ ├── iso9660.inc │ └── mem.inc └── loader.asm ├── docker ├── Dockerfile └── build.sh ├── docs ├── Makefile └── monk.doxy ├── include ├── core.h ├── kernel │ ├── debug │ │ ├── dump.h │ │ └── log.h │ ├── device │ │ ├── keyboard.h │ │ ├── pci.h │ │ ├── timer.h │ │ └── tty.h │ ├── interrupt │ │ ├── exception.h │ │ └── interrupt.h │ ├── mem │ │ ├── acpi.h │ │ ├── heap.h │ │ ├── paging.h │ │ ├── pmap.h │ │ └── segments.h │ ├── syscall │ │ └── syscall.h │ └── x86 │ │ ├── cpu.h │ │ └── cpu_inl.h └── libc │ ├── stdio.h │ ├── stdlib.h │ └── string.h ├── kernel ├── Makefile ├── debug │ ├── dump.c │ └── log.c ├── device │ ├── keyboard.c │ ├── pci.c │ ├── timer.c │ └── tty.c ├── interrupt │ ├── exception.c │ └── interrupt.asm ├── kernel.ld ├── main.c ├── mem │ ├── acpi.c │ ├── heap.c │ ├── kmem.c │ ├── kmem.h │ ├── paging.c │ └── pmap.c ├── shell.c ├── shell.h ├── start.asm ├── syscall │ └── syscall.c └── x86 │ └── cpu.asm ├── libc ├── Makefile ├── stdio │ ├── snprintf.c │ └── vsnprintf.c ├── stdlib │ └── qsort.c └── string │ ├── memcpy.asm │ ├── memmove.asm │ ├── memset.asm │ ├── memsetd.asm │ ├── memsetw.asm │ ├── memzero.asm │ ├── strcmp.c │ ├── strlcat.c │ ├── strlcpy.c │ └── strlen.c └── scripts ├── config.mk ├── lib.mk ├── mkcdrom.sh └── uncrustify.cfg /.gitignore: -------------------------------------------------------------------------------- 1 | .gdb 2 | .gdbinit 3 | .p4ignore 4 | .tags 5 | build 6 | deps 7 | docs/acpi 8 | docs/intel 9 | docs/monk 10 | notes 11 | TODO 12 | GPATH 13 | GRTAGS 14 | GSYMS 15 | GTAGS 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 Brett Vickers. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions 5 | are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDER ``AS IS'' AND ANY 15 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR 18 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 21 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 22 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #---------------------------------------------------------------------------- 2 | # MonkOS root makefile 3 | # 4 | # Makefile for all kernel and boot loader targets. 5 | #---------------------------------------------------------------------------- 6 | 7 | DIR_ROOT := . 8 | 9 | include $(DIR_ROOT)/scripts/config.mk 10 | 11 | 12 | #---------------------------------------------------------------------------- 13 | # Build targets 14 | #---------------------------------------------------------------------------- 15 | 16 | default: boot kernel iso 17 | 18 | all: boot kernel iso tags docs 19 | 20 | docker: .force 21 | @$(DIR_DOCKER)/build.sh iso 22 | 23 | boot: .force 24 | @$(MAKE) $(MAKE_FLAGS) --directory=$(DIR_BOOT) 25 | 26 | kernel: .force libc 27 | @$(MAKE) $(MAKE_FLAGS) --directory=$(DIR_KERNEL) 28 | 29 | libc: .force 30 | @$(MAKE) $(MAKE_FLAGS) --directory=$(DIR_LIBC) 31 | 32 | iso: .force boot kernel 33 | @echo "$(BLUE)[iso]$(NORMAL) Running mkcdrom.sh" 34 | @$(DIR_SCRIPTS)/mkcdrom.sh 2> /dev/null > /dev/null 35 | @echo "$(BLUE)[iso] $(SUCCESS)" 36 | 37 | docs: .force 38 | @$(MAKE) $(MAKE_FLAGS) --directory=$(DIR_DOCS) 39 | 40 | tags: .force 41 | @echo "$(BLUE)[tags]$(NORMAL) Running exuberant-ctags" 42 | @$(CTAGS) -R --exclude="docs/*" -f .tags 43 | @echo "$(BLUE)[tags] $(SUCCESS)" 44 | 45 | uncrustify: .force 46 | @$(MAKE) $(MAKE_FLAGS) --directory=$(DIR_LIBC) uncrustify 47 | @$(MAKE) $(MAKE_FLAGS) --directory=$(DIR_KERNEL) uncrustify 48 | 49 | debug: .force 50 | @$(QEMU) -gdb tcp::8864 -cdrom $(DIR_BUILD)/monk.iso 51 | 52 | debugwait: .force 53 | @$(QEMU) -S -gdb tcp::8864 -cdrom $(DIR_BUILD)/monk.iso 54 | 55 | hdebug: .force 56 | @$(QEMU) -gdb tcp::8864 -enable-kvm -cpu host \ 57 | -cdrom $(DIR_BUILD)/monk.iso 58 | 59 | test: .force 60 | @$(QEMU) -cdrom $(DIR_BUILD)/monk.iso 61 | 62 | htest: .force 63 | @$(QEMU) -enable-kvm -cpu host -cdrom $(DIR_BUILD)/monk.iso 64 | 65 | clean: .force 66 | @rm -rf $(DIR_BUILD) 67 | @$(MAKE) $(MAKE_FLAGS) --directory=$(DIR_DOCS) clean 68 | @echo "$(BLUE)[clean]$(NORMAL) Generated files deleted" 69 | 70 | cleandeps: .force 71 | @rm -rf $(DIR_DEPS) 72 | @echo "$(BLUE)[clean]$(NORMAL) Dependency files deleted" 73 | 74 | cleanall: clean cleandeps 75 | 76 | .force: 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MonkOS 2 | ====== 3 | 4 | MonkOS is an experimental 64-bit operating system for Intel and AMD 5 | processors. It is a mix of C and assembly language and is a work in progress. 6 | Currently, it consists of a BIOS boot loader, a virtual console system, an 7 | interrupt handling system, a paged memory manager, a heap allocator, a few 8 | simple device drivers, and a primitive interactive shell. The boot loader is 9 | designed to launch the operating system from a cdrom. 10 | 11 | ## Building 12 | 13 | The OS currently builds under linux using a cross-compiler. There are two 14 | ways to build it: (1) by installing all the necessary tools on your system and 15 | running `make`, or (2) by using a ready-made docker container that contains 16 | all the build tools you'll need. 17 | 18 | ### Building with local tools 19 | 20 | To build with tools installed on your local system, you'll need to have 21 | the following software already installed: 22 | 23 | * gcc x86_64 cross-compiler for elf binaries (I have used versions 4.8, 5.3, 24 | 6.3, 7.3, 8.2, 9.2, 10.2 and 14.2 successfully) 25 | * gnu binutils (I used version 2.34) 26 | * nasm assembler 27 | * genisoimage 28 | * exuberant-ctags (optional, for help with editing) 29 | * gdb (optional, for debugging) 30 | * qemu (optional, for testing) 31 | * doxygen (optional) 32 | 33 | Most of these tools are available from standard linux package managers. The 34 | cross-compiler, however, is not. To build a cross-compiler, consult the 35 | instructions on [this page](http://wiki.osdev.org/GCC_Cross-Compiler). Make 36 | sure to also follow the [libgcc without 37 | red-zone](http://wiki.osdev.org/Libgcc_without_red_zone) instructions. 38 | 39 | Once you've installed all the tools and made sure the cross-compiler is in 40 | your path, run `make`. 41 | 42 | ```bash 43 | $ make 44 | ``` 45 | 46 | This results in a bootable cdrom ISO file called `monk.iso` in your build 47 | subdirectory. 48 | 49 | ### Building with docker-ized tools 50 | 51 | Because it can be a bit of a hassle to build and install a cross-compiler, a 52 | docker container has been prepared, allowing you to avoid building the cross-compiler 53 | yourself. To run the docker-based build, make sure you have a recent 54 | version of docker installed on your system, add yourself to your system's 55 | docker user group, and then type the following: 56 | 57 | ```bash 58 | $ make docker 59 | ``` 60 | 61 | This will pull down the docker container 62 | ([`brett/monkos-build`](https://hub.docker.com/r/brett/monkos-build/)) 63 | if you don't already have it, run the build inside the container, and generate 64 | the iso file (and all other intermediate output files) in your build 65 | subdirectory. It behaves almost exactly as if you ran `make` using a cross- 66 | compiler installed locally on your system. 67 | 68 | ## Running MonkOS 69 | 70 | There are several ways to run MonkOS once you have the iso file. The first and 71 | most time-consuming way is to burn it to a CD or DVD ROM using your favorite 72 | burning utility. This is the only way to test MonkOS on a bare-metal system. 73 | 74 | Alternatively, you can launch the operating system using virtual machine 75 | software like VMware or virtualbox. 76 | 77 | Or you can run the operating system in a linux-based emulator like qemu or 78 | bochs. The MonkOS makefile makes this alternative particularly easy by 79 | providing a simple build rule to launch the OS in qemu: 80 | 81 | ```bash 82 | $ make test 83 | ``` 84 | 85 | You can also use the makefile to start a kernel debugging session under qemu 86 | and gdb. First, launch qemu in debugging mode: 87 | 88 | ```bash 89 | $ make debug 90 | ``` 91 | 92 | Then start a gdb debugger session by attaching gdb to the qemu debugger 93 | endpoint: 94 | 95 | ``` 96 | $ gdb 97 | (gdb) set arch i386:x86-64 98 | (gdb) symbol-file build/monk.sys 99 | (gdb) target remote localhost:8864 100 | (gdb) layout src 101 | ``` 102 | 103 | ## Other build options 104 | 105 | Run `make docs` to build nicely formatted documentation for MonkOS. You'll 106 | need doxygen installed on your system to do this. 107 | 108 | ```bash 109 | $ make docs 110 | ``` 111 | 112 | The doxygen documents will appear in the `docs/monk` subdirectory. To view 113 | them, launch them in your browser (in this case firefox): 114 | 115 | ```bash 116 | $ firefox docs/monk/index.html 117 | ``` 118 | 119 | To build code tags for easy symbol searching within your editor, use the 120 | makefile to run the exuberant-tags utility: 121 | 122 | ```bash 123 | $ make tags 124 | ``` 125 | 126 | This produces a `.tags` file in your MonkOS directory. 127 | 128 | To clean up all intermediate files, use the clean build: 129 | 130 | ```bash 131 | $ make clean 132 | ``` 133 | 134 | To clean all generated dependencies files, use the cleandeps build: 135 | 136 | ```bash 137 | $ make cleandeps 138 | ``` 139 | 140 | ## Documentation 141 | 142 | Please consult the 143 | [Doxygen-formatted documentation](https://beevik.github.io/MonkOS/docs/monk/index.html), 144 | which is part of the [MonkOS documentation set](https://beevik.github.io/MonkOS/). 145 | 146 | ## Resources 147 | 148 | These are some of the resources I have relied on in my attempt to better 149 | understand the numerous and various aspects of bootloader and OS development: 150 | 151 | * [Intel 64 and IA-32 Architectures: Software Developer’s Manual](https://www-ssl.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html) 152 | * [The OsDev wiki](http://wiki.osdev.org/Main_Page) 153 | * [Bare Bones guide](http://wiki.osdev.org/Bare_Bones) 154 | * [Setting up long mode](http://wiki.osdev.org/Setting_Up_Long_Mode) 155 | * [ISO 9660](http://wiki.osdev.org/ISO_9660) 156 | * [PS/2 Keyboard](http://wiki.osdev.org/PS2_Keyboard) 157 | * [PCI](http://wiki.osdev.org/PCI) 158 | * [The System V ABI](http://www.sco.com/developers/gabi/latest/contents.html) 159 | * [AMD64 supplement](https://www.uclibc.org/docs/psABI-x86_64.pdf) 160 | * [The El Torito specification](https://pdos.csail.mit.edu/6.828/2014/readings/boot-cdrom.pdf) 161 | * [Hardware Level VGA and SVGA Video Programming Information Page](http://www.osdever.net/FreeVGA/vga/vgareg.htm) 162 | * [CRT Controller Registers](http://www.osdever.net/FreeVGA/vga/crtcreg.htm) 163 | * [Advanced Programmable Interrupt Controller](http://www.osdever.net/tutorials/view/advanced-programming-interrupt-controller) 164 | * [PCI ID repository](https://pci-ids.ucw.cz/) 165 | * [Write your own operating system](https://web.archive.org/web/20160306003015/http://geezer.osdevbrasil.net/osd/index.htm) 166 | * [PC keyboard](https://web.archive.org/web/20160322061251/http://geezer.osdevbrasil.net/osd/kbd/index.htm) 167 | * Other experimental operating systems 168 | * [The Xeos project](https://github.com/macmade/XEOS) 169 | * [BareMetal OS](https://gitlab.com/ReturnInfinity/BareMetal-OS) 170 | * [IanOS](http://www.ijack.org.uk/) 171 | 172 | ## License 173 | 174 | Use of this source code is governed by a BSD-style license that can be found 175 | in the [LICENSE](https://github.com/beevik/MonkOS/blob/master/LICENSE) file. 176 | -------------------------------------------------------------------------------- /boot/Makefile: -------------------------------------------------------------------------------- 1 | #---------------------------------------------------------------------------- 2 | # MonkOS boot loader makefile 3 | # 4 | # Makefile for the boot loader. 5 | #---------------------------------------------------------------------------- 6 | 7 | DIR_ROOT := .. 8 | 9 | include $(DIR_ROOT)/scripts/config.mk 10 | 11 | DIR_TARGET := $(DIR_BUILD)/boot 12 | DIR_DEPS := $(DIR_DEPS)/boot 13 | ASFLAGS := -f bin 14 | 15 | ASM_FILES := $(wildcard *.asm) 16 | SYS_FILES := $(ASM_FILES:%.asm=$(DIR_BUILD)/%.sys) 17 | DEP_FILES := $(ASM_FILES:%.asm=$(DIR_DEPS)/%.d) 18 | 19 | TAG := $(BLUE)[boot]$(NORMAL) 20 | 21 | all: mkdir $(SYS_FILES) 22 | @echo "$(TAG) $(SUCCESS)" 23 | 24 | mkdir: 25 | @mkdir -p $(DIR_TARGET) $(DIR_DEPS) 26 | 27 | clean: 28 | @rm -f $(SYS_FILES) 29 | 30 | $(SYS_FILES): $(DIR_BUILD)/%.sys: %.asm 31 | @echo "$(TAG) Assembling $<" 32 | @$(AS) $(ASFLAGS) $< -o $@ 33 | 34 | $(DEP_FILES): $(DIR_DEPS)/%.d: %.asm | mkdir 35 | @echo "$(TAG) Generating dependencies for $<" 36 | @set -e; \ 37 | rm -f $@; \ 38 | $(AS) -M $(ASFLAGS) $< -MT @@@ > $@.$$$$; \ 39 | sed 's,@@@[ :]*,$(DIR_BUILD)/$*.sys $@ : ,g' < $@.$$$$ > $@; \ 40 | rm -f $@.$$$$ 41 | 42 | .force: 43 | 44 | -include $(DEP_FILES) 45 | -------------------------------------------------------------------------------- /boot/include/bios.inc: -------------------------------------------------------------------------------- 1 | ;============================================================================= 2 | ; @file bios.inc 3 | ; 4 | ; BIOS constants and structures, usable in real mode only. 5 | ; 6 | ; Copyright 2016 Brett Vickers. 7 | ; Use of this source code is governed by a BSD-style license that can 8 | ; be found in the LICENSE file. 9 | ;============================================================================= 10 | 11 | %ifndef __MONK_BOOT_BIOS_INC__ 12 | %define __MONK_BOOT_BIOS_INC__ 13 | 14 | 15 | ;============================================================================= 16 | ; Disk Address Packet (DAP) 17 | ;============================================================================= 18 | struc BIOS.DAP 19 | 20 | .Bytes: resw 1 ; size of DAP in bytes (16) 21 | .ReadSectors: resw 1 ; number of sectors to read 22 | .TargetBufferOffset: resw 1 ; target buffer address offset 23 | .TargetBufferSegment: resw 1 ; target buffer address segment 24 | .FirstSector: resq 1 ; first sector to read (up to 8 bytes) 25 | 26 | endstruc 27 | 28 | 29 | %endif ; __MONK_BOOT_BIOS_INC__ 30 | -------------------------------------------------------------------------------- /boot/include/gdt.inc: -------------------------------------------------------------------------------- 1 | ;============================================================================= 2 | ; @file gdt.inc 3 | ; 4 | ; Global Descriptor Table and Task State Segment structure definitions 5 | ; for 32-bit and 64-bit modes. 6 | ; 7 | ; Copyright 2016 Brett Vickers. 8 | ; Use of this source code is governed by a BSD-style license that can 9 | ; be found in the LICENSE file. 10 | ;============================================================================= 11 | 12 | %ifndef __MONK_BOOT_GDT_INC__ 13 | %define __MONK_BOOT_GDT_INC__ 14 | 15 | ;============================================================================= 16 | ; Constants 17 | ;============================================================================= 18 | 19 | ; GDT selectors, which should be used in protected mode to set segment 20 | ; register values. See loader_iso.asm for the descriptors referenced by these 21 | ; selectors. 22 | GDT32.Selector.Code32 equ 0x08 ; 32-bit protected mode (code) 23 | GDT32.Selector.Data32 equ 0x10 ; 32-bit protected mode (data) 24 | GDT32.Selector.Code16 equ 0x18 ; 16-bit protected mode (code) 25 | GDT32.Selector.Data16 equ 0x20 ; 16-bit protected mode (data) 26 | 27 | GDT64.Selector.Kernel.Data equ 0x08 ; 64-bit mode (kernel data) 28 | GDT64.Selector.Kernel.Code equ 0x10 ; 64-bit mode (kernel code) 29 | GDT64.Selector.User.Data equ 0x18 ; 64-bit mode (user data) 30 | GDT64.Selector.User.Code equ 0x20 ; 64-bit mode (user code) 31 | GDT64.Selector.TSS equ 0x28 ; 64-bit task state segment 32 | 33 | 34 | ;============================================================================= 35 | ; GDT 36 | ; 37 | ; Each GDT entry is an 8-byte structure organized as follows: 38 | ; 39 | ; 31 16 15 0 40 | ; +-----------------------+-----------------------+ 41 | ; | Base | Limit | 42 | ; | 0:15 | 0:15 | 43 | ; +-------+-------+-------+-----------+-----------+ 44 | ; | Base | Flags | Limit | Access | Base | 45 | ; | 24:31 | | 16:19 | | 16:23 | 46 | ; +-------+-------+-------+-----------+-----------+ 47 | ; 48 | ; Bits 49 | ; [0:15] Limit bits [0:15] 50 | ; [16:31] Base address bits [0:15] 51 | ; [32:39] Base address bits [16:23] 52 | ; 40 Access: AC (Accessed) bit 53 | ; 41 Access: RW (Readable/Writable) bit 54 | ; 42 Access: DC (Direction/Conforming) bit 55 | ; 43 Access: EX (Executable) bit 56 | ; 44 Access: must be 1 57 | ; [45:46] Access: Privilege ring (0 = highest, 3 = lowest) 58 | ; 47 Access: PR (Present) bit 59 | ; [48:51] Limit bits [16:19] 60 | ; 52 Flags: must be 0 61 | ; 53 Flags: LM (Long Mode) 62 | ; 54 Flags: SZ (Size) bit (0=16-bit, 1=32-bit) 63 | ; 55 Flags: GR (Granularity) (0=1B, 1=4KiB limit granularity) 64 | ; [56:63] Base address bits [24:31] 65 | ; 66 | ;============================================================================= 67 | struc GDT.Descriptor 68 | 69 | .LimitLow: resw 1 70 | .BaseLow: resw 1 71 | .BaseMiddle: resb 1 72 | .Access: resb 1 73 | .LimitHighFlags: resb 1 ; LimitHigh (4 bits) + Flags (4 bits) 74 | .BaseHigh: resb 1 75 | 76 | endstruc 77 | 78 | 79 | ;============================================================================= 80 | ; 64-bit TSS Descriptor 81 | ; 82 | ; Similar layout to GDT.Descriptor, but with two additional dwords, one of 83 | ; which is used to extend the base address. 84 | ;============================================================================= 85 | struc TSS64.Descriptor 86 | 87 | .LimitLow: resw 1 88 | .BaseLow: resw 1 89 | .BaseMiddle: resb 1 90 | .Access: resb 1 91 | .LimitHighFlags: resb 1 ; LimitHigh (4 bits) + Flags (4 bits) 92 | .BaseHigh: resb 1 93 | .BaseHighest: resd 1 94 | .Reserved: resd 1 95 | 96 | endstruc 97 | 98 | 99 | ;============================================================================= 100 | ; 64-bit TSS 101 | ; 102 | ; The 64-bit Task State Segment data structure. 103 | ; 104 | ; Contains information used during privilege mode changes. 105 | ;============================================================================= 106 | struc TSS64 107 | 108 | resd 1 ; Reserved 109 | .RSP0: resq 1 ; Stack pointer (priv 0) 110 | .RSP1: resq 1 ; ... 111 | .RSP2: resq 1 112 | resq 1 ; Reserved 113 | .IST1: resq 1 ; Interrupt Stack Table pointer 1 114 | .IST2: resq 1 ; ... 115 | .IST3: resq 1 116 | .IST4: resq 1 117 | .IST5: resq 1 118 | .IST6: resq 1 119 | .IST7: resq 1 120 | resq 1 ; Reserved 121 | resw 1 ; Reserved 122 | .IOPB: resw 1 ; I/O permission bitmap offset 123 | 124 | endstruc 125 | 126 | %endif ; __MONK_BOOT_GDT_INC__ 127 | -------------------------------------------------------------------------------- /boot/include/globals.inc: -------------------------------------------------------------------------------- 1 | ;============================================================================= 2 | ; @file globals.inc 3 | ; 4 | ; Global variables used by boot loaders. 5 | ; 6 | ; Copyright 2016 Brett Vickers. 7 | ; Use of this source code is governed by a BSD-style license that can 8 | ; be found in the LICENSE file. 9 | ;============================================================================= 10 | 11 | %ifndef __MONK_BOOT_GLOBALS_INC__ 12 | %define __MONK_BOOT_GLOBALS_INC__ 13 | 14 | %include "include/mem.inc" 15 | 16 | ;============================================================================= 17 | ; Global variables (stored at Mem.Boot.Globals) 18 | ;============================================================================= 19 | struc Globals, Mem.Globals 20 | .DriveNumber resd 1 21 | .RootDirectorySector resw 1 22 | .KernelSector resw 1 23 | .KernelSize resd 1 24 | .CPUFeatureBitsECX resd 1 25 | .CPUFeatureBitsEDX resd 1 26 | endstruc 27 | 28 | %endif ; __MONK_BOOT_GLOBALS_INC__ 29 | -------------------------------------------------------------------------------- /boot/include/iso9660.inc: -------------------------------------------------------------------------------- 1 | ;============================================================================= 2 | ; @file iso9660.inc 3 | ; 4 | ; ISO9660 constants and structures. 5 | ; 6 | ; Copyright 2016 Brett Vickers. 7 | ; Use of this source code is governed by a BSD-style license that can 8 | ; be found in the LICENSE file. 9 | ;============================================================================= 10 | 11 | %ifndef __MONK_BOOT_ISO9660_INC__ 12 | %define __MONK_BOOT_ISO9660_INC__ 13 | 14 | 15 | ;============================================================================= 16 | ; PrimaryVolumeDescriptor 17 | ;============================================================================= 18 | struc ISO.PrimaryVolumeDescriptor 19 | 20 | .Type resb 1 21 | .Identifier resb 5 22 | .Version resb 1 23 | resb 1 ; unused 24 | .SystemId resb 32 25 | .VolumeId resb 32 26 | resb 8 ; unused 27 | .VolumeSpaceSize resq 1 ; int32 LSB-MSB 28 | resb 32 ; unused 29 | .VolumeSetSize resd 1 ; int16 LSB-MSB 30 | .VolumeSeqNum resd 1 ; int16 LSB-MSB 31 | .BlockSize resd 1 ; int16 LSB-MSB 32 | .PathTableSize resq 1 ; int32 LSB-MSB 33 | .PathTableLBA resd 1 ; int32 LSB 34 | .PathTableLBAOpt resd 1 ; int32 LSB 35 | .MpathTableLBA resd 1 ; int32 MSB 36 | .MpathTableLBAOpt resd 1 ; int32 MSB 37 | .RootDirEntry resb 34 ; contains an ISO.DirectoryEntry 38 | .VolumeSetId resb 128 39 | .Publisher resb 128 40 | .DataPreparer resb 128 41 | .AppId resb 128 42 | .Copyright resb 38 43 | .AbstractFileId resb 36 44 | .BiblioFileId resb 37 45 | .CreationDateTime resb 17 46 | .ModifyDateTime resb 17 47 | .ExpireDateTime resb 17 48 | .EffectiveDateTime resb 17 49 | .FileStructVersion resb 1 50 | resb 1 ; unused 51 | .AppData resb 512 52 | .Reserved resb 653 53 | 54 | endstruc 55 | 56 | 57 | ;============================================================================= 58 | ; DirectoryEntry 59 | ;============================================================================= 60 | struc ISO.DirectoryEntry 61 | 62 | .RecordLength resb 1 63 | .ExtAttribLength resb 1 64 | .LocationLBA resq 1 ; int32 LSB-MSB 65 | .Size resq 1 ; int32 LSB-MSB 66 | .RecordingDateTime resb 7 67 | .FileFlags resb 1 68 | .FileUnitSize resb 1 69 | .GapSize resb 1 70 | .Volume resd 1 ; int16 LSB-MSB 71 | .NameLength resb 1 72 | .Name: ; variable length 73 | 74 | endstruc 75 | 76 | %endif ; __MONK_BOOT_ISO9660_INC__ 77 | -------------------------------------------------------------------------------- /boot/include/mem.inc: -------------------------------------------------------------------------------- 1 | ;============================================================================= 2 | ; @file mem.inc 3 | ; 4 | ; Memory layout constants and macros used by boot loader code. 5 | ; 6 | ; Copyright 2016 Brett Vickers. 7 | ; Use of this source code is governed by a BSD-style license that can 8 | ; be found in the LICENSE file. 9 | ;============================================================================= 10 | 11 | %ifndef __MONK_BOOT_MEM_INC__ 12 | %define __MONK_BOOT_MEM_INC__ 13 | 14 | ;============================================================================= 15 | ; Memory layout 16 | ;============================================================================= 17 | 18 | Mem.BIOS.IVT equ 0x00000000 19 | Mem.BIOS.Data equ 0x00000400 20 | Mem.Sector.Buffer equ 0x00000800 21 | Mem.GDT equ 0x00003000 22 | Mem.TSS64 equ 0x00003100 23 | Mem.Globals equ 0x00003200 24 | Mem.Stack.Bottom equ 0x00004000 25 | Mem.Stack.Top equ 0x00007c00 26 | Mem.Loader1 equ 0x00007c00 27 | Mem.Loader2 equ 0x00008000 28 | Mem.PageTable equ 0x00010000 29 | Mem.PageTable.PML4T equ 0x00010000 30 | Mem.PageTable.PDPT equ 0x00011000 31 | Mem.PageTable.PDT equ 0x00012000 ; maps first 10MiB 32 | Mem.PageTable.PT equ 0x00013000 ; maps first 2MiB 33 | Mem.PageTable.End equ 0x00020000 34 | Mem.Stack32.Temp.Bottom equ 0x0006f000 35 | Mem.Stack32.Temp.Top equ 0x00070000 36 | Mem.Table equ 0x00070000 ; BIOS-derived layout 37 | Mem.Kernel.LoadBuffer equ 0x00070000 38 | Mem.Kernel.Stack.NMI.Bottom equ 0x0008a000 ; NMI stack 39 | Mem.Kernel.Stack.NMI.Top equ 0x0008c000 40 | Mem.Kernel.Stack.DF.Bottom equ 0x0008c000 ; double-fault stack 41 | Mem.Kernel.Stack.DF.Top equ 0x0008e000 42 | Mem.Kernel.Stack.MC.Bottom equ 0x0008e000 ; machine-check stack 43 | Mem.Kernel.Stack.MC.Top equ 0x00090000 44 | Mem.BIOS.EBDA equ 0x0009e000 45 | Mem.Video equ 0x000a0000 46 | Mem.Kernel.Stack.Interrupt.Bottom equ 0x00100000 ; PL-change intr stack 47 | Mem.Kernel.Stack.Interrupt.Top equ 0x001ff000 48 | Mem.Kernel.Stack.Bottom equ 0x00200000 ; main kernel stack 49 | Mem.Kernel.Stack.Top equ 0x00300000 50 | Mem.Kernel.Image equ 0x00300000 51 | Mem.Kernel.Code equ 0x00301000 52 | 53 | ; Layout region sizes 54 | Mem.BIOS.IVT.Size equ 0x00000400 55 | Mem.BIOS.Data.Size equ 0x00000100 56 | Mem.Loader1.Size equ 0x00000200 57 | Mem.Loader2.Size equ 0x00008000 58 | Mem.Sector.Buffer.Size equ 0x00000800 59 | Mem.Table.Size equ 0x00006000 ; Up to 1023 regions 60 | Mem.Kernel.LoadBuffer.Size equ 0x00010000 61 | 62 | ; Real mode segment addresses 63 | Mem.Loader1.Segment equ Mem.Loader1 >> 4 64 | Mem.Loader2.Segment equ Mem.Loader2 >> 4 65 | 66 | %endif ; __MONK_BOOT_MEM_INC__ 67 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # monk OS build tools 2 | 3 | FROM brett/gcc-cross-x86_64-elf 4 | LABEL org.opencontainers.image.authors="Brett Vickers" 5 | 6 | RUN set -x \ 7 | && apt-get update \ 8 | && apt-get install -y git nasm genisoimage \ 9 | && mkdir -p /code 10 | 11 | VOLUME /code 12 | 13 | CMD ["/bin/bash"] 14 | -------------------------------------------------------------------------------- /docker/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | args=$@ 4 | 5 | uid=$(id -u) 6 | gid=$(id -g) 7 | code=$(realpath .) 8 | 9 | docker run --rm -it \ 10 | -v ${code}:/code \ 11 | -u ${uid}:${gid} \ 12 | brett/monkos-build /bin/bash -c "cd /code && make ${args}" 13 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | #---------------------------------------------------------------------------- 2 | # MonkOS documentation makefile 3 | # 4 | # Makefile for the doxygen documentation. 5 | #---------------------------------------------------------------------------- 6 | 7 | DIR_ROOT := .. 8 | 9 | include $(DIR_ROOT)/scripts/config.mk 10 | 11 | 12 | #---------------------------------------------------------------------------- 13 | # Build targets 14 | #---------------------------------------------------------------------------- 15 | 16 | CONFIG := $(DIR_DOCS)/monk.doxy 17 | 18 | TAG := $(BLUE)[docs]$(NORMAL) 19 | 20 | docs: .force $(CONFIG) 21 | @echo "$(TAG) Running doxygen" 22 | @$(DOXYGEN) $(CONFIG) > /dev/null 23 | @echo "$(BLUE)[docs] $(SUCCESS)" 24 | 25 | clean: 26 | @rm -rf $(DIR_DOCS)/monk 27 | 28 | .force: 29 | -------------------------------------------------------------------------------- /include/core.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file core.h 3 | /// @brief Core include file. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | // Standard headers included by all code in the project. 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | //---------------------------------------------------------------------------- 19 | // Macros 20 | //---------------------------------------------------------------------------- 21 | 22 | /// Force a function to be inline, even in debug mode. 23 | #define __forceinline inline __attribute__((always_inline)) 24 | 25 | /// Return the number of elements in the C array. 26 | #define arrsize(x) ((int)(sizeof(x) / sizeof(x[0]))) 27 | 28 | /// Generic min/max routines 29 | #define min(a, b) ((a) < (b) ? (a) : (b)) 30 | #define max(a, b) ((a) > (b) ? (a) : (b)) 31 | 32 | // Division macros, rounding (a/b) up or down 33 | #define div_dn(a, b) ((a) / (b)) 34 | #define div_up(a, b) (((a) + (b) - 1) / (b)) 35 | 36 | // Alignment macros, align a to the nearest multiple of b. 37 | #define align_dn(a, b) (div_dn(a, b) * (b)) 38 | #define align_up(a, b) (div_up(a, b) * (b)) 39 | 40 | // Typed pointer arithmetic. Pointer p +/- offset o, cast to t*. 41 | #define ptr_add(t, p, o) ((t *)((uint8_t *)(p) + (o))) 42 | #define ptr_sub(t, p, o) ((t *)((uint8_t *)(p) - (o))) 43 | 44 | /// Compile-time static assertion 45 | #define STATIC_ASSERT(a, b) _Static_assert(a, b) 46 | 47 | /// Forced structure packing (use only when absolutely necessary) 48 | #define PACKSTRUCT __attribute__((packed, aligned(1))) 49 | -------------------------------------------------------------------------------- /include/kernel/debug/dump.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file dump.h 3 | /// @brief Debugging memory and CPU state dump routines. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license 7 | // that can be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | 15 | //---------------------------------------------------------------------------- 16 | // @function dump_registers 17 | /// @brief Dump the contents of a CPU registers structure to a string 18 | /// buffer. 19 | /// @param[in] buf Pointer to the character buffer to hold the resulting 20 | /// null-terminated string. 21 | /// @param[in] bufsize Size of the buffer used to hold the string. 22 | /// @param[in] regs Pointer to a CPU registers structure. 23 | /// @returns The number of characters that would have been written to the 24 | /// buffer if n had been sufficiently large. 25 | //---------------------------------------------------------------------------- 26 | int 27 | dump_registers(char *buf, size_t bufsize, const registers_t *regs); 28 | 29 | //---------------------------------------------------------------------------- 30 | // @function dump_cpuflags 31 | /// @brief Dump the contents of the CPU flags register. 32 | /// @param[in] buf Pointer to the character buffer to hold the resulting 33 | /// null-terminated string. 34 | /// @param[in] bufsize Size of the buffer used to hold the string. 35 | /// @param[in] rflags The 64-bit rflags register value. 36 | /// @returns The number of characters that would have been written to the 37 | /// buffer if n had been sufficiently large. 38 | //---------------------------------------------------------------------------- 39 | int 40 | dump_cpuflags(char *buf, size_t bufsize, uint64_t rflags); 41 | 42 | //---------------------------------------------------------------------------- 43 | // @enum dumpstyle 44 | /// @brief Memory dump output style. 45 | /// @details Used to select how the memory address is displayed in a 46 | /// a memory dump. 47 | //---------------------------------------------------------------------------- 48 | enum dumpstyle 49 | { 50 | DUMPSTYLE_NOADDR = 0, ///< No address or offset in memory dump. 51 | DUMPSTYLE_ADDR = 1, ///< Include full address in memory dump. 52 | DUMPSTYLE_OFFSET = 2, ///< Include address offset in memory dump. 53 | }; 54 | 55 | //---------------------------------------------------------------------------- 56 | // @function dump_memory 57 | /// @brief Dump the contents of memory. 58 | /// @param[in] buf Pointer to the character buffer to hold the resulting 59 | /// null-terminated string. 60 | /// @param[in] bufsize Size of the buffer used to hold the string. 61 | /// @param[in] mem Pointer to the first byte of memory to dump. 62 | /// @param[in] memsize Number of memory bytes to dump. 63 | /// @param[in] style The output style used for the memory dump. 64 | /// @returns The number of characters that would have been written to the 65 | /// buffer if n had been sufficiently large. 66 | //---------------------------------------------------------------------------- 67 | int 68 | dump_memory(char *buf, size_t bufsize, const void *mem, size_t memsize, 69 | enum dumpstyle style); 70 | -------------------------------------------------------------------------------- /include/kernel/debug/log.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file log.c 3 | /// @brief Kernel logging module. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license 7 | // that can be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | 15 | //---------------------------------------------------------------------------- 16 | // @enum loglevel_t 17 | /// @brief A log level indicates the importance of a logged message. 18 | //---------------------------------------------------------------------------- 19 | typedef enum loglevel 20 | { 21 | LOG_CRIT, ///< Critical error, occurs just prior to crashing 22 | LOG_ERR, ///< Serious error in software or hardware 23 | LOG_WARNING, ///< Warning about a significant issue 24 | LOG_INFO, ///< Informational message 25 | LOG_DEBUG, ///< Used for kernel debugging 26 | LOG_DEFAULT, ///< Default kernel logging level 27 | } loglevel_t; 28 | 29 | //---------------------------------------------------------------------------- 30 | // @typedef log_callback 31 | /// @brief Callback handler called when a message is logged. 32 | /// @param[in] level The log level of the message. 33 | /// @param[in] msg The null-terminated string containing the logged 34 | /// message. 35 | //---------------------------------------------------------------------------- 36 | typedef void (*log_callback)(loglevel_t level, const char *msg); 37 | 38 | //---------------------------------------------------------------------------- 39 | // @function log_addcallback 40 | /// @brief Add a callback handler for log messages. 41 | /// @param[in] maxlevel Issue callback for log calls up to and including 42 | /// this log level. 43 | /// @param[in] cb The callback function 44 | //---------------------------------------------------------------------------- 45 | void 46 | log_addcallback(loglevel_t maxlevel, log_callback cb); 47 | 48 | //---------------------------------------------------------------------------- 49 | // @function log_removecallback 50 | /// @brief Remove a callback handler for log messages. 51 | /// @param[in] cb The callback function 52 | //---------------------------------------------------------------------------- 53 | void 54 | log_removecallback(log_callback cb); 55 | 56 | //---------------------------------------------------------------------------- 57 | // @function log 58 | /// @brief Log a message to the kernel log buffer. 59 | /// @param[in] level The importance level of the logged message. 60 | /// @param[in] str The null-terminated string to be printed. 61 | //---------------------------------------------------------------------------- 62 | void 63 | log(loglevel_t level, const char *str); 64 | 65 | //---------------------------------------------------------------------------- 66 | // @function logf 67 | /// @brief Log a printf-formatted message to the kernel log buffer. 68 | /// @param[in] level The importance level of the logged message. 69 | /// @param[in] format The null-terminated format string used to format the 70 | /// text to be printed. 71 | /// @param[in] ... Variable arguments list to be initialized with 72 | /// va_start. 73 | //---------------------------------------------------------------------------- 74 | void 75 | logf(loglevel_t level, const char *format, ...); 76 | 77 | //---------------------------------------------------------------------------- 78 | // @function logvf 79 | /// @brief Log a printf-formatted string using a variable argument list. 80 | /// @param[in] level The importance level of the logged message. 81 | /// @param[in] format The null-terminated format string used to format the 82 | /// text to be printed. 83 | /// @param[in] args Variable arguments list to be initialized with 84 | /// va_start. 85 | //---------------------------------------------------------------------------- 86 | void 87 | logvf(loglevel_t level, const char *format, va_list args); 88 | -------------------------------------------------------------------------------- /include/kernel/device/keyboard.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file keyboard.h 3 | /// @brief Keyboard input routines. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license 7 | // that can be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | //---------------------------------------------------------------------------- 15 | // Constants 16 | //---------------------------------------------------------------------------- 17 | 18 | // Keyboard break codes 19 | #define KEYBRK_DOWN 0 ///< Key-down break code 20 | #define KEYBRK_UP 1 ///< Key-up break code 21 | 22 | // Meta-key bit masks 23 | #define META_SHIFT (1 << 0) ///< Set while the shift key is pressed. 24 | #define META_CTRL (1 << 1) ///< Set while the ctrl key is pressed. 25 | #define META_ALT (1 << 2) ///< Set while the alt key is pressed. 26 | #define META_ESCAPED (1 << 3) ///< Set if key's scan code is escaped. 27 | #define META_CAPSLOCK (1 << 4) ///< Set while caps lock is on. 28 | #define META_NUMLOCK (1 << 5) ///< Set while num lock is on. 29 | #define META_SCRLOCK (1 << 6) ///< Set while scroll lock is on. 30 | 31 | //---------------------------------------------------------------------------- 32 | // @enum keycode_t 33 | /// @brief Key code values representing individual keys on the keyboard. 34 | /// @details Key codes corresponding to printable characters are not 35 | /// listed here, but they are equal in value to their lowercase 36 | /// ASCII representations (e.g., KEY_A = 'a', KEY_1 = '1', etc.). 37 | //---------------------------------------------------------------------------- 38 | enum keycode 39 | { 40 | KEY_BACKSPACE = 0x08, 41 | KEY_TAB = 0x09, 42 | KEY_ENTER = 0x0d, 43 | KEY_ESCAPE = 0x1b, 44 | KEY_CTRL = 0x81, 45 | KEY_SHIFT = 0x82, 46 | KEY_ALT = 0x83, 47 | KEY_PRTSCR = 0x90, 48 | KEY_CAPSLOCK = 0x91, 49 | KEY_NUMLOCK = 0x92, 50 | KEY_SCRLOCK = 0x93, 51 | KEY_INSERT = 0xa0, 52 | KEY_END = 0xa1, 53 | KEY_DOWN = 0xa2, 54 | KEY_PGDN = 0xa3, 55 | KEY_LEFT = 0xa4, 56 | KEY_CENTER = 0xa5, ///< Keypad Center 57 | KEY_RIGHT = 0xa6, 58 | KEY_HOME = 0xa7, 59 | KEY_UP = 0xa8, 60 | KEY_PGUP = 0xa9, 61 | KEY_DEL = 0xaa, 62 | KEY_MINUS = 0xab, ///< Keypad Minus 63 | KEY_PLUS = 0xac, ///< Keypad Plus 64 | KEY_F1 = 0xb0, 65 | KEY_F2 = 0xb1, 66 | KEY_F3 = 0xb2, 67 | KEY_F4 = 0xb3, 68 | KEY_F5 = 0xb4, 69 | KEY_F6 = 0xb5, 70 | KEY_F7 = 0xb6, 71 | KEY_F8 = 0xb7, 72 | KEY_F9 = 0xb8, 73 | KEY_F10 = 0xb9, 74 | KEY_F11 = 0xba, 75 | KEY_F12 = 0xbb, 76 | KEY_SCANESC = 0xfe, ///< Escaped scan code 77 | KEY_INVALID = 0xff, ///< Invalid scan code 78 | }; 79 | 80 | typedef enum keycode keycode_t; 81 | 82 | //---------------------------------------------------------------------------- 83 | // @struct key_t 84 | /// @brief A record representing the state of the keyboard at the time 85 | /// a key was presssed or unpressed. 86 | //---------------------------------------------------------------------------- 87 | struct key 88 | { 89 | uint8_t brk; ///< KEYBRK_UP or KEYBRK_DOWN 90 | uint8_t meta; ///< Metakey mask when key was generated. 91 | uint8_t code; ///< Keycode value (type = keycode_t). 92 | uint8_t ch; ///< Character value, if valid. 93 | }; 94 | 95 | typedef struct key key_t; 96 | 97 | //---------------------------------------------------------------------------- 98 | // @struct keylayout_t 99 | /// @brief A map of keyboard scan codes to key codes. 100 | //---------------------------------------------------------------------------- 101 | struct keylayout 102 | { 103 | uint8_t shifted[128]; ///< when shift key is down. 104 | uint8_t unshifted[128]; ///< when shift key is up. 105 | }; 106 | 107 | typedef struct keylayout keylayout_t; 108 | 109 | //---------------------------------------------------------------------------- 110 | // @function kb_init 111 | /// @brief Initialize the keyboard so that it can provide input to the 112 | /// kernel. 113 | /// @details kb_init installs the default US English PS/2 keyboard layout. 114 | //---------------------------------------------------------------------------- 115 | void 116 | kb_init(); 117 | 118 | //---------------------------------------------------------------------------- 119 | // @function kb_setlayout 120 | /// @brief Install a new keyboard layout. 121 | /// @param[in] layout The keyboard layout to install. 122 | //---------------------------------------------------------------------------- 123 | void 124 | kb_setlayout(keylayout_t *layout); 125 | 126 | //---------------------------------------------------------------------------- 127 | // @function kb_getchar 128 | /// @brief Return the next available character from the keyboard's 129 | /// input buffer. 130 | /// @returns The ascii value of the next character in the input buffer, 131 | /// or 0 if there are no characters available. 132 | //---------------------------------------------------------------------------- 133 | char 134 | kb_getchar(); 135 | 136 | //---------------------------------------------------------------------------- 137 | // @function kb_getkey 138 | /// @brief Return the available next key from the keyboard's input 139 | /// buffer. 140 | /// @param[out] key The key record of the next key in the buffer. 141 | /// @returns true if there is a key in the buffer, false otherwise. 142 | //---------------------------------------------------------------------------- 143 | bool 144 | kb_getkey(key_t *key); 145 | 146 | //---------------------------------------------------------------------------- 147 | // @function kb_meta 148 | /// @brief Return the current meta-key bit mask. 149 | /// @returns The meta-key bitmask. 150 | //---------------------------------------------------------------------------- 151 | uint8_t 152 | kb_meta(); 153 | -------------------------------------------------------------------------------- /include/kernel/device/pci.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file pci.h 3 | /// @brief PCI controller. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | void 15 | pci_init(); 16 | -------------------------------------------------------------------------------- /include/kernel/device/timer.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file timer.h 3 | /// @brief Programmable interval timer (8253/8254) controller. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license 7 | // that can be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | //---------------------------------------------------------------------------- 15 | // @function timer_init 16 | /// @brief Initialize the timer controller so that it interrupts the 17 | /// kernel at the requested frequency. 18 | /// 19 | /// @details Interrupts are enabled at the end of the function, so 20 | /// timer_enable does not need to be called after timer_init. 21 | /// 22 | /// Due to the clock granularity (1193181Hz), the requested 23 | /// frequency may not be perfectly met. 24 | /// 25 | /// @param[in] frequency The interrupt frequency in Hz. Clamped to the 26 | /// range [19:1193181]. 27 | //---------------------------------------------------------------------------- 28 | void 29 | timer_init(uint32_t frequency); 30 | 31 | //---------------------------------------------------------------------------- 32 | // @function timer_enable 33 | /// @brief Enable timer interrupts. 34 | //---------------------------------------------------------------------------- 35 | void 36 | timer_enable(); 37 | 38 | //---------------------------------------------------------------------------- 39 | // @function timer_disable 40 | /// @brief Disable timer interrupts. 41 | //---------------------------------------------------------------------------- 42 | void 43 | timer_disable(); 44 | -------------------------------------------------------------------------------- /include/kernel/device/tty.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file tty.h 3 | /// @brief Teletype (console) screen text manipulation routines. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | 15 | //---------------------------------------------------------------------------- 16 | // Constants 17 | //---------------------------------------------------------------------------- 18 | 19 | /// The number of available virtual consoles. 20 | #define MAX_TTYS 4 21 | 22 | //---------------------------------------------------------------------------- 23 | // @enum textcolor_t 24 | /// @brief Color values used for tty text. 25 | //---------------------------------------------------------------------------- 26 | enum textcolor 27 | { 28 | TEXTCOLOR_BLACK = 0, 29 | TEXTCOLOR_BLUE = 1, 30 | TEXTCOLOR_GREEN = 2, 31 | TEXTCOLOR_CYAN = 3, 32 | TEXTCOLOR_RED = 4, 33 | TEXTCOLOR_MAGENTA = 5, 34 | TEXTCOLOR_BROWN = 6, 35 | TEXTCOLOR_LTGRAY = 7, 36 | TEXTCOLOR_GRAY = 8, 37 | TEXTCOLOR_LTBLUE = 9, 38 | TEXTCOLOR_LTGREEN = 10, 39 | TEXTCOLOR_LTCYAN = 11, 40 | TEXTCOLOR_LTRED = 12, 41 | TEXTCOLOR_LTMAGENTA = 13, 42 | TEXTCOLOR_YELLOW = 14, 43 | TEXTCOLOR_WHITE = 15, 44 | }; 45 | 46 | typedef enum textcolor textcolor_t; 47 | 48 | //---------------------------------------------------------------------------- 49 | // @struct screenpos_t 50 | /// @brief tty screen text position. 51 | //---------------------------------------------------------------------------- 52 | struct screenpos 53 | { 54 | uint8_t x; ///< x position in range [0:79]. 55 | uint8_t y; ///< y position in range [0:24]. 56 | }; 57 | 58 | typedef struct screenpos screenpos_t; 59 | 60 | //---------------------------------------------------------------------------- 61 | // @function tty_init 62 | /// @brief Initialize all virtual consoles. 63 | /// @details This function must be called before any other console 64 | /// functions can be used. 65 | //---------------------------------------------------------------------------- 66 | void 67 | tty_init(); 68 | 69 | //---------------------------------------------------------------------------- 70 | // @function tty_activate 71 | /// @brief Activate the requested virtual console. 72 | /// @details The virtual console's buffer is immediately displayed on the 73 | /// screen. 74 | /// @param[in] id Virtual tty id (0-3). 75 | //---------------------------------------------------------------------------- 76 | void 77 | tty_activate(int id); 78 | 79 | //---------------------------------------------------------------------------- 80 | // @function tty_set_textcolor 81 | /// @brief Set the foreground and background colors used to display 82 | /// text on the virtual console. 83 | /// @param[in] id Virtual tty id (0-3). 84 | /// @param[in] fg Foreground color. 85 | /// @param[in] bg Background color. 86 | //---------------------------------------------------------------------------- 87 | void 88 | tty_set_textcolor(int id, textcolor_t fg, textcolor_t bg); 89 | 90 | //---------------------------------------------------------------------------- 91 | // @function tty_set_textcolor_fg 92 | /// @brief Set the foreground color used to display text on the virtual 93 | /// console. 94 | /// @param[in] id Virtual tty id (0-3). 95 | /// @param[in] fg Foreground color. 96 | //---------------------------------------------------------------------------- 97 | void 98 | tty_set_textcolor_fg(int id, textcolor_t fg); 99 | 100 | //---------------------------------------------------------------------------- 101 | // @function tty_set_textcolor_bg 102 | /// @brief Set the background color used to display text on the virtual 103 | /// console. 104 | /// @param[in] id Virtual tty id (0-3). 105 | /// @param[in] bg Background color. 106 | //---------------------------------------------------------------------------- 107 | void 108 | tty_set_textcolor_bg(int id, textcolor_t bg); 109 | 110 | //---------------------------------------------------------------------------- 111 | // @function tty_get_textcolor_fg 112 | /// @brief Get the foreground color used to display text on the virtual 113 | /// console. 114 | /// @param[in] id Virtual tty id (0-3). 115 | /// @returns Foreground color. 116 | //---------------------------------------------------------------------------- 117 | textcolor_t 118 | tty_get_textcolor_fg(int id); 119 | 120 | //---------------------------------------------------------------------------- 121 | // @function tty_get_textcolor_bg 122 | /// @brief Get the background color used to display text on the virtual 123 | /// console. 124 | /// @param[in] id Virtual tty id (0-3). 125 | /// @returns Background color. 126 | //---------------------------------------------------------------------------- 127 | textcolor_t 128 | tty_get_textcolor_bg(int id); 129 | 130 | //---------------------------------------------------------------------------- 131 | // @function tty_clear 132 | /// @brief Clear the virtual console screen's contents using the current 133 | /// text background color. 134 | /// @param[in] id Virtual tty id (0-3). 135 | //---------------------------------------------------------------------------- 136 | void 137 | tty_clear(int id); 138 | 139 | //---------------------------------------------------------------------------- 140 | // @function tty_setpos 141 | /// @brief Set the position of the cursor on the virtual console. 142 | /// @details Text written to the console after this function will be 143 | /// located at the requested screen position. 144 | /// @param[in] id Virtual tty id (0-3). 145 | /// @param[in] pos The screen position of the cursor. 146 | //---------------------------------------------------------------------------- 147 | void 148 | tty_setpos(int id, screenpos_t pos); 149 | 150 | //---------------------------------------------------------------------------- 151 | // @function tty_getpos 152 | /// @brief Get the current position of the cursor on the virtual console. 153 | /// @param[in] id Virtual tty id (0-3). 154 | /// @param[out] pos A pointer to a screenpos_t to receive the position. 155 | //---------------------------------------------------------------------------- 156 | void 157 | tty_getpos(int id, screenpos_t *pos); 158 | 159 | //---------------------------------------------------------------------------- 160 | // @function tty_print 161 | /// @brief Output a null-terminated string to the virtual console using 162 | /// the console's current text color and screen position. 163 | /// 164 | /// @details A newline character (\\n) causes the screen position to 165 | /// be updated as though a carriage return and line feed were 166 | /// performed. 167 | /// 168 | /// To change the foreground color on the fly without having to 169 | /// call a console function, you may use the escape sequence 170 | /// \033[x], where x is a single character representing the 171 | /// foreground color to use for all following text. If x is a 172 | /// hexadecimal digit, then it represents one of the 16 textcolor 173 | /// codes. If x is '-', then it represents the console's 174 | /// original foreground color setting. 175 | /// 176 | /// To change the background color on the fly, use the escape 177 | /// sequence \033{x}. The meaning of x is the same as with 178 | /// the foreground color escape sequence. 179 | /// @param[in] id Virtual tty id (0-3). 180 | /// @param[in] str The null-terminated string to be printed. 181 | //---------------------------------------------------------------------------- 182 | void 183 | tty_print(int id, const char *str); 184 | 185 | //---------------------------------------------------------------------------- 186 | // @function tty_printc 187 | /// @brief Output a single character to the virtual console using 188 | /// the console's current text color and screen position. 189 | /// @details See tty_print for further details. 190 | /// @param[in] id Virtual tty id (0-3). 191 | /// @param[in] ch The character to be printed. 192 | //---------------------------------------------------------------------------- 193 | void 194 | tty_printc(int id, char ch); 195 | 196 | //---------------------------------------------------------------------------- 197 | // @function tty_printf 198 | /// @brief Output a printf-formatted string to the virtual console using 199 | /// the console's current text color and screen position. 200 | /// @details See tty_print for further details. 201 | /// @param[in] id Virtual tty id (0-3). 202 | /// @param[in] format The null-terminated format string used to format the 203 | /// text to be printed. 204 | /// @param[in] ... Variable arguments list to be initialized with 205 | /// va_start. 206 | /// @returns The number of characters written to the console. 207 | //---------------------------------------------------------------------------- 208 | int 209 | tty_printf(int id, const char *format, ...); 210 | -------------------------------------------------------------------------------- /include/kernel/interrupt/exception.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file exception.h 3 | /// @brief CPU exceptions. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license 7 | // that can be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | // CPU exception constants 15 | #define EXCEPTION_DIVBYZERO 0x00 16 | #define EXCEPTION_DEBUG 0x01 17 | #define EXCEPTION_NMI 0x02 18 | #define EXCEPTION_BREAKPOINT 0x03 19 | #define EXCEPTION_OVERFLOW 0x04 20 | #define EXCEPTION_BOUNDS 0x05 21 | #define EXCEPTION_INVALID_OPCODE 0x06 22 | #define EXCEPTION_NO_DEVICE 0x07 23 | #define EXCEPTION_DOUBLE_FAULT 0x08 24 | #define EXCEPTION_COPROCESSOR 0x09 25 | #define EXCEPTION_INVALID_TSS 0x0a 26 | #define EXCEPTION_SEGMENT_NOT_PRESENT 0x0b 27 | #define EXCEPTION_STACK_FAULT 0x0c 28 | #define EXCEPTION_GENERAL_PROTECTION 0x0d 29 | #define EXCEPTION_PAGE_FAULT 0x0e 30 | #define EXCEPTION_FPU 0x10 31 | #define EXCEPTION_ALIGNMENT 0x11 32 | #define EXCEPTION_MACHINE_CHECK 0x12 33 | #define EXCEPTION_SIMD 0x13 34 | #define EXCEPTION_VIRTUALIZATION 0x14 35 | 36 | 37 | //---------------------------------------------------------------------------- 38 | // @function exceptions_init 39 | /// @brief Initialize all exception handling routines. 40 | //---------------------------------------------------------------------------- 41 | void 42 | exceptions_init(); 43 | -------------------------------------------------------------------------------- /include/kernel/interrupt/interrupt.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file interrupt.h 3 | /// @brief Interrupt handling operations. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license 7 | // that can be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | 15 | //---------------------------------------------------------------------------- 16 | // Constants 17 | //---------------------------------------------------------------------------- 18 | 19 | // Hardware IRQ values 20 | #define IRQ_TIMER 0 21 | #define IRQ_KEYBOARD 1 22 | 23 | // Interrupt vector numbers: hardware IRQ traps 24 | #define TRAP_IRQ_TIMER 0x20 25 | #define TRAP_IRQ_KEYBOARD 0x21 26 | 27 | // PIC port constants 28 | #define PIC_PORT_CMD_MASTER 0x20 ///< Command port for master PIC 29 | #define PIC_PORT_CMD_SLAVE 0xa0 ///< Command port for slave PIC 30 | #define PIC_PORT_DATA_MASTER 0x21 ///< Data port for master PIC 31 | #define PIC_PORT_DATA_SLAVE 0xa1 ///< Data port for slave PIC 32 | 33 | // PIC commands 34 | #define PIC_CMD_EOI 0x20 ///< End of interrupt 35 | 36 | //---------------------------------------------------------------------------- 37 | // @struct interrupt_context 38 | /// @brief A record describing the CPU state at the time of the 39 | /// interrupt. 40 | //---------------------------------------------------------------------------- 41 | struct interrupt_context 42 | { 43 | registers_t regs; ///< all general-purpose registers. 44 | uint64_t error; ///< exception error identifier. 45 | uint64_t interrupt; ///< interrupt vector number. 46 | uint64_t retaddr; ///< interrupt return address. 47 | uint64_t cs; ///< code segment. 48 | uint64_t rflags; ///< flags register. 49 | uint64_t rsp; ///< stack pointer. 50 | uint64_t ss; ///< stack segment. 51 | }; 52 | 53 | typedef struct interrupt_context interrupt_context_t; 54 | 55 | //---------------------------------------------------------------------------- 56 | // @function interrupts_init 57 | /// @brief Initialize all interrupt tables. 58 | /// @details Initialize a table of interrupt service routine thunks, one 59 | /// for each of the 256 possible interrupts. Then set up the 60 | /// interrupt descriptor table (IDT) to point to each of the 61 | /// thunks. 62 | /// 63 | /// Interrupts should not be enabled until this function has 64 | /// been called. 65 | //---------------------------------------------------------------------------- 66 | void 67 | interrupts_init(); 68 | 69 | //---------------------------------------------------------------------------- 70 | // @typedef isr_handler 71 | /// @brief Interrupt service routine called when an interrupt occurs. 72 | /// @param[in] context The CPU state at the time of the interrupt. 73 | //---------------------------------------------------------------------------- 74 | typedef void (*isr_handler)(const interrupt_context_t *context); 75 | 76 | //---------------------------------------------------------------------------- 77 | // @function isr_set 78 | /// @brief Set an interrupt service routine for the given interrupt 79 | /// number. 80 | /// @details Interrupts should be disabled while setting these handlers. 81 | /// To disable an ISR, set its handler to null. 82 | /// @param[in] interrupt Interrupt number (0-255). 83 | /// @param[in] handler Interrupt service routine handler function. 84 | //---------------------------------------------------------------------------- 85 | void 86 | isr_set(int interrupt, isr_handler handler); 87 | 88 | //---------------------------------------------------------------------------- 89 | // @function irq_enable 90 | /// @brief Tell the PIC to enable a hardware interrupt. 91 | /// @param[in] irq IRQ number to enable (0-15). 92 | //---------------------------------------------------------------------------- 93 | void 94 | irq_enable(uint8_t irq); 95 | 96 | //---------------------------------------------------------------------------- 97 | // @function irq_disable 98 | /// @brief Tell the PIC to disable a hardware interrupt. 99 | /// @param[in] irq IRQ number to enable (0-15). 100 | //---------------------------------------------------------------------------- 101 | void 102 | irq_disable(uint8_t irq); 103 | -------------------------------------------------------------------------------- /include/kernel/mem/heap.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file heap.h 3 | /// @brief A simple heap memory manager 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license 7 | // that can be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | 15 | typedef struct heap heap_t; 16 | 17 | //---------------------------------------------------------------------------- 18 | // @function heap_create 19 | /// @brief Create a new heap from which to allocate virtual memory. 20 | /// @param[in] pt The page table from which virtual memory is to be 21 | /// allocated. 22 | /// @param[in] vaddr The virtual address of the first byte to use for 23 | /// the heap. 24 | /// @param[in] maxpages The maximum number of pages that the heap will 25 | /// grow to fill. 26 | /// @returns A pointer to a the created heap structure. 27 | //---------------------------------------------------------------------------- 28 | heap_t * 29 | heap_create(pagetable_t *pt, void *vaddr, uint64_t maxpages); 30 | 31 | //---------------------------------------------------------------------------- 32 | // @function heap_destroy 33 | /// @brief Destroy a heap, returning its memory to page table. 34 | /// @param[in] heap The heap to be destroyed. 35 | //---------------------------------------------------------------------------- 36 | void 37 | heap_destroy(heap_t *heap); 38 | 39 | //---------------------------------------------------------------------------- 40 | // @function heap_alloc 41 | /// @brief Allocate memory from a heap. 42 | /// @param[in] heap The heap from which to allocate the memory. 43 | /// @param[in] size The size, in bytes, of the allocation. 44 | /// @returns A pointer to a the allocated memory. 45 | //---------------------------------------------------------------------------- 46 | void * 47 | heap_alloc(heap_t *heap, uint64_t size); 48 | 49 | //---------------------------------------------------------------------------- 50 | // @function heap_free 51 | /// @brief Free memory previously allocated with heap_alloc. 52 | /// @param[in] heap The heap from which the memory was allocated. 53 | /// @param[in] ptr A pointer to the memory that was allocated. 54 | //---------------------------------------------------------------------------- 55 | void 56 | heap_free(heap_t *heap, void *ptr); 57 | -------------------------------------------------------------------------------- /include/kernel/mem/paging.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file paging.h 3 | /// @brief Paged memory management. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | // Pag size constants 15 | #define PAGE_SIZE 0x1000 16 | #define PAGE_SIZE_LARGE 0x200000 17 | #define PAGE_SIZE_HUGE 0x40000000 18 | 19 | // Page table entry flags 20 | #define PF_PRESENT (1 << 0) // Page is present in the table 21 | #define PF_RW (1 << 1) // Read-write 22 | #define PF_USER (1 << 2) // User-mode (CPL==3) access allowed 23 | #define PF_PWT (1 << 3) // Page write-thru 24 | #define PF_PCD (1 << 4) // Cache disable 25 | #define PF_ACCESS (1 << 5) // Indicates whether page was accessed 26 | #define PF_DIRTY (1 << 6) // Indicates whether 4K page was written 27 | #define PF_PS (1 << 7) // Page size (valid for PD and PDPT only) 28 | #define PF_GLOBAL (1 << 8) // Indicates the page is globally cached 29 | #define PF_SYSTEM (1 << 9) // Page used by the kernel 30 | 31 | // Virtual address bitmasks and shifts 32 | #define PGSHIFT_PML4E 39 33 | #define PGSHIFT_PDPTE 30 34 | #define PGSHIFT_PDE 21 35 | #define PGSHIFT_PTE 12 36 | #define PGMASK_ENTRY 0x1ff 37 | #define PGMASK_OFFSET 0x3ff 38 | 39 | // Virtual address subfield accessors 40 | #define PML4E(a) (((a) >> PGSHIFT_PML4E) & PGMASK_ENTRY) 41 | #define PDPTE(a) (((a) >> PGSHIFT_PDPTE) & PGMASK_ENTRY) 42 | #define PDE(a) (((a) >> PGSHIFT_PDE) & PGMASK_ENTRY) 43 | #define PTE(a) (((a) >> PGSHIFT_PTE) & PGMASK_ENTRY) 44 | 45 | // Page table entry helpers 46 | #define PGPTR(pte) ((page_t *)((pte) & ~PGMASK_OFFSET)) 47 | 48 | //---------------------------------------------------------------------------- 49 | // @union page_t 50 | /// @brief A pagetable page record. 51 | /// @details Contains 512 page table entries if the page holds a page 52 | /// table. Otherwise it contains 4096 bytes of memory. 53 | //---------------------------------------------------------------------------- 54 | typedef union page 55 | { 56 | uint64_t entry[PAGE_SIZE / sizeof(uint64_t)]; 57 | uint8_t memory[PAGE_SIZE]; 58 | } page_t; 59 | 60 | //---------------------------------------------------------------------------- 61 | // @struct pagetable_t 62 | /// @brief A pagetable structure. 63 | /// @details Holds all the page table entries that map virtual addresses to 64 | /// physical addresses. 65 | //---------------------------------------------------------------------------- 66 | typedef struct pagetable 67 | { 68 | uint64_t proot; ///< Physical address of root page table (PML4T) entry 69 | uint64_t vroot; ///< Virtual address of root page table (PML4T) entry 70 | uint64_t vnext; ///< Virtual address to use for table's next page 71 | uint64_t vterm; ///< Boundary of pages used to store the table 72 | } pagetable_t; 73 | 74 | //---------------------------------------------------------------------------- 75 | // @function page_init 76 | /// @brief Initialize the page frame database. 77 | /// @details The page frame database manages the physical memory used by 78 | /// all memory pages known to the kernel. 79 | //---------------------------------------------------------------------------- 80 | void 81 | page_init(); 82 | 83 | //---------------------------------------------------------------------------- 84 | // @function pagetable_create 85 | /// @brief Create a new page table that can be used to associate virtual 86 | /// addresses with physical addresses. The page table includes 87 | /// protected mappings for kernel memory. 88 | /// @param[in] pt A pointer to the pagetable structure that will hold 89 | /// the page table. 90 | /// @param[in] vaddr The virtual address within the new page table where 91 | /// the page table will be mapped. 92 | /// @param[in] size Maximum size of the page table in bytes. Must be a 93 | /// multiple of PAGE_SIZE. 94 | /// @returns A handle to the created page table. 95 | //---------------------------------------------------------------------------- 96 | void 97 | pagetable_create(pagetable_t *pt, void *vaddr, uint64_t size); 98 | 99 | //---------------------------------------------------------------------------- 100 | // @function pagetable_destroy 101 | /// @brief Destroy a page table. 102 | /// @param[in] pt A handle to the page table to destroy. 103 | //---------------------------------------------------------------------------- 104 | void 105 | pagetable_destroy(pagetable_t *pt); 106 | 107 | //---------------------------------------------------------------------------- 108 | // @function pagetable_activate 109 | /// @brief Activate a page table on the CPU, so all virtual memory 110 | /// operations are performed relative to the page table. 111 | /// @param[in] pt A handle to the activated page table. Pass NULL to 112 | /// activate the kernel page table. 113 | //---------------------------------------------------------------------------- 114 | void 115 | pagetable_activate(pagetable_t *pt); 116 | 117 | //---------------------------------------------------------------------------- 118 | // @function page_alloc 119 | /// @brief Allocate one or more pages contiguous in virtual memory. 120 | /// @param[in] pt Handle to the page table from which to allocate the 121 | /// page(s). 122 | /// @param[in] vaddr The virtual address of the first allocated page. 123 | /// @param[in] count The number of contiguous virtual memory pages to 124 | /// allocate. 125 | /// @returns A virtual memory pointer to the first page allocated. 126 | //---------------------------------------------------------------------------- 127 | void * 128 | page_alloc(pagetable_t *pt, void *vaddr, int count); 129 | 130 | //---------------------------------------------------------------------------- 131 | // @function page_free 132 | /// @brief Free one or more contiguous pages from virtual memory. 133 | /// @param[in] pt Handle to ehte page table from which to free the 134 | /// page(s). 135 | /// @param[in] vaddr The virtual address of the first allocated page. 136 | /// @param[in] count The number of contiguous virtual memory pages to free. 137 | //---------------------------------------------------------------------------- 138 | void 139 | page_free(pagetable_t *pt, void *vaddr, int count); 140 | -------------------------------------------------------------------------------- /include/kernel/mem/pmap.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file pmap.h 3 | /// @brief Physical memory map describing usable and reserved regions 4 | /// of physical memory. 5 | /// @details Most of the map is derived from data provided by the system 6 | /// BIOS at boot time. 7 | // 8 | // Copyright 2016 Brett Vickers. 9 | // Use of this source code is governed by a BSD-style license that can 10 | // be found in the MonkOS LICENSE file. 11 | //============================================================================ 12 | 13 | #pragma once 14 | 15 | #include 16 | 17 | //---------------------------------------------------------------------------- 18 | // @enum pmemtype 19 | /// @brief The types of physical memory. 20 | //---------------------------------------------------------------------------- 21 | enum pmemtype 22 | { 23 | PMEMTYPE_USABLE = 1, ///< Reported usable by the BIOS. 24 | PMEMTYPE_RESERVED = 2, ///< Reported (or inferred) to be reserved. 25 | PMEMTYPE_ACPI = 3, ///< Used for ACPI tables or code. 26 | PMEMTYPE_ACPI_NVS = 4, ///< Used for ACPI non-volatile storage. 27 | PMEMTYPE_BAD = 5, ///< Reported as bad memory. 28 | PMEMTYPE_UNCACHED = 6, ///< Marked as uncacheable, usually for I/O. 29 | PMEMTYPE_UNMAPPED = 7, ///< Marked as "do not map". 30 | }; 31 | 32 | //---------------------------------------------------------------------------- 33 | // @struct pmapregion_t 34 | /// @brief A memregion represents and describes a contiguous region of 35 | /// physical memory. 36 | //---------------------------------------------------------------------------- 37 | struct pmapregion 38 | { 39 | uint64_t addr; ///< base address 40 | uint64_t size; ///< size of memory region 41 | int32_t type; ///< Memory type (see memtype enum) 42 | uint32_t flags; ///< flags (currently unused) 43 | }; 44 | typedef struct pmapregion pmapregion_t; 45 | 46 | //---------------------------------------------------------------------------- 47 | // @struct pmap_t 48 | /// @brief A memory map describing available and reserved regions of 49 | /// physical memory. 50 | /// @details There are no gaps in a memory map. 51 | //---------------------------------------------------------------------------- 52 | struct pmap 53 | { 54 | uint64_t count; ///< Memory regions in the memory map 55 | uint64_t last_usable; ///< End of last usable region 56 | pmapregion_t region[1]; ///< An array of 'count' memory regions 57 | }; 58 | typedef struct pmap pmap_t; 59 | 60 | //---------------------------------------------------------------------------- 61 | // @function pmap_init 62 | /// @brief Initialize the physical memory map using data installed by the 63 | /// BIOS during boot loading. 64 | //---------------------------------------------------------------------------- 65 | void 66 | pmap_init(); 67 | 68 | //---------------------------------------------------------------------------- 69 | // @function pmap_add 70 | /// @brief Add a region of memory to the physical memory map. 71 | /// @param[in] addr The starting address of the region. 72 | /// @param[in] size The size of the region. 73 | /// @param[in] type The type of memory to add. 74 | //---------------------------------------------------------------------------- 75 | void 76 | pmap_add(uint64_t addr, uint64_t size, enum pmemtype type); 77 | 78 | //---------------------------------------------------------------------------- 79 | // @function pmap 80 | /// @brief Return a pointer to the current physical memory map. 81 | /// @returns A pointer to the physical memory map. 82 | //---------------------------------------------------------------------------- 83 | const pmap_t * 84 | pmap(); 85 | -------------------------------------------------------------------------------- /include/kernel/mem/segments.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file segments.h 3 | /// @brief Memory segment definitions. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | // Segment selector values for segment registers. 15 | #define SEGMENT_SELECTOR_KERNEL_DATA 0x08 16 | #define SEGMENT_SELECTOR_KERNEL_CODE 0x10 17 | #define SEGMENT_SELECTOR_USER_DATA 0x18 18 | #define SEGMENT_SELECTOR_USER_CODE 0x20 19 | #define SEGMENT_SELECTOR_TSS 0x28 20 | -------------------------------------------------------------------------------- /include/kernel/syscall/syscall.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file syscall.h 3 | /// @brief System call support. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license 7 | // that can be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | //---------------------------------------------------------------------------- 15 | // @function syscall_init 16 | /// @brief Set up the CPU to handle system calls. 17 | //---------------------------------------------------------------------------- 18 | void 19 | syscall_init(); 20 | -------------------------------------------------------------------------------- /include/kernel/x86/cpu.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file cpu.h 3 | /// @brief x86 CPU-specific function implementations. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license 7 | // that can be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | // CPU EFLAGS register values 15 | #define CPU_EFLAGS_CARRY (1 << 0) 16 | #define CPU_EFLAGS_PARITY (1 << 2) 17 | #define CPU_EFLAGS_ADJUST (1 << 4) 18 | #define CPU_EFLAGS_ZERO (1 << 6) 19 | #define CPU_EFLAGS_SIGN (1 << 7) 20 | #define CPU_EFLAGS_TRAP (1 << 8) 21 | #define CPU_EFLAGS_INTERRUPT (1 << 9) 22 | #define CPU_EFLAGS_DIRECTION (1 << 10) 23 | #define CPU_EFLAGS_OVERFLOW (1 << 11) 24 | #define CPU_EFLAGS_IOPL1 (1 << 12) 25 | #define CPU_EFLAGS_IOPL0 (1 << 13) 26 | #define CPU_EFLAGS_NESTED (1 << 14) 27 | #define CPU_EFLAGS_RESUME (1 << 16) 28 | #define CPU_EFLAGS_V8086 (1 << 17) 29 | #define CPU_EFLAGS_ALIGNCHECK (1 << 18) 30 | #define CPU_EFLAGS_VINTERRUPT (1 << 19) 31 | #define CPU_EFLAGS_VPENDING (1 << 20) 32 | #define CPU_EFLAGS_CPUID (1 << 21) 33 | 34 | //---------------------------------------------------------------------------- 35 | // @struct registers_t 36 | /// @brief A record describing all 64-bit general-purpose registers. 37 | //---------------------------------------------------------------------------- 38 | typedef struct registers 39 | { 40 | uint64_t rax; 41 | uint64_t rbx; 42 | uint64_t rcx; 43 | uint64_t rdx; 44 | uint64_t rsi; 45 | uint64_t rdi; 46 | uint64_t rbp; 47 | uint64_t r8; 48 | uint64_t r9; 49 | uint64_t r10; 50 | uint64_t r11; 51 | uint64_t r12; 52 | uint64_t r13; 53 | uint64_t r14; 54 | uint64_t r15; 55 | } registers_t; 56 | 57 | //---------------------------------------------------------------------------- 58 | // @struct registers4_t 59 | /// @brief A record describing the first 4 general-purpose registers. 60 | //---------------------------------------------------------------------------- 61 | typedef struct registers4 62 | { 63 | uint64_t rax; 64 | uint64_t rbx; 65 | uint64_t rcx; 66 | uint64_t rdx; 67 | } registers4_t; 68 | 69 | 70 | #ifdef __NO_INLINE__ 71 | 72 | //---------------------------------------------------------------------------- 73 | // @function cpuid 74 | /// @brief Return the results of the CPUID instruction. 75 | /// @param[in] code The cpuid group code. 76 | /// @param[out] regs The contents of registers rax, rbx, rcx, and rdx. 77 | //---------------------------------------------------------------------------- 78 | void 79 | cpuid(uint32_t code, registers4_t *regs); 80 | 81 | //---------------------------------------------------------------------------- 82 | // @function rdmsr 83 | /// @brief Read the model-specific register and return the result. 84 | /// @param[in] id The register id to read. 85 | /// @returns The contents of the requested MSR. 86 | //---------------------------------------------------------------------------- 87 | uint64_t 88 | rdmsr(uint32_t id); 89 | 90 | //---------------------------------------------------------------------------- 91 | // @function wrmsr 92 | /// @brief Write to the model-specific register. 93 | /// @param[in] id The register id to write. 94 | /// @param[in] value The value to write. 95 | //---------------------------------------------------------------------------- 96 | void 97 | wrmsr(uint32_t id, uint64_t value); 98 | 99 | //---------------------------------------------------------------------------- 100 | // @function io_inb 101 | /// @brief Retrieve a byte value from an input port. 102 | /// @param[in] port Port number (0-65535). 103 | /// @returns value Byte value read from the port. 104 | //---------------------------------------------------------------------------- 105 | uint8_t 106 | io_inb(uint16_t port); 107 | 108 | //---------------------------------------------------------------------------- 109 | // @function io_outb 110 | /// @brief Write a byte value to an output port. 111 | /// @param[in] port Port number (0-65535). 112 | /// @param[in] value Byte value to write to the port. 113 | //---------------------------------------------------------------------------- 114 | void 115 | io_outb(uint16_t port, uint8_t value); 116 | 117 | //---------------------------------------------------------------------------- 118 | // @function io_inw 119 | /// @brief Retrieve a 16-bit word value from an input port. 120 | /// @param[in] port Port number (0-65535). 121 | /// @returns value Word value read from the port. 122 | //---------------------------------------------------------------------------- 123 | uint16_t 124 | io_inw(uint16_t port); 125 | 126 | //---------------------------------------------------------------------------- 127 | // @function io_outw 128 | /// @brief Write a 16-bit word value to an output port. 129 | /// @param[in] port Port number (0-65535). 130 | /// @param[in] value Word value to write to the port. 131 | //---------------------------------------------------------------------------- 132 | void 133 | io_outw(uint16_t port, uint16_t value); 134 | 135 | //---------------------------------------------------------------------------- 136 | // @function io_ind 137 | /// @brief Retrieve a 32-bit dword value from an input port. 138 | /// @param[in] port Port number (0-65535). 139 | /// @returns value Dword value read from the port. 140 | //---------------------------------------------------------------------------- 141 | uint32_t 142 | io_ind(uint16_t port); 143 | 144 | //---------------------------------------------------------------------------- 145 | // @function io_outd 146 | /// @brief Write a 32-bit dword value to an output port. 147 | /// @param[in] port Port number (0-65535). 148 | /// @param[in] value Dword value to write to the port. 149 | //---------------------------------------------------------------------------- 150 | void 151 | io_outd(uint16_t port, uint32_t value); 152 | 153 | //---------------------------------------------------------------------------- 154 | // @function set_pagetable 155 | /// @brief Update the CPU's page table register. 156 | /// @param[in] paddr The physical address containing the new pagetable. 157 | //---------------------------------------------------------------------------- 158 | void 159 | set_pagetable(uint64_t paddr); 160 | 161 | //----------------------------------------------------------------------------- 162 | // @function invalidate_page 163 | /// @brief Invalidate the page containing a virtual address. 164 | /// @reg[in] vaddr The virtual address of the page to invalidate. 165 | //----------------------------------------------------------------------------- 166 | void 167 | invalidate_page(void *vaddr); 168 | 169 | //---------------------------------------------------------------------------- 170 | // @function enable_interrupts 171 | /// @brief Enable interrupts. 172 | //---------------------------------------------------------------------------- 173 | void 174 | enable_interrupts(); 175 | 176 | //---------------------------------------------------------------------------- 177 | // @function disable_interrupts 178 | /// @brief Disable interrupts. 179 | //---------------------------------------------------------------------------- 180 | void 181 | disable_interrupts(); 182 | 183 | //---------------------------------------------------------------------------- 184 | // @function halt 185 | /// @brief Halt the CPU until an interrupt occurs. 186 | //---------------------------------------------------------------------------- 187 | void 188 | halt(); 189 | 190 | //---------------------------------------------------------------------------- 191 | // @function invalid_opcode 192 | /// @brief Raise an invalid opcode exception. 193 | //---------------------------------------------------------------------------- 194 | void 195 | invalid_opcode(); 196 | 197 | //---------------------------------------------------------------------------- 198 | // @function fatal 199 | /// @brief Raise a fatal interrupt that hangs the system. 200 | //---------------------------------------------------------------------------- 201 | void 202 | fatal(); 203 | 204 | #endif // __NO_INLINE__ 205 | 206 | 207 | #include "cpu_inl.h" 208 | -------------------------------------------------------------------------------- /include/kernel/x86/cpu_inl.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file cpu_inl.h 3 | /// @brief x86 CPU-specific function implementations -- inline assembly. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license 7 | // that can be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | #ifndef __NO_INLINE__ 15 | 16 | __forceinline void 17 | cpuid(uint32_t code, registers4_t *regs) 18 | { 19 | asm volatile ( 20 | "cpuid" 21 | : "=a" (regs->rax), "=b" (regs->rbx), "=c" (regs->rcx), 22 | "=d" (regs->rdx) 23 | : "0" (code)); 24 | } 25 | 26 | __forceinline uint64_t 27 | rdmsr(uint32_t id) 28 | { 29 | uint64_t value; 30 | asm volatile ( 31 | "rdmsr" 32 | : "=A" (value) 33 | : "c" (id)); 34 | return value; 35 | } 36 | 37 | __forceinline void 38 | wrmsr(uint32_t id, uint64_t value) 39 | { 40 | asm volatile ( 41 | "wrmsr" 42 | : 43 | : "c" (id), "A" (value)); 44 | } 45 | 46 | __forceinline uint8_t 47 | io_inb(uint16_t port) 48 | { 49 | uint8_t value; 50 | asm volatile ( 51 | "inb %[v], %[p]" 52 | : [v] "=a" (value) 53 | : [p] "Nd" (port) 54 | ); 55 | return value; 56 | } 57 | 58 | __forceinline void 59 | io_outb(uint16_t port, uint8_t value) 60 | { 61 | asm volatile ( 62 | "outb %[p], %[v]" 63 | : 64 | : [p] "Nd" (port), [v] "a" (value) 65 | ); 66 | } 67 | 68 | __forceinline uint16_t 69 | io_inw(uint16_t port) 70 | { 71 | uint16_t value; 72 | asm volatile ( 73 | "inw %[v], %[p]" 74 | : [v] "=a" (value) 75 | : [p] "Nd" (port) 76 | ); 77 | return value; 78 | } 79 | 80 | __forceinline void 81 | io_outw(uint16_t port, uint16_t value) 82 | { 83 | asm volatile ( 84 | "outw %[p], %[v]" 85 | : 86 | : [p] "Nd" (port), [v] "a" (value) 87 | ); 88 | } 89 | 90 | __forceinline uint32_t 91 | io_ind(uint16_t port) 92 | { 93 | uint32_t value; 94 | asm volatile ( 95 | "ind %[v], %[p]" 96 | : [v] "=a" (value) 97 | : [p] "Nd" (port) 98 | ); 99 | return value; 100 | } 101 | 102 | __forceinline void 103 | io_outd(uint16_t port, uint32_t value) 104 | { 105 | asm volatile ( 106 | "outd %[p], %[v]" 107 | : 108 | : [p] "Nd" (port), [v] "a" (value) 109 | ); 110 | } 111 | 112 | __forceinline void 113 | set_pagetable(uint64_t paddr) 114 | { 115 | asm volatile ( 116 | "mov rdi, %[paddr]\n" 117 | "mov cr3, rdi\n" 118 | : 119 | : [paddr] "m" (paddr) 120 | : "rdi"); 121 | } 122 | 123 | __forceinline void 124 | invalidate_page(void *vaddr) 125 | { 126 | asm volatile ( 127 | "invlpg %[v]\n" 128 | : 129 | : [v] "m" (vaddr) 130 | : "memory"); 131 | } 132 | 133 | __forceinline void 134 | enable_interrupts() 135 | { 136 | asm volatile ("sti"); 137 | } 138 | 139 | __forceinline void 140 | disable_interrupts() 141 | { 142 | asm volatile ("cli"); 143 | } 144 | 145 | __forceinline void 146 | halt() 147 | { 148 | asm volatile ("hlt"); 149 | } 150 | 151 | __forceinline void 152 | invalid_opcode() 153 | { 154 | asm volatile ("int 6"); 155 | } 156 | 157 | __forceinline void 158 | fatal() 159 | { 160 | asm volatile ("int 0xff"); 161 | } 162 | 163 | // 164 | #endif // ifndef __NO_INLINE__ 165 | -------------------------------------------------------------------------------- /include/libc/stdio.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file stdio.h 3 | /// @brief Standard i/o library. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | #include 13 | #include 14 | 15 | //---------------------------------------------------------------------------- 16 | // @function snprintf 17 | /// @brief Compose a printf-formatted string into the target buffer. 18 | /// @details If the length of the string is greater than n-1 characters, 19 | /// truncate the remaining characters but return the total number 20 | /// of characters processed. 21 | /// @param[in] buf Pointer to a buffer where the resulting string is 22 | /// to be stored. 23 | /// @param[in] n Maximum number of characters in the the buffer, 24 | /// including space for a null terminator. 25 | /// @param[in] format A format-specifier string. 26 | /// @param[in] ... A variable argument list based on the contents of the 27 | /// format string. 28 | /// @returns The number of characters that would have been written if 29 | /// n was sufficiently large. 30 | //---------------------------------------------------------------------------- 31 | int 32 | snprintf(char *buf, size_t n, const char *format, ...); 33 | 34 | //---------------------------------------------------------------------------- 35 | // @function vsnprintf 36 | /// @brief Compose a printf-formatted string into the target buffer using 37 | /// a variable argument list. 38 | /// @details If the length of the string is greater than n-1 characters, 39 | /// truncate the remaining characters but return the total number 40 | /// of characters processed. 41 | /// @param[in] buf Pointer to a buffer where the resulting string is 42 | /// to be stored. 43 | /// @param[in] n Maximum number of characters in the the buffer, 44 | /// including space for a null terminator. 45 | /// @param[in] format A format-specifier string. 46 | /// @param[in] arg Variable arguments list to be initialized with 47 | /// va_start. 48 | /// @returns The number of characters that would have been written if 49 | /// n was sufficiently large. 50 | //---------------------------------------------------------------------------- 51 | int 52 | vsnprintf(char *buf, size_t n, const char *format, va_list arg); 53 | -------------------------------------------------------------------------------- /include/libc/stdlib.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file stdlib.h 3 | /// @brief General utilities library. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | //---------------------------------------------------------------------------- 15 | // @typedef sortcmp 16 | /// @brief Comparison function pointer type for use with the qsort 17 | /// function. 18 | /// @param[in] a Pointer to first element to be compared. 19 | /// @param[in] b Pointer to second element to be compared. 20 | /// @returns <0 if the element pointed to by a should go before the 21 | /// element pointed to by b. 22 | /// 23 | /// 0 if the elements sort equivalently. 24 | /// 25 | /// >0 if the element pointed to by a should go after the 26 | /// element pointed to by b. 27 | //---------------------------------------------------------------------------- 28 | typedef int (*sortcmp)(const void *a, const void *b); 29 | 30 | //---------------------------------------------------------------------------- 31 | // @function qsort 32 | /// @brief Sort the elements of an array using a sort comparison 33 | /// function. 34 | /// @details Sorts a contiguous array of \b num elements pointed to by 35 | /// \b base, where each element is \b size bytes long. 36 | /// @param[in] base Pointer to the first element in the array. 37 | /// @param[in] num Number of element in the array to be sorted. 38 | /// @param[in] size Size of each element in bytes. 39 | /// @param[in] cmp Pointer to a function that compares two elements. 40 | //---------------------------------------------------------------------------- 41 | void 42 | qsort(void *base, size_t num, size_t size, sortcmp cmp); 43 | -------------------------------------------------------------------------------- /include/libc/string.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file string.h 3 | /// @brief String and memory operations. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | #include 13 | 14 | //---------------------------------------------------------------------------- 15 | // @function strlen 16 | /// @brief Return the length of a null-terminated string. 17 | /// @details Count the number of characters in the null-terminated string 18 | /// and return the count. 19 | /// @param[in] str Pointer to a null-terminated string. 20 | /// @returns The number of characters preceding the null 21 | /// terminator. 22 | //---------------------------------------------------------------------------- 23 | size_t 24 | strlen(const char *str); 25 | 26 | //---------------------------------------------------------------------------- 27 | // @function strlcpy 28 | /// @brief Copy the source string to the destination buffer. 29 | /// @details Appends string src to the end of dst. It will copy at most 30 | /// dstsize - 1 characters. A null terminator is added to the end 31 | /// of the destination string unless dstsize was 0. 32 | /// @param[in] dst Pointer to the destination buffer. 33 | /// @param[in] src Pointer to the source string. 34 | /// @param[in] dstsize Maximum number of characters in the dst buffer after 35 | /// copying, including the null terminator. 36 | /// @returns The length of the copied string after truncation. 37 | //---------------------------------------------------------------------------- 38 | size_t 39 | strlcpy(char *dst, const char *src, size_t dstsize); 40 | 41 | //---------------------------------------------------------------------------- 42 | // @function strlcat 43 | /// @brief Append the source string to the end of the destination string. 44 | /// @details The function will append at most dstsize - strlen(dst) - 1 45 | /// characters. A null terminator is added to the end of the 46 | /// concatenated string unless dstsize was 0. 47 | /// @param[in] dst Pointer to the destination string. 48 | /// @param[in] src Pointer to the source string. 49 | /// @param[in] dstsize Maximum number of characters in the dst buffer after 50 | /// concatenation, including the null terminator. 51 | /// @returns The length of the concatenated string after truncation. 52 | //---------------------------------------------------------------------------- 53 | size_t 54 | strlcat(char *dst, const char *src, size_t dstsize); 55 | 56 | //---------------------------------------------------------------------------- 57 | // @function strcmp 58 | /// @brief Compare two strings and return a value indicating their 59 | /// lexicographical order. 60 | /// @details String comparison continues until a null terminator is reached 61 | /// in one of the strings. 62 | /// @param[in] str1 Pointer to the first string. 63 | /// @param[in] str2 Pointer to the second string. 64 | /// @returns < 0 if the first character in str1 that doesn't match a 65 | /// character in str2 has a lower value. 66 | /// = 0 if the two strings are identical. 67 | /// > 0 otherwise. 68 | //---------------------------------------------------------------------------- 69 | int 70 | strcmp(const char *str1, const char *str2); 71 | 72 | //---------------------------------------------------------------------------- 73 | // @function memcpy 74 | /// @brief Copy bytes from one memory region to another. 75 | /// @details If the memory regions overlap, this function's behavior 76 | /// is undefined, and you should use memmove instead. 77 | /// @param[in] dst Address of the destination memory area. 78 | /// @param[in] src Address of the source memory area. 79 | /// @param[in] num Number of bytes to copy. 80 | /// @returns Destination address. 81 | //---------------------------------------------------------------------------- 82 | void * 83 | memcpy(void *dst, const void *src, size_t num); 84 | 85 | //---------------------------------------------------------------------------- 86 | // @function memmove 87 | /// @brief Move bytes from one memory region to another, even if the 88 | /// regions overlap. 89 | /// @param[in] dst Address of the destination memory area. 90 | /// @param[in] src Address of the source memory area. 91 | /// @param[in] num Number of bytes to copy. 92 | /// @returns Destination address. 93 | //---------------------------------------------------------------------------- 94 | void * 95 | memmove(void *dst, const void *src, size_t num); 96 | 97 | //---------------------------------------------------------------------------- 98 | // @function memset 99 | /// @brief Fill a region of memory with a single byte value. 100 | /// @param[in] dst Address of the destination memory area. 101 | /// @param[in] b Value of the byte used to fill memory. 102 | /// @param[in] num Number of bytes to set. 103 | /// @returns Destination address. 104 | //---------------------------------------------------------------------------- 105 | void * 106 | memset(void *dst, int b, size_t num); 107 | 108 | //---------------------------------------------------------------------------- 109 | // @function memsetw 110 | /// @brief Fill a region of memory with a single 16-bit word value. 111 | /// @param[in] dst Address of the destination memory area. 112 | /// @param[in] w Value of the word used to fill memory. 113 | /// @param[in] num Number of words to set. 114 | /// @returns Destination address. 115 | //---------------------------------------------------------------------------- 116 | void * 117 | memsetw(void *dst, int w, size_t num); 118 | 119 | //---------------------------------------------------------------------------- 120 | // @function memsetd 121 | /// @brief Fill a region of memory with a single 32-bit dword value. 122 | /// @param[in] dst Address of the destination memory area. 123 | /// @param[in] d Value of the dword used to fill memory. 124 | /// @param[in] num Number of dwords to set. 125 | /// @returns Destination address. 126 | //---------------------------------------------------------------------------- 127 | void * 128 | memsetd(void *dst, uint32_t d, size_t num); 129 | 130 | //---------------------------------------------------------------------------- 131 | // @function memzero 132 | /// @brief Fill a region of memory with zeroes. 133 | /// @param[in] dst Address of the destination memory area. 134 | /// @param[in] num Number of bytes to set to zero. 135 | /// @returns Destination address. 136 | //---------------------------------------------------------------------------- 137 | void * 138 | memzero(void *dst, size_t num); 139 | -------------------------------------------------------------------------------- /kernel/Makefile: -------------------------------------------------------------------------------- 1 | #---------------------------------------------------------------------------- 2 | # MonkOS kernel makefile 3 | # 4 | # Makefile for the kernel. 5 | #---------------------------------------------------------------------------- 6 | 7 | DIR_ROOT := .. 8 | LIB_NAME := kernel 9 | LIB_DEPS := libc 10 | POST_LIB_RULE := kernel 11 | LD_FILE := kernel.ld 12 | 13 | include $(DIR_ROOT)/scripts/lib.mk 14 | 15 | kernel: $(DIR_BUILD)/monk.sys 16 | 17 | $(DIR_BUILD)/monk.sys: $(LD_FILE) $(LIB_FILE) $(LIB_DEPS_PATHS) 18 | @echo "$(TAG) Linking $(notdir $@)" 19 | @$(CC) $(LDFLAGS) -T $(LD_FILE) -o $@ $(LIB_FILE) $(LIB_DEPS_PATHS) 20 | @chmod a-x $@ 21 | -------------------------------------------------------------------------------- /kernel/debug/dump.c: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file dump.c 3 | /// @brief Debugging memory and CPU state dump routines. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license 7 | // that can be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #include 11 | #include 12 | 13 | static const char digit[] = "0123456789abcdef"; 14 | 15 | int 16 | dump_registers(char *buf, size_t bufsize, const registers_t *regs) 17 | { 18 | return snprintf( 19 | buf, bufsize, 20 | "RAX: %016lx RSI: %016lx R11: %016lx\n" 21 | "RBX: %016lx RDI: %016lx R12: %016lx\n" 22 | "RCX: %016lx R8: %016lx R13: %016lx\n" 23 | "RDX: %016lx R9: %016lx R14: %016lx\n" 24 | "RBP: %016lx R10: %016lx R15: %016lx\n", 25 | regs->rax, regs->rsi, regs->r11, 26 | regs->rbx, regs->rdi, regs->r12, 27 | regs->rcx, regs->r8, regs->r13, 28 | regs->rdx, regs->r9, regs->r14, 29 | regs->rbp, regs->r10, regs->r15); 30 | } 31 | 32 | int 33 | dump_cpuflags(char *buf, size_t bufsize, uint64_t rflags) 34 | { 35 | #define B(F) ((rflags & F) ? 1 : 0) 36 | 37 | return snprintf( 38 | buf, bufsize, 39 | "CF=%u PF=%u AF=%u ZF=%u SF=%u " 40 | "TF=%u IF=%u DF=%u OF=%u IOPL=%u\n", 41 | B(CPU_EFLAGS_CARRY), B(CPU_EFLAGS_PARITY), B(CPU_EFLAGS_ADJUST), 42 | B(CPU_EFLAGS_ZERO), B(CPU_EFLAGS_SIGN), B(CPU_EFLAGS_TRAP), 43 | B(CPU_EFLAGS_INTERRUPT), B(CPU_EFLAGS_DIRECTION), 44 | B(CPU_EFLAGS_OVERFLOW), (rflags >> 12) & 3); 45 | 46 | #undef B 47 | } 48 | 49 | int 50 | dump_memory(char *buf, size_t bufsize, const void *mem, size_t memsize, 51 | enum dumpstyle style) 52 | { 53 | char *b = buf; 54 | char *bt = buf + bufsize; 55 | const uint8_t *m = (const uint8_t *)mem; 56 | const uint8_t *mt = (const uint8_t *)mem + memsize; 57 | 58 | while (b < bt && m < mt) { 59 | 60 | // Dump memory offset if requested. 61 | if (style == DUMPSTYLE_OFFSET) { 62 | if (b + 11 < bt) { 63 | uint64_t o = (uint64_t)(m - (const uint8_t *)mem); 64 | for (int i = 7; i >= 0; i--) { 65 | b[i] = digit[o & 0xf]; 66 | o >>= 4; 67 | } 68 | b[8] = ':'; 69 | b[9] = b[10] = ' '; 70 | } 71 | b += 11; 72 | } 73 | 74 | // Dump memory address if requested. 75 | else if (style == DUMPSTYLE_ADDR) { 76 | if (b + 20 < bt) { 77 | uint64_t a = (uint64_t)m; 78 | for (int i = 16; i > 8; i--) { 79 | b[i] = digit[a & 0xf]; 80 | a >>= 4; 81 | } 82 | b[8] = '`'; // tick separator for readability 83 | for (int i = 7; i >= 0; i--) { 84 | b[i] = digit[a & 0xf]; 85 | a >>= 4; 86 | } 87 | b[17] = ':'; 88 | b[18] = b[19] = ' '; 89 | } 90 | b += 20; 91 | } 92 | 93 | // Dump up to 16 hexadecimal byte values. 94 | for (int j = 0; j < 16; j++) { 95 | if (b + 2 < bt) { 96 | if (m + j < mt) { 97 | uint8_t v = m[j]; 98 | b[0] = digit[v >> 4]; 99 | b[1] = digit[v & 0xf]; 100 | } 101 | else { 102 | b[0] = b[1] = ' '; 103 | } 104 | } 105 | b += 2; 106 | 107 | // Add a 1-space gutter between each group of 4 bytes. 108 | if (((j + 1) & 3) == 0) { 109 | if (b + 1 < bt) { 110 | *b = ' '; 111 | } 112 | b++; 113 | } 114 | } 115 | 116 | // Add a gutter between hex and ascii displays. 117 | if (b + 1 < bt) { 118 | *b = ' '; 119 | } 120 | b++; 121 | 122 | // Dump up to 16 ASCII bytes. 123 | for (int j = 0; j < 16; j++) { 124 | if (b + 1 < bt) { 125 | if (m + j < mt) { 126 | uint8_t v = m[j]; 127 | *b = (v < 32 || v > 126) ? '.' : (char)v; 128 | } 129 | else { 130 | *b = ' '; 131 | } 132 | } 133 | b++; 134 | 135 | // Add a gutter between each group of 8 ascii characters. 136 | if (j == 7) { 137 | if (b + 1 < bt) { 138 | *b = ' '; 139 | } 140 | b++; 141 | } 142 | } 143 | 144 | // Dump a carriage return. 145 | if (b + 1 < bt) { 146 | *b = '\n'; 147 | } 148 | b++; 149 | 150 | m += 16; 151 | } 152 | 153 | // Null-terminate the buffer. 154 | if (b < bt) { 155 | *b = 0; 156 | } 157 | else if (bufsize > 0) { 158 | b[bufsize - 1] = 0; 159 | } 160 | 161 | // Return the number of bytes (that would have been) added to the buffer, 162 | // even if the buffer was too small. 163 | return (int)(b - buf); 164 | } 165 | -------------------------------------------------------------------------------- /kernel/debug/log.c: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file log.c 3 | /// @brief Kernel logging module. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license 7 | // that can be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | // Record buffer constants 15 | #define RBUFSHIFT 10 16 | #define RBUFSIZE (1 << RBUFSHIFT) // 1024 17 | #define RBUFMASK (RBUFSIZE - 1) 18 | 19 | // Message buffer constants 20 | #define MBUFSHIFT 16 21 | #define MBUFSIZE (1 << MBUFSHIFT) // 64KiB 22 | #define MBUFMASK (MBUFSIZE - 1) 23 | 24 | // Callback registrations 25 | #define MAX_CALLBACKS 8 26 | 27 | /// A log record represents a single logged event. 28 | typedef struct record 29 | { 30 | loglevel_t level; 31 | int moffset; ///< Offset of message in context::mbuf 32 | } record_t; 33 | 34 | /// Used for registering logging callback functions. 35 | typedef struct callback 36 | { 37 | loglevel_t maxlevel; 38 | log_callback cb; 39 | } callback_t; 40 | 41 | /// The log context describes the state of the log buffers. There are two 42 | /// buffers: the record buffer and the message buffer. The record buffer is a 43 | /// circular queue that contains records describing the most recent 1024 log 44 | /// messages. The message buffer contains the text messages associated with 45 | /// each of the records in the log buffer. 46 | struct context 47 | { 48 | // Record buffer 49 | record_t rbuf[RBUFSIZE]; ///< Circular record buffer 50 | int rhead; ///< index of oldest record in rbuf 51 | int rtail; ///< rbuf index of next record to write 52 | int rbufsz; ///< number of records in rbuf 53 | 54 | // Message buffer 55 | char mbuf[MBUFSIZE]; ///< Circular message text buffer 56 | int mhead; ///< index of oldest char in mbuf 57 | int mtail; ///< mbuf index of next char to write 58 | int mbufsz; ///< the number of characters in mbuf 59 | 60 | // Callback registrations 61 | callback_t callbacks[MAX_CALLBACKS]; 62 | int callbacks_size; ///< number of registrations 63 | }; 64 | 65 | static struct context lc; 66 | 67 | static void 68 | evict_record() 69 | { 70 | lc.rhead = (lc.rhead + 1) & RBUFMASK; 71 | lc.rbufsz--; 72 | } 73 | 74 | static void 75 | evict_msg(int chars) 76 | { 77 | // Evict the requested number of characters from the mbuf. Evict 78 | // associated records when the end of a message is reached. 79 | for (int i = 0; i < chars && lc.mbufsz > 0; i++) { 80 | if (lc.mbuf[lc.mhead] == 0) 81 | evict_record(); 82 | lc.mhead = (lc.mhead + 1) & MBUFMASK; 83 | lc.mbufsz--; 84 | } 85 | 86 | // Keep evicting characters from the message buffer until a null 87 | // terminator is reached. 88 | while (lc.mbufsz > 0) { 89 | char ch = lc.mbuf[lc.mhead]; 90 | lc.mhead = (lc.mhead + 1) & MBUFMASK; 91 | lc.mbufsz--; 92 | if (ch == 0) { 93 | evict_record(); 94 | break; 95 | } 96 | } 97 | } 98 | 99 | static void 100 | evict_oldest_msg() 101 | { 102 | while (lc.mbufsz > 0) { 103 | char ch = lc.mbuf[lc.mhead]; 104 | lc.mhead = (lc.mhead + 1) & MBUFMASK; 105 | lc.mbufsz--; 106 | if (ch == 0) 107 | break; 108 | } 109 | } 110 | 111 | static int 112 | add_msg(const char *str) 113 | { 114 | int offset = lc.mtail; 115 | 116 | // Evict one or more log records if we're going to exceed the buffer size. 117 | int schars = strlen(str) + 1; 118 | if (lc.mbufsz + schars > MBUFSIZE) 119 | evict_msg(lc.mbufsz + schars - MBUFSIZE); 120 | 121 | // Copy the string into the buffer, handling wrap-around. 122 | if (lc.mtail + schars > MBUFSIZE) { 123 | int split = MBUFSIZE - lc.mtail; 124 | memcpy(lc.mbuf + lc.mtail, str, split); 125 | memcpy(lc.mbuf, str + split, schars - split); 126 | lc.mtail = schars - split; 127 | } 128 | else { 129 | memcpy(lc.mbuf + lc.mtail, str, schars); 130 | lc.mtail += schars; 131 | } 132 | 133 | lc.mbufsz += schars; 134 | 135 | return offset; 136 | } 137 | 138 | static void 139 | add_record(int offset, loglevel_t level, const char *str) 140 | { 141 | // If the record buffer is full, evict the oldest record and its 142 | // associated message text. 143 | if (lc.rbufsz == RBUFSIZE) { 144 | evict_record(); 145 | evict_oldest_msg(); 146 | } 147 | 148 | // Add a new record on the tail of the circular record queue. 149 | record_t *r = lc.rbuf + lc.rtail; 150 | r->moffset = offset; 151 | r->level = level; 152 | 153 | lc.rtail = (lc.rtail + 1) & RBUFMASK; 154 | lc.rbufsz++; 155 | 156 | // Issue registered log callbacks. 157 | for (int i = 0; i < lc.callbacks_size; i++) { 158 | const callback_t *callback = &lc.callbacks[i]; 159 | if (level <= callback->maxlevel) 160 | callback->cb(level, str); 161 | } 162 | } 163 | 164 | void 165 | log_addcallback(loglevel_t maxlevel, log_callback cb) 166 | { 167 | if (lc.callbacks_size == MAX_CALLBACKS) 168 | return; 169 | 170 | callback_t *record = &lc.callbacks[lc.callbacks_size++]; 171 | record->maxlevel = maxlevel; 172 | record->cb = cb; 173 | } 174 | 175 | void 176 | log_removecallback(log_callback cb) 177 | { 178 | for (int i = 0; i < lc.callbacks_size; i++) { 179 | if (lc.callbacks[i].cb == cb) { 180 | memmove(lc.callbacks + i, lc.callbacks + i + 1, 181 | sizeof(callback_t) * (lc.callbacks_size - (i + 1))); 182 | return; 183 | } 184 | } 185 | } 186 | 187 | void 188 | log(loglevel_t level, const char *str) 189 | { 190 | int offset = add_msg(str); 191 | add_record(offset, level, str); 192 | } 193 | 194 | void 195 | logf(loglevel_t level, const char *format, ...) 196 | { 197 | va_list args; 198 | va_start(args, format); 199 | logvf(level, format, args); 200 | va_end(args); 201 | } 202 | 203 | void 204 | logvf(loglevel_t level, const char *format, va_list args) 205 | { 206 | char str[1024]; 207 | vsnprintf(str, arrsize(str), format, args); 208 | int offset = add_msg(str); 209 | add_record(offset, level, str); 210 | } 211 | -------------------------------------------------------------------------------- /kernel/device/keyboard.c: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file keyboard.c 3 | /// @brief Keyboard input routines. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | // Keyboard I/O ports 17 | #define KB_PORT_DATA 0x60 ///< Keyboard I/O data port. 18 | 19 | // Other constants 20 | #define MAX_BUFSIZ 32 ///< Keyboard input buffer size. 21 | 22 | // Key code abbreviations, used to set up the default scan map table below. 23 | #define BSP KEY_BACKSPACE 24 | #define TAB KEY_TAB 25 | #define ENT KEY_ENTER 26 | #define ESC KEY_ESCAPE 27 | #define CTL KEY_CTRL 28 | #define SHF KEY_SHIFT 29 | #define ALT KEY_ALT 30 | #define PSC KEY_PRTSCR 31 | #define CLK KEY_CAPSLOCK 32 | #define NLK KEY_NUMLOCK 33 | #define SLK KEY_SCRLOCK 34 | #define KIN KEY_INSERT 35 | #define KEN KEY_END 36 | #define KDN KEY_DOWN 37 | #define KPD KEY_PGDN 38 | #define KLT KEY_LEFT 39 | #define KCT KEY_CENTER 40 | #define KRT KEY_RIGHT 41 | #define KHM KEY_HOME 42 | #define KUP KEY_UP 43 | #define KPU KEY_PGUP 44 | #define KDL KEY_DEL 45 | #define KMI KEY_MINUS 46 | #define KPL KEY_PLUS 47 | #define F_1 KEY_F1 48 | #define F_2 KEY_F2 49 | #define F_3 KEY_F3 50 | #define F_4 KEY_F4 51 | #define F_5 KEY_F5 52 | #define F_6 KEY_F6 53 | #define F_7 KEY_F7 54 | #define F_8 KEY_F8 55 | #define F_9 KEY_F9 56 | #define F10 KEY_F10 57 | #define F11 KEY_F11 58 | #define F12 KEY_F12 59 | #define SES KEY_SCANESC 60 | #define INV KEY_INVALID 61 | #define APO '\'' 62 | #define BSL '\\' 63 | 64 | /// US English PSC/2 keyboard scan map (default setting) 65 | static const keylayout_t ps2_layout = 66 | { 67 | .shifted = 68 | { 69 | INV, ESC, '!', '@', '#', '$', '%', '^', 70 | '&', '*', '(', ')', '_', '+', BSP, TAB, // 0 71 | 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 72 | 'O', 'P', '{', '}', ENT, CTL, 'A', 'S', // 1 73 | 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', 74 | '"', '~', SHF, '|', 'Z', 'X', 'C', 'V', // 2 75 | 'B', 'N', 'M', '<', '>', '?', SHF, PSC, 76 | ALT, ' ', CLK, F_1, F_2, F_3, F_4, F_5, // 3 77 | F_6, F_7, F_8, F_9, F10, NLK, SLK, KHM, 78 | KUP, KPU, KMI, KLT, KCT, KRT, KPL, KEN, // 4 79 | KDN, KPD, KIN, KDL, INV, INV, INV, F11, 80 | F12, INV, INV, INV, INV, INV, INV, INV, // 5 81 | SES, SES, INV, INV, INV, INV, INV, INV, 82 | INV, INV, INV, INV, INV, INV, INV, INV, // 6 83 | INV, INV, INV, INV, INV, INV, INV, INV, 84 | INV, INV, INV, INV, INV, INV, INV, INV, // 7 85 | }, 86 | .unshifted = 87 | { 88 | INV, ESC, '1', '2', '3', '4', '5', '6', 89 | '7', '8', '9', '0', '-', '=', BSP, TAB, // 0 90 | 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 91 | 'o', 'p', '[', ']', ENT, CTL, 'a', 's', // 1 92 | 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', 93 | APO, '`', SHF, BSL, 'z', 'x', 'c', 'v', // 2 94 | 'b', 'n', 'm', ',', '.', '/', SHF, PSC, 95 | ALT, ' ', CLK, F_1, F_2, F_3, F_4, F_5, // 3 96 | F_6, F_7, F_8, F_9, F10, NLK, SLK, KHM, 97 | KUP, KPU, KMI, KLT, KCT, KRT, KPL, KEN, // 4 98 | KDN, KPD, KIN, KDL, INV, INV, INV, F11, 99 | F12, INV, INV, INV, INV, INV, INV, INV, // 5 100 | SES, SES, INV, INV, INV, INV, INV, INV, 101 | INV, INV, INV, INV, INV, INV, INV, INV, // 6 102 | INV, INV, INV, INV, INV, INV, INV, INV, 103 | INV, INV, INV, INV, INV, INV, INV, INV, // 7 104 | }, 105 | }; 106 | 107 | /// Keyboard state. 108 | struct kbstate 109 | { 110 | keylayout_t layout; ///< The installed keyboard layout. 111 | uint8_t meta; ///< Mask of meta keys currently pressed. 112 | uint8_t buf_head; ///< Index of oldest key in buf. 113 | uint8_t buf_tail; ///< Index of next empty slot in buf. 114 | atomic_uchar buf_size; ///< Number of keys in the buf. 115 | key_t buf[MAX_BUFSIZ]; ///< Buffer holding unconsumed keys. 116 | }; 117 | 118 | typedef struct kbstate kbstate_t; 119 | 120 | /// Current keyboard state. 121 | static kbstate_t state; 122 | 123 | static inline void 124 | toggle(uint8_t flag) 125 | { 126 | if (state.meta & flag) 127 | state.meta &= ~flag; 128 | else 129 | state.meta |= flag; 130 | } 131 | 132 | static void 133 | addkey(uint8_t brk, uint8_t meta, uint8_t code, uint8_t ch) 134 | { 135 | // Reset the scan code escape state whenever a new key is added to the 136 | // buffer. 137 | state.meta &= ~META_ESCAPED; 138 | 139 | // Is the buffer full? 140 | // There is no need for an atomic comparison here, because the ISR 141 | // function calling addkey can never be interrupted by anything that 142 | // touches the buffer. 143 | if (state.buf_size == MAX_BUFSIZ) 144 | return; 145 | 146 | key_t key = 147 | { 148 | .brk = brk, 149 | .meta = meta, 150 | .code = code, 151 | .ch = ch 152 | }; 153 | 154 | // Add the character to the tail of the buffer. 155 | state.buf[state.buf_tail++] = key; 156 | if (state.buf_tail == MAX_BUFSIZ) 157 | state.buf_tail = 0; 158 | 159 | // state.buf_size++; 160 | atomic_fetch_add_explicit(&state.buf_size, 1, memory_order_relaxed); 161 | } 162 | 163 | static void 164 | isr_keyboard(const interrupt_context_t *context) 165 | { 166 | (void)context; 167 | 168 | // Get the scan code and the break state (key up or key down). 169 | uint8_t scancode = io_inb(KB_PORT_DATA); 170 | bool keyup = !!(scancode & 0x80); 171 | 172 | // Chop off the break bit. 173 | scancode &= ~0x80; 174 | 175 | // Get the shifted state. 176 | bool shifted = !!(state.meta & META_SHIFT); 177 | 178 | // Convert the scan code into an unshifted key code. 179 | uint8_t ukeycode = state.layout.unshifted[scancode]; 180 | 181 | // Is the key a keyboard scan escape code? If so, don't add it to the 182 | // buffer, but track the escape as a meta-state. 183 | if (ukeycode == KEY_SCANESC) { 184 | state.meta |= META_ESCAPED; 185 | goto done; 186 | } 187 | 188 | // Alter shift state based on capslock state. 189 | if (state.meta & META_CAPSLOCK) { 190 | if ((ukeycode >= 'a') && (ukeycode <= 'z')) 191 | shifted = !shifted; 192 | } 193 | 194 | // Convert the scan code to a properly shifted key code. 195 | uint8_t keycode = shifted ? state.layout.shifted[scancode] : ukeycode; 196 | 197 | // Key up? 198 | if (keyup) { 199 | switch (keycode) 200 | { 201 | case KEY_SHIFT: 202 | state.meta &= ~META_SHIFT; 203 | break; 204 | 205 | case KEY_CTRL: 206 | state.meta &= ~META_CTRL; 207 | break; 208 | 209 | case KEY_ALT: 210 | state.meta &= ~META_ALT; 211 | break; 212 | 213 | case KEY_CAPSLOCK: 214 | toggle(META_CAPSLOCK); 215 | break; 216 | 217 | case KEY_NUMLOCK: 218 | toggle(META_NUMLOCK); 219 | break; 220 | 221 | case KEY_SCRLOCK: 222 | toggle(META_SCRLOCK); 223 | break; 224 | } 225 | addkey(KEYBRK_UP, state.meta, ukeycode, 0); 226 | } 227 | // Key down? 228 | else { 229 | switch (keycode) 230 | { 231 | case KEY_SHIFT: 232 | state.meta |= META_SHIFT; 233 | break; 234 | 235 | case KEY_CTRL: 236 | state.meta |= META_CTRL; 237 | break; 238 | 239 | case KEY_ALT: 240 | state.meta |= META_ALT; 241 | break; 242 | } 243 | 244 | // Convert the key to a character. 245 | char ch = 0; 246 | if (keycode < 0x80) { 247 | switch (state.meta & (META_CTRL | META_ALT)) 248 | { 249 | case 0: 250 | ch = (char)keycode; 251 | break; 252 | 253 | case META_CTRL: 254 | if ((ukeycode >= 'a') && (ukeycode <= 'z')) 255 | ch = (char)(ukeycode - 'a' + 1); 256 | break; 257 | } 258 | } 259 | addkey(KEYBRK_DOWN, state.meta, ukeycode, ch); 260 | } 261 | 262 | done: 263 | // Send the end-of-interrupt signal. 264 | io_outb(PIC_PORT_CMD_MASTER, PIC_CMD_EOI); 265 | } 266 | 267 | void 268 | kb_init() 269 | { 270 | // Default to the PSC/2 keyboard layout. 271 | memcpy(&state.layout, &ps2_layout, sizeof(state.layout)); 272 | 273 | // Initialize keyboard state. 274 | state.meta = 0; 275 | state.buf_head = 0; 276 | state.buf_tail = 0; 277 | state.buf_size = 0; 278 | memzero(&state.buf, sizeof(state.buf)); 279 | 280 | // Assign the interrupt service routine. 281 | isr_set(TRAP_IRQ_KEYBOARD, isr_keyboard); 282 | 283 | // Enable the keyboard hardware interrupt (IRQ1). 284 | irq_enable(IRQ_KEYBOARD); 285 | } 286 | 287 | void 288 | kb_setlayout(keylayout_t *layout) 289 | { 290 | memcpy(&state.layout, layout, sizeof(state.layout)); 291 | } 292 | 293 | char 294 | kb_getchar() 295 | { 296 | for (;;) { 297 | // Buffer empty? (state.buf_size == 0?) Check atomically because 298 | // this function could be interrupted by the keyboard ISR. 299 | uint8_t size = 0; 300 | if (atomic_compare_exchange_strong(&state.buf_size, &size, 0)) 301 | return 0; 302 | 303 | // Pull the next character from the head of the buffer. 304 | char ch = state.buf[state.buf_head++].ch; 305 | if (state.buf_head == MAX_BUFSIZ) 306 | state.buf_head = 0; 307 | 308 | // state.buf_size-- 309 | atomic_fetch_sub_explicit(&state.buf_size, 1, memory_order_relaxed); 310 | 311 | // Valid character? 312 | if (ch != 0) 313 | return ch; 314 | } 315 | } 316 | 317 | bool 318 | kb_getkey(key_t *key) 319 | { 320 | // Buffer empty? (state.buf_size == 0?) Check atomically because this 321 | // function could be interrupted by the keyboard ISR. 322 | uint8_t size = 0; 323 | if (atomic_compare_exchange_strong(&state.buf_size, &size, 0)) { 324 | *(uint32_t *)key = 0; 325 | return false; 326 | } 327 | 328 | // Pull the next character from the head of the buffer. 329 | *key = state.buf[state.buf_head++]; 330 | if (state.buf_head == MAX_BUFSIZ) 331 | state.buf_head = 0; 332 | 333 | // state.buf_size-- 334 | atomic_fetch_sub_explicit(&state.buf_size, 1, memory_order_relaxed); 335 | 336 | return true; 337 | } 338 | 339 | uint8_t 340 | kb_meta() 341 | { 342 | return state.meta; 343 | } 344 | -------------------------------------------------------------------------------- /kernel/device/pci.c: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file pci.c 3 | /// @brief PCI controller. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define DEBUG_PCI 1 16 | 17 | #define PCI_CONFIG_ADDR 0x0cf8 18 | #define PCI_CONFIG_DATA 0x0cfc 19 | 20 | static inline uint32_t 21 | read(uint32_t bus, uint32_t device, uint32_t func, uint32_t offset) 22 | { 23 | uint32_t addr = (1u << 31) | 24 | (bus << 16) | 25 | (device << 11) | 26 | (func << 8) | 27 | offset; 28 | 29 | io_outd(PCI_CONFIG_ADDR, addr); 30 | return io_ind(PCI_CONFIG_DATA); 31 | } 32 | 33 | static inline uint32_t 34 | read_hdrtype(uint32_t bus, uint32_t device, uint32_t func) 35 | { 36 | uint32_t value = read(bus, device, func, 0x0c); 37 | return (value >> 16) & 0xff; 38 | } 39 | 40 | static inline uint32_t 41 | read_deviceid(uint32_t bus, uint32_t device, uint32_t func) 42 | { 43 | uint32_t value = read(bus, device, func, 0x00); 44 | return (value >> 16) & 0xffff; 45 | } 46 | 47 | static inline uint32_t 48 | read_vendor(uint32_t bus, uint32_t device, uint32_t func) 49 | { 50 | uint32_t value = read(bus, device, func, 0x00); 51 | return value & 0xffff; 52 | } 53 | 54 | static inline uint32_t 55 | read_class(uint32_t bus, uint32_t device, uint32_t func) 56 | { 57 | uint32_t value = read(bus, device, func, 0x08); 58 | return value >> 24; 59 | } 60 | 61 | static inline uint32_t 62 | read_subclass(uint32_t bus, uint32_t device, uint32_t func) 63 | { 64 | uint32_t value = read(bus, device, func, 0x08); 65 | return (value >> 16) & 0xff; 66 | } 67 | 68 | static inline uint32_t 69 | read_secondary_bus(uint32_t bus, uint32_t device, uint32_t func) 70 | { 71 | uint32_t value = read(bus, device, func, 0x18); 72 | return (value >> 8) & 0xff; 73 | } 74 | 75 | static void probe_bus(uint32_t bus); 76 | 77 | static bool 78 | probe_function(uint32_t bus, uint32_t device, uint32_t func) 79 | { 80 | // Validate the function 81 | uint32_t vendor = read_vendor(bus, device, func); 82 | if (vendor == 0xffff) 83 | return false; 84 | 85 | // Read the device's class/subclass 86 | uint32_t class = read_class(bus, device, func); 87 | uint32_t subclass = read_subclass(bus, device, func); 88 | 89 | // Is this a PCI-to-PCI bridge device? If so, recursively scan the 90 | // bridge's secondary bus. 91 | if (class == 6 && subclass == 4) { 92 | uint32_t bus2 = read_secondary_bus(bus, device, func); 93 | probe_bus(bus2); 94 | } 95 | 96 | #if DEBUG_PCI 97 | else { 98 | uint32_t devid = read_deviceid(bus, device, func); 99 | tty_printf( 100 | 0, 101 | "[pci] %u/%u/%u vendor=0x%04x devid=0x%04x " 102 | "class=%02x subclass=%02x\n", 103 | bus, device, func, vendor, devid, class, subclass); 104 | } 105 | #endif 106 | 107 | return true; 108 | } 109 | 110 | static void 111 | probe_device(uint32_t bus, uint32_t device) 112 | { 113 | // Probe device function 0. 114 | if (!probe_function(bus, device, 0)) 115 | return; 116 | 117 | // Probe functions 1 through 8 if the device is multi-function. 118 | uint32_t hdrtype = read_hdrtype(bus, device, 0); 119 | if (hdrtype & 0x80) { 120 | for (uint32_t func = 1; func < 8; ++func) 121 | probe_function(bus, device, func); 122 | } 123 | } 124 | 125 | static void 126 | probe_bus(uint32_t bus) 127 | { 128 | // Probe all possible devices on the bus. 129 | for (uint32_t device = 0; device < 32; ++device) 130 | probe_device(bus, device); 131 | } 132 | 133 | void 134 | pci_init() 135 | { 136 | // Always probe bus 0. 137 | probe_bus(0); 138 | 139 | // If bus 0 device 0 is multi-function, probe remaining 7 buses. 140 | uint32_t hdrtype = read_hdrtype(0, 0, 0); 141 | if (hdrtype & 0x80) { 142 | for (uint32_t bus = 1; bus < 8; ++bus) { 143 | uint32_t vendor = read_vendor(0, 0, bus); // func = bus # 144 | if (vendor != 0xffff) 145 | probe_bus(bus); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /kernel/device/timer.c: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file timer.c 3 | /// @brief Programmable interval timer (8253/8254) controller. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | // 8253 timer ports 16 | #define TIMER_PORT_DATA_CH0 0x40 ///< Channel 0 data port. 17 | #define TIMER_PORT_DATA_CH1 0x41 ///< Channel 1 data port. 18 | #define TIMER_PORT_DATA_CH2 0x42 ///< Channel 2 data port. 19 | #define TIMER_PORT_CMD 0x43 ///< Timer command port. 20 | 21 | // Frequency bounds 22 | #define MIN_FREQUENCY 19 23 | #define MAX_FREQUENCY 1193181 24 | 25 | static void 26 | isr_timer(const interrupt_context_t *context) 27 | { 28 | (void)context; 29 | 30 | // Do nothing for now. 31 | 32 | // Send the end-of-interrupt signal. 33 | io_outb(PIC_PORT_CMD_MASTER, PIC_CMD_EOI); 34 | } 35 | 36 | void 37 | timer_init(uint32_t frequency) 38 | { 39 | // Clamp frequency to allowable range. 40 | if (frequency < MIN_FREQUENCY) { 41 | frequency = MIN_FREQUENCY; 42 | } 43 | else if (frequency > MAX_FREQUENCY) { 44 | frequency = MAX_FREQUENCY; 45 | } 46 | 47 | // Compute the clock count value. 48 | uint16_t count = (uint16_t)(MAX_FREQUENCY / frequency); 49 | 50 | // Channel=0, AccessMode=lo/hi, OperatingMode=rate-generator 51 | io_outb(TIMER_PORT_CMD, 0x34); 52 | 53 | // Output the lo/hi count value 54 | io_outb(TIMER_PORT_DATA_CH0, (uint8_t)count); 55 | io_outb(TIMER_PORT_DATA_CH0, (uint8_t)(count >> 8)); 56 | 57 | // Assign the interrupt service routine. 58 | isr_set(TRAP_IRQ_TIMER, isr_timer); 59 | 60 | // Enable the timer interrupt (IRQ0). 61 | irq_enable(IRQ_TIMER); 62 | } 63 | 64 | void 65 | timer_enable() 66 | { 67 | // Enable the timer interrupt (IRQ0). 68 | irq_enable(0); 69 | } 70 | 71 | void 72 | timer_disable() 73 | { 74 | // Disable the timer interrupt (IRQ0). 75 | irq_disable(0); 76 | } 77 | -------------------------------------------------------------------------------- /kernel/device/tty.c: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file tty.c 3 | /// @brief Teletype (console) screen text manipulation routines. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | // CRTC ports 17 | #define CRTC_PORT_CMD 0x03d4 ///< Command port for CRT controller. 18 | #define CRTC_PORT_DATA 0x03d5 ///< Data port for CRT controller. 19 | 20 | // CRTC commands 21 | #define CRTC_CMD_STARTADDR_HI 0x0c ///< Hi-byte of buffer start address. 22 | #define CRTC_CMD_STARTADDR_LO 0x0d ///< Lo-byte of buffer start address. 23 | #define CRTC_CMD_CURSORADDR_HI 0x0e ///< Hi-byte of cursor start address. 24 | #define CRTC_CMD_CURSORADDR_LO 0x0f ///< Lo-byte of cursor start address. 25 | 26 | // Visible screen geometry 27 | #define SCREEN_ROWS 25 28 | #define SCREEN_COLS 80 29 | #define SCREEN_SIZE (SCREEN_ROWS * SCREEN_COLS) 30 | #define SCREEN_BUFFER 0x000b8000 31 | 32 | /// Virtual console state. 33 | struct tty 34 | { 35 | uint16_t textcolor; ///< Current fg/bg color (shifted). 36 | uint16_t textcolor_orig; ///< Original, non-override text color. 37 | screenpos_t pos; ///< Current screen position. 38 | uint8_t ybuf; ///< Virtual buffer y position. 39 | uint16_t *screen; ///< Virtual screen buffer for 50 rows. 40 | uint16_t *tlcorner; ///< Points to char in top-left corner. 41 | }; 42 | 43 | typedef struct tty tty_t; 44 | 45 | static tty_t tty[MAX_TTYS]; ///< All virtual consoles. 46 | static tty_t *active_tty; ///< The currently visible console. 47 | 48 | static inline uint16_t 49 | color(textcolor_t fg, textcolor_t bg) 50 | { 51 | return (uint16_t)bg << 12 | (uint16_t)fg << 8; 52 | } 53 | 54 | static void 55 | update_buffer_offset() 56 | { 57 | // Calculate top-left corner offset from the start of the first screen 58 | // buffer. 59 | int offset = (int)(active_tty->tlcorner - (uint16_t *)SCREEN_BUFFER); 60 | 61 | uint8_t save = io_inb(CRTC_PORT_CMD); 62 | 63 | io_outb(CRTC_PORT_CMD, CRTC_CMD_STARTADDR_LO); 64 | io_outb(CRTC_PORT_DATA, (uint8_t)offset); 65 | io_outb(CRTC_PORT_CMD, CRTC_CMD_STARTADDR_HI); 66 | io_outb(CRTC_PORT_DATA, (uint8_t)(offset >> 8)); 67 | 68 | io_outb(CRTC_PORT_CMD, save); 69 | } 70 | 71 | static void 72 | update_cursor() 73 | { 74 | // Calculate cursor offset from the start of the first screen buffer. 75 | int offset = active_tty->ybuf * SCREEN_COLS + active_tty->pos.x + 76 | (int)(active_tty->screen - (uint16_t *)SCREEN_BUFFER); 77 | 78 | uint8_t save = io_inb(CRTC_PORT_CMD); 79 | 80 | io_outb(CRTC_PORT_CMD, CRTC_CMD_CURSORADDR_LO); 81 | io_outb(CRTC_PORT_DATA, (uint8_t)offset); 82 | io_outb(CRTC_PORT_CMD, CRTC_CMD_CURSORADDR_HI); 83 | io_outb(CRTC_PORT_DATA, (uint8_t)(offset >> 8)); 84 | 85 | io_outb(CRTC_PORT_CMD, save); 86 | } 87 | 88 | void 89 | tty_init() 90 | { 91 | uint16_t *screenptr = (uint16_t *)SCREEN_BUFFER; 92 | 93 | for (int id = 0; id < MAX_TTYS; id++) { 94 | tty[id].textcolor = color(TEXTCOLOR_WHITE, TEXTCOLOR_BLACK); 95 | tty[id].textcolor_orig = tty[id].textcolor; 96 | tty[id].pos.x = 0; 97 | tty[id].pos.y = 0; 98 | tty[id].ybuf = 0; 99 | tty[id].screen = screenptr; 100 | tty[id].tlcorner = screenptr; 101 | screenptr += 0x1000; // each screen is 4K words. 102 | } 103 | active_tty = &tty[0]; 104 | } 105 | 106 | void 107 | tty_activate(int id) 108 | { 109 | if ((id < 0) || (id >= MAX_TTYS)) { 110 | id = 0; 111 | } 112 | if (&tty[id] == active_tty) { 113 | return; 114 | } 115 | 116 | active_tty = &tty[id]; 117 | update_buffer_offset(); 118 | update_cursor(); 119 | } 120 | 121 | void 122 | tty_set_textcolor(int id, textcolor_t fg, textcolor_t bg) 123 | { 124 | if ((id < 0) || (id >= MAX_TTYS)) { 125 | id = 0; 126 | } 127 | 128 | tty[id].textcolor = tty[id].textcolor_orig = color(fg, bg); 129 | } 130 | 131 | void 132 | tty_set_textcolor_fg(int id, textcolor_t fg) 133 | { 134 | if ((id < 0) || (id >= MAX_TTYS)) { 135 | id = 0; 136 | } 137 | 138 | tty[id].textcolor = color(fg, tty_get_textcolor_bg(id)); 139 | tty[id].textcolor_orig = tty[id].textcolor; 140 | } 141 | 142 | void 143 | tty_set_textcolor_bg(int id, textcolor_t bg) 144 | { 145 | if ((id < 0) || (id >= MAX_TTYS)) { 146 | id = 0; 147 | } 148 | 149 | tty[id].textcolor = color(tty_get_textcolor_fg(id), bg); 150 | tty[id].textcolor_orig = tty[id].textcolor; 151 | } 152 | 153 | textcolor_t 154 | tty_get_textcolor_fg(int id) 155 | { 156 | if ((id < 0) || (id >= MAX_TTYS)) { 157 | id = 0; 158 | } 159 | 160 | return (textcolor_t)((tty[id].textcolor_orig >> 8) & 0x0f); 161 | } 162 | 163 | textcolor_t 164 | tty_get_textcolor_bg(int id) 165 | { 166 | if ((id < 0) || (id >= MAX_TTYS)) { 167 | id = 0; 168 | } 169 | 170 | return (textcolor_t)((tty[id].textcolor_orig >> 12) & 0x0f); 171 | } 172 | 173 | void 174 | tty_clear(int id) 175 | { 176 | if ((id < 0) || (id >= MAX_TTYS)) { 177 | id = 0; 178 | } 179 | 180 | memsetw(tty[id].screen, 181 | tty[id].textcolor | ' ', SCREEN_SIZE * 2); 182 | tty[id].pos.x = 0; 183 | tty[id].pos.y = 0; 184 | tty[id].ybuf = 0; 185 | tty[id].tlcorner = tty[id].screen; 186 | 187 | if (active_tty == &tty[id]) { 188 | update_buffer_offset(); 189 | update_cursor(); 190 | } 191 | } 192 | 193 | void 194 | tty_setpos(int id, screenpos_t pos) 195 | { 196 | if ((id < 0) || (id >= MAX_TTYS)) { 197 | id = 0; 198 | } 199 | 200 | int diff = (int)pos.y - (int)tty[id].pos.y; 201 | tty[id].pos = pos; 202 | tty[id].ybuf = (uint8_t)((int)tty[id].ybuf + diff); 203 | if (active_tty == &tty[id]) { 204 | update_cursor(); 205 | } 206 | } 207 | 208 | void 209 | tty_getpos(int id, screenpos_t *pos) 210 | { 211 | if ((id < 0) || (id >= MAX_TTYS)) { 212 | id = 0; 213 | } 214 | *pos = tty[id].pos; 215 | } 216 | 217 | static int 218 | colorcode(char x, int orig) 219 | { 220 | int code = x; 221 | 222 | if ((code >= '0') && (code <= '9')) { 223 | return code - '0'; 224 | } 225 | else if ((code >= 'a') && (code <= 'f')) { 226 | return code - 'a' + 10; 227 | } 228 | else if ((code >= 'A') && (code <= 'F')) { 229 | return code - 'A' + 10; 230 | } 231 | else if (code == '-') { 232 | return orig; 233 | } 234 | else { 235 | return -1; 236 | } 237 | } 238 | 239 | static void 240 | tty_printchar(tty_t *cons, const char **strptr) 241 | { 242 | bool linefeed = false; 243 | 244 | const char *str = *strptr; 245 | char ch = *str; 246 | 247 | // If the newline character is encountered, do a line feed + carriage 248 | // return. 249 | if (ch == '\n') { 250 | cons->pos.x = 0; 251 | linefeed = true; 252 | } 253 | // Handle color codes, e.g. "\033[#]". 254 | else if (ch == '\033') { 255 | if ((str[1] == '[') && str[2] && (str[3] == ']')) { 256 | int code = colorcode(str[2], (cons->textcolor_orig >> 8) & 0x0f); 257 | if (code != -1) { 258 | cons->textcolor = (cons->textcolor & 0xf000) | 259 | (uint16_t)(code << 8); 260 | *strptr += 3; 261 | } 262 | } 263 | else if ((str[1] == '{') && str[2] && (str[3] == '}')) { 264 | int code = colorcode(str[2], (cons->textcolor_orig >> 12)); 265 | if (code != -1) { 266 | cons->textcolor = (cons->textcolor & 0x0f00) | 267 | (uint16_t)(code << 12); 268 | *strptr += 3; 269 | } 270 | } 271 | return; 272 | } 273 | else if (ch == '\b') { 274 | if (cons->pos.x > 0) { 275 | int offset = cons->ybuf * SCREEN_COLS + --cons->pos.x; 276 | cons->screen[offset] = cons->textcolor | ' '; 277 | } 278 | } 279 | else { 280 | // Use the current foreground and background color. 281 | uint16_t value = cons->textcolor | ch; 282 | 283 | // Calculate the buffer offset using the virtual row. 284 | int offset = cons->ybuf * SCREEN_COLS + cons->pos.x; 285 | 286 | // Update the visible screen buffer. 287 | cons->screen[offset] = value; 288 | 289 | // If the right side of the screen was reached, we need a linefeed. 290 | if (++cons->pos.x == SCREEN_COLS) { 291 | cons->pos.x = 0; 292 | linefeed = true; 293 | } 294 | } 295 | 296 | // A linefeed causes a hardware scroll of one row. If we reach the end of 297 | // the virtual buffer, wrap it back one screen. 298 | if (linefeed) { 299 | 300 | // Copy the last line of the screen to a virtual row one screen 301 | // away. This way, when we reach the end of the virtual buffer, 302 | // we'll have another shifted copy of the screen ready to display. 303 | // This is better than copying the entire screen whenever we reach 304 | // the end of the virtual buffer, because it amortizes the cost 305 | // of copying. 306 | memcpy(cons->screen + cons->ybuf * SCREEN_COLS - SCREEN_SIZE, 307 | cons->screen + cons->ybuf * SCREEN_COLS, 308 | SCREEN_COLS * sizeof(uint16_t)); 309 | 310 | // Increment row (on screen and in the virtual buffer). 311 | ++cons->pos.y; 312 | ++cons->ybuf; 313 | 314 | // If we're at the bottom of the screen, we need to scroll a line. 315 | if (cons->pos.y == SCREEN_ROWS) { 316 | 317 | --cons->pos.y; 318 | 319 | // If we're at the end of the virtual buffer, we need to 320 | // wrap back a screen. 321 | if (cons->ybuf == SCREEN_ROWS * 2) { 322 | cons->ybuf -= SCREEN_ROWS; 323 | } 324 | 325 | // Clear the row at the bottom of the screen. 326 | memsetw(cons->screen + cons->ybuf * SCREEN_COLS, 327 | cons->textcolor | ' ', 328 | SCREEN_COLS * sizeof(uint16_t)); 329 | 330 | // Adjust the offset of the top-left corner of the screen. 331 | cons->tlcorner = cons->screen + (cons->ybuf + 1) * SCREEN_COLS - 332 | SCREEN_SIZE; 333 | 334 | // Do a hardware scroll if this console is currently active. 335 | if (cons == active_tty) { 336 | update_buffer_offset(); 337 | } 338 | } 339 | } 340 | } 341 | 342 | void 343 | tty_print(int id, const char *str) 344 | { 345 | if ((id < 0) || (id >= MAX_TTYS)) 346 | id = 0; 347 | 348 | tty_t *cons = &tty[id]; 349 | for (; *str; ++str) 350 | tty_printchar(cons, &str); 351 | 352 | if (cons == active_tty) 353 | update_cursor(); 354 | } 355 | 356 | void 357 | tty_printc(int id, char ch) 358 | { 359 | if ((id < 0) || (id >= MAX_TTYS)) 360 | id = 0; 361 | 362 | const char str[2] = { ch, 0 }; 363 | const char *ptr = str; 364 | tty_t *cons = &tty[id]; 365 | tty_printchar(cons, &ptr); 366 | 367 | if (cons == active_tty) 368 | update_cursor(); 369 | } 370 | 371 | int 372 | tty_printf(int id, const char *format, ...) 373 | { 374 | if ((id < 0) || (id >= MAX_TTYS)) 375 | id = 0; 376 | 377 | va_list args; 378 | va_start(args, format); 379 | char buffer[8 * 1024]; 380 | int result = vsnprintf(buffer, sizeof(buffer), format, args); 381 | va_end(args); 382 | 383 | tty_print(id, buffer); 384 | 385 | return result; 386 | } 387 | -------------------------------------------------------------------------------- /kernel/interrupt/exception.c: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file exception.c 3 | /// @brief CPU exceptions. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license 7 | // that can be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | static const char *exceptionstr[] = 19 | { 20 | "#DE: Divide by zero exception", 21 | "#DB: Debug exception", 22 | "Non-maskable interrupt", 23 | "#BP: Breakpoint exception", 24 | "#OF: Overflow exception", 25 | "#BR: BOUND Range exceeded exception", 26 | "#UD: Invalid opcode exception", 27 | "#NM: Device not available exception", 28 | "#DF: Double fault exception", 29 | "Coprocessor segment overrun", 30 | "#TS: Invalid TSS exception", 31 | "#NP: Segment not present exception", 32 | "#SS: Stack fault exception", 33 | "#GP: General protection exception", 34 | "#PF: Page fault exception", 35 | "Unknown exception", 36 | "#MF: x87 FPU floating-point error", 37 | "#AC: Alignment check exception", 38 | "#MC: Machine-check exception", 39 | "#XM: SIMD floating-point exception", 40 | "#VE: Virtualization exception", 41 | }; 42 | 43 | static void 44 | dump_context(int id, const interrupt_context_t *context) 45 | { 46 | tty_printf( 47 | id, 48 | "INT: %02x Error: %08x\n\n", 49 | context->interrupt, context->error); 50 | tty_printf( 51 | id, 52 | "CS:RIP: %04x:%016lx SS:RSP: %04x:%016lx\n\n", 53 | context->cs, context->retaddr, context->ss, context->rsp); 54 | 55 | char buf[640]; 56 | 57 | dump_registers(buf, sizeof(buf), &context->regs); 58 | tty_print(id, buf); 59 | tty_print(id, "\n"); 60 | 61 | dump_cpuflags(buf, sizeof(buf), context->rflags); 62 | tty_print(id, buf); 63 | tty_print(id, "\n"); 64 | 65 | tty_print(id, "Stack:\n"); 66 | void *stack = (void *)context->rsp; 67 | dump_memory(buf, sizeof(buf), stack, 8 * 16, DUMPSTYLE_ADDR); 68 | tty_print(id, buf); 69 | } 70 | 71 | static void 72 | hang() 73 | { 74 | for (;;) { 75 | disable_interrupts(); 76 | halt(); 77 | } 78 | } 79 | 80 | static void 81 | isr_fatal(const interrupt_context_t *context) 82 | { 83 | int i = context->interrupt; 84 | 85 | const char *exstr = i < arrsize(exceptionstr) 86 | ? exceptionstr[i] : "Unknown exception."; 87 | 88 | tty_activate(0); 89 | tty_set_textcolor(0, TEXTCOLOR_WHITE, TEXTCOLOR_RED); 90 | tty_clear(0); 91 | tty_printf(0, "%s\n\n", exstr); 92 | 93 | dump_context(0, context); 94 | 95 | hang(); 96 | } 97 | 98 | static void 99 | isr_breakpoint(const interrupt_context_t *context) 100 | { 101 | (void)context; 102 | 103 | tty_print(0, "Breakpoint hit.\n"); 104 | } 105 | 106 | void 107 | exceptions_init() 108 | { 109 | for (int i = 0; i < 32; i++) 110 | isr_set(i, isr_fatal); // fatal for now. temporary. 111 | isr_set(0xff, isr_fatal); 112 | 113 | isr_set(EXCEPTION_BREAKPOINT, isr_breakpoint); 114 | } 115 | -------------------------------------------------------------------------------- /kernel/kernel.ld: -------------------------------------------------------------------------------- 1 | /* Entry point in start.asm */ 2 | ENTRY(_start) 3 | 4 | SECTIONS 5 | { 6 | /* 7 | * We'll be loading the kernel ELF file at 0x00300000. We'd like to 8 | * set up our page tables so that the virtual and physical addresses of 9 | * the kernel code are the same ("identity-mapping"). To do this, 10 | * we need to ensure the code section lines up with its address in 11 | * memory once loaded. The code section begins at offset 0x1000, so 12 | * we make sure the start address is 0x00301000. 13 | * 14 | * Note that ld must be passed a command-line argument of 15 | * -z max-page-size=0x1000. Otherwise the kernel file will be too large. 16 | */ 17 | 18 | KERNEL_VMA = 0x00301000; 19 | 20 | . = KERNEL_VMA; 21 | 22 | /************************************************************************* 23 | * .text 24 | * 25 | * Code (and read-only data) section 26 | ************************************************************************/ 27 | .text : ALIGN(4K) 28 | { 29 | *(.start) /* from start.asm. Must be first. */ 30 | *(.text) 31 | *(.rodata*) 32 | } 33 | 34 | /************************************************************************* 35 | * .data 36 | * 37 | * Initialized read-write data section 38 | ************************************************************************/ 39 | .data : ALIGN(4K) 40 | { 41 | *(.data) 42 | } 43 | 44 | /************************************************************************* 45 | * .bss 46 | * 47 | * Uninitialized read-write data 48 | ************************************************************************/ 49 | .bss : ALIGN(4K) 50 | { 51 | _BSS_START = ABSOLUTE(.); 52 | *(.bss) 53 | *(COMMON) 54 | } 55 | _BSS_SIZE = ABSOLUTE(.) - _BSS_START; 56 | } 57 | -------------------------------------------------------------------------------- /kernel/main.c: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file main.c 3 | /// @brief The kernel's main entry point. 4 | /// @details This file contains the function kmain(), which is the first 5 | /// function called by the kernel's start code in start.asm. 6 | // 7 | // Copyright 2016 Brett Vickers. 8 | // Use of this source code is governed by a BSD-style license that can 9 | // be found in the MonkOS LICENSE file. 10 | //============================================================================ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "shell.h" 23 | 24 | #if defined(__linux__) 25 | # error "This code must be compiled with a cross-compiler." 26 | #endif 27 | 28 | #define TTY_CONSOLE 0 29 | 30 | void 31 | kmain() 32 | { 33 | // Memory initialization 34 | acpi_init(); 35 | pmap_init(); 36 | page_init(); 37 | 38 | // Interrupt initialization 39 | interrupts_init(); 40 | exceptions_init(); 41 | 42 | // Device initialization 43 | tty_init(); 44 | kb_init(); 45 | timer_init(20); // 20Hz 46 | 47 | // System call initialization 48 | syscall_init(); 49 | 50 | // Let the games begin 51 | enable_interrupts(); 52 | 53 | // Display a welcome message on the virtual console. 54 | tty_set_textcolor(TTY_CONSOLE, TEXTCOLOR_LTGRAY, TEXTCOLOR_BLACK); 55 | tty_clear(TTY_CONSOLE); 56 | tty_print(TTY_CONSOLE, "Welcome to \033[e]MonkOS\033[-] (v0.1).\n"); 57 | 58 | // Launch the interactive test shell. 59 | kshell(); 60 | } 61 | -------------------------------------------------------------------------------- /kernel/mem/kmem.c: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file kmem.c 3 | /// @brief Kernel physical (and virtual) memory map. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "kmem.h" 17 | 18 | /// Return flags for large-page leaf entries in level 3 (PDPT) and level 2 19 | /// (PDT) tables. 20 | static uint64_t 21 | get_pdflags(uint32_t memtype) 22 | { 23 | switch (memtype) 24 | { 25 | case PMEMTYPE_ACPI_NVS: 26 | case PMEMTYPE_UNCACHED: 27 | return PF_PRESENT | PF_GLOBAL | PF_SYSTEM | 28 | PF_RW | PF_PS | PF_PWT | PF_PCD; 29 | 30 | case PMEMTYPE_BAD: 31 | case PMEMTYPE_UNMAPPED: 32 | return 0; 33 | 34 | case PMEMTYPE_USABLE: 35 | case PMEMTYPE_RESERVED: 36 | case PMEMTYPE_ACPI: 37 | return PF_PRESENT | PF_GLOBAL | PF_SYSTEM | PF_RW | PF_PS; 38 | 39 | default: 40 | fatal(); 41 | return 0; 42 | } 43 | } 44 | 45 | /// Return flags for entries in level 1 (PT) tables. 46 | static uint64_t 47 | get_ptflags(uint32_t memtype) 48 | { 49 | switch (memtype) 50 | { 51 | case PMEMTYPE_ACPI_NVS: 52 | case PMEMTYPE_UNCACHED: 53 | return PF_PRESENT | PF_GLOBAL | PF_SYSTEM | 54 | PF_RW | PF_PWT | PF_PCD; 55 | 56 | case PMEMTYPE_BAD: 57 | case PMEMTYPE_UNMAPPED: 58 | return 0; 59 | 60 | case PMEMTYPE_USABLE: 61 | case PMEMTYPE_RESERVED: 62 | case PMEMTYPE_ACPI: 63 | return PF_PRESENT | PF_GLOBAL | PF_SYSTEM | PF_RW; 64 | 65 | default: 66 | fatal(); 67 | return 0; 68 | } 69 | } 70 | 71 | /// Allocate the next available page in the kernel page table and return 72 | /// its virtual address. 73 | static inline uint64_t 74 | alloc_page(pagetable_t *pt) 75 | { 76 | if (pt->vnext >= pt->vterm) 77 | fatal(); 78 | 79 | uint64_t vaddr = pt->vnext; 80 | pt->vnext += PAGE_SIZE; 81 | return vaddr | PF_SYSTEM | PF_PRESENT | PF_RW; 82 | } 83 | 84 | /// Create a 1GiB page entry in the kernel page table. 85 | static void 86 | create_huge_page(pagetable_t *pt, uint64_t addr, uint32_t memtype) 87 | { 88 | uint64_t pml4te = PML4E(addr); 89 | uint64_t pdpte = PDPTE(addr); 90 | 91 | page_t *pml4t = (page_t *)pt->proot; 92 | if (pml4t->entry[pml4te] == 0) 93 | pml4t->entry[pml4te] = alloc_page(pt); 94 | 95 | page_t *pdpt = PGPTR(pml4t->entry[pml4te]); 96 | pdpt->entry[pdpte] = addr | get_pdflags(memtype); 97 | } 98 | 99 | /// Create a 2MiB page entry in the kernel page table. 100 | static void 101 | create_large_page(pagetable_t *pt, uint64_t addr, uint32_t memtype) 102 | { 103 | uint64_t pml4te = PML4E(addr); 104 | uint64_t pdpte = PDPTE(addr); 105 | uint64_t pde = PDE(addr); 106 | 107 | page_t *pml4t = (page_t *)pt->proot; 108 | if (pml4t->entry[pml4te] == 0) 109 | pml4t->entry[pml4te] = alloc_page(pt); 110 | 111 | page_t *pdpt = PGPTR(pml4t->entry[pml4te]); 112 | if (pdpt->entry[pdpte] == 0) 113 | pdpt->entry[pdpte] = alloc_page(pt); 114 | 115 | page_t *pdt = PGPTR(pdpt->entry[pdpte]); 116 | pdt->entry[pde] = addr | get_pdflags(memtype); 117 | } 118 | 119 | /// Create a 4KiB page entry in the kernel page table. 120 | static void 121 | create_small_page(pagetable_t *pt, uint64_t addr, uint32_t memtype) 122 | { 123 | uint64_t pml4te = PML4E(addr); 124 | uint64_t pdpte = PDPTE(addr); 125 | uint64_t pde = PDE(addr); 126 | uint64_t pte = PTE(addr); 127 | 128 | page_t *pml4t = (page_t *)pt->proot; 129 | if (pml4t->entry[pml4te] == 0) 130 | pml4t->entry[pml4te] = alloc_page(pt); 131 | 132 | page_t *pdpt = PGPTR(pml4t->entry[pml4te]); 133 | if (pdpt->entry[pdpte] == 0) 134 | pdpt->entry[pdpte] = alloc_page(pt); 135 | 136 | page_t *pdt = PGPTR(pdpt->entry[pdpte]); 137 | if (pdt->entry[pde] == 0) 138 | pdt->entry[pde] = alloc_page(pt); 139 | 140 | page_t *ptt = PGPTR(pdt->entry[pde]); 141 | ptt->entry[pte] = addr | get_ptflags(memtype); 142 | } 143 | 144 | /// Map a region of memory into the kernel page table, using the largest 145 | /// page sizes possible. 146 | static void 147 | map_region(pagetable_t *pt, const pmap_t *map, const pmapregion_t *region) 148 | { 149 | // Don't map bad (or unmapped) memory. 150 | if (region->type == PMEMTYPE_UNMAPPED || region->type == PMEMTYPE_BAD) 151 | return; 152 | 153 | // Don't map reserved regions beyond the last usable physical address. 154 | if (region->type == PMEMTYPE_RESERVED && 155 | region->addr >= map->last_usable) 156 | return; 157 | 158 | uint64_t addr = region->addr; 159 | uint64_t term = region->addr + region->size; 160 | 161 | // Create a series of pages that cover the region. Try to use the largest 162 | // page sizes possible to keep the page table small. 163 | while (addr < term) { 164 | uint64_t remain = term - addr; 165 | 166 | // Create a huge page (1GiB) if possible. 167 | if ((addr & (PAGE_SIZE_HUGE - 1)) == 0 && 168 | (remain >= PAGE_SIZE_HUGE)) { 169 | create_huge_page(pt, addr, region->type); 170 | addr += PAGE_SIZE_HUGE; 171 | } 172 | 173 | // Create a large page (2MiB) if possible. 174 | else if ((addr & (PAGE_SIZE_LARGE - 1)) == 0 && 175 | (remain >= PAGE_SIZE_LARGE)) { 176 | create_large_page(pt, addr, region->type); 177 | addr += PAGE_SIZE_LARGE; 178 | } 179 | 180 | // Create a small page (4KiB). 181 | else { 182 | create_small_page(pt, addr, region->type); 183 | addr += PAGE_SIZE; 184 | } 185 | } 186 | } 187 | 188 | void 189 | kmem_init(pagetable_t *pt) 190 | { 191 | // Zero all kernel page table memory. 192 | memzero((void *)KMEM_KERNEL_PAGETABLE, KMEM_KERNEL_PAGETABLE_SIZE); 193 | 194 | // Initialize the kernel page table. 195 | pt->proot = KMEM_KERNEL_PAGETABLE; 196 | pt->vroot = KMEM_KERNEL_PAGETABLE; 197 | pt->vnext = KMEM_KERNEL_PAGETABLE + PAGE_SIZE; 198 | pt->vterm = KMEM_KERNEL_PAGETABLE_END; 199 | 200 | // For each region in the physical memory map, create appropriate page 201 | // table entries. 202 | const pmap_t *map = pmap(); 203 | for (uint64_t r = 0; r < map->count; r++) 204 | map_region(pt, map, &map->region[r]); 205 | } 206 | -------------------------------------------------------------------------------- /kernel/mem/kmem.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file kmem.h 3 | /// @brief Kernel physical (and virtual) memory map. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #include 11 | 12 | // Kernel physical (and virtual) memory layout 13 | #define KMEM_IDT 0x00001000 14 | #define KMEM_ISR_TABLE 0x00002000 15 | #define KMEM_ISR_THUNKS 0x00002800 16 | #define KMEM_GDT 0x00003000 17 | #define KMEM_TSS 0x00003100 18 | #define KMEM_GLOBALS 0x00003200 19 | #define KMEM_BOOT_PAGETABLE 0x00010000 20 | #define KMEM_BOOT_PAGETABLE_LOADED 0x00014000 21 | #define KMEM_BOOT_PAGETABLE_END 0x00020000 22 | #define KMEM_KERNEL_PAGETABLE 0x00020000 23 | #define KMEM_KERNEL_PAGETABLE_END 0x00070000 24 | #define KMEM_TABLE_BIOS 0x00070000 25 | #define KMEM_STACK_NMI_BOTTOM 0x0008a000 26 | #define KMEM_STACK_NMI_TOP 0x0008c000 27 | #define KMEM_STACK_DF_BOTTOM 0x0008c000 28 | #define KMEM_STACK_DF_TOP 0x0008e000 29 | #define KMEM_STACK_MC_BOTTOM 0x0008e000 30 | #define KMEM_STACK_MC_TOP 0x00090000 31 | #define KMEM_EXTENDED_BIOS 0x0009f800 32 | #define KMEM_VIDEO 0x000a0000 33 | #define KMEM_SYSTEM_ROM 0x000c0000 34 | #define KMEM_STACK_INTERRUPT_BOTTOM 0x00100000 35 | #define KMEM_STACK_INTERRUPT_TOP 0x00200000 36 | #define KMEM_STACK_KERNEL_BOTTOM 0x00200000 37 | #define KMEM_STACK_KERNEL_TOP 0x00300000 38 | #define KMEM_KERNEL_IMAGE 0x00300000 39 | #define KMEM_KERNEL_ENTRYPOINT 0x00301000 40 | #define KMEM_KERNEL_IMAGE_END 0x00a00000 41 | 42 | #define KMEM_EXTENDED_BIOS_SIZE 0x00000800 43 | #define KMEM_VIDEO_SIZE 0x00020000 44 | #define KMEM_SYSTEM_ROM_SIZE 0x00040000 45 | #define KMEM_KERNEL_PAGETABLE_SIZE 0x00050000 46 | 47 | //---------------------------------------------------------------------------- 48 | // @function kmem_init 49 | /// @brief Using the contents of the physical memory map, identity 50 | /// map all physical memory into the kernel's page table. 51 | /// @param[inout] pt The pagetable structure to hold a description of the 52 | /// kernel's page table. 53 | //---------------------------------------------------------------------------- 54 | void 55 | kmem_init(pagetable_t *pt); 56 | -------------------------------------------------------------------------------- /kernel/mem/pmap.c: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file pmap.c 3 | /// @brief Physical memory map describing usable and reserved regions 4 | /// of physical memory. 5 | /// @details Before this code is ever executed, the boot code has filled 6 | /// much of the memory map with memory regions supplied by the 7 | /// BIOS. 8 | // 9 | // Copyright 2016 Brett Vickers. 10 | // Use of this source code is governed by a BSD-style license that can 11 | // be found in the MonkOS LICENSE file. 12 | //============================================================================ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "kmem.h" 19 | 20 | // Pointer to the BIOS-generated memory map. 21 | static pmap_t *map = (pmap_t *)KMEM_TABLE_BIOS; 22 | static bool initialized = false; 23 | 24 | /// Add a memory region to the end of the memory map. 25 | static void 26 | add_region(uint64_t addr, uint64_t size, enum pmemtype type) 27 | { 28 | pmapregion_t *r = map->region + map->count; 29 | r->addr = addr; 30 | r->size = size; 31 | r->type = (int32_t)type; 32 | r->flags = 0; 33 | 34 | ++map->count; 35 | } 36 | 37 | /// Compare two memory region records and return a sorting integer. 38 | static int 39 | cmp_region(const void *a, const void *b) 40 | { 41 | const pmapregion_t *r1 = (const pmapregion_t *)a; 42 | const pmapregion_t *r2 = (const pmapregion_t *)b; 43 | if (r1->addr > r2->addr) 44 | return +1; 45 | if (r1->addr < r2->addr) 46 | return -1; 47 | if (r1->size > r2->size) 48 | return +1; 49 | if (r1->size < r2->size) 50 | return -1; 51 | return 0; 52 | } 53 | 54 | /// Remove a region from the memory map and shift all subsequent regions 55 | /// down by one. 56 | static inline void 57 | collapse(pmapregion_t *r, pmapregion_t *term) 58 | { 59 | if (r + 1 < term) 60 | memmove(r, r + 1, (term - r) * sizeof(pmapregion_t)); 61 | --map->count; 62 | } 63 | 64 | /// Insert a new, uninitialized memory region record after an existing record 65 | /// in the memory map. 66 | static inline void 67 | insertafter(pmapregion_t *r, pmapregion_t *term) 68 | { 69 | if (r + 1 < term) 70 | memmove(r + 2, r + 1, (term - (r + 1)) * sizeof(pmapregion_t)); 71 | ++map->count; 72 | } 73 | 74 | /// Re-sort all unsorted region records starting from the requested record. 75 | static void 76 | resort(pmapregion_t *r, pmapregion_t *term) 77 | { 78 | while (r + 1 < term) { 79 | if (cmp_region(r, r + 1) < 0) 80 | break; 81 | pmapregion_t tmp = r[0]; 82 | r[0] = r[1]; 83 | r[1] = tmp; 84 | r++; 85 | } 86 | } 87 | 88 | /// Find all overlapping memory regions in the memory map and collapse or 89 | /// reorganize them. 90 | static void 91 | collapse_overlaps() 92 | { 93 | pmapregion_t *curr = map->region; 94 | pmapregion_t *term = map->region + map->count; 95 | 96 | while (curr + 1 < term) { 97 | 98 | // Collapse empty entries. 99 | if (curr->size == 0) { 100 | collapse(curr, term--); 101 | continue; 102 | } 103 | 104 | pmapregion_t *next = curr + 1; 105 | 106 | uint64_t cl = curr->addr; 107 | uint64_t cr = curr->addr + curr->size; 108 | uint64_t nl = next->addr; 109 | uint64_t nr = next->addr + next->size; 110 | 111 | // No overlap? Then go to the next region. 112 | if (min(cr, nr) <= max(cl, nl)) { 113 | curr++; 114 | continue; 115 | } 116 | 117 | // Handle the 5 alignment cases: 118 | // xxx xxx xxxx xxx xxxxx 119 | // yyy yyyy yyy yyy yyy 120 | // The remaining cases are impossible due to sorting. 121 | 122 | if (cl == nl) { 123 | if (cr == nr) { 124 | if (next->type > curr->type) { 125 | // 111 -> 222 126 | // 222 127 | collapse(curr, term--); 128 | } 129 | else { 130 | // 222 -> 222 131 | // 111 132 | collapse(next, term--); 133 | } 134 | } 135 | else { /* if cr != nr */ 136 | if (next->type > curr->type) { 137 | // 111 -> 2222 138 | // 2222 139 | collapse(curr, term--); 140 | } 141 | else { 142 | // 222 -> 222 143 | // 1111 -> 1 144 | next->size = nr - cr; 145 | next->addr = cr; 146 | resort(next, term); 147 | } 148 | } 149 | } 150 | 151 | else { /* if cl != nl */ 152 | if (cr == nr) { 153 | if (next->type > curr->type) { 154 | // 1111 -> 1 155 | // 222 -> 222 156 | curr->size = nl - cl; 157 | } 158 | else { 159 | // 2222 -> 2222 160 | // 111 161 | collapse(next, term--); 162 | } 163 | } 164 | else if (cr < nr) { 165 | if (next->type > curr->type) { 166 | // 1111 -> 1 167 | // 2222 -> 2222 168 | curr->size = nl - cl; 169 | } 170 | else { 171 | // 2222 -> 2222 172 | // 1111 -> 1 173 | next->size = nr - cr; 174 | next->addr = cr; 175 | resort(next, term); 176 | } 177 | } 178 | else { /* if cr > nr */ 179 | if (next->type > curr->type) { 180 | // 11111 -> 1 181 | // 222 -> 222 182 | // -> 1 183 | curr->size = nl - cl; 184 | insertafter(next, term++); 185 | next[1].addr = nr; 186 | next[1].size = cr - nr; 187 | next[1].type = curr->type; 188 | next[1].flags = curr->flags; 189 | resort(next + 1, term); 190 | } 191 | else { 192 | // 22222 -> 22222 193 | // 111 194 | collapse(next, term--); 195 | } 196 | } 197 | } 198 | 199 | } 200 | } 201 | 202 | /// Find missing memory regions in the map, and fill them with entries of 203 | /// the requested type. 204 | static void 205 | fill_gaps(int32_t type) 206 | { 207 | pmapregion_t *curr = map->region; 208 | pmapregion_t *term = map->region + map->count; 209 | 210 | while (curr + 1 < term) { 211 | pmapregion_t *next = curr + 1; 212 | 213 | uint64_t cr = curr->addr + curr->size; 214 | uint64_t nl = next->addr; 215 | 216 | if (cr == nl) { 217 | curr++; 218 | continue; 219 | } 220 | 221 | // Try to expand one of the neighboring entries if one of them has the 222 | // same type as the fill type. 223 | if (curr->type == type) { 224 | curr->size += nl - cr; 225 | } 226 | else if (next->type == type) { 227 | next->size += nl - cr; 228 | next->addr = cr; 229 | } 230 | 231 | // Neither neighboring region has the fill type, so insert a new 232 | // region record. 233 | else { 234 | insertafter(curr, term++); 235 | next->addr = cr; 236 | next->size = nl - cr; 237 | next->type = type; 238 | next->flags = 0; 239 | } 240 | } 241 | } 242 | 243 | /// Find adjacent memory entries of the same type and merge them. 244 | static void 245 | consolidate_neighbors() 246 | { 247 | pmapregion_t *curr = map->region; 248 | pmapregion_t *term = map->region + map->count; 249 | 250 | while (curr + 1 < term) { 251 | pmapregion_t *next = curr + 1; 252 | if (curr->type == next->type) { 253 | curr->size += next->size; 254 | collapse(next, term--); 255 | } 256 | else { 257 | curr++; 258 | } 259 | } 260 | } 261 | 262 | static void 263 | update_last_usable() 264 | { 265 | map->last_usable = 0; 266 | for (int i = map->count - 1; i >= 0; i--) { 267 | const pmapregion_t *r = &map->region[i]; 268 | if (r->type == PMEMTYPE_USABLE) { 269 | map->last_usable = r->addr + r->size; 270 | break; 271 | } 272 | } 273 | } 274 | 275 | static void 276 | normalize() 277 | { 278 | // Sort the memory map by address. 279 | qsort(map->region, map->count, sizeof(pmapregion_t), 280 | cmp_region); 281 | 282 | // Remove overlapping regions, fill gaps between regions with "reserved" 283 | // memory, squash adjacent regions of the same type, and calculate the end 284 | // of the last usable memory region. 285 | collapse_overlaps(); 286 | fill_gaps(PMEMTYPE_RESERVED); 287 | consolidate_neighbors(); 288 | update_last_usable(); 289 | } 290 | 291 | void 292 | pmap_init() 293 | { 294 | // During the boot process, the physical memory map at KMEM_TABLE_BIOS has 295 | // been updated to include memory regions reported by the BIOS. This 296 | // function cleans up the BIOS memory map (sorts it, removes overlaps, 297 | // etc.) and adds a few additional memory regions. 298 | 299 | // Mark VGA video memory as uncached. 300 | add_region(KMEM_VIDEO, KMEM_VIDEO_SIZE, PMEMTYPE_UNCACHED); 301 | 302 | // Reserve memory for the kernel and its global data structures. 303 | add_region(0, KMEM_KERNEL_IMAGE_END, PMEMTYPE_RESERVED); 304 | 305 | // Mark the first page of memory as unmapped so deferencing a null pointer 306 | // always faults. 307 | add_region(0, 0x1000, PMEMTYPE_UNMAPPED); 308 | 309 | // Fix up the memory map. 310 | normalize(); 311 | 312 | initialized = true; 313 | } 314 | 315 | const pmap_t * 316 | pmap() 317 | { 318 | return map; 319 | } 320 | 321 | void 322 | pmap_add(uint64_t addr, uint64_t size, enum pmemtype type) 323 | { 324 | add_region(addr, size, type); 325 | 326 | if (initialized) 327 | normalize(); 328 | } 329 | -------------------------------------------------------------------------------- /kernel/shell.c: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file shell.c 3 | /// @brief Simple kernel shell for testing purposes. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #define TTY_CONSOLE 0 23 | 24 | // Forward declarations 25 | static void command_prompt(); 26 | static void command_run(); 27 | static void keycode_run(); 28 | static bool cmd_display_help(); 29 | static bool cmd_display_apic(); 30 | static bool cmd_display_pci(); 31 | static bool cmd_display_pcie(); 32 | static bool cmd_switch_to_keycodes(); 33 | static bool cmd_test_heap(); 34 | 35 | /// Shell mode descriptor. 36 | typedef struct mode 37 | { 38 | void (*start)(); 39 | void (*run)(); 40 | void (*stop)(); 41 | } mode_t; 42 | 43 | // Standard shell command mode 44 | static mode_t mode_command = 45 | { 46 | command_prompt, 47 | command_run, 48 | NULL 49 | }; 50 | 51 | // Keycode display mode 52 | static mode_t mode_keycode = 53 | { 54 | NULL, 55 | keycode_run, 56 | NULL 57 | }; 58 | 59 | static mode_t *active_mode; 60 | 61 | static void 62 | switch_mode(mode_t *mode) 63 | { 64 | if (active_mode->stop != NULL) 65 | active_mode->stop(); 66 | 67 | active_mode = mode; 68 | 69 | if (active_mode->start != NULL) 70 | active_mode->start(); 71 | } 72 | 73 | /// A command descriptor, describing each command accepted in command mode. 74 | struct cmd 75 | { 76 | const char *str; 77 | const char *help; 78 | bool (*run)(); 79 | }; 80 | 81 | static struct cmd commands[] = 82 | { 83 | { "?", NULL, cmd_display_help }, 84 | { "help", "Show this help text", cmd_display_help }, 85 | { "apic", "Show APIC configuration", cmd_display_apic }, 86 | { "pci", "Show PCI devices", cmd_display_pci }, 87 | { "pcie", "Show PCIexpress configuration", cmd_display_pcie }, 88 | { "kc", "Switch to keycode display mode", cmd_switch_to_keycodes }, 89 | { "heap", "Test heap allocation", cmd_test_heap }, 90 | }; 91 | 92 | static int 93 | cmp_cmds(const void *c1, const void *c2) 94 | { 95 | return strcmp(((const struct cmd *)c1)->str, 96 | ((const struct cmd *)c2)->str); 97 | } 98 | 99 | static bool 100 | cmd_display_help() 101 | { 102 | tty_print(TTY_CONSOLE, "Available commands:\n"); 103 | for (int i = 0; i < arrsize(commands); i++) { 104 | if (commands[i].help == NULL) 105 | continue; 106 | tty_printf(TTY_CONSOLE, " %-8s %s\n", 107 | commands[i].str, commands[i].help); 108 | } 109 | return true; 110 | } 111 | 112 | static bool 113 | cmd_display_apic() 114 | { 115 | const struct acpi_madt *madt = acpi_madt(); 116 | if (madt == NULL) { 117 | tty_print(TTY_CONSOLE, "No ACPI MADT detected.\n"); 118 | return true; 119 | } 120 | 121 | tty_printf(TTY_CONSOLE, "Local APIC addr: %#x\n", madt->ptr_local_apic); 122 | 123 | const struct acpi_madt_local_apic *local = NULL; 124 | while ((local = acpi_next_local_apic(local)) != NULL) { 125 | tty_printf(TTY_CONSOLE, "Local APIC id %u: %s\n", 126 | local->apicid, 127 | (local->flags & 1) ? "Usable" : "Unusable"); 128 | } 129 | 130 | const struct acpi_madt_io_apic *io = NULL; 131 | while ((io = acpi_next_io_apic(io)) != NULL) { 132 | tty_printf(TTY_CONSOLE, "I/O APIC id %u: Addr=%#x Base=%u\n", 133 | io->apicid, 134 | io->ptr_io_apic, 135 | io->interrupt_base); 136 | } 137 | 138 | const struct acpi_madt_iso *iso = NULL; 139 | while ((iso = acpi_next_iso(iso)) != NULL) { 140 | tty_printf(TTY_CONSOLE, "ISO irq=%-2u int=%-2u flags=0x%04x\n", 141 | iso->source, 142 | iso->interrupt, 143 | iso->flags); 144 | } 145 | 146 | return true; 147 | } 148 | 149 | static bool 150 | cmd_display_pci() 151 | { 152 | pci_init(); // Temporary 153 | return true; 154 | } 155 | 156 | static bool 157 | cmd_display_pcie() 158 | { 159 | const struct acpi_mcfg_addr *addr = acpi_next_mcfg_addr(NULL); 160 | if (addr == NULL) { 161 | tty_print(TTY_CONSOLE, "No PCIe configuration.\n"); 162 | return true; 163 | } 164 | 165 | while (addr != NULL) { 166 | tty_printf(TTY_CONSOLE, "PCIe addr=0x%08x grp=%-2u bus=%02x..%02x\n", 167 | addr->base, addr->seg_group, addr->bus_start, 168 | addr->bus_end); 169 | addr = acpi_next_mcfg_addr(addr); 170 | } 171 | 172 | return true; 173 | } 174 | 175 | static bool 176 | cmd_switch_to_keycodes() 177 | { 178 | tty_print(TTY_CONSOLE, 179 | "Entering keycode mode. Hit Alt-Tab to exit.\n"); 180 | switch_mode(&mode_keycode); 181 | return false; 182 | } 183 | 184 | static bool 185 | cmd_test_heap() 186 | { 187 | pagetable_t pt; 188 | pagetable_create(&pt, (void *)0x8000000000, PAGE_SIZE * 1024); 189 | pagetable_activate(&pt); 190 | 191 | struct heap *heap = heap_create(&pt, (void *)0x9000000000, 1024); 192 | void *ptr1 = heap_alloc(heap, 128); 193 | void *ptr2 = heap_alloc(heap, 0xff00); 194 | void *ptr3 = heap_alloc(heap, 8); 195 | heap_free(heap, ptr1); 196 | heap_free(heap, ptr2); 197 | heap_free(heap, ptr3); 198 | 199 | heap_destroy(heap); 200 | pagetable_activate(NULL); 201 | pagetable_destroy(&pt); 202 | return true; 203 | } 204 | 205 | static bool 206 | command_exec(const char *cmd) 207 | { 208 | if (cmd[0] == 0) 209 | return true; 210 | 211 | for (int i = 0; i < arrsize(commands); i++) { 212 | if (!strcmp(commands[i].str, cmd)) 213 | return commands[i].run(); 214 | } 215 | 216 | tty_printf(TTY_CONSOLE, "Unknown command: %s\n", cmd); 217 | return true; 218 | } 219 | 220 | static void 221 | command_prompt() 222 | { 223 | tty_print(TTY_CONSOLE, "> "); 224 | } 225 | 226 | static void 227 | command_run() 228 | { 229 | char cmd[256]; 230 | int cmdlen = 0; 231 | 232 | for (;;) { 233 | halt(); 234 | 235 | key_t key; 236 | bool avail; 237 | while ((avail = kb_getkey(&key)) != false) { 238 | 239 | // If a printable character was typed, append it to the command. 240 | if (key.ch >= 32 && key.ch < 127) { 241 | if (cmdlen < arrsize(cmd) - 1) { 242 | cmd[cmdlen] = key.ch; 243 | tty_printc(TTY_CONSOLE, cmd[cmdlen]); 244 | cmdlen++; 245 | } 246 | } 247 | 248 | // Handle special keys (like enter, backspace). 249 | else if (key.brk == KEYBRK_DOWN) { 250 | 251 | if (key.code == KEY_ENTER) { 252 | tty_printc(TTY_CONSOLE, '\n'); 253 | 254 | // Strip trailing whitespace. 255 | while (cmdlen > 0 && cmd[cmdlen - 1] == ' ') 256 | cmdlen--; 257 | cmd[cmdlen] = 0; 258 | 259 | // Execute the command. 260 | bool cont = command_exec(cmd); 261 | cmdlen = 0; 262 | if (cont) 263 | command_prompt(); 264 | else 265 | return; 266 | } 267 | 268 | else if (key.code == KEY_BACKSPACE && cmdlen > 0) { 269 | tty_printc(TTY_CONSOLE, '\b'); 270 | cmdlen--; 271 | } 272 | 273 | } 274 | } 275 | } 276 | } 277 | 278 | static void 279 | keycode_run() 280 | { 281 | for (;;) { 282 | halt(); 283 | 284 | key_t key; 285 | bool avail; 286 | while ((avail = kb_getkey(&key)) != false) { 287 | if (key.ch) { 288 | tty_printf( 289 | TTY_CONSOLE, 290 | "Keycode: \033[%c]%02x\033[-] meta=%02x '%c'\n", 291 | key.brk == KEYBRK_UP ? 'e' : '2', 292 | key.code, 293 | key.meta, 294 | key.ch); 295 | } 296 | else { 297 | tty_printf( 298 | TTY_CONSOLE, 299 | "Keycode: \033[%c]%02x\033[-] meta=%02x\n", 300 | key.brk == KEYBRK_UP ? 'e' : '2', 301 | key.code, 302 | key.meta); 303 | } 304 | if ((key.brk == KEYBRK_UP) && (key.meta & META_ALT) && 305 | (key.code == KEY_TAB)) { 306 | switch_mode(&mode_command); 307 | return; 308 | } 309 | } 310 | } 311 | } 312 | 313 | void 314 | kshell() 315 | { 316 | qsort(commands, arrsize(commands), sizeof(struct cmd), cmp_cmds); 317 | 318 | active_mode = &mode_command; 319 | active_mode->start(); 320 | for (;;) 321 | active_mode->run(); 322 | } 323 | -------------------------------------------------------------------------------- /kernel/shell.h: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file shell.h 3 | /// @brief Simple kernel shell for testing purposes. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #pragma once 11 | 12 | void 13 | kshell(); 14 | -------------------------------------------------------------------------------- /kernel/start.asm: -------------------------------------------------------------------------------- 1 | ;============================================================================= 2 | ; @file start.asm 3 | ; @brief The kernel launcher. 4 | ; @details The boot loader launches the kernel by jumping to _start. 5 | ; 6 | ; Copyright 2016 Brett Vickers. 7 | ; Use of this source code is governed by a BSD-style license that can 8 | ; be found in the MonkOS LICENSE file. 9 | ;============================================================================= 10 | 11 | ; The boot loader should have put us in 64-bit long mode. 12 | bits 64 13 | 14 | ; Include boot loader's memory layout. 15 | %include "../boot/include/mem.inc" 16 | 17 | ; Use a special section .start, which comes first in the linker.ld .text 18 | ; section. This way, the _start label will be given the lowest possible code 19 | ; address (0x00301000 in our case). 20 | section .start 21 | global _start 22 | 23 | extern kmain ; Exported by main.c 24 | extern memzero ; Exported by strings.asm 25 | extern _BSS_START ; Linker-generated symbol 26 | extern _BSS_SIZE ; Linker-generated symbol 27 | 28 | ;----------------------------------------------------------------------------- 29 | ; @function _start 30 | ; @brief Kernel entry point, called by the boot loader. 31 | ;----------------------------------------------------------------------------- 32 | _start: 33 | 34 | ; The System V ABI requires the direction flag to be clear on function 35 | ; entry. 36 | cld 37 | 38 | ; Zero out the stage-2 loader now that it's done running. 39 | mov rdi, Mem.Loader2 40 | mov rsi, Mem.Loader2.Size 41 | call memzero 42 | 43 | ; Zero out the kernel's bss section. 44 | mov rdi, _BSS_START 45 | mov rsi, _BSS_SIZE 46 | call memzero 47 | 48 | ; Call the kernel's main entry point. This function should never return. 49 | call kmain 50 | 51 | ; If the function does return for some reason, hang the computer. 52 | .hang: 53 | cli 54 | hlt 55 | jmp .hang 56 | -------------------------------------------------------------------------------- /kernel/syscall/syscall.c: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file syscall.c 3 | /// @brief System call support. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | // Model-specific registers used to set up system calls. 18 | #define MSR_IA32_STAR 0xc0000081 19 | #define MSR_IA32_LSTAR 0xc0000082 20 | #define MSR_IA32_FMASK 0xc0000084 21 | 22 | static void 23 | syscall_handle() 24 | { 25 | // Do nothing for now. 26 | } 27 | 28 | void 29 | syscall_init() 30 | { 31 | // Request the CPU's extended features. 32 | registers4_t regs; 33 | cpuid(0x80000001, ®s); 34 | 35 | // Bit 11 of rdx tells us if the SYSCALL/SYSRET instructions are 36 | // available. If they're not, raise an invalid opcode exception. 37 | if (!(regs.rdx & (1 << 11))) { 38 | invalid_opcode(); 39 | } 40 | 41 | // Update the IA32_STAR MSR with the segment selectors that will be used 42 | // by SYSCALL and SYSRET. 43 | uint64_t star = rdmsr(MSR_IA32_STAR); 44 | star &= 0x00000000ffffffff; 45 | star |= (uint64_t)SEGMENT_SELECTOR_KERNEL_CODE << 32; 46 | star |= (uint64_t)((SEGMENT_SELECTOR_USER_CODE - 16) | 3) << 48; 47 | wrmsr(MSR_IA32_STAR, star); 48 | 49 | // Write the address of the system call handler used by SYSCALL. 50 | wrmsr(MSR_IA32_LSTAR, (uint64_t)syscall_handle); 51 | 52 | // Write the CPU flag mask used during SYSCALL. 53 | wrmsr(MSR_IA32_FMASK, 0); 54 | } 55 | -------------------------------------------------------------------------------- /kernel/x86/cpu.asm: -------------------------------------------------------------------------------- 1 | ;============================================================================= 2 | ; @file asm.asm 3 | ; @brief x86 CPU-specific function implementations. 4 | ; @brief x86 CPU assembly code helpers. 5 | ; @details These implementations are for builds performed without inline 6 | ; assembly. 7 | ; 8 | ; Copyright 2016 Brett Vickers. 9 | ; Use of this source code is governed by a BSD-style license that can 10 | ; be found in the MonkOS LICENSE file. 11 | ;============================================================================= 12 | 13 | bits 64 14 | 15 | section .text 16 | 17 | global cpuid 18 | global rdmsr 19 | global wrmsr 20 | global io_inb 21 | global io_outb 22 | global io_inw 23 | global io_outw 24 | global io_ind 25 | global io_outd 26 | global set_pagetable 27 | global invalidate_page 28 | global enable_interrupts 29 | global disable_interrupts 30 | global halt 31 | global invalid_opcode 32 | global fatal 33 | 34 | ;----------------------------------------------------------------------------- 35 | ; @function cpuid 36 | ; @brief Return the results of the CPUID instruction. 37 | ; @reg[in] rdi The cpuid group code. 38 | ; @reg[in] rsi pointer to a registers4_t struct. 39 | ;----------------------------------------------------------------------------- 40 | cpuid: 41 | 42 | push rbx 43 | 44 | mov rax, rdi 45 | cpuid 46 | 47 | mov [rsi + 8 * 0], rax 48 | mov [rsi + 8 * 1], rbx 49 | mov [rsi + 8 * 2], rcx 50 | mov [rsi + 8 * 3], rdx 51 | 52 | pop rbx 53 | ret 54 | 55 | ;----------------------------------------------------------------------------- 56 | ; @function rdmsr 57 | ; @brief Read the model-specific register and return the result. 58 | ; @reg[in] rdi The MSR register id to read. 59 | ; @reg[out] rax The contents of the requested MSR. 60 | ;----------------------------------------------------------------------------- 61 | rdmsr: 62 | 63 | mov rcx, rdi 64 | 65 | rdmsr 66 | 67 | shl rdx, 32 68 | or rax, rdx 69 | ret 70 | 71 | 72 | ;----------------------------------------------------------------------------- 73 | ; @function wrmsr 74 | ; @brief Write to the model-specific register. 75 | ; @reg[in] rdi The MSR register id to write. 76 | ; @reg[in] rsi The value to write. 77 | ;----------------------------------------------------------------------------- 78 | wrmsr: 79 | 80 | mov ecx, edi 81 | 82 | mov rax, rsi 83 | mov rdx, rax 84 | shr rdx, 32 85 | 86 | wrmsr 87 | 88 | ret 89 | 90 | ;----------------------------------------------------------------------------- 91 | ; @function io_inb 92 | ; @brief Retrieve a byte value from an input port. 93 | ; @reg[in] rdi Port number (0-65535). 94 | ; @reg[out] rax Byte value read from the port. 95 | ;----------------------------------------------------------------------------- 96 | io_inb: 97 | 98 | mov dx, di 99 | xor rax, rax 100 | in al, dx 101 | ret 102 | 103 | ;----------------------------------------------------------------------------- 104 | ; @function io_outb 105 | ; @brief Write a byte value to an output port. 106 | ; @reg[in] rdi Port number (0-65535). 107 | ; @reg[in] rsi Byte value to write to the port. 108 | ;----------------------------------------------------------------------------- 109 | io_outb: 110 | 111 | mov dx, di 112 | mov ax, si 113 | out dx, al 114 | ret 115 | 116 | ;----------------------------------------------------------------------------- 117 | ; @function io_inw 118 | ; @brief Retrieve a 16-bit word value from an input port. 119 | ; @reg[in] rdi Port number (0-65535). 120 | ; @reg[out] rax Word value read from the port. 121 | ;----------------------------------------------------------------------------- 122 | io_inw: 123 | 124 | mov dx, di 125 | xor rax, rax 126 | in ax, dx 127 | ret 128 | 129 | ;----------------------------------------------------------------------------- 130 | ; @function io_outw 131 | ; @brief Write a 16-bit word value to an output port. 132 | ; @reg[in] rdi Port number (0-65535). 133 | ; @reg[in] rsi Word value to write to the port. 134 | ;----------------------------------------------------------------------------- 135 | io_outw: 136 | 137 | mov dx, di 138 | mov ax, si 139 | out dx, ax 140 | ret 141 | 142 | ;----------------------------------------------------------------------------- 143 | ; @function io_ind 144 | ; @brief Retrieve a 32-bit dword value from an input port. 145 | ; @reg[in] rdi Port number (0-65535). 146 | ; @reg[out] rax Dword value read from the port. 147 | ;----------------------------------------------------------------------------- 148 | io_ind: 149 | 150 | mov dx, di 151 | xor rax, rax 152 | in eax, dx 153 | ret 154 | 155 | ;----------------------------------------------------------------------------- 156 | ; @function io_outd 157 | ; @brief Write a 32-bit dword value to an output port. 158 | ; @reg[in] rdi Port number (0-65535). 159 | ; @reg[in] rsi Dord value to write to the port. 160 | ;----------------------------------------------------------------------------- 161 | io_outd: 162 | 163 | mov dx, di 164 | mov eax, esi 165 | out dx, eax 166 | ret 167 | 168 | ;----------------------------------------------------------------------------- 169 | ; @function set_pagetable 170 | ; @brief Update the CPU's page table register. 171 | ; @reg[in] rdi The physical address containing the new pagetable. 172 | ;----------------------------------------------------------------------------- 173 | set_pagetable: 174 | 175 | mov cr3, rdi 176 | ret 177 | 178 | ;----------------------------------------------------------------------------- 179 | ; @function invalidate_page 180 | ; @brief Invalidate the page containing a virtual address. 181 | ; @reg[in] rdi The virtual address of the page to invalidate. 182 | ;----------------------------------------------------------------------------- 183 | invalidate_page: 184 | 185 | invlpg [rdi] 186 | ret 187 | 188 | ;----------------------------------------------------------------------------- 189 | ; @function enable_interrupts 190 | ; @brief Enable interrupts. 191 | ;----------------------------------------------------------------------------- 192 | enable_interrupts: 193 | 194 | sti 195 | ret 196 | 197 | ;----------------------------------------------------------------------------- 198 | ; @function disable_interrupts 199 | ; @brief Disable interrupts. 200 | ;----------------------------------------------------------------------------- 201 | disable_interrupts: 202 | 203 | cli 204 | ret 205 | 206 | ;----------------------------------------------------------------------------- 207 | ; @function halt 208 | ; @brief Halt the CPU until an interrupt occurs. 209 | ;----------------------------------------------------------------------------- 210 | halt: 211 | 212 | hlt 213 | ret 214 | 215 | ;----------------------------------------------------------------------------- 216 | ; @function invalid_opcode 217 | ; @brief Raise an invalid opcode exception. 218 | ;----------------------------------------------------------------------------- 219 | invalid_opcode: 220 | 221 | int 6 222 | ret 223 | 224 | ;----------------------------------------------------------------------------- 225 | ; @function fatal 226 | ; @brief Raise a fatal interrupt that hangs the system. 227 | ;----------------------------------------------------------------------------- 228 | fatal: 229 | 230 | int 0xff 231 | ret 232 | -------------------------------------------------------------------------------- /libc/Makefile: -------------------------------------------------------------------------------- 1 | #---------------------------------------------------------------------------- 2 | # MonkOS libc makefile 3 | # 4 | # Makefile for the c library. 5 | #---------------------------------------------------------------------------- 6 | 7 | DIR_ROOT := .. 8 | LIB_NAME := libc 9 | LIB_DEPS := 10 | POST_LIB_RULE := 11 | 12 | include $(DIR_ROOT)/scripts/lib.mk 13 | -------------------------------------------------------------------------------- /libc/stdio/snprintf.c: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file snprintf.c 3 | /// @brief Write formatted output to a sized buffer. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #include 11 | 12 | int 13 | snprintf(char *buf, size_t n, const char *format, ...) 14 | { 15 | va_list args; 16 | va_start(args, format); 17 | int result = vsnprintf(buf, n, format, args); 18 | va_end(args); 19 | return result; 20 | } 21 | -------------------------------------------------------------------------------- /libc/stdlib/qsort.c: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file qsort.c 3 | /// @brief Quicksort algorithm 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #include 11 | #include 12 | 13 | void 14 | qsort(void *base, size_t num, size_t size, sortcmp cmp) 15 | { 16 | uint8_t pivot[size], tmp[size]; // use C99 VLAs instead of alloca. 17 | 18 | uint8_t *b = (uint8_t *)base; 19 | for (;;) { 20 | if (num < 2) { 21 | return; 22 | } 23 | 24 | // Use the first element as the pivot. 25 | memcpy(pivot, b, size); 26 | 27 | // Partition. 28 | size_t part; 29 | { 30 | // Work from outwards in (C.A.R. Hoare version of algorithm). 31 | uint8_t *i = b - (size * 1); 32 | uint8_t *j = b + (size * num); 33 | 34 | for (;;) { 35 | do { 36 | i += size; 37 | } while (cmp(i, pivot) < 0); 38 | 39 | do { 40 | j -= size; 41 | } while (cmp(j, pivot) > 0); 42 | 43 | if (i >= j) { 44 | part = (j - b) / size; 45 | break; 46 | } 47 | 48 | // Swap elements i and j. 49 | memcpy(tmp, i, size); 50 | memcpy(i, j, size); 51 | memcpy(j, tmp, size); 52 | } 53 | } 54 | 55 | // Recursively sort the left side of the partition. 56 | qsort(b, part + 1, size, cmp); 57 | 58 | // For the right side of the partition, do tail recursion. 59 | b += (part + 1) * size; 60 | num -= part + 1; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /libc/string/memcpy.asm: -------------------------------------------------------------------------------- 1 | ;============================================================================= 2 | ; @file memcpy.asm 3 | ; @brief Copy bytes from one memory region to another. 4 | ; 5 | ; Copyright 2016 Brett Vickers. 6 | ; Use of this source code is governed by a BSD-style license that can 7 | ; be found in the MonkOS LICENSE file. 8 | ;============================================================================= 9 | 10 | bits 64 11 | 12 | section .text 13 | 14 | global memcpy 15 | 16 | 17 | ;----------------------------------------------------------------------------- 18 | ; @function memcpy 19 | ; @brief Copy bytes from one memory region to another. 20 | ; @details If the memory regions overlap, this function's behavior 21 | ; is undefined, and you should use memmove instead. 22 | ; @reg[in] rdi Address of the destination memory area. 23 | ; @reg[in] rsi Address of the source memory area. 24 | ; @reg[in] rdx Number of bytes to copy. 25 | ; @reg[out] rax Destination address. 26 | ; @killedregs rcx 27 | ;----------------------------------------------------------------------------- 28 | memcpy: 29 | 30 | ; Preserve destination address because we have to return it. 31 | mov rax, rdi 32 | 33 | ; Do a byte-by-byte move. On modern x86 chips, there is not a significant 34 | ; performance difference between byte-wise and word-wise moves. In fact, 35 | ; sometimes movsb is faster. See instlatx64.atw.hu for benchmarks. 36 | mov rcx, rdx 37 | rep movsb 38 | 39 | ret 40 | -------------------------------------------------------------------------------- /libc/string/memmove.asm: -------------------------------------------------------------------------------- 1 | ;============================================================================= 2 | ; @file memmove.asm 3 | ; @brief Move bytes from one memory region to another. 4 | ; 5 | ; Copyright 2016 Brett Vickers. 6 | ; Use of this source code is governed by a BSD-style license that can 7 | ; be found in the MonkOS LICENSE file. 8 | ;============================================================================= 9 | 10 | bits 64 11 | 12 | section .text 13 | 14 | global memmove 15 | 16 | 17 | ;----------------------------------------------------------------------------- 18 | ; @function memmove 19 | ; @brief Move bytes from one memory region to another, even if the 20 | ; regions overlap. 21 | ; @reg[in] rdi Address of the destination memory area. 22 | ; @reg[in] rsi Address of the source memory area. 23 | ; @reg[in] rdx Number of bytes to copy. 24 | ; @reg[out] rax Destination address. 25 | ; @killedregs rcx 26 | ;----------------------------------------------------------------------------- 27 | memmove: 28 | 29 | ; Preserve destination address because we have to return it. 30 | mov rax, rdi 31 | 32 | ; If dest < src, we can always do a fast pointer-incrementing move. 33 | ; If dest == src, do nothing. 34 | cmp rdi, rsi 35 | je .done 36 | jb .fast 37 | 38 | ; If dest > src and there are no overlapping regions (dest >= src+num), we 39 | ; can still do a fast pointer-incrementing move. 40 | mov rcx, rsi 41 | add rcx, rdx 42 | cmp rdi, rcx 43 | jae .fast 44 | 45 | ; If dest > src and dest < src+num, we have to do a right-to-left move to 46 | ; preserve overlapping data. 47 | .slow: 48 | 49 | ; Set the direction flag so copying is right-to-left. 50 | std 51 | 52 | ; Set the move count register. 53 | mov rcx, rdx 54 | 55 | ; Update pointers to the right-hand side (minus one). 56 | dec rdx 57 | add rsi, rdx 58 | add rdi, rdx 59 | 60 | ; Do a byte-by-byte move. 61 | rep movsb 62 | 63 | ; Reset the direction flag. 64 | cld 65 | 66 | ret 67 | 68 | .fast: 69 | 70 | ; Do a byte-by-byte move. 71 | mov rcx, rdx 72 | rep movsb 73 | 74 | .done: 75 | 76 | ret 77 | -------------------------------------------------------------------------------- /libc/string/memset.asm: -------------------------------------------------------------------------------- 1 | ;============================================================================= 2 | ; @file memset.asm 3 | ; @brief Fill a region of memory with a single byte value. 4 | ; 5 | ; Copyright 2016 Brett Vickers. 6 | ; Use of this source code is governed by a BSD-style license that can 7 | ; be found in the MonkOS LICENSE file. 8 | ;============================================================================= 9 | 10 | bits 64 11 | 12 | section .text 13 | 14 | global memset 15 | 16 | 17 | ;----------------------------------------------------------------------------- 18 | ; @function memset 19 | ; @brief Fill a region of memory with a single byte value. 20 | ; @reg[in] rdi Address of the destination memory area. 21 | ; @reg[in] rsi Value of the byte used to fill memory. 22 | ; @reg[in] rdx Number of bytes to set. 23 | ; @reg[out] rax Destination address. 24 | ; @killedregs r8, rcx 25 | ;----------------------------------------------------------------------------- 26 | memset: 27 | 28 | ; Preserve the original destination address. 29 | mov r8, rdi 30 | 31 | ; The value to store is the second parameter (rsi). 32 | mov rax, rsi 33 | 34 | ; Do a byte-by-byte store. 35 | mov rcx, rdx 36 | rep stosb 37 | 38 | ; Return the original destination address. 39 | mov rax, r8 40 | ret 41 | -------------------------------------------------------------------------------- /libc/string/memsetd.asm: -------------------------------------------------------------------------------- 1 | ;============================================================================= 2 | ; @file memsetd.asm 3 | ; @brief Fill a region of memory with a single dword value. 4 | ; 5 | ; Copyright 2016 Brett Vickers. 6 | ; Use of this source code is governed by a BSD-style license that can 7 | ; be found in the MonkOS LICENSE file. 8 | ;============================================================================= 9 | 10 | bits 64 11 | 12 | section .text 13 | 14 | global memsetd 15 | 16 | 17 | ;----------------------------------------------------------------------------- 18 | ; @function memsetd 19 | ; @brief Fill a region of memory with a single 32-bit dword value. 20 | ; @reg[in] rdi Address of the destination memory area. 21 | ; @reg[in] rsi Value of the dword used to fill memory. 22 | ; @reg[in] rdx Number of dwords to set. 23 | ; @reg[out] rax Destination address. 24 | ; @killedregs r8, rcx 25 | ;----------------------------------------------------------------------------- 26 | memsetd: 27 | 28 | ; Preserve the original destination address. 29 | mov r8, rdi 30 | 31 | ; The value to store is the second parameter (rsi). 32 | mov rax, rsi 33 | 34 | ; Do a byte-by-byte store. 35 | mov rcx, rdx 36 | rep stosd 37 | 38 | ; Return the original destination address. 39 | mov rax, r8 40 | ret 41 | -------------------------------------------------------------------------------- /libc/string/memsetw.asm: -------------------------------------------------------------------------------- 1 | ;============================================================================= 2 | ; @file memsetw.asm 3 | ; @brief Fill a region of memory with a single word value. 4 | ; 5 | ; Copyright 2016 Brett Vickers. 6 | ; Use of this source code is governed by a BSD-style license that can 7 | ; be found in the MonkOS LICENSE file. 8 | ;============================================================================= 9 | 10 | bits 64 11 | 12 | section .text 13 | 14 | global memsetw 15 | 16 | 17 | ;----------------------------------------------------------------------------- 18 | ; @function memsetw 19 | ; @brief Fill a region of memory with a single 16-bit word value. 20 | ; @reg[in] rdi Address of the destination memory area. 21 | ; @reg[in] rsi Value of the word used to fill memory. 22 | ; @reg[in] rdx Number of words to set. 23 | ; @reg[out] rax Destination address. 24 | ; @killedregs r8, rcx 25 | ;----------------------------------------------------------------------------- 26 | memsetw: 27 | 28 | ; Preserve the original destination address. 29 | mov r8, rdi 30 | 31 | ; The value to store is the second parameter (rsi). 32 | mov rax, rsi 33 | 34 | ; Do a byte-by-byte store. 35 | mov rcx, rdx 36 | rep stosw 37 | 38 | ; Return the original destination address. 39 | mov rax, r8 40 | ret 41 | -------------------------------------------------------------------------------- /libc/string/memzero.asm: -------------------------------------------------------------------------------- 1 | ;============================================================================= 2 | ; @file memzero.asm 3 | ; @brief Fill a region of memory with zeroes. 4 | ; 5 | ; Copyright 2016 Brett Vickers. 6 | ; Use of this source code is governed by a BSD-style license that can 7 | ; be found in the MonkOS LICENSE file. 8 | ;============================================================================= 9 | 10 | bits 64 11 | 12 | section .text 13 | 14 | global memzero 15 | 16 | 17 | ;----------------------------------------------------------------------------- 18 | ; @function memzero 19 | ; @brief Fill a region of memory with zeroes. 20 | ; @reg[in] rdi Address of the destination memory area. 21 | ; @reg[in] rsi Number of bytes to set to zero. 22 | ; @reg[out] rax Destination address. 23 | ; @killedregs r8, rcx 24 | ;----------------------------------------------------------------------------- 25 | memzero: 26 | 27 | ; Preserve the original destination address. 28 | mov r8, rdi 29 | 30 | ; The value to store is zero. 31 | xor rax, rax 32 | 33 | ; Do a byte-by-byte store. 34 | mov rcx, rsi 35 | rep stosb 36 | 37 | ; Return the original destination address. 38 | mov rax, r8 39 | ret 40 | -------------------------------------------------------------------------------- /libc/string/strcmp.c: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file strcmp.c 3 | /// @brief Compare one string to another. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #include 11 | 12 | int 13 | strcmp(const char *str1, const char *str2) 14 | { 15 | int i = 0; 16 | for (; str1[i] && str2[i]; i++) { 17 | if (str1[i] == str2[i]) 18 | continue; 19 | return (int)str1[i] - (int)str2[i]; 20 | } 21 | return (int)str1[i] - (int)str2[i]; 22 | } 23 | -------------------------------------------------------------------------------- /libc/string/strlcat.c: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file strlcat.c 3 | /// @brief Appends one string to another. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #include 11 | 12 | size_t 13 | strlcat(char *dst, const char *src, size_t dstsize) 14 | { 15 | char *d = dst; 16 | const char *dt = dst + dstsize - 1; 17 | while (d < dt && *d) { 18 | d++; 19 | } 20 | 21 | const char *s = src; 22 | while (d < dt && *s) { 23 | *d++ = *s++; 24 | } 25 | 26 | if (dstsize > 0) { 27 | *d = 0; 28 | } 29 | 30 | return (size_t)(d - dst); 31 | } 32 | -------------------------------------------------------------------------------- /libc/string/strlcpy.c: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file strlcpy.c 3 | /// @brief Copies one string to another. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #include 11 | 12 | size_t 13 | strlcpy(char *dst, const char *src, size_t dstsize) 14 | { 15 | char *d = dst; 16 | const char *dt = dst + dstsize - 1; 17 | const char *s = src; 18 | while (d < dt && *s) { 19 | *d++ = *s++; 20 | } 21 | 22 | if (dstsize > 0) { 23 | *d = 0; 24 | } 25 | 26 | return (size_t)(d - dst); 27 | } 28 | -------------------------------------------------------------------------------- /libc/string/strlen.c: -------------------------------------------------------------------------------- 1 | //============================================================================ 2 | /// @file strlen.c 3 | /// @brief Returns the length of the a string. 4 | // 5 | // Copyright 2016 Brett Vickers. 6 | // Use of this source code is governed by a BSD-style license that can 7 | // be found in the MonkOS LICENSE file. 8 | //============================================================================ 9 | 10 | #include 11 | 12 | size_t 13 | strlen(const char *str) 14 | { 15 | size_t len = 0; 16 | for (; str[len]; ++len) { 17 | // do nothing 18 | } 19 | return len; 20 | } 21 | -------------------------------------------------------------------------------- /scripts/config.mk: -------------------------------------------------------------------------------- 1 | #---------------------------------------------------------------------------- 2 | # config.mk 3 | # MonkOS makefile shared configuration settings 4 | # 5 | # The following variables should be set before including this file: 6 | # 7 | # DIR_ROOT The root directory of the project. 8 | # 9 | #---------------------------------------------------------------------------- 10 | 11 | #-------------------- 12 | # Project directories 13 | #-------------------- 14 | DIR_BOOT := $(DIR_ROOT)/boot 15 | DIR_BUILD := $(DIR_ROOT)/build 16 | DIR_DEPS := $(DIR_ROOT)/deps 17 | DIR_DOCKER := $(DIR_ROOT)/docker 18 | DIR_DOCS := $(DIR_ROOT)/docs 19 | DIR_INCLUDE := $(DIR_ROOT)/include 20 | DIR_KERNEL := $(DIR_ROOT)/kernel 21 | DIR_LIBC := $(DIR_ROOT)/libc 22 | DIR_SCRIPTS := $(DIR_ROOT)/scripts 23 | 24 | 25 | #------------------- 26 | # Tool configuration 27 | #------------------- 28 | TARGET := x86_64-elf 29 | 30 | CC := $(TARGET)-gcc 31 | 32 | CCFLAGS := -std=gnu11 -I$(DIR_INCLUDE) -Qn -g \ 33 | -m64 -mno-red-zone -mno-mmx -mfpmath=sse -masm=intel \ 34 | -ffreestanding -fno-asynchronous-unwind-tables \ 35 | -Wall -Wextra -Wpedantic 36 | 37 | AS := nasm 38 | 39 | ASFLAGS := -f elf64 40 | 41 | AR := $(TARGET)-ar 42 | 43 | LDFLAGS := -g -nostdlib -m64 -mno-red-zone -ffreestanding -lgcc \ 44 | -z max-page-size=0x1000 45 | 46 | CTAGS := ctags 47 | 48 | DOXYGEN := doxygen 49 | 50 | MAKE_FLAGS := --quiet --no-print-directory 51 | 52 | QEMU := qemu-system-x86_64 53 | 54 | UNCRUSTIFY := uncrustify 55 | 56 | UNCRUSTIFY_CFG := $(DIR_SCRIPTS)/uncrustify.cfg 57 | 58 | 59 | #--------------------- 60 | # Display color macros 61 | #--------------------- 62 | BLUE := \033[1;34m 63 | YELLOW := \033[1;33m 64 | NORMAL := \033[0m 65 | 66 | SUCCESS := $(YELLOW)SUCCESS$(NORMAL) 67 | -------------------------------------------------------------------------------- /scripts/lib.mk: -------------------------------------------------------------------------------- 1 | #---------------------------------------------------------------------------- 2 | # lib.mk 3 | # MonkOS makefile rules for building a library 4 | # 5 | # The following variables should be set before including this file: 6 | # 7 | # DIR_ROOT The root directory of the project 8 | # LIB_NAME The name of the library produced by this makefile 9 | # LIB_DEPS The optional list of libraries the lib depends on 10 | # POST_LIB_RULE The optional rule that runs after the lib is built 11 | # 12 | #---------------------------------------------------------------------------- 13 | 14 | include $(DIR_ROOT)/scripts/config.mk 15 | 16 | 17 | # --------- 18 | # Functions 19 | # --------- 20 | 21 | # Recursively find files from the current working directory that match param1. 22 | findfiles = $(patsubst ./%,%,$(shell find . -name $(1))) 23 | 24 | # Recursively find files from the param1 directory that match param2. 25 | findfiles2 = $(shell find $(1) -name $(2)) 26 | 27 | # Calculate the list of output directories required by one or more files. The 28 | # base output directory is given in param1, and the list of files is given in 29 | # param2. 30 | outputdirs = $(addprefix $(dir $(1)/), $(sort $(dir $(2)))) 31 | 32 | 33 | # ----------------------- 34 | # Configuration variables 35 | # ----------------------- 36 | 37 | DIR_LIB_BUILD := $(DIR_BUILD)/$(LIB_NAME) 38 | DIR_LIB_DEPS := $(DIR_DEPS)/$(LIB_NAME) 39 | 40 | ASM_FILES := $(call findfiles,'*.asm') 41 | C_FILES := $(call findfiles,'*.c') 42 | CODE_FILES := $(ASM_FILES) $(C_FILES) 43 | H_FILES := $(call findfiles,'*.h') \ 44 | $(call findfiles2,$(DIR_INCLUDE)/$(LIB_NAME),'*.h') 45 | 46 | OBJ_FILES_ASM := $(ASM_FILES:%.asm=$(DIR_LIB_BUILD)/%_asm.o) 47 | OBJ_FILES_C := $(C_FILES:%.c=$(DIR_LIB_BUILD)/%.o) 48 | OBJ_FILES := $(OBJ_FILES_ASM) $(OBJ_FILES_C) 49 | 50 | DEP_FILES_ASM := $(ASM_FILES:%.asm=$(DIR_LIB_DEPS)/%_asm.d) 51 | DEP_FILES_C := $(C_FILES:%.c=$(DIR_LIB_DEPS)/%.d) 52 | DEP_FILES := $(DEP_FILES_ASM) $(DEP_FILES_C) 53 | 54 | LIB_FILE := $(DIR_LIB_BUILD)/$(LIB_NAME).a 55 | 56 | LIB_DEPS_PATHS := $(join $(LIB_DEPS:%=$(DIR_BUILD)/%), $(LIB_DEPS:%=/%.a)) 57 | 58 | TAG := $(BLUE)[$(LIB_NAME)]$(NORMAL) 59 | 60 | 61 | # ----------- 62 | # Build rules 63 | # ----------- 64 | 65 | all: mkdir $(LIB_FILE) $(POST_LIB_RULE) 66 | @echo "$(TAG) $(SUCCESS)" 67 | 68 | mkdir: .force 69 | @mkdir -p $(call outputdirs,$(DIR_LIB_BUILD),$(CODE_FILES)) 70 | @mkdir -p $(call outputdirs,$(DIR_LIB_DEPS),$(CODE_FILES)) 71 | 72 | uncrustify: 73 | @echo "$(TAG) Uncrustifying code" 74 | @$(UNCRUSTIFY) \ 75 | -c $(UNCRUSTIFY_CFG) \ 76 | -l C \ 77 | --replace \ 78 | --no-backup \ 79 | $(C_FILES) $(H_FILES) > /dev/null 2> /dev/null 80 | 81 | clean: 82 | @rm -f $(LIB_FILE) $(OBJ_FILES) 83 | 84 | $(LIB_FILE): $(OBJ_FILES) $(DEP_FILES) 85 | @echo "$(TAG) Archiving $(notdir $@)" 86 | @rm -f $@ 87 | @$(AR) cqs $@ $(OBJ_FILES) 88 | 89 | $(OBJ_FILES_ASM): $(DIR_LIB_BUILD)/%_asm.o: %.asm 90 | @echo "$(TAG) Assembling $<" 91 | @$(AS) $(ASFLAGS) $< -o $@ 92 | 93 | $(OBJ_FILES_C): $(DIR_LIB_BUILD)/%.o: %.c 94 | @echo "$(TAG) Compiling $<" 95 | @$(CC) $(CCFLAGS) -c $< -o $@ 96 | 97 | $(DEP_FILES_ASM): $(DIR_LIB_DEPS)/%_asm.d: %.asm | mkdir 98 | @echo "$(TAG) Generating dependencies for $<" 99 | @set -e; \ 100 | rm -f $@; \ 101 | $(AS) -M $(ASFLAGS) $< -MT @@@ > $@.$$$$; \ 102 | sed 's,@@@[ :]*,$(DIR_LIB_BUILD)/$*_asm.o $@ : ,g' < $@.$$$$ > $@; \ 103 | rm -f $@.$$$$ 104 | 105 | $(DEP_FILES_C): $(DIR_LIB_DEPS)/%.d: %.c | mkdir 106 | @echo "$(TAG) Generating dependencies for $<" 107 | @set -e; \ 108 | rm -f $@; \ 109 | $(CC) -MM -MT @@@ $(CCFLAGS) $< > $@.$$$$; \ 110 | sed 's,@@@[ :]*,$(DIR_LIB_BUILD)/$*.o $@ : ,g' < $@.$$$$ > $@; \ 111 | rm -f $@.$$$$ 112 | 113 | .force: 114 | 115 | 116 | # ---------------------- 117 | # Generated dependencies 118 | # ---------------------- 119 | 120 | -include $(DEP_FILES) 121 | -------------------------------------------------------------------------------- /scripts/mkcdrom.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Create a directory structure to hold the ISO image files. 4 | rm -rf build/iso 5 | mkdir -p build/iso 6 | mkdir -p build/iso/boot 7 | 8 | # Copy the stage 1 boot loader 9 | cp build/boot.sys build/iso/boot/boot.sys 10 | 11 | # Copy the stage 2 boot loader 12 | cp build/loader.sys build/iso/loader.sys 13 | 14 | # Copy the kernel and strip it 15 | cp build/monk.sys build/iso/monk.sys 16 | strip build/iso/monk.sys 17 | 18 | # Generate the ISO file (with a boot catalog) 19 | genisoimage -R -J \ 20 | -c boot/bootcat \ 21 | -b boot/boot.sys \ 22 | -no-emul-boot \ 23 | -boot-load-size 4 \ 24 | -o build/monk.iso \ 25 | ./build/iso 26 | --------------------------------------------------------------------------------