xref: /netbsd-src/sys/arch/usermode/usermode/db_memrw.c (revision 27e09b45b8832da531865670b51a207b934f2245)
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