1139738Simp/*-
2164010Smarcel * Copyright (c) 2004, 2006 Marcel Moolenaar
3138140Smarcel * All rights reserved.
4138140Smarcel *
5138140Smarcel * Redistribution and use in source and binary forms, with or without
6138140Smarcel * modification, are permitted provided that the following conditions
7138140Smarcel * are met:
8138140Smarcel *
9138140Smarcel * 1. Redistributions of source code must retain the above copyright
10138140Smarcel *    notice, this list of conditions and the following disclaimer.
11138140Smarcel * 2. Redistributions in binary form must reproduce the above copyright
12138140Smarcel *    notice, this list of conditions and the following disclaimer in the
13138140Smarcel *    documentation and/or other materials provided with the distribution.
14138140Smarcel *
15138140Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16138140Smarcel * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17138140Smarcel * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18138140Smarcel * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19138140Smarcel * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20138140Smarcel * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21138140Smarcel * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22138140Smarcel * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23138140Smarcel * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24138140Smarcel * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25138140Smarcel */
26138140Smarcel
27138140Smarcel#include <sys/cdefs.h>
28138140Smarcel__FBSDID("$FreeBSD$");
29138140Smarcel
30164010Smarcel#include <stand.h>
31164010Smarcel
32138140Smarcel#include <efi.h>
33138140Smarcel#include <efilib.h>
34138140Smarcel
35164010Smarcel#include <libia64.h>
36138140Smarcel
37164010Smarcel#define EFI_INTEL_FPSWA		\
38164010Smarcel    {0xc41b6531,0x97b9,0x11d3,{0x9a,0x29,0x00,0x90,0x27,0x3f,0xc1,0x4d}}
39164010Smarcel
40164010Smarcelstatic EFI_GUID fpswa_guid = EFI_INTEL_FPSWA;
41164010Smarcel
42164010Smarcel/* DIG64 Headless Console & Debug Port Table. */
43164010Smarcel#define	HCDP_TABLE_GUID		\
44164010Smarcel    {0xf951938d,0x620b,0x42ef,{0x82,0x79,0xa8,0x4b,0x79,0x61,0x78,0x98}}
45164010Smarcel
46164010Smarcelstatic EFI_GUID hcdp_guid = HCDP_TABLE_GUID;
47164010Smarcel
48219691Smarcelstatic EFI_MEMORY_DESCRIPTOR *memmap;
49219691Smarcelstatic UINTN memmapsz;
50164010Smarcelstatic UINTN mapkey;
51219691Smarcelstatic UINTN descsz;
52219691Smarcelstatic UINT32 descver;
53164010Smarcel
54219691Smarcel#define	IA64_EFI_CHUNK_SIZE	(32 * 1048576)
55219691Smarcelstatic vm_paddr_t ia64_efi_chunk;
56219691Smarcel
57219691Smarcel#define	IA64_EFI_PGTBLSZ_MAX	1048576
58219691Smarcelstatic vm_paddr_t ia64_efi_pgtbl;
59219691Smarcelstatic vm_size_t ia64_efi_pgtblsz;
60219691Smarcel
61219691Smarcel/* Don't allocate memory below the boundary */
62219691Smarcel#define	IA64_EFI_ALLOC_BOUNDARY	1048576
63219691Smarcel
64219691Smarcelstatic int
65219691Smarcelia64_efi_memmap_update(void)
66138140Smarcel{
67219691Smarcel	EFI_STATUS status;
68138140Smarcel
69219691Smarcel	if (memmap != NULL) {
70219691Smarcel		free(memmap);
71219691Smarcel		memmap = NULL;
72219691Smarcel	}
73219691Smarcel
74219691Smarcel	memmapsz = 0;
75219691Smarcel	BS->GetMemoryMap(&memmapsz, NULL, &mapkey, &descsz, &descver);
76219691Smarcel	if (memmapsz == 0)
77219691Smarcel		return (FALSE);
78219691Smarcel	memmap = malloc(memmapsz);
79219691Smarcel	if (memmap == NULL)
80219691Smarcel		return (FALSE);
81219691Smarcel
82219691Smarcel	status = BS->GetMemoryMap(&memmapsz, memmap, &mapkey, &descsz,
83219691Smarcel	    &descver);
84219691Smarcel	if (EFI_ERROR(status)) {
85219691Smarcel		free(memmap);
86219691Smarcel		memmap = NULL;
87219691Smarcel		return (FALSE);
88219691Smarcel	}
89219691Smarcel
90219691Smarcel	return (TRUE);
91219691Smarcel}
92219691Smarcel
93221269Smarcel/*
94221269Smarcel * Returns 0 on failure. Successful allocations return an address
95221269Smarcel * larger or equal to IA64_EFI_ALLOC_BOUNDARY.
96221269Smarcel */
97219691Smarcelstatic vm_paddr_t
98219691Smarcelia64_efi_alloc(vm_size_t sz)
99219691Smarcel{
100219691Smarcel	EFI_PHYSICAL_ADDRESS pa;
101219691Smarcel	EFI_MEMORY_DESCRIPTOR *mm;
102219691Smarcel	uint8_t *mmiter, *mmiterend;
103219691Smarcel	vm_size_t memsz;
104219691Smarcel	UINTN npgs;
105219691Smarcel	EFI_STATUS status;
106219691Smarcel
107219691Smarcel	/* We can't allocate less than a page */
108219691Smarcel	if (sz < EFI_PAGE_SIZE)
109219691Smarcel		return (0);
110219691Smarcel
111219691Smarcel	/* The size must be a power of 2. */
112219691Smarcel	if (sz & (sz - 1))
113219691Smarcel		return (0);
114219691Smarcel
115219691Smarcel	if (!ia64_efi_memmap_update())
116219691Smarcel		return (0);
117219691Smarcel
118219691Smarcel	mmiter = (void *)memmap;
119219691Smarcel	mmiterend = mmiter + memmapsz;
120219691Smarcel	for (; mmiter < mmiterend; mmiter += descsz) {
121219691Smarcel		mm = (void *)mmiter;
122219691Smarcel		if (mm->Type != EfiConventionalMemory)
123219691Smarcel			continue;
124219691Smarcel		memsz = mm->NumberOfPages * EFI_PAGE_SIZE;
125219691Smarcel		if (mm->PhysicalStart + memsz <= IA64_EFI_ALLOC_BOUNDARY)
126219691Smarcel			continue;
127219691Smarcel		/*
128219691Smarcel		 * XXX We really should make sure the memory is local to the
129219691Smarcel		 * BSP.
130219691Smarcel		 */
131219691Smarcel		pa = (mm->PhysicalStart < IA64_EFI_ALLOC_BOUNDARY) ?
132219691Smarcel		    IA64_EFI_ALLOC_BOUNDARY : mm->PhysicalStart;
133219691Smarcel		pa  = (pa + sz - 1) & ~(sz - 1);
134219691Smarcel		if (pa + sz > mm->PhysicalStart + memsz)
135219691Smarcel			continue;
136219691Smarcel
137219691Smarcel		npgs = EFI_SIZE_TO_PAGES(sz);
138219691Smarcel		status = BS->AllocatePages(AllocateAddress, EfiLoaderData,
139219691Smarcel		    npgs, &pa);
140219691Smarcel		if (!EFI_ERROR(status))
141219691Smarcel			return (pa);
142219691Smarcel	}
143219691Smarcel
144219691Smarcel	printf("%s: unable to allocate %lx bytes\n", __func__, sz);
145164010Smarcel	return (0);
146138140Smarcel}
147164010Smarcel
148219691Smarcelvm_paddr_t
149219691Smarcelia64_platform_alloc(vm_offset_t va, vm_size_t sz)
150219691Smarcel{
151221269Smarcel	vm_paddr_t pa;
152219691Smarcel
153219691Smarcel	if (va == 0) {
154219691Smarcel		/* Page table itself. */
155219691Smarcel		if (sz > IA64_EFI_PGTBLSZ_MAX)
156221269Smarcel			return (~0UL);
157219691Smarcel		if (ia64_efi_pgtbl == 0)
158219691Smarcel			ia64_efi_pgtbl = ia64_efi_alloc(IA64_EFI_PGTBLSZ_MAX);
159219691Smarcel		if (ia64_efi_pgtbl != 0)
160219691Smarcel			ia64_efi_pgtblsz = sz;
161219691Smarcel		return (ia64_efi_pgtbl);
162219691Smarcel	} else if (va < IA64_PBVM_BASE) {
163219691Smarcel		/* Should not happen. */
164221269Smarcel		return (~0UL);
165219691Smarcel	}
166219691Smarcel
167219691Smarcel	/* Loader virtual memory page. */
168219691Smarcel	va -= IA64_PBVM_BASE;
169219691Smarcel
170219691Smarcel	/* Allocate a big chunk that can be wired with a single PTE. */
171219691Smarcel	if (ia64_efi_chunk == 0)
172219691Smarcel		ia64_efi_chunk = ia64_efi_alloc(IA64_EFI_CHUNK_SIZE);
173219691Smarcel	if (va < IA64_EFI_CHUNK_SIZE)
174219691Smarcel		return (ia64_efi_chunk + va);
175219691Smarcel
176219691Smarcel	/* Allocate a page at a time when we go beyond the chunk. */
177221269Smarcel	pa = ia64_efi_alloc(sz);
178221269Smarcel	return ((pa == 0) ? ~0UL : pa);
179219691Smarcel}
180219691Smarcel
181219691Smarcelvoid
182219691Smarcelia64_platform_free(vm_offset_t va, vm_paddr_t pa, vm_size_t sz)
183219691Smarcel{
184219691Smarcel
185219691Smarcel	BS->FreePages(pa, sz >> EFI_PAGE_SHIFT);
186219691Smarcel}
187219691Smarcel
188164010Smarcelint
189219691Smarcelia64_platform_bootinfo(struct bootinfo *bi, struct bootinfo **res)
190164010Smarcel{
191164010Smarcel	VOID *fpswa;
192164010Smarcel	EFI_HANDLE handle;
193164010Smarcel	EFI_STATUS status;
194219691Smarcel	UINTN sz;
195164010Smarcel
196164010Smarcel	bi->bi_systab = (uint64_t)ST;
197164010Smarcel	bi->bi_hcdp = (uint64_t)efi_get_table(&hcdp_guid);
198164010Smarcel
199164010Smarcel	sz = sizeof(EFI_HANDLE);
200164010Smarcel	status = BS->LocateHandle(ByProtocol, &fpswa_guid, 0, &sz, &handle);
201164010Smarcel	if (status == 0)
202164010Smarcel		status = BS->HandleProtocol(handle, &fpswa_guid, &fpswa);
203164010Smarcel	bi->bi_fpswa = (status == 0) ? (uint64_t)fpswa : 0;
204164010Smarcel
205219691Smarcel	if (!ia64_efi_memmap_update())
206164010Smarcel		return (ENOMEM);
207164010Smarcel
208219691Smarcel	bi->bi_memmap = (uint64_t)memmap;
209219691Smarcel	bi->bi_memmap_size = memmapsz;
210219691Smarcel	bi->bi_memdesc_size = descsz;
211219691Smarcel	bi->bi_memdesc_version = descver;
212164010Smarcel
213219691Smarcel	if (IS_LEGACY_KERNEL())
214219691Smarcel		*res = malloc(sizeof(**res));
215219691Smarcel
216164010Smarcel	return (0);
217164010Smarcel}
218164010Smarcel
219164010Smarcelint
220219691Smarcelia64_platform_enter(const char *kernel)
221164010Smarcel{
222164010Smarcel	EFI_STATUS status;
223164010Smarcel
224164010Smarcel	status = BS->ExitBootServices(IH, mapkey);
225164010Smarcel	if (EFI_ERROR(status)) {
226164010Smarcel		printf("%s: ExitBootServices() returned 0x%lx\n", __func__,
227164010Smarcel		    (long)status);
228164010Smarcel		return (EINVAL);
229164010Smarcel	}
230164010Smarcel
231164010Smarcel	return (0);
232164010Smarcel}
233222799Smarcel
234222799SmarcelCOMMAND_SET(pbvm, "pbvm", "show PBVM details", command_pbvm);
235222799Smarcel
236222799Smarcelstatic int
237222799Smarcelcommand_pbvm(int argc, char *argv[])
238222799Smarcel{
239222799Smarcel	uint64_t limit, pg, start;
240222799Smarcel	u_int idx;
241222799Smarcel
242222799Smarcel	printf("Page table @ %p, size %x\n", ia64_pgtbl, ia64_pgtblsz);
243222799Smarcel
244222799Smarcel	if (ia64_pgtbl == NULL)
245222799Smarcel		return (0);
246222799Smarcel
247222799Smarcel	limit = ~0;
248222799Smarcel	start = ~0;
249222799Smarcel	idx = 0;
250222799Smarcel	while (ia64_pgtbl[idx] != 0) {
251222799Smarcel		pg = ia64_pgtbl[idx];
252222799Smarcel		if (pg != limit) {
253222799Smarcel			if (start != ~0)
254222799Smarcel				printf("%#lx-%#lx\n", start, limit);
255222799Smarcel			start = pg;
256222799Smarcel		}
257222799Smarcel		limit = pg + IA64_PBVM_PAGE_SIZE;
258222799Smarcel		idx++;
259222799Smarcel	}
260222799Smarcel	if (start != ~0)
261222799Smarcel		printf("%#lx-%#lx\n", start, limit);
262222799Smarcel
263222799Smarcel	return (0);
264222799Smarcel}
265