northbridge/intel/sandybridge/raminit: Prepare MRC path for x86_64

- Remove pointers in argument list passed to MRC to make sure the struct
  has the same size on x86_64 as on x86_32.
- Add assembly wrapper to call the MRC with argument in EAX.
- Wrap calling MRC in protected_mode_call_2arg, which is a stub on x86_32

Tested: Boots on Lenovo X220 using MRC in x86_32 and x86_64 mode.

Change-Id: Id755e7381c5a94360e3511c53432d68b7687df67
Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/79751
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Jérémy Compostella <jeremy.compostella@intel.com>
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
diff --git a/src/northbridge/intel/sandybridge/Makefile.inc b/src/northbridge/intel/sandybridge/Makefile.inc
index 36fc16b..6ecb17a 100644
--- a/src/northbridge/intel/sandybridge/Makefile.inc
+++ b/src/northbridge/intel/sandybridge/Makefile.inc
@@ -27,6 +27,7 @@
 romstage-y += raminit_tables.c
 else
 romstage-y += raminit_mrc.c
+romstage-y += mrc_wrapper.S
 cbfs-files-y += mrc.bin
 mrc.bin-file := $(call strip_quotes,$(CONFIG_MRC_FILE))
 mrc.bin-position := 0xfffa0000
diff --git a/src/northbridge/intel/sandybridge/mrc_wrapper.S b/src/northbridge/intel/sandybridge/mrc_wrapper.S
new file mode 100644
index 0000000..860526b
--- /dev/null
+++ b/src/northbridge/intel/sandybridge/mrc_wrapper.S
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Functions to call MRC.bin from x86_64 */
+
+.text
+.code32
+	.section ".text.mrc_wrapper", "ax", @progbits
+	.globl mrc_wrapper
+mrc_wrapper:
+	/* Set up new stack frame */
+	pushal
+	mov	%esp, %ebp
+
+	/* Align stack */
+	andl	$0xfffffff0, %esp
+
+	movl	36(%ebp), %ebx	/* Get function pointer */
+	movl	40(%ebp), %eax	/* Get argument */
+
+	call	*%ebx
+
+	/* Place return value on stack so that popal fetches it */
+	movl	%eax, 28(%ebp)
+
+	/* Restore stack pointer */
+	mov	%ebp, %esp
+	popal
+	ret
diff --git a/src/northbridge/intel/sandybridge/pei_data.h b/src/northbridge/intel/sandybridge/pei_data.h
index 7abc25f..0309cf3 100644
--- a/src/northbridge/intel/sandybridge/pei_data.h
+++ b/src/northbridge/intel/sandybridge/pei_data.h
@@ -50,10 +50,10 @@
 	uint32_t scrambler_seed;
 	uint32_t scrambler_seed_s3;
 	/* Data read from flash and passed into MRC */
-	unsigned char *mrc_input;
+	uint32_t mrc_input_ptr;
 	unsigned int mrc_input_len;
 	/* Data from MRC that should be saved to flash */
-	unsigned char *mrc_output;
+	uint32_t mrc_output_ptr;
 	unsigned int mrc_output_len;
 	/*
 	 * Max frequency DDR3 could be ran at. Could be one of four values:
@@ -87,7 +87,9 @@
 	 * which DIMMs should use the SPD from spd_data[0].
 	 */
 	uint8_t spd_data[4][256];
-	tx_byte_func tx_byte;
+	/* 32 bit pointer to tx_byte_func */
+	uint32_t tx_byte_ptr;
+
 	int ddr3lv_support;
 	/*
 	 * pcie_init needs to be set to 1 to have the system agent initialize PCIe.
diff --git a/src/northbridge/intel/sandybridge/raminit_mrc.c b/src/northbridge/intel/sandybridge/raminit_mrc.c
index 68b02b8..d59aa86 100644
--- a/src/northbridge/intel/sandybridge/raminit_mrc.c
+++ b/src/northbridge/intel/sandybridge/raminit_mrc.c
@@ -28,6 +28,7 @@
 #include <security/vboot/vboot_common.h>
 #include <southbridge/intel/bd82x6x/pch.h>
 #include <memory_info.h>
+#include <mode_switch.h>
 
 /* Management Engine is in the southbridge */
 #include <southbridge/intel/bd82x6x/me.h>
@@ -49,13 +50,17 @@
 
 #define MRC_CACHE_VERSION 0
 
+/* Assembly functions: */
+void mrc_wrapper(void *func_ptr, uint32_t arg1);
+
 static void save_mrc_data(struct pei_data *pei_data)
 {
 	u16 c1, c2, checksum;
 
 	/* Save the MRC S3 restore data to cbmem */
-	mrc_cache_stash_data(MRC_TRAINING_DATA, MRC_CACHE_VERSION, pei_data->mrc_output,
-			pei_data->mrc_output_len);
+	mrc_cache_stash_data(MRC_TRAINING_DATA, MRC_CACHE_VERSION,
+			     (void *)(uintptr_t)pei_data->mrc_output_ptr,
+			     pei_data->mrc_output_len);
 
 	/* Save the MRC seed values to CMOS */
 	cmos_write32(pei_data->scrambler_seed, CMOS_OFFSET_MRC_SEED);
@@ -81,7 +86,7 @@
 	size_t mrc_size;
 
 	/* Preset just in case there is an error */
-	pei_data->mrc_input = NULL;
+	pei_data->mrc_input_ptr = 0;
 	pei_data->mrc_input_len = 0;
 
 	/* Read scrambler seeds from CMOS */
@@ -108,18 +113,18 @@
 		return;
 	}
 
-	pei_data->mrc_input = mrc_cache_current_mmap_leak(MRC_TRAINING_DATA,
+	pei_data->mrc_input_ptr = (uintptr_t)mrc_cache_current_mmap_leak(MRC_TRAINING_DATA,
 							  MRC_CACHE_VERSION,
 							  &mrc_size);
-	if (!pei_data->mrc_input) {
+	if (!pei_data->mrc_input_ptr) {
 		/* Error message printed in find_current_mrc_cache */
 		return;
 	}
 
 	pei_data->mrc_input_len = mrc_size;
 
-	printk(BIOS_DEBUG, "%s: at %p, size %zx\n", __func__,
-	       pei_data->mrc_input, mrc_size);
+	printk(BIOS_DEBUG, "%s: at 0x%x, size %zx\n", __func__,
+	       pei_data->mrc_input_ptr, mrc_size);
 }
 
 /**
@@ -129,7 +134,7 @@
  */
 static void sdram_initialize(struct pei_data *pei_data)
 {
-	int (*entry)(struct pei_data *pei_data) __attribute__((regparm(1)));
+	int (*entry)(struct pei_data *pei_data);
 
 	/* Wait for ME to be ready */
 	intel_early_me_init();
@@ -144,19 +149,19 @@
 	prepare_mrc_cache(pei_data);
 
 	/* If MRC data is not found we cannot continue S3 resume. */
-	if (pei_data->boot_mode == 2 && !pei_data->mrc_input) {
+	if (pei_data->boot_mode == 2 && !pei_data->mrc_input_ptr) {
 		printk(BIOS_DEBUG, "Giving up in %s: No MRC data\n", __func__);
 		system_reset();
 	}
 
 	/* Pass console handler in pei_data */
-	pei_data->tx_byte = do_putchar;
+	pei_data->tx_byte_ptr = (uintptr_t)do_putchar;
 
 	/* Locate and call UEFI System Agent binary. */
 	entry = cbfs_map("mrc.bin", NULL);
 	if (entry) {
 		int rv;
-		rv = entry(pei_data);
+		rv = protected_mode_call_2arg(mrc_wrapper, (uintptr_t)entry, (uintptr_t)pei_data);
 		if (rv) {
 			switch (rv) {
 			case -1:
@@ -403,7 +408,7 @@
 
 	/* Sanity check mrc_var location by verifying a known field */
 	mrc_var = (void *)DCACHE_RAM_MRC_VAR_BASE;
-	if (mrc_var->tx_byte == (uintptr_t)pei_data.tx_byte) {
+	if (mrc_var->tx_byte == pei_data.tx_byte_ptr) {
 		printk(BIOS_DEBUG, "MRC_VAR pool occupied [%08x,%08x]\n",
 		       mrc_var->pool_base, mrc_var->pool_base + mrc_var->pool_used);