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