summaryrefslogtreecommitdiff
path: root/kexec/kexec-iomem.c
blob: d00b6b61ee0cbdf9eb1fdf27c7dec6a5336574e2 (plain)
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
#define _GNU_SOURCE
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include "kexec.h"
#include "crashdump.h"

/*
 * kexec_iomem_for_each_line()
 *
 * Iterate over each line in the file returned by proc_iomem(). If match is
 * NULL or if the line matches with our match-pattern then call the
 * callback if non-NULL.
 * If match is NULL, callback should return a negative if error.
 * Otherwise the interation goes on, incrementing nr but only if callback
 * returns 0 (matched).
 *
 * Return the number of lines matched.
 */

int kexec_iomem_for_each_line(char *match,
			      int (*callback)(void *data,
					      int nr,
					      char *str,
					      unsigned long long base,
					      unsigned long long length),
			      void *data)
{
	const char *iomem = proc_iomem();
	char line[MAX_LINE];
	FILE *fp;
	unsigned long long start, end, size;
	char *str;
	int consumed;
	int count;
	int nr = 0, ret;

	if (!callback)
		return nr;

	fp = fopen(iomem, "r");
	if (!fp)
		die("Cannot open %s\n", iomem);

	while(fgets(line, sizeof(line), fp) != 0) {
		count = sscanf(line, "%llx-%llx : %n", &start, &end, &consumed);
		if (count != 2)
			continue;
		str = line + consumed;
		size = end - start + 1;
		if (!match || memcmp(str, match, strlen(match)) == 0) {
			ret = callback(data, nr, str, start, size);
			if (ret < 0)
				break;
			else if (ret == 0)
				nr++;
		}
	}

	fclose(fp);

	return nr;
}

static int kexec_iomem_single_callback(void *data, int nr,
				       char *UNUSED(str),
				       unsigned long long base,
				       unsigned long long length)
{
	struct memory_range *range = data;

	if (nr == 0) {
		range->start = base;
		range->end = base + length - 1;
	}

	return 0;
}

int parse_iomem_single(char *str, uint64_t *start, uint64_t *end)
{
	struct memory_range range;
	int ret;

	memset(&range, 0, sizeof(range));

	ret = kexec_iomem_for_each_line(str,
	                                kexec_iomem_single_callback, &range);

	if (ret == 1) {
		if (start)
			*start = range.start;
		if (end)
			*end = range.end;

		ret = 0;
	}
	else
		ret = -1;

	return ret;
}