blob: 0f3b25778d8ca0b21d7da41ed26128cbe28e5553 [file] [log] [blame]
/*
* Copyright 2016 Google Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <inttypes.h>
#include <stdio.h>
#include "base/xalloc.h"
#include "base/fwdb.h"
#include "base/physmem.h"
#include "module/uefi/fwdb.h"
#include "uefi/edk/Protocol/EfiShell.h"
#include "uefi/edk/Protocol/EfiShellInterface.h"
#include "uefi/edk/Protocol/EfiShellParameters.h"
#include "uefi/edk/Protocol/LoadedImage.h"
#include "uefi/edk/Protocol/SimpleFileSystem.h"
#include "uefi/uefi.h"
#include "vboot/util/memory.h"
extern uint8_t _binary_ro_image_start;
extern uint8_t _binary_ro_image_size;
extern uint8_t ImageBase;
static EFI_GUID shell_parameters_protocol_guid =
EFI_SHELL_PARAMETERS_PROTOCOL_GUID;
static EFI_GUID shell_protocol_guid = EFI_SHELL_PROTOCOL_GUID;
static EFI_GUID shell_interface_guid = SHELL_INTERFACE_PROTOCOL_GUID;
static EFI_GUID file_info_guid = EFI_FILE_INFO_ID;
static EFI_GUID loaded_image_protocol_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
static EFI_GUID simple_fs_protocol_guid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
static EFI_SHELL_PARAMETERS_PROTOCOL *get_shell_parameters(
EFI_BOOT_SERVICES *bs)
{
EFI_HANDLE handle;
if (uefi_image_handle(&handle))
return NULL;
EFI_SHELL_PARAMETERS_PROTOCOL *shell_params;
EFI_STATUS status = bs->HandleProtocol(
handle, &shell_parameters_protocol_guid,
(void **)&shell_params);
if (status != EFI_SUCCESS) {
printf("No shell parameter protocol found.\n");
return NULL;
}
return shell_params;
}
static EFI_SHELL_INTERFACE *get_shell_interface(EFI_BOOT_SERVICES *bs)
{
EFI_HANDLE handle;
if (uefi_image_handle(&handle))
return NULL;
EFI_SHELL_INTERFACE *shell_int;
EFI_STATUS status = bs->HandleProtocol(
handle, &shell_interface_guid, (void **)&shell_int);
if (status != EFI_SUCCESS) {
printf("No shell interface found.\n");
return NULL;
}
return shell_int;
}
static EFI_SHELL_PROTOCOL *get_shell_protocol(EFI_BOOT_SERVICES *bs)
{
EFI_SHELL_PROTOCOL *shell_prot;
UINTN buf_size = 0;
EFI_HANDLE dummy;
EFI_STATUS status = bs->LocateHandle(
ByProtocol, &shell_protocol_guid, NULL, &buf_size, &dummy);
if (status == EFI_NOT_FOUND) {
printf("No shell protocol found.\n");
return NULL;
}
if (status != EFI_BUFFER_TOO_SMALL) {
printf("Error retrieving shell protocol handles.\n");
return NULL;
}
EFI_HANDLE *handles = xmalloc(buf_size);
status = bs->LocateHandle(ByProtocol, &shell_protocol_guid,
NULL, &buf_size, handles);
if (status != EFI_SUCCESS) {
printf("Failed to retrieve shell protocol handles.\n");
return NULL;
}
int handle_count = buf_size / sizeof(dummy);
if (handle_count > 1)
printf("More than one shell found?\n");
EFI_HANDLE handle = handles[0];
free(handles);
status = bs->HandleProtocol(handle, &shell_protocol_guid,
(void **)&shell_prot);
if (status != EFI_SUCCESS) {
printf("Failed to retrieve shell protocol.\n");
return NULL;
}
return shell_prot;
}
static int insert_file_into_fwdb(EFI_SHELL_PROTOCOL *shell_prot,
SHELL_FILE_HANDLE file, const char *name)
{
UINT64 size;
EFI_STATUS status = shell_prot->GetFileSize(file, &size);
if (status != EFI_SUCCESS) {
printf("Failed to get file size.\n");
return 1;
}
FwdbEntry entry = { .ptr = NULL, .size = size };
if (fwdb_access(name, NULL, &entry) || fwdb_access(name, &entry, NULL))
return 1;
UINTN buffer_size = size;
status = shell_prot->ReadFile(file, &buffer_size, entry.ptr);
if (status != EFI_SUCCESS) {
printf("Failed to read file.\n");
return 1;
}
return 0;
}
static int insert_file_name_into_fwdb_from_fs(EFI_FILE_PROTOCOL *root,
CHAR16 *file_name,
const char *name)
{
EFI_FILE_PROTOCOL *file;
EFI_STATUS status = root->Open(root, &file, file_name,
EFI_FILE_MODE_READ, 0);
if (status != EFI_SUCCESS) {
printf("Failed to open file.\n");
return 1;
}
EFI_FILE_INFO file_info;
UINTN buf_size = sizeof(file_info);
status = file->GetInfo(file, &file_info_guid,
&buf_size, &file_info);
if (status != EFI_SUCCESS) {
file->Close(file);
printf("Failed to get file size.\n");
return 1;
}
FwdbEntry entry = { .ptr = NULL, .size = file_info.FileSize };
if (fwdb_access(name, NULL, &entry) ||
fwdb_access(name, &entry, NULL)) {
file->Close(file);
return 1;
}
buf_size = entry.size;
status = file->Read(file, &buf_size, entry.ptr);
if (status != EFI_SUCCESS) {
file->Close(file);
printf("Failed to read file.\n");
return 1;
}
file->Close(file);
return 0;
}
static int insert_file_name_into_fwdb(EFI_SHELL_PROTOCOL *shell_prot,
CHAR16 *file_name, const char *name)
{
SHELL_FILE_HANDLE file;
EFI_STATUS status = shell_prot->OpenFileByName(
file_name, &file, EFI_FILE_MODE_READ);
if (status != EFI_SUCCESS) {
printf("Failed to open read/write image.\n");
return 1;
}
int ret = insert_file_into_fwdb(shell_prot, file, name);
shell_prot->CloseFile(file);
return ret;
}
int uefi_prepare_fwdb_storage(void)
{
FwdbEntry ro_image_entry = {
.ptr = &_binary_ro_image_start,
.size = &_binary_ro_image_size - &ImageBase,
};
if (fwdb_access("uefi_ro_image", NULL, &ro_image_entry))
return 1;
EFI_SYSTEM_TABLE *st = uefi_system_table_ptr();
if (!st)
return 1;
EFI_BOOT_SERVICES *bs = st->BootServices;
EFI_SHELL_PROTOCOL *shell_prot = get_shell_protocol(bs);
EFI_SHELL_PARAMETERS_PROTOCOL *shell_params = NULL;
EFI_SHELL_INTERFACE *shell_int = NULL;
if (shell_prot) {
printf("UEFI standard shell protocol found.\n");
shell_params = get_shell_parameters(bs);
if (!shell_params)
return 1;
if (shell_params->Argc != 3) {
printf("Bad number of arguments.\n");
printf("Usage: dc <rwa image> <rwb image>\n");
return 1;
}
if (insert_file_name_into_fwdb(
shell_prot, shell_params->Argv[1], "uefi_rw_a_image") ||
insert_file_name_into_fwdb(
shell_prot, shell_params->Argv[2], "uefi_rw_b_image")) {
return 1;
}
} else {
printf("Falling back to non-standard shell "
"interface protocol.\n");
shell_int = get_shell_interface(bs);
if (shell_int->Argc != 1) {
printf("Bad number of arguments.\n");
printf("Usage: dc\n");
printf("When using the non-standard interface, rwa "
"and rwb are assumed to be on \n");
printf("the same device as the main depthcharge "
"executable at the path \n");
printf("\\depthcharge\\rwa and \\depthcharge\\rwb.\n");
return 1;
}
EFI_HANDLE handle;
if (uefi_image_handle(&handle))
return 1;
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
EFI_STATUS status =
bs->HandleProtocol(handle, &loaded_image_protocol_guid,
(void **)&loaded_image);
if (status != EFI_SUCCESS) {
printf("Failed to open loaded image protocol.\n");
return 1;
}
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *simple_fs;
status = bs->HandleProtocol(loaded_image->DeviceHandle,
&simple_fs_protocol_guid,
(void **)&simple_fs);
if (status != EFI_SUCCESS) {
printf("Failed to open simple fs protocol.\n");
return 1;
}
EFI_FILE_PROTOCOL *root;
status = simple_fs->OpenVolume(simple_fs, &root);
if (status != EFI_SUCCESS) {
printf("Failed to open simple fs root.\n");
return 1;
}
int ret = insert_file_name_into_fwdb_from_fs(
root, L"depthcharge\\rwa", "uefi_rw_a_image");
ret = ret || insert_file_name_into_fwdb_from_fs(
root, L"depthcharge\\rwb", "uefi_rw_b_image");
status = root->Close(root);
if (status != EFI_SUCCESS) {
printf("Failed to close fs root.\n");
return 1;
}
return ret;
}
return 0;
}
int uefi_prepare_fwdb_e820_map(void)
{
if (prepare_e820_mem_ranges())
return 1;
E820MemRanges *e820 = get_e820_mem_ranges();
if (!e820)
return 1;
unsigned size = 0;
EFI_MEMORY_DESCRIPTOR *map;
unsigned desc_size;
uint32_t desc_ver;
if (uefi_get_memory_map(&size, &map, &desc_size, &desc_ver))
return 1;
if (desc_size < sizeof(EFI_MEMORY_DESCRIPTOR)) {
printf("Descriptor size is too small?\n");
free(map);
return 1;
}
int num_descs = size / desc_size;
int num_ranges = 0;
for (int i = 0; i < num_descs; i++) {
EFI_MEMORY_DESCRIPTOR *desc =
(void *)((uint8_t *)map + i * desc_size);
if (desc->NumberOfPages == 0)
continue;
if (num_ranges >= ARRAY_SIZE(e820->ranges)) {
printf("Too many memory ranges to fit in the "
"FWDB map.\n");
free(map);
return 1;
}
E820MemRange *range = &e820->ranges[num_ranges];
range->base = desc->PhysicalStart;
range->size = desc->NumberOfPages * 4 * 1024;
num_ranges++;
switch (desc->Type) {
case EfiLoaderCode:
case EfiLoaderData:
case EfiBootServicesCode:
case EfiBootServicesData:
memory_mark_used(range->base,
range->base + range->size);
case EfiConventionalMemory:
range->type = E820MemRange_Ram;
break;
case EfiReservedMemoryType:
case EfiRuntimeServicesCode:
case EfiRuntimeServicesData:
case EfiPalCode:
case EfiACPIMemoryNVS:
range->type = E820MemRange_Reserved;
break;
case EfiACPIReclaimMemory:
range->type = E820MemRange_Acpi;
break;
case EfiPersistentMemory:
range->type = E820MemRange_Nvs;
break;
case EfiUnusableMemory:
range->type = E820MemRange_Unusable;
break;
default:
printf("Warning: Memory range of type %#"PRIx32" "
"marked as reserved.\n", desc->Type);
range->type = E820MemRange_Reserved;
}
range->handoff_tag = desc->Type;
}
e820->num_ranges = num_ranges;
free(map);
return 0;
}