watchpoint.c 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. /*
  2. * CPU watchpoints
  3. *
  4. * Copyright (c) 2003 Fabrice Bellard
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "qemu/osdep.h"
  20. #include "qemu/error-report.h"
  21. #include "exec/cputlb.h"
  22. #include "exec/target_page.h"
  23. #include "hw/core/cpu.h"
  24. /* Add a watchpoint. */
  25. int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len,
  26. int flags, CPUWatchpoint **watchpoint)
  27. {
  28. CPUWatchpoint *wp;
  29. vaddr in_page;
  30. /* forbid ranges which are empty or run off the end of the address space */
  31. if (len == 0 || (addr + len - 1) < addr) {
  32. error_report("tried to set invalid watchpoint at %"
  33. VADDR_PRIx ", len=%" VADDR_PRIu, addr, len);
  34. return -EINVAL;
  35. }
  36. wp = g_malloc(sizeof(*wp));
  37. wp->vaddr = addr;
  38. wp->len = len;
  39. wp->flags = flags;
  40. /* keep all GDB-injected watchpoints in front */
  41. if (flags & BP_GDB) {
  42. QTAILQ_INSERT_HEAD(&cpu->watchpoints, wp, entry);
  43. } else {
  44. QTAILQ_INSERT_TAIL(&cpu->watchpoints, wp, entry);
  45. }
  46. in_page = -(addr | TARGET_PAGE_MASK);
  47. if (len <= in_page) {
  48. tlb_flush_page(cpu, addr);
  49. } else {
  50. tlb_flush(cpu);
  51. }
  52. if (watchpoint) {
  53. *watchpoint = wp;
  54. }
  55. return 0;
  56. }
  57. /* Remove a specific watchpoint. */
  58. int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, vaddr len,
  59. int flags)
  60. {
  61. CPUWatchpoint *wp;
  62. QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) {
  63. if (addr == wp->vaddr && len == wp->len
  64. && flags == (wp->flags & ~BP_WATCHPOINT_HIT)) {
  65. cpu_watchpoint_remove_by_ref(cpu, wp);
  66. return 0;
  67. }
  68. }
  69. return -ENOENT;
  70. }
  71. /* Remove a specific watchpoint by reference. */
  72. void cpu_watchpoint_remove_by_ref(CPUState *cpu, CPUWatchpoint *watchpoint)
  73. {
  74. QTAILQ_REMOVE(&cpu->watchpoints, watchpoint, entry);
  75. tlb_flush_page(cpu, watchpoint->vaddr);
  76. g_free(watchpoint);
  77. }
  78. /* Remove all matching watchpoints. */
  79. void cpu_watchpoint_remove_all(CPUState *cpu, int mask)
  80. {
  81. CPUWatchpoint *wp, *next;
  82. QTAILQ_FOREACH_SAFE(wp, &cpu->watchpoints, entry, next) {
  83. if (wp->flags & mask) {
  84. cpu_watchpoint_remove_by_ref(cpu, wp);
  85. }
  86. }
  87. }