@@ -35,7 +35,7 @@ static int vprlog(int log_level, const char *fmt, va_list ap)
if (log_level > (debug_descriptor.console_log_levels >> 4))
return 0;
- if (opal_v4_os) {
+ if (os_ops.os_printf) {
count = vsnprintf(buffer, sizeof(buffer), fmt, ap);
os_printf(log_level, buffer);
return count;
@@ -506,6 +506,12 @@ static int64_t opal_register_os_ops(struct opal_os_ops *ops, uint64_t size)
set_opal_console_to_raw();
}
+ if (size >= 24) {
+ os_ops.os_vm_map = (void *)be64_to_cpu(ops->os_vm_map);
+ os_ops.os_vm_unmap = (void *)be64_to_cpu(ops->os_vm_unmap);
+ vm_resurrect();
+ }
+
checksum_romem();
opal_v4_os = true;
@@ -519,6 +525,37 @@ void os_printf(uint32_t log_level, const char *str)
os_ops.os_printf(log_level, str);
}
+int64_t os_vm_map(uint64_t ea, uint64_t pa, uint64_t flags)
+{
+ struct cpu_thread *cpu = this_cpu();
+ uint64_t msr = mfmsr();
+ int64_t ret;
+
+ if (msr != cpu->opal_call_msr)
+ mtmsrd(cpu->opal_call_msr, 0);
+
+ ret = os_ops.os_vm_map(ea, pa, flags);
+
+ if (msr != cpu->opal_call_msr)
+ mtmsrd(cpu->opal_call_msr, 0);
+
+ return ret;
+}
+
+void os_vm_unmap(uint64_t ea)
+{
+ struct cpu_thread *cpu = this_cpu();
+ uint64_t msr = mfmsr();
+
+ if (msr != cpu->opal_call_msr)
+ mtmsrd(cpu->opal_call_msr, 0);
+
+ os_ops.os_vm_unmap(ea);
+
+ if (msr != cpu->opal_call_msr)
+ mtmsrd(cpu->opal_call_msr, 0);
+}
+
void add_opal_node(void)
{
uint64_t base, entry, size;
@@ -35,6 +35,7 @@ static bool vm_globals_allocated = false;
#define LOCAL_SLB_BASE GLOBAL_SLB_NR
#define LOCAL_EA_PERCPU (SLB_SZ)
+#define OS_LOCAL_EA_PERCPU (2ULL*1024*1024)
#define LOCAL_EA_BEGIN 0x0008000000000000ULL
#define LOCAL_EA_END 0x0009000000000000ULL
@@ -485,13 +486,16 @@ void *vm_map(unsigned long addr, unsigned long len, bool rw)
addr &= ~(PAGE_SIZE - 1);
len = end - addr;
- assert(len <= LOCAL_EA_PERCPU);
+ if (cpu_in_os())
+ assert(len <= OS_LOCAL_EA_PERCPU);
+ else
+ assert(len <= LOCAL_EA_PERCPU);
/* Can't do nested mappings */
assert(!c->vm_local_map_inuse);
c->vm_local_map_inuse = true;
- if (cpu_in_os() && c->vm_setup) {
+ if (cpu_in_os() && c->vm_setup && !os_ops.os_vm_map) {
assert(c->opal_call_msr & (MSR_IR|MSR_DR));
newaddr = addr;
mtmsr(c->opal_call_msr & ~MSR_DR);
@@ -501,9 +505,10 @@ void *vm_map(unsigned long addr, unsigned long len, bool rw)
} else {
struct vm_map *new = &c->vm_local_map;
- assert(!cpu_in_os());
-
- newaddr = LOCAL_EA_BEGIN + LOCAL_EA_PERCPU * c->pir;
+ if (cpu_in_os())
+ newaddr = LOCAL_EA_BEGIN + OS_LOCAL_EA_PERCPU * c->pir;
+ else
+ newaddr = LOCAL_EA_BEGIN + LOCAL_EA_PERCPU * c->pir;
new->name = "local";
new->address = newaddr;
@@ -513,6 +518,20 @@ void *vm_map(unsigned long addr, unsigned long len, bool rw)
new->writeable = rw;
new->executable = false;
new->ci = false;
+
+ if (cpu_in_os()) {
+ uint64_t ea, pa, flags;
+
+ flags = vmm_os_map_flags(new);
+
+ pa = new->pa;
+ ea = newaddr;
+ while (ea < newaddr + len) {
+ os_vm_map(ea, pa, flags);
+ ea += OS_PAGE_SIZE;
+ pa += OS_PAGE_SIZE;
+ }
+ }
}
return (void *)newaddr + offset;
@@ -528,12 +547,15 @@ void vm_unmap(unsigned long addr, unsigned long len)
addr &= ~(PAGE_SIZE - 1);
len = end - addr;
- assert(len <= LOCAL_EA_PERCPU);
+ if (cpu_in_os())
+ assert(len <= OS_LOCAL_EA_PERCPU);
+ else
+ assert(len <= LOCAL_EA_PERCPU);
assert(c->vm_local_map_inuse);
c->vm_local_map_inuse = false;
- if (cpu_in_os() && (c->opal_call_msr & (MSR_IR|MSR_DR))) {
+ if (cpu_in_os() && (c->opal_call_msr & (MSR_IR|MSR_DR)) && !os_ops.os_vm_map) {
assert(!c->vm_setup);
c->vm_setup = true;
mtmsr(c->opal_call_msr);
@@ -543,9 +565,10 @@ void vm_unmap(unsigned long addr, unsigned long len)
struct vm_map *vmm;
unsigned long ea;
- assert(!cpu_in_os());
-
- newaddr = LOCAL_EA_BEGIN + LOCAL_EA_PERCPU * c->pir;
+ if (cpu_in_os())
+ newaddr = LOCAL_EA_BEGIN + OS_LOCAL_EA_PERCPU * c->pir;
+ else
+ newaddr = LOCAL_EA_BEGIN + LOCAL_EA_PERCPU * c->pir;
vmm = &c->vm_local_map;
assert(newaddr == vmm->address);
@@ -554,8 +577,13 @@ void vm_unmap(unsigned long addr, unsigned long len)
ea = newaddr;
while (ea < newaddr + len) {
- htab_remove(ea, true);
- ea += PAGE_SIZE;
+ if (cpu_in_os()) {
+ os_vm_unmap(ea);
+ ea += OS_PAGE_SIZE;
+ } else {
+ htab_remove(ea, true);
+ ea += PAGE_SIZE;
+ }
}
}
}
@@ -987,6 +1015,24 @@ static int64_t opal_find_vm_area(uint64_t addr, struct opal_vm_area *opal_vm_are
return OPAL_SUCCESS;
}
+ if (addr < LOCAL_EA_BEGIN) {
+ opal_vm_area->address = cpu_to_be64(LOCAL_EA_BEGIN);
+ opal_vm_area->length = cpu_to_be64(cpu_max_pir * OS_LOCAL_EA_PERCPU);
+ opal_vm_area->pa = 0;
+ opal_vm_area->vm_flags = 0;
+
+ printf("0x%016llx-0x%016llx flags=0 (per-cpu maps)\n", LOCAL_EA_BEGIN, LOCAL_EA_BEGIN + cpu_max_pir * OS_LOCAL_EA_PERCPU);
+
+ return OPAL_SUCCESS;
+ }
+
return OPAL_EMPTY;
}
opal_call(OPAL_FIND_VM_AREA, opal_find_vm_area, 2);
+
+void vm_resurrect(void)
+{
+ /* OS takes over control of providing VM mappings */
+ prlog(PR_NOTICE, "VMM: Rise from your grave!\n");
+ print_maps();
+}
@@ -6,6 +6,7 @@
/* Alignment to which skiboot lays out memory. */
#define PAGE_SIZE 0x10000
+#define OS_PAGE_SIZE 0x10000
#define HAVE_TYPEOF 1
#define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1
@@ -1278,6 +1278,9 @@ struct opal_vm_area {
struct opal_os_ops {
__be64 os_printf; /* void printf(int32_t level, const char *str) */
+ __be64 os_vm_map; /* void os_vm_map(uint64_t ea, uint64_t pa, uint64_t flags) */
+ __be64 os_vm_unmap; /* static void os_vm_unmap(uint64_t ea) */
+
};
#endif /* __ASSEMBLY__ */
@@ -20,12 +20,16 @@ struct opal_table_entry {
struct os_ops {
void (*os_printf)(uint32_t log_level, const char *str);
+ int64_t (*os_vm_map)(uint64_t ea, uint64_t pa, uint64_t flags);
+ void (*os_vm_unmap)(uint64_t ea);
};
extern bool opal_v4_os;
extern struct os_ops os_ops;
extern void os_printf(uint32_t log_level, const char *str);
+extern int64_t os_vm_map(uint64_t ea, uint64_t pa, uint64_t flags);
+extern void os_vm_unmap(uint64_t ea);
#ifdef __CHECKER__
#define __opal_func_test_arg(__func, __nargs) 0
@@ -362,5 +362,6 @@ bool vm_dslb(uint64_t nia, uint64_t dar);
bool vm_islb(uint64_t nia);
bool vm_dsi(uint64_t nia, uint64_t dar, uint32_t dsisr);
bool vm_isi(uint64_t nia);
+void vm_resurrect(void);
#endif /* __SKIBOOT_H */
This implements vm_map/unmap API that an OS can provide. The per-CPU areas are advertised by OPAL_FIND_VM_AREA so page tables can be allocated ahead of time. The ops must not sleep or cause IPIs. This allows skiboot to run entirely in virtual mode and not have to drop to real mode to cope with vm_map(). Signed-off-by: Nicholas Piggin <npiggin@gmail.com> --- core/console-log.c | 2 +- core/opal.c | 37 ++++++++++++++++++++++ core/vm.c | 70 ++++++++++++++++++++++++++++++++++------- include/config.h | 1 + include/opal-api.h | 3 ++ include/opal-internal.h | 4 +++ include/skiboot.h | 1 + 7 files changed, 105 insertions(+), 13 deletions(-)