Skip to content

Commit

Permalink
[arch][riscv] change secondary cpu bootstrap api
Browse files Browse the repository at this point in the history
Instead of setting a counter of the number of secondaries to start, have
platform or target code pass in a list of harts to start instead. This
allows for there to be discontinuties in the layout of the cpu harts, or
in the case of some sifive based hardware, hart 0 is otherwise offline.
  • Loading branch information
travisg committed Apr 8, 2024
1 parent a070819 commit 00b06a8
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 49 deletions.
5 changes: 4 additions & 1 deletion arch/riscv/include/arch/riscv.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,10 @@ static inline uint riscv_current_hart(void) {
#endif
}

void riscv_set_secondary_count(int count);
// Platform should pass in a list of secondary harts to start, not
// including the boot hart. Will be trimmed to SMP_MAX_CPUS - 1.
// Machine mode will always get all of the secondaries released (for now).
void riscv_set_secondary_harts_to_start(const uint *harts, size_t count);

void riscv_exception_entry(void);
enum handler_return riscv_timer_exception(void);
Expand Down
66 changes: 38 additions & 28 deletions arch/riscv/mp.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@
* https://opensource.org/licenses/MIT
*/

#include <arch/riscv.h>

#include <lk/reg.h>
#include <lk/compiler.h>
#include <lk/debug.h>
#include <lk/trace.h>
#include <lk/err.h>
#include <lk/init.h>
#include <lk/main.h>
#include <stdlib.h>
#include <string.h>

#include <arch/atomic.h>
#include <arch/ops.h>
Expand All @@ -27,13 +31,16 @@
#define LOCAL_TRACE 0

// mapping of cpu -> hart
static int cpu_to_hart_map[SMP_MAX_CPUS];
static uint cpu_to_hart_map[SMP_MAX_CPUS];

// list of IPIs queued per cpu
static volatile int ipi_data[SMP_MAX_CPUS];

static spin_lock_t boot_cpu_lock = 1;
volatile int secondaries_to_init = SMP_MAX_CPUS - 1;

// list of cpus to boot, passed from platform
static uint harts_to_boot[SMP_MAX_CPUS];
static uint harts_to_boot_count = 0;

// modified in start.S to save the physical address of _start as the first cpu boots
uintptr_t _start_physical;
Expand Down Expand Up @@ -136,53 +143,56 @@ void riscv_secondary_entry(uint hart_id, uint __unused, uint cpu_id) {
riscv_get_mvendorid(), riscv_get_marchid(),
riscv_get_mimpid(), riscv_current_hart());

// atomic_add(&secondaries_to_init, -1);
// arch_mp_send_ipi(1 << 0, MP_IPI_GENERIC); // wake up hart0 to let it know this CPU has come up

lk_secondary_cpu_entry();
}

// platform can detect and set the number of cores to boot (optional)
void riscv_set_secondary_count(int count) {
if (count > SMP_MAX_CPUS - 1) {
count = SMP_MAX_CPUS - 1;
}
secondaries_to_init = count;
// platform hands the arch layer a list of harts to start, these will be
// started later in riscv_boot_secondaries()
void riscv_set_secondary_harts_to_start(const uint *harts, size_t count) {
harts_to_boot_count = MIN(count, SMP_MAX_CPUS);
memcpy(harts_to_boot, harts, harts_to_boot_count * sizeof(harts[0]));
}

// start any secondary cpus we are set to start. called on the boot processor
void riscv_boot_secondaries(void) {
// if theres nothing to start, abort here
if (secondaries_to_init == 0) {
if (harts_to_boot_count == 0) {
dprintf(INFO, "RISCV: No secondary harts to start\n");
return;
}

lk_init_secondary_cpus(secondaries_to_init);
lk_init_secondary_cpus(harts_to_boot_count);

#if RISCV_M_MODE
dprintf(INFO, "RISCV: Releasing %d secondary harts from purgatory\n", secondaries_to_init);
dprintf(INFO, "RISCV: Releasing all secondary harts from purgatory\n");

// For machine mode, we simply unblock all the trapped cpus in start.S by releasing
// the boot_cpu_lock. Via some mechanism or other, all of the cpus should have already entered
// start.S via the main entry point for this to work. This is the default behavior when
// running on QEMU, for example.
spin_unlock(&boot_cpu_lock);
#else
uint boot_hart = riscv_current_hart();
dprintf(INFO, "RISCV: Going to try to start %d secondary harts\n", secondaries_to_init);
dprintf(INFO, "RISCV: Going to try to start %d secondary harts\n", harts_to_boot_count);

// May as well release the boot cpu lock now, since there's no reason to hold back secondaries we
// haven't started yet.
spin_unlock(&boot_cpu_lock);

// use SBI HSM to boot the secondaries
// TODO: handle the range of harts we should consider, since they
// may not be zero based.
// Currently, starts from 0 and tries to start one extra core, with the idea
// that boot_hart should be one of them. This algorithm is somewhat broken, but
// works in the case of harts being 0-N and the boot hart being nonzero (but within [0...N]).
// Doesn't currently handle skipping cpus we shouldn't boot (like HART 0 on some machines)
for (uint i = 0; i <= (uint)secondaries_to_init; i++) {
uint boot_hart = riscv_current_hart();
for (uint i = 0; i < harts_to_boot_count; i++) {
// skip the boot hart
if (i != boot_hart) {
dprintf(INFO, "RISCV: using SBI to start hart %u at %#lx\n", i, _start_physical);
sbi_boot_hart(i, _start_physical, 0);
if (harts_to_boot[i] != boot_hart) {
dprintf(INFO, "RISCV: using SBI to start hart %u at %#lx\n", harts_to_boot[i], _start_physical);
sbi_boot_hart(harts_to_boot[i], _start_physical, 0);

// Pause a little bit here to give the secondary an opportunity to start. Not strictly
// needed but helps the boot output flow without much of a boot penalty. Remove
// if it's a problem.
thread_sleep(50);
}
}
#endif
/* release the secondary cpus */
spin_unlock(&boot_cpu_lock);
}


Expand Down
3 changes: 1 addition & 2 deletions arch/riscv/start.S
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,8 @@ LOCAL_FUNCTION(secondary_trap)

// bootstrap the secondary cpus
jal riscv_secondary_entry
#else
j hart_trap
#endif
// fallthrough if either no SMP or riscv_secondary_entry returns
END_FUNCTION(secondary_trap)

LOCAL_FUNCTION(hart_trap)
Expand Down
50 changes: 40 additions & 10 deletions platform/jh7110/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,23 @@ static void reserved_memory_callback(uint64_t base, uint64_t len, void *cookie)
}
}

struct detected_cpus {
size_t count;

struct {
uint hart;
} cpu[SMP_MAX_CPUS];
};

static void cpucallback(uint64_t id, void *cookie) {
int *cpu_count = (int *)cookie;
struct detected_cpus *cpus = cookie;

LTRACEF("id %#llx cookie %p\n", id, cookie);
LTRACEF("hart %#llx\n", id);

(*cpu_count)++;
if (cpus->count < SMP_MAX_CPUS) {
cpus->cpu[cpus->count].hart = id;
cpus->count++;
}
}

struct pcie_detect_state {
Expand All @@ -112,8 +123,8 @@ void platform_early_init(void) {

/* look for a flattened device tree in the second arg passed to us */
bool found_mem = false;
int cpu_count = 0;
struct reserved_memory_regions reserved = {0};
struct detected_cpus cpus = {0};

const void *fdt = (void *)lk_boot_args[1];
#if WITH_KERNEL_VM
Expand All @@ -126,7 +137,7 @@ void platform_early_init(void) {
.reserved_memory = reserved_memory_callback,
.reserved_memory_cookie = &reserved,
.cpu = cpucallback,
.cpucookie = &cpu_count,
.cpucookie = &cpus,
.pcie = pciecallback,
.pciecookie = &pcie_state,
};
Expand Down Expand Up @@ -163,12 +174,31 @@ void platform_early_init(void) {
}
#endif

if (cpu_count > 0) {
printf("FDT: found %d cpu%c\n", cpu_count, cpu_count == 1 ? ' ' : 's');
riscv_set_secondary_count(cpu_count - 1);
} else {
riscv_set_secondary_count(0);
#if WITH_SMP
// TODO: refactor this code into libfdt
if (cpus.count > 0) {
printf("FDT: found %zu cpu%c\n", cpus.count, cpus.count == 1 ? ' ' : 's');
uint harts[SMP_MAX_CPUS - 1];

// copy from the detected cpu list to an array of harts, excluding the boot hart
size_t hart_index = 0;
for (size_t i = 0; i < cpus.count; i++) {
if (cpus.cpu[i].hart != riscv_current_hart()) {
harts[hart_index++] = cpus.cpu[i].hart;
}

// we can start MAX CPUS - 1 secondaries
if (hart_index >= SMP_MAX_CPUS - 1) {
break;
}
}

// tell the riscv layer how many cores we have to start
if (hart_index > 0) {
riscv_set_secondary_harts_to_start(harts, hart_index);
}
}
#endif

LTRACEF("done scanning FDT\n");

Expand Down
50 changes: 42 additions & 8 deletions platform/qemu-virt-riscv/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <lk/main.h>
#include <lk/reg.h>
#include <lk/trace.h>
#include <arch/riscv.h>
#include <kernel/thread.h>
#include <platform.h>
#include <platform/interrupts.h>
Expand Down Expand Up @@ -92,12 +93,23 @@ static void reserved_memory_callback(uint64_t base, uint64_t len, void *cookie)
}
}

struct detected_cpus {
size_t count;

struct {
uint hart;
} cpu[SMP_MAX_CPUS];
};

static void cpucallback(uint64_t id, void *cookie) {
int *cpu_count = (int *)cookie;
struct detected_cpus *cpus = cookie;

LTRACEF("id %#llx cookie %p\n", id, cookie);
LTRACEF("hart %#llx\n", id);

(*cpu_count)++;
if (cpus->count < SMP_MAX_CPUS) {
cpus->cpu[cpus->count].hart = id;
cpus->count++;
}
}

struct pcie_detect_state {
Expand All @@ -118,8 +130,8 @@ void platform_early_init(void) {

/* look for a flattened device tree in the second arg passed to us */
bool found_mem = false;
int cpu_count = 0;
struct reserved_memory_regions reserved = {0};
struct detected_cpus cpus = {0};

const void *fdt = (void *)lk_boot_args[1];
#if WITH_KERNEL_VM
Expand All @@ -132,7 +144,7 @@ void platform_early_init(void) {
.reserved_memory = reserved_memory_callback,
.reserved_memory_cookie = &reserved,
.cpu = cpucallback,
.cpucookie = &cpu_count,
.cpucookie = &cpus,
.pcie = pciecallback,
.pciecookie = &pcie_state,
};
Expand Down Expand Up @@ -163,10 +175,32 @@ void platform_early_init(void) {
}
#endif

if (cpu_count > 0) {
printf("FDT: found %d cpu%c\n", cpu_count, cpu_count == 1 ? ' ' : 's');
riscv_set_secondary_count(cpu_count - 1);

#if WITH_SMP
// TODO: refactor this code into libfdt
if (cpus.count > 0) {
printf("FDT: found %zu cpu%c\n", cpus.count, cpus.count == 1 ? ' ' : 's');
uint harts[SMP_MAX_CPUS - 1];

// copy from the detected cpu list to an array of harts, excluding the boot hart
size_t hart_index = 0;
for (size_t i = 0; i < cpus.count; i++) {
if (cpus.cpu[i].hart != riscv_current_hart()) {
harts[hart_index++] = cpus.cpu[i].hart;
}

// we can start MAX CPUS - 1 secondaries
if (hart_index >= SMP_MAX_CPUS - 1) {
break;
}
}

// tell the riscv layer how many cores we have to start
if (hart_index > 0) {
riscv_set_secondary_harts_to_start(harts, hart_index);
}
}
#endif

LTRACEF("done scanning FDT\n");

Expand Down

0 comments on commit 00b06a8

Please sign in to comment.