Created new subtree for groups of related test programs.
[reactos.git] / freeldr / freeldr / linuxboot.c
1 /*
2 * FreeLoader
3 * Copyright (C) 1998-2002 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
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20
21 #include <freeldr.h>
22 #include <arch.h>
23 #include <miscboot.h>
24 #include <rtl.h>
25 #include <fs.h>
26 #include <ui.h>
27 #include <linux.h>
28 #include <debug.h>
29 #include <mm.h>
30 #include <inifile.h>
31 #include <oslist.h> // For RemoveQuotes()
32 #include <video.h>
33
34 PLINUX_BOOTSECTOR LinuxBootSector = NULL;
35 PLINUX_SETUPSECTOR LinuxSetupSector = NULL;
36 U32 SetupSectorSize = 0;
37 BOOL NewStyleLinuxKernel = FALSE;
38 U32 LinuxKernelSize = 0;
39 UCHAR LinuxKernelName[260];
40 UCHAR LinuxInitrdName[260];
41 BOOL LinuxHasInitrd = FALSE;
42 UCHAR LinuxCommandLine[260] = "";
43 U32 LinuxCommandLineSize = 0;
44 PVOID LinuxKernelLoadAddress = NULL;
45 PVOID LinuxInitrdLoadAddress = NULL;
46
47 VOID LoadAndBootLinux(PUCHAR OperatingSystemName)
48 {
49 PFILE LinuxKernel = NULL;
50 UCHAR TempString[260];
51
52 UiDrawBackdrop();
53
54 // Parse the .ini file section
55 if (!LinuxParseIniSection(OperatingSystemName))
56 {
57 goto LinuxBootFailed;
58 }
59
60 // Open the boot volume
61 if (!OpenDiskDrive(BootDrive, BootPartition))
62 {
63 UiMessageBox("Failed to open boot drive.");
64 goto LinuxBootFailed;
65 }
66
67 // Open the kernel
68 LinuxKernel = OpenFile(LinuxKernelName);
69 if (LinuxKernel == NULL)
70 {
71 sprintf(TempString, "Linux kernel \'%s\' not found.", LinuxKernelName);
72 UiMessageBox(TempString);
73 goto LinuxBootFailed;
74 }
75
76 // Read the boot sector
77 if (!LinuxReadBootSector(LinuxKernel))
78 {
79 goto LinuxBootFailed;
80 }
81
82 // Read the setup sector
83 if (!LinuxReadSetupSector(LinuxKernel))
84 {
85 goto LinuxBootFailed;
86 }
87
88 // Read the kernel
89 if (!LinuxReadKernel(LinuxKernel))
90 {
91 goto LinuxBootFailed;
92 }
93
94 // Read the initrd (if necessary)
95 if (LinuxHasInitrd)
96 {
97 if (!LinuxReadInitrd())
98 {
99 goto LinuxBootFailed;
100 }
101 }
102
103 // If the default root device is set to FLOPPY (0000h), change to /dev/fd0 (0200h)
104 if (LinuxBootSector->RootDevice == 0x0000)
105 {
106 LinuxBootSector->RootDevice = 0x0200;
107 }
108
109 LinuxBootSector->CommandLineMagic = LINUX_COMMAND_LINE_MAGIC;
110 LinuxBootSector->CommandLineOffset = 0x9000;
111
112 if (NewStyleLinuxKernel)
113 {
114 LinuxSetupSector->TypeOfLoader = LINUX_LOADER_TYPE_FREELOADER;
115 }
116 else
117 {
118 LinuxSetupSector->LoadFlags = 0;
119 }
120
121 RtlCopyMemory((PVOID)0x90000, LinuxBootSector, 512);
122 RtlCopyMemory((PVOID)0x90200, LinuxSetupSector, SetupSectorSize);
123 RtlCopyMemory((PVOID)0x99000, LinuxCommandLine, LinuxCommandLineSize);
124
125 VideoShowTextCursor();
126 VideoClearScreen();
127
128 StopFloppyMotor();
129
130 if (LinuxSetupSector->LoadFlags & LINUX_FLAG_LOAD_HIGH)
131 {
132 BootNewLinuxKernel();
133 }
134 else
135 {
136 BootOldLinuxKernel(LinuxKernelSize);
137 }
138
139
140 LinuxBootFailed:
141
142 if (LinuxKernel != NULL)
143 {
144 CloseFile(LinuxKernel);
145 }
146
147 if (LinuxBootSector != NULL)
148 {
149 MmFreeMemory(LinuxBootSector);
150 }
151 if (LinuxSetupSector != NULL)
152 {
153 MmFreeMemory(LinuxSetupSector);
154 }
155 if (LinuxKernelLoadAddress != NULL)
156 {
157 MmFreeMemory(LinuxKernelLoadAddress);
158 }
159 if (LinuxInitrdLoadAddress != NULL)
160 {
161 MmFreeMemory(LinuxInitrdLoadAddress);
162 }
163
164 LinuxBootSector = NULL;
165 LinuxSetupSector = NULL;
166 LinuxKernelLoadAddress = NULL;
167 LinuxInitrdLoadAddress = NULL;
168 SetupSectorSize = 0;
169 NewStyleLinuxKernel = FALSE;
170 LinuxKernelSize = 0;
171 LinuxHasInitrd = FALSE;
172 strcpy(LinuxCommandLine, "");
173 LinuxCommandLineSize = 0;
174 }
175
176 BOOL LinuxParseIniSection(PUCHAR OperatingSystemName)
177 {
178 UCHAR SettingName[260];
179 UCHAR SettingValue[260];
180 U32 SectionId;
181
182 // Find all the message box settings and run them
183 UiShowMessageBoxesInSection(OperatingSystemName);
184
185 // Try to open the operating system section in the .ini file
186 if (!IniOpenSection(OperatingSystemName, &SectionId))
187 {
188 sprintf(SettingName, "Section [%s] not found in freeldr.ini.\n", OperatingSystemName);
189 UiMessageBox(SettingName);
190 return FALSE;
191 }
192
193 if (!IniReadSettingByName(SectionId, "BootDrive", SettingValue, 260))
194 {
195 UiMessageBox("Boot drive not specified for selected OS!");
196 return FALSE;
197 }
198
199 BootDrive = atoi(SettingValue);
200
201 BootPartition = 0;
202 if (IniReadSettingByName(SectionId, "BootPartition", SettingValue, 260))
203 {
204 BootPartition = atoi(SettingValue);
205 }
206
207 // Get the kernel name
208 if (!IniReadSettingByName(SectionId, "Kernel", LinuxKernelName, 260))
209 {
210 UiMessageBox("Linux kernel filename not specified for selected OS!");
211 return FALSE;
212 }
213
214 // Get the initrd name
215 if (IniReadSettingByName(SectionId, "Initrd", LinuxInitrdName, 260))
216 {
217 LinuxHasInitrd = TRUE;
218 }
219
220 // Get the command line
221 if (IniReadSettingByName(SectionId, "CommandLine", LinuxCommandLine, 260))
222 {
223 RemoveQuotes(LinuxCommandLine);
224 LinuxCommandLineSize = strlen(LinuxCommandLine) + 1;
225 }
226
227 return TRUE;
228 }
229
230 BOOL LinuxReadBootSector(PFILE LinuxKernelFile)
231 {
232 // Allocate memory for boot sector
233 LinuxBootSector = (PLINUX_BOOTSECTOR)MmAllocateMemory(512);
234 if (LinuxBootSector == NULL)
235 {
236 return FALSE;
237 }
238
239 // Read linux boot sector
240 SetFilePointer(LinuxKernelFile, 0);
241 if (!ReadFile(LinuxKernelFile, 512, NULL, LinuxBootSector))
242 {
243 return FALSE;
244 }
245
246 // Check for validity
247 if (LinuxBootSector->BootFlag != LINUX_BOOT_SECTOR_MAGIC)
248 {
249 UiMessageBox("Invalid boot sector magic (0xaa55)");
250 return FALSE;
251 }
252
253 DbgDumpBuffer(DPRINT_LINUX, LinuxBootSector, 512);
254
255 DbgPrint((DPRINT_LINUX, "SetupSectors: %d\n", LinuxBootSector->SetupSectors));
256 DbgPrint((DPRINT_LINUX, "RootFlags: 0x%x\n", LinuxBootSector->RootFlags));
257 DbgPrint((DPRINT_LINUX, "SystemSize: 0x%x\n", LinuxBootSector->SystemSize));
258 DbgPrint((DPRINT_LINUX, "SwapDevice: 0x%x\n", LinuxBootSector->SwapDevice));
259 DbgPrint((DPRINT_LINUX, "RamSize: 0x%x\n", LinuxBootSector->RamSize));
260 DbgPrint((DPRINT_LINUX, "VideoMode: 0x%x\n", LinuxBootSector->VideoMode));
261 DbgPrint((DPRINT_LINUX, "RootDevice: 0x%x\n", LinuxBootSector->RootDevice));
262 DbgPrint((DPRINT_LINUX, "BootFlag: 0x%x\n", LinuxBootSector->BootFlag));
263
264 return TRUE;
265 }
266
267 BOOL LinuxReadSetupSector(PFILE LinuxKernelFile)
268 {
269 U8 TempLinuxSetupSector[512];
270
271 LinuxSetupSector = (PLINUX_SETUPSECTOR)TempLinuxSetupSector;
272
273 // Read first linux setup sector
274 SetFilePointer(LinuxKernelFile, 512);
275 if (!ReadFile(LinuxKernelFile, 512, NULL, TempLinuxSetupSector))
276 {
277 return FALSE;
278 }
279
280 // Check the kernel version
281 if (!LinuxCheckKernelVersion())
282 {
283 return FALSE;
284 }
285
286 if (NewStyleLinuxKernel)
287 {
288 SetupSectorSize = 512 * LinuxBootSector->SetupSectors;
289 }
290 else
291 {
292 SetupSectorSize = 4 * 512; // Always 4 setup sectors
293 }
294
295 // Allocate memory for setup sectors
296 LinuxSetupSector = (PLINUX_SETUPSECTOR)MmAllocateMemory(SetupSectorSize);
297 if (LinuxSetupSector == NULL)
298 {
299 return FALSE;
300 }
301
302 // Copy over first setup sector
303 RtlCopyMemory(LinuxSetupSector, TempLinuxSetupSector, 512);
304
305 // Read in the rest of the linux setup sectors
306 SetFilePointer(LinuxKernelFile, 1024);
307 if (!ReadFile(LinuxKernelFile, SetupSectorSize - 512, NULL, ((PVOID)LinuxSetupSector) + 512))
308 {
309 return FALSE;
310 }
311
312 DbgDumpBuffer(DPRINT_LINUX, LinuxSetupSector, SetupSectorSize);
313
314 DbgPrint((DPRINT_LINUX, "SetupHeaderSignature: 0x%x (HdrS)\n", LinuxSetupSector->SetupHeaderSignature));
315 DbgPrint((DPRINT_LINUX, "Version: 0x%x\n", LinuxSetupSector->Version));
316 DbgPrint((DPRINT_LINUX, "RealModeSwitch: 0x%x\n", LinuxSetupSector->RealModeSwitch));
317 DbgPrint((DPRINT_LINUX, "SetupSeg: 0x%x\n", LinuxSetupSector->SetupSeg));
318 DbgPrint((DPRINT_LINUX, "StartSystemSeg: 0x%x\n", LinuxSetupSector->StartSystemSeg));
319 DbgPrint((DPRINT_LINUX, "KernelVersion: 0x%x\n", LinuxSetupSector->KernelVersion));
320 DbgPrint((DPRINT_LINUX, "TypeOfLoader: 0x%x\n", LinuxSetupSector->TypeOfLoader));
321 DbgPrint((DPRINT_LINUX, "LoadFlags: 0x%x\n", LinuxSetupSector->LoadFlags));
322 DbgPrint((DPRINT_LINUX, "SetupMoveSize: 0x%x\n", LinuxSetupSector->SetupMoveSize));
323 DbgPrint((DPRINT_LINUX, "Code32Start: 0x%x\n", LinuxSetupSector->Code32Start));
324 DbgPrint((DPRINT_LINUX, "RamdiskAddress: 0x%x\n", LinuxSetupSector->RamdiskAddress));
325 DbgPrint((DPRINT_LINUX, "RamdiskSize: 0x%x\n", LinuxSetupSector->RamdiskSize));
326 DbgPrint((DPRINT_LINUX, "BootSectKludgeOffset: 0x%x\n", LinuxSetupSector->BootSectKludgeOffset));
327 DbgPrint((DPRINT_LINUX, "BootSectKludgeSegment: 0x%x\n", LinuxSetupSector->BootSectKludgeSegment));
328 DbgPrint((DPRINT_LINUX, "HeapEnd: 0x%x\n", LinuxSetupSector->HeapEnd));
329
330 return TRUE;
331 }
332
333 BOOL LinuxReadKernel(PFILE LinuxKernelFile)
334 {
335 U32 BytesLoaded;
336 UCHAR StatusText[260];
337 PVOID LoadAddress;
338
339 sprintf(StatusText, "Loading %s", LinuxKernelName);
340 UiDrawStatusText(StatusText);
341 UiDrawProgressBarCenter(0, 100);
342
343 // Calc kernel size
344 LinuxKernelSize = GetFileSize(LinuxKernelFile) - (512 + SetupSectorSize);
345
346 // Allocate memory for Linux kernel
347 LinuxKernelLoadAddress = MmAllocateMemoryAtAddress(LinuxKernelSize, (PVOID)LINUX_KERNEL_LOAD_ADDRESS);
348 if (LinuxKernelLoadAddress != (PVOID)LINUX_KERNEL_LOAD_ADDRESS)
349 {
350 return FALSE;
351 }
352
353 LoadAddress = LinuxKernelLoadAddress;
354
355 // Read linux kernel to 0x100000 (1mb)
356 SetFilePointer(LinuxKernelFile, 512 + SetupSectorSize);
357 for (BytesLoaded=0; BytesLoaded<LinuxKernelSize; )
358 {
359 if (!ReadFile(LinuxKernelFile, 0x4000, NULL, LoadAddress))
360 {
361 return FALSE;
362 }
363
364 BytesLoaded += 0x4000;
365 LoadAddress += 0x4000;
366
367 UiDrawProgressBarCenter(BytesLoaded, LinuxKernelSize);
368 }
369
370 return TRUE;
371 }
372
373 BOOL 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 == TRUE))
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 BOOL LinuxReadInitrd(VOID)
411 {
412 PFILE LinuxInitrdFile;
413 UCHAR TempString[260];
414 U32 LinuxInitrdSize;
415 U32 BytesLoaded;
416 UCHAR StatusText[260];
417
418 sprintf(StatusText, "Loading %s", LinuxInitrdName);
419 UiDrawStatusText(StatusText);
420 UiDrawProgressBarCenter(0, 100);
421
422 // Open the initrd file image
423 LinuxInitrdFile = OpenFile(LinuxInitrdName);
424 if (LinuxInitrdFile == NULL)
425 {
426 sprintf(TempString, "Linux initrd image \'%s\' not found.", LinuxInitrdName);
427 UiMessageBox(TempString);
428 return FALSE;
429 }
430
431 // Get the file size
432 LinuxInitrdSize = GetFileSize(LinuxInitrdFile);
433
434 // Allocate memory for the ramdisk
435 LinuxInitrdLoadAddress = MmAllocateMemory(LinuxInitrdSize);
436 if (LinuxInitrdLoadAddress == NULL)
437 {
438 return FALSE;
439 }
440
441 // Set the information in the setup struct
442 LinuxSetupSector->RamdiskAddress = (U32)LinuxInitrdLoadAddress;
443 LinuxSetupSector->RamdiskSize = LinuxInitrdSize;
444
445 // Read in the ramdisk
446 for (BytesLoaded=0; BytesLoaded<LinuxInitrdSize; )
447 {
448 if (!ReadFile(LinuxInitrdFile, 0x4000, NULL, (PVOID)LinuxInitrdLoadAddress))
449 {
450 return FALSE;
451 }
452
453 BytesLoaded += 0x4000;
454 LinuxInitrdLoadAddress += 0x4000;
455
456 UiDrawProgressBarCenter(BytesLoaded, LinuxInitrdSize);
457 }
458
459 return TRUE;
460 }