Nugget
Bare-metal libraries and examples for the original PlayStation
Loading...
Searching...
No Matches
monitor.c
Go to the documentation of this file.
1/*
2
3MIT License
4
5Copyright (c) 2026 PCSX-Redux authors
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25*/
26
27#include "monitor/monitor.h"
28
29#include <stddef.h>
30
33#include "monitor/cop0dbg.h"
34#include "monitor/install.h"
35#include "monitor/kernel.h"
37#include "monitor/link.h"
38#include "monitor/transport.h"
39
40#ifdef MONITOR_LZ4
41#define LZ4STREAM_TRUSTED 1
42#include "monitor/lz4stream.c"
43#define MON_CAPS_LZ4 MON_CAP_LZ4
44#else
45#define MON_CAPS_LZ4 0
46#endif
47
48/* A link that says where its received-byte flag is can take STOP while the
49 target runs (see monitorSlotEntry). ATCONS does not. */
50#ifdef LINK_RX_STAT_ADDR
51#define MON_CAPS_STOP MON_CAP_STOP
52#else
53#define MON_CAPS_STOP 0
54#endif
55
56#define MON_CAPS (MON_CAPS_LZ4 | MON_CAPS_STOP)
57
58/* READ_MEM responses stream out of target memory in 8 KiB chunks (design
59 section 13). Nothing is staged: bulk data goes straight to/from the operation's
60 real address, so the only buffer the monitor needs is a tiny one for the small
61 fixed-size commands (RUN carries the most at 6 words). */
62#define MON_CHUNK_BYTES 8192
63#define MON_CMD_WORDS 16
64
65/* All of the monitor's state in one object: the compiler then reaches every
66 field from one base register instead of materializing an address per
67 variable (there is no gp-relative data on this target). */
68static struct {
69 uint16_t cmd[MON_CMD_WORDS];
70 /* Saved context of the halted program (== the current thread's register
71 frame, which the kernel exception entry fills). NULL before the first
72 stop and while running. */
73 struct Registers *ctx;
74 /* BadVaddr captured at the last fault (struct Registers has no slot for it). */
76 /* Running DCIC image + the armed data-watch address, so SET_BP/CLR_BP can
77 add/remove one breakpoint kind and the stop path can report the watch. */
80 /* Set when WRITE_MEM or LOAD wrote memory since the last resume. */
82 /* Set from RUN/CONT until the next stop: only then is a byte on the link
83 a STOP request. Before the first RUN it is the host's next command. */
85#ifdef MONITOR_LZ4
86 /* The LZ4 stream in flight: its decoder, how much of clen has arrived, and
87 whether one is open at all (see streamWriteMemLz4). */
88 struct Lz4Stream lz;
89 uint32_t lzConsumed;
90 int lzActive;
91#endif
92} s_mon;
93
94/* SR the target runs under after a fresh RUN. The rfe in returnFromException
95 pops IEp->IEc, so IEp (bit2) enables interrupts; IM2 (bit10) unmasks the PS1
96 IRQ line; CU2 (bit30) keeps the GTE usable. BRING-UP KNOB: unverified on
97 silicon - adjust if a launched binary needs a different privilege/IRQ state. */
98#define MON_RUN_SR 0x40000404u
99
100/* ---- small helpers ---- */
101
102static struct Registers *currentRegs(void) { return &__globals.processes[0].thread->registers; }
103
104/* Resume the target. Anything written may be code the I-cache still holds
105 older lines for, so flush first when memory changed. */
106static inline __attribute__((noreturn)) void monitorResume(void) {
107 s_mon.running = 1;
108 if (s_mon.memWritten) {
109 s_mon.memWritten = 0;
110 syscall_flushCache();
111 }
112 syscall_returnFromException();
113}
114
115static uint32_t rd32(const uint16_t *p, unsigned i) { return (uint32_t)p[i] | ((uint32_t)p[i + 1] << 16); }
116
117static void sendWord32(uint32_t v) {
118 transportSendWord((uint16_t)(v & 0xffff));
119 transportSendWord((uint16_t)(v >> 16));
120}
121
122/* A command's reply: 0 is an ACK, a MON_E* code is an ERROR carrying it, and
123 MON_REPLIED means the handler already answered (DATA, REGS, PONG). */
124#define MON_REPLIED (-1)
125
126static void sendStatus(int code) {
127 uint16_t c = (uint16_t)code;
128 transportSendBegin(code ? MON_ERROR : MON_ACK, code != 0);
129 if (code) transportSendWord(c);
131}
132
133/* ---- events ---- */
134
135/* Fletcher-32 of the 512 KiB BIOS region, set once before the monitor is
136 entered (see monitorBiosChecksum). */
138
139/* The kernel exception handler patch slot monitorHook() pointed at
140 monitorSlotEntry, or 0 when it left the handler alone (install.h). */
142
143/* HELLO and PONG: [proto_ver:u16][caps:u16][bios_fletcher32:u32]. PONG
144 carries it too, so a host that attaches after boot can still ask. */
145static void emitIdentity(uint16_t type) {
146 transportSendBegin(type, 4);
149 sendWord32(s_biosChecksum);
151}
152
153/* STOPPED [reason:u16][epc:u32][a:u32][b:u32] */
154static void emitStopped(uint16_t reason, uint32_t epc, uint32_t a, uint32_t b) {
156 transportSendWord(reason);
157 sendWord32(epc);
158 sendWord32(a);
159 sendWord32(b);
161}
162
163/* ---- inspection / control command handlers ---- */
164
165/* READ_MEM [addr:u32][len:u32] -> one or more DATA [nbytes:u32][bytes] frames,
166 each capped at an 8 KiB chunk; the host concatenates until it has len bytes. */
167static int cmdReadMem(const uint16_t *p) {
168 uint32_t addr = rd32(p, 0);
169 uint32_t len = rd32(p, 2);
170 const uint8_t *src = (const uint8_t *)addr;
171
172 uint32_t off = 0;
173 do {
174 uint32_t chunk = len - off;
175 if (chunk > MON_CHUNK_BYTES) chunk = MON_CHUNK_BYTES;
176 uint32_t words = (chunk + 1) >> 1;
177
178 transportSendBegin(MON_DATA, (uint16_t)(2 + words));
179 sendWord32(chunk); /* nbytes in THIS frame */
180 for (uint32_t i = 0; i < words; i++) {
181 uint32_t bi = off + 2 * i;
182 uint8_t b0 = src[bi];
183 uint8_t b1 = (2 * i + 1 < chunk) ? src[bi + 1] : 0;
184 transportSendWord((uint16_t)(b0 | (b1 << 8)));
185 }
187 off += chunk;
188 } while (off < len);
189 return MON_REPLIED;
190}
191
192static uint32_t recvU32(void) {
195 return lo | (hi << 16);
196}
197
198/* WRITE_MEM / LOAD [addr:u32][len:u32][bytes] -> ACK | ERROR. Called mid-frame:
199 transportRecvBegin has already consumed TYPE and LEN. The bytes are decoded
200 straight into the target address as they arrive - no staging buffer - so a
201 single frame carries an arbitrarily large write (host still chunks at 8 KiB).
202 `frameWords` is the payload word count from the frame header. */
203static int streamWriteMem(uint16_t frameWords) {
204 if (frameWords < 4) {
205 for (uint16_t w = 0; w < frameWords; w++) transportRecvWord();
207 return MON_EBADLEN;
208 }
209 uint32_t addr = recvU32(); /* 2 words */
210 uint32_t nbytes = recvU32(); /* 2 words */
211 uint32_t consumed = 4;
212 uint8_t *dst = (uint8_t *)addr;
213 s_mon.memWritten = 1;
214
215 for (uint16_t w = consumed; w < frameWords; w++) {
216 uint16_t word = transportRecvWord();
217 uint32_t bi = (uint32_t)(w - consumed) * 2;
218 if (bi < nbytes) dst[bi] = (uint8_t)(word & 0xff);
219 if (bi + 1 < nbytes) dst[bi + 1] = (uint8_t)(word >> 8);
220 }
221
222 return transportRecvEnd() == TRANSPORT_OK ? 0 : MON_ECKSUM;
223}
224
225#ifdef MONITOR_LZ4
226/* WRITE_MEM / LOAD with MON_LZ4: [dest:u32][rawlen:u32][clen:u32][off:u32]
227 [nbytes:u32][bytes]. The frames carry consecutive slices of one LZ4 block
228 stream of clen bytes that decodes to rawlen bytes at dest; off is where this
229 slice starts in it, and 0 starts a new stream. Bytes are decoded as they
230 arrive, so every frame is ACKed on its own; the last one also checks the
231 stream ended cleanly at exactly rawlen bytes. Any failure drops the stream,
232 and the host starts over from off 0. */
233static int streamWriteMemLz4(uint16_t frameWords) {
234 uint32_t words = frameWords;
235 uint32_t rawLen = 0, clen = 0, nbytes = 0;
236 int bad = 0;
237
238 s_mon.memWritten = 1;
239 if (words < 10) {
240 bad = MON_EBADLEN; /* the rest of the frame is drained below */
241 } else {
242 uint32_t dest = recvU32();
243 rawLen = recvU32();
244 clen = recvU32();
245 uint32_t off = recvU32();
246 nbytes = recvU32();
247 words -= 10;
248 if (off == 0) {
249 lz4StreamInit(&s_mon.lz, (void *)dest);
250 s_mon.lzConsumed = 0;
251 s_mon.lzActive = 1;
252 } else if (!s_mon.lzActive || off != s_mon.lzConsumed) {
253 bad = MON_EBADSTATE;
254 }
255 if (!bad && (nbytes > words * 2 || off + nbytes > clen)) bad = MON_EBADLEN;
256 }
257
258 for (uint32_t w = 0; w < words; w++) {
259 uint16_t word = transportRecvWord();
260 if (bad) continue;
261 /* Low byte then high byte, as far as nbytes reaches. */
262 for (uint32_t bi = w * 2; bi < w * 2 + 2 && bi < nbytes; bi++, word >>= 8) {
263 if (!bad && lz4StreamFeed(&s_mon.lz, (uint8_t)word)) bad = MON_EDECODE;
264 }
265 }
267 if (!bad) {
268 s_mon.lzConsumed += nbytes;
269 if (s_mon.lzConsumed == clen) {
270 if (lz4StreamEndBlock(&s_mon.lz) || (uint32_t)(s_mon.lz.out - s_mon.lz.base) != rawLen) bad = MON_EDECODE;
271 s_mon.lzActive = 0;
272 }
273 }
274 if (bad) s_mon.lzActive = 0;
275 return bad;
276}
277#endif
278
279/* The REGS table, gdb g-packet order: 0..31 r0..r31, then SR, LO, HI,
280 BadVaddr, Cause, PC. Where entry idx lives; BadVaddr has no slot in struct
281 Registers and lives in s_mon. */
282static uint32_t *regSlot(struct Registers *r, unsigned idx) {
283 static const uint8_t special[] = {
284 offsetof(struct Registers, SR), offsetof(struct Registers, lo), offsetof(struct Registers, hi), 0,
285 offsetof(struct Registers, Cause), offsetof(struct Registers, returnPC),
286 };
287 if (idx < 32) return &r->GPR.r[idx];
288 if (idx == 35) return &s_mon.badVaddr;
289 return (uint32_t *)((uint8_t *)r + special[idx - 32]);
290}
291
292/* GET_REGS [] -> REGS [38 x u32]. Zeros for the registers before the first
293 stop; BadVaddr is reported regardless. */
294static int cmdGetRegs(void) {
295 struct Registers *r = s_mon.ctx;
297 for (unsigned i = 0; i < 38; i++) sendWord32((r || i == 35) ? *regSlot(r, i) : 0);
299 return MON_REPLIED;
300}
301
302/* SET_REG [idx:u16][value:u32] -> ACK | ERROR. idx indexes the REGS table. */
303static int cmdSetReg(const uint16_t *p) {
304 uint16_t idx = p[0];
305 uint32_t val = rd32(p, 1);
306 struct Registers *r = s_mon.ctx;
307
308 if (idx > 37) return MON_EBADREG;
309 if (!r) return MON_EBADSTATE;
310 if (idx != 0) *regSlot(r, idx) = val; /* r0 stays 0 */
311 return 0;
312}
313
314/* SET_BP [kind:u16][addr:u32][mask:u32] -> ACK.
315 kind: 0 exec, 1 data-read, 2 data-write, 3 data-rw. */
316static int cmdSetBp(const uint16_t *p) {
317 uint16_t kind = p[0];
318 uint32_t addr = rd32(p, 1);
319 uint32_t mask = rd32(p, 3);
320
321 if (kind == 0) {
322 writeBPC(addr);
323 writeBPCM(mask);
324 s_mon.dcic |= DCIC_DE | DCIC_PCE | DCIC_TR | DCIC_KD | DCIC_UD;
325 } else if (kind <= 3) {
326 writeBDA(addr);
327 writeBDAM(mask);
328 s_mon.watchAddr = addr;
329 /* kind bit0 is read, bit1 is write; DR and DW sit in that order in DCIC. */
330 s_mon.dcic |= DCIC_DE | DCIC_DAE | DCIC_TR | DCIC_KD | DCIC_UD | (uint32_t)kind * DCIC_DR;
331 } else {
332 return MON_EBADCMD;
333 }
334 writeDCIC(s_mon.dcic);
335 return 0;
336}
337
338/* CLR_BP [kind:u16] -> ACK. */
339static int cmdClrBp(const uint16_t *p) {
340 uint16_t kind = p[0];
341 if (kind == 0) {
342 s_mon.dcic &= ~DCIC_PCE;
343 } else {
344 s_mon.dcic &= ~(DCIC_DAE | DCIC_DR | DCIC_DW);
345 }
346 if ((s_mon.dcic & (DCIC_PCE | DCIC_DAE)) == 0) s_mon.dcic = 0; /* drop master enable */
347 writeDCIC(s_mon.dcic);
348 return 0;
349}
350
351/* RUN [pc:u32][gp:u32][sp:u32] -> ACK, then hand the CPU to the target. Never
352 returns (returnFromException does an rfe into pc). */
353static __attribute__((noreturn)) void cmdRun(const uint16_t *p) {
354 uint32_t pc = rd32(p, 0);
355 uint32_t gp = rd32(p, 2);
356 uint32_t sp = rd32(p, 4);
357 struct Registers *r = currentRegs();
358
359 /* Everything zero except what the target starts from. */
360 for (unsigned i = 0; i < sizeof(*r) / sizeof(uint32_t); i++) ((uint32_t *)r)[i] = 0;
361 r->GPR.n.gp = gp;
362 r->GPR.n.sp = sp;
363 r->GPR.n.fp = sp;
364 r->returnPC = pc;
365 r->SR = MON_RUN_SR;
366 s_mon.ctx = 0; /* running: no halted context */
367
368 sendStatus(0);
369 monitorResume();
370}
371
372/* CONT [] -> ACK, then resume the saved (possibly SET_REG-modified) context. */
373static int cmdCont(void) {
374 if (!s_mon.ctx) return MON_EBADSTATE;
375 s_mon.ctx = 0; /* running: no halted context */
376 sendStatus(0);
377 monitorResume();
378}
379
380/* SET_BAUD [reload:u16] -> ACK at the old rate, then PONG at the new one if
381 the host's PING arrives there (design section 2a). */
382static int cmdSetBaud(const uint16_t *p) {
383 uint16_t reload = p[0];
384 if (reload == 0) return MON_EBADLEN;
385 if (!transportHasRate()) return MON_EBADCMD;
386 sendStatus(0);
387 uint16_t ver = MON_PROTO_VER;
388 if (transportTryRate(reload, MON_PONG, ver) == 1) transportSendFrame(MON_PONG, &ver, 1);
389 return MON_REPLIED;
390}
391
392/* Dispatch one small HALTED-state command whose payload is already buffered in
393 s_mon.cmd. WRITE_MEM/LOAD are NOT here - they stream directly to target memory in
394 the loop. RUN does not return (it resumes the target). Returns the reply. */
395static int dispatchCommand(uint16_t type, const uint16_t *payload) {
396 switch (type) {
397 case MON_PING:
398 emitIdentity(MON_PONG);
399 return MON_REPLIED;
400 case MON_READ_MEM: return cmdReadMem(payload);
401 case MON_GET_REGS: return cmdGetRegs();
402 case MON_SET_REG: return cmdSetReg(payload);
403 case MON_SET_BP: return cmdSetBp(payload);
404 case MON_CLR_BP: return cmdClrBp(payload);
405 case MON_RUN: cmdRun(payload); /* does not return */
406 case MON_CONT: return cmdCont();
407 case MON_SET_BAUD: return cmdSetBaud(payload);
408 case MON_STOP:
409 /* Only meaningful while RUNNING, where the exception entry reads
410 it (monitorTake). In HALTED it is a no-op with no reply: a host
411 whose STOP crossed a stop on the wire must not get an ACK it
412 cannot match to a command. */
413 return MON_REPLIED;
414 default: return MON_EBADCMD;
415 }
416}
417
418/* Blocking HALTED command loop. Bulk WRITE_MEM/LOAD payloads are decoded
419 straight into target memory (no staging); every other command has a small
420 fixed payload buffered in s_mon.cmd. Returns only via a resume, which is noreturn,
421 so in practice it does not return. */
422static __attribute__((noreturn)) void monitorCommandLoop(void) {
423 for (;;) {
424 uint16_t type;
425 uint16_t len;
426 transportRecvBegin(&type, &len);
427
428 int reply;
429 uint16_t base = type & ~MON_LZ4;
430 if (base == MON_WRITE_MEM || base == MON_LOAD) {
431 if (!(type & MON_LZ4)) {
432 reply = streamWriteMem(len);
433 } else {
434#ifdef MONITOR_LZ4
435 reply = streamWriteMemLz4(len);
436#else
437 for (uint16_t i = 0; i < len; i++) transportRecvWord();
439 reply = MON_EBADCMD;
440#endif
441 }
442 } else {
443 for (uint16_t i = 0; i < len; i++) {
444 uint16_t w = transportRecvWord();
445 if (i < MON_CMD_WORDS) s_mon.cmd[i] = w; /* the rest is drained */
446 }
448 reply = MON_ECKSUM;
449 } else if (len > MON_CMD_WORDS) {
450 reply = MON_EBADLEN;
451 } else {
452 reply = dispatchCommand(type, s_mon.cmd);
453 }
454 }
455 if (reply != MON_REPLIED) sendStatus(reply);
456 }
457}
458
459/* Snapshot a stop, tell the host, and drop into the command loop until the host
460 resumes. Never returns. */
461static __attribute__((noreturn)) void monitorStop(struct Registers *r, uint16_t reason, uint32_t a, uint32_t b) {
462 s_mon.running = 0;
463 s_mon.ctx = r;
464 s_mon.badVaddr = (reason == MON_STOP_FAULT) ? b : 0;
465 emitStopped(reason, r->returnPC, a, b);
466 monitorCommandLoop();
468}
469
470/* ---- exception entry ---- */
471
472/* What the monitor owns, for either entry below. `r` is the current thread's
473 register frame with the whole context in it. Returns for exceptions that
474 are not the monitor's; when it owns one it never returns (it resumes the
475 target or enters the command loop). */
476static __attribute__((noinline)) void monitorTake(struct Registers *r) {
477 uint32_t excode = CAUSE_EXCCODE(r->Cause);
478
479 if (excode == EXCCODE_BP) {
480 uint32_t insn = *(uint32_t *)(r->returnPC);
481 if ((insn & 0x3f) == 0x0d) {
482 /* Software `break`. `break 4, 1` is the monitor's own entry; every
483 other one stops, with the instruction word in a and EPC left on
484 the break. What it means is the host's business, and so is
485 stepping past it. */
486 if (insn == ((4u << 16) | (1u << 6) | 0x0d)) {
487 s_mon.running = 0;
488 emitIdentity(MON_HELLO);
489 monitorCommandLoop();
490 }
491 monitorStop(r, MON_STOP_BREAKPOINT, insn, 0);
492 } else {
493 /* No `break` at EPC: a cop0 hardware breakpoint. Disable the debug
494 unit first (design section 10), then report. */
495 uint32_t dcic = readDCIC();
496 writeDCIC(0);
497 s_mon.dcic = 0;
498 if (dcic & DCIC_DA) {
499 monitorStop(r, MON_STOP_DATA_WATCH, s_mon.watchAddr, 0);
500 } else {
501 monitorStop(r, MON_STOP_BREAKPOINT, 0, 0);
502 }
503 }
504 }
505
506 if (excode == EXCCODE_ADEL || excode == EXCCODE_ADES) {
507 monitorStop(r, MON_STOP_FAULT, excode, readBadVaddr());
508 }
509 if (excode == EXCCODE_IBE || excode == EXCCODE_DBE || excode == EXCCODE_RI || excode == EXCCODE_CPU ||
510 excode == EXCCODE_OVF) {
511 monitorStop(r, MON_STOP_FAULT, excode, 0);
512 }
513
514 /* An interrupt while a target runs, with the host's STOP on the link:
515 halt here. The IRQ that brought us in stays pending and unacknowledged
516 in I_STAT, so after CONT the kernel and the program take it as if the
517 monitor had not been there. */
518 if (excode == EXCCODE_INT && s_mon.running && transportStopPending()) {
519 monitorStop(r, MON_STOP_INTERRUPT, 0, 0);
520 }
521
522 /* Syscall (8), any other interrupt, anything else: not ours. */
523}
524
525/* Entry 1, the kernel's chain: a priority-0 verifier, prepended ahead of the
526 kernel's own, called after the kernel has saved the whole context and
527 switched to its exception stack. It is lost if a program resets the
528 priority-0 chain, which is what entry 2 is for. Returns 0 for what it
529 does not own, so the rest of the chain runs. */
530static int monitorVerifier(void) {
531 monitorTake(currentRegs());
532 return 0;
533}
534
536 .next = 0,
537 .handler = 0,
538 .verifier = monitorVerifier,
539 .padding = 0,
540};
541
542/* Entry 2, the kernel exception handler's fourth patch slot (install.h puts
543 `lui at / ori at / jalr at / nop` there, calling monitorSlotEntry). The
544 slot runs before any chain, with only at, v0, v1 and ra saved in the frame
545 k0 points at (and the resume PC at +0x80); those four plus ra are all it
546 may touch before deciding. monitorSlotEntry keeps its own exceptions
547 (break, the faults above, an interrupt with a byte on the link) and
548 returns straight to the kernel for everything else. For its own, it saves
549 the rest of the context into the frame exactly as the kernel's code after
550 the slots would, moves to its own stack and calls monitorSlotDispatch. If
551 that returns (an interrupt that was console text, or came before the first
552 RUN), it puts back every register the C code may have changed and returns
553 to the kernel, which saves the same values again and runs its chains; the
554 verifier above then declines the exception too. Nothing it keeps ever
555 reaches the chains, so no exception is handled twice. */
556#ifndef MONITOR_SLOT_STACK_WORDS
557#define MONITOR_SLOT_STACK_WORDS 256
558#endif
559uint32_t monitorSlotStack[MONITOR_SLOT_STACK_WORDS] __attribute__((used, aligned(8)));
560
561void __attribute__((used, noinline)) monitorSlotDispatch(void) { monitorTake(currentRegs()); }
562
563/* ExcCodes 4-7, 9-12 as a bit mask: AdEL, AdES, IBE, DBE, Bp, RI, CpU, Ov. */
564#define MON_SLOT_EXCMASK 0x1ef0
565
566#define MON_STR_(x) #x
567#define MON_STR(x) MON_STR_(x)
568
569#ifdef LINK_RX_STAT_ADDR
570#define MON_SLOT_RX_TEST \
571 " li $v1, " MON_STR(LINK_RX_STAT_ADDR) "\n" \
572 " " LINK_RX_STAT_LOAD " $v1, 0($v1)\n" \
573 " nop\n" \
574 " andi $v1, $v1, " MON_STR(LINK_RX_STAT_BIT) "\n" \
575 " bnez $v1, 2f\n" \
576 " nop\n"
577#else
578#define MON_SLOT_RX_TEST
579#endif
580
582 " .section .text.monitorSlotEntry, \"ax\", @progbits\n"
583 " .align 2\n"
584 " .global monitorSlotEntry\n"
585 " .type monitorSlotEntry, @function\n"
586 " .set push\n"
587 " .set noreorder\n"
588 " .set noat\n"
589 "monitorSlotEntry:\n"
590 /* v0 = ExcCode */
591 " mfc0 $v0, $13\n"
592 " nop\n"
593 " andi $v0, $v0, 0x7c\n"
594 " beqz $v0, 1f\n"
595 " srl $v0, $v0, 2\n"
596 " li $v1, " MON_STR(MON_SLOT_EXCMASK) "\n"
597 " srlv $v1, $v1, $v0\n"
598 " andi $v1, $v1, 1\n"
599 " bnez $v1, 2f\n"
600 " nop\n"
601 " jr $ra\n"
602 " nop\n"
603 /* Interrupt: ours only if the link has a byte waiting. */
604 "1:\n" MON_SLOT_RX_TEST
605 " jr $ra\n"
606 " nop\n"
607 /* Ours: the kernel's own save sequence, same order, same offsets. */
608 "2:\n"
609 " sw $a0, 0x10($k0)\n"
610 " sw $a1, 0x14($k0)\n"
611 " sw $a2, 0x18($k0)\n"
612 " sw $a3, 0x1c($k0)\n"
613 " mfc0 $a0, $12\n"
614 " nop\n"
615 " sw $a0, 0x8c($k0)\n"
616 " mfc0 $a1, $13\n"
617 " nop\n"
618 " sw $a1, 0x90($k0)\n"
619 " sw $k1, 0x6c($k0)\n"
620 " sw $s0, 0x40($k0)\n"
621 " sw $s1, 0x44($k0)\n"
622 " sw $s2, 0x48($k0)\n"
623 " sw $s3, 0x4c($k0)\n"
624 " sw $s4, 0x50($k0)\n"
625 " sw $s5, 0x54($k0)\n"
626 " sw $s6, 0x58($k0)\n"
627 " sw $s7, 0x5c($k0)\n"
628 " sw $t0, 0x20($k0)\n"
629 " sw $t1, 0x24($k0)\n"
630 " sw $t2, 0x28($k0)\n"
631 " sw $t3, 0x2c($k0)\n"
632 " sw $t4, 0x30($k0)\n"
633 " sw $t5, 0x34($k0)\n"
634 " sw $t6, 0x38($k0)\n"
635 " sw $t7, 0x3c($k0)\n"
636 " sw $t8, 0x60($k0)\n"
637 " sw $t9, 0x64($k0)\n"
638 " sw $gp, 0x70($k0)\n"
639 " sw $sp, 0x74($k0)\n"
640 " sw $fp, 0x78($k0)\n"
641 " mfhi $a0\n"
642 " nop\n"
643 " sw $a0, 0x84($k0)\n"
644 " mflo $a0\n"
645 " nop\n"
646 " sw $a0, 0x88($k0)\n"
647 /* s0 (saved above, callee-saved in C) keeps the way back into the
648 kernel's handler. */
649 " move $s0, $ra\n"
650 " la $sp, monitorSlotStack + " MON_STR(MONITOR_SLOT_STACK_WORDS) " * 4 - 16\n"
651 " jal monitorSlotDispatch\n"
652 " nop\n"
653 /* Declined: the frame pointer again (k0 is the kernel's), the registers
654 C may have changed, and back to the kernel. */
655 " lw $k0, 0x108($zero)\n" /* the table of tables at 0x100: ->processes */
656 " move $ra, $s0\n"
657 " lw $k0, 0($k0)\n"
658 " nop\n"
659 " addiu $k0, $k0, 8\n"
660 " lw $a0, 0x84($k0)\n"
661 " lw $a1, 0x88($k0)\n"
662 " mthi $a0\n"
663 " mtlo $a1\n"
664 " lw $a0, 0x10($k0)\n"
665 " lw $a1, 0x14($k0)\n"
666 " lw $a2, 0x18($k0)\n"
667 " lw $a3, 0x1c($k0)\n"
668 " lw $t0, 0x20($k0)\n"
669 " lw $t1, 0x24($k0)\n"
670 " lw $t2, 0x28($k0)\n"
671 " lw $t3, 0x2c($k0)\n"
672 " lw $t4, 0x30($k0)\n"
673 " lw $t5, 0x34($k0)\n"
674 " lw $t6, 0x38($k0)\n"
675 " lw $t7, 0x3c($k0)\n"
676 " lw $t8, 0x60($k0)\n"
677 " lw $t9, 0x64($k0)\n"
678 " lw $s0, 0x40($k0)\n"
679 " lw $sp, 0x74($k0)\n"
680 " jr $ra\n"
681 " nop\n"
682 " .set pop\n"
683 " .size monitorSlotEntry, . - monitorSlotEntry\n"
684 " .previous\n");
685
686void monitorMain(void) {
687 psxprintf("OpenBIOS Monitor.\n");
689 s_mon.ctx = 0;
690 s_mon.badVaddr = 0;
691 s_mon.dcic = 0;
692 s_mon.watchAddr = 0;
693 s_mon.running = 0;
694
695 s_biosChecksum = monitorBiosChecksum();
696 monitorHook();
697 monitorEnter();
698}
__attribute__((weak))
Definition clz.c:56
#define DCIC_UD
Definition cop0dbg.h:83
#define EXCCODE_OVF
Definition cop0dbg.h:97
#define DCIC_DA
Definition cop0dbg.h:76
#define DCIC_DR
Definition cop0dbg.h:80
#define EXCCODE_ADEL
Definition cop0dbg.h:89
#define EXCCODE_DBE
Definition cop0dbg.h:92
#define EXCCODE_INT
Definition cop0dbg.h:88
#define DCIC_DE
Definition cop0dbg.h:77
#define DCIC_TR
Definition cop0dbg.h:84
#define DCIC_KD
Definition cop0dbg.h:82
#define CAUSE_EXCCODE(cause)
Definition cop0dbg.h:87
#define EXCCODE_BP
Definition cop0dbg.h:94
#define EXCCODE_ADES
Definition cop0dbg.h:90
#define DCIC_DAE
Definition cop0dbg.h:79
#define EXCCODE_IBE
Definition cop0dbg.h:91
#define DCIC_DW
Definition cop0dbg.h:81
#define EXCCODE_CPU
Definition cop0dbg.h:96
#define EXCCODE_RI
Definition cop0dbg.h:95
#define DCIC_PCE
Definition cop0dbg.h:78
int32_t hi
Definition cpu.c:154
uint32_t r
Definition cpu.c:222
int32_t lo
Definition cpu.c:154
volatile uint32_t * sp
Definition dcache.c:524
uint8_t b
Definition gte-depthcue.c:39
int i
Definition gte-regio.c:297
struct @21 __globals
int psxprintf(const char *msg,...)
Definition stdio.c:332
void lz4StreamInit(struct Lz4Stream *s, void *dest)
Definition lz4stream.c:38
int lz4StreamFeed(struct Lz4Stream *s, uint8_t b)
Definition lz4stream.c:43
int lz4StreamEndBlock(struct Lz4Stream *s)
Definition lz4stream.c:106
uint16_t cmd[MON_CMD_WORDS]
Definition monitor.c:69
uint32_t * s_monitorSlot
Definition monitor.c:141
#define MON_CMD_WORDS
Definition monitor.c:63
#define MON_CHUNK_BYTES
Definition monitor.c:62
int memWritten
Definition monitor.c:81
int running
Definition monitor.c:84
uint32_t s_biosChecksum
Definition monitor.c:137
struct Registers * ctx
Definition monitor.c:73
__asm__(" .section .text.monitorSlotEntry, \"ax\", @progbits\n" " .align 2\n" " .global monitorSlotEntry\n" " .type monitorSlotEntry, @function\n" " .set push\n" " .set noreorder\n" " .set noat\n" "monitorSlotEntry:\n" " mfc0 $v0, $13\n" " nop\n" " andi $v0, $v0, 0x7c\n" " beqz $v0, 1f\n" " srl $v0, $v0, 2\n" " li $v1, " MON_STR(MON_SLOT_EXCMASK) "\n" " srlv $v1, $v1, $v0\n" " andi $v1, $v1, 1\n" " bnez $v1, 2f\n" " nop\n" " jr $ra\n" " nop\n" "1:\n" MON_SLOT_RX_TEST " jr $ra\n" " nop\n" "2:\n" " sw $a0, 0x10($k0)\n" " sw $a1, 0x14($k0)\n" " sw $a2, 0x18($k0)\n" " sw $a3, 0x1c($k0)\n" " mfc0 $a0, $12\n" " nop\n" " sw $a0, 0x8c($k0)\n" " mfc0 $a1, $13\n" " nop\n" " sw $a1, 0x90($k0)\n" " sw $k1, 0x6c($k0)\n" " sw $s0, 0x40($k0)\n" " sw $s1, 0x44($k0)\n" " sw $s2, 0x48($k0)\n" " sw $s3, 0x4c($k0)\n" " sw $s4, 0x50($k0)\n" " sw $s5, 0x54($k0)\n" " sw $s6, 0x58($k0)\n" " sw $s7, 0x5c($k0)\n" " sw $t0, 0x20($k0)\n" " sw $t1, 0x24($k0)\n" " sw $t2, 0x28($k0)\n" " sw $t3, 0x2c($k0)\n" " sw $t4, 0x30($k0)\n" " sw $t5, 0x34($k0)\n" " sw $t6, 0x38($k0)\n" " sw $t7, 0x3c($k0)\n" " sw $t8, 0x60($k0)\n" " sw $t9, 0x64($k0)\n" " sw $gp, 0x70($k0)\n" " sw $sp, 0x74($k0)\n" " sw $fp, 0x78($k0)\n" " mfhi $a0\n" " nop\n" " sw $a0, 0x84($k0)\n" " mflo $a0\n" " nop\n" " sw $a0, 0x88($k0)\n" " move $s0, $ra\n" " la $sp, monitorSlotStack + " MON_STR(MONITOR_SLOT_STACK_WORDS) " * 4 - 16\n" " jal monitorSlotDispatch\n" " nop\n" " lw $k0, 0x108($zero)\n" " move $ra, $s0\n" " lw $k0, 0($k0)\n" " nop\n" " addiu $k0, $k0, 8\n" " lw $a0, 0x84($k0)\n" " lw $a1, 0x88($k0)\n" " mthi $a0\n" " mtlo $a1\n" " lw $a0, 0x10($k0)\n" " lw $a1, 0x14($k0)\n" " lw $a2, 0x18($k0)\n" " lw $a3, 0x1c($k0)\n" " lw $t0, 0x20($k0)\n" " lw $t1, 0x24($k0)\n" " lw $t2, 0x28($k0)\n" " lw $t3, 0x2c($k0)\n" " lw $t4, 0x30($k0)\n" " lw $t5, 0x34($k0)\n" " lw $t6, 0x38($k0)\n" " lw $t7, 0x3c($k0)\n" " lw $t8, 0x60($k0)\n" " lw $t9, 0x64($k0)\n" " lw $s0, 0x40($k0)\n" " lw $sp, 0x74($k0)\n" " jr $ra\n" " nop\n" " .set pop\n" " .size monitorSlotEntry, . - monitorSlotEntry\n" " .previous\n")
struct HandlerInfo s_monitorHandler
Definition monitor.c:535
#define MON_SLOT_EXCMASK
Definition monitor.c:564
void monitorMain(void)
Definition monitor.c:686
#define MON_STR(x)
Definition monitor.c:567
uint32_t dcic
Definition monitor.c:78
#define MON_RUN_SR
Definition monitor.c:98
uint32_t badVaddr
Definition monitor.c:75
#define MON_CAPS
Definition monitor.c:56
#define MON_REPLIED
Definition monitor.c:124
uint32_t watchAddr
Definition monitor.c:79
#define MON_SLOT_RX_TEST
Definition monitor.c:578
#define MONITOR_SLOT_STACK_WORDS
Definition monitor.c:557
#define MON_READ_MEM
Definition monitor.h:38
#define MON_PROTO_VER
Definition monitor.h:95
#define MON_STOP_BREAKPOINT
Definition monitor.h:90
#define MON_STOPPED
Definition monitor.h:77
#define MON_ACK
Definition monitor.h:71
#define MON_SET_BAUD
Definition monitor.h:49
#define MON_ECKSUM
Definition monitor.h:85
#define MON_ERROR
Definition monitor.h:75
#define MON_SET_REG
Definition monitor.h:41
#define MON_DATA
Definition monitor.h:72
#define MON_HELLO
Definition monitor.h:76
#define MON_CAP_SLOT
Definition monitor.h:62
#define MON_STOP_DATA_WATCH
Definition monitor.h:92
#define MON_STOP_FAULT
Definition monitor.h:93
#define MON_LZ4
Definition monitor.h:52
#define MON_EDECODE
Definition monitor.h:87
#define MON_SET_BP
Definition monitor.h:42
#define MON_EBADREG
Definition monitor.h:83
#define MON_REGS
Definition monitor.h:73
#define MON_EBADLEN
Definition monitor.h:84
#define MON_WRITE_MEM
Definition monitor.h:39
#define MON_EBADSTATE
Definition monitor.h:81
#define MON_PING
Definition monitor.h:37
#define MON_EBADCMD
Definition monitor.h:80
#define MON_CLR_BP
Definition monitor.h:43
#define MON_STOP
Definition monitor.h:47
#define MON_GET_REGS
Definition monitor.h:40
#define MON_CONT
Definition monitor.h:46
#define MON_LOAD
Definition monitor.h:44
#define MON_PONG
Definition monitor.h:74
#define MON_RUN
Definition monitor.h:45
#define MON_STOP_INTERRUPT
Definition monitor.h:91
list w
Definition gentable.py:108
tuple idx
Definition gentable.py:117
int base
Definition gentable.py:114
int off
Definition mkimage.py:49
list words
Definition mktest.py:123
Definition handlers.h:33
struct HandlerInfo * next
Definition handlers.h:34
void(* handler)(int)
Definition handlers.h:35
Definition lz4stream.h:37
Definition threads.h:31
uint32_t SR
Definition threads.h:43
uint32_t Cause
Definition threads.h:44
uint32_t gp
Definition threads.h:37
uint32_t returnPC
Definition threads.h:41
__builtin_unreachable()
static int c
Definition syscalls.h:122
static const char * src
Definition syscalls.h:80
void uint32_t(classId, spec)
uint16_t val
Definition timers.c:319
uint16_t transportRecvWord(void)
Definition transport.c:159
void transportSendWord(uint16_t w)
Definition transport.c:61
int transportRecvEnd(void)
Definition transport.c:165
void transportRecvBegin(uint16_t *type, uint16_t *len)
Definition transport.c:114
void transportSendEnd(void)
Definition transport.c:66
int transportStopPending(void)
Definition transport.c:156
int transportTryRate(uint16_t reload, uint16_t pongType, uint16_t pongWord)
Definition transport.c:225
int transportHasRate(void)
Definition transport.c:224
void transportInit(void)
Definition transport.c:46
void transportSendBegin(uint16_t type, uint16_t len)
Definition transport.c:51
void transportSendFrame(uint16_t type, const uint16_t *payload, uint16_t len)
Definition transport.c:72
#define TRANSPORT_OK
Definition transport.h:41