No more double faults B-)
This commit is contained in:
6
Makefile
6
Makefile
@@ -25,7 +25,9 @@ ASM_SOURCES := $(shell find $(SOURCE_DIR) -name '*.S')
|
|||||||
OBJECTS := $(patsubst $(SOURCE_DIR)/%, $(BUILD_DIR)/%, $(C_SOURCES:.c=.o) $(ASM_SOURCES:.S=.o))
|
OBJECTS := $(patsubst $(SOURCE_DIR)/%, $(BUILD_DIR)/%, $(C_SOURCES:.c=.o) $(ASM_SOURCES:.S=.o))
|
||||||
DEPENDS := $(patsubst $(SOURCE_DIR)/%, $(BUILD_DIR)/%, $(C_SOURCES:.c=.d))
|
DEPENDS := $(patsubst $(SOURCE_DIR)/%, $(BUILD_DIR)/%, $(C_SOURCES:.c=.d))
|
||||||
|
|
||||||
CFLAGS := -MMD -ffreestanding -O1 -Wall -Wextra -Werror -std=c2x -I$(SOURCE_DIR)/include -no-pie -fstack-protector-strong
|
CFLAGS := -MMD -ffreestanding -nostdlib -O1 -Wall -Wextra -Werror -std=c2x -I$(SOURCE_DIR)/include -no-pie -fstack-protector-strong -g3
|
||||||
|
CFLAGS += -Wno-unused-function
|
||||||
|
CFLAGS += -Wno-unused-variable
|
||||||
ASFLAGS :=
|
ASFLAGS :=
|
||||||
|
|
||||||
#$(info C_SOURCES is $(C_SOURCES))
|
#$(info C_SOURCES is $(C_SOURCES))
|
||||||
@@ -34,7 +36,7 @@ ASFLAGS :=
|
|||||||
#$(info DEPENDS is $(DEPENDS))
|
#$(info DEPENDS is $(DEPENDS))
|
||||||
|
|
||||||
run: myos.iso
|
run: myos.iso
|
||||||
qemu-system-i386 -cdrom myos.iso
|
qemu-system-i386 -d int -no-reboot -cdrom myos.iso
|
||||||
|
|
||||||
cross-compiler: cross-compiler-image/Dockerfile
|
cross-compiler: cross-compiler-image/Dockerfile
|
||||||
podman build cross-compiler-image -t cc-i686
|
podman build cross-compiler-image -t cc-i686
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
__attribute__((interrupt)) void interrupt_handler_1(struct interrupt_frame* frame);
|
|
||||||
@@ -33,7 +33,7 @@ undefined behavior.
|
|||||||
.section .bss
|
.section .bss
|
||||||
.align 16
|
.align 16
|
||||||
stack_bottom:
|
stack_bottom:
|
||||||
.skip 16384 # 16 KiB
|
.skip 1024 * 16 # 16384, 16 KiB
|
||||||
stack_top:
|
stack_top:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
#include "str.h"
|
#include "str.h"
|
||||||
#include "tty.h"
|
#include "tty.h"
|
||||||
|
|
||||||
__attribute__((noreturn)) void panic(struct str s);
|
__attribute__((noreturn))
|
||||||
|
void panic(struct str s);
|
||||||
|
|
||||||
void* memmove(void *dest, const void *src, size_t n);
|
void* memmove(void *dest, const void *src, size_t n);
|
||||||
void* memset(void *s, int c, size_t n);
|
void* memset(void *s, int c, size_t n);
|
||||||
|
|||||||
@@ -32,23 +32,23 @@ struct gdt_table_entry gdt_encode_entry(struct gdt_entry_content content)
|
|||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gdt_set(uint16_t size, const struct gdt_table_entry base[], uint32_t offset)
|
//void gdt_load(uint16_t size, const struct gdt_table_entry base[], uint32_t offset)
|
||||||
{
|
//{
|
||||||
base += offset;
|
// base += offset;
|
||||||
|
//
|
||||||
/* the lgdt instruction requires a packed alignment */
|
// /* the lgdt instruction requires a packed alignment */
|
||||||
struct __attribute__((packed)) gdtr {
|
// struct __attribute__((packed)) gdtr {
|
||||||
uint16_t size;
|
// uint16_t size;
|
||||||
uint32_t base;
|
// uint32_t base;
|
||||||
} gdt = {
|
// } gdt = {
|
||||||
.size = size,
|
// .size = size,
|
||||||
.base = (uint32_t)base,
|
// .base = (uint32_t)base,
|
||||||
};
|
// };
|
||||||
|
//
|
||||||
__asm__ volatile (
|
// __asm__ volatile (
|
||||||
"lgdt %[gdt]"
|
// "lgdt %[gdt]"
|
||||||
:
|
// :
|
||||||
: [gdt] "m"(gdt)
|
// : [gdt] "m"(gdt)
|
||||||
);
|
// );
|
||||||
}
|
//}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
#define GDT_LIMIT_MAX 0xFFFFF
|
#define GDT_LIMIT_MAX 0xFFFFF
|
||||||
|
|
||||||
@@ -48,16 +49,14 @@ enum gdt_access_flag : uint8_t {
|
|||||||
enum gdt_flags : uint8_t {
|
enum gdt_flags : uint8_t {
|
||||||
GDT_RESERVED = (1<<0),
|
GDT_RESERVED = (1<<0),
|
||||||
GDT_LONG = (1<<1),
|
GDT_LONG = (1<<1),
|
||||||
GDT_SIZE = (1<<2),
|
GDT_32BIT = (1<<2),
|
||||||
GDT_GRANULARITY = (1<<3),
|
GDT_GRANULARITY_PAGEWISE = (1<<3),
|
||||||
};
|
};
|
||||||
|
|
||||||
void gdt_set(uint16_t limit, const struct gdt_table_entry base[], uint32_t offset);
|
|
||||||
|
|
||||||
struct gdt_table_entry gdt_encode_entry(struct gdt_entry_content content);
|
struct gdt_table_entry gdt_encode_entry(struct gdt_entry_content content);
|
||||||
|
|
||||||
// only compiles with O1 or higher ¯\_(ツ)_/¯, might have to make this a macro
|
// only compiles with O1 or higher ¯\_(ツ)_/¯, might have to make this a macro
|
||||||
static inline void gdt_reload_granular(uint32_t code,
|
static inline void gdt_flush_granular(uint32_t code,
|
||||||
uint32_t data,
|
uint32_t data,
|
||||||
uint32_t extra_segment,
|
uint32_t extra_segment,
|
||||||
uint32_t general_segment_1,
|
uint32_t general_segment_1,
|
||||||
@@ -88,10 +87,19 @@ static inline void gdt_reload_granular(uint32_t code,
|
|||||||
: "ax" // Clobbered register
|
: "ax" // Clobbered register
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
static inline void gdt_load(struct gdt_table_entry base[], uint16_t gdt_size, segment_t data, segment_t code)
|
||||||
static inline void gdt_reload(uint32_t data, uint32_t code)
|
|
||||||
{
|
{
|
||||||
|
/* the lgdt instruction requires a packed alignment */
|
||||||
|
struct __attribute__((packed)) {
|
||||||
|
uint16_t size;
|
||||||
|
uint32_t base;
|
||||||
|
} gdt = {
|
||||||
|
.size = gdt_size,
|
||||||
|
.base = (uint32_t)base,
|
||||||
|
};
|
||||||
|
|
||||||
__asm__ volatile (
|
__asm__ volatile (
|
||||||
|
"lgdt %[gdt]\n"
|
||||||
// Far jump to reload the CS register
|
// Far jump to reload the CS register
|
||||||
"jmp %[cs], $reload_CS\n"
|
"jmp %[cs], $reload_CS\n"
|
||||||
"reload_CS:\n"
|
"reload_CS:\n"
|
||||||
@@ -103,7 +111,8 @@ static inline void gdt_reload(uint32_t data, uint32_t code)
|
|||||||
"movw %%ax, %%gs\n"
|
"movw %%ax, %%gs\n"
|
||||||
"movw %%ax, %%ss\n"
|
"movw %%ax, %%ss\n"
|
||||||
: // No output operands
|
: // No output operands
|
||||||
: [ds] "i"(data),
|
: [gdt] "m"(gdt),
|
||||||
|
[ds] "i"(data),
|
||||||
[cs] "i"(code)
|
[cs] "i"(code)
|
||||||
: "ax" // Clobbered register
|
: "ax" // Clobbered register
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,17 +3,18 @@
|
|||||||
|
|
||||||
#include "idt.h"
|
#include "idt.h"
|
||||||
|
|
||||||
struct idt_gate_descriptor idt_encode_descriptor(uint32_t offset, uint16_t segment_selector, uint8_t ist, enum idt_flags type)
|
struct idt_gate_descriptor idt_encode_descriptor(void* func, segment_t segment_selector, enum idt_dpl dpl, enum idt_flags type)
|
||||||
{
|
{
|
||||||
struct idt_gate_descriptor output = {0};
|
struct idt_gate_descriptor output = {0};
|
||||||
|
|
||||||
|
uint32_t offset = (uint32_t)func;
|
||||||
|
|
||||||
output.offset_0 = offset & 0xFFFF;
|
output.offset_0 = offset & 0xFFFF;
|
||||||
offset >>= 16;
|
offset >>= 16;
|
||||||
output.offset_1 = offset & 0xFFFF;
|
output.offset_1 = offset & 0xFFFF;
|
||||||
|
|
||||||
output.segment_selector = segment_selector;
|
output.segment_selector = segment_selector;
|
||||||
output.ist = ist;
|
output.flags = type | dpl | IDT_PRESENT;
|
||||||
output.flags = type | IDT_DPL_0 | IDT_PRESENT;
|
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
struct __attribute__((packed)) idt_gate_descriptor {
|
struct __attribute__((packed)) idt_gate_descriptor {
|
||||||
uint16_t offset_0;
|
uint16_t offset_0;
|
||||||
uint16_t segment_selector;
|
uint16_t segment_selector;
|
||||||
uint8_t ist;
|
|
||||||
|
uint8_t ist; // unused in 32-bit
|
||||||
|
|
||||||
/* `flags`
|
/* `flags`
|
||||||
=======
|
=======
|
||||||
@@ -20,22 +22,26 @@ struct __attribute__((packed)) idt_gate_descriptor {
|
|||||||
uint16_t offset_1;
|
uint16_t offset_1;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum idt_flags : uint8_t {
|
_Static_assert(sizeof(struct idt_gate_descriptor) == 8);
|
||||||
IDT_GATE_TYPE_TASK_GATE = 0x5,
|
|
||||||
IDT_GATE_TYPE_INTERUPT_GATE_16 = 0x6,
|
|
||||||
IDT_GATE_TYPE_TRAP_GATE_16 = 0x7,
|
|
||||||
IDT_GATE_TYPE_INTERUPT_GATE_32 = 0xE,
|
|
||||||
IDT_GATE_TYPE_TRAP_GATE_32 = 0xF,
|
|
||||||
|
|
||||||
IDT_DPL_0 = 0 << 5,
|
enum idt_flags : uint8_t {
|
||||||
IDT_DPL_1 = 1 << 5,
|
IDT_GATE_TYPE_TASK = 0x5,
|
||||||
IDT_DPL_2 = 2 << 5,
|
IDT_GATE_TYPE_INTERRUPT16 = 0x6,
|
||||||
IDT_DPL_3 = 3 << 5,
|
IDT_GATE_TYPE_TRAP16 = 0x7,
|
||||||
|
IDT_GATE_TYPE_INTERRUPT32 = 0xE,
|
||||||
|
IDT_GATE_TYPE_TRAP32 = 0xF,
|
||||||
|
|
||||||
IDT_PRESENT = 1 << 7,
|
IDT_PRESENT = 1 << 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct idt_gate_descriptor idt_encode_descriptor(uint32_t offset, uint16_t segment_selector, uint8_t ist, enum idt_flags type);
|
enum idt_dpl : uint8_t {
|
||||||
|
IDT_DPL_0 = 0 << 5,
|
||||||
|
IDT_DPL_1 = 1 << 5,
|
||||||
|
IDT_DPL_2 = 2 << 5,
|
||||||
|
IDT_DPL_3 = 3 << 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct idt_gate_descriptor idt_encode_descriptor(void* func, segment_t segment_selector, enum idt_dpl dpl, enum idt_flags type);
|
||||||
|
|
||||||
void idt_load(struct idt_gate_descriptor table[], uint16_t size);
|
void idt_load(struct idt_gate_descriptor table[], uint16_t size);
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "libc.h"
|
#include "libc.h"
|
||||||
|
#include "tty.h"
|
||||||
|
#include "interrupts.h"
|
||||||
|
#include "kernel_state.h"
|
||||||
|
|
||||||
#ifdef __x86_64__
|
#define EXCEPTION_DEPTH_MAX 5
|
||||||
typedef unsigned long long int uword_t;
|
|
||||||
#else
|
|
||||||
typedef unsigned int uword_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct __attribute__((packed)) interrupt_frame {
|
struct __attribute__((packed)) interrupt_frame {
|
||||||
uword_t ip;
|
uword_t ip;
|
||||||
@@ -15,7 +14,131 @@ struct __attribute__((packed)) interrupt_frame {
|
|||||||
uword_t ss;
|
uword_t ss;
|
||||||
};
|
};
|
||||||
|
|
||||||
__attribute__((interrupt)) void interrupt_handler_1(struct interrupt_frame* frame)
|
static void print_interrupt_frame(struct interrupt_frame* f)
|
||||||
|
{
|
||||||
|
printf(str_attach(
|
||||||
|
"Interrupt frame:\n"
|
||||||
|
"================\n"
|
||||||
|
"ip: {x32}\n"
|
||||||
|
"cs: {x32}\n"
|
||||||
|
"flags: {x32}\n"
|
||||||
|
"sp: {x32}\n"
|
||||||
|
"ss: {x32}\n"),
|
||||||
|
f->ip,
|
||||||
|
f->cs,
|
||||||
|
f->flags,
|
||||||
|
f->sp,
|
||||||
|
f->ss);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exceptions
|
||||||
|
* ==========
|
||||||
|
*/
|
||||||
|
__attribute__((interrupt, noreturn))
|
||||||
|
void exception_handler_general_protection_fault(struct interrupt_frame* frame, int err)
|
||||||
|
{
|
||||||
|
if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) {
|
||||||
|
panic(str_attach("fatal: too many nested exceptions\n"));
|
||||||
|
}
|
||||||
|
//terminal_clear();
|
||||||
|
terminal_set_color(VGA_COLOR_WHITE, VGA_COLOR_RED);
|
||||||
|
printf(str_attach(
|
||||||
|
"general protection fault by segment selector {str} :(\n"),
|
||||||
|
gdt_segment_index_str[err/8]);
|
||||||
|
if (frame != NULL) {
|
||||||
|
print_interrupt_frame(frame);
|
||||||
|
}
|
||||||
|
__asm__ volatile("cli; hlt");
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((interrupt, noreturn))
|
||||||
|
void exception_handler_double_fault(struct interrupt_frame* frame)
|
||||||
{
|
{
|
||||||
(void)frame;
|
(void)frame;
|
||||||
|
if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) {
|
||||||
|
panic(str_attach("fatal: too many nested exceptions\n"));
|
||||||
|
}
|
||||||
|
panic(str_attach("double fault :("));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__attribute__((interrupt, noreturn))
|
||||||
|
void exception_handler_div_by_zero(struct interrupt_frame* frame)
|
||||||
|
{
|
||||||
|
(void)frame;
|
||||||
|
if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) {
|
||||||
|
panic(str_attach("fatal: too many nested exceptions\n"));
|
||||||
|
}
|
||||||
|
panic(str_attach("div by zero occured :("));
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((interrupt, noreturn))
|
||||||
|
void exception_handler_page_fault(struct interrupt_frame* frame, int err)
|
||||||
|
{
|
||||||
|
(void)frame;
|
||||||
|
if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) {
|
||||||
|
panic(str_attach("fatal: too many nested exceptions\n"));
|
||||||
|
}
|
||||||
|
terminal_set_color(VGA_COLOR_WHITE, VGA_COLOR_RED);
|
||||||
|
printf(str_attach(
|
||||||
|
"page fault :(, err: 0x{x32}\n"),
|
||||||
|
err,
|
||||||
|
gdt_segment_index_str[err/8]);
|
||||||
|
__asm__ volatile("cli; hlt");
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((interrupt, noreturn))
|
||||||
|
void exception_default(struct interrupt_frame* frame)
|
||||||
|
{
|
||||||
|
(void)frame;
|
||||||
|
if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) {
|
||||||
|
panic(str_attach("fatal: too many nested exceptions\n"));
|
||||||
|
}
|
||||||
|
panic(str_attach("non-implemented exception occured"));
|
||||||
|
|
||||||
|
kernel.nested_exception_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interrupts
|
||||||
|
* ==========
|
||||||
|
*/
|
||||||
|
__attribute__((interrupt, noreturn))
|
||||||
|
void interrupt_default(struct interrupt_frame* frame)
|
||||||
|
{
|
||||||
|
if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) {
|
||||||
|
panic(str_attach("fatal: too many nested exceptions\n"));
|
||||||
|
}
|
||||||
|
(void)frame;
|
||||||
|
panic(str_attach("non-implemented interrupt invoked"));
|
||||||
|
|
||||||
|
kernel.nested_exception_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((interrupt))
|
||||||
|
void interrupt_handler_1(struct interrupt_frame* frame)
|
||||||
|
{
|
||||||
|
if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) {
|
||||||
|
panic(str_attach("fatal: too many nested exceptions\n"));
|
||||||
|
}
|
||||||
|
(void)frame;
|
||||||
|
printf(str_attach("interrupt_handler_1 called!\n"));
|
||||||
|
|
||||||
|
kernel.nested_exception_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((interrupt))
|
||||||
|
void interrupt_handler_userspace_exit(struct interrupt_frame* frame)
|
||||||
|
{
|
||||||
|
if (kernel.nested_exception_counter++ > EXCEPTION_DEPTH_MAX) {
|
||||||
|
panic(str_attach("fatal: too many nested exceptions\n"));
|
||||||
|
}
|
||||||
|
(void)frame;
|
||||||
|
printf(str_attach("interrupt_handler_userspace_exit called!\n"));
|
||||||
|
|
||||||
|
kernel.nested_exception_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
33
src/kernel/interrupts.h
Normal file
33
src/kernel/interrupts.h
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
struct __attribute__((packed)) interrupt_frame {
|
||||||
|
uword_t ip;
|
||||||
|
uword_t cs;
|
||||||
|
uword_t flags;
|
||||||
|
uword_t sp;
|
||||||
|
uword_t ss;
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__((interrupt, noreturn))
|
||||||
|
void exception_handler_div_by_zero(struct interrupt_frame* frame);
|
||||||
|
|
||||||
|
__attribute__((interrupt, noreturn))
|
||||||
|
void exception_handler_general_protection_fault(struct interrupt_frame* frame, int err);
|
||||||
|
|
||||||
|
__attribute__((interrupt, noreturn))
|
||||||
|
void exception_handler_double_fault(struct interrupt_frame* frame);
|
||||||
|
|
||||||
|
__attribute__((interrupt, noreturn))
|
||||||
|
void exception_default(struct interrupt_frame* frame);
|
||||||
|
|
||||||
|
__attribute__((interrupt, noreturn))
|
||||||
|
void interrupt_default(struct interrupt_frame* frame);
|
||||||
|
|
||||||
|
__attribute__((interrupt))
|
||||||
|
void interrupt_handler_1(struct interrupt_frame* frame);
|
||||||
|
|
||||||
|
__attribute__((interrupt))
|
||||||
|
void interrupt_handler_userspace_exit(struct interrupt_frame* frame);
|
||||||
|
|
||||||
@@ -5,34 +5,51 @@
|
|||||||
#include "gdt.h"
|
#include "gdt.h"
|
||||||
#include "tss.h"
|
#include "tss.h"
|
||||||
#include "idt.h"
|
#include "idt.h"
|
||||||
|
#include "interrupts.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "kernel_state.h"
|
||||||
|
|
||||||
// Future user-space
|
// Future user-space
|
||||||
#include "libc.h"
|
#include "libc.h"
|
||||||
#include "tty.h"
|
#include "tty.h"
|
||||||
|
#include "str.h"
|
||||||
|
|
||||||
// TOOD: clean up this
|
static void pic_disable()
|
||||||
typedef void(*func_t)(void*);
|
|
||||||
|
|
||||||
static void ring3_mode(uint32_t, uint32_t, uint32_t, uint32_t, func_t);
|
|
||||||
static void user_mode_code(void*);
|
|
||||||
|
|
||||||
void gdt_setup_flat_model()
|
|
||||||
{
|
{
|
||||||
|
__asm__ volatile ("outb %0, %1"
|
||||||
|
:
|
||||||
|
: "a"((uint8_t)0xff), "Nd"((uint16_t)0x21)
|
||||||
|
: "memory");
|
||||||
|
__asm__ volatile ("outb %0, %1"
|
||||||
|
:
|
||||||
|
: "a"((uint8_t)0xff), "Nd"((uint16_t)0xA1)
|
||||||
|
: "memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* copied and only slightly modified from an osdev wiki article with poor
|
|
||||||
taste, TODO: change
|
|
||||||
----------------------------------------------*/
|
|
||||||
static void user_mode_code(void*)
|
static void user_mode_code(void*)
|
||||||
{
|
{
|
||||||
printf(str_attach("hello world :)\n"));
|
printf(str_attach("hello from user-space before interrupt :)\n"));
|
||||||
|
__asm__ volatile ("int $0x80");
|
||||||
|
__asm__ volatile ("int $0x81");
|
||||||
|
//while (1) /* busy loop */;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
printf(str_attach("hello from user-space before exception :)\n"));
|
||||||
|
/* test division by 0 exception */
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wdiv-by-zero"
|
||||||
|
volatile int a = 5/0;
|
||||||
|
(void)a;
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
// should not happen
|
||||||
|
printf(str_attach("hello from userspace after interrupt and exception!\n"));
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ring3_mode(uint32_t udata_offset, uint32_t udata_rpl, uint32_t ucode_offset, uint32_t ucode_rpl, func_t callback)
|
static void ring3_mode(segment_t udata_segment, segment_t ucode_segment, func_t callback)
|
||||||
{
|
{
|
||||||
const uint32_t udata = udata_offset | udata_rpl;
|
|
||||||
const uint32_t ucode = ucode_offset | ucode_rpl;
|
|
||||||
|
|
||||||
__asm__ volatile (
|
__asm__ volatile (
|
||||||
"mov %[udata], %%ax\n"
|
"mov %[udata], %%ax\n"
|
||||||
"mov %%ax, %%ds\n"
|
"mov %%ax, %%ds\n"
|
||||||
@@ -42,15 +59,15 @@ static void ring3_mode(uint32_t udata_offset, uint32_t udata_rpl, uint32_t ucode
|
|||||||
"push %%ax\n"
|
"push %%ax\n"
|
||||||
|
|
||||||
"mov %%esp, %%eax\n"
|
"mov %%esp, %%eax\n"
|
||||||
"push %%eax\n" // current esp
|
"push %%eax\n" // esp
|
||||||
"pushf\n" // eflags
|
"pushf\n" // eflags
|
||||||
"push %[ucode]\n"
|
"push %[ucode]\n"
|
||||||
"push %[callback]\n" // instruction address to return to
|
"push %[callback]\n" // instruction address to return to
|
||||||
"iret"
|
"iret"
|
||||||
:
|
:
|
||||||
: [udata] "m"(udata),
|
: [udata] "i"(udata_segment),
|
||||||
[ucode] "m"(ucode),
|
[ucode] "i"(ucode_segment),
|
||||||
[callback] "m"(callback)
|
[callback] "i"(callback)
|
||||||
: "eax"
|
: "eax"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -65,25 +82,12 @@ void kernel_main(void)
|
|||||||
{
|
{
|
||||||
__asm__ volatile("cli");
|
__asm__ volatile("cli");
|
||||||
|
|
||||||
static struct tss tss = {0};
|
_Static_assert(sizeof(kernel.gdt) == 0x30);
|
||||||
|
|
||||||
enum segment_index : size_t {
|
kernel.gdt[SEGMENT_NULL] = gdt_encode_entry((struct gdt_entry_content){0});
|
||||||
null_segment = 0,
|
|
||||||
kernel_code_segment = 1,
|
|
||||||
kernel_data_segment = 2,
|
|
||||||
user_code_segment = 3,
|
|
||||||
user_data_segment = 4,
|
|
||||||
task_state_segment = 5,
|
|
||||||
segment_count,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct gdt_table_entry gdt[segment_count];
|
|
||||||
_Static_assert(sizeof(gdt) == 0x30);
|
|
||||||
|
|
||||||
gdt[null_segment] = gdt_encode_entry((struct gdt_entry_content){0});
|
|
||||||
|
|
||||||
/* kernel */
|
/* kernel */
|
||||||
gdt[kernel_code_segment] = gdt_encode_entry((struct gdt_entry_content){
|
kernel.gdt[SEGMENT_KERNEL_CODE] = gdt_encode_entry((struct gdt_entry_content){
|
||||||
.base = 0,
|
.base = 0,
|
||||||
.limit = GDT_LIMIT_MAX,
|
.limit = GDT_LIMIT_MAX,
|
||||||
.access_byte = GDT_ACCESS_RW
|
.access_byte = GDT_ACCESS_RW
|
||||||
@@ -91,21 +95,21 @@ void kernel_main(void)
|
|||||||
| GDT_ACCESS_DESCRIPTOR
|
| GDT_ACCESS_DESCRIPTOR
|
||||||
| GDT_ACCESS_DPL_0
|
| GDT_ACCESS_DPL_0
|
||||||
| GDT_ACCESS_PRESENT,
|
| GDT_ACCESS_PRESENT,
|
||||||
.flags = GDT_SIZE
|
.flags = GDT_32BIT
|
||||||
| GDT_GRANULARITY});
|
| GDT_GRANULARITY_PAGEWISE});
|
||||||
|
|
||||||
gdt[kernel_data_segment] = gdt_encode_entry((struct gdt_entry_content){
|
kernel.gdt[SEGMENT_KERNEL_DATA] = gdt_encode_entry((struct gdt_entry_content){
|
||||||
.base = 0,
|
.base = 0,
|
||||||
.limit = GDT_LIMIT_MAX,
|
.limit = GDT_LIMIT_MAX,
|
||||||
.access_byte = GDT_ACCESS_RW
|
.access_byte = GDT_ACCESS_RW
|
||||||
| GDT_ACCESS_DESCRIPTOR
|
| GDT_ACCESS_DESCRIPTOR
|
||||||
| GDT_ACCESS_DPL_0
|
| GDT_ACCESS_DPL_0
|
||||||
| GDT_ACCESS_PRESENT,
|
| GDT_ACCESS_PRESENT,
|
||||||
.flags = GDT_SIZE
|
.flags = GDT_32BIT
|
||||||
| GDT_GRANULARITY});
|
| GDT_GRANULARITY_PAGEWISE});
|
||||||
|
|
||||||
/* user */
|
/* user */
|
||||||
gdt[user_code_segment] = gdt_encode_entry((struct gdt_entry_content) {
|
kernel.gdt[SEGMENT_USER_CODE] = gdt_encode_entry((struct gdt_entry_content) {
|
||||||
.base = 0,
|
.base = 0,
|
||||||
.limit = GDT_LIMIT_MAX,
|
.limit = GDT_LIMIT_MAX,
|
||||||
.access_byte = GDT_ACCESS_RW
|
.access_byte = GDT_ACCESS_RW
|
||||||
@@ -113,54 +117,111 @@ void kernel_main(void)
|
|||||||
| GDT_ACCESS_DESCRIPTOR
|
| GDT_ACCESS_DESCRIPTOR
|
||||||
| GDT_ACCESS_DPL_3
|
| GDT_ACCESS_DPL_3
|
||||||
| GDT_ACCESS_PRESENT,
|
| GDT_ACCESS_PRESENT,
|
||||||
.flags = GDT_SIZE
|
.flags = GDT_32BIT
|
||||||
| GDT_GRANULARITY});
|
| GDT_GRANULARITY_PAGEWISE});
|
||||||
|
|
||||||
gdt[user_data_segment] = gdt_encode_entry((struct gdt_entry_content) {
|
kernel.gdt[SEGMENT_USER_DATA] = gdt_encode_entry((struct gdt_entry_content) {
|
||||||
.base = 0,
|
.base = 0,
|
||||||
.limit = GDT_LIMIT_MAX,
|
.limit = GDT_LIMIT_MAX,
|
||||||
.access_byte = GDT_ACCESS_RW
|
.access_byte = GDT_ACCESS_RW
|
||||||
| GDT_ACCESS_DESCRIPTOR
|
| GDT_ACCESS_DESCRIPTOR
|
||||||
| GDT_ACCESS_DPL_3
|
| GDT_ACCESS_DPL_3
|
||||||
| GDT_ACCESS_PRESENT,
|
| GDT_ACCESS_PRESENT,
|
||||||
.flags = GDT_SIZE
|
.flags = GDT_32BIT
|
||||||
| GDT_GRANULARITY});
|
| GDT_GRANULARITY_PAGEWISE});
|
||||||
|
|
||||||
/* tss */
|
/* tss */
|
||||||
gdt[task_state_segment] = gdt_encode_entry((struct gdt_entry_content) {
|
kernel.gdt[SEGMENT_TASK_STATE] = gdt_encode_entry((struct gdt_entry_content) {
|
||||||
.base = (uint32_t)&tss,
|
.base = (uint32_t)&kernel.tss,
|
||||||
.limit = sizeof(tss)-1,
|
.limit = sizeof(kernel.tss)-1,
|
||||||
.access_byte = GDT_ACCESS_ACCESSED
|
.access_byte = GDT_ACCESS_ACCESSED
|
||||||
| GDT_ACCESS_EXEC
|
| GDT_ACCESS_EXEC
|
||||||
| GDT_ACCESS_DPL_0
|
| GDT_ACCESS_DPL_0
|
||||||
| GDT_ACCESS_PRESENT,
|
| GDT_ACCESS_PRESENT,
|
||||||
.flags = 0});
|
.flags = 0});
|
||||||
|
|
||||||
gdt_set(sizeof gdt, gdt, 0);
|
|
||||||
|
|
||||||
gdt_reload(kernel_data_segment * sizeof (struct gdt_table_entry),
|
|
||||||
kernel_code_segment * sizeof (struct gdt_table_entry));
|
|
||||||
|
|
||||||
/* Setup the TSS */
|
/* Setup the TSS */
|
||||||
tss.ss0 = kernel_data_segment * sizeof (struct gdt_table_entry);
|
memset(&kernel.tss, 0, sizeof kernel.tss);
|
||||||
tss.esp0 = 0; /* TODO: set kernel stack pointer */
|
kernel.tss.ss0 = segment(SEGMENT_KERNEL_DATA, SEGMENT_GDT, 0);
|
||||||
|
#if 1
|
||||||
tss_load(task_state_segment * sizeof (struct gdt_table_entry));
|
static uint8_t kernel_stack[1024];
|
||||||
|
kernel.tss.esp0 = (uint32_t)kernel_stack;
|
||||||
|
#else
|
||||||
|
kernel.tss.esp0 = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 1
|
||||||
/* Setup the IDT */
|
/* Setup the IDT */
|
||||||
//static struct idt_gate_descriptor idt[256] = {0};
|
for (size_t i = 0; i < 32; i++) {
|
||||||
|
kernel.idt[i] = idt_encode_descriptor(
|
||||||
|
exception_default,
|
||||||
|
segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0),
|
||||||
|
IDT_DPL_3,
|
||||||
|
IDT_GATE_TYPE_TRAP32);
|
||||||
|
}
|
||||||
|
for (size_t i = 32; i < IDT_COUNT; i++) {
|
||||||
|
kernel.idt[i] = idt_encode_descriptor(
|
||||||
|
interrupt_default,
|
||||||
|
segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0),
|
||||||
|
IDT_DPL_3,
|
||||||
|
IDT_GATE_TYPE_INTERRUPT32);
|
||||||
|
}
|
||||||
|
|
||||||
//struct idt_gate_descriptor idt_encode_descriptor(uint32_t offset, uint16_t segment_selector, uint8_t ist, enum idt_flags type);
|
kernel.idt[0x0] = idt_encode_descriptor(
|
||||||
//idt[80] = idt_encode_descriptor(interrupt_handler_1, kernel_code_segment, 0, IDT_GATE_TYPE_INTERUPT_GATE_32);
|
exception_handler_div_by_zero,
|
||||||
|
segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0),
|
||||||
|
IDT_DPL_3,
|
||||||
|
IDT_GATE_TYPE_TRAP32
|
||||||
|
);
|
||||||
|
|
||||||
//idt_load(idt, sizeof idt);
|
kernel.idt[0x8] = idt_encode_descriptor(
|
||||||
|
exception_handler_double_fault,
|
||||||
|
segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0),
|
||||||
|
IDT_DPL_3,
|
||||||
|
IDT_GATE_TYPE_TRAP32
|
||||||
|
);
|
||||||
|
|
||||||
|
kernel.idt[0xD] = idt_encode_descriptor(
|
||||||
|
exception_handler_general_protection_fault,
|
||||||
|
segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0),
|
||||||
|
IDT_DPL_3,
|
||||||
|
IDT_GATE_TYPE_TRAP32
|
||||||
|
);
|
||||||
|
|
||||||
|
// Interrupts
|
||||||
|
kernel.idt[0x80] = idt_encode_descriptor(
|
||||||
|
interrupt_handler_1,
|
||||||
|
segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0),
|
||||||
|
IDT_DPL_3,
|
||||||
|
IDT_GATE_TYPE_INTERRUPT32
|
||||||
|
);
|
||||||
|
|
||||||
|
kernel.idt[0x81] = idt_encode_descriptor(
|
||||||
|
interrupt_handler_userspace_exit,
|
||||||
|
segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0),
|
||||||
|
IDT_DPL_3,
|
||||||
|
IDT_GATE_TYPE_INTERRUPT32
|
||||||
|
);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Flush the gdt and tss */
|
||||||
|
gdt_load(kernel.gdt,
|
||||||
|
sizeof kernel.gdt,
|
||||||
|
segment(SEGMENT_KERNEL_DATA, SEGMENT_GDT, 0),
|
||||||
|
segment(SEGMENT_KERNEL_CODE, SEGMENT_GDT, 0));
|
||||||
|
tss_load(segment(SEGMENT_TASK_STATE, SEGMENT_GDT, 0));
|
||||||
|
idt_load(kernel.idt, sizeof kernel.idt);
|
||||||
|
|
||||||
|
/* enable interrupts */
|
||||||
|
__asm__ volatile("sti");
|
||||||
|
|
||||||
/* Finally go to ring 3 */
|
/* Finally go to ring 3 */
|
||||||
ring3_mode(user_data_segment * sizeof (struct gdt_table_entry), 3,
|
printf(str_attach("hello from kernel space!\n"));
|
||||||
user_code_segment * sizeof (struct gdt_table_entry), 3,
|
|
||||||
|
pic_disable(); // temporary duct-tape
|
||||||
|
|
||||||
|
ring3_mode(segment(SEGMENT_USER_DATA, SEGMENT_GDT, 3),
|
||||||
|
segment(SEGMENT_USER_CODE, SEGMENT_GDT, 3),
|
||||||
user_mode_code);
|
user_mode_code);
|
||||||
|
|
||||||
|
|
||||||
__asm__ volatile("sti");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
src/kernel/kernel_state.c
Normal file
18
src/kernel/kernel_state.c
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
#include "str.h"
|
||||||
|
|
||||||
|
#include "kernel_state.h"
|
||||||
|
|
||||||
|
const struct str gdt_segment_index_str[SEGMENT_COUNT] = {
|
||||||
|
[SEGMENT_NULL ] = str_attach("SEGMENT_NULL"),
|
||||||
|
[SEGMENT_KERNEL_CODE] = str_attach("SEGMENT_KERNEL_CODE"),
|
||||||
|
[SEGMENT_KERNEL_DATA] = str_attach("SEGMENT_KERNEL_DATA"),
|
||||||
|
[SEGMENT_USER_CODE ] = str_attach("SEGMENT_USER_CODE"),
|
||||||
|
[SEGMENT_USER_DATA ] = str_attach("SEGMENT_USER_DATA"),
|
||||||
|
[SEGMENT_TASK_STATE ] = str_attach("SEGMENT_TASK_STATE"),
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct str idt_index_str[IDT_COUNT] = {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct kernel_state kernel = { 0 };
|
||||||
34
src/kernel/kernel_state.h
Normal file
34
src/kernel/kernel_state.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "str.h"
|
||||||
|
#include "tss.h"
|
||||||
|
#include "idt.h"
|
||||||
|
#include "gdt.h"
|
||||||
|
|
||||||
|
enum gdt_segment_index : size_t {
|
||||||
|
SEGMENT_NULL,
|
||||||
|
SEGMENT_KERNEL_CODE,
|
||||||
|
SEGMENT_KERNEL_DATA,
|
||||||
|
SEGMENT_USER_CODE,
|
||||||
|
SEGMENT_USER_DATA,
|
||||||
|
SEGMENT_TASK_STATE,
|
||||||
|
SEGMENT_COUNT,
|
||||||
|
};
|
||||||
|
extern const struct str gdt_segment_index_str[SEGMENT_COUNT]; // reverse lookup enum -> str
|
||||||
|
|
||||||
|
enum idt_index : size_t {
|
||||||
|
IDT_COUNT = 256
|
||||||
|
};
|
||||||
|
extern const struct str idt_index_str[IDT_COUNT]; // reverse lookup enum -> str
|
||||||
|
|
||||||
|
constexpr size_t BACKTRACE_MAX = 256;
|
||||||
|
|
||||||
|
struct kernel_state {
|
||||||
|
struct tss tss;
|
||||||
|
struct gdt_table_entry gdt[SEGMENT_COUNT];
|
||||||
|
struct idt_gate_descriptor idt[IDT_COUNT];
|
||||||
|
int nested_exception_counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct kernel_state kernel;
|
||||||
27
src/kernel/types.h
Normal file
27
src/kernel/types.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
typedef void(*func_t)(void*);
|
||||||
|
|
||||||
|
/* define uword_t per the instruction set manual */
|
||||||
|
#ifdef __x86_64__
|
||||||
|
typedef unsigned long long int uword_t;
|
||||||
|
#else
|
||||||
|
typedef unsigned int uword_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* segment selectors identifies segments either in the gdt or the ldt */
|
||||||
|
typedef uint16_t segment_t;
|
||||||
|
|
||||||
|
enum segment_ti : uint16_t {
|
||||||
|
SEGMENT_GDT = 0,
|
||||||
|
SEGMENT_LDT = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline segment_t segment(uint16_t index, enum segment_ti ti, uint16_t rpl)
|
||||||
|
{
|
||||||
|
return (index << 3) | ((ti & 0xF) << 1) | (rpl & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -15,10 +15,10 @@
|
|||||||
__attribute__((noreturn))
|
__attribute__((noreturn))
|
||||||
void panic(struct str s)
|
void panic(struct str s)
|
||||||
{
|
{
|
||||||
terminal_clear();
|
//terminal_clear();
|
||||||
terminal_set_color(VGA_COLOR_WHITE, VGA_COLOR_RED);
|
terminal_set_color(VGA_COLOR_WHITE, VGA_COLOR_RED);
|
||||||
terminal_write(s);
|
terminal_write(s);
|
||||||
__asm__ volatile("hlt");
|
__asm__ volatile("cli; hlt");
|
||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,12 +31,16 @@ static inline int ps_get(struct printf_state* s)
|
|||||||
return s->str.data[s->i++];
|
return s->str.data[s->i++];
|
||||||
}
|
}
|
||||||
|
|
||||||
static int print_long(unsigned long n, struct str alphabet, bool is_signed)
|
static int print_long(unsigned long n, struct str alphabet, bool is_signed, unsigned int padding, char pad_char)
|
||||||
{
|
{
|
||||||
constexpr size_t BUF_SZ = 21;
|
constexpr size_t BUF_SZ = 128;
|
||||||
char buf[BUF_SZ];
|
char buf[BUF_SZ];
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
|
||||||
|
if (padding > BUF_SZ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
terminal_putchar('0');
|
terminal_putchar('0');
|
||||||
return 1;
|
return 1;
|
||||||
@@ -59,32 +63,55 @@ static int print_long(unsigned long n, struct str alphabet, bool is_signed)
|
|||||||
buf[BUF_SZ-i] = alphabet.data[n % alphabet.len];
|
buf[BUF_SZ-i] = alphabet.data[n % alphabet.len];
|
||||||
n /= alphabet.len;
|
n /= alphabet.len;
|
||||||
}
|
}
|
||||||
|
while (i < padding) {
|
||||||
|
i += 1;
|
||||||
|
buf[BUF_SZ-i] = pad_char;
|
||||||
|
}
|
||||||
|
|
||||||
terminal_write((struct str){.data = &buf[BUF_SZ-i], .len = i});
|
terminal_write((struct str){.data = &buf[BUF_SZ-i], .len = i});
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int print_i32(struct printf_state* s)
|
static int print_i32(struct printf_state* s, int padding, char pad_char)
|
||||||
{
|
{
|
||||||
|
padding = padding ? padding : 0;
|
||||||
|
pad_char = pad_char ? pad_char : ' ';
|
||||||
long n = va_arg(s->ap, int32_t);
|
long n = va_arg(s->ap, int32_t);
|
||||||
return print_long(n, str_attach("0123456789"), true);
|
return print_long(n, str_attach("0123456789"), true, padding, pad_char);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int print_u32(struct printf_state* s)
|
static int print_u32(struct printf_state* s, int padding, char pad_char)
|
||||||
{
|
{
|
||||||
|
padding = padding ? padding : 0;
|
||||||
|
pad_char = pad_char ? pad_char : ' ';
|
||||||
unsigned long n = va_arg(s->ap, uint32_t);
|
unsigned long n = va_arg(s->ap, uint32_t);
|
||||||
return print_long(n, str_attach("0123456789"), false);
|
return print_long(n, str_attach("0123456789"), false, padding, pad_char);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int print_x32(struct printf_state* s)
|
static int print_x32(struct printf_state* s, int padding, char pad_char)
|
||||||
{
|
{
|
||||||
|
padding = padding ? padding : 0;
|
||||||
|
pad_char = pad_char ? pad_char : '0';
|
||||||
unsigned long n = va_arg(s->ap, uint32_t);
|
unsigned long n = va_arg(s->ap, uint32_t);
|
||||||
return print_long(n, str_attach("0123456789abcdefgh"), false);
|
struct str alphabet = str_attach("0123456789abcdef");
|
||||||
|
return print_long(n, alphabet, false, padding, pad_char);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int print_str(struct printf_state* s)
|
static int print_b32(struct printf_state* s, int padding, char pad_char)
|
||||||
{
|
{
|
||||||
|
padding = padding ? padding : 32;
|
||||||
|
pad_char = pad_char ? pad_char : '0';
|
||||||
|
unsigned long n = va_arg(s->ap, uint32_t);
|
||||||
|
struct str alphabet = str_attach("01");
|
||||||
|
return print_long(n, alphabet, false, padding, pad_char);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int print_str(struct printf_state* s, int padding, char pad_char)
|
||||||
|
{
|
||||||
|
// TODO: implement padding
|
||||||
|
(void)padding;
|
||||||
|
(void)pad_char;
|
||||||
const struct str str = va_arg(s->ap, struct str);
|
const struct str str = va_arg(s->ap, struct str);
|
||||||
for (size_t i=0; i < str.len; i++) {
|
for (size_t i=0; i < str.len; i++) {
|
||||||
terminal_putchar(str.data[i]);
|
terminal_putchar(str.data[i]);
|
||||||
@@ -97,6 +124,8 @@ static int parse_format_cmd(struct printf_state* s)
|
|||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
uint32_t cmd = 0;
|
uint32_t cmd = 0;
|
||||||
|
int pad = 0;
|
||||||
|
char pad_char = '\0';
|
||||||
|
|
||||||
constexpr uint32_t CHAR_MASK = (1<<CHAR_BIT)-1;
|
constexpr uint32_t CHAR_MASK = (1<<CHAR_BIT)-1;
|
||||||
|
|
||||||
@@ -112,18 +141,18 @@ static int parse_format_cmd(struct printf_state* s)
|
|||||||
// 16 bit types are promoted to 32 bit by va_arg(...) anyways
|
// 16 bit types are promoted to 32 bit by va_arg(...) anyways
|
||||||
case 'i16':
|
case 'i16':
|
||||||
case 'i32':
|
case 'i32':
|
||||||
return print_i32(s);
|
return print_i32(s, pad, pad_char);
|
||||||
|
|
||||||
case 'u16':
|
case 'u16':
|
||||||
case 'u32':
|
case 'u32':
|
||||||
return print_u32(s);
|
return print_u32(s, pad, pad_char);
|
||||||
|
|
||||||
case 'x16':
|
case 'x16':
|
||||||
case 'x32':
|
case 'x32':
|
||||||
return print_x32(s);
|
return print_x32(s, pad, pad_char);
|
||||||
|
|
||||||
case 'str':
|
case 'str':
|
||||||
return print_str(s);
|
return print_str(s, pad, pad_char);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
Reference in New Issue
Block a user