summaryrefslogtreecommitdiff
path: root/extlinux/main.c
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@linux.intel.com>2010-06-15 16:18:24 -0700
committerH. Peter Anvin <hpa@linux.intel.com>2010-06-15 16:18:24 -0700
commit7ebe9987b9e2416716a56213341221489bf8ebb7 (patch)
treea397933ab593103119fda5d638291363e46a7167 /extlinux/main.c
parent29f9390d8e278d31d39a60c8e37febea2c9e8454 (diff)
downloadsyslinux-7ebe9987b9e2416716a56213341221489bf8ebb7.tar.gz
Switch to 64-bit sector pointers everywhere
Switch to consistent use of 64-bit sector pointers; this should enable booting even for individual *partitions* larger than 2 TB. In order to not slow down the boot too much, switch the initial load from an enumeration to an extent map. This means the table gets larger (since we have to assume the worst case), but it simplifies the Sector 1 code (since we can push all the hard stuff into the installer), and will speed up booting in the general case. Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'extlinux/main.c')
-rw-r--r--extlinux/main.c78
1 files changed, 70 insertions, 8 deletions
diff --git a/extlinux/main.c b/extlinux/main.c
index 0a55692a..a4b81cdd 100644
--- a/extlinux/main.c
+++ b/extlinux/main.c
@@ -195,6 +195,55 @@ int get_geometry(int devfd, uint64_t totalbytes, struct hd_geometry *geo)
}
/*
+ * Generate sector extents
+ */
+static void generate_extents(struct syslinux_extent *ex, int nptrs,
+ sector_t *sectp, int nsect)
+{
+ uint32_t addr = 0x7c00 + 2*SECTOR_SIZE;
+ uint32_t base;
+ sector_t sect, lba;
+ unsigned int len;
+
+ len = lba = base = 0;
+
+ memset(ex, 0, nptrs * sizeof *ex);
+
+ while (nsect) {
+ sect = *sectp++;
+
+ if (len && sect == lba + len &&
+ ((addr ^ (base + len * SECTOR_SIZE)) & 0xffff0000) == 0) {
+ /* We can add to the current extent */
+ len++;
+ goto next;
+ }
+
+ if (len) {
+ set_64(&ex->lba, lba);
+ set_16(&ex->len, len);
+ printf("EXTENT: %11lu / %5u\n", lba, len);
+ ex++;
+ }
+
+ base = addr;
+ lba = sect;
+ len = 1;
+
+ next:
+ addr += SECTOR_SIZE;
+ nsect--;
+ }
+
+ if (len) {
+ set_64(&ex->lba, lba);
+ set_16(&ex->len, len);
+ printf("EXTENT: %11lu / %5u\n", lba, len);
+ ex++;
+ }
+}
+
+/*
* Query the device geometry and put it into the boot sector.
* Map the file and put the map in the boot sector and file.
* Stick the "current directory" inode number into the file.
@@ -205,16 +254,18 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd)
{
struct stat dirst, xdst;
struct hd_geometry geo;
- uint32_t *sectp;
+ sector_t *sectp;
uint64_t totalbytes, totalsectors;
int nsect;
uint32_t *wp;
struct boot_sector *bs;
struct patch_area *patcharea;
+ struct syslinux_extent *ex;
int i, dw, nptrs;
uint32_t csum;
int secptroffset, diroffset, dirlen, subvoloffset, subvollen;
char *dirpath, *subpath, *xdirpath, *xsubpath;
+ uint64_t *advptrs;
dirpath = realpath(dir, NULL);
if (!dirpath || stat(dir, &dirst)) {
@@ -299,7 +350,7 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd)
dprintf("directory inode = %lu\n", (unsigned long)dirst.st_ino);
nsect = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
nsect += 2; /* Two sectors for the ADV */
- sectp = alloca(sizeof(uint32_t) * nsect);
+ sectp = alloca(sizeof(sector_t) * nsect);
if (fs_type == EXT2 || fs_type == VFAT) {
if (sectmap(fd, sectp, nsect)) {
perror("bmap");
@@ -313,7 +364,7 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd)
}
/* First sector need pointer in boot sector */
- set_32(&bs->NextSector, *sectp++);
+ set_64(&bs->NextSector, *sectp++);
/* Search for LDLINUX_MAGIC to find the patch area */
for (wp = (uint32_t *) boot_image; get_32(wp) != LDLINUX_MAGIC; wp++) ;
@@ -329,14 +380,25 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd)
if (opt.stupid_mode)
set_16(&patcharea->maxtransfer, 1);
- /* Set the sector pointers */
+ /* Set the sector extents */
secptroffset = get_16(&patcharea->secptroffset);
- wp = (uint32_t *) ((char *)boot_image + secptroffset);
+ ex = (struct syslinux_extent *) ((char *)boot_image + secptroffset);
nptrs = get_16(&patcharea->secptrcnt);
- memset(wp, 0, nptrs * 4);
- while (--nsect) /* the first sector in bs->NextSector */
- set_32(wp++, *sectp++);
+ if (nsect > nptrs) {
+ /* Not necessarily an error in this case, but a general problem */
+ fprintf(stderr, "Insufficient extent space, build error!\n");
+ exit(1);
+ }
+
+ /* -1 for the pointer in the boot sector, -2 for the two ADVs */
+ generate_extents(ex, nptrs, sectp, nsect-1-2);
+
+ /* ADV pointers */
+ advptrs = (uint64_t *)((char *)boot_image +
+ get_16(&patcharea->advptroffset));
+ set_64(&advptrs[0], sectp[nsect-1-2]);
+ set_64(&advptrs[1], sectp[nsect-1-1]);
/* Poke in the base directory path */
diroffset = get_16(&patcharea->diroffset);