@@ -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;
@@ -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);
@@ -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)
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(-)