1192859Ssson/*- 2192859Ssson * Copyright (c) 2008-2009, Stacey Son <sson@freebsd.org> 3192859Ssson * All rights reserved. 4192859Ssson * 5192859Ssson * Redistribution and use in source and binary forms, with or without 6192859Ssson * modification, are permitted provided that the following conditions 7192859Ssson * are met: 8192859Ssson * 1. Redistributions of source code must retain the above copyright 9192859Ssson * notice, this list of conditions and the following disclaimer. 10192859Ssson * 2. Redistributions in binary form must reproduce the above copyright 11192859Ssson * notice, this list of conditions and the following disclaimer in the 12192859Ssson * documentation and/or other materials provided with the distribution. 13192859Ssson * 14192859Ssson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15192859Ssson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16192859Ssson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17192859Ssson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18192859Ssson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19192859Ssson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20192859Ssson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21192859Ssson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22192859Ssson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23192859Ssson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24192859Ssson * SUCH DAMAGE. 25192859Ssson * 26192859Ssson * $FreeBSD: releng/11.0/sys/dev/ksyms/ksyms.c 239303 2012-08-15 16:19:39Z hselasky $ 27192859Ssson */ 28192859Ssson 29192859Ssson#include <sys/param.h> 30192859Ssson#include <sys/systm.h> 31192859Ssson#include <sys/kernel.h> 32192859Ssson 33192859Ssson#include <sys/conf.h> 34192859Ssson#include <sys/elf.h> 35192859Ssson#include <sys/ksyms.h> 36192859Ssson#include <sys/linker.h> 37192859Ssson#include <sys/malloc.h> 38192859Ssson#include <sys/mman.h> 39192859Ssson#include <sys/module.h> 40192859Ssson#include <sys/mutex.h> 41192859Ssson#include <sys/proc.h> 42192859Ssson#include <sys/queue.h> 43192859Ssson#include <sys/resourcevar.h> 44192859Ssson#include <sys/stat.h> 45192859Ssson#include <sys/uio.h> 46192859Ssson 47192859Ssson#include <machine/elf.h> 48192859Ssson 49192859Ssson#include <vm/pmap.h> 50192859Ssson#include <vm/vm.h> 51192859Ssson#include <vm/vm_extern.h> 52192859Ssson#include <vm/vm_map.h> 53192859Ssson 54192859Ssson#include "linker_if.h" 55192859Ssson 56192859Ssson#define SHDR_NULL 0 57192859Ssson#define SHDR_SYMTAB 1 58192859Ssson#define SHDR_STRTAB 2 59192859Ssson#define SHDR_SHSTRTAB 3 60192859Ssson 61192859Ssson#define SHDR_NUM 4 62192859Ssson 63192859Ssson#define STR_SYMTAB ".symtab" 64192859Ssson#define STR_STRTAB ".strtab" 65192859Ssson#define STR_SHSTRTAB ".shstrtab" 66192859Ssson 67192859Ssson#define KSYMS_DNAME "ksyms" 68192859Ssson 69192859Sssonstatic d_open_t ksyms_open; 70192859Sssonstatic d_read_t ksyms_read; 71192859Sssonstatic d_close_t ksyms_close; 72192859Sssonstatic d_ioctl_t ksyms_ioctl; 73192859Sssonstatic d_mmap_t ksyms_mmap; 74192859Ssson 75192859Sssonstatic struct cdevsw ksyms_cdevsw = { 76192859Ssson .d_version = D_VERSION, 77226500Sed .d_flags = D_TRACKCLOSE, 78192859Ssson .d_open = ksyms_open, 79192859Ssson .d_close = ksyms_close, 80192859Ssson .d_read = ksyms_read, 81192859Ssson .d_ioctl = ksyms_ioctl, 82192859Ssson .d_mmap = ksyms_mmap, 83192859Ssson .d_name = KSYMS_DNAME 84192859Ssson}; 85192859Ssson 86192859Sssonstruct ksyms_softc { 87192859Ssson LIST_ENTRY(ksyms_softc) sc_list; 88192859Ssson vm_offset_t sc_uaddr; 89192859Ssson size_t sc_usize; 90192859Ssson pmap_t sc_pmap; 91192859Ssson struct proc *sc_proc; 92192859Ssson}; 93192859Ssson 94192859Sssonstatic struct mtx ksyms_mtx; 95192859Sssonstatic struct cdev *ksyms_dev; 96192859Sssonstatic LIST_HEAD(, ksyms_softc) ksyms_list = 97201145Santoine LIST_HEAD_INITIALIZER(ksyms_list); 98192859Ssson 99192859Sssonstatic const char ksyms_shstrtab[] = 100192859Ssson "\0" STR_SYMTAB "\0" STR_STRTAB "\0" STR_SHSTRTAB "\0"; 101192859Ssson 102192859Sssonstruct ksyms_hdr { 103192859Ssson Elf_Ehdr kh_ehdr; 104192859Ssson Elf_Phdr kh_txtphdr; 105192859Ssson Elf_Phdr kh_datphdr; 106192859Ssson Elf_Shdr kh_shdr[SHDR_NUM]; 107192859Ssson char kh_shstrtab[sizeof(ksyms_shstrtab)]; 108192859Ssson}; 109192859Ssson 110192859Sssonstruct tsizes { 111192859Ssson size_t ts_symsz; 112192859Ssson size_t ts_strsz; 113192859Ssson}; 114192859Ssson 115192859Sssonstruct toffsets { 116192859Ssson vm_offset_t to_symoff; 117192859Ssson vm_offset_t to_stroff; 118192859Ssson unsigned to_stridx; 119192859Ssson size_t to_resid; 120192859Ssson}; 121192859Ssson 122192859Sssonstatic MALLOC_DEFINE(M_KSYMS, "KSYMS", "Kernel Symbol Table"); 123192859Ssson 124192859Ssson/* 125192859Ssson * Get the symbol and string table sizes for a kernel module. Add it to the 126192859Ssson * running total. 127192859Ssson */ 128192859Sssonstatic int 129192859Sssonksyms_size_permod(linker_file_t lf, void *arg) 130192859Ssson{ 131192859Ssson struct tsizes *ts; 132194016Savg const Elf_Sym *symtab; 133192859Ssson caddr_t strtab; 134192859Ssson long syms; 135192859Ssson 136192859Ssson ts = arg; 137192859Ssson 138192859Ssson syms = LINKER_SYMTAB_GET(lf, &symtab); 139192859Ssson ts->ts_symsz += syms * sizeof(Elf_Sym); 140192859Ssson ts->ts_strsz += LINKER_STRTAB_GET(lf, &strtab); 141192859Ssson 142192859Ssson return (0); 143192859Ssson} 144192859Ssson 145192859Ssson/* 146192859Ssson * For kernel module get the symbol and string table sizes, returning the 147192859Ssson * totals in *ts. 148192859Ssson */ 149192859Sssonstatic void 150192859Sssonksyms_size_calc(struct tsizes *ts) 151192859Ssson{ 152192859Ssson ts->ts_symsz = 0; 153192859Ssson ts->ts_strsz = 0; 154192859Ssson 155192859Ssson (void) linker_file_foreach(ksyms_size_permod, ts); 156192859Ssson} 157192859Ssson 158192859Ssson#define KSYMS_EMIT(src, des, sz) do { \ 159192859Ssson copyout(src, (void *)des, sz); \ 160192859Ssson des += sz; \ 161192859Ssson } while (0) 162192859Ssson 163192859Ssson#define SYMBLKSZ 256 * sizeof (Elf_Sym) 164192859Ssson 165192859Ssson/* 166192859Ssson * For a kernel module, add the symbol and string tables into the 167192859Ssson * snapshot buffer. Fix up the offsets in the tables. 168192859Ssson */ 169192859Sssonstatic int 170192859Sssonksyms_add(linker_file_t lf, void *arg) 171192859Ssson{ 172192859Ssson struct toffsets *to; 173194016Savg const Elf_Sym *symtab; 174194016Savg Elf_Sym *symp; 175192859Ssson caddr_t strtab; 176192859Ssson long symsz; 177192859Ssson size_t strsz, numsyms; 178192859Ssson linker_symval_t symval; 179192859Ssson char *buf; 180192859Ssson int i, nsyms, len; 181192859Ssson 182192859Ssson to = arg; 183192859Ssson 184192859Ssson MOD_SLOCK; 185192859Ssson numsyms = LINKER_SYMTAB_GET(lf, &symtab); 186192859Ssson strsz = LINKER_STRTAB_GET(lf, &strtab); 187192859Ssson symsz = numsyms * sizeof(Elf_Sym); 188192859Ssson 189192859Ssson buf = malloc(SYMBLKSZ, M_KSYMS, M_WAITOK); 190192859Ssson 191192859Ssson while (symsz > 0) { 192192859Ssson len = min(SYMBLKSZ, symsz); 193192859Ssson bcopy(symtab, buf, len); 194192859Ssson 195192859Ssson /* 196192859Ssson * Fix up symbol table for kernel modules: 197192859Ssson * string offsets need adjusted 198192859Ssson * symbol values made absolute 199192859Ssson */ 200192859Ssson symp = (Elf_Sym *) buf; 201192859Ssson nsyms = len / sizeof (Elf_Sym); 202192859Ssson for (i = 0; i < nsyms; i++) { 203192859Ssson symp[i].st_name += to->to_stridx; 204192859Ssson if (lf->id > 1 && LINKER_SYMBOL_VALUES(lf, 205192859Ssson (c_linker_sym_t) &symtab[i], &symval) == 0) { 206192859Ssson symp[i].st_value = (uintptr_t) symval.value; 207192859Ssson } 208192859Ssson } 209192859Ssson 210192859Ssson if (len > to->to_resid) { 211192859Ssson MOD_SUNLOCK; 212192859Ssson free(buf, M_KSYMS); 213192859Ssson return (ENXIO); 214192859Ssson } else 215192859Ssson to->to_resid -= len; 216192859Ssson KSYMS_EMIT(buf, to->to_symoff, len); 217192859Ssson 218192859Ssson symtab += nsyms; 219192859Ssson symsz -= len; 220192859Ssson } 221192859Ssson free(buf, M_KSYMS); 222192859Ssson MOD_SUNLOCK; 223192859Ssson 224192859Ssson if (strsz > to->to_resid) 225192859Ssson return (ENXIO); 226192859Ssson else 227192859Ssson to->to_resid -= strsz; 228192859Ssson KSYMS_EMIT(strtab, to->to_stroff, strsz); 229192859Ssson to->to_stridx += strsz; 230192859Ssson 231192859Ssson return (0); 232192859Ssson} 233192859Ssson 234192859Ssson/* 235192859Ssson * Create a single ELF symbol table for the kernel and kernel modules loaded 236192859Ssson * at this time. Write this snapshot out in the process address space. Return 237192859Ssson * 0 on success, otherwise error. 238192859Ssson */ 239192859Sssonstatic int 240192859Sssonksyms_snapshot(struct tsizes *ts, vm_offset_t uaddr, size_t resid) 241192859Ssson{ 242192859Ssson 243192859Ssson struct ksyms_hdr *hdr; 244192859Ssson struct toffsets to; 245192859Ssson int error = 0; 246192859Ssson 247192859Ssson /* Be kernel stack friendly */ 248192859Ssson hdr = malloc(sizeof (*hdr), M_KSYMS, M_WAITOK|M_ZERO); 249192859Ssson 250192859Ssson /* 251192859Ssson * Create the ELF header. 252192859Ssson */ 253192859Ssson hdr->kh_ehdr.e_ident[EI_PAD] = 0; 254192859Ssson hdr->kh_ehdr.e_ident[EI_MAG0] = ELFMAG0; 255192859Ssson hdr->kh_ehdr.e_ident[EI_MAG1] = ELFMAG1; 256192859Ssson hdr->kh_ehdr.e_ident[EI_MAG2] = ELFMAG2; 257192859Ssson hdr->kh_ehdr.e_ident[EI_MAG3] = ELFMAG3; 258192859Ssson hdr->kh_ehdr.e_ident[EI_DATA] = ELF_DATA; 259192859Ssson hdr->kh_ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD; 260192859Ssson hdr->kh_ehdr.e_ident[EI_CLASS] = ELF_CLASS; 261192859Ssson hdr->kh_ehdr.e_ident[EI_VERSION] = EV_CURRENT; 262192859Ssson hdr->kh_ehdr.e_ident[EI_ABIVERSION] = 0; 263192859Ssson hdr->kh_ehdr.e_type = ET_EXEC; 264192859Ssson hdr->kh_ehdr.e_machine = ELF_ARCH; 265192859Ssson hdr->kh_ehdr.e_version = EV_CURRENT; 266192859Ssson hdr->kh_ehdr.e_entry = 0; 267192859Ssson hdr->kh_ehdr.e_phoff = offsetof(struct ksyms_hdr, kh_txtphdr); 268192859Ssson hdr->kh_ehdr.e_shoff = offsetof(struct ksyms_hdr, kh_shdr); 269192859Ssson hdr->kh_ehdr.e_flags = 0; 270192859Ssson hdr->kh_ehdr.e_ehsize = sizeof(Elf_Ehdr); 271192859Ssson hdr->kh_ehdr.e_phentsize = sizeof(Elf_Phdr); 272192859Ssson hdr->kh_ehdr.e_phnum = 2; /* Text and Data */ 273192859Ssson hdr->kh_ehdr.e_shentsize = sizeof(Elf_Shdr); 274192859Ssson hdr->kh_ehdr.e_shnum = SHDR_NUM; 275192859Ssson hdr->kh_ehdr.e_shstrndx = SHDR_SHSTRTAB; 276192859Ssson 277192859Ssson /* 278192859Ssson * Add both the text and data Program headers. 279192859Ssson */ 280192859Ssson hdr->kh_txtphdr.p_type = PT_LOAD; 281192859Ssson /* XXX - is there a way to put the actual .text addr/size here? */ 282192859Ssson hdr->kh_txtphdr.p_vaddr = 0; 283192859Ssson hdr->kh_txtphdr.p_memsz = 0; 284192859Ssson hdr->kh_txtphdr.p_flags = PF_R | PF_X; 285192859Ssson 286192859Ssson hdr->kh_datphdr.p_type = PT_LOAD; 287192859Ssson /* XXX - is there a way to put the actual .data addr/size here? */ 288192859Ssson hdr->kh_datphdr.p_vaddr = 0; 289192859Ssson hdr->kh_datphdr.p_memsz = 0; 290192859Ssson hdr->kh_datphdr.p_flags = PF_R | PF_W | PF_X; 291192859Ssson 292192859Ssson /* 293192859Ssson * Add the Section headers: null, symtab, strtab, shstrtab, 294192859Ssson */ 295192859Ssson 296192859Ssson /* First section header - null */ 297192859Ssson 298192859Ssson /* Second section header - symtab */ 299192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_name = 1; /* String offset (skip null) */ 300192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_type = SHT_SYMTAB; 301192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_flags = 0; 302192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_addr = 0; 303192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_offset = sizeof(*hdr); 304192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_size = ts->ts_symsz; 305192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_link = SHDR_STRTAB; 306192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_info = ts->ts_symsz / sizeof(Elf_Sym); 307192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_addralign = sizeof(long); 308192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_entsize = sizeof(Elf_Sym); 309192859Ssson 310192859Ssson /* Third section header - strtab */ 311192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_name = 1 + sizeof(STR_SYMTAB); 312192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_type = SHT_STRTAB; 313192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_flags = 0; 314192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_addr = 0; 315192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_offset = 316192859Ssson hdr->kh_shdr[SHDR_SYMTAB].sh_offset + ts->ts_symsz; 317192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_size = ts->ts_strsz; 318192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_link = 0; 319192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_info = 0; 320192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_addralign = sizeof(char); 321192859Ssson hdr->kh_shdr[SHDR_STRTAB].sh_entsize = 0; 322192859Ssson 323192859Ssson /* Fourth section - shstrtab */ 324192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_name = 1 + sizeof(STR_SYMTAB) + 325192859Ssson sizeof(STR_STRTAB); 326192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_type = SHT_STRTAB; 327192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_flags = 0; 328192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_addr = 0; 329192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_offset = 330192859Ssson offsetof(struct ksyms_hdr, kh_shstrtab); 331192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_size = sizeof(ksyms_shstrtab); 332192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_link = 0; 333192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_info = 0; 334192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_addralign = 0 /* sizeof(char) */; 335192859Ssson hdr->kh_shdr[SHDR_SHSTRTAB].sh_entsize = 0; 336192859Ssson 337192859Ssson /* Copy shstrtab into the header */ 338192859Ssson bcopy(ksyms_shstrtab, hdr->kh_shstrtab, sizeof(ksyms_shstrtab)); 339192859Ssson 340192859Ssson to.to_symoff = uaddr + hdr->kh_shdr[SHDR_SYMTAB].sh_offset; 341192859Ssson to.to_stroff = uaddr + hdr->kh_shdr[SHDR_STRTAB].sh_offset; 342192859Ssson to.to_stridx = 0; 343192859Ssson if (sizeof(struct ksyms_hdr) > resid) { 344192859Ssson free(hdr, M_KSYMS); 345192859Ssson return (ENXIO); 346192859Ssson } 347192859Ssson to.to_resid = resid - sizeof(struct ksyms_hdr); 348192859Ssson 349192859Ssson /* Emit Header */ 350192859Ssson copyout(hdr, (void *)uaddr, sizeof(struct ksyms_hdr)); 351192859Ssson 352192859Ssson free(hdr, M_KSYMS); 353192859Ssson 354192859Ssson /* Add symbol and string tables for each kernelmodule */ 355192859Ssson error = linker_file_foreach(ksyms_add, &to); 356192859Ssson 357192859Ssson if (to.to_resid != 0) 358192859Ssson return (ENXIO); 359192859Ssson 360192859Ssson return (error); 361192859Ssson} 362192859Ssson 363192859Sssonstatic void 364192859Sssonksyms_cdevpriv_dtr(void *data) 365192859Ssson{ 366192859Ssson struct ksyms_softc *sc; 367192859Ssson 368192859Ssson sc = (struct ksyms_softc *)data; 369192859Ssson 370192859Ssson mtx_lock(&ksyms_mtx); 371192859Ssson LIST_REMOVE(sc, sc_list); 372192859Ssson mtx_unlock(&ksyms_mtx); 373192859Ssson free(sc, M_KSYMS); 374192859Ssson} 375192859Ssson 376192859Ssson/* ARGSUSED */ 377192859Sssonstatic int 378192859Sssonksyms_open(struct cdev *dev, int flags, int fmt __unused, struct thread *td) 379192859Ssson{ 380192859Ssson struct tsizes ts; 381192859Ssson size_t total_elf_sz; 382192859Ssson int error, try; 383192859Ssson struct ksyms_softc *sc; 384192859Ssson 385192859Ssson /* 386192859Ssson * Limit one open() per process. The process must close() 387192859Ssson * before open()'ing again. 388192859Ssson */ 389192859Ssson mtx_lock(&ksyms_mtx); 390192859Ssson LIST_FOREACH(sc, &ksyms_list, sc_list) { 391192859Ssson if (sc->sc_proc == td->td_proc) { 392192859Ssson mtx_unlock(&ksyms_mtx); 393192859Ssson return (EBUSY); 394192859Ssson } 395192859Ssson } 396192859Ssson 397192859Ssson sc = (struct ksyms_softc *) malloc(sizeof (*sc), M_KSYMS, 398192859Ssson M_NOWAIT|M_ZERO); 399192859Ssson 400192859Ssson if (sc == NULL) { 401192859Ssson mtx_unlock(&ksyms_mtx); 402192859Ssson return (ENOMEM); 403192859Ssson } 404192859Ssson sc->sc_proc = td->td_proc; 405192859Ssson sc->sc_pmap = &td->td_proc->p_vmspace->vm_pmap; 406192859Ssson LIST_INSERT_HEAD(&ksyms_list, sc, sc_list); 407192859Ssson mtx_unlock(&ksyms_mtx); 408192859Ssson 409192859Ssson error = devfs_set_cdevpriv(sc, ksyms_cdevpriv_dtr); 410192859Ssson if (error) 411192859Ssson goto failed; 412192859Ssson 413192859Ssson /* 414192859Ssson * MOD_SLOCK doesn't work here (because of a lock reversal with 415192859Ssson * KLD_SLOCK). Therefore, simply try upto 3 times to get a "clean" 416192859Ssson * snapshot of the kernel symbol table. This should work fine in the 417192859Ssson * rare case of a kernel module being loaded/unloaded at the same 418192859Ssson * time. 419192859Ssson */ 420192859Ssson for(try = 0; try < 3; try++) { 421192859Ssson /* 422192859Ssson * Map a buffer in the calling process memory space and 423192859Ssson * create a snapshot of the kernel symbol table in it. 424192859Ssson */ 425192859Ssson 426192859Ssson /* Compute the size of buffer needed. */ 427192859Ssson ksyms_size_calc(&ts); 428192859Ssson total_elf_sz = sizeof(struct ksyms_hdr) + ts.ts_symsz + 429192859Ssson ts.ts_strsz; 430192859Ssson 431220100Skib error = copyout_map(td, &(sc->sc_uaddr), 432192859Ssson (vm_size_t) total_elf_sz); 433192859Ssson if (error) 434192859Ssson break; 435192859Ssson sc->sc_usize = total_elf_sz; 436192859Ssson 437192859Ssson error = ksyms_snapshot(&ts, sc->sc_uaddr, total_elf_sz); 438192859Ssson if (!error) { 439192859Ssson /* Successful Snapshot */ 440192859Ssson return (0); 441192859Ssson } 442192859Ssson 443192859Ssson /* Snapshot failed, unmap the memory and try again */ 444220100Skib (void) copyout_unmap(td, sc->sc_uaddr, sc->sc_usize); 445192859Ssson } 446192859Ssson 447192859Sssonfailed: 448192859Ssson ksyms_cdevpriv_dtr(sc); 449192859Ssson return (error); 450192859Ssson} 451192859Ssson 452192859Ssson/* ARGSUSED */ 453192859Sssonstatic int 454192859Sssonksyms_read(struct cdev *dev, struct uio *uio, int flags __unused) 455192859Ssson{ 456192859Ssson int error; 457192859Ssson size_t len, sz; 458192859Ssson struct ksyms_softc *sc; 459192859Ssson off_t off; 460192859Ssson char *buf; 461192859Ssson vm_size_t ubase; 462192859Ssson 463192859Ssson error = devfs_get_cdevpriv((void **)&sc); 464192859Ssson if (error) 465192859Ssson return (error); 466192859Ssson 467192859Ssson off = uio->uio_offset; 468192859Ssson len = uio->uio_resid; 469192859Ssson 470192859Ssson if (off < 0 || off > sc->sc_usize) 471192859Ssson return (EFAULT); 472192859Ssson 473192859Ssson if (len > (sc->sc_usize - off)) 474192859Ssson len = sc->sc_usize - off; 475192859Ssson 476192859Ssson if (len == 0) 477192859Ssson return (0); 478192859Ssson 479192859Ssson /* 480192859Ssson * Since the snapshot buffer is in the user space we have to copy it 481192859Ssson * in to the kernel and then back out. The extra copy saves valuable 482192859Ssson * kernel memory. 483192859Ssson */ 484192859Ssson buf = malloc(PAGE_SIZE, M_KSYMS, M_WAITOK); 485192859Ssson ubase = sc->sc_uaddr + off; 486192859Ssson 487192859Ssson while (len) { 488192859Ssson 489192859Ssson sz = min(PAGE_SIZE, len); 490192859Ssson if (copyin((void *)ubase, buf, sz)) 491192859Ssson error = EFAULT; 492192859Ssson else 493192859Ssson error = uiomove(buf, sz, uio); 494192859Ssson 495192859Ssson if (error) 496192859Ssson break; 497192859Ssson 498192859Ssson len -= sz; 499192859Ssson ubase += sz; 500192859Ssson } 501192859Ssson free(buf, M_KSYMS); 502192859Ssson 503192859Ssson return (error); 504192859Ssson} 505192859Ssson 506192859Ssson/* ARGSUSED */ 507192859Sssonstatic int 508192859Sssonksyms_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int32_t flag __unused, 509193278Sjhb struct thread *td __unused) 510192859Ssson{ 511192859Ssson int error = 0; 512192859Ssson struct ksyms_softc *sc; 513192859Ssson 514192859Ssson error = devfs_get_cdevpriv((void **)&sc); 515192859Ssson if (error) 516192859Ssson return (error); 517192859Ssson 518192859Ssson switch (cmd) { 519192859Ssson case KIOCGSIZE: 520192859Ssson /* 521192859Ssson * Return the size (in bytes) of the symbol table 522192859Ssson * snapshot. 523192859Ssson */ 524192859Ssson *(size_t *)data = sc->sc_usize; 525192859Ssson break; 526192859Ssson 527192859Ssson case KIOCGADDR: 528192859Ssson /* 529192859Ssson * Return the address of the symbol table snapshot. 530192859Ssson * XXX - compat32 version of this? 531192859Ssson */ 532192859Ssson *(void **)data = (void *)sc->sc_uaddr; 533192859Ssson break; 534192859Ssson 535192859Ssson default: 536192859Ssson error = ENOTTY; 537192859Ssson break; 538192859Ssson } 539192859Ssson 540192859Ssson return (error); 541192859Ssson} 542192859Ssson 543192859Ssson/* ARGUSED */ 544192859Sssonstatic int 545201223Srnolandksyms_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, 546201223Srnoland int prot __unused, vm_memattr_t *memattr __unused) 547192859Ssson{ 548192859Ssson struct ksyms_softc *sc; 549192859Ssson int error; 550192859Ssson 551192859Ssson error = devfs_get_cdevpriv((void **)&sc); 552192859Ssson if (error) 553192859Ssson return (error); 554192859Ssson 555192859Ssson /* 556192859Ssson * XXX mmap() will actually map the symbol table into the process 557192859Ssson * address space again. 558192859Ssson */ 559192859Ssson if (offset > round_page(sc->sc_usize) || 560192859Ssson (*paddr = pmap_extract(sc->sc_pmap, 561192859Ssson (vm_offset_t)sc->sc_uaddr + offset)) == 0) 562192859Ssson return (-1); 563192859Ssson 564192859Ssson return (0); 565192859Ssson} 566192859Ssson 567192859Ssson/* ARGUSED */ 568192859Sssonstatic int 569192859Sssonksyms_close(struct cdev *dev, int flags __unused, int fmt __unused, 570192859Ssson struct thread *td) 571192859Ssson{ 572192859Ssson int error = 0; 573192859Ssson struct ksyms_softc *sc; 574192859Ssson 575192859Ssson error = devfs_get_cdevpriv((void **)&sc); 576192859Ssson if (error) 577192859Ssson return (error); 578192859Ssson 579192859Ssson /* Unmap the buffer from the process address space. */ 580220100Skib error = copyout_unmap(td, sc->sc_uaddr, sc->sc_usize); 581192859Ssson 582192859Ssson return (error); 583192859Ssson} 584192859Ssson 585192859Ssson/* ARGSUSED */ 586192859Sssonstatic int 587192859Sssonksyms_modevent(module_t mod __unused, int type, void *data __unused) 588192859Ssson{ 589192859Ssson int error = 0; 590192859Ssson 591192859Ssson switch (type) { 592192859Ssson case MOD_LOAD: 593192859Ssson mtx_init(&ksyms_mtx, "KSyms mtx", NULL, MTX_DEF); 594192859Ssson ksyms_dev = make_dev(&ksyms_cdevsw, 0, UID_ROOT, GID_WHEEL, 595192859Ssson 0444, KSYMS_DNAME); 596192859Ssson break; 597192859Ssson 598192859Ssson case MOD_UNLOAD: 599192859Ssson if (!LIST_EMPTY(&ksyms_list)) 600192859Ssson return (EBUSY); 601192859Ssson destroy_dev(ksyms_dev); 602192859Ssson mtx_destroy(&ksyms_mtx); 603192859Ssson break; 604192859Ssson 605192859Ssson case MOD_SHUTDOWN: 606192859Ssson break; 607192859Ssson 608192859Ssson default: 609192859Ssson error = EOPNOTSUPP; 610192859Ssson break; 611192859Ssson } 612192859Ssson return (error); 613192859Ssson} 614192859Ssson 615192859SssonDEV_MODULE(ksyms, ksyms_modevent, NULL); 616192859SssonMODULE_VERSION(ksyms, 1); 617