[RFC PATCH 08/10] powerpc/powernv: Set up an mm context to call OPAL in
Nicholas Piggin
npiggin at gmail.com
Sat May 2 21:19:12 AEST 2020
This creates an mm context to be used for OPAL V4 calls, which
is populated with ptes according to querying OPAL_FIND_VM_AREA.
Signed-off-by: Nicholas Piggin <npiggin at gmail.com>
---
arch/powerpc/platforms/powernv/opal-call.c | 21 +++-
arch/powerpc/platforms/powernv/opal.c | 119 ++++++++++++++++++++-
2 files changed, 137 insertions(+), 3 deletions(-)
diff --git a/arch/powerpc/platforms/powernv/opal-call.c b/arch/powerpc/platforms/powernv/opal-call.c
index e62a74dfb3d0..4bdad3d2fa18 100644
--- a/arch/powerpc/platforms/powernv/opal-call.c
+++ b/arch/powerpc/platforms/powernv/opal-call.c
@@ -104,6 +104,9 @@ typedef int64_t (*opal_v4_le_entry_fn)(uint64_t r3, uint64_t r4, uint64_t r5,
uint64_t r6, uint64_t r7, uint64_t r8,
uint64_t r9, uint64_t r10);
+extern struct mm_struct *opal_mm;
+extern bool opal_mm_enabled;
+
static int64_t opal_call(int64_t a0, int64_t a1, int64_t a2, int64_t a3,
int64_t a4, int64_t a5, int64_t a6, int64_t a7, int64_t opcode)
{
@@ -117,6 +120,8 @@ static int64_t opal_call(int64_t a0, int64_t a1, int64_t a2, int64_t a3,
fn = (opal_v4_le_entry_fn)(opal.v4_le_entry);
if (fn) {
+ struct mm_struct *old_mm = current->active_mm;
+
if (!mmu) {
BUG_ON(msr & MSR_EE);
ret = fn(opcode, a0, a1, a2, a3, a4, a5, a6);
@@ -126,11 +131,23 @@ static int64_t opal_call(int64_t a0, int64_t a1, int64_t a2, int64_t a3,
local_irq_save(flags);
hard_irq_disable(); /* XXX r13 */
msr &= ~MSR_EE;
- mtmsr(msr & ~(MSR_IR|MSR_DR));
+ if (!opal_mm_enabled)
+ mtmsr(msr & ~(MSR_IR|MSR_DR));
+
+ if (opal_mm_enabled && old_mm != opal_mm) {
+ current->active_mm = opal_mm;
+ switch_mm_irqs_off(NULL, opal_mm, current);
+ }
ret = fn(opcode, a0, a1, a2, a3, a4, a5, a6);
- mtmsr(msr);
+ if (opal_mm_enabled && old_mm != opal_mm) {
+ current->active_mm = old_mm;
+ switch_mm_irqs_off(NULL, old_mm, current);
+ }
+
+ if (!opal_mm_enabled)
+ mtmsr(msr);
local_irq_restore(flags);
return ret;
diff --git a/arch/powerpc/platforms/powernv/opal.c b/arch/powerpc/platforms/powernv/opal.c
index d00772d40680..98d6d7fc5411 100644
--- a/arch/powerpc/platforms/powernv/opal.c
+++ b/arch/powerpc/platforms/powernv/opal.c
@@ -45,6 +45,10 @@ struct opal_msg_node {
static DEFINE_SPINLOCK(msg_list_lock);
static LIST_HEAD(msg_list);
+struct mm_struct *opal_mm __read_mostly;
+bool opal_v4_present __read_mostly;
+bool opal_mm_enabled __read_mostly;
+
/* /sys/firmware/opal */
struct kobject *opal_kobj __read_mostly;
@@ -172,7 +176,12 @@ int __init early_init_dt_scan_opal(unsigned long node,
if (of_flat_dt_is_compatible(node, "ibm,opal-v3")) {
powerpc_firmware_features |= FW_FEATURE_OPAL;
- pr_debug("OPAL detected !\n");
+ if (of_flat_dt_is_compatible(node, "ibm,opal-v4")) {
+ opal_v4_present = true;
+ pr_debug("OPAL v4 runtime firmware\n");
+ } else {
+ pr_debug("OPAL detected !\n");
+ }
} else {
panic("OPAL v3 compatible firmware not detected, can not continue.\n");
}
@@ -187,6 +196,9 @@ int __init early_init_dt_scan_opal(unsigned long node,
pr_debug("OPAL v4 Entry = 0x%llx (v4_le_entryp=%p v4_le_entrysz=%d)\n",
opal.v4_le_entry, v4_le_entryp, v4_le_entrysz);
+ } else {
+ /* Can't use v4 entry */
+ opal_v4_present = false;
}
return 1;
@@ -1033,6 +1045,111 @@ static void opal_init_heartbeat(void)
kopald_tsk = kthread_run(kopald, NULL, "kopald");
}
+static pgprot_t opal_vm_flags_to_prot(uint64_t flags)
+{
+ pgprot_t prot;
+
+ BUG_ON(!flags);
+ if (flags & OS_VM_FLAG_EXECUTE) {
+ if (flags & OS_VM_FLAG_CI)
+ BUG();
+ if (flags & OS_VM_FLAG_WRITE)
+ prot = PAGE_KERNEL_X;
+ else
+ prot = PAGE_KERNEL_X /* XXX!? PAGE_KERNEL_ROX */;
+ } else {
+ if (flags & OS_VM_FLAG_WRITE)
+ prot = PAGE_KERNEL;
+ else if (flags & OS_VM_FLAG_READ)
+ prot = PAGE_KERNEL_RO;
+ else
+ BUG();
+ if (flags & OS_VM_FLAG_CI)
+ prot = pgprot_noncached(prot);
+ }
+ return prot;
+}
+
+static int __init opal_init_mm(void)
+{
+ struct mm_struct *mm;
+ unsigned long addr;
+ struct opal_vm_area vm_area;
+
+ mm = copy_init_mm();
+ if (!mm)
+ return -ENOMEM;
+
+ /* Set up initial mappings for OPAL. */
+ addr = 0;
+ while (opal_find_vm_area(addr, &vm_area) == OPAL_SUCCESS) {
+ unsigned long length;
+ unsigned long pa;
+ unsigned long flags;
+ unsigned long end;
+ pgprot_t prot;
+
+ addr = be64_to_cpu(vm_area.address);
+ length = be64_to_cpu(vm_area.length);
+ pa = be64_to_cpu(vm_area.pa);
+ flags = be64_to_cpu(vm_area.vm_flags);
+
+ if (flags == 0) {
+ /* flags == 0 is a special case */
+ BUG_ON(pa != 0);
+ } else {
+ /* Don't support non-linear (yet?) */
+ BUG_ON(addr != pa);
+ prot = opal_vm_flags_to_prot(flags);
+ }
+
+ /* Align to PAGE_SIZE */
+ end = (addr + length + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+ addr &= ~(PAGE_SIZE - 1);
+
+ while (addr < end) {
+ spinlock_t *ptl;
+ pte_t pte, *ptep;
+
+ ptep = get_locked_pte(mm, addr, &ptl);
+ if (flags) {
+ pte = pfn_pte(addr >> PAGE_SHIFT, prot);
+ set_pte_at(mm, addr, ptep, pte);
+ } else {
+ pte_clear(mm, addr, ptep);
+ }
+ pte_unmap_unlock(ptep, ptl);
+
+ addr += PAGE_SIZE;
+ }
+ }
+
+ printk("OPAL Virtual Memory Runtime Enabled, using PID=0x%04x\n", (unsigned int)mm->context.id);
+
+ opal_mm = mm;
+ opal_mm_enabled = true;
+
+ return 0;
+}
+
+static int __init opal_init_early(void)
+{
+ int rc;
+
+ if (opal_v4_present) {
+ if (radix_enabled()) {
+ /* Hash can't resolve SLB faults to the switched mm */
+ rc = opal_init_mm();
+ if (rc) {
+ pr_warn("OPAL virtual memory init failed, firmware will run in real-mode.\n");
+ }
+ }
+ }
+
+ return 0;
+}
+machine_early_initcall(powernv, opal_init_early);
+
static int __init opal_init(void)
{
struct device_node *np, *consoles, *leds;
--
2.23.0
More information about the Linuxppc-dev
mailing list