/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ │ vi: set noet ft=asm ts=8 sw=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/intrin/kprintf.h" #include "libc/macros.h" #include "libc/intrin/kprintf.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 # kangeroo 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 push %r9 mov 48(%rsp),%rcx # edx:rcx = 'caller' cs:rip mov 56(%rsp),%edx mov 40(%rsp),%r8 # r8 = error code mov %cr2,%r9 # r9 = cr2, in case it is useful 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 cld # make sure DF is reset, for C code 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) .weak kprintf # weakly link kprintf() because we ezlea kprintf,bx # want to keep examples/life tiny test %ebx,%ebx jz 8f call *%rbx # print error message 8: 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 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 mov $GDT_LONG_TSS,%cl # load task register (cx = 0 here) ltr %cx 9: ret // String constants. .rodata.str1.1 .excep_msg: .ascii "\033[1;31mCPU exception %d @ %#llx:%#llx err code %#llx " .asciz "cr2 %#llx\33[0m\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