main.c revision 200946
1139738Simp/*-
284996Srobert * Initial implementation:
384996Srobert * Copyright (c) 2001 Robert Drehmel
484996Srobert * All rights reserved.
584996Srobert *
684996Srobert * As long as the above copyright statement and this notice remain
7181398Smarius * unchanged, you can do what ever you want with this file.
884996Srobert */
9182916Smarius/*-
10182916Smarius * Copyright (c) 2008 Marius Strobl <marius@FreeBSD.org>
11182916Smarius * All rights reserved.
12182916Smarius *
13182916Smarius * Redistribution and use in source and binary forms, with or without
14182916Smarius * modification, are permitted provided that the following conditions
15182916Smarius * are met:
16182916Smarius * 1. Redistributions of source code must retain the above copyright
17182916Smarius *    notice, this list of conditions and the following disclaimer.
18182916Smarius * 2. Redistributions in binary form must reproduce the above copyright
19182916Smarius *    notice, this list of conditions and the following disclaimer in the
20182916Smarius *    documentation and/or other materials provided with the distribution.
21182916Smarius *
22182916Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23182916Smarius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24182916Smarius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25182916Smarius * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26182916Smarius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27182916Smarius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28182916Smarius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29182916Smarius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30182916Smarius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31182916Smarius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32182916Smarius * SUCH DAMAGE.
33182916Smarius */
34124139Sobrien
35124139Sobrien#include <sys/cdefs.h>
36124139Sobrien__FBSDID("$FreeBSD: head/sys/boot/sparc64/loader/main.c 200946 2009-12-24 15:23:51Z marius $");
37182020Smarius
3884996Srobert/*
3984996Srobert * FreeBSD/sparc64 kernel loader - machine dependent part
4084996Srobert *
4184996Srobert *  - implements copyin and readin functions that map kernel
4284996Srobert *    pages on demand.  The machine independent code does not
4384996Srobert *    know the size of the kernel early enough to pre-enter
4484996Srobert *    TTEs and install just one 4MB mapping seemed to limiting
4584996Srobert *    to me.
4684996Srobert */
4791139Sjake
4884996Srobert#include <stand.h>
4984996Srobert#include <sys/exec.h>
5084996Srobert#include <sys/param.h>
51102219Srobert#include <sys/queue.h>
5284996Srobert#include <sys/linker.h>
53163145Skmacy#include <sys/types.h>
5484996Srobert
55163145Skmacy#include <vm/vm.h>
5684996Srobert#include <machine/asi.h>
5791139Sjake#include <machine/cpufunc.h>
5884996Srobert#include <machine/elf.h>
5991110Sjake#include <machine/lsu.h>
6091110Sjake#include <machine/metadata.h>
6184996Srobert#include <machine/tte.h>
62181398Smarius#include <machine/tlb.h>
6391139Sjake#include <machine/upa.h>
64182478Smarius#include <machine/ver.h>
65182877Smarius#include <machine/vmparam.h>
6684996Srobert
6784996Srobert#include "bootstrap.h"
6884996Srobert#include "libofw.h"
6985719Sjake#include "dev_net.h"
7084996Srobert
71170839Smariusextern char bootprog_name[], bootprog_rev[], bootprog_date[], bootprog_maker[];
72170839Smarius
7384996Srobertenum {
7484996Srobert	HEAPVA		= 0x800000,
7584996Srobert	HEAPSZ		= 0x1000000,
7684996Srobert	LOADSZ		= 0x1000000	/* for kernel and modules */
7784996Srobert};
7884996Srobert
79170839Smariusstatic struct mmu_ops {
80163145Skmacy	void (*tlb_init)(void);
81163145Skmacy	int (*mmu_mapin)(vm_offset_t va, vm_size_t len);
82163145Skmacy} *mmu_ops;
83163145Skmacy
8485719Sjaketypedef void kernel_entry_t(vm_offset_t mdp, u_long o1, u_long o2, u_long o3,
85170839Smarius    void *openfirmware);
8685719Sjake
87182478Smariusstatic inline u_long dtlb_get_data_sun4u(int slot);
88181398Smariusstatic void dtlb_enter_sun4u(u_long vpn, u_long data);
89181398Smariusstatic vm_offset_t dtlb_va_to_pa_sun4u(vm_offset_t);
90182478Smariusstatic inline u_long itlb_get_data_sun4u(int slot);
91181398Smariusstatic void itlb_enter_sun4u(u_long vpn, u_long data);
92181398Smariusstatic vm_offset_t itlb_va_to_pa_sun4u(vm_offset_t);
93182916Smariusstatic void itlb_relocate_locked0_sun4u(void);
9485719Sjakeextern vm_offset_t md_load(char *, vm_offset_t *);
95170839Smariusstatic int sparc64_autoload(void);
96170839Smariusstatic ssize_t sparc64_readin(const int, vm_offset_t, const size_t);
97170839Smariusstatic ssize_t sparc64_copyin(const void *, vm_offset_t, size_t);
98170839Smariusstatic void sparc64_maphint(vm_offset_t, size_t);
99170854Smariusstatic vm_offset_t claim_virt(vm_offset_t, size_t, int);
100170854Smariusstatic vm_offset_t alloc_phys(size_t, int);
101170854Smariusstatic int map_phys(int, size_t, vm_offset_t, vm_offset_t);
102170854Smariusstatic void release_phys(vm_offset_t, u_int);
103114386Speterstatic int __elfN(exec)(struct preloaded_file *);
104163145Skmacystatic int mmu_mapin_sun4u(vm_offset_t, vm_size_t);
105163145Skmacystatic int mmu_mapin_sun4v(vm_offset_t, vm_size_t);
106170839Smariusstatic vm_offset_t init_heap(void);
107163145Skmacystatic void tlb_init_sun4u(void);
108163145Skmacystatic void tlb_init_sun4v(void);
10984996Srobert
110181398Smarius#ifdef LOADER_DEBUG
111181398Smariustypedef u_int64_t tte_t;
112181398Smarius
113181398Smariusstatic void pmap_print_tlb_sun4u(void);
114181398Smariusstatic void pmap_print_tte_sun4u(tte_t, tte_t);
115181398Smarius#endif
116181398Smarius
117170839Smariusstatic struct mmu_ops mmu_ops_sun4u = { tlb_init_sun4u, mmu_mapin_sun4u };
118170839Smariusstatic struct mmu_ops mmu_ops_sun4v = { tlb_init_sun4v, mmu_mapin_sun4v };
119163145Skmacy
120163145Skmacy/* sun4u */
12197445Sjakestruct tlb_entry *dtlb_store;
12297445Sjakestruct tlb_entry *itlb_store;
123163145Skmacyint dtlb_slot;
124163145Skmacyint itlb_slot;
125182478Smariusint cpu_impl;
126170839Smariusstatic int dtlb_slot_max;
127170839Smariusstatic int itlb_slot_max;
12891139Sjake
129163145Skmacy/* sun4v */
130170839Smariusstatic struct tlb_entry *tlb_store;
131170839Smariusstatic int is_sun4v = 0;
132170839Smarius/*
133163145Skmacy * no direct TLB access on sun4v
134170839Smarius * we somewhat arbitrarily declare enough
135163145Skmacy * slots to cover a 4GB AS with 4MB pages
136163145Skmacy */
137170839Smarius#define	SUN4V_TLB_SLOT_MAX	(1 << 10)
138163145Skmacy
139170839Smariusstatic vm_offset_t curkva = 0;
140170839Smariusstatic vm_offset_t heapva;
141163145Skmacy
142170854Smariusstatic phandle_t root;
14384996Srobert
14484996Srobert/*
14584996Srobert * Machine dependent structures that the machine independent
14684996Srobert * loader part uses.
14784996Srobert */
14884996Srobertstruct devsw *devsw[] = {
14985719Sjake#ifdef LOADER_DISK_SUPPORT
15084996Srobert	&ofwdisk,
15185719Sjake#endif
15285719Sjake#ifdef LOADER_NET_SUPPORT
15385719Sjake	&netdev,
15485719Sjake#endif
15584996Srobert	0
15684996Srobert};
15784996Srobertstruct arch_switch archsw;
15884996Srobert
159170839Smariusstatic struct file_format sparc64_elf = {
160114386Speter	__elfN(loadfile),
161114386Speter	__elfN(exec)
16284996Srobert};
16384996Srobertstruct file_format *file_formats[] = {
16484996Srobert	&sparc64_elf,
16584996Srobert	0
16684996Srobert};
16784996Srobertstruct fs_ops *file_system[] = {
16891110Sjake#ifdef LOADER_UFS_SUPPORT
16984996Srobert	&ufs_fsops,
17085719Sjake#endif
17193606Stmm#ifdef LOADER_CD9660_SUPPORT
17293606Stmm	&cd9660_fsops,
17393606Stmm#endif
174108100Sjake#ifdef LOADER_ZIP_SUPPORT
175105065Sjake	&zipfs_fsops,
176105065Sjake#endif
177108100Sjake#ifdef LOADER_GZIP_SUPPORT
178108100Sjake	&gzipfs_fsops,
179108100Sjake#endif
180105065Sjake#ifdef LOADER_BZIP2_SUPPORT
181105065Sjake	&bzipfs_fsops,
182105065Sjake#endif
183117448Stmm#ifdef LOADER_NFS_SUPPORT
18485719Sjake	&nfs_fsops,
18585719Sjake#endif
18691110Sjake#ifdef LOADER_TFTP_SUPPORT
18791110Sjake	&tftp_fsops,
18891110Sjake#endif
18984996Srobert	0
19084996Srobert};
19185719Sjakestruct netif_driver *netif_drivers[] = {
19285719Sjake#ifdef LOADER_NET_SUPPORT
19385719Sjake	&ofwnet,
19485719Sjake#endif
19585719Sjake	0
19685719Sjake};
19784996Srobert
19884996Srobertextern struct console ofwconsole;
19984996Srobertstruct console *consoles[] = {
20084996Srobert	&ofwconsole,
20184996Srobert	0
20284996Srobert};
20384996Srobert
20491110Sjake#ifdef LOADER_DEBUG
20591110Sjakestatic int
20691110Sjakewatch_phys_set_mask(vm_offset_t pa, u_long mask)
20791110Sjake{
20891110Sjake	u_long lsucr;
20991110Sjake
21091110Sjake	stxa(AA_DMMU_PWPR, ASI_DMMU, pa & (((2UL << 38) - 1) << 3));
21191110Sjake	lsucr = ldxa(0, ASI_LSU_CTL_REG);
21291110Sjake	lsucr = ((lsucr | LSU_PW) & ~LSU_PM_MASK) |
21391110Sjake	    (mask << LSU_PM_SHIFT);
21491110Sjake	stxa(0, ASI_LSU_CTL_REG, lsucr);
21591110Sjake	return (0);
21691110Sjake}
21791110Sjake
21891110Sjakestatic int
21991110Sjakewatch_phys_set(vm_offset_t pa, int sz)
22091110Sjake{
22191110Sjake	u_long off;
22291110Sjake
22391110Sjake	off = (u_long)pa & 7;
22491110Sjake	/* Test for misaligned watch points. */
22591110Sjake	if (off + sz > 8)
22691110Sjake		return (-1);
22791110Sjake	return (watch_phys_set_mask(pa, ((1 << sz) - 1) << off));
22891110Sjake}
22991110Sjake
23091110Sjake
23191110Sjakestatic int
23291110Sjakewatch_virt_set_mask(vm_offset_t va, u_long mask)
23391110Sjake{
23491110Sjake	u_long lsucr;
23591110Sjake
23691110Sjake	stxa(AA_DMMU_VWPR, ASI_DMMU, va & (((2UL << 41) - 1) << 3));
23791110Sjake	lsucr = ldxa(0, ASI_LSU_CTL_REG);
23891110Sjake	lsucr = ((lsucr | LSU_VW) & ~LSU_VM_MASK) |
23991110Sjake	    (mask << LSU_VM_SHIFT);
24091110Sjake	stxa(0, ASI_LSU_CTL_REG, lsucr);
24191110Sjake	return (0);
24291110Sjake}
24391110Sjake
24491110Sjakestatic int
24591110Sjakewatch_virt_set(vm_offset_t va, int sz)
24691110Sjake{
24791110Sjake	u_long off;
24891110Sjake
24991110Sjake	off = (u_long)va & 7;
25091110Sjake	/* Test for misaligned watch points. */
25191110Sjake	if (off + sz > 8)
25291110Sjake		return (-1);
25391110Sjake	return (watch_virt_set_mask(va, ((1 << sz) - 1) << off));
25491110Sjake}
25591110Sjake#endif
25691110Sjake
25784996Srobert/*
25884996Srobert * archsw functions
25984996Srobert */
26084996Srobertstatic int
26184996Srobertsparc64_autoload(void)
26284996Srobert{
263170839Smarius
264170839Smarius	return (0);
26584996Srobert}
26684996Srobert
26784996Srobertstatic ssize_t
26884996Srobertsparc64_readin(const int fd, vm_offset_t va, const size_t len)
26984996Srobert{
270170839Smarius
271163145Skmacy	mmu_ops->mmu_mapin(va, len);
272170839Smarius	return (read(fd, (void *)va, len));
27384996Srobert}
27484996Srobert
27584996Srobertstatic ssize_t
27684996Srobertsparc64_copyin(const void *src, vm_offset_t dest, size_t len)
27784996Srobert{
278170839Smarius
279163145Skmacy	mmu_ops->mmu_mapin(dest, len);
28084996Srobert	memcpy((void *)dest, src, len);
281170839Smarius	return (len);
28284996Srobert}
28384996Srobert
284165325Skmacystatic void
285165325Skmacysparc64_maphint(vm_offset_t va, size_t len)
286165325Skmacy{
287165325Skmacy	vm_paddr_t pa;
288165325Skmacy	vm_offset_t mva;
289165325Skmacy	size_t size;
290170854Smarius	int i, free_excess = 0;
291165325Skmacy
292165325Skmacy	if (!is_sun4v)
293165325Skmacy		return;
294165325Skmacy
295165325Skmacy	if (tlb_store[va >> 22].te_pa != -1)
296165325Skmacy		return;
297165325Skmacy
298165325Skmacy	/* round up to nearest 4MB page */
299165325Skmacy	size = (len + PAGE_MASK_4M) & ~PAGE_MASK_4M;
300170839Smarius#if 0
301170854Smarius	pa = alloc_phys(PAGE_SIZE_256M, PAGE_SIZE_256M);
302165325Skmacy
303165325Skmacy	if (pa != -1)
304165325Skmacy		free_excess = 1;
305165325Skmacy	else
306165325Skmacy#endif
307170854Smarius		pa = alloc_phys(size, PAGE_SIZE_256M);
308165325Skmacy	if (pa == -1)
309170854Smarius		pa = alloc_phys(size, PAGE_SIZE_4M);
310165325Skmacy	if (pa == -1)
311170854Smarius		panic("%s: out of memory", __func__);
312165325Skmacy
313165325Skmacy	for (i = 0; i < size; i += PAGE_SIZE_4M) {
314170854Smarius		mva = claim_virt(va + i, PAGE_SIZE_4M, 0);
315170839Smarius		if (mva != (va + i))
316170854Smarius			panic("%s: can't claim virtual page "
317170839Smarius			    "(wanted %#lx, got %#lx)",
318170854Smarius			    __func__, va, mva);
319165325Skmacy
320165325Skmacy		tlb_store[mva >> 22].te_pa = pa + i;
321170854Smarius		if (map_phys(-1, PAGE_SIZE_4M, mva, pa + i) != 0)
322170854Smarius			printf("%s: can't map physical page\n", __func__);
323165325Skmacy	}
324165325Skmacy	if (free_excess)
325170854Smarius		release_phys(pa, PAGE_SIZE_256M);
326165325Skmacy}
327165325Skmacy
32884996Srobert/*
32984996Srobert * other MD functions
33084996Srobert */
331170854Smariusstatic vm_offset_t
332170854Smariusclaim_virt(vm_offset_t virt, size_t size, int align)
333170854Smarius{
334170854Smarius	vm_offset_t mva;
335170854Smarius
336170854Smarius	if (OF_call_method("claim", mmu, 3, 1, virt, size, align, &mva) == -1)
337170854Smarius		return ((vm_offset_t)-1);
338170854Smarius	return (mva);
339170854Smarius}
340170854Smarius
341170854Smariusstatic vm_offset_t
342170854Smariusalloc_phys(size_t size, int align)
343170854Smarius{
344170854Smarius	cell_t phys_hi, phys_low;
345170854Smarius
346170854Smarius	if (OF_call_method("claim", memory, 2, 2, size, align, &phys_low,
347170854Smarius	    &phys_hi) == -1)
348170854Smarius		return ((vm_offset_t)-1);
349170854Smarius	return ((vm_offset_t)phys_hi << 32 | phys_low);
350170854Smarius}
351170854Smarius
35284996Srobertstatic int
353170854Smariusmap_phys(int mode, size_t size, vm_offset_t virt, vm_offset_t phys)
354170854Smarius{
355170854Smarius
356170854Smarius	return (OF_call_method("map", mmu, 5, 0, (uint32_t)phys,
357170854Smarius	    (uint32_t)(phys >> 32), virt, size, mode));
358170854Smarius}
359170854Smarius
360170854Smariusstatic void
361170854Smariusrelease_phys(vm_offset_t phys, u_int size)
362170854Smarius{
363170854Smarius
364170854Smarius	(void)OF_call_method("release", memory, 3, 0, (uint32_t)phys,
365170854Smarius	    (uint32_t)(phys >> 32), size);
366170854Smarius}
367170854Smarius
368170854Smariusstatic int
369114386Speter__elfN(exec)(struct preloaded_file *fp)
37084996Srobert{
37184996Srobert	struct file_metadata *fmp;
37285719Sjake	vm_offset_t mdp;
373116415Sjake	Elf_Addr entry;
37491139Sjake	Elf_Ehdr *e;
37585719Sjake	int error;
37684996Srobert
377170839Smarius	if ((fmp = file_findmetadata(fp, MODINFOMD_ELFHDR)) == 0)
378170839Smarius		return (EFTYPE);
37991139Sjake	e = (Elf_Ehdr *)&fmp->md_data;
38084996Srobert
38185719Sjake	if ((error = md_load(fp->f_args, &mdp)) != 0)
382170839Smarius		return (error);
38384996Srobert
38491139Sjake	printf("jumping to kernel entry at %#lx.\n", e->e_entry);
385188455Smarius#ifdef LOADER_DEBUG
386181398Smarius	pmap_print_tlb_sun4u();
38784996Srobert#endif
38885719Sjake
389200946Smarius	dev_cleanup();
390200946Smarius
391116415Sjake	entry = e->e_entry;
392116415Sjake
393165325Skmacy	OF_release((void *)heapva, HEAPSZ);
394116415Sjake
395116415Sjake	((kernel_entry_t *)entry)(mdp, 0, 0, 0, openfirmware);
396116415Sjake
397170854Smarius	panic("%s: exec returned", __func__);
39884996Srobert}
39984996Srobert
400182478Smariusstatic inline u_long
401182478Smariusdtlb_get_data_sun4u(int slot)
402182478Smarius{
403182478Smarius
404182478Smarius	/*
405182478Smarius	 * We read ASI_DTLB_DATA_ACCESS_REG twice in order to work
406182478Smarius	 * around errata of USIII and beyond.
407182478Smarius	 */
408182478Smarius	(void)ldxa(TLB_DAR_SLOT(slot), ASI_DTLB_DATA_ACCESS_REG);
409182478Smarius	return (ldxa(TLB_DAR_SLOT(slot), ASI_DTLB_DATA_ACCESS_REG));
410182478Smarius}
411182478Smarius
412182478Smariusstatic inline u_long
413182478Smariusitlb_get_data_sun4u(int slot)
414182478Smarius{
415182478Smarius
416182478Smarius	/*
417182478Smarius	 * We read ASI_ITLB_DATA_ACCESS_REG twice in order to work
418182478Smarius	 * around errata of USIII and beyond.
419182478Smarius	 */
420182478Smarius	(void)ldxa(TLB_DAR_SLOT(slot), ASI_ITLB_DATA_ACCESS_REG);
421182478Smarius	return (ldxa(TLB_DAR_SLOT(slot), ASI_ITLB_DATA_ACCESS_REG));
422182478Smarius}
423182478Smarius
424181398Smariusstatic vm_offset_t
425181398Smariusdtlb_va_to_pa_sun4u(vm_offset_t va)
426181398Smarius{
427182766Smarius	u_long pstate, reg;
428181398Smarius	int i;
429181398Smarius
430182766Smarius	pstate = rdpr(pstate);
431182766Smarius	wrpr(pstate, pstate & ~PSTATE_IE, 0);
432181398Smarius	for (i = 0; i < dtlb_slot_max; i++) {
433181398Smarius		reg = ldxa(TLB_DAR_SLOT(i), ASI_DTLB_TAG_READ_REG);
434181398Smarius		if (TLB_TAR_VA(reg) != va)
435181398Smarius			continue;
436182478Smarius		reg = dtlb_get_data_sun4u(i);
437182766Smarius		wrpr(pstate, pstate, 0);
438191071Smarius		reg >>= TD_PA_SHIFT;
439182478Smarius		if (cpu_impl >= CPU_IMPL_ULTRASPARCIII)
440191071Smarius			return (reg & TD_PA_CH_MASK);
441191071Smarius		return (reg & TD_PA_SF_MASK);
442181398Smarius	}
443182766Smarius	wrpr(pstate, pstate, 0);
444181398Smarius	return (-1);
445181398Smarius}
446181398Smarius
447181398Smariusstatic vm_offset_t
448181398Smariusitlb_va_to_pa_sun4u(vm_offset_t va)
449181398Smarius{
450182766Smarius	u_long pstate, reg;
451181398Smarius	int i;
452181398Smarius
453182766Smarius	pstate = rdpr(pstate);
454182766Smarius	wrpr(pstate, pstate & ~PSTATE_IE, 0);
455181398Smarius	for (i = 0; i < itlb_slot_max; i++) {
456181398Smarius		reg = ldxa(TLB_DAR_SLOT(i), ASI_ITLB_TAG_READ_REG);
457181398Smarius		if (TLB_TAR_VA(reg) != va)
458181398Smarius			continue;
459182478Smarius		reg = itlb_get_data_sun4u(i);
460182766Smarius		wrpr(pstate, pstate, 0);
461191071Smarius		reg >>= TD_PA_SHIFT;
462182478Smarius		if (cpu_impl >= CPU_IMPL_ULTRASPARCIII)
463191071Smarius			return (reg & TD_PA_CH_MASK);
464191071Smarius		return (reg & TD_PA_SF_MASK);
465181398Smarius	}
466182766Smarius	wrpr(pstate, pstate, 0);
467181398Smarius	return (-1);
468181398Smarius}
469181398Smarius
470181398Smariusstatic void
471182478Smariusdtlb_enter_sun4u(u_long vpn, u_long data)
472181398Smarius{
473181398Smarius	u_long reg;
474181398Smarius
475181398Smarius	reg = rdpr(pstate);
476181398Smarius	wrpr(pstate, reg & ~PSTATE_IE, 0);
477182478Smarius	stxa(AA_DMMU_TAR, ASI_DMMU,
478191012Smarius	    TLB_TAR_VA(vpn) | TLB_TAR_CTX(TLB_CTX_KERNEL));
479182478Smarius	stxa(0, ASI_DTLB_DATA_IN_REG, data);
480181398Smarius	membar(Sync);
481181398Smarius	wrpr(pstate, reg, 0);
482181398Smarius}
483181398Smarius
484181398Smariusstatic void
485182478Smariusitlb_enter_sun4u(u_long vpn, u_long data)
486181398Smarius{
487181398Smarius	u_long reg;
488182916Smarius	int i;
489181398Smarius
490181398Smarius	reg = rdpr(pstate);
491181398Smarius	wrpr(pstate, reg & ~PSTATE_IE, 0);
492182916Smarius
493182916Smarius	if (cpu_impl == CPU_IMPL_ULTRASPARCIIIp) {
494182916Smarius		/*
495182916Smarius		 * Search an unused slot != 0 and explicitly enter the data
496182916Smarius		 * and tag there in order to avoid Cheetah+ erratum 34.
497182916Smarius		 */
498182916Smarius		for (i = 1; i < itlb_slot_max; i++) {
499182916Smarius			if ((itlb_get_data_sun4u(i) & TD_V) != 0)
500182916Smarius				continue;
501182916Smarius
502182916Smarius			stxa(AA_IMMU_TAR, ASI_IMMU,
503191012Smarius			    TLB_TAR_VA(vpn) | TLB_TAR_CTX(TLB_CTX_KERNEL));
504182916Smarius			stxa(TLB_DAR_SLOT(i), ASI_ITLB_DATA_ACCESS_REG, data);
505188455Smarius			flush(PROMBASE);
506182916Smarius			break;
507182916Smarius		}
508182916Smarius		wrpr(pstate, reg, 0);
509182916Smarius		if (i == itlb_slot_max)
510182916Smarius			panic("%s: could not find an unused slot", __func__);
511182916Smarius		return;
512182916Smarius	}
513182916Smarius
514182478Smarius	stxa(AA_IMMU_TAR, ASI_IMMU,
515191012Smarius	    TLB_TAR_VA(vpn) | TLB_TAR_CTX(TLB_CTX_KERNEL));
516182478Smarius	stxa(0, ASI_ITLB_DATA_IN_REG, data);
517188455Smarius	flush(PROMBASE);
518181398Smarius	wrpr(pstate, reg, 0);
519181398Smarius}
520181398Smarius
521182916Smariusstatic void
522182916Smariusitlb_relocate_locked0_sun4u(void)
523182916Smarius{
524182916Smarius	u_long data, pstate, tag;
525182916Smarius	int i;
526182916Smarius
527182916Smarius	if (cpu_impl != CPU_IMPL_ULTRASPARCIIIp)
528182916Smarius		return;
529182916Smarius
530182916Smarius	pstate = rdpr(pstate);
531182916Smarius	wrpr(pstate, pstate & ~PSTATE_IE, 0);
532182916Smarius
533182916Smarius	data = itlb_get_data_sun4u(0);
534182916Smarius	if ((data & (TD_V | TD_L)) != (TD_V | TD_L)) {
535182916Smarius		wrpr(pstate, pstate, 0);
536182916Smarius		return;
537182916Smarius	}
538182916Smarius
539182916Smarius	/* Flush the mapping of slot 0. */
540182916Smarius	tag = ldxa(TLB_DAR_SLOT(0), ASI_ITLB_TAG_READ_REG);
541182916Smarius	stxa(TLB_DEMAP_VA(TLB_TAR_VA(tag)) | TLB_DEMAP_PRIMARY |
542182916Smarius	    TLB_DEMAP_PAGE, ASI_IMMU_DEMAP, 0);
543182916Smarius	flush(0);	/* The USIII-family ignores the address. */
544182916Smarius
545182916Smarius	/*
546182916Smarius	 * Search a replacement slot != 0 and enter the data and tag
547182916Smarius	 * that formerly were in slot 0.
548182916Smarius	 */
549182916Smarius	for (i = 1; i < itlb_slot_max; i++) {
550182916Smarius		if ((itlb_get_data_sun4u(i) & TD_V) != 0)
551182916Smarius			continue;
552182916Smarius
553182916Smarius		stxa(AA_IMMU_TAR, ASI_IMMU, tag);
554182916Smarius		stxa(TLB_DAR_SLOT(i), ASI_ITLB_DATA_ACCESS_REG, data);
555182916Smarius		flush(0);	/* The USIII-family ignores the address. */
556182916Smarius		break;
557182916Smarius	}
558182916Smarius	wrpr(pstate, pstate, 0);
559182916Smarius	if (i == itlb_slot_max)
560182916Smarius		panic("%s: could not find a replacement slot", __func__);
561182916Smarius}
562182916Smarius
56384996Srobertstatic int
564163145Skmacymmu_mapin_sun4u(vm_offset_t va, vm_size_t len)
56584996Srobert{
56691110Sjake	vm_offset_t pa, mva;
56797445Sjake	u_long data;
56884996Srobert
56984996Srobert	if (va + len > curkva)
57084996Srobert		curkva = va + len;
57184996Srobert
57291110Sjake	pa = (vm_offset_t)-1;
57385719Sjake	len += va & PAGE_MASK_4M;
57485719Sjake	va &= ~PAGE_MASK_4M;
57584996Srobert	while (len) {
576181398Smarius		if (dtlb_va_to_pa_sun4u(va) == (vm_offset_t)-1 ||
577181398Smarius		    itlb_va_to_pa_sun4u(va) == (vm_offset_t)-1) {
578182020Smarius			/* Allocate a physical page, claim the virtual area. */
57991110Sjake			if (pa == (vm_offset_t)-1) {
580170854Smarius				pa = alloc_phys(PAGE_SIZE_4M, PAGE_SIZE_4M);
58191110Sjake				if (pa == (vm_offset_t)-1)
582170854Smarius					panic("%s: out of memory", __func__);
583170854Smarius				mva = claim_virt(va, PAGE_SIZE_4M, 0);
584170839Smarius				if (mva != va)
585170854Smarius					panic("%s: can't claim virtual page "
58691110Sjake					    "(wanted %#lx, got %#lx)",
587170854Smarius					    __func__, va, mva);
588182020Smarius				/*
589182020Smarius				 * The mappings may have changed, be paranoid.
590182020Smarius				 */
59191110Sjake				continue;
59291110Sjake			}
59393678Stmm			/*
59493678Stmm			 * Actually, we can only allocate two pages less at
59593678Stmm			 * most (depending on the kernel TSB size).
59693678Stmm			 */
59793678Stmm			if (dtlb_slot >= dtlb_slot_max)
598170839Smarius				panic("%s: out of dtlb_slots", __func__);
59993678Stmm			if (itlb_slot >= itlb_slot_max)
600170839Smarius				panic("%s: out of itlb_slots", __func__);
60197445Sjake			data = TD_V | TD_4M | TD_PA(pa) | TD_L | TD_CP |
60291139Sjake			    TD_CV | TD_P | TD_W;
60397445Sjake			dtlb_store[dtlb_slot].te_pa = pa;
60497445Sjake			dtlb_store[dtlb_slot].te_va = va;
60597445Sjake			itlb_store[itlb_slot].te_pa = pa;
60697445Sjake			itlb_store[itlb_slot].te_va = va;
60797445Sjake			dtlb_slot++;
60897445Sjake			itlb_slot++;
609181398Smarius			dtlb_enter_sun4u(va, data);
610181398Smarius			itlb_enter_sun4u(va, data);
61191110Sjake			pa = (vm_offset_t)-1;
61284996Srobert		}
61385719Sjake		len -= len > PAGE_SIZE_4M ? PAGE_SIZE_4M : len;
61485719Sjake		va += PAGE_SIZE_4M;
61584996Srobert	}
61691110Sjake	if (pa != (vm_offset_t)-1)
617170854Smarius		release_phys(pa, PAGE_SIZE_4M);
618170839Smarius	return (0);
61984996Srobert}
62084996Srobert
621163145Skmacystatic int
622163145Skmacymmu_mapin_sun4v(vm_offset_t va, vm_size_t len)
623163145Skmacy{
624163145Skmacy	vm_offset_t pa, mva;
625163145Skmacy
626163145Skmacy	if (va + len > curkva)
627163145Skmacy		curkva = va + len;
628163145Skmacy
629163145Skmacy	pa = (vm_offset_t)-1;
630163145Skmacy	len += va & PAGE_MASK_4M;
631163145Skmacy	va &= ~PAGE_MASK_4M;
632163145Skmacy	while (len) {
633163145Skmacy		if ((va >> 22) > SUN4V_TLB_SLOT_MAX)
634170854Smarius			panic("%s: trying to map more than 4GB", __func__);
635163145Skmacy		if (tlb_store[va >> 22].te_pa == -1) {
636163145Skmacy			/* Allocate a physical page, claim the virtual area */
637163145Skmacy			if (pa == (vm_offset_t)-1) {
638170854Smarius				pa = alloc_phys(PAGE_SIZE_4M, PAGE_SIZE_4M);
639163145Skmacy				if (pa == (vm_offset_t)-1)
640170854Smarius				    panic("%s: out of memory", __func__);
641170854Smarius				mva = claim_virt(va, PAGE_SIZE_4M, 0);
642170839Smarius				if (mva != va)
643170854Smarius					panic("%s: can't claim virtual page "
644163145Skmacy					    "(wanted %#lx, got %#lx)",
645170854Smarius					    __func__, va, mva);
646163145Skmacy			}
647163145Skmacy
648163145Skmacy			tlb_store[va >> 22].te_pa = pa;
649170854Smarius			if (map_phys(-1, PAGE_SIZE_4M, va, pa) == -1)
650170854Smarius				printf("%s: can't map physical page\n",
651170854Smarius				    __func__);
652163145Skmacy			pa = (vm_offset_t)-1;
653163145Skmacy		}
654163145Skmacy		len -= len > PAGE_SIZE_4M ? PAGE_SIZE_4M : len;
655163145Skmacy		va += PAGE_SIZE_4M;
656163145Skmacy	}
657163145Skmacy	if (pa != (vm_offset_t)-1)
658170854Smarius		release_phys(pa, PAGE_SIZE_4M);
659170839Smarius	return (0);
660163145Skmacy}
661163145Skmacy
66284996Srobertstatic vm_offset_t
66384996Srobertinit_heap(void)
66484996Srobert{
66584996Srobert
66684996Srobert	/* There is no need for continuous physical heap memory. */
66784996Srobert	heapva = (vm_offset_t)OF_claim((void *)HEAPVA, HEAPSZ, 32);
668170839Smarius	return (heapva);
66984996Srobert}
67084996Srobert
67191139Sjakestatic void
672163145Skmacytlb_init_sun4u(void)
67391139Sjake{
67491139Sjake	phandle_t child;
67591139Sjake	char buf[128];
67691139Sjake	u_int bootcpu;
67791139Sjake	u_int cpu;
67891139Sjake
679182478Smarius	cpu_impl = VER_IMPL(rdpr(ver));
68091139Sjake	bootcpu = UPA_CR_GET_MID(ldxa(0, ASI_UPA_CONFIG_REG));
68191139Sjake	for (child = OF_child(root); child != 0; child = OF_peer(child)) {
682182478Smarius		if (OF_getprop(child, "device_type", buf, sizeof(buf)) <= 0)
683182478Smarius			continue;
684182478Smarius		if (strcmp(buf, "cpu") != 0)
685182478Smarius			continue;
686182478Smarius		if (OF_getprop(child, cpu_impl < CPU_IMPL_ULTRASPARCIII ?
687182478Smarius		    "upa-portid" : "portid", &cpu, sizeof(cpu)) <= 0)
688182478Smarius			continue;
689182478Smarius		if (cpu == bootcpu)
690182478Smarius			break;
69191139Sjake	}
69291139Sjake	if (cpu != bootcpu)
693170839Smarius		panic("%s: no node for bootcpu?!?!", __func__);
694182020Smarius
69591139Sjake	if (OF_getprop(child, "#dtlb-entries", &dtlb_slot_max,
69691139Sjake	    sizeof(dtlb_slot_max)) == -1 ||
69791139Sjake	    OF_getprop(child, "#itlb-entries", &itlb_slot_max,
69891139Sjake	    sizeof(itlb_slot_max)) == -1)
699170854Smarius		panic("%s: can't get TLB slot max.", __func__);
700182916Smarius
701182916Smarius	if (cpu_impl == CPU_IMPL_ULTRASPARCIIIp) {
702182916Smarius#ifdef LOADER_DEBUG
703182916Smarius		printf("pre fixup:\n");
704182916Smarius		pmap_print_tlb_sun4u();
705182916Smarius#endif
706182916Smarius
707182916Smarius		/*
708182916Smarius		 * Relocate the locked entry in it16 slot 0 (if existent)
709182916Smarius		 * as part of working around Cheetah+ erratum 34.
710182916Smarius		 */
711182916Smarius		itlb_relocate_locked0_sun4u();
712182916Smarius
713182916Smarius#ifdef LOADER_DEBUG
714182916Smarius		printf("post fixup:\n");
715182916Smarius		pmap_print_tlb_sun4u();
716182916Smarius#endif
717182916Smarius	}
718182916Smarius
71991139Sjake	dtlb_store = malloc(dtlb_slot_max * sizeof(*dtlb_store));
72091139Sjake	itlb_store = malloc(itlb_slot_max * sizeof(*itlb_store));
72191139Sjake	if (dtlb_store == NULL || itlb_store == NULL)
722170854Smarius		panic("%s: can't allocate TLB store", __func__);
72391139Sjake}
72491139Sjake
725163145Skmacystatic void
726163145Skmacytlb_init_sun4v(void)
727163145Skmacy{
728170839Smarius
729163145Skmacy	tlb_store = malloc(SUN4V_TLB_SLOT_MAX * sizeof(*tlb_store));
730163145Skmacy	memset(tlb_store, 0xFF, SUN4V_TLB_SLOT_MAX * sizeof(*tlb_store));
731163145Skmacy}
732163145Skmacy
73385719Sjakeint
73485719Sjakemain(int (*openfirm)(void *))
73584996Srobert{
73684996Srobert	char bootpath[64];
737163145Skmacy	char compatible[32];
73884996Srobert	struct devsw **dp;
73984996Srobert
74084996Srobert	/*
741182020Smarius	 * Tell the Open Firmware functions where they find the OFW gate.
74284996Srobert	 */
74385719Sjake	OF_init(openfirm);
74484996Srobert
74584996Srobert	archsw.arch_getdev = ofw_getdev;
74684996Srobert	archsw.arch_copyin = sparc64_copyin;
74784996Srobert	archsw.arch_copyout = ofw_copyout;
74884996Srobert	archsw.arch_readin = sparc64_readin;
74984996Srobert	archsw.arch_autoload = sparc64_autoload;
750165325Skmacy	archsw.arch_maphint = sparc64_maphint;
75184996Srobert
75284996Srobert	init_heap();
75384996Srobert	setheap((void *)heapva, (void *)(heapva + HEAPSZ));
754170839Smarius
75584996Srobert	/*
75684996Srobert	 * Probe for a console.
75784996Srobert	 */
75884996Srobert	cons_probe();
75984996Srobert
760170854Smarius	if ((root = OF_peer(0)) == -1)
761170854Smarius		panic("%s: can't get root phandle", __func__);
762170854Smarius	OF_getprop(root, "compatible", compatible, sizeof(compatible));
763163145Skmacy	if (!strcmp(compatible, "sun4v")) {
764163145Skmacy		printf("\nBooting with sun4v support.\n");
765163145Skmacy		mmu_ops = &mmu_ops_sun4v;
766165325Skmacy		is_sun4v = 1;
767163145Skmacy	} else {
768163145Skmacy		printf("\nBooting with sun4u support.\n");
769163145Skmacy		mmu_ops = &mmu_ops_sun4u;
770163145Skmacy	}
77191139Sjake
772163145Skmacy	mmu_ops->tlb_init();
773163145Skmacy
77484996Srobert	/*
77584996Srobert	 * Initialize devices.
77684996Srobert	 */
77784996Srobert	for (dp = devsw; *dp != 0; dp++) {
77884996Srobert		if ((*dp)->dv_init != 0)
77984996Srobert			(*dp)->dv_init();
78084996Srobert	}
78184996Srobert
78284996Srobert	/*
78384996Srobert	 * Set up the current device.
78484996Srobert	 */
785170854Smarius	OF_getprop(chosen, "bootpath", bootpath, sizeof(bootpath));
78684996Srobert
787106738Sjake	/*
788106738Sjake	 * Sun compatible bootable CD-ROMs have a disk label placed
789106738Sjake	 * before the cd9660 data, with the actual filesystem being
790106738Sjake	 * in the first partition, while the other partitions contain
791106738Sjake	 * pseudo disk labels with embedded boot blocks for different
792106738Sjake	 * architectures, which may be followed by UFS filesystems.
793106738Sjake	 * The firmware will set the boot path to the partition it
794106738Sjake	 * boots from ('f' in the sun4u case), but we want the kernel
795106738Sjake	 * to be loaded from the cd9660 fs ('a'), so the boot path
796106738Sjake	 * needs to be altered.
797106738Sjake	 */
798106738Sjake	if (bootpath[strlen(bootpath) - 2] == ':' &&
799106738Sjake	    bootpath[strlen(bootpath) - 1] == 'f') {
800106738Sjake		bootpath[strlen(bootpath) - 1] = 'a';
801106738Sjake		printf("Boot path set to %s\n", bootpath);
80284996Srobert	}
80384996Srobert
804106738Sjake	env_setenv("currdev", EV_VOLATILE, bootpath,
80584996Srobert	    ofw_setcurrdev, env_nounset);
806106738Sjake	env_setenv("loaddev", EV_VOLATILE, bootpath,
80784996Srobert	    env_noset, env_nounset);
80884996Srobert
809101287Sjake	printf("\n");
810101287Sjake	printf("%s, Revision %s\n", bootprog_name, bootprog_rev);
811101287Sjake	printf("(%s, %s)\n", bootprog_maker, bootprog_date);
81284996Srobert	printf("bootpath=\"%s\"\n", bootpath);
81384996Srobert
81484996Srobert	/* Give control to the machine independent loader code. */
81584996Srobert	interact();
816170839Smarius	return (1);
81784996Srobert}
81884996Srobert
81991110SjakeCOMMAND_SET(reboot, "reboot", "reboot the system", command_reboot);
82091110Sjake
82191110Sjakestatic int
82291110Sjakecommand_reboot(int argc, char *argv[])
82391110Sjake{
82491110Sjake	int i;
82591110Sjake
82691110Sjake	for (i = 0; devsw[i] != NULL; ++i)
82791110Sjake		if (devsw[i]->dv_cleanup != NULL)
82891110Sjake			(devsw[i]->dv_cleanup)();
82991110Sjake
83091110Sjake	printf("Rebooting...\n");
83191110Sjake	OF_exit();
83291110Sjake}
83391110Sjake
83491110Sjake/* provide this for panic, as it's not in the startup code */
83591110Sjakevoid
83691110Sjakeexit(int code)
83791110Sjake{
838170839Smarius
83991110Sjake	OF_exit();
84091110Sjake}
84191110Sjake
84291110Sjake#ifdef LOADER_DEBUG
843188455Smariusstatic const char *const page_sizes[] = {
84484996Srobert	"  8k", " 64k", "512k", "  4m"
84584996Srobert};
84684996Srobert
84784996Srobertstatic void
848181398Smariuspmap_print_tte_sun4u(tte_t tag, tte_t tte)
84984996Srobert{
850170839Smarius
85184996Srobert	printf("%s %s ",
852191071Smarius	    page_sizes[(tte >> TD_SIZE_SHIFT) & TD_SIZE_MASK],
85384996Srobert	    tag & TD_G ? "G" : " ");
85484996Srobert	printf(tte & TD_W ? "W " : "  ");
85584996Srobert	printf(tte & TD_P ? "\e[33mP\e[0m " : "  ");
85684996Srobert	printf(tte & TD_E ? "E " : "  ");
85784996Srobert	printf(tte & TD_CV ? "CV " : "   ");
85884996Srobert	printf(tte & TD_CP ? "CP " : "   ");
85984996Srobert	printf(tte & TD_L ? "\e[32mL\e[0m " : "  ");
86084996Srobert	printf(tte & TD_IE ? "IE " : "   ");
86184996Srobert	printf(tte & TD_NFO ? "NFO " : "    ");
862181398Smarius	printf("pa=0x%lx va=0x%lx ctx=%ld\n",
863181398Smarius	    TD_PA(tte), TLB_TAR_VA(tag), TLB_TAR_CTX(tag));
86484996Srobert}
865181398Smarius
866181398Smariusstatic void
867181398Smariuspmap_print_tlb_sun4u(void)
86884996Srobert{
869181398Smarius	tte_t tag, tte;
870182478Smarius	u_long pstate;
87184996Srobert	int i;
87284996Srobert
873182478Smarius	pstate = rdpr(pstate);
874181398Smarius	for (i = 0; i < itlb_slot_max; i++) {
875182478Smarius		wrpr(pstate, pstate & ~PSTATE_IE, 0);
876182478Smarius		tte = itlb_get_data_sun4u(i);
877182478Smarius		wrpr(pstate, pstate, 0);
87884996Srobert		if (!(tte & TD_V))
87984996Srobert			continue;
880181398Smarius		tag = ldxa(TLB_DAR_SLOT(i), ASI_ITLB_TAG_READ_REG);
881181398Smarius		printf("iTLB-%2u: ", i);
882181398Smarius		pmap_print_tte_sun4u(tag, tte);
88384996Srobert	}
884181398Smarius	for (i = 0; i < dtlb_slot_max; i++) {
885182478Smarius		wrpr(pstate, pstate & ~PSTATE_IE, 0);
886182478Smarius		tte = dtlb_get_data_sun4u(i);
887182478Smarius		wrpr(pstate, pstate, 0);
888181398Smarius		if (!(tte & TD_V))
889181398Smarius			continue;
890181398Smarius		tag = ldxa(TLB_DAR_SLOT(i), ASI_DTLB_TAG_READ_REG);
891181398Smarius		printf("dTLB-%2u: ", i);
892181398Smarius		pmap_print_tte_sun4u(tag, tte);
893181398Smarius	}
89484996Srobert}
89591110Sjake#endif
896