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 |