Home | History | Annotate | Line # | Download | only in sparc
      1 /*	$NetBSD: db_interface.c,v 1.98 2023/10/26 10:41:03 andvar Exp $ */
      2 
      3 /*
      4  * Mach Operating System
      5  * Copyright (c) 1991,1990 Carnegie Mellon University
      6  * All Rights Reserved.
      7  *
      8  * Permission to use, copy, modify and distribute this software and its
      9  * documentation is hereby granted, provided that both the copyright
     10  * notice and this permission notice appear in all copies of the
     11  * software, derivative works or modified versions, and any portions
     12  * thereof, and that both notices appear in supporting documentation.
     13  *
     14  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
     15  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
     16  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
     17  *
     18  * Carnegie Mellon requests users of this software to return to
     19  *
     20  *  Software Distribution Coordinator  or  Software.Distribution (at) CS.CMU.EDU
     21  *  School of Computer Science
     22  *  Carnegie Mellon University
     23  *  Pittsburgh PA 15213-3890
     24  *
     25  * any improvements or extensions that they make and grant Carnegie the
     26  * rights to redistribute these changes.
     27  *
     28  *	From: db_interface.c,v 2.4 1991/02/05 17:11:13 mrt (CMU)
     29  */
     30 
     31 /*
     32  * Interface to new debugger.
     33  */
     34 
     35 #include <sys/cdefs.h>
     36 __KERNEL_RCSID(0, "$NetBSD: db_interface.c,v 1.98 2023/10/26 10:41:03 andvar Exp $");
     37 
     38 #ifdef _KERNEL_OPT
     39 #include "opt_ddb.h"
     40 #include "opt_kgdb.h"
     41 #include "opt_multiprocessor.h"
     42 #endif
     43 
     44 #include <sys/param.h>
     45 #include <sys/proc.h>
     46 #include <sys/cpu.h>
     47 #include <sys/reboot.h>
     48 #include <sys/systm.h>
     49 #include <sys/lwp.h>
     50 
     51 #include <dev/cons.h>
     52 
     53 #include <uvm/uvm.h>
     54 
     55 #include <machine/db_machdep.h>
     56 #include <machine/locore.h>
     57 
     58 #include <ddb/db_access.h>
     59 #include <ddb/db_active.h>
     60 #include <ddb/ddbvar.h>
     61 
     62 #if defined(DDB) || defined(_KMEMUSER)
     63 #include <ddb/db_user.h>
     64 #include <ddb/db_command.h>
     65 #include <ddb/db_sym.h>
     66 #include <ddb/db_variables.h>
     67 #include <ddb/db_extern.h>
     68 #include <ddb/db_output.h>
     69 #include <ddb/db_interface.h>
     70 #endif
     71 #ifdef KGDB
     72 #include <ddb/db_interface.h>
     73 #endif
     74 
     75 #include <machine/instr.h>
     76 #if defined(_KERNEL)
     77 #include <machine/promlib.h>
     78 #endif
     79 #include <machine/ctlreg.h>
     80 #include <machine/pmap.h>
     81 
     82 #if defined(_KERNEL)
     83 #include <sparc/sparc/asm.h>
     84 
     85 #include "fb.h"
     86 
     87 /*
     88  * Read bytes from kernel address space for debugger.
     89  */
     90 void
     91 db_read_bytes(vaddr_t addr, size_t size, char *data)
     92 {
     93 	char	*src;
     94 
     95 	src = (char *)addr;
     96 	while (size-- > 0)
     97 		*data++ = *src++;
     98 }
     99 
    100 /*
    101  * Write bytes to kernel address space for debugger.
    102  */
    103 void
    104 db_write_bytes(vaddr_t addr, size_t size, const char *data)
    105 {
    106 	char	*dst;
    107 
    108 	dst = (char *)addr;
    109 	while (size-- > 0) {
    110 		if ((dst >= (char *)VM_MIN_KERNEL_ADDRESS) && (dst < etext))
    111 			pmap_writetext(dst, *data);
    112 		else
    113 			*dst = *data;
    114 		dst++, data++;
    115 	}
    116 
    117 }
    118 #endif
    119 
    120 #if defined(DDB)
    121 
    122 /*
    123  * Data and functions used by DDB only.
    124  */
    125 
    126 void
    127 cpu_Debugger(void)
    128 {
    129 	__asm("ta 0x81");
    130 	sparc_noop();	/* Force this function to allocate a stack frame */
    131 }
    132 
    133 #endif /* DDB */
    134 
    135 #if defined(DDB) || defined(_KMEMUSER)
    136 
    137 int	db_active = 0;
    138 
    139 #ifdef _KERNEL
    140 void kdb_kbd_trap(struct trapframe *);
    141 void db_prom_cmd(db_expr_t, bool, db_expr_t, const char *);
    142 void db_page_cmd(db_expr_t, bool, db_expr_t, const char *);
    143 void db_proc_cmd(db_expr_t, bool, db_expr_t, const char *);
    144 void db_dump_pcb(db_expr_t, bool, db_expr_t, const char *);
    145 #endif
    146 #ifdef MULTIPROCESSOR
    147 void db_cpu_cmd(db_expr_t, bool, db_expr_t, const char *);
    148 void db_xcall_cmd(db_expr_t, bool, db_expr_t, const char *);
    149 #endif
    150 
    151 #ifdef _KERNEL
    152 /*
    153  * Received keyboard interrupt sequence.
    154  */
    155 void
    156 kdb_kbd_trap(struct trapframe *tf)
    157 {
    158 	if (db_active == 0 && (boothowto & RB_KDB)) {
    159 		printf("\n\nkernel: keyboard interrupt\n");
    160 		kdb_trap(-1, tf);
    161 	}
    162 }
    163 #endif
    164 
    165 /* struct cpu_info of CPU being investigated */
    166 struct cpu_info *ddb_cpuinfo;
    167 
    168 #ifdef MULTIPROCESSOR
    169 
    170 #define NOCPU -1
    171 
    172 static int db_suspend_others(void);
    173 static void db_resume_others(void);
    174 void ddb_suspend(struct trapframe *);
    175 
    176 /* from cpu.c */
    177 void mp_pause_cpus_ddb(void);
    178 void mp_resume_cpus_ddb(void);
    179 
    180 __cpu_simple_lock_t db_lock;
    181 int ddb_cpu = NOCPU;
    182 
    183 static int
    184 db_suspend_others(void)
    185 {
    186 	int cpu_me = cpu_number();
    187 	int win;
    188 
    189 	__cpu_simple_lock(&db_lock);
    190 	if (ddb_cpu == NOCPU)
    191 		ddb_cpu = cpu_me;
    192 	win = (ddb_cpu == cpu_me);
    193 	__cpu_simple_unlock(&db_lock);
    194 
    195 	if (win)
    196 		mp_pause_cpus_ddb();
    197 
    198 	return win;
    199 }
    200 
    201 static void
    202 db_resume_others(void)
    203 {
    204 
    205 	mp_resume_cpus_ddb();
    206 
    207 	__cpu_simple_lock(&db_lock);
    208 	ddb_cpu = NOCPU;
    209 	__cpu_simple_unlock(&db_lock);
    210 }
    211 
    212 void
    213 ddb_suspend(struct trapframe *tf)
    214 {
    215 	volatile db_regs_t dbregs;
    216 
    217 	/* Initialise local dbregs storage from trap frame */
    218 	dbregs.db_tf = *tf;
    219 	dbregs.db_fr = *(struct frame *)tf->tf_out[6];
    220 
    221 	cpuinfo.ci_ddb_regs = &dbregs;
    222 	while (cpuinfo.flags & CPUFLG_PAUSED) /*void*/;
    223 	cpuinfo.ci_ddb_regs = NULL;
    224 }
    225 #endif /* MULTIPROCESSOR */
    226 
    227 #if defined(DDB) || defined(KGDB)
    228 /*
    229  *  kdb_trap - field a TRACE or BPT trap
    230  */
    231 int
    232 kdb_trap(int type, struct trapframe *tf)
    233 {
    234 	db_regs_t dbregs;
    235 	int s;
    236 
    237 #if NFB > 0
    238 	fb_unblank();
    239 #endif
    240 
    241 	switch (type) {
    242 	case T_BREAKPOINT:	/* breakpoint */
    243 	case -1:		/* keyboard interrupt */
    244 		break;
    245 	default:
    246 		if (!db_onpanic && db_recover==0)
    247 			return (0);
    248 
    249 		printf("kernel: %s trap\n", trap_type[type & 0xff]);
    250 		if (db_recover != 0) {
    251 			db_error("Faulted in DDB; continuing...\n");
    252 			/*NOTREACHED*/
    253 		}
    254 	}
    255 
    256 #ifdef MULTIPROCESSOR
    257 	if (!db_suspend_others()) {
    258 		ddb_suspend(tf);
    259 		return 1;
    260 	}
    261 #endif
    262 	/* Initialise local dbregs storage from trap frame */
    263 	dbregs.db_tf = *tf;
    264 	dbregs.db_fr = *(struct frame *)tf->tf_out[6];
    265 
    266 	/* Setup current CPU & reg pointers */
    267 	ddb_cpuinfo = curcpu();
    268 	curcpu()->ci_ddb_regs = ddb_regp = &dbregs;
    269 
    270 	/* Should switch to kdb`s own stack here. */
    271 
    272 	s = splhigh();
    273 	db_active++;
    274 	cnpollc(true);
    275 	db_trap(type, 0/*code*/);
    276 	cnpollc(false);
    277 	db_active--;
    278 	splx(s);
    279 
    280 	/* Update trap frame from local dbregs storage */
    281 	*(struct frame *)tf->tf_out[6] = dbregs.db_fr;
    282 	*tf = dbregs.db_tf;
    283 	curcpu()->ci_ddb_regs = ddb_regp = 0;
    284 	ddb_cpuinfo = NULL;
    285 
    286 #ifdef MULTIPROCESSOR
    287 	db_resume_others();
    288 #endif
    289 
    290 	return (1);
    291 }
    292 #endif /* DDB || KGDB */
    293 
    294 #ifdef _KERNEL
    295 void
    296 db_proc_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif)
    297 {
    298 	struct lwp *l;
    299 	struct proc *p;
    300 
    301 	l = curlwp;
    302 	if (have_addr)
    303 		l = (struct lwp *) addr;
    304 
    305 	if (l == NULL) {
    306 		db_printf("no current process\n");
    307 		return;
    308 	}
    309 
    310 	p = l->l_proc;
    311 
    312 	db_printf("LWP %p: ", l);
    313 	db_printf("PID:%d.%d CPU:%d stat:%d vmspace:%p", p->p_pid,
    314 	    l->l_lid, l->l_cpu->ci_cpuid, l->l_stat, p->p_vmspace);
    315 	if (!P_ZOMBIE(p))
    316 		db_printf(" ctx: %p cpuset %x",
    317 			  p->p_vmspace->vm_map.pmap->pm_ctx,
    318 			  p->p_vmspace->vm_map.pmap->pm_cpuset);
    319 	db_printf("\npmap:%p wchan:%p pri:%d epri:%d\n",
    320 		  p->p_vmspace->vm_map.pmap,
    321 		  l->l_wchan, l->l_priority, lwp_eprio(l));
    322 	db_printf("maxsaddr:%p ssiz:%d pg or %llxB\n",
    323 		  p->p_vmspace->vm_maxsaddr, p->p_vmspace->vm_ssize,
    324 		  (unsigned long long)ctob(p->p_vmspace->vm_ssize));
    325 	db_printf("profile timer: %lld sec %ld nsec\n",
    326 		  p->p_stats->p_timer[ITIMER_PROF].it_value.tv_sec,
    327 		  p->p_stats->p_timer[ITIMER_PROF].it_value.tv_nsec);
    328 	db_printf("pcb: %p\n", lwp_getpcb(l));
    329 	return;
    330 }
    331 
    332 void
    333 db_dump_pcb(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif)
    334 {
    335 	struct pcb *pcb;
    336 	char bits[64];
    337 	int i;
    338 
    339 	if (have_addr)
    340 		pcb = (struct pcb *) addr;
    341 	else
    342 		pcb = curcpu()->curpcb;
    343 
    344 	snprintb(bits, sizeof(bits), PSR_BITS, pcb->pcb_psr);
    345 	db_printf("pcb@%p sp:%p pc:%p psr:%s onfault:%p\nfull windows:\n",
    346 		  pcb, (void *)(long)pcb->pcb_sp, (void *)(long)pcb->pcb_pc,
    347 		  bits, (void *)pcb->pcb_onfault);
    348 
    349 	for (i=0; i<pcb->pcb_nsaved; i++) {
    350 		db_printf("win %d: at %llx local, in\n", i,
    351 			  (unsigned long long)pcb->pcb_rw[i+1].rw_in[6]);
    352 		db_printf("%16llx %16llx %16llx %16llx\n",
    353 			  (unsigned long long)pcb->pcb_rw[i].rw_local[0],
    354 			  (unsigned long long)pcb->pcb_rw[i].rw_local[1],
    355 			  (unsigned long long)pcb->pcb_rw[i].rw_local[2],
    356 			  (unsigned long long)pcb->pcb_rw[i].rw_local[3]);
    357 		db_printf("%16llx %16llx %16llx %16llx\n",
    358 			  (unsigned long long)pcb->pcb_rw[i].rw_local[4],
    359 			  (unsigned long long)pcb->pcb_rw[i].rw_local[5],
    360 			  (unsigned long long)pcb->pcb_rw[i].rw_local[6],
    361 			  (unsigned long long)pcb->pcb_rw[i].rw_local[7]);
    362 		db_printf("%16llx %16llx %16llx %16llx\n",
    363 			  (unsigned long long)pcb->pcb_rw[i].rw_in[0],
    364 			  (unsigned long long)pcb->pcb_rw[i].rw_in[1],
    365 			  (unsigned long long)pcb->pcb_rw[i].rw_in[2],
    366 			  (unsigned long long)pcb->pcb_rw[i].rw_in[3]);
    367 		db_printf("%16llx %16llx %16llx %16llx\n",
    368 			  (unsigned long long)pcb->pcb_rw[i].rw_in[4],
    369 			  (unsigned long long)pcb->pcb_rw[i].rw_in[5],
    370 			  (unsigned long long)pcb->pcb_rw[i].rw_in[6],
    371 			  (unsigned long long)pcb->pcb_rw[i].rw_in[7]);
    372 	}
    373 }
    374 
    375 void
    376 db_prom_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif)
    377 {
    378 
    379 	prom_abort();
    380 }
    381 
    382 void
    383 db_page_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif)
    384 {
    385 
    386 	if (!have_addr) {
    387 		db_printf("Need paddr for page\n");
    388 		return;
    389 	}
    390 
    391 	db_printf("pa %llx pg %p\n", (unsigned long long)addr,
    392 	    PHYS_TO_VM_PAGE(addr));
    393 }
    394 #endif /* _KERNEL */
    395 
    396 #if defined(MULTIPROCESSOR)
    397 
    398 void
    399 db_cpu_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif)
    400 {
    401 	struct cpu_info *ci;
    402 	if (!have_addr) {
    403 		cpu_debug_dump();
    404 		return;
    405 	}
    406 
    407 	if ((addr < 0) || (addr >= sparc_ncpus)) {
    408 		db_printf("%ld: CPU out of range\n", addr);
    409 		return;
    410 	}
    411 	ci = cpus[addr];
    412 	if (ci == NULL) {
    413 		db_printf("CPU %ld not configured\n", addr);
    414 		return;
    415 	}
    416 	if (ci != curcpu()) {
    417 		if (!(ci->flags & CPUFLG_PAUSED)) {
    418 			db_printf("CPU %ld not paused\n", addr);
    419 			return;
    420 		}
    421 	}
    422 	if (ci->ci_ddb_regs == 0) {
    423 		db_printf("CPU %ld has no saved regs\n", addr);
    424 		return;
    425 	}
    426 	db_printf("using CPU %ld", addr);
    427 	ddb_regp = __UNVOLATILE(ci->ci_ddb_regs);
    428 	ddb_cpuinfo = ci;
    429 }
    430 
    431 void
    432 db_xcall_cmd(db_expr_t addr, bool have_addr, db_expr_t count, const char *modif)
    433 {
    434 	cpu_xcall_dump();
    435 }
    436 
    437 #endif /* MULTIPROCESSOR */
    438 
    439 const struct db_command db_machine_command_table[] = {
    440 #ifdef _KERNEL
    441 	{ DDB_ADD_CMD("prom",	db_prom_cmd,	0,
    442 	  "Enter the Sun PROM monitor.",NULL,NULL) },
    443 	{ DDB_ADD_CMD("page",	db_page_cmd,	0,
    444 	  "Display the address of a struct vm_page given a physical address",
    445 	   "pa", "   pa:\tphysical address to look up") },
    446 	{ DDB_ADD_CMD("proc",	db_proc_cmd,	0,
    447 	  "Display some information about an LWP",
    448 	  "[addr]","   addr:\tstruct lwp address (curlwp otherwise)") },
    449 	{ DDB_ADD_CMD("pcb",	db_dump_pcb,	0,
    450 	  "Display information about a struct pcb",
    451 	  "[address]",
    452 	  "   address:\tthe struct pcb to print (curpcb otherwise)") },
    453 #endif
    454 #ifdef MULTIPROCESSOR
    455 	{ DDB_ADD_CMD("cpu",	db_cpu_cmd,	0,
    456 	  "switch to another cpu's registers", "cpu-no", NULL) },
    457 	{ DDB_ADD_CMD("xcall",	db_xcall_cmd,	0,
    458 	  "show xcall information on all cpus", NULL, NULL) },
    459 #endif
    460 	{ DDB_END_CMD },
    461 };
    462 #endif /* DDB || _KMEMUSER */
    463 
    464 /*
    465  * support for SOFTWARE_SSTEP:
    466  * return the next pc if the given branch is taken.
    467  *
    468  * note: in the case of conditional branches with annul,
    469  * this actually returns the next pc in the "not taken" path,
    470  * but in that case next_instr_address() will return the
    471  * next pc in the "taken" path.  so even tho the breakpoints
    472  * are backwards, everything will still work, and the logic is
    473  * much simpler this way.
    474  */
    475 db_addr_t
    476 db_branch_taken(int inst, db_addr_t pc, db_regs_t *regs)
    477 {
    478     union instr insn;
    479     db_addr_t npc = ddb_regp->db_tf.tf_npc;
    480 
    481     insn.i_int = inst;
    482 
    483     /*
    484      * if this is not an annulled conditional branch, the next pc is "npc".
    485      */
    486 
    487     if (insn.i_any.i_op != IOP_OP2 || insn.i_branch.i_annul != 1)
    488 	return npc;
    489 
    490     switch (insn.i_op2.i_op2) {
    491       case IOP2_Bicc:
    492       case IOP2_FBfcc:
    493       case IOP2_BPcc:
    494       case IOP2_FBPfcc:
    495       case IOP2_CBccc:
    496 	/* branch on some condition-code */
    497 	switch (insn.i_branch.i_cond)
    498 	{
    499 	  case Icc_A: /* always */
    500 	    return pc + ((inst << 10) >> 8);
    501 
    502 	  default: /* all other conditions */
    503 	    return npc + 4;
    504 	}
    505 
    506       case IOP2_BPr:
    507 	/* branch on register, always conditional */
    508 	return npc + 4;
    509 
    510       default:
    511 	/* not a branch */
    512 #ifdef _KERNEL
    513 	panic("branch_taken() on non-branch");
    514 #else
    515 	printf("branch_taken() on non-branch\n");
    516 	return 0;
    517 #endif
    518     }
    519 }
    520 
    521 bool
    522 db_inst_branch(int inst)
    523 {
    524     union instr insn;
    525 
    526     insn.i_int = inst;
    527 
    528     if (insn.i_any.i_op != IOP_OP2)
    529 	return false;
    530 
    531     switch (insn.i_op2.i_op2) {
    532       case IOP2_BPcc:
    533       case IOP2_Bicc:
    534       case IOP2_BPr:
    535       case IOP2_FBPfcc:
    536       case IOP2_FBfcc:
    537       case IOP2_CBccc:
    538 	return true;
    539 
    540       default:
    541 	return false;
    542     }
    543 }
    544 
    545 
    546 bool
    547 db_inst_call(int inst)
    548 {
    549     union instr insn;
    550 
    551     insn.i_int = inst;
    552 
    553     switch (insn.i_any.i_op) {
    554       case IOP_CALL:
    555 	return true;
    556 
    557       case IOP_reg:
    558 	return (insn.i_op3.i_op3 == IOP3_JMPL) && !db_inst_return(inst);
    559 
    560       default:
    561 	return false;
    562     }
    563 }
    564 
    565 
    566 bool
    567 db_inst_unconditional_flow_transfer(int inst)
    568 {
    569     union instr insn;
    570 
    571     insn.i_int = inst;
    572 
    573     if (db_inst_call(inst))
    574 	return true;
    575 
    576     if (insn.i_any.i_op != IOP_OP2)
    577 	return false;
    578 
    579     switch (insn.i_op2.i_op2)
    580     {
    581       case IOP2_BPcc:
    582       case IOP2_Bicc:
    583       case IOP2_FBPfcc:
    584       case IOP2_FBfcc:
    585       case IOP2_CBccc:
    586 	return insn.i_branch.i_cond == Icc_A;
    587 
    588       default:
    589 	return false;
    590     }
    591 }
    592 
    593 
    594 bool
    595 db_inst_return(int inst)
    596 {
    597 
    598     return (inst == I_JMPLri(I_G0, I_O7, 8) ||		/* ret */
    599 	    inst == I_JMPLri(I_G0, I_I7, 8));		/* retl */
    600 }
    601 
    602 bool
    603 db_inst_trap_return(int inst)
    604 {
    605     union instr insn;
    606 
    607     insn.i_int = inst;
    608 
    609     return (insn.i_any.i_op == IOP_reg &&
    610 	    insn.i_op3.i_op3 == IOP3_RETT);
    611 }
    612 
    613 
    614 int
    615 db_inst_load(int inst)
    616 {
    617     union instr insn;
    618 
    619     insn.i_int = inst;
    620 
    621     if (insn.i_any.i_op != IOP_mem)
    622 	return 0;
    623 
    624     switch (insn.i_op3.i_op3) {
    625       case IOP3_LD:
    626       case IOP3_LDUB:
    627       case IOP3_LDUH:
    628       case IOP3_LDD:
    629       case IOP3_LDSB:
    630       case IOP3_LDSH:
    631       case IOP3_LDSTUB:
    632       case IOP3_SWAP:
    633       case IOP3_LDA:
    634       case IOP3_LDUBA:
    635       case IOP3_LDUHA:
    636       case IOP3_LDDA:
    637       case IOP3_LDSBA:
    638       case IOP3_LDSHA:
    639       case IOP3_LDSTUBA:
    640       case IOP3_SWAPA:
    641       case IOP3_LDF:
    642       case IOP3_LDFSR:
    643       case IOP3_LDDF:
    644       case IOP3_LFC:
    645       case IOP3_LDCSR:
    646       case IOP3_LDDC:
    647 	return 1;
    648 
    649       default:
    650 	return 0;
    651     }
    652 }
    653 
    654 int
    655 db_inst_store(int inst)
    656 {
    657     union instr insn;
    658 
    659     insn.i_int = inst;
    660 
    661     if (insn.i_any.i_op != IOP_mem)
    662 	return 0;
    663 
    664     switch (insn.i_op3.i_op3) {
    665       case IOP3_ST:
    666       case IOP3_STB:
    667       case IOP3_STH:
    668       case IOP3_STD:
    669       case IOP3_LDSTUB:
    670       case IOP3_SWAP:
    671       case IOP3_STA:
    672       case IOP3_STBA:
    673       case IOP3_STHA:
    674       case IOP3_STDA:
    675       case IOP3_LDSTUBA:
    676       case IOP3_SWAPA:
    677       case IOP3_STF:
    678       case IOP3_STFSR:
    679       case IOP3_STDFQ:
    680       case IOP3_STDF:
    681       case IOP3_STC:
    682       case IOP3_STCSR:
    683       case IOP3_STDCQ:
    684       case IOP3_STDC:
    685 	return 1;
    686 
    687       default:
    688 	return 0;
    689     }
    690 }
    691