devicename.c revision 163897
1/*- 2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: head/sys/boot/ia64/common/devicename.c 163897 2006-11-02 01:23:18Z marcel $"); 29 30#include <stand.h> 31#include <string.h> 32#include <sys/disklabel.h> 33#include "bootstrap.h" 34 35#include <efi.h> 36#include <efilib.h> 37#include "efiboot.h" 38 39static int efi_parsedev(struct efi_devdesc **dev, const char *devspec, const char **path); 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 */ 46int 47efi_getdev(void **vdev, const char *devspec, const char **path) 48{ 49 struct efi_devdesc **dev = (struct efi_devdesc **)vdev; 50 int rv; 51 52 /* 53 * If it looks like this is just a path and no 54 * device, go with the current device. 55 */ 56 if ((devspec == NULL) || 57 (devspec[0] == '/') || 58 (strchr(devspec, ':') == NULL)) { 59 60 if (((rv = efi_parsedev(dev, getenv("currdev"), NULL)) == 0) && 61 (path != NULL)) 62 *path = devspec; 63 return(rv); 64 } 65 66 /* 67 * Try to parse the device name off the beginning of the devspec 68 */ 69 return(efi_parsedev(dev, devspec, path)); 70} 71 72/* 73 * Point (dev) at an allocated device specifier matching the string version 74 * at the beginning of (devspec). Return a pointer to the remaining 75 * text in (path). 76 * 77 * In all cases, the beginning of (devspec) is compared to the names 78 * of known devices in the device switch, and then any following text 79 * is parsed according to the rules applied to the device type. 80 * 81 * For disk-type devices, the syntax is: 82 * 83 * disk<unit>[s<slice>][<partition>]: 84 * 85 */ 86static int 87efi_parsedev(struct efi_devdesc **dev, const char *devspec, const char **path) 88{ 89 struct efi_devdesc *idev; 90 struct devsw *dv; 91 int i, unit, slice, partition, err; 92 char *cp; 93 const char *np; 94 95 /* minimum length check */ 96 if (strlen(devspec) < 2) 97 return(EINVAL); 98 99 /* look for a device that matches */ 100 for (i = 0, dv = NULL; devsw[i] != NULL; i++) { 101 if (!strncmp(devspec, devsw[i]->dv_name, strlen(devsw[i]->dv_name))) { 102 dv = devsw[i]; 103 break; 104 } 105 } 106 107 if (dv == NULL) 108 return(ENOENT); 109 idev = malloc(sizeof(struct efi_devdesc)); 110 err = 0; 111 np = (devspec + strlen(dv->dv_name)); 112 113 switch(dv->dv_type) { 114 case DEVT_NONE: /* XXX what to do here? Do we care? */ 115 break; 116 117 case DEVT_DISK: 118 unit = -1; 119 slice = -1; 120 partition = -1; 121 if (*np && (*np != ':')) { 122 unit = strtol(np, &cp, 10); /* next comes the unit number */ 123 if (cp == np) { 124 err = EUNIT; 125 goto fail; 126 } 127 if (*cp == 's') { /* got a slice number */ 128 np = cp + 1; 129 slice = strtol(np, &cp, 10); 130 if (cp == np) { 131 err = ESLICE; 132 goto fail; 133 } 134 } 135 if (*cp && (*cp != ':')) { 136 partition = *cp - 'a'; /* get a partition number */ 137 if ((partition < 0) || (partition >= MAXPARTITIONS)) { 138 err = EPART; 139 goto fail; 140 } 141 cp++; 142 } 143 } 144 if (*cp && (*cp != ':')) { 145 err = EINVAL; 146 goto fail; 147 } 148 149 idev->d_unit = unit; 150 idev->d_kind.efidisk.slice = slice; 151 idev->d_kind.efidisk.partition = partition; 152 153 if (path != NULL) 154 *path = (*cp == 0) ? cp : cp + 1; 155 break; 156 157 case DEVT_NET: 158 unit = 0; 159 160 if (*np && (*np != ':')) { 161 unit = strtol(np, &cp, 0); /* get unit number if present */ 162 if (cp == np) { 163 err = EUNIT; 164 goto fail; 165 } 166 } 167 if (*cp && (*cp != ':')) { 168 err = EINVAL; 169 goto fail; 170 } 171 172 idev->d_unit = unit; 173 if (path != NULL) 174 *path = (*cp == 0) ? cp : cp + 1; 175 break; 176 177 default: 178 err = EINVAL; 179 goto fail; 180 } 181 idev->d_dev = dv; 182 idev->d_type = dv->dv_type; 183 if (dev == NULL) { 184 free(idev); 185 } else { 186 *dev = idev; 187 } 188 return(0); 189 190 fail: 191 free(idev); 192 return(err); 193} 194 195 196char * 197efi_fmtdev(void *vdev) 198{ 199 struct efi_devdesc *dev = (struct efi_devdesc *)vdev; 200 static char buf[128]; /* XXX device length constant? */ 201 char *cp; 202 203 switch(dev->d_type) { 204 case DEVT_NONE: 205 strcpy(buf, "(no device)"); 206 break; 207 208 case DEVT_DISK: 209 cp = buf; 210 cp += sprintf(cp, "%s%d", dev->d_dev->dv_name, dev->d_unit); 211 if (dev->d_kind.efidisk.slice > 0) 212 cp += sprintf(cp, "s%d", dev->d_kind.efidisk.slice); 213 if (dev->d_kind.efidisk.partition >= 0) 214 cp += sprintf(cp, "%c", dev->d_kind.efidisk.partition + 'a'); 215 strcat(cp, ":"); 216 break; 217 218 case DEVT_NET: 219 sprintf(buf, "%s%d:", dev->d_dev->dv_name, dev->d_unit); 220 break; 221 } 222 return(buf); 223} 224 225 226/* 227 * Set currdev to suit the value being supplied in (value) 228 */ 229int 230efi_setcurrdev(struct env_var *ev, int flags, void *value) 231{ 232 struct efi_devdesc *ncurr; 233 int rv; 234 235 if ((rv = efi_parsedev(&ncurr, value, NULL)) != 0) 236 return(rv); 237 free(ncurr); 238 env_setenv(ev->ev_name, flags | EV_NOHOOK, value, NULL, NULL); 239 return(0); 240} 241 242