| /* ----------------------------------------------------------------------- * |
| * |
| * 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: */ |