summaryrefslogtreecommitdiffstats
path: root/external/opal-prd/thunk.S
blob: f355158077a1ca1d6cf3e8bc77c0f02b9437cb43 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#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)

	.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	%r11
	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 r11 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 %r11,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)
	DISABLED_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)
.globl __hinterface_pad
__hinterface_pad:
	/* Reserved space for future growth */
	.space 31*8,0
.globl __hinterface_end
__hinterface_end:
	/* Eye catcher for debugging */
	.llong 0xdeadbeef

OpenPOWER on IntegriCloud