;;-*-Lisp-*- (in-package goal) (bundles "KERNEL.CGO") (require "kernel/gcommon.gc") ;; Debug-handler support for the EE kernel. These routines depend on the exact ;; memory layout of the supported BIOS and verify every hardwired address before ;; installing handlers. Syscall 102 is temporarily repurposed to enter the small ;; copy/read/write routines stored in low kernel memory. ;; ;; The PC port keeps the public entry points as explicit stubs. ;; Scratch layout beginning at #x80000. The kernel syscall dispatcher reads the ;; three function slots installed below; the remaining fields hold interrupt and ;; DMA bookkeeping shared with the exception handlers. (deftype lowmemmap (structure) ((irq-info-stack uint32) (irq2-info-stack uint32) (kernel-copy-fn uint32) (kernel-write-fn uint32) (r1-save uint128) (last-time uint32) (high-time uint32) (dma-status uint32) (dma-qnext uint32) (dma-qwc uint32) (dma-tnext uint32) (dma-stack0 uint32) (dma-stack1 uint32) (kernel-read-fn uint32))) (defmacro nyi-break (name) `(begin (format 0 "~A is not implemented!~%" (quote ,name)) (break) (none))) (defun kernel-copy-function ((unused object) (source pointer) (dest pointer) (word-count int)) "Copy a positive number of 32-bit words for the private kernel syscall. The first ABI argument is unused." (#unless PC_PORT (rlet ((source-reg :reg a1) (dest-reg :reg a2) (remaining :reg a3) (word)) copy-word (l.w word source-reg) (s.w word dest-reg) (&+! source-reg 4) (&+! dest-reg 4) (-! remaining 1) (b.nz remaining copy-word :delay (nop!)) (j ra :delay (nop!)))) (#when PC_PORT (nyi-break kernel-copy-function))) ;; The syscall dispatcher reads these entry points directly from low memory. (#unless PC_PORT (set! (-> (the lowmemmap #x80000) kernel-copy-fn) (the uint32 kernel-copy-function))) (defun kernel-copy-to-kernel-ram ((unused object) (source pointer) (dest pointer) (word-count int)) "Invoke the private syscall-102 kernel copy service." (#unless PC_PORT (rlet ((selector :reg a0) (syscall-id :reg v1)) (m! selector #xe001fd5a) (m! syscall-id 102) (syscall 0) (return (none)))) (#when PC_PORT (nyi-break kernel-copy-to-kernel-ram))) (defun kernel-write-function ((unused object) (dest pointer) (value uint32)) "Write one 32-bit word for the private kernel syscall. The first ABI argument is unused." (#unless PC_PORT (rlet ((dest-reg :reg a1) (value-reg :reg a2)) (j ra :delay (s.w value-reg dest-reg)))) (#when PC_PORT (nyi-break kernel-write-function))) (#unless PC_PORT (set! (-> (the lowmemmap #x80000) kernel-write-fn) (the uint32 kernel-write-function))) (defun kernel-write ((unused object) (dest pointer) (value uint32)) "Invoke the private syscall-102 kernel word-write service." (#unless PC_PORT (rlet ((selector :reg a0) (syscall-id :reg v1)) (m! selector #xe001fd5b) (m! syscall-id 102) (syscall 0) (return (none)))) (#when PC_PORT (nyi-break kernel-write))) (defun kernel-read-function ((unused object) (source pointer)) "Read one signed 32-bit word for the private kernel syscall. The first ABI argument is unused." (#unless PC_PORT (rlet ((source-reg :reg a1) (result :reg v0)) (j ra :delay (l.w result source-reg)))) (#when PC_PORT (nyi-break kernel-read-function))) (#unless PC_PORT (set! (-> (the lowmemmap #x80000) kernel-read-fn) (the uint32 kernel-read-function))) (defun kernel-read ((unused object) (source pointer)) "Invoke the private syscall-102 kernel word-read service." (#unless PC_PORT (rlet ((selector :reg a0) (syscall-id :reg v1) (result :reg v0)) (m! selector #xe001fd68) (m! syscall-id 102) (syscall 0) (return (the int result)))) (#when PC_PORT (nyi-break kernel-read))) (defun kernel-check-hardwired-addresses () "Verify that the running PS2 kernel matches the hardwired debug-monitor memory layout." (#unless PC_PORT (let* ((irq-table (+ #x80010000 (logand (kernel-read 0 (the pointer #x80000248)) #xffff))) (handler-2 (kernel-read 0 (the pointer (+ irq-table 8)))) (handler-3 (kernel-read 0 (the pointer (+ irq-table 12)))) (handler-7 (kernel-read 0 (the pointer (+ irq-table 28)))) (exception-table (+ #x80010000 (logand (kernel-read 0 (the pointer #x800001a0)) #xffff))) (exception-handler (kernel-read 0 (the pointer exception-table))) (level2-vector-1 (+ #x80000000 (* (logand (kernel-read 0 (the pointer #x80000088)) #xffff) 4))) (level2-vector-2 (+ #x80000000 (* (logand (kernel-read 0 (the pointer #x80000108)) #xffff) 4))) (syscall-table (+ #x80010000 (logand (kernel-read 0 (the pointer #x800002f8)) #xffff))) (syscall-102-handler (kernel-read 0 (the pointer (+ syscall-table 408)))) (syscall-102-data (logior (shl (logand (kernel-read 0 (the pointer syscall-102-handler)) #xffff) 16) (logand (kernel-read 0 (the pointer (+ syscall-102-handler 16))) #xffff))) (valid? #t)) (format 0 "Read data from kernel~%") (format 0 "IRQ table at ~8X~%" irq-table) (format 0 " Handler 2 ~8X~%" handler-2) (format 0 " Handler 3 ~8X~%" handler-3) (format 0 " Handler 7 ~8X~%" handler-7) (format 0 "Exception table at ~8X~%" exception-table) (format 0 " Handler ~8X~%" exception-handler) (format 0 "L2 exception #1 ~8X~%" level2-vector-1) (format 0 "L2 exception #2 ~8X~%" level2-vector-2) (format 0 "SYSCALL table at ~8X~%" syscall-table) (format 0 " SYSCALL 102 ~8X~%" syscall-102-handler) (format 0 " SYSCALL 102 data ~8X~%" syscall-102-data) (unless (= irq-table #x80014180) (set! valid? #f)) (unless (= handler-2 #x80000380) (set! valid? #f)) (unless (= handler-3 #x800004c0) (set! valid? #f)) (unless (= handler-7 #x80000600) (set! valid? #f)) (unless (= exception-table #x80014140) (set! valid? #f)) (unless (= exception-handler #x80012ec8) (set! valid? #f)) (unless (= level2-vector-1 #x80012f64) (set! valid? #f)) (unless (= level2-vector-2 #x80012f64) (set! valid? #f)) (unless (= syscall-table #x80013d40) (set! valid? #f)) (unless (= syscall-102-handler #x80000a80) (set! valid? #f)) (unless (= syscall-102-data #x80000aa0) (set! valid? #f)) (unless valid? (format 0 "======================================================================~%") (format 0 "I CANNOT CONTINUE TO LOAD GOAL, BECAUSE THE KERNEL VERSION HAS CHANGED~%") (format 0 "======================================================================~%") (format 0 "~%Alternatively, try removing pskernel.gc and debug-handlers.gc from project.cl, do an (lg) and then (:r) again~%") ;; Stop immediately: using these addresses with another kernel would corrupt kernel memory. (s.l r0 r0 2)) (dotimes (i 128) (unless (zero? (kernel-read 0 (the pointer (+ #x80030000 (* i 4))))) (set! valid? #f))) (unless valid? (format 0 "======================================================================~%") (format 0 "I CANNOT CONTINUE TO LOAD GOAL, BECAUSE THE KERNEL VERSION HAS CHANGED~%") (format 0 " (Previously unused area now has data) ~%") (format 0 "======================================================================~%") (format 0 "~%Alternatively, try removing pskernel.gc and debug-handlers.gc from project.cl, do an (lg) and then (:r) again~%") (s.l r0 r0 2)) (format 0 "Unused area OK~%") (return (none)))) (#when PC_PORT (nyi-break kernel-check-hardwired-addresses))) (defun install-default-debug-handler ((handler object)) "Install one debug handler for the supported EE exception classes." (#unless PC_PORT (install-debug-handler 1 handler) (install-debug-handler 2 handler) (install-debug-handler 3 handler) (install-debug-handler 4 handler) (install-debug-handler 5 handler) (install-debug-handler 6 handler) (install-debug-handler 7 handler) (install-debug-handler 10 handler) (install-debug-handler 11 handler) (install-debug-handler 12 handler) (install-debug-handler 13 handler) (return (none))) (#when PC_PORT (nyi-break install-default-debug-handler))) (defun return-from-exception ((regs (pointer uint128))) "Restore an EE register image and return from the exception with eret." (#unless PC_PORT (rlet ((regs-base :reg a0)) (l.q r0 regs-base 0) (l.q at regs-base 16) (l.q v0 regs-base 32) (l.q v1 regs-base 48) (l.q a1 regs-base 80) (l.q a2 regs-base 96) (l.q a3 regs-base 112) (l.q t0 regs-base 128) (l.q t1 regs-base 144) (l.q t2 regs-base 160) (l.q t3 regs-base 176) (l.q t4 regs-base 192) (l.q t5 regs-base 208) (l.q t6 regs-base 224) (l.q t7 regs-base 240) (l.q s0 regs-base 256) (l.q s1 regs-base 272) (l.q s2 regs-base 288) (l.q s3 regs-base 304) (l.q s4 regs-base 320) (l.q s5 regs-base 336) (l.q s6 regs-base 352) (l.q s7 regs-base 368) (l.q t8 regs-base 384) (l.q t9 regs-base 400) (l.q gp regs-base 448) (l.q sp regs-base 464) (l.q fp regs-base 480) (l.q ra regs-base 496) ;; a0 is restored last because it holds the register-image base. (l.q a0 regs-base 64) (sync.l) (sync.p) (eret :delay (nop!)) (nop!) (nop!))) (#when PC_PORT (nyi-break return-from-exception))) (defun kernel-set-exception-vector ((exception int) (handler object)) "Install a handler for an EE exception class." (#unless PC_PORT (rlet ((exception-reg :reg a0) (syscall-id :reg v1) (result :reg v0)) (b.geu exception-reg 4 common-handler :delay (nop!)) (m! syscall-id 13) (syscall 0) (m! result 0) (b vector-done :delay (nop!)) common-handler (m! syscall-id 14) (syscall 0) (m! result 0) vector-done (return (none)))) (#when PC_PORT (nyi-break kernel-set-exception-vector))) (defun kernel-set-interrupt-vector ((interrupt int) (handler object)) "Install an EE interrupt handler through syscall 15." (#unless PC_PORT (rlet ((syscall-id :reg v1) (result :reg v0)) (m! syscall-id 15) (syscall 0) (m! result 0) (return (none)))) (#when PC_PORT (nyi-break kernel-set-interrupt-vector))) (defun kernel-set-level2-vector ((vector-number int) (handler uint)) "Install a KSEG0 handler for one of the two level-two vectors." (#unless PC_PORT (when (!= (logand (shl #xf000 16) handler) (shl #x8000 16)) (format 0 "CANNOT SET LEVEL2 HANDLER IF IT IS NOT IN KSEG0 MEMORY (#x8xxxxxxx)~%") (return #f)) (if (= vector-number 1) (kernel-write 0 (the-as pointer (the-as uint #x80000088)) (the-as uint32 (logior #x8000000 (shr (logand #xfffffff handler) 2))))) (if (= vector-number 2) (kernel-write 0 (the-as pointer (the-as uint #x80000108)) (the-as uint32 (logior #x8000000 (shr (logand #xfffffff handler) 2))))) (return #t)) (#when PC_PORT (nyi-break kernel-set-level2-vector))) (defun deinstall-debug-handler ((exception int)) "Restore the default kernel handler for one exception class." (#unless PC_PORT (kernel-set-exception-vector exception (the-as uint #x80012ec8)) (return (none))) (#when PC_PORT (nyi-break deinstall-debug-handler))) (defun deinstall-debug-handlers () "Restore the default kernel handler for every exception class used by the debug monitor." (#unless PC_PORT (kernel-set-exception-vector 1 (the-as uint #x80012ec8)) (kernel-set-exception-vector 2 (the-as uint #x80012ec8)) (kernel-set-exception-vector 3 (the-as uint #x80012ec8)) (kernel-set-exception-vector 4 (the-as uint #x80012ec8)) (kernel-set-exception-vector 5 (the-as uint #x80012ec8)) (kernel-set-exception-vector 6 (the-as uint #x80012ec8)) (kernel-set-exception-vector 7 (the-as uint #x80012ec8)) (kernel-set-exception-vector 10 (the-as uint #x80012ec8)) (kernel-set-exception-vector 11 (the-as uint #x80012ec8)) (kernel-set-exception-vector 12 (the-as uint #x80012ec8)) (kernel-set-exception-vector 13 (the-as uint #x80012ec8)) (return (none))) (#when PC_PORT (nyi-break deinstall-debug-handlers))) (defun resend-exception ((status uint) (cause uint) (epc uint) (bad-vaddr uint) (bad-paddr uint) (regs (pointer uint128))) "Remove the debug handlers, restore the saved COP0 exception state, and resume through the supplied register image." (#unless PC_PORT (deinstall-debug-handlers) (m Status status) (m Cause cause) (m EPC epc) (m BadVAddr bad-vaddr) (m BadPAddr bad-paddr) (return-from-exception regs) (return (none))) (#when PC_PORT (nyi-break resend-exception)))