main.c revision 97445
121330Sdavidn/*
221330Sdavidn * Initial implementation:
321330Sdavidn * Copyright (c) 2001 Robert Drehmel
421330Sdavidn * All rights reserved.
521330Sdavidn *
621330Sdavidn * As long as the above copyright statement and this notice remain
721330Sdavidn * unchanged, you can do what ever you want with this file.
821330Sdavidn *
921330Sdavidn * $FreeBSD: head/sys/boot/sparc64/loader/main.c 97445 2002-05-29 05:49:59Z jake $
1021330Sdavidn */
1121330Sdavidn/*
1221330Sdavidn * FreeBSD/sparc64 kernel loader - machine dependent part
1321330Sdavidn *
1421330Sdavidn *  - implements copyin and readin functions that map kernel
1521330Sdavidn *    pages on demand.  The machine independent code does not
1621330Sdavidn *    know the size of the kernel early enough to pre-enter
1721330Sdavidn *    TTEs and install just one 4MB mapping seemed to limiting
1821330Sdavidn *    to me.
1921330Sdavidn */
2021330Sdavidn
2121330Sdavidn#include <stand.h>
2221330Sdavidn#include <sys/exec.h>
2321330Sdavidn#include <sys/param.h>
2421330Sdavidn#include <sys/linker.h>
2521330Sdavidn#include <sys/pcpu.h>
2621330Sdavidn
2730259Scharnier#include <machine/asi.h>
2830259Scharnier#include <machine/atomic.h>
2950479Speter#include <machine/cpufunc.h>
3030259Scharnier#include <machine/elf.h>
3130259Scharnier#include <machine/lsu.h>
3221330Sdavidn#include <machine/metadata.h>
33287084Sbapt#include <machine/smp.h>
34242349Sbapt#include <machine/tte.h>
35242349Sbapt#include <machine/upa.h>
36242349Sbapt
37308814Sasomers#include "bootstrap.h"
3821330Sdavidn#include "libofw.h"
3921330Sdavidn#include "dev_net.h"
4021330Sdavidn
4121330Sdavidnenum {
42242349Sbapt	HEAPVA		= 0x800000,
4321330Sdavidn	HEAPSZ		= 0x1000000,
44242349Sbapt	LOADSZ		= 0x1000000	/* for kernel and modules */
45242349Sbapt};
46242349Sbapt
4721330Sdavidnstruct memory_slice {
48287084Sbapt	vm_offset_t pstart;
49242349Sbapt	vm_offset_t size;
50242349Sbapt};
51242349Sbapt
52242349Sbapttypedef void kernel_entry_t(vm_offset_t mdp, u_long o1, u_long o2, u_long o3,
53242349Sbapt			    void *openfirmware);
54242349Sbapt
55242349Sbaptextern void itlb_enter(u_long vpn, u_long data);
56242349Sbaptextern void dtlb_enter(u_long vpn, u_long data);
57242349Sbaptextern vm_offset_t itlb_va_to_pa(vm_offset_t);
58242349Sbaptextern vm_offset_t dtlb_va_to_pa(vm_offset_t);
59242349Sbaptextern vm_offset_t md_load(char *, vm_offset_t *);
60242349Sbaptstatic int elf_exec(struct preloaded_file *);
61242349Sbaptstatic int sparc64_autoload(void);
62242349Sbaptstatic int mmu_mapin(vm_offset_t, vm_size_t);
63242349Sbapt
64242349Sbaptchar __progname[] = "FreeBSD/sparc64 loader";
65242349Sbapt
66242349Sbaptstruct tlb_entry *dtlb_store;
67308814Sasomersstruct tlb_entry *itlb_store;
68242349Sbapt
69242349Sbaptint dtlb_slot;
70310173Sasomersint itlb_slot;
71308814Sasomersint dtlb_slot_max;
72243335Sbaptint itlb_slot_max;
73243335Sbapt
74242349Sbaptvm_offset_t curkva = 0;
75242349Sbaptvm_offset_t heapva;
76242349Sbaptphandle_t pmemh;	/* OFW memory handle */
77242349Sbapt
78242349Sbaptstruct memory_slice memslices[18];
79242349Sbaptstruct ofw_devdesc bootdev;
80242349Sbapt
8121330Sdavidn/*
8221330Sdavidn * Machine dependent structures that the machine independent
8321330Sdavidn * loader part uses.
8421330Sdavidn */
8521330Sdavidnstruct devsw *devsw[] = {
86242349Sbapt#ifdef LOADER_DISK_SUPPORT
8721330Sdavidn	&ofwdisk,
8821330Sdavidn#endif
8921330Sdavidn#ifdef LOADER_NET_SUPPORT
9021330Sdavidn	&netdev,
9121330Sdavidn#endif
92242349Sbapt	0
9321330Sdavidn};
9421330Sdavidnstruct arch_switch archsw;
9521330Sdavidn
9621330Sdavidnstruct file_format sparc64_elf = {
9721330Sdavidn	elf_loadfile,
98242349Sbapt	elf_exec
9921330Sdavidn};
100struct file_format *file_formats[] = {
101	&sparc64_elf,
102	0
103};
104struct fs_ops *file_system[] = {
105#ifdef LOADER_UFS_SUPPORT
106	&ufs_fsops,
107#endif
108#ifdef LOADER_CD9660_SUPPORT
109	&cd9660_fsops,
110#endif
111#ifdef LOADER_NET_SUPPORT
112	&nfs_fsops,
113#endif
114#ifdef LOADER_TFTP_SUPPORT
115	&tftp_fsops,
116#endif
117	0
118};
119struct netif_driver *netif_drivers[] = {
120#ifdef LOADER_NET_SUPPORT
121	&ofwnet,
122#endif
123	0
124};
125
126extern struct console ofwconsole;
127struct console *consoles[] = {
128	&ofwconsole,
129	0
130};
131
132#ifdef LOADER_DEBUG
133static int
134watch_phys_set_mask(vm_offset_t pa, u_long mask)
135{
136	u_long lsucr;
137
138	stxa(AA_DMMU_PWPR, ASI_DMMU, pa & (((2UL << 38) - 1) << 3));
139	lsucr = ldxa(0, ASI_LSU_CTL_REG);
140	lsucr = ((lsucr | LSU_PW) & ~LSU_PM_MASK) |
141	    (mask << LSU_PM_SHIFT);
142	stxa(0, ASI_LSU_CTL_REG, lsucr);
143	return (0);
144}
145
146static int
147watch_phys_set(vm_offset_t pa, int sz)
148{
149	u_long off;
150
151	off = (u_long)pa & 7;
152	/* Test for misaligned watch points. */
153	if (off + sz > 8)
154		return (-1);
155	return (watch_phys_set_mask(pa, ((1 << sz) - 1) << off));
156}
157
158
159static int
160watch_virt_set_mask(vm_offset_t va, u_long mask)
161{
162	u_long lsucr;
163
164	stxa(AA_DMMU_VWPR, ASI_DMMU, va & (((2UL << 41) - 1) << 3));
165	lsucr = ldxa(0, ASI_LSU_CTL_REG);
166	lsucr = ((lsucr | LSU_VW) & ~LSU_VM_MASK) |
167	    (mask << LSU_VM_SHIFT);
168	stxa(0, ASI_LSU_CTL_REG, lsucr);
169	return (0);
170}
171
172static int
173watch_virt_set(vm_offset_t va, int sz)
174{
175	u_long off;
176
177	off = (u_long)va & 7;
178	/* Test for misaligned watch points. */
179	if (off + sz > 8)
180		return (-1);
181	return (watch_virt_set_mask(va, ((1 << sz) - 1) << off));
182}
183#endif
184
185/*
186 * archsw functions
187 */
188static int
189sparc64_autoload(void)
190{
191	printf("nothing to autoload yet.\n");
192	return 0;
193}
194
195static ssize_t
196sparc64_readin(const int fd, vm_offset_t va, const size_t len)
197{
198	mmu_mapin(va, len);
199	return read(fd, (void *)va, len);
200}
201
202static ssize_t
203sparc64_copyin(const void *src, vm_offset_t dest, size_t len)
204{
205	mmu_mapin(dest, len);
206	memcpy((void *)dest, src, len);
207	return len;
208}
209
210/*
211 * other MD functions
212 */
213static int
214elf_exec(struct preloaded_file *fp)
215{
216	struct file_metadata *fmp;
217	vm_offset_t mdp;
218	Elf_Ehdr *e;
219	int error;
220
221	if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == 0) {
222		return EFTYPE;
223	}
224	e = (Elf_Ehdr *)&fmp->md_data;
225
226	if ((error = md_load(fp->f_args, &mdp)) != 0)
227		return error;
228
229	printf("jumping to kernel entry at %#lx.\n", e->e_entry);
230#if 0
231	pmap_print_tlb('i');
232	pmap_print_tlb('d');
233#endif
234	((kernel_entry_t *)e->e_entry)(mdp, 0, 0, 0, openfirmware);
235
236	panic("exec returned");
237}
238
239static int
240mmu_mapin(vm_offset_t va, vm_size_t len)
241{
242	vm_offset_t pa, mva;
243	u_long data;
244
245	if (va + len > curkva)
246		curkva = va + len;
247
248	pa = (vm_offset_t)-1;
249	len += va & PAGE_MASK_4M;
250	va &= ~PAGE_MASK_4M;
251	while (len) {
252		if (dtlb_va_to_pa(va) == (vm_offset_t)-1 ||
253		    itlb_va_to_pa(va) == (vm_offset_t)-1) {
254			/* Allocate a physical page, claim the virtual area */
255			if (pa == (vm_offset_t)-1) {
256				pa = (vm_offset_t)OF_alloc_phys(PAGE_SIZE_4M,
257				    PAGE_SIZE_4M);
258				if (pa == (vm_offset_t)-1)
259					panic("out of memory");
260				mva = (vm_offset_t)OF_claim_virt(va,
261				    PAGE_SIZE_4M, 0);
262				if (mva != va) {
263					panic("can't claim virtual page "
264					    "(wanted %#lx, got %#lx)",
265					    va, mva);
266				}
267				/* The mappings may have changed, be paranoid. */
268				continue;
269			}
270			/*
271			 * Actually, we can only allocate two pages less at
272			 * most (depending on the kernel TSB size).
273			 */
274			if (dtlb_slot >= dtlb_slot_max)
275				panic("mmu_mapin: out of dtlb_slots");
276			if (itlb_slot >= itlb_slot_max)
277				panic("mmu_mapin: out of itlb_slots");
278			data = TD_V | TD_4M | TD_PA(pa) | TD_L | TD_CP |
279			    TD_CV | TD_P | TD_W;
280			dtlb_store[dtlb_slot].te_pa = pa;
281			dtlb_store[dtlb_slot].te_va = va;
282			itlb_store[itlb_slot].te_pa = pa;
283			itlb_store[itlb_slot].te_va = va;
284			dtlb_slot++;
285			itlb_slot++;
286			dtlb_enter(va, data);
287			itlb_enter(va, data);
288			pa = (vm_offset_t)-1;
289		}
290		len -= len > PAGE_SIZE_4M ? PAGE_SIZE_4M : len;
291		va += PAGE_SIZE_4M;
292	}
293	if (pa != (vm_offset_t)-1)
294		OF_release_phys(pa, PAGE_SIZE_4M);
295	return 0;
296}
297
298static vm_offset_t
299init_heap(void)
300{
301	if ((pmemh = OF_finddevice("/memory")) == (phandle_t)-1)
302		OF_exit();
303	if (OF_getprop(pmemh, "available", memslices, sizeof(memslices)) <= 0)
304		OF_exit();
305
306	/* There is no need for continuous physical heap memory. */
307	heapva = (vm_offset_t)OF_claim((void *)HEAPVA, HEAPSZ, 32);
308	return heapva;
309}
310
311static void
312tlb_init(void)
313{
314	phandle_t child;
315	phandle_t root;
316	char buf[128];
317	u_int bootcpu;
318	u_int cpu;
319
320	bootcpu = UPA_CR_GET_MID(ldxa(0, ASI_UPA_CONFIG_REG));
321	if ((root = OF_peer(0)) == -1)
322		panic("main: OF_peer");
323	for (child = OF_child(root); child != 0; child = OF_peer(child)) {
324		if (child == -1)
325			panic("main: OF_child");
326		if (OF_getprop(child, "device_type", buf, sizeof(buf)) > 0 &&
327		    strcmp(buf, "cpu") == 0) {
328			if (OF_getprop(child, "upa-portid", &cpu,
329			    sizeof(cpu)) == -1 && OF_getprop(child, "portid",
330			    &cpu, sizeof(cpu)) == -1)
331				panic("main: OF_getprop");
332			if (cpu == bootcpu)
333				break;
334		}
335	}
336	if (cpu != bootcpu)
337		panic("init_tlb: no node for bootcpu?!?!");
338	if (OF_getprop(child, "#dtlb-entries", &dtlb_slot_max,
339	    sizeof(dtlb_slot_max)) == -1 ||
340	    OF_getprop(child, "#itlb-entries", &itlb_slot_max,
341	    sizeof(itlb_slot_max)) == -1)
342		panic("init_tlb: OF_getprop");
343	dtlb_store = malloc(dtlb_slot_max * sizeof(*dtlb_store));
344	itlb_store = malloc(itlb_slot_max * sizeof(*itlb_store));
345	if (dtlb_store == NULL || itlb_store == NULL)
346		panic("init_tlb: malloc");
347}
348
349int
350main(int (*openfirm)(void *))
351{
352	char bootpath[64];
353	struct devsw **dp;
354	phandle_t chosenh;
355
356	/*
357	 * Tell the OpenFirmware functions where they find the ofw gate.
358	 */
359	OF_init(openfirm);
360
361	archsw.arch_getdev = ofw_getdev;
362	archsw.arch_copyin = sparc64_copyin;
363	archsw.arch_copyout = ofw_copyout;
364	archsw.arch_readin = sparc64_readin;
365	archsw.arch_autoload = sparc64_autoload;
366#ifdef ELF_CRC32
367	archsw.arch_crc32 = sparc64_crc32;
368#endif
369
370	init_heap();
371	setheap((void *)heapva, (void *)(heapva + HEAPSZ));
372
373	/*
374	 * Probe for a console.
375	 */
376	cons_probe();
377
378	tlb_init();
379
380	bcache_init(32, 512);
381
382	/*
383	 * Initialize devices.
384	 */
385	for (dp = devsw; *dp != 0; dp++) {
386		if ((*dp)->dv_init != 0)
387			(*dp)->dv_init();
388	}
389
390	/*
391	 * Set up the current device.
392	 */
393	chosenh = OF_finddevice("/chosen");
394	OF_getprop(chosenh, "bootpath", bootpath, sizeof(bootpath));
395
396	bootdev.d_type = ofw_devicetype(bootpath);
397	switch (bootdev.d_type) {
398	case DEVT_DISK:
399		bootdev.d_dev = &ofwdisk;
400		/*
401		 * Sun compatible bootable CD-ROMs have a disk label placed
402		 * before the cd9660 data, with the actual filesystem being
403		 * in the first partition, while the other partitions contain
404		 * pseudo disk labels with embedded boot blocks for different
405		 * architectures, which may be followed by UFS filesystems.
406		 * The firmware will set the boot path to the partition it
407		 * boots from ('f' in the sun4u case), but we want the kernel
408		 * to be loaded from the cd9660 fs ('a'), so the boot path
409		 * needs to be altered.
410		 */
411		if (strstr(bootpath, "cdrom") != NULL &&
412		    bootpath[strlen(bootpath) - 2] == ':') {
413			bootpath[strlen(bootpath) - 1] = 'a';
414			printf("Boot path set to %s\n", bootpath);
415		}
416		strncpy(bootdev.d_kind.ofwdisk.path, bootpath, 64);
417		ofw_parseofwdev(&bootdev, bootpath);
418		break;
419	case DEVT_NET:
420		bootdev.d_dev = &netdev;
421		strncpy(bootdev.d_kind.netif.path, bootpath, 64);
422		bootdev.d_kind.netif.unit = 0;
423		break;
424	}
425
426	env_setenv("currdev", EV_VOLATILE, ofw_fmtdev(&bootdev),
427	    ofw_setcurrdev, env_nounset);
428	env_setenv("loaddev", EV_VOLATILE, ofw_fmtdev(&bootdev),
429	    env_noset, env_nounset);
430
431	printf("%s\n", __progname);
432	printf("bootpath=\"%s\"\n", bootpath);
433	printf("loaddev=%s\n", getenv("loaddev"));
434
435	/* Give control to the machine independent loader code. */
436	interact();
437	return 1;
438}
439
440COMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
441
442static int
443command_reboot(int argc, char *argv[])
444{
445	int i;
446
447	for (i = 0; devsw[i] != NULL; ++i)
448		if (devsw[i]->dv_cleanup != NULL)
449			(devsw[i]->dv_cleanup)();
450
451	printf("Rebooting...\n");
452	OF_exit();
453}
454
455/* provide this for panic, as it's not in the startup code */
456void
457exit(int code)
458{
459	OF_exit();
460}
461
462#ifdef LOADER_DEBUG
463typedef u_int64_t tte_t;
464
465const char *page_sizes[] = {
466	"  8k", " 64k", "512k", "  4m"
467};
468
469static void
470pmap_print_tte(tte_t tag, tte_t tte)
471{
472	printf("%s %s ",
473	    page_sizes[(tte & TD_SIZE_MASK) >> TD_SIZE_SHIFT],
474	    tag & TD_G ? "G" : " ");
475	printf(tte & TD_W ? "W " : "  ");
476	printf(tte & TD_P ? "\e[33mP\e[0m " : "  ");
477	printf(tte & TD_E ? "E " : "  ");
478	printf(tte & TD_CV ? "CV " : "   ");
479	printf(tte & TD_CP ? "CP " : "   ");
480	printf(tte & TD_L ? "\e[32mL\e[0m " : "  ");
481	printf(tte & TD_IE ? "IE " : "   ");
482	printf(tte & TD_NFO ? "NFO " : "    ");
483	printf("tag=0x%lx pa=0x%lx va=0x%lx ctx=%ld\n", tag, TD_PA(tte),
484	    TT_VA(tag), TT_CTX(tag));
485}
486void
487pmap_print_tlb(char which)
488{
489	int i;
490	tte_t tte, tag;
491
492	for (i = 0; i < 64*8; i += 8) {
493		if (which == 'i') {
494			__asm__ __volatile__("ldxa	[%1] %2, %0\n" :
495			    "=r" (tag) : "r" (i),
496			    "i" (ASI_ITLB_TAG_READ_REG));
497			__asm__ __volatile__("ldxa	[%1] %2, %0\n" :
498			    "=r" (tte) : "r" (i),
499			    "i" (ASI_ITLB_DATA_ACCESS_REG));
500		}
501		else {
502			__asm__ __volatile__("ldxa	[%1] %2, %0\n" :
503			    "=r" (tag) : "r" (i),
504			    "i" (ASI_DTLB_TAG_READ_REG));
505			__asm__ __volatile__("ldxa	[%1] %2, %0\n" :
506			    "=r" (tte) : "r" (i),
507			    "i" (ASI_DTLB_DATA_ACCESS_REG));
508		}
509		if (!(tte & TD_V))
510			continue;
511		printf("%cTLB-%2u: ", which, i>>3);
512		pmap_print_tte(tag, tte);
513	}
514}
515#endif
516