-
Notifications
You must be signed in to change notification settings - Fork 81
/
klib.c
245 lines (229 loc) · 6.86 KB
/
klib.c
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
/*
* klib.c
***********************************************************************
* This program is part of the source code released for the book
* "Linux Kernel Programming" 2E
* (c) Author: Kaiwan N Billimoria
* Publisher: Packt
* GitHub repository:
* https://github.com/PacktPublishing/Linux-Kernel-Programming_2E
*
************************************************************************
* Brief Description:
* This kernel (module) code is meant to serve as a 'library' of sorts. Other
* kernel modules in our codebase might wish to link into it and use it's code.
*
* SECURITY NOTE
* Here we do use the %px printk format specifier in order to see the actual
* address; this is a security info-leak! Do NOT do this in production.
*
* For details, please refer the book.
*/
#include "klib.h"
/*
* Simple wrapper over the snprintf() - a bit more security-aware.
* Checks for and warns on overflow
*/
int snprintf_lkp(char *buf, size_t maxsize, const char *fmt, ...)
{
va_list args;
int n;
#ifndef __KERNEL__
#include <stdarg.h>
#endif
va_start(args, fmt);
n = vsnprintf(buf, maxsize, fmt, args);
va_end(args);
if (n >= maxsize) {
#ifdef __KERNEL__
pr_warn("snprintf(): possible overflow! (maxsize=%lu, ret=%d)\n",
maxsize, n);
dump_stack();
#else
fprintf(stderr, "snprintf(): possible overflow! (maxsize=%lu, ret=%d)\n",
maxsize, n);
#endif
}
return n;
}
/* minsysinfo:
* Similar to our ch5/min_sysinfo code; it's just simpler (avoiding deps) to
* package this code into this small 'library' of sorts rather than to use it
* via the module stacking approach.
*
* A more security-aware version of the klib_sysinfo routine. We used
* David Wheeler's flawfinder(1) tool to detect possible vulnerabilities;
* so, we change the strlen, and replace the strncat with strlcat.
*/
void minsysinfo(void)
{
#define MSGLEN 128
char msg[MSGLEN];
memset(msg, 0, MSGLEN);
snprintf_lkp(msg, 48, "%s(): minimal platform info:\nCPU: ", __func__);
/* Strictly speaking, all this #if... is considered ugly and should be
isolated as far as is possible */
#ifdef CONFIG_X86
#if(BITS_PER_LONG == 32)
strlcat(msg, "x86_32, ", MSGLEN);
#else
strlcat(msg, "x86_64, ", MSGLEN);
#endif
#endif
#ifdef CONFIG_ARM
strlcat(msg, "ARM-32, ", MSGLEN);
#endif
#ifdef CONFIG_ARM64
strlcat(msg, "Aarch64, ", MSGLEN);
#endif
#ifdef CONFIG_MIPS
strlcat(msg, "MIPS, ", MSGLEN);
#endif
#ifdef CONFIG_PPC
strlcat(msg, "PowerPC, ", MSGLEN);
#endif
#ifdef CONFIG_S390
strlcat(msg, "IBM S390, ", MSGLEN);
#endif
#ifdef __BIG_ENDIAN
strlcat(msg, "big-endian; ", MSGLEN);
#else
strlcat(msg, "little-endian; ", MSGLEN);
#endif
#if(BITS_PER_LONG == 32)
strlcat(msg, "32-bit OS.\n", MSGLEN);
#elif(BITS_PER_LONG == 64)
strlcat(msg, "64-bit OS.\n", MSGLEN);
#endif
pr_info("%s", msg);
}
/*
* show_phy_pages() - show the virtual, physical addresses and PFNs of the memory
* range provided on a per-page basis.
*
* ! NOTE NOTE NOTE !
* The starting kernel address MUST be a 'linear' address, i.e., an adrress
* within the 'lowmem' direct-mapped region of the kernel segment, else this
* will NOT work and can possibly crash the system.
*
* @kaddr: the starting kernel virtual address; MUST be a 'lowmem' region addr
* @len: length of the memory piece (bytes)
* @contiguity_check: if True, check for physical contiguity of pages
*
* 'Walk' the virtually contiguous 'array' of pages one by one (i.e. page by
* page), printing the virtual and physical address as well as the PFN- page
* frame number. This way, we can see if the memory really is *physically*
* contiguous or not.
*/
void show_phy_pages(void *kaddr, size_t len, bool contiguity_check)
{
void *vaddr = kaddr;
#if(BITS_PER_LONG == 64)
const char *hdr = "-pg#- --------va-------- --------pa-------- ---PFN---\n";
#else // 32-bit
const char *hdr = "-pg#- ----va---- --------pa-------- --PFN--\n";
#endif
phys_addr_t pa;
int loops = len/PAGE_SIZE, i;
long pfn, prev_pfn = 1;
#ifdef CONFIG_X86
if (!virt_addr_valid(vaddr)) {
pr_info("%s(): invalid virtual address (0x%px)\n", __func__, vaddr);
return;
}
/* Worry not, the ARM implementation of virt_to_phys() performs an internal
* validity check
*/
#endif
pr_info("%s():start kaddr %px, len %zu, contiguity_check is %s\n",
__func__, vaddr, len, contiguity_check?"on":"off");
pr_info("%s", hdr);
if (len % PAGE_SIZE)
loops++;
for (i = 0; i < loops; i++) {
pa = virt_to_phys(vaddr+(i*PAGE_SIZE));
pfn = PHYS_PFN(pa);
if (!!contiguity_check) {
/* What's with the 'if !!(<cond>) ...' ??
* It's a 'C' trick ensuring that the if condition always evaluates
* to a boolean - either 0 or 1. Cool, huh.
*/
if (i && (pfn != prev_pfn + 1)) {
pr_notice(" *** physical NON-contiguity detected (i=%d) ***\n", i);
break;
}
}
/* Below we show the actual virt addr and not a hashed value by
* using the 0x%px format specifier instead of the %pK as we
* should for security */
/* if(!(i%100)) */
#if(BITS_PER_LONG == 64)
pr_info("%05d 0x%px %pa %9ld\n",
#else
pr_info("%05d 0x%px %pa %7ld\n",
#endif
i, vaddr+(i*PAGE_SIZE), &pa, pfn);
if (!!contiguity_check)
prev_pfn = pfn;
}
}
/*
* powerof - a simple 'library' function to calculate and return
* @base to-the-power-of @exponent
* f.e. powerof(2, 5) returns 2^5 = 32.
* Returns -1UL on failure.
*/
u64 powerof(int base, int exponent)
{
u64 res = 1;
if (base == 0) // 0^e = 0
return 0;
if (base <= 0 || exponent < 0)
return -1UL;
if (exponent == 0) // b^0 = 1
return 1;
while (exponent--)
res *= base;
return res;
}
/*
* show_sizeof()
* Simply displays the sizeof data types on the platform.
*/
void show_sizeof(void)
{
#ifndef __KERNEL__
printf(
#else
pr_info(
#endif
"sizeof: (bytes)\n"
" char = %2zu short int = %2zu int = %2zu\n"
" long = %2zu long long = %2zu void * = %2zu\n"
" float = %2zu double = %2zu long double = %2zu\n",
sizeof(char), sizeof(short int), sizeof(int),
sizeof(long), sizeof(long long), sizeof(void *),
sizeof(float), sizeof(double), sizeof(long double));
}
/*------------ delay_sec --------------------------------------------------
* Delays execution for @val seconds.
* The fact is, it's unnecessary (just a demo): the kernel already has the
* ssleep() inline function (a simple wrapper over the msleep()).
*
* Parameters:
* @val : number of seconds to sleep for; if -1, sleep forever
* MUST be called from process context.
* (We deliberately do not inline this function; this way, we can see it's
* entry within a kernel stack call trace).
*/
void delay_sec(long val)
{
asm (""); // force the compiler to not inline it!
if (in_task()) {
set_current_state(TASK_INTERRUPTIBLE);
if (val == -1)
schedule_timeout(MAX_SCHEDULE_TIMEOUT);
else
schedule_timeout(val * HZ);
}
}