1/* ----------------------------------------------------------------------- *
2 *
3 *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
4 *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
5 *   Copyright 2010 Shao Miller
6 *   Copyright 2010-2012 Michal Soltys
7 *
8 *   Permission is hereby granted, free of charge, to any person
9 *   obtaining a copy of this software and associated documentation
10 *   files (the "Software"), to deal in the Software without
11 *   restriction, including without limitation the rights to use,
12 *   copy, modify, merge, publish, distribute, sublicense, and/or
13 *   sell copies of the Software, and to permit persons to whom
14 *   the Software is furnished to do so, subject to the following
15 *   conditions:
16 *
17 *   The above copyright notice and this permission notice shall
18 *   be included in all copies or substantial portions of the Software.
19 *
20 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 *   OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * ----------------------------------------------------------------------- */
30
31#include <com32.h>
32#include <fcntl.h>
33#include <stdint.h>
34#include <stdio.h>
35#include <errno.h>
36#include <unistd.h>
37#include <string.h>
38#include <fs.h>
39#include <syslinux/disk.h>
40#include <syslinux/pmapi.h>
41#include "utility.h"
42
43static const char *bpbtypes[] = {
44    [0] =  "unknown",
45    [1] =  "2.0",
46    [2] =  "3.0",
47    [3] =  "3.2",
48    [4] =  "3.4",
49    [5] =  "4.0",
50    [6] =  "8.0 (NT+)",
51    [7] =  "7.0",
52    [8] =  "exFAT",
53};
54
55void wait_key(void)
56{
57    int cnt;
58    char junk;
59
60    /* drain */
61    do {
62	errno = 0;
63	cnt = read(0, &junk, 1);
64    } while (cnt > 0 || (cnt < 0 && errno == EAGAIN));
65
66    /* wait */
67    do {
68	errno = 0;
69	cnt = read(0, &junk, 1);
70    } while (!cnt || (cnt < 0 && errno == EAGAIN));
71}
72
73int guid_is0(const struct guid *guid)
74{
75    return
76	!(guid->data1 ||
77	  guid->data2 ||
78	  guid->data3 ||
79	  guid->data4);
80}
81
82/*
83 * mode explanation:
84 *
85 * cnul - "strict" mode, never returning higher value than obtained from cbios
86 * cadd - if the disk is larger than reported geometry /and/ if the geometry has
87 *        less cylinders than 1024 - it means that the total size is somewhere
88 *        between cs and cs+1; in this particular case, we bump the cs to be able
89 *        to return matching chs triplet
90 * cmax - assume we can use any cylinder value
91 *
92 * by default cadd seems most reasonable, giving consistent results with e.g.
93 * sfdisk's behavior
94 */
95void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, int mode)
96{
97    uint32_t c, h, s, t;
98    uint32_t cs, hs, ss;
99
100    /*
101     * Not much reason here, but if we have no valid CHS geometry, we assume
102     * "typical" ones to have something to return.
103     */
104    if (di->cbios) {
105	cs = di->cyl;
106	hs = di->head;
107	ss = di->spt;
108	if (mode == L2C_CADD) {
109	    if (cs < 1024 && di->lbacnt > cs*hs*ss)
110		cs++;
111	} else if (mode == L2C_CMAX)
112	    cs = 1024;
113    } else {
114	if (di->disk & 0x80) {
115	    cs = 1024;
116	    hs = 255;
117	    ss = 63;
118	} else {
119	    cs = 80;
120	    hs = 2;
121	    ss = 18;
122	}
123    }
124
125    if (lba >= cs*hs*ss) {
126	s = ss;
127	h = hs - 1;
128	c = cs - 1;
129    } else {
130	s = (lba % ss) + 1;
131	t = lba / ss;
132	h = t % hs;
133	c = t / hs;
134    }
135
136    (*dst)[0] = h;
137    (*dst)[1] = s | ((c & 0x300) >> 2);
138    (*dst)[2] = c;
139}
140
141uint32_t get_file_lba(const char *filename)
142{
143    struct com32_filedata fd;
144    uint32_t lba = 0;
145    int size = 65536;
146    char *buf;
147
148    buf = lmalloc(size);
149    if (!buf)
150	return 0;
151
152    /* Put the filename in the bounce buffer */
153    strlcpy(buf, filename, size);
154
155    if (open_file(buf, O_RDONLY, &fd) <= 0) {
156	goto fail;		/* Filename not found */
157    }
158
159    /* Since the first member is the LBA, we simply cast */
160    lba = *((uint32_t *) MK_PTR(0, fd.handle));
161
162    /* Call comapi_close() to free the structure */
163    close_file(fd.handle);
164
165fail:
166    lfree(buf);
167    return lba;
168}
169
170/* drive offset detection */
171int drvoff_detect(int type)
172{
173    if (bpbV40 <= type && type <= bpbVNT) {
174	return 0x24;
175    } else if (type == bpbV70) {
176	return 0x40;
177    } else if (type == bpbEXF) {
178	return 0x6F;
179    }
180
181    return -1;
182}
183
184/*
185 * heuristics could certainly be improved
186 */
187int bpb_detect(const uint8_t *sec, const char *tag)
188{
189    int a, b, c, jmp = -1, rev = 0;
190
191    /* exFAT mess first (media descriptor is 0 here) */
192    if (!memcmp(sec + 0x03, "EXFAT   ", 8)) {
193	rev = bpbEXF;
194	goto out;
195    }
196
197    /* media descriptor check */
198    if ((sec[0x15] & 0xF0) != 0xF0)
199	goto out;
200
201    if (sec[0] == 0xEB)	/* jump short */
202	jmp = 2 + *(int8_t *)(sec + 1);
203    else if (sec[0] == 0xE9) /* jump near */
204	jmp = 3 + *(int16_t *)(sec + 1);
205
206    if (jmp < 0)    /* no boot code at all ? */
207	goto nocode;
208
209    /* sanity */
210    if (jmp < 0x18 || jmp > 0x1F0)
211	goto out;
212
213    /* detect by jump */
214    if (jmp >= 0x18 && jmp < 0x1E)
215	rev = bpbV20;
216    else if (jmp >= 0x1E && jmp < 0x20)
217	rev = bpbV30;
218    else if (jmp >= 0x20 && jmp < 0x24)
219	rev = bpbV32;
220    else if (jmp >= 0x24 && jmp < 0x46)
221	rev = bpbV34;
222
223    /* TODO: some better V2 - V3.4 checks ? */
224
225    if (rev)
226	goto out;
227    /*
228     * BPB info:
229     * 2.0 ==       0x0B - 0x17
230     * 3.0 == 2.0 + 0x18 - 0x1D
231     * 3.2 == 3.0 + 0x1E - 0x1F
232     * 3.4 ==!2.0 + 0x18 - 0x23
233     * 4.0 == 3.4 + 0x24 - 0x45
234     *  NT ==~3.4 + 0x24 - 0x53
235     * 7.0 == 3.4 + 0x24 - 0x59
236     */
237
238nocode:
239    a = memcmp(sec + 0x03, "NTFS", 4);
240    b = memcmp(sec + 0x36, "FAT", 3);
241    c = memcmp(sec + 0x52, "FAT", 3);	/* ext. DOS 7+ bs */
242
243    if ((sec[0x26] & 0xFE) == 0x28 && !b) {
244	rev = bpbV40;
245    } else if (sec[0x26] == 0x80 && !a) {
246	rev = bpbVNT;
247    } else if ((sec[0x42] & 0xFE) == 0x28 && !c) {
248	rev = bpbV70;
249    }
250
251out:
252    printf("BPB detection (%s): %s\n", tag, bpbtypes[rev]);
253    return rev;
254}
255
256/* vim: set ts=8 sts=4 sw=4 noet: */
257