summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2017-05-21 10:00:06 +0200
committerAndreas Baumann <mail@andreasbaumann.cc>2017-05-21 10:00:06 +0200
commita998c2b6ab7fbfc2b0a15a3de92632178c3564dd (patch)
treea86066741c7474f98809224bce2df42e53a0e4cd
parentd0385783af3f7e3567f19330dede6a96fd80ddab (diff)
downloadabaos-a998c2b6ab7fbfc2b0a15a3de92632178c3564dd.tar.gz
abaos-a998c2b6ab7fbfc2b0a15a3de92632178c3564dd.tar.bz2
added implementation of interrupts (IDT construction and loading), currently not
working yet. added A20 gate check reorganized some code, so it's easier to debug interrupt handlers in assembly
-rw-r--r--doc/LINKS.TODO4
-rw-r--r--src/Makefile16
-rw-r--r--src/boot.asm33
-rw-r--r--src/interrupts.asm67
-rw-r--r--src/interrupts.c49
-rw-r--r--src/interrupts.h60
-rw-r--r--src/kernel.c10
-rw-r--r--src/stage1_functions.asm2
-rw-r--r--src/stage2_a20.asm47
-rw-r--r--src/stage2_check_magic.asm48
-rw-r--r--src/stage2_functions.asm68
-rw-r--r--src/stage2_switch_mode.asm (renamed from src/switch_mode.asm)0
12 files changed, 309 insertions, 95 deletions
diff --git a/doc/LINKS.TODO b/doc/LINKS.TODO
index 02b72c8..33fc5bb 100644
--- a/doc/LINKS.TODO
+++ b/doc/LINKS.TODO
@@ -15,6 +15,7 @@ https://pdos.csail.mit.edu/6.828/2014/reference.html
tutorials:
https://littleosbook.github.io
+http://www.henkessoft.de/OS_Dev/OS_Dev1.htm
In C#:
https://github.com/FlingOS/FlingOS
@@ -28,3 +29,6 @@ http://wiki.osdev.org/Interrupt_Descriptor_Table
http://beefchunk.com/documentation/hardware/microprocessors/intel/i80386/Chap9.html
http://www.cs.cmu.edu/~ralf/files.html
https://blog.packagecloud.io/eng/2016/04/05/the-definitive-guide-to-linux-system-calls/
+
+A20:
+http://www.independent-software.com/writing-your-own-toy-operating-system-enabling-the-a20-line/
diff --git a/src/Makefile b/src/Makefile
index e46db57..9ffa885 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -8,18 +8,18 @@ MAGIC := $(shell printf '%x' `date +%s`)
all: image.bin kernel.sym
+# truncate to correct number of sectors, we have
+# 512 (boot, stage 1) + N * 512 (N currenty is 3, stage 2) = 2048 for boot.bin
+# + M * 512 (M is currently 7) = 3144 for kernel.bin
+# + 1 * 512 = 512 for magic.bin
+# (M + N + 1 is the number of sectors to be read in stage 2, as stage 1
+# loads only the first sector, so adapt NOF_LOAD_SECTORS to 18)
image.bin: boot.bin kernel.bin magic.bin
cat boot.bin kernel.bin > image.tmp
- # truncate to correct number of sectors, we have
- # 512 (boot, stage 1) + N * 512 (N currenty is 3, stage 2) = 2048 for boot.bin
- # + M * 512 (M is currently 7) = 3144 for kernel.bin
- # + 1 * 512 = 512 for magic.bin
- # (M + N + 1 is the number of sectors to be read in stage 2, as stage 1
- # loads only the first sector, so adapt NOF_LOAD_SECTORS to 16)
- truncate -s 8192 image.tmp
+ truncate -s 9216 image.tmp
cat image.tmp magic.bin > image.bin
-boot.bin: boot.asm boot_gdt.asm stage1_functions.asm stage2_functions.asm switch_mode.asm
+boot.bin: boot.asm boot_gdt.asm stage1_functions.asm stage2_functions.asm stage2_switch_mode.asm stage2_a20.asm
$(NASM) boot.asm -DMAGIC='"$(MAGIC)"' -f bin -o boot.bin
kernel.bin: kernel.elf
diff --git a/src/boot.asm b/src/boot.asm
index 8fb5d8a..1a7c141 100644
--- a/src/boot.asm
+++ b/src/boot.asm
@@ -79,6 +79,18 @@ stage2:
mov si, MESSAGE_STAGE_2_LOADED
call print_string
+; check A20 gate
+ call check_A20_enabled
+ cmp ax, 1
+ je A20_ENABLED
+ mov si, MESSAGE_STAGE_2_A20_DISABLED
+ call print_string
+ jmp HALT_OS_REAL
+
+A20_ENABLED:
+ mov si, MESSAGE_STAGE_2_A20_ENABLED
+ call print_string
+
; remember current position on screen, otherwise we loose this
; information after switching to protected mode
call current_row
@@ -90,10 +102,28 @@ stage2:
; we should never get here, but just in case
jmp $
+; stop loading in real mode
+HALT_OS_REAL:
+ mov si, MESSAGE_HALTED_REAL
+ call print_string
+.loop: cli
+ hlt
+ jmp .loop
+
MESSAGE_STAGE_2_LOADED:
db "Stage 2 boot sectors loaded", 13, 10, 0
+
+MESSAGE_STAGE_2_A20_ENABLED:
+ db "A20 address gate is enabled", 13, 10, 0
+
+MESSAGE_STAGE_2_A20_DISABLED:
+ db "A20 address gate is disabled", 13, 10, 0
+
+MESSAGE_HALTED_REAL:
+ db "Operating system halted in real mode", 13, 10, 0
-%include "switch_mode.asm"
+%include "stage2_switch_mode.asm"
+%include "stage2_a20.asm"
[bits 32]
@@ -145,6 +175,7 @@ MESSAGE_HALTED:
db "Operating system halted", 0
%include "stage2_functions.asm"
+%include "stage2_check_magic.asm"
; make sure we have full sectors, stage one is 512 bytes, so we
; have to will up 3 sectors
diff --git a/src/interrupts.asm b/src/interrupts.asm
index 2a09783..fd5f4a3 100644
--- a/src/interrupts.asm
+++ b/src/interrupts.asm
@@ -2,6 +2,8 @@
global interrupts_enable
global interrupts_disable
+global interrupts_load_idt
+extern interrupts_handle_interrupt
; void interrupts_enable( void )
interrupts_enable:
@@ -17,4 +19,67 @@ interrupts_disable:
leave
ret
-; uint32_t interrpts_handle_interrupt( uint8_t interrupt_no, uint32_t esp );
+%include "stage2_functions.asm"
+
+extern idt_pointer
+
+; void interrupts_load_idt( interrupt_descriptor_table_pointer_t *idt_pointer )
+interrupts_load_idt:
+ push ebp
+ mov ebp, esp
+ mov ecx, [ebp+8]
+ lidt [idt_pointer]
+ leave
+ ret
+
+; the handler to ignore interrupts
+global interrupts_ignore_request
+interrupts_ignore_request:
+ pusha
+ push si
+ mov si, IGNINTR
+ call pm_print_string
+ pop si
+ popa
+ iret
+
+IGNINTR:
+ db "IGNINTR ", 0
+
+; void interrupts_handle_request_0x00( );
+%macro exception_stub 1
+global interrupts_handle_request_%1
+interrupts_handle_request_%1:
+ mov [interrupt_no], byte %1
+%endmacro
+
+exception_stub 0x00
+
+int_entry:
+ ; safe state of interrupted code
+ pusha
+ push ds
+ push es
+ push fs
+ push gs
+
+ ; call the static C handler with the correct interrupt number
+ ; uint32_t interrupts_handle_interrupt( uint8_t interrupt_no, uint32_t esp );
+ push esp
+ mov eax, [interrupt_no]
+ push eax
+ call interrupts_handle_interrupt
+ add esp, 8
+; mov esp, eax
+
+ ; restore state
+ pop gs
+ pop fs
+ pop es
+ pop ds
+ popa
+
+ iret
+
+interrupt_no:
+ db 0
diff --git a/src/interrupts.c b/src/interrupts.c
index c456774..2355aec 100644
--- a/src/interrupts.c
+++ b/src/interrupts.c
@@ -3,14 +3,57 @@
#include "string.h"
#include "stdio.h"
-void interrupts_init( interrupt_t *interrupts )
+// TODO: for now the code segment is the first entry in the boot GDT
+#define GDT_CODE_SEGMENT_SELECTOR 8
+
+// the Present bit in a IDT entry
+#define IDT_PRESENT_BIT 0x80
+
+// DPL rings
+#define KERNEL_RING 0
+
+// types of IDT entries
+#define IDT_TYPE_INTERRUPT_GATE 0xE
+
+interrupt_descriptor_table_pointer_t idt_pointer;
+
+void interrupts_init( interrupt_t *interrupt )
+{
+ memset( interrupt, 0, sizeof( interrupt_t ) );
+
+ for( int i = 0; i < NOF_INTERRUPT_GATES; i++ ) {
+ interrupts_register_interrupt( interrupt, i, GDT_CODE_SEGMENT_SELECTOR,
+ &interrupts_ignore_request, KERNEL_RING, IDT_TYPE_INTERRUPT_GATE );
+ }
+
+ // divide-by-zero exception 0x00
+ interrupts_register_interrupt( interrupt, 0x00, GDT_CODE_SEGMENT_SELECTOR,
+ &interrupts_handle_request_0x00, KERNEL_RING, IDT_TYPE_INTERRUPT_GATE );
+
+ idt_pointer.size = 256 * sizeof( interrupt_gate_descriptor_t ) - 1;
+ idt_pointer.base = (uint32_t)interrupt->descriptor_table;
+
+ printf( "IDT pointer at 0x%X\n", &idt_pointer );
+
+ interrupts_load_idt( &idt_pointer );
+}
+
+void interrupts_register_interrupt( interrupt_t *interrupts,
+ uint8_t interrupt_no, uint16_t gdt_code_segment_selector,
+ void (*handler)( ), uint8_t privilege_level, uint8_t descriptor_type )
{
- memset( interrupts, 0, sizeof( interrupt_t ) );
+ interrupt_gate_descriptor_t *descr = &interrupts->descriptor_table[interrupt_no];
+
+ descr->handler_address_low_bits = ( (uint32_t )handler ) & 0xFFFF;
+ descr->handler_address_high_bits = ( ( (uint32_t )handler ) >> 16 ) & 0xFFFF;
+ descr->gdt_code_segment_selector = gdt_code_segment_selector;
+ descr->access = IDT_PRESENT_BIT | (( privilege_level & 0x3 ) << 5 ) | descriptor_type;
+ descr->reserved = 0;
}
uint32_t interrupts_handle_interrupt( uint8_t interrupt_no, uint32_t esp )
{
- puts( "interrupt" );
+ printf( "interrupt 0x%X with ESP 0x%X", interrupt_no, esp );
// for now, we are using the same stack for kernel and interrupt
// handlers (n task switching)
diff --git a/src/interrupts.h b/src/interrupts.h
index d4ceab6..42f6a5b 100644
--- a/src/interrupts.h
+++ b/src/interrupts.h
@@ -3,41 +3,59 @@
#include <stdint.h>
+// TCC bug (TODO: check, still there?), gcc croaks about
+// member pack alignments of things which cannot be packed like
+// uint8_t, so we define a macro depending on the compiler
+#if defined( __clang__ )
+#define MEMBER_ATTRIBUTE_PACKED
+#endif
+
+#if defined( __GNUC__ )
+#define MEMBER_ATTRIBUTE_PACKED
+#endif
+
+#if defined( __TINYC__ )
+#define MEMBER_ATTRIBUTE_PACKED __attribute__( ( packed ) )
+#endif
+
+// packed IDT structure, note TCC needs every member to have a packed
+// attribute, GCC/Clang are happy with the packed attribute at the
+// structure level
typedef struct {
-
-} interrupt_gate_descriptor_t;
+ uint16_t handler_address_low_bits MEMBER_ATTRIBUTE_PACKED;
+ uint16_t gdt_code_segment_selector MEMBER_ATTRIBUTE_PACKED;
+ uint8_t reserved MEMBER_ATTRIBUTE_PACKED;
+ uint8_t access MEMBER_ATTRIBUTE_PACKED;
+ uint16_t handler_address_high_bits MEMBER_ATTRIBUTE_PACKED;
+} __attribute__( ( packed ) ) interrupt_gate_descriptor_t;
+
+#define NOF_INTERRUPT_GATES 256
typedef struct {
-
+ interrupt_gate_descriptor_t descriptor_table[NOF_INTERRUPT_GATES];
} interrupt_t;
+typedef struct {
+ uint16_t size MEMBER_ATTRIBUTE_PACKED;
+ uint32_t base MEMBER_ATTRIBUTE_PACKED;
+} __attribute__( ( packed ) ) interrupt_descriptor_table_pointer_t;
+
void interrupts_enable( void );
void interrupts_disable( void );
-
void interrupts_init( interrupt_t *interrupt );
+void interrupts_register_interrupt( interrupt_t *interrupt,
+ uint8_t interrupt_no, uint16_t gdt_code_segment_selector,
+ void (*handler)( ), uint8_t privilege_level, uint8_t descriptor_type );
+void interrupts_load_idt( interrupt_descriptor_table_pointer_t *idt_pointer );
uint32_t interrupts_handle_interrupt( uint8_t interrupt_no, uint32_t esp );
+void interrupts_ignore_request( );
+void interrupts_handle_request_0x00( );
+
// initialize IDT
// handle gates
// assembly trampolines calling C (static) functions
// later: tasks and stacks, queues
- //~ struct GateDescriptor
- //~ {
- //~ myos::common::uint16_t handlerAddressLowBits;
- //~ myos::common::uint16_t gdt_codeSegmentSelector;
- //~ myos::common::uint8_t reserved;
- //~ myos::common::uint8_t access;
- //~ myos::common::uint16_t handlerAddressHighBits;
- //~ } __attribute__((packed));
-//~ struct IDTDescr {
- //~ uint16_t offset_1; // offset bits 0..15
- //~ uint16_t selector; // a code segment selector in GDT or LDT
- //~ uint8_t zero; // unused, set to 0
- //~ uint8_t type_attr; // type and attributes, see below
- //~ uint16_t offset_2; // offset bits 16..31
-//~ };
-
-
#endif // INTERRUPTS_H
diff --git a/src/kernel.c b/src/kernel.c
index a3ff1d7..44f1bad 100644
--- a/src/kernel.c
+++ b/src/kernel.c
@@ -30,11 +30,11 @@ void entry( void )
puts( "Initializing interrupts" );
interrupt_t interrupt;
interrupts_init( &interrupt );
- //~ interrupts_enable( );
+ interrupts_enable( );
- int y = 1;
- int x = 12 / y;
- printf( "Hex number is 0x%X and string is '%s'\n", x, "abaos" );
+ //~ int y = 1;
+ //~ int x = 12 / y;
+ //~ printf( "Hex number is 0x%X and string is '%s'\n", x, "abaos" );
console_put_string( &console, "Running.." );
@@ -49,7 +49,7 @@ void entry( void )
serial_put_char( &serial, '.' );
}
vga_put_char_at( &vga, x_pos, y_pos, bar[i%4] );
- for( int j = 0; j < 1500; j++ ) {
+ for( int j = 0; j < 150000; j++ ) {
}
}
vga_put_char_at( &vga, x_pos, y_pos, '.' );
diff --git a/src/stage1_functions.asm b/src/stage1_functions.asm
index 440b4be..7c66fc8 100644
--- a/src/stage1_functions.asm
+++ b/src/stage1_functions.asm
@@ -2,7 +2,7 @@
; 3 * 512 for bootloader stage2 and the kernel code
; (note: the first sector gets loaded by the BIOS, so
; subtract 1 here!)
-NOF_LOAD_SECTORS equ 16
+NOF_LOAD_SECTORS equ 18
; IN bx: begin of memory area to dump
; IN ax: number of words to dump
diff --git a/src/stage2_a20.asm b/src/stage2_a20.asm
new file mode 100644
index 0000000..094407b
--- /dev/null
+++ b/src/stage2_a20.asm
@@ -0,0 +1,47 @@
+; functions to handle A20 address line status and switching
+; returns 0 if not enabled, 1 if enabled in AX
+check_A20_enabled:
+ pushf
+ push ds
+ push es
+ push di
+ push si
+
+ cli
+
+ xor ax, ax
+ mov es, ax
+ mov di, 0x0500 ; es:di = 0000:0500
+
+ mov ax, 0xffff
+ mov ds, ax
+ mov si, 0x0510 ; ds:si = ffff:0510
+
+ mov al, byte [es:di] ; preserve values in memory
+ push ax ; on stack
+ mov al, byte [ds:si]
+ push ax
+
+ mov byte [es:di], 0x00 ; now the test: write 0x00 to 0000:0500
+ mov byte [ds:si], 0xFF ; write 0xff to ffff:0510
+
+ cmp byte [es:di], 0xFF ; memory wrap? A20 not enabled
+
+ pop ax ; restore original memory contents
+ mov byte [ds:si], al
+ pop ax
+ mov byte [es:di], al
+
+ mov ax, 0 ; wrapped around (last cmp)
+ je .exit
+
+ mov ax, 1 ; not wrapped around
+
+.exit:
+ pop si
+ pop di
+ pop es
+ pop ds
+ popf
+
+ ret
diff --git a/src/stage2_check_magic.asm b/src/stage2_check_magic.asm
new file mode 100644
index 0000000..43bbb95
--- /dev/null
+++ b/src/stage2_check_magic.asm
@@ -0,0 +1,48 @@
+; check whether the end of the loaded image contains in fact the magic
+; string (avoid truncation of image)
+check_magic:
+ push ebx
+ push ecx
+ push edx
+ push esi
+ push edi
+ mov eax, NOF_LOAD_SECTORS ; number of 512-byte sectors
+ shl eax, 9 ; 512 bytes per sector
+ mov edx, 0x7e00 ; offset of stage 2
+ add edx, eax
+ sub edx, MAGICLEN ; subtract the length of the magic string
+ mov esi, edx ; now use edx as first string address to compare to
+ mov edi, COMPARE_MAGIC ; position of second string
+ mov ecx, MAGICLEN ; length of the magic string
+ repe cmpsb
+ jne .ok
+ jmp .mismatch
+.ok:
+ mov si, MAGIC_OK_MSG
+ call pm_print_string
+ call pm_print_newline
+ xor eax, eax
+ jmp .end
+.mismatch:
+ mov si, MAGIC_NOT_OK_MSG
+ call pm_print_string
+ call pm_print_newline
+ xor eax, eax
+ mov eax, 1
+.end:
+ pop edi
+ pop esi
+ pop edx
+ pop ecx
+ pop ebx
+ ret
+
+COMPARE_MAGIC:
+db "ABAOS", %[MAGIC], 0
+MAGICLEN equ $ - COMPARE_MAGIC
+
+MAGIC_NOT_OK_MSG:
+db "Magic signature found", 0
+
+MAGIC_OK_MSG:
+db "Magic signature not found!", 0
diff --git a/src/stage2_functions.asm b/src/stage2_functions.asm
index 37d8075..f3c348b 100644
--- a/src/stage2_functions.asm
+++ b/src/stage2_functions.asm
@@ -59,25 +59,25 @@ pm_print_char:
pm_print_hex:
push bx
push si
- mov si, HEX_TEMPLATE
+ mov si, PM_HEX_TEMPLATE
mov bx, dx
shr bx, 12
- mov bx, [HEXABET+bx]
- mov [HEX_TEMPLATE+2], bl
+ mov bx, [PM_HEXABET+bx]
+ mov [PM_HEX_TEMPLATE+2], bl
mov bx, dx
and bx, 0x0FFF
shr bx, 8
- mov bx, [HEXABET+bx]
- mov [HEX_TEMPLATE+3], bl
+ mov bx, [PM_HEXABET+bx]
+ mov [PM_HEX_TEMPLATE+3], bl
mov bx, dx
and bx, 0x00FF
shr bx, 4
- mov bx, [HEXABET+bx]
- mov [HEX_TEMPLATE+4], bl
+ mov bx, [PM_HEXABET+bx]
+ mov [PM_HEX_TEMPLATE+4], bl
mov bx, dx
and bx, 0x000F
- mov bx, [HEXABET+bx]
- mov [HEX_TEMPLATE+5], bl
+ mov bx, [PM_HEXABET+bx]
+ mov [PM_HEX_TEMPLATE+5], bl
call pm_print_string
pop si
pop bx
@@ -169,51 +169,9 @@ read_hardware_vga_cursor:
pop eax
ret
-; check whether the end of the loaded image contains in fact the magic
-; string (avoid truncation of image)
-check_magic:
- push ebx
- push ecx
- push edx
- push esi
- push edi
- mov eax, NOF_LOAD_SECTORS ; number of 512-byte sectors
- shl eax, 9 ; 512 bytes per sector
- mov edx, 0x7e00 ; offset of stage 2
- add edx, eax
- sub edx, MAGICLEN ; subtract the length of the magic string
- mov esi, edx ; now use edx as first string address to compare to
- mov edi, COMPARE_MAGIC ; position of second string
- mov ecx, MAGICLEN ; length of the magic string
- repe cmpsb
- jne .ok
- jmp .mismatch
-.ok:
- mov si, MAGIC_OK_MSG
- call pm_print_string
- call pm_print_newline
- xor eax, eax
- jmp .end
-.mismatch:
- mov si, MAGIC_NOT_OK_MSG
- call pm_print_string
- call pm_print_newline
- xor eax, eax
- mov eax, 1
-.end:
- pop edi
- pop esi
- pop edx
- pop ecx
- pop ebx
- ret
-
-COMPARE_MAGIC:
-db "ABAOS", %[MAGIC], 0
-MAGICLEN equ $ - COMPARE_MAGIC
+PM_HEX_TEMPLATE:
+ db '0x???? ', 0
-MAGIC_NOT_OK_MSG:
-db "Magic signature found", 0
+PM_HEXABET:
+ db '0123456789ABCDEF'
-MAGIC_OK_MSG:
-db "Magic signature not found!", 0
diff --git a/src/switch_mode.asm b/src/stage2_switch_mode.asm
index 4082fda..4082fda 100644
--- a/src/switch_mode.asm
+++ b/src/stage2_switch_mode.asm