Blame view

fs/proc/kcore.c 15.9 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
5
6
7
8
9
10
11
  /*
   *	fs/proc/kcore.c kernel ELF core dumper
   *
   *	Modelled on fs/exec.c:aout_core_dump()
   *	Jeremy Fitzhardinge <jeremy@sw.oz.au>
   *	ELF version written by David Howells <David.Howells@nexor.co.uk>
   *	Modified and incorporated into 2.3.x by Tigran Aivazian <tigran@veritas.com>
   *	Support to dump vmalloc'd areas (ELF only), Tigran Aivazian <tigran@veritas.com>
   *	Safe accesses to vmalloc/direct-mapped discontiguous areas, Kanoj Sarcar <kanoj@sgi.com>
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
  #include <linux/mm.h>
  #include <linux/proc_fs.h>
2f96b8c1d   David Howells   proc: Split kcore...
14
  #include <linux/kcore.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
  #include <linux/user.h>
16f7e0fe2   Randy Dunlap   [PATCH] capable/c...
16
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
  #include <linux/elf.h>
  #include <linux/elfcore.h>
3c743a7f7   Andrew Morton   fs/proc/kcore.c: ...
19
  #include <linux/notifier.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
21
  #include <linux/vmalloc.h>
  #include <linux/highmem.h>
87ebdc00e   Andrew Morton   fs/proc: clean up...
22
  #include <linux/printk.h>
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
23
  #include <linux/bootmem.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
  #include <linux/init.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
25
  #include <linux/slab.h>
7c0f6ba68   Linus Torvalds   Replace <asm/uacc...
26
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
  #include <asm/io.h>
2ef43ec77   KAMEZAWA Hiroyuki   kcore: use usual ...
28
  #include <linux/list.h>
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
29
  #include <linux/ioport.h>
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
30
  #include <linux/memory.h>
299300258   Ingo Molnar   sched/headers: Pr...
31
  #include <linux/sched/task.h>
9492587cf   KAMEZAWA Hiroyuki   kcore: register t...
32
  #include <asm/sections.h>
59d8053f1   David Howells   proc: Move non-pu...
33
  #include "internal.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34

360276042   Magnus Damm   [PATCH] elf: fix ...
35
  #define CORE_STR "CORE"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36

79885b227   Edgar E. Iglesias   elf: use ELF_CORE...
37
38
39
  #ifndef ELF_CORE_EFLAGS
  #define ELF_CORE_EFLAGS	0
  #endif
97ce5d6dc   Alexey Dobriyan   proc: move all /p...
40
  static struct proc_dir_entry *proc_root_kcore;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
43
44
45
46
47
  
  #ifndef kc_vaddr_to_offset
  #define	kc_vaddr_to_offset(v) ((v) - PAGE_OFFSET)
  #endif
  #ifndef	kc_offset_to_vaddr
  #define	kc_offset_to_vaddr(o) ((o) + PAGE_OFFSET)
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
50
51
52
53
54
55
  /* An ELF note in memory */
  struct memelfnote
  {
  	const char *name;
  	int type;
  	unsigned int datasz;
  	void *data;
  };
2ef43ec77   KAMEZAWA Hiroyuki   kcore: use usual ...
56
  static LIST_HEAD(kclist_head);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57
  static DEFINE_RWLOCK(kclist_lock);
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
58
  static int kcore_need_update = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
60
  
  void
c30bb2a25   KAMEZAWA Hiroyuki   kcore: add kclist...
61
  kclist_add(struct kcore_list *new, void *addr, size_t size, int type)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
  {
  	new->addr = (unsigned long)addr;
  	new->size = size;
c30bb2a25   KAMEZAWA Hiroyuki   kcore: add kclist...
65
  	new->type = type;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
67
  
  	write_lock(&kclist_lock);
2ef43ec77   KAMEZAWA Hiroyuki   kcore: use usual ...
68
  	list_add_tail(&new->list, &kclist_head);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
69
70
71
72
73
74
75
76
77
78
  	write_unlock(&kclist_lock);
  }
  
  static size_t get_kcore_size(int *nphdr, size_t *elf_buflen)
  {
  	size_t try, size;
  	struct kcore_list *m;
  
  	*nphdr = 1; /* PT_NOTE */
  	size = 0;
2ef43ec77   KAMEZAWA Hiroyuki   kcore: use usual ...
79
  	list_for_each_entry(m, &kclist_head, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
82
83
84
85
86
  		try = kc_vaddr_to_offset((size_t)m->addr + m->size);
  		if (try > size)
  			size = try;
  		*nphdr = *nphdr + 1;
  	}
  	*elf_buflen =	sizeof(struct elfhdr) + 
  			(*nphdr + 2)*sizeof(struct elf_phdr) + 
360276042   Magnus Damm   [PATCH] elf: fix ...
87
88
89
90
  			3 * ((sizeof(struct elf_note)) +
  			     roundup(sizeof(CORE_STR), 4)) +
  			roundup(sizeof(struct elf_prstatus), 4) +
  			roundup(sizeof(struct elf_prpsinfo), 4) +
5aaeb5c01   Ingo Molnar   x86/fpu, sched: I...
91
  			roundup(arch_task_struct_size, 4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92
93
94
  	*elf_buflen = PAGE_ALIGN(*elf_buflen);
  	return size + *elf_buflen;
  }
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
95
96
97
98
99
100
101
102
103
104
  static void free_kclist_ents(struct list_head *head)
  {
  	struct kcore_list *tmp, *pos;
  
  	list_for_each_entry_safe(pos, tmp, head, list) {
  		list_del(&pos->list);
  		kfree(pos);
  	}
  }
  /*
26562c59f   KAMEZAWA Hiroyuki   kcore: register v...
105
   * Replace all KCORE_RAM/KCORE_VMEMMAP information with passed list.
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
106
107
108
   */
  static void __kcore_update_ram(struct list_head *list)
  {
678ad5d8a   KAMEZAWA Hiroyuki   /proc/kcore: fix ...
109
110
  	int nphdr;
  	size_t size;
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
111
112
113
114
115
116
  	struct kcore_list *tmp, *pos;
  	LIST_HEAD(garbage);
  
  	write_lock(&kclist_lock);
  	if (kcore_need_update) {
  		list_for_each_entry_safe(pos, tmp, &kclist_head, list) {
26562c59f   KAMEZAWA Hiroyuki   kcore: register v...
117
118
  			if (pos->type == KCORE_RAM
  				|| pos->type == KCORE_VMEMMAP)
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
119
120
121
122
123
124
  				list_move(&pos->list, &garbage);
  		}
  		list_splice_tail(list, &kclist_head);
  	} else
  		list_splice(list, &garbage);
  	kcore_need_update = 0;
678ad5d8a   KAMEZAWA Hiroyuki   /proc/kcore: fix ...
125
  	proc_root_kcore->size = get_kcore_size(&nphdr, &size);
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
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
  	write_unlock(&kclist_lock);
  
  	free_kclist_ents(&garbage);
  }
  
  
  #ifdef CONFIG_HIGHMEM
  /*
   * If no highmem, we can assume [0...max_low_pfn) continuous range of memory
   * because memory hole is not as big as !HIGHMEM case.
   * (HIGHMEM is special because part of memory is _invisible_ from the kernel.)
   */
  static int kcore_update_ram(void)
  {
  	LIST_HEAD(head);
  	struct kcore_list *ent;
  	int ret = 0;
  
  	ent = kmalloc(sizeof(*ent), GFP_KERNEL);
  	if (!ent)
  		return -ENOMEM;
  	ent->addr = (unsigned long)__va(0);
  	ent->size = max_low_pfn << PAGE_SHIFT;
  	ent->type = KCORE_RAM;
  	list_add(&ent->list, &head);
  	__kcore_update_ram(&head);
  	return ret;
  }
  
  #else /* !CONFIG_HIGHMEM */
26562c59f   KAMEZAWA Hiroyuki   kcore: register v...
156
157
  #ifdef CONFIG_SPARSEMEM_VMEMMAP
  /* calculate vmemmap's address from given system ram pfn and register it */
b908243c5   Djalal Harouni   fs/proc/kcore.c: ...
158
159
  static int
  get_sparsemem_vmemmap_info(struct kcore_list *ent, struct list_head *head)
26562c59f   KAMEZAWA Hiroyuki   kcore: register v...
160
161
162
163
164
165
166
167
168
  {
  	unsigned long pfn = __pa(ent->addr) >> PAGE_SHIFT;
  	unsigned long nr_pages = ent->size >> PAGE_SHIFT;
  	unsigned long start, end;
  	struct kcore_list *vmm, *tmp;
  
  
  	start = ((unsigned long)pfn_to_page(pfn)) & PAGE_MASK;
  	end = ((unsigned long)pfn_to_page(pfn + nr_pages)) - 1;
108a8a11c   Fabian Frederick   fs/proc/kcore.c: ...
169
  	end = PAGE_ALIGN(end);
26562c59f   KAMEZAWA Hiroyuki   kcore: register v...
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
  	/* overlap check (because we have to align page */
  	list_for_each_entry(tmp, head, list) {
  		if (tmp->type != KCORE_VMEMMAP)
  			continue;
  		if (start < tmp->addr + tmp->size)
  			if (end > tmp->addr)
  				end = tmp->addr;
  	}
  	if (start < end) {
  		vmm = kmalloc(sizeof(*vmm), GFP_KERNEL);
  		if (!vmm)
  			return 0;
  		vmm->addr = start;
  		vmm->size = end - start;
  		vmm->type = KCORE_VMEMMAP;
  		list_add_tail(&vmm->list, head);
  	}
  	return 1;
  
  }
  #else
b908243c5   Djalal Harouni   fs/proc/kcore.c: ...
191
192
  static int
  get_sparsemem_vmemmap_info(struct kcore_list *ent, struct list_head *head)
26562c59f   KAMEZAWA Hiroyuki   kcore: register v...
193
194
195
196
197
  {
  	return 1;
  }
  
  #endif
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
198
199
200
201
202
  static int
  kclist_add_private(unsigned long pfn, unsigned long nr_pages, void *arg)
  {
  	struct list_head *head = (struct list_head *)arg;
  	struct kcore_list *ent;
553495752   Laura Abbott   proc/kcore: don't...
203
204
205
206
207
208
209
210
  	struct page *p;
  
  	if (!pfn_valid(pfn))
  		return 1;
  
  	p = pfn_to_page(pfn);
  	if (!memmap_valid_within(pfn, p, page_zone(p)))
  		return 1;
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
211
212
213
214
  
  	ent = kmalloc(sizeof(*ent), GFP_KERNEL);
  	if (!ent)
  		return -ENOMEM;
553495752   Laura Abbott   proc/kcore: don't...
215
  	ent->addr = (unsigned long)page_to_virt(p);
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
216
  	ent->size = nr_pages << PAGE_SHIFT;
553495752   Laura Abbott   proc/kcore: don't...
217
  	if (!virt_addr_valid(ent->addr))
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
218
219
220
221
222
  		goto free_out;
  
  	/* cut not-mapped area. ....from ppc-32 code. */
  	if (ULONG_MAX - ent->addr < ent->size)
  		ent->size = ULONG_MAX - ent->addr;
553495752   Laura Abbott   proc/kcore: don't...
223
224
225
226
227
228
  	/*
  	 * We've already checked virt_addr_valid so we know this address
  	 * is a valid pointer, therefore we can check against it to determine
  	 * if we need to trim
  	 */
  	if (VMALLOC_START > ent->addr) {
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
229
230
231
232
233
234
  		if (VMALLOC_START - ent->addr < ent->size)
  			ent->size = VMALLOC_START - ent->addr;
  	}
  
  	ent->type = KCORE_RAM;
  	list_add_tail(&ent->list, head);
26562c59f   KAMEZAWA Hiroyuki   kcore: register v...
235
236
237
238
239
  
  	if (!get_sparsemem_vmemmap_info(ent, head)) {
  		list_del(&ent->list);
  		goto free_out;
  	}
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
  	return 0;
  free_out:
  	kfree(ent);
  	return 1;
  }
  
  static int kcore_update_ram(void)
  {
  	int nid, ret;
  	unsigned long end_pfn;
  	LIST_HEAD(head);
  
  	/* Not inialized....update now */
  	/* find out "max pfn" */
  	end_pfn = 0;
4ff1b2c29   Lai Jiangshan   procfs: use N_MEM...
255
  	for_each_node_state(nid, N_MEMORY) {
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
256
  		unsigned long node_end;
83285c72e   Xishi Qiu   mm: use pgdat_end...
257
  		node_end = node_end_pfn(nid);
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
258
259
260
261
262
263
264
265
266
267
268
269
270
  		if (end_pfn < node_end)
  			end_pfn = node_end;
  	}
  	/* scan 0 to max_pfn */
  	ret = walk_system_ram_range(0, end_pfn, &head, kclist_add_private);
  	if (ret) {
  		free_kclist_ents(&head);
  		return -ENOMEM;
  	}
  	__kcore_update_ram(&head);
  	return ret;
  }
  #endif /* CONFIG_HIGHMEM */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
272
273
274
275
276
277
278
279
280
  
  /*****************************************************************************/
  /*
   * determine size of ELF note
   */
  static int notesize(struct memelfnote *en)
  {
  	int sz;
  
  	sz = sizeof(struct elf_note);
632dd2053   Vivek Goyal   [PATCH] Kcore elf...
281
  	sz += roundup((strlen(en->name) + 1), 4);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
284
285
286
287
288
289
290
291
292
293
294
295
  	sz += roundup(en->datasz, 4);
  
  	return sz;
  } /* end notesize() */
  
  /*****************************************************************************/
  /*
   * store a note in the header buffer
   */
  static char *storenote(struct memelfnote *men, char *bufp)
  {
  	struct elf_note en;
  
  #define DUMP_WRITE(addr,nr) do { memcpy(bufp,addr,nr); bufp += nr; } while(0)
632dd2053   Vivek Goyal   [PATCH] Kcore elf...
296
  	en.n_namesz = strlen(men->name) + 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
  	en.n_descsz = men->datasz;
  	en.n_type = men->type;
  
  	DUMP_WRITE(&en, sizeof(en));
  	DUMP_WRITE(men->name, en.n_namesz);
  
  	/* XXX - cast from long long to long to avoid need for libgcc.a */
  	bufp = (char*) roundup((unsigned long)bufp,4);
  	DUMP_WRITE(men->data, men->datasz);
  	bufp = (char*) roundup((unsigned long)bufp,4);
  
  #undef DUMP_WRITE
  
  	return bufp;
  } /* end storenote() */
  
  /*
   * store an ELF coredump header in the supplied buffer
   * nphdr is the number of elf_phdr to insert
   */
  static void elf_kcore_store_hdr(char *bufp, int nphdr, int dataoff)
  {
  	struct elf_prstatus prstatus;	/* NT_PRSTATUS */
  	struct elf_prpsinfo prpsinfo;	/* NT_PRPSINFO */
  	struct elf_phdr *nhdr, *phdr;
  	struct elfhdr *elf;
  	struct memelfnote notes[3];
  	off_t offset = 0;
  	struct kcore_list *m;
  
  	/* setup ELF header */
  	elf = (struct elfhdr *) bufp;
  	bufp += sizeof(struct elfhdr);
  	offset += sizeof(struct elfhdr);
  	memcpy(elf->e_ident, ELFMAG, SELFMAG);
  	elf->e_ident[EI_CLASS]	= ELF_CLASS;
  	elf->e_ident[EI_DATA]	= ELF_DATA;
  	elf->e_ident[EI_VERSION]= EV_CURRENT;
  	elf->e_ident[EI_OSABI] = ELF_OSABI;
  	memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
  	elf->e_type	= ET_CORE;
  	elf->e_machine	= ELF_ARCH;
  	elf->e_version	= EV_CURRENT;
  	elf->e_entry	= 0;
  	elf->e_phoff	= sizeof(struct elfhdr);
  	elf->e_shoff	= 0;
79885b227   Edgar E. Iglesias   elf: use ELF_CORE...
343
  	elf->e_flags	= ELF_CORE_EFLAGS;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
  	elf->e_ehsize	= sizeof(struct elfhdr);
  	elf->e_phentsize= sizeof(struct elf_phdr);
  	elf->e_phnum	= nphdr;
  	elf->e_shentsize= 0;
  	elf->e_shnum	= 0;
  	elf->e_shstrndx	= 0;
  
  	/* setup ELF PT_NOTE program header */
  	nhdr = (struct elf_phdr *) bufp;
  	bufp += sizeof(struct elf_phdr);
  	offset += sizeof(struct elf_phdr);
  	nhdr->p_type	= PT_NOTE;
  	nhdr->p_offset	= 0;
  	nhdr->p_vaddr	= 0;
  	nhdr->p_paddr	= 0;
  	nhdr->p_filesz	= 0;
  	nhdr->p_memsz	= 0;
  	nhdr->p_flags	= 0;
  	nhdr->p_align	= 0;
  
  	/* setup ELF PT_LOAD program header for every area */
2ef43ec77   KAMEZAWA Hiroyuki   kcore: use usual ...
365
  	list_for_each_entry(m, &kclist_head, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
366
367
368
369
370
371
372
373
  		phdr = (struct elf_phdr *) bufp;
  		bufp += sizeof(struct elf_phdr);
  		offset += sizeof(struct elf_phdr);
  
  		phdr->p_type	= PT_LOAD;
  		phdr->p_flags	= PF_R|PF_W|PF_X;
  		phdr->p_offset	= kc_vaddr_to_offset(m->addr) + dataoff;
  		phdr->p_vaddr	= (size_t)m->addr;
305277dae   James Morse   fs/proc/kcore.c: ...
374
  		if (m->type == KCORE_RAM)
464920104   Pratyush Anand   /proc/kcore: upda...
375
  			phdr->p_paddr	= __pa(m->addr);
305277dae   James Morse   fs/proc/kcore.c: ...
376
377
  		else if (m->type == KCORE_TEXT)
  			phdr->p_paddr	= __pa_symbol(m->addr);
464920104   Pratyush Anand   /proc/kcore: upda...
378
379
  		else
  			phdr->p_paddr	= (elf_addr_t)-1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
381
382
383
384
385
386
387
388
389
390
  		phdr->p_filesz	= phdr->p_memsz	= m->size;
  		phdr->p_align	= PAGE_SIZE;
  	}
  
  	/*
  	 * Set up the notes in similar form to SVR4 core dumps made
  	 * with info from their /proc.
  	 */
  	nhdr->p_offset	= offset;
  
  	/* set up the process status */
360276042   Magnus Damm   [PATCH] elf: fix ...
391
  	notes[0].name = CORE_STR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
392
393
394
395
396
397
398
399
400
401
  	notes[0].type = NT_PRSTATUS;
  	notes[0].datasz = sizeof(struct elf_prstatus);
  	notes[0].data = &prstatus;
  
  	memset(&prstatus, 0, sizeof(struct elf_prstatus));
  
  	nhdr->p_filesz	= notesize(&notes[0]);
  	bufp = storenote(&notes[0], bufp);
  
  	/* set up the process info */
360276042   Magnus Damm   [PATCH] elf: fix ...
402
  	notes[1].name	= CORE_STR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
404
405
406
407
408
409
410
411
412
  	notes[1].type	= NT_PRPSINFO;
  	notes[1].datasz	= sizeof(struct elf_prpsinfo);
  	notes[1].data	= &prpsinfo;
  
  	memset(&prpsinfo, 0, sizeof(struct elf_prpsinfo));
  	prpsinfo.pr_state	= 0;
  	prpsinfo.pr_sname	= 'R';
  	prpsinfo.pr_zomb	= 0;
  
  	strcpy(prpsinfo.pr_fname, "vmlinux");
30bc30df1   Zhao Hongjiang   fs/proc/kcore.c: ...
413
  	strlcpy(prpsinfo.pr_psargs, saved_command_line, sizeof(prpsinfo.pr_psargs));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
414
415
416
417
418
  
  	nhdr->p_filesz	+= notesize(&notes[1]);
  	bufp = storenote(&notes[1], bufp);
  
  	/* set up the task structure */
360276042   Magnus Damm   [PATCH] elf: fix ...
419
  	notes[2].name	= CORE_STR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
420
  	notes[2].type	= NT_TASKSTRUCT;
5aaeb5c01   Ingo Molnar   x86/fpu, sched: I...
421
  	notes[2].datasz	= arch_task_struct_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
422
423
424
425
426
427
428
429
430
431
432
433
434
435
  	notes[2].data	= current;
  
  	nhdr->p_filesz	+= notesize(&notes[2]);
  	bufp = storenote(&notes[2], bufp);
  
  } /* end elf_kcore_store_hdr() */
  
  /*****************************************************************************/
  /*
   * read from the ELF header and then kernel memory
   */
  static ssize_t
  read_kcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos)
  {
f5beeb185   Jiri Olsa   fs/proc/kcore.c: ...
436
  	char *buf = file->private_data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437
438
439
440
441
442
443
  	ssize_t acc = 0;
  	size_t size, tsz;
  	size_t elf_buflen;
  	int nphdr;
  	unsigned long start;
  
  	read_lock(&kclist_lock);
678ad5d8a   KAMEZAWA Hiroyuki   /proc/kcore: fix ...
444
  	size = get_kcore_size(&nphdr, &elf_buflen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
  	if (buflen == 0 || *fpos >= size) {
  		read_unlock(&kclist_lock);
  		return 0;
  	}
  
  	/* trim buflen to not go beyond EOF */
  	if (buflen > size - *fpos)
  		buflen = size - *fpos;
  
  	/* construct an ELF core header if we'll need some of it */
  	if (*fpos < elf_buflen) {
  		char * elf_buf;
  
  		tsz = elf_buflen - *fpos;
  		if (buflen < tsz)
  			tsz = buflen;
f8314dc60   Panagiotis Issaris   [PATCH] fs: Conve...
461
  		elf_buf = kzalloc(elf_buflen, GFP_ATOMIC);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
462
463
464
465
  		if (!elf_buf) {
  			read_unlock(&kclist_lock);
  			return -ENOMEM;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
  		elf_kcore_store_hdr(elf_buf, nphdr, elf_buflen);
  		read_unlock(&kclist_lock);
  		if (copy_to_user(buffer, elf_buf + *fpos, tsz)) {
  			kfree(elf_buf);
  			return -EFAULT;
  		}
  		kfree(elf_buf);
  		buflen -= tsz;
  		*fpos += tsz;
  		buffer += tsz;
  		acc += tsz;
  
  		/* leave now if filled buffer already */
  		if (buflen == 0)
  			return acc;
  	} else
  		read_unlock(&kclist_lock);
  
  	/*
  	 * Check to see if our file offset matches with any of
  	 * the addresses in the elf_phdr on our list.
  	 */
  	start = kc_offset_to_vaddr(*fpos - elf_buflen);
  	if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen)
  		tsz = buflen;
  		
  	while (buflen) {
  		struct kcore_list *m;
  
  		read_lock(&kclist_lock);
2ef43ec77   KAMEZAWA Hiroyuki   kcore: use usual ...
496
  		list_for_each_entry(m, &kclist_head, list) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
497
498
499
500
  			if (start >= m->addr && start < (m->addr+m->size))
  				break;
  		}
  		read_unlock(&kclist_lock);
4fd2c20d9   Dan Carpenter   kcore: fix test f...
501
  		if (&m->list == &kclist_head) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
503
  			if (clear_user(buffer, tsz))
  				return -EFAULT;
737326aa5   Ard Biesheuvel   fs/proc: kcore: u...
504
  		} else if (m->type == KCORE_VMALLOC) {
f5beeb185   Jiri Olsa   fs/proc/kcore.c: ...
505
  			vread(buf, (char *)start, tsz);
73d7c33e8   KAMEZAWA Hiroyuki   kcore: /proc/kcor...
506
  			/* we have to zero-fill user buffer even if no read */
f5beeb185   Jiri Olsa   fs/proc/kcore.c: ...
507
  			if (copy_to_user(buffer, buf, tsz))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
508
  				return -EFAULT;
f4d6e4598   Jia Zhang   vfs/proc/kcore, x...
509
510
511
512
  		} else if (m->type == KCORE_USER) {
  			/* User page is handled prior to normal kernel page: */
  			if (copy_to_user(buffer, (char *)start, tsz))
  				return -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
513
514
  		} else {
  			if (kern_addr_valid(start)) {
df04abfd1   Jiri Olsa   fs/proc/kcore.c: ...
515
516
517
518
  				/*
  				 * Using bounce buffer to bypass the
  				 * hardened user copy kernel text checks.
  				 */
7e54b5828   Heiko Carstens   fs/proc/kcore.c: ...
519
520
521
522
523
  				if (probe_kernel_read(buf, (void *) start, tsz)) {
  					if (clear_user(buffer, tsz))
  						return -EFAULT;
  				} else {
  					if (copy_to_user(buffer, buf, tsz))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
  						return -EFAULT;
  				}
  			} else {
  				if (clear_user(buffer, tsz))
  					return -EFAULT;
  			}
  		}
  		buflen -= tsz;
  		*fpos += tsz;
  		buffer += tsz;
  		acc += tsz;
  		start += tsz;
  		tsz = (buflen > PAGE_SIZE ? PAGE_SIZE : buflen);
  	}
  
  	return acc;
  }
97ce5d6dc   Alexey Dobriyan   proc: move all /p...
541

3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
542
543
544
545
546
  
  static int open_kcore(struct inode *inode, struct file *filp)
  {
  	if (!capable(CAP_SYS_RAWIO))
  		return -EPERM;
f5beeb185   Jiri Olsa   fs/proc/kcore.c: ...
547
548
549
550
  
  	filp->private_data = kmalloc(PAGE_SIZE, GFP_KERNEL);
  	if (!filp->private_data)
  		return -ENOMEM;
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
551
552
  	if (kcore_need_update)
  		kcore_update_ram();
0d4c36a9b   KAMEZAWA Hiroyuki   /proc/kcore: upda...
553
  	if (i_size_read(inode) != proc_root_kcore->size) {
5955102c9   Al Viro   wrappers for ->i_...
554
  		inode_lock(inode);
0d4c36a9b   KAMEZAWA Hiroyuki   /proc/kcore: upda...
555
  		i_size_write(inode, proc_root_kcore->size);
5955102c9   Al Viro   wrappers for ->i_...
556
  		inode_unlock(inode);
0d4c36a9b   KAMEZAWA Hiroyuki   /proc/kcore: upda...
557
  	}
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
558
559
  	return 0;
  }
f5beeb185   Jiri Olsa   fs/proc/kcore.c: ...
560
561
562
563
564
  static int release_kcore(struct inode *inode, struct file *file)
  {
  	kfree(file->private_data);
  	return 0;
  }
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
565
566
567
568
  
  static const struct file_operations proc_kcore_operations = {
  	.read		= read_kcore,
  	.open		= open_kcore,
f5beeb185   Jiri Olsa   fs/proc/kcore.c: ...
569
  	.release	= release_kcore,
ceff1a770   Dave Anderson   /proc/kcore: fix ...
570
  	.llseek		= default_llseek,
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
571
  };
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
572
573
574
575
576
577
578
579
580
581
582
583
584
  /* just remember that we have to update kcore */
  static int __meminit kcore_callback(struct notifier_block *self,
  				    unsigned long action, void *arg)
  {
  	switch (action) {
  	case MEM_ONLINE:
  	case MEM_OFFLINE:
  		write_lock(&kclist_lock);
  		kcore_need_update = 1;
  		write_unlock(&kclist_lock);
  	}
  	return NOTIFY_OK;
  }
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
585

3c743a7f7   Andrew Morton   fs/proc/kcore.c: ...
586
587
588
589
  static struct notifier_block kcore_callback_nb __meminitdata = {
  	.notifier_call = kcore_callback,
  	.priority = 0,
  };
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
590

a0614da88   KAMEZAWA Hiroyuki   kcore: register v...
591
  static struct kcore_list kcore_vmalloc;
9492587cf   KAMEZAWA Hiroyuki   kcore: register t...
592
593
594
595
596
597
598
599
  #ifdef CONFIG_ARCH_PROC_KCORE_TEXT
  static struct kcore_list kcore_text;
  /*
   * If defined, special segment is used for mapping kernel text instead of
   * direct-map area. We need to create special TEXT section.
   */
  static void __init proc_kcore_text_init(void)
  {
36e15263a   Wu Fengguang   kcore: add _text ...
600
  	kclist_add(&kcore_text, _text, _end - _text, KCORE_TEXT);
9492587cf   KAMEZAWA Hiroyuki   kcore: register t...
601
602
603
604
605
606
  }
  #else
  static void __init proc_kcore_text_init(void)
  {
  }
  #endif
81ac3ad90   KAMEZAWA Hiroyuki   kcore: register m...
607
608
609
610
611
612
613
  #if defined(CONFIG_MODULES) && defined(MODULES_VADDR)
  /*
   * MODULES_VADDR has no intersection with VMALLOC_ADDR.
   */
  struct kcore_list kcore_modules;
  static void __init add_modules_range(void)
  {
bf3e26924   Baoquan He   fs/proc/kcore.c: ...
614
615
  	if (MODULES_VADDR != VMALLOC_START && MODULES_END != VMALLOC_END) {
  		kclist_add(&kcore_modules, (void *)MODULES_VADDR,
81ac3ad90   KAMEZAWA Hiroyuki   kcore: register m...
616
  			MODULES_END - MODULES_VADDR, KCORE_VMALLOC);
bf3e26924   Baoquan He   fs/proc/kcore.c: ...
617
  	}
81ac3ad90   KAMEZAWA Hiroyuki   kcore: register m...
618
619
620
621
622
623
  }
  #else
  static void __init add_modules_range(void)
  {
  }
  #endif
97ce5d6dc   Alexey Dobriyan   proc: move all /p...
624
625
  static int __init proc_kcore_init(void)
  {
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
626
627
  	proc_root_kcore = proc_create("kcore", S_IRUSR, NULL,
  				      &proc_kcore_operations);
90396f96b   KAMEZAWA Hiroyuki   kcore: more fixes...
628
  	if (!proc_root_kcore) {
87ebdc00e   Andrew Morton   fs/proc: clean up...
629
630
  		pr_err("couldn't create /proc/kcore
  ");
90396f96b   KAMEZAWA Hiroyuki   kcore: more fixes...
631
632
  		return 0; /* Always returns 0. */
  	}
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
633
  	/* Store text area if it's special */
9492587cf   KAMEZAWA Hiroyuki   kcore: register t...
634
  	proc_kcore_text_init();
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
635
  	/* Store vmalloc area */
a0614da88   KAMEZAWA Hiroyuki   kcore: register v...
636
637
  	kclist_add(&kcore_vmalloc, (void *)VMALLOC_START,
  		VMALLOC_END - VMALLOC_START, KCORE_VMALLOC);
81ac3ad90   KAMEZAWA Hiroyuki   kcore: register m...
638
  	add_modules_range();
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
639
640
  	/* Store direct-map area from physical memory map */
  	kcore_update_ram();
3c743a7f7   Andrew Morton   fs/proc/kcore.c: ...
641
  	register_hotmemory_notifier(&kcore_callback_nb);
3089aa1b0   KAMEZAWA Hiroyuki   kcore: use regist...
642

97ce5d6dc   Alexey Dobriyan   proc: move all /p...
643
644
  	return 0;
  }
abaf3787a   Paul Gortmaker   fs/proc: don't us...
645
  fs_initcall(proc_kcore_init);