[FREELDR] Unload freeldr.ini file before booting.
[reactos.git] / boot / freeldr / freeldr / linuxboot.c
1 /*
2 * FreeLoader
3 * Copyright (C) 1998-2003 Brian Palmer <brianp@sginet.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #ifndef _M_ARM
21
22 #ifdef _M_IX86
23
24 /* INCLUDES *******************************************************************/
25
26 #include <freeldr.h>
27 #include <debug.h>
28
29 DBG_DEFAULT_CHANNEL(LINUX);
30
31 /* GLOBALS ********************************************************************/
32
33 #define LINUX_READ_CHUNK_SIZE 0x20000 // Read 128k at a time
34
35 PLINUX_BOOTSECTOR LinuxBootSector = NULL;
36 PLINUX_SETUPSECTOR LinuxSetupSector = NULL;
37 ULONG SetupSectorSize = 0;
38 BOOLEAN NewStyleLinuxKernel = FALSE;
39 ULONG LinuxKernelSize = 0;
40 ULONG LinuxInitrdSize = 0;
41 CHAR LinuxKernelName[260];
42 CHAR LinuxInitrdName[260];
43 BOOLEAN LinuxHasInitrd = FALSE;
44 CHAR LinuxCommandLine[260] = "";
45 ULONG LinuxCommandLineSize = 0;
46 PVOID LinuxKernelLoadAddress = NULL;
47 PVOID LinuxInitrdLoadAddress = NULL;
48 CHAR LinuxBootDescription[80];
49 CHAR LinuxBootPath[260] = "";
50
51 /* FUNCTIONS ******************************************************************/
52
53 BOOLEAN RemoveQuotes(PCHAR QuotedString)
54 {
55 CHAR TempString[200];
56 PCHAR p;
57 PSTR Start;
58
59 /* Skip spaces up to " */
60 p = QuotedString;
61 while (*p == ' ' || *p == '"')
62 p++;
63 Start = p;
64
65 /* Go up to next " */
66 while (*p != '"' && *p != ANSI_NULL)
67 p++;
68 *p = ANSI_NULL;
69
70 /* Copy result */
71 strcpy(TempString, Start);
72 strcpy(QuotedString, TempString);
73
74 return TRUE;
75 }
76
77 VOID
78 LoadAndBootLinux(IN OperatingSystemItem* OperatingSystem,
79 IN USHORT OperatingSystemVersion)
80 {
81 PCSTR SectionName = OperatingSystem->SystemPartition;
82 PCSTR Description = OperatingSystem->LoadIdentifier;
83 PFILE LinuxKernel = 0;
84 PFILE LinuxInitrdFile = 0;
85
86 if (Description)
87 sprintf(LinuxBootDescription, "Loading %s...", Description);
88 else
89 strcpy(LinuxBootDescription, "Loading Linux...");
90
91 UiDrawBackdrop();
92 UiDrawStatusText(LinuxBootDescription);
93 UiDrawProgressBarCenter(0, 100, LinuxBootDescription);
94
95 /* Parse the .ini file section */
96 if (!LinuxParseIniSection(SectionName))
97 goto LinuxBootFailed;
98
99 /* Open the kernel */
100 LinuxKernel = FsOpenFile(LinuxKernelName);
101 if (!LinuxKernel)
102 {
103 UiMessageBox("Linux kernel \'%s\' not found.", LinuxKernelName);
104 goto LinuxBootFailed;
105 }
106
107 /* Open the initrd file image (if necessary) */
108 if (LinuxHasInitrd)
109 {
110 LinuxInitrdFile = FsOpenFile(LinuxInitrdName);
111 if (!LinuxInitrdFile)
112 {
113 UiMessageBox("Linux initrd image \'%s\' not found.", LinuxInitrdName);
114 goto LinuxBootFailed;
115 }
116 }
117
118 /* Read the boot sector */
119 if (!LinuxReadBootSector(LinuxKernel))
120 goto LinuxBootFailed;
121
122 /* Read the setup sector */
123 if (!LinuxReadSetupSector(LinuxKernel))
124 goto LinuxBootFailed;
125
126 /* Calc kernel size */
127 LinuxKernelSize = FsGetFileSize(LinuxKernel) - (512 + SetupSectorSize);
128
129 /* Get the file size */
130 LinuxInitrdSize = FsGetFileSize(LinuxInitrdFile);
131
132 /* Read the kernel */
133 if (!LinuxReadKernel(LinuxKernel))
134 goto LinuxBootFailed;
135
136 /* Read the initrd (if necessary) */
137 if (LinuxHasInitrd)
138 {
139 if (!LinuxReadInitrd(LinuxInitrdFile))
140 goto LinuxBootFailed;
141 }
142
143 // If the default root device is set to FLOPPY (0000h), change to /dev/fd0 (0200h)
144 if (LinuxBootSector->RootDevice == 0x0000)
145 LinuxBootSector->RootDevice = 0x0200;
146
147 if (LinuxSetupSector->Version >= 0x0202)
148 {
149 LinuxSetupSector->CommandLinePointer = 0x99000;
150 }
151 else
152 {
153 LinuxBootSector->CommandLineMagic = LINUX_COMMAND_LINE_MAGIC;
154 LinuxBootSector->CommandLineOffset = 0x9000;
155 }
156
157 if (NewStyleLinuxKernel)
158 LinuxSetupSector->TypeOfLoader = LINUX_LOADER_TYPE_FREELOADER;
159 else
160 LinuxSetupSector->LoadFlags = 0;
161
162 RtlCopyMemory((PVOID)0x90000, LinuxBootSector, 512);
163 RtlCopyMemory((PVOID)0x90200, LinuxSetupSector, SetupSectorSize);
164 RtlCopyMemory((PVOID)0x99000, LinuxCommandLine, LinuxCommandLineSize);
165
166 UiUnInitialize("Booting Linux...");
167 IniCleanup();
168
169 DiskStopFloppyMotor();
170
171 if (LinuxSetupSector->LoadFlags & LINUX_FLAG_LOAD_HIGH)
172 BootNewLinuxKernel();
173 else
174 BootOldLinuxKernel(LinuxKernelSize);
175
176
177 LinuxBootFailed:
178
179 if (LinuxKernel)
180 FsCloseFile(LinuxKernel);
181
182 if (LinuxInitrdFile)
183 FsCloseFile(LinuxInitrdFile);
184
185 if (LinuxBootSector != NULL)
186 MmFreeMemory(LinuxBootSector);
187
188 if (LinuxSetupSector != NULL)
189 MmFreeMemory(LinuxSetupSector);
190
191 if (LinuxKernelLoadAddress != NULL)
192 MmFreeMemory(LinuxKernelLoadAddress);
193
194 if (LinuxInitrdLoadAddress != NULL)
195 MmFreeMemory(LinuxInitrdLoadAddress);
196
197 LinuxBootSector = NULL;
198 LinuxSetupSector = NULL;
199 LinuxKernelLoadAddress = NULL;
200 LinuxInitrdLoadAddress = NULL;
201 SetupSectorSize = 0;
202 NewStyleLinuxKernel = FALSE;
203 LinuxKernelSize = 0;
204 LinuxHasInitrd = FALSE;
205 strcpy(LinuxCommandLine, "");
206 LinuxCommandLineSize = 0;
207 }
208
209 BOOLEAN LinuxParseIniSection(PCSTR SectionName)
210 {
211 ULONG_PTR SectionId;
212
213 /* Find all the message box settings and run them */
214 UiShowMessageBoxesInSection(SectionName);
215
216 /* Try to open the operating system section in the .ini file */
217 if (!IniOpenSection(SectionName, &SectionId))
218 {
219 UiMessageBox("Section [%s] not found in freeldr.ini.", SectionName);
220 return FALSE;
221 }
222
223 if (!IniReadSettingByName(SectionId, "BootPath", LinuxBootPath, sizeof(LinuxBootPath)))
224 {
225 UiMessageBox("Boot path not specified for selected OS!");
226 return FALSE;
227 }
228
229 /* Get the kernel name */
230 if (!IniReadSettingByName(SectionId, "Kernel", LinuxKernelName, sizeof(LinuxKernelName)))
231 {
232 UiMessageBox("Linux kernel filename not specified for selected OS!");
233 return FALSE;
234 }
235
236 /* Get the initrd name */
237 if (IniReadSettingByName(SectionId, "Initrd", LinuxInitrdName, sizeof(LinuxInitrdName)))
238 {
239 LinuxHasInitrd = TRUE;
240 }
241
242 /* Get the command line */
243 if (IniReadSettingByName(SectionId, "CommandLine", LinuxCommandLine, sizeof(LinuxCommandLine)))
244 {
245 RemoveQuotes(LinuxCommandLine);
246 LinuxCommandLineSize = strlen(LinuxCommandLine) + 1;
247 }
248
249 return TRUE;
250 }
251
252 BOOLEAN LinuxReadBootSector(PFILE LinuxKernelFile)
253 {
254 /* Allocate memory for boot sector */
255 LinuxBootSector = MmAllocateMemoryWithType(512, LoaderSystemCode);
256 if (LinuxBootSector == NULL)
257 return FALSE;
258
259 /* Read linux boot sector */
260 FsSetFilePointer(LinuxKernelFile, 0);
261 if (!FsReadFile(LinuxKernelFile, 512, NULL, LinuxBootSector))
262 return FALSE;
263
264 /* Check for validity */
265 if (LinuxBootSector->BootFlag != LINUX_BOOT_SECTOR_MAGIC)
266 {
267 UiMessageBox("Invalid boot sector magic (0xaa55)");
268 return FALSE;
269 }
270
271 // DbgDumpBuffer(DPRINT_LINUX, LinuxBootSector, 512);
272
273 TRACE("SetupSectors: %d\n", LinuxBootSector->SetupSectors);
274 TRACE("RootFlags: 0x%x\n", LinuxBootSector->RootFlags);
275 TRACE("SystemSize: 0x%x\n", LinuxBootSector->SystemSize);
276 TRACE("SwapDevice: 0x%x\n", LinuxBootSector->SwapDevice);
277 TRACE("RamSize: 0x%x\n", LinuxBootSector->RamSize);
278 TRACE("VideoMode: 0x%x\n", LinuxBootSector->VideoMode);
279 TRACE("RootDevice: 0x%x\n", LinuxBootSector->RootDevice);
280 TRACE("BootFlag: 0x%x\n", LinuxBootSector->BootFlag);
281
282 return TRUE;
283 }
284
285 BOOLEAN LinuxReadSetupSector(PFILE LinuxKernelFile)
286 {
287 UCHAR TempLinuxSetupSector[512];
288
289 LinuxSetupSector = (PLINUX_SETUPSECTOR)TempLinuxSetupSector;
290
291 /* Read first linux setup sector */
292 FsSetFilePointer(LinuxKernelFile, 512);
293 if (!FsReadFile(LinuxKernelFile, 512, NULL, TempLinuxSetupSector))
294 return FALSE;
295
296 /* Check the kernel version */
297 if (!LinuxCheckKernelVersion())
298 return FALSE;
299
300 if (NewStyleLinuxKernel)
301 SetupSectorSize = 512 * LinuxBootSector->SetupSectors;
302 else
303 SetupSectorSize = 512 * 4; // Always 4 setup sectors
304
305 /* Allocate memory for setup sectors */
306 LinuxSetupSector = MmAllocateMemoryWithType(SetupSectorSize, LoaderSystemCode);
307 if (LinuxSetupSector == NULL)
308 return FALSE;
309
310 /* Copy over first setup sector */
311 RtlCopyMemory(LinuxSetupSector, TempLinuxSetupSector, 512);
312
313 /* Read in the rest of the linux setup sectors */
314 FsSetFilePointer(LinuxKernelFile, 1024);
315 if (!FsReadFile(LinuxKernelFile, SetupSectorSize - 512, NULL, (PVOID)((ULONG_PTR)LinuxSetupSector + 512)))
316 return FALSE;
317
318 // DbgDumpBuffer(DPRINT_LINUX, LinuxSetupSector, SetupSectorSize);
319
320 TRACE("SetupHeaderSignature: 0x%x (HdrS)\n", LinuxSetupSector->SetupHeaderSignature);
321 TRACE("Version: 0x%x\n", LinuxSetupSector->Version);
322 TRACE("RealModeSwitch: 0x%x\n", LinuxSetupSector->RealModeSwitch);
323 TRACE("SetupSeg: 0x%x\n", LinuxSetupSector->SetupSeg);
324 TRACE("StartSystemSeg: 0x%x\n", LinuxSetupSector->StartSystemSeg);
325 TRACE("KernelVersion: 0x%x\n", LinuxSetupSector->KernelVersion);
326 TRACE("TypeOfLoader: 0x%x\n", LinuxSetupSector->TypeOfLoader);
327 TRACE("LoadFlags: 0x%x\n", LinuxSetupSector->LoadFlags);
328 TRACE("SetupMoveSize: 0x%x\n", LinuxSetupSector->SetupMoveSize);
329 TRACE("Code32Start: 0x%x\n", LinuxSetupSector->Code32Start);
330 TRACE("RamdiskAddress: 0x%x\n", LinuxSetupSector->RamdiskAddress);
331 TRACE("RamdiskSize: 0x%x\n", LinuxSetupSector->RamdiskSize);
332 TRACE("BootSectKludgeOffset: 0x%x\n", LinuxSetupSector->BootSectKludgeOffset);
333 TRACE("BootSectKludgeSegment: 0x%x\n", LinuxSetupSector->BootSectKludgeSegment);
334 TRACE("HeapEnd: 0x%x\n", LinuxSetupSector->HeapEnd);
335
336 return TRUE;
337 }
338
339 BOOLEAN LinuxReadKernel(PFILE LinuxKernelFile)
340 {
341 ULONG BytesLoaded;
342 CHAR StatusText[260];
343 PVOID LoadAddress;
344
345 RtlStringCbPrintfA(StatusText, sizeof(StatusText), "Loading %s", LinuxKernelName);
346 UiDrawStatusText(StatusText);
347
348 /* Allocate memory for Linux kernel */
349 LinuxKernelLoadAddress = MmAllocateMemoryAtAddress(LinuxKernelSize, (PVOID)LINUX_KERNEL_LOAD_ADDRESS, LoaderSystemCode);
350 if (LinuxKernelLoadAddress != (PVOID)LINUX_KERNEL_LOAD_ADDRESS)
351 {
352 return FALSE;
353 }
354
355 LoadAddress = LinuxKernelLoadAddress;
356
357 /* Read linux kernel to 0x100000 (1mb) */
358 FsSetFilePointer(LinuxKernelFile, 512 + SetupSectorSize);
359 for (BytesLoaded=0; BytesLoaded<LinuxKernelSize; )
360 {
361 if (!FsReadFile(LinuxKernelFile, LINUX_READ_CHUNK_SIZE, NULL, LoadAddress))
362 return FALSE;
363
364 BytesLoaded += LINUX_READ_CHUNK_SIZE;
365 LoadAddress = (PVOID)((ULONG_PTR)LoadAddress + LINUX_READ_CHUNK_SIZE);
366
367 UiDrawProgressBarCenter(BytesLoaded, LinuxKernelSize + LinuxInitrdSize, LinuxBootDescription);
368 }
369
370 return TRUE;
371 }
372
373 BOOLEAN LinuxCheckKernelVersion(VOID)
374 {
375 /* Just assume old kernel until we find otherwise */
376 NewStyleLinuxKernel = FALSE;
377
378 /* Check for new style setup header */
379 if (LinuxSetupSector->SetupHeaderSignature != LINUX_SETUP_HEADER_ID)
380 {
381 NewStyleLinuxKernel = FALSE;
382 }
383 /* Check for version below 2.0 */
384 else if (LinuxSetupSector->Version < 0x0200)
385 {
386 NewStyleLinuxKernel = FALSE;
387 }
388 /* Check for version 2.0 */
389 else if (LinuxSetupSector->Version == 0x0200)
390 {
391 NewStyleLinuxKernel = TRUE;
392 }
393 /* Check for version 2.01+ */
394 else if (LinuxSetupSector->Version >= 0x0201)
395 {
396 NewStyleLinuxKernel = TRUE;
397 LinuxSetupSector->HeapEnd = 0x9000;
398 LinuxSetupSector->LoadFlags |= LINUX_FLAG_CAN_USE_HEAP;
399 }
400
401 if ((NewStyleLinuxKernel == FALSE) && (LinuxHasInitrd))
402 {
403 UiMessageBox("Error: Cannot load a ramdisk (initrd) with an old kernel image.");
404 return FALSE;
405 }
406
407 return TRUE;
408 }
409
410 BOOLEAN LinuxReadInitrd(PFILE LinuxInitrdFile)
411 {
412 ULONG BytesLoaded;
413 CHAR StatusText[260];
414
415 RtlStringCbPrintfA(StatusText, sizeof(StatusText), "Loading %s", LinuxInitrdName);
416 UiDrawStatusText(StatusText);
417
418 // Allocate memory for the ramdisk
419 //LinuxInitrdLoadAddress = MmAllocateMemory(LinuxInitrdSize);
420 // Try to align it at the next MB boundary after the kernel
421 //LinuxInitrdLoadAddress = MmAllocateMemoryAtAddress(LinuxInitrdSize, (PVOID)ROUND_UP((LINUX_KERNEL_LOAD_ADDRESS + LinuxKernelSize), 0x100000));
422 if (LinuxSetupSector->Version <= 0x0202)
423 {
424 LinuxInitrdLoadAddress = MmAllocateHighestMemoryBelowAddress(LinuxInitrdSize, (PVOID)LINUX_MAX_INITRD_ADDRESS, LoaderSystemCode);
425 }
426 else
427 {
428 LinuxInitrdLoadAddress = MmAllocateHighestMemoryBelowAddress(LinuxInitrdSize, (PVOID)LinuxSetupSector->InitrdAddressMax, LoaderSystemCode);
429 }
430 if (LinuxInitrdLoadAddress == NULL)
431 {
432 return FALSE;
433 }
434
435 /* Set the information in the setup struct */
436 LinuxSetupSector->RamdiskAddress = (ULONG)LinuxInitrdLoadAddress;
437 LinuxSetupSector->RamdiskSize = LinuxInitrdSize;
438
439 TRACE("RamdiskAddress: 0x%x\n", LinuxSetupSector->RamdiskAddress);
440 TRACE("RamdiskSize: 0x%x\n", LinuxSetupSector->RamdiskSize);
441
442 if (LinuxSetupSector->Version >= 0x0203)
443 {
444 TRACE("InitrdAddressMax: 0x%x\n", LinuxSetupSector->InitrdAddressMax);
445 }
446
447 /* Read in the ramdisk */
448 for (BytesLoaded=0; BytesLoaded<LinuxInitrdSize; )
449 {
450 if (!FsReadFile(LinuxInitrdFile, LINUX_READ_CHUNK_SIZE, NULL, (PVOID)LinuxInitrdLoadAddress))
451 return FALSE;
452
453 BytesLoaded += LINUX_READ_CHUNK_SIZE;
454 LinuxInitrdLoadAddress = (PVOID)((ULONG_PTR)LinuxInitrdLoadAddress + LINUX_READ_CHUNK_SIZE);
455
456 UiDrawProgressBarCenter(BytesLoaded + LinuxKernelSize, LinuxInitrdSize + LinuxKernelSize, LinuxBootDescription);
457 }
458
459 return TRUE;
460 }
461
462 #endif // _M_IX86
463
464 #endif