diff --git a/arch/x64/arch-switch.hh b/arch/x64/arch-switch.hh index e919d603644c6c37d27b9d890a1036006b997600..5417961be24e0e01999683e7b466917a4b58735e 100644 --- a/arch/x64/arch-switch.hh +++ b/arch/x64/arch-switch.hh @@ -14,13 +14,37 @@ void stack_trampoline(sched::thread* t, void (*func)(sched::thread*), namespace sched { +void set_fsbase_msr(u64 v) +{ + processor::wrmsr(msr::IA32_FS_BASE, v); +} + +void set_fsbase_fsgsbase(u64 v) +{ + processor::wrfsbase(v); +} + +extern "C" +void (*resolve_set_fsbase(void))(u64 v) +{ + // can't use processor::features, because it is not initialized + // early enough. + if (processor::cpuid(0).a >= 7 && (processor::cpuid(7).b & 1)) { + return set_fsbase_fsgsbase; + } else { + return set_fsbase_msr; + } +} + +void set_fsbase(u64 v) __attribute__((ifunc("resolve_set_fsbase"))); + void thread::switch_to() { thread* old = current(); // writing to fs_base invalidates memory accesses, so surround with // barriers barrier(); - processor::wrmsr(msr::IA32_FS_BASE, reinterpret_cast<u64>(_tcb)); + set_fsbase(reinterpret_cast<u64>(_tcb)); barrier(); _cpu->arch.set_exception_stack(&_arch); asm volatile diff --git a/arch/x64/processor.hh b/arch/x64/processor.hh index 6c3cd6ea227a78435a4d97a6eb2c7725f66d7cb5..93ece95b9b73339322e6af138225309e7dba0182 100644 --- a/arch/x64/processor.hh +++ b/arch/x64/processor.hh @@ -202,6 +202,11 @@ inline void wrmsr(u32 index, u64 data) { asm volatile ("wrmsr" : : "c"(index), "a"(lo), "d"(hi)); } +inline void wrfsbase(u64 data) +{ + asm volatile("wrfsbase %0" : : "r"(data)); +} + inline void halt_no_interrupts() { asm volatile ("cli; hlt"); } diff --git a/core/elf.cc b/core/elf.cc index ecb479c9d32e58726bcd7c866590705dfc949e30..502484390c85a2a77a00e3a66d7001c37cbe42cb 100644 --- a/core/elf.cc +++ b/core/elf.cc @@ -924,6 +924,9 @@ init_table get_init(Elf64_Ehdr* header) // FIXME: assumes TLS segment comes before DYNAMIC segment *static_cast<u64*>(addr) = lookup()->st_value - ret.tls.size; break; + case R_X86_64_IRELATIVE: + *static_cast<void**>(addr) = reinterpret_cast<void *(*)()>(base + addend)(); + break; default: abort(); } diff --git a/include/elf.hh b/include/elf.hh index 43eb6e6adecd00a78b6b92ee26edd0ea59b0ee4d..b1419987927c39d663d0f8428ea2137f20bbd975 100644 --- a/include/elf.hh +++ b/include/elf.hh @@ -192,6 +192,7 @@ enum { R_X86_64_GOTPC32 = 26, // word32 GOT + A - P R_X86_64_SIZE32 = 32, // word32 Z + A R_X86_64_SIZE64 = 33, // word64 Z + A + R_X86_64_IRELATIVE = 37, // word64 indirect(B + A) }; enum {