Source to osfmk/i386/AT386/himem.c
/*
* Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.1 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
*
* This Original Code and all software distributed under the License are
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* @OSF_FREE_COPYRIGHT@
*/
/*
* HISTORY
*
* Revision 1.1.1.1 1998/09/22 21:05:38 wsanchez
* Import of Mac OS X kernel (~semeria)
*
* Revision 1.1.1.1 1998/03/07 02:25:39 wsanchez
* Import of OSF Mach kernel (~mburg)
*
* Revision 1.3.17.7 1995/08/21 20:33:13 devrcs
* ri-osc CR1547: Fix himem buffer translation to cope with non
* page-aligned addresses.
* [1995/08/08 16:51:58 bolinger]
*
* Revision 1.3.17.6 1995/02/24 15:51:12 alanl
* DIPC: Merge from nmk17b2 to nmk18b8.
* Notes: lock package cleanup.
* [95/01/23 alanl]
* [95/02/24 alanl]
*
* Revision 1.3.17.5 1995/01/26 22:14:52 ezf
* removed extraneous CMU CR
* [1995/01/26 20:24:45 ezf]
*
* Revision 1.3.17.4 1995/01/10 04:51:04 devrcs
* mk6 CR801 - merge up from nmk18b4 to nmk18b7
* * Rev 1.3.17.3 1994/10/21 18:41:39 joe
* Added ETAP support
* [1994/12/09 20:37:48 dwm]
*
* mk6 CR764 - s/spinlock/simple_lock/ (name change only)
* [1994/11/10 05:25:33 dwm]
*
* mk6 CR668 - 1.3b26 merge
* * Revision 1.3.5.8 1994/05/06 18:44:06 tmt
* Fix prototypes for new device signatures.
* * Revision 1.3.5.6 1993/12/10 18:08:15 jeffc
* CR10305 -- locking bug in himem_reserve(): change call to
* vm_page_free to VM_PAGE_FREE.
* * Revision 1.3.5.5 1993/11/19 17:56:58 jeffc
* CR10125 -- Uninitialized lock in himem_convert. Add himem_init
* CR9461 -- Locking bug in himem_convert - must retake lock after
* thread_sleep.
* * End1.3merge
* [1994/11/04 09:07:39 dwm]
*
* Revision 1.3.17.1 1994/06/14 03:04:20 toshi
* Merge MK6 and NMK17
* [1994/06/14 01:06:55 toshi]
*
* Revision 1.3.15.2 1994/06/08 21:14:24 dswartz
* Preemption merge.
* [1994/06/08 21:12:29 dswartz]
*
* Revision 1.3.15.1 1994/05/19 20:30:23 dwm
* mk6 CR 74. Locking bug in himem_reserve(): use VM_PAGE_FREE.
* mk6 CR 9461. Init hil_lock used by himem_convert();
* retake lock after sleeping.
* [1994/05/19 20:30:07 dwm]
*
* Revision 1.3.11.1 1994/02/09 07:27:07 bernadat
* Added himem_init() for module initialization.
* [93/08/12 paire]
*
* Take back hil_lock lock on return from thread_sleep()
* [93/07/16 bernadat]
*
* Add vm_page_gobble() calls where needed. (dwm bug #542)
* Change from NORMA_MK14.6 [1993/02/09 22:24:00 dwm]
* [93/07/16 bernadat]
* [94/02/08 bernadat]
*
* Revision 1.3.5.4 1993/08/09 19:37:19 dswartz
* Add ANSI prototypes - CR#9523
* [1993/08/06 17:50:02 dswartz]
*
* Revision 1.3.5.3 1993/08/03 22:21:26 bernard
* CR#9523 - ANSI prototype fixes.
* [1993/08/03 15:34:10 bernard]
*
* Revision 1.3.5.2 1993/06/09 02:25:18 gm
* CR9157 - Find himem.h in the right place.
* [1993/05/28 17:27:23 brezak]
*
* Revision 1.3 1993/04/19 16:09:46 devrcs
* make endif tags ansi compliant/include files
* [1993/02/20 21:46:44 david]
*
* Print an appropriate message when going out of HIMEM pages.
* [93/01/26 bernadat]
*
* Revision 1.2 1992/11/25 01:07:08 robert
* integrate changes below for norma_14
* [1992/11/13 19:28:44 robert]
*
* $EndLog$
*/
/*
* support of memory above 16 Megs for DMA limited to memory
* below 16 Megs. Copies high memory lo low memory before DMA
* write operations and does the reverse at completion time for
* DMA read operations
*/
#include <cpus.h>
#include <platforms.h>
#include <kern/lock.h>
#include <mach/vm_param.h>
#include <vm/vm_page.h>
#include <i386/AT386/himem.h>
#include <kern/kalloc.h>
#include <kern/spl.h>
#include <mach/boolean.h>
#include <kern/misc_protos.h>
#include <i386/AT386/misc_protos.h>
hil_t hil_head;
decl_simple_lock_data(,hil_lock)
#if HIMEM_STATS
int himem_request; /* number of requests */
int himem_used; /* number of times used */
#endif /* HIMEM_STATS */
void
himem_init(
void)
{
simple_lock_init(&hil_lock, ETAP_VM_HIMEM);
}
/*
* Called by drivers, this indicates himem that this driver might need
* to allocate as many as npages pages in a single I/O DMA transfer
*/
void
himem_reserve(
int npages)
{
register i = 0;
vm_page_t free_head = VM_PAGE_NULL;
vm_page_t low;
hil_t hil;
spl_t ipl;
extern vm_offset_t avail_end;
if (avail_end <= HIGH_MEM)
return;
hil = (hil_t)kalloc(npages*sizeof(struct himem_link));
if (hil == (hil_t)0)
panic("himem_reserve: kalloc failed\n");
for (i=0; i < npages-1; i++)
(hil+i)->next = hil+i+1;
/*
* This is the only way of getting low physical pages
* wtithout changing VM internals
*/
for (i=0; i != npages;) {
if ((low = vm_page_grab()) == VM_PAGE_NULL)
panic("No low memory pages for himem\n");
vm_page_gobble(low); /* mark as consumed internally */
if (_high_mem_page(low->phys_addr)) {
low->pageq.next = (queue_entry_t)free_head;
free_head = low;
} else {
(hil+i)->low_page = low->phys_addr;
i++;
}
}
for (low = free_head; low; low = free_head) {
free_head = (vm_page_t) low->pageq.next;
VM_PAGE_FREE(low);
}
ipl = splhi();
simple_lock(&hil_lock);
(hil+npages-1)->next = hil_head;
hil_head = hil;
simple_unlock(&hil_lock);
splx(ipl);
}
/*
* Called by driver at DMA initialization time. Converts a high memory
* physical page to a low memory one. If operation is a write,
* [phys_addr, phys_addr+length-1] is copied to new page. Caller must
* provide a pointer to a pointer to a himem_list. This is used to store
* all the conversions and is use at completion time to revert the pages.
* This pointer must point to a null hil_t value for the call on the first
* page of a DMA transfer.
*/
vm_offset_t
himem_convert(
vm_offset_t phys_addr,
vm_size_t length,
int io_op,
hil_t *hil)
{
hil_t h;
spl_t ipl;
vm_offset_t offset = phys_addr & (I386_PGBYTES - 1);
assert (offset + length <= I386_PGBYTES);
ipl = splhi();
simple_lock(&hil_lock);
while (!(h = hil_head)) {
printf("WARNING: out of HIMEM pages\n");
thread_sleep_simple_lock((event_t)&hil_head,
simple_lock_addr(hil_lock), FALSE);
simple_lock (&hil_lock);
}
hil_head = hil_head->next;
simple_unlock(&hil_lock);
splx(ipl);
h->high_addr = phys_addr;
if (io_op == D_WRITE) {
bcopy((char *)phystokv(phys_addr), (char *)phystokv(h->low_page + offset),
length);
h->length = 0;
} else {
h->length = length;
}
h->offset = offset;
assert(!*hil || (*hil)->high_addr);
h->next = *hil;
*hil = h;
return(h->low_page + offset);
}
/*
* Called by driver at DMA completion time. Converts a list of low memory
* physical page to the original high memory one. If operation was read,
* [phys_addr, phys_addr+lenght-1] is copied to original page
*/
void
himem_revert(
hil_t hil)
{
hil_t next;
boolean_t wakeup = FALSE;
spl_t ipl;
while(hil) {
if (hil->length) {
bcopy((char *)phystokv(hil->low_page + hil->offset),
(char *)phystokv(hil->high_addr),
hil->length);
}
hil->high_addr = 0;
hil->length = 0;
hil->offset = 0;
next = hil->next;
ipl = splhi();
simple_lock(&hil_lock);
if (!(hil->next = hil_head))
wakeup = TRUE;
hil_head = hil;
simple_unlock(&hil_lock);
splx(ipl);
hil = next;
}
if (wakeup)
thread_wakeup((event_t)&hil_head);
}