[CLT2012]
[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 MmHeapFree(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 = MmHeapAlloc(sizeof(DISKCONTEXT));
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 i, Length;
118 BOOLEAN ret;
119
120 *Count = 0;
121 i = 0;
122 while (N > 0)
123 {
124 Length = N;
125 if (Length > Context->SectorSize)
126 Length = Context->SectorSize;
127 ret = MachDiskReadLogicalSectors(
128 Context->DriveNumber,
129 Context->SectorNumber + Context->SectorOffset + i,
130 1,
131 (PVOID)DISKREADBUFFER);
132 if (!ret)
133 return EIO;
134 RtlCopyMemory(Ptr, (PVOID)DISKREADBUFFER, Length);
135 Ptr += Length;
136 *Count += Length;
137 N -= Length;
138 i++;
139 }
140
141 return ESUCCESS;
142 }
143
144 static LONG DiskSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
145 {
146 DISKCONTEXT* Context = FsGetDeviceSpecific(FileId);
147
148 if (SeekMode != SeekAbsolute)
149 return EINVAL;
150 if (Position->LowPart & (Context->SectorSize - 1))
151 return EINVAL;
152
153 /* FIXME: take HighPart into account */
154 Context->SectorNumber = Position->LowPart / Context->SectorSize;
155 return ESUCCESS;
156 }
157
158 static const DEVVTBL DiskVtbl = {
159 DiskClose,
160 DiskGetFileInformation,
161 DiskOpen,
162 DiskRead,
163 DiskSeek,
164 };
165
166 PCHAR
167 GetHarddiskIdentifier(
168 UCHAR DriveNumber)
169 {
170 return PcDiskIdentifier[DriveNumber - 0x80];
171 }
172
173 VOID
174 GetHarddiskInformation(
175 UCHAR DriveNumber)
176 {
177 PMASTER_BOOT_RECORD Mbr;
178 ULONG *Buffer;
179 ULONG i;
180 ULONG Checksum;
181 ULONG Signature;
182 CHAR ArcName[256];
183 PARTITION_TABLE_ENTRY PartitionTableEntry;
184 PCHAR Identifier = PcDiskIdentifier[DriveNumber - 0x80];
185
186 /* Read the MBR */
187 if (!MachDiskReadLogicalSectors(DriveNumber, 0ULL, 1, (PVOID)DISKREADBUFFER))
188 {
189 ERR("Reading MBR failed\n");
190 return;
191 }
192
193 Buffer = (ULONG*)DISKREADBUFFER;
194 Mbr = (PMASTER_BOOT_RECORD)DISKREADBUFFER;
195
196 Signature = Mbr->Signature;
197 TRACE("Signature: %x\n", Signature);
198
199 /* Calculate the MBR checksum */
200 Checksum = 0;
201 for (i = 0; i < 128; i++)
202 {
203 Checksum += Buffer[i];
204 }
205 Checksum = ~Checksum + 1;
206 TRACE("Checksum: %x\n", Checksum);
207
208 /* Fill out the ARC disk block */
209 reactos_arc_disk_info[reactos_disk_count].Signature = Signature;
210 reactos_arc_disk_info[reactos_disk_count].CheckSum = Checksum;
211 sprintf(ArcName, "multi(0)disk(0)rdisk(%lu)", reactos_disk_count);
212 strcpy(reactos_arc_strings[reactos_disk_count], ArcName);
213 reactos_arc_disk_info[reactos_disk_count].ArcName =
214 reactos_arc_strings[reactos_disk_count];
215 reactos_disk_count++;
216
217 sprintf(ArcName, "multi(0)disk(0)rdisk(%u)partition(0)", DriveNumber - 0x80);
218 FsRegisterDevice(ArcName, &DiskVtbl);
219
220 /* Add partitions */
221 i = 1;
222 DiskReportError(FALSE);
223 while (DiskGetPartitionEntry(DriveNumber, i, &PartitionTableEntry))
224 {
225 if (PartitionTableEntry.SystemIndicator != PARTITION_ENTRY_UNUSED)
226 {
227 sprintf(ArcName, "multi(0)disk(0)rdisk(%u)partition(%lu)", DriveNumber - 0x80, i);
228 FsRegisterDevice(ArcName, &DiskVtbl);
229 }
230 i++;
231 }
232 DiskReportError(TRUE);
233
234 /* Convert checksum and signature to identifier string */
235 Identifier[0] = Hex[(Checksum >> 28) & 0x0F];
236 Identifier[1] = Hex[(Checksum >> 24) & 0x0F];
237 Identifier[2] = Hex[(Checksum >> 20) & 0x0F];
238 Identifier[3] = Hex[(Checksum >> 16) & 0x0F];
239 Identifier[4] = Hex[(Checksum >> 12) & 0x0F];
240 Identifier[5] = Hex[(Checksum >> 8) & 0x0F];
241 Identifier[6] = Hex[(Checksum >> 4) & 0x0F];
242 Identifier[7] = Hex[Checksum & 0x0F];
243 Identifier[8] = '-';
244 Identifier[9] = Hex[(Signature >> 28) & 0x0F];
245 Identifier[10] = Hex[(Signature >> 24) & 0x0F];
246 Identifier[11] = Hex[(Signature >> 20) & 0x0F];
247 Identifier[12] = Hex[(Signature >> 16) & 0x0F];
248 Identifier[13] = Hex[(Signature >> 12) & 0x0F];
249 Identifier[14] = Hex[(Signature >> 8) & 0x0F];
250 Identifier[15] = Hex[(Signature >> 4) & 0x0F];
251 Identifier[16] = Hex[Signature & 0x0F];
252 Identifier[17] = '-';
253 Identifier[18] = 'A';
254 Identifier[19] = 0;
255 TRACE("Identifier: %s\n", Identifier);
256 }
257
258
259 VOID
260 HwInitializeBiosDisks(VOID)
261 {
262 UCHAR DiskCount, DriveNumber;
263 ULONG i;
264 BOOLEAN Changed;
265 CHAR BootPath[512];
266 BOOLEAN BootDriveReported = FALSE;
267
268 /* Count the number of visible drives */
269 DiskReportError(FALSE);
270 DiskCount = 0;
271 DriveNumber = 0x80;
272
273 /* There are some really broken BIOSes out there. There are even BIOSes
274 * that happily report success when you ask them to read from non-existent
275 * harddisks. So, we set the buffer to known contents first, then try to
276 * read. If the BIOS reports success but the buffer contents haven't
277 * changed then we fail anyway */
278 memset((PVOID) DISKREADBUFFER, 0xcd, 512);
279 while (MachDiskReadLogicalSectors(DriveNumber, 0ULL, 1, (PVOID)DISKREADBUFFER))
280 {
281 Changed = FALSE;
282 for (i = 0; ! Changed && i < 512; i++)
283 {
284 Changed = ((PUCHAR)DISKREADBUFFER)[i] != 0xcd;
285 }
286 if (! Changed)
287 {
288 TRACE("BIOS reports success for disk %d but data didn't change\n",
289 (int)DiskCount);
290 break;
291 }
292
293 GetHarddiskInformation(DriveNumber);
294
295 if (FrldrBootDrive == DriveNumber)
296 BootDriveReported = TRUE;
297
298 DiskCount++;
299 DriveNumber++;
300 memset((PVOID) DISKREADBUFFER, 0xcd, 512);
301 }
302 DiskReportError(TRUE);
303 TRACE("BIOS reports %d harddisk%s\n",
304 (int)DiskCount, (DiskCount == 1) ? "": "s");
305
306 /* Get the drive we're booting from */
307 MachDiskGetBootPath(BootPath, sizeof(BootPath));
308
309 /* Add it, if it's a floppy or cdrom */
310 if ((FrldrBootDrive >= 0x80 && !BootDriveReported) ||
311 DiskIsDriveRemovable(FrldrBootDrive))
312 {
313 /* TODO: Check if it's really a cdrom drive */
314 ULONG* Buffer;
315 ULONG Checksum = 0;
316
317 /* Read the MBR */
318 if (!MachDiskReadLogicalSectors(FrldrBootDrive, 16ULL, 1, (PVOID)DISKREADBUFFER))
319 {
320 ERR("Reading MBR failed\n");
321 return;
322 }
323
324 Buffer = (ULONG*)DISKREADBUFFER;
325
326 /* Calculate the MBR checksum */
327 for (i = 0; i < 2048 / sizeof(ULONG); i++) Checksum += Buffer[i];
328 Checksum = ~Checksum + 1;
329 TRACE("Checksum: %x\n", Checksum);
330
331 /* Fill out the ARC disk block */
332 reactos_arc_disk_info[reactos_disk_count].CheckSum = Checksum;
333 strcpy(reactos_arc_strings[reactos_disk_count], BootPath);
334 reactos_arc_disk_info[reactos_disk_count].ArcName =
335 reactos_arc_strings[reactos_disk_count];
336 reactos_disk_count++;
337
338 FsRegisterDevice(BootPath, &DiskVtbl);
339 }
340
341 PcBiosDiskCount = DiskCount;
342 }