summaryrefslogtreecommitdiffstats
path: root/arch/s390/kernel/mem_detect.c
blob: 19b4568f4ceec4ada5aa86db7251a2b8dc0afa75 (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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
 * Copyright IBM Corp. 2008, 2009
 *
 * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/ipl.h>
#include <asm/sclp.h>
#include <asm/setup.h>

#define ADDR2G (1ULL << 31)

static void find_memory_chunks(struct mem_chunk chunk[])
{
	unsigned long long memsize, rnmax, rzm;
	unsigned long addr = 0, size;
	int i = 0, type;

	rzm = sclp_get_rzm();
	rnmax = sclp_get_rnmax();
	memsize = rzm * rnmax;
	if (!rzm)
		rzm = 1ULL << 17;
	if (sizeof(long) == 4) {
		rzm = min(ADDR2G, rzm);
		memsize = memsize ? min(ADDR2G, memsize) : ADDR2G;
	}
	do {
		size = 0;
		type = tprot(addr);
		do {
			size += rzm;
			if (memsize && addr + size >= memsize)
				break;
		} while (type == tprot(addr + size));
		if (type == CHUNK_READ_WRITE || type == CHUNK_READ_ONLY) {
			chunk[i].addr = addr;
			chunk[i].size = size;
			chunk[i].type = type;
			i++;
		}
		addr += size;
	} while (addr < memsize && i < MEMORY_CHUNKS);
}

void detect_memory_layout(struct mem_chunk chunk[])
{
	unsigned long flags, cr0;

	memset(chunk, 0, MEMORY_CHUNKS * sizeof(struct mem_chunk));
	/* Disable IRQs, DAT and low address protection so tprot does the
	 * right thing and we don't get scheduled away with low address
	 * protection disabled.
	 */
	flags = __arch_local_irq_stnsm(0xf8);
	__ctl_store(cr0, 0, 0);
	__ctl_clear_bit(0, 28);
	find_memory_chunks(chunk);
	__ctl_load(cr0, 0, 0);
	arch_local_irq_restore(flags);
}
EXPORT_SYMBOL(detect_memory_layout);

/*
 * Create memory hole with given address, size, and type
 */
void create_mem_hole(struct mem_chunk chunks[], unsigned long addr,
		     unsigned long size, int type)
{
	unsigned long start, end, new_size;
	int i;

	for (i = 0; i < MEMORY_CHUNKS; i++) {
		if (chunks[i].size == 0)
			continue;
		if (addr + size < chunks[i].addr)
			continue;
		if (addr >= chunks[i].addr + chunks[i].size)
			continue;
		start = max(addr, chunks[i].addr);
		end = min(addr + size, chunks[i].addr + chunks[i].size);
		new_size = end - start;
		if (new_size == 0)
			continue;
		if (start == chunks[i].addr &&
		    end == chunks[i].addr + chunks[i].size) {
			/* Remove chunk */
			chunks[i].type = type;
		} else if (start == chunks[i].addr) {
			/* Make chunk smaller at start */
			if (i >= MEMORY_CHUNKS - 1)
				panic("Unable to create memory hole");
			memmove(&chunks[i + 1], &chunks[i],
				sizeof(struct mem_chunk) *
				(MEMORY_CHUNKS - (i + 1)));
			chunks[i + 1].addr = chunks[i].addr + new_size;
			chunks[i + 1].size = chunks[i].size - new_size;
			chunks[i].size = new_size;
			chunks[i].type = type;
			i += 1;
		} else if (end == chunks[i].addr + chunks[i].size) {
			/* Make chunk smaller at end */
			if (i >= MEMORY_CHUNKS - 1)
				panic("Unable to create memory hole");
			memmove(&chunks[i + 1], &chunks[i],
				sizeof(struct mem_chunk) *
				(MEMORY_CHUNKS - (i + 1)));
			chunks[i + 1].addr = start;
			chunks[i + 1].size = new_size;
			chunks[i + 1].type = type;
			chunks[i].size -= new_size;
			i += 1;
		} else {
			/* Create memory hole */
			if (i >= MEMORY_CHUNKS - 2)
				panic("Unable to create memory hole");
			memmove(&chunks[i + 2], &chunks[i],
				sizeof(struct mem_chunk) *
				(MEMORY_CHUNKS - (i + 2)));
			chunks[i + 1].addr = addr;
			chunks[i + 1].size = size;
			chunks[i + 1].type = type;
			chunks[i + 2].addr = addr + size;
			chunks[i + 2].size =
				chunks[i].addr + chunks[i].size - (addr + size);
			chunks[i + 2].type = chunks[i].type;
			chunks[i].size = addr - chunks[i].addr;
			i += 2;
		}
	}
}