diff options
author | Andreas Baumann <mail@andreasbaumann.cc> | 2017-05-21 10:00:06 +0200 |
---|---|---|
committer | Andreas Baumann <mail@andreasbaumann.cc> | 2017-05-21 10:00:06 +0200 |
commit | a998c2b6ab7fbfc2b0a15a3de92632178c3564dd (patch) | |
tree | a86066741c7474f98809224bce2df42e53a0e4cd | |
parent | d0385783af3f7e3567f19330dede6a96fd80ddab (diff) | |
download | abaos-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.TODO | 4 | ||||
-rw-r--r-- | src/Makefile | 16 | ||||
-rw-r--r-- | src/boot.asm | 33 | ||||
-rw-r--r-- | src/interrupts.asm | 67 | ||||
-rw-r--r-- | src/interrupts.c | 49 | ||||
-rw-r--r-- | src/interrupts.h | 60 | ||||
-rw-r--r-- | src/kernel.c | 10 | ||||
-rw-r--r-- | src/stage1_functions.asm | 2 | ||||
-rw-r--r-- | src/stage2_a20.asm | 47 | ||||
-rw-r--r-- | src/stage2_check_magic.asm | 48 | ||||
-rw-r--r-- | src/stage2_functions.asm | 68 | ||||
-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 |