xref: /openbsd/sys/arch/loongson/loongson/pmon32.S (revision 8dd7b53a)
1/*	$OpenBSD: pmon32.S,v 1.5 2018/04/20 14:36:42 visa Exp $	*/
2
3/*
4 * Copyright (c) 2009 Miodrag Vallat.
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19/*
20 * Wrapper routines to invoke PMON2000 functions from 64-bit code.
21 *
22 * PMON is compiled either as 32 or 64 bit code.  32 bit code uses the o32 ABI,
23 * while 64 bit code uses the gcc o64 ABI (similar to the o32 ABI, but using
24 * 64 bit registers).
25 *
26 * As a result, only up to four arguments to functions will be passed through
27 * registers.  It's up to the caller to never invoke pmon_printf() with more
28 * than four arguments or with a 64 bit argument; other functions are not
29 * affected.
30 */
31
32#include <machine/param.h>
33#include <machine/asm.h>
34#include <mips64/mips_cpu.h>
35
36#ifndef _STANDALONE
37#include "assym.h"
38#endif
39
40	.set	mips3
41
42	.data
43	.globl	pmon_callvec
44pmon_callvec:
45	.word	0
46
47pmon_o32:
48	.word	0
49
50	.text
51
52/*
53 * Note that we need to provide a CF_SZ untouched area above sp, or we'll risk
54 * our stack being corrupted upon return.
55 */
56#define	PMON_WRAP(name, index) \
57	NNON_LEAF(name, FRAMESZ(CF_SZ + 9 * REGSZ),  ra); \
58	PTR_SUBU sp, sp, FRAMESZ(CF_SZ + 9 * REGSZ); \
59	REG_S	ra, CF_RA_OFFS(sp); \
60	.mask	0xc0ff0000, (CF_RA_OFFS - FRAMESZ(CF_SZ + 9 * REGSZ)); \
61	REG_S	s0, (0 * REGSZ + CF_SZ)(sp); \
62	REG_S	s1, (1 * REGSZ + CF_SZ)(sp); \
63	REG_S	s2, (2 * REGSZ + CF_SZ)(sp); \
64	REG_S	s3, (3 * REGSZ + CF_SZ)(sp); \
65	REG_S	s4, (4 * REGSZ + CF_SZ)(sp); \
66	REG_S	s5, (5 * REGSZ + CF_SZ)(sp); \
67	REG_S	s6, (6 * REGSZ + CF_SZ)(sp); \
68	REG_S	s7, (7 * REGSZ + CF_SZ)(sp); \
69	REG_S	s8, (8 * REGSZ + CF_SZ)(sp); \
70	lw	t0, pmon_callvec; \
71	lw	t0, (index) * 4 (t0); \
72	jalr	t0; \
73	nop; \
74	REG_L	s8, (8 * REGSZ + CF_SZ)(sp); \
75	REG_L	s7, (7 * REGSZ + CF_SZ)(sp); \
76	REG_L	s6, (6 * REGSZ + CF_SZ)(sp); \
77	REG_L	s5, (5 * REGSZ + CF_SZ)(sp); \
78	REG_L	s4, (4 * REGSZ + CF_SZ)(sp); \
79	REG_L	s3, (3 * REGSZ + CF_SZ)(sp); \
80	REG_L	s2, (2 * REGSZ + CF_SZ)(sp); \
81	REG_L	s1, (1 * REGSZ + CF_SZ)(sp); \
82	REG_L	s0, (0 * REGSZ + CF_SZ)(sp); \
83	REG_L	ra, CF_RA_OFFS(sp); \
84	PTR_ADDU sp, sp, FRAMESZ(CF_SZ + 9 * REGSZ); \
85	jr	ra; \
86	nop; \
87	END(name)
88
89PMON_WRAP(pmon_printf, 5)
90PMON_WRAP(pmon_gets, 7)
91#ifdef _STANDALONE
92PMON_WRAP(pmon_open, 0)
93PMON_WRAP(pmon_close, 1)
94PMON_WRAP(pmon_read, 2)
95PMON_WRAP(pmon_cacheflush, 6)
96#endif
97#if 0	/* unused */
98PMON_WRAP(pmon_write, 3)
99#endif
100
101/*
102 * pmon_lseek() is defined separately to work around the 64-bit code's
103 * incompatibility with the o32 ABI.
104 */
105NNON_LEAF(pmon_lseek, FRAMESZ(CF_SZ + 9 * REGSZ), ra)
106	PTR_SUBU sp, sp, FRAMESZ(CF_SZ + 9 * REGSZ)
107	REG_S	ra, CF_RA_OFFS(sp)
108	.mask	0xc0ff0000, (CF_RA_OFFS - FRAMESZ(CF_SZ + 9 * REGSZ))
109	REG_S	s0, (0 * REGSZ + CF_SZ)(sp)
110	REG_S	s1, (1 * REGSZ + CF_SZ)(sp)
111	REG_S	s2, (2 * REGSZ + CF_SZ)(sp)
112	REG_S	s3, (3 * REGSZ + CF_SZ)(sp)
113	REG_S	s4, (4 * REGSZ + CF_SZ)(sp)
114	REG_S	s5, (5 * REGSZ + CF_SZ)(sp)
115	REG_S	s6, (6 * REGSZ + CF_SZ)(sp)
116	REG_S	s7, (7 * REGSZ + CF_SZ)(sp)
117	REG_S	s8, (8 * REGSZ + CF_SZ)(sp)
118	lw	t0, pmon_callvec
119	lw	t0, 4 * 4 (t0)
120	lw	t1, pmon_o32
121	bne	t1, zero, 1f
122	nop
123	/* gcc o64 ABI call. */
124	jalr	t0
125	nop
126	b	2f
127	nop
1281:
129	/* o32 ABI call.  The parameters have to be rearranged. */
130	sw	a2, 4 * 4 (sp)
131	sll	a2, a1, 0		# get the low 32 bits
132	dsrl	a3, a1, 32		# get the high 32 bits
133	jalr	t0
134	nop
135	/* Rearrange the return value. */
136	dsll	v0, v0, 32		# clear any sign extension
137	dsrl	v0, v0, 32
138	dsll	v1, v1, 32
139	or	v0, v0, v1
1402:
141	REG_L	s8, (8 * REGSZ + CF_SZ)(sp)
142	REG_L	s7, (7 * REGSZ + CF_SZ)(sp)
143	REG_L	s6, (6 * REGSZ + CF_SZ)(sp)
144	REG_L	s5, (5 * REGSZ + CF_SZ)(sp)
145	REG_L	s4, (4 * REGSZ + CF_SZ)(sp)
146	REG_L	s3, (3 * REGSZ + CF_SZ)(sp)
147	REG_L	s2, (2 * REGSZ + CF_SZ)(sp)
148	REG_L	s1, (1 * REGSZ + CF_SZ)(sp)
149	REG_L	s0, (0 * REGSZ + CF_SZ)(sp)
150	REG_L	ra, CF_RA_OFFS(sp)
151	PTR_ADDU sp, sp, FRAMESZ(CF_SZ + 9 * REGSZ)
152	jr	ra
153	nop
154END(pmon_lseek)
155
156/*
157 * Probe if PMON is compiled to use the o32 ABI.
158 */
159NNON_LEAF(pmon_probe_abi, FRAMESZ(CF_SZ + 9 * REGSZ), ra)
160	PTR_SUBU sp, sp, FRAMESZ(CF_SZ + 9 * REGSZ)
161	REG_S	ra, CF_RA_OFFS(sp)
162	.mask	0xc0ff0000, (CF_RA_OFFS - FRAMESZ(CF_SZ + 9 * REGSZ))
163	REG_S	s0, (0 * REGSZ + CF_SZ)(sp)
164	REG_S	s1, (1 * REGSZ + CF_SZ)(sp)
165	REG_S	s2, (2 * REGSZ + CF_SZ)(sp)
166	REG_S	s3, (3 * REGSZ + CF_SZ)(sp)
167	REG_S	s4, (4 * REGSZ + CF_SZ)(sp)
168	REG_S	s5, (5 * REGSZ + CF_SZ)(sp)
169	REG_S	s6, (6 * REGSZ + CF_SZ)(sp)
170	REG_S	s7, (7 * REGSZ + CF_SZ)(sp)
171	REG_S	s8, (8 * REGSZ + CF_SZ)(sp)
172	/* Skip probing on pre-LS3A machines.  They are not affected. */
173	mfc0	t0, COP_0_PRID
174	and	t1, t0, 0xff00
175	li	t2, 0x6300
176	bne	t1, t2, 1f
177	and	t1, t0, 0x00ff
178	li	t2, 0x0005
179	blt	t1, t2, 1f
180	nop
181	/*
182	 * Call lseek() with an invalid fd.  The call should fail with return
183	 * value -1.  The value is in register pair v1:v0 if the o32 ABI is
184	 * in use, while the gcc o64 ABI uses register v0 only.
185	 *
186	 * Zero v0 and v1, and if both contain value -1 when the call returns,
187	 * assume that PMON uses the o32 ABI.  This relies on the fact that
188	 * the firmware detects the invalid fd and returns before value -1
189	 * could accidentally be assigned to both registers.
190	 */
191	li	a0, 0x10000
192	move	a1, zero
193	move	a2, zero
194	move	a3, zero
195	sw	zero, 4 * 4 (sp)
196	move	v0, zero
197	move	v1, zero
198	lw	t0, pmon_callvec
199	lw	t0, 4 * 4 (t0)
200	jalr	t0
201	nop
202	/* Check the return value. */
203	li	t0, -1
204	bne	v0, t0, 1f
205	nop
206	bne	v1, t0, 1f
207	li	t1, 1
208	sw	t1, pmon_o32
2091:
210	REG_L	s8, (8 * REGSZ + CF_SZ)(sp)
211	REG_L	s7, (7 * REGSZ + CF_SZ)(sp)
212	REG_L	s6, (6 * REGSZ + CF_SZ)(sp)
213	REG_L	s5, (5 * REGSZ + CF_SZ)(sp)
214	REG_L	s4, (4 * REGSZ + CF_SZ)(sp)
215	REG_L	s3, (3 * REGSZ + CF_SZ)(sp)
216	REG_L	s2, (2 * REGSZ + CF_SZ)(sp)
217	REG_L	s1, (1 * REGSZ + CF_SZ)(sp)
218	REG_L	s0, (0 * REGSZ + CF_SZ)(sp)
219	REG_L	ra, CF_RA_OFFS(sp)
220	PTR_ADDU sp, sp, FRAMESZ(CF_SZ + 9 * REGSZ)
221	jr	ra
222	nop
223END(pmon_probe_abi)
224