Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 4424 → Rev 4608

/kernel/trunk/core/mtrr.inc/memory.inc
1,6 → 1,6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Copyright (C) KolibriOS team 2004-2012. All rights reserved. ;;
;; Copyright (C) KolibriOS team 2004-2014. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License ;;
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7,1545 → 7,872
 
$Revision$
 
; Initializes MTRRs.
proc init_mtrr
 
align 4
proc alloc_page
cmp [BOOT_VARS+BOOT_MTRR], byte 2
je .exit
 
pushfd
cli
push ebx
;//-
cmp [pg_data.pages_free], 1
jle .out_of_memory
;//-
bt [cpu_caps], CAPS_MTRR
jnc .exit
 
mov ebx, [page_start]
mov ecx, [page_end]
.l1:
bsf eax, [ebx];
jnz .found
add ebx, 4
cmp ebx, ecx
jb .l1
pop ebx
popfd
xor eax, eax
ret
.found:
;//-
dec [pg_data.pages_free]
jz .out_of_memory
;//-
btr [ebx], eax
mov [page_start], ebx
sub ebx, sys_pgmap
lea eax, [eax+ebx*8]
shl eax, 12
;//- dec [pg_data.pages_free]
pop ebx
popfd
ret
;//-
.out_of_memory:
mov [pg_data.pages_free], 1
xor eax, eax
pop ebx
popfd
ret
;//-
endp
call mtrr_reconfigure
stdcall set_mtrr, [LFBAddress], 0x1000000, MEM_WC
 
align 4
proc alloc_pages stdcall, count:dword
pushfd
push ebx
push edi
cli
mov eax, [count]
add eax, 7
shr eax, 3
mov [count], eax
;//-
mov ebx, [pg_data.pages_free]
sub ebx, 9
js .out_of_memory
shr ebx, 3
cmp eax, ebx
jg .out_of_memory
;//-
mov ecx, [page_start]
mov ebx, [page_end]
.find:
mov edx, [count]
mov edi, ecx
.match:
cmp byte [ecx], 0xFF
jne .next
dec edx
jz .ok
inc ecx
cmp ecx, ebx
jb .match
.out_of_memory:
.fail:
xor eax, eax
pop edi
pop ebx
popfd
.exit:
ret
.next:
inc ecx
cmp ecx, ebx
jb .find
pop edi
pop ebx
popfd
xor eax, eax
ret
.ok:
sub ecx, edi
inc ecx
push esi
mov esi, edi
xor eax, eax
rep stosb
sub esi, sys_pgmap
shl esi, 3+12
mov eax, esi
mov ebx, [count]
shl ebx, 3
sub [pg_data.pages_free], ebx
pop esi
pop edi
pop ebx
popfd
ret
endp
 
align 4
;proc map_page stdcall,lin_addr:dword,phis_addr:dword,flags:dword
map_page:
push ebx
mov eax, [esp+12] ; phis_addr
and eax, not 0xFFF
or eax, [esp+16] ; flags
mov ebx, [esp+8] ; lin_addr
shr ebx, 12
mov [page_tabs+ebx*4], eax
mov eax, [esp+8] ; lin_addr
pop ebx
invlpg [eax]
ret 12
 
align 4
map_space: ;not implemented
 
 
; Helper procedure for mtrr_reconfigure and set_mtrr,
; called before changes in MTRRs.
proc mtrr_begin_change
mov eax, cr0
or eax, 0x60000000 ;disable caching
mov cr0, eax
wbinvd ;invalidate cache
ret
 
 
align 4
proc free_page
;arg: eax page address
pushfd
cli
shr eax, 12 ;page index
bts dword [sys_pgmap], eax ;that's all!
cmc
adc [pg_data.pages_free], 0
shr eax, 3
and eax, not 3 ;dword offset from page_map
add eax, sys_pgmap
cmp [page_start], eax
ja @f
popfd
ret
@@:
mov [page_start], eax
popfd
ret
endp
 
align 4
proc map_io_mem stdcall, base:dword, size:dword, flags:dword
 
push ebx
push edi
mov eax, [size]
add eax, [base]
add eax, 4095
and eax, -4096
mov ecx, [base]
and ecx, -4096
sub eax, ecx
mov [size], eax
 
stdcall alloc_kernel_space, eax
test eax, eax
jz .fail
push eax
 
mov edi, 0x1000
mov ebx, eax
mov ecx, [size]
mov edx, [base]
shr eax, 12
shr ecx, 12
and edx, -4096
or edx, [flags]
@@:
mov [page_tabs+eax*4], edx
invlpg [ebx]
inc eax
add ebx, edi
add edx, edi
loop @B
 
pop eax
mov edx, [base]
and edx, 4095
add eax, edx
.fail:
pop edi
pop ebx
; Helper procedure for mtrr_reconfigure and set_mtrr,
; called after changes in MTRRs.
proc mtrr_end_change
wbinvd ;again invalidate
mov eax, cr0
and eax, not 0x60000000
mov cr0, eax ; enable caching
ret
endp
 
; param
; eax= page base + page flags
; ebx= linear address
; ecx= count
; Some limits to number of structures located in the stack.
MAX_USEFUL_MTRRS = 16
MAX_RANGES = 16
 
align 4
commit_pages:
test ecx, ecx
jz .fail
; mtrr_reconfigure keeps a list of MEM_WB ranges.
; This structure describes one item in the list.
struct mtrr_range
next dd ? ; next item
start dq ? ; first byte
length dq ? ; length in bytes
ends
 
push edi
push eax
push ecx
mov ecx, pg_data.mutex
call mutex_lock
pop ecx
pop eax
 
mov edi, ebx
shr edi, 12
lea edi, [page_tabs+edi*4]
@@:
stosd
invlpg [ebx]
add eax, 0x1000
add ebx, 0x1000
loop @B
 
pop edi
 
mov ecx, pg_data.mutex
call mutex_unlock
.fail:
ret
 
 
; param
; eax= base
; ecx= count
 
uglobal
align 4
release_pages:
num_variable_mtrrs dd 0 ; number of variable-range MTRRs
endg
 
push ebp
push esi
push edi
push ebx
 
mov esi, eax
mov edi, eax
 
shr esi, 12
lea esi, [page_tabs+esi*4]
 
push ecx
mov ecx, pg_data.mutex
call mutex_lock
pop ecx
 
mov ebp, [pg_data.pages_free]
mov ebx, [page_start]
mov edx, sys_pgmap
@@:
; Helper procedure for MTRR initialization.
; Takes MTRR configured by BIOS and tries to recongifure them
; in order to allow non-UC data at top of 4G memory.
; Example: if low part of physical memory is 3.5G = 0xE0000000 bytes wide,
; BIOS can configure two MTRRs so that the first MTRR describes [0, 4G) as WB
; and the second MTRR describes [3.5G, 4G) as UC;
; WB+UC=UC, so the resulting memory map would be as needed,
; but in this configuration our attempts to map LFB at (say) 0xE8000000 as WC
; would be ignored, WB+UC+WC is still UC.
; So we must keep top of 4G memory not covered by MTRRs,
; using three WB MTRRs [0,2G) + [2G,3G) + [3G,3.5G),
; this gives the same memory map, but allows to add further entries.
; See mtrrtest.asm for detailed input/output from real hardware+BIOS.
proc mtrr_reconfigure
push ebp ; we're called from init_LFB, and it feels hurt when ebp is destroyed
; 1. Prepare local variables.
; 1a. Create list of MAX_RANGES free (aka not yet allocated) ranges.
xor eax, eax
xchg eax, [esi]
invlpg [edi]
lea ecx, [eax+MAX_RANGES]
.init_ranges:
sub esp, sizeof.mtrr_range - 4
push eax
mov eax, esp
dec ecx
jnz .init_ranges
mov eax, esp
; 1b. Fill individual local variables.
xor edx, edx
sub esp, MAX_USEFUL_MTRRS * 16 ; .mtrrs
push edx ; .mtrrs_end
push edx ; .num_used_mtrrs
push eax ; .first_free_range
push edx ; .first_range: no ranges yet
mov cl, [cpu_phys_addr_width]
or eax, -1
shl eax, cl ; note: this uses cl&31 = cl-32, not the entire cl
push eax ; .phys_reserved_mask
virtual at esp
.phys_reserved_mask dd ?
.first_range dd ?
.first_free_range dd ?
.num_used_mtrrs dd ?
.mtrrs_end dd ?
.mtrrs rq MAX_USEFUL_MTRRS * 2
.local_vars_size = $ - esp
end virtual
 
test eax, 1
jz .next
 
shr eax, 12
bts [edx], eax
cmc
adc ebp, 0
shr eax, 3
and eax, -4
add eax, edx
cmp eax, ebx
jae .next
 
; 2. Get the number of variable-range MTRRs from MTRRCAP register.
; Abort if zero.
mov ecx, 0xFE
rdmsr
test al, al
jz .abort
mov byte [num_variable_mtrrs], al
; 3. Validate MTRR_DEF_TYPE register.
mov ecx, 0x2FF
rdmsr
; If BIOS has not initialized variable-range MTRRs, fallback to step 7.
test ah, 8
jz .fill_ranges_from_memory_map
; If the default memory type (not covered by MTRRs) is not UC,
; then probably BIOS did something strange, so it is better to exit immediately
; hoping for the best.
cmp al, MEM_UC
jnz .abort
; 4. Validate all variable-range MTRRs
; and copy configured MTRRs to the local array [.mtrrs].
; 4a. Prepare for the loop over existing variable-range MTRRs.
mov ecx, 0x200
lea edi, [.mtrrs]
.get_used_mtrrs_loop:
; 4b. For every MTRR, read PHYSBASEn and PHYSMASKn.
; In PHYSBASEn, clear upper bits and copy to ebp:ebx.
rdmsr
or edx, [.phys_reserved_mask]
xor edx, [.phys_reserved_mask]
mov ebp, edx
mov ebx, eax
.next:
add edi, 0x1000
add esi, 4
loop @B
 
mov [pg_data.pages_free], ebp
mov ecx, pg_data.mutex
call mutex_unlock
 
pop ebx
pop edi
pop esi
pop ebp
ret
 
; param
; eax= base
; ecx= count
 
align 4
unmap_pages:
 
push edi
 
inc ecx
; If PHYSMASKn is not active, ignore this MTRR.
rdmsr
inc ecx
test ah, 8
jz .get_used_mtrrs_next
; 4c. For every active MTRR, check that number of local entries is not too large.
inc [.num_used_mtrrs]
cmp [.num_used_mtrrs], MAX_USEFUL_MTRRS
ja .abort
; 4d. For every active MTRR, store PHYSBASEn with upper bits cleared.
; This contains the MTRR base and the memory type in low byte.
mov [edi], ebx
mov [edi+4], ebp
; 4e. For every active MTRR, check that the range is continuous:
; PHYSMASKn with upper bits set must be negated power of two, and
; low bits of PHYSBASEn must be zeroes:
; PHYSMASKn = 1...10...0,
; PHYSBASEn = x...x0...0,
; this defines a continuous range from x...x0...0 to x...x1...1,
; length = 10...0 = negated PHYSMASKn.
; Store length in the local array.
and eax, not 0xFFF
or edx, [.phys_reserved_mask]
mov dword [edi+8], 0
mov dword [edi+12], 0
sub [edi+8], eax
sbb [edi+12], edx
; (x and -x) is the maximum power of two that divides x.
; Condition for powers of two: (x and -x) equals x.
and eax, [edi+8]
and edx, [edi+12]
cmp eax, [edi+8]
jnz .abort
cmp edx, [edi+12]
jnz .abort
sub eax, 1
sbb edx, 0
and eax, not 0xFFF
and eax, ebx
jnz .abort
and edx, ebp
jnz .abort
; 4f. For every active MTRR, validate memory type: it must be either WB or UC.
add edi, 16
cmp bl, MEM_UC
jz .get_used_mtrrs_next
cmp bl, MEM_WB
jnz .abort
.get_used_mtrrs_next:
; 4g. Repeat the loop at 4b-4f for all [num_variable_mtrrs] entries.
mov eax, [num_variable_mtrrs]
lea eax, [0x200+eax*2]
cmp ecx, eax
jb .get_used_mtrrs_loop
; 4h. If no active MTRRs were detected, fallback to step 7.
cmp [.num_used_mtrrs], 0
jz .fill_ranges_from_memory_map
mov [.mtrrs_end], edi
; 5. Generate sorted list of ranges marked as WB.
; 5a. Prepare for the loop over configured MTRRs filled at step 4.
lea ecx, [.mtrrs]
.fill_wb_ranges:
; 5b. Ignore non-WB MTRRs.
mov ebx, [ecx]
cmp bl, MEM_WB
jnz .next_wb_range
mov ebp, [ecx+4]
and ebx, not 0xFFF ; clear memory type and reserved bits
; ebp:ebx = start of the range described by the current MTRR.
; 5c. Find the first existing range containing a point greater than ebp:ebx.
lea esi, [.first_range]
.find_range_wb:
; If there is no next range or start of the next range is greater than ebp:ebx,
; exit the loop to 5d.
mov edi, [esi]
test edi, edi
jz .found_place_wb
mov eax, ebx
mov edx, ebp
sub eax, dword [edi+mtrr_range.start]
sbb edx, dword [edi+mtrr_range.start+4]
jb .found_place_wb
; Otherwise, if end of the next range is greater than or equal to ebp:ebx,
; exit the loop to 5e.
mov esi, edi
sub eax, dword [edi+mtrr_range.length]
sbb edx, dword [edi+mtrr_range.length+4]
jb .expand_wb
or eax, edx
jnz .find_range_wb
jmp .expand_wb
.found_place_wb:
; 5d. ebp:ebx is not within any existing range.
; Insert a new range between esi and edi.
; (Later, during 5e, it can be merged with the following ranges.)
mov eax, [.first_free_range]
test eax, eax
jz .abort
mov [esi], eax
mov edx, [eax+mtrr_range.next]
mov [.first_free_range], edx
mov dword [eax+mtrr_range.start], ebx
mov dword [eax+mtrr_range.start+4], ebp
; Don't fill [eax+mtrr_range.next] and [eax+mtrr_range.length] yet,
; they will be calculated including merges at step 5e.
mov esi, edi
mov edi, eax
mov edx, eax
 
shr edi, 10
add edi, page_tabs
 
xor eax, eax
.expand_wb:
; 5e. The range at edi contains ebp:ebx, and esi points to the first range
; to be checked for merge: esi=edi if ebp:ebx was found in an existing range,
; esi is next after edi if a new range with ebp:ebx was created.
; Merge it with following ranges while start of the next range is not greater
; than the end of the new range.
add ebx, [ecx+8]
adc ebp, [ecx+12]
; ebp:ebx = end of the range described by the current MTRR.
.expand_wb_loop:
; If there is no next range or start of the next range is greater than ebp:ebx,
; exit the loop to 5g.
test esi, esi
jz .expand_wb_done
mov eax, ebx
mov edx, ebp
sub eax, dword [esi+mtrr_range.start]
sbb edx, dword [esi+mtrr_range.start+4]
jb .expand_wb_done
; Otherwise, if end of the next range is greater than or equal to ebp:ebx,
; exit the loop to 5f.
sub eax, dword [esi+mtrr_range.length]
sbb edx, dword [esi+mtrr_range.length+4]
jb .expand_wb_last
; Otherwise, the current range is completely within the new range.
; Free it and continue the loop.
mov edx, [esi+mtrr_range.next]
cmp esi, edi
jz @f
mov eax, [.first_free_range]
mov [esi+mtrr_range.next], eax
mov [.first_free_range], esi
@@:
stosd
invlpg [edx]
add edx, 0x1000
loop @b
 
pop edi
ret
 
 
align 4
proc map_page_table stdcall, lin_addr:dword, phis_addr:dword
push ebx
mov ebx, [lin_addr]
shr ebx, 22
mov eax, [phis_addr]
and eax, not 0xFFF
or eax, PG_UW ;+PG_NOCACHE
mov dword [master_tab+ebx*4], eax
mov eax, [lin_addr]
shr eax, 10
add eax, page_tabs
invlpg [eax]
pop ebx
ret
endp
 
align 4
proc init_LFB
locals
pg_count dd ?
endl
 
cmp dword [LFBAddress], -1
jne @f
mov [BOOT_VARS+BOOT_MTRR], byte 2
; max VGA=640*480*4=1228800 bytes
; + 32*640*4=81920 bytes for mouse pointer
stdcall alloc_pages, ((1228800+81920)/4096)
 
push eax
call alloc_page
stdcall map_page_table, LFB_BASE, eax
pop eax
or eax, PG_UW
mov ebx, LFB_BASE
; max VGA=640*480*4=1228800 bytes
; + 32*640*4=81920 bytes for mouse pointer
mov ecx, (1228800+81920)/4096
call commit_pages
mov [LFBAddress], dword LFB_BASE
ret
@@:
test [SCR_MODE], word 0100000000000000b
jnz @f
mov [BOOT_VARS+BOOT_MTRR], byte 2
ret
@@:
call init_mtrr
 
mov edx, LFB_BASE
mov esi, [LFBAddress]
mov edi, 0x00C00000
mov dword [exp_lfb+4], edx
 
shr edi, 12
mov [pg_count], edi
shr edi, 10
 
bt [cpu_caps], CAPS_PSE
jnc .map_page_tables
or esi, PG_LARGE+PG_UW
mov edx, sys_pgdir+(LFB_BASE shr 20)
@@:
mov [edx], esi
add edx, 4
add esi, 0x00400000
dec edi
jnz @B
 
bt [cpu_caps], CAPS_PGE
jnc @F
or dword [sys_pgdir+(LFB_BASE shr 20)], PG_GLOBAL
@@:
mov dword [LFBAddress], LFB_BASE
mov eax, cr3 ;flush TLB
mov cr3, eax
ret
 
.map_page_tables:
 
@@:
call alloc_page
stdcall map_page_table, edx, eax
add edx, 0x00400000
dec edi
jnz @B
 
mov eax, [LFBAddress]
mov edi, page_tabs + (LFB_BASE shr 10)
or eax, PG_UW
mov ecx, [pg_count]
cld
@@:
stosd
add eax, 0x1000
dec ecx
jnz @B
 
mov dword [LFBAddress], LFB_BASE
mov eax, cr3 ;flush TLB
mov cr3, eax
 
ret
endp
 
align 4
proc new_mem_resize stdcall, new_size:dword
 
push ebx
push esi
push edi
 
mov edx, [current_slot]
cmp [edx+APPDATA.heap_base], 0
jne .exit
 
mov edi, [new_size]
add edi, 4095
and edi, not 4095
mov [new_size], edi
 
mov esi, [edx+APPDATA.mem_size]
add esi, 4095
and esi, not 4095
 
cmp edi, esi
ja .expand
je .exit
 
mov ebx, edi
shr edi, 12
shr esi, 12
 
mov ecx, pg_data.mutex
call mutex_lock
@@:
mov eax, [app_page_tabs+edi*4]
test eax, 1
jz .next
 
mov dword [app_page_tabs+edi*4], 0
invlpg [ebx]
call free_page
 
.next:
inc edi
add ebx, 0x1000
cmp edi, esi
jb @B
 
mov ecx, pg_data.mutex
call mutex_unlock
 
.update_size:
mov edx, [current_slot]
mov ebx, [new_size]
call update_mem_size
.exit:
pop edi
pop esi
pop ebx
mov esi, edx
jmp .expand_wb_loop
.expand_wb_last:
; 5f. Start of the new range is inside range described by esi,
; end of the new range is inside range described by edi.
; If esi is equal to edi, the new range is completely within
; an existing range, so proceed to the next range.
cmp esi, edi
jz .next_wb_range
; Otherwise, set end of interval at esi to end of interval at edi
; and free range described by edi.
mov ebx, dword [esi+mtrr_range.start]
mov ebp, dword [esi+mtrr_range.start+4]
add ebx, dword [esi+mtrr_range.length]
adc ebp, dword [esi+mtrr_range.length+4]
mov edx, [esi+mtrr_range.next]
mov eax, [.first_free_range]
mov [esi+mtrr_range.next], eax
mov [.first_free_range], esi
mov esi, edx
.expand_wb_done:
; 5g. We have found the next range (maybe 0) after merging and
; the new end of range (maybe ebp:ebx from the new range
; or end of another existing interval calculated at step 5f).
; Write them to range at edi.
mov [edi+mtrr_range.next], esi
sub ebx, dword [edi+mtrr_range.start]
sbb ebp, dword [edi+mtrr_range.start+4]
mov dword [edi+mtrr_range.length], ebx
mov dword [edi+mtrr_range.length+4], ebp
.next_wb_range:
; 5h. Continue the loop 5b-5g over all configured MTRRs.
add ecx, 16
cmp ecx, [.mtrrs_end]
jb .fill_wb_ranges
; 6. Exclude all ranges marked as UC.
; 6a. Prepare for the loop over configured MTRRs filled at step 4.
lea ecx, [.mtrrs]
.fill_uc_ranges:
; 6b. Ignore non-UC MTRRs.
mov ebx, [ecx]
cmp bl, MEM_UC
jnz .next_uc_range
mov ebp, [ecx+4]
and ebx, not 0xFFF ; clear memory type and reserved bits
; ebp:ebx = start of the range described by the current MTRR.
lea esi, [.first_range]
; 6c. Find the first existing range containing a point greater than ebp:ebx.
.find_range_uc:
; If there is no next range, ignore this MTRR,
; exit the loop and continue to next MTRR.
mov edi, [esi]
test edi, edi
jz .next_uc_range
; If start of the next range is greater than or equal to ebp:ebx,
; exit the loop to 6e.
mov eax, dword [edi+mtrr_range.start]
mov edx, dword [edi+mtrr_range.start+4]
sub eax, ebx
sbb edx, ebp
jnb .truncate_uc
; Otherwise, continue the loop if end of the next range is less than ebp:ebx,
; exit the loop to 6d otherwise.
mov esi, edi
add eax, dword [edi+mtrr_range.length]
adc edx, dword [edi+mtrr_range.length+4]
jnb .find_range_uc
; 6d. ebp:ebx is inside (or at end of) an existing range.
; Split the range. (The second range, maybe containing completely within UC-range,
; maybe of zero length, can be removed at step 6e, if needed.)
mov edi, [.first_free_range]
test edi, edi
jz .abort
mov dword [edi+mtrr_range.start], ebx
mov dword [edi+mtrr_range.start+4], ebp
mov dword [edi+mtrr_range.length], eax
mov dword [edi+mtrr_range.length+4], edx
mov eax, [edi+mtrr_range.next]
mov [.first_free_range], eax
mov eax, [esi+mtrr_range.next]
mov [edi+mtrr_range.next], eax
; don't change [esi+mtrr_range.next] yet, it will be filled at step 6e
mov eax, ebx
mov edx, ebp
sub eax, dword [esi+mtrr_range.start]
sbb edx, dword [esi+mtrr_range.start+4]
mov dword [esi+mtrr_range.length], eax
mov dword [esi+mtrr_range.length+4], edx
.truncate_uc:
; 6e. edi is the first range after ebp:ebx, check it and next ranges
; for intersection with the new range, truncate heads.
add ebx, [ecx+8]
adc ebp, [ecx+12]
; ebp:ebx = end of the range described by the current MTRR.
.truncate_uc_loop:
; If start of the next range is greater than ebp:ebx,
; exit the loop to 6g.
mov eax, ebx
mov edx, ebp
sub eax, dword [edi+mtrr_range.start]
sbb edx, dword [edi+mtrr_range.start+4]
jb .truncate_uc_done
; Otherwise, if end of the next range is greater than ebp:ebx,
; exit the loop to 6f.
sub eax, dword [edi+mtrr_range.length]
sbb edx, dword [edi+mtrr_range.length+4]
jb .truncate_uc_last
; Otherwise, the current range is completely within the new range.
; Free it and continue the loop if there is a next range.
; If that was a last range, exit the loop to 6g.
mov edx, [edi+mtrr_range.next]
mov eax, [.first_free_range]
mov [.first_free_range], edi
mov [edi+mtrr_range.next], eax
mov edi, edx
test edi, edi
jnz .truncate_uc_loop
jmp .truncate_uc_done
.truncate_uc_last:
; 6f. The range at edi partially intersects with the UC-range described by MTRR.
; Truncate it from the head.
mov dword [edi+mtrr_range.start], ebx
mov dword [edi+mtrr_range.start+4], ebp
neg eax
adc edx, 0
neg edx
mov dword [edi+mtrr_range.length], eax
mov dword [edi+mtrr_range.length+4], edx
.truncate_uc_done:
; 6g. We have found the next range (maybe 0) after intersection.
; Write it to [esi+mtrr_range.next].
mov [esi+mtrr_range.next], edi
.next_uc_range:
; 6h. Continue the loop 6b-6g over all configured MTRRs.
add ecx, 16
cmp ecx, [.mtrrs_end]
jb .fill_uc_ranges
; Sanity check: if there are no ranges after steps 5-6,
; fallback to step 7. Otherwise, go to 8.
cmp [.first_range], 0
jnz .ranges_ok
.fill_ranges_from_memory_map:
; 7. BIOS has not configured variable-range MTRRs.
; Create one range from 0 to [MEM_AMOUNT].
mov eax, [.first_free_range]
mov edx, [eax+mtrr_range.next]
mov [.first_free_range], edx
mov [.first_range], eax
xor edx, edx
mov [eax+mtrr_range.next], edx
mov dword [eax+mtrr_range.start], edx
mov dword [eax+mtrr_range.start+4], edx
mov ecx, [MEM_AMOUNT]
mov dword [eax+mtrr_range.length], ecx
mov dword [eax+mtrr_range.length+4], edx
.ranges_ok:
; 8. We have calculated list of WB-ranges.
; Now we should calculate a list of MTRRs so that
; * every MTRR describes a range with length = power of 2 and start that is aligned,
; * every MTRR can be WB or UC
; * (sum of all WB ranges) minus (sum of all UC ranges) equals the calculated list
; * top of 4G memory must not be covered by any ranges
; Example: range [0,0xBC000000) can be converted to
; [0,0x80000000)+[0x80000000,0xC0000000)-[0xBC000000,0xC0000000)
; WB +WB -UC
; but not to [0,0x100000000)-[0xC0000000,0x100000000)-[0xBC000000,0xC0000000).
; 8a. Check that list of ranges is [0,something) plus, optionally, [4G,something).
; This holds in practice (see mtrrtest.asm for real-life examples)
; and significantly simplifies the code: ranges are independent, start of range
; is almost always aligned (the only exception >4G upper memory can be easily covered),
; there is no need to consider adding holes before start of range, only
; append them to end of range.
xor eax, eax
ret
 
.expand:
 
mov ecx, pg_data.mutex
call mutex_lock
 
xchg esi, edi
 
push esi ;new size
push edi ;old size
 
add edi, 0x3FFFFF
and edi, not(0x3FFFFF)
add esi, 0x3FFFFF
and esi, not(0x3FFFFF)
 
cmp edi, esi
jae .grow
mov edi, [.first_range]
cmp dword [edi+mtrr_range.start], eax
jnz .abort
cmp dword [edi+mtrr_range.start+4], eax
jnz .abort
cmp dword [edi+mtrr_range.length+4], eax
jnz .abort
mov edx, [edi+mtrr_range.next]
test edx, edx
jz @f
cmp dword [edx+mtrr_range.start], eax
jnz .abort
cmp dword [edx+mtrr_range.start+4], 1
jnz .abort
cmp [edx+mtrr_range.next], eax
jnz .abort
@@:
call alloc_page
; 8b. Initialize: no MTRRs filled.
mov [.num_used_mtrrs], eax
lea esi, [.mtrrs]
.range2mtrr_loop:
; 8c. If we are dealing with upper-memory range (after 4G)
; with length > start, create one WB MTRR with [start,2*start),
; reset start to 2*start and return to this step.
; Example: [4G,24G) -> [4G,8G) {returning} + [8G,16G) {returning}
; + [16G,24G) {advancing to ?}.
mov eax, dword [edi+mtrr_range.length+4]
test eax, eax
jz .exit_fail
 
stdcall map_page_table, edi, eax
 
push edi
shr edi, 10
add edi, page_tabs
mov ecx, 1024
jz .less4G
mov edx, dword [edi+mtrr_range.start+4]
cmp eax, edx
jb .start_aligned
inc [.num_used_mtrrs]
cmp [.num_used_mtrrs], MAX_USEFUL_MTRRS
ja .abort
mov dword [esi], MEM_WB
mov dword [esi+4], edx
mov dword [esi+8], 0
mov dword [esi+12], edx
add esi, 16
add dword [edi+mtrr_range.start+4], edx
sub dword [edi+mtrr_range.length+4], edx
jnz .range2mtrr_loop
cmp dword [edi+mtrr_range.length], 0
jz .range2mtrr_next
.less4G:
; 8d. If we are dealing with low-memory range (before 4G)
; and appending a maximal-size hole would create a range covering top of 4G,
; create a maximal-size WB range and return to this step.
; Example: for [0,0xBC000000) the following steps would consider
; variants [0,0x80000000)+(another range to be splitted) and
; [0,0x100000000)-(another range to be splitted); we forbid the last variant,
; so the first variant must be used.
bsr ecx, dword [edi+mtrr_range.length]
xor edx, edx
inc edx
shl edx, cl
lea eax, [edx*2]
add eax, dword [edi+mtrr_range.start]
jnz .start_aligned
inc [.num_used_mtrrs]
cmp [.num_used_mtrrs], MAX_USEFUL_MTRRS
ja .abort
mov eax, dword [edi+mtrr_range.start]
mov dword [esi], eax
or dword [esi], MEM_WB
mov dword [esi+4], 0
mov dword [esi+8], edx
mov dword [esi+12], 0
add esi, 16
add dword [edi+mtrr_range.start], edx
sub dword [edi+mtrr_range.length], edx
jnz .less4G
jmp .range2mtrr_next
.start_aligned:
; Start is aligned for any allowed length, maximum-size hole is allowed.
; Select the best MTRR configuration for one range.
; length=...101101
; Without hole at the end, we need one WB MTRR for every 1-bit in length:
; length=...100000 + ...001000 + ...000100 + ...000001
; We can also append one hole at the end so that one 0-bit (selected by us)
; becomes 1 and all lower bits become 0 for WB-range:
; length=...110000 - (...00010 + ...00001)
; In this way, we need one WB MTRR for every 1-bit higher than the selected bit,
; one WB MTRR for the selected bit, one UC MTRR for every 0-bit between
; the selected bit and lowest 1-bit (they become 1-bits after negation)
; and one UC MTRR for lowest 1-bit.
; So we need to select 0-bit with the maximal difference
; (number of 0-bits) - (number of 1-bits) between selected and lowest 1-bit,
; this equals the gain from using a hole. If the difference is negative for
; all 0-bits, don't append hole.
; Note that lowest 1-bit is not included when counting, but selected 0-bit is.
; 8e. Find the optimal bit position for hole.
; eax = current difference, ebx = best difference,
; ecx = hole bit position, edx = current bit position.
xor eax, eax
cld
rep stosd
pop edi
 
add edi, 0x00400000
cmp edi, esi
jb @B
.grow:
pop edi ;old size
pop ecx ;new size
 
shr edi, 10
shr ecx, 10
sub ecx, edi
shr ecx, 2 ;pages count
mov eax, 2
 
add edi, app_page_tabs
rep stosd
 
mov ecx, pg_data.mutex
call mutex_unlock
 
jmp .update_size
 
.exit_fail:
mov ecx, pg_data.mutex
call mutex_unlock
 
add esp, 8
pop edi
pop esi
pop ebx
xor eax, eax
inc eax
ret
endp
 
 
align 4
update_mem_size:
; in: edx = slot base
; ebx = new memory size
; destroys eax,ecx,edx
 
mov [APPDATA.mem_size+edx], ebx
;search threads and update
;application memory size infomation
mov ecx, [APPDATA.dir_table+edx]
mov eax, 2
 
.search_threads:
;eax = current slot
;ebx = new memory size
;ecx = page directory
cmp eax, [TASK_COUNT]
jg .search_threads_end
mov edx, eax
shl edx, 5
cmp word [CURRENT_TASK+edx+TASKDATA.state], 9 ;if slot empty?
jz .search_threads_next
shl edx, 3
cmp [SLOT_BASE+edx+APPDATA.dir_table], ecx ;if it is our thread?
jnz .search_threads_next
mov [SLOT_BASE+edx+APPDATA.mem_size], ebx ;update memory size
.search_threads_next:
inc eax
jmp .search_threads
.search_threads_end:
ret
 
; param
; eax= linear address
;
; retval
; eax= phisical page address
 
align 4
get_pg_addr:
sub eax, OS_BASE
cmp eax, 0x400000
jb @f
shr eax, 12
mov eax, [page_tabs+(eax+(OS_BASE shr 12))*4]
xor ebx, ebx
xor ecx, ecx
bsf edx, dword [edi+mtrr_range.length]
jnz @f
bsf edx, dword [edi+mtrr_range.length+4]
add edx, 32
@@:
and eax, 0xFFFFF000
ret
 
 
align 4
; Now it is called from core/sys32::exc_c (see stack frame there)
proc page_fault_handler
 
.err_addr equ ebp-4
 
push ebx ;save exception number (#PF)
mov ebp, esp
mov ebx, cr2
push ebx ;that is locals: .err_addr = cr2
inc [pg_data.pages_faults]
 
mov eax, [pf_err_code]
 
cmp ebx, OS_BASE ;ebx == .err_addr
jb .user_space ;страница в памяти приложения ;
 
cmp ebx, page_tabs
jb .kernel_space ;страница в памяти ядра
 
cmp ebx, kernel_tabs
jb .alloc;.app_tabs ;таблицы страниц приложения ;
;просто создадим одну
if 0 ;пока это просто лишнее
cmp ebx, LFB_BASE
jb .core_tabs ;таблицы страниц ядра
;Ошибка
.lfb:
;область LFB
;Ошибка
jmp .fail
end if
.core_tabs:
.fail: ;simply return to caller
mov esp, ebp
pop ebx ;restore exception number (#PF)
ret
 
; xchg bx, bx
; add esp,12 ;clear in stack: locals(.err_addr) + #PF + ret_to_caller
; restore_ring3_context
; iretd
 
.user_space:
test eax, PG_MAP
jnz .err_access ;Страница присутствует
;Ошибка доступа ?
 
shr ebx, 12
mov ecx, ebx
shr ecx, 10
mov edx, [master_tab+ecx*4]
test edx, PG_MAP
jz .fail ;таблица страниц не создана
;неверный адрес в программе
 
mov eax, [page_tabs+ebx*4]
test eax, 2
jz .fail ;адрес не зарезервирован для ;
;использования. Ошибка
.alloc:
call alloc_page
test eax, eax
jz .fail
 
stdcall map_page, [.err_addr], eax, PG_UW
 
mov edi, [.err_addr]
and edi, 0xFFFFF000
mov ecx, 1024
xor eax, eax
;cld ;caller is duty for this
rep stosd
.exit: ;iret with repeat fault instruction
add esp, 12;clear in stack: locals(.err_addr) + #PF + ret_to_caller
restore_ring3_context
iretd
 
.err_access:
; access denied? this may be a result of copy-on-write protection for DLL
; check list of HDLLs
and ebx, not 0xFFF
mov eax, [CURRENT_TASK]
shl eax, 8
mov eax, [SLOT_BASE+eax+APPDATA.dlls_list_ptr]
test eax, eax
jz .fail
mov esi, [eax+HDLL.fd]
.scan_hdll:
cmp esi, eax
jz .fail
mov edx, ebx
sub edx, [esi+HDLL.base]
cmp edx, [esi+HDLL.size]
jb .fault_in_hdll
.scan_hdll.next:
mov esi, [esi+HDLL.fd]
jmp .scan_hdll
.fault_in_hdll:
; allocate new page, map it as rw and copy data
call alloc_page
test eax, eax
jz .fail
stdcall map_page, ebx, eax, PG_UW
mov edi, ebx
mov ecx, 1024
sub ebx, [esi+HDLL.base]
mov esi, [esi+HDLL.parent]
mov esi, [esi+DLLDESCR.data]
add esi, ebx
rep movsd
jmp .exit
 
.kernel_space:
test eax, PG_MAP
jz .fail ;страница не присутствует
 
test eax, 12 ;U/S (+below)
jnz .fail ;приложение обратилось к памяти
;ядра
;test eax, 8
;jnz .fail ;установлен зарезервированный бит
;в таблицах страниц. добавлено в P4/Xeon
 
;попытка записи в защищённую страницу ядра
 
cmp ebx, tss._io_map_0
jb .fail
 
cmp ebx, tss._io_map_0+8192
jae .fail
 
; io permission map
; copy-on-write protection
 
call alloc_page
test eax, eax
jz .fail
 
push eax
stdcall map_page, [.err_addr], eax, dword PG_SW
pop eax
mov edi, [.err_addr]
and edi, -4096
lea esi, [edi+(not tss._io_map_0)+1]; -tss._io_map_0
 
mov ebx, esi
shr ebx, 12
mov edx, [current_slot]
or eax, PG_SW
mov [edx+APPDATA.io_map+ebx*4], eax
 
add esi, [default_io_map]
mov ecx, 4096/4
;cld ;caller is duty for this
rep movsd
jmp .exit
endp
 
; returns number of mapped bytes
proc map_mem stdcall, lin_addr:dword,slot:dword,\
ofs:dword,buf_size:dword,req_access:dword
push 0 ; initialize number of mapped bytes
 
cmp [buf_size], 0
jz .exit
 
mov eax, [slot]
shl eax, 8
mov eax, [SLOT_BASE+eax+APPDATA.dir_table]
and eax, 0xFFFFF000
 
stdcall map_page, [ipc_pdir], eax, PG_UW
mov ebx, [ofs]
shr ebx, 22
mov esi, [ipc_pdir]
mov edi, [ipc_ptab]
mov eax, [esi+ebx*4]
and eax, 0xFFFFF000
jz .exit
stdcall map_page, edi, eax, PG_UW
; inc ebx
; add edi, 0x1000
; mov eax, [esi+ebx*4]
; test eax, eax
; jz @f
; and eax, 0xFFFFF000
; stdcall map_page, edi, eax
 
@@:
mov edi, [lin_addr]
and edi, 0xFFFFF000
mov ecx, [buf_size]
add ecx, 4095
shr ecx, 12
inc ecx
 
mov edx, [ofs]
shr edx, 12
and edx, 0x3FF
mov esi, [ipc_ptab]
 
.map:
stdcall safe_map_page, [slot], [req_access], [ofs]
jnc .exit
add dword [ebp-4], 4096
add [ofs], 4096
dec ecx
jz .exit
add edi, 0x1000
push edx ; save position of lowest 1-bit for step 8f
.calc_stat:
inc edx
cmp edx, 0x400
jnz .map
inc ebx
mov eax, [ipc_pdir]
mov eax, [eax+ebx*4]
and eax, 0xFFFFF000
jz .exit
stdcall map_page, esi, eax, PG_UW
xor edx, edx
jmp .map
 
.exit:
pop eax
ret
endp
 
proc map_memEx stdcall, lin_addr:dword,slot:dword,\
ofs:dword,buf_size:dword,req_access:dword
push 0 ; initialize number of mapped bytes
 
cmp [buf_size], 0
jz .exit
 
mov eax, [slot]
shl eax, 8
mov eax, [SLOT_BASE+eax+APPDATA.dir_table]
and eax, 0xFFFFF000
 
stdcall map_page, [proc_mem_pdir], eax, PG_UW
mov ebx, [ofs]
shr ebx, 22
mov esi, [proc_mem_pdir]
mov edi, [proc_mem_tab]
mov eax, [esi+ebx*4]
and eax, 0xFFFFF000
test eax, eax
jz .exit
stdcall map_page, edi, eax, PG_UW
 
cmp edx, 64
jae .stat_done
inc eax ; increment difference in hope for 1-bit
; Note: bt conveniently works with both .length and .length+4,
; depending on whether edx>=32.
bt dword [edi+mtrr_range.length], edx
jc .calc_stat
dec eax ; hope was wrong, decrement difference to correct 'inc'
dec eax ; and again, now getting the real difference
cmp eax, ebx
jle .calc_stat
mov ebx, eax
mov ecx, edx
jmp .calc_stat
.stat_done:
; 8f. If we decided to create a hole, flip all bits between lowest and selected.
pop edx ; restore position of lowest 1-bit saved at step 8e
test ecx, ecx
jz .fill_hi_init
@@:
mov edi, [lin_addr]
and edi, 0xFFFFF000
mov ecx, [buf_size]
add ecx, 4095
shr ecx, 12
inc ecx
 
mov edx, [ofs]
shr edx, 12
and edx, 0x3FF
mov esi, [proc_mem_tab]
 
.map:
stdcall safe_map_page, [slot], [req_access], [ofs]
jnc .exit
add dword [ebp-4], 0x1000
add edi, 0x1000
add [ofs], 0x1000
inc edx
dec ecx
jnz .map
.exit:
pop eax
ret
endp
 
; in: esi+edx*4 = pointer to page table entry
; in: [slot], [req_access], [ofs] on the stack
; in: edi = linear address to map
; out: CF cleared <=> failed
; destroys: only eax
proc safe_map_page stdcall, slot:dword, req_access:dword, ofs:dword
mov eax, [esi+edx*4]
test al, PG_MAP
jz .not_present
test al, PG_WRITE
jz .resolve_readonly
; normal case: writable page, just map with requested access
.map:
stdcall map_page, edi, eax, [req_access]
stc
.fail:
ret
.not_present:
; check for alloc-on-demand page
test al, 2
jz .fail
; allocate new page, save it to source page table
push ecx
call alloc_page
pop ecx
test eax, eax
jz .fail
or al, PG_UW
mov [esi+edx*4], eax
jmp .map
.resolve_readonly:
; readonly page, probably copy-on-write
; check: readonly request of readonly page is ok
test [req_access], PG_WRITE
jz .map
; find control structure for this page
pushf
cli
cld
push ebx ecx
mov eax, [slot]
shl eax, 8
mov eax, [SLOT_BASE+eax+APPDATA.dlls_list_ptr]
test eax, eax
jz .no_hdll
mov ecx, [eax+HDLL.fd]
.scan_hdll:
cmp ecx, eax
jz .no_hdll
mov ebx, [ofs]
and ebx, not 0xFFF
sub ebx, [ecx+HDLL.base]
cmp ebx, [ecx+HDLL.size]
jb .hdll_found
mov ecx, [ecx+HDLL.fd]
jmp .scan_hdll
.no_hdll:
pop ecx ebx
popf
clc
ret
.hdll_found:
; allocate page, save it in page table, map it, copy contents from base
mov eax, [ecx+HDLL.parent]
add ebx, [eax+DLLDESCR.data]
call alloc_page
test eax, eax
jz .no_hdll
or al, PG_UW
mov [esi+edx*4], eax
stdcall map_page, edi, eax, [req_access]
push esi edi
mov esi, ebx
mov ecx, 4096/4
rep movsd
pop edi esi
pop ecx ebx
popf
stc
ret
endp
 
sys_IPC:
;input:
; ebx=1 - set ipc buffer area
; ecx=address of buffer
; edx=size of buffer
; eax=2 - send message
; ebx=PID
; ecx=address of message
; edx=size of message
 
dec ebx
jnz @f
 
mov eax, [current_slot]
pushf
cli
mov [eax+APPDATA.ipc_start], ecx ;set fields in extended information area
mov [eax+APPDATA.ipc_size], edx
 
add edx, ecx
add edx, 4095
and edx, not 4095
 
.touch:
mov eax, [ecx]
add ecx, 0x1000
cmp ecx, edx
jb .touch
 
popf
mov [esp+32], ebx ;ebx=0
ret
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;2
cmp edx, ecx
ja .fill_hi_init
btc dword [edi+mtrr_range.length], edx
jmp @b
.fill_hi_init:
; 8g. Create MTRR ranges corresponding to upper 32 bits.
sub ecx, 32
.fill_hi_loop:
bsr edx, dword [edi+mtrr_range.length+4]
jz .fill_hi_done
inc [.num_used_mtrrs]
cmp [.num_used_mtrrs], MAX_USEFUL_MTRRS
ja .abort
mov eax, dword [edi+mtrr_range.start]
mov [esi], eax
mov eax, dword [edi+mtrr_range.start+4]
mov [esi+4], eax
xor eax, eax
mov [esi+8], eax
bts eax, edx
mov [esi+12], eax
cmp edx, ecx
jl .fill_hi_uc
or dword [esi], MEM_WB
add dword [edi+mtrr_range.start+4], eax
jmp @f
.fill_hi_uc:
sub dword [esi+4], eax
sub dword [edi+mtrr_range.start+4], eax
@@:
dec ebx
jnz @f
 
stdcall sys_ipc_send, ecx, edx, esi
mov [esp+32], eax
ret
add esi, 16
sub dword [edi+mtrr_range.length], eax
jmp .fill_hi_loop
.fill_hi_done:
; 8h. Create MTRR ranges corresponding to lower 32 bits.
add ecx, 32
.fill_lo_loop:
bsr edx, dword [edi+mtrr_range.length]
jz .range2mtrr_next
inc [.num_used_mtrrs]
cmp [.num_used_mtrrs], MAX_USEFUL_MTRRS
ja .abort
mov eax, dword [edi+mtrr_range.start]
mov [esi], eax
mov eax, dword [edi+mtrr_range.start+4]
mov [esi+4], eax
xor eax, eax
mov [esi+12], eax
bts eax, edx
mov [esi+8], eax
cmp edx, ecx
jl .fill_lo_uc
or dword [esi], MEM_WB
add dword [edi+mtrr_range.start], eax
jmp @f
.fill_lo_uc:
sub dword [esi], eax
sub dword [edi+mtrr_range.start], eax
@@:
or eax, -1
mov [esp+32], eax
ret
 
;align 4
;proc set_ipc_buff
 
; mov eax,[current_slot]
; pushf
; cli
; mov [eax+APPDATA.ipc_start],ebx ;set fields in extended information area
; mov [eax+APPDATA.ipc_size],ecx
;
; add ecx, ebx
; add ecx, 4095
; and ecx, not 4095
;
;.touch: mov eax, [ebx]
; add ebx, 0x1000
; cmp ebx, ecx
; jb .touch
;
; popf
; xor eax, eax
; ret
;endp
 
proc sys_ipc_send stdcall, PID:dword, msg_addr:dword, msg_size:dword
locals
dst_slot dd ?
dst_offset dd ?
buf_size dd ?
used_buf dd ?
endl
 
pushf
cli
 
mov eax, [PID]
call pid_to_slot
test eax, eax
jz .no_pid
 
mov [dst_slot], eax
shl eax, 8
mov edi, [eax+SLOT_BASE+0xa0] ;is ipc area defined?
add esi, 16
sub dword [edi+mtrr_range.length], eax
jmp .fill_lo_loop
.range2mtrr_next:
; 8i. Repeat the loop at 8c-8h for all ranges.
mov edi, [edi+mtrr_range.next]
test edi, edi
jz .no_ipc_area
jnz .range2mtrr_loop
; 9. We have calculated needed MTRRs, now setup them in the CPU.
; 9a. Abort if number of MTRRs is too large.
mov eax, [num_variable_mtrrs]
cmp [.num_used_mtrrs], eax
ja .abort
 
mov ebx, edi
and ebx, 0xFFF
mov [dst_offset], ebx
; 9b. Prepare for changes.
call mtrr_begin_change
 
mov esi, [eax+SLOT_BASE+0xa4]
mov [buf_size], esi
 
mov ecx, [ipc_tmp]
cmp esi, 0x40000-0x1000; size of [ipc_tmp] minus one page
jbe @f
push esi edi
add esi, 0x1000
stdcall alloc_kernel_space, esi
mov ecx, eax
pop edi esi
; 9c. Prepare for loop over MTRRs.
lea esi, [.mtrrs]
mov ecx, 0x200
@@:
mov [used_buf], ecx
stdcall map_mem, ecx, [dst_slot], \
edi, esi, PG_SW
 
mov edi, [dst_offset]
add edi, [used_buf]
cmp dword [edi], 0
jnz .ipc_blocked ;if dword [buffer]<>0 - ipc blocked now
 
mov edx, dword [edi+4]
lea ebx, [edx+8]
add ebx, [msg_size]
cmp ebx, [buf_size]
ja .buffer_overflow ;esi<0 - not enough memory in buffer
 
mov dword [edi+4], ebx
mov eax, [TASK_BASE]
mov eax, [eax+0x04] ;eax - our PID
add edi, edx
mov [edi], eax
mov ecx, [msg_size]
 
mov [edi+4], ecx
add edi, 8
mov esi, [msg_addr]
; add esi, new_app_base
cld
rep movsb
 
mov ebx, [ipc_tmp]
mov edx, ebx
shr ebx, 12
; 9d. For every MTRR, copy PHYSBASEn as is: step 8 has configured
; start value and type bits as needed.
mov eax, [esi]
mov edx, [esi+4]
wrmsr
inc ecx
; 9e. For every MTRR, calculate PHYSMASKn = -(length) or 0x800
; with upper bits cleared, 0x800 = MTRR is valid.
xor eax, eax
mov [page_tabs+ebx*4], eax
invlpg [edx]
 
mov ebx, [ipc_pdir]
mov edx, ebx
shr ebx, 12
xor eax, eax
mov [page_tabs+ebx*4], eax
invlpg [edx]
 
mov ebx, [ipc_ptab]
mov edx, ebx
shr ebx, 12
xor eax, eax
mov [page_tabs+ebx*4], eax
invlpg [edx]
 
mov eax, [dst_slot]
shl eax, 8
or [eax+SLOT_BASE+0xA8], dword 0x40
push 0
jmp .ret
.no_pid:
popf
mov eax, 4
ret
.no_ipc_area:
popf
xor eax, eax
inc eax
ret
.ipc_blocked:
push 2
jmp .ret
.buffer_overflow:
push 3
.ret:
mov eax, [used_buf]
cmp eax, [ipc_tmp]
jz @f
stdcall free_kernel_space, eax
@@:
pop eax
popf
ret
endp
 
align 4
sysfn_meminfo:
 
; add ecx, new_app_base
cmp ecx, OS_BASE
jae .fail
 
mov eax, [pg_data.pages_count]
mov [ecx], eax
shl eax, 12
mov [esp+32], eax
mov eax, [pg_data.pages_free]
mov [ecx+4], eax
mov eax, [pg_data.pages_faults]
mov [ecx+8], eax
mov eax, [heap_size]
mov [ecx+12], eax
mov eax, [heap_free]
mov [ecx+16], eax
mov eax, [heap_blocks]
mov [ecx+20], eax
mov eax, [free_blocks]
mov [ecx+24], eax
ret
.fail:
or dword [esp+32], -1
ret
 
align 4
f68:
cmp ebx, 4
jbe sys_sheduler
 
cmp ebx, 11
jb .fail
 
cmp ebx, 27
ja .fail
 
jmp dword [f68call+ebx*4-11*4]
.11:
call init_heap
mov [esp+32], eax
ret
.12:
stdcall user_alloc, ecx
mov [esp+32], eax
ret
.13:
stdcall user_free, ecx
mov [esp+32], eax
ret
.14:
cmp ecx, OS_BASE
jae .fail
mov edi, ecx
call get_event_ex
mov [esp+32], eax
ret
.16:
test ecx, ecx
jz .fail
cmp ecx, OS_BASE
jae .fail
stdcall get_service, ecx
mov [esp+32], eax
ret
.17:
call srv_handlerEx ;ecx
mov [esp+32], eax
ret
.19:
cmp ecx, OS_BASE
jae .fail
stdcall load_library, ecx
mov [esp+32], eax
ret
.20:
mov eax, edx
mov ebx, ecx
call user_realloc ;in: eax = pointer, ebx = new size
mov [esp+32], eax
ret
.21:
cmp ecx, OS_BASE
jae .fail
 
cmp edx, OS_BASE
jae .fail
 
stdcall load_pe_driver, ecx, edx
mov [esp+32], eax
ret
.22:
cmp ecx, OS_BASE
jae .fail
 
stdcall shmem_open, ecx, edx, esi
mov [esp+24], edx
mov [esp+32], eax
ret
 
.23:
cmp ecx, OS_BASE
jae .fail
 
stdcall shmem_close, ecx
mov [esp+32], eax
ret
.24:
mov eax, [current_slot]
xchg ecx, [eax+APPDATA.exc_handler]
xchg edx, [eax+APPDATA.except_mask]
mov [esp+32], ecx ; reg_eax+8
mov [esp+20], edx ; reg_ebx+8
ret
.25:
cmp ecx, 32
jae .fail
mov eax, [current_slot]
btr [eax+APPDATA.except_mask], ecx
setc byte[esp+32]
jecxz @f
bts [eax+APPDATA.except_mask], ecx
@@:
ret
 
.26:
stdcall user_unmap, ecx, edx, esi
mov [esp+32], eax
ret
 
.27:
cmp ecx, OS_BASE
jae .fail
 
stdcall load_file_umode, ecx
mov [esp+24], edx
mov [esp+32], eax
ret
 
.fail:
xor eax, eax
mov [esp+32], eax
ret
 
 
align 4
f68call: ; keep this table closer to main code
 
dd f68.11 ; init_heap
dd f68.12 ; user_alloc
dd f68.13 ; user_free
dd f68.14 ; get_event_ex
dd f68.fail ; moved to f68.24
dd f68.16 ; get_service
dd f68.17 ; call_service
dd f68.fail ; moved to f68.25
dd f68.19 ; load_dll
dd f68.20 ; user_realloc
dd f68.21 ; load_driver
dd f68.22 ; shmem_open
dd f68.23 ; shmem_close
dd f68.24 ; set exception handler
dd f68.25 ; unmask exception
dd f68.26 ; user_unmap
dd f68.27 ; load_file_umode
 
 
align 4
proc load_pe_driver stdcall, file:dword, cmdline:dword
push esi
 
stdcall load_PE, [file]
test eax, eax
jz .fail
 
mov esi, eax
push [cmdline]
push DRV_ENTRY
call eax
pop ecx
pop ecx
test eax, eax
jz .fail
 
mov [eax+SRV.entry], esi
pop esi
ret
 
.fail:
xor eax, eax
pop esi
ret
endp
 
align 4
proc init_mtrr
 
cmp [BOOT_VARS+BOOT_MTRR], byte 2
je .exit
 
bt [cpu_caps], CAPS_MTRR
jnc .exit
 
mov eax, cr0
or eax, 0x60000000 ;disable caching
mov cr0, eax
wbinvd ;invalidate cache
 
mov ecx, 0x2FF
rdmsr ;
; has BIOS already initialized MTRRs?
test ah, 8
jnz .skip_init
; rarely needed, so mainly placeholder
; main memory - cached
push eax
 
mov eax, [MEM_AMOUNT]
; round eax up to next power of 2
dec eax
bsr ecx, eax
mov ebx, 2
shl ebx, cl
dec ebx
; base of memory range = 0, type of memory range = MEM_WB
xor edx, edx
mov eax, MEM_WB
mov ecx, 0x200
sub eax, [esi+8]
sbb edx, [esi+12]
or eax, 0x800
or edx, [.phys_reserved_mask]
xor edx, [.phys_reserved_mask]
wrmsr
; mask of memory range = 0xFFFFFFFFF - (size - 1), ebx = size - 1
mov eax, 0xFFFFFFFF
mov edx, 0x0000000F
sub eax, ebx
sbb edx, 0
or eax, 0x800
inc ecx
wrmsr
; clear unused MTRRs
; 9f. Continue steps 9d and 9e for all MTRRs calculated at step 8.
add esi, 16
dec [.num_used_mtrrs]
jnz @b
; 9g. Zero other MTRRs.
xor eax, eax
xor edx, edx
mov ebx, [num_variable_mtrrs]
lea ebx, [0x200+ebx*2]
@@:
cmp ecx, ebx
jae @f
wrmsr
inc ecx
wrmsr
cmp ecx, 0x20F
jb @b
; enable MTRRs
pop eax
or ah, 8
inc ecx
jmp @b
@@:
 
; 9i. Configure MTRR_DEF_TYPE.
mov ecx, 0x2FF
rdmsr
or ah, 8 ; enable variable-ranges MTRR
and al, 0xF0; default memtype = UC
mov ecx, 0x2FF
wrmsr
.skip_init:
stdcall set_mtrr, [LFBAddress], [LFBSize], MEM_WC
 
wbinvd ;again invalidate
; 9j. Changes are done.
call mtrr_end_change
 
mov eax, cr0
and eax, not 0x60000000
mov cr0, eax ; enable caching
.exit:
.abort:
add esp, .local_vars_size + MAX_RANGES * sizeof.mtrr_range
pop ebp
ret
endp
 
align 4
; Allocate&set one MTRR for given range.
; size must be power of 2 that divides base.
proc set_mtrr stdcall, base:dword,size:dword,mem_type:dword
; find unused register
mov ecx, 0x201
@@:
.scan:
rdmsr
dec ecx
test ah, 8
jz .found
rdmsr
mov al, 0; clear memory type field
test edx, edx
jnz @f
and eax, not 0xFFF ; clear reserved bits
cmp eax, [base]
jz .ret
@@:
add ecx, 3
cmp ecx, 0x210
jb @b
mov eax, [num_variable_mtrrs]
lea eax, [0x200+eax*2]
cmp ecx, eax
jb .scan
; no free registers, ignore the call
.ret:
ret
.found:
; found, write values
call mtrr_begin_change
xor edx, edx
mov eax, [base]
or eax, [mem_type]
wrmsr
 
mov ebx, [size]
dec ebx
mov eax, 0xFFFFFFFF
mov edx, 0x00000000
sub eax, ebx
mov al, [cpu_phys_addr_width]
xor edx, edx
bts edx, eax
xor eax, eax
sub eax, [size]
sbb edx, 0
or eax, 0x800
inc ecx
wrmsr
call mtrr_end_change
ret
endp
 
align 4
proc create_ring_buffer stdcall, size:dword, flags:dword
locals
buf_ptr dd ?
endl
 
mov eax, [size]
test eax, eax
jz .fail
 
add eax, eax
stdcall alloc_kernel_space, eax
test eax, eax
jz .fail
 
push ebx
 
mov [buf_ptr], eax
 
mov ebx, [size]
shr ebx, 12
push ebx
 
stdcall alloc_pages, ebx
pop ecx
 
test eax, eax
jz .mm_fail
 
push edi
 
or eax, [flags]
mov edi, [buf_ptr]
mov ebx, [buf_ptr]
mov edx, ecx
shl edx, 2
shr edi, 10
@@:
mov [page_tabs+edi], eax
mov [page_tabs+edi+edx], eax
invlpg [ebx]
invlpg [ebx+0x10000]
add eax, 0x1000
add ebx, 0x1000
add edi, 4
; Helper procedure for mtrr_validate.
; Calculates memory type for given address according to variable-range MTRRs.
; Assumes that MTRRs are enabled.
; in: ebx = 32-bit physical address
; out: eax = memory type for ebx
proc mtrr_get_real_type
; 1. Initialize: we have not yet found any MTRRs covering ebx.
push 0
mov ecx, 0x201
.mtrr_loop:
; 2. For every MTRR, check whether it is valid; if not, continue to the next MTRR.
rdmsr
dec ecx
jnz @B
 
mov eax, [buf_ptr]
pop edi
pop ebx
ret
.mm_fail:
stdcall free_kernel_space, [buf_ptr]
test ah, 8
jz .next
; 3. For every valid MTRR, check whether (ebx and PHYSMASKn) == PHYSBASEn,
; excluding low 12 bits.
and eax, ebx
push eax
rdmsr
test edx, edx
pop edx
jnz .next
xor edx, eax
and edx, not 0xFFF
jnz .next
; 4. If so, set the bit corresponding to memory type defined by this MTRR.
and eax, 7
bts [esp], eax
.next:
; 5. Continue loop at 2-4 for all variable-range MTRRs.
add ecx, 3
mov eax, [num_variable_mtrrs]
lea eax, [0x200+eax*2]
cmp ecx, eax
jb .mtrr_loop
; 6. If no MTRRs cover address in ebx, use default MTRR type from MTRR_DEF_CAP.
pop edx
test edx, edx
jz .default
; 7. Find&clear 1-bit in edx.
bsf eax, edx
btr edx, eax
; 8. If there was only one 1-bit, then all MTRRs are consistent, return that bit.
test edx, edx
jz .nothing
; Otherwise, return MEM_UC (e.g. WB+UC is UC).
xor eax, eax
pop ebx
.fail:
.nothing:
ret
.default:
mov ecx, 0x2FF
rdmsr
movzx eax, al
ret
endp
 
 
align 4
proc print_mem
mov edi, BOOT_VAR + 0x9104
mov ecx, [edi-4]
test ecx, ecx
jz .done
 
; If MTRRs are configured improperly, this is not obvious to the user;
; everything works, but the performance can be horrible.
; Try to detect this and let the user know that the low performance
; is caused by some problem and is not a global property of the system.
; Let's hope he would report it to developers...
proc mtrr_validate
; 1. If MTRRs are not supported, they cannot be configured improperly.
bt [cpu_caps], CAPS_MTRR
jnc .exit
; 2. If variable-range MTRRs are not configured, this is a problem.
mov ecx, 0x2FF
rdmsr
test ah, 8
jz .fail
; 3. Get the memory type for address somewhere inside working memory.
; It must be write-back.
mov ebx, 0x27FFFF
call mtrr_get_real_type
cmp al, MEM_WB
jnz .fail
; 4. If we're using a mode with LFB,
; get the memory type for last pixel of the framebuffer.
; It must be write-combined.
test word [SCR_MODE], 0x4000
jz .exit
mov eax, [_display.pitch]
mul [_display.height]
dec eax
; LFB is mapped to virtual address LFB_BASE,
; it uses global pages if supported by CPU.
mov ebx, [sys_pgdir+(LFB_BASE shr 20)]
test ebx, PG_LARGE
jnz @f
mov ebx, [page_tabs+(LFB_BASE shr 10)]
@@:
mov eax, [edi]
mov edx, [edi+4]
add eax, [edi+8]
adc edx, [edi+12]
 
DEBUGF 1, "K : E820 %x%x - %x%x type %d\n", \
[edi+4], [edi],\
edx, eax, [edi+16]
add edi, 20
dec ecx
jnz @b
.done:
and ebx, not 0xFFF
add ebx, eax
call mtrr_get_real_type
cmp al, MEM_WC
jz .exit
; 5. The check at step 4 fails on Bochs:
; Bochs BIOS configures MTRRs in a strange way not respecting [cpu_phys_addr_width],
; so mtrr_reconfigure avoids to touch anything.
; However, Bochs core ignores MTRRs (keeping them only for rdmsr/wrmsr),
; so we don't care about proper setting for Bochs.
; Use northbridge PCI id to detect Bochs: it emulates either i440fx or i430fx
; depending on configuration file.
mov eax, [pcidev_list.fd]
cmp eax, pcidev_list ; sanity check: fail if no PCI devices
jz .fail
cmp [eax+PCIDEV.vendor_device_id], 0x12378086
jz .exit
cmp [eax+PCIDEV.vendor_device_id], 0x01228086
jnz .fail
.exit:
ret
.fail:
mov ebx, mtrr_user_message
mov ebp, notifyapp
call fs_execute_from_sysdir_param
ret
endp