Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/ksyms/ksyms.c
Show All 26 Lines | |||||
*/ | */ | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/conf.h> | #include <sys/conf.h> | ||||
#include <sys/elf.h> | #include <sys/elf.h> | ||||
#include <sys/ksyms.h> | |||||
#include <sys/linker.h> | #include <sys/linker.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/mman.h> | #include <sys/mman.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/mutex.h> | |||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/queue.h> | #include <sys/queue.h> | ||||
#include <sys/resourcevar.h> | #include <sys/resourcevar.h> | ||||
#include <sys/stat.h> | #include <sys/stat.h> | ||||
#include <sys/sx.h> | |||||
#include <sys/uio.h> | #include <sys/uio.h> | ||||
#include <machine/elf.h> | #include <machine/elf.h> | ||||
#include <vm/pmap.h> | #include <vm/pmap.h> | ||||
#include <vm/vm.h> | #include <vm/vm.h> | ||||
#include <vm/vm_extern.h> | #include <vm/vm_extern.h> | ||||
#include <vm/vm_map.h> | #include <vm/vm_object.h> | ||||
#include "linker_if.h" | #include "linker_if.h" | ||||
#define SHDR_NULL 0 | #define SHDR_NULL 0 | ||||
#define SHDR_SYMTAB 1 | #define SHDR_SYMTAB 1 | ||||
#define SHDR_STRTAB 2 | #define SHDR_STRTAB 2 | ||||
#define SHDR_SHSTRTAB 3 | #define SHDR_SHSTRTAB 3 | ||||
#define SHDR_NUM 4 | #define SHDR_NUM 4 | ||||
#define STR_SYMTAB ".symtab" | #define STR_SYMTAB ".symtab" | ||||
#define STR_STRTAB ".strtab" | #define STR_STRTAB ".strtab" | ||||
#define STR_SHSTRTAB ".shstrtab" | #define STR_SHSTRTAB ".shstrtab" | ||||
#define KSYMS_DNAME "ksyms" | #define KSYMS_DNAME "ksyms" | ||||
static d_open_t ksyms_open; | static d_open_t ksyms_open; | ||||
static d_read_t ksyms_read; | static d_read_t ksyms_read; | ||||
static d_close_t ksyms_close; | static d_mmap_single_t ksyms_mmap_single; | ||||
static d_ioctl_t ksyms_ioctl; | |||||
static d_mmap_t ksyms_mmap; | |||||
static struct cdevsw ksyms_cdevsw = { | static struct cdevsw ksyms_cdevsw = { | ||||
.d_version = D_VERSION, | .d_version = D_VERSION, | ||||
.d_flags = D_TRACKCLOSE, | .d_flags = D_TRACKCLOSE, | ||||
.d_open = ksyms_open, | .d_open = ksyms_open, | ||||
.d_close = ksyms_close, | |||||
.d_read = ksyms_read, | .d_read = ksyms_read, | ||||
.d_ioctl = ksyms_ioctl, | .d_mmap_single = ksyms_mmap_single, | ||||
.d_mmap = ksyms_mmap, | |||||
.d_name = KSYMS_DNAME | .d_name = KSYMS_DNAME | ||||
}; | }; | ||||
struct ksyms_softc { | struct ksyms_softc { | ||||
LIST_ENTRY(ksyms_softc) sc_list; | LIST_ENTRY(ksyms_softc) sc_list; | ||||
vm_offset_t sc_uaddr; | vm_offset_t sc_uaddr; | ||||
size_t sc_usize; | size_t sc_usize; | ||||
pmap_t sc_pmap; | vm_object_t sc_obj; | ||||
vm_size_t sc_objsz; | |||||
struct proc *sc_proc; | struct proc *sc_proc; | ||||
}; | }; | ||||
static struct mtx ksyms_mtx; | static struct sx ksyms_mtx; | ||||
static struct cdev *ksyms_dev; | static struct cdev *ksyms_dev; | ||||
static LIST_HEAD(, ksyms_softc) ksyms_list = LIST_HEAD_INITIALIZER(ksyms_list); | static LIST_HEAD(, ksyms_softc) ksyms_list = LIST_HEAD_INITIALIZER(ksyms_list); | ||||
static const char ksyms_shstrtab[] = | static const char ksyms_shstrtab[] = | ||||
"\0" STR_SYMTAB "\0" STR_STRTAB "\0" STR_SHSTRTAB "\0"; | "\0" STR_SYMTAB "\0" STR_STRTAB "\0" STR_SHSTRTAB "\0"; | ||||
struct ksyms_hdr { | struct ksyms_hdr { | ||||
Elf_Ehdr kh_ehdr; | Elf_Ehdr kh_ehdr; | ||||
Elf_Phdr kh_txtphdr; | Elf_Phdr kh_txtphdr; | ||||
Elf_Phdr kh_datphdr; | Elf_Phdr kh_datphdr; | ||||
Elf_Shdr kh_shdr[SHDR_NUM]; | Elf_Shdr kh_shdr[SHDR_NUM]; | ||||
char kh_shstrtab[sizeof(ksyms_shstrtab)]; | char kh_shstrtab[sizeof(ksyms_shstrtab)]; | ||||
}; | }; | ||||
struct tsizes { | struct tsizes { | ||||
size_t ts_symsz; | size_t ts_symsz; | ||||
size_t ts_strsz; | size_t ts_strsz; | ||||
}; | }; | ||||
struct toffsets { | struct toffsets { | ||||
struct ksyms_softc *to_sc; | |||||
vm_offset_t to_symoff; | vm_offset_t to_symoff; | ||||
vm_offset_t to_stroff; | vm_offset_t to_stroff; | ||||
unsigned to_stridx; | unsigned to_stridx; | ||||
size_t to_resid; | size_t to_resid; | ||||
}; | }; | ||||
static MALLOC_DEFINE(M_KSYMS, "KSYMS", "Kernel Symbol Table"); | static MALLOC_DEFINE(M_KSYMS, "KSYMS", "Kernel Symbol Table"); | ||||
Show All 27 Lines | |||||
{ | { | ||||
ts->ts_symsz = 0; | ts->ts_symsz = 0; | ||||
ts->ts_strsz = 0; | ts->ts_strsz = 0; | ||||
(void)linker_file_foreach(ksyms_size_permod, ts); | (void)linker_file_foreach(ksyms_size_permod, ts); | ||||
} | } | ||||
#define KSYMS_EMIT(src, des, sz) do { \ | static int | ||||
copyout(src, (void *)des, sz); \ | ksyms_emit(struct ksyms_softc *sc, void *buf, off_t off, size_t sz) | ||||
des += sz; \ | { | ||||
} while (0) | struct iovec iov; | ||||
struct uio uio; | |||||
iov.iov_base = buf; | |||||
iov.iov_len = sz; | |||||
uio.uio_iov = &iov; | |||||
uio.uio_iovcnt = 1; | |||||
uio.uio_offset = off; | |||||
uio.uio_resid = (ssize_t)sz; | |||||
uio.uio_segflg = UIO_SYSSPACE; | |||||
uio.uio_rw = UIO_WRITE; | |||||
uio.uio_td = curthread; | |||||
return (uiomove_object(sc->sc_obj, sc->sc_objsz, &uio)); | |||||
} | |||||
#define SYMBLKSZ (256 * sizeof(Elf_Sym)) | #define SYMBLKSZ (256 * sizeof(Elf_Sym)) | ||||
/* | /* | ||||
* For a kernel module, add the symbol and string tables into the | * For a kernel module, add the symbol and string tables into the | ||||
* snapshot buffer. Fix up the offsets in the tables. | * snapshot buffer. Fix up the offsets in the tables. | ||||
*/ | */ | ||||
static int | static int | ||||
ksyms_add(linker_file_t lf, void *arg) | ksyms_add(linker_file_t lf, void *arg) | ||||
{ | { | ||||
char *buf; | char *buf; | ||||
struct ksyms_softc *sc; | |||||
struct toffsets *to; | struct toffsets *to; | ||||
const Elf_Sym *symtab; | const Elf_Sym *symtab; | ||||
Elf_Sym *symp; | Elf_Sym *symp; | ||||
caddr_t strtab; | caddr_t strtab; | ||||
long symsz; | size_t len, numsyms, strsz, symsz; | ||||
size_t strsz, numsyms; | |||||
linker_symval_t symval; | linker_symval_t symval; | ||||
int i, nsyms, len; | int error, i, nsyms; | ||||
buf = malloc(SYMBLKSZ, M_KSYMS, M_WAITOK); | |||||
to = arg; | to = arg; | ||||
sc = to->to_sc; | |||||
MOD_SLOCK; | MOD_SLOCK; | ||||
numsyms = LINKER_SYMTAB_GET(lf, &symtab); | numsyms = LINKER_SYMTAB_GET(lf, &symtab); | ||||
strsz = LINKER_STRTAB_GET(lf, &strtab); | strsz = LINKER_STRTAB_GET(lf, &strtab); | ||||
symsz = numsyms * sizeof(Elf_Sym); | symsz = numsyms * sizeof(Elf_Sym); | ||||
buf = malloc(SYMBLKSZ, M_KSYMS, M_WAITOK); | |||||
while (symsz > 0) { | while (symsz > 0) { | ||||
len = min(SYMBLKSZ, symsz); | len = min(SYMBLKSZ, symsz); | ||||
bcopy(symtab, buf, len); | bcopy(symtab, buf, len); | ||||
/* | /* | ||||
* Fix up symbol table for kernel modules: | * Fix up symbol table for kernel modules: | ||||
* string offsets need adjusted | * string offsets need adjusted | ||||
* symbol values made absolute | * symbol values made absolute | ||||
Show All 9 Lines | while (symsz > 0) { | ||||
} | } | ||||
if (len > to->to_resid) { | if (len > to->to_resid) { | ||||
MOD_SUNLOCK; | MOD_SUNLOCK; | ||||
free(buf, M_KSYMS); | free(buf, M_KSYMS); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
to->to_resid -= len; | to->to_resid -= len; | ||||
KSYMS_EMIT(buf, to->to_symoff, len); | error = ksyms_emit(sc, buf, to->to_symoff, len); | ||||
to->to_symoff += len; | |||||
if (error != 0) { | |||||
MOD_SUNLOCK; | |||||
free(buf, M_KSYMS); | |||||
return (error); | |||||
} | |||||
symtab += nsyms; | symtab += nsyms; | ||||
symsz -= len; | symsz -= len; | ||||
} | } | ||||
free(buf, M_KSYMS); | free(buf, M_KSYMS); | ||||
MOD_SUNLOCK; | MOD_SUNLOCK; | ||||
if (strsz > to->to_resid) | if (strsz > to->to_resid) | ||||
return (ENXIO); | return (ENXIO); | ||||
to->to_resid -= strsz; | to->to_resid -= strsz; | ||||
KSYMS_EMIT(strtab, to->to_stroff, strsz); | error = ksyms_emit(sc, strtab, to->to_stroff, strsz); | ||||
to->to_stroff += strsz; | |||||
to->to_stridx += strsz; | to->to_stridx += strsz; | ||||
return (0); | return (error); | ||||
} | } | ||||
/* | /* | ||||
* Create a single ELF symbol table for the kernel and kernel modules loaded | * Create a single ELF symbol table for the kernel and kernel modules loaded | ||||
* at this time. Write this snapshot out in the process address space. Return | * at this time. Write this snapshot out in the process address space. Return | ||||
* 0 on success, otherwise error. | * 0 on success, otherwise error. | ||||
*/ | */ | ||||
static int | static int | ||||
ksyms_snapshot(struct tsizes *ts, vm_offset_t uaddr, size_t resid) | ksyms_snapshot(struct ksyms_softc *sc, struct tsizes *ts) | ||||
{ | { | ||||
struct ksyms_hdr *hdr; | |||||
struct toffsets to; | struct toffsets to; | ||||
int error = 0; | struct ksyms_hdr *hdr; | ||||
int error; | |||||
hdr = malloc(sizeof(*hdr), M_KSYMS, M_WAITOK | M_ZERO); | hdr = malloc(sizeof(*hdr), M_KSYMS, M_WAITOK | M_ZERO); | ||||
/* | /* | ||||
* Create the ELF header. | * Create the ELF header. | ||||
*/ | */ | ||||
hdr->kh_ehdr.e_ident[EI_PAD] = 0; | hdr->kh_ehdr.e_ident[EI_PAD] = 0; | ||||
hdr->kh_ehdr.e_ident[EI_MAG0] = ELFMAG0; | hdr->kh_ehdr.e_ident[EI_MAG0] = ELFMAG0; | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | ksyms_snapshot(struct ksyms_softc *sc, struct tsizes *ts) | ||||
hdr->kh_shdr[SHDR_SHSTRTAB].sh_link = 0; | hdr->kh_shdr[SHDR_SHSTRTAB].sh_link = 0; | ||||
hdr->kh_shdr[SHDR_SHSTRTAB].sh_info = 0; | hdr->kh_shdr[SHDR_SHSTRTAB].sh_info = 0; | ||||
hdr->kh_shdr[SHDR_SHSTRTAB].sh_addralign = 0 /* sizeof(char) */; | hdr->kh_shdr[SHDR_SHSTRTAB].sh_addralign = 0 /* sizeof(char) */; | ||||
hdr->kh_shdr[SHDR_SHSTRTAB].sh_entsize = 0; | hdr->kh_shdr[SHDR_SHSTRTAB].sh_entsize = 0; | ||||
/* Copy shstrtab into the header. */ | /* Copy shstrtab into the header. */ | ||||
bcopy(ksyms_shstrtab, hdr->kh_shstrtab, sizeof(ksyms_shstrtab)); | bcopy(ksyms_shstrtab, hdr->kh_shstrtab, sizeof(ksyms_shstrtab)); | ||||
to.to_symoff = uaddr + hdr->kh_shdr[SHDR_SYMTAB].sh_offset; | to.to_sc = sc; | ||||
to.to_stroff = uaddr + hdr->kh_shdr[SHDR_STRTAB].sh_offset; | to.to_symoff = hdr->kh_shdr[SHDR_SYMTAB].sh_offset; | ||||
to.to_stroff = hdr->kh_shdr[SHDR_STRTAB].sh_offset; | |||||
to.to_stridx = 0; | to.to_stridx = 0; | ||||
if (sizeof(struct ksyms_hdr) > resid) { | to.to_resid = sc->sc_objsz - sizeof(struct ksyms_hdr); | ||||
free(hdr, M_KSYMS); | |||||
return (ENXIO); | |||||
} | |||||
to.to_resid = resid - sizeof(struct ksyms_hdr); | |||||
/* emit header */ | /* emit header */ | ||||
copyout(hdr, (void *)uaddr, sizeof(struct ksyms_hdr)); | error = ksyms_emit(sc, hdr, 0, sizeof(*hdr)); | ||||
free(hdr, M_KSYMS); | free(hdr, M_KSYMS); | ||||
if (error != 0) | |||||
return (error); | |||||
/* Add symbol and string tables for each kernel module. */ | /* Add symbol and string tables for each kernel module. */ | ||||
error = linker_file_foreach(ksyms_add, &to); | error = linker_file_foreach(ksyms_add, &to); | ||||
if (error != 0) | |||||
return (error); | |||||
if (to.to_resid != 0) | if (to.to_resid != 0) | ||||
return (ENXIO); | return (ENXIO); | ||||
return (0); | |||||
return (error); | |||||
} | } | ||||
static void | static void | ||||
ksyms_cdevpriv_dtr(void *data) | ksyms_cdevpriv_dtr(void *data) | ||||
{ | { | ||||
struct ksyms_softc *sc; | struct ksyms_softc *sc; | ||||
vm_object_t obj; | |||||
sc = (struct ksyms_softc *)data; | sc = (struct ksyms_softc *)data; | ||||
mtx_lock(&ksyms_mtx); | sx_xlock(&ksyms_mtx); | ||||
LIST_REMOVE(sc, sc_list); | LIST_REMOVE(sc, sc_list); | ||||
mtx_unlock(&ksyms_mtx); | sx_xunlock(&ksyms_mtx); | ||||
obj = sc->sc_obj; | |||||
if (obj != NULL) | |||||
vm_object_deallocate(obj); | |||||
free(sc, M_KSYMS); | free(sc, M_KSYMS); | ||||
} | } | ||||
static int | static int | ||||
ksyms_open(struct cdev *dev, int flags, int fmt __unused, struct thread *td) | ksyms_open(struct cdev *dev, int flags, int fmt __unused, struct thread *td) | ||||
{ | { | ||||
struct tsizes ts; | struct tsizes ts; | ||||
struct ksyms_softc *sc; | struct ksyms_softc *sc; | ||||
size_t total_elf_sz; | vm_size_t elfsz; | ||||
int error, try; | int error, try; | ||||
/* | /* | ||||
* Limit one open() per process. The process must close() | * Limit one open() per process. The process must close() | ||||
* before open()'ing again. | * before open()'ing again. | ||||
*/ | */ | ||||
mtx_lock(&ksyms_mtx); | sx_xlock(&ksyms_mtx); | ||||
LIST_FOREACH(sc, &ksyms_list, sc_list) { | LIST_FOREACH(sc, &ksyms_list, sc_list) { | ||||
if (sc->sc_proc == td->td_proc) { | if (sc->sc_proc == td->td_proc) { | ||||
mtx_unlock(&ksyms_mtx); | sx_xunlock(&ksyms_mtx); | ||||
return (EBUSY); | return (EBUSY); | ||||
} | } | ||||
} | } | ||||
sc = malloc(sizeof(*sc), M_KSYMS, M_NOWAIT | M_ZERO); | sc = malloc(sizeof(*sc), M_KSYMS, M_WAITOK | M_ZERO); | ||||
if (sc == NULL) { | |||||
mtx_unlock(&ksyms_mtx); | |||||
return (ENOMEM); | |||||
} | |||||
sc->sc_proc = td->td_proc; | sc->sc_proc = td->td_proc; | ||||
sc->sc_pmap = &td->td_proc->p_vmspace->vm_pmap; | |||||
LIST_INSERT_HEAD(&ksyms_list, sc, sc_list); | LIST_INSERT_HEAD(&ksyms_list, sc, sc_list); | ||||
mtx_unlock(&ksyms_mtx); | sx_xunlock(&ksyms_mtx); | ||||
error = devfs_set_cdevpriv(sc, ksyms_cdevpriv_dtr); | error = devfs_set_cdevpriv(sc, ksyms_cdevpriv_dtr); | ||||
if (error != 0) | if (error != 0) { | ||||
goto failed; | ksyms_cdevpriv_dtr(sc); | ||||
return (error); | |||||
} | |||||
/* | /* | ||||
* MOD_SLOCK doesn't work here (because of a lock reversal with | * MOD_SLOCK doesn't work here (because of a lock reversal with | ||||
* KLD_SLOCK). Therefore, simply try up to 3 times to get a "clean" | * KLD_SLOCK). Therefore, simply try up to 3 times to get a "clean" | ||||
* snapshot of the kernel symbol table. This should work fine in the | * snapshot of the kernel symbol table. This should work fine in the | ||||
* rare case of a kernel module being loaded/unloaded at the same | * rare case of a kernel module being loaded/unloaded at the same | ||||
* time. | * time. | ||||
*/ | */ | ||||
for (try = 0; try < 3; try++) { | for (try = 0; try < 3; try++) { | ||||
/* | |||||
* Map a buffer in the calling process memory space and | |||||
* create a snapshot of the kernel symbol table in it. | |||||
*/ | |||||
/* Compute the size of buffer needed. */ | |||||
ksyms_size_calc(&ts); | ksyms_size_calc(&ts); | ||||
total_elf_sz = sizeof(struct ksyms_hdr) + ts.ts_symsz + | elfsz = sizeof(struct ksyms_hdr) + ts.ts_symsz + ts.ts_strsz; | ||||
ts.ts_strsz; | |||||
error = copyout_map(td, &sc->sc_uaddr, (vm_size_t)total_elf_sz); | sc->sc_obj = vm_object_allocate(OBJT_DEFAULT, | ||||
if (error != 0) | OFF_TO_IDX(round_page(elfsz))); | ||||
break; | sc->sc_objsz = elfsz; | ||||
sc->sc_usize = total_elf_sz; | |||||
error = ksyms_snapshot(&ts, sc->sc_uaddr, total_elf_sz); | error = ksyms_snapshot(sc, &ts); | ||||
if (error == 0) | if (error == 0) | ||||
/* successful snapshot */ | break; | ||||
return (0); | |||||
/* Snapshot failed, unmap the memory and try again. */ | vm_object_deallocate(sc->sc_obj); | ||||
(void)copyout_unmap(td, sc->sc_uaddr, sc->sc_usize); | sc->sc_obj = NULL; | ||||
} | } | ||||
failed: | |||||
ksyms_cdevpriv_dtr(sc); | |||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
ksyms_read(struct cdev *dev, struct uio *uio, int flags __unused) | ksyms_read(struct cdev *dev, struct uio *uio, int flags __unused) | ||||
{ | { | ||||
struct ksyms_softc *sc; | struct ksyms_softc *sc; | ||||
char *buf; | |||||
off_t off; | |||||
size_t len, sz; | |||||
vm_size_t ubase; | |||||
int error; | int error; | ||||
error = devfs_get_cdevpriv((void **)&sc); | error = devfs_get_cdevpriv((void **)&sc); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
return (uiomove_object(sc->sc_obj, sc->sc_objsz, uio)); | |||||
off = uio->uio_offset; | |||||
len = uio->uio_resid; | |||||
if (off < 0 || off > sc->sc_usize) | |||||
return (EFAULT); | |||||
if (len > sc->sc_usize - off) | |||||
len = sc->sc_usize - off; | |||||
if (len == 0) | |||||
return (0); | |||||
/* | |||||
* Since the snapshot buffer is in the user space we have to copy it | |||||
* in to the kernel and then back out. The extra copy saves valuable | |||||
* kernel memory. | |||||
*/ | |||||
buf = malloc(PAGE_SIZE, M_KSYMS, M_WAITOK); | |||||
ubase = sc->sc_uaddr + off; | |||||
while (len) { | |||||
sz = min(PAGE_SIZE, len); | |||||
if (copyin((void *)ubase, buf, sz) != 0) | |||||
error = EFAULT; | |||||
else | |||||
error = uiomove(buf, sz, uio); | |||||
if (error != 0) | |||||
break; | |||||
len -= sz; | |||||
ubase += sz; | |||||
} | } | ||||
free(buf, M_KSYMS); | |||||
return (error); | |||||
} | |||||
static int | static int | ||||
ksyms_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int32_t flag __unused, | ksyms_mmap_single(struct cdev *dev, vm_ooffset_t *offset, vm_size_t size, | ||||
struct thread *td __unused) | vm_object_t *objp, int nprot) | ||||
{ | { | ||||
struct ksyms_softc *sc; | struct ksyms_softc *sc; | ||||
vm_object_t obj; | |||||
int error; | int error; | ||||
error = devfs_get_cdevpriv((void **)&sc); | error = devfs_get_cdevpriv((void **)&sc); | ||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
switch (cmd) { | if (*offset < 0 || *offset >= round_page(sc->sc_objsz) || | ||||
case KIOCGSIZE: | size > round_page(sc->sc_objsz) - *offset || | ||||
/* | (nprot & ~PROT_READ) != 0) | ||||
* Return the size (in bytes) of the symbol table | return (EINVAL); | ||||
* snapshot. | |||||
*/ | |||||
*(size_t *)data = sc->sc_usize; | |||||
break; | |||||
case KIOCGADDR: | |||||
/* | |||||
* Return the address of the symbol table snapshot. | |||||
* XXX - compat32 version of this? | |||||
*/ | |||||
*(void **)data = (void *)sc->sc_uaddr; | |||||
break; | |||||
default: | |||||
error = ENOTTY; | |||||
break; | |||||
} | |||||
return (error); | obj = sc->sc_obj; | ||||
} | vm_object_reference(obj); | ||||
*objp = obj; | |||||
static int | |||||
ksyms_mmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, | |||||
int prot __unused, vm_memattr_t *memattr __unused) | |||||
{ | |||||
struct ksyms_softc *sc; | |||||
int error; | |||||
error = devfs_get_cdevpriv((void **)&sc); | |||||
if (error != 0) | |||||
return (error); | |||||
/* | |||||
* XXX mmap() will actually map the symbol table into the process | |||||
* address space again. | |||||
*/ | |||||
if (offset > round_page(sc->sc_usize) || | |||||
(*paddr = pmap_extract(sc->sc_pmap, | |||||
(vm_offset_t)sc->sc_uaddr + offset)) == 0) | |||||
return (-1); | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
ksyms_close(struct cdev *dev, int flags __unused, int fmt __unused, | |||||
struct thread *td) | |||||
{ | |||||
struct ksyms_softc *sc; | |||||
int error; | |||||
error = devfs_get_cdevpriv((void **)&sc); | |||||
if (error != 0) | |||||
return (error); | |||||
/* Unmap the buffer from the process address space. */ | |||||
return (copyout_unmap(td, sc->sc_uaddr, sc->sc_usize)); | |||||
} | |||||
static int | |||||
ksyms_modevent(module_t mod __unused, int type, void *data __unused) | ksyms_modevent(module_t mod __unused, int type, void *data __unused) | ||||
{ | { | ||||
int error; | int error; | ||||
error = 0; | error = 0; | ||||
switch (type) { | switch (type) { | ||||
case MOD_LOAD: | case MOD_LOAD: | ||||
mtx_init(&ksyms_mtx, "KSyms mtx", NULL, MTX_DEF); | sx_init(&ksyms_mtx, "KSyms mtx"); | ||||
ksyms_dev = make_dev(&ksyms_cdevsw, 0, UID_ROOT, GID_WHEEL, | ksyms_dev = make_dev(&ksyms_cdevsw, 0, UID_ROOT, GID_WHEEL, | ||||
0400, KSYMS_DNAME); | 0400, KSYMS_DNAME); | ||||
break; | break; | ||||
case MOD_UNLOAD: | case MOD_UNLOAD: | ||||
if (!LIST_EMPTY(&ksyms_list)) | if (!LIST_EMPTY(&ksyms_list)) | ||||
return (EBUSY); | return (EBUSY); | ||||
destroy_dev(ksyms_dev); | destroy_dev(ksyms_dev); | ||||
mtx_destroy(&ksyms_mtx); | sx_destroy(&ksyms_mtx); | ||||
break; | break; | ||||
case MOD_SHUTDOWN: | case MOD_SHUTDOWN: | ||||
break; | break; | ||||
default: | default: | ||||
error = EOPNOTSUPP; | error = EOPNOTSUPP; | ||||
break; | break; | ||||
} | } | ||||
return (error); | return (error); | ||||
} | } | ||||
DEV_MODULE(ksyms, ksyms_modevent, NULL); | DEV_MODULE(ksyms, ksyms_modevent, NULL); | ||||
MODULE_VERSION(ksyms, 1); | MODULE_VERSION(ksyms, 1); |