aboutsummaryrefslogtreecommitdiff
path: root/arch/i386
diff options
context:
space:
mode:
Diffstat (limited to 'arch/i386')
-rw-r--r--arch/i386/BUILD.bazel44
-rw-r--r--arch/i386/boot.s120
-rw-r--r--arch/i386/gdt.c20
-rw-r--r--arch/i386/gdt.h55
-rw-r--r--arch/i386/idt.h23
-rw-r--r--arch/i386/init.s49
-rw-r--r--arch/i386/isr.c50
-rw-r--r--arch/i386/lgdt.c25
-rw-r--r--arch/i386/lidt.c55
-rw-r--r--arch/i386/linker.ld54
-rw-r--r--arch/i386/paging.h59
-rw-r--r--arch/i386/sys/control.h24
-rw-r--r--arch/i386/sys/cpuid.h35
-rw-r--r--arch/i386/sys/io.h92
-rw-r--r--arch/i386/sys/syscall.h8
-rw-r--r--arch/i386/test_gdt.cc25
16 files changed, 738 insertions, 0 deletions
diff --git a/arch/i386/BUILD.bazel b/arch/i386/BUILD.bazel
new file mode 100644
index 0000000..2842a5b
--- /dev/null
+++ b/arch/i386/BUILD.bazel
@@ -0,0 +1,44 @@
+exports_files(
+ ["linker.ld"],
+ visibility = ["//visibility:public"],
+)
+
+cc_library(
+ name = "arch",
+ srcs = [
+ "boot.s",
+ "gdt.c",
+ "init.s",
+ "isr.c",
+ "lgdt.c",
+ "lidt.c",
+ ],
+ hdrs = [
+ "gdt.h",
+ "idt.h",
+ "paging.h",
+ ] + glob(["sys/*.h"]),
+ includes = ["."],
+ target_compatible_with = [
+ "@platforms//os:none",
+ ],
+ visibility = ["//visibility:public"],
+ deps = ["//lib/libk:k"],
+)
+
+# tests
+cc_test(
+ name = "test_gdt",
+ srcs = [
+ "gdt.c",
+ "gdt.h",
+ "test_gdt.cc",
+ ],
+ target_compatible_with = select({
+ "@platforms//os:none": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ }),
+ deps = [
+ "@googletest//:gtest_main",
+ ],
+)
diff --git a/arch/i386/boot.s b/arch/i386/boot.s
new file mode 100644
index 0000000..40b0389
--- /dev/null
+++ b/arch/i386/boot.s
@@ -0,0 +1,120 @@
+/* The magic field should contain this. */
+.set MULTIBOOT2_HEADER_MAGIC, 0xe85250d6
+/* This should be in %eax. */
+.set MULTIBOOT2_BOOTLOADER_MAGIC, 0x36d76289
+.set MULTIBOOT_ARCHITECTURE_I386, 0
+.set MULTIBOOT_HEADER_TAG_END, 0
+.set MULTIBOOT_HEADER_TAG_INFORMATION_REQUEST, 1
+.set MULTIBOOT_TAG_TYPE_MMAP, 6
+
+.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
+
+/* 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/i386/gdt.c b/arch/i386/gdt.c
new file mode 100644
index 0000000..3148096
--- /dev/null
+++ b/arch/i386/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 = (uint8_t)(base >> 16);
+ self->base_31_24 = (uint8_t)(base >> 24);
+
+ self->limit_15_0 = (limit <= 0xffff) ? (limit & 0xffff) : ((limit >> 12) & 0xffff);
+ self->limit_19_16 = 0xfu & (uint8_t)((limit <= 0xffff) ? 0 : (limit >> 28));
+
+ 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/i386/gdt.h b/arch/i386/gdt.h
new file mode 100644
index 0000000..2bdfb22
--- /dev/null
+++ b/arch/i386/gdt.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+enum Ring { Ring0 = 0x0, Ring1 = 0x1, Ring2 = 0x2, Ring3 = 0x3 };
+
+struct __attribute__((packed)) Access {
+ unsigned accessed : 1; /* if 0, is set by processor when accessed */
+ unsigned readwrite : 1; /* code seg: read toggle; data seg: write toggle */
+ unsigned direction : 1; /* code seg: conforming bit; data seg: direction bit */
+ unsigned executable : 1; /* executable bit */
+ unsigned segment : 1; /* true for code/data; false for gates/tss */
+ unsigned privilege : 2; /* descriptor privilege level */
+ unsigned present : 1; /* true for every active segment */
+};
+/* _Static_assert(sizeof(struct Access) == 1, "access byte size"); */
+
+static const struct Access null_access = {0, 0, 0, 0, 0, Ring0, 0};
+static const struct Access ktext_access = {0, 1, 0, 1, 1, Ring0, 1};
+static const struct Access kdata_access = {0, 1, 0, 0, 1, Ring0, 1};
+
+/* 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 */
+ unsigned 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, "segment descriptor size"); */
+
+void SegmentDescriptor(struct SegmentDescriptor_t *self, unsigned base, unsigned limit, uint8_t access);
+
+void gdt_install();
+
+enum SegmentIndex {
+ ktextDescriptor = 2 * sizeof(struct SegmentDescriptor_t),
+ kdataDescriptor = 3 * sizeof(struct SegmentDescriptor_t)
+};
diff --git a/arch/i386/idt.h b/arch/i386/idt.h
new file mode 100644
index 0000000..ca39bde
--- /dev/null
+++ b/arch/i386/idt.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <stdint.h>
+
+struct interrupt_frame {
+ uint32_t ip;
+ uint32_t cs;
+ uint32_t flags;
+ uint32_t sp;
+ uint32_t ss;
+};
+
+/* typedef void (*irq_handler)(); */
+
+/* isr.c */
+void abort_handler(struct interrupt_frame *frame);
+void syscall_handler(struct interrupt_frame *frame);
+void irq0x00(struct interrupt_frame *frame); /* timer interrupt */
+void irq0x01(struct interrupt_frame *frame); /* keyboard interrupt */
+void irq0x0c(struct interrupt_frame *frame); /* mouse interrupt */
+
+/* lidt.c */
+void idt_install();
diff --git a/arch/i386/init.s b/arch/i386/init.s
new file mode 100644
index 0000000..ad329bb
--- /dev/null
+++ b/arch/i386/init.s
@@ -0,0 +1,49 @@
+.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
+.extern gdt_install
+.extern idt_install
+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
+
+ # hardware init
+ call gdt_install # Global Descriptor Table
+ call idt_install # Interrupt Descriptor Table
+
+ # jump into kernel
+ 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/i386/isr.c b/arch/i386/isr.c
new file mode 100644
index 0000000..acc2961
--- /dev/null
+++ b/arch/i386/isr.c
@@ -0,0 +1,50 @@
+/*
+ * Interrupt Service Routines
+ */
+
+#include "idt.h"
+#include "sys/control.h"
+#include <stdio.h>
+
+__attribute__((interrupt)) void
+abort_handler(struct interrupt_frame *frame)
+{
+ printf("### system abort ###\n");
+ printf("# ip: %x cs=%x\n", frame->ip, frame->cs);
+ printf("# sp: %x ss=%x\n", frame->sp, frame->ss);
+ printf("# flags: %x\n", frame->flags);
+ abort();
+}
+
+__attribute__((interrupt)) void
+syscall_handler(__attribute__((unused)) struct interrupt_frame *frame)
+{
+ unsigned int n;
+ __asm__("mov %%eax, %0" : "=r"(n));
+ printf("syscall %x\n, n");
+ abort();
+}
+
+void pic_clear(unsigned char irq);
+
+__attribute__((interrupt)) void
+irq0x00(__attribute__((unused)) struct interrupt_frame *frame)
+{
+ pic_clear(0x00);
+}
+
+extern void ps2_keyboard_irq_handler();
+__attribute__((interrupt)) void
+irq0x01(__attribute__((unused)) struct interrupt_frame *frame)
+{
+ ps2_keyboard_irq_handler();
+ pic_clear(0x01);
+}
+
+extern void mouse_packet();
+__attribute__((interrupt)) void
+irq0x0c(__attribute__((unused)) struct interrupt_frame *frame)
+{
+ mouse_packet();
+ pic_clear(0x0c);
+}
diff --git a/arch/i386/lgdt.c b/arch/i386/lgdt.c
new file mode 100644
index 0000000..473a91d
--- /dev/null
+++ b/arch/i386/lgdt.c
@@ -0,0 +1,25 @@
+#include "gdt.h"
+
+struct __attribute__((packed)) Pointer {
+ uint16_t limit;
+ uint32_t base;
+};
+
+static struct SegmentDescriptor_t segments[8] __attribute__((aligned(32)));
+
+void
+gdt_install()
+{
+ const struct Pointer ptr = {sizeof(segments) - 1, (unsigned)&segments};
+ SegmentDescriptor(&segments[0], 0, 0, 0); /* null segment */
+ SegmentDescriptor(&segments[2], 0, 0xffffffff, 0x9a); /* ktext segment */
+ SegmentDescriptor(&segments[3], 0, 0xffffffff, 0x92); /* kdata segment */
+
+ __asm__("lgdt (%0)" : : "a"(&ptr));
+
+ /* load the kernel data segment */
+ __asm__("mov %0, %%ds; mov %0, %%es; mov %0, %%fs; mov %0, %%gs; mov %0, %%ss" : : "ax"(kdataDescriptor));
+
+ /* load the kernel code segment */
+ __asm__("ljmp %0, $1f\n1:" : : "i"(ktextDescriptor));
+}
diff --git a/arch/i386/lidt.c b/arch/i386/lidt.c
new file mode 100644
index 0000000..86567f8
--- /dev/null
+++ b/arch/i386/lidt.c
@@ -0,0 +1,55 @@
+#include "idt.h"
+#include <stdint.h>
+
+struct __attribute__((packed)) Pointer {
+ uint16_t limit;
+ uint32_t base;
+};
+
+enum Type {
+ Null = 0,
+ Intr = 0x8e /* 1000 1110 32-bit interrupt */
+};
+
+struct __attribute__((packed)) Gate_t {
+ uint16_t offset_15_0; /* segment offset low */
+ uint16_t selector; /* code segment selector */
+ uint8_t __unused; /* unused in protected mode */
+ uint8_t type; /* interrupt type */
+ uint16_t offset_31_16; /* segment offset high */
+};
+/* _Static_assert(sizeof(struct Gate_t) == 8, "interrupt gate size"); */
+
+void
+Gate(struct Gate_t *entry, void (*f)(struct interrupt_frame *), uint16_t selector)
+{
+ uint32_t f_addr = (uint32_t)f;
+ entry->offset_15_0 = f_addr & 0xffff;
+ entry->offset_31_16 = (uint16_t)(f_addr >> 16) & 0xffff;
+ entry->selector = selector;
+ entry->__unused = 0;
+ entry->type = Intr;
+}
+
+static struct Gate_t interrupt_table[256] __attribute((aligned(4096)));
+
+void
+idt_install()
+{
+ int i;
+ const struct Pointer ptr = {sizeof(interrupt_table) - 1, (unsigned)&interrupt_table};
+
+ /* exceptions 0x00~0x13 */
+ for (i = 0; i <= 0x13; ++i) Gate(&interrupt_table[i], &abort_handler, 0x10);
+
+ /* irq 0x20~0x2f */
+ for (i = 0x22; i <= 0x2f; ++i) Gate(&interrupt_table[i], &abort_handler, 0x10);
+ Gate(&interrupt_table[0x20], &irq0x00, 0x10);
+ Gate(&interrupt_table[0x21], &irq0x01, 0x10);
+ Gate(&interrupt_table[0x2c], &irq0x0c, 0x10);
+
+ /* syscall 0x80 */
+ Gate(&interrupt_table[0x80], &syscall_handler, 0x10);
+
+ __asm__("lidt (%0)" : : "a"(&ptr));
+}
diff --git a/arch/i386/linker.ld b/arch/i386/linker.ld
new file mode 100644
index 0000000..61a3be9
--- /dev/null
+++ b/arch/i386/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/i386/paging.h b/arch/i386/paging.h
new file mode 100644
index 0000000..f5bfa78
--- /dev/null
+++ b/arch/i386/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;
+};
+/* TODO _Static_assert(sizeof(struct DirectoryEntry) == 4, "DirectoryEntry size"); */
+
+/* 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;
+};
+/* TODO _Static_assert(sizeof(struct DirectoryEntry4MB) == 4, "DirectoryEntry4M size"); */
+
+/* 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;
+};
+/* TODO _Static_assert(sizeof(struct TableEntry) == 4, "TableEntry size"); */
diff --git a/arch/i386/sys/control.h b/arch/i386/sys/control.h
new file mode 100644
index 0000000..89ab067
--- /dev/null
+++ b/arch/i386/sys/control.h
@@ -0,0 +1,24 @@
+#pragma once
+
+static __inline__ void
+abort()
+{
+ /* Symbol h is already defined?
+__asm__(R"(cli
+h: hlt
+jmp h)");
+*/
+ __asm__("cli; hlt");
+}
+
+static __inline__ void
+enable_interrupts()
+{
+ __asm__("sti");
+}
+
+static __inline__ void
+disable_interrupts()
+{
+ __asm__("cli");
+}
diff --git a/arch/i386/sys/cpuid.h b/arch/i386/sys/cpuid.h
new file mode 100644
index 0000000..6613967
--- /dev/null
+++ b/arch/i386/sys/cpuid.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <cpuid.h>
+
+struct CPUVersion {
+ unsigned int stepping : 4;
+ unsigned int model : 4;
+ unsigned int family : 4;
+ unsigned int type : 2;
+ unsigned int __unused_1 : 2;
+ unsigned int model_ex : 4;
+ unsigned int family_ex : 8;
+ unsigned int __unused_2 : 4;
+} __attribute__((packed, aligned(__alignof__(unsigned int))));
+/* FIXME _Static_assert(sizeof(struct CPUVersion) == sizeof(unsigned int), "cpuid version struct size"); */
+
+unsigned int
+family(const struct CPUVersion v)
+{
+ if (v.family == 0x0f) return v.family + v.family_ex;
+ else
+ return v.family;
+}
+
+unsigned int
+model(const struct CPUVersion v)
+{
+ switch (v.family) {
+ case 0x06:
+ case 0x0f:
+ return ((unsigned int)v.model_ex << 4) | v.model;
+ default:
+ return v.model;
+ }
+}
diff --git a/arch/i386/sys/io.h b/arch/i386/sys/io.h
new file mode 100644
index 0000000..4ff5d85
--- /dev/null
+++ b/arch/i386/sys/io.h
@@ -0,0 +1,92 @@
+#pragma once
+
+/* port listings */
+enum UART {
+ COM1 = 0x3f8,
+ COM2 = 0x2f8,
+ COM3 = 0x3e8,
+ COM4 = 0x2e8,
+ COM5 = 0x5f8,
+ COM6 = 0x4f8,
+ COM7 = 0x5e8,
+ COM8 = 0x4e8
+};
+
+static __inline__ void
+outb(unsigned char val, unsigned short port)
+{
+ __asm__("outb %0,%1" : : "a"(val), "dN"(port));
+}
+
+static __inline__ void
+outw(unsigned short val, unsigned short port)
+{
+ __asm__("outw %0,%1" : : "a"(val), "dN"(port));
+}
+
+static __inline__ void
+outl(unsigned int val, unsigned short port)
+{
+ __asm__("outl %0,%1" : : "a"(val), "dN"(port));
+}
+
+static __inline__ unsigned char
+inb(unsigned short port)
+{
+ unsigned char val;
+ __asm__("inb %1,%0" : "=a"(val) : "dN"(port));
+ return val;
+}
+
+static __inline__ unsigned short
+inw(unsigned short port)
+{
+ unsigned short val;
+ __asm__("inw %1,%0" : "=a"(val) : "dN"(port));
+ return val;
+}
+
+static __inline__ unsigned int
+inl(unsigned short port)
+{
+ unsigned int val;
+ __asm__("inl %1,%0" : "=a"(val) : "dN"(port));
+ return val;
+}
+
+static __inline__ void
+outsb(unsigned short port, const void *__buf, unsigned long __n)
+{
+ __asm__("cld; rep; outsb" : "+S"(__buf), "+c"(__n) : "d"(port));
+}
+
+static __inline__ void
+outsw(unsigned short port, const void *__buf, unsigned long __n)
+{
+ __asm__("cld; rep; outsw" : "+S"(__buf), "+c"(__n) : "d"(port));
+}
+
+static __inline__ void
+outsl(unsigned short port, const void *__buf, unsigned long __n)
+{
+ __asm__("cld; rep; outsl" : "+S"(__buf), "+c"(__n) : "d"(port));
+}
+
+static __inline__ void
+insb(unsigned short port, void *__buf, unsigned long __n)
+{
+ __asm__("cld; rep; insb" : "+D"(__buf), "+c"(__n) : "d"(port));
+}
+
+static __inline__ void
+insw(unsigned short port, void *__buf, unsigned long __n)
+{
+ __asm__("cld; rep; insw" : "+D"(__buf), "+c"(__n) : "d"(port));
+}
+
+static __inline__ void
+insl(unsigned short port, void *__buf, unsigned long __n)
+{
+ __asm__("cld; rep; insl" : "+D"(__buf), "+c"(__n) : "d"(port));
+}
+
diff --git a/arch/i386/sys/syscall.h b/arch/i386/sys/syscall.h
new file mode 100644
index 0000000..9e62c89
--- /dev/null
+++ b/arch/i386/sys/syscall.h
@@ -0,0 +1,8 @@
+#pragma once
+
+static inline int
+syscall(int number)
+{
+ asm volatile("int $0x80");
+ return 0;
+}
diff --git a/arch/i386/test_gdt.cc b/arch/i386/test_gdt.cc
new file mode 100644
index 0000000..3501ae9
--- /dev/null
+++ b/arch/i386/test_gdt.cc
@@ -0,0 +1,25 @@
+#include <gtest/gtest.h>
+
+extern "C" {
+#include "gdt.h"
+}
+
+TEST(i686GDT, KnownAccessByteValues)
+{
+ EXPECT_EQ(*(uint8_t *)&null_access, 0x00);
+ EXPECT_EQ(*(uint8_t *)&ktext_access, 0x9a);
+ EXPECT_EQ(*(uint8_t *)&kdata_access, 0x92);
+}
+
+TEST(i686GDT, NullSegmentDescriptor)
+{
+ struct SegmentDescriptor_t d;
+ SegmentDescriptor(&d, 0, 0, 0);
+ EXPECT_EQ(*(uint64_t *)&d, 0);
+}
+
+TEST(i686GDT, SegmentIndex)
+{
+ EXPECT_EQ(ktextDescriptor, 0x10);
+ EXPECT_EQ(kdataDescriptor, 0x18);
+}