Enable CPU exception handling w/ IDT & TSS (#640)

This commit is contained in:
tkchia 2022-09-30 05:43:08 +08:00 committed by GitHub
parent 09811e739f
commit bc8532688b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 231 additions and 7 deletions

View file

@ -1137,12 +1137,17 @@ sconf: .short 1843200/*hz*/ / 16/*wut*/ / 9600/*baud*/
.endobj sconf,global,hidden .endobj sconf,global,hidden
// Global Descriptor Table // Global Descriptor Table
//
// @note address portion only concern legacy modes
.align 8 .align 8
gdt: .short 2f-1f # table byte length _gdtrphy:
.long REAL(1f),0 # table address .short 2f-1f-1 # table byte length
.zero 2 .long REAL(1f) # table address (physical space)
.endobj _gdtrphy,global,hidden
_gdtr:
.short 2f-1f-1 # table byte length
.quad 1f # table address (final virtual space)
.endobj _gdtr,global,hidden
.align 8
_gdt:
1: 1:
// G:granularity (1 limit *= 0x1000) // G:granularity (1 limit *= 0x1000)
// D/B:default operation size (0 = 16|64bit, 1 = 32-bit) // D/B:default operation size (0 = 16|64bit, 1 = 32-bit)
@ -1172,7 +1177,9 @@ gdt: .short 2f-1f # table byte length
.quad 0b0000000011001111100100100000000000000000000000001111111111111111 #32 .quad 0b0000000011001111100100100000000000000000000000001111111111111111 #32
.quad 0b0000000010101111100110110000000000000000000000001111111111111111 #40 .quad 0b0000000010101111100110110000000000000000000000001111111111111111 #40
.quad 0b0000000010101111100100110000000000000000000000001111111111111111 #48 .quad 0b0000000010101111100100110000000000000000000000001111111111111111 #48
2: .endobj gdt,global,hidden .tssdescstub _tss #56,64
2:
.endobj _gdt,global,hidden
/* /*
αcτµαlly pδrταblε εxεcµταblε § real mode αcτµαlly pδrταblε εxεcµταblε § real mode
@ -1433,7 +1440,7 @@ golong: cli
rdmsr rdmsr
or $EFER_LME|EFER_SCE,%eax or $EFER_LME|EFER_SCE,%eax
wrmsr wrmsr
lgdt REAL(gdt) lgdt REAL(_gdtrphy)
mov %cr0,%eax mov %cr0,%eax
or $CR0_PE|CR0_PG|CR0_MP,%eax or $CR0_PE|CR0_PG|CR0_MP,%eax
and $~CR0_EM,%eax and $~CR0_EM,%eax
@ -1451,6 +1458,8 @@ long: xor %eax,%eax
mov %eax,%fs mov %eax,%fs
mov %eax,%gs mov %eax,%gs
mov $0x80000,%esp mov $0x80000,%esp
mov $GDT_LONG_TSS,%al
ltr %ax
xor %r12d,%r12d xor %r12d,%r12d
xor %r13d,%r13d xor %r13d,%r13d
xor %r14d,%r14d xor %r14d,%r14d
@ -1464,6 +1473,8 @@ long: xor %eax,%eax
call __map_phdrs call __map_phdrs
push $0x037f push $0x037f
fldcw (%rsp) fldcw (%rsp)
lgdt _gdtr # reload GDTR for
# virtual memory space
movabs $kernel,%rax movabs $kernel,%rax
jmp *%rax jmp *%rax
.endfn long .endfn long

View file

@ -572,6 +572,9 @@ HIDDEN(v_ape_realslacksectors =
HIDDEN(v_ape_realpages = v_ape_realsectors / (4096 / 512)); HIDDEN(v_ape_realpages = v_ape_realsectors / (4096 / 512));
HIDDEN(v_ape_highsectors = HIDDEN(v_ape_highsectors =
(ROUNDUP(RVA(_edata), 512) / 512) - v_ape_realsectors); (ROUNDUP(RVA(_edata), 512) / 512) - v_ape_realsectors);
PROVIDE_HIDDEN(_tss = 0);
PROVIDE_HIDDEN(_tss_end = 0);
TSSDESCSTUB2(_tss, _tss, _tss_end ? _tss_end - _tss - 1 : 0);
#endif #endif
/* ZIP End of Central Directory header */ /* ZIP End of Central Directory header */

View file

@ -180,6 +180,12 @@
.stub \name\()_bcs\n,long .stub \name\()_bcs\n,long
.endm .endm
// Task State Segment Descriptor Entries.
.macro .tssdescstub name:req
.stub \name\()_desc_ent0,quad
.stub \name\()_desc_ent1,quad
.endm
/* clang-format on */ /* clang-format on */
#elif defined(__LINKER__) #elif defined(__LINKER__)
@ -257,5 +263,22 @@
(X) % 10 * 0x1000000 \ (X) % 10 * 0x1000000 \
: 0xffffffffffffffff) : 0xffffffffffffffff)
/**
* Laying out the GDT entries for a TSS for bare metal operation.
*/
#define TSSDESCSTUB2(SYM, BASE, LIM) \
HIDDEN(SYM##_desc_ent0 = TSSDESC_ENT0(BASE, LIM)); \
HIDDEN(SYM##_desc_ent1 = TSSDESC_ENT1(BASE)); \
ASSERT((LIM) >= 0 && (LIM) <= 0xffff, "bare metal TSS is suspiciously fat")
#define TSSDESC_ENT0(BASE, LIM) \
(((LIM) << 0 & 0x000000000000ffff) | \
((BASE) << 16 & 0x000000ffffff0000) | \
0x89 << 40 | \
((LIM) >> 16 << 48 & 0x000f000000000000) | \
0x2 << 52 | \
((BASE) >> 24 << 56 & 0xff00000000000000))
#define TSSDESC_ENT1(BASE) \
((BASE) >> 32 << 0 & 0x00000000ffffffff)
#endif /* __ASSEMBLER__ */ #endif /* __ASSEMBLER__ */
#endif /* APE_MACROS_H_ */ #endif /* APE_MACROS_H_ */

173
libc/intrin/interrupts.S Normal file
View file

@ -0,0 +1,173 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/macros.internal.h"
#include "libc/runtime/pc.internal.h"
// Code and data structures for bare metal interrupt handling.
#define ISR_STK_SZ 0x10000
#define ISR_STK_ALIGN 0x10
// Interrupt stack to use for IRQs. TODO.
#define IRQ_IST 1
// Interrupt stack to use for CPU exceptions.
#define EXCEP_IST 2
// Interrupt numbers to use for IRQs 0 & 8. TODO: implement these!
#define IRQ0 0x20
#define IRQ8 0x28
.init.start 100,_init_isr
push %rdi
call isr_init
pop %rdi
.init.end 100,_init_isr
// Interrupt service routines for CPU exceptions 031.
i = 31
.rept 30
push %rsi # preserve rsi
mov $i,%sil # rsi = exception number
1: jmp 1f # 🦘
i = i - 1
.endr
__excep1_isr:
push %rsi
mov $1,%sil
1: jmp 1f
__excep0_isr:
push %rsi
xor %esi,%esi
1: test $8,%esp # if no error code was pushed,
jnz 2f # stuff our own
pushq (%rsp)
orq $-1,8(%rsp)
2: movzbq %sil,%rsi # zero-extend the exception number
push %rcx # preserve registers which we will
push %rdx # use to call kprintf
push %r8
mov 40(%rsp),%rcx # edx:rcx = "caller" cs:rip
mov 48(%rsp),%edx
mov 32(%rsp),%r8 # r8 = error code
push %rax # preserve other call-used registers
push %rdi
push %r9
push %r10
push %r11
mov %ss,%eax # preserve ds, es, ss
push %rax
mov %ds,%eax
push %rax
mov %es,%eax
push %rax
mov $GDT_LONG_DATA,%eax # ...& load ds, es, ss correctly
mov %eax,%ss
mov %eax,%ds
mov %eax,%es
ezlea .excep_msg,di # stack should be 16-byte aligned now
xor %eax,%eax # kprintf is variadic; remember to
# pass no. of vector regs. used (= 0)
call kprintf # print error message
cli
9: hlt
jmp 9b
/* TODO: link up with sigaction etc. */
// Initialization code for setting up a Task State Segment (TSS) &
// Interrupt Descriptor Table (IDT) in bare metal mode, to start
// processing exceptions & asynchronous IRQs.
isr_init:
testb IsMetal()
jz 9f
ezlea _tss+0x24,di # fill up TSS; we already loaded
# task register in ape/ape.S
ezlea _isr_stk_1+ISR_STK_SZ,ax
and $-ISR_STK_ALIGN,%al # be paranoid & enforce correct
stosq # alignment of stack pointers
ezlea _isr_stk_2+ISR_STK_SZ,ax
and $-ISR_STK_ALIGN,%al
stosq
add $0x66-0x34,%rdi
movw $_tss_iopb-_tss,%ax
stosw
lidt _idtr # load IDTR
ezlea _idt,di
pushpop 32,%rcx # fill IDT entries for CPU exceptions
ezlea __excep0_isr,dx
1: mov %edx,%eax
stosw
mov %cs,%eax
stosw
mov %rdx,%rax
// P:present
// DPL:privilege
// system segment (0)
// gate type (interrupt gate, i.e. disable IRQs)
// reserved
// IST:interrupt stack table
//
mov $0b1000111000000000|EXCEP_IST,%ax
stosl
shr $32,%rax
stosq
add $__excep1_isr-__excep0_isr,%rdx
loop 1b
9: ret
// String constants.
.rodata.str1.1
.excep_msg:
.asciz "CPU exception %d @ %#llx:%#llx err code %#llx\n"
.previous
// IDTR value.
.rodata
_idtr: .short _idt_end-_idt-1
.quad _idt
.endobj _idtr,globl,hidden
.balign 8
.previous
.bss
// Space for the Task State Segment.
_tss:
.space 0x68
_tss_iopb:
_tss_end:
.endobj _tss,globl,hidden
.endobj _tss_end,globl,hidden
// Space for the Interrupt Descriptor Table.
_idt: .space (MAX(IRQ0,IRQ8)+8)*0x10
_idt_end:
.endobj _idt,globl,hidden
.previous
// Interrupt stacks.
.lcomm _isr_stk_1,ISR_STK_SZ
.lcomm _isr_stk_2,ISR_STK_SZ

View file

@ -28,6 +28,10 @@
#include "libc/runtime/internal.h" #include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#if SupportsMetal()
STATIC_YOINK("_idt");
#endif
/** /**
* Aborts process after printing a backtrace. * Aborts process after printing a backtrace.
* *

View file

@ -16,9 +16,18 @@
#define IS2POW(X) (!((X) & ((X)-1))) #define IS2POW(X) (!((X) & ((X)-1)))
#define ROUNDUP(X, K) (((X) + (K)-1) & -(K)) #define ROUNDUP(X, K) (((X) + (K)-1) & -(K))
#define ROUNDDOWN(X, K) ((X) & -(K)) #define ROUNDDOWN(X, K) ((X) & -(K))
#ifndef __ASSEMBLER__
#define ABS(X) ((X) >= 0 ? (X) : -(X)) #define ABS(X) ((X) >= 0 ? (X) : -(X))
#define MIN(X, Y) ((Y) > (X) ? (X) : (Y)) #define MIN(X, Y) ((Y) > (X) ? (X) : (Y))
#define MAX(X, Y) ((Y) < (X) ? (X) : (Y)) #define MAX(X, Y) ((Y) < (X) ? (X) : (Y))
#else
// The GNU assembler does not grok the ?: ternary operator; furthermore,
// boolean expressions yield -1 and 0 for "true" and "false", not 1 and 0.
#define __MAPBOOL(P) (!!(P) / (!!(P) + !(P)))
#define __IFELSE(P, X, Y) (__MAPBOOL(P) * (X) + __MAPBOOL(!(P)) * (Y))
#define MIN(X, Y) (__IFELSE((Y) > (X), (X), (Y)))
#define MAX(X, Y) (__IFELSE((Y) < (X), (X), (Y)))
#endif
#define PASTE(A, B) __PASTE(A, B) #define PASTE(A, B) __PASTE(A, B)
#define STRINGIFY(A) __STRINGIFY(A) #define STRINGIFY(A) __STRINGIFY(A)
#define EQUIVALENT(X, Y) (__builtin_constant_p((X) == (Y)) && ((X) == (Y))) #define EQUIVALENT(X, Y) (__builtin_constant_p((X) == (Y)) && ((X) == (Y)))

View file

@ -120,6 +120,7 @@
#define GDT_LEGACY_DATA 32 #define GDT_LEGACY_DATA 32
#define GDT_LONG_CODE 40 #define GDT_LONG_CODE 40
#define GDT_LONG_DATA 48 #define GDT_LONG_DATA 48
#define GDT_LONG_TSS 56
#define PIC1 0x20 /* IO base address for master PIC */ #define PIC1 0x20 /* IO base address for master PIC */
#define PIC2 0xA0 /* IO base address for slave PIC */ #define PIC2 0xA0 /* IO base address for slave PIC */