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 { "_cpuid_to_pcpu" }, 52 { "_mp_maxcpus" }, 53 { 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 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 int i; 123 124 if (kd == NULL) { 125 _kvm_pcpu_clear(); 126 return (NULL); 127 } 128 129 if (maxcpu == 0) 130 if (_kvm_pcpu_init(kd) < 0) 131 return ((void *)-1); 132 133 if (cpu >= maxcpu || pcpu_data[cpu] == NULL) 134 return (NULL); 135 136 buf = malloc(sizeof(struct pcpu)); 137 if (buf == NULL) { 138 _kvm_err(kd, kd->program, "out of memory"); 139 return ((void *)-1); 140 } 141 if (kvm_read(kd, (uintptr_t)pcpu_data[cpu], buf, sizeof(struct pcpu)) != 142 sizeof(struct pcpu)) { 143 _kvm_err(kd, kd->program, "unable to read per-CPU data"); 144 free(buf); 145 return ((void *)-1); 146 } 147 return (buf); 148 } 149 150 int 151 kvm_getmaxcpu(kvm_t *kd) 152 { 153 154 if (kd == NULL) { 155 _kvm_pcpu_clear(); 156 return (0); 157 } 158 159 if (maxcpu == 0) 160 if (_kvm_pcpu_init(kd) < 0) 161 return (-1); 162 return (maxcpu); 163 } 164 165 static int 166 _kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu, int report_error) 167 { 168 169 if (!kd->dpcpu_initialized) { 170 if (report_error) 171 _kvm_err(kd, kd->program, "%s: not initialized", 172 __func__); 173 return (-1); 174 } 175 if (cpu >= kd->dpcpu_maxcpus) { 176 if (report_error) 177 _kvm_err(kd, kd->program, "%s: CPU %u too big", 178 __func__, cpu); 179 return (-1); 180 } 181 if (kd->dpcpu_off[cpu] == 0) { 182 if (report_error) 183 _kvm_err(kd, kd->program, "%s: CPU %u not found", 184 __func__, cpu); 185 return (-1); 186 } 187 kd->dpcpu_curcpu = cpu; 188 kd->dpcpu_curoff = kd->dpcpu_off[cpu]; 189 return (0); 190 } 191 192 /* 193 * Set up libkvm to handle dynamic per-CPU memory. 194 */ 195 static int 196 _kvm_dpcpu_init(kvm_t *kd) 197 { 198 struct nlist nl[] = { 199 #define NLIST_START_SET_PCPU 0 200 { "___start_set_pcpu" }, 201 #define NLIST_STOP_SET_PCPU 1 202 { "___stop_set_pcpu" }, 203 #define NLIST_DPCPU_OFF 2 204 { "_dpcpu_off" }, 205 #define NLIST_MP_MAXCPUS 3 206 { "_mp_maxcpus" }, 207 { NULL }, 208 }; 209 uintptr_t *dpcpu_off_buf; 210 size_t len; 211 u_int dpcpu_maxcpus; 212 213 /* 214 * Locate and cache locations of important symbols using the internal 215 * version of _kvm_nlist, turning off initialization to avoid 216 * recursion in case of unresolveable symbols. 217 */ 218 if (_kvm_nlist(kd, nl, 0) != 0) 219 return (-1); 220 if (kvm_read(kd, nl[NLIST_MP_MAXCPUS].n_value, &dpcpu_maxcpus, 221 sizeof(dpcpu_maxcpus)) != sizeof(dpcpu_maxcpus)) 222 return (-1); 223 len = dpcpu_maxcpus * sizeof(*dpcpu_off_buf); 224 dpcpu_off_buf = malloc(len); 225 if (dpcpu_off_buf == NULL) 226 return (-1); 227 if (kvm_read(kd, nl[NLIST_DPCPU_OFF].n_value, dpcpu_off_buf, len) != 228 len) { 229 free(dpcpu_off_buf); 230 return (-1); 231 } 232 kd->dpcpu_start = nl[NLIST_START_SET_PCPU].n_value; 233 kd->dpcpu_stop = nl[NLIST_STOP_SET_PCPU].n_value; 234 kd->dpcpu_maxcpus = dpcpu_maxcpus; 235 kd->dpcpu_off = dpcpu_off_buf; 236 kd->dpcpu_initialized = 1; 237 (void)_kvm_dpcpu_setcpu(kd, 0, 0); 238 return (0); 239 } 240 241 /* 242 * Check whether the dpcpu module has been initialized sucessfully or not, 243 * initialize it if permitted. 244 */ 245 int 246 _kvm_dpcpu_initialized(kvm_t *kd, int intialize) 247 { 248 249 if (kd->dpcpu_initialized || !intialize) 250 return (kd->dpcpu_initialized); 251 252 (void)_kvm_dpcpu_init(kd); 253 254 return (kd->dpcpu_initialized); 255 } 256 257 /* 258 * Check whether the value is within the dpcpu symbol range and only if so 259 * adjust the offset relative to the current offset. 260 */ 261 uintptr_t 262 _kvm_dpcpu_validaddr(kvm_t *kd, uintptr_t value) 263 { 264 265 if (value == 0) 266 return (value); 267 268 if (!kd->dpcpu_initialized) 269 return (value); 270 271 if (value < kd->dpcpu_start || value >= kd->dpcpu_stop) 272 return (value); 273 274 return (kd->dpcpu_curoff + value); 275 } 276 277 int 278 kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu) 279 { 280 int ret; 281 282 if (!kd->dpcpu_initialized) { 283 ret = _kvm_dpcpu_init(kd); 284 if (ret != 0) { 285 _kvm_err(kd, kd->program, "%s: init failed", 286 __func__); 287 return (ret); 288 } 289 } 290 291 return (_kvm_dpcpu_setcpu(kd, cpu, 1)); 292 } 293