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