Merge branch 'paging' into public

This commit is contained in:
Alexander Myltsev 2025-01-18 15:46:43 +04:00
commit 2989d96266
32 changed files with 664 additions and 177 deletions

3
.gitignore vendored
View File

@ -1,7 +1,6 @@
*.o
*.bin
*.img
*.bin
*.elf
*.dSYM
tools/mkfs
ejudge.sh

View File

@ -18,9 +18,10 @@ endif
CFLAGS = -fno-pic -ffreestanding -static -fno-builtin -fno-strict-aliasing \
-mno-sse \
-I. \
-Wall -ggdb -m32 -Werror -fno-omit-frame-pointer
CFLAGS += $(shell $(CC) -fno-stack-protector -E -x c /dev/null >/dev/null 2>&1 && echo -fno-stack-protector)
ASMFLAGS = -m32 -ffreestanding -c -g
ASMFLAGS = -m32 -ffreestanding -c -g -I.
ifeq ($(LLVM),on)
@ -38,7 +39,7 @@ endif
OBJECTS = ./kernel.o ./console.o ./drivers/vga.o ./drivers/uart.o ./drivers/keyboard.o \
./cpu/idt.o ./cpu/gdt.o ./cpu/swtch.o ./cpu/vectors.o ./lib/mem.o ./proc.o ./lib/string.o \
./fs/fs.o
./fs/fs.o ./drivers/ata.o ./lib/string.o ./proc.o ./drivers/pit.o ./kernel/vm.o
run: image.bin
qemu-system-i386 -drive format=raw,file=$< -serial mon:stdio
@ -87,7 +88,7 @@ debug: image.bin
qemu-system-i386 -drive format=raw,file=$< -s -S &
$(GDB) kernel.bin \
-ex "target remote localhost:1234" \
-ex "break _start" \
-ex "break kmain" \
-ex "continue"
debug-nox: image.bin
@ -97,16 +98,16 @@ debug-nox: image.bin
-ex "break _start" \
-ex "continue"
fs.img: ./kernel.bin ./tools/mkfs ./user/false ./user/greet ./user/div0
./tools/mkfs $@ $< ./user/false ./user/greet ./user/div0
fs.img: ./kernel.bin ./tools/mkfs ./user/false ./user/greet ./user/div0 ./user/shout
./tools/mkfs $@ $< ./user/false ./user/greet ./user/div0 ./user/shout
LDFLAGS=-m elf_i386
user/%: user/%.o user/crt.o
$(LD) $(LDFLAGS) -o $@ -Ttext 0x1000 $^
$(LD) $(LDFLAGS) -o $@ -Ttext 0x401000 $^
kernel.bin: $(OBJECTS)
$(LD) $(LDFLAGS) $(LDKERNELFLAGS) -o $@ -Ttext 0x9000 $^
$(LD) $(LDFLAGS) $(LDKERNELFLAGS) -o $@ -Ttext 0x80009000 $^
bootmain.o: bootmain.c
$(CC) $(CFLAGS) -Os -c $< -o $@

View File

@ -40,7 +40,7 @@ bootmain(void)
ph = (Elf32_Phdr*)((uchar*)elf + elf->e_phoff);
eph = ph + elf->e_phnum;
for(; ph < eph; ph++) {
pa = (uchar*)ph->p_paddr;
pa = (uchar*)(ph->p_paddr & 0x0fffffff);
readseg(pa, ph->p_filesz, ph->p_offset);
if(ph->p_memsz > ph->p_filesz)
stosb(pa + ph->p_filesz, 0, ph->p_memsz - ph->p_filesz);
@ -48,7 +48,7 @@ bootmain(void)
// Call the entry point from the ELF header.
// Does not return!
entry = (void(*)(void))(elf->e_entry);
entry = (void(*)(void))(elf->e_entry & 0x0fffffff);
entry();
}

View File

@ -3,7 +3,7 @@
#include "drivers/uart.h"
void printk(const char* msg) {
vga_print_string_noscroll(msg);
vga_print_string(msg);
for (; *msg; ++msg) {
uartputc(*msg);
}

View File

@ -1,5 +1,8 @@
#include "gdt.h"
#include "x86.h"
#include "memlayout.h"
#include "../lib/string.h"
#include "kernel/mem.h"
#include <stdint.h>
@ -31,10 +34,10 @@ struct seg_desc_t {
struct seg_desc_t seg_desc[6];
void init_seg_desc() {
seg_desc[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, 0);
seg_desc[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0);
seg_desc[SEG_UCODE] = SEG(STA_X|STA_R, USER_BASE, 0xffffffff - USER_BASE, DPL_USER);
seg_desc[SEG_UDATA] = SEG(STA_W, USER_BASE, 0xffffffff - USER_BASE, DPL_USER);
seg_desc[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, 0);
seg_desc[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0);
seg_desc[SEG_UCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, DPL_USER);
seg_desc[SEG_UDATA] = SEG(STA_W, 0, 0xffffffff, DPL_USER);
}
struct gdt_desc_t {
@ -51,7 +54,7 @@ void load_gdt() {
asm("lgdt %0": : "m"(gdt_desc));
}
void switchuvm(struct taskstate *tss, void* esp) {
void switchuvm(struct taskstate *tss, void* esp, pde_t *pgdir) {
memset(tss, 0, sizeof(*tss));
seg_desc[SEG_TSS] = SEG16(STS_T32A, tss, sizeof(*tss)-1, 0);
seg_desc[SEG_TSS].s = 0;
@ -62,4 +65,6 @@ void switchuvm(struct taskstate *tss, void* esp) {
tss->iomb = (ushort) 0xFFFF;
asm("ltr %0": : "r"((ushort)(SEG_TSS << 3)));
lcr3(V2P(pgdir));
}

View File

@ -29,6 +29,7 @@
#define KERN_STACK_BASE 0x90000
#ifndef __ASSEMBLER__
#include "kernel/mem.h"
typedef unsigned uint;
typedef unsigned short ushort;
@ -73,5 +74,5 @@ struct taskstate {
};
void load_gdt();
void switchuvm(struct taskstate *tss, void* esp);
void switchuvm(struct taskstate *tss, void* esp, pde_t *pgdir);
#endif

View File

@ -105,10 +105,32 @@ void trap(registers_t *r) {
if (r->int_no < ARRLEN(exception_messages)) {
msg = exception_messages[r->int_no];
}
if (r->cs & 3) {
// exception from user mode, kill offending process
printk("Exception: ");
printk(msg);
printk("\n");
killproc();
}
panic(msg);
}
}
static void* get_userspace_ptr(uint32_t ptr) {
if (ptr >= 0xffffffff - USER_BASE) {
return 0;
}
return (void*)(ptr + USER_BASE);
}
static int handle_puts(const char* s) {
if (!s) {
return -1;
}
printk(s);
return 0;
}
static void handle_syscall(registers_t* r) {
switch (r->eax) {
case SYS_exit:
@ -122,6 +144,13 @@ static void handle_syscall(registers_t* r) {
printk("Hello world!\n");
r->eax = 0;
break;
case SYS_putc:
printk((const char[]){r->ebx, '\0'});
r->eax = 0;
break;
case SYS_puts:
r->eax = handle_puts(get_userspace_ptr(r->ebx));
break;
default:
printk("Unknown syscall\n");
r->eax = -1;

View File

@ -21,19 +21,17 @@ enum {
};
/* Struct which aggregates many registers.
* It matches exactly the pushes on vectors.S. From the bottom:
* - pushed by the processor automatically
* It matches exactly the pushes on interrupt.asm. From the bottom:
* - Pushed by the processor automatically
* - `push byte`s on the isr-specific code: error code, then int number
* - segment registers
* - all the registers by pusha
* - All the registers by pusha
* - `push eax` whose lower 16-bits contain DS
*/
typedef struct {
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */
uint32_t gs, fs, es, ds;
uint32_t int_no, err_code; // Interrupt number and error code (if applicable)
uint32_t eip, cs, eflags; // Pushed by the processor automatically
uint32_t useresp, ss; // Pushed by the processor for userspace interrupts
uint32_t gs, fs, es, ds; /* Data segment selector */
uint32_t int_no, err_code; /* Interrupt number and error code (if applicable) */
uint32_t eip, cs, eflags, useresp, ss; /* Pushed by the processor automatically */
} registers_t;
void isr_install();

41
cpu/memlayout.h Normal file
View File

@ -0,0 +1,41 @@
#pragma once
#define KERNBASE 0x80000000
#define PGSIZE 0x1000
#define PHYSTOP 0x8000000
#define PGROUNDUP(sz) (((sz)+PGSIZE-1) & ~(PGSIZE-1))
#define PGROUNDDOWN(a) (((a)) & ~((uintptr_t)(PGSIZE-1)))
#define NPDENTRIES 1024 // # directory entries per page directory
#define NPTENTRIES 1024 // # PTEs per page table
#define PTXSHIFT 12 // offset of PTX in a linear address
#define PDXSHIFT 22 // offset of PDX in a linear address
#define PXMASK 0x3FF
#ifndef __ASSEMBLER__
#include <stdint.h>
#define V2P(a) (((uintptr_t) (a)) - KERNBASE)
#define P2V(a) ((void *)(((uintptr_t) (a)) + KERNBASE))
// page directory index
#define PDX(va) (((uintptr_t)(va) >> PDXSHIFT) & PXMASK)
// page table index
#define PTX(va) (((uintptr_t)(va) >> PTXSHIFT) & PXMASK)
// Address in page table or page directory entry
#define PTE_ADDR(pte) ((uintptr_t)(pte) & ~0xFFF)
#endif
// Page table/directory entry flags.
#define PTE_P 0x001 // Present
#define PTE_W 0x002 // Writeable
#define PTE_U 0x004 // User
#define PTE_PWT 0x008 // Write-Through
#define PTE_PCD 0x010 // Cache-Disable
#define PTE_A 0x020 // Accessed
#define PTE_D 0x040 // Dirty
#define PTE_PS 0x080 // Page Size

19
cpu/x86.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
static inline void
stosl(void *addr, int data, int cnt)
{
asm volatile("cld; rep stosl" : : "D"(addr), "c"(cnt), "a"(data) : "memory");
}
static inline void
stosb(void *addr, unsigned char data, int cnt)
{
asm volatile("cld; rep stosb" : : "D"(addr), "c"(cnt), "a"(data) : "memory");
}
static inline void
lcr3(uint32_t val)
{
asm volatile("mov %0,%%cr3" : : "r" (val));
}

View File

@ -1,80 +1,74 @@
// stolen from https://github.com/dhavalhirdhav/LearnOS/blob/master/drivers/ata/ata.c
// stolen from
// https://github.com/dhavalhirdhav/LearnOS/blob/master/drivers/ata/ata.c
#include "ata.h"
#include <stdint.h>
#include "ata.h"
#include "port.h"
/*
BSY: a 1 means that the controller is busy executing a command. No register should be accessed (except the digital output register) while this bit is set.
RDY: a 1 means that the controller is ready to accept a command, and the drive is spinning at correct speed..
WFT: a 1 means that the controller detected a write fault.
SKC: a 1 means that the read/write head is in position (seek completed).
DRQ: a 1 means that the controller is expecting data (for a write) or is sending data (for a read). Don't access the data register while this bit is 0.
COR: a 1 indicates that the controller had to correct data, by using the ECC bytes (error correction code: extra bytes at the end of the sector that allows to verify its integrity and, sometimes, to correct errors).
IDX: a 1 indicates the the controller retected the index mark (which is not a hole on hard-drives).
ERR: a 1 indicates that an error occured. An error code has been placed in the error register.
*/
#define STATUS_BSY 0x80
#define STATUS_RDY 0x40
#define STATUS_DRQ 0x08
#define STATUS_DF 0x20
#define STATUS_DF 0x20
#define STATUS_ERR 0x01
//This is really specific to our OS now, assuming ATA bus 0 master
//Source - OsDev wiki
// This is really specific to our OS now, assuming ATA bus 0 master
// Source - OsDev wiki https://wiki.osdev.org/ATA_PIO_Mode
static void ATA_wait_BSY();
static void ATA_wait_DRQ();
void read_sectors_ATA_PIO(uint32_t target_address, uint32_t LBA, uint8_t sector_count)
static void ATA_wait_RDY();
void read_sectors_ATA_PIO(void* target_address, uint32_t LBA, uint8_t sector_count)
{
ATA_wait_BSY();
port_byte_out(0x1F6, 0xE0 | ((LBA >> 24) & 0xF));
port_byte_out(0x1F2, sector_count);
port_byte_out(0x1F3, (uint8_t)LBA);
port_byte_out(0x1F4, (uint8_t)(LBA >> 8));
port_byte_out(0x1F5, (uint8_t)(LBA >> 16));
port_byte_out(0x1F7, 0x20); // Send the read command
ATA_wait_BSY();
port_byte_out(0x1F6, 0xE0 | ((LBA >>24) & 0xF));
port_byte_out(0x1F2, sector_count);
port_byte_out(0x1F3, (uint8_t) LBA);
port_byte_out(0x1F4, (uint8_t)(LBA >> 8));
port_byte_out(0x1F5, (uint8_t)(LBA >> 16));
port_byte_out(0x1F7, 0x20); //Send the read command
uint16_t *target = (uint16_t *)target_address;
uint16_t *target = (uint16_t*) target_address;
for (int j = 0; j < sector_count; j++)
{
ATA_wait_BSY();
ATA_wait_DRQ();
for(int i = 0; i < 256; i++)
target[i] = port_word_in(0x1F0);
target += 256;
}
for (int j = 0; j < sector_count; j++)
{
ATA_wait_BSY();
ATA_wait_RDY();
for (int i = 0; i < 256; i++)
target[i] = port_word_in(0x1F0);
target += 256;
}
}
void write_sectors_ATA_PIO(uint32_t LBA, uint8_t sector_count, uint32_t* bytes)
void write_sectors_ATA_PIO(uint32_t LBA, uint8_t sector_count, uint32_t *bytes)
{
ATA_wait_BSY();
port_byte_out(0x1F6,0xE0 | ((LBA >>24) & 0xF));
port_byte_out(0x1F2,sector_count);
port_byte_out(0x1F3, (uint8_t) LBA);
port_byte_out(0x1F4, (uint8_t)(LBA >> 8));
port_byte_out(0x1F5, (uint8_t)(LBA >> 16));
port_byte_out(0x1F7,0x30); //Send the write command
ATA_wait_BSY();
port_byte_out(0x1F6, 0xE0 | ((LBA >> 24) & 0xF));
port_byte_out(0x1F2, sector_count);
port_byte_out(0x1F3, (uint8_t)LBA);
port_byte_out(0x1F4, (uint8_t)(LBA >> 8));
port_byte_out(0x1F5, (uint8_t)(LBA >> 16));
port_byte_out(0x1F7, 0x30); // Send the write command
for (int j =0;j<sector_count;j++)
{
ATA_wait_BSY();
ATA_wait_DRQ();
for(int i=0;i<256;i++)
{
port_long_out(0x1F0, bytes[i]);
}
}
for (int j = 0; j < sector_count; j++)
{
ATA_wait_BSY();
ATA_wait_RDY();
for (int i = 0; i < 256; i++)
{
port_long_out(0x1F0, bytes[i]);
}
}
}
static void ATA_wait_BSY() //Wait for bsy to be 0
static void ATA_wait_BSY() // Wait for bsy to be 0
{
while (port_byte_in(0x1F7) & STATUS_BSY);
while (port_byte_in(0x1F7) & STATUS_BSY)
;
}
static void ATA_wait_DRQ() //Wait fot drq to be 1
static void ATA_wait_RDY() // Wait for rdy to be 1
{
while(!(port_byte_in(0x1F7) & STATUS_RDY));
while (!(port_byte_in(0x1F7) & STATUS_RDY))
;
}

View File

@ -1,5 +1,4 @@
#pragma once
#include <stdint.h>
void read_sectors_ATA_PIO(uint32_t target_address, uint32_t LBA, uint8_t sector_count);
void write_sectors_ATA_PIO(uint32_t LBA, uint8_t sector_count, uint32_t* bytes);
void read_sectors_ATA_PIO(void* target_address, uint32_t LBA, uint8_t sector_count);

View File

@ -1,8 +1,9 @@
#include "keyboard.h"
#include "../cpu/isr.h"
#include "../console.h"
#include "cpu/isr.h"
#include "cpu/memlayout.h"
#include "console.h"
#include "port.h"
#include "../lib/mem.h"
#include "kernel/mem.h"
static const char sc_ascii[] = {
'?', '?', '1', '2', '3', '4', '5', '6',
@ -12,7 +13,7 @@ static const char sc_ascii[] = {
'b', 'n', 'm', ',', '.', '/', '?', '?', '?', ' ',
};
enum { kbd_buf_capacity = 1024 };
enum { kbd_buf_capacity = PGSIZE };
static void interrupt_handler(registers_t *r) {
uint8_t scancode = port_byte_in(0x60);
@ -30,7 +31,7 @@ char* kbd_buf;
unsigned kbd_buf_size;
void init_keyboard() {
kbd_buf = kmalloc(kbd_buf_capacity);
kbd_buf = kalloc();
register_interrupt_handler(IRQ1, interrupt_handler);
}

View File

@ -4,8 +4,5 @@
__attribute__((noreturn))
static inline void qemu_shutdown() {
port_word_out(0x604, 0x2000);
while (1) {
asm("hlt");
}
__builtin_unreachable();
}

76
drivers/pit.c Normal file
View File

@ -0,0 +1,76 @@
#include "pit.h"
#include "port.h"
#include "../cpu/isr.h"
enum {
PIT_CRYSTAL_HZ = 1193182,
CLOCK_PRECISION_HZ = 100,
PIT_PROGRAM_REG = PIT_CRYSTAL_HZ / CLOCK_PRECISION_HZ,
PIT_SELECT_CHANNEL0 = 0x0,
PIT_SELECT_CHANNEL1 = 0x1,
PIT_SELECT_CHANNEL2 = 0x2,
PIT_SELECT_CHANNEL_RB = 0x3,
PIT_ACCESS_MODE_LATCH_COUNT_VALUE_COMMAND = 0x0,
PIT_ACCESS_MODE_LOBYTE_ONLY = 0x1,
PIT_ACCESS_MODE_HIBYTE_ONLY = 0x2,
PIT_ACCESS_MODE_LOHIBYTE = 0x3,
PIT_MODE_INTERRUPT_ON_TERMINAL_COUNT = 0x0,
PIT_MODE_HW_ONESHOT = 0x1,
PIT_MODE_RATE_GENERATOR = 0x2,
PIT_MODES_SQUARE_WAVE_GENERATOR = 0x3,
PIT_MODES_SW_TRIGGERRED_STROBE = 0x4,
PIT_MODES_HW_TRIGGERRED_STROBE = 0x5,
};
static timer_callback callbacks[100];
static int registered_callbacks = 0;
void add_timer_callback(timer_callback tc) {
callbacks[registered_callbacks++] = tc;
}
static void timer_interrupt_handler(registers_t *r) {
for (int i = 0; i < registered_callbacks; ++i) {
callbacks[i]();
}
}
struct pit_command_t {
unsigned char bcd : 1;
unsigned char operating_mode : 3;
unsigned char access_mode : 2;
unsigned char select_channel : 2;
} __attribute__((packed));
static void dec_sleep_counter(void);
void init_pit() {
struct pit_command_t cmd = {
.select_channel = PIT_SELECT_CHANNEL0,
.access_mode = PIT_ACCESS_MODE_LOHIBYTE,
.operating_mode = PIT_MODES_SQUARE_WAVE_GENERATOR,
.bcd = 0,
};
port_byte_out(0x43, *(unsigned char *)(&cmd));
port_byte_out(0x40, PIT_PROGRAM_REG & 0xff);
port_byte_out(0x40, (PIT_PROGRAM_REG & 0xff00) >> 8);
register_interrupt_handler(IRQ0, timer_interrupt_handler);
add_timer_callback(dec_sleep_counter);
}
static int sleep_counter = 0;
static void dec_sleep_counter(void) {
sleep_counter--;
}
void msleep(int ms) {
sleep_counter = ms / 10;
while (sleep_counter > 0) {
asm("hlt");
}
}

8
drivers/pit.h Normal file
View File

@ -0,0 +1,8 @@
#pragma once
typedef void (*timer_callback)(void);
void init_pit(void);
void add_timer_callback(timer_callback tc);
void msleep(int ms);

View File

@ -1,12 +1,42 @@
#include "port.h"
#include "vga.h"
#include "../lib/string.h"
#include "cpu/memlayout.h"
char* const video_memory = (char*) 0xb8000;
static char* const video_memory = (char*) (KERNBASE + 0xb8000);
enum colors16 {
black = 0,
blue,
green,
cyan,
red,
magenta,
brown,
light_gray,
dark_gray,
light_blue,
light_green,
light_cyan,
light_red,
light_magenta,
yellow,
white,
};
static unsigned char get_color(unsigned char fg, unsigned char bg) {
return (bg << 4) + fg;
}
enum {
ROWS = 25,
COLS = 80,
VGA_CTRL_REGISTER = 0x3d4,
VGA_DATA_REGISTER = 0x3d5,
VGA_OFFSET_LOW = 0x0f,
VGA_OFFSET_HIGH = 0x0e,
};
static unsigned get_offset(unsigned col, unsigned row) {
return row * COLS + col;
}
@ -15,6 +45,13 @@ static unsigned get_row_from_offset(unsigned offset) {
return offset / COLS;
}
void vga_set_cursor(unsigned offset) {
port_byte_out(VGA_CTRL_REGISTER, VGA_OFFSET_HIGH);
port_byte_out(VGA_DATA_REGISTER, (unsigned char) (offset >> 8));
port_byte_out(VGA_CTRL_REGISTER, VGA_OFFSET_LOW);
port_byte_out(VGA_DATA_REGISTER, (unsigned char) (offset & 0xff));
}
unsigned vga_get_cursor() {
port_byte_out(VGA_CTRL_REGISTER, VGA_OFFSET_HIGH);
unsigned offset = port_byte_in(VGA_DATA_REGISTER) << 8;
@ -28,15 +65,23 @@ void vga_set_char(unsigned offset, char c) {
video_memory[2 * offset + 1] = get_color(light_gray, black);
}
static unsigned offset;
void vga_clear_screen() {
for (unsigned i = 0; i < ROWS * COLS; ++i) {
vga_set_char(i, ' ');
}
vga_set_cursor(0);
}
void vga_print_string_noscroll(const char* s) {
static unsigned scroll() {
kmemmove(video_memory, video_memory + COLS, 2 * COLS * (ROWS-1));
for (int col = 0; col < COLS; col++) {
vga_set_char(get_offset(col, ROWS - 1), ' ');
}
return get_offset(0, ROWS - 1);
}
void vga_print_string(const char* s) {
unsigned offset = vga_get_cursor();
while (*s != 0) {
if (*s == '\n') {
offset = get_offset(0, get_row_from_offset(offset) + 1);
@ -44,7 +89,10 @@ void vga_print_string_noscroll(const char* s) {
vga_set_char(offset, *s);
offset++;
}
offset %= COLS * ROWS;
s++;
if (offset > COLS * ROWS) {
offset = scroll();
}
}
vga_set_cursor(offset);
}

View File

@ -1,38 +1,5 @@
#pragma once
extern char* const video_memory;
enum {
ROWS = 25,
COLS = 80,
VGA_CTRL_REGISTER = 0x3d4,
VGA_DATA_REGISTER = 0x3d5,
VGA_OFFSET_LOW = 0x0f,
VGA_OFFSET_HIGH = 0x0e,
};
enum colors16 {
black = 0,
blue,
green,
cyan,
red,
magenta,
brown,
light_gray,
dark_gray,
light_blue,
light_green,
light_cyan,
light_red,
light_magenta,
yellow,
white,
};
void vga_clear_screen();
void vga_set_char(unsigned offset, char c);
void vga_print_string(const char* s);
void vga_print_string_noscroll(const char* s);

35
fs/fs.c
View File

@ -1,12 +1,39 @@
#include "fs.h"
#include "../lib/string.h"
#include "../drivers/ata.h"
#include "../console.h"
enum {
fs_start = 1, // sector where the FS starts
};
int stat(const char* name, struct stat *buf) {
panic("stat not implemented");
struct dir dir;
read_sectors_ATA_PIO(&dir, fs_start, 1);
for (int i = 0; i < ents_in_dir; ++i) {
struct dirent *e = &dir.entries[i];
if (!strncmp(e->name, name, sizeof(e->name))) {
buf->size = e->size_bytes;
buf->start_sector = e->offset_sectors;
return 0;
}
}
return -1;
}
int read_file(const char* name, void* buf, uint32_t bufsize) {
panic("read_file not implemented");
/* Find file by name and read it into buffer with size bufsize.
* At most (bufsize & -512) bytes will be read.
* Return number of bytes read or -1 on failure. */
int read_file(const struct stat *statbuf, void* buf, uint32_t bufsize) {
uint32_t sector = fs_start + statbuf->start_sector;
uint32_t bytes_read = 0;
uint32_t file_sectors = (statbuf->size + sector_size - 1) / sector_size;
while (bufsize >= sector_size && file_sectors > 0) {
read_sectors_ATA_PIO(buf, sector, 1);
sector++;
file_sectors--;
bufsize -= sector_size;
buf += sector_size;
bytes_read += sector_size;
}
return bytes_read < statbuf->size ? bytes_read : statbuf->size;
}

View File

@ -38,7 +38,8 @@ struct dir {
struct stat {
uint32_t size;
uint32_t reserved[3];
uint32_t start_sector;
uint32_t reserved1, reserved2;
};
/* Find file by name and fill information in buf.
@ -48,4 +49,4 @@ int stat(const char* name, struct stat *buf);
/* Find file by name and read it into buffer with size bufsize.
* At most (bufsize & ~511) bytes will be read.
* Return number of bytes read or -1 on failure. */
int read_file(const char* name, void* buf, uint32_t bufsize);
int read_file(const struct stat *statbuf, void* buf, uint32_t bufsize);

View File

@ -1,21 +1,27 @@
asm(".asciz \"kernel start\\n\"");
#include "console.h"
#include "cpu/isr.h"
#include "cpu/gdt.h"
#include "cpu/memlayout.h"
#include "drivers/keyboard.h"
#include "drivers/vga.h"
#include "drivers/ata.h"
#include "drivers/misc.h"
#include "drivers/pit.h"
#include "drivers/uart.h"
#include "fs/fs.h"
#include "lib/string.h"
#include "proc.h"
#include "kernel/mem.h"
void _start() {
void kmain() {
freerange(P2V(1u<<20), P2V(2u<<20)); // 1MB - 2MB
kvmalloc(); // map all of physical memory at KERNBASE
freerange(P2V(2u<<20), P2V(PHYSTOP));
load_gdt();
init_keyboard();
init_pit();
uartinit();
load_idt();
sti();
@ -28,6 +34,11 @@ void _start() {
if (kbd_buf_size > 0 && kbd_buf[kbd_buf_size-1] == '\n') {
if (!strncmp("halt\n", kbd_buf, kbd_buf_size)) {
qemu_shutdown();
} else if (!strncmp("work\n", kbd_buf, kbd_buf_size)) {
for (int i = 0; i < 5; ++i) {
msleep(1000);
printk(".");
}
} else if (!strncmp("run ", kbd_buf, 4)) {
kbd_buf[kbd_buf_size-1] = '\0';
const char* cmd = kbd_buf + 4;

44
kernel/kstart.S Normal file
View File

@ -0,0 +1,44 @@
#include "cpu/memlayout.h"
/*
Memory layout at this point (see https://wiki.osdev.org/Memory_Map_(x86) for more details):
0x00500 - 0x08fff: usable memory
0x09000 0x14fff: kernel code and global data
0x15000 - 0x7ffff: usable memory
0x80000 - 0xfffff: BDA and upper memory
0x100000 - 0x8000000 (1 MiB - 128 MiB): usable memory
*/
.intel_syntax noprefix
.global _start
.asciz "kernel start\n"
_start:
// zero out PD at 0x1000
xor eax, eax
mov ecx, 1024
rep stosd
// Enable 4 MiB pages
mov eax, cr4
or eax, 0x10 // Set the PSE bit (bit 4)
mov cr4, eax
// Identity map low 4 MiB
mov dword ptr [0x1000], 0 | PTE_P | PTE_W | PTE_PS
// KERNBASE = 0x8000_0000
// Same mapping for the first 4 MiB after KERNBASE
mov dword ptr [0x1000 + ((KERNBASE >> 22) * 4)], 0 | PTE_P | PTE_W | PTE_PS
// Load physical address of PD into CR3
mov edi, 0x1000
mov cr3, edi
// Enable paging
mov eax, cr0
or eax, 1 << 31 // Set the PG bit
mov cr0, eax
// jump to the high half
add esp, KERNBASE
lea eax, kmain
jmp eax

70
kernel/mem.c Normal file
View File

@ -0,0 +1,70 @@
// Physical memory allocator, intended to allocate
// memory for user processes, kernel stacks, page table pages,
// and pipe buffers. Allocates 4096-byte pages.
#include <stdint.h>
#include "mem.h"
#include "console.h"
#include "cpu/memlayout.h"
#include "cpu/x86.h"
struct run {
struct run *next;
};
struct {
struct run *freelist;
} kmem;
void
freerange(void *vstart, void *vend)
{
char *p;
p = (char*)PGROUNDUP((uintptr_t)vstart);
for(; p + PGSIZE <= (char*)vend; p += PGSIZE)
kfree(p);
}
void*
memset(void *dst, unsigned c, uint64_t n)
{
if ((uintptr_t)dst%4 == 0 && n%4 == 0){
c &= 0xFF;
stosl(dst, (c<<24)|(c<<16)|(c<<8)|c, n/4);
} else
stosb(dst, c, n);
return dst;
}
// Free the page of physical memory pointed at by v,
// which normally should have been returned by a
// call to kalloc(). (The exception is when
// initializing the allocator; see kinit above.)
void
kfree(void *v)
{
struct run *r;
if((uintptr_t)v % PGSIZE || V2P(v) >= PHYSTOP)
panic("kfree");
// Fill with junk to catch dangling refs.
memset(v, 1, PGSIZE);
r = v;
r->next = kmem.freelist;
kmem.freelist = r;
}
// Allocate one 4096-byte page of physical memory.
// Returns a pointer that the kernel can use.
// Returns 0 if the memory cannot be allocated.
void* kalloc(void)
{
struct run *r;
r = kmem.freelist;
if(r)
kmem.freelist = r->next;
return (char*)r;
}

18
kernel/mem.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <stdint.h>
typedef uintptr_t pde_t;
typedef uintptr_t pte_t;
void* memset(void *dst, unsigned c, uint64_t n);
void freerange(void *vstart, void *vend);
void* kalloc(void);
void kfree(void*);
pde_t *setupkvm();
void kvmalloc();
void switchkvm();
int allocuvm(pde_t *pgdir, uintptr_t base, uintptr_t top);
void freevm(pde_t *pgdir);

112
kernel/vm.c Normal file
View File

@ -0,0 +1,112 @@
#include "mem.h"
#include "cpu/memlayout.h"
#include "cpu/x86.h"
#include "console.h"
pde_t *kvm;
pde_t *setupkvm(void) {
pde_t *kvm = kalloc();
memset(kvm, 0, PGSIZE);
// Map physical memory to KERNBASE..KERNBASE+PHYSTOP
for (uintptr_t pa = 0; pa < PHYSTOP; pa += 4 << 20) {
uintptr_t va = KERNBASE + pa;
kvm[PDX(va)] = pa | PTE_P | PTE_W | PTE_PS;
}
return kvm;
}
void kvmalloc() {
kvm = setupkvm();
switchkvm();
}
void switchkvm(void)
{
lcr3(V2P(kvm)); // switch to the kernel page table
}
// Return the address of the PTE in page table pgdir
// that corresponds to virtual address va. If alloc!=0,
// create any required page table pages.
static pte_t *
walkpgdir(pde_t *pgdir, const void *va, int alloc)
{
pde_t *pde;
pte_t *pgtab;
pde = &pgdir[PDX(va)];
if (*pde & PTE_P) {
pgtab = (pte_t *)P2V(PTE_ADDR(*pde));
} else {
if (!alloc || (pgtab = (pte_t *)kalloc()) == 0)
return 0;
// Make sure all those PTE_P bits are zero.
memset(pgtab, 0, PGSIZE);
// The permissions here are overly generous, but they can
// be further restricted by the permissions in the page table
// entries, if necessary.
*pde = V2P(pgtab) | PTE_P | PTE_W | PTE_U;
}
return &pgtab[PTX(va)];
}
// Create PTEs for virtual addresses starting at va that refer to
// physical addresses starting at pa. va and size might not
// be page-aligned.
static int
mappages(pde_t *pgdir, void *va, uintptr_t size, uintptr_t pa, int perm)
{
char *a, *last;
pte_t *pte;
a = (char *)PGROUNDDOWN((uintptr_t)va);
last = (char *)PGROUNDDOWN(((uintptr_t)va) + size - 1);
for (;;)
{
if ((pte = walkpgdir(pgdir, a, 1)) == 0)
return -1;
if (*pte & PTE_P)
panic("remap");
*pte = pa | perm | PTE_P;
if (a == last)
break;
a += PGSIZE;
pa += PGSIZE;
}
return 0;
}
int allocuvm(pde_t *pgdir, uintptr_t base, uintptr_t top) {
for (uintptr_t a = PGROUNDUP(base); a < top; a += PGSIZE) {
char *pa = kalloc();
if (pa == 0) {
return -1;
}
memset(pa, 0, PGSIZE);
if (mappages(pgdir, (void*)a, PGSIZE, V2P(pa), PTE_W | PTE_U) < 0) {
kfree(pa);
return -1;
}
}
return 0;
}
static void freept(pte_t *pt) {
for (int i = 0; i < NPTENTRIES; i++) {
if (pt[i] & PTE_P) {
kfree((char*)P2V(PTE_ADDR(pt[i])));
}
}
kfree((char*)pt);
}
void freevm(pde_t *pgdir) {
for (int i = 0; i < NPDENTRIES / 2; i++) {
if (pgdir[i] & PTE_P) {
freept((pte_t*)P2V(PTE_ADDR(pgdir[i])));
}
}
kfree(pgdir);
}

View File

@ -1,12 +0,0 @@
#include "mem.h"
static void* freeptr;
void* kmalloc(size_t size) {
if (!freeptr) {
freeptr = (void*)(1<<20);
}
void* result = freeptr;
freeptr += size;
return result;
}

View File

@ -1,5 +0,0 @@
#pragma once
typedef unsigned size_t;
void* kmalloc(size_t size);

View File

@ -1,5 +1,20 @@
#include "string.h"
void kmemmove(char* dst, char* src, size_t size) {
if (dst == src) return;
if (dst > src && dst < src + size) { // s d
// copy right-to-left
for (; size != 0; size--) {
dst[size - 1] = src[size - 1];
}
} else {
// copy left-to-right
for (size_t i = 0; i < size; ++i) {
dst[i] = src[i];
}
}
}
int strncmp(const char* s1, const char* s2, size_t size) {
while (size && *s1 && *s2 && *s1 == *s2) {
size--;
@ -11,10 +26,3 @@ int strncmp(const char* s1, const char* s2, size_t size) {
}
return (unsigned char)(*s1) - (unsigned char)(*s2);
}
void memset(void* b, char c, size_t len) {
char* p = b;
for (size_t i = 0; i < len; ++i) {
p[i] = c;
}
}

View File

@ -4,4 +4,3 @@ typedef unsigned size_t;
void kmemmove(char* dst, char* src, size_t size);
int strncmp(const char* s1, const char* s2, size_t size);
void memset(void* b, char c, size_t len);

35
proc.c
View File

@ -3,7 +3,8 @@
#include "fs/fs.h"
#include "cpu/gdt.h"
#include "cpu/isr.h"
#include "lib/mem.h"
#include "cpu/memlayout.h"
#include "kernel/mem.h"
#include "lib/string.h"
#include "console.h"
@ -23,31 +24,40 @@ struct kstack {
struct task {
struct taskstate tss;
struct kstack stack;
pde_t *pgdir;
};
struct vm {
void *kernel_thread;
void *user_thread;
struct task *user_task;
} *vm;
} vm;
void trapret();
void swtch(void** oldstack, void* newstack);
void run_elf(const char* name) {
if (!vm) {
vm = kmalloc(sizeof(struct vm));
vm->user_task = kmalloc(sizeof(struct task));
switchuvm(&vm->user_task->tss, vm->user_task->stack.bottom);
struct stat statbuf;
if (stat(name, &statbuf) != 0) {
printk(name);
printk(": file not found\n");
return;
}
if (read_file(name, (void*)USER_BASE, 100 << 20) <= 0) {
if (!vm.user_task) {
vm.user_task = kalloc();
}
vm.user_task->pgdir = setupkvm();
allocuvm(vm.user_task->pgdir, USER_BASE, USER_BASE + statbuf.size);
allocuvm(vm.user_task->pgdir, USER_STACK_BASE - 2 * PGSIZE, USER_STACK_BASE);
switchuvm(&vm.user_task->tss, vm.user_task->stack.bottom, vm.user_task->pgdir);
if (read_file(&statbuf, (void*)USER_BASE, 100 << 20) <= 0) {
printk(name);
printk(": file not found\n");
return;
}
Elf32_Ehdr *hdr = (void*)USER_BASE;
struct kstack *u = &vm->user_task->stack;
struct kstack *u = &vm.user_task->stack;
memset(u, 0, sizeof(*u));
u->context.eip = (uint32_t)trapret;
@ -63,13 +73,16 @@ void run_elf(const char* name) {
tf->useresp = USER_STACK_BASE;
// initialization done, now switch to the process
swtch(&vm->kernel_thread, &u->context);
swtch(&vm.kernel_thread, &u->context);
// process has finished
}
_Noreturn void killproc() {
void* task_stack;
swtch(&task_stack, vm->kernel_thread);
switchkvm();
freevm(vm.user_task->pgdir);
sti();
swtch(&task_stack, vm.kernel_thread);
__builtin_unreachable();
}

View File

@ -4,6 +4,8 @@ enum {
T_SYSCALL = 0x84,
SYS_exit = 0,
SYS_greet = 1,
SYS_putc = 2,
SYS_puts = 3,
};
int syscall(int call, int arg);

16
user/shout.c Normal file
View File

@ -0,0 +1,16 @@
#include "../syscall.h"
#include <stdint.h>
void userspace_puts(const char* s) {
char c;
while ((c = *s++)) {
syscall(SYS_putc, c);
}
}
int main() {
const char* spell = "cra cra trif traf not sgnieflet\n";
userspace_puts(spell);
syscall(SYS_puts, (uint32_t)spell);
return 0;
}