mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Enable CPU exception handling w/ IDT & TSS (#640)
This commit is contained in:
parent
09811e739f
commit
bc8532688b
7 changed files with 231 additions and 7 deletions
25
ape/ape.S
25
ape/ape.S
|
@ -1137,12 +1137,17 @@ sconf: .short 1843200/*hz*/ / 16/*wut*/ / 9600/*baud*/
|
|||
.endobj sconf,global,hidden
|
||||
|
||||
// Global Descriptor Table
|
||||
//
|
||||
// @note address portion only concern legacy modes
|
||||
.align 8
|
||||
gdt: .short 2f-1f # table byte length
|
||||
.long REAL(1f),0 # table address
|
||||
.zero 2
|
||||
_gdtrphy:
|
||||
.short 2f-1f-1 # table byte length
|
||||
.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:
|
||||
// ┌G:granularity (1 → limit *= 0x1000)
|
||||
// │┌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 0b0000000010101111100110110000000000000000000000001111111111111111 #40
|
||||
.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 │
|
||||
|
@ -1433,7 +1440,7 @@ golong: cli
|
|||
rdmsr
|
||||
or $EFER_LME|EFER_SCE,%eax
|
||||
wrmsr
|
||||
lgdt REAL(gdt)
|
||||
lgdt REAL(_gdtrphy)
|
||||
mov %cr0,%eax
|
||||
or $CR0_PE|CR0_PG|CR0_MP,%eax
|
||||
and $~CR0_EM,%eax
|
||||
|
@ -1451,6 +1458,8 @@ long: xor %eax,%eax
|
|||
mov %eax,%fs
|
||||
mov %eax,%gs
|
||||
mov $0x80000,%esp
|
||||
mov $GDT_LONG_TSS,%al
|
||||
ltr %ax
|
||||
xor %r12d,%r12d
|
||||
xor %r13d,%r13d
|
||||
xor %r14d,%r14d
|
||||
|
@ -1464,6 +1473,8 @@ long: xor %eax,%eax
|
|||
call __map_phdrs
|
||||
push $0x037f
|
||||
fldcw (%rsp)
|
||||
lgdt _gdtr # reload GDTR for
|
||||
# virtual memory space
|
||||
movabs $kernel,%rax
|
||||
jmp *%rax
|
||||
.endfn long
|
||||
|
|
|
@ -572,6 +572,9 @@ HIDDEN(v_ape_realslacksectors =
|
|||
HIDDEN(v_ape_realpages = v_ape_realsectors / (4096 / 512));
|
||||
HIDDEN(v_ape_highsectors =
|
||||
(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
|
||||
|
||||
/* ZIP End of Central Directory header */
|
||||
|
|
|
@ -180,6 +180,12 @@
|
|||
.stub \name\()_bcs\n,long
|
||||
.endm
|
||||
|
||||
// Task State Segment Descriptor Entries.
|
||||
.macro .tssdescstub name:req
|
||||
.stub \name\()_desc_ent0,quad
|
||||
.stub \name\()_desc_ent1,quad
|
||||
.endm
|
||||
|
||||
/* clang-format on */
|
||||
#elif defined(__LINKER__)
|
||||
|
||||
|
@ -257,5 +263,22 @@
|
|||
(X) % 10 * 0x1000000 \
|
||||
: 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 /* APE_MACROS_H_ */
|
||||
|
|
173
libc/intrin/interrupts.S
Normal file
173
libc/intrin/interrupts.S
Normal 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 0—31.
|
||||
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
|
|
@ -28,6 +28,10 @@
|
|||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
||||
#if SupportsMetal()
|
||||
STATIC_YOINK("_idt");
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Aborts process after printing a backtrace.
|
||||
*
|
||||
|
|
|
@ -16,9 +16,18 @@
|
|||
#define IS2POW(X) (!((X) & ((X)-1)))
|
||||
#define ROUNDUP(X, K) (((X) + (K)-1) & -(K))
|
||||
#define ROUNDDOWN(X, K) ((X) & -(K))
|
||||
#ifndef __ASSEMBLER__
|
||||
#define ABS(X) ((X) >= 0 ? (X) : -(X))
|
||||
#define MIN(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 STRINGIFY(A) __STRINGIFY(A)
|
||||
#define EQUIVALENT(X, Y) (__builtin_constant_p((X) == (Y)) && ((X) == (Y)))
|
||||
|
|
|
@ -120,6 +120,7 @@
|
|||
#define GDT_LEGACY_DATA 32
|
||||
#define GDT_LONG_CODE 40
|
||||
#define GDT_LONG_DATA 48
|
||||
#define GDT_LONG_TSS 56
|
||||
|
||||
#define PIC1 0x20 /* IO base address for master PIC */
|
||||
#define PIC2 0xA0 /* IO base address for slave PIC */
|
||||
|
|
Loading…
Reference in a new issue