Add some "kernel" code written in C

Draw few message on VGA
Setup Interruption Description Table
Setup IRQ
This commit is contained in:
Mathieu Maret 2017-11-24 23:41:14 +01:00
parent 523bff7fce
commit f1341d3d72
13 changed files with 353 additions and 3 deletions

View File

@ -1,8 +1,8 @@
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
CFLAGS += -m32 -Wall -Wextra -Werror -ffreestanding -fno-exceptions
CXXFLAGS += -m32 -Wall -Wextra -Werror -ffreestanding -fno-exceptions -fno-rtti
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
asmsrc=$(wildcard *.asm)

44
idt.c Normal file
View File

@ -0,0 +1,44 @@
#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;
}

44
idt.h Normal file
View File

@ -0,0 +1,44 @@
#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();

21
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;
}

8
irq.c Normal file
View File

@ -0,0 +1,8 @@
#include "irq.h"
#include "pic.h"
int irqSetup()
{
initPic();
return 0;
}

21
irq.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#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
typedef void (*irq_handler)(int irq);
int irqSetup();

37
main.c Normal file
View File

@ -0,0 +1,37 @@
#include "idt.h"
#include "io.h"
#include "irq.h"
#include "types.h"
#include "vga.h"
char getScancode()
{
char c = 0;
do {
if (inb(0x60) != c) {
c = inb(0x60);
if (c > 0)
return c;
}
} while (1);
}
void cpuid(int code, uint32_t *a, uint32_t *d)
{
asm volatile("cpuid" : "=a"(*a), "=d"(*d) : "0"(code) : "ebx", "ecx");
}
void kmain()
{
const short color = GREEN;
clearScreen(BLACK);
printString("Setting up IDT", color, BLACK, 0, 0);
idtSetup();
printString("Setting up IRQ", color, BLACK, 0, 1);
irqSetup();
while (1) {
char c = getScancode();
printChar(c, color, BLACK, 0, 5);
}
}

View File

@ -152,6 +152,14 @@ boot2:
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:

64
pic.c Normal file
View File

@ -0,0 +1,64 @@
#include "pic.h"
#include "io.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 + senquence in 4 DATA */
outb(ICW1_INIT + ICW1_ICW4, PIC_MASTER_CMD);
outb(ICW1_INIT + ICW1_ICW4, PIC_SLAVE_CMD);
/* 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(0x20, PIC_MASTER_DATA);
outb(0x28, PIC_SLAVE_DATA);
/* Send ICW3 master: mask where slaves are connected */
outb(0x4, PIC_MASTER_DATA);
/* Send ICW3 slave: index where the slave is connected on master */
outb(0x2, PIC_SLAVE_DATA);
/* Send ICW4: 8086 mode, fully nested, not buffered, no implicit EOI */
outb(ICW4_8086, PIC_MASTER_DATA);
outb(ICW4_8086, PIC_SLAVE_DATA);
/* 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(0xFB, PIC_MASTER_DATA);
outb(0xFF, PIC_SLAVE_DATA);
}
void enableIrq(int irq)
{
if (irq < 8) {
uint8_t status = inb(PIC_MASTER_DATA);
outb((status | (1 << irq)), PIC_MASTER_DATA);
} else {
uint8_t status = inb(PIC_SLAVE_DATA);
outb((status | (1 << irq)), PIC_SLAVE_DATA);
}
}
void disableIrq(int irq)
{
if (irq < 8) {
uint8_t status = inb(PIC_MASTER_DATA);
outb((status & ~(1 << irq)), PIC_MASTER_DATA);
} else {
uint8_t status = inb(PIC_SLAVE_DATA);
outb((status & ~(1 << irq)), PIC_SLAVE_DATA);
}
}

18
pic.h Normal file
View File

@ -0,0 +1,18 @@
#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 0xa0
void initPic(void);
void enableIrq(int irq);
void disableIrq(int irq);

36
types.h Normal file
View File

@ -0,0 +1,36 @@
#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;

30
vga.c Normal file
View File

@ -0,0 +1,30 @@
#include "vga.h"
void clearScreen(int 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 printChar(const char str, int color, int bgColor, int startX, int startY)
{
volatile short *vga = (short *)VGA_ADDR;
long int colorAttr = (bgColor << 4 | (color & 0x0f)) << 8;
vga[80 * startY + startX] = colorAttr | str;
}
void printString(const char *str, int color, int 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[80 * startY + startX + i] = colorAttr | *str;
str++;
i++;
}
}

19
vga.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#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 15
void clearScreen(int bgColor);
void printChar(const char str, int color, int bgColor, int startX, int startY);
void printString(const char *str, int color, int bgColor, int startX, int startY);