main.c revision 85719
1/*
2 * Initial implementation:
3 * Copyright (c) 2001 Robert Drehmel
4 * All rights reserved.
5 *
6 * As long as the above copyright statement and this notice remain
7 * unchanged, you can do what ever you want with this file.
8 *
9 * $FreeBSD: head/sys/boot/sparc64/loader/main.c 85719 2001-10-30 06:31:45Z jake $
10 */
11/*
12 * FreeBSD/sparc64 kernel loader - machine dependent part
13 *
14 *  - implements copyin and readin functions that map kernel
15 *    pages on demand.  The machine independent code does not
16 *    know the size of the kernel early enough to pre-enter
17 *    TTEs and install just one 4MB mapping seemed to limiting
18 *    to me.
19 */
20#include <stand.h>
21#include <sys/exec.h>
22#include <sys/param.h>
23#include <sys/linker.h>
24
25#include <machine/asi.h>
26#include <machine/bootinfo.h>
27#include <machine/elf.h>
28#include <machine/tte.h>
29
30#include "bootstrap.h"
31#include "libofw.h"
32#include "dev_net.h"
33
34enum {
35	HEAPVA		= 0x800000,
36	HEAPSZ		= 0x1000000,
37	LOADSZ		= 0x1000000	/* for kernel and modules */
38};
39
40struct memory_slice {
41	vm_offset_t pstart;
42	vm_offset_t size;
43};
44
45typedef void kernel_entry_t(vm_offset_t mdp, u_long o1, u_long o2, u_long o3,
46			    void *openfirmware);
47
48extern void itlb_enter(int, vm_offset_t, vm_offset_t, unsigned long);
49extern void dtlb_enter(int, vm_offset_t, vm_offset_t, unsigned long);
50extern vm_offset_t itlb_va_to_pa(vm_offset_t);
51extern vm_offset_t dtlb_va_to_pa(vm_offset_t);
52extern vm_offset_t md_load(char *, vm_offset_t *);
53static int elf_exec(struct preloaded_file *);
54static int sparc64_autoload(void);
55static int mmu_mapin(vm_offset_t, vm_size_t);
56
57char __progname[] = "FreeBSD/sparc64 loader";
58
59vm_offset_t kernelpa;	/* Begin of kernel and mod memory. */
60vm_offset_t curkpg;	/* (PA) used for on-demand map-in. */
61vm_offset_t curkva = 0;
62vm_offset_t heapva;
63int tlbslot = 63;	/* Insert first entry at this TLB slot. XXX */
64phandle_t pmemh;	/* OFW memory handle */
65
66struct memory_slice memslices[18];
67struct ofw_devdesc bootdev;
68
69/*
70 * Machine dependent structures that the machine independent
71 * loader part uses.
72 */
73struct devsw *devsw[] = {
74#ifdef LOADER_DISK_SUPPORT
75	&ofwdisk,
76#endif
77#ifdef LOADER_NET_SUPPORT
78	&netdev,
79#endif
80	0
81};
82struct arch_switch archsw;
83
84struct file_format sparc64_elf = {
85	elf_loadfile,
86	elf_exec
87};
88struct file_format *file_formats[] = {
89	&sparc64_elf,
90	0
91};
92struct fs_ops *file_system[] = {
93#ifdef LOAD_DISK_SUPPORT
94	&ufs_fsops,
95#endif
96#ifdef LOADER_NET_SUPPORT
97	&nfs_fsops,
98#endif
99	0
100};
101struct netif_driver *netif_drivers[] = {
102#ifdef LOADER_NET_SUPPORT
103	&ofwnet,
104#endif
105	0
106};
107
108extern struct console ofwconsole;
109struct console *consoles[] = {
110	&ofwconsole,
111	0
112};
113
114/*
115 * archsw functions
116 */
117static int
118sparc64_autoload(void)
119{
120	printf("nothing to autoload yet.\n");
121	return 0;
122}
123
124static ssize_t
125sparc64_readin(const int fd, vm_offset_t va, const size_t len)
126{
127	mmu_mapin(va, len);
128	return read(fd, (void *)va, len);
129}
130
131static ssize_t
132sparc64_copyin(const void *src, vm_offset_t dest, size_t len)
133{
134	mmu_mapin(dest, len);
135	memcpy((void *)dest, src, len);
136	return len;
137}
138
139/*
140 * other MD functions
141 */
142static int
143elf_exec(struct preloaded_file *fp)
144{
145	struct file_metadata *fmp;
146	vm_offset_t entry;
147	vm_offset_t mdp;
148	Elf_Ehdr *Ehdr;
149	int error;
150
151	if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == 0) {
152		return EFTYPE;
153	}
154	Ehdr = (Elf_Ehdr *)&fmp->md_data;
155	entry = Ehdr->e_entry;
156
157	if ((error = md_load(fp->f_args, &mdp)) != 0)
158		return error;
159
160	printf("jumping to kernel entry at 0x%lx.\n", entry);
161#if 0
162	pmap_print_tlb('i');
163	pmap_print_tlb('d');
164#endif
165	((kernel_entry_t *)entry)(mdp, 0, 0, 0, openfirmware);
166
167	panic("exec returned");
168}
169
170static int
171mmu_mapin(vm_offset_t va, vm_size_t len)
172{
173
174	if (va + len > curkva)
175		curkva = va + len;
176
177	len += va & PAGE_MASK_4M;
178	va &= ~PAGE_MASK_4M;
179	while (len) {
180		if (dtlb_va_to_pa(va) == (vm_offset_t)-1 ||
181		    itlb_va_to_pa(va) == (vm_offset_t)-1) {
182			dtlb_enter(tlbslot, curkpg, va,
183			    TD_V | TD_4M | TD_L | TD_CP | TD_CV | TD_P | TD_W);
184			itlb_enter(tlbslot, curkpg, va,
185			    TD_V | TD_4M | TD_L | TD_CP | TD_CV | TD_P | TD_W);
186			tlbslot--;
187			curkpg += PAGE_SIZE_4M;
188		}
189		len -= len > PAGE_SIZE_4M ? PAGE_SIZE_4M : len;
190		va += PAGE_SIZE_4M;
191	}
192	return 0;
193}
194
195static vm_offset_t
196init_heap(void)
197{
198	if ((pmemh = OF_finddevice("/memory")) == (phandle_t)-1)
199		OF_exit();
200	if (OF_getprop(pmemh, "available", memslices, sizeof(memslices)) <= 0)
201		OF_exit();
202
203	/* Reserve 16 MB continuous for kernel and modules. */
204	kernelpa = (vm_offset_t)OF_alloc_phys(LOADSZ, 0x400000);
205	curkpg = kernelpa;
206	/* There is no need for continuous physical heap memory. */
207	heapva = (vm_offset_t)OF_claim((void *)HEAPVA, HEAPSZ, 32);
208	return heapva;
209}
210
211int
212main(int (*openfirm)(void *))
213{
214	char bootpath[64];
215	struct devsw **dp;
216	phandle_t chosenh;
217
218	/*
219	 * Tell the OpenFirmware functions where they find the ofw gate.
220	 */
221	OF_init(openfirm);
222
223	archsw.arch_getdev = ofw_getdev;
224	archsw.arch_copyin = sparc64_copyin;
225	archsw.arch_copyout = ofw_copyout;
226	archsw.arch_readin = sparc64_readin;
227	archsw.arch_autoload = sparc64_autoload;
228
229	init_heap();
230	setheap((void *)heapva, (void *)(heapva + HEAPSZ));
231
232	/*
233	 * Probe for a console.
234	 */
235	cons_probe();
236
237	bcache_init(32, 512);
238
239	/*
240	 * Initialize devices.
241	 */
242	for (dp = devsw; *dp != 0; dp++) {
243		if ((*dp)->dv_init != 0)
244			(*dp)->dv_init();
245	}
246
247	/*
248	 * Set up the current device.
249	 */
250	chosenh = OF_finddevice("/chosen");
251	OF_getprop(chosenh, "bootpath", bootpath, sizeof(bootpath));
252
253	bootdev.d_type = ofw_devicetype(bootpath);
254	switch (bootdev.d_type) {
255	case DEVT_DISK:
256		bootdev.d_dev = &ofwdisk;
257		strncpy(bootdev.d_kind.ofwdisk.path, bootpath, 64);
258		ofw_parseofwdev(&bootdev, bootpath);
259		break;
260	case DEVT_NET:
261		bootdev.d_dev = &netdev;
262		strncpy(bootdev.d_kind.netif.path, bootpath, 64);
263		bootdev.d_kind.netif.unit = 0;
264		break;
265	}
266
267	env_setenv("currdev", EV_VOLATILE, ofw_fmtdev(&bootdev),
268	    ofw_setcurrdev, env_nounset);
269	env_setenv("loaddev", EV_VOLATILE, ofw_fmtdev(&bootdev),
270	    env_noset, env_nounset);
271
272	printf("%s\n", __progname);
273	printf("bootpath=\"%s\"\n", bootpath);
274	printf("loaddev=%s\n", getenv("loaddev"));
275	printf("kernelpa=0x%lx\n", curkpg);
276
277	/* Give control to the machine independent loader code. */
278	interact();
279	return 1;
280}
281
282typedef u_int64_t tte_t;
283
284const char *page_sizes[] = {
285	"  8k", " 64k", "512k", "  4m"
286};
287
288static void
289pmap_print_tte(tte_t tag, tte_t tte)
290{
291	printf("%s %s ",
292	    page_sizes[(tte & TD_SIZE_MASK) >> TD_SIZE_SHIFT],
293	    tag & TD_G ? "G" : " ");
294	printf(tte & TD_W ? "W " : "  ");
295	printf(tte & TD_P ? "\e[33mP\e[0m " : "  ");
296	printf(tte & TD_E ? "E " : "  ");
297	printf(tte & TD_CV ? "CV " : "   ");
298	printf(tte & TD_CP ? "CP " : "   ");
299	printf(tte & TD_L ? "\e[32mL\e[0m " : "  ");
300	printf(tte & TD_IE ? "IE " : "   ");
301	printf(tte & TD_NFO ? "NFO " : "    ");
302	printf("tag=0x%lx pa=0x%lx va=0x%lx ctx=%ld\n", tag, TD_PA(tte),
303	    TT_VA(tag), TT_CTX(tag));
304}
305void
306pmap_print_tlb(char which)
307{
308	int i;
309	tte_t tte, tag;
310
311	for (i = 0; i < 64*8; i += 8) {
312		if (which == 'i') {
313			__asm__ __volatile__("ldxa	[%1] %2, %0\n" :
314			    "=r" (tag) : "r" (i),
315			    "i" (ASI_ITLB_TAG_READ_REG));
316			__asm__ __volatile__("ldxa	[%1] %2, %0\n" :
317			    "=r" (tte) : "r" (i),
318			    "i" (ASI_ITLB_DATA_ACCESS_REG));
319		}
320		else {
321			__asm__ __volatile__("ldxa	[%1] %2, %0\n" :
322			    "=r" (tag) : "r" (i),
323			    "i" (ASI_DTLB_TAG_READ_REG));
324			__asm__ __volatile__("ldxa	[%1] %2, %0\n" :
325			    "=r" (tte) : "r" (i),
326			    "i" (ASI_DTLB_DATA_ACCESS_REG));
327		}
328		if (!(tte & TD_V))
329			continue;
330		printf("%cTLB-%2u: ", which, i>>3);
331		pmap_print_tte(tag, tte);
332	}
333}
334