-
-
Notifications
You must be signed in to change notification settings - Fork 17
/
initdisk.c
1463 lines (1241 loc) · 48.1 KB
/
initdisk.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
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
/****************************************************************/
/* */
/* initDISK.c */
/* */
/* Copyright (c) 2001 */
/* tom ehlert */
/* All Rights Reserved */
/* */
/* This file is part of DOS-C. */
/* */
/* DOS-C is free software; you can redistribute it and/or */
/* modify it under the terms of the GNU General Public License */
/* as published by the Free Software Foundation; either version */
/* 2, or (at your option) any later version. */
/* */
/* DOS-C is distributed in the hope that it will be useful, but */
/* WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */
/* the GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public */
/* License along with DOS-C; see the file COPYING. If not, */
/* write to the Free Software Foundation, 675 Mass Ave, */
/* Cambridge, MA 02139, USA. */
/****************************************************************/
#include "portab.h"
#include "debug.h"
#include "globals.h"
#include "init-mod.h"
#include "dyndata.h"
#define FLOPPY_SEC_SIZE 512u /* common sector size */
BSS(UBYTE FAR *, InitDiskTransferBuffer, NULL);
BSS(COUNT, nUnits, 0);
/*
* Rev 1.0 13 May 2001 tom ehlert
* Initial revision.
*
* this module implements the disk scanning for DOS accesible partitions
* the drive letter ordering is somewhat chaotic, but like MSDOS does it.
*
* this module expects to run with CS = INIT_TEXT, like other init_code,
* but SS = DS = DATA = DOS_DS, unlike other init_code.
*
* history:
* 1.0 extracted the disk init code from DSK.C
* added LBA support
* moved code to INIT_TEXT
* done the funny code segment stuff to switch between INIT_TEXT and TEXT
* added a couple of snity checks for partitions
*
****************************************************************************
*
* Implementation note:
* this module needs some interfacing to INT 13
* how to implement them
*
* a) using inline assembly
* _ASM mov ax,0x1314
*
* b) using assembly routines in some external FLOPPY.ASM
*
* c) using the funny TURBO-C style
* _AX = 0x1314
*
* d) using intr(intno, ®s) method.
*
* why not?
*
* a) this is my personal favorite, combining the best aof all worlds.
* TURBO-C does support inline assembly, but only by using TASM,
* which is not free.
* so - unfortunately- its excluded.
*
* b) keeping funny memory model in sync with external assembly
* routines is everything, but not fun
*
* c) you never know EXACT, what the compiler does, if its a bit
* more complicated. does
* _DL = drive & 0xff
* _BL = driveParam.chs.Sector;
* destroy any other register? sure? _really_ sure?
* at least, it has it's surprises.
* and - I found a couple of optimizer induced bugs (TC 2.01)
* believe me.
* it was coded - and operational that way.
* but - there are many surprises waiting there. so I opted against.
*
*
* d) this method is somewhat clumsy and certainly not the
* fastest way to do things.
* on the other hand, this is INIT code, executed once.
* and since it's the only portable method, I opted for it.
*
* e) and all this is my private opinion. tom ehlert.
*
*
* Some thoughts about LBA vs. CHS. by Bart Oldeman 2001/Nov/11
* Matthias Paul writes in www.freedos.org/freedos/news/technote/113.html:
* (...) MS-DOS 7.10+, which will always access logical drives in a type
* 05h extended partition via CHS, even if the individual logical drives
* in there are of LBA type, or go beyond 8 Gb... (Although this workaround
* is sometimes used in conjunction with OS/2, using a 05h partition going
* beyond 8 Gb may cause MS-DOS 7.10 to hang or corrupt your data...) (...)
*
* Also at http://www.win.tue.nl/~aeb/partitions/partition_types-1.html:
* (...) 5 DOS 3.3+ Extended Partition
* Supports at most 8.4 GB disks: with type 5 DOS/Windows will not use the
* extended BIOS call, even if it is available. (...)
*
* So MS-DOS 7.10+ is brain-dead in this respect, but we knew that ;-)
* However there is one reason to use old-style CHS calls:
* some programs intercept int 13 and do not support LBA addressing. So
* it is worth using CHS if possible, unless the user asks us not to,
* either by specifying a 0x0c/0x0e/0x0f partition type or enabling
* the ForceLBA setting in the fd kernel (sys) config. This will make
* multi-sector reads and BIOS computations more efficient, at the cost
* of some compatibility.
*
* However we need to be safe, and with varying CHS at different levels
* that might be difficult. Hence we _only_ trust the LBA values in the
* partition tables and the heads and sectors values the BIOS gives us.
* After all these are the values the BIOS uses to process our CHS values.
* So unless the BIOS is buggy, using CHS on one partition and LBA on another
* should be safe. The CHS values in the partition table are NOT trusted.
* We print a warning if there is a mismatch with the calculated values.
*
* The CHS values in the boot sector are used at a higher level. The CHS
* that DOS uses in various INT21/AH=44 IOCTL calls are converted to LBA
* using the boot sector values and then converted back to CHS using BIOS
* values if necessary. Internally we do LBA as much as possible.
*
* However if the partition extends beyond cylinder 1023 and is not labelled
* as one of the LBA types, we can't use CHS and print a warning, using LBA
* instead if possible, and otherwise refuse to use it.
*
* As for EXTENDED_LBA vs. EXTENDED, FreeDOS makes no difference. This is
* boot time - there is no reason not to use LBA for reading partition tables,
* and the MSDOS 7.10 behaviour is not desirable.
*
* Note: for floppies we need the boot sector values though and the boot sector
* code does not use LBA addressing yet.
*
* Conclusion: with all this implemented, FreeDOS should be able to gracefully
* handle and read foreign hard disks moved across computers, whether using
* CHS or LBA, strengthening its role as a rescue environment.
*/
#define LBA_to_CHS init_LBA_to_CHS
/*
interesting macros - used internally only
*/
#define SCAN_PRIMARYBOOT 0x00
#define SCAN_PRIMARY 0x01
#define SCAN_EXTENDED 0x02
#define SCAN_PRIMARY2 0x03
#define FAT12 0x01
#define FAT16SMALL 0x04
#define EXTENDED 0x05
#define FAT16LARGE 0x06
#define FAT32 0x0b /* FAT32 partition that ends before the 8.4 */
/* GB boundary */
#define FAT32_LBA 0x0c /* FAT32 partition that ends after the 8.4GB */
/* boundary. LBA is needed to access this. */
#define FAT16_LBA 0x0e /* like 0x06, but it is supposed to end past */
/* the 8.4GB boundary */
#define FAT12_LBA 0xff /* fake FAT12 LBA entry for internal use */
#define EXTENDED_LBA 0x0f /* like 0x05, but it is supposed to end past */
/* Let's play it safe and do not allow partitions with clusters above *
* or equal to 0xff0/0xfff0/0xffffff0 to be created *
* the problem with fff0-fff6 is that they might be interpreted as BAD *
* even though the standard BAD value is ...ff7 */
#define FAT12MAX (FAT_MAGIC-6)
#define FAT16MAX (FAT_MAGIC16-6)
#define FAT32MAX (FAT_MAGIC32-6)
#define IsExtPartition(parttyp) ((parttyp) == EXTENDED || \
(parttyp) == EXTENDED_LBA )
#define IsLBAPartition(parttyp) ((parttyp) == FAT12_LBA || \
(parttyp) == FAT16_LBA || \
(parttyp) == FAT32_LBA)
#ifdef WITHFAT32
#define IsFATPartition(parttyp) ((parttyp) == FAT12 || \
(parttyp) == FAT16SMALL || \
(parttyp) == FAT16LARGE || \
(parttyp) == FAT16_LBA || \
(parttyp) == FAT32 || \
(parttyp) == FAT32_LBA)
#else
#define IsFATPartition(parttyp) ((parttyp) == FAT12 || \
(parttyp) == FAT16SMALL || \
(parttyp) == FAT16LARGE || \
(parttyp) == FAT16_LBA)
#endif
#define MSDOS_EXT_SIGN 0x29 /* extended boot sector signature */
#define MSDOS_FAT12_SIGN "FAT12 " /* FAT12 filesystem signature */
#define MSDOS_FAT16_SIGN "FAT16 " /* FAT16 filesystem signature */
#define MSDOS_FAT32_SIGN "FAT32 " /* FAT32 filesystem signature */
/* local - returned and used for BIOS interface INT 13, AH=48*/
struct _bios_LBA_disk_parameterS {
UWORD size;
UWORD information;
ULONG cylinders;
ULONG heads;
ULONG sectors;
ULONG totalSect;
ULONG totalSectHigh;
UWORD BytesPerSector;
ULONG eddparameters;
};
/* physical characteristics of a drive */
struct DriveParamS {
UBYTE driveno; /* = 0x8x */
UWORD descflags;
ULONG total_sectors;
struct CHS chs; /* for normal INT 13 */
};
struct PartTableEntry /* INTERNAL representation of partition table entry */
{
UBYTE Bootable;
UBYTE FileSystem;
struct CHS Begin;
struct CHS End;
ULONG RelSect;
ULONG NumSect;
};
/*
internal global data
*/
BOOL ExtLBAForce = FALSE;
STATIC COUNT init_readdasd(UBYTE drive)
{
iregs regs = {};
regs.a.b.h = 0x15;
regs.d.b.l = drive;
init_call_intr(0x13, ®s);
if ((regs.flags & 1) == 0)
switch (regs.a.b.h)
{
case 2:
return DF_CHANGELINE;
case 3:
return DF_FIXED;
}
return 0;
}
typedef struct {
UWORD bpb_nbyte; /* Bytes per Sector */
UBYTE bpb_nsector; /* Sectors per Allocation Unit */
UWORD bpb_nreserved; /* # Reserved Sectors */
UBYTE bpb_nfat; /* # FATs */
UWORD bpb_ndirent; /* # Root Directory entries */
UWORD bpb_nsize; /* Size in sectors */
UBYTE bpb_mdesc; /* MEDIA Descriptor Byte */
UWORD bpb_nfsect; /* FAT size in sectors */
UWORD bpb_nsecs; /* Sectors per track */
UWORD bpb_nheads; /* Number of heads */
} PACKED floppy_bpb;
typedef struct {
__DOSFAR(struct ddtstruct)ddt_next;
/* pointer to next table (offset FFFFh if last table) */
UBYTE ddt_driveno; /* physical unit number (for INT 13) */
UBYTE ddt_logdriveno; /* logical drive number (0=A:) */
bpb ddt_bpb; /* BIOS Parameter Block */
UBYTE ddt_flags;
/* bit 6: 16-bit FAT instead of 12-bit
bit 7: unsupportable disk (all accesses will return Not Ready) */
UWORD ddt_FileOC; /* Count of Open files on Drv */
UBYTE ddt_type; /* device type */
UWORD ddt_descflags; /* bit flags describing drive */
UWORD ddt_ncyl; /* number of cylinders
(for partition only, if hard disk) */
// SYM_MEMB(struct ddtstruct, bpb, ddt_defbpb); /* BPB for default (highest) capacity supported */
bpb ddt_defbpb; /* BPB for default (highest) capacity supported */
UBYTE ddt_reserved[6]; /* (part of BPB above) */
UBYTE ddt_ltrack; /* last track accessed */
union {
ULONG ddt_lasttime; /* removable media: time of last access
in clock ticks (FFFFFFFFh if never) */
struct {
UWORD ddt_part; /* partition (FFFFh = primary, 0001h = extended)
always 0001h for DOS 5+ */
UWORD ddt_abscyl; /* absolute cylinder number of partition's
start on physical drive
(FFFFh if primary partition in DOS 4.x) */
} ddt_hd;
} ddt_fh;
UBYTE ddt_volume[12]; /* ASCIIZ volume label or "NO NAME " if none
(apparently taken from extended boot record
rather than root directory) */
ULONG ddt_serialno; /* serial number */
UBYTE ddt_fstype[9]; /* ASCIIZ filesystem type ("FAT12 " or "FAT16 ") */
ULONG ddt_offset; /* relative partition offset */
} PACKED _nddt;
floppy_bpb floppy_bpbs[5] = {
/* copied from Brian Reifsnyder's FORMAT, bpb.h */
{FLOPPY_SEC_SIZE, 2, 1, 2, 112, 720, 0xfd, 2, 9, 2}, /* FD360 5.25 DS */
{FLOPPY_SEC_SIZE, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2}, /* FD1200 5.25 HD */
{FLOPPY_SEC_SIZE, 2, 1, 2, 112, 1440, 0xf9, 3, 9, 2}, /* FD720 3.5 LD */
{FLOPPY_SEC_SIZE, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2}, /* FD1440 3.5 HD */
{FLOPPY_SEC_SIZE, 2, 1, 2, 240, 5760, 0xf0, 9, 36, 2} /* FD2880 3.5 ED */
};
STATIC void make_ddt (_nddt *pddt, int Unit, int driveno, int flags);
STATIC COUNT init_getdriveparm(UBYTE drive, bpb * pbpbarray)
{
iregs regs = {};
REG UBYTE type;
if (drive & 0x80)
return 5;
regs.a.b.h = 0x08;
regs.d.b.l = drive;
init_call_intr(0x13, ®s);
type = regs.b.b.l - 1;
if (regs.flags & 1)
type = 0; /* return 320-360 for XTs */
else if (type > 6)
type = 8; /* any odd ball drives get 8&7=0: the 320-360 table */
else if (type == 5)
type = 4; /* 5 and 4 are both 2.88 MB */
memcpy(pbpbarray, &floppy_bpbs[type & 7], sizeof(floppy_bpb));
((bpb *)pbpbarray)->bpb_hidden = 0; /* very important to init to 0, see bug#1789 */
((bpb *)pbpbarray)->bpb_huge = 0;
if (type == 3)
return 7; /* 1.44 MB */
if (type == 4)
return 9; /* 2.88 almost forgot this one */
/* 0=320-360kB, 1=1.2MB, 2=720kB, 8=any odd ball drives */
return type;
}
/*
translate LBA sectors into CHS addressing
initially copied and pasted from dsk.c!
LBA to/from CHS conversion - see http://www.ata-atapi.com/ How It Works section on CHSxlat - CHS Translation
LBA (logical block address) simple 0 to N-1 used internally and with extended int 13h (BIOS)
L-CHS (logical CHS) is the CHS view when using int 13h (BIOS)
P-CHS (physical CHS) is the CHS view when directly accessing disk, should not, but could be used in BS or MBR
LBA = ( (cylinder * heads_per_cylinder + heads ) * sectors_per_track ) + sector - 1
cylinder = LBA / (heads_per_cylinder * sectors_per_track)
temp = LBA % (heads_per_cylinder * sectors_per_track)
head = temp / sectors_per_track
sector = temp % sectors_per_track + 1
where heads_per_cylinder and sectors_per_track are the current translation mode values.
cyclinder and heads are 0 to N-1 based, sector is 1 to N based
*/
STATIC void init_LBA_to_CHS(struct CHS *chs, ULONG LBA_address,
struct DriveParamS *driveparam)
{
unsigned hs = driveparam->chs.Sector * driveparam->chs.Head;
unsigned hsrem = (unsigned)(LBA_address % hs);
LBA_address /= hs;
chs->Cylinder = LBA_address >= 0x10000ul ? 0xffffu : (unsigned)LBA_address;
chs->Head = hsrem / driveparam->chs.Sector;
chs->Sector = hsrem % driveparam->chs.Sector + 1;
}
STATIC void printCHS(const char *title, struct CHS *chs)
{
/* has no fixed size for head/sect: is often 1/1 in our context */
_printf("%s%4u-%u-%u", title, chs->Cylinder, chs->Head, chs->Sector);
}
/*
reason for this modules existence:
we have found a partition, and add them to the global
partition structure.
*/
/* Compute ceil(a/b) */
#define cdiv(a, b) (((a) + (b) - 1) / (b))
/* calculates FAT data:
code adapted by Bart Oldeman from mkdosfs from the Linux dosfstools:
Author: Dave Hudson
Updated by: Roman Hodek
Portions copyright 1992, 1993 Remy Card
and 1991 Linus Torvalds
*/
/* defaults: */
#define MAXCLUSTSIZE 128
#define NSECTORFAT12 8
#define NFAT 2
STATIC VOID CalculateFATData(_nddt * pddt, ULONG NumSectors, UBYTE FileSystem)
{
ULONG fatdata;
bpb *defbpb = &pddt->ddt_defbpb;
/* FAT related items */
defbpb->bpb_nfat = NFAT;
/* normal value of number of entries in root dir */
defbpb->bpb_ndirent = 512;
defbpb->bpb_nreserved = 1;
/* SEC_SIZE * DIRENT_SIZE / defbpb->bpb_ndirent + defbpb->bpb_nreserved */
fatdata = NumSectors - (DIRENT_SIZE + 1);
if (FileSystem == FAT12 || FileSystem == FAT12_LBA)
{
unsigned fatdat;
/* in DOS, FAT12 defaults to 4096kb (8 sector) - clusters. */
defbpb->bpb_nsector = NSECTORFAT12;
/* Force maximal fatdata=32696 sectors since with our only possible sector
size (512 bytes) this is the maximum for 4k clusters.
#clus*secperclus+#fats*fatlength= 4077 * 8 + 2 * 12 = 32640.
max FAT12 size for FreeDOS = 16,728,064 bytes */
fatdat = (unsigned)fatdata;
if (fatdata > 32640)
fatdat = 32640;
/* The "+2*NSECTORFAT12" is for the reserved first two FAT entries */
defbpb->bpb_nfsect = (UWORD)cdiv((fatdat + 2 * NSECTORFAT12) * 3UL,
FLOPPY_SEC_SIZE * 2 * NSECTORFAT12 + NFAT*3);
#ifdef DEBUG
/* Need to calculate number of clusters, since the unused parts of the
* FATS and data area together could make up space for an additional,
* not really present cluster.
* (This is really done in fatfs.c, bpbtodpb) */
{
unsigned clust = (fatdat - 2 * defbpb->bpb_nfsect) / NSECTORFAT12;
unsigned maxclust = (defbpb->bpb_nfsect * 2 * MAX_SEC_SIZE) / 3;
if (maxclust > FAT12MAX)
maxclust = FAT12MAX;
DebugPrintf(("FAT12: #clu=%u, fatlength=%u, maxclu=%u, limit=%u\n",
clust, defbpb->bpb_nfsect, maxclust, FAT12MAX));
if (clust > maxclust - 2)
{
clust = maxclust - 2;
DebugPrintf(("FAT12: too many clusters: setting to maxclu-2\n"));
}
}
#endif
memcpy(pddt->ddt_fstype, MSDOS_FAT12_SIGN, 8);
}
else
{ /* FAT16/FAT32 */
CLUSTER fatlength, maxcl;
unsigned long clust, maxclust;
unsigned fatentpersec;
unsigned divisor;
#ifdef WITHFAT32
if (FileSystem == FAT32 || FileSystem == FAT32_LBA)
{
/* For FAT32, use the cluster size table described in the FAT spec:
* http://www.microsoft.com/hwdev/download/hardware/fatgen103.pdf
*/
unsigned sz_gb = (unsigned)(NumSectors / 2097152UL);
unsigned char nsector = 64; /* disks greater than 32 GB, 32K cluster */
if (sz_gb <= 32) /* disks up to 32 GB, 16K cluster */
nsector = 32;
if (sz_gb <= 16) /* disks up to 16 GB, 8K cluster */
nsector = 16;
if (sz_gb <= 8) /* disks up to 8 GB, 4K cluster */
nsector = 8;
if (NumSectors <= 532480UL) /* disks up to 260 MB, 0.5K cluster */
nsector = 1;
defbpb->bpb_nsector = nsector;
defbpb->bpb_ndirent = 0;
defbpb->bpb_nreserved = 0x20;
fatdata = NumSectors - 0x20;
fatentpersec = FLOPPY_SEC_SIZE/4;
maxcl = FAT32MAX;
}
else
#endif
{
/* FAT16: start at 4 sectors per cluster */
defbpb->bpb_nsector = 4;
/* Force maximal fatdata=8387584 sectors (NumSectors=8387617)
since with our only possible sectorsize (512 bytes) this is the
maximum we can address with 64k clusters
#clus*secperclus+#fats*fatlength=65517 * 128 + 2 * 256=8386688.
max FAT16 size for FreeDOS = 4,293,984,256 bytes = 4GiB-983,040 */
if (fatdata > 8386688ul)
fatdata = 8386688ul;
fatentpersec = FLOPPY_SEC_SIZE/2;
maxcl = FAT16MAX;
}
DebugPrintf(("%d sectors for FAT+data, starting with %d sectors/cluster\n", fatdata, defbpb->bpb_nsector));
do
{
DebugPrintf(("Trying with %d sectors/cluster:\n", defbpb->bpb_nsector));
divisor = fatentpersec * defbpb->bpb_nsector + NFAT;
fatlength = (CLUSTER)((fatdata + (2 * defbpb->bpb_nsector + divisor - 1))/
divisor);
/* Need to calculate number of clusters, since the unused parts of the
* FATS and data area together could make up space for an additional,
* not really present cluster. */
clust = (fatdata - NFAT * fatlength) / defbpb->bpb_nsector;
maxclust = fatlength * fatentpersec;
if (maxclust > maxcl)
maxclust = maxcl;
DebugPrintf(("FAT: #clu=%lu, fatlen=%u, maxclu=%lu, limit=%u\n",
clust, fatlength, maxclust, maxcl));
if (clust > maxclust - 2)
{
clust = 0;
DebugPrintf(("FAT: too many clusters\n"));
}
else if (clust <= FAT_MAGIC)
{
/* The <= 4086 avoids that the filesystem will be misdetected as having a
* 12 bit FAT. */
DebugPrintf(("FAT: would be misdetected as FAT12\n"));
clust = 0;
}
if (clust)
break;
defbpb->bpb_nsector <<= 1;
}
while (defbpb->bpb_nsector && defbpb->bpb_nsector <= MAXCLUSTSIZE);
#ifdef WITHFAT32
if (FileSystem == FAT32 || FileSystem == FAT32_LBA)
{
defbpb->bpb_nfsect = 0;
defbpb->bpb_xnfsect = fatlength;
/* set up additional FAT32 fields */
defbpb->bpb_xflags = 0;
defbpb->bpb_xfsversion = 0;
defbpb->bpb_xrootclst = 2;
defbpb->bpb_xfsinfosec = 1;
defbpb->bpb_xbackupsec = 6;
memcpy(pddt->ddt_fstype, MSDOS_FAT32_SIGN, 8);
}
else
#endif
{
defbpb->bpb_nfsect = (UWORD)fatlength;
memcpy(pddt->ddt_fstype, MSDOS_FAT16_SIGN, 8);
}
}
pddt->ddt_fstype[8] = '\0';
}
STATIC void push_ddt(_nddt *pddt)
{
ddt FAR *fddt = (ddt FAR *)DynAlloc("ddt", 1, sizeof(ddt));
n_fmemcpy(fddt, pddt, sizeof(ddt));
if (nUnits != 0) {
ddt_buf[nUnits - 1]->ddt_next = fddt;
if (pddt->ddt_driveno == 0 && pddt->ddt_logdriveno == 1)
ddt_buf[nUnits - 1]->ddt_descflags |= DF_CURLOG | DF_MULTLOG;
}
ddt_buf[nUnits] = fddt;
}
STATIC void DosDefinePartition(struct DriveParamS *driveParam,
ULONG StartSector, struct PartTableEntry *pEntry,
int extendedPartNo, int PrimaryNum)
{
_nddt nddt;
_nddt *pddt = &nddt;
struct CHS chs;
while (1)
{
if (nUnits >= NDEV)
{
_printf("more Partitions detected then possible, max = %d\n", NDEV);
return; /* we are done */
}
if (!((1 << nUnits) & bprm.DriveMask))
break;
/* drive masked out */
make_ddt(&nddt, nUnits, 0, DF_NOACCESS);
nUnits++;
}
pddt->ddt_next = MK_FP(0, 0xffff);
pddt->ddt_driveno = driveParam->driveno;
pddt->ddt_logdriveno = nUnits;
pddt->ddt_descflags = driveParam->descflags;
/* Turn of LBA if not forced and the partition is within 1023 cyls and of the right type */
/* the FileSystem type was internally converted to LBA_xxxx if a non-LBA partition
above cylinder 1023 was found */
if (!InitKernelConfig.ForceLBA && !ExtLBAForce && !IsLBAPartition(pEntry->FileSystem))
pddt->ddt_descflags &= ~DF_LBA;
pddt->ddt_ncyl = driveParam->chs.Cylinder;
DebugPrintf(("LBA %senabled for drive %c:\n", (pddt->ddt_descflags & DF_LBA)?"":"not ", 'A' + nUnits));
pddt->ddt_offset = StartSector;
pddt->ddt_defbpb.bpb_nbyte = FLOPPY_SEC_SIZE;
pddt->ddt_defbpb.bpb_mdesc = 0xf8;
pddt->ddt_defbpb.bpb_nheads = driveParam->chs.Head;
pddt->ddt_defbpb.bpb_nsecs = driveParam->chs.Sector;
pddt->ddt_defbpb.bpb_hidden = pEntry->RelSect;
pddt->ddt_defbpb.bpb_nsize = 0;
pddt->ddt_defbpb.bpb_huge = pEntry->NumSect;
if (pEntry->NumSect <= 0xffff)
{
pddt->ddt_defbpb.bpb_nsize = (UWORD) (pEntry->NumSect);
pddt->ddt_defbpb.bpb_huge = 0; /* may still be set on Win95 */
}
/* sectors per cluster, sectors per FAT etc. */
CalculateFATData(pddt, pEntry->NumSect, pEntry->FileSystem);
pddt->ddt_serialno = 0x12345678l;
/* drive inaccessible until bldbpb successful */
pddt->ddt_descflags |= init_readdasd(pddt->ddt_driveno) | DF_NOACCESS;
pddt->ddt_type = 5;
memcpy(&pddt->ddt_bpb, &pddt->ddt_defbpb, sizeof(bpb));
push_ddt(pddt);
/* Alain whishes to keep this in later versions, too
Tom likes this too, so he made it configurable by SYS CONFIG ...
*/
if (InitKernelConfig.InitDiskShowDriveAssignment)
{
const char *ExtPri;
int num;
LBA_to_CHS(&chs, StartSector, driveParam);
ExtPri = "Pri";
num = PrimaryNum + 1;
if (extendedPartNo)
{
ExtPri = "Ext";
num = extendedPartNo;
}
_printf("\r%c: HD%d, %s[%2d]", 'A' + nUnits,
(driveParam->driveno & 0x7f) + 1, ExtPri, num);
printCHS(", CHS= ", &chs);
_printf(", start=%6u MB, size=%6u MB\n",
StartSector / 2048, pEntry->NumSect / 2048);
}
nUnits++;
}
/* Get the parameters of the hard disk */
STATIC int LBA_Get_Drive_Parameters(int drive, struct DriveParamS *driveParam)
{
iregs regs = {};
struct _bios_LBA_disk_parameterS lba_bios_parameters;
struct _bios_LBA_disk_parameterS FAR *bp;
ExtLBAForce = FALSE;
memset(driveParam, 0, sizeof *driveParam);
drive |= 0x80;
/* for tests - disable LBA support,
even if exists */
if (!InitKernelConfig.GlobalEnableLBAsupport)
{
goto StandardBios;
}
/* check for LBA support */
regs.b.x = 0x55aa;
regs.a.b.h = 0x41;
regs.d.b.l = drive;
init_call_intr(0x13, ®s);
if (regs.b.x != 0xaa55 || (regs.flags & 0x01))
{
goto StandardBios;
}
/* by ralph :
if DAP cannot be used, don't use
LBA
*/
if ((regs.c.x & 1) == 0)
{
goto StandardBios;
}
/* drive supports LBA addressing */
/* version 1.0, 2.0 have different verify */
if (regs.a.x < 0x2100)
LBA_WRITE_VERIFY = 0x4301;
memset(&lba_bios_parameters, 0, sizeof(lba_bios_parameters));
lba_bios_parameters.size = sizeof(lba_bios_parameters);
bp = MK_FAR(lba_bios_parameters);
regs.si = FP_OFF_OBJ(®s, bp);
regs.ds = FP_SEG_OBJ(®s, bp);
regs.a.b.h = 0x48;
regs.d.b.l = drive;
init_call_intr(0x13, ®s);
/* error or DMA boundary errors not handled transparently */
if (regs.flags & 0x01)
{
goto StandardBios;
}
/* verify maximum settings, we can't handle more */
if (lba_bios_parameters.heads > 0xffff ||
lba_bios_parameters.sectors > 0xffff ||
lba_bios_parameters.totalSectHigh != 0)
{
_printf("Drive is too large to handle, using only 1st 8 GB\n"
" drive %02x heads %u sectors %u , total=0x%x-%08x\n",
drive,
(ULONG) lba_bios_parameters.heads,
(ULONG) lba_bios_parameters.sectors,
(ULONG) lba_bios_parameters.totalSect,
(ULONG) lba_bios_parameters.totalSectHigh);
goto StandardBios;
}
driveParam->total_sectors = lba_bios_parameters.totalSect;
/* if we arrive here, success */
driveParam->descflags = DF_LBA;
if (lba_bios_parameters.information & 8)
driveParam->descflags |= DF_WRTVERIFY;
if (lba_bios_parameters.information & 1)
driveParam->descflags |= DF_DMA_TRANSPARENT; /* DMA boundary errors are handled transparently */
StandardBios: /* old way to get parameters */
regs.a.b.h = 0x08;
regs.d.b.l = drive;
init_call_intr(0x13, ®s);
if (regs.flags & 0x01)
goto ErrorReturn;
/* int13h call returns max value, store as count (#) i.e. +1 for 0 based heads & cylinders */
driveParam->chs.Head = (regs.d.x >> 8) + 1; /* DH = max head value = # of heads - 1 (0-255) */
driveParam->chs.Sector = (regs.c.x & 0x3f); /* CL bits 0-5 = max sector value = # (sectors/track) - 1 (1-63) */
/* max cylinder value = # cylinders - 1 (0-1023) = [high two bits]CL7:6=cyls9:8, [low byte]CH=cyls7:0 */
driveParam->chs.Cylinder = ((regs.c.x >> 8) | ((regs.c.x & 0xc0) << 2)) + 1;
if (driveParam->chs.Sector == 0) {
/* happens e.g. with Bochs 1.x if no harddisk defined */
driveParam->chs.Sector = 63; /* avoid division by zero...! */
_printf("BIOS reported 0 sectors/track, assuming 63!\n");
}
if (!(driveParam->descflags & DF_LBA))
{
driveParam->total_sectors =
(ULONG)driveParam->chs.Cylinder
* driveParam->chs.Head * driveParam->chs.Sector;
}
driveParam->driveno = drive;
DebugPrintf(("drive %02Xh total: C = %u, H = %u, S = %u,",
drive,
driveParam->chs.Cylinder,
driveParam->chs.Head, driveParam->chs.Sector));
DebugPrintf((" total size %uMB\n", driveParam->total_sectors / 2048));
ErrorReturn:
return driveParam->driveno;
}
/*
converts physical into logical representation of partition entry
*/
STATIC void ConvCHSToIntern(struct CHS *chs, UBYTE * pDisk)
{
chs->Head = pDisk[0];
chs->Sector = pDisk[1] & 0x3f;
chs->Cylinder = pDisk[2] + ((pDisk[1] & 0xc0) << 2);
}
STATIC BOOL ConvPartTableEntryToIntern(struct PartTableEntry * pEntry,
UBYTE FAR * pDisk)
{
int i;
if (pDisk[0x1fe] != 0x55 || pDisk[0x1ff] != 0xaa)
{
memset(pEntry, 0, 4 * sizeof(struct PartTableEntry));
return FALSE;
}
pDisk += 0x1be;
for (i = 0; i < 4; i++, pDisk += 16, pEntry++)
{
pEntry->Bootable = pDisk[0];
pEntry->FileSystem = pDisk[4];
ConvCHSToIntern(&pEntry->Begin, pDisk+1);
ConvCHSToIntern(&pEntry->End, pDisk+5);
pEntry->RelSect = *(ULONG *) (pDisk + 8);
pEntry->NumSect = *(ULONG *) (pDisk + 12);
}
return TRUE;
}
STATIC BOOL is_suspect(struct CHS *chs, struct CHS *pEntry_chs)
{
/* Valid entry:
entry == chs || // partition entry equal to computed values
(chs->Cylinder > 1023 && // or LBA partition
(entry->Cylinder == 1023 ||
entry->Cylinder == (0x3FF & chs->Cylinder)))
*/
return !((pEntry_chs->Cylinder == chs->Cylinder &&
pEntry_chs->Head == chs->Head &&
pEntry_chs->Sector == chs->Sector) ||
(chs->Cylinder > 1023u &&
(pEntry_chs->Cylinder == 1023 ||
pEntry_chs->Cylinder == (0x3ff & chs->Cylinder))));
}
STATIC void print_warning_suspect(char *partitionName, UBYTE fs, struct CHS *chs,
struct CHS *pEntry_chs)
{
if (!InitKernelConfig.ForceLBA)
{
_printf("WARNING: using suspect partition %s FS %02x:", partitionName, fs);
printCHS(" with calculated values ", chs);
printCHS(" instead of ", pEntry_chs);
_printf("\n");
}
memcpy(pEntry_chs, chs, sizeof(struct CHS));
}
STATIC BOOL ScanForPrimaryPartitions(struct DriveParamS * driveParam, int scan_type,
struct PartTableEntry * pEntry, ULONG startSector,
int partitionsToIgnore, int extendedPartNo)
{
int i;
struct CHS chs, end;
ULONG partitionStart;
char partitionName[12];
for (i = 0; i < 4; i++, pEntry++)
{
if (pEntry->FileSystem == 0)
continue;
if (partitionsToIgnore & (1 << i))
continue;
if (IsExtPartition(pEntry->FileSystem))
continue;
if (scan_type == SCAN_PRIMARYBOOT && !pEntry->Bootable)
continue;
partitionStart = startSector + pEntry->RelSect;
if (!IsFATPartition(pEntry->FileSystem))
{
continue;
}
if (extendedPartNo)
_sprintf(partitionName, "Ext:%d", extendedPartNo);
else
_sprintf(partitionName, "Pri:%d", i + 1);
/*
some sanity checks, that partition
structure is OK
*/
LBA_to_CHS(&chs, partitionStart, driveParam);
LBA_to_CHS(&end, partitionStart + pEntry->NumSect - 1, driveParam);
/* some FDISKs enter for partitions
> 8 GB cyl = 1023, other (cyl&1023)
*/
if (is_suspect(&chs, &pEntry->Begin))
{
print_warning_suspect(partitionName, pEntry->FileSystem, &chs,
&pEntry->Begin);
}
if (is_suspect(&end, &pEntry->End))
{
if (pEntry->NumSect == 0)
{
_printf("Not using partition %s with 0 sectors\n", partitionName);
continue;
}
print_warning_suspect(partitionName, pEntry->FileSystem, &end,
&pEntry->End);
}
if (chs.Cylinder > 1023 || end.Cylinder > 1023)
{
if (!(driveParam->descflags & DF_LBA))
{
_printf
("can't use LBA partition without LBA support - part %s FS %02x",
partitionName, pEntry->FileSystem);
printCHS(" start ", &chs);
printCHS(", end ", &end);
_printf("\n");
continue;
}
if (!InitKernelConfig.ForceLBA && !ExtLBAForce
&& !IsLBAPartition(pEntry->FileSystem))
{
_printf
("WARNING: Partition ID does not suggest LBA - part %s FS %02x.\n"
"Please run FDISK to correct this - using LBA to access partition.\n",
partitionName, pEntry->FileSystem);
printCHS(" start ", &chs);
printCHS(", end ", &end);
_printf("\n");
pEntry->FileSystem = (pEntry->FileSystem == FAT12 ? FAT12_LBA :
pEntry->FileSystem == FAT32 ? FAT32_LBA :
/* pEntry->FileSystem == FAT16 ? */
FAT16_LBA);
}
/* else its a diagnostic message only */
#ifdef DEBUG
DebugPrintf(("found and using LBA partition %s FS %02x",
partitionName, pEntry->FileSystem));
printCHS(" start ", &chs);
printCHS(", end ", &end);
DebugPrintf(("\n"));
#endif
}
/*
here we have a partition table in our hand !!
*/
partitionsToIgnore |= 1 << i;
DosDefinePartition(driveParam, partitionStart, pEntry,
extendedPartNo, i);
if (scan_type == SCAN_PRIMARYBOOT || scan_type == SCAN_PRIMARY)
{
return partitionsToIgnore;
}
}
return partitionsToIgnore;
}