├── .gdbinit ├── .gitignore ├── .vscode ├── c_cpp_properties.json ├── launch.json ├── settings.json └── tasks.json ├── Dockerfile ├── README.md ├── applications ├── Makefile ├── SmallerC │ └── test_SmallerC.c ├── cp │ └── cp.c ├── edit │ └── edit.c ├── fasm │ └── test_fasm.asm ├── image │ └── image.c ├── init │ └── init.c ├── ls │ └── ls.c ├── mkdir │ └── mkdir.c ├── mv │ └── mv.c ├── ping │ └── ping.c ├── rm │ └── rm.c ├── rmdir │ └── rmdir.c ├── shell │ └── shell.c └── test │ └── test.c ├── bootloader ├── Makefile ├── README.md ├── arch │ └── i386 │ │ ├── a20.asm │ │ ├── ata.c │ │ ├── ata.h │ │ ├── bootloader.asm │ │ ├── disk.asm │ │ ├── gdt.asm │ │ ├── linker.ld │ │ ├── make.config │ │ ├── memory.asm │ │ ├── port_io.h │ │ ├── print.asm │ │ ├── print_hex.asm │ │ ├── print_pm.asm │ │ ├── switch_pm.asm │ │ ├── tty.c │ │ ├── tty.h │ │ ├── utility.asm │ │ ├── vesa.asm │ │ └── vga.h ├── bootloader │ └── main.c ├── elf │ ├── elf.c │ └── elf.h ├── multiboot │ └── multiboot.h ├── string │ ├── string.c │ └── string.h ├── tar │ ├── tar.c │ └── tar.h └── video │ ├── vbe.h │ ├── video.c │ └── video.h ├── build-toolchain.sh ├── build.sh ├── clean.sh ├── cleanup_tap.sh ├── config.sh ├── default-host.sh ├── headers.sh ├── iso.sh ├── kernel ├── Makefile ├── arch │ └── i386 │ │ ├── arch_init │ │ └── arch_init.c │ │ ├── ata │ │ └── ata.c │ │ ├── boot │ │ ├── boot.asm │ │ └── gdt.asm │ │ ├── cpu │ │ ├── cpu.c │ │ └── cpuid.asm │ │ ├── crt │ │ ├── crti.S │ │ └── crtn.S │ │ ├── idt │ │ └── idt.c │ │ ├── isr │ │ ├── interrupt.asm │ │ └── isr.c │ │ ├── keyboard │ │ └── keyboard.c │ │ ├── linker.ld │ │ ├── make.config │ │ ├── paging │ │ └── paging.c │ │ ├── pci │ │ └── pci.c │ │ ├── pic │ │ └── pic.c │ │ ├── process │ │ ├── process.c │ │ ├── start_init.asm │ │ └── switch_kernel_context.asm │ │ ├── rtl8139 │ │ └── rtl8139.c │ │ ├── serial │ │ └── serial.c │ │ ├── syscall │ │ └── syscall.c │ │ ├── time │ │ └── time.c │ │ ├── timer │ │ └── timer.c │ │ └── tty │ │ └── tty.c ├── block_io │ └── block_io.c ├── console │ └── console.c ├── elf │ └── elf.c ├── fat │ └── fat.c ├── heap │ └── heap.c ├── include │ ├── arch │ │ └── i386 │ │ │ └── kernel │ │ │ ├── cpu.h │ │ │ ├── idt.h │ │ │ ├── isr.h │ │ │ ├── pic.h │ │ │ ├── port_io.h │ │ │ └── segmentation.h │ ├── common.h │ ├── datetime.h │ ├── fs.h │ ├── fsstat.h │ ├── kernel │ │ ├── arch_init.h │ │ ├── arp.h │ │ ├── ata.h │ │ ├── block_io.h │ │ ├── console.h │ │ ├── cpu.h │ │ ├── elf.h │ │ ├── errno.h │ │ ├── ethernet.h │ │ ├── fat.h │ │ ├── file_system.h │ │ ├── heap.h │ │ ├── icmp.h │ │ ├── ipv4.h │ │ ├── keyboard.h │ │ ├── lock.h │ │ ├── memory_bitmap.h │ │ ├── multiboot.h │ │ ├── network.h │ │ ├── paging.h │ │ ├── panic.h │ │ ├── pci.h │ │ ├── pipe.h │ │ ├── process.h │ │ ├── rtl8139.h │ │ ├── serial.h │ │ ├── socket.h │ │ ├── syscall.h │ │ ├── tar.h │ │ ├── time.h │ │ ├── timer.h │ │ ├── tty.h │ │ ├── types.h │ │ ├── vbe.h │ │ ├── vfs.h │ │ ├── vga.h │ │ └── video.h │ ├── network.h │ ├── syscall.h │ └── syscallnum.h ├── kernel │ └── kernel.c ├── lock │ └── lock.c ├── memory_bitmap │ └── memory_bitmap.c ├── network │ ├── arp.c │ ├── ethernet.c │ ├── icmp.c │ ├── ipv4.c │ └── network.c ├── panic │ └── panic.c ├── pipe │ └── pipe.c ├── socket │ └── socket.c ├── tar │ └── tar.c ├── vfs │ └── vfs.c └── video │ └── video.c ├── libc ├── .gitignore ├── Makefile ├── arch │ └── i386 │ │ ├── crt │ │ └── crt0.S │ │ └── make.config ├── assert │ └── assert.c ├── include │ ├── assert.h │ ├── stdio.h │ ├── stdlib.h │ ├── string.h │ ├── sys │ │ ├── cdefs.h │ │ ├── time.h │ │ └── types.h │ └── time.h ├── stdio │ ├── fileio.c │ ├── printf.c │ ├── putchar.c │ └── puts.c ├── stdlib │ ├── abort.c │ ├── exit.c │ ├── getenv.c │ └── malloc.c ├── string │ ├── memcmp.c │ ├── memcpy.c │ ├── memmove.c │ ├── memset.c │ ├── strcmp.c │ ├── strcpy.c │ ├── strdup.c │ └── strlen.c └── time │ └── time.c ├── qemu.sh ├── rebuild_newlib.sh ├── setup_tap.sh └── target-triplet-to-arch.sh /.gdbinit: -------------------------------------------------------------------------------- 1 | python 2 | # Usage: gdb) add-symbol-file-auto xxx.elf 3 | class AddSymbolFileAuto (gdb.Command): 4 | def __init__(self): 5 | super(AddSymbolFileAuto, self).__init__("add-symbol-file-auto", gdb.COMMAND_USER) 6 | 7 | def invoke(self, solibpath, from_tty): 8 | offset = self.get_text_offset(solibpath) 9 | gdb_cmd = "add-symbol-file %s %s" % (solibpath, offset) 10 | gdb.execute(gdb_cmd, from_tty) 11 | 12 | def get_text_offset(self, solibpath): 13 | import subprocess 14 | # Note: Replace "readelf" with path to binary if it is not in your PATH. 15 | elfres = subprocess.check_output(["readelf", "-WS", solibpath]) 16 | for line in elfres.decode('UTF-8').splitlines(): 17 | if "] .text " in line: 18 | 19 | return "0x" + line.split()[4] 20 | return "" # TODO: Raise error when offset is not found? 21 | AddSymbolFileAuto() 22 | end 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iso 2 | isodir 3 | sysroot 4 | *.o 5 | *.tar 6 | *.bin 7 | *.elf 8 | *.d 9 | simple_os.kernel 10 | init 11 | shell 12 | !init/ 13 | !shell/ 14 | serial_port_output.txt 15 | *.fat 16 | toolchain/ 17 | build-toolchain/ 18 | stb_image.h 19 | mnt/ 20 | *.map -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win32", 5 | "includePath": [ 6 | "${default}", 7 | "${workspaceFolder}/sysroot/usr/include" 8 | ], 9 | "defines": [ 10 | "_DEBUG", 11 | "UNICODE", 12 | "_UNICODE" 13 | ], 14 | "windowsSdkVersion": "10.0.19041.0", 15 | "compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.28.29333/bin/Hostx64/x64/cl.exe", 16 | "cStandard": "c17", 17 | "cppStandard": "c++17", 18 | "intelliSenseMode": "msvc-x64" 19 | }, 20 | { 21 | "name": "Linux", 22 | "includePath": [], 23 | "defines": [], 24 | "compilerPath": "/usr/bin/gcc", 25 | "cStandard": "gnu17", 26 | "cppStandard": "gnu++14", 27 | "intelliSenseMode": "${default}", 28 | "compilerArgs": [] 29 | }, 30 | { 31 | "name": "i686-elf", 32 | "includePath": [ 33 | "${workspaceFolder}/kernel/include/*", 34 | "${workspaceFolder}/libc/include/*" 35 | ], 36 | "defines": [], 37 | "compilerPath": "${workspaceFolder}/toolchain/usr/bin/i686-elf-gcc", 38 | "cStandard": "gnu17", 39 | "cppStandard": "gnu++14", 40 | "intelliSenseMode": "linux-gcc-x86", 41 | "compilerArgs": [ 42 | "--sysroot=${workspaceFolder}/sysroot", 43 | "-ffreestanding" 44 | ] 45 | }, 46 | { 47 | "name": "SimpleOS", 48 | "includePath": [], 49 | "defines": [], 50 | "compilerPath": "${workspaceFolder}/toolchain/usr/bin/i686-simpleos-gcc", 51 | "cStandard": "gnu17", 52 | "cppStandard": "gnu++14", 53 | "intelliSenseMode": "linux-gcc-x86", 54 | "compilerArgs": [ 55 | "-isystem ${workspaceFolder}/kernel/include" 56 | ] 57 | } 58 | ], 59 | "version": 4 60 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Source: https://stackoverflow.com/questions/53519668/how-to-attach-to-remote-gdb-with-vscode 3 | 4 | // Use IntelliSense to learn about possible attributes. 5 | // Hover to view descriptions of existing attributes. 6 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 7 | // This config is for VSCode to use gdb to connect to a remote QEMU session 8 | "version": "0.2.0", 9 | "configurations": [ 10 | { 11 | "name": "Debug All", 12 | "type": "gdb", // Provided by "Native Debug" extension 13 | "request": "attach", 14 | "executable": "${workspaceFolder}/kernel/simple_os.kernel", 15 | "target": "localhost:1234", 16 | "remote": true, 17 | "cwd": "${workspaceFolder}", 18 | "gdbpath": "/usr/bin/gdb", 19 | "preLaunchTask": "Build and Debug", 20 | "autorun": [ 21 | "add-symbol-file-auto bootloader/bootloader.elf", 22 | "add-symbol-file-auto kernel/simple_os.kernel", 23 | "add-symbol-file-auto applications/shell/shell.elf", 24 | // "add-symbol-file-auto applications/test/test.elf", 25 | // these dir are needed for asm files 26 | "dir kernel", 27 | "dir bootloader", 28 | ] 29 | }, 30 | { 31 | "name": "Debug Kernel", 32 | "type": "gdb", // Provided by "Native Debug" extension 33 | "request": "attach", 34 | "executable": "${workspaceFolder}/kernel/simple_os.kernel", 35 | "target": "localhost:1234", 36 | "remote": true, 37 | "cwd": "${workspaceFolder}", 38 | "gdbpath": "/usr/bin/gdb", 39 | "preLaunchTask": "Build and Debug", 40 | "autorun": [ 41 | "add-symbol-file-auto kernel/init/init", 42 | "dir kernel", 43 | ] 44 | }, 45 | { 46 | "name": "Debug Bootloader", 47 | "type": "gdb", // Provided by "Native Debug" extension 48 | "request": "attach", 49 | "executable": "${workspaceFolder}/bootloader/bootloader.elf", 50 | "target": "localhost:1234", 51 | "remote": true, 52 | "cwd": "${workspaceFolder}", 53 | "gdbpath": "/usr/bin/gdb", 54 | "preLaunchTask": "Build and Debug", 55 | "autorun": [ 56 | "dir bootloader" 57 | ] 58 | } 59 | ] 60 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "random": "c", 4 | "array": "c", 5 | "string_view": "c", 6 | "initializer_list": "c", 7 | "ranges": "c", 8 | "utility": "c" 9 | }, 10 | "C_Cpp.vcFormat.newLine.beforeElse": false, 11 | "C_Cpp.vcFormat.newLine.beforeOpenBrace.block": "sameLine", 12 | "C_Cpp.vcFormat.newLine.beforeOpenBrace.function": "sameLine", 13 | "C_Cpp.formatting": "vcFormat", 14 | "C_Cpp.files.exclude": { 15 | "**/build-toolchain/**": true, 16 | "**/.vscode": true 17 | }, 18 | "logViewer.watch": [ 19 | { 20 | "title": "Kernel Debug Output", 21 | "pattern": "serial_port_output.txt" 22 | } 23 | ] 24 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 (place to understand JSON file) 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Build Kernel", 8 | "type": "shell", 9 | "command": "${workspaceFolder}/build.sh kernel", 10 | "problemMatcher": { 11 | "base": "$gcc", 12 | "fileLocation": ["relative", "${workspaceFolder}/kernel"] 13 | } 14 | }, 15 | { 16 | "label": "Build Libc", 17 | "type": "shell", 18 | "command": "${workspaceFolder}/build.sh libc", 19 | "problemMatcher": { 20 | "base": "$gcc", 21 | "fileLocation": ["relative", "${workspaceFolder}/libc"] 22 | } 23 | }, 24 | { 25 | "label": "Build Bootloader", 26 | "type": "shell", 27 | "command": "${workspaceFolder}/build.sh bootloader", 28 | "problemMatcher": { 29 | "base": "$gcc", 30 | "fileLocation": ["relative", "${workspaceFolder}/bootloader"] 31 | } 32 | }, 33 | { 34 | "label": "Build Applications", 35 | "type": "shell", 36 | "command": "${workspaceFolder}/build.sh applications", 37 | "problemMatcher": { 38 | "base": "$gcc", 39 | "fileLocation": ["relative", "${workspaceFolder}/applications"] 40 | } 41 | }, 42 | { 43 | "label": "Build", 44 | "dependsOn": ["Build Libc", "Build Kernel", "Build Applications", "Build Bootloader"], 45 | "dependsOrder": "sequence", 46 | "problemMatcher": [] 47 | }, 48 | { 49 | "label": "Clean", 50 | "type": "shell", 51 | "command": "${workspaceFolder}/clean.sh", 52 | "problemMatcher": [] 53 | }, 54 | { 55 | "label": "Emulate", 56 | "type": "shell", 57 | "command": "${workspaceFolder}/qemu.sh", 58 | "problemMatcher": [] 59 | }, 60 | { 61 | // Source: https://stackoverflow.com/questions/44242048/how-to-make-vscode-not-wait-for-finishing-a-prelaunchtask 62 | "label": "Emulate (Debug)", 63 | "type": "shell", 64 | "command": "${workspaceFolder}/qemu.sh debug", 65 | "isBackground": true, 66 | 67 | // This task is run before some debug tasks. 68 | // Problem is, it's a watch script, and since it never exits, VSCode 69 | // complains. All this is needed so VSCode just lets it run. 70 | "problemMatcher": [ 71 | { 72 | "pattern": [ 73 | { 74 | "regexp": ".", 75 | "file": 1, 76 | "location": 2, 77 | "message": 3 78 | } 79 | ], 80 | "background": { 81 | "activeOnStart": true, 82 | "beginsPattern": ".", 83 | "endsPattern": ".", 84 | } 85 | } 86 | ] 87 | }, 88 | { 89 | "label": "Build and Emulate", 90 | "dependsOn": ["Build", "Emulate"], 91 | "dependsOrder": "sequence", 92 | "problemMatcher": [] 93 | }, 94 | { 95 | "label": "Build and Debug", 96 | "dependsOn": ["Build", "Emulate (Debug)"], 97 | "dependsOrder": "sequence", 98 | "problemMatcher": [] 99 | } 100 | ] 101 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | FROM ubuntu:focal AS base 3 | 4 | ENV DEBIAN_FRONTEND=noninteractive 5 | ENV TZ=US/Eastern 6 | 7 | RUN apt -y update 8 | RUN apt -y install build-essential autoconf automake git bison flex libgmp3-dev libmpc-dev libmpfr-dev texinfo libisl-dev curl gcc-multilib qemu-system-x86 nasm dosfstools xterm 9 | 10 | RUN git clone https://github.com/httpe/simple-os.git simple-os 11 | 12 | FROM base AS toolchain 13 | 14 | WORKDIR /simple-os 15 | 16 | RUN ./build-toolchain.sh 17 | 18 | FROM base AS final 19 | 20 | COPY --from=toolchain /simple-os/toolchain /simple-os/toolchain 21 | 22 | WORKDIR /simple-os 23 | 24 | COPY applications/Makefile applications/Makefile 25 | 26 | RUN ./clean.sh 27 | RUN ./build.sh 28 | 29 | RUN dd if=/dev/zero of=testfs.fat bs=1024 count=$(expr 16 \* 1024) && mkfs.fat -F 32 testfs.fat 30 | 31 | CMD ["./qemu.sh"] -------------------------------------------------------------------------------- /applications/Makefile: -------------------------------------------------------------------------------- 1 | DEFAULT_HOST!=../default-host.sh 2 | HOST?=$(DEFAULT_HOST) 3 | HOSTARCH!=../target-triplet-to-arch.sh $(HOST) 4 | 5 | CFLAGS?=-O2 -g 6 | CPPFLAGS?= 7 | LDFLAGS?= 8 | LIBS?= 9 | 10 | CFLAGS:= $(CFLAGS) -Wall -Wextra 11 | CPPFLAGS:=$(CPPFLAGS) -Iinclude 12 | LDFLAGS:=$(LDFLAGS) 13 | LIBS:=$(LIBS) 14 | 15 | DESTDIR?= 16 | PREFIX?=/usr 17 | BINDIR=$(PREFIX)/bin 18 | 19 | 20 | BINARIES=\ 21 | init/init.elf \ 22 | shell/shell.elf \ 23 | edit/edit.elf \ 24 | ls/ls.elf \ 25 | rm/rm.elf \ 26 | mv/mv.elf \ 27 | mkdir/mkdir.elf \ 28 | rmdir/rmdir.elf \ 29 | ping/ping.elf \ 30 | cp/cp.elf \ 31 | image/image.elf \ 32 | test/test.elf \ 33 | fasm/fasm.elf \ 34 | SmallerC/test_SmallerC.elf 35 | 36 | 37 | .PHONY: all clean install test/test.elf fasm/fasm.elf SmallerC/test_SmallerC.elf 38 | # .SUFFIXES: .o .c .S 39 | 40 | all: $(BINARIES) 41 | 42 | # init/init: $(INIT_OBJS) init/init.ld 43 | # $(CC) -MD -T init/init.ld -o $@ $(CFLAGS) $(INIT_OBJS) $(LIBS) 44 | 45 | # init/init.o: init/init.c 46 | # $(CC) -MD -c $< -o $@ -std=gnu11 $(CFLAGS) $(CPPFLAGS) 47 | 48 | KERNEL_CRT_DIR=$(PROJECT_ROOT)/kernel/arch/$(HOSTARCH)/crt 49 | LIBC_DIR=$(PROJECT_ROOT)/libc 50 | LIBC_CRT_DIR=$(LIBC_DIR)/arch/$(HOSTARCH)/crt 51 | 52 | APP_LINK_LIST_HEAD=\ 53 | $(LIBC_CRT_DIR)/crt0.o \ 54 | $(KERNEL_CRT_DIR)/crti.o \ 55 | $(KERNEL_CRT_DIR)/crtbegin.o \ 56 | 57 | APP_LINK_LIST_TAIL=\ 58 | -nostdlib \ 59 | -lu \ 60 | -lgcc \ 61 | $(KERNEL_CRT_DIR)/crtend.o \ 62 | $(KERNEL_CRT_DIR)/crtn.o \ 63 | 64 | # Test building executable from our own libc (libu) using standalone GCC 65 | test/test.elf: 66 | $(TOOL_CHAIN_ROOT)/usr/bin/i686-elf-gcc -MD -MP -ffreestanding -std=gnu11 -g -isystem $(SYSROOT)/usr/include -c test/test.c -o test/test.o 67 | $(TOOL_CHAIN_ROOT)/usr/bin/i686-elf-gcc --sysroot $(SYSROOT) -o $@ -g $(APP_LINK_LIST_HEAD) test/test.o $(APP_LINK_LIST_TAIL) 68 | 69 | $(TOOL_CHAIN_BUILD_DIR): 70 | mkdir -p $(TOOL_CHAIN_BUILD_DIR) 71 | 72 | # Download the FASM assembler. The WSL2 docker cannot run the 32bit FASM binary, so link to the 64bit version instead. 73 | $(TOOL_CHAIN_BUILD_DIR)/fasm/fasm: $(TOOL_CHAIN_BUILD_DIR) 74 | curl https://flatassembler.net/fasm-1.73.30.tgz --output $(TOOL_CHAIN_BUILD_DIR)/fasm-1.73.30.tgz 75 | cd $(TOOL_CHAIN_BUILD_DIR) && tar -xf fasm-1.73.30.tgz 76 | mv $(TOOL_CHAIN_BUILD_DIR)/fasm/fasm $(TOOL_CHAIN_BUILD_DIR)/fasm/fasm.x86 77 | ln -s $(TOOL_CHAIN_BUILD_DIR)/fasm/fasm.x64 $(TOOL_CHAIN_BUILD_DIR)/fasm/fasm 78 | 79 | fasm/fasm.elf: $(TOOL_CHAIN_BUILD_DIR)/fasm/fasm 80 | $(TOOL_CHAIN_BUILD_DIR)/fasm/fasm $(TOOL_CHAIN_BUILD_DIR)/fasm/source/libc/fasm.asm fasm/fasm.o 81 | $(TOOL_CHAIN_ROOT)/usr/bin/i686-elf-gcc -L $(LIBC_DIR) -o $@ -g $(APP_LINK_LIST_HEAD) fasm/fasm.o $(APP_LINK_LIST_TAIL) 82 | # Copy some ASM source codes to test our ported FASM 83 | mkdir -p $(SYSROOT)/src 84 | cp fasm/test_fasm.asm $(SYSROOT)/src 85 | cp -r $(TOOL_CHAIN_BUILD_DIR)/fasm/source $(SYSROOT)/src/fasm 86 | 87 | $(TOOL_CHAIN_BUILD_DIR)/SmallerC: $(TOOL_CHAIN_BUILD_DIR) 88 | cd $(TOOL_CHAIN_BUILD_DIR); git clone https://github.com/httpe/SmallerC 89 | 90 | $(TOOL_CHAIN_BUILD_DIR)/SmallerC/smlrcc: $(TOOL_CHAIN_BUILD_DIR)/SmallerC 91 | cd $(TOOL_CHAIN_BUILD_DIR)/SmallerC; export SIMPLEOS_SRC=$(PROJECT_ROOT); make smlrc smlrl smlrcc smlrpp n2f lcs.a 92 | 93 | SmallerC/test_SmallerC.elf: $(TOOL_CHAIN_BUILD_DIR)/SmallerC/smlrcc 94 | export PATH=$(TOOL_CHAIN_BUILD_DIR)/SmallerC:$(TOOL_CHAIN_BUILD_DIR)/fasm:$$PATH; smlrcc -simpleos -o SmallerC/test_SmallerC.elf -SI $(TOOL_CHAIN_BUILD_DIR)/SmallerC/v0100/include/ -SI $(PROJECT_ROOT)/kernel/include -SL $(TOOL_CHAIN_BUILD_DIR)/SmallerC -asm n2f -map SmallerC/test_SmallerC.map SmallerC/test_SmallerC.c 95 | 96 | %.elf: %.c 97 | $(TOOL_CHAIN_ROOT)/usr/bin/i686-simpleos-gcc -MD -o $@ $< -std=gnu11 $(CFLAGS) $(CPPFLAGS) $(LIBS) 98 | 99 | # .c.o: 100 | # $(CC) -MD -MP -c $< -o $@ -std=gnu11 $(CFLAGS) $(CPPFLAGS) 101 | 102 | # .S.o: 103 | # $(CC) -MD -MP -c $< -o $@ $(CFLAGS) $(CPPFLAGS) 104 | 105 | # %.o: %.asm 106 | # $(AS) $(ASMFLAGS) $< -o $@ 107 | 108 | image/stb_image.h: 109 | curl https://raw.githubusercontent.com/nothings/stb/master/stb_image.h > image/stb_image.h 110 | 111 | clean: 112 | rm -f $(BINARIES) 113 | rm -f $(OBJS) *.o */*.o */*/*.o 114 | rm -f $(OBJS:.o=.d) *.d */*.d */*/*.d 115 | (cd $(TOOL_CHAIN_BUILD_DIR)/SmallerC && make clean) || echo "No need to clean SmallerC" 116 | 117 | 118 | install: image/stb_image.h $(BINARIES) 119 | mkdir -p $(DESTDIR)$(BINDIR) 120 | cp -p $(BINARIES) $(DESTDIR)$(BINDIR) 121 | 122 | -include $(OBJS:.o=.d) 123 | -------------------------------------------------------------------------------- /applications/SmallerC/test_SmallerC.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | extern char** __environ; 6 | 7 | int main(int argc, char** argv) 8 | { 9 | printf("Hello, World from SmallerC!\n"); 10 | printf("argc: %d, argv[0]: %s\n", argc, argv[0]); 11 | printf("envp[\"SHELL\"]: %s\n", getenv("SHELL")); 12 | time_t rawtime; 13 | struct tm *info; 14 | time( &rawtime ); 15 | info = localtime( &rawtime ); 16 | printf("Current UTC time and date: %s\n", asctime(info)); 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /applications/cp/cp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | int isFile(const char* name) { 12 | DIR* directory = opendir(name); 13 | if (directory != NULL) { 14 | closedir(directory); 15 | return 0; 16 | } 17 | return 1; 18 | } 19 | 20 | int fileExist(const char* filename) { 21 | struct stat buffer; 22 | int exist = stat(filename, &buffer); 23 | if (exist == 0) { 24 | return 1; 25 | } 26 | return 0; 27 | } 28 | 29 | int main(int argc, char* argv[]) { 30 | if (argc < 3 || argv[1] == NULL || argv[2] == NULL) { 31 | exit(1); 32 | } 33 | char buffer[1024]; 34 | int file1; 35 | int file2; 36 | ssize_t count; 37 | file1 = open(argv[1], O_RDONLY); 38 | if (file1 == -1) { 39 | exit(1); 40 | } 41 | // consider the situation where argv[2] is a folder 42 | if (isFile(argv[2]) == 0) { 43 | if (argv[2][strlen(argv[2] - 1)] != '/') { 44 | strcat(argv[2], "/"); 45 | } 46 | char *src = argv[1]; 47 | for (int i = (int) strlen(argv[1]) - 1; i >= 0; i--) { 48 | if (argv[1][i] == '/') { 49 | src += (i + 1); 50 | break; 51 | } 52 | } 53 | strcat(argv[2], src); 54 | } 55 | if (fileExist(argv[2])) { 56 | remove(argv[2]); 57 | printf("Overwrite existing file\n"); 58 | } 59 | file2 = open(argv[2], O_WRONLY | O_CREAT | O_EXCL | S_IRUSR | S_IWUSR, 0666); 60 | if (file2 == -1) { 61 | exit(1); 62 | } 63 | while ((count = read(file1, buffer, sizeof(buffer))) != 0) { 64 | write(file2, buffer, count); 65 | } 66 | exit(0); 67 | } 68 | -------------------------------------------------------------------------------- /applications/fasm/test_fasm.asm: -------------------------------------------------------------------------------- 1 | format ELF executable 3 2 | entry start 3 | segment readable executable 4 | 5 | start: 6 | ; sys_test 7 | mov eax, 0 8 | push 4 9 | push 3 10 | push 2 11 | push 1 12 | ; dummy return address to mimic a function call 13 | ; i.e. (push eip + jmp) + (pop + jmp) = call + ret 14 | push 0 15 | int 88 16 | sub esp, 4*5 17 | 18 | ; exit 19 | mov eax, 5 20 | push 99 21 | push 0 22 | int 88 23 | -------------------------------------------------------------------------------- /applications/image/image.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define STB_IMAGE_IMPLEMENTATION 8 | #define STBI_NO_THREAD_LOCALS 9 | #include "stb_image.h" 10 | 11 | static inline _syscall5(SYS_DRAW_PICTURE, int, draw_picture, uint32_t*, buff, int, x, int, y, int, w, int, h) 12 | static inline _syscall0(SYS_REFRESH_SCREEN, int, sys_refresh_screen) 13 | 14 | int main(int argc, char* argv[]) { 15 | 16 | // uint32_t* buff = (uint32_t*) malloc(500*303*sizeof(uint32_t)); 17 | // memset(buff, 0, 500*303*sizeof(uint32_t)); 18 | // for(int i=0; i<500*303; i++) { 19 | // buff[i] = 0x0066CCFF; 20 | // } 21 | // draw_picture(buff, 0, 0, 500, 303); 22 | // sys_refresh_screen(); 23 | 24 | 25 | if(argc !=2 || argv[1] == NULL) { 26 | printf("Usage: image \n"); 27 | exit(1); 28 | } 29 | 30 | // // ... process data if not NULL ... 31 | // // ... x = width, y = height, n = # 8-bit components per pixel ... 32 | // // ... replace '0' with '1'..'4' to force that many components per pixel 33 | // // ... but 'n' will always be the number that it would have been if you said 0 34 | // 35 | // Standard parameters: 36 | // int *x -- outputs image width in pixels 37 | // int *y -- outputs image height in pixels 38 | // int *channels_in_file -- outputs # of image components in image file 39 | // int desired_channels -- if non-zero, # of image components requested in result 40 | int x,y,n; 41 | uint32_t *data = (uint32_t*) stbi_load(argv[1], &x, &y, &n, 4); 42 | 43 | for(int i=0; i> 24) & 0xFF; 45 | uint32_t b = (data[i] >> 16) & 0xFF; 46 | uint32_t g = (data[i] >> 8) & 0xFF; 47 | uint32_t r = (data[i]) & 0xFF; 48 | data[i] = (r << 16) | (g << 8) | (b << 0); 49 | } 50 | 51 | draw_picture(data, 0, 0, x, y); 52 | sys_refresh_screen(); 53 | 54 | stbi_image_free(data); 55 | 56 | // wait for the user to press any key 57 | int nread; 58 | char c; 59 | while ((nread = read(STDIN_FILENO, &c, 1)) != 1); 60 | 61 | // Clear screen before exit 62 | write(STDOUT_FILENO, "\x1b[2J", 4); 63 | write(STDOUT_FILENO, "\x1b[H", 3); 64 | 65 | exit(0); 66 | } -------------------------------------------------------------------------------- /applications/ls/ls.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define MAX_PATH_LEN 4096 12 | 13 | int main(int argc, char* argv[]) { 14 | UNUSED_ARG(argc); 15 | 16 | char* ls_path = argv[1]; 17 | 18 | char* path_buff = malloc(MAX_PATH_LEN+1); 19 | 20 | if(ls_path == NULL) { 21 | ls_path = ""; 22 | } 23 | 24 | DIR* dir = opendir(ls_path); 25 | if(dir == NULL) { 26 | perror("ls error"); 27 | exit(1); 28 | } 29 | struct dirent* entry; 30 | while(1) { 31 | entry = readdir(dir); 32 | if(entry == NULL) { 33 | if(errno != 0) { 34 | perror("ls error"); 35 | closedir(dir); 36 | exit(1); 37 | } else { 38 | closedir(dir); 39 | exit(0); 40 | } 41 | } 42 | 43 | size_t path_len = strlen(ls_path); 44 | size_t name_len = strlen(entry->d_name); 45 | if(path_len + 1 + name_len > MAX_PATH_LEN) { 46 | printf(" File: %s\n", entry->d_name); 47 | } else { 48 | memmove(path_buff, ls_path, path_len); 49 | if(path_len > 0 && ls_path[path_len-1] != '/') { 50 | path_buff[path_len++] = '/'; 51 | } 52 | memmove(path_buff + path_len, entry->d_name, name_len); 53 | path_buff[path_len + name_len] = 0; 54 | struct stat st = {0}; 55 | int r_stat = stat(path_buff, &st); 56 | if(r_stat < 0) { 57 | printf("ls %s stat error(%d): %s\n", entry->d_name, r_stat, strerror(-r_stat)); 58 | } else { 59 | char* type; 60 | if(S_ISDIR(st.st_mode)) { 61 | type = "DIR"; 62 | } else { 63 | type = "FILE"; 64 | } 65 | char* datetime = ctime(&st.st_mtim.tv_sec); 66 | // ctime result includes a trailing '\n', remove it 67 | char buf[64] = {0}; 68 | memmove(buf, datetime, strlen(datetime)-1); 69 | printf(" %-4s %s %10ld: %s\n", type, buf, st.st_size, entry->d_name); 70 | } 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /applications/mkdir/mkdir.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char* argv[]) { 8 | if(argc < 2 || argv[1] == NULL) { 9 | exit(1); 10 | } 11 | mode_t mode = 0; 12 | if(argc > 2 || argv[2] != NULL) { 13 | mode = (mode_t) atol(argv[2]); 14 | } 15 | int r = mkdir(argv[1], mode); 16 | if(r < 0) { 17 | perror("mkdir error"); 18 | exit(1); 19 | } else { 20 | exit(0); 21 | } 22 | } -------------------------------------------------------------------------------- /applications/mv/mv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char* argv[]) { 7 | if(argc < 3 || argv[1] == NULL || argv[2] == NULL) { 8 | exit(1); 9 | } 10 | int r = rename(argv[1], argv[2]); 11 | if(r < 0) { 12 | perror("mv error"); 13 | exit(1); 14 | } else { 15 | exit(0); 16 | } 17 | } -------------------------------------------------------------------------------- /applications/ping/ping.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | int main(int argc, char* argv[]) { 11 | 12 | ip_addr dst = {.addr = {8,8,8,8}}; 13 | 14 | if(argc > 1) { 15 | char* part = strtok(argv[1],"."); 16 | for(int i=0; i<4; i++) { 17 | if(part == NULL) { 18 | exit(1); 19 | } 20 | dst.addr[i] = atoi(part); 21 | part = strtok(NULL,"."); 22 | } 23 | } 24 | 25 | int s = syscall_socket_open(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); 26 | int r = 0; 27 | struct timeval tv = {.tv_sec = 5}; 28 | uint8_t ttl = 0x40, type = ICMP_TYPE_ECHO_REQUEST, code = ICMP_CODE_ECHO; 29 | uint16_t echo_id = getpid(), echo_seq = 0; 30 | r += syscall_socket_setopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval)); 31 | r += syscall_socket_setopt(s, SOL_IP, IP_TTL, &ttl, sizeof(uint8_t)); 32 | r += syscall_socket_setopt(s, SOL_ICMP, ICMP_TYPE, &type, sizeof(uint8_t)); 33 | r += syscall_socket_setopt(s, SOL_ICMP, ICMP_CODE, &code, sizeof(uint8_t)); 34 | r += syscall_socket_setopt(s, SOL_ICMP, ICMP_ECHO_ID, &echo_id, sizeof(uint16_t)); 35 | r += syscall_socket_setopt(s, SOL_ICMP, ICMP_ECHO_SEQ, &echo_seq, sizeof(uint16_t)); 36 | if(r<0) { 37 | printf("Set socket opt failed\n"); 38 | exit(-r); 39 | } 40 | 41 | char* buff = malloc(65535); 42 | memset(buff, 0, 65535); 43 | const char* msg = "A ping packet from Simple-OS!"; 44 | memmove(buff, msg, strlen(msg)); 45 | 46 | sockaddr_in socket_dst = {.sa_family = AF_INET, .sin_addr.s_addr = *(uint32_t*) dst.addr}; 47 | r = syscall_socket_sendto(s, buff, strlen(msg), 0, (sockaddr*) &socket_dst, sizeof(sockaddr_in)); 48 | 49 | uint8_t* receive_buff = malloc(65535); 50 | sockaddr_in socket_src; 51 | while(1) { 52 | socklen_t address_len = 0; 53 | int received = syscall_socket_recvfrom(s, receive_buff, 65535, 0, (sockaddr*) &socket_src, &address_len); 54 | if(received < 0) { 55 | printf("Ping: error in receiving packet\n"); 56 | exit(1); 57 | } 58 | 59 | if(received < (int) (sizeof(icmp_header) + strlen(msg))) { 60 | printf("Ping: Skipping packet too small\n"); 61 | continue; 62 | } 63 | 64 | icmp_header* p = (icmp_header*) receive_buff; 65 | if(p->type != ICMP_TYPE_ECHO_REPLY || p->code != ICMP_CODE_ECHO || 66 | p->rest.un.echo.id != switch_endian16(echo_id) || p->rest.un.echo.sequence != switch_endian16(echo_seq) 67 | ) { 68 | printf("Ping: Skipping other ICMP packet\n"); 69 | continue; 70 | } 71 | 72 | if(memcmp(p + 1, msg, received - sizeof(icmp_header)) == 0) { 73 | printf("Ping: GOOD reply received!\n"); 74 | exit(0); 75 | } else { 76 | printf("Ping: reply corrupted, data: \n%s\n", (char*) (p+1)); 77 | exit(1); 78 | } 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /applications/rm/rm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char* argv[]) { 7 | if(argc < 2 || argv[1] == NULL) { 8 | exit(0); 9 | } 10 | int r = unlink(argv[1]); 11 | if(r < 0) { 12 | perror("rm error"); 13 | exit(1); 14 | } else { 15 | exit(0); 16 | } 17 | } -------------------------------------------------------------------------------- /applications/rmdir/rmdir.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | int main(int argc, char* argv[]) { 7 | if(argc < 2 || argv[1] == NULL) { 8 | exit(1); 9 | } 10 | int r = rmdir(argv[1]); 11 | if(r < 0) { 12 | perror("rmdir error"); 13 | exit(1); 14 | } else { 15 | exit(0); 16 | } 17 | } -------------------------------------------------------------------------------- /applications/test/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int syscall(int syscall_number, int arg1, int arg2, int arg3, int arg4) 4 | { 5 | asm( 6 | "movl 8(%esp), %eax\n" // 7 | "addl $8, %esp\n" // skip saved ebp/eip, kernel will skip syscall_number 8 | "int $88\n" // syscall int 9 | "subl $8, %esp\n" // restore saved ebp/eip 10 | ); 11 | } 12 | 13 | int syscall0(int arg1, int arg2, int arg3, int arg4) 14 | { 15 | asm( 16 | "movl $0, %eax\n" 17 | "addl $4, %esp\n" // skip saved ebp, kernel will skip saved eip 18 | "int $88\n" // syscall int 19 | "subl $4, %esp\n" // restore saved ebp 20 | ); 21 | } 22 | 23 | int main(int argc, char** argv) { 24 | // printf() displays the string inside quotation 25 | printf("Hello, Application World! argc: %u, argv[0]: %s\n", argc, argv[0]); 26 | printf("Calling method 1: SYS_TEST(1,2,3,4): %d\n", syscall(0, 1, 2, 3, 4)); 27 | printf("Calling method 2: SYS_TEST(1,2,3,4): %d\n", syscall0(1, 2, 3, 4)); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /bootloader/Makefile: -------------------------------------------------------------------------------- 1 | DEFAULT_HOST!=../default-host.sh 2 | HOST?=$(DEFAULT_HOST) 3 | HOSTARCH!=../target-triplet-to-arch.sh $(HOST) 4 | 5 | # shell assignment operator ‘!=’ 6 | # Ref: https://www.gnu.org/software/make/manual/html_node/Setting.html#Setting 7 | SYSROOT!=realpath $(SYSROOT) 8 | 9 | # Pad the final binary to 16384 bytes (32 * 512 byte sectors) 10 | # This variable must be in sync with: bootloader.asm and main.c, search "BOOTLOADER_MAX_SIZE" there 11 | BOOTLOADER_MAX_SIZE=16384 12 | 13 | # Start of the Makefile 14 | 15 | ARCHDIR=arch/$(HOSTARCH) 16 | include $(ARCHDIR)/make.config 17 | 18 | OBJS=\ 19 | $(BOOTLOADER_ARCH_OBJS) \ 20 | bootloader/main.o \ 21 | string/string.o \ 22 | tar/tar.o \ 23 | elf/elf.o \ 24 | video/video.o \ 25 | 26 | # Detecting Linux vs macOS 27 | UNAME := $(shell uname) 28 | 29 | .PHONY: all clean install kernel.tar 30 | 31 | all: bootable_kernel.bin bootloader.elf 32 | 33 | $(ARCHDIR)/bootloader.o: $(BOOTLOADER_ASM) $(BOOTLOADER_INCLUDES) 34 | $(AS) $(ASMFLAGS) -o $@ -i $(ARCHDIR) $(BOOTLOADER_ASM) 35 | 36 | # Comming from https://wiki.osdev.org/Bare_Bones 37 | %.o: %.c 38 | $(CC) -g -c $< -o $@ -std=gnu99 -ffreestanding -Wall -Wextra 39 | 40 | bootloader.bin: $(OBJS) 41 | $(CC) -T $(ARCHDIR)/linker.ld -o $@ -ffreestanding -nostdlib $(OBJS) -lgcc -Wl,--oformat,binary 42 | 43 | # Generate elf version to allow gdb debugging 44 | bootloader.elf: $(OBJS) 45 | $(CC) -g -T $(ARCHDIR)/linker.ld -o $@ -ffreestanding -nostdlib $(OBJS) -lgcc -Wl,--oformat,"elf32-i386" 46 | 47 | # Note: Each script line is executed in a different shell 48 | # $(VAR) will be expanded before sending to the shell 49 | # i.e. $(VAR) expansion is not executed in a shell but by make when reading the Makefile 50 | # Any dollar sign needed to be sent must be replaced by $$ in a script line 51 | # Any line of script will terminate the make if the shell returns a non-zero exit code after executing it 52 | bootloader_padded.bin: bootloader.bin 53 | # Make sure the bootloader is less than the max size and pad it to the max size 54 | ifeq ($(UNAME), Linux) 55 | if [ "$(shell stat --format=%s bootloader.bin)" -ge "$(BOOTLOADER_MAX_SIZE)" ]; then exit 1; fi 56 | endif 57 | ifeq ($(UNAME), Darwin) 58 | if [ "$(shell stat -f%z bootloader.bin)" -ge "$(BOOTLOADER_MAX_SIZE)" ]; then exit 1; fi 59 | endif 60 | cp $< $@ 61 | dd if=/dev/zero of=$@ bs=1 count=0 seek=$(BOOTLOADER_MAX_SIZE) 62 | 63 | # A tar file system including all the files under the SYSROOT 64 | # Including the kernel image /boot/simple_os.kernel 65 | # Use --transform to clean up absolute paths recorded in the TAR ball 66 | # e.g. $(SYSROOT)/boot/simple_os.kernel -> /boot/simple_os.kernel 67 | kernel.tar: 68 | $(TAR) -P -cvf $@ --transform="s,^$(SYSROOT)/,/," $(SYSROOT)/* 69 | 70 | bootable_kernel.bin: bootloader_padded.bin kernel.tar 71 | # concat the kernel tarball 72 | # equivalent to cat bootloader_padded.bin kernel.tar > bootable_kernel.bin 73 | cat $^ > $@ 74 | 75 | clean: 76 | rm -f bootloader.bin 77 | rm -f bootloader_padded.bin 78 | rm -f bootable_kernel.bin 79 | rm -f kernel.tar 80 | rm -f bootloader.elf 81 | rm -f *.o 82 | rm -f $(OBJS) *.o */*.o */*/*.o 83 | 84 | install: bootable_kernel.bin bootloader.elf 85 | cp bootable_kernel.bin .. 86 | 87 | -------------------------------------------------------------------------------- /bootloader/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | 1. Setup WSL 4 | 5 | WSL1 is recommended to avoid issue with Hyper-V (e.g. interfering Android simulators) 6 | 1. Setup GCC cross compiler 7 | See: 8 | 1. Adjust Makefile 9 | Notably, point `CC` to your cross-compiler, `KERNEL_ROOT` to your kernel root folder, `KERNEL_BOOT_IMG` to your kernel image 10 | 11 | A kernel to test booting is 12 | 13 | The `KERNEL_BOOT_IMG` shall be a ELF executable 14 | 1. Compile 15 | In WSL: 16 | 17 | ```bash 18 | make clean 19 | make 20 | ``` 21 | 22 | A bootable binary file will be created named `bootable_kernel.bin` 23 | 1. Boot with QEMU 24 | QEMU needs GTK, so we will rather run the QEMU from Windows PowerShell 25 | 26 | ```ps 27 | qemu-system-i386.exe -hda .\bootable_kernel.bin 28 | ``` 29 | 30 | 1. Debugging by GDB 31 | See 32 | In Windows: 33 | 34 | ```ps 35 | qemu-system-i386.exe -s -S -hda .\bootable_kernel.bin 36 | ``` 37 | 38 | Ref: 39 | In WSL: 40 | 41 | ```bash 42 | gdb -ex "target remote localhost:1234" -ex "symbol-file bootloader.elf" 43 | ``` 44 | -------------------------------------------------------------------------------- /bootloader/arch/i386/ata.h: -------------------------------------------------------------------------------- 1 | #ifndef ARCH_I386_ATA_H 2 | #define ARCH_I386_ATA_H 3 | 4 | #include 5 | #include "port_io.h" 6 | 7 | // 28 bit ATA PIO disk driver 8 | // From http://learnitonweb.com/2020/05/22/12-developing-an-operating-system-tutorial-episode-6-ata-pio-driver-osdev/ 9 | // Source - https://wiki.osdev.org/ATA_PIO_Mode#x86_Directions 10 | 11 | /* 12 | BSY: a 1 means that the controller is busy executing a command. No register should be accessed (except the digital output register) while this bit is set. 13 | RDY: a 1 means that the controller is ready to accept a command, and the drive is spinning at correct speed.. 14 | WFT: a 1 means that the controller detected a write fault. 15 | SKC: a 1 means that the read/write head is in position (seek completed). 16 | DRQ: a 1 means that the controller is expecting data (for a write) or is sending data (for a read). Don't access the data register while this bit is 0. 17 | COR: a 1 indicates that the controller had to correct data, by using the ECC bytes (error correction code: extra bytes at the end of the sector that allows to verify its integrity and, sometimes, to correct errors). 18 | IDX: a 1 indicates the the controller retected the index mark (which is not a hole on hard-drives). 19 | ERR: a 1 indicates that an error occured. An error code has been placed in the error register. 20 | */ 21 | 22 | #define STATUS_BSY 0x80 23 | #define STATUS_RDY 0x40 24 | #define STATUS_DRQ 0x08 25 | #define STATUS_DF 0x20 26 | #define STATUS_ERR 0x01 27 | 28 | void read_sectors_ATA_PIO(void* buf, uint32_t LBA, uint32_t sector_count); 29 | uint32_t get_total_28bit_sectors(); 30 | 31 | #endif -------------------------------------------------------------------------------- /bootloader/arch/i386/disk.asm: -------------------------------------------------------------------------------- 1 | ; global disk_load 2 | ; Taken from https://github.com/cfenollosa/os-tutorial/blob/master/07-bootsector-disk/boot_sect_disk.asm 3 | 4 | 5 | ; load 'dh' sectors from drive 'dl' into ES:BX 6 | disk_load: 7 | pusha 8 | ; reading from disk requires setting specific values in all registers 9 | ; so we will overwrite our input parameters from 'dx'. Let's save it 10 | ; to the stack for later use. 11 | push dx 12 | 13 | mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read' 14 | mov al, dh ; al <- number of sectors to read (0x01 .. 0x80) 15 | mov cl, 0x02 ; cl <- sector (0x01 .. 0x11) 16 | ; 0x01 is our boot sector, 0x02 is the first 'available' sector 17 | mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl') 18 | ; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS 19 | ; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2) 20 | mov dh, 0x00 ; dh <- head number (0x0 .. 0xF) 21 | 22 | ; [es:bx] <- pointer to buffer where the data will be stored 23 | ; caller sets it up for us, and it is actually the standard location for int 13h 24 | int 0x13 ; BIOS interrupt 25 | jc disk_error ; if error (stored in the carry bit) 26 | 27 | pop dx 28 | cmp al, dh ; BIOS also sets 'al' to the # of sectors read. Compare it. 29 | jne sectors_error 30 | popa 31 | ret 32 | 33 | 34 | disk_error: 35 | mov bx, DISK_ERROR 36 | call print 37 | call print_nl 38 | mov dh, ah ; ah = error code, dl = disk drive that dropped the error 39 | call print_hex ; check out the code at http://stanislavs.org/helppc/int_13-1.html 40 | jmp disk_loop 41 | 42 | sectors_error: 43 | mov bx, SECTORS_ERROR 44 | call print 45 | 46 | disk_loop: 47 | jmp $ 48 | 49 | DISK_ERROR: db "Disk read error", 0 50 | SECTORS_ERROR: db "Incorrect number of sectors read", 0 -------------------------------------------------------------------------------- /bootloader/arch/i386/gdt.asm: -------------------------------------------------------------------------------- 1 | ; global gdt_descriptor, CODE_SEG, DATA_SEG 2 | ; Based on https://github.com/cfenollosa/os-tutorial/tree/master/09-32bit-gdt 3 | 4 | push CODE_SEG 5 | push DATA_SEG 6 | 7 | ; Using Flat Setup in GDT, basically leaving the offsets used in addressing untranslated 8 | gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps 9 | ; the GDT starts with a null 8-byte 10 | dd 0x0 ; 4 byte 11 | dd 0x0 ; 4 byte 12 | 13 | ; GDT for code segment. base = 0x00000000, length = 0xfffff 14 | ; for flags, refer to os-dev.pdf document, page 36 15 | ; Ref: https://wiki.osdev.org/Global_Descriptor_Table 16 | gdt_code: 17 | dw 0xffff ; segment length, bits 0-15 18 | dw 0x0 ; segment base, bits 0-15 19 | db 0x0 ; segment base, bits 16-23 20 | db 1001_1010b ; flags (8 bits) 21 | db 1100_1111b ; flags (4 bits) + segment length, bits 16-19 22 | db 0x0 ; segment base, bits 24-31 23 | 24 | ; GDT for data segment. base and length identical to code segment 25 | ; some flags changed, again, refer to os-dev.pdf 26 | gdt_data: 27 | dw 0xffff 28 | dw 0x0 29 | db 0x0 30 | db 1001_0010b 31 | db 1100_1111b 32 | db 0x0 33 | 34 | ; TODO: Add TSS segment descriptor here 35 | 36 | gdt_end: 37 | 38 | ; GDT descriptor to be loaded by lgdt instruction 39 | gdt_descriptor: 40 | dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size 41 | dd gdt_start ; address (32 bit) 42 | 43 | ; define some constants for later use 44 | ; these are segment selector used in protected mode 45 | ; Ref: https://wiki.osdev.org/Selector 46 | ; When Requested Privilege Level is 0 and we are using GDT, they are the same as the byte offset into the GDT table 47 | ; Since the GDT entry has a length of 8 bytes and and first two bits of selector is RPL, the third is to choose GDT (1b) or LDT (0b) 48 | ; and the remaining of the 16bits selector is the 0-based index into the GDT 49 | 50 | 51 | CODE_SEG equ 0000000000001_0_00b ; gdt_code - gdt_start is also correct 52 | DATA_SEG equ 0000000000010_0_00b ; gdt_data - gdt_start is also correct 53 | -------------------------------------------------------------------------------- /bootloader/arch/i386/linker.ld: -------------------------------------------------------------------------------- 1 | /* 2 | From: http://3zanders.co.uk/2017/10/18/writing-a-bootloader3/ 3 | */ 4 | 5 | ENTRY(boot) 6 | SECTIONS { 7 | /* Telling the linker that the bootloader will be loaded at 0x7c00, the convensional boot sector address */ 8 | . = 0x7c00; 9 | /* Make sure the .boot section is at the start of the file, so the 0xAA55 magic number/MBR signature will be placed at the correct location */ 10 | .text : 11 | { 12 | *(.boot) 13 | *(.text) 14 | } 15 | .rodata : 16 | { 17 | *(.rodata) 18 | } 19 | .data : 20 | { 21 | *(.data) 22 | } 23 | .bss : 24 | { 25 | *(.bss) 26 | } 27 | } -------------------------------------------------------------------------------- /bootloader/arch/i386/make.config: -------------------------------------------------------------------------------- 1 | BOOTLOADER_INCLUDES=\ 2 | $(ARCHDIR)/print.asm \ 3 | $(ARCHDIR)/print_hex.asm \ 4 | $(ARCHDIR)/disk.asm \ 5 | $(ARCHDIR)/a20.asm \ 6 | $(ARCHDIR)/gdt.asm \ 7 | $(ARCHDIR)/switch_pm.asm \ 8 | $(ARCHDIR)/print_pm.asm \ 9 | $(ARCHDIR)/memory.asm \ 10 | $(ARCHDIR)/vesa.asm \ 11 | $(ARCHDIR)/utility.asm \ 12 | 13 | 14 | BOOTLOADER_ASM=$(ARCHDIR)/bootloader.asm 15 | 16 | BOOTLOADER_ARCH_C_OBJS=\ 17 | $(ARCHDIR)/ata.o \ 18 | $(ARCHDIR)/tty.o 19 | 20 | BOOTLOADER_ARCH_ASM_OBJS=\ 21 | $(ARCHDIR)/bootloader.o 22 | 23 | BOOTLOADER_ARCH_OBJS=\ 24 | $(BOOTLOADER_ARCH_ASM_OBJS) \ 25 | $(BOOTLOADER_ARCH_C_OBJS) 26 | 27 | -------------------------------------------------------------------------------- /bootloader/arch/i386/memory.asm: -------------------------------------------------------------------------------- 1 | ; source https://wiki.osdev.org/Detecting_Memory_(x86)#Getting_an_E820_Memory_Map 2 | ; use the INT 0x15, eax= 0xE820 BIOS function to get a memory map 3 | ; note: initially di is 0, be sure to set it to a value so that the BIOS code will not be overwritten. 4 | ; The consequence of overwriting the BIOS code will lead to problems like getting stuck in `int 0x15` 5 | ; outputs: global MMAP, MMAP_COUNT 6 | ; MMAP_COUNT will store a dword count of entries; 7 | ; MMAP the memory layout structure 8 | ; If failed, hang 9 | 10 | ; Maximum number of memory map items 11 | MMAP_MAX_COUNT equ 100 12 | 13 | ; E820 memory map item is 24 bytes long 14 | MMAP_ITEM_SIZE equ 24 15 | 16 | ; declare the address of storing the memory map as global variables 17 | ; so it can be accessed in C 18 | global MMAP, MMAP_COUNT 19 | MMAP_COUNT dw 0 20 | MMAP: times (MMAP_MAX_COUNT*MMAP_ITEM_SIZE) db 0 21 | 22 | detect_memory_map_by_e820: 23 | pusha 24 | 25 | mov di, MMAP ; Set di to 0x0504. Otherwise this code will get stuck in `int 0x15` after some entries are fetched 26 | xor ebx, ebx ; ebx must be 0 to start 27 | xor bp, bp ; keep an entry count in bp 28 | mov edx, 0x0534D4150 ; Place "SMAP" into edx 29 | mov eax, 0xe820 30 | 31 | mov dword [es:di], MMAP_ITEM_SIZE; populate `size` field of standard multi-boot memory layout entry 32 | add di, 4 33 | 34 | mov [es:di + 20], dword 1 ; force a valid ACPI 3.X entry 35 | mov ecx, MMAP_ITEM_SIZE ; ask for 24 bytes 36 | int 0x15 37 | jc short .failed ; carry set on first call means "unsupported function" 38 | mov edx, 0x0534D4150 ; Some BIOSes apparently trash this register? 39 | cmp eax, edx ; on success, eax must have been reset to "SMAP" 40 | jne short .failed 41 | test ebx, ebx ; ebx = 0 implies list is only 1 entry long (worthless) 42 | je short .failed 43 | jmp short .jmpin 44 | .e820lp: 45 | mov eax, 0xe820 ; eax, ecx get trashed on every int 0x15 call 46 | 47 | mov dword [es:di], MMAP_ITEM_SIZE; populate `size` field of standard multi-boot memory layout entry 48 | add di, 4 49 | 50 | mov [es:di + MMAP_ITEM_SIZE - 4], dword 1 ; force a valid ACPI 3.X entry 51 | mov ecx, MMAP_ITEM_SIZE ; ask for 24 bytes again 52 | int 0x15 53 | jc short .e820f ; carry set means "end of list already reached" 54 | mov edx, 0x0534D4150 ; repair potentially trashed register 55 | .jmpin: 56 | jcxz .skipent ; skip any 0 length entries 57 | cmp cl, MMAP_ITEM_SIZE-4 ; got a 24 byte ACPI 3.X response? 58 | jbe short .notext 59 | test byte [es:di + MMAP_ITEM_SIZE-4], 1 ; if so: is the "ignore this data" bit clear? 60 | je short .skipent 61 | .notext: 62 | mov ecx, [es:di + 8] ; get lower uint32_t of memory region length 63 | or ecx, [es:di + 12] ; "or" it with upper uint32_t to test for zero 64 | jz .skipent ; if length uint64_t is 0, skip entry 65 | inc bp ; got a good entry: ++count, move to next storage spot 66 | cmp bp, MMAP_MAX_COUNT 67 | jg .failed_too_many 68 | add di, MMAP_ITEM_SIZE 69 | .skipent: 70 | test ebx, ebx ; if ebx resets to 0, list is complete 71 | jne short .e820lp 72 | .e820f: 73 | ; mov ebx, [ADDR_MMAP_COUNT] 74 | ; mov [ebx], bp ; store the entry count 75 | mov [MMAP_COUNT], bp 76 | clc ; there is "jc" on end of list to this point, so the carry must be cleared 77 | 78 | ; Print success message 79 | mov bx, MSG_E820_SUCESS0 80 | call print 81 | mov dx, bp 82 | call print_hex 83 | mov bx, MSG_E820_SUCESS1 84 | call print 85 | call print_nl 86 | 87 | popa 88 | ret 89 | .failed: 90 | mov bx, MSG_E820_FAILED 91 | call print 92 | jmp $ 93 | .failed_too_many: 94 | mov bx, MSG_E820_FAILED_TOO_MANY_SEG 95 | call print 96 | jmp $ 97 | 98 | 99 | MSG_E820_FAILED db "E820 memory detection failed, hanged", 0 100 | MSG_E820_FAILED_TOO_MANY_SEG db "E820 detected too many segments, hanged", 0 101 | MSG_E820_SUCESS0 db "E820 memory detection success with ", 0 102 | MSG_E820_SUCESS1 db " memory segments", 0 -------------------------------------------------------------------------------- /bootloader/arch/i386/port_io.h: -------------------------------------------------------------------------------- 1 | #ifndef ARCH_I386_IO_H 2 | #define ARCH_I386_IO_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // From https://wiki.osdev.org/Inline_Assembly/Examples 9 | 10 | static inline void outb(uint16_t port, uint8_t val) 11 | { 12 | asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) ); 13 | /* There's an outb %al, $imm8 encoding, for compile-time constant port numbers that fit in 8b. (N constraint). 14 | * Wider immediate constants would be truncated at assemble-time (e.g. "i" constraint). 15 | * The outb %al, %dx encoding is the only option for all other cases. 16 | * %1 expands to %dx because port is a uint16_t. %w1 could be used if we had the port number a wider C type */ 17 | } 18 | 19 | static inline void outw(uint16_t port, uint16_t val) 20 | { 21 | asm volatile ( "outw %0, %1" : : "a"(val), "Nd"(port) ); 22 | } 23 | 24 | static inline void outl(uint16_t port, uint32_t val) 25 | { 26 | asm volatile ( "outl %0, %1" : : "a"(val), "Nd"(port) ); 27 | } 28 | 29 | static inline uint8_t inb(uint16_t port) 30 | { 31 | uint8_t ret; 32 | asm volatile ( "inb %1, %0" 33 | : "=a"(ret) 34 | : "Nd"(port) ); 35 | return ret; 36 | } 37 | 38 | static inline uint16_t inw(uint16_t port) 39 | { 40 | uint16_t ret; 41 | asm volatile ( "inw %1, %0" 42 | : "=a"(ret) 43 | : "Nd"(port) ); 44 | return ret; 45 | } 46 | 47 | static inline uint32_t inl(uint16_t port) 48 | { 49 | uint32_t ret; 50 | asm volatile ( "inl %1, %0" 51 | : "=a"(ret) 52 | : "Nd"(port) ); 53 | return ret; 54 | } 55 | 56 | static inline void io_wait(void) 57 | { 58 | /* Port 0x80 is used for 'checkpoints' during POST. */ 59 | /* The Linux kernel seems to think it is free for use :-/ */ 60 | asm volatile ( "outb %%al, $0x80" : : "a"(0) ); 61 | /* %%al instead of %0 makes no difference. TODO: does the register need to be zeroed? */ 62 | } 63 | 64 | #endif -------------------------------------------------------------------------------- /bootloader/arch/i386/print.asm: -------------------------------------------------------------------------------- 1 | ; global print, print_nl 2 | ; Taken from https://github.com/cfenollosa/os-tutorial/tree/master/05-bootsector-functions-strings 3 | 4 | ; real mode print using BIOS interrupt 5 | ; print at current cursor location 6 | ; start address of null terminated string stored in bx before call 7 | print: 8 | pusha 9 | 10 | ; keep this in mind: 11 | ; while (string[i] != 0) { print string[i]; i++ } 12 | 13 | ; the comparison for string end (null byte) 14 | start: 15 | mov al, [bx] ; 'bx' is the base address for the string 16 | cmp al, 0 17 | je done 18 | 19 | ; the part where we print with the BIOS help 20 | mov ah, 0x0e 21 | int 0x10 ; 'al' already contains the char 22 | 23 | ; increment pointer and do next loop 24 | add bx, 1 25 | jmp start 26 | 27 | done: 28 | popa 29 | ret 30 | 31 | ; print cx characters from bx 32 | print_cx: 33 | pusha 34 | 35 | .print_cx_start: 36 | mov ah, 0x0e 37 | mov al, [bx] ; newline char 38 | int 0x10 39 | add bx, 1 40 | loop .print_cx_start 41 | 42 | popa 43 | ret 44 | 45 | ; real mode print newline using BIOS interrupt 46 | ; print at current cursor location 47 | print_nl: 48 | pusha 49 | 50 | mov ah, 0x0e 51 | mov al, 0x0a ; newline char 52 | int 0x10 53 | mov al, 0x0d ; carriage return 54 | int 0x10 55 | 56 | popa 57 | ret -------------------------------------------------------------------------------- /bootloader/arch/i386/print_hex.asm: -------------------------------------------------------------------------------- 1 | ; extern print 2 | ; global print_hex 3 | ; Taken from https://github.com/cfenollosa/os-tutorial/tree/master/05-bootsector-functions-strings 4 | 5 | ; receiving the data in 'dx' 6 | ; For the examples we'll assume that we're called with dx=0x1234 7 | print_hex: 8 | pusha 9 | 10 | mov cx, 0 ; our index variable 11 | 12 | ; Strategy: get the last char of 'dx', then convert to ASCII 13 | ; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N. 14 | ; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40 15 | ; Then, move the ASCII byte to the correct position on the resulting string 16 | hex_loop: 17 | cmp cx, 4 ; loop 4 times 18 | je end 19 | 20 | ; 1. convert last char of 'dx' to ascii 21 | mov ax, dx ; we will use 'ax' as our working register 22 | and ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zeros 23 | add al, 0x30 ; add 0x30 to N to convert it to ASCII "N" 24 | cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F' 25 | jle step2 26 | add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7 27 | 28 | step2: 29 | ; 2. get the correct position of the string to place our ASCII char 30 | ; bx <- base address + string length - index of char 31 | mov bx, HEX_OUT + 5 ; base + length 32 | sub bx, cx ; our index variable 33 | mov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx' 34 | ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234 35 | 36 | ; increment index and loop 37 | add cx, 1 38 | jmp hex_loop 39 | 40 | end: 41 | ; prepare the parameter and call the function 42 | ; remember that print receives parameters in 'bx' 43 | mov bx, HEX_OUT 44 | call print 45 | 46 | popa 47 | ret 48 | 49 | HEX_OUT: 50 | db '0x0000',0 ; reserve memory for our new string 51 | 52 | ; print cx bytes of memory pointed by bx in hex 53 | print_hex_cx: 54 | pusha 55 | 56 | .print_hex_cx_one_byte: 57 | 58 | mov ah, 0x0e 59 | mov al, '0' 60 | int 0x10 61 | mov al, 'x' 62 | int 0x10 63 | 64 | mov dl, [bx] 65 | add bx, 1 66 | 67 | mov al, dl 68 | 69 | ; print MSB 70 | shr al, 4 71 | add al, 0x30 ; add 0x30 to N to convert it to ASCII "N" 72 | cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F' 73 | jle .print_msb 74 | add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7 75 | .print_msb: 76 | int 0x10 77 | 78 | ; print LSB 79 | and dl, 0x0f 80 | add dl, 0x30 ; add 0x30 to N to convert it to ASCII "N" 81 | cmp dl, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F' 82 | jle .print_lsb 83 | add dl, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7 84 | .print_lsb: 85 | mov al, dl 86 | int 0x10 87 | 88 | mov al, ' ' 89 | int 0x10 90 | 91 | loop .print_hex_cx_one_byte 92 | 93 | popa 94 | ret 95 | 96 | -------------------------------------------------------------------------------- /bootloader/arch/i386/print_pm.asm: -------------------------------------------------------------------------------- 1 | ; global print_pm, WHITE_ON_BLACK, print_memory_hex 2 | 3 | [bits 32] ; using 32-bit protected mode 4 | 5 | ; this is how constants are defined 6 | VIDEO_MEMORY equ 0xb8000 7 | WHITE_ON_BLACK equ 0000_1111b ; color attribute: white character on black background 8 | 9 | 10 | ; print string in 32-bit protected mode using VGA text mode 11 | ; The screen will display 80 columns x 25 rows colored characters 12 | ; input 13 | ; esi = the starting address of a null terminated string 14 | ; ah = the char color attribute 15 | ; edi = 0-based character index on screen (0 ~ 16000 = 80 columns x 25 rows x 8 screens) 16 | ; Ref: https://en.wikipedia.org/wiki/VGA_text_mode 17 | ; Attribute bits: [Blink BackgroundRed BackgroundGreen BackgroundBlue ForegroundIntensity ForegroundRed ForegroundGreen ForegroundBlue] 18 | ; E.g. 0000_1111b means black background (R=G=B=0 -> Black) and white foreground/font (R=G=B=1 -> White) and show in intensified font 19 | print_pm: 20 | pusha 21 | add edi, edi ; each characters occupy two bytes 22 | add edi, VIDEO_MEMORY 23 | 24 | print_pm_loop: 25 | mov al, [esi] ; [esi] is the address of our character 26 | 27 | cmp al, 0 ; check if end of string 28 | je print_pm_done 29 | 30 | mov [edi], ax ; store character + attribute in video memory 31 | add esi, 1 ; next char 32 | add edi, 2 ; next video memory position 33 | 34 | jmp print_pm_loop 35 | 36 | print_pm_done: 37 | popa 38 | ret 39 | 40 | 41 | ; convert a byte in dl to its hex representation by two ASCII chars storing in ax 42 | ; dl = 0x1A -> dl = '1', dh = 'A' 43 | byte_to_hex_string: 44 | mov dh, dl ; dh = dl = 0x1A 45 | shr dl, 4 ; dl: 0x1A -> 0x01 46 | add dl, 0x30 ; add 0x30 to 1 to convert it to ASCII "1" 47 | cmp dl, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F' 48 | jle .next_char 49 | add dl, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7 50 | .next_char: 51 | and dh, 0x0f ; dh: 0x1A -> 0x0A 52 | add dh, 0x30 ; add 0x30 to A to convert it to ASCII "A" 53 | cmp dh, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F' 54 | jle .byte_to_hex_string_end 55 | add dh, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7 56 | .byte_to_hex_string_end: 57 | ret 58 | 59 | ; print a block of memory in hex 60 | ; input 61 | ; esi: print memory location start from here 62 | ; ecx: count (max 65535) 63 | ; ah: char color attribute 64 | ; edi = 0-based character index on screen (0 ~ 16000 = 80 rows x 25 columns x 8 screens), should be multiple of 80 65 | print_memory_hex: 66 | pusha 67 | mov ebx, 0 ; index of the current byte 68 | 69 | add edi, edi 70 | add edi, VIDEO_MEMORY 71 | 72 | .loop: 73 | mov dl, [esi+ebx] 74 | call byte_to_hex_string 75 | 76 | ; print '1A ', each byte will need 3 ASCII characters 77 | mov al, dl 78 | mov [edi], ax 79 | mov al, dh 80 | mov [edi+2], ax 81 | mov al, ' ' 82 | mov [edi+4], ax 83 | 84 | add edi, 6 85 | inc ebx 86 | 87 | ; if the byte just printed is multiple of 16, wrap to new line 88 | 89 | push eax ; save ah 90 | push ebx 91 | 92 | mov eax, ebx 93 | mov edx, 0 94 | mov ebx, 16 95 | div ebx 96 | 97 | pop ebx 98 | pop eax ; restore ah 99 | 100 | cmp edx, 0 ; test if ebx mod 16 = 0 101 | jnz .next 102 | push ecx 103 | mov ecx, 32 104 | mov al, ' ' 105 | .padding: ; wrap to new line by 32 space characters as padding, since 16 bytes will already occupy 16*3 = 48 characters, so 80 - 48 = 32 106 | mov [edi], ax 107 | add edi, 2 108 | loop .padding 109 | pop ecx 110 | .next: 111 | loop .loop 112 | 113 | popa 114 | ret 115 | -------------------------------------------------------------------------------- /bootloader/arch/i386/switch_pm.asm: -------------------------------------------------------------------------------- 1 | ; extern BEGIN_PM, do_e820 2 | ; global switch_to_pm 3 | 4 | [bits 16] 5 | 6 | switch_to_pm: 7 | 8 | ; mov bx, MSG_SWITCH_PM_START 9 | ; call print 10 | ; call print_nl 11 | 12 | ; start the procedure of entering real mode 13 | ; ref: https://wiki.osdev.org/Protected_Mode 14 | 15 | ; 1. Disable interrupts, including NMI (as suggested by Intel Developers Manual) 16 | ; void NMI_disable() { 17 | ; outb(0x70, inb(0x70) | 0x80); 18 | ; } 19 | ; port 0x70 is the CMOS register selector, the highest bit controls the NMI, set means disabled and clear means enabled 20 | ; see https://wiki.osdev.org/CMOS#CMOS_Registers and https://wiki.osdev.org/I/O_Ports 21 | in al, 0x70 22 | or al, 1000_0000b 23 | out 0x70, al 24 | 25 | ; mov bx, MSG_SWITCH_PM_NMI 26 | ; call print 27 | ; call print_nl 28 | 29 | ; 2. Enable the A20 Line 30 | call enable_a20 31 | cmp ax, 1 32 | je .a20_enabled 33 | .a20_enable_failed: 34 | mov bx, MSG_A20_ENABLE_FAILED 35 | call print 36 | .a20_hang: 37 | jmp .a20_hang ; if failed in enabling A20, hang 38 | .a20_enabled: 39 | ; mov bx, MSG_A20_ENABLED 40 | ; call print 41 | ; call print_nl 42 | 43 | ; 3. Load the Global Descriptor Table with segment descriptors suitable for code, data, and stack 44 | cli ; 1. disable interrupts 45 | lgdt [gdt_descriptor] ; 2. load the GDT descriptor 46 | mov eax, cr0 47 | or eax, 0x1 ; 3. set 32-bit mode bit (Protected Mode Enable bit) in cr0 (https://en.wikipedia.org/wiki/Control_register) 48 | mov cr0, eax 49 | jmp CODE_SEG:init_pm ; 4. far jump by using a different segment, basically setting CS to point to the correct protected mode segment (GDT entry) 50 | 51 | 52 | 53 | MSG_SWITCH_PM_START db "Start switching to Protected_Mode", 0 54 | MSG_SWITCH_PM_NMI db "NMI Disabled", 0 55 | MSG_A20_ENABLED db "A20 enabled", 0 56 | MSG_A20_ENABLE_FAILED db "Enabling A20 failed, hanged", 0 57 | 58 | 59 | [bits 32] 60 | init_pm: ; we are now using 32-bit instructions 61 | mov ax, DATA_SEG ; 5. update the segment registers other than CS 62 | mov ds, ax 63 | mov ss, ax 64 | mov es, ax 65 | mov fs, ax 66 | mov gs, ax 67 | 68 | 69 | mov ebp, 0x00F00000 ; 6. update the stack right at the top of the free space, a 14MiB space above 1MiB. Ref: https://wiki.osdev.org/Memory_Map_(x86) 70 | mov esp, ebp 71 | 72 | call BEGIN_PM ; 7. Call a well-known label with useful code 73 | 74 | -------------------------------------------------------------------------------- /bootloader/arch/i386/tty.c: -------------------------------------------------------------------------------- 1 | #include "tty.h" 2 | #include "vga.h" 3 | #include "../../video/video.h" 4 | 5 | #define COLOR_WHITE 0x00FFFFFF 6 | #define COLOR_BLACK 0x0 7 | 8 | static int video_mode; 9 | static size_t TEXT_WIDTH; 10 | static size_t TEXT_HEIGHT; 11 | 12 | static uint16_t* const VGA_MEMORY = (uint16_t*)0xB8000; 13 | 14 | // Print a null terminated string 15 | // 16 | // str: null terminated string 17 | // row: printing start at this screen row (0 ~ (TEXT_WIDTH-1)) 18 | // col: printing start at this screen column (0 ~ (TEXT_HEIGHT-1)) 19 | void print_str(const char* str, uint8_t row, uint8_t col) { 20 | print_memory(str, strlen(str), row, col); 21 | } 22 | 23 | // Print the content of a memory location in ASCII 24 | // buff: address of a memory location 25 | // size: length in bytes to print 26 | // row: printing start at this screen row (0 ~ (TEXT_WIDTH-1)) 27 | // col: printing start at this screen column (0 ~ (TEXT_HEIGHT-1)) 28 | void print_memory(const char* buff, size_t size, uint8_t row, uint8_t col) { 29 | if (row >= TEXT_HEIGHT || col >= TEXT_WIDTH) { 30 | return; 31 | } 32 | for (size_t i = 0; i < size; i++) { 33 | // Scrolling not implemented 34 | if(video_mode) { 35 | drawchar_textmode(buff[i], col++, row, COLOR_BLACK, COLOR_WHITE); 36 | if(col >= TEXT_WIDTH) { 37 | row++; 38 | col = 0; 39 | } 40 | } else { 41 | VGA_MEMORY[(row*TEXT_WIDTH) + col + i] = vga_entry(buff[i], vga_entry_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK)); 42 | } 43 | } 44 | } 45 | 46 | // Print the content of a memory location in hex, 16 bytes per row 47 | // buff: address of a memory location 48 | // size: length in bytes to print 49 | // row: printing start at this screen row (0 ~ (VGA_HEIGHT-1)) 50 | void print_memory_hex(const char* buff, size_t size, uint8_t row) { 51 | if (row >= TEXT_HEIGHT) { 52 | return; 53 | } 54 | 55 | uint32_t col = 0; 56 | for (uint32_t i = 0; i < size; i++) { 57 | // >> on signed char is arithmetic shift rather than logical, so we need to mask the significant bits 58 | uint8_t high = ((buff[i] >> 4) & 0x0F) + 0x30; 59 | if (high > 0x39) { 60 | high += 7; 61 | } 62 | uint8_t low = (buff[i] & 0x0F) + 0x30; 63 | if (low > 0x39) { 64 | low += 7; 65 | } 66 | // print 16 bytes per row 67 | if (i % 16 == 0 && i != 0) { 68 | row++; 69 | // cursor = (row - 1) * TEXT_WIDTH; 70 | col = 0; 71 | } 72 | 73 | // Scrolling not implemented 74 | if(video_mode) { 75 | drawchar_textmode(high, col++, row, COLOR_BLACK, COLOR_WHITE); 76 | drawchar_textmode(low, col++, row, COLOR_BLACK, COLOR_WHITE); 77 | drawchar_textmode(' ', col++, row, COLOR_BLACK, COLOR_WHITE); 78 | if(col >= TEXT_WIDTH) { 79 | row++; 80 | col = 0; 81 | } 82 | } else { 83 | size_t cursor = row * TEXT_WIDTH + col; 84 | VGA_MEMORY[cursor++] = vga_entry(high, vga_entry_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK)); 85 | VGA_MEMORY[cursor++] = vga_entry(low, vga_entry_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK)); 86 | VGA_MEMORY[cursor++] = vga_entry(' ', vga_entry_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK)); 87 | } 88 | } 89 | // VGA_MEMORY[cursor] = vga_entry(0, vga_entry_color(VGA_COLOR_WHITE, VGA_COLOR_BLACK)); 90 | 91 | } 92 | 93 | void init_tty(multiboot_info_t* info) 94 | { 95 | if( 96 | info->flags & MULTIBOOT_INFO_VBE_INFO && 97 | info->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB 98 | ) { 99 | video_mode = 1; 100 | get_textmode_screen_size(&TEXT_WIDTH,&TEXT_HEIGHT); 101 | } else { 102 | video_mode = 0; 103 | TEXT_WIDTH = 80; 104 | TEXT_HEIGHT = 25; 105 | } 106 | 107 | } -------------------------------------------------------------------------------- /bootloader/arch/i386/tty.h: -------------------------------------------------------------------------------- 1 | #ifndef TTY_H 2 | #define TTY_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "../../string/string.h" 8 | #include "../../multiboot/multiboot.h" 9 | 10 | void print_str(const char* str, uint8_t row, uint8_t col); 11 | void print_memory(const char* buff, size_t size, uint8_t row, uint8_t col); 12 | void print_memory_hex(const char* buff, size_t size, uint8_t row); 13 | void init_tty(multiboot_info_t* info); 14 | 15 | #endif -------------------------------------------------------------------------------- /bootloader/arch/i386/utility.asm: -------------------------------------------------------------------------------- 1 | ; move cx bytes from es:si to es:di 2 | memcpy: 3 | pusha 4 | rep movsb 5 | popa 6 | ret 7 | 8 | ; compare cx bytes from es:si and es:di 9 | ; set comparison flags accordingly 10 | memcmp: 11 | pusha 12 | ; repe: rep while equal 13 | repe cmpsb 14 | popa 15 | ret 16 | -------------------------------------------------------------------------------- /bootloader/arch/i386/vga.h: -------------------------------------------------------------------------------- 1 | #ifndef ARCH_I386_VGA_H 2 | #define ARCH_I386_VGA_H 3 | 4 | #include 5 | 6 | // From https://wiki.osdev.org/Meaty_Skeleton 7 | 8 | enum vga_color { 9 | VGA_COLOR_BLACK = 0, 10 | VGA_COLOR_BLUE = 1, 11 | VGA_COLOR_GREEN = 2, 12 | VGA_COLOR_CYAN = 3, 13 | VGA_COLOR_RED = 4, 14 | VGA_COLOR_MAGENTA = 5, 15 | VGA_COLOR_BROWN = 6, 16 | VGA_COLOR_LIGHT_GREY = 7, 17 | VGA_COLOR_DARK_GREY = 8, 18 | VGA_COLOR_LIGHT_BLUE = 9, 19 | VGA_COLOR_LIGHT_GREEN = 10, 20 | VGA_COLOR_LIGHT_CYAN = 11, 21 | VGA_COLOR_LIGHT_RED = 12, 22 | VGA_COLOR_LIGHT_MAGENTA = 13, 23 | VGA_COLOR_LIGHT_BROWN = 14, 24 | VGA_COLOR_WHITE = 15, 25 | }; 26 | 27 | static inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) { 28 | return fg | bg << 4; 29 | } 30 | 31 | static inline uint16_t vga_entry(unsigned char uc, uint8_t color) { 32 | return (uint16_t)uc | (uint16_t)color << 8; 33 | } 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /bootloader/elf/elf.c: -------------------------------------------------------------------------------- 1 | #include "elf.h" 2 | 3 | // Ref: http://www.skyfree.org/linux/references/ELF_Format.pdf 4 | 5 | // Test if the buff is a ELF image by checking the magic number 6 | bool is_elf(const char* buff) { 7 | return (buff[0] == ELFMAG[0]) && (buff[1] == ELFMAG[1]) && (buff[2] == ELFMAG[2]) && (buff[3] == ELFMAG[3]); 8 | } 9 | 10 | // Load a ELF executable to memory according to ELF meta information 11 | // 12 | // buff: pointer to the start of the ELF file buffer 13 | // 14 | // return: the physical address of the program entry point 15 | Elf32_Addr load_elf(const char* buff) { 16 | const Elf32_Ehdr* header = (const Elf32_Ehdr*)buff; 17 | Elf32_Half n_program_header = header->e_phnum; 18 | const Elf32_Phdr* program_header_table = (const Elf32_Phdr*)(((uint32_t)buff) + header->e_phoff); 19 | Elf32_Addr entry_point = header->e_entry; 20 | Elf32_Addr entry_point_physical = entry_point; 21 | for (Elf32_Half i = 0; i < n_program_header; i++) { 22 | Elf32_Phdr program_header = program_header_table[i]; 23 | if (program_header.p_type == PT_LOAD) { 24 | char* dest_ptr = (char*)program_header.p_paddr; 25 | char* src_ptr = (char*)buff + program_header.p_offset; 26 | // Detect offset of virtual (linear) address vs physical address 27 | if (entry_point >= program_header.p_vaddr && entry_point < program_header.p_vaddr + program_header.p_memsz) { 28 | // use if-else to avoid negative number 29 | if (program_header.p_paddr > program_header.p_vaddr) { 30 | entry_point_physical = entry_point + (program_header.p_paddr - program_header.p_vaddr); 31 | } else { 32 | entry_point_physical = entry_point - (program_header.p_vaddr - program_header.p_paddr); 33 | } 34 | } 35 | 36 | for (Elf32_Word j = 0; j < program_header.p_memsz; j++) { 37 | if (j < program_header.p_filesz) { 38 | dest_ptr[j] = src_ptr[j]; 39 | } else { 40 | dest_ptr[j] = 0; 41 | } 42 | } 43 | } 44 | } 45 | return entry_point_physical; 46 | } -------------------------------------------------------------------------------- /bootloader/elf/elf.h: -------------------------------------------------------------------------------- 1 | #ifndef ELF_H 2 | #define ELF_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // The following are copied from /usr/include/elf.h (Ubuntu) 9 | 10 | /* Type for a 16-bit quantity. */ 11 | typedef uint16_t Elf32_Half; 12 | 13 | /* Types for signed and unsigned 32-bit quantities. */ 14 | typedef uint32_t Elf32_Word; 15 | typedef int32_t Elf32_Sword; 16 | 17 | /* Types for signed and unsigned 64-bit quantities. */ 18 | typedef uint64_t Elf32_Xword; 19 | typedef int64_t Elf32_Sxword; 20 | 21 | /* Type of addresses. */ 22 | typedef uint32_t Elf32_Addr; 23 | 24 | /* Type of file offsets. */ 25 | typedef uint32_t Elf32_Off; 26 | 27 | /* Type for section indices, which are 16-bit quantities. */ 28 | typedef uint16_t Elf32_Section; 29 | 30 | /* Type for version symbol information. */ 31 | typedef Elf32_Half Elf32_Versym; 32 | 33 | 34 | /* The ELF file header. This appears at the start of every ELF file. */ 35 | 36 | #define EI_NIDENT (16) 37 | 38 | typedef struct 39 | { 40 | unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ 41 | Elf32_Half e_type; /* Object file type */ 42 | Elf32_Half e_machine; /* Architecture */ 43 | Elf32_Word e_version; /* Object file version */ 44 | Elf32_Addr e_entry; /* Entry point virtual address */ 45 | Elf32_Off e_phoff; /* Program header table file offset */ 46 | Elf32_Off e_shoff; /* Section header table file offset */ 47 | Elf32_Word e_flags; /* Processor-specific flags */ 48 | Elf32_Half e_ehsize; /* ELF header size in bytes */ 49 | Elf32_Half e_phentsize; /* Program header table entry size */ 50 | Elf32_Half e_phnum; /* Program header table entry count */ 51 | Elf32_Half e_shentsize; /* Section header table entry size */ 52 | Elf32_Half e_shnum; /* Section header table entry count */ 53 | Elf32_Half e_shstrndx; /* Section header string table index */ 54 | } Elf32_Ehdr; 55 | 56 | /* Conglomeration of the identification bytes, for easy testing as a word. */ 57 | #define ELFMAG "\177ELF" 58 | 59 | typedef struct 60 | { 61 | Elf32_Word p_type; /* Segment type */ 62 | Elf32_Off p_offset; /* Segment file offset */ 63 | Elf32_Addr p_vaddr; /* Segment virtual address */ 64 | Elf32_Addr p_paddr; /* Segment physical address */ 65 | Elf32_Word p_filesz; /* Segment size in file */ 66 | Elf32_Word p_memsz; /* Segment size in memory */ 67 | Elf32_Word p_flags; /* Segment flags */ 68 | Elf32_Word p_align; /* Segment alignment */ 69 | } Elf32_Phdr; 70 | 71 | 72 | /* Legal values for p_type (segment type). */ 73 | 74 | #define PT_NULL 0 /* Program header table entry unused */ 75 | #define PT_LOAD 1 /* Loadable program segment */ 76 | #define PT_DYNAMIC 2 /* Dynamic linking information */ 77 | #define PT_INTERP 3 /* Program interpreter */ 78 | #define PT_NOTE 4 /* Auxiliary information */ 79 | #define PT_SHLIB 5 /* Reserved */ 80 | #define PT_PHDR 6 /* Entry for header table itself */ 81 | #define PT_TLS 7 /* Thread-local storage segment */ 82 | #define PT_NUM 8 /* Number of defined types */ 83 | #define PT_LOOS 0x60000000 /* Start of OS-specific */ 84 | #define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ 85 | #define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ 86 | #define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ 87 | #define PT_LOSUNW 0x6ffffffa 88 | #define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ 89 | #define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ 90 | #define PT_HISUNW 0x6fffffff 91 | #define PT_HIOS 0x6fffffff /* End of OS-specific */ 92 | #define PT_LOPROC 0x70000000 /* Start of processor-specific */ 93 | #define PT_HIPROC 0x7fffffff /* End of processor-specific */ 94 | 95 | bool is_elf(const char* buff); 96 | 97 | Elf32_Addr load_elf(const char* buff); 98 | 99 | #endif -------------------------------------------------------------------------------- /bootloader/string/string.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | 3 | // From https://wiki.osdev.org/Meaty_Skeleton 4 | int memcmp(const void* aptr, const void* bptr, size_t size) { 5 | const unsigned char* a = (const unsigned char*)aptr; 6 | const unsigned char* b = (const unsigned char*)bptr; 7 | for (size_t i = 0; i < size; i++) { 8 | if (a[i] < b[i]) 9 | return -1; 10 | else if (b[i] < a[i]) 11 | return 1; 12 | } 13 | return 0; 14 | } 15 | 16 | // From https://wiki.osdev.org/Meaty_Skeleton 17 | size_t strlen(const char* str) { 18 | size_t len = 0; 19 | while (str[len]) 20 | len++; 21 | return len; 22 | } 23 | -------------------------------------------------------------------------------- /bootloader/string/string.h: -------------------------------------------------------------------------------- 1 | #ifndef STRING_H 2 | #define STRING_H 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | int memcmp(const void* aptr, const void* bptr, size_t size); 10 | size_t strlen(const char* str); 11 | 12 | #endif -------------------------------------------------------------------------------- /bootloader/tar/tar.c: -------------------------------------------------------------------------------- 1 | #include "tar.h" 2 | 3 | // From https://wiki.osdev.org/USTAR 4 | 5 | // Convert octal string to integer 6 | static int oct2bin(unsigned char* str, int size) { 7 | int n = 0; 8 | unsigned char* c = str; 9 | while (size-- > 0) { 10 | n *= 8; 11 | n += *c - '0'; 12 | c++; 13 | } 14 | return n; 15 | } 16 | 17 | 18 | // returns file size and pointer to file data in out 19 | // Assuming the tar ball is loaded into memory entirely 20 | // 21 | // archive: point to the start of tarball meta sector in memory 22 | // filename: the file name to match 23 | // out: will point to a pointer of the matched file in the tarball 24 | // 25 | // return: file size 26 | int tar_lookup(unsigned char* archive, const char* filename, char** out) { 27 | unsigned char* ptr = archive; 28 | 29 | while (!memcmp(ptr + 257, "ustar", 5)) { 30 | int filesize = oct2bin(ptr + 0x7c, 11); 31 | if (!memcmp(ptr, filename, strlen(filename) + 1)) { 32 | *out = (char*)ptr + 512; 33 | return filesize; 34 | } 35 | ptr += (((filesize + 511) / 512) + 1) * 512; 36 | } 37 | return 0; 38 | } 39 | 40 | // Check if archive is pointing to the start of a tarball meta sector 41 | bool is_tar_header(unsigned char* archive) { 42 | return !memcmp(archive + 257, "ustar", 5); 43 | } 44 | 45 | // Check if archive is pointing to the start of a tarball meta sector for file named filename 46 | int tar_match_filename(unsigned char* archive, const char* filename) { 47 | if (is_tar_header(archive)) { 48 | if (!memcmp(archive, filename, strlen(filename) + 1)) { 49 | return 0; 50 | } else { 51 | return TAR_ERR_FILE_NAME_NOT_MATCH; // Filename not match 52 | } 53 | } else { 54 | return TAR_ERR_NOT_USTAR; // Not USTAR file 55 | } 56 | } 57 | 58 | // Get the actual content size of a file in a tarball 59 | // 60 | // archive: pointer to the start of a tarball meta sector 61 | int tar_get_filesize(unsigned char* archive) { 62 | if (is_tar_header(archive)) { 63 | return oct2bin(archive + 0x7c, 11); 64 | } else { 65 | return TAR_ERR_NOT_USTAR; // Not USTAR file 66 | } 67 | } -------------------------------------------------------------------------------- /bootloader/tar/tar.h: -------------------------------------------------------------------------------- 1 | #ifndef TAR_H 2 | #define TAR_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "../string/string.h" 8 | 9 | enum tar_error_code { 10 | TAR_ERR_GENERAL = -1, 11 | TAR_ERR_NOT_USTAR = -2, 12 | TAR_ERR_FILE_NAME_NOT_MATCH = -3, 13 | TAR_ERR_LBA_GT_MAX_SECTOR = -4 14 | }; 15 | 16 | int tar_lookup(unsigned char* archive, const char* filename, char** out); 17 | bool is_tar_header(unsigned char* archive); 18 | int tar_match_filename(unsigned char* archive, const char* filename); 19 | int tar_get_filesize(unsigned char* archive); 20 | 21 | #endif -------------------------------------------------------------------------------- /bootloader/video/vbe.h: -------------------------------------------------------------------------------- 1 | #ifndef _VBE_H 2 | #define _VBE_H 3 | 4 | #include 5 | 6 | // Structure returned by VBE BIOS function 0x4F00 7 | // Number after colon is offset into the struct 8 | typedef struct vbe_info_structure { 9 | char signature[4]; // must be "VESA" to indicate valid VBE support: 0 10 | uint16_t version; // VBE version; high byte is major version, low byte is minor version: 4 11 | uint32_t oem; // segment:offset pointer to OEM: 6 12 | uint32_t capabilities; // bitfield that describes card capabilities: 10 13 | uint32_t video_modes; // segment:offset pointer to list of supported video modes: 14 14 | uint16_t video_memory; // amount of video memory in 64KB blocks: 18 15 | uint16_t software_rev; // software revision: 20 16 | uint32_t vendor; // segment:offset to card vendor string: 22 17 | uint32_t product_name; // segment:offset to card model name: 26 18 | uint32_t product_rev; // segment:offset pointer to product revision: 30 19 | char reserved[222]; // reserved for future expansion: 34 20 | char oem_data[256]; // OEM BIOSes store their strings in this area: 256 21 | } __attribute__ ((packed)) vbe_info_structure; 22 | 23 | // Structure returned by VBE BIOS function 0x4F01 24 | // Number after colon is offset into the struct 25 | typedef struct vbe_mode_info_structure { 26 | uint16_t attributes; // deprecated, only bit 7 should be of interest to you, and it indicates the mode supports a linear frame buffer.: 0 27 | uint8_t window_a; // deprecated: 2 28 | uint8_t window_b; // deprecated: 3 29 | uint16_t granularity; // deprecatedused while calculating bank numbers: 4 30 | uint16_t window_size; // : 6 31 | uint16_t segment_a; // : 8 32 | uint16_t segment_b; // : 10 33 | uint32_t win_func_ptr; // deprecatedused to switch banks from protected mode without returning to real mode: 12 34 | uint16_t pitch; // number of bytes per horizontal line: 16 35 | uint16_t width; // width in pixels: 18 36 | uint16_t height; // height in pixels: 20 37 | uint8_t w_char; // unused...: 22 38 | uint8_t y_char; // ...: 23 39 | uint8_t planes; // : 24 40 | uint8_t bpp; // bits per pixel in this mode: 25 41 | uint8_t banks; // deprecatedtotal number of banks in this mode: 26 42 | uint8_t memory_model; // : 27 43 | uint8_t bank_size; // deprecatedsize of a bank, almost always 64 KB but may be 16 KB...: 28 44 | uint8_t image_pages; // : 29 45 | uint8_t reserved0; // : 30 46 | 47 | uint8_t red_mask; // : 31 48 | uint8_t red_position; // : 32 49 | uint8_t green_mask; // : 33 50 | uint8_t green_position; // : 34 51 | uint8_t blue_mask; // : 35 52 | uint8_t blue_position; // : 36 53 | uint8_t reserved_mask; // : 37 54 | uint8_t reserved_position; // : 38 55 | uint8_t direct_color_attributes; // : 39 56 | 57 | uint32_t framebuffer; // physical address of the linear frame bufferwrite here to draw to the screen: 40 58 | uint32_t off_screen_mem_off; // : 44 59 | uint16_t off_screen_mem_size; // size of memory in the framebuffer but not being displayed on the screen : 48 60 | uint8_t reserved1[206]; // : 50 61 | } __attribute__((packed)) vbe_mode_info_structure; 62 | 63 | 64 | 65 | #endif -------------------------------------------------------------------------------- /bootloader/video/video.c: -------------------------------------------------------------------------------- 1 | #include "video.h" 2 | #include "../string/string.h" 3 | 4 | static int initialized; 5 | 6 | static uint32_t* framebuffer; 7 | static uint8_t bpp; 8 | static uint32_t pitch; 9 | static uint32_t width; 10 | static uint32_t height; 11 | static uint8_t* font; 12 | 13 | #define FONT_HEIGHT 16 14 | #define FONT_WIDTH 8 15 | 16 | void putpixel(uint32_t color, int x, int y) { 17 | framebuffer[x + y*width] = color; 18 | } 19 | 20 | // void putpixel24(uint32_t color, int x, int y) { 21 | // uint32_t where = x + y*pitch_in_pixel; 22 | // framebuffer[where] = color & 0xFF; 23 | // framebuffer[where+1] = (color >> 8) & 0xFF; 24 | // framebuffer[where+2] = (color >> 16) & 0xFF; 25 | // } 26 | 27 | void fillrect(uint32_t color, int x, int y, int w, int h) { 28 | uint32_t* where = &framebuffer[x + y*width]; 29 | int i, j; 30 | 31 | for (i = 0; i < h; i++) { 32 | for (j = 0; j < w; j++) { 33 | *where++ = color; 34 | } 35 | where += width - w; 36 | } 37 | } 38 | 39 | void drawchar(unsigned char c, int x, int y, uint32_t bgcolor, uint32_t fgcolor) 40 | { 41 | int cx,cy; 42 | int mask[8]={128,64,32,16,8,4,2,1}; // should match FONT_WIDTH 43 | unsigned char *glyph=font+(int)c*FONT_HEIGHT; 44 | 45 | for(cy=0;cyflags & MULTIBOOT_INFO_VBE_INFO) && (info->framebuffer_type == MULTIBOOT_FRAMEBUFFER_TYPE_RGB))) { 66 | return -1; 67 | } 68 | 69 | // Load VGA font from boot module 70 | if(!(info->flags & MULTIBOOT_INFO_MODS)) { 71 | // must have VBE info and boot modules 72 | while (1); 73 | } 74 | struct multiboot_mod_list* mods = (struct multiboot_mod_list*) info->mods_addr; 75 | for(uint32_t i=0; imods_count; i++) { 76 | // find boot module of BIOS VGA font 77 | if(memcmp((char*) mods[i].cmdline, "VGA FONT", 9) == 0 && mods[i].mod_end - mods[i].mod_start == 256*FONT_WIDTH*FONT_HEIGHT) { 78 | font = (uint8_t*) mods->mod_start; 79 | break; 80 | } 81 | } 82 | if(font == NULL) { 83 | while (1); 84 | } 85 | 86 | if(info->framebuffer_bpp != 32) { 87 | // hang assert the color depth must be 32 bits 88 | while (1); 89 | } 90 | 91 | framebuffer = (uint32_t*) (uint32_t) info->framebuffer_addr; 92 | bpp = info->framebuffer_bpp; 93 | pitch = info->framebuffer_pitch; 94 | width = info->framebuffer_width; 95 | height = info->framebuffer_height; 96 | 97 | initialized = 1; 98 | 99 | return 0; 100 | } 101 | -------------------------------------------------------------------------------- /bootloader/video/video.h: -------------------------------------------------------------------------------- 1 | #ifndef _VIDEO_H 2 | #define _VIDEO_H 3 | 4 | #include 5 | #include "vbe.h" 6 | #include "../multiboot/multiboot.h" 7 | 8 | // Defined in vesa.asm 9 | extern uint16_t VESA_MODE, VGA_FONT_ADDR; 10 | extern vbe_info_structure VESA_BIOS_INFO[]; 11 | extern vbe_mode_info_structure VESA_MODE_INFO[]; 12 | 13 | void putpixel(uint32_t color, int x, int y); 14 | void fillrect(uint32_t color, int x, int y, int w, int h); 15 | void drawchar(unsigned char c, int x, int y, uint32_t bgcolor, uint32_t fgcolor); 16 | void drawchar_textmode(unsigned char c, int char_x, int char_y, uint32_t bgcolor, uint32_t fgcolor); 17 | void get_textmode_screen_size(uint32_t* w_in_char, uint32_t* h_in_char); 18 | int init_video(multiboot_info_t* info); 19 | 20 | 21 | #endif -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | . ./headers.sh 4 | 5 | if [ -n "$*" ]; then 6 | TO_BUILD="$*" 7 | else 8 | TO_BUILD=$PROJECTS 9 | fi 10 | 11 | for PROJECT in $TO_BUILD; do 12 | (echo "Building $PROJECT" && cd $PROJECT && DESTDIR="$SYSROOT" $MAKE install) 13 | done 14 | -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | . ./config.sh 4 | 5 | for PROJECT in $PROJECTS; do 6 | echo "Cleaning $PROJECT" 7 | (cd $PROJECT && $MAKE clean) 8 | done 9 | 10 | rm -rf sysroot 11 | rm -rf isodir 12 | rm -rf simple_os.iso 13 | rm -f bootable_kernel.bin 14 | -------------------------------------------------------------------------------- /cleanup_tap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Clean up bridge and tap device set up by setup_tap.sh 4 | 5 | BRIDGE=br0 6 | NET_INTERFACE=enp0s3 7 | TAP=tap0 8 | 9 | sudo ip link set $TAP nomaster 10 | sudo ip link set dev $TAP down 11 | sudo ip tuntap del dev $TAP mode tap 12 | 13 | sudo ip addr flush dev $BRIDGE 14 | sudo ip link set $NET_INTERFACE up 15 | sudo ip link set $NET_INTERFACE nomaster 16 | sudo dhclient -v $NET_INTERFACE 17 | 18 | sudo ip link set $BRIDGE down 19 | sudo ip link delete $BRIDGE type bridge 20 | 21 | -------------------------------------------------------------------------------- /config.sh: -------------------------------------------------------------------------------- 1 | SYSTEM_HEADER_PROJECTS="libc kernel" 2 | PROJECTS="libc kernel applications bootloader" 3 | 4 | export MAKE=${MAKE:-make} 5 | export HOST=${HOST:-$(./default-host.sh)} 6 | export TAR=${TAR:-tar} 7 | 8 | # Root of hosted tool chain 9 | export PROJECT_ROOT="$(pwd)" 10 | export TOOL_CHAIN_BUILD_DIR="$(pwd)/build-toolchain" 11 | export TOOL_CHAIN_ROOT="$(pwd)/toolchain" 12 | export CROSSCOMPILERBIN=$TOOL_CHAIN_ROOT/usr/bin 13 | 14 | export AR=${CROSSCOMPILERBIN}/${HOST}-ar 15 | export AS=${AS:-nasm} 16 | export CC=${CROSSCOMPILERBIN}/${HOST}-gcc 17 | 18 | export BOOTDIR=/boot 19 | export PREFIX=/usr 20 | export INCLUDEDIR=$PREFIX/include 21 | export LIBDIR=$PREFIX/lib 22 | 23 | export CFLAGS='-O0 -g' 24 | export ASMFLAGS='-f elf32 -g -F dwarf' 25 | export CPPFLAGS='' 26 | 27 | # Configure the cross-compiler to use the desired system root. 28 | export SYSROOT="$(pwd)/sysroot" 29 | export CC="$CC --sysroot=$SYSROOT" 30 | 31 | # Work around that the -elf gcc targets doesn't have a system include directory 32 | # because it was configured with --without-headers rather than --with-sysroot. 33 | if echo "$HOST" | grep -Eq -- '-elf($|-)'; then 34 | export CC="$CC -isystem=$INCLUDEDIR" 35 | fi 36 | -------------------------------------------------------------------------------- /default-host.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo i686-elf 3 | -------------------------------------------------------------------------------- /headers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | . ./config.sh 4 | 5 | mkdir -p "$SYSROOT" 6 | 7 | for PROJECT in $SYSTEM_HEADER_PROJECTS; do 8 | (echo "Installing headers from $PROJECT" && cd $PROJECT && DESTDIR="$SYSROOT" $MAKE install-headers) 9 | done 10 | -------------------------------------------------------------------------------- /iso.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | . ./build.sh 4 | 5 | mkdir -p isodir 6 | mkdir -p isodir/boot 7 | mkdir -p isodir/boot/grub 8 | 9 | cp sysroot/boot/simple_os.kernel isodir/boot/simple_os.kernel 10 | cat > isodir/boot/grub/grub.cfg << EOF 11 | menuentry "simple_os" { 12 | multiboot /boot/simple_os.kernel 13 | } 14 | EOF 15 | grub-mkrescue -o simple_os.iso isodir 16 | -------------------------------------------------------------------------------- /kernel/Makefile: -------------------------------------------------------------------------------- 1 | DEFAULT_HOST!=../default-host.sh 2 | HOST?=$(DEFAULT_HOST) 3 | HOSTARCH!=../target-triplet-to-arch.sh $(HOST) 4 | 5 | CFLAGS?=-O2 -g 6 | CPPFLAGS?= 7 | LDFLAGS?= 8 | LIBS?= 9 | 10 | DESTDIR?= 11 | BOOTDIR?=/boot 12 | PREFIX?=/usr 13 | INCLUDEDIR?=$(PREFIX)/include 14 | 15 | CFLAGS:=$(CFLAGS) -ffreestanding -Wall -Wextra 16 | CPPFLAGS:=$(CPPFLAGS) -D__is_kernel -Iinclude 17 | LDFLAGS:=$(LDFLAGS) 18 | LIBS:=$(LIBS) -nostdlib -lk -lgcc 19 | 20 | ARCHDIR=arch/$(HOSTARCH) 21 | 22 | include $(ARCHDIR)/make.config 23 | 24 | CFLAGS:=$(CFLAGS) $(KERNEL_ARCH_CFLAGS) 25 | CPPFLAGS:=$(CPPFLAGS) $(KERNEL_ARCH_CPPFLAGS) 26 | LDFLAGS:=$(LDFLAGS) $(KERNEL_ARCH_LDFLAGS) 27 | LIBS:=$(LIBS) $(KERNEL_ARCH_LIBS) 28 | ASMFLAGS:=$(ASMFLAGS) $(KERNEL_ARCH_ASMFLAGS) 29 | 30 | KERNEL_OBJS=\ 31 | $(KERNEL_ARCH_OBJS) \ 32 | panic/panic.o \ 33 | memory_bitmap/memory_bitmap.o \ 34 | heap/heap.o \ 35 | tar/tar.o \ 36 | elf/elf.o \ 37 | block_io/block_io.o \ 38 | vfs/vfs.o \ 39 | fat/fat.o \ 40 | console/console.o \ 41 | kernel/kernel.o \ 42 | network/ethernet.o \ 43 | network/arp.o \ 44 | network/ipv4.o \ 45 | network/icmp.o \ 46 | network/network.o \ 47 | pipe/pipe.o \ 48 | video/video.o \ 49 | lock/lock.o \ 50 | socket/socket.o \ 51 | 52 | 53 | OBJS=\ 54 | $(ARCHDIR)/crt/crti.o \ 55 | $(ARCHDIR)/crt/crtbegin.o \ 56 | $(KERNEL_OBJS) \ 57 | $(ARCHDIR)/crt/crtend.o \ 58 | $(ARCHDIR)/crt/crtn.o \ 59 | 60 | LINK_LIST=\ 61 | $(LDFLAGS) \ 62 | $(ARCHDIR)/crt/crti.o \ 63 | $(ARCHDIR)/crt/crtbegin.o \ 64 | $(KERNEL_OBJS) \ 65 | $(LIBS) \ 66 | $(ARCHDIR)/crt/crtend.o \ 67 | $(ARCHDIR)/crt/crtn.o \ 68 | 69 | .PHONY: all clean install install-headers install-kernel 70 | .SUFFIXES: .o .c .S 71 | 72 | all: simple_os.kernel 73 | 74 | simple_os.kernel: $(OBJS) $(ARCHDIR)/linker.ld 75 | $(CC) -T $(ARCHDIR)/linker.ld -o $@ $(CFLAGS) $(LINK_LIST) 76 | # grub-file --is-x86-multiboot simple_os.kernel 77 | 78 | $(ARCHDIR)/crt/crtbegin.o $(ARCHDIR)/crt/crtend.o: 79 | OBJ=`$(CC) $(CFLAGS) $(LDFLAGS) -print-file-name=$(@F)` && cp "$$OBJ" $@ 80 | 81 | .c.o: 82 | $(CC) -MD -MP -c $< -o $@ -std=gnu11 $(CFLAGS) $(CPPFLAGS) 83 | 84 | .S.o: 85 | $(CC) -MD -MP -c $< -o $@ $(CFLAGS) $(CPPFLAGS) 86 | 87 | %.o: %.asm 88 | $(AS) $(ASMFLAGS) $< -o $@ 89 | 90 | clean: 91 | rm -f simple_os.kernel 92 | rm -f init/init 93 | rm -f $(OBJS) *.o */*.o */*/*.o 94 | rm -f $(OBJS:.o=.d) *.d */*.d */*/*.d 95 | 96 | install: install-headers install-kernel install-reserved-path 97 | 98 | install-headers: 99 | mkdir -p $(DESTDIR)$(INCLUDEDIR) 100 | cp -Rp include/. $(DESTDIR)$(INCLUDEDIR)/. 101 | mkdir -p $(TOOL_CHAIN_ROOT)$(INCLUDEDIR) 102 | cp -Rp include/. $(TOOL_CHAIN_ROOT)$(INCLUDEDIR)/. 103 | 104 | install-kernel: simple_os.kernel 105 | mkdir -p $(DESTDIR)$(BOOTDIR) 106 | cp -p simple_os.kernel $(DESTDIR)$(BOOTDIR) 107 | 108 | install-reserved-path: 109 | mkdir -p $(DESTDIR)/home 110 | mkdir -p $(DESTDIR)/tmp 111 | touch $(DESTDIR)/console 112 | touch $(DESTDIR)/pipe 113 | 114 | -include $(OBJS:.o=.d) 115 | -------------------------------------------------------------------------------- /kernel/arch/i386/arch_init/arch_init.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | 15 | // x86-32 architecture specific initialization sequence 16 | void initialize_architecture(uint32_t mbt_physical_addr) { 17 | 18 | // Initialize serial port I/O so we can print debug message out 19 | init_serial(); 20 | 21 | // Initialize the global CPU state 22 | init_cpu(); 23 | 24 | // Initialize memory bitmap for the physical memory manager (frame allocator) 25 | initialize_bitmap(mbt_physical_addr); 26 | 27 | // Initialize page frame allocator, install page fault handler, init GDT and map certain pages indicated by the multiboot struct 28 | initialize_paging(); 29 | 30 | // Initialize VESA/VGA video driver 31 | init_video(mbt_physical_addr); 32 | 33 | // Initialize terminal cursor and global variables like default color 34 | terminal_initialize(mbt_physical_addr); 35 | 36 | // Initialize IDT(Interrupt Descriptor Table) with ISR(Interrupt Service Routines) for Interrupts/IRQs 37 | // Including remapping the IRQs 38 | isr_install(); 39 | 40 | // Initialize a heap for kmalloc and kfree 41 | initialize_kernel_heap(); 42 | 43 | // Enumerate and initialize PCI devices 44 | init_pci(); 45 | 46 | // Set up system timer using PIT(Programmable Interval Timer) 47 | // Set freq = 50 (i.e. 50 tick per seconds) 48 | // and set tick_between_process_switch to 10, basically switch process every 0.2 second 49 | init_timer(50, 10); 50 | 51 | // initialize keyboard interrupt handler 52 | init_keyboard(); 53 | 54 | // Enable interruptions (it was disabled by the bootloader) 55 | // Commenting out, because here we not yet ready to do process/context switching based on PIT interrupt 56 | // We will enable interrupt when entering user mode 57 | // asm volatile("sti"); 58 | 59 | } 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /kernel/arch/i386/boot/gdt.asm: -------------------------------------------------------------------------------- 1 | ; global gdt_descriptor, CODE_SEG, DATA_SEG 2 | ; Based on https://github.com/cfenollosa/os-tutorial/tree/master/09-32bit-gdt 3 | 4 | push KERNEL_CODE_SEG 5 | push KERNEL_DATA_SEG 6 | 7 | ; Using Flat Setup in GDT, basically leaving the offsets used in addressing untranslated 8 | gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps 9 | ; the GDT starts with a null 8-byte 10 | dd 0x0 ; 4 byte 11 | dd 0x0 ; 4 byte 12 | 13 | ; GDT for code segment. base = 0x00000000, length = 0xfffff 14 | ; for flags, refer to os-dev.pdf document, page 36 15 | ; Ref: https://wiki.osdev.org/Global_Descriptor_Table 16 | gdt_code: 17 | dw 0xffff ; segment length, bits 0-15 18 | dw 0x0 ; segment base, bits 0-15 19 | db 0x0 ; segment base, bits 16-23 20 | db 1001_1010b ; flags (8 bits) 21 | db 1100_1111b ; flags (4 bits) + segment length, bits 16-19 22 | db 0x0 ; segment base, bits 24-31 23 | 24 | ; GDT for data segment. base and length identical to code segment 25 | ; some flags changed, again, refer to os-dev.pdf 26 | gdt_data: 27 | dw 0xffff 28 | dw 0x0 29 | db 0x0 30 | db 1001_0010b 31 | db 1100_1111b 32 | db 0x0 33 | 34 | gdt_end: 35 | 36 | 37 | ; GDT descriptor to be loaded by lgdt instruction 38 | gdt_descriptor: 39 | dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size 40 | dd gdt_start ; address (32 bit) 41 | 42 | 43 | ; define some constants for later use 44 | ; these are segment selector used in protected mode 45 | ; Ref: https://wiki.osdev.org/Selector 46 | ; When Requested Privilege Level is 0 and we are using GDT, they are the same as the byte offset into the GDT table 47 | ; Since the GDT entry has a length of 8 bytes and and first two bits of selector is RPL, the third is to choose GDT (0b) or LDT (1b) 48 | ; and the remaining of the 16bits selector is the 0-based index into the GDT 49 | 50 | 51 | KERNEL_CODE_SEG equ 0000000000001_0_00b ; gdt_code - gdt_start is also correct 52 | KERNEL_DATA_SEG equ 0000000000010_0_00b ; gdt_data - gdt_start is also correct 53 | -------------------------------------------------------------------------------- /kernel/arch/i386/cpu/cpuid.asm: -------------------------------------------------------------------------------- 1 | global check_cpuid 2 | 3 | section .text 4 | check_cpuid: 5 | pushfd ;Save EFLAGS 6 | pushfd ;Store EFLAGS 7 | xor dword [esp],0x00200000 ;Invert the ID bit in stored EFLAGS 8 | popfd ;Load stored EFLAGS (with ID bit inverted) 9 | pushfd ;Store EFLAGS again (ID bit may or may not be inverted) 10 | pop eax ;eax = modified EFLAGS (ID bit may or may not be inverted) 11 | xor eax,[esp] ;eax = whichever bits were changed 12 | popfd ;Restore original EFLAGS 13 | and eax,0x00200000 ;eax = zero if ID bit can't be changed, else non-zero 14 | ret 15 | -------------------------------------------------------------------------------- /kernel/arch/i386/crt/crti.S: -------------------------------------------------------------------------------- 1 | .section .init 2 | .global _init 3 | .type _init, @function 4 | _init: 5 | push %ebp 6 | movl %esp, %ebp 7 | /* gcc will nicely put the contents of crtbegin.o's .init section here. */ 8 | 9 | .section .fini 10 | .global _fini 11 | .type _fini, @function 12 | _fini: 13 | push %ebp 14 | movl %esp, %ebp 15 | /* gcc will nicely put the contents of crtbegin.o's .fini section here. */ 16 | -------------------------------------------------------------------------------- /kernel/arch/i386/crt/crtn.S: -------------------------------------------------------------------------------- 1 | .section .init 2 | /* gcc will nicely put the contents of crtend.o's .init section here. */ 3 | popl %ebp 4 | ret 5 | 6 | .section .fini 7 | /* gcc will nicely put the contents of crtend.o's .fini section here. */ 8 | popl %ebp 9 | ret 10 | -------------------------------------------------------------------------------- /kernel/arch/i386/idt/idt.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Modified from https://github.com/cfenollosa/os-tutorial/tree/master/18-interrupts 4 | 5 | idt_gate_t idt[IDT_ENTRIES]; 6 | idt_register_t idt_reg; 7 | 8 | void set_idt_gate(int n, uint32_t handler_address, uint8_t type, uint8_t dpl) { 9 | // For more details, see https://wiki.osdev.org/IDT 10 | idt[n].low_offset = handler_address & 0x0000FFFF; 11 | idt[n].sel = KERNEL_CS; 12 | idt[n].always0 = 0; 13 | idt[n].type = type; 14 | idt[n].s = 0; 15 | idt[n].dpl = dpl; 16 | idt[n].p = 1; 17 | idt[n].high_offset = (handler_address >> 16) & 0x0000FFFF; 18 | } 19 | 20 | void set_idt() { 21 | idt_reg.base = (uint32_t)&idt; 22 | idt_reg.limit = IDT_ENTRIES * sizeof(idt_gate_t) - 1; 23 | /* Don't make the mistake of loading &idt -- always load &idt_reg */ 24 | // https://gcc.gnu.org/onlinedocs/gcc/Simple-Constraints.html#Simple-Constraints 25 | // "r" means "A register operand is allowed provided that it is in a general register." 26 | asm volatile("lidtl (%0)" : : "r" (&idt_reg)); 27 | } -------------------------------------------------------------------------------- /kernel/arch/i386/linker.ld: -------------------------------------------------------------------------------- 1 | /* The bootloader will look at this image and start execution at the symbol 2 | designated at the entry point. */ 3 | ENTRY(_start) 4 | 5 | /* Tell where the various sections of the object files will be put in the final 6 | kernel image. */ 7 | SECTIONS 8 | { 9 | /* these labels get exported to the code files */ 10 | MAP_MEM_PA_ZERO_TO = 0xC0000000; /* This must be in sync with boot.asm */ 11 | KERNEL_PHYSICAL_START = 0x100000; 12 | KERNEL_VIRTUAL_START = MAP_MEM_PA_ZERO_TO + KERNEL_PHYSICAL_START; 13 | 14 | /* Begin putting sections at 1 MiB, a conventional place for kernels to be 15 | loaded at by the bootloader. */ 16 | . = KERNEL_PHYSICAL_START; 17 | 18 | /* First put the multiboot header, as it is required to be put very early 19 | early in the image or the bootloader won't recognize the file format. */ 20 | .multiboot : { 21 | *(.multiboot) 22 | } 23 | 24 | /* .start_init will be executed right after entering the user space 25 | Below we will tell the bootloader (through EFI headers) to place code of .start_init 26 | at the physical memory address START_INIT_PHY_BEGIN by the "AT" instruction */ 27 | START_INIT_PHY_BEGIN = .; 28 | /* VIRTUAL: After the higher half kernel memory mapping, the kernel can read start_init code at this virtual address */ 29 | START_INIT_VIRTUAL_BEGIN = START_INIT_PHY_BEGIN + MAP_MEM_PA_ZERO_TO; 30 | /* RELOC: virtual address where the start_init code shall be copied to before executing 31 | Instruct the compiler to generate the start_init code as if it will be loaded at START_INIT_RELOC_BEGIN 32 | i.e. the page (0x1000 = size of one page) right below the kernel space (area >= MAP_MEM_PA_ZERO_TO) 33 | */ 34 | START_INIT_RELOC_BEGIN = MAP_MEM_PA_ZERO_TO - 0x1000; 35 | . = START_INIT_RELOC_BEGIN; 36 | .start_init ALIGN (4K) : AT (START_INIT_PHY_BEGIN) { 37 | *(.start_init) 38 | } 39 | STRAT_INIT_RELOC_END = .; 40 | START_INIT_SIZE = STRAT_INIT_RELOC_END - START_INIT_RELOC_BEGIN; 41 | . = START_INIT_PHY_BEGIN + START_INIT_SIZE; 42 | 43 | /* The kernel will live at 3GB + 1MB in the virtual address space, */ 44 | /* which will be mapped to 1MB in the physical address space. */ 45 | /* "." is used to determine the absolute addresses in the generated binary file 46 | they will be generated based on the offset plus the value of . 47 | */ 48 | /* Adding 0xC0000000 to pretend the whole program was loaded at 0xC0000000 49 | (use += instead of than = to take the size of multiboot into consideration */ 50 | . += MAP_MEM_PA_ZERO_TO; 51 | 52 | /* Next we'll put the .text section. */ 53 | /* The section is aligned in 4KiB boundary */ 54 | /* AT(addr) means to actually ask the ELF loader to load this section at addr */ 55 | /* In combination of ". += 0xC0000000", we generated the binary with all address based on 0xC0000000 56 | but actually ask the bootloader to load the sections starting from 0x100000 (1MiB) */ 57 | .text ALIGN (4K) : AT (ADDR (.text) - MAP_MEM_PA_ZERO_TO) 58 | { 59 | *(.text) 60 | } 61 | 62 | /* Read-only data. */ 63 | .rodata ALIGN (4K) : AT (ADDR (.rodata) - MAP_MEM_PA_ZERO_TO) 64 | { 65 | *(.rodata) 66 | } 67 | 68 | /* Read-write data (initialized) */ 69 | .data ALIGN (4K) : AT (ADDR (.data) - MAP_MEM_PA_ZERO_TO) 70 | { 71 | *(.data) 72 | } 73 | 74 | /* Read-write data (uninitialized) and stack */ 75 | .bss ALIGN (4K) : AT (ADDR (.bss) - MAP_MEM_PA_ZERO_TO) 76 | { 77 | *(COMMON) 78 | *(.bss) 79 | } 80 | 81 | KERNEL_VIRTUAL_END = .; 82 | KERNEL_PHYSICAL_END = KERNEL_VIRTUAL_END - MAP_MEM_PA_ZERO_TO; 83 | 84 | /* The compiler may produce other sections, put them in the proper place in 85 | in this file, if you'd like to include them in the final kernel. */ 86 | } 87 | -------------------------------------------------------------------------------- /kernel/arch/i386/make.config: -------------------------------------------------------------------------------- 1 | KERNEL_ARCH_CFLAGS= 2 | KERNEL_ARCH_CPPFLAGS= 3 | KERNEL_ARCH_LDFLAGS= 4 | KERNEL_ARCH_LIBS= 5 | 6 | KERNEL_ARCH_ASMFLAGS=\ 7 | -i $(ARCHDIR)/boot 8 | 9 | KERNEL_ARCH_OBJS=\ 10 | $(ARCHDIR)/boot/boot.o \ 11 | $(ARCHDIR)/tty/tty.o \ 12 | $(ARCHDIR)/pic/pic.o \ 13 | $(ARCHDIR)/idt/idt.o \ 14 | $(ARCHDIR)/syscall/syscall.o \ 15 | $(ARCHDIR)/isr/interrupt.o \ 16 | $(ARCHDIR)/isr/isr.o \ 17 | $(ARCHDIR)/timer/timer.o \ 18 | $(ARCHDIR)/keyboard/keyboard.o \ 19 | $(ARCHDIR)/paging/paging.o \ 20 | $(ARCHDIR)/arch_init/arch_init.o \ 21 | $(ARCHDIR)/serial/serial.o \ 22 | $(ARCHDIR)/ata/ata.o \ 23 | $(ARCHDIR)/process/process.o \ 24 | $(ARCHDIR)/process/start_init.o \ 25 | $(ARCHDIR)/process/switch_kernel_context.o \ 26 | $(ARCHDIR)/time/time.o \ 27 | $(ARCHDIR)/cpu/cpu.o \ 28 | $(ARCHDIR)/cpu/cpuid.o \ 29 | $(ARCHDIR)/pci/pci.o \ 30 | $(ARCHDIR)/rtl8139/rtl8139.o \ 31 | -------------------------------------------------------------------------------- /kernel/arch/i386/process/start_init.asm: -------------------------------------------------------------------------------- 1 | ; Code to run right after entering user space 2 | ; Do a system call to EXEC /init 3 | 4 | SYS_EXEC equ 1 5 | INT_SYSCALL equ 88 6 | 7 | section .start_init ; special linkage treatment, see linker.ld 8 | start_init_begin: 9 | push envp 10 | push argv 11 | push init_prog_path 12 | push 0 ; fake return PC 13 | mov eax, SYS_EXEC 14 | int INT_SYSCALL 15 | 16 | exit: ; EXEC shall not return, but if so 17 | jmp $ ; enter infinite loop, hang 18 | 19 | 20 | align 4 21 | init_prog_path: db "/usr/bin/init.elf", 0 22 | 23 | argv: 24 | dd init_prog_path 25 | dd 0 26 | 27 | envp: 28 | dd 0 29 | 30 | start_init_end: 31 | 32 | -------------------------------------------------------------------------------- /kernel/arch/i386/process/switch_kernel_context.asm: -------------------------------------------------------------------------------- 1 | ; Source: xv6/swtch.S 2 | ; Context switch between kernel routine/stacks 3 | ; 4 | ; void switch_kernel_context(struct context **old_context, struct context *new_context); 5 | ; 6 | ; Save the current registers on the stack, creating 7 | ; a struct context, and save its address in *old. 8 | ; Switch stacks to new and pop previously-saved registers. 9 | 10 | global switch_kernel_context 11 | switch_kernel_context: 12 | 13 | mov eax, [esp + 4]; struct context **old_context 14 | mov edx, [esp + 8]; struct context *new_context 15 | 16 | ; Save old callee-saved registers 17 | push ebp 18 | push ebx 19 | push esi 20 | push edi 21 | 22 | ; Switch stacks 23 | mov [eax], esp; *old_context = esp, save pointer to old stack to *old 24 | mov esp, edx; esp = new_context, switch to new stack 25 | 26 | ; Load new callee-saved registers 27 | pop edi 28 | pop esi 29 | pop ebx 30 | pop ebp 31 | ret ; pop eip and jmp 32 | -------------------------------------------------------------------------------- /kernel/arch/i386/serial/serial.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // Serial port I/O utilities 6 | // Can be used to export screen content (history) that was scrolled up 7 | // Source: https://wiki.osdev.org/Serial_ports 8 | 9 | #define PORT 0x3f8 // COM1 10 | 11 | static bool serial_port_initialized = false; 12 | 13 | void init_serial() { 14 | outb(PORT + 1, 0x00); // Disable all interrupts 15 | outb(PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) 16 | outb(PORT + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud 17 | outb(PORT + 1, 0x00); // (hi byte) 18 | outb(PORT + 3, 0x03); // 8 bits, no parity, one stop bit 19 | outb(PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold 20 | outb(PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set 21 | outb(PORT + 4, 0x1E); // Set in loopback mode, test the serial chip 22 | outb(PORT + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte) 23 | 24 | // Check if serial is faulty (i.e: not same byte as sent) 25 | if (inb(PORT + 0) != 0xAE) { 26 | // Kernel panic, but we don't have I/O device to print error mesage, so just hang 27 | while(1); 28 | } 29 | 30 | // If serial is not faulty set it in normal operation mode 31 | // (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled) 32 | outb(PORT + 4, 0x0F); 33 | 34 | serial_port_initialized = true; 35 | 36 | } 37 | 38 | bool is_serial_port_initialized() { 39 | return serial_port_initialized; 40 | } 41 | 42 | int serial_received() { 43 | return inb(PORT + 5) & 1; 44 | } 45 | 46 | char read_serial() { 47 | while (serial_received() == 0); 48 | 49 | return inb(PORT); 50 | } 51 | 52 | int is_transmit_empty() { 53 | return inb(PORT + 5) & 0x20; 54 | } 55 | 56 | void write_serial(char a) { 57 | while (is_transmit_empty() == 0); 58 | 59 | outb(PORT, a); 60 | } -------------------------------------------------------------------------------- /kernel/arch/i386/timer/timer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // Ref: http://www.jamesmolloy.co.uk/tutorial_html/5.-IRQs%20and%20the%20PIT.html 12 | // Ref: https://github.com/cfenollosa/os-tutorial/blob/master/23-fixes/cpu/timer.c 13 | 14 | 15 | static uint32_t tick_between_call_to_scheduler = 0; 16 | static uint32_t timer_freq = 0; 17 | static uint64_t tick = 0; 18 | static time_t init_ts; 19 | 20 | static void timer_callback(trapframe *regs) { 21 | UNUSED_ARG(regs); 22 | 23 | tick++; 24 | 25 | if(tick_between_call_to_scheduler > 0 && tick % tick_between_call_to_scheduler == 0) { 26 | yield(); 27 | } 28 | 29 | // Calibrate tick at second precision 30 | if(tick % (timer_freq) == 0) { 31 | if(!init_ts) { 32 | date_time init_dt = current_datetime(); 33 | init_ts = datetime2epoch(&init_dt); 34 | } else { 35 | date_time dt = current_datetime(); 36 | time_t ts = datetime2epoch(&dt); 37 | int64_t sec_elapsed = ts - init_ts; 38 | uint64_t tick_est = sec_elapsed * timer_freq; 39 | if(tick_est > tick) { 40 | printf("Timer: Calibrating tick, %llu => %llu\n", tick, tick_est); 41 | tick = tick_est; 42 | } 43 | } 44 | } 45 | } 46 | 47 | 48 | /* 49 | Programmable Interval Timer Spec (https://wiki.osdev.org/PIT) 50 | 51 | I/O port Usage 52 | 0x40 Channel 0 data port (read/write) 53 | 0x41 Channel 1 data port (read/write) 54 | 0x42 Channel 2 data port (read/write) 55 | 0x43 Mode/Command register (write only, a read is ignored) 56 | 57 | The Mode/Command register at I/O address 0x43 contains the following: 58 | Bits Usage 59 | 6 and 7 Select channel : 60 | 0 0 = Channel 0 61 | 0 1 = Channel 1 62 | 1 0 = Channel 2 63 | 1 1 = Read-back command (8254 only) 64 | 4 and 5 Access mode : 65 | 0 0 = Latch count value command 66 | 0 1 = Access mode: lobyte only 67 | 1 0 = Access mode: hibyte only 68 | 1 1 = Access mode: lobyte/hibyte 69 | 1 to 3 Operating mode : 70 | 0 0 0 = Mode 0 (interrupt on terminal count) 71 | 0 0 1 = Mode 1 (hardware re-triggerable one-shot) 72 | 0 1 0 = Mode 2 (rate generator) 73 | 0 1 1 = Mode 3 (square wave generator) 74 | 1 0 0 = Mode 4 (software triggered strobe) 75 | 1 0 1 = Mode 5 (hardware triggered strobe) 76 | 1 1 0 = Mode 2 (rate generator, same as 010b) 77 | 1 1 1 = Mode 3 (square wave generator, same as 011b) 78 | 0 BCD/Binary mode: 0 = 16-bit binary, 1 = four-digit BCD 79 | 80 | */ 81 | 82 | // Initialize timer (IRQ0 from Programmable Interval Timer) 83 | void init_timer(uint32_t freq, uint32_t tick_between_process_switch) { 84 | /* Install the PIC interrupt handler */ 85 | register_interrupt_handler(IRQ_TO_INTERRUPT(0), timer_callback); 86 | 87 | // The value we send to the PIT is the value to divide it's input clock 88 | // (1193180 Hz) by, to get our required frequency. Important to note is 89 | // that the divisor must be small enough to fit into 16-bits. 90 | uint32_t divisor = 1193180 / freq; 91 | uint8_t low = (uint8_t)(divisor & 0x00FF); 92 | uint8_t high = (uint8_t)( (divisor >> 8) & 0x00FF); 93 | 94 | /* Send the command */ 95 | outb(0x43, 0b00110110); /* Command port */ 96 | outb(0x40, low); 97 | outb(0x40, high); 98 | 99 | timer_freq = freq; 100 | tick_between_call_to_scheduler = tick_between_process_switch; 101 | } 102 | -------------------------------------------------------------------------------- /kernel/block_io/block_io.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define MAX_STORAGE_DEV_COUNT 8 9 | 10 | typedef struct ata_storage_info { 11 | bool is_slave; 12 | } ata_storage_info; 13 | 14 | static struct { 15 | uint32_t next_block_dev_id; 16 | block_storage storage_list[MAX_STORAGE_DEV_COUNT]; 17 | yield_lock lk; 18 | } blk; 19 | 20 | static int64_t read_blocks_ata(block_storage* storage, void* buff, uint32_t LBA, uint32_t block_count) 21 | { 22 | ata_storage_info* info = (ata_storage_info*) storage->internal_info; 23 | PANIC_ASSERT(storage->block_size == 512); 24 | if(LBA >= storage->block_count || LBA + block_count >= storage->block_count) { 25 | return -1; 26 | } 27 | read_sectors_ATA_PIO(info->is_slave, buff, LBA, block_count); 28 | return 512 * block_count; 29 | } 30 | 31 | static int64_t write_blocks_ata(block_storage* storage, uint32_t LBA, uint32_t block_count, const void* buff) 32 | { 33 | ata_storage_info* info = (ata_storage_info*) storage->internal_info; 34 | PANIC_ASSERT(storage->block_size == 512); 35 | if(LBA >= storage->block_count || LBA + block_count >= storage->block_count) { 36 | return -1; 37 | } 38 | write_sectors_ATA_PIO(info->is_slave, buff, LBA, block_count); 39 | return 512 * block_count; 40 | } 41 | 42 | static void add_block_storage(block_storage* storage) 43 | { 44 | acquire(&blk.lk); 45 | storage->device_id = blk.next_block_dev_id++; 46 | for(uint32_t i=0; i 0); 75 | 76 | ata_storage_info* master_info = kmalloc(sizeof(ata_storage_info)); 77 | master_info->is_slave = false; 78 | block_storage master_storage = (block_storage) { 79 | .type=BLK_STORAGE_TYP_ATA_HARD_DRIVE, 80 | .block_size=512, 81 | .block_count=total_block_count, 82 | .read_blocks=read_blocks_ata, 83 | .write_blocks=write_blocks_ata, 84 | .internal_info=master_info 85 | }; 86 | add_block_storage(&master_storage); 87 | assert(master_storage.device_id == IDE_MASTER_DRIVE); 88 | 89 | int32_t slave_total_block_count = get_total_28bit_sectors(true); 90 | if(slave_total_block_count > 0) { 91 | // if slave IDE drive exist 92 | ata_storage_info* slave_info = kmalloc(sizeof(ata_storage_info)); 93 | slave_info->is_slave = true; 94 | block_storage slave_storage = (block_storage) { 95 | .type=BLK_STORAGE_TYP_ATA_HARD_DRIVE, 96 | .block_size=512, 97 | .block_count=slave_total_block_count, 98 | .read_blocks=read_blocks_ata, 99 | .write_blocks=write_blocks_ata, 100 | .internal_info=slave_info 101 | }; 102 | add_block_storage(&slave_storage); 103 | assert(slave_storage.device_id == IDE_SLAVE_DRIVE); 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /kernel/elf/elf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | // Ref: http://www.skyfree.org/linux/references/ELF_Format.pdf 7 | 8 | // Test if the buff is a ELF image by checking the magic number 9 | bool is_elf(const char* buff) { 10 | return (buff[0] == ELFMAG[0]) && (buff[1] == ELFMAG[1]) && (buff[2] == ELFMAG[2]) && (buff[3] == ELFMAG[3]); 11 | } 12 | 13 | // Load a ELF executable to memory according to ELF meta information 14 | // 15 | // buff: pointer to the start of the ELF file buffer 16 | // 17 | // return: the virtual address of the program entry point 18 | // vaddr_ub: returning the virtual address upper bound used by the loaded program 19 | Elf32_Addr load_elf(pde* page_dir, const char* buff, uint32_t* vaddr_ub) { 20 | const Elf32_Ehdr* header = (const Elf32_Ehdr*)buff; 21 | Elf32_Half n_program_header = header->e_phnum; 22 | const Elf32_Phdr* program_header_table = (const Elf32_Phdr*)(((uint32_t)buff) + header->e_phoff); 23 | Elf32_Addr entry_pioint = header->e_entry; 24 | *vaddr_ub = 0; 25 | for (Elf32_Half i = 0; i < n_program_header; i++) { 26 | Elf32_Phdr program_header = program_header_table[i]; 27 | if (program_header.p_type == PT_LOAD) { 28 | char* src_ptr = (char*)buff + program_header.p_offset; 29 | char* dest_ptr = (char*) link_pages(page_dir, program_header.p_vaddr, program_header.p_memsz, curr_page_dir(), true, (program_header.p_flags & PF_W)==PF_W, true); 30 | memset(dest_ptr, 0, program_header.p_memsz); 31 | memmove(dest_ptr, src_ptr, program_header.p_filesz); 32 | unmap_pages(curr_page_dir(), (uint32_t)dest_ptr, program_header.p_memsz); 33 | 34 | // calculate upper bound of the mapped user space memory 35 | // uint32_t mapped_vaddr_ub = (program_header.p_vaddr + program_header.p_memsz + (PAGE_SIZE - 1))/PAGE_SIZE*PAGE_SIZE; 36 | uint32_t mapped_vaddr_ub = program_header.p_vaddr + program_header.p_memsz - 1; 37 | if(mapped_vaddr_ub > *vaddr_ub) { 38 | *vaddr_ub = mapped_vaddr_ub; 39 | } 40 | } 41 | } 42 | return entry_pioint; 43 | } -------------------------------------------------------------------------------- /kernel/include/arch/i386/kernel/cpu.h: -------------------------------------------------------------------------------- 1 | #ifndef _ARCH_I386_KERNEL_CPU_H 2 | #define _ARCH_I386_KERNEL_CPU_H 3 | 4 | #include 5 | #include 6 | 7 | // Source: xv6/proc.c 8 | 9 | // Per-CPU state 10 | typedef struct cpu { 11 | struct context *scheduler_context; // scheduler context, switch_kernel_context() here to enter scheduler 12 | struct task_state ts; // Used by x86 to find stack for interrupt 13 | struct segdesc gdt[NSEGS]; // x86 global descriptor table 14 | int cli_count; // Depth of pushcli nesting. 15 | int orig_if_flag; // Were interrupts enabled before pushcli? 16 | proc* current_process; // The process running on this cpu or null 17 | } cpu; 18 | 19 | cpu* curr_cpu(); 20 | uint read_cpu_eflags(); 21 | uint64_t rdtsc(); 22 | 23 | #endif -------------------------------------------------------------------------------- /kernel/include/arch/i386/kernel/idt.h: -------------------------------------------------------------------------------- 1 | #ifndef _ARCH_I386_KERNEL_IDT_H 2 | #define _ARCH_I386_KERNEL_IDT_H 3 | 4 | #include 5 | 6 | // Modified from https://github.com/cfenollosa/os-tutorial/tree/master/18-interrupts 7 | 8 | /* Segment selectors */ 9 | #define KERNEL_CS 0x08 10 | 11 | /* How every interrupt gate (handler) is defined */ 12 | typedef struct { 13 | uint16_t low_offset; /* Lower 16 bits of handler function address */ 14 | uint16_t sel; /* Kernel segment selector */ 15 | uint8_t always0; 16 | uint8_t type : 4; // 1110b = 0xE = "32 bit interrupt gate", 1111b = 0xF = "32 bit trap gate" 17 | uint8_t s : 1; // Storage Segment, set to 0 for interrupt and trap gates 18 | uint8_t dpl : 2; // (Descriptor Privilege Level) Gate call protection. Specifies which privilege Level the calling Descriptor minimum should have. 19 | uint8_t p : 1; // Present 20 | uint16_t high_offset; /* Higher 16 bits of handler function address */ 21 | } __attribute__((packed)) idt_gate_t; 22 | 23 | #define IDT_GATE_TYPE_INT (0xE) 24 | #define IDT_GATE_TYPE_TRAP (0xF) 25 | 26 | /* A pointer to the array of interrupt handlers. 27 | * Assembly instruction 'lidt' will read it */ 28 | typedef struct { 29 | uint16_t limit; 30 | uint32_t base; 31 | } __attribute__((packed)) idt_register_t; 32 | 33 | #define IDT_ENTRIES 256 34 | 35 | /* Functions implemented in idt.c */ 36 | void set_idt_gate(int n, uint32_t handler_address, uint8_t type, uint8_t dpl); 37 | void set_idt(); 38 | 39 | #endif -------------------------------------------------------------------------------- /kernel/include/arch/i386/kernel/isr.h: -------------------------------------------------------------------------------- 1 | #ifndef _ARCH_I386_KERNEL_ISR_H 2 | #define _ARCH_I386_KERNEL_ISR_H 3 | 4 | #include 5 | 6 | // Modified from https://github.com/cfenollosa/os-tutorial/blob/master/23-fixes/cpu/isr.h 7 | // Ref: https://github.com/cfenollosa/os-tutorial/blob/master/23-fixes 8 | // Ref: https://wiki.osdev.org/James_Molloy%27s_Tutorial_Known_Bugs 9 | 10 | /* ISRs reserved for CPU exceptions */ 11 | // Actual implementations are in isr.asm 12 | extern void isr0(); 13 | extern void isr1(); 14 | extern void isr2(); 15 | extern void isr3(); 16 | extern void isr4(); 17 | extern void isr5(); 18 | extern void isr6(); 19 | extern void isr7(); 20 | extern void isr8(); 21 | extern void isr9(); 22 | extern void isr10(); 23 | extern void isr11(); 24 | extern void isr12(); 25 | extern void isr13(); 26 | extern void isr14(); 27 | extern void isr15(); 28 | extern void isr16(); 29 | extern void isr17(); 30 | extern void isr18(); 31 | extern void isr19(); 32 | extern void isr20(); 33 | extern void isr21(); 34 | extern void isr22(); 35 | extern void isr23(); 36 | extern void isr24(); 37 | extern void isr25(); 38 | extern void isr26(); 39 | extern void isr27(); 40 | extern void isr28(); 41 | extern void isr29(); 42 | extern void isr30(); 43 | extern void isr31(); 44 | 45 | /* ISR for IRQs */ 46 | extern void irq0(); 47 | extern void irq1(); 48 | extern void irq2(); 49 | extern void irq3(); 50 | extern void irq4(); 51 | extern void irq5(); 52 | extern void irq6(); 53 | extern void irq7(); 54 | extern void irq8(); 55 | extern void irq9(); 56 | extern void irq10(); 57 | extern void irq11(); 58 | extern void irq12(); 59 | extern void irq13(); 60 | extern void irq14(); 61 | extern void irq15(); 62 | 63 | // First 32 interrupts are occupied by CPU exceptions 64 | #define N_CPU_EXCEPTION_INT 32 65 | 66 | // Map IRQ{i} to Interrupt{IRQ_BASE_REMAPPED+i} 67 | #define IRQ_BASE_REMAPPED 32 68 | #define IRQ_TO_INTERRUPT(IRQ) (IRQ + IRQ_BASE_REMAPPED) 69 | 70 | 71 | 72 | /* Struct which aggregates many registers. 73 | It matches exactly the pushes on interrupt.asm. From the bottom: 74 | - Pushed by the processor automatically 75 | - If previledge level changed, "ss" and "esp" 76 | - "eflags", "cs", "eip" 77 | - For some CPU exceptions, "err" (error code) is pushed by CPU, otherwise pushed by our isr-specific handler 78 | - "trapno" pushed by our isr-specific handler (e.g. isr30) 79 | - common_stub 80 | - pushes "ds", "es", "fs" and "gs" 81 | - All the registers by pusha (from "eax" to "edi") 82 | 83 | C struct memory layout: 84 | 13 Within a structure object, the non-bit-field members and the units in which bit-fields reside 85 | have addresses that increase in the order in which they are declared. 86 | A pointer to a structure object, suitably converted, points to its initial member 87 | (or if that member is a bit-field, then to the unit in which it resides), and vice versa. 88 | There may be unnamed padding within a structure object, but not at its beginning. 89 | https://stackoverflow.com/questions/2748995/struct-memory-layout-in-c 90 | */ 91 | typedef struct trapframe { 92 | // registers as pushed by pusha 93 | uint32_t edi; 94 | uint32_t esi; 95 | uint32_t ebp; 96 | uint32_t oesp; // useless & ignored 97 | uint32_t ebx; 98 | uint32_t edx; 99 | uint32_t ecx; 100 | uint32_t eax; 101 | 102 | // rest of trap frame 103 | uint16_t gs; 104 | uint16_t padding1; 105 | uint16_t fs; 106 | uint16_t padding2; 107 | uint16_t es; 108 | uint16_t padding3; 109 | uint16_t ds; 110 | uint16_t padding4; 111 | uint32_t trapno; 112 | 113 | // below here defined by x86 hardware 114 | uint32_t err; 115 | uint32_t eip; 116 | uint16_t cs; 117 | uint16_t padding5; 118 | uint32_t eflags; 119 | 120 | // below here only when crossing rings, such as from user to kernel 121 | uint32_t esp; 122 | uint16_t ss; 123 | uint16_t padding6; 124 | } trapframe; 125 | 126 | 127 | void isr_install(); 128 | 129 | typedef void (*interrupt_handler)(trapframe*); 130 | void register_interrupt_handler(uint8_t n, interrupt_handler handler); 131 | 132 | 133 | #endif -------------------------------------------------------------------------------- /kernel/include/arch/i386/kernel/pic.h: -------------------------------------------------------------------------------- 1 | #ifndef _ARCH_I386_KERNEL_PIC_H 2 | #define _ARCH_I386_KERNEL_PIC_H 3 | 4 | #include "port_io.h" 5 | 6 | // Ref: https://wiki.osdev.org/PIC 7 | 8 | #define PIC1 0x20 /* IO base address for master PIC */ 9 | #define PIC2 0xA0 /* IO base address for slave PIC */ 10 | #define PIC1_COMMAND PIC1 11 | #define PIC1_DATA (PIC1+1) 12 | #define PIC2_COMMAND PIC2 13 | #define PIC2_DATA (PIC2+1) 14 | 15 | #define PIC_EOI 0x20 /* End-of-interrupt command code */ 16 | 17 | #define ICW1_ICW4 0x01 /* ICW4 (not) needed */ 18 | #define ICW1_SINGLE 0x02 /* Single (cascade) mode */ 19 | #define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */ 20 | #define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */ 21 | #define ICW1_INIT 0x10 /* Initialization - required! */ 22 | 23 | #define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */ 24 | #define ICW4_AUTO 0x02 /* Auto (normal) EOI */ 25 | #define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */ 26 | #define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ 27 | #define ICW4_SFNM 0x10 /* Special fully nested (not) */ 28 | 29 | #define PIC_READ_IRR 0x0a /* OCW3 irq ready next CMD read */ 30 | #define PIC_READ_ISR 0x0b /* OCW3 irq service next CMD read */ 31 | 32 | void PIC_remap(int offset1, int offset2); 33 | bool PIC_is_spurious_irq(unsigned char irq); 34 | void PIC_sendEOI(unsigned char irq); 35 | void IRQ_set_mask(unsigned char IRQline); 36 | void IRQ_clear_mask(unsigned char IRQline); 37 | 38 | 39 | #endif -------------------------------------------------------------------------------- /kernel/include/arch/i386/kernel/port_io.h: -------------------------------------------------------------------------------- 1 | #ifndef _ARCH_I386_KERNEL_IO_H 2 | #define _ARCH_I386_KERNEL_IO_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // From https://wiki.osdev.org/Inline_Assembly/Examples 9 | 10 | static inline void outb(uint16_t port, uint8_t val) { 11 | asm volatile ("outb %0, %1" : : "a"(val), "Nd"(port)); 12 | /* There's an outb %al, $imm8 encoding, for compile-time constant port numbers that fit in 8b. (N constraint). 13 | * Wider immediate constants would be truncated at assemble-time (e.g. "i" constraint). 14 | * The outb %al, %dx encoding is the only option for all other cases. 15 | * %1 expands to %dx because port is a uint16_t. %w1 could be used if we had the port number a wider C type */ 16 | } 17 | 18 | static inline void outw(uint16_t port, uint16_t val) { 19 | asm volatile ("outw %0, %1" : : "a"(val), "Nd"(port)); 20 | } 21 | 22 | static inline void outl(uint16_t port, uint32_t val) { 23 | asm volatile ("outl %0, %1" : : "a"(val), "Nd"(port)); 24 | } 25 | 26 | static inline uint8_t inb(uint16_t port) { 27 | uint8_t ret; 28 | asm volatile ("inb %1, %0" 29 | : "=a"(ret) 30 | : "Nd"(port)); 31 | return ret; 32 | } 33 | 34 | static inline uint16_t inw(uint16_t port) { 35 | uint16_t ret; 36 | asm volatile ("inw %1, %0" 37 | : "=a"(ret) 38 | : "Nd"(port)); 39 | return ret; 40 | } 41 | 42 | static inline uint32_t inl(uint16_t port) { 43 | uint32_t ret; 44 | asm volatile ("inl %1, %0" 45 | : "=a"(ret) 46 | : "Nd"(port)); 47 | return ret; 48 | } 49 | 50 | static inline void io_wait(void) { 51 | /* Port 0x80 is used for 'checkpoints' during POST. */ 52 | /* The Linux kernel seems to think it is free for use :-/ */ 53 | asm volatile ("outb %%al, $0x80" : : "a"(0)); 54 | /* %%al instead of %0 makes no difference. TODO: does the register need to be zeroed? */ 55 | } 56 | 57 | 58 | #endif -------------------------------------------------------------------------------- /kernel/include/arch/i386/kernel/segmentation.h: -------------------------------------------------------------------------------- 1 | #ifndef _ARCH_I386_KERNEL_SEGMENTATION_H 2 | #define _ARCH_I386_KERNEL_SEGMENTATION_H 3 | 4 | #include 5 | 6 | //////////////////////////////// 7 | // Segmentation (GDT/TSS) 8 | //////////////////////////////// 9 | 10 | // From: xv6/mmu.h 11 | 12 | // Eflags register 13 | #define FL_IF 0x00000200 // Interrupt Enable 14 | 15 | // Control Register flags 16 | #define CR0_PE 0x00000001 // Protection Enable 17 | #define CR0_WP 0x00010000 // Write Protect 18 | #define CR0_PG 0x80000000 // Paging 19 | 20 | #define CR4_PSE 0x00000010 // Page size extension 21 | 22 | // various segment selectors. 23 | #define SEG_NULL 0 // null segment 24 | #define SEG_KCODE 1 // kernel code 25 | #define SEG_KDATA 2 // kernel data+stack 26 | #define SEG_UCODE 3 // user code 27 | #define SEG_UDATA 4 // user data+stack 28 | #define SEG_TSS 5 // this process's task state 29 | 30 | // segment selector for CS/DS/ES etc. 31 | // See https://wiki.osdev.org/Selector (RPL=Requested Privilege Level) 32 | #define SEG_SELECTOR(seg,rpl) (((seg) << 3) + rpl) 33 | 34 | // global var gdt[NSEGS] holds the above segments. 35 | #define NSEGS 6 36 | 37 | // Segment Descriptor 38 | typedef struct segdesc { 39 | uint32_t lim_15_0 : 16; // Low bits of segment limit 40 | uint32_t base_15_0 : 16; // Low bits of segment base address 41 | uint32_t base_23_16 : 8; // Middle bits of segment base address 42 | uint32_t type : 4; // Segment type (see STS_ constants) 43 | uint32_t s : 1; // 0 = system, 1 = application 44 | uint32_t dpl : 2; // Descriptor Privilege Level 45 | uint32_t p : 1; // Present 46 | uint32_t lim_19_16 : 4; // High bits of segment limit 47 | uint32_t avl : 1; // Unused (available for software use) 48 | uint32_t rsv1 : 1; // Reserved 49 | uint32_t db : 1; // 0 = 16-bit segment, 1 = 32-bit segment 50 | uint32_t g : 1; // Granularity: limit scaled by 4K when set 51 | uint32_t base_31_24 : 8; // High bits of segment base address 52 | } segdesc; 53 | 54 | // For normal segments (4K granularity, so lim >> 12, lim >> 28) 55 | #define SEG(type, base, lim, dpl) (struct segdesc) \ 56 | { ((lim) >> 12) & 0xffff, (uint32_t)(base) & 0xffff, \ 57 | ((uint32_t)(base) >> 16) & 0xff, type, 1, dpl, 1, \ 58 | (uint32_t)(lim) >> 28, 0, 0, 1, 1, (uint32_t)(base) >> 24 } 59 | // For TSS segment (1B granularity) 60 | #define TSS_SEG(type, base, lim, dpl) (struct segdesc) \ 61 | { (uint32_t)(lim) & 0xffff, (uint32_t)(base) & 0xffff, \ 62 | ((uint32_t)(base) >> 16) & 0xff, type, 0, dpl, 1, \ 63 | (uint32_t)(lim) >> 16, 0, 0, 1, 0, (uint32_t)(base) >> 24 } 64 | 65 | #define DPL_KERNEL 0x0 // Kernel DPL 66 | #define DPL_USER 0x3 // User DPL 67 | 68 | // Application segment type bits 69 | #define STA_X 0x8 // Executable segment 70 | #define STA_W 0x2 // Writeable (non-executable segments) 71 | #define STA_R 0x2 // Readable (executable segments) 72 | 73 | // System segment type bits 74 | #define STS_T32A 0x9 // Available 32-bit TSS 75 | #define STS_IG32 0xE // 32-bit Interrupt Gate 76 | #define STS_TG32 0xF // 32-bit Trap Gate 77 | 78 | // Task state segment format 79 | typedef struct task_state { 80 | uint16_t link; // Old ts selector 81 | uint16_t padding0; 82 | uint32_t esp0; // Stack pointers after an increase in privilege level 83 | uint16_t ss0; // Segment selectors after an increase in privilege level 84 | uint16_t padding1; 85 | uint32_t *esp1; 86 | uint16_t ss1; 87 | uint16_t padding2; 88 | uint32_t *esp2; 89 | uint16_t ss2; 90 | uint16_t padding3; 91 | uint32_t cr3; // Page directory base 92 | uint32_t *eip; // Saved state from last task switch 93 | uint32_t eflags; 94 | uint32_t eax; // More saved state (registers) 95 | uint32_t ecx; 96 | uint32_t edx; 97 | uint32_t ebx; 98 | uint32_t *esp; 99 | uint32_t *ebp; 100 | uint32_t esi; 101 | uint32_t edi; 102 | uint16_t es; // Even more saved state (segment selectors) 103 | uint16_t padding4; 104 | uint16_t cs; 105 | uint16_t padding5; 106 | uint16_t ss; 107 | uint16_t padding6; 108 | uint16_t ds; 109 | uint16_t padding7; 110 | uint16_t fs; 111 | uint16_t padding8; 112 | uint16_t gs; 113 | uint16_t padding9; 114 | uint16_t ldtr; 115 | uint16_t padding10; 116 | uint16_t t; // Trap on task switch 117 | uint16_t iomb; // I/O map base address 118 | } task_state; 119 | 120 | #endif -------------------------------------------------------------------------------- /kernel/include/common.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMMON_H 2 | #define _COMMON_H 3 | 4 | #define UNUSED_ARG(x) ((void) (x)) 5 | 6 | typedef unsigned int uint; 7 | typedef unsigned short ushort; 8 | typedef unsigned char uchar; 9 | 10 | #endif -------------------------------------------------------------------------------- /kernel/include/datetime.h: -------------------------------------------------------------------------------- 1 | #ifndef _DATETIME_H 2 | #define _DATETIME_H 3 | 4 | // From Linux struct_tm.h 5 | /* ISO C `broken-down time' structure. */ 6 | typedef struct date_time 7 | { 8 | int tm_sec; /* Seconds. [0-60] (1 leap second) */ 9 | int tm_min; /* Minutes. [0-59] */ 10 | int tm_hour; /* Hours. [0-23] */ 11 | int tm_mday; /* Day. [1-31] */ 12 | int tm_mon; /* Month. [0-11] */ 13 | int tm_year; /* Year - 1900. */ 14 | /* Below are not yet implemented */ 15 | int tm_wday; /* Day of week. [0-6] */ 16 | int tm_yday; /* Days in year.[0-365] */ 17 | int tm_isdst; /* DST. [-1/0/1]*/ 18 | 19 | long int tm_gmtoff; /* Seconds east of UTC. */ 20 | const char *tm_zone; /* Timezone abbreviation. */ 21 | 22 | } date_time; 23 | 24 | #endif -------------------------------------------------------------------------------- /kernel/include/fs.h: -------------------------------------------------------------------------------- 1 | #ifndef _FS_H 2 | #define _FS_H 3 | 4 | #include 5 | 6 | // In line with newlib's unistd.h 7 | #define SEEK_WHENCE_SET 0 8 | #define SEEK_WHENCE_CUR 1 9 | #define SEEK_WHENCE_END 2 10 | 11 | #define FS_MAX_FILENAME_LEN 260 12 | typedef struct fs_dirent { 13 | uint32_t inum; 14 | char name[FS_MAX_FILENAME_LEN]; // file name, max len = 260 + null terminator (referencing FAT32 max file name) 15 | } fs_dirent; 16 | 17 | #endif -------------------------------------------------------------------------------- /kernel/include/fsstat.h: -------------------------------------------------------------------------------- 1 | #ifndef _FSSTAT_H 2 | #define _FSSTAT_H 3 | 4 | #include 5 | #include 6 | 7 | // Mimic Linux stat.h 8 | 9 | typedef struct fs_stat { 10 | uint32_t mount_point_id; /* Mount Point ID */ 11 | uint32_t inum; /* File serial number. */ 12 | uint32_t nlink; /* Link count. */ 13 | uint32_t mode; /* File mode. */ 14 | uint32_t size; /* Size of file, in bytes. */ 15 | uint32_t blocks; /* Number 512-byte blocks allocated. */ 16 | date_time mtime; /* Time of last modification. */ 17 | date_time ctime; /* Time of last status change. */ 18 | } fs_stat; 19 | 20 | 21 | // Sourced from Newlib sys/stat.h 22 | 23 | #define _IFMT 0170000 /* type of file */ 24 | #define _IFDIR 0040000 /* directory */ 25 | #define _IFCHR 0020000 /* character special */ 26 | #define _IFBLK 0060000 /* block special */ 27 | #define _IFREG 0100000 /* regular */ 28 | #define _IFLNK 0120000 /* symbolic link */ 29 | #define _IFSOCK 0140000 /* socket */ 30 | #define _IFIFO 0010000 /* fifo */ 31 | 32 | #define S_IFMT _IFMT 33 | #define S_IFDIR _IFDIR 34 | #define S_IFCHR _IFCHR 35 | #define S_IFBLK _IFBLK 36 | #define S_IFREG _IFREG 37 | #define S_IFLNK _IFLNK 38 | #define S_IFSOCK _IFSOCK 39 | #define S_IFIFO _IFIFO 40 | 41 | #define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR) 42 | #define S_IRUSR 0000400 /* read permission, owner */ 43 | #define S_IWUSR 0000200 /* write permission, owner */ 44 | #define S_IXUSR 0000100/* execute/search permission, owner */ 45 | #define S_IRWXG (S_IRGRP | S_IWGRP | S_IXGRP) 46 | #define S_IRGRP 0000040 /* read permission, group */ 47 | #define S_IWGRP 0000020 /* write permission, grougroup */ 48 | #define S_IXGRP 0000010/* execute/search permission, group */ 49 | #define S_IRWXO (S_IROTH | S_IWOTH | S_IXOTH) 50 | #define S_IROTH 0000004 /* read permission, other */ 51 | #define S_IWOTH 0000002 /* write permission, other */ 52 | #define S_IXOTH 0000001/* execute/search permission, other */ 53 | 54 | #define S_ISBLK(m) (((m)&_IFMT) == _IFBLK) 55 | #define S_ISCHR(m) (((m)&_IFMT) == _IFCHR) 56 | #define S_ISDIR(m) (((m)&_IFMT) == _IFDIR) 57 | #define S_ISFIFO(m) (((m)&_IFMT) == _IFIFO) 58 | #define S_ISREG(m) (((m)&_IFMT) == _IFREG) 59 | #define S_ISLNK(m) (((m)&_IFMT) == _IFLNK) 60 | #define S_ISSOCK(m) (((m)&_IFMT) == _IFSOCK) 61 | 62 | // Copied from Newlib sys/_default_fcntl.h 63 | 64 | #define O_RDONLY 0 /* +1 == FREAD */ 65 | #define O_WRONLY 1 /* +1 == FWRITE */ 66 | #define O_RDWR 2 /* +1 == FREAD|FWRITE */ 67 | 68 | #define _FAPPEND 0x0008 /* append (writes guaranteed at the end) */ 69 | #define _FCREAT 0x0200 /* open with file create */ 70 | #define _FTRUNC 0x0400 /* open with truncation */ 71 | #define _FEXCL 0x0800 /* error on open if file exists */ 72 | #define O_APPEND _FAPPEND 73 | #define O_CREAT _FCREAT 74 | #define O_TRUNC _FTRUNC 75 | #define O_EXCL _FEXCL 76 | 77 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/arch_init.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_ARCH_INIT_H 2 | #define _KERNEL_ARCH_INIT_H 3 | 4 | #include 5 | 6 | // Architecture specific initialization 7 | void initialize_architecture(uint32_t mbt_physical_addr); 8 | 9 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/arp.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_ARP_H 2 | #define _KERNEL_ARP_H 3 | 4 | #include 5 | #include 6 | 7 | // Number of cached IP-MAC mapping 8 | #define ARP_CACHE_N_RECORD 512 9 | 10 | int init_arp(); 11 | int arp_announce_ip(ip_addr ip); 12 | int arp_probe(ip_addr ip); 13 | int arp_process_packet(void* buf, uint16_t len); 14 | int arp_ip2mac(ip_addr ip, mac_addr* mac); 15 | 16 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/ata.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_ATA_H 2 | #define _KERNEL_ATA_H 3 | 4 | #include 5 | #include 6 | 7 | void read_sectors_ATA_PIO(bool slave, void* buf, uint32_t LBA, uint32_t sector_count); 8 | void write_sectors_ATA_PIO(bool slave, const void* buf, uint32_t LBA, uint32_t sector_count); 9 | int32_t get_total_28bit_sectors(bool slave); 10 | 11 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/block_io.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_BLOCK_IO_H 2 | #define _KERNEL_BLOCK_IO_H 3 | 4 | #include 5 | #include 6 | 7 | // device_id for IDE master/slave drives 8 | #define IDE_MASTER_DRIVE 1 9 | #define IDE_SLAVE_DRIVE 2 10 | 11 | typedef enum block_storage_type { 12 | BLK_STORAGE_TYP_ATA_HARD_DRIVE 13 | } block_storage_type; 14 | 15 | typedef struct block_storage { 16 | uint32_t device_id; //id == 0 means unused slot 17 | block_storage_type type; 18 | uint32_t block_size; // block (sector) size in bytes 19 | uint32_t block_count; // total number of blocks 20 | int64_t (*read_blocks)(struct block_storage* storage, void* buff, uint32_t LBA, uint32_t block_count); // return bytes read, 0 means error 21 | int64_t (*write_blocks)(struct block_storage* storage, uint32_t LBA, uint32_t block_count, const void* buff); // return bytes written, 0 means error 22 | void* internal_info; // internal data structure for the specfic storage type 23 | } block_storage; 24 | 25 | block_storage* get_block_storage(uint32_t device_id); 26 | 27 | // Shall use the this signature when implementing in kernel 28 | void initialize_block_storage(); 29 | 30 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/console.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_CONSOLE_H 2 | #define _KERNEL_CONSOLE_H 3 | 4 | #include 5 | 6 | #define CONSOLE_BUF_SIZE 255 7 | 8 | int console_init(struct file_system* fs); 9 | 10 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/cpu.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_CPU_H 2 | #define _KERNEL_CPU_H 3 | 4 | #include 5 | 6 | void init_cpu(); 7 | void disable_interrupt(); 8 | void enable_interrupt(); 9 | int is_interrupt_enabled(); 10 | void push_cli(); 11 | void pop_cli(); 12 | void halt(); 13 | // atomically exchange/swap value 14 | uint xchg(volatile uint *addr, uint newval); 15 | 16 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/elf.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_ELF_H 2 | #define _KERNEL_ELF_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | // The following are copied from /usr/include/elf.h (Ubuntu) 11 | 12 | /* Type for a 16-bit quantity. */ 13 | typedef uint16_t Elf32_Half; 14 | 15 | /* Types for signed and unsigned 32-bit quantities. */ 16 | typedef uint32_t Elf32_Word; 17 | typedef int32_t Elf32_Sword; 18 | 19 | /* Types for signed and unsigned 64-bit quantities. */ 20 | typedef uint64_t Elf32_Xword; 21 | typedef int64_t Elf32_Sxword; 22 | 23 | /* Type of addresses. */ 24 | typedef uint32_t Elf32_Addr; 25 | 26 | /* Type of file offsets. */ 27 | typedef uint32_t Elf32_Off; 28 | 29 | /* Type for section indices, which are 16-bit quantities. */ 30 | typedef uint16_t Elf32_Section; 31 | 32 | /* Type for version symbol information. */ 33 | typedef Elf32_Half Elf32_Versym; 34 | 35 | 36 | /* The ELF file header. This appears at the start of every ELF file. */ 37 | 38 | #define EI_NIDENT (16) 39 | 40 | typedef struct 41 | { 42 | unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ 43 | Elf32_Half e_type; /* Object file type */ 44 | Elf32_Half e_machine; /* Architecture */ 45 | Elf32_Word e_version; /* Object file version */ 46 | Elf32_Addr e_entry; /* Entry point virtual address */ 47 | Elf32_Off e_phoff; /* Program header table file offset */ 48 | Elf32_Off e_shoff; /* Section header table file offset */ 49 | Elf32_Word e_flags; /* Processor-specific flags */ 50 | Elf32_Half e_ehsize; /* ELF header size in bytes */ 51 | Elf32_Half e_phentsize; /* Program header table entry size */ 52 | Elf32_Half e_phnum; /* Program header table entry count */ 53 | Elf32_Half e_shentsize; /* Section header table entry size */ 54 | Elf32_Half e_shnum; /* Section header table entry count */ 55 | Elf32_Half e_shstrndx; /* Section header string table index */ 56 | } Elf32_Ehdr; 57 | 58 | /* Conglomeration of the identification bytes, for easy testing as a word. */ 59 | #define ELFMAG "\177ELF" 60 | 61 | typedef struct 62 | { 63 | Elf32_Word p_type; /* Segment type */ 64 | Elf32_Off p_offset; /* Segment file offset */ 65 | Elf32_Addr p_vaddr; /* Segment virtual address */ 66 | Elf32_Addr p_paddr; /* Segment physical address */ 67 | Elf32_Word p_filesz; /* Segment size in file */ 68 | Elf32_Word p_memsz; /* Segment size in memory */ 69 | Elf32_Word p_flags; /* Segment flags */ 70 | Elf32_Word p_align; /* Segment alignment */ 71 | } Elf32_Phdr; 72 | 73 | 74 | /* Legal values for p_type (segment type). */ 75 | 76 | #define PT_NULL 0 /* Program header table entry unused */ 77 | #define PT_LOAD 1 /* Loadable program segment */ 78 | #define PT_DYNAMIC 2 /* Dynamic linking information */ 79 | #define PT_INTERP 3 /* Program interpreter */ 80 | #define PT_NOTE 4 /* Auxiliary information */ 81 | #define PT_SHLIB 5 /* Reserved */ 82 | #define PT_PHDR 6 /* Entry for header table itself */ 83 | #define PT_TLS 7 /* Thread-local storage segment */ 84 | #define PT_NUM 8 /* Number of defined types */ 85 | #define PT_LOOS 0x60000000 /* Start of OS-specific */ 86 | #define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ 87 | #define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ 88 | #define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ 89 | #define PT_LOSUNW 0x6ffffffa 90 | #define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ 91 | #define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ 92 | #define PT_HISUNW 0x6fffffff 93 | #define PT_HIOS 0x6fffffff /* End of OS-specific */ 94 | #define PT_LOPROC 0x70000000 /* Start of processor-specific */ 95 | #define PT_HIPROC 0x7fffffff /* End of processor-specific */ 96 | 97 | /* Legal values for p_flags (segment flags). */ 98 | 99 | #define PF_X (1 << 0) /* Segment is executable */ 100 | #define PF_W (1 << 1) /* Segment is writable */ 101 | #define PF_R (1 << 2) /* Segment is readable */ 102 | #define PF_MASKOS 0x0ff00000 /* OS-specific */ 103 | #define PF_MASKPROC 0xf0000000 /* Processor-specific */ 104 | 105 | bool is_elf(const char* buff); 106 | 107 | Elf32_Addr load_elf(pde* page_dir, const char* buff, uint32_t* vaddr_ub); 108 | 109 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/ethernet.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_ETHERNET_H 2 | #define _KERNEL_ETHERNET_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | int init_ethernet(); 9 | int process_ethernet_packet(void* buf, uint16_t len, uint32_t crc); 10 | 11 | int eth_prep_pkt(eth_opt* opt, void* buf, uint16_t buf_len); 12 | int eth_send_pkt(void* buf, uint16_t pkt_len); 13 | 14 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/heap.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_HEAP_H 2 | #define _KERNEL_HEAP_H 3 | 4 | #include 5 | #include 6 | 7 | void initialize_kernel_heap(); 8 | void kfree(void* vaddr); 9 | void* kmalloc(size_t size); 10 | 11 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/icmp.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_ICMP_H 2 | #define _KERNEL_ICMP_H 3 | 4 | #include 5 | #include 6 | 7 | int init_icmp(); 8 | // int icmp_process_packet(ip_addr src_ip, void* buf, uint16_t pkt_len); 9 | // int icmp_send_packet(ip_addr dst, enum icmp_type type, enum icmp_code code, icmp_rest_of_header rest, void* data, uint16_t data_len); 10 | 11 | int icmp_prep_pkt(icmp_opt* opt, void* buf, uint16_t buf_len); 12 | int icmp_finalize_pkt(icmp_header* buf, uint16_t pkt_len); 13 | // int icmp_send_pkt(int header_offset, void* buf, uint16_t pkt_len); 14 | 15 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/ipv4.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_IPv4_H 2 | #define _KERNEL_IPv4_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | int init_ipv4(); 9 | int ipv4_process_packet(void* buf, uint len); 10 | int ipv4_wait_for_next_packet(void* buf, uint buf_size, uint timeout_sec); 11 | 12 | int ipv4_prep_pkt(ipv4_opt* opt, void* buf, uint16_t buf_len); 13 | int ipv4_send_pkt(void* buf, uint16_t pkt_len); 14 | 15 | int ipv4_opt_from_header(const ipv4_header* hdr, ipv4_opt* opt); 16 | 17 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/keyboard.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_KEYBOARD_H 2 | #define _KERNEL_KEYBOARD_H 3 | 4 | // Encoding keys into a 32 bit structure 5 | typedef int key; 6 | // The key representation as an int is similar to the following struct 7 | // struct { 8 | // int ascii:8; // zero if no ASCII counterpart 9 | // int special:8; // zero if not a special key 10 | // int keypad:1; // is keypad key 11 | // int ctrl:1; // is Ctrl combination key 12 | // int alt:1; // is Alt combination key 13 | // int unused:13; // for future extension 14 | // }; 15 | 16 | #define KEY_ASCII_BITS (0xFF) 17 | #define KEY_SPECIAL_BITS (0xFF << 8) 18 | #define KEY_KEYPAD_BIT (1 << 16) 19 | #define KEY_CTRL_BIT (1 << 17) 20 | #define KEY_ALT_BIT (1 << 18) 21 | 22 | #define KEY_GET_ASCII_BITS(k) ((k) & KEY_ASCII_BITS) 23 | #define KEY_GET_SPECIAL_BITS(k) (((k) & KEY_SPECIAL_BITS) >> 8) 24 | 25 | //not-mapped key stroke 26 | #define NO 0 27 | 28 | #define KEY_FN(n) (n << 8) 29 | 30 | // Special keycodes 31 | #define KEY_UP (13 << 8) 32 | #define KEY_DN (14 << 8) 33 | #define KEY_RT (15 << 8) 34 | #define KEY_LF (16 << 8) 35 | #define KEY_PGUP (17 << 8) 36 | #define KEY_PGDN (18 << 8) 37 | #define KEY_INS (19 << 8) 38 | #define KEY_DEL (20 << 8) 39 | #define KEY_LWIN (21 << 8) 40 | #define KEY_RWIN (22 << 8) 41 | #define KEY_MENU (23 << 8) 42 | #define KEY_HOME (24 << 8) 43 | #define KEY_END (25 << 8) 44 | #define KEY_BACKSPACE (26 << 8) 45 | 46 | // Print Screen / Sys Rq Key 47 | #define KEY_PSC ((0xFF-2) << 8) 48 | // Pause / Break Key 49 | #define KEY_BRK ((0xFF-1) << 8) 50 | // ESC 51 | #define KEY_ESC (0xFF << 8) 52 | 53 | #define KEY_KEYPAD(c) (KEY_KEYPAD_BIT | (c)) 54 | #define KEY_CTRL(c) (KEY_CTRL_BIT | (c)) 55 | #define KEY_ALT(c) (KEY_CTRL_BIT | (c)) 56 | 57 | void init_keyboard(); 58 | key read_key_buffer(); 59 | 60 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/lock.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_LOCK_H 2 | #define _KERNEL_LOCK_H 3 | 4 | #include 5 | #include 6 | 7 | // Disabling interrupt when locked, strictest 8 | // When the lock is holding by other process, yield 9 | // When the lock is holding by the same process, panic 10 | // Under single-CPU setting, if no manual yielding in critical region, 11 | // it is suffice to protect only the write operation 12 | typedef struct yield_lock { 13 | uint locked; 14 | int holding_pid; 15 | } yield_lock; 16 | 17 | // Does NOT disable interrupt when locked 18 | // Use this ONLY if the resoure will NOT be used in any interrupt handler 19 | // otherwise a deallock can happen, since the lock rely on yield() to wait 20 | // the writer to release the lock, but we cannot yield to the same process 21 | // from the interrupt handler 22 | typedef struct rw_lock { 23 | yield_lock lk; 24 | int writing_pid; 25 | uint reading; 26 | } rw_lock; 27 | 28 | 29 | void acquire(yield_lock* lk); 30 | void release(yield_lock* lk); 31 | uint holding(yield_lock* lk); 32 | 33 | void start_writing(rw_lock* lk); 34 | void finish_writing(rw_lock* lk); 35 | void start_reading(rw_lock* lk); 36 | void finish_reading(rw_lock* lk); 37 | 38 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/memory_bitmap.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_MEMORY_BITMAP_H 2 | #define _KERNEL_MEMORY_BITMAP_H 3 | 4 | #include 5 | #include 6 | 7 | // Macros used in the bitset algorithms. 8 | // A frame is 4KiB in size 9 | #define FRAME_SIZE 0x1000 10 | // Total number of frames: {4GB 32-bit addressable space: 2^32}/{4KiB: 2^12} = 2^20 = 0x100000 11 | #define N_FRAMES 0x100000 12 | // Get the 0-based frame index from physical address, one frame is 4KiB in size 13 | #define FRAME_INDEX_FROM_ADDR(a) ((a) / FRAME_SIZE) 14 | // Get the 4KiB aligned physical address from frame index 15 | #define ADDR_FROM_FRAME_INDEX(a) ((a) * FRAME_SIZE) 16 | // The bits are stored in an array of uint32_t 17 | // so each uint32_t can store the status of 32 frames (4bytes * 8bits/byte) 18 | #define ARRAY_INDEX_FROM_FRAME_INDEX(a) ((a) / (8 * 4)) 19 | // Get the 0-based offset into the uint32_t 20 | #define BIT_OFFSET_FROM_FRAME_INDEX(a) ((a) % (8 * 4)) 21 | 22 | void clear_frame(uint32_t frame_idx); 23 | uint32_t test_frame(uint32_t frame_idx); 24 | uint32_t first_free_frame(); 25 | uint32_t n_free_frames(uint n); 26 | void initialize_bitmap(uint32_t mbt_physical_addr); 27 | 28 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/network.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_NETWORK_H 2 | #define _KERNEL_NETWORK_H 3 | 4 | int init_network(); 5 | 6 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/paging.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_PAGING_H 2 | #define _KERNEL_PAGING_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // A page is 4KiB in size 10 | #define PAGE_SIZE 0x1000 11 | // Get 0-based page index from virtual address, one page is 4KiB in size 12 | #define PAGE_INDEX_FROM_VADDR(vaddr) ((vaddr) / PAGE_SIZE) 13 | // Get the 4KiB aligned virtual address from page index 14 | #define VADDR_FROM_PAGE_INDEX(idx) ((idx) * PAGE_SIZE) 15 | 16 | #define PAGE_COUNT_FROM_BYTES(n_bytes) (((n_bytes) + (PAGE_SIZE-1))/PAGE_SIZE) 17 | 18 | // Variables from linker script 19 | extern char MAP_MEM_PA_ZERO_TO[], KERNEL_VIRTUAL_END[]; 20 | 21 | void initialize_paging(); 22 | 23 | // definition various between architecture 24 | struct page_directory_entry; 25 | typedef struct page_directory_entry pde; 26 | 27 | pde* curr_page_dir(); 28 | pde* alloc_page_dir(); 29 | 30 | uint32_t alloc_pages(pde* page_dir, size_t page_count, bool is_kernel, bool is_writeable); 31 | uint32_t alloc_pages_consecutive_frames(pde* page_dir, size_t page_count, bool is_writeable, uint32_t* physical_addr); 32 | uint32_t change_page_rw_attr(pde* page_dir, uint32_t page_index, bool is_writeable); 33 | uint32_t alloc_pages_at(pde* page_dir, uint32_t page_index, size_t page_count, bool is_kernel, bool is_writeable); 34 | void dealloc_pages(pde* page_dir, uint32_t page_index, size_t page_count); 35 | 36 | uint map_pages_at(pde* page_dir, uint page_index, uint page_count, uint32_t* frames, bool is_kernel, bool is_writeable, bool consecutive_frame); 37 | uint32_t link_pages(pde* pd_source, uint32_t vaddr, uint32_t size, pde* pd_target, bool allow_alloc_source, bool alloc_source_rw, bool target_rw); 38 | void unmap_pages(pde* page_dir, uint32_t vaddr, uint32_t size); 39 | 40 | bool is_vaddr_accessible(pde* page_dir, uint32_t vaddr, bool is_from_kernel_code, bool is_writing); 41 | uint32_t vaddr2paddr(pde* page_dir, uint32_t vaddr); 42 | 43 | void switch_page_directory(uint32_t physical_addr); 44 | void set_tss(uint32_t kernel_stack_esp); 45 | 46 | void copy_kernel_space_mapping(pde* page_dir); 47 | pde* copy_user_space(pde* page_dir); 48 | void free_user_space(pde* page_dir); 49 | 50 | 51 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/panic.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_PANIC_H 2 | #define _KERNEL_PANIC_H 3 | 4 | #include 5 | #include 6 | 7 | #define PANIC_ASSERT(condition) ((condition) ? (void)0 : panic(__FILE__, __LINE__, "ASSERTION-FAILED", #condition)) 8 | #define PANIC(reason) (panic(__FILE__, __LINE__, "KERNEL PANIC", #reason)) 9 | 10 | void panic(const char* file, uint32_t line, const char* panic_type, const char* desc) __attribute__ ((noreturn)); 11 | 12 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/pci.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_PCI_H 2 | #define _KERNEL_PCI_H 3 | 4 | #include 5 | 6 | uint32_t pci_read_reg(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint8_t size); 7 | void pci_write_reg(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint8_t size, uint32_t value); 8 | 9 | void init_pci(); 10 | 11 | // For any header type 12 | #define PCI_VENDER_ID(bus,device,function) ((uint16_t) pci_read_reg((bus), (device), (function), 0, 2)) 13 | #define PCI_DEVICE_ID(bus,device,function) ((uint16_t) pci_read_reg((bus), (device), (function), 2, 2)) 14 | #define PCI_HEADER_TYPE(bus,device,function) ((uint8_t) pci_read_reg((bus), (device), (function), 0x0E, 1)) 15 | #define PCI_BASE_CLASS(bus,device,function) ((uint8_t) pci_read_reg((bus), (device), (function), 0x0B, 1)) 16 | #define PCI_SUB_CLASS(bus,device,function) ((uint8_t) pci_read_reg((bus), (device), (function), 0x0B, 1)) 17 | 18 | #define PCI_COMMAND(bus,device,function) ((uint16_t) pci_read_reg((bus), (device), (function), 4, 2)) 19 | #define PCI_W_COMMAND(bus,device,function,value) pci_write_reg((bus), (device), (function), 4, 2, (value)) 20 | #define PCI_COMMAND_BUS_MASTER (1 << 2) 21 | #define PCI_COMMAND_INT_DISABLE (1 << 10) 22 | 23 | #define PCI_STATUS(bus,device,function) ((uint16_t) pci_read_reg((bus), (device), (function), 6, 2)) 24 | 25 | // For header type 0 and 1 26 | #define PCI_BAR_0(bus,device,function) pci_read_reg((bus), (device), (function), 0x10, 4) 27 | #define PCI_BAR_1(bus,device,function) pci_read_reg((bus), (device), (function), 0x14, 4) 28 | #define PCI_INT_PIN(bus,device,function) ((uint8_t) pci_read_reg((bus), (device), (function), 0x3D, 1) 29 | #define PCI_INT_LINE(bus,device,function) ((uint8_t) pci_read_reg((bus), (device), (function), 0x3C, 1)) 30 | 31 | // For header type 1 32 | #define PCI_SECONDARY_BUS(bus,device,function) ((uint8_t) pci_read_reg((bus), (device), (function), 0x19, 1)) 33 | 34 | 35 | 36 | 37 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/pipe.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_PIPE_H 2 | #define _KERNEL_PIPE_H 3 | 4 | #include 5 | 6 | #define N_MAX_PIPE 64 7 | 8 | int pipe_init(struct file_system* fs); 9 | 10 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/process.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_PROCESS_H 2 | #define _KERNEL_PROCESS_H 3 | 4 | #include 5 | #include 6 | 7 | // maximum number of processes 8 | #define N_PROCESS 64 9 | // number of pages allocating to each process's kernel stack 10 | // 256 page = 1Mib 11 | #define N_KERNEL_STACK_PAGE_SIZE 256 12 | // maximum number of opened hanldes for one process 13 | #define MAX_HANDLE_PER_PROCESS 16 14 | 15 | // max number of command line arguments plus environment variables 16 | #define MAX_ARGC 20 17 | // user program stack size in pages 18 | // 256 page = 1Mib 19 | #define USER_STACK_PAGE_SIZE 256 20 | 21 | // process context, architecture specific 22 | struct context; 23 | // trapframe shall be provided by ISR 24 | struct trapframe; 25 | 26 | // Source: xv6/proc.h 27 | 28 | enum procstate { PROC_STATE_UNUSED, PROC_STATE_EMBRYO, PROC_STATE_SLEEPING, PROC_STATE_RUNNABLE, PROC_STATE_RUNNING, PROC_STATE_ZOMBIE }; 29 | 30 | enum handle_type { 31 | HANDLE_TYPE_UNUSED = 0, 32 | HANDLE_TYPE_FILE, 33 | HANDLE_TYPE_SOCKET 34 | }; 35 | 36 | struct handle_map { 37 | int type; // enum handle_type, resource type 38 | int grd; // global resource descriptor (unique among each source type) 39 | }; 40 | 41 | // Per-process state 42 | typedef struct proc { 43 | int32_t pid; // Process ID 44 | enum procstate state; // Process state 45 | pde* page_dir; // Page directory (only user space part matter) 46 | void *kernel_stack; // Bottom of kernel stack for this process 47 | void *user_stack; // Bottom of user space stack for this process 48 | struct proc *parent; // Parent process 49 | struct trapframe *tf; // Trap frame for current syscall 50 | struct context *context; // swtch() here to run process; used for switching between process in kernel space 51 | uint32_t size; // process size, a pointer to the end of the process memory 52 | uint32_t orig_size; // original size, size shall not shrink below this 53 | int32_t exit_code; // exit code for zombie process 54 | struct handle_map handles[MAX_HANDLE_PER_PROCESS]; // Opened handles for any system resources, e.g. files 55 | char* cwd; // Current working directory 56 | uint no_schedule; // if non zero, will not be scheduled to other process 57 | } proc; 58 | 59 | proc* create_process(); 60 | void init_first_process(); 61 | void scheduler(); 62 | proc* curr_proc(); 63 | // void process_IRQ(uint no_schedule); 64 | void yield(); 65 | int fork(); 66 | void exit(int exit_code); 67 | int wait(int* wait_status); 68 | void switch_process_memory_mapping(proc* p); 69 | char* get_abs_path(const char* path); 70 | int chdir(const char* path); 71 | int getcwd(char* buf, size_t buf_size); 72 | int exec(const char* path, char* const* argv, char* const* envp); 73 | 74 | // Manage per-process handles 75 | int alloc_handle(struct handle_map* pmap); 76 | struct handle_map* get_handle(int handle); 77 | int dup_handle(int handle); 78 | int release_handle(int handle); 79 | 80 | // defined in switch_kernel_context.asm 81 | extern void switch_kernel_context(struct context **old_context, struct context *new_context); 82 | 83 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/serial.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_SERIAL_H 2 | #define _KERNEL_SERIAL_H 3 | 4 | #include 5 | 6 | bool is_serial_port_initialized(); 7 | void init_serial(); 8 | char read_serial(); 9 | void write_serial(char a); 10 | 11 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/socket.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_SOCKET_H 2 | #define _KERNEL_SOCKET_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | // Ref: POSIX Socket Doc 10 | // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_socket.h.html 11 | 12 | 13 | int socket(int domain, int type, int protocol); 14 | 15 | ssize_t sendto(int socket, const void *message, size_t length, 16 | int flags, const struct sockaddr *dest_addr, 17 | socklen_t dest_len); 18 | 19 | int setsockopt(int socket, int level, int option_name, const void *option_value, socklen_t option_len); 20 | // int getsockopt(int socket, int level, int option_name, void * option_value, socklen_t * option_len); 21 | 22 | ssize_t recvfrom(int socket, void * buffer, size_t length, 23 | int flags, struct sockaddr * address, 24 | socklen_t * address_len); 25 | 26 | // int bind(int socket, const struct sockaddr *address, socklen_t address_len); 27 | 28 | // ssize_t recv(int, void *, size_t, int); 29 | // ssize_t send(int, const void *, size_t, int); 30 | 31 | // int listen(int, int); 32 | // int accept(int socket, struct sockaddr * address, socklen_t * address_len); 33 | // int connect(int, const struct sockaddr *, socklen_t); 34 | 35 | // struct msghdr { 36 | // void *msg_name; // Optional address. 37 | // socklen_t msg_namelen; // Size of address. 38 | // struct iovec *msg_iov; // Scatter/gather array. 39 | // int msg_iovlen; // Members in msg_iov. 40 | // void *msg_control; // Ancillary data; see below. 41 | // socklen_t msg_controllen; // Ancillary data buffer len. 42 | // int msg_flags; // Flags on received message. 43 | // }; 44 | // ssize_t sendmsg(int, const struct msghdr *, int); 45 | // ssize_t recvmsg(int, struct msghdr *, int); 46 | 47 | // int getpeername(int, struct sockaddr *, socklen_t *); 48 | // int getsockname(int, struct sockaddr *, socklen_t *); 49 | 50 | // int shutdown(int, int); 51 | // int sockatmark(int); 52 | // int socketpair(int, int, int, int [2]); 53 | 54 | 55 | // System specific definitions 56 | 57 | #define MAX_SOCKET_OPENED 32 58 | 59 | typedef struct pkt_cache { 60 | void* buff; 61 | uint pkt_len; 62 | struct pkt_cache* next; 63 | struct sockaddr src; 64 | socklen_t src_len; 65 | } pkt_cache; 66 | 67 | typedef struct socket_opt { 68 | struct timeval recv_timeout; 69 | } socket_opt; 70 | 71 | typedef struct socket_descriptor { 72 | int domain; 73 | int type; 74 | int protocol; 75 | 76 | // struct sockaddr filter; 77 | 78 | void* transmit_buff; 79 | pkt_cache* cache; 80 | int ref; 81 | 82 | socket_opt opt; 83 | ipv4_opt ip_opt; 84 | void* protocol_opt; 85 | } socket_descriptor; 86 | 87 | int socket_process_pkt(int protocol, void* pkt, uint16_t pkt_len); 88 | 89 | int close_socket(int socket); 90 | 91 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/syscall.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_SYSCALL_H 2 | #define _KERNEL_SYSCALL_H 3 | 4 | // Syscall int number 5 | #define INT_SYSCALL 88 6 | 7 | // defined in interrupt.asm 8 | extern void int88(); 9 | 10 | // arch specific, defined in isr.h 11 | struct trapframe; 12 | 13 | void syscall_handler(struct trapframe* r); 14 | 15 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/tar.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_TAR_H 2 | #define _KERNEL_TAR_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | // Copied from bootloader 12 | // The whole bootloader binary is assumed to take the first 16 sectors 13 | // must be in sync of BOOTLOADER_MAX_SIZE in Makefile (of bootloader) 14 | #define BOOTLOADER_SECTORS 32 15 | 16 | #define TAR_SECTOR_SIZE 512 17 | 18 | #define TAR_MAX_PATH_LEN 254 19 | 20 | typedef struct tar_file_header { 21 | char filename[100]; 22 | uint64_t mode; 23 | uint64_t owner_id; 24 | uint64_t group_id; 25 | char size[12]; 26 | char mod_time[12]; 27 | uint64_t checksum; 28 | char type; 29 | char linked_name[100]; 30 | char magic[6]; 31 | char version[2]; 32 | char owner[32]; 33 | char group[32]; 34 | uint64_t device_major; 35 | uint64_t device_minor; 36 | char filename_prefix[155]; 37 | } tar_file_header; 38 | 39 | // Copied from newlib tar.h 40 | /* General definitions */ 41 | #define TMAGIC "ustar" /* ustar plus null byte. */ 42 | #define TMAGLEN 6 /* Length of the above. */ 43 | #define TVERSION "00" /* 00 without a null byte. */ 44 | #define TVERSLEN 2 /* Length of the above. */ 45 | /* Typeflag field definitions */ 46 | #define REGTYPE '0' /* Regular file. */ 47 | #define AREGTYPE '\0' /* Regular file. */ 48 | #define LNKTYPE '1' /* Link. */ 49 | #define SYMTYPE '2' /* Symbolic link. */ 50 | #define CHRTYPE '3' /* Character special. */ 51 | #define BLKTYPE '4' /* Block special. */ 52 | #define DIRTYPE '5' /* Directory. */ 53 | #define FIFOTYPE '6' /* FIFO special. */ 54 | #define CONTTYPE '7' /* Reserved. */ 55 | /* Mode field bit definitions (octal) */ 56 | #define TSUID 04000 /* Set UID on execution. */ 57 | #define TSGID 02000 /* Set GID on execution. */ 58 | #define TSVTX 01000 /* On directories, restricted deletion flag. */ 59 | #define TUREAD 00400 /* Read by owner. */ 60 | #define TUWRITE 00200 /* Write by owner. */ 61 | #define TUEXEC 00100 /* Execute/search by owner. */ 62 | #define TGREAD 00040 /* Read by group. */ 63 | #define TGWRITE 00020 /* Write by group. */ 64 | #define TGEXEC 00010 /* Execute/search by group. */ 65 | #define TOREAD 00004 /* Read by other. */ 66 | #define TOWRITE 00002 /* Write by other. */ 67 | #define TOEXEC 00001 /* Execute/search by other. */ 68 | 69 | typedef struct tar_mount_option { 70 | block_storage* storage; 71 | uint starting_LBA; 72 | } tar_mount_option; 73 | 74 | enum tar_error_code { 75 | TAR_ERR_GENERAL = -1, 76 | TAR_ERR_NOT_USTAR = -2, 77 | TAR_ERR_FILE_NAME_NOT_MATCH = -3, 78 | TAR_ERR_LBA_GT_MAX_SECTOR = -4 79 | }; 80 | 81 | 82 | int tar_init(struct file_system* fs); 83 | 84 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/time.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_TIME_H 2 | #define _KERNEL_TIME_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | date_time current_datetime(); 9 | time_t datetime2epoch(date_time* tim_p); 10 | int64_t cpu_freq(); 11 | 12 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_TIMER_H 2 | #define _KERNEL_TIMER_H 3 | 4 | #include 5 | 6 | void init_timer(uint32_t freq, uint32_t tick_between_process_switch); 7 | 8 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/tty.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_TTY_H 2 | #define _KERNEL_TTY_H 3 | 4 | #include 5 | #include 6 | 7 | 8 | // Normally one row in text mode consists of scanline 0 - 15 9 | // We are defining an underscore '_' style cursor here 10 | #define TTY_CURSOR_SCANLINE_START 14 11 | #define TTY_CURSOR_SCANLINE_END 15 12 | 13 | enum tty_font_attr { 14 | TTY_FONT_ATTR_CLEAR = 0, 15 | TTY_FONT_ATTR_BOLD = 1, 16 | TTY_FONT_ATTR_UNDER_SCORE = 2, 17 | TTY_FONT_ATTR_BLINK = 4, 18 | TTY_FONT_ATTR_REVERSE_COLOR = 8, 19 | }; 20 | 21 | // Clear screen mode 22 | enum tty_clear_screen_mode { 23 | TTY_CLEAR_SCREEN_AFTER, 24 | TTY_CLEAR_SCREEN_BEFORE, 25 | TTY_CLEAR_ALL, 26 | TTY_CLEAR_LINE_AFTER, 27 | TTY_CLEAR_LINE_BEFORE, 28 | TTY_CLEAR_LINE 29 | }; 30 | 31 | #define ARGB(a,r,g,b) ((b)|(g << 8)|(r << 16)|(a << 24)) 32 | 33 | // This has identical to VGA 4bit color (VGA_COLOR_*) 34 | enum tty_color { 35 | TTY_COLOR_BLACK = 0, 36 | TTY_COLOR_BLUE = 1, 37 | TTY_COLOR_GREEN = 2, 38 | TTY_COLOR_CYAN = 3, 39 | TTY_COLOR_RED = 4, 40 | TTY_COLOR_MAGENTA = 5, 41 | TTY_COLOR_BROWN = 6, 42 | TTY_COLOR_LIGHT_GREY = 7, 43 | TTY_COLOR_DARK_GREY = 8, 44 | TTY_COLOR_LIGHT_BLUE = 9, 45 | TTY_COLOR_LIGHT_GREEN = 10, 46 | TTY_COLOR_LIGHT_CYAN = 11, 47 | TTY_COLOR_LIGHT_RED = 12, 48 | TTY_COLOR_LIGHT_MAGENTA = 13, 49 | TTY_COLOR_LIGHT_BROWN = 14, 50 | TTY_COLOR_WHITE = 15, 51 | }; 52 | 53 | // default foreground (FG) and background (BG) color 54 | #define TTY_DEFAULT_COLOR_FG TTY_COLOR_LIGHT_GREY 55 | #define TTY_DEFAULT_COLOR_BG TTY_COLOR_BLACK 56 | 57 | // In text mode, equal to tty_color 58 | // In video mode, equal to ARGB value 59 | typedef uint32_t tty_color_spec; 60 | 61 | void terminal_initialize(uint32_t mbt_physical_addr); 62 | void terminal_clear_screen(enum tty_clear_screen_mode mode); 63 | void terminal_putchar(char c); 64 | void terminal_write(const char* data, size_t size); 65 | void terminal_writestring(const char* data); 66 | void enable_cursor(); 67 | void disable_cursor(); 68 | void terminal_get_color(tty_color_spec *fg, tty_color_spec *bg); 69 | void terminal_set_color(tty_color_spec fg, tty_color_spec bg); 70 | tty_color_spec ttycolor2spec(enum tty_color c); 71 | void terminal_set_font_attr(enum tty_font_attr); 72 | void set_cursor(size_t row, size_t col); 73 | void move_cursor(int row_delta, int col_delta); 74 | void get_cursor_position(size_t* row, size_t* col); 75 | 76 | void tty_stop_refresh(); 77 | void tty_start_refresh(); 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /kernel/include/kernel/types.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_TYPES_H 2 | #define _KERNEL_TYPES_H 3 | 4 | #include 5 | 6 | // for newlib 7 | #ifndef _SSIZE_T_DECLARED 8 | // for SmallerC libc 9 | #ifndef __SSIZE_T_DEF 10 | typedef int32_t ssize_t; 11 | #define _SSIZE_T_DECLARED 12 | #define __SSIZE_T_DEF 13 | #endif 14 | #endif 15 | 16 | #ifndef _SUSECONDS_T_DECLARED 17 | typedef int32_t suseconds_t; 18 | #define _SUSECONDS_T_DECLARED 19 | #endif 20 | 21 | #ifndef _SIZE_T_DECLARED 22 | #ifndef __SIZE_T_DEF 23 | typedef __SIZE_TYPE__ size_t; 24 | #define _SIZE_T_DECLARED 25 | #define __SIZE_T_DEF 26 | #endif 27 | #endif 28 | 29 | #ifndef _TIME_T_DECLARED 30 | #ifndef __TIME_T_DEF 31 | typedef int32_t time_t; 32 | #define _TIME_T_DECLARED 33 | #define __TIME_T_DEF 34 | #endif 35 | #endif 36 | 37 | 38 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/vbe.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_VBE_H 2 | #define _KERNEL_VBE_H 3 | 4 | #include 5 | 6 | // Structure returned by VBE BIOS function 0x4F00 7 | // Number after colon is offset into the struct 8 | typedef struct vbe_info_structure { 9 | char signature[4]; // must be "VESA" to indicate valid VBE support: 0 10 | uint16_t version; // VBE version; high byte is major version, low byte is minor version: 4 11 | uint32_t oem; // segment:offset pointer to OEM: 6 12 | uint32_t capabilities; // bitfield that describes card capabilities: 10 13 | uint32_t video_modes; // segment:offset pointer to list of supported video modes: 14 14 | uint16_t video_memory; // amount of video memory in 64KB blocks: 18 15 | uint16_t software_rev; // software revision: 20 16 | uint32_t vendor; // segment:offset to card vendor string: 22 17 | uint32_t product_name; // segment:offset to card model name: 26 18 | uint32_t product_rev; // segment:offset pointer to product revision: 30 19 | char reserved[222]; // reserved for future expansion: 34 20 | char oem_data[256]; // OEM BIOSes store their strings in this area: 256 21 | } __attribute__ ((packed)) vbe_info_structure; 22 | 23 | // Structure returned by VBE BIOS function 0x4F01 24 | // Number after colon is offset into the struct 25 | typedef struct vbe_mode_info_structure { 26 | uint16_t attributes; // deprecated, only bit 7 should be of interest to you, and it indicates the mode supports a linear frame buffer.: 0 27 | uint8_t window_a; // deprecated: 2 28 | uint8_t window_b; // deprecated: 3 29 | uint16_t granularity; // deprecatedused while calculating bank numbers: 4 30 | uint16_t window_size; // : 6 31 | uint16_t segment_a; // : 8 32 | uint16_t segment_b; // : 10 33 | uint32_t win_func_ptr; // deprecatedused to switch banks from protected mode without returning to real mode: 12 34 | uint16_t pitch; // number of bytes per horizontal line: 16 35 | uint16_t width; // width in pixels: 18 36 | uint16_t height; // height in pixels: 20 37 | uint8_t w_char; // unused...: 22 38 | uint8_t y_char; // ...: 23 39 | uint8_t planes; // : 24 40 | uint8_t bpp; // bits per pixel in this mode: 25 41 | uint8_t banks; // deprecatedtotal number of banks in this mode: 26 42 | uint8_t memory_model; // : 27 43 | uint8_t bank_size; // deprecatedsize of a bank, almost always 64 KB but may be 16 KB...: 28 44 | uint8_t image_pages; // : 29 45 | uint8_t reserved0; // : 30 46 | 47 | uint8_t red_mask; // : 31 48 | uint8_t red_position; // : 32 49 | uint8_t green_mask; // : 33 50 | uint8_t green_position; // : 34 51 | uint8_t blue_mask; // : 35 52 | uint8_t blue_position; // : 36 53 | uint8_t reserved_mask; // : 37 54 | uint8_t reserved_position; // : 38 55 | uint8_t direct_color_attributes; // : 39 56 | 57 | uint32_t framebuffer; // physical address of the linear frame bufferwrite here to draw to the screen: 40 58 | uint32_t off_screen_mem_off; // : 44 59 | uint16_t off_screen_mem_size; // size of memory in the framebuffer but not being displayed on the screen : 48 60 | uint8_t reserved1[206]; // : 50 61 | } __attribute__((packed)) vbe_mode_info_structure; 62 | 63 | 64 | 65 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/vfs.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_VFS_H 2 | #define _KERNEL_VFS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // maximum number of mount points 10 | #define N_MOUNT_POINT 16 11 | 12 | // maximum number of files opened 13 | #define N_FILE_STRUCTURE 64 14 | 15 | int fs_mount(const char* target, enum file_system_type file_system_type, 16 | fs_mount_option option, void* fs_option, fs_mount_point** mount_point); 17 | int fs_unmount(const char* mount_root); 18 | int fs_getattr(const char * path, struct fs_stat * stat, int file_idx); 19 | int fs_mknod(const char * path, uint mode); 20 | int fs_mkdir(const char * path, uint mode); 21 | int fs_rmdir(const char * path); 22 | int fs_link(const char* old_path, const char* new_path); 23 | int fs_unlink(const char * path); 24 | int fs_truncate(const char * path, uint size, int file_idx); 25 | int fs_rename(const char * from, const char* to, uint flags); 26 | int fs_readdir(const char * path, uint entry_offset, fs_dirent* buf, uint buf_size); 27 | int fs_open(const char * path, int flags); 28 | int fs_release(int file_idx); 29 | int fs_read(int file_idx, void *buf, uint size); 30 | int fs_seek(int file_idx, int offset, int whence); 31 | int fs_tell(int file_idx); 32 | int fs_write(int file_idx, void *buf, uint size); 33 | int fs_dupfile(int file_idx); 34 | 35 | int init_vfs(); 36 | 37 | #endif -------------------------------------------------------------------------------- /kernel/include/kernel/vga.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_VGA_H 2 | #define _KERNEL_VGA_H 3 | 4 | #include 5 | 6 | enum vga_color { 7 | VGA_COLOR_BLACK = 0, 8 | VGA_COLOR_BLUE = 1, 9 | VGA_COLOR_GREEN = 2, 10 | VGA_COLOR_CYAN = 3, 11 | VGA_COLOR_RED = 4, 12 | VGA_COLOR_MAGENTA = 5, 13 | VGA_COLOR_BROWN = 6, 14 | VGA_COLOR_LIGHT_GREY = 7, 15 | VGA_COLOR_DARK_GREY = 8, 16 | VGA_COLOR_LIGHT_BLUE = 9, 17 | VGA_COLOR_LIGHT_GREEN = 10, 18 | VGA_COLOR_LIGHT_CYAN = 11, 19 | VGA_COLOR_LIGHT_RED = 12, 20 | VGA_COLOR_LIGHT_MAGENTA = 13, 21 | VGA_COLOR_LIGHT_BROWN = 14, 22 | VGA_COLOR_WHITE = 15, 23 | }; 24 | 25 | static inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) { 26 | return fg | bg << 4; 27 | } 28 | 29 | static inline uint16_t vga_entry(unsigned char uc, uint8_t color) { 30 | return (uint16_t)uc | (uint16_t)color << 8; 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /kernel/include/kernel/video.h: -------------------------------------------------------------------------------- 1 | #ifndef _KERNEL_VIDEO_H 2 | #define _KERNEL_VIDEO_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define FONT_HEIGHT 16 10 | #define FONT_WIDTH 8 11 | 12 | void putpixel(uint32_t color, int x, int y); 13 | void fillrect(uint32_t color, int x, int y, int w, int h); 14 | void drawpic(uint32_t* buff, int x, int y, int w, int h); 15 | void screen_scroll_up(uint32_t row, uint32_t bgcolor); 16 | void clear_screen(uint32_t bgcolor); 17 | 18 | void video_refresh(); 19 | void video_refresh_rect(uint x, uint y, uint w, uint h); 20 | 21 | void drawchar(unsigned char c, int x, int y, uint32_t bgcolor, uint32_t fgcolor); 22 | 23 | void get_screen_size(uint32_t* w, uint32_t* h); 24 | void get_vga_font_size(uint32_t* w, uint32_t* h); 25 | 26 | int init_video(uint32_t info_phy_addr); 27 | 28 | 29 | #endif -------------------------------------------------------------------------------- /kernel/include/syscall.h: -------------------------------------------------------------------------------- 1 | #ifndef _SYSCALL_H 2 | #define _SYSCALL_H 3 | 4 | #include 5 | 6 | #define _syscall0(syscall_num, retval_type, name) \ 7 | retval_type name() \ 8 | {\ 9 | int ret_code; \ 10 | asm volatile ("push $0; int $88; pop %%ebx" \ 11 | :"=a"(ret_code) \ 12 | :"a"(syscall_num) \ 13 | :"ebx"); \ 14 | return (retval_type) ret_code; \ 15 | } 16 | 17 | #define _syscall1(syscall_num, retval_type, name, argtype1, arg1) \ 18 | retval_type name(argtype1 arg1) \ 19 | {\ 20 | int ret_code; \ 21 | asm volatile ("push %2; push $0; int $88; pop %%ebx; pop %%ebx" \ 22 | :"=a"(ret_code) \ 23 | :"a"(syscall_num), "r"((unsigned int) arg1) \ 24 | :"ebx"); \ 25 | return (retval_type) ret_code; \ 26 | } 27 | 28 | #define _syscall2(syscall_num, retval_type, name, argtype1, arg1, argtype2, arg2) \ 29 | retval_type name(argtype1 arg1, argtype2 arg2) \ 30 | {\ 31 | int ret_code; \ 32 | asm volatile ("push %2; push %3; push $0; int $88; pop %%ebx; pop %%ebx; pop %%ebx" \ 33 | :"=a"(ret_code) \ 34 | :"a"(syscall_num), "r"((unsigned int) arg2), "r"((unsigned int) arg1) \ 35 | :"ebx"); \ 36 | return (retval_type) ret_code; \ 37 | } 38 | 39 | #define _syscall3(syscall_num, retval_type, name, argtype1, arg1, argtype2, arg2, argtype3, arg3) \ 40 | retval_type name(argtype1 arg1, argtype2 arg2, argtype3 arg3) \ 41 | {\ 42 | int ret_code; \ 43 | asm volatile ("push %2; push %3; push %4; push $0; int $88; pop %%ebx; pop %%ebx; pop %%ebx; pop %%ebx" \ 44 | :"=a"(ret_code) \ 45 | :"a"(syscall_num), "r"((unsigned int) arg3), "r"((unsigned int) arg2), "r"((unsigned int) arg1) \ 46 | :"ebx"); \ 47 | return (retval_type) ret_code; \ 48 | } 49 | 50 | #define _syscall4(syscall_num, retval_type, name, argtype1, arg1, argtype2, arg2, argtype3, arg3, argtype4, arg4) \ 51 | retval_type name(argtype1 arg1, argtype2 arg2, argtype3 arg3, argtype4 arg4) \ 52 | {\ 53 | int ret_code; \ 54 | asm volatile ("push %2; push %3; push %4; push %5; push $0; int $88; pop %%ebx; pop %%ebx; pop %%ebx; pop %%ebx; pop %%ebx" \ 55 | :"=a"(ret_code) \ 56 | :"a"(syscall_num), "m"((unsigned int) arg4), "r"((unsigned int) arg3), "r"((unsigned int) arg2), "r"((unsigned int) arg1) \ 57 | :"ebx"); \ 58 | return (retval_type) ret_code; \ 59 | } 60 | 61 | #define _syscall5(syscall_num, retval_type, name, argtype1, arg1, argtype2, arg2, argtype3, arg3, argtype4, arg4, argtype5, arg5) \ 62 | retval_type name(argtype1 arg1, argtype2 arg2, argtype3 arg3, argtype4 arg4, argtype5 arg5) \ 63 | {\ 64 | int ret_code; \ 65 | asm volatile ("push %2; push %3; push %4; push %5; push %6; push $0; int $88; pop %%ebx; pop %%ebx; pop %%ebx; pop %%ebx; pop %%ebx; pop %%ebx" \ 66 | :"=a"(ret_code) \ 67 | :"a"(syscall_num), "m"((unsigned int) arg5), "m"((unsigned int) arg4), "r"((unsigned int) arg3), "r"((unsigned int) arg2), "r"((unsigned int) arg1) \ 68 | :"ebx"); \ 69 | return (retval_type) ret_code; \ 70 | } 71 | 72 | #define _syscall6(syscall_num, retval_type, name, argtype1, arg1, argtype2, arg2, argtype3, arg3, argtype4, arg4, argtype5, arg5, argtype6, arg6) \ 73 | retval_type name(argtype1 arg1, argtype2 arg2, argtype3 arg3, argtype4 arg4, argtype5 arg5, argtype6 arg6) \ 74 | {\ 75 | int ret_code; \ 76 | asm volatile ("push %2; push %3; push %4; push %5; push %6; push %7; push $0; int $88; pop %%ebx; pop %%ebx; pop %%ebx; pop %%ebx; pop %%ebx; pop %%ebx; pop %%ebx" \ 77 | :"=a"(ret_code) \ 78 | :"a"(syscall_num), "m"((unsigned int) arg6), "m"((unsigned int) arg5), "m"((unsigned int) arg4), "r"((unsigned int) arg3), "r"((unsigned int) arg2), "r"((unsigned int) arg1) \ 79 | :"ebx"); \ 80 | return (retval_type) ret_code; \ 81 | } 82 | 83 | #endif -------------------------------------------------------------------------------- /kernel/include/syscallnum.h: -------------------------------------------------------------------------------- 1 | #ifndef _SYSCALL_NUMBER_H 2 | #define _SYSCALL_NUMBER_H 3 | 4 | #define SYS_TEST 0 5 | #define SYS_EXEC 1 6 | #define SYS_PRINT 2 7 | #define SYS_YIELD 3 8 | #define SYS_FORK 4 9 | #define SYS_EXIT 5 10 | #define SYS_WAIT 6 11 | #define SYS_SBRK 7 12 | #define SYS_OPEN 8 13 | #define SYS_CLOSE 9 14 | #define SYS_READ 10 15 | #define SYS_WRITE 11 16 | #define SYS_SEEK 12 17 | #define SYS_DUP 13 18 | #define SYS_GETATTR_PATH 14 19 | #define SYS_GETATTR_FD 15 20 | #define SYS_GET_PID 16 21 | #define SYS_CURR_DATE_TIME 17 22 | #define SYS_UNLINK 18 23 | #define SYS_LINK 19 24 | #define SYS_RENAME 20 25 | #define SYS_READDIR 21 26 | #define SYS_CHDIR 22 27 | #define SYS_GETCWD 23 28 | #define SYS_TRUNCATE_PATH 24 29 | #define SYS_TRUNCATE_FD 25 30 | #define SYS_MKDIR 26 31 | #define SYS_RMDIR 27 32 | #define SYS_REFRESH_SCREEN 28 33 | #define SYS_DRAW_PICTURE 29 34 | 35 | #define SYS_NETWORK_RECEIVE_IPv4_PKT 30 36 | #define SYS_PREP_IPV4_PKT 31 37 | #define SYS_SEND_IPV4_PKT 32 38 | #define SYS_PREP_ICMP_PKT 33 39 | #define SYS_FINALIZE_ICMP_PKT 34 40 | 41 | #define SYS_SOCKET_OPEN 35 42 | #define SYS_SOCKET_SETOPT 36 43 | #define SYS_SOCKET_SENDTO 37 44 | #define SYS_SOCKET_RECVFROM 38 45 | 46 | #define SYS_CURR_TIME_EPOCH 70 47 | #define SYS_GET_FILE_OFFSET 80 48 | 49 | #define SYS_BRK 90 50 | 51 | #endif -------------------------------------------------------------------------------- /kernel/lock/lock.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // Ref: xv6/spinlock.c 7 | 8 | void acquire(yield_lock* lk) 9 | { 10 | // Assuming there is only one CPU, so disabling interrupt will be enough 11 | // to ensure atomicity of the check-and-flag code below 12 | // no need to use atomic assembly like xchg 13 | push_cli(); 14 | 15 | proc* p = curr_proc(); 16 | int pid = p?p->pid:0; 17 | 18 | while(lk->locked) { 19 | if(lk->holding_pid == pid) { 20 | PANIC("Deal Lock"); 21 | } 22 | 23 | yield(); 24 | } 25 | lk->locked = 1; 26 | lk->holding_pid = pid; 27 | 28 | 29 | // if in multi-core scenario, need to insert 30 | // __sync_synchronize() 31 | // here 32 | } 33 | 34 | void release(yield_lock* lk) 35 | { 36 | // Assuming there is only one CPU, so disabling interrupt will be enough 37 | // to ensure atomicity of the check-and-flag code below 38 | // no need to use atomic assembly like xchg 39 | PANIC_ASSERT(!is_interrupt_enabled()); 40 | if(!lk->locked) { 41 | PANIC("Releasing Non-holding lock"); 42 | } 43 | 44 | // if in multi-core scenario, need to insert 45 | // __sync_synchronize() 46 | // here 47 | lk->locked = 0; 48 | pop_cli(); 49 | } 50 | 51 | uint holding(yield_lock* lk) 52 | { 53 | push_cli(); 54 | uint r = lk->locked; 55 | pop_cli(); 56 | return r; 57 | } 58 | 59 | void start_writing(rw_lock* lk) 60 | { 61 | // push_cli(); 62 | acquire(&lk->lk); 63 | proc* p = curr_proc(); 64 | PANIC_ASSERT(p != NULL); 65 | while(lk->writing_pid || lk->reading) { 66 | if(lk->writing_pid == p->pid) { 67 | PANIC("RW Write Dead Lock"); 68 | } 69 | release(&lk->lk); 70 | yield(); 71 | acquire(&lk->lk); 72 | } 73 | lk->writing_pid = p->pid; 74 | release(&lk->lk); 75 | // pop_cli(); 76 | } 77 | 78 | void finish_writing(rw_lock* lk) 79 | { 80 | acquire(&lk->lk); 81 | PANIC_ASSERT(lk->writing_pid); 82 | lk->writing_pid = 0; 83 | release(&lk->lk); 84 | } 85 | 86 | void start_reading(rw_lock* lk) 87 | { 88 | acquire(&lk->lk); 89 | proc* p = curr_proc(); 90 | PANIC_ASSERT(p != NULL); 91 | while(lk->writing_pid && lk->writing_pid != p->pid) { 92 | release(&lk->lk); 93 | yield(); 94 | acquire(&lk->lk); 95 | } 96 | lk->reading++; 97 | release(&lk->lk); 98 | } 99 | 100 | void finish_reading(rw_lock* lk) 101 | { 102 | acquire(&lk->lk); 103 | PANIC_ASSERT(lk->reading > 0); 104 | lk->reading--; 105 | release(&lk->lk); 106 | } 107 | 108 | 109 | -------------------------------------------------------------------------------- /kernel/network/arp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | typedef struct arp_record { 14 | ip_addr ip; 15 | mac_addr mac; 16 | } arp_record; 17 | 18 | #define ARP_TRANSMIT_BUFF_LEN 64 19 | 20 | static struct { 21 | char transmit_buff[ARP_TRANSMIT_BUFF_LEN]; 22 | uint16_t transmit_hdr_len; 23 | yield_lock transmit_lk; 24 | 25 | arp_record* arp_cache; 26 | arp_record* next_free_record; 27 | uint cached_records; 28 | yield_lock arp_cache_lk; 29 | } arp; 30 | 31 | int init_arp() 32 | { 33 | // for announce and prob the ethernet header and parts of the arp packet are the same, 34 | // so prepare the transmit buffer when init 35 | arp_packet pl = { 36 | .htype = switch_endian16(ARP_HARDWARE_TYPE_ETHERNET), 37 | .ptype = switch_endian16(ARP_PROTOCOL_TYPE_IPv4), 38 | .hlen = ARP_HARDWARE_ADDR_LEN_ETHERNET, 39 | .plen = ARP_PROTOCAL_ADDR_LEN_IPv4, 40 | .opcode = switch_endian16(ARP_OP_CODE_REQUEST), 41 | .sha = rtl8139_mac() 42 | }; 43 | mac_addr dest_mac = {.addr = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}}; // broadcast 44 | eth_opt opt = (eth_opt) {.dest_mac = dest_mac, .type = ETHER_TYPE_ARP}; 45 | arp.transmit_hdr_len = eth_prep_pkt(&opt, arp.transmit_buff, ARP_TRANSMIT_BUFF_LEN); 46 | PANIC_ASSERT(arp.transmit_hdr_len > 0); 47 | memmove(arp.transmit_buff + arp.transmit_hdr_len, &pl, sizeof(pl)); 48 | 49 | arp.arp_cache = (arp_record*) malloc(sizeof(arp_record) * ARP_CACHE_N_RECORD); 50 | memset(arp.arp_cache, 0, sizeof(arp_record) * ARP_CACHE_N_RECORD); 51 | arp.next_free_record = arp.arp_cache; 52 | return 0; 53 | } 54 | 55 | int arp_announce_ip(ip_addr ip) 56 | { 57 | acquire(&arp.transmit_lk); 58 | arp_packet* pl = (arp_packet*) (arp.transmit_buff + arp.transmit_hdr_len); 59 | pl->spa = ip; 60 | pl->tpa = ip; 61 | int res = eth_send_pkt(arp.transmit_buff, arp.transmit_hdr_len + sizeof(arp_packet)); 62 | release(&arp.transmit_lk); 63 | return res; 64 | } 65 | 66 | int arp_probe(ip_addr ip) 67 | { 68 | acquire(&arp.transmit_lk); 69 | arp_packet* pl = (arp_packet*) (arp.transmit_buff + arp.transmit_hdr_len); 70 | pl->spa = (ip_addr) {0}; 71 | pl->tpa = ip; 72 | int res = eth_send_pkt(arp.transmit_buff, arp.transmit_hdr_len + sizeof(arp_packet)); 73 | release(&arp.transmit_lk); 74 | return res; 75 | } 76 | 77 | int arp_process_packet(void* buf, uint16_t len) 78 | { 79 | UNUSED_ARG(len); 80 | acquire(&arp.arp_cache_lk); 81 | PANIC_ASSERT(arp.arp_cache != NULL); 82 | 83 | arp_packet* pkt = (arp_packet*) buf; 84 | if(pkt->opcode == switch_endian16(ARP_OP_CODE_REPLY)) { 85 | // Record any ARP reply to cache 86 | arp.next_free_record->ip = pkt->spa; 87 | arp.next_free_record->mac = pkt->sha; 88 | arp.next_free_record++; 89 | if(arp.cached_records < ARP_CACHE_N_RECORD) { 90 | arp.cached_records++; 91 | } 92 | if(arp.next_free_record - arp.arp_cache == ARP_CACHE_N_RECORD) { 93 | // circular buffer 94 | arp.next_free_record = arp.arp_cache; 95 | } 96 | printf("ARP Cached: IP[%d.%d.%d.%d] = MAC[%x:%x:%x:%x:%x:%x]\n", 97 | pkt->spa.addr[0], 98 | pkt->spa.addr[1], 99 | pkt->spa.addr[2], 100 | pkt->spa.addr[3], 101 | pkt->sha.addr[0], 102 | pkt->sha.addr[1], 103 | pkt->sha.addr[2], 104 | pkt->sha.addr[3], 105 | pkt->sha.addr[4], 106 | pkt->sha.addr[5] 107 | ); 108 | } 109 | release(&arp.arp_cache_lk); 110 | return 0; 111 | } 112 | 113 | 114 | int arp_ip2mac(ip_addr ip, mac_addr* mac) 115 | { 116 | acquire(&arp.arp_cache_lk); 117 | for(uint i=0; i 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define CRC_POLY 0xEDB88320 15 | 16 | // Source: https://stackoverflow.com/questions/60684565/c-crc32-checksum-does-not-match-wireshark-on-ethernet-frame-check-sequence 17 | static uint32_t crc32(uint8_t *data, uint len) 18 | { 19 | uint i, j; 20 | uint32_t crc; 21 | 22 | if (!data) 23 | return 0; 24 | 25 | if (len < 1) 26 | return 0; 27 | 28 | crc = 0xFFFFFFFF; 29 | 30 | for (j = 0; j < len; j++) { 31 | crc ^= data[j]; 32 | 33 | for (i = 0; i < 8; i++) { 34 | crc = (crc & 1) ? ((crc >> 1) ^ CRC_POLY) : (crc >> 1); 35 | } 36 | } 37 | 38 | return (crc ^ 0xFFFFFFFF); 39 | } 40 | 41 | int init_ethernet() 42 | { 43 | return 0; 44 | } 45 | 46 | 47 | 48 | int eth_prep_pkt(eth_opt* opt, void* buf, uint16_t buf_len) { 49 | if(sizeof(eth_header) > buf_len) return -1; 50 | eth_header* header = (eth_header*) buf; 51 | *header = (eth_header) { 52 | .dest = opt->dest_mac, 53 | .src = rtl8139_mac(), 54 | .ethertype = switch_endian16(opt->type) 55 | }; 56 | return sizeof(eth_header); 57 | } 58 | 59 | int eth_send_pkt(void* buf, uint16_t pkt_len) { 60 | int res = rtl8139_send_packet(buf, pkt_len); 61 | return res; 62 | } 63 | 64 | 65 | int process_ethernet_packet(void* buf, uint16_t len, uint32_t crc) 66 | { 67 | eth_header* head = (eth_header*) buf; 68 | 69 | uint8_t* buff = (uint8_t*) buf; 70 | uint32_t our_crc = crc32(buff, len); 71 | char* eq = (crc == our_crc) ? "==":"!="; 72 | printf("Ethernet Pkt Received, CRC: Received[0x%x] %s Calculated[0x%x]:\n", crc, eq, our_crc); 73 | 74 | if(head->ethertype == switch_endian16(ETHER_TYPE_ARP)) { 75 | arp_process_packet(buf + sizeof(eth_header), len - sizeof(eth_header)); 76 | } else if(head->ethertype == switch_endian16(ETHER_TYPE_IPv4)) { 77 | ipv4_process_packet(buf + sizeof(eth_header), len - sizeof(eth_header)); 78 | } 79 | 80 | return 0; 81 | } -------------------------------------------------------------------------------- /kernel/network/icmp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int init_icmp() 10 | { 11 | return 0; 12 | } 13 | 14 | int icmp_prep_pkt(icmp_opt* opt, void* buf, uint16_t buf_len) { 15 | if(buf_len < sizeof(icmp_header)) { 16 | return -1; 17 | } 18 | icmp_header* hdr = (icmp_header*) buf; 19 | *hdr = (icmp_header) { 20 | .code = opt->code, 21 | .type = opt->type, 22 | }; 23 | if(opt->type == ICMP_TYPE_ECHO_REQUEST) { 24 | hdr->rest.un.echo.id = switch_endian16(opt->rest.un.echo.id); 25 | hdr->rest.un.echo.sequence = switch_endian16(opt->rest.un.echo.sequence); 26 | } else { 27 | return -1; 28 | } 29 | return sizeof(icmp_header); 30 | } 31 | 32 | // int icmp_send_pkt(int header_offset, void* buf, uint16_t pkt_len) { 33 | // if(header_offset < 0) return -1; 34 | // icmp_header* hdr = (icmp_header*) (buf + header_offset); 35 | // hdr->checksum = ipv4_icmp_checksum(hdr, pkt_len - header_offset); 36 | // int res = ipv4_send_pkt(buf, pkt_len); 37 | // return res; 38 | // } 39 | 40 | int icmp_finalize_pkt(icmp_header* hdr, uint16_t pkt_len) 41 | { 42 | hdr->checksum = ipv4_icmp_checksum(hdr, pkt_len); 43 | return 0; 44 | } 45 | 46 | // icmp_opt* new_icmp_opt() 47 | // { 48 | // icmp_opt* opt = malloc(sizeof(icmp_opt)); 49 | // memset(opt, 0, sizeof(icmp_opt)); 50 | // opt->ipv4.protocol = IPv4_PROTOCAL_ICMP; 51 | // return opt; 52 | // } -------------------------------------------------------------------------------- /kernel/network/network.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | int init_network() 8 | { 9 | init_ethernet(); 10 | init_arp(); 11 | init_ipv4(); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /kernel/panic/panic.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void panic(const char* file, uint32_t line, const char* panic_type, const char* desc) { 5 | // Manually triggered kernel panic 6 | asm volatile("cli"); // Disable interrupts. 7 | 8 | printf("%s(%s) at %s:%d\n", panic_type, desc, file, line); 9 | // Halt by going into an infinite loop. 10 | for (;;); 11 | } -------------------------------------------------------------------------------- /libc/.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.d 3 | *.o 4 | -------------------------------------------------------------------------------- /libc/Makefile: -------------------------------------------------------------------------------- 1 | DEFAULT_HOST!=../default-host.sh 2 | HOST?=$(DEFAULT_HOST) 3 | HOSTARCH!=../target-triplet-to-arch.sh $(HOST) 4 | 5 | CFLAGS?=-O2 -g 6 | CPPFLAGS?= 7 | LDFLAGS?= 8 | LIBS?= 9 | 10 | DESTDIR?= 11 | PREFIX?=/usr 12 | INCLUDEDIR?=$(PREFIX)/include 13 | LIBDIR?=$(PREFIX)/lib 14 | 15 | CFLAGS:=$(CFLAGS) -ffreestanding -Wall -Wextra 16 | CPPFLAGS:=$(CPPFLAGS) -D__is_libc -Iinclude 17 | LIBK_CFLAGS:=$(CFLAGS) 18 | LIBK_CPPFLAGS:=$(CPPFLAGS) -D__is_libk 19 | 20 | ARCHDIR=arch/$(HOSTARCH) 21 | 22 | include $(ARCHDIR)/make.config 23 | 24 | CFLAGS:=$(CFLAGS) $(ARCH_CFLAGS) 25 | CPPFLAGS:=$(CPPFLAGS) $(ARCH_CPPFLAGS) 26 | LIBK_CFLAGS:=$(LIBK_CFLAGS) $(KERNEL_ARCH_CFLAGS) 27 | LIBK_CPPFLAGS:=$(LIBK_CPPFLAGS) $(KERNEL_ARCH_CPPFLAGS) 28 | 29 | FREEOBJS=\ 30 | $(ARCH_FREEOBJS) \ 31 | stdio/printf.o \ 32 | stdio/putchar.o \ 33 | stdio/puts.o \ 34 | stdlib/abort.o \ 35 | stdlib/exit.o \ 36 | stdlib/malloc.o \ 37 | string/memcmp.o \ 38 | string/memcpy.o \ 39 | string/memmove.o \ 40 | string/memset.o \ 41 | string/strlen.o \ 42 | string/strcmp.o \ 43 | string/strcpy.o \ 44 | string/strdup.o \ 45 | assert/assert.o \ 46 | stdlib/getenv.o \ 47 | time/time.o \ 48 | stdio/fileio.o \ 49 | 50 | HOSTEDOBJS=\ 51 | $(ARCH_HOSTEDOBJS) \ 52 | 53 | OBJS=\ 54 | $(FREEOBJS) \ 55 | $(HOSTEDOBJS) \ 56 | 57 | LIBK_OBJS=$(FREEOBJS:.o=.libk.o) 58 | 59 | BINARIES=libu.a libk.a 60 | # BINARIES=libk.a 61 | 62 | .PHONY: all clean install install-headers install-libs 63 | .SUFFIXES: .o .libk.o .c .S 64 | 65 | all: $(BINARIES) 66 | 67 | libu.a: $(OBJS) 68 | $(AR) rcs $@ $(OBJS) 69 | 70 | libk.a: $(LIBK_OBJS) 71 | $(AR) rcs $@ $(LIBK_OBJS) 72 | 73 | .c.o: 74 | $(CC) -MD -c $< -o $@ -std=gnu11 $(CFLAGS) $(CPPFLAGS) 75 | 76 | .S.o: 77 | $(CC) -MD -c $< -o $@ $(CFLAGS) $(CPPFLAGS) 78 | 79 | .c.libk.o: 80 | $(CC) -MD -c $< -o $@ -std=gnu11 $(LIBK_CFLAGS) $(LIBK_CPPFLAGS) 81 | 82 | .S.libk.o: 83 | $(CC) -MD -c $< -o $@ $(LIBK_CFLAGS) $(LIBK_CPPFLAGS) 84 | 85 | clean: 86 | rm -f $(BINARIES) *.a 87 | rm -f $(OBJS) $(LIBK_OBJS) *.o */*.o */*/*.o 88 | rm -f $(OBJS:.o=.d) $(LIBK_OBJS:.o=.d) *.d */*.d */*/*.d 89 | 90 | install: install-headers install-libs 91 | 92 | install-headers: 93 | mkdir -p $(DESTDIR)$(INCLUDEDIR) 94 | cp -Rp include/. $(DESTDIR)$(INCLUDEDIR)/. 95 | 96 | install-libs: $(BINARIES) 97 | mkdir -p $(DESTDIR)$(LIBDIR) 98 | cp -p $(BINARIES) $(DESTDIR)$(LIBDIR) 99 | 100 | -include $(OBJS:.o=.d) 101 | -include $(LIBK_OBJS:.o=.d) 102 | -------------------------------------------------------------------------------- /libc/arch/i386/crt/crt0.S: -------------------------------------------------------------------------------- 1 | .section .text 2 | 3 | .global _start 4 | _start: 5 | # Pop fake return address pushed by our own process loader 6 | popl %eax 7 | 8 | # Run the global constructors. 9 | call _init 10 | 11 | # Run main 12 | xor %eax, %eax 13 | call main 14 | 15 | # Terminate the process with the exit code. 16 | pushl %eax 17 | call exit 18 | .size _start, . - _start -------------------------------------------------------------------------------- /libc/arch/i386/make.config: -------------------------------------------------------------------------------- 1 | ARCH_CFLAGS= 2 | ARCH_CPPFLAGS= 3 | KERNEL_ARCH_CFLAGS= 4 | KERNEL_ARCH_CPPFLAGS= 5 | 6 | ARCH_FREEOBJS=\ 7 | $(ARCHDIR)/crt/crt0.o \ 8 | 9 | ARCH_HOSTEDOBJS=\ 10 | -------------------------------------------------------------------------------- /libc/assert/assert.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void user_panic(const char* file, uint32_t line, const char* panic_type, const char* desc) { 5 | printf("%s(%s) at %s:%d\n", panic_type, desc, file, line); 6 | // Halt by going into an infinite loop. 7 | for (;;); 8 | } -------------------------------------------------------------------------------- /libc/include/assert.h: -------------------------------------------------------------------------------- 1 | #ifndef _ASSERT_H 2 | #define _ASSERT_H 1 3 | 4 | #if defined(__is_libk) 5 | #include 6 | #define assert(condition) PANIC_ASSERT(condition) 7 | #else 8 | #define assert(condition) ((condition) ? (void)0 : user_panic(__FILE__, __LINE__, "ASSERTION-FAILED", #condition)) 9 | #endif 10 | 11 | #endif -------------------------------------------------------------------------------- /libc/include/stdio.h: -------------------------------------------------------------------------------- 1 | #ifndef _STDIO_H 2 | #define _STDIO_H 1 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define EOF (-1) 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | int printf(const char* __restrict format, ...); 15 | int kprintf(const char* __restrict format, ...); 16 | int snprintf(char* buf, size_t size, const char* __restrict format, ...); 17 | int putchar(int ic); 18 | int serial_putchar(int ic); 19 | int puts(const char* string); 20 | 21 | typedef void FILE; // we are using file descriptor as FILE* 22 | FILE* fopen(const char * pathname, const char * mode); 23 | int fclose(FILE *stream); 24 | size_t fread(void * ptr, size_t size, size_t nitems, 25 | FILE * stream); 26 | size_t fwrite(const void * ptr, size_t size, size_t nitems, 27 | FILE * stream); 28 | int fseek(FILE *stream, long offset, int whence); 29 | long ftell(FILE *stream); 30 | ssize_t write(int fildes, const void *buf, size_t nbyte); 31 | 32 | #ifdef __cplusplus 33 | } 34 | #endif 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /libc/include/stdlib.h: -------------------------------------------------------------------------------- 1 | #ifndef _STDLIB_H 2 | #define _STDLIB_H 1 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | __attribute__((__noreturn__)) 11 | void abort(void); 12 | 13 | void free(void *ap); 14 | void* malloc(unsigned int nbytes); 15 | char *getenv(const char *name); 16 | 17 | #ifdef __cplusplus 18 | } 19 | #endif 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /libc/include/string.h: -------------------------------------------------------------------------------- 1 | #ifndef _STRING_H 2 | #define _STRING_H 1 3 | 4 | #include 5 | 6 | #include 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | int memcmp(const void*, const void*, size_t); 13 | void* memcpy(void* __restrict, const void* __restrict, size_t); 14 | void* memmove(void*, const void*, size_t); 15 | void* memset(void*, int, size_t); 16 | size_t strlen(const char*); 17 | int strcmp(const char *p, const char *q); 18 | char* strcpy(char *s, const char *t); 19 | char* strdup(const char* s); 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /libc/include/sys/cdefs.h: -------------------------------------------------------------------------------- 1 | #ifndef _SYS_CDEFS_H 2 | #define _SYS_CDEFS_H 1 3 | 4 | #define __simple_os_libc 1 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /libc/include/sys/time.h: -------------------------------------------------------------------------------- 1 | #ifndef _SYS_TIME_H 2 | #define _SYS_TIME_H 1 3 | 4 | #include 5 | 6 | struct timeval { 7 | time_t tv_sec; /* seconds */ 8 | suseconds_t tv_usec; /* microseconds */ 9 | }; 10 | 11 | struct timezone { 12 | int tz_minuteswest; /* minutes west of Greenwich */ 13 | int tz_dsttime; /* type of DST correction */ 14 | }; 15 | 16 | int gettimeofday(struct timeval * tv, struct timezone * tz); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /libc/include/sys/types.h: -------------------------------------------------------------------------------- 1 | #ifndef _SYS_TYPES_H 2 | #define _SYS_TYPES_H 1 3 | 4 | #include 5 | 6 | #ifndef _SSIZE_T_DECLARED 7 | typedef int32_t ssize_t; 8 | #define _SSIZE_T_DECLARED 9 | #endif 10 | 11 | #ifndef _SUSECONDS_T_DECLARED 12 | typedef int32_t suseconds_t; 13 | #define _SUSECONDS_T_DECLARED 14 | #endif 15 | 16 | #ifndef _SIZE_T_DECLARED 17 | typedef __SIZE_TYPE__ size_t; 18 | #define _SIZE_T_DECLARED 19 | #endif 20 | 21 | #ifndef _TIME_T_DECLARED 22 | typedef int64_t time_t; 23 | #define _TIME_T_DECLARED 24 | #endif 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /libc/include/time.h: -------------------------------------------------------------------------------- 1 | #ifndef _TIME_H 2 | #define _TIME_H 1 3 | 4 | #include 5 | 6 | 7 | time_t time(time_t *); 8 | 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /libc/stdio/fileio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | static inline _syscall3(SYS_READ, int, sys_read, int, fd, void*, buf, uint, size) 9 | static inline _syscall3(SYS_WRITE, int, sys_write, int, fd, const void*, buf, uint, size) 10 | static inline _syscall2(SYS_OPEN, int, sys_open, const char*, path, int, flags) 11 | static inline _syscall1(SYS_CLOSE, int, sys_close, int, fd) 12 | static inline _syscall3(SYS_SEEK, int, sys_seek, int, fd, int, offset, int, whence) 13 | static inline _syscall1(SYS_GET_FILE_OFFSET, int, sys_get_file_offset, int, fd) 14 | 15 | FILE* fopen(const char *pathname, const char *mode) 16 | { 17 | // printf("fopen(%s, %s)\n", pathname, mode); 18 | int r = -1; 19 | if(strcmp(mode, "r") == 0) { 20 | r = sys_open(pathname, O_RDONLY); 21 | } else if(strcmp(mode, "w") == 0){ 22 | r = sys_open(pathname, O_WRONLY|O_CREAT|O_TRUNC); 23 | } 24 | if(r < 0) { 25 | return NULL; 26 | } else { 27 | return (FILE*) r; 28 | } 29 | } 30 | int fclose(FILE *stream) 31 | { 32 | // printf("fclose(%u)\n", stream); 33 | return sys_close((int)stream); 34 | } 35 | size_t fread(void * ptr, size_t size, size_t nitems, FILE * stream) 36 | { 37 | // printf("fread(%u, %u, %u, %u)\n", ptr, size, nitems, stream); 38 | return sys_read((int)stream, ptr, size*nitems); 39 | } 40 | size_t fwrite(const void * ptr, size_t size, size_t nitems, FILE * stream) 41 | { 42 | // printf("fwrite(%u, %u, %u, %u)\n", ptr, size, nitems, stream); 43 | return sys_write((int)stream, ptr, size*nitems); 44 | } 45 | int fseek(FILE *stream, long offset, int whence) 46 | { 47 | // printf("fseek(%u, %d, %d)\n", stream, offset, whence); 48 | return sys_seek((int)stream, offset, whence); 49 | } 50 | long ftell(FILE *stream) 51 | { 52 | // printf("ftell(%u)\n", stream); 53 | return sys_get_file_offset((int)stream); 54 | } 55 | ssize_t write(int fildes, const void *buf, size_t nbyte) 56 | { 57 | // if(fildes != 0 && fildes != 1) { 58 | // printf("write(%d, %u, %u)\n", fildes, buf, nbyte); 59 | // } 60 | return sys_write(fildes, buf, nbyte); 61 | } 62 | -------------------------------------------------------------------------------- /libc/stdio/putchar.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #if defined(__is_libk) 4 | #include 5 | #include 6 | #else 7 | #include 8 | #include 9 | static inline _syscall3(SYS_WRITE, int, sys_write, int, fd, const void*, buf, uint, size) 10 | #endif 11 | 12 | int putchar(int ic) { 13 | #if defined(__is_libk) 14 | terminal_putchar((char) ic); 15 | #else 16 | char c = (char) ic; 17 | sys_write(0, &c, 1); 18 | #endif 19 | return ic; 20 | } 21 | 22 | int serial_putchar(int ic) { 23 | #if defined(__is_libk) 24 | if (is_serial_port_initialized()) { 25 | write_serial((char) ic); 26 | } 27 | #else 28 | char c = (char) ic; 29 | sys_write(0, &c, 1); 30 | #endif 31 | return ic; 32 | } 33 | -------------------------------------------------------------------------------- /libc/stdio/puts.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int puts(const char* string) { 4 | return printf("%s\n", string); 5 | } 6 | -------------------------------------------------------------------------------- /libc/stdlib/abort.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #if defined(__is_libk) 5 | #include 6 | #else 7 | #include 8 | static inline _syscall1(SYS_EXIT, int, sys_exit, int, exit_code) 9 | #endif 10 | 11 | __attribute__((__noreturn__)) 12 | void abort(void) { 13 | #if defined(__is_libk) 14 | PANIC("Kernel abort()"); 15 | #else 16 | // TODO: Abnormally terminate the process as if by SIGABRT. 17 | sys_exit(-1); 18 | #endif 19 | while (1) { } 20 | __builtin_unreachable(); 21 | } 22 | -------------------------------------------------------------------------------- /libc/stdlib/exit.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #if defined(__is_libk) 4 | #include 5 | #else 6 | #include 7 | // #include 8 | static inline _syscall1(SYS_EXIT, int, sys_exit, int, exit_code) 9 | #endif 10 | 11 | __attribute__((__noreturn__)) 12 | void exit(int exit_code) { 13 | #if defined(__is_libk) 14 | (void) exit_code; // suppress unused arg warning 15 | PANIC("Kernel abort()"); 16 | #else 17 | // printf("exit(%d)\n", exit_code); 18 | sys_exit(exit_code); 19 | #endif 20 | while (1) { } 21 | __builtin_unreachable(); 22 | } 23 | -------------------------------------------------------------------------------- /libc/stdlib/getenv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | char *getenv(const char *name) 5 | { 6 | // printf("getenv(%s)\n", name); 7 | (void) name; 8 | // We don't have environment variable mechanism yet 9 | return 0; // NULL 10 | } 11 | -------------------------------------------------------------------------------- /libc/stdlib/malloc.c: -------------------------------------------------------------------------------- 1 | 2 | // Sourced from xv6/umalloc.c 3 | 4 | // Memory allocator by Kernighan and Ritchie, 5 | // The C programming Language, 2nd ed. Section 8.7. 6 | 7 | #if defined(__is_libk) 8 | 9 | #include 10 | 11 | void free(void *ap) { 12 | kfree(ap); 13 | } 14 | 15 | void* malloc(uint32_t size) { 16 | return kmalloc(size); 17 | } 18 | 19 | #else 20 | 21 | #include 22 | #include 23 | // #include 24 | 25 | static inline _syscall1(SYS_SBRK, void*, sbrk, int, size_delta) 26 | 27 | typedef unsigned int uint; 28 | typedef unsigned short ushort; 29 | typedef unsigned char uchar; 30 | 31 | typedef long Align; 32 | 33 | union header { 34 | struct { 35 | union header *ptr; 36 | uint size; 37 | } s; 38 | Align x; 39 | }; 40 | 41 | typedef union header Header; 42 | 43 | static Header base; 44 | static Header *freep; 45 | 46 | 47 | void 48 | free(void *ap) 49 | { 50 | // printf("free(%u)\n", ap); 51 | if(ap == NULL) return; 52 | 53 | Header *bp, *p; 54 | 55 | bp = (Header*)ap - 1; 56 | for(p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr) 57 | if(p >= p->s.ptr && (bp > p || bp < p->s.ptr)) 58 | break; 59 | if(bp + bp->s.size == p->s.ptr){ 60 | bp->s.size += p->s.ptr->s.size; 61 | bp->s.ptr = p->s.ptr->s.ptr; 62 | } else 63 | bp->s.ptr = p->s.ptr; 64 | if(p + p->s.size == bp){ 65 | p->s.size += bp->s.size; 66 | p->s.ptr = bp->s.ptr; 67 | } else 68 | p->s.ptr = bp; 69 | freep = p; 70 | } 71 | 72 | static Header* 73 | morecore(uint nu) 74 | { 75 | char *p; 76 | Header *hp; 77 | 78 | if(nu < 4096) 79 | nu = 4096; 80 | p = sbrk(nu * sizeof(Header)); 81 | if(p == (char*)-1) 82 | return 0; 83 | hp = (Header*)p; 84 | hp->s.size = nu; 85 | free((void*)(hp + 1)); 86 | return freep; 87 | } 88 | 89 | void* 90 | malloc(uint nbytes) 91 | { 92 | // printf("malloc(%u)\n", nbytes); 93 | Header *p, *prevp; 94 | uint nunits; 95 | 96 | nunits = (nbytes + sizeof(Header) - 1)/sizeof(Header) + 1; 97 | if((prevp = freep) == 0){ 98 | base.s.ptr = freep = prevp = &base; 99 | base.s.size = 0; 100 | } 101 | for(p = prevp->s.ptr; ; prevp = p, p = p->s.ptr){ 102 | if(p->s.size >= nunits){ 103 | if(p->s.size == nunits) 104 | prevp->s.ptr = p->s.ptr; 105 | else { 106 | p->s.size -= nunits; 107 | p += p->s.size; 108 | p->s.size = nunits; 109 | } 110 | freep = prevp; 111 | return (void*)(p + 1); 112 | } 113 | if(p == freep) 114 | if((p = morecore(nunits)) == 0) 115 | return 0; 116 | } 117 | } 118 | 119 | #endif -------------------------------------------------------------------------------- /libc/string/memcmp.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int memcmp(const void* aptr, const void* bptr, size_t size) { 4 | const unsigned char* a = (const unsigned char*) aptr; 5 | const unsigned char* b = (const unsigned char*) bptr; 6 | for (size_t i = 0; i < size; i++) { 7 | if (a[i] < b[i]) 8 | return -1; 9 | else if (b[i] < a[i]) 10 | return 1; 11 | } 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /libc/string/memcpy.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Copied from newlib/libc/string/memcpy.c 4 | 5 | /* Nonzero if either X or Y is not aligned on a "long" boundary. */ 6 | #define UNALIGNED(X, Y) \ 7 | (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) 8 | 9 | /* How many bytes are copied each iteration of the 4X unrolled loop. */ 10 | #define BIGBLOCKSIZE (sizeof (long) << 2) 11 | 12 | /* How many bytes are copied each iteration of the word copy loop. */ 13 | #define LITTLEBLOCKSIZE (sizeof (long)) 14 | 15 | /* Threshhold for punting to the byte copier. */ 16 | #define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE) 17 | 18 | 19 | 20 | // Original naive memcpy implementation 21 | // void* memcpy(void* restrict dstptr, const void* restrict srcptr, size_t size) { 22 | // unsigned char* dst = (unsigned char*) dstptr; 23 | // const unsigned char* src = (const unsigned char*) srcptr; 24 | // for (size_t i = 0; i < size; i++) 25 | // dst[i] = src[i]; 26 | // return dstptr; 27 | // } 28 | 29 | // Optimized memcpy from Newlib 30 | void* memcpy(void* restrict dst0, const void* restrict src0, size_t len0) 31 | { 32 | char *dst = dst0; 33 | const char *src = src0; 34 | long *aligned_dst; 35 | const long *aligned_src; 36 | 37 | /* If the size is small, or either SRC or DST is unaligned, 38 | then punt into the byte copy loop. This should be rare. */ 39 | if (!TOO_SMALL(len0) && !UNALIGNED (src, dst)) 40 | { 41 | aligned_dst = (long*)dst; 42 | aligned_src = (long*)src; 43 | 44 | /* Copy 4X long words at a time if possible. */ 45 | while (len0 >= BIGBLOCKSIZE) 46 | { 47 | *aligned_dst++ = *aligned_src++; 48 | *aligned_dst++ = *aligned_src++; 49 | *aligned_dst++ = *aligned_src++; 50 | *aligned_dst++ = *aligned_src++; 51 | len0 -= BIGBLOCKSIZE; 52 | } 53 | 54 | /* Copy one long word at a time if possible. */ 55 | while (len0 >= LITTLEBLOCKSIZE) 56 | { 57 | *aligned_dst++ = *aligned_src++; 58 | len0 -= LITTLEBLOCKSIZE; 59 | } 60 | 61 | /* Pick up any residual with a byte copier. */ 62 | dst = (char*)aligned_dst; 63 | src = (char*)aligned_src; 64 | } 65 | 66 | while (len0--) 67 | *dst++ = *src++; 68 | 69 | return dst0; 70 | } -------------------------------------------------------------------------------- /libc/string/memmove.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Copied from newlib/libc/string/memmove.c 4 | 5 | /* Nonzero if either X or Y is not aligned on a "long" boundary. */ 6 | #define UNALIGNED(X, Y) \ 7 | (((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1))) 8 | 9 | /* How many bytes are copied each iteration of the 4X unrolled loop. */ 10 | #define BIGBLOCKSIZE (sizeof (long) << 2) 11 | 12 | /* How many bytes are copied each iteration of the word copy loop. */ 13 | #define LITTLEBLOCKSIZE (sizeof (long)) 14 | 15 | /* Threshhold for punting to the byte copier. */ 16 | #define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE) 17 | 18 | 19 | // Original naive memmove implementation 20 | // void* memmove(void* dstptr, const void* srcptr, size_t size) { 21 | // unsigned char* dst = (unsigned char*) dstptr; 22 | // const unsigned char* src = (const unsigned char*) srcptr; 23 | // if (dst < src) { 24 | // for (size_t i = 0; i < size; i++) 25 | // dst[i] = src[i]; 26 | // } else { 27 | // for (size_t i = size; i != 0; i--) 28 | // dst[i-1] = src[i-1]; 29 | // } 30 | // return dstptr; 31 | // } 32 | 33 | // Optimized memcpy from Newlib 34 | void* memmove(void* dst_void, const void* src_void, size_t length) { 35 | if(dst_void == src_void) return dst_void; 36 | 37 | char* dst = dst_void; 38 | const char* src = src_void; 39 | long* aligned_dst; 40 | const long* aligned_src; 41 | 42 | if (src < dst && dst < src + length) { 43 | /* Destructive overlap...have to copy backwards */ 44 | src += length; 45 | dst += length; 46 | while (length--) { 47 | *--dst = *--src; 48 | } 49 | } else { 50 | /* Use optimizing algorithm for a non-destructive copy to closely 51 | match memcpy. If the size is small or either SRC or DST is unaligned, 52 | then punt into the byte copy loop. This should be rare. */ 53 | if (!TOO_SMALL(length) && !UNALIGNED(src, dst)) { 54 | aligned_dst = (long*)dst; 55 | aligned_src = (long*)src; 56 | 57 | /* Copy 4X long words at a time if possible. */ 58 | while (length >= BIGBLOCKSIZE) { 59 | *aligned_dst++ = *aligned_src++; 60 | *aligned_dst++ = *aligned_src++; 61 | *aligned_dst++ = *aligned_src++; 62 | *aligned_dst++ = *aligned_src++; 63 | length -= BIGBLOCKSIZE; 64 | } 65 | 66 | /* Copy one long word at a time if possible. */ 67 | while (length >= LITTLEBLOCKSIZE) { 68 | *aligned_dst++ = *aligned_src++; 69 | length -= LITTLEBLOCKSIZE; 70 | } 71 | 72 | /* Pick up any residual with a byte copier. */ 73 | dst = (char*)aligned_dst; 74 | src = (char*)aligned_src; 75 | } 76 | 77 | while (length--) { 78 | *dst++ = *src++; 79 | } 80 | } 81 | 82 | return dst_void; 83 | } 84 | -------------------------------------------------------------------------------- /libc/string/memset.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void* memset(void* bufptr, int value, size_t size) { 4 | unsigned char* buf = (unsigned char*) bufptr; 5 | for (size_t i = 0; i < size; i++) 6 | buf[i] = (unsigned char) value; 7 | return bufptr; 8 | } 9 | -------------------------------------------------------------------------------- /libc/string/strcmp.c: -------------------------------------------------------------------------------- 1 | // from xv6/ulib.c 2 | int strcmp(const char *p, const char *q) 3 | { 4 | while(*p && *p == *q) 5 | p++, q++; 6 | return (unsigned char)*p - (unsigned char)*q; 7 | } -------------------------------------------------------------------------------- /libc/string/strcpy.c: -------------------------------------------------------------------------------- 1 | // from xv6/ulib.c 2 | char* strcpy(char *s, const char *t) 3 | { 4 | char *os; 5 | 6 | os = s; 7 | while((*s++ = *t++) != 0) 8 | ; 9 | return os; 10 | } -------------------------------------------------------------------------------- /libc/string/strdup.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | char* strdup(const char* s) 5 | { 6 | char* s_dup = malloc(strlen(s)+1); 7 | strcpy(s_dup, s); 8 | return s_dup; 9 | } -------------------------------------------------------------------------------- /libc/string/strlen.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | size_t strlen(const char* str) { 4 | size_t len = 0; 5 | while (str[len]) 6 | len++; 7 | return len; 8 | } 9 | -------------------------------------------------------------------------------- /libc/time/time.c: -------------------------------------------------------------------------------- 1 | // #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | static inline _syscall0(SYS_CURR_TIME_EPOCH, time_t, sys_curr_time_epoch) 9 | 10 | // Get current clock time 11 | int gettimeofday(struct timeval* tp, struct timezone * tz) 12 | { 13 | // printf("gettimeofday(%u,%u)\n", tp, tz); 14 | (void) tz; 15 | tp->tv_sec = sys_curr_time_epoch(); 16 | tp->tv_usec = 0; 17 | return 0; 18 | } 19 | 20 | time_t time(time_t *t) 21 | { 22 | // printf("time(%u)\n", t); 23 | time_t curr = sys_curr_time_epoch(); 24 | if(t) { 25 | *t = curr; 26 | } 27 | return curr; 28 | } -------------------------------------------------------------------------------- /qemu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | . ./config.sh 5 | 6 | if [ "$1" = "debug" ]; then 7 | DEBUG_FLAG="-s -S" 8 | else 9 | DEBUG_FLAG="" 10 | fi 11 | 12 | if [ -f testfs.fat ]; then 13 | HDB="-hdb testfs.fat" 14 | else 15 | HDB="" 16 | fi 17 | 18 | # To use user mode network: 19 | NET_ARG="-nic user,model=rtl8139,mac=52:54:98:76:54:32" 20 | # To use tap network (see setup_tap.sh and cleanup_tap.sh): 21 | # NET_ARG="-netdev tap,id=mynet0,ifname=tap0,script=no,downscript=no -device rtl8139,netdev=mynet0,mac=52:54:98:76:54:32" 22 | 23 | if grep -q Microsoft /proc/version; then 24 | echo "Windows Subsystem for Linux" 25 | qemu-system-$(./target-triplet-to-arch.sh $HOST).exe ${DEBUG_FLAG} -hda bootable_kernel.bin ${HDB} -serial file:serial_port_output.txt ${NET_ARG} 26 | else 27 | echo "Native Linux" 28 | qemu-system-$(./target-triplet-to-arch.sh $HOST) ${DEBUG_FLAG} -hda bootable_kernel.bin ${HDB} -serial file:serial_port_output.txt ${NET_ARG} 29 | fi 30 | -------------------------------------------------------------------------------- /rebuild_newlib.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export SIMPLE_OS_SRC=$(readlink -f $(dirname "$0")) 4 | export TOOL_CHAIN_BUILD_DIR=$SIMPLE_OS_SRC/build-toolchain 5 | export TOOL_CHAIN_ROOT=$SIMPLE_OS_SRC/toolchain 6 | export PATH=$TOOL_CHAIN_ROOT/usr/bin:$PATH 7 | 8 | ################################################################# 9 | ### Rebuild Newlib for any change in the simple-newlib project 10 | ################################################################# 11 | 12 | cd $SIMPLE_OS_SRC 13 | 14 | # Install system headers 15 | ./headers.sh 16 | 17 | cd $TOOL_CHAIN_BUILD_DIR/simple-newlib 18 | git pull 19 | cd $TOOL_CHAIN_BUILD_DIR/build-newlib 20 | rm -rf $TOOL_CHAIN_BUILD_DIR/build-newlib/* 21 | ../simple-newlib/configure --prefix=/usr --target=i686-simpleos 22 | make -j4 all 23 | 24 | make DESTDIR="$TOOL_CHAIN_ROOT" install 25 | cp -ar $TOOL_CHAIN_ROOT/usr/i686-simpleos/lib $TOOL_CHAIN_ROOT/usr/ 26 | cp -ar $TOOL_CHAIN_ROOT/usr/i686-simpleos/include $TOOL_CHAIN_ROOT/usr/ 27 | 28 | -------------------------------------------------------------------------------- /setup_tap.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Setup network bridge br0 and TAP device tap0 for QEMU 4 | # Ref: https://www.linux-kvm.org/page/Networking 5 | # Ref: https://blog.stefan-koch.name/2020/10/25/qemu-public-ip-vm-with-tap 6 | # Ref: https://brezular.com/2011/06/19/bridging-qemu-image-to-the-real-network-using-tap-interface/ 7 | # Ref: https://gist.github.com/extremecoders-re/e8fd8a67a515fee0c873dcafc81d811c 8 | # Ref: https://wiki.qemu.org/Documentation/Networking 9 | 10 | BRIDGE=br0 11 | NET_INTERFACE=enp0s3 12 | TAP=tap0 13 | 14 | sudo ip link add $BRIDGE type bridge 15 | sudo ip link set $BRIDGE up 16 | 17 | sudo ip link set $NET_INTERFACE up 18 | sudo ip addr flush dev $NET_INTERFACE 19 | sudo ip link set $NET_INTERFACE master $BRIDGE 20 | sudo dhclient -v $BRIDGE 21 | 22 | sudo ip tuntap add dev $TAP mode tap user `whoami` 23 | sudo ip link set dev $TAP up 24 | sudo ip link set $TAP master $BRIDGE 25 | 26 | -------------------------------------------------------------------------------- /target-triplet-to-arch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if echo "$1" | grep -Eq 'i[[:digit:]]86-'; then 3 | echo i386 4 | else 5 | echo "$1" | grep -Eo '^[[:alnum:]_]*' 6 | fi 7 | --------------------------------------------------------------------------------