Chameleon

Chameleon Commit Details

Date:2011-09-13 22:24:40 (8 years 1 month ago)
Author:armel cadet-petit
Commit:1525
Parents: 1524
Message:combo update, see changelog
Changes:
D/branches/cparm/i386/boot1/boot1.s
A/branches/cparm/i386/boot1/boot1h.s
A/branches/cparm/i386/boot0/boot0md.s
M/branches/cparm/i386/modules/HibernateEnabler/resume.c
M/branches/cparm/i386/modules/GraphicsEnabler/nvidia.c
M/branches/cparm/i386/libsaio/platform.h
M/branches/cparm/i386/libsaio/acpi_tools.c
M/branches/cparm/i386/boot1/Makefile
M/branches/cparm/i386/boot2/options.c
M/branches/cparm/i386/libsaio/console.c
M/branches/cparm/i386/modules/HibernateEnabler/IOHibernatePrivate.h
M/branches/cparm/i386/modules/ACPICodec/acpi_codec.c
M/branches/cparm/i386/modules/HibernateEnabler/HibernateEnabler.c
M/branches/cparm/CHANGES
M/branches/cparm/i386/modules/Keymapper/Keymapper.c
M/branches/cparm/Makefile
M/branches/cparm/i386/modules/GraphicsEnabler/gma.c
M/branches/cparm/i386/boot2/mboot.c
M/branches/cparm/i386/libsaio/saio_types.h
M/branches/cparm/i386/libsaio/cpu.c
M/branches/cparm/i386/libsaio/fake_efi.c
M/branches/cparm/i386/modules/ACPIPatcher/AcpiPatcher.c
M/branches/cparm/i386/boot0/Makefile
M/branches/cparm/i386/modules/GUI/gui.c
M/branches/cparm/i386/boot2/Makefile
M/branches/cparm/i386/libsaio/ppm.h
M/branches/cparm/i386/boot0/boot0.s
M/branches/cparm/i386/libsaio/smp-imps.c
M/branches/cparm/i386/boot2/boot.c
M/branches/cparm/TODO
M/branches/cparm/i386/modules/ACPICodec/ACPICodec.c
M/branches/cparm/i386/boot2/modules.c
M/branches/cparm/i386/boot2/boot.h

File differences

branches/cparm/TODO
11
22
33
4
5
46
57
68
TODO List for Chameleon Boot Loader
====================================
- Implement a Host like in bits to avoid some return issues
- Implement snprintf to avoid buffer overflow in some case
- It seems that nvram variables must be set thru efiRuntimeServices->SetVariable(...),
branches/cparm/CHANGES
1
2
3
4
5
6
7
8
9
10
111
212
313
......
515
616
717
8
18
919
1020
11
21
1222
1323
1424
......
1626
1727
1828
19
29
2030
2131
2232
- Updated keymapper ...
- Applied scorpius's patch (to get an unstretched boot screen for nVidia cards on DVI) in boot0.s , disabled by default,
see - http://forum.voodooprojects.org/index.php/topic,2158.msg10345/boardseen.html#new - for more info.
- Applied Vladimir Zidar's hibernate patch on hibernateEnabler
- Applied "Intel HD Graphics 3000" patch to gma.c (credit: ???) ,
found at - http://www.darwinx86.net/forum/61-section-francaise-/1880-espace-de-discussion-anval?limit=10&start=60#6247 -
- Added AMD support (most of the code is derived from the trunk, MUST BE ENABLED BY HAND IN CPU.C) see - http://forge.voodooprojects.org/p/chameleon/issues/163/ -
- Improved Core Ix support in AcpiCodec
- Improved c-states support in AcpiCodec
- Sync'd nvidia.c and gma.c (see GraphicEnabler module) to the trunk
- Improved compatibility with xcode4 (afaik only the modules gui and smbiosgetters will not work with xcode4, even if they are built without any errors)
- Applied JrCs's path for more protection against buffer overflow in some case (trunk v1449)
- Moved smp code to Erich Boleyn's smp-imps (not fully implemented yet)
- Added the UseKernelCache option only for compatibility with the trunk ("Yes" have no effect, since it's the default),
the Flag "-F" which do the same things is still valid
- Re-worked SSDT generation, and merged with the Intel's code
- Re-worked SSDT generation, and merged with the Bits's code
- Removed aml_generator from AcpiCodec, now use acpicode.c to generate aml file
- Added YellowIconFixer module
- Merged fsb detection with Intel's code in cpu.c (slightly faster)
- Merged fsb detection with Bits's code in cpu.c (slightly faster)
- Fixed a bug related to ACPI in AcpiCodec (thanck to StephN666 for testing)
- Fixed a bug related to ACPI and the machine with a rsd table revision > 0 (acpi_tools.c) (thanck to StephN666 for testing)
- Fixed an issue with kernelPatcher, it seems that kernelPatcher is unable to patch the kernelCache
- Applied some fixes from the trunk
- Applied many under the hood fixes
- Added Andy Vandijck Server spoof
- Added Andy Vandijck's Server spoof
- Added a Patch from Andy Vandijck in SMBiosPatcher and SMBiosGetters
- Added a Patch from Netkas in GraphicEnabler (ati.c)
- Fixed an issue where the GUI module couldn't find the themes directory
branches/cparm/i386/libsaio/console.c
137137
138138
139139
140
140
141141
142142
143143
{
int c = bgetc();
execute_hook("Keymapper", &c, NULL, NULL, NULL, NULL, NULL);
//execute_hook("Keymapper", &c, NULL, NULL, NULL, NULL, NULL);
if ((c & 0xff) == 0)
return c;
branches/cparm/i386/libsaio/smp-imps.c
244244
245245
246246
247
248
249
247
248
249
250250
251251
252252
* under the 1MB boundary.
*/
//extern char patch_code_end[];
//bootaddr = (512-64)*1024;
//memcpy((char *)bootaddr, patch_code_start, 0x5fe00);
//extern char ???[];
//bootaddr = (512-64)*1024; ???
//memcpy((char *)bootaddr, ,??? );
/*
* Generic CPU startup sequence starts here.
branches/cparm/i386/libsaio/cpu.c
1717
1818
1919
20
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
2147
2248
2349
......
146172
147173
148174
149
175
176
150177
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
151238
152239
153240
......
184271
185272
186273
187
188
189
190
274
275
276
277
191278
192279
193280
194281
195
196
197
198
282
283
284
285
199286
200287
201288
......
319406
320407
321408
322
409
323410
324411
325
326412
413
327414
328415
329416
......
335422
336423
337424
338
425
339426
340427
341428
......
351438
352439
353440
354
441
355442
356443
357444
358445
359446
360
447
361448
362449
363450
......
386473
387474
388475
389
476
390477
391478
392479
393480
394481
482
395483
396484
397485
398
399
486
487
488
489
400490
401491
402492
......
406496
407497
408498
499
500
501
502
503
504
505
506
409507
410508
411509
......
416514
417515
418516
419
517
420518
421
519
422520
423
424
425
426
427
428
429
430
431
432521
522
523
433524
434525
526
527
528
529
530
531
532
533
534
535
435536
436
537
437538
438539
439540
440541
441
542
543
442544
545
443546
444547
445
548
549
446550
447551
448552
......
453557
454558
455559
456
560
457561
458562
459563
460
461
462
463
464
564
565
566
567
568
465569
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
490594
491
595
492596
493597
494
495
598
599
496600
497601
498602
499603
500
604
605
606
501607
502
503
504
505
506
507
608
508609
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
509662
510
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
511700
512701
513702
......
533722
534723
535724
536
725
537726
538727
539728
......
567756
568757
569758
570
759
571760
572761
573762
......
582771
583772
584773
585
774
586775
587776
588777
......
604793
605794
606795
607
796
608797
609798
610799
......
617806
618807
619808
620
809
810
621811
622
812
813
623814
624815
625816
......
642833
643834
644835
836
837
645838
646839
647840
......
661854
662855
663856
664
857
665858
666859
667860
668
669
861
862
670863
671864
672865
......
688881
689882
690883
691
884
692885
693886
694
887
888
889
695890
696891
697892
698893
699
700
701
702
703
894
704895
705896
706897
707
898
899
900
708901
709902
710
711
712
713
714
715
716
717
903
904
905
906
907
908
909
910
911
912
913
914
915
718916
719917
720918
721
919
920
722921
922
723923
724
924
725925
726926
727927
#define DBG(x...)msglog(x)
#endif
#if OLD_STYLE
//#define AMD_SUPPORT
#ifndef INTEL_SUPPORT
#define INTEL_SUPPORT 0 //Default (0: nolegacy, 1 : legacy)
#endif
#ifdef AMD_SUPPORT
#ifdef LEGACY_CPU
#undef LEGACY_CPU
#endif
#ifdef INTEL_SUPPORT
#undef INTEL_SUPPORT
#endif
#define LEGACY_CPU 1
#endif
#ifdef INTEL_SUPPORT
#ifdef LEGACY_CPU
#undef LEGACY_CPU
#endif
#define LEGACY_CPU INTEL_SUPPORT
#endif
// (?) : if AMD_SUPPORT then LEGACY_CPU = 1, INTEL_SUPPORT = disabled
// else LEGACY_CPU = INTEL_SUPPORT
#if LEGACY_CPU
static uint64_t measure_tsc_frequency(void);
// DFE: enable_PIT2 and disable_PIT2 come from older xnu
return retval;
}
#else
#ifdef AMD_SUPPORT
#define MSR_AMD_APERF 0x000000E8
/*
* Original comment/code:
* "DFE: Measures the Max Performance Frequency in Hz (64-bit)"
*
* Measures the Actual Performance Frequency in Hz (64-bit)
* (just a naming change, mperf --> aperf )
*/
static uint64_t measure_aperf_frequency(void)
{
uint64_t aperfStart;
uint64_t aperfEnd;
uint64_t aperfDelta = 0xffffffffffffffffULL;
unsigned long pollCount;
uint64_t retval = 0;
int i;
/* Time how many APERF ticks elapse in 30 msec using the 8254 PIT
* counter 2. We run this loop 3 times to make sure the cache
* is hot and we take the minimum delta from all of the runs.
* That is to say that we're biased towards measuring the minimum
* number of APERF ticks that occur while waiting for the timer to
* expire.
*/
for(i = 0; i < 10; ++i)
{
enable_PIT2();
set_PIT2_mode0(CALIBRATE_LATCH);
aperfStart = rdmsr64(MSR_AMD_APERF);
pollCount = poll_PIT2_gate();
aperfEnd = rdmsr64(MSR_AMD_APERF);
/* The poll loop must have run at least a few times for accuracy */
if (pollCount <= 1)
continue;
/* The TSC must increment at LEAST once every millisecond.
* We should have waited exactly 30 msec so the APERF delta should
* be >= 30. Anything less and the processor is way too slow.
*/
if ((aperfEnd - aperfStart) <= CALIBRATE_TIME_MSEC)
continue;
// tscDelta = MIN(tscDelta, (tscEnd - tscStart))
if ( (aperfEnd - aperfStart) < aperfDelta )
aperfDelta = aperfEnd - aperfStart;
}
/* mperfDelta is now the least number of MPERF ticks the processor made in
* a timespan of 0.03 s (e.g. 30 milliseconds)
*/
if (aperfDelta > (1ULL<<32))
retval = 0;
else
{
retval = aperfDelta * 1000 / 30;
}
disable_PIT2();
return retval;
}
#endif
#endif
/*
License for x2apic_enabled, get_apicbase, compute_bclk.
Copyright (c) 2010, Intel Corporation
static inline __attribute__((always_inline)) void rdmsr32(uint32_t msr, uint32_t * lo_data_addr, uint32_t * hi_data_addr)
{
__asm__ volatile(
"rdmsr"
: "=a" (*lo_data_addr), "=d" (*hi_data_addr)
: "c" (msr)
);
"rdmsr"
: "=a" (*lo_data_addr), "=d" (*hi_data_addr)
: "c" (msr)
);
}
static inline __attribute__((always_inline)) void wrmsr32(uint32_t msr, uint32_t lo_data, uint32_t hi_data)
{
__asm__ __volatile__ (
"wrmsr"
: /* No outputs */
: "c" (msr), "a" (lo_data), "d" (hi_data)
);
"wrmsr"
: /* No outputs */
: "c" (msr), "a" (lo_data), "d" (hi_data)
);
}
#define MSR_APIC_BASE 0x1B
#define APIC_TMR_INITIAL_CNT 0x380
// Round bclk to the nearest 100/12 integer value
bclk = ((((bclk * 24) + 100) / 200) * 200) / 24;
DBG("\nCompute bclk: %dMHz\n", bclk);
return bclk;
}
#endif
/*
* Calculates the FSB and CPU frequencies using specific MSRs for each CPU
* - multi. is read from a specific MSR. In the case of Intel, there is:
void scan_cpu(PlatformInfo_t *p)
{
uint64_ttscFrequency, fsbFrequency, cpuFrequency;
uint64_ttscFrequency = 0, fsbFrequency = 0, cpuFrequency = 0;
uint64_tmsr;
uint8_tmaxcoef = 0, maxdiv = 0, currcoef = 0, currdiv = 0;
uint32_treg[4];
/* get extended cpuid results */
do_cpuid(0x80000000, reg);
uint32_t cpuid_max_ext = reg[eax];
p->CPU.cpuid_max_ext = reg[eax];
/* Begin of Copyright: from Apple's XNU cpuid.c */
/* get brand string (if supported) */
if (cpuid_max_ext > 0x80000004)
if (p->CPU.cpuid_max_ext > 0x80000004)
{
char str[128], *s;
/*
p->CPU.BrandString[0] = '\0';
}
}
/*
* Get processor signature and decode
* and bracket this with the approved procedure for reading the
* the microcode version number a.k.a. signature a.k.a. BIOS ID
*/
#ifndef AMD_SUPPORT
wrmsr64(MSR_IA32_BIOS_SIGN_ID, 0);
do_cpuid(1, reg);
p->CPU.MicrocodeVersion =
(uint32_t) (rdmsr64(MSR_IA32_BIOS_SIGN_ID) >> 32);
(uint32_t) (rdmsr64(MSR_IA32_BIOS_SIGN_ID) >> 32);
#else
do_cpuid(1, reg);
#endif
p->CPU.Signature = reg[eax];
p->CPU.Stepping = bitfield(reg[eax], 3, 0);
p->CPU.Model = bitfield(reg[eax], 7, 4);
p->CPU.Brand = bitfield(reg[ebx], 7, 0);
p->CPU.Features = quad(reg[ecx], reg[edx]);
if (p->CPU.cpuid_max_ext >= 0x80000001)
{
do_cpuid(0x80000001, reg);
p->CPU.ExtFeatures =
quad(reg[ecx], reg[edx]);
}
/* Fold extensions into family/model */
if (p->CPU.Family == 0x0f)
p->CPU.Family += p->CPU.ExtFamily;
logical_per_package =
bitfield(reg[ebx], 23, 16);
else
logical_per_package = 1;
logical_per_package = 1;
if (cpuid_max_ext >= 0x80000001)
if (p->CPU.cpuid_max_ext >= 0x80000007)
{
do_cpuid(0x80000001, reg);
p->CPU.ExtFeatures =
quad(reg[ecx], reg[edx]);
}
/* Fold in the Invariant TSC feature bit, if present */
if (cpuid_max_ext >= 0x80000007)
{
do_cpuid(0x80000007, reg);
/* Fold in the Invariant TSC feature bit, if present */
p->CPU.ExtFeatures |=
reg[edx] & (uint32_t)CPUID_EXTFEATURE_TSCI;
#ifdef AMD_SUPPORT
/* Fold in the Hardware P-State control feature bit, if present */
p->CPU.ExtFeatures |=
reg[edx] & (uint32_t)_Bit(7);
/* Fold in the read-only effective frequency interface feature bit, if present */
p->CPU.ExtFeatures |=
reg[edx] & (uint32_t)_Bit(10);
#endif
}
if (p->CPU.cpuid_max_basic >= 0x5) {
/*
* Extract the Monitor/Mwait Leaf info:
*/
do_cpuid(5, reg);
do_cpuid(5, reg);
#ifndef AMD_SUPPORT
p->CPU.sub_Cstates = reg[edx];
#endif
p->CPU.extensions = reg[ecx];
}
#ifndef AMD_SUPPORT
if (p->CPU.cpuid_max_basic >= 0x6)
{
/*
p->CPU.invariant_APIC_timer = bitfield(reg[eax], 2, 2); // "Invariant APIC Timer"
p->CPU.fine_grain_clock_mod = bitfield(reg[eax], 4, 4);
}
if ((p->CPU.Vendor == 0x756E6547 /* Intel */) &&
(p->CPU.Family == 0x06))
{
/*
* Find the number of enabled cores and threads
* (which determines whether SMT/Hyperthreading is active).
*/
switch (p->CPU.Model)
/*
* Find the number of enabled cores and threads
* (which determines whether SMT/Hyperthreading is active).
*/
switch (p->CPU.Model)
{
case CPUID_MODEL_DALES_32NM:
case CPUID_MODEL_WESTMERE:
case CPUID_MODEL_WESTMERE_EX:
{
msr = rdmsr64(MSR_CORE_THREAD_COUNT);
p->CPU.NoThreads = bitfield((uint32_t)msr, 15, 0);
p->CPU.NoCores = bitfield((uint32_t)msr, 19, 16);
break;
}
case CPUID_MODEL_NEHALEM:
case CPUID_MODEL_FIELDS:
case CPUID_MODEL_DALES:
case CPUID_MODEL_NEHALEM_EX:
case CPUID_MODEL_SANDYBRIDGE:
case CPUID_MODEL_JAKETOWN:
{
msr = rdmsr64(MSR_CORE_THREAD_COUNT);
p->CPU.NoThreads = bitfield((uint32_t)msr, 15, 0);
p->CPU.NoCores = bitfield((uint32_t)msr, 31, 16);
break;
}
}
case CPUID_MODEL_DALES_32NM:
case CPUID_MODEL_WESTMERE:
case CPUID_MODEL_WESTMERE_EX:
{
msr = rdmsr64(MSR_CORE_THREAD_COUNT);
p->CPU.NoThreads = bitfield((uint32_t)msr, 15, 0);
p->CPU.NoCores = bitfield((uint32_t)msr, 19, 16);
break;
}
case CPUID_MODEL_NEHALEM:
case CPUID_MODEL_FIELDS:
case CPUID_MODEL_DALES:
case CPUID_MODEL_NEHALEM_EX:
case CPUID_MODEL_SANDYBRIDGE:
case CPUID_MODEL_JAKETOWN:
{
msr = rdmsr64(MSR_CORE_THREAD_COUNT);
p->CPU.NoThreads = bitfield((uint32_t)msr, 15, 0);
p->CPU.NoCores = bitfield((uint32_t)msr, 31, 16);
break;
}
}
}
#endif
if (p->CPU.NoCores == 0)
{
p->CPU.NoThreads = cores_per_package;
p->CPU.NoCores = logical_per_package;
p->CPU.NoThreads = logical_per_package;
p->CPU.NoCores = cores_per_package ? cores_per_package : 1 ;
}
/* End of Copyright: from Apple's XNU cpuid.c */
#if OLD_STYLE
fsbFrequency = (uint64_t)(compute_bclk() * 1000000);
#if LEGACY_CPU
tscFrequency = measure_tsc_frequency();
fsbFrequency = 0;
#else
tscFrequency = 0;
fsbFrequency = (uint64_t)(compute_bclk() * 1000000);
#endif
cpuFrequency = 0;
#endif
#ifdef AMD_SUPPORT
#define K8_FIDVID_STATUS0xC0010042
#define K10_COFVID_STATUS0xC0010071
if (p->CPU.ExtFeatures & _Bit(10))
{
cpuFrequency = measure_aperf_frequency();
}
if ((p->CPU.Vendor == 0x68747541 /* AMD */) && (p->CPU.Family == 0x0f))
{
switch(p->CPU.ExtFamily)
{
case 0x00: /* K8 */
msr = rdmsr64(K8_FIDVID_STATUS);
maxcoef = bitfield(msr, 21, 16) / 2 + 4;
currcoef = bitfield(msr, 5, 0) / 2 + 4;
break;
case 0x01: /* K10 */
{
//uint32_t reg[4];
msr = rdmsr64(K10_COFVID_STATUS);
/*
do_cpuid2(0x00000006, 0, reg);
EffFreq: effective frequency interface
if (bitfield(reg[ecx], 0, 0) == 1)
{
uint64_t aperf = measure_aperf_frequency();
cpuFrequency = aperf;
}
*/
// NOTE: tsc runs at the maccoeff (non turbo)
//*not* at the turbo frequency.
maxcoef = bitfield(msr, 54, 49) / 2 + 4;
currcoef = bitfield(msr, 5, 0) + 0x10;
currdiv = 2 << bitfield(msr, 8, 6);
break;
}
case 0x05: /* K14 */
msr = rdmsr64(K10_COFVID_STATUS);
currcoef = (bitfield(msr, 54, 49) + 0x10) << 2;
currdiv = (bitfield(msr, 8, 4) + 1) << 2;
currdiv += bitfield(msr, 3, 0);
break;
case 0x02: /* K11 */
DBG("K11 detected, but not supported !!!\n");
// not implimented
break;
}
if ((p->CPU.Vendor == 0x756E6547 /* Intel */) &&
if (!fsbFrequency)
{
if (maxcoef)
{
if (currdiv)
{
if (!currcoef) currcoef = maxcoef;
if (!cpuFrequency)
fsbFrequency = ((tscFrequency * currdiv) / currcoef);
else
fsbFrequency = ((cpuFrequency * currdiv) / currcoef);
DBG("%d.%d\n", currcoef / currdiv, ((currcoef % currdiv) * 100) / currdiv);
} else {
if (!cpuFrequency)
fsbFrequency = (tscFrequency / maxcoef);
else
fsbFrequency = (cpuFrequency / maxcoef);
DBG("%d\n", currcoef);
}
}
else if (currcoef)
{
if (currdiv)
{
fsbFrequency = ((tscFrequency * currdiv) / currcoef);
DBG("%d.%d\n", currcoef / currdiv, ((currcoef % currdiv) * 100) / currdiv);
} else {
fsbFrequency = (tscFrequency / currcoef);
DBG("%d\n", currcoef);
}
}
}
}
#else
if ((p->CPU.Vendor == 0x756E6547 /* Intel */) &&
((p->CPU.Family == 0x06) ||
(p->CPU.Family == 0x0f)))
{
DBG("msr(%d): platform_info %08x\n", __LINE__, msr & 0xffffffff);
#endif
bus_ratio_max = (msr >> 8) & 0xff;
bus_ratio_min = (msr >> 40) & 0xff; //valv: not sure about this one (Remarq.1)
bus_ratio_min = (msr >> 40) & 0xff;
msr = rdmsr64(MSR_FLEX_RATIO);
#if DEBUG_CPU
DBG("msr(%d): flex_ratio %08x\n", __LINE__, msr & 0xffffffff);
}
}
}
#if OLD_STYLE
#if LEGACY_CPU
if (bus_ratio_max)
{
fsbFrequency = (tscFrequency / bus_ratio_max);
}
else
{
#if OLD_STYLE
#if LEGACY_CPU
cpuFrequency = tscFrequency;
#else
cpuFrequency = bus_ratio_max * fsbFrequency;
maxdiv = (msr >> 46) & 0x01;
/* Non-integer bus ratio for the current-multi (undocumented)*/
currdiv = (msr >> 14) & 0x01;
if ((p->CPU.Family == 0x06 && p->CPU.Model >= 0x0e) ||
(p->CPU.Family == 0x0f)) // This will always be model >= 3
{
/* XXX */
maxcoef = currcoef;
}
#if OLD_STYLE
if (!currcoef) currcoef = maxcoef;
#if LEGACY_CPU
if (maxcoef)
{
{
if (maxdiv)
{
fsbFrequency = ((tscFrequency * 2) / ((maxcoef * 2) + 1));
#endif
}
#else
if (currdiv)
{
cpuFrequency = (fsbFrequency * ((currcoef * 2) + 1) / 2);
{
tscFrequency = fsbFrequency * maxcoef;
}
}
}
#if DEBUG_CPU
DBG("max: %d%s current: %d%s\n", maxcoef, maxdiv ? ".5" : "",currcoef, currdiv ? ".5" : "");
#endif
#endif // OLD_STYLE
#endif // LEGACY_CPU
}
}
p->CPU.isMobile = (rdmsr64(0x17) & (1 << 28));
break;
}
// TODO: this part of code seems to work very well for the intel platforms, need to find the equivalent for AMD
DBG("%s platform found.\n", p->CPU.isMobile?"Mobile":"Desktop");
}
#endif
if (!cpuFrequency) cpuFrequency = tscFrequency;
p->CPU.MaxCoef = maxcoef;
p->CPU.MaxDiv = maxdiv;
p->CPU.CurrCoef = currcoef;
p->CPU.CurrDiv = currdiv;
//p->CPU.TSCFrequency = (tscFrequency / 1000000) * 1000000;
//p->CPU.FSBFrequency = (fsbFrequency / 1000000) * 1000000;
//p->CPU.CPUFrequency = (cpuFrequency / 1000000) * 1000000;
p->CPU.TSCFrequency = tscFrequency ;
p->CPU.FSBFrequency = fsbFrequency ;
p->CPU.CPUFrequency = cpuFrequency ;
#ifdef AMD_SUPPORT
msglog("AMD CPU Detection Enabled\n");
#endif
DBG("CPU: Vendor/Model/ExtModel: 0x%x/0x%x/0x%x\n", p->CPU.Vendor, p->CPU.Model, p->CPU.ExtModel);
DBG("CPU: Family/ExtFamily: 0x%x/0x%x\n", p->CPU.Family, p->CPU.ExtFamily);
DBG("CPU: TSCFreq: %dMHz\n", p->CPU.TSCFrequency / 1000000);
if(p->CPU.Vendor == 0x756E6547 /* Intel */)
{
DBG("CPU: FSBFreq: %dMHz\n", p->CPU.FSBFrequency / 1000000);
DBG("CPU: CPUFreq: %dMHz\n", p->CPU.CPUFrequency / 1000000);
DBG("CPU: MaxCoef/CurrCoef: 0x%x/0x%x\n", p->CPU.MaxCoef, p->CPU.CurrCoef);
DBG("CPU: MaxDiv/CurrDiv: 0x%x/0x%x\n", p->CPU.MaxDiv, p->CPU.CurrDiv);
}
#ifdef AMD_SUPPORT
DBG("CPU (AMD): TSCFreq: %dMHz\n", p->CPU.TSCFrequency / 1000000);
DBG("CPU (AMD): FSBFreq: %dMHz\n", p->CPU.FSBFrequency / 1000000);
DBG("CPU (AMD): CPUFreq: %dMHz\n", p->CPU.CPUFrequency / 1000000);
DBG("CPU (AMD): MaxCoef/CurrCoef: 0x%x/0x%x\n", p->CPU.MaxCoef, p->CPU.CurrCoef);
DBG("CPU (AMD): MaxDiv/CurrDiv: 0x%x/0x%x\n", p->CPU.MaxDiv, p->CPU.CurrDiv);
#else
DBG("CPU: TSCFreq: %dMHz\n", p->CPU.TSCFrequency / 1000000);
DBG("CPU: FSBFreq: %dMHz\n", p->CPU.FSBFrequency / 1000000);
DBG("CPU: CPUFreq: %dMHz\n", p->CPU.CPUFrequency / 1000000);
DBG("CPU: MaxCoef/CurrCoef: 0x%x/0x%x\n", p->CPU.MaxCoef, p->CPU.CurrCoef);
DBG("CPU: MaxDiv/CurrDiv: 0x%x/0x%x\n", p->CPU.MaxDiv, p->CPU.CurrDiv);
#endif
DBG("CPU: NoCores/NoThreads: %d/%d\n", p->CPU.NoCores, p->CPU.NoThreads);
DBG("CPU: Features: 0x%08x\n", p->CPU.Features);
DBG("CPU: ExtFeatures: 0x%08x\n", p->CPU.ExtFeatures);
DBG("CPU: ExtFeatures: 0x%08x\n", p->CPU.ExtFeatures);
#ifndef AMD_SUPPORT
DBG("CPU: MicrocodeVersion: %d\n", p->CPU.MicrocodeVersion);
#endif
#if DEBUG_CPU
pause();
pause();
#endif
}
branches/cparm/i386/libsaio/platform.h
131131
132132
133133
134
134135
135136
136137
boolean_tfine_grain_clock_mod;
uint32_t cpuid_max_basic;
uint32_t cpuid_max_ext;
uint32_tsub_Cstates;
uint32_t extensions;
branches/cparm/i386/libsaio/acpi_tools.c
8989
9090
9191
92
92
9393
9494
9595
......
192192
193193
194194
195
195196
196197
197198
198199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
199220
200221
201222
......
210231
211232
212233
234
213235
214236
215
216237
217238
218239
......
259280
260281
261282
262
283
263284
264285
265286
// Init memory address as F000 segment and scan 64KB region
if (!success)
success = GetRsdtPointer((void *)0x0F0000, 0x10000, acpi_tables);
if (!success)
return (0ul);
// Procedure: GetTablePtr64 - Find ACPI table in XSDT with input signature.
//
//-------------------------------------------------------------------------------
#if 0
static ACPI_TABLE_HEADER *GetTablePtr64(ACPI_TABLE_XSDT * xsdt, U32 signature)
{
U32 index;
U32 num_tables;
ACPI_TABLE_HEADER *table = (ACPI_TABLE_HEADER *) xsdt->TableOffsetEntry;
// Compute number of table pointers included in XSDT
num_tables = get_num_tables64(xsdt);
for (index = 0; index < num_tables; index++) {
if (((U32) (table->Signature) == signature) &&
(GetChecksum(table, table->Length) == 0)) {
return (table);
}
// Move array pointer to next 64-bit pointer
table = (ACPI_TABLE_HEADER *) ((U32) table + sizeof(U64));
}
return (0);
}
#else
static ACPI_TABLE_HEADER *GetTablePtr64(ACPI_TABLE_XSDT * xsdt, U32 signature)
{
U32 index;
U32 num_tables;
// Compute number of table pointers included in XSDT
num_tables = get_num_tables64(xsdt);
}
return (0);
}
#endif
//-------------------------------------------------------------------------------
//
// Procedure: GetChecksum - Performs byte checksum
// RSD pointer structure checksum okay, lookup the RSDT pointer.
acpi_tables->RsdPointer = (ACPI_TABLE_RSDP *)current;
acpi_tables->RsdtPointer = (ACPI_TABLE_RSDT *) acpi_tables->RsdPointer->RsdtPhysicalAddress;
if ((acpi_tables->RsdPointer != 0) && (acpi_tables->RsdtPointer != 0))
if ((acpi_tables->RsdPointer != (void*)0ul) && (acpi_tables->RsdtPointer != (void*)0ul))
return (1ul);
else
return (0ul);
branches/cparm/i386/libsaio/ppm.h
138138
139139
140140
141
141142
142143
143
144
144145
145146
146147
......
196197
197198
198199
200
199201
202
200203
201204
202205
typedef enum cpu_cstate {
CPU_C1 = 1,
//CPU_C2 = 2,
CPU_C3_ACPI_C2 = 3,
CPU_C3_ACPI_C3 = 4,
CPU_C3_ACPI_C4 = 5,
CPU_C4 = 5,
CPU_C6 = 6,
CPU_C7 = 7,
} CPU_CSTATE;
U32 package_cstate_limit;
U32 core_c1_supported;
U32 core_c2_supported;
U32 core_c3_supported;
U32 core_c4_supported;
U32 core_c6_supported;
U32 core_c7_supported;
U32 mwait_supported;
branches/cparm/i386/libsaio/saio_types.h
236236
237237
238238
239
240
241
242
243
244
245
246
247
248
249
250239
251240
252241
//#define BIOS_DEV_TYPE(d) ((d) & kBIOSDevTypeMask)
#define BIOS_DEV_UNIT(bvr) ((bvr)->biosdev - (bvr)->type)
/*
* KernBootStruct device types.
*/
/*
enum {
DEV_SD = 0,
DEV_HD = 1,
DEV_FD = 2,
DEV_EN = 3
};
*/
#ifndef max
#define max(a,b) ((a) > (b) ? (a) : (b))
#endif
branches/cparm/i386/libsaio/fake_efi.c
429429
430430
431431
432
432433
434
435
433436
434
435437
436438
437439
......
469471
470472
471473
472
474
473475
474476
475477
......
708710
709711
710712
711
713
714
715
712716
713
714
717
718
715719
716720
717
721
718722
719723
720
724
725
726
721727
722728
723729
......
928934
929935
930936
937
931938
932939
933940
......
939946
940947
941948
942
943
949
950
944951
945952
946
953
947954
948
955
949956
950
951
957
952958
953959
954960
......
970976
971977
972978
973
979
980
974981
975982
976983
*/
/* These should be const but DT__AddProperty takes char* */
#if UNUSED
static const char const TSC_Frequency_prop[] = "TSCFrequency";
static const char const CPU_Frequency_prop[] = "CPUFrequency";
#endif
static const char const FSB_Frequency_prop[] = "FSBFrequency";
static const char const CPU_Frequency_prop[] = "CPUFrequency";
/*==========================================================================
* SMBIOS
{
EFI_STATUS ret = EFI_UNSUPPORTED;
execute_hook("setupEfiConfigurationTable", &ret, NULL, NULL, NULL, NULL, NULL);
execute_hook("setupAcpiEfi", &ret, NULL, NULL, NULL, NULL, NULL);
if (ret != EFI_SUCCESS)
{
DT__AddProperty(chosenNode, "boot-args", strlen(bootArgs->CommandLine)+1, (EFI_CHAR16*)bootArgs->CommandLine);
if (uuidSet &&bootInfo->uuidStr[0])
// "boot-uuid" MAIN GOAL IS SYMPLY TO BOOT FROM THE UUID SET IN THE DT AND DECREASE BOOT TIME, SEE IOKitBSDInit.cpp
// additionally this value can be used by third-party apps or osx components (ex: pre-10.7 kextcache, ...)
if (bootInfo->uuidStr[0])
DT__AddProperty(chosenNode, kBootUUIDKey, strlen(bootInfo->uuidStr)+1, bootInfo->uuidStr);
/*if (gRootPath[0])
#if 0
if (gRootPath[0])
{
DT__AddProperty(chosenNode, "rootpath", 256, gRootPath);
DT__AddProperty(chosenNode, "rootpath" or try "root-matching", strlen(gRootPath)+1, gRootPath);
}
else */if (gRootDevice)
else
#endif
if (gRootDevice)
{
DT__AddProperty(chosenNode, "boot-device-path", strlen(gRootDevice)+1, gRootDevice);
if (smbios_p)
addConfigurationTable(&gEfiSmbiosTableGuid, &smbios_p, NULL);
if (Platform->CPU.Vendor == 0x756E6547 /* Intel */)
{
int num_cpus;
addConfigurationTable(&gEfiMpsTableGuid, &mps, NULL);
}
#if DEBUG_ACPI
#if DEBUG_EFI
if (num_cpus != Platform->CPU.NoCores)
{
DBG("Warning: SMP nb of core mismatch with the value found in cpu.c \n");
printf("Warning: SMP nb of core (%d) mismatch with the value found in cpu.c (%d) \n",num_cpus,Platform->CPU.NoCores);
}
#endif
#endif
}
// PM_Model
if (Platform->CPU.isServer == true)
{
setup_acpi();
setup_machine_signature();
// We now have to write the systemm-type in ioregs: we cannot do it before in setupDeviceTree()
// We now have to write the system-type in ioregs: we cannot do it before in setupDeviceTree()
// because we need to take care of facp original content, if it is correct.
setupSystemType();
branches/cparm/i386/boot0/boot0.s
6060
6161
6262
63
64
65
66
67
6368
6469
6570
......
230235
231236
232237
238
239
240
241
233242
234243
235244
......
775784
776785
777786
778
787
779788
789
790
791
792
793
794
795
796
797
798
799
800
780801
781802
782803
783804
784805
806
807
808
809
785810
786811
787812
VERBOSEEQU 1
;
; Set to 1 to enable unstretch mode (WARNING: Verbose and Debug must be disabled)
;
UNSTRETCHEQU 0
;
; Various constants.
;
kBoot0SegmentEQU 0x0000
call print_hex
%endif
%if UNSTRETCH
call disable_scaler
%endif
;
; Since this code may not always reside in the MBR, always start by
; loading the MBR to kMBRBuffer and LBA1 to kGPTBuffer.
ret
%endif ;DEBUG
%if UNSTRETCH
;--------------------------------------------------------------------------
; Disable On-Chip Scaling for nVidia Cards
;
disable_scaler:
mov ax,4F14h ;VESA VBE OEM function
mov bl,2 ;Subfunction 02 = Set Panel Expansion/Centering
mov bh,1 ;00 = Return Current Setting, 01 = Set Centering/Expansion
mov cx,0001h ;Exp. mode: 00 = Scaled, 01 = Centered 1:1, 02 = Left Corner 1:1
int 10h ;call VGA/VBE service
LogString(nv_scaler_str)
ret
%endif
;--------------------------------------------------------------------------
; NULL terminated strings.
;
log_title_strdb 10, 13, 'boot0: ', 0
boot_error_str db 'error', 0
%if UNSTRETCH
nv_scaler_strdb 'Unstretch', 0
%endif ;DEBUG
%if VERBOSE
gpt_strdb 'GPT', 0
test_strdb 'test', 0
branches/cparm/i386/boot0/boot0md.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
819
820
; 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: boot0
;
; 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, boot0 is
; restricted to 446 bytes (512 - 64 - 2). If boot0 did not have to
; live in the MBR, then we would have 510 bytes to work with.
;
; boot0 is always loaded by the BIOS or another booter to 0:7C00h.
;
; This code is written for the NASM assembler.
; nasm boot0.s -o boot0
;
; This version of boot0 implements hybrid GUID/MBR partition scheme support
;
;
; Turbo added EFI System Partition boot support
;
; Added KillerJK's switchPass2 modifications
;
; dmazar: 10/7/2011 added scanning of all BIOS accessible drives:
; - iterates over all drives and searches for HSF bootable partition (with boot1h)
; and loads from it
; - if not found, itarates over drives again and searches for active partition and
; loads from it
;
;
; Set to 1 to enable obscure debug messages.
;
DEBUGEQU 0
;
; Set to 1 to enable verbose mode
;
VERBOSEEQU 1
;
; 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
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.
kEFISystemGUIDEQU 0x3BC93EC9; last 4 bytes of EFI System Partition Type GUID:
; C12A7328-F81F-11D2-BA4B-00A0C93EC93B
%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:
pushdx; save dl (boot drive) for second pass.
; will stay on stack if booter loaded in first pass.
; this should not be a problem
movbh, 1; BH = 1. two pass scanning (active or hfs partition).
; actuall use of it (scanning) is in find_boot
scan_drives:
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.
;
pushbx; save BH (scan pass counter)
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
popbx; restore BH
jc .mbr_load_error; MBR load error - normally because we scanned all drives
;
; 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
; if returns - booter partition not found
; try next drive
; if next drive does not exists - will break on above MBR load error
incdl
jmpscan_drives
.mbr_load_error:
; all drives scanned - see if we need to run second pass
popdx; restore orig boot drive
decbh; decrement scan pass counter
jzscan_drives; if zero - run seccond pass
; we ran two passes - nothing found - error
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.
; BH = pass counter (1=first pass, 0=second pass)
;
; 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 bl, bl; BL will be set to 1 later in case of
; Protective MBR has been found
.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.type], kPartTypeHFS; In pass 1 we're going to find a HFS+ partition
; equipped with boot1h in its boot record
; regardless if it's active or not.
jne .continue
movdh, 1 ; Argument for loadBootSector to check HFS+ partition signature.
jmp .tryToBoot
.Pass2:
cmp BYTE [si + part.bootid], kPartActive; In pass 2 we are walking on the standard path
; by trying to hop on the active partition.
jne .continue
xordh, dh ; Argument for loadBootSector to skip HFS+ partition
; signature check.
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 .exit; didn't find Protective MBR before
call checkGPT
.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, kEFISystemGUID; check current GUID Partition for EFI System 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
movdh, 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 VERBOSE
;LogString(test_str); dmazar: removed to get space
%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 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 (sucess 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
boot_error_str db 'error', 0
%if VERBOSE
gpt_strdb 'GPT', 0
;test_strdb 'test', 0
done_strdb 'done', 0
%endif
;--------------------------------------------------------------------------
; 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
;
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
branches/cparm/i386/boot0/Makefile
66
77
88
9
9
1010
1111
1212
......
1717
1818
1919
20
21
22
2023
21
24
2225
2326
2427
INSTALLDIR = $(DSTROOT)/usr/standalone/i386
DIRS_NEEDED = $(SYMROOT)
all embedtheme: $(DIRS_NEEDED) boot0 boot0hfs chain0
all embedtheme: $(DIRS_NEEDED) boot0 boot0hfs chain0 boot0md
boot0: boot0.s Makefile $(NASM)
$(NASM) boot0.s -o $(SYMROOT)/$@
chain0: chain0.s Makefile $(NASM)
$(NASM) chain0.s -o $(SYMROOT)/$@
boot0md: boot0md.s Makefile $(NASM)
$(NASM) boot0md.s -o $(SYMROOT)/$@
install_i386:: all $(INSTALLDIR)
cp $(SYMROOT)/boot0 $(SYMROOT)/chain0 $(INSTALLDIR)
cp $(SYMROOT)/boot0 $(SYMROOT)/chain0 $(SYMROOT)/boot0md $(INSTALLDIR)
cd $(INSTALLDIR); chmod u+w boot0
include ../MakeInc.dir
branches/cparm/i386/boot1/boot1.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
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
; 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@
;
; Partition Boot Loader: boot1h
;
; This program is designed to reside in sector 0+1 of an HFS+ partition.
; It expects that the MBR has left the drive number in DL
; and a pointer to the partition entry in SI.
;
; This version requires a BIOS with EBIOS (LBA) support.
;
; This code is written for the NASM assembler.
; nasm boot1.s -o boot1h
;
; This version of boot1h tries to find a stage2 boot file in the root folder.
;
; NOTE: this is an experimental version with multiple extent support.
;
;
;
; Set to 1 to enable obscure debug messages.
;
DEBUGEQU0
;
; Set to 1 to enable unused code.
;
UNUSEDEQU0
;
; Set to 1 to enable verbose mode.
;
VERBOSEEQU1
;
; Various constants.
;
NULL EQU0
CREQU0x0D
LFEQU0x0A
mallocStartEQU0x1000; start address of local workspace area
maxSectorCountEQU64; maximum sector count for readSectors
maxNodeSizeEQU16384
kSectorBytesEQU512; sector size in bytes
kBootSignatureEQU0xAA55; boot sector signature
kBoot1StackAddressEQU0xFFF0; boot1 stack pointer
kBoot1LoadAddrEQU0x7C00; boot1 load address
kBoot1RelocAddrEQU0xE000; boot1 relocated address
kBoot1Sector1AddrEQUkBoot1RelocAddr + kSectorBytes; boot1 load address for sector 1
kHFSPlusBufferEQUkBoot1Sector1Addr + kSectorBytes; HFS+ Volume Header address
kBoot2SectorsEQU(448 * 1024 - 512) / kSectorBytes; max size of 'boot' file in sectors
kBoot2SegmentEQU0x2000; boot2 load segment
kBoot2AddressEQUkSectorBytes; boot2 load address
;
; Format of fdisk partition entry.
;
; The symbol 'part_size' is automatically defined as an `EQU'
; giving the size of the structure.
;
struc part
.bootidresb 1; bootable or not
.headresb 1; starting head, sector, cylinder
.sectresb 1;
.cylresb 1;
.typeresb 1; partition type
.endheadresb 1; ending head, sector, cylinder
.endsectresb 1;
.endcylresb 1;
.lbaresd 1; starting lba
.sectorsresd 1; size in sectors
endstruc
;-------------------------------------------------------------------------
; HFS+ related structures and constants
;
kHFSPlusSignatureEQU'H+'; HFS+ volume signature
kHFSPlusCaseSignatureEQU'HX'; HFS+ volume case-sensitive signature
kHFSPlusCaseSigXEQU'X'; upper byte of HFS+ volume case-sensitive signature
kHFSPlusExtentDensityEQU8; 8 extent descriptors / extent record
;
; HFSUniStr255
;
strucHFSUniStr255
.lengthresw1
.unicoderesw255
endstruc
;
; HFSPlusExtentDescriptor
;
strucHFSPlusExtentDescriptor
.startBlockresd1
.blockCountresd1
endstruc
;
; HFSPlusForkData
;
strucHFSPlusForkData
.logicalSizeresq1
.clumpSizeresd1
.totalBlocksresd1
.extentsresbkHFSPlusExtentDensity * HFSPlusExtentDescriptor_size
endstruc
;
; HFSPlusVolumeHeader
;
strucHFSPlusVolumeHeader
.signatureresw1
.versionresw1
.attributesresd1
.lastMountedVersion resd1
.journalInfoBlockresd1
.createDateresd1
.modifyDateresd1
.backupDateresd1
.checkedDateresd1
.fileCountresd1
.folderCountresd1
.blockSizeresd1
.totalBlocksresd1
.freeBlocksresd1
.nextAllocationresd1
.rsrcClumpSizeresd1
.dataClumpSizeresd1
.nextCatalogIDresd1
.writeCountresd1
.encodingsBitmapresq1
.finderInforesd8
.allocationFileresbHFSPlusForkData_size
.extentsFileresbHFSPlusForkData_size
.catalogFileresbHFSPlusForkData_size
.attributesFileresbHFSPlusForkData_size
.startupFileresbHFSPlusForkData_size
endstruc
;
; B-tree related structures and constants
;
kBTIndexNodeEQU0
kBTMaxRecordLengthEQU264; sizeof(kHFSPlusFileThreadRecord)
kHFSRootParentIDEQU1; Parent ID of the root folder
kHFSRootFolderIDEQU2; Folder ID of the root folder
kHFSExtentsFileIDEQU3; File ID of the extents overflow file
kHFSCatalogFileIDEQU4; File ID of the catalog file
kHFSPlusFileRecordEQU0x200
kForkTypeDataEQU0
kForkTypeResourceEQU0xFF
;
; BTNodeDescriptor
;
strucBTNodeDescriptor
.fLinkresd1
.bLinkresd1
.kindresb1
.heightresb1
.numRecordsresw1
.reservedresw1
endstruc
;
; BTHeaderRec
;
strucBTHeaderRec
.treeDepthresw1
.rootNoderesd1
.leafRecordsresd1
.firstLeafNoderesd1
.lastLeafNoderesd1
.nodeSizeresw1
.maxKeyLengthresw1
.totalNodesresd1
.freeNodesresd1
.reserved1resw1
.clumpSizeresd1
.btreeTyperesb1
.keyCompareTyperesb1
.attributesresd1
.reserved3resd16
endstruc
;
; BTIndexRec
;
strucBTIndexRec
.childIDresd1
endstruc
;
; HFSPlusCatalogKey
;
strucHFSPlusCatalogKey
;
; won't use the keyLength field for easier addressing data inside this structure
;
;.keyLengthresw1
.parentIDresd1
.nodeNameresbHFSUniStr255_size
endstruc
;
; HFSPlusExtentKey
;
strucHFSPlusExtentKey
;
; won't use the keyLength field for easier addressing data inside this structure
;
;.keyLengthresw1
.forkTyperesb1
.padresb1
.fileIDresd1
.startBlockresd1
endstruc
;
; HFSPlusBSDInfo
;
strucHFSPlusBSDInfo
.ownerIDresd1
.groupIDresd1
.adminFlagsresb1
.ownerFlagsresb1
.fileModeresw1
.specialresd1
endstruc
;
; FileInfo
;
strucFileInfo
.fileTyperesd1
.fileCreatorresd1
.finderFlagsresw1
.locationresw2
.reservedFieldresw1
endstruc
;
; ExtendedFileInfo
;
strucExtendedFileInfo
.reserved1resw4
.extFinderFlagsresw1
.reserved2resw1
.putAwayFolderIDresd1
endstruc
;
; HFSPlusCatalogFile
;
strucHFSPlusCatalogFile
.recordTyperesw1
.flagsresw1
.reserved1resd1
.fileIDresd1
.createDateresd1
.contentModDateresd1
.attributeModDateresd1
.accessDateresd1
.backupDateresd1
.permissionsresbHFSPlusBSDInfo_size
.userInforesbFileInfo_size
.finderInforesbExtendedFileInfo_size
.textEncodingresd1
.reserved2resd1
.dataForkresbHFSPlusForkData_size
.resourceForkresbHFSPlusForkData_size
endstruc
;
; Macros.
;
%macro jmpabs 1
pushWORD %1
ret
%endmacro
%macro DebugCharMacro 1
pushad
moval, %1
callprint_char
callgetc
popad
%endmacro
%macro PrintCharMacro 1
pushad
moval, %1
callprint_char
popad
%endmacro
%macro PutCharMacro 1
callprint_char
%endmacro
%macro PrintHexMacro 1
callprint_hex
%endmacro
%macro PrintString 1
movsi, %1
callprint_string
%endmacro
%macro LogString 1
movdi, %1
calllog_string
%endmacro
%if DEBUG
%define DebugChar(x) DebugCharMacro x
%define PrintChar(x) PrintCharMacro x
%define PutChar(x) PutCharMacro
%define PrintHex(x) PrintHexMacro x
%else
%define DebugChar(x)
%define PrintChar(x)
%define PutChar(x)
%define PrintHex(x)
%endif
;--------------------------------------------------------------------------
; Start of text segment.
SEGMENT .text
ORGkBoot1RelocAddr
;--------------------------------------------------------------------------
; Boot code is loaded at 0:7C00h.
;
start:
;
; Set up the stack to grow down from kBoot1StackSegment:kBoot1StackAddress.
; Interrupts should be off while the stack is being manipulated.
;
cli ; interrupts off
xorax, ax ; zero ax
movss, ax ; ss <- 0
mov sp, kBoot1StackAddress ; sp <- top of stack
sti ; reenable interrupts
mov ds, ax ; ds <- 0
mov es, ax ; es <- 0
;
; Relocate boot1 code.
;
pushsi
movsi, kBoot1LoadAddr; si <- source
movdi, kBoot1RelocAddr; di <- destination
cld; auto-increment SI and/or DI registers
movcx, kSectorBytes; copy 256 words
repmovsb; repeat string move (word) operation
popsi
;
; Code relocated, jump to startReloc in relocated location.
;
; FIXME: Is there any way to instruct NASM to compile a near jump
; using absolute address instead of relative displacement?
;
jmpabsstartReloc
;--------------------------------------------------------------------------
; Start execution from the relocated location.
;
startReloc:
;
; Initializing global variables.
;
mov eax, [si + part.lba]
mov [gPartLBA], eax; save the current partition LBA offset
mov [gBIOSDriveNumber], dl; save BIOS drive number
movWORD [gMallocPtr], mallocStart; set free space pointer
;
; Loading upper 512 bytes of boot1h and HFS+ Volume Header.
;
xorecx, ecx; sector 1 of current partition
incecx
mov al, 2; read 2 sectors: sector 1 of boot1h + HFS+ Volume Header
mov edx, kBoot1Sector1Addr
call readLBA
;
; Initializing more global variables.
;
moveax, [kHFSPlusBuffer + HFSPlusVolumeHeader.blockSize]
bswapeax; convert to little-endian
shreax, 9; convert to sector unit
mov[gBlockSize], eax; save blockSize as little-endian sector unit!
;
; Looking for HFSPlus ('H+') or HFSPlus case-sensitive ('HX') signature.
;
movax, [kHFSPlusBuffer + HFSPlusVolumeHeader.signature]
cmpax, kHFSPlusCaseSignature
jefindRootBoot
cmp ax, kHFSPlusSignature
jne error
;--------------------------------------------------------------------------
; Find stage2 boot file in a HFS+ Volume's root folder.
;
findRootBoot:
moval, kHFSCatalogFileID
leasi, [searchCatalogKey]
leadi, [kHFSPlusBuffer + HFSPlusVolumeHeader.catalogFile + HFSPlusForkData.extents]
calllookUpBTree
jneerror
leasi, [bp + BTree.recordDataPtr]
movsi, [si]
cmpWORD [si], kHFSPlusFileRecord
jneerror
; EAX = Catalog File ID
; BX = read size in sectors
; ECX = file offset in sectors
; EDX = address of read buffer
; DI = address of HFSPlusForkData
;
; Use the second big-endian double-word as the file length in HFSPlusForkData.logicalSize
;
movebx, [si + HFSPlusCatalogFile.dataFork + HFSPlusForkData.logicalSize + 4]
bswapebx; convert file size to little-endian
addebx, kSectorBytes - 1; adjust size before unit conversion
shrebx, 9; convert file size to sector unit
cmpbx, kBoot2Sectors; check if bigger than max stage2 size
jaerror
moveax, [si + HFSPlusCatalogFile.fileID]
bswapeax; convert fileID to little-endian
xorecx, ecx
movedx, (kBoot2Segment << 4) + kBoot2Address
leadi, [si + HFSPlusCatalogFile.dataFork + HFSPlusForkData.extents]
callreadExtent
%if VERBOSE
LogString(root_str)
%endif
boot2:
%if DEBUG
DebugChar ('!')
%endif
%if UNUSED
;
; Waiting for a key press.
;
mov ah, 0
int0x16
%endif
mov dl, [gBIOSDriveNumber]; load BIOS drive number
jmp kBoot2Segment:kBoot2Address
error:
%if VERBOSE
LogString(error_str)
%endif
hang:
hlt
jmp hang
;--------------------------------------------------------------------------
; readSectors - Reads more than 127 sectors using LBA addressing.
;
; Arguments:
; AX = number of 512-byte sectors to read (valid from 1-1280).
; EDX = pointer to where the sectors should be stored.
; ECX = sector offset in partition
;
; Returns:
; CF = 0 success
; 1 error
;
readSectors:
pushad
movbx, ax
.loop:
xoreax, eax; EAX = 0
moval, bl; assume we reached the last block.
cmpbx, maxSectorCount; check if we really reached the last block
jb.readBlock; yes, BX < MaxSectorCount
moval, maxSectorCount; no, read MaxSectorCount
.readBlock:
callreadLBA
subbx, ax; decrease remaning sectors with the read amount
jz.exit; exit if no more sectors left to be loaded
addecx, eax; adjust LBA sector offset
shlax, 9; convert sectors to bytes
addedx, eax; adjust target memory location
jmp.loop; read remaining sectors
.exit:
popad
ret
;--------------------------------------------------------------------------
; readLBA - Read sectors from a partition using LBA addressing.
;
; Arguments:
; AL = number of 512-byte sectors to read (valid from 1-127).
; EDX = pointer to where the sectors should be stored.
; ECX = sector offset in partition
; [bios_drive_number] = drive number (0x80 + unit number)
;
; Returns:
; CF = 0 success
; 1 error
;
readLBA:
pushad ; save all registers
push es; save ES
mov bp, sp ; save current SP
;
; Convert EDX to segment:offset model and set ES:BX
;
; Some BIOSes do not like offset to be negative while reading
; from hard drives. This usually leads to "boot1: error" when trying
; to boot from hard drive, while booting normally from USB flash.
; The routines, responsible for this are apparently different.
; Thus we split linear address slightly differently for these
; capricious BIOSes to make sure offset is always positive.
;
movbx, dx; save offset to BX
andbh, 0x0f; keep low 12 bits
shredx, 4; adjust linear address to segment base
xordl, dl; mask low 8 bits
moves, dx; save segment to ES
;
; 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.
add ecx, [gPartLBA] ; 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
push WORD 16 ; offset 0-1, packet size
;
; INT13 Func 42 - Extended Read Sectors
;
; Arguments:
; AH = 0x42
; [bios_drive_number] = drive number (0x80 + unit number)
; 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 dl, [gBIOSDriveNumber]; load BIOS drive number
mov si, sp
mov ah, 0x42
int 0x13
jcerror
;
; 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
pop es; restore ES
popad
ret
%if VERBOSE
;--------------------------------------------------------------------------
; Write a string with 'boot1: ' prefix to the console.
;
; Arguments:
; ES:DI pointer to a NULL terminated string.
;
; Clobber list:
; DI
;
log_string:
pushad
pushdi
movsi, log_title_str
callprint_string
popsi
callprint_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
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 .loop
.exit:
ret
%endif ; VERBOSE
%if DEBUG
;--------------------------------------------------------------------------
; 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
%if UNUSED
mov al, 10; carriage return
call print_char
mov al, 13
call print_char
%endif ; UNUSED
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 - wait for a key press
;
getc:
pushad
mov ah, 0
int0x16
popad
ret
;--------------------------------------------------------------------------
; Write a ASCII character to the console.
;
; Arguments:
; AL = ASCII character.
;
print_char:
pushad
mov bx, 1 ; BH=0, BL=1 (blue)
mov ah, 0x0e ; bios INT 10, Function 0xE
int 0x10 ; display byte in tty mode
popad
ret
%endif ; DEBUG
%if UNUSED
;--------------------------------------------------------------------------
; Convert null terminated string to HFSUniStr255
;
; Arguments:
; DS:DX pointer to a NULL terminated string.
; ES:DI pointer to result.
;
ConvertStrToUni:
pushad; save registers
pushdi; save DI for unicode string length pointer
movsi, dx; use SI as source string pointer
xorax, ax; AX = unicode character
movcl, al; CL = string length
.loop:
stosw; store unicode character (length 0 at first run)
lodsb; load next character to AL
inccl; increment string length count
cmpal, NULL; check for string terminator
jne.loop
popdi; restore unicode string length pointer
deccl; ignoring terminator from length count
mov[di], cl; save string length
popad; restore registers
ret
%endif ; UNUSED
;--------------------------------------------------------------------------
; Convert big-endian HFSUniStr255 to little-endian
;
; Arguments:
; DS:SI = pointer to big-endian HFSUniStr255
;ES:DI = pointer to result buffer
;
ConvertHFSUniStr255ToLE:
pushad
lodsw
xchgah, al
stosw
cmpal, 0
je.exit
movcx, ax
.loop:
lodsw
xchgah, al; convert AX to little-endian
;
; When working with a case-sensitive HFS+ (HX) filesystem, we shouldn't change the case.
;
cmpBYTE [kHFSPlusBuffer + HFSPlusVolumeHeader.signature + 1], kHFSPlusCaseSigX
je.keepcase
orax, ax
jne.convertToLE
decax; NULL must be the strongest char
.convertToLE:
cmpah, 0
ja.keepcase
cmpal, 'A'
jb.keepcase
cmpal, 'Z'
ja.keepcase
addal, 32; convert to lower-case
.keepcase:
stosw
loop.loop
.exit:
popad
ret
;--------------------------------------------------------------------------
; compare HFSPlusExtentKey structures
;
; Arguments:
; DS:SI = search key
; ES:DI = trial key
;
; Returns:
;[BTree.searchResult] = result
;FLAGS = relation between search and trial keys
;
compareHFSPlusExtentKeys:
pushad
movdl, 0; DL = result of comparison, DH = bestGuess
moveax, [si + HFSPlusExtentKey.fileID]
cmpeax, [di + HFSPlusExtentKey.fileID]
jne.checkFlags
cmpBYTE [si + HFSPlusExtentKey.forkType], kForkTypeData
jne.checkFlags
moveax, [si + HFSPlusExtentKey.startBlock]
cmpeax, [di + HFSPlusExtentKey.startBlock]
jecompareHFSPlusCatalogKeys.exit
.checkFlags:
jacompareHFSPlusCatalogKeys.searchKeyGreater; search key > trial key
jbcompareHFSPlusCatalogKeys.trialKeyGreater; search key < trial key
;--------------------------------------------------------------------------
; Compare HFSPlusCatalogKey structures
;
; Arguments:
; DS:SI = search key
; ES:DI = trial key
;
; Returns:
;[BTree.searchResult] = result
;FLAGS = relation between search and trial keys
;
compareHFSPlusCatalogKeys:
pushad
xordx, dx; DL = result of comparison, DH = bestGuess
xchgsi, di
lodsd
movecx, eax; ECX = trial parentID
xchgsi, di
lodsd; EAX = search parentID
cmpeax, ecx
ja.searchKeyGreater; search parentID > trial parentID
jb.trialKeyGreater; search parentID < trial parentID
.compareNodeName:; search parentID = trial parentID
xchgsi, di
lodsw
movcx, ax; CX = trial nodeName.length
xchgsi, di
lodsw; AX = search nodeName.length
cmpcl, 0; trial nodeName.length = 0?
je.searchKeyGreater
cmpax, cx
je.strCompare
ja.searchStrLonger
.trialStrLonger:
decdh
movcx, ax
jmp.strCompare
.searchStrLonger:
incdh
.strCompare:
repecmpsw
ja.searchKeyGreater
jb.trialKeyGreater
movdl, dh
jmp.exit
.trialKeyGreater:
decdl
jmp.exit
.searchKeyGreater:
incdl
.exit:
mov[bp + BTree.searchResult], dl
cmpdl, 0; set flags to check relation between keys
popad
ret
;--------------------------------------------------------------------------
; Allocate memory
;
; Arguments:
; CX = size of requested memory
;
; Returns:
; BP = start address of allocated memory
;
; Clobber list:
; CX
;
malloc:
pushax; save AX
pushdi; save DI
movdi, [gMallocPtr]; start address of free space
pushdi; save free space start address
incdi;
incdi; keep the first word untouched
deccx; for the last memory block pointer.
deccx;
moval, NULL; fill with zero
repstosb; repeat fill
mov[gMallocPtr], di; adjust free space pointer
popbp; BP = start address of allocated memory
mov[di], bp; set start address of allocated memory at next
; allocation block's free space address.
popdi; restore DI
popax; restore AX
ret
%if UNUSED
;--------------------------------------------------------------------------
; Free allocated memory
;
; Returns:
; BP = start address of previously allocated memory
;
free:
leabp, [gMallocPtr]
movbp, [bp]
mov[gMallocPtr], bp
ret
%endif ; UNUSED
;--------------------------------------------------------------------------
; Static data.
;
%if VERBOSE
root_strdb'/boot', NULL
%endif
;--------------------------------------------------------------------------
; Pad the rest of the 512 byte sized sector 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.
pad_table_and_sig:
times510-($-$$) db 0
dwkBootSignature
;
; Sector 1 code area
;
;--------------------------------------------------------------------------
; lookUpBTree - initializes a new BTree instance and
; look up for HFSPlus Catalog File or Extent Overflow keys
;
; Arguments:
; AL = kHFSPlusFileID (Catalog or Extents Overflow)
;SI = address of searchKey
; DI = address of HFSPlusForkData.extents
;
; Returns:
; BP = address of BTree instance
; ECX = rootNode's logical offset in sectors
;
lookUpBTree:
movcx, BTree_size; allocate memory with BTree_size
callmalloc; BP = start address of allocated memory.
mov[bp + BTree.fileID], al; save fileFileID
movedx, [di]; first extent of current file
callblockToSector; ECX = converted to sector unit
moval, 1; 1 sector is enough for
xoredx, edx; reading current file's header.
leadx, [bp + BTree.BTHeaderBuffer]; load into BTreeHeaderBuffer
callreadLBA; read
movax, [bp + BTree.BTHeaderBuffer + BTNodeDescriptor_size + BTHeaderRec.nodeSize]
xchgah, al; convert to little-endian
mov[bp + BTree.nodeSize], ax; save nodeSize
;
; Always start the lookup process with the root node.
;
movedx, [bp + BTree.BTHeaderBuffer + BTNodeDescriptor_size + BTHeaderRec.rootNode]
.readNode:
;
; Converting nodeID to sector unit
;
movax, [bp + BTree.nodeSize]
shrax, 9; convert nodeSize to sectors
movbx, ax; BX = read sector count
cwde
bswapedx; convert node ID to little-endian
muledx; multiply with nodeSize converted to sector unit
movecx, eax; ECX = file offset in BTree
moveax, [bp + BTree.fileID]
leaedx, [bp + BTree.nodeBuffer]
callreadExtent
;
; AX = lowerBound = 0
;
xorax, ax
;
; BX = upperBound = numRecords - 1
;
movbx, [bp + BTree.nodeBuffer + BTNodeDescriptor.numRecords]
xchgbh, bl
decbx
.bsearch:
cmpax, bx
ja.checkResult; jump if lowerBound > upperBound
movcx, ax
addcx, bx
shrcx, 1; test index = (lowerBound + upperBound / 2)
callgetBTreeRecord
%if UNUSED
pushad
jl.csearchLessThanTrial
jg.csearchGreaterThanTrial
PrintChar('=')
jmp.csearchCont
.csearchGreaterThanTrial:
PrintChar('>')
jmp.csearchCont
.csearchLessThanTrial:
PrintChar('<')
.csearchCont:
popad
%endif ; UNUSED
.adjustBounds:
je.checkResult
jl.searchLessThanTrial
jg.searchGreaterThanTrial
jmp.bsearch
.searchLessThanTrial:
movbx, cx
decbx; upperBound = index - 1
jmp.bsearch
.searchGreaterThanTrial:
movax, cx
incax; lowerBound = index + 1
jmp.bsearch
.checkResult:
cmpBYTE [bp + BTree.searchResult], 0
jge.foundKey
movcx, bx
callgetBTreeRecord
.foundKey:
cmpBYTE [bp + BTree.nodeBuffer + BTNodeDescriptor.kind], kBTIndexNode
jne.exit
leabx, [bp + BTree.recordDataPtr]
movbx, [bx]
movedx, [bx]
jmp.readNode
.exit:
cmpBYTE [bp + BTree.searchResult], 0
ret
;--------------------------------------------------------------------------
; getBTreeRecord - read and compare BTree record
;
; Arguments:
; CX = record index
; SI = address of search key
;
; Returns:
; [BTree.searchResult] = result of key compare
; [BTree.recordDataPtr] = address of record data
;
getBTreeRecord:
pushad
pushsi; save SI
leadi, [bp + BTree.nodeBuffer]; DI = start of nodeBuffer
pushdi; use later
movax, [bp + BTree.nodeSize]; get nodeSize
adddi, ax; DI = beyond nodeBuffer
inccx; increment index
shlcx, 1; * 2
subdi, cx; DI = pointer to record
movax, [di]; offset to record
xchgah, al; convert to little-endian
popdi; start of nodeBuffer
adddi, ax; DI = address of record key
movsi, di; save to SI
movax, [di]; keyLength
xchgah, al; convert to little-endian
incax; suppress keySize (2 bytes)
incax;
adddi, ax; DI = address of record data
mov[bp + BTree.recordDataPtr], di; save address of record data
leadi, [bp + BTree.trialKey]
pushdi; save address of trialKey
lodsw; suppress keySize (2 bytes)
;
; Don't need to compare as DWORD since all reserved CNIDs fits to a single byte
;
cmpBYTE [bp + BTree.fileID], kHFSCatalogFileID
je.prepareTrialCatalogKey
.prepareTrialExtentKey:
movbx, compareHFSPlusExtentKeys
movsw; copy forkType + pad
movcx, 2; copy fileID + startBlock
.extentLoop:
lodsd
bswapeax; convert to little-endian
stosd
loop.extentLoop
jmp.exit
.prepareTrialCatalogKey:
movbx, compareHFSPlusCatalogKeys
lodsd
bswapeax; convert ParentID to little-endian
stosd
callConvertHFSUniStr255ToLE; convert nodeName to little-endian
.exit:
popdi; restore address of trialKey
%if UNUSED
;
; Print catalog trial key
;
pushad
movsi, di
lodsd
PrintChar('k')
PrintHex()
lodsw
cmpax, 0
je.printExit
movcx, ax
.printLoop:
lodsw
callprint_char
loop.printLoop
.printExit:
popad
;
;
;
%endif ; UNUSED
%if UNUSED
;
; Print extent trial key
;
pushad
PrintChar('k')
movsi, di
xoreax, eax
lodsw
PrintHex()
lodsd
PrintHex()
lodsd
PrintHex()
popad
;
;
;
%endif ; UNUSED
popsi; restore SI
callbx; call key compare proc
popad
ret
;--------------------------------------------------------------------------
; readExtent - read extents from a HFS+ file (multiple extent support)
;
; Arguments:
; EAX = Catalog File ID
; BX = read size in sectors
; ECX = file offset in sectors
; EDX = address of read buffer
; DI = address of HFSPlusForkData.extents
;
readExtent:
pushad
;
; Save Catalog File ID as part of a search HFSPlusExtentKey
; for a possible Extents Overflow lookup.
;
mov[bp + BTree.searchExtentKey + HFSPlusExtentKey.fileID], eax
mov[bp + BTree.readBufferPtr], edx
movax, bx
cwde
mov[bp + BTree.readSize], eax
movebx, ecx; EBX = file offset
xoreax, eax
mov[bp + BTree.currentExtentOffs], eax
.beginExtentBlock:
movBYTE [bp + BTree.extentCount], 0
.extentSearch:
cmpBYTE [bp + BTree.extentCount], kHFSPlusExtentDensity
jb.continue
.getNextExtentBlock:
pushebx
moveax, [bp + BTree.currentExtentOffs]
;
; Converting sector unit to HFS+ allocation block unit.
;
xoredx, edx
divDWORD [gBlockSize]; divide with blockSize
;
; Preparing searchExtentKey's startBlock field.
;
mov[bp + BTree.searchExtentKey + HFSPlusExtentKey.startBlock], eax
moval, kHFSExtentsFileID
leasi, [bp + BTree.searchExtentKey]
leadi, [kHFSPlusBuffer + HFSPlusVolumeHeader.extentsFile + HFSPlusForkData.extents]
calllookUpBTree
jnzNEAR .exit
;
; BP points to the new workspace allocated by lookUpBTree.
;
leadi, [bp + BTree.recordDataPtr]
movdi, [di]
;
; Switch back to the previous workspace.
;
leabp, [gMallocPtr]
movbp, [bp]
mov[gMallocPtr], bp
popebx
jmp.beginExtentBlock
.continue:
movedx, [di + HFSPlusExtentDescriptor.blockCount]
callblockToSector; ECX = converted current extent's blockCount to sectors
moveax, [bp + BTree.currentExtentOffs]; EAX = current extent's start offset (sector)
movedx, eax
addedx, ecx; EDX = next extent's start offset (sector)
cmpebx, edx
mov[bp + BTree.currentExtentOffs], edx; set currentExtentOffs as the next extent's start offset
jae.nextExtent; jump to next extent if file offset > next extent's start offset
.foundExtent:
movedx, ebx
subedx, eax; EDX = relative offset within current extent
moveax, edx; will be used below to determine read size
movesi, [bp + BTree.readSize]; ESI = remaining sectors to be read
addedx, esi
cmpedx, ecx; test if relative offset + readSize fits to this extent
jbe.read; read all remaining sectors from this extent
.splitRead:
subecx, eax; read amount of sectors beginning at relative offset
movesi, ecx; of current extent up to the end of current extent
.read:
movedx, [di + HFSPlusExtentDescriptor.startBlock]
callblockToSector; ECX = converted to sectors
addecx, eax; file offset converted to sectors
pushsi
movax, si
movedx, [bp + BTree.readBufferPtr]
callreadSectors
popsi
addebx, esi
movax, si
cwde
shlax, 9; convert SI (read sector count) to byte unit
add[bp + BTree.readBufferPtr], eax
sub[bp + BTree.readSize], esi
jz.exit
.nextExtent:
adddi, kHFSPlusExtentDensity
incBYTE [bp + BTree.extentCount]
jmp.extentSearch
.exit:
popad
ret
;--------------------------------------------------------------------------
; Convert big-endian HFSPlus allocation block to sector unit
;
; Arguments:
; EDX = allocation block
;
; Returns:
; ECX = allocation block converted to sector unit
;
; Clobber list:
; EDX
;
blockToSector:
pusheax
moveax, [gBlockSize]
bswapedx; convert allocation block to little-endian
muledx ; multiply with block number
movecx, eax; result in EAX
popeax
ret
%if UNUSED
;--------------------------------------------------------------------------
; Convert sector unit to HFSPlus allocation block unit
;
; Arguments:
; EDX = sector
;
; Returns:
; ECX = converted to allocation block unit
;
; Clobber list:
; EDX
;
sectorToBlock:
pusheax
moveax, edx
xoredx, edx
divDWORD [gBlockSize]; divide with blockSize
movecx, eax; result in EAX
popeax
ret
%endif ; UNUSED
%if UNUSED
;--------------------------------------------------------------------------
; Convert big-endian BTree node ID to sector unit
;
; Arguments:
; EDX = node ID
;
; Returns:
; ECX = node ID converted to sector unit
;
; Clobber list:
; EDX
;
nodeToSector:
pusheax
movax, [bp + BTree.nodeSize]
shrax, 9; convert nodeSize to sectors
cwde
bswapedx; convert node ID to little-endian
muledx; multiply with node ID
movecx, eax; result in EAX
popeax
ret
%endif ; UNUSED
;--------------------------------------------------------------------------
; Static data.
;
%if VERBOSE
log_title_strdbCR, LF, 'boot1: ', NULL
error_strdb'error', NULL
%endif
searchCatalogKeyddkHFSRootFolderID
dwsearchCatKeyNameLen
searchCatKeyNamedw'b', 'o', 'o', 't'; must be lower case
searchCatKeyNameLenEQU($ - searchCatKeyName) / 2
;--------------------------------------------------------------------------
; Pad the rest of the 512 byte sized sector with zeroes. The last
; two bytes is the mandatory boot sector signature.
;
pad_sector_1:
times1022-($-$$) db 0
dwkBootSignature
;
; Local BTree variables
;
strucBTree
.mallocLinkresw1; pointer to previously allocated memory block
.fileIDresd1; will use as BYTE
.nodeSizeresd1; will use as WORD
.searchExtentKeyresbHFSPlusExtentKey_size
.searchResultresb1
.trialKeyresbkBTMaxRecordLength
.recordDataPtrresw1
.readBufferPtrresd1
.currentExtentOffsresd1
.readSizeresd1
.extentCountresb1
ALIGNB2
.BTHeaderBufferresbkSectorBytes
.nodeBufferresbmaxNodeSize
endstruc
;
; Global variables
;
ABSOLUTEkHFSPlusBuffer + HFSPlusVolumeHeader_size
gPartLBAresd1
gBIOSDriveNumberresw1
gBlockSizeresd1
gMallocPtrresw1
; END
branches/cparm/i386/boot1/Makefile
1919
2020
2121
22
23
22
23
2424
2525
2626
all embedtheme: $(DIRS_NEEDED) $(VERSIONED_FILES)
boot1h: boot1.s Makefile
$(NASM) boot1.s -o $(SYMROOT)/boot1h
boot1h: boot1h.s Makefile
$(NASM) boot1h.s -o $(SYMROOT)/boot1h
$(NASM) boot1hp.s -o $(SYMROOT)/boot1hp
$(NASM) boot1he.s -o $(SYMROOT)/boot1he
$(NASM) boot1f32.s -o $(SYMROOT)/boot1f32
branches/cparm/i386/boot1/boot1h.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
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
; 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@
;
; Partition Boot Loader: boot1h
;
; This program is designed to reside in sector 0+1 of an HFS+ partition.
; It expects that the MBR has left the drive number in DL
; and a pointer to the partition entry in SI.
;
; This version requires a BIOS with EBIOS (LBA) support.
;
; This code is written for the NASM assembler.
; nasm boot1.s -o boot1h
;
; This version of boot1h tries to find a stage2 boot file in the root folder.
;
; NOTE: this is an experimental version with multiple extent support.
;
;
;
; Set to 1 to enable obscure debug messages.
;
DEBUGEQU0
;
; Set to 1 to enable unused code.
;
UNUSEDEQU0
;
; Set to 1 to enable verbose mode.
;
VERBOSEEQU1
;
; Various constants.
;
NULL EQU0
CREQU0x0D
LFEQU0x0A
mallocStartEQU0x1000; start address of local workspace area
maxSectorCountEQU64; maximum sector count for readSectors
maxNodeSizeEQU16384
kSectorBytesEQU512; sector size in bytes
kBootSignatureEQU0xAA55; boot sector signature
kBoot1StackAddressEQU0xFFF0; boot1 stack pointer
kBoot1LoadAddrEQU0x7C00; boot1 load address
kBoot1RelocAddrEQU0xE000; boot1 relocated address
kBoot1Sector1AddrEQUkBoot1RelocAddr + kSectorBytes; boot1 load address for sector 1
kHFSPlusBufferEQUkBoot1Sector1Addr + kSectorBytes; HFS+ Volume Header address
kBoot2SectorsEQU(448 * 1024 - 512) / kSectorBytes; max size of 'boot' file in sectors
kBoot2SegmentEQU0x2000; boot2 load segment
kBoot2AddressEQUkSectorBytes; boot2 load address
;
; Format of fdisk partition entry.
;
; The symbol 'part_size' is automatically defined as an `EQU'
; giving the size of the structure.
;
struc part
.bootidresb 1; bootable or not
.headresb 1; starting head, sector, cylinder
.sectresb 1;
.cylresb 1;
.typeresb 1; partition type
.endheadresb 1; ending head, sector, cylinder
.endsectresb 1;
.endcylresb 1;
.lbaresd 1; starting lba
.sectorsresd 1; size in sectors
endstruc
;-------------------------------------------------------------------------
; HFS+ related structures and constants
;
kHFSPlusSignatureEQU'H+'; HFS+ volume signature
kHFSPlusCaseSignatureEQU'HX'; HFS+ volume case-sensitive signature
kHFSPlusCaseSigXEQU'X'; upper byte of HFS+ volume case-sensitive signature
kHFSPlusExtentDensityEQU8; 8 extent descriptors / extent record
;
; HFSUniStr255
;
strucHFSUniStr255
.lengthresw1
.unicoderesw255
endstruc
;
; HFSPlusExtentDescriptor
;
strucHFSPlusExtentDescriptor
.startBlockresd1
.blockCountresd1
endstruc
;
; HFSPlusForkData
;
strucHFSPlusForkData
.logicalSizeresq1
.clumpSizeresd1
.totalBlocksresd1
.extentsresbkHFSPlusExtentDensity * HFSPlusExtentDescriptor_size
endstruc
;
; HFSPlusVolumeHeader
;
strucHFSPlusVolumeHeader
.signatureresw1
.versionresw1
.attributesresd1
.lastMountedVersion resd1
.journalInfoBlockresd1
.createDateresd1
.modifyDateresd1
.backupDateresd1
.checkedDateresd1
.fileCountresd1
.folderCountresd1
.blockSizeresd1
.totalBlocksresd1
.freeBlocksresd1
.nextAllocationresd1
.rsrcClumpSizeresd1
.dataClumpSizeresd1
.nextCatalogIDresd1
.writeCountresd1
.encodingsBitmapresq1
.finderInforesd8
.allocationFileresbHFSPlusForkData_size
.extentsFileresbHFSPlusForkData_size
.catalogFileresbHFSPlusForkData_size
.attributesFileresbHFSPlusForkData_size
.startupFileresbHFSPlusForkData_size
endstruc
;
; B-tree related structures and constants
;
kBTIndexNodeEQU0
kBTMaxRecordLengthEQU264; sizeof(kHFSPlusFileThreadRecord)
kHFSRootParentIDEQU1; Parent ID of the root folder
kHFSRootFolderIDEQU2; Folder ID of the root folder
kHFSExtentsFileIDEQU3; File ID of the extents overflow file
kHFSCatalogFileIDEQU4; File ID of the catalog file
kHFSPlusFileRecordEQU0x200
kForkTypeDataEQU0
kForkTypeResourceEQU0xFF
;
; BTNodeDescriptor
;
strucBTNodeDescriptor
.fLinkresd1
.bLinkresd1
.kindresb1
.heightresb1
.numRecordsresw1
.reservedresw1
endstruc
;
; BTHeaderRec
;
strucBTHeaderRec
.treeDepthresw1
.rootNoderesd1
.leafRecordsresd1
.firstLeafNoderesd1
.lastLeafNoderesd1
.nodeSizeresw1
.maxKeyLengthresw1
.totalNodesresd1
.freeNodesresd1
.reserved1resw1
.clumpSizeresd1
.btreeTyperesb1
.keyCompareTyperesb1
.attributesresd1
.reserved3resd16
endstruc
;
; BTIndexRec
;
strucBTIndexRec
.childIDresd1
endstruc
;
; HFSPlusCatalogKey
;
strucHFSPlusCatalogKey
;
; won't use the keyLength field for easier addressing data inside this structure
;
;.keyLengthresw1
.parentIDresd1
.nodeNameresbHFSUniStr255_size
endstruc
;
; HFSPlusExtentKey
;
strucHFSPlusExtentKey
;
; won't use the keyLength field for easier addressing data inside this structure
;
;.keyLengthresw1
.forkTyperesb1
.padresb1
.fileIDresd1
.startBlockresd1
endstruc
;
; HFSPlusBSDInfo
;
strucHFSPlusBSDInfo
.ownerIDresd1
.groupIDresd1
.adminFlagsresb1
.ownerFlagsresb1
.fileModeresw1
.specialresd1
endstruc
;
; FileInfo
;
strucFileInfo
.fileTyperesd1
.fileCreatorresd1
.finderFlagsresw1
.locationresw2
.reservedFieldresw1
endstruc
;
; ExtendedFileInfo
;
strucExtendedFileInfo
.reserved1resw4
.extFinderFlagsresw1
.reserved2resw1
.putAwayFolderIDresd1
endstruc
;
; HFSPlusCatalogFile
;
strucHFSPlusCatalogFile
.recordTyperesw1
.flagsresw1
.reserved1resd1
.fileIDresd1
.createDateresd1
.contentModDateresd1
.attributeModDateresd1
.accessDateresd1
.backupDateresd1
.permissionsresbHFSPlusBSDInfo_size
.userInforesbFileInfo_size
.finderInforesbExtendedFileInfo_size
.textEncodingresd1
.reserved2resd1
.dataForkresbHFSPlusForkData_size
.resourceForkresbHFSPlusForkData_size
endstruc
;
; Macros.
;
%macro jmpabs 1
pushWORD %1
ret
%endmacro
%macro DebugCharMacro 1
pushad
moval, %1
callprint_char
callgetc
popad
%endmacro
%macro PrintCharMacro 1
pushad
moval, %1
callprint_char
popad
%endmacro
%macro PutCharMacro 1
callprint_char
%endmacro
%macro PrintHexMacro 1
callprint_hex
%endmacro
%macro PrintString 1
movsi, %1
callprint_string
%endmacro
%macro LogString 1
movdi, %1
calllog_string
%endmacro
%if DEBUG
%define DebugChar(x) DebugCharMacro x
%define PrintChar(x) PrintCharMacro x
%define PutChar(x) PutCharMacro
%define PrintHex(x) PrintHexMacro x
%else
%define DebugChar(x)
%define PrintChar(x)
%define PutChar(x)
%define PrintHex(x)
%endif
;--------------------------------------------------------------------------
; Start of text segment.
SEGMENT .text
ORGkBoot1RelocAddr
;--------------------------------------------------------------------------
; Boot code is loaded at 0:7C00h.
;
start:
;
; Set up the stack to grow down from kBoot1StackSegment:kBoot1StackAddress.
; Interrupts should be off while the stack is being manipulated.
;
cli ; interrupts off
xorax, ax ; zero ax
movss, ax ; ss <- 0
mov sp, kBoot1StackAddress ; sp <- top of stack
sti ; reenable interrupts
mov ds, ax ; ds <- 0
mov es, ax ; es <- 0
;
; Relocate boot1 code.
;
pushsi
movsi, kBoot1LoadAddr; si <- source
movdi, kBoot1RelocAddr; di <- destination
cld; auto-increment SI and/or DI registers
movcx, kSectorBytes; copy 256 words
repmovsb; repeat string move (word) operation
popsi
;
; Code relocated, jump to startReloc in relocated location.
;
; FIXME: Is there any way to instruct NASM to compile a near jump
; using absolute address instead of relative displacement?
;
jmpabsstartReloc
;--------------------------------------------------------------------------
; Start execution from the relocated location.
;
startReloc:
;
; Initializing global variables.
;
mov eax, [si + part.lba]
mov [gPartLBA], eax; save the current partition LBA offset
mov [gBIOSDriveNumber], dl; save BIOS drive number
movWORD [gMallocPtr], mallocStart; set free space pointer
;
; Loading upper 512 bytes of boot1h and HFS+ Volume Header.
;
xorecx, ecx; sector 1 of current partition
incecx
mov al, 2; read 2 sectors: sector 1 of boot1h + HFS+ Volume Header
mov edx, kBoot1Sector1Addr
call readLBA
;
; Initializing more global variables.
;
moveax, [kHFSPlusBuffer + HFSPlusVolumeHeader.blockSize]
bswapeax; convert to little-endian
shreax, 9; convert to sector unit
mov[gBlockSize], eax; save blockSize as little-endian sector unit!
;
; Looking for HFSPlus ('H+') or HFSPlus case-sensitive ('HX') signature.
;
movax, [kHFSPlusBuffer + HFSPlusVolumeHeader.signature]
cmpax, kHFSPlusCaseSignature
jefindRootBoot
cmp ax, kHFSPlusSignature
jne error
;--------------------------------------------------------------------------
; Find stage2 boot file in a HFS+ Volume's root folder.
;
findRootBoot:
moval, kHFSCatalogFileID
leasi, [searchCatalogKey]
leadi, [kHFSPlusBuffer + HFSPlusVolumeHeader.catalogFile + HFSPlusForkData.extents]
calllookUpBTree
jneerror
leasi, [bp + BTree.recordDataPtr]
movsi, [si]
cmpWORD [si], kHFSPlusFileRecord
jneerror
; EAX = Catalog File ID
; BX = read size in sectors
; ECX = file offset in sectors
; EDX = address of read buffer
; DI = address of HFSPlusForkData
;
; Use the second big-endian double-word as the file length in HFSPlusForkData.logicalSize
;
movebx, [si + HFSPlusCatalogFile.dataFork + HFSPlusForkData.logicalSize + 4]
bswapebx; convert file size to little-endian
addebx, kSectorBytes - 1; adjust size before unit conversion
shrebx, 9; convert file size to sector unit
cmpbx, kBoot2Sectors; check if bigger than max stage2 size
jaerror
moveax, [si + HFSPlusCatalogFile.fileID]
bswapeax; convert fileID to little-endian
xorecx, ecx
movedx, (kBoot2Segment << 4) + kBoot2Address
leadi, [si + HFSPlusCatalogFile.dataFork + HFSPlusForkData.extents]
callreadExtent
%if VERBOSE
LogString(root_str)
%endif
boot2:
%if DEBUG
DebugChar ('!')
%endif
%if UNUSED
;
; Waiting for a key press.
;
mov ah, 0
int0x16
%endif
mov dl, [gBIOSDriveNumber]; load BIOS drive number
jmp kBoot2Segment:kBoot2Address
error:
%if VERBOSE
LogString(error_str)
%endif
hang:
hlt
jmp hang
;--------------------------------------------------------------------------
; readSectors - Reads more than 127 sectors using LBA addressing.
;
; Arguments:
; AX = number of 512-byte sectors to read (valid from 1-1280).
; EDX = pointer to where the sectors should be stored.
; ECX = sector offset in partition
;
; Returns:
; CF = 0 success
; 1 error
;
readSectors:
pushad
movbx, ax
.loop:
xoreax, eax; EAX = 0
moval, bl; assume we reached the last block.
cmpbx, maxSectorCount; check if we really reached the last block
jb.readBlock; yes, BX < MaxSectorCount
moval, maxSectorCount; no, read MaxSectorCount
.readBlock:
callreadLBA
subbx, ax; decrease remaning sectors with the read amount
jz.exit; exit if no more sectors left to be loaded
addecx, eax; adjust LBA sector offset
shlax, 9; convert sectors to bytes
addedx, eax; adjust target memory location
jmp.loop; read remaining sectors
.exit:
popad
ret
;--------------------------------------------------------------------------
; readLBA - Read sectors from a partition using LBA addressing.
;
; Arguments:
; AL = number of 512-byte sectors to read (valid from 1-127).
; EDX = pointer to where the sectors should be stored.
; ECX = sector offset in partition
; [bios_drive_number] = drive number (0x80 + unit number)
;
; Returns:
; CF = 0 success
; 1 error
;
readLBA:
pushad ; save all registers
push es; save ES
mov bp, sp ; save current SP
;
; Convert EDX to segment:offset model and set ES:BX
;
; Some BIOSes do not like offset to be negative while reading
; from hard drives. This usually leads to "boot1: error" when trying
; to boot from hard drive, while booting normally from USB flash.
; The routines, responsible for this are apparently different.
; Thus we split linear address slightly differently for these
; capricious BIOSes to make sure offset is always positive.
;
movbx, dx; save offset to BX
andbh, 0x0f; keep low 12 bits
shredx, 4; adjust linear address to segment base
xordl, dl; mask low 8 bits
moves, dx; save segment to ES
;
; 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.
add ecx, [gPartLBA] ; offset 8, lower 3