Merge branch 'paging' into public
This commit is contained in:
commit
2989d96266
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,7 +1,6 @@
|
||||
*.o
|
||||
*.bin
|
||||
*.img
|
||||
*.bin
|
||||
*.elf
|
||||
*.dSYM
|
||||
tools/mkfs
|
||||
ejudge.sh
|
||||
|
||||
15
Makefile
15
Makefile
@ -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 $@
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
15
cpu/gdt.c
15
cpu/gdt.c
@ -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));
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
29
cpu/idt.c
29
cpu/idt.c
@ -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;
|
||||
|
||||
16
cpu/isr.h
16
cpu/isr.h
@ -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
41
cpu/memlayout.h
Normal 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
19
cpu/x86.h
Normal 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));
|
||||
}
|
||||
106
drivers/ata.c
106
drivers/ata.c
@ -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))
|
||||
;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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
76
drivers/pit.c
Normal 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
8
drivers/pit.h
Normal 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);
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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
35
fs/fs.c
@ -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;
|
||||
}
|
||||
|
||||
5
fs/fs.h
5
fs/fs.h
@ -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);
|
||||
|
||||
17
kernel.c
17
kernel.c
@ -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
44
kernel/kstart.S
Normal 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
70
kernel/mem.c
Normal 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
18
kernel/mem.h
Normal 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
112
kernel/vm.c
Normal 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);
|
||||
}
|
||||
12
lib/mem.c
12
lib/mem.c
@ -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;
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
typedef unsigned size_t;
|
||||
|
||||
void* kmalloc(size_t size);
|
||||
22
lib/string.c
22
lib/string.c
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
35
proc.c
@ -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();
|
||||
}
|
||||
|
||||
@ -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
16
user/shout.c
Normal 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;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user