cpu/x86/mp_init.c: Add mp_run_on_all_cpus_synchronously
MTRR is a core level register which means 2 threads in one core share same MTRR. There is a race condition could happen that AP overrides BSP MTRR unintentionally. In order to prevent such race condition between BSP and APs, this patch provides a function to let BSP assign tasks to all APs and wait them to complete the assigned tasks. BUG=b:225766934 Change-Id: I8d1d49bca410c821a3ad0347548afc42eb860594 Signed-off-by: Kane Chen <kane.chen@intel.corp-partner.google.com> Signed-off-by: Arthur Heymans <arthur@aheymans.xyz> Reviewed-on: https://review.coreboot.org/c/coreboot/+/63566 Reviewed-by: Subrata Banik <subratabanik@google.com> Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
@ -859,6 +859,15 @@ static void trigger_smm_relocation(void)
|
|||||||
|
|
||||||
static struct mp_callback *ap_callbacks[CONFIG_MAX_CPUS];
|
static struct mp_callback *ap_callbacks[CONFIG_MAX_CPUS];
|
||||||
|
|
||||||
|
enum AP_STATUS {
|
||||||
|
/* AP takes the task but not yet finishes */
|
||||||
|
AP_BUSY = 1,
|
||||||
|
/* AP finishes the task or no task to run yet */
|
||||||
|
AP_NOT_BUSY
|
||||||
|
};
|
||||||
|
|
||||||
|
static atomic_t ap_status[CONFIG_MAX_CPUS];
|
||||||
|
|
||||||
static struct mp_callback *read_callback(struct mp_callback **slot)
|
static struct mp_callback *read_callback(struct mp_callback **slot)
|
||||||
{
|
{
|
||||||
struct mp_callback *ret;
|
struct mp_callback *ret;
|
||||||
@ -880,10 +889,10 @@ static void store_callback(struct mp_callback **slot, struct mp_callback *val)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum cb_err run_ap_work(struct mp_callback *val, long expire_us)
|
static enum cb_err run_ap_work(struct mp_callback *val, long expire_us, bool wait_ap_finish)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int cpus_accepted;
|
int cpus_accepted, cpus_finish;
|
||||||
struct stopwatch sw;
|
struct stopwatch sw;
|
||||||
int cur_cpu;
|
int cur_cpu;
|
||||||
|
|
||||||
@ -913,16 +922,28 @@ static enum cb_err run_ap_work(struct mp_callback *val, long expire_us)
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
cpus_accepted = 0;
|
cpus_accepted = 0;
|
||||||
|
cpus_finish = 0;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(ap_callbacks); i++) {
|
for (i = 0; i < ARRAY_SIZE(ap_callbacks); i++) {
|
||||||
if (cur_cpu == i)
|
if (cur_cpu == i)
|
||||||
continue;
|
continue;
|
||||||
if (read_callback(&ap_callbacks[i]) == NULL)
|
|
||||||
|
if (read_callback(&ap_callbacks[i]) == NULL) {
|
||||||
cpus_accepted++;
|
cpus_accepted++;
|
||||||
|
/* Only increase cpus_finish if AP took the task and not busy */
|
||||||
|
if (atomic_read(&ap_status[i]) == AP_NOT_BUSY)
|
||||||
|
cpus_finish++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if wait_ap_finish is true, need to make sure all CPUs finish task and return
|
||||||
|
* else just need to make sure all CPUs take task
|
||||||
|
*/
|
||||||
if (cpus_accepted == global_num_aps)
|
if (cpus_accepted == global_num_aps)
|
||||||
return CB_SUCCESS;
|
if (!wait_ap_finish || (cpus_finish == global_num_aps))
|
||||||
|
return CB_SUCCESS;
|
||||||
|
|
||||||
} while (expire_us <= 0 || !stopwatch_expired(&sw));
|
} while (expire_us <= 0 || !stopwatch_expired(&sw));
|
||||||
|
|
||||||
printk(BIOS_CRIT, "CRITICAL ERROR: AP call expired. %d/%d CPUs accepted.\n",
|
printk(BIOS_CRIT, "CRITICAL ERROR: AP call expired. %d/%d CPUs accepted.\n",
|
||||||
@ -948,6 +969,9 @@ static void ap_wait_for_instruction(void)
|
|||||||
|
|
||||||
per_cpu_slot = &ap_callbacks[cur_cpu];
|
per_cpu_slot = &ap_callbacks[cur_cpu];
|
||||||
|
|
||||||
|
/* Init ap_status[cur_cpu] to AP_NOT_BUSY and ready to take job */
|
||||||
|
atomic_set(&ap_status[cur_cpu], AP_NOT_BUSY);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
struct mp_callback *cb = read_callback(per_cpu_slot);
|
struct mp_callback *cb = read_callback(per_cpu_slot);
|
||||||
|
|
||||||
@ -955,16 +979,22 @@ static void ap_wait_for_instruction(void)
|
|||||||
asm ("pause");
|
asm ("pause");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
* Set ap_status to AP_BUSY before store_callback(per_cpu_slot, NULL).
|
||||||
|
* it's to let BSP know APs take tasks and busy to avoid race condition.
|
||||||
|
*/
|
||||||
|
atomic_set(&ap_status[cur_cpu], AP_BUSY);
|
||||||
|
|
||||||
/* Copy to local variable before signaling consumption. */
|
/* Copy to local variable before signaling consumption. */
|
||||||
memcpy(&lcb, cb, sizeof(lcb));
|
memcpy(&lcb, cb, sizeof(lcb));
|
||||||
mfence();
|
mfence();
|
||||||
store_callback(per_cpu_slot, NULL);
|
store_callback(per_cpu_slot, NULL);
|
||||||
if (lcb.logical_cpu_number && (cur_cpu !=
|
|
||||||
lcb.logical_cpu_number))
|
if (lcb.logical_cpu_number == MP_RUN_ON_ALL_CPUS ||
|
||||||
continue;
|
(cur_cpu == lcb.logical_cpu_number))
|
||||||
else
|
|
||||||
lcb.func(lcb.arg);
|
lcb.func(lcb.arg);
|
||||||
|
|
||||||
|
atomic_set(&ap_status[cur_cpu], AP_NOT_BUSY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -973,7 +1003,15 @@ enum cb_err mp_run_on_aps(void (*func)(void *), void *arg, int logical_cpu_num,
|
|||||||
{
|
{
|
||||||
struct mp_callback lcb = { .func = func, .arg = arg,
|
struct mp_callback lcb = { .func = func, .arg = arg,
|
||||||
.logical_cpu_number = logical_cpu_num};
|
.logical_cpu_number = logical_cpu_num};
|
||||||
return run_ap_work(&lcb, expire_us);
|
return run_ap_work(&lcb, expire_us, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum cb_err mp_run_on_aps_and_wait_for_complete(void (*func)(void *), void *arg,
|
||||||
|
int logical_cpu_num, long expire_us)
|
||||||
|
{
|
||||||
|
struct mp_callback lcb = { .func = func, .arg = arg,
|
||||||
|
.logical_cpu_number = logical_cpu_num};
|
||||||
|
return run_ap_work(&lcb, expire_us, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum cb_err mp_run_on_all_aps(void (*func)(void *), void *arg, long expire_us,
|
enum cb_err mp_run_on_all_aps(void (*func)(void *), void *arg, long expire_us,
|
||||||
@ -1008,6 +1046,16 @@ enum cb_err mp_run_on_all_cpus(void (*func)(void *), void *arg)
|
|||||||
return mp_run_on_aps(func, arg, MP_RUN_ON_ALL_CPUS, 1000 * USECS_PER_MSEC);
|
return mp_run_on_aps(func, arg, MP_RUN_ON_ALL_CPUS, 1000 * USECS_PER_MSEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum cb_err mp_run_on_all_cpus_synchronously(void (*func)(void *), void *arg)
|
||||||
|
{
|
||||||
|
/* Run on BSP first. */
|
||||||
|
func(arg);
|
||||||
|
|
||||||
|
/* For up to 1 second for AP to finish previous work. */
|
||||||
|
return mp_run_on_aps_and_wait_for_complete(func, arg, MP_RUN_ON_ALL_CPUS,
|
||||||
|
1000 * USECS_PER_MSEC);
|
||||||
|
}
|
||||||
|
|
||||||
enum cb_err mp_park_aps(void)
|
enum cb_err mp_park_aps(void)
|
||||||
{
|
{
|
||||||
struct stopwatch sw;
|
struct stopwatch sw;
|
||||||
|
@ -117,6 +117,10 @@ enum cb_err mp_run_on_all_aps(void (*func)(void *), void *arg, long expire_us,
|
|||||||
/* Like mp_run_on_aps() but also runs func on BSP. */
|
/* Like mp_run_on_aps() but also runs func on BSP. */
|
||||||
enum cb_err mp_run_on_all_cpus(void (*func)(void *), void *arg);
|
enum cb_err mp_run_on_all_cpus(void (*func)(void *), void *arg);
|
||||||
|
|
||||||
|
/* Like mp_run_on_all_cpus but make sure all APs finish executing the
|
||||||
|
function call. The time limit on a function call is 1 second. */
|
||||||
|
enum cb_err mp_run_on_all_cpus_synchronously(void (*func)(void *), void *arg);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Park all APs to prepare for OS boot. This is handled automatically
|
* Park all APs to prepare for OS boot. This is handled automatically
|
||||||
* by the coreboot infrastructure.
|
* by the coreboot infrastructure.
|
||||||
|
Reference in New Issue
Block a user