Documented GVF_SAVE_VAR alongside other flags, and removed a query/doubt
[AROS.git] / arch / x86_64-pc / kernel / smp.c
blob3f2c4616cdf951c3c761713f181addfe7b72222a
1 /*
2 Copyright © 1995-2014, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <aros/atomic.h>
7 #include <asm/io.h>
8 #include <exec/execbase.h>
9 #include <exec/memory.h>
10 #include <proto/exec.h>
12 #include "kernel_base.h"
13 #include "kernel_debug.h"
14 #include "kernel_globals.h"
15 #include "kernel_intern.h"
16 #include "kernel_syscall.h"
17 #include "apic.h"
18 #include "smp.h"
20 #define D(x) x
21 #define DWAKE(x)
23 extern const void *_binary_smpbootstrap_start;
24 extern const void *_binary_smpbootstrap_size;
26 static void smp_Entry(IPTR stackBase, volatile UBYTE *apicready, struct KernelBase *KernelBase)
29 * This is the entry point for secondary cores.
30 * KernelBase is already set up by the primary CPU, so we can use it.
32 IPTR _APICBase;
33 UWORD _APICID;
34 UBYTE _APICNO;
36 /* Enable fxsave/fxrstor */
37 wrcr(cr4, rdcr(cr4) | _CR4_OSFXSR | _CR4_OSXMMEXCPT);
39 /* Find out ourselves */
40 _APICBase = core_APIC_GetBase();
41 _APICID = core_APIC_GetID(_APICBase);
42 _APICNO = core_APIC_GetNumber(KernelBase->kb_PlatformData->kb_APIC);
44 D(bug("[SMP] smp_Entry[0x%02X]: launching on AP APIC ID 0x%02X, base @ 0x%p\n", _APICID, _APICID, _APICBase));
45 D(bug("[SMP] smp_Entry[0x%02X]: KernelBootPrivate 0x%p, stack base 0x%p\n", _APICID, __KernBootPrivate, stackBase));
46 D(bug("[SMP] smp_Entry[0x%02X]: Stack base 0x%p, ready lock 0x%p\n", _APICID, stackBase, apicready));
48 /* Set up GDT and LDT for our core */
49 core_CPUSetup(_APICID, stackBase);
51 bug("[SMP] APIC #%u of %u Going IDLE (Halting)...\n", _APICNO + 1, KernelBase->kb_PlatformData->kb_APIC->count);
53 /* Signal the bootstrap core that we are running */
54 *apicready = 1;
57 * Unfortunately at the moment we have nothing more to do.
58 * The rest of AROS is not SMP-capable. :-(
60 while (1) asm volatile("hlt");
63 static int smp_Setup(struct KernelBase *KernelBase)
65 struct PlatformData *pdata = KernelBase->kb_PlatformData;
66 /* Low memory header is in the tail of memory list - see kernel_startup.c */
67 struct MemHeader *lowmem = (struct MemHeader *)SysBase->MemList.lh_TailPred;
68 APTR smpboot;
69 struct SMPBootstrap *bs;
71 D(bug("[SMP] Setup\n"));
74 * Allocate space for SMP bootstrap code in low memory. Its address must be page-aligned.
75 * Every CPU starts up in real mode (DAMN CRAP!!!)
77 smpboot = Allocate(lowmem, (unsigned long)&_binary_smpbootstrap_size + PAGE_SIZE - 1);
78 if (!smpboot)
80 D(bug("[SMP] Failed to allocate space for SMP bootstrap\n"));
81 return 0;
84 /* Install SMP bootstrap code */
85 bs = (APTR)AROS_ROUNDUP2((IPTR)smpboot, PAGE_SIZE);
86 CopyMem(&_binary_smpbootstrap_start, bs, (unsigned long)&_binary_smpbootstrap_size);
87 pdata->kb_APIC_TrampolineBase = bs;
89 D(bug("[SMP] Copied APIC bootstrap code to 0x%p\n", bs));
92 * Store constant arguments in bootstrap's data area
93 * WARNING!!! The bootstrap code assumes PML4 is placed in a 32-bit memory,
94 * and there seem to be no easy way to fix this.
95 * If AROS kickstart is ever loaded into high memory, we would need to take
96 * a special care about it.
98 bs->Arg3 = (IPTR)KernelBase;
99 bs->PML4 = __KernBootPrivate->PML4;
100 bs->IP = smp_Entry;
102 return 1;
106 * Here we wake up our secondary cores.
108 static int smp_Wake(struct KernelBase *KernelBase)
110 struct PlatformData *pdata = KernelBase->kb_PlatformData;
111 struct SMPBootstrap *bs = pdata->kb_APIC_TrampolineBase;
112 struct APICData *apic = pdata->kb_APIC;
113 APTR _APICStackBase;
114 IPTR wakeresult;
115 UBYTE i;
116 volatile UBYTE apicready;
118 D(bug("[SMP] Ready spinlock at 0x%p\n", &apicready));
120 /* Core number 0 is our bootstrap core, so we start from No 1 */
121 for (i = 1; i < apic->count; i++)
123 UBYTE apic_id = apic->cores[i].lapicID;
125 D(bug("[SMP] Launching APIC #%u (ID 0x%02X)\n", i + 1, apic_id));
128 * First we need to allocate a stack for our CPU.
129 * We allocate the same three stacks as in core_CPUSetup().
131 _APICStackBase = AllocMem(STACK_SIZE * 3, MEMF_CLEAR);
132 D(bug("[SMP] Allocated STACK for APIC ID 0x%02X @ 0x%p ..\n", apic_id, _APICStackBase));
133 if (!_APICStackBase)
134 return 0;
136 /* Give the stack to the CPU */
137 bs->Arg1 = (IPTR)_APICStackBase;
138 bs->Arg2 = (IPTR)&apicready;
139 bs->SP = _APICStackBase + STACK_SIZE;
141 /* Initialize 'ready' flag to zero before launching the core */
142 apicready = 0;
144 /* Start IPI sequence */
145 wakeresult = core_APIC_Wake(bs, apic_id, apic->lapicBase);
146 /* wakeresult != 0 means error */
147 if (!wakeresult)
150 * Before we proceed we need to make sure that the core has picked up
151 * its stack and we can reload bootstrap argument area with another one.
152 * We use a very simple spinlock in order to perform this waiting.
153 * Previously we have set apicready to 0. When the core starts up,
154 * it writes 1 there.
156 DWAKE(bug("[SMP] Waiting for APIC #%u to initialise .. ", i + 1));
157 while (!apicready);
159 D(bug("[SMP] APIC #%u started up\n", i + 1));
161 D(else bug("[SMP] core_APIC_Wake() failed, status 0x%p\n", wakeresult));
164 D(bug("[SMP] Done\n"));
166 return 1;
169 int smp_Initialize(void)
171 struct KernelBase *KernelBase = getKernelBase();
172 struct PlatformData *pdata = KernelBase->kb_PlatformData;
174 if (pdata->kb_APIC && (pdata->kb_APIC->count > 1))
176 if (!smp_Setup(KernelBase))
178 D(bug("[SMP] Failed to prepare the environment!\n"));
180 pdata->kb_APIC->count = 1; /* We have only one workinng CPU */
181 return 0;
184 return smp_Wake(KernelBase);
187 /* This is not an SMP machine, but it's okay */
188 return 1;