diff mbox series

[RFC,6/9] OPAL V4 calling convention

Message ID 20200502113649.176329-7-npiggin@gmail.com
State New
Headers show
Series OPAL V4 | expand

Checks

Context Check Description
snowpatch_ozlabs/apply_patch warning Failed to apply on branch master (0f1937ef40fca0c3212a9dff1010b832a24fb063)
snowpatch_ozlabs/apply_patch fail Failed to apply to any branch

Commit Message

Nicholas Piggin May 2, 2020, 11:36 a.m. UTC
This is a new calling convention, currently implemented only a LE entry
point for LE skiboot builds.

It is simply called as an indirect function, with the first argument
selecting the opal call token, and the subsequent arguments are the opal
call args.

The caller's stack is used by OPAL!

Linux tries very hard to ensure the stack is re-entrant across interrupts
including non-maskable ones. We've currently hacked around this in skiboot
by using a different part of the skiboot stack if OPAL is re-entered, but
this is fragile and error prone. This makes it much more simple to add a
machine check handling OPAL API, for example.

This API may be called with MMU enabled!

If the OS sets up a virtual memory environment described by
OPAL_FIND_VM_AREA, it may call this API with MSR[IR] and MSR[DR] enabled,
or may call in real mode. Currently skiboot immediately drops to real
mode to make the call, and back to virtual mode to return.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 core/cpu.c    |  9 +-----
 core/opal.c   | 85 +++++++++++++++++++++++++++++++++++++++++++++++++--
 include/cpu.h |  9 ++++++
 3 files changed, 93 insertions(+), 10 deletions(-)
diff mbox series

Patch

diff --git a/core/cpu.c b/core/cpu.c
index 30f9c6e70..3cf43ab1c 100644
--- a/core/cpu.c
+++ b/core/cpu.c
@@ -23,14 +23,7 @@ 
 /* The cpu_threads array is static and indexed by PIR in
  * order to speed up lookup from asm entry points
  */
-struct cpu_stack {
-	union {
-		uint8_t	stack[STACK_SIZE];
-		struct cpu_thread cpu;
-	};
-} __align(STACK_SIZE);
-
-static struct cpu_stack * const cpu_stacks = (struct cpu_stack *)CPU_STACKS_BASE;
+struct cpu_stack * const cpu_stacks = (struct cpu_stack *)CPU_STACKS_BASE;
 unsigned int cpu_thread_count;
 unsigned int cpu_max_pir;
 struct cpu_thread *boot_cpu;
diff --git a/core/opal.c b/core/opal.c
index 864c244cc..27ce7a375 100644
--- a/core/opal.c
+++ b/core/opal.c
@@ -388,6 +388,79 @@  static void add_opal_firmware_node(void)
 	add_opal_firmware_exports_node(firmware);
 }
 
+typedef int64_t (*opal_call_fn)(uint64_t r3, uint64_t r4, uint64_t r5,
+				uint64_t r6, uint64_t r7, uint64_t r8,
+				uint64_t r9);
+
+static int64_t opal_v4_le_entry(uint64_t token, uint64_t r4, uint64_t r5,
+				uint64_t r6, uint64_t r7, uint64_t r8,
+				uint64_t r9, uint64_t r10)
+{
+	struct cpu_thread *cpu;
+	opal_call_fn *fn;
+	uint64_t msr;
+	uint32_t pir;
+	uint64_t r16;
+	uint64_t ret;
+
+	msr = mfmsr();
+	assert(!(msr & MSR_EE));
+
+	if (msr & (MSR_IR|MSR_DR))
+		mtmsrd(msr & ~(MSR_IR|MSR_DR), 0);
+
+	pir = mfspr(SPR_PIR);
+	r16 = (uint64_t)__this_cpu;
+	__this_cpu = &cpu_stacks[pir].cpu;
+	cpu = this_cpu();
+
+	cpu->in_opal_call++;
+	if (cpu->in_opal_call == 1) {
+		cpu->current_token = token;
+		cpu->entered_opal_call_at = mftb();
+	}
+
+	if (!opal_check_token(token)) {
+		ret = opal_bad_token(token);
+		goto out;
+	}
+
+	fn = (opal_call_fn *)(&opal_branch_table[token]);
+
+	ret = (*fn)(r4, r5, r6, r7, r8, r9, r10);
+
+out:
+	if (!list_empty(&cpu->locks_held)) {
+		prlog(PR_ERR, "OPAL exiting with locks held, pir=%04x token=%llu retval=%lld\n",
+		      pir, token, ret);
+		drop_my_locks(true);
+	}
+
+	if (token != OPAL_RESYNC_TIMEBASE) {
+		uint64_t call_time;
+
+		call_time = tb_to_msecs(mftb() - cpu->entered_opal_call_at);
+		if (call_time > 100) {
+			prlog((call_time < 1000) ? PR_DEBUG : PR_WARNING,
+			      "Spent %llu msecs in OPAL call %llu!\n",
+			      call_time, token);
+		}
+	}
+
+	if (cpu->in_opal_call == 1) {
+		cpu->current_token = 0;
+	}
+	assert(cpu->in_opal_call > 0);
+	cpu->in_opal_call--;
+
+	if (msr != mfmsr())
+		mtmsrd(msr, 0);
+
+	__this_cpu = (struct cpu_thread *)r16;
+
+	return ret;
+}
+
 void add_opal_node(void)
 {
 	uint64_t base, entry, size;
@@ -412,16 +485,24 @@  void add_opal_node(void)
 	dt_add_property_cells(opal_node, "#address-cells", 0);
 	dt_add_property_cells(opal_node, "#size-cells", 0);
 
-	if (proc_gen < proc_gen_p9)
+	if (proc_gen < proc_gen_p9) {
 		dt_add_property_strings(opal_node, "compatible", "ibm,opal-v2",
 					"ibm,opal-v3");
-	else
+	} else if (HAVE_LITTLE_ENDIAN) {
+		dt_add_property_strings(opal_node, "compatible", "ibm,opal-v3",
+					"ibm,opal-v4");
+	} else {
 		dt_add_property_strings(opal_node, "compatible", "ibm,opal-v3");
+	}
 
 	dt_add_property_cells(opal_node, "opal-msg-async-num", OPAL_MAX_ASYNC_COMP);
 	dt_add_property_cells(opal_node, "opal-msg-size", OPAL_MSG_SIZE);
 	dt_add_property_u64(opal_node, "opal-base-address", base);
 	dt_add_property_u64(opal_node, "opal-entry-address", entry);
+	if (HAVE_LITTLE_ENDIAN) {
+		dt_add_property_u64(opal_node, "opal-v4-le-entry-address",
+						(uint64_t)&opal_v4_le_entry);
+	}
 	dt_add_property_u64(opal_node, "opal-boot-address", (uint64_t)&boot_entry);
 	dt_add_property_u64(opal_node, "opal-runtime-size", size);
 
diff --git a/include/cpu.h b/include/cpu.h
index 5e8acbf6a..b769024d1 100644
--- a/include/cpu.h
+++ b/include/cpu.h
@@ -234,6 +234,15 @@  extern u8 get_available_nr_cores_in_chip(u32 chip_id);
 	for (core = first_available_core_in_chip(chip_id); core; \
 		core = next_available_core_in_chip(core, chip_id))
 
+struct cpu_stack {
+	union {
+		uint8_t	stack[STACK_SIZE];
+		struct cpu_thread cpu;
+	};
+} __align(STACK_SIZE);
+
+extern struct cpu_stack * const cpu_stacks;
+
 /* Return the caller CPU (only after init_cpu_threads) */
 register struct cpu_thread *__this_cpu asm("r16");
 static inline __nomcount struct cpu_thread *this_cpu(void)