1/*
2 * Copyright 2004-2008, Axel D��rfler, axeld@pinc-software.de.
3 * Based on code written by Travis Geiselbrecht for NewOS.
4 *
5 * Distributed under the terms of the MIT License.
6 */
7
8
9#include "mmu.h"
10
11#include <boot/platform.h>
12#include <boot/stdio.h>
13#include <boot/kernel_args.h>
14#include <boot/stage2.h>
15#include <arch/cpu.h>
16#include <arch_kernel.h>
17#include <platform/openfirmware/openfirmware.h>
18#ifdef __ARM__
19#include <arm_mmu.h>
20#endif
21#include <kernel.h>
22
23#include <board_config.h>
24
25#include <OS.h>
26
27#include <string.h>
28
29/*! This implements boot loader mmu support for Book-E PowerPC,
30	which only support a limited number of TLB and no hardware page table walk,
31	and does not standardize at how to use the mmu, requiring vendor-specific
32	code.
33
34	Like Linux, we pin one of the TLB entries to a fixed translation,
35	however we use it differently.
36	cf. http://kernel.org/doc/ols/2003/ols2003-pages-340-350.pdf
37
38	This translation uses a single large page (16 or 256MB are possible) which
39	directly maps the begining of the RAM.
40	We use it as a linear space to allocate from at boot time,
41	loading the kernel and modules into it, and other required data.
42	Near the end we reserve a page table (it doesn't need to be aligned),
43	but unlike Linux we use the same globally hashed page table that is
44	implemented by Classic PPC, to allow reusing code if possible, and also
45	to limit fragmentation which would occur by using a tree-based page table.
46	However this means we might actually run out of page table entries in case
47	of too many collisions.
48
49	The kernel will then create areas to cover this already-mapped space.
50	This also means proper permission bits (RWX) will not be applicable to
51	separate areas which are enclosed by this mapping.
52
53	We put the kernel stack at the end of the mapping so that the guard page is
54	outsite and thus unmapped. (we don't support SMP)
55*/
56
57/*!	The (physical) memory layout of the boot loader is currently as follows:
58	 0x00000000			kernel
59	 0x00400000			...modules
60
61	 (at least on the Sam460ex U-Boot; we'll need to accomodate other setups)
62	 0x01000000			boot loader
63	 0x01800000			Flattened Device Tree
64	 0x01900000			boot.tgz (= ramdisk)
65	 0x02000000			boot loader uimage
66
67
68					boot loader heap (should be discarded later on)
69	 ... 256M-Kstack		page hash table
70	 ... 256M			kernel stack
71					kernel stack guard page
72
73	The kernel is mapped at KERNEL_BASE, all other stuff mapped by the
74	loader (kernel args, modules, driver settings, ...) comes after
75	0x80040000 which means that there is currently only 4 MB reserved for
76	the kernel itself (see kMaxKernelSize). FIXME: downsize kernel_ppc
77*/
78
79
80int32 of_address_cells(int package);
81int32 of_size_cells(int package);
82
83extern bool gIs440;
84// XXX:use a base class for Book-E support?
85extern status_t arch_mmu_setup_pinned_tlb_amcc440(phys_addr_t totalRam,
86	size_t &tableSize, size_t &tlbSize);
87
88#define TRACE_MMU
89#ifdef TRACE_MMU
90#	define TRACE(x) dprintf x
91#else
92#	define TRACE(x) ;
93#endif
94
95#define TRACE_MEMORY_MAP
96	// Define this to print the memory map to serial debug.
97
98static const size_t kMaxKernelSize = 0x400000;		// 4 MB for the kernel
99
100static addr_t sNextPhysicalAddress = kMaxKernelSize; //will be set by mmu_init
101static addr_t sNextVirtualAddress = KERNEL_BASE + kMaxKernelSize;
102//static addr_t sMaxVirtualAddress = KERNEL_BASE + kMaxKernelSize;
103
104// working page directory and page table
105static void *sPageTable = 0 ;
106
107
108static addr_t
109get_next_virtual_address(size_t size)
110{
111	addr_t address = sNextVirtualAddress;
112	sNextPhysicalAddress += size;
113	sNextVirtualAddress += size;
114
115	return address;
116}
117
118
119#if 0
120static addr_t
121get_next_physical_address(size_t size)
122{
123	addr_t address = sNextPhysicalAddress;
124	sNextPhysicalAddress += size;
125	sNextVirtualAddress += size;
126
127	return address;
128}
129#endif
130
131
132//	#pragma mark -
133
134
135extern "C" addr_t
136mmu_map_physical_memory(addr_t physicalAddress, size_t size, uint32 flags)
137{
138	panic("WRITEME");
139	return 0;
140}
141
142
143/*!	Sets up the final and kernel accessible GDT and IDT tables.
144	BIOS calls won't work any longer after this function has
145	been called.
146*/
147extern "C" void
148mmu_init_for_kernel(void)
149{
150	TRACE(("mmu_init_for_kernel\n"));
151
152	// TODO: remove all U-Boot TLB
153
154#ifdef TRACE_MEMORY_MAP
155	{
156		uint32 i;
157
158		dprintf("phys memory ranges:\n");
159		for (i = 0; i < gKernelArgs.num_physical_memory_ranges; i++) {
160			dprintf("    base 0x%" B_PRIxPHYSADDR
161				", length 0x%" B_PRIxPHYSADDR "\n",
162				gKernelArgs.physical_memory_range[i].start,
163				gKernelArgs.physical_memory_range[i].size);
164		}
165
166		dprintf("allocated phys memory ranges:\n");
167		for (i = 0; i < gKernelArgs.num_physical_allocated_ranges; i++) {
168			dprintf("    base 0x%" B_PRIxPHYSADDR
169				", length 0x%" B_PRIxPHYSADDR "\n",
170				gKernelArgs.physical_allocated_range[i].start,
171				gKernelArgs.physical_allocated_range[i].size);
172		}
173
174		dprintf("allocated virt memory ranges:\n");
175		for (i = 0; i < gKernelArgs.num_virtual_allocated_ranges; i++) {
176			dprintf("    base 0x%" B_PRIxPHYSADDR
177				", length 0x%" B_PRIxPHYSADDR "\n",
178				gKernelArgs.virtual_allocated_range[i].start,
179				gKernelArgs.virtual_allocated_range[i].size);
180		}
181	}
182#endif
183}
184
185
186//TODO:move this to generic/ ?
187static status_t
188find_physical_memory_ranges(phys_addr_t &total)
189{
190	int memory = -1;
191	int package;
192	dprintf("checking for memory...\n");
193	if (of_getprop(gChosen, "memory", &memory, sizeof(int)) == OF_FAILED)
194		package = of_finddevice("/memory");
195	else
196		package = of_instance_to_package(memory);
197	if (package == OF_FAILED)
198		return B_ERROR;
199
200	total = 0;
201
202	// Memory base addresses are provided in 32 or 64 bit flavors
203	// #address-cells and #size-cells matches the number of 32-bit 'cells'
204	// representing the length of the base address and size fields
205	int root = of_finddevice("/");
206	int32 regAddressCells = of_address_cells(root);
207	int32 regSizeCells = of_size_cells(root);
208	if (regAddressCells == OF_FAILED || regSizeCells == OF_FAILED) {
209		dprintf("finding base/size length counts failed, assume 32-bit.\n");
210		regAddressCells = 1;
211		regSizeCells = 1;
212	}
213
214	// NOTE : Size Cells of 2 is possible in theory... but I haven't seen it yet.
215	if (regAddressCells > 2 || regSizeCells > 1) {
216		panic("%s: Unsupported OpenFirmware cell count detected.\n"
217		"Address Cells: %" B_PRId32 "; Size Cells: %" B_PRId32
218		" (CPU > 64bit?).\n", __func__, regAddressCells, regSizeCells);
219		return B_ERROR;
220	}
221
222	// On 64-bit PowerPC systems (G5), our mem base range address is larger
223	if (regAddressCells == 2) {
224		struct of_region<uint64, uint32> regions[64];
225		int count = of_getprop(package, "reg", regions, sizeof(regions));
226		if (count == OF_FAILED)
227			count = of_getprop(memory, "reg", regions, sizeof(regions));
228		if (count == OF_FAILED)
229			return B_ERROR;
230		count /= sizeof(regions[0]);
231
232		for (int32 i = 0; i < count; i++) {
233			if (regions[i].size <= 0) {
234				dprintf("%ld: empty region\n", i);
235				continue;
236			}
237			dprintf("%" B_PRIu32 ": base = %" B_PRIu64 ","
238				"size = %" B_PRIu32 "\n", i, regions[i].base, regions[i].size);
239
240			total += regions[i].size;
241
242			if (insert_physical_memory_range((addr_t)regions[i].base,
243					regions[i].size) != B_OK) {
244				dprintf("cannot map physical memory range "
245					"(num ranges = %" B_PRIu32 ")!\n",
246					gKernelArgs.num_physical_memory_ranges);
247				return B_ERROR;
248			}
249		}
250		return B_OK;
251	}
252
253	// Otherwise, normal 32-bit PowerPC G3 or G4 have a smaller 32-bit one
254	struct of_region<uint32, uint32> regions[64];
255	int count = of_getprop(package, "reg", regions, sizeof(regions));
256	if (count == OF_FAILED)
257		count = of_getprop(memory, "reg", regions, sizeof(regions));
258	if (count == OF_FAILED)
259		return B_ERROR;
260	count /= sizeof(regions[0]);
261
262	for (int32 i = 0; i < count; i++) {
263		if (regions[i].size <= 0) {
264			dprintf("%ld: empty region\n", i);
265			continue;
266		}
267		dprintf("%" B_PRIu32 ": base = %" B_PRIu32 ","
268			"size = %" B_PRIu32 "\n", i, regions[i].base, regions[i].size);
269
270		total += regions[i].size;
271
272		if (insert_physical_memory_range((addr_t)regions[i].base,
273				regions[i].size) != B_OK) {
274			dprintf("cannot map physical memory range "
275				"(num ranges = %" B_PRIu32 ")!\n",
276				gKernelArgs.num_physical_memory_ranges);
277			return B_ERROR;
278		}
279	}
280
281	return B_OK;
282}
283
284
285extern "C" void
286mmu_init(void* fdt)
287{
288	size_t tableSize, tlbSize;
289	status_t err;
290	TRACE(("mmu_init\n"));
291
292	// get map of physical memory (fill in kernel_args structure)
293
294	phys_addr_t total;
295	if (find_physical_memory_ranges(total) != B_OK) {
296		dprintf("Error: could not find physical memory ranges!\n");
297		return /*B_ERROR*/;
298	}
299	dprintf("total physical memory = %" B_PRId64 "MB\n", total / (1024 * 1024));
300
301	// XXX: ugly, and wrong, there are several 440 mmu types... FIXME
302	if (gIs440) {
303		err = arch_mmu_setup_pinned_tlb_amcc440(total, tableSize, tlbSize);
304		dprintf("setup_pinned_tlb: 0x%08lx table %zdMB tlb %zdMB\n",
305			err, tableSize / (1024 * 1024), tlbSize / (1024 * 1024));
306	} else {
307		panic("Unknown MMU type!");
308		return;
309	}
310
311	// remember the start of the allocated physical pages
312	gKernelArgs.physical_allocated_range[0].start
313		= gKernelArgs.physical_memory_range[0].start;
314	gKernelArgs.physical_allocated_range[0].size = tlbSize;
315	gKernelArgs.num_physical_allocated_ranges = 1;
316
317	// Save the memory we've virtually allocated (for the kernel and other
318	// stuff)
319	gKernelArgs.virtual_allocated_range[0].start = KERNEL_BASE;
320	gKernelArgs.virtual_allocated_range[0].size
321		= tlbSize + KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE;
322	gKernelArgs.num_virtual_allocated_ranges = 1;
323
324
325	sPageTable = (void *)(tlbSize - tableSize - KERNEL_STACK_SIZE);
326		// we put the page table near the end of the pinned TLB
327	TRACE(("page table at 0x%p to 0x%p\n", sPageTable,
328		(uint8 *)sPageTable + tableSize));
329
330	// map in a kernel stack
331	gKernelArgs.cpu_kstack[0].start = (addr_t)(tlbSize - KERNEL_STACK_SIZE);
332	gKernelArgs.cpu_kstack[0].size = KERNEL_STACK_SIZE
333		+ KERNEL_STACK_GUARD_PAGES * B_PAGE_SIZE;
334
335	TRACE(("kernel stack at 0x%Lx to 0x%Lx\n", gKernelArgs.cpu_kstack[0].start,
336		gKernelArgs.cpu_kstack[0].start + gKernelArgs.cpu_kstack[0].size));
337
338#ifdef __ARM__
339	init_page_directory();
340
341	// map the page directory on the next vpage
342	gKernelArgs.arch_args.vir_pgdir = mmu_map_physical_memory(
343		(addr_t)sPageDirectory, MMU_L1_TABLE_SIZE, kDefaultPageFlags);
344#endif
345}
346
347
348//	#pragma mark -
349
350
351extern "C" status_t
352platform_allocate_region(void **_address, size_t size, uint8 protection,
353	bool /*exactAddress*/)
354{
355	TRACE(("platform_allocate_region(&%p, %zd)\n", *_address, size));
356
357	//get_next_virtual_address
358	size = (size + B_PAGE_SIZE - 1) / B_PAGE_SIZE * B_PAGE_SIZE;
359		// roundup to page size for clarity
360
361	if (*_address != NULL) {
362		// This special path is almost only useful for loading the
363		// kernel into memory; it will only allow you to map the
364		// 'kMaxKernelSize' bytes following the kernel base address.
365		// Also, it won't check for already mapped addresses, so
366		// you better know why you are here :)
367		addr_t address = (addr_t)*_address;
368
369		// is the address within the valid range?
370		if (address < KERNEL_BASE
371			|| address + size >= KERNEL_BASE + kMaxKernelSize) {
372			TRACE(("mmu_allocate in illegal range\n address: %" B_PRIx32
373				"  KERNELBASE: %" B_PRIx32 " KERNEL_BASE + kMaxKernelSize:"
374				" %" B_PRIx32 "  address + size : %" B_PRIx32 " \n",
375				(uint32)address, (uint32)KERNEL_BASE,
376				KERNEL_BASE + kMaxKernelSize, (uint32)(address + size)));
377			return B_ERROR;
378		}
379		TRACE(("platform_allocate_region: allocated %zd bytes at %08lx\n", size,
380			address));
381
382		return B_OK;
383	}
384
385	void *address = (void *)get_next_virtual_address(size);
386	if (address == NULL)
387		return B_NO_MEMORY;
388
389	TRACE(("platform_allocate_region: allocated %zd bytes at %p\n", size,
390		address));
391	*_address = address;
392	return B_OK;
393}
394
395
396extern "C" status_t
397platform_free_region(void *address, size_t size)
398{
399	TRACE(("platform_free_region(%p, %zd)\n", address, size));
400#ifdef __ARM__
401	mmu_free(address, size);
402#endif
403	return B_OK;
404}
405
406
407void
408platform_release_heap(struct stage2_args *args, void *base)
409{
410	//XXX
411	// It will be freed automatically, since it is in the
412	// identity mapped region, and not stored in the kernel's
413	// page tables.
414}
415
416
417status_t
418platform_init_heap(struct stage2_args *args, void **_base, void **_top)
419{
420	// the heap is put right before the pagetable
421	void *heap = (uint8 *)sPageTable - args->heap_size;
422	//FIXME: use phys addresses to allow passing args to U-Boot?
423
424	*_base = heap;
425	*_top = (void *)((int8 *)heap + args->heap_size);
426	TRACE(("boot heap at 0x%p to 0x%p\n", *_base, *_top));
427	return B_OK;
428}
429