1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
/*
* linux/arch/arm/kernel/swp_emulate.c
*
* Copyright (C) 2009 ARM Limited
* __user_* functions adapted from include/asm/uaccess.h
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Implements emulation of the SWP/SWPB instructions using load-exclusive and
* store-exclusive for processors that have them disabled (or future ones that
* might not implement them).
*
* Syntax of SWP{B} instruction: SWP{B}<c> <Rt>, <Rt2>, [<Rn>]
* Where: Rt = destination
* Rt2 = source
* Rn = address
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/syscalls.h>
#include <linux/perf_event.h>
#include <asm/traps.h>
#include <asm/uaccess.h>
static unsigned long sdivcounter;
static unsigned long udivcounter;
static pid_t previous_pid;
#ifdef CONFIG_PROC_FS
static int proc_read_status(char *page, char **start, off_t off, int count,
int *eof, void *data)
{
char *p = page;
int len;
p += sprintf(p, "Emulated SDIV:\t\t%lu\n", sdivcounter);
p += sprintf(p, "Emulated UDIV:\t\t%lu\n", udivcounter);
if (previous_pid != 0)
p += sprintf(p, "Last process:\t\t%d\n", previous_pid);
len = (p - page) - off;
if (len < 0)
len = 0;
*eof = (len <= count) ? 1 : 0;
*start = page + off;
return len;
}
#endif
static inline bool isDivideByZeroTrappingOn(void)
{
uint32_t controlReg;
asm("mrc p15, 0, %0, c1, c0, 0":"=r"(controlReg));
return !!((controlReg >> 19) & 1);
}
#define GENERATE_HANDLER(nm, dispnm, typ) \
static int nm##_handler(struct pt_regs *regs, unsigned int instr) \
{ \
typ n, m, d; \
if (current->pid != previous_pid) { \
pr_debug("\"%s\" (%ld) uses unsupported " dispnm \
" instruction - this will be slow\n", \
current->comm, (unsigned long)current->pid); \
previous_pid = current->pid; \
} \
n = regs->uregs[(instr >> 16) & 15]; \
m = regs->uregs[(instr >> 0) & 15]; \
if (unlikely(!m)) { \
if (unlikely(isDivideByZeroTrappingOn())) \
return -1; \
else \
d = 0; \
} else \
d = n / m; \
regs->uregs[(instr >> 8) & 15] = d; \
regs->ARM_pc += 4; \
\
nm ## counter++; \
return 0; \
}
GENERATE_HANDLER(sdiv, "SDIV", int32_t)
GENERATE_HANDLER(udiv, "UDIV", uint32_t)
static struct undef_hook sdiv_hook = {
.instr_mask = 0xfff0f0f0,
.instr_val = 0xfb90f0f0,
.cpsr_mask = MODE_MASK | PSR_T_BIT | PSR_J_BIT,
.cpsr_val = USR_MODE | PSR_T_BIT,
.fn = sdiv_handler
};
static struct undef_hook udiv_hook = {
.instr_mask = 0xfff0f0f0,
.instr_val = 0xfbb0f0f0,
.cpsr_mask = MODE_MASK | PSR_T_BIT | PSR_J_BIT,
.cpsr_val = USR_MODE | PSR_T_BIT,
.fn = udiv_handler
};
/*
* Register handler and create status file in /proc/cpu
* Invoked as late_initcall, since not needed before init spawned.
*/
static int __init div_emulation_init(void)
{
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *res;
res = create_proc_entry("cpu/div_emulation", S_IRUGO, NULL);
if (!res)
return -ENOMEM;
res->read_proc = proc_read_status;
#endif /* CONFIG_PROC_FS */
printk(KERN_NOTICE "Registering SDIV/UDIV emulation handler\n");
register_undef_hook(&sdiv_hook);
register_undef_hook(&udiv_hook);
return 0;
}
late_initcall(div_emulation_init);
|