haswell: Parallel AP bringup
This patch parallelizes the AP startup for Haswell-based devices. It
does not touch the generic secondary startup code. Instead it provides
its own MP support matching up with the Haswell BWG. It seemed to be too
much trouble to support the old startup way and this new way. Because of
that parallel loading is the only thing supported.
A couple of things to note:
1. Micrcode needs to be loaded twice. Once before MTRR and caching is
enabled. And a second time after SMM relocation.
2. The sipi_vector is entirely self-contained. Once it is loaded and
written back to RAM the APs do not access memory outside of the
sipi_vector load location until a sync up in ramstage.
3. SMM relocation is kicked off by an IPI to self w/ SMI set as the
destination mode.
The following are timings from cbmem with dev mode disabled and recovery mode
enabled to boot directly into the kernel. This was done on the
baskingridge CRB with a 4-core 8-thread CPU and 2 DIMMs 1GiB each. The
kernel has console enabled on the serial port. Entry 70 is the device
initialization, and that is where the APs are brought up. With these two
examples it looks to shave off ~200 ms of boot time.
Before:
1:55,382
2:57,606 (2,223)
3:3,108,983 (3,051,377)
4:3,110,084 (1,101)
8:3,113,109 (3,024)
9:3,156,694 (43,585)
10:3,156,815 (120)
30:3,157,110 (295)
40:3,158,180 (1,069)
50:3,160,157 (1,977)
60:3,160,366 (208)
70:4,221,044 (1,060,677)
75:4,221,062 (18)
80:4,227,185 (6,122)
90:4,227,669 (484)
99:4,265,596 (37,927)
1000:4,267,822 (2,225)
1001:4,268,507 (685)
1002:4,268,780 (272)
1003:4,398,676 (129,896)
1004:4,398,979 (303)
1100:7,477,601 (3,078,621)
1101:7,480,210 (2,608)
After:
1:49,518
2:51,778 (2,259)
3:3,081,186 (3,029,407)
4:3,082,252 (1,066)
8:3,085,137 (2,884)
9:3,130,339 (45,202)
10:3,130,518 (178)
30:3,130,544 (26)
40:3,131,125 (580)
50:3,133,023 (1,897)
60:3,133,278 (255)
70:4,009,259 (875,980)
75:4,009,273 (13)
80:4,015,947 (6,674)
90:4,016,430 (482)
99:4,056,265 (39,835)
1000:4,058,492 (2,226)
1001:4,059,176 (684)
1002:4,059,450 (273)
1003:4,189,333 (129,883)
1004:4,189,770 (436)
1100:7,262,358 (3,072,588)
1101:7,263,926 (1,567)
Booted the baskingridge board as noted above. Also analyzed serial
messages with pcserial enabled.
Change-Id: Ifedc7f787953647c228b11afdb725686e38c4098
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: http://review.coreboot.org/2779
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
diff --git a/src/cpu/intel/haswell/haswell_init.c b/src/cpu/intel/haswell/haswell_init.c
index 9e62b31..82430b7 100644
--- a/src/cpu/intel/haswell/haswell_init.c
+++ b/src/cpu/intel/haswell/haswell_init.c
@@ -442,71 +442,30 @@
static unsigned ehci_debug_addr;
#endif
-/*
- * Initialize any extra cores/threads in this package.
- */
-static void intel_cores_init(device_t cpu)
+static void bsp_init_before_ap_bringup(struct bus *cpu_bus)
{
- struct cpuid_result result;
- unsigned threads_per_package, threads_per_core, i;
-
- /* Logical processors (threads) per core */
- result = cpuid_ext(0xb, 0);
- threads_per_core = result.ebx & 0xffff;
-
- /* Logical processors (threads) per package */
- result = cpuid_ext(0xb, 1);
- threads_per_package = result.ebx & 0xffff;
-
- /* Only initialize extra cores from BSP */
- if (cpu->path.apic.apic_id)
- return;
-
- printk(BIOS_DEBUG, "CPU: %u has %u cores, %u threads per core\n",
- cpu->path.apic.apic_id, threads_per_package/threads_per_core,
- threads_per_core);
-
- for (i = 1; i < threads_per_package; ++i) {
- struct device_path cpu_path;
- device_t new;
-
- /* Build the cpu device path */
- cpu_path.type = DEVICE_PATH_APIC;
- cpu_path.apic.apic_id =
- cpu->path.apic.apic_id + i;
-
- /* Update APIC ID if no hyperthreading */
- if (threads_per_core == 1)
- cpu_path.apic.apic_id <<= 1;
-
- /* Allocate the new cpu device structure */
- new = alloc_dev(cpu->bus, &cpu_path);
- if (!new)
- continue;
-
- printk(BIOS_DEBUG, "CPU: %u has core %u\n",
- cpu->path.apic.apic_id,
- new->path.apic.apic_id);
-
-#if CONFIG_SMP && CONFIG_MAX_CPUS > 1
- /* Start the new cpu */
- if (!start_cpu(new)) {
- /* Record the error in cpu? */
- printk(BIOS_ERR, "CPU %u would not start!\n",
- new->path.apic.apic_id);
- }
-#endif
- }
-}
-
-static void bsp_init_before_ap_bringup(void)
-{
+ struct device_path cpu_path;
+ struct cpu_info *info;
char processor_name[49];
/* Print processor name */
fill_processor_name(processor_name);
printk(BIOS_INFO, "CPU: %s.\n", processor_name);
+ /* Ensure the local apic is enabled */
+ enable_lapic();
+
+ /* Set the device path of the boot cpu. */
+ cpu_path.type = DEVICE_PATH_APIC;
+ cpu_path.apic.apic_id = lapicid();
+
+ /* Find the device structure for the boot cpu. */
+ info = cpu_info();
+ info->cpu = alloc_find_dev(cpu_bus, &cpu_path);
+
+ if (info->index != 0)
+ printk(BIOS_CRIT, "BSP index(%d) != 0!\n", info->index);
+
#if CONFIG_USBDEBUG
// Is this caution really needed?
if(!ehci_debug_addr)
@@ -523,23 +482,12 @@
set_ehci_debug(ehci_debug_addr);
#endif
- enable_lapic();
+ /* Call through the cpu driver's initialization. */
+ cpu_initialize(0);
}
-static void ap_init(device_t cpu)
-{
- /* Microcode needs to be loaded before caching is enabled. */
- intel_update_microcode_from_cbfs();
-
- /* Turn on caching if we haven't already */
- x86_enable_cache();
- x86_setup_fixed_mtrrs();
- x86_setup_var_mtrrs(cpuid_eax(0x80000008) & 0xff, 2);
-
- enable_lapic();
-}
-
-static void cpu_common_init(device_t cpu)
+/* All CPUs including BSP will run the following function. */
+static void haswell_init(device_t cpu)
{
/* Clear out pending MCEs */
configure_mca();
@@ -572,33 +520,40 @@
void bsp_init_and_start_aps(struct bus *cpu_bus)
{
+ int max_cpus;
+ int num_aps;
+ const void *microcode_patch;
+
/* Perform any necesarry BSP initialization before APs are brought up.
* This call alos allows the BSP to prepare for any secondary effects
* from calling cpu_initialize() such as smm_init(). */
- bsp_init_before_ap_bringup();
+ bsp_init_before_ap_bringup(cpu_bus);
- /*
- * This calls into the gerneic initialize_cpus() which attempts to
- * start APs on the APIC bus in the devicetree. No APs get started
- * because there is only the BSP and a placeholder (disabled) in the
- * devicetree. initialize_cpus() also does SMM initialization by way
- * of smm_init(). It will eventually call cpu_initialize(0) which calls
- * dev_ops->init(). For Haswell the dev_ops->init() starts up the APs
- * by way of intel_cores_init().
- */
- initialize_cpus(cpu_bus);
-}
+ microcode_patch = intel_microcode_find();
-static void haswell_init(device_t cpu)
-{
- if (cpu->path.apic.apic_id == 0) {
- cpu_common_init(cpu);
- /* Start up extra cores */
- intel_cores_init(cpu);
- } else {
- ap_init(cpu);
- cpu_common_init(cpu);
+ /* This needs to be called after the mtrr setup so the BSP mtrrs
+ * can be mirrored by the APs. */
+ if (setup_ap_init(cpu_bus, &max_cpus, microcode_patch)) {
+ printk(BIOS_CRIT, "AP setup initialization failed. "
+ "No APs will be brought up.\n");
+ return;
}
+
+ num_aps = max_cpus - 1;
+ if (start_aps(cpu_bus, num_aps)) {
+ printk(BIOS_CRIT, "AP startup failed. Trying to continue.\n");
+ }
+
+ if (smm_initialize()) {
+ printk(BIOS_CRIT, "SMM Initialiazation failed...\n");
+ return;
+ }
+
+ /* Release APs to perform SMM relocation. */
+ release_aps_for_smm_relocation();
+
+ /* After SMM relocation a 2nd microcode load is required. */
+ intel_microcode_load_unlocked(microcode_patch);
}
static struct device_operations cpu_dev_ops = {