1// Copyright 2011 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5#include "go_asm.h"
6#include "go_tls.h"
7#include "textflag.h"
8#include "time_windows.h"
9#include "cgo/abi_amd64.h"
10
11// Offsets into Thread Environment Block (pointer in GS)
12#define TEB_TlsSlots 0x1480
13#define TEB_ArbitraryPtr 0x28
14
15TEXT runtime·asmstdcall_trampoline<ABIInternal>(SB),NOSPLIT,$0
16	MOVQ	AX, CX
17	JMP	runtime·asmstdcall(SB)
18
19// void runtime·asmstdcall(void *c);
20TEXT runtime·asmstdcall(SB),NOSPLIT,$16
21	MOVQ	SP, AX
22	ANDQ	$~15, SP	// alignment as per Windows requirement
23	MOVQ	AX, 8(SP)
24	MOVQ	CX, 0(SP)	// asmcgocall will put first argument into CX.
25
26	MOVQ	libcall_fn(CX), AX
27	MOVQ	libcall_args(CX), SI
28	MOVQ	libcall_n(CX), CX
29
30	// SetLastError(0).
31	MOVQ	0x30(GS), DI
32	MOVL	$0, 0x68(DI)
33
34	SUBQ	$(const_maxArgs*8), SP	// room for args
35
36	// Fast version, do not store args on the stack.
37	CMPL	CX, $0;	JE	_0args
38	CMPL	CX, $1;	JE	_1args
39	CMPL	CX, $2;	JE	_2args
40	CMPL	CX, $3;	JE	_3args
41	CMPL	CX, $4;	JE	_4args
42
43	// Check we have enough room for args.
44	CMPL	CX, $const_maxArgs
45	JLE	2(PC)
46	INT	$3			// not enough room -> crash
47
48	// Copy args to the stack.
49	MOVQ	SP, DI
50	CLD
51	REP; MOVSQ
52	MOVQ	SP, SI
53
54	// Load first 4 args into correspondent registers.
55	// Floating point arguments are passed in the XMM
56	// registers. Set them here in case any of the arguments
57	// are floating point values. For details see
58	//	https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-170
59_4args:
60	MOVQ	24(SI), R9
61	MOVQ	R9, X3
62_3args:
63	MOVQ	16(SI), R8
64	MOVQ	R8, X2
65_2args:
66	MOVQ	8(SI), DX
67	MOVQ	DX, X1
68_1args:
69	MOVQ	0(SI), CX
70	MOVQ	CX, X0
71_0args:
72
73	// Call stdcall function.
74	CALL	AX
75
76	ADDQ	$(const_maxArgs*8), SP
77
78	// Return result.
79	MOVQ	0(SP), CX
80	MOVQ	8(SP), SP
81	MOVQ	AX, libcall_r1(CX)
82	// Floating point return values are returned in XMM0. Setting r2 to this
83	// value in case this call returned a floating point value. For details,
84	// see https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention
85	MOVQ    X0, libcall_r2(CX)
86
87	// GetLastError().
88	MOVQ	0x30(GS), DI
89	MOVL	0x68(DI), AX
90	MOVQ	AX, libcall_err(CX)
91
92	RET
93
94// faster get/set last error
95TEXT runtime·getlasterror(SB),NOSPLIT,$0
96	MOVQ	0x30(GS), AX
97	MOVL	0x68(AX), AX
98	MOVL	AX, ret+0(FP)
99	RET
100
101// Called by Windows as a Vectored Exception Handler (VEH).
102// CX is pointer to struct containing
103// exception record and context pointers.
104// DX is the kind of sigtramp function.
105// Return value of sigtrampgo is stored in AX.
106TEXT sigtramp<>(SB),NOSPLIT,$0-0
107	// Switch from the host ABI to the Go ABI.
108	PUSH_REGS_HOST_TO_ABI0()
109
110	// Set up ABIInternal environment: cleared X15 and R14.
111	// R14 is cleared in case there's a non-zero value in there
112	// if called from a non-go thread.
113	XORPS	X15, X15
114	XORQ	R14, R14
115
116	get_tls(AX)
117	CMPQ	AX, $0
118	JE	2(PC)
119	// Exception from Go thread, set R14.
120	MOVQ	g(AX), R14
121
122	// Reserve space for spill slots.
123	ADJSP	$16
124	MOVQ	CX, AX
125	MOVQ	DX, BX
126	// Calling ABIInternal because TLS might be nil.
127	CALL	runtime·sigtrampgo<ABIInternal>(SB)
128	// Return value is already stored in AX.
129
130	ADJSP	$-16
131
132	POP_REGS_HOST_TO_ABI0()
133	RET
134
135// Trampoline to resume execution from exception handler.
136// This is part of the control flow guard workaround.
137// It switches stacks and jumps to the continuation address.
138// R8 and R9 are set above at the end of sigtrampgo
139// in the context that starts executing at sigresume.
140TEXT runtime·sigresume(SB),NOSPLIT|NOFRAME,$0
141	MOVQ	R8, SP
142	JMP	R9
143
144TEXT runtime·exceptiontramp(SB),NOSPLIT|NOFRAME,$0
145	// PExceptionPointers already on CX
146	MOVQ	$const_callbackVEH, DX
147	JMP	sigtramp<>(SB)
148
149TEXT runtime·firstcontinuetramp(SB),NOSPLIT|NOFRAME,$0-0
150	// PExceptionPointers already on CX
151	MOVQ	$const_callbackFirstVCH, DX
152	JMP	sigtramp<>(SB)
153
154TEXT runtime·lastcontinuetramp(SB),NOSPLIT|NOFRAME,$0-0
155	// PExceptionPointers already on CX
156	MOVQ	$const_callbackLastVCH, DX
157	JMP	sigtramp<>(SB)
158
159TEXT runtime·sehtramp(SB),NOSPLIT,$40-0
160	// CX: PEXCEPTION_RECORD ExceptionRecord
161	// DX: ULONG64 EstablisherFrame
162	// R8: PCONTEXT ContextRecord
163	// R9: PDISPATCHER_CONTEXT DispatcherContext
164	// Switch from the host ABI to the Go ABI.
165	PUSH_REGS_HOST_TO_ABI0()
166
167	get_tls(AX)
168	CMPQ	AX, $0
169	JNE	2(PC)
170	// This shouldn't happen, sehtramp is only attached to functions
171	// called from Go, and exception handlers are only called from
172	// the thread that threw the exception.
173	INT	$3
174
175	// Exception from Go thread, set R14.
176	MOVQ	g(AX), R14
177
178	ADJSP	$40
179	MOVQ	CX, 0(SP)
180	MOVQ	DX, 8(SP)
181	MOVQ	R8, 16(SP)
182	MOVQ	R9, 24(SP)
183	CALL	runtime·sehhandler(SB)
184	MOVL	32(SP), AX
185
186	ADJSP	$-40
187
188	POP_REGS_HOST_TO_ABI0()
189	RET
190
191TEXT runtime·callbackasm1(SB),NOSPLIT|NOFRAME,$0
192	// Construct args vector for cgocallback().
193	// By windows/amd64 calling convention first 4 args are in CX, DX, R8, R9
194	// args from the 5th on are on the stack.
195	// In any case, even if function has 0,1,2,3,4 args, there is reserved
196	// but uninitialized "shadow space" for the first 4 args.
197	// The values are in registers.
198	MOVQ	CX, (16+0)(SP)
199	MOVQ	DX, (16+8)(SP)
200	MOVQ	R8, (16+16)(SP)
201	MOVQ	R9, (16+24)(SP)
202	// R8 = address of args vector
203	LEAQ	(16+0)(SP), R8
204
205	// remove return address from stack, we are not returning to callbackasm, but to its caller.
206	MOVQ	0(SP), AX
207	ADDQ	$8, SP
208
209	// determine index into runtime·cbs table
210	MOVQ	$runtime·callbackasm(SB), DX
211	SUBQ	DX, AX
212	MOVQ	$0, DX
213	MOVQ	$5, CX	// divide by 5 because each call instruction in runtime·callbacks is 5 bytes long
214	DIVL	CX
215	SUBQ	$1, AX	// subtract 1 because return PC is to the next slot
216
217	// Switch from the host ABI to the Go ABI.
218	PUSH_REGS_HOST_TO_ABI0()
219
220	// Create a struct callbackArgs on our stack to be passed as
221	// the "frame" to cgocallback and on to callbackWrap.
222	SUBQ	$(24+callbackArgs__size), SP
223	MOVQ	AX, (24+callbackArgs_index)(SP) 	// callback index
224	MOVQ	R8, (24+callbackArgs_args)(SP)  	// address of args vector
225	MOVQ	$0, (24+callbackArgs_result)(SP)	// result
226	LEAQ	24(SP), AX
227	// Call cgocallback, which will call callbackWrap(frame).
228	MOVQ	$0, 16(SP)	// context
229	MOVQ	AX, 8(SP)	// frame (address of callbackArgs)
230	LEAQ	·callbackWrap<ABIInternal>(SB), BX	// cgocallback takes an ABIInternal entry-point
231	MOVQ	BX, 0(SP)	// PC of function value to call (callbackWrap)
232	CALL	·cgocallback(SB)
233	// Get callback result.
234	MOVQ	(24+callbackArgs_result)(SP), AX
235	ADDQ	$(24+callbackArgs__size), SP
236
237	POP_REGS_HOST_TO_ABI0()
238
239	// The return value was placed in AX above.
240	RET
241
242// uint32 tstart_stdcall(M *newm);
243TEXT runtime·tstart_stdcall(SB),NOSPLIT|NOFRAME,$0
244	// Switch from the host ABI to the Go ABI.
245	PUSH_REGS_HOST_TO_ABI0()
246
247	// CX contains first arg newm
248	MOVQ	m_g0(CX), DX		// g
249
250	// Layout new m scheduler stack on os stack.
251	MOVQ	SP, AX
252	MOVQ	AX, (g_stack+stack_hi)(DX)
253	SUBQ	$(64*1024), AX		// initial stack size (adjusted later)
254	MOVQ	AX, (g_stack+stack_lo)(DX)
255	ADDQ	$const_stackGuard, AX
256	MOVQ	AX, g_stackguard0(DX)
257	MOVQ	AX, g_stackguard1(DX)
258
259	// Set up tls.
260	LEAQ	m_tls(CX), DI
261	MOVQ	CX, g_m(DX)
262	MOVQ	DX, g(DI)
263	CALL	runtime·settls(SB) // clobbers CX
264
265	CALL	runtime·stackcheck(SB)	// clobbers AX,CX
266	CALL	runtime·mstart(SB)
267
268	POP_REGS_HOST_TO_ABI0()
269
270	XORL	AX, AX			// return 0 == success
271	RET
272
273// set tls base to DI
274TEXT runtime·settls(SB),NOSPLIT,$0
275	MOVQ	runtime·tls_g(SB), CX
276	MOVQ	DI, 0(CX)(GS)
277	RET
278
279TEXT runtime·nanotime1(SB),NOSPLIT,$0-8
280	MOVQ	$_INTERRUPT_TIME, DI
281	MOVQ	time_lo(DI), AX
282	IMULQ	$100, AX
283	MOVQ	AX, ret+0(FP)
284	RET
285
286// func osSetupTLS(mp *m)
287// Setup TLS. for use by needm on Windows.
288TEXT runtime·osSetupTLS(SB),NOSPLIT,$0-8
289	MOVQ	mp+0(FP), AX
290	LEAQ	m_tls(AX), DI
291	CALL	runtime·settls(SB)
292	RET
293
294// This is called from rt0_go, which runs on the system stack
295// using the initial stack allocated by the OS.
296TEXT runtime·wintls(SB),NOSPLIT,$0
297	// Allocate a TLS slot to hold g across calls to external code
298	MOVQ	SP, AX
299	ANDQ	$~15, SP	// alignment as per Windows requirement
300	SUBQ	$48, SP	// room for SP and 4 args as per Windows requirement
301			// plus one extra word to keep stack 16 bytes aligned
302	MOVQ	AX, 32(SP)
303	MOVQ	runtime·_TlsAlloc(SB), AX
304	CALL	AX
305	MOVQ	32(SP), SP
306
307	MOVQ	AX, CX	// TLS index
308
309	// Assert that slot is less than 64 so we can use _TEB->TlsSlots
310	CMPQ	CX, $64
311	JB	ok
312
313	// Fallback to the TEB arbitrary pointer.
314	// TODO: don't use the arbitrary pointer (see go.dev/issue/59824)
315	MOVQ	$TEB_ArbitraryPtr, CX
316	JMP	settls
317ok:
318	// Convert the TLS index at CX into
319	// an offset from TEB_TlsSlots.
320	SHLQ	$3, CX
321
322	// Save offset from TLS into tls_g.
323	ADDQ	$TEB_TlsSlots, CX
324settls:
325	MOVQ	CX, runtime·tls_g(SB)
326	RET
327