3 * Bill Paul <wpaul@windriver.com>. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
33 #include <sys/cdefs.h>
35 __FBSDID("$FreeBSD: src/sys/compat/ndis/subr_hal.c,v 1.13.2.3 2005/03/31 04:24:35 wpaul Exp $");
38 __KERNEL_RCSID(0, "$NetBSD: subr_hal.c,v 1.6 2009/03/14 15:36:16 dsl Exp $");
41 #include <sys/param.h>
42 #include <sys/types.h>
43 #include <sys/errno.h>
45 #include <sys/callout.h>
46 #include <sys/kernel.h>
49 #include <sys/mutex.h>
52 #include <sys/sched.h>
54 #include <sys/module.h>
57 #include <sys/systm.h>
59 #include <machine/clock.h>
60 #include <machine/bus_memio.h>
61 #include <machine/bus_pio.h>
70 #include <compat/ndis/pe_var.h>
71 #include <compat/ndis/ntoskrnl_var.h>
72 #include <compat/ndis/hal_var.h>
74 __stdcall
static void KeStallExecutionProcessor(uint32_t);
75 __stdcall
static void WRITE_PORT_BUFFER_ULONG(uint32_t *,
76 uint32_t *, uint32_t);
77 __stdcall
static void WRITE_PORT_BUFFER_USHORT(uint16_t *,
78 uint16_t *, uint32_t);
79 __stdcall
static void WRITE_PORT_BUFFER_UCHAR(uint8_t *,
81 __stdcall
static void WRITE_PORT_ULONG(uint32_t *, uint32_t);
82 __stdcall
static void WRITE_PORT_USHORT(uint16_t *, uint16_t);
83 __stdcall
static void WRITE_PORT_UCHAR(uint8_t *, uint8_t);
84 __stdcall
static uint32_t READ_PORT_ULONG(uint32_t *);
85 __stdcall
static uint16_t READ_PORT_USHORT(uint16_t *);
86 __stdcall
static uint8_t READ_PORT_UCHAR(uint8_t *);
87 __stdcall
static void READ_PORT_BUFFER_ULONG(uint32_t *,
88 uint32_t *, uint32_t);
89 __stdcall
static void READ_PORT_BUFFER_USHORT(uint16_t *,
90 uint16_t *, uint32_t);
91 __stdcall
static void READ_PORT_BUFFER_UCHAR(uint8_t *,
93 __stdcall
static uint64_t KeQueryPerformanceCounter(uint64_t *);
94 __stdcall
static void dummy (void);
96 extern struct mtx_pool
*ndis_mtxpool
;
101 image_patch_table
*patch
;
104 while (patch
->ipt_func
!= NULL
) {
105 windrv_wrap((funcptr
)patch
->ipt_func
,
106 (funcptr
*)&patch
->ipt_wrap
);
116 image_patch_table
*patch
;
119 while (patch
->ipt_func
!= NULL
) {
120 windrv_unwrap(patch
->ipt_wrap
);
127 __stdcall
static void
128 KeStallExecutionProcessor(uint32_t usecs
)
134 __stdcall
static void
135 WRITE_PORT_ULONG(uint32_t *port
, uint32_t val
)
137 bus_space_write_4(NDIS_BUS_SPACE_IO
, 0x0, (bus_size_t
)port
, val
);
141 __stdcall
static void
142 WRITE_PORT_USHORT(uint16_t *port
, uint16_t val
)
144 bus_space_write_2(NDIS_BUS_SPACE_IO
, 0x0, (bus_size_t
)port
, val
);
148 __stdcall
static void
149 WRITE_PORT_UCHAR(uint8_t *port
, uint8_t val
)
151 bus_space_write_1(NDIS_BUS_SPACE_IO
, 0x0, (bus_size_t
)port
, val
);
155 __stdcall
static void
156 WRITE_PORT_BUFFER_ULONG(uint32_t *port
, uint32_t *val
, uint32_t cnt
)
158 bus_space_write_multi_4(NDIS_BUS_SPACE_IO
, 0x0,
159 (bus_size_t
)port
, val
, cnt
);
163 __stdcall
static void
164 WRITE_PORT_BUFFER_USHORT(uint16_t *port
, uint16_t *val
, uint32_t cnt
)
166 bus_space_write_multi_2(NDIS_BUS_SPACE_IO
, 0x0,
167 (bus_size_t
)port
, val
, cnt
);
171 __stdcall
static void
172 WRITE_PORT_BUFFER_UCHAR(uint8_t *port
, uint8_t *val
, uint32_t cnt
)
174 bus_space_write_multi_1(NDIS_BUS_SPACE_IO
, 0x0,
175 (bus_size_t
)port
, val
, cnt
);
179 __stdcall
static uint16_t
180 READ_PORT_USHORT(uint16_t *port
)
182 return(bus_space_read_2(NDIS_BUS_SPACE_IO
, 0x0, (bus_size_t
)port
));
185 __stdcall
static uint32_t
186 READ_PORT_ULONG(uint32_t *port
)
188 return(bus_space_read_4(NDIS_BUS_SPACE_IO
, 0x0, (bus_size_t
)port
));
191 __stdcall
static uint8_t
192 READ_PORT_UCHAR(uint8_t *port
)
194 return(bus_space_read_1(NDIS_BUS_SPACE_IO
, 0x0, (bus_size_t
)port
));
197 __stdcall
static void
198 READ_PORT_BUFFER_ULONG(uint32_t *port
, uint32_t *val
, uint32_t cnt
)
200 bus_space_read_multi_4(NDIS_BUS_SPACE_IO
, 0x0,
201 (bus_size_t
)port
, val
, cnt
);
205 __stdcall
static void
206 READ_PORT_BUFFER_USHORT(uint16_t *port
, uint16_t *val
, uint32_t cnt
)
208 bus_space_read_multi_2(NDIS_BUS_SPACE_IO
, 0x0,
209 (bus_size_t
)port
, val
, cnt
);
213 __stdcall
static void
214 READ_PORT_BUFFER_UCHAR(uint8_t *port
, uint8_t *val
, uint32_t cnt
)
216 bus_space_read_multi_1(NDIS_BUS_SPACE_IO
, 0x0,
217 (bus_size_t
)port
, val
, cnt
);
222 * The spinlock implementation in Windows differs from that of FreeBSD.
223 * The basic operation of spinlocks involves two steps: 1) spin in a
224 * tight loop while trying to acquire a lock, 2) after obtaining the
225 * lock, disable preemption. (Note that on uniprocessor systems, you're
226 * allowed to skip the first step and just lock out pre-emption, since
227 * it's not possible for you to be in contention with another running
228 * thread.) Later, you release the lock then re-enable preemption.
229 * The difference between Windows and FreeBSD lies in how preemption
230 * is disabled. In FreeBSD, it's done using critical_enter(), which on
231 * the x86 arch translates to a cli instruction. This masks off all
232 * interrupts, and effectively stops the scheduler from ever running
233 * so _nothing_ can execute except the current thread. In Windows,
234 * preemption is disabled by raising the processor IRQL to DISPATCH_LEVEL.
235 * This stops other threads from running, but does _not_ block device
236 * interrupts. This means ISRs can still run, and they can make other
237 * threads runable, but those other threads won't be able to execute
238 * until the current thread lowers the IRQL to something less than
241 * There's another commonly used IRQL in Windows, which is APC_LEVEL.
242 * An APC is an Asynchronous Procedure Call, which differs from a DPC
243 * (Defered Procedure Call) in that a DPC is queued up to run in
244 * another thread, while an APC runs in the thread that scheduled
245 * it (similar to a signal handler in a UNIX process). We don't
246 * actually support the notion of APCs in FreeBSD, so for now, the
247 * only IRQLs we're interested in are DISPATCH_LEVEL and PASSIVE_LEVEL.
249 * To simulate DISPATCH_LEVEL, we raise the current thread's priority
250 * to PI_REALTIME, which is the highest we can give it. This should,
251 * if I understand things correctly, prevent anything except for an
252 * interrupt thread from preempting us. PASSIVE_LEVEL is basically
255 * Be aware that, at least on the x86 arch, the Windows spinlock
256 * functions are divided up in peculiar ways. The actual spinlock
257 * functions are KfAcquireSpinLock() and KfReleaseSpinLock(), and
258 * they live in HAL.dll. Meanwhile, KeInitializeSpinLock(),
259 * KefAcquireSpinLockAtDpcLevel() and KefReleaseSpinLockFromDpcLevel()
260 * live in ntoskrnl.exe. Most Windows source code will call
261 * KeAcquireSpinLock() and KeReleaseSpinLock(), but these are just
262 * macros that call KfAcquireSpinLock() and KfReleaseSpinLock().
263 * KefAcquireSpinLockAtDpcLevel() and KefReleaseSpinLockFromDpcLevel()
264 * perform the lock aquisition/release functions without doing the
265 * IRQL manipulation, and are used when one is already running at
266 * DISPATCH_LEVEL. Make sense? Good.
268 * According to the Microsoft documentation, any thread that calls
269 * KeAcquireSpinLock() must be running at IRQL <= DISPATCH_LEVEL. If
270 * we detect someone trying to acquire a spinlock from DEVICE_LEVEL
271 * or HIGH_LEVEL, we panic.
275 KfAcquireSpinLock(REGARGS1(kspin_lock
*lock
))
279 /* I am so going to hell for this. */
280 if (KeGetCurrentIrql() > DISPATCH_LEVEL
)
281 panic("IRQL_NOT_LESS_THAN_OR_EQUAL");
283 oldirql
= KeRaiseIrql(DISPATCH_LEVEL
);
284 KeAcquireSpinLockAtDpcLevel(lock
);
290 KfReleaseSpinLock(REGARGS2(kspin_lock
*lock
, uint8_t newirql
))
292 KeReleaseSpinLockFromDpcLevel(lock
);
293 KeLowerIrql(newirql
);
299 KeGetCurrentIrql(void)
301 if (AT_DISPATCH_LEVEL(curthread
))
302 return(DISPATCH_LEVEL
);
303 return(PASSIVE_LEVEL
);
306 __stdcall
static uint64_t
307 KeQueryPerformanceCounter(uint64_t *freq
)
312 return((uint64_t)ticks
);
317 static int ipl_raised
= FALSE
;
320 KfRaiseIrql(REGARGS1(uint8_t irql
))
327 if (irql
< KeGetCurrentIrql())
328 panic("IRQL_NOT_LESS_THAN");
330 if (KeGetCurrentIrql() == DISPATCH_LEVEL
)
331 return(DISPATCH_LEVEL
);
333 if(irql
>= DISPATCH_LEVEL
&& !ipl_raised
) {
334 old_ipl
= splsoftclock();
339 #else /* __FreeBSD__ */
340 mtx_lock_spin(&sched_lock
);
341 oldirql
= curthread
->td_base_pri
;
342 sched_prio(curthread
, PI_REALTIME
);
343 #if __FreeBSD_version < 600000
344 curthread
->td_base_pri
= PI_REALTIME
;
346 mtx_unlock_spin(&sched_lock
);
347 #endif /* __FreeBSD__ */
353 KfLowerIrql(REGARGS1(uint8_t oldirql
))
359 if (oldirql
== DISPATCH_LEVEL
)
363 if (KeGetCurrentIrql() != DISPATCH_LEVEL
)
364 panic("IRQL_NOT_GREATER_THAN");
365 #else /* __NetBSD__ */
366 if (KeGetCurrentIrql() < oldirql
)
367 panic("IRQL_NOT_GREATER_THAN");
371 if(oldirql
< DISPATCH_LEVEL
&& ipl_raised
) {
377 mtx_lock_spin(&sched_lock
);
378 #if __FreeBSD_version < 600000
379 curthread
->td_base_pri
= oldirql
;
381 sched_prio(curthread
, oldirql
);
382 mtx_unlock_spin(&sched_lock
);
383 #endif /* __NetBSD__ */
389 static void dummy(void)
391 printf ("hal dummy called...\n");
395 image_patch_table hal_functbl
[] = {
396 IMPORT_FUNC(KeStallExecutionProcessor
),
397 IMPORT_FUNC(WRITE_PORT_ULONG
),
398 IMPORT_FUNC(WRITE_PORT_USHORT
),
399 IMPORT_FUNC(WRITE_PORT_UCHAR
),
400 IMPORT_FUNC(WRITE_PORT_BUFFER_ULONG
),
401 IMPORT_FUNC(WRITE_PORT_BUFFER_USHORT
),
402 IMPORT_FUNC(WRITE_PORT_BUFFER_UCHAR
),
403 IMPORT_FUNC(READ_PORT_ULONG
),
404 IMPORT_FUNC(READ_PORT_USHORT
),
405 IMPORT_FUNC(READ_PORT_UCHAR
),
406 IMPORT_FUNC(READ_PORT_BUFFER_ULONG
),
407 IMPORT_FUNC(READ_PORT_BUFFER_USHORT
),
408 IMPORT_FUNC(READ_PORT_BUFFER_UCHAR
),
409 IMPORT_FUNC(KfAcquireSpinLock
),
410 IMPORT_FUNC(KfReleaseSpinLock
),
411 IMPORT_FUNC(KeGetCurrentIrql
),
412 IMPORT_FUNC(KeQueryPerformanceCounter
),
413 IMPORT_FUNC(KfLowerIrql
),
414 IMPORT_FUNC(KfRaiseIrql
),
417 * This last entry is a catch-all for any function we haven't
418 * implemented yet. The PE import list patching routine will
419 * use it for any function that doesn't have an explicit match
423 { NULL
, (FUNC
)dummy
, NULL
},