Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[kernel.c]---------------------
- #define PIC1 0x20
- #define PIC2 0xA0
- #define PIC1_COMMAND PIC1
- #define PIC1_DATA (PIC1+1)
- #define PIC2_COMMAND PIC2
- #define PIC2_DATA (PIC2+1)
- #define ICW1_ICW4 0x01
- #define ICW1_SINGLE 0x02
- #define ICW1_INTERVAL4 0x04
- #define ICW1_LEVEL 0x08
- #define ICW1_INIT 0x10
- #define ICW4_8086 0x01
- #define ICW4_AUTO 0x02
- #define ICW4_BUF_SLAVE 0x08
- #define ICW4_BUF_MASTER 0x0C
- #define ICW4_SFNM 0x10
- typedef unsigned char uint8_t;
- typedef unsigned short uint16_t;
- typedef unsigned int uint32_t;
- typedef unsigned long long uint64_t;
- typedef char int8_t;
- typedef short int16_t;
- typedef int int32_t;
- typedef long long int64_t;
- typedef enum {
- /* Traps */
- IV_DIV0 = 0x00, // Divide by 0
- IV_RSV1 = 0x01, // Reserved
- IV_NMI = 0x02, // NMI Interrupt
- IV_BRKP = 0x03, // Breakpoint (INT3)
- IV_OVRF = 0x04, // Overflow (INTO)
- IV_BOUNDS = 0x05, // Bounds range exceeded (BOUND)
- IV_INVALOP = 0x06, // Invalid opcode (UD2)
- IV_DEVAVAIL = 0x07, // Device not available (WAIT/FWAIT)
- IV_DFAULT = 0x08, // Double fault
- IV_SEGOVRR = 0x09, // Coprocessor segment overrun
- IV_INVALTSS = 0x0A, // Invalid TSS
- IV_SEGNPRES = 0x0B, // Segment not present
- IV_STACKFLT = 0x0C, // Stack-segment fault
- IV_GPFLT = 0x0D, // General protection fault
- IV_PAGEFLT = 0x0E, // Page fault
- IV_RSV2 = 0x0F, // Reserved
- IV_FPUERR = 0x10, // x87 FPU error
- IV_ALIGNCHK = 0x11, // Alignment check
- IV_MCHK = 0x12, // Machine check
- IV_SIMDFP = 0x13, // SIMD Floating-Point Exception
- /* 0x14 - 0x1F Reserved */
- /* Interrupts */
- IV_TIMER = 0x20,
- IV_KEYBOARD = 0x21,
- IV_PANIC = 0x63,
- IV_COUNT = 0x100
- } interrupt_vector;
- typedef union interrupt_descriptor interrupt_descriptor;
- struct idtr {
- uint16_t limit;
- uint32_t base;
- } __attribute__((packed));
- union interrupt_descriptor {
- struct {
- uint64_t offsetl : 16,
- segsel : 16,
- reserved : 8,
- gate_type : 4,
- zero : 1,
- dpl : 2,
- present : 1,
- offseth : 16;
- } __attribute__((packed));
- uint64_t value;
- };
- struct gdtr {
- uint16_t limit;
- uint32_t base;
- } __attribute__((packed));
- typedef union {
- struct {
- uint64_t limitl : 16,
- basel : 24,
- access : 8,
- limith : 4,
- flags : 4,
- baseh : 8;
- } __attribute__((packed));
- uint64_t value;
- } segment_descriptor;
- interrupt_descriptor idt[256];
- extern void* interrupt_entries;
- extern segment_descriptor gdt;
- uint8_t ioport_read8(uint32_t port) {
- uint8_t out;
- asm("in %%dx, %%al"
- : "=a"(out)
- : "d"(port)
- :
- );
- return out;
- }
- void ioport_write8(uint32_t port, uint8_t b) {
- asm("out %%al, %%dx"
- :
- : "a"(b), "d"(port)
- :
- );
- }
- static interrupt_descriptor idte_create(uint32_t off, uint8_t gate_type, uint8_t dpl) {
- interrupt_descriptor desc;
- desc.offsetl = off & 0xFFFF;
- desc.offseth = (off >> 16) &0xFFFF;
- desc.segsel = 8;
- desc.gate_type = gate_type & 0xF;
- desc.zero = 0;
- desc.dpl = dpl & 0x3;
- desc.present = 1;
- desc.reserved = 0;
- return desc;
- }
- void eoi(unsigned char irq)
- {
- uint32_t pic1_c = 0x20;
- uint32_t pic2_c = 0xA0;
- if(irq >= 8) {
- ioport_write8(pic2_c, 0x20);
- }
- ioport_write8(pic1_c,0x20);
- }
- void gdt_init() {
- struct gdtr gdtreg;
- gdtreg.limit = 18;
- gdtreg.base = (uint32_t)&gdt;
- asm("lgdt %0" :: "m"(gdtreg));
- }
- void idt_init() {
- struct idtr idtreg;
- uint32_t** int_entries = (uint32_t**) &interrupt_entries;
- for (int i = 0; i < IV_COUNT; ++i) {
- interrupt_descriptor* idte = &idt[i];
- interrupt_descriptor desc = idte_create(int_entries[i], 0xE, 0b00);
- *idte = desc;
- }
- idtreg.limit = (uint16_t) (IV_COUNT*sizeof(interrupt_descriptor)) - 1;
- idtreg.base = (uint32_t) idt;
- asm("lidt %0" :: "m"(idtreg));
- }
- void interrupt_handler(uint32_t vector) {
- eoi(vector);
- }
- void picremap(int offset_master, int offset_slave) {
- uint8_t a1, a2;
- a1 = ioport_read8(PIC1_DATA);
- a2 = ioport_read8(PIC2_DATA);
- ioport_write8(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4);
- ioport_write8(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4);
- ioport_write8(PIC1_DATA, offset_master);
- ioport_write8(PIC2_DATA, offset_slave);
- ioport_write8(PIC1_DATA, 4);
- ioport_write8(PIC2_DATA, 2);
- ioport_write8(PIC1_DATA, ICW4_8086);
- ioport_write8(PIC2_DATA, ICW4_8086);
- ioport_write8(PIC1_DATA, 0x00);
- ioport_write8(PIC2_DATA, 0x00);
- }
- void kernel_main() {
- gdt_init();
- idt_init();
- picremap(0x20, 0x28);
- asm("sti");
- while (1) {
- }
- }
- --[gdtable.s]---------------------
- .globl gdt
- .section .data
- gdt:
- .short 0,0,0,0 # NULL Deskriptor
- .short 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
- .short 0x0000 # base address=0
- .short 0x9A00 # code read/exec
- .short 0x00CF # granularity=4096,
- .short 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb)
- .short 0x0000 # base address=0
- .short 0x9200 # data read/write
- .short 0x00CF # granularity=4096,
- --[ientry.s]---------------------
- .globl interrupt_entries
- .extern interrupt_handler
- .section .text
- .macro IRQ vector has_push has_error
- .align 8
- interrupt_entry_\vector:
- .if \has_push == 0
- pushl $0
- .endif
- pushl %edx
- pushl %ecx
- pushl %eax
- #pushl %esp
- pushl $\vector
- call interrupt_handler
- #addl $8, %esp
- addl $4, %esp
- popl %eax
- popl %ecx
- popl %edx
- addl $4, %esp
- iret
- .endm
- IRQ 0, 0, 0
- IRQ 1, 0, 0
- IRQ 2, 0, 0
- IRQ 3, 0, 0
- IRQ 4, 0, 0
- IRQ 5, 0, 0
- IRQ 6, 0, 0
- IRQ 7, 0, 0
- IRQ 8, 1, 1
- IRQ 9, 0, 0
- IRQ 10, 1, 1
- IRQ 11, 1, 1
- IRQ 12, 1, 1
- IRQ 13, 1, 1
- IRQ 14, 1, 1
- IRQ 15, 0, 0
- IRQ 16, 0, 0
- IRQ 17, 1, 1
- IRQ 18, 0, 0
- IRQ 19, 0, 0
- IRQ 20, 0, 0
- IRQ 21, 0, 1
- IRQ 22, 0, 0
- IRQ 23, 0, 0
- IRQ 24, 0, 0
- IRQ 25, 0, 0
- IRQ 26, 0, 0
- IRQ 27, 0, 0
- IRQ 28, 0, 0
- IRQ 29, 0, 1
- IRQ 30, 0, 1
- .altmacro
- .section .text
- .set i,31
- .rept 225
- IRQ %i, 0, 0
- .set i,i+1
- .endr
- .section .data
- interrupt_entries:
- .macro interrupt_vector vec
- .long interrupt_entry_\vec
- .endm
- .set i,0
- .rept 256
- interrupt_vector %i
- .set i, i+1
- .endr
- --[bootloader.s]---------------------
- /* Declare constants for the multiboot header. */
- .set ALIGN, 1<<0 /* align loaded modules on page boundaries */
- .set MEMINFO, 1<<1 /* provide memory map */
- .set FLAGS, ALIGN | MEMINFO /* this is the Multiboot 'flag' field */
- .set MAGIC, 0x1BADB002 /* 'magic number' lets bootloader find the header */
- .set CHECKSUM, -(MAGIC + FLAGS) /* checksum of above, to prove we are multiboot */
- /*
- Declare a multiboot header that marks the program as a kernel. These are magic
- values that are documented in the multiboot standard. The bootloader will
- search for this signature in the first 8 KiB of the kernel file, aligned at a
- 32-bit boundary. The signature is in its own section so the header can be
- forced to be within the first 8 KiB of the kernel file.
- */
- .section .multiboot
- .align 4
- .long MAGIC
- .long FLAGS
- .long CHECKSUM
- /*
- The multiboot standard does not define the value of the stack pointer register
- (esp) and it is up to the kernel to provide a stack. This allocates room for a
- small stack by creating a symbol at the bottom of it, then allocating 16384
- bytes for it, and finally creating a symbol at the top. The stack grows
- downwards on x86. The stack is in its own section so it can be marked nobits,
- which means the kernel file is smaller because it does not contain an
- uninitialized stack. The stack on x86 must be 16-byte aligned according to the
- System V ABI standard and de-facto extensions. The compiler will assume the
- stack is properly aligned and failure to align the stack will result in
- undefined behavior.
- */
- .section .bss
- .align 16
- stack_bottom:
- .skip 16384 # 16 KiB
- stack_top:
- /*
- The linker script specifies _start as the entry point to the kernel and the
- bootloader will jump to this position once the kernel has been loaded. It
- doesn't make sense to return from this function as the bootloader is gone.
- */
- .section .text
- .global _start
- .type _start, @function
- _start:
- /*
- The bootloader has loaded us into 32-bit protected mode on a x86
- machine. Interrupts are disabled. Paging is disabled. The processor
- state is as defined in the multiboot standard. The kernel has full
- control of the CPU. The kernel can only make use of hardware features
- and any code it provides as part of itself. There's no printf
- function, unless the kernel provides its own <stdio.h> header and a
- printf implementation. There are no security restrictions, no
- safeguards, no debugging mechanisms, only what the kernel provides
- itself. It has absolute and complete power over the
- machine.
- */
- /*
- To set up a stack, we set the esp register to point to the top of the
- stack (as it grows downwards on x86 systems). This is necessarily done
- in assembly as languages such as C cannot function without a stack.
- */
- mov $stack_top, %esp
- /*
- This is a good place to initialize crucial processor state before the
- high-level kernel is entered. It's best to minimize the early
- environment where crucial features are offline. Note that the
- processor is not fully initialized yet: Features such as floating
- point instructions and instruction set extensions are not initialized
- yet. The GDT should be loaded here. Paging should be enabled here.
- C++ features such as global constructors and exceptions will require
- runtime support to work as well.
- */
- /*
- Enter the high-level kernel. The ABI requires the stack is 16-byte
- aligned at the time of the call instruction (which afterwards pushes
- the return pointer of size 4 bytes). The stack was originally 16-byte
- aligned above and we've pushed a multiple of 16 bytes to the
- stack since (pushed 0 bytes so far), so the alignment has thus been
- preserved and the call is well defined.
- */
- call kernel_main
- /*
- If the system has nothing more to do, put the computer into an
- infinite loop. To do that:
- 1) Disable interrupts with cli (clear interrupt enable in eflags).
- They are already disabled by the bootloader, so this is not needed.
- Mind that you might later enable interrupts and return from
- kernel_main (which is sort of nonsensical to do).
- 2) Wait for the next interrupt to arrive with hlt (halt instruction).
- Since they are disabled, this will lock up the computer.
- 3) Jump to the hlt instruction if it ever wakes up due to a
- non-maskable interrupt occurring or due to system management mode.
- */
- cli
- 1: hlt
- jmp 1b
- /*
- Set the size of the _start symbol to the current location '.' minus its start.
- This is useful when debugging or when you implement call tracing.
- */
- .size _start, . - _start
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement