内容由用户产出,本次仅代为提交、未做任何语义修改;含 static 修饰符前置迁移等(新增 static 前缀 16 行)。提交消息为中性描述——如需更精确的说明请 amend。
260 lines
8.8 KiB
Plaintext
260 lines
8.8 KiB
Plaintext
import mm.mm as mm
|
|
import paging.paging as paging
|
|
import drivers.serial.uart.serial as serial
|
|
import drivers.fs.fat32.fat32 as fat32
|
|
import drivers.fs.fat32.fat32_types as fat32_types
|
|
import drivers.core.cpu.cpu as cpu
|
|
import intr.gdt as gdt
|
|
import string
|
|
|
|
|
|
define PROC_READY = 0
|
|
define PROC_RUNNING = 1
|
|
define PROC_BLOCKED = 2
|
|
define PROC_DONE = 3
|
|
define MAX_PROCESSES = 8
|
|
define MAX_PROC_THREADS = 8
|
|
define PROC_MAX_FDS = 16
|
|
define PROC_MAX_DIRS = 8
|
|
define KERN_STACK_SIZE = 8192
|
|
define USER_STACK_SIZE = 65536
|
|
|
|
|
|
class Process:
|
|
pid: int
|
|
pml4_root: uint64
|
|
threads: [8]int
|
|
thread_count: int
|
|
state: int
|
|
entry: rawptr
|
|
elf_base: uint64
|
|
heap_start: uint64
|
|
heap_size: uint64
|
|
name: [32]char
|
|
fd_table: [PROC_MAX_FDS]rawptr
|
|
fd_used: [PROC_MAX_FDS]uint8
|
|
dir_table: [PROC_MAX_DIRS]rawptr
|
|
dir_used: [PROC_MAX_DIRS]uint8
|
|
dir_pool: [PROC_MAX_DIRS]fat32_types.dirobj
|
|
kern_stack: uint64
|
|
user_stack: uint64
|
|
|
|
def addThread(self, tid: int) -> int:
|
|
if self.thread_count >= MAX_PROC_THREADS: return -1
|
|
self.threads[self.thread_count] = tid
|
|
self.thread_count += 1
|
|
return 0
|
|
|
|
def exit(self):
|
|
self.state = PROC_DONE
|
|
for i in range(PROC_MAX_FDS):
|
|
if self.fd_used[i]:
|
|
fp: rawptr = self.fd_table[i]
|
|
if fp:
|
|
fat32.close(fp)
|
|
self.fd_used[i] = 0
|
|
self.fd_table[i] = None
|
|
for i in range(PROC_MAX_DIRS):
|
|
if self.dir_used[i]:
|
|
dp: rawptr = self.dir_table[i]
|
|
if dp:
|
|
fat32.closedir(dp)
|
|
self.dir_used[i] = 0
|
|
self.dir_table[i] = None
|
|
m: ProcessManager* = _proc_mgr_ptr
|
|
if m.current_pid == self.pid:
|
|
m.current_pid = 0
|
|
m.processes[0].state = PROC_RUNNING
|
|
if self.pml4_root != 0:
|
|
kernel_cr3: uint64 = m.processes[0].pml4_root
|
|
if kernel_cr3 != 0:
|
|
__asm__(f"mov cr3, {asminp(kernel_cr3, ASM_DESCR.REG_ANY)}",
|
|
op=[ASM_DESCR.CLOBBER_MEMORY])
|
|
paging.set_active_pml4(kernel_cr3)
|
|
|
|
@property
|
|
def is_running(self) -> bool: return self.state == PROC_RUNNING
|
|
@property
|
|
def is_done(self) -> bool: return self.state == PROC_DONE
|
|
|
|
|
|
class ProcessManager:
|
|
processes: [MAX_PROCESSES]Process
|
|
current_pid: int
|
|
count: int
|
|
|
|
def __init__(self):
|
|
global _proc_mgr_ptr
|
|
_proc_mgr_ptr = &_proc_mgr
|
|
m: ProcessManager* = _proc_mgr_ptr
|
|
m.current_pid = 0
|
|
m.count = 1
|
|
p: Process* = &m.processes[0]
|
|
p.pid = 0
|
|
p.state = PROC_RUNNING
|
|
p.thread_count = 0
|
|
p.pml4_root = 0
|
|
p.entry = None
|
|
p.elf_base = 0
|
|
p.heap_start = 0
|
|
p.heap_size = 0
|
|
name_str: str = "kernel"
|
|
i: int = 0
|
|
for ch in name_str:
|
|
if i >= 31: break
|
|
p.name[i] = ch
|
|
i += 1
|
|
p.name[i] = 0
|
|
j: int
|
|
for j in range(MAX_PROC_THREADS):
|
|
p.threads[j] = -1
|
|
for j in range(PROC_MAX_FDS):
|
|
p.fd_used[j] = 0
|
|
p.fd_table[j] = None
|
|
for j in range(PROC_MAX_DIRS):
|
|
p.dir_used[j] = 0
|
|
p.dir_table[j] = None
|
|
__asm__(f"""mov {asmout(p.pml4_root, ASM_DESCR.OUTPUT_REG)}, cr3""",
|
|
op=[ASM_DESCR.CLOBBER_MEMORY])
|
|
|
|
@staticmethod
|
|
def _switch(pid: int) -> int:
|
|
global _saved_cr3
|
|
m: ProcessManager* = _proc_mgr_ptr
|
|
if pid < 0 or pid >= m.count: return -1
|
|
p: Process* = &m.processes[pid]
|
|
if p.state == PROC_DONE: return -1
|
|
old_pid: int = m.current_pid
|
|
if old_pid == pid: return 0
|
|
if old_pid >= 0 and old_pid < m.count:
|
|
m.processes[old_pid].state = PROC_READY
|
|
p.state = PROC_RUNNING
|
|
m.current_pid = pid
|
|
__asm__(f"""mov {asmout(_saved_cr3, ASM_DESCR.OUTPUT_REG)}, cr3
|
|
mov rax, {asminp(p.pml4_root, ASM_DESCR.REG_ANY)}
|
|
mov cr3, rax""",
|
|
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX])
|
|
return 0
|
|
|
|
@staticmethod
|
|
def current() -> Process*:
|
|
m: ProcessManager* = _proc_mgr_ptr
|
|
return &m.processes[m.current_pid]
|
|
|
|
@staticmethod
|
|
def get_process(pid: int) -> Process*:
|
|
m: ProcessManager* = _proc_mgr_ptr
|
|
if pid < 0 or pid >= m.count: return None
|
|
return &m.processes[pid]
|
|
|
|
@staticmethod
|
|
def create_process(name: str, entry: rawptr) -> Process*:
|
|
m: ProcessManager* = _proc_mgr_ptr
|
|
if m.count >= MAX_PROCESSES: return None
|
|
pid: int = m.count
|
|
m.count += 1
|
|
p: Process* = &m.processes[pid]
|
|
p.pid = pid
|
|
p.state = PROC_READY
|
|
p.entry = entry
|
|
p.elf_base = 0
|
|
p.thread_count = 0
|
|
p.heap_start = 0
|
|
p.heap_size = 0
|
|
string.memset(&p.name, 0, 32)
|
|
i: int = 0
|
|
for ch in name:
|
|
if i >= 31: break
|
|
p.name[i] = ch
|
|
i += 1
|
|
p.name[i] = 0
|
|
for j in range(MAX_PROC_THREADS):
|
|
p.threads[j] = -1
|
|
for j in range(PROC_MAX_FDS):
|
|
p.fd_used[j] = 0
|
|
p.fd_table[j] = None
|
|
for j in range(PROC_MAX_DIRS):
|
|
p.dir_used[j] = 0
|
|
p.dir_table[j] = None
|
|
ks: rawptr = mm.malloc(KERN_STACK_SIZE)
|
|
if ks is None:
|
|
m.count = m.count - 1
|
|
return None
|
|
ks_size: uint64 = KERN_STACK_SIZE
|
|
p.kern_stack = uint64(ks) + ks_size
|
|
us: rawptr = mm.malloc(USER_STACK_SIZE)
|
|
if us is None:
|
|
serial.puts("[process] user stack alloc failed\n")
|
|
mm.free(ks)
|
|
m.count = m.count - 1
|
|
return None
|
|
us_size: uint64 = USER_STACK_SIZE
|
|
p.user_stack = uint64(us) + us_size
|
|
__asm__(f"""mov {asmout(p.pml4_root, ASM_DESCR.OUTPUT_REG)}, cr3""",
|
|
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX])
|
|
return p
|
|
|
|
|
|
_proc_mgr: ProcessManager = ProcessManager()
|
|
_proc_mgr_ptr: ProcessManager* = &_proc_mgr
|
|
export _saved_cr3: uint64
|
|
|
|
def current() -> int:
|
|
m: ProcessManager* = _proc_mgr_ptr
|
|
return m.current_pid
|
|
|
|
def switch_pid(new_pid: int):
|
|
m: ProcessManager* = _proc_mgr_ptr
|
|
if m.current_pid == new_pid: return
|
|
if m.current_pid >= 0 and m.current_pid < m.count:
|
|
old_p: Process* = &m.processes[m.current_pid]
|
|
if old_p.state != PROC_DONE:
|
|
old_p.state = PROC_READY
|
|
if new_pid >= 0 and new_pid < m.count:
|
|
new_p: Process* = &m.processes[new_pid]
|
|
new_p.state = PROC_RUNNING
|
|
new_cr3: uint64 = new_p.pml4_root
|
|
if new_cr3 != 0:
|
|
__asm__(f"mov cr3, {asminp(new_cr3, ASM_DESCR.REG_ANY)}",
|
|
op=[ASM_DESCR.CLOBBER_MEMORY])
|
|
paging.set_active_pml4(new_cr3)
|
|
# Update TSS RSP0 and GS kernel_rsp0 for the new process
|
|
# This is needed so interrupts from Ring 3 use the correct kernel stack
|
|
if new_p.kern_stack != 0:
|
|
gdt.set_tss_rsp0(new_p.kern_stack)
|
|
cpu.set_kernel_rsp0(new_p.kern_stack)
|
|
m.current_pid = new_pid
|
|
|
|
def get_pid(tid: int) -> int:
|
|
m: ProcessManager* = _proc_mgr_ptr
|
|
for i in range(m.count):
|
|
p: Process* = &m.processes[i]
|
|
for j in range(p.thread_count):
|
|
if p.threads[j] == tid:
|
|
return p.pid
|
|
return -1
|
|
|
|
def drop_to_user_mode(entry: rawptr, user_rsp: uint64, kern_rsp: uint64):
|
|
# NOTE: Do NOT swapgs here. This OS uses reverse GS convention:
|
|
# GS_BASE = 0 (kernel & user), KERNEL_GS_BASE = &_per_cpu
|
|
# syscall/ISR entry does swapgs to get GS_BASE = &_per_cpu
|
|
# syscall/ISR return does swapgs to restore GS_BASE = 0
|
|
# GS state is restored in isrCommonhandler before _yield() if needed.
|
|
__asm__(f"""cli
|
|
mov rsp, {asminp(kern_rsp, ASM_DESCR.REG_ANY)}
|
|
mov rdx, {asminp(user_rsp, ASM_DESCR.REG_ANY)}
|
|
mov rcx, {asminp(entry, ASM_DESCR.REG_ANY)}
|
|
mov r11, 0x202
|
|
push 0x23
|
|
push rdx
|
|
push r11
|
|
push 0x1B
|
|
push rcx
|
|
iretq""",
|
|
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX,
|
|
ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RDX,
|
|
ASM_DESCR.CLOBBER_R11, ASM_DESCR.CLOBBER_RSI,
|
|
ASM_DESCR.CLOBBER_RDI, ASM_DESCR.CLOBBER_R8,
|
|
ASM_DESCR.CLOBBER_R9, ASM_DESCR.CLOBBER_R10,
|
|
ASM_DESCR.CLOBBER_RBP])
|