Sync with trunk r63283
[reactos.git] / boot / freeldr / freeldr / arch / i386 / hwdisk.c
1 /*
2 * FreeLoader
3 *
4 * Copyright (C) 2003, 2004 Eric Kohl
5 * Copyright (C) 2009 Hervé Poussineau
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #include <freeldr.h>
23
24 #define NDEBUG
25 #include <debug.h>
26
27 DBG_DEFAULT_CHANNEL(HWDETECT);
28
29 typedef struct tagDISKCONTEXT
30 {
31 UCHAR DriveNumber;
32 ULONG SectorSize;
33 ULONGLONG SectorOffset;
34 ULONGLONG SectorCount;
35 ULONGLONG SectorNumber;
36 } DISKCONTEXT;
37
38 extern ULONG reactos_disk_count;
39 extern ARC_DISK_SIGNATURE reactos_arc_disk_info[];
40 extern CHAR reactos_arc_strings[32][256];
41
42 static CHAR Hex[] = "0123456789abcdef";
43 UCHAR PcBiosDiskCount = 0;
44 CHAR PcDiskIdentifier[32][20];
45
46
47 static LONG DiskClose(ULONG FileId)
48 {
49 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
50
51 FrLdrTempFree(Context, TAG_HW_DISK_CONTEXT);
52 return ESUCCESS;
53 }
54
55 static LONG DiskGetFileInformation(ULONG FileId, FILEINFORMATION* Information)
56 {
57 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
58
59 RtlZeroMemory(Information, sizeof(FILEINFORMATION));
60 Information->EndingAddress.QuadPart = (Context->SectorOffset + Context->SectorCount) * Context->SectorSize;
61 Information->CurrentAddress.QuadPart = (Context->SectorOffset + Context->SectorNumber) * Context->SectorSize;
62
63 return ESUCCESS;
64 }
65
66 static LONG DiskOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
67 {
68 DISKCONTEXT* Context;
69 UCHAR DriveNumber;
70 ULONG DrivePartition, SectorSize;
71 ULONGLONG SectorOffset = 0;
72 ULONGLONG SectorCount = 0;
73 PARTITION_TABLE_ENTRY PartitionTableEntry;
74 CHAR FileName[1];
75
76 if (!DissectArcPath(Path, FileName, &DriveNumber, &DrivePartition))
77 return EINVAL;
78
79 if (DrivePartition == 0xff)
80 {
81 /* This is a CD-ROM device */
82 SectorSize = 2048;
83 }
84 else
85 {
86 /* This is either a floppy disk device (DrivePartition == 0) or
87 * a hard disk device (DrivePartition != 0 && DrivePartition != 0xFF) but
88 * it doesn't matter which one because they both have 512 bytes per sector */
89 SectorSize = 512;
90 }
91
92 if (DrivePartition != 0xff && DrivePartition != 0)
93 {
94 if (!DiskGetPartitionEntry(DriveNumber, DrivePartition, &PartitionTableEntry))
95 return EINVAL;
96 SectorOffset = PartitionTableEntry.SectorCountBeforePartition;
97 SectorCount = PartitionTableEntry.PartitionSectorCount;
98 }
99
100 Context = FrLdrTempAlloc(sizeof(DISKCONTEXT), TAG_HW_DISK_CONTEXT);
101 if (!Context)
102 return ENOMEM;
103 Context->DriveNumber = DriveNumber;
104 Context->SectorSize = SectorSize;
105 Context->SectorOffset = SectorOffset;
106 Context->SectorCount = SectorCount;
107 Context->SectorNumber = 0;
108 FsSetDeviceSpecific(*FileId, Context);
109
110 return ESUCCESS;
111 }
112
113 static LONG DiskRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
114 {
115 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
116 UCHAR* Ptr = (UCHAR*)Buffer;
117 ULONG Length, TotalSectors, MaxSectors, ReadSectors;
118 BOOLEAN ret;
119 ULONGLONG SectorOffset;
120
121 TotalSectors = (N + Context->SectorSize - 1) / Context->SectorSize;
122 MaxSectors = DISKREADBUFFER_SIZE / Context->SectorSize;
123 SectorOffset = Context->SectorNumber + Context->SectorOffset;
124
125 ret = 1;
126
127 while (TotalSectors)
128 {
129 ReadSectors = TotalSectors;
130 if (ReadSectors > MaxSectors)
131 ReadSectors = MaxSectors;
132
133 ret = MachDiskReadLogicalSectors(
134 Context->DriveNumber,
135 SectorOffset,
136 ReadSectors,
137 (PVOID)DISKREADBUFFER);
138 if (!ret)
139 break;
140
141 Length = ReadSectors * Context->SectorSize;
142 if (Length > N)
143 Length = N;
144
145 RtlCopyMemory(Ptr, (PVOID)DISKREADBUFFER, Length);
146
147 Ptr += Length;
148 N -= Length;
149 SectorOffset += ReadSectors;
150 TotalSectors -= ReadSectors;
151 }
152
153 *Count = (ULONG)(Ptr - (UCHAR*)Buffer);
154
155 return (!ret) ? EIO : ESUCCESS;
156 }
157
158 static LONG DiskSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
159 {
160 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
161
162 if (SeekMode != SeekAbsolute)
163 return EINVAL;
164 if (Position->LowPart & (Context->SectorSize - 1))
165 return EINVAL;
166
167 Context->SectorNumber = (ULONG)(Position->QuadPart / Context->SectorSize);
168 return ESUCCESS;
169 }
170
171 static const DEVVTBL DiskVtbl = {
172 DiskClose,
173 DiskGetFileInformation,
174 DiskOpen,
175 DiskRead,
176 DiskSeek,
177 };
178
179 PCHAR
180 GetHarddiskIdentifier(
181 UCHAR DriveNumber)
182 {
183 return PcDiskIdentifier[DriveNumber - 0x80];
184 }
185
186 VOID
187 GetHarddiskInformation(
188 UCHAR DriveNumber)
189 {
190 PMASTER_BOOT_RECORD Mbr;
191 ULONG *Buffer;
192 ULONG i;
193 ULONG Checksum;
194 ULONG Signature;
195 CHAR ArcName[256];
196 PARTITION_TABLE_ENTRY PartitionTableEntry;
197 PCHAR Identifier = PcDiskIdentifier[DriveNumber - 0x80];
198
199 /* Read the MBR */
200 if (!MachDiskReadLogicalSectors(DriveNumber, 0ULL, 1, (PVOID)DISKREADBUFFER))
201 {
202 ERR("Reading MBR failed\n");
203 return;
204 }
205
206 Buffer = (ULONG*)DISKREADBUFFER;
207 Mbr = (PMASTER_BOOT_RECORD)DISKREADBUFFER;
208
209 Signature = Mbr->Signature;
210 TRACE("Signature: %x\n", Signature);
211
212 /* Calculate the MBR checksum */
213 Checksum = 0;
214 for (i = 0; i < 128; i++)
215 {
216 Checksum += Buffer[i];
217 }
218 Checksum = ~Checksum + 1;
219 TRACE("Checksum: %x\n", Checksum);
220
221 /* Fill out the ARC disk block */
222 reactos_arc_disk_info[reactos_disk_count].Signature = Signature;
223 reactos_arc_disk_info[reactos_disk_count].CheckSum = Checksum;
224 sprintf(ArcName, "multi(0)disk(0)rdisk(%lu)", reactos_disk_count);
225 strcpy(reactos_arc_strings[reactos_disk_count], ArcName);
226 reactos_arc_disk_info[reactos_disk_count].ArcName =
227 reactos_arc_strings[reactos_disk_count];
228 reactos_disk_count++;
229
230 sprintf(ArcName, "multi(0)disk(0)rdisk(%u)partition(0)", DriveNumber - 0x80);
231 FsRegisterDevice(ArcName, &DiskVtbl);
232
233 /* Add partitions */
234 i = 1;
235 DiskReportError(FALSE);
236 while (DiskGetPartitionEntry(DriveNumber, i, &PartitionTableEntry))
237 {
238 if (PartitionTableEntry.SystemIndicator != PARTITION_ENTRY_UNUSED)
239 {
240 sprintf(ArcName, "multi(0)disk(0)rdisk(%u)partition(%lu)", DriveNumber - 0x80, i);
241 FsRegisterDevice(ArcName, &DiskVtbl);
242 }
243 i++;
244 }
245 DiskReportError(TRUE);
246
247 /* Convert checksum and signature to identifier string */
248 Identifier[0] = Hex[(Checksum >> 28) & 0x0F];
249 Identifier[1] = Hex[(Checksum >> 24) & 0x0F];
250 Identifier[2] = Hex[(Checksum >> 20) & 0x0F];
251 Identifier[3] = Hex[(Checksum >> 16) & 0x0F];
252 Identifier[4] = Hex[(Checksum >> 12) & 0x0F];
253 Identifier[5] = Hex[(Checksum >> 8) & 0x0F];
254 Identifier[6] = Hex[(Checksum >> 4) & 0x0F];
255 Identifier[7] = Hex[Checksum & 0x0F];
256 Identifier[8] = '-';
257 Identifier[9] = Hex[(Signature >> 28) & 0x0F];
258 Identifier[10] = Hex[(Signature >> 24) & 0x0F];
259 Identifier[11] = Hex[(Signature >> 20) & 0x0F];
260 Identifier[12] = Hex[(Signature >> 16) & 0x0F];
261 Identifier[13] = Hex[(Signature >> 12) & 0x0F];
262 Identifier[14] = Hex[(Signature >> 8) & 0x0F];
263 Identifier[15] = Hex[(Signature >> 4) & 0x0F];
264 Identifier[16] = Hex[Signature & 0x0F];
265 Identifier[17] = '-';
266 Identifier[18] = 'A';
267 Identifier[19] = 0;
268 TRACE("Identifier: %s\n", Identifier);
269 }
270
271
272 BOOLEAN
273 HwInitializeBiosDisks(VOID)
274 {
275 UCHAR DiskCount, DriveNumber;
276 ULONG i;
277 BOOLEAN Changed;
278 CHAR BootPath[512];
279 BOOLEAN BootDriveReported = FALSE;
280
281 /* Count the number of visible drives */
282 DiskReportError(FALSE);
283 DiskCount = 0;
284 DriveNumber = 0x80;
285
286 /* There are some really broken BIOSes out there. There are even BIOSes
287 * that happily report success when you ask them to read from non-existent
288 * harddisks. So, we set the buffer to known contents first, then try to
289 * read. If the BIOS reports success but the buffer contents haven't
290 * changed then we fail anyway */
291 memset((PVOID) DISKREADBUFFER, 0xcd, 512);
292 while (MachDiskReadLogicalSectors(DriveNumber, 0ULL, 1, (PVOID)DISKREADBUFFER))
293 {
294 Changed = FALSE;
295 for (i = 0; ! Changed && i < 512; i++)
296 {
297 Changed = ((PUCHAR)DISKREADBUFFER)[i] != 0xcd;
298 }
299 if (! Changed)
300 {
301 TRACE("BIOS reports success for disk %d but data didn't change\n",
302 (int)DiskCount);
303 break;
304 }
305
306 GetHarddiskInformation(DriveNumber);
307
308 if (FrldrBootDrive == DriveNumber)
309 BootDriveReported = TRUE;
310
311 DiskCount++;
312 DriveNumber++;
313 memset((PVOID) DISKREADBUFFER, 0xcd, 512);
314 }
315 DiskReportError(TRUE);
316
317 /* Get the drive we're booting from */
318 MachDiskGetBootPath(BootPath, sizeof(BootPath));
319
320 /* Add it, if it's a floppy or cdrom */
321 if ((FrldrBootDrive >= 0x80 && !BootDriveReported) ||
322 DiskIsDriveRemovable(FrldrBootDrive))
323 {
324 /* TODO: Check if it's really a cdrom drive */
325 ULONG* Buffer;
326 ULONG Checksum = 0;
327
328 /* Read the MBR */
329 if (!MachDiskReadLogicalSectors(FrldrBootDrive, 16ULL, 1, (PVOID)DISKREADBUFFER))
330 {
331 ERR("Reading MBR failed\n");
332 return FALSE;
333 }
334
335 Buffer = (ULONG*)DISKREADBUFFER;
336
337 /* Calculate the MBR checksum */
338 for (i = 0; i < 2048 / sizeof(ULONG); i++) Checksum += Buffer[i];
339 Checksum = ~Checksum + 1;
340 TRACE("Checksum: %x\n", Checksum);
341
342 /* Fill out the ARC disk block */
343 reactos_arc_disk_info[reactos_disk_count].CheckSum = Checksum;
344 strcpy(reactos_arc_strings[reactos_disk_count], BootPath);
345 reactos_arc_disk_info[reactos_disk_count].ArcName =
346 reactos_arc_strings[reactos_disk_count];
347 reactos_disk_count++;
348
349 FsRegisterDevice(BootPath, &DiskVtbl);
350 DiskCount++;
351 }
352
353 PcBiosDiskCount = DiskCount;
354 TRACE("BIOS reports %d harddisk%s\n",
355 (int)DiskCount, (DiskCount == 1) ? "": "s");
356
357 return DiskCount != 0;
358 }