1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 #include "uefi/uefi.h"
19 
20 #include <lib/bio.h>
21 #include <lib/heap.h>
22 #include <lk/console_cmd.h>
23 #include <lk/debug.h>
24 #include <lk/err.h>
25 #include <lk/trace.h>
26 #include <platform.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <uefi/boot_service.h>
31 #include <uefi/protocols/simple_text_output_protocol.h>
32 #include <uefi/runtime_service.h>
33 #include <uefi/system_table.h>
34 
35 #include "boot_service_provider.h"
36 #include "charset.h"
37 #include "configuration_table.h"
38 #include "defer.h"
39 #include "memory_protocols.h"
40 #include "pe.h"
41 #include "relocation.h"
42 #include "runtime_service_provider.h"
43 #include "switch_stack.h"
44 #include "text_protocol.h"
45 #include "uefi_platform.h"
46 #include "debug_support.h"
47 #include "variable_mem.h"
48 
49 namespace {
50 
51 constexpr auto EFI_SYSTEM_TABLE_SIGNATURE =
52     static_cast<u64>(0x5453595320494249ULL);
53 
54 // ASCII "PE\x0\x0"
55 
56 using EfiEntry = int (*)(void *, struct EfiSystemTable *);
57 
58 template <typename T>
fill(T * data,size_t skip,uint8_t begin=0)59 void fill(T *data, size_t skip, uint8_t begin = 0) {
60   auto ptr = reinterpret_cast<char *>(data);
61   for (size_t i = 0; i < sizeof(T); i++) {
62     if (i < skip) {
63       continue;
64     }
65     ptr[i] = begin++;
66   }
67 }
68 
69 static char16_t firmwareVendor[] = u"Little Kernel";
70 
load_sections_and_execute(bdev_t * dev,const IMAGE_NT_HEADERS64 * pe_header)71 int load_sections_and_execute(bdev_t *dev,
72                               const IMAGE_NT_HEADERS64 *pe_header) {
73   const auto file_header = &pe_header->FileHeader;
74   const auto optional_header = &pe_header->OptionalHeader;
75   const auto sections = file_header->NumberOfSections;
76   const auto section_header = reinterpret_cast<const IMAGE_SECTION_HEADER *>(
77       reinterpret_cast<const char *>(pe_header) + sizeof(IMAGE_FILE_HEADER) +
78       file_header->SizeOfOptionalHeader);
79   if (sections <= 0) {
80     printf("This PE file does not have any sections, unsupported.\n");
81     return -8;
82   }
83   for (size_t i = 0; i < sections; i++) {
84     if (section_header[i].NumberOfRelocations != 0) {
85       printf("Section %s requires relocation, which is not supported.\n",
86              section_header[i].Name);
87       return -6;
88     }
89   }
90   setup_heap();
91   DEFER { reset_heap(); };
92   const auto &last_section = section_header[sections - 1];
93   const auto virtual_size = ROUNDUP(
94       last_section.VirtualAddress + last_section.Misc.VirtualSize, PAGE_SIZE);
95   const auto image_base = reinterpret_cast<char *>(
96       alloc_page(reinterpret_cast<void *>(optional_header->ImageBase),
97                  virtual_size, 21 /* Kernel requires 2MB alignment */));
98   if (image_base == nullptr) {
99     return -7;
100   }
101   memset(image_base, 0, virtual_size);
102   DEFER { free_pages(image_base, virtual_size / PAGE_SIZE); };
103   bio_read(dev, image_base, 0, section_header[0].PointerToRawData);
104 
105   for (size_t i = 0; i < sections; i++) {
106     const auto &section = section_header[i];
107     bio_read(dev, image_base + section.VirtualAddress, section.PointerToRawData,
108              section.SizeOfRawData);
109   }
110   printf("Relocating image from 0x%llx to %p\n", optional_header->ImageBase,
111          image_base);
112   relocate_image(image_base);
113   auto entry = reinterpret_cast<int (*)(void *, void *)>(
114       image_base + optional_header->AddressOfEntryPoint);
115   printf("Entry function located at %p\n", entry);
116 
117   EfiSystemTable &table = *static_cast<EfiSystemTable *>(alloc_page(PAGE_SIZE));
118   memset(&table, 0, sizeof(EfiSystemTable));
119   DEFER { free_pages(&table, 1); };
120   EfiBootService boot_service{};
121   EfiRuntimeService runtime_service{};
122   fill(&runtime_service, 0);
123   fill(&boot_service, 0);
124   setup_runtime_service_table(&runtime_service);
125   setup_boot_service_table(&boot_service);
126   table.firmware_vendor = firmwareVendor;
127   table.runtime_service = &runtime_service;
128   table.boot_services = &boot_service;
129   table.header.signature = EFI_SYSTEM_TABLE_SIGNATURE;
130   table.header.revision = 2 << 16;
131   EfiSimpleTextOutputProtocol console_out = get_text_output_protocol();
132   table.con_out = &console_out;
133   table.configuration_table =
134       reinterpret_cast<EfiConfigurationTable *>(alloc_page(PAGE_SIZE));
135   DEFER { free_pages(table.configuration_table, 1); };
136   memset(table.configuration_table, 0, PAGE_SIZE);
137   setup_configuration_table(&table);
138   auto status = platform_setup_system_table(&table);
139   if (status != SUCCESS) {
140     printf("platform_setup_system_table failed: %lu\n", status);
141     return -static_cast<int>(status);
142   }
143   status = efi_initialize_system_table_pointer(&table);
144   if (status != SUCCESS) {
145     printf("efi_initialize_system_table_pointer failed: %lu\n", status);
146     return -static_cast<int>(status);
147   }
148   setup_debug_support(table, image_base, virtual_size, dev);
149 
150   constexpr size_t kStackSize = 1 * 1024ul * 1024;
151   auto stack = reinterpret_cast<char *>(alloc_page(kStackSize, 23));
152   memset(stack, 0, kStackSize);
153   DEFER {
154     free_pages(stack, kStackSize / PAGE_SIZE);
155     stack = nullptr;
156   };
157   printf("Calling kernel with stack [%p, %p]\n", stack, stack + kStackSize - 1);
158   int ret = static_cast<int>(
159       call_with_stack(stack + kStackSize, entry, image_base, &table));
160 
161   teardown_debug_support(image_base);
162 
163   return ret;
164 }
165 
cmd_uefi_load(int argc,const console_cmd_args * argv)166 int cmd_uefi_load(int argc, const console_cmd_args *argv) {
167   if (argc != 2) {
168     printf("Usage: %s <name of block device to load from>\n", argv[0].str);
169     return 1;
170   }
171   load_pe_file(argv[1].str);
172   return 0;
173 }
174 
cmd_uefi_set_variable(int argc,const console_cmd_args * argv)175 int cmd_uefi_set_variable(int argc, const console_cmd_args *argv) {
176   if (argc != 3) {
177     printf("Usage: %s <variable> <data>\n", argv[0].str);
178     return 1;
179   }
180   EfiGuid guid = EFI_GLOBAL_VARIABLE_GUID;
181   char16_t buffer[128];
182   utf8_to_utf16(buffer, argv[1].str, sizeof(buffer) / sizeof(buffer[0]));
183   efi_set_variable(buffer,
184                    &guid,
185                    EFI_VARIABLE_BOOTSERVICE_ACCESS,
186                    argv[2].str,
187                    strlen(argv[2].str));
188   return 0;
189 }
190 
cmd_uefi_list_variable(int argc,const console_cmd_args * argv)191 int cmd_uefi_list_variable(int argc, const console_cmd_args *argv) {
192   efi_list_variable();
193   return 0;
194 }
195 
196 STATIC_COMMAND_START
197 STATIC_COMMAND("uefi_load", "load UEFI application and run it", &cmd_uefi_load)
198 STATIC_COMMAND("uefi_set_var", "set UEFI variable", &cmd_uefi_set_variable)
199 STATIC_COMMAND("uefi_list_var", "list UEFI variable", &cmd_uefi_list_variable)
200 STATIC_COMMAND_END(uefi);
201 
202 }  // namespace
203 
load_pe_file(const char * blkdev)204 int load_pe_file(const char *blkdev) {
205   bdev_t *dev = bio_open(blkdev);
206   if (!dev) {
207     printf("error opening block device %s\n", blkdev);
208     return -1;
209   }
210   DEFER {
211     bio_close(dev);
212     dev = nullptr;
213   };
214   constexpr size_t kBlocKSize = 4096;
215 
216   lk_time_t t = current_time();
217   uint8_t *address = static_cast<uint8_t *>(malloc(kBlocKSize));
218   ssize_t err = bio_read(dev, static_cast<void *>(address), 0, kBlocKSize);
219   t = current_time() - t;
220   dprintf(INFO, "bio_read returns %d, took %u msecs (%d bytes/sec)\n", (int)err,
221           (uint)t, (uint32_t)((uint64_t)err * 1000 / t));
222 
223   const auto dos_header = reinterpret_cast<const IMAGE_DOS_HEADER *>(address);
224   if (!dos_header->CheckMagic()) {
225     printf("DOS Magic check failed %x\n", dos_header->e_magic);
226     return -2;
227   }
228   if (dos_header->e_lfanew > kBlocKSize - sizeof(IMAGE_FILE_HEADER)) {
229     printf(
230         "Invalid PE header offset %d exceeds maximum read size of %zu - %zu\n",
231         dos_header->e_lfanew, kBlocKSize, sizeof(IMAGE_FILE_HEADER));
232     return -3;
233   }
234   const auto pe_header = dos_header->GetPEHeader();
235   const auto file_header = &pe_header->FileHeader;
236   if (LE32(file_header->Signature) != kPEHeader) {
237     printf("COFF Magic check failed %x\n", LE32(file_header->Signature));
238     return -4;
239   }
240   printf("PE header machine type: %x\n",
241          static_cast<int>(file_header->Machine));
242   if (file_header->SizeOfOptionalHeader > sizeof(IMAGE_OPTIONAL_HEADER64) ||
243       file_header->SizeOfOptionalHeader <
244           sizeof(IMAGE_OPTIONAL_HEADER64) -
245               sizeof(IMAGE_OPTIONAL_HEADER64::DataDirectory)) {
246     printf("Unexpected size of optional header %d, expected %zu\n",
247            file_header->SizeOfOptionalHeader, sizeof(IMAGE_OPTIONAL_HEADER64));
248     return -5;
249   }
250   const auto optional_header = &pe_header->OptionalHeader;
251   if (optional_header->Subsystem != SubsystemType::EFIApplication) {
252     printf("Unsupported Subsystem type: %d %s\n", optional_header->Subsystem,
253            ToString(optional_header->Subsystem));
254   }
255   printf("Valid UEFI application found.\n");
256   auto ret = load_sections_and_execute(dev, pe_header);
257   printf("UEFI Application return code: %d\n", ret);
258   return ret;
259 }
260