1 /*-
2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3 * Copyright (c) 2006 Marcel Moolenaar
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 <stand.h>
32 #include <string.h>
33 #include <sys/disklabel.h>
34 #include "bootstrap.h"
35
36 #include <efi.h>
37 #include <efilib.h>
38
39 static int efi_parsedev(struct devdesc **, const char *, const char **);
40
41 /*
42 * Point (dev) at an allocated device specifier for the device matching the
43 * path in (devspec). If it contains an explicit device specification,
44 * use that. If not, use the default device.
45 */
46 int
efi_getdev(void ** vdev,const char * devspec,const char ** path)47 efi_getdev(void **vdev, const char *devspec, const char **path)
48 {
49 struct devdesc **dev = (struct devdesc **)vdev;
50 int rv;
51
52 /*
53 * If it looks like this is just a path and no device, then
54 * use the current device instead.
55 */
56 if (devspec == NULL || *devspec == '/' || !strchr(devspec, ':')) {
57 rv = efi_parsedev(dev, getenv("currdev"), NULL);
58 if (rv == 0 && path != NULL)
59 *path = devspec;
60 return (rv);
61 }
62
63 /* Parse the device name off the beginning of the devspec. */
64 return (efi_parsedev(dev, devspec, path));
65 }
66
67 /*
68 * Point (dev) at an allocated device specifier matching the string version
69 * at the beginning of (devspec). Return a pointer to the remaining
70 * text in (path).
71 *
72 * In all cases, the beginning of (devspec) is compared to the names
73 * of known devices in the device switch, and then any following text
74 * is parsed according to the rules applied to the device type.
75 *
76 * For disk-type devices, the syntax is:
77 *
78 * fs<unit>:
79 */
80 static int
efi_parsedev(struct devdesc ** dev,const char * devspec,const char ** path)81 efi_parsedev(struct devdesc **dev, const char *devspec, const char **path)
82 {
83 struct devdesc *idev;
84 struct devsw *dv;
85 char *cp;
86 const char *np;
87 int i, err;
88
89 /* minimum length check */
90 if (strlen(devspec) < 2)
91 return (EINVAL);
92
93 /* look for a device that matches */
94 for (i = 0; devsw[i] != NULL; i++) {
95 dv = devsw[i];
96 if (!strncmp(devspec, dv->dv_name, strlen(dv->dv_name)))
97 break;
98 }
99 if (devsw[i] == NULL)
100 return (ENOENT);
101
102 idev = malloc(sizeof(struct devdesc));
103 if (idev == NULL)
104 return (ENOMEM);
105
106 idev->d_dev = dv;
107 idev->d_type = dv->dv_type;
108 idev->d_unit = -1;
109
110 err = 0;
111 np = devspec + strlen(dv->dv_name);
112 if (*np != '\0' && *np != ':') {
113 idev->d_unit = strtol(np, &cp, 0);
114 if (cp == np) {
115 idev->d_unit = -1;
116 free(idev);
117 return (EUNIT);
118 }
119 }
120 if (*cp != '\0' && *cp != ':') {
121 free(idev);
122 return (EINVAL);
123 }
124
125 if (path != NULL)
126 *path = (*cp == 0) ? cp : cp + 1;
127 if (dev != NULL)
128 *dev = idev;
129 else
130 free(idev);
131 return (0);
132 }
133
134 char *
efi_fmtdev(void * vdev)135 efi_fmtdev(void *vdev)
136 {
137 struct devdesc *dev = (struct devdesc *)vdev;
138 static char buf[32]; /* XXX device length constant? */
139
140 switch(dev->d_type) {
141 case DEVT_NONE:
142 strcpy(buf, "(no device)");
143 break;
144
145 default:
146 sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit);
147 break;
148 }
149
150 return (buf);
151 }
152
153 /*
154 * Set currdev to suit the value being supplied in (value)
155 */
156 int
efi_setcurrdev(struct env_var * ev,int flags,const void * value)157 efi_setcurrdev(struct env_var *ev, int flags, const void *value)
158 {
159 struct devdesc *ncurr;
160 int rv;
161
162 rv = efi_parsedev(&ncurr, value, NULL);
163 if (rv != 0)
164 return (rv);
165
166 free(ncurr);
167 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL);
168 return (0);
169 }
170