main.c revision 91110
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 91110 2002-02-23 04:04:30Z 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/elf.h>
27#include <machine/lsu.h>
28#include <machine/metadata.h>
29#include <machine/tte.h>
30
31#include "bootstrap.h"
32#include "libofw.h"
33#include "dev_net.h"
34
35enum {
36	HEAPVA		= 0x800000,
37	HEAPSZ		= 0x1000000,
38	LOADSZ		= 0x1000000	/* for kernel and modules */
39};
40
41struct memory_slice {
42	vm_offset_t pstart;
43	vm_offset_t size;
44};
45
46typedef void kernel_entry_t(vm_offset_t mdp, u_long o1, u_long o2, u_long o3,
47			    void *openfirmware);
48
49extern void itlb_enter(int, vm_offset_t, vm_offset_t, unsigned long);
50extern void dtlb_enter(int, vm_offset_t, vm_offset_t, unsigned long);
51extern vm_offset_t itlb_va_to_pa(vm_offset_t);
52extern vm_offset_t dtlb_va_to_pa(vm_offset_t);
53extern vm_offset_t md_load(char *, vm_offset_t *);
54static int elf_exec(struct preloaded_file *);
55static int sparc64_autoload(void);
56static int mmu_mapin(vm_offset_t, vm_size_t);
57
58char __progname[] = "FreeBSD/sparc64 loader";
59
60vm_offset_t curkva = 0;
61vm_offset_t heapva;
62int tlbslot = 63;	/* Insert first entry at this TLB slot. XXX */
63phandle_t pmemh;	/* OFW memory handle */
64
65struct memory_slice memslices[18];
66struct ofw_devdesc bootdev;
67
68/*
69 * Machine dependent structures that the machine independent
70 * loader part uses.
71 */
72struct devsw *devsw[] = {
73#ifdef LOADER_DISK_SUPPORT
74	&ofwdisk,
75#endif
76#ifdef LOADER_NET_SUPPORT
77	&netdev,
78#endif
79	0
80};
81struct arch_switch archsw;
82
83struct file_format sparc64_elf = {
84	elf_loadfile,
85	elf_exec
86};
87struct file_format *file_formats[] = {
88	&sparc64_elf,
89	0
90};
91struct fs_ops *file_system[] = {
92#ifdef LOADER_UFS_SUPPORT
93	&ufs_fsops,
94#endif
95#ifdef LOADER_NFS_SUPPORT
96	&nfs_fsops,
97#endif
98#ifdef LOADER_TFTP_SUPPORT
99	&tftp_fsops,
100#endif
101	0
102};
103struct netif_driver *netif_drivers[] = {
104#ifdef LOADER_NET_SUPPORT
105	&ofwnet,
106#endif
107	0
108};
109
110extern struct console ofwconsole;
111struct console *consoles[] = {
112	&ofwconsole,
113	0
114};
115
116#ifdef LOADER_DEBUG
117static int
118watch_phys_set_mask(vm_offset_t pa, u_long mask)
119{
120	u_long lsucr;
121
122	stxa(AA_DMMU_PWPR, ASI_DMMU, pa & (((2UL << 38) - 1) << 3));
123	lsucr = ldxa(0, ASI_LSU_CTL_REG);
124	lsucr = ((lsucr | LSU_PW) & ~LSU_PM_MASK) |
125	    (mask << LSU_PM_SHIFT);
126	stxa(0, ASI_LSU_CTL_REG, lsucr);
127	return (0);
128}
129
130static int
131watch_phys_set(vm_offset_t pa, int sz)
132{
133	u_long off;
134
135	off = (u_long)pa & 7;
136	/* Test for misaligned watch points. */
137	if (off + sz > 8)
138		return (-1);
139	return (watch_phys_set_mask(pa, ((1 << sz) - 1) << off));
140}
141
142
143static int
144watch_virt_set_mask(vm_offset_t va, u_long mask)
145{
146	u_long lsucr;
147
148	stxa(AA_DMMU_VWPR, ASI_DMMU, va & (((2UL << 41) - 1) << 3));
149	lsucr = ldxa(0, ASI_LSU_CTL_REG);
150	lsucr = ((lsucr | LSU_VW) & ~LSU_VM_MASK) |
151	    (mask << LSU_VM_SHIFT);
152	stxa(0, ASI_LSU_CTL_REG, lsucr);
153	return (0);
154}
155
156static int
157watch_virt_set(vm_offset_t va, int sz)
158{
159	u_long off;
160
161	off = (u_long)va & 7;
162	/* Test for misaligned watch points. */
163	if (off + sz > 8)
164		return (-1);
165	return (watch_virt_set_mask(va, ((1 << sz) - 1) << off));
166}
167#endif
168
169/*
170 * archsw functions
171 */
172static int
173sparc64_autoload(void)
174{
175	printf("nothing to autoload yet.\n");
176	return 0;
177}
178
179static ssize_t
180sparc64_readin(const int fd, vm_offset_t va, const size_t len)
181{
182	mmu_mapin(va, len);
183	return read(fd, (void *)va, len);
184}
185
186static ssize_t
187sparc64_copyin(const void *src, vm_offset_t dest, size_t len)
188{
189	mmu_mapin(dest, len);
190	memcpy((void *)dest, src, len);
191	return len;
192}
193
194/*
195 * other MD functions
196 */
197static int
198elf_exec(struct preloaded_file *fp)
199{
200	struct file_metadata *fmp;
201	vm_offset_t entry;
202	vm_offset_t mdp;
203	Elf_Ehdr *Ehdr;
204	int error;
205
206	if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == 0) {
207		return EFTYPE;
208	}
209	Ehdr = (Elf_Ehdr *)&fmp->md_data;
210	entry = Ehdr->e_entry;
211
212	if ((error = md_load(fp->f_args, &mdp)) != 0)
213		return error;
214
215	printf("jumping to kernel entry at 0x%lx.\n", entry);
216#if 0
217	pmap_print_tlb('i');
218	pmap_print_tlb('d');
219#endif
220	((kernel_entry_t *)entry)(mdp, 0, 0, 0, openfirmware);
221
222	panic("exec returned");
223}
224
225static int
226mmu_mapin(vm_offset_t va, vm_size_t len)
227{
228	vm_offset_t pa, mva;
229
230	if (va + len > curkva)
231		curkva = va + len;
232
233	pa = (vm_offset_t)-1;
234	len += va & PAGE_MASK_4M;
235	va &= ~PAGE_MASK_4M;
236	while (len) {
237		if (dtlb_va_to_pa(va) == (vm_offset_t)-1 ||
238		    itlb_va_to_pa(va) == (vm_offset_t)-1) {
239			/* Allocate a physical page, claim the virtual area */
240			if (pa == (vm_offset_t)-1) {
241				pa = (vm_offset_t)OF_alloc_phys(PAGE_SIZE_4M,
242				    PAGE_SIZE_4M);
243				if (pa == (vm_offset_t)-1)
244					panic("out of memory");
245				mva = (vm_offset_t)OF_claim_virt(va,
246				    PAGE_SIZE_4M, 0);
247				if (mva != va) {
248					panic("can't claim virtual page "
249					    "(wanted %#lx, got %#lx)",
250					    va, mva);
251				}
252				/* The mappings may have changed, be paranoid. */
253				continue;
254			}
255			dtlb_enter(tlbslot, pa, va,
256			    TD_V | TD_4M | TD_L | TD_CP | TD_CV | TD_P | TD_W);
257			itlb_enter(tlbslot, pa, va,
258			    TD_V | TD_4M | TD_L | TD_CP | TD_CV | TD_P | TD_W);
259			tlbslot--;
260			pa = (vm_offset_t)-1;
261		}
262		len -= len > PAGE_SIZE_4M ? PAGE_SIZE_4M : len;
263		va += PAGE_SIZE_4M;
264	}
265	if (pa != (vm_offset_t)-1)
266		OF_release_phys(pa, PAGE_SIZE_4M);
267	return 0;
268}
269
270static vm_offset_t
271init_heap(void)
272{
273	if ((pmemh = OF_finddevice("/memory")) == (phandle_t)-1)
274		OF_exit();
275	if (OF_getprop(pmemh, "available", memslices, sizeof(memslices)) <= 0)
276		OF_exit();
277
278	/* There is no need for continuous physical heap memory. */
279	heapva = (vm_offset_t)OF_claim((void *)HEAPVA, HEAPSZ, 32);
280	return heapva;
281}
282
283int
284main(int (*openfirm)(void *))
285{
286	char bootpath[64];
287	struct devsw **dp;
288	phandle_t chosenh;
289
290	/*
291	 * Tell the OpenFirmware functions where they find the ofw gate.
292	 */
293	OF_init(openfirm);
294
295	archsw.arch_getdev = ofw_getdev;
296	archsw.arch_copyin = sparc64_copyin;
297	archsw.arch_copyout = ofw_copyout;
298	archsw.arch_readin = sparc64_readin;
299	archsw.arch_autoload = sparc64_autoload;
300
301	init_heap();
302	setheap((void *)heapva, (void *)(heapva + HEAPSZ));
303
304	/*
305	 * Probe for a console.
306	 */
307	cons_probe();
308
309	bcache_init(32, 512);
310
311	/*
312	 * Initialize devices.
313	 */
314	for (dp = devsw; *dp != 0; dp++) {
315		if ((*dp)->dv_init != 0)
316			(*dp)->dv_init();
317	}
318
319	/*
320	 * Set up the current device.
321	 */
322	chosenh = OF_finddevice("/chosen");
323	OF_getprop(chosenh, "bootpath", bootpath, sizeof(bootpath));
324
325	bootdev.d_type = ofw_devicetype(bootpath);
326	switch (bootdev.d_type) {
327	case DEVT_DISK:
328		bootdev.d_dev = &ofwdisk;
329		strncpy(bootdev.d_kind.ofwdisk.path, bootpath, 64);
330		ofw_parseofwdev(&bootdev, bootpath);
331		break;
332	case DEVT_NET:
333		bootdev.d_dev = &netdev;
334		strncpy(bootdev.d_kind.netif.path, bootpath, 64);
335		bootdev.d_kind.netif.unit = 0;
336		break;
337	}
338
339	env_setenv("currdev", EV_VOLATILE, ofw_fmtdev(&bootdev),
340	    ofw_setcurrdev, env_nounset);
341	env_setenv("loaddev", EV_VOLATILE, ofw_fmtdev(&bootdev),
342	    env_noset, env_nounset);
343
344	printf("%s\n", __progname);
345	printf("bootpath=\"%s\"\n", bootpath);
346	printf("loaddev=%s\n", getenv("loaddev"));
347
348	/* Give control to the machine independent loader code. */
349	interact();
350	return 1;
351}
352
353COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
354
355static int
356command_reboot(int argc, char *argv[])
357{
358	int i;
359
360	for (i = 0; devsw[i] != NULL; ++i)
361		if (devsw[i]->dv_cleanup != NULL)
362			(devsw[i]->dv_cleanup)();
363
364	printf("Rebooting...\n");
365	OF_exit();
366}
367
368/* provide this for panic, as it's not in the startup code */
369void
370exit(int code)
371{
372	OF_exit();
373}
374
375#ifdef LOADER_DEBUG
376typedef u_int64_t tte_t;
377
378const char *page_sizes[] = {
379	"  8k", " 64k", "512k", "  4m"
380};
381
382static void
383pmap_print_tte(tte_t tag, tte_t tte)
384{
385	printf("%s %s ",
386	    page_sizes[(tte & TD_SIZE_MASK) >> TD_SIZE_SHIFT],
387	    tag & TD_G ? "G" : " ");
388	printf(tte & TD_W ? "W " : "  ");
389	printf(tte & TD_P ? "\e[33mP\e[0m " : "  ");
390	printf(tte & TD_E ? "E " : "  ");
391	printf(tte & TD_CV ? "CV " : "   ");
392	printf(tte & TD_CP ? "CP " : "   ");
393	printf(tte & TD_L ? "\e[32mL\e[0m " : "  ");
394	printf(tte & TD_IE ? "IE " : "   ");
395	printf(tte & TD_NFO ? "NFO " : "    ");
396	printf("tag=0x%lx pa=0x%lx va=0x%lx ctx=%ld\n", tag, TD_PA(tte),
397	    TT_VA(tag), TT_CTX(tag));
398}
399void
400pmap_print_tlb(char which)
401{
402	int i;
403	tte_t tte, tag;
404
405	for (i = 0; i < 64*8; i += 8) {
406		if (which == 'i') {
407			__asm__ __volatile__("ldxa	[%1] %2, %0\n" :
408			    "=r" (tag) : "r" (i),
409			    "i" (ASI_ITLB_TAG_READ_REG));
410			__asm__ __volatile__("ldxa	[%1] %2, %0\n" :
411			    "=r" (tte) : "r" (i),
412			    "i" (ASI_ITLB_DATA_ACCESS_REG));
413		}
414		else {
415			__asm__ __volatile__("ldxa	[%1] %2, %0\n" :
416			    "=r" (tag) : "r" (i),
417			    "i" (ASI_DTLB_TAG_READ_REG));
418			__asm__ __volatile__("ldxa	[%1] %2, %0\n" :
419			    "=r" (tte) : "r" (i),
420			    "i" (ASI_DTLB_DATA_ACCESS_REG));
421		}
422		if (!(tte & TD_V))
423			continue;
424		printf("%cTLB-%2u: ", which, i>>3);
425		pmap_print_tte(tag, tte);
426	}
427}
428#endif
429