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