:: limine / common / sys / smp.c 38.8 KB raw

1
#include <stddef.h>
2
#include <stdint.h>
3
#include <stdbool.h>
4
#include <lib/libc.h>
5
#include <lib/acpi.h>
6
#include <sys/cpu.h>
7
#include <lib/misc.h>
8
#include <lib/print.h>
9
#include <sys/smp.h>
10
#include <sys/lapic.h>
11
#include <sys/gdt.h>
12
#include <mm/vmm.h>
13
#include <mm/pmm.h>
14
#include <mm/mtrr.h>
15
#define LIMINE_NO_POINTERS
16
#include <limine.h>
17
#if defined (__riscv)
18
#include <sys/sbi.h>
19
#endif
20
#if defined (__aarch64__) || defined(__loongarch__)
21
#include <libfdt.h>
22
#endif
23
24
extern symbol smp_trampoline_start;
25
extern size_t smp_trampoline_size;
26
27
#if defined (__x86_64__) || defined (__i386__)
28
29
struct trampoline_passed_info {
30
    uint8_t  smp_tpl_booted_flag;
31
    uint8_t  smp_tpl_target_mode;
32
    uint32_t smp_tpl_pagemap;
33
    uint32_t smp_tpl_info_struct;
34
    struct gdtr smp_tpl_gdt;
35
    uint64_t smp_tpl_hhdm;
36
    uint64_t smp_tpl_bsp_apic_addr_msr;
37
    uint64_t smp_tpl_mtrr_restore;
38
    uint64_t smp_tpl_temp_stack;
39
    uint64_t smp_tpl_lapic_setup;
40
} __attribute__((packed));
41
42
bool smp_configure_apic = false;
43
44
static bool smp_start_ap(uint32_t lapic_id, struct gdtr *gdtr,
45
                         struct limine_mp_info *info_struct,
46
                         int paging_mode, uint32_t pagemap,
47
                         bool x2apic, bool nx, uint64_t hhdm, bool wp) {
48
    // Prepare the trampoline
49
    static void *trampoline = NULL;
50
    if (trampoline == NULL) {
51
        trampoline = conv_mem_alloc(smp_trampoline_size);
52
53
        memcpy(trampoline, smp_trampoline_start, smp_trampoline_size);
54
    }
55
56
    static void *temp_stack = NULL;
57
    if (temp_stack == NULL) {
58
        temp_stack = ext_mem_alloc(8192);
59
    }
60
61
    static struct trampoline_passed_info *passed_info = NULL;
62
    if (passed_info == NULL) {
63
        passed_info = (void *)(((uintptr_t)trampoline + smp_trampoline_size)
64
                               - sizeof(struct trampoline_passed_info));
65
    }
66
67
    passed_info->smp_tpl_info_struct = (uint32_t)(uintptr_t)info_struct;
68
    passed_info->smp_tpl_booted_flag = 0;
69
    passed_info->smp_tpl_pagemap     = pagemap;
70
    passed_info->smp_tpl_target_mode = ((uint32_t)(paging_mode == PAGING_MODE_X86_64_5LVL) << 1)
71
                                     | ((uint32_t)nx << 3)
72
                                     | ((uint32_t)wp << 4);
73
    passed_info->smp_tpl_gdt = *gdtr;
74
    passed_info->smp_tpl_hhdm = hhdm;
75
    passed_info->smp_tpl_bsp_apic_addr_msr = rdmsr(0x1b);
76
    passed_info->smp_tpl_mtrr_restore = (uint64_t)(uintptr_t)mtrr_restore;
77
    passed_info->smp_tpl_temp_stack = (uint64_t)(uintptr_t)temp_stack + 8192;
78
    passed_info->smp_tpl_lapic_setup = smp_configure_apic
79
        ? (uint64_t)(uintptr_t)lapic_configure_handoff_state : 0;
80
81
    asm volatile ("" ::: "memory");
82
83
    // Send the INIT IPI
84
    if (x2apic) {
85
        x2apic_write(LAPIC_REG_ICR0, ((uint64_t)lapic_id << 32) | 0x4500);
86
    } else {
87
        lapic_icr_wait();
88
        lapic_write(LAPIC_REG_ICR1, lapic_id << 24);
89
        lapic_write(LAPIC_REG_ICR0, 0x4500);
90
    }
91
    stall(10000);
92
93
    // Send two Startup IPIs per Intel SDM recommendation (Vol 3, 8.4.4.1)
94
    for (int j = 0; j < 2; j++) {
95
        if (x2apic) {
96
            x2apic_write(LAPIC_REG_ICR0, ((uint64_t)lapic_id << 32) |
97
                                         ((size_t)trampoline / 4096) | 0x4600);
98
        } else {
99
            lapic_icr_wait();
100
            lapic_write(LAPIC_REG_ICR1, lapic_id << 24);
101
            lapic_write(LAPIC_REG_ICR0, ((size_t)trampoline / 4096) | 0x4600);
102
        }
103
        if (j == 0) {
104
            stall(200);
105
        }
106
    }
107
108
    if (!x2apic) {
109
        lapic_icr_wait();
110
    }
111
112
    for (int i = 0; i < 100; i++) {
113
        if (locked_read(&passed_info->smp_tpl_booted_flag) == 1) {
114
            return true;
115
        }
116
        stall(10000);
117
    }
118
119
    return false;
120
}
121
122
struct limine_mp_info *init_smp(size_t   *cpu_count,
123
                                 uint32_t *_bsp_lapic_id,
124
                                 int       paging_mode,
125
                                 pagemap_t pagemap,
126
                                 bool      x2apic,
127
                                 bool      nx,
128
                                 uint64_t  hhdm,
129
                                 bool      wp) {
130
    if (!lapic_check())
131
        return NULL;
132
133
    // Search for MADT table
134
    struct madt *madt = acpi_get_table("APIC", 0);
135
136
    if (madt == NULL)
137
        return NULL;
138
139
    struct gdtr gdtr = gdt;
140
141
    uint32_t bsp_lapic_id;
142
143
    // If x2APIC already enabled by firmware, try to revert to xAPIC
144
    if (rdmsr(0x1b) & (1 << 10)) {
145
        if (!x2apic) {
146
            if (!x2apic_disable()) {
147
                panic(false, "smp: Kernel does not support x2APIC and x2APIC cannot be disabled");
148
            }
149
            printv("smp: Firmware had x2APIC enabled, reverted to xAPIC mode\n");
150
        }
151
    }
152
153
    x2apic = x2apic && x2apic_enable();
154
155
    if (x2apic) {
156
        bsp_lapic_id = x2apic_read(LAPIC_REG_ID);
157
    } else {
158
        bsp_lapic_id = lapic_read(LAPIC_REG_ID) >> 24;
159
    }
160
161
    *_bsp_lapic_id = bsp_lapic_id;
162
163
    *cpu_count = 0;
164
165
    // Count the MAX of startable APs and allocate accordingly
166
    size_t max_cpus = 0;
167
168
    for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin;
169
      (uintptr_t)madt_ptr + 1 < (uintptr_t)madt + madt->header.length;
170
      madt_ptr += *(madt_ptr + 1)) {
171
        // Skip zero-length or out-of-bounds MADT entries
172
        if (*(madt_ptr + 1) == 0
173
         || (uintptr_t)madt_ptr + *(madt_ptr + 1) > (uintptr_t)madt + madt->header.length) {
174
            break;
175
        }
176
        switch (*madt_ptr) {
177
            case 0: {
178
                // Processor local xAPIC
179
                if (*(madt_ptr + 1) < sizeof(struct madt_lapic))
180
                    continue;
181
182
                struct madt_lapic *lapic = (void *)madt_ptr;
183
184
                // Check if we can actually try to start the AP
185
                if ((lapic->flags & 1) ^ ((lapic->flags >> 1) & 1))
186
                    max_cpus++;
187
188
                continue;
189
            }
190
            case 9: {
191
                // Processor local x2APIC
192
                if (!x2apic)
193
                    continue;
194
195
                if (*(madt_ptr + 1) < sizeof(struct madt_x2apic))
196
                    continue;
197
198
                struct madt_x2apic *x2lapic = (void *)madt_ptr;
199
200
                // Check if we can actually try to start the AP
201
                if ((x2lapic->flags & 1) ^ ((x2lapic->flags >> 1) & 1))
202
                    max_cpus++;
203
204
                continue;
205
            }
206
        }
207
    }
208
209
    if (max_cpus == 0) {
210
        return NULL;
211
    }
212
213
    struct limine_mp_info *ret = ext_mem_alloc_counted(max_cpus, sizeof(struct limine_mp_info));
214
    *cpu_count = 0;
215
216
    // Try to start all APs
217
    mtrr_save();
218
219
    for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin;
220
      (uintptr_t)madt_ptr + 1 < (uintptr_t)madt + madt->header.length;
221
      madt_ptr += *(madt_ptr + 1)) {
222
        // Skip zero-length or out-of-bounds MADT entries
223
        if (*(madt_ptr + 1) == 0
224
         || (uintptr_t)madt_ptr + *(madt_ptr + 1) > (uintptr_t)madt + madt->header.length) {
225
            break;
226
        }
227
        switch (*madt_ptr) {
228
            case 0: {
229
                // Processor local xAPIC
230
                if (*(madt_ptr + 1) < sizeof(struct madt_lapic))
231
                    continue;
232
233
                struct madt_lapic *lapic = (void *)madt_ptr;
234
235
                // Check if we can actually try to start the AP
236
                if (!((lapic->flags & 1) ^ ((lapic->flags >> 1) & 1)))
237
                    continue;
238
239
                struct limine_mp_info *info_struct = &ret[*cpu_count];
240
241
                info_struct->processor_id = lapic->acpi_processor_uid;
242
                info_struct->lapic_id = lapic->lapic_id;
243
244
                // Do not try to restart the BSP
245
                if (lapic->lapic_id == bsp_lapic_id) {
246
                    (*cpu_count)++;
247
                    continue;
248
                }
249
250
                printv("smp: [xAPIC] Found candidate AP for bring-up. LAPIC ID: %u\n", lapic->lapic_id);
251
252
                // Set up per-AP LINT values before starting
253
                if (smp_configure_apic) {
254
                    lapic_prep_lint(madt, lapic->acpi_processor_uid, x2apic);
255
                }
256
257
                // Try to start the AP
258
                if (!smp_start_ap(lapic->lapic_id, &gdtr, info_struct,
259
                                  paging_mode, (uintptr_t)pagemap.top_level,
260
                                  x2apic, nx, hhdm, wp)) {
261
                    print("smp: FAILED to bring-up AP\n");
262
                    continue;
263
                }
264
265
                printv("smp: Successfully brought up AP\n");
266
267
                (*cpu_count)++;
268
                continue;
269
            }
270
            case 9: {
271
                // Processor local x2APIC
272
                if (!x2apic)
273
                    continue;
274
275
                if (*(madt_ptr + 1) < sizeof(struct madt_x2apic))
276
                    continue;
277
278
                struct madt_x2apic *x2lapic = (void *)madt_ptr;
279
280
                // Check if we can actually try to start the AP
281
                if (!((x2lapic->flags & 1) ^ ((x2lapic->flags >> 1) & 1)))
282
                    continue;
283
284
                struct limine_mp_info *info_struct = &ret[*cpu_count];
285
286
                info_struct->processor_id = x2lapic->acpi_processor_uid;
287
                info_struct->lapic_id = x2lapic->x2apic_id;
288
289
                // Do not try to restart the BSP
290
                if (x2lapic->x2apic_id == bsp_lapic_id) {
291
                    (*cpu_count)++;
292
                    continue;
293
                }
294
295
                printv("smp: [x2APIC] Found candidate AP for bring-up. LAPIC ID: %u\n", x2lapic->x2apic_id);
296
297
                // Set up per-AP LINT values before starting
298
                if (smp_configure_apic) {
299
                    lapic_prep_lint(madt, x2lapic->acpi_processor_uid, true);
300
                }
301
302
                // Try to start the AP
303
                if (!smp_start_ap(x2lapic->x2apic_id, &gdtr, info_struct,
304
                                  paging_mode, (uintptr_t)pagemap.top_level,
305
                                  true, nx, hhdm, wp)) {
306
                    print("smp: FAILED to bring-up AP\n");
307
                    continue;
308
                }
309
310
                printv("smp: Successfully brought up AP\n");
311
312
                (*cpu_count)++;
313
                continue;
314
            }
315
        }
316
    }
317
318
    if (*cpu_count == 0) {
319
        pmm_free(ret, max_cpus * sizeof(struct limine_mp_info));
320
        return NULL;
321
    }
322
323
    return ret;
324
}
325
326
#elif defined (__aarch64__)
327
328
struct trampoline_passed_info {
329
    uint64_t smp_tpl_ap_el;
330
331
    uint64_t smp_tpl_booted_flag;
332
333
    uint64_t smp_tpl_hhdm_offset;
334
335
    uint64_t smp_tpl_ttbr0;
336
    uint64_t smp_tpl_ttbr1;
337
338
    uint64_t smp_tpl_mair;
339
    uint64_t smp_tpl_tcr;
340
    uint64_t smp_tpl_sctlr;
341
342
    uint64_t smp_tpl_info_struct;
343
};
344
345
enum {
346
    BOOT_WITH_SPIN_TBL,
347
    BOOT_WITH_PSCI_SMC,
348
    BOOT_WITH_PSCI_HVC,
349
    BOOT_WITH_ACPI_PARK
350
};
351
352
static uint32_t psci_cpu_on = 0xC4000003;
353
354
static bool try_start_ap(int boot_method, uint64_t method_ptr,
355
                         struct limine_mp_info *info_struct,
356
                         uint64_t ttbr0, uint64_t ttbr1, uint64_t mair,
357
                         uint64_t tcr, uint64_t sctlr,
358
                         uint64_t hhdm_offset) {
359
    // Prepare the trampoline
360
    static void *trampoline = NULL;
361
    if (trampoline == NULL) {
362
        trampoline = ext_mem_alloc(smp_trampoline_size);
363
364
        memcpy(trampoline, smp_trampoline_start, smp_trampoline_size);
365
    }
366
367
    static struct trampoline_passed_info *passed_info = NULL;
368
    if (passed_info == NULL) {
369
        passed_info = (void *)(((uintptr_t)trampoline + 0x1000)
370
                               - sizeof(struct trampoline_passed_info));
371
    }
372
373
    passed_info->smp_tpl_info_struct = (uint64_t)(uintptr_t)info_struct;
374
    passed_info->smp_tpl_booted_flag = 0;
375
    passed_info->smp_tpl_ap_el       = 0;
376
    passed_info->smp_tpl_ttbr0       = ttbr0;
377
    passed_info->smp_tpl_ttbr1       = ttbr1;
378
    passed_info->smp_tpl_mair        = mair;
379
    passed_info->smp_tpl_tcr         = tcr;
380
    passed_info->smp_tpl_sctlr       = sctlr;
381
    passed_info->smp_tpl_hhdm_offset = hhdm_offset;
382
383
    // Cache coherency between the I-Cache and D-Cache is not guaranteed by the
384
    // architecture and as such we must perform I-Cache invalidation.
385
    // Additionally, the newly-booted AP may have caches disabled which implies
386
    // it possibly does not see our cache contents either.
387
388
    clean_dcache_poc((uintptr_t)trampoline, (uintptr_t)trampoline + smp_trampoline_size);
389
    inval_icache_pou((uintptr_t)trampoline, (uintptr_t)trampoline + smp_trampoline_size);
390
391
    asm volatile ("" ::: "memory");
392
393
    switch (boot_method) {
394
        case BOOT_WITH_SPIN_TBL:
395
            *(volatile uint64_t *)method_ptr = (uint64_t)(uintptr_t)trampoline;
396
            clean_dcache_poc(method_ptr, method_ptr + 8);
397
            asm ("sev");
398
            break;
399
400
        case BOOT_WITH_PSCI_SMC:
401
        case BOOT_WITH_PSCI_HVC: {
402
            register int32_t result asm("w0");
403
            register uint32_t cmd asm("w0") = psci_cpu_on;
404
            register uint64_t cpu asm("x1") = info_struct->mpidr;
405
            register uint64_t addr asm("x2") = (uint64_t)(uintptr_t)trampoline;
406
            register uint64_t ctx asm("x3") = 0;
407
408
            if (boot_method == BOOT_WITH_PSCI_SMC)
409
                asm volatile ("smc #0" : "=r"(result) : "r"(cmd), "r"(cpu), "r"(addr), "r"(ctx));
410
            else
411
                asm volatile ("hvc #0" : "=r"(result) : "r"(cmd), "r"(cpu), "r"(addr), "r"(ctx));
412
413
            switch (result) {
414
                case 0: // Success
415
                    break;
416
                case -2:
417
                    printv("smp: PSCI says CPU_ON was given invalid arguments\n");
418
                    return false;
419
                case -4:
420
                    printv("smp: PSCI says AP is already on\n");
421
                    return false;
422
                case -5:
423
                    printv("smp: PSCI says CPU_ON is already pending for this AP\n");
424
                    return false;
425
                case -6:
426
                    printv("smp: PSCI reports internal failure\n");
427
                    return false;
428
                case -9:
429
                    printv("smp: PSCI says CPU_ON was given an invalid address\n");
430
                    return false;
431
                default:
432
                    printv("smp: PSCI reports an unexpected error (%d)\n", result);
433
                    return false;
434
            }
435
436
            break;
437
        }
438
439
        case BOOT_WITH_ACPI_PARK:
440
            panic(false, "ACPI parking protocol is unsupported, please report this!");
441
            break;
442
443
        default:
444
            panic(false, "Invalid boot method specified");
445
    }
446
447
    for (int i = 0; i < 1000000; i++) {
448
        // We do not need cache invalidation here as by the time the AP gets to
449
        // set this flag, it has enabled its caches
450
451
        if (locked_read(&passed_info->smp_tpl_booted_flag) == 1) {
452
            uint64_t ap_el = locked_read(&passed_info->smp_tpl_ap_el);
453
            uint64_t bsp_el = current_el();
454
            if (ap_el != bsp_el) {
455
                panic(false, "smp: AP started at EL%u but BSP is at EL%u",
456
                      (uint32_t)ap_el, (uint32_t)bsp_el);
457
            }
458
            return true;
459
        }
460
        stall(100);
461
    }
462
463
    return false;
464
}
465
466
static struct limine_mp_info *try_acpi_smp(size_t   *cpu_count,
467
                                            uint64_t *_bsp_mpidr,
468
                                            pagemap_t pagemap,
469
                                            uint64_t  mair,
470
                                            uint64_t  tcr,
471
                                            uint64_t  sctlr,
472
                                            uint64_t  hhdm_offset) {
473
    int boot_method = BOOT_WITH_ACPI_PARK;
474
475
    // Search for FADT table
476
    uint8_t *fadt = acpi_get_table("FACP", 0);
477
478
    if (fadt == NULL)
479
        return NULL;
480
481
    // Check FADT length before accessing ARM boot flags at offset 129
482
    uint32_t fadt_length;
483
    memcpy(&fadt_length, fadt + 4, 4);
484
    if (fadt_length >= 131) {
485
        // Read the single field from the FADT without defining a struct for the whole table
486
        uint16_t arm_boot_args;
487
        memcpy(&arm_boot_args, fadt + 129, 2);
488
489
        if (arm_boot_args & 1) // PSCI compliant?
490
            boot_method = arm_boot_args & 2 ? BOOT_WITH_PSCI_HVC : BOOT_WITH_PSCI_SMC;
491
    }
492
493
    // Search for MADT table
494
    struct madt *madt = acpi_get_table("APIC", 0);
495
496
    if (madt == NULL)
497
        return NULL;
498
499
    uint64_t bsp_mpidr;
500
    asm volatile ("mrs %0, mpidr_el1" : "=r"(bsp_mpidr));
501
502
    // This bit is Res1 in the system reg, but not included in the MPIDR from MADT
503
    bsp_mpidr &= ~((uint64_t)1 << 31);
504
505
    *_bsp_mpidr = bsp_mpidr;
506
507
    printv("smp: BSP MPIDR is %X\n", bsp_mpidr);
508
509
    *cpu_count = 0;
510
511
    // Count the MAX of startable APs and allocate accordingly
512
    size_t max_cpus = 0;
513
514
    for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin;
515
      (uintptr_t)madt_ptr + 1 < (uintptr_t)madt + madt->header.length;
516
      madt_ptr += *(madt_ptr + 1)) {
517
        if (*(madt_ptr + 1) == 0
518
         || (uintptr_t)madt_ptr + *(madt_ptr + 1) > (uintptr_t)madt + madt->header.length) {
519
            break;
520
        }
521
        switch (*madt_ptr) {
522
            case 11: {
523
                // GIC CPU Interface
524
                if (*(madt_ptr + 1) < sizeof(struct madt_gicc))
525
                    continue;
526
527
                struct madt_gicc *gicc = (void *)madt_ptr;
528
529
                // Check if we can actually try to start the AP
530
                if (gicc->flags & 1)
531
                    max_cpus++;
532
533
                continue;
534
            }
535
        }
536
    }
537
538
    struct limine_mp_info *ret = ext_mem_alloc_counted(max_cpus, sizeof(struct limine_mp_info));
539
    *cpu_count = 0;
540
541
    // Try to start all APs
542
    for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin;
543
      (uintptr_t)madt_ptr + 1 < (uintptr_t)madt + madt->header.length;
544
      madt_ptr += *(madt_ptr + 1)) {
545
        if (*(madt_ptr + 1) == 0
546
         || (uintptr_t)madt_ptr + *(madt_ptr + 1) > (uintptr_t)madt + madt->header.length) {
547
            break;
548
        }
549
        switch (*madt_ptr) {
550
            case 11: {
551
                // GIC CPU Interface
552
                if (*(madt_ptr + 1) < sizeof(struct madt_gicc))
553
                    continue;
554
555
                struct madt_gicc *gicc = (void *)madt_ptr;
556
557
                // Check if we can actually try to start the AP
558
                if (!(gicc->flags & 1))
559
                    continue;
560
561
                struct limine_mp_info *info_struct = &ret[*cpu_count];
562
563
                info_struct->processor_id = gicc->acpi_uid;
564
                info_struct->mpidr = gicc->mpidr;
565
566
                // Do not try to restart the BSP
567
                if (gicc->mpidr == bsp_mpidr) {
568
                    (*cpu_count)++;
569
                    continue;
570
                }
571
572
                printv("smp: Found candidate AP for bring-up. Interface no.: %x, MPIDR: %X\n", gicc->iface_no, gicc->mpidr);
573
574
                // Try to start the AP
575
                if (!try_start_ap(boot_method, gicc->parking_addr, info_struct,
576
                                  (uint64_t)(uintptr_t)pagemap.top_level[0],
577
                                  (uint64_t)(uintptr_t)pagemap.top_level[1],
578
                                  mair, tcr, sctlr, hhdm_offset)) {
579
                    print("smp: FAILED to bring-up AP\n");
580
                    continue;
581
                }
582
583
                printv("smp: Successfully brought up AP\n");
584
585
                (*cpu_count)++;
586
                continue;
587
            }
588
        }
589
    }
590
591
    if (*cpu_count == 0) {
592
        pmm_free(ret, max_cpus * sizeof(struct limine_mp_info));
593
        return NULL;
594
    }
595
596
    return ret;
597
}
598
599
static struct limine_mp_info *try_dtb_smp( void *dtb,
600
                                           size_t   *cpu_count,
601
                                           uint64_t *_bsp_mpidr,
602
                                           pagemap_t pagemap,
603
                                           uint64_t  mair,
604
                                           uint64_t  tcr,
605
                                           uint64_t  sctlr,
606
                                           uint64_t  hhdm_offset) {
607
    uint64_t bsp_mpidr;
608
    asm volatile ("mrs %0, mpidr_el1" : "=r"(bsp_mpidr));
609
610
    // This bit is Res1 in the system reg, but not included in the MPIDR from DT
611
    bsp_mpidr &= ~((uint64_t)1 << 31);
612
613
    *_bsp_mpidr = bsp_mpidr;
614
615
    printv("smp: BSP MPIDR is %X\n", bsp_mpidr);
616
617
    *cpu_count = 0;
618
619
    int cpus = fdt_path_offset(dtb, "/cpus");
620
    if (cpus < 0) {
621
        printv("smp: failed to find /cpus node: %s\n", fdt_strerror(cpus));
622
        return NULL;
623
    }
624
625
    int psci = fdt_path_offset(dtb, "/psci");
626
627
    if (psci > 0 && !fdt_node_check_compatible(dtb, psci, "arm,psci")) {
628
        int prop_len;
629
        const void *prop;
630
        if (!(prop = fdt_getprop(dtb, psci, "cpu_on", &prop_len)) || prop_len < 4) {
631
            printv("smp: failed to find PSCI cpu_on prop\n");
632
            return NULL;
633
        }
634
635
        const uint8_t *bytes = prop;
636
637
        psci_cpu_on = ((uint64_t)bytes[0] << 24)
638
            | ((uint64_t)bytes[1] << 16)
639
            | ((uint64_t)bytes[2] << 8)
640
            | ((uint64_t)bytes[3]);
641
    }
642
643
    int address_cells = fdt_address_cells(dtb, cpus);
644
    if (address_cells < 1) {
645
        printv("smp: fdt_address_cells failed: %s\n", fdt_strerror(address_cells));
646
        return NULL;
647
    }
648
    if (address_cells > 2) {
649
        printv("smp: illegal #address-cells value: %d\n", address_cells);
650
        return NULL;
651
    }
652
653
    uint64_t max_cpus = 0;
654
    int node;
655
    fdt_for_each_subnode(node, dtb, cpus) {
656
        const void *prop;
657
658
        if (!(prop = fdt_getprop(dtb, node, "device_type", NULL)) || strcmp(prop, "cpu")) {
659
            continue;
660
        }
661
662
        if (!(prop = fdt_getprop(dtb, node, "reg", NULL))) {
663
            continue;
664
        }
665
666
        max_cpus++;
667
    }
668
669
    struct limine_mp_info *ret = ext_mem_alloc_counted(max_cpus, sizeof(struct limine_mp_info));
670
671
    fdt_for_each_subnode(node, dtb, cpus) {
672
        const void *prop;
673
        int prop_len;
674
675
        if (!(prop = fdt_getprop(dtb, node, "device_type", NULL)) || strcmp(prop, "cpu")) {
676
            continue;
677
        }
678
679
        if (!(prop = fdt_getprop(dtb, node, "reg", &prop_len)) || prop_len < address_cells * 4) {
680
            continue;
681
        }
682
683
        uint64_t mpidr = 0;
684
685
        if (address_cells == 1) {
686
            const uint8_t *bytes = prop;
687
688
            mpidr = ((uint64_t)bytes[0] << 24)
689
                | ((uint64_t)bytes[1] << 16)
690
                | ((uint64_t)bytes[2] << 8)
691
                | ((uint64_t)bytes[3]);
692
        } else if (address_cells == 2) {
693
            const uint8_t *bytes = prop;
694
695
            mpidr = ((uint64_t)bytes[3] << 32)
696
                | ((uint64_t)bytes[4] << 24)
697
                | ((uint64_t)bytes[5] << 16)
698
                | ((uint64_t)bytes[6] << 8)
699
                | ((uint64_t)bytes[7]);
700
        }
701
702
703
        struct limine_mp_info *info_struct = &ret[*cpu_count];
704
705
        info_struct->processor_id = 0;
706
        info_struct->mpidr = mpidr;
707
708
        // Do not try to restart the BSP
709
        if (mpidr == bsp_mpidr) {
710
            (*cpu_count)++;
711
            continue;
712
        }
713
714
        if (!(prop = fdt_getprop(dtb, node, "enable-method", NULL))) {
715
            printv("smp: missing enable-method\n");
716
            continue;
717
        }
718
719
        int boot_method = -1;
720
        uint64_t method_ptr = 0;
721
722
        if (!strcmp(prop, "psci")) {
723
            if (psci < 0) {
724
                printv("smp: failed to find /psci: %s\n", fdt_strerror(psci));
725
                continue;
726
            }
727
728
            const void *psci_method = fdt_getprop(dtb, psci, "method", NULL);
729
730
            if (psci_method == NULL) {
731
                printv("smp: PSCI method property not found\n");
732
                continue;
733
            } else if (!strcmp(psci_method, "smc")) {
734
                boot_method = BOOT_WITH_PSCI_SMC;
735
            } else if (!strcmp(psci_method, "hvc")) {
736
                boot_method = BOOT_WITH_PSCI_HVC;
737
            } else {
738
                printv("smp: illegal PSCI method: '%s'\n", psci_method);
739
                continue;
740
            }
741
742
        } else if (!strcmp(prop, "spin-table")) {
743
            boot_method = BOOT_WITH_SPIN_TBL;
744
745
            if (!(prop = fdt_getprop(dtb, node, "cpu-release-addr", &prop_len)) || prop_len < 8) {
746
                printv("smp: missing cpu-release-addr\n");
747
                continue;
748
            }
749
750
            const uint8_t *bytes = prop;
751
752
            method_ptr = ((uint64_t)bytes[0] << 56)
753
                | ((uint64_t)bytes[1] << 48)
754
                | ((uint64_t)bytes[2] << 40)
755
                | ((uint64_t)bytes[3] << 32)
756
                | ((uint64_t)bytes[4] << 24)
757
                | ((uint64_t)bytes[5] << 16)
758
                | ((uint64_t)bytes[6] << 8)
759
                | ((uint64_t)bytes[7]);
760
        } else {
761
            printv("smp: illegal enable-method: '%s'\n", prop);
762
            continue;
763
        }
764
765
        printv("smp: Found candidate AP for bring-up. MPIDR: %X\n", mpidr);
766
767
        // Try to start the AP
768
        if (!try_start_ap(boot_method, method_ptr, info_struct,
769
                                        (uint64_t)(uintptr_t)pagemap.top_level[0],
770
                                        (uint64_t)(uintptr_t)pagemap.top_level[1],
771
                                        mair, tcr, sctlr, hhdm_offset)) {
772
            print("smp: FAILED to bring-up AP\n");
773
            continue;
774
        }
775
776
        printv("smp: Successfully brought up AP\n");
777
778
        (*cpu_count)++;
779
    }
780
781
    return ret;
782
}
783
784
785
struct limine_mp_info *init_smp(const char *config,
786
                                 size_t   *cpu_count,
787
                                 uint64_t *bsp_mpidr,
788
                                 pagemap_t pagemap,
789
                                 uint64_t  mair,
790
                                 uint64_t  tcr,
791
                                 uint64_t  sctlr,
792
                                 uint64_t  hhdm_offset) {
793
    struct limine_mp_info *info = NULL;
794
795
    if (acpi_get_rsdp() && (info = try_acpi_smp(
796
                                    cpu_count, bsp_mpidr, pagemap,
797
                                    mair, tcr, sctlr, hhdm_offset)))
798
        return info;
799
800
    // No RSDP means no ACPI, try device trees in that case.
801
    void *dtb = get_device_tree_blob(config, 0, false);
802
    if (dtb) {
803
        info = try_dtb_smp(dtb,
804
                           cpu_count, bsp_mpidr, pagemap,
805
                           mair, tcr, sctlr, hhdm_offset);
806
        pmm_free(dtb, fdt_totalsize(dtb));
807
        return info;
808
    }
809
810
    printv("Failed to figure out how to start APs.");
811
812
    return NULL;
813
}
814
815
#elif defined (__riscv)
816
817
struct trampoline_passed_info {
818
    uint64_t smp_tpl_booted_flag;
819
    uint64_t smp_tpl_satp;
820
    uint64_t smp_tpl_info_struct;
821
    uint64_t smp_tpl_hhdm_offset;
822
};
823
824
static bool smp_start_ap(size_t hartid, size_t satp, struct limine_mp_info *info_struct,
825
                         uint64_t hhdm_offset) {
826
    static struct trampoline_passed_info passed_info;
827
828
    passed_info.smp_tpl_booted_flag = 0;
829
    passed_info.smp_tpl_satp        = satp;
830
    passed_info.smp_tpl_info_struct = (uint64_t)info_struct;
831
    passed_info.smp_tpl_hhdm_offset = hhdm_offset;
832
833
    asm volatile ("fence w,w" ::: "memory");
834
835
    struct sbiret ret = sbi_hart_start(hartid, (size_t)smp_trampoline_start, (size_t)&passed_info);
836
    if (ret.error != SBI_SUCCESS)
837
        return false;
838
839
    for (int i = 0; i < 1000000; i++) {
840
        if (locked_read(&passed_info.smp_tpl_booted_flag) == 1)
841
            return true;
842
843
        stall(100);
844
    }
845
846
    return false;
847
}
848
849
struct limine_mp_info *init_smp(size_t *cpu_count, pagemap_t pagemap, uint64_t hhdm_offset) {
850
    size_t num_cpus = 0;
851
    for (struct riscv_hart *hart = hart_list; hart != NULL; hart = hart->next) {
852
        if (!(hart->flags & RISCV_HART_COPROC)) {
853
            num_cpus += 1;
854
        }
855
    }
856
857
    struct limine_mp_info *ret = ext_mem_alloc_counted(num_cpus, sizeof(struct limine_mp_info));
858
859
    *cpu_count = 0;
860
    for (struct riscv_hart *hart = hart_list; hart != NULL; hart = hart->next) {
861
        if (hart->flags & RISCV_HART_COPROC) {
862
            continue;
863
        }
864
        struct limine_mp_info *info_struct = &ret[*cpu_count];
865
866
        info_struct->hartid = hart->hartid;
867
        info_struct->processor_id = hart->acpi_uid;
868
869
        // Don't try to start the BSP.
870
        if (hart->hartid == bsp_hartid) {
871
            *cpu_count += 1;
872
            continue;
873
        }
874
875
        printv("smp: Found candidate AP for bring-up. Hart ID: %U\n", (uint64_t)hart->hartid);
876
877
        // Try to start the AP.
878
        size_t satp = make_satp(pagemap.paging_mode, pagemap.top_level);
879
        if (!smp_start_ap(hart->hartid, satp, info_struct, hhdm_offset)) {
880
            print("smp: FAILED to bring-up AP\n");
881
            continue;
882
        }
883
884
        (*cpu_count)++;
885
        continue;
886
    }
887
888
    return ret;
889
}
890
891
#elif defined (__loongarch64)
892
893
enum {
894
    LOONGARCH_CSR_CPUID = 0x20,
895
896
    LOONGARCH_IOCSR_IPI_SEND = 0x1040,
897
    LOONGARCH_IOCSR_MBUF_SEND = 0x1048,
898
899
    IOCSR_IPI_SEND_BLOCKING_BIT = 31,
900
    IOCSR_IPI_SEND_CPU_SHIFT    = 16,
901
    IOCSR_IPI_SEND_IP_SHIFT     = 0,
902
903
    IOCSR_MBUF_SEND_BLOCKING_BIT = 31,
904
    IOCSR_MBUF_SEND_CPU_SHIFT    = 16,
905
    IOCSR_MBUF_SEND_BOX_SHIFT    = 2,
906
907
    SMP_BOOT_CPU = 0x1,
908
909
    MADT_ENTRY_CORE_PIC = 17
910
};
911
912
struct trampoline_passed_info {
913
    uint64_t smp_tpl_booted_flag;
914
    uint64_t smp_tpl_info_struct;
915
    uint64_t smp_tpl_pgd_low;
916
    uint64_t smp_tpl_pgd_high;
917
    uint64_t smp_tpl_hhdm_offset;
918
    uint64_t smp_tpl_temp_stack;
919
};
920
921
struct trampoline_passed_info loongarch_smp_passed_info;
922
923
static inline uint32_t loongarch_phys_id(void) {
924
    return csr_read32(LOONGARCH_CSR_CPUID);
925
}
926
927
static inline bool core_pic_startable(uint32_t flags) {
928
    return (flags & MADT_CORE_PIC_ENABLED)
929
        || (flags & MADT_CORE_PIC_ONLINE_CAPABLE);
930
}
931
932
static void csr_mail_send(uint64_t data, int cpu, int mailbox) {
933
	uint64_t val;
934
935
    // High 32bit
936
	val = ((uint64_t)1 << IOCSR_MBUF_SEND_BLOCKING_BIT);
937
	val |= (((mailbox << 1) + 1) << IOCSR_MBUF_SEND_BOX_SHIFT);
938
	val |= (cpu << IOCSR_MBUF_SEND_CPU_SHIFT);
939
	val |= (data & 0xFFFFFFFF00000000);
940
	iocsr_write64(val, LOONGARCH_IOCSR_MBUF_SEND);
941
942
    // Low 32bit
943
	val = ((uint64_t)1 << IOCSR_MBUF_SEND_BLOCKING_BIT);
944
	val |= ((mailbox << 1) << IOCSR_MBUF_SEND_BOX_SHIFT);
945
	val |= (cpu << IOCSR_MBUF_SEND_CPU_SHIFT);
946
	val |= (data << 32);
947
	iocsr_write64(val, LOONGARCH_IOCSR_MBUF_SEND);
948
};
949
950
static void smp_send_ipi(uint32_t phys_id, uint32_t action) {
951
    uint32_t val = ((uint32_t)1 << IOCSR_IPI_SEND_BLOCKING_BIT)
952
                 | (phys_id << IOCSR_IPI_SEND_CPU_SHIFT)
953
                 | (action << IOCSR_IPI_SEND_IP_SHIFT);
954
955
    iocsr_write32(val, LOONGARCH_IOCSR_IPI_SEND);
956
}
957
958
static bool smp_start_ap(uint32_t phys_id, struct limine_mp_info *info_struct,
959
                         uint64_t pgd_low, uint64_t pgd_high,
960
                         uint64_t hhdm_offset) {
961
    static void *temp_stack =NULL;
962
    if (temp_stack == NULL) {
963
        temp_stack = ext_mem_alloc(8192);
964
    }
965
966
    loongarch_smp_passed_info.smp_tpl_booted_flag = 0;
967
    loongarch_smp_passed_info.smp_tpl_info_struct = (uint64_t)(uintptr_t)info_struct;
968
    loongarch_smp_passed_info.smp_tpl_pgd_low     = pgd_low;
969
    loongarch_smp_passed_info.smp_tpl_pgd_high    = pgd_high;
970
    loongarch_smp_passed_info.smp_tpl_hhdm_offset = hhdm_offset;
971
    loongarch_smp_passed_info.smp_tpl_temp_stack  = (uint64_t)(uintptr_t)temp_stack + 8192;
972
973
    asm volatile ("dbar 0" ::: "memory");
974
975
    uint64_t trampoline_entry = (uint64_t)(uintptr_t)smp_trampoline_start;
976
977
    // Mailbox 0 and 1 carry the low and high 32 bits of the AP entry point.
978
    csr_mail_send(trampoline_entry, phys_id, 0);
979
    smp_send_ipi(phys_id, SMP_BOOT_CPU);
980
981
    for (int i = 0; i < 1000000; i++) {
982
        if (locked_read(&loongarch_smp_passed_info.smp_tpl_booted_flag) == 1)
983
            return true;
984
        stall(100);
985
    }
986
987
    return false;
988
}
989
990
static struct limine_mp_info *try_acpi_smp(size_t *cpu_count, uint32_t *bsp_phys_id,
991
                                           pagemap_t pagemap, uint64_t hhdm_offset) {
992
    struct madt *madt = acpi_get_table("APIC", 0);
993
    if (madt == NULL)
994
        return NULL;
995
996
    *bsp_phys_id = loongarch_phys_id();
997
    *cpu_count = 0;
998
999
    size_t max_cpus = 0;
1000
1001
    for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin;
1002
         (uintptr_t)madt_ptr + 1 < (uintptr_t)madt + madt->header.length;
1003
         madt_ptr += *(madt_ptr + 1)) {
1004
        if (*(madt_ptr + 1) == 0
1005
         || (uintptr_t)madt_ptr + *(madt_ptr + 1) > (uintptr_t)madt + madt->header.length)
1006
            break;
1007
1008
        if (*madt_ptr != MADT_ENTRY_CORE_PIC)
1009
            continue;
1010
1011
        if (*(madt_ptr + 1) < sizeof(struct madt_core_pic))
1012
            continue;
1013
1014
        struct madt_core_pic *core_pic = (void *)madt_ptr;
1015
1016
        if (core_pic_startable(core_pic->flags))
1017
            max_cpus++;
1018
    }
1019
1020
    if (max_cpus == 0)
1021
        return NULL;
1022
1023
    struct limine_mp_info *ret = ext_mem_alloc_counted(max_cpus, sizeof(struct limine_mp_info));
1024
1025
    for (uint8_t *madt_ptr = (uint8_t *)madt->madt_entries_begin;
1026
         (uintptr_t)madt_ptr + 1 < (uintptr_t)madt + madt->header.length;
1027
         madt_ptr += *(madt_ptr + 1)) {
1028
        if (*(madt_ptr + 1) == 0
1029
         || (uintptr_t)madt_ptr + *(madt_ptr + 1) > (uintptr_t)madt + madt->header.length)
1030
            break;
1031
1032
        if (*madt_ptr != MADT_ENTRY_CORE_PIC)
1033
            continue;
1034
1035
        if (*(madt_ptr + 1) < sizeof(struct madt_core_pic))
1036
            continue;
1037
1038
        struct madt_core_pic *core_pic = (void *)madt_ptr;
1039
1040
        if (!core_pic_startable(core_pic->flags))
1041
            continue;
1042
1043
        struct limine_mp_info *info_struct = &ret[*cpu_count];
1044
        info_struct->processor_id = core_pic->acpi_processor_uid;
1045
        info_struct->phys_id = core_pic->core_id;
1046
1047
        // Do not try to restart the BSP.
1048
        if (core_pic->core_id == *bsp_phys_id) {
1049
            (*cpu_count)++;
1050
            continue;
1051
        }
1052
1053
        printv("smp: Found candidate AP for bring-up. Core ID: %u\n", core_pic->core_id);
1054
1055
        if (!smp_start_ap(core_pic->core_id, info_struct,
1056
                          (uint64_t)(uintptr_t)pagemap.pgd[0],
1057
                          (uint64_t)(uintptr_t)pagemap.pgd[1],
1058
                          hhdm_offset)) {
1059
            print("smp: FAILED to bring-up AP\n");
1060
            continue;
1061
        }
1062
1063
        printv("smp: Successfully brought up AP\n");
1064
        (*cpu_count)++;
1065
    }
1066
1067
    if (*cpu_count == 0) {
1068
        pmm_free(ret, max_cpus * sizeof(struct limine_mp_info));
1069
        return NULL;
1070
    }
1071
1072
    return ret;
1073
}
1074
1075
static struct limine_mp_info *try_dtb_smp(void *dtb, size_t *cpu_count,
1076
                                          uint32_t *bsp_phys_id,
1077
                                          pagemap_t pagemap,
1078
                                          uint64_t hhdm_offset) {
1079
    int cpus = fdt_path_offset(dtb, "/cpus");
1080
    if (cpus < 0) {
1081
        printv("smp: failed to find /cpus node: %s\n", fdt_strerror(cpus));
1082
        return NULL;
1083
    }
1084
1085
    int address_cells = fdt_address_cells(dtb, cpus);
1086
    if (address_cells < 1) {
1087
        printv("smp: fdt_address_cells failed: %s\n", fdt_strerror(address_cells));
1088
        return NULL;
1089
    }
1090
    if (address_cells > 2) {
1091
        printv("smp: illegal #address-cells value: %d\n", address_cells);
1092
        return NULL;
1093
    }
1094
1095
    *bsp_phys_id = loongarch_phys_id();
1096
    *cpu_count = 0;
1097
1098
    size_t max_cpus = 0;
1099
    int node;
1100
    fdt_for_each_subnode(node, dtb, cpus) {
1101
        const void *prop;
1102
        int prop_len;
1103
1104
        if (!(prop = fdt_getprop(dtb, node, "device_type", NULL)) || strcmp(prop, "cpu"))
1105
            continue;
1106
1107
        if (!(prop = fdt_getprop(dtb, node, "reg", &prop_len)) || prop_len < address_cells * 4)
1108
            continue;
1109
1110
        uint64_t phys_id = 0;
1111
        const uint8_t *bytes = prop;
1112
1113
        if (address_cells == 1) {
1114
            phys_id = ((uint64_t)bytes[0] << 24)
1115
                    | ((uint64_t)bytes[1] << 16)
1116
                    | ((uint64_t)bytes[2] << 8)
1117
                    | ((uint64_t)bytes[3] << 0);
1118
        } else {
1119
            phys_id = ((uint64_t)bytes[0] << 56)
1120
                    | ((uint64_t)bytes[1] << 48)
1121
                    | ((uint64_t)bytes[2] << 40)
1122
                    | ((uint64_t)bytes[3] << 32)
1123
                    | ((uint64_t)bytes[4] << 24)
1124
                    | ((uint64_t)bytes[5] << 16)
1125
                    | ((uint64_t)bytes[6] << 8)
1126
                    | ((uint64_t)bytes[7] << 0);
1127
        }
1128
1129
        if (phys_id > UINT32_MAX) {
1130
            printv("smp: core id %U does not fit in 32 bits, skipping\n", phys_id);
1131
            continue;
1132
        }
1133
1134
        max_cpus++;
1135
    }
1136
1137
    if (max_cpus == 0)
1138
        return NULL;
1139
1140
    struct limine_mp_info *ret = ext_mem_alloc_counted(max_cpus, sizeof(struct limine_mp_info));
1141
1142
    fdt_for_each_subnode(node, dtb, cpus) {
1143
        const void *prop;
1144
        int prop_len;
1145
1146
        if (!(prop = fdt_getprop(dtb, node, "device_type", NULL)) || strcmp(prop, "cpu"))
1147
            continue;
1148
1149
        if (!(prop = fdt_getprop(dtb, node, "reg", &prop_len)) || prop_len < address_cells * 4)
1150
            continue;
1151
1152
        uint64_t phys_id = 0;
1153
        const uint8_t *bytes = prop;
1154
1155
        if (address_cells == 1) {
1156
            phys_id = ((uint64_t)bytes[0] << 24)
1157
                    | ((uint64_t)bytes[1] << 16)
1158
                    | ((uint64_t)bytes[2] << 8)
1159
                    | ((uint64_t)bytes[3] << 0);
1160
        } else {
1161
            phys_id = ((uint64_t)bytes[0] << 56)
1162
                    | ((uint64_t)bytes[1] << 48)
1163
                    | ((uint64_t)bytes[2] << 40)
1164
                    | ((uint64_t)bytes[3] << 32)
1165
                    | ((uint64_t)bytes[4] << 24)
1166
                    | ((uint64_t)bytes[5] << 16)
1167
                    | ((uint64_t)bytes[6] << 8)
1168
                    | ((uint64_t)bytes[7] << 0);
1169
        }
1170
1171
        if (phys_id > UINT32_MAX) {
1172
            printv("smp: core id %U does not fit in 32 bits, skipping\n", phys_id);
1173
            continue;
1174
        }
1175
1176
        struct limine_mp_info *info_struct = &ret[*cpu_count];
1177
        info_struct->processor_id = 0;
1178
        info_struct->phys_id = phys_id;
1179
1180
        // Do not try to restart the BSP.
1181
        if (phys_id == *bsp_phys_id) {
1182
            (*cpu_count)++;
1183
            continue;
1184
        }
1185
1186
        printv("smp: Found candidate AP for bring-up. Core ID: %U\n", phys_id);
1187
1188
        if (!smp_start_ap((uint32_t)phys_id, info_struct,
1189
                          (uint64_t)(uintptr_t)pagemap.pgd[0],
1190
                          (uint64_t)(uintptr_t)pagemap.pgd[1],
1191
                          hhdm_offset)) {
1192
            print("smp: FAILED to bring-up AP\n");
1193
            continue;
1194
        }
1195
1196
        printv("smp: Successfully brought up AP\n");
1197
        (*cpu_count)++;
1198
    }
1199
1200
    if (*cpu_count == 0) {
1201
        pmm_free(ret, max_cpus * sizeof(struct limine_mp_info));
1202
        return NULL;
1203
    }
1204
1205
    return ret;
1206
}
1207
1208
struct limine_mp_info *init_smp(size_t *cpu_count, uint32_t *bsp_phys_id,
1209
                                pagemap_t pagemap, uint64_t hhdm_offset) {
1210
    struct limine_mp_info *info = NULL;
1211
1212
    if (acpi_get_rsdp() && (info = try_acpi_smp(cpu_count, bsp_phys_id, pagemap, hhdm_offset)))
1213
        return info;
1214
1215
    void *dtb = get_device_tree_blob(NULL, 0, false);
1216
    if (dtb) {
1217
        info = try_dtb_smp(dtb, cpu_count, bsp_phys_id, pagemap, hhdm_offset);
1218
        pmm_free(dtb, fdt_totalsize(dtb));
1219
        return info;
1220
    }
1221
1222
    printv("Failed to figure out how to start APs.");
1223
1224
    return NULL;
1225
}
1226
1227
#else
1228
#error Unknown architecture
1229
#endif
tab: 248 wrap: offon