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.
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 $
36 #include <sys/param.h>
37 #include <sys/types.h>
38 #include <sys/errno.h>
40 #include <sys/callout.h>
41 #include <sys/kernel.h>
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>
53 #include <sys/thread2.h>
57 #include "resource_var.h"
58 #include "ntoskrnl_var.h"
62 #define FUNC void(*)(void)
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 *,
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 *,
83 __stdcall static uint64_t hal_perfcount(uint64_t *);
84 __stdcall static void dummy (void);
86 extern struct mtx_pool *ndis_mtxpool;
89 hal_stall_exec_cpu(usecs)
97 hal_writeport_ulong(port, val)
101 bus_space_write_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val);
105 __stdcall static void
106 hal_writeport_ushort(port, val)
110 bus_space_write_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val);
114 __stdcall static void
115 hal_writeport_uchar(port, val)
119 bus_space_write_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val);
123 __stdcall static void
124 hal_writeport_buf_ulong(port, val, cnt)
129 bus_space_write_multi_4(NDIS_BUS_SPACE_IO, 0x0,
130 (bus_size_t)port, val, cnt);
134 __stdcall static void
135 hal_writeport_buf_ushort(port, val, cnt)
140 bus_space_write_multi_2(NDIS_BUS_SPACE_IO, 0x0,
141 (bus_size_t)port, val, cnt);
145 __stdcall static void
146 hal_writeport_buf_uchar(port, val, cnt)
151 bus_space_write_multi_1(NDIS_BUS_SPACE_IO, 0x0,
152 (bus_size_t)port, val, cnt);
156 __stdcall static uint16_t
157 hal_readport_ushort(port)
160 return(bus_space_read_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port));
163 __stdcall static uint32_t
164 hal_readport_ulong(port)
167 return(bus_space_read_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port));
170 __stdcall static uint8_t
171 hal_readport_uchar(port)
174 return(bus_space_read_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port));
177 __stdcall static void
178 hal_readport_buf_ulong(port, val, cnt)
183 bus_space_read_multi_4(NDIS_BUS_SPACE_IO, 0x0,
184 (bus_size_t)port, val, cnt);
188 __stdcall static void
189 hal_readport_buf_ushort(port, val, cnt)
194 bus_space_read_multi_2(NDIS_BUS_SPACE_IO, 0x0,
195 (bus_size_t)port, val, cnt);
199 __stdcall static void
200 hal_readport_buf_uchar(port, val, cnt)
205 bus_space_read_multi_1(NDIS_BUS_SPACE_IO, 0x0,
206 (bus_size_t)port, val, cnt);
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
230 * In FreeBSD, ISRs run in interrupt threads, so to duplicate the
231 * Windows notion of IRQLs, we use the following rules:
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())
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.
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.
257 __stdcall __regcall uint8_t
258 hal_lock(REGARGS1(kspin_lock *lock))
262 /* I am so going to hell for this. */
263 if (hal_irql() > DISPATCH_LEVEL)
264 panic("IRQL_NOT_LESS_THAN_OR_EQUAL");
266 oldirql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
267 FASTCALL1(ntoskrnl_lock_dpc, lock);
272 __stdcall __regcall void
273 hal_unlock(REGARGS2(kspin_lock *lock, uint8_t newirql))
275 FASTCALL1(ntoskrnl_unlock_dpc, lock);
276 FASTCALL1(hal_lower_irql, newirql);
284 if (AT_DISPATCH_LEVEL(curthread))
285 return(DISPATCH_LEVEL);
286 return(PASSIVE_LEVEL);
289 __stdcall static uint64_t
296 return((uint64_t)ticks);
299 __stdcall __regcall uint8_t
300 hal_raise_irql(REGARGS1(uint8_t irql))
304 if (irql < hal_irql())
305 panic("IRQL_NOT_LESS_THAN");
307 if (hal_irql() == DISPATCH_LEVEL)
308 return(DISPATCH_LEVEL);
310 oldirql = lwkt_getpri_self();
311 lwkt_setpri_self(TDPRI_INT_HIGH);
316 __stdcall __regcall void
317 hal_lower_irql(REGARGS1(uint8_t oldirql))
319 if (oldirql == DISPATCH_LEVEL)
322 if (hal_irql() != DISPATCH_LEVEL)
323 panic("IRQL_NOT_GREATER_THAN");
325 lwkt_setpri_self(oldirql);
331 printf ("hal dummy called...\n");
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 },
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
363 { NULL, (FUNC)dummy },