flat assembler
Message board for the users of flat assembler.

Index > OS Construction > Booting from EL Torito CDROM

Author
Thread Post new topic Reply to topic
bzt



Joined: 09 Nov 2018
Posts: 83
bzt 17 Oct 2024, 18:42
Hi,

If someone needs this, I've implemented a little hack to make BIOS boot loaders work with ISO9660 El Torito "no emulation" mode. The issue they are facing is, that BIOS assumes sector sizes to be 512 bytes always, except for CDs, where the sector size is 2048 bytes, therefore the LBA addressing is messed up (and could lead to potential buffer overflows as well).

How does this work?
Normally the disk has some partitioning table which points to the first sectors of partitions (legacy MBR or GPT). With El Torito, there's a so called Boot Catalog, which does the same, it points to some sectors, and when booted from a CDROM, this catalog is used instead of the partitioning table.

Now because of UEFI spec section 12.3.2.1, in order to boot from CDROM under EFI, the Boot Catalog must point to the same EFI System Partition's first sector (VBR, Volume Boot Record) where normally a GPT entry points. Under EFI this VBR is never executed and contains no code at all, it's just a FAT's first sector with BPB data. On the other hand, the BIOS in El Torito mode loads and executes this VBR. (Worth noting that the Boot Catalog should have a separate entry for BIOS boot sector and EFI ESP first sector, however because of TianoCore based buggy firmware this separation doesn't seem to work at all, both selects the same entry).

So this code should be installed in the ESP's VBR sector where the Boot Catalog points to and which is only executed when booted under BIOS in El Torito mode. This code hooks INT 13h and emulates a secondary HDD with 512 bytes sectors. It then loads the very first MBR sector of the CDROM to 7C00h and jumps to it. The MBR code then works as usual, it doesn't have to care about different sector sizes. This solution works with any kinds of boot loader.

Summary:
- Normal flow: BIOS loads MBR -> MBR parses partitioning table loads VBR
- This hack: BIOS parses Boot Catalog loads VBR -> VBR loads MBR (and after that business as usual)

Limitations:
- requires 80386 at least (beacuse I was lazy and used edx to store the LBA)
- limited to 32-bit LBA addresses (not an issue with CDROMs / DVDs)

cdemu_x86.asm
Code:
; cdemu_x86.asm
; Copyright (C) 2024 bzt, MIT license
;
;  Memory layout on handover:
;        0h -   400h    IVT (must be preserved)
;      400h -  7C00h    stack
;     7C00h -  7E00h    we're loaded here, also original MBR loaded here
;    9A000h - 9A200h    our relocated TSR code
;    9A200h - 9AA00h    CDROM sector cache

SEG         equ         09A00h
            ORG         0h
            USE16

virtual at 0
lbapacket.size: dw      ?
lbapacket.count:dw      ?
lbapacket.addr0:dw      ?
lbapacket.addr1:dw      ?
lbapacket.sect0:dw      ?
lbapacket.sect1:dw      ?
lbapacket.sect2:dw      ?
lbapacket.sect3:dw      ?
end virtual

;*********************************************************************
;*                          data area                                *
;*********************************************************************
            ;could be 0:7C00 or 07C0:0 as well depending on BIOS
lbapacket:  jmp         short @f                ; mandatory jump (magic)
            nop
            db          13 dup 0
origip:     dw          0
origcs:     dw          0
save:       dd          0
drive:      dw          0
            db          060h-($-$$) dup 0       ; skip over FAT32 BPB

;*********************************************************************
;*                             code                                  *
;*********************************************************************
@@:         cli
            cld
            xor         ax, ax
            mov         ss, ax
            mov         sp, 07C00h
            ;find our position in memory.
            push        cs
            pop         ds
            call        @f
@@:         pop         si
            sub         si, @b-lbapacket
            ;---- relocate ourself to 09A00:0000 ----
            mov         ax, SEG
            mov         es, ax
            xor         di, di
            mov         cx, 200h
            repnz       movsw
            push        es
            pop         ds
            jmp         SEG:.start
            ;clear and reuse BPB data area
.start:     xor         di, di
            xor         ax, ax
            mov         cx, 30h
            repnz       stosw
            mov         byte [drive], dl        ; save CDROM's drive code
            xor         di, di
            mov         al, 16                  ; .size
            stosw
            mov         al, 1                   ; .count
            stosw
            mov         ax, cache               ; .addr0
            push        ax
            stosw
            mov         ax, es                  ; .addr1
            stosw
            ;load the original MBR (plus 3 more sectors) into cache
            mov         ah, byte 42h
            xor         si, si
            int         13h
            ;copy the first 512 bytes of cache to 0:7C00h
            pop         si
            mov         di, sp
            push        es
            xor         ax, ax
            mov         es, ax
            mov         cx, 100h
            repnz       movsw
            pop         es
            ;---- install TSR, hook INT 13h ----
            mov         di, origip
            xor         ax, ax
            mov         ds, ax
            mov         si, 13h * 4
            mov         ax, word[si]
            stosw
            mov         word[si], tsr
            add         si, 2
            mov         ax, word[si]
            stosw
            mov         ax, es
            mov         word[si], ax
            ;---- arrange environment and jump to MBR code ----
            xor         ax, ax
            mov         es, ax
            mov         bx, ax
            mov         cx, ax
            mov         dx, ax
            mov         si, ax
            mov         di, ax
            mov         dl, 81h                 ; secondary BIOS HDD
            mov         ax, 0AA55h
            jmp         0:7C00h

            ;---- emulate 2048 bytes CDROM sectors as 512 bytes HDD sectors ----
tsr:        ;pass through all non "extended read on secondary HDD" calls
            cmp         ah, 42h
            jne         @f
            cmp         dl, 81h
            je          .emulate
@@:         jmp         dword[cs:origip]
.emulate:   push        es
            push        ds
            push        cx
            push        dx
            push        si
            ;read the lba packet we're servicing into registers
            mov         di, word [si + lbapacket.addr0]
            mov         ax, word [si + lbapacket.addr1]
            mov         es, ax
            xor         bh, bh
            mov         bl, byte [si + lbapacket.count]
            mov         edx, dword [si + lbapacket.sect0]
            push        cs
            pop         ds
            or          bl, bl
            jz          .none
            ;---- for each sector ----
.next:      ;do we have the relevant 2048 bytes sector cached?
            mov         eax, edx
            shr         eax, 2
            cmp         eax, dword[lbapacket.sect0]
            je          @f
            ;no, read it in from CDROM
            push        es
            push        ds
            push        di
            push        bx
            mov         dword[save], edx
            ;call original ISR
            mov         dword[lbapacket.sect0], eax
            mov         dl, byte[drive]
            mov         ax, 4200h
            xor         si, si
            clc
            pushf
            call        dword[origip]
            mov         edx, dword[save]
            pop         bx
            pop         di
            pop         ds
            pop         es
            jc          .end
            or          ah, ah
            jnz         .end
            ;copy 512 bytes from cache to caller's buffer
@@:         mov         si, dx
            and         si, 3
            shl         si, 9
            add         si, cache
            mov         cx, 100h
            repnz       movsw
            ;adjust to next sector
            inc         edx
            inc         bh
            dec         bl
            or          bl, bl
            jnz         .next
            ;---- finished ----
.none:      xor         ax, ax
            clc
.end:       pop         si
            pop         dx
            pop         cx
            pop         ds
            pop         es
            ;update the sector count in caller's lba packet
            mov         byte[si + lbapacket.count], bh
            mov         bx, 0AA55h
            iret

            ;padding (check if our code fits into 510 bytes)
            db          01FEh-($-$$) dup 0
            db          55h, 0AAh               ; mandatory magic bytes

;*********************************************************************
;*                           bss area                                *
;*********************************************************************
cache:      db          2048 dup ?              ; CDROM sector cache
    

Hope this going to be useful,
bzt
Post 17 Oct 2024, 18:42
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.