1 /* $NetBSD: db_memrw.c,v 1.6 2019/11/13 09:47:37 maxv Exp $ */
2
3 /*-
4 * Copyright (c) 1996, 2000 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Gordon W. Ross and Jason R. Thorpe.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 /*
33 * Interface to the debugger for virtual memory read/write.
34 * This file is shared by DDB and KGDB, and must work even
35 * when only KGDB is included (thus no db_printf calls).
36 *
37 * To write in the text segment, we have to first make
38 * the page writable, do the write, then restore the PTE.
39 * For writes outside the text segment, and all reads,
40 * just do the access -- if it causes a fault, the debugger
41 * will recover with a longjmp to an appropriate place.
42 *
43 * ALERT! If you want to access device registers with a
44 * specific size, then the read/write functions have to
45 * make sure to do the correct sized pointer access.
46 *
47 * Modified for i386 from hp300 version by
48 * Jason R. Thorpe <thorpej@zembu.com>.
49 *
50 * Basic copy to amd64 by fvdl.
51 *
52 * i386 and amd64 merge by jym.
53 */
54
55 #include <sys/cdefs.h>
56 __KERNEL_RCSID(0, "$NetBSD: db_memrw.c,v 1.6 2019/11/13 09:47:37 maxv Exp $");
57
58 #include <sys/param.h>
59 #include <sys/proc.h>
60 #include <sys/systm.h>
61 #include <sys/mman.h>
62
63 #include <machine/pmap.h>
64 #include <machine/db_machdep.h>
65 #include <uvm/uvm_extern.h>
66
67 #include <ddb/db_access.h>
68 #include <ddb/db_output.h>
69
70 #include <machine/thunk.h>
71
72 int
db_validate_address(vaddr_t addr)73 db_validate_address(vaddr_t addr)
74 {
75 struct proc *p = curproc;
76 struct pmap *pmap;
77
78 if (!p || !p->p_vmspace || !p->p_vmspace->vm_map.pmap ||
79 addr >= VM_MIN_KERNEL_ADDRESS) {
80 /* XXX safe??? */
81 return false;
82 }
83
84 pmap = p->p_vmspace->vm_map.pmap;
85 return (pmap_extract(pmap, addr, NULL) == false);
86 }
87
88 /*
89 * Read bytes from kernel address space for debugger.
90 */
91 void
db_read_bytes(vaddr_t addr,size_t size,char * data)92 db_read_bytes(vaddr_t addr, size_t size, char *data)
93 {
94 char *src;
95
96 src = (char *)addr;
97
98 if (db_validate_address((vaddr_t)src)) {
99 printf("address %p is invalid\n", src);
100 return;
101 }
102
103 if (size == 8) {
104 *((long *)data) = *((long *)src);
105 return;
106 }
107
108 if (size == 4) {
109 *((int *)data) = *((int *)src);
110 return;
111 }
112
113 if (size == 2) {
114 *((short *)data) = *((short *)src);
115 return;
116 }
117
118 while (size-- > 0) {
119 if (db_validate_address((vaddr_t)src)) {
120 printf("address %p is invalid\n", src);
121 return;
122 }
123
124 *data++ = *src++;
125 }
126 }
127
128 /*
129 * Write bytes somewhere in the kernel text. Make the text
130 * pages writable temporarily.
131 */
132 #if 0
133 static void
134 db_write_text(vaddr_t addr, size_t size, const char *data)
135 {
136 panic("%s: implement me\n", __func__);
137
138 pt_entry_t *ppte, pte;
139 size_t limit;
140 char *dst;
141
142 if (size == 0)
143 return;
144
145 dst = (char *)addr;
146
147 do {
148 addr = (vaddr_t)dst;
149 /*
150 * Get the PTE for the page.
151 */
152 ppte = kvtopte(addr);
153 pte = *ppte;
154
155 if ((pte & PTE_P) == 0) {
156 printf(" address %p not a valid page\n", dst);
157 return;
158 }
159
160 /*
161 * Compute number of bytes that can be written
162 * with this mapping and subtract it from the
163 * total size.
164 */
165 if (pte & PTE_PS)
166 limit = NBPD_L2 - (addr & (NBPD_L2 - 1));
167 else
168 limit = PAGE_SIZE - (addr & PGOFSET);
169 if (limit > size)
170 limit = size;
171 size -= limit;
172
173 /*
174 * Make the kernel text page writable.
175 */
176 pmap_pte_setbits(ppte, PTE_W);
177 pmap_update_pg(addr);
178
179 /*
180 * MULTIPROCESSOR: no shootdown required as the PTE continues to
181 * map the same page and other CPUs do not need write access.
182 */
183
184 /*
185 * Page is now writable. Do as much access as we
186 * can in this page.
187 */
188 for (; limit > 0; limit--)
189 *dst++ = *data++;
190
191 /*
192 * Turn the page back to read-only.
193 */
194 pmap_pte_clearbits(ppte, PTE_W);
195 pmap_update_pg(addr);
196
197 /*
198 * MULTIPROCESSOR: no shootdown required as all other CPUs
199 * should be in CPUF_PAUSE state and will not cache the PTE
200 * with the write access set.
201 */
202 } while (size != 0);
203 }
204 #endif
205
206 #include <machine/thunk.h>
207 /*
208 * Write bytes to kernel address space for debugger.
209 */
210 void
db_write_bytes(vaddr_t addr,size_t size,const char * data)211 db_write_bytes(vaddr_t addr, size_t size, const char *data)
212 {
213 char *dst;
214 int ret;
215
216 dst = (char *)addr;
217 thunk_printf_debug("\n%s : %p + %d\n", __func__, dst, (int) size);
218
219 if (db_validate_address((vaddr_t)addr)) {
220 printf("address %p is invalid\n", (void *) addr);
221 return;
222 }
223
224 /*
225 * if we are in the kernel range, just allow writing by using
226 * mprotect(); Note that this needs an unprotected binary, set with
227 * `paxctl +agm netbsd`
228 */
229 if (addr > kmem_k_start) {
230 ret = thunk_mprotect((void *) trunc_page(addr), PAGE_SIZE,
231 PROT_READ | PROT_WRITE | PROT_EXEC);
232 if (ret != 0)
233 panic("please unprotect kernel binary with "
234 "`paxctl +agm netbsd`");
235 assert(ret == 0);
236 }
237
238 dst = (char *)addr;
239
240 if (size == 8) {
241 *((long *)dst) = *((const long *)data);
242 return;
243 }
244
245 if (size == 4) {
246 *((int *)dst) = *((const int *)data);
247 return;
248 }
249
250 if (size == 2) {
251 *((short *)dst) = *((const short *)data);
252 return;
253 }
254
255 while (size-- > 0)
256 *dst++ = *data++;
257 }
258