aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoraqua <aqua@iserlohn-fortress.net>2022-03-28 20:03:38 +0300
committeraqua <aqua@iserlohn-fortress.net>2022-08-12 10:13:59 +0300
commitedf9e71e2a7b6b89775c29cf28c19c6b89992c25 (patch)
tree3adbf944d9e47a743063487c4facb7eed1fbdee0
downloadkernel-edf9e71e2a7b6b89775c29cf28c19c6b89992c25.tar.xz
Initial commit
x86 kernel that prints a hello world message to com1
-rw-r--r--.clang-format11
-rw-r--r--.hgignore3
-rw-r--r--README.md4
-rw-r--r--arch/i686/boot.S89
-rw-r--r--arch/i686/gdt.c20
-rw-r--r--arch/i686/include/gdt.h60
-rw-r--r--arch/i686/include/paging.h59
-rw-r--r--arch/i686/include/sys/io.h79
-rw-r--r--arch/i686/init.s42
-rw-r--r--arch/i686/lgdt.c31
-rw-r--r--arch/i686/linker.ld54
-rw-r--r--arch/i686/macros.s25
-rw-r--r--arch/i686/meson.build6
-rw-r--r--arch/i686/test/gdt.c20
-rw-r--r--arch/i686/toolchain.ini18
-rw-r--r--devices/meson.build5
-rw-r--r--devices/uart_16550.c68
-rw-r--r--devices/uart_16550.h56
-rw-r--r--devices/vga.c35
-rw-r--r--devices/vga.h24
-rw-r--r--grub/grub.cfg7
-rw-r--r--grub/include/multiboot2.h416
-rw-r--r--lib/memcpy.c20
-rw-r--r--lib/memset.c14
-rw-r--r--lib/stdio.h3
-rw-r--r--lib/stdio/printf.c38
-rw-r--r--lib/string.h3
-rw-r--r--lib/string/itoa.c13
-rw-r--r--meson.build26
-rwxr-xr-xscripts/mkiso.py27
-rw-r--r--src/boot.h24
-rw-r--r--src/kernel.c24
-rw-r--r--src/mem.h4
-rw-r--r--src/mem/vmm.c49
-rw-r--r--src/mmap.c45
-rw-r--r--src/mmap.h7
-rw-r--r--src/multiboot2.c49
37 files changed, 1478 insertions, 0 deletions
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 <multiboot2.h>
+#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 <gdt.h>
+
+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 <stdbool.h>
+#include <stdint.h>
+
+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 <gdt.h>
+
+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 <assert.h>
+#include <stdlib.h>
+
+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 <sys/io.h>
+
+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 <stddef.h>
+#include <stdint.h>
+
+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 <stdarg.h>
+#include <stdio.h>
+
+#include <devices/uart_16550.h>
+
+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 <gdt.h>
+#include <stdio.h>
+
+#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 <stdio.h>
+#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 <multiboot2.h>
+
+__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
+}