HACK
[asbestos.git] / stage2 / mm.c
blob4cb337b77a8eda0a0d9f65798fa2aad7a34887e1
1 /* mm.c - memory management
3 Copyright (C) 2010-2011 Hector Martin "marcan" <hector@marcansoft.com>
5 This code is licensed to you under the terms of the GNU GPL, version 2;
6 see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
7 */
9 #include "types.h"
10 #include "debug.h"
11 #include "mm.h"
12 #include "lv1call.h"
13 #include "string.h"
14 #include "repo.h"
16 #define MSR_IR (1UL<<5)
17 #define MSR_DR (1UL<<4)
19 #define NUM_SLB 64
21 int seg_idx = 1;
22 u64 vas_id;
24 #define NUM_MMIO 128
26 int mm_inited = 0;
28 // highmem size is aligned to 16MB blocks
29 #define HIGHMEM_PBITS 24
30 #define HIGHMEM_PALIGN (1<<HIGHMEM_PBITS)
32 // highmem pointer is aligned to 64kB
33 #define HIGHMEM_ALIGN 65536
35 u64 mm_bootmem_size;
36 u64 mm_highmem_addr;
37 u64 mm_highmem_size;
39 static u64 highmem_ptr;
41 struct mmio_region {
42 u64 start;
43 u32 end;
46 struct mmio_region mmios[NUM_MMIO];
48 int mm_addmmio(u64 start, u32 size)
50 int i;
51 u64 end = start+size;
53 if (!size)
54 return -1;
56 if ((start & 0xfff) || (end & 0xfff)) {
57 printf("WARNING: MMIO region is not aligned (0x%016lx 0x%x)", start, size);
58 start &= ~0xfff;
59 start = (start+0xfff)&~0xfff;
62 if (!start)
63 return -1;
65 for (i=0; i<NUM_MMIO; i++) {
66 if (mmios[i].start == 0) {
67 mmios[i].start = start;
68 mmios[i].end = end;
69 return 0;
72 printf("WARNING: Maximum number of MMIO regions exceeded\n");
73 return -1;
76 int mm_delmmio(u64 start)
78 int i;
79 start &= ~0xfff;
80 for (i=0; i<NUM_MMIO; i++) {
81 if (mmios[i].start == start) {
82 mmios[i].start = 0;
83 mmios[i].end = 0;
84 return 0;
87 printf("WARNING: Tried to delete nonexistent MMIO region 0x%016lx\n", start);
88 return -1;
91 int mm_clrmmio(void)
93 memset(mmios, 0, sizeof(mmios));
94 return 0;
97 int mm_ismmio(u64 addr)
99 int i;
100 for (i=0; i<NUM_MMIO; i++) {
101 if (mmios[i].start <= addr && mmios[i].end > addr)
102 return 1;
104 return 0;
107 int mm_loadseg(u64 addr)
109 if (!mm_inited)
110 return 0;
111 u64 esid = addr >> 28;
113 int index = 0;
115 if (esid) {
116 index = seg_idx;
117 seg_idx++;
118 if (seg_idx >= NUM_SLB)
119 seg_idx = 1;
122 u64 rs, rb;
124 rs = (esid<<12) | 0x400;
125 rb = (esid<<28) | (1<<27) | index;
127 asm("slbmte %0,%1 ; isync" :: "r"(rs), "r"(rb));
128 printf("SLB[%d] = %016lx %016lx (caused by %016lx)\n", index, rs, rb, addr);
129 return 1;
132 int mm_loadhtab(u64 addr)
134 if (!mm_inited)
135 return 0;
136 s64 result;
138 //printf("Load HTAB for %016lx\n", addr);
140 addr &= ~0xfff;
142 u32 wimg = 0x2;
143 if (mm_ismmio(addr)) {
144 wimg = 0x5;
147 u64 hpte_v;
148 u64 hpte_r;
149 u64 inserted_index, evicted_v, evicted_r;
151 hpte_v = (addr>>23)<<7;
152 hpte_v |= 0x1;
153 hpte_r = addr|(wimg<<3);
155 u64 htab_hash_mask = ((256*1024)>>7)-1;
157 u64 hash = (addr >> 28) ^ ((addr & 0x0fffffffUL) >> 12);
158 u64 hpte_group = ((hash & htab_hash_mask) * 8);
160 result = lv1_insert_htab_entry(0, hpte_group,
161 hpte_v, hpte_r,
162 0x10, 0,
163 &inserted_index,
164 &evicted_v, &evicted_r);
165 if (result) {
166 printf("Failed to insert HTAB entry:\n");
167 printf("hpte_group=0x%lx hpte_v=0x%016lx hpte_r=0x%016lx\n", hpte_group, hpte_v, hpte_r);
168 printf("result=%ld inserted_index=%lx evicted_v=%lx evicted_r=%lx\n", result, inserted_index, evicted_v, evicted_r);
169 return 0;
170 } else {
171 return 1;
175 void mm_init(void)
177 s32 result;
179 printf("Initializing memory management...\n");
181 memset(mmios, 0, sizeof(mmios));
182 u64 act_htab_size;
183 result = lv1_construct_virtual_address_space(18, 0, 0, &vas_id, &act_htab_size);
184 if (result)
185 fatal("Could not construct VAS\n");
186 printf("New VAS ID: %ld\n", vas_id);
188 result = lv1_select_virtual_address_space(vas_id);
189 if (result)
190 fatal("Could not switch VAS\n");
192 mm_inited = 1;
194 asm("slbia");
195 mm_loadseg(0);
197 u64 msr;
198 asm("mfmsr %0" : "=r"(msr));
199 msr |= MSR_IR | MSR_DR;
200 asm("isync ; mtmsrd %0,0 ; isync" :: "r"(msr));
202 printf("MMU initialized, now running in virtual mode\n");
204 u64 ppe_id, lpar_id;
206 result = lv1_get_logical_ppe_id(&ppe_id);
207 if (result)
208 fatal("could not get PPE ID");
210 result = lv1_get_logical_partition_id(&lpar_id);
211 if (result)
212 fatal("could not get LPAR ID");
214 u64 rm_size, rgntotal, v2;
216 result = lv1_read_repository_node(lpar_id, FIELD_FIRST("bi",0),
217 FIELD("pu",0), ppe_id, FIELD("rm_size",0),
218 &rm_size, &v2);
219 if (result)
220 fatal("could not get bootmem size");
222 result = lv1_read_repository_node(lpar_id, FIELD_FIRST("bi",0),
223 FIELD("rgntotal",0), 0, 0, &rgntotal, &v2);
224 if (result)
225 fatal("could not get total region size");
227 printf("Region size = %ld bytes (%ldMB)\n", rgntotal, rgntotal>>20);
228 printf("Bootmem = %ld bytes (%ldMB)\n", rm_size, rm_size>>20);
230 mm_bootmem_size = rm_size;
232 u64 exp_size = rgntotal - rm_size;
233 exp_size &= ~(HIGHMEM_PALIGN-1);
235 while (exp_size) {
236 u64 muid, addr;
237 result = lv1_allocate_memory(exp_size, HIGHMEM_PBITS, 0, 0, &addr, &muid);
238 if (result == 0) {
239 mm_highmem_addr = addr;
240 mm_highmem_size = exp_size;
241 break;
242 } else {
243 printf("Allocation of %ld bytes failed (%d), trying again...\n", exp_size, result);
244 exp_size -= HIGHMEM_PALIGN;
248 if (!exp_size)
249 fatal("could not allocate highmem");
251 highmem_ptr = mm_highmem_addr;
253 printf("Highmem = %ld bytes (%ldMB) at 0x%lx\n", mm_highmem_size, mm_highmem_size>>20, mm_highmem_addr);
256 void mm_set_highmem_repo_info(void)
258 s32 result;
259 u64 lpar_id;
261 result = lv1_get_logical_partition_id(&lpar_id);
262 if (result)
263 fatal("could not get LPAR ID");
265 result = set_repository_node(FIELD_FIRST("highmem", 0),
266 FIELD("region", 0),
267 FIELD("base", 0),
269 mm_highmem_addr, 0);
271 if (result)
272 fatal("error setting rep node for highmem base");
274 result = set_repository_node(FIELD_FIRST("highmem", 0),
275 FIELD("region", 0),
276 FIELD("size", 0),
278 mm_highmem_size, 0);
280 if (result)
281 fatal("error setting rep node for highmem size");
283 result = set_repository_node(FIELD_FIRST("highmem", 0),
284 FIELD("region", 0),
285 FIELD("count", 0),
287 1, 0);
289 if (result)
290 fatal("error setting rep node for highmem region count");
292 printf("Repository highmem values set to 0x%lx, 0x%lx\n",
293 mm_highmem_addr, mm_highmem_size);
296 void mm_shutdown_highmem(void)
298 s32 result;
300 printf("Freeing highmem...\n");
301 result = lv1_release_memory(mm_highmem_addr);
303 if (result)
304 fatal("could not free highmem");
306 printf("Highmem is gone\n");
309 void mm_shutdown(void)
311 s64 result;
312 printf("Shutting down memory management...\n");
314 u64 msr;
315 asm("mfmsr %0" : "=r"(msr));
316 msr &= ~(MSR_IR | MSR_DR);
317 asm("mtmsrd %0,0 ; isync" :: "r"(msr));
319 printf("Now running in real mode\n");
321 result = lv1_select_virtual_address_space(0);
322 if (result)
323 fatal("Could not switch to VAS 0\n");
325 result = lv1_destruct_virtual_address_space(vas_id);
326 if (result)
327 fatal("Could not destroy VAS\n");
329 printf("Destroyed VAS %ld\n", vas_id);
330 vas_id = 0;
332 mm_inited = 0;
335 void mm_highmem_reserve(size_t size)
337 if (size > mm_highmem_freesize())
338 fatal("mm_highmem_reserve: too large");
340 highmem_ptr += size + HIGHMEM_ALIGN - 1;
341 highmem_ptr &= ~(HIGHMEM_ALIGN-1);
344 void *mm_highmem_freestart(void)
346 return (void*)highmem_ptr;
349 size_t mm_highmem_freesize(void)
351 return mm_highmem_addr + mm_highmem_size - highmem_ptr;
354 u64 mm_addr_to_kernel(void *addr)
356 u64 a = (u64)addr;
358 if (a > mm_bootmem_size)
359 if (a >= mm_highmem_addr && a < (mm_highmem_addr+mm_highmem_size))
360 return a - mm_highmem_addr + mm_bootmem_size;
361 else
362 fatal("mm_addr_to_kernel: out of bounds");
363 else
364 return a;
367 void sync_before_exec(void *addr, int len)
369 u64 p = ((u64)addr) & ~0x1f;
370 u64 end = ((u64)addr) + len;
372 while (p < end) {
373 asm("dcbst 0,%0 ; sync ; icbi 0,%0" :: "r"(p));
374 p += 32;
377 asm("sync ; isync");