summaryrefslogtreecommitdiffstats
path: root/qemu/roms/SLOF/lib/libhvcall
diff options
context:
space:
mode:
Diffstat (limited to 'qemu/roms/SLOF/lib/libhvcall')
-rw-r--r--qemu/roms/SLOF/lib/libhvcall/Makefile57
-rw-r--r--qemu/roms/SLOF/lib/libhvcall/brokensc1.c162
-rw-r--r--qemu/roms/SLOF/lib/libhvcall/hvcall.S134
-rw-r--r--qemu/roms/SLOF/lib/libhvcall/hvcall.code131
-rw-r--r--qemu/roms/SLOF/lib/libhvcall/hvcall.in34
-rw-r--r--qemu/roms/SLOF/lib/libhvcall/libhvcall.h107
6 files changed, 625 insertions, 0 deletions
diff --git a/qemu/roms/SLOF/lib/libhvcall/Makefile b/qemu/roms/SLOF/lib/libhvcall/Makefile
new file mode 100644
index 000000000..2a9b2d7d1
--- /dev/null
+++ b/qemu/roms/SLOF/lib/libhvcall/Makefile
@@ -0,0 +1,57 @@
+# *****************************************************************************
+# * Copyright (c) 2004, 2008 IBM Corporation
+# * All rights reserved.
+# * This program and the accompanying materials
+# * are made available under the terms of the BSD License
+# * which accompanies this distribution, and is available at
+# * http://www.opensource.org/licenses/bsd-license.php
+# *
+# * Contributors:
+# * IBM Corporation - initial implementation
+# ****************************************************************************/
+
+TOPCMNDIR ?= ../..
+
+include $(TOPCMNDIR)/make.rules
+
+ASFLAGS = $(FLAG) $(RELEASE) $(CPUARCHDEF) -Wa,-mregnames
+CPPFLAGS = -I../libc/include $(CPUARCHDEF) -I$(INCLBRDDIR) \
+ -I$(INCLCMNDIR) -I$(INCLCMNDIR)/$(CPUARCH)
+LDFLAGS = -nostdlib
+
+TARGET = ../libhvcall.a
+
+
+all: $(TARGET)
+
+SRCS = brokensc1.c
+SRCSS = hvcall.S
+
+
+OBJS = $(SRCS:%.c=%.o) $(SRCSS:%.S=%.o)
+
+$(TARGET): $(OBJS)
+ $(AR) -rc $@ $(OBJS)
+ $(RANLIB) $@
+
+%.o: %.S
+ $(CC) $(CPPFLAGS) $(ASFLAGS) -c $< -o $@
+
+clean:
+ $(RM) $(TARGET) $(OBJS)
+
+distclean: clean
+ $(RM) Makefile.dep
+
+
+# Rules for creating the dependency file:
+depend:
+ $(RM) Makefile.dep
+ $(MAKE) Makefile.dep
+
+Makefile.dep: Makefile
+ $(CC) -M $(CPPFLAGS) $(CFLAGS) $(SRCS) $(SRCSS) > Makefile.dep
+
+# Include dependency file if available:
+-include Makefile.dep
+
diff --git a/qemu/roms/SLOF/lib/libhvcall/brokensc1.c b/qemu/roms/SLOF/lib/libhvcall/brokensc1.c
new file mode 100644
index 000000000..e6387e0ab
--- /dev/null
+++ b/qemu/roms/SLOF/lib/libhvcall/brokensc1.c
@@ -0,0 +1,162 @@
+#include <stdint.h>
+#include <stddef.h>
+#include <cpu.h>
+#include "libhvcall.h"
+#include "byteorder.h"
+
+// #define DEBUG_PATCHERY
+
+#define H_SET_DABR 0x28
+#define INS_SC1 0x44000022
+#define INS_SC1_REPLACE 0x7c000268
+
+extern volatile uint32_t sc1ins;
+
+static unsigned long hcall(uint32_t inst, unsigned long arg0, unsigned long arg1)
+{
+ register unsigned long r3 asm("r3") = arg0;
+ register unsigned long r4 asm("r4") = arg1;
+ register unsigned long r5 asm("r5") = inst;
+ asm volatile("bl 1f \n"
+ "1: \n"
+ "li 11, 2f - 1b \n"
+ "mflr 12 \n"
+ "add 11, 11, 12 \n"
+ "stw 5, 0(11) \n"
+ "dcbst 0, 11 \n"
+ "sync \n"
+ "icbi 0, 11 \n"
+ "isync \n"
+ "2: \n"
+ ".long 0 \n"
+ : "=r" (r3)
+ : "r" (r3), "r" (r4), "r" (r5)
+ : "ctr", "r0", "r6", "r7", "r8", "r9", "r10", "r11",
+ "r12", "r13", "r31", "lr", "cc");
+ return r3;
+}
+
+static int check_broken_sc1(void)
+{
+ long r;
+
+ /*
+ * Check if we can do a simple hcall. If it works, we are running in
+ * a sane environment and everything's fine. If it doesn't, we need
+ * to patch the hypercall instruction to something that traps into
+ * supervisor mode.
+ */
+ r = hcall(INS_SC1, H_SET_DABR, 0);
+ if (r == H_SUCCESS || r == H_HARDWARE) {
+ /* All is fine */
+ return 0;
+ }
+
+ /* We found a broken sc1 host! */
+ return 1;
+}
+
+int patch_broken_sc1(void *start, void *end, uint32_t *test_ins)
+{
+ uint32_t *p;
+ /* The sc 1 instruction */
+ uint32_t sc1 = INS_SC1;
+ /* An illegal instruction that KVM interprets as sc 1 */
+ uint32_t sc1_replacement = INS_SC1_REPLACE;
+ int is_le = (test_ins && *test_ins == 0x48000008);
+#ifdef DEBUG_PATCHERY
+ int cnt = 0;
+#endif
+
+ /* The host is sane, get out of here */
+ if (!check_broken_sc1())
+ return 0;
+
+ /* We only get here with a broken sc1 implementation */
+
+ /* Trim the range we scan to not cover the data section */
+ if (test_ins) {
+ /* This is the cpu table matcher for 970FX */
+ uint32_t end_bytes[] = { 0xffff0000, 0x3c0000 };
+ /*
+ * The .__start symbol contains a trap instruction followed
+ * by lots of zeros.
+ */
+ uint32_t start_bytes[] = { 0x7fe00008, 0, 0, 0, 0 };
+
+ if (is_le) {
+ end_bytes[0] = bswap_32(end_bytes[0]);
+ end_bytes[1] = bswap_32(end_bytes[1]);
+ start_bytes[1] = bswap_32(start_bytes[1]);
+ }
+
+ /* Find the start of the text section */
+ for (p = test_ins; (long)p > (long)start; p--) {
+ if (p[0] == start_bytes[0] &&
+ p[1] == start_bytes[1] &&
+ p[2] == start_bytes[2] &&
+ p[3] == start_bytes[3] &&
+ p[4] == start_bytes[4]) {
+ /*
+ * We found a match of the instruction sequence
+ * trap
+ * .long 0
+ * .long 0
+ * .long 0
+ * .long 0
+ * which marks the beginning of the .text
+ * section on all Linux kernels I've checked.
+ */
+#ifdef DEBUG_PATCHERY
+ printf("Shortened start from %p to %p\n", end, p);
+#endif
+ start = p;
+ break;
+ }
+ }
+
+ /* Find the end of the text section */
+ for (p = start; (long)p < (long)end; p++) {
+ if (p[0] == end_bytes[0] && p[1] == end_bytes[1]) {
+ /*
+ * We found a match of the PPC970FX entry in the
+ * guest kernel's CPU table. That table is
+ * usually found early in the .data section and
+ * thus marks the end of the .text section for
+ * us which we need to patch.
+ */
+#ifdef DEBUG_PATCHERY
+ printf("Shortened end from %p to %p\n", end, p);
+#endif
+ end = p;
+ break;
+ }
+ }
+ }
+
+ if (is_le) {
+ /*
+ * The kernel was built for LE mode, so our sc1 and replacement
+ * opcodes are in the wrong byte order. Reverse them.
+ */
+ sc1 = bswap_32(sc1);
+ sc1_replacement = bswap_32(sc1_replacement);
+ }
+
+ /* Patch all sc 1 instructions to reserved instruction 31/308 */
+ for (p = start; (long)p < (long)end; p++) {
+ if (*p == sc1) {
+ *p = sc1_replacement;
+ flush_cache(p, sizeof(*p));
+#ifdef DEBUG_PATCHERY
+ cnt++;
+#endif
+ }
+ }
+
+#ifdef DEBUG_PATCHERY
+ printf("Patched %d instructions (%p - %p)\n", cnt, start, end);
+#endif
+
+ return 1;
+}
diff --git a/qemu/roms/SLOF/lib/libhvcall/hvcall.S b/qemu/roms/SLOF/lib/libhvcall/hvcall.S
new file mode 100644
index 000000000..92cf22e4c
--- /dev/null
+++ b/qemu/roms/SLOF/lib/libhvcall/hvcall.S
@@ -0,0 +1,134 @@
+#define _ASM
+#define __ASSEMBLY__
+#include "macros.h"
+#include "libhvcall.h"
+#include <termctrl.h>
+#include <product.h>
+
+#define HVCALL .long 0x44000022
+ .text
+ .align 3
+
+ENTRY(get_print_banner)
+ LOAD32(r4, print_version)
+ LOAD32(r5, print_version_end)
+ std r4,0(r3)
+ std r5,8(r3)
+ blr
+
+ENTRY(hv_generic)
+ HVCALL
+ blr
+
+/* r3 = char, r4 = hvtermno */
+ENTRY(hv_putchar)
+ sldi r6,r3,(24+32)
+ li r3,H_PUT_TERM_CHAR
+ li r5,1
+ HVCALL
+ blr
+
+/* r3 = hvtermno */
+ENTRY(hv_getchar)
+ mflr r10
+ bl .hv_haschar
+ mtlr r10
+ cmpwi cr0,r3,0
+ beqlr
+ lis r9,inbuf@h
+ ori r9,r9,inbuf@l
+ lwz r4,20(r9)
+ lbzx r3,r4,r9
+ addi r4,r4,1
+ stw r4,20(r9)
+ blr
+
+/* r3 = hvtermno */
+ENTRY(hv_haschar)
+ mr r4,r3
+ li r3,-1
+ lis r9,inbuf@h
+ ori r9,r9,inbuf@l
+ lwz r5,16(r9)
+ lwz r6,20(r9)
+ cmplw cr0,r5,r6
+ bnelr
+ li r3,H_GET_TERM_CHAR
+ HVCALL
+ lis r9,inbuf@h
+ ori r9,r9,inbuf@l
+ stw r4,16(r9)
+ li r3,0
+ stw r3,20(r9)
+ cmplwi cr0,r4,0
+ beqlr
+ li r3,-1
+ std r5,0(r9)
+ std r6,8(r9)
+ blr
+
+ENTRY(hv_send_crq)
+ ld r5,0(r4)
+ ld r6,8(r4)
+ mr r4,r3
+ li r3,H_SEND_CRQ
+ HVCALL
+ blr
+
+ENTRY(hv_send_logical_lan)
+ li r11,0 /* no continue token for now */
+ mr r10,r9
+ mr r9,r8
+ mr r8,r7
+ mr r7,r6
+ mr r6,r5
+ mr r5,r4
+ mr r4,r3
+ li r3,H_SEND_LOGICAL_LAN
+ HVCALL
+ blr
+
+ENTRY(hv_logical_ci_load)
+ mr r5,r4
+ mr r4,r3
+ li r3,H_LOGICAL_CI_LOAD
+ HVCALL
+ cmpdi cr0,r3,0
+ mr r3,r4
+ beqlr
+ li r3,-1
+ blr
+
+ENTRY(hv_logical_ci_store)
+ mr r6,r5
+ mr r5,r4
+ mr r4,r3
+ li r3,H_LOGICAL_CI_STORE
+ HVCALL
+ blr
+
+ENTRY(hv_logical_memop)
+ mr r8,r7
+ mr r7,r6
+ mr r6,r5
+ mr r5,r4
+ mr r4,r3
+ lis r3,KVMPPC_H_LOGICAL_MEMOP@h
+ ori r3,r3,KVMPPC_H_LOGICAL_MEMOP@l
+ HVCALL
+ blr
+
+ENTRY(hv_cas)
+ mr r6,r5
+ mr r5,r4
+ mr r4,r3
+ lis r3,KVMPPC_H_CAS@h
+ ori r3,r3,KVMPPC_H_CAS@l
+ HVCALL
+ blr
+
+ .section ".bss"
+inbuf: .space 16
+inlen: .space 4
+inpos: .space 4
+ .text
diff --git a/qemu/roms/SLOF/lib/libhvcall/hvcall.code b/qemu/roms/SLOF/lib/libhvcall/hvcall.code
new file mode 100644
index 000000000..0ff50f27e
--- /dev/null
+++ b/qemu/roms/SLOF/lib/libhvcall/hvcall.code
@@ -0,0 +1,131 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+#include <libhvcall.h>
+
+// : hv-putchar ( hvtermno char -- )
+PRIM(hv_X2d_putchar)
+ char c = TOS.n; POP;
+ int hvtermno = TOS.n; POP;
+ hv_putchar(c, hvtermno);
+MIRP
+
+// : hv-getchar ( hvtermno -- char )
+PRIM(hv_X2d_getchar)
+ TOS.n = hv_getchar(TOS.n);
+MIRP
+
+// : hv-haschar ( hvtermno -- res )
+PRIM(hv_X2d_haschar)
+ TOS.n = hv_haschar(TOS.n);
+MIRP
+
+// : hv-reg-crq ( unit qaddr qsize -- res )
+PRIM(hv_X2d_reg_X2d_crq)
+ unsigned long qsize = TOS.u; POP;
+ unsigned long qaddr = TOS.u; POP;
+ unsigned int unit = TOS.u;
+ TOS.n = hv_reg_crq(unit, qaddr, qsize);
+MIRP
+
+// : hv-free-crq ( unit -- )
+PRIM(hv_X2d_free_X2d_crq)
+ unsigned int unit = TOS.u; POP;
+ hv_free_crq(unit);
+MIRP
+
+// : hv-send-crq ( unit msgaddr -- rc )
+PRIM(hv_X2d_send_X2d_crq)
+ uint64_t *msgaddr = (uint64_t *)TOS.u; POP;
+ unsigned int unit = TOS.u;
+ TOS.n = hv_send_crq(unit, msgaddr);
+MIRP
+
+// : hv-put-tce ( liobn ioba tce -- rc )
+PRIM(hv_X2d_put_X2d_tce)
+ uint64_t tce = TOS.u; POP;
+ uint64_t ioba = TOS.u; POP;
+ uint32_t liobn = TOS.u;
+ TOS.u = hv_generic(H_PUT_TCE, liobn, ioba, tce);
+MIRP
+
+PRIM(RB_X40)
+ unsigned long qaddr = TOS.u;
+ TOS.u = hv_logical_ci_load(1, qaddr);
+MIRP
+PRIM(RB_X21)
+ unsigned long qaddr = TOS.u; POP;
+ unsigned char val = TOS.u; POP;
+ hv_logical_ci_store(1, qaddr, val);
+MIRP
+PRIM(RW_X40)
+ unsigned long qaddr = TOS.u;
+ TOS.u = hv_logical_ci_load(2, qaddr);
+MIRP
+PRIM(RW_X21)
+ unsigned long qaddr = TOS.u; POP;
+ unsigned short val = TOS.u; POP;
+ hv_logical_ci_store(2, qaddr, val);
+MIRP
+PRIM(RL_X40)
+ unsigned long qaddr = TOS.u;
+ TOS.u = hv_logical_ci_load(4, qaddr);
+MIRP
+PRIM(RL_X21)
+ unsigned long qaddr = TOS.u; POP;
+ unsigned int val = TOS.u; POP;
+ hv_logical_ci_store(4, qaddr, val);
+MIRP
+PRIM(RX_X40)
+ unsigned long qaddr = TOS.u;
+ TOS.u = hv_logical_ci_load(8, qaddr);
+MIRP
+PRIM(RX_X21)
+ unsigned long qaddr = TOS.u; POP;
+ unsigned long val = TOS.u; POP;
+ hv_logical_ci_store(8, qaddr, val);
+MIRP
+
+PRIM(hv_X2d_logical_X2d_memop)
+ unsigned long op = TOS.u; POP;
+ unsigned long count = TOS.u; POP;
+ unsigned long esize = TOS.u; POP;
+ unsigned long src = TOS.u; POP;
+ unsigned long dst = TOS.u;
+ TOS.u = hv_logical_memop(dst, src, esize, count, op);
+MIRP
+
+PRIM(hv_X2d_cas)
+ unsigned long size = TOS.u; POP;
+ unsigned long buf = TOS.u; POP;
+ unsigned long vec = TOS.u;
+ TOS.u = hv_cas(vec, buf, size);
+MIRP
+
+PRIM(hv_X2d_rtas_X2d_update)
+ unsigned long rtas_entry = TOS.u; POP;
+ unsigned long rtas_base = TOS.u;
+ TOS.u = hv_generic(KVMPPC_H_RTAS_UPDATE, rtas_base, rtas_entry);
+MIRP
+
+PRIM(get_X2d_print_X2d_version)
+ unsigned long addr = TOS.u; POP;
+ get_print_banner(addr);
+MIRP
+
+PRIM(check_X2d_and_X2d_patch_X2d_sc1)
+ unsigned long end = TOS.u; POP;
+ unsigned long start = TOS.u; POP;
+ unsigned long patch_ins = TOS.u; POP;
+
+ patch_broken_sc1((void*)start, (void*)end, (void*)patch_ins);
+MIRP
diff --git a/qemu/roms/SLOF/lib/libhvcall/hvcall.in b/qemu/roms/SLOF/lib/libhvcall/hvcall.in
new file mode 100644
index 000000000..4437b77f0
--- /dev/null
+++ b/qemu/roms/SLOF/lib/libhvcall/hvcall.in
@@ -0,0 +1,34 @@
+/******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ * IBM Corporation - initial implementation
+ *****************************************************************************/
+
+cod(hv-putchar)
+cod(hv-getchar)
+cod(hv-haschar)
+cod(hv-reg-crq)
+cod(hv-free-crq)
+cod(hv-send-crq)
+cod(hv-put-tce)
+cod(check-and-patch-sc1)
+
+cod(RB@)
+cod(RB!)
+cod(RW@)
+cod(RW!)
+cod(RL@)
+cod(RL!)
+cod(RX@)
+cod(RX!)
+
+cod(hv-logical-memop)
+cod(hv-cas)
+cod(hv-rtas-update)
+cod(get-print-version)
diff --git a/qemu/roms/SLOF/lib/libhvcall/libhvcall.h b/qemu/roms/SLOF/lib/libhvcall/libhvcall.h
new file mode 100644
index 000000000..193b7383d
--- /dev/null
+++ b/qemu/roms/SLOF/lib/libhvcall/libhvcall.h
@@ -0,0 +1,107 @@
+#ifndef __LIBHVCALL_H__
+#define __LIBHVCALL_H__
+
+#define H_SUCCESS 0
+#define H_HARDWARE -1
+
+#define H_GET_TCE 0x1C
+#define H_PUT_TCE 0x20
+#define H_LOGICAL_CI_LOAD 0x3c
+#define H_LOGICAL_CI_STORE 0x40
+#define H_GET_TERM_CHAR 0x54
+#define H_PUT_TERM_CHAR 0x58
+#define H_REG_CRQ 0xFC
+#define H_FREE_CRQ 0x100
+#define H_SEND_CRQ 0x108
+#define H_REGISTER_LOGICAL_LAN 0x114
+#define H_FREE_LOGICAL_LAN 0x118
+#define H_ADD_LOGICAL_LAN_BUFFER 0x11C
+#define H_SEND_LOGICAL_LAN 0x120
+
+/* KVM specific ones */
+#define KVMPPC_HCALL_BASE 0xf000
+#define KVMPPC_H_RTAS (KVMPPC_HCALL_BASE + 0x0)
+#define KVMPPC_H_LOGICAL_MEMOP (KVMPPC_HCALL_BASE + 0x1)
+/* Client Architecture support */
+#define KVMPPC_H_CAS (KVMPPC_HCALL_BASE + 0x2)
+#define KVMPPC_H_RTAS_UPDATE (KVMPPC_HCALL_BASE + 0x3)
+#define KVMPPC_H_REPORT_MC_ERR (KVMPPC_HCALL_BASE + 0x4)
+#define KVMPPC_HCALL_MAX KVMPPC_H_NMI_MCE
+
+#ifndef __ASSEMBLY__
+
+extern long hv_generic(unsigned long opcode, ...);
+
+extern void hv_putchar(char c, int hvtermno);
+extern char hv_getchar(int hvtermno);
+extern char hv_haschar(int hvtermno);
+extern void get_print_banner(unsigned long addr);
+
+extern int hv_send_crq(unsigned int unit, uint64_t *msgaddr);
+
+static inline long hv_reg_crq(unsigned int unit, unsigned long qaddr,
+ unsigned long qsize)
+{
+ return hv_generic(H_REG_CRQ, unit, qaddr, qsize);
+}
+
+static inline void hv_free_crq(unsigned int unit)
+{
+ hv_generic(H_FREE_CRQ, unit);
+}
+
+extern long hv_send_logical_lan(unsigned long unit_address,
+ unsigned long desc1, unsigned long desc2,
+ unsigned long desc3, unsigned long desc4,
+ unsigned long desc5, unsigned long desc6);
+
+static inline long h_register_logical_lan(unsigned long unit_address,
+ unsigned long buf_list,
+ unsigned long rec_q,
+ unsigned long filter_list,
+ unsigned long mac_address)
+{
+ return hv_generic(H_REGISTER_LOGICAL_LAN, unit_address,
+ buf_list, rec_q, filter_list, mac_address);
+}
+
+static inline long h_free_logical_lan(unsigned long unit_address)
+{
+ return hv_generic(H_FREE_LOGICAL_LAN, unit_address);
+}
+
+static inline long h_add_logical_lan_buffer(unsigned long unit_address,
+ unsigned long buffer)
+{
+ return hv_generic(H_ADD_LOGICAL_LAN_BUFFER, unit_address, buffer);
+}
+
+#define HV_RTAS_MAX_ARGRET 5
+
+struct hv_rtas_call {
+ uint32_t token;
+ uint32_t nargs;
+ uint32_t nrets;
+ uint32_t argret[HV_RTAS_MAX_ARGRET];
+};
+
+static inline unsigned long h_rtas(struct hv_rtas_call *rtas_buf)
+{
+ return hv_generic(KVMPPC_H_RTAS, (unsigned long)rtas_buf);
+}
+
+extern unsigned long hv_logical_ci_load(unsigned long size, unsigned long addr);
+extern unsigned long hv_logical_ci_store(unsigned long size, unsigned long addr,
+ unsigned long value);
+
+extern unsigned long hv_logical_memop(unsigned long dst, unsigned long src,
+ unsigned long esize, unsigned long count,
+ unsigned long op);
+extern int patch_broken_sc1(void *start, void *end, uint32_t *test_ins);
+
+extern unsigned long hv_cas(unsigned long vec, unsigned long buf,
+ unsigned long size);
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __LIBHVCALL_H__ */