/* ----------------------------------------------------------------------- *
*
* Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
* Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
* Copyright 2010 Shao Miller
* Copyright 2010-2012 Michal Soltys
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall
* be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* ----------------------------------------------------------------------- */
#include <com32.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <fs.h>
#include <syslinux/disk.h>
#include <syslinux/pmapi.h>
#include "utility.h"
static const char *bpbtypes[] = {
[0] = "unknown",
[1] = "2.0",
[2] = "3.0",
[3] = "3.2",
[4] = "3.4",
[5] = "4.0",
[6] = "8.0 (NT+)",
[7] = "7.0",
[8] = "exFAT",
};
void wait_key(void)
{
int cnt;
char junk;
/* drain */
do {
errno = 0;
cnt = read(0, &junk, 1);
} while (cnt > 0 || (cnt < 0 && errno == EAGAIN));
/* wait */
do {
errno = 0;
cnt = read(0, &junk, 1);
} while (!cnt || (cnt < 0 && errno == EAGAIN));
}
int guid_is0(const struct guid *guid)
{
return
!(guid->data1 ||
guid->data2 ||
guid->data3 ||
guid->data4);
}
/*
* mode explanation:
*
* cnul - "strict" mode, never returning higher value than obtained from cbios
* cadd - if the disk is larger than reported geometry /and/ if the geometry has
* less cylinders than 1024 - it means that the total size is somewhere
* between cs and cs+1; in this particular case, we bump the cs to be able
* to return matching chs triplet
* cmax - assume we can use any cylinder value
*
* by default cadd seems most reasonable, giving consistent results with e.g.
* sfdisk's behavior
*/
void lba2chs(disk_chs *dst, const struct disk_info *di, uint64_t lba, int mode)
{
uint32_t c, h, s, t;
uint32_t cs, hs, ss;
/*
* Not much reason here, but if we have no valid CHS geometry, we assume
* "typical" ones to have something to return.
*/
if (di->cbios) {
cs = di->cyl;
hs = di->head;
ss = di->spt;
if (mode == L2C_CADD) {
if (cs < 1024 && di->lbacnt > cs*hs*ss)
cs++;
} else if (mode == L2C_CMAX)
cs = 1024;
} else {
if (di->disk & 0x80) {
cs = 1024;
hs = 255;
ss = 63;
} else {
cs = 80;
hs = 2;
ss = 18;
}
}
if (lba >= cs*hs*ss) {
s = ss;
h = hs - 1;
c = cs - 1;
} else {
s = (lba % ss) + 1;
t = lba / ss;
h = t % hs;
c = t / hs;
}
(*dst)[0] = h;
(*dst)[1] = s | ((c & 0x300) >> 2);
(*dst)[2] = c;
}
uint32_t get_file_lba(const char *filename)
{
struct com32_filedata fd;
uint32_t lba = 0;
int size = 65536;
char *buf;
buf = lmalloc(size);
if (!buf)
return 0;
/* Put the filename in the bounce buffer */
strlcpy(buf, filename, size);
if (open_file(buf, O_RDONLY, &fd) <= 0) {
goto fail; /* Filename not found */
}
/* Since the first member is the LBA, we simply cast */
lba = *((uint32_t *) MK_PTR(0, fd.handle));
/* Call comapi_close() to free the structure */
close_file(fd.handle);
fail:
lfree(buf);
return lba;
}
/* drive offset detection */
int drvoff_detect(int type)
{
if (bpbV40 <= type && type <= bpbVNT) {
return 0x24;
} else if (type == bpbV70) {
return 0x40;
} else if (type == bpbEXF) {
return 0x6F;
}
return -1;
}
/*
* heuristics could certainly be improved
*/
int bpb_detect(const uint8_t *sec, const char *tag)
{
int a, b, c, jmp = -1, rev = 0;
/* exFAT mess first (media descriptor is 0 here) */
if (!memcmp(sec + 0x03, "EXFAT ", 8)) {
rev = bpbEXF;
goto out;
}
/* media descriptor check */
if ((sec[0x15] & 0xF0) != 0xF0)
goto out;
if (sec[0] == 0xEB) /* jump short */
jmp = 2 + *(int8_t *)(sec + 1);
else if (sec[0] == 0xE9) /* jump near */
jmp = 3 + *(int16_t *)(sec + 1);
if (jmp < 0) /* no boot code at all ? */
goto nocode;
/* sanity */
if (jmp < 0x18 || jmp > 0x1F0)
goto out;
/* detect by jump */
if (jmp >= 0x18 && jmp < 0x1E)
rev = bpbV20;
else if (jmp >= 0x1E && jmp < 0x20)
rev = bpbV30;
else if (jmp >= 0x20 && jmp < 0x24)
rev = bpbV32;
else if (jmp >= 0x24 && jmp < 0x46)
rev = bpbV34;
/* TODO: some better V2 - V3.4 checks ? */
if (rev)
goto out;
/*
* BPB info:
* 2.0 == 0x0B - 0x17
* 3.0 == 2.0 + 0x18 - 0x1D
* 3.2 == 3.0 + 0x1E - 0x1F
* 3.4 ==!2.0 + 0x18 - 0x23
* 4.0 == 3.4 + 0x24 - 0x45
* NT ==~3.4 + 0x24 - 0x53
* 7.0 == 3.4 + 0x24 - 0x59
*/
nocode:
a = memcmp(sec + 0x03, "NTFS", 4);
b = memcmp(sec + 0x36, "FAT", 3);
c = memcmp(sec + 0x52, "FAT", 3); /* ext. DOS 7+ bs */
if ((sec[0x26] & 0xFE) == 0x28 && !b) {
rev = bpbV40;
} else if (sec[0x26] == 0x80 && !a) {
rev = bpbVNT;
} else if ((sec[0x42] & 0xFE) == 0x28 && !c) {
rev = bpbV70;
}
out:
printf("BPB detection (%s): %s\n", tag, bpbtypes[rev]);
return rev;
}
/* vim: set ts=8 sts=4 sw=4 noet: */