]> git.saurik.com Git - apple/xnu.git/blob - osfmk/i386/AT386/himem.c
xnu-792.17.14.tar.gz
[apple/xnu.git] / osfmk / i386 / AT386 / himem.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /*
29 * @OSF_FREE_COPYRIGHT@
30 */
31 /*
32 * HISTORY
33 *
34 * Revision 1.1.1.1 1998/09/22 21:05:38 wsanchez
35 * Import of Mac OS X kernel (~semeria)
36 *
37 * Revision 1.1.1.1 1998/03/07 02:25:39 wsanchez
38 * Import of OSF Mach kernel (~mburg)
39 *
40 * Revision 1.3.17.7 1995/08/21 20:33:13 devrcs
41 * ri-osc CR1547: Fix himem buffer translation to cope with non
42 * page-aligned addresses.
43 * [1995/08/08 16:51:58 bolinger]
44 *
45 * Revision 1.3.17.6 1995/02/24 15:51:12 alanl
46 * DIPC: Merge from nmk17b2 to nmk18b8.
47 * Notes: lock package cleanup.
48 * [95/01/23 alanl]
49 * [95/02/24 alanl]
50 *
51 * Revision 1.3.17.5 1995/01/26 22:14:52 ezf
52 * removed extraneous CMU CR
53 * [1995/01/26 20:24:45 ezf]
54 *
55 * Revision 1.3.17.4 1995/01/10 04:51:04 devrcs
56 * mk6 CR801 - merge up from nmk18b4 to nmk18b7
57 * * Rev 1.3.17.3 1994/10/21 18:41:39 joe
58 * Added ETAP support
59 * [1994/12/09 20:37:48 dwm]
60 *
61 * mk6 CR764 - s/spinlock/simple_lock/ (name change only)
62 * [1994/11/10 05:25:33 dwm]
63 *
64 * mk6 CR668 - 1.3b26 merge
65 * * Revision 1.3.5.8 1994/05/06 18:44:06 tmt
66 * Fix prototypes for new device signatures.
67 * * Revision 1.3.5.6 1993/12/10 18:08:15 jeffc
68 * CR10305 -- locking bug in himem_reserve(): change call to
69 * vm_page_free to VM_PAGE_FREE.
70 * * Revision 1.3.5.5 1993/11/19 17:56:58 jeffc
71 * CR10125 -- Uninitialized lock in himem_convert. Add himem_init
72 * CR9461 -- Locking bug in himem_convert - must retake lock after
73 * thread_sleep.
74 * * End1.3merge
75 * [1994/11/04 09:07:39 dwm]
76 *
77 * Revision 1.3.17.1 1994/06/14 03:04:20 toshi
78 * Merge MK6 and NMK17
79 * [1994/06/14 01:06:55 toshi]
80 *
81 * Revision 1.3.15.2 1994/06/08 21:14:24 dswartz
82 * Preemption merge.
83 * [1994/06/08 21:12:29 dswartz]
84 *
85 * Revision 1.3.15.1 1994/05/19 20:30:23 dwm
86 * mk6 CR 74. Locking bug in himem_reserve(): use VM_PAGE_FREE.
87 * mk6 CR 9461. Init hil_lock used by himem_convert();
88 * retake lock after sleeping.
89 * [1994/05/19 20:30:07 dwm]
90 *
91 * Revision 1.3.11.1 1994/02/09 07:27:07 bernadat
92 * Added himem_init() for module initialization.
93 * [93/08/12 paire]
94 *
95 * Take back hil_lock lock on return from thread_sleep()
96 * [93/07/16 bernadat]
97 *
98 * Add vm_page_gobble() calls where needed. (dwm bug #542)
99 * Change from NORMA_MK14.6 [1993/02/09 22:24:00 dwm]
100 * [93/07/16 bernadat]
101 * [94/02/08 bernadat]
102 *
103 * Revision 1.3.5.4 1993/08/09 19:37:19 dswartz
104 * Add ANSI prototypes - CR#9523
105 * [1993/08/06 17:50:02 dswartz]
106 *
107 * Revision 1.3.5.3 1993/08/03 22:21:26 bernard
108 * CR#9523 - ANSI prototype fixes.
109 * [1993/08/03 15:34:10 bernard]
110 *
111 * Revision 1.3.5.2 1993/06/09 02:25:18 gm
112 * CR9157 - Find himem.h in the right place.
113 * [1993/05/28 17:27:23 brezak]
114 *
115 * Revision 1.3 1993/04/19 16:09:46 devrcs
116 * make endif tags ansi compliant/include files
117 * [1993/02/20 21:46:44 david]
118 *
119 * Print an appropriate message when going out of HIMEM pages.
120 * [93/01/26 bernadat]
121 *
122 * Revision 1.2 1992/11/25 01:07:08 robert
123 * integrate changes below for norma_14
124 * [1992/11/13 19:28:44 robert]
125 *
126 * $EndLog$
127 */
128
129 /*
130 * support of memory above 16 Megs for DMA limited to memory
131 * below 16 Megs. Copies high memory lo low memory before DMA
132 * write operations and does the reverse at completion time for
133 * DMA read operations
134 */
135
136 #include <cpus.h>
137 #include <platforms.h>
138 #include <kern/lock.h>
139 #include <mach/vm_param.h>
140 #include <vm/vm_page.h>
141 #include <i386/AT386/himem.h>
142 #include <kern/kalloc.h>
143 #include <kern/spl.h>
144 #include <mach/boolean.h>
145 #include <kern/misc_protos.h>
146 #include <i386/AT386/misc_protos.h>
147 #include <i386/pmap.h>
148
149 hil_t hil_head;
150 decl_simple_lock_data(,hil_lock)
151
152 #if HIMEM_STATS
153 int himem_request; /* number of requests */
154 int himem_used; /* number of times used */
155 #endif /* HIMEM_STATS */
156
157 void
158 himem_init(
159 void)
160 {
161 simple_lock_init(&hil_lock, 0);
162 }
163
164 /*
165 * Called by drivers, this indicates himem that this driver might need
166 * to allocate as many as npages pages in a single I/O DMA transfer
167 */
168
169 void
170 himem_reserve(
171 int npages)
172 {
173 register i = 0;
174 vm_page_t free_head = VM_PAGE_NULL;
175 vm_page_t low;
176 hil_t hil;
177 spl_t ipl;
178 extern pmap_paddr_t_t avail_end;
179
180 if (avail_end <= HIGH_MEM)
181 return;
182 kprintf("looking for low mem pages\n");
183 hil = (hil_t)kalloc(npages*sizeof(struct himem_link));
184 if (hil == (hil_t)0)
185 panic("himem_reserve: kalloc failed\n");
186
187 for (i=0; i < npages-1; i++)
188 (hil+i)->next = hil+i+1;
189
190 /*
191 * This is the only way of getting low physical pages
192 * wtithout changing VM internals
193 */
194 for (i=0; i != npages;) {
195 if ((low = vm_page_grab()) == VM_PAGE_NULL)
196 panic("No low memory pages for himem\n");
197 vm_page_gobble(low); /* mark as consumed internally */
198 if (_high_mem_page(low->phys_addr)) {
199 low->pageq.next = (queue_entry_t)free_head;
200 free_head = low;
201 } else {
202 (hil+i)->low_page = low->phys_addr;
203 i++;
204 }
205 }
206 kprintf("freeing high pages back\n");
207 for (low = free_head; low; low = free_head) {
208 free_head = (vm_page_t) low->pageq.next;
209 VM_PAGE_FREE(low);
210 }
211
212 ipl = splhi();
213 simple_lock(&hil_lock);
214 (hil+npages-1)->next = hil_head;
215 hil_head = hil;
216 simple_unlock(&hil_lock);
217 splx(ipl);
218 }
219
220 /*
221 * Called by driver at DMA initialization time. Converts a high memory
222 * physical page to a low memory one. If operation is a write,
223 * [phys_addr, phys_addr+length-1] is copied to new page. Caller must
224 * provide a pointer to a pointer to a himem_list. This is used to store
225 * all the conversions and is use at completion time to revert the pages.
226 * This pointer must point to a null hil_t value for the call on the first
227 * page of a DMA transfer.
228 */
229
230 vm_offset_t
231 himem_convert(
232 vm_offset_t phys_addr,
233 vm_size_t length,
234 int io_op,
235 hil_t *hil)
236 {
237 hil_t h;
238 spl_t ipl;
239 vm_offset_t offset = phys_addr & (I386_PGBYTES - 1);
240
241 assert (offset + length <= I386_PGBYTES);
242
243 ipl = splhi();
244 simple_lock(&hil_lock);
245 while (!(h = hil_head)) {
246 printf("WARNING: out of HIMEM pages\n");
247 thread_sleep_simple_lock((event_t)&hil_head,
248 simple_lock_addr(hil_lock),
249 THREAD_UNINT);
250 /* hil_lock relocked */
251 }
252 hil_head = hil_head->next;
253 simple_unlock(&hil_lock);
254 splx(ipl);
255
256 h->high_addr = phys_addr;
257
258 if (io_op == D_WRITE) {
259 bcopy_phys((addr64_t)phys_addr, (addr64_t)(h->low_page + offset),
260 length);
261 h->length = 0;
262 } else {
263 h->length = length;
264 }
265 h->offset = offset;
266
267 assert(!*hil || (*hil)->high_addr);
268
269 h->next = *hil;
270 *hil = h;
271 return(h->low_page + offset);
272 }
273
274 /*
275 * Called by driver at DMA completion time. Converts a list of low memory
276 * physical page to the original high memory one. If operation was read,
277 * [phys_addr, phys_addr+lenght-1] is copied to original page
278 */
279
280 void
281 himem_revert(
282 hil_t hil)
283 {
284 hil_t next;
285 boolean_t wakeup = FALSE;
286 spl_t ipl;
287
288 while(hil) {
289 if (hil->length) {
290 bcopy_phys((addr64_t)hil->low_page + hil->offset),
291 (addr64_t)(hil->high_addr),
292 hil->length);
293 }
294 hil->high_addr = 0;
295 hil->length = 0;
296 hil->offset = 0;
297 next = hil->next;
298 ipl = splhi();
299 simple_lock(&hil_lock);
300 if (!(hil->next = hil_head))
301 wakeup = TRUE;
302 hil_head = hil;
303 simple_unlock(&hil_lock);
304 splx(ipl);
305 hil = next;
306 }
307 if (wakeup)
308 thread_wakeup((event_t)&hil_head);
309 }