summaryrefslogtreecommitdiff
path: root/src
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 /src
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
Diffstat (limited to 'src')
-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
11 files changed, 305 insertions, 95 deletions
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