1/*-
2 * Copyright (c) 2004 Marcel Moolenaar
3 * Copyright (c) 2001 Doug Rabson
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD$");
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <machine/bootinfo.h>
34#include <machine/efi.h>
35#include <machine/md_var.h>
36#include <machine/sal.h>
37#include <vm/vm.h>
38#include <vm/pmap.h>
39
40static struct efi_systbl *efi_systbl;
41static struct efi_cfgtbl *efi_cfgtbl;
42static struct efi_rt *efi_runtime;
43
44static int efi_status2err[25] = {
45	0,		/* EFI_SUCCESS */
46	ENOEXEC,	/* EFI_LOAD_ERROR */
47	EINVAL,		/* EFI_INVALID_PARAMETER */
48	ENOSYS,		/* EFI_UNSUPPORTED */
49	EMSGSIZE, 	/* EFI_BAD_BUFFER_SIZE */
50	EOVERFLOW,	/* EFI_BUFFER_TOO_SMALL */
51	EBUSY,		/* EFI_NOT_READY */
52	EIO,		/* EFI_DEVICE_ERROR */
53	EROFS,		/* EFI_WRITE_PROTECTED */
54	EAGAIN,		/* EFI_OUT_OF_RESOURCES */
55	EIO,		/* EFI_VOLUME_CORRUPTED */
56	ENOSPC,		/* EFI_VOLUME_FULL */
57	ENXIO,		/* EFI_NO_MEDIA */
58	ESTALE,		/* EFI_MEDIA_CHANGED */
59	ENOENT,		/* EFI_NOT_FOUND */
60	EACCES,		/* EFI_ACCESS_DENIED */
61	ETIMEDOUT,	/* EFI_NO_RESPONSE */
62	EADDRNOTAVAIL,	/* EFI_NO_MAPPING */
63	ETIMEDOUT,	/* EFI_TIMEOUT */
64	EDOOFUS,	/* EFI_NOT_STARTED */
65	EALREADY,	/* EFI_ALREADY_STARTED */
66	ECANCELED,	/* EFI_ABORTED */
67	EPROTO,		/* EFI_ICMP_ERROR */
68	EPROTO,		/* EFI_TFTP_ERROR */
69	EPROTO		/* EFI_PROTOCOL_ERROR */
70};
71
72static int
73efi_status_to_errno(efi_status status)
74{
75	u_long code;
76	int error;
77
78	code = status & 0x3ffffffffffffffful;
79	error = (code < 25) ? efi_status2err[code] : EDOOFUS;
80	return (error);
81}
82
83void
84efi_boot_finish(void)
85{
86}
87
88/*
89 * Collect the entry points for PAL and SAL. Be extra careful about NULL
90 * pointer values. We're running pre-console, so it's better to return
91 * error values than to cause panics, machine checks and other traps and
92 * faults. Keep this minimal...
93 */
94int
95efi_boot_minimal(uint64_t systbl)
96{
97	ia64_efi_f setvirt;
98	struct efi_md *md;
99	efi_status status;
100
101	if (systbl == 0)
102		return (EINVAL);
103	efi_systbl = (struct efi_systbl *)IA64_PHYS_TO_RR7(systbl);
104	if (efi_systbl->st_hdr.th_sig != EFI_SYSTBL_SIG) {
105		efi_systbl = NULL;
106		return (EFAULT);
107	}
108	efi_cfgtbl = (efi_systbl->st_cfgtbl == 0) ? NULL :
109	    (struct efi_cfgtbl *)IA64_PHYS_TO_RR7(efi_systbl->st_cfgtbl);
110	if (efi_cfgtbl == NULL)
111		return (ENOENT);
112	efi_runtime = (efi_systbl->st_rt == 0) ? NULL :
113	    (struct efi_rt *)IA64_PHYS_TO_RR7(efi_systbl->st_rt);
114	if (efi_runtime == NULL)
115		return (ENOENT);
116
117	/*
118	 * Relocate runtime memory segments for firmware.
119	 */
120	md = efi_md_first();
121	while (md != NULL) {
122		if (md->md_attr & EFI_MD_ATTR_RT) {
123			md->md_virt = (md->md_attr & EFI_MD_ATTR_WB) ?
124			    (void *)IA64_PHYS_TO_RR7(md->md_phys) :
125			    (void *)IA64_PHYS_TO_RR6(md->md_phys);
126		}
127		md = efi_md_next(md);
128	}
129	setvirt = (void *)IA64_PHYS_TO_RR7((u_long)efi_runtime->rt_setvirtual);
130	status = ia64_efi_physical(setvirt, bootinfo->bi_memmap_size,
131	    bootinfo->bi_memdesc_size, bootinfo->bi_memdesc_version,
132	    ia64_tpa(bootinfo->bi_memmap));
133	return ((status < 0) ? EFAULT : 0);
134}
135
136void *
137efi_get_table(struct uuid *uuid)
138{
139	struct efi_cfgtbl *ct;
140	u_long count;
141
142	if (efi_cfgtbl == NULL)
143		return (NULL);
144	count = efi_systbl->st_entries;
145	ct = efi_cfgtbl;
146	while (count--) {
147		if (!bcmp(&ct->ct_uuid, uuid, sizeof(*uuid)))
148			return ((void *)IA64_PHYS_TO_RR7(ct->ct_data));
149		ct++;
150	}
151	return (NULL);
152}
153
154void
155efi_get_time(struct efi_tm *tm)
156{
157
158	efi_runtime->rt_gettime(tm, NULL);
159}
160
161struct efi_md *
162efi_md_first(void)
163{
164	struct efi_md *md;
165
166	if (bootinfo->bi_memmap == 0)
167		return (NULL);
168	md = (struct efi_md *)bootinfo->bi_memmap;
169	return (md);
170}
171
172struct efi_md *
173efi_md_last(void)
174{
175	struct efi_md *md;
176
177	if (bootinfo->bi_memmap == 0)
178		return (NULL);
179	md = (struct efi_md *)(bootinfo->bi_memmap + bootinfo->bi_memmap_size -
180	    bootinfo->bi_memdesc_size);
181	return (md);
182}
183
184struct efi_md *
185efi_md_next(struct efi_md *md)
186{
187	struct efi_md *lim;
188
189	lim = efi_md_last();
190	md = (struct efi_md *)((uintptr_t)md + bootinfo->bi_memdesc_size);
191	return ((md > lim) ? NULL : md);
192}
193
194struct efi_md *
195efi_md_prev(struct efi_md *md)
196{
197	struct efi_md *lim;
198
199	lim = efi_md_first();
200	md = (struct efi_md *)((uintptr_t)md - bootinfo->bi_memdesc_size);
201	return ((md < lim) ? NULL : md);
202}
203
204struct efi_md *
205efi_md_find(vm_paddr_t pa)
206{
207	static struct efi_md *last = NULL;
208	struct efi_md *md, *p0, *p1;
209
210	md = (last != NULL) ? last : efi_md_first();
211	p1 = p0 = NULL;
212	while (md != NULL && md != p1) {
213		if (pa >= md->md_phys &&
214		    pa < md->md_phys + md->md_pages * EFI_PAGE_SIZE) {
215			last = md;
216			return (md);
217		}
218
219		p1 = p0;
220		p0 = md;
221		md = (pa < md->md_phys) ? efi_md_prev(md) : efi_md_next(md);
222	}
223
224	return (NULL);
225}
226
227void
228efi_reset_system(void)
229{
230
231	if (efi_runtime != NULL)
232		efi_runtime->rt_reset(EFI_RESET_WARM, 0, 0, NULL);
233	panic("%s: unable to reset the machine", __func__);
234}
235
236int
237efi_set_time(struct efi_tm *tm)
238{
239
240	return (efi_status_to_errno(efi_runtime->rt_settime(tm)));
241}
242
243int
244efi_var_get(efi_char *name, struct uuid *vendor, uint32_t *attrib,
245    size_t *datasize, void *data)
246{
247	efi_status status;
248
249	status = efi_runtime->rt_getvar(name, vendor, attrib, datasize, data);
250	return (efi_status_to_errno(status));
251}
252
253int
254efi_var_nextname(size_t *namesize, efi_char *name, struct uuid *vendor)
255{
256	efi_status status;
257
258	status = efi_runtime->rt_scanvar(namesize, name, vendor);
259	return (efi_status_to_errno(status));
260}
261
262int
263efi_var_set(efi_char *name, struct uuid *vendor, uint32_t attrib,
264    size_t datasize, void *data)
265{
266	efi_status status;
267
268	status = efi_runtime->rt_setvar(name, vendor, attrib, datasize, data);
269	return (efi_status_to_errno(status));
270}
271