From edf9e71e2a7b6b89775c29cf28c19c6b89992c25 Mon Sep 17 00:00:00 2001 From: aqua Date: Mon, 28 Mar 2022 20:03:38 +0300 Subject: Initial commit x86 kernel that prints a hello world message to com1 --- .clang-format | 11 ++ .hgignore | 3 + README.md | 4 + arch/i686/boot.S | 89 ++++++++++ arch/i686/gdt.c | 20 +++ arch/i686/include/gdt.h | 60 +++++++ arch/i686/include/paging.h | 59 +++++++ arch/i686/include/sys/io.h | 79 +++++++++ arch/i686/init.s | 42 +++++ arch/i686/lgdt.c | 31 ++++ arch/i686/linker.ld | 54 ++++++ arch/i686/macros.s | 25 +++ arch/i686/meson.build | 6 + arch/i686/test/gdt.c | 20 +++ arch/i686/toolchain.ini | 18 ++ devices/meson.build | 5 + devices/uart_16550.c | 68 ++++++++ devices/uart_16550.h | 56 ++++++ devices/vga.c | 35 ++++ devices/vga.h | 24 +++ grub/grub.cfg | 7 + grub/include/multiboot2.h | 416 +++++++++++++++++++++++++++++++++++++++++++++ lib/memcpy.c | 20 +++ lib/memset.c | 14 ++ lib/stdio.h | 3 + lib/stdio/printf.c | 38 +++++ lib/string.h | 3 + lib/string/itoa.c | 13 ++ meson.build | 26 +++ scripts/mkiso.py | 27 +++ src/boot.h | 24 +++ src/kernel.c | 24 +++ src/mem.h | 4 + src/mem/vmm.c | 49 ++++++ src/mmap.c | 45 +++++ src/mmap.h | 7 + src/multiboot2.c | 49 ++++++ 37 files changed, 1478 insertions(+) create mode 100644 .clang-format create mode 100644 .hgignore create mode 100644 README.md create mode 100644 arch/i686/boot.S create mode 100644 arch/i686/gdt.c create mode 100644 arch/i686/include/gdt.h create mode 100644 arch/i686/include/paging.h create mode 100644 arch/i686/include/sys/io.h create mode 100644 arch/i686/init.s create mode 100644 arch/i686/lgdt.c create mode 100644 arch/i686/linker.ld create mode 100644 arch/i686/macros.s create mode 100644 arch/i686/meson.build create mode 100644 arch/i686/test/gdt.c create mode 100644 arch/i686/toolchain.ini create mode 100644 devices/meson.build create mode 100644 devices/uart_16550.c create mode 100644 devices/uart_16550.h create mode 100644 devices/vga.c create mode 100644 devices/vga.h create mode 100644 grub/grub.cfg create mode 100644 grub/include/multiboot2.h create mode 100644 lib/memcpy.c create mode 100644 lib/memset.c create mode 100644 lib/stdio.h create mode 100644 lib/stdio/printf.c create mode 100644 lib/string.h create mode 100644 lib/string/itoa.c create mode 100644 meson.build create mode 100755 scripts/mkiso.py create mode 100644 src/boot.h create mode 100644 src/kernel.c create mode 100644 src/mem.h create mode 100644 src/mem/vmm.c create mode 100644 src/mmap.c create mode 100644 src/mmap.h create mode 100644 src/multiboot2.c diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..dabc460 --- /dev/null +++ b/.clang-format @@ -0,0 +1,11 @@ +--- +BasedOnStyle: LLVM +AllowShortIfStatementsOnASingleLine: Always +AllowShortBlocksOnASingleLine: 'true' +AllowShortLoopsOnASingleLine: 'true' +AlwaysBreakAfterDefinitionReturnType: All +BreakBeforeBraces: Stroustrup +ColumnLimit: '120' +UseTab: Never + +... diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..9954522 --- /dev/null +++ b/.hgignore @@ -0,0 +1,3 @@ +syntax: glob +lib/musl* +build* diff --git a/README.md b/README.md new file mode 100644 index 0000000..bff995e --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ + +# musl +- arch/i686/include/sys/io.h +- lib/string/itoa.c diff --git a/arch/i686/boot.S b/arch/i686/boot.S new file mode 100644 index 0000000..1eea9a3 --- /dev/null +++ b/arch/i686/boot.S @@ -0,0 +1,89 @@ +#define ASM_FILE +#include +#include "macros.s" + +/* Declare a multiboot header that marks this program as a kernel */ +.section .multiboot.header, "a" +header_begin: +.align 8 + .int MULTIBOOT2_HEADER_MAGIC + .int MULTIBOOT_ARCHITECTURE_I386 + .int header_end - header_begin + .int -(MULTIBOOT2_HEADER_MAGIC + MULTIBOOT_ARCHITECTURE_I386 + (header_end - header_begin)) + +.align 8 +header_info_begin: + .short MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST + .short 0 # flags + .int header_info_end - header_info_begin + .int MULTIBOOT_TAG_TYPE_MMAP +header_info_end: + +.align 8 + .short MULTIBOOT_HEADER_TAG_END + .short 0 + .int 8 +header_end: + +.section .multiboot.pages, "aw", @nobits +.align 4096 +bootstrap_page0: + .skip 1024 * 4 + +.section .multiboot.text, "ax" +.extern k_pagedir +.extern k_stack +.extern k_init + +#.global __vaddr_base +#.set __vaddr_base, 0xc0000000 +#.global __vaddr_offset +.set __vaddr_offset, 0xc0000000 - 0x2000 + +.global _start +.type _start, @function +_start: + cli + + # check multiboot header + cmp $MULTIBOOT2_BOOTLOADER_MAGIC, %eax + jz c + hlt + +c: mov $k_stack - __vaddr_offset, %esp + + push %ebx # pointer to multiboot structure + call __multiboot2 + + # mmap multiboot section into bootstrap page + movl $bootstrap_page0, %edi + movl $1024, %ecx + mmap_section __multiboot_begin, __multiboot_end, PAGE_RW + mmap_section (__text_begin - __vaddr_offset), (__text_end - __vaddr_offset), PAGE_RO + mmap_section (__rodata_begin - __vaddr_offset), (__rodata_end - __vaddr_offset), PAGE_RO + mmap_section (__bss_begin - __vaddr_offset), (__bss_end - __vaddr_offset), PAGE_RW + mmap_section (__data_begin - __vaddr_offset), (__data_end - __vaddr_offset), PAGE_RW + + # mmap all the other section into k_ptable0x300 + movl $(k_ptable0x300 - __vaddr_offset), %edi + movl $1024, %ecx + mmap_section (__text_begin - __vaddr_offset), (__text_end - __vaddr_offset), PAGE_RO + mmap_section (__rodata_begin - __vaddr_offset), (__rodata_end - __vaddr_offset), PAGE_RO + mmap_section (__bss_begin - __vaddr_offset), (__bss_end - __vaddr_offset), PAGE_RW + mmap_section (__data_begin - __vaddr_offset), (__data_end - __vaddr_offset), PAGE_RW + + movl $(bootstrap_page0 + PAGE_RW), k_pagedir - __vaddr_offset + 0 * 4 + movl $(k_ptable0x300 - __vaddr_offset + PAGE_RW), k_pagedir - __vaddr_offset + 768 * 4 + + movl $(k_pagedir - __vaddr_offset), %ecx + movl %ecx, %cr3 + + # enable paging and the write-protect bit + movl %cr0, %ecx + orl $0x80010000, %ecx + movl %ecx, %cr0 + + # jump to higher half with an absolute jump + lea (k_init), %ecx + jmp *%ecx + diff --git a/arch/i686/gdt.c b/arch/i686/gdt.c new file mode 100644 index 0000000..fe0bfc9 --- /dev/null +++ b/arch/i686/gdt.c @@ -0,0 +1,20 @@ +#include + +void +SegmentDescriptor(struct SegmentDescriptor_t *self, unsigned base, unsigned limit, uint8_t access) +{ + self->base_15_0 = base & 0xffff; + self->base_23_16 = (base >> 16) & 0xff; + self->base_31_24 = (base >> 24) & 0xff; + + self->limit_15_0 = (limit <= 0xffff) ? (limit & 0xffff) : ((limit >> 12) & 0xffff); + self->limit_19_16 = (limit <= 0xffff) ? 0 : ((limit >> 28) & 0xf); + + self->access = access; + + self->a = 0; + self->rsv = 0; + self->db = (limit > 0xffff) ? 1 : 0; + self->granularity = (limit > 0) ? 1 : 0; +} + diff --git a/arch/i686/include/gdt.h b/arch/i686/include/gdt.h new file mode 100644 index 0000000..6badf84 --- /dev/null +++ b/arch/i686/include/gdt.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include + +enum Ring { Ring0 = 0x0, Ring1 = 0x1, Ring2 = 0x2, Ring3 = 0x3 }; + +struct __attribute__((packed)) Access { + bool accessed : 1; // if 0, is set by processor when accessed + bool readwrite : 1; // code seg: read toggle; data seg: write toggle + bool direction : 1; // code seg: conforming bit; data seg: direction bit + bool executable : 1; // executable bit + bool segment : 1; // true for code/data; false for gates/tss + enum Ring privilege : 2; // descriptor privilege level + bool present : 1; // true for every active segment +}; +_Static_assert(sizeof(struct Access) == 1); + +static const struct Access null_access = {}; +static const struct Access ktext_access = {.readwrite = true, .executable = true, .segment = true, .present = true}; +static const struct Access kdata_access = {.readwrite = true, .segment = true, .present = true}; + +// Segment Descriptor +// A memory structure (part of a table) that tells the CPU the attributes of a given segment +// |31| | | | | | |24|23|22|21|20|19| | |16|15| | | | | | | 8| 7| | | | | | | 0| +// | base_31_24 | G|DB| | A| lim_19_16 | access | base_23_16 | +// | base_15_0 | limit_15_0 | +// |31| | | | | | | | | | | | | | |16|15| | | | | | | | | | | | | | | 0| +// limit size of segment - 1, either in bytes or in 4KiB chunks (check flags) +// base address of segment +// access +// flags defines the segment chunks and 16/32 bit +struct __attribute__((packed)) SegmentDescriptor_t { + uint16_t limit_15_0; // low bits of segment limit + uint16_t base_15_0; // low bits of segment base address + uint8_t base_23_16; // middle bits of segment base address + uint8_t access; // access byte + uint8_t limit_19_16 : 4; // high bits of segment limit + // flags + bool a : 1; // unused, available for software use + bool rsv : 1; // reserved + bool db : 1; // false => 16-bit seg; true => 32-bit seg + bool granularity : 1; // limit scaled by 4k when set + uint8_t base_31_24; // high bits of segment address +}; +_Static_assert(sizeof(struct SegmentDescriptor_t) == 8); + +void SegmentDescriptor(struct SegmentDescriptor_t *self, unsigned base, unsigned limit, uint8_t access); + +struct __attribute__((packed)) Pointer { + uint16_t limit; + uint32_t base; +}; + +void gdt_install(); + +enum SegmentIndex { + ktextDescriptor = 2 * sizeof(struct SegmentDescriptor_t), + kdataDescriptor = 3 * sizeof(struct SegmentDescriptor_t), +}; diff --git a/arch/i686/include/paging.h b/arch/i686/include/paging.h new file mode 100644 index 0000000..cff0506 --- /dev/null +++ b/arch/i686/include/paging.h @@ -0,0 +1,59 @@ +#pragma once + +// DirectoryEntry +// |31| | | | | | | | | | | | | | | | | | | |11| | 9| 8| 7| 6| 5| 4| 3| 2| 1| 0| +// | page table 4-kb aligned address | avail | G| S| | A| C| W| U| R| P| +struct __attribute__((packed)) DirectoryEntry { + unsigned present : 1; // 0: if set, the page is actually in physical memory + unsigned writeable : 1; // 1: if set, the page is read/write; otherwise the page is read-only + unsigned user : 1; // 2: if set, then page can be access by all; otherwise only the supervisor can access it + unsigned writethrough : 1; // 3: if set, write-through caching is enabled; otherwise write-back is enabled instead + unsigned cachedisable : 1; // 4: if set, the page will not be cached + unsigned accessed : 1; // 5: set by the CPU when the page is read from or written to + unsigned dirty : 1; // 6: used to determine whether a page has been written to + unsigned pagesize : 1; // 7: page size == 0 + unsigned global : 1; + unsigned int __available__ : 3; // available to the OS + unsigned int address : 20; +}; +_Static_assert(sizeof(struct DirectoryEntry) == 4); + +// DirectoryEntry4MB +// |31| | | | | | | | |22|21|20| | | | | | |13|12|11| | 9| 8| 7| 6| 5| 4| 3| 2| 1| 0| +// | bits 31-22 of address |RS| bits 39-22 of |AT| avail | G|PS| D| A|CD|WT|US|RW| P| +// | |VD| address +struct __attribute__((packed)) DirectoryEntry4MB { + unsigned present : 1; // 0: if set, the page is actually in physical memory + unsigned writeable : 1; // 1: if set, the page is read/write; otherwise the page is read-only + unsigned useraccess : 1; // 2: if set, then page can be access by all; otherwise only the supervisor can access it + unsigned writethrough : 1; // 3: if set, write-through caching is enabled; otherwise write-back is enabled instead + unsigned cachedisable : 1; // 4: if set, the page will not be cached + unsigned accessed : 1; // 5: set by the CPU when the page is read from or written to + unsigned dirty : 1; // 6: used to determine whether a page has been written to + unsigned pagesize : 1; // 7: page size == 1 + unsigned global : 1; // 8: + unsigned __available__ : 3; // 11..9 available to the OS + unsigned pat : 1; // 12: page attribute table + unsigned int address_high : 8; + unsigned rsvd : 1; // 21 + unsigned int address_low : 10; +}; +_Static_assert(sizeof(struct DirectoryEntry4MB) == 4); + +// TableEntry +// |31| | | | | | | | | | | | | | | | | | | |11| | 9| 8| 7| 6| 5| 4| 3| 2| 1| 0| +// | page table 4-kb aligned address | avail | G| | D| A| C| W|US|RW| P| +struct __attribute__((packed)) TableEntry { + unsigned present : 1; // if set, the page is actually in physical memory + unsigned writeable : 1; // if set, the page is read/write; otherwise the page is read-only + unsigned user : 1; // if set, then page can be access by all; otherwise only the supervisor can access it + unsigned writethrough : 1; // if set, write-through caching is enabled; otherwise write-back is enabled instead + unsigned cachedisable : 1; // if set, the page will not be cached + unsigned accessed : 1; // set by the CPU when the page is read from or written to + unsigned dirty : 1; // used to determine whether a page has been written to + unsigned pat : 1; // page attribute table? + unsigned global : 1; + unsigned int __available__ : 3; // available to the OS + unsigned int address : 20; +}; +_Static_assert(sizeof(struct TableEntry) == 4); diff --git a/arch/i686/include/sys/io.h b/arch/i686/include/sys/io.h new file mode 100644 index 0000000..74d4950 --- /dev/null +++ b/arch/i686/include/sys/io.h @@ -0,0 +1,79 @@ +#pragma once + +static inline void +outb(unsigned char val, unsigned short port) +{ + asm volatile("outb %0,%1" : : "a"(val), "dN"(port)); +} + +static inline void +outw(unsigned short val, unsigned short port) +{ + asm volatile("outw %0,%1" : : "a"(val), "dN"(port)); +} + +static inline void +outl(unsigned int val, unsigned short port) +{ + asm volatile("outl %0,%1" : : "a"(val), "dN"(port)); +} + +static inline unsigned char +inb(unsigned short port) +{ + unsigned char val; + asm volatile("inb %1,%0" : "=a"(val) : "dN"(port)); + return val; +} + +static inline unsigned short +inw(unsigned short port) +{ + unsigned short val; + asm volatile("inw %1,%0" : "=a"(val) : "dN"(port)); + return val; +} + +static inline unsigned int +inl(unsigned short port) +{ + unsigned int val; + asm volatile("inl %1,%0" : "=a"(val) : "dN"(port)); + return val; +} + +static inline void +outsb(unsigned short port, const void *__buf, unsigned long __n) +{ + asm volatile("cld; rep; outsb" : "+S"(__buf), "+c"(__n) : "d"(port)); +} + +static inline void +outsw(unsigned short port, const void *__buf, unsigned long __n) +{ + asm volatile("cld; rep; outsw" : "+S"(__buf), "+c"(__n) : "d"(port)); +} + +static inline void +outsl(unsigned short port, const void *__buf, unsigned long __n) +{ + asm volatile("cld; rep; outsl" : "+S"(__buf), "+c"(__n) : "d"(port)); +} + +static inline void +insb(unsigned short port, void *__buf, unsigned long __n) +{ + asm volatile("cld; rep; insb" : "+D"(__buf), "+c"(__n) : "d"(port)); +} + +static inline void +insw(unsigned short port, void *__buf, unsigned long __n) +{ + asm volatile("cld; rep; insw" : "+D"(__buf), "+c"(__n) : "d"(port)); +} + +static inline void +insl(unsigned short port, void *__buf, unsigned long __n) +{ + asm volatile("cld; rep; insl" : "+D"(__buf), "+c"(__n) : "d"(port)); +} diff --git a/arch/i686/init.s b/arch/i686/init.s new file mode 100644 index 0000000..92710df --- /dev/null +++ b/arch/i686/init.s @@ -0,0 +1,42 @@ +.section .stack, "aw", @nobits +.global k_stack +.align 16 /* Stack on x86 must be 16-byte aligned according to System V ABI */ +k_stack_bottom: + .skip 16 * 1024 /* 16 kByte */ +k_stack: + +.section .pages, "aw", @nobits +.align 4096 +.global k_pagedir +k_pagedir: .skip 1024 * 4 +.global k_ptable0x300 +k_ptable0x300: .skip 1024 * 4 + +.section .text +.global k_init +.extern kmain +k_init: + # kernel entry point, higher half + + # unmap the identity mapping as its no longer necessary + movl $0, k_pagedir + 0 * 4 + mov %cr3, %ecx # reload cr3 to force a TLB flush + mov %ecx, %cr3 + + # zero out the stack + mov $4096, %ecx # loop counter + mov $k_stack_bottom, %edi # start from the bottom + xor %edx, %edx # clear %edx +1: movl %edx, (%edi) # store %edx into address in %edi + addl $4, %edi # point to next address + loop 1b # loop according to %ecx + + mov $k_stack, %esp # point stack pointer to the stack + + call kmain + + /* If the system has nothing more to do, put it in an infinite loop */ + cli # disable interrupts +h: hlt # wait for interrupt + jmp h # continue waiting for interrupt + diff --git a/arch/i686/lgdt.c b/arch/i686/lgdt.c new file mode 100644 index 0000000..2d35e8d --- /dev/null +++ b/arch/i686/lgdt.c @@ -0,0 +1,31 @@ +#include + +static struct SegmentDescriptor_t segments[8] __attribute__((aligned(32))); + +void +gdt_install() +{ + SegmentDescriptor(&segments[0], 0, 0, 0); // null segment + SegmentDescriptor(&segments[2], 0, 0xffffffff, 0x9a); // ktext + SegmentDescriptor(&segments[3], 0, 0xffffffff, 0x92); // kdata + + struct Pointer ptr = {.limit = sizeof(segments) - 1, .base = (unsigned)&segments}; + + asm volatile("lgdt (%0)" : : "a"(&ptr)); + + // load the kernel data segment + asm volatile(R"(mov %0, %%ds + mov %0, %%es + mov %0, %%fs + mov %0, %%gs + mov %0, %%ss +)" + : + : "ax"(kdataDescriptor)); + + // load the kernel code segment + asm volatile(R"(ljmp %0, $1f + 1:)" + : + : "i"(ktextDescriptor)); +} diff --git a/arch/i686/linker.ld b/arch/i686/linker.ld new file mode 100644 index 0000000..61a3be9 --- /dev/null +++ b/arch/i686/linker.ld @@ -0,0 +1,54 @@ +ENTRY(_start) +OUTPUT_FORMAT(elf32-i386) + +MULTIBOOT_SIZE = 0x2000; +VADDR_BASE = 0xc0000000; + +SECTIONS +{ + /* First put the multiboot header, as it is required to be put very early in the image or the bootloader won't * + * recognize the file format. * + * Once the MMU is set up, this section is no longer needed and should be unmapped. */ + . = 0; + .bootstrap : { + . = ALIGN(8); + __multiboot_begin = .; + KEEP(*(.multiboot.header)) + KEEP(*(.multiboot.text)) + KEEP(*(.multiboot.pages)) + __multiboot_end = .; + } + + . = VADDR_BASE; + + .text ALIGN(4K) : AT(ADDR(.text) - VADDR_BASE + MULTIBOOT_SIZE) { + __text_begin = .; + *(.text*) + __text_end = .; + } + + /* Read-only data. */ + .rodata ALIGN(4K) : AT(ADDR(.rodata) - VADDR_BASE + MULTIBOOT_SIZE) { + __rodata_begin = .; + *(.constinit) + *(.rodata*) + __rodata_end = .; + } + + /* Read-write data (uninitialized) and stack */ + .bss ALIGN(4K) : AT(ADDR(.bss) - VADDR_BASE + MULTIBOOT_SIZE) { + __bss_begin = .; + *(.stack) + *(.pages) + *(.bss*) + __bss_end = .; + } + + /* Read-write data (initialized) */ + .data ALIGN(4K) : AT(ADDR(.data) - VADDR_BASE + MULTIBOOT_SIZE) { + __data_begin = .; + *(.init) + *(.data) + __data_end = .; + } +} diff --git a/arch/i686/macros.s b/arch/i686/macros.s new file mode 100644 index 0000000..a9b8b4d --- /dev/null +++ b/arch/i686/macros.s @@ -0,0 +1,25 @@ +.set PAGE_RO, 0x001 +.set PAGE_RW, 0x003 +/* write section to page table macro + * + * Registers used: + * %ecx: loop counter [ set to $1024 ] + * %edx: temporary + * %esi: current page being mapped + * %edi: page entry [ set to $page_addr ] + */ +.macro mmap_section begin, end, access + mov $\begin, %esi # from $begin +1: cmpl $\end, %esi # until $end + jge 2f + + movl %esi, %edx + orl $\access, %edx + movl %edx, (%edi) + + addl $4096, %esi # move to next page + addl $4, %edi # size of page entry is 4 bytes + loop 1b # loop according to %ecx +2: +.endm + diff --git a/arch/i686/meson.build b/arch/i686/meson.build new file mode 100644 index 0000000..25eebee --- /dev/null +++ b/arch/i686/meson.build @@ -0,0 +1,6 @@ +arch = declare_dependency( + sources: ['boot.S', 'init.s', 'gdt.c', 'lgdt.c'], + include_directories: 'include' +) + +test('GDT', executable('gdt', ['test/gdt.c', 'gdt.c'], include_directories: 'include', native: true)) diff --git a/arch/i686/test/gdt.c b/arch/i686/test/gdt.c new file mode 100644 index 0000000..2947b42 --- /dev/null +++ b/arch/i686/test/gdt.c @@ -0,0 +1,20 @@ +#include "gdt.h" +#include +#include + +int +main() +{ + assert(*(uint8_t *)&null_access == 0x00); + assert(*(uint8_t *)&ktext_access == 0x9a); + assert(*(uint8_t *)&kdata_access == 0x92); + + struct SegmentDescriptor_t d; + SegmentDescriptor(&d, 0, 0, 0); + assert(*(uint64_t *)&d == 0); + + assert(ktextDescriptor == 0x10); + assert(kdataDescriptor == 0x18); + + return EXIT_SUCCESS; +} diff --git a/arch/i686/toolchain.ini b/arch/i686/toolchain.ini new file mode 100644 index 0000000..6f6b5ef --- /dev/null +++ b/arch/i686/toolchain.ini @@ -0,0 +1,18 @@ +[constants] +arch = 'i686' + +[host_machine] +system = 'none' +cpu_family = 'x86' +cpu = 'i686' +endian = 'little' + +[binaries] +c = 'i686-elf-gcc' +ar = 'i686-elf-ar' +strip = 'i686-elf-strip' + +[built-in options] +c_args = ['-fanalyzer', '-ffreestanding'] +c_link_args = [ '-static', '-nostdlib', '-T', 'arch/i686/linker.ld' ] + diff --git a/devices/meson.build b/devices/meson.build new file mode 100644 index 0000000..c52c389 --- /dev/null +++ b/devices/meson.build @@ -0,0 +1,5 @@ +devices = declare_dependency( + sources: ['vga.c', 'uart_16550.c'], + include_directories: '.' +) + diff --git a/devices/uart_16550.c b/devices/uart_16550.c new file mode 100644 index 0000000..7cec968 --- /dev/null +++ b/devices/uart_16550.c @@ -0,0 +1,68 @@ +#include "uart_16550.h" +#include + +int +uart_init(enum UART port) +{ + outb(0x00, port + 1); // Disable all interrupts + outb(0x80, port + 3); // Enable DLAB (set baud rate divisor) + outb(0x03, port + 0); // Set divisor to 3 (lo byte) 38400 baud + outb(0x00, port + 1); // (hi byte) + outb(0x03, port + 3); // 8 bits, no parity, one stop bit + outb(0xc7, port + 2); // Enable FIFO, clear them, with 14-byte threshold + outb(0x0b, port + 4); // IRQs enabled, RTS/DSR set + outb(0x1e, port + 4); // Set in loopback mode, test the serial chip + outb(0xae, port + 0); // Test serial chip (send byte 0xAE and check if serial + // returns same byte) + + // Check if serial is faulty (i.e: not same byte as sent) + if (inb(port + 0) != 0xae) { + return 1; + } + + // If serial is not faulty set it in normal operation mode + // (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled) + outb(0x0f, port + 4); + return 0; +} + +int +uart_thre(enum UART port) +{ + return inb(port + LineStatus) & THRE; +} + +void +uart_write(enum UART port, char a) +{ + while (uart_thre(port) == 0) + ; + outb(a, port); + + if (a == '\n') { + while (uart_thre(port) == 0) + ; + outb('\r', port); + } +} + +int +uart_puts(enum UART port, const char *string, int length) +{ + int written = 0; + + if (length == -1) + while (*string != '\0') { + uart_write(port, *string); + ++string; + ++written; + } + + else + for (int i = 0; i < length; ++i) { + uart_write(port, string[i]); + ++written; + } + + return written; +} diff --git a/devices/uart_16550.h b/devices/uart_16550.h new file mode 100644 index 0000000..d053985 --- /dev/null +++ b/devices/uart_16550.h @@ -0,0 +1,56 @@ +#pragma once + +enum uart_16550_offset { + Data = 0, // read from receive buffer / write to transmit buffer | BaudDiv_l + InterruptControl = 1, // interrupt enable | BaudDiv_h + FifoControl = 2, // interrupt ID and FIFO control + LineControl = 3, // most significant bit is the DLAB + ModemControl = 4, + LineStatus = 5, + ModemStatus = 6, + Scratch = 7, +}; +// Line Control +// | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +// |dla| | parity | s | data | + +enum LineControl { + d5bit = 0b00000000, // data bits + d6bit = 0b00000001, + d7bit = 0b00000010, + d8bit = 0b00000011, + // none = 0b00000000, // parity bits + odd = 0b00001000, + even = 0b00011000, + mark = 0b00101000, + space = 0b00111000, + // s1bit = 0b00000000, // stop bits + s2bit = 0b00000100, // 1.5 for 5bit data; 2 otherwise + dlab = 0b10000000, // divisor latch access bit +}; + +// Line Status Register +enum LineStatus { + DR = 0b00000001, // data ready: see if there is data to read + OE = 0b00000010, // overrun error: see if there has been data lost + PE = 0b00000100, // parity error: see if there was error in transmission + FE = 0b00001000, // framing error: see if a stop bit was missing + BI = 0b00010000, // break indicator: see if there is a break in data input + THRE = 0b00100000, // transmitter holding register empty: see if transmission buffer is empty + TEMT = 0b01000000, // transmitter empty: see if transmitter is not doing anything + ERRO = 0b10000000, // impending error: see if there is an error with a word in the input buffer +}; + +enum UART { + COM1 = 0x3f8, + COM2 = 0x2f8, + COM3 = 0x3E8, + COM4 = 0x2E8, + COM5 = 0x5F8, + COM6 = 0x4F8, + COM7 = 0x5E8, + COM8 = 0x4E8, +}; + +int uart_init(enum UART port); +int uart_puts(enum UART port, const char *string, int length); diff --git a/devices/vga.c b/devices/vga.c new file mode 100644 index 0000000..983e630 --- /dev/null +++ b/devices/vga.c @@ -0,0 +1,35 @@ +#include "vga.h" +#include +#include + +struct __attribute__((packed)) VGAEntry { + unsigned char text; + uint8_t foreground : 4; + uint8_t background : 4; +}; + +_Static_assert(sizeof(struct VGAEntry) == 2); + +const size_t width = 80; +const size_t height = 25; + +struct VGAEntry *buffer; + +void +vga_init() +{ + buffer = (struct VGAEntry *)0xc03ff000; + vga_clear(VGA_COLOR_LIGHT_BLUE, VGA_COLOR_LIGHT_GREY); +} + +void +vga_clear(enum vga_color foreground, enum vga_color background) +{ + for (size_t y = 0; y < height; ++y) + for (size_t x = 0; x < width; ++x) { + const size_t index = y * width + x; + buffer[index].text = ' '; + buffer[index].foreground = foreground; + buffer[index].background = background; + } +} diff --git a/devices/vga.h b/devices/vga.h new file mode 100644 index 0000000..c2ef2ef --- /dev/null +++ b/devices/vga.h @@ -0,0 +1,24 @@ +#pragma once + +/* Hardware text mode color constants. */ +enum vga_color { + VGA_COLOR_BLACK = 0, + VGA_COLOR_BLUE = 1, + VGA_COLOR_GREEN = 2, + VGA_COLOR_CYAN = 3, + VGA_COLOR_RED = 4, + VGA_COLOR_MAGENTA = 5, + VGA_COLOR_BROWN = 6, + VGA_COLOR_LIGHT_GREY = 7, + VGA_COLOR_DARK_GREY = 8, + VGA_COLOR_LIGHT_BLUE = 9, + VGA_COLOR_LIGHT_GREEN = 10, + VGA_COLOR_LIGHT_CYAN = 11, + VGA_COLOR_LIGHT_RED = 12, + VGA_COLOR_LIGHT_MAGENTA = 13, + VGA_COLOR_LIGHT_BROWN = 14, + VGA_COLOR_WHITE = 15, +}; + +void vga_init(); +void vga_clear(enum vga_color foreground, enum vga_color background); diff --git a/grub/grub.cfg b/grub/grub.cfg new file mode 100644 index 0000000..5307624 --- /dev/null +++ b/grub/grub.cfg @@ -0,0 +1,7 @@ +set timeout=1 +set default=0 + +menuentry "glitch" { + multiboot2 /boot/glitch/glitch.elf quiet + boot +} diff --git a/grub/include/multiboot2.h b/grub/include/multiboot2.h new file mode 100644 index 0000000..5a3db5a --- /dev/null +++ b/grub/include/multiboot2.h @@ -0,0 +1,416 @@ +/* multiboot2.h - Multiboot 2 header file. */ +/* Copyright (C) 1999,2003,2007,2008,2009,2010 Free Software Foundation, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ANY + * DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MULTIBOOT_HEADER +#define MULTIBOOT_HEADER 1 + +/* How many bytes from the start of the file we search for the header. */ +#define MULTIBOOT_SEARCH 32768 +#define MULTIBOOT_HEADER_ALIGN 8 + +/* The magic field should contain this. */ +#define MULTIBOOT2_HEADER_MAGIC 0xe85250d6 + +/* This should be in %eax. */ +#define MULTIBOOT2_BOOTLOADER_MAGIC 0x36d76289 + +/* Alignment of multiboot modules. */ +#define MULTIBOOT_MOD_ALIGN 0x00001000 + +/* Alignment of the multiboot info structure. */ +#define MULTIBOOT_INFO_ALIGN 0x00000008 + +/* Flags set in the 'flags' member of the multiboot header. */ + +#define MULTIBOOT_TAG_ALIGN 8 +#define MULTIBOOT_TAG_TYPE_END 0 +#define MULTIBOOT_TAG_TYPE_CMDLINE 1 +#define MULTIBOOT_TAG_TYPE_BOOT_LOADER_NAME 2 +#define MULTIBOOT_TAG_TYPE_MODULE 3 +#define MULTIBOOT_TAG_TYPE_BASIC_MEMINFO 4 +#define MULTIBOOT_TAG_TYPE_BOOTDEV 5 +#define MULTIBOOT_TAG_TYPE_MMAP 6 +#define MULTIBOOT_TAG_TYPE_VBE 7 +#define MULTIBOOT_TAG_TYPE_FRAMEBUFFER 8 +#define MULTIBOOT_TAG_TYPE_ELF_SECTIONS 9 +#define MULTIBOOT_TAG_TYPE_APM 10 +#define MULTIBOOT_TAG_TYPE_EFI32 11 +#define MULTIBOOT_TAG_TYPE_EFI64 12 +#define MULTIBOOT_TAG_TYPE_SMBIOS 13 +#define MULTIBOOT_TAG_TYPE_ACPI_OLD 14 +#define MULTIBOOT_TAG_TYPE_ACPI_NEW 15 +#define MULTIBOOT_TAG_TYPE_NETWORK 16 +#define MULTIBOOT_TAG_TYPE_EFI_MMAP 17 +#define MULTIBOOT_TAG_TYPE_EFI_BS 18 +#define MULTIBOOT_TAG_TYPE_EFI32_IH 19 +#define MULTIBOOT_TAG_TYPE_EFI64_IH 20 +#define MULTIBOOT_TAG_TYPE_LOAD_BASE_ADDR 21 + +#define MULTIBOOT_HEADER_TAG_END 0 +#define MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST 1 +#define MULTIBOOT_HEADER_TAG_ADDRESS 2 +#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS 3 +#define MULTIBOOT_HEADER_TAG_CONSOLE_FLAGS 4 +#define MULTIBOOT_HEADER_TAG_FRAMEBUFFER 5 +#define MULTIBOOT_HEADER_TAG_MODULE_ALIGN 6 +#define MULTIBOOT_HEADER_TAG_EFI_BS 7 +#define MULTIBOOT_HEADER_TAG_ENTRY_ADDRESS_EFI64 9 +#define MULTIBOOT_HEADER_TAG_RELOCATABLE 10 + +#define MULTIBOOT_ARCHITECTURE_I386 0 +#define MULTIBOOT_ARCHITECTURE_MIPS32 4 +#define MULTIBOOT_HEADER_TAG_OPTIONAL 1 + +#define MULTIBOOT_LOAD_PREFERENCE_NONE 0 +#define MULTIBOOT_LOAD_PREFERENCE_LOW 1 +#define MULTIBOOT_LOAD_PREFERENCE_HIGH 2 + +#define MULTIBOOT_CONSOLE_FLAGS_CONSOLE_REQUIRED 1 +#define MULTIBOOT_CONSOLE_FLAGS_EGA_TEXT_SUPPORTED 2 + +#ifndef ASM_FILE + +typedef unsigned char multiboot_uint8_t; +typedef unsigned short multiboot_uint16_t; +typedef unsigned int multiboot_uint32_t; +typedef unsigned long long multiboot_uint64_t; + +struct multiboot_header +{ + /* Must be MULTIBOOT_MAGIC - see above. */ + multiboot_uint32_t magic; + + /* ISA */ + multiboot_uint32_t architecture; + + /* Total header length. */ + multiboot_uint32_t header_length; + + /* The above fields plus this one must equal 0 mod 2^32. */ + multiboot_uint32_t checksum; +}; + +struct multiboot_header_tag +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; +}; + +struct multiboot_header_tag_information_request +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t requests[0]; +}; + +struct multiboot_header_tag_address +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t header_addr; + multiboot_uint32_t load_addr; + multiboot_uint32_t load_end_addr; + multiboot_uint32_t bss_end_addr; +}; + +struct multiboot_header_tag_entry_address +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t entry_addr; +}; + +struct multiboot_header_tag_console_flags +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t console_flags; +}; + +struct multiboot_header_tag_framebuffer +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t width; + multiboot_uint32_t height; + multiboot_uint32_t depth; +}; + +struct multiboot_header_tag_module_align +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; +}; + +struct multiboot_header_tag_relocatable +{ + multiboot_uint16_t type; + multiboot_uint16_t flags; + multiboot_uint32_t size; + multiboot_uint32_t min_addr; + multiboot_uint32_t max_addr; + multiboot_uint32_t align; + multiboot_uint32_t preference; +}; + +struct multiboot_color +{ + multiboot_uint8_t red; + multiboot_uint8_t green; + multiboot_uint8_t blue; +}; + +struct multiboot_mmap_entry +{ + multiboot_uint64_t addr; + multiboot_uint64_t len; +#define MULTIBOOT_MEMORY_AVAILABLE 1 +#define MULTIBOOT_MEMORY_RESERVED 2 +#define MULTIBOOT_MEMORY_ACPI_RECLAIMABLE 3 +#define MULTIBOOT_MEMORY_NVS 4 +#define MULTIBOOT_MEMORY_BADRAM 5 + multiboot_uint32_t type; + multiboot_uint32_t zero; +}; +typedef struct multiboot_mmap_entry multiboot_memory_map_t; + +struct multiboot_tag +{ + multiboot_uint32_t type; + multiboot_uint32_t size; +}; + +struct multiboot_tag_string +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + char string[0]; +}; + +struct multiboot_tag_module +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t mod_start; + multiboot_uint32_t mod_end; + char cmdline[0]; +}; + +struct multiboot_tag_basic_meminfo +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t mem_lower; + multiboot_uint32_t mem_upper; +}; + +struct multiboot_tag_bootdev +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t biosdev; + multiboot_uint32_t slice; + multiboot_uint32_t part; +}; + +struct multiboot_tag_mmap +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t entry_size; + multiboot_uint32_t entry_version; + struct multiboot_mmap_entry entries[0]; +}; + +struct multiboot_vbe_info_block +{ + multiboot_uint8_t external_specification[512]; +}; + +struct multiboot_vbe_mode_info_block +{ + multiboot_uint8_t external_specification[256]; +}; + +struct multiboot_tag_vbe +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + + multiboot_uint16_t vbe_mode; + multiboot_uint16_t vbe_interface_seg; + multiboot_uint16_t vbe_interface_off; + multiboot_uint16_t vbe_interface_len; + + struct multiboot_vbe_info_block vbe_control_info; + struct multiboot_vbe_mode_info_block vbe_mode_info; +}; + +struct multiboot_tag_framebuffer_common +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + + multiboot_uint64_t framebuffer_addr; + multiboot_uint32_t framebuffer_pitch; + multiboot_uint32_t framebuffer_width; + multiboot_uint32_t framebuffer_height; + multiboot_uint8_t framebuffer_bpp; +#define MULTIBOOT_FRAMEBUFFER_TYPE_INDEXED 0 +#define MULTIBOOT_FRAMEBUFFER_TYPE_RGB 1 +#define MULTIBOOT_FRAMEBUFFER_TYPE_EGA_TEXT 2 + multiboot_uint8_t framebuffer_type; + multiboot_uint16_t reserved; +}; + +struct multiboot_tag_framebuffer +{ + struct multiboot_tag_framebuffer_common common; + + union + { + struct + { + multiboot_uint16_t framebuffer_palette_num_colors; + struct multiboot_color framebuffer_palette[0]; + }; + struct + { + multiboot_uint8_t framebuffer_red_field_position; + multiboot_uint8_t framebuffer_red_mask_size; + multiboot_uint8_t framebuffer_green_field_position; + multiboot_uint8_t framebuffer_green_mask_size; + multiboot_uint8_t framebuffer_blue_field_position; + multiboot_uint8_t framebuffer_blue_mask_size; + }; + }; +}; + +struct multiboot_tag_elf_sections +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t num; + multiboot_uint32_t entsize; + multiboot_uint32_t shndx; + char sections[0]; +}; + +struct multiboot_tag_apm +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint16_t version; + multiboot_uint16_t cseg; + multiboot_uint32_t offset; + multiboot_uint16_t cseg_16; + multiboot_uint16_t dseg; + multiboot_uint16_t flags; + multiboot_uint16_t cseg_len; + multiboot_uint16_t cseg_16_len; + multiboot_uint16_t dseg_len; +}; + +struct multiboot_tag_efi32 +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t pointer; +}; + +struct multiboot_tag_efi64 +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint64_t pointer; +}; + +struct multiboot_tag_smbios +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t major; + multiboot_uint8_t minor; + multiboot_uint8_t reserved[6]; + multiboot_uint8_t tables[0]; +}; + +struct multiboot_tag_old_acpi +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t rsdp[0]; +}; + +struct multiboot_tag_new_acpi +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t rsdp[0]; +}; + +struct multiboot_tag_network +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint8_t dhcpack[0]; +}; + +struct multiboot_tag_efi_mmap +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t descr_size; + multiboot_uint32_t descr_vers; + multiboot_uint8_t efi_mmap[0]; +}; + +struct multiboot_tag_efi32_ih +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t pointer; +}; + +struct multiboot_tag_efi64_ih +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint64_t pointer; +}; + +struct multiboot_tag_load_base_addr +{ + multiboot_uint32_t type; + multiboot_uint32_t size; + multiboot_uint32_t load_base_addr; +}; + +#endif /* ! ASM_FILE */ + +#endif /* ! MULTIBOOT_HEADER */ diff --git a/lib/memcpy.c b/lib/memcpy.c new file mode 100644 index 0000000..c648501 --- /dev/null +++ b/lib/memcpy.c @@ -0,0 +1,20 @@ +/** + * The memcpy() function copies n bytes from memory area src to memory area dest. The memory areas must not overlap. + * @param dest + * @param src + * @param n + * @return + */ +void * +memcpy(void *restrict dest, const void *restrict src, unsigned n) +{ + char *pDest = (char *)dest; + const char *pSrc = (const char *)src; + + while (n) { + *(pDest++) = *(pSrc++); + --n; + } + + return dest; +} \ No newline at end of file diff --git a/lib/memset.c b/lib/memset.c new file mode 100644 index 0000000..442a305 --- /dev/null +++ b/lib/memset.c @@ -0,0 +1,14 @@ +/** + * The memset() function fills the first n bytes of the memory area pointed to by s with the constant byte c. + * @param s + * @param c + * @param n + * @return + */ +void * +memset(void *s, char c, unsigned n) +{ + char *pDest = (char *)s; + for (unsigned i = 0; i < n; ++i) pDest[i] = c; + return s; +} \ No newline at end of file diff --git a/lib/stdio.h b/lib/stdio.h new file mode 100644 index 0000000..cf3a33f --- /dev/null +++ b/lib/stdio.h @@ -0,0 +1,3 @@ +#pragma once + +int printf(const char *restrict format, ...); diff --git a/lib/stdio/printf.c b/lib/stdio/printf.c new file mode 100644 index 0000000..f6b8ef6 --- /dev/null +++ b/lib/stdio/printf.c @@ -0,0 +1,38 @@ +#include +#include + +#include + +int +printf(const char *restrict format, ...) +{ + int written = 0; + va_list params; + va_start(params, format); + + int s = 0; + int l = 0; + for (int i = 0; format[i] != '\0'; ++i) { + if (format[i] == '%') { + written += uart_puts(COM1, &format[s], l); + s = i + 2; + ++i; + + switch (format[i]) { + case 's': + written += uart_puts(COM1, va_arg(params, const char *), -1); + break; + } + + l = 0; + } + + else + ++l; + } + + if (l > 0) written += uart_puts(COM1, &format[s], l); + + va_end(params); + return written; +} diff --git a/lib/string.h b/lib/string.h new file mode 100644 index 0000000..361d308 --- /dev/null +++ b/lib/string.h @@ -0,0 +1,3 @@ +#pragma once + +char *itoa(char *p, unsigned x); diff --git a/lib/string/itoa.c b/lib/string/itoa.c new file mode 100644 index 0000000..5eefb37 --- /dev/null +++ b/lib/string/itoa.c @@ -0,0 +1,13 @@ +#include "string.h" + +char * +itoa(char *p, unsigned x) +{ + p += 3 * sizeof(int); + *--p = 0; + do { + *--p = '0' + x % 10; + x /= 10; + } while (x); + return p; +} diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..e5c8410 --- /dev/null +++ b/meson.build @@ -0,0 +1,26 @@ +project('glitch kernel', 'c', version: '0.1.0', default_options: ['c_std=gnu11', 'warning_level=2']) +python3 = import('python').find_installation('python3') + +subdir('arch/i686') +subdir('devices') + +kernel = executable('glitch.elf', + ['src/multiboot2.c', 'src/mmap.c', 'src/kernel.c', + 'src/mem/vmm.c', + 'lib/string/itoa.c', 'lib/stdio/printf.c'], + link_language: 'c', + link_args: ['-ffreestanding', '-nostdlib', '-static', '-T', meson.current_source_dir()/'arch/i686/linker.ld'], + install: true, native: false, + include_directories: [ + include_directories('grub/include', is_system: true), + include_directories('lib') + ], + dependencies: [arch, devices] +) + +glitch_iso = custom_target('glitch.iso', + input: 'scripts/mkiso.py', output: 'glitch.iso', depends: [kernel], + command: [python3, '@INPUT@', kernel, '@SOURCE_ROOT@/grub/grub.cfg']) + +qemu = find_program('qemu-system-i386') +run_target('run', depends: glitch_iso, command: [qemu, '-cdrom', 'glitch.iso', '-accel', 'kvm']) diff --git a/scripts/mkiso.py b/scripts/mkiso.py new file mode 100755 index 0000000..b1f1dc5 --- /dev/null +++ b/scripts/mkiso.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 + +import argparse +from os import makedirs as mkpath +from shutil import copy +from subprocess import run + +def main(): + parser = argparse.ArgumentParser(description='Build a bootable CD-ROM image using grub-mkrescue') + parser.add_argument('kernel', metavar='zcore', type=str, help='path to kernel') + parser.add_argument('grub', metavar='grub.cfg', type=str, help='path to grub.cfg') + parser.add_argument('mkrescue_args', metavar='MKRESCUE_ARGS', type=str, nargs='*', help='Additional arguments passed to grub-mkrescue') + parser.add_argument('--cache', type=str, default='isodir', help='isodir') + parser.add_argument('-o', '--output', type=str, default='glitch.iso', help='glitch.iso') + + args = parser.parse_args() + + mkpath('isodir/boot/grub', exist_ok=True) + copy(args.grub, 'isodir/boot/grub/grub.cfg') + mkpath('isodir/boot/glitch', exist_ok=True) + copy(args.kernel, 'isodir/boot/glitch/glitch.elf') + + run(['grub-mkrescue', '-o', args.output, args.cache] + args.mkrescue_args) + +if __name__ == "__main__": + main() + diff --git a/src/boot.h b/src/boot.h new file mode 100644 index 0000000..56ff219 --- /dev/null +++ b/src/boot.h @@ -0,0 +1,24 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + // kernel command line + char cmdline[64]; + + // memory map + unsigned bitmap[1024 * 32]; + + // module + unsigned module_start; + unsigned module_end; + char module_cmdline[64]; +} boot_info_t; + +_Static_assert((1024 * 32 * sizeof(unsigned) * 8) == (1024 * 1024)); + +#ifdef __cplusplus +} +#endif diff --git a/src/kernel.c b/src/kernel.c new file mode 100644 index 0000000..b84d7ee --- /dev/null +++ b/src/kernel.c @@ -0,0 +1,24 @@ +#include "mem.h" + +#include +#include + +#include "devices/uart_16550.h" +#include "devices/vga.h" + +void kmain() { + gdt_install(); + + if (uart_init(COM1) != 0) printf("UART self-test failed.\r\n"); + + printf("hello %s world\n", "kernel"); + + vga_init(vmm_map(0xb8000, 0xc03ff000)); + + alloc4M(); + char *c = (char *)0xc0700000; + if (*c == 0) printf("c is 0\r\n"); + + while (1) + ; +} diff --git a/src/mem.h b/src/mem.h new file mode 100644 index 0000000..e06dd21 --- /dev/null +++ b/src/mem.h @@ -0,0 +1,4 @@ +#pragma once + +unsigned int vmm_map(unsigned int paddr, unsigned int vaddr); +void alloc4M(); diff --git a/src/mem/vmm.c b/src/mem/vmm.c new file mode 100644 index 0000000..431f0df --- /dev/null +++ b/src/mem/vmm.c @@ -0,0 +1,49 @@ +#include "../mem.h" +#include "paging.h" + +extern struct DirectoryEntry k_pagedir[1024]; +extern struct TableEntry k_ptable0x300[1024]; + +extern const unsigned MULTIBOOT_SIZE; +extern const unsigned VADDR_BASE; + +unsigned +to_vaddr(unsigned paddr) +{ + return paddr + (unsigned)&VADDR_BASE - (unsigned)&MULTIBOOT_SIZE; +} + +unsigned int +vmm_map(unsigned int paddr, unsigned int vaddr) +{ + if (paddr & 0xfff || vaddr & 0xfff) return 0; + + const unsigned table_idx = vaddr >> 22; // high 10 bits + const unsigned entry_idx = (vaddr >> 12) & 0x3ff; // low 10 bits + + if (k_pagedir[table_idx].present == 0) return 0; + struct TableEntry *table = (struct TableEntry *)to_vaddr(k_pagedir[table_idx].address << 12); + + table[entry_idx].address = paddr >> 12; + table[entry_idx].present = 1; + table[entry_idx].writeable = 1; + + return vaddr; +} + +void +alloc4M() +{ + // enable pse in cr4 + asm volatile(R"( + movl %cr4, %eax + orl $0x10, %eax + movl %eax, %cr4 +)"); + + struct DirectoryEntry4MB *directory = (struct DirectoryEntry4MB *)&k_pagedir[0x301]; + directory->address_low = 0x1; + directory->present = 1; + directory->writeable = 1; + directory->pagesize = 1; +} diff --git a/src/mmap.c b/src/mmap.c new file mode 100644 index 0000000..3fe35b5 --- /dev/null +++ b/src/mmap.c @@ -0,0 +1,45 @@ +#include "mmap.h" +#ifdef DEBUG +#include +#endif + +__attribute__((section(".multiboot.text"))) unsigned +multiboot2_mmap(const struct multiboot_mmap_entry entries[], unsigned entry_count, unsigned bitmap[1024 * 32]) +{ + // clear out the bitmap + for (unsigned i = 0; i < 1024 * 32; ++i) bitmap[i] = 0; + unsigned avail_frames = 0; + + // loop through all the mmap_entry structures where type is MULTIBOOT_MEMORY_AVAILABLE + for (unsigned i = 0; i < entry_count; ++i) { + if (entries[i].type != MULTIBOOT_MEMORY_AVAILABLE) continue; + + // number of frames in this entry + unsigned n_frames = entries[i].len / 4096; + avail_frames += n_frames; + +#ifdef DEBUG + printf("mmap_entry: 0x%16llx\tlen=%12llu\t%d frames (%d blocks + %d)\n", entries[i].addr, entries[i].len, n_frames, + n_frames / 32, n_frames % 32); +#endif + + // the bitmap is an array of blocks, each holding 32 (2^5) values + unsigned table_idx = (entries[i].addr >> 17); // get the upper 15 bits + + while (n_frames != 0) { + if (n_frames >= 32) { + bitmap[table_idx] = 0xffffffff; + table_idx++; + n_frames -= 32; + } + else { + unsigned block = bitmap[table_idx]; + for (unsigned l = 0; l < n_frames; ++l) block |= (1 << l); + bitmap[table_idx] = block; + n_frames = 0; + } + } + } + + return avail_frames; +} diff --git a/src/mmap.h b/src/mmap.h new file mode 100644 index 0000000..13f40f2 --- /dev/null +++ b/src/mmap.h @@ -0,0 +1,7 @@ +#pragma once + +#include "boot.h" +#include + +__attribute__((section(".multiboot.text"))) unsigned multiboot2_mmap(const struct multiboot_mmap_entry entries[], + unsigned entry_count, unsigned bitmap[1024 * 32]); diff --git a/src/multiboot2.c b/src/multiboot2.c new file mode 100644 index 0000000..d3e4175 --- /dev/null +++ b/src/multiboot2.c @@ -0,0 +1,49 @@ +#include "mmap.h" + +#define ADDR(x) ((unsigned)&x - 0xc0000000 + 0x2000) + +boot_info_t info __attribute__((section(".init"))); + +__attribute__((section(".multiboot.text"))) void +multiboot_strncpy(char *dest, const char *src, unsigned n) +{ + for (unsigned i = 0; i < n && src[i] != '\0'; ++i) dest[i] = src[i]; +} + +__attribute__((section(".multiboot.text"))) void +multiboot2_module(struct multiboot_tag_module *tag, boot_info_t *info) +{ + info->module_start = tag->mod_start; + info->module_end = tag->mod_end; + multiboot_strncpy(info->module_cmdline, tag->cmdline, 64); +} + +/** + * parse multiboot2 structures + */ +__attribute__((section(".multiboot.text"))) void +__multiboot2(multiboot_uint32_t addr) +{ + boot_info_t *__info = (boot_info_t *)ADDR(info); + + struct multiboot_tag *tag; + for (tag = (struct multiboot_tag *)(addr + 8); tag->type != MULTIBOOT_TAG_TYPE_END; + tag = (struct multiboot_tag *)((multiboot_uint8_t *)tag + ((tag->size + 7u) & ~7u))) { + + switch (tag->type) { + case MULTIBOOT_TAG_TYPE_CMDLINE: + multiboot_strncpy(__info->cmdline, ((struct multiboot_tag_string *)tag)->string, 64); + break; + case MULTIBOOT_TAG_TYPE_MODULE: + multiboot2_module((struct multiboot_tag_module *)tag, __info); + break; + case MULTIBOOT_TAG_TYPE_MMAP: + multiboot2_mmap(((struct multiboot_tag_mmap *)tag)->entries, + ((struct multiboot_tag_mmap *)tag)->size / ((struct multiboot_tag_mmap *)tag)->entry_size, + __info->bitmap); + break; + default: + break; + } // switch + } // for +} -- cgit v1.2.1