From: Scott Gasch Date: Thu, 2 Jun 2016 02:37:09 +0000 (-0700) Subject: Initial checkin of toy OS project. X-Git-Url: https://wannabe.guru.org/gitweb/?a=commitdiff_plain;h=HEAD;p=os.git Initial checkin of toy OS project. --- 0a41dae5f406d498b5c9ab1542cb5660e0d982f6 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ca5c681 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +SUBDIR += boot0 +SUBDIR += boot1 +SUBDIR += kernel + +.include diff --git a/boot0/Makefile b/boot0/Makefile new file mode 100755 index 0000000..162c810 --- /dev/null +++ b/boot0/Makefile @@ -0,0 +1,9 @@ +RM = /bin/rm +TARGET = boot0.bin +SOURCES = boot0.asm + +all: $(SOURCES) + nasm -f bin -o $(TARGET) $(SOURCES) ; \ + mformat -f 1440 -B boot0.bin Q: +clean: + $(RM) -f *.log *.core *~ *.gmon \#*\# $(TARGET) diff --git a/boot0/boot0.asm b/boot0/boot0.asm new file mode 100644 index 0000000..e31f126 --- /dev/null +++ b/boot0/boot0.asm @@ -0,0 +1,347 @@ +; +; This boot sector was based on VnutZ's example available online +; at http://www.geocities.com/mvea/bootstrap.htm. It also made use +; of info at http://support.microsoft.com/support/kb/articles/Q140/4/18.asp +; and the source code for geekos. +; +; It searches the root directory of a floppy for two files: boot.com and +; kernel.bin. If either is missing the boot fails. If they are found, they +; are loaded into memory at BOOT1SEG:BOOT1OFF and KERNSEG:KERNOFF. Then +; control is passed to the next stage bootloader, boot.com. +; +%include "../inc/defs.asm" + +[BITS 16] +[ORG 0] + +START: + jmp AROUND_DATA + +szOEM_ID db "SCOTTOS", 0 ; 8 bytes, null not required +wBytesPerSector dw 0x0200 ; bytes per sector +bSectorsPerCluster db 0x01 ; sectors per cluster +wReservedSectors dw 0x0001 ; reserved sectors (boot sector) +bTotalFATs db 0x02 ; num copies of the FAT +wMaxRootEntries dw 0x00E0 ; max num entries in root directory +wTotalSectorsSmall dw 0x0B40 ; +bMediaDescriptor db 0xF0 ; +wSectorsPerFAT dw 0x0009 ; length of single FAT copy in sectors +wSectorsPerTrack dw 0x0012 ; sectors per track +wNumHeads dw 0x0002 ; num heads +dwHiddenSectors dd 0x00000000 ; num hidden sectors +dwTotalSectorsLarge dd 0x00000000 ; +bDriveNumber db 0x00 ; 0x00=A: 0x01=B: 0x80=C: ... +bFlags db 0x00 ; +bSignature db 0x29 ; +dwVolumeID dd 0xFFFFFFFF ; +szVolumeLabel db "SCOTT BOOT", 0 ; 11 bytes, null not required +szSystemID db "FAT12 " ; + +AROUND_DATA: +;;; setup segment registers, this code is located at 7C00:0000 + mov dx, BOOT0SEG ; 7C00 + mov ds, dx + mov es, dx + +;;; create a temporary stack at the top of segment 7C00 + mov ss, dx + mov sp, 0xFFFF ; top of this segment + +LOAD_ROOT_DIRECTORY: +;;; +;;; Compute the length (in number of disk sectors) of the floppy's +;;; root directory information: +;;; +;;; wNumRootDirectorySectors = sizeof(directory_entry) * +;;; wMaxRootEntries / wBytesPerSector +;;; +;;; Store this in cx and in the wNumRootDirectorySectors identifier. +;;; + mov ax, 0x0020 ; sizeof(directory_entry) + mul WORD [wMaxRootEntries] + div WORD [wBytesPerSector] + mov cx, ax ; count of num sectors to read for dir + +;;; +;;; Compute the starting location of the root directory (sector number): +;;; +;;; wRootDirectoryStartSector = wReservedSectors + +;;; (bTotalFATs * wSectorsPerFAT) +;;; + mov al, BYTE [bTotalFATs] + mul WORD [wSectorsPerFAT] + add ax, WORD [wReservedSectors] + +;;; +;;; Compute the first sector after the root directory -- this is the +;;; sector where real data starts on the floppy. +;;; + mov WORD [wDriveDataStartSector], ax + add WORD [wDriveDataStartSector], cx + +;;; +;;; Read the floppy's root directory into memory at BOOT0SEG:0200 by +;;; calling ReadSectors. This function reads cx count (wNumRootDirSectors) +;;; starting at sector ax (wRootDirStartSector) into memory at es:bx +;;; + mov bx, 0x0200 + call ReadSectors + +SEARCH_ROOT_DIRECTORY: +;;; +;;; Search the root directory for the stage two loader and the kernel. +;;; + mov cx, WORD [wMaxRootEntries] + mov di, 0x0200 ; offset of first directory entry +.LOOP: + push cx ; save current entry count + mov cx, 0x000B ; 11 char name + mov si, szStage2Image ; name we are seeking + push di ; save current entry ptr + rep cmpsb ; test for match + pop di ; restore entry ptr + jne .TRY_KERN ; match? if not see if it matches the kernel + mov dx, WORD [di+1Ah] ; match. save the starting cluster + mov WORD [wStage2Cluster], dx +.TRY_KERN: + mov cx, 0x000B ; 11 char name + mov si, szKernelImage ; name we are seeking + push di ; save current entry ptr + rep cmpsb ; test for match + pop di ; restore entry ptr + jne .NEXT ; match? if not next entry + mov dx, WORD [di+1Ah] ; match. save the starting cluster + mov WORD [wKernelCluster], dx +.NEXT: + pop cx ; cx = entry count again + add di, 0x0020 ; next directory entry (+32 bytes later) + loop .LOOP ; cx -= 1, jmp .LOOP if cx != 0 + +.DONE: +;;; +;;; Did we find both the kernel and the second stage boot loader? If not +;;; give them a "missing operating system" message. +;;; + mov ax, [wStage2Cluster] + test ax, 0xFFFF + jz .NOOS + mov ax, [wKernelCluster] + test ax, 0xFFFF + jnz LOAD_FAT +.NOOS: + mov si, szNoOperatingSystem + call Write +FAIL: mov ah, 0x00 ; wait for a keystroke and reboot + int 0x16 + int 0x19 + +LOAD_FAT: +;;; +;;; Prepare the load the FAT into memory. Compute the size of the FAT (in +;;; number of sectors) and store in cx for a call to ReadSectors. +;;; + xor ax, ax + mov al, BYTE [bTotalFATs] + mul WORD [wSectorsPerFAT] + mov cx, ax + +;;; +;;; Compute location of FAT and store in ax +;;; + mov ax, WORD [wReservedSectors] + +;;; +;;; Read the FAT into memory at BOOT0SEG:0200. This overwrites the root +;;; directory information but we already have the cluster number for the +;;; stage two boot loader and the kernel. +;;; + mov bx, 0x0200 + call ReadSectors + +;;; +;;; Prepare to read the stage two boot loader into memory at +;;; BOOT1SEG:BOOT1OFF +;;; + ;; point es:bx at where we want the stage two loader read into memory + mov ax, BOOT1SEG + mov es, ax ; destination for image + mov bx, BOOT1OFF ; destination for image + + ;; use the starting cluster we found by reading the directory + mov ax, WORD [wStage2Cluster] + mov WORD [wCluster], ax + + ;; call LOAD_IMAGE to read in the file + call LOAD_IMAGE + +;;; +;;; Prepare to read the kernel into memory at KERNSEG:KERNOFF +;;; + ;; point es:bx at where we want the kernel read into memory + mov ax, KERNSEG + mov es, ax + mov bx, KERNOFF + + ;; use the starting cluster we found by reading the directory + mov ax, WORD [wKernelCluster] + mov WORD [wCluster], ax + + ;; call LOAD_IMAGE to read in the file + call LOAD_IMAGE + +DONE: +;;; +;;; We're done, transfer control to the second stage loader which we +;;; put at BOOT1SEG:BOOT1OFF. +;;; + push WORD BOOT1SEG + push WORD BOOT1OFF + retf + +;; ************************************************************************* +;; PROCEDURE ClusterLBA +;; convert FAT cluster into LBA addressing scheme +;; LBA = (cluster - 2) * sectors per cluster +;; ************************************************************************* +ClusterLBA: + sub ax, 0x0002 ; cluster number is zero-based + xor cx, cx + mov cl, BYTE [bSectorsPerCluster]; convert byte to word + mul cx + add ax, WORD [wDriveDataStartSector]; base data sector + ret + +;;; ************************************************************************* +;;; PROCEDURE LBACHS +;;; +;;; convert ax LBA addressing scheme to CHS addressing scheme using these +;;; formulas: +;;; +;;; absolute sector = (logical sector / sectors per track) + 1 +;;; absolute head = (logical sector / sectors per track) MOD number of heads +;;; absolute track = logical sector / (sectors per track * number of heads) +;;; ************************************************************************* +LBACHS: + xor dx, dx + div WORD [wSectorsPerTrack] + inc dl ; adjust for sector 0 + mov BYTE [bAbsoluteSector], dl + xor dx, dx ; prepare dx:ax for operation + div WORD [wNumHeads] + mov BYTE [bAbsoluteHead], dl + mov BYTE [bAbsoluteTrack], al + ret + +;;; ************************************************************************* +;;; PROCEDURE ReadSectors +;;; +;;; reads disk sectors ax..ax+cx from the disk into memory at es:bx +;;; +;;; ************************************************************************* +ReadSectors: +.MAIN + mov di, 5 ; five retries for disk errors +.SECTORLOOP + push ax + push bx + push cx + call LBACHS ; convert LBA sector in ax into absolute CHT + mov ah, 0x02 ; BIOS read sector + mov al, 0x01 ; read one sector + mov ch, BYTE [bAbsoluteTrack] + mov cl, BYTE [bAbsoluteSector] + mov dh, BYTE [bAbsoluteHead] + mov dl, BYTE [bDriveNumber] + int 0x13 + jnc .SUCCESS ; CF=1 on read error + + ;; read error, reset the disk and try again + xor ax, ax ; BIOS reset disk + int 0x13 + dec di ; error counter -= 1 + pop cx ; restore registers + pop bx + pop ax + jnz .SECTORLOOP ; if error counter != 0, try to read again + + ;; give up + mov si, szReadError + call Write + jmp FAIL + + ;; sector read succeeded +.SUCCESS + pop cx ; restore registers + pop bx + pop ax + inc ax ; next sector + + ;;; bx += wBytesPerSector + add bx, WORD [wBytesPerSector] + loop .MAIN ; cx -= 1, jmp .MAIN if cx != 0 + ret + +;;; ************************************************************************* +;;; PROCEDURE Write +;;; +;;; display ASCIIZ string at ds:si via BIOS +;;; ************************************************************************* +Write: + cld ; make sure direction flag is correct + lodsb ; load char to al, increment si + or al, al ; see if it's null + jz .DONE ; if so, we're finished + mov ah, 0x0E ; service 0x0E: output one char + mov bh, 0x00 ; display page 0 + mov bl, 0x07 ; char text attrib (white on black) + int 0x10 ; invoke bios + jmp Write ; next char +.DONE: + ret + +LOAD_IMAGE: + mov ax, WORD [wCluster] ; cluster to read + call ClusterLBA ; convert cluster to LBA + xor cx, cx + mov cl, BYTE [bSectorsPerCluster] + call ReadSectors ; this increments bx + + ;; compute next cluster + mov ax, WORD [wCluster] ; identify current cluster + mov cx, ax ; copy current cluster + mov dx, ax ; copy current cluster + shr dx, 0x0001 ; dx = wCluster / 2 + add cx, dx ; cx = wCluster + (wCluster / 2) + push bx + mov bx, 0x0200 ; location of FAT in memory + add bx, cx ; index into FAT + mov dx, WORD [bx] ; read two bytes from FAT + pop bx + test ax, 0x0001 ; is the cluster even or odd? + jnz .ODD +.EVEN: + and dx, 0x0FFF ; take low twelve bits + jmp .DONE +.ODD: + shr dx, 0x0004 ; take high twelve bits +.DONE: + mov WORD [wCluster], dx ; store new cluster + cmp dx, 0x0FF0 ; test for end of file + jb LOAD_IMAGE ; if not, go get the next one + ret + +wDriveDataStartSector dw 0x0000 +szStage2Image db "BOOT COM", 0 +wStage2Cluster dw 0x0000 +szKernelImage db "KERNEL BIN", 0 +wKernelCluster dw 0x0000 +szNoOperatingSystem db "Missing OS File", 13, 10, 0 +szReadError db "Disk read error", 13, 10, 0 +wCluster dw 0x0000 +bAbsoluteSector db 0x00 +bAbsoluteHead db 0x00 +bAbsoluteTrack db 0x00 +szProgress db ".", 0 + + TIMES 510-($-$$) DB 0 ; 0 padding + DW 0xAA55 ; bootsector signature diff --git a/boot0/boot0.bin b/boot0/boot0.bin new file mode 100644 index 0000000..764f7a6 Binary files /dev/null and b/boot0/boot0.bin differ diff --git a/boot1/Makefile b/boot1/Makefile new file mode 100755 index 0000000..5606866 --- /dev/null +++ b/boot1/Makefile @@ -0,0 +1,14 @@ +NASM= nasm +NASMFLAGS= -f bin -i../inc +RM= /bin/rm +TARGET= boot.com +SOURCES= boot1.asm +SUPPORT= video.asm hardware.asm + +all: $(SOURCES) $(SUPPORT) + $(NASM) $(NASMFLAGS) $(SOURCES) -o $(TARGET) ; \ + mcopy -o $(TARGET) Q:\ + +clean: + $(RM) -f *.log *.core *~ *.gmon \#*\# $(TARGET) ; \ + mdel Q:\$(TARGET) diff --git a/boot1/boot.com b/boot1/boot.com new file mode 100644 index 0000000..663284c Binary files /dev/null and b/boot1/boot.com differ diff --git a/boot1/boot1.asm b/boot1/boot1.asm new file mode 100644 index 0000000..59c0dd0 --- /dev/null +++ b/boot1/boot1.asm @@ -0,0 +1,113 @@ +[BITS 16] +[ORG 0] + +START: + ;; initialize segments + mov ax, BOOT1SEG + mov ds, ax + mov es, ax + mov ss, ax + + ;; make sure we are running on a 386+ + + ;; while we're still in real mode, detect system hardware via BIOS + call PROBE_VIDEO + call GET_EQUIPMENT_LIST + call GET_MEMORY_SIZE + call GET_TIME + + ;; switch the processor into protected mode, keep ints disabled + cli + lidt [IDT_Pointer] + lgdt [GDT_Pointer] + call ENABLE_A20 + mov ax, 0x01 + lmsw ax + + ;; Jump to 32 bit code, this completes the transition to protected mode + db 0x66 ; operand size override prefix + db 0xea ; jmp opcode + dd (BOOT1SEG<<4) + setup_32 ; want to jump here + dw (1<<3) ; ...in the kernel code segment + +[BITS 32] +setup_32: + ;; set up data segment registers + mov ax, (2<<3) + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + ;; create an initial stack + mov esp, KERN_STACK + 4096 + mov ebp, esp + + ;; create a stack frame, pass ptr to hardware block as param + mov eax, (BOOT1SEG<<4)+BIOS_HARDWARE_BLOCK + push eax + push (BOOT1SEG<<4)+.HANG + jmp (1<<3):0x00010000 ; entry point of kernel + +.HANG: + nop + jmp .HANG + + +; Setup data +; ---------------------------------------------------------------------- + +BIOS_HARDWARE_BLOCK: +bDisplayType db 0x00 +bFontSize db 0x00 +wEquipmentBitvector dw 0x0000 +wMemorySize dw 0x0000 +wSystemClock dw 0x0000 + +NUM_GDT_ENTRIES equ 3 ; number of entries in GDT +GDT_ENTRY_SZ equ 8 ; size of a single GDT entry + +align 8, db 0 +GDT: + ; Descriptor 0 is not used + dw 0 + dw 0 + dw 0 + dw 0 + + ; Descriptor 1: kernel code segment + dw 0xFFFF ; bytes 0 and 1 of segment size + dw 0x0000 ; bytes 0 and 1 of segment base address + db 0x00 ; byte 2 of segment base address + db 0x9A ; present, DPL=0, non-system, code, non-conforming, + ; readable, not accessed + db 0xCF ; granularity=page, 32 bit code, upper nibble of size + db 0x00 ; byte 3 of segment base address + + ; Descriptor 2: kernel data and stack segment + ; NOTE: what Intel calls an "expand-up" segment + ; actually means that the stack will grow DOWN, + ; towards lower memory. So, we can use this descriptor + ; for both data and stack references. + dw 0xFFFF ; bytes 0 and 1 of segment size + dw 0x0000 ; bytes 0 and 1 of segment base address + db 0x00 ; byte 2 of segment base address + db 0x92 ; present, DPL=0, non-system, data, expand-up, + ; writable, not accessed + db 0xCF ; granularity=page, big, upper nibble of size + db 0x00 ; byte 3 of segment base address + +GDT_Pointer: + dw NUM_GDT_ENTRIES*GDT_ENTRY_SZ ; limit + dd (BOOT1SEG<<4) + GDT ; base address + +IDT_Pointer: + dw 0 + dd 00 + +%include "defs.asm" +%include "hardware.asm" +%include "video.asm" + + \ No newline at end of file diff --git a/boot1/hardware.asm b/boot1/hardware.asm new file mode 100644 index 0000000..b7ac3af --- /dev/null +++ b/boot1/hardware.asm @@ -0,0 +1,42 @@ +[BITS 16] +;;; Call BIOS to populate equipment bitv in the HARDWARE buffer. +GET_EQUIPMENT_LIST: + xor ax, ax + int 0x11 + mov [wEquipmentBitvector], ax + ret + +;;; Call BIOS to get the system memory size. +GET_MEMORY_SIZE: + xor ax, ax + mov ah, 0x88 + int 0x15 + add ax, 1024 + mov [wMemorySize], ax + ret + +GET_TIME: + xor ax, ax + int 0x1a + push es + push si + mov es, cx + mov si, dx + mov cx, WORD [es:si] + mov [wSystemClock], cx + pop si + pop es + ret + +; Enable the A20 address line, so we can correctly address +; memory above 1MB. +ENABLE_A20: + mov al, 0xD1 + out 0x64, al + nop + jmp .here +.here: mov al, 0xDF + out 0x60, al + nop + jmp .there +.there: ret diff --git a/boot1/memory.asm b/boot1/memory.asm new file mode 100644 index 0000000..d834825 --- /dev/null +++ b/boot1/memory.asm @@ -0,0 +1,3 @@ +;;; +SIZE_MEMORY: + \ No newline at end of file diff --git a/boot1/video.asm b/boot1/video.asm new file mode 100644 index 0000000..61ff153 --- /dev/null +++ b/boot1/video.asm @@ -0,0 +1,81 @@ +;;; Display the ASCIIZ string at ds:si via BIOS VIDEO call (real mode only) +Write: + lodsb ; load char to al, increment si + or al, al ; see if it's null + jz .DONE ; if so, we're finished + mov ah, 0x0E ; service 0x0E: output one char + mov bh, 0x00 ; display page 0 + mov bl, 0x07 ; char text attrib (white on black) + int 0x10 ; invoke bios + jmp Write ; next char +.DONE: + ret + +;;; Probe the video system of the machine via BIOS VIDEO calls, populate +;;; the HARDWARE_INFO buffer (real mode only). +PROBE_VIDEO: + ;; identify the display type + call DISP_ID + mov [bDisplayType], al + + ;; determine the font size + call FONT_SIZE + mov [bFontSize], al + ret + +;;; Determine the font size (width in pixels) of the current display. +FONT_SIZE: + cmp al, 0x0a + jl .TRY_OLD + mov al, BYTE 16 + ret +.TRY_OLD: + cmp al, BYTE 1 + jne .TRY_CGA + mov al, BYTE 14 + ret +.TRY_CGA: + cmp al, BYTE 2 + jne .TRY_VGA + mov al, BYTE 8 + ret +.TRY_VGA: + mov ah, 0x11 + mov al, 0x30 + mov bh, 0 + int 0x10 + mov al, cl + ret + +;;; Determine the video type via BIOS VIDEO call +DISP_ID: + xor ax, ax + mov ah, 0x1a + int 0x10 + cmp al, 0x1a + jne .TRY_EGA + mov al, bl + ret +.TRY_EGA: + mov ah, 0x12 + mov bx, 0x10 + int 0x10 + cmp bx, 0x10 + je .OLD_BOARDS + cmp bh, 0 + je .EGA_COLOR + mov al, 5 + ret +.EGA_COLOR: + mov al, 4 + ret +.OLD_BOARDS: + int 0x11 + and al, 0x30 + cmp al, 0x30 + jne .CGA + mov al, 1 + ret +.CGA: + mov al, 2 + ret diff --git a/floppy.img b/floppy.img new file mode 100644 index 0000000..fb06de4 Binary files /dev/null and b/floppy.img differ diff --git a/inc/defs.asm b/inc/defs.asm new file mode 100644 index 0000000..e022a89 --- /dev/null +++ b/inc/defs.asm @@ -0,0 +1,33 @@ +BOOT0SEG equ 0x07C0 +BOOT0OFF equ 0x0000 + +BOOT1SEG equ 0x0100 +BOOT1OFF equ 0x0000 + +KERNSEG equ 0x1000 +KERNOFF equ 0x0000 + +VIDEOSEG equ 0xB800 + +; 8259A PIC initialization codes. +; Source: Linux bootsect.S, and Intel 8259A datasheet + +; The most important reason why we reprogram the PICs is to +; route the hardware interrupts through vectors *above* +; those reserved by Intel. The BIOS (for historical reasons :-) +; routes them such that they conflict with internal processor-generated +; interrupts. + +ICW1 equ 0x11 ; ICW1 - ICW4 needed, cascade mode, interval=8, + ; edge triggered. (I think interval is irrelevant + ; for x86.) +ICW2_MASTER equ 0x20 ; put IRQs 0-7 at 0x20 (above Intel reserved ints) +ICW2_SLAVE equ 0x28 ; put IRQs 8-15 at 0x28 +ICW3_MASTER equ 0x04 ; IR2 connected to slave +ICW3_SLAVE equ 0x02 ; slave has id 2 +ICW4 equ 0x01 ; 8086 mode, no auto-EOI, non-buffered mode, + ; not special fully nested mode + +KERN_THREAD_OBJ equ (1024*1024) +KERN_STACK equ KERN_THREAD_OBJ + 4096 + diff --git a/kernel/Makefile b/kernel/Makefile new file mode 100644 index 0000000..b5838d6 --- /dev/null +++ b/kernel/Makefile @@ -0,0 +1,30 @@ +DEBUGFLAGS=-DDEBUG + +SUBDIR += init +SUBDIR += hal +SUBDIR += rtl + +.include + +OBJCOPY= objcopy +OBJCOPY_FLAGS= -R .dynamic -R .note -R .comment +OBJS= init/entry.o init/main.o hal/video.o hal/io.o \ + rtl/string.o rtl/memory.o hal/intsupport.o hal/interrupts.o \ + hal/timer.o hal/keyboard.o hal/timestamp.o hal/counters.o +RM= /bin/rm + +all: kernel.bin + +kernel.bin: kernel.tmp + $(OBJCOPY) $(OBJCOPY_FLAGS) -S -O binary ./kernel.tmp kernel.bin ; \ + mcopy -o ./kernel.bin Q: ; \ + mcopy -o ./kernel.map Q: ; \ + $(RM) -f kernel.tmp + +kernel.tmp: $(OBJS) + $(LD) -M -o kernel.tmp -Ttext 0x00010000 -e StartOfKernelImage $(OBJS) \ + > kernel.map + +clean: + $(RM) -f $(TARGET) $(OBJS) *~ #*# .#* ; \ + mdel Q:\$(TARGET) diff --git a/kernel/defines b/kernel/defines new file mode 100644 index 0000000..dcee875 --- /dev/null +++ b/kernel/defines @@ -0,0 +1,6 @@ +// +// Symbols defined here are globally defined in all Makefiles below +// here. +// +#define KERNEL +#define DEBUG \ No newline at end of file diff --git a/kernel/driver/keyboard.c b/kernel/driver/keyboard.c new file mode 100644 index 0000000..7410171 --- /dev/null +++ b/kernel/driver/keyboard.c @@ -0,0 +1,37 @@ +//+---------------------------------------------------------------------------- +// +// File: keyboard.c +// +// Module: +// +// Synopsis: +// +// Created: sgasch 21 Oct 2003 +// +//+---------------------------------------------------------------------------- + +#include "kernel.h" +#include "hal.h" +#include "rtl.h" +#include "interrupts.h" + +void +HalKeyboardInt(INTERRUPT_STATE *is) +{ + BYTE c; + BYTE k; + + HalDisableInterrupts(); + + c = in(0x64); + HalIoDelay(); + if (c & 1) + { + k = in(0x60); + HalIoDelay(); + HalVideoPrint("Testing %x.\n\0", 1); + } + + HalEnableInterrupts(); +} + diff --git a/kernel/driver/video.c b/kernel/driver/video.c new file mode 100644 index 0000000..a63331b --- /dev/null +++ b/kernel/driver/video.c @@ -0,0 +1,503 @@ +//+---------------------------------------------------------------------------- +// +// File: video.c +// +// Module: Video driver +// +// Synopsis: low-level interface to video memory +// +// Created: sgasch 5 Jul 2003 +// +//+---------------------------------------------------------------------------- + +#include + +#include "kernel.h" +#include "hal.h" +#include "rtl.h" +#include "video.h" + +extern BIOS_HARDWARE_BLOCK g_BiosHardwareBlock; + +// +// Local #defines +// +#define FG_BG_TO_ATTRIB_BYTE(fg,bg) ((fg&0x0F)|((bg&0x0F)<<4)) +#define LINES (0) +#define COLS (1) +#define CRT_ADDR_REG (0x3D4) +#define CRT_DATA_REG (0x3D5) +#define CRT_CURSOR_LOC_HIGH_REG (0x0E) +#define CRT_CURSOR_LOC_LOW_REG (0x0F) +#define ZERO_BASED_LINE_COL_TO_BYTE_OFFSET(L,C)\ + (((g_bScreenResolution[COLS] << 1) * (L)) + ((C) << 1)) +#define ZERO_BASED_LINE_COL_TO_ULONG_OFFSET(L,C)\ + (ZERO_BASED_LINE_COL_TO_BYTE_OFFSET((L),(C)) >> 2) + +// +// Global identifiers +// +BYTE *g_pVideoMemory = NULL; +BYTE g_bScreenResolution[2] = { 0, 0 }; +BYTE g_bSoftCursor[2] = { 0, 0 }; +BYTE g_bColorAttribute = FG_BG_TO_ATTRIB_BYTE(BLACK, WHITE); + +typedef struct _VIDEO_LOOKUP_ENTRY +{ + CHAR *szBoardName; + BYTE *pStartOfVideoMemory; + BYTE uScreenLines[3]; +} +VIDEO_LOOKUP_ENTRY; + +static VIDEO_LOOKUP_ENTRY g_VideoLookupTable[] = +{ + { "No video adapter", + (BYTE *)0x000B0000, + { 25, 25, 25 } }, + { "Monochrome display adapter", + (BYTE *)0x000B0000, + { 25, 25, 25 } }, + { "CGA", + (BYTE *)0x000B8000, + { 25, 25, 25 } }, + { "Code 3: Undefined", + (BYTE *)0x000B0000, + { 25, 25, 25 } }, + { "EGA with color display", + (BYTE *)0x000B8000, + { 43, 25, 25 } }, + { "EGA with mono display", + (BYTE *)0x000B0000, + { 43, 25, 25 } }, + { "Code 6: Undefined", + (BYTE *)0x000B0000, + { 25, 25, 25 } }, + { "VGA with mono display", + (BYTE *)0x000B0000, + { 50, 27, 25 } }, + { "VGA with color display", + (BYTE *)0x000B8000, + { 50, 27, 25 } }, + { "Code 9: Undefined", + (BYTE *)0x000B0000, + { 25, 25, 25 } }, + { "MCGA with digital color", + (BYTE *)0x000B8000, + { 25, 25, 25 } }, + { "MCGA with monochrome", + (BYTE *)0x000B0000, + { 25, 25, 25 } }, + { "MCGA with analog color", + (BYTE *)0x000B8000, + { 25, 25, 25 } } +}; + + +//+---------------------------------------------------------------------------- +// +// Function: VideopInitialize +// +// Synopsis: +// +// Arguments: void +// +// Returns: STATUS +// +//+---------------------------------------------------------------------------- +STATUS +VideopInitialize(void) +{ + BYTE uType = g_BiosHardwareBlock.bDisplayType; + BYTE uIndex = 2; + + // + // Based on the font size (width in pixels), set the index into the + // screen lines lookup table + // + switch(g_BiosHardwareBlock.bFontSize) + { + case 8: + uIndex = 0; + break; + case 14: + uIndex = 1; + break; + case 16: + uIndex = 2; + break; + } + + // + // If the display type returned by BIOS is known to us, use the + // lookup table to populate some globals here. + // + if (uType <= ARRAY_LENGTH(g_VideoLookupTable)) + { + g_pVideoMemory = g_VideoLookupTable[uType].pStartOfVideoMemory; + g_bScreenResolution[LINES] = + g_VideoLookupTable[uType].uScreenLines[uIndex]; + } + + // + // Otherwise make some guesses + // + else + { + g_pVideoMemory = (BYTE *)0x000B8000; + g_bScreenResolution[LINES] = 25; + } + g_bScreenResolution[COLS] = 80; + + // + // Set color to WHITE on BLACK + // + VideoSetCurrentColorAttribute(WHITE, BLACK); + + // + // Clear the screen and thus synchronize the hardware and soft cursor + // position. + // + VideoClearScreen(); + return(STATUS_SUCCESS); +} + + +//+---------------------------------------------------------------------------- +// +// Function: VideoDriverMain +// +// Synopsis: +// +// Arguments: UINT uEvent +// +// Returns: STATUS +// +//+---------------------------------------------------------------------------- +STATUS +VideoDriverMain(UINT uEvent) +{ + switch(uEvent) + { + case DEVICE_DRIVER_INITIALIZATION: + return(VideopInitialize()); + + case DEVICE_DETATCH: + ASSERT(FALSE); + return(STATUS_NOT_SUPPORTED); + + case DEVICE_DRIVER_SHUTDOWN: + g_fVideoEnabled = FALSE; + return(STATUS_SUCCESS); + + default: + } + return(TRUE); +} + +STATUS +VideoDriverRead(UINT uAddress, + void *pBuffer, + UINT uBufferLength); + +STATUS +VideoDriverWrite(UINT uAddress, + void *pBuffer, + UINT uBufferLength); + +STATUS +VideoDriverIoctl(UINT uIoctlCode, + void *pBuffer, + UINT uBufferLength); + + +// +// Move the hardware cursor on the screen to sync up with the software +// cursor position in g_bSoftwareCursor +// +void +VideoSetHardwareCursor(void) +{ + WORD wOffset; // byte offset into video mem + BYTE bOrig; // original port contents holder + + // + // Cursor is zero-based (e.g. line 24 is the last one) + // + ASSERT(g_bSoftCursor[LINES] < g_bScreenResolution[LINES]); + ASSERT(g_bSoftCursor[COLS] < g_bScreenResolution[COLS]); + + wOffset = ZERO_BASED_LINE_COL_TO_BYTE_OFFSET(g_bSoftCursor[LINES], + g_bSoftCursor[COLS]); + ASSERT(wOffset <= (g_bScreenResolution[LINES] * + g_bScreenResolution[COLS]) * 2 - 2); + + // + // Save original contents of CRT address register. + // + bOrig = in(CRT_ADDR_REG); + + // + // Set the high cursor location byte + // + out(CRT_ADDR_REG, CRT_CURSOR_LOC_HIGH_REG); + out(CRT_DATA_REG, (wOffset >> 8) & 0xFF); + IoDelay(); + + // + // Set the low cursor location byte + // + out(CRT_ADDR_REG, CRT_CURSOR_LOC_LOW_REG); + IoDelay(); + out(CRT_DATA_REG, (wOffset & 0xFF)); + IoDelay(); + + // + // Restore contents of the CRT address register + // + out(CRT_ADDR_REG, bOrig); + IoDelay(); +} + +void +VideoSetCurrentColorAttribute(BYTE bFg, BYTE bBg) +{ + bFg &= 0x0F; // restrict these guys to colors + bBg &= 0x0F; // and the high intense bit + + g_bColorAttribute = FG_BG_TO_ATTRIB_BYTE(bFg, bBg); +} + +void +VideoClearScreen(void) +{ + ULONG *p = (ULONG *)g_pVideoMemory; + ULONG uFill; + ULONG uCount; + + // + // Start uCount at the number of ULONGs in visible video memory + // + uCount = ZERO_BASED_LINE_COL_TO_BYTE_OFFSET(g_bScreenResolution[LINES] - 1, + g_bScreenResolution[COLS] - 1); + uCount += 2; + uCount >>= 2; + + // + // Construct a fill ULONG: blank attrib blank attrib + // + uFill = 0; + uFill |= (ULONG)g_bColorAttribute; + uFill |= ((ULONG)g_bColorAttribute) << 16; + + // + // Blast uFills into video memory + // + while(uCount) + { + *p++ = uFill; + uCount--; + } + + // + // Set cursor position to 0,0 + // + g_bSoftCursor[LINES] = g_bSoftCursor[COLS] = 0; + VideoSetHardwareCursor(); +} + + +void +VideoScroll(void) +{ + ULONG *p = (ULONG *)g_pVideoMemory; + ULONG uUlongsPerLine = (g_bScreenResolution[COLS] >> 1); // 2X/4 + ULONG i, j; + ULONG uFill; + + ASSERT(sizeof(ULONG) == 4); + ASSERT(sizeof(BYTE) == 1); + + for (i = 0; + i < (g_bScreenResolution[LINES] - 2); + i++) + { + // + // Start at i, 0. Copy a ULONG up from i+1, 0. Do the whole line i. + // + for (j = 0; + j < uUlongsPerLine; + j++) + { + *p = *(p + uUlongsPerLine); + p++; + } + } + + // + // Construct a fill ULONG: blank attrib blank attrib + // + uFill = 0; + uFill |= (ULONG)g_bColorAttribute; + uFill |= ((ULONG)g_bColorAttribute) << 16; + + // + // Blank the last line + // + p = (ULONG *)(g_pVideoMemory + + ZERO_BASED_LINE_COL_TO_BYTE_OFFSET(g_bScreenResolution[LINES] - 1, 0)); + for (i = 0; + i < uUlongsPerLine; + i++) + { + *p = uFill; + p++; + } +} + +void +VideoPutChar(BYTE c) +{ + BYTE *p = (g_pVideoMemory + + ZERO_BASED_LINE_COL_TO_BYTE_OFFSET(g_bSoftCursor[LINES], + g_bSoftCursor[COLS])); + switch(c) + { + case '\n': + g_bSoftCursor[LINES]++; + if (g_bSoftCursor[LINES] >= g_bScreenResolution[LINES]) + { + VideoScroll(); + g_bSoftCursor[LINES] = g_bScreenResolution[LINES] - 1; + } + g_bSoftCursor[COLS] = 0; + return; + default: + *p = c; + p++; + *p = g_bColorAttribute; + p++; + g_bSoftCursor[COLS]++; + if (g_bSoftCursor[COLS] >= g_bScreenResolution[COLS]) + { + g_bSoftCursor[COLS] = 0; + g_bSoftCursor[LINES]++; + if (g_bSoftCursor[LINES] >= g_bScreenResolution[LINES]) + { + VideoScroll(); + g_bSoftCursor[LINES] = g_bScreenResolution[LINES] - 1; + } + } + } +} + +void +VideoPutNullTerminatedString(BYTE *s) +{ + ULONG uSafety = 0; + + while (*s != 0) + { + VideoPutChar(*s); + s++; + uSafety++; + + if (uSafety > 256) + { + VideoPutNullTerminatedString("RUNAWAYSTRING!?!\n\0"); + break; + } + } + VideoSetHardwareCursor(); +} + +void +VideoSetCursorPosition(BYTE bLine, BYTE bCol) +{ + if (bLine >= g_bScreenResolution[LINES]) + { + bLine = g_bScreenResolution[LINES] - 1; + } + if (bCol >= g_bScreenResolution[COLS]) + { + bCol = g_bScreenResolution[COLS] - 1; + } + + g_bSoftCursor[LINES] = bLine; + g_bSoftCursor[COLS] = bCol; + VideoSetHardwareCursor(); +} + +void +VideoGetCursorPosition(BYTE *pbLine, BYTE *pbCol) +{ + *pbLine = g_bSoftCursor[LINES]; + *pbCol = g_bSoftCursor[COLS]; +} + +void +VideoPrint(CHAR *szFormat, ...) +{ + CHAR *p = szFormat; + CHAR *q; + CHAR buf[32]; + va_list args; + INT iVal; + UINT uVal; + CHAR *szVal; + + va_start(args, szFormat); + while (*p != '\0') + { + switch (*p) + { + case '%': + p++; + switch (*p) + { + case '\0': + goto done; + case '%': + VideoPutChar('%'); + break; + case 'd': + iVal = va_arg(args, int); + q = RtlIntToAscii(iVal, buf, ARRAY_LENGTH(buf), 10); + ASSERT(strlen(q) < 20); + VideoPutNullTerminatedString(q); + break; + case 'u': + uVal = va_arg(args, unsigned int); + q = RtlIntToAscii(uVal, buf, ARRAY_LENGTH(buf), 10); + ASSERT(strlen(q) < 20); + VideoPutNullTerminatedString(q); + break; + case 'x': + uVal = va_arg(args, unsigned int); + q = RtlIntToAscii(uVal, buf, ARRAY_LENGTH(buf), 16); + ASSERT(strlen(q) < 20); + VideoPutNullTerminatedString(q); + break; + case 's': + szVal = va_arg(args, char *); + VideoPutNullTerminatedString(szVal); + break; + case 'c': + iVal = va_arg(args, int); + VideoPutChar(iVal & 0xFF); + break; + default: + VideoPutChar(*p); + break; + } + break; + default: + VideoPutChar(*p); + break; + } + p++; + } + done: + va_end(args); + VideoSetHardwareCursor(); +} diff --git a/kernel/hal/Makefile b/kernel/hal/Makefile new file mode 100644 index 0000000..3725f18 --- /dev/null +++ b/kernel/hal/Makefile @@ -0,0 +1,20 @@ +CC= gcc +CFLAGS= -Wall -imacros ../defines -I../inc +OBJS= video.o io.o interrupts.o intsupport.o keyboard.o timer.o \ + timestamp.o counters.o +RM= /bin/rm +NASM= nasm +NASMFLAGS= -f elf + +.SUFFIXES: .c .o .asm + +all: $(OBJS) + +.c.o: + $(CC) $(CFLAGS) -c $< + +.asm.o: + $(NASM) $(NASMFLAGS) -o $*.o $< + +clean: + $(RM) -f $(OBJS) *~ #*# .#* diff --git a/kernel/hal/counters.c b/kernel/hal/counters.c new file mode 100644 index 0000000..9bd9466 --- /dev/null +++ b/kernel/hal/counters.c @@ -0,0 +1,17 @@ +//+---------------------------------------------------------------------------- +// +// File: counters.c +// +// Module: +// +// Synopsis: +// +// Copyright (C) 2003 Scott Gasch +// +// Created: sgasch 24 Oct 2003 +// +//+---------------------------------------------------------------------------- + +#include "kernel.h" + +UINT64 g_ullTimeStampCounter = 0; diff --git a/kernel/hal/interrupts.c b/kernel/hal/interrupts.c new file mode 100644 index 0000000..94f2528 --- /dev/null +++ b/kernel/hal/interrupts.c @@ -0,0 +1,230 @@ +//+---------------------------------------------------------------------------- +// +// File: interrupts.c +// +// Module: +// +// Synopsis: +// +// Created: sgasch 6 Jul 2003 +// +//+---------------------------------------------------------------------------- + +#include "kernel.h" +#include "hal.h" +#include "rtl.h" +#include "interrupts.h" + +IDT_ENTRY g_IDT[NUM_IDT_ENTRIES]; +INTERRUPT_HANDLER g_InterruptHandlerTable[NUM_IDT_ENTRIES]; +ULONG g_uInterruptsEnabled = 0; + +void +HalEnableInterrupts(void) +{ + g_uInterruptsEnabled++; + ASSERT(g_uInterruptsEnabled == 1); + + __asm__ __volatile__ ( + "sti" + ); +} + +void +HalDisableInterrupts(void) +{ + g_uInterruptsEnabled--; + ASSERT(g_uInterruptsEnabled == 0); + + __asm__ __volatile__ ( + "cli" + ); +} + + +//+---------------------------------------------------------------------------- +// +// Function: DoNothing +// +// Synopsis: The default interrupt handler, do nothing +// +// Arguments: INTERRUPT_STATE *p +// +// Returns: void +// +//+---------------------------------------------------------------------------- +void HalDoNothing(INTERRUPT_STATE *p) +{ + ; +} + + +//+---------------------------------------------------------------------------- +// +// Function: InterruptSendIrqEoi +// +// Synopsis: Send Interrupt ReQuest End Of Interrupt to the PIC. +// +// Arguments: INTERRUPT_STATE *is +// +// Returns: void +// +//+---------------------------------------------------------------------------- +void +HalInterruptSendIrqEoi(INTERRUPT_STATE *is) +{ + ULONG uIrq; + BYTE bCommand; + BYTE *p = (BYTE *)0x000B8000; + + *p = '3'; + *(p + 1) = ' '; + + if ((is->uIntNum >= FIRST_EXTERNAL_INT) && + (is->uIntNum <= LAST_EXTERNAL_INT)) + { + uIrq = (is->uIntNum - FIRST_EXTERNAL_INT); + ASSERT(uIrq >= 0); + ASSERT(uIrq <= 15); + bCommand = 0x60 | (uIrq & 0x7); + + if (uIrq < 8) + { + // EOI to master PIC + out(0x20, bCommand); + HalIoDelay(); + } + else + { + // EOI to slave PIC and cascade line EOI to master PIC + out(0xA0, bCommand); + out(0x20, 0x62); + HalIoDelay(); + } + } + + *p = '4'; + *(p + 1) = ' '; +} + +//+---------------------------------------------------------------------------- +// +// Function: InterruptInitializeGate +// +// Synopsis: Initializes an interrupt gate with given handler address +// and descriptor privilege level. +// +// Arguments: IDT_ENTRY *pEntry, +// void *pPreamble, +// ULONG uDpl +// +// Returns: void +// +//+---------------------------------------------------------------------------- +void +HalInterruptInitializeGate(IDT_ENTRY *pEntry, + void *pPreamble, + ULONG uDpl) +{ + ULONG u = (ULONG)pPreamble; + + pEntry->i.offsetLow = u & 0xffff; + pEntry->i.segmentSelector = (1<<3); + pEntry->i.reserved = 0; + pEntry->i.signature = 0x70; // == 01110000b + pEntry->i.dpl = uDpl; + pEntry->i.present = 1; + pEntry->i.offsetHigh = u >> 16; +} + + +//+---------------------------------------------------------------------------- +// +// Function: InterruptInstallHandler +// +// Synopsis: Installs an interrupt handler routine for a given IRQ number +// +// Arguments: ULONG uInterruptNumber, +// INTERRUPT_HANDLER pHandler +// +// Returns: void +// +//+---------------------------------------------------------------------------- +void +HalInterruptInstallHandler(ULONG uInterruptNumber, + INTERRUPT_HANDLER pHandler) +{ + ASSERT(uInterruptNumber < ARRAY_LENGTH(g_InterruptHandlerTable)); + + g_InterruptHandlerTable[uInterruptNumber] = pHandler; +} + + +//+---------------------------------------------------------------------------- +// +// Function: InterruptInitialize +// +// Synopsis: Initialize the interrupt system +// +// Arguments: void +// +// Returns: void +// +//+---------------------------------------------------------------------------- +void +HalInitializeInterrupts(void) +{ + ULONG uPreambleEntrySize = (ULONG)&HalInterruptHandlerPreambleAfter; + ULONG u; + ULONG uDpl; + USHORT uLimitAndBase[3]; + BYTE *p; + + // + // Determine the size of an interrupt preamble table entry. + // + uPreambleEntrySize -= (ULONG)&HalInterruptHandlerPreambleBefore; + + // + // Set the base interrupt number for each PIC. See comments in + // intsupport.asm. + // + HalInterruptInitializePICs(); + + // + // Build interrupt descriptor table + // + for (u = 0, p = &HalInterruptHandlerPreamble; + u < ARRAY_LENGTH(g_IDT); + u++, p += uPreambleEntrySize) + { + uDpl = (u == 0x2E) ? USER_PRIVILEGE : KERNEL_PRIVILEGE; + + HalInterruptInitializeGate(&(g_IDT[u]), p, uDpl); + HalInterruptInstallHandler(u, HalDoNothing); + } + + // + // Install the IDT + // + u = (ULONG)&(g_IDT); + uLimitAndBase[0] = 8 * NUM_IDT_ENTRIES; + uLimitAndBase[1] = (USHORT)(u & 0xffff); + uLimitAndBase[2] = (USHORT)(u >> 16); + + HalInterruptLoadIDTR((void *)uLimitAndBase); + + // + // Install interrupt handlers + // + HalInterruptInstallHandler(IRQ0, HalTimerInt); + HalInterruptInstallHandler(IRQ1, HalKeyboardInt); + + out(0x21, 0xFC); // turn on keyboard and timer + + // + // Enable interrupts + // + HalEnableInterrupts(); +} + diff --git a/kernel/hal/interrupts.h b/kernel/hal/interrupts.h new file mode 100644 index 0000000..673b13b --- /dev/null +++ b/kernel/hal/interrupts.h @@ -0,0 +1,92 @@ +//+---------------------------------------------------------------------------- +// +// File: interrupts.h +// +// Module: +// +// Synopsis: +// +// Created: sgasch 6 Jul 2003 +// +//+---------------------------------------------------------------------------- + +#ifndef _INTERRUPTS_H_ +#define _INTERRUPTS_H_ + +#define NUM_IDT_ENTRIES (256) + +// Exceptions range from 0x00..0x11 +#define FIRST_EXCEPTION 0x00 +#define LAST_EXCEPTION 0x11 +#define NUM_EXCEPTIONS 18 + +// External IRQs range from 0x30..0x3F +#define FIRST_EXTERNAL_INT 0x30 +#define LAST_EXTERNAL_INT 0x3F +#define IRQ0 0x30 +#define IRQ1 0x31 +#define IRQ2 0x39 +#define IRQ3 0x33 +#define IRQ4 0x34 +#define IRQ5 0x35 +#define IRQ6 0x36 +#define IRQ7 0x37 +#define IRQ8 0x38 +#define IRQ9 0x39 +#define IRQ10 0x3A +#define IRQ11 0x3B +#define IRQ12 0x3C +#define IRQ13 0x3D +#define IRQ14 0x3E +#define IRQ15 0x3F +#define NUM_EXTERNAL_INTS 16 + +typedef struct _INTERRUPT_GATE +{ + USHORT offsetLow; + USHORT segmentSelector; + USHORT reserved : 5; + USHORT signature : 8; + USHORT dpl : 2; + USHORT present : 1; + USHORT offsetHigh; +} +INTERRUPT_GATE; + +typedef union _IDT_ENTRY +{ + INTERRUPT_GATE i; + // In theory we could have members for trap gates + // and task gates if we wanted. +} +IDT_ENTRY; + +// +// Defined in intsupport.asm +// +extern void HalInterruptInitializePICs(void); +extern void HalInterruptLoadIDTR(void *uLimitAndBase); +extern void HalIoDelay(void); +extern BYTE HalInterruptHandlerPreamble; +extern BYTE HalInterruptHandlerPreambleBefore; +extern BYTE HalInterruptHandlerPreambleAfter; + +// +// Defined in interrupts.c +// +extern void HalDisableInterrupts(void); +extern void HalEnableInterrupts(void); + + +// +// Defined in keyboard.c +// +extern void HalKeyboardInt(INTERRUPT_STATE *is); + +// +// Defined in timer.c +// +extern void HalTimerInt(INTERRUPT_STATE *is); + +#endif + diff --git a/kernel/hal/intsupport.asm b/kernel/hal/intsupport.asm new file mode 100644 index 0000000..3d1fcc8 --- /dev/null +++ b/kernel/hal/intsupport.asm @@ -0,0 +1,326 @@ +[BITS 32] + +FIRST_EXTERNAL_INT equ 0x30 +LAST_EXTERNAL_INT equ 0x30+15 + +GLOBAL HalInterruptLoadIDTR +GLOBAL HalInterruptGenericHandler +GLOBAL HalInterruptHandlerPreamble +GLOBAL HalInterruptHandlerPreambleBefore +GLOBAL HalInterruptHandlerPreambleAfter +GLOBAL HalInterruptInitializePICs +GLOBAL HalIoDelay + +EXTERN g_InterruptHandlerTable +EXTERN HalVideoPrint +EXTERN HalInterruptSendIrqEoi + +;; Save registers prior to calling a handler function. +;; This must be kept up to date with: +;; - Interrupt_State struct in int.h +;; - Setup_Initial_Thread_Context() in kthread.c +%macro SAVE_REGISTERS 0 + push eax + push ebx + push ecx + push edx + push esi + push edi + push ebp + push ds + push es + push fs + push gs +%endmacro + +;; Restore registers and clean up the stack after calling a handler function +;; (i.e., just before we return from the interrupt via an iret instruction). +%macro RESTORE_REGISTERS 0 + pop gs + pop fs + pop es + pop ds + pop ebp + pop edi + pop esi + pop edx + pop ecx + pop ebx + pop eax + add esp, 8 +%endmacro + +%macro INT_NO_ERROR 1 +align 8 + push 0xffffffff ; fake the error code + push %1 ; push the int number + jmp HalInterruptGenericHandler; +%endmacro + +%macro INT_WITH_ERROR 1 +align 8 + push %1 ; error code already pushed, just push int num + jmp HalInterruptGenericHandler +%endmacro + +[SECTION .text] +;;; +;;; void +;;; InterruptLoadIDTR(IDT *p) +;;; +;;; Function to load the interrupt descriptor table register (IDTR) +;;; callable from C. +;;; +HalInterruptLoadIDTR: + mov eax, [esp+4] ; p + lidt [eax] + ret + +;;; +;;; The following is code to initialize the master and slave 8259 +;;; programmable interrupt controllers (PICs). +;;; +;;; For ISA machines there are two 8259's, a master and a slave. Each has +;;; eight interrupt request (IR) pins and one interrupt (INT) pin. The +;;; master's INT pin is tied to the INT line of the microprocessor. The +;;; slave's INT pin is tied to the master's IR2 line. Thus the master's +;;; IR0, IR1, and IR3..7 are real devices. But the master's IR2 means +;;; that some IR pin on the slave is asserted. So the system IRQs are: +;;; +;;; (highest default priority) +;;; IRQ0 = master IR0 [IRQ0 = system timer] +;;; IRQ1 = master IR1 [IRQ1 = keyboard] +;;; IRQ2 = master IR2 --> IRQ8..IRQ15 = slave IR0..7 +;;; [IRQ8 = rt. timer][IRQ13 = coproc.] +;;; IRQ3 = master IR3 +;;; IRQ4 = master IR4 +;;; IRQ5 = master IR5 +;;; IRQ6 = master IR6 +;;; IRQ7 = master IR7 +;;; (lowest default priority) +;;; +;;; By default these IRQs raise the following interrupt numbers: +;;; +;;; IRQ0 = int 0x8 [collides with double exception] +;;; IRQ1 = int 0x9 [collides with coprocessor exception] +;;; IRQ2/9 = int 0x71 +;;; IRQ3 = int 0xb [collides with segment not present exception] +;;; IRQ4 = int 0xc [collides with stack fault exception] +;;; IRQ5 = int 0xd [collides with general protection exception] +;;; IRQ6 = int 0xe [collides with page fault] +;;; IRQ7 = int 0xf +;;; IRQ8 = int 0x70 +;;; IRQ10 = int 0x72 +;;; IRQ11 = int 0x73 +;;; IRQ12 = int 0x74 +;;; IRQ13 = int 0x75 +;;; IRQ14 = int 0x76 +;;; IRQ15 = int 0x77 +;;; +;;; So the problem is that IRQ0, IRQ1, IRQ3..6 all, by default, map to +;;; interrupt numbers that collide with interrupts raised by the cpu +;;; as a result of a software exception. The solution to this problem +;;; is to remap what interrupts are raised by these IRQs by reprogramming +;;; the 8259 PICs to raise new interrupt numbers. +;;; +;;; This is accomplished by sending initialization command words (ICWs) +;;; to I/O ports 0x20-0x21 (master PIC) and 0xA0-0xA1 (slave PIC). By doing +;;; so we can remap the interrupt numbers raised by IRQs so that: +;;; +;;; IRQ0 = 0x30 IRQ8 = 0x38 +;;; IRQ1 = 0x31 IRQ10 = 0x3A +;;; IRQ2/9 = 0x39 IRQ11 = 0x3B +;;; IRQ3 = 0x33 IRQ12 = 0x3C +;;; IRQ4 = 0x34 IRQ13 = 0x3D +;;; IRQ5 = 0x35 IRQ14 = 0x3E +;;; IRQ6 = 0x36 IRQ15 = 0x3F +;;; IRQ7 = 0x37 +;;; +;;; ICW1 is the same for both the master and slave PIC. Here's the meaning +;;; of the individual bits in the word: +;;; +;;; F E D C B A 9 8 | 7 6 5 4 3 2 1 0 +;;; (not used) | | | | |__ 1=expect ICW4 +;;; | | | |_____ 0=cascade 1=single +;;; | | |________ 0=interval-4, 1=8 +;;; | |___________ 0=edge triggered +;;; | 1=level triggered +;;; |______________ must be 1 +;;; +;;; +BOTH_ICW1 equ 0x11 ; expect ICW4, cascade mode, call address + ; interval=8, edge triggered mode + +;;; +;;; ICW2 tells the PICs their base interrupt number. For example, the +;;; default base interrupt for the master PIC is 0x8. Thus IRQ0=0x8, +;;; IRQ1=0x9 ... IRQ7=0xF. As stated, we want to relocate these. +;;; +MASTER_ICW2 equ 0x30 ; use ints 0x30..0x37 +SLAVE_ICW2 equ 0x38 ; use ints 0x38..0x3F + +;;; +;;; ICW3 to the master tells it which of its IR pins is connected to the +;;; slave PIC. ICW3 to the slave tells its id number. +;;; +MASTER_ICW3 equ 0x04 ; slave on IR pin 2 +SLAVE_ICW3 equ 0x02 ; slave id=2 + +;;; +;;; ICW4 to both PICs is another bitvector to set some features: +;;; +;;; +;;; F E D C B A 9 8 | 7 6 5 4 3 2 1 0 +;;; (not used) | ---- | |__ 0=MCS80/85 1=8086/88 +;;; | | |_____ 0=normal 1=auto EOI +;;; | |_________ 00=non-buf mode +;;; | 10=buf mode, slave +;;; | 11=buf mode, master +;;; |______________ 0=!nested 1=nested +;;; +BOTH_ICW4 equ 0x01 + +HalInterruptInitializePICs: + mov al, BOTH_ICW1 + out 0x20, al ; ICW1 to master PIC + call HalIoDelay + out 0xA0, al ; ICW1 to slave PIC + call HalIoDelay + + mov al, MASTER_ICW2 + out 0x21, al ; ICW2 to master PIC + call HalIoDelay + mov al, SLAVE_ICW2 + out 0xA1, al ; ICW2 to slave PIC + call HalIoDelay + + mov al, MASTER_ICW3 + out 0x21, al ; ICW3 to master PIC + call HalIoDelay + mov al, SLAVE_ICW3 + out 0xA1, al ; ICW3 to slave PIC + call HalIoDelay + + mov al, BOTH_ICW4 + out 0x21, al ; ICW4 to master PIC + call HalIoDelay + out 0xA1, al ; ICW4 to slave PIC + call HalIoDelay + +;;; +;;; After the four ICW sequence has been completed any subsequent writes +;;; to port 0x21 or 0xA1 set the IRQ mask for the master/slave PIC. If +;;; this mask is 0xFF all IRQs are disabled. If this mask is 0x00 +;;; all IRQs are enabled. +;;; + mov al, 0xff ; slave PIC cannot interrupt + out 0xA1, al + call HalIoDelay + mov al, 0xfb ; mask all IRQs but 2 (the slave PIC) in master + out 0x21, al + call HalIoDelay + ret + +;;; +;;; When doing I/O operations this routine can be used to "delay" long +;;; enough to let the external controller keep up with the cpu. +;;; +HalIoDelay: + jmp .done +.done: ret + +;;; +;;; This is the start of the main interrupt handler code. The first section +;;; of this code is called the InterruptHandlerPreamble. It consists of +;;; 256 entry points in a table (one per possible interrupt number). These +;;; preamble entry points push the interrupt number and (when needed for +;;; interrupt numbers that the processor doesn't automatically push an error +;;; code for) a fake error code. Then they jump to the main generic interrupt +;;; handling routing: InterruptGenericHandler. Thus the stack is always layed +;;; out in the same way when control passes to the generic handler: +;;; +;;; saved ss (pushed by cpu for system calls) +;;; saved esp (pushed by cpu for system calls) +;;; ------------ +;;; saved eflags (pushed by cpu) +;;; saved CS (pushed by cpu) +;;; saved IP (pushed by cpu) +;;; error code (pushed by preamble or cpu) +;;; interrupt number (pushed by preamble) +;;; esp --> return address (pushed by jmp InteruptGenericHandler) +;;; +;;; The first thing InterruptGenericHandler does is push the rest of the +;;; registers onto the stack. +;;; +align 8 +HalInterruptHandlerPreamble: +HalInterruptHandlerPreambleBefore: + INT_NO_ERROR 0 + align 8 +HalInterruptHandlerPreambleAfter: + INT_NO_ERROR 1 + INT_NO_ERROR 2 + INT_NO_ERROR 3 + INT_NO_ERROR 4 + INT_NO_ERROR 5 + INT_NO_ERROR 6 + INT_NO_ERROR 7 + INT_WITH_ERROR 8 + INT_NO_ERROR 9 + INT_WITH_ERROR 10 + INT_WITH_ERROR 11 + INT_WITH_ERROR 12 + INT_WITH_ERROR 13 + INT_WITH_ERROR 14 + INT_NO_ERROR 15 + INT_NO_ERROR 16 + INT_WITH_ERROR 17 + +;;; +;;; The rest of the interrupt numbers are INT_NO_ERRORs. Use nasm's +;;; %rep command to do them all at once. +;;; +%assign intNum 18 +%rep (256 - 18) + INT_NO_ERROR intNum +%assign intNum intNum + 1 +%endrep + +;;; +;;; This is the generic interrupt handler which is called by the preambles +;;; above. It's job is to save the registers on the stack and then transfer +;;; control to a specific per-interrupt handler. It finds the address of +;;; the per-interrupt handler to call by indexing into g_InterruptHandlerTable +;;; +align 8 +HalInterruptGenericHandler: + SAVE_REGISTERS + + ;; Ensure that we're using the kernel data segment + mov ax, (2<<3) + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + + ;; Get interrupt number the preamble pushed for us + mov esi, [esp+44] + + ;; Get the address of the handler function from the + ;; table of handler functions. + mov eax, g_InterruptHandlerTable + mov ebx, [eax+esi*4] + + ;; Call the handler. The argument passed is a pointer to + ;; INTERRUPT_STATE. Interrupts are enabled at this point + ;; so if the handler is non-reentrant it must disable them. + push esp + call ebx + add esp, 4 + + push esp + call HalInterruptSendIrqEoi + add esp, 4 + + RESTORE_REGISTERS + iret diff --git a/kernel/hal/io.c b/kernel/hal/io.c new file mode 100644 index 0000000..d97a211 --- /dev/null +++ b/kernel/hal/io.c @@ -0,0 +1,40 @@ +#include "kernel.h" + +// Write a byte to an I/O port. +void +out(WORD port, BYTE value ) +{ + __asm__ __volatile__ ( + "outb %b0, %w1" + : + : "a" (value), "Nd" (port) + ); +} + +// Read a byte from an I/O port. +BYTE +in(WORD port) +{ + BYTE value; + + __asm__ __volatile__ ( + "inb %w1, %b0" + : "=a" (value) + : "Nd" (port) + ); + + return value; +} + +// Short delay. May be needed when talking to some +// (slow) I/O devices. +void +iodelay(void) +{ + BYTE value = 0; + __asm__ __volatile__ ( + "outb %0, $0x80" + : + : "a" (value) + ); +} diff --git a/kernel/hal/timer.c b/kernel/hal/timer.c new file mode 100644 index 0000000..3c82691 --- /dev/null +++ b/kernel/hal/timer.c @@ -0,0 +1,43 @@ +//+---------------------------------------------------------------------------- +// +// File: timer.c +// +// Module: +// +// Synopsis: +// +// Created: sgasch 21 Oct 2003 +// +//+---------------------------------------------------------------------------- + +#include "kernel.h" +#include "hal.h" +#include "rtl.h" +#include "interrupts.h" + +void +HalTimerInt(INTERRUPT_STATE *is) +{ + static CHAR whee[] = "|/-\\\0"; + static ULONG u = 0; + static BYTE b = 0; + static unsigned int foo; + BYTE *p = (BYTE *)0x000B8000; + + out(0x61, 0x80); + u++; + + CURRENT_STAMP(&foo); + + if ((u % 500) == 0) + { + b++; + if (whee[b] == '\0') + { + b = 0; + } + *(p + 158) = whee[b]; + *(p + 159) = ' '; + } +} + diff --git a/kernel/hal/timestamp.asm b/kernel/hal/timestamp.asm new file mode 100644 index 0000000..b56bedb --- /dev/null +++ b/kernel/hal/timestamp.asm @@ -0,0 +1,19 @@ + + [BITS 32] + +GLOBAL HalReadTimestampCounter +EXTERN g_ullTimeStampCounter + +[SECTION .text] +;;; +;;; ULONGLONG +;;; HalReadTimestampCounter(void) +;;; +;;; Function to read the processor's timestamp counter +;;; +HalReadTimestampCounter: + ALIGN 4 + rdtsc + mov [g_ullTimeStampCounter], edx + mov [g_ullTimeStampCounter+4], eax + ret diff --git a/kernel/hal/video.h b/kernel/hal/video.h new file mode 100644 index 0000000..dcd7b01 --- /dev/null +++ b/kernel/hal/video.h @@ -0,0 +1,15 @@ +//+---------------------------------------------------------------------------- +// +// File: video.h +// +// Module: +// +// Synopsis: +// +// Created: sgasch 5 Jul 2003 +// +//+---------------------------------------------------------------------------- +#ifndef VIDEO_H +#define VIDEO_H + +#endif // VIDEO_H diff --git a/kernel/inc/constants.h b/kernel/inc/constants.h new file mode 100644 index 0000000..13f47bd --- /dev/null +++ b/kernel/inc/constants.h @@ -0,0 +1,50 @@ +//+---------------------------------------------------------------------------- +// +// File: constants.h +// +// Module: +// +// Synopsis: +// +// Copyright (C) 2003 +// +// Created: sgasch 10 Oct 2003 +// +//+---------------------------------------------------------------------------- + +#ifndef _CONSTANTS_H_ +#define _CONSTANTS_H_ + +// +// Constants +// +#define YES (1) +#define NO (0) +#ifndef TRUE +#define TRUE (YES) +#endif +#ifndef FALSE +#define FALSE (NO) +#endif + +#define NULL ((void *)0) + +#define ASCII_ESC (0x1B) +#define ASCII_BS (0x08) + +#define USER_PRIVILEGE (3) +#define KERNEL_PRIVILEGE (0) + +// +// Device driver constants +// +#define DEVICE_DRIVER_INITIALIZATION (1) +#define DEVICE_IO_CONTROL (2) +#define DEVICE_READ (3) +#define DEVICE_WRITE (4) +#define DEVICE_DETATCH (6) +#define DEVICE_DRIVER_SHUTDOWN (7) + +#endif /* _CONSTANTS_H_ */ + + diff --git a/kernel/inc/hal.h b/kernel/inc/hal.h new file mode 100644 index 0000000..9a52a4e --- /dev/null +++ b/kernel/inc/hal.h @@ -0,0 +1,104 @@ +//+---------------------------------------------------------------------------- +// +// File: hal.h +// +// Module: +// +// Synopsis: +// +// Created: sgasch 5 Jul 2003 +// +//+---------------------------------------------------------------------------- +#ifndef HAL_H +#define HAL_H + +// +// Video/CRT driver +// +#define BLACK (0) +#define BLUE (1) +#define GREEN (2) +#define CYAN (3) +#define RED (4) +#define MAGENTA (5) +#define YELLOW (6) +#define GRAY (7) +#define HIGH (8) +#define WHITE (HIGH | GRAY) + +void +HalVideoSetCurrentColorAttribute(BYTE bFg, BYTE bBg); + +void +HalVideoClearScreen(void); + +void +HalVideoGetCursorPosition(BYTE *pbLine, BYTE *pbCol); + +void +HalVideoSetCursorPosition(BYTE bLine, BYTE bCol); + +void +HalVideoPutNullTerminatedString(BYTE *s); + +void +HalVideoInitialize(BIOS_HARDWARE_BLOCK *phw); + +void +HalVideoPrint(CHAR *szFormat, ...); + +// +// Keyboard driver +// +#define KB_IRQ (1) +typedef WORD KEYCODE; + +typedef struct _INTERRUPT_STATE +{ + // The register contents at the time of the exception. + // We save these explicitly. + ULONG gs; + ULONG fs; + ULONG es; + ULONG ds; + ULONG ebp; + ULONG edi; + ULONG esi; + ULONG edx; + ULONG ecx; + ULONG ebx; + ULONG eax; + + // We explicitly push the interrupt number. + // This makes it easy for the handler function to determine + // which interrupt occurred. + ULONG uIntNum; + + // This may be pushed by the processor; if not, we push + // a dummy error code, so the stack layout is the same + // for every type of interrupt. + ULONG uErrorCode; + + // These are always pushed on the stack by the processor. + ULONG eip; + ULONG cs; + ULONG eflags; +} +INTERRUPT_STATE; + +typedef void (*INTERRUPT_HANDLER)(INTERRUPT_STATE *pState); + +void HalInitializeInterrupts(void); +void HalInterruptInstallHandler(ULONG uIntNum, INTERRUPT_HANDLER p); +void HalInterruptDisable(); +void HalInterruptEnable(); + +void HalIoDelay(); + +void HalTimerInt(INTERRUPT_STATE *p); +void HalKeyboardInt(INTERRUPT_STATE *p); + +extern UINT64 g_ullTimeStampCounter; +ULONG HalReadTimestampCounter(void); + +#endif // HAL_H diff --git a/kernel/inc/kernel.h b/kernel/inc/kernel.h new file mode 100644 index 0000000..7172401 --- /dev/null +++ b/kernel/inc/kernel.h @@ -0,0 +1,43 @@ +//+---------------------------------------------------------------------------- +// +// File: kernel.h +// +// Module: +// +// Synopsis: +// +// Created: sgasch 5 Jul 2003 +// +//+---------------------------------------------------------------------------- + +#ifndef _KERNEL_H_ +#define _KERNEL_H_ + +#include "types.h" +#include "constants.h" +#include "macros.h" + +// +// Shared data structures +// +typedef struct _BIOS_HARDWARE_BLOCK +{ + BYTE bDisplayType; + BYTE bFontSize; + WORD wEquipmentBitvector; + WORD wMemorySize; + WORD wSystemClock; +} +BIOS_HARDWARE_BLOCK; + +// Write a byte to an I/O port. +void +out(WORD port, BYTE value); + +BYTE +in(WORD port); + +void +iodelay(void); + +#endif /* _KERNEL_H_ */ diff --git a/kernel/inc/macros.h b/kernel/inc/macros.h new file mode 100644 index 0000000..21340ad --- /dev/null +++ b/kernel/inc/macros.h @@ -0,0 +1,43 @@ +//+---------------------------------------------------------------------------- +// +// File: macros.h +// +// Module: +// +// Synopsis: +// +// Copyright (C) 2003 +// +// Created: sgasch 10 Oct 2003 +// +//+---------------------------------------------------------------------------- + +#ifndef _MACROS_H_ +#define _MACROS_H_ + +// +// Macros +// +#define ARRAY_LENGTH(a) (sizeof((a)) / sizeof((a)[0])) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) + +#if 1 +void _assert(CHAR *, CHAR *, ULONG); +void VideoPrint(CHAR *, ...); +#define ASSERT(x) if (x) \ + { ; } \ + else \ + { _assert(__FUNCTION__ , \ + __FILE__ , \ + __LINE__); } +#define TRACE(f, ...) VideoPrint("%s:%u> " ##f , __FUNCTION__ , \ + __LINE__ , ##__VA_ARGS__) +#else +#define ASSERT(x) ; +#define TRACE(f, ...) ; +#endif // DEBUG + +#define CURRENT_STAMP(a) asm volatile("rdtsc" : "=a"(((unsigned int *)(a))[0]), "=d"(((unsigned int *)a)[1])) + +#endif /* _MACROS_H_ */ diff --git a/kernel/inc/rtl.h b/kernel/inc/rtl.h new file mode 100644 index 0000000..3aff129 --- /dev/null +++ b/kernel/inc/rtl.h @@ -0,0 +1,33 @@ +//+---------------------------------------------------------------------------- +// +// File: rtl.h +// +// Module: +// +// Synopsis: +// +// Copyright (C) 2003 Scott Gasch +// +// Created: sgasch 5 Jul 2003 +// +//+---------------------------------------------------------------------------- + +#ifndef _RTL_H_ +#define _RTL_H_ + +void +RtlSetMemory(void *pStart, + BYTE bFill, + ULONG uLength); + +CHAR * +RtlIntToAscii(INT i, + CHAR *buf, + ULONG uBufLen, + ULONG uBase); + +#endif /* _RTL_H_ */ + + + + diff --git a/kernel/inc/types.h b/kernel/inc/types.h new file mode 100644 index 0000000..b3379e5 --- /dev/null +++ b/kernel/inc/types.h @@ -0,0 +1,80 @@ +//+---------------------------------------------------------------------------- +// +// File: types.h +// +// Module: +// +// Synopsis: +// +// Created: sgasch 10 Oct 2003 +// +//+---------------------------------------------------------------------------- + +#ifndef _TYPES_H_ +#define _TYPES_H_ + +// +// Datatype wrappers +// +#define MIN_CHAR (0x80) +#define MAX_CHAR (0x7f) +typedef char CHAR; + +#define MIN_BYTE (0x00) +#define MAX_BYTE (0xff) +typedef unsigned char BYTE; + +#define MIN_UCHAR (0x00) +#define MAX_UCHAR (0xff) +typedef unsigned char UCHAR; + +#define MIN_SHORT (0x8000) +#define MAX_SHORT (0x7fff) +typedef signed short SHORT; + +#define MIN_USHORT (0x0000) +#define MAX_USHORT (0xffff) +typedef unsigned short USHORT; + +#define MIN_WORD (0x0000) +#define MAX_WORD (0xffff) +typedef unsigned short WORD; + +#define MIN_INT (0x80000000) +#define MAX_INT (0x7fffffff) +typedef signed int INT; + +#define MIN_UINT (0x00000000) +#define MAX_UINT (0xffffffff) +typedef unsigned int UINT; + +#define MIN_LONG (0x80000000) +#define MAX_LONG (0x7fffffff) +typedef signed int LONG; + +#define MIN_ULONG (0x00000000) +#define MAX_ULONG (0xffffffff) +typedef unsigned long ULONG; + +#define MIN_INT64 (0x8000000000000000) +#define MAX_INT64 (0x7fffffffffffffff) +typedef signed long long INT64; + +#define MIN_UINT64 (0x0000000000000000) +#define MAX_UINT64 (0xffffffffffffffff) +typedef unsigned long long UINT64; + +#define MIN_BITV MIN_UINT +#define MAX_BITV MAX_UINT +typedef unsigned int BITV; + +#define MIN_BOOL MIN_UCHAR +#define MAX_BOOL MAX_UCHAR +typedef unsigned char BOOL; + +typedef unsigned int STATUS; +#define STATUS_SUCCESS (0) + +#define SIZE_T ULONG + +#endif /* _TYPES_H_ */ diff --git a/kernel/init/Makefile b/kernel/init/Makefile new file mode 100644 index 0000000..815dc69 --- /dev/null +++ b/kernel/init/Makefile @@ -0,0 +1,20 @@ +CC= gcc +CFLAGS= -Wall -imacros ../defines -I../inc +OBJS= main.o entry.o +RM= /bin/rm +NASM= nasm +NASMFLAGS= -f elf + +.SUFFIXES: .c .asm .o + +all: $(OBJS) + +.c.o: + $(CC) $(CFLAGS) -c $< + +.asm.o: + $(NASM) $(NASMFLAGS) -o $*.o $< + +clean: + $(RM) -f $(OBJS) *~ #*# .#* + diff --git a/kernel/init/entry.asm b/kernel/init/entry.asm new file mode 100644 index 0000000..afb0434 --- /dev/null +++ b/kernel/init/entry.asm @@ -0,0 +1,23 @@ +[BITS 32] + +EXTERN KernelEntry +GLOBAL StartOfKernelImage +GLOBAL IdleLoop + +;;; +;;; I put "entry.o" first in line at the linker so I am sure this function +;;; gets put first in the kernel image. This is good because: +;;; +;;; 1. We know there is valid code at 0x00010000 when we jump here +;;; 2. We can use the address of this function as the start of image +;;; 3. We don't have to make sure main (or KernelEntry or whatever) +;;; is always the first function in main.c +;;; +StartOfKernelImage: + jmp KernelEntry + ret + +IdleLoop: + sti + hlt + jmp IdleLoop diff --git a/kernel/init/main.c b/kernel/init/main.c new file mode 100644 index 0000000..22fd14a --- /dev/null +++ b/kernel/init/main.c @@ -0,0 +1,67 @@ +//+---------------------------------------------------------------------------- +// +// File: main.c +// +// Module: +// +// Synopsis: +// +// Created: sgasch 4 Jul 2003 +// +//+---------------------------------------------------------------------------- + +#include "kernel.h" +#include "hal.h" +#include "rtl.h" + +extern char __bss_start; +extern char end; + +void +ZeroBSS(void) +{ + BYTE *pBssStart, *pBssEnd; + + pBssStart = &__bss_start; + pBssEnd = &end; + + // Fill .bss section with zero + RtlSetMemory((void *)pBssStart, '\0', (pBssEnd - pBssStart)); +} + +void +_assert(CHAR *szFunction, CHAR *szFile, ULONG uLine) +{ + HalVideoSetCursorPosition(0, 0); + HalVideoPrint("ASSERTION in %s at %s line %u.\n", + szFunction, szFile, uLine); + while(1) + { + ; + } +} + +// +// Entry point of the kernel. Not called main because I don't care to +// listen to gcc's warnings about argc and argv anymore. +// +int +KernelEntry(BIOS_HARDWARE_BLOCK *phw) +{ + ZeroBSS(); + + HalVideoInitialize(phw); + HalInitializeInterrupts(); + + HalVideoPrint("Interrupts initialized.\n"); + + hang: + HalReadTimestampCounter(); + HalVideoPrint("Timestamp Counter: %u%u\n", + ((g_ullTimeStampCounter >> 32) & 0xffffffff), + (g_ullTimeStampCounter & 0xffffffff)); + goto hang; + + return(-1); +} + diff --git a/kernel/kernel.bin b/kernel/kernel.bin new file mode 100755 index 0000000..d6b13b4 Binary files /dev/null and b/kernel/kernel.bin differ diff --git a/kernel/kernel.map b/kernel/kernel.map new file mode 100644 index 0000000..2d15ed2 --- /dev/null +++ b/kernel/kernel.map @@ -0,0 +1,370 @@ + +Allocating common symbols +Common symbol size file + +g_IDT 0x800 hal/interrupts.o +g_InterruptHandlerTable + 0x400 hal/interrupts.o + +Memory Configuration + +Name Origin Length Attributes +*default* 0x00000000 0xffffffff + +Linker script and memory map + +Address of section .text set to 0x10000 +LOAD init/entry.o +LOAD init/main.o +LOAD hal/video.o +LOAD hal/io.o +LOAD rtl/string.o +LOAD rtl/memory.o +LOAD hal/intsupport.o +LOAD hal/interrupts.o +LOAD hal/timer.o +LOAD hal/keyboard.o +LOAD hal/timestamp.o +LOAD hal/counters.o + 0x08048074 . = (0x8048000 + SIZEOF_HEADERS ) + +.interp + *(.interp) + +.hash + *(.hash) + +.dynsym + *(.dynsym) + +.dynstr + *(.dynstr) + +.gnu.version + *(.gnu.version) + +.gnu.version_d + *(.gnu.version_d) + +.gnu.version_r + *(.gnu.version_r) + +.rel.init + *(.rel.init) + +.rela.init + *(.rela.init) + +.rel.text 0x08048074 0x0 + *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) + +.rela.text + *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) + +.rel.fini + *(.rel.fini) + +.rela.fini + *(.rela.fini) + +.rel.rodata + *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) + +.rela.rodata + *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) + +.rel.data + *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) + +.rela.data + *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) + +.rel.ctors + *(.rel.ctors) + +.rela.ctors + *(.rela.ctors) + +.rel.dtors + *(.rel.dtors) + +.rela.dtors + *(.rela.dtors) + +.rel.got + *(.rel.got) + +.rela.got + *(.rela.got) + +.rel.bss + *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) + +.rela.bss + *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) + +.rel.plt + *(.rel.plt) + +.rela.plt + *(.rela.plt) + +.init + *(.init) + +.plt + *(.plt) + +.text 0x00010000 0x20b0 + *(.text .stub .text.* .gnu.linkonce.t.*) + .text 0x00010000 0xa init/entry.o + 0x00010000 StartOfKernelImage + 0x00010006 IdleLoop + *fill* 0x0001000a 0x2 2425393296 + .text 0x0001000c 0x104 init/main.o + 0x0001003c _assert + 0x00010078 KernelEntry + 0x0001000c ZeroBSS + .text 0x00010110 0x814 hal/video.o + 0x000105cc HalVideoGetCursorPosition + 0x00010110 HalVideoSetHardwareCursor + 0x000106b0 HalVideoPrint + 0x00010298 HalVideoClearScreen + 0x0001051c HalVideoPutNullTerminatedString + 0x00010260 HalVideoSetCurrentColorAttribute + 0x0001032c HalVideoScroll + 0x00010578 HalVideoSetCursorPosition + 0x0001044c HalVideoPutChar + 0x000105e8 HalVideoInitialize + .text 0x00010924 0x58 hal/io.o + 0x00010944 in + 0x00010924 out + 0x00010968 iodelay + .text 0x0001097c 0x220 rtl/string.o + 0x00010a10 RtlIntToAscii + 0x0001097c strncpy + 0x000109e0 strlen + .text 0x00010b9c 0x34 rtl/memory.o + 0x00010b9c RtlSetMemory + .text 0x00010bd0 0x10b6 hal/intsupport.o + 0x00011c38 HalInterruptGenericHandler + 0x00010bd8 HalInterruptInitializePICs + 0x00010bd0 HalInterruptLoadIDTR + 0x00010c38 HalInterruptHandlerPreamble + 0x00010c2f HalIoDelay + 0x00010c48 HalInterruptHandlerPreambleAfter + 0x00010c38 HalInterruptHandlerPreambleBefore + *fill* 0x00011c86 0x2 2425393296 + .text 0x00011c88 0x2f4 hal/interrupts.o + 0x00011cbc HalDisableInterrupts + 0x00011e80 HalInitializeInterrupts + 0x00011cf8 HalInterruptSendIrqEoi + 0x00011cf0 HalDoNothing + 0x00011dd8 HalInterruptInitializeGate + 0x00011c88 HalEnableInterrupts + 0x00011e38 HalInterruptInstallHandler + .text 0x00011f7c 0xbc hal/timer.o + 0x00011f7c HalTimerInt + .text 0x00012038 0x5c hal/keyboard.o + 0x00012038 HalKeyboardInt + *fill* 0x00012094 0xc 2425393296 + .text 0x000120a0 0xe hal/timestamp.o + 0x000120a0 HalReadTimestampCounter + *fill* 0x000120ae 0x2 2425393296 + *(.gnu.warning) + +.fini + *(.fini) + 0x000120b0 PROVIDE (__etext, .) + 0x000120b0 PROVIDE (_etext, .) + 0x000120b0 PROVIDE (etext, .) + +.rodata 0x000120c0 0x246 + *(.rodata .rodata.* .gnu.linkonce.r.*) + .rodata 0x000120c0 0x60 init/main.o + .rodata 0x00012120 0x156 hal/video.o + *fill* 0x00012276 0x2 + .rodata 0x00012278 0x18 rtl/string.o + .rodata 0x00012290 0x68 hal/interrupts.o + .rodata 0x000122f8 0xe hal/keyboard.o + +.rodata1 + *(.rodata1) + +.eh_frame_hdr + *(.eh_frame_hdr) + 0x00013306 . = ( ALIGN (0x1000) + (. & 0xfff)) + 0x00012308 . = ALIGN (0x4) + 0x00012306 PROVIDE (__preinit_array_start, .) + +.preinit_array + *(.preinit_array) + 0x00012306 PROVIDE (__preinit_array_end, .) + 0x00012306 PROVIDE (__init_array_start, .) + +.init_array + *(.init_array) + 0x00012306 PROVIDE (__init_array_end, .) + 0x00012306 PROVIDE (__fini_array_start, .) + +.fini_array + *(.fini_array) + 0x00012306 PROVIDE (__fini_array_end, .) + +.data 0x00013308 0xc4 + *(.data .data.* .gnu.linkonce.d.*) + .data 0x00013308 0xa8 hal/video.o + 0x0001330e g_bSoftCursor + 0x00013308 g_pVideoMemory + 0x0001330c g_bScreenResolution + 0x00013310 g_bColorAttribute + .data 0x000133b0 0x4 hal/interrupts.o + 0x000133b0 g_uInterruptsEnabled + .data 0x000133b4 0x10 hal/timer.o + .data 0x000133c4 0x8 hal/counters.o + 0x000133c4 g_ullTimeStampCounter + +.data1 + *(.data1) + +.eh_frame + *(.eh_frame) + +.gcc_except_table + *(.gcc_except_table) + +.dynamic + *(.dynamic) + +.ctors + *crtbegin.o(.ctors) + *(EXCLUDE_FILE(*crtend.o) .ctors) + *(SORT(.ctors.*)) + *(.ctors) + +.dtors + *crtbegin.o(.dtors) + *(EXCLUDE_FILE(*crtend.o) .dtors) + *(SORT(.dtors.*)) + *(.dtors) + +.jcr + *(.jcr) + +.got + *(.got.plt) + *(.got) + 0x000133cc _edata = . + 0x000133cc PROVIDE (edata, .) + 0x000133cc __bss_start = . + +.bss 0x000133e0 0xc20 + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + .bss 0x000133e0 0x4 hal/timer.o + *(COMMON) + *fill* 0x000133e4 0x1c + COMMON 0x00013400 0xc00 hal/interrupts.o + 0x0 (size before relaxing) + 0x00013400 g_IDT + 0x00013c00 g_InterruptHandlerTable + 0x00014000 . = ALIGN (0x4) + 0x00014000 . = ALIGN (0x4) + 0x00014000 _end = . + 0x00014000 PROVIDE (end, .) + +.stab + *(.stab) + +.stabstr + *(.stabstr) + +.stab.excl + *(.stab.excl) + +.stab.exclstr + *(.stab.exclstr) + +.stab.index + *(.stab.index) + +.stab.indexstr + *(.stab.indexstr) + +.comment 0x00000000 0x1c5 + *(.comment) + .comment 0x00000000 0x1f init/entry.o + .comment 0x0000001f 0x28 init/main.o + .comment 0x00000047 0x28 hal/video.o + .comment 0x0000006f 0x28 hal/io.o + .comment 0x00000097 0x28 rtl/string.o + .comment 0x000000bf 0x28 rtl/memory.o + .comment 0x000000e7 0x1f hal/intsupport.o + .comment 0x00000106 0x28 hal/interrupts.o + .comment 0x0000012e 0x28 hal/timer.o + .comment 0x00000156 0x28 hal/keyboard.o + .comment 0x0000017e 0x1f hal/timestamp.o + .comment 0x0000019d 0x28 hal/counters.o + +.debug + *(.debug) + +.line + *(.line) + +.debug_srcinfo + *(.debug_srcinfo) + +.debug_sfnames + *(.debug_sfnames) + +.debug_aranges + *(.debug_aranges) + +.debug_pubnames + *(.debug_pubnames) + +.debug_info + *(.debug_info .gnu.linkonce.wi.*) + +.debug_abbrev + *(.debug_abbrev) + +.debug_line + *(.debug_line) + +.debug_frame + *(.debug_frame) + +.debug_str + *(.debug_str) + +.debug_loc + *(.debug_loc) + +.debug_macinfo + *(.debug_macinfo) + +.debug_weaknames + *(.debug_weaknames) + +.debug_funcnames + *(.debug_funcnames) + +.debug_typenames + *(.debug_typenames) + +.debug_varnames + *(.debug_varnames) +OUTPUT(kernel.tmp elf32-i386) + +.note 0x00000000 0xb4 + .note 0x00000000 0x14 init/main.o + .note 0x00000014 0x14 hal/video.o + .note 0x00000028 0x14 hal/io.o + .note 0x0000003c 0x14 rtl/string.o + .note 0x00000050 0x14 rtl/memory.o + .note 0x00000064 0x14 hal/interrupts.o + .note 0x00000078 0x14 hal/timer.o + .note 0x0000008c 0x14 hal/keyboard.o + .note 0x000000a0 0x14 hal/counters.o diff --git a/kernel/rtl/Makefile b/kernel/rtl/Makefile new file mode 100644 index 0000000..1767c0a --- /dev/null +++ b/kernel/rtl/Makefile @@ -0,0 +1,19 @@ +CC= gcc +CFLAGS= -Wall -imacros ../defines -I../inc +OBJS= string.o memory.o +RM= /bin/rm +NASM= nasm +NASMFLAGS= + +.SUFFIXES: .c .o .asm + +all: $(OBJS) + +.c.o: + $(CC) $(CFLAGS) -c $< + +.asm.o: + $(NASM) $(NASMFLAGS) -o $*.o $< + +clean: + $(RM) -f $(OBJS) *~ #*# .#* diff --git a/kernel/rtl/memory.c b/kernel/rtl/memory.c new file mode 100644 index 0000000..f4f845e --- /dev/null +++ b/kernel/rtl/memory.c @@ -0,0 +1,29 @@ +//+---------------------------------------------------------------------------- +// +// File: memory.c +// +// Module: +// +// Synopsis: +// +// Copyright (C) Scott Gasch +// +// Created: sgasch 5 Jul 2003 +// +//+---------------------------------------------------------------------------- + +#include "kernel.h" + +void +RtlSetMemory(void *pStart, + BYTE bFill, + ULONG uLength) +{ + BYTE *p = (BYTE *)pStart; + + while(uLength > 0) + { + *p++ = bFill; + uLength--; + } +} diff --git a/kernel/rtl/string.c b/kernel/rtl/string.c new file mode 100644 index 0000000..da42000 --- /dev/null +++ b/kernel/rtl/string.c @@ -0,0 +1,138 @@ +//+---------------------------------------------------------------------------- +// +// File: string.c +// +// Module: +// +// Synopsis: +// +// Copyright (C) 2003 Scott Gasch +// +// Created: sgasch 5 Jul 2003 +// +//+---------------------------------------------------------------------------- + +#include "kernel.h" + +CHAR * +strncpy(CHAR *pDest, CHAR *pSrc, SIZE_T u) +{ + char *p = pDest; + ULONG v = 0; + + while ((v < u) && (*pSrc)) + { + *pDest = *pSrc; + pDest++; pSrc++; v++; + } + + // + // If we ran out of space, null terminate the dest buffer + // + if ((*pSrc) && (u > 0)) + { + *(pDest - 1) = 0; + } + + return(p); +} + +SIZE_T +strlen(CHAR *p) +{ + ULONG i = 0; + while(*p) + { + i++; + p++; + } + return(i); +} + +CHAR * +RtlIntToAscii(INT i, + CHAR *buf, + ULONG uBufLen, + ULONG uBase) +{ + CHAR *p = (buf + uBufLen); + ULONG uSpaceLeft = uBufLen; + ULONG uMagnitude; + BOOL fNeg = FALSE; + + // + // null terminate the workspace buffer + // + if (uSpaceLeft == 0) + { + return(NULL); + } + *p = '\0'; + p--; + uSpaceLeft--; + + // + // Get the int's sign + // + if (i < 0) + { + fNeg = TRUE; + uMagnitude = -i; + } + else + { + uMagnitude = i; + } + + // + // Based on base, do the conversion. Build the string backwards from + // less significant digits. Stop if we finish or if we run out of + // uSpaceLeft. + // + switch(uBase) + { + case 10: + do + { + *p = (uMagnitude % 10) + '0'; + p--; + uSpaceLeft--; + uMagnitude /= 10; + } + while((uSpaceLeft > 0) && (uMagnitude != 0)); + break; + case 16: + do + { + *p = "0123456789ABCDEF"[uMagnitude & 15]; + p--; + uSpaceLeft--; + uMagnitude >>= 4; + } + while((uSpaceLeft > 0) && (uMagnitude != 0)); + break; + case 8: + do + { + *p = (uMagnitude & 7) + '0'; + p--; + uSpaceLeft--; + uMagnitude >>= 3; + } + while((uSpaceLeft > 0) && (uMagnitude != 0)); + break; + } + + if (TRUE == fNeg) + { + if (uSpaceLeft > 0) + { + *p = '-'; + } + } + else + { + p++; + } + return(p); +}