kvm_pcpu.c revision 223758
1193323Sed/*- 2193323Sed * Copyright (c) 2010 Juniper Networks, Inc. 3193323Sed * Copyright (c) 2009 Robert N. M. Watson 4193323Sed * Copyright (c) 2009 Bjoern A. Zeeb <bz@FreeBSD.org> 5193323Sed * Copyright (c) 2008 Yahoo!, Inc. 6193323Sed * All rights reserved. 7193323Sed * 8193323Sed * Written by: John Baldwin <jhb@FreeBSD.org> 9193323Sed * 10193323Sed * This software was developed by Robert N. M. Watson under contract 11193323Sed * to Juniper Networks, Inc. 12193323Sed * 13193323Sed * Redistribution and use in source and binary forms, with or without 14193323Sed * modification, are permitted provided that the following conditions 15193323Sed * are met: 16193323Sed * 1. Redistributions of source code must retain the above copyright 17198090Srdivacky * notice, this list of conditions and the following disclaimer. 18193323Sed * 2. Redistributions in binary form must reproduce the above copyright 19193323Sed * notice, this list of conditions and the following disclaimer in the 20193323Sed * documentation and/or other materials provided with the distribution. 21193323Sed * 3. Neither the name of the author nor the names of any co-contributors 22193323Sed * may be used to endorse or promote products derived from this software 23193323Sed * without specific prior written permission. 24193323Sed * 25193323Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 26193323Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27193323Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28193323Sed * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 29193323Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31193323Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32198090Srdivacky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33193323Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34193323Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35193323Sed * SUCH DAMAGE. 36193323Sed */ 37221337Sdim 38221337Sdim#include <sys/cdefs.h> 39193323Sed__FBSDID("$FreeBSD: head/lib/libkvm/kvm_pcpu.c 223758 2011-07-04 12:04:52Z attilio $"); 40193323Sed 41221337Sdim#include <sys/param.h> 42221337Sdim#include <sys/pcpu.h> 43198090Srdivacky#include <sys/sysctl.h> 44198090Srdivacky#include <kvm.h> 45193323Sed#include <limits.h> 46212793Sdim#include <stdlib.h> 47193323Sed 48193323Sed#include "kvm_private.h" 49193323Sed 50193323Sedstatic struct nlist kvm_pcpu_nl[] = { 51212793Sdim { .n_name = "_cpuid_to_pcpu" }, 52212793Sdim { .n_name = "_mp_maxcpus" }, 53207618Srdivacky { .n_name = NULL }, 54193323Sed}; 55193323Sed 56193323Sed/* 57193323Sed * Kernel per-CPU data state. We cache this stuff on the first 58193323Sed * access. 59193323Sed * 60193323Sed * XXXRW: Possibly, this (and kvmpcpu_nl) should be per-kvm_t, in case the 61212793Sdim * consumer has multiple handles in flight to differently configured 62212793Sdim * kernels/crashdumps. 63207618Srdivacky */ 64193323Sedstatic void **pcpu_data; 65193323Sedstatic int maxcpu; 66198090Srdivacky 67198090Srdivacky#define NL_CPUID_TO_PCPU 0 68193323Sed#define NL_MP_MAXCPUS 1 69193323Sed 70212793Sdimstatic int 71212793Sdim_kvm_pcpu_init(kvm_t *kd) 72198090Srdivacky{ 73198090Srdivacky size_t len; 74193323Sed int max; 75212793Sdim void *data; 76193323Sed 77193323Sed if (kvm_nlist(kd, kvm_pcpu_nl) < 0) 78221337Sdim return (-1); 79193323Sed if (kvm_pcpu_nl[NL_CPUID_TO_PCPU].n_value == 0) { 80212793Sdim _kvm_err(kd, kd->program, "unable to find cpuid_to_pcpu"); 81212793Sdim return (-1); 82207618Srdivacky } 83207618Srdivacky if (kvm_pcpu_nl[NL_MP_MAXCPUS].n_value == 0) { 84207618Srdivacky _kvm_err(kd, kd->program, "unable to find mp_maxcpus"); 85198090Srdivacky return (-1); 86193323Sed } 87198090Srdivacky if (kvm_read(kd, kvm_pcpu_nl[NL_MP_MAXCPUS].n_value, &max, 88198090Srdivacky sizeof(max)) != sizeof(max)) { 89193323Sed _kvm_err(kd, kd->program, "cannot read mp_maxcpus"); 90193323Sed return (-1); 91193323Sed } 92193323Sed len = max * sizeof(void *); 93198090Srdivacky data = malloc(len); 94193323Sed if (data == NULL) { 95193323Sed _kvm_err(kd, kd->program, "out of memory"); 96193323Sed return (-1); 97193323Sed } 98193323Sed if (kvm_read(kd, kvm_pcpu_nl[NL_CPUID_TO_PCPU].n_value, data, len) != 99198090Srdivacky (ssize_t)len) { 100198090Srdivacky _kvm_err(kd, kd->program, "cannot read cpuid_to_pcpu array"); 101193323Sed free(data); 102193323Sed return (-1); 103193323Sed } 104193323Sed pcpu_data = data; 105193323Sed maxcpu = max; 106193323Sed return (0); 107193323Sed} 108212793Sdim 109212793Sdimstatic void 110198090Srdivacky_kvm_pcpu_clear(void) 111198090Srdivacky{ 112193323Sed 113212793Sdim maxcpu = 0; 114193323Sed free(pcpu_data); 115193323Sed pcpu_data = NULL; 116193323Sed} 117193323Sed 118212793Sdimvoid * 119207618Srdivackykvm_getpcpu(kvm_t *kd, int cpu) 120207618Srdivacky{ 121207618Srdivacky char *buf; 122198090Srdivacky 123193323Sed if (kd == NULL) { 124193323Sed _kvm_pcpu_clear(); 125193323Sed return (NULL); 126198090Srdivacky } 127193323Sed 128193323Sed if (maxcpu == 0) 129193323Sed if (_kvm_pcpu_init(kd) < 0) 130193323Sed return ((void *)-1); 131208599Srdivacky 132208599Srdivacky if (cpu >= maxcpu || pcpu_data[cpu] == NULL) 133193323Sed return (NULL); 134198090Srdivacky 135198090Srdivacky buf = malloc(sizeof(struct pcpu)); 136193323Sed if (buf == NULL) { 137193323Sed _kvm_err(kd, kd->program, "out of memory"); 138193323Sed return ((void *)-1); 139193323Sed } 140193323Sed if (kvm_read(kd, (uintptr_t)pcpu_data[cpu], buf, 141193323Sed sizeof(struct pcpu)) != sizeof(struct pcpu)) { 142193323Sed _kvm_err(kd, kd->program, "unable to read per-CPU data"); 143193323Sed free(buf); 144198090Srdivacky return ((void *)-1); 145193323Sed } 146193323Sed return (buf); 147193323Sed} 148208599Srdivacky 149212793Sdimint 150212793Sdimkvm_getmaxcpu(kvm_t *kd) 151198090Srdivacky{ 152198090Srdivacky 153193323Sed if (kd == NULL) { 154212793Sdim _kvm_pcpu_clear(); 155193323Sed return (0); 156193323Sed } 157193323Sed 158193323Sed if (maxcpu == 0) 159212793Sdim if (_kvm_pcpu_init(kd) < 0) 160212793Sdim return (-1); 161207618Srdivacky return (maxcpu); 162207618Srdivacky} 163207618Srdivacky 164198090Srdivackystatic int 165193323Sed_kvm_dpcpu_setcpu(kvm_t *kd, u_int cpu, int report_error) 166193323Sed{ 167193323Sed 168193323Sed if (!kd->dpcpu_initialized) { 169198090Srdivacky if (report_error) 170208599Srdivacky _kvm_err(kd, kd->program, "%s: not initialized", 171208599Srdivacky __func__); 172193323Sed return (-1); 173193323Sed } 174193323Sed if (cpu >= kd->dpcpu_maxcpus) { 175193323Sed if (report_error) 176193323Sed _kvm_err(kd, kd->program, "%s: CPU %u too big", 177193323Sed __func__, cpu); 178207618Srdivacky return (-1); 179193323Sed } 180193323Sed if (kd->dpcpu_off[cpu] == 0) { 181207618Srdivacky if (report_error) 182207618Srdivacky _kvm_err(kd, kd->program, "%s: CPU %u not found", 183193323Sed __func__, cpu); 184193323Sed return (-1); 185193323Sed } 186207618Srdivacky kd->dpcpu_curcpu = cpu; 187207618Srdivacky kd->dpcpu_curoff = kd->dpcpu_off[cpu]; 188207618Srdivacky return (0); 189207618Srdivacky} 190207618Srdivacky 191207618Srdivacky/* 192207618Srdivacky * Set up libkvm to handle dynamic per-CPU memory. 193207618Srdivacky */ 194207618Srdivackystatic int 195207618Srdivacky_kvm_dpcpu_init(kvm_t *kd) 196207618Srdivacky{ 197207618Srdivacky struct nlist nl[] = { 198207618Srdivacky#define NLIST_START_SET_PCPU 0 199207618Srdivacky { .n_name = "___start_" DPCPU_SETNAME }, 200207618Srdivacky#define NLIST_STOP_SET_PCPU 1 201207618Srdivacky { .n_name = "___stop_" DPCPU_SETNAME }, 202193323Sed#define NLIST_DPCPU_OFF 2 203193323Sed { .n_name = "_dpcpu_off" }, 204193323Sed#define NLIST_MP_MAXCPUS 3 205212793Sdim { .n_name = "_mp_maxcpus" }, 206193323Sed { .n_name = NULL }, 207193323Sed }; 208193323Sed uintptr_t *dpcpu_off_buf; 209193323Sed size_t len; 210212793Sdim u_int dpcpu_maxcpus; 211212793Sdim 212212793Sdim /* 213212793Sdim * Locate and cache locations of important symbols using the internal 214212793Sdim * version of _kvm_nlist, turning off initialization to avoid 215193323Sed * recursion in case of unresolveable symbols. 216212793Sdim */ 217212793Sdim if (_kvm_nlist(kd, nl, 0) != 0) 218212793Sdim return (-1); 219193323Sed if (kvm_read(kd, nl[NLIST_MP_MAXCPUS].n_value, &dpcpu_maxcpus, 220193323Sed sizeof(dpcpu_maxcpus)) != sizeof(dpcpu_maxcpus)) 221193323Sed return (-1); 222193323Sed len = dpcpu_maxcpus * sizeof(*dpcpu_off_buf); 223193323Sed dpcpu_off_buf = malloc(len); 224193323Sed if (dpcpu_off_buf == NULL) 225226584Sdim return (-1); 226198090Srdivacky if (kvm_read(kd, nl[NLIST_DPCPU_OFF].n_value, dpcpu_off_buf, len) != 227198090Srdivacky (ssize_t)len) { 228193323Sed free(dpcpu_off_buf); 229193323Sed return (-1); 230193323Sed } 231193323Sed kd->dpcpu_start = nl[NLIST_START_SET_PCPU].n_value; 232212793Sdim kd->dpcpu_stop = nl[NLIST_STOP_SET_PCPU].n_value; 233212793Sdim kd->dpcpu_maxcpus = dpcpu_maxcpus; 234207618Srdivacky kd->dpcpu_off = dpcpu_off_buf; 235212793Sdim kd->dpcpu_initialized = 1; 236212793Sdim (void)_kvm_dpcpu_setcpu(kd, 0, 0); 237212793Sdim return (0); 238207618Srdivacky} 239212793Sdim 240193323Sed/* 241193323Sed * Check whether the dpcpu module has been initialized sucessfully or not, 242193323Sed * initialize it if permitted. 243193323Sed */ 244193323Sedint 245193323Sed_kvm_dpcpu_initialized(kvm_t *kd, int intialize) 246212793Sdim{ 247212793Sdim 248193323Sed if (kd->dpcpu_initialized || !intialize) 249193323Sed return (kd->dpcpu_initialized); 250198090Srdivacky 251198090Srdivacky (void)_kvm_dpcpu_init(kd); 252198090Srdivacky 253198090Srdivacky return (kd->dpcpu_initialized); 254193323Sed} 255198090Srdivacky 256193323Sed/* 257212793Sdim * Check whether the value is within the dpcpu symbol range and only if so 258212793Sdim * adjust the offset relative to the current offset. 259212793Sdim */ 260212793Sdimuintptr_t 261212793Sdim_kvm_dpcpu_validaddr(kvm_t *kd, uintptr_t value) 262212793Sdim{ 263212793Sdim 264218885Sdim if (value == 0) 265212793Sdim return (value); 266212793Sdim 267212793Sdim if (!kd->dpcpu_initialized) 268212793Sdim return (value); 269212793Sdim 270212793Sdim if (value < kd->dpcpu_start || value >= kd->dpcpu_stop) 271212793Sdim return (value); 272212793Sdim 273212793Sdim return (kd->dpcpu_curoff + value); 274193323Sed} 275212793Sdim 276210006Srdivackyint 277212793Sdimkvm_dpcpu_setcpu(kvm_t *kd, u_int cpu) 278210006Srdivacky{ 279193323Sed int ret; 280193323Sed 281212793Sdim if (!kd->dpcpu_initialized) { 282212793Sdim ret = _kvm_dpcpu_init(kd); 283212793Sdim if (ret != 0) { 284212793Sdim _kvm_err(kd, kd->program, "%s: init failed", 285212793Sdim __func__); 286193323Sed return (ret); 287193323Sed } 288202878Srdivacky } 289193323Sed 290193323Sed return (_kvm_dpcpu_setcpu(kd, cpu, 1)); 291193323Sed} 292202878Srdivacky