1 /* $NetBSD: db_memrw.c,v 1.16 2022/08/27 20:40:03 riastradh 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.16 2022/08/27 20:40:03 riastradh Exp $"); 57 58 #include <sys/param.h> 59 #include <sys/proc.h> 60 #include <sys/systm.h> 61 62 #include <machine/db_machdep.h> 63 #include <machine/pmap_private.h> 64 65 #include <x86/bootspace.h> 66 67 #include <ddb/db_access.h> 68 #include <ddb/db_output.h> 69 70 static int 71 db_validate_address(vaddr_t addr) 72 { 73 struct proc *p = curproc; 74 struct pmap *pmap; 75 76 if (!p || !p->p_vmspace || !p->p_vmspace->vm_map.pmap || 77 addr >= VM_MIN_KERNEL_ADDRESS) 78 pmap = pmap_kernel(); 79 else 80 pmap = p->p_vmspace->vm_map.pmap; 81 82 return (pmap_extract(pmap, addr, NULL) == false); 83 } 84 85 /* 86 * Read bytes from kernel address space for debugger. 87 */ 88 void __noubsan 89 db_read_bytes(vaddr_t addr, size_t size, char *data) 90 { 91 char *src; 92 93 src = (char *)addr; 94 95 if (db_validate_address((vaddr_t)src)) { 96 #ifdef DDB 97 db_printf("address %p is invalid\n", src); 98 #endif 99 return; 100 } 101 102 if (size == 8) { 103 *((uint64_t *)data) = *((uint64_t *)src); 104 return; 105 } 106 107 if (size == 4) { 108 *((uint32_t *)data) = *((uint32_t *)src); 109 return; 110 } 111 112 if (size == 2) { 113 *((uint16_t *)data) = *((uint16_t *)src); 114 return; 115 } 116 117 while (size-- > 0) { 118 if (db_validate_address((vaddr_t)src)) { 119 #ifdef DDB 120 db_printf("address %p is invalid\n", src); 121 #endif 122 return; 123 } 124 125 *data++ = *src++; 126 } 127 } 128 129 /* 130 * Write bytes somewhere in the kernel text. Make the text 131 * pages writable temporarily. 132 */ 133 static void 134 db_write_text(vaddr_t addr, size_t size, const char *data) 135 { 136 pt_entry_t *ppte, pte; 137 size_t limit; 138 char *dst; 139 140 if (size == 0) 141 return; 142 143 dst = (char *)addr; 144 145 do { 146 addr = (vaddr_t)dst; 147 /* 148 * Get the PTE for the page. 149 */ 150 ppte = kvtopte(addr); 151 pte = *ppte; 152 153 if ((pte & PTE_P) == 0) { 154 #ifdef DDB 155 db_printf(" address %p not a valid page\n", dst); 156 #endif 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 205 /* 206 * Write bytes to kernel address space for debugger. 207 */ 208 void __noubsan 209 db_write_bytes(vaddr_t addr, size_t size, const char *data) 210 { 211 extern struct bootspace bootspace; 212 char *dst; 213 size_t i; 214 215 dst = (char *)addr; 216 217 /* If any part is in kernel text or rodata, use db_write_text() */ 218 for (i = 0; i < BTSPACE_NSEGS; i++) { 219 if (bootspace.segs[i].type != BTSEG_TEXT && 220 bootspace.segs[i].type != BTSEG_RODATA) { 221 continue; 222 } 223 if (addr >= bootspace.segs[i].va && 224 addr < (bootspace.segs[i].va + bootspace.segs[i].sz)) { 225 db_write_text(addr, size, data); 226 return; 227 } 228 } 229 230 dst = (char *)addr; 231 232 if (size == 8) { 233 *((uint64_t *)dst) = *((const uint64_t *)data); 234 return; 235 } 236 237 if (size == 4) { 238 *((uint32_t *)dst) = *((const uint32_t *)data); 239 return; 240 } 241 242 if (size == 2) { 243 *((uint16_t *)dst) = *((const uint16_t *)data); 244 return; 245 } 246 247 while (size-- > 0) 248 *dst++ = *data++; 249 } 250