blob: 80146b179dbe411f4dd167fad7d7d6f0631467f0 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2009 Corey Tabaka
// Copyright (c) 2015 Intel Corporation
// Copyright (c) 2016 Travis Geiselbrecht
//
// 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 <align.h>
#include <arch.h>
#include <assert.h>
#include <debug.h>
#include <inttypes.h>
#include <lib/arch/x86/boot-cpuid.h>
#include <lib/arch/x86/lbr.h>
#include <lib/backtrace/global_cpu_context_exchange.h>
#include <lib/console.h>
#include <lib/version.h>
#include <platform.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <trace.h>
#include <zircon/compiler.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <arch/mp.h>
#include <arch/ops.h>
#include <arch/regs.h>
#include <arch/vm.h>
#include <arch/x86/apic.h>
#include <arch/x86/descriptor.h>
#include <arch/x86/feature.h>
#include <arch/x86/interrupts.h>
#include <arch/x86/mmu.h>
#include <arch/x86/mmu_mem_types.h>
#include <arch/x86/mp.h>
#include <arch/x86/proc_trace.h>
#include <hwreg/x86msr.h>
#include <kernel/cpu.h>
#include <kernel/mp.h>
#include <kernel/percpu.h>
#include <lk/init.h>
#include <lk/main.h>
#include <vm/vm.h>
#include "arch/x86.h"
#define LOCAL_TRACE 0
#ifndef __GCC_HAVE_SYNC_COMPARE_AND_SWAP_16
#error "missing -mcx16"
#endif
namespace {
void EnableLbrs(cpu_mask_t mask) {
mp_sync_exec(
MP_IPI_TARGET_MASK, mask,
[](void*) {
arch::LbrStack stack(arch::BootCpuidIo{});
if (stack.is_supported()) {
stack.Enable(hwreg::X86MsrIo{}, false);
printf("CPU-%u: LBRs enabled\n", arch_curr_cpu_num());
} else {
printf("CPU-%u: LBRs are not supported\n", arch_curr_cpu_num());
}
},
nullptr);
}
void DisableLbrs(cpu_mask_t mask) {
mp_sync_exec(
MP_IPI_TARGET_MASK, mask,
[](void*) {
arch::LbrStack stack(arch::BootCpuidIo{});
if (stack.is_supported()) {
stack.Disable(hwreg::X86MsrIo{});
printf("CPU-%u: LBRs disabled\n", arch_curr_cpu_num());
} else {
printf("CPU-%u: LBRs are not supported\n", arch_curr_cpu_num());
}
},
nullptr);
}
void DumpLbrs(cpu_num_t cpu_num) {
mp_sync_exec(
MP_IPI_TARGET_MASK, cpu_num_to_mask(cpu_num),
[](void* ctx) {
arch::LbrStack stack(arch::BootCpuidIo{});
hwreg::X86MsrIo io;
uint32_t cpu_num = *reinterpret_cast<uint32_t*>(ctx);
if (stack.is_enabled(io)) {
PrintSymbolizerContext(stdout);
printf("CPU-%u: Last Branch Records (omitting records braching to userspace)\n", cpu_num);
stack.ForEachRecord(io, [](const arch::LastBranchRecord& lbr) {
// Only include branches that end in the kernel, as we cannot make
// sense of any recorded userspace code; we do not know a priori at
// which addresses the relevant modules were loaded.
if (is_kernel_address(lbr.to)) {
printf("from: {{{pc:%#" PRIxPTR "}}}\n", lbr.from);
printf("to: {{{pc:%#" PRIxPTR "}}}\n", lbr.to);
}
});
} else {
printf("CPU-%u: LBRs are not enabled\n", cpu_num);
}
},
&cpu_num);
}
int LbrCtrl(int argc, const cmd_args* argv, uint32_t flags) {
auto print_usage = [&]() {
printf("usage:\n");
printf("%s lbr enable [cpu mask = CPU_MASK_ALL]\n", argv[0].str);
printf("%s lbr disable [cpu mask = CPU_MASK_ALL]\n", argv[0].str);
printf("%s lbr dump [cpu num = 0]\n", argv[0].str);
};
if (argc < 3) {
printf("not enough arguments\n");
print_usage();
return 1;
}
if (!strcmp(argv[2].str, "enable")) {
cpu_mask_t mask = (argc > 3) ? static_cast<cpu_mask_t>(argv[3].u) : CPU_MASK_ALL;
EnableLbrs(mask);
} else if (!strcmp(argv[2].str, "disable")) {
cpu_mask_t mask = (argc > 3) ? static_cast<cpu_mask_t>(argv[3].u) : CPU_MASK_ALL;
DisableLbrs(mask);
} else if (!strcmp(argv[2].str, "dump")) {
auto cpu_num = (argc > 3) ? static_cast<cpu_num_t>(argv[3].u) : 0;
DumpLbrs(cpu_num);
} else {
printf("unrecognized subcommand: %s\n", argv[2].str);
print_usage();
return 1;
}
return 0;
}
int GetContext(int argc, const cmd_args* argv, uint32_t flags) {
auto print_usage = [&]() {
printf("usage:\n");
printf("%s context <cpu_id> <timeout_ms>\n", argv[0].str);
};
if (argc < 4) {
printf("not enough arguments\n");
print_usage();
return 1;
}
cpu_num_t target = static_cast<cpu_num_t>(argv[2].u);
if (target >= percpu::processor_count()) {
printf("invalid cpu_id: %u\n", target);
return 1;
}
const zx_duration_t timeout = ZX_MSEC(argv[2].u);
printf("requesting context of CPU-%u\n", target);
CpuContext context;
zx_status_t status;
{
InterruptDisableGuard irqd;
status = g_cpu_context_exchange.RequestContext(target, timeout, context);
}
if (status != ZX_OK) {
printf("error: %d\n", status);
return 1;
}
context.backtrace.Print();
PrintFrame(stdout, context.frame);
return 0;
}
} // namespace
void arch_early_init(void) {
x86_mmu_early_init();
// Mark the boot cpu as online here after global constructors have run
mp_set_curr_cpu_online(true);
}
void arch_prevm_init(void) { x86_cpu_feature_init(); }
void arch_init(void) {
const struct x86_model_info* model = x86_get_model();
printf("Processor Model Info: type %#x family %#x model %#x stepping %#x\n",
model->processor_type, model->family, model->model, model->stepping);
printf("\tdisplay_family %#x display_model %#x\n", model->display_family, model->display_model);
x86_feature_debug();
x86_mmu_init();
gdt_setup();
idt_setup_readonly();
x86_processor_trace_init();
}
void arch_late_init_percpu(void) {
// Call per-CPU init function for the boot CPU.
x86_cpu_feature_late_init_percpu();
}
void arch_setup_uspace_iframe(iframe_t* iframe, uintptr_t pc, uintptr_t sp, uintptr_t arg1,
uintptr_t arg2) {
/* default user space flags:
* IOPL 0
* Interrupts enabled
*/
iframe->flags = (0 << X86_FLAGS_IOPL_SHIFT) | X86_FLAGS_IF;
iframe->cs = USER_CODE_64_SELECTOR;
iframe->ip = pc;
iframe->user_ss = USER_DATA_SELECTOR;
iframe->user_sp = sp;
iframe->rdi = arg1;
iframe->rsi = arg2;
}
void arch_enter_uspace(iframe_t* iframe) {
LTRACEF("entry %#" PRIxPTR " user stack %#" PRIxPTR "\n", iframe->ip, iframe->user_sp);
LTRACEF("kernel stack %#" PRIxPTR "\n", x86_get_percpu()->default_tss.rsp0);
#if __has_feature(safe_stack)
LTRACEF("kernel unsafe stack %#" PRIxPTR "\n", Thread::Current::Get()->stack().unsafe_top());
#endif
arch_disable_ints();
/* check that we are accessing userspace code */
ASSERT(arch_is_valid_user_pc(iframe->ip));
/* check that we're still pointed at the kernel gs */
DEBUG_ASSERT(is_kernel_address(read_msr(X86_MSR_IA32_GS_BASE)));
/* check that the kernel stack is set properly */
DEBUG_ASSERT(is_kernel_address(x86_get_percpu()->default_tss.rsp0));
#if __has_feature(safe_stack)
/* set the kernel unsafe stack back to the top as we enter user space */
auto unsafe_top = Thread::Current::Get()->stack().unsafe_top();
x86_uspace_entry(iframe, unsafe_top);
#else
x86_uspace_entry(iframe);
#endif
__UNREACHABLE;
}
void arch_suspend(void) {
DEBUG_ASSERT(arch_ints_disabled());
apic_io_save();
}
void arch_resume(void) {
DEBUG_ASSERT(arch_ints_disabled());
x86_init_percpu(0);
x86_mmu_percpu_init();
mp_set_curr_cpu_online(true);
x86_pat_sync(cpu_num_to_mask(0));
apic_local_init();
// Ensure the CPU that resumed was assigned the correct percpu object.
DEBUG_ASSERT(apic_local_id() == x86_get_percpu()->apic_id);
apic_io_restore();
}
[[noreturn, gnu::noinline]] static void finish_secondary_entry(
ktl::atomic<unsigned int>* aps_still_booting, Thread* thread, uint cpu_num) {
// Mark this cpu as online so MP code can try to deliver IPIs.
// Mark here so any code waiting for the cpu to be started will see the cpu
// online after the atomic below.
mp_set_curr_cpu_online(true);
// Signal that this CPU is initialized. It is important that after this
// operation, we do not touch any resources associated with bootstrap
// besides our Thread and stack, since this is the checkpoint the
// bootstrap process uses to identify completion.
unsigned int old_val = aps_still_booting->fetch_and(~(1U << cpu_num));
if (old_val == 0) {
// If the value is already zero, then booting this CPU timed out.
goto fail;
}
// Defer configuring memory settings until after the atomic_and above.
// This ensures that we were in no-fill cache mode for the duration of early
// AP init.
DEBUG_ASSERT(x86_get_cr0() & X86_CR0_CD);
x86_mmu_percpu_init();
// Load the appropriate PAT/MTRRs. This must happen after init_percpu, so
// that this CPU is considered online.
x86_pat_sync(1U << cpu_num);
/* run early secondary cpu init routines up to the threading level */
lk_init_level(LK_INIT_FLAG_SECONDARY_CPUS, LK_INIT_LEVEL_EARLIEST, LK_INIT_LEVEL_THREADING - 1);
thread->SecondaryCpuInitEarly();
// The thread stacks and struct are from a single allocation, free it
// when we exit into the scheduler.
thread->set_free_struct(true);
lk_secondary_cpu_entry();
// lk_secondary_cpu_entry only returns on an error, halt the core in this
// case.
fail:
arch_disable_ints();
while (1) {
x86_hlt();
}
}
// This is called from assembly, before any other C code.
// The %gs.base is not set up yet, so we have to trust that
// this function is simple enough that the compiler won't
// want to generate stack-protector prologue/epilogue code,
// which would use %gs.
__NO_SAFESTACK __NO_RETURN void x86_secondary_entry(ktl::atomic<unsigned int>* aps_still_booting,
Thread* thread) {
// Would prefer this to be in init_percpu, but there is a dependency on a
// page mapping existing, and the BP calls that before the VM subsystem is
// initialized.
apic_local_init();
uint32_t local_apic_id = apic_local_id();
int cpu_num = x86_apic_id_to_cpu_num(local_apic_id);
if (cpu_num < 0) {
// If we could not find our CPU number, do not proceed further
arch_disable_ints();
while (1) {
x86_hlt();
}
}
DEBUG_ASSERT(cpu_num > 0);
// Set %gs.base to our percpu struct. This has to be done before
// calling x86_init_percpu, which initializes most of that struct, so
// that x86_init_percpu can use safe-stack and/or stack-protector code.
struct x86_percpu* const percpu = &ap_percpus[cpu_num - 1];
write_msr(X86_MSR_IA32_GS_BASE, (uintptr_t)percpu);
// Copy the stack-guard value from the boot CPU's perpcu.
percpu->stack_guard = bp_percpu.stack_guard;
#if __has_feature(safe_stack)
// Set up the initial unsafe stack pointer.
DEBUG_ASSERT(IS_ALIGNED(thread->stack().unsafe_top(), 16));
x86_write_gs_offset64(ZX_TLS_UNSAFE_SP_OFFSET, thread->stack().unsafe_top());
#endif
x86_init_percpu((uint)cpu_num);
// Now do the rest of the work, in a function that is free to
// use %gs in its code.
finish_secondary_entry(aps_still_booting, thread, cpu_num);
}
static int cmd_cpu(int argc, const cmd_args* argv, uint32_t flags) {
if (argc < 2) {
printf("not enough arguments\n");
usage:
printf("usage:\n");
printf("%s features\n", argv[0].str);
printf("%s rdmsr <cpu_id> <msr_id>\n", argv[0].str);
printf("%s wrmsr <cpu_id> <msr_id> <value>\n", argv[0].str);
printf("%s lbr <subcommand>\n", argv[0].str);
printf("%s context <cpu_id> <timeout_ms>\n", argv[0].str);
return ZX_ERR_INTERNAL;
}
if (!strcmp(argv[1].str, "features")) {
x86_feature_debug();
} else if (!strcmp(argv[1].str, "rdmsr")) {
if (argc != 4) {
goto usage;
}
uint64_t val = read_msr_on_cpu((uint)argv[2].u, (uint)argv[3].u);
printf("CPU %lu RDMSR %lxh val %lxh\n", argv[2].u, argv[3].u, val);
} else if (!strcmp(argv[1].str, "wrmsr")) {
if (argc != 5) {
goto usage;
}
printf("CPU %lu WRMSR %lxh val %lxh\n", argv[2].u, argv[3].u, argv[4].u);
write_msr_on_cpu((uint)argv[2].u, (uint)argv[3].u, argv[4].u);
} else if (!strcmp(argv[1].str, "lbr")) {
return LbrCtrl(argc, argv, flags);
} else if (!strcmp(argv[1].str, "context")) {
return GetContext(argc, argv, flags);
} else {
printf("unknown command\n");
goto usage;
}
return ZX_OK;
}
STATIC_COMMAND_START
STATIC_COMMAND("cpu", "cpu test commands", &cmd_cpu)
STATIC_COMMAND_END(cpu)