; =============================================================================
;
; Litos - Initialize memory map, real mode
;
; =============================================================================
; This service uses miscellaneous BIOS functions to determine RAM memory size.
; Every usable RAM area (up to 2 GB of RAM, we cannot use more RAM) is added
; to memory bit map PageMemMap (in LITOS.ASM) which contains bit flag for every
; 4 KB RAM page - if a flag is set than RAM page is free to use. This memory
; bit map is 64 KB long and is used only in initialization process. Later it
; will be taken off by SysMemInit function (in KERNEL\SYSMEM.ASM) and freed.
; Note that PageMemMap is cleared when kernel starts.
;
; For memory below 1 MB we use information from Int 12h and not other functions
; due to possibility to lower end of this memory area by BIOS. Memory map is
; get from BIOS via Int 15h/0E820h, it returns fragments of memory (even above
; 4 GB of RAM address space). If this function is not supported then function
; Int 15h/0E801h is used. It returns size of extended memory above 1 MB.
; Finally, if this function is not supported, the Int 15h/88h function is used.
; It returns extended memory size from 1 MB to 16 MB (or 64 MB on some BIOSes).
; -----------------------------------------------------------------------------
%ifdef DEBUG
;%define DEBUG_MEM ; uncomment this to display MEM info
%endif
CODE_SECTION 16
; ------------- BIOS memory area structure
struc BIOSMEM
BIOSMEM_Addr: resd 2 ; 0: base address of this address range
BIOSMEM_Size: resd 2 ; 8: length (in bytes)
BIOSMEM_Type: resd 1 ; 10h: type (se below)
endstruc ; size 14h = 20 bytes
; ------------- BIOS memory area types
BIOSMEM_RAM EQU 1 ; RAM memory, available to OS
BIOSMEM_ROM EQU 2 ; ROM or device
BIOSMEM_ACPI EQU 3 ; ACPI Reclaim Memory
BIOSMEM_NVS EQU 4 ; ACPI NVS Memory
; -----------------------------------------------------------------------------
; Initialize memory map
; -----------------------------------------------------------------------------
; INPUT: DS = data segment
; DESTROYS: All registers except DS
; -----------------------------------------------------------------------------
; ------------- Debug display basic memory info
InitMem:
%ifdef DEBUG_MEM
call DebInitMem ; debug display basic memory info
%endif
; ------------- Add low memory (below 1 MB)
xor ebx,ebx ; EBX <- 0
mov bx,[LowMemory] ; EBX <- low memory (below 1 MB)
shl ebx,10 ; EBX <- convert KB to B
xor eax,eax ; EAX <- 0, begin of LOW memory
call AddMemReg ; add memory region
; ------------- Load system memory map using Int 15h/0E820h (only newer BIOSes)
xor ebx,ebx ; EBX <- 0 start searching map
mov ebp,200 ; EBP <- maximal number of regions
InitMem2: push ds ; DS <- data segment
pop es ; ES <- data segment
mov di,BiosMem ; DI <- BIOS memory buffer
mov eax,0e820h ; EAX <- function code
mov ecx,BIOSMEM_size; ECX <- 20, size of buffer
mov edx,534D4150h ; EDX <- magic ('SMAP')
stc ; preset error flag
push ebp ; push EBP
int 15h ; get memory map entry
pop ebp ; pop EBP
jc InitMem5 ; error
cmp eax,534D4150h ; check magic ('SMAP')
jne InitMem5 ; error
cmp ecx,byte BIOSMEM_size; check size of data
jne InitMem5 ; error
; ------------- Add memory region
cmp byte [di+BIOSMEM_Type],BIOSMEM_RAM ; is it RAM area?
jne InitMem4 ; it is not RAM area
cmp dword [di+BIOSMEM_Addr+4],byte 0 ; is it more than 4GB?
jne InitMem4 ; it is more than 4 GB
mov eax,[di+BIOSMEM_Addr] ; EAX <- address of region
cmp eax,0c0000h ; only memory above VRAM is accepted
jb InitMem4 ; unused memory area
push ebx ; push EBX
mov ebx,[di+BIOSMEM_Size] ; EBX <- size of region
cmp dword [di+BIOSMEM_Size+4],byte 0 ; more than 4 GB?
je InitMem3 ; area size is OK
or ebx,byte -1 ; limit size to 4 GB
InitMem3: call AddMemReg ; add memory region
pop ebx ; pop EBX
; ------------- Next memory region
InitMem4: dec ebp ; number of regions
jz InitMem5 ; end of regions
or ebx,ebx ; is it last entry?
jnz InitMem2 ; get next entry
; ------------- Check if function was successful
InitMem5: cmp dword [TotalMemory],2*1024*1024 ; minimum 2 MB of RAM
jae InitMem9 ; function was successful
; ------------- Get extended memory size using Int 15h/0E801h (above 1 MB)
mov ax,0e801h ; AX <- function code
xor bx,bx ; BX <- 0 preset invalid value
xor cx,cx ; CX <- 0 preset invalid value
xor dx,dx ; DX <- 0 preset invalid value
stc ; preset error flag
int 15h ; get total map > 64 MB
jc InitMem8 ; error
or ax,ax ; check value in AX
jnz InitMem6 ; AX is OK
or bx,bx ; check value in BX
jnz InitMem6 ; BX is OK
jcxz InitMem8 ; invalid function or no memory
xchg ax,cx ; AX <- use CX
mov bx,dx ; BX <- use DX
InitMem6: xor ecx,ecx ; ECX <- 0
xchg ax,cx ; ECX <- memory size below 16 MB
xor eax,eax ; EAX <- 0
xchg ax,bx ; EAX <- size of memory above 16 MB
shl eax,6 ; recalc 64 KB blocks to 1 KB chunks
add eax,ecx ; add chunks below 16 MB
cmp eax,SYSTEM_SIZE>>10 ; check memory size
jbe InitMem7 ; memory size is OK
mov eax,SYSTEM_SIZE>>10; limit memory size (avoid overflow)
InitMem7: shl eax,10 ; EAX <- recalc size of memory to bytes
xchg eax,ebx ; EBX <- size of memory
mov eax,1024*1024 ; EAX <- 1 MB, begin of memory
call AddMemReg ; add memory region
ret
; ------------- Get extended memory size using Int 15h/88h (1MB to 16 or 64MB)
InitMem8: mov ah,88h ; AH <- function code
stc ; preset error flag
int 15h ; get memory size
jc InitMem9 ; error, not supported
xor ebx,ebx ; EBX <- 0
xchg ax,bx ; EBX <- size of memory
shl ebx,10 ; EBX <- convert KB to B
mov eax,1024*1024 ; EAX <- 1 MB, begin of memory
call AddMemReg ; add memory region
InitMem9:
ret
; -----------------------------------------------------------------------------
; Add memory region
; -----------------------------------------------------------------------------
; INPUT: EAX = linear address of memory region
; EBX = size of memory region
; DS = data segment
; DESTROYS: EAX, EBX, ECX, EDX, SI, ES
; -----------------------------------------------------------------------------
; ------------- Debug display memory region info
AddMemReg:
%ifdef DEBUG_MEM
call DebInitReg ; debug display memory region info
%endif
; ------------- Page of end of region (-> EBX)
add ebx,eax ; EBX <- address of end of region
jc AddMemReg2 ; overflow
cmp ebx,PAGE_MAX<<PAGE_SHIFT ; maximal end of region
jbe AddMemReg3 ; end of region is OK
AddMemReg2: mov ebx,PAGE_MAX<<PAGE_SHIFT ; limit end of memory
AddMemReg3: cmp ebx,[MemoryMax] ; is it higher end of memory?
jb AddMemReg4 ; it is not higher end of memory
mov [MemoryMax],ebx ; new end of memory
AddMemReg4: shr ebx,PAGE_SHIFT ; page number of end of region
; ------------- Page of start of region (-> EAX)
cmp eax,KernelEnd-SYSTEM_ADDR ; is it in the kernel?
jae AddMemReg5 ; address is OK
mov eax,KernelEnd-SYSTEM_ADDR ; cut off kernel area
AddMemReg5: add eax,PAGE_SIZE-1 ; round up to next page boundary
shr eax,PAGE_SHIFT ; convert to page number of start
cmp eax,PAGE_MAX ; maximal page number
jb AddMemReg6 ; page number is OK
mov eax,PAGE_MAX ; limit page number
; ------------- Check if there is any other page
AddMemReg6: mov edx,ebx ; EDX <- end of region
sub edx,eax ; EDX <- number of pages
jbe AddMemReg9 ; there is no other page
; ------------- Address of byte in memory bit map (-> ES:SI)
mov ecx,eax ; ECX <- current page
shr ecx,3 ; ECX <- convert to byte offset
add ecx,PageMemMap-SYSTEM_ADDR ; ECX <- address in bit map
mov si,cx ; ESI <- offset of byte
shr ecx,4 ; ECX <- convert offset to segment
and si,byte 0fh ; SI <- offset of byte
mov es,cx ; ES <- segment of byte
; ------------- Check if set of 32 pages can be marked
test al,7 ; is address rounded to byte?
jnz AddMemReg7 ; address is not rounded
cmp edx,byte 32 ; remain 32 pages or more?
jb AddMemReg7 ; not enough pages remain
cmp dword [es:si],byte 0 ; is this set of pages marked?
jne AddMemReg7 ; some of pages are already marked
; ------------- Mark set of 32 pages
or dword [es:si],byte -1 ; mark all pages as free
add dword [TotalMemory],PAGE_SIZE*32 ; increase memory
jmp short AddMemReg8
; ------------- Mark one page
AddMemReg7: mov cl,al ; CL <- page number
mov ch,1 ; CH <- bit mask
and cl,7 ; CL <- bit offset
shl ch,cl ; rotate bit mask to its position
test [es:si],ch ; is this page already marked?
jnz AddMemReg8 ; page is already marked
or [es:si],ch ; mark this page
add dword [TotalMemory],PAGE_SIZE ; increase total memory
; ------------- Next page
AddMemReg8: inc eax ; next page number
jmp short AddMemReg6
AddMemReg9: ret
; -----------------------------------------------------------------------------
; Debug display basic memory info
; -----------------------------------------------------------------------------
%ifdef DEBUG_MEM
; ------------- Display kernel size
DebInitMem: mov si,MemoryKerMsg ; SI <- debug text
call BIOSDispText ; display message
mov eax,KernelEnd ; EAX <- end of kernel
shr eax,10 ; convert to KB
call BIOSDispNum ; display size of kernel
; ------------- Display kernel start of data section
mov si,MemoryKerData; SI <- debug text
call BIOSDispText ; display message
mov eax,DataStart
mov edx,eax
shr edx,16
call BIOSDispHexDWrd
; ------------- Display kernel start of code section
mov si,MemoryKerCode; SI <- debug text
call BIOSDispText ; display message
mov eax,CodeStart
mov edx,eax
shr edx,16
call BIOSDispHexDWrd
; ------------- Display kernel start of fixup section
mov si,MemoryKerFix; SI <- debug text
call BIOSDispText ; display message
mov eax,FixStart
mov edx,eax
shr edx,16
call BIOSDispHexDWrd
; ------------- Display kernel start of exc section
mov si,MemoryKerExc; SI <- debug text
call BIOSDispText ; display message
mov eax,ExcStart
mov edx,eax
shr edx,16
call BIOSDispHexDWrd
; ------------- Display kernel start of exc2 section
mov si,MemoryKerExc2; SI <- debug text
call BIOSDispText ; display message
mov eax,Exc2Start
mov edx,eax
shr edx,16
call BIOSDispHexDWrd
; ------------- Display kernel start of bss section
mov si,MemoryKerBss; SI <- debug text
call BIOSDispText ; display message
mov eax,BSSStart
mov edx,eax
shr edx,16
call BIOSDispHexDWrd
; ------------- Display kernel end
mov si,MemoryKerEnd; SI <- debug text
call BIOSDispText ; display message
mov eax,BSSEnd
mov edx,eax
shr edx,16
call BIOSDispHexDWrd
; ------------- Display low memory
mov si,MemoryLowMsg ; SI <- debug text
call BIOSDispText ; display message
mov ax,[LowMemory] ; low memory in KB
call BIOSDispNum ; display size of low memory
mov si,MemoryMapMsg ; SI <- debug text
call BIOSDispText ; display message
ret
; -----------------------------------------------------------------------------
; Debug display memory region info
; -----------------------------------------------------------------------------
; INPUT: EAX = linear address of memory region
; EBX = size of memory region
; DS = data segment
; -----------------------------------------------------------------------------
; ------------- Push registers
DebInitReg: push eax ; push EAX
push si ; push SI
; ------------- Start of memory region
push eax ; push EAX
push eax ; push EAX
shr eax,16 ; EAX <- address of region HIGH
call BIOSDispHexWord ; display base address of region HIGH
mov al,"'" ; AL <- separator
call BIOSDispChar ; display separator character
pop eax ; pop EAX
call BIOSDispHexWord ; display base address of region LOW
; ------------- Display separator " - "
call BIOSDispSpc ; display space character
mov al,"-" ; AL <- separator
call BIOSDispChar ; display separator character
call BIOSDispSpc ; display space character
; ------------- End of memory region
pop eax ; pop EAX
add eax,ebx ; EAX <- end of memory region
dec eax ; EAX <- last address of memory region
push eax ; push EAX
shr eax,16 ; EAX <- address of region HIGH
call BIOSDispHexWord ; display base address of region HIGH
mov al,"'" ; AL <- separator
call BIOSDispChar ; display separator character
pop eax ; pop EAX
call BIOSDispHexWord ; display base address of region LOW
; ------------- Size of memory region
mov al,"," ; AL <- separator
call BIOSDispChar ; display separator character
call BIOSDispSpc ; display space character
mov eax,ebx ; EAX <- region size
shr eax,10 ; EAX <- region size in KB
mov si,MemoryMapKB ; SI <- text KB
cmp eax,1024 ; is big region?
jb DebInitReg2 ; it is little region
shr eax,10 ; EAX <- region size in MB
mov si,MemoryMapMB ; SI <- text MB
DebInitReg2: call BIOSDispNum ; display region size
call BIOSDispText ; display text
; ------------- Pop registers
pop si ; pop SI
pop eax ; pop EAX
ret
%endif
; -----------------------------------------------------------------------------
; Data
; -----------------------------------------------------------------------------
DATA_SECTION
; ------------- Low memory below 1 MB (in KB, used only by initialization)
align 2, db 0
LowMemory: dw 0 ; memory below 1 MB (in KB)
; ------------- BIOS memory structure (used only in initialization)
align 8, db 0
BiosMem: dd 0,0 ; 0: base address of this address range
dd 0,0 ; 8: length (in bytes)
dd 0 ; 10h: type
; ------------- Debug messages
%ifdef DEBUG_MEM
MemoryKerMsg: db 13,10,'--- Memory:',13,10
db 'Kernel size in low memory: ',0
MemoryKerData: db ' KB',13,10,' Kernel data section: ',0
MemoryKerCode: db 13,10,' Kernel code section: ',0
MemoryKerFix: db 13,10,' Kernel fixup section: ',0
MemoryKerExc: db 13,10,' Kernel exc section: ',0
MemoryKerExc2: db 13,10,' Kernel exc2 section: ',0
MemoryKerBss: db 13,10,' Kernel bss section: ',0
MemoryKerEnd: db 13,10,' Kernel end: ',0
MemoryLowMsg: db 13,10,'Real memory (below 1 MB): ',0
MemoryMapMsg: db ' KB',13,10,'Memory regions (start - end, size):',13,10,0
MemoryMapKB: db ' KB',13,10,0
MemoryMapMB: db ' MB',13,10,0
%endif
|