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 §ion = 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