(broken) user mode support

This commit is contained in:
Sami Samhuri 2012-01-10 08:47:26 -08:00
parent fb0358197b
commit f0b2ebefea
45 changed files with 3387 additions and 0 deletions

220
user_mode/bochs.out Normal file
View file

@ -0,0 +1,220 @@
00000000000i[ ] Bochs x86 Emulator 2.4.2
00000000000i[ ] Build from CVS snapshot on November 12, 2009
00000000000i[ ] System configuration
00000000000i[ ] processors: 1 (cores=1, HT threads=1)
00000000000i[ ] A20 line support: yes
00000000000i[ ] CPU configuration
00000000000i[ ] level: 6
00000000000i[ ] SMP support: no
00000000000i[ ] APIC support: yes
00000000000i[ ] FPU support: yes
00000000000i[ ] MMX support: yes
00000000000i[ ] SSE support: 4E
00000000000i[ ] CLFLUSH support: yes
00000000000i[ ] 3dnow! support: yes
00000000000i[ ] 1G paging support: no
00000000000i[ ] x86-64 support: yes
00000000000i[ ] SEP support: yes
00000000000i[ ] MWAIT support: no
00000000000i[ ] XSAVE support: yes
00000000000i[ ] AES support: no
00000000000i[ ] VMX support: no
00000000000i[ ] Optimization configuration
00000000000i[ ] RepeatSpeedups support: yes
00000000000i[ ] Trace cache support: yes
00000000000i[ ] Fast function calls: yes
00000000000i[ ] Devices configuration
00000000000i[ ] ACPI support: yes
00000000000i[ ] NE2000 support: yes
00000000000i[ ] PCI support: yes, enabled=yes
00000000000i[ ] SB16 support: yes
00000000000i[ ] USB support: yes
00000000000i[ ] VGA extension support: vbe
00000000000i[MEM0 ] allocated memory at 0xb42f9008. after alignment, vector=0xb42fa000
00000000000i[MEM0 ] 32.00MB
00000000000i[MEM0 ] mem block size = 0x00100000, blocks=32
00000000000i[MEM0 ] rom at 0xfffe0000/131072 ('/usr/share/bochs/BIOS-bochs-latest')
00000000000i[MEM0 ] rom at 0xc0000/38400 ('/usr/share/bochs/VGABIOS-lgpl-latest')
00000000000i[VTIME] using 'realtime pit' synchronization method
00000000000i[ ] lt_dlhandle is 0x97998b8
00000000000i[PLGIN] loaded plugin libbx_cmos.so
00000000000i[ ] lt_dlhandle is 0x9799de8
00000000000i[PLGIN] loaded plugin libbx_dma.so
00000000000i[ ] lt_dlhandle is 0x979a350
00000000000i[PLGIN] loaded plugin libbx_pic.so
00000000000i[ ] lt_dlhandle is 0x979a900
00000000000i[PLGIN] loaded plugin libbx_pit.so
00000000000i[ ] lt_dlhandle is 0x979ae10
00000000000i[PLGIN] loaded plugin libbx_vga.so
00000000000i[ ] lt_dlhandle is 0x979b3f8
00000000000i[PLGIN] loaded plugin libbx_floppy.so
00000000000i[ ] lt_dlhandle is 0x979bc00
00000000000i[PLGIN] loaded plugin libbx_pci.so
00000000000i[ ] lt_dlhandle is 0x979c330
00000000000i[PLGIN] loaded plugin libbx_pci2isa.so
00000000000i[ ] lt_dlhandle is 0x979c838
00000000000i[PLGIN] loaded plugin libbx_unmapped.so
00000000000i[ ] lt_dlhandle is 0x979ce50
00000000000i[PLGIN] loaded plugin libbx_biosdev.so
00000000000i[ ] lt_dlhandle is 0x979d320
00000000000i[PLGIN] loaded plugin libbx_speaker.so
00000000000i[ ] lt_dlhandle is 0x979d930
00000000000i[PLGIN] loaded plugin libbx_extfpuirq.so
00000000000i[ ] lt_dlhandle is 0x979de08
00000000000i[PLGIN] loaded plugin libbx_gameport.so
00000000000i[ ] lt_dlhandle is 0x979e2e8
00000000000i[PLGIN] loaded plugin libbx_pci_ide.so
00000000000i[ ] lt_dlhandle is 0x979e7f8
00000000000i[PLGIN] loaded plugin libbx_acpi.so
00000000000i[ ] lt_dlhandle is 0x979ee20
00000000000i[PLGIN] loaded plugin libbx_ioapic.so
00000000000i[ ] lt_dlhandle is 0x979f438
00000000000i[PLGIN] loaded plugin libbx_keyboard.so
00000000000i[ ] lt_dlhandle is 0x979fa30
00000000000i[PLGIN] loaded plugin libbx_harddrv.so
00000000000i[ ] lt_dlhandle is 0x97b17f0
00000000000i[PLGIN] loaded plugin libbx_serial.so
00000000000i[ ] lt_dlhandle is 0x97b2320
00000000000i[PLGIN] loaded plugin libbx_parallel.so
00000000000i[CMOS ] Using local time for initial clock
00000000000i[CMOS ] Setting initial clock to: Sat Jan 7 21:09:59 2012 (time0=1325999399)
00000000000i[DMA ] channel 4 used by cascade
00000000000i[DMA ] channel 2 used by Floppy Drive
00000000000e[FDD ] cannot determine media geometry, trying to use defaults
00000000000i[FDD ] fd0: '/dev/loop0' ro=0, h=2,t=80,spt=18
00000000000i[PCI ] 440FX Host bridge present at device 0, function 0
00000000000i[PCI ] PIIX3 PCI-to-ISA bridge present at device 1, function 0
00000000000i[VGA ] interval=50000
00000000000i[MEM0 ] Register memory access handlers: 0x000a0000 - 0x000bffff
00000000000i[XGUI ] test_alloc_colors: 16 colors available out of 16 colors tried
00000000000i[XGUI ] font 8 wide x 16 high, display depth = 24
00000000000i[MEM0 ] Register memory access handlers: 0xe0000000 - 0xe0ffffff
00000000000i[VGA ] VBE Bochs Display Extension Enabled
00000000000i[PLGIN] init_dev of 'unmapped' plugin device by virtual method
00000000000i[PLGIN] init_dev of 'biosdev' plugin device by virtual method
00000000000i[PLGIN] init_dev of 'speaker' plugin device by virtual method
00000000000i[SPEAK] Open /dev/console successfully
00000000000i[PLGIN] init_dev of 'extfpuirq' plugin device by virtual method
00000000000i[PLGIN] init_dev of 'gameport' plugin device by virtual method
00000000000i[PLGIN] init_dev of 'pci_ide' plugin device by virtual method
00000000000i[PCI ] PIIX3 PCI IDE controller present at device 1, function 1
00000000000i[PLGIN] init_dev of 'acpi' plugin device by virtual method
00000000000i[PCI ] ACPI Controller present at device 1, function 3
00000000000i[PLGIN] init_dev of 'ioapic' plugin device by virtual method
00000000000i[IOAP ] initializing I/O APIC
00000000000i[MEM0 ] Register memory access handlers: 0xfec00000 - 0xfec00fff
00000000000i[PLGIN] init_dev of 'keyboard' plugin device by virtual method
00000000000i[KBD ] will paste characters every 1000 keyboard ticks
00000000000i[PLGIN] init_dev of 'harddrv' plugin device by virtual method
00000000000i[HD ] Using boot sequence floppy, none, none
00000000000i[HD ] Floppy boot signature check is enabled
00000000000i[PLGIN] init_dev of 'serial' plugin device by virtual method
00000000000i[SER ] com1 at 0x03f8 irq 4
00000000000i[PLGIN] init_dev of 'parallel' plugin device by virtual method
00000000000i[PAR ] parallel port 1 at 0x0378 irq 7
00000000000i[PLGIN] register state of 'unmapped' plugin device by virtual method
00000000000i[PLGIN] register state of 'biosdev' plugin device by virtual method
00000000000i[PLGIN] register state of 'speaker' plugin device by virtual method
00000000000i[PLGIN] register state of 'extfpuirq' plugin device by virtual method
00000000000i[PLGIN] register state of 'gameport' plugin device by virtual method
00000000000i[PLGIN] register state of 'pci_ide' plugin device by virtual method
00000000000i[PLGIN] register state of 'acpi' plugin device by virtual method
00000000000i[PLGIN] register state of 'ioapic' plugin device by virtual method
00000000000i[PLGIN] register state of 'keyboard' plugin device by virtual method
00000000000i[PLGIN] register state of 'harddrv' plugin device by virtual method
00000000000i[PLGIN] register state of 'serial' plugin device by virtual method
00000000000i[PLGIN] register state of 'parallel' plugin device by virtual method
00000000000i[SYS ] bx_pc_system_c::Reset(HARDWARE) called
00000000000i[CPU0 ] cpu hardware reset
00000000000i[APIC0] allocate APIC id=0 (MMIO enabled) to 0xfee00000
00000000000i[PLGIN] reset of 'unmapped' plugin device by virtual method
00000000000i[PLGIN] reset of 'biosdev' plugin device by virtual method
00000000000i[PLGIN] reset of 'speaker' plugin device by virtual method
00000000000i[PLGIN] reset of 'extfpuirq' plugin device by virtual method
00000000000i[PLGIN] reset of 'gameport' plugin device by virtual method
00000000000i[PLGIN] reset of 'pci_ide' plugin device by virtual method
00000000000i[PLGIN] reset of 'acpi' plugin device by virtual method
00000000000i[PLGIN] reset of 'ioapic' plugin device by virtual method
00000000000i[PLGIN] reset of 'keyboard' plugin device by virtual method
00000000000i[PLGIN] reset of 'harddrv' plugin device by virtual method
00000000000i[PLGIN] reset of 'serial' plugin device by virtual method
00000000000i[PLGIN] reset of 'parallel' plugin device by virtual method
00000000000i[XGUI ] [x] Mouse off
00000003305i[BIOS ] $Revision: 1.235 $ $Date: 2009/09/28 16:36:02 $
00000316426i[KBD ] reset-disable command received
00000432848i[VBIOS] VGABios $Id$
00000432919i[VGA ] VBE known Display Interface b0c0
00000432951i[VGA ] VBE known Display Interface b0c4
00000433620i[VBIOS] VBE Bios $Id$
00000475000i[XGUI ] charmap update. Font Height is 16
00000525000i[XGUI ] charmap update. Font Height is 16
00000744973i[BIOS ] Starting rombios32
00000745430i[BIOS ] Shutdown flag 0
00000746053i[BIOS ] ram_size=0x02000000
00000746502i[BIOS ] ram_end=32MB
00000805906i[BIOS ] Found 1 cpu(s)
00000821921i[BIOS ] bios_table_addr: 0x000fba98 end=0x000fcc00
00000822029i[PCI ] 440FX PMC write to PAM register 59 (TLB Flush)
00001149723i[PCI ] 440FX PMC write to PAM register 59 (TLB Flush)
00001477653i[P2I ] PCI IRQ routing: PIRQA# set to 0x0b
00001477674i[P2I ] PCI IRQ routing: PIRQB# set to 0x09
00001477695i[P2I ] PCI IRQ routing: PIRQC# set to 0x0b
00001477716i[P2I ] PCI IRQ routing: PIRQD# set to 0x09
00001477726i[P2I ] write: ELCR2 = 0x0a
00001478536i[BIOS ] PIIX3/PIIX4 init: elcr=00 0a
00001486300i[BIOS ] PCI: bus=0 devfn=0x00: vendor_id=0x8086 device_id=0x1237 class=0x0600
00001488673i[BIOS ] PCI: bus=0 devfn=0x08: vendor_id=0x8086 device_id=0x7000 class=0x0601
00001490885i[BIOS ] PCI: bus=0 devfn=0x09: vendor_id=0x8086 device_id=0x7010 class=0x0101
00001491120i[PIDE ] new BM-DMA address: 0xc000
00001491775i[BIOS ] region 4: 0x0000c000
00001493896i[BIOS ] PCI: bus=0 devfn=0x0b: vendor_id=0x8086 device_id=0x7113 class=0x0680
00001494139i[ACPI ] new irq line = 11
00001494151i[ACPI ] new irq line = 9
00001494182i[ACPI ] new PM base address: 0xb000
00001494196i[ACPI ] new SM base address: 0xb100
00001494224i[PCI ] setting SMRAM control register to 0x4a
00001658315i[CPU0 ] Enter to System Management Mode
00001658325i[CPU0 ] RSM: Resuming from System Management Mode
00001822343i[PCI ] setting SMRAM control register to 0x0a
00001831140i[BIOS ] MP table addr=0x000fbb70 MPC table addr=0x000fbaa0 size=0xd0
00001832890i[BIOS ] SMBIOS table addr=0x000fbb80
00001835925i[BIOS ] Firmware waking vector 0x1ff00cc
00001876821i[BIOS ] ACPI tables: RSDP addr=0x000fbc90 ACPI DATA addr=0x01ff0000 size=0x1f18
00001876858i[PCI ] 440FX PMC write to PAM register 59 (TLB Flush)
00001877610i[BIOS ] bios_table_cur_addr: 0x000fbcb4
00081162072i[BIOS ] Booting from 0000:7c00
00082779758i[BIOS ] int13_harddisk: function 41, unmapped device for ELDL=80
00082784535i[BIOS ] int13_harddisk: function 08, unmapped device for ELDL=80
00082789188i[BIOS ] *** int 15h function AX=00c0, BX=0000 not yet supported!
00202100000i[ ] cpu loop quit, shutting down simulator
00202100000i[CPU0 ] CPU is in protected mode (active)
00202100000i[CPU0 ] CS.d_b = 32 bit
00202100000i[CPU0 ] SS.d_b = 32 bit
00202100000i[CPU0 ] EFER = 0x00000000
00202100000i[CPU0 ] | RAX=0000000000000000 RBX=0000000000000200
00202100000i[CPU0 ] | RCX=00000000000b8000 RDX=00000000000003d5
00202100000i[CPU0 ] | RSP=00000000dffffd3c RBP=00000000dffffd54
00202100000i[CPU0 ] | RSI=0000000000053c9d RDI=0000000000053c9e
00202100000i[CPU0 ] | R8=0000000000000000 R9=0000000000000000
00202100000i[CPU0 ] | R10=0000000000000000 R11=0000000000000000
00202100000i[CPU0 ] | R12=0000000000000000 R13=0000000000000000
00202100000i[CPU0 ] | R14=0000000000000000 R15=0000000000000000
00202100000i[CPU0 ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf ZF af PF cf
00202100000i[CPU0 ] | SEG selector base limit G D
00202100000i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
00202100000i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 ffffffff 1 1
00202100000i[CPU0 ] | DS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00202100000i[CPU0 ] | SS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00202100000i[CPU0 ] | ES:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00202100000i[CPU0 ] | FS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00202100000i[CPU0 ] | GS:0010( 0002| 0| 0) 00000000 ffffffff 1 1
00202100000i[CPU0 ] | MSR_FS_BASE:0000000000000000
00202100000i[CPU0 ] | MSR_GS_BASE:0000000000000000
00202100000i[CPU0 ] | RIP=000000000010094e (000000000010094e)
00202100000i[CPU0 ] | CR0=0xe0000011 CR2=0x00000000123890ab
00202100000i[CPU0 ] | CR3=0x00482000 CR4=0x00000000
00202100000i[CPU0 ] 0x000000000010094e>> jmp .+0xfffffffe (0x0010094e) : EBFE
00202100000i[CMOS ] Last time is 1325999803 (Sat Jan 7 21:16:43 2012)
00202100000i[XGUI ] Exit
00202100000i[ ] restoring default signal behavior
00202100000i[CTRL ] quit_sim called with exit code 1

9
user_mode/bochsrc Normal file
View file

@ -0,0 +1,9 @@
megs: 32
romimage: file=/usr/share/bochs/BIOS-bochs-latest
vgaromimage: file=/usr/share/bochs/VGABIOS-lgpl-latest
floppya: 1_44=/dev/loop0, status=inserted
boot: a
log: bochs.out
mouse: enabled=0
clock: sync=realtime
cpu: ips=500000

10
user_mode/bochsrc.txt Normal file
View file

@ -0,0 +1,10 @@
megs: 32
romimage: file=/usr/share/bochs/BIOS-bochs-latest, address=0xf0000
vgaromimage: /usr/share/bochs/VGABIOS-elpin-2.40
floppya: 1_44=/dev/loop0, status=inserted
boot: a
log: bochsout.txt
mouse: enabled=0
clock: sync=realtime
cpu: ips=500000

BIN
user_mode/floppy.img Normal file

Binary file not shown.

11
user_mode/go Executable file
View file

@ -0,0 +1,11 @@
#!/bin/sh
cd src
make
success=$?
cd ..
if [ $success -eq 0 ]; then
sudo ./update_image.sh
sudo ./run_bochs.sh
grep -F 'CPU0' bochs.out
fi

BIN
user_mode/initrd.img Normal file

Binary file not shown.

BIN
user_mode/make_initrd Executable file

Binary file not shown.

58
user_mode/make_initrd.c Normal file
View file

@ -0,0 +1,58 @@
#include <stdio.h>
struct initrd_header
{
unsigned char magic;
char name[64];
unsigned int offset;
unsigned int length;
};
int main(char argc, char **argv)
{
int nheaders = (argc-1)/2;
struct initrd_header headers[64];
printf("size of header: %d\n", sizeof(struct initrd_header));
unsigned int off = sizeof(struct initrd_header) * 64 + sizeof(int);
int i;
for(i = 0; i < nheaders; i++)
{
printf("writing file %s->%s at 0x%x\n", argv[i*2+1], argv[i*2+2], off);
strcpy(headers[i].name, argv[i*2+2]);
headers[i].name[strlen(argv[i*2+2])] = '\0';
headers[i].offset = off;
FILE *stream = fopen(argv[i*2+1], "r");
if(stream == 0)
{
printf("Error: file not found: %s\n", argv[i*2+1]);
return 1;
}
fseek(stream, 0, SEEK_END);
headers[i].length = ftell(stream);
off += headers[i].length;
fclose(stream);
headers[i].magic = 0xBF;
}
FILE *wstream = fopen("./initrd.img", "w");
unsigned char *data = (unsigned char *)malloc(off);
fwrite(&nheaders, sizeof(int), 1, wstream);
fwrite(headers, sizeof(struct initrd_header), 64, wstream);
for(i = 0; i < nheaders; i++)
{
FILE *stream = fopen(argv[i*2+1], "r");
unsigned char *buf = (unsigned char *)malloc(headers[i].length);
fread(buf, 1, headers[i].length, stream);
fwrite(buf, 1, headers[i].length, wstream);
fclose(stream);
free(buf);
}
fclose(wstream);
free(data);
return 0;
}

8
user_mode/run_bochs.sh Executable file
View file

@ -0,0 +1,8 @@
#!/bin/bash
# run_bochs.sh
# mounts the correct loopback device, runs bochs, then unmounts.
sudo /sbin/losetup /dev/loop0 floppy.img
sudo bochs -f bochsrc
sudo /sbin/losetup -d /dev/loop0

22
user_mode/src/Makefile Normal file
View file

@ -0,0 +1,22 @@
# Makefile for JamesM's kernel tutorials.
# The C and C++ rules are already setup by default.
# The only one that needs changing is the assembler
# rule, as we use nasm instead of GNU as.
SOURCES=boot.o main.o monitor.o common.o descriptor_tables.o isr.o interrupt.o gdt.o timer.o \
kheap.o paging.o ordered_array.o fs.o initrd.o task.o process.o syscall.o
CFLAGS=-nostdlib -nostdinc -fno-builtin
LDFLAGS=-Tlink.ld
ASFLAGS=-felf
all: $(SOURCES) link
clean:
-rm *.o kernel
link:
ld $(LDFLAGS) -o kernel $(SOURCES)
.s.o:
yasm $(ASFLAGS) $<

47
user_mode/src/boot.s Normal file
View file

@ -0,0 +1,47 @@
;
; boot.s -- Kernel start location. Also defines multiboot header.
; Based on Bran's kernel development tutorial file start.asm
;
MBOOT_PAGE_ALIGN equ 1<<0 ; Load kernel and modules on a page boundary
MBOOT_MEM_INFO equ 1<<1 ; Provide your kernel with memory info
MBOOT_HEADER_MAGIC equ 0x1BADB002 ; Multiboot Magic value
; NOTE: We do not use MBOOT_AOUT_KLUDGE. It means that GRUB does not
; pass us a symbol table.
MBOOT_HEADER_FLAGS equ MBOOT_PAGE_ALIGN | MBOOT_MEM_INFO
MBOOT_CHECKSUM equ -(MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS)
[BITS 32] ; All instructions should be 32-bit.
[GLOBAL mboot] ; Make 'mboot' accessible from C.
[EXTERN code] ; Start of the '.text' section.
[EXTERN bss] ; Start of the .bss section.
[EXTERN end] ; End of the last loadable section.
mboot:
dd MBOOT_HEADER_MAGIC ; GRUB will search for this value on each
; 4-byte boundary in your kernel file
dd MBOOT_HEADER_FLAGS ; How GRUB should load your file / settings
dd MBOOT_CHECKSUM ; To ensure that the above values are correct
dd mboot ; Location of this descriptor
dd code ; Start of kernel '.text' (code) section.
dd bss ; End of kernel '.data' section.
dd end ; End of kernel.
dd start ; Kernel entry point (initial EIP).
[GLOBAL start] ; Kernel entry point.
[EXTERN main] ; This is the entry point of our C code
start:
; Load multiboot information:
push esp
push ebx
; Execute the kernel:
cli ; Disable interrupts.
call main ; call our main() function.
jmp $ ; Enter an infinite loop, to stop the processor
; executing whatever rubbish is in the memory
; after our kernel!

129
user_mode/src/common.c Normal file
View file

@ -0,0 +1,129 @@
// common.c -- Defines some global functions.
// From JamesM's kernel development tutorials.
#include "common.h"
// Write a byte out to the specified port.
void outb(u16int port, u8int value)
{
asm volatile ("outb %1, %0" : : "dN" (port), "a" (value));
}
u8int inb(u16int port)
{
u8int ret;
asm volatile("inb %1, %0" : "=a" (ret) : "dN" (port));
return ret;
}
u16int inw(u16int port)
{
u16int ret;
asm volatile ("inw %1, %0" : "=a" (ret) : "dN" (port));
return ret;
}
// Copy len bytes from src to dest.
void memcpy(u8int *dest, const u8int *src, u32int len)
{
const u8int *sp = (const u8int *)src;
u8int *dp = (u8int *)dest;
for(; len != 0; len--) *dp++ = *sp++;
}
// Write len copies of val into dest.
void memset(u8int *dest, u8int val, u32int len)
{
u8int *temp = (u8int *)dest;
for ( ; len != 0; len--) *temp++ = val;
}
// Compare two strings. Should return -1 if
// str1 < str2, 0 if they are equal or 1 otherwise.
int strcmp(char *str1, char *str2)
{
int i = 0;
int failed = 0;
while(str1[i] != '\0' && str2[i] != '\0')
{
if(str1[i] != str2[i])
{
failed = 1;
break;
}
i++;
}
// why did the loop exit?
if( (str1[i] == '\0' && str2[i] != '\0') || (str1[i] != '\0' && str2[i] == '\0') )
failed = 1;
return failed;
}
// Copy the NULL-terminated string src into dest, and
// return dest.
char *strcpy(char *dest, const char *src)
{
do
{
*dest++ = *src++;
}
while (*src != 0);
}
// Concatenate the NULL-terminated string src onto
// the end of dest, and return dest.
char *strcat(char *dest, const char *src)
{
while (*dest != 0)
{
*dest = *dest++;
}
do
{
*dest++ = *src++;
}
while (*src != 0);
return dest;
}
int strlen(char *src)
{
int i = 0;
while (*src++)
i++;
return i;
}
extern void panic(const char *message, const char *file, u32int line)
{
// We encountered a massive problem and have to stop.
asm volatile("cli"); // Disable interrupts.
monitor_write("PANIC(");
monitor_write(message);
monitor_write(") at ");
monitor_write(file);
monitor_write(":");
monitor_write_dec(line);
monitor_write("\n");
// Halt by going into an infinite loop.
for(;;);
}
extern void panic_assert(const char *file, u32int line, const char *desc)
{
// An assertion failed, and we have to panic.
asm volatile("cli"); // Disable interrupts.
monitor_write("ASSERTION-FAILED(");
monitor_write(desc);
monitor_write(") at ");
monitor_write(file);
monitor_write(":");
monitor_write_dec(line);
monitor_write("\n");
// Halt by going into an infinite loop.
for(;;);
}

26
user_mode/src/common.h Normal file
View file

@ -0,0 +1,26 @@
// common.h -- Defines typedefs and some global functions.
// From JamesM's kernel development tutorials.
#ifndef COMMON_H
#define COMMON_H
// Some nice typedefs, to standardise sizes across platforms.
// These typedefs are written for 32-bit X86.
typedef unsigned int u32int;
typedef int s32int;
typedef unsigned short u16int;
typedef short s16int;
typedef unsigned char u8int;
typedef char s8int;
void outb(u16int port, u8int value);
u8int inb(u16int port);
u16int inw(u16int port);
#define PANIC(msg) panic(msg, __FILE__, __LINE__);
#define ASSERT(b) ((b) ? (void)0 : panic_assert(__FILE__, __LINE__, #b))
extern void panic(const char *message, const char *file, u32int line);
extern void panic_assert(const char *file, u32int line, const char *desc);
#endif // COMMON_H

View file

@ -0,0 +1,187 @@
//
// descriptor_tables.c - Initialises the GDT and IDT, and defines the
// default ISR and IRQ handler.
// Based on code from Bran's kernel development tutorials.
// Rewritten for JamesM's kernel development tutorials.
//
#include "common.h"
#include "descriptor_tables.h"
#include "isr.h"
// Lets us access our ASM functions from our C code.
extern void gdt_flush(u32int);
extern void idt_flush(u32int);
extern void tss_flush();
// Internal function prototypes.
static void init_gdt();
static void init_idt();
static void gdt_set_gate(s32int,u32int,u32int,u8int,u8int);
static void idt_set_gate(u8int,u32int,u16int,u8int);
static void write_tss(s32int,u16int,u32int);
gdt_entry_t gdt_entries[6];
gdt_ptr_t gdt_ptr;
idt_entry_t idt_entries[256];
idt_ptr_t idt_ptr;
tss_entry_t tss_entry;
// Extern the ISR handler array so we can nullify them on startup.
extern isr_t interrupt_handlers[];
// Initialisation routine - zeroes all the interrupt service routines,
// initialises the GDT and IDT.
void init_descriptor_tables()
{
// Initialise the global descriptor table.
init_gdt();
// Initialise the interrupt descriptor table.
init_idt();
// Nullify all the interrupt handlers.
memset(&interrupt_handlers, 0, sizeof(isr_t)*256);
}
static void init_gdt()
{
gdt_ptr.limit = (sizeof(gdt_entry_t) * 6) - 1;
gdt_ptr.base = (u32int)&gdt_entries;
gdt_set_gate(0, 0, 0, 0, 0); // Null segment
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code segment
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment
gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // User mode code segment
gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // User mode data segment
write_tss(5, 0x10, 0x0);
gdt_flush((u32int)&gdt_ptr);
tss_flush();
}
// Set the value of one GDT entry.
static void gdt_set_gate(s32int num, u32int base, u32int limit, u8int access, u8int gran)
{
gdt_entries[num].base_low = (base & 0xFFFF);
gdt_entries[num].base_middle = (base >> 16) & 0xFF;
gdt_entries[num].base_high = (base >> 24) & 0xFF;
gdt_entries[num].limit_low = (limit & 0xFFFF);
gdt_entries[num].granularity = (limit >> 16) & 0x0F;
gdt_entries[num].granularity |= gran & 0xF0;
gdt_entries[num].access = access;
}
// Initialise our task state segment structure.
static void write_tss(s32int num, u16int ss0, u32int esp0)
{
// Firstly, let's compute the base and limit of our entry into the GDT.
u32int base = (u32int) &tss_entry;
u32int limit = base + sizeof(tss_entry);
// Now, add our TSS descriptor's address to the GDT.
gdt_set_gate(num, base, limit, 0xE9, 0x00);
// Ensure the descriptor is initially zero.
memset(&tss_entry, 0, sizeof(tss_entry));
tss_entry.ss0 = ss0; // Set the kernel stack segment.
tss_entry.esp0 = esp0; // Set the kernel stack pointer.
// Here we set the cs, ss, ds, es, fs and gs entries in the TSS. These specify what
// segments should be loaded when the processor switches to kernel mode. Therefore
// they are just our normal kernel code/data segments - 0x08 and 0x10 respectively,
// but with the last two bits set, making 0x0b and 0x13. The setting of these bits
// sets the RPL (requested privilege level) to 3, meaning that this TSS can be used
// to switch to kernel mode from ring 3.
tss_entry.cs = 0x0b;
tss_entry.ss = tss_entry.ds = tss_entry.es = tss_entry.fs = tss_entry.gs = 0x13;
}
void set_kernel_stack(u32int stack)
{
tss_entry.esp0 = stack;
}
static void init_idt()
{
idt_ptr.limit = sizeof(idt_entry_t) * 256 -1;
idt_ptr.base = (u32int)&idt_entries;
memset(&idt_entries, 0, sizeof(idt_entry_t)*256);
// Remap the irq table.
outb(0x20, 0x11);
outb(0xA0, 0x11);
outb(0x21, 0x20);
outb(0xA1, 0x28);
outb(0x21, 0x04);
outb(0xA1, 0x02);
outb(0x21, 0x01);
outb(0xA1, 0x01);
outb(0x21, 0x0);
outb(0xA1, 0x0);
idt_set_gate( 0, (u32int)isr0 , 0x08, 0x8E);
idt_set_gate( 1, (u32int)isr1 , 0x08, 0x8E);
idt_set_gate( 2, (u32int)isr2 , 0x08, 0x8E);
idt_set_gate( 3, (u32int)isr3 , 0x08, 0x8E);
idt_set_gate( 4, (u32int)isr4 , 0x08, 0x8E);
idt_set_gate( 5, (u32int)isr5 , 0x08, 0x8E);
idt_set_gate( 6, (u32int)isr6 , 0x08, 0x8E);
idt_set_gate( 7, (u32int)isr7 , 0x08, 0x8E);
idt_set_gate( 8, (u32int)isr8 , 0x08, 0x8E);
idt_set_gate( 9, (u32int)isr9 , 0x08, 0x8E);
idt_set_gate(10, (u32int)isr10, 0x08, 0x8E);
idt_set_gate(11, (u32int)isr11, 0x08, 0x8E);
idt_set_gate(12, (u32int)isr12, 0x08, 0x8E);
idt_set_gate(13, (u32int)isr13, 0x08, 0x8E);
idt_set_gate(14, (u32int)isr14, 0x08, 0x8E);
idt_set_gate(15, (u32int)isr15, 0x08, 0x8E);
idt_set_gate(16, (u32int)isr16, 0x08, 0x8E);
idt_set_gate(17, (u32int)isr17, 0x08, 0x8E);
idt_set_gate(18, (u32int)isr18, 0x08, 0x8E);
idt_set_gate(19, (u32int)isr19, 0x08, 0x8E);
idt_set_gate(20, (u32int)isr20, 0x08, 0x8E);
idt_set_gate(21, (u32int)isr21, 0x08, 0x8E);
idt_set_gate(22, (u32int)isr22, 0x08, 0x8E);
idt_set_gate(23, (u32int)isr23, 0x08, 0x8E);
idt_set_gate(24, (u32int)isr24, 0x08, 0x8E);
idt_set_gate(25, (u32int)isr25, 0x08, 0x8E);
idt_set_gate(26, (u32int)isr26, 0x08, 0x8E);
idt_set_gate(27, (u32int)isr27, 0x08, 0x8E);
idt_set_gate(28, (u32int)isr28, 0x08, 0x8E);
idt_set_gate(29, (u32int)isr29, 0x08, 0x8E);
idt_set_gate(30, (u32int)isr30, 0x08, 0x8E);
idt_set_gate(31, (u32int)isr31, 0x08, 0x8E);
idt_set_gate(32, (u32int)irq0, 0x08, 0x8E);
idt_set_gate(33, (u32int)irq1, 0x08, 0x8E);
idt_set_gate(34, (u32int)irq2, 0x08, 0x8E);
idt_set_gate(35, (u32int)irq3, 0x08, 0x8E);
idt_set_gate(36, (u32int)irq4, 0x08, 0x8E);
idt_set_gate(37, (u32int)irq5, 0x08, 0x8E);
idt_set_gate(38, (u32int)irq6, 0x08, 0x8E);
idt_set_gate(39, (u32int)irq7, 0x08, 0x8E);
idt_set_gate(40, (u32int)irq8, 0x08, 0x8E);
idt_set_gate(41, (u32int)irq9, 0x08, 0x8E);
idt_set_gate(42, (u32int)irq10, 0x08, 0x8E);
idt_set_gate(43, (u32int)irq11, 0x08, 0x8E);
idt_set_gate(44, (u32int)irq12, 0x08, 0x8E);
idt_set_gate(45, (u32int)irq13, 0x08, 0x8E);
idt_set_gate(46, (u32int)irq14, 0x08, 0x8E);
idt_set_gate(47, (u32int)irq15, 0x08, 0x8E);
idt_set_gate(128, (u32int)isr128, 0x08, 0x8E);
idt_flush((u32int)&idt_ptr);
}
static void idt_set_gate(u8int num, u32int base, u16int sel, u8int flags)
{
idt_entries[num].base_lo = base & 0xFFFF;
idt_entries[num].base_hi = (base >> 16) & 0xFFFF;
idt_entries[num].sel = sel;
idt_entries[num].always0 = 0;
idt_entries[num].flags = flags | 0x60;
}

View file

@ -0,0 +1,154 @@
//
// descriptor_tables.h - Defines the interface for initialising the GDT and IDT.
// Also defines needed structures.
// Based on code from Bran's kernel development tutorials.
// Rewritten for JamesM's kernel development tutorials.
//
#ifndef DESCRIPTOR_TABLES_H
#define DESCRIPTOR_TABLES_H
#include "common.h"
// Initialisation function is publicly accessible.
void init_descriptor_tables();
// Allows the kernel stack in the TSS to be changed.
void set_kernel_stack(u32int stack);
// This structure contains the value of one GDT entry.
// We use the attribute 'packed' to tell GCC not to change
// any of the alignment in the structure.
struct gdt_entry_struct
{
u16int limit_low; // The lower 16 bits of the limit.
u16int base_low; // The lower 16 bits of the base.
u8int base_middle; // The next 8 bits of the base.
u8int access; // Access flags, determine what ring this segment can be used in.
u8int granularity;
u8int base_high; // The last 8 bits of the base.
} __attribute__((packed));
typedef struct gdt_entry_struct gdt_entry_t;
// This struct describes a GDT pointer. It points to the start of
// our array of GDT entries, and is in the format required by the
// lgdt instruction.
struct gdt_ptr_struct
{
u16int limit; // The upper 16 bits of all selector limits.
u32int base; // The address of the first gdt_entry_t struct.
} __attribute__((packed));
typedef struct gdt_ptr_struct gdt_ptr_t;
// A struct describing an interrupt gate.
struct idt_entry_struct
{
u16int base_lo; // The lower 16 bits of the address to jump to when this interrupt fires.
u16int sel; // Kernel segment selector.
u8int always0; // This must always be zero.
u8int flags; // More flags. See documentation.
u16int base_hi; // The upper 16 bits of the address to jump to.
} __attribute__((packed));
typedef struct idt_entry_struct idt_entry_t;
// A struct describing a pointer to an array of interrupt handlers.
// This is in a format suitable for giving to 'lidt'.
struct idt_ptr_struct
{
u16int limit;
u32int base; // The address of the first element in our idt_entry_t array.
} __attribute__((packed));
typedef struct idt_ptr_struct idt_ptr_t;
// A struct describing a Task State Segment.
struct tss_entry_struct
{
u32int prev_tss; // The previous TSS - if we used hardware task switching this would form a linked list.
u32int esp0; // The stack pointer to load when we change to kernel mode.
u32int ss0; // The stack segment to load when we change to kernel mode.
u32int esp1; // Unused...
u32int ss1;
u32int esp2;
u32int ss2;
u32int cr3;
u32int eip;
u32int eflags;
u32int eax;
u32int ecx;
u32int edx;
u32int ebx;
u32int esp;
u32int ebp;
u32int esi;
u32int edi;
u32int es; // The value to load into ES when we change to kernel mode.
u32int cs; // The value to load into CS when we change to kernel mode.
u32int ss; // The value to load into SS when we change to kernel mode.
u32int ds; // The value to load into DS when we change to kernel mode.
u32int fs; // The value to load into FS when we change to kernel mode.
u32int gs; // The value to load into GS when we change to kernel mode.
u32int ldt; // Unused...
u16int trap;
u16int iomap_base;
} __attribute__((packed));
typedef struct tss_entry_struct tss_entry_t;
// These extern directives let us access the addresses of our ASM ISR handlers.
extern void isr0 ();
extern void isr1 ();
extern void isr2 ();
extern void isr3 ();
extern void isr4 ();
extern void isr5 ();
extern void isr6 ();
extern void isr7 ();
extern void isr8 ();
extern void isr9 ();
extern void isr10();
extern void isr11();
extern void isr12();
extern void isr13();
extern void isr14();
extern void isr15();
extern void isr16();
extern void isr17();
extern void isr18();
extern void isr19();
extern void isr20();
extern void isr21();
extern void isr22();
extern void isr23();
extern void isr24();
extern void isr25();
extern void isr26();
extern void isr27();
extern void isr28();
extern void isr29();
extern void isr30();
extern void isr31();
extern void irq0 ();
extern void irq1 ();
extern void irq2 ();
extern void irq3 ();
extern void irq4 ();
extern void irq5 ();
extern void irq6 ();
extern void irq7 ();
extern void irq8 ();
extern void irq9 ();
extern void irq10();
extern void irq11();
extern void irq12();
extern void irq13();
extern void irq14();
extern void irq15();
extern void isr128();
#endif

58
user_mode/src/fs.c Normal file
View file

@ -0,0 +1,58 @@
// fs.c -- Defines the interface for and structures relating to the virtual file system.
// Written for JamesM's kernel development tutorials.
#include "fs.h"
fs_node_t *fs_root = 0; // The root of the filesystem.
u32int read_fs(fs_node_t *node, u32int offset, u32int size, u8int *buffer)
{
// Has the node got a read callback?
if (node->read != 0)
return node->read(node, offset, size, buffer);
else
return 0;
}
u32int write_fs(fs_node_t *node, u32int offset, u32int size, u8int *buffer)
{
// Has the node got a write callback?
if (node->write != 0)
return node->write(node, offset, size, buffer);
else
return 0;
}
void open_fs(fs_node_t *node, u8int read, u8int write)
{
// Has the node got an open callback?
if (node->open != 0)
return node->open(node);
}
void close_fs(fs_node_t *node)
{
// Has the node got a close callback?
if (node->close != 0)
return node->close(node);
}
struct dirent *readdir_fs(fs_node_t *node, u32int index)
{
// Is the node a directory, and does it have a callback?
if ( (node->flags&0x7) == FS_DIRECTORY &&
node->readdir != 0 )
return node->readdir(node, index);
else
return 0;
}
fs_node_t *finddir_fs(fs_node_t *node, char *name)
{
// Is the node a directory, and does it have a callback?
if ( (node->flags&0x7) == FS_DIRECTORY &&
node->finddir != 0 )
return node->finddir(node, name);
else
return 0;
}

65
user_mode/src/fs.h Normal file
View file

@ -0,0 +1,65 @@
// fs.h -- Defines the interface for and structures relating to the virtual file system.
// Written for JamesM's kernel development tutorials.
#ifndef FS_H
#define FS_H
#include "common.h"
#define FS_FILE 0x01
#define FS_DIRECTORY 0x02
#define FS_CHARDEVICE 0x03
#define FS_BLOCKDEVICE 0x04
#define FS_PIPE 0x05
#define FS_SYMLINK 0x06
#define FS_MOUNTPOINT 0x08 // Is the file an active mountpoint?
struct fs_node;
// These typedefs define the type of callbacks - called when read/write/open/close
// are called.
typedef u32int (*read_type_t)(struct fs_node*,u32int,u32int,u8int*);
typedef u32int (*write_type_t)(struct fs_node*,u32int,u32int,u8int*);
typedef void (*open_type_t)(struct fs_node*);
typedef void (*close_type_t)(struct fs_node*);
typedef struct dirent * (*readdir_type_t)(struct fs_node*,u32int);
typedef struct fs_node * (*finddir_type_t)(struct fs_node*,char *name);
typedef struct fs_node
{
char name[128]; // The filename.
u32int mask; // The permissions mask.
u32int uid; // The owning user.
u32int gid; // The owning group.
u32int flags; // Includes the node type. See #defines above.
u32int inode; // This is device-specific - provides a way for a filesystem to identify files.
u32int length; // Size of the file, in bytes.
u32int impl; // An implementation-defined number.
read_type_t read;
write_type_t write;
open_type_t open;
close_type_t close;
readdir_type_t readdir;
finddir_type_t finddir;
struct fs_node *ptr; // Used by mountpoints and symlinks.
} fs_node_t;
struct dirent
{
char name[128]; // Filename.
u32int ino; // Inode number. Required by POSIX.
};
extern fs_node_t *fs_root; // The root of the filesystem.
// Standard read/write/open/close functions. Note that these are all suffixed with
// _fs to distinguish them from the read/write/open/close which deal with file descriptors
// , not file nodes.
u32int read_fs(fs_node_t *node, u32int offset, u32int size, u8int *buffer);
u32int write_fs(fs_node_t *node, u32int offset, u32int size, u8int *buffer);
void open_fs(fs_node_t *node, u8int read, u8int write);
void close_fs(fs_node_t *node);
struct dirent *readdir_fs(fs_node_t *node, u32int index);
fs_node_t *finddir_fs(fs_node_t *node, char *name);
#endif

37
user_mode/src/gdt.s Normal file
View file

@ -0,0 +1,37 @@
;
; Gdt.s -- contains global descriptor table and interrupt descriptor table
; setup code.
; Based on code from Bran's kernel development tutorials.
; Rewritten for JamesM's kernel development tutorials.
[GLOBAL gdt_flush] ; Allows the C code to call gdt_flush().
gdt_flush:
mov eax, [esp+4] ; Get the pointer to the GDT, passed as a parameter.
lgdt [eax] ; Load the new GDT pointer
mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment
mov ds, ax ; Load all data segment selectors
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp 0x08:.flush ; 0x08 is the offset to our code segment: Far jump!
.flush:
ret
[GLOBAL idt_flush] ; Allows the C code to call idt_flush().
idt_flush:
mov eax, [esp+4] ; Get the pointer to the IDT, passed as a parameter.
lidt [eax] ; Load the IDT pointer.
ret
[GLOBAL tss_flush] ; Allows our C code to call tss_flush().
tss_flush:
mov ax, 0x2B ; Load the index of our TSS structure - The index is
; 0x28, as it is the 5th selector and each is 8 bytes
; long, but we set the bottom two bits (making 0x2B)
; so that it has an RPL of 3, not zero.
ltr ax ; Load 0x2B into the task state register.
ret

117
user_mode/src/initrd.c Normal file
View file

@ -0,0 +1,117 @@
// initrd.c -- Defines the interface for and structures relating to the initial ramdisk.
// Written for JamesM's kernel development tutorials.
#include "initrd.h"
initrd_header_t *initrd_header; // The header.
initrd_file_header_t *file_headers; // The list of file headers.
fs_node_t *initrd_root; // Our root directory node.
fs_node_t *initrd_dev; // We also add a directory node for /dev, so we can mount devfs later on.
fs_node_t *root_nodes; // List of file nodes.
int nroot_nodes; // Number of file nodes.
struct dirent dirent;
static u32int initrd_read(fs_node_t *node, u32int offset, u32int size, u8int *buffer)
{
initrd_file_header_t header = file_headers[node->inode];
if (offset > header.length)
return 0;
if (offset+size > header.length)
size = header.length-offset;
memcpy(buffer, (u8int*) (header.offset+offset), size);
return size;
}
static struct dirent *initrd_readdir(fs_node_t *node, u32int index)
{
if (node == initrd_root && index == 0)
{
strcpy(dirent.name, "dev");
dirent.name[3] = 0;
dirent.ino = 0;
return &dirent;
}
if (index-1 >= nroot_nodes)
return 0;
strcpy(dirent.name, root_nodes[index-1].name);
dirent.name[strlen(root_nodes[index-1].name)] = 0;
dirent.ino = root_nodes[index-1].inode;
return &dirent;
}
static fs_node_t *initrd_finddir(fs_node_t *node, char *name)
{
if (node == initrd_root &&
!strcmp(name, "dev") )
return initrd_dev;
int i;
for (i = 0; i < nroot_nodes; i++)
if (!strcmp(name, root_nodes[i].name))
return &root_nodes[i];
return 0;
}
fs_node_t *initialise_initrd(u32int location)
{
// Initialise the main and file header pointers and populate the root directory.
initrd_header = (initrd_header_t *)location;
file_headers = (initrd_file_header_t *) (location+sizeof(initrd_header_t));
// Initialise the root directory.
initrd_root = (fs_node_t*)kmalloc(sizeof(fs_node_t));
strcpy(initrd_root->name, "initrd");
initrd_root->mask = initrd_root->uid = initrd_root->gid = initrd_root->inode = initrd_root->length = 0;
initrd_root->flags = FS_DIRECTORY;
initrd_root->read = 0;
initrd_root->write = 0;
initrd_root->open = 0;
initrd_root->close = 0;
initrd_root->readdir = &initrd_readdir;
initrd_root->finddir = &initrd_finddir;
initrd_root->ptr = 0;
initrd_root->impl = 0;
// Initialise the /dev directory (required!)
initrd_dev = (fs_node_t*)kmalloc(sizeof(fs_node_t));
strcpy(initrd_dev->name, "dev");
initrd_dev->mask = initrd_dev->uid = initrd_dev->gid = initrd_dev->inode = initrd_dev->length = 0;
initrd_dev->flags = FS_DIRECTORY;
initrd_dev->read = 0;
initrd_dev->write = 0;
initrd_dev->open = 0;
initrd_dev->close = 0;
initrd_dev->readdir = &initrd_readdir;
initrd_dev->finddir = &initrd_finddir;
initrd_dev->ptr = 0;
initrd_dev->impl = 0;
root_nodes = (fs_node_t*)kmalloc(sizeof(fs_node_t) * initrd_header->nfiles);
nroot_nodes = initrd_header->nfiles;
// For every file...
int i;
for (i = 0; i < initrd_header->nfiles; i++)
{
// Edit the file's header - currently it holds the file offset
// relative to the start of the ramdisk. We want it relative to the start
// of memory.
file_headers[i].offset += location;
// Create a new file node.
strcpy(root_nodes[i].name, &file_headers[i].name);
root_nodes[i].mask = root_nodes[i].uid = root_nodes[i].gid = 0;
root_nodes[i].length = file_headers[i].length;
root_nodes[i].inode = i;
root_nodes[i].flags = FS_FILE;
root_nodes[i].read = &initrd_read;
root_nodes[i].write = 0;
root_nodes[i].readdir = 0;
root_nodes[i].finddir = 0;
root_nodes[i].open = 0;
root_nodes[i].close = 0;
root_nodes[i].impl = 0;
}
return initrd_root;
}

27
user_mode/src/initrd.h Normal file
View file

@ -0,0 +1,27 @@
// initrd.h -- Defines the interface for and structures relating to the initial ramdisk.
// Written for JamesM's kernel development tutorials.
#ifndef INITRD_H
#define INITRD_H
#include "common.h"
#include "fs.h"
typedef struct
{
u32int nfiles; // The number of files in the ramdisk.
} initrd_header_t;
typedef struct
{
u8int magic; // Magic number, for error checking.
s8int name[64]; // Filename.
u32int offset; // Offset in the initrd that the file starts.
u32int length; // Length of the file.
} initrd_file_header_t;
// Initialises the initial ramdisk. It gets passed the address of the multiboot module,
// and returns a completed filesystem node.
fs_node_t *initialise_initrd(u32int location);
#endif

151
user_mode/src/interrupt.s Normal file
View file

@ -0,0 +1,151 @@
;
; interrupt.s -- Contains interrupt service routine wrappers.
; Based on Bran's kernel development tutorials.
; Rewritten for JamesM's kernel development tutorials.
; This macro creates a stub for an ISR which does NOT pass it's own
; error code (adds a dummy errcode byte).
%macro ISR_NOERRCODE 1
global isr%1
isr%1:
cli ; Disable interrupts firstly.
push byte 0 ; Push a dummy error code.
push %1 ; Push the interrupt number.
jmp isr_common_stub ; Go to our common handler code.
%endmacro
; This macro creates a stub for an ISR which passes it's own
; error code.
%macro ISR_ERRCODE 1
global isr%1
isr%1:
cli ; Disable interrupts.
push %1 ; Push the interrupt number
jmp isr_common_stub
%endmacro
; This macro creates a stub for an IRQ - the first parameter is
; the IRQ number, the second is the ISR number it is remapped to.
%macro IRQ 2
global irq%1
irq%1:
cli
push byte 0
push byte %2
jmp irq_common_stub
%endmacro
ISR_NOERRCODE 0
ISR_NOERRCODE 1
ISR_NOERRCODE 2
ISR_NOERRCODE 3
ISR_NOERRCODE 4
ISR_NOERRCODE 5
ISR_NOERRCODE 6
ISR_NOERRCODE 7
ISR_ERRCODE 8
ISR_NOERRCODE 9
ISR_ERRCODE 10
ISR_ERRCODE 11
ISR_ERRCODE 12
ISR_ERRCODE 13
ISR_ERRCODE 14
ISR_NOERRCODE 15
ISR_NOERRCODE 16
ISR_NOERRCODE 17
ISR_NOERRCODE 18
ISR_NOERRCODE 19
ISR_NOERRCODE 20
ISR_NOERRCODE 21
ISR_NOERRCODE 22
ISR_NOERRCODE 23
ISR_NOERRCODE 24
ISR_NOERRCODE 25
ISR_NOERRCODE 26
ISR_NOERRCODE 27
ISR_NOERRCODE 28
ISR_NOERRCODE 29
ISR_NOERRCODE 30
ISR_NOERRCODE 31
ISR_NOERRCODE 128
IRQ 0, 32
IRQ 1, 33
IRQ 2, 34
IRQ 3, 35
IRQ 4, 36
IRQ 5, 37
IRQ 6, 38
IRQ 7, 39
IRQ 8, 40
IRQ 9, 41
IRQ 10, 42
IRQ 11, 43
IRQ 12, 44
IRQ 13, 45
IRQ 14, 46
IRQ 15, 47
; In isr.c
extern isr_handler
; This is our common ISR stub. It saves the processor state, sets
; up for kernel mode segments, calls the C-level fault handler,
; and finally restores the stack frame.
isr_common_stub:
pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax
mov ax, ds ; Lower 16-bits of eax = ds.
push eax ; save the data segment descriptor
mov ax, 0x10 ; load the kernel data segment descriptor
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
call isr_handler
pop ebx ; reload the original data segment descriptor
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
popa ; Pops edi,esi,ebp...
add esp, 8 ; Cleans up the pushed error code and pushed ISR number
sti
iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
; In isr.c
extern irq_handler
; This is our common IRQ stub. It saves the processor state, sets
; up for kernel mode segments, calls the C-level fault handler,
; and finally restores the stack frame.
irq_common_stub:
pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax
mov ax, ds ; Lower 16-bits of eax = ds.
push eax ; save the data segment descriptor
mov ax, 0x10 ; load the kernel data segment descriptor
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
call irq_handler
pop ebx ; reload the original data segment descriptor
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
popa ; Pops edi,esi,ebp...
add esp, 8 ; Cleans up the pushed error code and pushed ISR number
sti
iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP

58
user_mode/src/isr.c Normal file
View file

@ -0,0 +1,58 @@
//
// isr.c -- High level interrupt service routines and interrupt request handlers.
// Part of this code is modified from Bran's kernel development tutorials.
// Rewritten for JamesM's kernel development tutorials.
//
#include "common.h"
#include "isr.h"
#include "monitor.h"
isr_t interrupt_handlers[256];
void register_interrupt_handler(u8int n, isr_t handler)
{
interrupt_handlers[n] = handler;
}
// This gets called from our ASM interrupt handler stub.
void isr_handler(registers_t regs)
{
// This line is important. When the processor extends the 8-bit interrupt number
// to a 32bit value, it sign-extends, not zero extends. So if the most significant
// bit (0x80) is set, regs.int_no will be very large (about 0xffffff80).
u8int int_no = regs.int_no & 0xFF;
if (interrupt_handlers[int_no] != 0)
{
isr_t handler = interrupt_handlers[int_no];
handler(&regs);
}
else
{
monitor_write("unhandled interrupt: ");
monitor_write_hex(int_no);
monitor_put('\n');
for(;;);
}
}
// This gets called from our ASM interrupt handler stub.
void irq_handler(registers_t regs)
{
// Send an EOI (end of interrupt) signal to the PICs.
// If this interrupt involved the slave.
if (regs.int_no >= 40)
{
// Send reset signal to slave.
outb(0xA0, 0x20);
}
// Send reset signal to master. (As well as slave, if necessary).
outb(0x20, 0x20);
if (interrupt_handlers[regs.int_no] != 0)
{
isr_t handler = interrupt_handlers[regs.int_no];
handler(&regs);
}
}

39
user_mode/src/isr.h Normal file
View file

@ -0,0 +1,39 @@
//
// isr.h -- Interface and structures for high level interrupt service routines.
// Part of this code is modified from Bran's kernel development tutorials.
// Rewritten for JamesM's kernel development tutorials.
//
#include "common.h"
// A few defines to make life a little easier
#define IRQ0 32
#define IRQ1 33
#define IRQ2 34
#define IRQ3 35
#define IRQ4 36
#define IRQ5 37
#define IRQ6 38
#define IRQ7 39
#define IRQ8 40
#define IRQ9 41
#define IRQ10 42
#define IRQ11 43
#define IRQ12 44
#define IRQ13 45
#define IRQ14 46
#define IRQ15 47
typedef struct registers
{
u32int ds; // Data segment selector
u32int edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed by pusha.
u32int int_no, err_code; // Interrupt number and error code (if applicable)
u32int eip, cs, eflags, useresp, ss; // Pushed by the processor automatically.
} registers_t;
// Enables registration of callbacks for interrupts or IRQs.
// For IRQs, to ease confusion, use the #defines above as the
// first parameter.
typedef void (*isr_t)(registers_t*);
void register_interrupt_handler(u8int n, isr_t handler);

BIN
user_mode/src/kernel Executable file

Binary file not shown.

408
user_mode/src/kheap.c Normal file
View file

@ -0,0 +1,408 @@
// kheap.c -- Kernel heap functions, also provides
// a placement malloc() for use before the heap is
// initialised.
// Written for JamesM's kernel development tutorials.
#include "kheap.h"
#include "paging.h"
// end is defined in the linker script.
extern u32int end;
u32int placement_address = (u32int)&end;
extern page_directory_t *kernel_directory;
heap_t *kheap=0;
u32int kmalloc_int(u32int sz, int align, u32int *phys)
{
if (kheap != 0)
{
void *addr = alloc(sz, (u8int)align, kheap);
if (phys != 0)
{
page_t *page = get_page((u32int)addr, 0, kernel_directory);
*phys = page->frame*0x1000 + ((u32int)addr&0xFFF);
}
return (u32int)addr;
}
else
{
if (align == 1 && (placement_address & 0xFFFFF000) )
{
// Align the placement address;
placement_address &= 0xFFFFF000;
placement_address += 0x1000;
}
if (phys)
{
*phys = placement_address;
}
u32int tmp = placement_address;
placement_address += sz;
return tmp;
}
}
void kfree(void *p)
{
free(p, kheap);
}
u32int kmalloc_a(u32int sz)
{
return kmalloc_int(sz, 1, 0);
}
u32int kmalloc_p(u32int sz, u32int *phys)
{
return kmalloc_int(sz, 0, phys);
}
u32int kmalloc_ap(u32int sz, u32int *phys)
{
return kmalloc_int(sz, 1, phys);
}
u32int kmalloc(u32int sz)
{
return kmalloc_int(sz, 0, 0);
}
static void expand(u32int new_size, heap_t *heap)
{
// Sanity check.
ASSERT(new_size > heap->end_address - heap->start_address);
// Get the nearest following page boundary.
if (new_size&0xFFFFF000 != 0)
{
new_size &= 0xFFFFF000;
new_size += 0x1000;
}
// Make sure we are not overreaching ourselves.
ASSERT(heap->start_address+new_size <= heap->max_address);
// This should always be on a page boundary.
u32int old_size = heap->end_address-heap->start_address;
u32int i = old_size;
while (i < new_size)
{
alloc_frame( get_page(heap->start_address+i, 1, kernel_directory),
(heap->supervisor)?1:0, (heap->readonly)?0:1);
i += 0x1000 /* page size */;
}
heap->end_address = heap->start_address+new_size;
}
static u32int contract(u32int new_size, heap_t *heap)
{
// Sanity check.
ASSERT(new_size < heap->end_address-heap->start_address);
// Get the nearest following page boundary.
if (new_size&0x1000)
{
new_size &= 0x1000;
new_size += 0x1000;
}
// Don't contract too far!
if (new_size < HEAP_MIN_SIZE)
new_size = HEAP_MIN_SIZE;
u32int old_size = heap->end_address-heap->start_address;
u32int i = old_size - 0x1000;
while (new_size < i)
{
free_frame(get_page(heap->start_address+i, 0, kernel_directory));
i -= 0x1000;
}
heap->end_address = heap->start_address + new_size;
return new_size;
}
static s32int find_smallest_hole(u32int size, u8int page_align, heap_t *heap)
{
// Find the smallest hole that will fit.
u32int iterator = 0;
while (iterator < heap->index.size)
{
header_t *header = (header_t *)lookup_ordered_array(iterator, &heap->index);
// If the user has requested the memory be page-aligned
if (page_align > 0)
{
// Page-align the starting point of this header.
u32int location = (u32int)header;
s32int offset = 0;
if ((location+sizeof(header_t)) & 0xFFFFF000 != 0)
offset = 0x1000 /* page size */ - (location+sizeof(header_t))%0x1000;
s32int hole_size = (s32int)header->size - offset;
// Can we fit now?
if (hole_size >= (s32int)size)
break;
}
else if (header->size >= size)
break;
iterator++;
}
// Why did the loop exit?
if (iterator == heap->index.size)
return -1; // We got to the end and didn't find anything.
else
return iterator;
}
static s8int header_t_less_than(void*a, void *b)
{
return (((header_t*)a)->size < ((header_t*)b)->size)?1:0;
}
heap_t *create_heap(u32int start, u32int end_addr, u32int max, u8int supervisor, u8int readonly)
{
heap_t *heap = (heap_t*)kmalloc(sizeof(heap_t));
// All our assumptions are made on startAddress and endAddress being page-aligned.
ASSERT(start%0x1000 == 0);
ASSERT(end_addr%0x1000 == 0);
// Initialise the index.
heap->index = place_ordered_array( (void*)start, HEAP_INDEX_SIZE, &header_t_less_than);
// Shift the start address forward to resemble where we can start putting data.
start += sizeof(type_t)*HEAP_INDEX_SIZE;
// Make sure the start address is page-aligned.
if (start & 0xFFFFF000 != 0)
{
start &= 0xFFFFF000;
start += 0x1000;
}
// Write the start, end and max addresses into the heap structure.
heap->start_address = start;
heap->end_address = end_addr;
heap->max_address = max;
heap->supervisor = supervisor;
heap->readonly = readonly;
// We start off with one large hole in the index.
header_t *hole = (header_t *)start;
hole->size = end_addr-start;
hole->magic = HEAP_MAGIC;
hole->is_hole = 1;
insert_ordered_array((void*)hole, &heap->index);
return heap;
}
void *alloc(u32int size, u8int page_align, heap_t *heap)
{
// Make sure we take the size of header/footer into account.
u32int new_size = size + sizeof(header_t) + sizeof(footer_t);
// Find the smallest hole that will fit.
s32int iterator = find_smallest_hole(new_size, page_align, heap);
if (iterator == -1) // If we didn't find a suitable hole
{
// Save some previous data.
u32int old_length = heap->end_address - heap->start_address;
u32int old_end_address = heap->end_address;
// We need to allocate some more space.
expand(old_length+new_size, heap);
u32int new_length = heap->end_address-heap->start_address;
// Find the endmost header. (Not endmost in size, but in location).
iterator = 0;
// Vars to hold the index of, and value of, the endmost header found so far.
u32int idx = -1; u32int value = 0x0;
while (iterator < heap->index.size)
{
u32int tmp = (u32int)lookup_ordered_array(iterator, &heap->index);
if (tmp > value)
{
value = tmp;
idx = iterator;
}
iterator++;
}
// If we didn't find ANY headers, we need to add one.
if (idx == -1)
{
header_t *header = (header_t *)old_end_address;
header->magic = HEAP_MAGIC;
header->size = new_length - old_length;
header->is_hole = 1;
footer_t *footer = (footer_t *) (old_end_address + header->size - sizeof(footer_t));
footer->magic = HEAP_MAGIC;
footer->header = header;
insert_ordered_array((void*)header, &heap->index);
}
else
{
// The last header needs adjusting.
header_t *header = lookup_ordered_array(idx, &heap->index);
header->size += new_length - old_length;
// Rewrite the footer.
footer_t *footer = (footer_t *) ( (u32int)header + header->size - sizeof(footer_t) );
footer->header = header;
footer->magic = HEAP_MAGIC;
}
// We now have enough space. Recurse, and call the function again.
return alloc(size, page_align, heap);
}
header_t *orig_hole_header = (header_t *)lookup_ordered_array(iterator, &heap->index);
u32int orig_hole_pos = (u32int)orig_hole_header;
u32int orig_hole_size = orig_hole_header->size;
// Here we work out if we should split the hole we found into two parts.
// Is the original hole size - requested hole size less than the overhead for adding a new hole?
if (orig_hole_size-new_size < sizeof(header_t)+sizeof(footer_t))
{
// Then just increase the requested size to the size of the hole we found.
size += orig_hole_size-new_size;
new_size = orig_hole_size;
}
// If we need to page-align the data, do it now and make a new hole in front of our block.
if (page_align && orig_hole_pos&0xFFFFF000)
{
u32int new_location = orig_hole_pos + 0x1000 /* page size */ - (orig_hole_pos&0xFFF) - sizeof(header_t);
header_t *hole_header = (header_t *)orig_hole_pos;
hole_header->size = 0x1000 /* page size */ - (orig_hole_pos&0xFFF) - sizeof(header_t);
hole_header->magic = HEAP_MAGIC;
hole_header->is_hole = 1;
footer_t *hole_footer = (footer_t *) ( (u32int)new_location - sizeof(footer_t) );
hole_footer->magic = HEAP_MAGIC;
hole_footer->header = hole_header;
orig_hole_pos = new_location;
orig_hole_size = orig_hole_size - hole_header->size;
}
else
{
// Else we don't need this hole any more, delete it from the index.
remove_ordered_array(iterator, &heap->index);
}
// Overwrite the original header...
header_t *block_header = (header_t *)orig_hole_pos;
block_header->magic = HEAP_MAGIC;
block_header->is_hole = 0;
block_header->size = new_size;
// ...And the footer
footer_t *block_footer = (footer_t *) (orig_hole_pos + sizeof(header_t) + size);
block_footer->magic = HEAP_MAGIC;
block_footer->header = block_header;
// We may need to write a new hole after the allocated block.
// We do this only if the new hole would have positive size...
if (orig_hole_size - new_size > 0)
{
header_t *hole_header = (header_t *) (orig_hole_pos + sizeof(header_t) + size + sizeof(footer_t));
hole_header->magic = HEAP_MAGIC;
hole_header->is_hole = 1;
hole_header->size = orig_hole_size - new_size;
footer_t *hole_footer = (footer_t *) ( (u32int)hole_header + orig_hole_size - new_size - sizeof(footer_t) );
if ((u32int)hole_footer < heap->end_address)
{
hole_footer->magic = HEAP_MAGIC;
hole_footer->header = hole_header;
}
// Put the new hole in the index;
insert_ordered_array((void*)hole_header, &heap->index);
}
// ...And we're done!
return (void *) ( (u32int)block_header+sizeof(header_t) );
}
void free(void *p, heap_t *heap)
{
// Exit gracefully for null pointers.
if (p == 0)
return;
// Get the header and footer associated with this pointer.
header_t *header = (header_t*) ( (u32int)p - sizeof(header_t) );
footer_t *footer = (footer_t*) ( (u32int)header + header->size - sizeof(footer_t) );
// Sanity checks.
ASSERT(header->magic == HEAP_MAGIC);
ASSERT(footer->magic == HEAP_MAGIC);
// Make us a hole.
header->is_hole = 1;
// Do we want to add this header into the 'free holes' index?
char do_add = 1;
// Unify left
// If the thing immediately to the left of us is a footer...
footer_t *test_footer = (footer_t*) ( (u32int)header - sizeof(footer_t) );
if (test_footer->magic == HEAP_MAGIC &&
test_footer->header->is_hole == 1)
{
u32int cache_size = header->size; // Cache our current size.
header = test_footer->header; // Rewrite our header with the new one.
footer->header = header; // Rewrite our footer to point to the new header.
header->size += cache_size; // Change the size.
do_add = 0; // Since this header is already in the index, we don't want to add it again.
}
// Unify right
// If the thing immediately to the right of us is a header...
header_t *test_header = (header_t*) ( (u32int)footer + sizeof(footer_t) );
if (test_header->magic == HEAP_MAGIC &&
test_header->is_hole)
{
header->size += test_header->size; // Increase our size.
test_footer = (footer_t*) ( (u32int)test_header + // Rewrite it's footer to point to our header.
test_header->size - sizeof(footer_t) );
footer = test_footer;
// Find and remove this header from the index.
u32int iterator = 0;
while ( (iterator < heap->index.size) &&
(lookup_ordered_array(iterator, &heap->index) != (void*)test_header) )
iterator++;
// Make sure we actually found the item.
ASSERT(iterator < heap->index.size);
// Remove it.
remove_ordered_array(iterator, &heap->index);
}
// If the footer location is the end address, we can contract.
if ( (u32int)footer+sizeof(footer_t) == heap->end_address)
{
u32int old_length = heap->end_address-heap->start_address;
u32int new_length = contract( (u32int)header - heap->start_address, heap);
// Check how big we will be after resizing.
if (header->size - (old_length-new_length) > 0)
{
// We will still exist, so resize us.
header->size -= old_length-new_length;
footer = (footer_t*) ( (u32int)header + header->size - sizeof(footer_t) );
footer->magic = HEAP_MAGIC;
footer->header = header;
}
else
{
// We will no longer exist :(. Remove us from the index.
u32int iterator = 0;
while ( (iterator < heap->index.size) &&
(lookup_ordered_array(iterator, &heap->index) != (void*)test_header) )
iterator++;
// If we didn't find ourselves, we have nothing to remove.
if (iterator < heap->index.size)
remove_ordered_array(iterator, &heap->index);
}
}
// If required, add us to the index.
if (do_add == 1)
insert_ordered_array((void*)header, &heap->index);
}

100
user_mode/src/kheap.h Normal file
View file

@ -0,0 +1,100 @@
// kheap.h -- Interface for kernel heap functions, also provides
// a placement malloc() for use before the heap is
// initialised.
// Written for JamesM's kernel development tutorials.
#ifndef KHEAP_H
#define KHEAP_H
#include "common.h"
#include "ordered_array.h"
#define KHEAP_START 0xC0000000
#define KHEAP_INITIAL_SIZE 0x100000
#define HEAP_INDEX_SIZE 0x20000
#define HEAP_MAGIC 0x123890AB
#define HEAP_MIN_SIZE 0x70000
/**
Size information for a hole/block
**/
typedef struct
{
u32int magic; // Magic number, used for error checking and identification.
u8int is_hole; // 1 if this is a hole. 0 if this is a block.
u32int size; // size of the block, including the end footer.
} header_t;
typedef struct
{
u32int magic; // Magic number, same as in header_t.
header_t *header; // Pointer to the block header.
} footer_t;
typedef struct
{
ordered_array_t index;
u32int start_address; // The start of our allocated space.
u32int end_address; // The end of our allocated space. May be expanded up to max_address.
u32int max_address; // The maximum address the heap can be expanded to.
u8int supervisor; // Should extra pages requested by us be mapped as supervisor-only?
u8int readonly; // Should extra pages requested by us be mapped as read-only?
} heap_t;
/**
Create a new heap.
**/
heap_t *create_heap(u32int start, u32int end, u32int max, u8int supervisor, u8int readonly);
/**
Allocates a contiguous region of memory 'size' in size. If page_align==1, it creates that block starting
on a page boundary.
**/
void *alloc(u32int size, u8int page_align, heap_t *heap);
/**
Releases a block allocated with 'alloc'.
**/
void free(void *p, heap_t *heap);
/**
Allocate a chunk of memory, sz in size. If align == 1,
the chunk must be page-aligned. If phys != 0, the physical
location of the allocated chunk will be stored into phys.
This is the internal version of kmalloc. More user-friendly
parameter representations are available in kmalloc, kmalloc_a,
kmalloc_ap, kmalloc_p.
**/
u32int kmalloc_int(u32int sz, int align, u32int *phys);
/**
Allocate a chunk of memory, sz in size. The chunk must be
page aligned.
**/
u32int kmalloc_a(u32int sz);
/**
Allocate a chunk of memory, sz in size. The physical address
is returned in phys. Phys MUST be a valid pointer to u32int!
**/
u32int kmalloc_p(u32int sz, u32int *phys);
/**
Allocate a chunk of memory, sz in size. The physical address
is returned in phys. It must be page-aligned.
**/
u32int kmalloc_ap(u32int sz, u32int *phys);
/**
General allocation function.
**/
u32int kmalloc(u32int sz);
/**
General deallocation function.
**/
void kfree(void *p);
#endif // KHEAP_H

33
user_mode/src/link.ld Normal file
View file

@ -0,0 +1,33 @@
/* Link.ld -- Linker script for the kernel - ensure everything goes in the */
/* Correct place. */
/* Original file taken from Bran's Kernel Development */
/* tutorials: http://www.osdever.net/bkerndev/index.php. */
ENTRY(start)
SECTIONS
{
.text 0x100000 :
{
code = .; _code = .; __code = .;
*(.text)
. = ALIGN(4096);
}
.data :
{
data = .; _data = .; __data = .;
*(.data)
*(.rodata)
. = ALIGN(4096);
}
.bss :
{
bss = .; _bss = .; __bss = .;
*(.bss)
. = ALIGN(4096);
}
end = .; _end = .; __end = .;
}

98
user_mode/src/main.c Normal file
View file

@ -0,0 +1,98 @@
// main.c -- Defines the C-code kernel entry point, calls initialisation routines.
// Made for JamesM's tutorials <www.jamesmolloy.co.uk>
#include "monitor.h"
#include "descriptor_tables.h"
#include "timer.h"
#include "paging.h"
#include "multiboot.h"
#include "fs.h"
#include "initrd.h"
#include "task.h"
#include "syscall.h"
extern u32int placement_address;
u32int initial_esp;
int main(struct multiboot *mboot_ptr, u32int initial_stack)
{
initial_esp = initial_stack;
// Initialise all the ISRs and segmentation
init_descriptor_tables();
// Initialise the screen (by clearing it)
monitor_clear();
// Initialise the PIT to 100Hz
asm volatile("sti");
init_timer(50);
monitor_write(" -- sjs kernel --\n\n");
// Find the location of our initial ramdisk.
ASSERT(mboot_ptr->mods_count > 0);
u32int initrd_location = *((u32int*)mboot_ptr->mods_addr);
u32int initrd_end = *(u32int*)(mboot_ptr->mods_addr+4);
// Don't trample our module with placement accesses, please!
placement_address = initrd_end;
// Start paging.
initialise_paging();
monitor_write("*** paging\n\n");
// Initialise the initial ramdisk, and set it as the filesystem root.
fs_root = initialise_initrd(initrd_location);
monitor_write("*** initrd\n\n");
// Start multitasking.
initialise_tasking();
monitor_write("*** multitasking\n\n");
// Create a new process in a new address space which is a clone of this.
int ret = fork();
monitor_write("fork() returned ");
monitor_write_hex(ret);
monitor_write(", and getpid() returned ");
monitor_write_hex(getpid());
monitor_write("\n============================================================================\n\n");
monitor_write("*** fs\n\n");
// The next section of code is not reentrant so make sure we aren't interrupted during.
asm volatile("cli");
// list the contents of /
int i = 0;
struct dirent *node = 0;
while ( (node = readdir_fs(fs_root, i)) != 0) {
monitor_write("Found file ");
monitor_write(node->name);
fs_node_t *fsnode = finddir_fs(fs_root, node->name);
if ((fsnode->flags&0x7) == FS_DIRECTORY) {
monitor_write("\n\t(directory)\n");
} else {
monitor_write("\n\t contents: \"");
char buf[256];
u32int sz = read_fs(fsnode, 0, 256, buf);
int j;
for (j = 0; j < sz; j++)
monitor_put(buf[j]);
monitor_write("\"\n");
}
i++;
}
monitor_write("\n");
asm volatile("sti");
initialise_syscalls();
monitor_write("*** syscalls\n\n");
switch_to_user_mode();
monitor_write("*** user mode\n\n");
syscall_monitor_write("Hello, user world!\n");
return 0xdeadbeef;
}

216
user_mode/src/monitor.c Normal file
View file

@ -0,0 +1,216 @@
// monitor.c -- Defines functions for writing to the monitor.
// heavily based on Bran's kernel development tutorials,
// but rewritten for JamesM's kernel tutorials.
#include "monitor.h"
// The VGA framebuffer starts at 0xB8000.
u16int *video_memory = (u16int *)0xB8000;
// Stores the cursor position.
u8int cursor_x = 0;
u8int cursor_y = 0;
// Updates the hardware cursor.
static void move_cursor()
{
// The screen is 80 characters wide...
u16int cursorLocation = cursor_y * 80 + cursor_x;
outb(0x3D4, 14); // Tell the VGA board we are setting the high cursor byte.
outb(0x3D5, cursorLocation >> 8); // Send the high cursor byte.
outb(0x3D4, 15); // Tell the VGA board we are setting the low cursor byte.
outb(0x3D5, cursorLocation); // Send the low cursor byte.
}
// Scrolls the text on the screen up by one line.
static void scroll()
{
// Get a space character with the default colour attributes.
u8int attributeByte = (0 /*black*/ << 4) | (15 /*white*/ & 0x0F);
u16int blank = 0x20 /* space */ | (attributeByte << 8);
// Row 25 is the end, this means we need to scroll up
if(cursor_y >= 25)
{
// Move the current text chunk that makes up the screen
// back in the buffer by a line
int i;
for (i = 0*80; i < 24*80; i++)
{
video_memory[i] = video_memory[i+80];
}
// The last line should now be blank. Do this by writing
// 80 spaces to it.
for (i = 24*80; i < 25*80; i++)
{
video_memory[i] = blank;
}
// The cursor should now be on the last line.
cursor_y = 24;
}
}
// Writes a single character out to the screen.
void monitor_put(char c)
{
// The background colour is black (0), the foreground is white (15).
u8int backColour = 0;
u8int foreColour = 15;
// The attribute byte is made up of two nibbles - the lower being the
// foreground colour, and the upper the background colour.
u8int attributeByte = (backColour << 4) | (foreColour & 0x0F);
// The attribute byte is the top 8 bits of the word we have to send to the
// VGA board.
u16int attribute = attributeByte << 8;
u16int *location;
// Handle a backspace, by moving the cursor back one space
if (c == 0x08 && cursor_x)
{
cursor_x--;
}
// Handle a tab by increasing the cursor's X, but only to a point
// where it is divisible by 8.
else if (c == 0x09)
{
cursor_x = (cursor_x+8) & ~(8-1);
}
// Handle carriage return
else if (c == '\r')
{
cursor_x = 0;
}
// Handle newline by moving cursor back to left and increasing the row
else if (c == '\n')
{
cursor_x = 0;
cursor_y++;
}
// Handle any other printable character.
else if(c >= ' ')
{
location = video_memory + (cursor_y*80 + cursor_x);
*location = c | attribute;
cursor_x++;
}
// Check if we need to insert a new line because we have reached the end
// of the screen.
if (cursor_x >= 80)
{
cursor_x = 0;
cursor_y ++;
}
// Scroll the screen if needed.
scroll();
// Move the hardware cursor.
move_cursor();
}
// Clears the screen, by copying lots of spaces to the framebuffer.
void monitor_clear()
{
// Make an attribute byte for the default colours
u8int attributeByte = (0 /*black*/ << 4) | (15 /*white*/ & 0x0F);
u16int blank = 0x20 /* space */ | (attributeByte << 8);
int i;
for (i = 0; i < 80*25; i++)
{
video_memory[i] = blank;
}
// Move the hardware cursor back to the start.
cursor_x = 0;
cursor_y = 0;
move_cursor();
}
// Outputs a null-terminated ASCII string to the monitor.
void monitor_write(char *c)
{
int i = 0;
while (c[i])
{
monitor_put(c[i++]);
}
}
void monitor_write_hex(u32int n)
{
s32int tmp;
monitor_write("0x");
char noZeroes = 1;
int i;
for (i = 28; i > 0; i -= 4)
{
tmp = (n >> i) & 0xF;
if (tmp == 0 && noZeroes != 0)
{
continue;
}
if (tmp >= 0xA)
{
noZeroes = 0;
monitor_put (tmp-0xA+'a' );
}
else
{
noZeroes = 0;
monitor_put( tmp+'0' );
}
}
tmp = n & 0xF;
if (tmp >= 0xA)
{
monitor_put (tmp-0xA+'a');
}
else
{
monitor_put (tmp+'0');
}
}
void monitor_write_dec(u32int n)
{
if (n == 0)
{
monitor_put('0');
return;
}
s32int acc = n;
char c[32];
int i = 0;
while (acc > 0)
{
c[i] = '0' + acc%10;
acc /= 10;
i++;
}
c[i] = 0;
char c2[32];
c2[i--] = 0;
int j = 0;
while(i >= 0)
{
c2[i--] = c[j++];
}
monitor_write(c2);
}

24
user_mode/src/monitor.h Normal file
View file

@ -0,0 +1,24 @@
// monitor.h -- Defines the interface for monitor.h
// From JamesM's kernel development tutorials.
#ifndef MONITOR_H
#define MONITOR_H
#include "common.h"
// Write a single character out to the screen.
void monitor_put(char c);
// Clear the screen to all black.
void monitor_clear();
// Output a null-terminated ASCII string to the monitor.
void monitor_write(char *c);
// Output a hex value to the monitor.
void monitor_write_hex(u32int n);
// Output a decimal value to the monitor.
void monitor_write_dec(u32int n);
#endif // MONITOR_H

51
user_mode/src/multiboot.h Normal file
View file

@ -0,0 +1,51 @@
// multiboot.h -- Declares the multiboot info structure.
// Made for JamesM's tutorials <www.jamesmolloy.co.uk>
#ifndef MULTIBOOT_H
#define MULTIBOOT_H
#include "common.h"
#define MULTIBOOT_FLAG_MEM 0x001
#define MULTIBOOT_FLAG_DEVICE 0x002
#define MULTIBOOT_FLAG_CMDLINE 0x004
#define MULTIBOOT_FLAG_MODS 0x008
#define MULTIBOOT_FLAG_AOUT 0x010
#define MULTIBOOT_FLAG_ELF 0x020
#define MULTIBOOT_FLAG_MMAP 0x040
#define MULTIBOOT_FLAG_CONFIG 0x080
#define MULTIBOOT_FLAG_LOADER 0x100
#define MULTIBOOT_FLAG_APM 0x200
#define MULTIBOOT_FLAG_VBE 0x400
struct multiboot
{
u32int flags;
u32int mem_lower;
u32int mem_upper;
u32int boot_device;
u32int cmdline;
u32int mods_count;
u32int mods_addr;
u32int num;
u32int size;
u32int addr;
u32int shndx;
u32int mmap_length;
u32int mmap_addr;
u32int drives_length;
u32int drives_addr;
u32int config_table;
u32int boot_loader_name;
u32int apm_table;
u32int vbe_control_info;
u32int vbe_mode_info;
u32int vbe_mode;
u32int vbe_interface_seg;
u32int vbe_interface_off;
u32int vbe_interface_len;
} __attribute__((packed));
typedef struct multiboot_header multiboot_header_t;
#endif

View file

@ -0,0 +1,76 @@
// ordered_array.c -- Implementation for creating, inserting and deleting
// from ordered arrays.
// Written for JamesM's kernel development tutorials.
#include "ordered_array.h"
s8int standard_lessthan_predicate(type_t a, type_t b)
{
return (a<b)?1:0;
}
ordered_array_t create_ordered_array(u32int max_size, lessthan_predicate_t less_than)
{
ordered_array_t to_ret;
to_ret.array = (void*)kmalloc(max_size*sizeof(type_t));
memset(to_ret.array, 0, max_size*sizeof(type_t));
to_ret.size = 0;
to_ret.max_size = max_size;
to_ret.less_than = less_than;
return to_ret;
}
ordered_array_t place_ordered_array(void *addr, u32int max_size, lessthan_predicate_t less_than)
{
ordered_array_t to_ret;
to_ret.array = (type_t*)addr;
memset(to_ret.array, 0, max_size*sizeof(type_t));
to_ret.size = 0;
to_ret.max_size = max_size;
to_ret.less_than = less_than;
return to_ret;
}
void destroy_ordered_array(ordered_array_t *array)
{
// kfree(array->array);
}
void insert_ordered_array(type_t item, ordered_array_t *array)
{
ASSERT(array->less_than);
u32int iterator = 0;
while (iterator < array->size && array->less_than(array->array[iterator], item))
iterator++;
if (iterator == array->size) // just add at the end of the array.
array->array[array->size++] = item;
else
{
type_t tmp = array->array[iterator];
array->array[iterator] = item;
while (iterator < array->size)
{
iterator++;
type_t tmp2 = array->array[iterator];
array->array[iterator] = tmp;
tmp = tmp2;
}
array->size++;
}
}
type_t lookup_ordered_array(u32int i, ordered_array_t *array)
{
ASSERT(i < array->size);
return array->array[i];
}
void remove_ordered_array(u32int i, ordered_array_t *array)
{
while (i < array->size)
{
array->array[i] = array->array[i+1];
i++;
}
array->size--;
}

View file

@ -0,0 +1,59 @@
// ordered_array.h -- Interface for creating, inserting and deleting
// from ordered arrays.
// Written for JamesM's kernel development tutorials.
#ifndef ORDERED_ARRAY_H
#define ORDERED_ARRAY_H
#include "common.h"
/**
This array is insertion sorted - it always remains in a sorted state (between calls).
It can store anything that can be cast to a void* -- so a u32int, or any pointer.
**/
typedef void* type_t;
/**
A predicate should return nonzero if the first argument is less than the second. Else
it should return zero.
**/
typedef s8int (*lessthan_predicate_t)(type_t,type_t);
typedef struct
{
type_t *array;
u32int size;
u32int max_size;
lessthan_predicate_t less_than;
} ordered_array_t;
/**
A standard less than predicate.
**/
s8int standard_lessthan_predicate(type_t a, type_t b);
/**
Create an ordered array.
**/
ordered_array_t create_ordered_array(u32int max_size, lessthan_predicate_t less_than);
ordered_array_t place_ordered_array(void *addr, u32int max_size, lessthan_predicate_t less_than);
/**
Destroy an ordered array.
**/
void destroy_ordered_array(ordered_array_t *array);
/**
Add an item into the array.
**/
void insert_ordered_array(type_t item, ordered_array_t *array);
/**
Lookup the item at index i.
**/
type_t lookup_ordered_array(u32int i, ordered_array_t *array);
/**
Deletes the item at location i from the array.
**/
void remove_ordered_array(u32int i, ordered_array_t *array);
#endif // ORDERED_ARRAY_H

297
user_mode/src/paging.c Executable file
View file

@ -0,0 +1,297 @@
// paging.c -- Defines the interface for and structures relating to paging.
// Written for JamesM's kernel development tutorials.
#include "paging.h"
#include "kheap.h"
// The kernel's page directory
page_directory_t *kernel_directory=0;
// The current page directory;
page_directory_t *current_directory=0;
// A bitset of frames - used or free.
u32int *frames;
u32int nframes;
// Defined in kheap.c
extern u32int placement_address;
extern heap_t *kheap;
// Macros used in the bitset algorithms.
#define INDEX_FROM_BIT(a) (a/(8*4))
#define OFFSET_FROM_BIT(a) (a%(8*4))
// Static function to set a bit in the frames bitset
static void set_frame(u32int frame_addr)
{
u32int frame = frame_addr/0x1000;
u32int idx = INDEX_FROM_BIT(frame);
u32int off = OFFSET_FROM_BIT(frame);
frames[idx] |= (0x1 << off);
}
// Static function to clear a bit in the frames bitset
static void clear_frame(u32int frame_addr)
{
u32int frame = frame_addr/0x1000;
u32int idx = INDEX_FROM_BIT(frame);
u32int off = OFFSET_FROM_BIT(frame);
frames[idx] &= ~(0x1 << off);
}
// Static function to test if a bit is set.
static u32int test_frame(u32int frame_addr)
{
u32int frame = frame_addr/0x1000;
u32int idx = INDEX_FROM_BIT(frame);
u32int off = OFFSET_FROM_BIT(frame);
return (frames[idx] & (0x1 << off));
}
// Static function to find the first free frame.
static u32int first_frame()
{
u32int i, j;
for (i = 0; i < INDEX_FROM_BIT(nframes); i++)
{
if (frames[i] != 0xFFFFFFFF) // nothing free, exit early.
{
// at least one bit is free here.
for (j = 0; j < 32; j++)
{
u32int toTest = 0x1 << j;
if ( !(frames[i]&toTest) )
{
return i*4*8+j;
}
}
}
}
}
// Function to allocate a frame.
void alloc_frame(page_t *page, int is_kernel, int is_writeable)
{
if (page->frame != 0)
{
return;
}
else
{
u32int idx = first_frame();
if (idx == (u32int)-1)
{
// PANIC! no free frames!!
}
set_frame(idx*0x1000);
page->present = 1;
page->rw = (is_writeable==1)?1:0;
page->user = (is_kernel==1)?0:1;
page->frame = idx;
}
}
// Function to deallocate a frame.
void free_frame(page_t *page)
{
u32int frame;
if (!(frame=page->frame))
{
return;
}
else
{
clear_frame(frame);
page->frame = 0x0;
}
}
void initialise_paging()
{
// The size of physical memory. For the moment we
// assume it is 16MB big.
u32int mem_end_page = 0x1000000;
nframes = mem_end_page / 0x1000;
frames = (u32int*)kmalloc(INDEX_FROM_BIT(nframes));
memset(frames, 0, INDEX_FROM_BIT(nframes));
// Let's make a page directory.
u32int phys;
kernel_directory = (page_directory_t*)kmalloc_a(sizeof(page_directory_t));
memset(kernel_directory, 0, sizeof(page_directory_t));
kernel_directory->physicalAddr = (u32int)kernel_directory->tablesPhysical;
// Map some pages in the kernel heap area.
// Here we call get_page but not alloc_frame. This causes page_table_t's
// to be created where necessary. We can't allocate frames yet because they
// they need to be identity mapped first below, and yet we can't increase
// placement_address between identity mapping and enabling the heap!
int i = 0;
for (i = KHEAP_START; i < KHEAP_START+KHEAP_INITIAL_SIZE; i += 0x1000)
get_page(i, 1, kernel_directory);
// We need to identity map (phys addr = virt addr) from
// 0x0 to the end of used memory, so we can access this
// transparently, as if paging wasn't enabled.
// NOTE that we use a while loop here deliberately.
// inside the loop body we actually change placement_address
// by calling kmalloc(). A while loop causes this to be
// computed on-the-fly rather than once at the start.
// Allocate a lil' bit extra so the kernel heap can be
// initialised properly.
i = 0;
while (i < 0x400000 ) //placement_address+0x1000)
{
// Kernel code is readable but not writeable from userspace.
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
i += 0x1000;
}
// Now allocate those pages we mapped earlier.
for (i = KHEAP_START; i < KHEAP_START+KHEAP_INITIAL_SIZE; i += 0x1000)
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
// Before we enable paging, we must register our page fault handler.
register_interrupt_handler(14, page_fault);
// Now, enable paging!
switch_page_directory(kernel_directory);
// Initialise the kernel heap.
kheap = create_heap(KHEAP_START, KHEAP_START+KHEAP_INITIAL_SIZE, 0xCFFFF000, 0, 0);
current_directory = clone_directory(kernel_directory);
switch_page_directory(current_directory);
}
void switch_page_directory(page_directory_t *dir)
{
current_directory = dir;
asm volatile("mov %0, %%cr3":: "r"(dir->physicalAddr));
u32int cr0;
asm volatile("mov %%cr0, %0": "=r"(cr0));
cr0 |= 0x80000000; // Enable paging!
asm volatile("mov %0, %%cr0":: "r"(cr0));
}
page_t *get_page(u32int address, int make, page_directory_t *dir)
{
// Turn the address into an index.
address /= 0x1000;
// Find the page table containing this address.
u32int table_idx = address / 1024;
if (dir->tables[table_idx]) // If this table is already assigned
{
return &dir->tables[table_idx]->pages[address%1024];
}
else if(make)
{
u32int tmp;
dir->tables[table_idx] = (page_table_t*)kmalloc_ap(sizeof(page_table_t), &tmp);
memset(dir->tables[table_idx], 0, 0x1000);
dir->tablesPhysical[table_idx] = tmp | 0x7; // PRESENT, RW, US.
return &dir->tables[table_idx]->pages[address%1024];
}
else
{
return 0;
}
}
void page_fault(registers_t *regs)
{
// A page fault has occurred.
// The faulting address is stored in the CR2 register.
u32int faulting_address;
asm volatile("mov %%cr2, %0" : "=r" (faulting_address));
// The error code gives us details of what happened.
int present = !(regs->err_code & 0x1); // Page not present
int rw = regs->err_code & 0x2; // Write operation?
int us = regs->err_code & 0x4; // Processor was in user-mode?
int reserved = regs->err_code & 0x8; // Overwritten CPU-reserved bits of page entry?
int id = regs->err_code & 0x10; // Caused by an instruction fetch?
// Output an error message.
monitor_write("Page fault! ( ");
if (present) {monitor_write("present ");}
if (rw) {monitor_write("read-only ");}
if (us) {monitor_write("user-mode ");}
if (reserved) {monitor_write("reserved ");}
monitor_write(") at ");
monitor_write_hex(faulting_address);
monitor_write(" - EIP: ");
monitor_write_hex(regs->eip);
monitor_write("\n");
PANIC("Page fault");
}
static page_table_t *clone_table(page_table_t *src, u32int *physAddr)
{
// Make a new page table, which is page aligned.
page_table_t *table = (page_table_t*)kmalloc_ap(sizeof(page_table_t), physAddr);
// Ensure that the new table is blank.
memset(table, 0, sizeof(page_directory_t));
// For every entry in the table...
int i;
for (i = 0; i < 1024; i++)
{
// If the source entry has a frame associated with it...
if (!src->pages[i].frame)
continue;
// Get a new frame.
alloc_frame(&table->pages[i], 0, 0);
// Clone the flags from source to destination.
if (src->pages[i].present) table->pages[i].present = 1;
if (src->pages[i].rw) table->pages[i].rw = 1;
if (src->pages[i].user) table->pages[i].user = 1;
if (src->pages[i].accessed)table->pages[i].accessed = 1;
if (src->pages[i].dirty) table->pages[i].dirty = 1;
// Physically copy the data across. This function is in process.s.
copy_page_physical(src->pages[i].frame*0x1000, table->pages[i].frame*0x1000);
}
return table;
}
page_directory_t *clone_directory(page_directory_t *src)
{
u32int phys;
// Make a new page directory and obtain its physical address.
page_directory_t *dir = (page_directory_t*)kmalloc_ap(sizeof(page_directory_t), &phys);
// Ensure that it is blank.
memset(dir, 0, sizeof(page_directory_t));
// Get the offset of tablesPhysical from the start of the page_directory_t structure.
u32int offset = (u32int)dir->tablesPhysical - (u32int)dir;
// Then the physical address of dir->tablesPhysical is:
dir->physicalAddr = phys + offset;
// Go through each page table. If the page table is in the kernel directory, do not make a new copy.
int i;
for (i = 0; i < 1024; i++)
{
if (!src->tables[i])
continue;
if (kernel_directory->tables[i] == src->tables[i])
{
// It's in the kernel, so just use the same pointer.
dir->tables[i] = src->tables[i];
dir->tablesPhysical[i] = src->tablesPhysical[i];
}
else
{
// Copy the table.
u32int phys;
dir->tables[i] = clone_table(src->tables[i], &phys);
dir->tablesPhysical[i] = phys | 0x07;
}
}
return dir;
}

75
user_mode/src/paging.h Normal file
View file

@ -0,0 +1,75 @@
// paging.h -- Defines the interface for and structures relating to paging.
// Written for JamesM's kernel development tutorials.
#ifndef PAGING_H
#define PAGING_H
#include "common.h"
#include "isr.h"
typedef struct page
{
u32int present : 1; // Page present in memory
u32int rw : 1; // Read-only if clear, readwrite if set
u32int user : 1; // Supervisor level only if clear
u32int accessed : 1; // Has the page been accessed since last refresh?
u32int dirty : 1; // Has the page been written to since last refresh?
u32int unused : 7; // Amalgamation of unused and reserved bits
u32int frame : 20; // Frame address (shifted right 12 bits)
} page_t;
typedef struct page_table
{
page_t pages[1024];
} page_table_t;
typedef struct page_directory
{
/**
Array of pointers to pagetables.
**/
page_table_t *tables[1024];
/**
Array of pointers to the pagetables above, but gives their *physical*
location, for loading into the CR3 register.
**/
u32int tablesPhysical[1024];
/**
The physical address of tablesPhysical. This comes into play
when we get our kernel heap allocated and the directory
may be in a different location in virtual memory.
**/
u32int physicalAddr;
} page_directory_t;
/**
Sets up the environment, page directories etc and
enables paging.
**/
void initialise_paging();
/**
Causes the specified page directory to be loaded into the
CR3 register.
**/
void switch_page_directory(page_directory_t *new);
/**
Retrieves a pointer to the page required.
If make == 1, if the page-table in which this page should
reside isn't created, create it!
**/
page_t *get_page(u32int address, int make, page_directory_t *dir);
/**
Handler for page faults.
**/
void page_fault(registers_t *regs);
/**
Makes a copy of a page directory.
**/
page_directory_t *clone_directory(page_directory_t *src);
#endif

37
user_mode/src/process.s Normal file
View file

@ -0,0 +1,37 @@
[GLOBAL read_eip]
read_eip:
pop eax ; Get the return address
jmp eax ; Return. Can't use RET because return
; address popped off the stack.
[GLOBAL copy_page_physical]
copy_page_physical:
push ebx ; According to __cdecl, we must preserve the contents of EBX.
pushf ; push EFLAGS, so we can pop it and reenable interrupts
; later, if they were enabled anyway.
cli ; Disable interrupts, so we aren't interrupted.
; Load these in BEFORE we disable paging!
mov ebx, [esp+12] ; Source address
mov ecx, [esp+16] ; Destination address
mov edx, cr0 ; Get the control register...
and edx, 0x7fffffff ; and...
mov cr0, edx ; Disable paging.
mov edx, 1024 ; 1024*4bytes = 4096 bytes
.loop:
mov eax, [ebx] ; Get the word at the source address
mov [ecx], eax ; Store it at the dest address
add ebx, 4 ; Source address += sizeof(word)
add ecx, 4 ; Dest address += sizeof(word)
dec edx ; One less word to do
jnz .loop
mov edx, cr0 ; Get the control register again
or edx, 0x80000000 ; and...
mov cr0, edx ; Enable paging.
popf ; Pop EFLAGS back.
pop ebx ; Get the original value of EBX back.
ret

57
user_mode/src/syscall.c Normal file
View file

@ -0,0 +1,57 @@
// syscall.c -- Defines the implementation of a system call system.
// Written for JamesM's kernel development tutorials.
#include "syscall.h"
#include "isr.h"
#include "monitor.h"
static void syscall_handler(registers_t *regs);
DEFN_SYSCALL1(monitor_write, 0, const char*);
DEFN_SYSCALL1(monitor_write_hex, 1, const char*);
DEFN_SYSCALL1(monitor_write_dec, 2, const char*);
static void *syscalls[3] =
{
&monitor_write,
&monitor_write_hex,
&monitor_write_dec,
};
u32int num_syscalls = 3;
void initialise_syscalls()
{
// Register our syscall handler.
register_interrupt_handler (0x80, &syscall_handler);
}
void syscall_handler(registers_t *regs)
{
// Firstly, check if the requested syscall number is valid.
// The syscall number is found in EAX.
if (regs->eax >= num_syscalls)
return;
// Get the required syscall location.
void *location = syscalls[regs->eax];
// We don't know how many parameters the function wants, so we just
// push them all onto the stack in the correct order. The function will
// use all the parameters it wants, and we can pop them all back off afterwards.
int ret;
asm volatile (" \
push %1; \
push %2; \
push %3; \
push %4; \
push %5; \
call *%6; \
pop %%ebx; \
pop %%ebx; \
pop %%ebx; \
pop %%ebx; \
pop %%ebx; \
" : "=a" (ret) : "r" (regs->edi), "r" (regs->esi), "r" (regs->edx), "r" (regs->ecx), "r" (regs->ebx), "r" (location));
regs->eax = ret;
}

70
user_mode/src/syscall.h Normal file
View file

@ -0,0 +1,70 @@
// syscall.h -- Defines the interface for and structures relating to the syscall dispatch system.
// Written for JamesM's kernel development tutorials.
#ifndef SYSCALL_H
#define SYSCALL_H
#include "common.h"
void initialise_syscalls();
#define DECL_SYSCALL0(fn) int syscall_##fn();
#define DECL_SYSCALL1(fn,p1) int syscall_##fn(p1);
#define DECL_SYSCALL2(fn,p1,p2) int syscall_##fn(p1,p2);
#define DECL_SYSCALL3(fn,p1,p2,p3) int syscall_##fn(p1,p2,p3);
#define DECL_SYSCALL4(fn,p1,p2,p3,p4) int syscall_##fn(p1,p2,p3,p4);
#define DECL_SYSCALL5(fn,p1,p2,p3,p4,p5) int syscall_##fn(p1,p2,p3,p4,p5);
#define DEFN_SYSCALL0(fn, num) \
int syscall_##fn() \
{ \
int a; \
asm volatile("int $0x80" : "=a" (a) : "0" (num)); \
return a; \
}
#define DEFN_SYSCALL1(fn, num, P1) \
int syscall_##fn(P1 p1) \
{ \
int a; \
asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((int)p1)); \
return a; \
}
#define DEFN_SYSCALL2(fn, num, P1, P2) \
int syscall_##fn(P1 p1, P2 p2) \
{ \
int a; \
asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((int)p1), "c" ((int)p2)); \
return a; \
}
#define DEFN_SYSCALL3(fn, num, P1, P2, P3) \
int syscall_##fn(P1 p1, P2 p2, P3 p3) \
{ \
int a; \
asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((int)p1), "c" ((int)p2), "d"((int)p3)); \
return a; \
}
#define DEFN_SYSCALL4(fn, num, P1, P2, P3, P4) \
int syscall_##fn(P1 p1, P2 p2, P3 p3, P4 p4) \
{ \
int a; \
asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((int)p1), "c" ((int)p2), "d" ((int)p3), "S" ((int)p4)); \
return a; \
}
#define DEFN_SYSCALL5(fn, num) \
int syscall_##fn(P1 p1, P2 p2, P3 p3, P4 p4, P5 p5) \
{ \
int a; \
asm volatile("int $0x80" : "=a" (a) : "0" (num), "b" ((int)p1), "c" ((int)p2), "d" ((int)p3), "S" ((int)p4), "D" ((int)p5)); \
return a; \
}
DECL_SYSCALL1(monitor_write, const char*)
DECL_SYSCALL1(monitor_write_hex, const char*)
DECL_SYSCALL1(monitor_write_dec, const char*)
#endif

255
user_mode/src/task.c Normal file
View file

@ -0,0 +1,255 @@
//
// task.c - Implements the functionality needed to multitask.
// Written for JamesM's kernel development tutorials.
//
#include "task.h"
#include "paging.h"
#include "monitor.h"
// The currently running task.
volatile task_t *current_task;
// The start of the task linked list.
volatile task_t *ready_queue;
// Some externs are needed to access members in paging.c...
extern page_directory_t *kernel_directory;
extern page_directory_t *current_directory;
extern void alloc_frame(page_t*,int,int);
extern u32int initial_esp;
extern u32int read_eip();
// The next available process ID.
u32int next_pid = 1;
void initialise_tasking()
{
// Rather important stuff happening, no interrupts please!
asm volatile("cli");
// Relocate the stack so we know where it is.
move_stack((void*)0xE0000000, 0x2000);
// Initialise the first task (kernel task)
current_task = ready_queue = (task_t*)kmalloc(sizeof(task_t));
current_task->id = next_pid++;
current_task->esp = current_task->ebp = 0;
current_task->eip = 0;
current_task->page_directory = current_directory;
current_task->next = 0;
current_task->kernel_stack = kmalloc_a(KERNEL_STACK_SIZE);
// Reenable interrupts.
asm volatile("sti");
}
void move_stack(void *new_stack_start, u32int size)
{
u32int i;
// Allocate some space for the new stack.
for( i = (u32int)new_stack_start;
i >= ((u32int)new_stack_start-size);
i -= 0x1000)
{
// General-purpose stack is in user-mode.
alloc_frame( get_page(i, 1, current_directory), 0 /* User mode */, 1 /* Is writable */ );
}
// Flush the TLB by reading and writing the page directory address again.
u32int pd_addr;
asm volatile("mov %%cr3, %0" : "=r" (pd_addr));
asm volatile("mov %0, %%cr3" : : "r" (pd_addr));
// Old ESP and EBP, read from registers.
u32int old_stack_pointer; asm volatile("mov %%esp, %0" : "=r" (old_stack_pointer));
u32int old_base_pointer; asm volatile("mov %%ebp, %0" : "=r" (old_base_pointer));
// Offset to add to old stack addresses to get a new stack address.
u32int offset = (u32int)new_stack_start - initial_esp;
// New ESP and EBP.
u32int new_stack_pointer = old_stack_pointer + offset;
u32int new_base_pointer = old_base_pointer + offset;
// Copy the stack.
memcpy((void*)new_stack_pointer, (void*)old_stack_pointer, initial_esp-old_stack_pointer);
// Backtrace through the original stack, copying new values into
// the new stack.
for(i = (u32int)new_stack_start; i > (u32int)new_stack_start-size; i -= 4)
{
u32int tmp = * (u32int*)i;
// If the value of tmp is inside the range of the old stack, assume it is a base pointer
// and remap it. This will unfortunately remap ANY value in this range, whether they are
// base pointers or not.
if (( old_stack_pointer < tmp) && (tmp < initial_esp))
{
tmp = tmp + offset;
u32int *tmp2 = (u32int*)i;
*tmp2 = tmp;
}
}
// Change stacks.
asm volatile("mov %0, %%esp" : : "r" (new_stack_pointer));
asm volatile("mov %0, %%ebp" : : "r" (new_base_pointer));
}
void switch_task()
{
// If we haven't initialised tasking yet, just return.
if (!current_task)
return;
// Read esp, ebp now for saving later on.
u32int esp, ebp, eip;
asm volatile("mov %%esp, %0" : "=r"(esp));
asm volatile("mov %%ebp, %0" : "=r"(ebp));
// Read the instruction pointer. We do some cunning logic here:
// One of two things could have happened when this function exits -
// (a) We called the function and it returned the EIP as requested.
// (b) We have just switched tasks, and because the saved EIP is essentially
// the instruction after read_eip(), it will seem as if read_eip has just
// returned.
// In the second case we need to return immediately. To detect it we put a dummy
// value in EAX further down at the end of this function. As C returns values in EAX,
// it will look like the return value is this dummy value! (0x12345).
eip = read_eip();
// Have we just switched tasks?
if (eip == 0x12345)
return;
// No, we didn't switch tasks. Let's save some register values and switch.
current_task->eip = eip;
current_task->esp = esp;
current_task->ebp = ebp;
// Get the next task to run.
current_task = current_task->next;
// If we fell off the end of the linked list start again at the beginning.
if (!current_task) current_task = ready_queue;
eip = current_task->eip;
esp = current_task->esp;
ebp = current_task->ebp;
// Make sure the memory manager knows we've changed page directory.
current_directory = current_task->page_directory;
// Change our kernel stack over.
set_kernel_stack(current_task->kernel_stack+KERNEL_STACK_SIZE);
// Here we:
// * Stop interrupts so we don't get interrupted.
// * Temporarily put the new EIP location in ECX.
// * Load the stack and base pointers from the new task struct.
// * Change page directory to the physical address (physicalAddr) of the new directory.
// * Put a dummy value (0x12345) in EAX so that above we can recognise that we've just
// switched task.
// * Restart interrupts. The STI instruction has a delay - it doesn't take effect until after
// the next instruction.
// * Jump to the location in ECX (remember we put the new EIP in there).
asm volatile(" \
cli; \
mov %0, %%ecx; \
mov %1, %%esp; \
mov %2, %%ebp; \
mov %3, %%cr3; \
mov $0x12345, %%eax; \
sti; \
jmp *%%ecx "
: : "r"(eip), "r"(esp), "r"(ebp), "r"(current_directory->physicalAddr));
}
int fork()
{
// We are modifying kernel structures, and so cannot be interrupted.
asm volatile("cli");
monitor_write(">> interrupts disabled\n");
// Take a pointer to this process' task struct for later reference.
task_t *parent_task = (task_t*)current_task;
monitor_write(">> parent task\n");
// Clone the address space.
page_directory_t *directory = clone_directory(current_directory);
monitor_write(">> directory\n");
// Create a new process.
task_t *new_task = (task_t*)kmalloc(sizeof(task_t));
new_task->id = next_pid++;
new_task->esp = new_task->ebp = 0;
new_task->eip = 0;
new_task->page_directory = directory;
monitor_write(">> new task\n");
current_task->kernel_stack = kmalloc_a(KERNEL_STACK_SIZE);
new_task->next = 0;
monitor_write(">> kernel stack\n");
// Add it to the end of the ready queue.
// Find the end of the ready queue...
task_t *tmp_task = (task_t*)ready_queue;
while (tmp_task->next)
tmp_task = tmp_task->next;
// ...And extend it.
tmp_task->next = new_task;
// This will be the entry point for the new process.
u32int eip = read_eip();
// We could be the parent or the child here - check.
if (current_task == parent_task)
{
// We are the parent, so set up the esp/ebp/eip for our child.
u32int esp; asm volatile("mov %%esp, %0" : "=r"(esp));
u32int ebp; asm volatile("mov %%ebp, %0" : "=r"(ebp));
new_task->esp = esp;
new_task->ebp = ebp;
new_task->eip = eip;
// All finished: Reenable interrupts.
asm volatile("sti");
// And by convention return the PID of the child.
return new_task->id;
}
else
{
// We are the child - by convention return 0.
return 0;
}
}
int getpid()
{
return current_task->id;
}
void switch_to_user_mode()
{
// Set up our kernel stack.
set_kernel_stack(current_task->kernel_stack+KERNEL_STACK_SIZE);
// Set up a stack structure for switching to user mode.
asm volatile(" \
cli; \
mov $0x23, %ax; \
mov %ax, %ds; \
mov %ax, %es; \
mov %ax, %fs; \
mov %ax, %gs; \
\
\
mov %esp, %eax; \
pushl $0x23; \
pushl %esp; \
pushf; \
pushl $0x1B; \
push $1f; \
iret; \
1: \
");
}

41
user_mode/src/task.h Normal file
View file

@ -0,0 +1,41 @@
//
// task.h - Defines the structures and prototypes needed to multitask.
// Written for JamesM's kernel development tutorials.
//
#ifndef TASK_H
#define TASK_H
#include "common.h"
#include "paging.h"
#define KERNEL_STACK_SIZE 2048 // Use a 2kb kernel stack.
// This structure defines a 'task' - a process.
typedef struct task
{
int id; // Process ID.
u32int esp, ebp; // Stack and base pointers.
u32int eip; // Instruction pointer.
page_directory_t *page_directory; // Page directory.
u32int kernel_stack; // Kernel stack location.
struct task *next; // The next task in a linked list.
} task_t;
// Initialises the tasking system.
void initialise_tasking();
// Called by the timer hook, this changes the running process.
void task_switch();
// Forks the current process, spawning a new one with a different
// memory space.
int fork();
// Causes the current process' stack to be forcibly moved to a new location.
void move_stack(void *new_stack_start, u32int size);
// Returns the pid of the current process.
int getpid();
#endif

36
user_mode/src/timer.c Normal file
View file

@ -0,0 +1,36 @@
// timer.c -- Initialises the PIT, and handles clock updates.
// Written for JamesM's kernel development tutorials.
#include "timer.h"
#include "isr.h"
#include "monitor.h"
u32int tick = 0;
static void timer_callback(registers_t *regs)
{
tick++;
switch_task();
}
void init_timer(u32int frequency)
{
// Firstly, register our timer callback.
register_interrupt_handler(IRQ0, &timer_callback);
// The value we send to the PIT is the value to divide it's input clock
// (1193180 Hz) by, to get our required frequency. Important to note is
// that the divisor must be small enough to fit into 16-bits.
u32int divisor = 1193180 / frequency;
// Send the command byte.
outb(0x43, 0x36);
// Divisor has to be sent byte-wise, so split here into upper/lower bytes.
u8int l = (u8int)(divisor & 0xFF);
u8int h = (u8int)( (divisor>>8) & 0xFF );
// Send the frequency divisor.
outb(0x40, l);
outb(0x40, h);
}

11
user_mode/src/timer.h Normal file
View file

@ -0,0 +1,11 @@
// timer.h -- Defines the interface for all PIT-related functions.
// Written for JamesM's kernel development tutorials.
#ifndef TIMER_H
#define TIMER_H
#include "common.h"
void init_timer(u32int frequency);
#endif

1
user_mode/test.txt Normal file
View file

@ -0,0 +1 @@
Hello, VFS world!

1
user_mode/test2.txt Normal file
View file

@ -0,0 +1 @@
My filename is test2.txt!

8
user_mode/update_image.sh Executable file
View file

@ -0,0 +1,8 @@
#!/bin/bash
sudo /sbin/losetup /dev/loop0 floppy.img
sudo mount /dev/loop0 /mnt
sudo cp src/kernel /mnt/kernel
sudo cp initrd.img /mnt/initrd
sudo umount /dev/loop0
sudo /sbin/losetup -d /dev/loop0