xref: /freebsd-src/sys/ddb/db_watch.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
1dd3cb568SWarner Losh /*-
2796df753SPedro F. Giffuni  * SPDX-License-Identifier: MIT-CMU
3796df753SPedro F. Giffuni  *
45b81b6b3SRodney W. Grimes  * Mach Operating System
55b81b6b3SRodney W. Grimes  * Copyright (c) 1991,1990 Carnegie Mellon University
65b81b6b3SRodney W. Grimes  * All Rights Reserved.
75b81b6b3SRodney W. Grimes  *
85b81b6b3SRodney W. Grimes  * Permission to use, copy, modify and distribute this software and its
95b81b6b3SRodney W. Grimes  * documentation is hereby granted, provided that both the copyright
105b81b6b3SRodney W. Grimes  * notice and this permission notice appear in all copies of the
115b81b6b3SRodney W. Grimes  * software, derivative works or modified versions, and any portions
125b81b6b3SRodney W. Grimes  * thereof, and that both notices appear in supporting documentation.
135b81b6b3SRodney W. Grimes  *
145b81b6b3SRodney W. Grimes  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
155b81b6b3SRodney W. Grimes  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
165b81b6b3SRodney W. Grimes  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
175b81b6b3SRodney W. Grimes  *
185b81b6b3SRodney W. Grimes  * Carnegie Mellon requests users of this software to return to
195b81b6b3SRodney W. Grimes  *
205b81b6b3SRodney W. Grimes  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
215b81b6b3SRodney W. Grimes  *  School of Computer Science
225b81b6b3SRodney W. Grimes  *  Carnegie Mellon University
235b81b6b3SRodney W. Grimes  *  Pittsburgh PA 15213-3890
245b81b6b3SRodney W. Grimes  *
255b81b6b3SRodney W. Grimes  * any improvements or extensions that they make and grant Carnegie the
265b81b6b3SRodney W. Grimes  * rights to redistribute these changes.
275b81b6b3SRodney W. Grimes  */
285b81b6b3SRodney W. Grimes /*
295b81b6b3SRodney W. Grimes  * 	Author: Richard P. Draves, Carnegie Mellon University
305b81b6b3SRodney W. Grimes  *	Date:	10/90
315b81b6b3SRodney W. Grimes  */
325b81b6b3SRodney W. Grimes 
33f540b106SGarrett Wollman #include <sys/param.h>
349d81dd54SMitchell Horne #include <sys/kdb.h>
35b7aa38c1SBruce Evans #include <sys/kernel.h>
36fb919e4dSMark Murray #include <sys/lock.h>
37fb919e4dSMark Murray #include <sys/proc.h>
385ccbc3ccSBruce Evans 
39efeaf95aSDavid Greenman #include <vm/vm.h>
40efeaf95aSDavid Greenman #include <vm/pmap.h>
41efeaf95aSDavid Greenman #include <vm/vm_map.h>
425b81b6b3SRodney W. Grimes 
439d81dd54SMitchell Horne #include <machine/kdb.h>
449d81dd54SMitchell Horne 
455ccbc3ccSBruce Evans #include <ddb/ddb.h>
465b81b6b3SRodney W. Grimes #include <ddb/db_watch.h>
475b81b6b3SRodney W. Grimes 
485b81b6b3SRodney W. Grimes /*
495b81b6b3SRodney W. Grimes  * Watchpoints.
505b81b6b3SRodney W. Grimes  */
515b81b6b3SRodney W. Grimes 
52cd508278SPedro F. Giffuni static bool		db_watchpoints_inserted = true;
535b81b6b3SRodney W. Grimes 
545b81b6b3SRodney W. Grimes #define	NWATCHPOINTS	100
5525eb640dSPoul-Henning Kamp static struct db_watchpoint	db_watch_table[NWATCHPOINTS];
56f73a856dSPoul-Henning Kamp static db_watchpoint_t	db_next_free_watchpoint = &db_watch_table[0];
57f73a856dSPoul-Henning Kamp static db_watchpoint_t	db_free_watchpoints = 0;
58f73a856dSPoul-Henning Kamp static db_watchpoint_t	db_watchpoint_list = 0;
595b81b6b3SRodney W. Grimes 
6014e10f99SAlfred Perlstein static db_watchpoint_t	db_watchpoint_alloc(void);
6114e10f99SAlfred Perlstein static void		db_watchpoint_free(db_watchpoint_t watch);
6214e10f99SAlfred Perlstein static void		db_delete_watchpoint(vm_map_t map, db_addr_t addr);
63f73a856dSPoul-Henning Kamp #ifdef notused
64cd508278SPedro F. Giffuni static bool		db_find_watchpoint(vm_map_t map, db_addr_t addr,
6514e10f99SAlfred Perlstein 					db_regs_t *regs);
66f73a856dSPoul-Henning Kamp #endif
6714e10f99SAlfred Perlstein static void		db_list_watchpoints(void);
6814e10f99SAlfred Perlstein static void		db_set_watchpoint(vm_map_t map, db_addr_t addr,
6914e10f99SAlfred Perlstein 				       vm_size_t size);
70f73a856dSPoul-Henning Kamp 
7137c84183SPoul-Henning Kamp static db_watchpoint_t
db_watchpoint_alloc(void)72a41dd031SPedro F. Giffuni db_watchpoint_alloc(void)
735b81b6b3SRodney W. Grimes {
740a95ab74SPedro F. Giffuni 	db_watchpoint_t	watch;
755b81b6b3SRodney W. Grimes 
765b81b6b3SRodney W. Grimes 	if ((watch = db_free_watchpoints) != 0) {
775b81b6b3SRodney W. Grimes 	    db_free_watchpoints = watch->link;
785b81b6b3SRodney W. Grimes 	    return (watch);
795b81b6b3SRodney W. Grimes 	}
805b81b6b3SRodney W. Grimes 	if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) {
815b81b6b3SRodney W. Grimes 	    db_printf("All watchpoints used.\n");
825b81b6b3SRodney W. Grimes 	    return (0);
835b81b6b3SRodney W. Grimes 	}
845b81b6b3SRodney W. Grimes 	watch = db_next_free_watchpoint;
855b81b6b3SRodney W. Grimes 	db_next_free_watchpoint++;
865b81b6b3SRodney W. Grimes 
875b81b6b3SRodney W. Grimes 	return (watch);
885b81b6b3SRodney W. Grimes }
895b81b6b3SRodney W. Grimes 
9037c84183SPoul-Henning Kamp static void
db_watchpoint_free(db_watchpoint_t watch)91a41dd031SPedro F. Giffuni db_watchpoint_free(db_watchpoint_t watch)
925b81b6b3SRodney W. Grimes {
935b81b6b3SRodney W. Grimes 	watch->link = db_free_watchpoints;
945b81b6b3SRodney W. Grimes 	db_free_watchpoints = watch;
955b81b6b3SRodney W. Grimes }
965b81b6b3SRodney W. Grimes 
97f73a856dSPoul-Henning Kamp static void
db_set_watchpoint(vm_map_t map,db_addr_t addr,vm_size_t size)98a41dd031SPedro F. Giffuni db_set_watchpoint(vm_map_t map, db_addr_t addr, vm_size_t size)
995b81b6b3SRodney W. Grimes {
1000a95ab74SPedro F. Giffuni 	db_watchpoint_t	watch;
1015b81b6b3SRodney W. Grimes 
1025b81b6b3SRodney W. Grimes 	if (map == NULL) {
1035b81b6b3SRodney W. Grimes 	    db_printf("No map.\n");
1045b81b6b3SRodney W. Grimes 	    return;
1055b81b6b3SRodney W. Grimes 	}
1065b81b6b3SRodney W. Grimes 
1075b81b6b3SRodney W. Grimes 	/*
1085b81b6b3SRodney W. Grimes 	 *	Should we do anything fancy with overlapping regions?
1095b81b6b3SRodney W. Grimes 	 */
1105b81b6b3SRodney W. Grimes 
1115b81b6b3SRodney W. Grimes 	for (watch = db_watchpoint_list;
1125b81b6b3SRodney W. Grimes 	     watch != 0;
1135b81b6b3SRodney W. Grimes 	     watch = watch->link)
1145b81b6b3SRodney W. Grimes 	    if (db_map_equal(watch->map, map) &&
1155b81b6b3SRodney W. Grimes 		(watch->loaddr == addr) &&
1165b81b6b3SRodney W. Grimes 		(watch->hiaddr == addr+size)) {
1175b81b6b3SRodney W. Grimes 		db_printf("Already set.\n");
1185b81b6b3SRodney W. Grimes 		return;
1195b81b6b3SRodney W. Grimes 	    }
1205b81b6b3SRodney W. Grimes 
1215b81b6b3SRodney W. Grimes 	watch = db_watchpoint_alloc();
1225b81b6b3SRodney W. Grimes 	if (watch == 0) {
1235b81b6b3SRodney W. Grimes 	    db_printf("Too many watchpoints.\n");
1245b81b6b3SRodney W. Grimes 	    return;
1255b81b6b3SRodney W. Grimes 	}
1265b81b6b3SRodney W. Grimes 
1275b81b6b3SRodney W. Grimes 	watch->map = map;
1285b81b6b3SRodney W. Grimes 	watch->loaddr = addr;
1295b81b6b3SRodney W. Grimes 	watch->hiaddr = addr+size;
1305b81b6b3SRodney W. Grimes 
1315b81b6b3SRodney W. Grimes 	watch->link = db_watchpoint_list;
1325b81b6b3SRodney W. Grimes 	db_watchpoint_list = watch;
1335b81b6b3SRodney W. Grimes 
1342b490bc7SPedro F. Giffuni 	db_watchpoints_inserted = false;
1355b81b6b3SRodney W. Grimes }
1365b81b6b3SRodney W. Grimes 
137f73a856dSPoul-Henning Kamp static void
db_delete_watchpoint(vm_map_t map,db_addr_t addr)138a41dd031SPedro F. Giffuni db_delete_watchpoint(vm_map_t map, db_addr_t addr)
1395b81b6b3SRodney W. Grimes {
1400a95ab74SPedro F. Giffuni 	db_watchpoint_t	watch;
1410a95ab74SPedro F. Giffuni 	db_watchpoint_t	*prev;
1425b81b6b3SRodney W. Grimes 
1435b81b6b3SRodney W. Grimes 	for (prev = &db_watchpoint_list;
1445b81b6b3SRodney W. Grimes 	     (watch = *prev) != 0;
1455b81b6b3SRodney W. Grimes 	     prev = &watch->link)
1465b81b6b3SRodney W. Grimes 	    if (db_map_equal(watch->map, map) &&
1475b81b6b3SRodney W. Grimes 		(watch->loaddr <= addr) &&
1485b81b6b3SRodney W. Grimes 		(addr < watch->hiaddr)) {
1495b81b6b3SRodney W. Grimes 		*prev = watch->link;
1505b81b6b3SRodney W. Grimes 		db_watchpoint_free(watch);
1515b81b6b3SRodney W. Grimes 		return;
1525b81b6b3SRodney W. Grimes 	    }
1535b81b6b3SRodney W. Grimes 
1545b81b6b3SRodney W. Grimes 	db_printf("Not set.\n");
1555b81b6b3SRodney W. Grimes }
1565b81b6b3SRodney W. Grimes 
157f73a856dSPoul-Henning Kamp static void
db_list_watchpoints(void)158a41dd031SPedro F. Giffuni db_list_watchpoints(void)
1595b81b6b3SRodney W. Grimes {
1600a95ab74SPedro F. Giffuni 	db_watchpoint_t	watch;
1615b81b6b3SRodney W. Grimes 
1625b81b6b3SRodney W. Grimes 	if (db_watchpoint_list == 0) {
1635b81b6b3SRodney W. Grimes 	    db_printf("No watchpoints set\n");
1645b81b6b3SRodney W. Grimes 	    return;
1655b81b6b3SRodney W. Grimes 	}
1665b81b6b3SRodney W. Grimes 
167e6337905SJohn Baldwin #ifdef __LP64__
1685b81b6b3SRodney W. Grimes 	db_printf(" Map                Address          Size\n");
169e6337905SJohn Baldwin #else
170e6337905SJohn Baldwin 	db_printf(" Map        Address  Size\n");
171e6337905SJohn Baldwin #endif
1725b81b6b3SRodney W. Grimes 	for (watch = db_watchpoint_list;
1735b81b6b3SRodney W. Grimes 	     watch != 0;
1745b81b6b3SRodney W. Grimes 	     watch = watch->link)
175e6337905SJohn Baldwin #ifdef __LP64__
176e6337905SJohn Baldwin 	    db_printf("%s%16p  %16lx  %lx\n",
177e6337905SJohn Baldwin #else
1781c6989faSPeter Wemm 	    db_printf("%s%8p  %8lx  %lx\n",
179e6337905SJohn Baldwin #endif
1805b81b6b3SRodney W. Grimes 		      db_map_current(watch->map) ? "*" : " ",
1811c6989faSPeter Wemm 		      (void *)watch->map, (long)watch->loaddr,
1821c6989faSPeter Wemm 		      (long)watch->hiaddr - (long)watch->loaddr);
1835b81b6b3SRodney W. Grimes }
1845b81b6b3SRodney W. Grimes 
1855b81b6b3SRodney W. Grimes /* Delete watchpoint */
1865b81b6b3SRodney W. Grimes /*ARGSUSED*/
1875b81b6b3SRodney W. Grimes void
db_deletewatch_cmd(db_expr_t addr,bool have_addr,db_expr_t count,char * modif)188cd508278SPedro F. Giffuni db_deletewatch_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
189a41dd031SPedro F. Giffuni    char *modif)
1905b81b6b3SRodney W. Grimes {
1915b81b6b3SRodney W. Grimes 	db_delete_watchpoint(db_map_addr(addr), addr);
1925b81b6b3SRodney W. Grimes }
1935b81b6b3SRodney W. Grimes 
1945b81b6b3SRodney W. Grimes /* Set watchpoint */
1955b81b6b3SRodney W. Grimes /*ARGSUSED*/
1965b81b6b3SRodney W. Grimes void
db_watchpoint_cmd(db_expr_t addr,bool have_addr,db_expr_t count,char * modif)197cd508278SPedro F. Giffuni db_watchpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
198a41dd031SPedro F. Giffuni    char *modif)
1995b81b6b3SRodney W. Grimes {
2005b81b6b3SRodney W. Grimes 	vm_size_t	size;
2015b81b6b3SRodney W. Grimes 	db_expr_t	value;
2025b81b6b3SRodney W. Grimes 
2035b81b6b3SRodney W. Grimes 	if (db_expression(&value))
2045b81b6b3SRodney W. Grimes 	    size = (vm_size_t) value;
2055b81b6b3SRodney W. Grimes 	else
2065b81b6b3SRodney W. Grimes 	    size = 4;
2075b81b6b3SRodney W. Grimes 	db_skip_to_eol();
2085b81b6b3SRodney W. Grimes 
2095b81b6b3SRodney W. Grimes 	db_set_watchpoint(db_map_addr(addr), addr, size);
2105b81b6b3SRodney W. Grimes }
2115b81b6b3SRodney W. Grimes 
212b7aa38c1SBruce Evans /*
213b7aa38c1SBruce Evans  * At least one non-optional show-command must be implemented using
214b7aa38c1SBruce Evans  * DB_SHOW_COMMAND() so that db_show_cmd_set gets created.  Here is one.
215b7aa38c1SBruce Evans  */
DB_SHOW_COMMAND_FLAGS(watches,db_listwatch_cmd,DB_CMD_MEMSAFE)216*c84c5e00SMitchell Horne DB_SHOW_COMMAND_FLAGS(watches, db_listwatch_cmd, DB_CMD_MEMSAFE)
2175b81b6b3SRodney W. Grimes {
2185b81b6b3SRodney W. Grimes 	db_list_watchpoints();
21917bbfb58SBrian S. Dean 	db_md_list_watchpoints();
2205b81b6b3SRodney W. Grimes }
2215b81b6b3SRodney W. Grimes 
2225b81b6b3SRodney W. Grimes void
db_set_watchpoints(void)223a41dd031SPedro F. Giffuni db_set_watchpoints(void)
2245b81b6b3SRodney W. Grimes {
2250a95ab74SPedro F. Giffuni 	db_watchpoint_t	watch;
2265b81b6b3SRodney W. Grimes 
2275b81b6b3SRodney W. Grimes 	if (!db_watchpoints_inserted) {
2285b81b6b3SRodney W. Grimes 	    for (watch = db_watchpoint_list;
2295b81b6b3SRodney W. Grimes 	         watch != 0;
2305b81b6b3SRodney W. Grimes 	         watch = watch->link)
2315b81b6b3SRodney W. Grimes 		pmap_protect(watch->map->pmap,
2325b81b6b3SRodney W. Grimes 			     trunc_page(watch->loaddr),
2335b81b6b3SRodney W. Grimes 			     round_page(watch->hiaddr),
2345b81b6b3SRodney W. Grimes 			     VM_PROT_READ);
2355b81b6b3SRodney W. Grimes 
2362b490bc7SPedro F. Giffuni 	    db_watchpoints_inserted = true;
2375b81b6b3SRodney W. Grimes 	}
2385b81b6b3SRodney W. Grimes }
2395b81b6b3SRodney W. Grimes 
2405b81b6b3SRodney W. Grimes void
db_clear_watchpoints(void)241a41dd031SPedro F. Giffuni db_clear_watchpoints(void)
2425b81b6b3SRodney W. Grimes {
2432b490bc7SPedro F. Giffuni 	db_watchpoints_inserted = false;
2445b81b6b3SRodney W. Grimes }
2455b81b6b3SRodney W. Grimes 
246f73a856dSPoul-Henning Kamp #ifdef notused
247cd508278SPedro F. Giffuni static bool
db_find_watchpoint(vm_map_t map,db_addr_t addr,db_regs_t regs)248a41dd031SPedro F. Giffuni db_find_watchpoint(vm_map_t map, db_addr_t addr, db_regs_t regs)
2495b81b6b3SRodney W. Grimes {
2500a95ab74SPedro F. Giffuni 	db_watchpoint_t watch;
2515b81b6b3SRodney W. Grimes 	db_watchpoint_t found = 0;
2525b81b6b3SRodney W. Grimes 
2535b81b6b3SRodney W. Grimes 	for (watch = db_watchpoint_list;
2545b81b6b3SRodney W. Grimes 	     watch != 0;
2555b81b6b3SRodney W. Grimes 	     watch = watch->link)
2565b81b6b3SRodney W. Grimes 	    if (db_map_equal(watch->map, map)) {
2575b81b6b3SRodney W. Grimes 		if ((watch->loaddr <= addr) &&
2585b81b6b3SRodney W. Grimes 		    (addr < watch->hiaddr))
2592b490bc7SPedro F. Giffuni 		    return (true);
2605b81b6b3SRodney W. Grimes 		else if ((trunc_page(watch->loaddr) <= addr) &&
2615b81b6b3SRodney W. Grimes 			 (addr < round_page(watch->hiaddr)))
2625b81b6b3SRodney W. Grimes 		    found = watch;
2635b81b6b3SRodney W. Grimes 	    }
2645b81b6b3SRodney W. Grimes 
2655b81b6b3SRodney W. Grimes 	/*
2665b81b6b3SRodney W. Grimes 	 *	We didn't hit exactly on a watchpoint, but we are
2675b81b6b3SRodney W. Grimes 	 *	in a protected region.  We want to single-step
2685b81b6b3SRodney W. Grimes 	 *	and then re-protect.
2695b81b6b3SRodney W. Grimes 	 */
2705b81b6b3SRodney W. Grimes 
2715b81b6b3SRodney W. Grimes 	if (found) {
2722b490bc7SPedro F. Giffuni 	    db_watchpoints_inserted = false;
2735b81b6b3SRodney W. Grimes 	    db_single_step(regs);
2745b81b6b3SRodney W. Grimes 	}
2755b81b6b3SRodney W. Grimes 
2762b490bc7SPedro F. Giffuni 	return (false);
2775b81b6b3SRodney W. Grimes }
278f73a856dSPoul-Henning Kamp #endif
27917bbfb58SBrian S. Dean 
28017bbfb58SBrian S. Dean /* Delete hardware watchpoint */
28117bbfb58SBrian S. Dean void
db_deletehwatch_cmd(db_expr_t addr,bool have_addr,db_expr_t size,char * modif)2829d81dd54SMitchell Horne db_deletehwatch_cmd(db_expr_t addr, bool have_addr, db_expr_t size,
283a41dd031SPedro F. Giffuni    char *modif)
28417bbfb58SBrian S. Dean {
28517bbfb58SBrian S. Dean 	int rc;
28617bbfb58SBrian S. Dean 
2879d81dd54SMitchell Horne 	if (size < 0)
2889d81dd54SMitchell Horne 		size = 4;
28917bbfb58SBrian S. Dean 
2909d81dd54SMitchell Horne 	rc = kdb_cpu_clr_watchpoint((vm_offset_t)addr, (vm_size_t)size);
2919d81dd54SMitchell Horne 	switch (rc) {
2929d81dd54SMitchell Horne 	case ENXIO:
2939d81dd54SMitchell Horne 		/* Not supported, ignored. */
2949d81dd54SMitchell Horne 		break;
2959d81dd54SMitchell Horne 	case EINVAL:
2969d81dd54SMitchell Horne 		db_printf("Invalid watchpoint address or size.\n");
2979d81dd54SMitchell Horne 		break;
2989d81dd54SMitchell Horne 	default:
2999d81dd54SMitchell Horne 		if (rc != 0)
3009d81dd54SMitchell Horne 			db_printf("Hardware watchpoint could not be deleted, "
3019d81dd54SMitchell Horne 			    "status=%d\n", rc);
3029d81dd54SMitchell Horne 		break;
3039d81dd54SMitchell Horne 	}
30417bbfb58SBrian S. Dean }
30517bbfb58SBrian S. Dean 
30617bbfb58SBrian S. Dean /* Set hardware watchpoint */
30717bbfb58SBrian S. Dean void
db_hwatchpoint_cmd(db_expr_t addr,bool have_addr,db_expr_t size,char * modif)3089d81dd54SMitchell Horne db_hwatchpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t size,
309a41dd031SPedro F. Giffuni    char *modif)
31017bbfb58SBrian S. Dean {
31117bbfb58SBrian S. Dean 	int rc;
31217bbfb58SBrian S. Dean 
3139d81dd54SMitchell Horne 	if (size < 0)
3149d81dd54SMitchell Horne 		size = 4;
31517bbfb58SBrian S. Dean 
3169d81dd54SMitchell Horne 	rc = kdb_cpu_set_watchpoint((vm_offset_t)addr, (vm_size_t)size,
3179d81dd54SMitchell Horne 	    KDB_DBG_ACCESS_W);
3189d81dd54SMitchell Horne 
3199d81dd54SMitchell Horne 	switch (rc) {
3209d81dd54SMitchell Horne 	case EINVAL:
3219d81dd54SMitchell Horne 		db_printf("Invalid watchpoint size or address.\n");
3229d81dd54SMitchell Horne 		break;
3239d81dd54SMitchell Horne 	case EBUSY:
3249d81dd54SMitchell Horne 		db_printf("No hardware watchpoints available.\n");
3259d81dd54SMitchell Horne 		break;
3269d81dd54SMitchell Horne 	case ENXIO:
3279d81dd54SMitchell Horne 		db_printf("Hardware watchpoints are not supported on this platform.\n");
3289d81dd54SMitchell Horne 		break;
3299d81dd54SMitchell Horne 	default:
3309d81dd54SMitchell Horne 		if (rc != 0)
3319d81dd54SMitchell Horne 			db_printf("Could not set hardware watchpoint, "
3329d81dd54SMitchell Horne 			    "status=%d\n", rc);
3339d81dd54SMitchell Horne 	}
33417bbfb58SBrian S. Dean }
335