diff --git a/user_mode/bochs.out b/user_mode/bochs.out new file mode 100644 index 0000000..6895457 --- /dev/null +++ b/user_mode/bochs.out @@ -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 diff --git a/user_mode/bochsrc b/user_mode/bochsrc new file mode 100644 index 0000000..7920a4d --- /dev/null +++ b/user_mode/bochsrc @@ -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 diff --git a/user_mode/bochsrc.txt b/user_mode/bochsrc.txt new file mode 100644 index 0000000..011fa44 --- /dev/null +++ b/user_mode/bochsrc.txt @@ -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 + diff --git a/user_mode/floppy.img b/user_mode/floppy.img new file mode 100644 index 0000000..27f38bb Binary files /dev/null and b/user_mode/floppy.img differ diff --git a/user_mode/go b/user_mode/go new file mode 100755 index 0000000..3ce37dc --- /dev/null +++ b/user_mode/go @@ -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 diff --git a/user_mode/initrd.img b/user_mode/initrd.img new file mode 100644 index 0000000..630c3c1 Binary files /dev/null and b/user_mode/initrd.img differ diff --git a/user_mode/make_initrd b/user_mode/make_initrd new file mode 100755 index 0000000..2e629e0 Binary files /dev/null and b/user_mode/make_initrd differ diff --git a/user_mode/make_initrd.c b/user_mode/make_initrd.c new file mode 100644 index 0000000..af93d89 --- /dev/null +++ b/user_mode/make_initrd.c @@ -0,0 +1,58 @@ +#include + +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; +} diff --git a/user_mode/run_bochs.sh b/user_mode/run_bochs.sh new file mode 100755 index 0000000..c3fc32a --- /dev/null +++ b/user_mode/run_bochs.sh @@ -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 diff --git a/user_mode/src/Makefile b/user_mode/src/Makefile new file mode 100644 index 0000000..c748567 --- /dev/null +++ b/user_mode/src/Makefile @@ -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) $< diff --git a/user_mode/src/boot.s b/user_mode/src/boot.s new file mode 100644 index 0000000..e9b6d12 --- /dev/null +++ b/user_mode/src/boot.s @@ -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! diff --git a/user_mode/src/common.c b/user_mode/src/common.c new file mode 100644 index 0000000..445aa07 --- /dev/null +++ b/user_mode/src/common.c @@ -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(;;); +} diff --git a/user_mode/src/common.h b/user_mode/src/common.h new file mode 100644 index 0000000..20b003e --- /dev/null +++ b/user_mode/src/common.h @@ -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 diff --git a/user_mode/src/descriptor_tables.c b/user_mode/src/descriptor_tables.c new file mode 100644 index 0000000..71fe778 --- /dev/null +++ b/user_mode/src/descriptor_tables.c @@ -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; +} diff --git a/user_mode/src/descriptor_tables.h b/user_mode/src/descriptor_tables.h new file mode 100644 index 0000000..ea3f74f --- /dev/null +++ b/user_mode/src/descriptor_tables.h @@ -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 diff --git a/user_mode/src/fs.c b/user_mode/src/fs.c new file mode 100644 index 0000000..a9e88b3 --- /dev/null +++ b/user_mode/src/fs.c @@ -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; +} diff --git a/user_mode/src/fs.h b/user_mode/src/fs.h new file mode 100644 index 0000000..ba9f38b --- /dev/null +++ b/user_mode/src/fs.h @@ -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 diff --git a/user_mode/src/gdt.s b/user_mode/src/gdt.s new file mode 100644 index 0000000..1b507ec --- /dev/null +++ b/user_mode/src/gdt.s @@ -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 diff --git a/user_mode/src/initrd.c b/user_mode/src/initrd.c new file mode 100644 index 0000000..3bf2a7c --- /dev/null +++ b/user_mode/src/initrd.c @@ -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; +} diff --git a/user_mode/src/initrd.h b/user_mode/src/initrd.h new file mode 100644 index 0000000..3d0d28b --- /dev/null +++ b/user_mode/src/initrd.h @@ -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 diff --git a/user_mode/src/interrupt.s b/user_mode/src/interrupt.s new file mode 100644 index 0000000..35a7cca --- /dev/null +++ b/user_mode/src/interrupt.s @@ -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 + + + diff --git a/user_mode/src/isr.c b/user_mode/src/isr.c new file mode 100644 index 0000000..41cdadc --- /dev/null +++ b/user_mode/src/isr.c @@ -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); + } + +} diff --git a/user_mode/src/isr.h b/user_mode/src/isr.h new file mode 100644 index 0000000..c7a5630 --- /dev/null +++ b/user_mode/src/isr.h @@ -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); diff --git a/user_mode/src/kernel b/user_mode/src/kernel new file mode 100755 index 0000000..911ed4f Binary files /dev/null and b/user_mode/src/kernel differ diff --git a/user_mode/src/kheap.c b/user_mode/src/kheap.c new file mode 100644 index 0000000..dde7d50 --- /dev/null +++ b/user_mode/src/kheap.c @@ -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); + +} diff --git a/user_mode/src/kheap.h b/user_mode/src/kheap.h new file mode 100644 index 0000000..eb6819f --- /dev/null +++ b/user_mode/src/kheap.h @@ -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 diff --git a/user_mode/src/link.ld b/user_mode/src/link.ld new file mode 100644 index 0000000..bbb2ee0 --- /dev/null +++ b/user_mode/src/link.ld @@ -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 = .; +} diff --git a/user_mode/src/main.c b/user_mode/src/main.c new file mode 100644 index 0000000..ac58871 --- /dev/null +++ b/user_mode/src/main.c @@ -0,0 +1,98 @@ +// main.c -- Defines the C-code kernel entry point, calls initialisation routines. +// Made for JamesM's tutorials + +#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; +} diff --git a/user_mode/src/monitor.c b/user_mode/src/monitor.c new file mode 100644 index 0000000..5bbc24e --- /dev/null +++ b/user_mode/src/monitor.c @@ -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); + +} diff --git a/user_mode/src/monitor.h b/user_mode/src/monitor.h new file mode 100644 index 0000000..7b95771 --- /dev/null +++ b/user_mode/src/monitor.h @@ -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 diff --git a/user_mode/src/multiboot.h b/user_mode/src/multiboot.h new file mode 100644 index 0000000..69b327f --- /dev/null +++ b/user_mode/src/multiboot.h @@ -0,0 +1,51 @@ +// multiboot.h -- Declares the multiboot info structure. +// Made for JamesM's tutorials + +#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 diff --git a/user_mode/src/ordered_array.c b/user_mode/src/ordered_array.c new file mode 100644 index 0000000..7ed6639 --- /dev/null +++ b/user_mode/src/ordered_array.c @@ -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 (aarray); +} + +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--; +} diff --git a/user_mode/src/ordered_array.h b/user_mode/src/ordered_array.h new file mode 100644 index 0000000..bb2b8f4 --- /dev/null +++ b/user_mode/src/ordered_array.h @@ -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 diff --git a/user_mode/src/paging.c b/user_mode/src/paging.c new file mode 100755 index 0000000..32b02a3 --- /dev/null +++ b/user_mode/src/paging.c @@ -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; +} diff --git a/user_mode/src/paging.h b/user_mode/src/paging.h new file mode 100644 index 0000000..e324ae5 --- /dev/null +++ b/user_mode/src/paging.h @@ -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 diff --git a/user_mode/src/process.s b/user_mode/src/process.s new file mode 100644 index 0000000..75ba553 --- /dev/null +++ b/user_mode/src/process.s @@ -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 diff --git a/user_mode/src/syscall.c b/user_mode/src/syscall.c new file mode 100644 index 0000000..be946f6 --- /dev/null +++ b/user_mode/src/syscall.c @@ -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; +} diff --git a/user_mode/src/syscall.h b/user_mode/src/syscall.h new file mode 100644 index 0000000..ac59b0b --- /dev/null +++ b/user_mode/src/syscall.h @@ -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 diff --git a/user_mode/src/task.c b/user_mode/src/task.c new file mode 100644 index 0000000..a6f4fed --- /dev/null +++ b/user_mode/src/task.c @@ -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: \ + "); + +} diff --git a/user_mode/src/task.h b/user_mode/src/task.h new file mode 100644 index 0000000..bcd440b --- /dev/null +++ b/user_mode/src/task.h @@ -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 diff --git a/user_mode/src/timer.c b/user_mode/src/timer.c new file mode 100644 index 0000000..9f5be23 --- /dev/null +++ b/user_mode/src/timer.c @@ -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); +} diff --git a/user_mode/src/timer.h b/user_mode/src/timer.h new file mode 100644 index 0000000..2aa1af5 --- /dev/null +++ b/user_mode/src/timer.h @@ -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 diff --git a/user_mode/test.txt b/user_mode/test.txt new file mode 100644 index 0000000..00d165e --- /dev/null +++ b/user_mode/test.txt @@ -0,0 +1 @@ +Hello, VFS world! \ No newline at end of file diff --git a/user_mode/test2.txt b/user_mode/test2.txt new file mode 100644 index 0000000..04ab8dd --- /dev/null +++ b/user_mode/test2.txt @@ -0,0 +1 @@ +My filename is test2.txt! \ No newline at end of file diff --git a/user_mode/update_image.sh b/user_mode/update_image.sh new file mode 100755 index 0000000..7fbf505 --- /dev/null +++ b/user_mode/update_image.sh @@ -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