Index: branches/ErmaC/Enoch/i386/libsaio/disk.c =================================================================== --- branches/ErmaC/Enoch/i386/libsaio/disk.c (revision 2497) +++ branches/ErmaC/Enoch/i386/libsaio/disk.c (revision 2498) @@ -1478,16 +1478,31 @@ { case FDISK_NTFS: bvr = newGPTBVRef(biosdev, gptID, gptMap->ent_lba_start, gptMap, - 0, 0, 0, 0, 0, 0, NTFSGetDescription, + 0, 0, 0, 0, 0, NTFSGetUUID, NTFSGetDescription, (BVFree)free, 0, kBIOSDevTypeHardDrive, 0); break; case FDISK_LINUX: bvr = newGPTBVRef(biosdev, gptID, gptMap->ent_lba_start, gptMap, - 0, 0, 0, 0, 0, 0, EX2GetDescription, + 0, 0, 0, 0, 0, EX2GetUUID, EX2GetDescription, (BVFree)free, 0, kBIOSDevTypeHardDrive, 0); break; + case FDISK_FAT32: + case FDISK_DOS12: + case FDISK_DOS16B: + bvr = newGPTBVRef(biosdev, gptID, gptMap->ent_lba_start, gptMap, + MSDOSInitPartition, + MSDOSLoadFile, + MSDOSReadFile, + MSDOSGetDirEntry, + MSDOSGetFileBlock, + MSDOSGetUUID, + MSDOSGetDescription, + MSDOSFree, + 0, kBIOSDevTypeHardDrive, 0); + break; + default: bvr = newGPTBVRef(biosdev, gptID, gptMap->ent_lba_start, gptMap, 0, 0, 0, 0, 0, 0, 0, Index: branches/ErmaC/Enoch/i386/boot0/boot0xg.s =================================================================== --- branches/ErmaC/Enoch/i386/boot0/boot0xg.s (revision 0) +++ branches/ErmaC/Enoch/i386/boot0/boot0xg.s (revision 2498) @@ -0,0 +1,818 @@ +; Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved. +; +; @APPLE_LICENSE_HEADER_START@ +; +; Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights +; Reserved. This file contains Original Code and/or Modifications of +; Original Code as defined in and that are subject to the Apple Public +; Source License Version 2.0 (the "License"). You may not use this file +; except in compliance with the License. Please obtain a copy of the +; License at http://www.apple.com/publicsource and read it before using +; this file. +; +; The Original Code and all software distributed under the License are +; distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER +; EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +; INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +; FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the +; License for the specific language governing rights and limitations +; under the License. +; +; @APPLE_LICENSE_HEADER_END@ +; +; Boot Loader: boot0xg +; +; A small boot sector program written in x86 assembly whose only +; responsibility is to locate the active partition, load the +; partition booter into memory, and jump to the booter's entry point. +; It leaves the boot drive in DL and a pointer to the partition entry in SI. +; +; This boot loader must be placed in the Master Boot Record. +; +; In order to coexist with a fdisk partition table (64 bytes), and +; leave room for a two byte signature (0xAA55) in the end, boot0xg is +; restricted to 440 bytes (512 - 64 - 2 - 6). If boot0xg did not have to +; live in the MBR, then we would have 510 bytes to work with. +; +; boot0xg is always loaded by the BIOS or another booter to 0:7C00h. +; +; This code is written for the NASM assembler. +; nasm -f bin -DCONFIG_BOOT0_DEBUG=? -DCONFIG_BOOT0_VERBOSE=? boot0xg.s -o boot0xg + +; +; This version of boot0xg implements hybrid GUID/MBR partition scheme support +; +; Written by Tam‡s Kos‡rszky on 2008-03-10 +; +; Turbo added EFI System Partition boot support +; +; Added KillerJK's switchPass2 modifications +; + +; +; Set to 1 to enable obscure debug messages. +; +DEBUG EQU CONFIG_BOOT0_DEBUG + +; +; Set to 1 to enable verbose mode +; +VERBOSE EQU CONFIG_BOOT0_VERBOSE + +; +; Various constants. +; +kBoot0Segment EQU 0x0000 +kBoot0Stack EQU 0xFFF0 ; boot0 stack pointer +kBoot0LoadAddr EQU 0x7C00 ; boot0 load address +kBoot0RelocAddr EQU 0xE000 ; boot0 relocated address + +kMBRBuffer EQU 0x1000 ; MBR buffer address +kLBA1Buffer EQU 0x1200 ; LBA1 - GPT Partition Table Header buffer address +kGPTABuffer EQU 0x1400 ; GUID Partition Entry Array buffer address + +kPartTableOffset EQU 0x1be +kMBRPartTable EQU kMBRBuffer + kPartTableOffset + +kSectorBytes EQU 512 ; sector size in bytes +kBootSignature EQU 0xAA55 ; boot sector signature +kHFSPSignature EQU 'H+' ; HFS+ volume signature +kHFSPCaseSignature EQU 'HX' ; HFS+ volume case-sensitive signature +kFAT32BootCodeOffset EQU 0x5a ; offset of boot code in FAT32 boot sector +kBoot1FAT32Magic EQU 'BO' ; Magic string to detect our boot1f32 code + + +kGPTSignatureLow EQU 'EFI ' ; GUID Partition Table Header Signature +kGPTSignatureHigh EQU 'PART' +kGUIDLastDwordOffs EQU 12 ; last 4 byte offset of a GUID + +kPartCount EQU 4 ; number of paritions per table +kPartTypeHFS EQU 0xaf ; HFS+ Filesystem type +kPartTypeABHFS EQU 0xab ; Apple_Boot partition +kPartTypePMBR EQU 0xee ; On all GUID Partition Table disks a Protective MBR (PMBR) + ; in LBA 0 (that is, the first block) precedes the + ; GUID Partition Table Header to maintain compatibility + ; with existing tools that do not understand GPT partition structures. + ; The Protective MBR has the same format as a legacy MBR + ; and contains one partition entry with an OSType set to 0xEE + ; reserving the entire space used on the disk by the GPT partitions, + ; including all headers. + +kPartActive EQU 0x80 ; active flag enabled +kPartInactive EQU 0x00 ; active flag disabled +kHFSGUID EQU 0x48465300 ; first 4 bytes of Apple HFS Partition Type GUID. +kAppleGUID EQU 0xACEC4365 ; last 4 bytes of Apple type GUIDs. +kMicrosoftGUID EQU 0xC79926B7 ; last 4 bytes of Microsoft Basic Data Partition Type GUID: + ; EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 + +%ifdef FLOPPY +kDriveNumber EQU 0x00 +%else +kDriveNumber EQU 0x80 +%endif + +; +; Format of fdisk partition entry. +; +; The symbol 'part_size' is automatically defined as an `EQU' +; giving the size of the structure. +; + struc part +.bootid resb 1 ; bootable or not +.head resb 1 ; starting head, sector, cylinder +.sect resb 1 ; +.cyl resb 1 ; +.type resb 1 ; partition type +.endhead resb 1 ; ending head, sector, cylinder +.endsect resb 1 ; +.endcyl resb 1 ; +.lba resd 1 ; starting lba +.sectors resd 1 ; size in sectors + endstruc + +; +; Format of GPT Partition Table Header +; + struc gpth +.Signature resb 8 +.Revision resb 4 +.HeaderSize resb 4 +.HeaderCRC32 resb 4 +.Reserved resb 4 +.MyLBA resb 8 +.AlternateLBA resb 8 +.FirstUsableLBA resb 8 +.LastUsableLBA resb 8 +.DiskGUID resb 16 +.PartitionEntryLBA resb 8 +.NumberOfPartitionEntries resb 4 +.SizeOfPartitionEntry resb 4 +.PartitionEntryArrayCRC32 resb 4 + endstruc + +; +; Format of GUID Partition Entry Array +; + struc gpta +.PartitionTypeGUID resb 16 +.UniquePartitionGUID resb 16 +.StartingLBA resb 8 +.EndingLBA resb 8 +.Attributes resb 8 +.PartitionName resb 72 + endstruc + +; +; Macros. +; +%macro DebugCharMacro 1 + mov al, %1 + call print_char +%endmacro + +%macro LogString 1 + mov di, %1 + call log_string +%endmacro + +%if DEBUG +%define DebugChar(x) DebugCharMacro x +%else +%define DebugChar(x) +%endif + +;-------------------------------------------------------------------------- +; Start of text segment. + + SEGMENT .text + + ORG kBoot0RelocAddr + +;-------------------------------------------------------------------------- +; Boot code is loaded at 0:7C00h. +; +start: + ; + ; Set up the stack to grow down from kBoot0Segment:kBoot0Stack. + ; Interrupts should be off while the stack is being manipulated. + ; + cli ; interrupts off + xor ax, ax ; zero ax + mov ss, ax ; ss <- 0 + mov sp, kBoot0Stack ; sp <- top of stack + sti ; reenable interrupts + + mov es, ax ; es <- 0 + mov ds, ax ; ds <- 0 + + ; + ; Relocate boot0 code. + ; + mov si, kBoot0LoadAddr ; si <- source + mov di, kBoot0RelocAddr ; di <- destination + cld ; auto-increment SI and/or DI registers + mov cx, kSectorBytes/2 ; copy 256 words + repnz movsw ; repeat string move (word) operation + + ; + ; Code relocated, jump to start_reloc in relocated location. + ; + jmp kBoot0Segment:start_reloc + +;-------------------------------------------------------------------------- +; Start execution from the relocated location. +; +start_reloc: + + DebugChar('>') + +%if DEBUG + mov al, dl + call print_hex +%endif + + ; + ; Since this code may not always reside in the MBR, always start by + ; loading the MBR to kMBRBuffer and LBA1 to kGPTBuffer. + ; + + xor eax, eax + mov [my_lba], eax ; store LBA sector 0 for read_lba function + mov al, 2 ; load two sectors: MBR and LBA1 + mov bx, kMBRBuffer ; MBR load address + call load + jc error ; MBR load error + + ; + ; Look for the booter partition in the MBR partition table, + ; which is at offset kMBRPartTable. + ; + mov si, kMBRPartTable ; pointer to partition table + call find_boot ; will not return on success + +error: + LogString(boot_error_str) + +hang: + hlt + jmp hang + + +;-------------------------------------------------------------------------- +; Find the active (boot) partition and load the booter from the partition. +; +; Arguments: +; DL = drive number (0x80 + unit number) +; SI = pointer to fdisk partition table. +; +; Clobber list: +; EAX, BX, EBP +; +find_boot: + + ; + ; Check for boot block signature 0xAA55 following the 4 partition + ; entries. + ; + cmp WORD [si + part_size * kPartCount], kBootSignature + jne .exit ; boot signature not found. + + xor bx, bx ; BL will be set to 1 later in case of + ; Protective MBR has been found + + inc bh ; BH = 1. Giving a chance for a second pass + ; to boot an inactive but boot1h aware HFS+ partition + ; by scanning the MBR partition entries again. + +.start_scan: + mov cx, kPartCount ; number of partition entries per table + +.loop: + + ; + ; First scan through the partition table looking for the active + ; partition. + ; +%if DEBUG + mov al, [si + part.type] ; print partition type + call print_hex +%endif + + mov eax, [si + part.lba] ; save starting LBA of current + mov [my_lba], eax ; MBR partition entry for read_lba function + cmp BYTE [si + part.type], 0 ; unused partition? + je .continue ; skip to next entry + cmp BYTE [si + part.type], kPartTypePMBR ; check for Protective MBR + jne .testPass + + mov BYTE [si + part.bootid], kPartInactive ; found Protective MBR + ; clear active flag to make sure this protective + ; partition won't be used as a bootable partition. + mov bl, 1 ; Assume we can deal with GPT but try to scan + ; later if not found any other bootable partitions. + +.testPass: + cmp bh, 1 + jne .Pass2 + +.Pass1: + cmp BYTE [si + part.bootid], kPartActive ; In pass 1 we are walking on the standard path + ; by trying to hop on the active partition. + jne .continue + xor dh, dh ; Argument for loadBootSector to skip HFS+ partition + ; signature check. + jmp .tryToBoot + +.Pass2: + cmp BYTE [si + part.type], kPartTypeHFS ; In pass 2 we're going to find a HFS+ partition + ; equipped with boot1h in its boot record + ; regardless if it's active or not. + jne .continue + mov dh, 1 ; Argument for loadBootSector to check HFS+ partition signature. + + DebugChar('*') + + ; + ; Found boot partition, read boot sector to memory. + ; + +.tryToBoot: + + call loadBootSector + jne .continue + jmp SHORT initBootLoader + +.continue: + add si, BYTE part_size ; advance SI to next partition entry + loop .loop ; loop through all partition entries + + ; + ; Scanned all partitions but not found any with active flag enabled + ; Anyway if we found a protective MBR before we still have a chance + ; for a possible GPT Header at LBA 1 + ; + dec bl + jnz .switchPass2 ; didn't find Protective MBR before + call checkGPT + +.switchPass2: + ; + ; Switching to Pass 2 + ; try to find a boot1h aware HFS+ MBR partition + ; + dec bh + mov si, kMBRPartTable ; set SI to first entry of MBR Partition table + jz .start_scan ; scan again + +.exit: + ret ; Giving up. + + + ; + ; Jump to partition booter. The drive number is already in register DL. + ; SI is pointing to the modified partition entry. + ; +initBootLoader: + +DebugChar('J') + +%if VERBOSE + LogString(done_str) +%endif + + jmp kBoot0LoadAddr + + ; + ; Found Protective MBR Partition Type: 0xEE + ; Check for 'EFI PART' string at the beginning + ; of LBA1 for possible GPT Table Header + ; +checkGPT: + push bx + + mov di, kLBA1Buffer ; address of GUID Partition Table Header + cmp DWORD [di], kGPTSignatureLow ; looking for 'EFI ' + jne .exit ; not found. Giving up. + cmp DWORD [di + 4], kGPTSignatureHigh ; looking for 'PART' + jne .exit ; not found. Giving up indeed. + mov si, di + + ; + ; Loading GUID Partition Table Array + ; + mov eax, [si + gpth.PartitionEntryLBA] ; starting LBA of GPT Array + mov [my_lba], eax ; save starting LBA for read_lba function + mov cx, [si + gpth.NumberOfPartitionEntries] ; number of GUID Partition Array entries + mov bx, [si + gpth.SizeOfPartitionEntry] ; size of GUID Partition Array entry + + push bx ; push size of GUID Partition entry + + ; + ; Calculating number of sectors we need to read for loading a GPT Array + ; +; push dx ; preserve DX (DL = BIOS drive unit number) +; mov ax, cx ; AX * BX = number of entries * size of one entry +; mul bx ; AX = total byte size of GPT Array +; pop dx ; restore DX +; shr ax, 9 ; convert to sectors + + ; + ; ... or: + ; Current GPT Arrays uses 128 partition entries each 128 bytes long + ; 128 entries * 128 bytes long GPT Array entries / 512 bytes per sector = 32 sectors + ; + mov al, 32 ; maximum sector size of GPT Array (hardcoded method) + + mov bx, kGPTABuffer + push bx ; push address of GPT Array + call load ; read GPT Array + pop si ; SI = address of GPT Array + pop bx ; BX = size of GUID Partition Array entry + jc error + + ; + ; Walk through GUID Partition Table Array + ; and load boot record from first available HFS+ partition. + ; + ; If it has boot signature (0xAA55) then jump to it + ; otherwise skip to next partition. + ; + +%if VERBOSE + LogString(gpt_str) +%endif + +.gpt_loop: + + mov eax, [si + gpta.PartitionTypeGUID + kGUIDLastDwordOffs] + + cmp eax, kAppleGUID ; check current GUID Partition for Apple's GUID type + je .gpt_ok + + ; + ; Turbo - also try EFI System Partition + ; + + cmp eax, kMicrosoftGUID ; check current GUID Partition for Microsoft Basic Data Partition GUID type + jne .gpt_continue + +.gpt_ok: + ; + ; Found HFS Partition + ; + + mov eax, [si + gpta.StartingLBA] ; load boot sector from StartingLBA + mov [my_lba], eax + mov dh, 1 ; Argument for loadBootSector to check HFS+ partition signature. + call loadBootSector + jne .gpt_continue ; no boot loader signature + + mov si, kMBRPartTable ; fake the current GUID Partition + mov [si + part.lba], eax ; as MBR style partition for boot1h + mov BYTE [si + part.type], kPartTypeHFS ; with HFS+ filesystem type (0xAF) + jmp SHORT initBootLoader + +.gpt_continue: + + add si, bx ; advance SI to next partition entry + loop .gpt_loop ; loop through all partition entries + +.exit: + pop bx + ret ; no more GUID partitions. Giving up. + + +;-------------------------------------------------------------------------- +; loadBootSector - Load boot sector +; +; Arguments: +; DL = drive number (0x80 + unit number) +; DH = 0 skip HFS+ partition signature checking +; 1 enable HFS+ partition signature checking +; [my_lba] = starting LBA. +; +; Returns: +; ZF = 0 if boot sector hasn't kBootSignature +; 1 if boot sector has kBootSignature +; +loadBootSector: + pusha + + mov al, 3 + mov bx, kBoot0LoadAddr + call load + jc error + + or dh, dh + jz .checkBootSignature + +.checkHFSSignature: + +%if 0 +%if VERBOSE + LogString(test_str) +%endif +%endif + + ; + ; Looking for HFSPlus ('H+') or HFSPlus case-sensitive ('HX') signature. + ; + mov ax, [kBoot0LoadAddr + 2 * kSectorBytes] + cmp ax, kHFSPSignature ; 'H+' + je .checkBootSignature + cmp ax, kHFSPCaseSignature ; 'HX' + je .checkBootSignature + + ; + ; Looking for exFAT signature + ; + mov ax, [kBoot0LoadAddr + 3] + cmp ax, 0x5845 ; 'EX' + jz .checkBootSignature + + ; + ; Looking for boot1f32 magic string. + ; + mov ax, [kBoot0LoadAddr + kFAT32BootCodeOffset] + cmp ax, kBoot1FAT32Magic + jne .exit + +.checkBootSignature: + ; + ; Check for boot block signature 0xAA55 + ; + mov di, bx + cmp WORD [di + kSectorBytes - 2], kBootSignature + +.exit: + + popa + + ret + + +;-------------------------------------------------------------------------- +; load - Load one or more sectors from a partition. +; +; Arguments: +; AL = number of 512-byte sectors to read. +; ES:BX = pointer to where the sectors should be stored. +; DL = drive number (0x80 + unit number) +; [my_lba] = starting LBA. +; +; Returns: +; CF = 0 success +; 1 error +; +load: + push cx + +.ebios: + mov cx, 5 ; load retry count +.ebios_loop: + call read_lba ; use INT13/F42 + jnc .exit + loop .ebios_loop + +.exit: + pop cx + ret + + +;-------------------------------------------------------------------------- +; read_lba - Read sectors from a partition using LBA addressing. +; +; Arguments: +; AL = number of 512-byte sectors to read (valid from 1-127). +; ES:BX = pointer to where the sectors should be stored. +; DL = drive number (0x80 + unit number) +; [my_lba] = starting LBA. +; +; Returns: +; CF = 0 success +; 1 error +; +read_lba: + pushad ; save all registers + mov bp, sp ; save current SP + + ; + ; Create the Disk Address Packet structure for the + ; INT13/F42 (Extended Read Sectors) on the stack. + ; + +; push DWORD 0 ; offset 12, upper 32-bit LBA + push ds ; For sake of saving memory, + push ds ; push DS register, which is 0. + mov ecx, [my_lba] ; offset 8, lower 32-bit LBA + push ecx + push es ; offset 6, memory segment + push bx ; offset 4, memory offset + xor ah, ah ; offset 3, must be 0 + push ax ; offset 2, number of sectors + + ; It pushes 2 bytes with a smaller opcode than if WORD was used + push BYTE 16 ; offset 0-1, packet size + + DebugChar('<') +%if DEBUG + mov eax, ecx + call print_hex +%endif + + ; + ; INT13 Func 42 - Extended Read Sectors + ; + ; Arguments: + ; AH = 0x42 + ; DL = drive number (80h + drive unit) + ; DS:SI = pointer to Disk Address Packet + ; + ; Returns: + ; AH = return status (success is 0) + ; carry = 0 success + ; 1 error + ; + ; Packet offset 2 indicates the number of sectors read + ; successfully. + ; + mov si, sp + mov ah, 0x42 + int 0x13 + + jnc .exit + + DebugChar('R') ; indicate INT13/F42 error + + ; + ; Issue a disk reset on error. + ; Should this be changed to Func 0xD to skip the diskette controller + ; reset? + ; + xor ax, ax ; Func 0 + int 0x13 ; INT 13 + stc ; set carry to indicate error + +.exit: + mov sp, bp ; restore SP + popad + ret + + +;-------------------------------------------------------------------------- +; Write a string with 'boot0: ' prefix to the console. +; +; Arguments: +; ES:DI pointer to a NULL terminated string. +; +; Clobber list: +; DI +; +log_string: + pusha + + push di + mov si, log_title_str + call print_string + + pop si + call print_string + + popa + + ret + + +;-------------------------------------------------------------------------- +; Write a string to the console. +; +; Arguments: +; DS:SI pointer to a NULL terminated string. +; +; Clobber list: +; AX, BX, SI +; +print_string: + mov bx, 1 ; BH=0, BL=1 (blue) + cld ; increment SI after each lodsb call +.loop: + lodsb ; load a byte from DS:SI into AL + cmp al, 0 ; Is it a NULL? + je .exit ; yes, all done + mov ah, 0xE ; INT10 Func 0xE + int 0x10 ; display byte in tty mode + jmp short .loop +.exit: + ret + + +%if DEBUG + +;-------------------------------------------------------------------------- +; Write a ASCII character to the console. +; +; Arguments: +; AL = ASCII character. +; +print_char: + pusha + mov bx, 1 ; BH=0, BL=1 (blue) + mov ah, 0x0e ; bios INT 10, Function 0xE + int 0x10 ; display byte in tty mode + popa + ret + + +;-------------------------------------------------------------------------- +; Write the 4-byte value to the console in hex. +; +; Arguments: +; EAX = Value to be displayed in hex. +; +print_hex: + pushad + mov cx, WORD 4 + bswap eax +.loop: + push ax + ror al, 4 + call print_nibble ; display upper nibble + pop ax + call print_nibble ; display lower nibble + ror eax, 8 + loop .loop + + mov al, 10 ; carriage return + call print_char + mov al, 13 + call print_char + + popad + ret + +print_nibble: + and al, 0x0f + add al, '0' + cmp al, '9' + jna .print_ascii + add al, 'A' - '9' - 1 +.print_ascii: + call print_char + ret + +getc: + pusha + mov ah, 0 + int 0x16 + popa + ret +%endif ;DEBUG + + +;-------------------------------------------------------------------------- +; NULL terminated strings. +; +log_title_str db 10, 13, 'boot0: ', 0 + +%if VERBOSE +gpt_str db 'GPT', 0 +%if 0 +test_str db 'test', 0 +%endif +done_str db 'done', 0 +%endif + +boot_error_str db 'error', 0 + +;-------------------------------------------------------------------------- +; Pad the rest of the 512 byte sized booter with zeroes. The last +; two bytes is the mandatory boot sector signature. +; +; If the booter code becomes too large, then nasm will complain +; that the 'times' argument is negative. + +; +; According to EFI specification, maximum boot code size is 440 bytes +; + +; +; XXX - compilation errors with debug enabled (see comment above about nasm) +; Azi: boot0.s:808: error: TIMES value -111 is negative +; boot0.s:811: error: TIMES value -41 is negative +; +pad_boot: + times 440-($-$$) db 0 + +pad_table_and_sig: + times 510-($-$$) db 0 + dw kBootSignature + + + ABSOLUTE 0xE400 + +; +; In memory variables. +; +my_lba resd 1 ; Starting LBA for read_lba function + +; END Index: branches/ErmaC/Enoch/i386/boot0/boot0hfs.s =================================================================== --- branches/ErmaC/Enoch/i386/boot0/boot0hfs.s (revision 2497) +++ branches/ErmaC/Enoch/i386/boot0/boot0hfs.s (revision 2498) @@ -20,7 +20,7 @@ ; ; @APPLE_LICENSE_HEADER_END@ ; -; Boot Loader: boot0 +; Boot Loader: boot0hfs ; ; A small boot sector program written in x86 assembly whose only ; responsibility is to locate the active partition, load the Index: branches/ErmaC/Enoch/i386/boot0/boot0md.s =================================================================== --- branches/ErmaC/Enoch/i386/boot0/boot0md.s (revision 2497) +++ branches/ErmaC/Enoch/i386/boot0/boot0md.s (revision 2498) @@ -20,7 +20,7 @@ ; ; @APPLE_LICENSE_HEADER_END@ ; -; Boot Loader: boot0 +; Boot Loader: boot0md ; ; A small boot sector program written in x86 assembly whose only ; responsibility is to locate the active partition, load the Index: branches/ErmaC/Enoch/i386/boot0/Makefile =================================================================== --- branches/ErmaC/Enoch/i386/boot0/Makefile (revision 2497) +++ branches/ErmaC/Enoch/i386/boot0/Makefile (revision 2498) @@ -12,7 +12,7 @@ DIRS_NEEDED = $(SYMROOT) -OBJS = boot0 boot0hfs boot0md chain0 +OBJS = boot0 boot0hfs boot0md boot0xg chain0 OBJS := $(addprefix $(SYMROOT)/, $(OBJS)) all: $(DIRS_NEEDED) $(OBJS) @@ -22,7 +22,7 @@ $(OBJS): $(SRCROOT)/autoconf.inc @echo " [NASM] $(@F)" - @$(NASM) $(@F).s -o $@ + @$(NASM) $(@F).s -o $@ -O3 clean-local: @for o in $(OBJS); do if [ -f "$${o}" ];then echo " [RM] $${o}"; fi; done Index: branches/ErmaC/Enoch/i386/boot1/Makefile =================================================================== --- branches/ErmaC/Enoch/i386/boot1/Makefile (revision 2497) +++ branches/ErmaC/Enoch/i386/boot1/Makefile (revision 2498) @@ -16,7 +16,7 @@ VERS = `vers_string -f 5.0 | tr - .` NEW_VERS = Darwin boot1h v$(VERS) -PROGRAMS = boot1hp boot1f32 +PROGRAMS = boot1hp boot1f32 boot1x ifeq (${CONFIG_BOOT1_HFS}, y) PROGRAMS += boot1h @@ -37,7 +37,7 @@ $(PROGRAMS): $(SRCROOT)/autoconf.inc @echo " [NASM] $(@F)" - @$(NASM) $(@F).s -o $@ + @$(NASM) $(@F).s -o $@ -O3 install_i386:: all $(INSTALLDIR) cp $(SYMROOT)/boot1h $(INSTALLDIR)/ Index: branches/ErmaC/Enoch/i386/boot1/boot1x.s =================================================================== --- branches/ErmaC/Enoch/i386/boot1/boot1x.s (revision 0) +++ branches/ErmaC/Enoch/i386/boot1/boot1x.s (revision 2498) @@ -0,0 +1,642 @@ +; +; Copyright (c) 2014 Zenith432 All rights reserved. +; +; Partition Boot Loader: boot1x +; This version of boot1x tries to find a stage2 boot file in the root folder. +; +; Credits: +; Portions based on boot1f32. +; Thanks to Robert Shullich for +; "Reverse Engineering the Microsoft exFAT File System" dated Dec 1, 2009. +; T13 Commitee document EDD-4 for information about BIOS int 0x13. +; +; This program is designed to reside in blocks 0 - 1 of an exFAT partition. +; It expects that the MBR has left the drive number in DL. +; +; This version requires a BIOS with EBIOS (LBA) support. +; +; This code is written for the NASM assembler. +; nasm -f bin -o boot1x boot1x.s +; +; Written by zenith432 during November 2014. +; + bits 16 + +%define VERBOSE 1 +%define USESIDL 1 +%define USEBP 1 +kMaxBlockCount equ 127 ; Max block count supported by Int 0x13, function 0x42, old school +kBootBlockBytes equ 512 ; Bytes in a exFAT Boot block +kBootSignature equ 0xaa55 ; Boot block signature +kBoot1StackAddress equ 0xfff0 ; Address of top-of-stack 0:0xfff0 +kBoot1LoadAddr equ 0x7c00 ; Address of loaded boot block 0:0x7c00 +kBoot2Segment equ 0x2000 ; Address for boot2 0x2000:0x200 +kBoot2Address equ 512 +kFATBuf equ 0x6c00 ; Address for FAT block buffer 0:0x6c00 (4K space) +kRootDirBuf equ 0x5c00 ; Address for Root Directory block buffer 0:0x5c00 (4K space) +kMaxCluster equ 0xfffffff7 ; exFAT max cluster value + 1 (for FAT32 it's 0x0ffffff8) +kMaxContigClusters equ 1024 ; Max contiguous clusters returned by getRange +kBootNameHash equ 0xdc36 ; exFAT name hash for 'BOOT' (in UTF16LE) +kBoot2MaxBytes equ (512 * 1024 - 512) ; must fit between 0x20200 and 0xa0000 + + struc PartitionEntry ; MBR partition entry (truncated) + times 8 resb 1 +.lba: resd 1 ; starting lba + endstruc + + struc BootParams ; BOOT file parameters +.cluster: resd 1 ; 1st cluster of BOOT +.size: resd 1 ; size of BOOT in bytes + resw 1 +.flag: resb 1 + endstruc + + struc DirIterator ; exFAT Directory Iterator +.entries_end: resb 1 ; beyond last 32-byte entry (possible values 16, 32, 64, 128) +.cluster: resd 1 ; current cluster +.lba_high: resd 1 ; upper 32 bits of lba +.lba_end: resd 1 ; beyond last block (lower 32-bits) +.lba: resd 1 ; current block +.entry: resb 1 ; current 32-byte entry + endstruc + + struc FATCache ; Manages cache state for FAT blocks +.shift: resb 1 ; right shift for converting cluster # to FAT block address +.mask: resw 1 ; bit mask for finding cluster # in FAT block +.lba: resd 1 ; lba # cached in FAT block buffer (note that FAT block address is limited to 32 bits) + endstruc + +%ifdef USEBP +%define BPR bp - gPartitionOffset + +%else +%define BPR +%endif + + section .text + org kBoot1LoadAddr + jmp start + times (3 - $ + $$) nop +gOEMName: times 8 db 0 ; 'EXFAT ' + +; +; Scratch Area +; Used for data structures +; + times (64 - BootParams_size - DirIterator_size - FATCache_size - $ + $$) db 0 +gsParams: times BootParams_size db 0 +gsIterator: times DirIterator_size db 0 +gsFATCache: times FATCache_size db 0 + +; +; exFAT BPB +; +gPartitionOffset: dd 0, 0 +gVolumeLength: dd 0, 0 +gFATOffset: dd 0 +gFATLength: dd 0 +gClusterHeapOffset: dd 0 +gClusterCount: dd 0 +gRootDirectory1stCluster: dd 0 +gVolumeSerialNubmer: dd 0 +gFileSystemRevision: dw 0 ; 0x100 +gVolumeFlags: dw 0 +gBytesPerBlock: db 0 ; range 9 - 12 (power of 2) +gBlocksPerCluster: db 0 ; gBytesPerBlock + gBlocksPerCluster <= 25 (power of 2) +gNumberOfFATs: db 0 ; should be 1 +gDriveSelect: db 0 ; probably 0x80 +gPercentInUse: db 0 + times 7 db 0 +start: + cli + xor eax, eax + mov ss, ax + mov sp, kBoot1StackAddress + sti + mov ds, ax + mov es, ax + + ; + ; Initializing global variables. + ; +%ifdef USEBP + mov bp, gPartitionOffset +%endif +%ifdef USESIDL + ; + ; Shouldn't be necessary to use DS:SI because + ; 1) Existing gPartitionOffset must be correct in + ; order for filesystem to work well when mounted. + ; 2) LBA may be 64 bits if booted from GPT. + ; 3) Not all MBR boot records pass DS:SI + ; pointing to MBR partition entry. + ; +%if 0 + mov ecx, [si + PartitionEntry.lba] + mov [BPR gPartitionOffset + 4], eax + mov [BPR gPartitionOffset], ecx +%endif + ; + ; However, by convention BIOS passes boot + ; drive number in dl, so use that instead + ; of existing gDriveSelect + ; + mov [BPR gDriveSelect], dl +%endif + + ; + ; Initialize FAT Cache + ; + dec eax + mov dword [BPR gsFATCache + FATCache.lba], eax ; alternatively store gFATLength here + mov cl, [BPR gBytesPerBlock] + sub cl, 2 ; range 7 - 10 + mov [BPR gsFATCache + FATCache.shift], cl + neg ax + shl ax, cl + dec ax + mov [BPR gsFATCache + FATCache.mask], ax + + ; + ; Initialize Iterator + ; + mov al, 1 + sub cl, 3 ; range 4 - 7 + shl al, cl + mov [BPR gsIterator + DirIterator.entries_end], al + mov [BPR gsIterator + DirIterator.entry], al + xor eax, eax + mov ecx, [BPR gRootDirectory1stCluster] + mov [BPR gsIterator + DirIterator.lba_end], eax + mov [BPR gsIterator + DirIterator.lba], eax + mov [BPR gsIterator + DirIterator.cluster], ecx + +%ifdef VERBOSE + mov di, init_str + call log_string +%endif + + ; + ; Search root directory for BOOT + ; +.loop: + call nextDirEntry + jc error + cld + lodsb +.revert: + test al, al ; end of root directory? + jz error + cmp al, 0x85 ; file/subdir entry? + jnz .loop + lodsb + cmp al, 2 ; 2ndary count should be 2 + jb .loop + add si, 2 ; skip checksum + lodsb + test al, 0x10 ; file attributes - check not a directory + jnz .loop + call nextDirEntry + jc error + cld + lodsb + cmp al, 0xc0 ; stream extension entry? + jnz .revert + lodsb + mov dl, al ; General 2ndary flag + inc si + lodsb + cmp al, 4 ; name length + jnz .loop + lodsw ; name hash + cmp ax, kBootNameHash + jnz .loop + add si, 2 + mov eax, [si + 4] ; high 32 bits of valid data length + test eax, eax + jz .more + and dl, 0xfe ; if size too big, mark as no allocation +.more: + lodsd ; valid data length + mov [BPR gsParams + BootParams.size], eax + add si, 8 + lodsd ; first cluster + mov [BPR gsParams + BootParams.cluster], eax + mov [BPR gsParams + BootParams.flag], dl + call nextDirEntry + jc error + cld + lodsb + cmp al, 0xc1 + jnz .revert + inc si ; skip flags + lodsd ; unicode chars 1 - 2 + or eax, 0x200020 ; tolower + cmp eax, 0x6f0062 ; 'bo' in UTF16LE + jnz .loop + lodsd ; unicode chars 3 - 4 + or eax, 0x200020 ; tolower + cmp eax, 0x74006f ; 'ot' in UTF16LE + jnz .loop + ; + ; done - found boot file! + ; + mov dl, [BPR gsParams + BootParams.flag] + test dl, 1 ; no allocation or length too big? + jz error + mov ebx, [BPR gsParams + BootParams.size] + cmp ebx, kBoot2MaxBytes + 1 + jnb error + call BytesToBlocks ; convert size to blocks + ; boot2 file size in blocks is in bx +load_boot2: ; anchor for localizing next labels + xor esi, esi ; no blocks after 1st range + test dl, 2 ; FAT Chain? + cmovnz edx, [BPR gsParams + BootParams.cluster] ; if not + jnz .oneshot ; load contiguous file + ; + ; load via FAT + ; + mov si, bx ; total blocks to si +.loop: + mov eax, [BPR gsParams + BootParams.cluster] + mov edx, eax + call getRange + test ebx, ebx + jnz .nonempty + test si, si + jnz error + jmp boot2 +.nonempty: + cmp ebx, esi + cmovnb bx, si + sub si, bx + mov [BPR gsParams + BootParams.cluster], eax +.oneshot: + call ClusterToLBA + mov ax, bx + mov ecx, edx + mov edx, (kBoot2Segment << 4) | kBoot2Address + call readBlocks + ; TODO: error + test si, si + jnz .loop + ; fall through to boot2 +boot2: + mov dl, [BPR gDriveSelect] ; load BIOS drive number + jmp kBoot2Segment:kBoot2Address + +error: +%ifdef VERBOSE + mov di, error_str + call log_string +%endif + +hang: + hlt + jmp hang + +;-------------------------------------------------------------------------- +; ClusterToLBA - Converts cluster number to 64-bit LBA +; +; Arguments: +; EDX = cluster number +; +; Returns +; EDI:EDX = corresponding block address +; +; Assumes input cluster number is valid +; +ClusterToLBA: + push cx + xor edi, edi + sub edx, 2 + mov cl, [BPR gBlocksPerCluster] + shld edi, edx, cl + shl edx, cl + add edx, [BPR gClusterHeapOffset] + adc edi, 0 + pop cx + ret + +;-------------------------------------------------------------------------- +; BytesToBlocks - Converts byte size to blocks (rounding up to next block) +; +; Arguments: +; EBX = size in bytes +; +; Returns: +; EBX = size in blocks (rounded up) +; +; Clobbers eax, cl +; +BytesToBlocks: + xor eax, eax + inc ax + mov cl, [BPR gBytesPerBlock] + shl ax, cl + dec ax + add ebx, eax + shr ebx, cl + ret + + times (kBootBlockBytes - 2 - $ + $$) nop + dw kBootSignature +block1_end: + +;-------------------------------------------------------------------------- +; nextDirEntry - Locates the next 32-byte entry in Root Directory, +; loading block if necessary. +; +; Returns: +; CF set if end of Root Directory +; CF clear, and DS:SI points to next entry if exists +; +; Clobbers eax, ebx, ecx, edx, edi +; +nextDirEntry: + movzx ax, [BPR gsIterator + DirIterator.entry] + cmp al, [BPR gsIterator + DirIterator.entries_end] + jb .addressentry + mov ecx, [BPR gsIterator + DirIterator.lba] + mov edi, [BPR gsIterator + DirIterator.lba_high] + cmp ecx, [BPR gsIterator + DirIterator.lba_end] + jnz .readblock + mov eax, [BPR gsIterator + DirIterator.cluster] + mov edx, eax + call getRange + test ebx, ebx + jnz .nonempty + stc + ret +.nonempty: + mov [BPR gsIterator + DirIterator.cluster], eax + call ClusterToLBA + mov ecx, edx + add edx, ebx + mov [BPR gsIterator + DirIterator.lba_high], edi + mov [BPR gsIterator + DirIterator.lba_end], edx +.readblock: + mov al, 1 +%if 0 + mov edx, kRootDirBuf +%else + xor edx, edx + mov dh, kRootDirBuf >> 8 +%endif + call readLBA + ; TODO error + inc ecx + jnz .skip + inc edi + mov [BPR gsIterator + DirIterator.lba_high], edi +.skip: + mov [BPR gsIterator + DirIterator.lba], ecx + xor ax, ax +.addressentry: + mov si, ax + inc al + mov [BPR gsIterator + DirIterator.entry], al + shl si, 5 + add si, kRootDirBuf + clc + ret + +;-------------------------------------------------------------------------- +; getRange - Calculates contiguous range of clusters from FAT +; +; Arguments: +; EAX = start cluster +; +; Returns: +; EAX = next cluster after range +; EBX = number of contiguous blocks in range +; +; Range calculated is at most kMaxContigClusters clusters long +; +getRange: + push ecx + push edx + push edi + push si + xor edi, edi +%if 0 + mov edx, kFATBuf +%else + mov edx, edi + mov dh, kFATBuf >> 8 +%endif + mov ebx, edi +.loop: + cmp eax, 2 + jb .finishup + cmp eax, -9 ;kMaxCluster + jnb .finishup + cmp bx, kMaxContigClusters + jnb .finishup + inc bx + mov si, ax + and si, [BPR gsFATCache + FATCache.mask] + shl si, 2 + mov ecx, eax + inc ecx + push ecx + mov cl, [BPR gsFATCache + FATCache.shift] + shr eax, cl + cmp eax, [BPR gsFATCache + FATCache.lba] + jz .iscached + mov ecx, [BPR gFATOffset] + add ecx, eax + mov [BPR gsFATCache + FATCache.lba], eax + mov al, 1 + call readLBA + ; TODO: error? +.iscached: + pop ecx + mov eax, [kFATBuf + si] + cmp eax, ecx + jz .loop + +.finishup: + mov cl, [BPR gBlocksPerCluster] + shl ebx, cl + pop si + pop edi + pop edx + pop ecx + ret + +;-------------------------------------------------------------------------- +; readBlocks - Reads more than kMaxBlockCount blocks using LBA addressing. +; +; Arguments: +; AX = number of blocks to read (valid from 1-1280). +; EDX = pointer to where the blocks should be stored. +; EDI:ECX = block offset in partition (64 bits) +; +; Returns: +; CF = 0 success +; 1 error +; +readBlocks: + pushad + mov bx, ax + +.loop: + xor eax, eax + mov al, kMaxBlockCount + cmp bx, ax + cmovb ax, bx + call readLBA + ; TODO: error? + sub bx, ax + jz .exit + add ecx, eax + adc edi, 0 + push cx + mov cl, [BPR gBytesPerBlock] + shl eax, cl + pop cx + add edx, eax + jmp .loop + +.exit: + popad + ret + +;-------------------------------------------------------------------------- +; readLBA - Read blocks from a partition using LBA addressing. +; +; Arguments: +; AL = number of blocks to read (valid from 1-kMaxBlockCount). +; EDX = pointer to where the blocks should be stored. +; EDI:ECX = block offset in partition (64 bits) +; [gDriveSelect] = drive number (0x80 + unit number) +; [gPartitionOffset] = partition location on drive +; +; Returns: +; CF = 0 success +; 1 error +; Presently, jumps to error on BIOS-reported failure +; +readLBA: + pushad ; save all registers + push es ; save ES + mov bp, sp ; save current SP + + ; + ; Adjust to 16 bit segment:offset address + ; to allow for reading up to 64K + ; + mov bl, dl + and bx, 0xf + shr edx, 4 + mov es, dx + + ; + ; Create the Disk Address Packet structure for the + ; INT13/F42 (Extended Read Sectors) on the stack. + ; + + add ecx, [gPartitionOffset] + adc edi, [gPartitionOffset + 4] + push edi + push ecx + push es + push bx + xor ah, ah + push ax + push word 16 + + ; + ; INT13 Func 42 - Extended Read Sectors + ; + ; Arguments: + ; AH = 0x42 + ; DL = drive number (0x80 + unit number) + ; DS:SI = pointer to Disk Address Packet + ; + ; Returns: + ; AH = return status (sucess is 0) + ; carry = 0 success + ; 1 error + ; + ; Packet offset 2 indicates the number of sectors read + ; successfully. + ; + mov dl, [gDriveSelect] ; load BIOS drive number + mov si, sp + mov ah, 0x42 + int 0x13 + + jc error + + ; + ; Issue a disk reset on error. + ; Should this be changed to Func 0xD to skip the diskette controller + ; reset? + ; +; xor ax, ax ; Func 0 +; int 0x13 ; INT 13 +; stc ; set carry to indicate error + +;.exit + mov sp, bp + pop es + popad + ret + +%ifdef VERBOSE + +;-------------------------------------------------------------------------- +; Write a string with log_title_str prefix to the console. +; +; Arguments: +; DS:DI pointer to a NULL terminated string. +; +log_string: + pushad + push di + mov si, log_title_str + call print_string + pop si + call print_string + popad + ret + +;------------------------------------------------------------------------- +; Write a string to the console. +; +; Arguments: +; DS:SI pointer to a NULL terminated string. +; +; Clobber list: +; AX, BX, SI +; +print_string: + mov bx, 1 ; BH=0, BL=1 (blue) + +.loop: + lodsb ; load a byte from DS:SI into AL + test al, al ; Is it a NULL? + jz .exit ; yes, all done + mov ah, 0xE ; INT10 Func 0xE + int 0x10 ; display byte in tty mode + jmp .loop + +.exit: + ret + +%endif ; VERBOSE + +;-------------------------------------------------------------------------- +; Static data. +; + +%ifdef VERBOSE +log_title_str: db 13, 10, 'boot1x: ', 0 +init_str: db 'init', 0 +error_str: db 'error', 0 +%endif + + times (kBootBlockBytes - 4 - $ + block1_end) db 0 + dw 0, kBootSignature Index: branches/ErmaC/Enoch/i386/util/Cconfig =================================================================== --- branches/ErmaC/Enoch/i386/util/Cconfig (revision 2497) +++ branches/ErmaC/Enoch/i386/util/Cconfig (revision 2498) @@ -9,6 +9,8 @@ source "i386/util/fdisk/Cconfig" +source "i386/util/boot1-install/Cconfig" + config OPENUP bool "openUp utility" default n Index: branches/ErmaC/Enoch/i386/util/boot1-install/Cconfig =================================================================== --- branches/ErmaC/Enoch/i386/util/boot1-install/Cconfig (revision 0) +++ branches/ErmaC/Enoch/i386/util/boot1-install/Cconfig (revision 2498) @@ -0,0 +1,10 @@ +config BOOT1INSTALL + bool "boot1-install utility" + default y + help + Say Y here if you want to compile the boot1-install program. + boot1-install is a program 'boot1-install' that can install the stage 1 boot loader for all three file systems + boot1f32 -> FAT32 + boot1h -> HFS+ + boot1x -> exFAT + When in doubt, say "Y". Index: branches/ErmaC/Enoch/i386/util/boot1-install/boot1-install.c =================================================================== --- branches/ErmaC/Enoch/i386/util/boot1-install/boot1-install.c (revision 0) +++ branches/ErmaC/Enoch/i386/util/boot1-install/boot1-install.c (revision 2498) @@ -0,0 +1,830 @@ +/* + * boot1-install.c + * boot1-install + * + * Created by Zenith432 on November 19th, 2014. + * Copyright (c) 2014 Zenith432. All rights reserved. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +struct buffer_t +{ + unsigned char* _b; + size_t _s; +}; + +enum volume_kind_t +{ + _undetected = 0, + _exfat = 1, + _hfs = 2, + _msdos = 3, + _other = 255 +}; + +static int isVolumeMounted = 0; +static int isMediaWhole = 0; +static int isMediaLeaf = 0; +static enum volume_kind_t daVolumeKind = _undetected; + +static struct buffer_t bpbBlob = { NULL, 0 }; +static struct buffer_t bootBlob = { NULL, 0 }; +static struct buffer_t outputBlob = { NULL, 0 }; + +static char const UnsupportedMessage[] = "Only exFAT, FAT32 or HFS+ volumes are supported\n"; +static char const exfatID[] = "EXFAT "; +static char const fat32ID[] = "FAT32 "; +static char const devrdisk[] = "/dev/rdisk"; +static char const devdisk[] = "/dev/disk"; +static char const defaultBootFile_exfat[] = "./boot1x"; +static char const defaultBootFile_hfs[] = "./boot1h"; +static char const defaultBootFile_fat32[] = "./boot1f32"; + +static __used char const copyright[] = "Copyright 2014 Zenith432"; + +static int checkExfat(struct buffer_t const*); +static int checkFat32(struct buffer_t const*); +static int loadChunk(char const*, off_t, off_t, struct buffer_t*); +static void unsupported(void); + +#pragma mark - +#pragma mark Cleaners +#pragma mark - + +static +void free_buffer(struct buffer_t* pBuffer) +{ + assert(pBuffer); + if (pBuffer->_b) { + free(pBuffer->_b); + pBuffer->_b = NULL; + pBuffer->_s = 0; + } +} + +/* + * Uses statics + */ +static +void cleanup(void) +{ + free_buffer(&outputBlob); + free_buffer(&bootBlob); + free_buffer(&bpbBlob); +} + +#pragma mark - +#pragma mark ExFAT Processor +#pragma mark - + +static +unsigned VBRChecksum(unsigned char const* octets, size_t NumberOfBytes) +{ + unsigned Checksum = 0; + size_t Index; + for (Index = 0; Index != NumberOfBytes; ++Index) + { + if (Index == 106 || Index == 107 || Index == 112) + continue; + Checksum = ((Checksum << 31) | (Checksum >> 1)) + (unsigned) octets[Index]; + } + return Checksum; +} + +static +int calcSum(struct buffer_t const* pBootBlob, + struct buffer_t const* pBpbBlob, + struct buffer_t* pOutputBlob, + char const* pathName) +{ + unsigned char *outBuffer, *p, *q; + size_t outSize, toCopy, leftOver; + unsigned Checksum; + + assert(pBootBlob && pBpbBlob); + if (pBootBlob->_s > 9U * 512U) { + fprintf(stderr, "Boot Code must be at most 4608 bytes\n"); + return -1; + } + if (pBpbBlob->_s < 113U) { + fprintf(stderr, "BPB must be at least 113 bytes\n"); + return -1; + } + if (!checkExfat(pBpbBlob)) { + fprintf(stderr, "BPB does not contain proper exFAT signature\n"); + return -1; + } + outSize = 12U * 512U; + outBuffer = malloc(outSize); + if (!outBuffer) { + fprintf(stderr, "%s: Memory allocation failed\n", __FUNCTION__); + return -1; + } + memset(outBuffer, 0, outSize); + memcpy(outBuffer, pBootBlob->_b, pBootBlob->_s); + memcpy(&outBuffer[3], &pBpbBlob->_b[3], 8); + memset(&outBuffer[11], 0, 53); + toCopy = 120; + if (pBpbBlob->_s < toCopy) + toCopy = pBpbBlob->_s; + leftOver = 120 - toCopy; + memcpy(&outBuffer[64], &pBpbBlob->_b[64], toCopy - 64); + if (leftOver) + memset(&outBuffer[120 - leftOver], 0, leftOver); + for (toCopy = 0; toCopy != 9; ++toCopy) { + p = outBuffer + toCopy * 512U + 508U; + p[2] = 0x55U; + p[3] = 0xAAU; + if (toCopy) { + p[0] = 0U; + p[1] = 0U; + } + } + if (pathName) { + /* + * Copy OEM Parameters record + */ + struct buffer_t auxBlob = { NULL, 0 }; + if (loadChunk(pathName, 9 * 512 , 512, &auxBlob) >= 0) { + memcpy(&outBuffer[9 * 512], &auxBlob._b[0], 512); + free_buffer(&auxBlob); + } + } + Checksum = VBRChecksum(outBuffer, 11U * 512U); + p = outBuffer + 11U * 512U; + q = p + 512U; + for (; p < q; p += 4) { + *(unsigned*) p = Checksum; + } + if (pOutputBlob) { + pOutputBlob->_b = outBuffer; + pOutputBlob->_s = outSize; + } else + free(outBuffer); + return 0; +} + +#pragma mark - +#pragma mark FAT32 Processor +#pragma mark - + +static +int fat32Layout(struct buffer_t const* pBootBlob, + struct buffer_t const* pBpbBlob, + struct buffer_t* pOutputBlob) +{ + unsigned char *outBuffer; + size_t outSize; + + assert(pBootBlob && pBpbBlob); + if (pBootBlob->_s > 512U) { + fprintf(stderr, "Boot Code must be at most 512 bytes\n"); + return -1; + } + if (pBpbBlob->_s < 90U) { + fprintf(stderr, "BPB must be at least 90 bytes\n"); + return -1; + } + if (!checkFat32(pBpbBlob)) { + fprintf(stderr, "BPB does not contain proper FAT32 signature\n"); + return -1; + } + outSize = 512U; + outBuffer = malloc(outSize); + if (!outBuffer) { + fprintf(stderr, "%s: Memory allocation failed\n", __FUNCTION__); + return -1; + } + memset(outBuffer, 0, outSize); + memcpy(outBuffer, pBootBlob->_b, pBootBlob->_s); + memcpy(&outBuffer[3], &pBpbBlob->_b[3], 87); + outBuffer[510] = 0x55U; + outBuffer[511] = 0xAAU; + if (pOutputBlob) { + pOutputBlob->_b = outBuffer; + pOutputBlob->_s = outSize; + } else + free(outBuffer); + return 0; +} + +#pragma mark - +#pragma mark File Operations +#pragma mark - + +static +void writeVBR(char const* pathName, + struct buffer_t const* pBuffer, + int numCopies, + size_t expectedSize, + char const* volumeType) +{ + int fd, j; + + assert(pathName && pBuffer && volumeType); + if (pBuffer->_s != expectedSize) { + fprintf(stderr, "Unexpected %s VBR size %lu (expected %lu)\n", volumeType, pBuffer->_s, expectedSize); + return; + } + fd = open(pathName, O_WRONLY); + if (fd < 0) { + fprintf(stderr, "Unable to write boot record to %s, %s\n", pathName, strerror(errno)); + } + for (j = 0; j != numCopies; ++j) + write(fd, pBuffer->_b, pBuffer->_s); + close(fd); +} + +static +int loadChunk(char const* pathName, off_t startOffset, off_t bytesToRead, struct buffer_t* pBuffer) +{ + int fd; + ssize_t rc; + unsigned char* p; + struct stat buf; + + assert(pathName); + fd = open(pathName, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Unable to open %s, %s\n", pathName, strerror(errno)); + return -1; + } + if (bytesToRead > 0) + buf.st_size = bytesToRead; + else if (fstat(fd, &buf) < 0) { + fprintf(stderr, "Unable to fstat %s, %s\n", pathName, strerror(errno)); + close(fd); + return -1; + } + if (startOffset > 0) { + off_t t = lseek(fd, startOffset, SEEK_SET); + if (t < 0) { + fprintf(stderr, "Unable to lseek %s, %s\n", pathName, strerror(errno)); + close(fd); + return -1; + } + if (t != startOffset) { + fprintf(stderr, "lseek %s returned wrong value %lld instead of %lld\n", pathName, t, startOffset); + close(fd); + return -1; + } + if (bytesToRead <= 0) + buf.st_size -= t; + } + p = malloc((size_t) buf.st_size); + if (!p) { + fprintf(stderr, "%s: Memory allocation failed\n", __FUNCTION__); + close(fd); + return -1; + } + rc = read(fd, p, (size_t) buf.st_size); + if (rc < 0) { + fprintf(stderr, "Unable to read from %s, %s\n", pathName, strerror(errno)); + free(p); + close(fd); + return -1; + } + close(fd); + if (rc != buf.st_size) { + fprintf(stderr, "Unable to read entire chunk from %s, read %ld/%lld\n", pathName, rc, buf.st_size); + free(p); + return -1; + } + if (pBuffer) { + pBuffer->_b = p; + pBuffer->_s = (size_t) rc; + } else + free(p); + return 0; +} + +#pragma mark - +#pragma mark DiskArbitration Helpers +#pragma mark - + +static +char const* toBSDName(char const* pathName) +{ + assert(pathName); + return strncmp(pathName, &devrdisk[0], 10) ? pathName : &pathName[6]; +} + +static +char const* daReturnStr(DAReturn v) +{ + if (unix_err(err_get_code(v)) == v) + return strerror(err_get_code(v)); + switch (v) { + case kDAReturnError: + return "Error"; + case kDAReturnBusy: + return "Busy"; + case kDAReturnBadArgument: + return "Bad Argument"; + case kDAReturnExclusiveAccess: + return "Exclusive Access"; + case kDAReturnNoResources: + return "No Resources"; + case kDAReturnNotFound: + return "Not Found"; + case kDAReturnNotMounted: + return "Not Mounted"; + case kDAReturnNotPermitted: + return "Not Permitted"; + case kDAReturnNotPrivileged: + return "Not Privileged"; + case kDAReturnNotReady: + return "Not Ready"; + case kDAReturnNotWritable: + return "Not Writable"; + case kDAReturnUnsupported: + return "Unsupported"; + default: + return "Unknown"; + } +} + +static +int getDASessionAndDisk(char const* pathName, DASessionRef* pSession, DADiskRef* pDisk) +{ + DASessionRef session; + DADiskRef disk; + + assert(pathName); + session = DASessionCreate(kCFAllocatorDefault); + if (!session) { + fprintf(stderr, "DASessionCreate returned NULL\n"); + return -1; + } + disk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, toBSDName(pathName)); + if (!disk) { + CFRelease(session); + fprintf(stderr, "DADiskCreateFromBSDName(%s) returned NULL\n", pathName); + return -1; + } + if (pDisk) + *pDisk = disk; + else + CFRelease(disk); + if (pSession) + *pSession = session; + else + CFRelease(session); + return 0; +} + +#pragma mark - +#pragma mark Mount/UMount +#pragma mark - + +static +void umountCallback(DADiskRef disk __unused, + DADissenterRef dissenter, + void *context) +{ + if (context && dissenter != NULL) { + *(int*) context = -1; + fprintf(stderr, "umount unsuccessful, status %s\n", daReturnStr(DADissenterGetStatus(dissenter))); + } + CFRunLoopStop(CFRunLoopGetCurrent()); +} + +static +int umount(char const* pathName) +{ + DASessionRef session; + DADiskRef disk; + int rc; + + assert(pathName); + if (getDASessionAndDisk(pathName, &session, &disk) < 0) + return -1; + rc = 0; + DASessionScheduleWithRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + DADiskUnmount(disk, kDADiskUnmountOptionDefault, umountCallback, &rc); + CFRunLoopRun(); + DASessionUnscheduleFromRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + CFRelease(disk); + CFRelease(session); + return rc; +} + +static +void mountCallback(DADiskRef disk __unused, + DADissenterRef dissenter, + void *context) +{ + if (context && dissenter != NULL) { + *(int*) context = -1; + fprintf(stderr, "mount unsuccessful, status %s\n", daReturnStr(DADissenterGetStatus(dissenter))); + } + CFRunLoopStop(CFRunLoopGetCurrent()); +} + +static +int mount(char const* pathName) +{ + DASessionRef session; + DADiskRef disk; + int rc; + + assert(pathName); + if (getDASessionAndDisk(pathName, &session, &disk) < 0) + return -1; + rc = 0; + DASessionScheduleWithRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + DADiskMount(disk, NULL, kDADiskMountOptionDefault, mountCallback, &rc); + CFRunLoopRun(); + DASessionUnscheduleFromRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + CFRelease(disk); + CFRelease(session); + return rc; +} + +#pragma mark - +#pragma mark Analyze Volume +#pragma mark - + +static +int checkExfat(struct buffer_t const* pBpbBlob) +{ + assert(pBpbBlob); + return !memcmp(&pBpbBlob->_b[3], &exfatID[0], 8); +} + +static +int checkHFS(struct buffer_t const* pBpbBlob) +{ + uint16_t sig; + + assert(pBpbBlob); + sig = OSSwapBigToHostInt16(*(uint16_t const*)&pBpbBlob->_b[0]); + return sig == 0x4244 || sig == 0x482B || sig == 0x4858; /* 'BD', 'H+', 'HX' */ +} + +static +int checkFat32(struct buffer_t const* pBpbBlob) +{ + uint16_t bytesPerSector, rootEntCnt; + uint8_t sectorsPerCluster; + + assert(pBpbBlob); + bytesPerSector = OSSwapLittleToHostInt16(*(uint16_t const*)&pBpbBlob->_b[11]); + if ((bytesPerSector & (bytesPerSector - 1U)) || + bytesPerSector < 0x200U || + bytesPerSector > 0x1000U) + return 0; + sectorsPerCluster = pBpbBlob->_b[13]; + if (!sectorsPerCluster || + (sectorsPerCluster & (sectorsPerCluster - 1U))) + return 0; + rootEntCnt = OSSwapLittleToHostInt16(*(uint16_t const*)&pBpbBlob->_b[17]); + if (rootEntCnt) + return 0; + return !memcmp(&pBpbBlob->_b[82], &fat32ID[0], 8); +} + +static +int checkSupportedVolume(enum volume_kind_t* pKind, struct buffer_t const* pBpbBlob, char const* pathName) +{ + int rc; + + assert(pKind && pBpbBlob); + rc = -1; + switch (*pKind) { + case _undetected: + if (checkExfat(pBpbBlob)) { + *pKind = _exfat; + rc = 0; + } else if (checkFat32(pBpbBlob)) { + *pKind = _msdos; + rc = 0; + } else if (pathName) { + struct buffer_t auxBlob = { NULL, 0 }; + if (loadChunk(pathName, 1024 , 512, &auxBlob) >= 0) { + if (checkHFS(&auxBlob)) { + *pKind = _hfs; + rc = 0; + } + free_buffer(&auxBlob); + } + } + break; + case _exfat: + if (checkExfat(pBpbBlob)) + rc = 0; + else + *pKind = _other; + break; + case _hfs: + if (checkHFS(pBpbBlob)) + rc = 0; + else + *pKind = _other; + break; + case _msdos: + if (checkFat32(pBpbBlob)) + rc = 0; + else + *pKind = _other; + break; + default: + break; + } + if (rc < 0) + unsupported(); + return rc; +} + +/* + * Uses statics + */ +static +int checkDevicePath2(char const* pathName) +{ + DASessionRef session; + DADiskRef disk; + CFDictionaryRef descDict; + CFStringRef s_ref; + CFBooleanRef b_ref; + + assert(pathName); + if (getDASessionAndDisk(pathName, &session, &disk) < 0) + return -1; + descDict = DADiskCopyDescription(disk); + if (!descDict) { + CFRelease(disk); + CFRelease(session); + fprintf(stderr, "DADiskCopyDescription(%s) returned NULL\n", pathName); + return -1; + } + if (CFDictionaryGetValueIfPresent(descDict, kDADiskDescriptionMediaWholeKey, (void const**) &b_ref) && + CFBooleanGetValue(b_ref)) + isMediaWhole = 1; + if (CFDictionaryGetValueIfPresent(descDict, kDADiskDescriptionMediaLeafKey, (void const**) &b_ref) && + CFBooleanGetValue(b_ref)) + isMediaLeaf = 1; + if (CFDictionaryContainsKey(descDict, kDADiskDescriptionVolumePathKey)) + isVolumeMounted = 1; + if (CFDictionaryGetValueIfPresent(descDict, kDADiskDescriptionVolumeKindKey, (void const**) &s_ref)) { + static char cstr_buffer[64]; + char const* cstr = CFStringGetCStringPtr(s_ref, kCFStringEncodingUTF8); + if (!cstr) { + CFStringGetCString(s_ref, &cstr_buffer[0], (CFIndex) sizeof cstr_buffer, kCFStringEncodingUTF8); + cstr = &cstr_buffer[0]; + } +#if 0 + printf("DAVolumeKind %s\n", cstr); +#endif + if (!strcmp(cstr, "exfat")) + daVolumeKind = _exfat; + else if (!strcmp(cstr, "hfs")) + daVolumeKind = _hfs; + else if (!strcmp(cstr, "msdos")) + daVolumeKind = _msdos; + else + daVolumeKind = _other; + } +#if 0 + printf(stderr, "whole %c, leaf %c, mounted %c\n", + isMediaWhole ? 'Y' : 'N', + isMediaLeaf ? 'Y' : 'N', + isVolumeMounted ? 'Y' : 'N'); +#endif +#if 0 + CFShow(descDict); +#endif + CFRelease(descDict); + CFRelease(disk); + CFRelease(session); + return 0; +} + +static +int checkDevicePath(char const* pathName) +{ + struct stat buf; + + assert(pathName); + if (strncmp(pathName, &devdisk[0], 9) != 0 && + strncmp(pathName, &devrdisk[0], 10) != 0) { + fprintf(stderr, "disk must be of form /dev/rdiskUsS or /dev/diskUsS\n"); + return -1; + } + if (stat(pathName, &buf) < 0) { + fprintf(stderr, "stat on %s failed, %s\n", pathName, strerror(errno)); + return -1; + } + if (!(buf.st_mode & (S_IFCHR | S_IFBLK))) { + fprintf(stderr, "%s is not a block or character special device\n", pathName); + return -1; + } + /* + * FIXME: milk information from st_rdev - what's in it? + */ +#if 0 + printf("size of buf is %lu\n", sizeof buf); + printf("st_dev %#x\n", buf.st_dev); + printf("st_ino %llu\n", buf.st_ino); + printf("st_mode %#o\n", buf.st_mode); + printf("st_nlink %u\n", buf.st_nlink); + printf("st_uid %u\n", buf.st_uid); + printf("st_gid %u\n", buf.st_gid); + printf("st_rdev %#x\n", buf.st_rdev); + printf("st_size %llu\n", buf.st_size); + printf("st_blocks %llu\n", buf.st_blocks); + printf("st_blksize %u\n", buf.st_blksize); + printf("st_flags %#x\n", buf.st_flags); + printf("st_gen %u\n", buf.st_gen); +#endif + return 0; +} + +#pragma mark - +#pragma mark Usage +#pragma mark - + +static +void usage(char const* self) +{ + assert(self); + fprintf(stderr, "Usage: %s [-yM] [-f boot_code_file] disk\n", self); + fprintf(stderr, " boot_code_file is an optional boot template\n"); + fprintf(stderr, " -y: don't ask any questions\n"); + fprintf(stderr, " -M: keep volume mounted while proceeding (useful for root filesystem)\n"); + fprintf(stderr, "disk is of the form /dev/rdiskUsS or /dev/diskUsS\n"); + fprintf(stderr, "default boot files are\n"); + fprintf(stderr, " boot1h for HFS+\n"); + fprintf(stderr, " boot1f32 for FAT32\n"); + fprintf(stderr, " boot1x for exFAT\n"); +} + +static +void unsupported(void) +{ + fprintf(stderr, "%s", &UnsupportedMessage[0]); +} + +#pragma mark - +#pragma mark Main +#pragma mark - + +int main(int argc, char* const argv[]) +{ + int ch; + char const* bootFile = NULL; + char const* devicePath = NULL; + int dontAsk = 0; + int keepMounted = 0; + + while ((ch = getopt(argc, argv, "yMf:")) != -1) + switch (ch) { + case 'y': + dontAsk = 1; + break; + case 'M': + keepMounted = 1; + break; + case 'f': + bootFile = optarg; + break; + default: + goto usage_and_error; + } + if (optind + 1 > argc) + goto usage_and_error; + devicePath = argv[optind]; + if (geteuid() != 0) { + fprintf(stderr, "This program must be run as root\n"); + return -1; + } +#if 0 + printf("bootFile %s, devicePath %s, dontAsk %d\n", bootFile, devicePath, dontAsk); +#endif + if (checkDevicePath(devicePath) < 0) + return -1; + if (checkDevicePath2(devicePath) >= 0) { + if (isMediaWhole && !isMediaLeaf) { + fprintf(stderr, "%s is a whole disk\n", devicePath); + return -1; + } + switch (daVolumeKind) { + case _undetected: + case _exfat: + case _hfs: + case _msdos: + break; + default: + unsupported(); + return -1; + } + if (isVolumeMounted && keepMounted) + isVolumeMounted = 0; + if (isVolumeMounted && umount(devicePath) < 0) { + fprintf(stderr, "Unable to umount %s, please 'diskutil umount' manually before running this program\n", devicePath); + return -1; + } + } + /* + * Note: + * Reading a non-multiple of 512 does not work on /dev/rdisk + */ + if (loadChunk(devicePath, daVolumeKind == _hfs ? 1024 : 0, 512, &bpbBlob) < 0) + goto remount_and_error; + if (checkSupportedVolume(&daVolumeKind, &bpbBlob, devicePath) < 0) + goto cleanup_and_error; + if (!bootFile) { + switch (daVolumeKind) { + case _exfat: + bootFile = &defaultBootFile_exfat[0]; + break; + case _hfs: + bootFile = &defaultBootFile_hfs[0]; + break; + case _msdos: + bootFile = &defaultBootFile_fat32[0]; + break; + default: + assert(0); + break; + } + printf("Using %s as default boot template\n", bootFile); + } + if (loadChunk(bootFile, 0, 0, &bootBlob) < 0) + goto cleanup_and_error; + switch (daVolumeKind) { + case _exfat: + if (calcSum(&bootBlob, &bpbBlob, &outputBlob, devicePath) < 0) + goto cleanup_and_error; + break; + case _hfs: + free_buffer(&bpbBlob); + if (bootBlob._s != 1024U) { + fprintf(stderr, "Boot Code size must be 1024 bytes\n"); + goto cleanup_and_error; + } + break; + case _msdos: + if (fat32Layout(&bootBlob, &bpbBlob, &outputBlob) < 0) + goto cleanup_and_error; + break; + default: + assert(0); + break; + } + if (!dontAsk) { + printf("About to write new boot record on %s, Are You Sure (Y/N)?", devicePath); + ch = 0; + while (ch != 'Y' && ch != 'N') + ch = getchar(); + if (ch != 'Y') { + printf("Aborted due to user request\n"); + goto cleanup_and_exit; + } + } + switch (daVolumeKind) { + case _exfat: + writeVBR(devicePath, &outputBlob, 2, 12U * 512U, "exFAT"); + break; + case _hfs: + writeVBR(devicePath, &bootBlob, 1, 1024U, "HFS+"); + break; + case _msdos: + writeVBR(devicePath, &outputBlob, 1, 512U, "FAT32"); + break; + default: + assert(0); + break; + } + +cleanup_and_exit: + cleanup(); + if (isVolumeMounted) + mount(devicePath); + return 0; + +cleanup_and_error: + cleanup(); +remount_and_error: + if (isVolumeMounted) + mount(devicePath); + return -1; + +usage_and_error: + usage(argv[0]); + return -1; +} Index: branches/ErmaC/Enoch/i386/util/boot1-install/Makefile =================================================================== --- branches/ErmaC/Enoch/i386/util/boot1-install/Makefile (revision 0) +++ branches/ErmaC/Enoch/i386/util/boot1-install/Makefile (revision 2498) @@ -0,0 +1,59 @@ +SRCROOT = $(abspath $(CURDIR)/../../..) +OBJROOT = $(SRCROOT)/obj/i386/util/boot1-install +SYMROOT = $(SRCROOT)/sym/i386 +DSTROOT = $(SRCROOT)/dst/i386 +DOCROOT = $(SRCROOT)/doc +IMGROOT = $(SRCROOT)/sym/cache +IMGSKELROOT = $(SRCROOT)/imgskel +CDBOOT = ${IMGROOT}/usr/standalone/i386/cdboot + +DIR = boot1-install + +include ${SRCROOT}/Make.rules + +LDFLAGS := $(LDFALGS) -mmacosx-version-min=10.5 \ +-framework CoreFoundation \ +-framework DiskArbitration \ +-Wl,-no_source_version \ +-Wl,-no_function_starts \ +-Wl,-no_data_in_code_info \ +-Wl,-no_version_load_command \ +-Wl,-no_uuid \ +-Wl,-no_dependent_dr_info + +OBJS = boot1-install.o32 \ + boot1-install.o64 + +OBJS := $(addprefix $(OBJROOT)/, $(OBJS)) + +PROGRAM = boot1-install +PROGRAM:= $(addprefix $(SYMROOT)/, $(PROGRAM)) + +ifeq ($(CONFIG_BOOT1INSTALL),y) + +all: $(SYMROOT) $(OBJROOT) $(PROGRAM) + +$(PROGRAM): $(OBJS) + @echo "\t[LD32] $(@F)_32" + @$(CC) $(CFLAGS) $(LDFLAGS) $(DEFINES) -arch i386 -o $@_32 $(filter %.o32,$^) + @echo "\t[LD64] $(@F)_64" + @$(CC) $(CFLAGS) $(LDFLAGS) $(DEFINES) -arch x86_64 -o $@_64 $(filter %.o64,$^) + @echo "\t[LIPO] $@" + @lipo -create -arch i386 $@_32 -arch x86_64 $@_64 -output $@ + @strip $@ + @rm $@_32 $@_64 + +else + +all: + +endif + + +#dependencies +-include $(OBJROOT)/Makedep + +clean-local: + @for o in $(OBJS); do if [ -f "$${o}" ];then echo " [RM] $${o}"; fi; done + @for p in $(SYMPROG); do if [ -f "$${p}" ];then echo " [RM] $${p}"; fi; done + @rm -f $(SYMPROG) $(OBJS) \ No newline at end of file Index: branches/ErmaC/Enoch/i386/util/Makefile =================================================================== --- branches/ErmaC/Enoch/i386/util/Makefile (revision 2497) +++ branches/ErmaC/Enoch/i386/util/Makefile (revision 2498) @@ -36,7 +36,7 @@ DIRS_NEEDED = $(OBJROOT) $(SYMROOT) -SUBDIRS = fdisk +SUBDIRS = fdisk boot1-install all: $(DIRS_NEEDED) $(SYMPROG) all-recursive Index: branches/ErmaC/Enoch/package/buildpkg.sh =================================================================== --- branches/ErmaC/Enoch/package/buildpkg.sh (revision 2497) +++ branches/ErmaC/Enoch/package/buildpkg.sh (revision 2498) @@ -470,13 +470,16 @@ ditto --noextattr --noqtn ${SYMROOT}/i386/boot ${PKG_BUILD_DIR}/${choiceId}/Root/usr/standalone/i386 ditto --noextattr --noqtn ${SYMROOT}/i386/boot0 ${PKG_BUILD_DIR}/${choiceId}/Root/usr/standalone/i386 ditto --noextattr --noqtn ${SYMROOT}/i386/boot0md ${PKG_BUILD_DIR}/${choiceId}/Root/usr/standalone/i386 + ditto --noextattr --noqtn ${SYMROOT}/i386/boot0xg ${PKG_BUILD_DIR}/${choiceId}/Root/usr/standalone/i386 ditto --noextattr --noqtn ${SYMROOT}/i386/boot1f32 ${PKG_BUILD_DIR}/${choiceId}/Root/usr/standalone/i386 ditto --noextattr --noqtn ${SYMROOT}/i386/boot1h ${PKG_BUILD_DIR}/${choiceId}/Root/usr/standalone/i386 + ditto --noextattr --noqtn ${SYMROOT}/i386/boot1x ${PKG_BUILD_DIR}/${choiceId}/Root/usr/standalone/i386 ditto --noextattr --noqtn ${SYMROOT}/i386/boot1he ${PKG_BUILD_DIR}/${choiceId}/Root/usr/standalone/i386 ditto --noextattr --noqtn ${SYMROOT}/i386/boot1hp ${PKG_BUILD_DIR}/${choiceId}/Root/usr/standalone/i386 ditto --noextattr --noqtn ${SYMROOT}/i386/cdboot ${PKG_BUILD_DIR}/${choiceId}/Root/usr/standalone/i386 ditto --noextattr --noqtn ${SYMROOT}/i386/chain0 ${PKG_BUILD_DIR}/${choiceId}/Root/usr/standalone/i386 ditto --noextattr --noqtn ${SYMROOT}/i386/fdisk440 ${PKG_BUILD_DIR}/${choiceId}/Root/usr/local/bin + ditto --noextattr --noqtn ${SYMROOT}/i386/boot1-install ${PKG_BUILD_DIR}/${choiceId}/Root/usr/local/bin ditto --noextattr --noqtn ${SYMROOT}/i386/bdmesg ${PKG_BUILD_DIR}/${choiceId}/Root/usr/local/bin packageRefId=$(getPackageRefId "${packagesidentity}" "${choiceId}") Index: branches/ErmaC/Enoch/CHANGES =================================================================== --- branches/ErmaC/Enoch/CHANGES (revision 2497) +++ branches/ErmaC/Enoch/CHANGES (revision 2498) @@ -1,3 +1,6 @@ +- Zenith432 : Completed patch for ExFAT support ( http://www.insanelymac.com/forum/topic/302938-exfat-volume-boot-record-for-chameleon ) +- Zenith432 : add EXFAT boot support by Zenith432 +- zenith432 : Merge patch from issue 386 (boot2 does not know how to read files from FAT partitions on GPT) - ErmaC : define recursive cpu series for BrandString - meklort : Update laoder.h to latest, declare gMI global, Load modules passed in via the multiboot header / first bootloader, Fix mboot.h include, Add ?log command to print out bdmesg without needing Wait=y, Add slightly more debugging for modules. Index: branches/ErmaC/Enoch/Makefile =================================================================== --- branches/ErmaC/Enoch/Makefile (revision 2497) +++ branches/ErmaC/Enoch/Makefile (revision 2498) @@ -73,11 +73,16 @@ @cp -f ${SYMROOT}/i386/boot0 ${IMGROOT}/usr/standalone/i386 @cp -f ${SYMROOT}/i386/boot0hfs ${IMGROOT}/usr/standalone/i386 @cp -f ${SYMROOT}/i386/boot0md ${IMGROOT}/usr/standalone/i386 + @cp -f ${SYMROOT}/i386/boot0xg ${IMGROOT}/usr/standalone/i386 @cp -f ${SYMROOT}/i386/boot1h ${IMGROOT}/usr/standalone/i386 @cp -f ${SYMROOT}/i386/boot1f32 ${IMGROOT}/usr/standalone/i386 + @cp -f ${SYMROOT}/i386/boot1x ${IMGROOT}/usr/standalone/i386 ifdef CONFIG_FDISK440 @cp -f ${SYMROOT}/i386/fdisk440 ${IMGROOT}/usr/bin endif +ifdef CONFIG_BOOT1INSTALL + @cp -f ${SYMROOT}/i386/boot1-install ${IMGROOT}/usr/bin +endif ifdef CONFIG_BDMESG @cp -f ${SYMROOT}/i386/bdmesg ${IMGROOT}/usr/bin endif