feat(kernel): vmem_size, vmem_add

This commit is contained in:
d0p1 🏳️‍⚧️ 2025-01-23 14:12:55 +01:00
parent fb77f055c5
commit f676062269
3 changed files with 530 additions and 55 deletions

View file

@ -12,10 +12,10 @@ mm_init:
mov eax, stVmemKernel
push dword 4
push dword 5
push dword 0
push dword 0
push dword 0
push dword 4096
mov edx, PMM_VIRT_TEMP
mov ecx, kend
@ -25,6 +25,13 @@ mm_init:
call vmem_create
add esp, 24
mov eax, stVmemKernel
mov edx, 4096
mov ecx, VMEM_VM_BESTFIT
call vmem_alloc
xchg bx, bx
ret
szVmemKernelName db "kmem", 0

View file

@ -3,41 +3,73 @@
;; General purpose resource allocator.
;; Based on <tinyvmem at https://github.com/rdmsr/tinyvmem> and on <Bonwick's paper at https://www.usenix.org/legacy/event/usenix01/full_papers/bonwick/bonwick.pdf>
;;; MARK: macros and structs
;;; MARK: Macros and structs
VM_BESTFIT = (1 shl 0)
VM_INSTANTFIT = (1 shl 1)
VM_NEXTFIT = (1 shl 2)
VM_SLEEP = (1 shl 3)
VM_NOSLEEP = (1 shl 4)
VM_BOOTSTRAP = (1 shl 5)
;; Constant: VMEM_VM_BESTFIT
VMEM_VM_BESTFIT = (1 shl 0)
;; Constant: VMEM_VM_INSTANTFIT
VMEM_VM_INSTANTFIT = (1 shl 1)
;; Constant: VMEM_VM_SLEEP
VMEM_VM_SLEEP = (1 shl 3)
;; Constant: VMEM_VM_NOSLEEP
VMEM_VM_NOSLEEP = (1 shl 4)
;; Constant: VMEM_VM_BOOTSTRAP
VMEM_VM_BOOTSTRAP = (1 shl 5)
;; Constant: VMEM_VM_POPULATING
VMEM_VM_POPULATING = (1 shl 6)
STATIC_SEG_COUNT = 64
;; Constant: VMEM_VM_FIT_MASK
VMEM_VM_FIT_MASK = (VMEM_VM_BESTFIT or VMEM_VM_INSTANTFIT)
FREELISTS_N = 4 * 8
HASHTABLE_N = 16
;; Constant: VMEM_STATIC_SEG_COUNT
VMEM_STATIC_SEG_COUNT = 64
SEGMENT_ALLOCATED = 0
SEGMENT_FREE = 1
SEGMENT_SPAN = 2
;; Constant: VMEM_MIN_RESERVE
VMEM_MIN_RESERVE = 4
macro GET_LIST reg, N {
;; Constant: VMEM_FREELISTS_N
VMEM_FREELISTS_N = 4 * 8
;; Constant: VMEM_HASHTABLE_N
VMEM_HASHTABLE_N = 16
;; Constant: VMEM_SEGMENT_ALLOCATED
VMEM_SEGMENT_ALLOCATED = 0
;; Constant: VMEM_SEGMENT_FREE
VMEM_SEGMENT_FREE = 1
;; Constant: VMEM_SEGMENT_SPAN
VMEM_SEGMENT_SPAN = 2
;; Constant: VMEM_SEGMENT_STATIC_SPAN
VMEM_SEGMENT_STATIC_SPAN = 3
;; Constant: VMEM_ALLOC
VMEM_ALLOC = (1 shl 0)
;; Constant: VMEM_FREE
VMEM_FREE = (1 shl 1)
;; Constant: VMEM_MIN_ADDR
VMEM_ADDR_MIN = 0
;; Constant: VMEM_MAX_ADDR
VMEM_ADDR_MAX = -1
;; macro: VMEM_GET_LIST_FROM_SIZE
macro VMEM_GET_LIST_FROM_SIZE reg, N {
bsr reg, reg
xor reg, 31
add reg, -N
neg reg
sub reg, 1
}
struc VmSegment {
.type db ?
.imported db ?
;; Struc: VmemSegment
struc VmemSegment {
.type dd ? ; TODO: rework VmemSegment struct
.base dd ?
.size dd ?
.spanlist ListEntry
.segqueue TailQEntry
.seglist ListEntry
}
DEFN VmSegment
DEFN VmemSegment
;; Struc: Vmem
struc Vmem {
@ -50,13 +82,15 @@ struc Vmem {
.source dd ?
.flags dd ?
.freelist dd FREELISTS_N dup(0)
.hashtable dd HASHTABLE_N dup(0)
.spanlist ListHead 0
.in_use dd ?
.segqueue TailQHead
.freelist dd VMEM_FREELISTS_N dup(0)
.hashtable dd VMEM_HASHTABLE_N dup(0)
}
DEFN Vmem
;;; MARK: private functions
;;; MARK: Private functions
;; Function: _murmur32
;;
@ -100,7 +134,7 @@ __hash_table_addr:
mov esi, eax
mov eax, edx
call _murmur32
and eax, HASHTABLE_N-1 ; x % y (y is a power of two)
and eax, VMEM_HASHTABLE_N-1 ; x % y (y is a power of two)
shl eax, 2
mov edx, esi
@ -119,7 +153,7 @@ __hash_table_addr:
;;
_vmem_segfree:
; XXX: lock
add eax, VmSegment.seglist
add eax, VmemSegment.seglist
mov edx, pVmemFreeSegHead
xchg eax, edx
call list_insert_head
@ -132,11 +166,12 @@ _vmem_segfree:
;; Function: _vmem_segalloc
;;
;; Out:
;; EAX - Pointer to <VmSegment> object on success, 0 on error
;; EAX - Pointer to <VmemSegment> object on success, 0 on error
_vmem_segalloc:
push ebp
mov ebp, esp
; XXX: lock
mov eax, [pVmemFreeSegHead]
or eax, eax
jz .end
@ -146,9 +181,11 @@ _vmem_segalloc:
call list_remove
sub ebx, VmSegment.seglist
sub ebx, VmemSegment.seglist
dec [iVmemFreeSegCount]
mov eax, ebx
pop ebx
.end:
@ -156,8 +193,146 @@ _vmem_segalloc:
leave
ret
;; Function: _vmem_refill
;;
_vmem_refill:
push ebp
mov ebp, esp
;;; MARK: public functions
xor eax, eax
; TODO: allocate page
;call kalloc()
or eax, eax
jz .end
push esi
push edi
mov esi, eax
xor edi, edi
@@:
mov eax, [esi]
call _vmem_segfree
inc [iVmemFreeSegCount]
add esi, sizeof.VmemSegment
add edi, sizeof.VmemSegment
cmp edi, PAGE_SIZE
jl @b
pop edi
pop esi
.end:
leave
ret
;; Function: _vmem_segfit
;;
;; Check if a <VmemSegment> meet restrictions.
;;
;; In:
;; EAX - seg
;; EDX - size
;; ECX - align
;; ESP[4] - phase
;; ESP[8] - nocross
;; ESP[12] - minaddr
;; ESP[16] - macaddr
;; ESP[20] - addrp
;;
_vmem_segfit:
xor eax, eax
ret
;; Function: _vmem_add
;;
;; In:
;; EAX - Pointer to <Vmem> object.
;; EDX - Address.
;; ECX - Size in bytes.
;; ESP[4] - Flags. (<VMEM_VM_SLEEP> or <VMEM_VM_NOSLEEP>)
;; ESP[8] - Span type (<VMEM_SEGMENT_SPAN> or <VMEM_SEGMENT_STATIC_SPAN>)
;;
;; Out:
;; EAX - 0 on success.
_vmem_add:
push ebp
mov ebp, esp
sub esp, 8
push esi
push edi
push ebx
mov esi, eax
mov edi, edx
mov ebx, ecx
; create new span
call _vmem_segalloc
or eax, eax
jz .end ; TODO: error handling
mov [eax + VmemSegment.base], edi
mov [eax + VmemSegment.size], ebx
mov edx, [ebp + 12] ; get span type
mov [eax + VmemSegment.type], edx
mov [ebp - 8], eax
; create free segment
call _vmem_segalloc
or eax, eax
jz .end ; TODO: error handling
mov [eax + VmemSegment.base], edi
mov [eax + VmemSegment.size], ebx
mov [eax + VmemSegment.type], VMEM_SEGMENT_FREE
mov [ebp - 4], eax
mov edx, [ebp - 8]
add edx, VmemSegment.segqueue
mov eax, esi
add eax, Vmem.segqueue
push eax
push edx
call tailq_insert_tail
pop edx
pop eax
mov ecx, [ebp - 4]
add ecx, VmemSegment.segqueue
call tailq_insert_after
VMEM_GET_LIST_FROM_SIZE ebx, VMEM_FREELISTS_N
shl ebx, 2
mov eax, esi
add eax, Vmem.freelist
add eax, ebx
mov edx, [ebp - 4]
add edx, VmemSegment.seglist
call list_insert_head
.end:
pop ebx
pop edi
pop esi
leave
ret
;; Function: _vmem_import
;;
_vmem_import:
ret
;;; MARK: Public functions
;; Function: vmem_create
;;
@ -172,7 +347,7 @@ _vmem_segalloc:
;; ESP[12] - Alloc function.
;; ESP[16] - Free function.
;; ESP[20] - Source.
;; ESP[24] - Flags. (<VM_SLEEP> or <VM_NOSLEEP>)
;; ESP[24] - Flags. (<VMEM_VM_SLEEP> or <VMEM_VM_NOSLEEP>)
vmem_create:
push ebp
mov ebp, esp
@ -220,17 +395,10 @@ vmem_create:
add edi, Vmem.size
rep movsd
; initialize segqueue
mov eax, [ebp - 8]
mov edx, [ebp - 4]
or edx, edx
jnz @f
mov ecx, [eax + Vmem.size]
or ecx, ecx
jz @f
push dword [eax + Vmem.flags]
call vmem_add
add esp, 4
@@:
add eax, Vmem.segqueue
mov [eax + 4], eax
if CONFIG_TRACE_VMEM
mov esi, [ebp - 8]
@ -240,6 +408,19 @@ if CONFIG_TRACE_VMEM
TRACE szTraceVmemCreate, esi, edx, ecx
end if
mov eax, [ebp - 8]
mov edx, [ebp + 24]
or edx, edx
jnz @f
mov ecx, [eax + Vmem.size]
or ecx, ecx
jz @f
mov edx, [ebp - 4]
push dword [eax + Vmem.flags]
call vmem_add
add esp, 4
@@:
pop edi
pop esi
@ -267,6 +448,14 @@ vmem_destroy:
;; Out:
;; EAX - Allocated address on succes, 0 on failure.
vmem_alloc:
push ecx
push dword VMEM_ADDR_MAX
push dword VMEM_ADDR_MIN
push dword 0
push dword 0
xor ecx, ecx
call vmem_xalloc
add esp, 20
ret
;; Function: vmem_free
@ -290,11 +479,231 @@ vmem_free:
;; ESP[8] - Nocross.
;; ESP[12] - Min address.
;; ESP[16] - Max address.
;; ESP[20] - Flags. (<VM_BESTFIT>, <VM_INSTANTFIT>, <VM_NEXTFIT>, <VM_SLEEP>, <VM_NOSLEEP>)
;; ESP[20] - Flags. (<VMEM_VM_BESTFIT>, <VMEM_VM_INSTANTFIT>, <VMEM_VM_SLEEP>, <VMEM_VM_NOSLEEP>)
;;
;; Out:
;; EAX - Allocated address on succes, 0 on failure.
vmem_xalloc:
push ebp
mov ebp, esp
sub esp, 20
push esi
push edi
push ebx
mov esi, eax
mov edi, edx
mov ebx, ecx
; refill segment if needed
mov eax, [ebp + 24]
and eax, VMEM_VM_POPULATING
jz @f
cmp [iVmemFreeSegCount], VMEM_MIN_RESERVE
jg @f
call _vmem_refill
@@:
xor eax, eax
mov [ebp - 8], eax
mov [ebp - 4], eax
call _vmem_segalloc
or eax, eax
jz .fail
mov [ebp - 8], eax
call _vmem_segalloc
or eax, eax
jz .fail
mov [ebp - 4], eax
; set alignment to quantum if not set
or ebx, ebx
jnz @f
mov ebx, [esi + Vmem.quantum]
mov eax, [ebp + 24]
and eax, VMEM_VM_FIT_MASK
jz .fail ; TODO: error
.retry_strat:
; get freelist
mov edx, edi
VMEM_GET_LIST_FROM_SIZE edx, VMEM_FREELISTS_N
shl edx, 2
mov eax, esi
add eax, Vmem.freelist
add eax, edx
mov [ebp - 12], eax
.retry:
mov eax, [ebp + 24]
and eax, VMEM_VM_INSTANTFIT
jz .best_fit
; if not power of two use next
mov eax, edi
mov edx, eax
dec edx
and eax, edx
jz @f
add dword [ebp - 12], 4
@@:
mov eax, esi
add eax, Vmem.hashtable
cmp [ebp - 12], eax
jge .next
mov eax, [ebp - 12]
mov eax, [eax]
or eax, eax
jz .instant_fit_next
sub eax, VmemSegment.seglist
; save segment
mov [ebp - 16], eax
; block found
lea edx, [ebp - 20]
push edx ; &start
push dword [ebp + 20] ; maxaddr
push dword [ebp + 16] ; min addr
push dword [ebp + 12] ; nocross
push dword [ebp + 8] ; phase
mov edx, edi
mov ecx, ebx
call _vmem_segfit
add esp, 20
or eax, eax
jz .found
.instant_fit_next:
add dword [ebp - 12], 4
jmp @b
.best_fit:
; iterate each item in list
xchg bx, bx
mov eax, [ebp - 12]
.best_fit_loop:
mov eax, [eax]
or eax, eax
jz .best_fit_next
sub eax, VmemSegment.seglist
mov [ebp - 16], eax
cmp edi, [eax + VmemSegment.size]
jge @f
; block found
lea edx, [ebp - 20]
push edx ; &start
push dword [ebp + 20] ; maxaddr
push dword [ebp + 16] ; min addr
push dword [ebp + 12] ; nocross
push dword [ebp + 8] ; phase
mov edx, edi
mov ecx, ebx
call _vmem_segfit
add esp, 20
or eax, eax
jz .found
@@:
mov eax, [ebp - 16]
add eax, VmemSegment.seglist.next
jmp .best_fit_loop
.best_fit_next:
add dword [ebp - 12], 4
mov eax, esi
add eax, Vmem.hashtable
cmp [ebp - 12], eax
jl .best_fit
.next:
if 0
; NetBSD does this
; https://github.com/NetBSD/src/blob/trunk/sys/kern/subr_vmem.c#L1296-L1301
mov eax, [ebp + 24]
and eax, VMEM_VM_INSTANTFIT
jz @f
or eax, VMEM_VM_BESTFIT
mov edx, VMEM_VM_INSTANTFIT
not edx
and eax, edx
jmp .retry_strat
@@:
end if
; return error and free allocated segment if needed
.fail:
mov eax, [ebp - 8]
or eax, eax
jz @f
call _vmem_segfree
@@:
mov eax, [ebp - 4]
or eax, eax
jz @f
call _vmem_segfree
@@:
xor eax, eax
mov edx, ENOMEM
jmp .end
.found:
; remove seg from freelist
mov eax, [ebp - 16]
add eax, VmemSegment.seglist
call list_remove
; split left
mov eax, [ebp - 16]
mov eax, [eax + VmemSegment.base]
cmp eax, [ebp - 20]
je @f
mov edx, [ebp - 8]
mov dword [edx + VmemSegment.type], VMEM_SEGMENT_FREE
mov [edx + VmemSegment.base], eax
mov ecx, [ebp - 20]
sub ecx, eax
mov [edx + VmemSegment.size], ecx
mov eax, [ebp - 16]
mov dword [eax + VmemSegment.base], [ebp - 20]
mov edx, [eax + VmemSegment.size]
sub edx, ecx
mov [eax + VmemSegment.size], edx
; insert to freelist
VMEM_GET_LIST_FROM_SIZE ecx, VMEM_FREELISTS_N
shl ecx, 2
mov eax, esi
add eax, Vmem.freelist
add eax, ecx
mov edx, [ebp - 8]
add edx, VmemSegment.seglist
call list_insert_head
mov dword [ebp - 8], 0
@@:
; split right
.end:
pop ebx
pop esi
pop edi
pop ebx
leave
ret
;; Function: vmem_xfree
@ -316,7 +725,7 @@ vmem_xfree:
;; EAX - Pointer to <Vmem> object.
;; EDX - Address.
;; ECX - Size in bytes.
;; ESP[4] - Flags. (<VM_SLEEP> or <VM_NOSLEEP>)
;; ESP[4] - Flags. (<VMEM_VM_SLEEP> or <VMEM_VM_NOSLEEP>)
;;
;; Out:
;; EAX - Return Address on success, 0 on failure.
@ -324,22 +733,69 @@ vmem_add:
push ebp
mov ebp, esp
push esi
if CONFIG_TRACE_VMEM
sub esp, 12
mov [ebp - 12], eax
mov [ebp - 8], edx
mov [ebp - 4], ecx
end if
push edi
push ebx
; save flag to edi
mov edi, [ebp + 8]
mov esi, eax
mov edi, ebx
mov ebx, ecx
push dword VMEM_SEGMENT_STATIC_SPAN
push edi
call _vmem_add
add esp, 8
pop ebx
pop edi
pop esi
if CONFIG_TRACE_VMEM
mov edx, [ebp - 8]
mov ecx, [ebp - 4]
add edx, ecx
TRACE szTraceVmemAdd, [ebp - 12], [ebp - 8], edx
end if
leave
ret
;; Function: vmem_size
;;
;; Return information about arenas size
;;
;; In:
;; EAX - Pointer to <Vmem> object.
;; EDX - Type mask.
;;
;; Out:
;; EAX - Free/allocated size in arena.
vmem_size:
xchg edx, eax
and eax, (VMEM_ALLOC or VMEM_FREE)
cmp eax, VMEM_ALLOC
jne @f
mov eax, [edx + Vmem.in_use]
ret
@@:
cmp eax, VMEM_FREE
mov eax, [edx + Vmem.size]
jne @f
mov edx, [edx + Vmem.in_use]
sub eax, edx
ret
@@:
ret
if CONFIG_TRACE_VMEM
;; Function: vmem_dump
;;
vmem_dump:
ret
end if
;; Function: vmem_bootstrap
;;
;; Initialize static segments
@ -361,20 +817,28 @@ end if
call _vmem_segfree
add ebx, sizeof.VmSegment
cmp ebx, STATIC_SEG_COUNT*sizeof.VmSegment
add ebx, sizeof.VmemSegment
cmp ebx, VMEM_STATIC_SEG_COUNT*sizeof.VmemSegment
jl @b
pop ebx
leave
ret
;;; MARK: variables
;;; MARK: Variables
aVmemStaticSegs db STATIC_SEG_COUNT*sizeof.VmSegment dup(0)
;; Variable: aVmemStaticSegs
aVmemStaticSegs db VMEM_STATIC_SEG_COUNT*sizeof.VmemSegment dup(0)
;; Variable: pVmemFreeSegHead
pVmemFreeSegHead dd 0
;; Variable: iVmemFreeSegCount
iVmemFreeSegCount dd 0
szVmemSegmentAllocated db "allocated", 0
szVmemSegmentFree db "free", 0
szVmemSegmentSpan db "span", 0
;; Group: Debug
if CONFIG_TRACE_VMM
szTraceVmemBootstrap db "Trace(VMEM): Bootstrap vmem", 0
szTraceVmemCreate db "Trace(VMEM): Create %s [%x, %x)", 0

View file

@ -113,6 +113,10 @@ tailq_insert_after:
ret
;; Function: tailq_insert_tail
;;
;; In:
;; EAX - X
;; EDX - X
tailq_insert_tail:
ret