1 /*- 2 * Copyright (c) 2010 Juniper Networks, Inc. 3 * Copyright (c) 2009 Robert N. M. Watson 4 * Copyright (c) 2009 Bjoern A. Zeeb <bz@FreeBSD.org> 5 * Copyright (c) 2008 Yahoo!, Inc. 6 * All rights reserved. 7 * 8 * Written by: John Baldwin <jhb@FreeBSD.org> 9 * 10 * This software was developed by Robert N. M. Watson under contract 11 * to Juniper Networks, Inc. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. Neither the name of the author nor the names of any co-contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38 #include <sys/cdefs.h> 39 __FBSDID("$FreeBSD$"); 40 41 #include <sys/param.h> 42 #include <sys/pcpu.h> 43 #include <sys/sysctl.h> 44 #include <kvm.h> 45 #include <limits.h> 46 #include <stdlib.h> 47 48 #include "kvm_private.h" 49 50 static struct nlist kvm_pcpu_nl[] = { 51 { .n_name = "_cpuid_to_pcpu" }, 52 { .n_name = "_mp_maxcpus" }, 53 { .n_name = NULL }, 54 }; 55 56 /* 57 * Kernel per-CPU data state. We cache this stuff on the first 58 * access. 59 * 60 * XXXRW: Possibly, this (and kvmpcpu_nl) should be per-kvm_t, in case the 61 * consumer has multiple handles in flight to differently configured 62 * kernels/crashdumps. 63 */ 64 static void **pcpu_data; 65 static int maxcpu; 66 67 #define NL_CPUID_TO_PCPU 0 68 #define NL_MP_MAXCPUS 1 69 70 static int 71 _kvm_pcpu_init(kvm_t *kd) 72 { 73 size_t len; 74 int max; 75 void *data; 76 77 if (kvm_nlist(kd, kvm_pcpu_nl) < 0) 78 return (-1); 79 if (kvm_pcpu_nl[NL_CPUID_TO_PCPU].n_value == 0) { 80 _kvm_err(kd, kd->program, "unable to find cpuid_to_pcpu"); 81 return (-1); 82 } 83 if (kvm_pcpu_nl[NL_MP_MAXCPUS].n_value == 0) { 84 _kvm_err(kd, kd->program, "unable to find mp_maxcpus"); 85 return (-1); 86 } 87 if (kvm_read(kd, kvm_pcpu_nl[NL_MP_MAXCPUS].n_value, &max, 88 sizeof(max)) != sizeof(max)) { 89 _kvm_err(kd, kd->program, "cannot read mp_maxcpus"); 90 return (-1); 91 } 92 len = max * sizeof(void *); 93 data = malloc(len); 94 if (data == NULL) { 95 _kvm_err(kd, kd->program, "out of memory"); 96 return (-1); 97 } 98 if (kvm_read(kd, kvm_pcpu_nl[NL_CPUID_TO_PCPU].n_value, data, len) != 99 (ssize_t)len) { 100 _kvm_err(kd, kd->program, "cannot read cpuid_to_pcpu array"); 101 free(data); 102 return (-1); 103 } 104 pcpu_data = data; 105 maxcpu = max; 106 return (0); 107 } 108 109 static void 110 _kvm_pcpu_clear(void) 111 { 112 113 maxcpu = 0; 114 free(pcpu_data); 115 pcpu_data = NULL; 116 } 117 118 void * 119 kvm_getpcpu(kvm_t *kd, int cpu) 120 { 121 char *buf; 122 123 if (kd == NULL) { 124 _kvm_pcpu_clear(); 125 return (NULL); 126 } 127 128 if (maxcpu == 0) 129 if (_kvm_pcpu_init(kd) < 0) 130 return ((void *)-1); 131 132 if (cpu >= maxcpu || pcpu_data[cpu] == NULL) 133 return (NULL); 134 135 buf = malloc(sizeof(struct pcpu)); 136 if (buf == NULL) { 137 _kvm_err(kd, kd->program, "out of memory"); 138 return ((void *)-1); 139 } 140 if (kvm_read(kd, (uintptr_t)pcpu_data[cpu], buf, 141 sizeof(struct pcpu)) != sizeof(struct pcpu)) { 142 _kvm_err(kd, kd->program, "unable to read per-CPU data"); 143 free(buf); 144 return ((void *)-1); 145 } 146 return (buf); 147 } 148 149 int 150 kvm_getmaxcpu(kvm_t *kd) 151 { 152 153 if (kd == NULL) { 154 _kvm_pcpu_clear(); 155 return (0); 156 } 157 158 if (maxcpu == 0) 159 if (_kvm_pcpu_init(kd) < 0) 160 return (-1); 161 return (maxcpu); 162 } 163 164 static int 165 _kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu, int report_error) 166 { 167 168 if (!kd->dpcpu_initialized) { 169 if (report_error) 170 _kvm_err(kd, kd->program, "%s: not initialized", 171 __func__); 172 return (-1); 173 } 174 if (cpu >= kd->dpcpu_maxcpus) { 175 if (report_error) 176 _kvm_err(kd, kd->program, "%s: CPU %u too big", 177 __func__, cpu); 178 return (-1); 179 } 180 if (kd->dpcpu_off[cpu] == 0) { 181 if (report_error) 182 _kvm_err(kd, kd->program, "%s: CPU %u not found", 183 __func__, cpu); 184 return (-1); 185 } 186 kd->dpcpu_curcpu = cpu; 187 kd->dpcpu_curoff = kd->dpcpu_off[cpu]; 188 return (0); 189 } 190 191 /* 192 * Set up libkvm to handle dynamic per-CPU memory. 193 */ 194 static int 195 _kvm_dpcpu_init(kvm_t *kd) 196 { 197 struct nlist nl[] = { 198 #define NLIST_START_SET_PCPU 0 199 { .n_name = "___start_" DPCPU_SETNAME }, 200 #define NLIST_STOP_SET_PCPU 1 201 { .n_name = "___stop_" DPCPU_SETNAME }, 202 #define NLIST_DPCPU_OFF 2 203 { .n_name = "_dpcpu_off" }, 204 #define NLIST_MP_MAXCPUS 3 205 { .n_name = "_mp_maxcpus" }, 206 { .n_name = NULL }, 207 }; 208 uintptr_t *dpcpu_off_buf; 209 size_t len; 210 u_int dpcpu_maxcpus; 211 212 /* 213 * Locate and cache locations of important symbols using the internal 214 * version of _kvm_nlist, turning off initialization to avoid 215 * recursion in case of unresolveable symbols. 216 */ 217 if (_kvm_nlist(kd, nl, 0) != 0) 218 return (-1); 219 if (kvm_read(kd, nl[NLIST_MP_MAXCPUS].n_value, &dpcpu_maxcpus, 220 sizeof(dpcpu_maxcpus)) != sizeof(dpcpu_maxcpus)) 221 return (-1); 222 len = dpcpu_maxcpus * sizeof(*dpcpu_off_buf); 223 dpcpu_off_buf = malloc(len); 224 if (dpcpu_off_buf == NULL) 225 return (-1); 226 if (kvm_read(kd, nl[NLIST_DPCPU_OFF].n_value, dpcpu_off_buf, len) != 227 (ssize_t)len) { 228 free(dpcpu_off_buf); 229 return (-1); 230 } 231 kd->dpcpu_start = nl[NLIST_START_SET_PCPU].n_value; 232 kd->dpcpu_stop = nl[NLIST_STOP_SET_PCPU].n_value; 233 kd->dpcpu_maxcpus = dpcpu_maxcpus; 234 kd->dpcpu_off = dpcpu_off_buf; 235 kd->dpcpu_initialized = 1; 236 (void)_kvm_dpcpu_setcpu(kd, 0, 0); 237 return (0); 238 } 239 240 /* 241 * Check whether the dpcpu module has been initialized sucessfully or not, 242 * initialize it if permitted. 243 */ 244 int 245 _kvm_dpcpu_initialized(kvm_t *kd, int intialize) 246 { 247 248 if (kd->dpcpu_initialized || !intialize) 249 return (kd->dpcpu_initialized); 250 251 (void)_kvm_dpcpu_init(kd); 252 253 return (kd->dpcpu_initialized); 254 } 255 256 /* 257 * Check whether the value is within the dpcpu symbol range and only if so 258 * adjust the offset relative to the current offset. 259 */ 260 uintptr_t 261 _kvm_dpcpu_validaddr(kvm_t *kd, uintptr_t value) 262 { 263 264 if (value == 0) 265 return (value); 266 267 if (!kd->dpcpu_initialized) 268 return (value); 269 270 if (value < kd->dpcpu_start || value >= kd->dpcpu_stop) 271 return (value); 272 273 return (kd->dpcpu_curoff + value); 274 } 275 276 int 277 kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu) 278 { 279 int ret; 280 281 if (!kd->dpcpu_initialized) { 282 ret = _kvm_dpcpu_init(kd); 283 if (ret != 0) { 284 _kvm_err(kd, kd->program, "%s: init failed", 285 __func__); 286 return (ret); 287 } 288 } 289 290 return (_kvm_dpcpu_setcpu(kd, cpu, 1)); 291 } 292