boot1.c revision 294981
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 294981 2016-01-28 12:11:42Z 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>> FreeBSD EFI boot block\n");
98	printf("   Loader path: %s\n", path);
99
100	status = systab->BootServices->LocateHandle(ByProtocol,
101	    &BlockIoProtocolGUID, NULL, &nparts, handles);
102	nparts /= sizeof(handles[0]);
103
104	for (i = 0; i < nparts; i++) {
105		status = systab->BootServices->HandleProtocol(handles[i],
106		    &DevicePathGUID, (void **)&devpath);
107		if (EFI_ERROR(status))
108			continue;
109
110		while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
111			devpath = NextDevicePathNode(devpath);
112
113		status = systab->BootServices->HandleProtocol(handles[i],
114		    &BlockIoProtocolGUID, (void **)&blkio);
115		if (EFI_ERROR(status))
116			continue;
117
118		if (!blkio->Media->LogicalPartition)
119			continue;
120
121		if (domount(devpath, blkio, 1) >= 0)
122			break;
123	}
124
125	if (i == nparts)
126		panic("No bootable partition found");
127
128	bootdevhandle = handles[i];
129	load(path);
130
131	panic("Load failed");
132
133	return EFI_SUCCESS;
134}
135
136static int
137dskread(void *buf, u_int64_t lba, int nblk)
138{
139	EFI_STATUS status;
140	int size;
141
142	lba = lba / (bootdev->Media->BlockSize / DEV_BSIZE);
143	size = nblk * DEV_BSIZE;
144	status = bootdev->ReadBlocks(bootdev, bootdev->Media->MediaId, lba,
145	    size, buf);
146
147	if (EFI_ERROR(status))
148		return (-1);
149
150	return (0);
151}
152
153#include "ufsread.c"
154
155static ssize_t
156fsstat(ufs_ino_t inode)
157{
158#ifndef UFS2_ONLY
159	static struct ufs1_dinode dp1;
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, size;
169	static ufs2_daddr_t blkmap, indmap;
170
171	blkbuf = dmadat->blkbuf;
172	indbuf = dmadat->indbuf;
173	if (!dsk_meta) {
174		inomap = 0;
175		for (n = 0; sblock_try[n] != -1; n++) {
176			if (dskread(dmadat->sbbuf, sblock_try[n] / DEV_BSIZE,
177			    SBLOCKSIZE / DEV_BSIZE))
178				return -1;
179			memcpy(&fs, dmadat->sbbuf, sizeof(struct fs));
180			if ((
181#if defined(UFS1_ONLY)
182			    fs.fs_magic == FS_UFS1_MAGIC
183#elif defined(UFS2_ONLY)
184			    (fs.fs_magic == FS_UFS2_MAGIC &&
185			    fs.fs_sblockloc == sblock_try[n])
186#else
187			    fs.fs_magic == FS_UFS1_MAGIC ||
188			    (fs.fs_magic == FS_UFS2_MAGIC &&
189			    fs.fs_sblockloc == sblock_try[n])
190#endif
191			    ) &&
192			    fs.fs_bsize <= MAXBSIZE &&
193			    fs.fs_bsize >= (int32_t)sizeof(struct fs))
194				break;
195		}
196		if (sblock_try[n] == -1) {
197			return -1;
198		}
199		dsk_meta++;
200	} else
201		memcpy(&fs, dmadat->sbbuf, sizeof(struct fs));
202	if (!inode)
203		return 0;
204	if (inomap != inode) {
205		n = IPERVBLK(&fs);
206		if (dskread(blkbuf, INO_TO_VBA(&fs, n, inode), DBPERVBLK))
207			return -1;
208		n = INO_TO_VBO(n, inode);
209#if defined(UFS1_ONLY)
210		memcpy(&dp1, (struct ufs1_dinode *)blkbuf + n,
211		    sizeof(struct ufs1_dinode));
212#elif defined(UFS2_ONLY)
213		memcpy(&dp2, (struct ufs2_dinode *)blkbuf + n,
214		    sizeof(struct ufs2_dinode));
215#else
216		if (fs.fs_magic == FS_UFS1_MAGIC)
217			memcpy(&dp1, (struct ufs1_dinode *)(void *)blkbuf + n,
218			    sizeof(struct ufs1_dinode));
219		else
220			memcpy(&dp2, (struct ufs2_dinode *)(void *)blkbuf + n,
221			    sizeof(struct ufs2_dinode));
222#endif
223		inomap = inode;
224		fs_off = 0;
225		blkmap = indmap = 0;
226	}
227	size = DIP(di_size);
228	n = size - fs_off;
229	return (n);
230}
231
232static struct dmadat __dmadat;
233
234static int
235domount(EFI_DEVICE_PATH *device, EFI_BLOCK_IO *blkio, int quiet)
236{
237
238	dmadat = &__dmadat;
239	bootdev = blkio;
240	bootdevpath = device;
241	if (fsread(0, NULL, 0)) {
242		if (!quiet)
243			printf("domount: can't read superblock\n");
244		return (-1);
245	}
246	if (!quiet)
247		printf("Succesfully mounted UFS filesystem\n");
248	return (0);
249}
250
251static void
252load(const char *fname)
253{
254	ufs_ino_t ino;
255	EFI_STATUS status;
256	EFI_HANDLE loaderhandle;
257	EFI_LOADED_IMAGE *loaded_image;
258	void *buffer;
259	size_t bufsize;
260
261	if ((ino = lookup(fname)) == 0) {
262		printf("File %s not found\n", fname);
263		return;
264	}
265
266	bufsize = fsstat(ino);
267	status = systab->BootServices->AllocatePool(EfiLoaderData,
268	    bufsize, &buffer);
269	fsread(ino, buffer, bufsize);
270
271	/* XXX: For secure boot, we need our own loader here */
272	status = systab->BootServices->LoadImage(TRUE, image, bootdevpath,
273	    buffer, bufsize, &loaderhandle);
274	if (EFI_ERROR(status))
275		printf("LoadImage failed with error %lu\n",
276		    EFI_ERROR_CODE(status));
277
278	status = systab->BootServices->HandleProtocol(loaderhandle,
279	    &LoadedImageGUID, (VOID**)&loaded_image);
280	if (EFI_ERROR(status))
281		printf("HandleProtocol failed with error %lu\n",
282		    EFI_ERROR_CODE(status));
283
284	loaded_image->DeviceHandle = bootdevhandle;
285
286	status = systab->BootServices->StartImage(loaderhandle, NULL, NULL);
287	if (EFI_ERROR(status))
288		printf("StartImage failed with error %lu\n",
289		    EFI_ERROR_CODE(status));
290}
291
292void
293panic(const char *fmt, ...)
294{
295	va_list ap;
296
297	printf("panic: ");
298	va_start(ap, fmt);
299	vprintf(fmt, ap);
300	va_end(ap);
301	printf("\n");
302
303	while (1) {}
304}
305
306void
307putchar(int c)
308{
309	CHAR16 buf[2];
310
311	if (c == '\n') {
312		buf[0] = '\r';
313		buf[1] = 0;
314		systab->ConOut->OutputString(systab->ConOut, buf);
315	}
316	buf[0] = c;
317	buf[1] = 0;
318	systab->ConOut->OutputString(systab->ConOut, buf);
319}
320