/
kernel_thread-ia32.cpp
207 lines (161 loc) · 5.59 KB
/
kernel_thread-ia32.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
IMPLEMENTATION[ia32,amd64]:
#include "apic.h"
#include "config.h"
#include "cpu.h"
#include "io_apic.h"
#include "irq_mgr.h"
#include "koptions.h"
#include "mem_layout.h"
#include "pic.h"
#include "trap_state.h"
#include "watchdog.h"
IMPLEMENT inline NEEDS["mem_layout.h"]
void
Kernel_thread::free_initcall_section()
{
// just fill up with invalid opcodes
for (char *i = const_cast<char *>(Mem_layout::initcall_start);
i + 1 < const_cast<char *>(Mem_layout::initcall_end); i += 2)
{
// UD2
i[0] = 0x0f;
i[1] = 0x0b;
}
}
IMPLEMENT FIASCO_INIT
void
Kernel_thread::bootstrap_arch()
{
//
// install our slow trap handler
//
nested_trap_handler = Trap_state::base_handler;
Trap_state::base_handler = thread_handle_trap;
// initialize the profiling timer
bool user_irq0 = Koptions::o()->opt(Koptions::F_irq0);
if ((int)Config::Scheduler_mode == Config::SCHED_PIT && user_irq0)
panic("option -irq0 not possible since irq 0 is used for scheduling");
boot_app_cpus();
}
//--------------------------------------------------------------------------
IMPLEMENTATION [!mp]:
PUBLIC
static inline void
Kernel_thread::boot_app_cpus()
{}
//--------------------------------------------------------------------------
IMPLEMENTATION [mp]:
#include "acpi.h"
#include "per_cpu_data.h"
EXTENSION class Kernel_thread
{
public:
static Cpu_number find_cpu_num_by_apic_id(Unsigned32 apic_id);
static bool boot_deterministic;
private:
typedef Per_cpu_array<Unsigned32> Apic_id_array;
// store all APIC IDs found in the MADT
// this is used by boot_ap_cpu() to determine its CPU number by looking up
// its APIC ID in the array, the array position is equivalent to the CPU
// number
static Apic_id_array _cpu_num_to_apic_id;
};
Kernel_thread::Apic_id_array Kernel_thread::_cpu_num_to_apic_id;
bool Kernel_thread::boot_deterministic;
/**
* Retrieve reserved CPU number for the given APIC ID.
*
* \param apic_id Local APIC ID whose CPU number should be returned.
*
* \retval Cpu_number The CPU number reserved for the provided APIC ID.
* \retval Cpu_number::nil() No valid CPU number for the provided APIC ID was
* found.
*/
IMPLEMENT
Cpu_number
Kernel_thread::find_cpu_num_by_apic_id(Unsigned32 apic_id)
{
for (Cpu_number n = Cpu_number::first(); n < Config::max_num_cpus(); ++n)
if (_cpu_num_to_apic_id[n] == apic_id)
return n;
return Cpu_number::nil();
}
PUBLIC
static void
Kernel_thread::boot_app_cpus()
{
// sending (INIT-)IPIs on non-MP systems might not work
if ( Cpu::boot_cpu()->vendor() == Cpu::Vendor_amd
&& Cpu::amd_cpuid_mnc() < 2)
return;
// where to start the APs for detection of the APIC-IDs
extern char _tramp_mp_entry[];
// feature enabling flags (esp. cache enabled flag and paging enabled flag)
extern volatile Mword _tramp_mp_startup_cr0;
// feature enabling flags (esp. needed for big pages)
extern volatile Mword _tramp_mp_startup_cr4;
// physical address of the page table directory to use
extern volatile Address _realmode_startup_pdbr;
// pseudo descriptor for the gdt to load
extern Pseudo_descriptor _tramp_mp_startup_gdt_pdesc;
Address tramp_page;
_realmode_startup_pdbr = Kmem::get_realmode_startup_pdbr();
_tramp_mp_startup_cr4 = Cpu::get_cr4();
_tramp_mp_startup_cr0 = Cpu::get_cr0();
_tramp_mp_startup_gdt_pdesc
= Pseudo_descriptor((Address)Cpu::boot_cpu()->get_gdt(), Gdt::gdt_max -1);
__asm__ __volatile__ ("" : : : "memory");
Acpi_madt const *madt = Io_apic::madt();
// if we cannot find a MADT we cannot boot in deterministic order
if (madt)
{
boot_deterministic = true;
Unsigned32 boot_apic_id = Apic::get_id();
// make sure the boot CPU gets the right CPU number
_cpu_num_to_apic_id[Cpu_number::boot_cpu()] = boot_apic_id;
unsigned entry = 0;
Cpu_number last_cpu = Cpu_number::first();
// First we collect all enabled CPUs and assign them the leading CPU
// numbers. Disabled CPUs are collected in a second run and get the
// remaining CPU numbers assigned. This way we make sure that we can boot
// at least the maximum number of enabled CPUs. Disabled CPUs may come
// online later through e.g. hot plugging.
while (last_cpu < Config::max_num_cpus())
{
auto const *lapic = madt->find<Acpi_madt::Lapic>(entry++);
if (!lapic)
break;
// skip disabled CPUs
if (!(lapic->flags & 1))
continue;
// skip logical boot CPU number
if (last_cpu == Cpu_number::boot_cpu())
++last_cpu;
Unsigned32 aid = ((Unsigned32)lapic->apic_id) << 24;
// the boot CPU already has a CPU number assigned
if (aid == boot_apic_id)
continue;
_cpu_num_to_apic_id[last_cpu++] = aid;
}
entry = 0;
while (last_cpu < Config::max_num_cpus())
{
auto const *lapic = madt->find<Acpi_madt::Lapic>(entry++);
if (!lapic)
break;
// skip enabled CPUs
if (lapic->flags & 1)
continue;
// skip logical boot CPU number
if (last_cpu == Cpu_number::boot_cpu())
++last_cpu;
_cpu_num_to_apic_id[last_cpu++] = ((Unsigned32)lapic->apic_id) << 24;
}
}
// Say what we do
printf("MP: detecting APs...\n");
// broadcast an AP startup via the APIC (let run the self-registration code)
tramp_page = (Address)&(_tramp_mp_entry[0]);
// Send IPI-Sequency to startup the APs
Apic::mp_startup(Cpu::boot_cpu(), Apic::APIC_IPI_OTHERS, tramp_page);
}