Skip to content
Permalink
Browse files
dtrace: core and x86
This implements DTrace's core kernel (linked-in) components,
including platform-dependent portions for x86.  (Most of this
machinery is not used until the next commit.)

Signed-off-by: Nick Alcock <nick.alcock@oracle.com>
Signed-off-by: Kris Van Hees <kris.van.hees@oracle.com>
Signed-off-by: Tomas Jedlicka <tomas.jedlicka@oracle.com>
Signed-off-by: Eugene Loh <eugene.loh@oracle.com>
Signed-off-by: David Mc Lean <david.mclean@oracle.com>
Signed-off-by: Vincent Lim <vincent.lim@oracle.com>
  • Loading branch information
kvanhees authored and nickalcock committed Nov 7, 2021
1 parent 0fd5e39 commit 62a2550ef447ac372a5bb92e5baad3e2932865c1
Show file tree
Hide file tree
Showing 85 changed files with 7,133 additions and 6 deletions.
@@ -1125,15 +1125,15 @@ core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypto/ block/

vmlinux-dirs := $(patsubst %/,%,$(filter %/, \
$(core-y) $(core-m) $(drivers-y) $(drivers-m) \
$(libs-y) $(libs-m)))
$(libs-y) $(libs-m) $(dtrace-y) $(dtrace-m)))

vmlinux-alldirs := $(sort $(vmlinux-dirs) Documentation \
$(patsubst %/,%,$(filter %/, $(core-) \
$(drivers-) $(libs-))))
$(drivers-) $(libs-) $(dtrace-))))

subdir-modorder := $(addsuffix modules.order,$(filter %/, \
$(core-y) $(core-m) $(libs-y) $(libs-m) \
$(drivers-y) $(drivers-m)))
$(drivers-y) $(drivers-m) $(dtrace-y) $(dtrace-m)))

build-dirs := $(vmlinux-dirs)
clean-dirs := $(vmlinux-alldirs)
@@ -1148,6 +1148,7 @@ else
KBUILD_VMLINUX_LIBS := $(patsubst %/,%/lib.a, $(libs-y))
endif
KBUILD_VMLINUX_OBJS += $(patsubst %/,%/built-in.a, $(drivers-y))
KBUILD_VMLINUX_OBJS += $(patsubst %/,%/built-in.a, $(dtrace-y))

export KBUILD_VMLINUX_OBJS KBUILD_VMLINUX_LIBS
export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds
@@ -386,6 +386,9 @@ config PGTABLE_LEVELS
default 3 if X86_PAE
default 2

config ARCH_SUPPORTS_DTRACE
def_bool y if X86_64

config CC_HAS_SANE_STACKPROTECTOR
bool
default $(success,$(srctree)/scripts/gcc-x86_64-has-stack-protector.sh $(CC)) if 64BIT
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0 */

/*
* Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
*/

#ifndef _X86_DTRACE_ARCH_H
#define _X86_DTRACE_ARCH_H

/* Number of arguments stored inside the mstate. */
#define DTRACE_MSTATE_ARGS_MAX 6

typedef uint8_t asm_instr_t;

typedef int (*prov_exit_f)(void);

/*
* Structure to hold DTrace specific information about modules (including the
* core kernel module). Note that each module (and the main kernel) already
* has one field that relates to probing:
* - pdata: pointer to a dtrace_module struct (for DTrace)
*/
struct dtrace_module {
int enabled_cnt;
prov_exit_f prov_exit; /* Called with module_mutex held */
};

#endif /* _X86_DTRACE_ARCH_H */
@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0 */

/* Copyright (C) 2013-2014 Oracle, Inc. */

#ifndef _ASM_X86_DTRACE_CPUINFO_H_
#define _ASM_X86_DTRACE_CPUINFO_H_

#include <asm/processor.h>

typedef struct cpuinfo_x86 cpuinfo_arch_t;

#define dtrace_cpuinfo_chip(ci) ((ci)->phys_proc_id)

#endif /* _ASM_X86_DTRACE_CPUINFO_H_ */
@@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
*/

#ifndef _X86_DTRACE_UTIL_H
#define _X86_DTRACE_UTIL_H

#ifndef __ASSEMBLY__

#include <asm/dtrace_arch.h>
#include <asm/ptrace.h>

#endif

#endif /* _X86_DTRACE_UTIL_H */
@@ -0,0 +1,244 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* FILE: dtrace_util.c
* DESCRIPTION: Dynamic Tracing: Architecture utility functions
*
* Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
*/

#include <linux/dtrace_cpu.h>
#include <linux/dtrace_os.h>
#include <linux/dtrace_task_impl.h>
#include <linux/kdebug.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/memory.h>
#include <linux/notifier.h>
#include <linux/ptrace.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/sched/task_stack.h>
#include <asm/insn.h>
#include <asm/pgtable.h>
#include <asm/ptrace.h>
#include <asm/text-patching.h>
#include <asm/dtrace_arch.h>
#include <asm/dtrace_util.h>

int dtrace_instr_size(const asm_instr_t *addr)
{
struct insn insn;

kernel_insn_init(&insn, addr, MAX_INSN_SIZE);
insn_get_length(&insn);

return insn_complete(&insn) ? insn.length : -1;
}
EXPORT_SYMBOL(dtrace_instr_size);

/*
* Move the instruction pointer forward to the next instruction, effectiely
* skipping the current one.
*/
static void dtrace_skip_instruction(struct pt_regs *regs)
{
int delta;

delta = dtrace_instr_size((asm_instr_t *)regs->ip);
BUG_ON(delta <= 0);

regs->ip += delta;
}

void dtrace_handle_badaddr(struct pt_regs *regs)
{
unsigned long addr = read_cr2();

DTRACE_CPUFLAG_SET(CPU_DTRACE_BADADDR);
this_cpu_core->cpuc_dtrace_illval = addr;

dtrace_skip_instruction(regs);
}

/*
* Trap notification handler.
*/
int dtrace_die_notifier(struct notifier_block *nb, unsigned long val,
void *args)
{
struct die_args *dargs = args;

switch (val) {
case DIE_PAGE_FAULT: {
if (!DTRACE_CPUFLAG_ISSET(CPU_DTRACE_NOFAULT))
return NOTIFY_DONE;

dtrace_handle_badaddr(dargs->regs);

return NOTIFY_OK | NOTIFY_STOP_MASK;
}
case DIE_GPF: {
if (!DTRACE_CPUFLAG_ISSET(CPU_DTRACE_NOFAULT))
return NOTIFY_DONE;

dtrace_handle_badaddr(dargs->regs);

return NOTIFY_OK | NOTIFY_STOP_MASK;
}
fallthrough;
default:
return NOTIFY_DONE;
}
}

static inline int dtrace_bad_address(void *addr)
{
unsigned long dummy;

return get_kernel_nofault(dummy, (unsigned long *)addr);
}

static int dtrace_user_addr_is_exec(uintptr_t addr)
{
struct mm_struct *mm = current->mm;
pgd_t *pgd;

#if CONFIG_PGTABLE_LEVELS > 3
p4d_t *p4d;
#endif

pud_t *pud;
pmd_t *pmd;
pte_t *pte;
unsigned long flags;
int ret = 0;

if (mm == NULL)
return 0;

addr &= PAGE_MASK;

local_irq_save(flags);

pgd = pgd_offset(mm, addr);
if (dtrace_bad_address(pgd))
goto out;
if (pgd_none(*pgd) || !pgd_present(*pgd))
goto out;

#if CONFIG_PGTABLE_LEVELS > 3
p4d = p4d_offset(pgd, addr);
if (dtrace_bad_address(p4d))
goto out;
if (p4d_none(*p4d) || !p4d_present(*p4d))
goto out;

pud = pud_offset(p4d, addr);
#else
pud = pud_offset(pgd, addr);
#endif

if (dtrace_bad_address(pud))
goto out;
if (pud_none(*pud) || !pud_present(*pud))
goto out;
if (unlikely(pud_large(*pud))) {
pte = (pte_t *)pud;
if (dtrace_bad_address(pte))
goto out;

ret = pte_exec(*pte);
goto out;
}

pmd = pmd_offset(pud, addr);
if (dtrace_bad_address(pmd))
goto out;
if (pmd_none(*pmd))
goto out;
if (unlikely(pmd_large(*pmd) || !pmd_present(*pmd))) {
pte = (pte_t *)pmd;
if (dtrace_bad_address(pte))
goto out;

ret = pte_exec(*pte);
goto out;
}

pte = pte_offset_map(pmd, addr);
if (dtrace_bad_address(pte))
goto out;
if (pte_protnone(*pte))
goto out;
if ((pte_flags(*pte) & (_PAGE_PRESENT|_PAGE_USER|_PAGE_SPECIAL)) !=
(_PAGE_PRESENT|_PAGE_USER))
goto out;

ret = pte_exec(*pte);

out:
local_irq_restore(flags);

return ret;
}

void dtrace_user_stacktrace(struct stacktrace_state *st)
{
struct pt_regs *regs = current_pt_regs();
uint64_t *pcs = st->pcs;
int limit = st->limit;
unsigned long *bos;
unsigned long *sp = (unsigned long *)user_stack_pointer(regs);
int ret;

if (!user_mode(regs))
goto out;

if (current->dt_task == NULL)
goto out;

bos = current->dt_task->dt_ustack;

st->depth = 1;
if (pcs)
*pcs++ = (uint64_t)instruction_pointer(regs);
limit--;

if (!limit)
goto out;

while (sp <= bos && limit) {
unsigned long pc;

pagefault_disable();
ret = __copy_from_user_inatomic(&pc, sp, sizeof(pc));
pagefault_enable();

if (ret)
break;

if (dtrace_user_addr_is_exec(pc)) {
if (pcs)
*pcs++ = pc;
limit--;
st->depth++;
}

sp++;
}

out:
if (pcs) {
while (limit--)
*pcs++ = 0;
}
}

void dtrace_mod_pdata_init(struct dtrace_module *pdata)
{
}

void dtrace_mod_pdata_cleanup(struct dtrace_module *pdata)
{
}

0 comments on commit 62a2550

Please sign in to comment.