Skip to content

Commit 8d6e06d

Browse files
committed
x86_64: Initial SMP support (WIP)
1 parent c097dc0 commit 8d6e06d

10 files changed

Lines changed: 127 additions & 50 deletions

File tree

api/kernel/irq_manager.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ class alignas(SMP_ALIGN) IRQ_manager {
123123
{ return count_handled; }
124124

125125
/** Initialize for a local APIC */
126-
static void init(int cpuid);
126+
static void init();
127127
IRQ_manager() = default;
128128

129129
private:

src/arch/x86_64/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ set(CMAKE_ASM_NASM_OBJECT_FORMAT elf64)
33
### x86_64 arch specific ###
44
set(ARCH_OBJECTS
55
apic_asm.asm
6+
apic_longmode.asm
67
arch_start.asm
78
interrupts.asm
89
fiber_asm.asm

src/arch/x86_64/apic_longmode.asm

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
; This file is a part of the IncludeOS unikernel - www.includeos.org
2+
;
3+
; Copyright 2015 Oslo and Akershus University College of Applied Sciences
4+
; and Alfred Bratterud
5+
;
6+
; Licensed under the Apache License, Version 2.0 (the "License");
7+
; you may not use this file except in compliance with the License.
8+
; You may obtain a copy of the License at
9+
;
10+
; http://www.apache.org/licenses/LICENSE-2.0
11+
;
12+
; Unless required by applicable law or agreed to in writing, software
13+
; distributed under the License is distributed on an "AS IS" BASIS,
14+
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
; See the License for the specific language governing permissions and
16+
; limitations under the License.
17+
;
18+
global __apic_trampoline:function
19+
extern __gdt64_base_pointer
20+
extern revenant_main
21+
22+
%define P4_TAB 0x1000
23+
24+
[BITS 32]
25+
__apic_trampoline:
26+
pop edi ;; cpuid
27+
28+
;; use same pagetable as CPU 0
29+
mov eax, P4_TAB
30+
mov cr3, eax
31+
32+
;; enable PAE
33+
mov eax, cr4
34+
or eax, 1 << 5
35+
mov cr4, eax
36+
37+
;; enable long mode
38+
mov ecx, 0xC0000080 ; EFER MSR
39+
rdmsr
40+
or eax, 1 << 8 ; Long Mode bit
41+
wrmsr
42+
43+
;; enable paging
44+
mov eax, cr0 ; Set the A-register to control register 0.
45+
or eax, 1 << 31
46+
mov cr0, eax ; Set control register 0 to the A-register.
47+
48+
;; load 64-bit GDT
49+
lgdt [__gdt64_base_pointer]
50+
jmp 0x8:long_mode ;; 0x8 = code seg
51+
52+
[BITS 64]
53+
long_mode:
54+
;; segment regs
55+
mov cx, 0x10 ;; 0x10 = data seg
56+
mov ds, cx
57+
mov es, cx
58+
mov fs, cx
59+
mov gs, cx
60+
mov ss, cx
61+
62+
;; align stack
63+
and rsp, -16
64+
;; retrieve CPU id
65+
mov rax, 1
66+
cpuid
67+
shr rbx, 24
68+
;; geronimo!
69+
mov rdi, rbx
70+
call revenant_main
71+
; stop execution
72+
cli
73+
hlt

src/arch/x86_64/arch_start.asm

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
; See the License for the specific language governing permissions and
1616
; limitations under the License.
1717
global __arch_start:function
18+
global __gdt64_base_pointer
1819
extern kernel_start
1920
extern __multiboot_magic
2021
extern __multiboot_addr
@@ -78,19 +79,16 @@ __arch_start:
7879
;; enable long mode
7980
mov ecx, 0xC0000080 ; EFER MSR
8081
rdmsr
81-
or eax, 1 << 8 ; Long Mode bit
82+
or eax, 1 << 8 ; Long Mode bit
8283
wrmsr
8384

8485
;; enable paging
8586
mov eax, cr0 ; Set the A-register to control register 0.
86-
or eax, 1 << 31
87+
or eax, 1 << 31
8788
mov cr0, eax ; Set control register 0 to the A-register.
8889

89-
mov eax, DWORD[__multiboot_magic] ; Preserve multiboot regs
90-
mov ebx, DWORD[__multiboot_addr]
91-
9290
;; load 64-bit GDT
93-
lgdt [GDT64.Pointer]
91+
lgdt [__gdt64_base_pointer]
9492
jmp GDT64.Code:long_mode
9593

9694

@@ -112,7 +110,7 @@ GDT64:
112110
db 00000000b ; Granularity.
113111
db 0 ; Base (high).
114112
dw 0x0 ;; alignment padding
115-
.Pointer: ; The GDT-pointer.
113+
__gdt64_base_pointer:
116114
dw $ - GDT64 - 1 ; Limit.
117115
dq GDT64 ; Base.
118116

@@ -130,12 +128,12 @@ long_mode:
130128

131129
;; set up new stack for 64-bit
132130
push rsp
133-
mov rsp, STACK_LOCATION
134-
mov rbp, rsp
131+
mov rsp, STACK_LOCATION
132+
mov rbp, rsp
135133

136134
;; geronimo!
137-
mov rdi, rax
138-
mov rsi, rbx
135+
mov edi, DWORD[__multiboot_magic]
136+
mov esi, DWORD[__multiboot_addr]
139137
call kernel_start
140138
pop rsp
141139
ret

src/kernel/irq_manager.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,9 @@ void IRQ_manager::enable_interrupts() {
218218
asm volatile("sti");
219219
}
220220

221-
void IRQ_manager::init(int cpuid)
221+
void IRQ_manager::init()
222222
{
223-
get(cpuid).init_local();
223+
get().init_local();
224224
}
225225

226226
void IRQ_manager::init_local()
@@ -359,9 +359,10 @@ void IRQ_manager::subscribe(uint8_t irq, irq_delegate del)
359359
irq_subs.atomic_set(irq);
360360

361361
// Create stat for this event
362-
Stat& subscribed = Statman::get().create(Stat::UINT64,
363-
"cpu" + std::to_string(SMP::cpu_id()) + ".irq" + std::to_string(irq));
364-
count_handled[irq] = &subscribed.get_uint64();
362+
//Stat& subscribed = Statman::get().create(Stat::UINT64,
363+
// "cpu" + std::to_string(SMP::cpu_id()) + ".irq" + std::to_string(irq));
364+
//count_handled[irq] = &subscribed.get_uint64();
365+
count_handled[irq] = nullptr;
365366

366367
// Add callback to subscriber list (for now overwriting any previous)
367368
irq_delegates_[irq] = del;

src/platform/x86_pc/apic_boot.asm

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,19 @@ ALIGN 4
3434
boot_code:
3535
; disable interrupts
3636
cli
37-
37+
3838
; segment descriptor table
3939
lgdt [cs:gdtr]
40-
40+
4141
mov edx, cr0 ; set bit 0 of CR0
4242
or edx, 1 ; to enable protected mode
4343
mov cr0, edx
44-
44+
4545
; A small delay
4646
nop
4747
nop
4848
nop
49-
49+
5050
; flush and enter protected mode
5151
JMP DWORD 0x08:protected_mode
5252

@@ -56,7 +56,7 @@ gdtr:
5656
dq gdt32
5757
gdt32:
5858
;; Entry 0x0: Null desriptor
59-
dq 0x0
59+
dq 0x0
6060
;; Entry 0x8: Code segment
6161
dw 0xffff ;Limit
6262
dw 0x0000 ;Base 15:00
@@ -68,38 +68,36 @@ gdt32:
6868
dw 0x0000 ;Base 15:00
6969
db 0x00 ;Base 23:16
7070
dw 0xcf92 ;Flags
71-
db 0x00 ;Base 32:24
71+
db 0x00 ;Base 32:24
7272
gdt32_end:
7373

7474
BITS 32
7575
protected_mode:
7676
cld
77-
77+
7878
; set most segments to data (0x10)
7979
mov ax, 0x10
8080
mov ds, ax
8181
mov es, ax
8282
mov ss, ax
83-
83+
8484
; retrieve CPU id
8585
mov eax, 1
8686
cpuid
8787
shr ebx, 24
88-
88+
8989
; give separate stack to each cpu
9090
mov eax, DWORD [stack_size]
9191
mul ebx
9292
add eax, [stack_base]
9393
mov ebp, eax
9494
mov esp, ebp
95-
95+
9696
; enable SSE
9797
call enable_sse
98-
98+
9999
push ebx
100100
call [revenant_main]
101-
pop ebx
102-
103101
; stop execution
104102
cli
105103
hlt

src/platform/x86_pc/apic_revenant.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
#include "apic.hpp"
44
#include "apic_timer.hpp"
5-
#include "gdt.hpp"
65
#include <kernel/irq_manager.hpp>
76
#include <kprint>
87

@@ -64,13 +63,13 @@ void revenant_main(int cpu)
6463
x86::APIC::get().smp_enable();
6564
// setup GDT & per-cpu feature
6665
initialize_gdt_for_cpu(cpu);
67-
// newlibs printf just does way too much static stuff to work in SMP
68-
// it can work if REENTs are initalized per-cpu ... and other things
66+
// show we are online, and verify CPU ID is correct
6967
::SMP::global_lock();
70-
INFO2("AP %d started at %p", get_cpu_id(), get_cpu_esp());
68+
INFO2("AP %d started at %p", cpu, get_cpu_esp());
7169
::SMP::global_unlock();
70+
assert(cpu == ::SMP::cpu_id());
7271

73-
IRQ_manager::init(cpu);
72+
IRQ_manager::init();
7473
// enable interrupts
7574
IRQ_manager::enable_interrupts();
7675
// init timer system

src/platform/x86_pc/apic_revenant.hpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,24 @@
2323
#include <cstdint>
2424
#include <deque>
2525

26-
extern "C"
27-
void revenant_main(int);
26+
extern "C" void revenant_main(int);
2827

2928
struct smp_stuff
3029
{
3130
struct task {
3231
task(SMP::task_func a,
3332
SMP::done_func b)
3433
: func(a), done(b) {}
35-
34+
3635
SMP::task_func func;
3736
SMP::done_func done;
3837
};
39-
38+
4039
minimal_barrier_t boot_barrier;
41-
40+
4241
spinlock_t tlock;
4342
std::deque<task> tasks;
44-
43+
4544
spinlock_t flock;
4645
std::deque<SMP::done_func> completed;
4746
};

src/platform/x86_pc/platform.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ void __platform_init()
5050
initialize_gdt_for_cpu(APIC::get().get_id());
5151

5252
// IDT manager: Interrupt and exception handlers
53-
IRQ_manager::init(APIC::get().get_id());
53+
IRQ_manager::init();
5454

5555
// initialize and start registered APs found in ACPI-tables
5656
#ifndef INCLUDEOS_SINGLE_THREADED

src/platform/x86_pc/smp.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,26 +28,27 @@
2828
extern "C" {
2929
extern char _binary_apic_boot_bin_start;
3030
extern char _binary_apic_boot_bin_end;
31+
extern void __apic_trampoline(); // 64-bit entry
3132
}
3233

3334
static const uintptr_t BOOTLOADER_LOCATION = 0x10000;
3435
static const size_t REV_STACK_SIZE = 1 << 18; // 256kb
3536

3637
struct apic_boot {
3738
// the jump instruction at the start
38-
uint32_t jump;
39+
uint32_t padding;
3940
// stuff we will modify
40-
void* worker_addr;
41-
char* stack_base;
42-
size_t stack_size;
41+
uint32_t worker_addr;
42+
uint32_t stack_base;
43+
uint32_t stack_size;
4344
};
4445

4546
namespace x86
4647
{
4748

4849
void SMP::init()
4950
{
50-
size_t CPUcount = ACPI::get_cpus().size();
51+
uint32_t CPUcount = ACPI::get_cpus().size();
5152
if (CPUcount <= 1) return;
5253
assert(CPUcount <= SMP_MAX_CORES);
5354

@@ -59,11 +60,18 @@ void SMP::init()
5960
// modify bootloader to support our cause
6061
auto* boot = (apic_boot*) BOOTLOADER_LOCATION;
6162

62-
boot->worker_addr = (void*) &revenant_main;
63-
boot->stack_base = (char*) memalign(4096, CPUcount * REV_STACK_SIZE);
63+
#if defined(ARCH_i686)
64+
boot->worker_addr = (uint32_t) &revenant_main;
65+
#elif defined(ARCH_x86_64)
66+
boot->worker_addr = (uint32_t) (uintptr_t) &__apic_trampoline;
67+
#else
68+
#error "Unimplemented arch"
69+
#endif
70+
auto* stack = memalign(4096, CPUcount * REV_STACK_SIZE);
71+
boot->stack_base = (uint32_t) (uintptr_t) stack;
6472
boot->stack_base += REV_STACK_SIZE; // make sure the stack starts at the top
6573
boot->stack_size = REV_STACK_SIZE;
66-
debug("APIC stack base: %p size: %u main size: %u\n",
74+
debug("APIC stack base: %#x size: %u main size: %u\n",
6775
boot->stack_base, boot->stack_size, sizeof(boot->worker_addr));
6876

6977
// reset barrier

0 commit comments

Comments
 (0)