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