#include <endian.h>
#include <asm/unistd.h>

#ifndef __NR_switch_endian
#define __NR_switch_endian 363
#endif

/* a constant to use in the SI field of a little-endian D-form instruction */
#define le_si16(x) (((x & 0xff) << 24) | ((x & 0xff00) << 8))

	.text

	/*
	 * Call into a HBRT BE function
	 * Func desc (opd) will be in BE
	 * Use ldbrx to load from opd
	 */

call_be:

	/* Before we switch, we need to perform some ABI
	 * conversion. We are currently running LE with the
	 * new ABI v2. The GPR content is the same, we do
	 * need save/restore and adjust r2. At this point r11
	 * contain the OPD
	 */
	nop
	nop

	/* We first create a stack frame compatible with BE, we
	 * do a big one just in case... we save LR into our caller's
	 * frame and r2 in our own frame. This is a BE formatted
	 * frame so we store it as 40(r1), not 24(r1)
	 */
	stdu %r1,-128(%r1)
	mflr %r0
	std %r0,(128 + 16)(%r1)
	std %r2,40(%r1)

	/* Grab the target r2 and function pointer */
#if __BYTE_ORDER == __LITTLE_ENDIAN
	ldbrx %r0, 0, %r11
	li %r2, 8
	ldbrx %r2, %r2, %r11
#else
	ld %r0,0(%r11)
	ld %r2,8(%r11)
#endif

	mtlr %r0

#if __BYTE_ORDER == __LITTLE_ENDIAN
	/* Switch to the "other endian" */
	li %r0,__NR_switch_endian
	sc

	/* Branch to LR */
	.long 0x2100804e /* (byteswapped blrl) */

	/* Switch endian back */
	.long 0x00000038 | le_si16(__NR_switch_endian)
			/* byteswapped li %r0,__NR_switch_endian */
	.long 0x02000044 /* byteswapped sc */
#else
	bctrl
#endif
	/* Recover our r2, LR, undo stack frame ... */
	ld %r2,40(%r1)
	ld  %r0,(128+16)(%r1)
	addi %r1,%r1,128
	mtlr %r0
	blr

#define CALL_THUNK(name, idx) 		 	 \
	.globl call_##name			;\
call_##name:					;\
	ld %r11,hservice_runtime_fixed@got(%r2)	;\
	ld %r11,(idx * 8)(%r11)			;\
	b call_be

	/* Instanciate call to HBRT thunks */
	CALL_THUNK(cxxtestExecute, 1)
	CALL_THUNK(get_lid_list, 2)
	CALL_THUNK(occ_load, 3)
	CALL_THUNK(occ_start, 4)
	CALL_THUNK(occ_stop, 5)
	CALL_THUNK(process_occ_error, 6)
	CALL_THUNK(enable_attns, 7)
	CALL_THUNK(disable_attns, 8)
	CALL_THUNK(handle_attns, 9)
	CALL_THUNK(process_occ_reset, 10)
	CALL_THUNK(enable_occ_actuation, 11)
	CALL_THUNK(apply_attr_override, 12)
	CALL_THUNK(mfg_htmgt_pass_thru, 13)
	CALL_THUNK(run_command, 14)
	CALL_THUNK(verify_container, 15)
	CALL_THUNK(sbe_message_passing, 16)
	CALL_THUNK(load_pm_complex, 17)
	CALL_THUNK(start_pm_complex, 18)
	CALL_THUNK(reset_pm_complex, 19)
	CALL_THUNK(get_ipoll_events, 20)
	CALL_THUNK(firmware_notify, 21)
	CALL_THUNK(prepare_hbrt_update, 22)

	.globl call_hbrt_init
call_hbrt_init:
	ld %r11,hbrt_entry@got(%r2)
	b call_be

#if __BYTE_ORDER == __LITTLE_ENDIAN
	/* Callback from HBRT, stack conversion and call into C code,
	 * we arrive here from the thunk macro with r11 containing the
	 * target function and r2 already set from the OPD.
	 */
call_le:
	/* Create a LE stack frame, save LR */
	stdu %r1,-32(%r1)
	mflr %r0
	std %r0,(32+16)(%r1)

	/* Branch to original function */
	mtlr	%r12
	blrl

	/* Restore stack and LR */
	ld  %r0,(32+16)(%r1)
	addi %r1,%r1,32
	mtlr %r0

	/* Switch endian back to BE */
	li %r0,__NR_switch_endian
	sc

	/* Return to BE */
	.long 0x2000804e /* byteswapped blr */

	/* Callback from HBRT. There is one entry point per function.
	 *
	 * We assume the proper r2 is already set via the OPD, so we grab our
	 * target function pointer in r12 and jump to call_le
	 */
#define CALLBACK_THUNK(name)							 \
	.pushsection ".text","ax" 						;\
	.globl	name##_thunk							;\
name##_thunk:									;\
	.long 0x00000038 | le_si16(__NR_switch_endian)				;\
			/* byteswapped li %r0,__NR_switch_endian */		;\
	.long 0x02000044 /* byteswapped sc */					;\
	ld %r12,name@got(%r2)							;\
	b call_le								;\
	.popsection								;\
	.pushsection ".data.thunk_opd","aw" 					;\
1:	.llong name##_thunk, .TOC., 0 						;\
	.popsection								;\
	.llong 1b
#else /* __BYTE_ORDER == __LITTLE_ENDIAN */
#define CALLBACK_THUNK(name)							 \
	.llong name
#endif

#define DISABLED_THUNK(name) .llong 0x0

	/* Here's the callback table generation. It creates the table and
	 * all the thunks for all the callbacks from HBRT to us
	 */
	.data
	.globl hinterface
	.globl __hinterface_start
__hinterface_start:
hinterface:
	/* HBRT interface version */
	.llong 1

	/* Callout pointers */
	CALLBACK_THUNK(hservice_puts)
	CALLBACK_THUNK(hservice_assert)
	CALLBACK_THUNK(hservice_set_page_execute)
	CALLBACK_THUNK(hservice_malloc)
	CALLBACK_THUNK(hservice_free)
	CALLBACK_THUNK(hservice_realloc)
	DISABLED_THUNK(hservice_send_error_log)
	CALLBACK_THUNK(hservice_scom_read)
	CALLBACK_THUNK(hservice_scom_write)
	DISABLED_THUNK(hservice_lid_load)
	DISABLED_THUNK(hservice_lid_unload)
	CALLBACK_THUNK(hservice_get_reserved_mem)
	CALLBACK_THUNK(hservice_wakeup)
	CALLBACK_THUNK(hservice_nanosleep)
	DISABLED_THUNK(hservice_report_occ_failure)
	CALLBACK_THUNK(hservice_clock_gettime)
	CALLBACK_THUNK(hservice_pnor_read)
	CALLBACK_THUNK(hservice_pnor_write)
	CALLBACK_THUNK(hservice_i2c_read)
	CALLBACK_THUNK(hservice_i2c_write)
	CALLBACK_THUNK(hservice_ipmi_msg)
	CALLBACK_THUNK(hservice_memory_error)
	CALLBACK_THUNK(hservice_get_interface_capabilities)
	DISABLED_THUNK(hservice_map_phys_mem)
	DISABLED_THUNK(hservice_unmap_phys_mem)
	DISABLED_THUNK(hservice_hcode_scom_update)
	CALLBACK_THUNK(hservice_firmware_request)
.globl __hinterface_pad
__hinterface_pad:
	/* Reserved space for future growth */
	.space 27*8,0
.globl __hinterface_end
__hinterface_end:
	/* Eye catcher for debugging */
	.llong 0xdeadbeef

