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.2 2005/12/10 16:06:20 swildner 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(uint32_t usecs)
96 hal_writeport_ulong(uint32_t *port, uint32_t val)
98 bus_space_write_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val);
102 __stdcall static void
103 hal_writeport_ushort(uint16_t *port, uint16_t val)
105 bus_space_write_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val);
109 __stdcall static void
110 hal_writeport_uchar(uint8_t *port, uint8_t val)
112 bus_space_write_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port, val);
116 __stdcall static void
117 hal_writeport_buf_ulong(uint32_t *port, uint32_t *val, uint32_t cnt)
119 bus_space_write_multi_4(NDIS_BUS_SPACE_IO, 0x0,
120 (bus_size_t)port, val, cnt);
124 __stdcall static void
125 hal_writeport_buf_ushort(uint16_t *port, uint16_t *val, uint32_t cnt)
127 bus_space_write_multi_2(NDIS_BUS_SPACE_IO, 0x0,
128 (bus_size_t)port, val, cnt);
132 __stdcall static void
133 hal_writeport_buf_uchar(uint8_t *port, uint8_t *val, uint32_t cnt)
135 bus_space_write_multi_1(NDIS_BUS_SPACE_IO, 0x0,
136 (bus_size_t)port, val, cnt);
140 __stdcall static uint16_t
141 hal_readport_ushort(uint16_t *port)
143 return(bus_space_read_2(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port));
146 __stdcall static uint32_t
147 hal_readport_ulong(uint32_t *port)
149 return(bus_space_read_4(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port));
152 __stdcall static uint8_t
153 hal_readport_uchar(uint8_t *port)
155 return(bus_space_read_1(NDIS_BUS_SPACE_IO, 0x0, (bus_size_t)port));
158 __stdcall static void
159 hal_readport_buf_ulong(uint32_t *port, uint32_t *val, uint32_t cnt)
161 bus_space_read_multi_4(NDIS_BUS_SPACE_IO, 0x0,
162 (bus_size_t)port, val, cnt);
166 __stdcall static void
167 hal_readport_buf_ushort(uint16_t *port, uint16_t *val, uint32_t cnt)
169 bus_space_read_multi_2(NDIS_BUS_SPACE_IO, 0x0,
170 (bus_size_t)port, val, cnt);
174 __stdcall static void
175 hal_readport_buf_uchar(uint8_t *port, uint8_t *val, uint32_t cnt)
177 bus_space_read_multi_1(NDIS_BUS_SPACE_IO, 0x0,
178 (bus_size_t)port, val, cnt);
183 * The spinlock implementation in Windows differs from that of FreeBSD.
184 * The basic operation of spinlocks involves two steps: 1) spin in a
185 * tight loop while trying to acquire a lock, 2) after obtaining the
186 * lock, disable preemption. (Note that on uniprocessor systems, you're
187 * allowed to skip the first step and just lock out pre-emption, since
188 * it's not possible for you to be in contention with another running
189 * thread.) Later, you release the lock then re-enable preemption.
190 * The difference between Windows and FreeBSD lies in how preemption
191 * is disabled. In FreeBSD, it's done using critical_enter(), which on
192 * the x86 arch translates to a cli instruction. This masks off all
193 * interrupts, and effectively stops the scheduler from ever running
194 * so _nothing_ can execute except the current thread. In Windows,
195 * preemption is disabled by raising the processor IRQL to DISPATCH_LEVEL.
196 * This stops other threads from running, but does _not_ block device
197 * interrupts. This means ISRs can still run, and they can make other
198 * threads runable, but those other threads won't be able to execute
199 * until the current thread lowers the IRQL to something less than
202 * In FreeBSD, ISRs run in interrupt threads, so to duplicate the
203 * Windows notion of IRQLs, we use the following rules:
205 * PASSIVE_LEVEL == normal kernel thread priority
206 * DISPATCH_LEVEL == lowest interrupt thread priotity (PI_SOFT)
207 * DEVICE_LEVEL == highest interrupt thread priority (PI_REALTIME)
208 * HIGH_LEVEL == interrupts disabled (critical_enter())
210 * Be aware that, at least on the x86 arch, the Windows spinlock
211 * functions are divided up in peculiar ways. The actual spinlock
212 * functions are KfAcquireSpinLock() and KfReleaseSpinLock(), and
213 * they live in HAL.dll. Meanwhile, KeInitializeSpinLock(),
214 * KefAcquireSpinLockAtDpcLevel() and KefReleaseSpinLockFromDpcLevel()
215 * live in ntoskrnl.exe. Most Windows source code will call
216 * KeAcquireSpinLock() and KeReleaseSpinLock(), but these are just
217 * macros that call KfAcquireSpinLock() and KfReleaseSpinLock().
218 * KefAcquireSpinLockAtDpcLevel() and KefReleaseSpinLockFromDpcLevel()
219 * perform the lock aquisition/release functions without doing the
220 * IRQL manipulation, and are used when one is already running at
221 * DISPATCH_LEVEL. Make sense? Good.
223 * According to the Microsoft documentation, any thread that calls
224 * KeAcquireSpinLock() must be running at IRQL <= DISPATCH_LEVEL. If
225 * we detect someone trying to acquire a spinlock from DEVICE_LEVEL
226 * or HIGH_LEVEL, we panic.
229 __stdcall __regcall uint8_t
230 hal_lock(REGARGS1(kspin_lock *lock))
234 /* I am so going to hell for this. */
235 if (hal_irql() > DISPATCH_LEVEL)
236 panic("IRQL_NOT_LESS_THAN_OR_EQUAL");
238 oldirql = FASTCALL1(hal_raise_irql, DISPATCH_LEVEL);
239 FASTCALL1(ntoskrnl_lock_dpc, lock);
244 __stdcall __regcall void
245 hal_unlock(REGARGS2(kspin_lock *lock, uint8_t newirql))
247 FASTCALL1(ntoskrnl_unlock_dpc, lock);
248 FASTCALL1(hal_lower_irql, newirql);
256 if (AT_DISPATCH_LEVEL(curthread))
257 return(DISPATCH_LEVEL);
258 return(PASSIVE_LEVEL);
261 __stdcall static uint64_t
262 hal_perfcount(uint64_t *freq)
267 return((uint64_t)ticks);
270 __stdcall __regcall uint8_t
271 hal_raise_irql(REGARGS1(uint8_t irql))
275 if (irql < hal_irql())
276 panic("IRQL_NOT_LESS_THAN");
278 if (hal_irql() == DISPATCH_LEVEL)
279 return(DISPATCH_LEVEL);
281 oldirql = lwkt_getpri_self();
282 lwkt_setpri_self(TDPRI_INT_HIGH);
287 __stdcall __regcall void
288 hal_lower_irql(REGARGS1(uint8_t oldirql))
290 if (oldirql == DISPATCH_LEVEL)
293 if (hal_irql() != DISPATCH_LEVEL)
294 panic("IRQL_NOT_GREATER_THAN");
296 lwkt_setpri_self(oldirql);
300 static void dummy(void)
302 printf ("hal dummy called...\n");
306 image_patch_table hal_functbl[] = {
307 { "KeStallExecutionProcessor", (FUNC)hal_stall_exec_cpu },
308 { "WRITE_PORT_ULONG", (FUNC)hal_writeport_ulong },
309 { "WRITE_PORT_USHORT", (FUNC)hal_writeport_ushort },
310 { "WRITE_PORT_UCHAR", (FUNC)hal_writeport_uchar },
311 { "WRITE_PORT_BUFFER_ULONG", (FUNC)hal_writeport_buf_ulong },
312 { "WRITE_PORT_BUFFER_USHORT", (FUNC)hal_writeport_buf_ushort },
313 { "WRITE_PORT_BUFFER_UCHAR", (FUNC)hal_writeport_buf_uchar },
314 { "READ_PORT_ULONG", (FUNC)hal_readport_ulong },
315 { "READ_PORT_USHORT", (FUNC)hal_readport_ushort },
316 { "READ_PORT_UCHAR", (FUNC)hal_readport_uchar },
317 { "READ_PORT_BUFFER_ULONG", (FUNC)hal_readport_buf_ulong },
318 { "READ_PORT_BUFFER_USHORT", (FUNC)hal_readport_buf_ushort },
319 { "READ_PORT_BUFFER_UCHAR", (FUNC)hal_readport_buf_uchar },
320 { "KfAcquireSpinLock", (FUNC)hal_lock },
321 { "KfReleaseSpinLock", (FUNC)hal_unlock },
322 { "KeGetCurrentIrql", (FUNC)hal_irql },
323 { "KeQueryPerformanceCounter", (FUNC)hal_perfcount },
324 { "KfLowerIrql", (FUNC)hal_lower_irql },
325 { "KfRaiseIrql", (FUNC)hal_raise_irql },
328 * This last entry is a catch-all for any function we haven't
329 * implemented yet. The PE import list patching routine will
330 * use it for any function that doesn't have an explicit match
334 { NULL, (FUNC)dummy },