]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/LoadLinuxLib/Linux.c
OvmfPkg: Add LoadLinuxLib library implementation
[mirror_edk2.git] / OvmfPkg / Library / LoadLinuxLib / Linux.c
CommitLineData
3c0a051f 1/** @file\r
2\r
3 Copyright (c) 2011 - 2012, Intel Corporation. All rights reserved.<BR>\r
4\r
5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include "LoadLinuxLib.h"\r
16\r
17\r
18/**\r
19 A simple check of the kernel setup image\r
20\r
21 An assumption is made that the size of the data is at least the\r
22 size of struct boot_params.\r
23\r
24 @param[in] KernelSetup - The kernel setup image\r
25\r
26 @retval EFI_SUCCESS - The kernel setup looks valid and supported\r
27 @retval EFI_INVALID_PARAMETER - KernelSetup was NULL\r
28 @retval EFI_UNSUPPORTED - The kernel setup is not valid or supported\r
29\r
30**/\r
31STATIC\r
32EFI_STATUS\r
33EFIAPI\r
34BasicKernelSetupCheck (\r
35 IN VOID *KernelSetup\r
36 )\r
37{\r
38 return LoadLinuxCheckKernelSetup(KernelSetup, sizeof (struct boot_params));\r
39}\r
40\r
41\r
42EFI_STATUS\r
43EFIAPI\r
44LoadLinuxCheckKernelSetup (\r
45 IN VOID *KernelSetup,\r
46 IN UINTN KernelSetupSize\r
47 )\r
48{\r
49 struct boot_params *Bp;\r
50\r
51 if (KernelSetup == NULL) {\r
52 return EFI_INVALID_PARAMETER;\r
53 }\r
54\r
55 if (KernelSetupSize < sizeof (*Bp)) {\r
56 return EFI_UNSUPPORTED;\r
57 }\r
58\r
59 Bp = (struct boot_params*) KernelSetup;\r
60\r
61 if ((Bp->hdr.signature != 0xAA55) || // Check boot sector signature\r
62 (Bp->hdr.header != SETUP_HDR) ||\r
63 (Bp->hdr.version < 0x205) // We only support relocatable kernels\r
64 ) {\r
65 return EFI_UNSUPPORTED;\r
66 } else {\r
67 return EFI_SUCCESS;\r
68 }\r
69}\r
70\r
71\r
72UINTN\r
73EFIAPI\r
74LoadLinuxGetKernelSize (\r
75 IN VOID *KernelSetup,\r
76 IN UINTN KernelSize\r
77 )\r
78{\r
79 struct boot_params *Bp;\r
80\r
81 if (EFI_ERROR (BasicKernelSetupCheck (KernelSetup))) {\r
82 return 0;\r
83 }\r
84\r
85 Bp = (struct boot_params*) KernelSetup;\r
86\r
87 if (Bp->hdr.version > 0x20a) {\r
88 return Bp->hdr.init_size;\r
89 } else {\r
90 //\r
91 // Add extra size for kernel decompression\r
92 //\r
93 return 3 * KernelSize;\r
94 }\r
95}\r
96\r
97\r
98VOID*\r
99EFIAPI\r
100LoadLinuxAllocateKernelSetupPages (\r
101 IN UINTN Pages\r
102 )\r
103{\r
104 EFI_STATUS Status;\r
105 EFI_PHYSICAL_ADDRESS Address;\r
106\r
107 Address = BASE_1GB;\r
108 Status = gBS->AllocatePages (\r
109 AllocateMaxAddress,\r
110 EfiLoaderData,\r
111 Pages,\r
112 &Address\r
113 );\r
114 if (!EFI_ERROR (Status)) {\r
115 return (VOID*)(UINTN) Address;\r
116 } else {\r
117 return NULL;\r
118 }\r
119}\r
120\r
121\r
122VOID*\r
123EFIAPI\r
124LoadLinuxAllocateKernelPages (\r
125 IN VOID *KernelSetup,\r
126 IN UINTN Pages\r
127 )\r
128{\r
129 EFI_STATUS Status;\r
130 EFI_PHYSICAL_ADDRESS KernelAddress;\r
131 UINT32 Loop;\r
132 struct boot_params *Bp;\r
133\r
134 if (EFI_ERROR (BasicKernelSetupCheck (KernelSetup))) {\r
135 return NULL;\r
136 }\r
137\r
138 Bp = (struct boot_params*) KernelSetup;\r
139\r
140 for (Loop = 1; Loop < 512; Loop++) {\r
141 KernelAddress = MultU64x32 (\r
142 2 * Bp->hdr.kernel_alignment,\r
143 Loop\r
144 );\r
145 Status = gBS->AllocatePages (\r
146 AllocateAddress,\r
147 EfiLoaderData,\r
148 Pages,\r
149 &KernelAddress\r
150 );\r
151 if (!EFI_ERROR (Status)) {\r
152 return (VOID*)(UINTN) KernelAddress;\r
153 }\r
154 }\r
155\r
156 return NULL;\r
157}\r
158\r
159\r
160VOID*\r
161EFIAPI\r
162LoadLinuxAllocateCommandLinePages (\r
163 IN UINTN Pages\r
164 )\r
165{\r
166 EFI_STATUS Status;\r
167 EFI_PHYSICAL_ADDRESS Address;\r
168\r
169 Address = 0xa0000;\r
170 Status = gBS->AllocatePages (\r
171 AllocateMaxAddress,\r
172 EfiLoaderData,\r
173 Pages,\r
174 &Address\r
175 );\r
176 if (!EFI_ERROR (Status)) {\r
177 return (VOID*)(UINTN) Address;\r
178 } else {\r
179 return NULL;\r
180 }\r
181}\r
182\r
183\r
184VOID*\r
185EFIAPI\r
186LoadLinuxAllocateInitrdPages (\r
187 IN VOID *KernelSetup,\r
188 IN UINTN Pages\r
189 )\r
190{\r
191 EFI_STATUS Status;\r
192 EFI_PHYSICAL_ADDRESS Address;\r
193\r
194 struct boot_params *Bp;\r
195\r
196 if (EFI_ERROR (BasicKernelSetupCheck (KernelSetup))) {\r
197 return NULL;\r
198 }\r
199\r
200 Bp = (struct boot_params*) KernelSetup;\r
201\r
202 Address = (EFI_PHYSICAL_ADDRESS)(UINTN) Bp->hdr.ramdisk_max;\r
203 Status = gBS->AllocatePages (\r
204 AllocateMaxAddress,\r
205 EfiLoaderData,\r
206 Pages,\r
207 &Address\r
208 );\r
209 if (!EFI_ERROR (Status)) {\r
210 return (VOID*)(UINTN) Address;\r
211 } else {\r
212 return NULL;\r
213 }\r
214}\r
215\r
216\r
217STATIC\r
218VOID\r
219SetupLinuxMemmap (\r
220 IN OUT struct boot_params *Bp\r
221 )\r
222{\r
223 EFI_STATUS Status;\r
224 UINT8 TmpMemoryMap[1];\r
225 UINTN MapKey;\r
226 UINTN DescriptorSize;\r
227 UINT32 DescriptorVersion;\r
228 UINTN MemoryMapSize;\r
229 EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
230 EFI_MEMORY_DESCRIPTOR *MemoryMapPtr;\r
231 UINTN Index;\r
232 struct efi_info *Efi;\r
233 struct e820_entry *LastE820;\r
234 struct e820_entry *E820;\r
235 UINTN E820EntryCount;\r
236 EFI_PHYSICAL_ADDRESS LastEndAddr;\r
237\r
238 //\r
239 // Get System MemoryMapSize\r
240 //\r
241 MemoryMapSize = sizeof (TmpMemoryMap);\r
242 Status = gBS->GetMemoryMap (\r
243 &MemoryMapSize,\r
244 (EFI_MEMORY_DESCRIPTOR *)TmpMemoryMap,\r
245 &MapKey,\r
246 &DescriptorSize,\r
247 &DescriptorVersion\r
248 );\r
249 ASSERT (Status == EFI_BUFFER_TOO_SMALL);\r
250 //\r
251 // Enlarge space here, because we will allocate pool now.\r
252 //\r
253 MemoryMapSize += EFI_PAGE_SIZE;\r
254 MemoryMap = AllocatePool (MemoryMapSize);\r
255 ASSERT (MemoryMap != NULL);\r
256\r
257 //\r
258 // Get System MemoryMap\r
259 //\r
260 Status = gBS->GetMemoryMap (\r
261 &MemoryMapSize,\r
262 MemoryMap,\r
263 &MapKey,\r
264 &DescriptorSize,\r
265 &DescriptorVersion\r
266 );\r
267 ASSERT_EFI_ERROR (Status);\r
268\r
269 LastE820 = NULL;\r
270 E820 = &Bp->e820_map[0];\r
271 E820EntryCount = 0;\r
272 LastEndAddr = 0;\r
273 MemoryMapPtr = MemoryMap;\r
274 for (Index = 0; Index < (MemoryMapSize / DescriptorSize); Index++) {\r
275 UINTN E820Type = 0;\r
276\r
277 if (MemoryMap->NumberOfPages == 0) {\r
278 continue;\r
279 }\r
280\r
281 switch(MemoryMap->Type) {\r
282 case EfiReservedMemoryType:\r
283 case EfiRuntimeServicesCode:\r
284 case EfiRuntimeServicesData:\r
285 case EfiMemoryMappedIO:\r
286 case EfiMemoryMappedIOPortSpace:\r
287 case EfiPalCode:\r
288 E820Type = E820_RESERVED;\r
289 break;\r
290\r
291 case EfiUnusableMemory:\r
292 E820Type = E820_UNUSABLE;\r
293 break;\r
294\r
295 case EfiACPIReclaimMemory:\r
296 E820Type = E820_ACPI;\r
297 break;\r
298\r
299 case EfiLoaderCode:\r
300 case EfiLoaderData:\r
301 case EfiBootServicesCode:\r
302 case EfiBootServicesData:\r
303 case EfiConventionalMemory:\r
304 E820Type = E820_RAM;\r
305 break;\r
306\r
307 case EfiACPIMemoryNVS:\r
308 E820Type = E820_NVS;\r
309 break;\r
310\r
311 default:\r
312 DEBUG ((\r
313 EFI_D_ERROR,\r
314 "Invalid EFI memory descriptor type (0x%x)!\n",\r
315 MemoryMap->Type\r
316 ));\r
317 continue;\r
318 }\r
319\r
320 if ((LastE820 != NULL) &&\r
321 (LastE820->type == (UINT32) E820Type) &&\r
322 (MemoryMap->PhysicalStart == LastEndAddr)) {\r
323 LastE820->size += EFI_PAGES_TO_SIZE (MemoryMap->NumberOfPages);\r
324 LastEndAddr += EFI_PAGES_TO_SIZE (MemoryMap->NumberOfPages);\r
325 } else {\r
326 if (E820EntryCount >= (sizeof (Bp->e820_map) / sizeof (Bp->e820_map[0]))) {\r
327 break;\r
328 }\r
329 E820->type = (UINT32) E820Type;\r
330 E820->addr = MemoryMap->PhysicalStart;\r
331 E820->size = EFI_PAGES_TO_SIZE (MemoryMap->NumberOfPages);\r
332 LastE820 = E820;\r
333 LastEndAddr = E820->addr + E820->size;\r
334 E820++;\r
335 E820EntryCount++;\r
336 }\r
337\r
338 //\r
339 // Get next item\r
340 //\r
341 MemoryMap = (EFI_MEMORY_DESCRIPTOR *)((UINTN)MemoryMap + DescriptorSize);\r
342 }\r
343 Bp->e820_entries = (UINT8) E820EntryCount;\r
344\r
345 Efi = &Bp->efi_info;\r
346 Efi->efi_systab = (UINT32)(UINTN) gST;\r
347 Efi->efi_memdesc_size = (UINT32) DescriptorSize;\r
348 Efi->efi_memdesc_version = DescriptorVersion;\r
349 Efi->efi_memmap = (UINT32)(UINTN) MemoryMapPtr;\r
350 Efi->efi_memmap_size = (UINT32) MemoryMapSize;\r
351#ifdef MDE_CPU_IA32\r
352 Efi->efi_loader_signature = SIGNATURE_32 ('E', 'L', '3', '2');\r
353#else\r
354 Efi->efi_systab_hi = ((UINT64)(UINTN) gST) >> 32;\r
355 Efi->efi_memmap_hi = ((UINT64)(UINTN) MemoryMapPtr) >> 32;\r
356 Efi->efi_loader_signature = SIGNATURE_32 ('E', 'L', '6', '4');\r
357#endif\r
358\r
359 gBS->ExitBootServices (gImageHandle, MapKey);\r
360}\r
361\r
362\r
363EFI_STATUS\r
364EFIAPI\r
365LoadLinuxSetCommandLine (\r
366 IN OUT VOID *KernelSetup,\r
367 IN CHAR8 *CommandLine\r
368 )\r
369{\r
370 EFI_STATUS Status;\r
371 struct boot_params *Bp;\r
372\r
373 Status = BasicKernelSetupCheck (KernelSetup);\r
374 if (EFI_ERROR (Status)) {\r
375 return Status;\r
376 }\r
377\r
378 Bp = (struct boot_params*) KernelSetup;\r
379\r
380 Bp->hdr.cmd_line_ptr = (UINT32)(UINTN) CommandLine;\r
381\r
382 return EFI_SUCCESS;\r
383}\r
384\r
385\r
386EFI_STATUS\r
387EFIAPI\r
388LoadLinuxSetInitrd (\r
389 IN OUT VOID *KernelSetup,\r
390 IN VOID *Initrd,\r
391 IN UINTN InitrdSize\r
392 )\r
393{\r
394 EFI_STATUS Status;\r
395 struct boot_params *Bp;\r
396\r
397 Status = BasicKernelSetupCheck (KernelSetup);\r
398 if (EFI_ERROR (Status)) {\r
399 return Status;\r
400 }\r
401\r
402 Bp = (struct boot_params*) KernelSetup;\r
403\r
404 Bp->hdr.ramdisk_start = (UINT32)(UINTN) Initrd;\r
405 Bp->hdr.ramdisk_len = (UINT32) InitrdSize;\r
406\r
407 return EFI_SUCCESS;\r
408}\r
409\r
410\r
411STATIC VOID\r
412FindBits (\r
413 unsigned long Mask,\r
414 UINT8 *Pos,\r
415 UINT8 *Size\r
416 )\r
417{\r
418 UINT8 First, Len;\r
419\r
420 First = 0;\r
421 Len = 0;\r
422\r
423 if (Mask) {\r
424 while (!(Mask & 0x1)) {\r
425 Mask = Mask >> 1;\r
426 First++;\r
427 }\r
428\r
429 while (Mask & 0x1) {\r
430 Mask = Mask >> 1;\r
431 Len++;\r
432 }\r
433 }\r
434 *Pos = First;\r
435 *Size = Len;\r
436}\r
437\r
438\r
439STATIC\r
440EFI_STATUS\r
441SetupGraphicsFromGop (\r
442 struct screen_info *Si,\r
443 EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop\r
444 )\r
445{\r
446 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;\r
447 EFI_STATUS Status;\r
448 UINTN Size;\r
449\r
450 Status = Gop->QueryMode(Gop, Gop->Mode->Mode, &Size, &Info);\r
451 if (EFI_ERROR (Status)) {\r
452 return Status;\r
453 }\r
454\r
455 /* We found a GOP */\r
456\r
457 /* EFI framebuffer */\r
458 Si->orig_video_isVGA = 0x70;\r
459\r
460 Si->orig_x = 0;\r
461 Si->orig_y = 0;\r
462 Si->orig_video_page = 0;\r
463 Si->orig_video_mode = 0;\r
464 Si->orig_video_cols = 0;\r
465 Si->orig_video_lines = 0;\r
466 Si->orig_video_ega_bx = 0;\r
467 Si->orig_video_points = 0;\r
468\r
469 Si->lfb_base = (UINT32) Gop->Mode->FrameBufferBase;\r
470 Si->lfb_size = (UINT32) Gop->Mode->FrameBufferSize;\r
471 Si->lfb_width = (UINT16) Info->HorizontalResolution;\r
472 Si->lfb_height = (UINT16) Info->VerticalResolution;\r
473 Si->pages = 1;\r
474 Si->vesapm_seg = 0;\r
475 Si->vesapm_off = 0;\r
476\r
477 if (Info->PixelFormat == PixelRedGreenBlueReserved8BitPerColor) {\r
478 Si->lfb_depth = 32;\r
479 Si->red_size = 8;\r
480 Si->red_pos = 0;\r
481 Si->green_size = 8;\r
482 Si->green_pos = 8;\r
483 Si->blue_size = 8;\r
484 Si->blue_pos = 16;\r
485 Si->rsvd_size = 8;\r
486 Si->rsvd_pos = 24;\r
487 Si->lfb_linelength = (UINT16) (Info->PixelsPerScanLine * 4);\r
488\r
489 } else if (Info->PixelFormat == PixelBlueGreenRedReserved8BitPerColor) {\r
490 Si->lfb_depth = 32;\r
491 Si->red_size = 8;\r
492 Si->red_pos = 16;\r
493 Si->green_size = 8;\r
494 Si->green_pos = 8;\r
495 Si->blue_size = 8;\r
496 Si->blue_pos = 0;\r
497 Si->rsvd_size = 8;\r
498 Si->rsvd_pos = 24;\r
499 Si->lfb_linelength = (UINT16) (Info->PixelsPerScanLine * 4);\r
500 } else if (Info->PixelFormat == PixelBitMask) {\r
501 FindBits(Info->PixelInformation.RedMask,\r
502 &Si->red_pos, &Si->red_size);\r
503 FindBits(Info->PixelInformation.GreenMask,\r
504 &Si->green_pos, &Si->green_size);\r
505 FindBits(Info->PixelInformation.BlueMask,\r
506 &Si->blue_pos, &Si->blue_size);\r
507 FindBits(Info->PixelInformation.ReservedMask,\r
508 &Si->rsvd_pos, &Si->rsvd_size);\r
509 Si->lfb_depth = Si->red_size + Si->green_size +\r
510 Si->blue_size + Si->rsvd_size;\r
511 Si->lfb_linelength = (UINT16) ((Info->PixelsPerScanLine * Si->lfb_depth) / 8);\r
512 } else {\r
513 Si->lfb_depth = 4;\r
514 Si->red_size = 0;\r
515 Si->red_pos = 0;\r
516 Si->green_size = 0;\r
517 Si->green_pos = 0;\r
518 Si->blue_size = 0;\r
519 Si->blue_pos = 0;\r
520 Si->rsvd_size = 0;\r
521 Si->rsvd_pos = 0;\r
522 Si->lfb_linelength = Si->lfb_width / 2;\r
523 }\r
524\r
525 return Status;\r
526}\r
527\r
528\r
529STATIC\r
530EFI_STATUS\r
531SetupGraphics (\r
532 IN OUT struct boot_params *Bp\r
533 )\r
534{\r
535 EFI_STATUS Status;\r
536 EFI_HANDLE *HandleBuffer;\r
537 UINTN HandleCount;\r
538 UINTN Index;\r
539 EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;\r
540\r
541 ZeroMem ((VOID*)&Bp->screen_info, sizeof(Bp->screen_info));\r
542\r
543 Status = gBS->LocateHandleBuffer (\r
544 ByProtocol,\r
545 &gEfiGraphicsOutputProtocolGuid,\r
546 NULL,\r
547 &HandleCount,\r
548 &HandleBuffer\r
549 );\r
550 if (!EFI_ERROR (Status)) {\r
551 for (Index = 0; Index < HandleCount; Index++) {\r
552 Status = gBS->HandleProtocol (\r
553 HandleBuffer[Index],\r
554 &gEfiGraphicsOutputProtocolGuid,\r
555 (VOID*) &Gop\r
556 );\r
557 if (EFI_ERROR (Status)) {\r
558 continue;\r
559 }\r
560\r
561 Status = SetupGraphicsFromGop (&Bp->screen_info, Gop);\r
562 if (!EFI_ERROR (Status)) {\r
563 FreePool (HandleBuffer);\r
564 return EFI_SUCCESS;\r
565 }\r
566 }\r
567\r
568 FreePool (HandleBuffer);\r
569 }\r
570\r
571 return EFI_NOT_FOUND;\r
572}\r
573\r
574\r
575STATIC\r
576EFI_STATUS\r
577SetupLinuxBootParams (\r
578 IN VOID *Kernel,\r
579 IN OUT struct boot_params *Bp\r
580 )\r
581{\r
582 SetupGraphics (Bp);\r
583\r
584 Bp->hdr.code32_start = (UINT32)(UINTN) Kernel;\r
585\r
586 SetupLinuxMemmap (Bp);\r
587\r
588 return EFI_SUCCESS;\r
589}\r
590\r
591\r
592EFI_STATUS\r
593EFIAPI\r
594LoadLinux (\r
595 IN VOID *Kernel,\r
596 IN OUT VOID *KernelSetup\r
597 )\r
598{\r
599 EFI_STATUS Status;\r
600 struct boot_params *Bp;\r
601\r
602 Status = BasicKernelSetupCheck (KernelSetup);\r
603 if (EFI_ERROR (Status)) {\r
604 return Status;\r
605 }\r
606\r
607 Bp = (struct boot_params *) KernelSetup;\r
608\r
609 if (Bp->hdr.version < 0x205) {\r
610 //\r
611 // We only support relocatable kernels\r
612 //\r
613 return EFI_UNSUPPORTED;\r
614 }\r
615\r
616 InitLinuxDescriptorTables ();\r
617\r
618 SetupLinuxBootParams (Kernel, (struct boot_params*) KernelSetup);\r
619\r
620 DEBUG ((EFI_D_INFO, "Jumping to kernel\n"));\r
621 DisableInterrupts ();\r
622 SetLinuxDescriptorTables ();\r
623 JumpToKernel (Kernel, (VOID*) KernelSetup);\r
624\r
625 return EFI_SUCCESS;\r
626}\r
627\r