├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── Vagrantfile ├── api └── radium.h ├── boot └── grub │ └── menu.lst ├── hdd.base.img.gz ├── kernel ├── Makefile ├── inc │ ├── console.h │ ├── gdt.h │ ├── idt.h │ ├── io.h │ ├── kernel_page.h │ ├── multiboot.h │ ├── paging.h │ ├── panic.h │ ├── pit.h │ ├── sched.h │ ├── stdarg.h │ ├── string.h │ ├── syscall.h │ ├── task.h │ ├── types.h │ └── util.h ├── linker.ld ├── script │ └── embed_symbol_table.rb └── src │ ├── console.c │ ├── gdt.c │ ├── gdt_helper.asm │ ├── idt.c │ ├── isrs.asm │ ├── kernel_page.c │ ├── loader.asm │ ├── main.c │ ├── paging.c │ ├── paging_init.c │ ├── panic.c │ ├── panic_helper.asm │ ├── pit.c │ ├── sched.asm │ ├── string.c │ ├── syscall.c │ ├── syscall_entry.asm │ └── task.c ├── mtoolsrc ├── provision.sh └── user ├── Makefile ├── crt.h ├── crt0.asm ├── crt1.c ├── init.c └── linker.ld /.gitignore: -------------------------------------------------------------------------------- 1 | /.vagrant 2 | /bochs.bxrc 3 | /hdd.base.img 4 | /hdd.img 5 | 6 | *.bin 7 | *.o 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2015 Charlie Somerville 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean kernel/radium.bin user/init.bin 2 | 3 | ifeq ($(shell uname),Darwin) 4 | $(error Cowardly refusing to run on Mac OS X) 5 | endif 6 | 7 | all: hdd.img 8 | 9 | hdd.img: hdd.base.img boot/grub/menu.lst kernel/radium.bin user/init.bin 10 | cp $< $@ 11 | MTOOLSRC=mtoolsrc mcopy boot/grub/menu.lst C:/boot/grub/menu.lst 12 | MTOOLSRC=mtoolsrc mcopy kernel/radium.bin C:/boot/radium.bin 13 | MTOOLSRC=mtoolsrc mcopy user/init.bin C:/init.bin 14 | 15 | hdd.base.img: hdd.base.img.gz 16 | gzip -dc $< > $@ 17 | 18 | clean: 19 | rm -f hdd.img hdd.base.img 20 | make -C kernel clean 21 | 22 | kernel/radium.bin: 23 | make -C kernel radium.bin 24 | 25 | user/init.bin: 26 | make -C user init.bin 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Radium 2 | 3 | Hobby OS. 4 | 5 | ## Memory Map 6 | 7 | | Begin | End (incl.) | Purpose 8 | | ----------- | ----------- | ------- 9 | | `0000_0000` | `0000_0fff` | Null page. Not mapped in. 10 | | `0000_1000` | `000f_ffff` | Low memory. Untouched. 11 | | `0010_0000` | *???* | The kernel is loaded here by GRUB. 12 | | *???* | `0fbf_ffff` | Kernel dynamic allocation area. 13 | | `0fc0_0000` | `0fc0_0fff` | Kernel stack guard page. Not mapped in. 14 | | `0fc0_1000` | `0fff_ffff` | Kernel stack. 15 | | `1000_0000` | `ffbf_ffff` | User address space. 16 | | `ffc0_0000` | `ffff_efff` | Recursively mapped page tables 17 | | `ffff_f000` | `ffff_ffff` | Recursively mapped page directory 18 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | VAGRANTFILE_API_VERSION = "2" 2 | 3 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 4 | config.vm.box = "precise32" 5 | config.vm.box_url = "http://cloud-images.ubuntu.com/vagrant/precise/current/precise-server-cloudimg-i386-vagrant-disk1.box" 6 | 7 | config.ssh.forward_agent = true 8 | 9 | config.vm.provision :shell, :path => "provision.sh" 10 | end 11 | -------------------------------------------------------------------------------- /api/radium.h: -------------------------------------------------------------------------------- 1 | #ifndef RADIUM_H 2 | #define RADIUM_H 3 | 4 | #define ENOSYS 1 // no such syscall 5 | #define EFAULT 2 // bad address 6 | 7 | // debug syscalls: 8 | #define SYS_REGDUMP 0 9 | #define SYS_CONSOLE_LOG 1 10 | 11 | // process management syscalls: 12 | #define SYS_EXIT 2 13 | #define SYS_YIELD 3 14 | #define SYS_FORK 4 15 | #define SYS_WAIT 5 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /boot/grub/menu.lst: -------------------------------------------------------------------------------- 1 | timeout 1 2 | default 0 3 | 4 | title Radium 5 | root (hd0,0) 6 | kernel /boot/radium.bin 7 | module /init.bin 8 | -------------------------------------------------------------------------------- /hdd.base.img.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haileys/radium/349d705afd8addae8d9f73c50a33cde784782985/hdd.base.img.gz -------------------------------------------------------------------------------- /kernel/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = \ 2 | -Wall -Wextra -pedantic -Werror -nostdlib -nostdinc -fno-builtin \ 3 | -nostartfiles -nodefaultlibs -fno-exceptions -ffreestanding \ 4 | -fno-stack-protector -fno-pic -std=c99 -m32 -iquote inc -masm=intel \ 5 | -I ../api 6 | 7 | OBJECTS = \ 8 | src/console.o \ 9 | src/gdt.o \ 10 | src/gdt_helper.o \ 11 | src/idt.o \ 12 | src/isrs.o \ 13 | src/kernel_page.o \ 14 | src/loader.o \ 15 | src/main.o \ 16 | src/paging.o \ 17 | src/paging_init.o \ 18 | src/panic.o \ 19 | src/panic_helper.o \ 20 | src/pit.o \ 21 | src/sched.o \ 22 | src/string.o \ 23 | src/syscall.o \ 24 | src/syscall_entry.o \ 25 | src/task.o \ 26 | 27 | .PHONY: clean 28 | 29 | radium.bin: linker.ld $(OBJECTS) script/embed_symbol_table.rb 30 | @echo ld $@ 31 | @ld -o $@ -T linker.ld $(OBJECTS) 32 | ruby script/embed_symbol_table.rb 33 | 34 | %.o: %.c inc/*.h 35 | @echo cc $@ 36 | @gcc -o $@ $(CFLAGS) -c $< 37 | 38 | %.o: %.asm 39 | @echo nasm $@ 40 | @nasm -felf -o $@ $< 41 | 42 | clean: 43 | rm -f radium.bin $(OBJECTS) 44 | -------------------------------------------------------------------------------- /kernel/inc/console.h: -------------------------------------------------------------------------------- 1 | #ifndef CONSOLE_H 2 | #define CONSOLE_H 3 | 4 | #include "stdarg.h" 5 | 6 | void 7 | console_init(); 8 | 9 | void 10 | console_puts(const char* str, uint32_t len); 11 | 12 | void 13 | printf(const char* format, ...); 14 | 15 | void 16 | vprintf(const char* format, va_list va); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /kernel/inc/gdt.h: -------------------------------------------------------------------------------- 1 | #ifndef GDT_H 2 | #define GDT_H 3 | 4 | #include "types.h" 5 | 6 | typedef enum { 7 | GDT_KERNEL_CODE = 0x08, 8 | GDT_KERNEL_DATA = 0x10, 9 | GDT_USER_CODE = 0x18, 10 | GDT_USER_DATA = 0x20, 11 | GDT_TSS = 0x28, 12 | } 13 | gdt_selector_t; 14 | 15 | typedef enum { 16 | GDT_KERNEL = 0, 17 | GDT_USER = 3, 18 | } 19 | gdt_privilege_t; 20 | 21 | typedef enum { 22 | GDT_DATA = 0, 23 | GDT_CODE = 1, 24 | } 25 | gdt_type_t; 26 | 27 | void 28 | gdt_set_entry(gdt_selector_t sel, uint32_t base, uint32_t limit, gdt_privilege_t priv, gdt_type_t type); 29 | 30 | void 31 | gdt_set_tss(gdt_selector_t sel, uint32_t base, uint32_t limit); 32 | 33 | void 34 | gdt_reload(); 35 | 36 | void 37 | gdt_init(); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /kernel/inc/idt.h: -------------------------------------------------------------------------------- 1 | #ifndef IDT_H 2 | #define IDT_H 3 | 4 | void 5 | idt_init(); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /kernel/inc/io.h: -------------------------------------------------------------------------------- 1 | #ifndef IO_H 2 | #define IO_H 3 | 4 | #include "types.h" 5 | 6 | static void __attribute__((unused)) 7 | outb(uint16_t port, uint8_t value) 8 | { 9 | __asm__ volatile("outb %1, %0" :: "r"(value), "Nd"(port)); 10 | } 11 | 12 | static uint8_t __attribute__((unused)) 13 | inb(uint16_t port) 14 | { 15 | volatile uint8_t value; 16 | __asm__ volatile("inb %0, %1" : "=r"(value) : "Nd"(port)); 17 | return value; 18 | } 19 | 20 | static void __attribute__((unused)) 21 | outl(uint16_t port, uint32_t value) 22 | { 23 | __asm__ volatile("outd %1, %0" :: "r"(value), "Nd"(port)); 24 | } 25 | 26 | static uint32_t __attribute__((unused)) 27 | inl(uint16_t port) 28 | { 29 | volatile uint32_t value; 30 | __asm__ volatile("ind %0, %1" : "=r"(value) : "Nd"(port)); 31 | return value; 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /kernel/inc/kernel_page.h: -------------------------------------------------------------------------------- 1 | #ifndef KALLOC_H 2 | #define KALLOC_H 3 | 4 | #include "types.h" 5 | 6 | void 7 | kernel_page_init(virt_t begin, virt_t end); 8 | 9 | void* 10 | kernel_page_alloc(); 11 | 12 | void* 13 | kernel_page_alloc_zeroed(); 14 | 15 | void 16 | kernel_page_free(void*); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /kernel/inc/multiboot.h: -------------------------------------------------------------------------------- 1 | /* multiboot.h - Multiboot header file. */ 2 | /* Copyright (C) 1999,2003,2007,2008,2009 Free Software Foundation, Inc. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY 17 | * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 18 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 19 | * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | #ifndef MULTIBOOT_HEADER 23 | #define MULTIBOOT_HEADER 1 24 | 25 | /* How many bytes from the start of the file we search for the header. */ 26 | #define MULTIBOOT_SEARCH 8192 27 | 28 | /* The magic field should contain this. */ 29 | #define MULTIBOOT_HEADER_MAGIC 0x1BADB002 30 | 31 | /* This should be in %eax. */ 32 | #define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 33 | 34 | /* The bits in the required part of flags field we don't support. */ 35 | #define MULTIBOOT_UNSUPPORTED 0x0000fffc 36 | 37 | /* Alignment of multiboot modules. */ 38 | #define MULTIBOOT_MOD_ALIGN 0x00001000 39 | 40 | /* Alignment of the multiboot info structure. */ 41 | #define MULTIBOOT_INFO_ALIGN 0x00000004 42 | 43 | /* Flags set in the 'flags' member of the multiboot header. */ 44 | 45 | /* Align all boot modules on i386 page (4KB) boundaries. */ 46 | #define MULTIBOOT_PAGE_ALIGN 0x00000001 47 | 48 | /* Must pass memory information to OS. */ 49 | #define MULTIBOOT_MEMORY_INFO 0x00000002 50 | 51 | /* Must pass video information to OS. */ 52 | #define MULTIBOOT_VIDEO_MODE 0x00000004 53 | 54 | /* This flag indicates the use of the address fields in the header. */ 55 | #define MULTIBOOT_AOUT_KLUDGE 0x00010000 56 | 57 | /* Flags to be set in the 'flags' member of the multiboot info structure. */ 58 | 59 | /* is there basic lower/upper memory information? */ 60 | #define MULTIBOOT_INFO_MEMORY 0x00000001 61 | /* is there a boot device set? */ 62 | #define MULTIBOOT_INFO_BOOTDEV 0x00000002 63 | /* is the command-line defined? */ 64 | #define MULTIBOOT_INFO_CMDLINE 0x00000004 65 | /* are there modules to do something with? */ 66 | #define MULTIBOOT_INFO_MODS 0x00000008 67 | 68 | /* These next two are mutually exclusive */ 69 | 70 | /* is there a symbol table loaded? */ 71 | #define MULTIBOOT_INFO_AOUT_SYMS 0x00000010 72 | /* is there an ELF section header table? */ 73 | #define MULTIBOOT_INFO_ELF_SHDR 0X00000020 74 | 75 | /* is there a full memory map? */ 76 | #define MULTIBOOT_INFO_MEM_MAP 0x00000040 77 | 78 | /* Is there drive info? */ 79 | #define MULTIBOOT_INFO_DRIVE_INFO 0x00000080 80 | 81 | /* Is there a config table? */ 82 | #define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100 83 | 84 | /* Is there a boot loader name? */ 85 | #define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200 86 | 87 | /* Is there a APM table? */ 88 | #define MULTIBOOT_INFO_APM_TABLE 0x00000400 89 | 90 | /* Is there video information? */ 91 | #define MULTIBOOT_INFO_VIDEO_INFO 0x00000800 92 | 93 | #ifndef ASM_FILE 94 | 95 | typedef unsigned short multiboot_uint16_t; 96 | typedef unsigned int multiboot_uint32_t; 97 | typedef unsigned long long multiboot_uint64_t; 98 | 99 | struct multiboot_header 100 | { 101 | /* Must be MULTIBOOT_MAGIC - see above. */ 102 | multiboot_uint32_t magic; 103 | 104 | /* Feature flags. */ 105 | multiboot_uint32_t flags; 106 | 107 | /* The above fields plus this one must equal 0 mod 2^32. */ 108 | multiboot_uint32_t checksum; 109 | 110 | /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */ 111 | multiboot_uint32_t header_addr; 112 | multiboot_uint32_t load_addr; 113 | multiboot_uint32_t load_end_addr; 114 | multiboot_uint32_t bss_end_addr; 115 | multiboot_uint32_t entry_addr; 116 | 117 | /* These are only valid if MULTIBOOT_VIDEO_MODE is set. */ 118 | multiboot_uint32_t mode_type; 119 | multiboot_uint32_t width; 120 | multiboot_uint32_t height; 121 | multiboot_uint32_t depth; 122 | }; 123 | 124 | /* The symbol table for a.out. */ 125 | struct multiboot_aout_symbol_table 126 | { 127 | multiboot_uint32_t tabsize; 128 | multiboot_uint32_t strsize; 129 | multiboot_uint32_t addr; 130 | multiboot_uint32_t reserved; 131 | }; 132 | typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t; 133 | 134 | /* The section header table for ELF. */ 135 | struct multiboot_elf_section_header_table 136 | { 137 | multiboot_uint32_t num; 138 | multiboot_uint32_t size; 139 | multiboot_uint32_t addr; 140 | multiboot_uint32_t shndx; 141 | }; 142 | typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t; 143 | 144 | struct multiboot_info 145 | { 146 | /* Multiboot info version number */ 147 | multiboot_uint32_t flags; 148 | 149 | /* Available memory from BIOS */ 150 | multiboot_uint32_t mem_lower; 151 | multiboot_uint32_t mem_upper; 152 | 153 | /* "root" partition */ 154 | multiboot_uint32_t boot_device; 155 | 156 | /* Kernel command line */ 157 | multiboot_uint32_t cmdline; 158 | 159 | /* Boot-Module list */ 160 | multiboot_uint32_t mods_count; 161 | multiboot_uint32_t mods_addr; 162 | 163 | union 164 | { 165 | multiboot_aout_symbol_table_t aout_sym; 166 | multiboot_elf_section_header_table_t elf_sec; 167 | } u; 168 | 169 | /* Memory Mapping buffer */ 170 | multiboot_uint32_t mmap_length; 171 | multiboot_uint32_t mmap_addr; 172 | 173 | /* Drive Info buffer */ 174 | multiboot_uint32_t drives_length; 175 | multiboot_uint32_t drives_addr; 176 | 177 | /* ROM configuration table */ 178 | multiboot_uint32_t config_table; 179 | 180 | /* Boot Loader Name */ 181 | multiboot_uint32_t boot_loader_name; 182 | 183 | /* APM table */ 184 | multiboot_uint32_t apm_table; 185 | 186 | /* Video */ 187 | multiboot_uint32_t vbe_control_info; 188 | multiboot_uint32_t vbe_mode_info; 189 | multiboot_uint16_t vbe_mode; 190 | multiboot_uint16_t vbe_interface_seg; 191 | multiboot_uint16_t vbe_interface_off; 192 | multiboot_uint16_t vbe_interface_len; 193 | }; 194 | typedef struct multiboot_info multiboot_info_t; 195 | 196 | struct multiboot_mmap_entry 197 | { 198 | multiboot_uint32_t size; 199 | multiboot_uint64_t addr; 200 | multiboot_uint64_t len; 201 | #define MULTIBOOT_MEMORY_AVAILABLE 1 202 | #define MULTIBOOT_MEMORY_RESERVED 2 203 | multiboot_uint32_t type; 204 | } __attribute__((packed)); 205 | typedef struct multiboot_mmap_entry multiboot_memory_map_t; 206 | 207 | struct multiboot_mod_list 208 | { 209 | /* the memory used goes from bytes 'mod_start' to 'mod_end-1' inclusive */ 210 | multiboot_uint32_t mod_start; 211 | multiboot_uint32_t mod_end; 212 | 213 | /* Module command line */ 214 | multiboot_uint32_t cmdline; 215 | 216 | /* padding to take it to 16 bytes (must be zero) */ 217 | multiboot_uint32_t pad; 218 | }; 219 | typedef struct multiboot_mod_list multiboot_module_t; 220 | 221 | #endif /* ! ASM_FILE */ 222 | 223 | #endif /* ! MULTIBOOT_HEADER */ 224 | -------------------------------------------------------------------------------- /kernel/inc/paging.h: -------------------------------------------------------------------------------- 1 | #ifndef PAGING_H 2 | #define PAGING_H 3 | 4 | #include "types.h" 5 | #include "multiboot.h" 6 | 7 | #define PAGE_SIZE 4096 8 | 9 | #define PE_FLAG_MASK (PAGE_SIZE - 1) 10 | #define PE_ADDR_MASK (~PE_FLAG_MASK) 11 | 12 | #define KERNEL_STACK_BEGIN 0x0fc00000ul 13 | #define KERNEL_STACK_END 0x0ffffffcul 14 | #define USER_BEGIN 0x10000000ul 15 | #define USER_STACK_END 0xffc00000ul 16 | #define CURRENT_PAGE_DIRECTORY 0xfffff000ul 17 | #define CURRENT_PAGE_TABLE_BASE 0xffc00000ul 18 | 19 | typedef enum { 20 | PE_PRESENT = 1 << 0, 21 | PE_READ_WRITE = 1 << 1, 22 | PE_USER = 1 << 2, 23 | } 24 | page_flags_t; 25 | 26 | void 27 | paging_set_allocatable_start(phys_t addr); 28 | 29 | void 30 | paging_init(struct multiboot_info* mb); 31 | 32 | void 33 | set_page_directory(phys_t page_directory); 34 | 35 | phys_t 36 | page_alloc(); 37 | 38 | void 39 | page_free(phys_t addr); 40 | 41 | void 42 | page_map(virt_t virt_page, phys_t phys_page, page_flags_t flags); 43 | 44 | void 45 | page_unmap(virt_t virt_page); 46 | 47 | phys_t 48 | virt_to_phys(virt_t virt); 49 | 50 | void* 51 | page_temp_map(phys_t phys_page); 52 | 53 | void 54 | page_temp_unmap(); 55 | 56 | bool 57 | page_is_user_mapped(virt_t virt); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /kernel/inc/panic.h: -------------------------------------------------------------------------------- 1 | #ifndef PANIC_H 2 | #define PANIC_H 3 | 4 | void 5 | panic(const char* message, ...) __attribute__((noreturn)); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /kernel/inc/pit.h: -------------------------------------------------------------------------------- 1 | #ifndef PIT_H 2 | #define PIT_H 3 | 4 | #include "types.h" 5 | 6 | void 7 | pit_set_frequency(uint32_t hz); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /kernel/inc/sched.h: -------------------------------------------------------------------------------- 1 | #ifndef SCHED_H 2 | #define SCHED_H 3 | 4 | void 5 | sched_begin() __attribute__((noreturn)); 6 | 7 | void 8 | sched_switch(); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /kernel/inc/stdarg.h: -------------------------------------------------------------------------------- 1 | #ifndef STDARG_H 2 | #define STDARG_H 3 | 4 | #include "types.h" 5 | 6 | typedef uint32_t* va_list; 7 | 8 | #define va_start(va, last_arg) ((va) = (va_list)&(last_arg)) 9 | #define va_arg(va, type) (*(type*)++va) 10 | #define va_end(va) ((void)va) 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /kernel/inc/string.h: -------------------------------------------------------------------------------- 1 | #ifndef STRING_H 2 | #define STRING_H 3 | 4 | #include "types.h" 5 | 6 | void 7 | memcpy(void* dest, void* src, size_t n); 8 | 9 | void 10 | memset(void* ptr, uint8_t c, size_t n); 11 | 12 | void 13 | memset16(void* ptr, uint16_t w, size_t n); 14 | 15 | void 16 | memset32(void* ptr, uint32_t l, size_t n); 17 | 18 | bool 19 | streq(const char* a, const char* b); 20 | 21 | void 22 | strlcpy(char* dest, const char* src, size_t dest_total_size); 23 | 24 | size_t 25 | strlen(const char* str); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /kernel/inc/syscall.h: -------------------------------------------------------------------------------- 1 | #ifndef SYSCALL_H 2 | #define SYSCALL_H 3 | 4 | #include "types.h" 5 | 6 | typedef struct { 7 | uint32_t edi; 8 | uint32_t esi; 9 | uint32_t ebp; 10 | uint32_t esp; 11 | uint32_t ebx; 12 | uint32_t edx; 13 | uint32_t ecx; 14 | uint32_t eax; 15 | } 16 | registers_t; 17 | 18 | void 19 | syscall_init(); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /kernel/inc/task.h: -------------------------------------------------------------------------------- 1 | #ifndef TASK_H 2 | #define TASK_H 3 | 4 | #include "types.h" 5 | #include "syscall.h" 6 | 7 | typedef struct { 8 | // 0x00 9 | uint16_t link; 10 | uint16_t _res_1; 11 | // 0x04 12 | uint32_t esp0; 13 | // 0x08 14 | uint16_t ss0; 15 | uint16_t _res_2; 16 | // 0x0c 17 | uint32_t esp1; 18 | // 0x10 19 | uint16_t ss1; 20 | uint16_t _res_3; 21 | // 0x14 22 | uint32_t esp2; 23 | // 0x18 24 | uint16_t ss2; 25 | uint16_t _res_4; 26 | // 0x1c 27 | uint32_t cr3; 28 | // 0x20 29 | uint32_t eip; 30 | // 0x24 31 | uint32_t eflags; 32 | // 0x28 33 | uint32_t eax; 34 | // 0x2c 35 | uint32_t ecx; 36 | // 0x30 37 | uint32_t edx; 38 | // 0x34 39 | uint32_t ebx; 40 | // 0x38 41 | uint32_t esp; 42 | // 0x3c 43 | uint32_t ebp; 44 | // 0x40 45 | uint32_t esi; 46 | // 0x44 47 | uint32_t edi; 48 | // 0x48 49 | uint16_t es; 50 | uint16_t _res_5; 51 | // 0x4c 52 | uint16_t cs; 53 | uint16_t _res_6; 54 | // 0x50 55 | uint16_t ss; 56 | uint16_t _res_7; 57 | // 0x54 58 | uint16_t ds; 59 | uint16_t _res_8; 60 | // 0x58 61 | uint16_t fs; 62 | uint16_t _res_9; 63 | // 0x5c 64 | uint16_t gs; 65 | uint16_t _res_10; 66 | // 0x60 67 | uint16_t ldtr; 68 | uint16_t _res_11; 69 | // 0x64 70 | uint16_t _res_12; 71 | uint16_t iopb; 72 | // 0x68 73 | } __attribute__((packed)) 74 | tss_t; 75 | 76 | typedef enum { 77 | TASK_READY = 1, 78 | TASK_BLOCK_WAIT = 2, 79 | TASK_EXITING = 3, 80 | } 81 | task_state_t; 82 | 83 | // sched.asm refers to hardcoded offsets within this struct. 84 | // make sure to change it when changing anything here. 85 | typedef struct task { 86 | /* 0 */ uint8_t fpu_state[512]; 87 | /* 512 */ uint32_t esp; 88 | /* 516 */ uint32_t eip; 89 | /* 520 */ void* kernel_stack; 90 | // allocated within the kernel's identity-mapped region: 91 | /* 524 */ uint32_t* page_directory; 92 | /* 528 */ phys_t page_directory_phys; 93 | 94 | // struct members past this point may be freely rearranged without needing 95 | // to update any assembly source. 96 | 97 | registers_t* syscall_registers; 98 | 99 | uint32_t pid; 100 | uint32_t ppid; 101 | 102 | task_state_t state; 103 | uint8_t exit_status; 104 | 105 | union { 106 | struct { 107 | struct task* head; 108 | struct task* tail; 109 | } live; 110 | struct { 111 | struct task* next; 112 | } dead; 113 | } wait_queue; 114 | } 115 | task_t; 116 | 117 | extern task_t* 118 | current_task; 119 | 120 | void 121 | task_init(); 122 | 123 | void 124 | task_boot_init(const char* init_bin, size_t size) __attribute__((noreturn)); 125 | 126 | task_t* 127 | task_fork(); 128 | 129 | void 130 | task_kill(task_t* task, uint8_t status); 131 | 132 | void 133 | task_destroy(task_t* task); 134 | 135 | task_t* 136 | task_for_pid(uint32_t pid); 137 | 138 | #endif 139 | -------------------------------------------------------------------------------- /kernel/inc/types.h: -------------------------------------------------------------------------------- 1 | #ifndef TYPES_H 2 | #define TYPES_H 3 | 4 | typedef unsigned char uint8_t; 5 | typedef unsigned short uint16_t; 6 | typedef unsigned int uint32_t; 7 | typedef unsigned long long uint64_t; 8 | 9 | typedef signed char int8_t; 10 | typedef signed short int16_t; 11 | typedef signed int int32_t; 12 | typedef signed long long int64_t; 13 | 14 | typedef uint32_t size_t; 15 | 16 | typedef _Bool bool; 17 | #define true 1 18 | #define false 0 19 | 20 | #define NULL ((void*)0) 21 | 22 | typedef uint32_t virt_t; 23 | typedef uint32_t phys_t; 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /kernel/inc/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | #include "types.h" 5 | 6 | #define static_assert(name, expr) static char __static_assert__##name[(expr) ? 1 : -1] __attribute__((unused)) 7 | 8 | #define countof(x) (sizeof(x) / sizeof(*(x))) 9 | 10 | static inline size_t 11 | round_down(size_t val, size_t divisor) 12 | { 13 | return val - val % divisor; 14 | } 15 | 16 | static inline size_t 17 | round_up(size_t val, size_t divisor) 18 | { 19 | return round_down(val + divisor - 1, divisor); 20 | } 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /kernel/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(loader) 2 | OUTPUT_FORMAT("elf32-i386") 3 | 4 | SECTIONS { 5 | . = 0x00100000; 6 | 7 | .text : { 8 | *(.multiboot_header) 9 | *(.text) 10 | } 11 | 12 | .rodata ALIGN (0x1000) : { 13 | *(.rodata) 14 | } 15 | 16 | .data ALIGN (0x1000) : { 17 | *(.data) 18 | } 19 | 20 | .bss : { 21 | sbss = .; 22 | *(COMMON) 23 | *(.bss) 24 | ebss = .; 25 | } 26 | 27 | .end_of_image : { 28 | *(.end_of_image) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /kernel/script/embed_symbol_table.rb: -------------------------------------------------------------------------------- 1 | RECORD_SIZE = 32 2 | TABLE_SIZE = 65536 3 | 4 | bin = File.read("radium.bin") 5 | i = bin.index("@@@ PANIC SYMBOL TABLE @@@") 6 | 7 | `nm radium.bin`.lines.map { |line| 8 | hex_addr, _, name = line.chomp.split 9 | bin[i, RECORD_SIZE] = [hex_addr.to_i(16), name].pack("L= TABLE_SIZE 12 | fail "symbol table overflow" 13 | end 14 | } 15 | 16 | bin[i, 4] = "\0\0\0\0" 17 | 18 | File.open("radium.bin", "wb") do |f| 19 | f.write bin 20 | end 21 | 22 | puts "Wrote #{i / RECORD_SIZE} symbols" 23 | -------------------------------------------------------------------------------- /kernel/src/console.c: -------------------------------------------------------------------------------- 1 | #include "console.h" 2 | #include "io.h" 3 | #include "string.h" 4 | #include "stdarg.h" 5 | 6 | typedef enum { 7 | COLOUR_BLACK = 0, 8 | COLOUR_BLUE = 1, 9 | COLOUR_GREEN = 2, 10 | COLOUR_CYAN = 3, 11 | COLOUR_RED = 4, 12 | COLOUR_PINK = 5, 13 | COLOUR_BROWN = 6, 14 | COLOUR_LIGHT_GREY = 7, 15 | COLOUR_DARK_GREY = 8, 16 | COLOUR_LIGHT_BLUE = 9, 17 | COLOUR_LIGHT_GREEN = 10, 18 | COLOUR_LIGHT_CYAN = 11, 19 | COLOUR_LIGHT_RED = 12, 20 | COLOUR_LIGHT_PINK = 13, 21 | COLOUR_YELLOW = 14, 22 | COLOUR_WHITE = 15, 23 | } 24 | colour_t; 25 | 26 | static uint8_t 27 | make_attr(colour_t background, colour_t foreground) 28 | { 29 | return (background << 4) | foreground; 30 | } 31 | 32 | typedef struct { 33 | uint8_t character; 34 | uint8_t attr; 35 | } 36 | vchar_t; 37 | 38 | static int x, y; 39 | 40 | static const int width = 80, height = 25; 41 | 42 | static vchar_t* const vram = (void*)0xb8000; 43 | 44 | static uint16_t base_vga_port; 45 | 46 | void 47 | console_init() 48 | { 49 | // read base vga port from bios data area 50 | base_vga_port = *(uint16_t*)0x463; 51 | 52 | memset16(vram, make_attr(COLOUR_BLACK, COLOUR_LIGHT_GREY) << 8, width * height); 53 | memset16(vram + width * height, (make_attr(COLOUR_RED, COLOUR_WHITE) << 8) | 'X', width); 54 | } 55 | 56 | static void 57 | scroll_up() 58 | { 59 | memcpy(vram, vram + width, width * (height - 1) * 2); 60 | 61 | uint16_t empty_attr = make_attr(COLOUR_BLACK, COLOUR_LIGHT_GREY) << 8; 62 | memset16(vram + width * (height - 1), empty_attr, width); 63 | } 64 | 65 | static void 66 | newline() 67 | { 68 | x = 0; 69 | 70 | if(++y == height) { 71 | scroll_up(); 72 | y = height - 1; 73 | } 74 | } 75 | 76 | static void 77 | update_cursor() 78 | { 79 | uint16_t pos = y * width + x; 80 | 81 | outb(base_vga_port, 0x0e); 82 | outb(base_vga_port + 1, (pos >> 8) & 0xff); 83 | 84 | outb(base_vga_port, 0x0f); 85 | outb(base_vga_port + 1, pos & 0xff); 86 | } 87 | 88 | static void 89 | putc(char c) 90 | { 91 | if(c == '\r') { 92 | x = 0; 93 | return; 94 | } 95 | 96 | if(c == '\n') { 97 | newline(); 98 | return; 99 | } 100 | 101 | vram[y * width + x].attr = COLOUR_LIGHT_GREY; 102 | vram[y * width + x].character = c; 103 | 104 | if(++x == width) { 105 | newline(); 106 | } 107 | } 108 | 109 | void 110 | console_puts(const char* str, uint32_t len) 111 | { 112 | while(len--) { 113 | putc(*str++); 114 | } 115 | 116 | update_cursor(); 117 | } 118 | 119 | static void 120 | itoa(int n, char* out) 121 | { 122 | int len = 0, negative = 0; 123 | 124 | if(n == 0) { 125 | out[len++] = '0'; 126 | out[len++] = 0; 127 | return; 128 | } 129 | 130 | if(n < 0) { 131 | negative = 1; 132 | n *= -1; 133 | } 134 | 135 | while(n) { 136 | out[len++] = '0' + (n % 10); 137 | n /= 10; 138 | } 139 | 140 | if(negative) { 141 | out[len++] = '-'; 142 | } 143 | 144 | out[len] = 0; 145 | 146 | for(int i = 0; i < len / 2; i++) { 147 | int j = len - i - 1; 148 | char c = out[j]; 149 | out[j] = out[i]; 150 | out[i] = c; 151 | } 152 | } 153 | 154 | static char 155 | hex_dig(uint32_t dig) 156 | { 157 | if(dig <= 9) { 158 | return '0' + dig; 159 | } else if(dig >= 10 && dig <= 15) { 160 | return 'a' + dig - 10; 161 | } 162 | return '?'; 163 | } 164 | 165 | static void 166 | utox(uint32_t u, char* out) 167 | { 168 | out[0] = hex_dig((u >> 28) & 0xf); 169 | out[1] = hex_dig((u >> 24) & 0xf); 170 | out[2] = hex_dig((u >> 20) & 0xf); 171 | out[3] = hex_dig((u >> 16) & 0xf); 172 | out[4] = hex_dig((u >> 12) & 0xf); 173 | out[5] = hex_dig((u >> 8) & 0xf); 174 | out[6] = hex_dig((u >> 4) & 0xf); 175 | out[7] = hex_dig((u >> 0) & 0xf); 176 | out[8] = 0; 177 | } 178 | 179 | void 180 | printf(const char* format, ...) 181 | { 182 | va_list va; 183 | va_start(va, format); 184 | vprintf(format, va); 185 | va_end(va); 186 | } 187 | 188 | void 189 | vprintf(const char* format, va_list va) 190 | { 191 | while(1) { 192 | char c = *format++; 193 | 194 | if(c == 0) { 195 | break; 196 | } 197 | 198 | if(c != '%') { 199 | putc(c); 200 | continue; 201 | } 202 | 203 | switch(c = *format++) { 204 | case 'd': { 205 | char buff[16]; 206 | itoa(va_arg(va, int), buff); 207 | console_puts(buff, strlen(buff)); 208 | break; 209 | } 210 | case 'x': { 211 | char buff[16]; 212 | utox(va_arg(va, uint32_t), buff); 213 | console_puts(buff, strlen(buff)); 214 | break; 215 | } 216 | case 's': { 217 | const char* str = va_arg(va, const char*); 218 | console_puts(str, strlen(str)); 219 | break; 220 | } 221 | case 'c': { 222 | putc(va_arg(va, char)); 223 | break; 224 | } 225 | case 0: { 226 | goto ret; 227 | } 228 | default: { 229 | putc(c); 230 | break; 231 | } 232 | } 233 | } 234 | ret: 235 | update_cursor(); 236 | } 237 | -------------------------------------------------------------------------------- /kernel/src/gdt.c: -------------------------------------------------------------------------------- 1 | #include "gdt.h" 2 | #include "panic.h" 3 | #include "types.h" 4 | 5 | typedef struct { 6 | uint16_t limit_0_15; 7 | uint16_t base_0_15; 8 | uint8_t base_16_23; 9 | uint8_t access; 10 | uint8_t limit_16_19_and_flags; 11 | uint8_t base_24_31; 12 | } 13 | gdt_entry_t; 14 | 15 | static gdt_entry_t 16 | gdt[6]; 17 | 18 | volatile struct { 19 | uint16_t size; 20 | gdt_entry_t* offset; 21 | } __attribute__((packed)) 22 | gdtr; 23 | 24 | static void 25 | gdt_set_entry_raw(gdt_selector_t sel, uint32_t base, uint32_t limit, uint8_t access) 26 | { 27 | if(sel >= sizeof(gdt)) { 28 | panic("GDT overflow"); 29 | } 30 | 31 | uint8_t flags = 1 << 6; // 32 bit segment 32 | 33 | // 1 MiB is the maximum segment size that can be expressed with 1 byte 34 | // granularity. If we need to express a size bigger than this, we need 35 | // to divide the size by 4 KiB and use 4 KiB granularity. 36 | if(limit >= (1 << 20)) { 37 | limit /= 4096; 38 | flags |= 1 << 7; // 4 KiB granularity 39 | } 40 | 41 | gdt_entry_t ent; 42 | ent.limit_0_15 = limit & 0xffff; 43 | ent.base_0_15 = base & 0xffff; 44 | ent.base_16_23 = (base >> 16) & 0xff; 45 | ent.access = access; 46 | ent.limit_16_19_and_flags = ((limit >> 16) & 0x0f) | flags; 47 | ent.base_24_31 = (base >> 24) & 0xff; 48 | 49 | gdt[sel / sizeof(gdt_entry_t)] = ent; 50 | } 51 | 52 | void 53 | gdt_set_entry(gdt_selector_t sel, uint32_t base, uint32_t limit, gdt_privilege_t priv, gdt_type_t type) 54 | { 55 | gdt_set_entry_raw(sel, base, limit, 56 | (1 << 7) | // present 57 | ((priv & 3) << 5) | // privilege 58 | (1 << 4) | // dunno lol 59 | ((type & 1) << 3) | // code/data 60 | (1 << 1) // data segments are always writable, code segments are always readable 61 | ); 62 | } 63 | 64 | void 65 | gdt_set_tss(gdt_selector_t sel, uint32_t base, uint32_t limit) 66 | { 67 | gdt_set_entry_raw(sel, base, limit, 0x89); 68 | } 69 | 70 | void 71 | gdt_reload(); 72 | 73 | void 74 | gdt_init() 75 | { 76 | gdtr.size = sizeof(gdt) - 1; 77 | gdtr.offset = gdt; 78 | 79 | gdt_set_entry(GDT_KERNEL_CODE, 0, 0xfffffffful, GDT_KERNEL, GDT_CODE); 80 | gdt_set_entry(GDT_KERNEL_DATA, 0, 0xfffffffful, GDT_KERNEL, GDT_DATA); 81 | gdt_set_entry(GDT_USER_CODE, 0, 0xfffffffful, GDT_USER, GDT_CODE); 82 | gdt_set_entry(GDT_USER_DATA, 0, 0xfffffffful, GDT_USER, GDT_DATA); 83 | 84 | gdt_reload(); 85 | } 86 | -------------------------------------------------------------------------------- /kernel/src/gdt_helper.asm: -------------------------------------------------------------------------------- 1 | extern gdtr 2 | global gdt_reload 3 | 4 | section .text 5 | gdt_reload: 6 | lgdt [gdtr] 7 | jmp 0x08:.flush_cs 8 | .flush_cs: 9 | mov eax, 0x10 10 | mov ds, eax 11 | mov es, eax 12 | mov fs, eax 13 | mov gs, eax 14 | mov ss, eax 15 | ret 16 | -------------------------------------------------------------------------------- /kernel/src/idt.c: -------------------------------------------------------------------------------- 1 | #include "console.h" 2 | #include "gdt.h" 3 | #include "idt.h" 4 | #include "io.h" 5 | #include "types.h" 6 | 7 | typedef struct { 8 | uint16_t offset_0_15; 9 | uint16_t segment; 10 | uint8_t zero; 11 | uint8_t type_attr; 12 | uint16_t offset_16_31; 13 | } 14 | idt_entry_t; 15 | 16 | static idt_entry_t 17 | idt[256]; 18 | 19 | volatile struct { 20 | uint16_t size; 21 | idt_entry_t* offset; 22 | } __attribute__((packed)) 23 | idtr; 24 | 25 | void 26 | interrupts_register_isr(uint8_t interrupt_no, uint32_t handler) 27 | { 28 | idt_entry_t ent; 29 | 30 | ent.offset_0_15 = handler & 0xffff; 31 | ent.segment = GDT_KERNEL_CODE; 32 | ent.zero = 0; 33 | ent.type_attr = (1 << 7) // present 34 | | 0xe // interrupt gate 35 | ; 36 | ent.offset_16_31 = (handler >> 16) & 0xffff; 37 | 38 | idt[interrupt_no] = ent; 39 | } 40 | 41 | static void 42 | remap_irqs() 43 | { 44 | // remap IRQ table 45 | outb(0x20, 0x11); 46 | outb(0xa0, 0x11); 47 | outb(0x21, 0x20); 48 | outb(0xa1, 0x28); 49 | outb(0x21, 0x04); 50 | outb(0xa1, 0x02); 51 | outb(0x21, 0x01); 52 | outb(0xa1, 0x01); 53 | outb(0x21, 0x00); 54 | outb(0xa1, 0x00); 55 | } 56 | 57 | void 58 | idt_init_asm(); 59 | 60 | void 61 | idt_load(); 62 | 63 | void 64 | idt_init() 65 | { 66 | remap_irqs(); 67 | idt_init_asm(); 68 | 69 | idtr.size = sizeof(idt) - 1; 70 | idtr.offset = idt; 71 | idt_load(); 72 | } 73 | -------------------------------------------------------------------------------- /kernel/src/isrs.asm: -------------------------------------------------------------------------------- 1 | global idt_init_asm 2 | global idt_load 3 | extern interrupts_register_isr 4 | extern idtr 5 | extern panic 6 | extern sched_switch 7 | 8 | %macro register_isr 1 9 | push isr_%1 10 | push %1 11 | call interrupts_register_isr 12 | add esp, 8 13 | %endmacro 14 | 15 | %macro ack_irq 0 16 | push ax 17 | mov al, 0x20 18 | out 0xa0, al 19 | out 0x20, al 20 | pop ax 21 | %endmacro 22 | 23 | %macro begin_isr 1 24 | jmp isr_%1.end 25 | isr_%1: 26 | %endmacro 27 | 28 | %macro end_isr 1 29 | .end: 30 | register_isr %1 31 | %endmacro 32 | 33 | %macro generic_exception 2 34 | begin_isr %1 35 | push .msg 36 | call panic 37 | .msg db %2, 0 38 | end_isr %1 39 | %endmacro 40 | 41 | section .text 42 | idt_load: 43 | lidt [idtr] 44 | ret 45 | 46 | idt_init_asm: 47 | 48 | generic_exception 0, "divide by zero" 49 | generic_exception 1, "debug" 50 | generic_exception 2, "non-maskable interrupt" 51 | generic_exception 3, "breakpoint" 52 | generic_exception 4, "overflow" 53 | generic_exception 5, "bound range exceeded" 54 | generic_exception 6, "invalid opcode" 55 | generic_exception 7, "device not available" 56 | generic_exception 8, "double fault" 57 | generic_exception 10, "invalid tss" 58 | generic_exception 11, "segment not present" 59 | generic_exception 12, "stack segment fault" 60 | generic_exception 13, "general protection fault" 61 | 62 | ; page fault 63 | begin_isr 14 64 | mov eax, cr2 65 | push eax 66 | push .msg 67 | call panic 68 | iret 69 | 70 | .msg db "page fault at 0x%x, error code: %x", 0 71 | end_isr 14 72 | 73 | ; PIT irq 74 | begin_isr 32 75 | ack_irq 76 | 77 | push ebp 78 | push dword 0 79 | push dword 0 80 | mov ebp, esp 81 | 82 | call sched_switch 83 | 84 | add esp, 8 ; fix up the fake stack frame we created 85 | pop ebp 86 | 87 | iret 88 | end_isr 32 89 | 90 | ; keyboard irq 91 | begin_isr 33 92 | ack_irq 93 | iret 94 | end_isr 33 95 | 96 | ; spurious irq 97 | begin_isr 39 98 | iret 99 | end_isr 39 100 | 101 | ret 102 | -------------------------------------------------------------------------------- /kernel/src/kernel_page.c: -------------------------------------------------------------------------------- 1 | #include "kernel_page.h" 2 | #include "paging.h" 3 | #include "panic.h" 4 | #include "string.h" 5 | 6 | typedef union kernel_page { 7 | union kernel_page* next; 8 | char mem[PAGE_SIZE]; 9 | } 10 | kernel_page_t; 11 | 12 | static kernel_page_t* 13 | allocated_to; 14 | 15 | static kernel_page_t* 16 | end; 17 | 18 | static kernel_page_t* 19 | next_free; 20 | 21 | void 22 | kernel_page_init(virt_t begin_, virt_t end_) 23 | { 24 | allocated_to = (void*)begin_; 25 | end = (void*)end_; 26 | } 27 | 28 | void* 29 | kernel_page_alloc() 30 | { 31 | if(next_free) { 32 | kernel_page_t* page = next_free; 33 | next_free = next_free->next; 34 | return page; 35 | } 36 | 37 | if(allocated_to >= end) { 38 | return NULL; 39 | } 40 | 41 | page_map((virt_t)allocated_to, page_alloc(), PE_PRESENT | PE_READ_WRITE); 42 | void* retn = allocated_to++; 43 | 44 | return retn; 45 | } 46 | 47 | void* 48 | kernel_page_alloc_zeroed() 49 | { 50 | void* page = kernel_page_alloc(); 51 | memset32(page, 0, 1024); 52 | return page; 53 | } 54 | 55 | void 56 | kernel_page_free(void* ptr) 57 | { 58 | kernel_page_t* page = ptr; 59 | 60 | page->next = next_free; 61 | next_free = page; 62 | } 63 | -------------------------------------------------------------------------------- /kernel/src/loader.asm: -------------------------------------------------------------------------------- 1 | global loader 2 | global end_of_image 3 | 4 | extern kmain 5 | 6 | section .multiboot_header 7 | align 4 8 | multiboot_header: 9 | dd 0x1badb002 ; magic 10 | dd 3 ; flags 11 | dd -(0x1badb002 + 3) ; checksum = -(flags + magic) 12 | 13 | section .text 14 | align 4 15 | loader: 16 | mov esp, stack 17 | push dword 0 18 | push dword 0 19 | mov ebp, esp 20 | 21 | push eax ; multiboot magic number 22 | push ebx ; pointer to multiboot struct 23 | 24 | fninit 25 | mov eax, cr0 26 | or eax, 1 << 5 ; FPU NE bit 27 | mov cr0, eax 28 | 29 | call kmain 30 | 31 | section .bss 32 | align 4 33 | resb 65536 34 | stack: 35 | 36 | section .end_of_image 37 | end_of_image: 38 | -------------------------------------------------------------------------------- /kernel/src/main.c: -------------------------------------------------------------------------------- 1 | #include "console.h" 2 | #include "gdt.h" 3 | #include "idt.h" 4 | #include "kernel_page.h" 5 | #include "multiboot.h" 6 | #include "paging.h" 7 | #include "panic.h" 8 | #include "pit.h" 9 | #include "sched.h" 10 | #include "string.h" 11 | #include "syscall.h" 12 | #include "task.h" 13 | #include "types.h" 14 | 15 | static multiboot_info_t* mb; 16 | 17 | static multiboot_module_t* 18 | find_module(const char* name) 19 | { 20 | multiboot_module_t* mods = (void*)mb->mods_addr; 21 | 22 | for(size_t i = 0; i < mb->mods_count; i++) { 23 | if(streq((const char*)mods[i].cmdline, name)) { 24 | return &mods[i]; 25 | } 26 | } 27 | 28 | return NULL; 29 | } 30 | 31 | void 32 | kmain(multiboot_info_t* mb_, uint32_t magic) 33 | { 34 | (void)magic; 35 | mb = mb_; 36 | 37 | console_init(); 38 | 39 | printf("Radium booting from %s.\n", (const char*)mb->boot_loader_name); 40 | 41 | for(size_t i = 0; i < mb->mods_count; i++) { 42 | multiboot_module_t* mods = (void*)mb->mods_addr; 43 | paging_set_allocatable_start(mods[i].mod_end); 44 | } 45 | 46 | gdt_init(); 47 | idt_init(); 48 | pit_set_frequency(100); 49 | paging_init(mb); 50 | task_init(); 51 | syscall_init(); 52 | 53 | multiboot_module_t* mod = find_module("/init.bin"); 54 | 55 | task_boot_init((const char*)mod->mod_start, mod->mod_end - mod->mod_start); 56 | } 57 | -------------------------------------------------------------------------------- /kernel/src/paging.c: -------------------------------------------------------------------------------- 1 | #include "console.h" 2 | #include "paging.h" 3 | #include "panic.h" 4 | #include "string.h" 5 | 6 | static phys_t 7 | next_free_page; 8 | 9 | #define FL_PAGING_ENABLED (1 << 31) 10 | 11 | static bool 12 | paging_enabled() 13 | { 14 | uint32_t cr0; 15 | __asm__("mov %0, cr0" : "=r"(cr0)); 16 | return !!(cr0 & FL_PAGING_ENABLED); 17 | } 18 | 19 | void 20 | set_page_directory(phys_t page_directory) 21 | { 22 | uint32_t cr0; 23 | 24 | __asm__("mov cr3, %0" :: "r"(page_directory)); 25 | 26 | __asm__("mov %0, cr0" : "=r"(cr0)); 27 | cr0 |= FL_PAGING_ENABLED; 28 | __asm__("mov cr0, %0" :: "r"(cr0) : "memory"); 29 | } 30 | 31 | static void 32 | invlpg(virt_t virt) 33 | { 34 | __asm__ volatile("invlpg [%0]" :: "r"(virt) : "memory"); 35 | } 36 | 37 | phys_t 38 | page_alloc() 39 | { 40 | phys_t page = next_free_page; 41 | 42 | if(paging_enabled()) { 43 | phys_t* temp_mapping = page_temp_map(page); 44 | next_free_page = *temp_mapping; 45 | page_temp_unmap(); 46 | } else { 47 | next_free_page = *(phys_t*)page; 48 | } 49 | 50 | return page; 51 | } 52 | 53 | void 54 | page_free(phys_t addr) 55 | { 56 | if(paging_enabled()) { 57 | phys_t* temp_mapping = page_temp_map(addr); 58 | *temp_mapping = next_free_page; 59 | page_temp_unmap(); 60 | } else { 61 | *(phys_t*)addr = next_free_page; 62 | } 63 | 64 | next_free_page = addr; 65 | } 66 | 67 | void 68 | page_map(virt_t virt_page, phys_t phys_page, page_flags_t flags) 69 | { 70 | // page directories are always recursively mapped into themselves: 71 | uint32_t* current_page_directory = (uint32_t*)CURRENT_PAGE_DIRECTORY; 72 | 73 | size_t page_dir_i = (virt_page / 4096) / 1024; 74 | size_t page_tab_i = (virt_page / 4096) % 1024; 75 | 76 | uint32_t* page_table = (uint32_t*)(CURRENT_PAGE_TABLE_BASE + page_dir_i * PAGE_SIZE); 77 | 78 | uint32_t pd_entry = current_page_directory[page_dir_i]; 79 | if(!(pd_entry & PE_PRESENT)) { 80 | current_page_directory[page_dir_i] = page_alloc() | (flags & PE_FLAG_MASK); 81 | invlpg((virt_t)page_table); 82 | memset(page_table, 0, 4096); 83 | } 84 | 85 | page_table[page_tab_i] = (phys_page & PE_ADDR_MASK) | (flags & PE_FLAG_MASK); 86 | invlpg((virt_t)virt_page); 87 | } 88 | 89 | void 90 | page_unmap(virt_t virt_page) 91 | { 92 | page_map(virt_page, 0, 0); 93 | } 94 | 95 | phys_t 96 | virt_to_phys(virt_t virt) 97 | { 98 | uint32_t* current_page_directory = (uint32_t*)CURRENT_PAGE_DIRECTORY; 99 | 100 | size_t page_dir_i = (virt / 4096) / 1024; 101 | size_t page_tab_i = (virt / 4096) % 1024; 102 | size_t page_offset = virt % 4096; 103 | 104 | if(!(current_page_directory[page_dir_i] & PE_PRESENT)) { 105 | return 0; 106 | } 107 | 108 | uint32_t* page_table = (uint32_t*)(CURRENT_PAGE_TABLE_BASE + page_dir_i * PAGE_SIZE); 109 | 110 | uint32_t page_tab_entry = page_table[page_tab_i]; 111 | 112 | if(!(page_tab_entry & PE_PRESENT)) { 113 | return 0; 114 | } 115 | 116 | return (page_tab_entry & PE_ADDR_MASK) + page_offset; 117 | } 118 | 119 | static uint32_t* const 120 | temp_page_entry = (void*)CURRENT_PAGE_TABLE_BASE; 121 | 122 | static uint32_t 123 | old_null_page; 124 | 125 | void* 126 | page_temp_map(phys_t phys_page) 127 | { 128 | old_null_page = *temp_page_entry; 129 | *temp_page_entry = phys_page | PE_PRESENT | PE_READ_WRITE; 130 | invlpg(0); 131 | return NULL; // we map in the temp page at NULL - todo fix... 132 | } 133 | 134 | void 135 | page_temp_unmap() 136 | { 137 | *temp_page_entry = old_null_page; 138 | invlpg(0); 139 | } 140 | 141 | bool 142 | page_is_user_mapped(virt_t virt) 143 | { 144 | uint32_t dir_i = virt / (1024 * PAGE_SIZE); 145 | uint32_t base_i = (virt / PAGE_SIZE); 146 | 147 | uint32_t* page_directory = (uint32_t*)CURRENT_PAGE_DIRECTORY; 148 | 149 | if(!(page_directory[dir_i] & (PE_PRESENT | PE_USER))) { 150 | return false; 151 | } 152 | 153 | uint32_t* page_table_base = (uint32_t*)CURRENT_PAGE_TABLE_BASE; 154 | 155 | if(!(page_table_base[base_i] & (PE_PRESENT | PE_USER))) { 156 | return false; 157 | } 158 | 159 | return true; 160 | } 161 | -------------------------------------------------------------------------------- /kernel/src/paging_init.c: -------------------------------------------------------------------------------- 1 | #include "console.h" 2 | #include "kernel_page.h" 3 | #include "multiboot.h" 4 | #include "paging.h" 5 | #include "string.h" 6 | #include "util.h" 7 | 8 | static phys_t 9 | kernel_end; 10 | 11 | static phys_t 12 | alloc_zeroed_page() 13 | { 14 | phys_t page = page_alloc(); 15 | memset32((void*)page, 0, 1024); 16 | return page; 17 | } 18 | 19 | static size_t 20 | register_available_memory_region(multiboot_memory_map_t* region) 21 | { 22 | size_t pages_registered = 0; 23 | 24 | for(size_t offset = 0; offset + PAGE_SIZE <= region->len; offset += PAGE_SIZE) { 25 | phys_t addr = region->addr + offset; 26 | 27 | if(addr < kernel_end) { 28 | // don't put memory before the end of the kernel in the free list 29 | continue; 30 | } 31 | 32 | page_free(addr); 33 | pages_registered++; 34 | } 35 | 36 | return pages_registered; 37 | } 38 | 39 | static size_t 40 | register_available_memory(multiboot_info_t* mb) 41 | { 42 | multiboot_memory_map_t* mmap = (void*)mb->mmap_addr; 43 | size_t pages_registered = 0; 44 | 45 | for(size_t i = 0; i < mb->mmap_length / sizeof(multiboot_memory_map_t); i++) { 46 | if(mmap[i].type == MULTIBOOT_MEMORY_AVAILABLE) { 47 | pages_registered += register_available_memory_region(mmap + i); 48 | } 49 | } 50 | 51 | return pages_registered; 52 | } 53 | 54 | static void 55 | create_page_tables_for_kernel_space(uint32_t* page_directory) 56 | { 57 | for(size_t i = 0; i < KERNEL_STACK_END / (4 * 1024 * 1024); i++) { 58 | page_directory[i] = alloc_zeroed_page() | PE_PRESENT | PE_READ_WRITE; 59 | } 60 | } 61 | 62 | static void 63 | identity_map_kernel(uint32_t* page_directory) 64 | { 65 | // we start looping from PAGE_SIZE in order to leave the null page unmapped 66 | // accessing it will cause a page fault. 67 | for(phys_t addr = PAGE_SIZE; addr <= kernel_end; addr += PAGE_SIZE) { 68 | size_t page_dir_i = addr / 4096 / 1024; 69 | size_t page_tab_i = addr / 4096 % 1024; 70 | uint32_t page_dir_ent = page_directory[page_dir_i]; 71 | if(!page_dir_ent) { 72 | page_dir_ent = page_directory[page_dir_i] = alloc_zeroed_page() | PE_PRESENT | PE_READ_WRITE; 73 | } 74 | uint32_t* page_tab = (uint32_t*)(page_dir_ent & PE_ADDR_MASK); 75 | page_tab[page_tab_i] = addr | PE_PRESENT | PE_READ_WRITE; 76 | } 77 | } 78 | 79 | static void 80 | recursively_map_page_directory(uint32_t* page_directory) 81 | { 82 | page_directory[1023] = (phys_t)page_directory | PE_PRESENT | PE_READ_WRITE; 83 | } 84 | 85 | void 86 | paging_set_allocatable_start(phys_t addr) 87 | { 88 | if(addr > kernel_end) { 89 | kernel_end = round_up(addr, PAGE_SIZE); 90 | } 91 | } 92 | 93 | void 94 | paging_init(multiboot_info_t* mb) 95 | { 96 | extern int end_of_image; 97 | paging_set_allocatable_start((phys_t)&end_of_image); 98 | 99 | size_t pages_registered = register_available_memory(mb); 100 | printf("%d MiB available useful memory.\n", pages_registered * PAGE_SIZE / 1024 / 1024); 101 | 102 | uint32_t* page_directory = (uint32_t*)alloc_zeroed_page(); 103 | 104 | create_page_tables_for_kernel_space(page_directory); 105 | identity_map_kernel(page_directory); 106 | recursively_map_page_directory(page_directory); 107 | 108 | set_page_directory((phys_t)page_directory); 109 | 110 | kernel_page_init(kernel_end, KERNEL_STACK_BEGIN); 111 | } 112 | -------------------------------------------------------------------------------- /kernel/src/panic.c: -------------------------------------------------------------------------------- 1 | #include "console.h" 2 | #include "panic.h" 3 | #include "stdarg.h" 4 | #include "types.h" 5 | 6 | #define RECORD_SIZE 32 7 | 8 | typedef struct { 9 | uint32_t addr; 10 | char name[RECORD_SIZE - sizeof(uint32_t)]; 11 | } 12 | symbol_record_t; 13 | 14 | #define SYMBOL_ENTRIES (65536 / RECORD_SIZE) 15 | 16 | extern symbol_record_t panic_symbols[SYMBOL_ENTRIES]; 17 | 18 | void 19 | panic_print_backtrace() __attribute__((noreturn)); 20 | 21 | void 22 | panic(const char* format, ...) 23 | { 24 | __asm__ volatile("cli"); 25 | 26 | va_list va; 27 | va_start(va, format); 28 | printf("\npanic: "); 29 | vprintf(format, va); 30 | printf("\n"); 31 | va_end(va); 32 | 33 | panic_print_backtrace(); 34 | } 35 | 36 | void 37 | panic_print_backtrace_item(uint32_t addr) 38 | { 39 | uint32_t base = 0; 40 | const char* name = "?"; 41 | 42 | for(size_t i = 0; i < SYMBOL_ENTRIES; i++) { 43 | if(panic_symbols[i].addr == 0) { 44 | break; 45 | } 46 | if(panic_symbols[i].addr < addr && panic_symbols[i].addr > base) { 47 | base = panic_symbols[i].addr; 48 | name = panic_symbols[i].name; 49 | } 50 | } 51 | 52 | printf(" [<0x%x>] %s +%d\n", addr, name, addr - base); 53 | } 54 | -------------------------------------------------------------------------------- /kernel/src/panic_helper.asm: -------------------------------------------------------------------------------- 1 | global panic_print_backtrace 2 | global panic_symbols 3 | 4 | extern printf 5 | extern vprintf 6 | extern panic_print_backtrace_item 7 | 8 | section .rodata 9 | 10 | panic_symbols: 11 | db '@@@ PANIC SYMBOL TABLE @@@' 12 | times 65536 - ($ - panic_symbols) db 0 13 | 14 | section .text 15 | panic_print_backtrace: 16 | mov esp, ebp 17 | pop ebp 18 | cmp dword [esp], 0 19 | je .end_loop 20 | call panic_print_backtrace_item 21 | jmp panic_print_backtrace 22 | .end_loop: 23 | hlt 24 | -------------------------------------------------------------------------------- /kernel/src/pit.c: -------------------------------------------------------------------------------- 1 | #include "io.h" 2 | #include "panic.h" 3 | #include "pit.h" 4 | 5 | #define PIT_CLOCK_FREQUENCY 1193180 6 | 7 | void 8 | pit_set_frequency(uint32_t hz) 9 | { 10 | uint32_t divisor = PIT_CLOCK_FREQUENCY / hz; 11 | 12 | if(divisor > 0xffff) { 13 | panic("frequency too low"); 14 | } 15 | 16 | outb(0x43, 0x36); 17 | outb(0x40, divisor & 0xff); 18 | outb(0x40, (divisor >> 8) & 0xff); 19 | } 20 | -------------------------------------------------------------------------------- /kernel/src/sched.asm: -------------------------------------------------------------------------------- 1 | use32 2 | 3 | global sched_begin 4 | global sched_switch 5 | global task_fork 6 | 7 | extern current_task 8 | extern sched_next 9 | extern task_fork_inner 10 | 11 | %define USER_CODE (0x18 | 3) 12 | %define USER_DATA (0x20 | 3) 13 | 14 | %define task_fpu_state(task) [(task) + 0] 15 | %define task_esp(task) [(task) + 512] 16 | %define task_eip(task) [(task) + 516] 17 | %define task_kernel_stack(task) [(task) + 520] 18 | %define task_page_dir_phys(task) [(task) + 528] 19 | 20 | sched_begin: 21 | mov ax, USER_DATA 22 | mov ds, ax 23 | mov es, ax 24 | mov fs, ax 25 | mov gs, ax 26 | 27 | mov ecx, 0xffc00000 ; userland stack end 28 | mov edx, 0x10000000 ; task entry point 29 | sti 30 | sysexit 31 | 32 | sched_switch: 33 | ; save old task state 34 | pusha 35 | mov eax, [current_task] 36 | fxsave task_fpu_state(eax) 37 | mov task_esp(eax), esp 38 | mov task_eip(eax), dword .return 39 | 40 | call sched_next 41 | mov [current_task], eax 42 | 43 | mov ebx, task_page_dir_phys(eax) 44 | mov cr3, ebx 45 | 46 | fxrstor task_fpu_state(eax) 47 | mov esp, task_esp(eax) 48 | jmp task_eip(eax) 49 | .return: 50 | popa 51 | ret 52 | 53 | task_fork: 54 | xor eax, eax 55 | 56 | pusha 57 | call task_fork_inner 58 | 59 | ; we need to save the return value of task_fork_inner so that the parent 60 | ; task that calls this function gets the right task_t* for the newly 61 | ; created child task: 62 | mov [esp + 7*4], eax ; EAX from pusha 63 | 64 | mov task_esp(eax), esp 65 | mov task_eip(eax), dword .return 66 | .return: 67 | popa 68 | ret 69 | -------------------------------------------------------------------------------- /kernel/src/string.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | 3 | void 4 | memcpy(void* dest_, void* src_, size_t n) 5 | { 6 | char* dest = dest_; 7 | char* src = src_; 8 | 9 | if(src > dest) { 10 | while(n--) { 11 | *dest++ = *src++; 12 | } 13 | } else { 14 | dest += n; 15 | src += n; 16 | while(n--) { 17 | *--dest = *--src; 18 | } 19 | } 20 | } 21 | 22 | void 23 | memset(void* ptr_, uint8_t c, size_t n) 24 | { 25 | uint8_t* ptr = ptr_; 26 | while(n--) { 27 | *ptr++ = c; 28 | } 29 | } 30 | 31 | void 32 | memset16(void* ptr_, uint16_t w, size_t n) 33 | { 34 | uint16_t* ptr = ptr_; 35 | while(n--) { 36 | *ptr++ = w; 37 | } 38 | } 39 | 40 | void 41 | memset32(void* ptr_, uint32_t l, size_t n) 42 | { 43 | uint32_t* ptr = ptr_; 44 | while(n--) { 45 | *ptr++ = l; 46 | } 47 | } 48 | 49 | bool 50 | streq(const char* a, const char* b) 51 | { 52 | while(1) { 53 | if(*a != *b) { 54 | return false; 55 | } 56 | if(*a == 0) { 57 | return true; 58 | } 59 | a++; 60 | b++; 61 | } 62 | } 63 | 64 | void 65 | strlcpy(char* dest, const char* src, size_t dest_total_size) 66 | { 67 | if(dest_total_size == 0) { 68 | return; 69 | } 70 | 71 | while(*src && dest_total_size > 1) { 72 | *dest++ = *src++; 73 | } 74 | 75 | *dest = 0; 76 | } 77 | 78 | size_t 79 | strlen(const char* str) 80 | { 81 | size_t len = 0; 82 | 83 | while(*str++) { 84 | len++; 85 | } 86 | 87 | return len; 88 | } 89 | -------------------------------------------------------------------------------- /kernel/src/syscall.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "console.h" 4 | #include "paging.h" 5 | #include "panic.h" 6 | #include "sched.h" 7 | #include "syscall.h" 8 | #include "task.h" 9 | #include "util.h" 10 | 11 | #define REG_VECTOR(regs) ((regs)->eax) 12 | #define REG_RETURN(regs) ((regs)->eax) 13 | 14 | #define REG_ARG1(regs) ((regs)->ebx) 15 | #define REG_ARG2(regs) ((regs)->edi) 16 | #define REG_ARG3(regs) ((regs)->esi) 17 | 18 | static bool 19 | valid_user_buffer(virt_t ptr, size_t len) 20 | { 21 | if((0xfffffffful - len) < ptr) { 22 | return false; 23 | } 24 | 25 | size_t page_offset = ptr % PAGE_SIZE; 26 | 27 | ptr -= page_offset; 28 | len += page_offset; 29 | 30 | virt_t end = ptr + len; 31 | 32 | while(ptr < end) { 33 | if(!page_is_user_mapped(ptr)) { 34 | return false; 35 | } 36 | 37 | ptr += PAGE_SIZE; 38 | } 39 | 40 | return true; 41 | } 42 | 43 | static uint32_t 44 | syscall_regdump(registers_t* regs) 45 | { 46 | printf("process %d register dump:\n", current_task->pid); 47 | printf(" eax=%x, ebx=%x, ecx=%x, edx=%x\n", regs->eax, regs->ebx, regs->ecx, regs->edx); 48 | printf(" esp=%x, ebp=%x, esi=%x, edi=%x\n", regs->esp, regs->ebp, regs->esi, regs->edi); 49 | 50 | return 0; 51 | } 52 | 53 | static uint32_t 54 | syscall_exit(registers_t* regs) 55 | { 56 | uint8_t status = REG_ARG1(regs) & 0xff; 57 | 58 | if(current_task->pid == 1) { 59 | panic("init exited with status: %d", status); 60 | } 61 | 62 | task_kill(current_task, status); 63 | 64 | // goodbye! 65 | sched_switch(); 66 | 67 | return 0; 68 | } 69 | 70 | static uint32_t 71 | syscall_yield(registers_t* regs) 72 | { 73 | (void)regs; 74 | sched_switch(); 75 | return 0; 76 | } 77 | 78 | static uint32_t 79 | syscall_fork(registers_t* regs) 80 | { 81 | (void)regs; 82 | 83 | task_t* new_task = task_fork(); 84 | 85 | if(new_task) { 86 | // parent 87 | return new_task->pid; 88 | } else { 89 | // child 90 | return 0; 91 | } 92 | } 93 | 94 | static uint32_t 95 | syscall_wait(registers_t* regs) 96 | { 97 | if(REG_ARG1(regs) != 0 && !valid_user_buffer(REG_ARG1(regs), sizeof(int))) { 98 | return -EFAULT; 99 | } 100 | 101 | int* stat_loc = (int*)REG_ARG1(regs); 102 | 103 | again: 104 | if(current_task->wait_queue.live.head) { 105 | task_t* child = current_task->wait_queue.live.head; 106 | current_task->wait_queue.live.head = child->wait_queue.dead.next; 107 | if(!current_task->wait_queue.live.head) { 108 | current_task->wait_queue.live.tail = NULL; 109 | } 110 | 111 | uint32_t child_pid = child->pid; 112 | uint8_t child_status = child->exit_status; 113 | 114 | task_destroy(child); 115 | 116 | if(stat_loc) { 117 | *stat_loc = child_status; 118 | } 119 | 120 | return child_pid; 121 | } 122 | 123 | current_task->state = TASK_BLOCK_WAIT; 124 | sched_switch(); 125 | goto again; 126 | } 127 | 128 | static uint32_t 129 | syscall_console_log(registers_t* regs) 130 | { 131 | if(valid_user_buffer(REG_ARG1(regs), REG_ARG2(regs))) { 132 | console_puts((const char*)REG_ARG1(regs), REG_ARG2(regs)); 133 | return 0; 134 | } else { 135 | return -EFAULT; 136 | } 137 | } 138 | 139 | typedef uint32_t(syscall_t)(registers_t*); 140 | 141 | static syscall_t* 142 | syscall_table[] = { 143 | [SYS_REGDUMP] = syscall_regdump, 144 | [SYS_EXIT] = syscall_exit, 145 | [SYS_YIELD] = syscall_yield, 146 | [SYS_FORK] = syscall_fork, 147 | [SYS_WAIT] = syscall_wait, 148 | [SYS_CONSOLE_LOG] = syscall_console_log, 149 | }; 150 | 151 | void 152 | syscall_dispatch(registers_t* regs) 153 | { 154 | if(REG_VECTOR(regs) > countof(syscall_table)) { 155 | REG_RETURN(regs) = -ENOSYS; 156 | return; 157 | } 158 | 159 | syscall_t* func = syscall_table[regs->eax]; 160 | 161 | if(!func) { 162 | REG_RETURN(regs) = -ENOSYS; 163 | return; 164 | } 165 | 166 | current_task->syscall_registers = regs; 167 | 168 | REG_RETURN(regs) = func(regs); 169 | 170 | current_task->syscall_registers = NULL; 171 | } 172 | -------------------------------------------------------------------------------- /kernel/src/syscall_entry.asm: -------------------------------------------------------------------------------- 1 | use32 2 | 3 | extern syscall_dispatch 4 | 5 | global syscall_entry 6 | global syscall_init 7 | 8 | %define IA32_SYSENTER_CS 0x174 9 | %define IA32_SYSENTER_ESP 0x175 10 | %define IA32_SYSENTER_EIP 0x176 11 | 12 | %define KERNEL_CODE 0x08 13 | %define KERNEL_DATA 0x10 14 | 15 | syscall_init: 16 | xor edx, edx 17 | 18 | mov ecx, IA32_SYSENTER_CS 19 | mov eax, KERNEL_CODE 20 | wrmsr 21 | 22 | mov ecx, IA32_SYSENTER_ESP 23 | mov eax, 0x0ffffffc 24 | wrmsr 25 | 26 | mov ecx, IA32_SYSENTER_EIP 27 | mov eax, syscall_entry 28 | wrmsr 29 | 30 | ret 31 | 32 | syscall_entry: 33 | pusha 34 | 35 | push dword 0 36 | push dword 0 37 | mov ebp, esp 38 | 39 | lea eax, [esp + 8] 40 | push eax 41 | call syscall_dispatch 42 | add esp, 12 ; 4 bytes for the argument we passed to syscall_dispatch, and 43 | ; another 8 to compensate for the fake stack frame we pushed. 44 | 45 | popa 46 | 47 | ; STI apparently waits one instruction before enabling interrupts, so 48 | ; despite how it appears, this return sequence should be race-free. 49 | sti 50 | sysexit 51 | -------------------------------------------------------------------------------- /kernel/src/task.c: -------------------------------------------------------------------------------- 1 | #include "console.h" 2 | #include "gdt.h" 3 | #include "kernel_page.h" 4 | #include "paging.h" 5 | #include "panic.h" 6 | #include "sched.h" 7 | #include "string.h" 8 | #include "task.h" 9 | #include "util.h" 10 | 11 | static_assert(tss_t_is_0x68_bytes_long, sizeof(tss_t) == 0x68); 12 | 13 | static tss_t 14 | tss; 15 | 16 | task_t* 17 | current_task; 18 | 19 | static task_t* 20 | tasks[1024]; 21 | 22 | task_t* 23 | task_for_pid(uint32_t pid) 24 | { 25 | if(pid > countof(tasks)) { 26 | return NULL; 27 | } 28 | 29 | return tasks[pid]; 30 | } 31 | 32 | static task_t* 33 | alloc_empty_task() 34 | { 35 | for(uint32_t pid = 1; pid < countof(tasks); pid++) { 36 | if(tasks[pid]) { 37 | continue; 38 | } 39 | 40 | tasks[pid] = kernel_page_alloc_zeroed(); 41 | tasks[pid]->state = TASK_READY; 42 | tasks[pid]->pid = pid; 43 | tasks[pid]->wait_queue.live.head = NULL; 44 | tasks[pid]->wait_queue.live.tail = NULL; 45 | return tasks[pid]; 46 | } 47 | 48 | return NULL; 49 | } 50 | 51 | static void 52 | create_skeleton_page_directory(task_t* task) 53 | { 54 | // initialise task page directory 55 | uint32_t* current_page_directory = (uint32_t*)CURRENT_PAGE_DIRECTORY; 56 | uint32_t* task_page_directory = kernel_page_alloc_zeroed(); 57 | 58 | if(!task_page_directory) { 59 | panic("could not allocate page for new task's page directory"); 60 | } 61 | 62 | for(size_t i = 0; i < KERNEL_STACK_BEGIN / (4*1024*1024); i++) { 63 | task_page_directory[i] = current_page_directory[i]; 64 | } 65 | 66 | task->page_directory = task_page_directory; 67 | task->page_directory_phys = virt_to_phys((virt_t)task_page_directory); 68 | task->page_directory[1023] = task->page_directory_phys | PE_PRESENT | PE_READ_WRITE; 69 | 70 | uint32_t* kernel_stack_page_table = kernel_page_alloc_zeroed(); 71 | task->page_directory[KERNEL_STACK_BEGIN / (4*1024*1024)] = virt_to_phys((virt_t)kernel_stack_page_table) | PE_PRESENT | PE_READ_WRITE; 72 | 73 | task->kernel_stack = kernel_page_alloc_zeroed(); 74 | kernel_stack_page_table[1023] = virt_to_phys((virt_t)task->kernel_stack) | PE_PRESENT | PE_READ_WRITE; 75 | } 76 | 77 | void 78 | task_init() 79 | { 80 | gdt_set_tss(GDT_TSS, (uint32_t)&tss, sizeof(tss)); 81 | gdt_reload(); 82 | 83 | tss.ss0 = GDT_KERNEL_DATA; 84 | tss.esp0 = KERNEL_STACK_END; 85 | 86 | // pointer to the IO permission bitmap is beyond the end of the segment 87 | tss.iopb = sizeof(tss); 88 | 89 | __asm__ volatile("ltr ax" :: "a"((uint16_t)GDT_TSS | 3)); 90 | 91 | task_t* init_task = alloc_empty_task(); 92 | create_skeleton_page_directory(init_task); 93 | 94 | // user stack 95 | uint32_t* user_stack_page_table = kernel_page_alloc_zeroed(); 96 | init_task->page_directory[1022] = virt_to_phys((virt_t)user_stack_page_table) | PE_PRESENT | PE_READ_WRITE | PE_USER; 97 | user_stack_page_table[1023] = page_alloc() | PE_PRESENT | PE_READ_WRITE | PE_USER; 98 | 99 | init_task->ppid = 0; 100 | 101 | current_task = init_task; 102 | } 103 | 104 | void 105 | task_boot_init(const char* text, size_t size) 106 | { 107 | task_t* init_task = task_for_pid(1); 108 | 109 | set_page_directory(init_task->page_directory_phys); 110 | 111 | for(size_t i = 0; i < size; i += PAGE_SIZE) { 112 | phys_t page = page_alloc(); 113 | page_map(USER_BEGIN + i, page, PE_PRESENT | PE_USER); 114 | size_t copy_size = size - i; 115 | if(copy_size > PAGE_SIZE) { 116 | copy_size = PAGE_SIZE; 117 | } 118 | memcpy((void*)(USER_BEGIN + i), (void*)(text + i), copy_size); 119 | } 120 | 121 | sched_begin(); 122 | } 123 | 124 | static void 125 | copy_userland_pages(task_t* new_task) 126 | { 127 | uint32_t* current_page_directory = (uint32_t*)CURRENT_PAGE_DIRECTORY; 128 | 129 | for(size_t dir_i = USER_BEGIN / (PAGE_SIZE * 1024); dir_i < 1023; dir_i++) { 130 | if(!current_page_directory[dir_i]) { 131 | continue; 132 | } 133 | 134 | uint32_t* current_page_table = (uint32_t*)(CURRENT_PAGE_TABLE_BASE + dir_i * PAGE_SIZE); 135 | 136 | uint32_t* new_page_table = kernel_page_alloc(); 137 | new_task->page_directory[dir_i] = 138 | virt_to_phys((virt_t)new_page_table) | (current_page_directory[dir_i] & PE_FLAG_MASK); 139 | 140 | for(size_t tab_i = 0; tab_i < 1024; tab_i++) { 141 | uint32_t current_entry = current_page_table[tab_i]; 142 | void* current_virt = (void*)((dir_i * PAGE_SIZE * 1024) + (tab_i * PAGE_SIZE)); 143 | 144 | if(!(current_entry & PE_PRESENT)) { 145 | continue; 146 | } 147 | 148 | phys_t new_page = page_alloc(); 149 | 150 | void* new_page_mapping = page_temp_map(new_page); 151 | memcpy(new_page_mapping, current_virt, PAGE_SIZE); 152 | page_temp_unmap(); 153 | 154 | new_page_table[tab_i] = new_page | (current_entry & PE_FLAG_MASK); 155 | } 156 | } 157 | } 158 | 159 | task_t* 160 | task_fork_inner() 161 | { 162 | task_t* new_task = alloc_empty_task(); 163 | create_skeleton_page_directory(new_task); 164 | 165 | memcpy(&new_task->fpu_state, ¤t_task->fpu_state, sizeof(new_task->fpu_state)); 166 | memcpy(new_task->kernel_stack, current_task->kernel_stack, PAGE_SIZE); 167 | 168 | copy_userland_pages(new_task); 169 | 170 | new_task->ppid = current_task->pid; 171 | 172 | new_task->syscall_registers = current_task->syscall_registers; 173 | 174 | return new_task; 175 | } 176 | 177 | void 178 | task_kill(task_t* task, uint8_t status) 179 | { 180 | task->exit_status = status; 181 | 182 | task_t* parent = task_for_pid(task->ppid); 183 | 184 | // if this task has any children in the wait queue, just clean them up as 185 | // nothing is interested in them anymore. 186 | task_t* waitable_child = task->wait_queue.live.head; 187 | while(waitable_child) { 188 | task_t* next_waitable_child = waitable_child->wait_queue.dead.next; 189 | task_destroy(waitable_child); 190 | waitable_child = next_waitable_child; 191 | } 192 | 193 | // reparent children - TODO don't scan all processes... 194 | for(uint32_t i = 2; i < countof(tasks); i++) { 195 | task_t* child = task_for_pid(i); 196 | if(child && child->ppid == task->pid) { 197 | child->ppid = 1; 198 | } 199 | } 200 | 201 | // insert this task into the parent's wait queue 202 | if(parent->wait_queue.live.tail) { 203 | parent->wait_queue.live.tail->wait_queue.dead.next = task; 204 | parent->wait_queue.live.tail = task; 205 | } else { 206 | parent->wait_queue.live.head = task; 207 | parent->wait_queue.live.tail = task; 208 | } 209 | 210 | task->wait_queue.dead.next = NULL; 211 | 212 | // wake parent up if it's blocked in wait() 213 | if(parent->state == TASK_BLOCK_WAIT) { 214 | parent->state = TASK_READY; 215 | } 216 | 217 | // set state to EXITING so the scheduler never reschedules this task 218 | current_task->state = TASK_EXITING; 219 | } 220 | 221 | void 222 | task_destroy(task_t* task) 223 | { 224 | tasks[task->pid] = NULL; 225 | 226 | kernel_page_free(task->kernel_stack); 227 | 228 | // TODO - free pages allocated for the task that are referenced from its 229 | // page directory 230 | 231 | kernel_page_free(task->page_directory); 232 | kernel_page_free(task); 233 | } 234 | 235 | task_t* 236 | sched_next() 237 | { 238 | uint32_t current_pid = current_task->pid; 239 | 240 | for(size_t i = current_pid + 1; i < countof(tasks); i++) { 241 | task_t* task = task_for_pid(i); 242 | 243 | if(!task) { 244 | continue; 245 | } 246 | 247 | if(task->state == TASK_READY) { 248 | return task; 249 | } 250 | } 251 | 252 | for(size_t i = 1; i <= current_pid; i++) { 253 | task_t* task = task_for_pid(i); 254 | 255 | if(!task) { 256 | continue; 257 | } 258 | 259 | if(task->state == TASK_READY) { 260 | return task; 261 | } 262 | } 263 | 264 | panic("no tasks ready to schedule!"); 265 | } 266 | -------------------------------------------------------------------------------- /mtoolsrc: -------------------------------------------------------------------------------- 1 | drive c: file="hdd.img" partition=1 mtools_skip_check=1 2 | -------------------------------------------------------------------------------- /provision.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -xe 3 | 4 | sudo apt-get update 5 | 6 | # essential toolchain stuff 7 | sudo apt-get install -y build-essential nasm git mtools 8 | 9 | # development niceties 10 | sudo apt-get install -y vim gdb 11 | 12 | # get a decent shell prompt going 13 | cat > ~/.bash_profile </dev/null | grep '^*' | cut -b2- 16 | } 17 | export PS1=$'\e[33;1m\w\e[36m\$(current-branch) \e[32mλ\e[0m ' 18 | SH 19 | -------------------------------------------------------------------------------- /user/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = \ 2 | -Wall -Wextra -pedantic -Werror -nostdlib -nostdinc -fno-builtin \ 3 | -nostartfiles -nodefaultlibs -fno-exceptions -ffreestanding \ 4 | -fno-stack-protector -fno-pic -std=c99 -m32 -masm=intel \ 5 | -I ../api -fleading-underscore 6 | 7 | all: init.bin 8 | 9 | %.bin: crt0.o crt1.o %.o 10 | @echo ld $@ 11 | @ld -o $@ -T linker.ld $^ 12 | 13 | crt0.o: crt0.asm 14 | @echo nasm $@ 15 | @nasm -felf -o $@ $< 16 | 17 | %.o: %.c 18 | @echo cc $@ 19 | @gcc -o $@ $(CFLAGS) -c $< 20 | -------------------------------------------------------------------------------- /user/crt.h: -------------------------------------------------------------------------------- 1 | #ifndef CRT_H 2 | #define CRT_H 3 | 4 | typedef unsigned int uint32_t; 5 | 6 | #define NULL ((void*)0) 7 | 8 | uint32_t 9 | _syscall0(uint32_t number); 10 | 11 | uint32_t 12 | _syscall1(uint32_t number, uint32_t arg1); 13 | 14 | uint32_t 15 | _syscall2(uint32_t number, uint32_t arg1, uint32_t arg2); 16 | 17 | uint32_t 18 | _syscall3(uint32_t number, uint32_t arg1, uint32_t arg2, uint32_t arg3); 19 | 20 | void 21 | regdump(); 22 | 23 | void 24 | exit(int status); 25 | 26 | void 27 | yield(); 28 | 29 | uint32_t 30 | fork(); 31 | 32 | uint32_t 33 | wait(int* stat_loc); 34 | 35 | void 36 | console_log(const char* str); 37 | 38 | int 39 | main(); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /user/crt0.asm: -------------------------------------------------------------------------------- 1 | use32 2 | 3 | global __syscall0 4 | global __syscall1 5 | global __syscall2 6 | global __syscall3 7 | 8 | extern _main 9 | extern _exit 10 | 11 | section .crt0 12 | 13 | %macro perform_syscall 0 14 | push ecx 15 | push edx 16 | mov ecx, esp 17 | mov edx, .ret 18 | sysenter 19 | .ret: 20 | pop edx 21 | pop ecx 22 | %endmacro 23 | 24 | start: 25 | call _main 26 | push eax 27 | call _exit 28 | 29 | __syscall0: 30 | mov eax, [esp+0+4] 31 | perform_syscall 32 | ret 33 | 34 | __syscall1: 35 | push ebx 36 | mov ebx, [esp+4+8] 37 | mov eax, [esp+4+4] 38 | perform_syscall 39 | pop ebx 40 | ret 41 | 42 | __syscall2: 43 | push ebx 44 | push edi 45 | mov edi, [esp+8+12] 46 | mov ebx, [esp+8+8] 47 | mov eax, [esp+8+4] 48 | perform_syscall 49 | pop edi 50 | pop ebx 51 | ret 52 | 53 | __syscall3: 54 | push ebx 55 | push edi 56 | push esi 57 | mov esi, [esp+12+16] 58 | mov edi, [esp+12+12] 59 | mov ebx, [esp+12+8] 60 | mov eax, [esp+12+4] 61 | perform_syscall 62 | pop esi 63 | pop edi 64 | pop ebx 65 | ret 66 | -------------------------------------------------------------------------------- /user/crt1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "crt.h" 3 | 4 | void 5 | regdump() 6 | { 7 | _syscall0(SYS_REGDUMP); 8 | } 9 | 10 | void 11 | exit(int status) 12 | { 13 | _syscall1(SYS_EXIT, status); 14 | } 15 | 16 | void 17 | yield() 18 | { 19 | _syscall0(SYS_YIELD); 20 | } 21 | 22 | uint32_t 23 | fork() 24 | { 25 | return _syscall0(SYS_FORK); 26 | } 27 | 28 | uint32_t 29 | wait(int* stat_loc) 30 | { 31 | return _syscall1(SYS_WAIT, (uint32_t)stat_loc); 32 | } 33 | 34 | void 35 | console_log(const char* str) 36 | { 37 | uint32_t len = 0; 38 | 39 | for(const char* p = str; *p; p++) { 40 | len++; 41 | } 42 | 43 | _syscall2(SYS_CONSOLE_LOG, (uint32_t)str, len); 44 | } 45 | -------------------------------------------------------------------------------- /user/init.c: -------------------------------------------------------------------------------- 1 | #include "crt.h" 2 | 3 | int 4 | main() 5 | { 6 | console_log("Hello world from init!\n"); 7 | 8 | if(fork()) { 9 | wait(NULL); 10 | return 123; 11 | } else { 12 | regdump(); 13 | yield(); 14 | regdump(); 15 | return 456; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /user/linker.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("binary") 2 | 3 | SECTIONS { 4 | . = 0x10000000; 5 | 6 | .text : { 7 | *(.crt0) 8 | *(.text) 9 | } 10 | 11 | .rodata ALIGN (0x1000) : { 12 | *(.rodata) 13 | } 14 | 15 | .data ALIGN (0x1000) : { 16 | *(.data) 17 | } 18 | 19 | .bss : { 20 | sbss = .; 21 | *(COMMON) 22 | *(.bss) 23 | ebss = .; 24 | } 25 | } 26 | --------------------------------------------------------------------------------