diff options
author | H. Peter Anvin <hpa@zytor.com> | 2009-05-14 16:33:37 -0700 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2009-05-14 16:33:37 -0700 |
commit | 0046660e3849bef9d922ba69d07c5bc639d1d133 (patch) | |
tree | 0d60efb691e622912682b7bab367a51ce8ead76e | |
parent | 1c7146a2eeaaf0021cc15e682e12609a652ed870 (diff) | |
download | syslinux-0046660e3849bef9d922ba69d07c5bc639d1d133.tar.gz |
core: handle more than 32K of code for disk-based derivatives
Handle more than 32K worth of code for disk-based derivatives. We do
this by allowing the sector pointers to overflow past sector 1; this
is OK because we limit a run to be based on only the pointers that we
have read so far.
XXX: This is implemented for EXTLINUX, but breaks SYSLINUX. Need to
update (and unify!) the SYSLINUX installers to cope.
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r-- | core/adv.inc | 14 | ||||
-rw-r--r-- | core/diskstart.inc | 56 | ||||
-rw-r--r-- | core/init.inc | 2 | ||||
-rw-r--r-- | core/ldlinux.asm | 1 | ||||
-rw-r--r-- | extlinux/main.c | 125 | ||||
-rw-r--r-- | libinstaller/setadv.c | 13 | ||||
-rw-r--r-- | libinstaller/syslxint.h | 78 |
7 files changed, 172 insertions, 117 deletions
diff --git a/core/adv.inc b/core/adv.inc index c1a2628e..cfb39bdc 100644 --- a/core/adv.inc +++ b/core/adv.inc @@ -67,24 +67,26 @@ adv_init: cmp byte [ADVDrive],-1 jne adv_read -;%if IS_SYSLINUX || IS_MDSLINUX || IS_EXTLINUX -%if IS_EXTLINUX ; Not yet implemented for the other derivatives +%if IS_SYSLINUX || IS_EXTLINUX + cmp word [ADVSectors],2 ; Not present? + jb adv_verify + ; ; Update pointers to default ADVs... ; - mov bx,[LDLSectors] + mov bx,[DataSectors] shl bx,2 mov ecx,[bsHidden] - mov eax,[bx+SectorPtrs-8] - mov edx,[bx+SectorPtrs-4] + mov eax,[bx+SectorPtrs] ; First ADV sector + mov edx,[bx+SectorPtrs+4] ; Second ADV sector add eax,ecx add edx,ecx mov [ADVSec0],eax mov [ADVSec1],edx mov al,[DriveNumber] mov [ADVDrive],al + jmp adv_read %endif - ; ** fall through to adv_verify ** ; ; Initialize the ADV data structure in memory diff --git a/core/diskstart.inc b/core/diskstart.inc index 0749dd84..c4fefbf1 100644 --- a/core/diskstart.inc +++ b/core/diskstart.inc @@ -482,18 +482,14 @@ ldlinux_magic dd LDLINUX_MAGIC ; LDLINUX_MAGIC, plus 8 bytes. ; patch_area: -LDLDwords dw 0 ; Total dwords starting at ldlinux_sys, - ; not including ADVs -LDLSectors dw 0 ; Number of sectors - (bootsec+this sec) - ; but including any ADVs +DataSectors dw 0 ; Number of sectors (not including bootsec) +ADVSectors dw 0 ; Additional sectors for ADVs +LDLDwords dd 0 ; Total dwords starting at ldlinux_sys, CheckSum dd 0 ; Checksum starting at ldlinux_sys ; value = LDLINUX_MAGIC - [sum of dwords] -%if IS_EXTLINUX -CurrentDir dd 2 ; "Current" directory inode number -%endif - -; Space for up to 64 sectors, the theoretical maximum -SectorPtrs times 64 dd 0 +CurrentDir dd 2 ; "Current" directory inode number (EXTLINUX) +SecPtrOffset dw SectorPtrs - ldlinux_sys +SecPtrCnt dw (SectorPtrsEnd - SectorPtrs) >> 2 ldlinux_ent: ; @@ -534,13 +530,14 @@ BIOSName resw 1 ; sector again, though. ; load_rest: - mov si,SectorPtrs - mov bx,7C00h+2*SECTOR_SIZE ; Where we start loading - mov cx,[LDLSectors] + lea esi,[SectorPtrs] + mov ebx,7C00h+2*SECTOR_SIZE ; Where we start loading + mov cx,[DataSectors] + dec cx ; Minus this sector .get_chunk: jcxz .done - xor bp,bp + xor ebp,ebp lodsd ; First sector of this chunk mov edx,eax @@ -549,6 +546,8 @@ load_rest: inc bp dec cx jz .chunk_ready + cmp ebx,esi ; 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 @@ -556,9 +555,17 @@ load_rest: jmp short .make_chunk .chunk_ready: + push ebx + push es + shr ebx,4 ; Convert to a segment + mov es,bx + xor bx,bx + xor edx,edx ; Zero-extend LBA call getlinsecsr - shl bp,SECTOR_SHIFT - add bx,bp + pop es + pop ebx + shl ebp,SECTOR_SHIFT + add ebx,ebp jmp .get_chunk .done: @@ -572,10 +579,18 @@ verify_checksum: mov si,ldlinux_sys mov cx,[LDLDwords] mov edx,-LDLINUX_MAGIC + push ds .checksum: lodsd add edx,eax + and di,di + jnz .nowrap + mov ax,ds + add ax,1000h + mov ds,ax +.nowrap: loop .checksum + pop ds and edx,edx ; Should be zero jz all_read ; We're cool, go for it! @@ -643,11 +658,18 @@ rl_checkpt equ $ ; Must be <= 8000h rl_checkpt_off equ ($-$$) %ifndef DEPEND -%if rl_checkpt_off > 400h +%if rl_checkpt_off > 3FCh ; Need one pointer in here %error "Sector 1 overflow" %endif %endif +; Sector pointers + alignz 4 + global MaxInitDataSize +MaxInitDataSize equ 96 << 10 +SectorPtrs times MaxInitDataSize >> SECTOR_SHIFT dd 0 +SectorPtrsEnd equ $ + ; ---------------------------------------------------------------------------- ; End of code and data that have to be in the first sector ; ---------------------------------------------------------------------------- diff --git a/core/init.inc b/core/init.inc index edfde5de..90c607ee 100644 --- a/core/init.inc +++ b/core/init.inc @@ -28,7 +28,7 @@ common_init: mov edi,__pm_code_start mov ecx,__pm_code_len call bcopy - or esi,-1 ; Set to zero + or esi,-1 ; bzero mov edi,__bss_start mov ecx,__bss_len call bcopy diff --git a/core/ldlinux.asm b/core/ldlinux.asm index 6c5cac15..c9f3f955 100644 --- a/core/ldlinux.asm +++ b/core/ldlinux.asm @@ -963,7 +963,6 @@ readdir: section .bss16 alignb 4 -CurrentDir resd 1 ; Current directory PrevDir resd 1 ; Last scanned directory section .text16 diff --git a/extlinux/main.c b/extlinux/main.c index e9226fce..c4ccd538 100644 --- a/extlinux/main.c +++ b/extlinux/main.c @@ -1,6 +1,7 @@ /* ----------------------------------------------------------------------- * * * Copyright 1998-2008 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -63,12 +64,14 @@ struct my_options { unsigned int sectors; unsigned int heads; int raid_mode; + int stupid_mode; int reset_adv; const char *set_once; } opt = { .sectors = 0, .heads = 0, .raid_mode = 0, + .stupid_mode = 0, .reset_adv = 0, .set_once = NULL, }; @@ -82,6 +85,7 @@ static void __attribute__((noreturn)) usage(int rv) " --zip -z Force zipdrive geometry (-H 64 -S 32)\n" " --sectors=# -S Force the number of sectors per track\n" " --heads=# -H Force number of heads\n" + " --stupid -s Slow, safe and stupid mode\n" " --raid -r Fall back to the next device on boot failure\n" " --once=... -o Execute a command once upon boot\n" " --clear-once -O Clear the boot-once command\n" @@ -109,6 +113,7 @@ static const struct option long_options[] = { { "update", 0, NULL, 'U' }, { "zipdrive", 0, NULL, 'z' }, { "sectors", 1, NULL, 'S' }, + { "stupid", 0, NULL, 's' }, { "heads", 1, NULL, 'H' }, { "raid-mode", 0, NULL, 'r' }, { "version", 0, NULL, 'v' }, @@ -126,57 +131,6 @@ static const char short_options[] = "iUuzS:H:rvho:O"; # define BLKGETSIZE64 _IOR(0x12,114,size_t) #endif -#define LDLINUX_MAGIC 0x3eb202fe - -enum bs_offsets { - bsJump = 0x00, - bsOemName = 0x03, - bsBytesPerSec = 0x0b, - bsSecPerClust = 0x0d, - bsResSectors = 0x0e, - bsFATs = 0x10, - bsRootDirEnts = 0x11, - bsSectors = 0x13, - bsMedia = 0x15, - bsFATsecs = 0x16, - bsSecPerTrack = 0x18, - bsHeads = 0x1a, - bsHiddenSecs = 0x1c, - bsHugeSectors = 0x20, - - /* FAT12/16 only */ - bs16DriveNumber = 0x24, - bs16Reserved1 = 0x25, - bs16BootSignature = 0x26, - bs16VolumeID = 0x27, - bs16VolumeLabel = 0x2b, - bs16FileSysType = 0x36, - bs16Code = 0x3e, - - /* FAT32 only */ - bs32FATSz32 = 36, - bs32ExtFlags = 40, - bs32FSVer = 42, - bs32RootClus = 44, - bs32FSInfo = 48, - bs32BkBootSec = 50, - bs32Reserved = 52, - bs32DriveNumber = 64, - bs32Reserved1 = 65, - bs32BootSignature = 66, - bs32VolumeID = 67, - bs32VolumeLabel = 71, - bs32FileSysType = 82, - bs32Code = 90, - - bsSignature = 0x1fe -}; - -#define bsHead bsJump -#define bsHeadLen (bsOemName-bsHead) -#define bsCode bs32Code /* The common safe choice */ -#define bsCodeLen (bsSignature-bs32Code) - #ifndef EXT2_SUPER_OFFSET #define EXT2_SUPER_OFFSET 1024 #endif @@ -406,8 +360,10 @@ patch_file_and_bootblock(int fd, int dirfd, int devfd) uint32_t *sectp; uint64_t totalbytes, totalsectors; int nsect; - unsigned char *p, *patcharea; - int i, dw; + uint32_t *wp; + struct boot_sector *bs; + struct patch_area *patcharea; + int i, dw, nptrs; uint32_t csum; if ( fstat(dirfd, &dirst) ) { @@ -428,26 +384,28 @@ patch_file_and_bootblock(int fd, int dirfd, int devfd) early bootstrap share code with the FAT version. */ dprintf("heads = %u, sect = %u\n", geo.heads, geo.sectors); + bs = (struct boot_sector *)boot_block; + totalsectors = totalbytes >> SECTOR_SHIFT; if ( totalsectors >= 65536 ) { - set_16(boot_block+bsSectors, 0); + set_16(&bs->bsSectors, 0); } else { - set_16(boot_block+bsSectors, totalsectors); + set_16(&bs->bsSectors, totalsectors); } - set_32(boot_block+bsHugeSectors, totalsectors); + set_32(&bs->bsHugeSectors, totalsectors); - set_16(boot_block+bsBytesPerSec, SECTOR_SIZE); - set_16(boot_block+bsSecPerTrack, geo.sectors); - set_16(boot_block+bsHeads, geo.heads); - set_32(boot_block+bsHiddenSecs, geo.start); + set_16(&bs->bsBytesPerSec, SECTOR_SIZE); + set_16(&bs->bsSecPerTrack, geo.sectors); + set_16(&bs->bsHeads, geo.heads); + set_32(&bs->bsHiddenSecs, geo.start); /* If we're in RAID mode then patch the appropriate instruction; either way write the proper boot signature */ - i = get_16(boot_block+0x1FE); + i = get_16(&bs->bsSignature); if (opt.raid_mode) - set_16(boot_block+i, 0x18CD); /* INT 18h */ + set_16((uint16_t *)(boot_block+i), 0x18CD); /* INT 18h */ - set_16(boot_block+0x1FE, 0xAA55); + set_16(&bs->bsSignature, 0xAA55); /* Construct the boot file */ @@ -461,37 +419,39 @@ patch_file_and_bootblock(int fd, int dirfd, int devfd) } /* First sector need pointer in boot sector */ - set_32(boot_block+0x1F8, *sectp++); - nsect--; + set_32(&bs->NextSector, *sectp++); + + /* Stupid mode? */ + if (opt.stupid_mode) + set_16(&bs->MaxTransfer, 1); /* Search for LDLINUX_MAGIC to find the patch area */ - for ( p = boot_image ; get_32(p) != LDLINUX_MAGIC ; p += 4 ); - patcharea = p+8; + for (wp = (uint32_t *)boot_image; get_32(wp) != LDLINUX_MAGIC; wp++); + patcharea = (struct patch_area *)wp; /* Set up the totals */ dw = boot_image_len >> 2; /* COMPLETE dwords, excluding ADV */ - set_16(patcharea, dw); - set_16(patcharea+2, nsect); /* Not including the first sector, but - including the ADV */ - set_32(patcharea+8, dirst.st_ino); /* "Current" directory */ + set_16(&patcharea->data_sectors, nsect-2); /* -2 for the ADVs */ + set_16(&patcharea->adv_sectors, 2); + set_32(&patcharea->dwords, dw); + set_32(&patcharea->currentdir, dirst.st_ino); /* Set the sector pointers */ - p = patcharea+12; + wp = (uint32_t *)((char *)boot_image+get_16(&patcharea->secptroffset)); + nptrs = get_16(&patcharea->secptrcnt); - memset(p, 0, 64*4); - while ( nsect-- ) { - set_32(p, *sectp++); - p += 4; - } + memset(wp, 0, nptrs*4); + while ( nsect-- ) + set_32(wp++, *sectp++); /* Now produce a checksum */ - set_32(patcharea+4, 0); + set_32(&patcharea->checksum, 0); csum = LDLINUX_MAGIC; - for ( i = 0, p = boot_image ; i < dw ; i++, p += 4 ) - csum -= get_32(p); /* Negative checksum */ + for (i = 0, wp = (uint32_t *)boot_image; i < dw; i++, wp++) + csum -= get_32(wp); /* Negative checksum */ - set_32(patcharea+4, csum); + set_32(&patcharea->checksum, csum); } /* @@ -981,6 +941,9 @@ main(int argc, char *argv[]) case 'r': opt.raid_mode = 1; break; + case 's': + opt.stupid_mode = 1; + break; case 'i': update_only = 0; break; diff --git a/libinstaller/setadv.c b/libinstaller/setadv.c index c768d1b1..c57f0974 100644 --- a/libinstaller/setadv.c +++ b/libinstaller/setadv.c @@ -36,14 +36,14 @@ static void cleanup_adv(unsigned char *advbuf) uint32_t csum; /* Make sure both copies agree, and update the checksum */ - set_32(advbuf, ADV_MAGIC1); + set_32((uint32_t *)advbuf, ADV_MAGIC1); csum = ADV_MAGIC2; for (i = 8; i < ADV_SIZE-4; i += 4) - csum -= get_32(advbuf+i); + csum -= get_32((uint32_t *)(advbuf+i)); - set_32(advbuf+4, csum); - set_32(advbuf+ADV_SIZE-4, ADV_MAGIC3); + set_32((uint32_t *)(advbuf+4), csum); + set_32((uint32_t *)(advbuf+ADV_SIZE-4), ADV_MAGIC3); memcpy(advbuf+ADV_SIZE, advbuf, ADV_SIZE); } @@ -130,12 +130,13 @@ static int adv_consistent(const unsigned char *p) int i; uint32_t csum; - if (get_32(p) != ADV_MAGIC1 || get_32(p+ADV_SIZE-4) != ADV_MAGIC3) + if (get_32((uint32_t *)p) != ADV_MAGIC1 || + get_32((uint32_t *)(p+ADV_SIZE-4)) != ADV_MAGIC3) return 0; csum = 0; for (i = 4; i < ADV_SIZE-4; i += 4) - csum += get_32(p+i); + csum += get_32((uint32_t *)(p+i)); return csum == ADV_MAGIC2; } diff --git a/libinstaller/syslxint.h b/libinstaller/syslxint.h index 120026de..3cecf3c0 100644 --- a/libinstaller/syslxint.h +++ b/libinstaller/syslxint.h @@ -18,12 +18,12 @@ /* * Access functions for littleendian numbers, possibly misaligned. */ -static inline uint8_t get_8(const unsigned char *p) +static inline uint8_t get_8(const uint8_t *p) { return *(const uint8_t *)p; } -static inline uint16_t get_16(const unsigned char *p) +static inline uint16_t get_16(const uint16_t *p) { #if defined(__i386__) || defined(__x86_64__) /* Littleendian and unaligned-capable */ @@ -33,7 +33,7 @@ static inline uint16_t get_16(const unsigned char *p) #endif } -static inline uint32_t get_32(const unsigned char *p) +static inline uint32_t get_32(const uint32_t *p) { #if defined(__i386__) || defined(__x86_64__) /* Littleendian and unaligned-capable */ @@ -44,7 +44,7 @@ static inline uint32_t get_32(const unsigned char *p) #endif } -static inline void set_16(unsigned char *p, uint16_t v) +static inline void set_16(uint16_t *p, uint16_t v) { #if defined(__i386__) || defined(__x86_64__) /* Littleendian and unaligned-capable */ @@ -55,7 +55,7 @@ static inline void set_16(unsigned char *p, uint16_t v) #endif } -static inline void set_32(unsigned char *p, uint32_t v) +static inline void set_32(uint32_t *p, uint32_t v) { #if defined(__i386__) || defined(__x86_64__) /* Littleendian and unaligned-capable */ @@ -71,4 +71,72 @@ static inline void set_32(unsigned char *p, uint32_t v) #define SECTOR_SHIFT 9 /* 512-byte sectors */ #define SECTOR_SIZE (1 << SECTOR_SHIFT) +#define LDLINUX_MAGIC 0x3eb202fe + +/* Patch area for disk-based installers */ +struct patch_area { + uint32_t magic; /* LDLINUX_MAGIC */ + uint32_t instance; /* Per-version value */ + uint16_t data_sectors; + uint16_t adv_sectors; + uint32_t dwords; + uint32_t checksum; + uint32_t currentdir; + uint16_t secptroffset; + uint16_t secptrcnt; +}; + + /* FAT bootsector format, also used by other disk-based derivatives */ +struct boot_sector { + uint8_t bsJump[3]; + char bsOemName[8]; + uint16_t bsBytesPerSec; + uint8_t bsSecPerClust; + uint16_t bsResSectors; + uint8_t bsFATs; + uint16_t bsRootDirEnts; + uint16_t bsSectors; + uint8_t bsMedia; + uint16_t bsFATsecs; + uint16_t bsSecPerTrack; + uint16_t bsHeads; + uint32_t bsHiddenSecs; + uint32_t bsHugeSectors; + + union { + struct { + uint8_t DriveNumber; + uint8_t Reserved1; + uint8_t BootSignature; + uint32_t VolumeID; + char VolumeLabel[11]; + char FileSysType[8]; + uint8_t Code[442]; + } __attribute__((packed)) bs16; + struct { + uint32_t FATSz32; + uint16_t ExtFlags; + uint16_t FSVer; + uint32_t RootClus; + uint16_t FSInfo; + uint16_t BkBootSec; + uint8_t DriveNumber; + uint8_t Reserved1; + uint8_t BootSignature; + uint32_t VolumeID; + char VolumeLabel[11]; + char FileSysType[8]; + uint8_t Code[414]; + } __attribute__((packed)) bs32; + } __attribute__((packed)); + + uint32_t NextSector; /* Pointer to the first unused sector */ + uint16_t MaxTransfer; /* Max sectors per transfer */ + uint16_t bsSignature; +} __attribute__((packed)); + +#define bsHead bsJump +#define bsHeadLen offsetof(struct boot_sector, bsJump) +#define bsCode bs32.Code /* The common safe choice */ + #endif /* SYSLXINT_H */ |