xref: /netbsd-src/sys/arch/i386/stand/efiboot/devopen.c (revision 3827b167b3365a652d4e133c0fee4b88d5527c03)
1*3827b167Srin /*	$NetBSD: devopen.c,v 1.14 2023/06/20 07:46:03 rin Exp $	 */
2d13f4989Snonaka 
3d13f4989Snonaka /*-
4d13f4989Snonaka  * Copyright (c) 2005 The NetBSD Foundation, Inc.
5d13f4989Snonaka  * All rights reserved.
6d13f4989Snonaka  *
7d13f4989Snonaka  * This code is derived from software contributed to The NetBSD Foundation
8d13f4989Snonaka  * by Bang Jun-Young.
9d13f4989Snonaka  *
10d13f4989Snonaka  * Redistribution and use in source and binary forms, with or without
11d13f4989Snonaka  * modification, are permitted provided that the following conditions
12d13f4989Snonaka  * are met:
13d13f4989Snonaka  * 1. Redistributions of source code must retain the above copyright
14d13f4989Snonaka  *    notice, this list of conditions and the following disclaimer.
15d13f4989Snonaka  * 2. Redistributions in binary form must reproduce the above copyright
16d13f4989Snonaka  *    notice, this list of conditions and the following disclaimer in the
17d13f4989Snonaka  *    documentation and/or other materials provided with the distribution.
18d13f4989Snonaka  *
19d13f4989Snonaka  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20d13f4989Snonaka  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21d13f4989Snonaka  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22d13f4989Snonaka  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23d13f4989Snonaka  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24d13f4989Snonaka  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25d13f4989Snonaka  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26d13f4989Snonaka  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27d13f4989Snonaka  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28d13f4989Snonaka  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29d13f4989Snonaka  * POSSIBILITY OF SUCH DAMAGE.
30d13f4989Snonaka  */
31d13f4989Snonaka 
32d13f4989Snonaka /*
33d13f4989Snonaka  * Copyright (c) 1996, 1997
34d13f4989Snonaka  *	Matthias Drochner.  All rights reserved.
35d13f4989Snonaka  *
36d13f4989Snonaka  * Redistribution and use in source and binary forms, with or without
37d13f4989Snonaka  * modification, are permitted provided that the following conditions
38d13f4989Snonaka  * are met:
39d13f4989Snonaka  * 1. Redistributions of source code must retain the above copyright
40d13f4989Snonaka  *    notice, this list of conditions and the following disclaimer.
41d13f4989Snonaka  * 2. Redistributions in binary form must reproduce the above copyright
42d13f4989Snonaka  *    notice, this list of conditions and the following disclaimer in the
43d13f4989Snonaka  *    documentation and/or other materials provided with the distribution.
44d13f4989Snonaka  *
45d13f4989Snonaka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
46d13f4989Snonaka  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
47d13f4989Snonaka  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
48d13f4989Snonaka  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
49d13f4989Snonaka  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
50d13f4989Snonaka  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
51d13f4989Snonaka  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
52d13f4989Snonaka  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
53d13f4989Snonaka  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
54d13f4989Snonaka  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
55d13f4989Snonaka  */
56d13f4989Snonaka 
57d13f4989Snonaka #include "efiboot.h"
58d13f4989Snonaka 
59b014c9faSnonaka #include <lib/libsa/dev_net.h>
60fcd0bf31Snonaka #include <lib/libsa/net.h>
61b014c9faSnonaka 
62d13f4989Snonaka #include <biosdisk.h>
63d13f4989Snonaka #include "devopen.h"
64d13f4989Snonaka #include <bootinfo.h>
6595e6c117Snonaka #include "efidisk.h"
66d13f4989Snonaka 
67d13f4989Snonaka static int
dev2bios(char * devname,int unit,int * biosdev)68d13f4989Snonaka dev2bios(char *devname, int unit, int *biosdev)
69d13f4989Snonaka {
70d13f4989Snonaka 
71d13f4989Snonaka 	if (strcmp(devname, "hd") == 0)
72d13f4989Snonaka 		*biosdev = 0x80 + unit;
737a39eefeSnonaka 	else if (strcmp(devname, "cd") == 0)
74cefc4c6cSnonaka 		*biosdev = 0x80 + get_harddrives() + unit;
75d13f4989Snonaka 	else
76d13f4989Snonaka 		return ENXIO;
77cefc4c6cSnonaka 
78d13f4989Snonaka 	return 0;
79d13f4989Snonaka }
80d13f4989Snonaka 
81d13f4989Snonaka void
bios2dev(int biosdev,daddr_t sector,char ** devname,int * unit,int * partition,const char ** part_name)8292f692b3Smanu bios2dev(int biosdev, daddr_t sector, char **devname, int *unit,
8392f692b3Smanu 	 int *partition, const char **part_name)
84d13f4989Snonaka {
8592f692b3Smanu 	static char savedevname[MAXDEVNAME+1];
86d13f4989Snonaka 
87d13f4989Snonaka 	*unit = biosdev & 0x7f;
88cefc4c6cSnonaka 
89b014c9faSnonaka 	if (efi_bootdp_type == BOOT_DEVICE_TYPE_NET) {
90b014c9faSnonaka 		*devname = "net";
91b014c9faSnonaka 		*unit = efi_net_get_booted_interface_unit();
92b014c9faSnonaka 		if (*unit < 0)
93b014c9faSnonaka 			*unit = 0;
94b014c9faSnonaka 		*partition = 0;
95b014c9faSnonaka 		return;
96b014c9faSnonaka 	} else if (biosdev >= 0x80 + get_harddrives()) {
97cefc4c6cSnonaka 		*devname = "cd";
98cefc4c6cSnonaka 		*unit -= get_harddrives();
99cefc4c6cSnonaka 	} else
100d13f4989Snonaka 		*devname = "hd";
101cefc4c6cSnonaka 
10292f692b3Smanu 	(void)biosdisk_findpartition(biosdev, sector, partition, part_name);
103214d5a7aSnonaka 	if (part_name != NULL && *part_name != NULL) {
1042e9b7540Smanu 		snprintf(savedevname, sizeof(savedevname),
1052e9b7540Smanu 		    "NAME=%s", *part_name);
10692f692b3Smanu 		*devname = savedevname;
10792f692b3Smanu 	}
108d13f4989Snonaka }
109d13f4989Snonaka 
110fcd0bf31Snonaka #if defined(SUPPORT_NFS) || defined(SUPPORT_TFTP)
111fcd0bf31Snonaka const struct netboot_fstab *
netboot_fstab_find(const char * name)112fcd0bf31Snonaka netboot_fstab_find(const char *name)
113fcd0bf31Snonaka {
114fcd0bf31Snonaka 	int i;
115fcd0bf31Snonaka 
116fcd0bf31Snonaka 	if (strcmp(name, "net") == 0)
117fcd0bf31Snonaka 		return &netboot_fstab[0];
118fcd0bf31Snonaka 
119fcd0bf31Snonaka 	for (i = 0; i < nnetboot_fstab; i++) {
120fcd0bf31Snonaka 		if (strcmp(name, netboot_fstab[i].name) == 0)
121fcd0bf31Snonaka 			return &netboot_fstab[i];
122fcd0bf31Snonaka 	}
123fcd0bf31Snonaka 
124fcd0bf31Snonaka 	return NULL;
125fcd0bf31Snonaka }
126fcd0bf31Snonaka 
127fcd0bf31Snonaka static const struct netboot_fstab *
netboot_fstab_findn(const char * name,size_t len)128fcd0bf31Snonaka netboot_fstab_findn(const char *name, size_t len)
129fcd0bf31Snonaka {
130fcd0bf31Snonaka 	int i;
131fcd0bf31Snonaka 
132fcd0bf31Snonaka 	if (strncmp(name, "net", len) == 0)
133fcd0bf31Snonaka 		return &netboot_fstab[0];
134fcd0bf31Snonaka 
135fcd0bf31Snonaka 	for (i = 0; i < nnetboot_fstab; i++) {
136fcd0bf31Snonaka 		if (strncmp(name, netboot_fstab[i].name, len) == 0)
137fcd0bf31Snonaka 			return &netboot_fstab[i];
138fcd0bf31Snonaka 	}
139fcd0bf31Snonaka 
140fcd0bf31Snonaka 	return NULL;
141fcd0bf31Snonaka }
142fcd0bf31Snonaka #endif
143fcd0bf31Snonaka 
144d13f4989Snonaka struct btinfo_bootpath bibp;
145d13f4989Snonaka extern bool kernel_loaded;
146d13f4989Snonaka 
147d13f4989Snonaka /*
148d13f4989Snonaka  * Open the EFI disk device
149d13f4989Snonaka  */
150d13f4989Snonaka int
devopen(struct open_file * f,const char * fname,char ** file)151d13f4989Snonaka devopen(struct open_file *f, const char *fname, char **file)
152d13f4989Snonaka {
153d13f4989Snonaka 	char *fsname, *devname;
1549d087602Smanu 	const char *xname = NULL;
155d13f4989Snonaka 	int unit, partition;
156d13f4989Snonaka 	int biosdev;
157*3827b167Srin 	int error;
158fcd0bf31Snonaka #if defined(SUPPORT_NFS) || defined(SUPPORT_TFTP)
159fcd0bf31Snonaka 	struct devdesc desc;
160fcd0bf31Snonaka 	const struct netboot_fstab *nf;
161fcd0bf31Snonaka 	char *filename;
162fcd0bf31Snonaka 	size_t fsnamelen;
163*3827b167Srin 	int i, n;
164fcd0bf31Snonaka #endif
165d13f4989Snonaka 
16695e6c117Snonaka 	error = parsebootfile(fname, &fsname, &devname, &unit, &partition,
16795e6c117Snonaka 	    (const char **) file);
16895e6c117Snonaka 	if (error)
16995e6c117Snonaka 		return error;
17095e6c117Snonaka 
171fcd0bf31Snonaka 	memcpy(file_system, file_system_disk,
172fcd0bf31Snonaka 	    sizeof(struct fs_ops) * nfsys_disk);
173fcd0bf31Snonaka 	nfsys = nfsys_disk;
174fcd0bf31Snonaka 
17592f692b3Smanu 	/* Search by GPT label or raidframe name */
1769d087602Smanu 	if (strstr(devname, "NAME=") == devname)
1779d087602Smanu 		xname = devname;
1789d087602Smanu 	if (strstr(devname, "raid") == devname)
1799d087602Smanu 		xname = fname;
1809d087602Smanu 
1819d087602Smanu 	if (xname != NULL) {
18292f692b3Smanu 		f->f_dev = &devsw[0];		/* must be biosdisk */
18392f692b3Smanu 
18492f692b3Smanu 		if (!kernel_loaded) {
18592f692b3Smanu 			strncpy(bibp.bootpath, *file, sizeof(bibp.bootpath));
18692f692b3Smanu 			BI_ADD(&bibp, BTINFO_BOOTPATH, sizeof(bibp));
18792f692b3Smanu 		}
18892f692b3Smanu 
1899d087602Smanu 		error = biosdisk_open_name(f, xname);
19092f692b3Smanu 		return error;
19192f692b3Smanu 	}
19292f692b3Smanu 
193fcd0bf31Snonaka 	/*
194fcd0bf31Snonaka 	 * Network
195fcd0bf31Snonaka 	 */
196b014c9faSnonaka #if defined(SUPPORT_NFS) || defined(SUPPORT_TFTP)
197fcd0bf31Snonaka 	nf = netboot_fstab_find(devname);
198fcd0bf31Snonaka 	if (nf != NULL) {
199b014c9faSnonaka 		n = 0;
200fcd0bf31Snonaka 		if (strcmp(devname, "net") == 0) {
201fcd0bf31Snonaka 			for (i = 0; i < nnetboot_fstab; i++) {
202fcd0bf31Snonaka 				memcpy(&file_system[n++], netboot_fstab[i].ops,
203fcd0bf31Snonaka 				    sizeof(struct fs_ops));
204fcd0bf31Snonaka 			}
205fcd0bf31Snonaka 		} else {
206fcd0bf31Snonaka 			memcpy(&file_system[n++], nf->ops,
207fcd0bf31Snonaka 			    sizeof(struct fs_ops));
208b014c9faSnonaka 		}
209b014c9faSnonaka 		nfsys = n;
210b014c9faSnonaka 
211fcd0bf31Snonaka #ifdef SUPPORT_BOOTP
212b014c9faSnonaka 		try_bootp = 1;
213fcd0bf31Snonaka #endif
214fcd0bf31Snonaka 
215fcd0bf31Snonaka 		/* If we got passed a filename, pass it to the BOOTP server. */
216fcd0bf31Snonaka 		if (fname) {
217fcd0bf31Snonaka 			filename = strchr(fname, ':');
218fcd0bf31Snonaka 			if (filename != NULL)
219fcd0bf31Snonaka 				filename++;
220fcd0bf31Snonaka 			else
221fcd0bf31Snonaka 				filename = (char *)fname;
222fcd0bf31Snonaka 			strlcpy(bootfile, filename, sizeof(bootfile));
223b014c9faSnonaka 		}
224b014c9faSnonaka 
225b014c9faSnonaka 		memset(&desc, 0, sizeof(desc));
226fcd0bf31Snonaka 		strlcpy(desc.d_name, "net", sizeof(desc.d_name));
227b014c9faSnonaka 		desc.d_unit = unit;
228b014c9faSnonaka 
229fcd0bf31Snonaka 		f->f_dev = &devsw[1];		/* must be net */
230b014c9faSnonaka 		if (!kernel_loaded) {
231fcd0bf31Snonaka 			strncpy(bibp.bootpath, *file, sizeof(bibp.bootpath));
232b014c9faSnonaka 			BI_ADD(&bibp, BTINFO_BOOTPATH, sizeof(bibp));
233b014c9faSnonaka 		}
234fcd0bf31Snonaka 		error = DEV_OPEN(f->f_dev)(f, &desc);
235fcd0bf31Snonaka 		if (error)
236fcd0bf31Snonaka 			return error;
237fcd0bf31Snonaka 
238fcd0bf31Snonaka 		/*
239fcd0bf31Snonaka 		 * If the DHCP server provided a file name:
240fcd0bf31Snonaka 		 * - If it contains a ":", assume it points to a NetBSD kernel.
241fcd0bf31Snonaka 		 * - If not, assume that the DHCP server was not able to pass
242fcd0bf31Snonaka 		 *   a separate filename for the kernel. (The name probably was
243fcd0bf31Snonaka 		 *   the same as used to load "efiboot".) Ignore it and use
244fcd0bf31Snonaka 		 *   the default in this case.
245fcd0bf31Snonaka 		 * So we cater to simple DHCP servers while being able to use
246fcd0bf31Snonaka 		 * the power of conditional behaviour in modern ones.
247fcd0bf31Snonaka 		 */
248fcd0bf31Snonaka 		filename = strchr(bootfile, ':');
249fcd0bf31Snonaka 		if (filename != NULL) {
250fcd0bf31Snonaka 			fname = bootfile;
251fcd0bf31Snonaka 
252fcd0bf31Snonaka 			fsnamelen = filename - fname;
253fcd0bf31Snonaka 			nf = netboot_fstab_findn(fname, fsnamelen);
254fcd0bf31Snonaka 			if (nf == NULL ||
255fcd0bf31Snonaka 			    strncmp(fname, "net", fsnamelen) == 0) {
256fcd0bf31Snonaka 				printf("Invalid file system type specified in "
257fcd0bf31Snonaka 				    "%s\n", fname);
258fcd0bf31Snonaka 				error = EINVAL;
259fcd0bf31Snonaka 				goto neterr;
260b014c9faSnonaka 			}
261fcd0bf31Snonaka 
262fcd0bf31Snonaka 			memcpy(file_system, nf->ops, sizeof(struct fs_ops));
263fcd0bf31Snonaka 			nfsys = 1;
264b014c9faSnonaka 		}
265b014c9faSnonaka 
266fcd0bf31Snonaka 		filename = fname ? strchr(fname, ':') : NULL;
267fcd0bf31Snonaka 		if (filename != NULL) {
268fcd0bf31Snonaka 			filename++;
269fcd0bf31Snonaka 			if (*filename == '\0') {
270fcd0bf31Snonaka 				printf("No file specified in %s\n", fname);
271fcd0bf31Snonaka 				error = EINVAL;
272fcd0bf31Snonaka 				goto neterr;
273fcd0bf31Snonaka 			}
274fcd0bf31Snonaka 		} else
275fcd0bf31Snonaka 			filename = (char *)fname;
276fcd0bf31Snonaka 
277fcd0bf31Snonaka 		*file = filename;
278fcd0bf31Snonaka 		return 0;
279fcd0bf31Snonaka 
280fcd0bf31Snonaka neterr:
281fcd0bf31Snonaka 		DEV_CLOSE(f->f_dev)(f);
282fcd0bf31Snonaka 		f->f_dev = NULL;
283fcd0bf31Snonaka 		return error;
284fcd0bf31Snonaka 	}
285fcd0bf31Snonaka #endif
286fcd0bf31Snonaka 
287b014c9faSnonaka 	/*
288b014c9faSnonaka 	 * biosdisk
289b014c9faSnonaka 	 */
29095e6c117Snonaka 	if (strcmp(devname, "esp") == 0) {
29195e6c117Snonaka 		bios2dev(boot_biosdev, boot_biossector, &devname, &unit,
2920637ed76Ssimonb 		    &partition, NULL);
29395e6c117Snonaka 		if (efidisk_get_efi_system_partition(boot_biosdev, &partition))
29495e6c117Snonaka 			return ENXIO;
29595e6c117Snonaka 	}
29695e6c117Snonaka 
29795e6c117Snonaka 	error = dev2bios(devname, unit, &biosdev);
29895e6c117Snonaka 	if (error)
299d13f4989Snonaka 		return error;
300d13f4989Snonaka 
301d13f4989Snonaka 	f->f_dev = &devsw[0];		/* must be biosdisk */
302d13f4989Snonaka 
303d13f4989Snonaka 	if (!kernel_loaded) {
304d13f4989Snonaka 		strncpy(bibp.bootpath, *file, sizeof(bibp.bootpath));
305d13f4989Snonaka 		BI_ADD(&bibp, BTINFO_BOOTPATH, sizeof(bibp));
306d13f4989Snonaka 	}
307d13f4989Snonaka 
308d13f4989Snonaka 	return biosdisk_open(f, biosdev, partition);
309d13f4989Snonaka }
310