flat assembler
Message board for the users of flat assembler.

Index > OS Construction > Booting from Minix3 File System

Author
Thread Post new topic Reply to topic
bzt



Joined: 09 Nov 2018
Posts: 84
bzt 29 Mar 2025, 22:40
I got fed up that everybody is struggling with FAT when there's a much better alternative, so some time ago I've created a repository with detailed documentation and code examples on the Minix3 File System. Due to that, recently someone asked me how to boot from MFS (because Minix3 is using the NetBSD's boot loader, which is terrible and not user friendly and they could not find any other example, just FAT boot sectors). Turned out to be a fair question, obviously no existing boot code examples were able to load files from MFS volumes...

So I've decided to add a MFS boot sector example to my repo, written in flat assembler. This code can boot any arbitrary file (a kernel or stage2 loader executable) from a Minix3 File System, it autodetects the format (ELF, PE/COFF, a.out), loads its segments and sets up environment accordingly (either protected mode or long mode). Because of this sophistication, the executable could be compiled from any high-level language (C, Ada, Rust, Go, whatever), but flat assembler works fine as well of course.
Code:
;
;  https://gitlab.com/bztsrc/minix3fs
;
;  Copyright (C) 2025 bzt, MIT license
;
;  Permission is hereby granted, free of charge, to any person obtaining a copy
;  of this software and associated documentation files (the "Software"), to
;  deal in the Software without restriction, including without limitation the
;  rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
;  sell copies of the Software, and to permit persons to whom the Software is
;  furnished to do so, subject to the following conditions:
;
;  The above copyright notice and this permission notice shall be included in
;  all copies or substantial portions of the Software.
;
;  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
;  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
;  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL ANY
;  DEVELOPER OR DISTRIBUTOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
;  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
;  IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
;
;  @brief example boot loader for the Minix3 File System
;
;  By default, this code is a Master Boot Record, to make it work as a Volume Boot
;  Record, just set the partition's starting LBA in "partition_lba" and that's it.
;  It boots an arbitrary statically linked executable file from the file system.
;
;  Supported executable formats (autodetected in run-time):
;   - ELF 32 bit: started in protected mode
;   - ELF 64 bit: started in long mode
;   - PE/COFF 32 bit: started in protected mode
;   - PE/COFF 64 bit: started in long mode
;   - a.out 32 bit: started in protected mode
;
;  Limitations:
;   - no relocations nor dynamic linking, the executable must be statically linked
;   - all segment descriptors of the executable must fit in the first block (typically 4K)
;   - its segments can be placed in 64K to 640K (conventional RAM) or above 1M (normal RAM)
;   - only one indirection level supported (up to 4M with 4K blocks, up to 237M with 32K)
;   - for long mode executable, first 3G of RAM is identity mapped (regardless to RAM size).
;
;  Memory layout on handover:
;        0h -    400h   IVT (must be preserved)
;      400h -    4FFh   BDA (must be preserved)
;      4FFh -    500h   BIOS boot drive code
;      500h -    51Ch   BIOS LBA packet and other bss variables
;      51Ch -   7C00h   file's block list (zone numbers), stack from top
;     1000h -   6000h   paging tables (only exists if the executable is 64 bit)
;     7C00h -   7E00h   1st sector (boot loader code and data, later segments list)
;     7E00h -   8000h   2nd sector (boot loader code and data)
;     8000h -  10000h   temporary disk block buffer (32K)
;    10000h -  9A000h   free, to be used by segments (64K to 640K)
;    9A000h - 100000h   EBDA and BIOS ROM (must be preserved)
;   100000h -      ?    free, to be used by segments (above 1M to top of RAM)
;

;*********************************************************************
;*                            macros                                 *
;*********************************************************************

; uninitialized variables
lba_packet        equ   0500h
block_size        equ   0510h
entry_point       equ   0514h
numseg            equ   0518h
longmode          equ   0519h
zones             equ   051Ch
segs              equ   7C00h
buffer            equ   8000h

; Minix3 File System superblock
virtual at buffer
sb.s_ninodes:     dd    ?
sb.reserved0:     dw    ?
sb.s_imap_blocks: dw    ?
sb.s_zmap_blocks: dw    ?
sb.reserved1:     db    10 dup ?
sb.s_zones:       dd    ?
sb.s_magic:       dw    ?
sb.reserved2:     dw    ?
sb.s_block_size:  dw    ?
sb.s_disk_version:dw    ?
end virtual

; Minix3 File System inode
virtual at 0
inode.i_mode      dw    ?
inode.i_nlinks:   dw    ?
inode.i_uid:      dw    ?
inode.i_gid:      dw    ?
inode.i_size:     dd    ?
inode.i_atime:    dd    ?
inode.i_mtime:    dd    ?
inode.i_ctime:    dd    ?
inode.i_zones:    dd    7 dup ?     ; direct block numbers
inode.i_indirect: dd    ?           ; indirect
inode.i_double:   dd    ?           ; double indirect
inode.i_triple:   dd    ?           ; triple indirect
end virtual

; ELF format
ELF_MAGIC         equ   457Fh
PT_LOAD           equ   1
; 32 bit
virtual at 0
ehdr32.e_ident:   db    16 dup ?    ; elf header
ehdr32.e_type:    dw    ?
ehdr32.e_machine: dw    ?
ehdr32.e_version: dd    ?
ehdr32.e_entry:   dd    ?
ehdr32.e_phoff:   dd    ?
ehdr32.e_shoff:   dd    ?
ehdr32.e_flags:   dd    ?
ehdr32.e_ehsize:  dw    ?
ehdr32.e_phentsize: dw  ?
ehdr32.e_phnum:   dw    ?
ehdr32.e_shentsize: dw  ?
ehdr32.e_shnum:   dw    ?
ehdr32.e_shstrndx: dw   ?
end virtual
virtual at 0
phdr32.p_type:    dd    ?           ; program header
phdr32.p_offset:  dd    ?
phdr32.p_vaddr:   dd    ?
phdr32.p_paddr:   dd    ?
phdr32.p_filesz:  dd    ?
phdr32.p_memsz:   dd    ?
phdr32.p_align:   dd    ?
end virtual
; 64 bit
virtual at 0
ehdr64.e_ident:   db    16 dup ?    ; elf header
ehdr64.e_type:    dw    ?
ehdr64.e_machine: dw    ?
ehdr64.e_version: dd    ?
ehdr64.e_entry:   dq    ?
ehdr64.e_phoff:   dq    ?
ehdr64.e_shoff:   dq    ?
ehdr64.e_flags:   dd    ?
ehdr64.e_ehsize:  dw    ?
ehdr64.e_phentsize: dw  ?
ehdr64.e_phnum:   dw    ?
ehdr64.e_shentsize: dw  ?
ehdr64.e_shnum:   dw    ?
ehdr64.e_shstrndx: dw   ?
end virtual
virtual at 0
phdr64.p_type:    dd    ?           ; program header
phdr64.p_flags:   dd    ?
phdr64.p_offset:  dq    ?
phdr64.p_vaddr:   dq    ?
phdr64.p_paddr:   dq    ?
phdr64.p_filesz:  dq    ?
phdr64.p_memsz:   dq    ?
phdr64.p_align:   dq    ?
end virtual

; PE/COFF format
MZ_MAGIC          equ   5A4Dh
PE_MAGIC          equ   4550h
PE32PLUS_MAGIC    equ   020Bh
virtual at 0
pe.magic:         dd    ?           ; pe header
pe.machine:       dw    ?
pe.sections:      dw    ?
pe.timestamp:     dd    ?
pe.sym_table:     dd    ?
pe.num_sym:       dd    ?
pe.opt_hdr_size:  dw    ?
pe.flags:         dw    ?
pe.file_type:     dw    ?
pe.ld_ver:        dw    ?
pe.text_size:     dd    ?
pe.data_size:     dd    ?
pe.bss_size:      dd    ?
pe.entry_point:   dd    ?
pe.code_base:     dd    ?
pe.data_base:     dd    ?
pe.img_base:      dd    ?
end virtual
virtual at 0
pesec.name:       dq    ?           ; section
pesec.vsiz:       dd    ?
pesec.vaddr:      dd    ?
pesec.rsiz:       dd    ?
pesec.raddr:      dd    ?
pesec.reloc:      dd    ?
pesec.ln:         dd    ?
pesec.nreloc:     dw    ?
pesec.nln:        dw    ?
pesec.chr:        dd    ?
end virtual

; struct exec (a.out) format
AOUT_MAGIC        equ   0801h       ; NMAGIC, 0410 in big-endian
virtual at 0
aout.a_midmag:    dd    ?           ; magic (network byte order)
aout.a_text:      dd    ?           ; text segment size
aout.a_data:      dd    ?           ; data segment size
aout.a_bss:       dd    ?           ; bss segment size
aout.a_syms:      dd    ?           ; symbol section size
aout.a_entry:     dd    ?           ; entry point
aout.a_trsize:    dd    ?           ; text relocation size
aout.a_drsize:    dd    ?           ; data relocation size
end virtual

; BIOS lba packet
virtual at lba_packet
lbapacket.size:   dw    ?
lbapacket.count:  dw    ?
lbapacket.addr0:  dw    ?
lbapacket.addr1:  dw    ?
lbapacket.sect0:  dd    ?
lbapacket.sect1:  dd    ?
end virtual

; format-independent file segment descriptor records
virtual at 0
seg.offs:         dd    ?           ; byte offset in file
seg.addr:         dd    ?           ; memory address to load to
seg.size:         dd    ?           ; size in file
seg.bss:          dd    ?           ; bss size
end virtual

            ORG         07C00h
            USE16

;*********************************************************************
;*                          1st sector                               *
;*********************************************************************

loader:     jmp         short @f                ; mandatory jump (magic for some BIOS)
            nop
            db          "MFS"
            ;---- set up environment ----
@@:         cli
            cld
            mov         al, 0FFh                ; disable PIC
            out         021h, al
            out         0A1h, al
            in          al, 70h                 ; disable NMI
            or          al, 80h
            out         70h, al
            xor         ax, ax                  ; set up segment registers and stack
            mov         ss, ax
            mov         ds, ax
            mov         es, ax
            mov         sp, 07C00h
            jmp         0:@f
            ;---- read in the remaining part of the boot loader and the MFS superblock ----
@@:         mov         si, lba_packet
            mov         di, si
            mov         byte [di - 1], dl
            xor         ah, ah
            mov         al, 16                  ; size
            stosw
            mov         al, 2                   ; count
            stosw
            mov         ax, 07E00h              ; addr0, load to 0000:7E00h
            stosw
            xor         ax, ax                  ; addr1
            stosw
            mov         eax, dword [partition_lba]
            inc         eax                     ; sect0, LBA 1
            stosd
            xor         ax, ax
            stosw                               ; sect1
            stosw
            push        si
            mov         ah, 42h
            int         13h
            pop         si

            ;---- parse the MFS superblock ----
            ; block size
            xor         eax, eax
            mov         word [numseg], ax
            mov         ax, word [sb.s_block_size]
            mov         dword [block_size], eax
            shr         ax, 9
            mov         word [lbapacket.count], ax
            ; get inode table's starting sector
            mov         al, 2
            add         ax, word [sb.s_imap_blocks]
            add         ax, word [sb.s_zmap_blocks]
            call        blk2sec
            ; adjust by inode structure's offset
            mov         eax, dword [s_inode_to_boot]
            dec         eax
            shl         eax, 6                  ; offset = (s_inode_to_boot - 1) * sizeof(inode_t)
            mov         ebx, eax
            shr         eax, 9                  ; eax = offset / sector size
            add         dword [lbapacket.sect0], eax
            mov         byte [lbapacket.addr0 + 1], 80h ; load to 0000:8000h
            ; load the sector where the inode resides
            push        si
            push        bx
            mov         ah, 42h
            int         13h
            xor         esi, esi
            pop         si                      ; was bx
            ; offset of inode structure inside the sector
            and         si, 511

            ;---- parse the MFS inode ----
            ; copy direct block numbers from inode to zones[0..6]
            add         si, buffer + inode.i_zones
            mov         di, zones
            mov         cx, 7
            repnz       movsd
            ; load and copy indirect zones (if any)
            lodsd
            pop         si
            or          eax, eax
            jz          @f
            call        blk2sec
            ; load indirect block
            push        si
            push        di
            mov         ah, 42h
            int         13h
            pop         di
            ; copy block numbers to zones[7..7606]
            mov         si, buffer
            mov         cx, 1DB0h   ; (7C00h - 51Ch) / 4 - some stack
            repnz       movsd
            pop         si
@@:         ; load the first block of the file (needed for autodetection)
            mov         eax, dword [zones]
            call        blk2sec
            mov         ah, 42h
            int         13h
            ;---- enable protmode ----
            mov         ax, 2401h               ; enable A20
            int         15h
            lgdt        [GDT_value]             ; prot mode descriptors
            mov         eax, cr0
            or          al, 1
            mov         cr0, eax                ; prot mode enable flag
            jmp         16:@f                   ; reload segments
            USE32
@@:         mov         ax, 24
            mov         ds, ax
            mov         es, ax

            ;---- parse the executable's header, collect segment descriptor list ----
            mov         edi, segs
            mov         ebx, buffer
            mov         ax, word [ebx]
            xor         ecx, ecx

            ; is it in struct exec (a.out) format?
isaout:     cmp         word [ebx + 2], AOUT_MAGIC          ; this is in network byte order, so big endian
            jne         ispe
            inc         ch                                  ; we have 1 segment
            mov         eax, 32                             ; offs = sizeof(struct exec)
            stosd
            mov         eax, dword [ebx + aout.a_entry]
            mov         dword [entry_point], eax            ; addr (first byte of text segment)
            stosd
            mov         eax, dword [ebx + aout.a_text]      ; size (bytes to load)
            add         eax, dword [ebx + aout.a_data]
            stosd
            mov         eax, dword [ebx + aout.a_bss]       ; bss (bytes to zero out)
            stosd
            jmp         loadsegs

            ; is it in PE/COFF format?
ispe:       cmp         ax, MZ_MAGIC
            jne         iself
            add         ebx, dword [ebx + 0x3c]
            cmp         word [ebx + pe.magic], PE_MAGIC
            jne         die
            mov         ebp, dword [ebx + pe.img_base]
            cmp         word [ebx + pe.file_type], PE32PLUS_MAGIC
            jne         @f
            inc         byte [longmode]
            mov         ebp, dword [ebx + pe.data_base]     ; there's no data base, the two is one 64 bit base
@@:         mov         eax, dword [ebx + pe.entry_point]
            add         eax, ebp
            mov         dword [entry_point], eax
            mov         cl, byte [ebx + pe.sections]        ; number of sections
            add         bx, word [ebx + pe.opt_hdr_size]
            add         bx, 24                              ; ebx now points to sections
.next:      cmp         di, @b - 16                         ; failsafe bound check
            jae         loadsegs
            mov         eax, dword [ebx + pesec.raddr]
            stosd                                           ; offs (in file offset)
            mov         eax, dword [ebx + pesec.vaddr]
            add         eax, ebp
            stosd                                           ; addr (to memory)
            ; unfortunately rsiz can be bigger than vsiz, which would overflow...
            mov         eax, dword [ebx + pesec.rsiz]
            mov         edx, dword [ebx + pesec.vsiz]
            cmp         eax, edx    ; rsiz < vsiz?
            jb          @f
            mov         eax, edx    ; size = vsiz
            xor         edx, edx    ; bss = 0
            jmp         .stor
@@:         sub         edx, eax    ; size = rsiz, bss = vsiz - rsiz
.stor:      stosd                                           ; size (size in file)
            mov         eax, edx
            stosd                                           ; bss (bytes to zero out)
            inc         ch
            add         ebx, 40                             ; go to next section
            dec         cl
            jnz         .next
            jmp         loadsegs
            ; this code never reached, instead execution continues on 2nd sector

            ;---- die function ----
            ; prints an error string and halts processor
die:        mov         esi, errstr
            mov         edi, 0B8000h
            mov         ah, 04fh
@@:         lodsb
            or          al, al
            jz          @f
            stosw
            jmp         @b
@@:         hlt

            ;---- convert block to sectors ----
            ; eax: block number
            USE16
blk2sec:    movzx       ebx, word [lbapacket.count]
            mul         ebx
            add         eax, dword [partition_lba]
            mov         dword [lbapacket.sect0], eax
            mov         dl, byte [lba_packet - 1]
            ret
            USE32

errstr:     db          "ERROR", 0
GDT_value:  dw          GDT_value.end-GDT_value ; value / null descriptor
            dd          GDT_value
            dw          0
            dd          0000FFFFh,00009800h     ;  8 - legacy real cs
            dd          0000FFFFh,00CF9A00h     ; 16 - prot mode cs
            dd          0000FFFFh,008F9200h     ; 24 - prot mode ds
            dd          0000FFFFh,00AF9A00h     ; 32 - long mode cs
            dd          0000FFFFh,00CF9200h     ; 40 - long mode ds
.end:       db          01FAh-($-$$) dup 0
            ;---- in case the Minix3 File System is on a partition ----
partition_lba: dd       0
            db          55h, 0AAh               ; mandatory magic bytes

;*********************************************************************
;*                          2nd sector                               *
;*********************************************************************

            ; is it in ELF format?
iself:      cmp         ax, ELF_MAGIC
            jne         die
            mov         eax, dword [ebx + ehdr32.e_entry]
            mov         dword [entry_point], eax
            cmp         byte [ebx + ehdr32.e_ident + 4], 2  ; ELFCLASS64?
            je          iself64
            ; 32 bit
            mov         cl, byte [ebx + ehdr32.e_phnum]
            mov         dx, word [ebx + ehdr32.e_phentsize]
            add         bx, word [ebx + ehdr32.e_phoff]
.next32:    cmp         edi, die - 16                       ; failsafe bound check
            jae         loadsegs
            cmp         byte [ebx + phdr32.p_type], PT_LOAD
            jne         @f
            mov         eax, dword [ebx + phdr32.p_offset]
            stosd                                           ; offs (in file offset)
            mov         eax, dword [ebx + phdr32.p_vaddr]
            stosd                                           ; addr (to memory)
            mov         eax, dword [ebx + phdr32.p_filesz]
            stosd                                           ; size (size in file)
            mov         eax, dword [ebx + phdr32.p_memsz]
            sub         eax, dword [ebx + phdr32.p_filesz]
            stosd                                           ; bss
            inc         ch
@@:         add         bx, dx                              ; go to next program header
            dec         cl
            jnz         .next32
            jmp         loadsegs
            ; 64 bit
iself64:    inc         byte [longmode]
            mov         cl, byte [ebx + ehdr64.e_phnum]
            mov         dx, word [ebx + ehdr64.e_phentsize]
            add         bx, word [ebx + ehdr64.e_phoff]
.next64:    cmp         edi, die - 16                       ; failsafe bound check
            jae         loadsegs
            cmp         byte [ebx + phdr64.p_type], PT_LOAD
            jne         @f
            mov         eax, dword [ebx + phdr64.p_offset]
            stosd                                           ; offs (in file offset)
            mov         eax, dword [ebx + phdr64.p_vaddr]
            stosd                                           ; addr (to memory)
            mov         eax, dword [ebx + phdr64.p_filesz]
            stosd                                           ; size (size in file)
            mov         eax, dword [ebx + phdr64.p_memsz]
            sub         eax, dword [ebx + phdr64.p_filesz]
            stosd                                           ; bss (bytes to zero out)
            inc         ch
@@:         add         bx, dx                              ; go to next program header
            dec         cl
            jnz         .next64

            ;---- load the executable's segments ----
            ; numseg: number of segments
            ; segs: segment descriptor records, each 16 bytes, offs + addr + size + bss
loadsegs:   or          ch, ch
            jz          die
            mov         byte [numseg], ch
            mov         esi, segs
            ; there are three possible scenarios here for every segment:
            ;  1. first iteration, maybe edx != 0, ecx <= block size (maybe ecx = filesz)
            ;  2. middle iteration, edx = 0, ecx = block size (maybe called multiple times)
            ;  3. last iteration, edx = 0, ecx < block size (if filesz % block size != 0)
            ; (+1 special case, when filesz < block size also taken care of in step 1)
.nextseg:   xor         edx, edx
            mov         eax, dword [esi + seg.offs]
            mov         ecx, dword [block_size]
            ; file offset to file block number (eax) and block offset (edx)
            div         ecx
            sub         ecx, edx
            ; check size (ecx = block size - block offset) is not bigger than segment filesz
            mov         ebx, dword [esi + seg.size]
            cmp         ecx, ebx
            jbe         @f
            mov         ecx, ebx
            ; convert file block number to volume block number
@@:         shl         eax, 2
            add         eax, zones
            mov         eax, dword [eax]  ; eax = zones[eax]
            mov         edi, dword [esi + seg.addr]
            ; load the next block from the segment
            ; eax: volume block number
            ; edx: offset within the block
            ; edi: destination buffer
            ; ecx: number of bytes to load
            or          ecx, ecx
            jz          .nofilesz
            push        ecx
            push        esi
            push        edx
            push        edi
            push        ecx
            movzx       ebx, word [lbapacket.count]
            mul         ebx
            add         eax, dword [partition_lba]
            mov         dword [lbapacket.sect0], eax
            jmp         8:@f
            USE16
@@:         mov         eax, CR0
            and         al, 0FEh          ; switching back to real mode
            mov         CR0, eax
            jmp         0:@f
@@:         xor         ax, ax
            mov         ds, ax
            mov         es, ax
            mov         ss, ax
            mov         si, lba_packet    ; call BIOS
            mov         dl, byte [si - 1]
            mov         ah, 42h
            int         13h
            mov         eax, cr0          ; enable protected mode
            or          al, 1
            mov         cr0, eax
            jmp         16:@f
            USE32
@@:         mov         ax, 24
            mov         ds, ax
            mov         es, ax
            mov         ss, ax
            pop         ecx
            pop         edi
            pop         esi
            ; copy from temporary buffer to final position
            add         esi, buffer
            repnz       movsb
            pop         esi
            pop         ecx
            ; adjust offsets (ecx is no bigger than block size)
            add         dword [esi + seg.offs], ecx
            add         dword [esi + seg.addr], ecx
            sub         dword [esi + seg.size], ecx
            ; repeat if there's more blocks left to load in the segment
            cmp         dword [esi + seg.size], 0
            jnz         .nextseg                            ; load next block of segment
            ; finished with all blocks, edi now points right after the loaded data
.nofilesz:  mov         ecx, dword [esi + seg.bss]
            or          ecx, ecx                            ; clear bss area
            jz          @f
            xor         al, al
            repnz       stosb
@@:         add         esi, 16                             ; go to first block of next segment
            dec         byte [numseg]
            jnz         .nextseg

            ;---- pass control to executable's entry point ----
            cmp         byte [longmode], 0
            jnz         @f
            jmp         dword [entry_point]
@@:         ;---- enable longmode ----
            xor         eax, eax
            mov         ah, 010h
            mov         cr3, eax
            ; we only map 3G here
            mov         edx, eax                ; PML4
            mov         ebx, eax
            xor         eax, eax
            mov         dword [ebx], 02003h     ; pointer to 2M PDPE
            mov         dword [ebx + 4], eax
            add         ebx, edx                ; 2M PDPE
            mov         dword [ebx], 03003h
            mov         dword [ebx + 4], eax
            mov         dword [ebx + 8], 04003h
            mov         dword [ebx + 12], eax
            mov         dword [ebx + 16], 05003h
            mov         dword [ebx + 20], eax
            add         ebx, edx                ; 2M PDE
            mov         cx, 3 * 512
            mov         al, 83h
@@:         mov         dword [ebx], eax
            mov         dword [ebx + 4], 0
            add         ebx, 8
            add         eax, 2*1024*1024
            dec         cx
            jnz         @b
            xor         eax, eax
            mov         al, 0E0h                ; set PAE, MCE, PGE; clear everything else
            mov         cr4, eax
            mov         ecx, 0C0000080h         ; EFER MSR
            rdmsr
            bts         eax, 8                  ; enable long mode page tables
            wrmsr
            mov         eax, cr0
            xor         cl, cl
            or          eax, ecx
            btc         eax, 16                 ; clear WP
            mov         cr0, eax                ; enable paging with cache disabled (set PE, CD)
            lgdt        [GDT_value]             ; read 80 bit address (16+64)
            jmp         32:@f
            USE64
@@:         xor         rax, rax                ; load long mode segments
            mov         al, 40
            mov         ds, ax
            mov         es, ax
            mov         ss, ax
            mov         eax, dword [entry_point]
            jmp         rax

            db          1020-($-$$) dup 0
            ;---- file to be booted, set by the mfsboot installer tool ----
s_inode_to_boot: dd     0
            ;---- this is followed directly by the Minix3 File System superblock on disk ----

;*********************************************************************
;*   the power of fasm macros: generate C literals from the binary   *
;*********************************************************************
display "/* Generated by fasm from boot.asm */", 10
first = 1
repeat $-loader
    load b byte from (loader+%-1)
    if first
      first = 0
    else
      display ','
    end if
    display '0', 'x'
    repeat 2
        d = '0' + b shr (8-%*4) and 0Fh
        if d > '9'
            d = d + 'A'-'9'-1
        end if
        display d
    end repeat
end repeat
display 10
    
The source contains lots and lots of comments, but if you have any questions or you find a bug, let me know!

Hint: there's also a dependency-free, multiplatform command line tool mfsboot in the repo, which installs this boot sector into a Minix3 File System's superblock. There's no more component to this loader, it only consist of this boot sector.

Cheers,
bzt
Post 29 Mar 2025, 22:40
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.