├── .gitignore ├── src ├── apps │ ├── .gitignore │ ├── link.ld │ ├── Makefile │ ├── init.c │ └── sh.c ├── libc │ ├── .gitignore │ ├── unistd.h │ ├── stddef.h │ ├── stdint.h │ ├── sys │ │ └── syscall.h │ ├── start.s │ ├── string.h │ ├── Makefile │ ├── unistd.s │ └── string.c ├── kernel │ ├── .gitignore │ ├── idt.h │ ├── stddef.h │ ├── vnode.c │ ├── vattr.h │ ├── idt_asm.s │ ├── mem.h │ ├── pit.h │ ├── io.h │ ├── keyboard.h │ ├── tss_asm.s │ ├── devfs.h │ ├── gdt.h │ ├── kmalloc.h │ ├── stdint.h │ ├── math.h │ ├── log.h │ ├── tss.c │ ├── common.h │ ├── serial.h │ ├── mem.c │ ├── math.c │ ├── stdarg.h │ ├── kernel.h │ ├── string.h │ ├── pic.h │ ├── fb.h │ ├── page_frame_allocator.h │ ├── paging_asm.s │ ├── c_to_nasm.sh │ ├── stdio.h │ ├── scheduler.h │ ├── link.ld │ ├── io.s │ ├── constants.h │ ├── gdt_asm.s │ ├── vfs.h │ ├── vnode.h │ ├── Makefile │ ├── stdio.c │ ├── pit.c │ ├── interrupt.c │ ├── paging.h │ ├── interrupt.h │ ├── tss.h │ ├── pic.c │ ├── aefs.h │ ├── process.h │ ├── string.c │ ├── module.c │ ├── scheduler_asm.s │ ├── vfs.c │ ├── interrupt_asm.s │ ├── log.c │ ├── devfs.c │ ├── loader.s │ ├── serial.c │ ├── kmalloc.c │ ├── gdt.c │ ├── idt.c │ ├── fb.c │ ├── aefs.c │ ├── kmain.c │ ├── multiboot.h │ ├── syscall.c │ ├── scheduler.c │ ├── page_frame_allocator.c │ ├── keyboard.c │ └── paging.c ├── .gitignore ├── run_bochs.sh ├── Makefile ├── create_iso.sh ├── rdaefs.c └── mkfs.c ├── doc ├── .gitignore ├── installing_bochs.md ├── proposal.tex └── diary.md ├── tools └── grub │ └── stage2_eltorito └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | doxygen_doc 2 | -------------------------------------------------------------------------------- /src/apps/.gitignore: -------------------------------------------------------------------------------- 1 | init 2 | sh 3 | -------------------------------------------------------------------------------- /src/libc/.gitignore: -------------------------------------------------------------------------------- 1 | libc.a 2 | *.o 3 | -------------------------------------------------------------------------------- /src/kernel/.gitignore: -------------------------------------------------------------------------------- 1 | kernel.elf 2 | *.o 3 | *.inc 4 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | *.pdf 3 | *.aux 4 | *.out 5 | *.html 6 | -------------------------------------------------------------------------------- /tools/grub/stage2_eltorito: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/littleosbook/aenix/HEAD/tools/grub/stage2_eltorito -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | aenix.iso 2 | bochsrc.txt 3 | bochslog.txt 4 | com1.out 5 | mkfs 6 | rdaefs 7 | fs_root 8 | fs 9 | -------------------------------------------------------------------------------- /src/libc/unistd.h: -------------------------------------------------------------------------------- 1 | #ifndef UNISTD_H 2 | #define UNISTD_H 3 | 4 | int syscall(int number, ...); 5 | 6 | #endif /* UNISTD_H */ 7 | -------------------------------------------------------------------------------- /src/kernel/idt.h: -------------------------------------------------------------------------------- 1 | #ifndef IDT_H 2 | #define IDT_H 3 | 4 | #define IDT_NUM_ENTRIES 256 5 | 6 | void idt_init(void); 7 | 8 | #endif /* IDT_H */ 9 | -------------------------------------------------------------------------------- /src/libc/stddef.h: -------------------------------------------------------------------------------- 1 | #ifndef STDDEF_H 2 | #define STDDEF_H 3 | 4 | #define NULL ((void *)0) 5 | 6 | typedef unsigned int size_t; 7 | 8 | #endif /* STDDEF_H */ 9 | -------------------------------------------------------------------------------- /src/kernel/stddef.h: -------------------------------------------------------------------------------- 1 | #ifndef STDDEF_H 2 | #define STDDEF_H 3 | 4 | #define NULL ((void *)0) 5 | 6 | typedef unsigned int size_t; 7 | 8 | #endif /* STDDEF_H */ 9 | -------------------------------------------------------------------------------- /src/kernel/vnode.c: -------------------------------------------------------------------------------- 1 | #include "vnode.h" 2 | 3 | void vnode_copy(vnode_t *from, vnode_t *to) 4 | { 5 | to->v_op = from->v_op; 6 | to->v_data = from->v_data; 7 | } 8 | -------------------------------------------------------------------------------- /src/kernel/vattr.h: -------------------------------------------------------------------------------- 1 | #ifndef VATTR_H 2 | #define VATTR_H 3 | 4 | struct vattr { 5 | uint32_t file_size; 6 | }; 7 | 8 | typedef struct vattr vattr_t; 9 | 10 | #endif /* VATTR_H */ 11 | -------------------------------------------------------------------------------- /src/kernel/idt_asm.s: -------------------------------------------------------------------------------- 1 | global idt_load_and_set 2 | 3 | section .text 4 | 5 | idt_load_and_set: 6 | mov eax, [esp+4] ; move ldt_ptr to eax 7 | lidt [eax] ; load the idt table stored in eax 8 | ret 9 | -------------------------------------------------------------------------------- /src/kernel/mem.h: -------------------------------------------------------------------------------- 1 | #ifndef MEM_H 2 | #define MEM_H 3 | 4 | #include "stdint.h" 5 | 6 | uint32_t align_up(uint32_t n, uint32_t a); 7 | uint32_t align_down(uint32_t n, uint32_t a); 8 | 9 | #endif /* MEM_H */ 10 | -------------------------------------------------------------------------------- /src/kernel/pit.h: -------------------------------------------------------------------------------- 1 | #ifndef PIT_H 2 | #define PIT_H 3 | 4 | #include "stdint.h" 5 | 6 | void pit_init(void); 7 | /* interval is in ms */ 8 | void pit_set_interval(uint32_t interval); 9 | 10 | #endif /* PIT_H */ 11 | -------------------------------------------------------------------------------- /src/kernel/io.h: -------------------------------------------------------------------------------- 1 | #ifndef IO_H 2 | #define IO_H 3 | 4 | #include "stdint.h" 5 | 6 | void outb(uint16_t port, uint8_t value); 7 | uint8_t inb(uint16_t port); 8 | uint16_t inw(uint16_t port); 9 | 10 | #endif /* IO_H */ 11 | -------------------------------------------------------------------------------- /src/kernel/keyboard.h: -------------------------------------------------------------------------------- 1 | #ifndef KEYBOARD_H 2 | #define KEYBOARD_H 3 | 4 | #include "stdint.h" 5 | #include "vnode.h" 6 | 7 | uint32_t kbd_init(void); 8 | int kbd_get_vnode(vnode_t *out); 9 | 10 | #endif /* KEYBOARD_H */ 11 | -------------------------------------------------------------------------------- /src/kernel/tss_asm.s: -------------------------------------------------------------------------------- 1 | global tss_load_and_set 2 | 3 | section .text 4 | tss_load_and_set: 5 | mov ax, [esp+4] ; mov the tss segsel into ax (16 bits) 6 | ltr ax ; load the task register with the selector 7 | ret 8 | -------------------------------------------------------------------------------- /src/kernel/devfs.h: -------------------------------------------------------------------------------- 1 | #ifndef DEVFS_H 2 | #define DEVFS_H 3 | 4 | #include "vfs.h" 5 | #include "vnode.h" 6 | 7 | int devfs_init(vfs_t *vfs); 8 | int devfs_add_device(char const *path, vnode_t *node); 9 | 10 | #endif /* DEVFS_H */ 11 | -------------------------------------------------------------------------------- /src/kernel/gdt.h: -------------------------------------------------------------------------------- 1 | #ifndef GDT_H 2 | #define GDT_H 3 | 4 | #include "stdint.h" 5 | #include "constants.h" 6 | 7 | #define PL0 0x0 8 | #define PL3 0x3 9 | 10 | void gdt_init(uint32_t tss_vaddr); 11 | 12 | #endif /* GDT_H */ 13 | 14 | -------------------------------------------------------------------------------- /src/libc/stdint.h: -------------------------------------------------------------------------------- 1 | #ifndef STDINT_H 2 | #define STDINT_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 | #endif /* STDINT_H */ 10 | -------------------------------------------------------------------------------- /src/kernel/kmalloc.h: -------------------------------------------------------------------------------- 1 | #ifndef KMALLOC_H 2 | #define KMALLOC_H 3 | 4 | #include "stddef.h" 5 | #include "stdint.h" 6 | 7 | void kmalloc_init(uint32_t addr); 8 | void *kmalloc(size_t); 9 | void kfree(void *); 10 | 11 | #endif /* KMALLOC_H */ 12 | -------------------------------------------------------------------------------- /src/kernel/stdint.h: -------------------------------------------------------------------------------- 1 | #ifndef STDINT_H 2 | #define STDINT_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 | #endif /* STDINT_H */ 10 | -------------------------------------------------------------------------------- /src/kernel/math.h: -------------------------------------------------------------------------------- 1 | #ifndef MATH_H 2 | #define MATH_H 3 | 4 | #include "stdint.h" 5 | 6 | uint32_t div_ceil(uint32_t n, uint32_t d); 7 | uint32_t minu(uint32_t a, uint32_t b); 8 | uint32_t maxu(uint32_t a, uint32_t b); 9 | 10 | #endif /* MATH_H */ 11 | -------------------------------------------------------------------------------- /src/kernel/log.h: -------------------------------------------------------------------------------- 1 | #ifndef LOG_H 2 | #define LOG_H 3 | 4 | #include "stdarg.h" 5 | 6 | void log_debug(char *fname, char *fmt, ...); 7 | void log_info(char *fname, char *fmt, ...); 8 | void log_error(char *fname, char *fmt, ...); 9 | 10 | #endif /* LOG_H */ 11 | -------------------------------------------------------------------------------- /src/kernel/tss.c: -------------------------------------------------------------------------------- 1 | #include "tss.h" 2 | #include "log.h" 3 | 4 | static tss_t tss; 5 | 6 | uint32_t tss_init() 7 | { 8 | return (uint32_t) &tss; 9 | } 10 | 11 | void tss_set_kernel_stack(uint16_t segsel, uint32_t vaddr) 12 | { 13 | tss.esp0 = vaddr; 14 | tss.ss0 = segsel; 15 | } 16 | -------------------------------------------------------------------------------- /src/kernel/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | #include "constants.h" 5 | 6 | #define UNUSED_ARGUMENT(x) (void) x; 7 | 8 | #define PHYSICAL_TO_VIRTUAL(addr) ((addr)+KERNEL_START_VADDR) 9 | 10 | #define NEXT_ADDR(addr) ((addr) + (4 - ((addr) % 4))) 11 | 12 | #endif /* COMMON_H */ 13 | -------------------------------------------------------------------------------- /src/kernel/serial.h: -------------------------------------------------------------------------------- 1 | #ifndef SERIAL_H 2 | #define SERIAL_H 3 | 4 | #include "stdint.h" 5 | 6 | #define COM1 0x3F8 7 | #define COM2 0x2F8 8 | 9 | void serial_init(uint16_t com); 10 | void serial_write(uint16_t com, uint8_t data); 11 | uint8_t serial_read(uint16_t com); 12 | 13 | #endif /* SERIAL_H */ 14 | -------------------------------------------------------------------------------- /src/kernel/mem.c: -------------------------------------------------------------------------------- 1 | #include "mem.h" 2 | 3 | uint32_t align_up(uint32_t n, uint32_t a) 4 | { 5 | uint32_t m = n % a; 6 | if (m == 0) { 7 | return n; 8 | } 9 | return n + (a - m); 10 | } 11 | 12 | uint32_t align_down(uint32_t n, uint32_t a) 13 | { 14 | return n - (n % a); 15 | } 16 | -------------------------------------------------------------------------------- /src/libc/sys/syscall.h: -------------------------------------------------------------------------------- 1 | #ifndef SYSCALL_H 2 | #define SYSCALL_H 3 | 4 | #define SYS_open 0 5 | #define SYS_read 1 6 | #define SYS_write 2 7 | #define SYS_execve 3 8 | #define SYS_fork 4 9 | #define SYS_yield 5 10 | #define SYS_exit 6 11 | #define SYS_wait 7 12 | 13 | #endif /* SYSCALL_H */ 14 | -------------------------------------------------------------------------------- /src/kernel/math.c: -------------------------------------------------------------------------------- 1 | #include "math.h" 2 | 3 | uint32_t div_ceil(uint32_t num, uint32_t den) 4 | { 5 | return (num - 1) / den + 1; 6 | } 7 | 8 | uint32_t minu(uint32_t a, uint32_t b) 9 | { 10 | return a < b ? a : b; 11 | } 12 | 13 | uint32_t maxu(uint32_t a, uint32_t b) 14 | { 15 | return a > b ? a : b; 16 | } 17 | -------------------------------------------------------------------------------- /src/apps/link.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("binary") 2 | 3 | SECTIONS 4 | { 5 | . = 0; 6 | 7 | .text ALIGN(4): 8 | { 9 | start.o(.text) 10 | *(.text) 11 | } 12 | 13 | .data ALIGN(4): 14 | { 15 | *(.data) 16 | } 17 | 18 | .rodata ALIGN(4): 19 | { 20 | *(.rodata*) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/libc/start.s: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | extern main 4 | extern syscall 5 | 6 | SYS_exit equ 6 7 | 8 | section .text 9 | align 4 10 | call main 11 | push eax ; eax is the status code from main, send it to exit 12 | push SYS_exit 13 | call syscall 14 | jmp $ ; we'll never get here, but if, loop indefinitely 15 | -------------------------------------------------------------------------------- /src/kernel/stdarg.h: -------------------------------------------------------------------------------- 1 | #ifndef STDARG_H 2 | #define STDARG_H 3 | 4 | /* Uses GCC builtin implementations for all the variadic argument macros */ 5 | #define va_start(v, l) __builtin_va_start(v, l) 6 | #define va_arg(v,l) __builtin_va_arg(v, l) 7 | #define va_end(v) __builtin_va_end(v) 8 | #define va_copy(d, s) __builtin_va_copy(d, s) 9 | typedef __builtin_va_list va_list; 10 | 11 | #endif /* STDARG_H */ 12 | -------------------------------------------------------------------------------- /src/kernel/kernel.h: -------------------------------------------------------------------------------- 1 | #ifndef KERNEL_H 2 | #define KERNEL_H 3 | 4 | #include "stdint.h" 5 | 6 | #define KERNEL_VIRTUAL_ADDRESS 0xC040000 7 | 8 | struct kernel_meminfo { 9 | uint32_t kernel_physical_start; 10 | uint32_t kernel_physical_end; 11 | uint32_t kernel_virtual_start; 12 | uint32_t kernel_virtual_end; 13 | } __attribute__((packed)); 14 | typedef struct kernel_meminfo kernel_meminfo_t; 15 | 16 | #endif /* KERNEL_H */ 17 | -------------------------------------------------------------------------------- /src/kernel/string.h: -------------------------------------------------------------------------------- 1 | #ifndef STRING_H 2 | #define STRING_H 3 | 4 | #include "stddef.h" 5 | 6 | void *memset(void *s, int c, size_t n); 7 | void *memcpy(void *dest, const void *src, size_t n); 8 | int strcmp(const char *s1, const char *s2); 9 | int strncmp(const char *s1, const char *s2, size_t n); 10 | size_t strlen(const char *s); 11 | size_t strcspn(const char *s, const char *reject); 12 | char *strchr(const char *s, int c); 13 | 14 | #endif /* STRING_H */ 15 | -------------------------------------------------------------------------------- /src/libc/string.h: -------------------------------------------------------------------------------- 1 | #ifndef STRING_H 2 | #define STRING_H 3 | 4 | #include "stddef.h" 5 | 6 | void *memset(void *s, int c, size_t n); 7 | void *memcpy(void *dest, const void *src, size_t n); 8 | int strcmp(const char *s1, const char *s2); 9 | int strncmp(const char *s1, const char *s2, size_t n); 10 | size_t strlen(const char *s); 11 | size_t strcspn(const char *s, const char *reject); 12 | char *strchr(const char *s, int c); 13 | 14 | #endif /* STRING_H */ 15 | -------------------------------------------------------------------------------- /src/libc/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector \ 3 | -nostartfiles -nodefaultlibs -Wall -Wextra -Werror \ 4 | -Wno-unused-function -c 5 | AS = nasm 6 | ASFLAGS = -f elf 7 | OBJECTS = unistd.o start.o string.o 8 | 9 | all: libc.a 10 | 11 | libc.a: $(OBJECTS) 12 | ar rcs libc.a $(OBJECTS) 13 | 14 | %.o: %.c 15 | $(CC) $(CFLAGS) $< -o $@ 16 | 17 | %.o: %.s 18 | $(AS) $(ASFLAGS) $< -o $@ 19 | 20 | clean: 21 | rm -rf *.a *.o 22 | -------------------------------------------------------------------------------- /src/kernel/pic.h: -------------------------------------------------------------------------------- 1 | #ifndef PIC_H 2 | #define PIC_H 3 | 4 | #include "stdint.h" 5 | 6 | #define PIC1_START 0x20 7 | #define PIC2_START 0x28 8 | #define PIC_NUM_IRQS 16 9 | 10 | #define PIT_INT_IDX PIC1_START 11 | #define KBD_INT_IDX PIC1_START + 1 12 | 13 | #define COM1_INT_IDX PIC1_START + 4 14 | #define COM2_INT_IDX PIC1_START + 3 15 | 16 | void pic_init(void); 17 | void pic_acknowledge(void); 18 | void pic_mask(uint8_t mask1, uint8_t mask2); 19 | 20 | #endif /* PIC_H */ 21 | -------------------------------------------------------------------------------- /src/libc/unistd.s: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | global syscall 4 | 5 | section .text 6 | align 4 7 | ; syscall 8 | ; - Performs a syscall, the stack has to be set up in advance. If the C 9 | ; syscall declaration is used in unistd.h, the stack will be correct 10 | ; due to the cdecl calling convention 11 | syscall: 12 | add esp, 4 ; do not send the return address to the kernel 13 | int 0xAE ; trap into the kernel 14 | sub esp, 4 ; restore the return address (given that kernel didn't fuck up) 15 | ret 16 | -------------------------------------------------------------------------------- /src/kernel/fb.h: -------------------------------------------------------------------------------- 1 | #ifndef FB_H 2 | #define FB_H 3 | 4 | #include "stdint.h" 5 | #include "vnode.h" 6 | 7 | void fb_put_b(uint8_t b); 8 | void fb_put_s(char const *s); 9 | 10 | /* output unsigned integer in decimal format */ 11 | void fb_put_ui(uint32_t i); 12 | /* output unsigned integer in hexadecimal format */ 13 | void fb_put_ui_hex(uint32_t i); 14 | 15 | void fb_clear(); 16 | void fb_move_cursor(uint16_t row, uint16_t col); 17 | 18 | int fb_init(void); 19 | int fb_get_vnode(vnode_t *out); 20 | 21 | #endif /* FB_H */ 22 | -------------------------------------------------------------------------------- /src/kernel/page_frame_allocator.h: -------------------------------------------------------------------------------- 1 | #ifndef PAGE_FRAME_ALLOCATOR_H 2 | #define PAGE_FRAME_ALLOCATOR_H 3 | 4 | #include "stdint.h" 5 | #include "kernel.h" 6 | #include "multiboot.h" 7 | 8 | uint32_t pfa_init(multiboot_info_t const *mbinfo, 9 | kernel_meminfo_t const *mem, 10 | uint32_t fs_paddr, uint32_t fs_size); 11 | uint32_t pfa_allocate(uint32_t num_page_frames); 12 | void pfa_free(uint32_t paddr); 13 | void pfa_free_cont(uint32_t paddr, uint32_t n); 14 | 15 | #endif /* PAGE_FRAME_ALLOCATOR_H */ 16 | -------------------------------------------------------------------------------- /doc/installing_bochs.md: -------------------------------------------------------------------------------- 1 | % How to install bochs 2 | % Erik Helin, Adam Renberg 3 | % 25 January, 2012 4 | 5 | How to install bochs 6 | ==================== 7 | 8 | 1. Grab the soruce for bochs 2.5.1 from [sourceforge][sf] 9 | 2. Extract the source with `tar xvzf bochs-2.5.1.tar.gz` 10 | 3. Install the package `xorg-dev` with 11 | sudo apt-get install xorg-dev 12 | 4. Configure with `./configure --enable-gdb-stub` to enable GDB support 13 | 5. Compile with `make` 14 | 15 | [sf]: http://sourceforge.net/projects/bochs/files/bochs/2.5.1/ 16 | -------------------------------------------------------------------------------- /src/kernel/paging_asm.s: -------------------------------------------------------------------------------- 1 | global pdt_set 2 | global invalidate_page_table_entry 3 | 4 | section .text: 5 | 6 | pdt_set: 7 | mov eax, [esp+4] ; loads the address of the pdt into eax 8 | and eax, 0xFFFFF000 ; we only care about the highest 20 bits 9 | or eax, 0x08 ; we wan't page write through! PWT FTW! 10 | mov cr3, eax ; loads the PDT 11 | ret 12 | 13 | invalidate_page_table_entry: 14 | mov eax, [esp+4] ; loads the virtual address which page table entry 15 | ; will be flushed 16 | invlpg [eax] 17 | ret 18 | -------------------------------------------------------------------------------- /src/kernel/c_to_nasm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Translates a C header to a NASM header 4 | # The C header can only make use of: 5 | # - #define 6 | # - #ifndef 7 | # - #endif 8 | 9 | # INPUT: A list of the required NASM headers 10 | 11 | set -e 12 | 13 | for NASM_HEADER in $@ 14 | do 15 | C_HEADER="${NASM_HEADER%%.inc}.h" 16 | if [ $C_HEADER -nt $NASM_HEADER ]; then 17 | sed 's/\/\*/;/' $C_HEADER | # change start of comments 18 | sed 's/\*\///' | # change end of comments 19 | sed 's/^#/%/' > $NASM_HEADER 20 | fi 21 | done 22 | -------------------------------------------------------------------------------- /src/kernel/stdio.h: -------------------------------------------------------------------------------- 1 | #ifndef STDIO_H 2 | #define STDIO_H 3 | 4 | /** 5 | * Prints a formatted string to the framebuffer. 6 | * The current supported types are: 7 | * - \c %u: Prints an \c uint32_t or an \c uint8_t 8 | * - \c %X: Prints an \c uint32_t as a hexadecimal number in capital letters 9 | * - \c %s: Prints a \c char* 10 | * - \c %: Prints the character \c % 11 | * 12 | * @param fmt The format string describing how the argument should be printed. 13 | * @param ... A variadic argument list with one argument for each description 14 | */ 15 | void printf(char *fmt, ...); 16 | 17 | #endif /* STDIO_H */ 18 | -------------------------------------------------------------------------------- /src/apps/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector \ 3 | -nostartfiles -nodefaultlibs -Wall -Wextra -Werror \ 4 | -Wno-unused-function -c 5 | AS = nasm 6 | ASFLAGS = -f elf 7 | LD = ld 8 | LDFLAGS = -T link.ld -melf_i386 9 | LIBC = -L../libc -lc 10 | STDINCLUDE = -I../libc 11 | 12 | all: init sh 13 | 14 | init: init.o 15 | $(LD) $(LDFLAGS) init.o $(LIBC) -o init 16 | 17 | sh: sh.o 18 | $(LD) $(LDFLAGS) sh.o $(LIBC) -o sh 19 | 20 | %.o: %.c 21 | $(CC) $(CFLAGS) $(STDINCLUDE) $< -o $@ 22 | 23 | %.o: %.s 24 | $(AS) $(ASFLAGS) $< -o $@ 25 | 26 | clean: 27 | rm -rf init *.o 28 | -------------------------------------------------------------------------------- /src/kernel/scheduler.h: -------------------------------------------------------------------------------- 1 | #ifndef SCHEDULER_H 2 | #define SCHEDULER_H 3 | 4 | #include "stdint.h" 5 | #include "process.h" 6 | 7 | uint32_t scheduler_next_pid(void); 8 | 9 | int scheduler_init(void); 10 | 11 | int scheduler_add_runnable_process(ps_t *ps); 12 | int scheduler_replace_process(ps_t *old, ps_t *new); 13 | void scheduler_terminate_process(ps_t *ps); 14 | int scheduler_has_any_child_terminated(ps_t *parent); 15 | int scheduler_num_children(uint32_t pid); 16 | 17 | void scheduler_schedule(void); 18 | ps_t *scheduler_get_current_process(); 19 | 20 | void snapshot_and_schedule(registers_t *current); 21 | 22 | #endif /* SCHEDULER_H */ 23 | -------------------------------------------------------------------------------- /src/apps/init.c: -------------------------------------------------------------------------------- 1 | #include "unistd.h" 2 | #include "string.h" 3 | #include "sys/syscall.h" 4 | 5 | int main(void) 6 | { 7 | int pid; 8 | 9 | /* open the devices for standard file descriptors */ 10 | syscall(SYS_open, "/dev/keyboard"); /* STDIN */ 11 | syscall(SYS_open, "/dev/console"); /* STDOUT */ 12 | syscall(SYS_open, "/dev/console"); /* STDERR */ 13 | 14 | /* start the shell */ 15 | pid = syscall(SYS_fork); 16 | if (pid == 0) { 17 | syscall(SYS_execve, "/bin/sh"); 18 | } else { 19 | while (syscall(SYS_wait) != -1) { 20 | } 21 | /* TODO: shut down the kernel */ 22 | while (1) {} 23 | } 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /src/kernel/link.ld: -------------------------------------------------------------------------------- 1 | ENTRY(loader) 2 | 3 | SECTIONS 4 | { 5 | . = 0xC0100000; 6 | kernel_virtual_start = .; 7 | kernel_physical_start = . - 0xC0000000; 8 | 9 | .text ALIGN (0x1000) : AT(ADDR(.text)-0xC0000000) 10 | { 11 | *(.text) 12 | } 13 | 14 | .rodata ALIGN (0x1000) : AT(ADDR(.rodata)-0xC0000000) 15 | { 16 | *(.rodata*) 17 | } 18 | 19 | .data ALIGN (0x1000) : AT(ADDR(.data)-0xC0000000) 20 | { 21 | *(.data) 22 | } 23 | 24 | .bss ALIGN (0x1000) : AT(ADDR(.bss)-0xC0000000) 25 | { 26 | *(COMMON) 27 | *(.bss) 28 | } 29 | 30 | kernel_virtual_end = .; 31 | kernel_physical_end = . - 0xC0000000; 32 | } 33 | -------------------------------------------------------------------------------- /src/kernel/io.s: -------------------------------------------------------------------------------- 1 | global outb ; for sending a byte to a port 2 | global inb ; for reading a byte from a port 3 | global inw ; for reading a word from port 4 | 5 | section .text: 6 | 7 | outb: 8 | mov dx, [esp+4] ; the port to send data to 9 | mov al, [esp+8] ; the data to send 10 | out dx, al ; send the contents of al to the port in cx 11 | ret 12 | 13 | inb: 14 | mov dx, [esp+4] ; the port to read from 15 | in al, dx ; reads a byte from dx into al 16 | ret ; returns the read byte 17 | 18 | inw: 19 | mov dx, [esp+4] ; the port to read from 20 | in ax, dx ; read a word from dx into ax 21 | ret ; returns the read word 22 | -------------------------------------------------------------------------------- /src/kernel/constants.h: -------------------------------------------------------------------------------- 1 | #ifndef CONSTANTS_H 2 | #define CONSTANTS_H 3 | 4 | /* numeric contants */ 5 | #define FOUR_KB 0x1000 6 | #define ONE_MB 0x100000 7 | #define FOUR_MB 0x400000 8 | #define EIGHT_MB 0x800000 9 | 10 | /* virtual memory */ 11 | #define KERNEL_START_VADDR 0xC0000000 12 | #define KERNEL_PDT_IDX (KERNEL_START_VADDR >> 22) 13 | 14 | /* kernel stack */ 15 | #define KERNEL_STACK_SIZE FOUR_KB 16 | 17 | /* interrupts */ 18 | #define SYSCALL_INT_IDX 0xAE 19 | 20 | /* segements */ 21 | #define SEGSEL_KERNEL_CS 0x08 22 | #define SEGSEL_KERNEL_DS 0x10 23 | #define SEGSEL_USER_SPACE_CS 0x18 24 | #define SEGSEL_USER_SPACE_DS 0x20 25 | 26 | /* registers */ 27 | #define REG_EFLAGS_DEFAULT 0x202 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/kernel/gdt_asm.s: -------------------------------------------------------------------------------- 1 | ; these are functions dealing with the gdt and idt 2 | ; see also descriptor_tables.[c,h] 3 | 4 | global gdt_load_and_set 5 | 6 | SEGSEL_KERNEL_CS equ 0x08 7 | SEGSEL_KERNEL_DS equ 0x10 8 | 9 | section .text 10 | 11 | ; load the gdt into the cpu, and enter the kernel segments 12 | gdt_load_and_set: 13 | mov eax, [esp+4] ; fetch gdt_ptr from parameter stack 14 | lgdt [eax] ; load gdt table 15 | 16 | ; load cs segment by doing a far jump 17 | jmp SEGSEL_KERNEL_CS:.reload_segments 18 | 19 | .reload_segments: 20 | ; we only use one segment for data 21 | mov ax, SEGSEL_KERNEL_DS 22 | mov ds, ax 23 | mov ss, ax 24 | mov es, ax 25 | mov gs, ax 26 | mov fs, ax 27 | ret 28 | -------------------------------------------------------------------------------- /src/kernel/vfs.h: -------------------------------------------------------------------------------- 1 | #ifndef VFS_H 2 | #define VFS_H 3 | 4 | #include "stdint.h" 5 | #include "vnode.h" 6 | #include "vattr.h" 7 | 8 | struct vfsops; 9 | 10 | struct vfs { 11 | struct vfs *vfs_next; 12 | struct vfsops *vfs_op; 13 | char const *mount_path; 14 | uint32_t vfs_data; 15 | }; 16 | typedef struct vfs vfs_t; 17 | 18 | struct vfsops { 19 | int (*vfs_root)(vfs_t *vfs, vnode_t *root); 20 | }; 21 | typedef struct vfsops vfsops_t; 22 | 23 | int vfs_mount(char const *path, vfs_t *vfs); 24 | int vfs_lookup(char const *path, vnode_t *res); 25 | int vfs_open(vnode_t *node); 26 | int vfs_read(vnode_t *node, void *buf, uint32_t count); 27 | int vfs_write(vnode_t *node, char const *str, size_t len); 28 | int vfs_getattr(vnode_t *node, vattr_t *attr); 29 | 30 | #endif /* VFS_H */ 31 | -------------------------------------------------------------------------------- /src/kernel/vnode.h: -------------------------------------------------------------------------------- 1 | #ifndef VNODE_H 2 | #define VNODE_H 3 | 4 | #include "stddef.h" 5 | #include "stdint.h" 6 | #include "vattr.h" 7 | 8 | struct vnodeops; 9 | 10 | struct vnode { 11 | struct vnodeops *v_op; 12 | uint32_t v_data; 13 | }; 14 | typedef struct vnode vnode_t; 15 | 16 | struct vnodeops { 17 | int (*vn_open)(vnode_t *node); 18 | int (*vn_lookup)(vnode_t *dir, char const *name, vnode_t *res); 19 | int (*vn_read)(vnode_t *node, void *buf, size_t count); 20 | int (*vn_write)(vnode_t *node, char const *buf, size_t count); 21 | int (*vn_getattr)(vnode_t *node, vattr_t *attr); 22 | }; 23 | typedef struct vnodeops vnodeops_t; 24 | 25 | /* NOTE: This is not a "deep" copy. The v_op pointer will point to the 26 | * same struct in both from and to. 27 | */ 28 | void vnode_copy(vnode_t *from, vnode_t *to); 29 | 30 | #endif /* VNODE_H */ 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aenix 2 | 3 | aenix (Adam and Eriks NIX) is a small hobby operating system. 4 | 5 | ## Dependencies 6 | - [GCC](http://gcc.gnu.org/) 7 | - [NASM](http://www.nasm.us/) 8 | - [genisoimage](http://cdrkit.org/) 9 | - [bochs](http://bochs.sourceforge.net/) 10 | - [Make](http://www.gnu.org/software/make/) 11 | 12 | If you are running Ubuntu 10.10 or later, these can all be installed by running 13 | 14 | sudo apt-get install build-essential nasm genisoimage bochs bochs-x 15 | 16 | ## Building 17 | 18 | cd src 19 | make 20 | 21 | ## Running 22 | 23 | cd src 24 | make run 25 | 26 | ## Documentation 27 | The book [The little book about OS development](http://littleosbook.github.com) is 28 | based on our experience writing aenix. Everything described in the book is used 29 | in aenix. 30 | 31 | ## License 32 | [GPLv3](http://www.gnu.org/licenses/gpl.html) 33 | -------------------------------------------------------------------------------- /src/kernel/Makefile: -------------------------------------------------------------------------------- 1 | OBJECTS = loader.o kmain.o fb.o io.o gdt.o gdt_asm.o pic.o idt.o idt_asm.o \ 2 | interrupt.o interrupt_asm.o keyboard.o pit.o stdio.o string.o \ 3 | paging.o paging_asm.o kmalloc.o module.o serial.o log.o \ 4 | aefs.o process.o page_frame_allocator.o mem.o math.o tss.o \ 5 | tss_asm.o syscall.o scheduler.o scheduler_asm.o vfs.o devfs.o \ 6 | vnode.o 7 | CC = gcc 8 | CFLAGS = -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector \ 9 | -nostartfiles -nodefaultlibs -Wall -Wextra -Werror -fomit-frame-pointer \ 10 | -Wno-unused-function -c 11 | LDFLAGS = -T link.ld -melf_i386 12 | AS = nasm 13 | ASFLAGS = -f elf 14 | AS_HEADERS = constants.inc 15 | 16 | all: kernel.elf 17 | 18 | %.o: %.c 19 | $(CC) $(CFLAGS) $< -o $@ 20 | 21 | %.o: %.s 22 | ./c_to_nasm.sh $(AS_HEADERS) 23 | $(AS) $(ASFLAGS) $< 24 | 25 | kernel.elf: $(OBJECTS) 26 | ld $(LDFLAGS) $(OBJECTS) -o kernel.elf 27 | 28 | clean: 29 | rm -rf *.o kernel.elf *.inc 30 | -------------------------------------------------------------------------------- /src/apps/sh.c: -------------------------------------------------------------------------------- 1 | #include "unistd.h" 2 | #include "string.h" 3 | #include "sys/syscall.h" 4 | 5 | int main(void) 6 | { 7 | int i = 0, j = 0; 8 | if (syscall(SYS_fork)) { 9 | while (1) { 10 | syscall(SYS_write, 1, "parent\n", 7); 11 | while (j++ < 1000000) {} /* do work */ 12 | j = 0; 13 | 14 | if (i++ >= 10) { 15 | char *msg = "parent doing nothing\n"; 16 | syscall(SYS_write, 1, msg, strlen(msg)); 17 | syscall(SYS_wait); 18 | syscall(SYS_write, 1, "done waiting\n", 13); 19 | 20 | while (1) { 21 | } 22 | } 23 | } 24 | } else { 25 | while (1) { 26 | syscall(SYS_write, 1, "child\n", 6); 27 | while (j++ < 1000000) {} /* do work */ 28 | j = 0; 29 | 30 | if (i++ >= 7) { 31 | syscall(SYS_write, 1, "child exiting\n", 14); 32 | return 0; 33 | } 34 | } 35 | } 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /src/kernel/stdio.c: -------------------------------------------------------------------------------- 1 | #include "stdio.h" 2 | #include "stdint.h" 3 | #include "stdarg.h" 4 | 5 | #include "fb.h" 6 | 7 | void printf(char *s, ...) 8 | { 9 | va_list ap; 10 | char *p; 11 | uint32_t uival; 12 | char *sval; 13 | 14 | va_start(ap, s); 15 | for (p = s; *p != '\0'; ++p) { 16 | if (*p != '%') { 17 | fb_put_b(*p); 18 | continue; 19 | } 20 | 21 | switch (*++p) { 22 | case 'c': 23 | uival = va_arg(ap, uint32_t); 24 | fb_put_b((uint8_t) uival); 25 | break; 26 | case 'u': 27 | uival = va_arg(ap, uint32_t); 28 | fb_put_ui(uival); 29 | break; 30 | case 'X': 31 | uival = va_arg(ap, uint32_t); 32 | fb_put_ui_hex(uival); 33 | break; 34 | case 's': 35 | sval = va_arg(ap, char*); 36 | fb_put_s(sval); 37 | break; 38 | case '%': 39 | fb_put_b('%'); 40 | break; 41 | } 42 | } 43 | 44 | va_end(ap); 45 | } 46 | -------------------------------------------------------------------------------- /src/kernel/pit.c: -------------------------------------------------------------------------------- 1 | #include "pit.h" 2 | #include "io.h" 3 | #include "interrupt.h" 4 | #include "common.h" 5 | #include "pic.h" 6 | 7 | #define PIT_CHANNEL_0_DATA 0x40 8 | #define PIT_CHANNEL_1_DATA 0x41 9 | #define PIT_CHANNEL_2_DATA 0x42 10 | #define PIT_COMMAND 0x43 11 | 12 | #define PIT_FREQUENCY 1193182 /* Hz */ 13 | 14 | void pit_init(void) 15 | { 16 | /* name | value | size | desc 17 | * -------------------------- 18 | * chan | 0 | 2 | the channel to use, channel 0 = IRQ0 19 | * acs | 0x3 | 2 | how the divider is sent, 3 = lobyte then hibyte 20 | * mode | 0x3 | 3 | the mode of the pit, mode 3 = square wave 21 | * bcd | 0 | 1 | bcd or binary mode, 0 = binary, 1 = bcd 22 | */ 23 | uint8_t data = (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | 0x00; 24 | outb(PIT_COMMAND, data); 25 | } 26 | 27 | /* interval is in ms */ 28 | void pit_set_interval(uint32_t interval) 29 | { 30 | uint32_t frequency = 1000 / interval; 31 | uint16_t divider = (uint16_t) (PIT_FREQUENCY / frequency); 32 | 33 | outb(PIT_CHANNEL_0_DATA, (uint8_t) divider); 34 | outb(PIT_CHANNEL_0_DATA, (uint8_t) (divider >> 8)); 35 | } 36 | -------------------------------------------------------------------------------- /src/kernel/interrupt.c: -------------------------------------------------------------------------------- 1 | #include "interrupt.h" 2 | #include "stddef.h" 3 | #include "pic.h" 4 | #include "keyboard.h" 5 | #include "common.h" 6 | #include "pit.h" 7 | #include "stdio.h" 8 | #include "log.h" 9 | #include "constants.h" 10 | 11 | static interrupt_handler_t interrupt_handlers[IDT_NUM_ENTRIES]; 12 | 13 | uint32_t register_interrupt_handler(uint32_t interrupt, 14 | interrupt_handler_t handler) 15 | { 16 | if (interrupt > 255) { 17 | return 1; 18 | } 19 | if (interrupt == SYSCALL_INT_IDX) { 20 | return 1; 21 | } 22 | if (interrupt_handlers[interrupt] != NULL) { 23 | return 1; 24 | } 25 | 26 | interrupt_handlers[interrupt] = handler; 27 | return 0; 28 | } 29 | 30 | void interrupt_handler(cpu_state_t state, idt_info_t info, stack_state_t exec) 31 | { 32 | if (interrupt_handlers[info.idt_index] != NULL) { 33 | interrupt_handlers[info.idt_index](state, info, exec); 34 | } else { 35 | log_info("interrupt_handler", 36 | "unhandled interrupt: %u, eip: %X, cs: %X, eflags: %X\n", 37 | info.idt_index, exec.eip, exec.cs, exec.eflags); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/run_bochs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Starts bochs using the iso created by create_iso.sh 3 | 4 | ISO=aenix.iso 5 | BOCHS_LOG=bochslog.txt 6 | BOCHS_CONFIG=bochsrc.txt 7 | BOCHS_BIOS_PATH=/usr/share/bochs/ 8 | COM1_LOG=com1.out 9 | 10 | set -e # fail as soon as one command fails 11 | 12 | # check if an old log file exists, if so, remove it 13 | if [ -e $BOCHS_LOG ]; then 14 | rm $BOCHS_LOG 15 | fi 16 | 17 | # check if an old config file exists, if so, remove it 18 | if [ -e $BOCHS_CONFIG ]; then 19 | rm $BOCHS_CONFIG 20 | fi 21 | 22 | # remove the old log 23 | rm -f $COM1_LOG 24 | 25 | # create the config file for bochs 26 | CONFIG="megs: 32 27 | display_library: x 28 | romimage: file=\"$BOCHS_BIOS_PATH/BIOS-bochs-latest\" 29 | vgaromimage: file=\"$BOCHS_BIOS_PATH/VGABIOS-lgpl-latest\" 30 | ata0-master: type=cdrom, path=$ISO, status=inserted 31 | boot: cdrom 32 | log: $BOCHS_LOG 33 | mouse: enabled=1 34 | clock: sync=realtime, time0=local 35 | cpu: count=1, ips=1000000 36 | com1: enabled=1, mode=file, dev=$COM1_LOG" 37 | 38 | echo "$CONFIG" > $BOCHS_CONFIG 39 | 40 | # start bochs 41 | bochs -f $BOCHS_CONFIG -q 42 | 43 | exit 0 44 | -------------------------------------------------------------------------------- /src/kernel/paging.h: -------------------------------------------------------------------------------- 1 | #ifndef PAGING_H 2 | #define PAGING_H 3 | 4 | #include "stdint.h" 5 | 6 | #define PAGING_READ_ONLY 0 7 | #define PAGING_READ_WRITE 1 8 | #define PAGING_PL0 0 9 | #define PAGING_PL3 1 10 | 11 | typedef struct pde pde_t; 12 | 13 | uint32_t paging_init(uint32_t kernel_pdt_vaddr, uint32_t kernel_pt_vaddr); 14 | 15 | uint32_t pdt_kernel_find_next_vaddr(uint32_t size); 16 | 17 | uint32_t pdt_map_kernel_memory(uint32_t paddr, 18 | uint32_t vaddr, 19 | uint32_t size, 20 | uint8_t rw, 21 | uint8_t pl); 22 | uint32_t pdt_map_memory(pde_t *pdt, 23 | uint32_t paddr, 24 | uint32_t vaddr, 25 | uint32_t size, 26 | uint8_t rw, 27 | uint8_t pl); 28 | 29 | uint32_t pdt_unmap_kernel_memory(uint32_t vaddr, uint32_t size); 30 | uint32_t pdt_unmap_memory(pde_t *pdt, uint32_t vaddr, uint32_t size); 31 | 32 | pde_t *pdt_create(uint32_t *out_paddr); 33 | void pdt_delete(pde_t *pdt); 34 | 35 | void pdt_load_process_pdt(pde_t *pdt, uint32_t pdt_paddr); 36 | 37 | #endif /* PAGING_H */ 38 | -------------------------------------------------------------------------------- /src/kernel/interrupt.h: -------------------------------------------------------------------------------- 1 | #ifndef INTERRUPT_H 2 | #define INTERRUPT_H 3 | 4 | #include "stdint.h" 5 | #include "idt.h" 6 | 7 | struct idt_info { 8 | uint32_t idt_index; 9 | uint32_t error_code; 10 | } __attribute__((packed)); 11 | typedef struct idt_info idt_info_t; 12 | 13 | struct cpu_state { 14 | uint32_t edi; 15 | uint32_t esi; 16 | uint32_t ebp; 17 | uint32_t edx; 18 | uint32_t ecx; 19 | uint32_t ebx; 20 | uint32_t eax; 21 | uint32_t esp; 22 | } __attribute__((packed)); 23 | typedef struct cpu_state cpu_state_t; 24 | 25 | struct stack_state { 26 | uint32_t eip; 27 | uint32_t cs; 28 | uint32_t eflags; 29 | uint32_t user_esp; /* not always safe to derefence! */ 30 | uint32_t user_ss; /* not always safe to derefence! */ 31 | } __attribute__((packed)); 32 | typedef struct stack_state stack_state_t; 33 | 34 | typedef void (*interrupt_handler_t)(cpu_state_t state, 35 | idt_info_t info, 36 | stack_state_t exec); 37 | 38 | uint32_t register_interrupt_handler(uint32_t interrupt, 39 | interrupt_handler_t handler); 40 | 41 | void enable_interrupts(void); 42 | void disable_interrupts(void); 43 | void switch_to_kernel_stack(void (*continuation)(uint32_t), uint32_t data); 44 | 45 | #endif /* INTERRUPT_H */ 46 | -------------------------------------------------------------------------------- /src/kernel/tss.h: -------------------------------------------------------------------------------- 1 | #ifndef TSS_H 2 | #define TSS_H 3 | 4 | #include "stdint.h" 5 | 6 | struct tss { 7 | uint16_t prev_task_link; 8 | uint16_t reserved; 9 | uint32_t esp0; 10 | uint16_t ss0; 11 | uint16_t reserved0; 12 | uint32_t esp1; 13 | uint16_t ss1; 14 | uint16_t reserved1; 15 | uint32_t esp2; 16 | uint16_t ss2; 17 | uint16_t reserved2; 18 | 19 | uint32_t cr3; 20 | uint32_t eip; 21 | uint32_t eflags; 22 | uint32_t eax; 23 | uint32_t ecx; 24 | uint32_t edx; 25 | uint32_t ebx; 26 | uint32_t esp; 27 | uint32_t ebp; 28 | uint32_t esi; 29 | uint32_t edi; 30 | 31 | uint16_t es; 32 | uint16_t reserved3; 33 | uint16_t cs; 34 | uint16_t reserved4; 35 | uint16_t ss; 36 | uint16_t reserved5; 37 | uint16_t ds; 38 | uint16_t reserved6; 39 | uint16_t fs; 40 | uint16_t reserved7; 41 | uint16_t gs; 42 | uint16_t reserved8; 43 | 44 | uint16_t ldt_ss; 45 | uint16_t reserved9; 46 | 47 | uint16_t debug_and_reserved; /* The lowest bit is for debug */ 48 | uint16_t io_map_base; 49 | 50 | } __attribute__((packed)); 51 | 52 | typedef struct tss tss_t; 53 | 54 | uint32_t tss_init(); 55 | 56 | void tss_load_and_set(uint16_t tss_segsel); /* defined in tss_asm.s */ 57 | 58 | void tss_set_kernel_stack(uint16_t segsel, uint32_t vaddr); 59 | 60 | #endif /* TSS_H */ 61 | -------------------------------------------------------------------------------- /src/kernel/pic.c: -------------------------------------------------------------------------------- 1 | #include "pic.h" 2 | #include "io.h" 3 | 4 | /* Information about how to program the PIC was found at 5 | * http://www.acm.uiuc.edu/sigops/roll_your_own/i386/irq.html 6 | */ 7 | 8 | #define PIC1_PORT_A 0x20 9 | #define PIC1_PORT_B 0x21 10 | 11 | #define PIC2_PORT_A 0xA0 12 | #define PIC2_PORT_B 0xA1 13 | 14 | #define PIC1_ICW1 0x11 /* Initialize the PIC and enable ICW4 */ 15 | #define PIC2_ICW1 PIC1_ICW2 16 | 17 | #define PIC1_ICW2 0x20 /* IRQ 0-7 will be remapped to IDT index 32 - 39 */ 18 | #define PIC2_ICW2 0x28 /* IRQ 8-15 will be remapped to IDT index 40 - 47 */ 19 | 20 | #define PIC1_ICW3 0x04 /* PIC1 is connected to PIC2 via IRQ2 */ 21 | #define PIC2_ICW3 0x02 /* PIC2 is connected to PIC1 via IRQ1 */ 22 | 23 | #define PIC1_ICW4 0x05 /* 8086/88 mode is enabled and PIC1 is master */ 24 | #define PIC2_ICW4 0x01 /* 8086/88 mode is enabled */ 25 | 26 | #define PIC_EOI 0x20 27 | 28 | void pic_init(void) 29 | { 30 | /* ICW1 */ 31 | outb(PIC1_PORT_A, PIC1_ICW1); 32 | outb(PIC2_PORT_A, PIC2_ICW1); 33 | 34 | /* ICW2 */ 35 | outb(PIC1_PORT_B, PIC1_ICW2); 36 | outb(PIC2_PORT_B, PIC2_ICW2); 37 | 38 | /* ICW3 */ 39 | outb(PIC1_PORT_B, PIC1_ICW3); 40 | outb(PIC2_PORT_B, PIC2_ICW3); 41 | 42 | /* ICW4 */ 43 | outb(PIC1_PORT_B, PIC1_ICW4); 44 | outb(PIC2_PORT_B, PIC2_ICW4); 45 | 46 | pic_mask(0xEC, 0xFF); 47 | } 48 | 49 | void pic_acknowledge() 50 | { 51 | outb(PIC1_PORT_A, PIC_EOI); 52 | outb(PIC2_PORT_A, PIC_EOI); 53 | } 54 | 55 | void pic_mask(uint8_t mask1, uint8_t mask2) 56 | { 57 | outb(PIC1_PORT_B, mask1); 58 | outb(PIC2_PORT_B, mask2); 59 | } 60 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | APPS = init sh 2 | FS_ROOT = fs_root 3 | BIN_PATH = $(FS_ROOT)/bin 4 | WARNINGS = -Wall -Wextra -Werror 5 | CFLAGS = -m32 6 | CC = gcc 7 | FS_SIZE = 4 # in MB 8 | 9 | all: iso 10 | 11 | iso: build_kernel build_libc fs 12 | @echo '-> Creating ISO' 13 | ./create_iso.sh fs 14 | @echo '' 15 | 16 | run: iso 17 | @echo '-> Starting BOCHS' 18 | ./run_bochs.sh || echo "\n" ; cat com1.out 19 | 20 | create_bin_folder: build_apps 21 | @echo '-> Copying applications to fs folder' 22 | mkdir -p $(BIN_PATH) 23 | for app in $(APPS); do cp apps/$$app $(BIN_PATH); done 24 | @echo '' 25 | 26 | fs: mkfs create_bin_folder 27 | @echo '-> Creating filesystem' 28 | ./mkfs $(FS_ROOT) $(FS_SIZE) $@ 29 | @echo '' 30 | 31 | mkfs_release: CFLAGS = $(CFLAGS) -O2 32 | mkfs_release: mkfs 33 | 34 | mkfs_debug: CFLAGS = $(CFLAGS) -g 35 | mkfs_debug: mkfs 36 | 37 | mkfs: mkfs.c 38 | $(CC) $(WARNINGS) $(CFLAGS) mkfs.c -o mkfs 39 | 40 | rdaefs_release: CFLAGS = $(CFLAGS) -O2 41 | rdaefs_release: rdaefs 42 | 43 | rdaefs_debug: CFLAGS = $(CFLAGS) -g 44 | rdaefs_debug: rdaefs 45 | 46 | rdaefs: rdaefs.c 47 | $(CC) $(WARNINGS) $(CFLAGS) rdaefs.c -o rdaefs 48 | 49 | build_apps: 50 | @echo '-> Building applications' 51 | @make --no-print-directory -C apps 52 | @echo '' 53 | 54 | build_kernel: 55 | @echo '-> Building kernel' 56 | @make --no-print-directory -C kernel 57 | @echo '' 58 | 59 | build_libc: 60 | @echo '-> Building libc' 61 | @make --no-print-directory -C libc 62 | @echo '' 63 | 64 | clean: 65 | @echo '-> Cleaning kernel' 66 | @make --no-print-directory -C kernel clean 67 | @echo '' 68 | @echo '-> Cleaning apps' 69 | @make --no-print-directory -C apps clean 70 | @echo '' 71 | @echo '-> Cleaning libc' 72 | @make --no-print-directory -C libc clean 73 | @echo '' 74 | rm -rf mkfs aenix.iso fs rdaefs 75 | -------------------------------------------------------------------------------- /src/kernel/aefs.h: -------------------------------------------------------------------------------- 1 | #ifndef FS_H 2 | #define FS_H 3 | 4 | #include "stdint.h" 5 | #include "vfs.h" 6 | 7 | #define AEFS_FILENAME_MAX_LEN 254 8 | 9 | #define AEFS_FILETYPE_REG 0 10 | #define AEFS_FILETYPE_DIR 1 11 | 12 | #define AEFS_BLOCK_SIZE 1024 /* in bytes */ 13 | #define AEFS_INODE_NUM_BLOCKS 5 14 | 15 | #define AEFS_MAGIC_NUMBER 0xAE12AE34 16 | 17 | /* sizeof(inode_t) == 16 bytes */ 18 | struct aefs_inode { 19 | uint8_t type; 20 | uint8_t size_high; 21 | uint16_t size_low; /* in bytes */ 22 | uint16_t blocks[AEFS_INODE_NUM_BLOCKS]; 23 | uint16_t inode_tail; 24 | } __attribute__((packed)); 25 | typedef struct aefs_inode aefs_inode_t; 26 | 27 | /* sizeof(inode_list_t) == 16 bytes */ 28 | struct aefs_inode_list { 29 | uint32_t padding; 30 | uint16_t blocks[AEFS_INODE_NUM_BLOCKS]; 31 | uint16_t inode_tail; 32 | } __attribute__((packed)); 33 | typedef struct aefs_inode_list aefs_inode_list_t; 34 | 35 | /* sizeof(direntry_t) == 256 bytes */ 36 | struct aefs_direntry { 37 | char name[AEFS_FILENAME_MAX_LEN]; 38 | uint16_t inode_id; 39 | } __attribute__((packed)); 40 | typedef struct aefs_direntry aefs_direntry_t; 41 | 42 | /* sizeof(superblock_t) == 12 bytes */ 43 | struct aefs_superblock { 44 | uint32_t magic_number; 45 | uint16_t num_inodes; 46 | uint16_t start_block; 47 | } __attribute__((packed)); 48 | typedef struct aefs_superblock aefs_superblock_t; 49 | 50 | #define AEFS_INODE_SIZE(inode) ((((uint32_t) (inode)->size_high) << 16) | \ 51 | ((inode)->size_low)) 52 | #define AEFS_DIRENTRIES_PER_BLOCK (AEFS_BLOCK_SIZE/sizeof(aefs_direntry_t)) 53 | 54 | #define AEFS_INODE_IS_REG(inode) ((inode)->type == AEFS_FILETYPE_REG) 55 | #define AEFS_INODE_IS_DIR(inode) ((inode)->type == AEFS_FILETYPE_DIR) 56 | 57 | uint32_t aefs_init(uint32_t fs_paddr, uint32_t fs_size, vfs_t *vfs); 58 | 59 | #endif /* FS_H */ 60 | -------------------------------------------------------------------------------- /src/kernel/process.h: -------------------------------------------------------------------------------- 1 | #ifndef PROCESS_H 2 | #define PROCESS_H 3 | 4 | #include "stdint.h" 5 | #include "vnode.h" 6 | #include "paging.h" 7 | 8 | #define PROCESS_MAX_NUM_FD 64 9 | 10 | /* do not change order of variables in the struct, asm code depends on it! */ 11 | struct registers { 12 | uint32_t eax; 13 | uint32_t ebx; 14 | uint32_t ecx; 15 | uint32_t edx; 16 | uint32_t ebp; 17 | uint32_t esi; 18 | uint32_t edi; 19 | uint32_t ss; 20 | uint32_t esp; 21 | uint32_t eflags; 22 | uint32_t cs; 23 | uint32_t eip; 24 | } __attribute__((packed)); 25 | typedef struct registers registers_t; 26 | 27 | struct paddr_ele { 28 | uint32_t paddr; 29 | uint32_t count; 30 | struct paddr_ele *next; 31 | }; 32 | typedef struct paddr_ele paddr_ele_t; 33 | 34 | struct paddr_list { 35 | paddr_ele_t *start; 36 | paddr_ele_t *end; 37 | }; 38 | typedef struct paddr_list paddr_list_t; 39 | 40 | 41 | struct fd { 42 | vnode_t *vnode; 43 | }; 44 | typedef struct fd fd_t; 45 | 46 | struct ps { 47 | uint32_t id; 48 | uint32_t parent_id; 49 | 50 | pde_t *pdt; 51 | uint32_t pdt_paddr; 52 | 53 | registers_t current; 54 | registers_t user_mode; 55 | 56 | uint32_t kernel_stack_start_vaddr; 57 | uint32_t stack_start_vaddr; 58 | uint32_t code_start_vaddr; 59 | 60 | fd_t file_descriptors[PROCESS_MAX_NUM_FD]; 61 | 62 | paddr_list_t code_paddrs; 63 | paddr_list_t stack_paddrs; 64 | paddr_list_t kernel_stack_paddrs; 65 | }; 66 | typedef struct ps ps_t; 67 | 68 | ps_t *process_create(char const *path, uint32_t id); 69 | ps_t *process_replace(ps_t *ps, char const *path); 70 | 71 | /* 72 | * does not free the ps_t struct, only the memory and resources used by the 73 | * process 74 | */ 75 | void process_delete_resources(ps_t *ps); 76 | ps_t *process_create_replacement(ps_t *parent, char const *path); 77 | ps_t *process_clone(ps_t *parent, uint32_t pid); 78 | void process_mark_as_user(ps_t *ps); 79 | void process_mark_as_kernel(ps_t *ps); 80 | 81 | #endif /* PROCESS_H */ 82 | -------------------------------------------------------------------------------- /src/create_iso.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Creates a bootable ISO for aenix. 3 | # Arguments: A list of modules to load at boot 4 | # Dependencies: genisoimage 5 | 6 | # check if the program grub-mkrescue is in the path 7 | GENISOIMAGE_PATH=`which genisoimage` 8 | if [ -z $GENISOIMAGE_PATH ]; then 9 | echo "ERROR: The program genisoimage must be installed" 10 | exit 1 11 | fi 12 | 13 | set -e # fail as soon as one command fails 14 | 15 | MODULES="$@" 16 | ISO_FOLDER="iso" 17 | STAGE2_ELTORITO="../tools/grub/stage2_eltorito" 18 | 19 | # create the ISO catalog structure 20 | mkdir -p $ISO_FOLDER/boot/grub 21 | mkdir $ISO_FOLDER/modules 22 | 23 | # copy the stage2_eltorito into the correct place 24 | cp $STAGE2_ELTORITO $ISO_FOLDER/boot/grub/ 25 | 26 | # copy the kernel to the correct location 27 | KERNEL=kernel/kernel.elf 28 | cp $KERNEL $ISO_FOLDER/boot/kernel.elf 29 | 30 | # copy all the modules 31 | for m in $MODULES 32 | do 33 | cp $m $ISO_FOLDER/modules/ 34 | done 35 | 36 | # create the menu.lst file 37 | 38 | # set default=0: boot from the first entry (which will be aenix) 39 | # set timeout=0: immediatly boot from the first entry 40 | MENU="default=0 41 | timeout=0 42 | " 43 | 44 | # add the menu entry for aenix 45 | MENU="$MENU 46 | title aenix 47 | kernel /boot/kernel.elf" 48 | 49 | # create one entry for each module 50 | for m in $MODULES 51 | do 52 | MENU="$MENU 53 | module /modules/$m" 54 | done 55 | 56 | 57 | echo "$MENU" > $ISO_FOLDER/boot/grub/menu.lst 58 | 59 | # build the ISO image 60 | # -R: Use the Rock Ridge protocol (needed by GRUB) 61 | # -b file: The file to boot from (relative to the root folder of 62 | # the ISO) 63 | # -no-emul-boot: Do not perform any disk emulation 64 | # -boot-load-size sz: The number 512 byte sectors to load. Apparently most 65 | # BIOS likes the number 4. 66 | # -boot-info-table: Writes information about the ISO layout to ISO (needed 67 | # by GRUB) 68 | # -o name: The name of the iso 69 | # -A name: The label of the iso 70 | # -input-charset cs: The charset for the input files 71 | # -quiet: Disable any output from genisoimage 72 | genisoimage -R -b boot/grub/stage2_eltorito -no-emul-boot -boot-load-size 4 \ 73 | -A aenix -input-charset utf8 -quiet -boot-info-table \ 74 | -o aenix.iso $ISO_FOLDER 75 | 76 | # clean up 77 | rm -r $ISO_FOLDER 78 | -------------------------------------------------------------------------------- /src/kernel/string.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | #include "stdint.h" 3 | #include "stddef.h" 4 | 5 | void *memset(void *s, int c, size_t n) 6 | { 7 | uint8_t *base = (uint8_t *) s; 8 | uint8_t *p; 9 | uint8_t cb = (uint8_t) (c & 0xFF); 10 | 11 | for (p = base; p < base + n; ++p) { 12 | *p = cb; 13 | } 14 | 15 | return s; 16 | } 17 | 18 | void *memcpy(void *dest, const void *src, size_t n) 19 | { 20 | uint8_t *to = (uint8_t *) dest; 21 | uint8_t *from = (uint8_t *) src; 22 | size_t i; 23 | 24 | for(i = 0; i < n; ++i) { 25 | *to++ = *from++; 26 | } 27 | 28 | return dest; 29 | } 30 | 31 | int strcmp(const char *s1, const char *s2) 32 | { 33 | size_t len1 = strlen(s1), len2 = strlen(s2); 34 | 35 | if (len1 < len2) { 36 | return -1; 37 | } 38 | 39 | if (len1 > len2) { 40 | return 1; 41 | } 42 | 43 | for (; *s1; ++s1, ++s2) { 44 | if (*s1 == *s2) { 45 | continue; 46 | } else if (*s1 < *s2) { 47 | return -1; 48 | } else { 49 | return 1; 50 | } 51 | } 52 | 53 | return 0; 54 | } 55 | 56 | int strncmp(const char *s1, const char *s2, size_t n) 57 | { 58 | size_t i; 59 | 60 | for (i = 0; i < n; ++i) { 61 | if (*s1 == *s2) { 62 | continue; 63 | } 64 | 65 | if (*s1 == '\0' && *s2 != '\0') { 66 | return -1; 67 | } 68 | 69 | if (*s2 == '\0' && *s1 != '\0') { 70 | return 1; 71 | } 72 | 73 | if (*s1 < *s2) { 74 | return -1; 75 | } else { 76 | return 1; 77 | } 78 | } 79 | 80 | return 0; 81 | } 82 | 83 | size_t strlen(const char *s) 84 | { 85 | size_t len = 0; 86 | 87 | for (; *s; ++s) { 88 | ++len; 89 | } 90 | 91 | return len; 92 | } 93 | 94 | size_t strcspn(const char *s, const char *reject) 95 | { 96 | size_t l = 0; 97 | 98 | for (; *s; ++s) { 99 | if (strchr(reject, (int) *s) != NULL) { 100 | break; 101 | } 102 | ++l; 103 | } 104 | 105 | return l; 106 | } 107 | 108 | char *strchr(const char *s, int c) 109 | { 110 | for (; *s; ++s) { 111 | if (*s == (char) c) { 112 | return (char *) s; /* ugly, but it follows the standard */ 113 | } 114 | } 115 | 116 | return NULL; 117 | } 118 | -------------------------------------------------------------------------------- /src/libc/string.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | #include "stdint.h" 3 | #include "stddef.h" 4 | 5 | void *memset(void *s, int c, size_t n) 6 | { 7 | uint8_t *base = (uint8_t *) s; 8 | uint8_t *p; 9 | uint8_t cb = (uint8_t) (c & 0xFF); 10 | 11 | for (p = base; p < base + n; ++p) { 12 | *p = cb; 13 | } 14 | 15 | return s; 16 | } 17 | 18 | void *memcpy(void *dest, const void *src, size_t n) 19 | { 20 | uint8_t *to = (uint8_t *) dest; 21 | uint8_t *from = (uint8_t *) src; 22 | size_t i; 23 | 24 | for(i = 0; i < n; ++i) { 25 | *to++ = *from++; 26 | } 27 | 28 | return dest; 29 | } 30 | 31 | int strcmp(const char *s1, const char *s2) 32 | { 33 | size_t len1 = strlen(s1), len2 = strlen(s2); 34 | 35 | if (len1 < len2) { 36 | return -1; 37 | } 38 | 39 | if (len1 > len2) { 40 | return 1; 41 | } 42 | 43 | for (; *s1; ++s1, ++s2) { 44 | if (*s1 == *s2) { 45 | continue; 46 | } else if (*s1 < *s2) { 47 | return -1; 48 | } else { 49 | return 1; 50 | } 51 | } 52 | 53 | return 0; 54 | } 55 | 56 | int strncmp(const char *s1, const char *s2, size_t n) 57 | { 58 | size_t i; 59 | 60 | for (i = 0; i < n; ++i) { 61 | if (*s1 == *s2) { 62 | continue; 63 | } 64 | 65 | if (*s1 == '\0' && *s2 != '\0') { 66 | return -1; 67 | } 68 | 69 | if (*s2 == '\0' && *s1 != '\0') { 70 | return 1; 71 | } 72 | 73 | if (*s1 < *s2) { 74 | return -1; 75 | } else { 76 | return 1; 77 | } 78 | } 79 | 80 | return 0; 81 | } 82 | 83 | size_t strlen(const char *s) 84 | { 85 | size_t len = 0; 86 | 87 | for (; *s; ++s) { 88 | ++len; 89 | } 90 | 91 | return len; 92 | } 93 | 94 | size_t strcspn(const char *s, const char *reject) 95 | { 96 | size_t l = 0; 97 | 98 | for (; *s; ++s) { 99 | if (strchr(reject, (int) *s) != NULL) { 100 | break; 101 | } 102 | ++l; 103 | } 104 | 105 | return l; 106 | } 107 | 108 | char *strchr(const char *s, int c) 109 | { 110 | for (; *s; ++s) { 111 | if (*s == (char) c) { 112 | return (char *) s; /* ugly, but it follows the standard */ 113 | } 114 | } 115 | 116 | return NULL; 117 | } 118 | -------------------------------------------------------------------------------- /src/kernel/module.c: -------------------------------------------------------------------------------- 1 | #include "stdint.h" 2 | #include "string.h" 3 | #include "stdio.h" 4 | #include "common.h" 5 | #include "multiboot.h" 6 | 7 | #define ARE_MODULES_ENABLED(flags) (flags & 0x00000008) 8 | #define MODULES_BASE_VIRTUAL_ADDRESS 0xC0400000 9 | #define MODULES_BASE_PHYSICAL_ADDRESS 0x00400000 10 | 11 | uint32_t end_address_of_modules(multiboot_info_t *mbinfo) 12 | { 13 | uint32_t modules_end = 0, i; 14 | multiboot_module_t *module = (multiboot_module_t *) mbinfo->mods_addr; 15 | for (i = 0; i < mbinfo->mods_count; ++i, ++module) { 16 | if (module->mod_end > modules_end) { 17 | modules_end = module->mod_end; 18 | } 19 | } 20 | return modules_end; 21 | } 22 | 23 | uint32_t total_size_of_modules(multiboot_info_t *mbinfo) 24 | { 25 | uint32_t size = 0, i; 26 | multiboot_module_t *module = (multiboot_module_t *) mbinfo->mods_addr; 27 | for (i = 0; i < mbinfo->mods_count; ++i, ++module) { 28 | size += module->mod_end - module->mod_start; 29 | } 30 | return size; 31 | } 32 | 33 | void copy_modules(multiboot_info_t *mbinfo, uint32_t dest) 34 | { 35 | uint32_t size = 0, i; 36 | multiboot_module_t *module = (multiboot_module_t *) mbinfo->mods_addr; 37 | for (i = 0; i < mbinfo->mods_count; ++i, ++module) { 38 | size = module->mod_end - module->mod_start; 39 | memcpy((void *) dest, (void *) module->mod_start, size); 40 | 41 | module->mod_start = dest; 42 | module->mod_end = dest + size; 43 | 44 | dest += size; 45 | } 46 | } 47 | 48 | void move_multiboot_modules(multiboot_info_t *mbinfo) 49 | { 50 | uint32_t end_addr, start_addr, total_size; 51 | if (ARE_MODULES_ENABLED(mbinfo->flags)) { 52 | start_addr = mbinfo->mods_addr; 53 | end_addr = end_address_of_modules(mbinfo); 54 | total_size = total_size_of_modules(mbinfo); 55 | 56 | if (end_addr < MODULES_BASE_PHYSICAL_ADDRESS) { 57 | /* cool, just copy the shit */ 58 | copy_modules(mbinfo, MODULES_BASE_VIRTUAL_ADDRESS); 59 | } else if (start_addr > (MODULES_BASE_PHYSICAL_ADDRESS + total_size)) { 60 | /* also cool, enough space to just copy the modules */ 61 | copy_modules(mbinfo, MODULES_BASE_VIRTUAL_ADDRESS); 62 | } else { 63 | /* not cool anymore, need to copy to temp area first */ 64 | uint32_t tmp_area = 0; 65 | if (end_addr < MODULES_BASE_PHYSICAL_ADDRESS + total_size) { 66 | tmp_area = 67 | NEXT_ADDR(MODULES_BASE_PHYSICAL_ADDRESS + total_size); 68 | } else { 69 | tmp_area = NEXT_ADDR(end_addr); 70 | } 71 | 72 | copy_modules(mbinfo, tmp_area); 73 | copy_modules(mbinfo, MODULES_BASE_VIRTUAL_ADDRESS); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/kernel/scheduler_asm.s: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | %include "constants.inc" 4 | 5 | global run_process_in_user_mode 6 | global run_process_in_kernel_mode 7 | global snapshot_and_schedule 8 | 9 | extern fb_put_b 10 | extern fb_put_ui_hex 11 | extern scheduler_schedule 12 | 13 | section .text 14 | align 4 15 | run_process_in_user_mode: 16 | cli ; disable external interrupts 17 | mov eax, [esp+4] ; load address of registers_t into eax 18 | 19 | ; restore all the registers except eax 20 | mov ebx, [eax+4] 21 | mov ecx, [eax+8] 22 | mov edx, [eax+12] 23 | mov ebp, [eax+16] 24 | mov esi, [eax+20] 25 | mov edi, [eax+24] 26 | 27 | ; push information for iret onto the stack 28 | push DWORD [eax+28] ; push the SS onto the stack 29 | push DWORD [eax+32] ; push the ESP of the user stack 30 | push DWORD [eax+36] ; push EFLAGS 31 | push DWORD [eax+40] ; push the segment selector 32 | push DWORD [eax+44] ; push EIP 33 | 34 | ; move index for the data segment into data registers 35 | push ecx 36 | mov cx, [eax+28] 37 | mov ds, cx 38 | mov gs, cx 39 | mov es, cx 40 | mov fs, cx 41 | pop ecx 42 | 43 | mov eax, [eax] ; restore eax 44 | 45 | iret ; iret into the given mode 46 | 47 | run_process_in_kernel_mode: 48 | cli ; disable external interrupts 49 | mov eax, [esp+4] ; load address of registers_t into eax 50 | 51 | ; restore all the registers except eax 52 | mov ebx, [eax+4] 53 | mov ecx, [eax+8] 54 | mov edx, [eax+12] 55 | mov ebp, [eax+16] 56 | mov esi, [eax+20] 57 | mov edi, [eax+24] 58 | 59 | ; restore the stack pointer 60 | mov esp, [eax+32] 61 | 62 | ; push information for iret onto the stack 63 | push DWORD [eax+36] ; push EFLAGS 64 | push DWORD [eax+40] ; push the segment selector 65 | push DWORD [eax+44] ; push EIP 66 | 67 | mov eax, [eax] ; restore eax 68 | 69 | iret ; iret to return to the process 70 | 71 | snapshot_and_schedule: 72 | cli ; disable external interrupts 73 | mov eax, [esp+4] ; load address of registers_t into eax 74 | 75 | ; restore all the registers except eax, since eax holds return value 76 | mov DWORD [eax], 0 ; 0 => the function call was succesfull 77 | mov [eax+4], ebx 78 | mov [eax+8], ecx 79 | mov [eax+12], edx 80 | mov [eax+16], ebp 81 | mov [eax+20], esi 82 | mov [eax+24], edi 83 | mov [eax+28], ss 84 | mov [eax+32], esp 85 | add DWORD [eax+32], 4 86 | 87 | ; snapshot eflags 88 | pushf 89 | pop DWORD [eax+36] 90 | 91 | mov [eax+40], cs 92 | mov ebx, [esp] 93 | mov [eax+44], ebx ; jump back to the calling code 94 | 95 | call scheduler_schedule 96 | jmp $ 97 | -------------------------------------------------------------------------------- /src/kernel/vfs.c: -------------------------------------------------------------------------------- 1 | #include "vfs.h" 2 | #include "string.h" 3 | #include "log.h" 4 | #include "kmalloc.h" 5 | 6 | static vfs_t *vfs_list = NULL; 7 | 8 | int vfs_mount(char const *path, vfs_t *vfs) 9 | { 10 | if (vfs_list == NULL) { 11 | vfs_list = vfs; 12 | } else { 13 | vfs_t *i = vfs_list; 14 | while (i->vfs_next != NULL) { 15 | if (strcmp(i->mount_path, path) == 0) { 16 | return -1; 17 | } 18 | i = i->vfs_next; 19 | } 20 | i->vfs_next = vfs; 21 | } 22 | 23 | int plen = strlen(path) + 1; 24 | char *p = kmalloc(plen * sizeof(char)); 25 | memcpy(p, path, plen); 26 | vfs->mount_path = p; 27 | vfs->vfs_next = NULL; 28 | 29 | return 0; 30 | } 31 | 32 | static int find_longest_mount_path(char const *path, vfs_t **out) 33 | { 34 | vfs_t *i = vfs_list; 35 | vfs_t *root; 36 | uint32_t max_len = 0, idx; 37 | while (i != NULL) { 38 | char const *mp = i->mount_path; 39 | char const *p = path; 40 | for (idx = 0; *p && *mp && *p == *mp; ++p, ++mp, ++idx) { 41 | } 42 | if (idx > max_len) { 43 | root = i; 44 | max_len = idx; 45 | } 46 | i = i->vfs_next; 47 | } 48 | 49 | *out = root; 50 | return max_len; 51 | 52 | } 53 | 54 | int vfs_lookup(char const *path, vnode_t *res) 55 | { 56 | if (strlen(path) < 1) { 57 | return -1; 58 | } 59 | 60 | if (path[0] != '/') { 61 | return -1; 62 | } 63 | 64 | 65 | vfs_t *root; 66 | int max_len = find_longest_mount_path(path, &root); 67 | path += max_len; 68 | 69 | vnode_t vnode; 70 | if (root->vfs_op->vfs_root(root, &vnode)) { 71 | log_error("vfs_lookup", "Could not find vnode for path %s\n", path); 72 | return -1; 73 | } 74 | 75 | if (strlen(path) == 0) { 76 | res->v_op = vnode.v_op; 77 | res->v_data = vnode.v_data; 78 | return 0; 79 | } 80 | 81 | int ret = 0, inc; 82 | uint32_t slash_index; 83 | char *p = kmalloc(strlen(path) * sizeof(char) + 1); 84 | char *orgp = p; 85 | memcpy(p, path, strlen(path) + 1); 86 | 87 | while (strlen(p) != 0 && ret == 0) { 88 | slash_index = strcspn(p, "/"); 89 | 90 | if (slash_index != strlen(p)) { 91 | p[slash_index] = '\0'; 92 | inc = slash_index + 1; 93 | } else { 94 | inc = slash_index; 95 | } 96 | 97 | ret = vnode.v_op->vn_lookup(&vnode, p, res); 98 | vnode.v_op = res->v_op; 99 | vnode.v_data = res->v_data; 100 | 101 | p += inc; 102 | } 103 | 104 | kfree(orgp); 105 | 106 | return ret; 107 | } 108 | 109 | int vfs_open(vnode_t *node) 110 | { 111 | return node->v_op->vn_open(node); 112 | } 113 | 114 | int vfs_read(vnode_t *node, void *buf, uint32_t count) 115 | { 116 | return node->v_op->vn_read(node, buf, count); 117 | } 118 | 119 | int vfs_write(vnode_t *node, char const *str, size_t len) 120 | { 121 | return node->v_op->vn_write(node, str, len); 122 | } 123 | 124 | int vfs_getattr(vnode_t *node, vattr_t *attr) 125 | { 126 | return node->v_op->vn_getattr(node, attr); 127 | } 128 | -------------------------------------------------------------------------------- /src/kernel/interrupt_asm.s: -------------------------------------------------------------------------------- 1 | %include "constants.inc" 2 | 3 | extern interrupt_handler 4 | extern syscall_handle_interrupt 5 | extern kernel_stack 6 | extern run_process_in_user_mode 7 | 8 | global enable_interrupts 9 | global disable_interrupts 10 | global handle_syscall 11 | global switch_to_kernel_stack 12 | 13 | %macro no_error_code_handler 1 14 | global interrupt_handler_%1 15 | interrupt_handler_%1: 16 | push dword 0 17 | push dword %1 18 | jmp common_interrupt_handler 19 | %endmacro 20 | 21 | %macro error_code_handler 1 22 | global interrupt_handler_%1 23 | interrupt_handler_%1: 24 | push dword %1 25 | jmp common_interrupt_handler 26 | %endmacro 27 | 28 | section .text: 29 | 30 | common_interrupt_handler: 31 | push esp 32 | ; BEWARE: DON'T ADD INSTRUCTIONS ABOVE THIS LINE IF YOU DON'T KNOW WHAT THE 33 | ; INSTRUCTIONS FOLLOWING THIS COMMENT DOES! 34 | add DWORD [esp], 8 35 | push eax 36 | push ebx 37 | push ecx 38 | push edx 39 | push ebp 40 | push esi 41 | push edi 42 | call interrupt_handler 43 | pop edi 44 | pop esi 45 | pop ebp 46 | pop edx 47 | pop ecx 48 | pop ebx 49 | pop eax 50 | pop esp 51 | iret 52 | 53 | enable_interrupts: 54 | sti 55 | ret 56 | 57 | disable_interrupts: 58 | cli 59 | ret 60 | 61 | ; protected mode exceptions 62 | no_error_code_handler 0 63 | no_error_code_handler 1 64 | no_error_code_handler 2 65 | no_error_code_handler 3 66 | no_error_code_handler 4 67 | no_error_code_handler 5 68 | no_error_code_handler 6 69 | no_error_code_handler 7 70 | error_code_handler 8 71 | no_error_code_handler 9 72 | error_code_handler 10 73 | error_code_handler 11 74 | error_code_handler 12 75 | error_code_handler 13 76 | error_code_handler 14 77 | no_error_code_handler 15 78 | no_error_code_handler 16 79 | error_code_handler 17 80 | no_error_code_handler 18 81 | no_error_code_handler 19 82 | 83 | ; irqs 84 | no_error_code_handler 32 85 | no_error_code_handler 33 86 | no_error_code_handler 34 87 | no_error_code_handler 35 88 | no_error_code_handler 36 89 | no_error_code_handler 37 90 | no_error_code_handler 38 91 | no_error_code_handler 39 92 | no_error_code_handler 40 93 | no_error_code_handler 41 94 | no_error_code_handler 42 95 | no_error_code_handler 43 96 | no_error_code_handler 44 97 | no_error_code_handler 45 98 | no_error_code_handler 46 99 | no_error_code_handler 47 100 | 101 | ; system call interrupt 102 | handle_syscall: 103 | ;cli ; TODO: replace with kernel locks 104 | push eax 105 | push ecx 106 | push edx 107 | push ebx 108 | push esp 109 | push ebp 110 | push esi 111 | push edi 112 | call syscall_handle_interrupt 113 | push eax 114 | call run_process_in_user_mode 115 | jmp $ 116 | 117 | switch_to_kernel_stack: 118 | cli ; can't be interrupted while running on shared stack 119 | mov eax, [esp+4] ; load address of continuation into eax 120 | mov ebx, [esp+8] ; load the data into ebx 121 | mov esp, kernel_stack + KERNEL_STACK_SIZE 122 | push ebx ; push the data on the new stack 123 | call eax ; use call instead of jmp since C expects ret addr 124 | jmp $ 125 | -------------------------------------------------------------------------------- /src/kernel/log.c: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | #include "serial.h" 3 | 4 | #define LOG_COM COM1 5 | 6 | static void log_ui(uint32_t i) 7 | { 8 | /* FIXME: please make this code more beautiful */ 9 | uint32_t n, digit; 10 | if (i >= 1000000000) { 11 | n = 1000000000; 12 | } else { 13 | n = 1; 14 | while (n*10 <= i) { 15 | n *= 10; 16 | } 17 | } 18 | while (n > 0) { 19 | digit = i / n; 20 | serial_write(LOG_COM, '0'+digit); 21 | i %= n; 22 | n /= 10; 23 | } 24 | } 25 | 26 | static void log_hex(uint32_t i) 27 | { 28 | char *digits = "0123456789ABCDEF"; 29 | uint32_t n, digit, min_digits = 8; 30 | 31 | /* find the largest nibble to output */ 32 | if (i >= 0x10000000) { 33 | n = 28; 34 | } else { 35 | n = 0; 36 | while ((((uint32_t)0x01) << (n+4)) <= i) { 37 | n += 4; 38 | } 39 | } 40 | 41 | serial_write(LOG_COM, '0'); 42 | serial_write(LOG_COM, 'x'); 43 | 44 | /* pad with zeroes */ 45 | if (min_digits > 0) { 46 | min_digits -= 1; 47 | } 48 | min_digits <<= 2; 49 | while (min_digits > n) { 50 | serial_write(LOG_COM, '0'); 51 | min_digits -= 4; 52 | } 53 | /* print the number */ 54 | while (1) { 55 | digit = (i >> n) & 0x0000000F; 56 | serial_write(LOG_COM, digits[digit]); 57 | if (n == 0) { 58 | break; 59 | } 60 | n -= 4; 61 | } 62 | } 63 | 64 | static void log_vprintf(char *fmt, va_list ap) 65 | { 66 | char *p; 67 | uint32_t uival; 68 | char *sval; 69 | 70 | for (p = fmt; *p != '\0'; ++p) { 71 | if (*p != '%') { 72 | serial_write(LOG_COM, *p); 73 | continue; 74 | } 75 | 76 | switch (*++p) { 77 | case 'c': 78 | uival = va_arg(ap, uint32_t); 79 | serial_write(LOG_COM, (uint8_t) uival); 80 | break; 81 | case 'u': 82 | uival = va_arg(ap, uint32_t); 83 | log_ui(uival); 84 | break; 85 | case 'X': 86 | uival = va_arg(ap, uint32_t); 87 | log_hex(uival); 88 | break; 89 | case 's': 90 | sval = va_arg(ap, char*); 91 | for(; *sval; ++sval) { 92 | serial_write(LOG_COM, *sval); 93 | } 94 | break; 95 | case '%': 96 | serial_write(LOG_COM, '%'); 97 | break; 98 | } 99 | } 100 | 101 | } 102 | 103 | static void log_printf(char *fmt, ...) 104 | { 105 | va_list ap; 106 | va_start(ap, fmt); 107 | log_vprintf(fmt, ap); 108 | va_end(ap); 109 | } 110 | 111 | void log_debug(char *fname, char *fmt, ...) 112 | { 113 | va_list ap; 114 | log_printf("DEBUG: %s: ", fname); 115 | va_start(ap, fmt); 116 | log_vprintf(fmt, ap); 117 | va_end(ap); 118 | } 119 | 120 | void log_info(char *fname, char *fmt, ...) 121 | { 122 | va_list ap; 123 | log_printf("INFO: %s: ", fname); 124 | va_start(ap, fmt); 125 | log_vprintf(fmt, ap); 126 | va_end(ap); 127 | } 128 | 129 | void log_error(char *fname, char *fmt, ...) 130 | { 131 | va_list ap; 132 | log_printf("ERROR: %s: ", fname); 133 | va_start(ap, fmt); 134 | log_vprintf(fmt, ap); 135 | va_end(ap); 136 | } 137 | -------------------------------------------------------------------------------- /src/kernel/devfs.c: -------------------------------------------------------------------------------- 1 | #include "devfs.h" 2 | #include "common.h" 3 | #include "kmalloc.h" 4 | #include "log.h" 5 | #include "string.h" 6 | 7 | struct devfs_inode { 8 | struct devfs_inode *next; 9 | char const *name; 10 | vnode_t *node; 11 | }; 12 | typedef struct devfs_inode devfs_inode_t; 13 | 14 | static devfs_inode_t *devices = NULL; 15 | 16 | static vnode_t root; 17 | static vnodeops_t vnodeops; 18 | static vfsops_t vfsops; 19 | 20 | static int devfs_root(vfs_t *vfs, vnode_t *out) 21 | { 22 | UNUSED_ARGUMENT(vfs); 23 | 24 | out->v_op = root.v_op; 25 | out->v_data = root.v_data; 26 | 27 | return 0; 28 | } 29 | 30 | static int devfs_open(vnode_t *n) 31 | { 32 | UNUSED_ARGUMENT(n); 33 | 34 | return -1; 35 | } 36 | static int devfs_read(vnode_t *node, void *buf, uint32_t count) 37 | { 38 | UNUSED_ARGUMENT(node); 39 | UNUSED_ARGUMENT(buf); 40 | UNUSED_ARGUMENT(count); 41 | 42 | return -1; 43 | } 44 | 45 | static int devfs_write(vnode_t *n, char const *s, size_t l) 46 | { 47 | UNUSED_ARGUMENT(n); 48 | UNUSED_ARGUMENT(s); 49 | UNUSED_ARGUMENT(l); 50 | 51 | return -1; 52 | } 53 | 54 | static int devfs_getattr(vnode_t *n, vattr_t *attr) 55 | { 56 | UNUSED_ARGUMENT(n); 57 | 58 | attr->file_size = 0; 59 | 60 | return 0; 61 | } 62 | 63 | static int devfs_lookup(vnode_t *dir, char const *name, vnode_t *out) 64 | { 65 | UNUSED_ARGUMENT(dir); 66 | 67 | if (name == NULL) { 68 | log_error("devfs_lookup", 69 | "Trying to lookup a null path\n"); 70 | return -1; 71 | } 72 | 73 | devfs_inode_t *i; 74 | for (i = devices; i != NULL; i = i->next) { 75 | if (strcmp(i->name, name) == 0) { 76 | out->v_op = i->node->v_op; 77 | out->v_data = i->node->v_data; 78 | return 0; 79 | } 80 | } 81 | 82 | return -1; 83 | } 84 | 85 | int devfs_add_device(char const *path, vnode_t *node) 86 | { 87 | if (path == NULL || strlen(path) == 0) { 88 | log_error("devfs_add_device", 89 | "path is empty or NULL\n"); 90 | return -1; 91 | } 92 | 93 | devfs_inode_t *i; 94 | for (i = devices; i != NULL; i = i->next) { 95 | if (strcmp(i->name, path) == 0) { 96 | log_error("devfs_add_device", 97 | "Trying to add a device that is already added: %s\n", 98 | path); 99 | return -1; 100 | } 101 | } 102 | 103 | int len = strlen(path) + 1; 104 | char *copy = kmalloc(len); 105 | if (copy == NULL) { 106 | log_error("devfs_add_device", 107 | "Could not allocated memory for path name %s\n", 108 | path); 109 | return -1; 110 | } 111 | memcpy(copy, path, len); 112 | 113 | devfs_inode_t *inode = kmalloc(sizeof(devfs_inode_t)); 114 | if (inode == NULL) { 115 | log_error("devfs_add_device", 116 | "Could not allocated memory for devfs_inode_t struct\n"); 117 | kfree(copy); 118 | return -1; 119 | } 120 | 121 | inode->next = NULL; 122 | inode->name = copy; 123 | inode->node = node; 124 | 125 | if (devices == NULL) { 126 | devices = inode; 127 | } else { 128 | devices->next = inode; 129 | } 130 | 131 | return 0; 132 | } 133 | 134 | int devfs_init(vfs_t *vfs) 135 | { 136 | vnodeops.vn_open = &devfs_open; 137 | vnodeops.vn_getattr = &devfs_getattr; 138 | vnodeops.vn_read = &devfs_read; 139 | vnodeops.vn_lookup = &devfs_lookup; 140 | vnodeops.vn_write = &devfs_write; 141 | root.v_op = &vnodeops; 142 | 143 | vfsops.vfs_root = &devfs_root; 144 | vfs->vfs_op = &vfsops; 145 | vfs->vfs_data = 0; 146 | 147 | return 0; 148 | } 149 | -------------------------------------------------------------------------------- /doc/proposal.tex: -------------------------------------------------------------------------------- 1 | \documentclass[11pt,oneside,a4paper]{article} 2 | 3 | \usepackage[utf8]{inputenc} 4 | \usepackage[swedish]{babel} 5 | \usepackage{enumerate} 6 | \usepackage{hyperref} 7 | 8 | \begin{document} 9 | \title{Avancerad individuell kurs, 15 hp} 10 | \author{Erik Helin \& Adam Renberg} 11 | \date{\today} 12 | \maketitle 13 | 14 | \section{Ämne} 15 | Kursen kommer vara en projektkurs inom operativsystem (OS). 16 | 17 | \section{Handledare} 18 | Torbjörn Granlund 19 | 20 | \section{Syfte} 21 | Syftet med kursen är att bygga vidare på kursen ID2206 Operativsystem, 22 | 7.5 hp, med en mer praktisk inriktning. Kursen kommer att fokusera 23 | på implementationen av en OS kärna för att tillämpa den teori som lärs 24 | ut i kursen ID2206. 25 | 26 | \section{Förkunskapskrav} 27 | Kursen ID2206 Operativsystem samt IS1200 Datorteknik 28 | 29 | \section{Tid} 30 | Kursen går i period 3 och det kommer vara den enda kurs som vi läser under 31 | den perioden. Vi räknar med att lägga 40 timmar i veckan under hela period 3. 32 | 33 | \section{Kursmål} 34 | Kursen mål är: 35 | \begin{itemize} 36 | \item Ge förståelse för hur drivrutiner implementeras 37 | \item Ge förståelse för hur timers implementeras 38 | \item Ge god insikt i svårigheterna med att implementera en OS kärna 39 | \item Ge god förståelse för hur virtuellt minne implementeras 40 | \item Ge god förståelse för hur ett fleranvändar-OS implementeras 41 | \end{itemize} 42 | 43 | \section{Examination} 44 | För att kursen ska anses avklarad krävs det att OS kärnan kan starta i 45 | simulatorn. När OS väl har startat skall text kunna skrivas ut på 46 | skärmen och användaren ska kunna använda tangentbordet. 47 | För godkänt krävs att det finns en distinktion mellan user-space och 48 | kernel-space, det program som körs i user-space ska \emph{inte} kunna komma åt 49 | kärnans minne. För godkänt 50 | krävs alltså \emph{inte} stöd för processer eller virtuellt minne. 51 | Vidare, för att betona vikten av OS kärnor måste vara korrekta måste koden vara 52 | väl dokumenterad och tydlig för att uppnå godkänt betyg. 53 | Slutligen skall även en rapport lämnas in som sammanfattar projektet. 54 | 55 | För högsta betyg krävs att flera program kan exekveras samtidigt samt att 56 | virtuellt minne är 57 | implementerat. Notera att det \emph{ej} krävs ett filsystem för att nå högsta 58 | betyg, men för att kunna demonstrera att flera program kan köras samtidigt 59 | kommer förmodligen ett readonly RAM filsystem att behövas. 60 | 61 | Betyg mellan godkänt och högsta betyg delas ut beroende på hur mycket som har 62 | hunnit bli implementerat av kriterierna för högsta betyg. 63 | 64 | \section{Förenklingar} 65 | För att det skall vara rimligt att bli klar med projektet under period 3 så 66 | kommer en del förenklingar att göras. 67 | Projektet behöver ej köra på riktig hårdvara, det räcker med om OS:t går att 68 | köra i x86 emulatorn bochs~\cite{bochs}. Projektet kommer även att använda 69 | GRUB~\cite{grub} som bootloader. 70 | 71 | \section{Kursplan} 72 | \begin{enumerate} 73 | \item Sätta upp en utvecklingsmiljö (lyckas bygga minsta möjliga kärna och 74 | starta den i bochs) 75 | \item Implementera drivrutiner för konsol, timer och tangentbord. 76 | \item Implementera readonly RAM filsystem (eller integrera befintligt) 77 | \item Implementera virtuellt minne 78 | \item Implementera stöd för flera användare 79 | \end{enumerate} 80 | 81 | \section{Kurslitteratur} 82 | Finns till stora delar tillgänglig på internet, wikin OSDev~\cite{os} 83 | har väldigt mycket relevant information. 84 | Det finns en liknande kurs på CMU~\cite{cmu} som också 85 | har mycket bra information. 86 | 87 | \begin{thebibliography}{9} 88 | \bibitem{bochs} 89 | \url{http://bochs.sourceforge.net/} 90 | 91 | \bibitem{grub} 92 | \url{http://www.gnu.org/software/grub/} 93 | 94 | \bibitem{cmu} 95 | \url{http://www.cs.cmu.edu/~410/} 96 | 97 | \bibitem{os} 98 | \url{http://wiki.osdev.org/Main_Page} 99 | \end{thebibliography} 100 | 101 | \end{document} 102 | -------------------------------------------------------------------------------- /src/kernel/loader.s: -------------------------------------------------------------------------------- 1 | ; this functions as a bridge between the bootloader (GRUB) and the kernel 2 | ; the main purpose of this file it to set up symbols for the bootloader 3 | ; and jump to the kmain function in the kernel 4 | 5 | %include "constants.inc" 6 | 7 | global loader ; the entry point for the linker 8 | global kernel_stack ; the address of the kernel stack 9 | 10 | extern kmain ; kmain is defined in kmain.c 11 | extern kernel_virtual_end ; these are defined in the link script 12 | extern kernel_virtual_start 13 | extern kernel_physical_end 14 | extern kernel_physical_start 15 | 16 | ; setting up the multiboot headers for GRUB 17 | MODULEALIGN equ 1<<0 ; align loaded modules on page 18 | ; boundaries 19 | MEMINFO equ 1<<1 ; provide memory map 20 | FLAGS equ MODULEALIGN | MEMINFO ; the multiboot flag field 21 | MAGIC equ 0x1BADB002 ; magic number for bootloader to 22 | ; find the header 23 | CHECKSUM equ -(MAGIC + FLAGS) ; checksum required 24 | 25 | ; we set Page write-through, Writable, Present 26 | KERNEL_PT_CFG equ 00000000000000000000000000001011b 27 | KERNEL_PDT_ID_MAP equ 00000000000000000000000010001011b 28 | 29 | ; the page directory used to boot the kernel into the higher half 30 | section .data 31 | align 4096 32 | kernel_pt: 33 | times 1024 dd 0 34 | kernel_pdt: 35 | dd KERNEL_PDT_ID_MAP 36 | times 1023 dd 0 37 | 38 | section .data 39 | align 4 40 | grub_magic_number: 41 | dd 0 42 | grub_multiboot_info: 43 | dd 0 44 | 45 | section .bss 46 | align 4 47 | kernel_stack: 48 | resb KERNEL_STACK_SIZE 49 | 50 | section .text 51 | align 4 52 | dd MAGIC 53 | dd FLAGS 54 | dd CHECKSUM 55 | 56 | ; the entry point, called by GRUB 57 | loader: 58 | mov ecx, (grub_magic_number - KERNEL_START_VADDR) 59 | mov [ecx], eax 60 | mov ecx, (grub_multiboot_info - KERNEL_START_VADDR) 61 | mov [ecx], ebx 62 | 63 | set_up_kernel_pdt: 64 | ; set up kernel_pdt to point to kernel_pt 65 | mov ecx, (kernel_pdt - KERNEL_START_VADDR + KERNEL_PDT_IDX*4) 66 | mov edx, (kernel_pt - KERNEL_START_VADDR) 67 | or edx, KERNEL_PT_CFG 68 | mov [ecx], edx 69 | 70 | set_up_kernel_pt: 71 | mov eax, (kernel_pt - KERNEL_START_VADDR) 72 | mov ecx, KERNEL_PT_CFG 73 | .loop: 74 | mov [eax], ecx 75 | add eax, 4 76 | add ecx, FOUR_KB 77 | cmp ecx, kernel_physical_end 78 | jle .loop 79 | 80 | enable_paging: 81 | mov ecx, (kernel_pdt - KERNEL_START_VADDR) 82 | and ecx, 0xFFFFF000 ; we only care about the upper 20 bits 83 | or ecx, 0x08 ; PWT, enable page write through? 84 | mov cr3, ecx ; load pdt 85 | 86 | mov ecx, cr4 ; read current config from cr4 87 | or ecx, 0x00000010 ; set bit enabling 4MB pages 88 | mov cr4, ecx ; enable it by writing to cr4 89 | 90 | mov ecx, cr0 ; read current config from cr0 91 | or ecx, 0x80000000 ; the highest bit controls paging 92 | mov cr0, ecx ; enable paging by writing config to cr0 93 | 94 | lea ecx, [higher_half] ; store the address higher_half in ecx 95 | jmp ecx ; now we jump into 0xC0100000 96 | 97 | ; code executing from here on uses the page table, and is accessed through 98 | ; the upper half, 0xC0100000 99 | higher_half: 100 | mov [kernel_pdt], DWORD 0 101 | invlpg [0] 102 | mov esp, kernel_stack+KERNEL_STACK_SIZE ; set up the stack 103 | 104 | enter_kmain: 105 | push kernel_pt 106 | push kernel_pdt 107 | push kernel_virtual_end ; these are used by kmain, see 108 | push kernel_virtual_start ; kernel_limits_t in kmain.c 109 | push kernel_physical_end 110 | push kernel_physical_start 111 | push DWORD [grub_magic_number] 112 | push DWORD [grub_multiboot_info] 113 | 114 | call kmain ; call the main function of the kernel 115 | hang: 116 | jmp hang ; loop forever 117 | -------------------------------------------------------------------------------- /src/kernel/serial.c: -------------------------------------------------------------------------------- 1 | #include "serial.h" 2 | #include "io.h" 3 | #include "interrupt.h" 4 | #include "common.h" 5 | #include "log.h" 6 | #include "pic.h" 7 | 8 | /* ports */ 9 | #define DATA_PORT(port) port 10 | #define INTERRUPT_ENABLE_PORT(port) port+1 11 | #define FIFO_CONTROL_PORT(port) port+2 12 | #define LINE_CONTROL_PORT(port) port+3 13 | #define MODEM_CONTROL_PORT(port) port+4 14 | #define LINE_STATUTS_PORT(port) port+5 15 | #define DLAB_LOW_BYTE_PORT(port) port 16 | #define DLAB_HIGH_BYTE_PORT(port) port + 1 17 | 18 | /* constants */ 19 | #define ENABLE_DLAB 0x80 20 | #define BAUD_RATE_DIVISOR 0x03 /* will give a baud rate of 115200 / 3 = 38400 */ 21 | 22 | static void serial_handle_interrupt_com1(cpu_state_t state, idt_info_t info, 23 | stack_state_t exec) 24 | { 25 | UNUSED_ARGUMENT(state); 26 | UNUSED_ARGUMENT(info); 27 | UNUSED_ARGUMENT(exec); 28 | log_info("serial_handle_interrupt_com1", "data on com1\n"); 29 | pic_acknowledge(); 30 | } 31 | 32 | static void serial_handle_interrupt_com2(cpu_state_t state, idt_info_t info, 33 | stack_state_t exec) 34 | { 35 | UNUSED_ARGUMENT(state); 36 | UNUSED_ARGUMENT(info); 37 | UNUSED_ARGUMENT(exec); 38 | log_info("serial_handle_interrupt_com2", "data on com2\n"); 39 | pic_acknowledge(); 40 | } 41 | 42 | void serial_init(uint16_t com) 43 | { 44 | uint8_t config; 45 | /* disable innterupts */ 46 | outb(INTERRUPT_ENABLE_PORT(com), 0x00); 47 | 48 | /* write the baud_rate_divisor */ 49 | outb(LINE_CONTROL_PORT(com), ENABLE_DLAB); 50 | outb(DLAB_LOW_BYTE_PORT(com), BAUD_RATE_DIVISOR & 0x00FF); 51 | outb(DLAB_HIGH_BYTE_PORT(com), (BAUD_RATE_DIVISOR & 0xFF00) >> 8); 52 | 53 | /* write the config */ 54 | 55 | /* name | value | size | desc 56 | * -------------------------- 57 | * dl | 3 | 2 | data length, 3 = 8 bits 58 | * sp | 0 | 1 | # stop bits, 0 = 1, 1 = 1.5 or 2 59 | * p | 0 | 3 | # parity bits, 0 = NONE 60 | * bc | 0 | 1 | break control, 0 = disabled, 1 = enabled 61 | * dlab | 0 | 1 | if dlab is enabled or not 62 | */ 63 | config = 0x00 | (1 << 1) | 0x01; 64 | outb(LINE_CONTROL_PORT(com), config); 65 | 66 | /* name | value | size | desc 67 | * -------------------------- 68 | * on | 1 | 1 | enable/disables the FIFO queues 69 | * rcvr | 1 | 1 | clear and reset the receiver FIFO queue 70 | * xmit | 1 | 1 | clear and reset the transmitter FIFO queue 71 | * dma | 0 | 1 | 0 = single byta DMA, 1 = multi byte DMA 72 | * res | 0 | 2 | reserved 73 | * lvl | 3 | 2 | num bytes to trigger FIFO, 0x3 = 14 bytes 74 | */ 75 | config = (1 << 7) | (1 << 6) | (1 << 2) | (1 << 1) | 0x01; 76 | outb(FIFO_CONTROL_PORT(com), config); 77 | 78 | /* name | value | size | desc 79 | * -------------------------- 80 | * dtr | 1 | 1 | DTR = Data Terminal Ready, 1 = on 81 | * rts | 1 | 1 | RTS = Request To Send, 1 = on 82 | * out1 | 0 | 1 | controls output 1, 0 = off 83 | * out2 | 1 | 1 | controls output 2, 1 = on 84 | * diag | 0 | 1 | diagnotic mode, 0 = off 85 | * resv | 0 | 3 | reserved 86 | */ 87 | config = (1 << 3) | (1 << 1) | 0x01; 88 | outb(MODEM_CONTROL_PORT(com), config); 89 | 90 | register_interrupt_handler(COM1_INT_IDX, serial_handle_interrupt_com1); 91 | register_interrupt_handler(COM2_INT_IDX, serial_handle_interrupt_com2); 92 | } 93 | 94 | static int is_transmit_fifo_empty(uint16_t com) 95 | { 96 | /* 0x20 = bit 5: 1 if XMIT fifo is empty */ 97 | return inb(LINE_STATUTS_PORT(com)) & 0x20; 98 | } 99 | 100 | void serial_write(uint16_t com, uint8_t data) 101 | { 102 | while (!is_transmit_fifo_empty(com)) { 103 | /* wait and try again */ 104 | } 105 | outb(DATA_PORT(com), data); 106 | } 107 | 108 | static int is_receiver_fifo_full(uint16_t com) 109 | { 110 | /* 0x01 = bit 5: 1 if RCVR fifo is full */ 111 | return inb(LINE_STATUTS_PORT(com)) & 0x01; 112 | } 113 | 114 | uint8_t serial_read(uint16_t com) 115 | { 116 | while (!is_receiver_fifo_full(com)) { 117 | /* wait and try again */ 118 | } 119 | 120 | return inb(DATA_PORT(com)); 121 | } 122 | -------------------------------------------------------------------------------- /src/kernel/kmalloc.c: -------------------------------------------------------------------------------- 1 | #include "kmalloc.h" 2 | #include "stdio.h" 3 | #include "log.h" 4 | #include "math.h" 5 | #include "paging.h" 6 | #include "page_frame_allocator.h" 7 | #include "constants.h" 8 | 9 | #define MIN_BLOCK_SIZE 1024 /* in units */ 10 | 11 | /* malloc() and free() as implemented by K&R */ 12 | 13 | typedef double align; 14 | 15 | struct header { 16 | struct header *next; 17 | size_t size; /* size in "number of header units" */ 18 | }; 19 | typedef struct header header_t; 20 | 21 | /* empty list to get started */ 22 | static header_t base; 23 | /* start of free list */ 24 | static header_t *freep = 0; 25 | 26 | static void *acquire_more_heap(size_t nunits); 27 | 28 | void *kmalloc(size_t nbytes) 29 | { 30 | header_t *p, *prevp; 31 | size_t nunits; 32 | 33 | if (nbytes == 0) 34 | return NULL; 35 | 36 | if (freep == 0) { 37 | /* no free list yet */ 38 | base.next = freep = &base; 39 | base.size = 0; 40 | } 41 | 42 | nunits = (nbytes+sizeof(header_t)-1)/sizeof(header_t) + 1; 43 | prevp = freep; 44 | 45 | for (p = prevp->next; ; prevp = p, p = p->next) { 46 | if (p->size >= nunits) { 47 | /* the block is big enough */ 48 | if (p->size == nunits) { 49 | /* exactly */ 50 | prevp->next = p->next; 51 | } else { 52 | /* allocate at the tail end of the block and create a new 53 | * header in front of allocated block */ 54 | p->size -= nunits; 55 | p += p->size; /* make p point to new header */ 56 | p->size = nunits; 57 | } 58 | freep = prevp; 59 | return (void *)(p+1); 60 | } 61 | if (p == freep) { 62 | /* wrapped around free list */ 63 | if ((p = acquire_more_heap(nunits)) == NULL) { 64 | log_error("kmalloc", "Cannot acquire more memory. memory: %u", 65 | nbytes); 66 | return NULL; 67 | } 68 | } 69 | } 70 | } 71 | 72 | static void *acquire_more_heap(size_t nunits) 73 | { 74 | uint32_t vaddr, paddr, bytes, page_frames, mapped_mem; 75 | header_t *p; 76 | 77 | if (nunits < MIN_BLOCK_SIZE) { 78 | nunits = MIN_BLOCK_SIZE; 79 | } 80 | 81 | page_frames = div_ceil(nunits * sizeof(header_t), FOUR_KB); 82 | bytes = page_frames * FOUR_KB; 83 | 84 | paddr = pfa_allocate(page_frames); 85 | if (paddr == 0) { 86 | log_error("acquire_more_heap", 87 | "Could't allocated page frames for kmalloc. " 88 | "page_frames: %u, bytes: %u\n", 89 | page_frames, bytes); 90 | return NULL; 91 | } 92 | 93 | vaddr = pdt_kernel_find_next_vaddr(bytes); 94 | if (vaddr == 0) { 95 | log_error("acquire_more_heap", 96 | "Could't find a virtual address. " 97 | "paddr: %X, page_frames: %u, bytes: %u\n", 98 | paddr, page_frames, bytes); 99 | return NULL; 100 | } 101 | 102 | mapped_mem = pdt_map_kernel_memory(paddr, vaddr, bytes, 103 | PAGING_READ_WRITE, PAGING_PL0); 104 | if (mapped_mem < bytes) { 105 | log_error("acquire_more_heap", 106 | "Could't map virtual memory. " 107 | "vaddr: %X, paddr: %X, page_frames: %u, bytes: %u\n", 108 | vaddr, paddr, page_frames, bytes); 109 | return NULL; 110 | } 111 | 112 | p = (header_t *) vaddr; 113 | p->size = bytes / sizeof(header_t); 114 | 115 | kfree((void *)(p+1)); 116 | 117 | return freep; 118 | } 119 | 120 | void kfree(void * ap) 121 | { 122 | header_t *bp, *p; 123 | 124 | if (ap == 0) 125 | return; 126 | 127 | /* point to block header */ 128 | bp = (header_t *)ap - 1; 129 | for (p = freep; !(bp > p && bp < p->next); p = p->next) { 130 | if (p >= p->next && (bp > p || bp < p->next)) { 131 | /* freed block at start of end of arena */ 132 | break; 133 | } 134 | } 135 | 136 | if (bp + bp->size == p->next) { 137 | /* join to upper nbr */ 138 | bp->size += p->next->size; 139 | bp->next = p->next->next; 140 | } else { 141 | bp->next = p->next; 142 | } 143 | 144 | if (p + p->size == bp) { 145 | /* join to lower nbr */ 146 | p->size += bp->size; 147 | p->next = bp->next; 148 | } else { 149 | p->next = bp; 150 | } 151 | 152 | /* TODO: If the block is larger than a page frame, give back to pfa! */ 153 | freep = p; 154 | } 155 | -------------------------------------------------------------------------------- /src/kernel/gdt.c: -------------------------------------------------------------------------------- 1 | #include "gdt.h" 2 | #include "tss.h" 3 | 4 | #define SEGMENT_BASE 0 5 | #define SEGMENT_LIMIT 0xFFFFF 6 | 7 | #define CODE_RX_TYPE 0xA 8 | #define DATA_RW_TYPE 0x2 9 | 10 | #define GDT_NUM_ENTRIES 6 11 | 12 | #define TSS_SEGSEL (5*8) 13 | 14 | struct gdt_entry { 15 | uint16_t limit_low; /* The lower 16 bits of the limit */ 16 | uint16_t base_low; /* The lower 16 bits of the base */ 17 | uint8_t base_mid; /* The next 8 bits of the base */ 18 | uint8_t access; /* Contains access flags */ 19 | uint8_t granularity; /* Specify granularity, and 4 bits of limit */ 20 | uint8_t base_high; /* The last 8 bits of the base; */ 21 | } __attribute__((packed)); /* It needs to be packed like this, 64 bits */ 22 | typedef struct gdt_entry gdt_entry_t; 23 | 24 | struct gdt_ptr { 25 | uint16_t limit; /* Size of gdt table in bytes*/ 26 | uint32_t base; /* Address to the first gdt entry */ 27 | } __attribute__((packed)); 28 | typedef struct gdt_ptr gdt_ptr_t; 29 | 30 | gdt_entry_t gdt_entries[GDT_NUM_ENTRIES]; 31 | 32 | /* external assembly function to set the gdt */ 33 | void gdt_load_and_set(uint32_t); 34 | static void gdt_create_entry(uint32_t n, uint8_t pl, uint8_t type); 35 | static void gdt_create_tss_entry(uint32_t n, uint32_t tss_vaddr); 36 | 37 | void gdt_init(uint32_t tss_vaddr) 38 | { 39 | gdt_ptr_t gdt_ptr; 40 | gdt_ptr.limit = sizeof(gdt_entry_t)*GDT_NUM_ENTRIES; 41 | gdt_ptr.base = (uint32_t)&gdt_entries; 42 | 43 | /* the null entry */ 44 | gdt_create_entry(0, 0, 0); 45 | /* kernel mode code segment */ 46 | gdt_create_entry(1, PL0, CODE_RX_TYPE); 47 | /* kernel mode data segment */ 48 | gdt_create_entry(2, PL0, DATA_RW_TYPE); 49 | /* user mode code segment */ 50 | gdt_create_entry(3, PL3, CODE_RX_TYPE); 51 | /* user mode data segment */ 52 | gdt_create_entry(4, PL3, DATA_RW_TYPE); 53 | 54 | gdt_create_tss_entry(5, tss_vaddr); 55 | 56 | gdt_load_and_set((uint32_t)&gdt_ptr); 57 | 58 | tss_load_and_set(TSS_SEGSEL); 59 | } 60 | 61 | 62 | 63 | static void gdt_create_entry(uint32_t n, uint8_t pl, uint8_t type) 64 | { 65 | gdt_entries[n].base_low = (SEGMENT_BASE & 0xFFFF); 66 | gdt_entries[n].base_mid = (SEGMENT_BASE >> 16) & 0xFF; 67 | gdt_entries[n].base_high = (SEGMENT_BASE >> 24) & 0xFF; 68 | 69 | gdt_entries[n].limit_low = (SEGMENT_LIMIT & 0xFFFF); 70 | 71 | /* 72 | * name | value | size | desc 73 | * --------------------------- 74 | * G | 1 | 1 | granularity, size of segment unit, 1 = 4kB 75 | * D/B | 1 | 1 | size of operation size, 0 = 16 bits, 1 = 32 bits 76 | * L | 0 | 1 | 1 = 64 bit code 77 | * AVL | 0 | 1 | "available for use by system software" 78 | * LIM | 0xF | 4 | the four highest bits of segment limit 79 | */ 80 | gdt_entries[n].granularity |= (0x01 << 7) | (0x01 << 6) | 0x0F; 81 | 82 | /* 83 | * name | value | size | desc 84 | * --------------------------- 85 | * P | 1 | 1 | segment present in memory 86 | * DPL | pl | 2 | privilege level 87 | * S | 1 | 1 | descriptor type, 0 = system, 1 = code or data 88 | * Type | type | 4 | segment type, how the segment can be accessed 89 | */ 90 | gdt_entries[n].access = 91 | (0x01 << 7) | ((pl & 0x03) << 5) | (0x01 << 4) | (type & 0x0F); 92 | } 93 | 94 | static void gdt_create_tss_entry(uint32_t n, uint32_t tss_vaddr) 95 | { 96 | gdt_entries[n].base_low = (tss_vaddr & 0xFFFF); 97 | gdt_entries[n].base_mid = (tss_vaddr >> 16) & 0xFF; 98 | gdt_entries[n].base_high = (tss_vaddr >> 24) & 0xFF; 99 | 100 | gdt_entries[n].limit_low = sizeof(tss_t) - 1; 101 | 102 | /* access should here be called type */ 103 | /* 104 | * name | values | size | desc 105 | * 1 | 1 | 1 | Constant 106 | * B | 0 | 1 | Busy 107 | * 0 | 0 | 1 | Constant 108 | * 1 | 1 | 1 | Constant 109 | * 0 | 0 | 1 | Constant 110 | * DPL | PL0 | 2 | Privilege level 111 | * P | 1 | 1 | Present 112 | */ 113 | gdt_entries[n].access = (0x01 << 7) | (0x01 << 3) | (0x01); 114 | 115 | /* 116 | * name | value | size | desc 117 | * --------------------------- 118 | * G | 0 | 1 | granularity, size of segment unit, 0 = bytes 119 | * 0 | 0 | 1 | Constant 120 | * 0 | 0 | 1 | Constant 121 | * AVL | 0 | 1 | "available for use by system software" 122 | * LIM | 0 | 4 | the four highest bits of segment limit 123 | */ 124 | gdt_entries[n].granularity = 0; 125 | } 126 | -------------------------------------------------------------------------------- /src/rdaefs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "kernel/aefs.h" 11 | 12 | static void die(char const *msg) 13 | { 14 | perror(msg); 15 | exit(1); 16 | } 17 | 18 | uint32_t div_ceil(uint32_t num, uint32_t den) 19 | { 20 | return (num - 1) / den + 1; 21 | } 22 | 23 | struct block { 24 | char data[AEFS_BLOCK_SIZE]; 25 | } __attribute__((packed)); 26 | typedef struct block block_t; 27 | 28 | static block_t *file; 29 | static uint16_t num_inodes; 30 | static block_t *block_start; 31 | 32 | void read_superblock() 33 | { 34 | aefs_superblock_t *sb = (aefs_superblock_t *) file; 35 | printf("-> Superblock\n"); 36 | printf("\tmagic_number: %X\n", sb->magic_number); 37 | printf("\tnum_inodes: %hu\n", sb->num_inodes); 38 | printf("\tstart_block: %hu\n", sb->start_block); 39 | printf("\n"); 40 | num_inodes = sb->num_inodes; 41 | block_start = file + sb->start_block; 42 | } 43 | 44 | aefs_inode_t *get_inode(uint16_t inode_id) 45 | { 46 | return ((aefs_inode_t *)(file + 1)) + inode_id; 47 | } 48 | 49 | void print_inode_tail(aefs_inode_t *inode) 50 | { 51 | uint32_t i = 0; 52 | for (; i < AEFS_INODE_NUM_BLOCKS; ++i) { 53 | printf("\tblock %u: %hu\n", i, inode->blocks[i]); 54 | } 55 | printf("\ttail: %hu\n", inode->inode_tail); 56 | if (inode->inode_tail != 0) { 57 | print_inode_tail(get_inode(inode->inode_tail)); 58 | } 59 | } 60 | 61 | void print_inode(aefs_inode_t *inode) { 62 | printf("\ttype: %hhu\n", inode->type); 63 | printf("\tsize_high: %hhu\n", inode->size_high); 64 | printf("\tsize_low: %hu\n", inode->size_low); 65 | uint32_t i = 0; 66 | for (; i < AEFS_INODE_NUM_BLOCKS; ++i) { 67 | printf("\tblock %u: %hu\n", i, inode->blocks[i]); 68 | } 69 | printf("\ttail: %hu\n", inode->inode_tail); 70 | if (inode->inode_tail != 0) { 71 | print_inode_tail(get_inode(inode->inode_tail)); 72 | } 73 | } 74 | 75 | void visit_reg(aefs_inode_t *inode, char const *path); 76 | void visit_dir(aefs_inode_t *inode, char const *path); 77 | 78 | void visit_inode(aefs_inode_t *inode, char const *path) 79 | { 80 | if (AEFS_INODE_IS_REG(inode)) { 81 | visit_reg(inode, path); 82 | } else if (AEFS_INODE_IS_DIR(inode)) { 83 | visit_dir(inode, path); 84 | } else { 85 | die("ERROR: Unknown file type"); 86 | } 87 | } 88 | 89 | void print_root() 90 | { 91 | aefs_inode_t *root_inode = get_inode(1); 92 | visit_inode(root_inode, "/"); 93 | } 94 | 95 | 96 | void visit_reg(aefs_inode_t *inode, char const *path) 97 | { 98 | printf("-> REG: %s\n", path); 99 | print_inode(inode); 100 | } 101 | 102 | void visit_dir(aefs_inode_t *inode, char const *path) 103 | { 104 | int num_entries = AEFS_INODE_SIZE(inode)/sizeof(aefs_direntry_t); 105 | int i; 106 | aefs_direntry_t *entry; 107 | block_t *block; 108 | int block_num = 0; 109 | aefs_inode_t *inode_to_read_from = inode; 110 | printf("-> DIR: %s (%hu entries)\n", path, num_entries); 111 | print_inode(inode); 112 | 113 | char *child_path = malloc(strlen(path) + AEFS_FILENAME_MAX_LEN + 1); 114 | if (child_path == NULL) { 115 | die("ERROR: Out of memory"); 116 | } 117 | 118 | for (i = 0; i < num_entries; ++i) { 119 | if (i % (AEFS_DIRENTRIES_PER_BLOCK) == 0) { 120 | block = block_start + inode_to_read_from->blocks[block_num]; 121 | block_num++; 122 | if (block_num == AEFS_INODE_NUM_BLOCKS) { 123 | block_num = 0; 124 | inode_to_read_from = get_inode(inode_to_read_from->inode_tail); 125 | } 126 | } 127 | entry = ((aefs_direntry_t *) (block)) + (i % AEFS_DIRENTRIES_PER_BLOCK); 128 | printf("\t\t-> ENTRY: %s -> %hu\n", entry->name, entry->inode_id); 129 | 130 | sprintf(child_path, "%s/%s", path, entry->name); 131 | visit_inode(get_inode(entry->inode_id), child_path); 132 | } 133 | 134 | free(child_path); 135 | } 136 | 137 | int main(int argc, char **argv) 138 | { 139 | if (argc != 2) { 140 | fprintf(stderr, "Too many arguments\n"); 141 | fprintf(stderr, "Usage: %s fs\n", argv[0]); 142 | exit(1); 143 | } 144 | 145 | 146 | int fd = open(argv[1], O_RDONLY); 147 | if (fd == -1) { 148 | die("ERROR: Can't open fs"); 149 | } 150 | struct stat st; 151 | 152 | fstat(fd, &st); 153 | 154 | void *mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 155 | if (mem == MAP_FAILED) { 156 | die("ERROR: Couldn't mmap file"); 157 | } 158 | 159 | file = (block_t *) mem; 160 | 161 | read_superblock(); 162 | print_root(); 163 | 164 | if (munmap(mem, st.st_size)) { 165 | die("ERROR: Could non munmap file"); 166 | } 167 | close(fd); 168 | 169 | return 0; 170 | } 171 | -------------------------------------------------------------------------------- /src/kernel/idt.c: -------------------------------------------------------------------------------- 1 | #include "idt.h" 2 | #include "stdint.h" 3 | #include "gdt.h" 4 | #include "interrupt.h" 5 | #include "constants.h" 6 | 7 | #define IDT_INTERRUPT_GATE_TYPE 0 8 | #define IDT_TRAP_GATE_TYPE 1 9 | 10 | #define IDT_TIMER_INTERRUPT_INDEX 0x20 11 | #define IDT_KEYBOARD_INTERRUPT_INDEX 0x21 12 | 13 | #define CREATE_IDT_GATE(idx) \ 14 | create_idt_gate(idx, (uint32_t) &interrupt_handler_##idx,\ 15 | IDT_TRAP_GATE_TYPE, PL0); 16 | 17 | #define DECLARE_INTERRUPT_HANDLER(i) void interrupt_handler_##i(void) 18 | 19 | void debug_handler(void); 20 | /* Protected mode exceptions interrupts */ 21 | DECLARE_INTERRUPT_HANDLER(0); 22 | DECLARE_INTERRUPT_HANDLER(1); 23 | DECLARE_INTERRUPT_HANDLER(2); 24 | DECLARE_INTERRUPT_HANDLER(3); 25 | DECLARE_INTERRUPT_HANDLER(4); 26 | DECLARE_INTERRUPT_HANDLER(5); 27 | DECLARE_INTERRUPT_HANDLER(6); 28 | DECLARE_INTERRUPT_HANDLER(7); 29 | DECLARE_INTERRUPT_HANDLER(8); 30 | DECLARE_INTERRUPT_HANDLER(9); 31 | DECLARE_INTERRUPT_HANDLER(10); 32 | DECLARE_INTERRUPT_HANDLER(11); 33 | DECLARE_INTERRUPT_HANDLER(12); 34 | DECLARE_INTERRUPT_HANDLER(13); 35 | DECLARE_INTERRUPT_HANDLER(14); 36 | DECLARE_INTERRUPT_HANDLER(15); 37 | DECLARE_INTERRUPT_HANDLER(16); 38 | DECLARE_INTERRUPT_HANDLER(17); 39 | DECLARE_INTERRUPT_HANDLER(18); 40 | DECLARE_INTERRUPT_HANDLER(19); 41 | 42 | /* IRQs */ 43 | DECLARE_INTERRUPT_HANDLER(32); 44 | DECLARE_INTERRUPT_HANDLER(33); 45 | DECLARE_INTERRUPT_HANDLER(34); 46 | DECLARE_INTERRUPT_HANDLER(35); 47 | DECLARE_INTERRUPT_HANDLER(36); 48 | DECLARE_INTERRUPT_HANDLER(37); 49 | DECLARE_INTERRUPT_HANDLER(38); 50 | DECLARE_INTERRUPT_HANDLER(39); 51 | DECLARE_INTERRUPT_HANDLER(40); 52 | DECLARE_INTERRUPT_HANDLER(41); 53 | DECLARE_INTERRUPT_HANDLER(42); 54 | DECLARE_INTERRUPT_HANDLER(43); 55 | DECLARE_INTERRUPT_HANDLER(44); 56 | DECLARE_INTERRUPT_HANDLER(45); 57 | DECLARE_INTERRUPT_HANDLER(46); 58 | DECLARE_INTERRUPT_HANDLER(47); 59 | 60 | struct idt_gate { 61 | uint16_t handler_low; 62 | uint16_t segsel; 63 | uint8_t zero; 64 | uint8_t config; 65 | uint16_t handler_high; 66 | } __attribute__((packed)); 67 | typedef struct idt_gate idt_gate_t; 68 | 69 | struct idt_ptr { 70 | uint16_t limit; 71 | uint32_t base; 72 | } __attribute__((packed)); 73 | typedef struct idt_ptr idt_ptr_t; 74 | 75 | idt_gate_t idt[IDT_NUM_ENTRIES]; 76 | 77 | /* external assembly function for loading the ldt */ 78 | void idt_load_and_set(uint32_t idt_ptr); 79 | 80 | /* external assembly function for handling syscalls */ 81 | void handle_syscall(void); 82 | 83 | static void create_idt_gate(uint8_t n, uint32_t handler, uint8_t type, 84 | uint8_t pl); 85 | 86 | void idt_init(void) 87 | { 88 | idt_ptr_t idt_ptr; 89 | idt_ptr.limit = IDT_NUM_ENTRIES * sizeof(idt_gate_t) - 1; 90 | idt_ptr.base = (uint32_t) &idt; 91 | 92 | /* Protected mode exceptions */ 93 | CREATE_IDT_GATE(0); 94 | CREATE_IDT_GATE(1); 95 | CREATE_IDT_GATE(2); 96 | CREATE_IDT_GATE(3); 97 | CREATE_IDT_GATE(4); 98 | CREATE_IDT_GATE(5); 99 | CREATE_IDT_GATE(6); 100 | CREATE_IDT_GATE(7); 101 | CREATE_IDT_GATE(8); 102 | CREATE_IDT_GATE(9); 103 | CREATE_IDT_GATE(10); 104 | CREATE_IDT_GATE(11); 105 | CREATE_IDT_GATE(12); 106 | CREATE_IDT_GATE(13); 107 | CREATE_IDT_GATE(14); 108 | CREATE_IDT_GATE(15); 109 | CREATE_IDT_GATE(16); 110 | CREATE_IDT_GATE(17); 111 | CREATE_IDT_GATE(18); 112 | CREATE_IDT_GATE(19); 113 | 114 | /* IRQs */ 115 | CREATE_IDT_GATE(32); 116 | CREATE_IDT_GATE(33); 117 | CREATE_IDT_GATE(34); 118 | CREATE_IDT_GATE(35); 119 | CREATE_IDT_GATE(36); 120 | CREATE_IDT_GATE(37); 121 | CREATE_IDT_GATE(38); 122 | CREATE_IDT_GATE(39); 123 | CREATE_IDT_GATE(40); 124 | CREATE_IDT_GATE(41); 125 | CREATE_IDT_GATE(42); 126 | CREATE_IDT_GATE(43); 127 | CREATE_IDT_GATE(44); 128 | CREATE_IDT_GATE(45); 129 | CREATE_IDT_GATE(46); 130 | CREATE_IDT_GATE(47); 131 | 132 | /* System call interrupt */ 133 | create_idt_gate(SYSCALL_INT_IDX, (uint32_t) handle_syscall, 134 | IDT_TRAP_GATE_TYPE, PL3); 135 | 136 | idt_load_and_set((uint32_t) &idt_ptr); 137 | } 138 | 139 | static void create_idt_gate(uint8_t n, uint32_t handler, uint8_t type, 140 | uint8_t pl) 141 | { 142 | idt[n].handler_low = handler & 0x0000FFFF; 143 | idt[n].handler_high = (handler >> 16) & 0x0000FFFF; 144 | 145 | idt[n].segsel = SEGSEL_KERNEL_CS; 146 | idt[n].zero = 0; 147 | 148 | /* name | value | size | desc 149 | * -------------------------- 150 | * P | 1 | 1 | segment present in memory 151 | * DPL | pl | 2 | privilege level 152 | * 0 | 0 | 1 | a zero bit 153 | * D | 1 | 1 | size of gate, 1 = 32 bits, 0 = 16 bits 154 | * 1 | 1 | 1 | a one bit 155 | * 1 | 1 | 1 | a one bit 156 | * T | type | 1 | the type of the gate, 1 = trap, 0 = interrupt 157 | */ 158 | idt[n].config = 159 | (0x01 << 7) | 160 | ((pl & 0x03) << 5) | 161 | (0x01 << 3) | 162 | (0x01 << 2) | 163 | (0x01 << 1) | 164 | type; 165 | } 166 | 167 | -------------------------------------------------------------------------------- /src/kernel/fb.c: -------------------------------------------------------------------------------- 1 | #include "fb.h" 2 | #include "io.h" 3 | #include "common.h" 4 | #include "vfs.h" 5 | #include "log.h" 6 | 7 | #define FB_MEMORY KERNEL_START_VADDR + 0x000B8000 8 | 9 | #define FB_NUM_COLS 80 10 | #define FB_NUM_ROWS 25 11 | 12 | #define FB_CURSOR_DATA_PORT 0x3D5 13 | #define FB_CURSOR_INDEX_PORT 0x3D4 14 | 15 | #define FB_HIGH_BYTE 14 16 | #define FB_LOW_BYTE 15 17 | 18 | #define BLACK_ON_WHITE 0x0F 19 | 20 | #define TO_ADDRESS(row, col) (fb + 2 * (row * FB_NUM_COLS + col)) 21 | 22 | #define FB_BACKSPACE_ASCII 8 23 | 24 | static uint8_t *fb = (uint8_t *) FB_MEMORY; 25 | static uint16_t cursor_pos; 26 | static vnodeops_t vnodeops; 27 | 28 | static uint8_t read_cell(uint32_t row, uint32_t col) 29 | { 30 | uint8_t *cell = TO_ADDRESS(row, col); 31 | return *cell; 32 | } 33 | 34 | static void write_cell(uint8_t *cell, uint8_t b) 35 | { 36 | cell[0] = b; 37 | cell[1] = BLACK_ON_WHITE; 38 | } 39 | 40 | static void write_at(uint8_t b, uint32_t row, uint32_t col) 41 | { 42 | uint8_t *cell = TO_ADDRESS(row, col); 43 | write_cell(cell, b); 44 | } 45 | 46 | 47 | static void set_cursor(uint16_t loc) 48 | { 49 | outb(FB_CURSOR_INDEX_PORT, FB_HIGH_BYTE); 50 | outb(FB_CURSOR_DATA_PORT, loc >> 8); 51 | outb(FB_CURSOR_INDEX_PORT, FB_LOW_BYTE); 52 | outb(FB_CURSOR_DATA_PORT, loc); 53 | } 54 | 55 | static void move_cursor_forward(void) 56 | { 57 | cursor_pos++; 58 | set_cursor(cursor_pos); 59 | } 60 | 61 | static void move_cursor_back(void) 62 | { 63 | if (cursor_pos != 0) { 64 | cursor_pos--; 65 | set_cursor(cursor_pos); 66 | } 67 | } 68 | 69 | static void move_cursor_down() 70 | { 71 | cursor_pos += FB_NUM_COLS; 72 | set_cursor(cursor_pos); 73 | } 74 | 75 | static void move_cursor_start() 76 | { 77 | cursor_pos -= cursor_pos % FB_NUM_COLS; 78 | set_cursor(cursor_pos); 79 | } 80 | 81 | static void scroll() 82 | { 83 | uint32_t r, c; 84 | for (r = 1; r < FB_NUM_ROWS; ++r) { 85 | for (c = 0; c < FB_NUM_COLS; ++c) { 86 | write_at(read_cell(r, c), r - 1, c); 87 | } 88 | } 89 | 90 | for (c = 0; c < FB_NUM_COLS; ++c) { 91 | write_at(' ', FB_NUM_ROWS - 1, c); 92 | } 93 | } 94 | 95 | void fb_put_b(uint8_t b) 96 | { 97 | if (b != '\n' && b != '\t' && b != FB_BACKSPACE_ASCII) { 98 | uint8_t *cell = fb + 2 * cursor_pos; 99 | write_cell(cell, b); 100 | } 101 | 102 | if (b == '\n') { 103 | move_cursor_down(); 104 | move_cursor_start(); 105 | } else if (b == FB_BACKSPACE_ASCII) { 106 | move_cursor_back(); 107 | uint8_t *cell = fb + 2 * cursor_pos; 108 | write_cell(cell, ' '); 109 | } else if (b == '\t') { 110 | int i; 111 | for (i = 0; i < 4; ++i) { 112 | fb_put_b(' '); 113 | } 114 | } else { 115 | move_cursor_forward(); 116 | } 117 | 118 | if (cursor_pos >= FB_NUM_COLS * FB_NUM_ROWS) { 119 | scroll(); 120 | fb_move_cursor(24, 0); 121 | } 122 | } 123 | 124 | void fb_put_s(char const *s) 125 | { 126 | while (*s != '\0') { 127 | fb_put_b(*s++); 128 | } 129 | } 130 | 131 | void fb_put_ui(uint32_t i) 132 | { 133 | /* FIXME: please make this code more beautiful */ 134 | uint32_t n, digit; 135 | if (i >= 1000000000) { 136 | n = 1000000000; 137 | } else { 138 | n = 1; 139 | while (n*10 <= i) { 140 | n *= 10; 141 | } 142 | } 143 | while (n > 0) { 144 | digit = i / n; 145 | fb_put_b('0'+digit); 146 | i %= n; 147 | n /= 10; 148 | } 149 | } 150 | 151 | void fb_put_ui_hex(unsigned int n) 152 | { 153 | char *chars = "0123456789ABCDEF"; 154 | unsigned char b = 0; 155 | int i; 156 | 157 | fb_put_s("0x"); 158 | 159 | for (i = 7; i >= 0; --i) { 160 | b = (n >> i*4) & 0x0F; 161 | fb_put_b(chars[b]); 162 | } 163 | } 164 | 165 | void fb_clear() 166 | { 167 | uint8_t i, j; 168 | for (i = 0; i < FB_NUM_ROWS; ++i) { 169 | for (j = 0; j < FB_NUM_COLS; ++j) { 170 | write_at(' ', i, j); 171 | } 172 | } 173 | fb_move_cursor(0, 0); 174 | } 175 | 176 | void fb_move_cursor(uint16_t row, uint16_t col) 177 | { 178 | uint16_t loc = row*FB_NUM_COLS + col; 179 | cursor_pos = loc; 180 | set_cursor(loc); 181 | } 182 | 183 | static int fb_open(vnode_t *n) 184 | { 185 | UNUSED_ARGUMENT(n); 186 | 187 | return 0; 188 | } 189 | 190 | static int fb_lookup(vnode_t *n, char const *p, vnode_t *o) 191 | { 192 | UNUSED_ARGUMENT(n); 193 | UNUSED_ARGUMENT(p); 194 | UNUSED_ARGUMENT(o); 195 | 196 | return -1; 197 | } 198 | 199 | static int fb_read(vnode_t *n, void *buf, uint32_t count) 200 | { 201 | UNUSED_ARGUMENT(n); 202 | UNUSED_ARGUMENT(buf); 203 | UNUSED_ARGUMENT(count); 204 | 205 | /* TODO: this can actually be implemented by copying the console memory */ 206 | 207 | return -1; 208 | } 209 | 210 | static int fb_getattr(vnode_t *n, vattr_t *attr) 211 | { 212 | UNUSED_ARGUMENT(n); 213 | UNUSED_ARGUMENT(attr); 214 | 215 | return -1; 216 | } 217 | 218 | static int fb_write(vnode_t *n, char const *str, size_t len) 219 | { 220 | UNUSED_ARGUMENT(n); 221 | UNUSED_ARGUMENT(len); 222 | 223 | fb_put_s(str); 224 | 225 | return 0; 226 | } 227 | 228 | int fb_init(void) 229 | { 230 | vnodeops.vn_open = &fb_open; 231 | vnodeops.vn_lookup = &fb_lookup; 232 | vnodeops.vn_read = &fb_read; 233 | vnodeops.vn_getattr = &fb_getattr; 234 | vnodeops.vn_write = &fb_write; 235 | 236 | return 0; 237 | } 238 | 239 | int fb_get_vnode(vnode_t *out) 240 | { 241 | out->v_op = &vnodeops; 242 | out->v_data = 123456; 243 | 244 | return 0; 245 | } 246 | -------------------------------------------------------------------------------- /src/kernel/aefs.c: -------------------------------------------------------------------------------- 1 | #include "aefs.h" 2 | #include "string.h" 3 | #include "log.h" 4 | #include "paging.h" 5 | #include "common.h" 6 | #include "math.h" 7 | 8 | #define ROOT_INODE_ID 1 9 | 10 | struct aefs_block { 11 | char data[AEFS_BLOCK_SIZE]; 12 | } __attribute__((packed)); 13 | typedef struct aefs_block aefs_block_t; 14 | 15 | static uint32_t fs_paddr; 16 | static uint32_t fs_size; 17 | static aefs_superblock_t *sb; 18 | static aefs_inode_t *root_inode; 19 | static aefs_block_t *fs_start; 20 | static vnodeops_t vnodeops; 21 | static vfsops_t vfsops; 22 | 23 | static int aefs_root(vfs_t *vfs, vnode_t *vroot) 24 | { 25 | UNUSED_ARGUMENT(vfs); 26 | 27 | vroot->v_op = &vnodeops; 28 | vroot->v_data = (uint32_t) root_inode; 29 | 30 | return 0; 31 | } 32 | 33 | static aefs_inode_t *get_inode(uint16_t inode_id) 34 | { 35 | return ((aefs_inode_t *)(fs_start + 1)) + inode_id; 36 | } 37 | 38 | static aefs_block_t *get_block(uint32_t i) 39 | { 40 | return fs_start + sb->start_block + i; 41 | } 42 | 43 | static int aefs_getattr(vnode_t *vnode, vattr_t *attr) 44 | { 45 | aefs_inode_t *inode = (aefs_inode_t *) vnode->v_data; 46 | attr->file_size = AEFS_INODE_SIZE(inode); 47 | 48 | return 0; 49 | } 50 | 51 | static int aefs_read(vnode_t *vnode, void *buf, uint32_t count) 52 | { 53 | uint32_t i, read = 0, to_read; 54 | aefs_inode_t *inode = (aefs_inode_t *) vnode->v_data; 55 | 56 | if (!AEFS_INODE_IS_REG(inode)) { 57 | return -1; 58 | } 59 | 60 | uint32_t size = minu(count, AEFS_INODE_SIZE(inode)); 61 | uint32_t blocks = div_ceil(size, AEFS_BLOCK_SIZE); 62 | 63 | aefs_inode_t *current = inode; 64 | for (i = 0; i < blocks; ++i) { 65 | if (i % AEFS_INODE_NUM_BLOCKS == 0 && i != 0) { 66 | current = get_inode(current->inode_tail); 67 | } 68 | 69 | to_read = minu(size, AEFS_BLOCK_SIZE); 70 | memcpy(buf + read, 71 | get_block(current->blocks[i % AEFS_INODE_NUM_BLOCKS]), 72 | to_read); 73 | size -= to_read; 74 | read += to_read; 75 | } 76 | 77 | return read; 78 | } 79 | 80 | static int aefs_write(vnode_t *n, char const *s, size_t l) 81 | { 82 | /* TODO: Not implemented yet! */ 83 | UNUSED_ARGUMENT(n); 84 | UNUSED_ARGUMENT(s); 85 | UNUSED_ARGUMENT(l); 86 | 87 | log_error("aefs_write", "NOT IMPLEMENTED YET!\n"); 88 | 89 | return -1; 90 | } 91 | 92 | static int aefs_lookup(vnode_t *dir, char const *name, vnode_t *res) 93 | { 94 | uint32_t i, j; 95 | aefs_block_t *block; 96 | aefs_direntry_t *entry; 97 | aefs_inode_t *inode = (aefs_inode_t *) dir->v_data; 98 | 99 | if (!AEFS_INODE_IS_DIR(inode)) { 100 | log_error("aefs_lookup", 101 | "dir is not a directory, looking for name %s\n", 102 | name); 103 | return -1; 104 | } 105 | 106 | uint32_t num_blocks = div_ceil(AEFS_INODE_SIZE(inode), AEFS_BLOCK_SIZE); 107 | 108 | aefs_inode_t *current = inode; 109 | for (i = 0; i < num_blocks; ++i) { 110 | if (i % AEFS_INODE_NUM_BLOCKS == 0 && i != 0) { 111 | current = get_inode(current->inode_tail); 112 | } 113 | block = get_block(current->blocks[i % AEFS_INODE_NUM_BLOCKS]); 114 | entry = (aefs_direntry_t *) block; 115 | 116 | for (j = 0; j < AEFS_DIRENTRIES_PER_BLOCK; ++j, ++entry) { 117 | if (strncmp(entry->name, name, AEFS_FILENAME_MAX_LEN) == 0) { 118 | res->v_op = &vnodeops; 119 | res->v_data = (uint32_t) get_inode(entry->inode_id); 120 | return 0; 121 | } 122 | } 123 | } 124 | 125 | return -1; 126 | } 127 | 128 | static int aefs_open(vnode_t *node) 129 | { 130 | UNUSED_ARGUMENT(node); 131 | /* NOOP for AEFS */ 132 | return 0; 133 | } 134 | 135 | 136 | static int map_aefs_to_virtual_memory(uint32_t paddr, uint32_t size) 137 | { 138 | uint32_t fs_vaddr, mapped_mem_size; 139 | fs_vaddr = pdt_kernel_find_next_vaddr(size); 140 | if (fs_vaddr == 0) { 141 | log_error("map_aefs_to_virtual_memory", 142 | "Could not find virtual memory in kernel for AEFS." 143 | "fs_paddr: %X, fs_size: %u\n", 144 | paddr, size); 145 | return 0; 146 | } 147 | 148 | mapped_mem_size = pdt_map_kernel_memory(paddr, fs_vaddr, size, 149 | PAGING_PL0, PAGING_READ_ONLY); 150 | if (mapped_mem_size < size) { 151 | log_error("map_aefs_to_virtual_memory", 152 | "Could not map kernel memory for AEFS." 153 | "fs_paddr: %X, fs_vaddr: %X, fs_size: %u, mapped_size: %u\n", 154 | paddr, fs_vaddr, size, mapped_mem_size); 155 | return 0; 156 | } 157 | 158 | log_info("map_aefs_to_virtual_memory", 159 | "fs info: fs_vaddr: %X, fs_paddr: %X, fs_size: %u\n", 160 | fs_vaddr, fs_paddr, size); 161 | 162 | return fs_vaddr; 163 | } 164 | 165 | uint32_t aefs_init(uint32_t paddr, uint32_t size, vfs_t *vfs) 166 | { 167 | uint32_t fs_vaddr; 168 | fs_paddr = paddr; 169 | fs_size = size; 170 | 171 | fs_vaddr = map_aefs_to_virtual_memory(paddr, size); 172 | 173 | if (fs_vaddr == 0) { 174 | log_error("aefs_init", 175 | "Could not map AEFS to virtual memory\n"); 176 | return 1; 177 | } 178 | 179 | fs_start = (aefs_block_t *) fs_vaddr; 180 | sb = (aefs_superblock_t *) fs_vaddr; 181 | 182 | if (sb->magic_number != AEFS_MAGIC_NUMBER) { 183 | log_error("aefs_init", 184 | "Magic number of superblock is wrong: %X\n", 185 | sb->magic_number); 186 | return 1; 187 | } 188 | 189 | root_inode = get_inode(ROOT_INODE_ID); 190 | 191 | vnodeops.vn_open = &aefs_open; 192 | vnodeops.vn_lookup = &aefs_lookup; 193 | vnodeops.vn_read = &aefs_read; 194 | vnodeops.vn_getattr = &aefs_getattr; 195 | vnodeops.vn_write = &aefs_write; 196 | 197 | vfsops.vfs_root = &aefs_root; 198 | 199 | vfs->vfs_next = NULL; 200 | vfs->vfs_op = &vfsops; 201 | 202 | return 0; 203 | } 204 | -------------------------------------------------------------------------------- /src/kernel/kmain.c: -------------------------------------------------------------------------------- 1 | #include "stdio.h" 2 | #include "fb.h" 3 | #include "gdt.h" 4 | #include "pic.h" 5 | #include "idt.h" 6 | #include "interrupt.h" 7 | #include "constants.h" 8 | #include "common.h" 9 | #include "pit.h" 10 | #include "multiboot.h" 11 | #include "paging.h" 12 | #include "kernel.h" 13 | #include "serial.h" 14 | #include "log.h" 15 | #include "vfs.h" 16 | #include "aefs.h" 17 | #include "process.h" 18 | #include "page_frame_allocator.h" 19 | #include "tss.h" 20 | #include "stddef.h" 21 | #include "keyboard.h" 22 | #include "scheduler.h" 23 | #include "process.h" 24 | #include "kmalloc.h" 25 | #include "vfs.h" 26 | #include "devfs.h" 27 | 28 | #define KINIT_ERROR_LOAD_FS 1 29 | #define KINIT_ERROR_INIT_FS 2 30 | #define KINIT_ERROR_INIT_PAGING 3 31 | #define KINIT_ERROR_INIT_PFA 4 32 | #define KINIT_ERROR_MALLOC_ROOT_VFS 5 33 | #define KINIT_ERROR_INIT_VFS 6 34 | #define KINIT_ERROR_INIT_SCHEDULER 7 35 | 36 | /* Gets the physical address of the filesystem, which is the address of the 37 | * only GRUB module loaded 38 | * 39 | * @param mbinfo A pointer to the multiboot info struct 40 | * @param size OUT The size of the fs in bytes will be written to his ptr 41 | * 42 | * @return The physical address of the filesystem 43 | */ 44 | static uint32_t get_fs_paddr(const multiboot_info_t *mbinfo, uint32_t *size) 45 | { 46 | *size = 0; 47 | if (mbinfo->flags & 0x00000008) { 48 | if (mbinfo->mods_count != 1) { 49 | return 0; 50 | } 51 | 52 | multiboot_module_t *module = (multiboot_module_t *) mbinfo->mods_addr; 53 | *size = module->mod_end - module->mod_start; 54 | return module->mod_start; 55 | } 56 | 57 | return 0; 58 | } 59 | 60 | static uint32_t add_device(char const *name, int (*get_vnode)(vnode_t *out)) 61 | { 62 | vnode_t *device = kmalloc(sizeof(vnode_t)); 63 | if (device == NULL) { 64 | log_error("populate_devfs", 65 | "Could not allocate vnode_t struct for device %s\n", name); 66 | return 1; 67 | } 68 | 69 | if (get_vnode(device)) { 70 | log_error("populate_devfs", 71 | "Device %s could not generate vnode\n", name); 72 | return 1; 73 | } 74 | 75 | devfs_add_device(name, device); 76 | 77 | return 0; 78 | } 79 | 80 | static uint32_t populate_devfs() 81 | { 82 | vfs_t *devfs = kmalloc(sizeof(vfs_t)); 83 | if (devfs == NULL) { 84 | log_error("populate_devfs", 85 | "Could not allocate vfs_t struct for devfs\n"); 86 | return 1; 87 | } 88 | 89 | devfs_init(devfs); 90 | 91 | add_device("console", fb_get_vnode); 92 | add_device("keyboard", kbd_get_vnode); 93 | 94 | vfs_mount("/dev/", devfs); 95 | 96 | return 0; 97 | } 98 | 99 | static uint32_t kinit(kernel_meminfo_t *mem, 100 | const multiboot_info_t *mbinfo, 101 | uint32_t kernel_pdt_vaddr, 102 | uint32_t kernel_pt_vaddr) 103 | { 104 | uint32_t fs_paddr, fs_size; 105 | uint32_t res; 106 | uint32_t tss_vaddr; 107 | disable_interrupts(); 108 | 109 | fb_init(); 110 | 111 | fs_paddr = get_fs_paddr(mbinfo, &fs_size); 112 | if (fs_paddr == 0 && fs_size == 0) { 113 | return KINIT_ERROR_LOAD_FS; 114 | } 115 | 116 | tss_vaddr = tss_init(); 117 | gdt_init(tss_vaddr); 118 | idt_init(); 119 | pic_init(); 120 | 121 | kbd_init(); 122 | serial_init(COM1); 123 | 124 | pit_init(); 125 | 126 | res = paging_init(kernel_pdt_vaddr, kernel_pt_vaddr); 127 | if (res != 0) { 128 | return KINIT_ERROR_INIT_PAGING; 129 | } 130 | 131 | res = pfa_init(mbinfo, mem, fs_paddr, fs_size); 132 | if (res != 0) { 133 | return KINIT_ERROR_INIT_PFA; 134 | } 135 | 136 | vfs_t *aefs_vfs = kmalloc(sizeof(vfs_t)); 137 | if (aefs_vfs == NULL) { 138 | return KINIT_ERROR_MALLOC_ROOT_VFS; 139 | } 140 | 141 | res = aefs_init(fs_paddr, fs_size, aefs_vfs); 142 | if (res != 0) { 143 | return KINIT_ERROR_INIT_FS; 144 | } 145 | 146 | res = vfs_mount("/", aefs_vfs); 147 | if (res != 0) { 148 | return KINIT_ERROR_INIT_VFS; 149 | } 150 | 151 | populate_devfs(); 152 | 153 | res = scheduler_init(); 154 | if (res != 0) { 155 | return KINIT_ERROR_INIT_SCHEDULER; 156 | } 157 | 158 | enable_interrupts(); 159 | return 0; 160 | } 161 | 162 | static multiboot_info_t *remap_multiboot_info(uint32_t mbaddr) 163 | { 164 | multiboot_info_t *mbinfo = (multiboot_info_t *) PHYSICAL_TO_VIRTUAL(mbaddr); 165 | 166 | mbinfo->mmap_addr = PHYSICAL_TO_VIRTUAL(mbinfo->mmap_addr); 167 | mbinfo->mods_addr = PHYSICAL_TO_VIRTUAL(mbinfo->mods_addr); 168 | 169 | return mbinfo; 170 | } 171 | 172 | static void start_init() 173 | { 174 | ps_t *init = process_create("/bin/init", scheduler_next_pid()); 175 | if (init == NULL) { 176 | printf("ERROR: Could not create init!\n"); 177 | } else { 178 | scheduler_add_runnable_process(init); 179 | scheduler_schedule(); 180 | } 181 | } 182 | 183 | int kmain(uint32_t mbaddr, uint32_t magic_number, kernel_meminfo_t mem, 184 | uint32_t kernel_pdt_vaddr, uint32_t kernel_pt_vaddr) 185 | { 186 | uint32_t res; 187 | multiboot_info_t *mbinfo = remap_multiboot_info(mbaddr); 188 | 189 | fb_clear(); 190 | 191 | if (magic_number != MULTIBOOT_BOOTLOADER_MAGIC) { 192 | printf("ERROR: magic number is wrong!\n"); 193 | printf("magic_number: %u\n", magic_number); 194 | return 0xDEADDEAD; 195 | } 196 | 197 | res = kinit(&mem, mbinfo, kernel_pdt_vaddr, kernel_pt_vaddr); 198 | if (res != 0) { 199 | switch (res) { 200 | case KINIT_ERROR_LOAD_FS: 201 | printf("ERROR: Could not load filesystem!\n"); 202 | break; 203 | case KINIT_ERROR_INIT_FS: 204 | printf("ERROR: Could not initialize filesystem!\n"); 205 | break; 206 | case KINIT_ERROR_INIT_PAGING: 207 | printf("ERROR: Could not initialize paging!\n"); 208 | break; 209 | case KINIT_ERROR_INIT_PFA: 210 | printf("ERROR: Could not initialize page frame allocator!\n"); 211 | break; 212 | case KINIT_ERROR_MALLOC_ROOT_VFS: 213 | printf("ERROR: Could not allocate VFS node for root FS!\n"); 214 | break; 215 | case KINIT_ERROR_INIT_VFS: 216 | printf("ERROR: Could not initialize virtual filesystem!\n"); 217 | break; 218 | case KINIT_ERROR_INIT_SCHEDULER: 219 | printf("ERROR: Could not initialize scheduler!\n"); 220 | break; 221 | default: 222 | printf("ERROR: Unknown error\n"); 223 | break; 224 | } 225 | 226 | return 0xDEADDEAD; 227 | } 228 | 229 | log_info("kmain", "kernel initialized successfully!\n"); 230 | 231 | start_init(); 232 | 233 | return 0xDEADBEEF; 234 | } 235 | -------------------------------------------------------------------------------- /src/kernel/multiboot.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 1999,2003,2007,2008,2009 Free Software Foundation, Inc. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY 16 | * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 17 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 18 | * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | */ 20 | 21 | #ifndef MULTIBOOT_HEADER 22 | #define MULTIBOOT_HEADER 1 23 | 24 | /* How many bytes from the start of the file we search for the header. */ 25 | #define MULTIBOOT_SEARCH 8192 26 | 27 | /* The magic field should contain this. */ 28 | #define MULTIBOOT_HEADER_MAGIC 0x1BADB002 29 | 30 | /* This should be in %eax. */ 31 | #define MULTIBOOT_BOOTLOADER_MAGIC 0x2BADB002 32 | 33 | /* The bits in the required part of flags field we don't support. */ 34 | #define MULTIBOOT_UNSUPPORTED 0x0000fffc 35 | 36 | /* Alignment of multiboot modules. */ 37 | #define MULTIBOOT_MOD_ALIGN 0x00001000 38 | 39 | /* Alignment of the multiboot info structure. */ 40 | #define MULTIBOOT_INFO_ALIGN 0x00000004 41 | 42 | /* Flags set in the 'flags' member of the multiboot header. */ 43 | 44 | /* Align all boot modules on i386 page (4KB) boundaries. */ 45 | #define MULTIBOOT_PAGE_ALIGN 0x00000001 46 | 47 | /* Must pass memory information to OS. */ 48 | #define MULTIBOOT_MEMORY_INFO 0x00000002 49 | 50 | /* Must pass video information to OS. */ 51 | #define MULTIBOOT_VIDEO_MODE 0x00000004 52 | 53 | /* This flag indicates the use of the address fields in the header. */ 54 | #define MULTIBOOT_AOUT_KLUDGE 0x00010000 55 | 56 | /* Flags to be set in the 'flags' member of the multiboot info structure. */ 57 | 58 | /* is there basic lower/upper memory information? */ 59 | #define MULTIBOOT_INFO_MEMORY 0x00000001 60 | /* is there a boot device set? */ 61 | #define MULTIBOOT_INFO_BOOTDEV 0x00000002 62 | /* is the command-line defined? */ 63 | #define MULTIBOOT_INFO_CMDLINE 0x00000004 64 | /* are there modules to do something with? */ 65 | #define MULTIBOOT_INFO_MODS 0x00000008 66 | 67 | /* These next two are mutually exclusive */ 68 | 69 | /* is there a symbol table loaded? */ 70 | #define MULTIBOOT_INFO_AOUT_SYMS 0x00000010 71 | /* is there an ELF section header table? */ 72 | #define MULTIBOOT_INFO_ELF_SHDR 0X00000020 73 | 74 | /* is there a full memory map? */ 75 | #define MULTIBOOT_INFO_MEM_MAP 0x00000040 76 | 77 | /* Is there drive info? */ 78 | #define MULTIBOOT_INFO_DRIVE_INFO 0x00000080 79 | 80 | /* Is there a config table? */ 81 | #define MULTIBOOT_INFO_CONFIG_TABLE 0x00000100 82 | 83 | /* Is there a boot loader name? */ 84 | #define MULTIBOOT_INFO_BOOT_LOADER_NAME 0x00000200 85 | 86 | /* Is there a APM table? */ 87 | #define MULTIBOOT_INFO_APM_TABLE 0x00000400 88 | 89 | /* Is there video information? */ 90 | #define MULTIBOOT_INFO_VIDEO_INFO 0x00000800 91 | 92 | #ifndef ASM_FILE 93 | 94 | typedef unsigned short multiboot_uint16_t; 95 | typedef unsigned int multiboot_uint32_t; 96 | typedef unsigned long long multiboot_uint64_t; 97 | 98 | struct multiboot_header 99 | { 100 | /* Must be MULTIBOOT_MAGIC - see above. */ 101 | multiboot_uint32_t magic; 102 | 103 | /* Feature flags. */ 104 | multiboot_uint32_t flags; 105 | 106 | /* The above fields plus this one must equal 0 mod 2^32. */ 107 | multiboot_uint32_t checksum; 108 | 109 | /* These are only valid if MULTIBOOT_AOUT_KLUDGE is set. */ 110 | multiboot_uint32_t header_addr; 111 | multiboot_uint32_t load_addr; 112 | multiboot_uint32_t load_end_addr; 113 | multiboot_uint32_t bss_end_addr; 114 | multiboot_uint32_t entry_addr; 115 | 116 | /* These are only valid if MULTIBOOT_VIDEO_MODE is set. */ 117 | multiboot_uint32_t mode_type; 118 | multiboot_uint32_t width; 119 | multiboot_uint32_t height; 120 | multiboot_uint32_t depth; 121 | }; 122 | 123 | /* The symbol table for a.out. */ 124 | struct multiboot_aout_symbol_table 125 | { 126 | multiboot_uint32_t tabsize; 127 | multiboot_uint32_t strsize; 128 | multiboot_uint32_t addr; 129 | multiboot_uint32_t reserved; 130 | }; 131 | typedef struct multiboot_aout_symbol_table multiboot_aout_symbol_table_t; 132 | 133 | /* The section header table for ELF. */ 134 | struct multiboot_elf_section_header_table 135 | { 136 | multiboot_uint32_t num; 137 | multiboot_uint32_t size; 138 | multiboot_uint32_t addr; 139 | multiboot_uint32_t shndx; 140 | }; 141 | typedef struct multiboot_elf_section_header_table multiboot_elf_section_header_table_t; 142 | 143 | struct multiboot_info 144 | { 145 | /* Multiboot info version number */ 146 | multiboot_uint32_t flags; 147 | 148 | /* Available memory from BIOS */ 149 | multiboot_uint32_t mem_lower; 150 | multiboot_uint32_t mem_upper; 151 | 152 | /* "root" partition */ 153 | multiboot_uint32_t boot_device; 154 | 155 | /* Kernel command line */ 156 | multiboot_uint32_t cmdline; 157 | 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 | -------------------------------------------------------------------------------- /src/mkfs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "kernel/aefs.h" 11 | 12 | #define SLASH_LEN 1 13 | #define READ_BUFFER_LEN 4096 14 | 15 | static void die(char const *msg) 16 | { 17 | perror(msg); 18 | exit(1); 19 | } 20 | 21 | uint32_t div_ceil(uint32_t num, uint32_t den) 22 | { 23 | return (num - 1) / den + 1; 24 | } 25 | 26 | struct block { 27 | char data[AEFS_BLOCK_SIZE]; 28 | } __attribute__((packed)); 29 | typedef struct block block_t; 30 | 31 | static block_t *file; 32 | static uint16_t num_inodes; 33 | static uint16_t next_inode_id = 1; 34 | static uint16_t next_block_id = 0; 35 | static aefs_inode_t *start_inode; 36 | static block_t *start_block; 37 | 38 | static void write_superblock(uint16_t num_inodes, uint16_t start_block_offset); 39 | static uint16_t visit_dir(char *path, int is_root); 40 | static uint16_t visit_file(char *path); 41 | 42 | void fill_inode_blocks(aefs_inode_t *inode, uint16_t blocks_required, 43 | uint16_t start_block_id) 44 | { 45 | uint32_t i; 46 | aefs_inode_t *list = inode; 47 | for (i = 0; i < blocks_required; ++i) { 48 | if (i % AEFS_INODE_NUM_BLOCKS == 0 && i != 0) { 49 | list->inode_tail = next_inode_id; 50 | list = start_inode + next_inode_id; 51 | memset(list, 0, sizeof(aefs_inode_t)); 52 | next_inode_id++; 53 | } 54 | list->blocks[i % AEFS_INODE_NUM_BLOCKS] = start_block_id + i; 55 | } 56 | } 57 | 58 | void fill_inode_size(aefs_inode_t *inode, uint32_t size) 59 | { 60 | inode->size_low = size & 0xFFFF; 61 | inode->size_high = (size >> 16) & 0xFF; 62 | } 63 | 64 | int main(int argc, char **argv) 65 | { 66 | if (argc != 4) { 67 | fprintf(stderr, "Bad root dir specified\n"); 68 | fprintf(stderr, "Usage: %s root_dir fs_size (in MB) output_file\n", 69 | argv[0]); 70 | exit(1); 71 | } 72 | 73 | int fd; 74 | if ((fd = open(argv[3], O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) == 75 | -1) { 76 | die("ERROR: Couldn't open output file"); 77 | } 78 | 79 | size_t size = (size_t) (atoi(argv[2]) << 20); 80 | 81 | if (lseek(fd, size - 1, SEEK_SET) == (off_t) -1) { 82 | die("ERROR: Could not seek in file"); 83 | } 84 | 85 | char dummy = '\0'; 86 | if (write(fd, &dummy, 1) == -1) { 87 | die("ERROR: Could not write last byte of file"); 88 | } 89 | 90 | void *mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 91 | if (mem == MAP_FAILED) { 92 | die("ERROR: Couldn't mmap file"); 93 | } 94 | file = (block_t *) mem; 95 | 96 | uint16_t blocks = size / AEFS_BLOCK_SIZE; 97 | 98 | uint16_t inode_blocks = div_ceil(blocks*sizeof(aefs_inode_t), AEFS_BLOCK_SIZE); 99 | num_inodes = blocks - inode_blocks - 1; 100 | uint16_t start_block_offset = 1 + inode_blocks; 101 | start_block = file + start_block_offset; 102 | start_inode = ((aefs_inode_t *)(file + 1)); 103 | 104 | write_superblock(num_inodes, start_block_offset); 105 | visit_dir(argv[1], 1); 106 | 107 | if (msync(mem, size, MS_SYNC)) { 108 | die("ERROR: Could not msync"); 109 | } 110 | if (munmap(mem, size)) { 111 | die("ERROR: Couldn't munmap file"); 112 | } 113 | close(fd); 114 | 115 | return 0; 116 | } 117 | 118 | static void write_superblock(uint16_t num_inodes, uint16_t start_block_offset) 119 | { 120 | aefs_superblock_t *sb = (aefs_superblock_t*) file; 121 | memset(sb, 0, AEFS_BLOCK_SIZE); 122 | sb->num_inodes = num_inodes; 123 | sb->start_block = start_block_offset; 124 | sb->magic_number = AEFS_MAGIC_NUMBER; 125 | } 126 | 127 | static uint16_t visit_dir(char *path, int is_root) 128 | { 129 | DIR *dir; 130 | uint32_t i; 131 | struct dirent *ent; 132 | uint32_t num_files = 0; 133 | char *child_path; 134 | uint16_t child_inode_id; 135 | struct stat st; 136 | uint16_t dir_inode_id; 137 | aefs_inode_t *dir_inode; 138 | 139 | child_path = 140 | malloc(sizeof(char)*(strlen(path) + AEFS_FILENAME_MAX_LEN + SLASH_LEN)); 141 | if (child_path == NULL) { 142 | die("ERROR: out of memory!"); 143 | } 144 | 145 | dir = opendir(path); 146 | if (dir == NULL) { 147 | die("ERROR: couldn't open directory"); 148 | } 149 | 150 | while ((ent = readdir(dir)) != NULL) { 151 | if (strcmp(ent->d_name, ".") == 0 || 152 | strcmp(ent->d_name, "..") == 0) { 153 | continue; 154 | } 155 | num_files++; 156 | } 157 | 158 | if (is_root) { 159 | //num_files++; /* to make room for /dev */ 160 | } 161 | 162 | dir_inode_id = next_inode_id; 163 | dir_inode = start_inode + dir_inode_id; 164 | next_inode_id++; 165 | if (dir_inode_id == num_inodes) { 166 | die("ERROR: Too many inodes required"); 167 | } 168 | 169 | uint32_t blocks_required = div_ceil(num_files*sizeof(aefs_direntry_t), 170 | AEFS_BLOCK_SIZE); 171 | uint16_t dir_start_block_id = next_block_id; 172 | next_block_id += blocks_required; 173 | block_t *dir_start_block = start_block + dir_start_block_id; 174 | 175 | memset(dir_start_block, 0, AEFS_BLOCK_SIZE * blocks_required); 176 | aefs_direntry_t *entries = (aefs_direntry_t *) dir_start_block; 177 | 178 | rewinddir(dir); 179 | 180 | i = 0; 181 | while ((ent = readdir(dir)) != NULL) { 182 | if (strcmp(ent->d_name, ".") == 0 || 183 | strcmp(ent->d_name, "..") == 0) { 184 | continue; 185 | } 186 | if (sprintf(child_path, "%s/%s", path, ent->d_name) == -1) { 187 | die("ERROR: Couldn't sprintf"); 188 | } 189 | if (stat(child_path, &st)) { 190 | die("ERROR: Couldn't stat file"); 191 | } 192 | if (S_ISREG(st.st_mode)) { 193 | child_inode_id = visit_file(child_path); 194 | } else if (S_ISDIR(st.st_mode)) { 195 | child_inode_id = visit_dir(child_path, 0); 196 | } else { 197 | fprintf(stderr, "ERROR: Unsupported file type: %s", child_path); 198 | exit(1); 199 | } 200 | entries[i].inode_id = child_inode_id; 201 | strncpy(entries[i].name, ent->d_name, AEFS_FILENAME_MAX_LEN); 202 | ++i; 203 | } 204 | 205 | dir_inode->type = AEFS_FILETYPE_DIR; 206 | uint32_t dir_size = sizeof(aefs_direntry_t) * num_files; 207 | fill_inode_size(dir_inode, dir_size); 208 | 209 | fill_inode_blocks(dir_inode, blocks_required, dir_start_block_id); 210 | 211 | free(child_path); 212 | if (closedir(dir)) { 213 | die("ERROR: Couldn't close dir"); 214 | } 215 | 216 | return dir_inode_id; 217 | } 218 | 219 | uint16_t visit_file(char *path) 220 | { 221 | FILE *file; 222 | struct stat st; 223 | size_t bytes_read; 224 | 225 | if (stat(path, &st)) { 226 | die("ERROR: Couldn't stat file"); 227 | } 228 | 229 | uint16_t file_inode_id = next_inode_id; 230 | next_inode_id++; 231 | aefs_inode_t *file_inode = start_inode + file_inode_id; 232 | uint16_t blocks_required = div_ceil(st.st_size, AEFS_BLOCK_SIZE); 233 | uint16_t file_start_block_id = next_block_id; 234 | block_t *file_start_block = start_block + file_start_block_id; 235 | next_block_id += blocks_required; 236 | 237 | file_inode->type = AEFS_FILETYPE_REG; 238 | fill_inode_size(file_inode, st.st_size); 239 | 240 | if ((file = fopen(path, "r")) == NULL) { 241 | die("ERROR: Couldn't open file for read"); 242 | } 243 | 244 | while ((bytes_read = 245 | fread(file_start_block++, sizeof(char), AEFS_BLOCK_SIZE, file))) { 246 | } 247 | 248 | fill_inode_blocks(file_inode, blocks_required, file_start_block_id); 249 | 250 | fclose(file); 251 | return file_inode_id; 252 | } 253 | -------------------------------------------------------------------------------- /src/kernel/syscall.c: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "interrupt.h" 3 | #include "log.h" 4 | #include "stddef.h" 5 | #include "vfs.h" 6 | #include "scheduler.h" 7 | #include "kmalloc.h" 8 | #include "process.h" 9 | 10 | #define NUM_SYSCALLS 8 11 | #define NEXT_STACK_ITEM(stack) ((uint32_t *) (stack) + 1) 12 | #define PEEK_STACK(stack, type) (*((type *) (stack))) 13 | 14 | typedef int (*syscall_handler_t)(uint32_t syscall, void *stack); 15 | 16 | static int sys_read(uint32_t syscall, void *stack) 17 | { 18 | UNUSED_ARGUMENT(syscall); 19 | 20 | uint32_t fd = PEEK_STACK(stack, uint32_t); 21 | stack = NEXT_STACK_ITEM(stack); 22 | 23 | char *buf = PEEK_STACK(stack, char *); 24 | stack = NEXT_STACK_ITEM(stack); 25 | 26 | size_t count = PEEK_STACK(stack, size_t); 27 | 28 | ps_t *ps = scheduler_get_current_process(); 29 | 30 | if (fd >= PROCESS_MAX_NUM_FD) { 31 | log_info("sys_read", "pid %u tried to open bad fd %u\n", 32 | ps->id, fd); 33 | return -1; 34 | } 35 | 36 | vnode_t *vnode = ps->file_descriptors[fd].vnode; 37 | if (vnode == NULL) { 38 | log_info("sys_read", "Couldn't find vnode for fd %u, pid %u\n", 39 | fd, ps->id); 40 | return -1; 41 | } 42 | 43 | return vnode->v_op->vn_read(vnode, buf, count); 44 | } 45 | 46 | static int sys_write(uint32_t syscall, void *stack) 47 | { 48 | UNUSED_ARGUMENT(syscall); 49 | 50 | uint32_t fd = PEEK_STACK(stack, uint32_t); 51 | stack = NEXT_STACK_ITEM(stack); 52 | 53 | char const *str = PEEK_STACK(stack, char const *); 54 | stack = NEXT_STACK_ITEM(stack); 55 | 56 | size_t len = PEEK_STACK(stack, size_t); 57 | 58 | ps_t *ps = scheduler_get_current_process(); 59 | 60 | vnode_t *vn = ps->file_descriptors[fd].vnode; 61 | if (vn == NULL) { 62 | log_error("sys_write", 63 | "trying to write to empty fd. fd: %u, pid: %u\n", 64 | fd, ps->id); 65 | return -1; 66 | } 67 | 68 | return vfs_write(vn, str, len); 69 | } 70 | 71 | static int get_next_fd(fd_t *fds, uint32_t num_fds) 72 | { 73 | uint32_t i; 74 | 75 | for (i = 0; i < num_fds; ++i) { 76 | if (fds[i].vnode == NULL) { 77 | return i; 78 | } 79 | } 80 | 81 | return -1; 82 | } 83 | 84 | static int sys_open(uint32_t syscall, void *stack) 85 | { 86 | UNUSED_ARGUMENT(syscall); 87 | 88 | char const *path = PEEK_STACK(stack, char const *); 89 | stack = NEXT_STACK_ITEM(stack); 90 | 91 | /* flags and mode isn't used at the moment */ 92 | 93 | ps_t *ps = scheduler_get_current_process(); 94 | 95 | vnode_t *vnode = kmalloc(sizeof(vnode_t)); 96 | if(vfs_lookup(path, vnode)) { 97 | log_info("sys_open", 98 | "process %u tried to open non existing file %s.\n", 99 | ps->id, path); 100 | return -1; 101 | } 102 | 103 | int fd = get_next_fd(ps->file_descriptors, PROCESS_MAX_NUM_FD); 104 | if (fd == -1) { 105 | log_info("sys_open", 106 | "File descriptor table for ps %u is full.\n", 107 | ps->id); 108 | kfree(vnode); 109 | return -1; 110 | } 111 | 112 | if (vnode->v_op->vn_open(vnode)) { 113 | log_error("sys_open", 114 | "Can't open the vnode for path %s for ps %u\n", 115 | path, ps->id); 116 | kfree(vnode); 117 | return -1; 118 | } 119 | 120 | ps->file_descriptors[fd].vnode = vnode; 121 | 122 | return fd; 123 | } 124 | 125 | 126 | static void continue_execve(uint32_t data) 127 | { 128 | ps_t *new = (ps_t *) data; 129 | ps_t *old = scheduler_get_current_process(); 130 | 131 | scheduler_replace_process(old, new); 132 | 133 | scheduler_schedule(); 134 | /* will never reach this code */ 135 | } 136 | 137 | static int sys_execve(uint32_t syscall, void *stack) 138 | { 139 | UNUSED_ARGUMENT(syscall); 140 | 141 | char const *path; 142 | ps_t *current, *new; 143 | 144 | path = PEEK_STACK(stack, char const *); 145 | current = scheduler_get_current_process(); 146 | new = process_create_replacement(current, path); 147 | if (new == NULL) { 148 | log_error("sys_execve", "Could not create replacement process. " 149 | "current: %u, path: %s\n", current->id, path); 150 | return -1; 151 | } 152 | 153 | switch_to_kernel_stack(&continue_execve, (uint32_t) new); 154 | 155 | /* will never reach this code */ 156 | 157 | return 0; 158 | } 159 | 160 | static int sys_fork(uint32_t syscall, void *stack) 161 | { 162 | UNUSED_ARGUMENT(syscall); 163 | UNUSED_ARGUMENT(stack); 164 | 165 | ps_t *parent, *new; 166 | 167 | uint32_t new_pid = scheduler_next_pid(); 168 | parent = scheduler_get_current_process(); 169 | new = process_clone(parent, new_pid); 170 | if (new == NULL) { 171 | log_error("sys_fork", "can't create fork of process %u\n", 172 | parent->id); 173 | return -1; 174 | } 175 | 176 | new->user_mode.eax = 0; 177 | new->current = new->user_mode; 178 | 179 | scheduler_add_runnable_process(new); 180 | 181 | return new_pid; 182 | } 183 | 184 | static int sys_yield(uint32_t syscall, void *stack) 185 | { 186 | UNUSED_ARGUMENT(syscall); 187 | UNUSED_ARGUMENT(stack); 188 | 189 | ps_t *ps = scheduler_get_current_process(); 190 | ps->user_mode.eax = 0; 191 | 192 | disable_interrupts(); 193 | ps->current = ps->user_mode; 194 | 195 | scheduler_schedule(); 196 | 197 | /* we should not get here */ 198 | 199 | return -1; 200 | } 201 | 202 | static void continue_exit(uint32_t data) 203 | { 204 | UNUSED_ARGUMENT(data); 205 | ps_t *ps = scheduler_get_current_process(); 206 | 207 | scheduler_terminate_process(ps); 208 | 209 | scheduler_schedule(); 210 | /* we should never get here */ 211 | } 212 | 213 | static int sys_exit(uint32_t syscall, void *stack) 214 | { 215 | UNUSED_ARGUMENT(syscall); 216 | UNUSED_ARGUMENT(stack); 217 | 218 | /* TODO: use exit status from stack */ 219 | 220 | switch_to_kernel_stack(continue_exit, 0); 221 | 222 | /* we should never get here */ 223 | return -1; 224 | } 225 | 226 | static int sys_wait(uint32_t syscall, void *stack) 227 | { 228 | UNUSED_ARGUMENT(syscall); 229 | UNUSED_ARGUMENT(stack); 230 | 231 | ps_t *ps = scheduler_get_current_process(); 232 | 233 | if (!scheduler_num_children(ps->id)) { 234 | return -1; 235 | } 236 | 237 | while (1) { 238 | if (scheduler_has_any_child_terminated(ps)) { 239 | /* will be turned to user mode process by wrapper */ 240 | return 0; 241 | } else { 242 | /* should continue to be kernel process */ 243 | snapshot_and_schedule(&ps->current); 244 | } 245 | } 246 | 247 | /* can't reach this code */ 248 | 249 | return -1; 250 | } 251 | 252 | static syscall_handler_t handlers[NUM_SYSCALLS] = { 253 | /* 0 */ sys_open, 254 | /* 1 */ sys_read, 255 | /* 2 */ sys_write, 256 | /* 3 */ sys_execve, 257 | /* 4 */ sys_fork, 258 | /* 5 */ sys_yield, 259 | /* 6 */ sys_exit, 260 | /* 7 */ sys_wait, 261 | }; 262 | 263 | static void update_user_mode_registers(ps_t *ps, cpu_state_t cs, 264 | stack_state_t es) 265 | { 266 | ps->user_mode.eax = cs.eax; 267 | ps->user_mode.ebx = cs.ebx; 268 | ps->user_mode.ecx = cs.ecx; 269 | ps->user_mode.edx = cs.edx; 270 | ps->user_mode.ebp = cs.ebp; 271 | ps->user_mode.esi = cs.esi; 272 | ps->user_mode.edi = cs.edi; 273 | ps->user_mode.eflags = es.eflags; 274 | ps->user_mode.eip = es.eip; 275 | ps->user_mode.esp = es.user_esp; 276 | ps->user_mode.ss = es.user_ss; 277 | ps->user_mode.cs = es.cs; 278 | } 279 | 280 | registers_t *syscall_handle_interrupt(cpu_state_t cpu_state, 281 | stack_state_t exec_state) 282 | { 283 | ps_t *ps = scheduler_get_current_process(); 284 | update_user_mode_registers(ps, cpu_state, exec_state); 285 | 286 | uint32_t *user_stack = (uint32_t *) exec_state.user_esp; 287 | uint32_t syscall = *user_stack; 288 | 289 | if (syscall >= NUM_SYSCALLS) { 290 | log_info("syscall_handle_interrupt", 291 | "bad syscall used." "syscall: %u, ps: %u\n", syscall, ps->id); 292 | ps->user_mode.eax = -1; 293 | return &ps->user_mode; 294 | } 295 | 296 | int eax = handlers[syscall](syscall, user_stack + 1); 297 | 298 | disable_interrupts(); 299 | ps->user_mode.eax = eax; 300 | return &ps->user_mode; 301 | } 302 | -------------------------------------------------------------------------------- /src/kernel/scheduler.c: -------------------------------------------------------------------------------- 1 | #include "scheduler.h" 2 | #include "stddef.h" 3 | #include "tss.h" 4 | #include "paging.h" 5 | #include "constants.h" 6 | #include "log.h" 7 | #include "kmalloc.h" 8 | #include "math.h" 9 | #include "pit.h" 10 | #include "interrupt.h" 11 | #include "pic.h" 12 | #include "common.h" 13 | 14 | #define SCHEDULER_PIT_INTERVAL 2 /* in ms */ 15 | #define SCHEDULER_TIME_SLICE (5 * SCHEDULER_PIT_INTERVAL) /* in ms */ 16 | 17 | uint32_t ms = 0; 18 | 19 | struct ps_list_ele { 20 | struct ps_list_ele *next; 21 | ps_t *ps; 22 | }; 23 | typedef struct ps_list_ele ps_list_ele_t; 24 | 25 | struct ps_list { 26 | ps_list_ele_t *start; 27 | ps_list_ele_t *end; 28 | }; 29 | typedef struct ps_list ps_list_t; 30 | 31 | static ps_list_t runnable_pss = { NULL, NULL }; 32 | static ps_list_t zombie_pss = { NULL, NULL }; 33 | 34 | /* defined in scheduler_asm.s */ 35 | void run_process_in_user_mode(registers_t *registers); 36 | void run_process_in_kernel_mode(registers_t *registers); 37 | 38 | static uint32_t max_id_in_ps_list(ps_list_t *pss) 39 | { 40 | uint32_t max = 0; 41 | ps_list_ele_t *p = pss->start; 42 | while (p != NULL) { 43 | if (p->ps != NULL && p->ps->id >= max) { 44 | max = p->ps->id; 45 | } 46 | p = p->next; 47 | } 48 | 49 | return max; 50 | } 51 | 52 | uint32_t scheduler_next_pid(void) 53 | { 54 | uint32_t pid_runnable = max_id_in_ps_list(&runnable_pss); 55 | uint32_t pid_zombie = max_id_in_ps_list(&zombie_pss); 56 | 57 | return maxu(pid_runnable, pid_zombie) + 1; 58 | } 59 | 60 | static void scheduler_copy_common_registers(registers_t *r, 61 | cpu_state_t const *cpu, 62 | stack_state_t const *stack) 63 | { 64 | r->eax = cpu->eax; 65 | r->ebx = cpu->ebx; 66 | r->ecx = cpu->ecx; 67 | r->edx = cpu->edx; 68 | r->ebp = cpu->ebp; 69 | r->esi = cpu->esi; 70 | r->edi = cpu->edi; 71 | r->eip = stack->eip; 72 | r->eflags = stack->eflags; 73 | r->cs = stack->cs; 74 | } 75 | 76 | static void scheduler_update_user_registers(ps_t *ps, cpu_state_t const *cpu, 77 | stack_state_t const *stack) 78 | { 79 | scheduler_copy_common_registers(&ps->user_mode, cpu, stack); 80 | ps->user_mode.esp = stack->user_esp; 81 | ps->user_mode.ss = stack->user_ss; 82 | ps->current = ps->user_mode; 83 | } 84 | 85 | static void scheduler_update_kernel_registers(ps_t *ps, cpu_state_t const *cpu, 86 | stack_state_t const *stack) 87 | { 88 | scheduler_copy_common_registers(&ps->current, cpu, stack); 89 | ps->current.esp = cpu->esp + 12; /* +12 to skip EIP, CS and EFLAGS */ 90 | ps->current.ss = SEGSEL_KERNEL_DS; 91 | } 92 | 93 | 94 | static void scheduler_schedule_on_intterupt(cpu_state_t const *cpu, 95 | stack_state_t const *stack) 96 | { 97 | disable_interrupts(); 98 | ps_t *ps = scheduler_get_current_process(); 99 | if (stack->cs == (SEGSEL_USER_SPACE_CS | 0x03)) { 100 | scheduler_update_user_registers(ps, cpu, stack); 101 | } else { 102 | scheduler_update_kernel_registers(ps, cpu, stack); 103 | } 104 | 105 | pic_acknowledge(); 106 | 107 | scheduler_schedule(); 108 | } 109 | 110 | static void scheduler_handle_pit_interrupt(cpu_state_t cpu, idt_info_t info, 111 | stack_state_t stack) 112 | { 113 | UNUSED_ARGUMENT(info); 114 | ms += SCHEDULER_PIT_INTERVAL; 115 | 116 | if (ms >= SCHEDULER_TIME_SLICE) { 117 | ms = 0; 118 | scheduler_schedule_on_intterupt(&cpu, &stack); 119 | } else { 120 | pic_acknowledge(); 121 | } 122 | } 123 | 124 | int scheduler_init(void) 125 | { 126 | pit_set_interval(SCHEDULER_PIT_INTERVAL); 127 | return register_interrupt_handler(PIT_INT_IDX, 128 | &scheduler_handle_pit_interrupt); 129 | } 130 | 131 | ps_t *scheduler_get_current_process() 132 | { 133 | return runnable_pss.start == NULL ? NULL : runnable_pss.start->ps; 134 | } 135 | 136 | static int scheduler_add_process(ps_list_t *pss, ps_t *ps) 137 | { 138 | ps_list_ele_t *ele = kmalloc(sizeof(ps_list_ele_t)); 139 | if (ele == NULL) { 140 | log_error("scheduler_add_process", 141 | "Couldn't allocate memory for ps_list_t struct\n"); 142 | return -1; 143 | } 144 | 145 | ele->ps = ps; 146 | ele->next = NULL; 147 | 148 | if (pss->start == NULL) { 149 | pss->start = ele; 150 | } else { 151 | pss->end->next = ele; 152 | } 153 | 154 | pss->end = ele; 155 | 156 | return 0; 157 | } 158 | 159 | int scheduler_add_runnable_process(ps_t *ps) 160 | { 161 | return scheduler_add_process(&runnable_pss, ps); 162 | } 163 | 164 | static int scheduler_remove_process(ps_list_t *pss, uint32_t pid, 165 | uint32_t should_delete) 166 | { 167 | ps_list_ele_t *current = pss->start; 168 | ps_list_ele_t *prev = NULL; 169 | while (current != NULL) { 170 | if (current->ps != NULL && current->ps->id == pid) { 171 | if (prev == NULL) { 172 | pss->start= current->next; 173 | } else { 174 | prev->next = current->next; 175 | } 176 | 177 | if (pss->end == current) { 178 | pss->end = prev; 179 | prev->next = NULL; 180 | } 181 | 182 | if (should_delete) { 183 | process_delete_resources(current->ps); 184 | kfree(current->ps); 185 | } 186 | 187 | kfree(current); 188 | 189 | return 0; 190 | } 191 | prev = current; 192 | current = current->next; 193 | } 194 | 195 | return -1; 196 | } 197 | 198 | void scheduler_terminate_process(ps_t *ps) 199 | { 200 | scheduler_remove_process(&runnable_pss, ps->id, 0); 201 | process_delete_resources(ps); 202 | scheduler_add_process(&zombie_pss, ps); 203 | } 204 | 205 | int scheduler_has_any_child_terminated(ps_t *parent) 206 | { 207 | ps_list_ele_t *e = zombie_pss.start; 208 | while (e != NULL) { 209 | if (e->ps->parent_id == parent->id) { 210 | return 1; 211 | } 212 | e = e->next; 213 | } 214 | 215 | return 0; 216 | } 217 | 218 | static int scheduler_count_children_in_list(ps_list_t *pss, uint32_t pid) 219 | { 220 | int num = 0; 221 | ps_list_ele_t *e = pss->start; 222 | while (e != NULL) { 223 | if (e->ps->parent_id == pid) { 224 | ++num; 225 | } 226 | e = e->next; 227 | } 228 | 229 | return num; 230 | } 231 | 232 | int scheduler_num_children(uint32_t pid) 233 | { 234 | int num_zombies = scheduler_count_children_in_list(&zombie_pss, pid); 235 | int num_running = scheduler_count_children_in_list(&runnable_pss, pid); 236 | 237 | return num_zombies + num_running; 238 | } 239 | 240 | int scheduler_replace_process(ps_t *old, ps_t *new) 241 | { 242 | int error; 243 | 244 | error = scheduler_remove_process(&runnable_pss, old->id, 1); 245 | if (error) { 246 | log_error("scheduler_replace_process", 247 | "Couldn't remove old process %u\n", old->id); 248 | return -1; 249 | } 250 | 251 | return scheduler_add_runnable_process(new); 252 | } 253 | 254 | static ps_t *scheduler_get_and_rotate_runnable_process(void) 255 | { 256 | if (runnable_pss.start == NULL || runnable_pss.start->ps == NULL) { 257 | log_error("scheduler_get_and_rotate_runnable_process", 258 | "There are no processes to schedule\n"); 259 | return NULL; 260 | } 261 | 262 | ps_list_ele_t *start = runnable_pss.start; 263 | if (start->next != NULL) { 264 | /* more than one element in the list */ 265 | /* move current process (head of list) to end of list */ 266 | runnable_pss.start = start->next; 267 | start->next = NULL; 268 | runnable_pss.end->next = start; 269 | runnable_pss.end = start; 270 | } 271 | 272 | return runnable_pss.start->ps; 273 | } 274 | 275 | void scheduler_schedule(void) 276 | { 277 | ps_t *ps = scheduler_get_and_rotate_runnable_process(); 278 | if (ps == NULL) { 279 | log_error("scheduler_schedule", "Can't schedule processes\n"); 280 | return; 281 | } 282 | 283 | tss_set_kernel_stack(SEGSEL_KERNEL_DS, ps->kernel_stack_start_vaddr); 284 | pdt_load_process_pdt(ps->pdt, ps->pdt_paddr); 285 | 286 | if (ps->current.cs == SEGSEL_KERNEL_CS) { 287 | run_process_in_kernel_mode(&ps->current); 288 | } else { 289 | run_process_in_user_mode(&ps->current); 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /src/kernel/page_frame_allocator.c: -------------------------------------------------------------------------------- 1 | #include "page_frame_allocator.h" 2 | #include "log.h" 3 | #include "string.h" 4 | #include "common.h" 5 | #include "constants.h" 6 | #include "mem.h" 7 | #include "paging.h" 8 | #include "math.h" 9 | 10 | #define MAX_NUM_MEMORY_MAP 100 11 | 12 | struct memory_map { 13 | uint32_t addr; 14 | uint32_t len; 15 | }; 16 | typedef struct memory_map memory_map_t; 17 | 18 | struct page_frame_bitmap { 19 | uint32_t *start; 20 | uint32_t len; /* in bits */ 21 | }; 22 | typedef struct page_frame_bitmap page_frame_bitmap_t; 23 | 24 | static page_frame_bitmap_t page_frames; 25 | static memory_map_t mmap[MAX_NUM_MEMORY_MAP]; 26 | static uint32_t mmap_len; 27 | 28 | static uint32_t fill_memory_map(multiboot_info_t const *mbinfo, 29 | kernel_meminfo_t const *mem, 30 | uint32_t fs_paddr, uint32_t fs_size) 31 | { 32 | uint32_t addr, len, i = 0; 33 | if ((mbinfo->flags & 0x00000020) == 0) { 34 | log_error("fill_memory_map", "No memory map from GRUB\n"); 35 | return 0; 36 | } 37 | 38 | multiboot_memory_map_t *entry = 39 | (multiboot_memory_map_t *) mbinfo->mmap_addr; 40 | while ((uint32_t) entry < mbinfo->mmap_addr + mbinfo->mmap_length) { 41 | if (entry->type == MULTIBOOT_MEMORY_AVAILABLE) { 42 | addr = (uint32_t) entry->addr; 43 | len = (uint32_t) entry->len; 44 | if (addr <= mem->kernel_physical_start 45 | && (addr + len) > mem->kernel_physical_end) { 46 | 47 | addr = mem->kernel_physical_end; 48 | len = len - mem->kernel_physical_end; 49 | 50 | } 51 | 52 | if (addr > ONE_MB) { 53 | if (addr < fs_paddr && ((addr + len) > (fs_paddr + fs_size))) { 54 | mmap[i].addr = addr; 55 | mmap[i].len = fs_paddr - addr; 56 | ++i; 57 | 58 | addr = fs_paddr + fs_size; 59 | len -= (fs_paddr + fs_size) - addr; 60 | } 61 | 62 | mmap[i].addr = addr; 63 | mmap[i].len = len; 64 | ++i; 65 | } 66 | } 67 | entry = (multiboot_memory_map_t *) 68 | (((uint32_t) entry) + entry->size + sizeof(entry->size)); 69 | } 70 | 71 | return i; 72 | } 73 | 74 | static uint32_t construct_bitmap(memory_map_t *mmap, uint32_t n) 75 | { 76 | uint32_t i, bitmap_pfs, bitmap_size, paddr, vaddr, mapped_mem; 77 | uint32_t total_pfs = 0; 78 | 79 | /* calculate number of available page frames */ 80 | for (i = 0; i < n; ++i) { 81 | total_pfs += mmap[i].len / FOUR_KB; 82 | } 83 | 84 | bitmap_pfs = div_ceil(div_ceil(total_pfs, 8), FOUR_KB); 85 | 86 | for (i = 0; i < n; ++i) { 87 | if (mmap[i].len >= bitmap_pfs * FOUR_KB) { 88 | paddr = mmap[i].addr; 89 | 90 | mmap[i].addr += bitmap_pfs * FOUR_KB; 91 | mmap[i].len -= bitmap_pfs * FOUR_KB; 92 | break; 93 | } 94 | } 95 | 96 | page_frames.len = total_pfs - bitmap_pfs; 97 | bitmap_size = div_ceil(page_frames.len, 8); 98 | 99 | if (i == n) { 100 | log_error("construct_bitmap", 101 | "Couldn't find place for bitmap. bitmap_size: %u\n", 102 | bitmap_size); 103 | return 1; 104 | } 105 | 106 | vaddr = pdt_kernel_find_next_vaddr(bitmap_size); 107 | if (vaddr == 0) { 108 | log_error("construct_bitmap", 109 | "Could not find virtual address for bitmap in kernel. " 110 | "paddr: %X, bitmap_size: %u, bitmap_pfs: %u\n", 111 | paddr, bitmap_size); 112 | return 1; 113 | 114 | } 115 | log_info("construct_bitmap", 116 | "bitmap vaddr: %X, bitmap paddr: %X, page_frames.len: %u, " 117 | "bitmap_size: %u, bitmap_pfs: %u\n", 118 | vaddr, paddr, page_frames.len, bitmap_size, bitmap_pfs); 119 | 120 | mapped_mem = pdt_map_kernel_memory(paddr, vaddr, bitmap_size, 121 | PAGING_PL0, PAGING_READ_WRITE); 122 | if (mapped_mem < bitmap_size) { 123 | log_error("construct_bitmap", 124 | "Could not map kernel memory for bitmap. " 125 | "paddr: %X, vaddr: %X, bitmap_size: %u\n", 126 | paddr, vaddr, bitmap_size); 127 | return 1; 128 | } 129 | 130 | page_frames.start = (uint32_t *) vaddr; 131 | 132 | memset(page_frames.start, 0xFF, bitmap_size); 133 | uint8_t *last = (uint8_t *)((uint32_t)page_frames.start + bitmap_size - 1); 134 | *last = 0; 135 | for (i = 0; i < page_frames.len % 8; ++i) { 136 | *last |= 0x01 << (7 - i); 137 | } 138 | 139 | return 0; 140 | } 141 | 142 | uint32_t pfa_init(multiboot_info_t const *mbinfo, 143 | kernel_meminfo_t const *mem, 144 | uint32_t fs_paddr, uint32_t fs_size) 145 | { 146 | uint32_t i, n, addr, len; 147 | 148 | n = fill_memory_map(mbinfo, mem, fs_paddr, fs_size); 149 | if (n == 0) { 150 | return 1; 151 | } 152 | 153 | log_info("pfa_init", 154 | "\n\tkernel_physical_start: %X\n" 155 | "\tkernel_physical_end: %X\n" 156 | "\tkernel_virtual_start: %X\n" 157 | "\tkernel_virtual_end: %X\n", 158 | mem->kernel_physical_start, mem->kernel_physical_end, 159 | mem->kernel_virtual_start, mem->kernel_virtual_end); 160 | 161 | mmap_len = n; 162 | 163 | for (i = 0; i < n; ++i) { 164 | /* align addresses on 4kB blocks */ 165 | addr = align_up(mmap[i].addr, FOUR_KB); 166 | len = align_down(mmap[i].len - (addr - mmap[i].addr), FOUR_KB); 167 | 168 | mmap[i].addr = addr; 169 | mmap[i].len = len; 170 | 171 | log_debug("pfa_init", "mmap[%u] -> addr: %X, len: %u, pfs: %u\n", i, addr, len, len / FOUR_KB); 172 | } 173 | 174 | return construct_bitmap(mmap, n); 175 | } 176 | 177 | static void toggle_bit(uint32_t bit_idx) 178 | { 179 | uint32_t *bits = page_frames.start; 180 | bits[bit_idx/32] ^= (0x01 << (31 - (bit_idx % 32))); 181 | } 182 | 183 | static void toggle_bits(uint32_t bit_idx, uint32_t num_bits) 184 | { 185 | uint32_t i; 186 | for (i = bit_idx; i < bit_idx + num_bits; ++i) { 187 | toggle_bit(i); 188 | } 189 | } 190 | 191 | static uint32_t paddr_for_idx(uint32_t bit_idx) 192 | { 193 | uint32_t i, current_offset = 0, offset = bit_idx * FOUR_KB; 194 | for (i = 0; i < mmap_len; ++i) { 195 | if (current_offset + mmap[i].len <= offset) { 196 | current_offset += mmap[i].len; 197 | } else { 198 | offset -= current_offset; 199 | return mmap[i].addr + offset; 200 | } 201 | } 202 | 203 | return 0; 204 | } 205 | 206 | static uint32_t idx_for_paddr(uint32_t paddr) 207 | { 208 | uint32_t i, byte_offset = 0; 209 | for (i = 0; i < mmap_len; ++i) { 210 | if (paddr < mmap[i].addr + mmap[i].len) { 211 | byte_offset += paddr - mmap[i].addr; 212 | return byte_offset / FOUR_KB; 213 | } else { 214 | byte_offset += mmap[i].len; 215 | } 216 | } 217 | 218 | return page_frames.len; 219 | } 220 | 221 | static uint32_t fits_in_one_mmap_entry(uint32_t bit_idx, uint32_t pfs) 222 | { 223 | uint32_t i, current_offset = 0, offset = bit_idx * FOUR_KB; 224 | for (i = 0; i < mmap_len; ++i) { 225 | if (current_offset + mmap[i].len <= offset) { 226 | current_offset += mmap[i].len; 227 | } else { 228 | offset -= current_offset; 229 | if (offset + pfs * FOUR_KB <= mmap[i].len) { 230 | return 1; 231 | } else { 232 | return 0; 233 | } 234 | } 235 | } 236 | 237 | return 0; 238 | } 239 | 240 | uint32_t pfa_allocate(uint32_t num_page_frames) 241 | { 242 | uint32_t i, j, cell, bit_idx; 243 | uint32_t n = div_ceil(page_frames.len, 32), frames_found = 0; 244 | 245 | for (i = 0; i < n; ++i) { 246 | cell = page_frames.start[i]; 247 | if (cell != 0) { 248 | for (j = 0; j < 32; ++j) { 249 | if (((cell >> (31 - j)) & 0x1) == 1) { 250 | if (frames_found == 0) { 251 | bit_idx = i * 32 + j; 252 | } 253 | ++frames_found; 254 | if (frames_found == num_page_frames) { 255 | if (fits_in_one_mmap_entry(bit_idx, num_page_frames)) { 256 | toggle_bits(bit_idx, num_page_frames); 257 | return paddr_for_idx(bit_idx); 258 | } else { 259 | frames_found = 0; 260 | } 261 | } 262 | } else { 263 | frames_found = 0; 264 | } 265 | } 266 | } else { 267 | frames_found = 0; 268 | } 269 | } 270 | 271 | return 0; 272 | } 273 | 274 | void pfa_free(uint32_t paddr) 275 | { 276 | uint32_t bit_idx = idx_for_paddr(paddr); 277 | if (bit_idx == page_frames.len) { 278 | log_error("pfa_free", "invalid paddr %X\n", paddr); 279 | } else { 280 | toggle_bit(bit_idx); 281 | } 282 | } 283 | 284 | void pfa_free_cont(uint32_t paddr, uint32_t n) 285 | { 286 | uint32_t i; 287 | for (i = 0; i < n; ++i) { 288 | pfa_free(paddr + i * FOUR_KB); 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /src/kernel/keyboard.c: -------------------------------------------------------------------------------- 1 | #include "keyboard.h" 2 | #include "io.h" 3 | #include "interrupt.h" 4 | #include "common.h" 5 | #include "pic.h" 6 | 7 | #define KBD_DATA_PORT 0x60 8 | #define KBD_BUFFER_SIZE 512 9 | 10 | /* Alphabet */ 11 | #define KBD_SC_A 0x1e 12 | #define KBD_SC_B 0x30 13 | #define KBD_SC_C 0x2e 14 | #define KBD_SC_D 0x20 15 | #define KBD_SC_E 0x12 16 | #define KBD_SC_F 0x21 17 | #define KBD_SC_G 0x22 18 | #define KBD_SC_H 0x23 19 | #define KBD_SC_I 0x17 20 | #define KBD_SC_J 0x24 21 | #define KBD_SC_K 0x25 22 | #define KBD_SC_L 0x26 23 | #define KBD_SC_M 0x32 24 | #define KBD_SC_N 0x31 25 | #define KBD_SC_O 0x18 26 | #define KBD_SC_P 0x19 27 | #define KBD_SC_Q 0x10 28 | #define KBD_SC_R 0x13 29 | #define KBD_SC_S 0x1f 30 | #define KBD_SC_T 0x14 31 | #define KBD_SC_U 0x16 32 | #define KBD_SC_V 0x2f 33 | #define KBD_SC_W 0x11 34 | #define KBD_SC_X 0x2d 35 | #define KBD_SC_Y 0x15 36 | #define KBD_SC_Z 0x2c 37 | 38 | /* Numeric keys */ 39 | #define KBD_SC_1 0x02 40 | #define KBD_SC_2 0x03 41 | #define KBD_SC_3 0x04 42 | #define KBD_SC_4 0x05 43 | #define KBD_SC_5 0x06 44 | #define KBD_SC_6 0x07 45 | #define KBD_SC_7 0x08 46 | #define KBD_SC_8 0x09 47 | #define KBD_SC_9 0x0a 48 | #define KBD_SC_0 0x0b 49 | 50 | /* Special keys */ 51 | #define KBD_SC_ENTER 0x1c 52 | #define KBD_SC_SPACE 0x39 53 | #define KBD_SC_BS 0x0e 54 | #define KBD_SC_LSHIFT 0x2a 55 | #define KBD_SC_RSHIFT 0x36 56 | #define KBD_SC_DASH 0x0c 57 | #define KBD_SC_EQUALS 0x0d 58 | #define KBD_SC_LBRACKET 0x1a 59 | #define KBD_SC_RBRACKET 0x1b 60 | #define KBD_SC_BSLASH 0x2b 61 | #define KBD_SC_SCOLON 0x27 62 | #define KBD_SC_QUOTE 0x28 63 | #define KBD_SC_COMMA 0x33 64 | #define KBD_SC_DOT 0x34 65 | #define KBD_SC_FSLASH 0x35 66 | #define KBD_SC_TILDE 0x29 67 | #define KBD_SC_CAPSLOCK 0x3a 68 | #define KBD_SC_TAB 0x0f 69 | 70 | static uint8_t is_lshift_down = 0; 71 | static uint8_t is_rshift_down = 0; 72 | static uint8_t is_caps_lock_pressed = 0; 73 | 74 | struct kbd_buffer { 75 | uint8_t buffer[KBD_BUFFER_SIZE]; 76 | uint8_t *head; 77 | uint8_t *tail; 78 | uint32_t count; 79 | }; 80 | typedef struct kbd_buffer kbd_buffer_t; 81 | static kbd_buffer_t kbd_buffer; 82 | 83 | static vnode_t kbd_vnode; 84 | static vnodeops_t kbd_vnodeops; 85 | 86 | /* function declarations */ 87 | static char kbd_scan_code_to_ascii(uint8_t sc); 88 | static uint8_t kbd_read_scan_code(void); 89 | 90 | static void keyboard_handle_interrupt(cpu_state_t state, 91 | idt_info_t info, 92 | stack_state_t exec) 93 | { 94 | UNUSED_ARGUMENT(state); 95 | UNUSED_ARGUMENT(info); 96 | UNUSED_ARGUMENT(exec); 97 | 98 | /* DO NOT MODIFY kbd_buffer.head in this function, 99 | * it will introcude race conditions! 100 | */ 101 | 102 | if (kbd_buffer.count < KBD_BUFFER_SIZE) { 103 | *kbd_buffer.tail++ = kbd_read_scan_code(); 104 | ++kbd_buffer.count; 105 | if (kbd_buffer.tail == kbd_buffer.buffer + KBD_BUFFER_SIZE) { 106 | kbd_buffer.tail = kbd_buffer.buffer; 107 | } 108 | } 109 | 110 | pic_acknowledge(); 111 | } 112 | 113 | static int kbd_open(vnode_t *n) 114 | { 115 | UNUSED_ARGUMENT(n); 116 | 117 | return 0; 118 | } 119 | 120 | static int kbd_lookup(vnode_t *d, char const *n, vnode_t *o) 121 | { 122 | UNUSED_ARGUMENT(d); 123 | UNUSED_ARGUMENT(n); 124 | UNUSED_ARGUMENT(o); 125 | 126 | return -1; 127 | } 128 | 129 | static int kbd_read(vnode_t *n, void *buf, size_t count) 130 | { 131 | UNUSED_ARGUMENT(n); 132 | 133 | /* DO NOT MODIFY kbd_buffer.tail or kbd_buffer.count in this function, 134 | * it will introcude race conditions! 135 | */ 136 | 137 | char ch; 138 | int i = 0; 139 | char *b = buf; 140 | 141 | while (count > 0) { 142 | while (kbd_buffer.head != kbd_buffer.tail) { 143 | ch = kbd_scan_code_to_ascii(*kbd_buffer.head++); 144 | if (kbd_buffer.head == kbd_buffer.buffer + KBD_BUFFER_SIZE) { 145 | kbd_buffer.head = kbd_buffer.buffer; 146 | } 147 | if (ch != -1) { 148 | b[i] = ch; 149 | ++i; 150 | --count; 151 | } 152 | } 153 | } 154 | 155 | return i; 156 | } 157 | 158 | static int kbd_write(vnode_t *n, char const *name, size_t count) 159 | { 160 | UNUSED_ARGUMENT(n); 161 | UNUSED_ARGUMENT(name); 162 | UNUSED_ARGUMENT(count); 163 | 164 | return -1; 165 | } 166 | 167 | static int kbd_getattr(vnode_t *n, vattr_t *a) 168 | { 169 | UNUSED_ARGUMENT(n); 170 | UNUSED_ARGUMENT(a); 171 | 172 | return -1; 173 | } 174 | 175 | uint32_t kbd_init(void) 176 | { 177 | register_interrupt_handler(KBD_INT_IDX, keyboard_handle_interrupt); 178 | 179 | kbd_buffer.count = 0; 180 | kbd_buffer.head = kbd_buffer.buffer; 181 | kbd_buffer.tail = kbd_buffer.buffer; 182 | 183 | kbd_vnodeops.vn_open = &kbd_open; 184 | kbd_vnodeops.vn_lookup = &kbd_lookup; 185 | kbd_vnodeops.vn_read = &kbd_read; 186 | kbd_vnodeops.vn_write = &kbd_write; 187 | kbd_vnodeops.vn_getattr = &kbd_getattr; 188 | 189 | kbd_vnode.v_op = &kbd_vnodeops; 190 | kbd_vnode.v_data = 0; 191 | 192 | return 0; 193 | } 194 | 195 | int kbd_get_vnode(vnode_t *out) 196 | { 197 | out->v_op = kbd_vnode.v_op; 198 | out->v_data = kbd_vnode.v_data; 199 | 200 | return 0; 201 | } 202 | 203 | static void toggle_left_shift(void) 204 | { 205 | is_lshift_down = is_lshift_down ? 0 : 1; 206 | } 207 | 208 | static void toggle_right_shift(void) 209 | { 210 | is_rshift_down = is_rshift_down ? 0 : 1; 211 | } 212 | 213 | static void toggle_caps_lock(void) 214 | { 215 | is_caps_lock_pressed = is_caps_lock_pressed ? 0 : 1; 216 | } 217 | 218 | static char handle_caps_lock(uint8_t ch) 219 | { 220 | if (ch >= 'a' && ch <= 'z') { 221 | return ch + 'A' - 'a'; 222 | } 223 | return ch; 224 | } 225 | 226 | static char handle_shift(uint8_t ch) 227 | { 228 | /* Alphabetic characters */ 229 | if (ch >= 'a' && ch <= 'z') { 230 | return ch + 'A' - 'a'; 231 | } 232 | 233 | /* Number characters */ 234 | switch (ch) { 235 | case '0': 236 | return ')'; 237 | case '1': 238 | return '!'; 239 | case '2': 240 | return '@'; 241 | case '3': 242 | return '#'; 243 | case '4': 244 | return '$'; 245 | case '5': 246 | return '%'; 247 | case '6': 248 | 249 | return '^'; 250 | case '7': 251 | return '&'; 252 | case '8': 253 | return '*'; 254 | case '9': 255 | return '('; 256 | default: 257 | break; 258 | } 259 | 260 | /* Special charachters */ 261 | switch (ch) { 262 | case '-': 263 | return '_'; 264 | case '=': 265 | return '+'; 266 | case '[': 267 | return '{'; 268 | case ']': 269 | return '}'; 270 | case '\\': 271 | return '|'; 272 | case ';': 273 | return ':'; 274 | case '\'': 275 | return '\"'; 276 | case ',': 277 | return '<'; 278 | case '.': 279 | return '>'; 280 | case '/': 281 | return '?'; 282 | case '`': 283 | return '~'; 284 | } 285 | 286 | return ch; 287 | } 288 | 289 | uint8_t kbd_read_scan_code(void) 290 | { 291 | return inb(KBD_DATA_PORT); 292 | } 293 | 294 | static char kbd_scan_code_to_ascii(uint8_t scan_code) 295 | { 296 | char ch = -1; 297 | 298 | if (scan_code & 0x80) { 299 | scan_code &= 0x7F; /* clear the bit set by key break */ 300 | 301 | switch (scan_code) { 302 | case KBD_SC_LSHIFT: 303 | toggle_left_shift(); 304 | break; 305 | case KBD_SC_RSHIFT: 306 | toggle_right_shift(); 307 | break; 308 | case KBD_SC_CAPSLOCK: 309 | toggle_caps_lock(); 310 | break; 311 | default: 312 | break; 313 | } 314 | 315 | return -1; 316 | } 317 | 318 | switch (scan_code) { 319 | case KBD_SC_A: 320 | ch = 'a'; 321 | break; 322 | case KBD_SC_B: 323 | ch = 'b'; 324 | break; 325 | case KBD_SC_C: 326 | ch = 'c'; 327 | break; 328 | case KBD_SC_D: 329 | ch = 'd'; 330 | break; 331 | case KBD_SC_E: 332 | ch = 'e'; 333 | break; 334 | case KBD_SC_F: 335 | ch = 'f'; 336 | break; 337 | case KBD_SC_G: 338 | ch = 'g'; 339 | break; 340 | case KBD_SC_H: 341 | ch = 'h'; 342 | break; 343 | case KBD_SC_I: 344 | ch = 'i'; 345 | break; 346 | case KBD_SC_J: 347 | ch = 'j'; 348 | break; 349 | case KBD_SC_K: 350 | ch = 'k'; 351 | break; 352 | case KBD_SC_L: 353 | ch = 'l'; 354 | break; 355 | case KBD_SC_M: 356 | ch = 'm'; 357 | break; 358 | case KBD_SC_N: 359 | ch = 'n'; 360 | break; 361 | case KBD_SC_O: 362 | ch = 'o'; 363 | break; 364 | case KBD_SC_P: 365 | ch = 'p'; 366 | break; 367 | case KBD_SC_Q: 368 | ch = 'q'; 369 | break; 370 | case KBD_SC_R: 371 | ch = 'r'; 372 | break; 373 | case KBD_SC_S: 374 | ch = 's'; 375 | break; 376 | case KBD_SC_T: 377 | ch = 't'; 378 | break; 379 | case KBD_SC_U: 380 | ch = 'u'; 381 | break; 382 | case KBD_SC_V: 383 | ch = 'v'; 384 | break; 385 | case KBD_SC_W: 386 | ch = 'w'; 387 | break; 388 | case KBD_SC_X: 389 | ch = 'x'; 390 | break; 391 | case KBD_SC_Y: 392 | ch = 'y'; 393 | break; 394 | case KBD_SC_Z: 395 | ch = 'z'; 396 | break; 397 | case KBD_SC_0: 398 | ch = '0'; 399 | break; 400 | case KBD_SC_1: 401 | ch = '1'; 402 | break; 403 | case KBD_SC_2: 404 | ch = '2'; 405 | break; 406 | case KBD_SC_3: 407 | ch = '3'; 408 | break; 409 | case KBD_SC_4: 410 | ch = '4'; 411 | break; 412 | case KBD_SC_5: 413 | ch = '5'; 414 | break; 415 | case KBD_SC_6: 416 | ch = '6'; 417 | break; 418 | case KBD_SC_7: 419 | ch = '7'; 420 | break; 421 | case KBD_SC_8: 422 | ch = '8'; 423 | break; 424 | case KBD_SC_9: 425 | ch = '9'; 426 | break; 427 | case KBD_SC_ENTER: 428 | ch = '\n'; 429 | break; 430 | case KBD_SC_SPACE: 431 | ch = ' '; 432 | break; 433 | case KBD_SC_BS: 434 | ch = 8; 435 | break; 436 | case KBD_SC_DASH: 437 | ch = '-'; 438 | break; 439 | case KBD_SC_EQUALS: 440 | ch = '='; 441 | break; 442 | case KBD_SC_LBRACKET: 443 | ch = '['; 444 | break; 445 | case KBD_SC_RBRACKET: 446 | ch = ']'; 447 | break; 448 | case KBD_SC_BSLASH: 449 | ch = '\\'; 450 | break; 451 | case KBD_SC_SCOLON: 452 | ch = ';'; 453 | break; 454 | case KBD_SC_QUOTE: 455 | ch = '\''; 456 | break; 457 | case KBD_SC_COMMA: 458 | ch = ','; 459 | break; 460 | case KBD_SC_DOT: 461 | ch = '.'; 462 | break; 463 | case KBD_SC_FSLASH: 464 | ch = '/'; 465 | break; 466 | case KBD_SC_TILDE: 467 | ch = '`'; 468 | break; 469 | case KBD_SC_TAB: 470 | ch = '\t'; 471 | break; 472 | case KBD_SC_LSHIFT: 473 | toggle_left_shift(); 474 | break; 475 | case KBD_SC_RSHIFT: 476 | toggle_right_shift(); 477 | break; 478 | default: 479 | return -1; 480 | } 481 | 482 | if (is_caps_lock_pressed) { 483 | ch = handle_caps_lock(ch); 484 | } 485 | 486 | if (is_lshift_down || is_rshift_down) { 487 | ch = handle_shift(ch); 488 | } 489 | 490 | return ch; 491 | } 492 | -------------------------------------------------------------------------------- /doc/diary.md: -------------------------------------------------------------------------------- 1 | % Developer diary 2 | % Erik Helin, Adam Renberg 3 | % 2012-01-27 4 | 5 | # 2012-01-16 6 | 7 | ## Achievements 8 | 9 | - wrote a short guide for how to install bochs on Ubuntu with GDB support 10 | - installed nasm 11 | - set up an initial develop environment consisting of: 12 | - gcc as compiler 13 | - make as build system 14 | - nasm as assembler 15 | - bochs as simulator 16 | - bash as scripting language 17 | - managed to boot the kernel in bochs using the following two guides: 18 | - 19 | - 20 | - started working on a framebuffer device driver, wrote the two functions 21 | `fb_write` and `fb_clear` 22 | - started investigating on how to improve the booting of the kernel 23 | 24 | ## Issues 25 | 26 | - the current boot sequence must automatize how grub executes the kernel 27 | - the media used for the OS should probably be an ISO image instead of a 28 | floppy image if we ever want to boot the OS on a real computer 29 | 30 | 31 | # 2012-01-17 32 | 33 | ## Achievements 34 | 35 | - read up on x86 assembly, nasm, cdecl, and figured out how to call assembler 36 | code from c 37 | - wrote short functions to read and write to io ports 38 | - wrote more on the framebuffer device driver, perhaps completed it 39 | - can move the cursor 40 | - can print chars at cursor position (`putb`) and print a string (puts) 41 | - newlines are handled, and scrolling 42 | - wrote a new floppy-creator script (`src/create_image.sh`) that makes it easier 43 | to start the os - just type make run (no need to tell grub start block or 44 | size any more 45 | - started to read up on segmentation and gdt/idt stuff 46 | 47 | ## Issues 48 | 49 | - the current floppy-build script requires `sudo` (to mount and use loopback 50 | devices), and it might cause nautilus (on ubuntu) to open the mounted 51 | directory, and complain when it is unmounted (can be turned off though) 52 | 53 | # 2012-01-18 54 | 55 | ## Achievements 56 | 57 | - the gdt loads correctly. yay! 58 | - wrote code to remap pic interrupts into the idt 59 | - started to write code to initialize the idt 60 | 61 | ## Issues 62 | 63 | - the documentation on the x86 architecture is sparse at best - pic i/o port 64 | address, especially. We take the values from a tutorial 65 | 66 | # 2012-01-19 67 | 68 | ## Achievements 69 | 70 | - interrupts is finally working correctly. This took a while to implement, 71 | mostly due to having to deal with the PIC and the keyboard. 72 | - added code for printing integers 73 | - improved the build scripts slightly 74 | - joined the `#osdev` irc channel on `irc.freenode.org` where we got help with 75 | some keyboard hardware details. we also got help on the os-dev forum 76 | 77 | ## Issues 78 | 79 | - We are still having problems with the lack of documentation for various 80 | hardware, these are the issues that takes the longest time to solve, since we 81 | often have to ask for help on irc or os-dev 82 | 83 | # 2012-01-20 84 | 85 | ## Achievements 86 | 87 | - the keyboard driver is more or less finished (all normal keys are covered, 88 | but support for numpad, F1 - F12, Insert, Home etc are missing). We will add 89 | support for them later on if needed. 90 | - the PIT (Programmable Interrupt Timer) driver seems to be finished 91 | (hard to test), at least the timer 92 | fires at different intervals depending on the frequency you set via the 93 | driver. 94 | - started reading up on paging. Also implemented full support for GRUB 95 | multiboot info. We can now find reserved and available memory areas, which 96 | is crucial to set up the page table. 97 | 98 | ## Issues 99 | 100 | - Testing things such as timers will be hard. They seems to work correctly, but 101 | it is hard to know if they fire at the exact correct interval. 102 | - We will have to implement a function that print integers in hexadecimal 103 | notation to ease debugging. 104 | 105 | ## Notes 106 | 107 | - Since the basic infrastructure is now working (console, keyboard, timer), 108 | the road ahead is not as clear any longer. Now we will probably have to read 109 | a bit more theory and also start to discuss how we want to design the kernel, 110 | since at the moment we do not need any more low-level plumbing. 111 | 112 | # 2012-01-21 113 | 114 | ## Achievements 115 | 116 | - wrote a function that prints integers in hexadecimal notation 117 | 118 | # 2012-01-22 119 | 120 | ## Achievements 121 | 122 | - wrote a simple `printf()` based on the one in K&R 123 | - added configuration file for doxygen 124 | - wrote some doxygen documentation 125 | 126 | ## Issues 127 | 128 | - We need to document some more :D 129 | 130 | # 2012-01-23 131 | 132 | ## Achievements 133 | 134 | - wrote and enabled identity (1:1) paging for the first 4MB of memory (this 135 | is where the kernel resides) 136 | - rewrote the paging so that the kernel is an "upper-half" ("higher-half") 137 | kernel, which means that memory from `0xC0000000 - 0XFFFFFFFF` is mapped to 138 | the kernel (the kernel is still at the physical memory at 1MB) 139 | 140 | ## Issues 141 | 142 | - We need to write more documentation on the paging and upper-half kernel 143 | stuff. 144 | 145 | # 2012-01-24 146 | 147 | ## Achievements 148 | 149 | - wrote a script to generate a bootable ISO file with GRUB 2 150 | - rewrote the script to generate a bootable ISO file with GRUB 1 and possibly 151 | arbitrary binary modules that the kernel load into memory 152 | - we now only depend on `genisoimage` to create the bootable media 153 | - managed to jump to a loaded module and perform an INT 0x86 instruction to 154 | trigger a sys call interrupt 155 | - make can now automatically build all modules and copy them to the iso 156 | 157 | ## Issues 158 | 159 | - We were not able to jump back to the kernel code after jumping loaded binary 160 | code. This will require more investigation. 161 | - The document about paging and kernel is still not written, Erik will write it 162 | tomorrow morning. 163 | 164 | # 2012-01-25 165 | 166 | ## Achievements 167 | 168 | - wrote text about paging, the kernel, and putting the kernel in the upper half 169 | of memory 170 | - configured pandoc/markdown2pdf to generate html and pdf from the markdown 171 | files 172 | - changed the interrupt number for syscalls to `0xAE` 173 | - we can call modules loaded by GRUB with call, and they can return to the 174 | kernel with ret 175 | - determined to change the kernel stack to the end of the kernels 4MB page. 176 | Started to write code to relocate the modules loaded by GRUB to make it 177 | safe to put the stack there. The code written today creates an identity 178 | mapping of all memory (except for the kernels upper-half place), and uses a 179 | small temporary stack in `.bss`. No module moving as of yet. 180 | 181 | # 2012-01-26 182 | 183 | ## Achievements 184 | 185 | - finished the code that moves the modules loaded by GRUB to a well-known 186 | position 187 | - ported the malloc and free implementations from K&R to aenix. Also set up a 188 | dedicated heap for the kernel in the first 4 MB. 189 | - created a "real" stack for the kernel, the stack now grows from the end of 190 | the kernel page towards "lower" addresses. The heap grows from the end of the 191 | kernel towards the stack. 192 | - created a new entry in the stack page descritor table for the modules. 193 | - discussed how to enable user mode processes and how to layout the memory for 194 | the processes. 195 | - started to read up on how to change the to code with PL3 instead of PL0 196 | - wrote a driver for the COM ports (only COM1 enabled for now) 197 | - wrote a log module. Since bochs can redirect the output from the COM ports to 198 | file, we can now log arbitrary text to files (very helpful!) 199 | 200 | ## Issues 201 | 202 | None, as we learn more and more about how everything works together, the issues 203 | are more architectural and less "low-level", which is a nice change! 204 | 205 | # 2012-01-27 206 | 207 | ## Achievements 208 | 209 | - wrote the utility `mkfs` for creating a filesystem on a disk. Basically, 210 | `mkfs` traverses a folder, then writes every file and directory to a 211 | combined large file along with some headers. The GRUB loads the file as a 212 | module into memory. Then, the kernel can read the "filesystem" with the help 213 | of the headers. This is essentially a very small initrd filesystem. 214 | - wrote the very small program `init.s` that just loops forever and placed in 215 | the `/bin/` folder in the filesystem. 216 | - wrote code the locate the `init` program in the memory. 217 | - managed to jump to the `init` and at the same change the privilege level to 218 | 3. This means that the code in `init` is executing in user mode! 219 | 220 | ## Issues 221 | 222 | - Got a taste today of what it's like to debug kernel code. When changing 223 | privilege level, the CPU throw fault 13 (General Protection fault). The bug 224 | was caused by the size of the GDT being specified as 3 when it should be 5 225 | (we simply forgot to change the size when adding to new entries). This shows 226 | how important it is to review _all_ kernel code to avoid these mistakes. 227 | 228 | # 2012-01-28 229 | 230 | ## Achievements 231 | 232 | - Wrote skeleton code for process creation, planned the structure of how to do 233 | it. 234 | 235 | 236 | # 2012-01-30 237 | 238 | ## Achievements 239 | 240 | - Worked on setting up paging tables and such for user mode processes - mapping 241 | and unmapping memory from virtual to physical. 242 | - Sketched an outline of how to implement the page frame allocator. 243 | 244 | ## Issues 245 | 246 | - Cannot allocate 4kB aligned memory needed for both the process' sections and 247 | for their page tables. Writing the page frame allocator tomorrow. 248 | 249 | 250 | # 2012-01-31 251 | 252 | ## Achievements 253 | 254 | - The kernel is now mapped in into virtual memory in 4kB page frames, which 255 | leaves more room for other kernel data and/or user processes. This breaks 256 | kmalloc() and the in-memory file system 257 | - Thought a lot on the page frame allocator. Since the page frames for the page 258 | tables might not actually have a virtual memory address we can't access them 259 | until we map them in. We implemented a way to temporarily map in a page frame. 260 | 261 | ## Issues 262 | 263 | - kmalloc() and the file system code needs to be updated. 264 | 265 | ## Notes 266 | 267 | - The kernel's code and rodata should be mapped as read-only. This requires the 268 | kernel to be mapped in as several 4kB pages, the sections to be properly 269 | aligned (which they are), and the setting-up to be done in assembler. 270 | 271 | 272 | # 2012-02-01 273 | 274 | ## Achievements 275 | 276 | - Cleaned up some code. Wrote a script to turn c headers with constant 277 | definitions into .inc files to be included in nasm code. 278 | - Met with Torbjörn 279 | - Started to implement the page frame allocator 280 | 281 | ## Issues 282 | 283 | - We couldn't get the Makefile to correctly use .inc as dependencies for the 284 | assembler files. We worked around it, but it would be nice for it to work 285 | directly from the Makefile. 286 | 287 | 288 | # 2012-02-02 289 | 290 | ## Achievements 291 | 292 | - Wrote a lot of code to do error checking, which caused us to find several 293 | bugs. `log_err()`, `log_info()` and `log_debug()` were created to simplify 294 | logging 295 | - "Finished" the page frame allocator. 296 | - Debugged. A lot. Found several bugs, which we fixed. 297 | - Modified `kmalloc()` to use the page frame allocator to ask for page frame-sized 298 | blocks. 299 | - `process_create()` creates a new process, with code loaded from file and pdt 300 | set up. 301 | - Can enter user mode for the init program, which is created through 302 | `process_create()`. 303 | 304 | 305 | # 2012-02-06 306 | 307 | ## Achievements 308 | 309 | - User mode works, after a long fight with weird and difficult-to-find bugs. 310 | (The kernel stack pointer in the TSS was pointed at the wrong end, which only 311 | mattered if the TSS struct was non-static, because then the TSS was located 312 | next to the stack and overwritten on some interrupts...) 313 | - Syscalls work! 314 | - Restructured code base to simplfy adding "apps" such as init. 315 | - User mode programs can now be written in C. With a modest start on our libc 316 | (there is an interrupt.h|s and a start.s that is linked first into the 317 | binary with link.ld), init runs compiled from C. 318 | 319 | 320 | # 2012-02-07 - 2012-02-10 321 | 322 | ## Achievements 323 | 324 | - AEFS2 - We extended the file system created by mkfs so that it is more general 325 | and can handle writes. It the "device" is divied into blocks of 1024 bits; the 326 | first block is a superblock with general info, then there are some inode 327 | blocks, and then datablocks. 328 | - VFS - We have implemented a virtual file system layer. This deals with the 329 | path hierarchy in the file systems (name/path lookup) and with mounting file 330 | systems on top of it. An in-memory AEFS file system is mounted at / and a 331 | devfs file system is mounted at /dev/. Actually reading a "file" (vnode) is 332 | delegated to the file system within which it is located. 333 | - devfs - devfs is a quite cool idea and a very simple file system. A devfs is 334 | mouned at /dev/ and devices (such as the frame buffer, keyboard, serial port 335 | etc. ) can register vnodes with the devfs. These vnodes (/dev/cons, 336 | /dev/keyboard, etc.) will then be presented as files to user code. Opening, 337 | reading, writing etc. to these files will actually be delegated, via the 338 | VFS, to the corresponding device drivers to do the work. This also means that 339 | there can actually be hierarchies within these devices, such as /dev/cons/1 340 | and /dev/cons/2 for two different consoles. These hierarchies will be 341 | accessed through the file system but managed by the devices. 342 | - `SYS_write` and `SYS_open` work, with file descriptors and the whole chebang. 343 | 344 | # 2012-02-13 345 | 346 | ## Achievements 347 | - Implemented the `SYS_read` system call which enables reading from the 348 | keyboard, which is mounted under `/dev/keyboard`. However, we haven't 349 | implemented the ECHO feature that most consoles have (if you type something, 350 | it will show up on your console, whether the program reads it or not). 351 | - Implemented the `SYS_execve` as a first step towards a multiprocess OS. As a 352 | result of this, `/bin/init` is now launching `/bin/sh`. 353 | - Wrote our first concurrent code in the kernel when buffering the keyboard 354 | input. 355 | 356 | ## Issues 357 | - We were a little bit puzzled about how to handle the input buffering from the 358 | keyboard, but we settled on the way MINIX and FreeBSD does when the buffer is 359 | full: discarding the new input. 360 | - We haven't implemented the ECHO feature of the console yet, since we want to 361 | do it in a proper way (NOT in the keyboard interrupt handler!). To do this, 362 | we think we want some kind of higher level event handling facility. But in 363 | order to achieve that, we need kernel processes, so therefore we have started 364 | to work on the scheduler. 365 | -------------------------------------------------------------------------------- /src/kernel/paging.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | #include "stdint.h" 3 | #include "stdio.h" 4 | #include "common.h" 5 | #include "log.h" 6 | #include "mem.h" 7 | #include "constants.h" 8 | #include "page_frame_allocator.h" 9 | 10 | #define NUM_ENTRIES 1024 11 | #define PDT_SIZE NUM_ENTRIES * sizeof(pde_t) 12 | #define PT_SIZE NUM_ENTRIES * sizeof(pte_t) 13 | 14 | #define VIRTUAL_TO_PDT_IDX(a) (((a) >> 22) & 0x3FF) 15 | #define VIRTUAL_TO_PT_IDX(a) (((a) >> 12) & 0x3FF) 16 | #define PDT_IDX_TO_VIRTUAL(a) (((a) << 22)) 17 | #define PT_IDX_TO_VIRTUAL(a) (((a) << 12)) 18 | 19 | #define PS_4KB 0x00 20 | #define PS_4MB 0x01 21 | 22 | #define IS_ENTRY_PRESENT(e) ((e)->config && 0x01) 23 | #define IS_ENTRY_PAGE_TABLE(e) (((e)->config && 0x80) == 0) 24 | 25 | #define PT_ENTRY_SIZE FOUR_KB 26 | #define PDT_ENTRY_SIZE FOUR_MB 27 | 28 | #define KERNEL_TMP_PT_IDX 1023 29 | #define KERNEL_TMP_VADDR \ 30 | (KERNEL_START_VADDR + KERNEL_TMP_PT_IDX * PT_ENTRY_SIZE) 31 | #define KERNEL_PT_PDT_IDX VIRTUAL_TO_PDT_IDX(KERNEL_START_VADDR) 32 | 33 | /* pde: page directory entry */ 34 | struct pde { 35 | uint8_t config; 36 | uint8_t low_addr; /* only the highest 4 bits are used */ 37 | uint16_t high_addr; 38 | } __attribute__((packed)); 39 | 40 | #include "paging.h" /* must be included after the pde struct */ 41 | 42 | struct pte { 43 | uint8_t config; 44 | uint8_t middle; /* only the highest 4 bits and the lowest bit are used */ 45 | uint16_t high_addr; 46 | } __attribute__((packed)); 47 | typedef struct pte pte_t; 48 | 49 | static pde_t *kernel_pdt; 50 | static pte_t *kernel_pt; 51 | 52 | extern void pdt_set(uint32_t pdt_addr); /* defined in paging_asm.s */ 53 | extern void invalidate_page_table_entry(uint32_t vaddr); 54 | 55 | 56 | static void create_pdt_entry(pde_t *pdt, 57 | uint32_t n, 58 | uint32_t paddr, 59 | uint8_t ps, 60 | uint8_t rw, 61 | uint8_t pl); 62 | static void create_pt_entry(pte_t *pt, 63 | uint32_t n, 64 | uint32_t paddr, 65 | uint8_t rw, 66 | uint8_t pl); 67 | 68 | static uint32_t get_pt_paddr(pde_t *pde, uint32_t pde_idx) 69 | { 70 | pde_t *e = pde + pde_idx; 71 | uint32_t addr = e->high_addr; 72 | addr <<= 16; 73 | addr |= ((uint32_t) (e->low_addr & 0xF0) << 8); 74 | 75 | return addr; 76 | } 77 | 78 | static uint32_t get_pf_paddr(pte_t *pt, uint32_t pt_idx) 79 | { 80 | pte_t *e = pt + pt_idx; 81 | uint32_t addr = e->high_addr; 82 | addr <<= 16; 83 | addr |= ((uint32_t) (e->middle & 0xF0) << 8); 84 | 85 | return addr; 86 | } 87 | 88 | static uint32_t kernel_map_temporary_memory(uint32_t paddr) 89 | { 90 | create_pt_entry(kernel_pt, KERNEL_TMP_PT_IDX, paddr, 91 | PAGING_READ_WRITE, PAGING_PL0); 92 | invalidate_page_table_entry(KERNEL_TMP_VADDR); 93 | return KERNEL_TMP_VADDR; 94 | } 95 | 96 | static uint32_t kernel_get_temporary_entry() 97 | { 98 | return *((uint32_t *) &kernel_pt[KERNEL_TMP_PT_IDX]); 99 | } 100 | 101 | static void kernel_set_temporary_entry(uint32_t entry) 102 | { 103 | kernel_pt[KERNEL_TMP_PT_IDX] = *((pte_t *) &entry); 104 | invalidate_page_table_entry(KERNEL_TMP_VADDR); 105 | } 106 | 107 | /* The given pdt must be mapped somwhere in the kernels page table, 108 | * otherwise it would have been impossible to use the pointer. 109 | * 110 | * Therefore to look up the physical address, we can simply use the kernel_pdt. 111 | */ 112 | static uint32_t get_pdt_paddr(pde_t *pdt) 113 | { 114 | uint32_t pdt_vaddr = (uint32_t) pdt; 115 | uint32_t kpdt_idx = VIRTUAL_TO_PDT_IDX(pdt_vaddr); 116 | uint32_t kpt_paddr = get_pt_paddr(kernel_pdt, kpdt_idx); 117 | uint32_t kpt_idx = VIRTUAL_TO_PT_IDX(pdt_vaddr); 118 | 119 | uint32_t prev_tmp_entry = kernel_get_temporary_entry(); 120 | uint32_t kpt_vaddr = kernel_map_temporary_memory(kpt_paddr); 121 | 122 | pte_t *kpt = (pte_t *) kpt_vaddr; 123 | uint32_t pdt_paddr = get_pf_paddr(kpt, kpt_idx); 124 | 125 | kernel_set_temporary_entry(prev_tmp_entry); 126 | 127 | return pdt_paddr; 128 | } 129 | 130 | uint32_t paging_init(uint32_t kernel_pdt_vaddr, uint32_t kernel_pt_vaddr) 131 | { 132 | log_info("paging_init", "kernel_pdt_vaddr: %X, kernel_pt_vaddr: %X\n", 133 | kernel_pdt_vaddr, kernel_pt_vaddr); 134 | kernel_pdt = (pde_t *) kernel_pdt_vaddr; 135 | kernel_pt = (pte_t *) kernel_pt_vaddr; 136 | return 0; 137 | } 138 | 139 | static uint32_t pt_kernel_find_next_vaddr(uint32_t pdt_idx, 140 | pte_t *pt, uint32_t size) 141 | { 142 | uint32_t i, num_to_find, num_found = 0, org_i; 143 | num_to_find = align_up(size, FOUR_KB) / FOUR_KB; 144 | 145 | for (i = 0; i < NUM_ENTRIES; ++i) { 146 | if (IS_ENTRY_PRESENT(pt+i) || 147 | (pdt_idx == KERNEL_PT_PDT_IDX && i == KERNEL_TMP_PT_IDX)) { 148 | num_found = 0; 149 | } else { 150 | if (num_found == 0) { 151 | org_i = i; 152 | } 153 | ++num_found; 154 | if (num_found == num_to_find) { 155 | return PDT_IDX_TO_VIRTUAL(pdt_idx) | 156 | PT_IDX_TO_VIRTUAL(org_i); 157 | } 158 | } 159 | } 160 | return 0; 161 | } 162 | 163 | uint32_t pdt_kernel_find_next_vaddr(uint32_t size) 164 | { 165 | uint32_t pdt_idx, pt_paddr, pt_vaddr, tmp_entry, vaddr = 0; 166 | /* TODO: support > 4MB sizes */ 167 | 168 | pdt_idx = VIRTUAL_TO_PDT_IDX(KERNEL_START_VADDR); 169 | for (; pdt_idx < NUM_ENTRIES; ++pdt_idx) { 170 | if (IS_ENTRY_PRESENT(kernel_pdt + pdt_idx)) { 171 | tmp_entry = kernel_get_temporary_entry(); 172 | 173 | pt_paddr = get_pt_paddr(kernel_pdt, pdt_idx); 174 | pt_vaddr = kernel_map_temporary_memory(pt_paddr); 175 | vaddr = 176 | pt_kernel_find_next_vaddr(pdt_idx, (pte_t *) pt_vaddr, size); 177 | 178 | kernel_set_temporary_entry(tmp_entry); 179 | } else { 180 | /* no pdt entry */ 181 | vaddr = PDT_IDX_TO_VIRTUAL(pdt_idx); 182 | } 183 | if (vaddr != 0) { 184 | return vaddr; 185 | } 186 | } 187 | 188 | return 0; 189 | } 190 | 191 | static uint32_t pt_map_memory(pte_t *pt, 192 | uint32_t pdt_idx, 193 | uint32_t paddr, 194 | uint32_t vaddr, 195 | uint32_t size, 196 | uint8_t rw, 197 | uint8_t pl) 198 | { 199 | uint32_t pt_idx = VIRTUAL_TO_PT_IDX(vaddr); 200 | uint32_t mapped_size = 0; 201 | 202 | while (mapped_size < size && pt_idx < NUM_ENTRIES) { 203 | if (IS_ENTRY_PRESENT(pt + pt_idx)) { 204 | log_info("pt_map_memory", 205 | "Entry is present: pt: %X, pt_idx: %u, pdt_idx: %u " 206 | "pt[pt_idx]: %X\n", 207 | pt, pt_idx, pdt_idx, pt[pt_idx]); 208 | return mapped_size; 209 | } else if(pdt_idx == KERNEL_PT_PDT_IDX && pt_idx == KERNEL_TMP_PT_IDX) { 210 | return mapped_size; 211 | } 212 | 213 | create_pt_entry(pt, pt_idx, paddr, rw, pl); 214 | 215 | paddr += PT_ENTRY_SIZE; 216 | mapped_size += PT_ENTRY_SIZE; 217 | ++pt_idx; 218 | } 219 | 220 | return mapped_size; 221 | } 222 | 223 | uint32_t pdt_map_memory(pde_t *pdt, 224 | uint32_t paddr, 225 | uint32_t vaddr, 226 | uint32_t size, 227 | uint8_t rw, 228 | uint8_t pl) 229 | { 230 | uint32_t pdt_idx; 231 | pte_t *pt; 232 | uint32_t pt_paddr, pt_vaddr, tmp_entry; 233 | uint32_t mapped_size = 0; 234 | uint32_t total_mapped_size = 0; 235 | size = align_up(size, PT_ENTRY_SIZE); 236 | 237 | while (size != 0) { 238 | pdt_idx = VIRTUAL_TO_PDT_IDX(vaddr); 239 | 240 | tmp_entry = kernel_get_temporary_entry(); 241 | 242 | if (!IS_ENTRY_PRESENT(pdt + pdt_idx)) { 243 | pt_paddr = pfa_allocate(1); 244 | if (pt_paddr == 0) { 245 | log_error("pdt_map_memory", 246 | "Couldn't allocate page frame for new page table." 247 | "pdt_idx: %u, data vaddr: %X, data paddr: %X, " 248 | "data size: %u\n", 249 | pdt_idx, vaddr, paddr, size); 250 | return 0; 251 | } 252 | pt_vaddr = kernel_map_temporary_memory(pt_paddr); 253 | memset((void *) pt_vaddr, 0, PT_SIZE); 254 | } else { 255 | pt_paddr = get_pt_paddr(pdt, pdt_idx); 256 | pt_vaddr = kernel_map_temporary_memory(pt_paddr); 257 | } 258 | 259 | pt = (pte_t *) pt_vaddr; 260 | mapped_size = 261 | pt_map_memory(pt, pdt_idx, paddr, vaddr, size, rw, pl); 262 | 263 | if (mapped_size == 0) { 264 | log_error("pdt_map_memory", 265 | "Could not map memory in page table. " 266 | "pt: %X, paddr: %X, vaddr: %X, size: %u\n", 267 | (uint32_t) pt, paddr, vaddr, size); 268 | kernel_set_temporary_entry(tmp_entry); 269 | return 0; 270 | } 271 | 272 | if (!IS_ENTRY_PRESENT(pdt + pdt_idx)) { 273 | create_pdt_entry(pdt, pdt_idx, pt_paddr, PS_4KB, rw, pl); 274 | } 275 | 276 | kernel_set_temporary_entry(tmp_entry); 277 | 278 | size -= mapped_size; 279 | total_mapped_size += mapped_size; 280 | vaddr += mapped_size; 281 | paddr += mapped_size; 282 | } 283 | 284 | return total_mapped_size; 285 | } 286 | 287 | uint32_t pdt_map_kernel_memory(uint32_t paddr, 288 | uint32_t vaddr, 289 | uint32_t size, 290 | uint8_t rw, 291 | uint8_t pl) 292 | { 293 | return pdt_map_memory(kernel_pdt, paddr, vaddr, 294 | size, rw, pl); 295 | } 296 | 297 | static uint32_t pt_unmap_memory(pte_t *pt, 298 | uint32_t pdt_idx, 299 | uint32_t vaddr, 300 | uint32_t size) 301 | { 302 | uint32_t pt_idx = VIRTUAL_TO_PT_IDX(vaddr); 303 | uint32_t freed_size = 0; 304 | 305 | while (freed_size < size && pt_idx < NUM_ENTRIES) { 306 | if (pdt_idx == KERNEL_PT_PDT_IDX && pt_idx == KERNEL_TMP_PT_IDX) { 307 | /* can't touch this */ 308 | return freed_size; 309 | } 310 | if (IS_ENTRY_PRESENT(pt + pt_idx)) { 311 | memset(pt + pt_idx, 0, sizeof(pte_t)); 312 | invalidate_page_table_entry(vaddr); 313 | } 314 | 315 | freed_size += PT_ENTRY_SIZE; 316 | vaddr += PT_ENTRY_SIZE; 317 | ++pt_idx; 318 | } 319 | 320 | return freed_size; 321 | } 322 | 323 | uint32_t pdt_unmap_memory(pde_t *pdt, uint32_t vaddr, uint32_t size) 324 | { 325 | uint32_t pdt_idx, pt_paddr, pt_vaddr, tmp_entry; 326 | 327 | uint32_t freed_size = 0; 328 | uint32_t end_vaddr; 329 | 330 | size = align_up(size, PT_ENTRY_SIZE); 331 | end_vaddr = vaddr + size; 332 | 333 | while (vaddr < end_vaddr) { 334 | pdt_idx = VIRTUAL_TO_PDT_IDX(vaddr); 335 | 336 | if (!IS_ENTRY_PRESENT(pdt + pdt_idx)) { 337 | vaddr = align_up(vaddr, PDT_ENTRY_SIZE); 338 | continue; 339 | } 340 | 341 | pt_paddr = get_pt_paddr(pdt, pdt_idx); 342 | tmp_entry = kernel_get_temporary_entry(); 343 | 344 | pt_vaddr = kernel_map_temporary_memory(pt_paddr); 345 | 346 | freed_size = 347 | pt_unmap_memory((pte_t *) pt_vaddr, pdt_idx, vaddr, size); 348 | 349 | kernel_set_temporary_entry(tmp_entry); 350 | 351 | if (freed_size == PDT_ENTRY_SIZE) { 352 | if (pdt_idx != KERNEL_PT_PDT_IDX) { 353 | pfa_free(pt_paddr); 354 | memset(pdt + pdt_idx, 0, sizeof(pde_t)); 355 | } 356 | } 357 | 358 | vaddr += freed_size; 359 | } 360 | 361 | return freed_size; 362 | } 363 | 364 | uint32_t pdt_unmap_kernel_memory(uint32_t virtual_addr, uint32_t size) 365 | { 366 | return pdt_unmap_memory(kernel_pdt, virtual_addr, size); 367 | } 368 | 369 | /* OUT paddr: The physical address for the PDT */ 370 | pde_t *pdt_create(uint32_t *out_paddr) 371 | { 372 | pde_t *pdt; 373 | *out_paddr = 0; 374 | uint32_t pdt_paddr = pfa_allocate(1); 375 | uint32_t pdt_vaddr = pdt_kernel_find_next_vaddr(PDT_SIZE); 376 | uint32_t size = pdt_map_kernel_memory(pdt_paddr, pdt_vaddr, PDT_SIZE, 377 | PAGING_READ_WRITE, PAGING_PL0); 378 | if (size < PDT_SIZE) { 379 | /* Since PDT_SIZE is the size of one frame, size must either be equal 380 | * to PDT_SIZE or 0 381 | */ 382 | pfa_free(pdt_paddr); 383 | return NULL; 384 | } 385 | 386 | pdt = (pde_t *) pdt_vaddr; 387 | 388 | memset(pdt, 0, PDT_SIZE); 389 | 390 | *out_paddr = pdt_paddr; 391 | return pdt; 392 | } 393 | 394 | void pdt_delete(pde_t *pdt) 395 | { 396 | uint32_t i, pdt_paddr; 397 | for (i = 0; i < NUM_ENTRIES; ++i) { 398 | if (IS_ENTRY_PRESENT(pdt + i) && IS_ENTRY_PAGE_TABLE(pdt + i)) { 399 | pfa_free(get_pt_paddr(pdt, i)); 400 | } 401 | } 402 | 403 | pdt_paddr = get_pdt_paddr(pdt); 404 | pfa_free(pdt_paddr); 405 | } 406 | 407 | void pdt_set(uint32_t pdt_paddr); 408 | void pdt_load_process_pdt(pde_t *pdt, uint32_t pdt_paddr) 409 | { 410 | uint32_t i; 411 | 412 | for (i = KERNEL_PDT_IDX; i < NUM_ENTRIES; ++i) { 413 | if (IS_ENTRY_PRESENT(kernel_pdt + i)) { 414 | pdt[i] = kernel_pdt[i]; 415 | } 416 | } 417 | 418 | pdt_set(pdt_paddr); 419 | } 420 | /** 421 | * Creates an entry in the page descriptor table at the specified index. 422 | * THe entry will point to the given PTE. 423 | * 424 | * @param pdt The page descriptor table 425 | * @param n The index in the PDT 426 | * @param addr The address to the first entry in the page table, or a 427 | * 4MB page frame 428 | * @param ps Page size, either PS_4KB or PS_4MB 429 | * @param rw Read/write permission, 0 = read-only, 1 = read and write 430 | * @param pl The required privilege level to access the page, 431 | * 0 = PL0, 1 = PL3 432 | */ 433 | static void create_pdt_entry(pde_t *pdt, 434 | uint32_t n, 435 | uint32_t addr, 436 | uint8_t ps, 437 | uint8_t rw, 438 | uint8_t pl) 439 | { 440 | /* Since page tables are aligned at 4kB boundaries, we only need to store 441 | * the 20 highest bits */ 442 | /* The lower 4 bits */ 443 | pdt[n].low_addr = ((addr >> 12) & 0xF) << 4; 444 | pdt[n].high_addr = ((addr >> 16) & 0xFFFF); 445 | 446 | /* 447 | * name | value | size | desc 448 | * --------------------------- 449 | * P | 1 | 1 | If the entry is present or not 450 | * R/W | rw | 1 | Read/Write: 451 | * 0 = Read-only 452 | * 1 = Write and Read 453 | * U/S | pl | 1 | User/Supervisor: 454 | * 0 = PL3 can't access 455 | * 1 = PL3 can access 456 | * PWT | 1 | 1 | Page-level write-through: 457 | * 0 = writes are cached 458 | * 1 = writes are not cached 459 | * PCD | 0 | 1 | Page-level cache disable 460 | * A | 0 | 1 | Is set if the entry has been accessed 461 | * Ignored | 0 | 1 | Ignored 462 | * PS | ps | 1 | Page size: 463 | * 0 = address point to pt entry, 464 | * 1 = address points to 4 MB page 465 | * Ignored | 0 | 4 | Ignored 466 | * 467 | * NOTE: Ignored is not part of pdt[n].config! 468 | */ 469 | pdt[n].config = 470 | ((ps & 0x01) << 7) | (0x01 << 3) | ((pl & 0x01) << 2) | 471 | ((rw & 0x01) << 1) | 0x01; 472 | } 473 | 474 | /** 475 | * Creates a new entry at the specified index in the given page table. 476 | * 477 | * @param pt The page table 478 | * @param n The index in the page table to create the entry at 479 | * @param addr The addres to the page frame 480 | * @param rw Read/write permission, 0 = read-only, 1 = read and write 481 | * @param pl The required privilege level to access the page, 482 | * 0 = PL0, 1 = PL3 483 | */ 484 | static void create_pt_entry(pte_t *pt, 485 | uint32_t n, 486 | uint32_t addr, 487 | uint8_t rw, 488 | uint8_t pl) 489 | { 490 | /* Since page tables are aligned at 4kB boundaries, we only need to store 491 | * the 20 highest bits */ 492 | /* The lower 4 bits */ 493 | pt[n].middle = ((addr >> 12) & 0xF) << 4; 494 | pt[n].high_addr = ((addr >> 16) & 0xFFFF); 495 | 496 | /* 497 | * name | value | size | desc 498 | * --------------------------- 499 | * P | 1 | 1 | If the entry is present or not 500 | * R/W | rw | 1 | Read/Write: 501 | * 0 = Read-only 502 | * 1 = Write and Read 503 | * U/S | pl | 1 | User/Supervisor: 504 | * 0 = PL3 can't access 505 | * 1 = PL3 can access 506 | * PWT | 1 | 1 | Page-level write-through: 507 | * 0 = writes are cached 508 | * 1 = writes are not cached 509 | * PCD | 0 | 1 | Page-level cache disable 510 | * A | 0 | 1 | Is set if the entry has been accessed 511 | * Ignored | 0 | 1 | Ignored 512 | * PAT | 0 | 1 | 1 = PAT is support, 0 = PAT is not supported 513 | * G | 0 | 1 | 1 = The PTE is global, 0 = The PTE is local 514 | * Ignored | 0 | 3 | Ignored 515 | * 516 | * NOTE: G and Ignore are part of pt[n].middle, not pt[n].config! 517 | */ 518 | pt[n].config = 519 | (0x01 << 3) | ((0x01 & pl) << 2) | ((0x01 & rw) << 1) | 0x01; 520 | } 521 | --------------------------------------------------------------------------------