Merge from vendor branch BIND:
[dragonfly.git] / sys / emulation / ndis / subr_hal.c
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  * $FreeBSD: src/sys/compat/ndis/subr_hal.c,v 1.12 2004/04/19 22:39:04 wpaul Exp $
33  * $DragonFly: src/sys/emulation/ndis/subr_hal.c,v 1.1 2004/07/29 20:51:34 dillon Exp $
34  */
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/proc.h>
44
45 #include <sys/systm.h>
46 #include <machine/clock.h>
47 #include <machine/bus_memio.h>
48 #include <machine/bus_pio.h>
49 #include <machine/bus.h>
50
51 #include <sys/bus.h>
52 #include <sys/rman.h>
53 #include <sys/thread2.h>
54
55 #include "regcall.h"
56 #include "pe_var.h"
57 #include "resource_var.h"
58 #include "ntoskrnl_var.h"
59 #include "ndis_var.h"
60 #include "hal_var.h"
61
62 #define FUNC void(*)(void)
63
64 __stdcall static void hal_stall_exec_cpu(uint32_t);
65 __stdcall static void hal_writeport_buf_ulong(uint32_t *,
66         uint32_t *, uint32_t);
67 __stdcall static void hal_writeport_buf_ushort(uint16_t *,
68         uint16_t *, uint32_t);
69 __stdcall static void hal_writeport_buf_uchar(uint8_t *,
70         uint8_t *, uint32_t);
71 __stdcall static void hal_writeport_ulong(uint32_t *, uint32_t);
72 __stdcall static void hal_writeport_ushort(uint16_t *, uint16_t);
73 __stdcall static void hal_writeport_uchar(uint8_t *, uint8_t);
74 __stdcall static uint32_t hal_readport_ulong(uint32_t *);
75 __stdcall static uint16_t hal_readport_ushort(uint16_t *);
76 __stdcall static uint8_t hal_readport_uchar(uint8_t *);
77 __stdcall static void hal_readport_buf_ulong(uint32_t *,
78         uint32_t *, uint32_t);
79 __stdcall static void hal_readport_buf_ushort(uint16_t *,
80         uint16_t *, uint32_t);
81 __stdcall static void hal_readport_buf_uchar(uint8_t *,
82         uint8_t *, uint32_t);
83 __stdcall static uint64_t hal_perfcount(uint64_t *);
84 __stdcall static void dummy (void);
85
86 extern struct mtx_pool *ndis_mtxpool;
87
88 __stdcall static void
89 hal_stall_exec_cpu(usecs)
90         uint32_t                usecs;
91 {
92         DELAY(usecs);
93         return;
94 }
95
96 __stdcall static void
97 hal_writeport_ulong(port, val)
98         uint32_t                *port;
99         uint32_t                val;
100 {
101         bus_space_write_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val);
102         return;
103 }
104
105 __stdcall static void
106 hal_writeport_ushort(port, val)
107         uint16_t                *port;
108         uint16_t                val;
109 {
110         bus_space_write_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val);
111         return;
112 }
113
114 __stdcall static void
115 hal_writeport_uchar(port, val)
116         uint8_t                 *port;
117         uint8_t                 val;
118 {
119         bus_space_write_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val);
120         return;
121 }
122
123 __stdcall static void
124 hal_writeport_buf_ulong(port, val, cnt)
125         uint32_t                *port;
126         uint32_t                *val;
127         uint32_t                cnt;
128 {
129         bus_space_write_multi_4(NDIS_BUS_SPACE_IO, 0x0,
130             (bus_size_t)port, val, cnt);
131         return;
132 }
133
134 __stdcall static void
135 hal_writeport_buf_ushort(port, val, cnt)
136         uint16_t                *port;
137         uint16_t                *val;
138         uint32_t                cnt;
139 {
140         bus_space_write_multi_2(NDIS_BUS_SPACE_IO, 0x0,
141             (bus_size_t)port, val, cnt);
142         return;
143 }
144
145 __stdcall static void
146 hal_writeport_buf_uchar(port, val, cnt)
147         uint8_t                 *port;
148         uint8_t                 *val;
149         uint32_t                cnt;
150 {
151         bus_space_write_multi_1(NDIS_BUS_SPACE_IO, 0x0,
152             (bus_size_t)port, val, cnt);
153         return;
154 }
155
156 __stdcall static uint16_t
157 hal_readport_ushort(port)
158         uint16_t                *port;
159 {
160         return(bus_space_read_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port));
161 }
162
163 __stdcall static uint32_t
164 hal_readport_ulong(port)
165         uint32_t                *port;
166 {
167         return(bus_space_read_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port));
168 }
169
170 __stdcall static uint8_t
171 hal_readport_uchar(port)
172         uint8_t                 *port;
173 {
174         return(bus_space_read_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port));
175 }
176
177 __stdcall static void
178 hal_readport_buf_ulong(port, val, cnt)
179         uint32_t                *port;
180         uint32_t                *val;
181         uint32_t                cnt;
182 {
183         bus_space_read_multi_4(NDIS_BUS_SPACE_IO, 0x0,
184             (bus_size_t)port, val, cnt);
185         return;
186 }
187
188 __stdcall static void
189 hal_readport_buf_ushort(port, val, cnt)
190         uint16_t                *port;
191         uint16_t                *val;
192         uint32_t                cnt;
193 {
194         bus_space_read_multi_2(NDIS_BUS_SPACE_IO, 0x0,
195             (bus_size_t)port, val, cnt);
196         return;
197 }
198
199 __stdcall static void
200 hal_readport_buf_uchar(port, val, cnt)
201         uint8_t                 *port;
202         uint8_t                 *val;
203         uint32_t                cnt;
204 {
205         bus_space_read_multi_1(NDIS_BUS_SPACE_IO, 0x0,
206             (bus_size_t)port, val, cnt);
207         return;
208 }
209
210 /*
211  * The spinlock implementation in Windows differs from that of FreeBSD.
212  * The basic operation of spinlocks involves two steps: 1) spin in a
213  * tight loop while trying to acquire a lock, 2) after obtaining the
214  * lock, disable preemption. (Note that on uniprocessor systems, you're
215  * allowed to skip the first step and just lock out pre-emption, since
216  * it's not possible for you to be in contention with another running
217  * thread.) Later, you release the lock then re-enable preemption.
218  * The difference between Windows and FreeBSD lies in how preemption
219  * is disabled. In FreeBSD, it's done using critical_enter(), which on
220  * the x86 arch translates to a cli instruction. This masks off all
221  * interrupts, and effectively stops the scheduler from ever running
222  * so _nothing_ can execute except the current thread. In Windows,
223  * preemption is disabled by raising the processor IRQL to DISPATCH_LEVEL.
224  * This stops other threads from running, but does _not_ block device
225  * interrupts. This means ISRs can still run, and they can make other
226  * threads runable, but those other threads won't be able to execute
227  * until the current thread lowers the IRQL to something less than
228  * DISPATCH_LEVEL.
229  *
230  * In FreeBSD, ISRs run in interrupt threads, so to duplicate the
231  * Windows notion of IRQLs, we use the following rules:
232  *
233  * PASSIVE_LEVEL == normal kernel thread priority
234  * DISPATCH_LEVEL == lowest interrupt thread priotity (PI_SOFT)
235  * DEVICE_LEVEL == highest interrupt thread priority  (PI_REALTIME)
236  * HIGH_LEVEL == interrupts disabled (critical_enter())
237  *
238  * Be aware that, at least on the x86 arch, the Windows spinlock
239  * functions are divided up in peculiar ways. The actual spinlock
240  * functions are KfAcquireSpinLock() and KfReleaseSpinLock(), and
241  * they live in HAL.dll. Meanwhile, KeInitializeSpinLock(),
242  * KefAcquireSpinLockAtDpcLevel() and KefReleaseSpinLockFromDpcLevel()
243  * live in ntoskrnl.exe. Most Windows source code will call
244  * KeAcquireSpinLock() and KeReleaseSpinLock(), but these are just
245  * macros that call KfAcquireSpinLock() and KfReleaseSpinLock().
246  * KefAcquireSpinLockAtDpcLevel() and KefReleaseSpinLockFromDpcLevel()
247  * perform the lock aquisition/release functions without doing the
248  * IRQL manipulation, and are used when one is already running at
249  * DISPATCH_LEVEL. Make sense? Good.
250  *
251  * According to the Microsoft documentation, any thread that calls
252  * KeAcquireSpinLock() must be running at IRQL <= DISPATCH_LEVEL. If
253  * we detect someone trying to acquire a spinlock from DEVICE_LEVEL
254  * or HIGH_LEVEL, we panic.
255  */
256
257 __stdcall __regcall uint8_t
258 hal_lock(REGARGS1(kspin_lock *lock))
259 {
260         uint8_t                 oldirql;
261
262         /* I am so going to hell for this. */
263         if (hal_irql() > DISPATCH_LEVEL)
264                 panic("IRQL_NOT_LESS_THAN_OR_EQUAL");
265
266         oldirql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
267         FASTCALL1(ntoskrnl_lock_dpc, lock);
268
269         return(oldirql);
270 }
271
272 __stdcall __regcall void
273 hal_unlock(REGARGS2(kspin_lock *lock, uint8_t newirql))
274 {
275         FASTCALL1(ntoskrnl_unlock_dpc, lock);
276         FASTCALL1(hal_lower_irql, newirql);
277
278         return;
279 }
280
281 __stdcall uint8_t
282 hal_irql(void)
283 {
284         if (AT_DISPATCH_LEVEL(curthread))
285                 return(DISPATCH_LEVEL);
286         return(PASSIVE_LEVEL);
287 }
288
289 __stdcall static uint64_t
290 hal_perfcount(freq)
291         uint64_t                *freq;
292 {
293         if (freq != NULL)
294                 *freq = hz;
295
296         return((uint64_t)ticks);
297 }
298
299 __stdcall __regcall uint8_t
300 hal_raise_irql(REGARGS1(uint8_t irql))
301 {
302         uint8_t                 oldirql;
303
304         if (irql < hal_irql())
305                 panic("IRQL_NOT_LESS_THAN");
306
307         if (hal_irql() == DISPATCH_LEVEL)
308                 return(DISPATCH_LEVEL);
309
310         oldirql = lwkt_getpri_self();
311         lwkt_setpri_self(TDPRI_INT_HIGH);
312
313         return(oldirql);
314 }
315
316 __stdcall __regcall void 
317 hal_lower_irql(REGARGS1(uint8_t oldirql))
318 {
319         if (oldirql == DISPATCH_LEVEL)
320                 return;
321
322         if (hal_irql() != DISPATCH_LEVEL)
323                 panic("IRQL_NOT_GREATER_THAN");
324
325         lwkt_setpri_self(oldirql);
326 }
327
328 __stdcall
329 static void dummy()
330 {
331         printf ("hal dummy called...\n");
332         return;
333 }
334
335 image_patch_table hal_functbl[] = {
336         { "KeStallExecutionProcessor",  (FUNC)hal_stall_exec_cpu },
337         { "WRITE_PORT_ULONG",           (FUNC)hal_writeport_ulong },
338         { "WRITE_PORT_USHORT",          (FUNC)hal_writeport_ushort },
339         { "WRITE_PORT_UCHAR",           (FUNC)hal_writeport_uchar },
340         { "WRITE_PORT_BUFFER_ULONG",    (FUNC)hal_writeport_buf_ulong },
341         { "WRITE_PORT_BUFFER_USHORT",   (FUNC)hal_writeport_buf_ushort },
342         { "WRITE_PORT_BUFFER_UCHAR",    (FUNC)hal_writeport_buf_uchar },
343         { "READ_PORT_ULONG",            (FUNC)hal_readport_ulong },
344         { "READ_PORT_USHORT",           (FUNC)hal_readport_ushort },
345         { "READ_PORT_UCHAR",            (FUNC)hal_readport_uchar },
346         { "READ_PORT_BUFFER_ULONG",     (FUNC)hal_readport_buf_ulong },
347         { "READ_PORT_BUFFER_USHORT",    (FUNC)hal_readport_buf_ushort },
348         { "READ_PORT_BUFFER_UCHAR",     (FUNC)hal_readport_buf_uchar },
349         { "KfAcquireSpinLock",          (FUNC)hal_lock },
350         { "KfReleaseSpinLock",          (FUNC)hal_unlock },
351         { "KeGetCurrentIrql",           (FUNC)hal_irql },
352         { "KeQueryPerformanceCounter",  (FUNC)hal_perfcount },
353         { "KfLowerIrql",                (FUNC)hal_lower_irql },
354         { "KfRaiseIrql",                (FUNC)hal_raise_irql },
355
356         /*
357          * This last entry is a catch-all for any function we haven't
358          * implemented yet. The PE import list patching routine will
359          * use it for any function that doesn't have an explicit match
360          * in this table.
361          */
362
363         { NULL, (FUNC)dummy },
364
365         /* End of list. */
366
367         { NULL, NULL },
368 };