subr_hal.c revision 128447
1/*
2 * Copyright (c) 2003
3 *	Bill Paul <wpaul@windriver.com>.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by Bill Paul.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30 * THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/cdefs.h>
34__FBSDID("$FreeBSD: head/sys/compat/ndis/subr_hal.c 128447 2004-04-19 22:39:04Z wpaul $");
35
36#include <sys/param.h>
37#include <sys/types.h>
38#include <sys/errno.h>
39
40#include <sys/callout.h>
41#include <sys/kernel.h>
42#include <sys/lock.h>
43#include <sys/mutex.h>
44#include <sys/proc.h>
45#include <sys/sched.h>
46
47#include <sys/systm.h>
48#include <machine/clock.h>
49#include <machine/bus_memio.h>
50#include <machine/bus_pio.h>
51#include <machine/bus.h>
52
53#include <sys/bus.h>
54#include <sys/rman.h>
55
56#include <compat/ndis/pe_var.h>
57#include <compat/ndis/hal_var.h>
58#include <compat/ndis/ntoskrnl_var.h>
59
60#define FUNC void(*)(void)
61
62__stdcall static void hal_stall_exec_cpu(uint32_t);
63__stdcall static void hal_writeport_buf_ulong(uint32_t *,
64	uint32_t *, uint32_t);
65__stdcall static void hal_writeport_buf_ushort(uint16_t *,
66	uint16_t *, uint32_t);
67__stdcall static void hal_writeport_buf_uchar(uint8_t *,
68	uint8_t *, uint32_t);
69__stdcall static void hal_writeport_ulong(uint32_t *, uint32_t);
70__stdcall static void hal_writeport_ushort(uint16_t *, uint16_t);
71__stdcall static void hal_writeport_uchar(uint8_t *, uint8_t);
72__stdcall static uint32_t hal_readport_ulong(uint32_t *);
73__stdcall static uint16_t hal_readport_ushort(uint16_t *);
74__stdcall static uint8_t hal_readport_uchar(uint8_t *);
75__stdcall static void hal_readport_buf_ulong(uint32_t *,
76	uint32_t *, uint32_t);
77__stdcall static void hal_readport_buf_ushort(uint16_t *,
78	uint16_t *, uint32_t);
79__stdcall static void hal_readport_buf_uchar(uint8_t *,
80	uint8_t *, uint32_t);
81__stdcall static uint64_t hal_perfcount(uint64_t *);
82__stdcall static void dummy (void);
83
84extern struct mtx_pool *ndis_mtxpool;
85
86__stdcall static void
87hal_stall_exec_cpu(usecs)
88	uint32_t		usecs;
89{
90	DELAY(usecs);
91	return;
92}
93
94__stdcall static void
95hal_writeport_ulong(port, val)
96	uint32_t		*port;
97	uint32_t		val;
98{
99	bus_space_write_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val);
100	return;
101}
102
103__stdcall static void
104hal_writeport_ushort(port, val)
105	uint16_t		*port;
106	uint16_t		val;
107{
108	bus_space_write_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val);
109	return;
110}
111
112__stdcall static void
113hal_writeport_uchar(port, val)
114	uint8_t			*port;
115	uint8_t			val;
116{
117	bus_space_write_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val);
118	return;
119}
120
121__stdcall static void
122hal_writeport_buf_ulong(port, val, cnt)
123	uint32_t		*port;
124	uint32_t		*val;
125	uint32_t		cnt;
126{
127	bus_space_write_multi_4(NDIS_BUS_SPACE_IO, 0x0,
128	    (bus_size_t)port, val, cnt);
129	return;
130}
131
132__stdcall static void
133hal_writeport_buf_ushort(port, val, cnt)
134	uint16_t		*port;
135	uint16_t		*val;
136	uint32_t		cnt;
137{
138	bus_space_write_multi_2(NDIS_BUS_SPACE_IO, 0x0,
139	    (bus_size_t)port, val, cnt);
140	return;
141}
142
143__stdcall static void
144hal_writeport_buf_uchar(port, val, cnt)
145	uint8_t			*port;
146	uint8_t			*val;
147	uint32_t		cnt;
148{
149	bus_space_write_multi_1(NDIS_BUS_SPACE_IO, 0x0,
150	    (bus_size_t)port, val, cnt);
151	return;
152}
153
154__stdcall static uint16_t
155hal_readport_ushort(port)
156	uint16_t		*port;
157{
158	return(bus_space_read_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port));
159}
160
161__stdcall static uint32_t
162hal_readport_ulong(port)
163	uint32_t		*port;
164{
165	return(bus_space_read_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port));
166}
167
168__stdcall static uint8_t
169hal_readport_uchar(port)
170	uint8_t			*port;
171{
172	return(bus_space_read_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port));
173}
174
175__stdcall static void
176hal_readport_buf_ulong(port, val, cnt)
177	uint32_t		*port;
178	uint32_t		*val;
179	uint32_t		cnt;
180{
181	bus_space_read_multi_4(NDIS_BUS_SPACE_IO, 0x0,
182	    (bus_size_t)port, val, cnt);
183	return;
184}
185
186__stdcall static void
187hal_readport_buf_ushort(port, val, cnt)
188	uint16_t		*port;
189	uint16_t		*val;
190	uint32_t		cnt;
191{
192	bus_space_read_multi_2(NDIS_BUS_SPACE_IO, 0x0,
193	    (bus_size_t)port, val, cnt);
194	return;
195}
196
197__stdcall static void
198hal_readport_buf_uchar(port, val, cnt)
199	uint8_t			*port;
200	uint8_t			*val;
201	uint32_t		cnt;
202{
203	bus_space_read_multi_1(NDIS_BUS_SPACE_IO, 0x0,
204	    (bus_size_t)port, val, cnt);
205	return;
206}
207
208/*
209 * The spinlock implementation in Windows differs from that of FreeBSD.
210 * The basic operation of spinlocks involves two steps: 1) spin in a
211 * tight loop while trying to acquire a lock, 2) after obtaining the
212 * lock, disable preemption. (Note that on uniprocessor systems, you're
213 * allowed to skip the first step and just lock out pre-emption, since
214 * it's not possible for you to be in contention with another running
215 * thread.) Later, you release the lock then re-enable preemption.
216 * The difference between Windows and FreeBSD lies in how preemption
217 * is disabled. In FreeBSD, it's done using critical_enter(), which on
218 * the x86 arch translates to a cli instruction. This masks off all
219 * interrupts, and effectively stops the scheduler from ever running
220 * so _nothing_ can execute except the current thread. In Windows,
221 * preemption is disabled by raising the processor IRQL to DISPATCH_LEVEL.
222 * This stops other threads from running, but does _not_ block device
223 * interrupts. This means ISRs can still run, and they can make other
224 * threads runable, but those other threads won't be able to execute
225 * until the current thread lowers the IRQL to something less than
226 * DISPATCH_LEVEL.
227 *
228 * In FreeBSD, ISRs run in interrupt threads, so to duplicate the
229 * Windows notion of IRQLs, we use the following rules:
230 *
231 * PASSIVE_LEVEL == normal kernel thread priority
232 * DISPATCH_LEVEL == lowest interrupt thread priotity (PI_SOFT)
233 * DEVICE_LEVEL == highest interrupt thread priority  (PI_REALTIME)
234 * HIGH_LEVEL == interrupts disabled (critical_enter())
235 *
236 * Be aware that, at least on the x86 arch, the Windows spinlock
237 * functions are divided up in peculiar ways. The actual spinlock
238 * functions are KfAcquireSpinLock() and KfReleaseSpinLock(), and
239 * they live in HAL.dll. Meanwhile, KeInitializeSpinLock(),
240 * KefAcquireSpinLockAtDpcLevel() and KefReleaseSpinLockFromDpcLevel()
241 * live in ntoskrnl.exe. Most Windows source code will call
242 * KeAcquireSpinLock() and KeReleaseSpinLock(), but these are just
243 * macros that call KfAcquireSpinLock() and KfReleaseSpinLock().
244 * KefAcquireSpinLockAtDpcLevel() and KefReleaseSpinLockFromDpcLevel()
245 * perform the lock aquisition/release functions without doing the
246 * IRQL manipulation, and are used when one is already running at
247 * DISPATCH_LEVEL. Make sense? Good.
248 *
249 * According to the Microsoft documentation, any thread that calls
250 * KeAcquireSpinLock() must be running at IRQL <= DISPATCH_LEVEL. If
251 * we detect someone trying to acquire a spinlock from DEVICE_LEVEL
252 * or HIGH_LEVEL, we panic.
253 */
254
255__stdcall uint8_t
256hal_lock(/*lock*/void)
257{
258	kspin_lock		*lock;
259	uint8_t			oldirql;
260
261	__asm__ __volatile__ ("" : "=c" (lock));
262
263	/* I am so going to hell for this. */
264	if (hal_irql() > DISPATCH_LEVEL)
265		panic("IRQL_NOT_LESS_THAN_OR_EQUAL");
266
267	oldirql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
268	FASTCALL1(ntoskrnl_lock_dpc, lock);
269
270	return(oldirql);
271}
272
273__stdcall void
274hal_unlock(/*lock, newirql*/void)
275{
276	kspin_lock		*lock;
277	uint8_t			newirql;
278
279	__asm__ __volatile__ ("" : "=c" (lock), "=d" (newirql));
280
281	FASTCALL1(ntoskrnl_unlock_dpc, lock);
282	FASTCALL1(hal_lower_irql, newirql);
283
284	return;
285}
286
287__stdcall uint8_t
288hal_irql(void)
289{
290	if (AT_DISPATCH_LEVEL(curthread))
291		return(DISPATCH_LEVEL);
292	return(PASSIVE_LEVEL);
293}
294
295__stdcall static uint64_t
296hal_perfcount(freq)
297	uint64_t		*freq;
298{
299	if (freq != NULL)
300		*freq = hz;
301
302	return((uint64_t)ticks);
303}
304
305__stdcall uint8_t
306hal_raise_irql(/*irql*/ void)
307{
308	uint8_t			irql;
309	uint8_t			oldirql;
310
311	__asm__ __volatile__ ("" : "=c" (irql));
312
313	if (irql < hal_irql())
314		panic("IRQL_NOT_LESS_THAN");
315
316	if (hal_irql() == DISPATCH_LEVEL)
317		return(DISPATCH_LEVEL);
318
319	mtx_lock_spin(&sched_lock);
320	oldirql = curthread->td_base_pri;
321	sched_prio(curthread, PI_REALTIME);
322	curthread->td_base_pri = PI_REALTIME;
323	mtx_unlock_spin(&sched_lock);
324
325	return(oldirql);
326}
327
328__stdcall void
329hal_lower_irql(/*oldirql*/ void)
330{
331	uint8_t			oldirql;
332
333	__asm__ __volatile__ ("" : "=c" (oldirql));
334
335	if (oldirql == DISPATCH_LEVEL)
336		return;
337
338	if (hal_irql() != DISPATCH_LEVEL)
339		panic("IRQL_NOT_GREATER_THAN");
340
341	mtx_lock_spin(&sched_lock);
342	curthread->td_base_pri = oldirql;
343	sched_prio(curthread, oldirql);
344	mtx_unlock_spin(&sched_lock);
345
346	return;
347}
348
349__stdcall
350static void dummy()
351{
352	printf ("hal dummy called...\n");
353	return;
354}
355
356image_patch_table hal_functbl[] = {
357	{ "KeStallExecutionProcessor",	(FUNC)hal_stall_exec_cpu },
358	{ "WRITE_PORT_ULONG",		(FUNC)hal_writeport_ulong },
359	{ "WRITE_PORT_USHORT",		(FUNC)hal_writeport_ushort },
360	{ "WRITE_PORT_UCHAR",		(FUNC)hal_writeport_uchar },
361	{ "WRITE_PORT_BUFFER_ULONG",	(FUNC)hal_writeport_buf_ulong },
362	{ "WRITE_PORT_BUFFER_USHORT",	(FUNC)hal_writeport_buf_ushort },
363	{ "WRITE_PORT_BUFFER_UCHAR",	(FUNC)hal_writeport_buf_uchar },
364	{ "READ_PORT_ULONG",		(FUNC)hal_readport_ulong },
365	{ "READ_PORT_USHORT",		(FUNC)hal_readport_ushort },
366	{ "READ_PORT_UCHAR",		(FUNC)hal_readport_uchar },
367	{ "READ_PORT_BUFFER_ULONG",	(FUNC)hal_readport_buf_ulong },
368	{ "READ_PORT_BUFFER_USHORT",	(FUNC)hal_readport_buf_ushort },
369	{ "READ_PORT_BUFFER_UCHAR",	(FUNC)hal_readport_buf_uchar },
370	{ "KfAcquireSpinLock",		(FUNC)hal_lock },
371	{ "KfReleaseSpinLock",		(FUNC)hal_unlock },
372	{ "KeGetCurrentIrql",		(FUNC)hal_irql },
373	{ "KeQueryPerformanceCounter",	(FUNC)hal_perfcount },
374	{ "KfLowerIrql",		(FUNC)hal_lower_irql },
375	{ "KfRaiseIrql",		(FUNC)hal_raise_irql },
376
377	/*
378	 * This last entry is a catch-all for any function we haven't
379	 * implemented yet. The PE import list patching routine will
380	 * use it for any function that doesn't have an explicit match
381	 * in this table.
382	 */
383
384	{ NULL, (FUNC)dummy },
385
386	/* End of list. */
387
388	{ NULL, NULL },
389};
390