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 (at) 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 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 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 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