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:
Kane Chen 2022-04-29 16:35:23 +08:00 committed by Werner Zeh
parent 8b02bd1f8d
commit c9b1f8a28e
2 changed files with 61 additions and 9 deletions

View File

@ -859,6 +859,15 @@ static void trigger_smm_relocation(void)
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)
{
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 cpus_accepted;
int cpus_accepted, cpus_finish;
struct stopwatch sw;
int cur_cpu;
@ -913,16 +922,28 @@ static enum cb_err run_ap_work(struct mp_callback *val, long expire_us)
do {
cpus_accepted = 0;
cpus_finish = 0;
for (i = 0; i < ARRAY_SIZE(ap_callbacks); i++) {
if (cur_cpu == i)
continue;
if (read_callback(&ap_callbacks[i]) == NULL)
if (read_callback(&ap_callbacks[i]) == NULL) {
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)
return CB_SUCCESS;
if (!wait_ap_finish || (cpus_finish == global_num_aps))
return CB_SUCCESS;
} while (expire_us <= 0 || !stopwatch_expired(&sw));
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];
/* 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) {
struct mp_callback *cb = read_callback(per_cpu_slot);
@ -955,16 +979,22 @@ static void ap_wait_for_instruction(void)
asm ("pause");
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. */
memcpy(&lcb, cb, sizeof(lcb));
mfence();
store_callback(per_cpu_slot, NULL);
if (lcb.logical_cpu_number && (cur_cpu !=
lcb.logical_cpu_number))
continue;
else
if (lcb.logical_cpu_number == MP_RUN_ON_ALL_CPUS ||
(cur_cpu == lcb.logical_cpu_number))
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,
.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,
@ -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);
}
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)
{
struct stopwatch sw;

View File

@ -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. */
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
* by the coreboot infrastructure.