xref: /openbsd/sys/arch/armv7/stand/efiboot/efidev.c (revision 264ca280)
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