Copied from https://cs.android.com/android/kernel/superproject/+/common-android-mainline:bootable/libbootloader/gbl/libefi_types/defs/protocols/
263 lines
8.7 KiB
C++
263 lines
8.7 KiB
C++
/*
|
|
* Copyright (C) 2025 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
#include "uefi_platform.h"
|
|
|
|
#include <arch/arm64.h>
|
|
#include <lib/bio.h>
|
|
#include <lib/watchdog.h>
|
|
#include <libfdt.h>
|
|
#include <lk/err.h>
|
|
#include <lk/trace.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <uefi/protocols/erase_block_protocol.h>
|
|
#include <uefi/protocols/gbl_efi_image_loading_protocol.h>
|
|
#include <uefi/protocols/gbl_efi_os_configuration_protocol.h>
|
|
#include <uefi/types.h>
|
|
|
|
#include "defer.h"
|
|
#include "memory_protocols.h"
|
|
|
|
#define LOCAL_TRACE 0
|
|
|
|
__WEAK EFI_STATUS efi_dt_fixup(struct EfiDtFixupProtocol* self, void* fdt,
|
|
size_t* buffer_size, uint32_t flags) {
|
|
auto offset = fdt_subnode_offset(fdt, 0, "chosen");
|
|
if (offset < 0) {
|
|
printf("Failed to find chosen node %d\n", offset);
|
|
return EFI_STATUS_SUCCESS;
|
|
}
|
|
int length = 0;
|
|
auto prop = fdt_get_property(fdt, offset, "bootargs", &length);
|
|
|
|
if (prop == nullptr) {
|
|
printf("Failed to find chosen/bootargs prop\n");
|
|
return EFI_STATUS_SUCCESS;
|
|
}
|
|
char* new_prop_data = reinterpret_cast<char*>(malloc(length));
|
|
DEFER {
|
|
free(new_prop_data);
|
|
new_prop_data = nullptr;
|
|
};
|
|
auto prop_length = strnlen(prop->data, length);
|
|
static constexpr auto&& to_add =
|
|
"console=ttyAMA0 earlycon=pl011,mmio32,0x9000000 ";
|
|
memset(new_prop_data, 0, length);
|
|
memcpy(new_prop_data, to_add, sizeof(to_add) - 1);
|
|
memcpy(new_prop_data + sizeof(to_add) - 1, prop->data, prop_length);
|
|
auto ret = fdt_setprop(fdt, offset, "bootargs", new_prop_data, length);
|
|
|
|
printf("chosen/bootargs: %d %d \"%s\"\n", ret, length, new_prop_data);
|
|
|
|
return EFI_STATUS_SUCCESS;
|
|
}
|
|
|
|
// Generates fixups for the bootconfig built by GBL.
|
|
__WEAK EfiStatus fixup_bootconfig(struct GblEfiOsConfigurationProtocol* self,
|
|
const uint8_t* bootconfig, size_t size,
|
|
uint8_t* fixup, size_t* fixup_buffer_size) {
|
|
printf("%s(%p, %s, %lu, %lu)\n", __FUNCTION__, self,
|
|
reinterpret_cast<const char*>(bootconfig), size, *fixup_buffer_size);
|
|
constexpr auto&& to_add =
|
|
"\nandroidboot.fstab_suffix=cf.f2fs."
|
|
"hctr2\nandroidboot.boot_devices=4010000000.pcie";
|
|
const auto final_len = sizeof(to_add);
|
|
if (final_len > *fixup_buffer_size) {
|
|
*fixup_buffer_size = final_len;
|
|
return EFI_STATUS_OUT_OF_RESOURCES;
|
|
}
|
|
*fixup_buffer_size = final_len;
|
|
memcpy(fixup, to_add, final_len);
|
|
|
|
return EFI_STATUS_SUCCESS;
|
|
}
|
|
|
|
// Selects which device trees and overlays to use from those loaded by GBL.
|
|
__WEAK EfiStatus select_device_trees(struct GblEfiOsConfigurationProtocol* self,
|
|
GblEfiVerifiedDeviceTree* device_trees,
|
|
size_t num_device_trees) {
|
|
printf("%s(%p, %p %lu)\n", __FUNCTION__, self, device_trees,
|
|
num_device_trees);
|
|
return EFI_STATUS_UNSUPPORTED;
|
|
}
|
|
|
|
__WEAK EfiStatus exit_boot_services(EfiHandle image_handle, size_t map_key) {
|
|
printf("%s is called\n", __FUNCTION__);
|
|
return EFI_STATUS_SUCCESS;
|
|
}
|
|
|
|
__WEAK EfiStatus platform_setup_system_table(EfiSystemTable* table) {
|
|
printf("%s is called\n", __FUNCTION__);
|
|
return EFI_STATUS_SUCCESS;
|
|
}
|
|
|
|
__WEAK uint64_t get_timestamp() { return ARM64_READ_SYSREG(cntpct_el0); }
|
|
|
|
__WEAK EfiStatus get_timestamp_properties(EfiTimestampProperties* properties) {
|
|
if (properties == nullptr) {
|
|
return EFI_STATUS_INVALID_PARAMETER;
|
|
}
|
|
properties->frequency = ARM64_READ_SYSREG(cntfrq_el0) & 0xFFFFFFFF;
|
|
properties->end_value = UINT64_MAX;
|
|
return EFI_STATUS_SUCCESS;
|
|
}
|
|
|
|
__BEGIN_CDECLS
|
|
|
|
// Redeclaring these as WEAK has two effects:
|
|
// 1. Since we are also including lib/watchdog.h, the compiler would ensure
|
|
// that the declarations had matching prototypes.
|
|
// 2. If the platform did not provide these methods, then these symbols would
|
|
// resolve to NULL, and can be checked at runtime.
|
|
extern __WEAK status_t platform_watchdog_init(
|
|
lk_time_t target_timeout, lk_time_t* recommended_pet_period);
|
|
extern __WEAK void platform_watchdog_set_enabled(bool enabled);
|
|
|
|
__END_CDECLS
|
|
|
|
__WEAK EfiStatus set_watchdog_timer(size_t timeout, uint64_t watchdog_code,
|
|
size_t data_size, uint16_t* watchdog_data) {
|
|
if (platform_watchdog_init == nullptr ||
|
|
platform_watchdog_set_enabled == nullptr) {
|
|
TRACEF(
|
|
"unimplemented: platform_watchdog_init = %p "
|
|
"platform_watchdog_set_enabled = %p\n",
|
|
platform_watchdog_init, platform_watchdog_set_enabled);
|
|
return EFI_STATUS_UNSUPPORTED;
|
|
}
|
|
if (timeout != 0) {
|
|
lk_time_t ignored = 0;
|
|
status_t ret = platform_watchdog_init(timeout * 1000, &ignored);
|
|
LTRACEF("platform_watchdog_init() ret=%d\n", ret);
|
|
if (ret == ERR_INVALID_ARGS) {
|
|
return EFI_STATUS_INVALID_PARAMETER;
|
|
} else if (ret != NO_ERROR) {
|
|
return EFI_STATUS_UNSUPPORTED;
|
|
}
|
|
platform_watchdog_set_enabled(true);
|
|
LTRACEF("enabled hw watchdog\n");
|
|
} else {
|
|
platform_watchdog_set_enabled(false);
|
|
LTRACEF("disabled hw watchdog\n");
|
|
}
|
|
return EFI_STATUS_SUCCESS;
|
|
}
|
|
|
|
namespace {
|
|
|
|
const char* GetImageType(const char16_t* ImageType) {
|
|
if (memcmp(ImageType, GBL_IMAGE_TYPE_OS_LOAD,
|
|
sizeof(GBL_IMAGE_TYPE_OS_LOAD)) == 0) {
|
|
return "os_load";
|
|
} else if (memcmp(ImageType, GBL_IMAGE_TYPE_FASTBOOT,
|
|
sizeof(GBL_IMAGE_TYPE_FASTBOOT)) == 0) {
|
|
return "fastboot";
|
|
} else if (memcmp(ImageType, GBL_IMAGE_TYPE_PVMFW_DATA,
|
|
sizeof(GBL_IMAGE_TYPE_PVMFW_DATA)) == 0) {
|
|
return "pvmfw_data";
|
|
}
|
|
return "unknown";
|
|
}
|
|
template <typename T>
|
|
T clamp(T n, T lower, T upper) {
|
|
if (n < lower) {
|
|
return lower;
|
|
}
|
|
if (n > upper) {
|
|
return upper;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
struct EfiEraseBlockInterface {
|
|
EfiEraseBlockProtocol protocol;
|
|
bdev_t* dev;
|
|
};
|
|
|
|
EfiStatus erase_blocks(EfiEraseBlockProtocol* self, uint32_t media_id,
|
|
uint64_t lba, EfiEraseBlockToken* token, size_t size) {
|
|
LTRACEF("media_id=%d, lba=0x%llx, token=%p, size=0x%zx\n", media_id, lba,
|
|
token, size);
|
|
bdev_t* dev = reinterpret_cast<EfiEraseBlockInterface*>(self)->dev;
|
|
if (dev == NULL) {
|
|
LTRACEF("block dev is NULL\n");
|
|
return EFI_STATUS_NO_MEDIA;
|
|
}
|
|
if (token->event != nullptr) {
|
|
// TODO: Implement async IO
|
|
LTRACEF("async erase not supported, token->event=%p\n", token->event);
|
|
return EFI_STATUS_DEVICE_ERROR;
|
|
}
|
|
|
|
if (lba >= dev->block_count) {
|
|
LTRACEF("OOB erase lba=%llu block_count=%u\n", lba, dev->block_count);
|
|
return EFI_STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ssize_t ret = bio_erase(dev, lba * dev->block_size, size);
|
|
if (ret < 0) {
|
|
LTRACEF("bio_erase failed ret=%zd\n", ret);
|
|
return EFI_STATUS_DEVICE_ERROR;
|
|
}
|
|
return EFI_STATUS_SUCCESS;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
__WEAK EfiStatus get_buffer(struct GblEfiImageLoadingProtocol* self,
|
|
const GblEfiImageInfo* ImageInfo,
|
|
GblEfiImageBuffer* Buffer) {
|
|
printf("%s(%s, %lu)\n", __FUNCTION__, GetImageType(ImageInfo->ImageType),
|
|
ImageInfo->SizeBytes);
|
|
|
|
// Allow maximum of 128MB buffer
|
|
const size_t buffer_size =
|
|
clamp(Buffer->SizeBytes, PAGE_SIZE, 128ul * 1024 * 1024);
|
|
// Bottom line, kernel, ramdisk, device tree must be identity mapped.
|
|
// Otherwise linux kernel would crash immediately after entering.
|
|
// Other buffers can be allocated from kernel address space, up to
|
|
// OEM for customization.
|
|
Buffer->Memory = alloc_page(buffer_size);
|
|
if (Buffer->Memory == nullptr) {
|
|
return EFI_STATUS_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Buffer->SizeBytes = buffer_size;
|
|
return EFI_STATUS_SUCCESS;
|
|
}
|
|
|
|
__WEAK
|
|
EfiStatus open_efi_erase_block_protocol(const EfiHandle handle, void** intf) {
|
|
auto* device_name = static_cast<const char*>(handle);
|
|
LTRACEF("handle=%p (%s)\n", handle, device_name);
|
|
auto* p = reinterpret_cast<EfiEraseBlockInterface*>(
|
|
uefi_malloc(sizeof(EfiEraseBlockInterface)));
|
|
if (p == nullptr) {
|
|
return EFI_STATUS_OUT_OF_RESOURCES;
|
|
}
|
|
memset(p, 0, sizeof(*p));
|
|
p->dev = bio_open(device_name);
|
|
p->protocol = {
|
|
.revision = EFI_ERASE_BLOCK_PROTOCOL_REVISION,
|
|
.erase_length_granularity = 1, // Erase block size == 1 filesystem block
|
|
.erase_blocks = erase_blocks,
|
|
};
|
|
*intf = p;
|
|
return EFI_STATUS_SUCCESS;
|
|
}
|