1139747Simp/*-
24Srgrimes * Mach Operating System
34Srgrimes * Copyright (c) 1991,1990 Carnegie Mellon University
44Srgrimes * All Rights Reserved.
58876Srgrimes *
64Srgrimes * Permission to use, copy, modify and distribute this software and its
74Srgrimes * documentation is hereby granted, provided that both the copyright
84Srgrimes * notice and this permission notice appear in all copies of the
94Srgrimes * software, derivative works or modified versions, and any portions
104Srgrimes * thereof, and that both notices appear in supporting documentation.
118876Srgrimes *
128876Srgrimes * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
134Srgrimes * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
144Srgrimes * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
158876Srgrimes *
164Srgrimes * Carnegie Mellon requests users of this software to return to
178876Srgrimes *
184Srgrimes *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
194Srgrimes *  School of Computer Science
204Srgrimes *  Carnegie Mellon University
214Srgrimes *  Pittsburgh PA 15213-3890
228876Srgrimes *
234Srgrimes * any improvements or extensions that they make and grant Carnegie the
244Srgrimes * rights to redistribute these changes.
254Srgrimes */
264Srgrimes/*
274Srgrimes * 	Author: Richard P. Draves, Carnegie Mellon University
284Srgrimes *	Date:	10/90
294Srgrimes */
304Srgrimes
31116176Sobrien#include <sys/cdefs.h>
32116176Sobrien__FBSDID("$FreeBSD$");
33116176Sobrien
342056Swollman#include <sys/param.h>
3533296Sbde#include <sys/kernel.h>
3676166Smarkm#include <sys/lock.h>
3776166Smarkm#include <sys/proc.h>
3812734Sbde
3912662Sdg#include <vm/vm.h>
4012662Sdg#include <vm/pmap.h>
4112662Sdg#include <vm/vm_map.h>
4212734Sbde
432056Swollman#include <ddb/ddb.h>
444Srgrimes#include <ddb/db_watch.h>
454Srgrimes
464Srgrimes/*
474Srgrimes * Watchpoints.
484Srgrimes */
494Srgrimes
5012515Sphkstatic boolean_t	db_watchpoints_inserted = TRUE;
514Srgrimes
524Srgrimes#define	NWATCHPOINTS	100
5312720Sphkstatic struct db_watchpoint	db_watch_table[NWATCHPOINTS];
5412515Sphkstatic db_watchpoint_t	db_next_free_watchpoint = &db_watch_table[0];
5512515Sphkstatic db_watchpoint_t	db_free_watchpoints = 0;
5612515Sphkstatic db_watchpoint_t	db_watchpoint_list = 0;
574Srgrimes
5892756Salfredstatic db_watchpoint_t	db_watchpoint_alloc(void);
5992756Salfredstatic void		db_watchpoint_free(db_watchpoint_t watch);
6092756Salfredstatic void		db_delete_watchpoint(vm_map_t map, db_addr_t addr);
6112515Sphk#ifdef notused
6292756Salfredstatic boolean_t	db_find_watchpoint(vm_map_t map, db_addr_t addr,
6392756Salfred					db_regs_t *regs);
6412515Sphk#endif
6592756Salfredstatic void		db_list_watchpoints(void);
6692756Salfredstatic void		db_set_watchpoint(vm_map_t map, db_addr_t addr,
6792756Salfred				       vm_size_t size);
6812473Sbde
69104094Sphkstatic db_watchpoint_t
70273265Spfgdb_watchpoint_alloc(void)
714Srgrimes{
724Srgrimes	register db_watchpoint_t	watch;
734Srgrimes
744Srgrimes	if ((watch = db_free_watchpoints) != 0) {
754Srgrimes	    db_free_watchpoints = watch->link;
764Srgrimes	    return (watch);
774Srgrimes	}
784Srgrimes	if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) {
794Srgrimes	    db_printf("All watchpoints used.\n");
804Srgrimes	    return (0);
814Srgrimes	}
824Srgrimes	watch = db_next_free_watchpoint;
834Srgrimes	db_next_free_watchpoint++;
844Srgrimes
854Srgrimes	return (watch);
864Srgrimes}
874Srgrimes
88104094Sphkstatic void
89273265Spfgdb_watchpoint_free(db_watchpoint_t watch)
904Srgrimes{
914Srgrimes	watch->link = db_free_watchpoints;
924Srgrimes	db_free_watchpoints = watch;
934Srgrimes}
944Srgrimes
9512515Sphkstatic void
96273265Spfgdb_set_watchpoint(vm_map_t map, db_addr_t addr, vm_size_t size)
974Srgrimes{
984Srgrimes	register db_watchpoint_t	watch;
994Srgrimes
1004Srgrimes	if (map == NULL) {
1014Srgrimes	    db_printf("No map.\n");
1024Srgrimes	    return;
1034Srgrimes	}
1044Srgrimes
1054Srgrimes	/*
1064Srgrimes	 *	Should we do anything fancy with overlapping regions?
1074Srgrimes	 */
1084Srgrimes
1094Srgrimes	for (watch = db_watchpoint_list;
1104Srgrimes	     watch != 0;
1114Srgrimes	     watch = watch->link)
1124Srgrimes	    if (db_map_equal(watch->map, map) &&
1134Srgrimes		(watch->loaddr == addr) &&
1144Srgrimes		(watch->hiaddr == addr+size)) {
1154Srgrimes		db_printf("Already set.\n");
1164Srgrimes		return;
1174Srgrimes	    }
1184Srgrimes
1194Srgrimes	watch = db_watchpoint_alloc();
1204Srgrimes	if (watch == 0) {
1214Srgrimes	    db_printf("Too many watchpoints.\n");
1224Srgrimes	    return;
1234Srgrimes	}
1244Srgrimes
1254Srgrimes	watch->map = map;
1264Srgrimes	watch->loaddr = addr;
1274Srgrimes	watch->hiaddr = addr+size;
1284Srgrimes
1294Srgrimes	watch->link = db_watchpoint_list;
1304Srgrimes	db_watchpoint_list = watch;
1314Srgrimes
1324Srgrimes	db_watchpoints_inserted = FALSE;
1334Srgrimes}
1344Srgrimes
13512515Sphkstatic void
136273265Spfgdb_delete_watchpoint(vm_map_t map, db_addr_t addr)
1374Srgrimes{
1384Srgrimes	register db_watchpoint_t	watch;
1394Srgrimes	register db_watchpoint_t	*prev;
1404Srgrimes
1414Srgrimes	for (prev = &db_watchpoint_list;
1424Srgrimes	     (watch = *prev) != 0;
1434Srgrimes	     prev = &watch->link)
1444Srgrimes	    if (db_map_equal(watch->map, map) &&
1454Srgrimes		(watch->loaddr <= addr) &&
1464Srgrimes		(addr < watch->hiaddr)) {
1474Srgrimes		*prev = watch->link;
1484Srgrimes		db_watchpoint_free(watch);
1494Srgrimes		return;
1504Srgrimes	    }
1514Srgrimes
1524Srgrimes	db_printf("Not set.\n");
1534Srgrimes}
1544Srgrimes
15512515Sphkstatic void
156273265Spfgdb_list_watchpoints(void)
1574Srgrimes{
1584Srgrimes	register db_watchpoint_t	watch;
1594Srgrimes
1604Srgrimes	if (db_watchpoint_list == 0) {
1614Srgrimes	    db_printf("No watchpoints set\n");
1624Srgrimes	    return;
1634Srgrimes	}
1644Srgrimes
165164359Sjhb#ifdef __LP64__
166164359Sjhb	db_printf(" Map                Address          Size\n");
167164359Sjhb#else
1684Srgrimes	db_printf(" Map        Address  Size\n");
169164359Sjhb#endif
1704Srgrimes	for (watch = db_watchpoint_list;
1714Srgrimes	     watch != 0;
1724Srgrimes	     watch = watch->link)
173164359Sjhb#ifdef __LP64__
174164359Sjhb	    db_printf("%s%16p  %16lx  %lx\n",
175164359Sjhb#else
17648407Speter	    db_printf("%s%8p  %8lx  %lx\n",
177164359Sjhb#endif
1784Srgrimes		      db_map_current(watch->map) ? "*" : " ",
17948407Speter		      (void *)watch->map, (long)watch->loaddr,
18048407Speter		      (long)watch->hiaddr - (long)watch->loaddr);
1814Srgrimes}
1824Srgrimes
1834Srgrimes/* Delete watchpoint */
1844Srgrimes/*ARGSUSED*/
1854Srgrimesvoid
186273265Spfgdb_deletewatch_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count,
187273265Spfg   char *modif)
1884Srgrimes{
1894Srgrimes	db_delete_watchpoint(db_map_addr(addr), addr);
1904Srgrimes}
1914Srgrimes
1924Srgrimes/* Set watchpoint */
1934Srgrimes/*ARGSUSED*/
1944Srgrimesvoid
195273265Spfgdb_watchpoint_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count,
196273265Spfg   char *modif)
1974Srgrimes{
1984Srgrimes	vm_size_t	size;
1994Srgrimes	db_expr_t	value;
2004Srgrimes
2014Srgrimes	if (db_expression(&value))
2024Srgrimes	    size = (vm_size_t) value;
2034Srgrimes	else
2044Srgrimes	    size = 4;
2054Srgrimes	db_skip_to_eol();
2064Srgrimes
2074Srgrimes	db_set_watchpoint(db_map_addr(addr), addr, size);
2084Srgrimes}
2094Srgrimes
21033296Sbde/*
21133296Sbde * At least one non-optional show-command must be implemented using
21233296Sbde * DB_SHOW_COMMAND() so that db_show_cmd_set gets created.  Here is one.
21333296Sbde */
21433296SbdeDB_SHOW_COMMAND(watches, db_listwatch_cmd)
2154Srgrimes{
2164Srgrimes	db_list_watchpoints();
21779573Sbsd	db_md_list_watchpoints();
2184Srgrimes}
2194Srgrimes
2204Srgrimesvoid
221273265Spfgdb_set_watchpoints(void)
2224Srgrimes{
2234Srgrimes	register db_watchpoint_t	watch;
2244Srgrimes
2254Srgrimes	if (!db_watchpoints_inserted) {
2264Srgrimes	    for (watch = db_watchpoint_list;
2274Srgrimes	         watch != 0;
2284Srgrimes	         watch = watch->link)
2294Srgrimes		pmap_protect(watch->map->pmap,
2304Srgrimes			     trunc_page(watch->loaddr),
2314Srgrimes			     round_page(watch->hiaddr),
2324Srgrimes			     VM_PROT_READ);
2334Srgrimes
2344Srgrimes	    db_watchpoints_inserted = TRUE;
2354Srgrimes	}
2364Srgrimes}
2374Srgrimes
2384Srgrimesvoid
239273265Spfgdb_clear_watchpoints(void)
2404Srgrimes{
2414Srgrimes	db_watchpoints_inserted = FALSE;
2424Srgrimes}
2434Srgrimes
24412515Sphk#ifdef notused
24512515Sphkstatic boolean_t
246273265Spfgdb_find_watchpoint(vm_map_t map, db_addr_t addr, db_regs_t regs)
2474Srgrimes{
2484Srgrimes	register db_watchpoint_t watch;
2494Srgrimes	db_watchpoint_t found = 0;
2504Srgrimes
2514Srgrimes	for (watch = db_watchpoint_list;
2524Srgrimes	     watch != 0;
2534Srgrimes	     watch = watch->link)
2544Srgrimes	    if (db_map_equal(watch->map, map)) {
2554Srgrimes		if ((watch->loaddr <= addr) &&
2564Srgrimes		    (addr < watch->hiaddr))
2574Srgrimes		    return (TRUE);
2584Srgrimes		else if ((trunc_page(watch->loaddr) <= addr) &&
2594Srgrimes			 (addr < round_page(watch->hiaddr)))
2604Srgrimes		    found = watch;
2614Srgrimes	    }
2624Srgrimes
2634Srgrimes	/*
2644Srgrimes	 *	We didn't hit exactly on a watchpoint, but we are
2654Srgrimes	 *	in a protected region.  We want to single-step
2664Srgrimes	 *	and then re-protect.
2674Srgrimes	 */
2684Srgrimes
2694Srgrimes	if (found) {
2704Srgrimes	    db_watchpoints_inserted = FALSE;
2714Srgrimes	    db_single_step(regs);
2724Srgrimes	}
2734Srgrimes
2744Srgrimes	return (FALSE);
2754Srgrimes}
27612515Sphk#endif
27779573Sbsd
27879573Sbsd
27979573Sbsd
28079573Sbsd/* Delete hardware watchpoint */
28179573Sbsd/*ARGSUSED*/
28279573Sbsdvoid
283273265Spfgdb_deletehwatch_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count,
284273265Spfg   char *modif)
28579573Sbsd{
28679573Sbsd	int rc;
28779573Sbsd
288273265Spfg	if (count < 0)
289273265Spfg		count = 4;
29079573Sbsd
29179573Sbsd	rc = db_md_clr_watchpoint(addr, count);
29279573Sbsd	if (rc < 0)
29379573Sbsd		db_printf("hardware watchpoint could not be deleted\n");
29479573Sbsd}
29579573Sbsd
29679573Sbsd/* Set hardware watchpoint */
29779573Sbsd/*ARGSUSED*/
29879573Sbsdvoid
299273265Spfgdb_hwatchpoint_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count,
300273265Spfg   char *modif)
30179573Sbsd{
30279573Sbsd	int rc;
30379573Sbsd
304273265Spfg	if (count < 0)
305273265Spfg		count = 4;
30679573Sbsd
30779573Sbsd	rc = db_md_set_watchpoint(addr, count);
30879573Sbsd	if (rc < 0)
30979573Sbsd		db_printf("hardware watchpoint could not be set\n");
31079573Sbsd}
311