Initial import from mbr_asm project

This commit is contained in:
Mathieu Maret 2018-07-20 15:41:58 +02:00
commit 2c251fa51c
28 changed files with 1495 additions and 0 deletions

43
Makefile Normal file
View File

@ -0,0 +1,43 @@
# Used to generated .d file that deal with header dependencies
CPPFLAGS = -MMD
AS=nasm
ASFLAGS += -f elf32
LDFLAGS += -m32 -nostdlib -static -fno-common -fno-use-cxa-atexit -fno-exceptions -fno-non-call-exceptions -fno-weak -fno-rtti -fno-stack-protector
CFLAGS += -m32 -Wall -Wextra -Werror -ffreestanding -fno-exceptions -fno-pie -fno-stack-protector
CXXFLAGS += -m32 -Wall -Wextra -Werror -ffreestanding -fno-exceptions -fno-rtti -fno-pie
SUBDIRS := core drivers
CPPFLAGS += $(foreach dir, $(SUBDIRS), -I$(dir))
asmsrc=$(wildcard *.asm)
asmobj=$(asmsrc:%.asm=%.o)
csrc=$(shell find $(SUBDIRS) -type f -name "*.c")# $(wildcard *.c)
cobj=$(csrc:%.c=%.o)
deps = $(csrc:%.c=%.d)
kernel:$(asmobj) $(cobj) linker.ld
$(CXX) $(LDFLAGS) $(cobj) $(asmobj) -o $@ -T linker.ld
fd.img: kernel
dd if=/dev/zero of=$@ bs=512 count=2880
dd if=$< of=$@ conv=notrunc
#https://gcc.gnu.org/onlinedocs/gcc/x86-Function-Attributes.html#x86-Function-Attributes
core/exception_handler.o:core/exception_handler.c
$(CC) $(CPPFLAGS) $(CFLAGS) -mgeneral-regs-only -c $< -o $@
core/irq_handler.o:core/irq_handler.c
$(CC) $(CPPFLAGS) $(CFLAGS) -mgeneral-regs-only -c $< -o $@
%.o:%.asm
$(AS) $(ASFLAGS) -o $@ $<
test:kernel
qemu-system-x86_64 -fda $<
clean:
$(RM) kernel $(asmobj) $(cobj) $(deps)
ifneq ($(MAKECMDGOALS),clean)
-include $(deps)
endif

24
core/exception.c Normal file
View File

@ -0,0 +1,24 @@
#include "exception.h"
#include "idt.h"
#include "interrupt.h"
#include "irq.h"
exception_handler exception_handler_array[EXCEPTION_NUM] = {
NULL,
};
int exceptionSetRoutine(int exception, exception_handler handler)
{
uint32_t flags;
if ((exception < 0) || exception >= EXCEPTION_NUM)
return -1;
disable_IRQs(flags);
exception_handler_array[exception] = handler;
idt_set_handler(EXCEPTION_INTERRUPT_BASE_ADDRESS + exception, (unsigned int)handler,
0);
restore_IRQs(flags);
return 0;
}

46
core/exception.h Normal file
View File

@ -0,0 +1,46 @@
#pragma once
#include "interrupt.h"
#include "types.h"
#define EXCEPTION_INTERRUPT_BASE_ADDRESS 0
// list and description https://wiki.osdev.org/Exceptions
#define EXCEPTION_DIVIDE_ZERO 0
#define EXCEPTION_DEBUG 1
#define EXCEPTION_NMI 2
#define EXCEPTION_BREAKPOINT 3
#define EXCEPTION_OVERFLOW 4
#define EXCEPTION_BOUND_RANGE_EXCEEDED 5
#define EXCEPTION_INVALID_OPCODE 6
#define EXCEPTION_DEVICE_NOT_AVAILABLE 7
#define EXCEPTION_DOUBLE_FAULT 8
#define EXCEPTION_COPRO_OVERRUN 9
#define EXCEPTION_INVALID_TSS 10
#define EXCEPTION_SEGMENT_NOT_PRESENT 11
#define EXCEPTION_STACK_SEGMENT_FAULT 12
#define EXCEPTION_GENERAL_PROTECTION_FAULT 13
#define EXCEPTION_PAGE_FAULT 14
#define EXCEPTION_RESERVED_1 15
#define EXCEPTION_X87_FP_EXCEPTION 16
#define EXCEPTION_ALIGNMENT_CHECK 17
#define EXCEPTION_MACHINE_CHECK 18
#define EXCEPTION_SIMD_FP 19
#define EXCEPTION_VIRTUALIZATION 20
#define EXCEPTION_RESERVED_2 21
#define EXCEPTION_RESERVED_3 22
#define EXCEPTION_RESERVED_4 23
#define EXCEPTION_RESERVED_5 24
#define EXCEPTION_RESERVED_6 25
#define EXCEPTION_RESERVED_7 26
#define EXCEPTION_RESERVED_8 27
#define EXCEPTION_RESERVED_9 28
#define EXCEPTION_RESERVED_10 29
#define EXCEPTION_SECURITY 30
#define EXCEPTION_RESERVED_11 31
#define EXCEPTION_NUM 32
typedef void (*exception_handler) (struct interrupt_frame *frame, ulong error_code);
int exceptionSetRoutine(int exception, exception_handler handler);

12
core/exception_handler.c Normal file
View File

@ -0,0 +1,12 @@
#include "exception.h"
#include "vga.h"
// Need GCC > 6
__attribute__((interrupt)) void print_handler(struct interrupt_frame *frame, ulong error_code)
{
printStringDetails("EXCEPTION", RED, BLACK, 0, VGA_HEIGHT - 1);
printIntDetails(error_code, RED, BLACK, 11, VGA_HEIGHT - 1);
(void)frame;
(void)error_code;
}

147
core/gdt.c Normal file
View File

@ -0,0 +1,147 @@
/* Copyright (C) 2004 David Decotigny
Copyright (C) 1999 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
*/
#include "segment.h"
#include "gdt.h"
/**
* The sructure of a segment descriptor.
*
* @see Intel x86 doc, Vol 3, section 3.4.3, figure 3-8. For segment
* types, see section 3.5
*/
struct x86_segment_descriptor {
/* Lowest dword */
uint16_t limit_15_0; /* Segment limit, bits 15..0 */
uint16_t base_paged_addr_15_0; /* Base address, bits 15..0 */
/* Highest dword */
uint8_t base_paged_addr_23_16; /* Base address bits 23..16 */
uint8_t segment_type : 4; /* Section 3.4.3.1 (code/data)
and 3.5 (system) of Intel x86 vol 3 */
uint8_t descriptor_type : 1; /* 0=system, 1=Code/Data */
uint8_t dpl : 2;
uint8_t present : 1;
uint8_t limit_19_16 : 4; /* Segment limit, bits 19..16 */
uint8_t custom : 1;
uint8_t zero : 1;
uint8_t op_size : 1; /* 0=16bits instructions, 1=32bits */
uint8_t granularity : 1; /* 0=limit in bytes, 1=limit in pages */
uint8_t base_paged_addr_31_24; /* Base address bits 31..24 */
} __attribute__((packed, aligned(8)));
/**
* The GDT register, which stores the address and size of the
* GDT.
*
* @see Intel x86 doc vol 3, section 2.4, figure 2-4; and section
* 3.5.1
*/
struct x86_gdt_register {
/* The maximum GDT offset allowed to access an entry in the GDT */
uint16_t limit;
/* This is not exactly a "virtual" address, ie an adddress such as
those of instructions and data; this is a "linear" address, ie an
address in the paged memory. However, in SOS we configure the
segmented memory as a "flat" space: the 0-4GB segment-based (ie
"virtual") addresses directly map to the 0-4GB paged memory (ie
"linear"), so that the "linear" addresses are numerically equal
to the "virtual" addresses: this base_addr will thus be the same
as the address of the gdt array */
uint32_t base_addr;
} __attribute__((packed, aligned(8)));
/**
* Helper macro that builds a Segment descriptor for the virtual
* 0..4GB addresses to be mapped to the linear 0..4GB linear
* addresses.
*/
#define BUILD_GDTE(descr_privilege_level, is_code) \
((struct x86_segment_descriptor){ \
.limit_15_0 = 0xffff, \
.base_paged_addr_15_0 = 0, \
.base_paged_addr_23_16 = 0, \
.segment_type = \
((is_code) ? 0xb : 0x3), /* With descriptor_type (below) = 1 (code/data), \
* see Figure 3-1 of section 3.4.3.1 in Intel \
* x86 vol 3: \
* - Code (bit 3 = 1): \
* bit 0: 1=Accessed \
* bit 1: 1=Readable \
* bit 2: 0=Non-Conforming \
* - Data (bit 3 = 0): \
* bit 0: 1=Accessed \
* bit 1: 1=Writable \
* bit 2: 0=Expand up (stack-related) \
* For Conforming/non conforming segments, see \
* Intel x86 Vol 3 section 4.8.1.1 \
*/ \
.descriptor_type = 1, /* 1=Code/Data */ \
.dpl = ((descr_privilege_level)&0x3), \
.present = 1, \
.limit_19_16 = 0xf, \
.custom = 0, \
.op_size = 1, /* 32 bits instr/data */ \
.granularity = 1 /* limit is in 4kB Pages */ \
})
/** The actual GDT */
static struct x86_segment_descriptor gdt[] = {
[SOS_SEG_NULL] =
(struct x86_segment_descriptor){
0,
},
[SOS_SEG_KCODE] = BUILD_GDTE(0, 1),
[SOS_SEG_KDATA] = BUILD_GDTE(0, 0),
};
int gdtSetup(void)
{
struct x86_gdt_register gdtr;
/* Address of the GDT */
gdtr.base_addr = (uint32_t)gdt;
/* The limit is the maximum offset in bytes from the base address of
the GDT */
gdtr.limit = sizeof(gdt) - 1;
/* Commit the GDT into the CPU, and update the segment
registers. The CS register may only be updated with a long jump
to an absolute address in the given segment (see Intel x86 doc
vol 3, section 4.8.1). */
asm volatile("lgdt %0 \n\
ljmp %1,$1f \n\
1: \n\
movw %2, %%ax \n\
movw %%ax, %%ss \n\
movw %%ax, %%ds \n\
movw %%ax, %%es \n\
movw %%ax, %%fs \n\
movw %%ax, %%gs"
:
: "m"(gdtr), "i"(SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KCODE)),
"i"(SOS_BUILD_SEGMENT_REG_VALUE(0, FALSE, SOS_SEG_KDATA))
: "memory", "eax");
return 0;
}

39
core/gdt.h Normal file
View File

@ -0,0 +1,39 @@
/* Copyright (C) 2004 David Decotigny
Copyright (C) 1999 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
*/
#ifndef _SOS_GDT_H_
#define _SOS_GDT_H_
/**
* @file gdt.h
*
* The routines that manage the GDT, the table that maps the virtual
* addresses (data/instructions, segment-relative), to "linear"
* addresses (ie paged-memory). In SOS/x86, we use a "flat" virtual
* space, ie the virtual and linear spaces are equivalent.
*
* @see Intel x86 doc vol 3, chapter 3
*/
/**
* Configure the virtual space as a direct mapping to the linear
* address space (ie "flat" virtual space).
*/
int gdtSetup(void);
#endif /* _SOS_GDT_H_ */

68
core/idt.c Normal file
View File

@ -0,0 +1,68 @@
#include "idt.h"
static struct idtEntry idt[IDT_NUM];
int idtSetup()
{
struct idtRegister idtr;
for (int i = 0; i < IDT_NUM; i++) {
struct idtEntry *idte = idt + i;
/* Setup an empty IDTE interrupt gate, see figure 5-2 in Intel
x86 doc, vol 3 */
idte->seg_sel = BUILD_SEGMENT_SELECTOR(RING_0, 0, SEGMENT_IDX_CODE);
idte->reserved = 0;
idte->flags = 0;
idte->type = 0x6; /* Interrupt gate (110b) */
idte->op_size = 1; /* 32bits instructions */
/* Disabled it for now */
idte->zero = 0;
idte->offset_low = 0;
idte->offset_high = 0;
idte->dpl = 0;
idte->present = 0;
}
/*
* Setup the IDT register, see Intel x86 doc vol 3, section 5.8.
*/
/* Address of the IDT */
idtr.base_addr = (uint32_t)idt;
/* The limit is the maximum offset in bytes from the base address of
the IDT */
idtr.limit = sizeof(idt) - 1;
/* Commit the IDT into the CPU */
asm volatile("lidt %0\n" ::"m"(idtr) : "memory");
return 0;
}
int idt_set_handler(int index, unsigned int addr, int priviledge)
{
struct idtEntry *idte;
if (index < 0 || index >= IDT_NUM)
return -1;
if ((priviledge < 0) || priviledge > 3)
return -1;
idte = idt + index;
if (addr != (unsigned int)NULL) {
idte->offset_low = addr & 0xffff;
idte->offset_high = (addr >> 16) & 0xffff;
idte->dpl = priviledge;
idte->present = 1;
} else {
idte->offset_low = 0;
idte->offset_high = 0;
idte->dpl = 0;
idte->present = 0;
}
return 0;
}

45
core/idt.h Normal file
View File

@ -0,0 +1,45 @@
#pragma once
#include "types.h"
#define IDT_NUM 256
#define RING_0 0
#define RING_1 1
#define RING_2 2
#define RING_3 3
#define SEGMENT_IDX_NULL 0
#define SEGMENT_IDX_CODE 1
#define SEGMENT_IDX_DATA 2
struct idtEntry {
uint16_t offset_low;
uint16_t seg_sel;
uint8_t reserved : 5;
uint8_t flags : 3;
uint8_t type : 3;
uint8_t op_size : 1;
uint8_t zero : 1;
uint8_t dpl : 2;
uint8_t present : 1;
uint16_t offset_high;
} __attribute__((packed));
/**
* The IDT register, which stores the address and size of the
* IDT.
*
* @see Intel x86 doc vol 3, section 2.4, figure 2-4
*/
struct idtRegister {
uint16_t limit;
uint32_t base_addr;
} __attribute__((packed, aligned(8)));
/* Build segment http://wiki.osdev.org/Selector*/
#define BUILD_SEGMENT_SELECTOR(desc_privilege, in_ldt, index) \
((((desc_privilege)&0x3) << 0) | (((in_ldt) ? 1 : 0) << 2) | \
((index) << 3))
int idtSetup();
int idt_set_handler(int index, unsigned int addr, int priviledge);

11
core/interrupt.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include "types.h"
struct interrupt_frame;
//Exception
void print_handler(struct interrupt_frame *frame, ulong error_code);
//IRQ
void keyboard_handler(struct interrupt_frame *frame);
void timer_handler(struct interrupt_frame *frame);

21
core/io.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include "types.h"
// NIH http://wiki.osdev.org/Inline_Assembly/Examples#I.2FO_access
static inline void outb(uint16_t port, uint8_t val)
{
asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) );
/* There's an outb %al, $imm8 encoding, for compile-time constant port numbers that fit in 8b. (N constraint).
* Wider immediate constants would be truncated at assemble-time (e.g. "i" constraint).
* The outb %al, %dx encoding is the only option for all other cases.
* %1 expands to %dx because port is a uint16_t. %w1 could be used if we had the port number a wider C type */
}
static inline uint8_t inb(uint16_t port)
{
uint8_t ret;
asm volatile ( "inb %1, %0"
: "=a"(ret)
: "Nd"(port) );
return ret;
}

32
core/irq.c Normal file
View File

@ -0,0 +1,32 @@
#include "irq.h"
#include "idt.h"
#include "pic.h"
#include "types.h"
int irqSetup()
{
initPic();
return 0;
}
irq_handler irq_handler_array[IRQ_NUM] = {NULL, };
int irqSetRoutine(int irq, irq_handler handler)
{
uint32_t flags;
if ((irq < 0) || irq >= IRQ_NUM)
return -1;
disable_IRQs(flags);
irq_handler_array[irq] = handler;
if (handler != NULL) {
int ret =
idt_set_handler(IRQ_INTERRUPT_BASE_ADDRESS + irq, (unsigned int)irq_handler_array[irq], 0);
if (!ret)
enableIrq(irq);
}
restore_IRQs(flags);
return 0;
}

40
core/irq.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include "interrupt.h"
#define save_flags(flags) asm volatile("pushfl ; popl %0" : "=g"(flags)::"memory")
#define restore_flags(flags) asm volatile("push %0; popfl" ::"g"(flags) : "memory")
#define disable_IRQs(flags) \
({ \
save_flags(flags); \
asm("cli\n"); \
})
#define restore_IRQs(flags) restore_flags(flags)
#define IRQ_TIMER 0 // MASTER IRQ
#define IRQ_KEYBOARD 1
#define IRQ_SLAVE_PIC 2
#define IRQ_COM2 3
#define IRQ_COM1 4
#define IRQ_LPT2 5
#define IRQ_FLOPPY 6
#define IRQ_LPT1 7
#define IRQ_8_NOT_DEFINED 8 // SLAVE
#define IRQ_RESERVED_1 9 // SLAVE IRQ
#define IRQ_RESERVED_2 10
#define IRQ_RESERVED_3 11
#define IRQ_RESERVED_4 12
#define IRQ_COPROCESSOR 13
#define IRQ_HARDDISK 14
#define IRQ_RESERVED_5 15
#define IRQ_INTERRUPT_BASE_ADDRESS 0x20
#define IRQ_NUM 16
// An handler should finish by the iret opcode -> https://wiki.osdev.org/Interrupt_Service_Routines
// That's why we use wrapper around them or the gcc interrupt attribut
// __attribute__((interrupt)) void (*irq_handler)(int irq);
typedef void (*irq_handler)(struct interrupt_frame *frame);
int irqSetup();
int irqSetRoutine(int irq, irq_handler handler);

22
core/irq_handler.c Normal file
View File

@ -0,0 +1,22 @@
#include "interrupt.h"
#include "io.h"
#include "irq.h"
#include "keyboard.h"
#include "pic.h"
#include "vga.h"
// Need GCC > 6
__attribute__((interrupt)) void keyboard_handler(struct interrupt_frame *frame)
{
EOIIrq(IRQ_KEYBOARD);
keyboard_do_irq();
(void)frame;
}
__attribute__((interrupt)) void timer_handler(struct interrupt_frame *frame)
{
static int timeCnt = 0;
EOIIrq(IRQ_TIMER);
printIntDetails(timeCnt++, RED, BLACK, 20, VGA_HEIGHT - 1);
(void)frame;
}

11
core/klibc.c Normal file
View File

@ -0,0 +1,11 @@
#include "klibc.h"
void *memcpy(void *dst, const void *src, size_t n)
{
char *dstChar = dst;
const char *srcChar = src;
for (size_t i = 0; i < n; i++) {
*(dstChar++) = *(srcChar++);
}
return dst;
}

4
core/klibc.h Normal file
View File

@ -0,0 +1,4 @@
#pragma once
#include "types.h"
void *memcpy(void *dest, const void *src, size_t n );

37
core/main.c Normal file
View File

@ -0,0 +1,37 @@
#include "exception.h"
#include "gdt.h"
#include "idt.h"
#include "interrupt.h"
#include "io.h"
#include "irq.h"
#include "pit.h"
#include "types.h"
#include "vga.h"
void cpuid(int code, uint32_t *a, uint32_t *d)
{
asm volatile("cpuid" : "=a"(*a), "=d"(*d) : "0"(code) : "ebx", "ecx");
}
void kmain()
{
initVGA(BLACK, GREEN);
printString("Setting up IDT\n");
gdtSetup();
idtSetup();
irqSetup();
initPit(100);
printString("Setting up IRQ handlers\n");
irqSetRoutine(IRQ_KEYBOARD, keyboard_handler);
irqSetRoutine(IRQ_TIMER, timer_handler);
printString("Enabling HW interrupts\n");
exceptionSetRoutine(EXCEPTION_DOUBLE_FAULT, print_handler);
// Enabling the HW interrupts
asm volatile("sti\n");
int count = 0;
while (1) {
printIntDetails(count++, GREEN, BLACK, 0, VGA_HEIGHT - 1);
}
printString("exiting\n");
}

59
core/segment.h Normal file
View File

@ -0,0 +1,59 @@
/* Copyright (C) 2004 The SOS Team
Copyright (C) 1999 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
*/
#ifndef _SOS_HWSEGS_H_
#define _SOS_HWSEGS_H_
/**
* @file segments.h
*
* Global and local (GDT/LDT) segment descriptor definition and
* structure. These segments map virtual addresses (ie
* data/instruction addresses, relative to these segment descriptors)
* to linear addresses (ie addresses in the paged-memory space).
*
* @see Intel x86 doc, vol 3 chapter 3.
*/
#include "types.h"
/*
* Global segment selectors (GDT) for SOS/x86.
*
* @see gdt.h
*/
#define SOS_SEG_NULL 0 /* NULL segment, unused by the procesor */
#define SOS_SEG_KCODE 1 /* Kernel code segment */
#define SOS_SEG_KDATA 2 /* Kernel data segment */
/**
* Helper macro that builds a segment register's value
*/
#define SOS_BUILD_SEGMENT_REG_VALUE(desc_privilege,in_ldt,seg_index) \
( (((desc_privilege) & 0x3) << 0) \
| (((in_ldt)?1:0) << 2) \
| ((seg_index) << 3) )
/*
* Local segment selectors (LDT) for SOS/x86
*/
/* None */
#endif /* _SOS_HWSEGS_H_ */

47
core/types.h Normal file
View File

@ -0,0 +1,47 @@
#pragma once
typedef __signed__ char __s8;
typedef unsigned char __u8;
typedef __signed__ short __s16;
typedef unsigned short __u16;
typedef __signed__ int __s32;
typedef unsigned int __u32;
#ifdef __GNUC__
__extension__ typedef __signed__ long long __s64;
__extension__ typedef unsigned long long __u64;
#else
typedef __signed__ long long __s64;
typedef unsigned long long __u64;
#endif
/* sysv */
typedef unsigned char unchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;
typedef __s8 int8_t;
typedef __s16 int16_t;
typedef __s32 int32_t;
typedef __u8 uint8_t;
typedef __u16 uint16_t;
typedef __u32 uint32_t;
typedef __u64 uint64_t;
typedef __u64 u_int64_t;
typedef __s64 int64_t;
typedef enum { FALSE=0, TRUE } bool_t;
#define NULL ((void*)0)
#if __x86_64__
typedef unsigned long size_t;
typedef long ssize_t;
#else
typedef unsigned int size_t;
typedef int ssize_t;
#endif

301
drivers/keyboard.c Normal file
View File

@ -0,0 +1,301 @@
#include "keyboard.h"
#include "io.h"
#include "vga.h"
const char *scancode[128] = {
/* 0 */ 0,
/* 1 */ "\e", /* escape */
/* 2 */ "&",
/* 3 */ "é",
/* 4 */ "\"",
/* 5 */ "'",
/* 6 */ "(",
/* 7 */ "-",
/* 8 */ "è",
/* 9 */ "_",
/* 10 */ "ç",
/* 11 */ "à",
/* 12 */ ")",
/* 13 */ "=",
/* 14 */ "\b", /* Backspace */
/* 15 */ "\t", /* Tab */
/* 16 */ "a",
/* 17 */ "z",
/* 18 */ "e",
/* 19 */ "r",
/* 20 */ "t",
/* 21 */ "y",
/* 22 */ "u",
/* 23 */ "i",
/* 24 */ "o",
/* 25 */ "p",
/* 26 */ "^",
/* 27 */ "$",
/* 28 */ "\n",
/* 29 */ 0, /* left control */
/* 30 */ "q",
/* 31 */ "s",
/* 32 */ "d",
/* 33 */ "f",
/* 34 */ "g",
/* 35 */ "h",
/* 36 */ "j",
/* 37 */ "k",
/* 38 */ "l",
/* 39 */ "m",
/* 40 */ "ù",
/* 41 */ 0, /*²*/
/* 42 */ 0, /* left shift */
/* 43 */ "*",
/* 44 */ "w",
/* 45 */ "x",
/* 46 */ "c",
/* 47 */ "v",
/* 48 */ "b",
/* 49 */ "n",
/* 50 */ ",",
/* 51 */ ";",
/* 52 */ ":",
/* 53 */ "!",
/* 54 */ 0, /* right shift*/
/* 55 */ 0,
/* 56 */ 0, /* left alt*/
/* 57 */ " ",
/* 58 */ 0,
/* 59 */ "\eOP", /* F1 */
/* 60 */ "\eOQ", /* F2 */
/* 61 */ "\eOR", /* F3 */
/* 62 */ "\eOS", /* F4 */
/* 63 */ "\e[15~", /* F5 */
/* 64 */ "\e[17~", /* F6 */
/* 65 */ "\e[18~", /* F7 */
/* 66 */ "\e[19~", /* F8 */
/* 67 */ "\e[20~", /* F9 */
/* 68 */ "\e[21~", /* F10 */
/* 69 */ 0,
/* 70 */ 0,
/* 71 */ 0,
/* 72 */ 0,
/* 73 */ 0,
/* 74 */ 0,
/* 75 */ 0,
/* 76 */ 0,
/* 77 */ 0,
/* 78 */ 0,
/* 79 */ 0,
/* 80 */ 0,
/* 81 */ 0,
/* 82 */ 0,
/* 83 */ 0,
/* 84 */ 0,
/* 85 */ 0,
/* 86 */ "<",
/* 87 */ "\e[23~", /* F11 */
/* 88 */ "\e[24~", /* F12 */
/* 89 */ 0,
/* 90 */ 0,
/* 91 */ 0,
/* 92 */ 0,
/* 93 */ 0,
/* 94 */ 0,
/* 95 */ 0,
/* 96 */ 0,
/* 97 */ 0,
/* 98 */ 0,
/* 99 */ 0,
/* 100 */ 0,
/* 101 */ 0,
/* 102 */ 0,
/* 103 */ 0,
/* 104 */ 0,
/* 105 */ 0,
/* 106 */ 0,
/* 107 */ 0,
/* 108 */ 0,
/* 109 */ 0,
/* 110 */ 0,
/* 111 */ 0,
/* 112 */ 0,
/* 113 */ 0,
/* 114 */ 0,
/* 115 */ 0,
/* 116 */ 0,
/* 117 */ 0,
/* 118 */ 0,
/* 119 */ 0,
/* 120 */ 0,
/* 121 */ 0,
/* 122 */ 0,
/* 123 */ 0,
/* 124 */ 0,
/* 125 */ 0,
/* 126 */ 0,
/* 127 */ 0};
const char *scancode_shift[128] = {
/* 0 */ 0,
/* 1 */ "\e",
/* 2 */ "1",
/* 3 */ "2",
/* 4 */ "3",
/* 5 */ "4",
/* 6 */ "5",
/* 7 */ "6",
/* 8 */ "7",
/* 9 */ "8",
/* 10 */ "9",
/* 11 */ "0",
/* 12 */ "°",
/* 13 */ "+",
/* 14 */ "\b", /* Shift-Backspace */
/* 15 */ "\e[Z", /* Shift-Tab */
/* 16 */ "A",
/* 17 */ "Z",
/* 18 */ "E",
/* 19 */ "R",
/* 20 */ "T",
/* 21 */ "Y",
/* 22 */ "U",
/* 23 */ "I",
/* 24 */ "O",
/* 25 */ "P",
/* 26 */ "\"",
/* 27 */ "£",
/* 28 */ "\n",
/* 29 */ 0, /* left control */
/* 30 */ "Q",
/* 31 */ "S",
/* 32 */ "D",
/* 33 */ "F",
/* 34 */ "G",
/* 35 */ "H",
/* 36 */ "J",
/* 37 */ "K",
/* 38 */ "L",
/* 39 */ "M",
/* 40 */ "%",
/* 41 */ 0,
/* 42 */ 0,
/* 43 */ "µ",
/* 44 */ "W",
/* 45 */ "X",
/* 46 */ "C",
/* 47 */ "V",
/* 48 */ "B",
/* 49 */ "N",
/* 50 */ "?",
/* 51 */ ".",
/* 52 */ "/",
/* 53 */ "§",
/* 54 */ 0,
/* 55 */ 0,
/* 56 */ 0,
/* 57 */ 0,
/* 58 */ 0,
/* 59 */ "\eOP", /* Shift-F1 */
/* 60 */ "\eOQ", /* Shift-F2 */
/* 61 */ "\eOR", /* Shift-F3 */
/* 62 */ "\eOS", /* Shift-F4 */
/* 63 */ "\e[15;2~", /* Shift-F5 */
/* 64 */ "\e[17;2~", /* Shift-F6 */
/* 65 */ "\e[18;2~", /* Shift-F7 */
/* 66 */ "\e[19;2~", /* Shift-F8 */
/* 67 */ "\e[20:2~", /* Shift-F9 */
/* 68 */ "\e[21:2~", /* Shift-F10 */
/* 69 */ 0,
/* 70 */ 0,
/* 71 */ 0,
/* 72 */ 0,
/* 73 */ 0,
/* 74 */ 0,
/* 75 */ 0,
/* 76 */ 0,
/* 77 */ 0,
/* 78 */ 0,
/* 79 */ 0,
/* 80 */ 0,
/* 81 */ 0,
/* 82 */ 0,
/* 83 */ 0,
/* 84 */ 0,
/* 85 */ 0,
/* 86 */ ">",
/* 87 */ "\e[23;2~", /* Shift-F11 */
/* 88 */ "\e[24;2~", /* Shift-F12 */
/* 89 */ 0,
/* 90 */ 0,
/* 91 */ 0,
/* 92 */ 0,
/* 93 */ 0,
/* 94 */ 0,
/* 95 */ 0,
/* 96 */ 0,
/* 97 */ 0,
/* 98 */ 0,
/* 99 */ 0,
/* 100 */ 0,
/* 101 */ 0,
/* 102 */ 0,
/* 103 */ 0,
/* 104 */ 0,
/* 105 */ 0,
/* 106 */ 0,
/* 107 */ 0,
/* 108 */ 0,
/* 109 */ 0,
/* 110 */ 0,
/* 111 */ 0,
/* 112 */ 0,
/* 113 */ 0,
/* 114 */ 0,
/* 115 */ 0,
/* 116 */ 0,
/* 117 */ 0,
/* 118 */ 0,
/* 119 */ 0,
/* 120 */ 0,
/* 121 */ 0,
/* 122 */ 0,
/* 123 */ 0,
/* 124 */ 0,
/* 125 */ 0,
/* 126 */ 0,
/* 127 */ 0};
void keyboard_do_irq()
{
static int lshift = 0;
static int rshift = 0;
unsigned char c = 0;
if (inb(0x60) != c) {
c = inb(0x60);
if (c > 0) {
if (c < BREAK_CODE) {
switch (c) {
case 42:
lshift = 1;
break;
case 54:
rshift = 1;
break;
default:
if (lshift || rshift)
printString(scancode_shift[(int)c]);
else
printString(scancode[(int)c]);
}
}else {
c = c - BREAK_CODE;
switch (c) {
case 42:
lshift = 0;
break;
case 54:
rshift = 0;
break;
}
}
}
}
}

6
drivers/keyboard.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
// Pushing generate MAKE_CODE (<0x80)
// Releasing generate BREAK_CODE (break_code = make_code + 0x80)
#define BREAK_CODE 0x80
void keyboard_do_irq();

73
drivers/pic.c Normal file
View File

@ -0,0 +1,73 @@
#include "pic.h"
#include "io.h"
#include "irq.h"
#define ICW1_ICW4 0x01 /* ICW4 (not) needed */
#define ICW1_SINGLE 0x02 /* Single (cascade) mode */
#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */
#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */
#define ICW1_INIT 0x10 /* Initialization - required! */
#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */
#define ICW4_AUTO 0x02 /* Auto (normal) EOI */
#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */
#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */
#define ICW4_SFNM 0x10 /* Special fully nested (not) */
void initPic(void)
{
/* Send CMD: Init + sequence in 4 DATA */
outb(PIC_MASTER_CMD, ICW1_INIT + ICW1_ICW4);
outb(PIC_SLAVE_CMD, ICW1_INIT + ICW1_ICW4);
/* Send ICW2: ctrl base address. Remap IRQ from interupt range 0x0-0xF to 0x20-0x2F as
* intel
* reserve interupt 0x0-0x1F in protected mode (e.g. 0-7 are CPU exception) */
outb(PIC_MASTER_DATA, IRQ_INTERRUPT_BASE_ADDRESS);
outb(PIC_SLAVE_DATA, IRQ_INTERRUPT_BASE_ADDRESS + 8);
/* Send ICW3 master: mask where slaves are connected */
outb(PIC_MASTER_DATA, 0x4);
/* Send ICW3 slave: index where the slave is connected on master */
outb(PIC_SLAVE_DATA, 0x2);
/* Send ICW4: 8086 mode, fully nested, not buffered, no implicit EOI */
outb(PIC_MASTER_DATA, ICW4_8086);
outb(PIC_SLAVE_DATA, ICW4_8086);
/* Send OCW1:
* Closing all IRQs : waiting for a correct handler The only IRQ
* enabled is the cascade (that's why we use 0xFB for the master) */
outb(PIC_MASTER_DATA, 0xFB);
outb(PIC_SLAVE_DATA, 0xFF);
}
void EOIIrq(int irq)
{
if (irq >= 8)
outb(PIC_SLAVE_CMD, PIC_EOI);
outb(PIC_MASTER_CMD, PIC_EOI);
}
void disableIrq(int irq)
{
if (irq < 8) {
uint8_t status = inb(PIC_MASTER_DATA);
outb(PIC_MASTER_DATA, (status | (1 << irq)));
} else {
uint8_t status = inb(PIC_SLAVE_DATA);
outb(PIC_SLAVE_DATA, (status | (1 << (irq - 8))));
}
}
void enableIrq(int irq)
{
if (irq < 8) {
uint8_t status = inb(PIC_MASTER_DATA);
outb(PIC_MASTER_DATA, (status & ~(1 << irq)));
} else {
uint8_t status = inb(PIC_SLAVE_DATA);
outb(PIC_SLAVE_DATA, (status & ~(1 << (irq - 8))));
}
}

21
drivers/pic.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
// 2 PIC 8259 are available on x86
//
// Master - command: 0x20, data: 0x21
// Slave - command: 0xA0, data: 0xA1
//
// http://www.jamesmolloy.co.uk/tutorial_html/5.-IRQs%20and%20the%20PIT.html
// SimpleOS art2
// http://wiki.osdev.org/PIC
#define PIC_MASTER_CMD 0x20
#define PIC_SLAVE_CMD 0xa0
#define PIC_MASTER_DATA 0x21
#define PIC_SLAVE_DATA 0xa1
#define PIC_EOI 0x20 /* End-of-interrupt command code */
void EOIIrq(int irq);
void initPic(void);
void enableIrq(int irq);
void disableIrq(int irq);

14
drivers/pit.c Normal file
View File

@ -0,0 +1,14 @@
#include "pit.h"
#include "io.h"
int initPit(unsigned int freq)
{
unsigned int divisor = PIT_FREQ / freq;
if (divisor > 65535)
divisor = 0; // Used to represent 35536
outb(PIT_CMD, 0x34); // chan 0; low then high; mode 2
outb(PIT_CHAN_0, divisor & 0xFF);
outb(PIT_CHAN_0, divisor >> 8u);
return 0;
}

16
drivers/pit.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
// C.f https://wiki.osdev.org/PIT
#define PIT_FREQ 1193182
#define PIT_CHAN_0 0x40 // IRQ0
#define PIT_CHAN_1 0x41 // Used for DRAM refresh. Not used anymore
#define PIT_CHAN_2 0x42 // PC Speaker
#define PIT_CMD 0x43
// Cmd are
// 7-6: select channel. 0 ->chan0, 1 -> chan1, 2 -> chan 2, 3 -> read back
// 5-4: access mode. 0 -> latch count; 1 -> low value only; 2 -> high value only;
// 3 -> low then high 3-1: mode. See https://wiki.osdev.org/PIT
int initPit(unsigned int freq);

142
drivers/vga.c Normal file
View File

@ -0,0 +1,142 @@
#include "vga.h"
#include "io.h"
#include "klibc.h"
static uint vgaBgColor;
static uint vgaColor;
static int line, col;
int initVGA(uint bgColor, uint color)
{
vgaBgColor = bgColor;
vgaColor = color;
clearScreen(bgColor);
line = 0;
col = 0;
return 0;
}
void clearScreen(uint bgColor)
{
volatile short *vga = (short *)VGA_ADDR;
long int colorAttr = bgColor << 12;
for (int i = 0; i < VGA_WIDTH * VGA_HEIGHT; i++) {
vga[i] = colorAttr;
}
}
void printInt(int integer)
{
char num[sizeof(int) *
3]; // int max is 2^(sizeof(int)*8) which is (2^3)^(sizeof(int)*8/3) =
// 8^(sizeof(int)*8/3) ~ 10^(sizeof(int)*8/3)
int i = 0, k = 0;
if (integer < 0) {
printChar('-');
}
while (integer != 0) {
int digit = integer % 10;
num[i++] = (digit > 0) ? digit : -digit;
integer = integer / 10;
}
for (k = i - 1; k >= 0; k--) {
printChar(num[k] + '0');
}
}
void printIntDetails(int integer, uint color, uint bgColor, int startX, int startY)
{
char num[sizeof(int) *
3]; // int max is 2^(sizeof(int)*8) which is (2^3)^(sizeof(int)*8/3) =
// 8^(sizeof(int)*8/3) ~ 10^(sizeof(int)*8/3)
int x = startX;
int i = 0, k = 0;
if (integer < 0) {
printCharDetails('-', color, bgColor, x++, startY);
}
while (integer != 0) {
int digit = integer % 10;
num[i++] = (digit > 0) ? digit : -digit;
integer = integer / 10;
}
for (k = i - 1; k >= 0; k--) {
printCharDetails(num[k] + '0', color, bgColor, x++, startY);
}
}
void printCharDetails(const char str, uint color, uint bgColor, int startX, int startY)
{
volatile short *vga = (short *)VGA_ADDR;
long int colorAttr = (bgColor << 4 | (color & 0x0f)) << 8;
vga[VGA_WIDTH * startY + startX] = colorAttr | str;
}
void vgaScrollUp(void)
{
long int colorAttr = vgaBgColor << 12;
volatile short *vga = (short *)VGA_ADDR;
for (int i = 1; i < VGA_HEIGHT - 2;
i++) { // last line is status line. Do not scroll it
memcpy((void *)&vga[VGA_WIDTH * (i - 1)], (void *)&vga[VGA_WIDTH * i],
VGA_WIDTH * sizeof(short));
}
for (int i = 0; i < VGA_WIDTH; i++) {
vga[(VGA_HEIGHT - 2) * VGA_WIDTH + i] = colorAttr;
}
}
void printString(const char *str)
{
while (*str) {
printChar(*(str++));
}
}
void printChar(const char str)
{
if (str == '\n') {
line++;
col = 0;
if (line >= VGA_HEIGHT - 1) {
vgaScrollUp();
line--;
}
}
else if(str == '\r')
{
col = 0;
}
else if(str == '\b')
{
col--;
if (col < 0) {
col = VGA_WIDTH - 1;
line--;
}
printCharDetails(' ', vgaColor, vgaBgColor, col, line);
}
else
{
printCharDetails(str, vgaColor, vgaBgColor, col++, line);
if (col == VGA_WIDTH) {
col = 0;
line++;
}
if (line >= VGA_HEIGHT - 1) {
vgaScrollUp();
line--;
}
}
}
void printStringDetails(const char *str, uint color, uint bgColor, int startX, int startY)
{
volatile short *vga = (short *)VGA_ADDR;
int i = 0;
long int colorAttr = (bgColor << 4 | (color & 0x0f)) << 8;
while (*str) {
vga[VGA_WIDTH * startY + startX + i] = colorAttr | *str;
str++;
i++;
}
}

27
drivers/vga.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include "types.h"
// https://wiki.osdev.org/Text_UI
#define BLACK 0x00
#define BLUE 0x01
#define GREEN 0x02
#define CYAN 0x03
#define RED 0x04
#define MAGENTA 0x05
#define BROWN 0x06
#define GREY 0x07
#define WHITE 0x0F
#define VGA_ADDR 0xB8000
#define VGA_WIDTH 80
#define VGA_HEIGHT 25
int initVGA(uint bgColor, uint color);
void clearScreen(uint bgColor);
void printInt(int integer);
void printIntDetails(int integer, uint color, uint bgColor, int startX, int startY);
void printCharDetails(char str, uint color, uint bgColor, int startX, int startY);
void printStringDetails(const char *str, uint color, uint bgColor, int startX, int startY);
void printString(const char *str);
void printChar(const char str);
void vgaScrollUp(void);

26
linker.ld Normal file
View File

@ -0,0 +1,26 @@
ENTRY(boot)
OUTPUT_FORMAT("binary")
SECTIONS {
. = 0x7c00;
.text :
{
*(.boot)
*(.text)
}
.rodata :
{
*(.rodata)
}
.data :
{
*(.data)
}
.bss :
{
*(.bss)
}
}

161
mbr.asm Normal file
View File

@ -0,0 +1,161 @@
section .boot
bits 16
global boot
boot:
jmp main
display_enable:
push bp
mov bp, sp
mov ah, 0h ; 00h Set Video Mode
mov al, 07h ; Txt, monochrome, 80x25
int 10h
mov sp, bp
pop bp
ret
print:
push bp
mov bp, sp
mov si, [bp + 4]; put first function arg in si. sp is stask pointer
.loop:
lodsb ; load si content into al then inc si
cmp al, 0;
je .end
mov ah, 0eh
mov bx, 0
int 10h
jmp .loop
.end:
mov sp, bp
pop bp
ret
println:
push bp
mov bp, sp
push word [bp + 4]
call print
add sp, 2
mov ah, 03h ; read cursor position
int 10h ; row number in dh. Col in dl
inc dh ; goto next line
mov dl, 0
mov ah, 02h ; Set Cursor Position
int 10h
mov sp, bp
pop bp
ret
hello db 'Booting matOs', 0
main:
sti ; enable virtual interupts
mov [disk],dl ; save disk used to boot by bios
call display_enable
push hello
call println
add sp, 2
; Switch in 32bits Protected mode
; Activate A20 http://wiki.osdev.org/A20_Line to be able to access more than 1Mb memory
mov ah, 0h
mov ax, 0x2401
int 0x15
; Change video mode to display VGA
mov ax, 0x3
int 0x10
; http://www.ctyme.com/intr/rb-0607.htm
; Bios read first 512 bytes, read next disk sector
mov ah, 0x2 ;read sectors
mov al, 15 ;sectors to read
mov ch, 0 ;cylinder idx
mov dh, 0 ;head idx
mov cl, 2 ;sector idx
mov dl, [disk] ;disk idx
mov bx, copy_target;target pointer
int 0x13
cli ; disable interruption when setting GDT
; switch in 32 bits
lgdt [gdt_pointer] ; switch in 32bits here
mov eax, cr0
or eax,0x1; set the protected mode bit on special CPU reg cr0
mov cr0, eax
jmp CODE_SEG:boot2 ; In protected mode we need to add the segment selector
; GDT table desciption could be found http://wiki.osdev.org/Global_Descriptor_Table
; here we define the 3 64bits segment needed: null segment, code segment and data segment
gdt_start: ;null segment
dq 0x0
gdt_code: ;code segment
dw 0xFFFF ; limit [0:15]
dw 0x0 ; base [0:15]
db 0x0 ; base [16:23]
db 10011010b ; access byte: Present(1)| Priv(2) 0 ->kernel 3->userspace | 1 | Executable(1) | Direction/Conformity (1) | RW(1) | Accessed(1)
db 11001111b ; Granularity(1) | Size (1) 0-> 16bit mode 1->32protected mode | 0 | 0 | Limit [16:19]
db 0x0 ; base [24:31]
gdt_data:
dw 0xFFFF
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdt_end:
gdt_pointer:
dw gdt_end - gdt_start
dd gdt_start
disk:
db 0x0
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
times 510 - ($-$$) db 0
dw 0xaa55
copy_target:
bits 32
boot2:
mov ax, DATA_SEG ; set all segments to point to DATA_SEG https://en.wikipedia.org/wiki/X86_memory_segmentation
mov ds, ax ; Data segment
mov es, ax ; Extra Segment (for string operation)
mov fs, ax ; No Specific use
mov gs, ax ; No Specific use
mov ss, ax ; stack segment
mov esi,hello32
mov ebx,0xb8000 ; Cannot use BIOS anymore, use VGA Text buffer instead
.loop32:
lodsb
or al,al
jz halt
or eax,0x0100 ; blue bg
mov word [ebx], ax
add ebx,2
jmp .loop32
halt:
mov esp,kernel_stack_top
extern kmain
call kmain
cli
hlt
hello32: db "Hello 32 bits world!",0
section .bss
align 4
kernel_stack_bottom: equ $
resb 16384 ; 16 KB
kernel_stack_top: