├── .clang-format ├── .gitignore ├── README.md ├── _img ├── bootloader-1.png └── kernel-1.png ├── bootloader ├── .gitignore ├── Makefile ├── acpi.c ├── acpi.h ├── bin │ ├── main.efi │ └── main.so ├── bootloader.h ├── file.c ├── file.h ├── font.c ├── font.h ├── gop.c ├── gop.h ├── kernel_loader.c ├── kernel_loader.h ├── kernel_starter.c ├── kernel_starter.h ├── main.c ├── startup.nsh ├── utils.c └── utils.h ├── build-img.sh ├── init.sh ├── kernel ├── .gitignore ├── Makefile ├── fonts │ └── zap-light16.psf ├── kernel.ld └── src │ ├── acpi │ ├── acpi.cpp │ ├── acpi.hpp │ ├── apic │ │ ├── ioapic.cpp │ │ ├── ioapic.hpp │ │ ├── lapic.cpp │ │ ├── lapic.hpp │ │ ├── madt.cpp │ │ └── madt.hpp │ └── hpet │ │ ├── comparator.cpp │ │ ├── comparator.hpp │ │ ├── hpet.cpp │ │ └── hpet.hpp │ ├── console │ ├── console.cpp │ └── console.hpp │ ├── cpu │ ├── cpu.asm │ ├── cpu.cpp │ ├── cpu.hpp │ └── mmio.h │ ├── debug │ ├── print.cpp │ └── print.hpp │ ├── drivers │ ├── keyboard.cpp │ ├── keyboard.hpp │ ├── ps2.cpp │ └── ps2.hpp │ ├── framebuffer │ ├── colors.hpp │ ├── framebuffer.cpp │ └── framebuffer.hpp │ ├── gdt │ ├── gdt.asm │ ├── gdt.cpp │ └── gdt.hpp │ ├── idt │ ├── idt.cpp │ ├── idt.hpp │ ├── interrupts.cpp │ └── interrupts.hpp │ ├── kernel.cpp │ ├── kernel.hpp │ ├── klog │ ├── klog.cpp │ └── klog.hpp │ ├── kutils │ ├── assertions.cpp │ ├── assertions.hpp │ ├── bitmap.cpp │ ├── bitmap.hpp │ ├── bits.hpp │ ├── console.cpp │ └── console.hpp │ ├── libc │ ├── cmath.cpp │ ├── cmath.hpp │ ├── stdarg.hpp │ ├── stdint.hpp │ ├── stdlib.cpp │ └── stdlib.hpp │ ├── memory │ ├── heap.cpp │ ├── heap.hpp │ ├── mmap.cpp │ ├── mmap.hpp │ ├── paging.asm │ ├── paging.cpp │ ├── paging.hpp │ ├── pmem.cpp │ └── pmem.hpp │ └── pic │ ├── pic.asm │ ├── pic.cpp │ └── pic.hpp ├── ovmf ├── OVMF_CODE.fd └── OVMF_VARS.fd ├── recompile.sh └── run.sh /.clang-format: -------------------------------------------------------------------------------- 1 | # Basics 2 | Language: Cpp 3 | Standard: c++11 4 | UseTab: Always 5 | IndentWidth: 4 6 | TabWidth: 4 7 | IndentCaseLabels: true 8 | ColumnLimit: 100 9 | ContinuationIndentWidth: 4 10 | 11 | # Arguments and parameters aligns 12 | BinPackArguments: false 13 | BinPackParameters: false 14 | AlignAfterOpenBracket: true 15 | AllowAllArgumentsOnNextLine: true 16 | AlignOperands: true 17 | 18 | # Other 19 | SpaceAfterCStyleCast: true 20 | AlignTrailingComments: true 21 | AlignEscapedNewlines: Left 22 | AlignConsecutiveMacros: true 23 | AlignConsecutiveAssignments: true 24 | AllowShortFunctionsOnASingleLine: false 25 | AlignConsecutiveBitFields: true 26 | AllowShortEnumsOnASingleLine: false 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | 3 | os-image.hdd -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # printOS 2 | 3 | ## How to setup 4 | 5 | ### Requirements 6 | 7 | - NASM 8 | - gcc && g++ 9 | - Qemu (`sudo apt install qemu qemu-utils qemu-system-x86 qemu-system-gui`) 10 | 11 | ### Start development 12 | 13 | 1. Download a GNU-EFI source code. Place it in the `bootloader/gnuefi` directory. 14 | 2. `./init.sh` 15 | 16 | ### Run OS 17 | 18 | Tool chain: `./recompile.sh && ./build-img.sh && ./run.sh` 19 | 20 | ## Kernel 21 | 64-bit simple hobby kernel written in C++ for educational purpose and fun. 22 | ![Kernel info](_img/kernel-1.png) 23 | 24 | ### Features 25 | 26 | - [x] CPUID - state and features checking 27 | - [x] Framebuffer (double-buffering) driver, writing output, colors 28 | - [x] Basic read-only console (+ auto-scroll) 29 | - [x] GDT - flat model 30 | - [x] Physical memory allocator 31 | - [x] Virtual memory - 64-bit paging 32 | - [x] IDT - basic interrupts 33 | - [x] Very basic libc (it will be written further if needed) 34 | - [x] Kernel panic && asserts 35 | - [x] ACPI support 36 | - [X] Local APIC and I/O APIC support 37 | - [X] High Precision Event Timer (HPET): one-shot and periodic mode 38 | - [X] Heap (kmalloc & free) 39 | - [ ] PS/2 driver (keyboard support) 40 | - [ ] All x86 exceptions 41 | - [ ] Uniprocessor multitasking 42 | - [ ] User mode 43 | - [ ] Virtual relocation over 0xFFFFFFFF 44 | 45 | ## Bootloader (UEFI) 46 | My GNU-EFI (UEFI) bootloader written in C for fun and education. 47 | ![Bootloader info](_img/bootloader-1.png) 48 | 49 | ### Features 50 | 51 | - [x] Reading kernel file from drive 52 | - [x] ELF kernel parsing and loading into memory 53 | - [x] Framebuffer initialization 54 | - [x] Memory map gathering 55 | - [x] Psf font loading 56 | - [x] ACPI support 57 | - [x] Error checking 58 | - [x] Jumping into kernel 59 | 60 | ### Documentation & specifications abbreviations used in comments 61 | 62 | - IM3 - Intel Manual, vol. 3 63 | - EFI - UEFI Specification 64 | - ACP - ACPI Documentation 65 | - APC - APIC Specification 66 | - IOA - IO APIC Documentation 67 | - HPT - HPET Documentation 68 | -------------------------------------------------------------------------------- /_img/bootloader-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Print3M/printOS/eee93510620eafb1efcb1367c26eee92d01568ac/_img/bootloader-1.png -------------------------------------------------------------------------------- /_img/kernel-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Print3M/printOS/eee93510620eafb1efcb1367c26eee92d01568ac/_img/kernel-1.png -------------------------------------------------------------------------------- /bootloader/.gitignore: -------------------------------------------------------------------------------- 1 | objs/ 2 | result/ 3 | gnuefi/ -------------------------------------------------------------------------------- /bootloader/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | LD = ld 3 | ARCH = x86_64 4 | OUTPUT_EFI = main.efi 5 | 6 | SRC_DIR = . 7 | OBJ_DIR = objs 8 | TARGET_DIR = bin 9 | SRCS := $(wildcard $(SRC_DIR)/*.c) 10 | OBJS := $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRCS)) 11 | 12 | GNUEFI_DIR = gnuefi 13 | EFIINC = $(GNUEFI_DIR)/inc 14 | EFIINCS = -I $(EFIINC) -I $(EFIINC)/$(ARCH) -I $(EFIINC)/protocol 15 | LIB = $(GNUEFI_DIR)/$(ARCH)/lib 16 | EFI_LIB = $(GNUEFI_DIR)/$(ARCH)/gnuefi 17 | EFI_CRT_OBJS = $(EFI_LIB)/crt0-efi-$(ARCH).o 18 | EFI_LDS = $(GNUEFI_DIR)/gnuefi/elf_$(ARCH)_efi.lds 19 | 20 | CFLAGS = $(EFIINCS) -c -fno-stack-protector -fpic -fshort-wchar -mno-red-zone -Wall -Wextra -std=c11 -DGNU_EFI_USE_MS_ABI -DEFI_FUNCTION_WRAPPER 21 | ADD = -ffreestanding 22 | LDLIBS = -l:libgnuefi.a -l:libefi.a 23 | LDFLAGS = -T $(EFI_LDS) -L $(EFI_LIB) -L $(LIB) -nostdlib --warn-common --no-undefined --fatal-warnings -znocombreloc -shared -Bsymbolic 24 | 25 | $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c 26 | @echo !==== COMPILING $^ ====! 27 | $(CC) $(CFLAGS) $(ADD) -c $^ -o $@ 28 | 29 | $(TARGET_DIR)/main.so: $(OBJS) 30 | @echo !==== LINKING $^ ====! 31 | $(LD) $(OBJS) $(EFI_CRT_OBJS) $(LDFLAGS) $(LDLIBS) -o $@ 32 | @echo !==== MAKING EFI FORMAT ====! 33 | objcopy -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel -j .rela -j .reloc --verbose --target=efi-app-$(ARCH) $@ $(TARGET_DIR)/$(OUTPUT_EFI) 34 | 35 | init: 36 | mkdir -p $(OBJ_DIR) 37 | mkdir -p $(TARGET_DIR) 38 | 39 | clean: 40 | rm -rf $(OBJ_DIR)/* 41 | rm -rf $(TARGET_DIR)/* -------------------------------------------------------------------------------- /bootloader/acpi.c: -------------------------------------------------------------------------------- 1 | #include "acpi.h" 2 | #include 3 | #include 4 | 5 | void *get_rsdp(void) { 6 | // RSDP - Root System Description Pointer 7 | // Read: ACPI Specification, chapter 5.2.5. 8 | 9 | EFI_CONFIGURATION_TABLE *ct_entry = ST->ConfigurationTable; 10 | EFI_GUID acpi_table_guid = ACPI_TABLE_GUID; 11 | 12 | for (UINT64 i = 0; i < ST->NumberOfTableEntries; i++) { 13 | if (CompareGuid(&ct_entry->VendorGuid, &acpi_table_guid) == 0) { 14 | return ct_entry->VendorTable; 15 | } 16 | 17 | ct_entry += 1; 18 | } 19 | 20 | return NULL; 21 | } -------------------------------------------------------------------------------- /bootloader/acpi.h: -------------------------------------------------------------------------------- 1 | /* 2 | See ACPI Specification, chapter 5.2.5. Root System Description Pointer 3 | 4 | RSDP is pointer to the RSDP structure. 5 | RSDP structure has field xsdt_addr which is pointer to the table of 6 | many different System Description Tables (SDT). 7 | XSDT starts with header STD_HEADER. 8 | 9 | */ 10 | 11 | #pragma once 12 | #include 13 | #include 14 | 15 | #define EFI_ACPI_TABLE_GUID \ 16 | { \ 17 | 0xeb9d2d30, 0x2d88, 0x11d3, { 0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \ 18 | } 19 | 20 | void *get_rsdp(void); -------------------------------------------------------------------------------- /bootloader/bin/main.efi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Print3M/printOS/eee93510620eafb1efcb1367c26eee92d01568ac/bootloader/bin/main.efi -------------------------------------------------------------------------------- /bootloader/bin/main.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Print3M/printOS/eee93510620eafb1efcb1367c26eee92d01568ac/bootloader/bin/main.so -------------------------------------------------------------------------------- /bootloader/bootloader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "font.h" 3 | #include "gop.h" 4 | #include "kernel_starter.h" 5 | 6 | typedef struct { 7 | Framebuffer *framebuffer; 8 | Psf1_font *font; 9 | MemoryData *memory; 10 | void *acpi_rsdp; 11 | } BootloaderData; -------------------------------------------------------------------------------- /bootloader/file.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | EFI_STATUS get_image(EFI_HANDLE image_handle, EFI_LOADED_IMAGE_PROTOCOL **loaded_image) { 8 | EFI_STATUS status = BS->OpenProtocol(image_handle, 9 | &gEfiLoadedImageProtocolGuid, 10 | (void **) loaded_image, 11 | image_handle, 12 | NULL, 13 | EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL); 14 | if (status == EFI_UNSUPPORTED) { 15 | print_efi_err(L"The device doesn't support specified image protocol", status); 16 | } else if (status != EFI_SUCCESS) { 17 | print_efi_err(L"Opening EFI Loaded Image Protocol failed", status); 18 | } 19 | return status; 20 | } 21 | 22 | EFI_STATUS get_root_fs(EFI_HANDLE image_handle, 23 | EFI_LOADED_IMAGE_PROTOCOL *loaded_image, 24 | EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **root_fs) { 25 | EFI_STATUS status = BS->OpenProtocol(loaded_image->DeviceHandle, 26 | &gEfiSimpleFileSystemProtocolGuid, 27 | (void **) root_fs, 28 | image_handle, 29 | NULL, 30 | EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL); 31 | if (status == EFI_UNSUPPORTED) { 32 | print_efi_err(L"The device does not support specified file system protocol", status); 33 | } else if (status != EFI_SUCCESS) { 34 | print_efi_err(L"Opening EFI Simple File System failed", status); 35 | } 36 | return status; 37 | } 38 | 39 | EFI_STATUS get_root_dir(EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *root_fs, EFI_FILE **dir) { 40 | EFI_STATUS status = root_fs->OpenVolume(root_fs, dir); 41 | if (status != EFI_SUCCESS) { 42 | print_efi_err(L"Accessing root directory failed", status); 43 | } 44 | return status; 45 | } 46 | 47 | EFI_STATUS get_file(EFI_FILE *dir, CHAR16 *file_name, EFI_FILE **file) { 48 | EFI_STATUS status = dir->Open(dir, file, file_name, EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY); 49 | if (status != EFI_SUCCESS) { 50 | print_efi_err(L"Fail during opening file", status); 51 | } 52 | return status; 53 | } 54 | 55 | EFI_STATUS get_file_info(EFI_FILE *file, EFI_FILE_INFO **file_info) { 56 | EFI_STATUS status = EFI_SUCCESS; 57 | UINTN file_size = 0; 58 | 59 | // Get size required for allocation memory for file info 60 | status = file->GetInfo(file, &gEfiFileInfoGuid, &file_size, NULL); 61 | if (status != EFI_BUFFER_TOO_SMALL) { 62 | print_efi_err(L"First GetInfo() should return BUFFER_TO_SMALL", status); 63 | return EFI_ABORTED; 64 | } 65 | 66 | // Allocate memory for file info 67 | status = BS->AllocatePool(EfiLoaderData, file_size, (void **) file_info); 68 | if (status != EFI_SUCCESS) { 69 | print_efi_err(L"Memory allocation failed", status); 70 | return status; 71 | } 72 | 73 | // Write file info into allocated memory 74 | status = file->GetInfo(file, &gEfiFileInfoGuid, &file_size, (void *) *file_info); 75 | if (status != EFI_SUCCESS) { 76 | print_efi_err(L"Get file info failed", status); 77 | } 78 | return status; 79 | } 80 | 81 | EFI_FILE *load_file(EFI_FILE *dir, CHAR16 *file_name, EFI_HANDLE image_handle) { 82 | 83 | // Get loaded image (by image_handler) 84 | EFI_LOADED_IMAGE_PROTOCOL *loaded_image = NULL; 85 | if (get_image(image_handle, &loaded_image) != EFI_SUCCESS) { 86 | return NULL; 87 | } 88 | 89 | // Get file system 90 | EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *root_fs = NULL; 91 | if (get_root_fs(image_handle, loaded_image, &root_fs) != EFI_SUCCESS) { 92 | return NULL; 93 | } 94 | 95 | // If directory not specified, get root directory of volume 96 | if (dir == NULL) { 97 | if (get_root_dir(root_fs, &dir) != EFI_SUCCESS) { 98 | return NULL; 99 | } 100 | } 101 | 102 | // Open file 103 | EFI_FILE *file = NULL; 104 | if (get_file(dir, file_name, &file) != EFI_SUCCESS) { 105 | return NULL; 106 | } 107 | 108 | return file; 109 | } -------------------------------------------------------------------------------- /bootloader/file.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | EFI_STATUS get_file_info(EFI_FILE *file, EFI_FILE_INFO **file_info); 7 | 8 | EFI_FILE *load_file(EFI_FILE *directory, CHAR16 *file_name, EFI_HANDLE image_handle); 9 | -------------------------------------------------------------------------------- /bootloader/font.c: -------------------------------------------------------------------------------- 1 | #include "font.h" 2 | #include "file.h" 3 | #include "utils.h" 4 | #include 5 | #include 6 | #include 7 | 8 | EFI_STATUS get_psf_header(EFI_FILE *psf_file, Psf1_header **psf_header) { 9 | EFI_STATUS status = EFI_SUCCESS; 10 | UINTN psf_header_sz = sizeof(Psf1_header); 11 | 12 | status = ST->BootServices->AllocatePool(EfiLoaderData, psf_header_sz, (void **) psf_header); 13 | if (status != EFI_SUCCESS) { 14 | print_efi_err(L"Allocating memory for PSF header failed", status); 15 | return status; 16 | } 17 | status = psf_file->Read(psf_file, &psf_header_sz, (void *) *psf_header); 18 | if (status != EFI_SUCCESS) { 19 | print_efi_err(L"Reading PSF header failed", status); 20 | } 21 | return status; 22 | } 23 | 24 | BOOLEAN verify_psf_header(Psf1_header *psf_header) { return psf_header->magic == PSF1_MAGIC; } 25 | 26 | EFI_STATUS get_psf_glyphs(EFI_FILE *psf_file, UINTN glyphs_buf_sz, void **glyphs_buf) { 27 | EFI_STATUS status = EFI_SUCCESS; 28 | 29 | status = psf_file->SetPosition(psf_file, sizeof(Psf1_header)); 30 | if (status != EFI_SUCCESS) { 31 | print_efi_err(L"Jumping at PSF glyphs buffer failed", status); 32 | return status; 33 | } 34 | status = ST->BootServices->AllocatePool(EfiLoaderData, glyphs_buf_sz, (void **) glyphs_buf); 35 | if (status != EFI_SUCCESS) { 36 | print_efi_err(L"Allocating memory for PSF glyphs failed", status); 37 | return status; 38 | } 39 | status = psf_file->Read(psf_file, &glyphs_buf_sz, (void *) *glyphs_buf); 40 | if (status != EFI_SUCCESS) { 41 | print_efi_err(L"Reading PSF glyphs failed", status); 42 | } 43 | return status; 44 | } 45 | 46 | Psf1_font * 47 | load_psf_font(EFI_FILE *dir, CHAR16 *path, EFI_HANDLE image_handle) { 48 | /* 49 | Read more about PSF fonts: 50 | https://www.win.tue.nl/~aeb/linux/kbd/font-formats-1.html 51 | */ 52 | 53 | // Load .psf font file 54 | EFI_FILE *psf_file = load_file(dir, path, image_handle); 55 | if (psf_file == NULL) { 56 | print_err(L"Loading PSF file failed"); 57 | return NULL; 58 | } 59 | 60 | // Get .psf file's header 61 | Psf1_header *psf_header = NULL; 62 | if (get_psf_header(psf_file, &psf_header) != EFI_SUCCESS) { 63 | return NULL; 64 | } 65 | 66 | // Verify .psf format header 67 | if (verify_psf_header(psf_header)) { 68 | print_err(L"Invalid PSF file header"); 69 | return NULL; 70 | } 71 | 72 | // Buffer size for all glyphs (check if 512-mode is enabled) 73 | UINTN glyphs_buf_sz; 74 | if (psf_header->mode == PSF1_MODE512) { 75 | glyphs_buf_sz = psf_header->charsize * 512; 76 | } else { 77 | glyphs_buf_sz = psf_header->charsize * 256; 78 | } 79 | 80 | // Allocate memory and load entire glyphs buffer of psf file 81 | void *glyphs_buf = NULL; 82 | if (get_psf_glyphs(psf_file, glyphs_buf_sz, &glyphs_buf) != EFI_SUCCESS) { 83 | return NULL; 84 | } 85 | 86 | // Allocate memory for psf_font struct 87 | Psf1_font *psf_font = NULL; 88 | EFI_STATUS status = 89 | ST->BootServices->AllocatePool(EfiLoaderData, sizeof(Psf1_font), (void **) &psf_font); 90 | if (status != EFI_SUCCESS) { 91 | print_efi_err(L"Allocating memory for PSF font struct failed", status); 92 | return NULL; 93 | } 94 | 95 | psf_font->psf_header = psf_header; 96 | psf_font->glyph_buffer = glyphs_buf; 97 | psf_font->glyph_height = GLYPH_HEIGHT; 98 | psf_font->glyph_width = GLYPH_WIDTH; 99 | 100 | return psf_font; 101 | } -------------------------------------------------------------------------------- /bootloader/font.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "file.h" 3 | #include 4 | 5 | #define GLYPH_WIDTH 8 6 | #define GLYPH_HEIGHT 16 7 | 8 | #define PSF1_MAGIC 0x3604 9 | #define PSF1_MODE512 0x01 10 | 11 | typedef struct { 12 | UINT16 magic; 13 | UINT8 mode; 14 | UINT8 charsize; 15 | } Psf1_header; 16 | 17 | typedef struct { 18 | Psf1_header *psf_header; 19 | UINT8 glyph_height; 20 | UINT8 glyph_width; 21 | void *glyph_buffer; 22 | } Psf1_font; 23 | 24 | Psf1_font *load_psf_font(EFI_FILE *directory, CHAR16 *path, EFI_HANDLE image_handle); -------------------------------------------------------------------------------- /bootloader/gop.c: -------------------------------------------------------------------------------- 1 | #include "gop.h" 2 | #include "utils.h" 3 | #include 4 | #include 5 | 6 | EFI_STATUS locate_gop(EFI_GRAPHICS_OUTPUT_PROTOCOL **gop) { 7 | EFI_STATUS status = EFI_SUCCESS; 8 | 9 | status = BS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid, NULL, (void **) gop); 10 | if (status != EFI_SUCCESS) { 11 | print_efi_err(L"Locate GOP failed", status); 12 | } 13 | 14 | return status; 15 | } 16 | 17 | Framebuffer *get_framebuffer() { 18 | EFI_GRAPHICS_OUTPUT_PROTOCOL *gop = NULL; 19 | if (locate_gop(&gop) != EFI_SUCCESS) { 20 | return NULL; 21 | } 22 | 23 | // Allocate framebuffer struct 24 | Framebuffer *framebuffer = NULL; 25 | EFI_STATUS status = 26 | BS->AllocatePool(EfiLoaderData, sizeof(Framebuffer), (void **) &framebuffer); 27 | if (status != EFI_SUCCESS) { 28 | print_efi_err(L"Allocating memory for framebuffer struct failed", status); 29 | return NULL; 30 | } 31 | 32 | framebuffer->base_address = (void *) gop->Mode->FrameBufferBase; 33 | framebuffer->buffer_size = gop->Mode->FrameBufferSize; 34 | framebuffer->width = gop->Mode->Info->HorizontalResolution; 35 | framebuffer->height = gop->Mode->Info->VerticalResolution; 36 | framebuffer->pixels_per_scanline = gop->Mode->Info->PixelsPerScanLine; 37 | framebuffer->bytes_per_pixel = BYTES_PER_PIXEL; 38 | 39 | return framebuffer; 40 | } 41 | 42 | void *allocate_double_framebuffer(UINT64 size) { 43 | // Allocate memory for double-buffering of framebuffer 44 | void *buffer = NULL; 45 | EFI_STATUS status = BS->AllocatePool(EfiLoaderData, size, (void **) &buffer); 46 | if (status != EFI_SUCCESS) { 47 | print_efi_err(L"Allocating memory for double framebuffer failed", status); 48 | return NULL; 49 | } 50 | 51 | return buffer; 52 | } -------------------------------------------------------------------------------- /bootloader/gop.h: -------------------------------------------------------------------------------- 1 | /* 2 | GOP stands for Graphic Output Protocol (UEFI feature) 3 | */ 4 | #pragma once 5 | #include 6 | 7 | #define BYTES_PER_PIXEL 4 8 | 9 | typedef struct { 10 | void *base_address; 11 | void *double_buffer; 12 | UINT64 buffer_size; 13 | UINT16 width; 14 | UINT16 height; 15 | UINT16 pixels_per_scanline; 16 | UINT8 bytes_per_pixel; 17 | } Framebuffer; 18 | 19 | Framebuffer *get_framebuffer(); 20 | void *allocate_double_framebuffer(UINT64 size); -------------------------------------------------------------------------------- /bootloader/kernel_loader.c: -------------------------------------------------------------------------------- 1 | #include "kernel_loader.h" 2 | #include "file.h" 3 | #include "utils.h" 4 | #include 5 | #include 6 | #include 7 | 8 | BOOLEAN verify_kernel_elf_header(Elf64_Ehdr *e_hdr) { 9 | return memcmp((void *) (UINTN) e_hdr->e_ident[EI_MAG0], ELFMAG, SELFMAG) == 0 && 10 | e_hdr->e_ident[EI_CLASS] == ELFCLASS64 && e_hdr->e_ident[EI_DATA] == ELFDATA2LSB && 11 | e_hdr->e_type == ET_EXEC && e_hdr->e_machine == EM_X86_64 && 12 | e_hdr->e_version == EV_CURRENT; 13 | } 14 | 15 | EFI_STATUS get_elf_header(EFI_FILE *file, Elf64_Ehdr *e_hdr) { 16 | EFI_STATUS status = EFI_SUCCESS; 17 | UINTN e_hdr_size = sizeof(Elf64_Ehdr); 18 | 19 | status = file->SetPosition(file, 0); 20 | if (status != EFI_SUCCESS) { 21 | print_efi_err(L"Jumping at the beginning of ELF failed", status); 22 | return status; 23 | } 24 | 25 | status = file->Read(file, &e_hdr_size, (void *) e_hdr); 26 | if (status != EFI_SUCCESS) { 27 | print_efi_err(L"Reading ELF header failed", status); 28 | } 29 | 30 | return status; 31 | } 32 | 33 | EFI_STATUS get_elf_p_hdrs(EFI_FILE *file, Elf64_Ehdr *e_hdr, Elf64_Phdr **p_hdrs) { 34 | EFI_STATUS status = EFI_SUCCESS; 35 | UINTN all_p_hdrs_sz = e_hdr->e_phentsize * e_hdr->e_phnum; // Size of all program headers 36 | 37 | status = BS->AllocatePool(EfiLoaderData, all_p_hdrs_sz, (void **) p_hdrs); 38 | if (status != EFI_SUCCESS) { 39 | print_efi_err(L"Allocating memory for ELF p_hdrs failed", status); 40 | return status; 41 | } 42 | status = file->SetPosition(file, e_hdr->e_phoff); 43 | if (status != EFI_SUCCESS) { 44 | print_efi_err(L"Jumping at e_phoff in ELF file failed", status); 45 | return status; 46 | } 47 | status = file->Read(file, &all_p_hdrs_sz, (void *) *p_hdrs); 48 | if (status != EFI_SUCCESS) { 49 | print_efi_err(L"Reading ELF program headers failed", status); 50 | return status; 51 | } 52 | return status; 53 | } 54 | 55 | Elf64_Phdr *_get_next_p_hdr(Elf64_Phdr *p_hdr, UINTN p_hdr_size) { 56 | return (Elf64_Phdr *) ((UINTN) p_hdr + p_hdr_size); 57 | } 58 | 59 | EFI_STATUS load_elf_segment_by_p_hdr(EFI_FILE *file, Elf64_Phdr *p_hdr) { 60 | EFI_STATUS status = EFI_SUCCESS; 61 | 62 | // Allocate pages for segment at its physical address 63 | UINT16 num_of_pages = (p_hdr->p_filesz / PAGE_SZ) + 1; 64 | Elf64_Addr segment_paddr = p_hdr->p_paddr; 65 | 66 | status = BS->AllocatePages(AllocateAddress, EfiLoaderData, num_of_pages, &segment_paddr); 67 | if (status != EFI_SUCCESS) { 68 | print_efi_err(L"Memory allocation for ELF segment failed", status); 69 | return status; 70 | } 71 | 72 | // Read segment and load into allocated memory 73 | status = file->SetPosition(file, p_hdr->p_offset); 74 | if (status != EFI_SUCCESS) { 75 | print_efi_err(L"Jumping at segment offset", status); 76 | return status; 77 | } 78 | 79 | UINTN segment_file_sz = p_hdr->p_filesz; 80 | status = file->Read(file, &segment_file_sz, (void *) segment_paddr); 81 | if (status != EFI_SUCCESS) { 82 | print_efi_err(L"Reading one of ELF's PT_LOAD segments failed", status); 83 | } 84 | return status; 85 | } 86 | 87 | void *load_kernel_into_memory(EFI_FILE *kernel_file) { 88 | /* 89 | Returns entry point to the kernel. 90 | IMPORTANT: Kernel is loaded into EfiLoaderData memory type! 91 | */ 92 | 93 | // Read ELF file header 94 | Elf64_Ehdr e_hdr; 95 | if (get_elf_header(kernel_file, &e_hdr) != EFI_SUCCESS) { 96 | return NULL; 97 | } 98 | 99 | // Verify ELF header 100 | if (verify_kernel_elf_header(&e_hdr)) { 101 | print_err(L"Kernel ELF format is bad"); 102 | return NULL; 103 | } 104 | 105 | // Get ELF program headers 106 | Elf64_Phdr *p_hdrs = NULL; 107 | if (get_elf_p_hdrs(kernel_file, &e_hdr, &p_hdrs) != EFI_SUCCESS) { 108 | return NULL; 109 | } 110 | 111 | // Get first program header 112 | Elf64_Phdr *p_hdr = p_hdrs; 113 | 114 | // Iterate through ELF program headers and load PT_LOAD segments 115 | for (int i = 0; i < e_hdr.e_phnum; i++) { 116 | if (i > 0) { 117 | p_hdr = _get_next_p_hdr(p_hdr, e_hdr.e_phentsize); 118 | } 119 | 120 | if (p_hdr->p_type == PT_LOAD) { 121 | if (load_elf_segment_by_p_hdr(kernel_file, p_hdr) != EFI_SUCCESS) { 122 | return NULL; 123 | } 124 | } 125 | } 126 | 127 | return (void *) e_hdr.e_entry; 128 | } -------------------------------------------------------------------------------- /bootloader/kernel_loader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | #define PAGE_SZ 0x1000 5 | 6 | void *load_kernel_into_memory(EFI_FILE *kernel_file); 7 | -------------------------------------------------------------------------------- /bootloader/kernel_starter.c: -------------------------------------------------------------------------------- 1 | #include "kernel_starter.h" 2 | #include "bootloader.h" 3 | #include "utils.h" 4 | #include 5 | #include 6 | 7 | typedef void __attribute__((sysv_abi)) (*kernel)(BootloaderData *); 8 | 9 | EFI_STATUS 10 | get_memory_map(UINTN *mmap_sz, 11 | EFI_MEMORY_DESCRIPTOR **mmap, 12 | UINTN *mmap_key, 13 | UINTN *descriptor_sz) { 14 | EFI_STATUS status = EFI_SUCCESS; 15 | UINT32 descriptor_version; 16 | 17 | // Get memory map size 18 | status = BS->GetMemoryMap(mmap_sz, *mmap, NULL, descriptor_sz, &descriptor_version); 19 | if (status != EFI_BUFFER_TOO_SMALL) { 20 | print_efi_err(L"First GetMemoryMap() should return BUFFER_TO_SMALL", status); 21 | return EFI_ABORTED; 22 | } 23 | // Allocate memory for memory map and get it 24 | status = BS->AllocatePool(EfiLoaderData, *mmap_sz, (void **) mmap); 25 | if (status != EFI_SUCCESS) { 26 | print_efi_err(L"Allocation for memory map failed", status); 27 | return status; 28 | } 29 | status = BS->GetMemoryMap(mmap_sz, *mmap, mmap_key, descriptor_sz, &descriptor_version); 30 | if (status != EFI_SUCCESS) { 31 | print_efi_err(L"GetMemoryMap() failed", status); 32 | } 33 | 34 | return status; 35 | } 36 | 37 | MemoryData *get_memory_data(UINTN *mmap_key) { 38 | /* 39 | Get memory map and related info. 40 | Should be last function in bootloader before BS->ExitBootServices() 41 | */ 42 | EFI_STATUS status = EFI_SUCCESS; 43 | 44 | // Allocate memory_data struct 45 | MemoryData *memory_data = NULL; 46 | status = BS->AllocatePool(EfiLoaderData, sizeof(MemoryData), (void **) &memory_data); 47 | if (status != EFI_SUCCESS) { 48 | print_efi_err(L"Allocating memory for memory data struct failed", status); 49 | } 50 | 51 | UINTN mmap_sz = 0; 52 | UINTN descriptor_sz = 0; 53 | EFI_MEMORY_DESCRIPTOR *mmap = NULL; 54 | if (get_memory_map(&mmap_sz, &mmap, mmap_key, &descriptor_sz) != EFI_SUCCESS) { 55 | return NULL; 56 | } 57 | 58 | memory_data->descriptor_sz = descriptor_sz; 59 | memory_data->map_sz = mmap_sz; 60 | memory_data->memory_map = mmap; 61 | memory_data->entries = mmap_sz / descriptor_sz; 62 | 63 | return memory_data; 64 | } 65 | 66 | void allocate_memory_for_kernel_stack(void **kernel_stack) { 67 | EFI_STATUS status = BS->AllocatePool(EfiLoaderData, KERNEL_STACK_SIZE, kernel_stack); 68 | if (status != EFI_SUCCESS) { 69 | print_efi_err(L"Memory allocation for kernel stack failed", status); 70 | return; 71 | } 72 | } 73 | 74 | void start_kernel(EFI_HANDLE image_handle, 75 | Framebuffer *framebuffer, 76 | Psf1_font *psf_font, 77 | void *kernel_addr, 78 | void *acpi_rsdp) { 79 | 80 | // TODO: Allocate memory for stack purpose (maybe custom memory) 81 | void *kernel_stack = NULL; 82 | allocate_memory_for_kernel_stack(&kernel_stack); 83 | if (kernel_stack == NULL) { 84 | print_err(L"Kernel stack allocation failed"); 85 | return; 86 | } 87 | 88 | UINTN mmap_key = 0; 89 | MemoryData *memory_data = get_memory_data(&mmap_key); 90 | if (memory_data == NULL) { 91 | print_err(L"Memory data gathering failed"); 92 | return; 93 | } 94 | 95 | // Exit boot services immediately after memory map gathering 96 | EFI_STATUS status = BS->ExitBootServices(image_handle, mmap_key); 97 | if (status != EFI_SUCCESS) { 98 | print_efi_err(L"ExitBootServices() failed", status); 99 | return; 100 | } 101 | 102 | // !!! 103 | // NOTE: Now you are not able to call EFI functions! 104 | // !!! 105 | 106 | BootloaderData bootloader_data = { 107 | .framebuffer = framebuffer, 108 | .font = psf_font, 109 | .memory = memory_data, 110 | .acpi_rsdp = acpi_rsdp, 111 | }; 112 | 113 | // Jump to kernel function 114 | ((kernel) kernel_addr)(&bootloader_data); 115 | } -------------------------------------------------------------------------------- /bootloader/kernel_starter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "font.h" 3 | #include "gop.h" 4 | #include 5 | 6 | #define KERNEL_STACK_SIZE 0x1000 7 | 8 | typedef struct { 9 | EFI_MEMORY_DESCRIPTOR *memory_map; 10 | UINTN map_sz; 11 | UINTN descriptor_sz; 12 | UINT64 entries; 13 | } MemoryData; 14 | 15 | void start_kernel(EFI_HANDLE image_handle, 16 | Framebuffer *framebuffer, 17 | Psf1_font *psf_font, 18 | void *kernel_addr, 19 | void *acpi_rsdp); -------------------------------------------------------------------------------- /bootloader/main.c: -------------------------------------------------------------------------------- 1 | #include "acpi.h" 2 | #include "bootloader.h" 3 | #include "file.h" 4 | #include "font.h" 5 | #include "gop.h" 6 | #include "kernel_loader.h" 7 | #include "kernel_starter.h" 8 | #include "utils.h" 9 | #include 10 | #include 11 | #include 12 | 13 | EFI_STATUS efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table) { 14 | InitializeLib(image_handle, system_table); 15 | 16 | Print(L"[*] Starting bootloader... \n\r"); 17 | 18 | EFI_FILE *root_dir = NULL; 19 | EFI_FILE *kernel_file = load_file(root_dir, L"kernel.elf", image_handle); 20 | if (kernel_file == NULL) { 21 | print_err(L"Loading kernel file error"); 22 | return EFI_LOAD_ERROR; 23 | } 24 | Print(L"[+] Kernel file has been loaded successfully \n\r"); 25 | 26 | void *kernel_addr = load_kernel_into_memory(kernel_file); 27 | if (kernel_addr == NULL) { 28 | print_err(L"Loading kernel into memory error"); 29 | return EFI_LOAD_ERROR; 30 | } 31 | Print(L"[+] Kernel has been successfully loaded into memory \n\r"); 32 | 33 | Psf1_font *psf_font = load_psf_font(root_dir, L"zap-light16.psf", image_handle); 34 | if (psf_font == NULL) { 35 | print_err(L"Psf font loading error"); 36 | return EFI_LOAD_ERROR; 37 | } 38 | Print(L"[+] Psf font loaded successfully \n\r"); 39 | 40 | Framebuffer *framebuffer = get_framebuffer(); 41 | if (framebuffer == NULL) { 42 | print_err(L"Framebuffer initialization error"); 43 | return EFI_LOAD_ERROR; 44 | } 45 | Print(L"[+] Framebuffer initialized successfully \n\r"); 46 | 47 | framebuffer->double_buffer = allocate_double_framebuffer(framebuffer->buffer_size); 48 | if (framebuffer->double_buffer == NULL) { 49 | print_err(L"Double framebuffer allocation error"); 50 | return EFI_LOAD_ERROR; 51 | } 52 | Print(L"[+] Double framebuffer allocated successfully \n\r"); 53 | 54 | void *acpi_rsdp = get_rsdp(); 55 | if (acpi_rsdp == NULL) { 56 | print_err(L"Obtaining RSDP of ACPI failed"); 57 | return EFI_LOAD_ERROR; 58 | } 59 | Print(L"[+] RSDP of ACPI obtained successfully \n\r"); 60 | 61 | Print(L"[*] Starting kernel... \n\r"); 62 | start_kernel(image_handle, framebuffer, psf_font, kernel_addr, acpi_rsdp); 63 | 64 | return EFI_SUCCESS; 65 | } 66 | -------------------------------------------------------------------------------- /bootloader/startup.nsh: -------------------------------------------------------------------------------- 1 | set StartupDelay 0 2 | @echo -off 3 | mode 80 25 4 | 5 | cls 6 | if exist .\efi\boot\main.efi then 7 | .\efi\boot\main.efi 8 | goto END 9 | endif 10 | 11 | if exist fs0:\efi\boot\main.efi then 12 | fs0: 13 | echo Found bootloader on fs0: 14 | efi\boot\main.efi 15 | goto END 16 | endif 17 | 18 | if exist fs1:\efi\boot\main.efi then 19 | fs1: 20 | echo Found bootloader on fs1: 21 | efi\boot\main.efi 22 | goto END 23 | endif 24 | 25 | if exist fs2:\efi\boot\main.efi then 26 | fs2: 27 | echo Found bootloader on fs2: 28 | efi\boot\main.efi 29 | goto END 30 | endif 31 | 32 | if exist fs3:\efi\boot\main.efi then 33 | fs3: 34 | echo Found bootloader on fs3: 35 | efi\boot\main.efi 36 | goto END 37 | endif 38 | 39 | if exist fs4:\efi\boot\main.efi then 40 | fs4: 41 | echo Found bootloader on fs4: 42 | efi\boot\main.efi 43 | goto END 44 | endif 45 | 46 | if exist fs5:\efi\boot\main.efi then 47 | fs5: 48 | echo Found bootloader on fs5: 49 | efi\boot\main.efi 50 | goto END 51 | endif 52 | 53 | if exist fs6:\efi\boot\main.efi then 54 | fs6: 55 | echo Found bootloader on fs6: 56 | efi\boot\main.efi 57 | goto END 58 | endif 59 | 60 | if exist fs7:\efi\boot\main.efi then 61 | fs7: 62 | echo Found bootloader on fs7: 63 | efi\boot\main.efi 64 | goto END 65 | endif 66 | 67 | echo "Unable to find bootloader". 68 | 69 | :END 70 | -------------------------------------------------------------------------------- /bootloader/utils.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include 3 | #include 4 | 5 | #define STATUSES_COUNT 33 6 | 7 | static CHAR16 *STATUSES[STATUSES_COUNT] = { 8 | L"success", L"load error", L"invalid parameter", 9 | L"unsupported", L"bad buffer size", L"buffer too small", 10 | L"not ready", L"device error", L"write protected", 11 | L"out of resources", L"volume corrupted", L"volume full", 12 | L"no media", L"media changed", L"not found", 13 | L"access denied", L"no response", L"no mapping", 14 | L"timeout", L"not started", L"already started", 15 | L"aborted", L"ICMP error", L"TFTP error", 16 | L"protocol error", L"incompatible version", L"security violation", 17 | L"CRC error", L"end of media", L"end of file", 18 | L"invalid language", L"compromised data", 19 | L"undefined status" // One extra status for special undefined case 20 | }; 21 | 22 | CHAR16 *efi_status_str(EFI_STATUS _status) { 23 | UINT8 status = (UINT8) _status; // Get only first 8 bits 24 | 25 | if (status > 32) { 26 | return STATUSES[STATUSES_COUNT - 1]; 27 | } 28 | return STATUSES[status]; 29 | } 30 | 31 | void print_efi_err(const CHAR16 *str, EFI_STATUS status) { 32 | Print(L"[!] Error: %s (EFI error: %s) \n\r", str, efi_status_str(status)); 33 | } 34 | void print_err(const CHAR16 *str) { 35 | Print(L"[!] Error: %s \n\r", str); 36 | } 37 | 38 | int memcmp(const void *a_ptr, const void *b_ptr, UINT64 n) { 39 | const UINT8 *a = a_ptr, *b = b_ptr; 40 | 41 | for (UINT64 i = 0; i < n; i++) { 42 | if (a[i] < b[i]) { 43 | return -1; 44 | } else if (a[i] > b[i]) { 45 | return 1; 46 | } 47 | } 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /bootloader/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | CHAR16 *efi_status_str(EFI_STATUS status); 5 | void print_efi_err(const CHAR16 *str, EFI_STATUS status); 6 | void print_err(const CHAR16* str); 7 | 8 | int memcmp(const void *aptr, const void *bptr, UINT64 n); -------------------------------------------------------------------------------- /build-img.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ueo pipefail 4 | 5 | SCRIPT=$(dirname "$0") 6 | BOOTLOADER="$SCRIPT/bootloader/bin/main.efi" 7 | KERNEL="$SCRIPT/kernel/bin/kernel.elf" 8 | STARTUP="$SCRIPT/bootloader/startup.nsh" 9 | KERNEL_FONT="$SCRIPT/kernel/fonts/zap-light16.psf" 10 | OUTPUT_IMG="$SCRIPT/os-image.hdd" 11 | 12 | function build_final_img() { 13 | # Create empty image - sector=512 bytes, sectors number=93750 14 | dd if=/dev/zero of="$OUTPUT_IMG" bs=512 count=93750 15 | 16 | # Create MS-DOS partition - FAT32 filesystem 17 | mformat -i "$OUTPUT_IMG" :: 18 | 19 | # Create subdirs for default EFI boot place 20 | mmd -i "$OUTPUT_IMG" ::/EFI 21 | mmd -i "$OUTPUT_IMG" ::/EFI/BOOT 22 | 23 | # Copy compiled EFI bootloader to default boot place 24 | mcopy -i "$OUTPUT_IMG" "$BOOTLOADER" ::/EFI/BOOT 25 | 26 | # Copy startup file into root EFI dir 27 | mcopy -i "$OUTPUT_IMG" "$STARTUP" :: 28 | 29 | # Copy compiled kernel executable into EFI root dir 30 | mcopy -i "$OUTPUT_IMG" "$KERNEL" :: 31 | 32 | # Copy psf font file into EFI root dir 33 | mcopy -i "$OUTPUT_IMG" "$KERNEL_FONT" :: 34 | } 35 | 36 | rm -rf "$OUTPUT_IMG" 37 | build_final_img 38 | 39 | echo -e "\n[+] Final OS image built successfully!" 40 | echo " Path: $OUTPUT_IMG" 41 | -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ueo pipefail 4 | 5 | SCRIPT=$(dirname "$0") 6 | 7 | function init_all() { 8 | make -C "$SCRIPT/kernel" init 9 | make -C "$SCRIPT/bootloader" init 10 | } 11 | 12 | init_all 13 | 14 | echo -e "\n[+] Kernel & bootloader initialized successfully!" -------------------------------------------------------------------------------- /kernel/.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | lib 3 | .vscode -------------------------------------------------------------------------------- /kernel/Makefile: -------------------------------------------------------------------------------- 1 | 2 | LDS = kernel.ld 3 | ASM = nasm 4 | CC = g++ 5 | 6 | C_FLAGS = -ffreestanding -fshort-wchar -std=c++2a -nostdlib -Wno-builtin-macro-redefined -nostdinc -pedantic -Wall -Wextra -mno-red-zone -I ./src 7 | 8 | LD_FLAGS = -T $(LDS) -static -Bsymbolic -nostdlib 9 | ASM_FLAGS = -f elf64 10 | 11 | SRC_DIR = src 12 | OBJ_DIR = lib 13 | BUILD_DIR = bin 14 | FONT_DIR = fonts 15 | 16 | rwildcard = $(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d)) 17 | 18 | INTERRUPTS_OBJECT_PATH = $(OBJ_DIR)/idt/interrupts.o 19 | INTERRUPTS_SOURCE_PATH = $(SRC_DIR)/idt/interrupts.cpp 20 | 21 | # C_SRC = $(call rwildcard,$(SRCDIR),*.c) 22 | CPP_SRC = $(call rwildcard,$(SRC_DIR),*.cpp) 23 | ASM_SRC = $(call rwildcard,$(SRC_DIR),*.asm) 24 | # OBJS = $(patsubst $(SRCDIR)/%.c, $(OBJDIR)/%.o, $(C_SRC)) 25 | OBJS = $(patsubst $(SRC_DIR)/%.cpp, $(OBJ_DIR)/%.o, $(CPP_SRC)) 26 | OBJS += $(patsubst $(SRC_DIR)/%.asm, $(OBJ_DIR)/%_asm.o, $(ASM_SRC)) 27 | 28 | kernel: $(OBJS) link 29 | 30 | $(INTERRUPTS_OBJECT_PATH): $(INTERRUPTS_SOURCE_PATH) 31 | # Interrupts have to use only general registers 32 | @echo !=== INTERRUPTS COMPILING ===! 33 | @mkdir -p $(@D) 34 | $(CC) $(C_FLAGS) -mgeneral-regs-only -c $^ -o $@ 35 | 36 | $(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp 37 | @echo !==== COMPILING C++ ====! 38 | @mkdir -p $(@D) 39 | $(CC) $(C_FLAGS) -c $^ -o $@ 40 | 41 | $(OBJ_DIR)/%_asm.o: $(SRC_DIR)/%.asm 42 | @echo !====== NASM =======! 43 | $(ASM) $(ASM_FLAGS) $^ -o $@ 44 | 45 | link: 46 | @echo !==== LINKING ====! 47 | $(LD) $(LD_FLAGS) -o $(BUILD_DIR)/kernel.elf $(OBJS) 48 | 49 | init: 50 | mkdir -p $(BUILD_DIR) 51 | mkdir -p $(OBJ_DIR) 52 | mkdir -p $(FONT_DIR) 53 | 54 | clean: 55 | rm -rf $(OBJ_DIR)/* -------------------------------------------------------------------------------- /kernel/fonts/zap-light16.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Print3M/printOS/eee93510620eafb1efcb1367c26eee92d01568ac/kernel/fonts/zap-light16.psf -------------------------------------------------------------------------------- /kernel/kernel.ld: -------------------------------------------------------------------------------- 1 | /* 2 | Script has been copied from: 3 | https://github.com/lucic71/lucicOS/blob/master/kernel/arch/i386/linker.ld 4 | */ 5 | OUTPUT_FORMAT(elf64-x86-64) 6 | ENTRY(_start) 7 | 8 | SECTIONS { 9 | 10 | /* 11 | * The code should be loaded at an address greater or equal to 1MB because 12 | * lower addresses are reserved to GRUB, BIOS and memory mapped I/O. 13 | * 14 | */ 15 | 16 | . = 0x100000; /* Where the kernel is loaded into physical memory /* 17 | 18 | /* Align all sections from all files to a 4KB boundary. */ 19 | 20 | _kernel_start = .; 21 | _kernel_text_start = .; 22 | 23 | .text ALIGN (0x1000) : { 24 | *(.multiboot) 25 | *(.text) 26 | } 27 | 28 | _kernel_text_end = .; 29 | _kernel_rodata_start = .; 30 | 31 | .rodata ALIGN (0x1000) : { 32 | *(.rodata*) 33 | } 34 | 35 | _kernel_rodata_end = .; 36 | _kernel_data_start = .; 37 | 38 | .data ALIGN (0x1000) : { 39 | *(.data) 40 | } 41 | 42 | _kernel_data_end = .; 43 | _kernel_bss_start = .; 44 | 45 | .bss ALIGN (0x1000) : { 46 | *(COMMON) 47 | *(.bss) 48 | } 49 | 50 | _kernel_bss_end = .; 51 | 52 | . = ALIGN(4K); 53 | _kernel_end = .; 54 | 55 | /DISCARD/ : { 56 | *(.comment) 57 | *(.note.gnu.build-id) 58 | } 59 | 60 | _kernel_size = _kernel_end - _kernel_start; 61 | 62 | } -------------------------------------------------------------------------------- /kernel/src/acpi/acpi.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace acpi { 8 | 9 | Version get_version() { 10 | auto rsdp = kernel.acpi_rsdp; 11 | 12 | if (rsdp->revision == (u8) Version::VER_1) { 13 | return Version::VER_1; 14 | } 15 | if (rsdp->revision == (u8) Version::VER_2) { 16 | return Version::VER_2; 17 | } 18 | return Version::UNKNOWN; 19 | } 20 | 21 | bool verify_rsdp() { 22 | // Check RSDP signature 23 | auto rsdp = kernel.acpi_rsdp; 24 | 25 | if (strncmp(rsdp->signature, RSDP_SIG, 8) == 0) { 26 | return true; 27 | } 28 | return false; 29 | } 30 | 31 | } // namespace acpi 32 | 33 | namespace acpi::sdt { 34 | 35 | bool verify_rsdt() { 36 | // Check RSDT signature 37 | auto rsdp = kernel.acpi_rsdp; 38 | auto rsdt = (RSDT *) (u64) rsdp->rsdt_addr; 39 | 40 | if (strncmp(rsdt->header.signature, RSDT_SIG, 4) == 0) { 41 | return true; 42 | } 43 | return false; 44 | } 45 | 46 | Header *get_header(const char *signature) { 47 | /* 48 | Return pointer to the ACPI Standard Description Table Header 49 | identified by signature. If not found, return null. 50 | 51 | It iterates over sdt_table in RSDT and matches signatures one by one. 52 | See all possible signatures: ACPI Spec., Table 5.5, Table 5.6 53 | */ 54 | 55 | auto rsdp = kernel.acpi_rsdp; 56 | auto rsdt = (RSDT *) rsdp->rsdt_addr; 57 | Header *sdt = nullptr; 58 | 59 | // Calculate size of sdt_table (total amount of 32-bit addresses) 60 | size entries = (rsdt->header.length - sizeof(RSDT)) / 4; 61 | 62 | for (size i = 0; i < entries; i++) { 63 | sdt = (Header *) rsdt->sdt_table[i]; 64 | 65 | // Check signature 66 | if (strncmp((const char *) sdt->signature, signature, 4) == 0) { 67 | return sdt; 68 | } 69 | } 70 | 71 | return nullptr; 72 | } 73 | 74 | } // namespace acpi::sdt -------------------------------------------------------------------------------- /kernel/src/acpi/acpi.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | See ACPI Specification: 3 | For general long description what is going on: 4 | 5. ACPI Software Programming Model 5 | For structures and essentials start with: 6 | 5.2.5. Root System Description Pointer 7 | 8 | Shortly: 9 | RSDP.rsdt_addr -> RSDT.sdt_table[] = (example) [APIC, HPET, FACP, MADT] 10 | 11 | Longer: 12 | Kernel gets the address of ACPI RSDP structure from the bootloader. 13 | The ACPI RSDP structure has address of RSDT structure. 14 | RSDT structure ends with table of 32-bit SDT addresses. 15 | To use some ACPI device/abilities we need to get right SDT structure. 16 | Every SDT structure starts with standard SDT header but then it has 17 | custom fields and values for specific ACPI device. 18 | */ 19 | #pragma once 20 | #include 21 | 22 | // #define RSDP_SIGNATURE "RSD PTR " 23 | // #define RSDT_SIGNATURE "RSDT" 24 | 25 | namespace acpi { 26 | 27 | enum class Version { 28 | VER_1 = 0, 29 | VER_2 = 2, 30 | UNKNOWN = 10 31 | }; 32 | constexpr char const *RSDP_SIG = "RSD PTR "; 33 | 34 | // ACP, Table 5.3 35 | struct __attribute__((packed)) RSDP { 36 | // RSDP for ACPI 1.0 37 | char signature[8]; 38 | u8 checksum; 39 | u8 oem_id[6]; 40 | u8 revision; 41 | u32 rsdt_addr; 42 | }; 43 | 44 | // ACP, 5.2.3.2. Generic Address Structure 45 | struct __attribute__((packed)) GAS { 46 | u8 address_space_id; 47 | u8 reg_bit_width; 48 | u8 reg_bit_offset; 49 | u8 access_size; 50 | u64 address; 51 | }; 52 | 53 | Version get_version(); 54 | 55 | bool verify_rsdp(); 56 | 57 | } // namespace acpi 58 | 59 | namespace acpi::sdt { 60 | 61 | // All available signatures: ACP, Table 5.5 and 5.6 62 | constexpr char const *HPET_SIG = "HPET"; 63 | constexpr char const *MADT_SIG = "APIC"; 64 | constexpr char const *RSDT_SIG = "RSDT"; 65 | 66 | // ACP, Table 5.4 67 | struct __attribute__((packed)) Header { 68 | char signature[4]; 69 | u32 length; 70 | u8 revision; 71 | u8 checksum; 72 | char oem_id[6]; 73 | char oem_table_id[8]; 74 | u32 oem_revision; 75 | u32 creator_id; 76 | u32 creator_revision; 77 | }; 78 | 79 | // ACP, Table 5.7 80 | struct __attribute__((packed)) RSDT { 81 | Header header; 82 | u32 sdt_table[]; // Table of 32-bit addresses of SDT headers 83 | }; 84 | 85 | bool verify_rsdt(); 86 | Header *get_header(const char *signature); 87 | 88 | }; // namespace acpi::sdt -------------------------------------------------------------------------------- /kernel/src/acpi/apic/ioapic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | namespace ioapic { 7 | 8 | void set_ioregsel(u8 reg_id) { 9 | // IOA, 3.1.1. I/O Register Select Register 10 | mmio::write32((void *) (u64) kernel.acpi_tables.ioapic->io_apic_addr, (u32) reg_id); 11 | } 12 | 13 | void set_iowin(u32 val) { 14 | // IOA, 3.1.2. I/O Window Register 15 | mmio::write32((void *) ((u64) kernel.acpi_tables.ioapic->io_apic_addr + IOWIN), val); 16 | } 17 | 18 | u32 get_iowin() { 19 | // IOA, 3.1.2. I/O Window Register 20 | return mmio::read32((void *) ((u64) kernel.acpi_tables.ioapic->io_apic_addr + IOWIN)); 21 | } 22 | 23 | u32 get_id(void) { 24 | // IOA, 3.2.1. IOAPIC Identification Register 25 | set_ioregsel(IOAPICID); 26 | auto iowin = get_iowin(); 27 | iowin <<= 4; 28 | iowin >>= 28; 29 | 30 | return (u8) iowin; 31 | } 32 | 33 | u32 __get_version_reg(void) { 34 | // IOA, 3.2.2. IOAPIC Version Register 35 | set_ioregsel(IOAPICVER); 36 | 37 | return get_iowin(); 38 | } 39 | 40 | u8 get_version(void) { 41 | // Get implementation version IOAPIC 42 | u32 iowin = __get_version_reg(); 43 | 44 | return (u8) iowin; 45 | } 46 | 47 | u8 get_entries_num(void) { 48 | // Get Maximum Redirection Entry number. 49 | // This is number of interrupt input pins for the IOAPIC. 50 | // Range of values: 0-239 51 | u32 iowin = __get_version_reg(); 52 | 53 | return (u8) (iowin >> 16) + 1; 54 | } 55 | 56 | void set_redir_entry(u8 entry_id, RedirEntry &entry) { 57 | // Set redirection entry (counting from 0). 58 | // IOA, 3.2.2. I/O Redirection Table Registers 59 | u8 addr_off = IOREDTBL + (entry_id * 2); 60 | set_ioregsel(addr_off); 61 | set_iowin(entry.lower_half); 62 | 63 | set_ioregsel(addr_off + 1); 64 | set_iowin(entry.upper_half); 65 | } 66 | 67 | RedirEntry get_redir_entry(u8 entry_id) { 68 | // Get redirection entry (counting from 0). 69 | u8 addr_off = IOREDTBL + (entry_id * 2); 70 | set_ioregsel(addr_off); 71 | RedirEntry entry = {.lower_half = get_iowin()}; 72 | set_ioregsel(addr_off + 1); 73 | entry.upper_half = get_iowin(); 74 | 75 | return entry; 76 | } 77 | 78 | void init() { 79 | RedirEntry entry; 80 | entry.delmod = FIXED; 81 | entry.destmod = PHYSICAL; 82 | entry.intpol = HIGH; 83 | entry.int_mask = 0; 84 | entry.destination = 0; 85 | entry.trigmod = TriggerMode::EDGE; 86 | 87 | entry.intvec = Interrupt::TIMER; 88 | set_redir_entry(IRQ::HPET, entry); 89 | 90 | entry.intvec = Interrupt::KEYBOARD; 91 | set_redir_entry(IRQ::KEYBOARD, entry); 92 | } 93 | 94 | } // namespace ioapic -------------------------------------------------------------------------------- /kernel/src/acpi/apic/ioapic.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | I/O APIC 3 | 4 | # Example: 5 | Hardware sends IRQ0. IRQ0 is connected with INTIN2 (INTerrupt INput 2) pin 6 | in I/O APIC chip. To see all of these default mappings, see: 7 | 2.4. Interrupt Signals. 8 | */ 9 | #pragma once 10 | #include 11 | 12 | namespace ioapic { 13 | 14 | enum Polarity { 15 | HIGH = 0x0, 16 | LOW = 0x1 17 | }; 18 | enum TriggerMode { 19 | EDGE = 0x0, 20 | LEVEL = 0X1 21 | }; 22 | enum DestMode { 23 | PHYSICAL = 0x0, 24 | LOGICAL = 0x1 25 | }; 26 | enum DeliveryMode { 27 | FIXED = 0x0, 28 | LOWEST_PRIORITY = 0x1, 29 | SMI = 0x2, 30 | NMI = 0x4, 31 | INIT = 0x5, 32 | EXTINT = 0x7 33 | }; 34 | 35 | // IOA, Table 1. Memory Mapped Registers 36 | enum MMIO_Reg { 37 | IOREGSEL = 0x00, // IOA, 3.1.1. I/O Register Select Register 38 | IOWIN = 0x10 // IOA, 3.1.2. I/O Window Register 39 | }; 40 | 41 | enum Reg { 42 | // Accessible registers offsets + access 43 | // IOA, Table 2. IOAPIC Registers 44 | IOAPICID = 0x00, // R/W 45 | IOAPICVER = 0x01, // RO 46 | IOAPICARB = 0x02, // RO 47 | IOREDTBL = 0x10 // R/W 48 | }; 49 | 50 | // Number of IRQ (try to follow Standard ISA IRQs) 51 | enum IRQ { 52 | HPET = 0, 53 | KEYBOARD = 1, 54 | }; 55 | 56 | // IOA, 3.2.4. I/O Redirection Table Registers (table) 57 | union RedirEntry { 58 | struct __attribute__((packed)) { 59 | u8 intvec; // R/W - Interrupt Vector (range: 0x10 - 0xFE) 60 | u8 delmod : 3; // R/W - Delivery Mode 61 | u8 destmod : 1; // R/W - Destination Mode, physical=APIC 62 | u8 delivs : 1; // RO - Delivery Status (0=idle, 1=send and pending) 63 | u8 intpol : 1; // R/W - Interrupt Input Pin Polarity (0=high, 1=low) 64 | u8 remote_irr : 1; // RO - (for level trigerring) 65 | u8 trigmod : 1; // R/W - Trigger Mode 66 | u8 int_mask : 1; // R/W - Interrupt Mask, 1=pin is masked (inactive) 67 | u64 reserved : 39; // 68 | u8 destination; // R/W - APIC ID 69 | }; 70 | struct __attribute__((packed)) { 71 | u32 lower_half; 72 | u32 upper_half; 73 | }; 74 | }; 75 | 76 | void init(); 77 | 78 | }; // namespace ioapic -------------------------------------------------------------------------------- /kernel/src/acpi/apic/lapic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace lapic { 11 | 12 | void write_msr(u64 value) { 13 | // Write into APIC MSR 14 | // IM3, Figure 10-5. IA32_APIC_BASE MSR 15 | cpu::write_msr(BASE_MSR, value); 16 | } 17 | 18 | u64 read_msr() { 19 | // Get APIC MSR value 20 | // IM3, Figure 10-5. IA32_APIC_BASE MSR 21 | return cpu::read_msr(BASE_MSR); 22 | } 23 | 24 | u32 read_reg(u16 reg) { 25 | return mmio::read32((void *) kernel.acpi_tables.madt->lapic_addr + reg); 26 | } 27 | 28 | void write_reg(u16 reg, u32 val) { 29 | mmio::write32((void *) kernel.acpi_tables.madt->lapic_addr + reg, val); 30 | } 31 | 32 | u8 get_init_id() { 33 | // Get init APIC id from CPUID 34 | // IM3, 10.4.6 Local APIC ID 35 | u32 ebx = 0; 36 | cpu::get_cpuid(0x1, NULL, &ebx, NULL, NULL); 37 | ebx >>= 24; 38 | 39 | return (u8) ebx; 40 | } 41 | 42 | void enable() { 43 | // IM3, 10.4.3 Enabling or Disabling the Local APIC 44 | // Enable Local APIC by setting `enable` bit in MSR register 45 | u64 msr = cpu::read_msr(BASE_MSR); 46 | msr = set_bit(msr, 11); 47 | write_msr(msr); 48 | 49 | // Set APIC software `enable` bit in spurious vector register 50 | u32 reg = read_reg(Reg::SPURIOUS); 51 | reg = set_bit(reg, 8); 52 | write_reg(Reg::SPURIOUS, reg); 53 | } 54 | 55 | void set_spurious_interrupt() { 56 | // Set spurious interrupt handler and `enabled` byte 57 | // IM3, 10.9 Spurious interrupt 58 | lapic::write_reg(Reg::SPURIOUS, Interrupt::SPURIOUS); 59 | } 60 | 61 | u8 get_id() { 62 | // IM3, 10.4.6 Local APIC ID 63 | return read_reg(Reg::ID) >> 24; 64 | } 65 | 66 | u8 get_version() { 67 | // IM3, 10.4.8 Local APIC Version Register 68 | return (u8) read_reg(Reg::VERSION); 69 | } 70 | 71 | void write_eoi() { 72 | // End Of Interrupt 73 | write_reg(Reg::EOI, 0xff); 74 | } 75 | 76 | void init() { 77 | // Clear priority register 78 | lapic::write_reg(TPR, 0x0); 79 | 80 | set_spurious_interrupt(); 81 | enable(); 82 | } 83 | 84 | }; // namespace lapic -------------------------------------------------------------------------------- /kernel/src/acpi/apic/lapic.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | About APIC in general in IM3: 3 | 10. Advanced Programmable Interrupt Controller (APIC) 4 | 5 | Local APIC handles interrupts from externally connected I/O devices 6 | and local interrupt sources (processor LINT0 and LINT1 pins, APIC timer, 7 | performance-monitoring counters, thermal sensors, internal APIC error 8 | detector). LAPIC manages all external interrupts for CPU. There is one 9 | LAPIC in each CPU in the system. 10 | 11 | Useful commands Qemu monitor commands: info pic, info lapic, info irq. 12 | */ 13 | #pragma once 14 | #include 15 | 16 | namespace lapic { 17 | 18 | enum Const { 19 | BASE_MSR = 0x1b, 20 | }; 21 | enum Reg { 22 | // IM3, Table 10-1 Local APIC Register Address Map 23 | ID = 0x020, 24 | VERSION = 0x030, 25 | TPR = 0x080, 26 | EOI = 0x0b0, 27 | LDR = 0x0d0, 28 | DFR = 0x0e0, 29 | SPURIOUS = 0x0f0, 30 | IRR = 0x200, 31 | ERR = 0x280, 32 | IRC1 = 0x300, // IM3, 10.6.1 Interrupt Command Register 33 | IRC2 = 0x310 34 | }; 35 | 36 | // IM3, 10.4.4 Local APIC Status and Location 37 | struct __attribute__((packed)) MSR { 38 | u8 reserved1; 39 | u8 bsp : 1; 40 | u8 reserved2 : 2; 41 | u8 is_enabled : 1; 42 | u32 base_addr : 24; 43 | }; 44 | 45 | void init(); 46 | void write_eoi(); 47 | 48 | }; // namespace lapic -------------------------------------------------------------------------------- /kernel/src/acpi/apic/madt.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | namespace madt { 7 | 8 | void dbg_print_lapic_info(void) { 9 | // TODO: enumerate ALL APICs for all CPUs 10 | auto apic = reinterpret_cast(ics::get_header(ics::CPU_LAPIC)); 11 | 12 | kprintf("ACPI CPU UID: %d, APIC ID: %d, flags: %d\n", 13 | apic->acpi_cpu_uid, 14 | apic->apic_id, 15 | apic->flags); 16 | } 17 | 18 | }; // namespace madt 19 | 20 | namespace madt::ics { 21 | 22 | Header *get_header(Type type) { 23 | /* 24 | Return pointer to the MADT Interrupt Controller Structure or NULL if not found. 25 | */ 26 | auto header = kernel.acpi_tables.madt->ics_table; 27 | 28 | for (u8 id = 0; id < 0xf; id++) { 29 | if (header->type == type) { 30 | return header; 31 | } 32 | 33 | header = (Header *) ((u64) header + header->length); 34 | } 35 | 36 | return nullptr; 37 | } 38 | 39 | }; // namespace madt::ics -------------------------------------------------------------------------------- /kernel/src/acpi/apic/madt.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Multiple APIC Description Table (MADT). 3 | Read more in ACP, chapter 5.2.12. 4 | */ 5 | #pragma once 6 | #include 7 | #include 8 | 9 | // ================================================ // 10 | // Interrupt Controller Structures // 11 | // ACP, Table 5-45 // 12 | // ================================================ // 13 | 14 | namespace madt::ics { 15 | 16 | enum Type { 17 | CPU_LAPIC = 0x00, 18 | IO_APIC = 0x01, 19 | LAPIC_ADDR_OVERRIDE = 0x05 20 | }; 21 | 22 | // ACP, under table 5-44 23 | struct __attribute__((packed)) Header { 24 | u8 type; 25 | u8 length; 26 | }; 27 | 28 | // ACP, Table 5-47 29 | struct __attribute__((packed)) CPU_LAPIC_Flags { 30 | u8 enabled : 1; 31 | u8 online_capable : 1; 32 | u32 reserved : 30; 33 | }; 34 | 35 | // ACP, Table 5-46 36 | struct __attribute__((packed)) CPU_LAPIC_Struct { 37 | Header header; 38 | u8 acpi_cpu_uid; 39 | u8 apic_id; 40 | CPU_LAPIC_Flags flags; 41 | }; 42 | 43 | // ACP, Table 5-48 44 | struct __attribute__((packed)) IO_APIC_Struct { 45 | Header header; 46 | u8 io_apic_id; 47 | u8 reserved; 48 | u32 io_apic_addr; 49 | u32 gsib; 50 | }; 51 | 52 | Header *get_header(Type type); 53 | 54 | }; // namespace madt::ics 55 | 56 | namespace madt { 57 | 58 | // ACP, Table 5-44 59 | struct __attribute__((packed)) Flags { 60 | u8 pcat_compat : 1; 61 | u32 reserved : 31; 62 | }; 63 | 64 | // ACP, Table 5-43 65 | struct __attribute__((packed)) SDT_Table { 66 | acpi::sdt::Header header; 67 | u32 lapic_addr; 68 | Flags flags; 69 | ics::Header ics_table[]; 70 | }; 71 | 72 | void dbg_print_lapic_info(); 73 | 74 | }; // namespace madt -------------------------------------------------------------------------------- /kernel/src/acpi/hpet/comparator.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace hpet::comparator { 10 | 11 | Comparator::Comparator(u8 timer_id) : __id(timer_id){ 12 | ASSERT(g_clk_period_ns != 0); 13 | }; 14 | 15 | void *Comparator::get_compar_reg_ptr(u64 reg) { 16 | // Return pointer to the HPET n-comparator block 17 | auto timers = (volatile MMIO *) (kernel.acpi_tables.hpet->base_address.address + FIRST_OFF); 18 | u64 base_addr = (u64) (timers + this->__id); 19 | 20 | return (void *) (base_addr + reg); 21 | } 22 | 23 | void Comparator::write_reg(u64 reg, u64 val) { 24 | void *ptr = this->get_compar_reg_ptr(reg); 25 | mmio::write64(ptr, val); 26 | } 27 | 28 | u64 Comparator::read_reg(u64 reg) { 29 | void *ptr = this->get_compar_reg_ptr(reg); 30 | 31 | return mmio::read64(ptr); 32 | } 33 | 34 | void Comparator::set_config(ConfigReg ®) { 35 | // Set periodic or non-periodic type of HPET timer 36 | this->write_reg(CONFIG, *reinterpret_cast(®)); 37 | } 38 | 39 | void Comparator::set_compar_value(u64 val) { 40 | // Read: 2.3.9.1 Register Definition and Usage Model 41 | this->write_reg(COMPAR, val); 42 | } 43 | 44 | u64 Comparator::get_compar_value() { 45 | // Get comparator of HPET timer 46 | return this->read_reg(COMPAR); 47 | } 48 | 49 | u32 Comparator::get_available_ioapic_routes() { 50 | // HPT, Tn_INT_ROUTE_CAP field 51 | return this->read_reg(Reg::CONFIG) >> 32; 52 | } 53 | 54 | static inline size __calc_ms_to_compar(u64 counter, size ms_time) { 55 | // Calculate comparator value basing 56 | 57 | // Convert miliseconds to nanoseconds per tick 58 | return counter + (ms_time * 1000000) / g_clk_period_ns; 59 | } 60 | 61 | void Comparator::set_one_shot(size ms_time) { 62 | // Set timer to one-shot mode. It fires interrupt only once. 63 | // IMPORTANT: This function assumes that main counter is already enabled. 64 | // 65 | // :ms_time - amount of miliseconds till interrupt 66 | ConfigReg config_reg = { 67 | .int_type_cnf = 0, 68 | .int_enb_cnf = 1, 69 | .type_cnf = ONE_SHOT, 70 | .val_set_cnf = 0, 71 | .mode32_cnf = 0, 72 | .int_route_cnf = ioapic::IRQ::HPET, 73 | .fsb_en_cnf = 0, 74 | }; 75 | this->set_compar_value(__calc_ms_to_compar(hpet::get_counter(), ms_time)); 76 | this->set_config(config_reg); 77 | } 78 | 79 | void Comparator::set_periodic(size ms_time) { 80 | // Set timer to periodic mode. It fires interrupt every period of time. 81 | // IMPORTANT: This function resets the main counter! 82 | // 83 | // :ms_time - amount of miliseconds till interrupt 84 | hpet::disable_counter(); 85 | ConfigReg config_reg = { 86 | .int_type_cnf = 0, 87 | .int_enb_cnf = 1, 88 | .type_cnf = PERIODIC, 89 | .val_set_cnf = 1, 90 | .mode32_cnf = 0, 91 | .int_route_cnf = ioapic::IRQ::HPET, 92 | .fsb_en_cnf = 0, 93 | }; 94 | this->set_config(config_reg); 95 | this->set_compar_value(__calc_ms_to_compar(0, ms_time)); 96 | enable_counter(); 97 | } 98 | 99 | }; // namespace hpet::comparator 100 | -------------------------------------------------------------------------------- /kernel/src/acpi/hpet/comparator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /* 3 | Read: HPT, 2.4.2. Interrupt Mapping 4 | Each comparator selects where an interrupt should be send. In this OS the 5 | 'Standard Option' (HPET.LEG_RT_CNF must be set to 0) is used. This mode 6 | makes it possible to map every comparator to the IRQ selected by us. There 7 | is no legacy requirements. We're using I/O APIC to handle these IRQs and 8 | pass them on. Comparator interrupt settings should match I/O APIC settings. 9 | 10 | Read: HPT, 2.4.5 Interrupt Levels 11 | 2.4.6 Handling Interrupts 12 | 13 | Important notes for I/O APIC settings: 14 | - All interrupts are 'active high' 15 | - Edge-triggered option is used 16 | */ 17 | #include 18 | 19 | namespace hpet::comparator { 20 | 21 | constexpr u32 NANOSECONDS_PER_TICK = 100; 22 | constexpr size BLOCK_SZ = 4 * 8; // 4 * 64-bit registers 23 | constexpr size FIRST_OFF = 0x100; 24 | 25 | enum Type { 26 | ONE_SHOT = 0, 27 | PERIODIC = 1 28 | }; 29 | 30 | // Register offsets: HPT, Table 2. Memory-Mapped Registers 31 | enum Reg { 32 | CONFIG = 0x0, 33 | COMPAR = 0x8, 34 | FSB = 0xf 35 | }; 36 | 37 | // HPT, 2.3.8 Timer N Configuration and Capabilities Register 38 | struct __attribute__((packed)) ConfigReg { 39 | u8 reserved1 : 1; // 40 | u8 int_type_cnf : 1; // interrupts: 0=level triggered, 1=edge triggered 41 | u8 int_enb_cnf : 1; // enable interrupts 42 | u8 type_cnf : 1; // set periodic or one-shot type 43 | u8 per_int_cap : 1; // is periodic available? 44 | u8 size_cap : 1; // timer size, 0=32-bits, 1=64-bits 45 | u8 val_set_cnf : 1; // (periodic-mode only, for one-shot should be 0) 46 | u8 reserved2 : 1; // 47 | u8 mode32_cnf : 1; // 0=use 64-bit timer mode, 1=use 32-bit 48 | u8 int_route_cnf : 5; // I/O APIC interrupt number (IRQ) 49 | u8 fsb_en_cnf : 1; // enable FSB (we use I/O APIC, so should be 0) 50 | u8 fsb_int_del_cap : 1; // 51 | u16 reserved3; // 52 | u32 int_route_cap; // 53 | }; 54 | 55 | // HPT, 2.3.10 Timer N FSB Interrupt Route Register 56 | struct __attribute__((packed)) FsbReg { 57 | u32 tn_fsb_int_val; 58 | u32 tn_fsb_int_addr; 59 | }; 60 | 61 | struct __attribute__((packed)) MMIO { 62 | ConfigReg config_reg; 63 | u64 compar_reg; 64 | FsbReg fsb_reg; 65 | u64 reserved; 66 | }; 67 | 68 | class Comparator { 69 | private: 70 | u8 __id; 71 | 72 | public: 73 | Comparator(u8 timer_id); 74 | void set_one_shot(size ms_time); 75 | void set_periodic(size ms_time); 76 | u64 get_compar_value(); 77 | 78 | private: 79 | void *get_compar_reg_ptr(u64 reg); 80 | void write_reg(u64 reg, u64 val); 81 | u64 read_reg(u64 reg); 82 | void set_config(ConfigReg ®); 83 | void set_compar_value(u64 val); 84 | u32 get_available_ioapic_routes(); 85 | }; 86 | 87 | }; // namespace hpet::comparator -------------------------------------------------------------------------------- /kernel/src/acpi/hpet/hpet.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace hpet { 8 | 9 | size g_clk_period_ns = 0; // Number of nanoseconds per 1 tick 10 | 11 | void *__get_reg_ptr(u64 reg) { 12 | // Get pointer to the specific HPET register 13 | return (void *) ((u64) kernel.acpi_tables.hpet->base_address.address + reg); 14 | } 15 | 16 | u64 read_reg(u64 reg) { 17 | void *ptr = __get_reg_ptr(reg); 18 | return mmio::read64(ptr); 19 | } 20 | 21 | void write_reg(u64 reg, u64 val) { 22 | void *ptr = __get_reg_ptr(reg); 23 | mmio::write64(ptr, val); 24 | } 25 | 26 | u32 get_counter_clk_period() { 27 | // The returned value is in femtoseconds 28 | // Number of femtoseconds per 1 tick 29 | return read_reg(Reg::CAP_REG) >> 32; 30 | } 31 | 32 | void init() { 33 | // Convert femtoseconds to nanoseconds 34 | g_clk_period_ns = get_counter_clk_period() / 1000000; 35 | } 36 | 37 | void enable_counter() { 38 | // Set to 0 and start 39 | write_reg(COUNTER_REG, 0); 40 | u64 reg = read_reg(CONFIG_REG); 41 | auto config = (ConfigReg *) ® 42 | config->enable_cnf = 1; 43 | write_reg(CONFIG_REG, reg); 44 | } 45 | 46 | void disable_counter() { 47 | // Stop and reset to 0 48 | u64 reg = read_reg(CONFIG_REG); 49 | auto config = (ConfigReg *) ® 50 | config->enable_cnf = 0; 51 | write_reg(CONFIG_REG, reg); 52 | write_reg(COUNTER_REG, 0); 53 | } 54 | 55 | SDT_Table *get_sdt_table() { 56 | return (SDT_Table *) acpi::sdt::get_header(acpi::sdt::HPET_SIG); 57 | } 58 | 59 | bool is_counter_enabled() { 60 | // Check if counter is running 61 | u64 reg = read_reg(CONFIG_REG); 62 | auto config = (ConfigReg *) ® 63 | 64 | return config->enable_cnf; 65 | } 66 | 67 | u64 get_counter() { 68 | // Get value from main counter 69 | return read_reg(COUNTER_REG); 70 | } 71 | 72 | u8 get_no_comparators() { 73 | // Get number of available HPET comparators 74 | u64 reg = read_reg(CAP_REG); 75 | auto cap = (CapReg *) ® 76 | 77 | return cap->num_tim_cap + 1; 78 | } 79 | 80 | u64 get_mmio_sz() { 81 | /* 82 | Get size of HPET entire MMIO space in bytes 83 | Calculate it by adding up width of all HPET registers 84 | See: Table 2 Memory-Mapped Registers 85 | */ 86 | 87 | // General registers = 8 registers * 8 bytes 88 | u32 result = 8 * 8; 89 | 90 | // result + number_of_comparators * bytes_per_timer 91 | return result + get_no_comparators() * (4 * 8); 92 | } 93 | 94 | }; // namespace hpet -------------------------------------------------------------------------------- /kernel/src/acpi/hpet/hpet.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | HPET (High Precision Event Timer) 3 | 4 | Specification URL: 5 | https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/software-developers-hpet-spec-1-0a.pdf 6 | 7 | HPET MMIO registers: HPT, Table 2. Memory-Mapped Registers 8 | */ 9 | #pragma once 10 | #include 11 | #include 12 | 13 | namespace hpet { 14 | 15 | extern size g_clk_period_ns; 16 | 17 | enum Reg { 18 | // General HPET registers offset 19 | // See: Table 2 Memory-Mapped Registers 20 | CAP_REG = 0x000, 21 | CONFIG_REG = 0x010, 22 | INT_STATUS_REG = 0x020, 23 | COUNTER_REG = 0x0f0 24 | }; 25 | 26 | // See: Figure 3 General Capability and ID Register 27 | struct __attribute__((packed)) CapReg { 28 | u8 rev_id; 29 | u8 num_tim_cap : 5; 30 | u8 count_size_cap : 1; 31 | u8 reserved1 : 1; 32 | u8 leg_route_cap : 1; 33 | u16 vendor_id; 34 | u32 counter_clk_period; 35 | }; 36 | 37 | // See: Figure 4 General Configuration Register 38 | struct __attribute__((packed)) ConfigReg { 39 | u8 enable_cnf : 1; // Global enable timer flag 40 | u8 leg_rt_cnf : 1; 41 | u64 reserved1 : 62; 42 | }; 43 | 44 | // See: Table 3. HPET Description Table 45 | struct __attribute__((packed)) SDT_Table { 46 | acpi::sdt::Header header; 47 | u32 event_timer_block_id; 48 | acpi::GAS base_address; 49 | u8 hpet_number; 50 | u16 min_clock_tick_periodic_mode; 51 | u8 page_protection; 52 | }; 53 | 54 | void init(); 55 | SDT_Table *get_sdt_table(); 56 | void enable_counter(); 57 | void disable_counter(); 58 | bool is_counter_enabled(); 59 | u64 get_counter(); 60 | u8 get_no_timers(); 61 | u64 get_mmio_sz(); 62 | 63 | }; // namespace hpet -------------------------------------------------------------------------------- /kernel/src/console/console.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | ConsoleCursor::ConsoleCursor() { 11 | // Initialize state 12 | this->reset(); 13 | } 14 | 15 | void ConsoleCursor::next() { 16 | if (this->_col + 1 < this->_max_col) { 17 | this->_col += 1; 18 | } else { 19 | this->_col = 0; 20 | this->_row += 1; 21 | } 22 | } 23 | 24 | void ConsoleCursor::back() { 25 | if (this->_col > 0) { 26 | this->_col -= 1; 27 | } else { 28 | this->_col = this->_max_col - 1; 29 | this->_row -= 1; 30 | } 31 | } 32 | 33 | void ConsoleCursor::cr() { 34 | // Carrieg return 35 | this->_col = 0; 36 | } 37 | 38 | void ConsoleCursor::lf() { 39 | // Line feed 40 | this->_row += 1; 41 | } 42 | 43 | void ConsoleCursor::gotoxy(u16 x, u16 y) { 44 | this->_col = x; 45 | this->_row = y; 46 | } 47 | 48 | void ConsoleCursor::reset() { 49 | this->_col = 0; 50 | this->_row = 0; 51 | this->_bg = (u64) ConsoleDefault::bg; 52 | this->_fg = (u64) ConsoleDefault::fg; 53 | this->_max_col = (u16) ConsoleDefault::max_col; 54 | this->_max_row = (u16) ConsoleDefault::max_row; 55 | } 56 | 57 | void ConsoleCursor::set_bg(u64 color) { 58 | // Set background 59 | this->_bg = color; 60 | } 61 | 62 | void ConsoleCursor::set_fg(u64 color) { 63 | // Set foreground 64 | this->_fg = color; 65 | } 66 | 67 | Console::Console(ConsoleFont &font, Framebuffer &framebuffer) 68 | : _font(font), _framebuffer(framebuffer) { 69 | } 70 | 71 | void Console::clear() { 72 | // Clear console and reset cursor 73 | this->_framebuffer.set_all_pixels(BLACK); 74 | this->cursor.reset(); 75 | 76 | this->_framebuffer.draw(); 77 | } 78 | 79 | void Console::clear_line(u16 line) { 80 | // Indexing from 0 81 | for (size x = 0; x < this->_framebuffer.width; x++) { 82 | for (size y = 0; y < this->_font.glyph_height; y++) { 83 | this->_framebuffer.set_pixel(x, line * this->_font.glyph_height + y, BLACK); 84 | } 85 | } 86 | 87 | this->_framebuffer.draw(); 88 | } 89 | 90 | void Console::_print_char(char chr) { 91 | if (this->cursor.max_row == this->cursor.row) { 92 | this->_scroll_down(); 93 | } 94 | 95 | if (chr >= ASCII_PRINTABLE_MIN && chr <= ASCII_PRINTABLE_MAX) [[likely]] { 96 | // Get glyph bitmap ptr 97 | int offset = chr * this->_font.psf_header->charsize; 98 | char *glyph_bitmap = reinterpret_cast((size) this->_font.glyph_buffer + offset); 99 | 100 | Glyph glyph = { 101 | .bitmap = glyph_bitmap, 102 | .x_off = this->cursor.col * this->_font.glyph_width, 103 | .y_off = this->cursor.row * this->_font.glyph_height, 104 | .height = this->_font.glyph_height, 105 | .width = this->_font.glyph_width, 106 | .fg_color = this->cursor.fg, 107 | .bg_color = this->cursor.bg, 108 | }; 109 | this->_framebuffer.print_glyph(glyph); 110 | this->cursor.next(); 111 | 112 | return; 113 | } 114 | 115 | switch (chr) { 116 | case '\n': { 117 | this->cursor.lf(); 118 | this->cursor.cr(); 119 | break; 120 | } 121 | case '\t': { 122 | this->cursor.next(); 123 | this->cursor.next(); 124 | this->cursor.next(); 125 | break; 126 | } 127 | case '\r': { 128 | this->cursor.cr(); 129 | break; 130 | } 131 | case '\b': { 132 | this->cursor.back(); 133 | this->_print_char(' '); 134 | this->cursor.back(); 135 | break; 136 | } 137 | } 138 | } 139 | 140 | void Console::print(char chr) { 141 | this->_print_char(chr); 142 | this->_framebuffer.draw(); 143 | } 144 | 145 | void Console::print(char *str) { 146 | while (*str != '\0') { 147 | this->_print_char(*str); 148 | str++; 149 | } 150 | 151 | this->_framebuffer.draw(); 152 | } 153 | 154 | void Console::_scroll_down() { 155 | auto fb = this->_framebuffer.get_double_fb_addr(); 156 | auto start = this->_framebuffer.get_pixel_addr(0, this->_font.glyph_height); 157 | auto size = reinterpret_cast(start) - reinterpret_cast(fb); 158 | memcpy(fb, start, this->_framebuffer.b_size - size); 159 | 160 | this->cursor.gotoxy(this->cursor.col, this->cursor.row - 1); 161 | this->clear_line(this->cursor.max_row - 1); 162 | } 163 | -------------------------------------------------------------------------------- /kernel/src/console/console.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | struct __attribute__((packed)) PsfHeader { 7 | u8 magic[2]; 8 | u8 mode; 9 | u8 charsize; 10 | }; 11 | 12 | constexpr u8 ASCII_PRINTABLE_MIN = 32; 13 | constexpr u8 ASCII_PRINTABLE_MAX = 127; 14 | 15 | struct ConsoleFont { 16 | PsfHeader *psf_header; 17 | u8 glyph_height; 18 | u8 glyph_width; 19 | void *glyph_buffer; 20 | }; 21 | 22 | enum class ConsoleDefault { 23 | max_col = 100, 24 | max_row = 37, 25 | tab_spaces = 4, 26 | fg = WHITE, 27 | bg = BLACK, 28 | }; 29 | 30 | class ConsoleCursor { 31 | private: 32 | u16 _row = 0; 33 | u16 _col = 0; 34 | u64 _fg = (u64) ConsoleDefault::fg; 35 | u64 _bg = (u64) ConsoleDefault::bg; 36 | u16 _max_col = (u16) ConsoleDefault::max_col; 37 | u16 _max_row = (u16) ConsoleDefault::max_row; 38 | 39 | public: 40 | const u16 &row = _row; 41 | const u16 &col = _col; 42 | const u64 &fg = _fg; 43 | const u64 &bg = _bg; 44 | const u16 &max_col = _max_col; 45 | const u16 &max_row = _max_row; 46 | 47 | public: 48 | ConsoleCursor(); 49 | void next(); 50 | void back(); 51 | void gotoxy(u16 x, u16 y); 52 | void cr(); // Carriege return 53 | void lf(); // Line feed 54 | void set_fg(u64 fg); 55 | void set_bg(u64 bg); 56 | void reset(); 57 | }; 58 | 59 | class Console { 60 | private: 61 | ConsoleFont &_font; 62 | Framebuffer &_framebuffer; 63 | 64 | public: 65 | ConsoleCursor cursor = ConsoleCursor(); 66 | 67 | private: 68 | void _print_char(char chr); 69 | void _scroll_down(); 70 | 71 | public: 72 | /* 73 | All public methods of Console class should execute 74 | framebuffer.draw() method implicitly. It's not 75 | guaranteed in case of private methods! 76 | */ 77 | Console(ConsoleFont &font, Framebuffer &framebuffer); 78 | void print(char *str); 79 | void print(char chr); 80 | void clear_line(u16 line); 81 | void clear(); 82 | }; -------------------------------------------------------------------------------- /kernel/src/cpu/cpu.asm: -------------------------------------------------------------------------------- 1 | [bits 64] 2 | 3 | section .text: 4 | global get_cpuid 5 | global write_msr 6 | global read_msr 7 | 8 | get_cpuid: 9 | ; void cpuid(uint32_t cpuid_eax, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); 10 | 11 | ; Save arguments 12 | mov r10, rsi ; :eax 13 | mov r11, rdx ; :ebx 14 | mov r12, rcx ; :ecx 15 | mov r13, r8 ; :edx 16 | 17 | mov eax, edi 18 | cpuid 19 | 20 | .check_eax_param: 21 | cmp r10, 0x0 ; If :eax is not NULL ptr 22 | jne .ret_eax_param 23 | .check_ebx_param: 24 | cmp r11, 0x0 ; If :ebx is not NULL ptr 25 | jne .ret_ebx_param 26 | .check_ecx_param: 27 | cmp r12, 0x0 ; If :ecx is not NULL ptr 28 | jne .ret_ecx_param 29 | .check_edx_param: 30 | cmp r13, 0x0 ;If :edx is not NULL ptr 31 | jne .ret_edx_param 32 | 33 | .return: 34 | ret 35 | 36 | .ret_eax_param: 37 | mov dword [r10], eax ; Return argument :eax (esi) 38 | jmp .check_ebx_param 39 | .ret_ebx_param: 40 | mov dword [r11], ebx ; Return argument :ebx (edx) 41 | jmp .check_ecx_param 42 | .ret_ecx_param: 43 | mov dword [r12], ecx ; Return argument :ecx (ecx) 44 | jmp .check_edx_param 45 | .ret_edx_param: 46 | mov dword [r13], edx ; Return argument :edx (r8) 47 | jmp .return 48 | 49 | write_msr: 50 | ; void write_msr(uint64_t msr_no, uint64_t value); 51 | ; value: higher 32 bits -> edx && lower 32 bits -> eax 52 | mov ecx, edi ; ecx = msr_no 53 | mov eax, esi ; lower bits -> eax 54 | 55 | ; higher bits -> edx 56 | mov rdx, rsi 57 | shr rdx, 32 58 | 59 | wrmsr 60 | ret 61 | 62 | read_msr: 63 | ; uint64_t read_msr(uint64_t msr_no); 64 | mov ecx, edi ; ecx = msr_no 65 | rdmsr ; returns 64-bit value stored in edx:eax (both 32-bit) 66 | 67 | shl rdx, 32 ; shift edx to higher bits (0:31 -> 32:64) 68 | 69 | ; add edx to higher bits of rax 70 | or rax, rdx 71 | ret -------------------------------------------------------------------------------- /kernel/src/cpu/cpu.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace cpu { 9 | 10 | void _get_cpu_brand(char *brand) { 11 | u32 eax = 0, ebx = 0, ecx = 0, edx = 0; 12 | 13 | get_cpuid(0x80000002, &eax, &ebx, &edx, &ecx); 14 | memcpy(&brand[0], &eax, 4); 15 | memcpy(&brand[4], &ebx, 4); 16 | memcpy(&brand[8], &edx, 4); 17 | memcpy(&brand[12], &ecx, 4); 18 | 19 | get_cpuid(0x80000003, &eax, &ebx, &edx, &ecx); 20 | memcpy(&brand[16], &eax, 4); 21 | memcpy(&brand[20], &ebx, 4); 22 | memcpy(&brand[24], &edx, 4); 23 | memcpy(&brand[28], &ecx, 4); 24 | 25 | get_cpuid(0x80000004, &eax, &ebx, &edx, &ecx); 26 | memcpy(&brand[32], &eax, 4); 27 | memcpy(&brand[36], &ebx, 4); 28 | memcpy(&brand[40], &edx, 4); 29 | memcpy(&brand[44], &ecx, 4); 30 | 31 | brand[48] = '\0'; 32 | } 33 | 34 | void get_info(Info &info) { 35 | // To know more about certain registers, see: 36 | // https://en.wikipedia.org/wiki/CPUID 37 | u32 eax = 0, ebx = 0, ecx = 0, edx = 0; 38 | 39 | // Get manufacturer id 40 | get_cpuid(0x0, NULL, &ebx, &ecx, &edx); 41 | memcpy(&info.manufacturer[0], &ebx, 4); 42 | memcpy(&info.manufacturer[4], &edx, 4); 43 | memcpy(&info.manufacturer[8], &ecx, 4); 44 | info.manufacturer[12] = '\0'; 45 | 46 | // Get model info 47 | get_cpuid(0x1, &eax, &ebx, NULL, &edx); 48 | info.stepping_id = eax & 0xf; 49 | eax >>= 4; 50 | info.model_id = eax & 0xf; 51 | eax >>= 4; 52 | info.family_id = eax & 0xf; 53 | eax >>= 4; 54 | info.type = eax & 0x3; 55 | eax >>= 4; 56 | 57 | if (info.model_id == 0xf) { 58 | // Extended model id 59 | info.model_id += eax & 0xf; 60 | } 61 | eax >>= 4; 62 | 63 | if (info.family_id == 0xf) { 64 | // Extended family id 65 | info.family_id += eax & 0xff; 66 | } 67 | 68 | _get_cpu_brand(info.brand); 69 | 70 | // Get CPU technical info 71 | info.logical_cores = (ebx >> 16) & 0xff; 72 | info.hyperthreading = test_bit(edx, 28); 73 | 74 | get_cpuid(0x80000008, NULL, NULL, &ecx, NULL); 75 | info.physical_cores = (u8) ecx; 76 | info.physical_cores += 1; 77 | } 78 | 79 | State check_state() { 80 | u32 edx = 0; 81 | 82 | // ---------------------------------------------- 83 | get_cpuid(0x1, NULL, NULL, NULL, &edx); 84 | if (test_bit(edx, 0) == false) { 85 | return State::NO_FPU; 86 | } 87 | if (test_bit(edx, 5) == false) { 88 | return State::NO_MSR; 89 | } 90 | if (test_bit(edx, 9) == false) { 91 | // Read: 10.4.2 Presence of the Local APIC 92 | return State::NO_LAPIC; 93 | } 94 | 95 | // ---------------------------------------------- 96 | get_cpuid(0x80000001, NULL, NULL, NULL, &edx); 97 | if (test_bit(edx, 29) == false) { 98 | return State::NO_LONG_MODE; 99 | } 100 | 101 | return State::SUCCESS; 102 | } 103 | 104 | }; // namespace cpu -------------------------------------------------------------------------------- /kernel/src/cpu/cpu.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace cpu { 5 | 6 | enum class State { 7 | ERROR, 8 | SUCCESS, 9 | NO_LONG_MODE, 10 | NO_FPU, 11 | NO_LAPIC, 12 | NO_MSR 13 | }; 14 | 15 | struct Info { 16 | char manufacturer[13]; // + \0 17 | u8 stepping_id : 4; 18 | u8 type : 2; 19 | u16 model_id; 20 | u16 family_id; 21 | char brand[49]; // + \0 22 | bool hyperthreading; 23 | u8 physical_cores; 24 | u8 logical_cores; 25 | }; 26 | 27 | // Assembly functions 28 | extern "C" void get_cpuid(u32 cpuid_eax, u32 *eax, u32 *ebx, u32 *ecx, u32 *edx); 29 | extern "C" u64 read_msr(u32 msr_no); 30 | extern "C" void write_msr(u32 msr_no, u64 value); 31 | 32 | void get_info(Info &info); 33 | State check_state(); 34 | 35 | static inline void outb(u16 port, u8 val) { 36 | // I/O Port OUT byte 37 | __asm__ volatile("outb %b0, %w1" : : "a"(val), "Nd"(port) : "memory"); 38 | } 39 | 40 | static inline u8 inb(u16 port) { 41 | // I/O Port IN byte 42 | u8 ret; 43 | __asm__ volatile("inb %w1, %b0" : "=a"(ret) : "Nd"(port) : "memory"); 44 | return ret; 45 | } 46 | 47 | static inline void io_wait() { 48 | cpu::outb(0x80, 0); 49 | } 50 | 51 | }; // namespace cpu -------------------------------------------------------------------------------- /kernel/src/cpu/mmio.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace mmio { 5 | 6 | static inline void write8(void *p, u8 data) { 7 | *(volatile u8 *) (p) = data; 8 | } 9 | 10 | static inline u8 read8(void *p) { 11 | return *(volatile u8 *) (p); 12 | } 13 | 14 | static inline void write16(void *p, u16 data) { 15 | *(volatile u16 *) (p) = data; 16 | } 17 | 18 | static inline u16 read16(void *p) { 19 | return *(volatile u16 *) (p); 20 | } 21 | 22 | static inline void write32(void *p, u32 data) { 23 | *(volatile u32 *) (p) = data; 24 | } 25 | 26 | static inline u32 read32(void *p) { 27 | return *(volatile u32 *) (p); 28 | } 29 | 30 | static inline void write64(void *p, u64 data) { 31 | *(volatile u64 *) (p) = data; 32 | } 33 | 34 | static inline u64 read64(void *p) { 35 | return *(volatile u64 *) (p); 36 | } 37 | 38 | static inline void readn(void *dst, const volatile void *src, size bytes) { 39 | volatile u8 *s = (volatile u8 *) src; 40 | u8 *d = (u8 *) dst; 41 | 42 | while (bytes > 0) { 43 | *d = *s; 44 | ++s; 45 | ++d; 46 | --bytes; 47 | } 48 | } 49 | 50 | }; // namespace mmio -------------------------------------------------------------------------------- /kernel/src/debug/print.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void kprint_mem_hex(u8 *ptr, u16 n_bytes) { 8 | // Print bytes of memory as hex numbers 9 | // printf("(first: 0x%p, last: 0x%p, %d bytes):\n", ptr + n_bytes - 1, ptr, n_bytes); 10 | // TODO: get 4 bytes at once by mmio_read32 11 | 12 | ptr += n_bytes - 1; 13 | char str[4]; 14 | u8 line_c = 0; 15 | 16 | while (n_bytes > 0) { 17 | if (line_c == 8 || line_c == 0) { 18 | line_c = 0; 19 | kprintf("\n%p-%p: ", ptr, ptr - 7); 20 | } 21 | 22 | memset((void *) str, 0x0, 4); 23 | itoa(mmio::read8((void *) ptr), str, 16); 24 | 25 | if (str_len(str) == 1) { 26 | // Zero alignment 27 | // kernel.console->print('0'); 28 | } 29 | 30 | // kernel.console->print(str); 31 | // kernel.console->print(' '); 32 | ptr--; 33 | n_bytes--; 34 | line_c++; 35 | } 36 | 37 | // kernel.console->print('\n'); 38 | } 39 | 40 | void kprint_mem_bin(u8 *ptr, u16 n_bytes) { 41 | // Print bytes of memory as binary numbers 42 | kprintf("(first: 0x%p, last: 0x%p, %d bytes):\n", ptr + n_bytes - 1, ptr, n_bytes); 43 | ptr += n_bytes - 1; 44 | char str[16]; 45 | 46 | while (n_bytes > 0) { 47 | memset((void *) str, 0x0, 16); 48 | itoa(mmio::read8((void *) ptr), str, 2); 49 | 50 | // Zero alignment 51 | u8 len = str_len(str); 52 | while (len++ < 8) { 53 | // kernel.console->print('0'); 54 | } 55 | 56 | kernel.console->print(str); 57 | // kernel.console->print(' '); 58 | ptr--; 59 | n_bytes--; 60 | } 61 | 62 | // kernel.console->print('\n'); 63 | } 64 | 65 | void kprint_mem_ascii(u8 *ptr, u16 n_bytes) { 66 | // Print bytes of memory as ASCII characters 67 | kprintf("(first: 0x%p, last: 0x%p, %d bytes):\n", ptr + n_bytes - 1, ptr, n_bytes); 68 | ptr += n_bytes - 1; 69 | 70 | while (n_bytes > 0) { 71 | // kernel.console->print(mmio::read8((void *) ptr)); 72 | ptr--; 73 | n_bytes--; 74 | } 75 | 76 | // kernel.console->print('\n'); 77 | } -------------------------------------------------------------------------------- /kernel/src/debug/print.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | void kprint_mem_hex(uint8_t *ptr, uint16_t n_bytes); 5 | void kprint_mem_bin(uint8_t *ptr, uint16_t n_bytes); 6 | void kprint_mem_ascii(uint8_t *ptr, uint16_t n_bytes); 7 | -------------------------------------------------------------------------------- /kernel/src/drivers/keyboard.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | char Keyboard::read_key() { 6 | auto code = ps2::controller::read_data(); 7 | 8 | // Map scan code to ASCII 9 | switch (code) { 10 | case (0x2): 11 | return this->_mod_special('1', '!'); 12 | case (0x3): 13 | return this->_mod_special('2', '@'); 14 | case (0x4): 15 | return this->_mod_special('3', '#'); 16 | case (0x5): 17 | return this->_mod_special('4', '$'); 18 | case (0x6): 19 | return this->_mod_special('5', '%'); 20 | case (0x7): 21 | return this->_mod_special('6', '^'); 22 | case (0x8): 23 | return this->_mod_special('7', '&'); 24 | case (0x9): 25 | return this->_mod_special('8', '*'); 26 | case (0xA): 27 | return this->_mod_special('9', '('); 28 | case (0xB): 29 | return this->_mod_special('0', ')'); 30 | case (0xC): 31 | return this->_mod_special('-', '_'); 32 | case (0xD): 33 | return this->_mod_special('=', '+'); 34 | case (0x10): 35 | return this->_mod_letter('q', 'Q'); 36 | case (0x11): 37 | return this->_mod_letter('w', 'W'); 38 | case (0x12): 39 | return this->_mod_letter('e', 'E'); 40 | case (0x13): 41 | return this->_mod_letter('r', 'R'); 42 | case (0x14): 43 | return this->_mod_letter('t', 'T'); 44 | case (0x15): 45 | return this->_mod_letter('y', 'Y'); 46 | case (0x16): 47 | return this->_mod_letter('u', 'U'); 48 | case (0x17): 49 | return this->_mod_letter('i', 'I'); 50 | case (0x18): 51 | return this->_mod_letter('o', 'O'); 52 | case (0x19): 53 | return this->_mod_letter('p', 'P'); 54 | case (0x1A): 55 | return this->_mod_special('[', '{'); 56 | case (0x1B): 57 | return this->_mod_special(']', '}'); 58 | case (0x1E): 59 | return this->_mod_letter('a', 'A'); 60 | case (0x1F): 61 | return this->_mod_letter('s', 'S'); 62 | case (0x20): 63 | return this->_mod_letter('d', 'D'); 64 | case (0x21): 65 | return this->_mod_letter('f', 'F'); 66 | case (0x22): 67 | return this->_mod_letter('g', 'G'); 68 | case (0x23): 69 | return this->_mod_letter('h', 'H'); 70 | case (0x24): 71 | return this->_mod_letter('j', 'J'); 72 | case (0x25): 73 | return this->_mod_letter('k', 'K'); 74 | case (0x26): 75 | return this->_mod_letter('l', 'L'); 76 | case (0x27): 77 | return this->_mod_special(';', ':'); 78 | case (0x28): 79 | return this->_mod_special('\'', '"'); 80 | case (0x29): 81 | return this->_mod_special('`', '~'); 82 | case (0x2B): 83 | return this->_mod_special('\\', '|'); 84 | case (0x2C): 85 | return this->_mod_letter('z', 'Z'); 86 | case (0x2D): 87 | return this->_mod_letter('x', 'X'); 88 | case (0x2E): 89 | return this->_mod_letter('c', 'C'); 90 | case (0x2F): 91 | return this->_mod_letter('v', 'V'); 92 | case (0x30): 93 | return this->_mod_letter('b', 'B'); 94 | case (0x31): 95 | return this->_mod_letter('n', 'N'); 96 | case (0x32): 97 | return this->_mod_letter('m', 'M'); 98 | case (0x33): 99 | return this->_mod_special(',', '<'); 100 | case (0x34): 101 | return this->_mod_special('.', '>'); 102 | case (0x35): 103 | return this->_mod_special('/', '?'); 104 | case (ScanCode::ENTER_IN): 105 | return ASCII::LINE_FEED; 106 | case (ScanCode::TAB_IN): 107 | return ASCII::HORIZONTAL_TAB; 108 | case (ScanCode::BACKSPACE_IN): 109 | return ASCII::BACKSPACE; 110 | case (ScanCode::ESCAPE_IN): 111 | return ASCII::ESCAPE; 112 | case (ScanCode::SPACE_IN): 113 | return ASCII::SPACE; 114 | case (ScanCode::LEFT_SHIFT_IN): 115 | case (ScanCode::RIGHT_SHIFT_IN): 116 | this->shift = true; 117 | return ASCII::SHIFT_IN; 118 | case (ScanCode::LEFT_SHIFT_OUT): 119 | case (ScanCode::RIGHT_SHIFT_OUT): 120 | this->shift = false; 121 | return ASCII::SHIFT_OUT; 122 | case (ScanCode::CAPSLOCK_IN): 123 | this->capslock = !this->capslock; 124 | break; 125 | default: 126 | return ASCII::NULL_CHAR; 127 | } 128 | 129 | return ASCII::NULL_CHAR; 130 | } 131 | 132 | inline char Keyboard::_mod_letter(char chr, char mod_chr) { 133 | if (this->capslock) { 134 | if (this->shift) { 135 | return chr; 136 | } else { 137 | return mod_chr; 138 | } 139 | } else { 140 | if (this->shift) { 141 | return mod_chr; 142 | } else { 143 | return chr; 144 | } 145 | } 146 | 147 | return chr; 148 | } 149 | 150 | inline char Keyboard::_mod_special(char chr, char mod_chr) { 151 | if (this->shift) { 152 | return mod_chr; 153 | } else { 154 | return chr; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /kernel/src/drivers/keyboard.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace ScanCode { 5 | 6 | constexpr u8 ESCAPE_IN = 0x01; 7 | constexpr u8 BACKSPACE_IN = 0x0e; 8 | constexpr u8 TAB_IN = 0x0f; 9 | constexpr u8 ENTER_IN = 0x1C; 10 | constexpr u8 LEFT_SHIFT_IN = 0x2a; 11 | constexpr u8 RIGHT_SHIFT_IN = 0x36; 12 | constexpr u8 SPACE_IN = 0x39; 13 | constexpr u8 CAPSLOCK_IN = 0x3a; 14 | constexpr u8 LEFT_SHIFT_OUT = 0xaa; 15 | constexpr u8 RIGHT_SHIFT_OUT = 0xb6; 16 | 17 | } // namespace ScanCode 18 | 19 | namespace ASCII { 20 | 21 | constexpr u8 NULL_CHAR = 0x00; 22 | constexpr u8 BACKSPACE = 0x08; 23 | constexpr u8 HORIZONTAL_TAB = 0x09; 24 | constexpr u8 LINE_FEED = 0x0a; 25 | constexpr u8 CARRIAGE_RETURN = 0x0d; 26 | constexpr u8 SHIFT_OUT = 0x0e; 27 | constexpr u8 SHIFT_IN = 0x0f; 28 | constexpr u8 ESCAPE = 0x1b; 29 | constexpr u8 SPACE = 0x20; 30 | 31 | } // namespace ASCII 32 | 33 | class Keyboard { 34 | private: 35 | inline char _mod_letter(char chr, char mod_chr); 36 | inline char _mod_special(char chr, char mod_chr); 37 | 38 | public: 39 | bool shift = false; 40 | bool capslock = false; 41 | 42 | public: 43 | char read_key(); 44 | bool is_mod(); 45 | }; -------------------------------------------------------------------------------- /kernel/src/drivers/ps2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace ps2::controller { 6 | 7 | u8 send_cmd(Command cmd, u8 data) { 8 | // Utility to execute PS/2 Controller command and read output. 9 | set_data(data); 10 | cpu::io_wait(); 11 | cpu::outb(static_cast(Port::COMMAND_W), static_cast(cmd)); 12 | cpu::io_wait(); 13 | 14 | return read_data(); 15 | } 16 | 17 | bool test() { 18 | auto result = send_cmd(Command::TEST_CONTROLLER, 0x0); 19 | 20 | return result == static_cast(CtrlTestResult::PASSED); 21 | } 22 | 23 | u8 read_status() { 24 | // Read STATUS port of PS/2 Controller 25 | return cpu::inb(static_cast(Port::STATUS_R)); 26 | } 27 | 28 | void set_status(u8 val) { 29 | // Set STATUS port of PS/2 Controller 30 | send_cmd(Command::CPY_LOW_TO_STAT, val); 31 | send_cmd(Command::CPY_HIGH_TO_STAT, val); 32 | } 33 | 34 | u8 read_data() { 35 | // Read DATA port of PS/2 Controller 36 | return cpu::inb(static_cast(Port::DATA_RW)); 37 | } 38 | 39 | void set_data(u8 val) { 40 | // Set DATA port of PS/2 Controller 41 | return cpu::outb(static_cast(Port::DATA_RW), val); 42 | } 43 | 44 | } // namespace ps2::controller 45 | 46 | namespace ps2::port_1 { 47 | 48 | using namespace ps2::controller; 49 | 50 | PortTestResult test() { 51 | auto result = send_cmd(Command::TEST_1_PORT, 0x0); 52 | 53 | return static_cast(result); 54 | } 55 | 56 | bool is_interrupt_enabled() { 57 | // Bit 0: First PS/2 port interrupt (1 = enabled, 0 = disabled) 58 | auto cfg = send_cmd(Command::GET_CONFIG, 0x0); 59 | 60 | return test_bit(cfg, 0) == 1; 61 | } 62 | 63 | void disable() { 64 | send_cmd(Command::DISABLE_1_PORT, 0x0); 65 | } 66 | 67 | void enable() { 68 | send_cmd(Command::ENABLE_1_PORT, 0x0); 69 | } 70 | 71 | } // namespace ps2::port_1 -------------------------------------------------------------------------------- /kernel/src/drivers/ps2.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Universal PS/2 driver. 3 | 4 | https://wiki.osdev.org/PS/2 5 | https://wiki.osdev.org/PS/2_Keyboard 6 | 7 | How it works: 8 | Actually, the PS/2 driver itself doesn't do much. 9 | After setting the appropriate interrupts (IRQ1 for the keyboard) on 10 | the APIC, the corresponding ISR is executed. The DATA port is read taking 11 | the scan code of the pressed key. 12 | 13 | Commands to the PS/2 Controller are sent via the COMMAND port. Their result 14 | is read from the DATA port. Data from the PS/2 devices arrives to the DATA 15 | port. They must be read to free up space. The distinction between devices 16 | (PS/2 Port 1 and Port 2) is made through different ISRs. 17 | */ 18 | 19 | #pragma once 20 | #include 21 | 22 | namespace ps2::controller { 23 | 24 | enum class Port { 25 | DATA_RW = 0x60, 26 | STATUS_R = 0x64, 27 | COMMAND_W = 0x64 28 | }; 29 | 30 | enum class Command { 31 | TEST_CONTROLLER = 0xAA, 32 | GET_CONFIG = 0x20, 33 | 34 | // Copy bits 0 to 3 of Data Reg to Status Reg bits 0 to 3 35 | CPY_LOW_TO_STAT = 0xC1, 36 | // Copy bits 4 to 7 of Data Reg to Status Reg bits 4 to 7 37 | CPY_HIGH_TO_STAT = 0xC2, 38 | 39 | TEST_1_PORT = 0xAB, 40 | DISABLE_1_PORT = 0xAD, 41 | ENABLE_1_PORT = 0xAE, 42 | 43 | TEST_2_PORT = 0xA9, 44 | DISABLE_2_PORT = 0xA7, 45 | ENABLE_2_PORT = 0xA8, 46 | }; 47 | 48 | enum class CtrlTestResult { 49 | PASSED = 0x55, 50 | FAILED = 0xFC 51 | }; 52 | 53 | bool test(); 54 | u8 send_cmd(Command cmd, u8 data); 55 | void set_status(u8 val); 56 | u8 read_status(); 57 | void set_data(u8 val); 58 | u8 read_data(); 59 | 60 | } // namespace ps2::controller 61 | 62 | namespace ps2::port_1 { 63 | 64 | enum class PortTestResult { 65 | PASSED = 0x00, 66 | CLOCK_STUCK_L = 0x01, 67 | CLOCK_STUCK_H = 0x02, 68 | DATA_STUCK_L = 0x03, 69 | DATA_STUCK_H = 0x04, 70 | }; 71 | 72 | PortTestResult test(); 73 | bool is_interrupt_enabled(); 74 | void disable(); 75 | void enable(); 76 | 77 | } // namespace ps2::port_1 78 | 79 | namespace ps2::port_2 { 80 | 81 | bool test(); 82 | void disable(); 83 | void enable(); 84 | 85 | } // namespace ps2::port_2 -------------------------------------------------------------------------------- /kernel/src/framebuffer/colors.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | constexpr u64 rgb_to_int(u8 red, u8 green, u8 blue) { 5 | u64 result = 0; 6 | result = blue; 7 | result += green << 8; 8 | result += red << 16; 9 | 10 | return result; 11 | } 12 | 13 | constexpr u64 BLACK = rgb_to_int(0, 0, 0); 14 | constexpr u64 WHITE = rgb_to_int(255, 255, 255); 15 | constexpr u64 RED = rgb_to_int(255, 0, 0); 16 | constexpr u64 GREEN = rgb_to_int(0, 255, 0); 17 | constexpr u64 BLUE = rgb_to_int(0, 0, 255); -------------------------------------------------------------------------------- /kernel/src/framebuffer/framebuffer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | Framebuffer::Framebuffer(FramebufferData &data) { 8 | this->__fb_addr = data.base_addr; 9 | this->__double_fb_addr = data.double_buffer; 10 | this->_height = data.height; 11 | this->_width = data.width; 12 | this->_b_size = data.b_size; 13 | this->_px_per_scan = data.px_per_scan; 14 | this->_b_per_px = data.b_per_px; 15 | 16 | ASSERT(this->__fb_addr != nullptr); 17 | } 18 | 19 | inline void *Framebuffer::get_pixel_addr(u32 x, u32 y) { 20 | // Calculate framebuffer address using :x and :y coordinates 21 | // addr = base addr + y offset (from base addr) + x offset (from y) 22 | u64 addr = reinterpret_cast(this->__double_fb_addr) + 23 | (y * this->_px_per_scan * this->_b_per_px) + (x * this->_b_per_px); 24 | 25 | return reinterpret_cast(addr); 26 | } 27 | 28 | void Framebuffer::set_pixel(u32 x, u32 y, u64 color) { 29 | // Write :color into calculated framebuffer address 30 | auto pixel = static_cast(this->get_pixel_addr(x, y)); 31 | *pixel = color; 32 | } 33 | 34 | void Framebuffer::set_all_pixels(u64 color) { 35 | // Set solid color for entire framebuffer 36 | for (u16 x = 0; x < this->_width; x++) { 37 | for (u16 y = 0; y < this->_height; y++) { 38 | this->set_pixel(x, y, color); 39 | } 40 | } 41 | } 42 | 43 | void Framebuffer::print_glyph(Glyph &glyph) { 44 | // Iterate through glyph pixels in glyph bitmap 45 | for (u16 y = 0; y < glyph.height; y++) { 46 | for (u16 x = 0; x < glyph.width; x++) { 47 | // Print bitmap pixels 48 | u64 color; 49 | if ((*glyph.bitmap & ((1 << 7) >> x)) > 0) { 50 | // Glyph color 51 | color = glyph.fg_color; 52 | } else { 53 | // Background color 54 | color = glyph.bg_color; 55 | } 56 | this->set_pixel(glyph.x_off + x, glyph.y_off + y, color); 57 | } 58 | glyph.bitmap++; 59 | } 60 | } 61 | 62 | void *Framebuffer::get_fb_addr() { 63 | // Return base address of the framebuffer 64 | return this->__fb_addr; 65 | } 66 | 67 | void *Framebuffer::get_double_fb_addr() { 68 | // Return base address of the double framebuffer 69 | return this->__double_fb_addr; 70 | } 71 | 72 | void Framebuffer::draw() { 73 | /* 74 | If a double-buffer is initialized, copy its memory to the framebuffer. 75 | Otherwise, the framebuffer was used directly and do nothing here. 76 | */ 77 | 78 | // TODO: It's slow :/ Why? Maybe inefficient memcpy implementation 79 | memcpy(this->__fb_addr, this->__double_fb_addr, this->_b_size); 80 | } -------------------------------------------------------------------------------- /kernel/src/framebuffer/framebuffer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | struct __attribute__((packed)) FramebufferData { 5 | void *base_addr; 6 | void *double_buffer; 7 | size b_size; // Size in bytes 8 | u16 width; 9 | u16 height; 10 | u16 px_per_scan; 11 | u8 b_per_px; 12 | }; 13 | 14 | struct Glyph { 15 | char *bitmap; 16 | u16 x_off; 17 | u16 y_off; 18 | u8 height; 19 | u8 width; 20 | u32 fg_color; 21 | u32 bg_color; 22 | }; 23 | 24 | class Framebuffer { 25 | private: 26 | size _b_size; // Size of framebuffer in bytes 27 | u16 _width; // Width in pixels 28 | u16 _height; // Height in pixels 29 | u16 _px_per_scan; // Pixels per scanline 30 | u8 _b_per_px; // Bytes per pixels 31 | 32 | void *__fb_addr; // Base memory address of framebuffer 33 | void *__double_fb_addr; // Double framebuffer 34 | 35 | public: 36 | const u16 &width = _width; 37 | const u16 &height = _height; 38 | const size &b_size = _b_size; 39 | const u8 &b_per_px = _b_per_px; 40 | 41 | public: 42 | Framebuffer(FramebufferData &data); 43 | inline void *get_pixel_addr(u32 x, u32 y); 44 | void set_pixel(u32 x, u32 y, u64 color); 45 | void set_all_pixels(u64 color); 46 | void print_glyph(Glyph &glyph); 47 | void *get_fb_addr(); 48 | void *get_double_fb_addr(); 49 | void draw(); 50 | }; 51 | -------------------------------------------------------------------------------- /kernel/src/gdt/gdt.asm: -------------------------------------------------------------------------------- 1 | [bits 64] 2 | 3 | section .text: 4 | global __load_gdt 5 | 6 | __load_gdt: 7 | lgdt [rdi] ; Load GDT 8 | 9 | ; Set segment registers to kernel data segment 10 | ; (2nd position in GDT = 0x10) 11 | mov ax, 0x10 12 | mov ds, ax 13 | mov es, ax 14 | mov fs, ax 15 | mov gs, ax 16 | mov ss, ax 17 | 18 | ; Far return to segment:offset (from stack) 19 | ; in order to reload segment registers. 20 | ; Put on stack 0x08:ret_addr: 21 | pop rdi 22 | mov rax, 0x08 23 | push rax 24 | push rdi 25 | 26 | ; Call qword far return: 27 | ; It gets two values from stack and loads 28 | ; them into CS (Code Segment) and IP registers. 29 | retfq -------------------------------------------------------------------------------- /kernel/src/gdt/gdt.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* 5 | Segmentation is still required in x86-64 but we use that as many 6 | other kernels - we are making flat model using four segments. Each 7 | segment holds entire address space. 8 | 9 | To better understanding why whe are using segmentation 10 | to make just flat-model: 11 | https://wiki.osdev.org/Segmentation#Notes_Regarding_C 12 | 13 | */ 14 | 15 | namespace gdt { 16 | 17 | extern "C" void __load_gdt(GDTR *gdtr_ptr); 18 | 19 | GDT gdt = { 20 | .null = 21 | { 22 | .limit0 = 0x0, 23 | .base0 = 0x0, 24 | .base1 = 0x0, 25 | .flags1 = 0x0, 26 | .limit1_flags2 = 0x0, 27 | .base2 = 0x0, 28 | }, 29 | .kernel_code = 30 | { 31 | .limit0 = 0x0, 32 | .base0 = 0x0, 33 | .base1 = 0x0, 34 | .flags1 = 0x9a, 35 | .limit1_flags2 = 0xa0, 36 | .base2 = 0x0, 37 | }, 38 | .kernel_data = 39 | { 40 | .limit0 = 0x0, 41 | .base0 = 0x0, 42 | .base1 = 0x0, 43 | .flags1 = 0x92, // 10010010 44 | .limit1_flags2 = 0xa0, // 10100000 45 | .base2 = 0x0, 46 | }, 47 | .user_code = 48 | { 49 | .limit0 = 0x0, 50 | .base0 = 0x0, 51 | .base1 = 0x0, 52 | .flags1 = 0x9a, 53 | .limit1_flags2 = 0xa0, 54 | .base2 = 0x0, 55 | }, 56 | .user_data = 57 | { 58 | .limit0 = 0x0, 59 | .base0 = 0x0, 60 | .base1 = 0x0, 61 | .flags1 = 0x92, 62 | .limit1_flags2 = 0xa0, 63 | .base2 = 0x0, 64 | }, 65 | }; 66 | 67 | GDTR gdtr = { 68 | .size = sizeof(GDT) - 1, 69 | .offset = (u64) &gdt, 70 | }; 71 | 72 | GDT *init() { 73 | __load_gdt(&gdtr); 74 | 75 | return &gdt; 76 | } 77 | 78 | }; // namespace gdt -------------------------------------------------------------------------------- /kernel/src/gdt/gdt.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Interesting overview of GTD and segmentation (memory protection at all) 3 | is included in 'Intel's x86 Manual vol. 3A': 4 | 3.1 MEMORY MANAGEMENT OVERVIEW 5 | Overview of different memory models utilising segmentation: 6 | 3.2 USING SEGMENTS 7 | Usage of segmentation on x86-64 (IA-32e): 8 | 3.2.4 Segmentation in IA-32e Mode 9 | About segment registers on x86-64: 10 | 3.4.4 Segment Loading Instructions in IA-32e Mode 11 | About segment descriptors: 12 | 3.4.5 Segment Descriptors 13 | About GDT (Segment Descriptors Table): 14 | 3.5.1 Segment Descriptor Tables 15 | */ 16 | #pragma once 17 | #include 18 | 19 | namespace gdt { 20 | 21 | enum class SegmentSelector { 22 | KERNEL_CODE = 8, 23 | KERNEL_DATA = 16, 24 | USER_CODE = 24, 25 | USER_DATA = 32, 26 | }; 27 | 28 | struct __attribute__((packed)) GDTR { 29 | u16 size; 30 | u64 offset; 31 | }; 32 | 33 | struct __attribute__((packed)) SegmentDescriptor { 34 | u16 limit0; 35 | u16 base0; 36 | u8 base1; 37 | u8 flags1; 38 | u8 limit1_flags2; 39 | u8 base2; 40 | }; 41 | 42 | // Minimal implementation of Protected Flat Model 43 | // 3.2.2. Protected Flat Model 44 | // "Similar design is used by several popular multitasking OS'es." 45 | struct __attribute__((packed, aligned(0x1000))) GDT { 46 | SegmentDescriptor null; 47 | SegmentDescriptor kernel_code; 48 | SegmentDescriptor kernel_data; 49 | SegmentDescriptor user_code; 50 | SegmentDescriptor user_data; 51 | }; 52 | // https://stackoverflow.com/questions/11770451/what-is-the-meaning-of-attribute-packed-aligned4 53 | // __attribute__((packed)) -> use the smallest possible space for struct (avoid padding) 54 | // __attribute__((aligned(0x1000))) -> its start address can be divided by 0x1000 55 | 56 | GDT *init(); 57 | 58 | } // namespace gdt -------------------------------------------------------------------------------- /kernel/src/idt/idt.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace idt { 9 | 10 | IDT::IDT() { 11 | this->add_int(TRAP_GATE, Interrupt::DIVISION_BY_ZERO, ÷_by_zero_handler); 12 | this->add_int(INTERRUPT_GATE, Interrupt::PAGE_FAULT, &page_fault_handler); 13 | this->add_int(INTERRUPT_GATE, Interrupt::TIMER, &timer_handler); 14 | this->add_int(INTERRUPT_GATE, Interrupt::KEYBOARD, &keyboard_handler); 15 | this->add_int(INTERRUPT_GATE, Interrupt::SPURIOUS, &spurious_handler); 16 | 17 | this->__load_idtr(); 18 | } 19 | 20 | void IDT::add_int(Gate gate, Interrupt index, void (*handler)(InterruptFrame *)) { 21 | /* 22 | Add interrupt handler into IDT. 23 | IMPORTANT: It doesn't check if the table entry is already occupied. 24 | */ 25 | auto entry = &idt[index]; 26 | entry->present = 1; 27 | entry->gate_type = gate; 28 | entry->segment_selector = (u16) gdt::SegmentSelector::KERNEL_CODE; 29 | 30 | u64 offset = (u64) handler; 31 | entry->offset1 = (u16) (offset & 0xffff); 32 | offset >>= 16; 33 | entry->offset2 = (u16) (offset & 0xffff); 34 | offset >>= 16; 35 | entry->offset3 = (u32) (offset & 0xffffffff); 36 | } 37 | 38 | void IDT::__load_idtr() { 39 | // Load IDT to IDTR 40 | u16 limit = sizeof(Entry) * ALL_IDT_ENTRIES; 41 | IDTR idtr = {.limit = limit, .base_addr = (void *) this->idt}; 42 | __asm__ volatile("lidt %0" ::"m"(idtr)); 43 | } 44 | 45 | void enable_external_interrupts() { 46 | /* 47 | DEVLOG: I literally spent weeks debugging IOAPIC, LAPIC and HPET. 48 | I rewrote the kernel to C++ thinking it would help. It turned out 49 | that the IF flag is not set in the CPU. Well... n00b exposed. 50 | */ 51 | asm("sti"); 52 | } 53 | 54 | } // namespace idt -------------------------------------------------------------------------------- /kernel/src/idt/idt.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | About interrupts and exceptions in general in IM3: 3 | 6. Interrupt and exception handling 4 | */ 5 | #pragma once 6 | #include 7 | #include 8 | 9 | #define ALL_IDT_ENTRIES 256 10 | 11 | namespace idt { 12 | 13 | // IM3, Table 3-2 - System-Segment and Gate-Descriptor Types 14 | enum Gate { 15 | CALL_GATE = 12, 16 | INTERRUPT_GATE = 14, 17 | TRAP_GATE = 15 18 | }; 19 | 20 | struct __attribute__((packed)) Entry { 21 | // IM3, Figure 6-8 - 64-Bit IDT Gate Descriptor 22 | u16 offset1; 23 | u16 segment_selector; 24 | u8 ist : 3; 25 | u8 ignore1 : 5; 26 | u8 gate_type : 4; 27 | u8 ignore2 : 1; 28 | u8 dpl : 2; 29 | u8 present : 1; 30 | u16 offset2; 31 | u32 offset3; 32 | u32 reserved1; 33 | }; 34 | 35 | struct __attribute__((packed)) IDTR { 36 | // IM3, Figure 6-1 - Relationship of the IDTR and IDT 37 | u16 limit; 38 | void *base_addr; 39 | }; 40 | 41 | // Interrupt Descript Table 42 | class IDT { 43 | private: 44 | Entry idt[ALL_IDT_ENTRIES]; 45 | 46 | public: 47 | IDT(); 48 | 49 | private: 50 | void add_int(Gate gate, Interrupt index, void (*handler)(InterruptFrame *)); 51 | void __load_idtr(); 52 | }; 53 | 54 | void enable_external_interrupts(); 55 | 56 | } // namespace idt -------------------------------------------------------------------------------- /kernel/src/idt/interrupts.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define INTERRUPT(v) __asm__ volatile("int %0" ::"N"(v)); 13 | 14 | void interrupt(Interrupt isr_index) { 15 | /* 16 | Call interrupt. Make jump to Interrupt Service Routine. 17 | NOTE: I've tried to make this cleaner but apparently inline assembly 18 | cannot put variable value as `int` argument. 19 | */ 20 | switch (isr_index) { 21 | case Interrupt::DIVISION_BY_ZERO: 22 | INTERRUPT(Interrupt::DIVISION_BY_ZERO); 23 | break; 24 | case Interrupt::PAGE_FAULT: 25 | INTERRUPT(Interrupt::PAGE_FAULT); 26 | break; 27 | case Interrupt::KEYBOARD: 28 | INTERRUPT(Interrupt::KEYBOARD); 29 | break; 30 | case Interrupt::SPURIOUS: 31 | INTERRUPT(Interrupt::SPURIOUS); 32 | default: 33 | break; 34 | } 35 | } 36 | 37 | __attribute__((interrupt)) void page_fault_handler(InterruptFrame *frame) { 38 | kprintf("page fault"); 39 | 40 | while (1) 41 | ; 42 | } 43 | 44 | __attribute__((interrupt)) void divide_by_zero_handler(InterruptFrame *frame) { 45 | kpanic("division by zero"); 46 | 47 | while (1) 48 | ; 49 | } 50 | 51 | size secs = 0; 52 | u8 ms = 0; 53 | 54 | __attribute__((interrupt)) void timer_handler(InterruptFrame *frame) { 55 | ms += 1; 56 | if (ms == 10) { 57 | ms = 0; 58 | secs++; 59 | } 60 | kprintf("%d.%d\r", secs, ms); 61 | 62 | lapic::write_eoi(); 63 | } 64 | 65 | __attribute__((interrupt)) void keyboard_handler(InterruptFrame *frame) { 66 | auto chr = kernel.keyboard->read_key(); 67 | 68 | if (chr != 0x00) [[likely]] { 69 | kernel.console->print(chr); 70 | } 71 | 72 | lapic::write_eoi(); 73 | } 74 | 75 | __attribute__((interrupt)) void spurious_handler(InterruptFrame *frame) { 76 | // IM3, 10.9 Spurious Interrupt (APIC) 77 | kprintf("SPURIOUS!!!"); 78 | 79 | while (1) 80 | ; 81 | } 82 | 83 | // TODO: Check if needed and remove 84 | __attribute__((interrupt)) void apic_error_handler(InterruptFrame *frame) { 85 | kprintf("APIC ERROR!!!"); 86 | 87 | while (1) 88 | ; 89 | } 90 | -------------------------------------------------------------------------------- /kernel/src/idt/interrupts.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Vector (entries) indexes in IDT. 4 | // For all standard vectors, see: 5 | // IM3, Table 6-1. Protected-Mode Exceptions and Interrupts 6 | enum Interrupt { 7 | // Standard x86 interrupts 8 | DIVISION_BY_ZERO = 0, 9 | PAGE_FAULT = 14, 10 | 11 | // Custom interrupts 12 | TIMER = 40, 13 | KEYBOARD = 41, 14 | 15 | SPURIOUS = 0xff 16 | }; 17 | 18 | struct InterruptFrame; 19 | 20 | void interrupt(Interrupt isr_index); 21 | 22 | __attribute__((interrupt)) void page_fault_handler(InterruptFrame *frame); 23 | __attribute__((interrupt)) void divide_by_zero_handler(InterruptFrame *frame); 24 | __attribute__((interrupt)) void timer_handler(InterruptFrame *frame); 25 | __attribute__((interrupt)) void keyboard_handler(InterruptFrame *frame); 26 | __attribute__((interrupt)) void spurious_handler(InterruptFrame *frame); 27 | __attribute__((interrupt)) void apic_error_handler(InterruptFrame *frame); -------------------------------------------------------------------------------- /kernel/src/kernel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | // Global kernel structures 23 | KernelData kernel; 24 | DevicesInfo devices; 25 | 26 | extern "C" void _start(BootloaderData *bd) { 27 | ASSERT(bd != nullptr); 28 | 29 | kernel.stack = static_cast(bd); 30 | kernel.acpi_rsdp = (acpi::RSDP *) bd->acpi_rsdp; 31 | 32 | /* 33 | Keyboard 34 | */ 35 | auto keyboard = Keyboard(); 36 | kernel.keyboard = &keyboard; 37 | 38 | /* 39 | Memory map 40 | */ 41 | auto mmap = MemoryMap(*bd->memory); 42 | kernel.mmap = &mmap; 43 | 44 | /* 45 | Framebuffer 46 | */ 47 | auto fb = Framebuffer(*bd->framebuffer); 48 | kernel.framebuffer = &fb; 49 | kernel.memory = bd->memory; 50 | 51 | /* 52 | Kernel console 53 | */ 54 | auto con = Console(*bd->font, *kernel.framebuffer); 55 | kernel.console = &con; 56 | kernel.console->clear(); 57 | 58 | /* 59 | Physical memory 60 | */ 61 | auto pmem = PMem(); 62 | kernel.pmem = &pmem; 63 | kprint_succ("Physical memory initalization success"); 64 | kprint_pmem_info(); 65 | 66 | /* 67 | CPU 68 | */ 69 | cpu::get_info(devices.cpu); 70 | auto cpu_s = cpu::check_state(); 71 | if (cpu_s != cpu::State::SUCCESS) { 72 | kprintf("[-] CPU state error: %d", cpu_s); 73 | } else { 74 | kprint_succ("CPU state correct"); 75 | } 76 | // kprint_cpu_info(); 77 | 78 | /* 79 | Global Description Table 80 | */ 81 | kernel.gdt = gdt::init(); 82 | kprint_succ("GDT initalization success"); 83 | 84 | /* 85 | IDT 86 | */ 87 | auto idt = idt::IDT(); 88 | kernel.idt = &idt; 89 | kprint_succ("IDT initialization success"); 90 | 91 | ASSERT(kernel.keyboard != nullptr); 92 | 93 | /* 94 | ACPI 95 | */ 96 | switch (acpi::get_version()) { 97 | case acpi::Version::VER_1: 98 | kprint_succ("ACPI 1.0 detected"); 99 | break; 100 | case acpi::Version::VER_2: 101 | kprint_succ("ACPI 2.0 detected"); 102 | break; 103 | default: 104 | kpanic("Unknown version of ACPI detected"); 105 | break; 106 | } 107 | 108 | if (acpi::verify_rsdp() == false) { 109 | kpanic("RSDP not verified"); 110 | } else { 111 | kprint_succ("RSDP verified"); 112 | } 113 | 114 | if (acpi::sdt::verify_rsdt() == false) { 115 | kpanic("RSDT not verified"); 116 | } else { 117 | kprint_succ("RSDT verified"); 118 | } 119 | 120 | /* 121 | MADT 122 | */ 123 | kernel.acpi_tables.madt = (madt::SDT_Table *) acpi::sdt::get_header(acpi::sdt::MADT_SIG); 124 | ASSERT(kernel.acpi_tables.madt != nullptr); 125 | 126 | kernel.acpi_tables.lapic = 127 | (madt::ics::CPU_LAPIC_Struct *) madt::ics::get_header(madt::ics::CPU_LAPIC); 128 | ASSERT(kernel.acpi_tables.lapic != nullptr); 129 | 130 | kernel.acpi_tables.ioapic = 131 | (madt::ics::IO_APIC_Struct *) madt::ics::get_header(madt::ics::IO_APIC); 132 | ASSERT(kernel.acpi_tables.ioapic != nullptr); 133 | 134 | /* 135 | HPET 136 | */ 137 | kernel.acpi_tables.hpet = (hpet::SDT_Table *) acpi::sdt::get_header(acpi::sdt::HPET_SIG); 138 | ASSERT(kernel.acpi_tables.hpet != nullptr); 139 | kprint_succ("HPET initialization success"); 140 | 141 | /* 142 | Paging 143 | */ 144 | auto paging = Paging(); 145 | kernel.paging = &paging; 146 | kprint_succ("4-level paging initialization success"); 147 | 148 | madt::dbg_print_lapic_info(); 149 | 150 | /* 151 | External interrupts 152 | */ 153 | pic::disable(); 154 | idt::enable_external_interrupts(); 155 | ioapic::init(); 156 | lapic::init(); 157 | 158 | /* 159 | Heap (Dynamic Allocation) 160 | */ 161 | auto heap = Heap(); 162 | kernel.heap = &heap; 163 | 164 | // TODO: PS/2 keyboard driver 165 | 166 | auto ptr1 = kernel.heap->malloc(10); 167 | auto ptr2 = kernel.heap->malloc(10); 168 | auto ptr3 = kernel.heap->malloc(10); 169 | auto ptr4 = kernel.heap->malloc(10); 170 | auto ptr5 = kernel.heap->malloc(10); 171 | auto ptr6 = kernel.heap->malloc(10); 172 | auto ptr7 = kernel.heap->malloc(10); 173 | auto ptr8 = kernel.heap->malloc(10); 174 | auto ptr9 = kernel.heap->malloc(5000); 175 | kprintf("init: "); 176 | kprint_heap_info(); 177 | kernel.heap->free(ptr2); 178 | kprintf("ptr2: "); 179 | kprint_heap_info(); 180 | kernel.heap->free(ptr3); 181 | kprintf("ptr3: "); 182 | kprint_heap_info(); 183 | kernel.heap->free(ptr8); 184 | kprintf("ptr8: "); 185 | kprint_heap_info(); 186 | kernel.heap->free(ptr6); 187 | kprintf("ptr6: "); 188 | kprint_heap_info(); 189 | kernel.heap->free(ptr4); 190 | kprintf("ptr4: "); 191 | kprint_heap_info(); 192 | kernel.heap->free(ptr1); 193 | kprintf("ptr1: "); 194 | kprint_heap_info(); 195 | kernel.heap->free(ptr5); 196 | kprintf("ptr5: "); 197 | kprint_heap_info(); 198 | kernel.heap->free(ptr7); 199 | kprintf("ptr7: "); 200 | kprint_heap_info(); 201 | kernel.heap->free(ptr9); 202 | 203 | auto hdr = reinterpret_cast(ptr9 - sizeof(BlockHeader)); 204 | kprintf("Bytes: %d, next: %d, prev: %d, header: %d\n", 205 | hdr->bytes, 206 | hdr->next->bytes, 207 | hdr->prev->bytes, 208 | sizeof(BlockHeader)); 209 | 210 | cpu::outb(0x64, 0xAA); 211 | auto resp = cpu::inb(0x60); 212 | kprintf("%p\n", resp); 213 | cpu::outb(0x64, 0xAB); 214 | resp = cpu::inb(0x60); 215 | kprintf("%p\n", resp); 216 | 217 | ps2::port_1::enable(); 218 | 219 | /* 220 | kprintf("PS/2 Controller Test: %d\n", ps2::controller::test()); 221 | kprintf("PS/2 Port 1 Enabled : %d\n", ps2::port_1::is_interrupt_enabled()); 222 | kprintf("\nPrev-Status: %d\n", ps2::controller::get_status_reg()); 223 | ps2::controller::set_status_reg(28); 224 | kprintf("Next-Status: %d\n", ps2::controller::get_status_reg()); 225 | */ 226 | 227 | /* 228 | Main timer 229 | hpet::init(); 230 | auto comp = hpet::comparator::Comparator(0); 231 | comp.set_periodic(100); 232 | */ 233 | 234 | // Just in sake of debugging 235 | for (u64 i = 0; i < 999999999999; i++) { 236 | u8 x = i + 1; 237 | u16 y = x + 3 - i; 238 | 239 | if (i % 6000000 == 0) { 240 | // kprintf("."); 241 | } 242 | } 243 | kprintf("Done :/"); 244 | for (;;) 245 | ; 246 | } -------------------------------------------------------------------------------- /kernel/src/kernel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | struct BootloaderData { 17 | FramebufferData *framebuffer = nullptr; 18 | ConsoleFont *font = nullptr; 19 | MemoryData *memory = nullptr; 20 | void *acpi_rsdp = nullptr; 21 | }; 22 | 23 | struct ACPI_Tables { 24 | madt::SDT_Table *madt = nullptr; 25 | madt::ics::CPU_LAPIC_Struct *lapic = nullptr; 26 | madt::ics::IO_APIC_Struct *ioapic = nullptr; 27 | hpet::SDT_Table *hpet = nullptr; 28 | }; 29 | 30 | struct DevicesInfo { 31 | cpu::Info cpu = {}; 32 | }; 33 | 34 | struct KernelData { 35 | void *stack = nullptr; 36 | Console *console = nullptr; 37 | acpi::RSDP *acpi_rsdp = nullptr; 38 | gdt::GDT *gdt = nullptr; 39 | idt::IDT *idt = nullptr; 40 | ACPI_Tables acpi_tables; 41 | Framebuffer *framebuffer = nullptr; 42 | 43 | // Memory 44 | MemoryData *memory = nullptr; 45 | MemoryMap *mmap = nullptr; 46 | PMem *pmem = nullptr; 47 | Paging *paging = nullptr; 48 | Heap *heap = nullptr; 49 | Keyboard *keyboard = nullptr; 50 | }; 51 | 52 | extern DevicesInfo devices; 53 | extern KernelData kernel; 54 | -------------------------------------------------------------------------------- /kernel/src/klog/klog.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Print3M/printOS/eee93510620eafb1efcb1367c26eee92d01568ac/kernel/src/klog/klog.cpp -------------------------------------------------------------------------------- /kernel/src/klog/klog.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Kernel logging system. 3 | */ 4 | #pragma once 5 | #include 6 | 7 | enum class KLogType { 8 | info, 9 | debug, 10 | error, 11 | panic, 12 | }; 13 | 14 | class KLog { 15 | private: 16 | size _num_of_entries; 17 | void *_buff; 18 | char *_free_entry; // Next free entry to write 19 | 20 | public: 21 | KLog(); 22 | const char *get_entry(size id); 23 | void add_entry(KLogType type, char *msg); 24 | }; 25 | -------------------------------------------------------------------------------- /kernel/src/kutils/assertions.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | [[noreturn]] void 5 | __assertion_failed(char const *msg, char const *file, size line, char const *func) { 6 | 7 | // Call PANIC here 8 | kpanic(msg, file, line, func); 9 | 10 | for (;;) 11 | ; 12 | } 13 | -------------------------------------------------------------------------------- /kernel/src/kutils/assertions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | [[noreturn]] void 5 | __assertion_failed(char const *msg, char const *file, size line, char const *func); 6 | 7 | #define ASSERT(expr) \ 8 | do { \ 9 | if (!static_cast(expr)) [[unlikely]] \ 10 | __assertion_failed(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); \ 11 | } while (0) 12 | -------------------------------------------------------------------------------- /kernel/src/kutils/bitmap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | Bitmap::Bitmap(void *__buf, size __size_in_bits) : __buf(__buf) { 7 | // Clear memory for the bitmap 8 | this->__size = (__size_in_bits + 7) / 8; 9 | memset(__buf, 0x00000000, this->__size); 10 | }; 11 | 12 | bool Bitmap::get(size idx) { 13 | // Get bitmap's entry (bit) 14 | auto rest = idx % 8; 15 | auto base = (idx - rest) / 8; 16 | auto result = *(static_cast(this->__buf) + base); 17 | 18 | return result & (0x1 << rest); 19 | } 20 | 21 | void Bitmap::set(size idx) { 22 | // Set to 1 bitmap's entry (bit) 23 | auto rest = idx % 8; 24 | auto base = (idx - rest) / 8; 25 | auto addr = static_cast(this->__buf) + base; 26 | 27 | *addr = set_bit(*addr, rest); 28 | } 29 | 30 | void Bitmap::unset(size idx) { 31 | // Set to 0 bitmap's entry (bit) 32 | auto rest = idx % 8; 33 | auto base = (idx - rest) / 8; 34 | auto addr = static_cast(this->__buf) + base; 35 | 36 | *addr = clear_bit(*addr, rest); 37 | } 38 | 39 | size Bitmap::get_mem_size() { 40 | // Return memory size of the bitmap in bytes 41 | return this->__size; 42 | } -------------------------------------------------------------------------------- /kernel/src/kutils/bitmap.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class Bitmap { 5 | private: 6 | void *__buf; 7 | size __size; // In bytes 8 | 9 | public: 10 | const void *buf = &__buf; // TODO: Does it work? 11 | 12 | public: 13 | Bitmap(void *__buf, size __size); 14 | bool get(size idx); 15 | void set(size idx); 16 | void unset(size idx); 17 | size get_mem_size(); 18 | }; -------------------------------------------------------------------------------- /kernel/src/kutils/bits.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define set_bit(var, pos) ((var) | 1 << pos) 4 | #define clear_bit(var, pos) ((var) & ~(1 << pos)) 5 | #define test_bit(var, pos) ((var) & (1 << pos)) -------------------------------------------------------------------------------- /kernel/src/kutils/console.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void __printf(const char *str, va_list args) { 8 | ASSERT(kernel.console != nullptr); 9 | 10 | // Dynamic list of arguments 11 | char buf[4096]; // Buffer for result string 12 | memset(buf, 0, 4096); 13 | char temp_buf[64]; // 8*8 = 64 bits + 9th byte for '\0'; 14 | memset(temp_buf, 0, 64); 15 | 16 | size len = str_len((char *) str); 17 | size temp_len = 0; 18 | bool is_percent = false; // %x notation detector 19 | 20 | u16 j = 0; 21 | for (u16 i = 0; i < len; i++) { 22 | if (is_percent) { 23 | switch (str[i]) { 24 | case 's': { 25 | // If %s 26 | // Get next argument 27 | char *arg = va_arg(args, char *); 28 | temp_len = str_len(arg); 29 | 30 | // Add string to result buffer 31 | memcpy((void *) (buf + j), (void *) arg, temp_len); 32 | 33 | j += temp_len; 34 | break; 35 | } 36 | // case 'c': { 37 | // // If %c 38 | // uint8_t arg = va_arg(args, uint8_t); 39 | // buf[j] = arg; 40 | // j += 1; 41 | // break; 42 | // } 43 | case 'd': { 44 | // If %d 45 | u64 arg = va_arg(args, u64); 46 | itoa(arg, temp_buf, 10); 47 | temp_len = str_len(temp_buf); 48 | memcpy((void *) (buf + j), (void *) temp_buf, temp_len); 49 | j += temp_len; 50 | break; 51 | } 52 | case 'p': { 53 | // If %p 54 | u64 arg = va_arg(args, u64); 55 | itoa(arg, temp_buf, 16); 56 | temp_len = str_len(temp_buf); 57 | memcpy((void *) (buf + j), (void *) temp_buf, temp_len); 58 | j += temp_len; 59 | break; 60 | } 61 | } 62 | 63 | is_percent = false; 64 | continue; 65 | } else { 66 | if (str[i] == '%') { 67 | is_percent = true; 68 | continue; 69 | } else { 70 | // Add new char 71 | buf[j] = str[i]; 72 | j++; 73 | continue; 74 | } 75 | } 76 | } 77 | 78 | buf[j] = '\0'; 79 | 80 | kernel.console->print(buf); 81 | } 82 | 83 | void kprintf(const char *fmt, ...) { 84 | va_list args; 85 | va_start(args, fmt); 86 | __printf(fmt, args); 87 | va_end(args); 88 | } 89 | 90 | void kprint_err(const char *msg) { 91 | kprintf("[!] Kernel error: %s \n", msg); 92 | } 93 | 94 | void kprint_succ(const char *msg) { 95 | kprintf("[+] Kernel success: %s \n", msg); 96 | } 97 | 98 | void kprint_info(const char *msg) { 99 | kprintf("[*] Kernel info: %s \n", msg); 100 | } 101 | 102 | void kprint_pmem_info() { 103 | kprint_info("physical memory info (provided by the UEFI Memory Map):"); 104 | kprintf("\t all: frames = %d, memory = %dMB (holes and MMIO included) \n", 105 | kernel.pmem->frames, 106 | kernel.pmem->frames * PMemConsts::FRAME_SZ / 1024 / 1024); 107 | kprintf("\t free: frames = %d, memory = %dMB \n", 108 | kernel.pmem->free_frames, 109 | kernel.pmem->free_frames * PMemConsts::FRAME_SZ / 1024 / 1024); 110 | kprintf("\t locked: frames = %d, memory = %dMB \n", 111 | kernel.pmem->locked_frames, 112 | kernel.pmem->locked_frames * PMemConsts::FRAME_SZ / 1024 / 1024); 113 | } 114 | 115 | void kprint_cpu_info() { 116 | kprint_info("CPU info:"); 117 | kprintf("\t - manufacturer: %s \n", devices.cpu.manufacturer); 118 | kprintf("\t - stepping id: %d \n", devices.cpu.stepping_id); 119 | kprintf("\t - family id: %d \n", devices.cpu.family_id); 120 | kprintf("\t - model id: %d \n", devices.cpu.model_id); 121 | kprintf("\t - type: %d \n", devices.cpu.type); 122 | kprintf("\t - brand: %s \n", devices.cpu.brand); 123 | kprintf("\t - logical cores: %d \n", devices.cpu.logical_cores); 124 | kprintf("\t - physical cores: %d \n", devices.cpu.physical_cores); 125 | kprintf("\t - hyperthreading: %d \n", devices.cpu.hyperthreading); 126 | } 127 | 128 | void kprint_heap_info() { 129 | auto stats = kernel.heap->get_heap_stats(); 130 | kprintf("Heap blocks: all = %d, free = %d, allocated: %d\n", 131 | stats.all_blocks, 132 | stats.free_blocks, 133 | stats.allocated_blocks); 134 | } 135 | 136 | void __prepare_kpanic() { 137 | kernel.framebuffer->set_all_pixels(BLUE); 138 | kernel.console->cursor.gotoxy(0, 0); 139 | kernel.console->cursor.set_bg(BLUE); 140 | kernel.console->cursor.set_fg(WHITE); 141 | kprintf("===== Kernel panic ===== \n"); 142 | } 143 | 144 | void kpanic(const char *msg) { 145 | __prepare_kpanic(); 146 | kprintf("Error: %s", msg); 147 | } 148 | 149 | void kpanic(const char *msg, char const *file, size line, char const *func) { 150 | __prepare_kpanic(); 151 | kprintf("Message: %s\n", msg); 152 | kprintf("File: %s\n", file); 153 | kprintf("Line: %d\n", line); 154 | kprintf("Func: %s\n", func); 155 | } -------------------------------------------------------------------------------- /kernel/src/kutils/console.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | void kprint_err(const char *msg); 6 | void kprint_succ(const char *msg); 7 | void kprint_info(const char *msg); 8 | 9 | void kprint_pmem_info(); 10 | void kprint_cpu_info(); 11 | void kprint_heap_info(); 12 | 13 | void kpanic(const char *msg); 14 | void kpanic(const char *msg, char const *file, size line, char const *func); 15 | 16 | void kprintf(const char *fmt, ...); 17 | -------------------------------------------------------------------------------- /kernel/src/libc/cmath.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Print3M/printOS/eee93510620eafb1efcb1367c26eee92d01568ac/kernel/src/libc/cmath.cpp -------------------------------------------------------------------------------- /kernel/src/libc/cmath.hpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Print3M/printOS/eee93510620eafb1efcb1367c26eee92d01568ac/kernel/src/libc/cmath.hpp -------------------------------------------------------------------------------- /kernel/src/libc/stdarg.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef __builtin_va_list va_list; 4 | 5 | #define va_start(v, l) __builtin_va_start(v, l) 6 | #define va_end(v) __builtin_va_end(v) 7 | #define va_arg(v, l) __builtin_va_arg(v, l) -------------------------------------------------------------------------------- /kernel/src/libc/stdint.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define NULL 0 4 | 5 | typedef unsigned char uint8_t; 6 | typedef unsigned char u8; 7 | 8 | typedef signed char int8_t; 9 | typedef signed char i8; 10 | 11 | typedef unsigned short int uint16_t; 12 | typedef unsigned short int u16; 13 | 14 | typedef signed short int int16_t; 15 | typedef signed short int i16; 16 | 17 | typedef unsigned int uint32_t; 18 | typedef unsigned int u32; 19 | 20 | typedef signed int int32_t; 21 | typedef signed int i32; 22 | 23 | typedef unsigned long int uint64_t; 24 | typedef unsigned long int u64; 25 | 26 | typedef signed long int int64_t; 27 | typedef signed long int i64; 28 | 29 | typedef u64 size_t; 30 | typedef u64 size; -------------------------------------------------------------------------------- /kernel/src/libc/stdlib.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | size_t str_len(char *str) { 7 | // Returns string length without '\0' char 8 | size_t count = 0; 9 | 10 | while (str[count] != '\0') { 11 | count++; 12 | } 13 | 14 | return count; 15 | } 16 | 17 | char *str_reverse(char *str, int len) { 18 | char temp; 19 | 20 | for (int i = 0; i < (len / 2); i++) { 21 | // Switch two chars 22 | temp = str[i]; 23 | str[i] = str[len - i - 1]; 24 | str[len - i - 1] = temp; 25 | } 26 | 27 | return str; 28 | } 29 | 30 | void *memcpy(void *dest, void *src, size_t n) { 31 | volatile uint8_t *d = (uint8_t *) dest; 32 | volatile uint8_t *s = (uint8_t *) src; 33 | 34 | while (n--) { 35 | *d++ = *s++; 36 | } 37 | 38 | return dest; 39 | } 40 | 41 | void *memset(void *ptr, u8 v, size n) { 42 | u8 *p = (u8 *) ptr; 43 | 44 | while (n--) { 45 | *p++ = v; 46 | } 47 | 48 | return ptr; 49 | } 50 | 51 | char *itoa(u64 num, char *buf, u8 base) { 52 | // This simple implementation has been copied from: 53 | // https://www.geeksforgeeks.org/implement-itoa/ 54 | // 55 | // How it works: 56 | // 123 => "123\0" 57 | 58 | int i = 0; 59 | 60 | /* Handle 0 explicitely, otherwise empty string is printed for 0 */ 61 | if (num == 0) { 62 | buf[i++] = '0'; 63 | buf[i] = '\0'; 64 | return buf; 65 | } 66 | 67 | // Process individual digits 68 | while (num != 0) { 69 | int rem = num % base; 70 | buf[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0'; 71 | num = num / base; 72 | } 73 | 74 | buf[i] = '\0'; // Append string terminator 75 | 76 | // Reverse the string 77 | buf = str_reverse(buf, i); 78 | 79 | return buf; 80 | } 81 | 82 | uint8_t strcmp(const char *str1, const char *str2) { 83 | while (*str1) { 84 | if (*str1 != *str2) { 85 | break; 86 | } 87 | 88 | str1++; 89 | str2++; 90 | } 91 | 92 | // return the ASCII difference 93 | return *(const uint8_t *) str1 - *(const uint8_t *) str2; 94 | } 95 | 96 | uint8_t strncmp(const char *str1, const char *str2, size_t n) { 97 | while (n && *str1 && (*str1 == *str2)) { 98 | ++str1; 99 | ++str2; 100 | --n; 101 | } 102 | 103 | if (n == 0) { 104 | return 0; 105 | } else { 106 | return *(uint8_t *) str1 - *(uint8_t *) str2; 107 | } 108 | } -------------------------------------------------------------------------------- /kernel/src/libc/stdlib.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | // String operations 5 | size_t str_len(char *str); 6 | char *str_reverse(char *str, int len); 7 | uint8_t strcmp(const char *str1, const char *str2); 8 | uint8_t strncmp(const char *str1, const char *str2, const size_t n); 9 | 10 | // Conversions 11 | char *itoa(uint64_t num, char *buf, uint8_t base); 12 | 13 | // Memory operations 14 | void *memset(void *ptr, uint8_t v, size_t n); 15 | void *memcpy(void *dest, void *src, size_t n); -------------------------------------------------------------------------------- /kernel/src/memory/heap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | Heap::Heap() { 10 | // Initialize memory allocator by allocating one page 11 | auto frame = kernel.pmem->request_frame(); 12 | ASSERT(frame != nullptr); 13 | kernel.paging->allocate_page(frame, frame); 14 | 15 | auto block = static_cast(frame); 16 | block->is_free = true; 17 | block->next = nullptr; 18 | block->prev = nullptr; 19 | block->bytes = PagingConsts::PAGE_SZ - sizeof(BlockHeader); 20 | this->_blocks = block; 21 | } 22 | 23 | BlockHeader *Heap::_find_free_block(size bytes) { 24 | auto block = this->_blocks; 25 | ASSERT(block != nullptr); 26 | 27 | do { 28 | if (block->is_free && block->bytes >= bytes) 29 | return block; 30 | 31 | block = block->next; 32 | } while (block != nullptr); 33 | 34 | return nullptr; 35 | } 36 | 37 | inline void *get_block_data_addr(BlockHeader *block) { 38 | return static_cast((u8 *) block + sizeof(BlockHeader)); 39 | } 40 | 41 | inline size get_total_block_size(BlockHeader *block) { 42 | /* 43 | Total size = block header + data 44 | */ 45 | return sizeof(BlockHeader) + block->bytes; 46 | } 47 | 48 | inline bool are_blocks_contiguous(BlockHeader *block, BlockHeader *next_block) { 49 | if (block == nullptr || next_block == nullptr) 50 | return false; 51 | 52 | auto addr = reinterpret_cast(block) + get_total_block_size(block); 53 | 54 | return reinterpret_cast(addr) == next_block; 55 | } 56 | 57 | BlockHeader *Heap::_find_block_close_before_addr(void *addr) { 58 | auto block = this->_blocks; 59 | size current_block_addr = 0; 60 | size next_block_addr = 0; 61 | size find_block_addr = reinterpret_cast(addr); 62 | 63 | while (block != nullptr) { 64 | 65 | // Last block, there's no other chance 66 | if (block->next == nullptr) { 67 | return block; 68 | } 69 | 70 | current_block_addr = reinterpret_cast(block); 71 | next_block_addr = reinterpret_cast(block->next); 72 | 73 | // If the wanted address is between two blocks 74 | if (current_block_addr < find_block_addr && next_block_addr < find_block_addr) { 75 | return block; 76 | } 77 | 78 | block = block->next; 79 | } 80 | 81 | ASSERT(1 == 2); 82 | return nullptr; 83 | } 84 | 85 | void *Heap::malloc(size bytes) { 86 | ASSERT(bytes > 0); 87 | auto block = this->_find_free_block(bytes); 88 | 89 | // No sufficient free block have been found. 90 | // Allocate new pages, create new block and add it to linked-list of blocks. 91 | if (block == nullptr) { 92 | auto pages = (bytes / PagingConsts::PAGE_SZ) + 1; 93 | auto init_block = kernel.pmem->request_frames(pages); 94 | 95 | for (size i = 0; i < pages; i++) { 96 | auto page = static_cast((u8 *) init_block + (i * PagingConsts::PAGE_SZ)); 97 | kernel.paging->allocate_page(page, page); 98 | } 99 | 100 | block = reinterpret_cast(init_block); 101 | block->bytes = (pages * PagingConsts::PAGE_SZ) - sizeof(BlockHeader); 102 | block->is_free = true; 103 | auto prev_block = this->_find_block_close_before_addr(static_cast(block)); 104 | block->next = prev_block->next; 105 | block->prev = prev_block; 106 | prev_block->next = block; 107 | 108 | if (block->next != nullptr) { 109 | block->next->prev = block; 110 | } 111 | 112 | block = this->_find_free_block(bytes); 113 | ASSERT(block != nullptr); 114 | } 115 | 116 | // Free block has exactly the amount of space that we want 117 | // or the rest of the space is too small for further fragmentation 118 | if (block->bytes == bytes || block->bytes - bytes <= sizeof(BlockHeader)) { 119 | block->is_free = false; 120 | 121 | return get_block_data_addr(block); 122 | } 123 | 124 | // Free block has way more free space than we want and it's ready 125 | // to be fragmented. 126 | // 127 | // Linked-list structure before: 128 | // | block | next | 129 | // After: 130 | // | block | new_block | next | 131 | block->is_free = false; 132 | auto new_block = reinterpret_cast((u8 *) get_block_data_addr(block) + bytes); 133 | new_block->is_free = true; 134 | new_block->bytes = block->bytes - bytes - sizeof(BlockHeader); 135 | 136 | ASSERT(new_block->bytes > 0); 137 | 138 | if (block->next != nullptr) { 139 | block->next->prev = new_block; 140 | } 141 | 142 | new_block->next = block->next; 143 | new_block->prev = block; 144 | block->bytes = bytes; 145 | block->next = new_block; 146 | 147 | return get_block_data_addr(block); 148 | } 149 | 150 | void Heap::free(void *ptr) { 151 | if (ptr == nullptr) 152 | return; 153 | 154 | auto block = reinterpret_cast((u8 *) ptr - sizeof(BlockHeader)); 155 | block->is_free = true; 156 | 157 | // Merge with next block (if blocks are contiguous): 158 | // The next block is destroyed and its total size is 159 | // absorbed into the current block. 160 | if (block->next != nullptr && block->next->is_free && 161 | are_blocks_contiguous(block, block->next)) { 162 | 163 | // Skip next block 164 | block->bytes += get_total_block_size(block->next); 165 | block->next = block->next->next; 166 | 167 | // Set .prev of next block 168 | if (block->next != nullptr) { 169 | block->next->prev = block; 170 | } 171 | } 172 | 173 | // Merge with prev block (if blocks are contiguous): 174 | // The current block is destroyed and its total size is 175 | // absorbed into the prev block. 176 | if (block->prev != nullptr && block->prev->is_free && 177 | are_blocks_contiguous(block->prev, block)) { 178 | 179 | // Skip current block 180 | block->prev->bytes += get_total_block_size(block); 181 | block->prev->next = block->next; 182 | 183 | // Set .prev of next block 184 | block->next->prev = block->prev; 185 | } 186 | } 187 | 188 | HeapStats Heap::get_heap_stats() { 189 | auto block = this->_blocks; 190 | HeapStats stats = { 191 | .all_blocks = 0, 192 | .free_blocks = 0, 193 | .allocated_blocks = 0, 194 | }; 195 | 196 | while (block != nullptr) { 197 | stats.all_blocks++; 198 | 199 | if (block->is_free) { 200 | stats.free_blocks++; 201 | } else { 202 | stats.allocated_blocks++; 203 | } 204 | 205 | block = block->next; 206 | } 207 | 208 | return stats; 209 | } -------------------------------------------------------------------------------- /kernel/src/memory/heap.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | enum AllocedFlag { 5 | FREE = 0b00000000, 6 | ALLOCATED = 0b00000001, 7 | }; 8 | 9 | struct BlockHeader { 10 | BlockHeader *prev; 11 | BlockHeader *next; 12 | bool is_free; 13 | size bytes; 14 | }; 15 | 16 | struct HeapStats { 17 | size all_blocks; 18 | size free_blocks; 19 | size allocated_blocks; 20 | }; 21 | 22 | class Heap { 23 | private: 24 | // Double-linked-list of allocated blocks 25 | BlockHeader *_blocks = nullptr; 26 | 27 | public: 28 | Heap(); 29 | void *malloc(size bytes); 30 | void free(void *ptr); 31 | HeapStats get_heap_stats(); 32 | 33 | private: 34 | void _free_block(); 35 | BlockHeader *_find_free_block(size bytes); 36 | BlockHeader *_find_block_close_before_addr(void *addr); 37 | }; -------------------------------------------------------------------------------- /kernel/src/memory/mmap.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | MemoryMap::MemoryMap(MemoryData &data) { 8 | ASSERT(data.entries > 0); 9 | 10 | this->__desc_sz = data.desc_sz; 11 | this->__entries = data.entries; 12 | this->__mmap_sz = data.mmap_sz; 13 | this->mmap = data.mmap; 14 | } 15 | 16 | EfiMMapDesc *MemoryMap::get_desc(size idx) { 17 | // Get Memory Map Descriptor of index 18 | return (EfiMMapDesc *) ((u8 *) this->mmap + (idx * this->__desc_sz)); 19 | } 20 | 21 | size MemoryMap::get_page_frames_number() { 22 | // Get total number of pages in memory (based on memory map) 23 | // but holes are included as well. 24 | // It's described in UEFI Specification "EFI_BOOT_SERVICES.GetMemoryMap()" 25 | // that memory map might not be contigoues. Bitmap in Physical Memory 26 | // Allocator should keep number of all pages (even if there is a hole). 27 | return this->__get_entire_memory_size() / PMemConsts::FRAME_SZ; 28 | } 29 | 30 | size MemoryMap::get_largest_free_seg(void **mem_seg) { 31 | // Return - size of largest free memory segment 32 | // OUT mem_seg - largest free memory segment 33 | size max_sz = 0; 34 | 35 | for (u64 i = 0; i < kernel.memory->entries; i++) { 36 | auto desc = kernel.mmap->get_desc(i); 37 | 38 | if (desc->type == (u32) EfiMemoryType::EFI_CONVENTIONAL_MEMORY) { 39 | size seg_sz = desc->pages * PMemConsts::FRAME_SZ; 40 | 41 | if (seg_sz > max_sz) { 42 | max_sz = seg_sz; 43 | *mem_seg = desc->paddr; 44 | } 45 | } 46 | } 47 | 48 | return max_sz; 49 | } 50 | 51 | size MemoryMap::__get_entire_memory_size() { 52 | // Return size of the memory (with holes and unavailable spaces) in bytes. 53 | auto last_desc = this->get_desc(this->__entries - 1); 54 | 55 | // size of memory = (paddr of last page + last page size) 56 | return reinterpret_cast(last_desc->paddr) + (PMemConsts::FRAME_SZ * last_desc->pages) + 57 | PMemConsts::FRAME_SZ; 58 | } 59 | 60 | void MemoryMap::dbg_print_mmap() { 61 | kprintf("Memory map (all entries: %d)", this->entries); 62 | 63 | for (size i = 0; i < this->entries; i++) { 64 | auto desc = this->get_desc(i); 65 | kprintf("%p-%p: type=%d, frames=%d \n", 66 | desc->paddr, 67 | reinterpret_cast(desc->paddr) + (desc->pages * PMemConsts::FRAME_SZ), 68 | desc->type, 69 | desc->pages); 70 | } 71 | } 72 | 73 | bool MemoryMap::is_usable_memory(EfiMMapDesc *desc) { 74 | // Check if EFI memory segment can be used by kernel. Only these memory 75 | // segments can be used as standard RAM! 76 | return (desc->type == (u32) EfiMemoryType::EFI_CONVENTIONAL_MEMORY || 77 | desc->type == (u32) EfiMemoryType::EFI_BOOT_SERVICES_CODE || 78 | desc->type == (u32) EfiMemoryType::EFI_BOOT_SERVICES_DATA); 79 | } -------------------------------------------------------------------------------- /kernel/src/memory/mmap.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | struct __attribute__((packed)) EfiMMapDesc { 5 | // EFI Memory Map Descriptor 6 | u32 type; 7 | u32 pad; 8 | void *paddr; // Physical address 9 | void *vaddr; // Virtual address 10 | u64 pages; // Number of pages 11 | u64 attr; 12 | }; 13 | 14 | struct MemoryData { 15 | EfiMMapDesc *mmap; 16 | size mmap_sz; 17 | size desc_sz; 18 | size entries; 19 | }; 20 | 21 | enum class EfiMemoryType { 22 | EFI_RESERVED_MEMORY_TYPE, 23 | EFI_LOADER_CODE, 24 | EFI_LOADER_DATA, 25 | EFI_BOOT_SERVICES_CODE, 26 | EFI_BOOT_SERVICES_DATA, 27 | EFI_RUNTIME_SERVICES_CODE, 28 | EFI_RUNTIME_SERVICES_DATA, 29 | EFI_CONVENTIONAL_MEMORY, 30 | EFI_UNUSABLE_MEMORY, 31 | EFI_ACPI_RECLAIM_MEMORY, 32 | EFI_ACPI_MEMORY_NVS, 33 | EFI_MEMORY_MAPPED_IO, 34 | EFI_MEMORY_MAPPED_IO_PORT_SPACE, 35 | EFI_PAL_CODE, 36 | EFI_MAX_MEMORY_TYPE 37 | }; 38 | 39 | class MemoryMap { 40 | private: 41 | size __mmap_sz; // Memory map size 42 | size __desc_sz; // Memory descriptor size 43 | size __entries; // Number of mmap entries 44 | 45 | public: 46 | const size &entries = __entries; 47 | EfiMMapDesc *mmap; // Memory Map TODO: Is it still valid after exiting UEFI services? 48 | 49 | private: 50 | size __get_entire_memory_size(); 51 | 52 | public: 53 | MemoryMap(MemoryData &data); 54 | EfiMMapDesc *get_desc(size idx); 55 | size get_page_frames_number(); 56 | size get_largest_free_seg(void **mem_seg); 57 | bool is_usable_memory(EfiMMapDesc *desc); 58 | void dbg_print_mmap(); 59 | }; -------------------------------------------------------------------------------- /kernel/src/memory/paging.asm: -------------------------------------------------------------------------------- 1 | [bits 64] 2 | 3 | section .text: 4 | global enable_cpu_4_level_paging 5 | 6 | enable_cpu_4_level_paging: 7 | ; void enable_cpu_4_level_paging() 8 | 9 | ; cr0.PG = 1 10 | ; Enable paging 11 | mov rax, cr0 12 | or rax, (1 << 31) 13 | mov cr0, rax 14 | 15 | ; cr4.PAE = 1 16 | mov rax, cr4 17 | or rax, (1 << 5) 18 | mov cr4, rax 19 | 20 | ; cr4.LA57 = 0 21 | ; Disable 57-bit linear addressing mode (5-level paging). 22 | mov rax, cr4 23 | and rax, ~(1 << 57) 24 | mov cr4, rax 25 | 26 | xor rax, rax 27 | ret 28 | -------------------------------------------------------------------------------- /kernel/src/memory/paging.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | extern "C" void enable_cpu_4_level_paging(); 14 | 15 | void set_cr3_register(size paddr, bool pwt, bool pcd) { 16 | /* 17 | IN :paddr - physical address of first paging-translation struct 18 | IN :pwt - page-level write-through 19 | IN :pcd - page-level cache disable 20 | 21 | More about what is going on here: 22 | IM3, chapter: 4.5 - 4-level paging 23 | */ 24 | u64 cr3 = paddr; 25 | 26 | if (pwt) 27 | cr3 = set_bit(cr3, CR3_PWT); 28 | if (pcd) 29 | cr3 = set_bit(cr3, CR3_PCD); 30 | 31 | __asm__ volatile("mov %0, %%cr3" ::"r"(cr3)); 32 | } 33 | 34 | PageIndexer::PageIndexer(u64 vaddr) { 35 | /* 36 | Virtual address schema (48-bits in total): 37 | | 111111111 | 111111111 | 111111111 | 111111111 | 111111111111 38 | PML4 PDPT PD PT OFFSET 39 | 9 bits 9-bits 9 bits 9 bits 12 bits 40 | */ 41 | 42 | vaddr >>= PAGE_OFFSET_BITS; // Remove first 12 bytes (page offset) 43 | // - 44 | this->__pt = vaddr & BITS_9_MASK; // PageTable entry 45 | vaddr >>= 9; // - 46 | this->__pd = vaddr & BITS_9_MASK; // PageDirectory entry 47 | vaddr >>= 9; // - 48 | this->__pdpt = vaddr & BITS_9_MASK; // PageDirectoryPointerTable entry 49 | vaddr >>= 9; // - 50 | this->__pml4 = vaddr & BITS_9_MASK; // PageMapLevel4 entry 51 | } 52 | 53 | Paging::Paging() { 54 | /* 55 | IM3: 4.1 - Paging modes and control bits 56 | Software enables paging by using the MOV to CR0 instruction to set CR0.PG. 57 | Before doing so, software should ensure that control register CR3 contains 58 | the physical address of the first paging structure that the processor will 59 | use for linear-address translation (see Section 4.2) and that structure is 60 | initialized as desired. 61 | */ 62 | 63 | // Clear space for a page structure 64 | memset((void *) this->PML4, 0x0, sizeof(PML4_Entry) * 512); 65 | 66 | // IM3: 4.1.1 - specific options to set in order to get 4-level paging enabled 67 | enable_cpu_4_level_paging(); 68 | 69 | // Set IA32_EFER.LME bit 70 | u64 msr = cpu::read_msr(IA32_EFER); 71 | cpu::write_msr(IA32_EFER, set_bit(msr, EFER_LME)); 72 | this->init(); 73 | 74 | // Load PML4 structure -> enable paging 75 | set_cr3_register((u64) PML4, false, false); 76 | 77 | // Test paging abilities 78 | size test_addr = 0x99999999999; // Way more than physical memory 79 | u8 *test_addr_ptr = (u8 *) test_addr; 80 | this->allocate_page((void *) test_addr, (void *) 0x0); 81 | *test_addr_ptr = 123; 82 | 83 | if (*test_addr_ptr != 123) { 84 | kpanic("Paging test failed!"); 85 | } 86 | } 87 | 88 | bool Paging::is_page_allocated(void *vaddr) { 89 | // Check if vaddr has its page present in the page table 90 | auto index = PageIndexer((u64) vaddr); 91 | 92 | PML4_Entry *pml4_entry = &PML4[index.pml4]; 93 | if (pml4_entry->present == false) { 94 | return false; 95 | } 96 | 97 | PDPT_Entry *PDPT = reinterpret_cast(pml4_entry->pdpt_addr << PAGE_OFFSET_BITS); 98 | PDPT_Entry *pdpt_entry = &PDPT[index.pdpt]; 99 | if (pdpt_entry->present == false) { 100 | return false; 101 | } 102 | 103 | PD_Entry *PD = reinterpret_cast(pdpt_entry->pd_addr << PAGE_OFFSET_BITS); 104 | PD_Entry *pd_entry = &PD[index.pd]; 105 | if (pd_entry->present == false) { 106 | return false; 107 | } 108 | 109 | PT_Entry *PT = reinterpret_cast(pd_entry->pt_addr << PAGE_OFFSET_BITS); 110 | PT_Entry *pt_entry = &PT[index.pt]; 111 | return pt_entry->present; 112 | } 113 | 114 | void Paging::allocate_page(void *vaddr, void *paddr) { 115 | // Go down the paging-structure (by :vaddr) to the page and set its :paddr 116 | // TODO: What if pmem_request_frame returns addr higher than 44 bits (32 + PAGE_OFFSET)? 117 | 118 | auto index = PageIndexer((u64) vaddr); 119 | 120 | /* 121 | PML4 122 | */ 123 | PML4_Entry *pml4_entry = &PML4[index.pml4]; 124 | PDPT_Entry *PDPT; 125 | if (pml4_entry->present == false) { 126 | // If not already present, create PDPT 127 | PDPT = (PDPT_Entry *) kernel.pmem->request_frame(); 128 | ASSERT(PDPT != nullptr); 129 | memset(PDPT, 0, PagingConsts::PAGE_SZ); 130 | 131 | // Set PML4 entry 132 | pml4_entry->pdpt_addr = (u64) PDPT >> PAGE_OFFSET_BITS; 133 | pml4_entry->present = true; 134 | pml4_entry->rw = true; 135 | } else { 136 | PDPT = reinterpret_cast(pml4_entry->pdpt_addr << PAGE_OFFSET_BITS); 137 | } 138 | 139 | /* 140 | PDPT 141 | */ 142 | PDPT_Entry *pdpt_entry = &PDPT[index.pdpt]; 143 | PD_Entry *PD; 144 | if (pdpt_entry->present == false) { 145 | // If PDPT entry not present, create PD and set PDPT entry 146 | PD = (PD_Entry *) kernel.pmem->request_frame(); 147 | ASSERT(PD != nullptr); 148 | memset(PD, 0, PagingConsts::PAGE_SZ); 149 | 150 | // Set PDPT entry 151 | pdpt_entry->pd_addr = (u64) PD >> PAGE_OFFSET_BITS; 152 | pdpt_entry->present = true; 153 | pdpt_entry->rw = true; 154 | } else { 155 | PD = reinterpret_cast((pdpt_entry->pd_addr << PAGE_OFFSET_BITS)); 156 | } 157 | 158 | /* 159 | PD 160 | */ 161 | PD_Entry *pd_entry = &PD[index.pd]; 162 | PT_Entry *PT; 163 | if (pd_entry->present == false) { 164 | // If PD entry not present, create PT and set PD entry 165 | PT = (PT_Entry *) kernel.pmem->request_frame(); 166 | ASSERT(PT != nullptr); 167 | memset(PT, 0, PagingConsts::PAGE_SZ); 168 | 169 | // Set PD entry 170 | pd_entry->pt_addr = (u64) PT >> PAGE_OFFSET_BITS; 171 | pd_entry->present = true; 172 | pd_entry->rw = true; 173 | } else { 174 | PT = reinterpret_cast(pd_entry->pt_addr << PAGE_OFFSET_BITS); 175 | } 176 | 177 | /* 178 | PT 179 | */ 180 | PT_Entry *pt_entry = &PT[index.pt]; 181 | pt_entry->page_addr = (u64) paddr >> PAGE_OFFSET_BITS; 182 | pt_entry->present = true; 183 | pt_entry->rw = true; 184 | } 185 | 186 | void Paging::init() { 187 | // Identity paging for the entire memory physically installed in the system. 188 | // Just iterate over memory map and map every segment. Holes are not present 189 | // in the memory map - only physically present memory. 190 | for (size i = 0; i < kernel.mmap->entries; i++) { 191 | auto desc = kernel.mmap->get_desc(i); 192 | this->__identity_paging(desc->paddr, PMemConsts::FRAME_SZ * desc->pages); 193 | } 194 | 195 | // Identity paging for framebuffer 196 | this->__identity_paging(kernel.framebuffer->get_fb_addr(), kernel.framebuffer->b_size); 197 | 198 | // Identity paging for Local APIC 199 | this->__identity_paging(reinterpret_cast(kernel.acpi_tables.madt->lapic_addr), 4096); 200 | 201 | // Identity paging for I/O APIC 202 | this->__identity_paging(reinterpret_cast(kernel.acpi_tables.ioapic->io_apic_addr), 8); 203 | 204 | // Identity paging for HPET 205 | this->__identity_paging(reinterpret_cast(kernel.acpi_tables.hpet->base_address.address), 206 | hpet::get_mmio_sz()); 207 | } 208 | 209 | void Paging::__identity_paging(void *addr, size n) { 210 | // Perform identity paging (1:1) page frame -> virtual page 211 | // NOTICE: :n will be always rounded up to full page size 212 | // :addr - start address of identity paging area 213 | // :n - number of bytes 214 | 215 | for (size i = 0; i < (n / (PagingConsts::PAGE_SZ + 1)) + 1; i++) { 216 | this->allocate_page(addr, addr); 217 | addr = reinterpret_cast((size) addr + PagingConsts::PAGE_SZ); 218 | } 219 | } -------------------------------------------------------------------------------- /kernel/src/memory/paging.hpp: -------------------------------------------------------------------------------- 1 | /* More about paging on x86 in IM3: 2 | 4.1.1 - specific options for each mode of paging 3 | 4.2 - overview of paging, description of translation 4 | 4.5 - specific 4-level paging documentation 5 | */ 6 | #pragma once 7 | #include 8 | #include 9 | #include 10 | 11 | enum PagingConsts { 12 | PAGE_SZ = PMemConsts::FRAME_SZ 13 | }; 14 | 15 | #define IA32_EFER 0xC0000080 16 | 17 | #define EFER_LME 8 18 | #define CR3_PWT 3 19 | #define CR3_PCD 4 20 | #define BITS_9_MASK 0x1ff 21 | #define PAGE_OFFSET_BITS 12 22 | 23 | /* 24 | Every paging structure is 4096 Bytes in size and contains a 25 | number of individual entries. With the 4-level paging modes, 26 | each entry is 64 bits (8 bytes); there are thus 512 entries 27 | in each structure. 4-level paging translates 48-bit linear 28 | addresses to 52-bit physical addresses. Although 52 bits 29 | corresponds to 4 PBytes, linear addresses are limited to 48 bits; 30 | at most 256 TBytes of linear-address space may be accessed at 31 | any given time. 4-level paging translates linear addresses using 32 | a hierarchy of in-memory paging structures located using the 33 | contents of CR3, which is used to locate the first paging-structure. 34 | */ 35 | 36 | struct __attribute__((packed)) PT_Entry { 37 | // Description: IM3, Table 4-20 38 | size present : 1; 39 | size rw : 1; 40 | size us : 1; 41 | size pwt : 1; 42 | size pcd : 1; 43 | size a : 1; 44 | size d : 1; 45 | size pat : 1; 46 | size g : 1; 47 | size ignored1 : 3; 48 | size page_addr : 32; 49 | size reserved1 : 8; 50 | size ignored3 : 7; 51 | size protection_key : 4; 52 | size xd : 1; 53 | }; // PageTable 54 | 55 | struct __attribute__((packed)) PD_Entry { 56 | // Description: IM3, Table 4-19 57 | size present : 1; 58 | size rw : 1; 59 | size us : 1; 60 | size pwt : 1; 61 | size pcd : 1; 62 | size a : 1; 63 | size ignored1 : 1; 64 | size ps : 1; 65 | size ignored2 : 4; 66 | size pt_addr : 32; 67 | size reserved1 : 8; 68 | size ignored3 : 11; 69 | size xd : 1; 70 | }; // PageDirectory 71 | 72 | struct __attribute__((packed)) PDPT_Entry { 73 | // Description: IM3, Table 4-17 74 | size present : 1; 75 | size rw : 1; 76 | size us : 1; 77 | size pwt : 1; 78 | size pcd : 1; 79 | size a : 1; 80 | size ignored1 : 1; 81 | size ps : 1; 82 | size ignored2 : 4; 83 | size pd_addr : 32; 84 | size reserved1 : 8; 85 | size ignored3 : 11; 86 | size xd : 1; 87 | }; // PageDirectoryPointerTable 88 | 89 | struct __attribute__((packed)) PML4_Entry { 90 | // Description: IM3, Table 4-15 91 | size present : 1; 92 | size rw : 1; 93 | size us : 1; 94 | size pwt : 1; 95 | size pcd : 1; 96 | size a : 1; 97 | size ignored1 : 1; 98 | size ps : 1; 99 | size ignored2 : 4; 100 | size pdpt_addr : 32; 101 | size reserved1 : 8; 102 | size ignored3 : 11; 103 | size xd : 1; 104 | }; // PageMapLevel4 105 | 106 | class Paging { 107 | private: 108 | PML4_Entry __attribute__((aligned(0x1000))) PML4[512]; 109 | 110 | public: 111 | Paging(); 112 | bool is_page_allocated(void *vaddr); 113 | void allocate_page(void *vaddr, void *paddr); 114 | 115 | private: 116 | void init(); 117 | void __identity_paging(void *addr, size_t n); 118 | }; 119 | 120 | class PageIndexer { 121 | private: 122 | u16 __pml4; // PageMapLevel4 123 | u16 __pdpt; // PageDirectoryPointerTable 124 | u16 __pd; // PageDirectory 125 | u16 __pt; // PageTable 126 | 127 | public: 128 | const u16 &pml4 = __pml4; 129 | const u16 &pdpt = __pdpt; 130 | const u16 &pd = __pd; 131 | const u16 &pt = __pt; 132 | 133 | public: 134 | PageIndexer(u64 vaddr); 135 | }; -------------------------------------------------------------------------------- /kernel/src/memory/pmem.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | Bitmap PMem::__init_bitmap() { 11 | void *free_mem = nullptr; 12 | auto free_mem_sz = kernel.mmap->get_largest_free_seg(&free_mem); 13 | ASSERT(free_mem_sz > 0); 14 | 15 | auto page_frames = kernel.mmap->get_page_frames_number(); 16 | 17 | ASSERT(page_frames > 0); 18 | // ERROR: Space for bitmap is to small 19 | ASSERT(free_mem_sz > page_frames / 8); 20 | 21 | return Bitmap(free_mem, page_frames); 22 | } 23 | 24 | PMem::PMem() { 25 | /* 26 | Array of frames is based on a memory map recived from the UEFI. 27 | There might be holes like MMIO. 28 | 29 | Note: It is not just the available memory. If the system actually has 256MB of 30 | memory, here we will still have the number of frames rounded up to 4GB. Empty spaces will 31 | simply be locked as they do not exist in the Memory Map provided by UEFI. MMIO spaces will 32 | also be locked. Therefore, the free frames number will be around 250 MB - the amount of 33 | usable memory actually installed in the machine. 34 | */ 35 | this->__frames = kernel.mmap->get_page_frames_number(); 36 | this->__free_frames = this->__frames; 37 | this->__locked_frames = 0; 38 | 39 | ASSERT(this->__frames > 0); 40 | 41 | // Lock all frames at the beggining 42 | for (size i = 0; i < this->__frames; i++) { 43 | this->lock_frame(i); 44 | } 45 | 46 | // Initialize frames by memory map 47 | for (size i = 0; i < kernel.mmap->entries; i++) { 48 | auto desc = kernel.mmap->get_desc(i); 49 | 50 | if (kernel.mmap->is_usable_memory(desc)) { 51 | 52 | // Free frames that can be used 53 | for (size n = 0; n < desc->pages; n++) { 54 | auto idx = this->ptr_to_frame_idx(static_cast(desc->paddr) + 55 | (n * PMemConsts::FRAME_SZ)); 56 | this->free_frame(idx); 57 | } 58 | } 59 | } 60 | 61 | // TODO: Lock frames with kernel itself !!! 62 | 63 | // Lock NULL pointer 64 | this->lock_frame(0); 65 | 66 | // Lock frame with stack itself 67 | auto stack_frame = this->ptr_to_frame_idx(kernel.stack); 68 | this->lock_frame(stack_frame); 69 | 70 | // Lock frames where the bitmap is placed 71 | auto start_frame_idx = this->ptr_to_frame_idx(this->__bitmap.buf); 72 | for (size i = 0; i < this->__bitmap.get_mem_size() / PMemConsts::FRAME_SZ; i++) { 73 | this->lock_frame(start_frame_idx + i); 74 | } 75 | 76 | ASSERT(this->locked_frames > 0); 77 | ASSERT(this->free_frames > 0); 78 | ASSERT(this->__bitmap.get(0) == true); 79 | } 80 | 81 | void PMem::free_frame(size idx) { 82 | // If is locked -> free 83 | if (this->__bitmap.get(idx) == true) { 84 | this->__bitmap.unset(idx); 85 | this->__free_frames++; 86 | this->__locked_frames--; 87 | } 88 | } 89 | 90 | void PMem::lock_frame(size idx) { 91 | // If is free -> lock 92 | if (this->__bitmap.get(idx) == false) { 93 | this->__bitmap.set(idx); 94 | this->__free_frames--; 95 | this->__locked_frames++; 96 | } 97 | } 98 | 99 | inline size PMem::ptr_to_frame_idx(const void *ptr) { 100 | auto intptr = reinterpret_cast(ptr); 101 | 102 | return intptr / PMemConsts::FRAME_SZ; 103 | } 104 | 105 | inline void *PMem::idx_to_frame_ptr(size idx) { 106 | return reinterpret_cast(idx * PMemConsts::FRAME_SZ); 107 | } 108 | 109 | void *PMem::request_frame() { 110 | ASSERT(this->__free_frames > 0); 111 | 112 | for (size i = this->__last_free_idx; i < this->__frames; i++) { 113 | if (this->__bitmap.get(i) == false) { 114 | this->lock_frame(i); 115 | this->__last_free_idx = i; 116 | 117 | return this->idx_to_frame_ptr(i); 118 | } 119 | } 120 | 121 | return nullptr; 122 | } 123 | 124 | void *PMem::request_frames(size number) { 125 | ASSERT(number > 0); 126 | 127 | size free_start_idx = 0; 128 | size free_end_idx = 0; 129 | 130 | for (size i = this->__last_free_idx; i < this->__frames; i++) { 131 | 132 | // Enough frames found, exit the loop 133 | if (free_start_idx > 0 && i - free_start_idx == number) { 134 | free_end_idx = i - 1; 135 | break; 136 | } 137 | 138 | auto current_frame_state = this->__bitmap.get(i); 139 | 140 | // Increment search state 141 | if (free_start_idx > 0 && !current_frame_state) { 142 | free_end_idx = i; 143 | continue; 144 | } 145 | 146 | // Reset search state 147 | if (current_frame_state) { 148 | free_start_idx = 0; 149 | continue; 150 | } 151 | 152 | // Init search state 153 | if (free_start_idx == 0) { 154 | free_start_idx = i; 155 | free_end_idx = i; 156 | continue; 157 | } 158 | } 159 | 160 | ASSERT(free_start_idx > 0); 161 | ASSERT(free_end_idx > 0); 162 | ASSERT(free_start_idx - free_end_idx >= number); 163 | 164 | kprintf("%d, %d\n", free_start_idx, free_end_idx); 165 | 166 | // Lock all frames 167 | for (size i = free_start_idx; i <= free_end_idx; i++) { 168 | this->lock_frame(i); 169 | } 170 | 171 | return this->idx_to_frame_ptr(free_start_idx); 172 | } -------------------------------------------------------------------------------- /kernel/src/memory/pmem.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | enum PMemConsts { 7 | FRAME_SZ = 4096, 8 | }; 9 | 10 | class PMem { 11 | private: 12 | size __last_free_idx = 0; 13 | size __free_frames; // Number of free frames 14 | size __locked_frames; // Number of locked frames 15 | size __frames; // Total amount of frames 16 | Bitmap __bitmap = __init_bitmap(); // Frames bitmap 17 | 18 | public: 19 | const size &frames = __frames; 20 | const size &locked_frames = __locked_frames; 21 | const size &free_frames = __free_frames; 22 | 23 | private: 24 | Bitmap __init_bitmap(); 25 | 26 | public: 27 | PMem(); 28 | 29 | void lock_frame(size idx); 30 | void free_frame(size idx); 31 | u8 is_frame_locked(size idx); 32 | 33 | void *request_frame(); 34 | void *request_frames(size number); 35 | 36 | static void *idx_to_frame_ptr(size idx); 37 | static size ptr_to_frame_idx(const void *mem_ptr); 38 | }; -------------------------------------------------------------------------------- /kernel/src/pic/pic.asm: -------------------------------------------------------------------------------- 1 | [bits 64] 2 | 3 | section .text: 4 | global __disable_pic 5 | 6 | __disable_pic: 7 | ; Source: https://wiki.osdev.org/8259_PIC 8 | mov al, 0xff 9 | out 0xa1, al 10 | out 0x21, al 11 | ret 12 | -------------------------------------------------------------------------------- /kernel/src/pic/pic.cpp: -------------------------------------------------------------------------------- 1 | 2 | namespace pic { 3 | 4 | extern "C" void __disable_pic(void); 5 | 6 | void disable() { 7 | __disable_pic(); 8 | } 9 | 10 | } // namespace pic -------------------------------------------------------------------------------- /kernel/src/pic/pic.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | PIC - Programmable Interrupt Controller 3 | Legacy device which is replaced by APIC. 4 | 5 | It should be disabled to use Local APIC and I/O APIC. 6 | https://wiki.osdev.org/8259_PIC 7 | */ 8 | 9 | namespace pic { 10 | 11 | void disable(); 12 | 13 | } -------------------------------------------------------------------------------- /ovmf/OVMF_CODE.fd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Print3M/printOS/eee93510620eafb1efcb1367c26eee92d01568ac/ovmf/OVMF_CODE.fd -------------------------------------------------------------------------------- /ovmf/OVMF_VARS.fd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Print3M/printOS/eee93510620eafb1efcb1367c26eee92d01568ac/ovmf/OVMF_VARS.fd -------------------------------------------------------------------------------- /recompile.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ueo pipefail 4 | 5 | SCRIPT=$(dirname "$0") 6 | 7 | function compile_all() { 8 | make -C "$SCRIPT/kernel" 9 | make -C "$SCRIPT/bootloader" 10 | } 11 | 12 | function clean_all() { 13 | make -C "$SCRIPT/kernel" clean 14 | make -C "$SCRIPT/bootloader" clean 15 | } 16 | 17 | clean_all 18 | compile_all 19 | 20 | echo -e "\n[+] Kernel & bootloader recompiled successfully!" -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | SCRIPT=$(dirname "$0") 4 | OVMF_DIR="$SCRIPT/ovmf" 5 | 6 | qemu-system-x86_64 \ 7 | -drive format=raw,file=os-image.hdd \ 8 | -m 256M \ 9 | -smp 1,cores=1,threads=1 \ 10 | -drive format=raw,if=pflash,unit=0,file="$OVMF_DIR/OVMF_CODE.fd",readonly=on \ 11 | -drive format=raw,if=pflash,unit=1,file="$OVMF_DIR/OVMF_VARS.fd" \ 12 | -net none \ 13 | -monitor stdio \ 14 | -no-reboot \ 15 | # -d int 16 | --------------------------------------------------------------------------------