mirror of https://github.com/red-prig/fpPS4.git
2860 lines
70 KiB
Plaintext
2860 lines
70 KiB
Plaintext
unit vm_map;
|
|
|
|
{$mode ObjFPC}{$H+}
|
|
{$CALLING SysV_ABI_CDecl}
|
|
|
|
interface
|
|
|
|
uses
|
|
vm,
|
|
vmparam,
|
|
vm_pmap,
|
|
vm_object,
|
|
kern_mtx,
|
|
kern_thr,
|
|
_resource,
|
|
kern_resource;
|
|
|
|
type
|
|
vm_flags_t =type Byte;
|
|
vm_eflags_t=type Integer;
|
|
|
|
vm_map_object=vm_object_t;
|
|
|
|
p_vm_map_entry_t=^vm_map_entry_t;
|
|
vm_map_entry_t=^vm_map_entry;
|
|
vm_map_entry=packed record
|
|
prev :vm_map_entry_t; // previous entry
|
|
next :vm_map_entry_t; // next entry
|
|
left :vm_map_entry_t; // left child in binary search tree
|
|
right :vm_map_entry_t; // right child in binary search tree
|
|
start :vm_offset_t; // start address
|
|
__end :vm_offset_t; // end address
|
|
avail_ssize :vm_offset_t; // amt can grow if this is a stack
|
|
adj_free :vm_offset_t; // amount of adjacent free space
|
|
max_free :vm_offset_t; // max free space in subtree
|
|
vm_obj :vm_map_object; // object I point to
|
|
offset :vm_ooffset_t; // offset into object
|
|
eflags :vm_eflags_t; // map entry flags
|
|
protection :vm_prot_t; // protection code
|
|
max_protection:vm_prot_t; // maximum protection
|
|
inheritance :vm_inherit_t; // inheritance
|
|
name :array[0..31] of Char;
|
|
end;
|
|
|
|
p_vm_map_t=^vm_map_t;
|
|
vm_map_t=^_vm_map;
|
|
_vm_map=packed object
|
|
header:vm_map_entry; // List of entries
|
|
lock:mtx; // Lock for map data
|
|
nentries:Integer; // Number of entries
|
|
size:vm_size_t; // virtual size
|
|
timestamp:DWORD; // Version number
|
|
flags:vm_flags_t; // flags for this vm_map
|
|
root:vm_map_entry_t; // Root of a binary search tree
|
|
pmap:pmap_t; // (c) Physical map
|
|
busy:Integer;
|
|
property min_offset:vm_offset_t read header.start write header.start;
|
|
property max_offset:vm_offset_t read header.__end write header.__end;
|
|
end;
|
|
|
|
p_vmspace=^vmspace;
|
|
vmspace=packed record
|
|
vm_map :_vm_map; // VM address map
|
|
//
|
|
sv_usrstack :caddr_t; // USRSTACK
|
|
sv_psstrings:caddr_t; // PS_STRINGS
|
|
ps_strings :Pointer;
|
|
//
|
|
vm_swrss :segsz_t; // resident set size before last swap
|
|
vm_tsize :segsz_t; // text size (pages) XXX
|
|
vm_dsize :segsz_t; // data size (pages) XXX
|
|
vm_ssize :segsz_t; // stack size (pages)
|
|
vm_taddr :caddr_t; // (c) user virtual address of text
|
|
vm_daddr :caddr_t; // (c) user virtual address of data
|
|
vm_maxsaddr :caddr_t; // user VA at max stack growth
|
|
//
|
|
vm_pmap :_pmap; // private physical map
|
|
end;
|
|
|
|
const
|
|
MAP_ENTRY_NOSYNC =$0001;
|
|
MAP_ENTRY_IS_SUB_MAP=$0002;
|
|
MAP_ENTRY_COW =$0004;
|
|
MAP_ENTRY_NEEDS_COPY=$0008;
|
|
MAP_ENTRY_NOFAULT =$0010;
|
|
//MAP_ENTRY_USER_WIRED=$0020;
|
|
|
|
MAP_ENTRY_BEHAV_NORMAL =$0000; // default behavior
|
|
MAP_ENTRY_BEHAV_SEQUENTIAL=$0040; // expect sequential access
|
|
MAP_ENTRY_BEHAV_RANDOM =$0080; // expect random access
|
|
MAP_ENTRY_BEHAV_RESERVED =$00C0; // future use
|
|
|
|
MAP_ENTRY_BEHAV_MASK=$00C0;
|
|
|
|
//MAP_ENTRY_IN_TRANSITION=$0100; // entry being changed
|
|
//MAP_ENTRY_NEEDS_WAKEUP =$0200; // waiters in transition
|
|
MAP_ENTRY_NOCOREDUMP =$0400; // don't include in a core
|
|
|
|
MAP_ENTRY_GROWS_DOWN=$1000; // Top-down stacks
|
|
MAP_ENTRY_GROWS_UP =$2000; // Bottom-up stacks
|
|
|
|
MAP_ENTRY_WIRE_SKIPPED=$4000;
|
|
MAP_ENTRY_VN_WRITECNT =$8000; // writeable vnode mapping
|
|
|
|
//vm_flags_t values
|
|
//MAP_WIREFUTURE =$01; // wire all future pages
|
|
//MAP_BUSY_WAKEUP=$02;
|
|
|
|
//Copy-on-write flags for vm_map operations
|
|
MAP_INHERIT_SHARE =$0001;
|
|
MAP_COPY_ON_WRITE =$0002;
|
|
MAP_NOFAULT =$0004;
|
|
MAP_PREFAULT =$0008;
|
|
MAP_PREFAULT_PARTIAL=$0010;
|
|
MAP_DISABLE_SYNCER =$0020;
|
|
MAP_DISABLE_COREDUMP=$0100;
|
|
MAP_PREFAULT_MADVISE=$0200; // from (user) madvise request
|
|
MAP_VN_WRITECOUNT =$0400;
|
|
MAP_STACK_GROWS_DOWN=$1000;
|
|
MAP_STACK_GROWS_UP =$2000;
|
|
MAP_ACC_CHARGED =$4000;
|
|
MAP_ACC_NO_CHARGE =$8000;
|
|
|
|
//vm_fault option flags
|
|
VM_FAULT_NORMAL =0; // Nothing special
|
|
VM_FAULT_CHANGE_WIRING=1; // Change the wiring as appropriate
|
|
VM_FAULT_DIRTY =2; // Dirty the page; use w/VM_PROT_COPY
|
|
|
|
VMFS_NO_SPACE =0; // don't find; use the given range
|
|
VMFS_ANY_SPACE =1; // find a range with any alignment
|
|
VMFS_SUPER_SPACE =2; // find a superpage-aligned range
|
|
VMFS_OPTIMAL_SPACE=4; // find a range with optimal alignment
|
|
|
|
//vm_map_wire and vm_map_unwire option flags
|
|
//VM_MAP_WIRE_SYSTEM =0; // wiring in a kernel map
|
|
//VM_MAP_WIRE_USER =1; // wiring in a user map
|
|
|
|
//VM_MAP_WIRE_NOHOLES=0; // region must not have holes
|
|
//VM_MAP_WIRE_HOLESOK=2; // region may have holes
|
|
|
|
//VM_MAP_WIRE_WRITE =4; // Validate writable.
|
|
|
|
VM_FAULT_READ_AHEAD_MIN = 7;
|
|
VM_FAULT_READ_AHEAD_INIT=15;
|
|
VM_FAULT_READ_AHEAD_MAX = 7;
|
|
|
|
function VMFS_ALIGNED_SPACE(x:QWORD):QWORD; inline; // find a range with fixed alignment
|
|
|
|
function vm_map_entry_behavior(entry:vm_map_entry_t):Integer;
|
|
function vm_map_max(map:vm_map_t):vm_offset_t;
|
|
function vm_map_min(map:vm_map_t):vm_offset_t;
|
|
function vm_map_pmap(map:vm_map_t):pmap_t;
|
|
procedure vm_map_modflags(map:vm_map_t;_set,clear:vm_flags_t);
|
|
|
|
var
|
|
g_vmspace:vmspace;
|
|
|
|
function vm_map_lookup_entry(
|
|
map :vm_map_t;
|
|
address :vm_offset_t;
|
|
entry :p_vm_map_entry_t):Boolean;
|
|
|
|
function vm_map_insert(
|
|
map :vm_map_t;
|
|
vm_obj :vm_object_t;
|
|
offset :vm_ooffset_t;
|
|
start :vm_offset_t;
|
|
__end :vm_offset_t;
|
|
prot :vm_prot_t;
|
|
max :vm_prot_t;
|
|
cow :Integer):Integer;
|
|
|
|
procedure vm_map_lookup_done(map:vm_map_t;entry:vm_map_entry_t);
|
|
|
|
function vm_map_lookup(var_map :p_vm_map_t; { IN/OUT }
|
|
vaddr :vm_offset_t;
|
|
fault_typea:vm_prot_t;
|
|
out_entry :p_vm_map_entry_t; { OUT }
|
|
vm_obj :p_vm_object_t; { OUT }
|
|
pindex :p_vm_pindex_t; { OUT }
|
|
out_prot :p_vm_prot_t { OUT }
|
|
):Integer;
|
|
|
|
function vm_map_lookup_locked(var_map :p_vm_map_t; { IN/OUT }
|
|
vaddr :vm_offset_t;
|
|
fault_typea:vm_prot_t;
|
|
out_entry :p_vm_map_entry_t; { OUT }
|
|
vm_obj :p_vm_object_t; { OUT }
|
|
pindex :p_vm_pindex_t; { OUT }
|
|
out_prot :p_vm_prot_t; { OUT }
|
|
wired :PBoolean { OUT }
|
|
):Integer;
|
|
|
|
function vm_map_protect(map :vm_map_t;
|
|
start :vm_offset_t;
|
|
__end :vm_offset_t;
|
|
new_prot:vm_prot_t;
|
|
set_max :Boolean):Integer;
|
|
|
|
function vm_map_madvise(map :vm_map_t;
|
|
start :vm_offset_t;
|
|
__end :vm_offset_t;
|
|
behav :Integer):Integer;
|
|
|
|
function vm_map_find(map :vm_map_t;
|
|
vm_obj :vm_object_t;
|
|
offset :vm_ooffset_t;
|
|
addr :p_vm_offset_t;
|
|
length :vm_size_t;
|
|
find_space:Integer;
|
|
prot :vm_prot_t;
|
|
max :vm_prot_t;
|
|
cow :Integer):Integer;
|
|
|
|
procedure vm_map_simplify_entry(map:vm_map_t;entry:vm_map_entry_t);
|
|
|
|
function vm_map_fixed(map :vm_map_t;
|
|
vm_obj :vm_object_t;
|
|
offset :vm_ooffset_t;
|
|
start :vm_offset_t;
|
|
length :vm_size_t;
|
|
prot :vm_prot_t;
|
|
max :vm_prot_t;
|
|
cow :Integer;
|
|
overwr :Integer):Integer;
|
|
|
|
function vm_map_stack(map :vm_map_t;
|
|
addrbos :vm_offset_t;
|
|
max_ssize:vm_size_t;
|
|
prot :vm_prot_t;
|
|
max :vm_prot_t;
|
|
cow :Integer):Integer;
|
|
|
|
function vm_map_growstack(addr:vm_offset_t):Integer;
|
|
function vmspace_exec(minuser,maxuser:vm_offset_t):Integer;
|
|
|
|
procedure vm_map_lock(map:vm_map_t);
|
|
function vm_map_trylock(map:vm_map_t):Boolean;
|
|
procedure vm_map_unlock(map:vm_map_t);
|
|
|
|
function vm_map_delete(map:vm_map_t;start:vm_offset_t;__end:vm_offset_t):Integer;
|
|
function vm_map_remove(map:vm_map_t;start:vm_offset_t;__end:vm_offset_t):Integer;
|
|
|
|
procedure vm_map_set_name(map:vm_map_t;start,__end:vm_offset_t;name:PChar);
|
|
procedure vm_map_set_name_locked(map:vm_map_t;start,__end:vm_offset_t;name:PChar);
|
|
|
|
function vmspace_pmap(vm:p_vmspace):pmap_t; inline;
|
|
|
|
procedure vm_map_entry_deallocate(entry:vm_map_entry_t);
|
|
|
|
procedure vminit; //SYSINIT
|
|
|
|
implementation
|
|
|
|
var
|
|
sgrowsiz:QWORD=vmparam.SGROWSIZ;
|
|
stack_guard_page:Integer=0;
|
|
|
|
function OFF_TO_IDX(x:QWORD):DWORD; inline;
|
|
begin
|
|
Result:=QWORD(x) shr PAGE_SHIFT;
|
|
end;
|
|
|
|
function VMFS_ALIGNED_SPACE(x:QWORD):QWORD; inline; // find a range with fixed alignment
|
|
begin
|
|
Result:=x shl 8;
|
|
end;
|
|
|
|
function vm_map_entry_behavior(entry:vm_map_entry_t):Integer; inline;
|
|
begin
|
|
Result:=(entry^.eflags and MAP_ENTRY_BEHAV_MASK);
|
|
end;
|
|
|
|
function vm_map_max(map:vm_map_t):vm_offset_t; inline;
|
|
begin
|
|
Result:=map^.max_offset;
|
|
end;
|
|
|
|
function vm_map_min(map:vm_map_t):vm_offset_t; inline;
|
|
begin
|
|
Result:=map^.min_offset;
|
|
end;
|
|
|
|
function vm_map_pmap(map:vm_map_t):pmap_t; inline;
|
|
begin
|
|
Result:=map^.pmap;
|
|
end;
|
|
|
|
procedure vm_map_modflags(map:vm_map_t;_set,clear:vm_flags_t); inline;
|
|
begin
|
|
map^.flags:=(map^.flags or _set) and (not clear);
|
|
end;
|
|
|
|
{
|
|
* VM_MAP_RANGE_CHECK: [ internal use only ]
|
|
*
|
|
* Asserts that the starting and ending region
|
|
* addresses fall within the valid range of the map.
|
|
}
|
|
procedure VM_MAP_RANGE_CHECK(map:vm_map_t;var start,__end:vm_offset_t);
|
|
begin
|
|
if (start<vm_map_min(map)) then
|
|
begin
|
|
start:=vm_map_min(map);
|
|
end;
|
|
if (__end>vm_map_max(map)) then
|
|
begin
|
|
__end:=vm_map_max(map);
|
|
end;
|
|
if (start>__end) then
|
|
begin
|
|
start:=__end;
|
|
end;
|
|
end;
|
|
|
|
function ENTRY_CHARGED(e:vm_map_entry_t):Boolean; inline;
|
|
begin
|
|
Result:=False;//MAP_ENTRY_NEEDS_COPY
|
|
end;
|
|
|
|
{
|
|
* vm_map_startup:
|
|
*
|
|
* Initialize the vm_map module. Must be called before
|
|
* any other vm_map routines.
|
|
*
|
|
* Map and entry structures are allocated from the general
|
|
* purpose memory pool with some exceptions:
|
|
*
|
|
* - The kernel map and kmem submap are allocated statically.
|
|
* - Kernel map entries are allocated out of a static pool.
|
|
*
|
|
* These restrictions are necessary since malloc() uses the
|
|
* maps and requires map entries.
|
|
}
|
|
procedure vmspace_zinit;
|
|
begin
|
|
FillChar(g_vmspace,SizeOf(vmspace),0);
|
|
end;
|
|
|
|
function vmspace_pmap(vm:p_vmspace):pmap_t; inline;
|
|
begin
|
|
Result:=@vm^.vm_pmap;
|
|
end;
|
|
|
|
procedure vm_map_init(map:vm_map_t;pmap:pmap_t;min,max:vm_offset_t); forward;
|
|
|
|
{
|
|
* Allocate a vmspace structure, including a vm_map and pmap,
|
|
* and initialize those structures. The refcnt is set to 1.
|
|
}
|
|
function vmspace_alloc(min,max:vm_offset_t):p_vmspace;
|
|
var
|
|
vm:p_vmspace;
|
|
begin
|
|
vm:=@g_vmspace;
|
|
|
|
pmap_pinit(vmspace_pmap(vm));
|
|
|
|
vm_map_init(@vm^.vm_map,vmspace_pmap(vm),min,max);
|
|
//vm^.vm_refcnt:=1;
|
|
//vm^.vm_shm:=nil;
|
|
vm^.vm_swrss:=0;
|
|
vm^.vm_tsize:=0;
|
|
vm^.vm_dsize:=0;
|
|
vm^.vm_ssize:=0;
|
|
vm^.vm_taddr:=nil;
|
|
vm^.vm_daddr:=nil;
|
|
vm^.vm_maxsaddr:=nil;
|
|
|
|
Result:=vm;
|
|
end;
|
|
|
|
procedure vm_map_lock(map:vm_map_t);
|
|
begin
|
|
mtx_lock(map^.lock);
|
|
Inc(map^.timestamp);
|
|
end;
|
|
|
|
function vm_map_trylock(map:vm_map_t):Boolean;
|
|
begin
|
|
Result:=mtx_trylock(map^.lock);
|
|
if Result then
|
|
begin
|
|
Inc(map^.timestamp);
|
|
end;
|
|
end;
|
|
|
|
procedure vm_map_process_deferred;
|
|
var
|
|
td:p_kthread;
|
|
entry,next:vm_map_entry_t;
|
|
begin
|
|
td:=curkthread;
|
|
if (td=nil) then Exit;
|
|
entry:=td^.td_map_def_user;
|
|
td^.td_map_def_user:=nil;
|
|
while (entry<>nil) do
|
|
begin
|
|
next:=entry^.next;
|
|
if ((entry^.eflags and MAP_ENTRY_VN_WRITECNT)<>0) then
|
|
begin
|
|
Assert((entry^.eflags and MAP_ENTRY_IS_SUB_MAP)=0,'Submap with writecount');
|
|
end;
|
|
vm_map_entry_deallocate(entry);
|
|
entry:=next;
|
|
end;
|
|
end;
|
|
|
|
procedure vm_map_unlock(map:vm_map_t);
|
|
begin
|
|
mtx_unlock(map^.lock);
|
|
vm_map_process_deferred();
|
|
end;
|
|
|
|
{
|
|
* vm_map_locked:
|
|
*
|
|
* Returns a non-zero value if the caller holds a write (exclusive) lock
|
|
* on the specified map and the value "0" otherwise.
|
|
}
|
|
function vm_map_locked(map:vm_map_t):Boolean; inline;
|
|
begin
|
|
Result:=mtx_owned(map^.lock);
|
|
end;
|
|
|
|
procedure VM_MAP_ASSERT_LOCKED(map:vm_map_t); inline;
|
|
begin
|
|
Assert(vm_map_locked(map));
|
|
end;
|
|
|
|
{
|
|
* vm_map_create:
|
|
*
|
|
* Creates and returns a new empty VM map with
|
|
* the given physical map structure, and having
|
|
* the given lower and upper address bounds.
|
|
}
|
|
function vm_map_create(pmap:pmap_t;min,max:vm_offset_t):vm_map_t;
|
|
begin
|
|
Result:=AllocMem(SizeOf(_vm_map));
|
|
vm_map_init(Result,pmap,min,max);
|
|
end;
|
|
|
|
{
|
|
* Initialize an existing vm_map structure
|
|
* such as that in the vmspace structure.
|
|
}
|
|
procedure _vm_map_init(map:vm_map_t;pmap:pmap_t;min,max:vm_offset_t);
|
|
begin
|
|
map^.header.next:=@map^.header;
|
|
map^.header.prev:=@map^.header;
|
|
map^.pmap:=pmap;
|
|
map^.min_offset:=min;
|
|
map^.max_offset:=max;
|
|
map^.header.adj_free:=(max-min);
|
|
map^.header.max_free:=(max-min);
|
|
map^.flags:=0;
|
|
map^.root:=nil;
|
|
map^.timestamp:=0;
|
|
map^.busy:=0;
|
|
end;
|
|
|
|
procedure vm_map_init(map:vm_map_t;pmap:pmap_t;min,max:vm_offset_t);
|
|
begin
|
|
_vm_map_init(map, pmap, min, max);
|
|
mtx_init(map^.lock,'user map');
|
|
end;
|
|
|
|
{
|
|
* vm_map_entry_dispose: [ internal use only ]
|
|
*
|
|
* Inverse of vm_map_entry_create.
|
|
}
|
|
procedure vm_map_entry_dispose(map:vm_map_t;entry:vm_map_entry_t); inline;
|
|
begin
|
|
FreeMem(entry);
|
|
end;
|
|
|
|
{
|
|
* vm_map_entry_create: [ internal use only ]
|
|
*
|
|
* Allocates a VM map entry for insertion.
|
|
* No entry fields are filled in.
|
|
}
|
|
function vm_map_entry_create(map:vm_map_t):vm_map_entry_t;
|
|
var
|
|
new_entry:vm_map_entry_t;
|
|
begin
|
|
new_entry:=AllocMem(SizeOf(vm_map_entry));
|
|
Assert((new_entry<>nil),'vm_map_entry_create: kernel resources exhausted');
|
|
Result:=new_entry;
|
|
end;
|
|
|
|
{
|
|
* vm_map_entry_set_behavior:
|
|
*
|
|
* Set the expected access behavior, either normal, random, or
|
|
* sequential.
|
|
}
|
|
procedure vm_map_entry_set_behavior(entry:vm_map_entry_t;behavior:Byte); inline;
|
|
begin
|
|
entry^.eflags:=(entry^.eflags and (not MAP_ENTRY_BEHAV_MASK)) or (behavior and MAP_ENTRY_BEHAV_MASK);
|
|
end;
|
|
|
|
{
|
|
* vm_map_entry_set_max_free:
|
|
*
|
|
* Set the max_free field in a vm_map_entry.
|
|
}
|
|
procedure vm_map_entry_set_max_free(entry:vm_map_entry_t);
|
|
begin
|
|
entry^.max_free:=entry^.adj_free;
|
|
if (entry^.left<>nil) then
|
|
if (entry^.left^.max_free>entry^.max_free) then
|
|
begin
|
|
entry^.max_free:=entry^.left^.max_free;
|
|
end;
|
|
if (entry^.right<>nil) then
|
|
if (entry^.right^.max_free>entry^.max_free) then
|
|
begin
|
|
entry^.max_free:=entry^.right^.max_free;
|
|
end;
|
|
end;
|
|
|
|
{
|
|
* vm_map_entry_splay:
|
|
*
|
|
* The Sleator and Tarjan top-down splay algorithm with the
|
|
* following variation. Max_free must be computed bottom-up, so
|
|
* on the downward pass, maintain the left and right spines in
|
|
* reverse order. Then, make a second pass up each side to fix
|
|
* the pointers and compute max_free. The time bound is O(log n)
|
|
* amortized.
|
|
*
|
|
* The new root is the vm_map_entry containing "addr", or else an
|
|
* adjacent entry (lower or higher) if addr is not in the tree.
|
|
*
|
|
* The map must be locked, and leaves it so.
|
|
*
|
|
* Returns: the new root.
|
|
}
|
|
function vm_map_entry_splay(addr:vm_offset_t;root:vm_map_entry_t):vm_map_entry_t;
|
|
var
|
|
llist,rlist:vm_map_entry_t;
|
|
ltree,rtree:vm_map_entry_t;
|
|
y :vm_map_entry_t;
|
|
begin
|
|
{ Special case of empty tree. }
|
|
if (root=nil) then Exit(root);
|
|
|
|
{
|
|
* Pass One: Splay down the tree until we find addr or a nil
|
|
* pointer where addr would go. llist and rlist are the two
|
|
* sides in reverse order (bottom-up), with llist linked by
|
|
* the right pointer and rlist linked by the left pointer in
|
|
* the vm_map_entry. Wait until Pass Two to set max_free on
|
|
* the two spines.
|
|
}
|
|
llist:=nil;
|
|
rlist:=nil;
|
|
repeat
|
|
{ root is never nil in here. }
|
|
if (addr<root^.start) then
|
|
begin
|
|
y:=root^.left;
|
|
if (y=nil) then break;
|
|
if (addr<y^.start) and (y^.left<>nil) then
|
|
begin
|
|
{ Rotate right and put y on rlist. }
|
|
root^.left:=y^.right;
|
|
y^.right:=root;
|
|
vm_map_entry_set_max_free(root);
|
|
root:=y^.left;
|
|
y^.left:=rlist;
|
|
rlist:=y;
|
|
end else
|
|
begin
|
|
{ Put root on rlist. }
|
|
root^.left:=rlist;
|
|
rlist:=root;
|
|
root:=y;
|
|
end;
|
|
end else
|
|
if (addr>=root^.__end) then
|
|
begin
|
|
y:=root^.right;
|
|
if (y=nil) then break;
|
|
if (addr>=y^.__end) and (y^.right<>nil) then
|
|
begin
|
|
{ Rotate left and put y on llist. }
|
|
root^.right:=y^.left;
|
|
y^.left:=root;
|
|
vm_map_entry_set_max_free(root);
|
|
root:=y^.right;
|
|
y^.right:=llist;
|
|
llist:=y;
|
|
end else
|
|
begin
|
|
{ Put root on llist. }
|
|
root^.right:=llist;
|
|
llist:=root;
|
|
root:=y;
|
|
end;
|
|
end else
|
|
begin
|
|
break;
|
|
end;
|
|
until false;
|
|
|
|
{
|
|
* Pass Two: Walk back up the two spines, flip the pointers
|
|
* and set max_free. The subtrees of the root go at the
|
|
* bottom of llist and rlist.
|
|
}
|
|
ltree:=root^.left;
|
|
while (llist<>nil) do
|
|
begin
|
|
y:=llist^.right;
|
|
llist^.right:=ltree;
|
|
vm_map_entry_set_max_free(llist);
|
|
ltree:=llist;
|
|
llist:=y;
|
|
end;
|
|
rtree:=root^.right;
|
|
while (rlist<>nil) do
|
|
begin
|
|
y:=rlist^.left;
|
|
rlist^.left:=rtree;
|
|
vm_map_entry_set_max_free(rlist);
|
|
rtree:=rlist;
|
|
rlist:=y;
|
|
end;
|
|
|
|
{
|
|
* Final assembly: add ltree and rtree as subtrees of root.
|
|
}
|
|
root^.left:=ltree;
|
|
root^.right:=rtree;
|
|
vm_map_entry_set_max_free(root);
|
|
|
|
Result:=(root);
|
|
end;
|
|
|
|
{
|
|
* vm_map_entry_{un,}link:
|
|
*
|
|
* Insert/remove entries from maps.
|
|
}
|
|
procedure vm_map_entry_link(
|
|
map :vm_map_t;
|
|
after_where:vm_map_entry_t;
|
|
entry :vm_map_entry_t);
|
|
begin
|
|
VM_MAP_ASSERT_LOCKED(map);
|
|
|
|
Inc(map^.nentries);
|
|
entry^.prev:=after_where;
|
|
entry^.next:=after_where^.next;
|
|
entry^.next^.prev:=entry;
|
|
after_where^.next:=entry;
|
|
|
|
if (after_where<>@map^.header) then
|
|
begin
|
|
if (after_where<>map^.root) then
|
|
begin
|
|
vm_map_entry_splay(after_where^.start, map^.root);
|
|
end;
|
|
entry^.right:=after_where^.right;
|
|
entry^.left:=after_where;
|
|
after_where^.right:=nil;
|
|
after_where^.adj_free:=entry^.start - after_where^.__end;
|
|
vm_map_entry_set_max_free(after_where);
|
|
end else
|
|
begin
|
|
entry^.right:=map^.root;
|
|
entry^.left:=nil;
|
|
end;
|
|
if (entry^.next=@map^.header) then
|
|
begin
|
|
entry^.adj_free:=map^.max_offset-entry^.__end;
|
|
end else
|
|
begin
|
|
entry^.adj_free:=entry^.next^.start-entry^.__end;
|
|
end;
|
|
vm_map_entry_set_max_free(entry);
|
|
map^.root:=entry;
|
|
end;
|
|
|
|
procedure vm_map_entry_unlink(
|
|
map :vm_map_t;
|
|
entry :vm_map_entry_t);
|
|
var
|
|
next,prev,root:vm_map_entry_t;
|
|
begin
|
|
VM_MAP_ASSERT_LOCKED(map);
|
|
|
|
if (entry<>map^.root) then
|
|
begin
|
|
vm_map_entry_splay(entry^.start, map^.root);
|
|
end;
|
|
if (entry^.left=nil) then
|
|
begin
|
|
root:=entry^.right;
|
|
end else
|
|
begin
|
|
root:=vm_map_entry_splay(entry^.start, entry^.left);
|
|
root^.right:=entry^.right;
|
|
if (root^.next=@map^.header) then
|
|
begin
|
|
root^.adj_free:=map^.max_offset-root^.__end;
|
|
end else
|
|
begin
|
|
root^.adj_free:=entry^.next^.start-root^.__end;
|
|
end;
|
|
vm_map_entry_set_max_free(root);
|
|
end;
|
|
map^.root:=root;
|
|
|
|
prev:=entry^.prev;
|
|
next:=entry^.next;
|
|
next^.prev:=prev;
|
|
prev^.next:=next;
|
|
Dec(map^.nentries);
|
|
end;
|
|
|
|
{
|
|
* vm_map_entry_resize_free:
|
|
*
|
|
* Recompute the amount of free space following a vm_map_entry
|
|
* and propagate that value up the tree. Call this function after
|
|
* resizing a map entry in-place, that is, without a call to
|
|
* vm_map_entry_link() or _unlink().
|
|
*
|
|
* The map must be locked, and leaves it so.
|
|
}
|
|
procedure vm_map_entry_resize_free(map:vm_map_t;entry:vm_map_entry_t);
|
|
begin
|
|
|
|
{
|
|
* Using splay trees without parent pointers, propagating
|
|
* max_free up the tree is done by moving the entry to the
|
|
* root and making the change there.
|
|
}
|
|
if (entry<>map^.root) then
|
|
begin
|
|
map^.root:=vm_map_entry_splay(entry^.start, map^.root);
|
|
end;
|
|
|
|
if (entry^.next=@map^.header) then
|
|
begin
|
|
entry^.adj_free:=map^.max_offset-entry^.__end;
|
|
end else
|
|
begin
|
|
entry^.adj_free:=entry^.next^.start-entry^.__end;
|
|
end;
|
|
vm_map_entry_set_max_free(entry);
|
|
end;
|
|
|
|
{
|
|
* vm_map_lookup_entry: [ internal use only ]
|
|
*
|
|
* Finds the map entry containing (or
|
|
* immediately preceding) the specified address
|
|
* in the given map; the entry is returned
|
|
* in the "entry" parameter. The boolean
|
|
* result indicates whether the address is
|
|
* actually contained in the map.
|
|
}
|
|
function vm_map_lookup_entry(
|
|
map :vm_map_t;
|
|
address :vm_offset_t;
|
|
entry :p_vm_map_entry_t):Boolean;
|
|
var
|
|
cur:vm_map_entry_t;
|
|
begin
|
|
VM_MAP_ASSERT_LOCKED(map);
|
|
|
|
{
|
|
* If the map is empty, then the map entry immediately preceding
|
|
* "address" is the map's header.
|
|
}
|
|
cur:=map^.root;
|
|
if (cur=nil) then
|
|
begin
|
|
entry^:=@map^.header;
|
|
end else
|
|
if (address>=cur^.start) and (cur^.__end>address) then
|
|
begin
|
|
entry^:=cur;
|
|
Exit(TRUE);
|
|
end else
|
|
begin
|
|
{
|
|
* Splay requires a write lock on the map. However, it only
|
|
* restructures the binary search tree; it does not otherwise
|
|
* change the map. Thus, the map's timestamp need not change
|
|
* on a temporary upgrade.
|
|
}
|
|
cur:=vm_map_entry_splay(address,cur);
|
|
map^.root:=cur;
|
|
|
|
{
|
|
* If "address" is contained within a map entry, the new root
|
|
* is that map entry. Otherwise, the new root is a map entry
|
|
* immediately before or after "address".
|
|
}
|
|
if (address>=cur^.start) then
|
|
begin
|
|
entry^:=cur;
|
|
if (cur^.__end>address) then
|
|
begin
|
|
Exit(TRUE);
|
|
end;
|
|
end else
|
|
begin
|
|
entry^:=cur^.prev;
|
|
end;
|
|
end;
|
|
Result:=(FALSE);
|
|
end;
|
|
|
|
{
|
|
* vm_map_insert:
|
|
*
|
|
* Inserts the given whole VM object into the target
|
|
* map at the specified address range. The object's
|
|
* size should match that of the address range.
|
|
*
|
|
* Requires that the map be locked, and leaves it so.
|
|
*
|
|
* If object is non-nil, ref count must be bumped by caller
|
|
* prior to making call to account for the new entry.
|
|
}
|
|
function vm_map_insert(
|
|
map :vm_map_t;
|
|
vm_obj :vm_object_t;
|
|
offset :vm_ooffset_t;
|
|
start :vm_offset_t;
|
|
__end :vm_offset_t;
|
|
prot :vm_prot_t;
|
|
max :vm_prot_t;
|
|
cow :Integer):Integer;
|
|
label
|
|
charged;
|
|
var
|
|
new_entry :vm_map_entry_t;
|
|
prev_entry :vm_map_entry_t;
|
|
temp_entry :vm_map_entry_t;
|
|
protoeflags:vm_eflags_t;
|
|
inheritance:vm_inherit_t;
|
|
charge_prev_obj:Boolean;
|
|
begin
|
|
VM_MAP_ASSERT_LOCKED(map);
|
|
|
|
{
|
|
* Check that the start and end points are not bogus.
|
|
}
|
|
if (start<map^.min_offset) or (__end>map^.max_offset) or (start>=__end) then
|
|
begin
|
|
Exit(KERN_INVALID_ADDRESS);
|
|
end;
|
|
|
|
{
|
|
* Find the entry prior to the proposed starting address; if it's part
|
|
* of an existing entry, this range is bogus.
|
|
}
|
|
if vm_map_lookup_entry(map,start,@temp_entry) then
|
|
begin
|
|
Exit(KERN_NO_SPACE);
|
|
end;
|
|
|
|
prev_entry:=temp_entry;
|
|
|
|
{
|
|
* Assert that the next entry doesn't overlap the end point.
|
|
}
|
|
if (prev_entry^.next<>@map^.header) and
|
|
(prev_entry^.next^.start<__end) then
|
|
begin
|
|
Exit(KERN_NO_SPACE);
|
|
end;
|
|
|
|
protoeflags:=0;
|
|
charge_prev_obj:=FALSE;
|
|
|
|
if ((cow and MAP_COPY_ON_WRITE)<>0) then
|
|
protoeflags:=protoeflags or MAP_ENTRY_COW or MAP_ENTRY_NEEDS_COPY;
|
|
|
|
if ((cow and MAP_NOFAULT)<>0) then
|
|
begin
|
|
protoeflags:=protoeflags or MAP_ENTRY_NOFAULT;
|
|
|
|
Assert(vm_obj=nil,'vm_map_insert: paradoxical MAP_NOFAULT request');
|
|
end;
|
|
|
|
if ((cow and MAP_DISABLE_SYNCER)<>0) then
|
|
protoeflags:=protoeflags or MAP_ENTRY_NOSYNC;
|
|
|
|
if ((cow and MAP_DISABLE_COREDUMP)<>0) then
|
|
protoeflags:=protoeflags or MAP_ENTRY_NOCOREDUMP;
|
|
|
|
if ((cow and MAP_VN_WRITECOUNT)<>0) then
|
|
protoeflags:=protoeflags or MAP_ENTRY_VN_WRITECNT;
|
|
|
|
if ((cow and MAP_INHERIT_SHARE)<>0) then
|
|
inheritance:=VM_INHERIT_SHARE
|
|
else
|
|
inheritance:=VM_INHERIT_DEFAULT;
|
|
|
|
if ((cow and (MAP_ACC_NO_CHARGE or MAP_NOFAULT))<>0) then
|
|
goto charged;
|
|
|
|
if ((cow and MAP_ACC_CHARGED)<>0) or (((prot and VM_PROT_WRITE)<>0) and
|
|
(((protoeflags and MAP_ENTRY_NEEDS_COPY)<>0) or (vm_obj=nil))) then
|
|
begin
|
|
Assert((vm_obj=nil) or
|
|
((protoeflags and MAP_ENTRY_NEEDS_COPY)<>0),'OVERCOMMIT: vm_map_insert o %p", object');
|
|
if (vm_obj=nil) and ((protoeflags and MAP_ENTRY_NEEDS_COPY)=0) then
|
|
charge_prev_obj:=TRUE;
|
|
end;
|
|
|
|
charged:
|
|
|
|
if (vm_obj<>nil) then
|
|
begin
|
|
{
|
|
* OBJ_ONEMAPPING must be cleared unless this mapping
|
|
* is trivially proven to be the only mapping for any
|
|
* of the object's pages. (Object granularity
|
|
* reference counting is insufficient to recognize
|
|
* aliases with precision.)
|
|
}
|
|
VM_OBJECT_LOCK(vm_obj);
|
|
if (vm_obj^.ref_count>1) then
|
|
begin
|
|
vm_object_clear_flag(vm_obj, OBJ_ONEMAPPING);
|
|
end;
|
|
VM_OBJECT_UNLOCK(vm_obj);
|
|
end else
|
|
if ((prev_entry<>@map^.header) and
|
|
(prev_entry^.eflags=protoeflags) and
|
|
((cow and (MAP_ENTRY_GROWS_DOWN or MAP_ENTRY_GROWS_UP))=0) and
|
|
(prev_entry^.__end=start) and
|
|
vm_object_coalesce(prev_entry^.vm_obj,
|
|
prev_entry^.offset,
|
|
vm_size_t(prev_entry^.__end - prev_entry^.start),
|
|
vm_size_t(__end - prev_entry^.__end), charge_prev_obj)) then
|
|
begin
|
|
{
|
|
* We were able to extend the object. Determine if we
|
|
* can extend the previous map entry to include the
|
|
* new range as well.
|
|
}
|
|
if ((prev_entry^.inheritance=inheritance) and
|
|
(prev_entry^.protection=prot) and
|
|
(prev_entry^.max_protection=max)) then
|
|
begin
|
|
map^.size:=map^.size+(__end - prev_entry^.__end);
|
|
prev_entry^.__end:=__end;
|
|
//change size
|
|
|
|
if (cow<>-1) then
|
|
begin
|
|
pmap_enter_object(map^.pmap,
|
|
start,
|
|
__end,
|
|
prot);
|
|
end;
|
|
|
|
vm_map_entry_resize_free(map, prev_entry);
|
|
vm_map_simplify_entry(map, prev_entry);
|
|
Exit(KERN_SUCCESS);
|
|
end;
|
|
|
|
{
|
|
* If we can extend the object but cannot ext__end the
|
|
* map entry, we have to create a new map entry. We
|
|
* must bump the ref count on the ext__ended object to
|
|
* account for it. object may be nil.
|
|
}
|
|
vm_obj:=prev_entry^.vm_obj;
|
|
offset:=prev_entry^.offset + (prev_entry^.__end - prev_entry^.start);
|
|
vm_object_reference(vm_obj);
|
|
if (vm_obj<>nil) and
|
|
((prev_entry^.eflags and MAP_ENTRY_NEEDS_COPY)=0) then
|
|
begin
|
|
{ Object already accounts for this uid. }
|
|
end;
|
|
end;
|
|
|
|
{
|
|
* NOTE: if conditionals fail, object can be nil here. This occurs
|
|
* in things like the buffer map where we manage kva but do not manage
|
|
* backing objects.
|
|
}
|
|
|
|
{
|
|
* Create a new entry
|
|
}
|
|
new_entry:=vm_map_entry_create(map);
|
|
new_entry^.start:=start;
|
|
new_entry^.__end:=__end;
|
|
|
|
new_entry^.eflags:=protoeflags;
|
|
new_entry^.vm_obj:=vm_obj;
|
|
new_entry^.offset:=offset;
|
|
new_entry^.avail_ssize:=0;
|
|
|
|
new_entry^.inheritance:=inheritance;
|
|
new_entry^.protection:=prot;
|
|
new_entry^.max_protection:=max;
|
|
|
|
vm_object_reference(vm_obj);
|
|
|
|
Assert(not ENTRY_CHARGED(new_entry),'OVERCOMMIT: vm_map_insert leaks vm_map %p", new_entry');
|
|
|
|
{
|
|
* Insert the new entry into the list
|
|
}
|
|
vm_map_entry_link(map, prev_entry, new_entry);
|
|
map^.size:=map^.size+(new_entry^.__end - new_entry^.start);
|
|
|
|
{
|
|
* It may be possible to merge the new entry with the next and/or
|
|
* previous entries. However, due to MAP_STACK_* being a hack, a
|
|
* panic can result from merging such entries.
|
|
}
|
|
if ((cow and (MAP_STACK_GROWS_DOWN or MAP_STACK_GROWS_UP))=0) then
|
|
begin
|
|
vm_map_simplify_entry(map, new_entry);
|
|
end;
|
|
|
|
if (cow<>-1) then
|
|
begin
|
|
pmap_enter_object(map^.pmap,
|
|
start,
|
|
__end,
|
|
prot);
|
|
|
|
end;
|
|
|
|
Result:=KERN_SUCCESS;
|
|
end;
|
|
|
|
{
|
|
* vm_map_findspace:
|
|
*
|
|
* Find the first fit (lowest VM address) for "length" free bytes
|
|
* beginning at address>=start in the given map.
|
|
*
|
|
* In a vm_map_entry, "adj_free" is the amount of free space
|
|
* adjacent (higher address) to this entry, and "max_free" is the
|
|
* maximum amount of contiguous free space in its subtree. This
|
|
* allows finding a free region in one path down the tree, so
|
|
* O(log n) amortized with splay trees.
|
|
*
|
|
* The map must be locked, and leaves it so.
|
|
*
|
|
* Returns: 0 on success, and starting address in *addr,
|
|
* 1 if insufficient space.
|
|
}
|
|
function vm_map_findspace(map :vm_map_t;
|
|
start :vm_offset_t;
|
|
length:vm_size_t;
|
|
addr :p_vm_offset_t):Integer;
|
|
label
|
|
_nxt;
|
|
var
|
|
entry:vm_map_entry_t;
|
|
st:vm_offset_t;
|
|
begin
|
|
{
|
|
* Request must fit within min/max VM address and must avoid
|
|
* address wrap.
|
|
}
|
|
if (start<map^.min_offset) then
|
|
begin
|
|
start:=map^.min_offset;
|
|
end;
|
|
if (start + length>map^.max_offset) or (start + length<start) then
|
|
begin
|
|
Exit(1);
|
|
end;
|
|
|
|
{ Empty tree means wide open address space. }
|
|
if (map^.root=nil) then
|
|
begin
|
|
addr^:=start;
|
|
Exit(0);
|
|
end;
|
|
|
|
{
|
|
* After splay, if start comes before root node, then there
|
|
* must be a gap from start to the root.
|
|
}
|
|
map^.root:=vm_map_entry_splay(start, map^.root);
|
|
if (start + length<=map^.root^.start) then
|
|
begin
|
|
addr^:=start;
|
|
Exit(0);
|
|
end;
|
|
|
|
{
|
|
* Root is the last node that might begin its gap before
|
|
* start, and this is the last comparison where address
|
|
* wrap might be a problem.
|
|
}
|
|
|
|
if (start>map^.root^.__end) then
|
|
begin
|
|
st:=start;
|
|
end else
|
|
begin
|
|
st:=map^.root^.__end
|
|
end;
|
|
|
|
if (length<=map^.root^.__end + map^.root^.adj_free - st) then
|
|
begin
|
|
addr^:=st;
|
|
Exit(0);
|
|
end;
|
|
|
|
{ With max_free, can immediately tell if no solution. }
|
|
entry:=map^.root^.right;
|
|
|
|
if (entry=nil) then
|
|
begin
|
|
Exit(1);
|
|
end;
|
|
|
|
if (length>entry^.max_free) then
|
|
begin
|
|
Exit(1);
|
|
end;
|
|
|
|
{
|
|
* Search the right subtree in the order: left subtree, root,
|
|
* right subtree (first fit). The previous splay implies that
|
|
* all regions in the right subtree have addresses>start.
|
|
}
|
|
while (entry<>nil) do
|
|
begin
|
|
if (entry^.left<>nil) then
|
|
begin
|
|
if not (entry^.left^.max_free>=length) then goto _nxt;
|
|
entry:=entry^.left;
|
|
end else
|
|
begin
|
|
_nxt:
|
|
if (entry^.adj_free>=length) then
|
|
begin
|
|
addr^:=entry^.__end;
|
|
Exit(0);
|
|
end else
|
|
begin
|
|
entry:=entry^.right;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{ Can't get here, so panic if we do. }
|
|
Assert(false,'vm_map_findspace: max_free corrupt');
|
|
end;
|
|
|
|
function vm_map_fixed(map :vm_map_t;
|
|
vm_obj :vm_object_t;
|
|
offset :vm_ooffset_t;
|
|
start :vm_offset_t;
|
|
length :vm_size_t;
|
|
prot :vm_prot_t;
|
|
max :vm_prot_t;
|
|
cow :Integer;
|
|
overwr :Integer):Integer;
|
|
var
|
|
__end:vm_offset_t;
|
|
begin
|
|
__end:=start + length;
|
|
vm_map_lock(map);
|
|
VM_MAP_RANGE_CHECK(map, start, __end);
|
|
if (overwr<>0) then
|
|
begin
|
|
vm_map_delete(map, start, __end);
|
|
end;
|
|
Result:=vm_map_insert(map, vm_obj, offset, start, __end, prot, max, cow);
|
|
vm_map_unlock(map);
|
|
end;
|
|
|
|
{
|
|
* vm_map_find finds an unallocated region in the target address
|
|
* map with the given length. The search is defined to be
|
|
* first-fit from the specified address; the region found is
|
|
* returned in the same parameter.
|
|
*
|
|
* If object is non-nil, ref count must be bumped by caller
|
|
* prior to making call to account for the new entry.
|
|
}
|
|
function vm_map_find(map :vm_map_t;
|
|
vm_obj :vm_object_t;
|
|
offset :vm_ooffset_t;
|
|
addr :p_vm_offset_t;
|
|
length :vm_size_t;
|
|
find_space:Integer;
|
|
prot :vm_prot_t;
|
|
max :vm_prot_t;
|
|
cow :Integer):Integer;
|
|
label
|
|
again;
|
|
var
|
|
alignment,initial_addr,start:vm_offset_t;
|
|
begin
|
|
if (find_space=VMFS_OPTIMAL_SPACE) then
|
|
begin
|
|
if (vm_obj=nil) then
|
|
begin
|
|
find_space:=VMFS_ANY_SPACE;
|
|
end else
|
|
if ((vm_obj^.flags and OBJ_COLORED)=0) then
|
|
begin
|
|
find_space:=VMFS_ANY_SPACE;
|
|
end;
|
|
end;
|
|
if ((find_space shr 8)<>0) then
|
|
begin
|
|
Assert((find_space and $ff)=0,'bad VMFS flags');
|
|
alignment:=vm_offset_t(1) shl (find_space shr 8);
|
|
end else
|
|
begin
|
|
alignment:=0;
|
|
end;
|
|
initial_addr:=addr^;
|
|
again:
|
|
start:=initial_addr;
|
|
vm_map_lock(map);
|
|
repeat
|
|
if (find_space<>VMFS_NO_SPACE) then
|
|
begin
|
|
if (vm_map_findspace(map, start, length, addr)<>0) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
if (find_space=VMFS_OPTIMAL_SPACE) then
|
|
begin
|
|
find_space:=VMFS_ANY_SPACE;
|
|
goto again;
|
|
end;
|
|
Exit(KERN_NO_SPACE);
|
|
end;
|
|
|
|
case find_space of
|
|
VMFS_SUPER_SPACE,
|
|
VMFS_OPTIMAL_SPACE: pmap_align_superpage(vm_obj, offset, addr, length);
|
|
VMFS_ANY_SPACE:;
|
|
else
|
|
if ((addr^ and (alignment - 1))<>0) then
|
|
begin
|
|
addr^:=addr^ and (not (alignment - 1));
|
|
addr^:=addr^ + alignment;
|
|
end;
|
|
end;
|
|
|
|
start:=addr^;
|
|
end;
|
|
Result:=vm_map_insert(map, vm_obj, offset, start, start + length, prot, max, cow);
|
|
until not ((Result=KERN_NO_SPACE) and
|
|
(find_space<>VMFS_NO_SPACE) and
|
|
(find_space<>VMFS_ANY_SPACE));
|
|
vm_map_unlock(map);
|
|
end;
|
|
|
|
{
|
|
* vm_map_simplify_entry:
|
|
*
|
|
* Simplify the given map entry by merging with either neighbor. This
|
|
* routine also has the ability to merge with both neighbors.
|
|
*
|
|
* The map must be locked.
|
|
*
|
|
* This routine guarentees that the passed entry remains valid (though
|
|
* possibly extended). When merging, this routine may delete one or
|
|
* both neighbors.
|
|
}
|
|
procedure vm_map_simplify_entry(map:vm_map_t;entry:vm_map_entry_t);
|
|
var
|
|
next,prev:vm_map_entry_t;
|
|
prevsize, esize:vm_size_t;
|
|
begin
|
|
if ((entry^.eflags and (MAP_ENTRY_IS_SUB_MAP))<>0) then
|
|
begin
|
|
Exit;
|
|
end;
|
|
|
|
prev:=entry^.prev;
|
|
if (prev<>@map^.header) then
|
|
begin
|
|
prevsize:=prev^.__end - prev^.start;
|
|
if (prev^.__end=entry^.start) and
|
|
(prev^.vm_obj=entry^.vm_obj) and
|
|
((prev^.vm_obj=nil) or (prev^.offset + prevsize=entry^.offset)) and
|
|
(prev^.eflags=entry^.eflags) and
|
|
(prev^.protection=entry^.protection) and
|
|
(prev^.max_protection=entry^.max_protection) and
|
|
(prev^.inheritance=entry^.inheritance) then
|
|
begin
|
|
vm_map_entry_unlink(map, prev);
|
|
entry^.start:=prev^.start;
|
|
entry^.offset:=prev^.offset;
|
|
//change
|
|
if (entry^.prev<>@map^.header) then
|
|
begin
|
|
vm_map_entry_resize_free(map, entry^.prev);
|
|
end;
|
|
|
|
{
|
|
* If the backing object is a vnode object,
|
|
* vm_object_deallocate() calls vrele().
|
|
* However, vrele() does not lock the vnode
|
|
* because the vnode has additional
|
|
* references. Thus, the map lock can be kept
|
|
* without causing a lock-order reversal with
|
|
* the vnode lock.
|
|
*
|
|
* Since we count the number of virtual page
|
|
* mappings in object^.un_pager.vnp.writemappings,
|
|
* the writemappings value should not be adjusted
|
|
* when the entry is disposed of.
|
|
}
|
|
vm_object_deallocate(prev^.vm_obj);
|
|
vm_map_entry_dispose(map, prev);
|
|
end;
|
|
end;
|
|
|
|
next:=entry^.next;
|
|
if (next<>@map^.header) then
|
|
begin
|
|
esize:=entry^.__end - entry^.start;
|
|
if (entry^.__end=next^.start) and
|
|
(next^.vm_obj=entry^.vm_obj) and
|
|
((entry^.vm_obj=nil) or (entry^.offset + esize=next^.offset)) and
|
|
(next^.eflags=entry^.eflags) and
|
|
(next^.protection=entry^.protection) and
|
|
(next^.max_protection=entry^.max_protection) and
|
|
(next^.inheritance=entry^.inheritance) then
|
|
begin
|
|
vm_map_entry_unlink(map, next);
|
|
entry^.__end:=next^.__end;
|
|
//change
|
|
vm_map_entry_resize_free(map, entry);
|
|
|
|
vm_object_deallocate(next^.vm_obj);
|
|
vm_map_entry_dispose(map, next);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{
|
|
* This routine is called only when it is known that
|
|
* the entry must be split.
|
|
}
|
|
procedure _vm_map_clip_start(map:vm_map_t;entry:vm_map_entry_t;start:vm_offset_t);
|
|
var
|
|
new_entry:vm_map_entry_t;
|
|
begin
|
|
VM_MAP_ASSERT_LOCKED(map);
|
|
|
|
{
|
|
* Split off the front portion -- note that we must insert the new
|
|
* entry BEFORE this one, so that this entry has the specified
|
|
* starting address.
|
|
}
|
|
vm_map_simplify_entry(map, entry);
|
|
|
|
new_entry:=vm_map_entry_create(map);
|
|
new_entry^:=entry^;
|
|
|
|
new_entry^.__end:=start;
|
|
entry^.offset:=entry^.offset + (start - entry^.start);
|
|
entry^.start:=start;
|
|
|
|
vm_map_entry_link(map, entry^.prev, new_entry);
|
|
|
|
if ((entry^.eflags and MAP_ENTRY_IS_SUB_MAP)=0) then
|
|
begin
|
|
vm_object_reference(new_entry^.vm_obj);
|
|
end;
|
|
end;
|
|
|
|
{
|
|
* vm_map_clip_start: [ internal use only ]
|
|
*
|
|
* Asserts that the given entry begins at or after
|
|
* the specified address; if necessary,
|
|
* it splits the entry into two.
|
|
}
|
|
procedure vm_map_clip_start(map:vm_map_t;entry:vm_map_entry_t;start:vm_offset_t);
|
|
begin
|
|
if (start>entry^.start) then
|
|
begin
|
|
_vm_map_clip_start(map,entry,start);
|
|
end;
|
|
end;
|
|
|
|
{
|
|
* This routine is called only when it is known that
|
|
* the entry must be split.
|
|
}
|
|
|
|
procedure _vm_map_clip_end(map:vm_map_t;entry:vm_map_entry_t;__end:vm_offset_t);
|
|
var
|
|
new_entry:vm_map_entry_t;
|
|
begin
|
|
VM_MAP_ASSERT_LOCKED(map);
|
|
|
|
{
|
|
* Create a new entry and insert it AFTER the specified entry
|
|
}
|
|
new_entry:=vm_map_entry_create(map);
|
|
new_entry^:=entry^;
|
|
|
|
new_entry^.start:=__end;
|
|
entry^.__end:=__end;
|
|
new_entry^.offset:=new_entry^.offset + (__end - entry^.start);
|
|
|
|
vm_map_entry_link(map, entry, new_entry);
|
|
|
|
if ((entry^.eflags and MAP_ENTRY_IS_SUB_MAP)=0) then
|
|
begin
|
|
vm_object_reference(new_entry^.vm_obj);
|
|
end;
|
|
end;
|
|
|
|
{
|
|
* vm_map_clip_end: [ internal use only ]
|
|
*
|
|
* Asserts that the given entry ends at or before
|
|
* the specified address; if necessary,
|
|
* it splits the entry into two.
|
|
}
|
|
procedure vm_map_clip_end(map:vm_map_t;entry:vm_map_entry_t;__end:vm_offset_t);
|
|
begin
|
|
if (__end<entry^.__end) then
|
|
begin
|
|
_vm_map_clip_end(map,entry,__end);
|
|
end;
|
|
end;
|
|
|
|
{
|
|
* vm_map_protect:
|
|
*
|
|
* Sets the protection of the specified address
|
|
* region in the target map. If "set_max" is
|
|
* specified, the maximum protection is to be set;
|
|
* otherwise, only the current protection is affected.
|
|
}
|
|
function vm_map_protect(map :vm_map_t;
|
|
start :vm_offset_t;
|
|
__end :vm_offset_t;
|
|
new_prot:vm_prot_t;
|
|
set_max :Boolean):Integer;
|
|
|
|
function MASK(entry:vm_map_entry_t):vm_eflags_t; inline;
|
|
begin
|
|
if ((entry^.eflags and MAP_ENTRY_COW)<>0) then
|
|
Result:=(not VM_PROT_WRITE)
|
|
else
|
|
Result:=VM_PROT_ALL;
|
|
end;
|
|
|
|
label
|
|
_continue;
|
|
var
|
|
current,entry:vm_map_entry_t;
|
|
obj:vm_object_t;
|
|
old_prot:vm_prot_t;
|
|
begin
|
|
if (start=__end) then
|
|
begin
|
|
Exit(KERN_SUCCESS);
|
|
end;
|
|
|
|
vm_map_lock(map);
|
|
|
|
VM_MAP_RANGE_CHECK(map, start, __end);
|
|
|
|
if (vm_map_lookup_entry(map, start,@entry)) then
|
|
begin
|
|
vm_map_clip_start(map, entry, start);
|
|
end else
|
|
begin
|
|
entry:=entry^.next;
|
|
end;
|
|
|
|
{
|
|
* Make a first pass to check for protection violations.
|
|
}
|
|
current:=entry;
|
|
while ((current<>@map^.header) and (current^.start<__end)) do
|
|
begin
|
|
if ((current^.eflags and MAP_ENTRY_IS_SUB_MAP)<>0) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
Exit(KERN_INVALID_ARGUMENT);
|
|
end;
|
|
if ((new_prot and current^.max_protection)<>new_prot) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
Exit(KERN_PROTECTION_FAILURE);
|
|
end;
|
|
current:=current^.next;
|
|
end;
|
|
|
|
|
|
{
|
|
* Do an accounting pass for private read-only mappings that
|
|
* now will do cow due to allowed write (e.g. debugger sets
|
|
* breakpoint on text segment)
|
|
}
|
|
current:=entry;
|
|
while (current<>@map^.header) and (current^.start<__end) do
|
|
begin
|
|
|
|
vm_map_clip_end(map, current, __end);
|
|
|
|
if set_max or
|
|
(((new_prot and (not current^.protection)) and VM_PROT_WRITE)=0) or
|
|
ENTRY_CHARGED(current) then
|
|
begin
|
|
goto _continue;
|
|
end;
|
|
|
|
obj:=current^.vm_obj;
|
|
|
|
if (obj=nil) or ((current^.eflags and MAP_ENTRY_NEEDS_COPY)<>0) then
|
|
begin
|
|
goto _continue;
|
|
end;
|
|
|
|
VM_OBJECT_LOCK(obj);
|
|
if (obj^.otype<>OBJT_DEFAULT) then
|
|
begin
|
|
VM_OBJECT_UNLOCK(obj);
|
|
goto _continue;
|
|
end;
|
|
|
|
VM_OBJECT_UNLOCK(obj);
|
|
|
|
_continue:
|
|
current:=current^.next;
|
|
end;
|
|
|
|
{
|
|
* Go back and fix up protections. [Note that clipping is not
|
|
* necessary the second time.]
|
|
}
|
|
current:=entry;
|
|
while ((current<>@map^.header) and (current^.start<__end)) do
|
|
begin
|
|
old_prot:=current^.protection;
|
|
|
|
if set_max then
|
|
begin
|
|
current^.protection:=new_prot and old_prot;
|
|
current^.max_protection:=current^.protection;
|
|
end else
|
|
begin
|
|
current^.protection:=new_prot;
|
|
end;
|
|
|
|
if ((current^.protection and VM_PROT_WRITE)<>0) and
|
|
((old_prot and VM_PROT_WRITE)=0) then
|
|
begin
|
|
//vm_fault_copy_entry(map, map, current, current, nil);
|
|
end;
|
|
|
|
{
|
|
* When restricting access, update the physical map. Worry
|
|
* about copy-on-write here.
|
|
}
|
|
if (old_prot<>current^.protection) then
|
|
begin
|
|
pmap_protect(map^.pmap, current^.start,
|
|
current^.__end,
|
|
current^.protection and MASK(current),
|
|
old_prot);
|
|
end;
|
|
vm_map_simplify_entry(map, current);
|
|
current:=current^.next;
|
|
end;
|
|
vm_map_unlock(map);
|
|
Result:=(KERN_SUCCESS);
|
|
end;
|
|
|
|
{
|
|
* vm_map_madvise:
|
|
*
|
|
* This routine traverses a processes map handling the madvise
|
|
* system call. Advisories are classified as either those effecting
|
|
* the vm_map_entry structure, or those effecting the underlying
|
|
* objects.
|
|
}
|
|
function vm_map_madvise(map :vm_map_t;
|
|
start :vm_offset_t;
|
|
__end :vm_offset_t;
|
|
behav :Integer):Integer;
|
|
var
|
|
current,entry:vm_map_entry_t;
|
|
modify_map:Integer;
|
|
pstart,pend:vm_pindex_t;
|
|
useStart:vm_offset_t;
|
|
begin
|
|
modify_map:=0;
|
|
|
|
{
|
|
* Some madvise calls directly modify the vm_map_entry, in which case
|
|
* we need to use an exclusive lock on the map and we need to perform
|
|
* various clipping operations. Otherwise we only need a read-lock
|
|
* on the map.
|
|
}
|
|
case behav of
|
|
MADV_NORMAL,
|
|
MADV_SEQUENTIAL,
|
|
MADV_RANDOM,
|
|
MADV_NOSYNC,
|
|
MADV_AUTOSYNC,
|
|
MADV_NOCORE,
|
|
MADV_CORE:
|
|
begin
|
|
if (start=__end) then
|
|
begin
|
|
Exit(KERN_SUCCESS);
|
|
end;
|
|
modify_map:=1;
|
|
vm_map_lock(map);
|
|
end;
|
|
MADV_WILLNEED,
|
|
MADV_DONTNEED,
|
|
MADV_FREE:
|
|
begin
|
|
if (start=__end) then
|
|
begin
|
|
Exit(KERN_SUCCESS);
|
|
end;
|
|
vm_map_lock(map);
|
|
end;
|
|
else
|
|
Exit(KERN_INVALID_ARGUMENT);
|
|
end;
|
|
|
|
{
|
|
* Locate starting entry and clip if necessary.
|
|
}
|
|
VM_MAP_RANGE_CHECK(map, start, __end);
|
|
|
|
if (vm_map_lookup_entry(map,start,@entry)) then
|
|
begin
|
|
if (modify_map<>0) then
|
|
begin
|
|
vm_map_clip_start(map, entry, start);
|
|
end;
|
|
end else
|
|
begin
|
|
entry:=entry^.next;
|
|
end;
|
|
|
|
if (modify_map<>0) then
|
|
begin
|
|
{
|
|
* madvise behaviors that are implemented in the vm_map_entry.
|
|
*
|
|
* We clip the vm_map_entry so that behavioral changes are
|
|
* limited to the specified address range.
|
|
}
|
|
current:=entry;
|
|
while (current<>@map^.header) and (current^.start<__end) do
|
|
begin
|
|
if (current^.eflags and MAP_ENTRY_IS_SUB_MAP)<>0 then
|
|
begin
|
|
current:=current^.next;
|
|
continue;
|
|
end;
|
|
|
|
vm_map_clip_end(map, current, __end);
|
|
|
|
case behav of
|
|
MADV_NORMAL:
|
|
begin
|
|
vm_map_entry_set_behavior(current, MAP_ENTRY_BEHAV_NORMAL);
|
|
end;
|
|
MADV_SEQUENTIAL:
|
|
begin
|
|
vm_map_entry_set_behavior(current, MAP_ENTRY_BEHAV_SEQUENTIAL);
|
|
end;
|
|
MADV_RANDOM:
|
|
begin
|
|
vm_map_entry_set_behavior(current, MAP_ENTRY_BEHAV_RANDOM);
|
|
end;
|
|
MADV_NOSYNC:
|
|
begin
|
|
current^.eflags:=current^.eflags or MAP_ENTRY_NOSYNC;
|
|
end;
|
|
MADV_AUTOSYNC:
|
|
begin
|
|
current^.eflags:=current^.eflags and (not MAP_ENTRY_NOSYNC);
|
|
end;
|
|
MADV_NOCORE:
|
|
begin
|
|
current^.eflags:=current^.eflags or MAP_ENTRY_NOCOREDUMP;
|
|
end;
|
|
MADV_CORE:
|
|
begin
|
|
current^.eflags:=current^.eflags and (not MAP_ENTRY_NOCOREDUMP);
|
|
end;
|
|
else;
|
|
end;
|
|
vm_map_simplify_entry(map, current);
|
|
|
|
current:=current^.next;
|
|
end;
|
|
|
|
vm_map_unlock(map);
|
|
end else
|
|
begin
|
|
{
|
|
* madvise behaviors that are implemented in the underlying
|
|
* vm_object.
|
|
*
|
|
* Since we don't clip the vm_map_entry, we have to clip
|
|
* the vm_object pindex and count.
|
|
}
|
|
current:=entry;
|
|
while (current<>@map^.header) and (current^.start<__end) do
|
|
begin
|
|
if (current^.eflags and MAP_ENTRY_IS_SUB_MAP)<>0 then
|
|
begin
|
|
current:=current^.next;
|
|
continue;
|
|
end;
|
|
|
|
pstart:=OFF_TO_IDX(current^.offset);
|
|
pend :=pstart + atop(current^.__end - current^.start);
|
|
useStart:=current^.start;
|
|
|
|
if (current^.start<start) then
|
|
begin
|
|
pstart:=pstart+atop(start - current^.start);
|
|
useStart:=start;
|
|
end;
|
|
if (current^.__end>__end) then
|
|
pend:=pend-atop(current^.__end - __end);
|
|
|
|
if (pstart>=pend) then
|
|
begin
|
|
current:=current^.next;
|
|
continue;
|
|
end;
|
|
|
|
//vm_object_madvise(current^.vm_obj, pstart, p__end, behav);
|
|
|
|
if (current^.vm_obj=nil) then
|
|
begin
|
|
Case behav of
|
|
MADV_WILLNEED:
|
|
begin
|
|
pmap_enter_object(map^.pmap,
|
|
useStart,
|
|
useStart+ptoa(pend-pstart),
|
|
current^.protection);
|
|
end;
|
|
MADV_FREE:
|
|
begin
|
|
pmap_madv_free(map^.pmap,
|
|
useStart,
|
|
useStart+ptoa(pend-pstart),
|
|
current^.protection);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
current:=current^.next;
|
|
end;
|
|
|
|
vm_map_unlock(map);
|
|
end;
|
|
Result:=(0);
|
|
end;
|
|
|
|
|
|
{
|
|
* vm_map_inherit:
|
|
*
|
|
* Sets the inheritance of the specified address
|
|
* range in the target map. Inheritance
|
|
* affects how the map will be shared with
|
|
* child maps at the time of vmspace_fork.
|
|
}
|
|
function vm_map_inherit(map :vm_map_t;
|
|
start :vm_offset_t;
|
|
__end :vm_offset_t;
|
|
new_inheritance:vm_inherit_t
|
|
):Integer;
|
|
var
|
|
entry :vm_map_entry_t;
|
|
temp_entry:vm_map_entry_t;
|
|
begin
|
|
case (new_inheritance) of
|
|
VM_INHERIT_NONE,
|
|
VM_INHERIT_COPY,
|
|
VM_INHERIT_SHARE:;
|
|
else
|
|
Exit(KERN_INVALID_ARGUMENT);
|
|
end;
|
|
if (start=__end) then
|
|
begin
|
|
Exit(KERN_SUCCESS);
|
|
end;
|
|
vm_map_lock(map);
|
|
VM_MAP_RANGE_CHECK(map, start, __end);
|
|
|
|
if (vm_map_lookup_entry(map, start, @temp_entry)) then
|
|
begin
|
|
entry:=temp_entry;
|
|
vm_map_clip_start(map, entry, start);
|
|
end else
|
|
begin
|
|
entry:=temp_entry^.next;
|
|
end;
|
|
|
|
while ((entry<>@map^.header) and (entry^.start<__end)) do
|
|
begin
|
|
vm_map_clip_end(map, entry, __end);
|
|
entry^.inheritance:=new_inheritance;
|
|
vm_map_simplify_entry(map, entry);
|
|
entry:=entry^.next;
|
|
end;
|
|
vm_map_unlock(map);
|
|
Result:=(KERN_SUCCESS);
|
|
end;
|
|
|
|
{
|
|
* vm_map_sync
|
|
*
|
|
* Push any dirty cached pages in the address range to their pager.
|
|
* If syncio is TRUE, dirty pages are written synchronously.
|
|
* If invalidate is TRUE, any cached pages are freed as well.
|
|
*
|
|
* If the size of the region from start to __end is zero, we are
|
|
* supposed to flush all modified pages within the region containing
|
|
* start. Unfortunately, a region can be split or coalesced with
|
|
* neighboring regions, making it difficult to determine what the
|
|
* original region was. Therefore, we approximate this requirement by
|
|
* flushing the current region containing start.
|
|
*
|
|
* Returns an error if any part of the specified range is not mapped.
|
|
}
|
|
function vm_map_sync(map :vm_map_t;
|
|
start:vm_offset_t;
|
|
__end:vm_offset_t;
|
|
syncio :Boolean;
|
|
invalidate:Boolean):Integer;
|
|
var
|
|
current:vm_map_entry_t;
|
|
entry :vm_map_entry_t;
|
|
size :vm_size_t;
|
|
vm_obj :vm_object_t;
|
|
offset :vm_ooffset_t;
|
|
last_timestamp:DWORD;
|
|
failed:Boolean;
|
|
|
|
smap:vm_map_t;
|
|
tentry:vm_map_entry_t;
|
|
tsize:vm_size_t;
|
|
begin
|
|
vm_map_lock(map);
|
|
VM_MAP_RANGE_CHECK(map, start, __end);
|
|
if (not vm_map_lookup_entry(map, start, @entry)) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
Exit(KERN_INVALID_ADDRESS);
|
|
end else
|
|
if (start=__end) then
|
|
begin
|
|
start:=entry^.start;
|
|
__end:=entry^.__end;
|
|
end;
|
|
{
|
|
* Make a first pass to check for user-wired memory and holes.
|
|
}
|
|
current:=entry;
|
|
while (current<>@map^.header) and (current^.start<__end) do
|
|
begin
|
|
if (__end>current^.__end) and
|
|
((current^.next=@map^.header) or
|
|
(current^.__end<>current^.next^.start)) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
Exit(KERN_INVALID_ADDRESS);
|
|
end;
|
|
|
|
current:=current^.next;
|
|
end;
|
|
|
|
if invalidate then
|
|
begin
|
|
//
|
|
end;
|
|
failed:=FALSE;
|
|
|
|
{
|
|
* Make a second pass, cleaning/uncaching pages from the indicated
|
|
* objects as we go.
|
|
}
|
|
current:=entry;
|
|
while (current<>@map^.header) and (current^.start<__end) do
|
|
begin
|
|
offset:=current^.offset + (start - current^.start);
|
|
|
|
if (__end<=current^.__end) then
|
|
size:=__end-start
|
|
else
|
|
size:=current^.__end-start;
|
|
|
|
if ((current^.eflags and MAP_ENTRY_IS_SUB_MAP)<>0) then
|
|
begin
|
|
smap:=vm_map_t(current^.vm_obj);
|
|
vm_map_lock(smap);
|
|
vm_map_lookup_entry(smap, offset, @tentry);
|
|
tsize:=tentry^.__end - offset;
|
|
if (tsize<size) then
|
|
size:=tsize;
|
|
vm_obj:=tentry^.vm_obj;
|
|
offset:=tentry^.offset + (offset - tentry^.start);
|
|
vm_map_unlock(smap);
|
|
end else
|
|
begin
|
|
vm_obj:=current^.vm_obj;
|
|
end;
|
|
vm_object_reference(vm_obj);
|
|
last_timestamp:=map^.timestamp;
|
|
vm_map_unlock(map);
|
|
//if (not vm_object_sync(_object, offset, size, syncio, invalidate)) then
|
|
// failed:=TRUE;
|
|
start:=start+size;
|
|
vm_object_deallocate(vm_obj);
|
|
vm_map_lock(map);
|
|
if (last_timestamp=map^.timestamp) or
|
|
(not vm_map_lookup_entry(map, start, @current)) then
|
|
begin
|
|
current:=current^.next;
|
|
end;
|
|
end;
|
|
|
|
vm_map_unlock(map);
|
|
|
|
case failed of
|
|
True :Result:=KERN_FAILURE;
|
|
FAlse:Result:=KERN_SUCCESS;
|
|
end;
|
|
end;
|
|
|
|
procedure vm_map_entry_deallocate(entry:vm_map_entry_t);
|
|
begin
|
|
if ((entry^.eflags and MAP_ENTRY_IS_SUB_MAP)=0) then
|
|
begin
|
|
vm_object_deallocate(entry^.vm_obj);
|
|
end;
|
|
Freemem(entry);
|
|
end;
|
|
|
|
{
|
|
* vm_map_entry_delete: [ internal use only ]
|
|
*
|
|
* Deallocate the given entry from the target map.
|
|
}
|
|
procedure vm_map_entry_delete(map:vm_map_t;entry:vm_map_entry_t);
|
|
var
|
|
vm_obj:vm_object_t;
|
|
offidxstart,offidx_end,count:vm_pindex_t;
|
|
size:vm_ooffset_t;
|
|
begin
|
|
vm_map_entry_unlink(map, entry);
|
|
vm_obj:=entry^.vm_obj;
|
|
size:=entry^.__end - entry^.start;
|
|
map^.size:=map^.size-size;
|
|
|
|
if ((entry^.eflags and MAP_ENTRY_IS_SUB_MAP)=0) and
|
|
(vm_obj<>nil) then
|
|
begin
|
|
count:=OFF_TO_IDX(size);
|
|
offidxstart:=OFF_TO_IDX(entry^.offset);
|
|
offidx_end:=offidxstart + count;
|
|
VM_OBJECT_LOCK(vm_obj);
|
|
if (vm_obj^.ref_count<>1) and
|
|
(((vm_obj^.flags and (OBJ_NOSPLIT or OBJ_ONEMAPPING))=OBJ_ONEMAPPING)) then
|
|
begin
|
|
vm_object_collapse(vm_obj);
|
|
|
|
{
|
|
* The option OBJPR_NOTMAPPED can be passed here
|
|
* because vm_map_delete() already performed
|
|
* pmap_remove() on the only mapping to this range
|
|
* of pages.
|
|
}
|
|
vm_object_page_remove(vm_obj, offidxstart, offidx_end, OBJPR_NOTMAPPED);
|
|
|
|
if (offidx_end>=vm_obj^.size) and
|
|
(offidxstart<vm_obj^.size) then
|
|
begin
|
|
vm_obj^.size:=offidxstart;
|
|
end;
|
|
end;
|
|
VM_OBJECT_UNLOCK(vm_obj);
|
|
end else
|
|
begin
|
|
entry^.vm_obj:=nil;
|
|
end;
|
|
|
|
begin
|
|
entry^.next:=curkthread^.td_map_def_user;
|
|
curkthread^.td_map_def_user:=entry;
|
|
end;
|
|
end;
|
|
|
|
{
|
|
* vm_map_delete: [ internal use only ]
|
|
*
|
|
* Deallocates the given address range from the target
|
|
* map.
|
|
}
|
|
function vm_map_delete(map:vm_map_t;start:vm_offset_t;__end:vm_offset_t):Integer;
|
|
var
|
|
entry :vm_map_entry_t;
|
|
first_entry:vm_map_entry_t;
|
|
next :vm_map_entry_t;
|
|
begin
|
|
VM_MAP_ASSERT_LOCKED(map);
|
|
if (start=__end) then
|
|
begin
|
|
Exit(KERN_SUCCESS);
|
|
end;
|
|
|
|
{
|
|
* Find the start of the region, and clip it
|
|
}
|
|
if (not vm_map_lookup_entry(map, start, @first_entry)) then
|
|
begin
|
|
entry:=first_entry^.next;
|
|
end else
|
|
begin
|
|
entry:=first_entry;
|
|
vm_map_clip_start(map, entry, start);
|
|
end;
|
|
|
|
{
|
|
* Step through all entries in this region
|
|
}
|
|
while (entry<>@map^.header) and (entry^.start<__end) do
|
|
begin
|
|
|
|
vm_map_clip_end(map, entry, __end);
|
|
|
|
next:=entry^.next;
|
|
|
|
pmap_remove(map^.pmap,entry^.start,entry^.__end,entry^.protection);
|
|
|
|
{
|
|
* Delete the entry only after removing all pmap
|
|
* entries pointing to its pages. (Otherwise, its
|
|
* page frames may be reallocated, and any modify bits
|
|
* will be set in the wrong object!)
|
|
}
|
|
vm_map_entry_delete(map, entry);
|
|
entry:=next;
|
|
end;
|
|
Result:=(KERN_SUCCESS);
|
|
end;
|
|
|
|
{
|
|
* vm_map_remove:
|
|
*
|
|
* Remove the given address range from the target map.
|
|
* This is the exported form of vm_map_delete.
|
|
}
|
|
function vm_map_remove(map:vm_map_t;start:vm_offset_t;__end:vm_offset_t):Integer;
|
|
begin
|
|
vm_map_lock(map);
|
|
VM_MAP_RANGE_CHECK(map, start, __end);
|
|
Result:=vm_map_delete(map, start, __end);
|
|
vm_map_unlock(map);
|
|
end;
|
|
|
|
{
|
|
* vm_map_check_protection:
|
|
*
|
|
* Assert that the target map allows the specified privilege on the
|
|
* entire address region given. The entire region must be allocated.
|
|
*
|
|
* WARNING! This code does not and should not check whether the
|
|
* contents of the region is accessible. For example a smaller file
|
|
* might be mapped into a larger address space.
|
|
*
|
|
* NOTE! This code is also called by munmap().
|
|
*
|
|
* The map must be locked. A read lock is sufficient.
|
|
}
|
|
function vm_map_check_protection(map:vm_map_t;
|
|
start:vm_offset_t;
|
|
__end:vm_offset_t;
|
|
protection:vm_prot_t):boolean;
|
|
var
|
|
entry :vm_map_entry_t;
|
|
tmp_entry:vm_map_entry_t;
|
|
begin
|
|
if (not vm_map_lookup_entry(map, start, @tmp_entry)) then
|
|
begin
|
|
Exit(FALSE);
|
|
end;
|
|
|
|
entry:=tmp_entry;
|
|
|
|
while (start<__end) do
|
|
begin
|
|
if (entry=@map^.header) then
|
|
begin
|
|
Exit (FALSE);
|
|
end;
|
|
{
|
|
* No holes allowed!
|
|
}
|
|
if (start<entry^.start) then
|
|
begin
|
|
Exit(FALSE);
|
|
end;
|
|
{
|
|
* Check protection associated with entry.
|
|
}
|
|
if ((entry^.protection and protection)<>protection) then
|
|
begin
|
|
Exit(FALSE);
|
|
end;
|
|
{ go to next entry }
|
|
start:=entry^.__end;
|
|
entry:=entry^.next;
|
|
end;
|
|
Exit(TRUE);
|
|
end;
|
|
|
|
function vm_map_stack(map :vm_map_t;
|
|
addrbos :vm_offset_t;
|
|
max_ssize:vm_size_t;
|
|
prot :vm_prot_t;
|
|
max :vm_prot_t;
|
|
cow :Integer):Integer;
|
|
var
|
|
new_entry, prev_entry:vm_map_entry_t;
|
|
bot, top:vm_offset_t;
|
|
growsize, init_ssize:vm_size_t;
|
|
orient, rv:Integer;
|
|
vmemlim:QWORD;
|
|
begin
|
|
{
|
|
* The stack orientation is piggybacked with the cow argument.
|
|
* Extract it into orient and mask the cow argument so that we
|
|
* don't pass it around further.
|
|
* NOTE: We explicitly allow bi-directional stacks.
|
|
}
|
|
orient:=cow and (MAP_STACK_GROWS_DOWN or MAP_STACK_GROWS_UP);
|
|
Assert(orient<>0,'No stack grow direction');
|
|
|
|
if (addrbos<vm_map_min(map)) or
|
|
(addrbos>vm_map_max(map)) or
|
|
(addrbos + max_ssize<addrbos) then
|
|
begin
|
|
Exit(KERN_NO_SPACE);
|
|
end;
|
|
|
|
growsize:=sgrowsiz;
|
|
|
|
if (max_ssize<growsize) then
|
|
init_ssize:=max_ssize
|
|
else
|
|
init_ssize:=growsize;
|
|
|
|
vmemlim:=lim_cur(RLIMIT_VMEM);
|
|
|
|
vm_map_lock(map);
|
|
|
|
{ If addr is already mapped, no go }
|
|
if (vm_map_lookup_entry(map, addrbos, @prev_entry)) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
Exit(KERN_NO_SPACE);
|
|
end;
|
|
|
|
{ If we would blow our VMEM resource limit, no go }
|
|
if (map^.size + init_ssize>vmemlim) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
Exit(KERN_NO_SPACE);
|
|
end;
|
|
|
|
{
|
|
* If we can't accomodate max_ssize in the current mapping, no go.
|
|
* However, we need to be aware that subsequent user mappings might
|
|
* map into the space we have reserved for stack, and currently this
|
|
* space is not protected.
|
|
*
|
|
* Hopefully we will at least detect this condition when we try to
|
|
* grow the stack.
|
|
}
|
|
if (prev_entry^.next<>@map^.header) and
|
|
(prev_entry^.next^.start<addrbos + max_ssize) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
Exit(KERN_NO_SPACE);
|
|
end;
|
|
|
|
{
|
|
* We initially map a stack of only init_ssize. We will grow as
|
|
* needed later. Dep__ending on the orientation of the stack (i.e.
|
|
* the grow direction) we either map at the top of the range, the
|
|
* bottom of the range or in the middle.
|
|
*
|
|
* Note: we would normally expect prot and max to be VM_PROT_ALL,
|
|
* and cow to be 0. Possibly we should eliminate these as input
|
|
* parameters, and just pass these values here in the insert call.
|
|
}
|
|
if (orient=MAP_STACK_GROWS_DOWN) then
|
|
begin
|
|
bot:=addrbos + max_ssize - init_ssize;
|
|
end else
|
|
if (orient=MAP_STACK_GROWS_UP) then
|
|
begin
|
|
bot:=addrbos;
|
|
end else
|
|
begin
|
|
bot:=round_page(addrbos + (max_ssize div 2) - (init_ssize div 2));
|
|
end;
|
|
|
|
top:=bot + init_ssize;
|
|
rv:=vm_map_insert(map, nil, 0, bot, top, prot, max, cow);
|
|
|
|
{ Now set the avail_ssize amount. }
|
|
if (rv=KERN_SUCCESS) then
|
|
begin
|
|
if (prev_entry<>@map^.header) then
|
|
begin
|
|
vm_map_clip_end(map, prev_entry, bot);
|
|
end;
|
|
|
|
new_entry:=prev_entry^.next;
|
|
if (new_entry^.__end<>top) or (new_entry^.start<>bot) then
|
|
begin
|
|
Assert(false,'Bad entry start/end for new stack entry');
|
|
end;
|
|
|
|
new_entry^.avail_ssize:=max_ssize - init_ssize;
|
|
if ((orient and MAP_STACK_GROWS_DOWN)<>0) then
|
|
begin
|
|
new_entry^.eflags:=new_entry^.eflags or MAP_ENTRY_GROWS_DOWN;
|
|
end;
|
|
|
|
if ((orient and MAP_STACK_GROWS_UP)<>0) then
|
|
begin
|
|
new_entry^.eflags:=new_entry^.eflags or MAP_ENTRY_GROWS_UP;
|
|
end;
|
|
end;
|
|
|
|
vm_map_unlock(map);
|
|
Result:=(rv);
|
|
end;
|
|
|
|
{ Attempts to grow a vm stack entry. Returns KERN_SUCCESS if the
|
|
* desired address is already mapped, or if we successfully grow
|
|
* the stack. Also returns KERN_SUCCESS if addr is outside the
|
|
* stack range (this is strange, but preserves compatibility with
|
|
* the grow function in vm_machdep.c).
|
|
}
|
|
function vm_map_growstack(addr:vm_offset_t):Integer;
|
|
label
|
|
_or,
|
|
_out;
|
|
var
|
|
next_entry, prev_entry:vm_map_entry_t;
|
|
new_entry, stack_entry:vm_map_entry_t;
|
|
map:vm_map_t;
|
|
__end:vm_offset_t;
|
|
growsize:vm_size_t;
|
|
grow_amount, max_grow:QWORD;
|
|
stacklim, vmemlim:QWORD;
|
|
is_procstack, rv:Integer;
|
|
|
|
function _stack_guard_page:QWORD; inline;
|
|
begin
|
|
if (stack_guard_page<>0) then
|
|
Result:=PAGE_SIZE
|
|
else
|
|
Result:=0;
|
|
end;
|
|
|
|
begin
|
|
map:=@g_vmspace.vm_map;
|
|
|
|
stacklim:=lim_cur(RLIMIT_STACK);
|
|
vmemlim:=lim_cur(RLIMIT_VMEM);
|
|
|
|
vm_map_lock(map);
|
|
|
|
{ If addr is already in the entry range, no need to grow.}
|
|
if (vm_map_lookup_entry(map, addr, @prev_entry)) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
Exit(KERN_SUCCESS);
|
|
end;
|
|
|
|
next_entry:=prev_entry^.next;
|
|
if ((prev_entry^.eflags and MAP_ENTRY_GROWS_UP)=0) then
|
|
begin
|
|
{
|
|
* This entry does not grow upwards. Since the address lies
|
|
* beyond this entry, the next entry (if one exists) has to
|
|
* be a downward growable entry. The entry list header is
|
|
* never a growable entry, so it suffices to check the flags.
|
|
}
|
|
if ((next_entry^.eflags and MAP_ENTRY_GROWS_DOWN)=0) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
Exit(KERN_SUCCESS);
|
|
end;
|
|
stack_entry:=next_entry;
|
|
end else
|
|
begin
|
|
{
|
|
* This entry grows upward. If the next entry does not at
|
|
* least grow downwards, this is the entry we need to grow.
|
|
* otherwise we have two possible choices and we have to
|
|
* select one.
|
|
}
|
|
if ((next_entry^.eflags and MAP_ENTRY_GROWS_DOWN)<>0) then
|
|
begin
|
|
{
|
|
* We have two choices; grow the entry closest to
|
|
* the address to minimize the amount of growth.
|
|
}
|
|
if (addr - prev_entry^.__end<=next_entry^.start - addr) then
|
|
stack_entry:=prev_entry
|
|
else
|
|
stack_entry:=next_entry;
|
|
|
|
end else
|
|
begin
|
|
stack_entry:=prev_entry;
|
|
end;
|
|
end;
|
|
|
|
if (stack_entry=next_entry) then
|
|
begin
|
|
Assert((stack_entry^.eflags and MAP_ENTRY_GROWS_DOWN<>0), 'foo');
|
|
Assert(addr<stack_entry^.start, 'foo');
|
|
|
|
if (prev_entry<>@map^.header) then
|
|
begin
|
|
__end:=prev_entry^.__end;
|
|
end else
|
|
begin
|
|
__end:=stack_entry^.start - stack_entry^.avail_ssize;
|
|
end;
|
|
|
|
grow_amount:=round_page(stack_entry^.start - addr);
|
|
max_grow:=stack_entry^.start - __end;
|
|
end else
|
|
begin
|
|
Assert((stack_entry^.eflags and MAP_ENTRY_GROWS_UP)<>0,'foo');
|
|
Assert(addr>=stack_entry^.__end, 'foo');
|
|
|
|
if (next_entry<>@map^.header) then
|
|
begin
|
|
__end:=next_entry^.start;
|
|
end else
|
|
begin
|
|
__end:=stack_entry^.__end + stack_entry^.avail_ssize;
|
|
end;
|
|
|
|
grow_amount:=round_page(addr + 1 - stack_entry^.__end);
|
|
max_grow:=__end - stack_entry^.__end;
|
|
end;
|
|
|
|
if (grow_amount>stack_entry^.avail_ssize) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
Exit(KERN_NO_SPACE);
|
|
end;
|
|
|
|
{
|
|
* If there is no longer enough space between the entries nogo, and
|
|
* adjust the available space. Note: this should only happen if the
|
|
* user has mapped into the stack area after the stack was created,
|
|
* and is probably an error.
|
|
*
|
|
* This also effectively destroys any guard page the user might have
|
|
* intended by limiting the stack size.
|
|
}
|
|
if (grow_amount + _stack_guard_page>max_grow) then
|
|
begin
|
|
stack_entry^.avail_ssize:=max_grow;
|
|
|
|
vm_map_unlock(map);
|
|
Exit(KERN_NO_SPACE);
|
|
end;
|
|
|
|
if (addr>=vm_offset_t(g_vmspace.vm_maxsaddr)) then
|
|
is_procstack:=1
|
|
else
|
|
is_procstack:=0;
|
|
|
|
{
|
|
* If this is the main process stack, see if we're over the stack
|
|
* limit.
|
|
}
|
|
if ((is_procstack<>0) and (ctob(g_vmspace.vm_ssize) + grow_amount>stacklim)) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
Exit(KERN_NO_SPACE);
|
|
end;
|
|
|
|
{ Round up the grow amount modulo sgrowsiz }
|
|
growsize:=sgrowsiz;
|
|
grow_amount:=roundup(grow_amount, growsize);
|
|
if (grow_amount>stack_entry^.avail_ssize) then
|
|
begin
|
|
grow_amount:=stack_entry^.avail_ssize;
|
|
end;
|
|
if (is_procstack<>0) and (ctob(g_vmspace.vm_ssize) + grow_amount>stacklim) then
|
|
begin
|
|
grow_amount:=trunc_page(stacklim) - ctob(g_vmspace.vm_ssize);
|
|
end;
|
|
|
|
{ If we would blow our VMEM resource limit, no go }
|
|
if (map^.size + grow_amount>vmemlim) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
rv:=KERN_NO_SPACE;
|
|
goto _out;
|
|
end;
|
|
|
|
if (stack_entry=next_entry) then
|
|
begin
|
|
{
|
|
* Growing downward.
|
|
}
|
|
{ Get the preliminary new entry start value }
|
|
addr:=stack_entry^.start - grow_amount;
|
|
|
|
{
|
|
* If this puts us into the previous entry, cut back our
|
|
* growth to the available space. Also, see the note above.
|
|
}
|
|
if (addr<__end) then
|
|
begin
|
|
stack_entry^.avail_ssize:=max_grow;
|
|
addr:=__end;
|
|
if (stack_guard_page<>0) then
|
|
begin
|
|
addr:=addr+PAGE_SIZE;
|
|
end;
|
|
end;
|
|
|
|
rv:=vm_map_insert(map, nil, 0, addr, stack_entry^.start,
|
|
next_entry^.protection, next_entry^.max_protection, 0);
|
|
|
|
{ Adjust the available stack space by the amount we grew. }
|
|
if (rv=KERN_SUCCESS) then
|
|
begin
|
|
if (prev_entry<>@map^.header) then
|
|
begin
|
|
vm_map_clip_end(map, prev_entry, addr);
|
|
end;
|
|
new_entry:=prev_entry^.next;
|
|
Assert(new_entry=stack_entry^.prev, 'foo');
|
|
Assert(new_entry^.__end=stack_entry^.start, 'foo');
|
|
Assert(new_entry^.start=addr, 'foo');
|
|
grow_amount:=new_entry^.__end - new_entry^.start;
|
|
new_entry^.avail_ssize:=stack_entry^.avail_ssize - grow_amount;
|
|
stack_entry^.eflags:=stack_entry^.eflags and (not MAP_ENTRY_GROWS_DOWN);
|
|
new_entry^.eflags:=new_entry^.eflags or MAP_ENTRY_GROWS_DOWN;
|
|
end;
|
|
end else
|
|
begin
|
|
{
|
|
* Growing upward.
|
|
}
|
|
addr:=stack_entry^.__end + grow_amount;
|
|
|
|
{
|
|
* If this puts us into the next entry, cut back our growth
|
|
* to the available space. Also, see the note above.
|
|
}
|
|
if (addr>__end) then
|
|
begin
|
|
stack_entry^.avail_ssize:=__end - stack_entry^.__end;
|
|
addr:=__end;
|
|
if (stack_guard_page<>0) then
|
|
begin
|
|
addr:=addr-PAGE_SIZE;
|
|
end;
|
|
end;
|
|
|
|
grow_amount:=addr - stack_entry^.__end;
|
|
{ Grow the underlying object if applicable. }
|
|
|
|
if (stack_entry^.vm_obj=nil) then goto _or;
|
|
|
|
if vm_object_coalesce(stack_entry^.vm_obj,
|
|
stack_entry^.offset,
|
|
vm_size_t(stack_entry^.__end - stack_entry^.start),
|
|
vm_size_t(grow_amount), false) then
|
|
begin
|
|
_or:
|
|
map^.size:=map^.size+(addr - stack_entry^.__end);
|
|
{ Update the current entry. }
|
|
stack_entry^.__end:=addr;
|
|
stack_entry^.avail_ssize:=stack_entry^.avail_ssize-grow_amount;
|
|
vm_map_entry_resize_free(map, stack_entry);
|
|
rv:=KERN_SUCCESS;
|
|
|
|
if (next_entry<>@map^.header) then
|
|
begin
|
|
vm_map_clip_start(map, next_entry, addr);
|
|
end;
|
|
end else
|
|
begin
|
|
rv:=KERN_FAILURE;
|
|
end;
|
|
end;
|
|
|
|
if (rv=KERN_SUCCESS) and (is_procstack<>0) then
|
|
begin
|
|
g_vmspace.vm_ssize:=g_vmspace.vm_ssize+btoc(grow_amount);
|
|
end;
|
|
|
|
vm_map_unlock(map);
|
|
|
|
_out:
|
|
|
|
Result:=rv;
|
|
end;
|
|
|
|
function vmspace_exec(minuser,maxuser:vm_offset_t):Integer;
|
|
begin
|
|
Assert((curkthread^.td_pflags and TDP_EXECVMSPC)=0, 'vmspace_exec recursed');
|
|
|
|
//if (p=curkthread^.td_proc) then
|
|
//begin
|
|
// pmap_activate(curthread);
|
|
//end;
|
|
|
|
curkthread^.td_pflags:=curkthread^.td_pflags or TDP_EXECVMSPC;
|
|
|
|
Exit(0);
|
|
end;
|
|
|
|
{
|
|
* vm_map_lookup:
|
|
*
|
|
* Finds the VM object, offset, and
|
|
* protection for a given virtual address in the
|
|
* specified map, assuming a page fault of the
|
|
* type specified.
|
|
*
|
|
* Leaves the map in question locked for read; return
|
|
* values are guaranteed until a vm_map_lookup_done
|
|
* call is performed. Note that the map argument
|
|
* is in/out; the returned map must be used in
|
|
* the call to vm_map_lookup_done.
|
|
*
|
|
* A handle (out_entry) is returned for use in
|
|
* vm_map_lookup_done, to make that fast.
|
|
*
|
|
* If a lookup is requested with "write protection"
|
|
* specified, the map may be changed to perform virtual
|
|
* copying operations, although the data referenced will
|
|
* remain the same.
|
|
}
|
|
function vm_map_lookup(var_map :p_vm_map_t; { IN/OUT }
|
|
vaddr :vm_offset_t;
|
|
fault_typea:vm_prot_t;
|
|
out_entry :p_vm_map_entry_t; { OUT }
|
|
vm_obj :p_vm_object_t; { OUT }
|
|
pindex :p_vm_pindex_t; { OUT }
|
|
out_prot :p_vm_prot_t { OUT }
|
|
):Integer;
|
|
label
|
|
RetryLookup;
|
|
var
|
|
entry:vm_map_entry_t;
|
|
map:vm_map_t;
|
|
prot:vm_prot_t;
|
|
fault_type:vm_prot_t;
|
|
size:vm_size_t;
|
|
old_map:vm_map_t;
|
|
begin
|
|
map:=var_map^;
|
|
fault_type:=fault_typea;
|
|
|
|
RetryLookup:
|
|
|
|
vm_map_lock(map);
|
|
|
|
{
|
|
* Lookup the faulting address.
|
|
}
|
|
if (not vm_map_lookup_entry(map, vaddr, out_entry)) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
Exit(KERN_INVALID_ADDRESS);
|
|
end;
|
|
|
|
entry:=out_entry^;
|
|
|
|
{
|
|
* Handle submaps.
|
|
}
|
|
if ((entry^.eflags and MAP_ENTRY_IS_SUB_MAP)<>0) then
|
|
begin
|
|
old_map:=map;
|
|
|
|
map:=vm_map_t(entry^.vm_obj);
|
|
var_map^:=map;
|
|
vm_map_unlock(old_map);
|
|
goto RetryLookup;
|
|
end;
|
|
|
|
{
|
|
* Check whether this task is allowed to have this page.
|
|
}
|
|
prot:=entry^.protection;
|
|
fault_type:=fault_type and (VM_PROT_READ or VM_PROT_WRITE or VM_PROT_EXECUTE);
|
|
if ((fault_type and prot)<>fault_type) or (prot=VM_PROT_NONE) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
Exit(KERN_PROTECTION_FAILURE);
|
|
end;
|
|
Assert(((prot and VM_PROT_WRITE)=0) or (
|
|
(entry^.eflags and (MAP_ENTRY_NEEDS_COPY))<>
|
|
(MAP_ENTRY_NEEDS_COPY)
|
|
),'entry %p flags %x');
|
|
if ((fault_typea and VM_PROT_COPY)<>0) and
|
|
((entry^.max_protection and VM_PROT_WRITE)=0) and
|
|
((entry^.eflags and MAP_ENTRY_COW)=0) then
|
|
begin
|
|
vm_map_unlock(map);
|
|
Exit(KERN_PROTECTION_FAILURE);
|
|
end;
|
|
|
|
size:=entry^.__end - entry^.start;
|
|
{
|
|
* If the entry was copy-on-write, we either ...
|
|
}
|
|
if ((entry^.eflags and MAP_ENTRY_NEEDS_COPY)<>0) then
|
|
begin
|
|
{
|
|
* If we want to write the page, we may as well handle that
|
|
* now since we've got the map locked.
|
|
*
|
|
* If we don't need to write the page, we just demote the
|
|
* permissions allowed.
|
|
}
|
|
if ((fault_type and VM_PROT_WRITE)<>0) or
|
|
((fault_typea and VM_PROT_COPY)<>0) then
|
|
begin
|
|
{
|
|
* Make a new object, and place it in the object
|
|
* chain. Note that no new references have appeared
|
|
* -- one just moved from the map to the new
|
|
* object.
|
|
}
|
|
|
|
//vm_object_shadow(@entry^.vm_obj, @entry^.offset, size);
|
|
|
|
entry^.eflags:=entry^.eflags and (not MAP_ENTRY_NEEDS_COPY);
|
|
end else
|
|
begin
|
|
{
|
|
* We're attempting to read a copy-on-write page --
|
|
* don't allow writes.
|
|
}
|
|
prot:=prot and (not VM_PROT_WRITE);
|
|
end;
|
|
end;
|
|
|
|
{
|
|
* Return the object/offset from this entry. If the entry was
|
|
* copy-on-write or empty, it has been fixed up.
|
|
}
|
|
pindex^:=OFF_TO_IDX((vaddr - entry^.start) + entry^.offset);
|
|
vm_obj^:=entry^.vm_obj;
|
|
|
|
out_prot^:=prot;
|
|
Result:=(KERN_SUCCESS);
|
|
end;
|
|
|
|
{
|
|
* vm_map_lookup_locked:
|
|
*
|
|
* Lookup the faulting address. A version of vm_map_lookup that returns
|
|
* KERN_FAILURE instead of blocking on map lock or memory allocation.
|
|
}
|
|
function vm_map_lookup_locked(var_map :p_vm_map_t; { IN/OUT }
|
|
vaddr :vm_offset_t;
|
|
fault_typea:vm_prot_t;
|
|
out_entry :p_vm_map_entry_t; { OUT }
|
|
vm_obj :p_vm_object_t; { OUT }
|
|
pindex :p_vm_pindex_t; { OUT }
|
|
out_prot :p_vm_prot_t; { OUT }
|
|
wired :PBoolean { OUT }
|
|
):Integer;
|
|
var
|
|
entry:vm_map_entry_t;
|
|
map:vm_map_t;
|
|
prot:vm_prot_t;
|
|
fault_type:vm_prot_t;
|
|
eobject:vm_object_t;
|
|
size:vm_size_t;
|
|
old_map:vm_map_t;
|
|
begin
|
|
map:=var_map^;
|
|
fault_type:=fault_typea;
|
|
|
|
{
|
|
* Lookup the faulting address.
|
|
}
|
|
if (not vm_map_lookup_entry(map, vaddr, out_entry)) then
|
|
begin
|
|
Exit(KERN_INVALID_ADDRESS);
|
|
end;
|
|
|
|
entry:=out_entry^;
|
|
|
|
{
|
|
* Fail if the entry refers to a submap.
|
|
}
|
|
if ((entry^.eflags and MAP_ENTRY_IS_SUB_MAP)<>0) then
|
|
begin
|
|
Exit(KERN_FAILURE);
|
|
end;
|
|
|
|
{
|
|
* Check whether this task is allowed to have this page.
|
|
}
|
|
prot:=entry^.protection;
|
|
fault_type:=fault_type and (VM_PROT_READ or VM_PROT_WRITE or VM_PROT_EXECUTE);
|
|
if ((fault_type and prot)<>fault_type) then
|
|
begin
|
|
Exit(KERN_PROTECTION_FAILURE);
|
|
end;
|
|
|
|
if ((entry^.eflags and MAP_ENTRY_NEEDS_COPY)<>0) then
|
|
begin
|
|
{
|
|
* Fail if the entry was copy-on-write for a write fault.
|
|
}
|
|
if ((fault_type and VM_PROT_WRITE)<>0) then
|
|
begin
|
|
Exit(KERN_FAILURE);
|
|
end;
|
|
{
|
|
* We're attempting to read a copy-on-write page --
|
|
* don't allow writes.
|
|
}
|
|
prot:=prot and (not VM_PROT_WRITE);
|
|
end;
|
|
|
|
{
|
|
* Fail if an object should be created.
|
|
}
|
|
if (entry^.vm_obj=nil) then
|
|
begin
|
|
Exit(KERN_FAILURE);
|
|
end;
|
|
|
|
{
|
|
* Return the object/offset from this entry. If the entry was
|
|
* copy-on-write or empty, it has been fixed up.
|
|
}
|
|
pindex^:=OFF_TO_IDX((vaddr - entry^.start) + entry^.offset);
|
|
vm_obj^:=entry^.vm_obj;
|
|
|
|
out_prot^:=prot;
|
|
Result:=(KERN_SUCCESS);
|
|
end;
|
|
|
|
{
|
|
* vm_map_lookup_done:
|
|
*
|
|
* Releases locks acquired by a vm_map_lookup
|
|
* (according to the handle returned by that lookup).
|
|
}
|
|
procedure vm_map_lookup_done(map:vm_map_t;entry:vm_map_entry_t);
|
|
begin
|
|
{
|
|
* Unlock the main-level map
|
|
}
|
|
vm_map_unlock(map);
|
|
end;
|
|
|
|
procedure vm_map_set_name_locked(map:vm_map_t;start,__end:vm_offset_t;name:PChar);
|
|
var
|
|
current:vm_map_entry_t;
|
|
entry:vm_map_entry_t;
|
|
begin
|
|
VM_MAP_RANGE_CHECK(map, start, __end);
|
|
|
|
|
|
if (vm_map_lookup_entry(map, start,@entry)) then
|
|
begin
|
|
vm_map_clip_start(map, entry, start);
|
|
end else
|
|
begin
|
|
entry:=entry^.next;
|
|
end;
|
|
|
|
current:=entry;
|
|
while ((current<>@map^.header) and (current^.start<__end)) do
|
|
begin
|
|
vm_map_clip_end(map,current,__end);
|
|
MoveChar0(name^,current^.name,32);
|
|
|
|
vm_map_simplify_entry(map, current);
|
|
|
|
current:=current^.next;
|
|
end;
|
|
end;
|
|
|
|
procedure vm_map_set_name(map:vm_map_t;start,__end:vm_offset_t;name:PChar);
|
|
begin
|
|
vm_map_lock(map);
|
|
vm_map_set_name_locked(map,start,__end,name);
|
|
vm_map_unlock(map);
|
|
end;
|
|
|
|
procedure vminit;
|
|
var
|
|
i:Integer;
|
|
begin
|
|
vmspace_alloc(PROC_IMAGE_AREA_START,VM_MAXUSER_ADDRESS);
|
|
|
|
//exclude addr
|
|
if Length(exclude_mem)<>0 then
|
|
begin
|
|
vm_map_lock(@g_vmspace.vm_map);
|
|
For i:=0 to High(exclude_mem) do
|
|
begin
|
|
vm_map_insert (@g_vmspace.vm_map, nil, 0, exclude_mem[i].start, exclude_mem[i].__end, 0, 0, -1);
|
|
vm_map_set_name(@g_vmspace.vm_map, exclude_mem[i].start, exclude_mem[i].__end, '(exclude)');
|
|
end;
|
|
vm_map_unlock(@g_vmspace.vm_map);
|
|
end;
|
|
end;
|
|
|
|
end.
|
|
|