diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/i386/BUILD.bazel | 44 | ||||
-rw-r--r-- | arch/i386/boot.s | 120 | ||||
-rw-r--r-- | arch/i386/gdt.c | 20 | ||||
-rw-r--r-- | arch/i386/gdt.h | 55 | ||||
-rw-r--r-- | arch/i386/idt.h | 23 | ||||
-rw-r--r-- | arch/i386/init.s | 49 | ||||
-rw-r--r-- | arch/i386/isr.c | 50 | ||||
-rw-r--r-- | arch/i386/lgdt.c | 25 | ||||
-rw-r--r-- | arch/i386/lidt.c | 55 | ||||
-rw-r--r-- | arch/i386/linker.ld | 54 | ||||
-rw-r--r-- | arch/i386/paging.h | 59 | ||||
-rw-r--r-- | arch/i386/sys/control.h | 24 | ||||
-rw-r--r-- | arch/i386/sys/cpuid.h | 35 | ||||
-rw-r--r-- | arch/i386/sys/io.h | 92 | ||||
-rw-r--r-- | arch/i386/sys/syscall.h | 8 | ||||
-rw-r--r-- | arch/i386/test_gdt.cc | 25 |
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); +} |