[FREELDR] Improvements for the RamDisk support.
[reactos.git] / boot / freeldr / freeldr / disk / ramdisk.c
1 /*
2 * PROJECT: FreeLoader
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * PURPOSE: Implements routines to support booting from a RAM Disk.
5 * COPYRIGHT: Copyright 2008 ReactOS Portable Systems Group
6 * Copyright 2009 Hervé Poussineau
7 * Copyright 2019 Hermes Belusca-Maito
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #include <freeldr.h>
13
14 /* GLOBALS ********************************************************************/
15
16 PVOID gInitRamDiskBase = NULL;
17 ULONG gInitRamDiskSize = 0;
18
19 static BOOLEAN RamDiskDeviceRegistered = FALSE;
20 static PVOID RamDiskBase;
21 static ULONGLONG RamDiskFileSize; // FIXME: RAM disks currently limited to 4GB.
22 static ULONGLONG RamDiskImageLength; // Size of valid data in the Ramdisk (usually == RamDiskFileSize - RamDiskImageOffset)
23 static ULONG RamDiskImageOffset; // Starting offset from the Ramdisk base.
24 static ULONGLONG RamDiskOffset; // Current position in the Ramdisk.
25
26 /* FUNCTIONS ******************************************************************/
27
28 static ARC_STATUS RamDiskClose(ULONG FileId)
29 {
30 /* Nothing to do */
31 return ESUCCESS;
32 }
33
34 static ARC_STATUS RamDiskGetFileInformation(ULONG FileId, FILEINFORMATION* Information)
35 {
36 RtlZeroMemory(Information, sizeof(*Information));
37 Information->EndingAddress.QuadPart = RamDiskImageLength;
38 Information->CurrentAddress.QuadPart = RamDiskOffset;
39
40 return ESUCCESS;
41 }
42
43 static ARC_STATUS RamDiskOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
44 {
45 /* Always return success, as contents are already in memory */
46 return ESUCCESS;
47 }
48
49 static ARC_STATUS RamDiskRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
50 {
51 PVOID StartAddress;
52
53 /* Don't allow reads past our image */
54 if (RamDiskOffset >= RamDiskImageLength || RamDiskOffset + N > RamDiskImageLength)
55 {
56 *Count = 0;
57 return EIO;
58 }
59 // N = min(N, RamdiskImageLength - RamDiskOffset);
60
61 /* Get actual pointer */
62 StartAddress = (PVOID)((ULONG_PTR)RamDiskBase + RamDiskImageOffset + (ULONG_PTR)RamDiskOffset);
63
64 /* Do the read */
65 RtlCopyMemory(Buffer, StartAddress, N);
66 RamDiskOffset += N;
67 *Count = N;
68
69 return ESUCCESS;
70 }
71
72 static ARC_STATUS RamDiskSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
73 {
74 LARGE_INTEGER NewPosition = *Position;
75
76 switch (SeekMode)
77 {
78 case SeekAbsolute:
79 break;
80 case SeekRelative:
81 NewPosition.QuadPart += RamDiskOffset;
82 break;
83 default:
84 ASSERT(FALSE);
85 return EINVAL;
86 }
87
88 if (NewPosition.QuadPart >= RamDiskImageLength)
89 return EINVAL;
90
91 RamDiskOffset = NewPosition.QuadPart;
92 return ESUCCESS;
93 }
94
95 static const DEVVTBL RamDiskVtbl =
96 {
97 RamDiskClose,
98 RamDiskGetFileInformation,
99 RamDiskOpen,
100 RamDiskRead,
101 RamDiskSeek,
102 };
103
104 static ARC_STATUS
105 RamDiskLoadVirtualFile(
106 IN PCSTR FileName,
107 IN PCSTR DefaultPath OPTIONAL)
108 {
109 ARC_STATUS Status;
110 ULONG RamFileId;
111 ULONG ChunkSize, Count;
112 ULONGLONG TotalRead;
113 PCHAR MsgBuffer = "Loading RamDisk...";
114 ULONG PercentPerChunk, Percent;
115 FILEINFORMATION Information;
116 LARGE_INTEGER Position;
117
118 /* Display progress */
119 UiDrawBackdrop();
120 UiDrawProgressBarCenter(1, 100, MsgBuffer);
121
122 /* Try opening the Ramdisk file */
123 Status = FsOpenFile(FileName, DefaultPath, OpenReadOnly, &RamFileId);
124 if (Status != ESUCCESS)
125 return Status;
126
127 /* Get the file size */
128 Status = ArcGetFileInformation(RamFileId, &Information);
129 if (Status != ESUCCESS)
130 {
131 ArcClose(RamFileId);
132 return Status;
133 }
134
135 /* FIXME: For now, limit RAM disks to 4GB */
136 if (Information.EndingAddress.HighPart != 0)
137 {
138 UiMessageBox("RAM disk too big.");
139 ArcClose(RamFileId);
140 return ENOMEM;
141 }
142 RamDiskFileSize = Information.EndingAddress.QuadPart;
143 ASSERT(RamDiskFileSize < 0x100000000); // See FIXME above.
144
145 /* Allocate memory for it */
146 ChunkSize = 8 * 1024 * 1024;
147 if (RamDiskFileSize < ChunkSize)
148 Percent = PercentPerChunk = 0;
149 else
150 Percent = PercentPerChunk = 100 / (RamDiskFileSize / ChunkSize);
151 RamDiskBase = MmAllocateMemoryWithType(RamDiskFileSize, LoaderXIPRom);
152 if (!RamDiskBase)
153 {
154 UiMessageBox("Failed to allocate memory for RAM disk.");
155 ArcClose(RamFileId);
156 return ENOMEM;
157 }
158
159 /*
160 * Read it in chunks
161 */
162 for (TotalRead = 0; TotalRead < RamDiskFileSize; TotalRead += ChunkSize)
163 {
164 /* Check if we're at the last chunk */
165 if ((RamDiskFileSize - TotalRead) < ChunkSize)
166 {
167 /* Only need the actual data required */
168 ChunkSize = (ULONG)(RamDiskFileSize - TotalRead);
169 }
170
171 /* Draw progress */
172 UiDrawProgressBarCenter(Percent, 100, MsgBuffer);
173 Percent += PercentPerChunk;
174
175 /* Copy the contents */
176 Position.QuadPart = TotalRead;
177 Status = ArcSeek(RamFileId, &Position, SeekAbsolute);
178 if (Status == ESUCCESS)
179 {
180 Status = ArcRead(RamFileId,
181 (PVOID)((ULONG_PTR)RamDiskBase + (ULONG_PTR)TotalRead),
182 ChunkSize,
183 &Count);
184 }
185
186 /* Check for success */
187 if ((Status != ESUCCESS) || (Count != ChunkSize))
188 {
189 MmFreeMemory(RamDiskBase);
190 RamDiskBase = NULL;
191 RamDiskFileSize = 0;
192 ArcClose(RamFileId);
193 UiMessageBox("Failed to read RAM disk.");
194 return ((Status != ESUCCESS) ? Status : EIO);
195 }
196 }
197
198 ArcClose(RamFileId);
199
200 return ESUCCESS;
201 }
202
203 ARC_STATUS
204 RamDiskInitialize(
205 IN BOOLEAN InitRamDisk,
206 IN PCSTR LoadOptions OPTIONAL,
207 IN PCSTR DefaultPath OPTIONAL)
208 {
209 /* Reset the RAMDISK device */
210 if ((RamDiskBase != gInitRamDiskBase) &&
211 (RamDiskFileSize != gInitRamDiskSize) &&
212 (gInitRamDiskSize != 0))
213 {
214 /* This is not the initial Ramdisk, so we can free the allocated memory */
215 MmFreeMemory(RamDiskBase);
216 }
217 RamDiskBase = NULL;
218 RamDiskFileSize = 0;
219 RamDiskImageLength = 0;
220 RamDiskImageOffset = 0;
221 RamDiskOffset = 0;
222
223 if (InitRamDisk)
224 {
225 /* We initialize the initial Ramdisk: it should be present in memory */
226 if (!gInitRamDiskBase || gInitRamDiskSize == 0)
227 return ENODEV;
228
229 // TODO: Handle SDI image.
230
231 RamDiskBase = gInitRamDiskBase;
232 RamDiskFileSize = gInitRamDiskSize;
233 ASSERT(RamDiskFileSize < 0x100000000); // See FIXME about 4GB support in RamDiskLoadVirtualFile().
234 }
235 else
236 {
237 /* We initialize the Ramdisk from the load options */
238 ARC_STATUS Status;
239 CHAR FileName[MAX_PATH] = "";
240
241 /* If we don't have any load options, initialize an empty Ramdisk */
242 if (LoadOptions)
243 {
244 PCHAR Option;
245
246 /* Ramdisk image file name */
247 Option = strstr(LoadOptions, "/RDPATH=");
248 if (Option)
249 {
250 /* Copy the file name - everything until the next separator */
251 Option += 8;
252 RtlStringCbCopyNA(FileName, sizeof(FileName),
253 Option, strcspn(Option, " \t") * sizeof(CHAR));
254 }
255
256 /* Ramdisk image length */
257 Option = strstr(LoadOptions, "/RDIMAGELENGTH=");
258 if (Option)
259 {
260 RamDiskImageLength = _atoi64(Option + 15);
261 }
262
263 /* Ramdisk image offset */
264 Option = strstr(LoadOptions, "/RDIMAGEOFFSET=");
265 if (Option)
266 {
267 RamDiskImageOffset = atol(Option + 15);
268 }
269 }
270
271 if (*FileName)
272 {
273 Status = RamDiskLoadVirtualFile(FileName, DefaultPath);
274 if (Status != ESUCCESS)
275 return Status;
276 }
277 }
278
279 /* Adjust the Ramdisk image length if needed */
280 if (!RamDiskImageLength || (RamDiskImageLength > RamDiskFileSize - RamDiskImageOffset))
281 RamDiskImageLength = RamDiskFileSize - RamDiskImageOffset;
282
283 /* Register the RAMDISK device */
284 if (!RamDiskDeviceRegistered)
285 {
286 FsRegisterDevice("ramdisk(0)", &RamDiskVtbl);
287 RamDiskDeviceRegistered = TRUE;
288 }
289
290 return ESUCCESS;
291 }