├── homeworks ├── hw3 │ ├── .S-depend │ ├── .c-depend │ ├── mm │ │ ├── Makefile │ │ ├── obj.c │ │ ├── frame_alloc.h │ │ ├── obj.h │ │ ├── vmem.h │ │ ├── paging.h │ │ ├── vmem.c │ │ └── frame_alloc.c │ ├── boot │ │ ├── Makefile │ │ ├── gdt.h │ │ ├── early.h │ │ ├── multiboot.S │ │ ├── early.c │ │ └── early.S │ ├── drivers │ │ ├── Makefile │ │ ├── apic.h │ │ ├── acpi.h │ │ ├── vga.h │ │ ├── acpi.c │ │ ├── vga.c │ │ └── apic.c │ ├── kernel │ │ ├── Makefile │ │ ├── printk.h │ │ ├── panic.h │ │ ├── panic.c │ │ ├── irq.h │ │ ├── irq.c │ │ ├── kernel.c │ │ ├── multiboot.h │ │ ├── multiboot.c │ │ ├── irq.S │ │ └── printk.c │ ├── grub.cfg │ ├── gdb.script │ ├── a.py │ ├── types.h │ ├── build │ │ └── Makefile.inc │ ├── defs.h │ ├── arch │ │ └── x86.h │ ├── Makefile │ ├── common.h │ ├── linker.ld │ └── README.md ├── hw4 │ ├── mm │ │ ├── Makefile │ │ ├── obj.c │ │ ├── frame_alloc.h │ │ ├── obj.h │ │ ├── paging.h │ │ ├── vmem.h │ │ ├── frame_alloc.c │ │ └── vmem.c │ ├── boot │ │ ├── Makefile │ │ ├── early.h │ │ ├── multiboot.S │ │ ├── early.c │ │ └── early.S │ ├── drivers │ │ ├── Makefile │ │ ├── apic.h │ │ ├── acpi.h │ │ ├── vga.h │ │ ├── acpi.c │ │ ├── vga.c │ │ └── apic.c │ ├── kernel │ │ ├── Makefile │ │ ├── irq.h │ │ ├── errno.h │ │ ├── printk.h │ │ ├── syscall.h │ │ ├── panic.c │ │ ├── panic.h │ │ ├── syscall.c │ │ ├── multiboot.h │ │ ├── kernel.c │ │ ├── multiboot.c │ │ └── printk.c │ ├── sched │ │ ├── Makefile │ │ ├── sched.h │ │ ├── user.c │ │ └── sched.c │ ├── arch │ │ ├── x86 │ │ │ ├── Makefile │ │ │ ├── irq.h │ │ │ ├── syscall.h │ │ │ ├── context_switch.h │ │ │ ├── context_switch.S │ │ │ ├── gdt.h │ │ │ ├── regs_asm.h │ │ │ ├── msr.h │ │ │ ├── arch.h │ │ │ ├── x86.h │ │ │ ├── syscall.S │ │ │ ├── irq.c │ │ │ └── irq.S │ │ └── README.md │ ├── grub.cfg │ ├── linker.h │ ├── types.h │ ├── build │ │ └── Makefile.inc │ ├── defs.h │ ├── Makefile │ ├── common.h │ ├── linker.ld │ └── README.md ├── hw1 │ ├── grub.cfg │ ├── paging.h │ ├── gdb.script │ ├── gdt.h │ ├── printk.h │ ├── panic.c │ ├── types.h │ ├── panic.h │ ├── multiboot.c │ ├── linker.ld │ ├── Makefile │ ├── vga.h │ ├── kernel.c │ ├── common.h │ ├── multiboot.h │ ├── vga.c │ ├── README.md │ ├── printk.c │ └── boot.S └── hw2 │ ├── grub.cfg │ ├── paging.h │ ├── gdb.script │ ├── gdt.h │ ├── x86.h │ ├── apic.h │ ├── printk.h │ ├── types.h │ ├── panic.c │ ├── panic.h │ ├── linker.ld │ ├── irq.h │ ├── acpi.h │ ├── vga.h │ ├── common.h │ ├── kernel.c │ ├── acpi.c │ ├── Makefile │ ├── irq.c │ ├── multiboot.h │ ├── multiboot.c │ ├── vga.c │ ├── README.md │ ├── irq.S │ ├── printk.c │ ├── boot.S │ └── apic.c ├── .gitignore ├── lectures ├── 09-mp │ └── main.pdf ├── 00-intro │ ├── main.pdf │ └── main.md ├── 07-tasks │ ├── main.pdf │ ├── tss_64.jpg │ ├── task-states.png │ ├── context-switch.png │ └── main.md ├── 03-interrupts │ ├── apic.png │ ├── main.pdf │ ├── lapic.png │ ├── idt_descriptor.png │ └── main.md ├── 12-file-systems │ └── main.pdf ├── 01-x86-64-overview │ ├── main.pdf │ ├── eflags.png │ ├── x86_address.png │ └── x86_page_tables_and_address.png ├── 05-memory-management │ └── main.pdf └── 11-buses-and-drives │ ├── main.pdf │ └── main.md └── README.md /homeworks/hw3/.S-depend: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.bin 3 | *.sym 4 | *.iso 5 | -------------------------------------------------------------------------------- /homeworks/hw3/.c-depend: -------------------------------------------------------------------------------- 1 | page_alloc.c.o: mm/page_alloc.c 2 | -------------------------------------------------------------------------------- /homeworks/hw3/mm/Makefile: -------------------------------------------------------------------------------- 1 | MODULE=mm 2 | include ../build/Makefile.inc 3 | -------------------------------------------------------------------------------- /homeworks/hw4/mm/Makefile: -------------------------------------------------------------------------------- 1 | MODULE=mm 2 | include ../build/Makefile.inc 3 | -------------------------------------------------------------------------------- /homeworks/hw3/boot/Makefile: -------------------------------------------------------------------------------- 1 | MODULE=boot 2 | include ../build/Makefile.inc 3 | -------------------------------------------------------------------------------- /homeworks/hw4/boot/Makefile: -------------------------------------------------------------------------------- 1 | MODULE=boot 2 | include ../build/Makefile.inc 3 | -------------------------------------------------------------------------------- /homeworks/hw3/drivers/Makefile: -------------------------------------------------------------------------------- 1 | MODULE=drivers 2 | include ../build/Makefile.inc 3 | -------------------------------------------------------------------------------- /homeworks/hw3/kernel/Makefile: -------------------------------------------------------------------------------- 1 | MODULE=kernel 2 | include ../build/Makefile.inc 3 | -------------------------------------------------------------------------------- /homeworks/hw4/drivers/Makefile: -------------------------------------------------------------------------------- 1 | MODULE=drivers 2 | include ../build/Makefile.inc 3 | -------------------------------------------------------------------------------- /homeworks/hw4/kernel/Makefile: -------------------------------------------------------------------------------- 1 | MODULE=kernel 2 | include ../build/Makefile.inc 3 | -------------------------------------------------------------------------------- /homeworks/hw4/sched/Makefile: -------------------------------------------------------------------------------- 1 | MODULE=sched 2 | include ../build/Makefile.inc 3 | -------------------------------------------------------------------------------- /homeworks/hw4/arch/x86/Makefile: -------------------------------------------------------------------------------- 1 | MODULE=arch-x86 2 | include ../../build/Makefile.inc 3 | -------------------------------------------------------------------------------- /homeworks/hw4/kernel/irq.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "arch/x86/arch.h" 4 | 5 | -------------------------------------------------------------------------------- /lectures/09-mp/main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carzil/mipt-llp-2022/HEAD/lectures/09-mp/main.pdf -------------------------------------------------------------------------------- /homeworks/hw1/grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=0 2 | 3 | menuentry "myos" { 4 | multiboot2 /boot/kernel.bin 5 | } 6 | -------------------------------------------------------------------------------- /homeworks/hw2/grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=0 2 | 3 | menuentry "myos" { 4 | multiboot2 /boot/kernel.bin 5 | } 6 | -------------------------------------------------------------------------------- /homeworks/hw3/grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=0 2 | 3 | menuentry "myos" { 4 | multiboot2 /boot/kernel.bin 5 | } 6 | -------------------------------------------------------------------------------- /homeworks/hw4/grub.cfg: -------------------------------------------------------------------------------- 1 | set timeout=0 2 | 3 | menuentry "myos" { 4 | multiboot2 /boot/kernel.bin 5 | } 6 | -------------------------------------------------------------------------------- /lectures/00-intro/main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carzil/mipt-llp-2022/HEAD/lectures/00-intro/main.pdf -------------------------------------------------------------------------------- /lectures/07-tasks/main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carzil/mipt-llp-2022/HEAD/lectures/07-tasks/main.pdf -------------------------------------------------------------------------------- /homeworks/hw1/paging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PTE_PAGE_SIZE (1<<7) 4 | #define PTE_PRESENT (1<<0) 5 | -------------------------------------------------------------------------------- /homeworks/hw2/paging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PTE_PAGE_SIZE (1<<7) 4 | #define PTE_PRESENT (1<<0) 5 | -------------------------------------------------------------------------------- /homeworks/hw4/kernel/errno.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define ENOSYS 1 4 | #define ENOMEM 2 5 | #define EINVAL 3 6 | -------------------------------------------------------------------------------- /lectures/07-tasks/tss_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carzil/mipt-llp-2022/HEAD/lectures/07-tasks/tss_64.jpg -------------------------------------------------------------------------------- /lectures/03-interrupts/apic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carzil/mipt-llp-2022/HEAD/lectures/03-interrupts/apic.png -------------------------------------------------------------------------------- /lectures/03-interrupts/main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carzil/mipt-llp-2022/HEAD/lectures/03-interrupts/main.pdf -------------------------------------------------------------------------------- /homeworks/hw4/arch/README.md: -------------------------------------------------------------------------------- 1 | # Architecture-specific code 2 | 3 | This directory contains architecture-specific code. 4 | -------------------------------------------------------------------------------- /lectures/03-interrupts/lapic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carzil/mipt-llp-2022/HEAD/lectures/03-interrupts/lapic.png -------------------------------------------------------------------------------- /lectures/07-tasks/task-states.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carzil/mipt-llp-2022/HEAD/lectures/07-tasks/task-states.png -------------------------------------------------------------------------------- /lectures/12-file-systems/main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carzil/mipt-llp-2022/HEAD/lectures/12-file-systems/main.pdf -------------------------------------------------------------------------------- /homeworks/hw1/gdb.script: -------------------------------------------------------------------------------- 1 | target remote :1234 2 | symbol-file kernel.bin 3 | layout regs 4 | focus cmd 5 | b _start 6 | c 7 | -------------------------------------------------------------------------------- /homeworks/hw2/gdb.script: -------------------------------------------------------------------------------- 1 | target remote :1234 2 | symbol-file kernel.sym 3 | layout regs 4 | focus cmd 5 | b _start 6 | c 7 | -------------------------------------------------------------------------------- /homeworks/hw4/arch/x86/irq.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define GATE_INTERRUPT 0b10001110 4 | #define GATE_TRAP 0b10001111 5 | -------------------------------------------------------------------------------- /lectures/01-x86-64-overview/main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carzil/mipt-llp-2022/HEAD/lectures/01-x86-64-overview/main.pdf -------------------------------------------------------------------------------- /lectures/07-tasks/context-switch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carzil/mipt-llp-2022/HEAD/lectures/07-tasks/context-switch.png -------------------------------------------------------------------------------- /lectures/01-x86-64-overview/eflags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carzil/mipt-llp-2022/HEAD/lectures/01-x86-64-overview/eflags.png -------------------------------------------------------------------------------- /lectures/05-memory-management/main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carzil/mipt-llp-2022/HEAD/lectures/05-memory-management/main.pdf -------------------------------------------------------------------------------- /lectures/11-buses-and-drives/main.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carzil/mipt-llp-2022/HEAD/lectures/11-buses-and-drives/main.pdf -------------------------------------------------------------------------------- /lectures/03-interrupts/idt_descriptor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carzil/mipt-llp-2022/HEAD/lectures/03-interrupts/idt_descriptor.png -------------------------------------------------------------------------------- /lectures/01-x86-64-overview/x86_address.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carzil/mipt-llp-2022/HEAD/lectures/01-x86-64-overview/x86_address.png -------------------------------------------------------------------------------- /homeworks/hw3/gdb.script: -------------------------------------------------------------------------------- 1 | target remote :1234 2 | set confirm off 3 | symbol-file kernel.sym 4 | layout src 5 | focus cmd 6 | b vmem_map_page 7 | c 8 | -------------------------------------------------------------------------------- /lectures/01-x86-64-overview/x86_page_tables_and_address.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carzil/mipt-llp-2022/HEAD/lectures/01-x86-64-overview/x86_page_tables_and_address.png -------------------------------------------------------------------------------- /homeworks/hw1/gdt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define GDT_PRESENT (1<<15) 4 | #define GDT_SYSTEM (1<<12) 5 | #define GDT_GRANULARITY (1<<23) 6 | #define GDT_LONG (1<<21) 7 | #define GDT_READ (1<<9) 8 | -------------------------------------------------------------------------------- /homeworks/hw2/gdt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define GDT_PRESENT (1<<15) 4 | #define GDT_SYSTEM (1<<12) 5 | #define GDT_GRANULARITY (1<<23) 6 | #define GDT_LONG (1<<21) 7 | #define GDT_READ (1<<9) 8 | -------------------------------------------------------------------------------- /homeworks/hw3/boot/gdt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define GDT_PRESENT (1<<15) 4 | #define GDT_SYSTEM (1<<12) 5 | #define GDT_GRANULARITY (1<<23) 6 | #define GDT_LONG (1<<21) 7 | #define GDT_READ (1<<9) 8 | -------------------------------------------------------------------------------- /homeworks/hw2/x86.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | 5 | uint64_t x86_read_cr2() { 6 | uint64_t ret; 7 | asm volatile ( 8 | "mov %%cr2, %0" 9 | : "=r"(ret) 10 | ); 11 | return ret; 12 | } 13 | -------------------------------------------------------------------------------- /homeworks/hw3/mm/obj.c: -------------------------------------------------------------------------------- 1 | #include "obj.h" 2 | #include "mm/frame_alloc.h" 3 | 4 | void* object_alloc(obj_alloc_t* alloc) { 5 | return frame_alloc(); 6 | } 7 | 8 | void object_free(obj_alloc_t* alloc, void* obj) { 9 | frame_free(obj); 10 | } 11 | -------------------------------------------------------------------------------- /homeworks/hw2/apic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "acpi.h" 4 | 5 | // apic_init initializes APIC. 6 | void apic_init(); 7 | 8 | // apic_eoi signals end-of-interrupt to the APIC. Must be called before interrupt handler finishes. 9 | void apic_eoi(); 10 | -------------------------------------------------------------------------------- /homeworks/hw1/printk.h: -------------------------------------------------------------------------------- 1 | // Original implementation by @pixelindigo: https://github.com/carzil/KeltOS/blob/master/kernel/printk.c 2 | 3 | #pragma once 4 | #include 5 | 6 | void printk(const char* fmt, ...); 7 | void vprintk(const char* fmt, va_list args); 8 | -------------------------------------------------------------------------------- /homeworks/hw2/printk.h: -------------------------------------------------------------------------------- 1 | // Original implementation by @pixelindigo: https://github.com/carzil/KeltOS/blob/master/kernel/printk.c 2 | 3 | #pragma once 4 | #include 5 | 6 | void printk(const char* fmt, ...); 7 | void vprintk(const char* fmt, va_list args); 8 | -------------------------------------------------------------------------------- /homeworks/hw3/a.py: -------------------------------------------------------------------------------- 1 | addr = int(input("addr> "), 16) 2 | print("addr:", hex(addr)) 3 | print("pml4 idx:", (addr >> 39) & 0x1ff) 4 | print("pdpe idx:", (addr >> 30) & 0x1ff) 5 | print("pde idx:", (addr >> 21) & 0x1ff) 6 | print("pte idx:", (addr >> 12) & 0x1ff) 7 | 8 | -------------------------------------------------------------------------------- /homeworks/hw3/boot/early.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | #include "kernel/multiboot.h" 5 | 6 | typedef struct early_data { 7 | // Contains virtual address of multiboot info. 8 | struct multiboot_info* multiboot_info; 9 | } early_data_t; 10 | -------------------------------------------------------------------------------- /homeworks/hw3/drivers/apic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "acpi.h" 4 | 5 | // apic_init initializes APIC. 6 | void apic_init(); 7 | 8 | // apic_eoi signals end-of-interrupt to the APIC. Must be called before interrupt handler finishes. 9 | void apic_eoi(); 10 | -------------------------------------------------------------------------------- /homeworks/hw3/kernel/printk.h: -------------------------------------------------------------------------------- 1 | // Original implementation by @pixelindigo: https://github.com/carzil/KeltOS/blob/master/kernel/printk.c 2 | 3 | #pragma once 4 | #include 5 | 6 | void printk(const char* fmt, ...); 7 | void vprintk(const char* fmt, va_list args); 8 | -------------------------------------------------------------------------------- /homeworks/hw4/boot/early.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | #include "kernel/multiboot.h" 5 | 6 | typedef struct early_data { 7 | // Contains virtual address of multiboot info. 8 | struct multiboot_info* multiboot_info; 9 | } early_data_t; 10 | -------------------------------------------------------------------------------- /homeworks/hw4/drivers/apic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "acpi.h" 4 | 5 | // apic_init initializes APIC. 6 | void apic_init(); 7 | 8 | // apic_eoi signals end-of-interrupt to the APIC. Must be called before interrupt handler finishes. 9 | void apic_eoi(); 10 | -------------------------------------------------------------------------------- /homeworks/hw4/kernel/printk.h: -------------------------------------------------------------------------------- 1 | // Original implementation by @pixelindigo: https://github.com/carzil/KeltOS/blob/master/kernel/printk.c 2 | 3 | #pragma once 4 | #include 5 | 6 | void printk(const char* fmt, ...); 7 | void vprintk(const char* fmt, va_list args); 8 | -------------------------------------------------------------------------------- /homeworks/hw4/linker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Those constants are defined by linker script. 4 | 5 | extern int _phys_start_kernel_sections; 6 | extern int _phys_end_kernel_sections; 7 | extern int _phys_start_user; 8 | extern int _phys_end_user; 9 | extern int _phys_start_hh; 10 | -------------------------------------------------------------------------------- /homeworks/hw4/kernel/syscall.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "arch/x86/arch.h" 5 | 6 | enum { 7 | SYS_SLEEP = 0, 8 | SYS_FORK = 1, 9 | SYS_GETPID = 2, 10 | SYS_EXIT = 3, 11 | SYS_MAX, 12 | }; 13 | 14 | typedef int64_t (*syscall_fn_t)(arch_regs_t*); 15 | -------------------------------------------------------------------------------- /homeworks/hw4/mm/obj.c: -------------------------------------------------------------------------------- 1 | #include "obj.h" 2 | #include "common.h" 3 | #include "mm/frame_alloc.h" 4 | 5 | void* object_alloc(obj_alloc_t* alloc) { 6 | UNUSED(alloc); 7 | return frame_alloc(); 8 | } 9 | 10 | void object_free(obj_alloc_t* alloc, void* obj) { 11 | UNUSED(alloc); 12 | frame_free(obj); 13 | } 14 | -------------------------------------------------------------------------------- /homeworks/hw1/panic.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "panic.h" 4 | 5 | 6 | void __panic(const char* location, const char* msg, ...) { 7 | // TODO: panic function should print all arguments passed via variadic parameters and then enter inifinite non-busy loop. 8 | // Example of a such loop could be found in boot.S. 9 | } 10 | -------------------------------------------------------------------------------- /homeworks/hw4/arch/x86/syscall.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define syscall_arg0(regs) ((regs)->rdi) 6 | #define syscall_arg1(regs) ((regs)->rsi) 7 | #define syscall_arg2(regs) ((regs)->rdx) 8 | #define syscall_arg3(regs) ((regs)->r8) 9 | #define syscall_arg4(regs) ((regs)->r9) 10 | #define syscall_arg5(regs) ((regs)->r10) 11 | -------------------------------------------------------------------------------- /homeworks/hw1/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef unsigned char u8; 7 | typedef unsigned short u16; 8 | typedef unsigned int u32; 9 | typedef unsigned long long u64; 10 | 11 | typedef signed char s8; 12 | typedef signed short s16; 13 | typedef signed int s32; 14 | typedef signed long long s64; 15 | -------------------------------------------------------------------------------- /homeworks/hw2/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef unsigned char u8; 7 | typedef unsigned short u16; 8 | typedef unsigned int u32; 9 | typedef unsigned long long u64; 10 | 11 | typedef signed char s8; 12 | typedef signed short s16; 13 | typedef signed int s32; 14 | typedef signed long long s64; 15 | -------------------------------------------------------------------------------- /homeworks/hw3/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef unsigned char u8; 7 | typedef unsigned short u16; 8 | typedef unsigned int u32; 9 | typedef unsigned long long u64; 10 | 11 | typedef signed char s8; 12 | typedef signed short s16; 13 | typedef signed int s32; 14 | typedef signed long long s64; 15 | -------------------------------------------------------------------------------- /homeworks/hw4/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef unsigned char u8; 7 | typedef unsigned short u16; 8 | typedef unsigned int u32; 9 | typedef unsigned long long u64; 10 | 11 | typedef signed char s8; 12 | typedef signed short s16; 13 | typedef signed int s32; 14 | typedef signed long long s64; 15 | -------------------------------------------------------------------------------- /homeworks/hw2/panic.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "panic.h" 4 | 5 | 6 | void __panic(const char* location, const char* msg, ...) { 7 | printk("panic at %s: ", location); 8 | 9 | va_list args; 10 | va_start(args, msg); 11 | vprintk(msg, args); 12 | va_end(args); 13 | 14 | printk(".\n"); 15 | 16 | for (;;) { 17 | asm volatile ("hlt"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /homeworks/hw4/arch/x86/context_switch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct { 6 | uint64_t rsp; 7 | } context_t; 8 | 9 | typedef struct { 10 | uint64_t r15; 11 | uint64_t r14; 12 | uint64_t r13; 13 | uint64_t r12; 14 | uint64_t rbp; 15 | uint64_t rbx; 16 | uint64_t ret_addr; 17 | } on_stack_context_t; 18 | 19 | void context_switch(context_t* curr, context_t* next); 20 | -------------------------------------------------------------------------------- /homeworks/hw1/panic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "printk.h" 6 | 7 | 8 | #define S1(x) #x 9 | #define S2(x) S1(x) 10 | 11 | void __panic(const char* location, const char* msg, ...); 12 | 13 | #define panic(msg, ...) __panic(__FILE__ ":" S2(__LINE__), msg __VA_OPT__(,) __VA_ARGS__) 14 | 15 | #define BUG_ON(expr) if (expr) { panic("bug: '" #expr "'"); } 16 | #define BUG_ON_NULL(expr) BUG_ON(expr == NULL) 17 | -------------------------------------------------------------------------------- /homeworks/hw2/panic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "printk.h" 6 | 7 | 8 | #define S1(x) #x 9 | #define S2(x) S1(x) 10 | 11 | void __panic(const char* location, const char* msg, ...); 12 | 13 | #define panic(msg, ...) __panic(__FILE__ ":" S2(__LINE__), msg __VA_OPT__(,) __VA_ARGS__) 14 | 15 | #define BUG_ON(expr) if (expr) { panic("bug: '" #expr "'"); } 16 | #define BUG_ON_NULL(expr) BUG_ON(expr == NULL) 17 | -------------------------------------------------------------------------------- /homeworks/hw3/kernel/panic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "printk.h" 6 | 7 | 8 | #define S1(x) #x 9 | #define S2(x) S1(x) 10 | 11 | void __panic(const char* location, const char* msg, ...); 12 | 13 | #define panic(msg, ...) __panic(__FILE__ ":" S2(__LINE__), msg __VA_OPT__(,) __VA_ARGS__) 14 | 15 | #define BUG_ON(expr) if (expr) { panic("bug: '" #expr "'"); } 16 | #define BUG_ON_NULL(expr) BUG_ON(expr == NULL) 17 | -------------------------------------------------------------------------------- /homeworks/hw3/kernel/panic.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "kernel/irq.h" 4 | #include "panic.h" 5 | #include "arch/x86.h" 6 | 7 | 8 | void __panic(const char* location, const char* msg, ...) { 9 | irq_disable(); 10 | 11 | printk("panic at %s: ", location); 12 | 13 | va_list args; 14 | va_start(args, msg); 15 | vprintk(msg, args); 16 | va_end(args); 17 | 18 | printk(".\n"); 19 | 20 | x86_hlt_forever(); 21 | } 22 | -------------------------------------------------------------------------------- /homeworks/hw1/multiboot.c: -------------------------------------------------------------------------------- 1 | #include "multiboot.h" 2 | #include "printk.h" 3 | #include "types.h" 4 | #include "panic.h" 5 | 6 | 7 | void multiboot_init() { 8 | // TODO: implement me 9 | } 10 | 11 | void multiboot_mmap_iter_init(struct multiboot_mmap_iter* it) { 12 | // TODO: implement me 13 | } 14 | 15 | struct multiboot_mmap_entry* multiboot_mmap_iter_next(struct multiboot_mmap_iter* it) { 16 | // TODO: implement me 17 | return NULL; 18 | } 19 | -------------------------------------------------------------------------------- /homeworks/hw4/kernel/panic.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "kernel/irq.h" 4 | #include "panic.h" 5 | #include "arch/x86/x86.h" 6 | 7 | 8 | _Noreturn void __panic(const char* location, const char* msg, ...) { 9 | irq_disable(); 10 | 11 | printk("panic at %s: ", location); 12 | 13 | va_list args; 14 | va_start(args, msg); 15 | vprintk(msg, args); 16 | va_end(args); 17 | 18 | printk(".\n"); 19 | 20 | x86_hlt_forever(); 21 | } 22 | -------------------------------------------------------------------------------- /homeworks/hw3/build/Makefile.inc: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | C_SOURCES := $(wildcard *.c) 4 | ASM_SOURCES := $(wildcard *.S) 5 | C_OBJS := $(C_SOURCES:.c=.c.o) 6 | ASM_OBJS := $(ASM_SOURCES:.S=.S.o) 7 | 8 | build: $(C_OBJS) $(ASM_OBJS) 9 | 10 | %.c.o: %.c 11 | $(CC) $(CCFLAGS) -c $< -o $@ 12 | 13 | %.S.o: %.S 14 | $(AS) $(ASFLAGS) -c $< -o $@ 15 | 16 | clean: 17 | rm -f *.o 18 | rm -f kernel.bin 19 | rm -f kernel.sym 20 | rm -f kernel.iso 21 | 22 | .PHONY: build clean 23 | -------------------------------------------------------------------------------- /homeworks/hw4/build/Makefile.inc: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | C_SOURCES := $(wildcard *.c) 4 | ASM_SOURCES := $(wildcard *.S) 5 | C_OBJS := $(C_SOURCES:.c=.c.o) 6 | ASM_OBJS := $(ASM_SOURCES:.S=.S.o) 7 | 8 | build: $(C_OBJS) $(ASM_OBJS) 9 | 10 | %.c.o: %.c 11 | $(CC) $(CCFLAGS) -c $< -o $@ 12 | 13 | %.S.o: %.S 14 | $(AS) $(ASFLAGS) -c $< -o $@ 15 | 16 | clean: 17 | rm -f *.o 18 | rm -f kernel.bin 19 | rm -f kernel.sym 20 | rm -f kernel.iso 21 | 22 | .PHONY: build clean 23 | -------------------------------------------------------------------------------- /homeworks/hw3/defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PAGE_SIZE 4096 4 | 5 | #define CACHE_LINE_SIZE_BYTES 64 6 | 7 | #define GB (1 << 30) 8 | #define MB (1 << 20) 9 | #define KB (1 << 10) 10 | 11 | #define KERNEL_HIGHER_HALF_START 0xffff800000000000 12 | 13 | #define KERNEL_SECTIONS_START 0xffffffff80000000 14 | #define KERNEL_SECTIONS_SIZE 2 * (1ull << 30) 15 | 16 | #define KERNEL_DIRECT_PHYS_MAPPING_START 0xffff888000000000 17 | #define KERNEL_DIRECT_PHYS_MAPPING_SIZE 64 * (1ull << 40) 18 | -------------------------------------------------------------------------------- /homeworks/hw4/defs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PAGE_SIZE 4096 4 | 5 | #define CACHE_LINE_SIZE_BYTES 64 6 | 7 | #define GB (1 << 30) 8 | #define MB (1 << 20) 9 | #define KB (1 << 10) 10 | 11 | #define KERNEL_HIGHER_HALF_START 0xffff800000000000 12 | 13 | #define KERNEL_SECTIONS_START 0xffffffff80000000 14 | #define KERNEL_SECTIONS_SIZE 2 * (1ull << 30) 15 | 16 | #define KERNEL_DIRECT_PHYS_MAPPING_START 0xffff888000000000 17 | #define KERNEL_DIRECT_PHYS_MAPPING_SIZE 64 * (1ull << 40) 18 | -------------------------------------------------------------------------------- /homeworks/hw4/arch/x86/context_switch.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | 3 | // rdi holds pointer to current context 4 | // rsi holds pointer to next context 5 | .global context_switch 6 | .type context_switch, @function 7 | context_switch: 8 | push rbx 9 | push rbp 10 | push r12 11 | push r13 12 | push r14 13 | push r15 14 | 15 | mov qword ptr [rdi], rsp 16 | mov rsp, qword ptr [rsi] 17 | 18 | pop r15 19 | pop r14 20 | pop r13 21 | pop r12 22 | pop rbp 23 | pop rbx 24 | ret 25 | -------------------------------------------------------------------------------- /homeworks/hw4/boot/multiboot.S: -------------------------------------------------------------------------------- 1 | #include "mm/paging.h" 2 | 3 | #define MULTIBOOT_MAGIC 0xE85250D6 4 | #define MULTIBOOT_ARCH_32 0 5 | 6 | .intel_syntax noprefix 7 | 8 | # .multiboot section defines Multiboot 2 compatible header. 9 | .section .multiboot,"a" 10 | .long MULTIBOOT_MAGIC 11 | .long MULTIBOOT_ARCH_32 12 | .long HEADER_LEN 13 | .long -(MULTIBOOT_MAGIC + MULTIBOOT_ARCH_32 + HEADER_LEN) 14 | 15 | // Tag end. 16 | .word 0 17 | .word 0 18 | .long 8 19 | 20 | .set HEADER_LEN, . - .multiboot 21 | -------------------------------------------------------------------------------- /homeworks/hw3/boot/multiboot.S: -------------------------------------------------------------------------------- 1 | #include "gdt.h" 2 | #include "mm/paging.h" 3 | 4 | #define MULTIBOOT_MAGIC 0xE85250D6 5 | #define MULTIBOOT_ARCH_32 0 6 | 7 | .intel_syntax noprefix 8 | 9 | # .multiboot section defines Multiboot 2 compatible header. 10 | .section .multiboot,"a" 11 | .long MULTIBOOT_MAGIC 12 | .long MULTIBOOT_ARCH_32 13 | .long HEADER_LEN 14 | .long -(MULTIBOOT_MAGIC + MULTIBOOT_ARCH_32 + HEADER_LEN) 15 | 16 | // Tag end. 17 | .word 0 18 | .word 0 19 | .long 8 20 | 21 | .set HEADER_LEN, . - .multiboot 22 | -------------------------------------------------------------------------------- /homeworks/hw4/arch/x86/gdt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define GDT_PRESENT (1<<15) 4 | #define GDT_SYSTEM (1<<12) 5 | #define GDT_GRANULARITY (1<<23) 6 | #define GDT_LONG (1<<21) 7 | #define GDT_READ (1<<9) 8 | #define GDT_DPL_RING3 (3<<13) 9 | #define GDT_CODE_SEG (1<<11) 10 | 11 | #define KERNEL_CODE_SEG 1 12 | #define KERNEL_DATA_SEG 2 13 | #define USER_DATA_SEG 3 14 | #define USER_CODE_SEG 4 15 | #define TSS_SEG 5 16 | 17 | #define RPL_RING3 3 18 | #define RPL_RING0 0 19 | 20 | #define GDT_SEGMENT_SELECTOR(idx, rpl) (((idx) << 3) | (rpl)) 21 | -------------------------------------------------------------------------------- /homeworks/hw1/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | 3 | SECTIONS 4 | { 5 | . = 2M; 6 | 7 | .multiboot : ALIGN(4K) { 8 | KEEP(*(.multiboot)) 9 | } 10 | 11 | .boot.text : { 12 | *(.boot.text) 13 | } 14 | 15 | .boot.bss : { 16 | *(.boot.bss) 17 | } 18 | 19 | .text BLOCK(4K) : ALIGN(4K) 20 | { 21 | *(.text) 22 | } 23 | 24 | .rodata BLOCK(4K) : ALIGN(4K) 25 | { 26 | *(.rodata) 27 | } 28 | 29 | .data BLOCK(4K) : ALIGN(4K) 30 | { 31 | *(.data) 32 | } 33 | 34 | .bss BLOCK(4K) : ALIGN(4K) 35 | { 36 | *(COMMON) 37 | *(.bss) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /homeworks/hw2/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_start) 2 | 3 | SECTIONS 4 | { 5 | . = 2M; 6 | 7 | .multiboot : ALIGN(4K) { 8 | KEEP(*(.multiboot)) 9 | } 10 | 11 | .boot.text : { 12 | *(.boot.text) 13 | } 14 | 15 | .boot.bss : { 16 | *(.boot.bss) 17 | } 18 | 19 | .text BLOCK(4K) : ALIGN(4K) 20 | { 21 | *(.text) 22 | } 23 | 24 | .rodata BLOCK(4K) : ALIGN(4K) 25 | { 26 | *(.rodata) 27 | } 28 | 29 | .data BLOCK(4K) : ALIGN(4K) 30 | { 31 | *(.data) 32 | } 33 | 34 | .bss BLOCK(4K) : ALIGN(4K) 35 | { 36 | *(COMMON) 37 | *(.bss) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /homeworks/hw3/mm/frame_alloc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // frames_alloc allocates continuous memory region contains at least n pages. 6 | void* frames_alloc(size_t n); 7 | 8 | // frame_alloc allocates single frame. 9 | void* frame_alloc(); 10 | 11 | // frames_free frees n frames at given base address. 12 | void frames_free(void* addr, size_t n); 13 | 14 | // frame_free frees frame at given address. 15 | void frame_free(void* addr); 16 | 17 | // frame_alloc_init initializes frame allocator. Must be called after direct physical memory mapping is created. 18 | void frame_alloc_init(); 19 | -------------------------------------------------------------------------------- /homeworks/hw4/mm/frame_alloc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // frames_alloc allocates continuous memory region contains at least n pages. 6 | void* frames_alloc(size_t n); 7 | 8 | // frame_alloc allocates single frame. 9 | void* frame_alloc(); 10 | 11 | // frames_free frees n frames at given base address. 12 | void frames_free(void* addr, size_t n); 13 | 14 | // frame_free frees frame at given address. 15 | void frame_free(void* addr); 16 | 17 | // frame_alloc_init initializes frame allocator. Must be called after direct physical memory mapping is created. 18 | void frame_alloc_init(); 19 | -------------------------------------------------------------------------------- /homeworks/hw3/mm/obj.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "common.h" 6 | #include "defs.h" 7 | 8 | typedef struct obj_alloc { 9 | // TODO: fill me. 10 | } obj_alloc_t; 11 | 12 | #define OBJ_ALLOC_DEFINE(var, type) obj_alloc_t var = { /* initialize fields here, hint: use sizeof(type) */ } 13 | #define OBJ_ALLOC_DECLARE(var) obj_alloc_t var 14 | 15 | // object_alloc allocates single object and returns its virtual address. 16 | void* object_alloc(obj_alloc_t* alloc); 17 | 18 | // object_free frees obj associated with given allocator. 19 | void object_free(obj_alloc_t* alloc, void* obj); 20 | -------------------------------------------------------------------------------- /homeworks/hw4/mm/obj.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "common.h" 6 | #include "defs.h" 7 | 8 | typedef struct obj_alloc { 9 | // TODO: fill me. 10 | } obj_alloc_t; 11 | 12 | #define OBJ_ALLOC_DEFINE(var, type) obj_alloc_t var = { /* initialize fields here, hint: use sizeof(type) */ } 13 | #define OBJ_ALLOC_DECLARE(var) obj_alloc_t var 14 | 15 | // object_alloc allocates single object and returns its virtual address. 16 | void* object_alloc(obj_alloc_t* alloc); 17 | 18 | // object_free frees obj associated with given allocator. 19 | void object_free(obj_alloc_t* alloc, void* obj); 20 | -------------------------------------------------------------------------------- /homeworks/hw2/irq.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define GATE_INTERRUPT 0b10001110 4 | #define GATE_TRAP 0b10001111 5 | 6 | #ifndef __ASSEMBLER__ 7 | 8 | #include "types.h" 9 | 10 | void irq_init(); 11 | 12 | static inline void irq_disable() { 13 | asm volatile ("cli"); 14 | } 15 | 16 | static inline void irq_enable() { 17 | asm volatile ("sti"); 18 | } 19 | 20 | // irqctx_errcode represent interrupt handler frame. 21 | struct irqctx { 22 | // TODO: fill me. 23 | }; 24 | 25 | // irqctx_errcode represent interrupt handler frame with error code. 26 | struct irqctx_errcode { 27 | // TODO: fill me. 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /homeworks/hw3/mm/vmem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "paging.h" 4 | #include "arch/x86.h" 5 | 6 | // vmem_t is used to manage an address space, including all its page tables. 7 | typedef struct vmem { 8 | pml4_t* pml4; 9 | } vmem_t; 10 | 11 | // vmem_init_from_cr3 initializes vmem from current address space. 12 | void vmem_init_from_current(vmem_t* vm); 13 | 14 | // vmem_switch_to switches to the specified address space. 15 | void vmem_switch_to(vmem_t* vm); 16 | 17 | // vmem_alloc_pages allocates pgcnt frames and then maps it to the virtual address virt_addr. 18 | void vmem_alloc_pages(vmem_t* vm, void* virt_addr, size_t pgcnt); 19 | -------------------------------------------------------------------------------- /homeworks/hw3/kernel/irq.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define GATE_INTERRUPT 0b10001110 4 | #define GATE_TRAP 0b10001111 5 | 6 | #ifndef __ASSEMBLER__ 7 | 8 | #include "types.h" 9 | 10 | void irq_init(); 11 | 12 | static inline void irq_disable() { 13 | __asm__ volatile ("cli"); 14 | } 15 | 16 | static inline void irq_enable() { 17 | __asm__ volatile ("sti"); 18 | } 19 | 20 | // irqctx_errcode represent interrupt handler frame. 21 | struct irqctx { 22 | // TODO: fill me. 23 | }; 24 | 25 | // irqctx_errcode represent interrupt handler frame with error code. 26 | struct irqctx_errcode { 27 | // TODO: fill me. 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /homeworks/hw4/kernel/panic.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "printk.h" 6 | 7 | 8 | #define S1(x) #x 9 | #define S2(x) S1(x) 10 | 11 | _Noreturn void __panic(const char* location, const char* msg, ...); 12 | 13 | #define panic(msg, ...) __panic(__FILE__ ":" S2(__LINE__), msg __VA_OPT__(,) __VA_ARGS__) 14 | 15 | #define BUG_ON(expr) if (expr) { panic("bug: '" #expr "'"); } 16 | #define BUG_ON_NULL(expr) BUG_ON(expr == NULL) 17 | #define BUG_ON_ERROR(expr) { \ 18 | int __err = expr; \ 19 | if (__err < 0) { \ 20 | panic("bug: " #expr " == %d", __err); \ 21 | } \ 22 | } 23 | #define BUG_ON_REACH() panic("unreachable code executed") 24 | 25 | -------------------------------------------------------------------------------- /homeworks/hw4/arch/x86/regs_asm.h: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | 3 | .macro PUSH_REGS 4 | push rax 5 | push rbx 6 | push rcx 7 | push rdx 8 | push rsi 9 | push rdi 10 | push rbp 11 | push r8 12 | push r9 13 | push r10 14 | push r11 15 | push r12 16 | push r13 17 | push r14 18 | push r15 19 | .endm 20 | 21 | .macro POP_REGS pop_rax=1 22 | pop r15 23 | pop r14 24 | pop r13 25 | pop r12 26 | pop r11 27 | pop r10 28 | pop r9 29 | pop r8 30 | pop rbp 31 | pop rdi 32 | pop rsi 33 | pop rdx 34 | pop rcx 35 | pop rbx 36 | .if \pop_rax 37 | pop rax 38 | .else 39 | add rsp, 8 40 | .endif 41 | .endm 42 | -------------------------------------------------------------------------------- /homeworks/hw4/sched/sched.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "arch/x86/arch.h" 6 | #include "mm/vmem.h" 7 | 8 | #define MAX_TASK_COUNT (1 << 16) 9 | 10 | typedef enum state { 11 | TASK_NOT_ALLOCATED = 0, 12 | TASK_RUNNABLE = 1, 13 | TASK_WAITING = 2, 14 | TASK_ZOMBIE = 3, 15 | } state_t; 16 | 17 | typedef struct task { 18 | // arch_thread_t must be the first member. 19 | arch_thread_t arch_thread; 20 | size_t pid; 21 | state_t state; 22 | uint64_t flags; 23 | int ticks; 24 | vmem_t vmem; 25 | int exitcode; 26 | } task_t; 27 | 28 | void sched_start(); 29 | void sched_switch(); 30 | void sched_timer_tick(); 31 | 32 | extern task_t* _current; 33 | #define sched_current() _current 34 | -------------------------------------------------------------------------------- /homeworks/hw2/acpi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct acpi_sdt_header { 7 | char signature[4]; 8 | uint32_t length; 9 | uint8_t revision; 10 | uint8_t checksum; 11 | char oemid[6]; 12 | char oem_tableid[8]; 13 | uint32_t oem_revision; 14 | uint32_t creator_id; 15 | uint32_t creator_revision; 16 | } __attribute__((packed)); 17 | 18 | struct acpi_sdt { 19 | struct acpi_sdt_header header; 20 | 21 | union { 22 | uint32_t entries[0]; 23 | char data[0]; 24 | }; 25 | 26 | } __attribute__((packed)); 27 | 28 | void acpi_init(); 29 | struct acpi_sdt* acpi_lookup_rsdt(const char* signature); 30 | struct acpi_sdt* acpi_lookup_sdt(struct acpi_sdt* root, const char* signature); 31 | -------------------------------------------------------------------------------- /homeworks/hw3/drivers/acpi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct acpi_sdt_header { 7 | char signature[4]; 8 | uint32_t length; 9 | uint8_t revision; 10 | uint8_t checksum; 11 | char oemid[6]; 12 | char oem_tableid[8]; 13 | uint32_t oem_revision; 14 | uint32_t creator_id; 15 | uint32_t creator_revision; 16 | } __attribute__((packed)); 17 | 18 | struct acpi_sdt { 19 | struct acpi_sdt_header header; 20 | 21 | union { 22 | uint32_t entries[0]; 23 | char data[0]; 24 | }; 25 | 26 | } __attribute__((packed)); 27 | 28 | void acpi_init(); 29 | struct acpi_sdt* acpi_lookup_rsdt(const char* signature); 30 | struct acpi_sdt* acpi_lookup_sdt(struct acpi_sdt* root, const char* signature); 31 | -------------------------------------------------------------------------------- /homeworks/hw4/drivers/acpi.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct acpi_sdt_header { 7 | char signature[4]; 8 | uint32_t length; 9 | uint8_t revision; 10 | uint8_t checksum; 11 | char oemid[6]; 12 | char oem_tableid[8]; 13 | uint32_t oem_revision; 14 | uint32_t creator_id; 15 | uint32_t creator_revision; 16 | } __attribute__((packed)); 17 | 18 | struct acpi_sdt { 19 | struct acpi_sdt_header header; 20 | 21 | union { 22 | uint32_t entries[0]; 23 | char data[0]; 24 | }; 25 | 26 | } __attribute__((packed)); 27 | 28 | void acpi_init(); 29 | struct acpi_sdt* acpi_lookup_rsdt(const char* signature); 30 | struct acpi_sdt* acpi_lookup_sdt(struct acpi_sdt* root, const char* signature); 31 | -------------------------------------------------------------------------------- /homeworks/hw3/arch/x86.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | 5 | static inline uint64_t x86_read_cr2() { 6 | uint64_t ret; 7 | __asm__ volatile ( 8 | "mov %%cr2, %0" 9 | : "=r"(ret) 10 | ); 11 | return ret; 12 | } 13 | 14 | static inline uint64_t x86_read_cr3() { 15 | uint64_t ret; 16 | __asm__ volatile ( 17 | "mov %%cr3, %0" 18 | : "=r"(ret) 19 | ); 20 | return ret; 21 | } 22 | 23 | static inline void x86_write_cr3(uint64_t x) { 24 | __asm__ volatile ( 25 | "mov %0, %%cr3" 26 | : : "r"(x) 27 | ); 28 | } 29 | 30 | static inline void x86_hlt_forever() { 31 | for (;;) { 32 | __asm__ volatile ("hlt"); 33 | } 34 | } 35 | 36 | static inline void x86_outb(uint16_t port, uint8_t data) { 37 | __asm__ volatile("out %0,%1" : : "a" (data), "d" (port)); 38 | } 39 | -------------------------------------------------------------------------------- /homeworks/hw4/kernel/syscall.c: -------------------------------------------------------------------------------- 1 | #include "syscall.h" 2 | #include "kernel/printk.h" 3 | #include "kernel/errno.h" 4 | #include "arch/x86/arch.h" 5 | #include "sched/sched.h" 6 | #include "common.h" 7 | 8 | int64_t sys_sleep(arch_regs_t* regs) { 9 | uint64_t ms = syscall_arg0(regs); 10 | // TODO: implement me. 11 | 12 | return 0; 13 | } 14 | 15 | int64_t sys_fork(arch_regs_t* regs); 16 | int64_t sys_getpid(arch_regs_t* regs); 17 | int64_t sys_exit(arch_regs_t* regs); 18 | 19 | syscall_fn_t syscall_table[] = { 20 | [SYS_SLEEP] = sys_sleep, 21 | [SYS_FORK] = sys_fork, 22 | [SYS_GETPID] = sys_getpid, 23 | [SYS_EXIT] = sys_exit, 24 | }; 25 | 26 | uint64_t do_syscall(uint64_t sysno, arch_regs_t* regs) { 27 | if (sysno >= SYS_MAX) { 28 | return -ENOSYS; 29 | } 30 | syscall_fn_t syscall = syscall_table[sysno]; 31 | return syscall(regs); 32 | } 33 | -------------------------------------------------------------------------------- /homeworks/hw1/Makefile: -------------------------------------------------------------------------------- 1 | AS=x86_64-elf-gcc 2 | CC=x86_64-elf-gcc 3 | LD=x86_64-elf-gcc 4 | AS_FLAGS=-c -g 5 | CC_FLAGS=-mno-mmx -mno-sse -mno-sse2 -fno-pie -c -g -mno-red-zone -std=gnu99 -ffreestanding -Wall -Wextra 6 | LD_FLAGS= 7 | OBJCOPY=objcopy 8 | GRUB_MKRESCUE=grub-mkrescue 9 | 10 | image: build 11 | mkdir -p isodir/boot/grub 12 | cp grub.cfg isodir/boot/grub 13 | cp kernel.bin isodir/boot 14 | $(GRUB_MKRESCUE) -o kernel.iso isodir 15 | rm -rf isodir 16 | 17 | build: 18 | $(AS) $(AS_FLAGS) boot.S -o boot.o 19 | $(CC) $(CC_FLAGS) vga.c -o vga.o 20 | $(CC) $(CC_FLAGS) kernel.c -o kernel.o 21 | $(CC) $(CC_FLAGS) multiboot.c -o multiboot.o 22 | $(CC) $(CC_FLAGS) printk.c -o printk.o 23 | $(CC) $(CC_FLAGS) panic.c -o panic.o 24 | $(LD) $(LD_FLAGS) -T linker.ld -ffreestanding -O2 -nostdlib boot.o vga.o printk.o panic.o multiboot.o kernel.o -o kernel.bin 25 | 26 | clean: 27 | rm -f *.o 28 | rm -f kernel.iso 29 | rm -f kernel.bin 30 | 31 | .PHONY: build clean 32 | -------------------------------------------------------------------------------- /homeworks/hw1/vga.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "types.h" 5 | 6 | enum vga_color { 7 | VGA_COLOR_BLACK = 0, 8 | VGA_COLOR_BLUE = 1, 9 | VGA_COLOR_GREEN = 2, 10 | VGA_COLOR_CYAN = 3, 11 | VGA_COLOR_RED = 4, 12 | VGA_COLOR_MAGENTA = 5, 13 | VGA_COLOR_BROWN = 6, 14 | VGA_COLOR_LIGHT_GREY = 7, 15 | VGA_COLOR_DARK_GREY = 8, 16 | VGA_COLOR_LIGHT_BLUE = 9, 17 | VGA_COLOR_LIGHT_GREEN = 10, 18 | VGA_COLOR_LIGHT_CYAN = 11, 19 | VGA_COLOR_LIGHT_RED = 12, 20 | VGA_COLOR_LIGHT_MAGENTA = 13, 21 | VGA_COLOR_LIGHT_BROWN = 14, 22 | VGA_COLOR_WHITE = 15, 23 | }; 24 | 25 | static inline u8 vga_entry_color(enum vga_color fg, enum vga_color bg) { 26 | return fg | bg << 4; 27 | } 28 | 29 | static inline u16 vga_entry(unsigned char uc, u8 color) { 30 | return (u16) uc | (u16) color << 8; 31 | } 32 | 33 | void vga_init(); 34 | void vga_write(const char* data, size_t size, u8 color); 35 | void vga_writestring(const char* data); 36 | void vga_writestring_color(const char* data, u8 color); 37 | -------------------------------------------------------------------------------- /homeworks/hw2/vga.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "types.h" 5 | 6 | enum vga_color { 7 | VGA_COLOR_BLACK = 0, 8 | VGA_COLOR_BLUE = 1, 9 | VGA_COLOR_GREEN = 2, 10 | VGA_COLOR_CYAN = 3, 11 | VGA_COLOR_RED = 4, 12 | VGA_COLOR_MAGENTA = 5, 13 | VGA_COLOR_BROWN = 6, 14 | VGA_COLOR_LIGHT_GREY = 7, 15 | VGA_COLOR_DARK_GREY = 8, 16 | VGA_COLOR_LIGHT_BLUE = 9, 17 | VGA_COLOR_LIGHT_GREEN = 10, 18 | VGA_COLOR_LIGHT_CYAN = 11, 19 | VGA_COLOR_LIGHT_RED = 12, 20 | VGA_COLOR_LIGHT_MAGENTA = 13, 21 | VGA_COLOR_LIGHT_BROWN = 14, 22 | VGA_COLOR_WHITE = 15, 23 | }; 24 | 25 | static inline u8 vga_entry_color(enum vga_color fg, enum vga_color bg) { 26 | return fg | bg << 4; 27 | } 28 | 29 | static inline u16 vga_entry(unsigned char uc, u8 color) { 30 | return (u16) uc | (u16) color << 8; 31 | } 32 | 33 | void vga_init(); 34 | void vga_write(const char* data, size_t size, u8 color); 35 | void vga_writestring(const char* data); 36 | void vga_writestring_color(const char* data, u8 color); 37 | -------------------------------------------------------------------------------- /homeworks/hw3/drivers/vga.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "types.h" 5 | 6 | enum vga_color { 7 | VGA_COLOR_BLACK = 0, 8 | VGA_COLOR_BLUE = 1, 9 | VGA_COLOR_GREEN = 2, 10 | VGA_COLOR_CYAN = 3, 11 | VGA_COLOR_RED = 4, 12 | VGA_COLOR_MAGENTA = 5, 13 | VGA_COLOR_BROWN = 6, 14 | VGA_COLOR_LIGHT_GREY = 7, 15 | VGA_COLOR_DARK_GREY = 8, 16 | VGA_COLOR_LIGHT_BLUE = 9, 17 | VGA_COLOR_LIGHT_GREEN = 10, 18 | VGA_COLOR_LIGHT_CYAN = 11, 19 | VGA_COLOR_LIGHT_RED = 12, 20 | VGA_COLOR_LIGHT_MAGENTA = 13, 21 | VGA_COLOR_LIGHT_BROWN = 14, 22 | VGA_COLOR_WHITE = 15, 23 | }; 24 | 25 | static inline u8 vga_entry_color(enum vga_color fg, enum vga_color bg) { 26 | return fg | bg << 4; 27 | } 28 | 29 | static inline u16 vga_entry(unsigned char uc, u8 color) { 30 | return (u16) uc | (u16) color << 8; 31 | } 32 | 33 | void vga_init(); 34 | void vga_write(const char* data, size_t size, u8 color); 35 | void vga_writestring(const char* data); 36 | void vga_writestring_color(const char* data, u8 color); 37 | -------------------------------------------------------------------------------- /homeworks/hw4/drivers/vga.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "types.h" 5 | 6 | enum vga_color { 7 | VGA_COLOR_BLACK = 0, 8 | VGA_COLOR_BLUE = 1, 9 | VGA_COLOR_GREEN = 2, 10 | VGA_COLOR_CYAN = 3, 11 | VGA_COLOR_RED = 4, 12 | VGA_COLOR_MAGENTA = 5, 13 | VGA_COLOR_BROWN = 6, 14 | VGA_COLOR_LIGHT_GREY = 7, 15 | VGA_COLOR_DARK_GREY = 8, 16 | VGA_COLOR_LIGHT_BLUE = 9, 17 | VGA_COLOR_LIGHT_GREEN = 10, 18 | VGA_COLOR_LIGHT_CYAN = 11, 19 | VGA_COLOR_LIGHT_RED = 12, 20 | VGA_COLOR_LIGHT_MAGENTA = 13, 21 | VGA_COLOR_LIGHT_BROWN = 14, 22 | VGA_COLOR_WHITE = 15, 23 | }; 24 | 25 | static inline u8 vga_entry_color(enum vga_color fg, enum vga_color bg) { 26 | return fg | bg << 4; 27 | } 28 | 29 | static inline u16 vga_entry(unsigned char uc, u8 color) { 30 | return (u16) uc | (u16) color << 8; 31 | } 32 | 33 | void vga_init(); 34 | void vga_write(const char* data, size_t size, u8 color); 35 | void vga_writestring(const char* data); 36 | void vga_writestring_color(const char* data, u8 color); 37 | -------------------------------------------------------------------------------- /homeworks/hw1/kernel.c: -------------------------------------------------------------------------------- 1 | #include "vga.h" 2 | #include "multiboot.h" 3 | #include "printk.h" 4 | #include "panic.h" 5 | 6 | void dump_mmap() { 7 | struct multiboot_mmap_iter mmap_iter; 8 | multiboot_mmap_iter_init(&mmap_iter); 9 | 10 | struct multiboot_mmap_entry* mmap_entry; 11 | while ((mmap_entry = multiboot_mmap_iter_next(&mmap_iter)) != NULL) { 12 | printk("mem region: "); 13 | switch (mmap_entry->type) { 14 | case MULTIBOOT_MMAP_TYPE_RAM: 15 | printk("[RAM] "); 16 | break; 17 | case MULTIBOOT_MMAP_TYPE_ACPI: 18 | printk("[ACPI] "); 19 | break; 20 | default: 21 | printk("[RESERVED] "); 22 | break; 23 | } 24 | printk(" base_addr=%p, len=%d\n", mmap_entry->base_addr, mmap_entry->length); 25 | } 26 | } 27 | 28 | 29 | void kernel_main() { 30 | vga_init(); 31 | multiboot_init(); 32 | 33 | printk("HeLL OS loaded.\n"); 34 | 35 | dump_mmap(); 36 | } 37 | -------------------------------------------------------------------------------- /homeworks/hw2/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | 5 | static inline int memcmp(const void* str1, const void* str2, size_t count) { 6 | const unsigned char *s1 = (const unsigned char*)str1; 7 | const unsigned char *s2 = (const unsigned char*)str2; 8 | while (count-- > 0) { 9 | if (*s1++ != *s2++) { 10 | return s1[-1] < s2[-1] ? -1 : 1; 11 | } 12 | } 13 | return 0; 14 | } 15 | 16 | static inline void memset(void* p, int ch, size_t sz) { 17 | u8* ptr = (u8*)p; 18 | while (sz > 0) { 19 | *ptr = ch; 20 | ptr++; 21 | sz--; 22 | } 23 | } 24 | 25 | static inline void memcpy(void* dst, void* src, size_t sz) { 26 | u8* d = dst; 27 | const u8* s = src; 28 | while (sz > 0) { 29 | *d = *s; 30 | d++; 31 | s++; 32 | sz--; 33 | } 34 | } 35 | 36 | static inline size_t strlen(const char* str) { 37 | size_t len = 0; 38 | while (str[len]) { 39 | len++; 40 | } 41 | return len; 42 | } 43 | 44 | #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) 45 | -------------------------------------------------------------------------------- /homeworks/hw1/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | 5 | static inline int memcmp(const void* str1, const void* str2, size_t count) { 6 | const unsigned char *s1 = (const unsigned char*)str1; 7 | const unsigned char *s2 = (const unsigned char*)str2; 8 | 9 | while (count-- > 0) { 10 | if (*s1++ != *s2++) { 11 | return s1[-1] < s2[-1] ? -1 : 1; 12 | } 13 | } 14 | return 0; 15 | } 16 | 17 | static inline void memset(void* p, int ch, size_t sz) { 18 | u8* ptr = (u8*)p; 19 | while (sz > 0) { 20 | *ptr = ch; 21 | ptr++; 22 | sz--; 23 | } 24 | } 25 | 26 | static inline void memcpy(void* dst, void* src, size_t sz) { 27 | u8* d = dst; 28 | const u8* s = src; 29 | while (sz > 0) { 30 | *d = *s; 31 | d++; 32 | s++; 33 | sz--; 34 | } 35 | } 36 | 37 | static inline size_t strlen(const char* str) { 38 | size_t len = 0; 39 | while (str[len]) { 40 | len++; 41 | } 42 | return len; 43 | } 44 | 45 | #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) 46 | -------------------------------------------------------------------------------- /homeworks/hw1/multiboot.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define MULTIBOOT_MAGIC 0xE85250D6 4 | #define MULTIBOOT_ARCH_32 0 5 | 6 | #define MULTIBOOT_TAG_END 0 7 | #define MULTIBOOT_TAG_MMAP 6 8 | 9 | #define MULTIBOOT_MMAP_TYPE_RAM 1 10 | #define MULTIBOOT_MMAP_TYPE_ACPI 3 11 | 12 | 13 | #ifndef __ASSEMBLER__ 14 | 15 | #include "types.h" 16 | 17 | 18 | struct multiboot_mmap_entry { 19 | u64 base_addr; 20 | u64 length; 21 | u32 type; 22 | u32 reserved; 23 | }; 24 | 25 | // multiboot_init parses information provided by bootloader. It panics, if memory map tag found. 26 | void multiboot_init(); 27 | 28 | struct multiboot_mmap_iter { 29 | }; 30 | 31 | // multiboot_mmap_iter_init creates new iterator over memory map regions provided by bootloader. 32 | void multiboot_mmap_iter_init(struct multiboot_mmap_iter* it); 33 | 34 | // multiboot_mmap_iter_next should return next memory region from given iterator. 35 | // If there are no more regions, return NULL instead. 36 | struct multiboot_mmap_entry* multiboot_mmap_iter_next(struct multiboot_mmap_iter* it); 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /homeworks/hw4/arch/x86/msr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define IA32_EFER 0xc0000080 6 | #define IA32_EFER_SCE (1<<0) 7 | 8 | #define IA32_STAR 0xc0000081 9 | #define IA32_LSTAR 0xc0000082 10 | #define IA32_CSTAR 0xc0000083 11 | #define IA32_FMASK 0xc0000084 12 | 13 | #define IA32_FS_BASE 0xc0000100 14 | #define IA32_GS_BASE 0xc0000101 15 | #define IA32_KERNEL_GS_BASE 0xc0000102 16 | 17 | void x86_wrmsr(uint32_t msr, uint64_t x) { 18 | uint32_t hi = (uint32_t)(x >> 32); 19 | uint32_t lo = x & 0xffffffff; 20 | __asm__ volatile ( 21 | "movl %0, %%ecx\n" 22 | "movl %1, %%eax\n" 23 | "movl %2, %%edx\n" 24 | "wrmsr" 25 | : 26 | : "r"(msr), "r"(lo), "r"(hi) 27 | : "eax", "ecx", "edx" 28 | ); 29 | } 30 | 31 | uint64_t x86_rdmsr(uint32_t msr) { 32 | uint32_t lo; 33 | uint32_t hi; 34 | __asm__ volatile ( 35 | "movl %2, %%ecx\n" 36 | "rdmsr\n" 37 | : "=a"(lo), "=d"(hi) 38 | : "r"(msr) 39 | : "ecx" 40 | ); 41 | uint64_t res = hi; 42 | return (res << 32) | lo; 43 | } 44 | -------------------------------------------------------------------------------- /homeworks/hw2/kernel.c: -------------------------------------------------------------------------------- 1 | #include "vga.h" 2 | #include "multiboot.h" 3 | #include "printk.h" 4 | #include "panic.h" 5 | #include "irq.h" 6 | #include "acpi.h" 7 | #include "apic.h" 8 | 9 | void dump_mmap() { 10 | struct multiboot_mmap_iter mmap_iter; 11 | multiboot_mmap_iter_init(&mmap_iter); 12 | 13 | struct multiboot_mmap_entry* mmap_entry; 14 | while ((mmap_entry = multiboot_mmap_iter_next(&mmap_iter)) != NULL) { 15 | printk("mem region: "); 16 | switch (mmap_entry->type) { 17 | case MULTIBOOT_MMAP_TYPE_RAM: 18 | printk("[RAM] "); 19 | break; 20 | case MULTIBOOT_MMAP_TYPE_ACPI: 21 | printk("[ACPI] "); 22 | break; 23 | default: 24 | printk("[RESERVED] "); 25 | break; 26 | } 27 | printk(" base_addr=%p, len=%d\n", mmap_entry->base_addr, mmap_entry->length); 28 | } 29 | } 30 | 31 | void kernel_main() { 32 | irq_init(); 33 | vga_init(); 34 | multiboot_init(); 35 | acpi_init(); 36 | apic_init(); 37 | 38 | printk("HeLL OS loaded.\n"); 39 | dump_mmap(); 40 | 41 | irq_enable(); 42 | } 43 | -------------------------------------------------------------------------------- /homeworks/hw3/Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | AS=x86_64-elf-gcc 4 | CC=x86_64-elf-gcc 5 | LD=x86_64-elf-ld 6 | OBJCOPY=x86_64-elf-objcopy 7 | SED=sed 8 | GRUB_MKRESCUE=grub-mkrescue 9 | 10 | ROOT=$(shell pwd) 11 | ASFLAGS=-g -I$(ROOT) 12 | CCFLAGS=-I$(ROOT) -mno-mmx -mno-sse -mno-sse2 -maddress-mode=long -mcmodel=kernel -g -m64 -mno-red-zone -std=c11 -ffreestanding -fno-common -Wall -Wextra -nostdlib 13 | LDFLAGS=-nostdlib --no-dynamic-linker --warn-constructors --warn-common --no-eh-frame-hdr --fatal-warnings 14 | export 15 | 16 | kernel.iso: kernel.bin 17 | mkdir -p isodir/boot/grub 18 | cp grub.cfg isodir/boot/grub 19 | cp kernel.bin isodir/boot 20 | $(GRUB_MKRESCUE) -o kernel.iso isodir 21 | rm -rf isodir 22 | 23 | kernel.bin: 24 | $(MAKE) -C boot/ 25 | $(MAKE) -C drivers/ 26 | $(MAKE) -C kernel/ 27 | $(MAKE) -C mm/ 28 | $(LD) $(LDFLAGS) -T <(cpp -P -E linker.ld) `find $(ROOT) -name '*.o'` -o kernel.bin 29 | $(OBJCOPY) --only-keep-debug kernel.bin kernel.sym 30 | $(OBJCOPY) --strip-debug kernel.bin 31 | 32 | clean: 33 | $(MAKE) -C boot/ clean 34 | $(MAKE) -C drivers/ clean 35 | $(MAKE) -C kernel/ clean 36 | $(MAKE) -C mm/ clean 37 | rm -f kernel.bin 38 | rm -f kernel.sym 39 | rm -f kernel.iso 40 | 41 | .PHONY: kernel.bin clean 42 | -------------------------------------------------------------------------------- /homeworks/hw2/acpi.c: -------------------------------------------------------------------------------- 1 | #include "acpi.h" 2 | #include "common.h" 3 | #include "panic.h" 4 | #include "multiboot.h" 5 | 6 | struct acpi_sdt* acpi_lookup_sdt(struct acpi_sdt* root, const char* signature) { 7 | size_t sz = (root->header.length - sizeof(root->header)) / 4; 8 | for (size_t i = 0; i < sz; i++) { 9 | struct acpi_sdt* sdt = (void*)(uint64_t)root->entries[i]; 10 | if (memcmp(signature, &sdt->header.signature, 4) == 0) { 11 | return sdt; 12 | } 13 | } 14 | return NULL; 15 | } 16 | 17 | static struct acpi_sdt* rsdt = NULL; 18 | 19 | struct acpi_sdt* acpi_lookup_rsdt(const char* signature) { 20 | return acpi_lookup_sdt(rsdt, signature); 21 | } 22 | 23 | struct acpi_rsdp { 24 | char signature[8]; 25 | uint8_t checksum; 26 | char oemid[6]; 27 | uint8_t revision; 28 | uint32_t rsdt_addr; 29 | } __attribute__ ((packed)); 30 | 31 | 32 | void acpi_init() { 33 | struct multiboot_tag* mb_tag = multiboot_lookup_tag(MULTIBOOT_TAG_ACPI_OLD_RSDP); 34 | if (mb_tag == NULL) { 35 | panic("no multiboot tag with ACPI RSDP addres"); 36 | } 37 | struct acpi_rsdp* rsdp = (void*)(uint64_t)&mb_tag->data; 38 | rsdt = (void*)(uint64_t)rsdp->rsdt_addr; 39 | printk("found RSDT at %p\n", rsdt); 40 | 41 | // TODO: verify ACPI table checksums here. 42 | 43 | } 44 | -------------------------------------------------------------------------------- /homeworks/hw3/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | 5 | #define ALIGN_UP(X, R) ((((uint64_t)X) + (R) - 1) / (R) * (R)) 6 | #define DIV_ROUNDUP(X, R) ((((uint64_t)X) + (R) - 1) / (R)) 7 | 8 | static inline int memcmp(const void* str1, const void* str2, size_t count) { 9 | const unsigned char *s1 = (const unsigned char*)str1; 10 | const unsigned char *s2 = (const unsigned char*)str2; 11 | while (count-- > 0) { 12 | if (*s1++ != *s2++) { 13 | return s1[-1] < s2[-1] ? -1 : 1; 14 | } 15 | } 16 | return 0; 17 | } 18 | 19 | static inline void memset(void* p, int ch, size_t sz) { 20 | u8* ptr = (u8*)p; 21 | while (sz > 0) { 22 | *ptr = ch; 23 | ptr++; 24 | sz--; 25 | } 26 | } 27 | 28 | static inline void memcpy(void* dst, void* src, size_t sz) { 29 | u8* d = dst; 30 | const u8* s = src; 31 | while (sz > 0) { 32 | *d = *s; 33 | d++; 34 | s++; 35 | sz--; 36 | } 37 | } 38 | 39 | static inline size_t strlen(const char* str) { 40 | size_t len = 0; 41 | while (str[len]) { 42 | len++; 43 | } 44 | return len; 45 | } 46 | 47 | #define BOCHS_BREAK { __asm__ volatile ("xchgw %bx, %bx"); }; 48 | 49 | #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) 50 | 51 | typedef struct mem_region { 52 | void* start; 53 | void* end; 54 | } mem_region_t; 55 | -------------------------------------------------------------------------------- /homeworks/hw4/Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | AS=x86_64-elf-gcc 4 | CC=x86_64-elf-gcc 5 | LD=x86_64-elf-ld 6 | OBJCOPY=x86_64-elf-objcopy 7 | SED=sed 8 | GRUB_MKRESCUE=grub-mkrescue 9 | 10 | ROOT=$(shell pwd) 11 | ASFLAGS=-g -I$(ROOT) 12 | CCFLAGS=-I$(ROOT) -mno-mmx -mno-sse -mno-sse2 -maddress-mode=long -mcmodel=kernel -g -m64 -mno-red-zone -std=c11 -ffreestanding -nostdlib -fno-common -Wall -Werror -Wextra -Wno-unused-function 13 | LDFLAGS=-nostdlib --no-dynamic-linker --warn-constructors --warn-common --no-eh-frame-hdr --fatal-warnings 14 | export 15 | 16 | kernel.iso: kernel.bin 17 | mkdir -p isodir/boot/grub 18 | cp grub.cfg isodir/boot/grub 19 | cp kernel.bin isodir/boot 20 | $(GRUB_MKRESCUE) -o kernel.iso isodir 21 | rm -rf isodir 22 | 23 | kernel.bin: 24 | $(MAKE) -C arch/x86 25 | $(MAKE) -C boot/ 26 | $(MAKE) -C drivers/ 27 | $(MAKE) -C kernel/ 28 | $(MAKE) -C mm/ 29 | $(MAKE) -C sched/ 30 | $(LD) $(LDFLAGS) -T <(cpp -P -E linker.ld) `find $(ROOT) -name '*.o'` -o kernel.bin 31 | $(OBJCOPY) --only-keep-debug kernel.bin kernel.sym 32 | $(OBJCOPY) --strip-debug kernel.bin 33 | 34 | clean: 35 | $(MAKE) -C arch/x86 clean 36 | $(MAKE) -C boot/ clean 37 | $(MAKE) -C drivers/ clean 38 | $(MAKE) -C kernel/ clean 39 | $(MAKE) -C mm/ clean 40 | $(MAKE) -C sched/ clean 41 | rm -f kernel.bin 42 | rm -f kernel.sym 43 | rm -f kernel.iso 44 | 45 | .PHONY: kernel.bin clean 46 | -------------------------------------------------------------------------------- /homeworks/hw4/arch/x86/arch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "context_switch.h" 5 | #include "syscall.h" 6 | 7 | void arch_init(); 8 | 9 | typedef struct { 10 | uint64_t r15; 11 | uint64_t r14; 12 | uint64_t r13; 13 | uint64_t r12; 14 | uint64_t r11; 15 | uint64_t r10; 16 | uint64_t r9; 17 | uint64_t r8; 18 | uint64_t rbp; 19 | uint64_t rdi; 20 | uint64_t rsi; 21 | uint64_t rdx; 22 | uint64_t rcx; 23 | uint64_t rbx; 24 | uint64_t rax; 25 | uint64_t errcode; 26 | uint64_t rip; 27 | uint64_t cs; 28 | uint64_t rflags; 29 | uint64_t rsp; 30 | uint64_t ss; 31 | } arch_regs_t; 32 | 33 | #define arch_regs_set_retval(regs, retval) (regs)->rax = (retval) 34 | #define arch_regs_copy(dst, src) memcpy(dst, src, sizeof(arch_regs_t)) 35 | 36 | typedef struct { 37 | uint8_t* kstack_top; 38 | uint8_t saved_rsp; 39 | context_t context; 40 | } arch_thread_t; 41 | 42 | static inline void irq_disable() { 43 | __asm__ volatile ("cli"); 44 | } 45 | 46 | static inline void irq_enable() { 47 | __asm__ volatile ("sti"); 48 | } 49 | 50 | int arch_thread_new(arch_thread_t* thread, arch_regs_t** regs); 51 | int arch_thread_clone(arch_thread_t* dst, arch_regs_t** regs, arch_thread_t* src); 52 | void arch_thread_destroy(arch_thread_t* thread); 53 | void arch_thread_switch(arch_thread_t* prev, arch_thread_t* next); 54 | -------------------------------------------------------------------------------- /homeworks/hw4/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | 5 | #define ALIGN_UP(X, R) ((((uint64_t)X) + (R) - 1) / (R) * (R)) 6 | #define DIV_ROUNDUP(X, R) ((((uint64_t)X) + (R) - 1) / (R)) 7 | #define UNUSED(x) (void)(x) 8 | 9 | static inline int memcmp(const void* str1, const void* str2, size_t count) { 10 | const unsigned char *s1 = (const unsigned char*)str1; 11 | const unsigned char *s2 = (const unsigned char*)str2; 12 | while (count-- > 0) { 13 | if (*s1++ != *s2++) { 14 | return s1[-1] < s2[-1] ? -1 : 1; 15 | } 16 | } 17 | return 0; 18 | } 19 | 20 | static inline void memset(void* p, int ch, size_t sz) { 21 | u8* ptr = (u8*)p; 22 | while (sz > 0) { 23 | *ptr = ch; 24 | ptr++; 25 | sz--; 26 | } 27 | } 28 | 29 | static inline void memcpy(void* dst, void* src, size_t sz) { 30 | u8* d = dst; 31 | const u8* s = src; 32 | while (sz > 0) { 33 | *d = *s; 34 | d++; 35 | s++; 36 | sz--; 37 | } 38 | } 39 | 40 | static inline size_t strlen(const char* str) { 41 | size_t len = 0; 42 | while (str[len]) { 43 | len++; 44 | } 45 | return len; 46 | } 47 | 48 | #define BOCHS_BREAK { __asm__ volatile ("xchgw %bx, %bx"); }; 49 | 50 | #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) 51 | 52 | typedef struct mem_region { 53 | void* start; 54 | void* end; 55 | } mem_region_t; 56 | -------------------------------------------------------------------------------- /homeworks/hw3/drivers/acpi.c: -------------------------------------------------------------------------------- 1 | #include "acpi.h" 2 | #include "common.h" 3 | #include "kernel/panic.h" 4 | #include "kernel/multiboot.h" 5 | #include "mm/paging.h" 6 | 7 | struct acpi_sdt* acpi_lookup_sdt(struct acpi_sdt* root, const char* signature) { 8 | size_t sz = (root->header.length - sizeof(root->header)) / 4; 9 | for (size_t i = 0; i < sz; i++) { 10 | struct acpi_sdt* sdt = PHYS_TO_VIRT((void*)(uint64_t)root->entries[i]); 11 | if (memcmp(signature, &sdt->header.signature, 4) == 0) { 12 | return sdt; 13 | } 14 | } 15 | return NULL; 16 | } 17 | 18 | static struct acpi_sdt* rsdt = NULL; 19 | 20 | struct acpi_sdt* acpi_lookup_rsdt(const char* signature) { 21 | return acpi_lookup_sdt(rsdt, signature); 22 | } 23 | 24 | struct acpi_rsdp { 25 | char signature[8]; 26 | uint8_t checksum; 27 | char oemid[6]; 28 | uint8_t revision; 29 | uint32_t rsdt_addr; 30 | } __attribute__ ((packed)); 31 | 32 | 33 | void acpi_init() { 34 | struct multiboot_tag* mb_tag = multiboot_lookup_tag(MULTIBOOT_TAG_ACPI_OLD_RSDP); 35 | if (mb_tag == NULL) { 36 | panic("no multiboot tag with ACPI RSDP addres"); 37 | } 38 | struct acpi_rsdp* rsdp = (void*)(uint64_t)&mb_tag->data; 39 | rsdt = PHYS_TO_VIRT((uint64_t)rsdp->rsdt_addr); 40 | printk("found RSDT at %p\n", rsdt); 41 | 42 | // TODO: verify ACPI table checksums here. 43 | 44 | } 45 | -------------------------------------------------------------------------------- /homeworks/hw4/drivers/acpi.c: -------------------------------------------------------------------------------- 1 | #include "acpi.h" 2 | #include "common.h" 3 | #include "kernel/panic.h" 4 | #include "kernel/multiboot.h" 5 | #include "mm/paging.h" 6 | 7 | struct acpi_sdt* acpi_lookup_sdt(struct acpi_sdt* root, const char* signature) { 8 | size_t sz = (root->header.length - sizeof(root->header)) / 4; 9 | for (size_t i = 0; i < sz; i++) { 10 | struct acpi_sdt* sdt = PHYS_TO_VIRT((void*)(uint64_t)root->entries[i]); 11 | if (memcmp(signature, &sdt->header.signature, 4) == 0) { 12 | return sdt; 13 | } 14 | } 15 | return NULL; 16 | } 17 | 18 | static struct acpi_sdt* rsdt = NULL; 19 | 20 | struct acpi_sdt* acpi_lookup_rsdt(const char* signature) { 21 | return acpi_lookup_sdt(rsdt, signature); 22 | } 23 | 24 | struct acpi_rsdp { 25 | char signature[8]; 26 | uint8_t checksum; 27 | char oemid[6]; 28 | uint8_t revision; 29 | uint32_t rsdt_addr; 30 | } __attribute__ ((packed)); 31 | 32 | 33 | void acpi_init() { 34 | struct multiboot_tag* mb_tag = multiboot_lookup_tag(MULTIBOOT_TAG_ACPI_OLD_RSDP); 35 | if (mb_tag == NULL) { 36 | panic("no multiboot tag with ACPI RSDP addres"); 37 | } 38 | struct acpi_rsdp* rsdp = (void*)(uint64_t)&mb_tag->data; 39 | rsdt = PHYS_TO_VIRT((uint64_t)rsdp->rsdt_addr); 40 | printk("found RSDT at %p\n", rsdt); 41 | 42 | // TODO: verify ACPI table checksums here. 43 | 44 | } 45 | -------------------------------------------------------------------------------- /homeworks/hw3/mm/paging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "defs.h" 4 | #ifndef __ASSEMBLER__ 5 | #include 6 | #include 7 | #endif 8 | 9 | #define PTE_PAGE_SIZE (1ull << 7) 10 | #define PTE_PRESENT (1ull << 0) 11 | 12 | #define PTE_FLAGS_MASK ((1ull << 12) - 1) 13 | #define PTE_ADDR_MASK ((1ull << 48) - 1) 14 | #define PTE_ADDR(pte) ((void*)(((pte) & PTE_ADDR_MASK) & ~PTE_FLAGS_MASK)) 15 | 16 | #define ATTR_ALIGN_4096 __attribute__((aligned(4096))) 17 | 18 | #define PML4E_FROM_ADDR(addr) (((uint64_t)(addr) >> 39) & 0x1ff) 19 | #define PDPE_FROM_ADDR(addr) (((uint64_t)(addr) >> 30) & 0x1ff) 20 | #define PDE_FROM_ADDR(addr) (((uint64_t)(addr) >> 21) & 0x1ff) 21 | #define PTE_FROM_ADDR(addr) (((uint64_t)(addr) >> 12) & 0x1ff) 22 | 23 | #define PHYS_TO_VIRT(addr) ((void*)(KERNEL_DIRECT_PHYS_MAPPING_START + (uint64_t)addr)) 24 | #define VIRT_TO_PHYS(addr) ((void*)((uint64_t)addr - KERNEL_DIRECT_PHYS_MAPPING_START)) 25 | 26 | #ifndef __ASSEMBLER__ 27 | 28 | struct pml4 { 29 | uint64_t entries[512]; 30 | } ATTR_ALIGN_4096; 31 | typedef struct pml4 pml4_t; 32 | 33 | struct pdpt { 34 | uint64_t entries[512]; 35 | } ATTR_ALIGN_4096; 36 | typedef struct pdpt pdpt_t; 37 | 38 | struct pgdir { 39 | uint64_t entries[512]; 40 | } ATTR_ALIGN_4096; 41 | typedef struct pgdir pgdir_t; 42 | 43 | struct pgtbl { 44 | uint64_t entries[512]; 45 | } ATTR_ALIGN_4096; 46 | typedef struct pgtbl pgtbl_t; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /homeworks/hw4/arch/x86/x86.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | 5 | #define RFLAGS_IF (1<<9) 6 | 7 | static inline uint64_t x86_read_cr2() { 8 | uint64_t ret; 9 | __asm__ volatile ( 10 | "mov %%cr2, %0" 11 | : "=r"(ret) 12 | ); 13 | return ret; 14 | } 15 | 16 | static inline uint64_t x86_read_cr3() { 17 | uint64_t ret; 18 | __asm__ volatile ( 19 | "mov %%cr3, %0" 20 | : "=r"(ret) 21 | ); 22 | return ret; 23 | } 24 | 25 | static inline void x86_write_cr3(uint64_t x) { 26 | __asm__ volatile ( 27 | "mov %0, %%cr3" 28 | : : "r"(x) 29 | ); 30 | } 31 | 32 | static inline void x86_hlt() { 33 | __asm__ volatile ("hlt"); 34 | } 35 | 36 | static _Noreturn inline void x86_hlt_forever() { 37 | for (;;) { 38 | x86_hlt(); 39 | } 40 | } 41 | 42 | static inline void x86_outb(uint16_t port, uint8_t data) { 43 | __asm__ volatile ("out %0,%1" : : "a" (data), "d" (port)); 44 | } 45 | 46 | typedef struct x86_tss { 47 | uint32_t reserved0; 48 | uint64_t rsp0; 49 | uint64_t rsp1; 50 | uint64_t rsp2; 51 | uint64_t reserved1; 52 | uint64_t ist1; 53 | uint64_t ist2; 54 | uint64_t ist3; 55 | uint64_t ist4; 56 | uint64_t ist5; 57 | uint64_t ist6; 58 | uint64_t ist7; 59 | uint64_t reserved2; 60 | uint16_t reserved3; 61 | uint16_t io_map_addr; 62 | } __attribute__((packed)) x86_tss_t; 63 | -------------------------------------------------------------------------------- /homeworks/hw3/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_early_entry_point) 2 | 3 | #include "defs.h" 4 | 5 | SECTIONS 6 | { 7 | // https://stackoverflow.com/questions/15400910/ld-linker-script-producing-huge-binary 8 | . = SIZEOF_HEADERS; 9 | 10 | .multiboot : ALIGN(4) { 11 | KEEP(*(.multiboot)) 12 | } 13 | 14 | . = 2M; 15 | 16 | PROVIDE(_phys_start_kernel_sections = .); 17 | .early.text : { 18 | *(.early.text) 19 | } 20 | 21 | .early.rodata : { 22 | *(.early.rodata) 23 | } 24 | 25 | .early.data : { 26 | *(.early.data) 27 | } 28 | 29 | .early.bss : { 30 | *(.early.bss) 31 | } 32 | 33 | . = ALIGN(0x200000); 34 | _end_early = .; 35 | 36 | . = KERNEL_SECTIONS_START; 37 | _phys_start_hh = _end_early + . - KERNEL_SECTIONS_START; 38 | .text : AT(_end_early + ADDR(.text) - KERNEL_SECTIONS_START) { 39 | *(.text) 40 | } 41 | 42 | .rodata : AT(_end_early + ADDR(.rodata) - KERNEL_SECTIONS_START) { 43 | *(.rodata) 44 | } 45 | 46 | .data : AT(_end_early + ADDR(.data) - KERNEL_SECTIONS_START) { 47 | *(.data) 48 | } 49 | 50 | .bss : AT(_end_early + ADDR(.bss) - KERNEL_SECTIONS_START) { 51 | *(COMMON) 52 | *(.bss) 53 | } 54 | . = ALIGN(0x200000); 55 | PROVIDE(_phys_end_kernel_sections = _end_early + . - KERNEL_SECTIONS_START); 56 | 57 | /DISCARD/ : { 58 | *(.eh_frame) 59 | *(.note.gnu.*) 60 | *(.dynamic) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /homeworks/hw2/Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | AS=x86_64-elf-gcc 4 | CC=x86_64-elf-gcc 5 | LD=x86_64-elf-gcc 6 | OBJCOPY=x86_64-elf-objcopy 7 | SED=sed 8 | GRUB_MKRESCUE=grub-mkrescue 9 | 10 | ASFLAGS=-g 11 | CCFLAGS=-mno-mmx -mno-sse -mno-sse2 -fno-pie -g -mno-red-zone -std=gnu99 -ffreestanding -Wall -Wextra -nostdlib 12 | LDFLAGS=-ffreestanding -nostdlib 13 | 14 | C_SOURCES := $(shell ls *.c) 15 | ASM_SOURCES := $(shell ls *.S) 16 | C_OBJS := $(C_SOURCES:.c=.c.o) 17 | ASM_OBJS := $(ASM_SOURCES:.S=.S.o) 18 | 19 | image: build 20 | mkdir -p isodir/boot/grub 21 | cp grub.cfg isodir/boot/grub 22 | cp kernel.bin isodir/boot 23 | $(GRUB_MKRESCUE) -o kernel.iso isodir 24 | rm -rf isodir 25 | 26 | build: .c-depend .S-depend $(C_OBJS) $(ASM_OBJS) linker.ld 27 | $(LD) $(LDFLAGS) -T <(cpp -P -E linker.ld) $(ASM_OBJS) $(C_OBJS) -o kernel.bin 28 | $(OBJCOPY) --only-keep-debug kernel.bin kernel.sym 29 | $(OBJCOPY) --strip-debug kernel.bin 30 | 31 | %.c.o: %.c 32 | $(CC) $(CCFLAGS) -c $< -o $@ 33 | 34 | %.S.o: %.S 35 | $(AS) $(ASFLAGS) -c $< -o $@ 36 | 37 | .c-depend: $(C_SOURCES) 38 | rm -f ./.depend.c 39 | $(CC) $(CCFLAGS) -MM $^ > ./.c-depend 40 | $(SED) -i 's/^\(.*\)\.o/\1.c.o/g' .c-depend 41 | 42 | .S-depend: $(ASM_SOURCES) 43 | rm -f ./.depend.S 44 | $(CC) $(CCFLAGS) -MM $^ > ./.S-depend 45 | $(SED) -i 's/^\(.*\)\.o/\1.S.o/g' .S-depend 46 | 47 | clean: 48 | rm -f *.o 49 | rm -f .*-depend 50 | rm -f kernel.bin 51 | rm -f kernel.sym 52 | rm -f kernel.iso 53 | 54 | .PHONY: build clean 55 | -------------------------------------------------------------------------------- /homeworks/hw4/sched/user.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "kernel/syscall.h" 4 | 5 | #define USER_TEXT __attribute__((section(".user.text,\"ax\",@progbits#"))) 6 | 7 | #define SYSCALL0(n, res) __asm__ volatile ("syscall" : "=a"(res) : "a"(n) : "rdi", "rsi", "rdx", "rcx", "r8", "r9", "r10", "r11", "memory" ) 8 | #define SYSCALL1(n, arg0, res) __asm__ volatile ("syscall" : "=a"(res) : "a"(n), "D"(arg0) : "rsi", "rdx", "rcx", "r8", "r9", "r10", "r11", "memory" ) 9 | #define SYSCALL2(n, arg0, arg1, res) __asm__ volatile ("syscall" : "=a(res)": "a"(n), "D"(arg0), "S"(arg1) : "rdx", "rcx", "r8", "r9", "r10", "r11", "memory" ) 10 | 11 | USER_TEXT int64_t getpid() { 12 | int64_t res; 13 | SYSCALL0(SYS_GETPID, res); 14 | return res; 15 | } 16 | 17 | USER_TEXT int64_t sleep(uint64_t arg) { 18 | int64_t res; 19 | SYSCALL1(SYS_SLEEP, arg, res); 20 | return res; 21 | } 22 | 23 | USER_TEXT int64_t fork() { 24 | int64_t res; 25 | SYSCALL0(SYS_FORK, res); 26 | return res; 27 | } 28 | 29 | USER_TEXT int64_t exit(uint64_t arg) { 30 | int64_t res; 31 | SYSCALL1(SYS_EXIT, arg, res); 32 | return res; 33 | } 34 | 35 | USER_TEXT int main() { 36 | int64_t pid = getpid(); 37 | 38 | int err = fork(); 39 | if (err < 0) { 40 | return -1; 41 | } 42 | 43 | if (err == 0) { 44 | sleep(getpid()); 45 | } else { 46 | sleep(pid); 47 | } 48 | 49 | return 0; 50 | } 51 | 52 | USER_TEXT void user_program() { 53 | exit(main()); 54 | } 55 | -------------------------------------------------------------------------------- /homeworks/hw2/irq.c: -------------------------------------------------------------------------------- 1 | #include "irq.h" 2 | #include "panic.h" 3 | #include "x86.h" 4 | #include "apic.h" 5 | 6 | void timer_handler() { 7 | printk("."); 8 | apic_eoi(); 9 | } 10 | 11 | void spurious_handler() { 12 | apic_eoi(); 13 | } 14 | 15 | void pf_handler(struct irqctx_errcode* ctx) { 16 | // TODO: print information about context: error code, RIP and dump general-purpose registers. 17 | } 18 | 19 | void gp_handler(struct irqctx_errcode* ctx) { 20 | // TODO: print information about context: error code, RIP and dump general-purpose registers. 21 | } 22 | 23 | void np_handler(struct irqctx_errcode* ctx) { 24 | // TODO: print information about context: error code, RIP and dump general-purpose registers. 25 | } 26 | 27 | void ts_handler(struct irqctx_errcode* ctx) { 28 | // TODO: print information about context: error code, RIP and dump general-purpose registers. 29 | } 30 | 31 | void ss_handler(struct irqctx_errcode* ctx) { 32 | // TODO: print information about context: error code, RIP and dump general-purpose registers. 33 | } 34 | 35 | void df_handler(struct irqctx_errcode* ctx) { 36 | // TODO: print information about context: error code, RIP and dump general-purpose registers. 37 | } 38 | 39 | void ud_handler(struct irqctx* ctx) { 40 | // TODO: print information about context: error code, RIP and dump general-purpose registers. 41 | } 42 | 43 | void nm_handler(struct irqctx* ctx) { 44 | // TODO: print information about context: error code, RIP and dump general-purpose registers. 45 | } 46 | -------------------------------------------------------------------------------- /homeworks/hw3/kernel/irq.c: -------------------------------------------------------------------------------- 1 | #include "irq.h" 2 | #include "kernel/panic.h" 3 | #include "arch/x86.h" 4 | #include "drivers/apic.h" 5 | 6 | void timer_handler() { 7 | apic_eoi(); 8 | } 9 | 10 | void spurious_handler() { 11 | apic_eoi(); 12 | } 13 | 14 | void pf_handler(struct irqctx_errcode* ctx) { 15 | // TODO: print information about context: error code, RIP and dump general-purpose registers. 16 | } 17 | 18 | void gp_handler(struct irqctx_errcode* ctx) { 19 | // TODO: print information about context: error code, RIP and dump general-purpose registers. 20 | } 21 | 22 | void np_handler(struct irqctx_errcode* ctx) { 23 | // TODO: print information about context: error code, RIP and dump general-purpose registers. 24 | } 25 | 26 | void ts_handler(struct irqctx_errcode* ctx) { 27 | // TODO: print information about context: error code, RIP and dump general-purpose registers. 28 | } 29 | 30 | void ss_handler(struct irqctx_errcode* ctx) { 31 | // TODO: print information about context: error code, RIP and dump general-purpose registers. 32 | } 33 | 34 | void df_handler(struct irqctx_errcode* ctx) { 35 | // TODO: print information about context: error code, RIP and dump general-purpose registers. 36 | } 37 | 38 | void ud_handler(struct irqctx_errcode* ctx) { 39 | // TODO: print information about context: error code, RIP and dump general-purpose registers. 40 | } 41 | 42 | void nm_handler(struct irqctx* ctx) { 43 | // TODO: print information about context: error code, RIP and dump general-purpose registers. 44 | } 45 | -------------------------------------------------------------------------------- /homeworks/hw3/kernel/kernel.c: -------------------------------------------------------------------------------- 1 | #include "drivers/vga.h" 2 | #include "kernel/multiboot.h" 3 | #include "boot/early.h" 4 | #include "kernel/printk.h" 5 | #include "kernel/panic.h" 6 | #include "kernel/irq.h" 7 | #include "drivers/acpi.h" 8 | #include "drivers/apic.h" 9 | #include "arch/x86.h" 10 | #include "mm/frame_alloc.h" 11 | #include "mm/vmem.h" 12 | 13 | 14 | void dump_mmap() { 15 | struct multiboot_mmap_iter mmap_iter; 16 | multiboot_mmap_iter_init(&mmap_iter); 17 | 18 | struct multiboot_mmap_entry* mmap_entry; 19 | while ((mmap_entry = multiboot_mmap_iter_next(&mmap_iter)) != NULL) { 20 | printk("mem region: "); 21 | switch (mmap_entry->type) { 22 | case MULTIBOOT_MMAP_TYPE_RAM: 23 | printk("[RAM] "); 24 | break; 25 | case MULTIBOOT_MMAP_TYPE_ACPI: 26 | printk("[ACPI] "); 27 | break; 28 | default: 29 | printk("[RESERVED] "); 30 | break; 31 | } 32 | printk(" base_addr=%p, len=%d\n", mmap_entry->base_addr, mmap_entry->length); 33 | } 34 | } 35 | 36 | void kernel_main_return() { 37 | panic("kernel_main returned, this should never happen"); 38 | } 39 | 40 | void kernel_main(early_data_t* early_data) { 41 | irq_init(); 42 | vga_init(); 43 | multiboot_init(early_data->multiboot_info); 44 | acpi_init(); 45 | apic_init(); 46 | irq_enable(); 47 | 48 | printk("Hello from higher-half!.\n"); 49 | dump_mmap(); 50 | 51 | frame_alloc_init(); 52 | 53 | x86_hlt_forever(); 54 | } 55 | -------------------------------------------------------------------------------- /homeworks/hw4/mm/paging.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "defs.h" 4 | #ifndef __ASSEMBLER__ 5 | #include 6 | #include 7 | #endif 8 | 9 | #define PTE_PAGE_SIZE (1ull << 7) 10 | #define PTE_PRESENT (1ull << 0) 11 | #define PTE_USER (1ull << 2) 12 | #define PTE_WRITE (1ull << 1) 13 | 14 | #define PTE_FLAGS_MASK ((1ull << 12) - 1) 15 | #define PTE_ADDR_MASK ((1ull << 48) - 1) 16 | #define PTE_ADDR(pte) ((void*)(((pte) & PTE_ADDR_MASK) & ~PTE_FLAGS_MASK)) 17 | 18 | #define PTE_COUNT 512 19 | 20 | #define ATTR_ALIGN_4096 __attribute__((aligned(4096))) 21 | 22 | #define PML4E_FROM_ADDR(addr) (((uint64_t)(addr) >> 39) & 0x1ff) 23 | #define PDPE_FROM_ADDR(addr) (((uint64_t)(addr) >> 30) & 0x1ff) 24 | #define PDE_FROM_ADDR(addr) (((uint64_t)(addr) >> 21) & 0x1ff) 25 | #define PTE_FROM_ADDR(addr) (((uint64_t)(addr) >> 12) & 0x1ff) 26 | 27 | #define PHYS_TO_VIRT(addr) ((void*)(KERNEL_DIRECT_PHYS_MAPPING_START + (uint64_t)addr)) 28 | #define VIRT_TO_PHYS(addr) ((void*)((uint64_t)addr - KERNEL_DIRECT_PHYS_MAPPING_START)) 29 | 30 | #ifndef __ASSEMBLER__ 31 | 32 | typedef uint64_t pte_t; 33 | 34 | struct pml4 { 35 | pte_t entries[512]; 36 | } ATTR_ALIGN_4096; 37 | typedef struct pml4 pml4_t; 38 | 39 | struct pdpt { 40 | pte_t entries[512]; 41 | } ATTR_ALIGN_4096; 42 | typedef struct pdpt pdpt_t; 43 | 44 | struct pgdir { 45 | pte_t entries[512]; 46 | } ATTR_ALIGN_4096; 47 | typedef struct pgdir pgdir_t; 48 | 49 | struct pgtbl { 50 | pte_t entries[512]; 51 | } ATTR_ALIGN_4096; 52 | typedef struct pgtbl pgtbl_t; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /homeworks/hw3/kernel/multiboot.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define MULTIBOOT_TAG_END 0 4 | #define MULTIBOOT_TAG_MMAP 6 5 | #define MULTIBOOT_TAG_ACPI_OLD_RSDP 14 6 | 7 | #define MULTIBOOT_MMAP_TYPE_RAM 1 8 | #define MULTIBOOT_MMAP_TYPE_ACPI 3 9 | 10 | #include "types.h" 11 | #include "common.h" 12 | 13 | struct multiboot_mmap_entry { 14 | u64 base_addr; 15 | u64 length; 16 | u32 type; 17 | u32 reserved; 18 | }; 19 | 20 | struct multiboot_tag { 21 | u32 type; 22 | u32 size; 23 | char data[0]; 24 | }; 25 | 26 | struct multiboot_info { 27 | u32 total_size; 28 | u32 reserved; 29 | struct multiboot_tag first_tag; 30 | }; 31 | 32 | struct multiboot_mmap_tag { 33 | struct multiboot_tag base; 34 | u32 entry_size; 35 | u32 entry_version; 36 | struct multiboot_mmap_entry first_entry; 37 | }; 38 | 39 | // multiboot_init parses information provided by bootloader. It panics, if memory map tag found. 40 | void multiboot_init(); 41 | 42 | struct multiboot_tag* multiboot_lookup_tag(uint32_t tag_code); 43 | 44 | struct multiboot_mmap_iter { 45 | struct multiboot_mmap_entry* curr; 46 | }; 47 | 48 | // multiboot_mmap_iter_init creates new iterator over memory map regions provided by bootloader. 49 | void multiboot_mmap_iter_init(struct multiboot_mmap_iter* it); 50 | 51 | // multiboot_mmap_iter_next should return next memory region from given iterator. 52 | // If there are no more regions, return NULL instead. 53 | struct multiboot_mmap_entry* multiboot_mmap_iter_next(struct multiboot_mmap_iter* it); 54 | 55 | mem_region_t multiboot_mem_region(); 56 | -------------------------------------------------------------------------------- /homeworks/hw4/kernel/multiboot.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define MULTIBOOT_TAG_END 0 4 | #define MULTIBOOT_TAG_MMAP 6 5 | #define MULTIBOOT_TAG_ACPI_OLD_RSDP 14 6 | 7 | #define MULTIBOOT_MMAP_TYPE_RAM 1 8 | #define MULTIBOOT_MMAP_TYPE_ACPI 3 9 | 10 | #include "types.h" 11 | #include "common.h" 12 | 13 | struct multiboot_mmap_entry { 14 | u64 base_addr; 15 | u64 length; 16 | u32 type; 17 | u32 reserved; 18 | }; 19 | 20 | struct multiboot_tag { 21 | u32 type; 22 | u32 size; 23 | char data[0]; 24 | }; 25 | 26 | struct multiboot_info { 27 | u32 total_size; 28 | u32 reserved; 29 | struct multiboot_tag first_tag; 30 | }; 31 | 32 | struct multiboot_mmap_tag { 33 | struct multiboot_tag base; 34 | u32 entry_size; 35 | u32 entry_version; 36 | struct multiboot_mmap_entry first_entry; 37 | }; 38 | 39 | // multiboot_init parses information provided by bootloader. It panics, if memory map tag found. 40 | void multiboot_init(); 41 | 42 | struct multiboot_tag* multiboot_lookup_tag(uint32_t tag_code); 43 | 44 | struct multiboot_mmap_iter { 45 | struct multiboot_mmap_entry* curr; 46 | }; 47 | 48 | // multiboot_mmap_iter_init creates new iterator over memory map regions provided by bootloader. 49 | void multiboot_mmap_iter_init(struct multiboot_mmap_iter* it); 50 | 51 | // multiboot_mmap_iter_next should return next memory region from given iterator. 52 | // If there are no more regions, return NULL instead. 53 | struct multiboot_mmap_entry* multiboot_mmap_iter_next(struct multiboot_mmap_iter* it); 54 | 55 | mem_region_t multiboot_mem_region(); 56 | -------------------------------------------------------------------------------- /homeworks/hw4/linker.ld: -------------------------------------------------------------------------------- 1 | ENTRY(_early_entry_point) 2 | 3 | #include "defs.h" 4 | 5 | SECTIONS 6 | { 7 | . = SIZEOF_HEADERS; 8 | 9 | .multiboot : ALIGN(4) { 10 | KEEP(*(.multiboot)) 11 | } 12 | 13 | . = 2M; 14 | 15 | _phys_start_kernel_sections = .; 16 | .early.text : { 17 | *(.early.text) 18 | } 19 | 20 | .early.rodata : { 21 | *(.early.rodata) 22 | } 23 | 24 | .early.data : { 25 | *(.early.data) 26 | } 27 | 28 | .early.bss : { 29 | *(.early.bss) 30 | } 31 | 32 | _phys_start_user = ALIGN(PAGE_SIZE); 33 | 34 | . = 0x10000; 35 | 36 | .user.text : AT(_phys_start_user) { 37 | *(.user.text) 38 | } 39 | _phys_end_user = _phys_start_user + . - 0x10000; 40 | 41 | . = _phys_end_user; 42 | . = ALIGN(0x200000); 43 | _phys_start_hh = .; 44 | 45 | . = KERNEL_SECTIONS_START; 46 | .text : AT(_phys_start_hh + ADDR(.text) - KERNEL_SECTIONS_START) { 47 | *(.text) 48 | } 49 | 50 | .rodata : AT(_phys_start_hh + ADDR(.rodata) - KERNEL_SECTIONS_START) { 51 | *(.rodata) 52 | } 53 | 54 | .data : AT(_phys_start_hh + ADDR(.data) - KERNEL_SECTIONS_START) { 55 | *(.data) 56 | } 57 | 58 | .bss : AT(_phys_start_hh + ADDR(.bss) - KERNEL_SECTIONS_START) { 59 | *(COMMON) 60 | *(.bss) 61 | } 62 | . = ALIGN(0x200000); 63 | PROVIDE(_phys_end_kernel_sections = _phys_start_hh + . - KERNEL_SECTIONS_START); 64 | 65 | /DISCARD/ : { 66 | *(.eh_frame) 67 | *(.note.gnu.*) 68 | *(.dynamic) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /homeworks/hw4/arch/x86/syscall.S: -------------------------------------------------------------------------------- 1 | .intel_syntax noprefix 2 | 3 | #include "gdt.h" 4 | #include "regs_asm.h" 5 | 6 | .extern _current 7 | .extern do_syscall 8 | 9 | .section .bss 10 | .Lsaved_rsp: 11 | .quad 0 12 | 13 | # This is an entry point for syscall instruction. 14 | # On enter, following holds: 15 | # rax contains syscall number; 16 | # rdi, rsi, rdx, rcx, r8, r9 contain syscall arguments (in order); 17 | # rcx contains userspace rip; 18 | # r11 contains userspace rflags; 19 | # rsp contains *userspace* stack (it may be corrupted or not mapped); 20 | # interrupts are disabled (IF set in IA32_FMASK). 21 | .section .text 22 | .global syscall_entry 23 | .type syscall_entry, @function 24 | syscall_entry: 25 | # We cannot use user-controlled rsp here: 26 | # No stack switch will be performed if exception or interrupt occurs here since we are already in ring0. 27 | # So, invalid rsp leads us to the double fault. 28 | mov qword ptr [.Lsaved_rsp], rsp 29 | # rsp = _current->arch_thread.kstack_top 30 | mov rsp, qword ptr [_current] 31 | mov rsp, qword ptr [rsp] 32 | 33 | # We have a reliable stack now, enable interrupts. 34 | sti 35 | 36 | # TODO: construct arch_regs_t on stack and call do_syscall. 37 | 38 | 39 | # Restore user-space rsp. 40 | mov rsp, qword ptr [.Lsaved_rsp] 41 | 42 | sysretq 43 | 44 | .global pop_and_iret 45 | .type pop_and_iret, @function 46 | pop_and_iret: 47 | POP_REGS 48 | # Skip error code. 49 | add rsp, 8 50 | iretq 51 | -------------------------------------------------------------------------------- /homeworks/hw4/kernel/kernel.c: -------------------------------------------------------------------------------- 1 | #include "arch/x86/x86.h" 2 | #include "boot/early.h" 3 | #include "drivers/acpi.h" 4 | #include "drivers/apic.h" 5 | #include "drivers/vga.h" 6 | #include "kernel/irq.h" 7 | #include "kernel/multiboot.h" 8 | #include "kernel/panic.h" 9 | #include "kernel/printk.h" 10 | #include "mm/frame_alloc.h" 11 | #include "mm/vmem.h" 12 | #include "sched/sched.h" 13 | #include "arch/x86/arch.h" 14 | 15 | void dump_mmap() { 16 | struct multiboot_mmap_iter mmap_iter; 17 | multiboot_mmap_iter_init(&mmap_iter); 18 | 19 | struct multiboot_mmap_entry* mmap_entry; 20 | while ((mmap_entry = multiboot_mmap_iter_next(&mmap_iter)) != NULL) { 21 | printk("mem region: "); 22 | switch (mmap_entry->type) { 23 | case MULTIBOOT_MMAP_TYPE_RAM: 24 | printk("[RAM] "); 25 | break; 26 | case MULTIBOOT_MMAP_TYPE_ACPI: 27 | printk("[ACPI] "); 28 | break; 29 | default: 30 | printk("[RESERVED] "); 31 | break; 32 | } 33 | printk(" base_addr=%p, len=%d\n", mmap_entry->base_addr, mmap_entry->length); 34 | } 35 | } 36 | 37 | void kernel_main_return() { 38 | panic("kernel_main returned, this should never happen"); 39 | } 40 | 41 | void kernel_main(early_data_t* early_data) { 42 | arch_init(); 43 | vga_init(); 44 | multiboot_init(early_data->multiboot_info); 45 | acpi_init(); 46 | apic_init(); 47 | irq_enable(); 48 | 49 | printk("Hello from higher-half!.\n"); 50 | dump_mmap(); 51 | 52 | frame_alloc_init(); 53 | 54 | sched_start(); 55 | } 56 | -------------------------------------------------------------------------------- /homeworks/hw2/multiboot.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define MULTIBOOT_MAGIC 0xE85250D6 4 | #define MULTIBOOT_ARCH_32 0 5 | 6 | #define MULTIBOOT_TAG_END 0 7 | #define MULTIBOOT_TAG_MMAP 6 8 | #define MULTIBOOT_TAG_ACPI_OLD_RSDP 14 9 | 10 | #define MULTIBOOT_MMAP_TYPE_RAM 1 11 | #define MULTIBOOT_MMAP_TYPE_ACPI 3 12 | 13 | 14 | #ifndef __ASSEMBLER__ 15 | 16 | #include "types.h" 17 | 18 | struct multiboot_mmap_entry { 19 | u64 base_addr; 20 | u64 length; 21 | u32 type; 22 | u32 reserved; 23 | }; 24 | 25 | struct multiboot_tag { 26 | u32 type; 27 | u32 size; 28 | char data[0]; 29 | }; 30 | 31 | struct multiboot_info { 32 | u32 total_size; 33 | u32 reserved; 34 | struct multiboot_tag first_tag; 35 | }; 36 | 37 | struct multiboot_mmap_tag { 38 | struct multiboot_tag base; 39 | u32 entry_size; 40 | u32 entry_version; 41 | struct multiboot_mmap_entry first_entry; 42 | }; 43 | 44 | // multiboot_init parses information provided by bootloader. It panics, if memory map tag found. 45 | void multiboot_init(); 46 | 47 | struct multiboot_tag* multiboot_lookup_tag(uint32_t tag_code); 48 | 49 | struct multiboot_mmap_iter { 50 | struct multiboot_mmap_entry* curr; 51 | }; 52 | 53 | // multiboot_mmap_iter_init creates new iterator over memory map regions provided by bootloader. 54 | void multiboot_mmap_iter_init(struct multiboot_mmap_iter* it); 55 | 56 | // multiboot_mmap_iter_next should return next memory region from given iterator. 57 | // If there are no more regions, return NULL instead. 58 | struct multiboot_mmap_entry* multiboot_mmap_iter_next(struct multiboot_mmap_iter* it); 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /lectures/00-intro/main.md: -------------------------------------------------------------------------------- 1 | --- 2 | marp: true 3 | --- 4 | 5 | # Вводная лекция 6 | 7 | --- 8 | 9 | # Про меня 10 | * Зовут Александр 11 | * Сейчас работаю в инфраструктуре Joom 12 | * До этого работал в Яндексе: сначала в поиске, потом в L7 балансере 13 | * Сфера интересов: системное программирование, операционные системы, компиляторы, распределённые системы 14 | 15 | --- 16 | 17 | # О чём курс? 18 | Будем изучать: 19 | * Процесс загрузки ОС (Multiboot 2) 20 | * Работу с виртуальной памятью 21 | * Работу с периферией (ACPI, PS/2, ATA, PCI, etc) 22 | * Multitasking & multiprocessing, планировщики ядер 23 | * Файловые системы 24 | * ...и ещё много интересных вещей 25 | 26 | --- 27 | 28 | # Организационная часть 29 | * Только лекции, семинаров нет 30 | * Проведение консультаций – по требованию 31 | * Я готов вам помогать с возникающими вопросами (https://t.me/carzil) 32 | * В зависимости от своего расписания отвечаю почти сразу, ближе к вечеру или в редких случаях могу ответить на следующий день 33 | * На курсе есть ассистент – Сергей Горбунов (https://t.me/eaglemango), ему тоже можно задавать вопросы! 34 | 35 | --- 36 | 37 | # Оценивание курса 38 | * За весь курс будет 5-6 штук, каждое оценивается в 10 баллов 39 | * Code review 40 | * Две попытки: до дедлайна и неделя на исправления 41 | * Оценка за курса = средневзешенное оценок домашек 42 | * Задачи "для души" = доп. баллы 43 | 44 | --- 45 | # Домашние задания 46 | * Два трека: "с нуля" и "доработка" 47 | * "С нуля" = пишите с нуля игрушечную ОС по мотивам лекций 48 | * Ограничение по языкам: C, C++, Rust. 49 | * "Доработка" = для каждой домашки будет выложен каркас, который нужно доработать 50 | 51 | --- 52 | 53 | # Репозиторий курса 54 | 55 | https://github.com/carzil/mipt-llp-2022 56 | 57 | --- 58 | 59 | # Спасибо! 60 | -------------------------------------------------------------------------------- /homeworks/hw4/mm/vmem.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "paging.h" 4 | #include "arch/x86/x86.h" 5 | 6 | #define VMEM_NO_FLAGS 0 7 | #define VMEM_USER (1 << 0) 8 | #define VMEM_WRITE (1 << 1) 9 | 10 | typedef struct vmem_area { 11 | void* start; 12 | size_t pgcnt; 13 | uint64_t flags; 14 | struct vmem_area* next; 15 | } vmem_area_t; 16 | 17 | // vmem_t is used to manage an address space, including all its page tables. 18 | typedef struct vmem { 19 | pml4_t* pml4; 20 | vmem_area_t* areas_head; 21 | } vmem_t; 22 | 23 | // vmem_init_from_cr3 initializes vmem from current address space. 24 | void vmem_init_from_current(vmem_t* vm); 25 | 26 | // vmem_init_new initialized new vmem. 27 | int vmem_init_new(vmem_t* vm); 28 | 29 | // vmem_destroy destroys givem vmem and releases all allocated areas. 30 | void vmem_destroy(vmem_t* vm); 31 | 32 | // vmem_switch_to switches to the specified address space. 33 | void vmem_switch_to(vmem_t* vm); 34 | 35 | // vmem_alloc_pages allocates pgcnt frames and then maps it to the virtual address virt_addr. 36 | int vmem_alloc_pages(vmem_t* vm, void* virt_addr, size_t pgcnt, uint64_t flags); 37 | 38 | // vmem_map_1gb_page maps 1GB page to specified virt_addr. 39 | int vmem_map_1gb_page(vmem_t* vm, void* virt_addr, void* phys_addr, uint64_t flags); 40 | 41 | // vmem_map_2mb_page maps 2mb page to specified virt_addr. 42 | int vmem_map_2mb_page(vmem_t* vm, void* virt_addr, void* phys_addr, uint64_t flags); 43 | 44 | // vmem_map_page maps single page to specified virt_addr with given PTE flags. 45 | int vmem_map_page(vmem_t* vm, void* virt_addr, void* phys_addr, uint64_t flags); 46 | 47 | // vmem_copy_from_current copies all allocated areas from curr to dst, assuming that curr is an active address space. 48 | int vmem_clone_from_current(vmem_t* dst, vmem_t* curr); 49 | -------------------------------------------------------------------------------- /homeworks/hw3/mm/vmem.c: -------------------------------------------------------------------------------- 1 | #include "vmem.h" 2 | #include "frame_alloc.h" 3 | #include "paging.h" 4 | 5 | static void vmem_map_page(vmem_t* vm, void* virt_addr, void* frame) { 6 | uint64_t pml4e = vm->pml4->entries[PML4E_FROM_ADDR(virt_addr)]; 7 | pdpt_t* pdpt = NULL; 8 | if (!(pml4e & PTE_PRESENT)) { 9 | pdpt = frames_alloc(1); 10 | pml4e = vm->pml4->entries[PML4E_FROM_ADDR(virt_addr)] = (uint64_t)VIRT_TO_PHYS(pdpt) | PTE_PRESENT; 11 | } else { 12 | pdpt = PHYS_TO_VIRT(PTE_ADDR(pml4e)); 13 | } 14 | 15 | uint64_t pdpe = pdpt->entries[PDPE_FROM_ADDR(virt_addr)]; 16 | pgdir_t* pgdir = NULL; 17 | if (!(pdpe & PTE_PRESENT)) { 18 | pgdir = frames_alloc(1); 19 | pdpe = pdpt->entries[PDPE_FROM_ADDR(virt_addr)] = (uint64_t)VIRT_TO_PHYS(pgdir) | PTE_PRESENT; 20 | } else { 21 | pgdir = PHYS_TO_VIRT(PTE_ADDR(pdpe)); 22 | } 23 | 24 | uint64_t pde = pgdir->entries[PDE_FROM_ADDR(virt_addr)]; 25 | pgtbl_t* pgtbl = NULL; 26 | if (!(pde & PTE_PRESENT)) { 27 | pgtbl = frames_alloc(1); 28 | pdpe = pgdir->entries[PDE_FROM_ADDR(virt_addr)] = (uint64_t)VIRT_TO_PHYS(pgtbl) | PTE_PRESENT; 29 | } else { 30 | pgtbl = PHYS_TO_VIRT(PTE_ADDR(pde)); 31 | } 32 | 33 | pgtbl->entries[PTE_FROM_ADDR(virt_addr)] = (uint64_t)VIRT_TO_PHYS(frame) | PTE_PRESENT; 34 | } 35 | 36 | void vmem_alloc_pages(vmem_t* vm, void* virt_addr, size_t pgcnt) { 37 | while (pgcnt > 0) { 38 | void* frame = frames_alloc(1); 39 | vmem_map_page(vm, virt_addr, frame); 40 | virt_addr += PAGE_SIZE; 41 | pgcnt--; 42 | } 43 | } 44 | 45 | void vmem_init_from_current(vmem_t* vm) { 46 | vm->pml4 = PHYS_TO_VIRT(x86_read_cr3()); 47 | } 48 | 49 | void vmem_switch_to(vmem_t* vm) { 50 | x86_write_cr3((uint64_t)VIRT_TO_PHYS(vm->pml4)); 51 | } 52 | -------------------------------------------------------------------------------- /homeworks/hw2/multiboot.c: -------------------------------------------------------------------------------- 1 | #include "multiboot.h" 2 | #include "printk.h" 3 | #include "types.h" 4 | #include "panic.h" 5 | 6 | extern uint32_t _multiboot_info; 7 | static struct multiboot_mmap_tag* mmap_tag; 8 | 9 | static struct multiboot_info* multiboot_info() { 10 | return (struct multiboot_info*)(uint64_t)_multiboot_info; 11 | } 12 | 13 | struct multiboot_tag* multiboot_lookup_tag(uint32_t tag_code) { 14 | struct multiboot_tag* tag = &multiboot_info()->first_tag; 15 | for (;;) { 16 | if (tag->type == MULTIBOOT_TAG_END) { 17 | return NULL; 18 | } 19 | 20 | if (tag->type == tag_code) { 21 | break; 22 | } 23 | 24 | // Size doesn't include padding, so fix it to be aligned. 25 | u32 sz = tag->size; 26 | if ((tag->size & 0b111) != 0) { 27 | sz = (sz + 8) & ~0b111; 28 | } 29 | 30 | tag = (struct multiboot_tag*)(((u8*)tag) + sz); 31 | } 32 | return tag; 33 | } 34 | 35 | void multiboot_init() { 36 | mmap_tag = (struct multiboot_mmap_tag*)multiboot_lookup_tag(MULTIBOOT_TAG_MMAP); 37 | if (mmap_tag == NULL) { 38 | panic("bootloader didn't provide memory map"); 39 | } 40 | 41 | printk("parsed multiboot info at %p, total_size=%d\n", _multiboot_info, multiboot_info()->total_size); 42 | } 43 | 44 | void multiboot_mmap_iter_init(struct multiboot_mmap_iter* it) { 45 | it->curr = NULL; 46 | } 47 | 48 | struct multiboot_mmap_entry* multiboot_mmap_iter_next(struct multiboot_mmap_iter* it) { 49 | if (it->curr == NULL) { 50 | it->curr = &mmap_tag->first_entry; 51 | } else { 52 | it->curr = (struct multiboot_mmap_entry*)((u8*)it->curr + mmap_tag->entry_size); 53 | } 54 | 55 | if ((u8*)it->curr - (u8*)mmap_tag >= mmap_tag->base.size) { 56 | return NULL; 57 | } 58 | 59 | return it->curr; 60 | } 61 | -------------------------------------------------------------------------------- /homeworks/hw1/vga.c: -------------------------------------------------------------------------------- 1 | #include "vga.h" 2 | 3 | static const u64 VGA_WIDTH = 80; 4 | static const u64 VGA_HEIGHT = 25; 5 | 6 | static u64 vga_row; 7 | static u64 vga_column; 8 | static volatile u16* vga_buffer; 9 | 10 | static inline u64 vga_index(u64 row, u64 col) { 11 | return row * VGA_WIDTH + col; 12 | } 13 | 14 | void vga_init(void) { 15 | vga_row = 0; 16 | vga_column = 0; 17 | u8 color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK); 18 | vga_buffer = (u16*)0xB8000; 19 | for (u64 row = 0; row < VGA_HEIGHT; row++) { 20 | for (u64 col = 0; col < VGA_WIDTH; col++) { 21 | vga_buffer[vga_index(row, col)] = vga_entry(' ', color); 22 | } 23 | } 24 | } 25 | 26 | void vga_putentryat(char c, u8 color, u64 row, u64 col) { 27 | vga_buffer[vga_index(row, col)] = vga_entry(c, color); 28 | } 29 | 30 | // TODO: vga_newline_and_return should support terminal scrolling. 31 | static void vga_newline_and_return() { 32 | vga_row++; 33 | if (vga_row == VGA_HEIGHT) { 34 | vga_row = 0; 35 | } 36 | vga_column = 0; 37 | } 38 | 39 | void vga_putchar_color(char c, u8 color) { 40 | switch (c) { 41 | case '\n': 42 | vga_newline_and_return(); 43 | break; 44 | 45 | case '\r': 46 | vga_column = 0; 47 | break; 48 | 49 | default: 50 | vga_putentryat(c, color, vga_row, vga_column); 51 | vga_column++; 52 | if (vga_column == VGA_WIDTH) { 53 | vga_newline_and_return(); 54 | } 55 | } 56 | } 57 | 58 | void vga_write(const char* data, size_t size, u8 color) { 59 | for (u64 i = 0; i < size; i++) { 60 | vga_putchar_color(data[i], color); 61 | } 62 | } 63 | 64 | void vga_writestring(const char* data) { 65 | vga_write(data, strlen(data), vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK)); 66 | } 67 | 68 | void vga_writestring_color(const char* data, u8 color) { 69 | vga_write(data, strlen(data), color); 70 | } 71 | -------------------------------------------------------------------------------- /homeworks/hw3/kernel/multiboot.c: -------------------------------------------------------------------------------- 1 | #include "multiboot.h" 2 | #include "kernel/printk.h" 3 | #include "types.h" 4 | #include "mm/paging.h" 5 | #include "kernel/panic.h" 6 | 7 | static struct multiboot_info* multiboot_info; 8 | static struct multiboot_mmap_tag* mmap_tag; 9 | 10 | struct multiboot_tag* multiboot_lookup_tag(uint32_t tag_code) { 11 | struct multiboot_tag* tag = &multiboot_info->first_tag; 12 | for (;;) { 13 | if (tag->type == MULTIBOOT_TAG_END) { 14 | return NULL; 15 | } 16 | 17 | if (tag->type == tag_code) { 18 | break; 19 | } 20 | 21 | // Size doesn't include padding, so fix it to be aligned. 22 | u32 sz = ALIGN_UP(tag->size, 8); 23 | 24 | tag = (struct multiboot_tag*)(((u8*)tag) + sz); 25 | } 26 | return tag; 27 | } 28 | 29 | void multiboot_init(struct multiboot_info* info) { 30 | multiboot_info = info; 31 | mmap_tag = (struct multiboot_mmap_tag*)multiboot_lookup_tag(MULTIBOOT_TAG_MMAP); 32 | if (mmap_tag == NULL) { 33 | panic("bootloader didn't provide memory map"); 34 | } 35 | 36 | printk("parsed multiboot info at %p, total_size=%d\n", multiboot_info, multiboot_info->total_size); 37 | } 38 | 39 | void multiboot_mmap_iter_init(struct multiboot_mmap_iter* it) { 40 | it->curr = NULL; 41 | } 42 | 43 | struct multiboot_mmap_entry* multiboot_mmap_iter_next(struct multiboot_mmap_iter* it) { 44 | if (it->curr == NULL) { 45 | it->curr = &mmap_tag->first_entry; 46 | } else { 47 | it->curr = (struct multiboot_mmap_entry*)((u8*)it->curr + mmap_tag->entry_size); 48 | } 49 | 50 | if ((u8*)it->curr - (u8*)mmap_tag >= mmap_tag->base.size) { 51 | return NULL; 52 | } 53 | 54 | return it->curr; 55 | } 56 | 57 | mem_region_t multiboot_mem_region() { 58 | mem_region_t res = { 59 | .start = multiboot_info, 60 | .end = multiboot_info + ALIGN_UP(multiboot_info->total_size, PAGE_SIZE), 61 | }; 62 | return res; 63 | } 64 | -------------------------------------------------------------------------------- /homeworks/hw4/kernel/multiboot.c: -------------------------------------------------------------------------------- 1 | #include "multiboot.h" 2 | #include "kernel/printk.h" 3 | #include "types.h" 4 | #include "mm/paging.h" 5 | #include "kernel/panic.h" 6 | 7 | static struct multiboot_info* multiboot_info; 8 | static struct multiboot_mmap_tag* mmap_tag; 9 | 10 | struct multiboot_tag* multiboot_lookup_tag(uint32_t tag_code) { 11 | struct multiboot_tag* tag = &multiboot_info->first_tag; 12 | for (;;) { 13 | if (tag->type == MULTIBOOT_TAG_END) { 14 | return NULL; 15 | } 16 | 17 | if (tag->type == tag_code) { 18 | break; 19 | } 20 | 21 | // Size doesn't include padding, so fix it to be aligned. 22 | u32 sz = ALIGN_UP(tag->size, 8); 23 | 24 | tag = (struct multiboot_tag*)(((u8*)tag) + sz); 25 | } 26 | return tag; 27 | } 28 | 29 | void multiboot_init(struct multiboot_info* info) { 30 | multiboot_info = info; 31 | mmap_tag = (struct multiboot_mmap_tag*)multiboot_lookup_tag(MULTIBOOT_TAG_MMAP); 32 | if (mmap_tag == NULL) { 33 | panic("bootloader didn't provide memory map"); 34 | } 35 | 36 | printk("parsed multiboot info at %p, total_size=%d\n", multiboot_info, multiboot_info->total_size); 37 | } 38 | 39 | void multiboot_mmap_iter_init(struct multiboot_mmap_iter* it) { 40 | it->curr = NULL; 41 | } 42 | 43 | struct multiboot_mmap_entry* multiboot_mmap_iter_next(struct multiboot_mmap_iter* it) { 44 | if (it->curr == NULL) { 45 | it->curr = &mmap_tag->first_entry; 46 | } else { 47 | it->curr = (struct multiboot_mmap_entry*)((u8*)it->curr + mmap_tag->entry_size); 48 | } 49 | 50 | if ((u8*)it->curr - (u8*)mmap_tag >= mmap_tag->base.size) { 51 | return NULL; 52 | } 53 | 54 | return it->curr; 55 | } 56 | 57 | mem_region_t multiboot_mem_region() { 58 | mem_region_t res = { 59 | .start = multiboot_info, 60 | .end = multiboot_info + ALIGN_UP(multiboot_info->total_size, PAGE_SIZE), 61 | }; 62 | return res; 63 | } 64 | -------------------------------------------------------------------------------- /homeworks/hw4/arch/x86/irq.c: -------------------------------------------------------------------------------- 1 | #include "irq.h" 2 | #include "kernel/panic.h" 3 | #include "sched/sched.h" 4 | #include "arch/x86/x86.h" 5 | #include "arch/x86/arch.h" 6 | #include "drivers/apic.h" 7 | 8 | void timer_handler() { 9 | apic_eoi(); 10 | sched_timer_tick(); 11 | } 12 | 13 | void spurious_handler() { 14 | apic_eoi(); 15 | } 16 | 17 | #define PF_ERRCODE_P (1<<0) 18 | #define PF_ERRCODE_W (1<<1) 19 | #define PF_ERRCODE_U (1<<2) 20 | #define PF_ERRCODE_RSVD (1<<3) 21 | #define PF_ERRCODE_I (1<<4) 22 | #define PF_ERRCODE_PK (1<<5) 23 | 24 | void pf_handler(arch_regs_t* ctx) { 25 | char reason[64]; 26 | size_t pos = 0; 27 | 28 | #define WR(f, msg) \ 29 | if (ctx->errcode & (f)) { \ 30 | const char* __msg = msg; \ 31 | while (*__msg != '\0') { \ 32 | reason[pos++] = *__msg; \ 33 | __msg++; \ 34 | } \ 35 | reason[pos++] = ' '; \ 36 | } 37 | 38 | WR(PF_ERRCODE_P, "P"); 39 | WR(PF_ERRCODE_W, "W") 40 | WR(PF_ERRCODE_U, "U") 41 | WR(PF_ERRCODE_RSVD, "RSVD") 42 | WR(PF_ERRCODE_I, "I") 43 | WR(PF_ERRCODE_PK, "P") 44 | #undef WR 45 | if (pos > 0) { 46 | reason[pos - 1] = '\0'; 47 | } 48 | panic("pagefault [%s]: addr=%p, rip=%p", reason, x86_read_cr2(), ctx->rip); 49 | } 50 | 51 | void gp_handler(arch_regs_t* ctx) { 52 | panic("#GP(%x) at rip=%p", ctx->errcode, ctx->rip); 53 | } 54 | 55 | void np_handler(arch_regs_t* ctx) { 56 | panic("#NP(%x) at rip=%p", ctx->errcode, ctx->rip); 57 | } 58 | 59 | void ts_handler(arch_regs_t* ctx) { 60 | panic("#TS(%x) at rip=%p", ctx->errcode, ctx->rip); 61 | } 62 | 63 | void ss_handler(arch_regs_t* ctx) { 64 | panic("#SS(%x) at rip=%p", ctx->errcode, ctx->rip); 65 | } 66 | 67 | void df_handler(arch_regs_t* ctx) { 68 | panic("#DF(0) at rip=%p", ctx->errcode, ctx->rip); 69 | } 70 | 71 | void ud_handler(arch_regs_t* ctx) { 72 | panic("#UD(%x) at rip=%p", ctx->errcode, ctx->rip); 73 | } 74 | 75 | void nm_handler(arch_regs_t* ctx) { 76 | panic("#NM at rip=%p", ctx->rip); 77 | } 78 | -------------------------------------------------------------------------------- /homeworks/hw2/vga.c: -------------------------------------------------------------------------------- 1 | #include "vga.h" 2 | 3 | static const u64 VGA_WIDTH = 80; 4 | static const u64 VGA_HEIGHT = 25; 5 | 6 | static u64 vga_row; 7 | static u64 vga_column; 8 | static volatile u16* vga_buffer; 9 | 10 | static inline u64 vga_index(u64 row, u64 col) { 11 | return row * VGA_WIDTH + col; 12 | } 13 | 14 | void vga_init(void) { 15 | vga_row = 0; 16 | vga_column = 0; 17 | u8 color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK); 18 | vga_buffer = (u16*)0xB8000; 19 | for (u64 row = 0; row < VGA_HEIGHT; row++) { 20 | for (u64 col = 0; col < VGA_WIDTH; col++) { 21 | vga_buffer[vga_index(row, col)] = vga_entry(' ', color); 22 | } 23 | } 24 | } 25 | 26 | void vga_putentryat(char c, u8 color, u64 row, u64 col) { 27 | vga_buffer[vga_index(row, col)] = vga_entry(c, color); 28 | } 29 | 30 | // TODO: vga_newline_and_return should support terminal scrolling. 31 | static void vga_newline_and_return() { 32 | vga_row++; 33 | if (vga_row == VGA_HEIGHT) { 34 | for (u64 row = 0; row < VGA_HEIGHT - 1; row++) { 35 | for (u64 col = 0; col < VGA_WIDTH; col++) { 36 | vga_buffer[vga_index(row, col)] = vga_buffer[vga_index(row + 1, col)]; 37 | } 38 | } 39 | for (u64 col = 0; col < VGA_WIDTH; col++) { 40 | vga_buffer[vga_index(VGA_HEIGHT - 1, col)] = 0; 41 | } 42 | vga_row = VGA_HEIGHT - 1; 43 | } 44 | vga_column = 0; 45 | } 46 | 47 | void vga_putchar_color(char c, u8 color) { 48 | switch (c) { 49 | case '\n': 50 | vga_newline_and_return(); 51 | break; 52 | 53 | case '\r': 54 | vga_column = 0; 55 | break; 56 | 57 | default: 58 | vga_putentryat(c, color, vga_row, vga_column); 59 | vga_column++; 60 | if (vga_column == VGA_WIDTH) { 61 | vga_newline_and_return(); 62 | } 63 | } 64 | } 65 | 66 | void vga_write(const char* data, size_t size, u8 color) { 67 | for (u64 i = 0; i < size; i++) { 68 | vga_putchar_color(data[i], color); 69 | } 70 | } 71 | 72 | void vga_writestring(const char* data) { 73 | vga_write(data, strlen(data), vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK)); 74 | } 75 | 76 | void vga_writestring_color(const char* data, u8 color) { 77 | vga_write(data, strlen(data), color); 78 | } 79 | -------------------------------------------------------------------------------- /homeworks/hw3/drivers/vga.c: -------------------------------------------------------------------------------- 1 | #include "vga.h" 2 | #include "mm/paging.h" 3 | 4 | static const u64 VGA_WIDTH = 80; 5 | static const u64 VGA_HEIGHT = 25; 6 | 7 | static u64 vga_row; 8 | static u64 vga_column; 9 | static volatile u16* vga_buffer; 10 | 11 | static inline u64 vga_index(u64 row, u64 col) { 12 | return row * VGA_WIDTH + col; 13 | } 14 | 15 | void vga_init(void) { 16 | vga_row = 0; 17 | vga_column = 0; 18 | u8 color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK); 19 | vga_buffer = PHYS_TO_VIRT((u16*)0xB8000); 20 | for (u64 row = 0; row < VGA_HEIGHT; row++) { 21 | for (u64 col = 0; col < VGA_WIDTH; col++) { 22 | vga_buffer[vga_index(row, col)] = vga_entry(' ', color); 23 | } 24 | } 25 | } 26 | 27 | void vga_putentryat(char c, u8 color, u64 row, u64 col) { 28 | vga_buffer[vga_index(row, col)] = vga_entry(c, color); 29 | } 30 | 31 | // TODO: vga_newline_and_return should support terminal scrolling. 32 | static void vga_newline_and_return() { 33 | vga_row++; 34 | if (vga_row == VGA_HEIGHT) { 35 | for (u64 row = 0; row < VGA_HEIGHT - 1; row++) { 36 | for (u64 col = 0; col < VGA_WIDTH; col++) { 37 | vga_buffer[vga_index(row, col)] = vga_buffer[vga_index(row + 1, col)]; 38 | } 39 | } 40 | for (u64 col = 0; col < VGA_WIDTH; col++) { 41 | vga_buffer[vga_index(VGA_HEIGHT - 1, col)] = 0; 42 | } 43 | vga_row = VGA_HEIGHT - 1; 44 | } 45 | vga_column = 0; 46 | } 47 | 48 | void vga_putchar_color(char c, u8 color) { 49 | switch (c) { 50 | case '\n': 51 | vga_newline_and_return(); 52 | break; 53 | 54 | case '\r': 55 | vga_column = 0; 56 | break; 57 | 58 | default: 59 | vga_putentryat(c, color, vga_row, vga_column); 60 | vga_column++; 61 | if (vga_column == VGA_WIDTH) { 62 | vga_newline_and_return(); 63 | } 64 | } 65 | } 66 | 67 | void vga_write(const char* data, size_t size, u8 color) { 68 | for (u64 i = 0; i < size; i++) { 69 | vga_putchar_color(data[i], color); 70 | } 71 | } 72 | 73 | void vga_writestring(const char* data) { 74 | vga_write(data, strlen(data), vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK)); 75 | } 76 | 77 | void vga_writestring_color(const char* data, u8 color) { 78 | vga_write(data, strlen(data), color); 79 | } 80 | -------------------------------------------------------------------------------- /homeworks/hw4/drivers/vga.c: -------------------------------------------------------------------------------- 1 | #include "vga.h" 2 | #include "mm/paging.h" 3 | 4 | static const u64 VGA_WIDTH = 80; 5 | static const u64 VGA_HEIGHT = 25; 6 | 7 | static u64 vga_row; 8 | static u64 vga_column; 9 | static volatile u16* vga_buffer; 10 | 11 | static inline u64 vga_index(u64 row, u64 col) { 12 | return row * VGA_WIDTH + col; 13 | } 14 | 15 | void vga_init(void) { 16 | vga_row = 0; 17 | vga_column = 0; 18 | u8 color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK); 19 | vga_buffer = PHYS_TO_VIRT((u16*)0xB8000); 20 | for (u64 row = 0; row < VGA_HEIGHT; row++) { 21 | for (u64 col = 0; col < VGA_WIDTH; col++) { 22 | vga_buffer[vga_index(row, col)] = vga_entry(' ', color); 23 | } 24 | } 25 | } 26 | 27 | void vga_putentryat(char c, u8 color, u64 row, u64 col) { 28 | vga_buffer[vga_index(row, col)] = vga_entry(c, color); 29 | } 30 | 31 | // TODO: vga_newline_and_return should support terminal scrolling. 32 | static void vga_newline_and_return() { 33 | vga_row++; 34 | if (vga_row == VGA_HEIGHT) { 35 | for (u64 row = 0; row < VGA_HEIGHT - 1; row++) { 36 | for (u64 col = 0; col < VGA_WIDTH; col++) { 37 | vga_buffer[vga_index(row, col)] = vga_buffer[vga_index(row + 1, col)]; 38 | } 39 | } 40 | for (u64 col = 0; col < VGA_WIDTH; col++) { 41 | vga_buffer[vga_index(VGA_HEIGHT - 1, col)] = 0; 42 | } 43 | vga_row = VGA_HEIGHT - 1; 44 | } 45 | vga_column = 0; 46 | } 47 | 48 | void vga_putchar_color(char c, u8 color) { 49 | switch (c) { 50 | case '\n': 51 | vga_newline_and_return(); 52 | break; 53 | 54 | case '\r': 55 | vga_column = 0; 56 | break; 57 | 58 | default: 59 | vga_putentryat(c, color, vga_row, vga_column); 60 | vga_column++; 61 | if (vga_column == VGA_WIDTH) { 62 | vga_newline_and_return(); 63 | } 64 | } 65 | } 66 | 67 | void vga_write(const char* data, size_t size, u8 color) { 68 | for (u64 i = 0; i < size; i++) { 69 | vga_putchar_color(data[i], color); 70 | } 71 | } 72 | 73 | void vga_writestring(const char* data) { 74 | vga_write(data, strlen(data), vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK)); 75 | } 76 | 77 | void vga_writestring_color(const char* data, u8 color) { 78 | vga_write(data, strlen(data), color); 79 | } 80 | -------------------------------------------------------------------------------- /homeworks/hw2/README.md: -------------------------------------------------------------------------------- 1 | # Валидация ACPI таблиц (1 балл) 2 | [Спецификация ACPI](https://uefi.org/sites/default/files/resources/ACPI_6_3_final_Jan30.pdf) определяет чексумму каждой таблицы таким образом, что если сложить все байты таблицы как `uint8_t` (вместе с полем checksum), то должен получиться `0`. 3 | 4 | Ваша задача — добавить проверку в `acpi_init` (внутри [`acpi.c`](./acpi.c)), что для всех корневых таблиц чексумма корректна. Если для какой-то таблицы чексумма не корректна, то вызвать `panic`, а также вывести сигнатуру таблицы (обратите внимание, что это необязательно null-terminated строка). 5 | 6 | # Работа с контекстом прерывания (1 балл) 7 | В [`irq.S`](./irq.S) есть макрос `IRQ_ENTRY`, который генерирует входные точки для interrupt handler'ов. Сгенерированный код сохраняет контекст предыдущего состояния процессора на стек и передаёт управление в код обработчика. 8 | 9 | В этом задании вам предлагается обработать некоторые прерывания процессора, которые перечислены в [`irq.c`](./irq.c) (добавлять оставшиеся не запрещается :slightly_smiling_face:) и вывести информацию о состоянии процессора **на момент возникновения исключения**. 10 | 11 | Для успешного выполнения этого задания должна быть выведена следующая информация: 12 | * состояние всех general purpose регистров; 13 | * RIP; 14 | * error code, если он есть; 15 | * для `#PF` вывести адрес, при обращени к которому произошёл fault (его можно получить из регистра `CR2`, загляните в [`x86.h`](./x86.h)). 16 | 17 | Обратите внимание, что размер фрейма прерывания отличается для исключений с error code и без. 18 | 19 | # Калибровка APIC таймера (1 балл) 20 | Сейчас в `apic.c` указано рандомное число тиков, поэтому время между соседними прерываниями таймера неизвестно. В дальшнейшем нам потребуется знать, сколько времени прошло. Например, чтобы обрабатывать насильные переключения контекста раз в несколько миллисекунд, или пользовательские таймеры (`usleep/sleep`). 21 | 22 | Для этого вам потребуется некоторый источник времени, который гарантированно отрабатывает за заданное время. Здесь есть несколько вариантов, можно использовать [Programmable Interval Timer](https://wiki.osdev.org/Programmable_Interval_Timer) или [подсмотреть](https://github.com/torvalds/linux/blob/7cf726a59435301046250c42131554d9ccc566b8/arch/x86/kernel/apic/apic.c#L825), как калибровка реализована в Linux. 23 | 24 | В этом задании вам предлагается настроить таймер APIC таким образом, чтобы время между соседними прерываниями составляло 1 миллисекунду. Реализация может быть любой. 25 | -------------------------------------------------------------------------------- /homeworks/hw1/README.md: -------------------------------------------------------------------------------- 1 | # Функция `panic` (1 балл) 2 | В HeLL OS есть макросы `BUG_ON` и `BUG_ON_NULL` (реализация в [panic.h](panic.h)). Их можно (и нужно!) использовать в случаях, если вы ожидаете какие-то инварианты кода. Например, если ожидается, что какой-то указатель всегда ненулевой, стоит написать перед этим `BUG_ON_NULL(ptr)`. Если переданный им предикат не выполняются, эти функции аварийно завершат ядро. Однако, что такое аварийное завершение ядра? Мы не можем просто отключить питание — тогда пользователь не увидит сообщение об ошибке. Поэтому ядру нужно просто "зависнуть" на одном месте. За это должна отвечать функция `__panic`: она принимает на вход уже форматированное сообщение об ошибке, которое должна выести на экран, а затем зависнуть в бесконечном цикле. В этом задании вам предлагается реализовать эту функцию в файле [panic.c](panic.c). Бесконечный цикл должен быть организован также, как в конце `boot.S` через инструкцию `hlt`. Для этого вам понадобится [inline assembly](https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html). 3 | 4 | # Парсинг регионов памяти из Multiboot2 (1 балл) 5 | После загрузки ядра bootloader'ом, процессор оказывается в самом начале функции `_start` (в [boot.S](boot.S)). Он имеет определённое состоние, которое описано в [спецификации Multiboot2](https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html), секция "I386 machine state". Например, `eax` всегда содержит специальное магическое значение `0x36D76289`, а `ebx` содержит указатель на структуру, которая сгенерированная загрузчиком. В этой структуре есть довольно много всякой полезной информации, однако, нас пока будет интересовать информация о доступной памяти. Цель этого задания — прочитать документацию и реализовать read-only итератор, который позволяет проходить по всем регионам памяти. Интерфейс итератора можно найти в файле [multiboot.h](multiboot.h), пример использования — в [kernel.c](kernel.c). Обратите внимание на комментарий перед `multiboot_init` – эта функция должна паниковать, если загрузчиком не предоставлена информация о регионах памяти. 6 | 7 | # Скроллинг терминала (1 балл) 8 | Если сейчас вы попытаетесь вывести больше 25 строчек c помощью `printk`, то самые верхние начнут перезатираться. Это неудобно и нечитаемо. Поэтому в этом задании вам предлагается добавить возможность скроллить строки терминала: то есть, если появляется новая строчка, самая первая строчка "уезжает наверх", все строчки потягиваются на одну вверх, а новая занимает место последней. Пролистывание мышкой/клавишами пока реализовывать не нужно: для этого нужны прерывания, мы пока не умеем пользоваться ими. Функции для изменения находятся в файле [vga.c](vga.c). 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Архитектура операционных систем, МФТИ, 2022 2 | 3 | ## Полезные ссылки 4 | * [Гайд по разработке](GUIDE.md) 5 | * [Intel Software Developer Manual](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html) 6 | * [OSDev Wiki](https://wiki.osdev.org/Main_Page) 7 | * [Multiboot 2 specification](https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html) 8 | * [Using GNU as](https://sourceware.org/binutils/docs/as/) 9 | * [Extended Asm - Assembler Instructions with C Expression Operands](https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html) 10 | * [GDB docs](https://sourceware.org/gdb/onlinedocs/gdb/index.html) 11 | * [QEMU docs](https://qemu-project.gitlab.io/qemu/system/monitor.html) 12 | 13 | ## Лекции 14 | 1. [Вводная](lectures/00-intro/main.pdf). 15 | 1. [Обзор x86-64](lectures/01-x86-64-overview/main.pdf). 16 | 1. Cеминар. Multiboot2 и загрузка ОС. 17 | 1. [Прерывания процессора](lectures/03-interrupts/main.pdf). 18 | 1. Cеминар. Прерывания процессора, ACPI, APIC. 19 | 1. [Управление памятью](lectures/05-memory-management/main.pdf). 20 | 1. [Процессы и переключение контекста](lectures/07-tasks/main.md) 21 | 1. Семинар. 22 | 1. [Мультипроцессорность и модель памяти x86](lectures/09-mp/main.pdf) 23 | 1. Семинар. 24 | 1. [Шины и диски](lectures/11-buses-and-drivers/main.pdf) 25 | 1. [Блочные устроства, файловые системы и VFS](lectures/12-file-systems/main.pdf) 26 | 27 | ## Домашние задания 28 | 29 | [Табличка с результатами проверки ДЗ](https://docs.google.com/spreadsheets/d/1nR7B1dQMwmAK9gzDFhuoJcE5jKRsJr6OF5HHe2WJEBo/edit?usp=sharing). 30 | 31 | 1. [Домашнее задание №1](homeworks/hw1/README.md), ***дедлайн: 04.03.2022 23:59***. 32 | 1. [Домашнее задание №2](homeworks/hw2/README.md), ***дедлайн: 27.03.2022 23:59***. 33 | 1. [Домашнее задание №3](homeworks/hw3/README.md), ***дедлайн: 15.04.2022 23:59***. 34 | 1. [Домашнее задание №4](homeworks/hw4/README.md), ***дедлайн: 12.05.2022, 23:59***. 35 | 36 | ### Оформление домашних заданий 37 | Заведите себе отдельный *приватный* репозиторий на Github добавляете в коллабораторы @carzil и @eaglemango, затем заполните [форму](https://forms.gle/7o2HqnS2MthsdUXp7). 38 | * Если вы дописываете код, копируйте все файлы в отдельную директорию в вашем репозитории (`hw1`, `hw2`, ...), затем дописываете код домашки и делаете Pull request. Если не умеете этим пользоваться — пишите в чат, научим. 39 | * Если вы пишете с нуля, то структура всего на ваш вкус. Однако требования про PR'ы сохраняется: каждая домашка — отдельный PR. 40 | * PR должен быть создан ДО дедлайна. PR'ы созданые после ***учитываться не будут*** 41 | * Если во время проверки вашего ДЗ будут выявлены недочёты, то в вашем PR появятся комментарии. На их исправление у вас будет ровно ***неделя*** после публикации ревью. Исправления не влияют на итоговую оценку. 42 | -------------------------------------------------------------------------------- /homeworks/hw4/arch/x86/irq.S: -------------------------------------------------------------------------------- 1 | #include "irq.h" 2 | #include "regs_asm.h" 3 | 4 | .intel_syntax noprefix 5 | 6 | .macro IDT_ENTRY vec entry flags 7 | // rbx = &IDT[vec] 8 | lea rbx, [rip + .Lidt + \vec*16] 9 | 10 | // rax = (0x08 << 16) | (entry & 0xFFFF) 11 | lea rax, [rip + _irq_entry_\entry] 12 | and eax, 0xFFFF 13 | or eax, 0x08 << 16 14 | mov dword ptr [rbx], eax 15 | 16 | // rax = (entry & 0xFFFF0000) | (flags << 8) 17 | lea rax, [rip + _irq_entry_\entry] 18 | and eax, 0xFFFF0000 19 | or eax, (\flags << 8) 20 | mov dword ptr [rbx + 4], eax 21 | 22 | // rax = entry >> 32 23 | lea rax, [rip + _irq_entry_\entry] 24 | shr rax, 32 25 | mov dword ptr [rbx + 8], eax 26 | 27 | mov dword ptr [rbx + 16], 0 28 | .endm 29 | 30 | .macro IRQ_ENTRY push_errcode entry 31 | .section .text 32 | .extern \entry 33 | .global _irq_entry_\entry 34 | .type _irq_entry_\entry, @function 35 | .align 16 36 | _irq_entry_\entry: 37 | # Emulate error code on stack. 38 | .ifeq \push_errcode 39 | pushq -1 40 | .endif 41 | 42 | PUSH_REGS 43 | 44 | # Pass pointer for arch_regs_t* which is current stack top. 45 | mov rdi, rsp 46 | call \entry 47 | 48 | POP_REGS 49 | 50 | # Skip error code. 51 | add rsp, 8 52 | 53 | # Return from interrupt. 54 | iretq 55 | .endm 56 | 57 | .section .bss 58 | .Lidt: 59 | .zero 4*256 60 | 61 | .section .data 62 | .Lidt_ptr: 63 | .word 4*256 64 | .quad .Lidt 65 | 66 | .section .text 67 | IRQ_ENTRY push_errcode=1 entry=ud_handler 68 | IRQ_ENTRY push_errcode=1 entry=df_handler 69 | IRQ_ENTRY push_errcode=1 entry=ts_handler 70 | IRQ_ENTRY push_errcode=1 entry=np_handler 71 | IRQ_ENTRY push_errcode=1 entry=ss_handler 72 | IRQ_ENTRY push_errcode=1 entry=gp_handler 73 | IRQ_ENTRY push_errcode=1 entry=pf_handler 74 | IRQ_ENTRY push_errcode=0 entry=nm_handler 75 | IRQ_ENTRY push_errcode=0 entry=spurious_handler 76 | IRQ_ENTRY push_errcode=0 entry=timer_handler 77 | 78 | .global irq_init 79 | .type irq_init, @function 80 | irq_init: 81 | push rbp 82 | mov rbp, rsp 83 | 84 | IDT_ENTRY 6 ud_handler GATE_INTERRUPT 85 | IDT_ENTRY 7 nm_handler GATE_INTERRUPT 86 | IDT_ENTRY 8 df_handler GATE_INTERRUPT 87 | IDT_ENTRY 10 ts_handler GATE_INTERRUPT 88 | IDT_ENTRY 11 np_handler GATE_INTERRUPT 89 | IDT_ENTRY 12 ss_handler GATE_INTERRUPT 90 | IDT_ENTRY 13 gp_handler GATE_INTERRUPT 91 | IDT_ENTRY 14 pf_handler GATE_INTERRUPT 92 | IDT_ENTRY 32 timer_handler GATE_INTERRUPT 93 | IDT_ENTRY 39 spurious_handler GATE_INTERRUPT 94 | 95 | lidt [rip + .Lidt_ptr] 96 | 97 | mov rsp, rbp 98 | pop rbp 99 | ret 100 | -------------------------------------------------------------------------------- /homeworks/hw3/README.md: -------------------------------------------------------------------------------- 1 | # Frame allocator (2 балла) 2 | Сейчас в HellOS [реализован](mm/frame_alloc.h) элементарный аллокатор физических фреймов, основанный на связном списке свободных фреймов. Однако у него есть принципиальный недостаток: он не позволяет выделить более одной страницы непрерывно в памяти. В этом задании вам предлагается исправить этот недочёт. 3 | 4 | Оценка задания производится следующим образом: 5 | * В 1 балл будут оцениваться решения, позволяющие выделять непрерывно N фреймов (N можно ограничить сверху). 6 | * В 2 балла будут оцениваться решения, выделяющие и освобождающие фреймы амортизировано за `O(1)`. 7 | 8 | # Object allocator (1 балл) 9 | В следующих домашних заданиях нам потребуется выделять большое количество «маленьких объектов». Это, например, структуры, описывающие процессы, файловые дескрипторы итд. Такие объекты будут довольно маленькими (несколько десятков байт), поэтому использовать frame allocator для таких объектов слишком расточительно. В этом задании вам предлагается реализовать простой аллокатор, позволяющий «нарезать» страницы, выдаваемые frame allocator'ом на объекты заданного типа. 10 | 11 | Код для задания можно найти в [mm/obj.h](mm/obj.h) 12 | 13 | Аллокатор для объектов объявляется следующими макросами: 14 | ```c 15 | OBJ_ALLOC_DEFINE(task_alloc, struct task); 16 | OBJ_ALLOC_DECLARE(task_alloc); 17 | ``` 18 | 19 | Аллокация объектов: 20 | ```c 21 | struct task* task = object_alloc(&task_alloc); 22 | if (task != NULL) { 23 | // ... 24 | } 25 | ``` 26 | Для того, чтобы в будущем избежать false sharing, выравнивайте возвращаемые указатели по размеру кэш-линии (задаётся константой `CACHE_LINE_SIZE_BYTES`). 27 | 28 | Освобождение: 29 | ```c 30 | object_free(&task_alloc, task); 31 | ``` 32 | 33 | # On-demand paging (2 балла) 34 | В HellOS есть структура [`vmem_t`](mm/vmem.h), отвечающая за адресное пространство. Основная задача этой структуры — выделять виртуальные адреса и хранить их отображение в физические фреймы. Для выделения диапазона адресов используется функция `vmem_alloc_pages`, для освобождения – `vmem_free_pages`. Самая примитивная реализация `vmem_alloc_pages` выделяет всю память сразу. Однако современные ОС не выделяют всю запрошенную память сразу, а используют on-demand paging: 35 | 1. При аллокации памяти создаётся объект-регион (с помощью object allocator) о том, что регион виртуальных адресов был запрошен процессом. 36 | 1. При первом обращении к выделенной памяти случится `#PF`, т.к. она не добавлена в страницы таблиц. 37 | 1. Внутри `#PF` ОС проверяет, что страница принадлежит одному из запрошенных регионов, выделяет странницу с помощью frame allocator'а и добавляет её в маппинг. 38 | 39 | Если страницы нет в маппинге, то сейчас достаточно будет вызвать `panic`. Потом в этом месте будет происходить что-то более сложное (например, завершение процесса или отправка `SIGSEGV`). 40 | 41 | Оценка задания производится следующим образом: 42 | * В 1 балл будут оцениваться решения, не поддерживающие on-demand paging. 43 | * В 2 балла будут оцениваться решения, поддерживающие on-demand paging. 44 | -------------------------------------------------------------------------------- /homeworks/hw4/README.md: -------------------------------------------------------------------------------- 1 | # Подготовка и вызов `do_syscall` (1 балл) 2 | 3 | В файле [`syscall.S`](arch/x86/syscall.S) реализован простой обработчик инструкции `syscall`. Настройку соответствующих MSR можно найти в [`x86.c`](arch/x86/x86.c). Пока это просто заглушка, которая просто возвращается обратно в процесс. Вам нужно реализовать полноценную входную точку для системных вызовов: сформировать `arch_regs_t` (объявлен в [`arch.h`](arch/x86/arch.h)) на стеке и передать их в `do_syscall`. Также смотрите на комментарий-подсказку к `syscall_entry`. 4 | 5 | Если вы пишете всё с нуля, достаточно поддержать таблицу сисколлов и их вызов через инструкцию `syscall`. 6 | 7 | # Preemptive multitasking (1 балл) 8 | Вытеснение процессов – неотъемлемая часть современных ОС. В этом задании нужно обеспечить переключение между процессами раз в 10 тиков таймера. Для реализации будет удобно менять функцию `sched_timer_tick`, которая уже вызывается в обработчике прерывания от таймера. 9 | 10 | # Реализация `sys_sleep` (1 балл) 11 | Нужно реализовать системный вызов `sys_sleep(sleep_ms)`, который возвращает управление в процесс не раньше, чем через `sleep_ms` миллисекунд. 12 | 13 | # Реализация `sys_exit` (1 балл) 14 | `sys_exit(exitcode)` вызывается процессом при его завершении. Он должен перевести процесс в состояние `TASK_ZOMBIE` до того момента, как родитель запросит его состояние через `sys_wait`. 15 | Несмотря на то, что сам системный вызов прост в исполнении, для его реализации потребуется написать функцию `vmem_destroy`, которая освободит все страницы, занятые структурой `vmem_t`. Обратите внимание, что также нужно освободить страницы, используемые для самих таблиц. 16 | 17 | # Реализация `sys_wait` (1 балла + 1 балл бонус) 18 | Полная сигнатура: `int64_t sys_wait(pid_t pid, int* status)`. Этот системный вызов должен зависать до того момента, пока ребёнок с заданным `pid` не будет завершён. Код возврата нужно записать в пользовательское пространство по заданному указателю. 19 | 20 | Не забудьте проверить, что этот указатель ссылается на валидный регион памяти. Сейчас это можно сделать, например, проходя по всем `vmem_area_t`. Однако если вы сильны духом, можете реализовать схему аналогичную той, что используется в Linux. Про неё можно почитать [здесь](https://t.me/carzil_channel/65) или в исходниках ядра. Это будет оцениваться дополнительный балл. 21 | 22 | # Реализация `sys_fork` (1 балл + 1 балл бонус) 23 | В этом задании вам предстоит реализовать системный вызов `fork`. Он должен создавать точный клон текущего процесса: должны быть скопированы регистры, стек и выделенная процессом память. После выхода в родителе должен быть возвращён PID ребёнка. А в ребёнке нужно вернуть 0. 24 | 25 | Кроме самого системного вызова вам также потребуется реализовать функцию клонирования текущего адресного пространства `vmem_clone_from_current` (обратите внимание, что пространство-источник предполагается активным, поэтому вы можете напрямую обращаться к его виртуальным адресам), а также функцию трансляции виртуального адреса в физический `translate_address`. 26 | 27 | Дополнительный балл получат решения, которые поддерживают copy-on-write клонирование адресного пространства. 28 | -------------------------------------------------------------------------------- /homeworks/hw3/boot/early.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "mm/paging.h" 3 | #include "early.h" 4 | #include "arch/x86.h" 5 | #include "common.h" 6 | 7 | #define EARLY_TEXT __attribute__((section(".early.text"))) 8 | #define EARLY_BSS __attribute__((section(".early.bss,\"wa\",@nobits#"))) 9 | 10 | #define KERNEL_DIRECT_PHYS_MAPPING_SIZE_IN_GB (KERNEL_DIRECT_PHYS_MAPPING_SIZE / GB) 11 | #define KERNEL_SECTIONS_SIZE_IN_GB (KERNEL_SECTIONS_SIZE / GB) 12 | 13 | // Provided by early.S. 14 | extern pml4_t early_pml4; 15 | extern uint32_t _multiboot_info_addr; 16 | 17 | EARLY_BSS static pdpt_t direct_phys_mapping_pdpts[DIV_ROUNDUP(KERNEL_DIRECT_PHYS_MAPPING_SIZE_IN_GB, 512)] = {}; 18 | EARLY_BSS static pdpt_t higher_half_pdpt = {}; 19 | EARLY_BSS static pgdir_t higher_half_pgdirs[KERNEL_SECTIONS_SIZE_IN_GB] = {}; 20 | EARLY_BSS early_data_t early_data = {}; 21 | 22 | static EARLY_TEXT void outb(uint16_t port, uint8_t data) { 23 | __asm__ volatile("out %0,%1" : : "a" (data), "d" (port)); 24 | } 25 | 26 | static EARLY_TEXT void early_mask_pic_interrupts() { 27 | // Mask all interrupts in old PIC. 28 | outb(0x21, 0xff); 29 | outb(0xa1, 0xff); 30 | } 31 | 32 | extern int _phys_start_hh; 33 | extern int _phys_end_kernel_sections; 34 | 35 | // early_setup_paging prepares virtual memory for entering higher-half kernel code. 36 | static EARLY_TEXT void early_setup_paging() { 37 | // 1. Setup direct physical memory mapping at KERNEL_DIRECT_PHYS_MAPPING_START. 38 | for (size_t i = 0; i < DIV_ROUNDUP(KERNEL_DIRECT_PHYS_MAPPING_SIZE_IN_GB, 512); i++) { 39 | uint64_t phys_addr = i * 512 * GB; 40 | uint64_t virt_addr = KERNEL_DIRECT_PHYS_MAPPING_START + phys_addr; 41 | for (size_t j = 0; j < 512; j++) { 42 | direct_phys_mapping_pdpts[i].entries[j] = phys_addr | PTE_PAGE_SIZE | PTE_PRESENT; 43 | phys_addr += GB; 44 | } 45 | uint64_t pml4e = ((uint64_t)&direct_phys_mapping_pdpts[i]) | PTE_PRESENT; 46 | early_pml4.entries[PML4E_FROM_ADDR(virt_addr)] = pml4e; 47 | } 48 | 49 | // 2. Setup higher-half kernel sections mapping. 50 | uint64_t virt_addr_start = KERNEL_SECTIONS_START; 51 | uint64_t virt_addr_curr = KERNEL_SECTIONS_START; 52 | uint64_t phys_addr_curr = (uint64_t)&_phys_start_hh; 53 | uint64_t phys_addr_end = (uint64_t)&_phys_end_kernel_sections; 54 | while (phys_addr_curr < phys_addr_end) { 55 | size_t pgdir_idx = PDPE_FROM_ADDR(virt_addr_curr) - PDPE_FROM_ADDR(virt_addr_start); 56 | higher_half_pgdirs[pgdir_idx].entries[PDE_FROM_ADDR(virt_addr_curr)] = phys_addr_curr | PTE_PAGE_SIZE | PTE_PRESENT; 57 | phys_addr_curr += 2 * MB; 58 | virt_addr_curr += 2 * MB; 59 | higher_half_pdpt.entries[PDPE_FROM_ADDR(virt_addr_curr)] = ((uint64_t)&higher_half_pgdirs[pgdir_idx]) | PTE_PRESENT; 60 | } 61 | early_pml4.entries[PML4E_FROM_ADDR(virt_addr_curr)] = ((uint64_t)&higher_half_pdpt) | PTE_PRESENT; 62 | } 63 | 64 | extern void jump_to_kernel_main(); 65 | 66 | EARLY_TEXT void early_start() { 67 | early_mask_pic_interrupts(); 68 | early_setup_paging(); 69 | early_data.multiboot_info = PHYS_TO_VIRT(_multiboot_info_addr); 70 | jump_to_kernel_main(); 71 | } 72 | -------------------------------------------------------------------------------- /homeworks/hw4/boot/early.c: -------------------------------------------------------------------------------- 1 | #include "types.h" 2 | #include "mm/paging.h" 3 | #include "early.h" 4 | #include "arch/x86/x86.h" 5 | #include "common.h" 6 | #include "linker.h" 7 | 8 | #define EARLY_TEXT __attribute__((section(".early.text"))) 9 | #define EARLY_BSS __attribute__((section(".early.bss,\"wa\",@nobits#"))) 10 | 11 | #define KERNEL_DIRECT_PHYS_MAPPING_SIZE_IN_GB (KERNEL_DIRECT_PHYS_MAPPING_SIZE / GB) 12 | #define KERNEL_SECTIONS_SIZE_IN_GB (KERNEL_SECTIONS_SIZE / GB) 13 | 14 | // Provided by early.S. 15 | extern pml4_t early_pml4; 16 | extern uint32_t _multiboot_info_addr; 17 | 18 | EARLY_BSS static pdpt_t direct_phys_mapping_pdpts[DIV_ROUNDUP(KERNEL_DIRECT_PHYS_MAPPING_SIZE_IN_GB, 512)] = {}; 19 | EARLY_BSS static pdpt_t higher_half_pdpt = {}; 20 | EARLY_BSS static pgdir_t higher_half_pgdirs[KERNEL_SECTIONS_SIZE_IN_GB] = {}; 21 | EARLY_BSS early_data_t early_data = {}; 22 | 23 | static EARLY_TEXT void outb(uint16_t port, uint8_t data) { 24 | __asm__ volatile("out %0,%1" : : "a" (data), "d" (port)); 25 | } 26 | 27 | static EARLY_TEXT void early_mask_pic_interrupts() { 28 | // Mask all interrupts in old PIC. 29 | outb(0x21, 0xff); 30 | outb(0xa1, 0xff); 31 | } 32 | 33 | // early_setup_paging prepares virtual memory for entering higher-half kernel code. 34 | static EARLY_TEXT void early_setup_paging() { 35 | // 1. Setup direct physical memory mapping at KERNEL_DIRECT_PHYS_MAPPING_START. 36 | for (size_t i = 0; i < DIV_ROUNDUP(KERNEL_DIRECT_PHYS_MAPPING_SIZE_IN_GB, 512); i++) { 37 | uint64_t phys_addr = i * 512 * GB; 38 | uint64_t virt_addr = KERNEL_DIRECT_PHYS_MAPPING_START + phys_addr; 39 | for (size_t j = 0; j < 512; j++) { 40 | direct_phys_mapping_pdpts[i].entries[j] = phys_addr | PTE_PAGE_SIZE | PTE_PRESENT | PTE_WRITE; 41 | phys_addr += GB; 42 | } 43 | uint64_t pml4e = ((uint64_t)&direct_phys_mapping_pdpts[i]) | PTE_PRESENT | PTE_WRITE; 44 | early_pml4.entries[PML4E_FROM_ADDR(virt_addr)] = pml4e; 45 | } 46 | 47 | // 2. Setup higher-half kernel sections mapping. 48 | uint64_t virt_addr_start = KERNEL_SECTIONS_START; 49 | uint64_t virt_addr_curr = KERNEL_SECTIONS_START; 50 | uint64_t phys_addr_curr = (uint64_t)&_phys_start_hh; 51 | uint64_t phys_addr_end = (uint64_t)&_phys_end_kernel_sections; 52 | while (phys_addr_curr < phys_addr_end) { 53 | size_t pgdir_idx = PDPE_FROM_ADDR(virt_addr_curr) - PDPE_FROM_ADDR(virt_addr_start); 54 | higher_half_pgdirs[pgdir_idx].entries[PDE_FROM_ADDR(virt_addr_curr)] = phys_addr_curr | PTE_PAGE_SIZE | PTE_PRESENT | PTE_WRITE; 55 | phys_addr_curr += 2 * MB; 56 | virt_addr_curr += 2 * MB; 57 | higher_half_pdpt.entries[PDPE_FROM_ADDR(virt_addr_curr)] = ((uint64_t)&higher_half_pgdirs[pgdir_idx]) | PTE_PRESENT | PTE_WRITE; 58 | } 59 | early_pml4.entries[PML4E_FROM_ADDR(virt_addr_curr)] = ((uint64_t)&higher_half_pdpt) | PTE_PRESENT | PTE_WRITE; 60 | } 61 | 62 | extern void jump_to_kernel_main(); 63 | 64 | EARLY_TEXT void early_start() { 65 | early_mask_pic_interrupts(); 66 | early_setup_paging(); 67 | early_data.multiboot_info = PHYS_TO_VIRT(_multiboot_info_addr); 68 | jump_to_kernel_main(); 69 | } 70 | -------------------------------------------------------------------------------- /homeworks/hw2/irq.S: -------------------------------------------------------------------------------- 1 | #include "irq.h" 2 | 3 | .intel_syntax noprefix 4 | 5 | .macro IDT_ENTRY vec entry flags 6 | // rbx = &IDT[vec] 7 | lea rbx, .Lidt+\vec*16 8 | 9 | // rax = (0x08 << 16) | (entry & 0xFFFF) 10 | lea rax, _irq_entry_\entry 11 | and eax, 0xFFFF 12 | or eax, 0x08 << 16 13 | mov dword ptr [rbx], eax 14 | 15 | // rax = (entry & 0xFFFF0000) | (flags << 8) 16 | lea rax, _irq_entry_\entry 17 | and eax, 0xFFFF0000 18 | or eax, (\flags << 8) 19 | mov dword ptr [rbx + 4], eax 20 | 21 | // rax = entry >> 32 22 | lea rax, _irq_entry_\entry 23 | shr rax, 32 24 | mov dword ptr [ebx + 8], eax 25 | 26 | mov dword ptr [ebx + 16], 0 27 | .endm 28 | 29 | #define ERRCODE 1 30 | #define NOERRCODE 0 31 | 32 | .macro IRQ_ENTRY errcode entry 33 | .section .text 34 | .extern \entry 35 | .global _irq_entry_\entry 36 | .type _irq_entry_\entry, @function 37 | .align 16 38 | _irq_entry_\entry: 39 | # First of all, save GPRs on stack. 40 | push rax 41 | push rbx 42 | push rcx 43 | push rdx 44 | push rsi 45 | push rdi 46 | push rbp 47 | push r8 48 | push r9 49 | push r10 50 | push r11 51 | push r12 52 | push r13 53 | push r14 54 | push r15 55 | 56 | # Pass pointer for struct irqctx* which is current stack top. 57 | mov rdi, rsp 58 | call \entry 59 | 60 | pop r15 61 | pop r14 62 | pop r13 63 | pop r12 64 | pop r11 65 | pop r10 66 | pop r9 67 | pop r8 68 | pop rbp 69 | pop rdi 70 | pop rsi 71 | pop rdx 72 | pop rcx 73 | pop rbx 74 | pop rax 75 | 76 | .if \errcode 77 | # Skip error code. 78 | add rsp, 8 79 | .endif 80 | 81 | # Return from interrupt. 82 | iretq 83 | .endm 84 | 85 | .section .bss 86 | .Lidt: 87 | .zero 4*256 88 | 89 | .section .data 90 | .Lidt_ptr: 91 | .word 4*256 92 | .quad .Lidt 93 | 94 | .section .text 95 | IRQ_ENTRY NOERRCODE ud_handler 96 | IRQ_ENTRY ERRCODE df_handler 97 | IRQ_ENTRY ERRCODE ts_handler 98 | IRQ_ENTRY ERRCODE np_handler 99 | IRQ_ENTRY ERRCODE ss_handler 100 | IRQ_ENTRY ERRCODE gp_handler 101 | IRQ_ENTRY ERRCODE pf_handler 102 | IRQ_ENTRY NOERRCODE nm_handler 103 | IRQ_ENTRY NOERRCODE spurious_handler 104 | IRQ_ENTRY NOERRCODE timer_handler 105 | 106 | .global irq_init 107 | .type irq_init, @function 108 | irq_init: 109 | push rbp 110 | mov rbp, rsp 111 | 112 | IDT_ENTRY 6 ud_handler GATE_INTERRUPT 113 | IDT_ENTRY 7 nm_handler GATE_INTERRUPT 114 | IDT_ENTRY 8 df_handler GATE_INTERRUPT 115 | IDT_ENTRY 10 ts_handler GATE_INTERRUPT 116 | IDT_ENTRY 11 np_handler GATE_INTERRUPT 117 | IDT_ENTRY 12 ss_handler GATE_INTERRUPT 118 | IDT_ENTRY 13 gp_handler GATE_INTERRUPT 119 | IDT_ENTRY 14 pf_handler GATE_INTERRUPT 120 | IDT_ENTRY 32 timer_handler GATE_INTERRUPT 121 | IDT_ENTRY 39 spurious_handler GATE_INTERRUPT 122 | 123 | lidt [.Lidt_ptr] 124 | 125 | mov rsp, rbp 126 | pop rbp 127 | ret 128 | -------------------------------------------------------------------------------- /homeworks/hw3/kernel/irq.S: -------------------------------------------------------------------------------- 1 | #include "irq.h" 2 | 3 | .intel_syntax noprefix 4 | 5 | .macro IDT_ENTRY vec entry flags 6 | // rbx = &IDT[vec] 7 | lea rbx, [rip + .Lidt + \vec*16] 8 | 9 | // rax = (0x08 << 16) | (entry & 0xFFFF) 10 | lea rax, [rip + _irq_entry_\entry] 11 | and eax, 0xFFFF 12 | or eax, 0x08 << 16 13 | mov dword ptr [rbx], eax 14 | 15 | // rax = (entry & 0xFFFF0000) | (flags << 8) 16 | lea rax, [rip + _irq_entry_\entry] 17 | and eax, 0xFFFF0000 18 | or eax, (\flags << 8) 19 | mov dword ptr [rbx + 4], eax 20 | 21 | // rax = entry >> 32 22 | lea rax, [rip + _irq_entry_\entry] 23 | shr rax, 32 24 | mov dword ptr [rbx + 8], eax 25 | 26 | mov dword ptr [rbx + 16], 0 27 | .endm 28 | 29 | #define ERRCODE 1 30 | #define NOERRCODE 0 31 | 32 | .macro IRQ_ENTRY errcode entry 33 | .section .text 34 | .extern \entry 35 | .global _irq_entry_\entry 36 | .type _irq_entry_\entry, @function 37 | .align 16 38 | _irq_entry_\entry: 39 | # First of all, save GPRs on stack. 40 | push rax 41 | push rbx 42 | push rcx 43 | push rdx 44 | push rsi 45 | push rdi 46 | push rbp 47 | push r8 48 | push r9 49 | push r10 50 | push r11 51 | push r12 52 | push r13 53 | push r14 54 | push r15 55 | 56 | # Pass pointer for struct irqctx* which is current stack top. 57 | mov rdi, rsp 58 | call \entry 59 | 60 | pop r15 61 | pop r14 62 | pop r13 63 | pop r12 64 | pop r11 65 | pop r10 66 | pop r9 67 | pop r8 68 | pop rbp 69 | pop rdi 70 | pop rsi 71 | pop rdx 72 | pop rcx 73 | pop rbx 74 | pop rax 75 | 76 | .if \errcode 77 | # Skip error code. 78 | add rsp, 8 79 | .endif 80 | 81 | # Return from interrupt. 82 | iretq 83 | .endm 84 | 85 | .section .bss 86 | .Lidt: 87 | .zero 4*256 88 | 89 | .section .data 90 | .Lidt_ptr: 91 | .word 4*256 92 | .quad .Lidt 93 | 94 | .section .text 95 | IRQ_ENTRY ERRCODE ud_handler 96 | IRQ_ENTRY ERRCODE df_handler 97 | IRQ_ENTRY ERRCODE ts_handler 98 | IRQ_ENTRY ERRCODE np_handler 99 | IRQ_ENTRY ERRCODE ss_handler 100 | IRQ_ENTRY ERRCODE gp_handler 101 | IRQ_ENTRY ERRCODE pf_handler 102 | IRQ_ENTRY NOERRCODE nm_handler 103 | IRQ_ENTRY NOERRCODE spurious_handler 104 | IRQ_ENTRY NOERRCODE timer_handler 105 | 106 | .global irq_init 107 | .type irq_init, @function 108 | irq_init: 109 | push rbp 110 | mov rbp, rsp 111 | 112 | IDT_ENTRY 6 ud_handler GATE_INTERRUPT 113 | IDT_ENTRY 7 nm_handler GATE_INTERRUPT 114 | IDT_ENTRY 8 df_handler GATE_INTERRUPT 115 | IDT_ENTRY 10 ts_handler GATE_INTERRUPT 116 | IDT_ENTRY 11 np_handler GATE_INTERRUPT 117 | IDT_ENTRY 12 ss_handler GATE_INTERRUPT 118 | IDT_ENTRY 13 gp_handler GATE_INTERRUPT 119 | IDT_ENTRY 14 pf_handler GATE_INTERRUPT 120 | IDT_ENTRY 32 timer_handler GATE_INTERRUPT 121 | IDT_ENTRY 39 spurious_handler GATE_INTERRUPT 122 | 123 | lidt [rip + .Lidt_ptr] 124 | 125 | mov rsp, rbp 126 | pop rbp 127 | ret 128 | -------------------------------------------------------------------------------- /homeworks/hw1/printk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "vga.h" 6 | 7 | enum { PRINTK_BUF_SIZE = 11 }; 8 | static const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 9 | 10 | size_t bprintu64(char* buf, uint64_t a, int base) { 11 | size_t i; 12 | size_t bytes; 13 | 14 | if (a == 0) { 15 | buf[0] = '0'; 16 | return 1; 17 | } 18 | for (bytes = 0; a > 0; bytes++) { 19 | buf[bytes] = digits[a % base]; 20 | a /= base; 21 | } 22 | for (i = 0; i < bytes / 2; i++) { 23 | buf[i] ^= buf[bytes - i - 1]; 24 | buf[bytes - i - 1] ^= buf[i]; 25 | buf[i] ^= buf[bytes - i - 1]; 26 | } 27 | return bytes; 28 | } 29 | 30 | size_t bprints64(char* buf, int64_t a, int base) { 31 | if (a < 0) { 32 | *(buf++) = '-'; 33 | return 1 + bprintu64(buf, -a, base); 34 | } 35 | return bprintu64(buf, a, base); 36 | } 37 | 38 | size_t bprintstr(char* buf, const char* str) { 39 | const char* cur = str; 40 | size_t length = 0; 41 | while (*cur) { 42 | buf[length++] = *(cur++); 43 | } 44 | return length; 45 | } 46 | 47 | size_t bprintptr(char* buf, void* ptr) { 48 | size_t bytes; 49 | if (ptr == NULL) { 50 | return bprintstr(buf, "(nil)"); 51 | } 52 | bytes = bprintstr(buf, "0x"); 53 | return bytes + bprintu64(buf + bytes, (uint64_t) ptr, 16); 54 | } 55 | 56 | void vprintk(const char* fmt, va_list args) { 57 | const char* cursor = fmt; 58 | int idle = 1; 59 | uint32_t u32value; 60 | int32_t s32value; 61 | void* ptrvalue; 62 | char* str; 63 | char buf[PRINTK_BUF_SIZE]; 64 | size_t size; 65 | 66 | uint16_t color = vga_entry(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK); 67 | 68 | while (*cursor) { 69 | if (idle) { 70 | if (*cursor != '%') { 71 | vga_write(cursor, 1, color); 72 | } else { 73 | idle = 0; 74 | } 75 | } else { 76 | switch (*cursor) { 77 | case 'u': 78 | u32value = va_arg(args, uint32_t); 79 | size = bprintu64(buf, u32value, 10); 80 | break; 81 | case 'x': 82 | u32value = va_arg(args, uint32_t); 83 | size = bprintu64(buf, u32value, 16); 84 | break; 85 | case 'd': 86 | s32value = va_arg(args, int32_t); 87 | size = bprints64(buf, s32value, 10); 88 | break; 89 | case 'p': 90 | ptrvalue = va_arg(args, void*); 91 | size = bprintptr(buf, ptrvalue); 92 | break; 93 | case 's': 94 | str = va_arg(args, char*); 95 | vga_writestring_color(str, color); 96 | size = 0; 97 | break; 98 | default: 99 | size = 0; 100 | break; 101 | } 102 | if (size) { 103 | vga_write(buf, size, color); 104 | } 105 | idle = 1; 106 | } 107 | ++cursor; 108 | } 109 | } 110 | 111 | void printk(const char* fmt, ...) { 112 | va_list args; 113 | va_start(args, fmt); 114 | vprintk(fmt, args); 115 | va_end(args); 116 | } 117 | -------------------------------------------------------------------------------- /homeworks/hw2/printk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "vga.h" 6 | 7 | enum { PRINTK_BUF_SIZE = 11 }; 8 | static const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 9 | 10 | size_t bprintu64(char* buf, uint64_t a, int base) { 11 | size_t i; 12 | size_t bytes; 13 | 14 | if (a == 0) { 15 | buf[0] = '0'; 16 | return 1; 17 | } 18 | for (bytes = 0; a > 0; bytes++) { 19 | buf[bytes] = digits[a % base]; 20 | a /= base; 21 | } 22 | for (i = 0; i < bytes / 2; i++) { 23 | buf[i] ^= buf[bytes - i - 1]; 24 | buf[bytes - i - 1] ^= buf[i]; 25 | buf[i] ^= buf[bytes - i - 1]; 26 | } 27 | return bytes; 28 | } 29 | 30 | size_t bprints64(char* buf, int64_t a, int base) { 31 | if (a < 0) { 32 | *(buf++) = '-'; 33 | return 1 + bprintu64(buf, -a, base); 34 | } 35 | return bprintu64(buf, a, base); 36 | } 37 | 38 | size_t bprintstr(char* buf, const char* str) { 39 | const char* cur = str; 40 | size_t length = 0; 41 | while (*cur) { 42 | buf[length++] = *(cur++); 43 | } 44 | return length; 45 | } 46 | 47 | size_t bprintptr(char* buf, void* ptr) { 48 | size_t bytes; 49 | if (ptr == NULL) { 50 | return bprintstr(buf, "(nil)"); 51 | } 52 | bytes = bprintstr(buf, "0x"); 53 | return bytes + bprintu64(buf + bytes, (uint64_t) ptr, 16); 54 | } 55 | 56 | void vprintk(const char* fmt, va_list args) { 57 | const char* cursor = fmt; 58 | int idle = 1; 59 | uint32_t u32value; 60 | int32_t s32value; 61 | void* ptrvalue; 62 | char* str; 63 | char buf[PRINTK_BUF_SIZE]; 64 | size_t size; 65 | 66 | uint16_t color = vga_entry(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK); 67 | 68 | while (*cursor) { 69 | if (idle) { 70 | if (*cursor != '%') { 71 | vga_write(cursor, 1, color); 72 | } else { 73 | idle = 0; 74 | } 75 | } else { 76 | switch (*cursor) { 77 | case 'u': 78 | u32value = va_arg(args, uint32_t); 79 | size = bprintu64(buf, u32value, 10); 80 | break; 81 | case 'x': 82 | u32value = va_arg(args, uint32_t); 83 | size = bprintu64(buf, u32value, 16); 84 | break; 85 | case 'd': 86 | s32value = va_arg(args, int32_t); 87 | size = bprints64(buf, s32value, 10); 88 | break; 89 | case 'p': 90 | ptrvalue = va_arg(args, void*); 91 | size = bprintptr(buf, ptrvalue); 92 | break; 93 | case 's': 94 | str = va_arg(args, char*); 95 | vga_writestring_color(str, color); 96 | size = 0; 97 | break; 98 | default: 99 | size = 0; 100 | break; 101 | } 102 | if (size) { 103 | vga_write(buf, size, color); 104 | } 105 | idle = 1; 106 | } 107 | ++cursor; 108 | } 109 | } 110 | 111 | void printk(const char* fmt, ...) { 112 | va_list args; 113 | va_start(args, fmt); 114 | vprintk(fmt, args); 115 | va_end(args); 116 | } 117 | -------------------------------------------------------------------------------- /homeworks/hw3/kernel/printk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "drivers/vga.h" 6 | 7 | enum { PRINTK_BUF_SIZE = 11 }; 8 | static const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 9 | 10 | size_t bprintu64(char* buf, uint64_t a, int base) { 11 | size_t i; 12 | size_t bytes; 13 | 14 | if (a == 0) { 15 | buf[0] = '0'; 16 | return 1; 17 | } 18 | for (bytes = 0; a > 0; bytes++) { 19 | buf[bytes] = digits[a % base]; 20 | a /= base; 21 | } 22 | for (i = 0; i < bytes / 2; i++) { 23 | buf[i] ^= buf[bytes - i - 1]; 24 | buf[bytes - i - 1] ^= buf[i]; 25 | buf[i] ^= buf[bytes - i - 1]; 26 | } 27 | return bytes; 28 | } 29 | 30 | size_t bprints64(char* buf, int64_t a, int base) { 31 | if (a < 0) { 32 | *(buf++) = '-'; 33 | return 1 + bprintu64(buf, -a, base); 34 | } 35 | return bprintu64(buf, a, base); 36 | } 37 | 38 | size_t bprintstr(char* buf, const char* str) { 39 | const char* cur = str; 40 | size_t length = 0; 41 | while (*cur) { 42 | buf[length++] = *(cur++); 43 | } 44 | return length; 45 | } 46 | 47 | size_t bprintptr(char* buf, void* ptr) { 48 | size_t bytes; 49 | if (ptr == NULL) { 50 | return bprintstr(buf, "(nil)"); 51 | } 52 | bytes = bprintstr(buf, "0x"); 53 | return bytes + bprintu64(buf + bytes, (uint64_t) ptr, 16); 54 | } 55 | 56 | void vprintk(const char* fmt, va_list args) { 57 | const char* cursor = fmt; 58 | int idle = 1; 59 | uint32_t u32value; 60 | int32_t s32value; 61 | void* ptrvalue; 62 | char* str; 63 | char buf[PRINTK_BUF_SIZE]; 64 | size_t size; 65 | 66 | uint16_t color = vga_entry(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK); 67 | 68 | while (*cursor) { 69 | if (idle) { 70 | if (*cursor != '%') { 71 | vga_write(cursor, 1, color); 72 | } else { 73 | idle = 0; 74 | } 75 | } else { 76 | switch (*cursor) { 77 | case 'u': 78 | u32value = va_arg(args, uint32_t); 79 | size = bprintu64(buf, u32value, 10); 80 | break; 81 | case 'x': 82 | u32value = va_arg(args, uint32_t); 83 | size = bprintu64(buf, u32value, 16); 84 | break; 85 | case 'd': 86 | s32value = va_arg(args, int32_t); 87 | size = bprints64(buf, s32value, 10); 88 | break; 89 | case 'p': 90 | ptrvalue = va_arg(args, void*); 91 | size = bprintptr(buf, ptrvalue); 92 | break; 93 | case 's': 94 | str = va_arg(args, char*); 95 | vga_writestring_color(str, color); 96 | size = 0; 97 | break; 98 | default: 99 | size = 0; 100 | break; 101 | } 102 | if (size) { 103 | vga_write(buf, size, color); 104 | } 105 | idle = 1; 106 | } 107 | ++cursor; 108 | } 109 | } 110 | 111 | void printk(const char* fmt, ...) { 112 | va_list args; 113 | va_start(args, fmt); 114 | vprintk(fmt, args); 115 | va_end(args); 116 | } 117 | -------------------------------------------------------------------------------- /homeworks/hw4/kernel/printk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "drivers/vga.h" 6 | 7 | enum { PRINTK_BUF_SIZE = 11 }; 8 | static const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 9 | 10 | size_t bprintu64(char* buf, uint64_t a, int base) { 11 | size_t i; 12 | size_t bytes; 13 | 14 | if (a == 0) { 15 | buf[0] = '0'; 16 | return 1; 17 | } 18 | for (bytes = 0; a > 0; bytes++) { 19 | buf[bytes] = digits[a % base]; 20 | a /= base; 21 | } 22 | for (i = 0; i < bytes / 2; i++) { 23 | buf[i] ^= buf[bytes - i - 1]; 24 | buf[bytes - i - 1] ^= buf[i]; 25 | buf[i] ^= buf[bytes - i - 1]; 26 | } 27 | return bytes; 28 | } 29 | 30 | size_t bprints64(char* buf, int64_t a, int base) { 31 | if (a < 0) { 32 | *(buf++) = '-'; 33 | return 1 + bprintu64(buf, -a, base); 34 | } 35 | return bprintu64(buf, a, base); 36 | } 37 | 38 | size_t bprintstr(char* buf, const char* str) { 39 | const char* cur = str; 40 | size_t length = 0; 41 | while (*cur) { 42 | buf[length++] = *(cur++); 43 | } 44 | return length; 45 | } 46 | 47 | size_t bprintptr(char* buf, void* ptr) { 48 | size_t bytes; 49 | if (ptr == NULL) { 50 | return bprintstr(buf, "(nil)"); 51 | } 52 | bytes = bprintstr(buf, "0x"); 53 | return bytes + bprintu64(buf + bytes, (uint64_t) ptr, 16); 54 | } 55 | 56 | void vprintk(const char* fmt, va_list args) { 57 | const char* cursor = fmt; 58 | int idle = 1; 59 | uint32_t u32value; 60 | int32_t s32value; 61 | void* ptrvalue; 62 | char* str; 63 | char buf[PRINTK_BUF_SIZE]; 64 | size_t size; 65 | 66 | uint16_t color = vga_entry(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK); 67 | 68 | while (*cursor) { 69 | if (idle) { 70 | if (*cursor != '%') { 71 | vga_write(cursor, 1, color); 72 | } else { 73 | idle = 0; 74 | } 75 | } else { 76 | switch (*cursor) { 77 | case 'u': 78 | u32value = va_arg(args, uint32_t); 79 | size = bprintu64(buf, u32value, 10); 80 | break; 81 | case 'x': 82 | u32value = va_arg(args, uint32_t); 83 | size = bprintu64(buf, u32value, 16); 84 | break; 85 | case 'd': 86 | s32value = va_arg(args, int32_t); 87 | size = bprints64(buf, s32value, 10); 88 | break; 89 | case 'p': 90 | ptrvalue = va_arg(args, void*); 91 | size = bprintptr(buf, ptrvalue); 92 | break; 93 | case 's': 94 | str = va_arg(args, char*); 95 | vga_writestring_color(str, color); 96 | size = 0; 97 | break; 98 | default: 99 | size = 0; 100 | break; 101 | } 102 | if (size) { 103 | vga_write(buf, size, color); 104 | } 105 | idle = 1; 106 | } 107 | ++cursor; 108 | } 109 | } 110 | 111 | void printk(const char* fmt, ...) { 112 | va_list args; 113 | va_start(args, fmt); 114 | vprintk(fmt, args); 115 | va_end(args); 116 | } 117 | -------------------------------------------------------------------------------- /homeworks/hw1/boot.S: -------------------------------------------------------------------------------- 1 | #include "gdt.h" 2 | #include "multiboot.h" 3 | #include "paging.h" 4 | 5 | .intel_syntax noprefix 6 | 7 | # .multiboot section defines Multiboot 2 compatible header. 8 | .section .multiboot 9 | .long MULTIBOOT_MAGIC 10 | .long MULTIBOOT_ARCH_32 11 | .long HEADER_LEN 12 | .long -(MULTIBOOT_MAGIC + MULTIBOOT_ARCH_32 + HEADER_LEN) 13 | 14 | .word MULTIBOOT_TAG_END 15 | .word 0 16 | .long 8 17 | 18 | .set HEADER_LEN, . - .multiboot 19 | 20 | # Section .bss contains zero-filled data. It doesn't appear in ELF file, but filled during image load. 21 | .section .bss 22 | .Lpml4: 23 | .zero 4096 24 | .Lpdpt: 25 | .zero 4096 26 | .Lbootstrap_stack: 27 | .zero 4096 28 | 29 | 30 | # Section .data contains some global variables. 31 | .section .data 32 | # Setup GDT for 64-bit mode. 33 | .Lgdt64: 34 | # 0: null segment. 35 | .long 0 36 | .long 0 37 | 38 | # 1: 64-bit code segment. 39 | .long 0xFFFF 40 | # (0xF << 16) fills base 31:24. 41 | .long GDT_GRANULARITY | GDT_LONG | GDT_PRESENT | GDT_SYSTEM | (1<<11) | GDT_READ | (0xF << 16) 42 | 43 | .Lgdt64_ptr: 44 | .word . - .Lgdt64 45 | .long .Lgdt64 46 | 47 | 48 | # kernel_main symbol is declared outside this file. 49 | .extern kernel_main 50 | 51 | .section .text 52 | # _start is an entry point of our kernel, execution after bootloader starts here. 53 | .global _start 54 | .type _start, @function 55 | _start: 56 | # Tell assembler, that we are running 32-bit protected mode. 57 | .code32 58 | # Our kernel just loaded, current processor state is described in "I386 machine state" section of Multiboot2 specification. 59 | 60 | # TODO: here you need remember pointer to Multiboot2 information structure and later pass it to the multiboot_init() function. 61 | # It's up to you how to save pointer: you can use stack, register or store it in .data section. 62 | 63 | 64 | # To enable long mode, we need to setup basic paging. 65 | # Here we setup identity mapping for the first gigabyte. 66 | 67 | # PML4[0] = PDPT_addr | FLAG_PRESENT 68 | lea edi, .Lpml4 69 | lea eax, .Lpdpt 70 | or eax, PTE_PRESENT 71 | mov dword ptr [edi], eax 72 | 73 | # PDPT[0] = 0 | PTE_PAGE_SIZE | PTE_PRESENT 74 | lea edi, .Lpdpt 75 | mov dword ptr [edi], PTE_PAGE_SIZE | PTE_PRESENT 76 | 77 | # Enabling 64-bit mode is described in ISDM, Volume 3A, Section 9.8.5. 78 | 79 | # Write PML4 physical address into CR3. 80 | lea eax, .Lpml4 81 | mov cr3, eax 82 | 83 | # Set PAE bit in CR4. 84 | mov eax, cr4 85 | or eax, 1<<5 86 | mov cr4, eax 87 | 88 | # Set IA32_EFER.LME bit (Long Mode Enable). 89 | mov ecx, 0xC0000080 90 | rdmsr 91 | or eax, 1<<8 92 | wrmsr 93 | 94 | # Enable paging by setting PG bit in CR0. 95 | mov eax, cr0 96 | or eax, 1<<31 97 | mov cr0, eax 98 | 99 | # Load GDT with 64-bit code segment. 100 | lgdt .Lgdt64_ptr 101 | 102 | # Load prepared code segment with index 1 with RPL=0 from GDT: segment selector is 0b1000 = 0x8. 103 | ljmp 0x8:.Lret 104 | .Lret: 105 | # We we are now running 64-bit mode. Yay! 106 | .code64 107 | # Setup booststrap stack and call kernel_main. 108 | lea rsp, .Lbootstrap_stack 109 | call kernel_main 110 | 111 | # Halt processor, i.e. put it in a such state that next instruction will be executed only after interrupt. 112 | # Effectively it means inifinite non-busy loop. 113 | 1: hlt 114 | jmp 1b 115 | 116 | .size _start, . - _start 117 | -------------------------------------------------------------------------------- /homeworks/hw3/mm/frame_alloc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "frame_alloc.h" 3 | #include "kernel/multiboot.h" 4 | #include "kernel/panic.h" 5 | #include "defs.h" 6 | #include "paging.h" 7 | 8 | typedef struct frame { 9 | struct frame* next; 10 | } __attribute__((aligned(PAGE_SIZE))) frame_t; 11 | 12 | static bool intersects(frame_t* frame, mem_region_t area) { 13 | return area.start <= (void*)frame && (void*)frame < area.end; 14 | } 15 | 16 | static size_t preserved_areas_size = 0; 17 | static mem_region_t preserved_areas[100]; 18 | 19 | // mark_preserved_area marks memory area as allocated, all its frames are not touched by frame allocator. 20 | static void mark_preserved_area(mem_region_t area) { 21 | area.end = (void*)ALIGN_UP(area.end, PAGE_SIZE); 22 | preserved_areas[preserved_areas_size++] = area; 23 | } 24 | 25 | // Those constants are defined by linker script. 26 | extern int _phys_start_kernel_sections; 27 | extern int _phys_end_kernel_sections; 28 | 29 | // mark_preserved_areas fills pre-allocated regions: early & kernel sections, multiboot info, etc 30 | void mark_preserved_areas() { 31 | mark_preserved_area((mem_region_t){ .start = PHYS_TO_VIRT(&_phys_start_kernel_sections), .end = PHYS_TO_VIRT(&_phys_end_kernel_sections) }); 32 | mark_preserved_area(multiboot_mem_region()); 33 | } 34 | 35 | // intersects_with_kernel_sections checks if a frame at the given virtual address intersects with kernel sections. 36 | static bool is_allocated(frame_t* frame) { 37 | for (size_t i = 0; i < preserved_areas_size; i++) { 38 | if (intersects(frame, preserved_areas[i])) { 39 | return true; 40 | } 41 | } 42 | return false; 43 | } 44 | 45 | static frame_t* freelist_head; 46 | 47 | // allocated_memory_region adds a region of RAM to the frame allocator. 48 | static size_t frame_alloc_add_area(frame_t* base, size_t sz) { 49 | size_t pgcnt = 0; 50 | frame_t* prev_free = NULL; 51 | frame_t* first_free = NULL; 52 | for (size_t i = 0; i < sz - 1; i++) { 53 | frame_t* curr = &base[i]; 54 | if (is_allocated(curr)) { 55 | continue; 56 | } 57 | if (first_free == NULL) { 58 | prev_free = curr; 59 | first_free = curr; 60 | pgcnt++; 61 | continue; 62 | } 63 | prev_free->next = curr; 64 | prev_free = curr; 65 | pgcnt++; 66 | } 67 | prev_free->next = freelist_head; 68 | freelist_head = first_free; 69 | return pgcnt; 70 | } 71 | 72 | // frame_alloc_add_areas obtains memory areas of usable RAM from Multiboot2 and adds them to the frame allocator. 73 | static void frame_alloc_add_areas() { 74 | struct multiboot_mmap_iter mmap_it; 75 | multiboot_mmap_iter_init(&mmap_it); 76 | struct multiboot_mmap_entry* mmap_entry; 77 | size_t pgcnt = 0; 78 | while ((mmap_entry = multiboot_mmap_iter_next(&mmap_it)) != NULL) { 79 | if (mmap_entry->type != MULTIBOOT_MMAP_TYPE_RAM) { 80 | continue; 81 | } 82 | pgcnt += frame_alloc_add_area(PHYS_TO_VIRT(mmap_entry->base_addr), mmap_entry->length / PAGE_SIZE); 83 | } 84 | printk("initialized page_alloc with %d pages\n", pgcnt); 85 | } 86 | 87 | void frame_alloc_init() { 88 | mark_preserved_areas(); 89 | frame_alloc_add_areas(); 90 | } 91 | 92 | void* frames_alloc(size_t n) { 93 | if (freelist_head == NULL) { 94 | return NULL; 95 | } 96 | BUG_ON(n != 1); 97 | frame_t* frame = freelist_head; 98 | freelist_head = frame->next; 99 | frame->next = NULL; 100 | return frame; 101 | } 102 | 103 | void frames_free(void* addr, size_t n) { 104 | BUG_ON(addr == NULL); 105 | BUG_ON(n != 1); 106 | 107 | frame_t* frame = addr; 108 | frame->next = freelist_head; 109 | freelist_head = frame; 110 | } 111 | 112 | void* frame_alloc() { 113 | return frames_alloc(1); 114 | } 115 | 116 | void frame_free(void* addr) { 117 | return frames_free(addr, 1); 118 | } 119 | -------------------------------------------------------------------------------- /homeworks/hw4/mm/frame_alloc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "frame_alloc.h" 3 | #include "kernel/multiboot.h" 4 | #include "kernel/panic.h" 5 | #include "defs.h" 6 | #include "paging.h" 7 | #include "linker.h" 8 | 9 | typedef struct frame { 10 | struct frame* next; 11 | } __attribute__((aligned(PAGE_SIZE))) frame_t; 12 | 13 | static bool intersects(frame_t* frame, mem_region_t area) { 14 | return area.start <= (void*)frame && (void*)frame < area.end; 15 | } 16 | 17 | static size_t preserved_areas_size = 0; 18 | static mem_region_t preserved_areas[100]; 19 | 20 | // mark_preserved_area marks memory area as allocated, all its frames are not touched by frame allocator. 21 | static void mark_preserved_area(mem_region_t area) { 22 | area.end = (void*)ALIGN_UP(area.end, PAGE_SIZE); 23 | printk("preserved area: %p-%p\n", area.start, area.end); 24 | preserved_areas[preserved_areas_size++] = area; 25 | } 26 | 27 | // mark_preserved_areas fills pre-allocated regions: early & kernel sections, multiboot info, etc 28 | void mark_preserved_areas() { 29 | mark_preserved_area((mem_region_t){ .start = PHYS_TO_VIRT(&_phys_start_kernel_sections), .end = PHYS_TO_VIRT(&_phys_end_kernel_sections) }); 30 | mark_preserved_area((mem_region_t){ .start = PHYS_TO_VIRT(&_phys_start_user), .end = PHYS_TO_VIRT(&_phys_end_user) }); 31 | mark_preserved_area(multiboot_mem_region()); 32 | } 33 | 34 | // intersects_with_kernel_sections checks if a frame at the given virtual address intersects with kernel sections. 35 | static bool is_allocated(frame_t* frame) { 36 | for (size_t i = 0; i < preserved_areas_size; i++) { 37 | if (intersects(frame, preserved_areas[i])) { 38 | return true; 39 | } 40 | } 41 | return false; 42 | } 43 | 44 | static frame_t* freelist_head; 45 | 46 | // allocated_memory_region adds a region of RAM to the frame allocator. 47 | static size_t frame_alloc_add_area(frame_t* base, size_t sz) { 48 | size_t pgcnt = 0; 49 | frame_t* prev_free = NULL; 50 | frame_t* first_free = NULL; 51 | for (size_t i = 0; i < sz; i++) { 52 | frame_t* curr = &base[i]; 53 | if (is_allocated(curr)) { 54 | continue; 55 | } 56 | if (first_free == NULL) { 57 | prev_free = curr; 58 | first_free = curr; 59 | pgcnt++; 60 | continue; 61 | } 62 | prev_free->next = curr; 63 | prev_free = curr; 64 | pgcnt++; 65 | } 66 | prev_free->next = freelist_head; 67 | freelist_head = first_free; 68 | return pgcnt; 69 | } 70 | 71 | // frame_alloc_add_areas obtains memory areas of usable RAM from Multiboot2 and adds them to the frame allocator. 72 | static void frame_alloc_add_areas() { 73 | struct multiboot_mmap_iter mmap_it; 74 | multiboot_mmap_iter_init(&mmap_it); 75 | struct multiboot_mmap_entry* mmap_entry; 76 | size_t pgcnt = 0; 77 | while ((mmap_entry = multiboot_mmap_iter_next(&mmap_it)) != NULL) { 78 | if (mmap_entry->type != MULTIBOOT_MMAP_TYPE_RAM) { 79 | continue; 80 | } 81 | pgcnt += frame_alloc_add_area(PHYS_TO_VIRT(mmap_entry->base_addr), mmap_entry->length / PAGE_SIZE); 82 | } 83 | printk("initialized page_alloc with %d pages\n", pgcnt); 84 | } 85 | 86 | void frame_alloc_init() { 87 | mark_preserved_areas(); 88 | frame_alloc_add_areas(); 89 | } 90 | 91 | void* frames_alloc(size_t n) { 92 | if (freelist_head == NULL) { 93 | return NULL; 94 | } 95 | BUG_ON(n != 1); 96 | frame_t* frame = freelist_head; 97 | freelist_head = frame->next; 98 | frame->next = NULL; 99 | return frame; 100 | } 101 | 102 | void frames_free(void* addr, size_t n) { 103 | BUG_ON(addr == NULL); 104 | BUG_ON(n != 1); 105 | 106 | frame_t* frame = addr; 107 | frame->next = freelist_head; 108 | freelist_head = frame; 109 | } 110 | 111 | void* frame_alloc() { 112 | return frames_alloc(1); 113 | } 114 | 115 | void frame_free(void* addr) { 116 | return frames_free(addr, 1); 117 | } 118 | -------------------------------------------------------------------------------- /homeworks/hw2/boot.S: -------------------------------------------------------------------------------- 1 | #include "gdt.h" 2 | #include "multiboot.h" 3 | #include "paging.h" 4 | 5 | .intel_syntax noprefix 6 | 7 | # .multiboot section defines Multiboot 2 compatible header. 8 | .section .multiboot 9 | .long MULTIBOOT_MAGIC 10 | .long MULTIBOOT_ARCH_32 11 | .long HEADER_LEN 12 | .long -(MULTIBOOT_MAGIC + MULTIBOOT_ARCH_32 + HEADER_LEN) 13 | 14 | .word MULTIBOOT_TAG_END 15 | .word 0 16 | .long 8 17 | 18 | .set HEADER_LEN, . - .multiboot 19 | 20 | # Section .bss contains zero-filled data. It doesn't appear in ELF file, but filled during image load. 21 | .section .bss 22 | .Lbootstrap_stack: 23 | .zero 8192 24 | .Lpml4: 25 | .zero 4096 26 | .Lpdpt: 27 | .zero 4096 28 | 29 | .global _multiboot_info 30 | _multiboot_info: 31 | .long 0 32 | 33 | # Section .data contains some global variables. 34 | .section .data 35 | # Setup GDT for 64-bit mode. 36 | .Lgdt64: 37 | # 0: null segment. 38 | .long 0 39 | .long 0 40 | 41 | # 1: 64-bit kernel code segment. 42 | .long 0xFFFF 43 | # (0xF << 16) fills base 31:24. 44 | .long GDT_GRANULARITY | GDT_LONG | GDT_PRESENT | GDT_SYSTEM | (1<<11) | GDT_READ | (0xF << 16) 45 | 46 | .Lgdt64_ptr: 47 | .word . - .Lgdt64 48 | .long .Lgdt64 49 | 50 | # kernel_main symbol is declared outside this file. 51 | .extern kernel_main 52 | 53 | .section .text 54 | # _start is an entry point of our kernel, execution after bootloader starts here. 55 | .global _start 56 | .type _start, @function 57 | _start: 58 | # Tell assembler, that we are running 32-bit protected mode. 59 | .code32 60 | # Our kernel just loaded, current processor state is described in "I386 machine state" section of Multiboot2 specification. 61 | 62 | mov _multiboot_info, ebx 63 | 64 | # To enable long mode, we need to setup basic paging. 65 | # Here we setup identity mapping for the first four gigabytes. 66 | 67 | # PML4[0] = PDPT_addr | FLAG_PRESENT 68 | lea edi, .Lpml4 69 | lea eax, .Lpdpt 70 | or eax, PTE_PRESENT 71 | mov dword ptr [edi], eax 72 | 73 | # PDPT[0] = 0 | PTE_PAGE_SIZE | PTE_PRESENT 74 | lea edi, .Lpdpt 75 | mov dword ptr [edi], PTE_PAGE_SIZE | PTE_PRESENT 76 | # PDPT[1] = (1 << 30) | PTE_PAGE_SIZE | PTE_PRESENT 77 | add edi, 8 78 | mov dword ptr [edi], (1 << 30) | PTE_PAGE_SIZE | PTE_PRESENT 79 | # PDPT[2] = 2 * (1 << 30) | PTE_PAGE_SIZE | PTE_PRESENT 80 | add edi, 8 81 | mov dword ptr [edi], 2 * (1 << 30) | PTE_PAGE_SIZE | PTE_PRESENT 82 | # PDPT[3] = 3 * (1 << 30) | PTE_PAGE_SIZE | PTE_PRESENT 83 | add edi, 8 84 | mov dword ptr [edi], 3 * (1 << 30) | PTE_PAGE_SIZE | PTE_PRESENT 85 | 86 | # Enabling 64-bit mode is described in ISDM, Volume 3A, Section 9.8.5. 87 | 88 | # Write PML4 physical address into CR3. 89 | lea eax, .Lpml4 90 | mov cr3, eax 91 | 92 | # Set PAE bit in CR4. 93 | mov eax, cr4 94 | or eax, 1<<5 95 | mov cr4, eax 96 | 97 | # Set IA32_EFER.LME bit (Long Mode Enable). 98 | mov ecx, 0xC0000080 99 | rdmsr 100 | or eax, 1<<8 101 | wrmsr 102 | 103 | # Enable paging by setting PG bit in CR0. 104 | mov eax, cr0 105 | or eax, 1<<31 106 | mov cr0, eax 107 | 108 | # Load GDT with 64-bit code segment. 109 | lgdt .Lgdt64_ptr 110 | 111 | # Load prepared code segment with index 1 with RPL=0 from GDT: segment selector is 0b1000 = 0x8. 112 | ljmp 0x8:.Lret 113 | .Lret: 114 | # We we are now running 64-bit mode. Yay! 115 | .code64 116 | # Load prepared data segments: segment selector is 0b10000 = 0x10. 117 | mov ax, 0 118 | mov ds, ax 119 | mov es, ax 120 | mov gs, ax 121 | mov fs, ax 122 | # Loading null segment in SS allowed only in 64-bit mode. 123 | mov ss, ax 124 | 125 | # Setup booststrap stack and call kernel_main. 126 | lea rsp, .Lbootstrap_stack 127 | call kernel_main 128 | 129 | # Halt processor, i.e. put it in a such state that next instruction will be executed only after interrupt. 130 | # Effectively it means inifinite non-busy loop. 131 | 1: hlt 132 | jmp 1b 133 | 134 | .size _start, . - _start 135 | -------------------------------------------------------------------------------- /homeworks/hw3/boot/early.S: -------------------------------------------------------------------------------- 1 | #include "gdt.h" 2 | #include "mm/paging.h" 3 | 4 | .intel_syntax noprefix 5 | 6 | # Section .early.bss contains zero-filled data. It doesn't appear in ELF file, but filled during image load. 7 | .section .early.bss,"wa",@nobits 8 | .align PAGE_SIZE 9 | .Lbootstrap_stack: 10 | .zero 4*PAGE_SIZE 11 | 12 | .global early_pml4 13 | .type early_pml4, @object 14 | .align PAGE_SIZE 15 | early_pml4: 16 | .zero PAGE_SIZE 17 | 18 | .align PAGE_SIZE 19 | .Learly_pdpt: 20 | .zero PAGE_SIZE 21 | 22 | .global _multiboot_info_addr 23 | _multiboot_info_addr: 24 | .long 0 25 | 26 | # Section .data contains some global variables. 27 | .section .early.data,"wa" 28 | # Setup GDT for 64-bit mode. 29 | .Lgdt64: 30 | # 0: null segment. 31 | .long 0 32 | .long 0 33 | 34 | # 1: 64-bit kernel code segment. 35 | .long 0xFFFF 36 | # (0xF << 16) fills base 31:24. 37 | .long GDT_GRANULARITY | GDT_LONG | GDT_PRESENT | GDT_SYSTEM | (1<<11) | GDT_READ | (0xF << 16) 38 | 39 | .Lgdt64_ptr: 40 | .word . - .Lgdt64 41 | .long .Lgdt64 42 | 43 | .section .early.text 44 | # _early_entry_point is an entry point of our kernel, execution after bootloader starts here. 45 | .global _early_entry_point 46 | .type _early_entry_point, @function 47 | _early_entry_point: 48 | # Tell assembler, that we are running 32-bit protected mode. 49 | .code32 50 | # Our kernel just loaded, current processor state is described in "I386 machine state" section of Multiboot2 specification. 51 | mov _multiboot_info_addr, ebx 52 | 53 | # To enable long mode, we need to setup basic paging. 54 | # Here we setup identity mapping for the first four gigabytes. 55 | 56 | # PML4[0] = PDPT_addr | FLAG_PRESENT 57 | lea edi, early_pml4 58 | lea eax, .Learly_pdpt 59 | or eax, PTE_PRESENT 60 | mov dword ptr [edi], eax 61 | 62 | # PDPT[0] = 0 | PTE_PAGE_SIZE | PTE_PRESENT 63 | lea edi, .Learly_pdpt 64 | mov dword ptr [edi], PTE_PAGE_SIZE | PTE_PRESENT 65 | 66 | # 64-bit mode enabling process is described in ISDM, Volume 3A, Section 9.8.5. 67 | 68 | # Write PML4 physical address into CR3. 69 | lea eax, early_pml4 70 | mov cr3, eax 71 | 72 | # Set PAE bit in CR4. 73 | mov eax, cr4 74 | or eax, 1<<5 75 | mov cr4, eax 76 | 77 | # Set IA32_EFER.LME bit (Long Mode Enable). 78 | mov ecx, 0xC0000080 79 | rdmsr 80 | or eax, 1<<8 81 | wrmsr 82 | 83 | # Enable paging by setting PG bit in CR0. 84 | mov eax, cr0 85 | or eax, 1<<31 86 | mov cr0, eax 87 | 88 | # Load GDT with 64-bit code segment. 89 | lgdt .Lgdt64_ptr 90 | 91 | # Load prepared code segment with index 1 with RPL=0 from GDT: segment selector is 0b1000 = 0x8. 92 | ljmp 0x8:.Lret 93 | .Lret: 94 | # We we are now running 64-bit mode. Yay! 95 | .code64 96 | # Load prepared data segments: segment selector is 0b10000 = 0x10. 97 | mov ax, 0 98 | mov ds, ax 99 | mov es, ax 100 | mov gs, ax 101 | mov fs, ax 102 | # Loading null segment in SS allowed only in 64-bit mode. 103 | mov ss, ax 104 | 105 | # Setup booststrap stack and call kernel_main. 106 | lea rsp, .Lbootstrap_stack 107 | call early_start 108 | 109 | .extern kernel_main 110 | .extern kernel_main_return 111 | .extern early_data 112 | 113 | .global jump_to_kernel_main 114 | .type jump_to_kernel_main, @function 115 | jump_to_kernel_main: 116 | # Because KERNEL_DIRECT_PHYS_MAPPING_START is not representable as imm32, we need move it to register first. 117 | mov rbx, KERNEL_DIRECT_PHYS_MAPPING_START 118 | 119 | # Reset to the same stack, but use its virtual address instead. 120 | lea rsp, .Lbootstrap_stack 121 | add rsp, rbx 122 | 123 | # Fix GDT pointer. 124 | lea rax, .Lgdt64 125 | add rax, rbx 126 | mov qword ptr [.Lgdt64_ptr + 2], rax 127 | lgdt .Lgdt64_ptr 128 | 129 | # If kernel_main occasionally returns, kernel_main_return will be called. 130 | lea rax, kernel_main_return 131 | push rax 132 | 133 | # Use movabs to indicate that we want use imm64. 134 | lea rax, kernel_main 135 | lea rdi, early_data 136 | add rdi, rbx 137 | jmp rax 138 | -------------------------------------------------------------------------------- /homeworks/hw3/drivers/apic.c: -------------------------------------------------------------------------------- 1 | #include "apic.h" 2 | #include "kernel/panic.h" 3 | #include "mm/paging.h" 4 | #include "arch/x86.h" 5 | 6 | #define TYPE_LAPIC 0 7 | #define TYPE_IOAPIC 1 8 | #define TYPE_ISO 2 9 | #define TYPE_NMI 3 10 | #define TYPE_LAPIC_OVERRIDE 4 11 | 12 | #define FLAGS_ACTIVE_LOW 2 13 | #define FLAGS_LEVEL_TRIGGERED 8 14 | 15 | struct ioapic { 16 | uint32_t reg; 17 | uint32_t pad[3]; 18 | uint32_t data; 19 | }; 20 | 21 | volatile uint32_t* lapic_ptr = NULL; 22 | volatile struct ioapic* ioapic_ptr = NULL; 23 | 24 | struct madt_entry { 25 | uint8_t type; 26 | uint8_t length; 27 | uint8_t data[0]; 28 | } __attribute__((packed)); 29 | 30 | struct madt_header { 31 | struct acpi_sdt_header acpi; 32 | uint32_t lapic_addr; 33 | uint32_t flags; 34 | struct madt_entry first_entry; 35 | } __attribute__((packed)); 36 | 37 | static void lapic_write(size_t idx, uint32_t value) { 38 | lapic_ptr[idx / 4] = value; 39 | lapic_ptr[0]; 40 | } 41 | 42 | static uint32_t lapic_read(size_t idx) { 43 | return lapic_ptr[idx / 4]; 44 | } 45 | 46 | #define APIC_ID 0x20 47 | #define APIC_VER 0x30 48 | #define APIC_TASKPRIOR 0x80 49 | #define APIC_EOI 0x0B0 50 | #define APIC_LDR 0x0D0 51 | #define APIC_DFR 0x0E0 52 | #define APIC_SPURIOUS 0x0F0 53 | #define APIC_ESR 0x280 54 | #define APIC_ICRL 0x300 55 | #define APIC_ICRH 0x310 56 | #define APIC_LVT_TMR 0x320 57 | #define APIC_LVT_PERF 0x340 58 | #define APIC_LVT_LINT0 0x350 59 | #define APIC_LVT_LINT1 0x360 60 | #define APIC_LVT_ERR 0x370 61 | #define APIC_TMRINITCNT 0x380 62 | #define APIC_TMRCURRCNT 0x390 63 | #define APIC_TMRDIV 0x3E0 64 | #define APIC_LAST 0x38F 65 | #define APIC_DISABLE 0x10000 66 | #define APIC_SW_ENABLE 0x100 67 | #define APIC_CPUFOCUS 0x200 68 | #define APIC_NMI (4<<8) 69 | #define APIC_INIT 0x500 70 | #define APIC_BCAST 0x80000 71 | #define APIC_LEVEL 0x8000 72 | #define APIC_DELIVS 0x1000 73 | #define TMR_PERIODIC 0x20000 74 | #define TMR_BASEDIV (1<<20) 75 | 76 | #define IOAPIC_REG_TABLE 0x10 77 | 78 | static void ioapic_write(int reg, uint32_t data) { 79 | ioapic_ptr->reg = reg; 80 | ioapic_ptr->data = data; 81 | } 82 | 83 | static void ioapic_enable(int irq, int target_irq) { 84 | ioapic_write(IOAPIC_REG_TABLE + 2 * irq, target_irq); 85 | ioapic_write(IOAPIC_REG_TABLE + 2 * irq + 1, 0); 86 | } 87 | 88 | void apic_init() { 89 | // Find Multiple APIC Description Table, it contains addresses of I/O APIC and LAPIC. 90 | struct madt_header* header = (struct madt_header*)acpi_lookup_rsdt("APIC"); 91 | if (!header) { 92 | panic("MADT not found!"); 93 | } 94 | 95 | lapic_ptr = PHYS_TO_VIRT((uint64_t)header->lapic_addr); 96 | 97 | struct madt_entry* entry = &header->first_entry; 98 | 99 | for (;;) { 100 | if ((uint8_t*)entry >= (uint8_t*)header + header->acpi.length) { 101 | break; 102 | } 103 | 104 | switch (entry->type) { 105 | case TYPE_LAPIC: 106 | break; 107 | case TYPE_IOAPIC: 108 | ioapic_ptr = PHYS_TO_VIRT((*(uint32_t*)(&entry->data[2]))); 109 | break; 110 | } 111 | 112 | entry = (struct madt_entry*)((uint8_t*)entry + entry->length); 113 | } 114 | 115 | if (!ioapic_ptr) { 116 | panic("cannot locate I/O APIC address"); 117 | } 118 | 119 | if (!lapic_ptr) { 120 | panic("cannot locate Local APIC address"); 121 | } 122 | 123 | // Enable APIC, by setting spurious interrupt vector and APIC Software Enabled/Disabled flag. 124 | lapic_write(APIC_SPURIOUS, 39 | APIC_SW_ENABLE); 125 | 126 | // Disable performance monitoring counters. 127 | lapic_write(APIC_LVT_PERF, APIC_DISABLE); 128 | 129 | // Disable local interrupt pins. 130 | lapic_write(APIC_LVT_LINT0, APIC_DISABLE); 131 | lapic_write(APIC_LVT_LINT1, APIC_DISABLE); 132 | 133 | // Signal EOI. 134 | lapic_write(APIC_EOI, 0); 135 | 136 | // Set highest priority for current task. 137 | lapic_write(APIC_TASKPRIOR, 0); 138 | 139 | // APIC timer setup. 140 | // Divide Configuration Registers, set to X1 141 | lapic_write(APIC_TMRDIV, 0xB); 142 | // Interrupt vector and timer mode. 143 | lapic_write(APIC_LVT_TMR, 32 | TMR_PERIODIC); 144 | // Init counter. 145 | lapic_write(APIC_TMRINITCNT, 10000000); 146 | } 147 | 148 | void apic_eoi() { 149 | lapic_write(APIC_EOI, 0); 150 | } 151 | -------------------------------------------------------------------------------- /homeworks/hw4/sched/sched.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "sched.h" 4 | 5 | #include "kernel/errno.h" 6 | #include "kernel/irq.h" 7 | #include "kernel/panic.h" 8 | #include "linker.h" 9 | #include "mm/frame_alloc.h" 10 | #include "mm/obj.h" 11 | #include "mm/paging.h" 12 | 13 | task_t tasks[MAX_TASK_COUNT] = {}; 14 | extern void jump_userspace(); 15 | 16 | static int setup_vmem(vmem_t* vm) { 17 | // Setup direct physical mapping. 18 | uint8_t* virt_addr = (uint8_t*)KERNEL_DIRECT_PHYS_MAPPING_START; 19 | uint8_t* phys_addr = (uint8_t*)0; 20 | for (size_t i = 0; i < (KERNEL_DIRECT_PHYS_MAPPING_SIZE / GB); i++) { 21 | int err = vmem_map_1gb_page(vm, virt_addr, phys_addr, VMEM_WRITE); 22 | if (err < 0) { 23 | return err; 24 | } 25 | virt_addr += GB; 26 | phys_addr += GB; 27 | } 28 | 29 | // Setup kernel sections mapping. 30 | uint8_t* virt_addr_curr = (uint8_t*)KERNEL_SECTIONS_START; 31 | uint8_t* phys_addr_curr = (uint8_t*)&_phys_start_hh; 32 | uint8_t* phys_addr_end = (uint8_t*)&_phys_end_kernel_sections; 33 | while (phys_addr_curr < phys_addr_end) { 34 | int err = vmem_map_2mb_page(vm, virt_addr_curr, phys_addr_curr, VMEM_WRITE); 35 | if (err < 0) { 36 | return err; 37 | } 38 | phys_addr_curr += 2 * MB; 39 | virt_addr_curr += 2 * MB; 40 | } 41 | 42 | // Setup user-space code. 43 | int err = vmem_map_page(vm, (void*)0x10000, &_phys_start_user, VMEM_USER); 44 | if (err < 0) { 45 | return err; 46 | } 47 | 48 | return 0; 49 | } 50 | 51 | static task_t* allocate_task() { 52 | uint64_t curr_pid = 1; 53 | while (tasks[curr_pid].state != TASK_NOT_ALLOCATED && curr_pid < MAX_TASK_COUNT) { 54 | curr_pid++; 55 | } 56 | if (curr_pid == MAX_TASK_COUNT) { 57 | return NULL; 58 | } 59 | tasks[curr_pid].pid = curr_pid; 60 | return &tasks[curr_pid]; 61 | } 62 | 63 | static int setup_init_task() { 64 | task_t* new_task = allocate_task(); 65 | if (new_task == NULL) { 66 | return -ENOMEM; 67 | } 68 | 69 | int err = vmem_init_new(&new_task->vmem); 70 | if (err < 0) { 71 | return err; 72 | } 73 | 74 | err = setup_vmem(&new_task->vmem); 75 | if (err < 0) { 76 | return err; 77 | } 78 | 79 | // Setup user-space stack. 80 | err = vmem_alloc_pages(&new_task->vmem, (void*)0x70000000, 4, VMEM_USER | VMEM_WRITE); 81 | if (err < 0) { 82 | return err; 83 | } 84 | 85 | new_task->state = TASK_RUNNABLE; 86 | 87 | err = arch_thread_new(&new_task->arch_thread, NULL); 88 | if (err < 0) { 89 | return err; 90 | } 91 | return 0; 92 | } 93 | 94 | task_t* _current = NULL; 95 | 96 | static arch_thread_t sched_context = {}; 97 | static vmem_t sched_vmem = {}; 98 | 99 | static void sched_switch_to(task_t* next) { 100 | _current = next; 101 | vmem_switch_to(&next->vmem); 102 | arch_thread_switch(&sched_context, &next->arch_thread); 103 | } 104 | 105 | void sched_start() { 106 | // Interrupts are still disabled. 107 | if (setup_init_task() < 0) { 108 | panic("cannot allocate init task"); 109 | } 110 | 111 | vmem_init_from_current(&sched_vmem); 112 | 113 | irq_enable(); 114 | 115 | for (;;) { 116 | bool found = false; 117 | for (size_t i = 0; i < MAX_TASK_COUNT; i++) { 118 | if (tasks[i].state == TASK_RUNNABLE) { 119 | // We found running task, switch to it. 120 | sched_switch_to(&tasks[i]); 121 | found = 1; 122 | // We've returned to the scheduler. 123 | } 124 | } 125 | 126 | if (!found) { 127 | // If we didn't found a runnable task, wait for next interrupt and retry scheduling. 128 | x86_hlt(); 129 | } 130 | } 131 | } 132 | 133 | void sched_switch() { 134 | BUG_ON_NULL(_current); 135 | task_t* prev = _current; 136 | _current = NULL; 137 | arch_thread_switch(&prev->arch_thread, &sched_context); 138 | } 139 | 140 | void sched_timer_tick() { 141 | if (!_current) { 142 | return; 143 | } 144 | 145 | // TODO: force process preemption here if needed. 146 | } 147 | 148 | int64_t sys_getpid(arch_regs_t* regs) { 149 | UNUSED(regs); 150 | return _current->pid; 151 | } 152 | 153 | int64_t sys_fork(arch_regs_t* parent_regs) { 154 | // TODO: implement me. 155 | } 156 | 157 | _Noreturn int64_t sys_exit(arch_regs_t* regs) { 158 | // TODO: implement me. 159 | 160 | BUG_ON_REACH(); 161 | } 162 | -------------------------------------------------------------------------------- /homeworks/hw4/drivers/apic.c: -------------------------------------------------------------------------------- 1 | #include "apic.h" 2 | #include "kernel/panic.h" 3 | #include "mm/paging.h" 4 | #include "arch/x86/x86.h" 5 | 6 | #define TYPE_LAPIC 0 7 | #define TYPE_IOAPIC 1 8 | #define TYPE_ISO 2 9 | #define TYPE_NMI 3 10 | #define TYPE_LAPIC_OVERRIDE 4 11 | 12 | #define FLAGS_ACTIVE_LOW 2 13 | #define FLAGS_LEVEL_TRIGGERED 8 14 | 15 | struct ioapic { 16 | uint32_t reg; 17 | uint32_t pad[3]; 18 | uint32_t data; 19 | }; 20 | 21 | volatile uint32_t* lapic_ptr = NULL; 22 | volatile struct ioapic* ioapic_ptr = NULL; 23 | 24 | struct madt_entry { 25 | uint8_t type; 26 | uint8_t length; 27 | uint8_t data[0]; 28 | } __attribute__((packed)); 29 | 30 | struct madt_header { 31 | struct acpi_sdt_header acpi; 32 | uint32_t lapic_addr; 33 | uint32_t flags; 34 | struct madt_entry first_entry; 35 | } __attribute__((packed)); 36 | 37 | static void lapic_write(size_t idx, uint32_t value) { 38 | lapic_ptr[idx / 4] = value; 39 | lapic_ptr[0]; 40 | } 41 | 42 | static uint32_t lapic_read(size_t idx) { 43 | return lapic_ptr[idx / 4]; 44 | } 45 | 46 | #define APIC_ID 0x20 47 | #define APIC_VER 0x30 48 | #define APIC_TASKPRIOR 0x80 49 | #define APIC_EOI 0x0B0 50 | #define APIC_LDR 0x0D0 51 | #define APIC_DFR 0x0E0 52 | #define APIC_SPURIOUS 0x0F0 53 | #define APIC_ESR 0x280 54 | #define APIC_ICRL 0x300 55 | #define APIC_ICRH 0x310 56 | #define APIC_LVT_TMR 0x320 57 | #define APIC_LVT_PERF 0x340 58 | #define APIC_LVT_LINT0 0x350 59 | #define APIC_LVT_LINT1 0x360 60 | #define APIC_LVT_ERR 0x370 61 | #define APIC_TMRINITCNT 0x380 62 | #define APIC_TMRCURRCNT 0x390 63 | #define APIC_TMRDIV 0x3E0 64 | #define APIC_LAST 0x38F 65 | #define APIC_DISABLE 0x10000 66 | #define APIC_SW_ENABLE 0x100 67 | #define APIC_CPUFOCUS 0x200 68 | #define APIC_NMI (4<<8) 69 | #define APIC_INIT 0x500 70 | #define APIC_BCAST 0x80000 71 | #define APIC_LEVEL 0x8000 72 | #define APIC_DELIVS 0x1000 73 | #define TMR_PERIODIC 0x20000 74 | #define TMR_BASEDIV (1<<20) 75 | 76 | #define IOAPIC_REG_TABLE 0x10 77 | 78 | static void ioapic_write(int reg, uint32_t data) { 79 | ioapic_ptr->reg = reg; 80 | ioapic_ptr->data = data; 81 | } 82 | 83 | static void ioapic_enable(int irq, int target_irq) { 84 | ioapic_write(IOAPIC_REG_TABLE + 2 * irq, target_irq); 85 | ioapic_write(IOAPIC_REG_TABLE + 2 * irq + 1, 0); 86 | } 87 | 88 | void apic_init() { 89 | // Find Multiple APIC Description Table, it contains addresses of I/O APIC and LAPIC. 90 | struct madt_header* header = (struct madt_header*)acpi_lookup_rsdt("APIC"); 91 | if (!header) { 92 | panic("MADT not found!"); 93 | } 94 | 95 | lapic_ptr = PHYS_TO_VIRT((uint64_t)header->lapic_addr); 96 | 97 | struct madt_entry* entry = &header->first_entry; 98 | 99 | for (;;) { 100 | if ((uint8_t*)entry >= (uint8_t*)header + header->acpi.length) { 101 | break; 102 | } 103 | 104 | switch (entry->type) { 105 | case TYPE_LAPIC: 106 | break; 107 | case TYPE_IOAPIC: 108 | ioapic_ptr = PHYS_TO_VIRT((*(uint32_t*)(&entry->data[2]))); 109 | break; 110 | } 111 | 112 | entry = (struct madt_entry*)((uint8_t*)entry + entry->length); 113 | } 114 | 115 | if (!ioapic_ptr) { 116 | panic("cannot locate I/O APIC address"); 117 | } 118 | 119 | if (!lapic_ptr) { 120 | panic("cannot locate Local APIC address"); 121 | } 122 | 123 | // Enable APIC, by setting spurious interrupt vector and APIC Software Enabled/Disabled flag. 124 | lapic_write(APIC_SPURIOUS, 39 | APIC_SW_ENABLE); 125 | 126 | // Disable performance monitoring counters. 127 | lapic_write(APIC_LVT_PERF, APIC_DISABLE); 128 | 129 | // Disable local interrupt pins. 130 | lapic_write(APIC_LVT_LINT0, APIC_DISABLE); 131 | lapic_write(APIC_LVT_LINT1, APIC_DISABLE); 132 | 133 | // Signal EOI. 134 | lapic_write(APIC_EOI, 0); 135 | 136 | // Set highest priority for current task. 137 | lapic_write(APIC_TASKPRIOR, 0); 138 | 139 | // APIC timer setup. 140 | // Divide Configuration Registers, set to X1 141 | lapic_write(APIC_TMRDIV, 0xB); 142 | // Interrupt vector and timer mode. 143 | lapic_write(APIC_LVT_TMR, 32 | TMR_PERIODIC); 144 | // Init counter. 145 | lapic_write(APIC_TMRINITCNT, 10000000); 146 | } 147 | 148 | void apic_eoi() { 149 | lapic_write(APIC_EOI, 0); 150 | } 151 | -------------------------------------------------------------------------------- /homeworks/hw4/boot/early.S: -------------------------------------------------------------------------------- 1 | #include "arch/x86/gdt.h" 2 | #include "mm/paging.h" 3 | 4 | .intel_syntax noprefix 5 | 6 | # Section .early.bss contains zero-filled data. It doesn't appear in ELF file, but filled during image load. 7 | .section .early.bss,"wa",@nobits 8 | .align PAGE_SIZE 9 | .Lbootstrap_stack_bottom: 10 | .zero 4*PAGE_SIZE 11 | bootstrap_stack_top: 12 | 13 | .global early_pml4 14 | .type early_pml4, @object 15 | .align PAGE_SIZE 16 | early_pml4: 17 | .zero PAGE_SIZE 18 | 19 | .align PAGE_SIZE 20 | .Learly_pdpt: 21 | .zero PAGE_SIZE 22 | 23 | .global _multiboot_info_addr 24 | _multiboot_info_addr: 25 | .long 0 26 | 27 | # Section .data contains some global variables. 28 | .section .early.data,"wa" 29 | # Setup GDT for 64-bit mode. 30 | .Lgdt64: 31 | # 0: null segment. 32 | .long 0 33 | .long 0 34 | 35 | # 1: 64-bit kernel code segment. 36 | .long 0xFFFF 37 | # (0xF << 16) fills base 31:24. 38 | .long GDT_GRANULARITY | GDT_LONG | GDT_PRESENT | GDT_SYSTEM | GDT_CODE_SEG | GDT_READ | (0xF << 16) 39 | 40 | .Lgdt64_ptr: 41 | .word . - .Lgdt64 42 | .quad .Lgdt64 43 | 44 | .macro CLEAR start size 45 | mov esi, \start 46 | mov al, 0 47 | mov ecx, \size 48 | rep stosb 49 | .endm 50 | 51 | .section .early.text 52 | # _early_entry_point is an entry point of our kernel, execution after bootloader starts here. 53 | .global _early_entry_point 54 | .type _early_entry_point, @function 55 | _early_entry_point: 56 | # Tell assembler, that we are running 32-bit protected mode. 57 | .code32 58 | # Our kernel just loaded, current processor state is described in "I386 machine state" section of Multiboot2 specification. 59 | mov _multiboot_info_addr, ebx 60 | 61 | # To enable long mode, we need to setup basic paging. 62 | # Here we setup identity mapping for the first four gigabytes. 63 | 64 | CLEAR early_pml4 PAGE_SIZE 65 | CLEAR .Learly_pdpt PAGE_SIZE 66 | 67 | # PML4[0] = PDPT_addr | FLAG_PRESENT 68 | lea edi, early_pml4 69 | lea eax, .Learly_pdpt 70 | or eax, PTE_PRESENT 71 | mov dword ptr [edi], eax 72 | 73 | # PDPT[0] = 0 | PTE_PAGE_SIZE | PTE_PRESENT 74 | lea edi, .Learly_pdpt 75 | mov dword ptr [edi], PTE_PAGE_SIZE | PTE_PRESENT 76 | 77 | # 64-bit mode enabling process is described in ISDM, Volume 3A, Section 9.8.5. 78 | 79 | # Write PML4 physical address into CR3. 80 | lea eax, early_pml4 81 | mov cr3, eax 82 | 83 | # Set PAE bit in CR4. 84 | mov eax, cr4 85 | or eax, 1<<5 86 | mov cr4, eax 87 | 88 | # Set IA32_EFER.LME bit (Long Mode Enable). 89 | mov ecx, 0xC0000080 90 | rdmsr 91 | or eax, 1<<8 92 | wrmsr 93 | 94 | # Enable paging by setting PG bit in CR0. 95 | mov eax, cr0 96 | or eax, 1<<31 97 | mov cr0, eax 98 | 99 | # Load GDT with 64-bit code segment. 100 | lgdt .Lgdt64_ptr 101 | 102 | # Load prepared code segment with index 1 with RPL=0 from GDT: segment selector is 0b1000 = 0x8. 103 | ljmp 0x8:.Lret 104 | .Lret: 105 | # We we are now running 64-bit mode. Yay! 106 | .code64 107 | # Load prepared data segments: segment selector is 0b10000 = 0x10. 108 | mov ax, 0 109 | mov ds, ax 110 | mov es, ax 111 | mov gs, ax 112 | mov fs, ax 113 | # Loading null segment in SS allowed only in 64-bit mode. 114 | mov ss, ax 115 | 116 | # Setup booststrap stack and call kernel_main. 117 | lea rsp, bootstrap_stack_top 118 | call early_start 119 | 120 | .extern kernel_main 121 | .extern kernel_main_return 122 | .extern early_data 123 | 124 | .global jump_to_kernel_main 125 | .type jump_to_kernel_main, @function 126 | jump_to_kernel_main: 127 | # Because KERNEL_DIRECT_PHYS_MAPPING_START is not representable as imm32, we need move it to register first. 128 | mov rbx, KERNEL_DIRECT_PHYS_MAPPING_START 129 | 130 | # Reset to the same stack, but use its virtual address instead. 131 | lea rsp, bootstrap_stack_top 132 | add rsp, rbx 133 | mov rbp, rsp 134 | 135 | # If kernel_main occasionally returns, kernel_main_return will be called. 136 | lea rax, kernel_main_return 137 | push rax 138 | 139 | # Use movabs to indicate that we want use imm64. 140 | lea rax, kernel_main 141 | lea rdi, early_data 142 | add rdi, rbx 143 | jmp rax 144 | -------------------------------------------------------------------------------- /homeworks/hw2/apic.c: -------------------------------------------------------------------------------- 1 | #include "apic.h" 2 | #include "panic.h" 3 | #include "paging.h" 4 | 5 | #define TYPE_LAPIC 0 6 | #define TYPE_IOAPIC 1 7 | #define TYPE_ISO 2 8 | #define TYPE_NMI 3 9 | #define TYPE_LAPIC_OVERRIDE 4 10 | 11 | #define FLAGS_ACTIVE_LOW 2 12 | #define FLAGS_LEVEL_TRIGGERED 8 13 | 14 | struct ioapic { 15 | uint32_t reg; 16 | uint32_t pad[3]; 17 | uint32_t data; 18 | }; 19 | 20 | volatile uint32_t* lapic_ptr = NULL; 21 | volatile struct ioapic* ioapic_ptr = NULL; 22 | 23 | struct madt_entry { 24 | uint8_t type; 25 | uint8_t length; 26 | uint8_t data[0]; 27 | } __attribute__((packed)); 28 | 29 | struct madt_header { 30 | struct acpi_sdt_header acpi; 31 | uint32_t lapic_addr; 32 | uint32_t flags; 33 | struct madt_entry first_entry; 34 | } __attribute__((packed)); 35 | 36 | static void lapic_write(size_t idx, uint32_t value) { 37 | lapic_ptr[idx / 4] = value; 38 | lapic_ptr[0]; 39 | } 40 | 41 | static uint32_t lapic_read(size_t idx) { 42 | return lapic_ptr[idx / 4]; 43 | } 44 | 45 | #define APIC_ID 0x20 46 | #define APIC_VER 0x30 47 | #define APIC_TASKPRIOR 0x80 48 | #define APIC_EOI 0x0B0 49 | #define APIC_LDR 0x0D0 50 | #define APIC_DFR 0x0E0 51 | #define APIC_SPURIOUS 0x0F0 52 | #define APIC_ESR 0x280 53 | #define APIC_ICRL 0x300 54 | #define APIC_ICRH 0x310 55 | #define APIC_LVT_TMR 0x320 56 | #define APIC_LVT_PERF 0x340 57 | #define APIC_LVT_LINT0 0x350 58 | #define APIC_LVT_LINT1 0x360 59 | #define APIC_LVT_ERR 0x370 60 | #define APIC_TMRINITCNT 0x380 61 | #define APIC_TMRCURRCNT 0x390 62 | #define APIC_TMRDIV 0x3E0 63 | #define APIC_LAST 0x38F 64 | #define APIC_DISABLE 0x10000 65 | #define APIC_SW_ENABLE 0x100 66 | #define APIC_CPUFOCUS 0x200 67 | #define APIC_NMI (4<<8) 68 | #define APIC_INIT 0x500 69 | #define APIC_BCAST 0x80000 70 | #define APIC_LEVEL 0x8000 71 | #define APIC_DELIVS 0x1000 72 | #define TMR_PERIODIC 0x20000 73 | #define TMR_BASEDIV (1<<20) 74 | 75 | static inline void outb(uint16_t port, uint8_t data) { 76 | asm volatile("out %0,%1" : : "a" (data), "d" (port)); 77 | } 78 | 79 | #define IOAPIC_REG_TABLE 0x10 80 | 81 | static void ioapic_write(int reg, uint32_t data) { 82 | ioapic_ptr->reg = reg; 83 | ioapic_ptr->data = data; 84 | } 85 | 86 | static void ioapic_enable(int irq, int target_irq) { 87 | ioapic_write(IOAPIC_REG_TABLE + 2 * irq, target_irq); 88 | ioapic_write(IOAPIC_REG_TABLE + 2 * irq + 1, 0); 89 | } 90 | 91 | void apic_init() { 92 | // Find Multiple APIC Description Table, it contains addresses of I/O APIC and LAPIC. 93 | struct madt_header* header = (struct madt_header*)acpi_lookup_rsdt("APIC"); 94 | if (!header) { 95 | panic("MADT not found!"); 96 | } 97 | 98 | lapic_ptr = (volatile uint32_t*)header->lapic_addr; 99 | 100 | struct madt_entry* entry = &header->first_entry; 101 | 102 | for (;;) { 103 | if ((uint8_t*)entry >= (uint8_t*)header + header->acpi.length) { 104 | break; 105 | } 106 | 107 | switch (entry->type) { 108 | case TYPE_LAPIC: 109 | break; 110 | case TYPE_IOAPIC: 111 | ioapic_ptr = (volatile struct ioapic*)(*(uint32_t*)(&entry->data[2])); 112 | break; 113 | } 114 | 115 | entry = (struct madt_entry*)((uint8_t*)entry + entry->length); 116 | } 117 | 118 | if (!ioapic_ptr) { 119 | panic("cannot locate I/O APIC address"); 120 | } 121 | 122 | if (!lapic_ptr) { 123 | panic("cannot locate Local APIC address"); 124 | } 125 | 126 | // Disable old PIC. 127 | outb(0x21, 0xff); 128 | outb(0xa1, 0xff); 129 | 130 | // Enable APIC, by setting spurious interrupt vector and APIC Software Enabled/Disabled flag. 131 | lapic_write(APIC_SPURIOUS, 39 | APIC_SW_ENABLE); 132 | 133 | // Disable performance monitoring counters. 134 | lapic_write(APIC_LVT_PERF, APIC_DISABLE); 135 | 136 | // Disable local interrupt pins. 137 | lapic_write(APIC_LVT_LINT0, APIC_DISABLE); 138 | lapic_write(APIC_LVT_LINT1, APIC_DISABLE); 139 | 140 | // Signal EOI. 141 | lapic_write(APIC_EOI, 0); 142 | 143 | // Set highest priority for current task. 144 | lapic_write(APIC_TASKPRIOR, 0); 145 | 146 | // TODO: calibrate APIC timer to fire interrupt every millisecond. 147 | 148 | // APIC timer setup. 149 | // Divide Configuration Registers, set to X1 150 | lapic_write(APIC_TMRDIV, 0xB); 151 | // Interrupt vector and timer mode. 152 | lapic_write(APIC_LVT_TMR, 32 | TMR_PERIODIC); 153 | // Init counter. 154 | lapic_write(APIC_TMRINITCNT, 10000000); 155 | } 156 | 157 | void apic_eoi() { 158 | lapic_write(APIC_EOI, 0); 159 | } 160 | -------------------------------------------------------------------------------- /lectures/03-interrupts/main.md: -------------------------------------------------------------------------------- 1 | --- 2 | marp: true 3 | paginate: true 4 | --- 5 | 6 | 12 | 13 | # Прерывания процессора 14 | 15 | 16 | --- 17 | # Прерывания 18 | * Interrupts – события, генерируемые перефирией, отправляются процессору через специальный пин 19 | * Exceptions — события, возникающие во время обработки инструкций: доступ к несуществующей памяти, некорректная кодировка инструкции, etc 20 | * Software interrupts — прерывания, вызываемые инструкцией `int` 21 | 22 | --- 23 | # Прерывания 24 | * IA-32 определяет 256 прерываний: от 0 до 255 25 | * Прерывания 0-31 используются процессором для architecture-defined exceptions, они часто обозначаются `#PF`, `#GP`, ... 26 | * Прерывания 32-255 можно использовать как угодно 27 | * Прерывания можно временно отключать, для этого существуют инструкции `cli`/`sti`, также можно поменять IF внутри EFLAGS с помощью `pushf`/`popf` 28 | 29 | --- 30 | # Некоторые exceptions 31 | * #UD (6) — undefined opcode 32 | * #GP (13) — general protection fault 33 | * #PF (14) — page fault 34 | * ... 35 | 36 | --- 37 | # Fault vs trap, error code 38 | * Fault возникает *во время исполнения* инструкции; после возврата из обработчика, инструкция исполняется заново 39 | * Trap возникает *сразу после исполнения* инструкции; после возврата из обработчика, начнёт выполняться *следующая* инструкция 40 | * Также некоторые исключения код ошибки, он имеет разные значения для разных исключений 41 | 42 | --- 43 | # Double fault 44 | * Если исключение случилось во время обработки исключения, возникает double fault 45 | * Double fault можно обработать (#DF, 8), однако вернуться в предыдущий контекст нельзя 46 | * Если во время обработки double fault происходит ещё одно исключение, то процессор переходит в специальный режим: shutdown mode 47 | * Иногда это называют triple fault 48 | 49 | --- 50 | # NMI 51 | * Non-maskable interrupts 52 | * Используются для сообщения очень срочных вещей процессору: например, ошибки памяти 53 | * IF никак не влияет на их появление 54 | * Nested NMI запрещены 55 | 56 | --- 57 | # Interrupt Descriptor Table (IDT) 58 | * ISDM, Volume 3A, 6.10 59 | * Синонимы: interrupt vector, вектор прерываний 60 | * Аналогично GDT содержит в себе дескрипторы — gate descriptors 61 | * Task gate, interrupt gate и trap gate 62 | * Загрузка IDT происходит аналогично GDT: описывается 6 байтный IDT pointer, затем вызывается `lidt` 63 | 64 | --- 65 | # Interrupt Descriptor Table (IDT) 66 | ![center](idt_descriptor.png) 67 | 68 | --- 69 | # Обработка прерывания 70 | * При входе в обработчик прерывания, процессор прыгает в указанное место, сохраняя на стеке текущее состояние процессора 71 | * Обратите внимание, что место прыжка задаётся *виртуальным* адресом, поэтому обработчики прерываний всегда должны быть подмаплены в текущее адресное пространство 72 | * Т.к. прерывание происходит после любой инструкции, мы не можем полагаться на calling conventions, необходимо сохранять все регистры, а затем их восстанавливать 73 | * Для выхода из обработчика используется `iret` 74 | * Interrupt gate (в отличие от trap gate) выставляет IF=0 перед входом в обработчик, запрещая interrupt in interrupt 75 | 76 | --- 77 | # Interrupt stack frame 78 | ![center](https://os.phil-opp.com/handling-exceptions/exception-stack-frame.svg) 79 | 80 | --- 81 | # APIC 82 | * Контроллер прерываний, ISDM, Vol. 3A, 10 83 | * Local APIC = LAPIC, размещается на кристалле процессора, один на ядро 84 | * I/O APIC принимает сигналы от переферии и перенаправляет их в ядра процессора 85 | * Inter-processor interrupts = IPIs 86 | * APIC Timer 87 | 88 | --- 89 | # APIC 90 | ![center](apic.png) 91 | 92 | --- 93 | # LAPIC 94 | * Управляется через специальный регион памяти, memory-mapped IO = MMIO 95 | * Для каждого ядра адрес один, но LAPIC разный 96 | * Базовый адрес можно найти через MSR `IA32_APIC_BASE` 97 | * Local vector table (LVT) описывает как обслуживаются различные прерывания: edge/level triggered, mask/unmask 98 | * EOI — окончание текущего прерывания 99 | 100 | --- 101 | # LAPIC 102 | ![center height:600px](lapic.png) 103 | 104 | --- 105 | # IOAPIC 106 | * Не описывается в ISDM 107 | * [Cпецификация](https://pdos.csail.mit.edu/6.828/2016/readings/ia32/ioapic.pdf), которую сложно найти и легко потерять :) 108 | * Основан на 82093AA I/O APIC (90-ые) 109 | * Как и LAPIC, управляется с помощью MMIO 110 | * Базовый адрес обычно находят через таблицы ACPI 111 | 112 | --- 113 | # ACPI 114 | * Advanced Configuration and Power Interface 115 | * Стандарт, который описывает, как работать с переферией и как управлять питанием 116 | * APICA = ACPI Component Architecture — открытая реализация ACPI 117 | * Чтобы интегрировать APICA в вашу ОС нужно реализовать специальный интерфейс — OSL (OS interface layer) 118 | * Нас пока будут интересовать только ACPI таблицы 119 | * RSDP (Root System Description Table) -> XSDT (eXtended System Descriptor Table) -> MADT (Multiple APIC Description Table) 120 | 121 | --- 122 | # ありがとう! 123 | -------------------------------------------------------------------------------- /lectures/11-buses-and-drives/main.md: -------------------------------------------------------------------------------- 1 | --- 2 | marp: true 3 | paginate: true 4 | --- 5 | 6 | 12 | 13 | # I/O устройства 14 | 15 | --- 16 | 17 | # Шины 18 | * Шина — это комплекс различных схем, которые позволяют перемещать данные между компонентами компьютера 19 | * System bus, data bus, address bus, internal bus 20 | 21 | --- 22 | 23 | # I/O ports 24 | * x86 т.н. "I/O bus" 25 | * На ней есть 65536 8-битных портов I/O-портов 26 | * Каждая пара портов может выглядеть как единый 16-битный порт, но такие порты должны начинаться с чётного номера 27 | * Исторически x86 использовал I/O порты для общения с устройствами, сейчас эта модель устаревает 28 | * Примеры: PIC, PS/2. 29 | 30 | --- 31 | 32 | # MMIO 33 | * Memory-mapped I/O 34 | * Специальные адреса физической памяти (шины данных) выделены для управления регистрами устройств 35 | * Примеры: большинство современных устройств, APIC. 36 | 37 | --- 38 | 39 | # DMA: bus mastering 40 | * DMA = Direct Memory Access 41 | * Bus mastering — это фича шины, которая позволяет пересылать данные напрямую из/в память без участия CPU 42 | * Одно из устройств может завладеть шиной данных (become a bus master) и инициировать запись в RAM 43 | * First-party DMA 44 | * В теории возможно читать/писать не только в RAM, но сейчас так не делается 45 | 46 | --- 47 | 48 | # Third-party DMA 49 | * Единый DMA контроллер на всю шину, который контроллирует все трансферы 50 | * Не требует наличия DMA-компонентов в своих устройствах 51 | * Каждый трансфер требует инициации из CPU (однако копирования не требует) 52 | 53 | --- 54 | 55 | # ISA 56 | * Industry Standard Architecture 57 | * 16-битная шина IBM-совместимых компьютеров 58 | * Не поддерживает Plug'n'Play (PnP) 59 | * Intel 8237 DMA controller (third-party DMA) 60 | 61 | 62 | --- 63 | 64 | # PCI 65 | * Peripheral Component Interconnect 66 | * Придумана Intel в начале 90-ых 67 | * 64-битная шина 68 | * Более быстрая (8 MHz vs 33 MHz) 69 | * Поддерживает PnP 70 | * PCI bus mastering 71 | * Пришла как дополнение к ISA, поэтому долгое времям компьютеры поддерживали и ISA, и PCI 72 | 73 | --- 74 | 75 | # IDE/ATA 76 | * IDE (Integrated Drive Electronics) или ATA (AT Attachment) — старинный интерфейс доступа к дискам 77 | * Разработан в 80-ых годах 78 | * Под ATA обычно имеется в виду сам интерфейс, под IDE обычно — наличие также контроллера диска 79 | * PATA = Parallel ATA ([ссылки на спецификации](https://ata.wiki.kernel.org/index.php/Developer_Resources)) 80 | * ATA использует I/O порты для управления устройством 81 | * Однако передача данных может осущствляться как через I/O порты, так и через DMA 82 | 83 | --- 84 | 85 | # IDE/ATA 86 | * Каждый подключённый контроллер позволяет подключать к нему четыре диска: primary/secondary bus + master/slave 87 | * Для каждой "шины", используется отдельный набор I/O-портов 88 | * Для выбора master/slave используется cпециальный бит в одном из этих портов (Drive Register) 89 | * CHS (Cylinder-Head-Sector) addressing — сильно устаревшая схема адресации дисков 90 | * LBA (Logical Block Address) addressing — линейная схема адресации секторов диска 91 | 92 | --- 93 | 94 | # ATA PIO 95 | * В регистры устройста загружается нужный адрес сектора, количество секторов на чтение и отправляется команда на начало трансфера 96 | * После окончания считывания присылается IRQ (или можно подождать специальный флаг в busy-loop) 97 | * Затем процессор копирует 256 16-битных значений из data port 98 | * Процесс повторяется, пока не прочитано нужное количество секторов 99 | * За одно чтение можно прочитать 65536 секторов (32 мегабайта) 100 | * 28-bit PIO vs 48-bit PIO 101 | 102 | --- 103 | 104 | # Интерфейс PCI 105 | * Концептуально управляющие регистры PCI устройства подключаются к PCI-пространству 106 | * Они доступны для управления через два I/O порта: `CONFIG_ADDRESS` (`0xcf8`) и `CONFIG_DATA` (`0xcfc`) 107 | * `CONFIG_ADDRESS` кодирует адрес регистра который состоит из bus number, device number, function number и register offset 108 | * Из `CONFIG_DATA` можно прочитать регистр или записать в него 109 | 110 | --- 111 | 112 | # ATA & DMA via PCI Bus mastering 113 | * Трансферы DMA всегда происходят по *физическим адресам* памяти 114 | * Трансферы не могут превышать 64 Кб 115 | * Адреса памяти, в/из которых происходят трансферы, должны быть выровненны по границе 64 Кб и должны располагаться в первых 4 Гб физической памяти 116 | * Для каждого трансфера создаётся специальная PRDT (Physcal Region Descriptor Table), состоящая из PRD — описания региона физической памяти (базовый адрес и размер) 117 | * Из PCI регистра BAR4 берётся адрес т.н. Bus Master Register 118 | * BMR состоит из регистра статуса, регистра команд и нескольих I/O портов для записи адреса (физического) PRDT 119 | * После инициализации DMA, выполняются аналогичные действия как при PIO 120 | 121 | --- 122 | 123 | # AHCI & SATA 124 | * AHCI = Advance Host Controller Interface 125 | * SATA = Serial ATA 126 | * AHCI — адаптер (host bus adapter) SATA-to-PCI, разработан в Intel 127 | * SATA — протокол общения с дисками, который пришёл на замену PATA 128 | * SATA быстрее быстрее, требует меньше пинов => более энергоэффективен 129 | -------------------------------------------------------------------------------- /homeworks/hw4/mm/vmem.c: -------------------------------------------------------------------------------- 1 | #include "vmem.h" 2 | #include "frame_alloc.h" 3 | #include "obj.h" 4 | #include "paging.h" 5 | #include "kernel/errno.h" 6 | #include "kernel/panic.h" 7 | #include "common.h" 8 | 9 | static uint64_t convert_flags(uint64_t flags) { 10 | uint64_t pte_flags = 0; 11 | if (flags & VMEM_USER) { 12 | pte_flags |= PTE_USER; 13 | } 14 | if (flags & VMEM_WRITE) { 15 | pte_flags |= PTE_WRITE; 16 | } 17 | return pte_flags; 18 | } 19 | 20 | static void* ensure_next_table(pte_t* tbl, size_t idx, uint64_t raw_flags) { 21 | pte_t pte = tbl[idx]; 22 | void* next_tbl = NULL; 23 | if (pte & PTE_PRESENT) { 24 | next_tbl = PHYS_TO_VIRT(PTE_ADDR(pte)); 25 | tbl[idx] |= raw_flags; 26 | } else { 27 | next_tbl = frame_alloc(); 28 | if (next_tbl == NULL) { 29 | return NULL; 30 | } 31 | memset(next_tbl, '\0', PAGE_SIZE); 32 | tbl[idx] = (uint64_t)VIRT_TO_PHYS(next_tbl) | PTE_PRESENT | raw_flags; 33 | } 34 | return next_tbl; 35 | } 36 | 37 | // translate_address translate virtual address vaddr into physical address and returns virtual address from direct mapping. 38 | static int translate_address(vmem_t* vm, void* vaddr, void** paddr) { 39 | // TODO: implement me. 40 | return 0; 41 | } 42 | 43 | int vmem_map_1gb_page(vmem_t* vm, void* virt_addr, void* phys_addr, uint64_t flags) { 44 | BUG_ON(((uint64_t)phys_addr) % GB); 45 | BUG_ON(((uint64_t)virt_addr) % GB); 46 | 47 | flags = convert_flags(flags); 48 | 49 | pdpt_t* pdpt = ensure_next_table(vm->pml4->entries, PML4E_FROM_ADDR(virt_addr), flags); 50 | if (pdpt == NULL) { 51 | return -ENOMEM; 52 | } 53 | 54 | uint64_t pdpe = pdpt->entries[PDPE_FROM_ADDR(virt_addr)]; 55 | BUG_ON((pdpe & PTE_PRESENT) && !(pdpe & PTE_PAGE_SIZE)); 56 | pdpt->entries[PDPE_FROM_ADDR(virt_addr)] = (uint64_t)phys_addr | PTE_PRESENT | PTE_PAGE_SIZE | flags; 57 | return 0; 58 | } 59 | 60 | int vmem_map_2mb_page(vmem_t* vm, void* virt_addr, void* phys_addr, uint64_t flags) { 61 | BUG_ON(((uint64_t)phys_addr) % (2*MB)); 62 | BUG_ON(((uint64_t)virt_addr) % (2*MB)); 63 | 64 | flags = convert_flags(flags); 65 | 66 | pdpt_t* pdpt = ensure_next_table(vm->pml4->entries, PML4E_FROM_ADDR(virt_addr), flags); 67 | if (pdpt == NULL) { 68 | return -ENOMEM; 69 | } 70 | 71 | pgdir_t* pgdir = ensure_next_table(pdpt->entries, PDPE_FROM_ADDR(virt_addr), flags); 72 | if (pgdir == NULL) { 73 | return -ENOMEM; 74 | } 75 | 76 | uint64_t pde = pgdir->entries[PDE_FROM_ADDR(virt_addr)]; 77 | BUG_ON((pde & PTE_PRESENT) && !(pde & PTE_PAGE_SIZE)); 78 | pgdir->entries[PDE_FROM_ADDR(virt_addr)] = (uint64_t)phys_addr | PTE_PRESENT | PTE_PAGE_SIZE | flags; 79 | return 0; 80 | } 81 | 82 | int vmem_map_page(vmem_t* vm, void* virt_addr, void* phys_addr, uint64_t flags) { 83 | flags = convert_flags(flags); 84 | 85 | pdpt_t* pdpt = ensure_next_table(vm->pml4->entries, PML4E_FROM_ADDR(virt_addr), flags); 86 | if (pdpt == NULL) { 87 | return -ENOMEM; 88 | } 89 | 90 | pgdir_t* pgdir = ensure_next_table(pdpt->entries, PDPE_FROM_ADDR(virt_addr), flags); 91 | if (pgdir == NULL) { 92 | return -ENOMEM; 93 | } 94 | 95 | pgtbl_t* pgtbl = ensure_next_table(pgdir->entries, PDE_FROM_ADDR(virt_addr), flags); 96 | if (pgtbl == NULL) { 97 | return -ENOMEM; 98 | } 99 | 100 | pgtbl->entries[PTE_FROM_ADDR(virt_addr)] = (uint64_t)phys_addr | PTE_PRESENT | flags; 101 | return 0; 102 | } 103 | 104 | OBJ_ALLOC_DEFINE(vmem_area_alloc, vmem_area_t); 105 | 106 | int vmem_alloc_pages(vmem_t* vm, void* virt_addr, size_t pgcnt, uint64_t flags) { 107 | size_t allocated = 0; 108 | void* start_addr = virt_addr; 109 | while (allocated < pgcnt) { 110 | void* frame = frame_alloc(); 111 | if (frame == NULL) { 112 | return -ENOMEM; 113 | } 114 | memset(frame, '\0', PAGE_SIZE); 115 | int err = vmem_map_page(vm, virt_addr, VIRT_TO_PHYS(frame), flags); 116 | if (err < 0) { 117 | return -ENOMEM; 118 | } 119 | virt_addr += PAGE_SIZE; 120 | allocated++; 121 | } 122 | 123 | vmem_area_t* area = object_alloc(&vmem_area_alloc); 124 | area->start = start_addr; 125 | area->pgcnt = pgcnt; 126 | area->flags = flags; 127 | area->next = vm->areas_head; 128 | vm->areas_head = area; 129 | 130 | return 0; 131 | } 132 | 133 | void vmem_init_from_current(vmem_t* vm) { 134 | vm->pml4 = PHYS_TO_VIRT(x86_read_cr3()); 135 | } 136 | 137 | void vmem_switch_to(vmem_t* vm) { 138 | x86_write_cr3((uint64_t)VIRT_TO_PHYS(vm->pml4)); 139 | } 140 | 141 | 142 | void vmem_destroy(vmem_t* vm) { 143 | // TODO: implement me. 144 | } 145 | 146 | int vmem_clone_from_current(vmem_t* dst, vmem_t* curr) { 147 | // TODO: implement me. Notice that curr is an active address space, so you can directly access its virtual addresses. 148 | return 0; 149 | } 150 | 151 | int vmem_init_new(vmem_t* vm) { 152 | vm->pml4 = frame_alloc(); 153 | if (vm->pml4 == NULL) { 154 | return -ENOMEM; 155 | } 156 | memset(vm->pml4, '\0', sizeof(pml4_t)); 157 | return 0; 158 | } 159 | -------------------------------------------------------------------------------- /lectures/07-tasks/main.md: -------------------------------------------------------------------------------- 1 | --- 2 | marp: true 3 | paginate: true 4 | --- 5 | 6 | 12 | 13 | # Процессы 14 | 15 | --- 16 | 17 | # Software interrupts 18 | * Ещё один вид прерываний, вызываемые специальными инструкциями 19 | * `int X`, `int3`, `into`, `int1` 20 | 21 | --- 22 | 23 | # CPL, DPL и RPL 24 | * CPL (current privilege level) – текущий уровень привилегий, определяется регистром `cs` 25 | * DPL (descriptor privilege level) – уровень привилегий, откуда доступен сегмент (например, DPL в IDT определяет уровень привилегий для софтварных прерываний) 26 | * RPL (requested privilege level) — то, что записывается в нижние биты segment selector 27 | 28 | --- 29 | 30 | # TSS 31 | * Task-state segment 32 | * В protected mode может использоваться для hardware multitasking 33 | * В 64-bit mode используется для хранения информации 34 | * В современных ОС требутся заводить по одному TSS на ядро процессора 35 | 36 | --- 37 | 38 | ![center width:500px](./tss_64.jpg) 39 | 40 | --- 41 | 42 | # Stack switching 43 | * Если при входе в прерывание меняется CPL, то процессор изменяет RSP на соответсвующий новому CPL в TSS 44 | * `RSP0` для ring0, `RSP1` для ring1, итд 45 | * Если CPL не изменяется, то RSP не меняется и фрейм прерывания кладётся прямо на текущий стек 46 | 47 | --- 48 | 49 | # Interrupt stack table 50 | * IST появился впервые в 64-bit mode 51 | * Позволяет прерываниям назначать отдельный стек при входе (переключается безусловно) 52 | * Какой стек выбрать описывается в IDT (3 бита) 53 | * 0 зарезервирован — обозначает то, что прерывание должно использовать старый механизм переключения стека 54 | 55 | --- 56 | 57 | # Процессы, системные вызовы и переключение контекста 58 | 59 | --- 60 | 61 | # Что такое процесс? 62 | 63 | --- 64 | 65 | # Что такое процесс? 66 | * Процесс — это «запущенная программа», исполняемая процессором 67 | * Результат загрузки программы из исполняемого файла называется *образом* (image) 68 | * Контекст исполнения: RIP, EFLAGS, RSP, etc 69 | * Адресное пространство 70 | * Ресурсы ОС: файловые дескрипторы, сокеты итд 71 | 72 | --- 73 | 74 | # Процессы и треды 75 | * Внутри процесса может быть несколько потоков 76 | * Удобно представлять процесс как группу тредов, разделяющих одно адресное пространство и некоторые другие ресурсы 77 | * В Linux треды называются тасками и описываются `struct task_struct` 78 | 79 | --- 80 | 81 | # Состояния процесса 82 | ![center](./task-states.png) 83 | 84 | --- 85 | 86 | # User-space и kernel-space 87 | * После перехода в обработчик системного вызова, процесс находится в kernel-space 88 | * Ядро использует выделеный per-task стек небольшого размера — kernel stack 89 | * В kernel-space тоже могут возникать прерывания, поэтому в критических местах их нужно отключать 90 | 91 | --- 92 | 93 | # Системные вызовы 94 | * Процессы изолированы от периферии, поэтому нужен способ взаимодействия с операционной системой 95 | * Системные вызовы — это API для ОС 96 | 97 | --- 98 | 99 | # Системные вызовы: `int 0x80` 100 | * 32-bit linux для системных вызовов использует программное прерывание 128 (0x80) 101 | * В `eax` передаётся номер системного вызова 102 | * Аргументы передаются через регистры (по порядку): `ebx`, `ecx`, `edx`, `esi`, `edi` 103 | * Прерывания — медленный механизм: проверки на стороне процесса 104 | 105 | --- 106 | 107 | # Системные вызовы: `sysenter/sysexit` 108 | * Вариант Intel 109 | * AMD поддерживает только в 32 bit mode 110 | * IA32_SYSENTER_CS задаёт описание «виртуального» code segment: он не описывается в GDT/LDT, а имеет константные значения 111 | * `IA32_SYSENTER_ESP` — стек при заходе в ядро 112 | * `IA32_SYSENTER_EIP` — адрес точки входа 113 | * Не является частью ABI ядра 114 | 115 | --- 116 | 117 | # Системные вызовы: `syscall/sysret` 118 | * Изначально придуман AMD 119 | * Intel поддерживает только в 64 bit mode 120 | * `IA32_LSTAR` — адрес точки входа 121 | * `IA32_STAR` аналогично `IA32_SYSENTER_CS` 122 | * `rip -> rcx`, `rflags -> r11` 123 | * Аргументы передаются через регистры (по порядку): `rdi`, `rsi`, `rdx`, `r10`, `r8`, `r9` 124 | 125 | --- 126 | 127 | # Переключение контекста 128 | 129 | --- 130 | ![center](./context-switch.png) 131 | 132 | --- 133 | # Переключение контекста: `schedule` 134 | ```c 135 | void schedule() { 136 | // called from task A 137 | struct task* next = select_next_task(); 138 | switch_context(current, &next); 139 | // already in task B 140 | vmem_switch_to(next->vmem); 141 | restore_fpu(next->fpu_ctx); 142 | // ... 143 | } 144 | ``` 145 | 146 | --- 147 | 148 | # Переключение контекста: `switch_context` 149 | * Основная магия 150 | * Переключает процесс на другой ядерный стек 151 | * Можно не сохранять все регистры, только callee-saved, т.к. компилятор знает, что вызывает функцию, => уже сохранит нужный ему контекст 152 | 153 | --- 154 | 155 | # Переключение контекста: `switch_context` 156 | ```x86asm 157 | // rdi points to current context 158 | // rsi points to next context 159 | switch_context: 160 | push rbp 161 | push rbx 162 | // ... 163 | 164 | // save current stack 165 | mov qword ptr [rdi], rsp 166 | 167 | // restore next stack 168 | mov rsp, qword ptr [rsi] 169 | 170 | // ... 171 | pop rbx 172 | pop rbp 173 | ret 174 | ``` 175 | 176 | --- 177 | 178 | # На этом всё! 179 | --------------------------------------------------------------------------------