// Copyright 2016 The Fuchsia Authors // // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // The size of the `clac` instruction #define CLAC_SIZE 3 // Early boot shared IDT structure struct idt _idt_startup; // IDT after early boot struct idt _idt __ALIGNED(PAGE_SIZE); // Read-only remapping of the IDT static struct idt* _idt_ro; static inline void idt_set_segment_sel(struct idt_entry* entry, uint16_t sel) { entry->w0 = (entry->w0 & 0x0000ffff) | (sel << 16); } static inline void idt_set_offset(struct idt_entry* entry, uintptr_t offset) { uint32_t low_16 = offset & 0xffff; uint32_t mid_16 = (offset >> 16) & 0xffff; entry->w0 = (entry->w0 & 0xffff0000) | low_16; entry->w1 = (entry->w1 & 0x0000ffff) | (mid_16 << 16); uint32_t high_32 = (uint32_t)(offset >> 32); entry->w2 = high_32; } static inline void idt_set_present(struct idt_entry* entry, bool present) { entry->w1 = (entry->w1 & ~(1 << 15)) | ((!!present) << 15); } static inline void idt_set_dpl(struct idt_entry* entry, enum idt_dpl dpl) { ASSERT(dpl <= 3); entry->w1 = (entry->w1 & ~(3 << 13)) | ((uint32_t)dpl << 13); } static inline void idt_set_type( struct idt_entry* entry, enum idt_entry_type typ) { entry->w1 = (entry->w1 & ~(0xf << 8)) | ((uint32_t)typ << 8); } void idt_set_vector( struct idt* idt, uint8_t vec, uint16_t code_segment_sel, uintptr_t entry_point_offset, enum idt_dpl dpl, enum idt_entry_type typ) { struct idt_entry* entry = &idt->entries[vec]; memset(entry, 0, sizeof(*entry)); idt_set_segment_sel(entry, code_segment_sel); idt_set_offset(entry, entry_point_offset); idt_set_type(entry, typ); idt_set_dpl(entry, dpl); idt_set_present(entry, true); } void idt_set_ist_index(struct idt* idt, uint8_t vec, uint8_t ist_idx) { ASSERT(ist_idx < 8); struct idt_entry* entry = &idt->entries[vec]; entry->w1 = (entry->w1 & ~0x7) | ist_idx; } void idt_setup(struct idt* idt) { extern uintptr_t const _isr_table[]; // If SMAP is not available, we need to skip past the CLAC instruction // at the beginning of the ISR stubs. int clac_shift = 0; if (!x86_feature_test(X86_FEATURE_SMAP)) { clac_shift += CLAC_SIZE; } uint16_t sel; enum idt_entry_type typ; sel = CODE_64_SELECTOR; typ = IDT_INTERRUPT_GATE64; for (size_t i = 0; i < fbl::count_of(idt->entries); ++i) { uintptr_t offset = _isr_table[i] + clac_shift; enum idt_dpl dpl; switch (i) { case X86_INT_BREAKPOINT: dpl = IDT_DPL3; break; default: dpl = IDT_DPL0; break; } idt_set_vector(idt, (uint8_t)i, sel, offset, dpl, typ); } } // Create a read-only remapping of the global IDT. // This function is called on arch initialization before additional cpus // started. It reloads the main processor IDT to be read-only. Each // additional cpu will pick-up the read-only IDT by default. // TODO(thgarnie): Move to C++ and non-compact VMAR for KASLR support. void idt_setup_readonly(void) { DEBUG_ASSERT(arch_curr_cpu_num() == 0); DEBUG_ASSERT(mp_get_online_mask() == 1); zx_status_t status = VmAspace::kernel_aspace()->AllocPhysical( "idt_readonly", sizeof(_idt), (void**)&_idt_ro, PAGE_SIZE_SHIFT, vaddr_to_paddr(&_idt), 0 /* vmm flags */, ARCH_MMU_FLAG_PERM_READ); ASSERT(status == ZX_OK); idt_load(_idt_ro); } // Get the read-only IDT struct idt* idt_get_readonly(void) { ASSERT(_idt_ro != nullptr); return _idt_ro; }