aboutsummaryrefslogtreecommitdiffstats
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
parent29f9390d8e278d31d39a60c8e37febea2c9e8454 (diff)
downloadsyslinux-7ebe9987b9e2416716a56213341221489bf8ebb7.tar.gz
syslinux-7ebe9987b9e2416716a56213341221489bf8ebb7.tar.xz
syslinux-7ebe9987b9e2416716a56213341221489bf8ebb7.zip
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>
-rw-r--r--core/adv.inc38
-rw-r--r--core/diskstart.inc62
-rw-r--r--extlinux/Makefile2
-rw-r--r--extlinux/main.c78
-rw-r--r--libfat/libfat.h2
-rw-r--r--libinstaller/syslinux.h3
-rw-r--r--libinstaller/syslxcom.c109
-rw-r--r--libinstaller/syslxcom.h4
-rw-r--r--libinstaller/syslxint.h69
-rw-r--r--libinstaller/syslxmod.c144
-rw-r--r--linux/syslinux.c5
11 files changed, 385 insertions, 131 deletions
diff --git a/core/adv.inc b/core/adv.inc
index 67252619..76da5047 100644
--- a/core/adv.inc
+++ b/core/adv.inc
@@ -71,18 +71,12 @@ adv_init:
cmp word [ADVSectors],2 ; Not present?
jb adv_verify
- ;
- ; Update pointers to default ADVs...
- ;
- mov bx,[DataSectors]
- shl bx,2
- mov ecx,[bsHidden]
- mov eax,[bx+SectorPtrs-4]; First ADV sector
- mov edx,[bx+SectorPtrs] ; Second ADV sector
- add eax,ecx
- add edx,ecx
- mov [ADVSec0],eax
- mov [ADVSec1],edx
+ mov eax,[Hidden]
+ mov edx,[Hidden+4]
+ add [ADVSec0],eax
+ adc [ADVSec0+4],edx
+ add [ADVSec1],eax
+ adc [ADVSec1+4],edx
mov al,[DriveNumber]
mov [ADVDrive],al
jmp adv_read
@@ -300,23 +294,26 @@ adv_cleanup:
; Returns CF=1 if the ADV cannot be written.
;
adv_write:
- cmp dword [ADVSec0],0
+ push eax
+ mov eax,[ADVSec0]
+ or eax,[ADVSec0+4]
je .bad
- cmp dword [ADVSec1],0
+ mov eax,[ADVSec1]
+ or eax,[ADVSec1+4]
je .bad
cmp byte [ADVDrive],-1
je .bad
- push ax
call adv_cleanup
mov ah,3 ; Write
call adv_read_write
- pop ax
clc
+ pop eax
ret
.bad: ; No location for ADV set
stc
+ pop eax
ret
;
@@ -358,10 +355,12 @@ adv_read_write:
.noedd:
mov eax,[ADVSec0]
+ mov edx,[ADVSec0+4]
mov bx,adv0
call .doone
mov eax,[ADVSec1]
+ mov edx,[ADVSec1+4]
mov bx,adv1
call .doone
@@ -369,7 +368,6 @@ adv_read_write:
ret
.doone:
- xor edx,edx ; Zero-extend LBA
push si
jmp si
@@ -495,9 +493,9 @@ adv_read_write:
jmp .cb_done
section .data16
- alignz 4
-ADVSec0 dd 0 ; Not specified
-ADVSec1 dd 0 ; Not specified
+ alignz 8
+ADVSec0 dq 0 ; Not specified
+ADVSec1 dq 0 ; Not specified
ADVDrive db -1 ; No ADV defined
ADVCHSInfo db -1 ; We have CHS info for this drive
diff --git a/core/diskstart.inc b/core/diskstart.inc
index e9254655..d858c35d 100644
--- a/core/diskstart.inc
+++ b/core/diskstart.inc
@@ -236,6 +236,7 @@ eddcheck:
; together with EBIOS support, unfortunately.
;
mov eax,[FirstSector] ; Sector start
+ mov edx,[FirstSector+4]
mov bx,ldlinux_sys ; Where to load it
call getonesec
@@ -258,7 +259,6 @@ eddcheck:
; the order to dst,src to keep things sane.
;
getonesec:
- xor edx,edx ; Assume 32-bit partition size
add eax,[Hidden] ; Add partition offset
adc edx,[Hidden+4]
mov cx,retry_count
@@ -404,8 +404,8 @@ xint13:
bailmsg: db 'Boot error', 0Dh, 0Ah, 0
; This fails if the boot sector overflowsg
- zb 1FAh-($-$$)
-FirstSector dd 0xDEADBEEF ; Location of sector 1
+ zb 1F6h-($-$$)
+FirstSector dq 0xFEEDFACEDEADBEEF ; Location of sector 1
; This field will be filled in 0xAA55 by the installer, but we abuse it
; to house a pointer to the INT 16h instruction at
@@ -443,12 +443,13 @@ LDLDwords dd 0 ; Total dwords starting at ldlinux_sys,
CheckSum dd 0 ; Checksum starting at ldlinux_sys
; value = LDLINUX_MAGIC - [sum of dwords]
MaxTransfer dw 127 ; Max sectors to transfer
+ADVSecPtr dw ADVSec0 - LDLINUX_SYS
CurrentDirPtr dw CurrentDirName-LDLINUX_SYS ; Current directory name string
CurrentDirLen dw CURRENTDIR_MAX
SubvolPtr dw SubvolName-LDLINUX_SYS
SubvolLen dw SUBVOL_MAX
SecPtrOffset dw SectorPtrs-LDLINUX_SYS
-SecPtrCnt dw (SectorPtrsEnd - SectorPtrs) >> 2
+SecPtrCnt dw (SectorPtrsEnd - SectorPtrs)/10
;
; Installer pokes the base directory here. This is in .data16 so it
@@ -518,37 +519,19 @@ load_rest:
.get_chunk:
jcxz .done
- xor ebp,ebp
- mov di,bx ; Low 64K of target address
- lodsd ; First sector of this chunk
-
- mov edx,eax
-
-.make_chunk:
- inc bp
- dec cx
- jz .chunk_ready
- cmp esi,ebx ; Pointer we don't have yet?
- jae .chunk_ready
- inc edx ; Next linear sector
- cmp [si],edx ; Does it match
- jnz .chunk_ready ; If not, this is it
- add si,4 ; If so, add sector to chunk
- add di,SECTOR_SIZE ; Check for 64K segment wrap
- jnz .make_chunk
-
-.chunk_ready:
+ mov eax,[si]
+ mov edx,[si+4]
+ movzx ebp,word [si+8]
+ sub cx,bp
push ebx
- push es
shr ebx,4 ; Convert to a segment
mov es,bx
xor bx,bx
- xor edx,edx ; Zero-extend LBA
call getlinsec
- pop es
pop ebx
shl ebp,SECTOR_SHIFT
add ebx,ebp
+ add si,10
jmp .get_chunk
.done:
@@ -563,7 +546,6 @@ verify_checksum:
mov ecx,[LDLDwords]
sub ecx,SECTOR_SIZE >> 2
mov eax,[CheckSum]
- push ds
.checksum:
add eax,[si]
add si,4
@@ -575,7 +557,6 @@ verify_checksum:
.nowrap:
dec ecx
jnz .checksum
- pop ds
and eax,eax ; Should be zero
jz all_read ; We're cool, go for it!
@@ -609,7 +590,6 @@ verify_checksum:
global getlinsec
getlinsec:
pushad
- xor edx,edx ; For now only 32-bit internal
add eax,[Hidden] ; Add partition offset
adc edx,[Hidden+4]
.jmp: jmp strict short getlinsec_cbios
@@ -790,16 +770,22 @@ rl_checkpt equ $ ; Must be <= 8000h
rl_checkpt_off equ ($-$$)
%ifndef DEPEND
-%if rl_checkpt_off > 3FCh ; Need one pointer in here
-%error "Sector 1 overflow"
-%endif
+ %if rl_checkpt_off > 3F6h ; Need one extent
+ %assign rl_checkpt_overflow rl_checkpt_off - 3F6h
+ %error Sector 1 overflow by rl_checkpt_overflow bytes
+ %endif
%endif
-; Sector pointers
- alignz 4
+;
+; Extent pointers... each extent contains an 8-byte LBA and an 2-byte
+; sector count. In most cases, we will only ever need a handful of
+; extents, but we have to assume a maximally fragmented system where each
+; extent contains only one sector.
+;
+ alignz 2
MaxInitDataSize equ 96 << 10
MaxLMA equ TEXT_START+SECTOR_SIZE+MaxInitDataSize
-SectorPtrs times MaxInitDataSize >> SECTOR_SHIFT dd 0
+SectorPtrs zb 10*(MaxInitDataSize >> SECTOR_SHIFT)
SectorPtrsEnd equ $
; ----------------------------------------------------------------------------
@@ -808,6 +794,10 @@ SectorPtrsEnd equ $
section .text16
all_read:
+ ; We enter here with both DS and ES scrambled...
+ xor ax,ax
+ mov ds,ax
+ mov es,ax
;
; Let the user (and programmer!) know we got this far. This used to be
; in Sector 1, but makes a lot more sense here.
diff --git a/extlinux/Makefile b/extlinux/Makefile
index ab92c2c6..c197790d 100644
--- a/extlinux/Makefile
+++ b/extlinux/Makefile
@@ -17,7 +17,7 @@
topdir = ..
include $(topdir)/MCONFIG
-OPTFLAGS = -g -Os
+OPTFLAGS = -g -O0
INCLUDES = -I. -I.. -I../libinstaller
CFLAGS = $(GCCWARN) -Wno-sign-compare -D_FILE_OFFSET_BITS=64 \
$(OPTFLAGS) $(INCLUDES)
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);
diff --git a/libfat/libfat.h b/libfat/libfat.h
index 1ebc8698..a0179d7c 100644
--- a/libfat/libfat.h
+++ b/libfat/libfat.h
@@ -26,7 +26,7 @@
#define LIBFAT_SECTOR_SIZE 512
#define LIBFAT_SECTOR_MASK 511
-typedef uint32_t libfat_sector_t;
+typedef uint64_t libfat_sector_t;
struct libfat_filesystem;
struct libfat_direntry {
diff --git a/libinstaller/syslinux.h b/libinstaller/syslinux.h
index 311b9f2e..bf2b716a 100644
--- a/libinstaller/syslinux.h
+++ b/libinstaller/syslinux.h
@@ -46,7 +46,8 @@ void syslinux_make_bootsect(void *);
const char *syslinux_check_bootsect(const void *bs);
/* This patches the boot sector and ldlinux.sys based on a sector map */
-int syslinux_patch(const uint32_t * sectors, int nsectors,
+typedef uint64_t sector_t;
+int syslinux_patch(const sector_t *sectors, int nsectors,
int stupid, int raid_mode, const char *subdir);
#endif
diff --git a/libinstaller/syslxcom.c b/libinstaller/syslxcom.c
index 825419b4..013b69af 100644
--- a/libinstaller/syslxcom.c
+++ b/libinstaller/syslxcom.c
@@ -33,6 +33,8 @@
#include <sys/vfs.h>
#include <linux/fs.h> /* FIGETBSZ, FIBMAP */
#include <linux/msdos_fs.h> /* FAT_IOCTL_SET_ATTRIBUTES */
+#undef SECTOR_SIZE /* Defined in msdos_fs.h for no good reason */
+#include <linux/fiemap.h> /* FIEMAP definitions */
#include "syslxcom.h"
const char *program;
@@ -180,13 +182,86 @@ void set_attributes(int fd)
}
}
-/*
- * Produce file map
- */
-int sectmap(int fd, uint32_t * sectors, int nsectors)
+/* New FIEMAP based mapping */
+static int sectmap_fie(int fd, sector_t *sectors, int nsectors)
{
- unsigned int blksize, blk, nblk;
+ struct fiemap *fm;
+ struct fiemap_extent *fe;
+ unsigned int i, nsec;
+ sector_t sec, *secp, *esec;
+ struct stat st;
+ uint64_t maplen;
+
+ if (fstat(fd, &st))
+ return -1;
+
+ fm = alloca(sizeof(struct fiemap)
+ + nsectors * sizeof(struct fiemap_extent));
+
+ memset(fm, 0, sizeof *fm);
+
+ maplen = (uint64_t)nsectors << SECTOR_SHIFT;
+ if (maplen > (uint64_t)st.st_size)
+ maplen = st.st_size;
+
+ fm->fm_start = 0;
+ fm->fm_length = maplen;
+ fm->fm_flags = FIEMAP_FLAG_SYNC;
+ fm->fm_extent_count = nsectors;
+
+ if (ioctl(fd, FS_IOC_FIEMAP, &fm))
+ return -1;
+
+ memset(sectors, 0, nsectors * sizeof *sectors);
+ esec = sectors + nsectors;
+
+ fe = fm->fm_extents;
+
+ if (fm->fm_mapped_extents < 1 ||
+ !(fe[fm->fm_mapped_extents-1].fe_flags & FIEMAP_EXTENT_LAST))
+ return -1;
+
+ for (i = 0; i < fm->fm_mapped_extents; i++) {
+ if (fe->fe_flags & FIEMAP_EXTENT_LAST) {
+ /* If this is the *final* extent, pad the length */
+ fe->fe_length = (fe->fe_length + SECTOR_SIZE - 1)
+ & (SECTOR_SIZE - 1);
+ }
+
+ if ((fe->fe_logical | fe->fe_physical| fe->fe_length) &
+ (SECTOR_SIZE - 1))
+ return -1;
+
+ if (fe->fe_flags & (FIEMAP_EXTENT_UNKNOWN|
+ FIEMAP_EXTENT_DELALLOC|
+ FIEMAP_EXTENT_ENCODED|
+ FIEMAP_EXTENT_DATA_ENCRYPTED|
+ FIEMAP_EXTENT_UNWRITTEN))
+ return -1;
+
+ secp = sectors + (fe->fe_logical >> SECTOR_SHIFT);
+ sec = fe->fe_physical >> SECTOR_SHIFT;
+ nsec = fe->fe_length >> SECTOR_SHIFT;
+
+ while (nsec--) {
+ if (secp >= esec)
+ break;
+ *secp++ = sec++;
+ }
+
+ fe++;
+ }
+
+ return 0;
+}
+
+/* Legacy FIBMAP based mapping */
+static int sectmap_fib(int fd, sector_t *sectors, int nsectors)
+{
+ unsigned int blk, nblk;
unsigned int i;
+ unsigned int blksize;
+ sector_t sec;
/* Get block size */
if (ioctl(fd, FIGETBSZ, &blksize))
@@ -197,22 +272,28 @@ int sectmap(int fd, uint32_t * sectors, int nsectors)
nblk = 0;
while (nsectors) {
-
blk = nblk++;
- dprintf("querying block %u\n", blk);
if (ioctl(fd, FIBMAP, &blk))
return -1;
- blk *= blksize;
+ sec = (sector_t)blk * blksize;
for (i = 0; i < blksize; i++) {
- if (!nsectors)
- return 0;
-
- dprintf("Sector: %10u\n", blk);
- *sectors++ = blk++;
- nsectors--;
+ *sectors++ = sec++;
+ if (! --nsectors)
+ break;
}
}
return 0;
}
+
+/*
+ * Produce file map
+ */
+int sectmap(int fd, sector_t *sectors, int nsectors)
+{
+ if (!sectmap_fie(fd, sectors, nsectors))
+ return 0;
+
+ return sectmap_fib(fd, sectors, nsectors);
+}
diff --git a/libinstaller/syslxcom.h b/libinstaller/syslxcom.h
index ba4f1d00..39ca09d3 100644
--- a/libinstaller/syslxcom.h
+++ b/libinstaller/syslxcom.h
@@ -1,6 +1,8 @@
#ifndef _H_SYSLXCOM_
#define _H_SYSLXCOM_
+#include "syslinux.h"
+
/* Global fs_type for handling fat, ext2/3/4 and btrfs */
enum filesystem {
NONE,
@@ -15,6 +17,6 @@ ssize_t xpread(int fd, void *buf, size_t count, off_t offset);
ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset);
void clear_attributes(int fd);
void set_attributes(int fd);
-int sectmap(int fd, uint32_t * sectors, int nsectors);
+int sectmap(int fd, sector_t *sectors, int nsectors);
#endif
diff --git a/libinstaller/syslxint.h b/libinstaller/syslxint.h
index bc818df6..53aa05dc 100644
--- a/libinstaller/syslxint.h
+++ b/libinstaller/syslxint.h
@@ -15,6 +15,12 @@
#include "syslinux.h"
+#if defined(__386__) || defined(__i386__) || defined(__x86_64__)
+# define X86_MEM 1 /* Littleendian and unaligned safe */
+#else
+# define X86_MEM 0
+#endif
+
/*
* Access functions for littleendian numbers, possibly misaligned.
*/
@@ -25,37 +31,47 @@ static inline uint8_t get_8(const uint8_t * p)
static inline uint16_t get_16(const uint16_t * p)
{
-#if defined(__i386__) || defined(__x86_64__)
+#if X86_MEM
/* Littleendian and unaligned-capable */
return *p;
#else
const uint8_t *pp = (const uint8_t *)p;
- return (uint16_t) pp[0] + ((uint16_t) pp[1] << 8);
+ return pp[0] + ((uint16_t)pp[1] << 8);
#endif
}
static inline uint32_t get_32(const uint32_t * p)
{
-#if defined(__i386__) || defined(__x86_64__)
+#if X86_MEM
/* Littleendian and unaligned-capable */
return *p;
#else
- const uint8_t *pp = (const uint8_t *)p;
- return (uint32_t) pp[0] + ((uint32_t) pp[1] << 8) +
- ((uint32_t) pp[2] << 16) + ((uint32_t) pp[3] << 24);
+ const uint16_t *pp = (const uint16_t *)p;
+ return get_16(pp[0]) + (uint32_t)get_16(pp[1]);
+#endif
+}
+
+static inline uint64_t get_64(const uint64_t * p)
+{
+#if X86_MEM
+ /* Littleendian and unaligned-capable */
+ return *p;
+#else
+ const uint32_t *pp = (const uint32_t *)p;
+ return get_32(pp[0]) + (uint64_t)get_32(pp[1]);
#endif
}
-static inline void set_8(uint8_t * p, uint8_t v)
+static inline void set_8(uint8_t *p, uint8_t v)
{
*p = v;
}
-static inline void set_16(uint16_t * p, uint16_t v)
+static inline void set_16(uint16_t *p, uint16_t v)
{
-#if defined(__i386__) || defined(__x86_64__)
+#if X86_MEM
/* Littleendian and unaligned-capable */
- *(uint16_t *) p = v;
+ *p = v;
#else
uint8_t *pp = (uint8_t *) p;
pp[0] = (v & 0xff);
@@ -63,11 +79,11 @@ static inline void set_16(uint16_t * p, uint16_t v)
#endif
}
-static inline void set_32(uint32_t * p, uint32_t v)
+static inline void set_32(uint32_t *p, uint32_t v)
{
-#if defined(__i386__) || defined(__x86_64__)
+#if X86_MEM
/* Littleendian and unaligned-capable */
- *(uint32_t *) p = v;
+ *p = v;
#else
uint8_t *pp = (uint8_t *) p;
pp[0] = (v & 0xff);
@@ -77,6 +93,18 @@ static inline void set_32(uint32_t * p, uint32_t v)
#endif
}
+static inline void set_64(uint64_t *p, uint64_t v)
+{
+#if X86_MEM
+ /* Littleendian and unaligned-capable */
+ *p = v;
+#else
+ uint32_t *pp = (uint32_t *) p;
+ set_32(pp[0], v);
+ set_32(pp[1], v >> 32);
+#endif
+}
+
#define LDLINUX_MAGIC 0x3eb202fe
/* Patch area for disk-based installers */
@@ -88,6 +116,7 @@ struct patch_area {
uint32_t dwords;
uint32_t checksum;
uint16_t maxtransfer;
+ uint16_t advptroffset;
uint16_t diroffset;
uint16_t dirlen;
uint16_t subvoloffset;
@@ -96,7 +125,13 @@ struct patch_area {
uint16_t secptrcnt;
};
- /* FAT bootsector format, also used by other disk-based derivatives */
+/* Sector extent */
+struct syslinux_extent {
+ uint64_t lba;
+ uint16_t len;
+} __attribute__((packed));
+
+/* FAT bootsector format, also used by other disk-based derivatives */
struct boot_sector {
uint8_t bsJump[3];
char bsOemName[8];
@@ -121,7 +156,7 @@ struct boot_sector {
uint32_t VolumeID;
char VolumeLabel[11];
char FileSysType[8];
- uint8_t Code[444];
+ uint8_t Code[440];
} __attribute__ ((packed)) bs16;
struct {
uint32_t FATSz32;
@@ -137,11 +172,11 @@ struct boot_sector {
uint32_t VolumeID;
char VolumeLabel[11];
char FileSysType[8];
- uint8_t Code[416];
+ uint8_t Code[412];
} __attribute__ ((packed)) bs32;
} __attribute__ ((packed));
- uint32_t NextSector; /* Pointer to the first unused sector */
+ uint64_t NextSector; /* Pointer to the first unused sector */
uint16_t bsSignature;
} __attribute__ ((packed));
diff --git a/libinstaller/syslxmod.c b/libinstaller/syslxmod.c
index c600a816..c9eddec1 100644
--- a/libinstaller/syslxmod.c
+++ b/libinstaller/syslxmod.c
@@ -161,7 +161,7 @@ static __noinline uint8_t get_8_sl(const uint8_t * p)
uint8_t v;
p = set_fs(p);
- asm volatile ("movb %%fs:%1,%0":"=q" (v):"m"(*p));
+ asm volatile("movb %%fs:%1,%0":"=q" (v):"m"(*p));
return v;
}
#endif
@@ -171,7 +171,7 @@ static __noinline uint16_t get_16_sl(const uint16_t * p)
uint16_t v;
p = set_fs(p);
- asm volatile ("movw %%fs:%1,%0":"=r" (v):"m"(*p));
+ asm volatile("movw %%fs:%1,%0":"=r" (v):"m"(*p));
return v;
}
@@ -180,28 +180,50 @@ static __noinline uint32_t get_32_sl(const uint32_t * p)
uint32_t v;
p = set_fs(p);
- asm volatile ("movl %%fs:%1,%0":"=r" (v):"m"(*p));
+ asm volatile("movl %%fs:%1,%0":"=r" (v):"m"(*p));
return v;
}
#if 0 /* unused */
+static __noinline uint64_t get_64_sl(const uint64_t * p)
+{
+ uint32_t v0, v1;
+ const uint32_t *pp = (const uint32_t *)p;
+
+ p = set_fs(p);
+ asm volatile("movl %%fs:%1,%0" : "=r" (v0) : "m" (pp[0]));
+ asm volatile("movl %%fs:%1,%0" : "=r" (v1) : "m" (pp[1]));
+ return v0 + ((uint64_t)v1 << 32);
+}
+#endif
+
+#if 0 /* unused */
static __noinline void set_8_sl(uint8_t * p, uint8_t v)
{
p = set_fs(p);
- asm volatile ("movb %1,%%fs:%0":"=m" (*p):"qi"(v));
+ asm volatile("movb %1,%%fs:%0":"=m" (*p):"qi"(v));
}
#endif
static __noinline void set_16_sl(uint16_t * p, uint16_t v)
{
p = set_fs(p);
- asm volatile ("movw %1,%%fs:%0":"=m" (*p):"ri"(v));
+ asm volatile("movw %1,%%fs:%0":"=m" (*p):"ri"(v));
}
static __noinline void set_32_sl(uint32_t * p, uint32_t v)
{
p = set_fs(p);
- asm volatile ("movl %1,%%fs:%0":"=m" (*p):"ri"(v));
+ asm volatile("movl %1,%%fs:%0":"=m" (*p):"ri"(v));
+}
+
+static __noinline void set_64_sl(uint64_t * p, uint64_t v)
+{
+ uint32_t *pp = (uint32_t *)p;
+
+ p = set_fs(p);
+ asm volatile("movl %1,%%fs:%0" : "=m" (pp[0]) : "ri"((uint32_t)v));
+ asm volatile("movl %1,%%fs:%0" : "=m" (pp[1]) : "ri"((uint32_t)(v >> 32)));
}
#else
@@ -210,13 +232,63 @@ static __noinline void set_32_sl(uint32_t * p, uint32_t v)
#define get_8_sl(x) get_8(x)
#define get_16_sl(x) get_16(x)
#define get_32_sl(x) get_32(x)
+#define get_64_sl(x) get_64(x)
#define set_8_sl(x,y) set_8(x,y)
#define set_16_sl(x,y) set_16(x,y)
#define set_32_sl(x,y) set_32(x,y)
+#define set_64_sl(x,y) set_64(x,y)
#endif
/*
+ * Generate sector extents
+ */
+static void generate_extents(struct syslinux_extent *ex, int nptrs,
+ const sector_t *sectp, int nsect)
+{
+ struct syslinux_extent thisex;
+ uint32_t addr = 0x7c00 + 2*SECTOR_SIZE;
+ uint32_t base;
+ sector_t sect;
+
+ thisex.len = base = 0;
+
+ memset(ex, 0, nptrs * sizeof *ex);
+
+ while (nsect) {
+ sect = *sectp++;
+
+ if (thisex.len &&
+ sect == thisex.lba + thisex.len &&
+ ((addr ^ (base + thisex.len * SECTOR_SIZE)) & 0xffff0000) == 0) {
+ /* We can add to the current extent */
+ thisex.len++;
+ goto next;
+ }
+
+ if (thisex.len) {
+ set_64_sl(&ex->lba, thisex.lba);
+ set_16_sl(&ex->len, thisex.len);
+ ex++;
+ }
+
+ base = addr;
+ thisex.lba = sect;
+ thisex.len = 1;
+
+ next:
+ addr += SECTOR_SIZE;
+ nsect--;
+ }
+
+ if (thisex.len) {
+ set_64_sl(&ex->lba, thisex.lba);
+ set_16_sl(&ex->len, thisex.len);
+ ex++;
+ }
+}
+
+/*
* This patches the boot sector and the beginning of ldlinux.sys
* based on an ldlinux.sys sector map passed in. Typically this is
* handled by writing ldlinux.sys, mapping it, and then overwrite it
@@ -227,18 +299,23 @@ static __noinline void set_32_sl(uint32_t * p, uint32_t v)
* Returns the number of modified bytes in ldlinux.sys if successful,
* otherwise -1.
*/
-int syslinux_patch(const uint32_t * sectors, int nsectors,
+#define NADV 2
+
+int syslinux_patch(const sector_t *sectp, int nsectors,
int stupid, int raid_mode, const char *subdir)
{
struct patch_area *patcharea;
+ struct syslinux_extent *ex;
uint32_t *wp;
- int nsect = (boot_image_len + 511) >> 9;
+ int nsect = ((boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT) + 2;
uint32_t csum;
int i, dw, nptrs;
struct boot_sector *sbs = (struct boot_sector *)boot_sector;
size_t diroffset, dirlen;
+ int secptroffset;
+ uint64_t *advptrs;
- if (nsectors < nsect)
+ if (nsectors <= nsect)
return -1;
/* Handle RAID mode, write proper bsSignature */
@@ -248,7 +325,7 @@ int syslinux_patch(const uint32_t * sectors, int nsectors,
set_16(&sbs->bsSignature, 0xAA55);
/* First sector need pointer in boot sector */
- set_32(&sbs->NextSector, *sectors++);
+ set_64(&sbs->NextSector, *sectp++);
/* Search for LDLINUX_MAGIC to find the patch area */
for (wp = (uint32_t *)boot_image; get_32_sl(wp) != LDLINUX_MAGIC;
@@ -257,10 +334,36 @@ int syslinux_patch(const uint32_t * sectors, int nsectors,
/* Set up the totals */
dw = boot_image_len >> 2; /* COMPLETE dwords, excluding ADV */
- set_16_sl(&patcharea->data_sectors, nsect); /* Not including ADVs */
+ set_16_sl(&patcharea->data_sectors, nsect - 2); /* Not including ADVs */
set_16_sl(&patcharea->adv_sectors, 2); /* ADVs need 2 sectors */
set_32_sl(&patcharea->dwords, dw);
+ /* Handle Stupid mode */
+ if (stupid) {
+ /* Access only one sector at a time */
+ set_16(&patcharea->maxtransfer, 1);
+ }
+
+ /* Set the sector extents */
+ secptroffset = get_16_sl(&patcharea->secptroffset);
+ ex = (struct syslinux_extent *) ((char *)boot_image + secptroffset);
+ nptrs = get_16_sl(&patcharea->secptrcnt);
+
+ 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_sl(&patcharea->advptroffset));
+ set_64_sl(&advptrs[0], sectp[nsect-1-2]);
+ set_64_sl(&advptrs[1], sectp[nsect-1-1]);
+
/* Poke in the base directory path */
if (subdir) {
diroffset = get_16(&patcharea->diroffset);
@@ -272,25 +375,6 @@ int syslinux_patch(const uint32_t * sectors, int nsectors,
memcpy((char *)boot_image + diroffset, subdir, strlen(subdir) + 1);
}
- /* Handle Stupid mode */
- if (stupid) {
- /* Access only one sector at a time */
- set_16(&patcharea->maxtransfer, 1);
- }
-
- /* Set the sector pointers */
- wp = (uint32_t *) ((char *)boot_image +
- get_16_sl(&patcharea->secptroffset));
- nptrs = get_16_sl(&patcharea->secptrcnt);
-
- nsect += 2;
- while (--nsect) { /* the first sector is in bs->NextSector */
- set_32_sl(wp++, *sectors++);
- nptrs--;
- }
- while (nptrs--)
- set_32_sl(wp++, 0);
-
/* Now produce a checksum */
set_32_sl(&patcharea->checksum, 0);
diff --git a/linux/syslinux.c b/linux/syslinux.c
index d6a5d834..34d591b7 100644
--- a/linux/syslinux.c
+++ b/linux/syslinux.c
@@ -277,7 +277,7 @@ int main(int argc, char *argv[])
char *ldlinux_name;
char *ldlinux_path;
char *subdir;
- uint32_t *sectors = NULL;
+ sector_t *sectors = NULL;
int ldlinux_sectors = (boot_image_len + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
const char *errmsg;
int mnt_cookie;
@@ -472,7 +472,8 @@ umount:
/*
* Patch ldlinux.sys and the boot sector
*/
- i = syslinux_patch(sectors, ldlinux_sectors, opt.stupid_mode, opt.raid_mode, subdir);
+ i = syslinux_patch(sectors, ldlinux_sectors, opt.stupid_mode,
+ opt.raid_mode, subdir);
patch_sectors = (i + SECTOR_SIZE - 1) >> SECTOR_SHIFT;
/*