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