windows-nt/Source/XPSP1/NT/base/mvdm/wow16/kernel31/3galloc.asm

1745 lines
67 KiB
NASM
Raw Normal View History

2020-09-26 03:20:57 -05:00
PAGE ,132
TITLE GALLOC - Global memory allocator
.sall
.xlist
include kernel.inc
include protect.inc
include wowcmpat.inc
.list
.386
DataBegin
externB Kernel_flags
externB fBooting
externB fCheckFree
externW pGlobalHeap
externW Win386_Blocks
externW FreeArenaCount
gsearch_state_machine dw 0
gsearch_compact_first dw 0
public ffixedlow
ffixedlow db 0
DataEnd
sBegin CODE
assumes CS,CODE
externNP gcompact
externNP gmovebusy
externNP gslide
externNP galign
externNP genter
externNP gleave
ifdef WOW
externFP MyGetAppWOWCompatFlagsEx
endif
externNP gwin386discard
externNP GetDPMIFreeSpace
externNP InnerShrinkHeap
externNP get_physical_address
externNP set_physical_address
externNP alloc_arena_header
externNP free_arena_header
ifndef WOW_x86
externNP get_blotto
endif
externNP PreallocArena
externNP DPMIProc
;-----------------------------------------------------------------------;
; gsearch ;
; ;
; Searches from start to finish for a free global object to allocate ;
; space from. For a fixed request it tries to get the space as low as ;
; possible, moving movable out of the way if neccessary. For movable ;
; requests it also starts at the bottom. For discardable code it ;
; starts from the top, only using the first free block it finds. ;
; If at first blush it can't find a block it compacts memory and tries ;
; again. ;
; When it finally finds a block it gsplices it in, makes sure the ;
; arena headers are fine, and returns the allocated block. ;
; Called from within the global memory manager's critical section. ;
; ;
; Arguments: ;
; AX = allocations flags ;
; EBX = #bytes ;
; CX = owner field value ;
; DS:DI = address of global arena information structure ;
; ;
; Returns: ;
; AX = data address of block allocated or NULL ;
; BX = ga_prev or ga_next ;
; DX = allocation flags ;
; ;
; Error Returns: ;
; ZF = 1 ;
; AX = 0 ;
; BX = ga_prev or ga_next ;
; DX = size of largest free block ;
; ;
; Registers Preserved: ;
; DI,DS ;
; ;
; Registers Destroyed: ;
; CX,SI,ES ;
; ;
; Calls: ;
; galign ;
; gfindfree ;
; fmovebusy ;
; gcheckfree ;
; gcompact ;
; gsplice ;
; gzero ;
; gmarkfree ;
; ;
; History: ;
; ;
; ;
; 19-Aug-95 davehart: Win 3.1 tries to grow the heap before ;
; compacting it, for WOW we want to compact first to be a good ;
; multitasking neighbor. ;
; ;
; Wed Jul 22, 1987 11:15:19p -by- David N. Weise [davidw] ;
; Fixed BOGUS BLOCK freeing yet again. ;
; ;
; Sun May 10, 1987 11:29:38p -by- David N. Weise [davidw] ;
; Added the state machine to handle the case of wanting to search ;
; both global arenas. ;
; ;
; Sat Feb 28, 1987 06:31:11p -by- David N. Weise [davidw] ;
; Putting in support for allocating discardable code from EEMS land. ;
; ;
; Tue Dec 30, 1986 01:54:50p -by- David N. Weise [davidw] ;
; Made sure it freed any bogus blocks created. ;
; ;
; Thu Nov 20, 1986 04:00:06p -by- David N. Weise [davidw] ;
; Rewrote it use the global free list. Also made it put fixed requests ;
; as low as possible and to search again after a compact. ;
; ;
; Mon Nov 17, 1986 11:41:49a -by- David N. Weise [davidw] ;
; Added support for the free list of global partitions. ;
; ;
; Tue Sep 23, 1986 04:35:39p -by- David N. Weise [davidw] ;
; Added this nifty comment block. ;
;-----------------------------------------------------------------------;
assumes ds,nothing
assumes es,nothing
cProc gsearch,<PUBLIC,NEAR>
cBegin nogen
CheckKernelDS fs
ReSetKernelDS fs
cmp word ptr [di].gi_free_count, 96
jae short compact_first
mov gsearch_compact_first, 0
mov gsearch_state_machine,codeOFFSET grow_heap
jmp short look_again
compact_first:
mov gsearch_compact_first, 1
mov gsearch_state_machine,codeOFFSET do_compact_nodiscard
look_again:
push ebx ; Save requested size
push cx ; Save owner
push ax ; Save flags
clc ; Instead of add
call galign ; Get requested size in EDX
push edx ; Save adjusted requested size
; see if there is any one free space large enough for the request first
mov cx,[di].gi_free_count
jcxz were_hosed_from_start
mov esi,[di].phi_first
is_there_one:
mov esi,ds:[esi].pga_freenext
cmp edx,ds:[esi].pga_size
jbe short got_one
loop is_there_one
were_hosed_from_start:
jmp space_not_found
got_one:
mov ebx,pga_prev ; search backwards
test al,GA_ALLOCHIGH
jz short alloc_low
;------ allocate disc code -------
public alloc_high
alloc_high:
mov cx,[di].gi_free_count
mov esi,[di].phi_last ; Start with last entry.
alloc_high_loop:
mov esi,ds:[esi].pga_freeprev
cmp edx,ds:[esi].pga_size
jbe afound ; Yes, exit search loop
loop alloc_high_loop
jmp space_not_found
;------ allocate moveable ---------
public alloc_low
alloc_low:
mov ebx,pga_next ; Search forwards.
test al,GA_MOVEABLE
jz short alloc_fixed
call gcheckfree ; Room for requested size?
jb space_not_found
jmp afound
;------ allocate fixed ------------
public alloc_fixed
alloc_fixed:
mov esi,[di].phi_first ; Start with first entry.
mov cx,[di].hi_count
mov esi,ds:[esi+ebx] ; Skip first sentinel
; maybe ALLOC_DOS or regular fixed.
test ah, GA_ALLOC_DOS
jnz alloc_fixed_loop
test fBooting, 2 ; are we past KERNEL loading
jnz alloc_fixed_loop ; N: give him under 1MB line
; check to see if we want old behaviour
; this is to fix some mm drivers that mmsystem.dll will load
test ffixedlow, 1 ; want FIXED under 1MB?
jnz alloc_fixed_loop ; Y: give him under 1MB line
; start fixed blocks > 1MB. This will leave room for GlobalDOSAllocs
; Memory < 1MB will also get used for moveable blocks as we start
; from phi_first for these. These can be moved out of the way when
; we need to alloc GA_DOS_ALLOC requests.
fast_forward_to_1MB_line: ; need to store this ptr
; for speed?
cmp ds:[esi].pga_address, 100000h ; > 1Mb?
jae skip_not_there ; OK for fixed allocs now
mov esi,ds:[esi+ebx]
loop fast_forward_to_1MB_Line
; when here it means we have no free blocks > 1MB!!!
; but have a block < 1MB. space_not_found will discard
; enough to move blocks up to generate room for this fixed blk.
jmp space_not_found
skip_not_there:
if KDEBUG
cmp [esi].pga_owner, GA_NOT_THERE
je @f
krDebugOut <DEB_ERROR OR DEB_KrMemMan>, "NOT_THERE block not there!"
@@:
endif
mov esi,ds:[esi+ebx]
alloc_fixed_loop:
push cx
push esi
call is_there_theoretically_enough_space
cmp eax,edx
jb short nope
pop esi
pop cx
call can_we_clear_this_space
jz short anext1
call gcheckfree ; Yes, room for requested size?
jb short anext1
; Now we have enough free space,
; slide it back as far as possible.
push eax ; This is to prevent us blocking
push edx ; the growth of moveable blocks.
push ebx ; AND to keep pagelocked code together
call PreallocArena
jz short no_sliding
mov ebx, pga_prev ; Sliding backwards
keep_sliding:
call gslide
jnz keep_sliding
no_sliding:
pop ebx
pop edx
pop eax
pop edx
pop cx
test ch, GA_ALLOC_DOS
push cx
push edx
jz afound
cmp ds:[esi].pga_address, 100000h ; > 1Mb?
jb afound
jmp gsearch_fail
nope:
or eax,eax
jz short hosed_again
anext:
add sp, 6 ; get rid of CX, ESI on the stack
anext1:
mov esi,ds:[esi+ebx]
loop alloc_fixed_loop
jmps and_again
hosed_again:
pop esi
pop cx
and_again:
; no one space big enough, try compacting
public space_not_found
space_not_found:
pop edx ; get adjusted size
pop ax ; get flags
push ax
push edx
; test al,GA_ALLOCHIGH
; jnz short ask_for_what_we_need
; add edx,0400h ; ask for 1k more
; jnc short ask_for_what_we_need ; no overflow
; mov edx,-1
ask_for_what_we_need:
jmp gsearch_state_machine
;------------------------------
public do_compact_nodiscard ; for debugging
do_compact_nodiscard:
mov gsearch_state_machine,codeOFFSET grow_heap
;
; Before growing the heap try compacting without
; discarding. This step isn't executed unless there
; were 96 or more free blocks at entry to gsearch.
;
test ds:[di].gi_cmpflags, GA_NODISCARD
jnz short dcn_nodiscard
or ds:[di].gi_cmpflags, GA_NODISCARD
call gcompact
and ds:[di].gi_cmpflags, NOT GA_NODISCARD
jmp short over_compact
dcn_nodiscard:
call gcompact
jmp short over_compact
public do_compact ; for debugging
do_compact:
mov gsearch_state_machine,codeOFFSET gsearch_fail
;
; If we tried compacting before and GA_NODISCARD was set,
; there is no need to compact again, since we already
; compacted with GA_NODISCARD before attempting to
; grow the heap. If GA_NODISCARD was not set, our earlier
; compact forced it on, so it's worth trying again since
; we may be able to discard enough to satisfy the request.
;
cmp gsearch_compact_first, 0
je short @f
test ds:[di].gi_cmpflags, GA_NODISCARD
jnz short gsearch_fail
@@:
call gcompact
over_compact:
pop edx
pop ax
pop cx
pop ebx
jmp look_again
public grow_heap ; for debugging
grow_heap:
mov gsearch_state_machine,codeOFFSET do_compact
push edx ; can we get the space from DPMI?
call GrowHeap
pop edx
jnc short over_compact ; heap grew, go look again
call InnerShrinkHeap ; try to give back DPMI blocks so DPMI
; mmgr can defragment its memory
jz short do_compact ; heap did not shrink
push edx ; gave some back, try to get it again
call GrowHeap
pop edx
jnc short over_compact ; heap grew, go look again
doomed:
;------------------------------
public gsearch_fail ; for debugging
gsearch_fail: ; get size of largest free block
.errnz doomed-gsearch_fail
xor edx,edx
mov cx,[di].gi_free_count
jcxz gs_failure
mov esi,[di].phi_first
largest_loop:
mov esi,ds:[esi].pga_freenext
mov eax,ds:[esi].pga_size
cmp edx,eax
jae short new_smaller
mov edx,eax
new_smaller:
loop largest_loop
gs_failure:
pop eax ; adjusted requested size
pop ax ; AX = flags
pop cx ; CX = owner field
pop eax ; waste requested size
xor eax,eax ; Return zero, with ZF = 1
ret
; Here when we have a block big enough.
; ES:DI = address of block
; AX = size of block, including header
; DX = requested size, including header
; BX = ga_prev if backwards search and ga_next if forwards search
afound:
mov ecx,ds:[esi].pga_size ; Use actual size of free block
sub ecx,edx ; (found size - requested size)
jecxz no_arena_needed
call PreallocArena
jnz short no_arena_needed
;
; Detect infinite loop here. If we are out of arenas, and
; the compact failed once, then another one isn't going to do much
; good.
;
cmp gsearch_state_machine,codeOFFSET gsearch_fail
je gsearch_fail
pop edx ; get adjusted size
pop ax ; get flags
push ax
push edx
and ds:[di].gi_cmpflags, NOT (GA_NODISCARD+GA_NOCOMPACT)
or ds:[di].gi_cmpflags, COMPACT_ALLOC
mov edx, -1 ; Discard the world!
jmp do_compact
no_arena_needed:
mov eax,ds:[esi].pga_freeprev
call gdel_free ; remove the alloc block from freelist
jecxz aexitx
cmp bl,pga_prev ; Yes, scanning forwards or backwards?
je short abackward ; Backwards.
call gsplice ; FS:ESI = block we are allocating
jmps aexit ; EDX = block to mark as free
abackward:
neg edx
add edx,ds:[esi].pga_size ; Scanning backwards. Put extra space
call gsplice
xchg edx, esi
jmps aexit
; Here with allocated block
; AX = data address or zero if nothing allocated
; ES:DI = address of block to mark as busy and zero init if requested
; EDX = address of block to mark as free
aexitx:
xor edx,edx ; Assume nothing extra to free
aexit:
pop ecx ; waste adjusted requested size
pop cx ; Restore flags
pop ds:[esi].pga_owner ; Mark block as busy with owner field value
add sp, 4 ; waste requested size
mov ds:[esi].pga_lruprev,edi
mov ds:[esi].pga_lrunext,edi
push esi
mov esi, edx ; Free any extra space
mov edx, eax ; Previous free block
call gmarkfree
pop esi
mov dx, cx
mov al,GA_SEGTYPE
and al,dl
test dh,GAH_NOTIFY
jz short no_notify
or al,GAH_NOTIFY
no_notify:
mov ds:[esi].pga_flags,al ; Store segment type bits
mov eax,esi ; AX = address of client data
test cl,GA_ZEROINIT ; Want it zeroed?
jz short aexit1 ; No, all done
push eax
ifdef WOW_x86
;; On NT we try never to set selectors when we don't need to since it is a
;; slow operation - a system call. In this case we can use selector 23h
;; which points to all flat vdm memory as data
push es
push bx
push edi
mov bx,FLAT_SEL
mov es,bx
mov ecx,ds:[esi].pga_size ; Yes, zero paragraphs
push ecx
shr ecx, 2 ; # dwords to clear
mov edi, ds:[esi].pga_address
xor eax, eax
cld
rep stos dword ptr es:[edi]
pop ecx
pop edi
pop bx
pop es
else
cCall get_blotto
mov ecx,ds:[esi].pga_size ; Yes, zero paragraphs
push bx
mov bx,ax ; from beginning of client data
call gzero ; zero them
pop bx
endif; WOW_x86
pop eax
aexit1:
or eax,eax
ret ; Return AX points to client portion
UnSetKernelDS FS ; of block allocated.
cEnd nogen
;------------------------------------------------------------------
;
; ChangeAllocFixedBehaviour
; GlobalAlloc(FIXED) used to return address < 1MB if possible in 3.1
; It doesn't anymore in Chicago. You need to call this API if you
; want the old behaviour. Bad things will happen if you switch to old
; behaviour and forget to switch it back.
; MMSYSTEM.DLL loads some drivers that may expect this behaviour and
; they are the only callers of this fun. at the time of its writing.
; ENTRY = flags
; 0 = chicago behaviour
; 1 = win31 behaviour
; EXIT
; old bahaviour before this change (can be used to restore)
;
;------------------------------------------------------------------
cProc ChangeAllocFixedBehaviour,<PUBLIC,FAR>
parmW flags
cBegin
GENTER32
CheckKernelDS FS
ReSetKernelDS fs
mov ax, flags
xchg al, ffixedlow
GLEAVE32
cEnd
cProc GrowHeap,<PUBLIC,NEAR>
cBegin nogen
CheckKernelDS FS
ReSetKernelDS fs
cmp FreeArenaCount, 4 ; 3 for below, 1 for a gsplice later
jb short gh_fail
pushad
push edx ; Save requested size
; If they want more than 64k, assume they
; want a big block and just allocate that amount.
cmp edx, 64*1024 ; Want more than 64k?
jae short ask_for_it ; yes, just round up
mov edx, 128*1024 ; no, try for 128k
ask_for_it:
mov ebx, edx
add ebx, 4096-1 ; Round up to 4k multiple
and bx, NOT (4096-1)
cCall MyGetAppWOWCompatFlagsEx ; check if we need to pad it
test ax, WOWCFEX_BROKENFLATPOINTER
jz short @f
add ebx, 4096*4 ; make it a bit bigger
@@:
push ebx ; Length we will ask for
mov cx, bx
shr ebx, 16
DPMICALL 0501h ; Allocate Memory Block
; Get our memory
jnc short got_more_memory
pop ebx ; Toss length we asked for
; Couldn't get our 1st choice, how
call GetDPMIFreeSpace ; much is available
mov ebx, eax ; ebx = largest available
pop edx ; Requested size
cmp ebx, edx ; Enough for request?
jbe SHORT gh_fail_pop
push edx ; Expected on stack below
push ebx ; Length we will ask for
mov cx, bx
shr ebx, 16
DPMICALL 0501h ; Allocate Memory Block
; Get our memory
jnc short got_more_memory
add sp, 8 ; Toss requested size & len asked for
gh_fail_pop:
popad
gh_fail:
stc ; No chance mate!
ret
; Now we have a new block
; Sort it in to the heap
; Create NOT THERE blocks to
got_more_memory: ; bracket the new block
inc Win386_Blocks
pop edx ; Length of block allocated
if KDEBUG
mov eax, edx
shr eax, 16
krDebugOut <DEB_TRACE OR DEB_KrMemMan>, "GrowHeap: #ax#DX allocated"
endif
shl ebx, 16
mov bx, cx ; EBX has linear address
pop ecx ; Toss original length
shl esi, 16
mov si, di ; ESI has WIN386 handle
xor edi, edi
cCall alloc_arena_header,<ebx>
mov ecx, eax ; First not there arena
mov [ecx].pga_size, edi
mov [ecx].pga_sig, GA_SIGNATURE
mov [ecx].pga_owner, GA_NOT_THERE
mov [ecx].pga_handle, di
mov [ecx].pga_flags, di
mov [ecx].pga_lrunext, esi ; Save WIN386 handle here
mov [ecx].pga_lruprev, edi
cCall alloc_arena_header,<ebx>
mov [eax].pga_size, edx ; Free block
push ebx ; Save address
add edx, ebx ; Address of end of block
mov ebx, eax
mov [ecx].pga_next, ebx
mov [ebx].pga_prev, ecx
mov [ebx].pga_owner, di
cCall alloc_arena_header,<edx>
mov edx, eax
mov [ebx].pga_next, edx
mov [edx].pga_prev, ebx
mov [edx].pga_size, edi
mov [edx].pga_owner, GA_NOT_THERE
mov [edx].pga_handle, di
mov [edx].pga_flags, di
mov [edx].pga_sig, GA_SIGNATURE
mov [edx].pga_lrunext, edi
mov [edx].pga_lruprev, edi
pop eax ; Address of block
sort_it:
mov esi, [edi].phi_first
cmp eax, [esi].pga_address ; Below start of heap?
ja short sort_loop ; no, sort it in
; int 3 ; [this code never reached]
mov [esi].pga_address, eax ; yes, adjust sentinel
jmps link_it_in ; Sentinel now points to new block
sort_loop:
mov esi, [esi].pga_next
cmp [esi].pga_next, esi ; At end?
je short sort_found ; yes, put here
cmp [esi].pga_owner, GA_NOT_THERE
jne short sort_loop
mov esi, [esi].pga_prev ; Will go after previous block.
sort_found: ; Block will go after ESI
cmp [esi].pga_next, esi ; This the sentinel?
jne short link_it_in ; no, link it in
mov eax, [edx].pga_address ; yes, adjust sentinel
mov [esi].pga_address, eax
sub eax, [di].gi_reserve ; Adjust fence
mov [di].gi_disfence_lo, ax
shr eax, 16
mov [di].gi_disfence_hi, ax
mov esi, [esi].pga_prev ; New block goes before sentinel
link_it_in: ; Link it in after ESI
mov [ecx].pga_prev, esi
xchg [esi].pga_next, ecx
mov [edx].pga_next, ecx
mov [ecx].pga_prev, edx
add [di].hi_count, 3 ; Three more entries in heap
mov esi, ebx
xor edx, edx
call gmarkfree ; To be picked up next time around
popad
clc
ret
UnSetKernelDS FS
cEnd nogen
; Input - DWORD - Old Arena offset from burgermaster
;
; Output - None
cProc FreeHeapDib,<PUBLIC,FAR>
parmD OldArena
cBegin
CheckKernelDS FS
ReSetKernelDS fs
; Make sure arena before and after are GA_NOT_THERE
mov ebx,OldArena
mov edx,ds:[ebx].pga_prev
cmp ds:[edx].pga_owner,GA_NOT_THERE
jne short fhd5
mov ecx,ds:[ebx].pga_next
cmp ds:[ecx].pga_owner,GA_NOT_THERE
je short fhd7
fhd5:
if KDEBUG
krDebugOut <DEB_TRACE OR DEB_KrMemMan>, "FreeHeapDIB: Corrupt DIB Block"
endif
fhd7:
;Free all the three arenas. First fixup the arena list.
; edx - First GA_NOT_THERE arena
; ebx - Actual DIB arean
; ecx - Last GA_NOT_THERE arena
mov eax,ds:[edx].pga_prev
mov ebx,ds:[ecx].pga_next
mov ds:[eax].pga_next,ebx
mov ds:[ebx].pga_prev,eax
mov ds:[edx].pga_handle,0
cCall free_arena_header,<edx>
mov ds:[ecx].pga_handle,0
cCall free_arena_header,<ecx>
mov edx,OldArena
mov ds:[edx].pga_handle,0
cCall free_arena_header,<edx>
xor di,di
sub [di].hi_count, 3 ; Three less entries in heap
dec Win386_Blocks
UnSetKernelDS FS
cEnd
; Input - DWORD - Dib Address
; DWORD - Old Arena offset from burgermaster
;
; Output - eax = new arena if operation successful
; eax = NULL if operation failed
cProc GrowHeapDib,<PUBLIC,FAR>
parmD OldArena
parmD NewAddress
cBegin
CheckKernelDS FS
ReSetKernelDS fs
cmp FreeArenaCount, 4 ; 3 for below, 1 for a gsplice later
jae short ghd_start
xor eax,eax
ret
; Now we have a new block. Sort it in to the heap. Create
; NOT THERE blocks as well.
ghd_start:
inc Win386_Blocks
mov ebx,OldArena
mov edx,ds:[ebx].pga_size ; Length of block
if KDEBUG
mov eax, edx
shr eax, 16
krDebugOut <DEB_TRACE OR DEB_KrMemMan>, "GrowHeapDIB: #ax#DX allocated"
endif
mov ebx,NewAddress ; Ebx is the address of new block
mov esi, ebx ; ESI has WIN386 handle
xor edi, edi
cCall alloc_arena_header,<ebx>
mov ecx, eax ; First not there arena
mov [ecx].pga_size, edi
mov [ecx].pga_sig, GA_SIGNATURE
mov [ecx].pga_owner, GA_NOT_THERE
mov [ecx].pga_handle, di
mov [ecx].pga_flags, di
mov [ecx].pga_lrunext, esi ; Save WIN386 handle here
mov [ecx].pga_lruprev, edi
cCall alloc_arena_header,<ebx>
mov [eax].pga_size, edx ; DIB block
push ebx ; Save address
add edx, ebx ; Address of end of block
mov ebx, eax
mov [ecx].pga_next, ebx
mov [ebx].pga_prev, ecx
push ecx
mov ecx,OldArena
mov ax, [ecx].pga_handle
mov [ebx].pga_handle,ax
mov ax, [ecx].pga_owner
mov [ebx].pga_owner,ax
mov al, [ecx].pga_count
mov [ebx].pga_count,al
inc [ebx].pga_count ; make sure it doesn't move
mov al, [ecx].pga_pglock
mov [ebx].pga_pglock,al
mov al, [ecx].pga_flags
mov [ebx].pga_flags,al
mov al, [ecx].pga_selcount
mov [ebx].pga_selcount,al
mov [ebx].pga_lrunext, edi
mov [ebx].pga_lruprev, edi
pop ecx
cCall alloc_arena_header,<edx>
mov edx, eax
mov [ebx].pga_next, edx
mov [edx].pga_prev, ebx
mov [edx].pga_size, edi
mov [edx].pga_owner, GA_NOT_THERE
mov [edx].pga_handle, di
mov [edx].pga_flags, di
mov [edx].pga_sig, GA_SIGNATURE
mov [edx].pga_lrunext, edi
mov [edx].pga_lruprev, edi
pop eax ; Address of block
mov esi, [edi].phi_first
cmp eax, [esi].pga_address ; Below start of heap?
ja short ghd_sort_loop ; no, sort it in
; int 3 ; [this code never reached]
mov [esi].pga_address, eax ; yes, adjust sentinel
jmps ghd_link_it_in ; Sentinel now points to new block
ghd_sort_loop:
mov esi, [esi].pga_next
cmp [esi].pga_next, esi ; At end?
je short ghd_sort_found ; yes, put here
cmp [esi].pga_owner, GA_NOT_THERE
jne short ghd_sort_loop
mov esi, [esi].pga_prev ; Will go after previous block.
ghd_sort_found: ; Block will go after ESI
cmp [esi].pga_next, esi ; This the sentinel?
jne short ghd_link_it_in ; no, link it in
mov eax, [edx].pga_address ; yes, adjust sentinel
mov [esi].pga_address, eax
sub eax, [di].gi_reserve ; Adjust fence
mov [di].gi_disfence_lo, ax
shr eax, 16
mov [di].gi_disfence_hi, ax
mov esi, [esi].pga_prev ; New block goes before sentinel
ghd_link_it_in: ; Link it in after ESI
mov [ecx].pga_prev, esi
xchg [esi].pga_next, ecx
mov [edx].pga_next, ecx
mov [ecx].pga_prev, edx
add [di].hi_count, 3 ; Three more entries in heap
mov eax,ebx
UnSetKernelDS FS
cEnd
;-----------------------------------------------------------------------;
; is_there_theoretically_enough_space
;
; Starting at the given arena checks to see if there are enough
; continuous free and unlocked moveable blocks.
;
; Entry:
; CX = arenas to search
; EDX = size requested
; DS = BurgerMaster
; FS:ESI = arena to start from
;
; Returns:
; EAX = 0 => not enough space and no more memory left to search
; EAX = 1 => not enough space in this block, but maybe....
; otherwise
; EAX = size of block
;
; Registers Destroyed:
; CX,ESI
;
; Registers Preserved:
; BX,DX,DI,ES
;
; History:
; Mon 05-Sep-1988 15:21:14 -by- David N. Weise [davidw]
; Moved it here from gsearch so that grealloc could use it as well.
;-----------------------------------------------------------------------;
assumes ds,nothing
assumes es,nothing
cProc is_there_theoretically_enough_space,<PUBLIC,NEAR>
cBegin nogen
xor eax,eax
ittes:
cmp ds:[esi].pga_owner,di
jne short is_it_moveable
add eax,ds:[esi].pga_size
push ebx
push eax
mov bx,word ptr [di].gi_disfence_hi ; See if begin of reserve area
shl ebx, 16
mov bx,word ptr [di].gi_disfence_lo ; is above end of free block
mov eax, ds:[esi].pga_address
add eax, ds:[esi].pga_size ; Address of end of free block
sub eax,ebx
ja short ittes_above_fence ; All below fence?
ittes_below_fence:
pop eax ; yes, we can use it
pop ebx
jmps this_ones_free
ittes_above_fence:
cmp eax, ds:[di].gi_reserve
jae ittes_below_fence
mov ebx, eax ; portion above the fence
pop eax ; Total size so far
sub eax,ebx ; No, Reduce apparent size of free block
pop ebx
cmp eax,edx
jae short theoretically_enough
jmps absolutely_not
is_it_moveable:
cmp ds:[esi].pga_owner,GA_NOT_THERE ; Against end of heap partition?
je short theoretically_not ; no room here.
test ds:[esi].pga_handle,GA_FIXED ; See if movable.
jnz short theoretically_not
cmp ds:[esi].pga_count,0
jne short theoretically_not ; See if locked.
add eax,ds:[esi].pga_size
this_ones_free:
cmp eax,edx
jae short theoretically_enough
mov esi,ds:[esi].pga_next
loop ittes
; For the case of gsearch we should never get here for two reasons.
; 1) It should be impossible to have no discardable code loaded, in
; this case we would have failed in the above loop. 2) We checked
; at the very start for a free block somewhere that could have
; satisfied the request. In our mucking around to load as low as
; possible we destroyed this free block and we did not produce a free
; block we could use. However we know from debugging code in 2.03
; that this happens extremely rarely. Because of the rareness of
; this event we will not try to recover, instead we simply fail the call.
absolutely_not:
mov eax,-1 ; return EAX = 0
theoretically_not:
inc eax ; DX is even, => cmp the same.
theoretically_enough:
ret
cEnd nogen
;-----------------------------------------------------------------------;
; can_we_clear_this_space
;
; Attempts to make a free space starting at the address moved in.
; To do this it moves moveable out of the area. The only subtlety
; involves a free block that stradles the end of wanted area. This
; may get broken into two pieces, the lower piece gets temporary marked
; as allocated and BOGUS, the upper piece will remain free.
;
; Entry:
; CX = max number of blocks to look at
; EDX = size wanted in bytes
; DS = BurgerMaster
; FS:ESI = beginning arena
;
; Returns:
; ZF = 0
; FS:ESI points to free space
; ZF = 1
; couldn't free the space up
;
; Registers Destroyed:
; AX,SI
;
; History:
; Mon 05-Sep-1988 16:48:31 -by- David N. Weise [davidw]
; Moved it out of gsearch so that grealloc could use it.
;-----------------------------------------------------------------------;
assumes ds,nothing
assumes es,nothing
cProc can_we_clear_this_space,<PUBLIC,NEAR>
cBegin nogen
CheckKernelDS FS
ReSetKernelDS FS
push ecx
push esi
cmp di,[di].gi_free_count ; any free blocks?
jz short cwcts_fail
mov eax, esi ; Beginning of space we want.
cmp di,ds:[esi].pga_owner ; Is it free?
jnz short can_we_move_it
mov ecx,edx
cmp ecx,ds:[esi].pga_size
ja short asdf
or eax,eax ; return ZF = 0
cwcts_fail:
pop esi
pop ecx
ret
asdf: mov esi,ds:[esi].pga_next
public can_we_move_it
can_we_move_it:
push ebx
push ecx
push edx
push esi
cmp ds:[esi].pga_owner,GA_BOGUS_BLOCK
jnz short not_bogus
xor edx,edx
call gmarkfree
jmp restart
not_bogus:
mov ebx, ds:[eax].pga_address
add ebx, edx ; EBX is the end of the space we want
mov cx,[di].gi_free_count
mov edx,ds:[esi].pga_size ; Yes, try to find a place for the
mov esi,[di].phi_first ; moveable block
look_loop:
call PreAllocArena ; Needed for gmovebusy or gsplice
jz couldnt_clear_it
mov esi,ds:[esi].pga_freenext
push esi
mov esi, ds:[esi].pga_address
cmp esi, ds:[eax].pga_address ; It defeats our purpose to move the
jb short check_this_out ; block to a free space we want.
cmp esi, ebx
jb short is_there_hope
check_this_out:
pop esi
push eax
call gcheckfree
push ecx
jb short inopportune_free_space
pop ecx
pop eax
pop edx ; EDX = moveable block for gmovebusy
mov ebx,pga_next
call gmovebusy ; Move moveable block out of the way
mov esi,edx ; Replace the ESI on the stack,
; the free block may have grown
; downward with the gmovebusy.
pop edx
pop ecx
pop ebx
pop ecx ; WAS pop esi but esi set above now
pop ecx
jmp can_we_clear_this_space
inopportune_free_space:
pop ecx
pop eax
loop look_loop
jmps couldnt_clear_it
public is_there_hope
is_there_hope:
pop esi
push eax
push ecx
mov ecx, ds:[esi].pga_address
add ecx, ds:[esi].pga_size ; ECX end of block
mov ax, [di].gi_disfence_hi
shl eax, 16
mov ax, [di].gi_disfence_lo ; EAX == fence
sub eax, ecx ; Fence - End
jae short below_reserved ; Block is below fence
neg eax ; End - Fence
cmp eax, ds:[di].gi_reserve
jae short below_reserved ; Block is above reserved
sub ecx, eax ; End - (End - Fence)
; Gives Fence in ECX
below_reserved:
sub ecx, ebx ; Adjust size of free block
jbe inopportune_free_space ; No room here
overlap:
cmp ecx,edx ; Is it big enough?
jbe short inopportune_free_space
mov edx, ebx
sub edx, ds:[esi].pga_address ; Calculate overlap
pop ecx
pop eax
; cut off the first piece for the original alloc
push ds:[esi].pga_freeprev
call gdel_free
call gsplice
; DS:ESI = addr of block to mark as busy, FS:EDX = addr of block to mark as free
mov ds:[esi].pga_owner,GA_BOGUS_BLOCK
mov ds:[esi].pga_lruprev,edi
mov ds:[esi].pga_lrunext,edi
mov esi, edx
pop edx ; previous free block
call gmarkfree ; Free any extra space
restart:
pop edx ; WAS pop es
pop edx
pop ecx
pop ebx
pop esi
pop ecx
jmp can_we_clear_this_space
; If here then failure! see if we made a bogus block!
couldnt_clear_it:
pop esi ; recover block we wanted moved
check_again:
mov edx,ds:[esi].pga_next
cmp edx, ds:[edx].pga_next ; At end of arenas?
je short no_bogus_block ; Yes, let's go
cmp ds:[edx].pga_address, ebx ; EBX points to where bogus block
ja short no_bogus_block ; would be.
je short is_it_bogus
mov esi,edx
jmps check_again
is_it_bogus:
cmp ds:[esi].pga_owner,GA_BOGUS_BLOCK
jnz short no_bogus_block
xor edx,edx
call gmarkfree
no_bogus_block:
if KDEBUG
mov cx,[di].hi_count
mov esi,[di].phi_first
bogus_all:
cmp ds:[esi].pga_owner,GA_BOGUS_BLOCK
jnz short not_me
int 3
not_me: mov esi,ds:[esi].pga_next
loop bogus_all
endif
pop edx
pop ecx
pop ebx
pop esi
pop ecx
xor eax,eax ; return ZF = 1
ret
UnSetKernelDS FS
cEnd nogen
;-----------------------------------------------------------------------;
; gcheckfree ;
; ;
; Checks the size of the passed free block against the passed desired ;
; size, making sure that the limitations of the code reserve area are ;
; not violated for objects other than discardable code. It also checks ;
; for Phantoms. ;
; ;
; Arguments: ;
; FS:ESI = address of free block ;
; DX = #bytes needed ;
; DS:DI = address of global arena information structure ;
; ;
; Returns: ;
; CF = 0 block big enough ;
; EAX = apparent size of free block ;
; ;
; Error Returns: ;
; none ;
; ;
; Registers Preserved: ;
; All ;
; ;
; Registers Destroyed: ;
; ;
; Calls: ;
; nothing ;
; ;
; History: ;
; ;
; Thu 27-Apr-1989 10:38:05 -by- David N. Weise [davidw] ;
; Fixed this to work in pmode. ;
; ;
; Thu Apr 02, 1987 10:45:22p -by- David N. Weise [davidw] ;
; Added Phantom support. ;
; ;
; Tue Sep 23, 1986 05:54:51p -by- David N. Weise [davidw] ;
; Added this nifty comment block. ;
;-----------------------------------------------------------------------;
cProc gcheckfree,<PUBLIC,NEAR>
cBegin nogen
mov eax,ds:[esi].pga_size ; Compute size of free block
test byte ptr [di].gi_cmpflags,GA_DISCCODE
jnz short gcftest ; Discardable code not restricted
push ebx
mov ebx, [di].phi_last ; Last sentinel
mov ebx, ds:[ebx].pga_address
cmp ebx, ds:[esi].pga_address ; Above sentinel?
jbe short gcftest1 ; yes, not in reserved area!
push eax
mov bx,word ptr [di].gi_disfence_hi ; See if begin of reserve area
shl ebx, 16
mov bx,word ptr [di].gi_disfence_lo ; is above end of free block
add eax, ds:[esi].pga_address ; EAX address of end of block
sub ebx, eax
pop eax
jae short gcftest1 ; Yes, return actual size of free block
neg ebx
sub eax,ebx ; No, Reduce apparent size of free block
ja short gcftest1 ; Is it more than what is free?
xor eax,eax ; Yes, then apparent size is zero
; Nothing left, set apparent size to 0
gcftest1:
pop ebx
jmps gcftest
gcfrsrv1: ; Yes, then apparent size is zero
xor eax,eax ; Nothing left, set apparent size to 0
gcftest:
cmp eax,edx ; Return results of the comparison
ret
cEnd nogen
;-----------------------------------------------------------------------;
; gsplice [should be named gslice, ;
; since splice means combine two into one - donc] ;
; ;
; Splits one block into two. ;
; ;
; Arguments: ;
; EDX = size in bytes of new block to make ;
; DS:ESI = address of existing block ;
; DS:DI = address of global arena information structure ;
; ;
; Returns: ;
; EDX = address of new block
; ;
; Error Returns: ;
; nothing ;
; ;
; Registers Preserved: ;
; AX,BX,DX,DI,DS,ES ;
; ;
; Registers Destroyed: ;
; CX ;
; ;
; Calls: ;
; nothing ;
; History: ;
; ;
; Tue Sep 23, 1986 03:50:30p -by- David N. Weise [davidw] ;
; Added this nifty comment block. ;
;-----------------------------------------------------------------------;
cProc gsplice,<PUBLIC,NEAR>,<EAX,EBX>
cBegin
mov ebx,ds:[esi].pga_size
sub ebx,edx ; get size of 2nd new block made
mov eax, ds:[esi].pga_address
add eax, edx
cCall alloc_arena_header,<eax>
inc [di].hi_count ; Adding new arena entry
mov ecx, eax
xchg ds:[esi].pga_next,ecx ; and old.next = new
mov ds:[ecx].pga_prev,eax ; [old old.next].prev = new
mov ds:[eax].pga_next,ecx ; new.next = old old.next
mov ds:[eax].pga_prev,esi
mov ds:[eax].pga_size,ebx
mov ds:[eax].pga_sig,GA_SIGNATURE
mov ds:[eax].pga_owner,di ; Zero owner & handle fields
mov ds:[eax].pga_flags,0 ; For good measure.
mov ds:[eax].pga_handle,di
mov ds:[esi].pga_size,edx
mov edx, eax
gsplice_ret:
cEnd
;-----------------------------------------------------------------------;
; gjoin ;
; ;
; Merges a block into his previous neighbor. ;
; ;
; Arguments: ;
; FS:ESI = address of block to remove ;
; DS:DI = address of global arena information structure ;
; ;
; Returns: ;
; nothing ;
; ;
; Error Returns: ;
; nothing ;
; ;
; Registers Preserved: ;
; AX,BX,CX,DX,DI,SI,DS,ES ;
; ;
; Registers Destroyed: ;
; SI ;
; ;
; Calls: ;
; gdel_free ;
; ;
; History: ;
; ;
; Mon Nov 17, 1986 11:41:49a -by- David N. Weise [davidw] ;
; Added support for the free list of global partitions. ;
; ;
; Tue Sep 23, 1986 03:58:00p -by- David N. Weise [davidw] ;
; Added this nifty comment block. ;
;-----------------------------------------------------------------------;
cProc gjoin,<PUBLIC,NEAR>
cBegin nogen
if KDEBUG
cmp esi, ds:[esi].pga_prev
jne short ok
int 3
int 3
ok:
endif
push eax ; assumes one is on freelist
push edx
dec [di].hi_count
call gdel_free
mov eax,ds:[esi].pga_size
mov edx,ds:[esi].pga_next ; Get address of block after
mov esi,ds:[esi].pga_prev ; Get address of block before
cCall free_arena_header,<ds:[edx].pga_prev> ; Free arena being removed
mov ds:[edx].pga_prev,esi ; Fix up block after
mov ds:[esi].pga_next, edx ; and the one before
add ds:[esi].pga_size,eax ; Recompute size of block
pop edx
pop eax
ret
cEnd nogen
;-----------------------------------------------------------------------;
; gzero ;
; ;
; Fills the given area with zeros. ;
; ;
; Arguments: ;
; BX = address of first paragraph ;
; ECX = Bytes to clear ;
; ;
; Returns: ;
; BX = 0 ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; AX,DX,DI,SI,DS,ES ;
; ;
; Registers Destroyed: ;
; CX ;
; Calls: ;
; nothing ;
; ;
; History: ;
; ;
; Tue Sep 23, 1986 04:08:55p -by- David N. Weise [davidw] ;
; Added this nifty comment block. ;
;-----------------------------------------------------------------------;
cProc gzero,<PUBLIC,NEAR>
cBegin nogen
; Assumptions: on entry, BX contains selector to start of block, and is
; for a scratch descriptor that can be modified. ECX contains the # of
; bytes to be zeroed.
push es
push eax
push edi
push ecx
shr ecx, 2 ; # dwords to clear
mov es, bx
xor eax, eax
xor edi, edi
cld
rep stos dword ptr es:[edi]
pop ecx
pop edi
pop eax
pop es
ret
cEnd nogen
;-----------------------------------------------------------------------;
; gmarkfree ;
; ;
; Marks a block as free, coalesceing it with any free blocks before ;
; or after it. This does not free any handles. ;
; ;
; Arguments: ;
; EDX = the first free object before this one ;
; 0 if unknown ;
; Ring 1 if no free list update wanted ;
; Ring 0 if free list update required ;
; FS:ESI = block to mark as free. ;
; DS:DI = address of global arena information structure ;
; ;
; Returns: ;
; ZF = 1 if freed a fixed block ;
; DX = 0 ;
; ZF = 0 if freed a moveable block ;
; DX = handle table entry ;
; FS:ESI = block freed (may have been coalesced) ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; AX,BX,CX,DX,DS ;
; ;
; Registers Destroyed: ;
; none ;
; ;
; Calls: ;
; gjoin ;
; gadd_free ;
; ;
; History: ;
; ;
; Mon Nov 17, 1986 11:41:49a -by- David N. Weise [davidw] ;
; Added support for the free list of global partitions. ;
; ;
; Sun Nov 09, 1986 01:35:08p -by- David N. Weise [davidw] ;
; Made the debugging version fill all free space with CCCC. ;
; ;
; Wed Sep 24, 1986 10:27:06p -by- David N. Weise [davidw] ;
; Added this nifty comment block. ;
;-----------------------------------------------------------------------;
cProc gmarkfree,<PUBLIC,NEAR>
cBegin nogen
call gadd_free
or esi,esi
jz short gmf_exit
; Mark this block as free by clearing the owner field.
mov ds:[esi].pga_sig,GA_SIGNATURE
mov ds:[esi].pga_owner,di ; Mark as free
mov ds:[esi].pga_flags,0 ; For good measure.
; Remember the handle value in DX, before setting to zero.
xor dx,dx
xchg ds:[esi].pga_handle,dx
; Try to coalesce with next block, if it is free
push ds:[esi].pga_prev ; save previous block
mov esi,ds:[esi].pga_next ; ESI = next block
cmp ds:[esi].pga_owner,di ; Is it free?
jne short free2 ; No, continue
call gjoin ; Yes, coalesce with block we are freeing
free2:
pop esi ; ESI = previous block
cmp ds:[esi].pga_owner,di ; Is it free?
jne short free3 ; No, continue
mov esi,ds:[esi].pga_next ; Yes, coalesce with block we are freeing;
call gjoin
free3:
cmp ds:[esi].pga_owner,di ; Point to free block?
je short free4 ; Yes, done
mov esi,ds:[esi].pga_next ; No, leave ES pointing at free block
free4:
call gwin386discard
gmf_exit:
or dx,dx
ret
cEnd nogen
;-----------------------------------------------------------------------;
; gadd_free ;
; ;
; Links in the given partition into the global free list. ;
; ;
; Arguments: ;
; EDX = the first free object before this one ;
; 0 if unknown ;
; odd if no free list update wanted ;
; DS:DI = BurgerMaster ;
; FS:ESI = free global object to add ;
; ;
; Returns: ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; all ;
; ;
; Registers Destroyed: ;
; ;
; Calls: ;
; nothing ;
; ;
; History: ;
; ;
; Sun Nov 09, 1986 02:42:53p -by- David N. Weise [davidw] ;
; Wrote it. ;
;-----------------------------------------------------------------------;
cProc gadd_free,<PUBLIC,NEAR>
cBegin nogen
push eax
push edx
test dl,1
jnz short no_update_wanted
or esi,esi ; this happens with gmovebusy
jz short no_update_wanted
inc [di].gi_free_count
mov edx, esi
need_a_home_loop:
mov edx, ds:[edx].pga_prev
cmp ds:[edx].pga_owner, di ; Found a free block?
je short found_a_home
cmp ds:[edx].pga_prev, edx ; Sentinel?
jne short need_a_home_loop
found_a_home:
mov eax, ds:[edx].pga_freenext
mov ds:[esi].pga_freenext, eax
mov ds:[esi].pga_freeprev, edx
mov ds:[edx].pga_freenext, esi
mov ds:[eax].pga_freeprev, esi
if KDEBUG
call check_free_list
endif
gaf_exit:
no_update_wanted:
pop edx
pop eax
ret
cEnd nogen
;-----------------------------------------------------------------------;
; gdel_free ;
; ;
; Removes a partition from the global free list. ;
; ;
; Arguments: ;
; DS:DI = BurgerMaster ;
; FS:ESI = arena header of partition ;
; ;
; Returns: ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; All ;
; ;
; Registers Destroyed: ;
; ;
; Calls: ;
; nothing ;
; ;
; History: ;
; ;
; Sun Nov 09, 1986 02:43:26p -by- David N. Weise [davidw] ;
; Wrote it. ;
;-----------------------------------------------------------------------;
cProc gdel_free,<PUBLIC,NEAR>
cBegin nogen
push edi
push esi
mov edi,ds:[esi].pga_freeprev
mov esi,ds:[esi].pga_freenext
mov ds:[edi].pga_freenext,esi
mov ds:[esi].pga_freeprev,edi
pop esi
pop edi
dec [di].gi_free_count
if KDEBUG
call check_free_list
endif
ret
cEnd nogen
if KDEBUG
;-----------------------------------------------------------------------;
; check_free_list ;
; ;
; Checks the global free list for consistency. ;
; ;
; Arguments: ;
; none ;
; ;
; Returns: ;
; ;
; Error Returns: ;
; ;
; Registers Preserved: ;
; All ;
; ;
; Registers Destroyed: ;
; ;
; Calls: ;
; nothing ;
; ;
; History: ;
; ;
; Wed Oct 29, 1986 10:13:42a -by- David N. Weise [davidw] ;
; Wrote it. ;
;-----------------------------------------------------------------------;
cProc check_free_list,<PUBLIC,NEAR>
cBegin nogen
push eax
push ebx
push cx
push esi
push ds
SetKernelDS
cmp fCheckFree,0
jnz short cfl_outta_here
test Kernel_flags,kf_check_free
jnz short cfl_check_it_out
cfl_outta_here:
jmp all_done
cfl_check_it_out:
mov ds,pGlobalHeap
UnSetKernelDS
mov esi,[di].phi_first
mov cx,[di].gi_free_count
or cx,cx
jz all_done
mov eax, ds:[esi].pga_freenext
mov esi, eax
check_chain_loop:
mov ebx,ds:[esi].pga_freeprev
mov esi,ds:[esi].pga_freenext
cmp ds:[ebx].pga_freenext,eax
jz short prev_okay
prev_bad:
push esi
mov esi, eax
kerror 0FFh,<free_list: prev bad>,bx,si
mov eax, esi
pop esi
prev_okay:
cmp ds:[esi].pga_freeprev,eax
jnz short next_bad
mov ebx,esi
jmps next_okay
next_bad:
mov bx, ax
kerror 0FFh,<free_list: next bad>,bx,es
next_okay:
mov eax,esi
loop check_chain_loop
SetKernelDS
mov ds,pGlobalHeap
UnSetKernelDS
cmp [di].phi_last,eax
jz short all_done
mov bx, ax
kerror 0FFh,<free_list: count bad>,[di].phi_last,bx
all_done:
pop ds
pop esi
pop cx
pop ebx
pop eax
ret
cEnd nogen
endif
cProc ValidateFreeSpaces,<PUBLIC,FAR>
cBegin nogen
ret
cEnd nogen
sEnd CODE
end