Thomas Schmitt
2017-Mar-24 13:12 UTC
[syslinux] "isolinux.bin missing or corrupt" when booting USB flash drive in old PC
Hi, i am looking now at the code which i assume loads the rest of isolinux.bin. The entry point for program execution from the MBR is obviously at http://git.zytor.com/syslinux/syslinux.git/tree/core/isolinux.asm#n186 (Do i get it right that this is the Intel syntax ? (Gronfff)) If POP yields the victim of the most recent not yet popped PUSH, then this does not look correctly coordinated with the MBR. isohdpfx.S has: pushw %cx /* -16: Save sectors on the stack */ movzbw %dh, %ax /* dh = max head */ incw %ax /* From 0-based max to count */ pushw %ax /* -18: Save heads on the stack */ mulw %cx /* Heads*sectors -> sectors per cylinder */ pushw %bx /* -20: EBIOS flag */ isolinux.asm has: ; The following values will have been pushed onto the ; entry stack: ; - partition offset (qword) ; - ES ; - DI ; - DX (including drive number) ; - CBIOS Heads ; - CBIOS Sectors ; - EBIOS flag ; (top of stack) _start_hybrid: pop cx ; EBIOS flag pop word [cs:bsSecPerTrack] pop word [cs:bsHeads] pop dx pop di pop es Shouldn't the sequence of pop word [cs:bsSecPerTrack] pop word [cs:bsHeads] be the reverse sequence of pushw %cx /* -16: Save sectors on the stack */ pushw %ax /* -18: Save heads on the stack */ ? Can it be that the two POPW which would need to swap places are the two byte groups "8f 06 28 30 2e" and "8f 06 2a 30 5a" near byte position 64 of isolinux.bin : fb c0 78 70 59 2e 8f 06 28 30 2e 8f 06 2a 30 5a given that isohdpfx.S has: HYBRID_MAGIC = 0x7078c0fb isolinux_hybrid_signature = 0x7c00+64 isolinux_start_hybrid = 0x7c00+64+4 (One may guess that i will propose to patch the ISO at byte 8444*512+70, if i get a "yes" on this question.) --------------------------------------------------------------------- If this is indeed the wrong sequence in isolinux.bin then the riddle arises why the second try of David with isohdpfc.bin succeeded. And why do our virtual BIOSes work ? Don't they get the fake EBIOS flag ? Can it be that isolinux.bin unconditionally first tries "getlinsec_ebios" before it defaults to "getlinsec_cbios" which is a consumer of "bsSecPerTrack" and "bsHeads" ? How can so few bytes impose so many riddles ? Have a nice day :) Thomas
Thomas Schmitt
2017-Mar-24 16:38 UTC
[syslinux] "isolinux.bin missing or corrupt" when booting USB flash drive in old PC
Hi, i now believe to see a difference between isohdpfd.S and isohdpfc.S which explains why isohdpfc.bin works with isolinux.bin on our virtual BIOSes: isohdpfc pushes the CX value to the stack which it gets from INT 13 AH 41. Quite surely bit 0 of that CX is not set. But bit 2 "Enhanced Disk Drive" could be set. https://en.wikipedia.org/wiki/INT_13H I understand on David's BIOS after andw $1,%cx /* Bit 0 = fixed disk subset */ jz 1f jz jumps because CX is 0. This gets pushed to the stack. (So the code around the int 13 assumes that either int 13 fails and returns CX == 0, or the reply is good enough to reach "andw 1,%cx". Ewww ...) Now in isolinux.asm we have this test, quite immediately after the stack was exploited: mov si,bios_cbios jcxz _start_common mov si,bios_ebios jmp _start_common If CX is 4 (= EDD) here, then we go to LBA addressing and the confused geometry is not of interest on the first try. At least this would explain why our modern virtual BIOSes let isolinux.bin work. (I.e. the "EBIOS" flag is indeed effective in isolinux.bin.) David's BIOS would have to announce in CX "EDD" (or "Drive Locking and Ejecting" = bit 1) and would have to be able to do LBA addressing despite no bit 0 "Device Access using the packet structure". (Wikipedia gives the vague impression that bit 0 really shall indicate the availability of INT 13 AH 42 which does the LBA reading.) Have a nice day :) Thomas
Martin Str|mberg
2017-Mar-24 18:55 UTC
[syslinux] "isolinux.bin missing or corrupt" when booting USB flash drive in old PC
On Fri, Mar 24, 2017 at 05:38:31PM +0100, Thomas Schmitt via Syslinux wrote:> isohdpfc pushes the CX value to the stack which it gets from INT 13 AH 41. > Quite surely bit 0 of that CX is not set. But bit 2 "Enhanced Disk Drive" > could be set. > https://en.wikipedia.org/wiki/INT_13H > > I understand on David's BIOS after > > andw $1,%cx /* Bit 0 = fixed disk subset */ > jz 1f > > jz jumps because CX is 0. This gets pushed to the stack. > (So the code around the int 13 assumes that either int 13 fails and returns > CX == 0, or the reply is good enough to reach "andw 1,%cx". Ewww ...)Well... I'm not quite following you. If it reaches andw 1,%cx then CX will indicate if wanted EBIOS calls are supported or not as the and will mask away all but the interesting bit. The code: /* Check to see if we have EBIOS */ pushw %dx /* drive number */ movb $0x41, %ah /* %al == 0 already */ movw $0x55aa, %bx xorw %cx, %cx xorb %dh, %dh stc int $0x13 jc 1f cmpw $0xaa55, %bx jne 1f andw $1,%cx /* Bit 0 = fixed disk subset */ jz 1f /* We have EBIOS; patch in the following code at read_sector_cbios: movb $0x42, %ah ; jmp read_common */ movl $0xeb42b4+((read_common-read_sector_cbios-4) << 24), \ (read_sector_cbios) jmp 1f 1: This is my interpretation: David's machine's BIOS returns no carry indicating maybe support. However it doesn't set BX to the correct value, so isohdpfx.S jumps to 1f with some flags in CX set. Or David's machine's BIOS returns carry but corrupts CX. My versions of isohdpfc just replaced the first "jc 1f" above with "jnc 1f" or with "jmp 1f", thus in the end possibly indicating EBIOS support to isolinux (assuming weird return status on the EBIOS check). Then I interpret the code in isolinux that when EBIOS calls doesn't work it will fall back to old not extended calls (CBIOS?). That's why it worked after a delay. Your findings in isohdpfx.S made clear it confused/is wrong with regard to order of number of heads and sectors on the stack. Either the declared offsets were wrong or the order of pushes. At that time I choose the order of pushes as the correct way. Good work digging in the code again (isolinux.asm). Yes, it's Intel syntax, or more specifically nasm syntax (in case it matters). Now given your findings in isolinux.asm it seems that previous choice was wrong and the declared offsets were correct. Thus a new patch that corrects the order of pushes (instead of offsets) _and_ setting CX to zero if EBIOS detection fails. Apply it to orginal syslinux code. diff --git a/mbr/isohdpfx.S b/mbr/isohdpfx.S index 17e1efe..14eca14 100644 --- a/mbr/isohdpfx.S +++ b/mbr/isohdpfx.S @@ -151,7 +151,7 @@ next: /* Check to see if we have EBIOS */ pushw %dx /* drive number */ - movb $0x41, %ah /* %al == 0 already */ + movb $0x41, %ah movw $0x55aa, %bx xorw %cx, %cx xorb %dh, %dh @@ -167,20 +167,22 @@ next: read_sector_cbios: movb $0x42, %ah ; jmp read_common */ movl $0xeb42b4+((read_common-read_sector_cbios-4) << 24), \ (read_sector_cbios) - jmp 1f + jmp 2f 1: + xor %cx, %cx /* Clear EBIOS flag. */ +2: popw %dx pushw %cx /* EBIOS flag */ /* Get (C)HS geometry */ movb $0x08, %ah int $0x13 - andw $0x3f, %cx /* Sector count */ popw %bx /* EBIOS flag */ - pushw %cx /* -16: Save sectors on the stack */ movzbw %dh, %ax /* dh = max head */ incw %ax /* From 0-based max to count */ - pushw %ax /* -18: Save heads on the stack */ + pushw %ax /* -16: Save heads on the stack */ + andw $0x3f, %cx /* Sector count */ + pushw %cx /* -18: Save sectors on the stack */ mulw %cx /* Heads*sectors -> sectors per cylinder */ pushw %bx /* -20: EBIOS flag */ I suppose you'd want a .bin as well: <http://www.ludd.ltu.se/~ams/tmp/isohdpfx.bin.170324> -- MartinS