boot1.c revision 293724
1/*-
2 * Copyright (c) 1998 Robert Nordier
3 * All rights reserved.
4 * Copyright (c) 2001 Robert Drehmel
5 * All rights reserved.
6 * Copyright (c) 2014 Nathan Whitehorn
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms are freely
10 * permitted provided that the above copyright notice and this
11 * paragraph and the following disclaimer are duplicated in all
12 * such forms.
13 *
14 * This software is provided "AS IS" and without any express or
15 * implied warranties, including, without limitation, the implied
16 * warranties of merchantability and fitness for a particular
17 * purpose.
18 */
19
20#include <sys/cdefs.h>
21__FBSDID("$FreeBSD: head/sys/boot/efi/boot1/boot1.c 293724 2016-01-12 02:17:39Z smh $");
22
23#include <sys/param.h>
24#include <sys/dirent.h>
25#include <machine/elf.h>
26#include <machine/stdarg.h>
27#include <stand.h>
28
29#include <efi.h>
30#include <eficonsctl.h>
31
32#define _PATH_LOADER	"/boot/loader.efi"
33#define _PATH_KERNEL	"/boot/kernel/kernel"
34
35#define BSIZEMAX	16384
36
37void panic(const char *fmt, ...) __dead2;
38void putchar(int c);
39EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab);
40
41static int domount(EFI_DEVICE_PATH *device, EFI_BLOCK_IO *blkio, int quiet);
42static void load(const char *fname);
43
44static EFI_SYSTEM_TABLE *systab;
45static EFI_HANDLE *image;
46
47static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL;
48static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
49static EFI_GUID LoadedImageGUID = LOADED_IMAGE_PROTOCOL;
50static EFI_GUID ConsoleControlGUID = EFI_CONSOLE_CONTROL_PROTOCOL_GUID;
51
52static EFI_BLOCK_IO *bootdev;
53static EFI_DEVICE_PATH *bootdevpath;
54static EFI_HANDLE *bootdevhandle;
55
56EFI_STATUS efi_main(EFI_HANDLE Ximage, EFI_SYSTEM_TABLE* Xsystab)
57{
58	EFI_HANDLE handles[128];
59	EFI_BLOCK_IO *blkio;
60	UINTN i, nparts = sizeof(handles), cols, rows, max_dim, best_mode;
61	EFI_STATUS status;
62	EFI_DEVICE_PATH *devpath;
63	EFI_BOOT_SERVICES *BS;
64	EFI_CONSOLE_CONTROL_PROTOCOL *ConsoleControl = NULL;
65	SIMPLE_TEXT_OUTPUT_INTERFACE *conout = NULL;
66	const char *path = _PATH_LOADER;
67
68	systab = Xsystab;
69	image = Ximage;
70
71	BS = systab->BootServices;
72	status = BS->LocateProtocol(&ConsoleControlGUID, NULL,
73	    (VOID **)&ConsoleControl);
74	if (status == EFI_SUCCESS)
75		(void)ConsoleControl->SetMode(ConsoleControl,
76		    EfiConsoleControlScreenText);
77	/*
78	 * Reset the console and find the best text mode.
79	 */
80	conout = systab->ConOut;
81	conout->Reset(conout, TRUE);
82	max_dim = best_mode = 0;
83	for (i = 0; ; i++) {
84		status = conout->QueryMode(conout, i, &cols, &rows);
85		if (EFI_ERROR(status))
86			break;
87		if (cols * rows > max_dim) {
88			max_dim = cols * rows;
89			best_mode = i;
90		}
91	}
92	if (max_dim > 0)
93		conout->SetMode(conout, best_mode);
94	conout->EnableCursor(conout, TRUE);
95	conout->ClearScreen(conout);
96
97	printf("\n"
98	       ">> FreeBSD EFI boot block\n");
99	printf("   Loader path: %s\n", path);
100
101	status = systab->BootServices->LocateHandle(ByProtocol,
102	    &BlockIoProtocolGUID, NULL, &nparts, handles);
103	nparts /= sizeof(handles[0]);
104
105	for (i = 0; i < nparts; i++) {
106		status = systab->BootServices->HandleProtocol(handles[i],
107		    &DevicePathGUID, (void **)&devpath);
108		if (EFI_ERROR(status))
109			continue;
110
111		while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
112			devpath = NextDevicePathNode(devpath);
113
114		status = systab->BootServices->HandleProtocol(handles[i],
115		    &BlockIoProtocolGUID, (void **)&blkio);
116		if (EFI_ERROR(status))
117			continue;
118
119		if (!blkio->Media->LogicalPartition)
120			continue;
121
122		if (domount(devpath, blkio, 1) >= 0)
123			break;
124	}
125
126	if (i == nparts)
127		panic("No bootable partition found");
128
129	bootdevhandle = handles[i];
130	load(path);
131
132	panic("Load failed");
133
134	return EFI_SUCCESS;
135}
136
137static int
138dskread(void *buf, u_int64_t lba, int nblk)
139{
140	EFI_STATUS status;
141	int size;
142
143	lba = lba / (bootdev->Media->BlockSize / DEV_BSIZE);
144	size = nblk * DEV_BSIZE;
145	status = bootdev->ReadBlocks(bootdev, bootdev->Media->MediaId, lba,
146	    size, buf);
147
148	if (EFI_ERROR(status))
149		return (-1);
150
151	return (0);
152}
153
154#include "ufsread.c"
155
156static ssize_t
157fsstat(ufs_ino_t inode)
158{
159#ifndef UFS2_ONLY
160	static struct ufs1_dinode dp1;
161#endif
162#ifndef UFS1_ONLY
163	static struct ufs2_dinode dp2;
164#endif
165	static struct fs fs;
166	static ufs_ino_t inomap;
167	char *blkbuf;
168	void *indbuf;
169	size_t n, size;
170	static ufs2_daddr_t blkmap, indmap;
171
172	blkbuf = dmadat->blkbuf;
173	indbuf = dmadat->indbuf;
174	if (!dsk_meta) {
175		inomap = 0;
176		for (n = 0; sblock_try[n] != -1; n++) {
177			if (dskread(dmadat->sbbuf, sblock_try[n] / DEV_BSIZE,
178			    SBLOCKSIZE / DEV_BSIZE))
179				return -1;
180			memcpy(&fs, dmadat->sbbuf, sizeof(struct fs));
181			if ((
182#if defined(UFS1_ONLY)
183			    fs.fs_magic == FS_UFS1_MAGIC
184#elif defined(UFS2_ONLY)
185			    (fs.fs_magic == FS_UFS2_MAGIC &&
186			    fs.fs_sblockloc == sblock_try[n])
187#else
188			    fs.fs_magic == FS_UFS1_MAGIC ||
189			    (fs.fs_magic == FS_UFS2_MAGIC &&
190			    fs.fs_sblockloc == sblock_try[n])
191#endif
192			    ) &&
193			    fs.fs_bsize <= MAXBSIZE &&
194			    fs.fs_bsize >= (int32_t)sizeof(struct fs))
195				break;
196		}
197		if (sblock_try[n] == -1) {
198			return -1;
199		}
200		dsk_meta++;
201	} else
202		memcpy(&fs, dmadat->sbbuf, sizeof(struct fs));
203	if (!inode)
204		return 0;
205	if (inomap != inode) {
206		n = IPERVBLK(&fs);
207		if (dskread(blkbuf, INO_TO_VBA(&fs, n, inode), DBPERVBLK))
208			return -1;
209		n = INO_TO_VBO(n, inode);
210#if defined(UFS1_ONLY)
211		memcpy(&dp1, (struct ufs1_dinode *)blkbuf + n,
212		    sizeof(struct ufs1_dinode));
213#elif defined(UFS2_ONLY)
214		memcpy(&dp2, (struct ufs2_dinode *)blkbuf + n,
215		    sizeof(struct ufs2_dinode));
216#else
217		if (fs.fs_magic == FS_UFS1_MAGIC)
218			memcpy(&dp1, (struct ufs1_dinode *)(void *)blkbuf + n,
219			    sizeof(struct ufs1_dinode));
220		else
221			memcpy(&dp2, (struct ufs2_dinode *)(void *)blkbuf + n,
222			    sizeof(struct ufs2_dinode));
223#endif
224		inomap = inode;
225		fs_off = 0;
226		blkmap = indmap = 0;
227	}
228	size = DIP(di_size);
229	n = size - fs_off;
230	return (n);
231}
232
233static struct dmadat __dmadat;
234
235static int
236domount(EFI_DEVICE_PATH *device, EFI_BLOCK_IO *blkio, int quiet)
237{
238
239	dmadat = &__dmadat;
240	bootdev = blkio;
241	bootdevpath = device;
242	if (fsread(0, NULL, 0)) {
243		if (!quiet)
244			printf("domount: can't read superblock\n");
245		return (-1);
246	}
247	if (!quiet)
248		printf("Succesfully mounted UFS filesystem\n");
249	return (0);
250}
251
252static void
253load(const char *fname)
254{
255	ufs_ino_t ino;
256	EFI_STATUS status;
257	EFI_HANDLE loaderhandle;
258	EFI_LOADED_IMAGE *loaded_image;
259	void *buffer;
260	size_t bufsize;
261
262	if ((ino = lookup(fname)) == 0) {
263		printf("File %s not found\n", fname);
264		return;
265	}
266
267	bufsize = fsstat(ino);
268	status = systab->BootServices->AllocatePool(EfiLoaderData,
269	    bufsize, &buffer);
270	fsread(ino, buffer, bufsize);
271
272	/* XXX: For secure boot, we need our own loader here */
273	status = systab->BootServices->LoadImage(TRUE, image, bootdevpath,
274	    buffer, bufsize, &loaderhandle);
275	if (EFI_ERROR(status))
276		printf("LoadImage failed with error %lu\n",
277		    EFI_ERROR_CODE(status));
278
279	status = systab->BootServices->HandleProtocol(loaderhandle,
280	    &LoadedImageGUID, (VOID**)&loaded_image);
281	if (EFI_ERROR(status))
282		printf("HandleProtocol failed with error %lu\n",
283		    EFI_ERROR_CODE(status));
284
285	loaded_image->DeviceHandle = bootdevhandle;
286
287	status = systab->BootServices->StartImage(loaderhandle, NULL, NULL);
288	if (EFI_ERROR(status))
289		printf("StartImage failed with error %lu\n",
290		    EFI_ERROR_CODE(status));
291}
292
293void
294panic(const char *fmt, ...)
295{
296	va_list ap;
297
298	printf("panic: ");
299	va_start(ap, fmt);
300	vprintf(fmt, ap);
301	va_end(ap);
302	printf("\n");
303
304	while (1) {}
305}
306
307void
308putchar(int c)
309{
310	CHAR16 buf[2];
311
312	if (c == '\n') {
313		buf[0] = '\r';
314		buf[1] = 0;
315		systab->ConOut->OutputString(systab->ConOut, buf);
316	}
317	buf[0] = c;
318	buf[1] = 0;
319	systab->ConOut->OutputString(systab->ConOut, buf);
320}
321