[FREELDR] Add some missing UiDrawBackdrop() calls.
[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
168 DiskStopFloppyMotor();
169
170 if (LinuxSetupSector->LoadFlags & LINUX_FLAG_LOAD_HIGH)
171 BootNewLinuxKernel();
172 else
173 BootOldLinuxKernel(LinuxKernelSize);
174
175
176 LinuxBootFailed:
177
178 if (LinuxKernel)
179 FsCloseFile(LinuxKernel);
180
181 if (LinuxInitrdFile)
182 FsCloseFile(LinuxInitrdFile);
183
184 if (LinuxBootSector != NULL)
185 MmFreeMemory(LinuxBootSector);
186
187 if (LinuxSetupSector != NULL)
188 MmFreeMemory(LinuxSetupSector);
189
190 if (LinuxKernelLoadAddress != NULL)
191 MmFreeMemory(LinuxKernelLoadAddress);
192
193 if (LinuxInitrdLoadAddress != NULL)
194 MmFreeMemory(LinuxInitrdLoadAddress);
195
196 LinuxBootSector = NULL;
197 LinuxSetupSector = NULL;
198 LinuxKernelLoadAddress = NULL;
199 LinuxInitrdLoadAddress = NULL;
200 SetupSectorSize = 0;
201 NewStyleLinuxKernel = FALSE;
202 LinuxKernelSize = 0;
203 LinuxHasInitrd = FALSE;
204 strcpy(LinuxCommandLine, "");
205 LinuxCommandLineSize = 0;
206 }
207
208 BOOLEAN LinuxParseIniSection(PCSTR SectionName)
209 {
210 ULONG_PTR SectionId;
211
212 /* Find all the message box settings and run them */
213 UiShowMessageBoxesInSection(SectionName);
214
215 /* Try to open the operating system section in the .ini file */
216 if (!IniOpenSection(SectionName, &SectionId))
217 {
218 UiMessageBox("Section [%s] not found in freeldr.ini.", SectionName);
219 return FALSE;
220 }
221
222 if (!IniReadSettingByName(SectionId, "BootPath", LinuxBootPath, sizeof(LinuxBootPath)))
223 {
224 UiMessageBox("Boot path not specified for selected OS!");
225 return FALSE;
226 }
227
228 /* Get the kernel name */
229 if (!IniReadSettingByName(SectionId, "Kernel", LinuxKernelName, sizeof(LinuxKernelName)))
230 {
231 UiMessageBox("Linux kernel filename not specified for selected OS!");
232 return FALSE;
233 }
234
235 /* Get the initrd name */
236 if (IniReadSettingByName(SectionId, "Initrd", LinuxInitrdName, sizeof(LinuxInitrdName)))
237 {
238 LinuxHasInitrd = TRUE;
239 }
240
241 /* Get the command line */
242 if (IniReadSettingByName(SectionId, "CommandLine", LinuxCommandLine, sizeof(LinuxCommandLine)))
243 {
244 RemoveQuotes(LinuxCommandLine);
245 LinuxCommandLineSize = strlen(LinuxCommandLine) + 1;
246 }
247
248 return TRUE;
249 }
250
251 BOOLEAN LinuxReadBootSector(PFILE LinuxKernelFile)
252 {
253 /* Allocate memory for boot sector */
254 LinuxBootSector = MmAllocateMemoryWithType(512, LoaderSystemCode);
255 if (LinuxBootSector == NULL)
256 return FALSE;
257
258 /* Read linux boot sector */
259 FsSetFilePointer(LinuxKernelFile, 0);
260 if (!FsReadFile(LinuxKernelFile, 512, NULL, LinuxBootSector))
261 return FALSE;
262
263 /* Check for validity */
264 if (LinuxBootSector->BootFlag != LINUX_BOOT_SECTOR_MAGIC)
265 {
266 UiMessageBox("Invalid boot sector magic (0xaa55)");
267 return FALSE;
268 }
269
270 // DbgDumpBuffer(DPRINT_LINUX, LinuxBootSector, 512);
271
272 TRACE("SetupSectors: %d\n", LinuxBootSector->SetupSectors);
273 TRACE("RootFlags: 0x%x\n", LinuxBootSector->RootFlags);
274 TRACE("SystemSize: 0x%x\n", LinuxBootSector->SystemSize);
275 TRACE("SwapDevice: 0x%x\n", LinuxBootSector->SwapDevice);
276 TRACE("RamSize: 0x%x\n", LinuxBootSector->RamSize);
277 TRACE("VideoMode: 0x%x\n", LinuxBootSector->VideoMode);
278 TRACE("RootDevice: 0x%x\n", LinuxBootSector->RootDevice);
279 TRACE("BootFlag: 0x%x\n", LinuxBootSector->BootFlag);
280
281 return TRUE;
282 }
283
284 BOOLEAN LinuxReadSetupSector(PFILE LinuxKernelFile)
285 {
286 UCHAR TempLinuxSetupSector[512];
287
288 LinuxSetupSector = (PLINUX_SETUPSECTOR)TempLinuxSetupSector;
289
290 /* Read first linux setup sector */
291 FsSetFilePointer(LinuxKernelFile, 512);
292 if (!FsReadFile(LinuxKernelFile, 512, NULL, TempLinuxSetupSector))
293 return FALSE;
294
295 /* Check the kernel version */
296 if (!LinuxCheckKernelVersion())
297 return FALSE;
298
299 if (NewStyleLinuxKernel)
300 SetupSectorSize = 512 * LinuxBootSector->SetupSectors;
301 else
302 SetupSectorSize = 512 * 4; // Always 4 setup sectors
303
304 /* Allocate memory for setup sectors */
305 LinuxSetupSector = MmAllocateMemoryWithType(SetupSectorSize, LoaderSystemCode);
306 if (LinuxSetupSector == NULL)
307 return FALSE;
308
309 /* Copy over first setup sector */
310 RtlCopyMemory(LinuxSetupSector, TempLinuxSetupSector, 512);
311
312 /* Read in the rest of the linux setup sectors */
313 FsSetFilePointer(LinuxKernelFile, 1024);
314 if (!FsReadFile(LinuxKernelFile, SetupSectorSize - 512, NULL, (PVOID)((ULONG_PTR)LinuxSetupSector + 512)))
315 return FALSE;
316
317 // DbgDumpBuffer(DPRINT_LINUX, LinuxSetupSector, SetupSectorSize);
318
319 TRACE("SetupHeaderSignature: 0x%x (HdrS)\n", LinuxSetupSector->SetupHeaderSignature);
320 TRACE("Version: 0x%x\n", LinuxSetupSector->Version);
321 TRACE("RealModeSwitch: 0x%x\n", LinuxSetupSector->RealModeSwitch);
322 TRACE("SetupSeg: 0x%x\n", LinuxSetupSector->SetupSeg);
323 TRACE("StartSystemSeg: 0x%x\n", LinuxSetupSector->StartSystemSeg);
324 TRACE("KernelVersion: 0x%x\n", LinuxSetupSector->KernelVersion);
325 TRACE("TypeOfLoader: 0x%x\n", LinuxSetupSector->TypeOfLoader);
326 TRACE("LoadFlags: 0x%x\n", LinuxSetupSector->LoadFlags);
327 TRACE("SetupMoveSize: 0x%x\n", LinuxSetupSector->SetupMoveSize);
328 TRACE("Code32Start: 0x%x\n", LinuxSetupSector->Code32Start);
329 TRACE("RamdiskAddress: 0x%x\n", LinuxSetupSector->RamdiskAddress);
330 TRACE("RamdiskSize: 0x%x\n", LinuxSetupSector->RamdiskSize);
331 TRACE("BootSectKludgeOffset: 0x%x\n", LinuxSetupSector->BootSectKludgeOffset);
332 TRACE("BootSectKludgeSegment: 0x%x\n", LinuxSetupSector->BootSectKludgeSegment);
333 TRACE("HeapEnd: 0x%x\n", LinuxSetupSector->HeapEnd);
334
335 return TRUE;
336 }
337
338 BOOLEAN LinuxReadKernel(PFILE LinuxKernelFile)
339 {
340 ULONG BytesLoaded;
341 CHAR StatusText[260];
342 PVOID LoadAddress;
343
344 sprintf(StatusText, "Loading %s", LinuxKernelName);
345 UiDrawStatusText(StatusText);
346
347 /* Allocate memory for Linux kernel */
348 LinuxKernelLoadAddress = MmAllocateMemoryAtAddress(LinuxKernelSize, (PVOID)LINUX_KERNEL_LOAD_ADDRESS, LoaderSystemCode);
349 if (LinuxKernelLoadAddress != (PVOID)LINUX_KERNEL_LOAD_ADDRESS)
350 {
351 return FALSE;
352 }
353
354 LoadAddress = LinuxKernelLoadAddress;
355
356 /* Read linux kernel to 0x100000 (1mb) */
357 FsSetFilePointer(LinuxKernelFile, 512 + SetupSectorSize);
358 for (BytesLoaded=0; BytesLoaded<LinuxKernelSize; )
359 {
360 if (!FsReadFile(LinuxKernelFile, LINUX_READ_CHUNK_SIZE, NULL, LoadAddress))
361 return FALSE;
362
363 BytesLoaded += LINUX_READ_CHUNK_SIZE;
364 LoadAddress = (PVOID)((ULONG_PTR)LoadAddress + LINUX_READ_CHUNK_SIZE);
365
366 UiDrawProgressBarCenter(BytesLoaded, LinuxKernelSize + LinuxInitrdSize, LinuxBootDescription);
367 }
368
369 return TRUE;
370 }
371
372 BOOLEAN LinuxCheckKernelVersion(VOID)
373 {
374 /* Just assume old kernel until we find otherwise */
375 NewStyleLinuxKernel = FALSE;
376
377 /* Check for new style setup header */
378 if (LinuxSetupSector->SetupHeaderSignature != LINUX_SETUP_HEADER_ID)
379 {
380 NewStyleLinuxKernel = FALSE;
381 }
382 /* Check for version below 2.0 */
383 else if (LinuxSetupSector->Version < 0x0200)
384 {
385 NewStyleLinuxKernel = FALSE;
386 }
387 /* Check for version 2.0 */
388 else if (LinuxSetupSector->Version == 0x0200)
389 {
390 NewStyleLinuxKernel = TRUE;
391 }
392 /* Check for version 2.01+ */
393 else if (LinuxSetupSector->Version >= 0x0201)
394 {
395 NewStyleLinuxKernel = TRUE;
396 LinuxSetupSector->HeapEnd = 0x9000;
397 LinuxSetupSector->LoadFlags |= LINUX_FLAG_CAN_USE_HEAP;
398 }
399
400 if ((NewStyleLinuxKernel == FALSE) && (LinuxHasInitrd))
401 {
402 UiMessageBox("Error: Cannot load a ramdisk (initrd) with an old kernel image.");
403 return FALSE;
404 }
405
406 return TRUE;
407 }
408
409 BOOLEAN LinuxReadInitrd(PFILE LinuxInitrdFile)
410 {
411 ULONG BytesLoaded;
412 CHAR StatusText[260];
413
414 sprintf(StatusText, "Loading %s", LinuxInitrdName);
415 UiDrawStatusText(StatusText);
416
417 // Allocate memory for the ramdisk
418 //LinuxInitrdLoadAddress = MmAllocateMemory(LinuxInitrdSize);
419 // Try to align it at the next MB boundary after the kernel
420 //LinuxInitrdLoadAddress = MmAllocateMemoryAtAddress(LinuxInitrdSize, (PVOID)ROUND_UP((LINUX_KERNEL_LOAD_ADDRESS + LinuxKernelSize), 0x100000));
421 if (LinuxSetupSector->Version <= 0x0202)
422 {
423 LinuxInitrdLoadAddress = MmAllocateHighestMemoryBelowAddress(LinuxInitrdSize, (PVOID)LINUX_MAX_INITRD_ADDRESS, LoaderSystemCode);
424 }
425 else
426 {
427 LinuxInitrdLoadAddress = MmAllocateHighestMemoryBelowAddress(LinuxInitrdSize, (PVOID)LinuxSetupSector->InitrdAddressMax, LoaderSystemCode);
428 }
429 if (LinuxInitrdLoadAddress == NULL)
430 {
431 return FALSE;
432 }
433
434 /* Set the information in the setup struct */
435 LinuxSetupSector->RamdiskAddress = (ULONG)LinuxInitrdLoadAddress;
436 LinuxSetupSector->RamdiskSize = LinuxInitrdSize;
437
438 TRACE("RamdiskAddress: 0x%x\n", LinuxSetupSector->RamdiskAddress);
439 TRACE("RamdiskSize: 0x%x\n", LinuxSetupSector->RamdiskSize);
440
441 if (LinuxSetupSector->Version >= 0x0203)
442 {
443 TRACE("InitrdAddressMax: 0x%x\n", LinuxSetupSector->InitrdAddressMax);
444 }
445
446 /* Read in the ramdisk */
447 for (BytesLoaded=0; BytesLoaded<LinuxInitrdSize; )
448 {
449 if (!FsReadFile(LinuxInitrdFile, LINUX_READ_CHUNK_SIZE, NULL, (PVOID)LinuxInitrdLoadAddress))
450 return FALSE;
451
452 BytesLoaded += LINUX_READ_CHUNK_SIZE;
453 LinuxInitrdLoadAddress = (PVOID)((ULONG_PTR)LinuxInitrdLoadAddress + LINUX_READ_CHUNK_SIZE);
454
455 UiDrawProgressBarCenter(BytesLoaded + LinuxKernelSize, LinuxInitrdSize + LinuxKernelSize, LinuxBootDescription);
456 }
457
458 return TRUE;
459 }
460
461 #endif // _M_IX86
462
463 #endif