1 /*	$NetBSD: db_memrw.c,v 1.2 2016/05/12 06:45:16 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.2 2016/05/12 06:45:16 maxv 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 
64 #include <ddb/db_access.h>
65 
66 /*
67  * Read bytes from kernel address space for debugger.
68  */
69 void
db_read_bytes(vaddr_t addr,size_t size,char * data)70 db_read_bytes(vaddr_t addr, size_t size, char *data)
71 {
72 	char *src;
73 
74 	src = (char *)addr;
75 
76 	if (size == 8) {
77 		*((long *)data) = *((long *)src);
78 		return;
79 	}
80 
81 	if (size == 4) {
82 		*((int *)data) = *((int *)src);
83 		return;
84 	}
85 
86 	if (size == 2) {
87 		*((short *)data) = *((short *)src);
88 		return;
89 	}
90 
91 	while (size-- > 0)
92 		*data++ = *src++;
93 }
94 
95 /*
96  * Write bytes somewhere in the kernel text.  Make the text
97  * pages writable temporarily.
98  */
99 static void
db_write_text(vaddr_t addr,size_t size,const char * data)100 db_write_text(vaddr_t addr, size_t size, const char *data)
101 {
102 	pt_entry_t *ppte, pte;
103 	size_t limit;
104 	char *dst;
105 
106 	if (size == 0)
107 		return;
108 
109 	dst = (char *)addr;
110 
111 	do {
112 		addr = (vaddr_t)dst;
113 		/*
114 		 * Get the PTE for the page.
115 		 */
116 		ppte = kvtopte(addr);
117 		pte = *ppte;
118 
119 		if ((pte & PG_V) == 0) {
120 			printf(" address %p not a valid page\n", dst);
121 			return;
122 		}
123 
124 		/*
125 		 * Compute number of bytes that can be written
126 		 * with this mapping and subtract it from the
127 		 * total size.
128 		 */
129 		if (pte & PG_PS)
130 			limit = NBPD_L2 - (addr & (NBPD_L2 - 1));
131 		else
132 			limit = PAGE_SIZE - (addr & PGOFSET);
133 		if (limit > size)
134 			limit = size;
135 		size -= limit;
136 
137 		/*
138 		 * Make the kernel text page writable.
139 		 */
140 		pmap_pte_clearbits(ppte, PG_KR);
141 		pmap_pte_setbits(ppte, PG_KW);
142 		pmap_update_pg(addr);
143 
144 		/*
145 		 * MULTIPROCESSOR: no shootdown required as the PTE continues to
146 		 * map the same page and other CPUs do not need write access.
147 		 */
148 
149 		/*
150 		 * Page is now writable.  Do as much access as we
151 		 * can in this page.
152 		 */
153 		for (; limit > 0; limit--)
154 			*dst++ = *data++;
155 
156 		/*
157 		 * Turn the page back to read-only.
158 		 */
159 		pmap_pte_clearbits(ppte, PG_KW);
160 		pmap_pte_setbits(ppte, PG_KR);
161 		pmap_update_pg(addr);
162 
163 		/*
164 		 * MULTIPROCESSOR: no shootdown required as all other CPUs
165 		 * should be in CPUF_PAUSE state and will not cache the PTE
166 		 * with the write access set.
167 		 */
168 	} while (size != 0);
169 }
170 
171 /*
172  * Write bytes to kernel address space for debugger.
173  */
174 void
db_write_bytes(vaddr_t addr,size_t size,const char * data)175 db_write_bytes(vaddr_t addr, size_t size, const char *data)
176 {
177 	extern int __rodata_start;
178 	extern int __data_start;
179 	char *dst;
180 
181 	dst = (char *)addr;
182 
183 	/* If any part is in kernel text or rodata, use db_write_text() */
184 	if ((addr >= KERNBASE && addr < (vaddr_t)&__rodata_start) ||
185 	    (addr >= (vaddr_t)&__rodata_start && addr < (vaddr_t)&__data_start)) {
186 		db_write_text(addr, size, data);
187 		return;
188 	}
189 
190 	dst = (char *)addr;
191 
192 	if (size == 8) {
193 		*((long *)dst) = *((const long *)data);
194 		return;
195 	}
196 
197 	if (size == 4) {
198 		*((int *)dst) = *((const int *)data);
199 		return;
200 	}
201 
202 	if (size == 2) {
203 		*((short *)dst) = *((const short *)data);
204 		return;
205 	}
206 
207 	while (size-- > 0)
208 		*dst++ = *data++;
209 }
210