Chameleon

Chameleon Commit Details

Date:2014-11-29 04:25:16 (9 years 3 months ago)
Author:ErmaC
Commit:2495
Parents: 2494
Message:add EXFAT boot support by Zenith432
Changes:
A/trunk/i386/util/boot1-install
A/trunk/i386/boot0/boot0xg.s
A/trunk/i386/util/boot1-install/Cconfig
A/trunk/i386/util/boot1-install/boot1-install.c
A/trunk/i386/boot1/boot1x.s
A/trunk/i386/util/boot1-install/Makefile
M/trunk/Makefile
M/trunk/i386/util/Cconfig
M/trunk/i386/boot0/boot0hfs.s
M/trunk/i386/boot0/boot0md.s
M/trunk/i386/util/Makefile
M/trunk/package/buildpkg.sh
M/trunk/CHANGES
M/trunk/i386/boot0/Makefile

File differences

trunk/i386/boot0/boot0xg.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
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
;
;
; Turbo added EFI System Partition boot support
;
; Added KillerJK's switchPass2 modifications
;
;
; Set to 1 to enable obscure debug messages.
;
DEBUGEQU CONFIG_BOOT0_DEBUG
;
; Set to 1 to enable verbose mode
;
VERBOSEEQU CONFIG_BOOT0_VERBOSE
;
; Various constants.
;
kBoot0SegmentEQU 0x0000
kBoot0StackEQU 0xFFF0; boot0 stack pointer
kBoot0LoadAddrEQU 0x7C00; boot0 load address
kBoot0RelocAddrEQU 0xE000; boot0 relocated address
kMBRBufferEQU 0x1000; MBR buffer address
kLBA1BufferEQU 0x1200; LBA1 - GPT Partition Table Header buffer address
kGPTABufferEQU 0x1400; GUID Partition Entry Array buffer address
kPartTableOffsetEQU 0x1be
kMBRPartTableEQU kMBRBuffer + kPartTableOffset
kSectorBytesEQU 512; sector size in bytes
kBootSignatureEQU 0xAA55; boot sector signature
kHFSPSignatureEQU 'H+'; HFS+ volume signature
kHFSPCaseSignatureEQU 'HX'; HFS+ volume case-sensitive signature
kFAT32BootCodeOffset EQU 0x5a; offset of boot code in FAT32 boot sector
kBoot1FAT32MagicEQU 'BO'; Magic string to detect our boot1f32 code
kGPTSignatureLowEQU 'EFI '; GUID Partition Table Header Signature
kGPTSignatureHighEQU 'PART'
kGUIDLastDwordOffsEQU 12; last 4 byte offset of a GUID
kPartCountEQU 4; number of paritions per table
kPartTypeHFSEQU 0xaf; HFS+ Filesystem type
kPartTypeABHFSEQU 0xab; Apple_Boot partition
kPartTypePMBREQU 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.
kAppleGUIDEQU 0xACEC4365; last 4 bytes of Apple type GUIDs.
kMicrosoftGUIDEQU 0xC79926B7; last 4 bytes of Microsoft Basic Data Partition Type GUID:
; EBD0A0A2-B9E5-4433-87C0-68B6B72699C7
%ifdef FLOPPY
kDriveNumberEQU 0x00
%else
kDriveNumberEQU 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
;
strucgpth
.Signature resb8
.Revision resb4
.HeaderSizeresb4
.HeaderCRC32resb4
.Reservedresb4
.MyLBAresb8
.AlternateLBAresb8
.FirstUsableLBAresb8
.LastUsableLBAresb8
.DiskGUIDresb16
.PartitionEntryLBAresb8
.NumberOfPartitionEntriesresb4
.SizeOfPartitionEntryresb4
.PartitionEntryArrayCRC32resb4
endstruc
;
; Format of GUID Partition Entry Array
;
strucgpta
.PartitionTypeGUIDresb16
.UniquePartitionGUIDresb16
.StartingLBAresb8
.EndingLBAresb8
.Attributesresb8
.PartitionNameresb72
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
;
moval, 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]
cmpeax, kAppleGUID; check current GUID Partition for Apple's GUID type
je.gpt_ok
;
; Turbo - also try EFI System Partition
;
cmpeax, 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
ordh, dh
jz.checkBootSignature
.checkHFSSignature:
%if 0
%if VERBOSE
LogString(test_str)
%endif
%endif
;
; Looking for HFSPlus ('H+') or HFSPlus case-sensitive ('HX') signature.
;
movax, [kBoot0LoadAddr + 2 * kSectorBytes]
cmp ax, kHFSPSignature; 'H+'
je.checkBootSignature
cmpax, kHFSPCaseSignature; 'HX'
je.checkBootSignature
;
; Looking for exFAT signature
;
movax, [kBoot0LoadAddr + 3]
cmpax, 0x5845; 'EX'
jz.checkBootSignature
;
; Looking for boot1f32 magic string.
;
movax, [kBoot0LoadAddr + kFAT32BootCodeOffset]
cmpax, 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
pushdi
movsi, log_title_str
callprint_string
popsi
callprint_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_strdb 10, 13, 'boot0: ', 0
%if VERBOSE
gpt_strdb 'GPT', 0
%if 0
test_strdb 'test', 0
%endif
done_strdb '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_lbaresd1; Starting LBA for read_lba function
; END
trunk/i386/boot0/boot0hfs.s
2020
2121
2222
23
23
2424
2525
2626
;
; @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
trunk/i386/boot0/boot0md.s
2020
2121
2222
23
23
2424
2525
2626
;
; @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
trunk/i386/boot0/Makefile
1212
1313
1414
15
15
1616
1717
1818
DIRS_NEEDED = $(SYMROOT)
OBJS = boot0 boot0hfs boot0md chain0
OBJS = boot0 boot0hfs boot0md boot0xg chain0
OBJS := $(addprefix $(SYMROOT)/, $(OBJS))
all: $(DIRS_NEEDED) $(OBJS)
trunk/i386/boot1/boot1x.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
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: dq 0
gVolumeLength: dq 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, 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
trunk/i386/util/Cconfig
99
1010
1111
12
13
1214
1315
1416
source "i386/util/fdisk/Cconfig"
source "i386/util/boot1-install/Cconfig"
config OPENUP
bool "openUp utility"
default n
trunk/i386/util/boot1-install/Cconfig
1
2
3
4
5
6
7
8
9
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".
trunk/i386/util/boot1-install/boot1-install.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
/*
* boot1-install.c
* boot1-install
*
* Created by Zenith432 on November 19th, 2014.
* Copyright (c) 2014 Zenith432. All rights reserved.
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <CoreFoundation/CoreFoundation.h>
#include <DiskArbitration/DiskArbitration.h>
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;
}
trunk/i386/util/boot1-install/Makefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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)
trunk/i386/util/Makefile
3636
3737
3838
39
39
4040
4141
4242
DIRS_NEEDED = $(OBJROOT) $(SYMROOT)
SUBDIRS = fdisk
SUBDIRS = fdisk boot1-install
all: $(DIRS_NEEDED) $(SYMPROG) all-recursive
trunk/package/buildpkg.sh
470470
471471
472472
473
473474
474475
476
475477
476478
477479
478480
479481
482
480483
481484
482485
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}")
trunk/CHANGES
1
12
23
34
- 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)
- 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.
trunk/Makefile
7373
7474
7575
76
7677
7778
7879
7980
8081
82
83
84
8185
8286
8387
@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
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

Archive Download the corresponding diff file

Revision: 2495