① [mm] BAD blk 的解码:site=2=free_block、reason=1=magic 失配;blk 实际是 slab_page+16(页在 0x1b56020),sz 是该页 free_head,而非块大小。被踩的是页头前 8~16 字节,最可能是无锁 buddy 在 usb.init(sched.enable 之后)与用户进程 MMAP 并发 malloc 时重复授予同一 8KB 块。② mm 加固:_slab_find_page 不再只看 magic,新增 _slab_page_owns(block_ptr 回指、指针在对象区内、按 obj_size 对齐、序号 < total_objs);slab_page 新增 alloc_bits,只有已分配对象可 free,重复释放/野指针被拒,避免 free_count 提前到 total_objs 把仍在用的页拆回 buddy;free_obj 拒绝已全空继续收对象;_create_page 失败路径清零 magic。③ hid_ascii 仅只读且已有边界检查,非本次堆损坏来源;usb_kbd 把 code >= 64 改为显式早返回。④ 关闭按钮:按下只置 close_down 并置脏(按钮画按下态:CLASSIC 40,40,70→20,20,45;WIN7/XP 红底 200,50,50→140,25,25;WIN10/11 补深灰底),松手且仍命中才发 CLOSE + used=0,移出则撤销按下态恢复;几何复用 _close_btn_* 同一套函数。
884 lines
37 KiB
Plaintext
884 lines
37 KiB
Plaintext
import bootinfo
|
||
import paging.paging as paging
|
||
import string
|
||
import drivers.serial.uart.serial as serial
|
||
import viperlib
|
||
|
||
|
||
# 伙伴系统常量定义
|
||
define MIN_BLOCK_SIZE = 4096 # 最小块大小(4KB)
|
||
define MAX_BLOCK_SIZE = 268435456 # 最大块大小(256MB)
|
||
define MAX_ORDER = 18 # 最大阶数(2^18 = 262144)
|
||
|
||
# 内存块状态
|
||
define BLOCK_FREE = 0 # 空闲
|
||
define BLOCK_USED = 1 # 已使用
|
||
|
||
# 内存类型定义
|
||
define MEMORY_TYPE_AVAILABLE: uint64 = 7
|
||
define MEMORY_TYPE_RESERVED: uint64 = 0
|
||
define MEMORY_TYPE_LOADER_CODE: uint64 = 1
|
||
define MEMORY_TYPE_LOADER_DATA: uint64 = 2
|
||
define MEMORY_TYPE_BOOT_SERVICES_CODE: uint64 = 3
|
||
define MEMORY_TYPE_BOOT_SERVICES_DATA: uint64 = 4
|
||
define MEMORY_TYPE_RUNTIME_SERVICES_CODE: uint64 = 5
|
||
define MEMORY_TYPE_RUNTIME_SERVICES_DATA: uint64 = 6
|
||
define MEMORY_TYPE_ACPI_RECLAIMABLE: uint64 = 9
|
||
define MEMORY_TYPE_ACPI_NVS: uint64 = 10
|
||
define MEMORY_TYPE_BAD_MEMORY: uint64 = 8
|
||
|
||
# 内存区域结构
|
||
class memory_region:
|
||
start: uint64
|
||
end: uint64
|
||
size: uint64
|
||
type: uint32
|
||
next: memory_region*
|
||
|
||
# 全局内存区域链表
|
||
static memory_regions: memory_region* = None
|
||
|
||
# 坏块报告限流计数:同一处坏块会被反复检出(每次分配都撞到坏表头),前几次打全量、
|
||
# 之后只提示一次,避免串口被同一份报告淹没。
|
||
static _mm_bad_reports: int = 0
|
||
|
||
# 内存块结构
|
||
# 说明:state/order 之后正好有 2 字节 padding,magic 塞进去 ⇒ 结构仍是 32 字节、
|
||
# 布局不变(next@+0x10、prev@+0x18),不会改变任何载荷偏移。
|
||
class memory_block:
|
||
size: uint64 # 块大小
|
||
state: uint8 # 块状态
|
||
order: uint8 # 块阶数
|
||
magic: uint32 # 块头魔数:任何字段被越界写覆盖时通常先在这里失配(比范围/对齐检查更早)
|
||
next: memory_block* # 下一个块
|
||
prev: memory_block* # 上一个块
|
||
|
||
# 块头魔数("BLK!")
|
||
define BLOCK_MAGIC = 0x424C4B21
|
||
|
||
# 伙伴系统结构
|
||
class __buddy_system:
|
||
free_lists: [MAX_ORDER + 1]memory_block*
|
||
total_memory: uint64 # 总内存大小
|
||
used_memory: uint64 # 已使用内存大小
|
||
free_memory: uint64 # 空闲内存大小
|
||
start_addr: rawptr # 内存起始地址
|
||
end_addr: rawptr # 内存结束地址
|
||
|
||
|
||
define SLAB_MAGIC = 0x534C4142
|
||
define SLAB_NUM_SIZES = 6
|
||
define SLAB_THRESHOLD = 1024
|
||
define SLAB_BITMAP_BYTES = 32
|
||
|
||
class slab_page:
|
||
magic: uint32
|
||
obj_size: uint16
|
||
free_count: uint16
|
||
data_offset: uint16
|
||
total_objs: uint16
|
||
free_head: rawptr
|
||
block_ptr: memory_block*
|
||
next: slab_page*
|
||
prev: slab_page*
|
||
# 每对象 1 bit:只有置位的对象才能被 free。释放未分配对象(重复释放/野指针撞上
|
||
# 页内空闲对象)会让 free_count 提前到达 total_objs ⇒ 页被拆回 buddy,而页内仍
|
||
# 有活对象,随后它的 free 就只剩 magic=0 的旧页头(现场 0x1b56030 即 page+16)。
|
||
alloc_bits: [SLAB_BITMAP_BYTES]uint8
|
||
|
||
|
||
# 4K对齐函数
|
||
def align_4k(addr: uint64) -> uint64:
|
||
return (addr + 0xFFF) & ~0xFFF
|
||
|
||
# 计算阶数
|
||
static def get_order(size: uint64) -> int:
|
||
order: int = 0
|
||
while size > MIN_BLOCK_SIZE:
|
||
size >>= 1
|
||
order += 1
|
||
return order
|
||
|
||
# 计算块大小
|
||
static def get_block_size(order: int) -> uint64:
|
||
# 4096 << 20 = 4GB 超出 i32 范围 → 0x0 → 伙伴初始化无限循环(bs=0x0)。
|
||
# 必须用 64 位移位(4096<<20 = 4294967296)。
|
||
return uint64(MIN_BLOCK_SIZE) << uint64(order)
|
||
|
||
|
||
class _BuddySystemObject:
|
||
# 全局伙伴系统实例
|
||
buddy: __buddy_system
|
||
# 初始化伙伴系统
|
||
def __init__(self, start_addr: rawptr, size: uint64):
|
||
for i in range(MAX_ORDER + 1):
|
||
self.buddy.free_lists[i] = None
|
||
self.buddy.total_memory = size
|
||
self.buddy.used_memory = 0
|
||
self.buddy.free_memory = size
|
||
self.buddy.start_addr = start_addr
|
||
self.buddy.end_addr = rawptr(uint64(start_addr) + size)
|
||
remaining: uint64 = size
|
||
cur_addr: uint64 = uint64(start_addr)
|
||
while remaining >= MIN_BLOCK_SIZE:
|
||
order: int = 0
|
||
tmp: uint64 = remaining
|
||
while tmp > MIN_BLOCK_SIZE:
|
||
tmp >>= 1
|
||
order += 1
|
||
block_size: uint64 = get_block_size(order)
|
||
while block_size > remaining and order > 0:
|
||
order -= 1
|
||
block_size = get_block_size(order)
|
||
if block_size > remaining: break
|
||
block: memory_block* = memory_block*(cur_addr)
|
||
block.size = block_size
|
||
block.state = BLOCK_FREE
|
||
block.order = uint8(order)
|
||
block.magic = uint32(BLOCK_MAGIC)
|
||
block.next = None
|
||
block.prev = None
|
||
if order <= MAX_ORDER:
|
||
if self.buddy.free_lists[order]:
|
||
block.next = self.buddy.free_lists[order]
|
||
self.buddy.free_lists[order].prev = block
|
||
self.buddy.free_lists[order] = block
|
||
cur_addr += block_size
|
||
remaining -= block_size
|
||
|
||
# 分割内存块
|
||
def split_block(self, block: memory_block*, target_order: int) -> memory_block*:
|
||
current_order: int = block.order
|
||
# 从当前阶数向下分割,直到达到目标阶数
|
||
while current_order > target_order:
|
||
current_order -= 1
|
||
new_block_size: uint64 = get_block_size(current_order)
|
||
# 创建新块
|
||
new_block: memory_block* = memory_block*(uint64(block) + new_block_size)
|
||
new_block.size = new_block_size
|
||
new_block.state = BLOCK_FREE
|
||
new_block.order = uint8(current_order)
|
||
new_block.magic = uint32(BLOCK_MAGIC)
|
||
new_block.next = None
|
||
new_block.prev = None
|
||
# 更新原块大小和阶数
|
||
block.size = new_block_size
|
||
block.order = uint8(current_order)
|
||
# 将新块添加到对应阶数的空闲链表
|
||
if current_order <= MAX_ORDER:
|
||
new_block.next = self.buddy.free_lists[current_order]
|
||
if self.buddy.free_lists[current_order]:
|
||
self.buddy.free_lists[current_order].prev = new_block
|
||
self.buddy.free_lists[current_order] = new_block
|
||
return block
|
||
|
||
# 块头一致性检查:0 = OK;1 = magic 失配;2 = size/order 不一致;3 = state 不符;4 = prev/next 越界或未对齐
|
||
# want_state < 0 表示不校验 state。任一项不符 ⇒ 块头被越界写 / 释放后又被写覆盖。
|
||
static def _check_block(self, block: memory_block*, want_state: int) -> int:
|
||
if block.magic != uint32(BLOCK_MAGIC): return 1
|
||
if int(block.order) > MAX_ORDER: return 2
|
||
if block.size != get_block_size(int(block.order)): return 2
|
||
if want_state >= 0 and int(block.state) != want_state: return 3
|
||
_s: uint64 = uint64(self.buddy.start_addr)
|
||
_e: uint64 = uint64(self.buddy.end_addr)
|
||
_pv: uint64 = uint64(block.prev)
|
||
_nx: uint64 = uint64(block.next)
|
||
if _pv != 0 and (_pv < _s or _pv >= _e or (_pv & 7) != 0): return 4
|
||
if _nx != 0 and (_nx < _s or _nx >= _e or (_nx & 7) != 0): return 4
|
||
return 0
|
||
|
||
# 坏块报告(加固):块头一行摘要。site: 1=allocate_block 2=free_block 3=merge_blocks
|
||
# ctx = 触发检查的那一阶(merge/allocate 的 order)。
|
||
static def _report_bad_block(self, site: int, reason: int, block: memory_block*, order: int):
|
||
global _mm_bad_reports
|
||
_mm_bad_reports += 1
|
||
if _mm_bad_reports > 4:
|
||
if _mm_bad_reports == 5:
|
||
serial.puts("[mm] (further BAD blk reports suppressed)\n")
|
||
return
|
||
_rb: [224]char
|
||
viperlib.snprintf(&_rb, 224, "[mm] BAD blk site=%d reason=%d blk=0x%lx sz=0x%lx st=%d ord=%d ctx=%d magic=0x%lx nxt=0x%lx prv=0x%lx\n",
|
||
site, reason, uint64(block), block.size, int(block.state), int(block.order), order,
|
||
uint64(uint32(block.magic)), uint64(block.next), uint64(block.prev))
|
||
serial.puts(&_rb)
|
||
|
||
# 合并内存块
|
||
static def merge_blocks(self, block: memory_block*) -> memory_block*:
|
||
order: int = block.order
|
||
while order < MAX_ORDER:
|
||
block_size: uint64 = get_block_size(order)
|
||
block_addr: uint64 = uint64(block) - uint64(self.buddy.start_addr)
|
||
buddy_addr: uint64 = block_addr ^ block_size
|
||
# 计算伙伴块地址
|
||
buddy: memory_block* = memory_block*(uint64(self.buddy.start_addr) + buddy_addr)
|
||
# 检查伙伴块是否存在且空闲
|
||
if buddy >= memory_block*(self.buddy.end_addr):
|
||
break # 伙伴块超出内存范围
|
||
if int(buddy.state) != BLOCK_FREE or int(buddy.order) != order:
|
||
break # 伙伴块不空闲或阶数不匹配
|
||
# 加固:摘链前先校验伙伴块头(magic / size-order 一致 / prev-next 在堆内且对齐)。
|
||
# 损坏时只报不改 —— 跳过本次合并、空闲链保持原样,不再让坏值流到
|
||
# `buddy.prev.next = buddy.next`(现场那条写非规范地址 → #GP err=0,rip=0x119e50)。
|
||
_rc: int = self._check_block(buddy, BLOCK_FREE)
|
||
if _rc != 0:
|
||
self._report_bad_block(3, _rc, buddy, order)
|
||
break
|
||
# 从空闲链表中移除伙伴块
|
||
if buddy.prev: buddy.prev.next = buddy.next
|
||
else: self.buddy.free_lists[order] = buddy.next
|
||
if buddy.next:
|
||
buddy.next.prev = buddy.prev
|
||
# 合并块
|
||
merged_block: memory_block* = block if block < buddy else buddy
|
||
merged_block.size = get_block_size(order + 1)
|
||
merged_block.order = uint8(order + 1)
|
||
block = merged_block
|
||
order += 1
|
||
return block
|
||
|
||
# 分配内存块
|
||
static def allocate_block(self, size: uint64) -> memory_block*:
|
||
# 计算所需阶数
|
||
order: int = get_order(size)
|
||
if order > MAX_ORDER: return None # 所需块大小超过最大限制
|
||
# 查找合适的空闲块
|
||
found_order: int = -1
|
||
for i in range(order, MAX_ORDER + 1):
|
||
if self.buddy.free_lists[i]:
|
||
found_order = i
|
||
break
|
||
if found_order == -1:
|
||
# [DIAG] 池面耗尽/链表坏:请求大小、所需阶数与整体水位(一眼分辨"用光"还是"碎片化")
|
||
_adb: [176]char
|
||
viperlib.snprintf(&_adb, 176, "[mm] allocate_block(size=%lu) need order=%d -> NO FREE BLOCK (used=0x%lx free=0x%lx total=0x%lx)\n",
|
||
size, order, self.buddy.used_memory, self.buddy.free_memory, self.buddy.total_memory)
|
||
serial.puts(&_adb)
|
||
return None
|
||
# 取出空闲块
|
||
block: memory_block* = self.buddy.free_lists[found_order]
|
||
# 加固:摘链前校验块头(magic + size/order + 双链范围)。坏则放弃本次分配、
|
||
# 保持空闲链原样 —— 否则紧接着的 `block.next.prev = None` 就会踩坏值。
|
||
_arc: int = self._check_block(block, BLOCK_FREE)
|
||
if _arc != 0:
|
||
self._report_bad_block(1, _arc, block, found_order)
|
||
return None
|
||
# 从空闲链表中移除块
|
||
if block.next: block.next.prev = None
|
||
self.buddy.free_lists[found_order] = block.next
|
||
# 如果块大小大于所需大小,分割块
|
||
if found_order > order:
|
||
block = self.split_block(block, order)
|
||
# 标记块为已使用
|
||
block.state = BLOCK_USED
|
||
# 更新内存统计
|
||
self.buddy.used_memory += block.size
|
||
self.buddy.free_memory -= block.size
|
||
return block
|
||
|
||
# 释放内存块
|
||
def free_block(self, block: memory_block*):
|
||
# 加固:先校验传入块头(magic + size/order 一致 + state 必须是 USED + 双链在堆内且对齐)。
|
||
# 坏块**拒绝入链并报出**:重复释放 / 野指针 / 头被越界写,都会在这里被早抓到,
|
||
# 而不是等到某次 merge 写非规范地址才 #GP(err=0)(现场 rip=0x119e50)。
|
||
_frc: int = self._check_block(block, BLOCK_USED)
|
||
if _frc != 0:
|
||
self._report_bad_block(2, _frc, block, int(block.order))
|
||
return
|
||
# 标记块为空闲
|
||
block.state = BLOCK_FREE
|
||
# 更新内存统计
|
||
self.buddy.used_memory -= block.size
|
||
self.buddy.free_memory += block.size
|
||
# 尝试合并块
|
||
block = self.merge_blocks(block)
|
||
# 将块添加到对应阶数的空闲链表
|
||
order: int = block.order
|
||
if order <= MAX_ORDER:
|
||
block.next = self.buddy.free_lists[order]
|
||
block.prev = None
|
||
if self.buddy.free_lists[order]:
|
||
self.buddy.free_lists[order].prev = block
|
||
self.buddy.free_lists[order] = block
|
||
BuddySystemObject: _BuddySystemObject
|
||
|
||
|
||
def _slab_obj_size(size_class: int) -> uint16:
|
||
if size_class == 0: return 32
|
||
if size_class == 1: return 64
|
||
if size_class == 2: return 128
|
||
if size_class == 3: return 256
|
||
if size_class == 4: return 512
|
||
return 1024
|
||
|
||
def _slab_size_class(size: uint64) -> int:
|
||
if size <= 32: return 0
|
||
if size <= 64: return 1
|
||
if size <= 128: return 2
|
||
if size <= 256: return 3
|
||
if size <= 512: return 4
|
||
if size <= 1024: return 5
|
||
return -1
|
||
|
||
def _slab_read_ptr(addr: rawptr) -> rawptr:
|
||
result: rawptr
|
||
__asm__(f"""mov rax, [{asminp(addr, ASM_DESCR.REG_ANY)}]
|
||
mov {asmout(result, ASM_DESCR.OUTPUT_REG)}, rax""",
|
||
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX])
|
||
return result
|
||
|
||
def _slab_write_ptr(addr: rawptr, val: rawptr):
|
||
__asm__(f"""mov rax, {asminp(val, ASM_DESCR.REG_ANY)}
|
||
mov [{asminp(addr, ASM_DESCR.REG_ANY)}], rax""",
|
||
op=[ASM_DESCR.CLOBBER_RAX, ASM_DESCR.CLOBBER_MEMORY])
|
||
|
||
# 对象在页内的序号(-1 = 不在对象边界上)
|
||
def _slab_bit_index(page: slab_page*, ptr: rawptr) -> int:
|
||
if page.obj_size == 0: return -1
|
||
hdr_off: uint64 = memory_block.__sizeof__()
|
||
block: uint64 = uint64(page) - hdr_off
|
||
data: uint64 = block + uint64(page.data_offset)
|
||
if uint64(ptr) < data: return -1
|
||
return int((uint64(ptr) - data) / uint64(page.obj_size))
|
||
|
||
def _slab_bit_set(page: slab_page*, idx: int):
|
||
if idx < 0 or idx >= SLAB_BITMAP_BYTES * 8: return
|
||
byte_idx: int = idx / 8
|
||
page.alloc_bits[byte_idx] = uint8(page.alloc_bits[byte_idx] | uint8(1 << (idx & 7)))
|
||
|
||
def _slab_bit_clear(page: slab_page*, idx: int):
|
||
if idx < 0 or idx >= SLAB_BITMAP_BYTES * 8: return
|
||
byte_idx: int = idx / 8
|
||
page.alloc_bits[byte_idx] = uint8(page.alloc_bits[byte_idx] & ~uint8(1 << (idx & 7)))
|
||
|
||
def _slab_bit_test(page: slab_page*, idx: int) -> int:
|
||
if idx < 0 or idx >= SLAB_BITMAP_BYTES * 8: return 0
|
||
byte_idx: int = idx / 8
|
||
mask: uint8 = uint8(1 << (idx & 7))
|
||
if (page.alloc_bits[byte_idx] & mask) != 0: return 1
|
||
return 0
|
||
|
||
# slab 页归属校验:页头必须自洽(block_ptr 指回本页所在块),且 ptr 必须落在页内
|
||
# [data_offset, data_offset + total*obj) 范围并按其对象大小对齐。只查 magic 会把
|
||
# 大块分配头部残留的“看起来像页头”的数据误判为 slab 页,free 会写进页外并把仍在
|
||
# 使用的块还给 buddy(现场:页头 magic 被清零后 free(err) 落到 buddy 路径)。
|
||
def _slab_page_owns(page: slab_page*, ptr: rawptr, hdr_off: uint64) -> int:
|
||
if page.magic != SLAB_MAGIC: return 0
|
||
obj: uint64 = uint64(page.obj_size)
|
||
if obj == 0: return 0
|
||
block: uint64 = uint64(page) - hdr_off
|
||
if uint64(page.block_ptr) != block: return 0
|
||
if uint64(page.data_offset) < hdr_off: return 0
|
||
data: uint64 = block + uint64(page.data_offset)
|
||
if uint64(ptr) < data: return 0
|
||
off: uint64 = uint64(ptr) - data
|
||
if (off % obj) != 0: return 0
|
||
if (off / obj) >= uint64(page.total_objs): return 0
|
||
return 1
|
||
|
||
def _slab_find_page(ptr: rawptr) -> slab_page*:
|
||
if ptr is None: return None
|
||
hdr_off: uint64 = memory_block.__sizeof__()
|
||
page_addr: uint64 = uint64(ptr) & ~uint64(0xFFF)
|
||
page: slab_page* = slab_page*(page_addr + hdr_off)
|
||
if _slab_page_owns(page, ptr, hdr_off) != 0:
|
||
os: uint16 = page.obj_size
|
||
if os == 32 or os == 64 or os == 128 or os == 256 or os == 512 or os == 1024:
|
||
return page
|
||
page_addr2: uint64 = page_addr - 4096
|
||
page2: slab_page* = slab_page*(page_addr2 + hdr_off)
|
||
if _slab_page_owns(page2, ptr, hdr_off) != 0:
|
||
os2: uint16 = page2.obj_size
|
||
if os2 == 32 or os2 == 64 or os2 == 128 or os2 == 256 or os2 == 512 or os2 == 1024:
|
||
return page2
|
||
return None
|
||
|
||
|
||
class _SlabAllocator:
|
||
caches: [SLAB_NUM_SIZES]slab_page*
|
||
|
||
def __init__(self):
|
||
for i in range(SLAB_NUM_SIZES):
|
||
self.caches[i] = None
|
||
|
||
def _create_page(self, size_class: int) -> slab_page*:
|
||
obj_size: uint16 = _slab_obj_size(size_class)
|
||
aligned_size: uint16 = uint16((obj_size + 15) & ~15)
|
||
block: memory_block* = BuddySystemObject.allocate_block(4096 + memory_block.__sizeof__())
|
||
if not block:
|
||
# [DIAG] slab 建页拿不到伙伴块
|
||
_cpb: [96]char
|
||
viperlib.snprintf(&_cpb, 96, "[mm] slab _create_page(sc=%d) allocate_block(4128) -> NULL\n", size_class)
|
||
serial.puts(&_cpb)
|
||
return None
|
||
hdr_off: uint64 = memory_block.__sizeof__()
|
||
page: slab_page* = slab_page*(uint64(block) + hdr_off)
|
||
page.magic = SLAB_MAGIC
|
||
page.obj_size = aligned_size
|
||
d_off: uint16 = uint16((hdr_off + slab_page.__sizeof__() + 15) & ~15)
|
||
page.data_offset = d_off
|
||
total: uint16 = uint16((block.size - d_off) / aligned_size)
|
||
page.total_objs = total
|
||
page.free_count = total
|
||
page.block_ptr = block
|
||
page.next = None
|
||
page.prev = None
|
||
for bi in range(SLAB_BITMAP_BYTES):
|
||
page.alloc_bits[bi] = 0
|
||
data_start: uint64 = uint64(block) + d_off
|
||
# 加固:下面那个建链循环是分配器里唯一"成片写指针"的地方,一旦 total 因下溢/截断而
|
||
# 离谱,就会把 nxt 指针写到自己页外、覆盖相邻块头。先把 total 与地址范围校验成自洽。
|
||
if block.size < uint64(d_off) + uint64(aligned_size):
|
||
serial.puts("[mm] slab _create_page: block too small for page header - refused\n")
|
||
page.magic = 0 # 归还前抹掉页头,避免后续把它误认成有效 slab 页
|
||
BuddySystemObject.free_block(block)
|
||
return None
|
||
if uint64(total) * uint64(aligned_size) + uint64(d_off) > block.size:
|
||
serial.puts("[mm] slab _create_page: total*obj_size+d_off > block.size (would write outside page) - refused\n")
|
||
page.magic = 0 # 同上
|
||
BuddySystemObject.free_block(block)
|
||
return None
|
||
page.free_head = rawptr(data_start)
|
||
for i in range(1, total):
|
||
cur: rawptr = rawptr(data_start + uint64(i - 1) * aligned_size)
|
||
nxt: rawptr = rawptr(data_start + uint64(i) * aligned_size)
|
||
_slab_write_ptr(cur, nxt)
|
||
last: rawptr = rawptr(data_start + uint64(total - 1) * aligned_size)
|
||
_slab_write_ptr(last, None)
|
||
return page
|
||
|
||
def alloc(self, size_class: int) -> rawptr:
|
||
page: slab_page* = self.caches[size_class]
|
||
while page:
|
||
if page.free_count > 0: break
|
||
page = page.next
|
||
if not page:
|
||
page = self._create_page(size_class)
|
||
if not page: return None
|
||
page.next = self.caches[size_class]
|
||
if self.caches[size_class]:
|
||
self.caches[size_class].prev = page
|
||
self.caches[size_class] = page
|
||
obj: rawptr = page.free_head
|
||
_slab_bit_set(page, _slab_bit_index(page, obj))
|
||
page.free_head = _slab_read_ptr(obj)
|
||
page.free_count -= 1
|
||
return obj
|
||
|
||
def free_obj(self, ptr: rawptr, page: slab_page*):
|
||
# 只有“已分配”的对象能被 free:否则 free_count 会提前到 total_objs,页被拆回
|
||
# buddy 而页内仍有活对象(后续 free 只看到 magic=0 的旧页头)。
|
||
bi: int = _slab_bit_index(page, ptr)
|
||
if _slab_bit_test(page, bi) == 0:
|
||
_fd: [112]char
|
||
viperlib.snprintf(&_fd, 112, "[mm] slab free_obj: page=0x%lx ptr=0x%lx idx=%d not allocated - refused\n",
|
||
uint64(page), uint64(ptr), bi)
|
||
serial.puts(&_fd)
|
||
return
|
||
# 已全空的页也不能再收对象(计数/指针已不可信)
|
||
if page.free_count >= page.total_objs:
|
||
_fo: [96]char
|
||
viperlib.snprintf(&_fo, 96, "[mm] slab free_obj: page=0x%lx free=%d total=%d - refused\n",
|
||
uint64(page), int(page.free_count), int(page.total_objs))
|
||
serial.puts(&_fo)
|
||
return
|
||
_slab_bit_clear(page, bi)
|
||
_slab_write_ptr(ptr, page.free_head)
|
||
page.free_head = ptr
|
||
page.free_count += 1
|
||
if page.free_count == page.total_objs:
|
||
if page.prev:
|
||
page.prev.next = page.next
|
||
else:
|
||
sc: int = _slab_size_class(page.obj_size)
|
||
if sc >= 0:
|
||
self.caches[sc] = page.next
|
||
if page.next:
|
||
page.next.prev = page.prev
|
||
page.magic = 0
|
||
BuddySystemObject.free_block(page.block_ptr)
|
||
|
||
SlabAllocator: _SlabAllocator
|
||
|
||
|
||
# 内存分配函数
|
||
export def malloc(size: uint64) -> rawptr:
|
||
if size == 0: return None
|
||
if size <= SLAB_THRESHOLD:
|
||
sc: int = _slab_size_class(size)
|
||
if sc >= 0:
|
||
result: rawptr = SlabAllocator.alloc(sc)
|
||
if result is None:
|
||
# [DIAG] slab 分配失败(缓存链与建页都失败)
|
||
_mb: [96]char
|
||
viperlib.snprintf(&_mb, 96, "[mm] malloc(%lu) slab sc=%d -> NULL\n", size, sc)
|
||
serial.puts(&_mb)
|
||
return result
|
||
hdr: uint64 = memory_block.__sizeof__()
|
||
if size >= 4096:
|
||
total: uint64 = size + 4096
|
||
total = align_4k(total)
|
||
order: int = get_order(total)
|
||
if order > MAX_ORDER: return None
|
||
block: memory_block* = BuddySystemObject.allocate_block(total)
|
||
if not block: return None
|
||
user_ptr: uint64 = (uint64(block) + hdr + 4095) & ~uint64(4095)
|
||
real_hdr: uint64 = user_ptr - 8
|
||
__asm__(f"""mov rax, {asminp(block, ASM_DESCR.REG_ANY)}
|
||
mov [{asminp(rawptr(real_hdr), ASM_DESCR.REG_ANY)}], rax""",
|
||
op=[ASM_DESCR.CLOBBER_RAX, ASM_DESCR.CLOBBER_MEMORY])
|
||
return rawptr(user_ptr)
|
||
total2: uint64 = size + hdr
|
||
total2 = align_4k(total2)
|
||
order2: int = get_order(total2)
|
||
if order2 > MAX_ORDER: return None
|
||
block2: memory_block* = BuddySystemObject.allocate_block(total2)
|
||
if not block2: return None
|
||
return rawptr(uint64(block2) + hdr)
|
||
|
||
|
||
export def malloc_direct(size: uint64) -> rawptr:
|
||
if size == 0: return None
|
||
hdr: uint64 = memory_block.__sizeof__()
|
||
total: uint64 = size + hdr
|
||
total = align_4k(total)
|
||
order: int = get_order(total)
|
||
if order > MAX_ORDER: return None
|
||
block: memory_block* = BuddySystemObject.allocate_block(total)
|
||
if not block: return None
|
||
return rawptr(uint64(block) + hdr)
|
||
|
||
|
||
export def free(ptr: rawptr):
|
||
if ptr is None: return
|
||
sp: slab_page* = _slab_find_page(ptr)
|
||
if sp:
|
||
SlabAllocator.free_obj(ptr, sp)
|
||
return
|
||
# 加固:堆范围校验 —— 野指针(或已跑飞的指针)在动任何块头之前就报出来,
|
||
# 不然后面 `ptr-32` / `ptr-8` 取到的"块头"是随机内存,会把空闲链写坏。
|
||
_hs: uint64 = uint64(BuddySystemObject.buddy.start_addr)
|
||
_he: uint64 = uint64(BuddySystemObject.buddy.end_addr)
|
||
_hp: uint64 = uint64(ptr)
|
||
if _hp < _hs or _hp >= _he:
|
||
_fb2: [160]char
|
||
viperlib.snprintf(&_fb2, 160, "[mm] free(0x%lx) outside heap [0x%lx,0x%lx) - ignored\n", _hp, _hs, _he)
|
||
serial.puts(&_fb2)
|
||
return
|
||
if (uint64(ptr) & 0xFFF) == 0:
|
||
real_hdr: uint64 = uint64(ptr) - 8
|
||
block: memory_block*
|
||
__asm__(f"""mov rax, [{asminp(rawptr(real_hdr), ASM_DESCR.REG_ANY)}]
|
||
mov {asmout(block, ASM_DESCR.OUTPUT_REG)}, rax""",
|
||
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX])
|
||
BuddySystemObject.free_block(block)
|
||
return
|
||
block2: memory_block* = rawptr(uint64(ptr) - memory_block.__sizeof__())
|
||
BuddySystemObject.free_block(block2)
|
||
|
||
export def calloc(nmemb: uint64, size: uint64) -> rawptr:
|
||
total_size: uint64 = nmemb * size
|
||
ptr: rawptr = malloc(total_size)
|
||
if ptr:
|
||
string.memset(ptr, 0, total_size)
|
||
return ptr
|
||
|
||
export def realloc(ptr: rawptr, size: uint64) -> rawptr:
|
||
if ptr is None: return malloc(size)
|
||
if size == 0:
|
||
free(ptr)
|
||
return None
|
||
new_ptr: rawptr = malloc(size)
|
||
if not new_ptr: return None
|
||
sp: slab_page* = _slab_find_page(ptr)
|
||
if sp:
|
||
old_size: uint64 = sp.obj_size
|
||
elif (uint64(ptr) & 0xFFF) == 0:
|
||
real_hdr_r: uint64 = uint64(ptr) - 8
|
||
block_r: memory_block*
|
||
__asm__(f"""mov rax, [{asminp(rawptr(real_hdr_r), ASM_DESCR.REG_ANY)}]
|
||
mov {asmout(block_r, ASM_DESCR.OUTPUT_REG)}, rax""",
|
||
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX])
|
||
old_size = block_r.size - 4096
|
||
else:
|
||
block2: memory_block* = rawptr(uint64(ptr) - memory_block.__sizeof__())
|
||
old_size = block2.size - memory_block.__sizeof__()
|
||
copy_size: uint64 = old_size if old_size < size else size
|
||
string.memcpy(new_ptr, ptr, copy_size)
|
||
free(ptr)
|
||
return new_ptr
|
||
|
||
|
||
# 回收内存区域链表
|
||
def free_memory_regions():
|
||
global memory_regions
|
||
current: memory_region* = memory_regions
|
||
while current:
|
||
next_region: memory_region* = current.next
|
||
free(current)
|
||
current = next_region
|
||
memory_regions = None
|
||
|
||
# 初始化内存管理系统
|
||
def init(MemmapAddr: uint64, MemmapSize: uint64, MemmapDescSize: uint64, FbAddr: uint64 = 0, FbSize: uint64 = 0, KernelAddr: uint64 = 0, KernelEnd: uint64 = 0):
|
||
global BuddySystemObject
|
||
start_addr: rawptr = None
|
||
size: uint64 = 0
|
||
if MemmapAddr != 0:
|
||
if MemmapSize > 0:
|
||
if MemmapDescSize > 0:
|
||
num_entries: uint64 = MemmapSize / MemmapDescSize
|
||
max_memory: uint64 = 0
|
||
best_start: uint64 = 0
|
||
best_avail_size: uint64 = 0
|
||
best_avail_start: uint64 = 0
|
||
best_bs_size: uint64 = 0
|
||
best_bs_start: uint64 = 0
|
||
best_ld_size: uint64 = 0
|
||
best_ld_start: uint64 = 0
|
||
for i in range(num_entries):
|
||
entry: bootinfo.memory_map_entry* = bootinfo.memory_map_entry*(MemmapAddr + i * MemmapDescSize)
|
||
etype: uint64 = entry.type
|
||
start: uint64 = align_4k(entry.physical_start)
|
||
np: uint64 = entry.num_pages
|
||
end: uint64 = entry.physical_start + np * 4096
|
||
end = align_4k(end - 1)
|
||
region_size: uint64 = end - start
|
||
if FbAddr != 0 and FbSize != 0:
|
||
fb_end: uint64 = FbAddr + FbSize
|
||
if start < fb_end and end > FbAddr:
|
||
continue
|
||
# 排除内核镜像区([KernelAddr, KernelEnd),含 .bss):bootloader 只按
|
||
# 文件大小预留页,.bss 不占文件 → 其尾部留在 memmap 的 EfiConventionalMemory
|
||
# 里。若不排除,堆 arena 会用这段内存写 memory_block 头/用户数据,与内核
|
||
# 全局量(ticks/_perf_* 等)互相破坏,并进一步搞乱堆元数据。
|
||
# 内核镜像位于常规内存低端,故把与该区间重叠的 region 起点抬到镜像末端,
|
||
# 而不是整段丢弃(整段丢弃会连带弃掉镜像之上的一大片可用内存)。
|
||
if KernelEnd > KernelAddr:
|
||
if end <= KernelEnd:
|
||
continue
|
||
if start < KernelEnd:
|
||
# 抬高到页对齐(KernelEnd 未必 4KB 对齐):arena 起点必须页对齐,
|
||
# 否则伙伴系统的块地址全部错位。
|
||
start = align_4k(KernelEnd)
|
||
region_size = end - start
|
||
if end <= start:
|
||
continue
|
||
if etype == MEMORY_TYPE_AVAILABLE:
|
||
if region_size > best_avail_size:
|
||
best_avail_size = region_size
|
||
best_avail_start = start
|
||
if etype == MEMORY_TYPE_BOOT_SERVICES_DATA:
|
||
if region_size > best_bs_size:
|
||
best_bs_size = region_size
|
||
best_bs_start = start
|
||
if etype == MEMORY_TYPE_LOADER_DATA:
|
||
if region_size > best_ld_size:
|
||
best_ld_size = region_size
|
||
best_ld_start = start
|
||
min_blk: uint64 = MIN_BLOCK_SIZE
|
||
if best_avail_size >= min_blk:
|
||
best_start = best_avail_start
|
||
max_memory = best_avail_size
|
||
elif best_bs_size >= min_blk:
|
||
best_start = best_bs_start
|
||
max_memory = best_bs_size
|
||
elif best_ld_size >= min_blk:
|
||
best_start = best_ld_start
|
||
max_memory = best_ld_size
|
||
if best_start != 0:
|
||
if max_memory >= min_blk:
|
||
start_addr = rawptr(best_start)
|
||
size = max_memory
|
||
if start_addr is None:
|
||
_dsz: uint64 = detect_memory_size()
|
||
size = detect_memory_size()
|
||
size = align_4k(size)
|
||
# 起始地址 4MB(内核之后),避免超出物理 RAM
|
||
# 旧值 0x10000000 在 256MB QEMU 中超出物理 RAM 边界
|
||
start_addr = rawptr(align_4k(0x400000))
|
||
if size > 0x400000:
|
||
size = size - 0x400000
|
||
size = align_4k(size)
|
||
min_blk2: uint64 = MIN_BLOCK_SIZE
|
||
if size < min_blk2:
|
||
size = 33554432
|
||
BuddySystemObject.__init__(start_addr, size)
|
||
SlabAllocator.__init__()
|
||
|
||
|
||
# 虚拟内存映射结构
|
||
class vm_area:
|
||
VirtAddr: uint64
|
||
PhysAddr: uint64
|
||
size: uint64
|
||
flags: uint32
|
||
next: vm_area*
|
||
|
||
# 全局虚拟内存区域链表
|
||
vm_areas: vm_area* = None
|
||
|
||
# 博弈机制参数
|
||
define ALLOCATION_RETRIES = 5 # 分配重试次数
|
||
define FRAGMENTATION_THRESHOLD = 0.7 # 碎片率阈值
|
||
|
||
# 获取内存使用统计
|
||
def GetMemoryStats() -> tuple[uint64, uint64, uint64]:
|
||
return BuddySystemObject.buddy.total_memory, BuddySystemObject.buddy.used_memory, BuddySystemObject.buddy.free_memory
|
||
|
||
|
||
# 计算内存碎片率
|
||
def calculate_fragmentation() -> double:
|
||
# 计算空闲块的数量和总大小
|
||
free_blocks: int = 0
|
||
free_size: uint64 = 0
|
||
|
||
for i in range(MAX_ORDER + 1):
|
||
block: memory_block* = BuddySystemObject.buddy.free_lists[i]
|
||
while block:
|
||
free_blocks += 1
|
||
free_size += block.size
|
||
block = block.next
|
||
|
||
# 如果没有空闲内存,返回0
|
||
if free_size == 0:
|
||
return 0.0
|
||
|
||
# 计算碎片率
|
||
# 碎片率 = 1 - (最大空闲块大小 / 总空闲内存大小)
|
||
max_free_block: uint64 = 0
|
||
for i in range(MAX_ORDER, -1, -1):
|
||
if BuddySystemObject.buddy.free_lists[i]:
|
||
max_free_block = get_block_size(i)
|
||
break
|
||
|
||
if max_free_block == 0:
|
||
return 1.0
|
||
|
||
return 1.0 - (double(max_free_block) / double(free_size))
|
||
|
||
# 内存整理函数
|
||
static def compact_memory() -> int:
|
||
freed_pages: int = 0
|
||
for sc in range(SLAB_NUM_SIZES):
|
||
page: slab_page* = SlabAllocator.caches[sc]
|
||
while page:
|
||
next_page: slab_page* = page.next
|
||
if page.free_count == page.total_objs:
|
||
if page.prev:
|
||
page.prev.next = page.next
|
||
else:
|
||
SlabAllocator.caches[sc] = page.next
|
||
if page.next:
|
||
page.next.prev = page.prev
|
||
page.magic = 0
|
||
BuddySystemObject.free_block(page.block_ptr)
|
||
freed_pages += 1
|
||
page = next_page
|
||
return freed_pages
|
||
|
||
# 虚拟内存分配函数
|
||
def vm_alloc(size: uint64, flags: uint32) -> uint64:
|
||
global vm_areas
|
||
# 4K对齐
|
||
size = align_4k(size)
|
||
# 尝试分配物理内存,使用博弈机制
|
||
PhysAddr: rawptr = None
|
||
for retries in range(ALLOCATION_RETRIES):
|
||
PhysAddr = malloc(size)
|
||
if PhysAddr:
|
||
break
|
||
# 内存不足,尝试进行内存整理
|
||
if calculate_fragmentation() > FRAGMENTATION_THRESHOLD:
|
||
compact_memory()
|
||
if not PhysAddr: return 0
|
||
# 分配虚拟地址
|
||
# 简单的虚拟地址分配策略:从0x100000000开始
|
||
VirtAddr: uint64 = 0x100000000
|
||
current: vm_area* = vm_areas
|
||
# 寻找合适的虚拟地址空间
|
||
while current:
|
||
if VirtAddr + size <= current.VirtAddr: break
|
||
VirtAddr = current.VirtAddr + current.size
|
||
current = current.next
|
||
# 创建虚拟内存区域
|
||
area: vm_area* = malloc(vm_area.__sizeof__())
|
||
if area:
|
||
area.VirtAddr = VirtAddr
|
||
area.PhysAddr = uint64(PhysAddr)
|
||
area.size = size
|
||
area.flags = flags
|
||
area.next = vm_areas
|
||
vm_areas = area
|
||
# 映射虚拟地址到物理地址
|
||
# 计算需要映射的页数
|
||
pages: uint64 = size / 4096
|
||
for i in range(pages):
|
||
page_virt: uint64 = VirtAddr + (i * 4096)
|
||
page_phys: uint64 = uint64(PhysAddr) + (i * 4096)
|
||
# 调用paging模块的映射函数
|
||
paging.MapPage(page_virt, page_phys, flags)
|
||
return VirtAddr
|
||
|
||
# 虚拟内存释放函数
|
||
def vm_free(VirtAddr: uint64):
|
||
global vm_areas
|
||
# 查找虚拟内存区域
|
||
current: vm_area* = vm_areas
|
||
prev: vm_area* = None
|
||
while current:
|
||
if current.VirtAddr == VirtAddr:
|
||
# 取消虚拟地址映射
|
||
pages: uint64 = current.size / 4096
|
||
for i in range(pages):
|
||
page_virt: uint64 = current.VirtAddr + (i * 4096)
|
||
paging.UnMapPage(page_virt)
|
||
# 释放物理内存
|
||
free(rawptr(current.PhysAddr))
|
||
if prev:
|
||
prev.next = current.next
|
||
else:
|
||
vm_areas = current.next
|
||
free(current)
|
||
return
|
||
prev = current
|
||
current = current.next
|
||
|
||
# CMOS读取函数
|
||
def cmos_read(reg: uint8) -> uint8:
|
||
# in/out al 会改 EAX,必须 CLOBBER_RAX;显式 mov 到输出寄存器
|
||
#(与 asm.inb/inw 修复同模式,避免调用方 EAX 被静默破坏)。
|
||
__asm__(f"""mov eax, {asminp(uint32(reg), ASM_DESCR.REG_ANY)}
|
||
out 0x70, al""",
|
||
op=[ASM_DESCR.CLOBBER_RAX])
|
||
val32: uint32
|
||
__asm__(f"""in al, 0x71
|
||
movzx eax, al
|
||
mov {asmout(val32, ASM_DESCR.OUTPUT_REG)}, eax""",
|
||
out=[asmout(val32, ASM_DESCR.OUTPUT_REG)],
|
||
op=[ASM_DESCR.CLOBBER_RAX])
|
||
return uint8(val32)
|
||
|
||
# 内存大小测试函数
|
||
def detect_memory_size() -> uint64:
|
||
# 读取 1MB-16MB 扩展内存(KB 单位)
|
||
low: uint8 = cmos_read(0x15)
|
||
high: uint8 = cmos_read(0x16)
|
||
mem_kb: uint32 = (uint32(high) << 8) | low
|
||
# 读取 16MB 以上扩展内存(64KB 块为单位)
|
||
low2: uint8 = cmos_read(0x17)
|
||
high2: uint8 = cmos_read(0x18)
|
||
mem_above_16mb: uint32 = (uint32(high2) << 8) | low2
|
||
# 两个寄存器都为 0 时使用默认值
|
||
if mem_kb == 0 and mem_above_16mb == 0: return 0x10000000 # 256MB
|
||
# 计算总内存:1MB 基础 + 1MB-16MB + 16MB 以上
|
||
total_size: uint64 = 1048576 # 前 1MB
|
||
if mem_kb > 0:
|
||
total_size = total_size + uint64(mem_kb) * 1024
|
||
if mem_above_16mb > 0:
|
||
total_size = total_size + uint64(mem_above_16mb) * 65536
|
||
total_size = align_4k(total_size)
|
||
# 确保内存大小至少为32MB
|
||
if total_size < 33554432: # 32MB
|
||
total_size = 33554432
|
||
return total_size
|