diff options
Diffstat (limited to 'roms/SLOF/lib/libhvcall')
| -rw-r--r-- | roms/SLOF/lib/libhvcall/Makefile | 57 | ||||
| -rw-r--r-- | roms/SLOF/lib/libhvcall/brokensc1.c | 162 | ||||
| -rw-r--r-- | roms/SLOF/lib/libhvcall/hvcall.S | 134 | ||||
| -rw-r--r-- | roms/SLOF/lib/libhvcall/hvcall.code | 131 | ||||
| -rw-r--r-- | roms/SLOF/lib/libhvcall/hvcall.in | 34 | ||||
| -rw-r--r-- | roms/SLOF/lib/libhvcall/libhvcall.h | 107 | 
6 files changed, 625 insertions, 0 deletions
diff --git a/roms/SLOF/lib/libhvcall/Makefile b/roms/SLOF/lib/libhvcall/Makefile new file mode 100644 index 00000000..2a9b2d7d --- /dev/null +++ b/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/roms/SLOF/lib/libhvcall/brokensc1.c b/roms/SLOF/lib/libhvcall/brokensc1.c new file mode 100644 index 00000000..e6387e0a --- /dev/null +++ b/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/roms/SLOF/lib/libhvcall/hvcall.S b/roms/SLOF/lib/libhvcall/hvcall.S new file mode 100644 index 00000000..92cf22e4 --- /dev/null +++ b/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/roms/SLOF/lib/libhvcall/hvcall.code b/roms/SLOF/lib/libhvcall/hvcall.code new file mode 100644 index 00000000..0ff50f27 --- /dev/null +++ b/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/roms/SLOF/lib/libhvcall/hvcall.in b/roms/SLOF/lib/libhvcall/hvcall.in new file mode 100644 index 00000000..4437b77f --- /dev/null +++ b/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/roms/SLOF/lib/libhvcall/libhvcall.h b/roms/SLOF/lib/libhvcall/libhvcall.h new file mode 100644 index 00000000..193b7383 --- /dev/null +++ b/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__ */  | 
