1 /*
2 * Copyright (c) 2019 Intel Corporation
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #include <cpuid.h>
7 #include <zephyr/kernel.h>
8 #include <zephyr/arch/cpu.h>
9 #include <kernel_arch_data.h>
10 #include <kernel_arch_func.h>
11 #include <zephyr/kernel_structs.h>
12 #include <kernel_internal.h>
13 #include <zephyr/arch/x86/multiboot.h>
14 #include <x86_mmu.h>
15 #include <zephyr/drivers/interrupt_controller/loapic.h>
16 #ifdef CONFIG_ACPI
17 #include <zephyr/arch/x86/cpuid.h>
18 #include <zephyr/acpi/acpi.h>
19 #endif
20
21 /*
22 * Map of CPU logical IDs to CPU local APIC IDs. By default,
23 * we assume this simple identity mapping, as found in QEMU.
24 * The symbol is weak so that boards/SoC files can override.
25 */
26
27 #if defined(CONFIG_ACPI)
28 __weak uint8_t x86_cpu_loapics[CONFIG_MP_MAX_NUM_CPUS];
29 #else
30 #define INIT_CPUID(n, _) n
31 __weak uint8_t x86_cpu_loapics[] = {
32 LISTIFY(CONFIG_MP_MAX_NUM_CPUS, INIT_CPUID, (,)),};
33 #endif
34 extern char x86_ap_start[]; /* AP entry point in locore.S */
35
36 LISTIFY(CONFIG_MP_MAX_NUM_CPUS, ACPI_CPU_INIT, (;));
37
38 Z_GENERIC_SECTION(.boot_arg)
39 x86_boot_arg_t x86_cpu_boot_arg;
40
41 struct x86_cpuboot x86_cpuboot[] = {
42 LISTIFY(CONFIG_MP_MAX_NUM_CPUS, X86_CPU_BOOT_INIT, (,)),
43 };
44
45 /*
46 * Send the INIT/STARTUP IPI sequence required to start up CPU 'cpu_num', which
47 * will enter the kernel at fn(arg), running on the specified stack.
48 */
49
arch_cpu_start(int cpu_num,k_thread_stack_t * stack,int sz,arch_cpustart_t fn,void * arg)50 void arch_cpu_start(int cpu_num, k_thread_stack_t *stack, int sz,
51 arch_cpustart_t fn, void *arg)
52 {
53 #if CONFIG_MP_MAX_NUM_CPUS > 1
54 uint8_t vector = ((unsigned long) x86_ap_start) >> 12;
55 uint8_t apic_id;
56
57 IF_ENABLED(CONFIG_ACPI, ({
58 ACPI_MADT_LOCAL_APIC *lapic = acpi_local_apic_get(cpu_num);
59
60 if (lapic != NULL) {
61 /* We update the apic_id, __start will need it. */
62 x86_cpu_loapics[cpu_num] = lapic->Id;
63 } else {
64 /* TODO: kernel need to handle the error scenario if someone config
65 * CONFIG_MP_MAX_NUM_CPUS more than what platform supported.
66 */
67 __ASSERT(false, "CPU reached more than maximum supported!");
68 return;
69 }
70 }));
71
72 apic_id = x86_cpu_loapics[cpu_num];
73
74 x86_cpuboot[cpu_num].sp = (uint64_t) K_KERNEL_STACK_BUFFER(stack) + sz;
75 x86_cpuboot[cpu_num].stack_size = sz;
76 x86_cpuboot[cpu_num].fn = fn;
77 x86_cpuboot[cpu_num].arg = arg;
78 x86_cpuboot[cpu_num].cpu_id = cpu_num;
79
80 z_loapic_ipi(apic_id, LOAPIC_ICR_IPI_INIT, 0);
81 k_busy_wait(10000);
82 z_loapic_ipi(apic_id, LOAPIC_ICR_IPI_STARTUP, vector);
83
84 while (x86_cpuboot[cpu_num].ready == 0) {
85 }
86 #else
87 ARG_UNUSED(cpu_num);
88 ARG_UNUSED(stack);
89 ARG_UNUSED(sz);
90 ARG_UNUSED(fn);
91 ARG_UNUSED(arg);
92 #endif
93 }
94
95 /* Per-CPU initialization, C domain. On the first CPU, z_prep_c is the
96 * next step. For other CPUs it is probably smp_init_top().
97 */
z_x86_cpu_init(struct x86_cpuboot * cpuboot)98 FUNC_NORETURN void z_x86_cpu_init(struct x86_cpuboot *cpuboot)
99 {
100 #if defined(CONFIG_ACPI)
101 __ASSERT(z_x86_cpuid_get_current_physical_apic_id() ==
102 x86_cpu_loapics[cpuboot->cpu_id], "APIC ID miss match!");
103 #endif
104 x86_sse_init(NULL);
105
106 if (cpuboot->cpu_id == 0U) {
107 /* Only need to do these once per boot */
108 z_bss_zero();
109 z_data_copy();
110 }
111
112 z_loapic_enable(cpuboot->cpu_id);
113
114 #ifdef CONFIG_USERSPACE
115 /* Set landing site for 'syscall' instruction */
116 z_x86_msr_write(X86_LSTAR_MSR, (uint64_t)z_x86_syscall_entry_stub);
117
118 /* Set segment descriptors for syscall privilege transitions */
119 z_x86_msr_write(X86_STAR_MSR, (uint64_t)X86_STAR_UPPER << 32);
120
121 /* Mask applied to RFLAGS when making a syscall */
122 z_x86_msr_write(X86_FMASK_MSR, EFLAGS_SYSCALL);
123 #endif
124
125 /* Enter kernel, never return */
126 cpuboot->ready++;
127 cpuboot->fn(cpuboot->arg);
128
129 CODE_UNREACHABLE; /* LCOV_EXCL_LINE */
130 }
131