diff options
Diffstat (limited to 'pc-bios')
-rw-r--r-- | pc-bios/README | 4 | ||||
-rw-r--r-- | pc-bios/vof-nvram.bin | bin | 0 -> 16384 bytes | |||
-rwxr-xr-x | pc-bios/vof.bin | bin | 0 -> 3784 bytes | |||
-rw-r--r-- | pc-bios/vof/Makefile | 23 | ||||
-rw-r--r-- | pc-bios/vof/bootmem.c | 14 | ||||
-rw-r--r-- | pc-bios/vof/ci.c | 91 | ||||
-rw-r--r-- | pc-bios/vof/entry.S | 49 | ||||
-rw-r--r-- | pc-bios/vof/libc.c | 92 | ||||
-rw-r--r-- | pc-bios/vof/main.c | 21 | ||||
-rw-r--r-- | pc-bios/vof/vof.h | 43 | ||||
-rw-r--r-- | pc-bios/vof/vof.lds | 48 |
11 files changed, 385 insertions, 0 deletions
diff --git a/pc-bios/README b/pc-bios/README index c101c9a04f..d344e3bc1b 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -16,6 +16,10 @@ https://github.com/aik/SLOF, and the image currently in qemu is built from git tag qemu-slof-20210217. +- VOF (Virtual Open Firmware) is a minimalistic firmware to work with + -machine pseries,x-vof=on. When enabled, the firmware acts as a slim shim and + QEMU implements parts of the IEEE 1275 Open Firmware interface. + - sgabios (the Serial Graphics Adapter option ROM) provides a means for legacy x86 software to communicate with an attached serial console as if a video card were attached. The master sources reside in a subversion diff --git a/pc-bios/vof-nvram.bin b/pc-bios/vof-nvram.bin Binary files differnew file mode 100644 index 0000000000..d183901cf9 --- /dev/null +++ b/pc-bios/vof-nvram.bin diff --git a/pc-bios/vof.bin b/pc-bios/vof.bin Binary files differnew file mode 100755 index 0000000000..1ec670be82 --- /dev/null +++ b/pc-bios/vof.bin 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..2b56050238 --- /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 PROM_ERROR; + } + + 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..00c10e6e7d --- /dev/null +++ b/pc-bios/vof/libc.c @@ -0,0 +1,92 @@ +#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 *memmove(void *dest, const void *src, size_t n) +{ + char *cdest; + const char *csrc; + int i; + + /* Do the buffers overlap in a bad way? */ + if (src < dest && src + n >= dest) { + /* Copy from end to start */ + cdest = dest + n - 1; + csrc = src + n - 1; + for (i = 0; i < n; i++) { + *cdest-- = *csrc--; + } + } else { + /* Normal copy is possible */ + cdest = dest; + csrc = src; + for (i = 0; i < n; i++) { + *cdest++ = *csrc++; + } + } + + return dest; +} + +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..9fc30d2d09 --- /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; + + ((client *)(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..2d89580769 --- /dev/null +++ b/pc-bios/vof/vof.h @@ -0,0 +1,43 @@ +/* + * 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) +#define PROM_ERROR (-1u) +typedef unsigned long ihandle; +typedef unsigned long phandle; +typedef int size_t; +typedef void client(void); + +/* 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 = .; +} |