1 /* $OpenBSD: efidev.c,v 1.2 2016/05/20 11:53:19 kettenis Exp $ */ 2 3 /* 4 * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> 5 * Copyright (c) 2016 Mark Kettenis 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 */ 30 #include <sys/param.h> 31 #include <sys/reboot.h> 32 #include <sys/disklabel.h> 33 #include <lib/libz/zlib.h> 34 35 #include "libsa.h" 36 37 #include <efi.h> 38 #include "eficall.h" 39 40 extern EFI_BOOT_SERVICES *BS; 41 42 extern int debug; 43 44 #include "disk.h" 45 #include "efidev.h" 46 47 #define EFI_BLKSPERSEC(_ed) ((_ed)->blkio->Media->BlockSize / DEV_BSIZE) 48 #define EFI_SECTOBLK(_ed, _n) ((_n) * EFI_BLKSPERSEC(_ed)) 49 50 extern EFI_BLOCK_IO *disk; 51 struct diskinfo diskinfo; 52 53 static EFI_STATUS 54 efid_io(int, efi_diskinfo_t, u_int, int, void *); 55 static int efid_diskio(int, struct diskinfo *, u_int, int, void *); 56 57 static EFI_STATUS 58 efid_io(int rw, efi_diskinfo_t ed, u_int off, int nsect, void *buf) 59 { 60 EFI_STATUS status = EFI_SUCCESS; 61 EFI_PHYSICAL_ADDRESS addr; 62 caddr_t data; 63 64 if (ed->blkio->Media->BlockSize != DEV_BSIZE) 65 return (EFI_UNSUPPORTED); 66 67 status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData, 68 EFI_SIZE_TO_PAGES(nsect * DEV_BSIZE), &addr); 69 if (EFI_ERROR(status)) 70 goto on_eio; 71 data = (caddr_t)(uintptr_t)addr; 72 73 switch (rw) { 74 case F_READ: 75 status = EFI_CALL(ed->blkio->ReadBlocks, 76 ed->blkio, ed->mediaid, off, 77 nsect * DEV_BSIZE, data); 78 if (EFI_ERROR(status)) 79 goto on_eio; 80 memcpy(buf, data, nsect * DEV_BSIZE); 81 break; 82 case F_WRITE: 83 if (ed->blkio->Media->ReadOnly) 84 goto on_eio; 85 /* XXX not yet */ 86 goto on_eio; 87 break; 88 } 89 return (EFI_SUCCESS); 90 91 on_eio: 92 BS->FreePages(addr, EFI_SIZE_TO_PAGES(nsect * DEV_BSIZE)); 93 94 return (status); 95 } 96 97 static int 98 efid_diskio(int rw, struct diskinfo *dip, u_int off, int nsect, void *buf) 99 { 100 EFI_STATUS status; 101 102 status = efid_io(rw, &dip->ed, off, nsect, buf); 103 104 return ((EFI_ERROR(status))? -1 : 0); 105 } 106 107 /* 108 * Read disk label from the device. 109 */ 110 int 111 efi_getdisklabel(struct diskinfo *dip) 112 { 113 char *msg; 114 int sector; 115 size_t rsize; 116 struct disklabel *lp; 117 unsigned char buf[DEV_BSIZE]; 118 119 /* 120 * Find OpenBSD Partition in DOS partition table. 121 */ 122 sector = 0; 123 if (efistrategy(dip, F_READ, DOSBBSECTOR, DEV_BSIZE, buf, &rsize)) 124 return EOFFSET; 125 126 if (*(u_int16_t *)&buf[DOSMBR_SIGNATURE_OFF] == DOSMBR_SIGNATURE) { 127 int i; 128 struct dos_partition *dp = (struct dos_partition *)buf; 129 130 /* 131 * Lookup OpenBSD slice. If there is none, go ahead 132 * and try to read the disklabel off sector #0. 133 */ 134 135 memcpy(dp, &buf[DOSPARTOFF], NDOSPART * sizeof(*dp)); 136 for (i = 0; i < NDOSPART; i++) { 137 if (dp[i].dp_typ == DOSPTYP_OPENBSD) { 138 sector = letoh32(dp[i].dp_start); 139 break; 140 } 141 } 142 } 143 144 if (efistrategy(dip, F_READ, sector + DOS_LABELSECTOR, DEV_BSIZE, 145 buf, &rsize)) 146 return EOFFSET; 147 148 if ((msg = getdisklabel(buf + LABELOFFSET, &dip->disklabel))) 149 printf("sd%d: getdisklabel: %s\n", 0, msg); 150 151 lp = &dip->disklabel; 152 153 /* check partition */ 154 if ((dip->sc_part >= lp->d_npartitions) || 155 (lp->d_partitions[dip->sc_part].p_fstype == FS_UNUSED)) { 156 DPRINTF(("illegal partition\n")); 157 return (EPART); 158 } 159 160 return (0); 161 } 162 163 int 164 efiopen(struct open_file *f, ...) 165 { 166 struct diskinfo *dip = &diskinfo; 167 va_list ap; 168 u_int unit, part; 169 int error; 170 171 if (disk == NULL) 172 return (ENXIO); 173 174 va_start(ap, f); 175 unit = va_arg(ap, u_int); 176 part = va_arg(ap, u_int); 177 va_end(ap); 178 179 if (unit != 0) 180 return (ENXIO); 181 182 diskinfo.ed.blkio = disk; 183 diskinfo.ed.mediaid = disk->Media->MediaId; 184 diskinfo.sc_part = part; 185 186 error = efi_getdisklabel(&diskinfo); 187 if (error) 188 return (error); 189 190 f->f_devdata = dip; 191 192 return 0; 193 } 194 195 int 196 efistrategy(void *devdata, int rw, daddr32_t blk, size_t size, void *buf, 197 size_t *rsize) 198 { 199 struct diskinfo *dip = (struct diskinfo *)devdata; 200 int error = 0; 201 size_t nsect; 202 203 nsect = (size + DEV_BSIZE - 1) / DEV_BSIZE; 204 blk += dip->disklabel.d_partitions[B_PARTITION(dip->sc_part)].p_offset; 205 206 if (blk < 0) 207 error = EINVAL; 208 else 209 error = efid_diskio(rw, dip, blk, nsect, buf); 210 211 if (rsize != NULL) 212 *rsize = nsect * DEV_BSIZE; 213 214 return (error); 215 } 216 217 int 218 eficlose(struct open_file *f) 219 { 220 f->f_devdata = NULL; 221 222 return 0; 223 } 224 225 int 226 efiioctl(struct open_file *f, u_long cmd, void *data) 227 { 228 return 0; 229 } 230