aboutsummaryrefslogtreecommitdiff
path: root/i686
diff options
context:
space:
mode:
Diffstat (limited to 'i686')
-rw-r--r--i686/Makefile17
-rw-r--r--i686/boot.S89
-rw-r--r--i686/gdt.c20
-rw-r--r--i686/gdt.h55
-rw-r--r--i686/idt.h18
-rw-r--r--i686/init.s42
-rw-r--r--i686/lgdt.c35
-rw-r--r--i686/lidt.c46
-rw-r--r--i686/linker.ld54
-rw-r--r--i686/macros.s25
-rw-r--r--i686/meson.build9
-rw-r--r--i686/paging.h59
-rw-r--r--i686/sys/control.h9
-rw-r--r--i686/sys/cpuid.h35
-rw-r--r--i686/sys/io.h79
-rw-r--r--i686/test/gdt.c20
-rw-r--r--i686/toolchain.ini18
-rw-r--r--i686/toolchain.mk2
18 files changed, 632 insertions, 0 deletions
diff --git a/i686/Makefile b/i686/Makefile
new file mode 100644
index 0000000..52295a8
--- /dev/null
+++ b/i686/Makefile
@@ -0,0 +1,17 @@
+include ../${ARCH}/toolchain.mk
+include ../rules.mk
+
+CCFLAGS += -I../grub/include
+
+all: arch.a
+
+arch,SRCS = boot.S init.s \
+ gdt.c lgdt.c \
+ lidt.c
+arch,OBJS = $(filter %.o,$(arch,SRCS:%.S=%.o) $(arch,SRCS:%.s=%.o) $(arch,SRCS:%.c=%.o))
+arch.a: ${arch,OBJS}
+ @echo ' AR $@'
+ @${AR} ${ARFLAGS} $@ $^
+
+clean:
+ @rm -rf ${arch,OBJS}
diff --git a/i686/boot.S b/i686/boot.S
new file mode 100644
index 0000000..1eea9a3
--- /dev/null
+++ b/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/i686/gdt.c b/i686/gdt.c
new file mode 100644
index 0000000..c0898f3
--- /dev/null
+++ b/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/i686/gdt.h b/i686/gdt.h
new file mode 100644
index 0000000..9148c3d
--- /dev/null
+++ b/i686/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 {
+ 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);
+
+void gdt_install();
+
+enum SegmentIndex {
+ ktextDescriptor = 2 * sizeof(struct SegmentDescriptor_t),
+ kdataDescriptor = 3 * sizeof(struct SegmentDescriptor_t),
+};
diff --git a/i686/idt.h b/i686/idt.h
new file mode 100644
index 0000000..22dc6de
--- /dev/null
+++ b/i686/idt.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <stdint.h>
+
+struct interrupt_frame {
+ uint32_t ip;
+ uint32_t cs;
+ uint32_t flags;
+ uint32_t sp;
+ uint32_t ss;
+};
+
+void abort_handler(struct interrupt_frame *frame);
+void interrupt_handler(struct interrupt_frame *frame);
+void interrupt_handler_e(struct interrupt_frame *frame, uint32_t error);
+void syscall_handler(struct interrupt_frame *frame);
+
+void idt_install();
diff --git a/i686/init.s b/i686/init.s
new file mode 100644
index 0000000..92710df
--- /dev/null
+++ b/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/i686/lgdt.c b/i686/lgdt.c
new file mode 100644
index 0000000..10781db
--- /dev/null
+++ b/i686/lgdt.c
@@ -0,0 +1,35 @@
+#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()
+{
+ SegmentDescriptor(&segments[0], 0, 0, 0); // null segment
+ SegmentDescriptor(&segments[2], 0, 0xffffffff, 0x9a); // ktext
+ SegmentDescriptor(&segments[3], 0, 0xffffffff, 0x92); // kdata
+
+ const 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/i686/lidt.c b/i686/lidt.c
new file mode 100644
index 0000000..3e261ac
--- /dev/null
+++ b/i686/lidt.c
@@ -0,0 +1,46 @@
+#include "idt.h"
+#include <stdint.h>
+
+struct __attribute__((packed)) Pointer {
+ uint16_t limit;
+ uint32_t base;
+};
+
+enum Type {
+ Null = 0,
+ Intr = 0b10001110, // 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);
+
+void
+Gate(struct Gate_t *entry, void *f, uint16_t selector)
+{
+ uint32_t f_addr = (uint32_t)f;
+ entry->offset_15_0 = f_addr & 0xffff;
+ entry->offset_31_16 = (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()
+{
+ for (int i = 0; i <= 0x2f; ++i) Gate(&interrupt_table[i], &interrupt_handler, 0x10);
+ Gate(&interrupt_table[0x80], &abort_handler, 0x10);
+
+ const struct Pointer ptr = {.limit = sizeof(interrupt_table) - 1, .base = (unsigned)&interrupt_table};
+ asm volatile("lidt (%0)" : : "a"(&ptr));
+
+ asm volatile("sti");
+}
diff --git a/i686/linker.ld b/i686/linker.ld
new file mode 100644
index 0000000..61a3be9
--- /dev/null
+++ b/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/i686/macros.s b/i686/macros.s
new file mode 100644
index 0000000..a9b8b4d
--- /dev/null
+++ b/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/i686/meson.build b/i686/meson.build
new file mode 100644
index 0000000..f6bed8e
--- /dev/null
+++ b/i686/meson.build
@@ -0,0 +1,9 @@
+arch = declare_dependency(
+ sources: ['boot.S', 'init.s',
+ 'gdt.c', 'lgdt.c', 'lidt.c'
+ ],
+ include_directories: 'include',
+ compile_args: '-mgeneral-regs-only'
+)
+
+test('GDT', executable('gdt', ['test/gdt.c', 'gdt.c'], include_directories: 'include', native: true))
diff --git a/i686/paging.h b/i686/paging.h
new file mode 100644
index 0000000..cff0506
--- /dev/null
+++ b/i686/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/i686/sys/control.h b/i686/sys/control.h
new file mode 100644
index 0000000..a40a67f
--- /dev/null
+++ b/i686/sys/control.h
@@ -0,0 +1,9 @@
+#pragma once
+
+static void
+abort()
+{
+ asm volatile(R"(cli
+h: hlt
+jmp h)");
+}
diff --git a/i686/sys/cpuid.h b/i686/sys/cpuid.h
new file mode 100644
index 0000000..f2ffe37
--- /dev/null
+++ b/i686/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))));
+_Static_assert(sizeof(struct CPUVersion) == sizeof(unsigned int));
+
+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/i686/sys/io.h b/i686/sys/io.h
new file mode 100644
index 0000000..74d4950
--- /dev/null
+++ b/i686/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/i686/test/gdt.c b/i686/test/gdt.c
new file mode 100644
index 0000000..2947b42
--- /dev/null
+++ b/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/i686/toolchain.ini b/i686/toolchain.ini
new file mode 100644
index 0000000..5114cfe
--- /dev/null
+++ b/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 = ['-ffreestanding']
+c_link_args = ['-nostdlib']
+
diff --git a/i686/toolchain.mk b/i686/toolchain.mk
index 884369c..c41c784 100644
--- a/i686/toolchain.mk
+++ b/i686/toolchain.mk
@@ -1,10 +1,12 @@
# define compiler, linker, archiver and strip and their flags
#
+AS = i686-elf-as
CC = i686-elf-gcc
CCFLAGS = -Wall -Wextra -Wpedantic -fanalyzer -ffreestanding
LD = i686-elf-ld
LDFLAGS = -nostdlib
AR = i686-elf-ar
+ARFLAGS = r
STRIP = i686-elf-strip