aboutsummaryrefslogtreecommitdiff
path: root/pc-bios/vof
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2021-07-10 16:06:24 +0100
committerPeter Maydell <peter.maydell@linaro.org>2021-07-10 16:06:24 +0100
commitfc32b91a88cc9cd560da5488bdca4d69f2bac620 (patch)
tree3e9848c0aeb1d570879368e1636b6918216a01cd /pc-bios/vof
parentebd1f710029e9a5746541d80508d8ea9956b81fc (diff)
parent82123b756a1a2f1965350e5794aaa7b5c6a15282 (diff)
Merge remote-tracking branch 'remotes/dg-gitlab/tags/ppc-for-6.1-20210709' into staging
ppc patch queue 2021-07-09 Here's a (probably) final pull request before the qemu-6.1 soft freeze. Includes: * Implementation of the new H_RPT_INVALIDATE hypercall * Virtual Open Firmware for pSeries and pegasos2 machine types. This is an experimental minimal Open Firmware implementation which works by delegating nearly everything to qemu itself via a special hypercall. * A number of cleanups to the ppc soft MMU code * Fix to handling of two-level radix mode translations for the powernv machine type * Update the H_GET_CPU_CHARACTERISTICS call with newly defined bits. This will allow more flexible handling of possible future CPU Spectre-like flaws * Correctly treat mtmsrd as an illegal instruction on BookE cpus * Firmware update for the ppce500 machine type # gpg: Signature made Fri 09 Jul 2021 06:16:42 BST # gpg: using RSA key 75F46586AE61A66CC44E87DC6C38CACA20D9B392 # gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" [full] # gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>" [full] # gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" [full] # gpg: aka "David Gibson (kernel.org) <dwg@kernel.org>" [unknown] # Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392 * remotes/dg-gitlab/tags/ppc-for-6.1-20210709: (33 commits) target/ppc: Support for H_RPT_INVALIDATE hcall linux-headers: Update spapr: Fix implementation of Open Firmware client interface target/ppc: Don't compile ppc_tlb_invalid_all without TCG ppc/pegasos2: Implement some RTAS functions with VOF ppc/pegasos2: Fix use of && instead of & ppc/pegasos2: Use Virtual Open Firmware as firmware replacement target/ppc/spapr: Update H_GET_CPU_CHARACTERISTICS L1D cache flush bits target/ppc: Allow virtual hypervisor on CPU without HV ppc/pegasos2: Introduce Pegasos2MachineState structure target/ppc: mtmsrd is an illegal instruction on BookE spapr: Implement Open Firmware client interface docs/system: ppc: Update ppce500 documentation with eTSEC support roms/u-boot: Bump ppce500 u-boot to v2021.07 to add eTSEC support target/ppc: change ppc_hash32_xlate to use mmu_idx target/ppc: introduce mmu-books.h target/ppc: changed ppc_hash64_xlate to use mmu_idx target/ppc: fix address translation bug for radix mmus target/ppc: Fix compilation with DEBUG_BATS debug option target/ppc: Fix compilation with FLUSH_ALL_TLBS debug option ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'pc-bios/vof')
-rw-r--r--pc-bios/vof/Makefile23
-rw-r--r--pc-bios/vof/bootmem.c14
-rw-r--r--pc-bios/vof/ci.c91
-rw-r--r--pc-bios/vof/entry.S49
-rw-r--r--pc-bios/vof/libc.c66
-rw-r--r--pc-bios/vof/main.c21
-rw-r--r--pc-bios/vof/vof.h41
-rw-r--r--pc-bios/vof/vof.lds48
8 files changed, 353 insertions, 0 deletions
diff --git a/pc-bios/vof/Makefile b/pc-bios/vof/Makefile
new file mode 100644
index 0000000000..aa1678c4d8
--- /dev/null
+++ b/pc-bios/vof/Makefile
@@ -0,0 +1,23 @@
+all: build-all
+
+build-all: vof.bin
+
+CROSS ?=
+CC = $(CROSS)gcc
+LD = $(CROSS)ld
+OBJCOPY = $(CROSS)objcopy
+
+%.o: %.S
+ $(CC) -m32 -mbig-endian -mcpu=power4 -c -o $@ $<
+
+%.o: %.c
+ $(CC) -m32 -mbig-endian -mcpu=power4 -c -fno-stack-protector -o $@ $<
+
+vof.elf: entry.o main.o ci.o bootmem.o libc.o
+ $(LD) -nostdlib -e_start -Tvof.lds -EB -o $@ $^
+
+%.bin: %.elf
+ $(OBJCOPY) -O binary -j .text -j .data -j .toc -j .got2 $^ $@
+
+clean:
+ rm -f *.o vof.bin vof.elf *~
diff --git a/pc-bios/vof/bootmem.c b/pc-bios/vof/bootmem.c
new file mode 100644
index 0000000000..771b9e95f9
--- /dev/null
+++ b/pc-bios/vof/bootmem.c
@@ -0,0 +1,14 @@
+#include "vof.h"
+
+void boot_from_memory(uint64_t initrd, uint64_t initrdsize)
+{
+ uint64_t kern[2];
+ phandle chosen = ci_finddevice("/chosen");
+
+ if (ci_getprop(chosen, "qemu,boot-kernel", kern, sizeof(kern)) !=
+ sizeof(kern)) {
+ return;
+ }
+
+ do_boot(kern[0], initrd, initrdsize);
+}
diff --git a/pc-bios/vof/ci.c b/pc-bios/vof/ci.c
new file mode 100644
index 0000000000..fc4821b3e9
--- /dev/null
+++ b/pc-bios/vof/ci.c
@@ -0,0 +1,91 @@
+#include "vof.h"
+
+struct prom_args {
+ uint32_t service;
+ uint32_t nargs;
+ uint32_t nret;
+ uint32_t args[10];
+};
+
+typedef unsigned long prom_arg_t;
+
+#define ADDR(x) (uint32_t)(x)
+
+static int prom_handle(struct prom_args *pargs)
+{
+ void *rtasbase;
+ uint32_t rtassize = 0;
+ phandle rtas;
+
+ if (strcmp("call-method", (void *)(unsigned long)pargs->service)) {
+ return -1;
+ }
+
+ if (strcmp("instantiate-rtas", (void *)(unsigned long)pargs->args[0])) {
+ return -1;
+ }
+
+ rtas = ci_finddevice("/rtas");
+ /* rtas-size is set by QEMU depending of FWNMI support */
+ ci_getprop(rtas, "rtas-size", &rtassize, sizeof(rtassize));
+ if (rtassize < hv_rtas_size) {
+ return -1;
+ }
+
+ rtasbase = (void *)(unsigned long) pargs->args[2];
+
+ memcpy(rtasbase, hv_rtas, hv_rtas_size);
+ pargs->args[pargs->nargs] = 0;
+ pargs->args[pargs->nargs + 1] = pargs->args[2];
+
+ return 0;
+}
+
+void prom_entry(uint32_t args)
+{
+ if (prom_handle((void *)(unsigned long) args)) {
+ ci_entry(args);
+ }
+}
+
+static int call_ci(const char *service, int nargs, int nret, ...)
+{
+ int i;
+ struct prom_args args;
+ va_list list;
+
+ args.service = ADDR(service);
+ args.nargs = nargs;
+ args.nret = nret;
+
+ va_start(list, nret);
+ for (i = 0; i < nargs; i++) {
+ args.args[i] = va_arg(list, prom_arg_t);
+ }
+ va_end(list);
+
+ for (i = 0; i < nret; i++) {
+ args.args[nargs + i] = 0;
+ }
+
+ if (ci_entry((uint32_t)(&args)) < 0) {
+ return -1;
+ }
+
+ return (nret > 0) ? args.args[nargs] : 0;
+}
+
+void ci_panic(const char *str)
+{
+ call_ci("exit", 0, 0);
+}
+
+phandle ci_finddevice(const char *path)
+{
+ return call_ci("finddevice", 1, 1, path);
+}
+
+uint32_t ci_getprop(phandle ph, const char *propname, void *prop, int len)
+{
+ return call_ci("getprop", 4, 1, ph, propname, prop, len);
+}
diff --git a/pc-bios/vof/entry.S b/pc-bios/vof/entry.S
new file mode 100644
index 0000000000..10a101fb6d
--- /dev/null
+++ b/pc-bios/vof/entry.S
@@ -0,0 +1,49 @@
+#define LOAD32(rn, name) \
+ lis rn,name##@h; \
+ ori rn,rn,name##@l
+
+#define ENTRY(func_name) \
+ .text; \
+ .align 2; \
+ .globl .func_name; \
+ .func_name: \
+ .globl func_name; \
+ func_name:
+
+#define KVMPPC_HCALL_BASE 0xf000
+#define KVMPPC_H_RTAS (KVMPPC_HCALL_BASE + 0x0)
+#define KVMPPC_H_VOF_CLIENT (KVMPPC_HCALL_BASE + 0x5)
+
+ . = 0x100 /* Do exactly as SLOF does */
+
+ENTRY(_start)
+ LOAD32(2, __toc_start)
+ b entry_c
+
+ENTRY(_prom_entry)
+ LOAD32(2, __toc_start)
+ stwu %r1,-112(%r1)
+ stw %r31,104(%r1)
+ mflr %r31
+ bl prom_entry
+ nop
+ mtlr %r31
+ lwz %r31,104(%r1)
+ addi %r1,%r1,112
+ blr
+
+ENTRY(ci_entry)
+ mr 4,3
+ LOAD32(3,KVMPPC_H_VOF_CLIENT)
+ sc 1
+ blr
+
+/* This is the actual RTAS blob copied to the OS at instantiate-rtas */
+ENTRY(hv_rtas)
+ mr %r4,%r3
+ LOAD32(3,KVMPPC_H_RTAS)
+ sc 1
+ blr
+ .globl hv_rtas_size
+hv_rtas_size:
+ .long . - hv_rtas;
diff --git a/pc-bios/vof/libc.c b/pc-bios/vof/libc.c
new file mode 100644
index 0000000000..fdbc30f777
--- /dev/null
+++ b/pc-bios/vof/libc.c
@@ -0,0 +1,66 @@
+#include "vof.h"
+
+int strlen(const char *s)
+{
+ int len = 0;
+
+ while (*s != 0) {
+ len += 1;
+ s += 1;
+ }
+
+ return len;
+}
+
+int strcmp(const char *s1, const char *s2)
+{
+ while (*s1 != 0 && *s2 != 0) {
+ if (*s1 != *s2) {
+ break;
+ }
+ s1 += 1;
+ s2 += 1;
+ }
+
+ return *s1 - *s2;
+}
+
+void *memcpy(void *dest, const void *src, size_t n)
+{
+ char *cdest;
+ const char *csrc = src;
+
+ cdest = dest;
+ while (n-- > 0) {
+ *cdest++ = *csrc++;
+ }
+
+ return dest;
+}
+
+int memcmp(const void *ptr1, const void *ptr2, size_t n)
+{
+ const unsigned char *p1 = ptr1;
+ const unsigned char *p2 = ptr2;
+
+ while (n-- > 0) {
+ if (*p1 != *p2) {
+ return *p1 - *p2;
+ }
+ p1 += 1;
+ p2 += 1;
+ }
+
+ return 0;
+}
+
+void *memset(void *dest, int c, size_t size)
+{
+ unsigned char *d = (unsigned char *)dest;
+
+ while (size-- > 0) {
+ *d++ = (unsigned char)c;
+ }
+
+ return dest;
+}
diff --git a/pc-bios/vof/main.c b/pc-bios/vof/main.c
new file mode 100644
index 0000000000..0f0f6b4cb1
--- /dev/null
+++ b/pc-bios/vof/main.c
@@ -0,0 +1,21 @@
+#include "vof.h"
+
+void do_boot(unsigned long addr, unsigned long _r3, unsigned long _r4)
+{
+ register unsigned long r3 __asm__("r3") = _r3;
+ register unsigned long r4 __asm__("r4") = _r4;
+ register unsigned long r5 __asm__("r5") = (unsigned long) _prom_entry;
+
+ ((void (*)(void))(uint32_t)addr)();
+}
+
+void entry_c(void)
+{
+ register unsigned long r3 __asm__("r3");
+ register unsigned long r4 __asm__("r4");
+ register unsigned long r5 __asm__("r5");
+ uint64_t initrd = r3, initrdsize = r4;
+
+ boot_from_memory(initrd, initrdsize);
+ ci_panic("*** No boot target ***\n");
+}
diff --git a/pc-bios/vof/vof.h b/pc-bios/vof/vof.h
new file mode 100644
index 0000000000..5f12c077f5
--- /dev/null
+++ b/pc-bios/vof/vof.h
@@ -0,0 +1,41 @@
+/*
+ * Virtual Open Firmware
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <stdarg.h>
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned long uint32_t;
+typedef unsigned long long uint64_t;
+#define NULL (0)
+typedef unsigned long ihandle;
+typedef unsigned long phandle;
+typedef int size_t;
+
+/* globals */
+extern void _prom_entry(void); /* OF CI entry point (i.e. this firmware) */
+
+void do_boot(unsigned long addr, unsigned long r3, unsigned long r4);
+
+/* libc */
+int strlen(const char *s);
+int strcmp(const char *s1, const char *s2);
+void *memcpy(void *dest, const void *src, size_t n);
+int memcmp(const void *ptr1, const void *ptr2, size_t n);
+void *memmove(void *dest, const void *src, size_t n);
+void *memset(void *dest, int c, size_t size);
+
+/* CI wrappers */
+void ci_panic(const char *str);
+phandle ci_finddevice(const char *path);
+uint32_t ci_getprop(phandle ph, const char *propname, void *prop, int len);
+
+/* booting from -kernel */
+void boot_from_memory(uint64_t initrd, uint64_t initrdsize);
+
+/* Entry points for CI and RTAS */
+extern uint32_t ci_entry(uint32_t params);
+extern unsigned long hv_rtas(unsigned long params);
+extern unsigned int hv_rtas_size;
diff --git a/pc-bios/vof/vof.lds b/pc-bios/vof/vof.lds
new file mode 100644
index 0000000000..1506ab4b01
--- /dev/null
+++ b/pc-bios/vof/vof.lds
@@ -0,0 +1,48 @@
+OUTPUT_FORMAT("elf32-powerpc")
+OUTPUT_ARCH(powerpc:common)
+
+/* set the entry point */
+ENTRY ( __start )
+
+SECTIONS {
+ __executable_start = .;
+
+ .text : {
+ *(.text)
+ }
+
+ __etext = .;
+
+ . = ALIGN(8);
+
+ .data : {
+ *(.data)
+ *(.rodata .rodata.*)
+ *(.got1)
+ *(.sdata)
+ *(.opd)
+ }
+
+ /* FIXME bss at end ??? */
+
+ . = ALIGN(8);
+ __bss_start = .;
+ .bss : {
+ *(.sbss) *(.scommon)
+ *(.dynbss)
+ *(.bss)
+ }
+
+ . = ALIGN(8);
+ __bss_end = .;
+ __bss_size = (__bss_end - __bss_start);
+
+ . = ALIGN(256);
+ __toc_start = DEFINED (.TOC.) ? .TOC. : ADDR (.got) + 0x8000;
+ .got :
+ {
+ *(.toc .got)
+ }
+ . = ALIGN(8);
+ __toc_end = .;
+}