内容由用户产出,本次仅代为提交、未做任何语义修改;含 static 修饰符前置迁移等(新增 static 前缀 16 行)。提交消息为中性描述——如需更精确的说明请 amend。
290 lines
9.0 KiB
Plaintext
290 lines
9.0 KiB
Plaintext
import drivers.serial.uart.serial as serial
|
|
import sched.process as proc
|
|
import mm.mm as mm
|
|
import viperlib
|
|
import asm
|
|
|
|
define THREAD_READY = 0
|
|
define THREAD_RUNNING = 1
|
|
define THREAD_BLOCKED = 2
|
|
define THREAD_DONE = 3
|
|
define MAX_THREADS = 16
|
|
define THREAD_STACK_SIZE = 65536
|
|
define SCHED_QUANTUM = 10
|
|
|
|
_yield_cnt: uint32 = 0
|
|
_yield_lock: int = 0
|
|
|
|
|
|
class Thread:
|
|
rsp: uint64
|
|
stack: rawptr
|
|
stack_size: uint64
|
|
func: rawptr
|
|
arg: rawptr
|
|
state: int
|
|
tid: int
|
|
pid: int
|
|
|
|
def block(self):
|
|
self.state = THREAD_BLOCKED
|
|
Scheduler._yield()
|
|
|
|
def unblock(self):
|
|
if self.state == THREAD_BLOCKED:
|
|
self.state = THREAD_READY
|
|
|
|
@property
|
|
def is_running(self) -> bool: return self.state == THREAD_RUNNING
|
|
@property
|
|
def is_blocked(self) -> bool: return self.state == THREAD_BLOCKED
|
|
@property
|
|
def is_done(self) -> bool: return self.state == THREAD_DONE
|
|
|
|
|
|
class Scheduler:
|
|
threads: [MAX_THREADS]Thread
|
|
current_tid: int
|
|
thread_count: int
|
|
enabled: int
|
|
needs_reschedule: int
|
|
tick_count: int
|
|
|
|
def __init__(self):
|
|
global _sched_ptr
|
|
_sched_ptr = &_sched_storage
|
|
s: Scheduler* = _sched_ptr
|
|
s.current_tid = 0
|
|
s.thread_count = 1
|
|
s.enabled = 0
|
|
s.needs_reschedule = 0
|
|
s.tick_count = 0
|
|
for i in range(MAX_THREADS):
|
|
s.threads[i].rsp = 0
|
|
s.threads[i].stack = None
|
|
s.threads[i].stack_size = 0
|
|
s.threads[i].func = None
|
|
s.threads[i].arg = None
|
|
s.threads[i].state = THREAD_DONE
|
|
s.threads[i].tid = i
|
|
s.threads[i].pid = 0
|
|
s.enabled = 1
|
|
s.threads[0].state = THREAD_RUNNING
|
|
s.threads[0].tid = 0
|
|
main_rsp_ptr: rawptr = &s.threads[0].rsp
|
|
__asm__(f"""mov qword ptr [{asminp(main_rsp_ptr, ASM_DESCR.REG_ANY)}], rsp""",
|
|
op=[ASM_DESCR.CLOBBER_MEMORY])
|
|
|
|
@staticmethod
|
|
def _yield():
|
|
global _switch_old_rsp, _switch_new_rsp, _yield_cnt, _yield_lock
|
|
_if_saved: int = 0
|
|
__asm__(f"""pushfq
|
|
pop rax
|
|
shr rax, 9
|
|
and eax, 1
|
|
mov {asmout(_if_saved, ASM_DESCR.OUTPUT_REG)}, eax""",
|
|
op=[ASM_DESCR.CLOBBER_RAX])
|
|
asm.cli()
|
|
if _yield_lock:
|
|
if _if_saved:
|
|
asm.sti()
|
|
return
|
|
_yield_lock = 1
|
|
s: Scheduler* = _sched_ptr
|
|
s.needs_reschedule = 0
|
|
s.tick_count = 0
|
|
old_tid: int = s.current_tid
|
|
next_tid: int = old_tid + 1
|
|
if next_tid >= s.thread_count:
|
|
next_tid = 0
|
|
found: int = 0
|
|
count: int = 0
|
|
while count < s.thread_count:
|
|
st: int = s.threads[next_tid].state
|
|
if st == THREAD_READY or st == THREAD_RUNNING:
|
|
found = 1
|
|
break
|
|
next_tid += 1
|
|
if next_tid >= s.thread_count:
|
|
next_tid = 0
|
|
count += 1
|
|
if found == 0:
|
|
_yield_lock = 0
|
|
if _if_saved:
|
|
asm.sti()
|
|
return
|
|
if next_tid == old_tid:
|
|
_yield_lock = 0
|
|
if _if_saved:
|
|
asm.sti()
|
|
return
|
|
if s.threads[old_tid].state == THREAD_RUNNING:
|
|
s.threads[old_tid].state = THREAD_READY
|
|
s.threads[next_tid].state = THREAD_RUNNING
|
|
_switch_old_rsp = &s.threads[old_tid].rsp
|
|
_switch_new_rsp = &s.threads[next_tid].rsp
|
|
s.current_tid = next_tid
|
|
next_pid: int = s.threads[next_tid].pid
|
|
_yield_cnt += 1
|
|
proc.switch_pid(next_pid)
|
|
_yield_lock = 0
|
|
_do_switch()
|
|
if _if_saved:
|
|
asm.sti()
|
|
|
|
@staticmethod
|
|
def sched_tick():
|
|
s: Scheduler* = _sched_ptr
|
|
if not s.enabled: return
|
|
if s.thread_count <= 1: return
|
|
s.tick_count += 1
|
|
if s.tick_count >= SCHED_QUANTUM:
|
|
s.tick_count = 0
|
|
s.needs_reschedule = 1
|
|
|
|
@staticmethod
|
|
def try_reschedule():
|
|
s: Scheduler* = _sched_ptr
|
|
if s.needs_reschedule:
|
|
s.needs_reschedule = 0
|
|
Scheduler._yield()
|
|
|
|
@staticmethod
|
|
def disable():
|
|
s: Scheduler* = _sched_ptr
|
|
s.enabled = 0
|
|
|
|
@staticmethod
|
|
def enable():
|
|
s: Scheduler* = _sched_ptr
|
|
s.enabled = 1
|
|
|
|
@staticmethod
|
|
def current() -> Thread*:
|
|
s: Scheduler* = _sched_ptr
|
|
return &s.threads[s.current_tid]
|
|
|
|
@staticmethod
|
|
def get_thread(tid: int) -> Thread*:
|
|
s: Scheduler* = _sched_ptr
|
|
if tid < 0 or tid >= s.thread_count: return None
|
|
return &s.threads[tid]
|
|
|
|
@staticmethod
|
|
def create_thread(func: rawptr, arg: rawptr, thread_pid: int = 0) -> Thread*:
|
|
s: Scheduler* = _sched_ptr
|
|
if s.thread_count >= MAX_THREADS: return None
|
|
tid: int = s.thread_count
|
|
s.thread_count += 1
|
|
th: Thread* = &s.threads[tid]
|
|
th.state = THREAD_READY
|
|
th.tid = tid
|
|
th.pid = thread_pid
|
|
th.func = func
|
|
th.arg = arg
|
|
stack_size: uint64 = THREAD_STACK_SIZE
|
|
stack_ptr: rawptr = mm.malloc(stack_size)
|
|
if not stack_ptr:
|
|
s.thread_count -= 1
|
|
return None
|
|
th.stack = stack_ptr
|
|
th.stack_size = stack_size
|
|
stack_top: uint64 = uint64(stack_ptr) + stack_size - 16
|
|
stack_top = stack_top & ~uint64(0xF)
|
|
entry_addr: uint64 = uint64(&_thread_entry)
|
|
sp: rawptr = rawptr(stack_top)
|
|
__asm__(f"""mov rax, {asminp(sp, ASM_DESCR.REG_ANY)}
|
|
mov qword ptr [rax - 64], 0x202
|
|
mov qword ptr [rax - 56], 0
|
|
mov qword ptr [rax - 48], 0
|
|
mov qword ptr [rax - 40], 0
|
|
mov qword ptr [rax - 32], 0
|
|
mov qword ptr [rax - 24], 0
|
|
mov qword ptr [rax - 16], 0
|
|
mov qword ptr [rax - 8], 0
|
|
mov qword ptr [rax], {asminp(entry_addr, ASM_DESCR.REG_ANY)}""",
|
|
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX])
|
|
stack_top = stack_top - 64
|
|
th.rsp = stack_top
|
|
return th
|
|
|
|
|
|
_sched_storage: Scheduler = Scheduler()
|
|
export _sched_ptr: Scheduler* = &_sched_storage
|
|
export _switch_old_rsp: rawptr
|
|
export _switch_new_rsp: rawptr
|
|
|
|
@__attribute__(naked)
|
|
def _do_switch():
|
|
__asm__(f"""push rax
|
|
push rbx
|
|
push rbp
|
|
push r12
|
|
push r13
|
|
push r14
|
|
push r15
|
|
pushfq
|
|
mov rax, qword ptr [rip + _switch_old_rsp]
|
|
mov qword ptr [rax], rsp
|
|
mov rax, qword ptr [rip + _switch_new_rsp]
|
|
mov rsp, qword ptr [rax]
|
|
popfq
|
|
pop r15
|
|
pop r14
|
|
pop r13
|
|
pop r12
|
|
pop rbp
|
|
pop rbx
|
|
pop rax
|
|
ret""",
|
|
op=[ASM_DESCR.CLOBBER_MEMORY])
|
|
|
|
@__attribute__(naked)
|
|
def _thread_entry():
|
|
__asm__(f"""sub rsp, 8
|
|
mov rax, qword ptr [rip + _sched_ptr]
|
|
mov ecx, dword ptr [rax + 896]
|
|
movsxd rcx, ecx
|
|
imul rcx, rcx, 56
|
|
mov rdi, qword ptr [rax + rcx + 32]
|
|
mov rax, qword ptr [rax + rcx + 24]
|
|
call rax
|
|
mov rax, qword ptr [rip + _sched_ptr]
|
|
mov ecx, dword ptr [rax + 896]
|
|
movsxd rcx, ecx
|
|
imul rcx, rcx, 56
|
|
mov dword ptr [rax + rcx + 40], 3
|
|
add rsp, 8""",
|
|
# `call rax` 破坏全部 caller-saved 寄存器(x64 SysV):除 RAX/RCX/RDX/
|
|
# R8-R11 外,RSI 与 RDI 也必须声明,否则编译器可能假设其值存活(N9)。
|
|
# RSP 由 `sub rsp,8` / `add rsp,8` 配对抵消,净变化为 0,无需声明。
|
|
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX,
|
|
ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RDX,
|
|
ASM_DESCR.CLOBBER_RSI, ASM_DESCR.CLOBBER_RDI,
|
|
ASM_DESCR.CLOBBER_R8, ASM_DESCR.CLOBBER_R9,
|
|
ASM_DESCR.CLOBBER_R10, ASM_DESCR.CLOBBER_R11])
|
|
Scheduler._yield()
|
|
while True:
|
|
asm.sti()
|
|
asm.hlt()
|
|
Scheduler._yield()
|
|
|
|
def thread_current() -> int:
|
|
s: Scheduler* = _sched_ptr
|
|
return s.current_tid
|
|
|
|
def needs_reschedule() -> int:
|
|
s: Scheduler* = _sched_ptr
|
|
return s.needs_reschedule
|
|
|
|
def sched_dump():
|
|
s: Scheduler* = _sched_ptr
|
|
dl: [80]char
|
|
viperlib.snprintf(&dl, 80, "[sched] dump: cur=%d cnt=%d yields=%u\n", s.current_tid, s.thread_count, _yield_cnt)
|
|
serial.puts(dl)
|
|
for i in range(s.thread_count):
|
|
sl: [80]char
|
|
viperlib.snprintf(&sl, 80, " t%d: st=%d pid=%d rsp=0x%lx\n", i, s.threads[i].state, s.threads[i].pid, s.threads[i].rsp)
|
|
serial.puts(sl)
|