mirror of
https://github.com/samsonjs/kernel.git
synced 2026-03-25 09:25:46 +00:00
(broken) user mode support
This commit is contained in:
parent
fb0358197b
commit
f0b2ebefea
45 changed files with 3387 additions and 0 deletions
220
user_mode/bochs.out
Normal file
220
user_mode/bochs.out
Normal 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
9
user_mode/bochsrc
Normal 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
10
user_mode/bochsrc.txt
Normal 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
BIN
user_mode/floppy.img
Normal file
Binary file not shown.
11
user_mode/go
Executable file
11
user_mode/go
Executable 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
BIN
user_mode/initrd.img
Normal file
Binary file not shown.
BIN
user_mode/make_initrd
Executable file
BIN
user_mode/make_initrd
Executable file
Binary file not shown.
58
user_mode/make_initrd.c
Normal file
58
user_mode/make_initrd.c
Normal 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
8
user_mode/run_bochs.sh
Executable 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
22
user_mode/src/Makefile
Normal 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
47
user_mode/src/boot.s
Normal 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
129
user_mode/src/common.c
Normal 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
26
user_mode/src/common.h
Normal 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
|
||||
187
user_mode/src/descriptor_tables.c
Normal file
187
user_mode/src/descriptor_tables.c
Normal 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;
|
||||
}
|
||||
154
user_mode/src/descriptor_tables.h
Normal file
154
user_mode/src/descriptor_tables.h
Normal 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
58
user_mode/src/fs.c
Normal 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
65
user_mode/src/fs.h
Normal 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
37
user_mode/src/gdt.s
Normal 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
117
user_mode/src/initrd.c
Normal 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
27
user_mode/src/initrd.h
Normal 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
151
user_mode/src/interrupt.s
Normal 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
58
user_mode/src/isr.c
Normal 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(®s);
|
||||
}
|
||||
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(®s);
|
||||
}
|
||||
|
||||
}
|
||||
39
user_mode/src/isr.h
Normal file
39
user_mode/src/isr.h
Normal 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
BIN
user_mode/src/kernel
Executable file
Binary file not shown.
408
user_mode/src/kheap.c
Normal file
408
user_mode/src/kheap.c
Normal 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
100
user_mode/src/kheap.h
Normal 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
33
user_mode/src/link.ld
Normal 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
98
user_mode/src/main.c
Normal 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
216
user_mode/src/monitor.c
Normal 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
24
user_mode/src/monitor.h
Normal 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
51
user_mode/src/multiboot.h
Normal 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
|
||||
76
user_mode/src/ordered_array.c
Normal file
76
user_mode/src/ordered_array.c
Normal 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--;
|
||||
}
|
||||
59
user_mode/src/ordered_array.h
Normal file
59
user_mode/src/ordered_array.h
Normal 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
297
user_mode/src/paging.c
Executable 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
75
user_mode/src/paging.h
Normal 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
37
user_mode/src/process.s
Normal 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
57
user_mode/src/syscall.c
Normal 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
70
user_mode/src/syscall.h
Normal 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
255
user_mode/src/task.c
Normal 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
41
user_mode/src/task.h
Normal 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
36
user_mode/src/timer.c
Normal 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
11
user_mode/src/timer.h
Normal 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
1
user_mode/test.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
Hello, VFS world!
|
||||
1
user_mode/test2.txt
Normal file
1
user_mode/test2.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
My filename is test2.txt!
|
||||
8
user_mode/update_image.sh
Executable file
8
user_mode/update_image.sh
Executable 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
|
||||
Loading…
Reference in a new issue