[lib][uefi] Merge pull request #452 from zhangxp1998/master
Support os_loading protocol and async io protocol
This commit is contained in:
139
lib/uefi/blockio2_protocols.cpp
Normal file
139
lib/uefi/blockio2_protocols.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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 "blockio2_protocols.h"
|
||||
|
||||
#include <kernel/thread.h>
|
||||
#include <kernel/vm.h>
|
||||
#include <lib/bio.h>
|
||||
#include <lk/err.h>
|
||||
#include <lk/trace.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <uefi/protocols/block_io2_protocol.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
#include "events.h"
|
||||
#include "io_stack.h"
|
||||
#include "memory_protocols.h"
|
||||
#include "switch_stack.h"
|
||||
#include "thread_utils.h"
|
||||
|
||||
#define LOCAL_TRACE 0
|
||||
|
||||
namespace {
|
||||
|
||||
struct EfiBlockIo2Interface {
|
||||
EfiBlockIo2Protocol protocol;
|
||||
EfiBlockIoMedia media;
|
||||
void* dev;
|
||||
};
|
||||
|
||||
EfiStatus reset(EfiBlockIo2Protocol* self, bool extended_verification) {
|
||||
return UNSUPPORTED;
|
||||
}
|
||||
|
||||
// Read from dev, after I/O completes, signal token->event and set
|
||||
// token->transaction_status
|
||||
EfiStatus read_blocks_async(bdev_t* dev, uint64_t lba, EfiBlockIo2Token* token,
|
||||
size_t buffer_size, void* buffer) {
|
||||
if (lba >= dev->block_count) {
|
||||
printf("OOB async read %s %llu %u\n", dev->name, lba, dev->block_count);
|
||||
return END_OF_MEDIA;
|
||||
}
|
||||
if (token == nullptr) {
|
||||
printf("Invalid token %p\n", token);
|
||||
return INVALID_PARAMETER;
|
||||
}
|
||||
// First draft of this API will just use a background thread.
|
||||
// More efficient version can be implemented once LK's bio layer
|
||||
// supports async IO
|
||||
auto thread = thread_create_functor(
|
||||
"async_bio",
|
||||
[dev, lba, buffer_size, buffer, token]() {
|
||||
auto aspace = set_boot_aspace();
|
||||
vmm_set_active_aspace(aspace);
|
||||
auto bytes_read =
|
||||
bio_read_block(dev, buffer, lba, buffer_size / dev->block_size);
|
||||
if (static_cast<size_t>(bytes_read) != buffer_size) {
|
||||
token->transaction_status = DEVICE_ERROR;
|
||||
} else {
|
||||
token->transaction_status = SUCCESS;
|
||||
}
|
||||
signal_event(token->event);
|
||||
return 0;
|
||||
},
|
||||
get_current_thread()->priority, kIoStackSize);
|
||||
if (thread == nullptr) {
|
||||
printf("Failed to create thread for IO read\n");
|
||||
return DEVICE_ERROR;
|
||||
}
|
||||
|
||||
auto err = thread_detach_and_resume(thread);
|
||||
if (err != NO_ERROR) {
|
||||
printf("Failed to resume thread for IO read %d\n", err);
|
||||
return DEVICE_ERROR;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
EfiStatus read_blocks_trampoline(EfiBlockIo2Protocol* self, uint32_t media_id,
|
||||
uint64_t lba, EfiBlockIo2Token* token,
|
||||
size_t buffer_size, void* buffer) {
|
||||
auto interface = reinterpret_cast<EfiBlockIo2Interface*>(self);
|
||||
auto dev = reinterpret_cast<bdev_t*>(interface->dev);
|
||||
void* io_stack = reinterpret_cast<char*>(get_io_stack()) + kIoStackSize;
|
||||
auto ret = call_with_stack(io_stack, read_blocks_async, dev, lba, token,
|
||||
buffer_size, buffer);
|
||||
return static_cast<EfiStatus>(ret);
|
||||
}
|
||||
|
||||
EfiStatus write_blocks_ex(EfiBlockIo2Protocol* self, uint32_t media_id,
|
||||
uint64_t lba, EfiBlockIo2Token* token,
|
||||
size_t buffer_size, const void* buffer) {
|
||||
printf(
|
||||
"Writing blocks from UEFI app is currently not supported to protect the "
|
||||
"device.\n");
|
||||
return UNSUPPORTED;
|
||||
}
|
||||
|
||||
EfiStatus flush_blocks_ex(EfiBlockIo2Protocol* self, EfiBlockIo2Token* token) {
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EfiStatus open_async_block_device(EfiHandle handle, void** intf) {
|
||||
auto dev = bio_open(reinterpret_cast<const char*>(handle));
|
||||
printf("%s(%s)\n", __FUNCTION__, dev->name);
|
||||
auto interface = reinterpret_cast<EfiBlockIo2Interface*>(
|
||||
uefi_malloc(sizeof(EfiBlockIo2Interface)));
|
||||
auto protocol = &interface->protocol;
|
||||
auto media = &interface->media;
|
||||
protocol->media = media;
|
||||
protocol->reset = reset;
|
||||
protocol->read_blocks_ex = read_blocks_trampoline;
|
||||
protocol->write_blocks_ex = write_blocks_ex;
|
||||
protocol->flush_blocks_ex = flush_blocks_ex;
|
||||
media->block_size = dev->block_size;
|
||||
media->io_align = media->block_size;
|
||||
media->last_block = dev->block_count - 1;
|
||||
interface->dev = dev;
|
||||
*intf = interface;
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
25
lib/uefi/blockio2_protocols.h
Normal file
25
lib/uefi/blockio2_protocols.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LIB_UEFI_BLOCKIO2_PROTOCOL_H_
|
||||
#define __LIB_UEFI_BLOCKIO2_PROTOCOL_H_
|
||||
|
||||
#include <uefi/types.h>
|
||||
|
||||
EfiStatus open_async_block_device(EfiHandle handle, void **intf);
|
||||
|
||||
#endif
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <uefi/protocols/block_io_protocol.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
#include "io_stack.h"
|
||||
#include "memory_protocols.h"
|
||||
#include "switch_stack.h"
|
||||
|
||||
@@ -32,9 +33,13 @@ EfiStatus read_blocks(EfiBlockIoProtocol *self, uint32_t media_id, uint64_t lba,
|
||||
auto interface = reinterpret_cast<EfiBlockIoInterface *>(self);
|
||||
auto dev = reinterpret_cast<bdev_t *>(interface->dev);
|
||||
if (lba >= dev->block_count) {
|
||||
printf("OOB read %llu %u\n", lba, dev->block_count);
|
||||
printf("OOB read %s %llu %u\n", dev->name, lba, dev->block_count);
|
||||
return END_OF_MEDIA;
|
||||
}
|
||||
if (interface->io_stack == nullptr) {
|
||||
printf("No IO stack allocted.\n");
|
||||
return OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
const size_t bytes_read =
|
||||
call_with_stack(interface->io_stack, bio_read_block, dev, buffer, lba,
|
||||
@@ -64,15 +69,16 @@ EfiStatus reset(EfiBlockIoProtocol *self, bool extended_verification) {
|
||||
} // namespace
|
||||
|
||||
EfiStatus open_block_device(EfiHandle handle, void **intf) {
|
||||
static constexpr size_t kIoStackSize = 1024ul * 1024 * 64;
|
||||
static void *io_stack = nullptr;
|
||||
if (io_stack == nullptr) {
|
||||
vmm_alloc(vmm_get_kernel_aspace(), "uefi_io_stack", kIoStackSize, &io_stack,
|
||||
PAGE_SIZE_SHIFT, 0, 0);
|
||||
}
|
||||
printf("%s(%p)\n", __FUNCTION__, handle);
|
||||
auto io_stack = get_io_stack();
|
||||
if (io_stack == nullptr) {
|
||||
return OUT_OF_RESOURCES;
|
||||
}
|
||||
const auto interface = reinterpret_cast<EfiBlockIoInterface *>(
|
||||
uefi_malloc(sizeof(EfiBlockIoInterface)));
|
||||
if (interface == nullptr) {
|
||||
return OUT_OF_RESOURCES;
|
||||
}
|
||||
memset(interface, 0, sizeof(EfiBlockIoInterface));
|
||||
auto dev = bio_open(reinterpret_cast<const char *>(handle));
|
||||
interface->dev = dev;
|
||||
@@ -88,3 +94,22 @@ EfiStatus open_block_device(EfiHandle handle, void **intf) {
|
||||
*intf = interface;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
EfiStatus list_block_devices(size_t *num_handles, EfiHandle **buf) {
|
||||
size_t device_count = 0;
|
||||
bio_iter_devices([&device_count](bdev_t *dev) {
|
||||
device_count++;
|
||||
return true;
|
||||
});
|
||||
auto devices =
|
||||
reinterpret_cast<char **>(uefi_malloc(sizeof(char *) * device_count));
|
||||
size_t i = 0;
|
||||
bio_iter_devices([&i, devices, device_count](bdev_t *dev) {
|
||||
devices[i] = dev->name;
|
||||
i++;
|
||||
return i < device_count;
|
||||
});
|
||||
*num_handles = i;
|
||||
*buf = reinterpret_cast<EfiHandle *>(devices);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@@ -5,4 +5,6 @@
|
||||
|
||||
EfiStatus open_block_device(EfiHandle handle, void **intf);
|
||||
|
||||
EfiStatus list_block_devices(size_t *num_handles, EfiHandle **buf);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -24,15 +24,17 @@
|
||||
#include <uefi/boot_service.h>
|
||||
#include <uefi/protocols/block_io_protocol.h>
|
||||
#include <uefi/protocols/dt_fixup_protocol.h>
|
||||
#include <uefi/protocols/gbl_efi_image_loading_protocol.h>
|
||||
#include <uefi/protocols/gbl_efi_os_configuration_protocol.h>
|
||||
#include <uefi/protocols/loaded_image_protocol.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
#include "arch/defines.h"
|
||||
#include "blockio2_protocols.h"
|
||||
#include "blockio_protocols.h"
|
||||
#include "lib/bio.h"
|
||||
#include "events.h"
|
||||
#include "io_stack.h"
|
||||
#include "memory_protocols.h"
|
||||
#include "switch_stack.h"
|
||||
#include "uefi_platform.h"
|
||||
|
||||
namespace {
|
||||
@@ -173,6 +175,54 @@ EfiTpl raise_tpl(EfiTpl new_tpl) {
|
||||
return APPLICATION;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
Buffer->Memory = alloc_page(buffer_size);
|
||||
if (Buffer->Memory == nullptr) {
|
||||
return OUT_OF_RESOURCES;
|
||||
}
|
||||
|
||||
Buffer->SizeBytes = buffer_size;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
EfiStatus get_verify_partitions(struct GblEfiImageLoadingProtocol *self,
|
||||
size_t *NumberOfPartitions,
|
||||
GblEfiPartitionName *Partitions) {
|
||||
printf("%s is called\n", __FUNCTION__);
|
||||
return UNSUPPORTED;
|
||||
}
|
||||
|
||||
EfiStatus open_protocol(EfiHandle handle, const EfiGuid *protocol, void **intf,
|
||||
EfiHandle agent_handle, EfiHandle controller_handle,
|
||||
@@ -203,7 +253,7 @@ EfiStatus open_protocol(EfiHandle handle, const EfiGuid *protocol, void **intf,
|
||||
printf("%s(EFI_BLOCK_IO2_PROTOCOL_GUID, handle=%p, agent_handle=%p, "
|
||||
"controller_handle=%p, attr=0x%x)\n",
|
||||
__FUNCTION__, handle, agent_handle, controller_handle, attr);
|
||||
return UNSUPPORTED;
|
||||
return open_async_block_device(handle, intf);
|
||||
} else if (guid_eq(protocol, EFI_DT_FIXUP_PROTOCOL_GUID)) {
|
||||
printf("%s(EFI_DT_FIXUP_PROTOCOL_GUID, handle=%p, agent_handle=%p, "
|
||||
"controller_handle=%p, attr=0x%x)\n",
|
||||
@@ -233,10 +283,23 @@ EfiStatus open_protocol(EfiHandle handle, const EfiGuid *protocol, void **intf,
|
||||
}
|
||||
config->revision = GBL_EFI_OS_CONFIGURATION_PROTOCOL_REVISION;
|
||||
config->fixup_bootconfig = fixup_bootconfig;
|
||||
config->fixup_kernel_commandline = fixup_kernel_commandline;
|
||||
config->select_device_trees = select_device_trees;
|
||||
*intf = reinterpret_cast<void *>(config);
|
||||
return SUCCESS;
|
||||
} else if (guid_eq(protocol, EFI_GBL_EFI_IMAGE_LOADING_PROTOCOL_GUID)) {
|
||||
printf(
|
||||
"%s(EFI_GBL_EFI_IMAGE_LOADING_PROTOCOL_GUID, handle=%p, "
|
||||
"agent_handle%p, controller_handle=%p, attr=0x%x)\n",
|
||||
__FUNCTION__, handle, agent_handle, controller_handle, attr);
|
||||
GblEfiImageLoadingProtocol *image_loading = nullptr;
|
||||
allocate_pool(BOOT_SERVICES_DATA, sizeof(*image_loading),
|
||||
reinterpret_cast<void **>(&image_loading));
|
||||
if (image_loading == nullptr) {
|
||||
return OUT_OF_RESOURCES;
|
||||
}
|
||||
image_loading->revision = GBL_EFI_IMAGE_LOADING_PROTOCOL_REVISION;
|
||||
*intf = reinterpret_cast<void *>(image_loading);
|
||||
return SUCCESS;
|
||||
}
|
||||
printf("%s is unsupported 0x%x 0x%x 0x%x 0x%llx\n", __FUNCTION__,
|
||||
protocol->data1, protocol->data2, protocol->data3,
|
||||
@@ -272,25 +335,6 @@ EfiStatus close_protocol(EfiHandle handle, const EfiGuid *protocol,
|
||||
return UNSUPPORTED;
|
||||
}
|
||||
|
||||
EfiStatus list_block_devices(size_t *num_handles, EfiHandle **buf) {
|
||||
size_t device_count = 0;
|
||||
bio_iter_devices([&device_count](bdev_t *dev) {
|
||||
device_count++;
|
||||
return true;
|
||||
});
|
||||
auto devices =
|
||||
reinterpret_cast<char **>(uefi_malloc(sizeof(char *) * device_count));
|
||||
size_t i = 0;
|
||||
bio_iter_devices([&i, devices, device_count](bdev_t *dev) {
|
||||
devices[i] = dev->name;
|
||||
i++;
|
||||
return i < device_count;
|
||||
});
|
||||
*num_handles = i;
|
||||
*buf = reinterpret_cast<EfiHandle *>(devices);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
EfiStatus locate_handle_buffer(EfiLocateHandleSearchType search_type,
|
||||
const EfiGuid *protocol, void *search_key,
|
||||
size_t *num_handles, EfiHandle **buf) {
|
||||
@@ -333,16 +377,6 @@ EfiStatus locate_handle_buffer(EfiLocateHandleSearchType search_type,
|
||||
return UNSUPPORTED;
|
||||
}
|
||||
|
||||
EfiStatus wait_for_event(size_t num_events, EfiEvent *event, size_t *index) {
|
||||
printf("%s is unsupported\n", __FUNCTION__);
|
||||
return UNSUPPORTED;
|
||||
}
|
||||
|
||||
EfiStatus signal_event(EfiEvent event) {
|
||||
printf("%s is unsupported\n", __FUNCTION__);
|
||||
return UNSUPPORTED;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void setup_boot_service_table(EfiBootService *service) {
|
||||
@@ -368,6 +402,10 @@ void setup_boot_service_table(EfiBootService *service) {
|
||||
service->open_protocol = open_protocol;
|
||||
service->locate_handle_buffer = locate_handle_buffer;
|
||||
service->close_protocol = close_protocol;
|
||||
service->wait_for_event = wait_for_event;
|
||||
service->signal_event = signal_event;
|
||||
service->wait_for_event =
|
||||
switch_stack_wrapper<size_t, EfiEvent *, size_t *, wait_for_event>();
|
||||
service->signal_event = switch_stack_wrapper<EfiEvent, signal_event>();
|
||||
service->check_event = switch_stack_wrapper<EfiEvent, check_event>();
|
||||
service->create_event = create_event;
|
||||
service->close_event = close_event;
|
||||
}
|
||||
|
||||
@@ -80,6 +80,12 @@ static constexpr auto EFI_GBL_OS_CONFIGURATION_PROTOCOL_GUID =
|
||||
0x42ff,
|
||||
{0x85, 0xac, 0xe3, 0xad, 0x6e, 0xfb, 0x46, 0x19}};
|
||||
|
||||
static constexpr auto EFI_GBL_EFI_IMAGE_LOADING_PROTOCOL_GUID =
|
||||
EfiGuid{0xdb84b4fa,
|
||||
0x53bd,
|
||||
0x4436,
|
||||
{0x98, 0xa7, 0x4e, 0x02, 0x71, 0x42, 0x8b, 0xa8}};
|
||||
|
||||
static constexpr auto EFI_DT_FIXUP_PROTOCOL_GUID =
|
||||
EfiGuid{0xe617d64c,
|
||||
0xfe08,
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
|
||||
#include "configuration_table.h"
|
||||
|
||||
#include <libfdt.h>
|
||||
#include <string.h>
|
||||
#include <uefi/system_table.h>
|
||||
|
||||
#include "libfdt.h"
|
||||
#include "memory_protocols.h"
|
||||
#include "platform.h"
|
||||
|
||||
|
||||
205
lib/uefi/events.cpp
Normal file
205
lib/uefi/events.cpp
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* 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 "events.h"
|
||||
|
||||
#include <kernel/event.h>
|
||||
#include <kernel/mutex.h>
|
||||
#include <kernel/thread.h>
|
||||
#include <lk/err.h>
|
||||
#include <lk/trace.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
#define LOCAL_TRACE 0
|
||||
|
||||
namespace {
|
||||
constexpr size_t kMaxEventCount = 16;
|
||||
struct {
|
||||
public:
|
||||
template <typename Func>
|
||||
void read_access(Func f) const {
|
||||
AutoLock a(&m);
|
||||
f(arr, events);
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
void write_access(Func f) {
|
||||
AutoLock a(&m);
|
||||
f(arr, events);
|
||||
}
|
||||
size_t get_events_count() const { return events; }
|
||||
|
||||
private:
|
||||
// Cache up to 16 completed events
|
||||
mutable Mutex m;
|
||||
EfiEventImpl *volatile arr[kMaxEventCount];
|
||||
volatile size_t events = 0;
|
||||
} completed_events;
|
||||
|
||||
lk_time_t backoff_wait_time = 1;
|
||||
|
||||
} // namespace
|
||||
|
||||
EfiStatus wait_for_event(size_t num_events, EfiEvent *event, size_t *index) {
|
||||
LTRACEF("waiting for %zu events\n", num_events);
|
||||
for (size_t i = 0; i < num_events; i++) {
|
||||
EfiEventImpl *ev = reinterpret_cast<EfiEventImpl *>(event[i]);
|
||||
if (ev->ready()) {
|
||||
*index = i;
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
while (true) {
|
||||
for (size_t i = 0; i < num_events; i++) {
|
||||
EfiEventImpl *ev = reinterpret_cast<EfiEventImpl *>(event[i]);
|
||||
if (ev->ready()) {
|
||||
*index = i;
|
||||
return SUCCESS;
|
||||
}
|
||||
auto status = event_wait_timeout(&ev->ev, 200);
|
||||
if (status == ERR_TIMED_OUT) {
|
||||
continue;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
return NOT_READY;
|
||||
}
|
||||
|
||||
EfiStatus signal_event(EfiEvent event) {
|
||||
LTRACEF("%s(type=0x%x, ready=%d)\n", __FUNCTION__, event->type,
|
||||
event->ready());
|
||||
if (event->ready()) {
|
||||
printf("Event %p already signaled\n", event);
|
||||
return SUCCESS;
|
||||
}
|
||||
event_signal(&event->ev, true);
|
||||
if ((event->type & NOTIFY_SIGNAL) && event->notify_fn != nullptr) {
|
||||
// If this event is signaled on a different thread, defer
|
||||
// calling callbacks until the next check_event call. As UEFI apps
|
||||
// are single threaded, we don't want to call event callbacks from another
|
||||
// thread
|
||||
if (event->creator_thread != get_current_thread()) {
|
||||
LTRACEF(
|
||||
"Event %p of type 0x%x is signaled from thread %s, defer notify_fn "
|
||||
"because event is created on thread %s\n",
|
||||
event, event->type, get_current_thread()->name,
|
||||
event->creator_thread->name);
|
||||
bool success = false;
|
||||
while (!success) {
|
||||
completed_events.write_access(
|
||||
[event, &success](auto &&events, auto &i) {
|
||||
if (i >= kMaxEventCount) {
|
||||
return;
|
||||
}
|
||||
events[i++] = event;
|
||||
success = true;
|
||||
});
|
||||
if (!success) {
|
||||
thread_yield();
|
||||
}
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
event->notify_fn(event, event->notify_ctx);
|
||||
event->callback_called = true;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
EfiStatus create_event(EfiEventType type, EfiTpl notify_tpl,
|
||||
EfiEventNotify notify_fn, void *notify_ctx,
|
||||
EfiEvent *event) {
|
||||
if ((type & TIMER) != 0) {
|
||||
printf("Creating timer event is not supported yet\n");
|
||||
return UNSUPPORTED;
|
||||
}
|
||||
if ((type & SIGNAL_EXIT_BOOT_SERVICES) == SIGNAL_EXIT_BOOT_SERVICES ||
|
||||
(type & SIGNAL_VIRTUAL_ADDRESS_CHANGE) == SIGNAL_VIRTUAL_ADDRESS_CHANGE) {
|
||||
printf(
|
||||
"Creating SIGNAL_EXIT_BOOT_SERVICES or SIGNAL_VIRTUAL_ADDRESS_CHANGE "
|
||||
"event is not supported yet 0x%x\n",
|
||||
type);
|
||||
return UNSUPPORTED;
|
||||
}
|
||||
if ((type & NOTIFY_WAIT)) {
|
||||
printf("Creating NOTIFY_WAIT event is not supported yet\n");
|
||||
}
|
||||
auto ev = reinterpret_cast<EfiEventImpl *>(malloc(sizeof(EfiEventImpl)));
|
||||
memset(ev, 0, sizeof(EfiEventImpl));
|
||||
ev->type = type;
|
||||
event_init(&ev->ev, false, 0);
|
||||
ev->notify_ctx = notify_ctx;
|
||||
ev->notify_fn = notify_fn;
|
||||
ev->creator_thread = get_current_thread();
|
||||
LTRACEF("Created event 0x%x callback %p %p on thread %s\n", type, notify_fn,
|
||||
notify_ctx, get_current_thread()->name);
|
||||
*event = ev;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
EfiStatus check_event(EfiEvent event) {
|
||||
while (completed_events.get_events_count() > 0) {
|
||||
completed_events.write_access([](auto &&events, auto &count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
EfiEventImpl *ev = events[i];
|
||||
if (!ev->callback_called) {
|
||||
// reset exponential backoff timer
|
||||
LTRACEF("Triggering event %p callback %p %p on thread %s\n", ev,
|
||||
ev->notify_fn, ev->notify_ctx, get_current_thread()->name);
|
||||
backoff_wait_time = 0;
|
||||
ev->notify_fn(ev, ev->notify_ctx);
|
||||
ev->callback_called = true;
|
||||
events[i] = nullptr;
|
||||
} else {
|
||||
printf("Event %p with callback %p %p already called, unusual\n", ev,
|
||||
ev->notify_fn, ev->notify_ctx);
|
||||
}
|
||||
}
|
||||
count = 0;
|
||||
});
|
||||
}
|
||||
// Some UEFI applications repeadtely call check_event(NULL) as a way to poll
|
||||
// for completed events. To avoid busy waiting, implement an exponential
|
||||
// backoff strategy if check_event is called repeatdely AND no events in the
|
||||
// system are completed.
|
||||
if (event == nullptr) {
|
||||
if (backoff_wait_time == 0) {
|
||||
thread_yield();
|
||||
backoff_wait_time++;
|
||||
} else {
|
||||
thread_sleep(backoff_wait_time);
|
||||
if (backoff_wait_time < 500) {
|
||||
backoff_wait_time <<= 1;
|
||||
}
|
||||
}
|
||||
return INVALID_PARAMETER;
|
||||
}
|
||||
if (event->ready()) {
|
||||
return SUCCESS;
|
||||
}
|
||||
return NOT_READY;
|
||||
}
|
||||
|
||||
EfiStatus close_event(EfiEvent event) {
|
||||
event_destroy(&event->ev);
|
||||
free(event);
|
||||
return SUCCESS;
|
||||
}
|
||||
46
lib/uefi/events.h
Normal file
46
lib/uefi/events.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LIB_UEFI_EVENTS_H_
|
||||
#define __LIB_UEFI_EVENTS_H_
|
||||
#include <kernel/event.h>
|
||||
#include <kernel/thread.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
struct EfiEventImpl final {
|
||||
EfiEventType type;
|
||||
event_t ev;
|
||||
EfiEventNotify notify_fn = nullptr;
|
||||
void *notify_ctx = nullptr;
|
||||
volatile bool callback_called = false;
|
||||
thread_t *creator_thread = nullptr;
|
||||
bool ready() const { return ev.signaled; }
|
||||
};
|
||||
|
||||
EfiStatus wait_for_event(size_t num_events, EfiEvent *event, size_t *index);
|
||||
|
||||
EfiStatus signal_event(EfiEvent event);
|
||||
|
||||
EfiStatus check_event(EfiEvent event);
|
||||
|
||||
EfiStatus create_event(EfiEventType type, EfiTpl notify_tpl,
|
||||
EfiEventNotify notify_fn, void *notify_ctx,
|
||||
EfiEvent *event);
|
||||
|
||||
EfiStatus close_event(EfiEvent event);
|
||||
|
||||
#endif
|
||||
@@ -84,7 +84,7 @@ typedef struct {
|
||||
size_t* desc_size, uint32_t* desc_version);
|
||||
EfiStatus (*allocate_pool)(EfiMemoryType pool_type, size_t size, void** buf);
|
||||
EfiStatus (*free_pool)(void* buf);
|
||||
EfiStatus (*create_event)(uint32_t type, EfiTpl notify_tpl,
|
||||
EfiStatus (*create_event)(EfiEventType type, EfiTpl notify_tpl,
|
||||
EfiEventNotify notify_fn, void* notify_ctx,
|
||||
EfiEvent* event);
|
||||
EfiStatus (*set_timer)(EfiEvent event, EfiTimerDelay type,
|
||||
|
||||
46
lib/uefi/include/uefi/protocols/block_io2_protocol.h
Normal file
46
lib/uefi/include/uefi/protocols/block_io2_protocol.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __BLOCK_IO2_PROTOCOL_H__
|
||||
#define __BLOCK_IO2_PROTOCOL_H__
|
||||
|
||||
#include <uefi/types.h>
|
||||
|
||||
#include "block_io_protocol.h"
|
||||
|
||||
typedef struct EfiBlockIoMedia EfiBlockIoMedia;
|
||||
typedef struct EfiBlockIo2Protocol EfiBlockIo2Protocol;
|
||||
|
||||
typedef struct {
|
||||
EfiEvent event;
|
||||
EfiStatus transaction_status;
|
||||
} EfiBlockIo2Token;
|
||||
|
||||
struct EfiBlockIo2Protocol {
|
||||
EfiBlockIoMedia* media;
|
||||
EfiStatus (*reset)(EfiBlockIo2Protocol* self, bool extended_verification);
|
||||
EfiStatus (*read_blocks_ex)(EfiBlockIo2Protocol* self, uint32_t media_id,
|
||||
uint64_t lba, EfiBlockIo2Token* token,
|
||||
size_t buffer_size, void* buffer);
|
||||
EfiStatus (*write_blocks_ex)(EfiBlockIo2Protocol* self, uint32_t media_id,
|
||||
uint64_t lba, EfiBlockIo2Token* token,
|
||||
size_t buffer_size, const void* buffer);
|
||||
EfiStatus (*flush_blocks_ex)(EfiBlockIo2Protocol* self,
|
||||
EfiBlockIo2Token* token);
|
||||
};
|
||||
|
||||
#endif //__BLOCK_IO2_PROTOCOL_H__
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __GBL_EFI_IMAGE_LOADING_PROTOCOL_H__
|
||||
#define __GBL_EFI_IMAGE_LOADING_PROTOCOL_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
static constexpr uint64_t GBL_EFI_IMAGE_LOADING_PROTOCOL_REVISION = 0x00010000;
|
||||
|
||||
#define PARTITION_NAME_LEN_U16 36
|
||||
|
||||
//******************************************************
|
||||
// GBL reserved image types
|
||||
//******************************************************
|
||||
// Buffer for loading, verifying and fixing up OS images.
|
||||
#define GBL_IMAGE_TYPE_OS_LOAD L"os_load"
|
||||
// Buffer for use as fastboot download buffer.
|
||||
#define GBL_IMAGE_TYPE_FASTBOOT L"fastboot"
|
||||
// Buffer reserved for pvmfw binary and configuration (must be 4KiB-aligned).
|
||||
#define GBL_IMAGE_TYPE_PVMFW_DATA L"pvmfw_data"
|
||||
|
||||
typedef struct GblEfiImageInfo {
|
||||
char16_t ImageType[PARTITION_NAME_LEN_U16];
|
||||
size_t SizeBytes;
|
||||
} GblEfiImageInfo;
|
||||
|
||||
typedef struct GblEfiImageBuffer {
|
||||
void* Memory;
|
||||
size_t SizeBytes;
|
||||
} GblEfiImageBuffer;
|
||||
|
||||
typedef struct GblEfiPartitionName {
|
||||
char16_t StrUtf16[PARTITION_NAME_LEN_U16];
|
||||
} GblEfiPartitionName;
|
||||
|
||||
typedef struct GblEfiImageLoadingProtocol {
|
||||
uint64_t revision;
|
||||
EfiStatus (*get_buffer)(struct GblEfiImageLoadingProtocol* self,
|
||||
const GblEfiImageInfo* ImageInfo,
|
||||
GblEfiImageBuffer* Buffer);
|
||||
EfiStatus (*get_verify_partitions)(struct GblEfiImageLoadingProtocol* self,
|
||||
size_t* NumberOfPartitions,
|
||||
GblEfiPartitionName* Partitions);
|
||||
} GblEfiImageLoadingProtocol;
|
||||
|
||||
#endif //__GBL_EFI_IMAGE_LOADING_PROTOCOL_H__
|
||||
@@ -24,24 +24,35 @@
|
||||
#include <uefi/types.h>
|
||||
|
||||
static constexpr size_t GBL_EFI_OS_CONFIGURATION_PROTOCOL_REVISION = 0x00000000;
|
||||
typedef enum GBL_EFI_DEVICE_TREE_TYPE {
|
||||
// HLOS device tree.
|
||||
DEVICE_TREE,
|
||||
// HLOS device tree overlay.
|
||||
OVERLAY,
|
||||
// pVM device assignment overlay.
|
||||
PVM_DA_OVERLAY,
|
||||
} GblEfiDeviceTreeType;
|
||||
|
||||
typedef enum GBL_EFI_DEVICE_TREE_SOURCE {
|
||||
// Device tree loaded from boot partition.
|
||||
BOOT,
|
||||
// Device tree loaded from vendor_boot partition.
|
||||
VENDOR_BOOT,
|
||||
// Device tree loaded from dtbo partition.
|
||||
DTBO,
|
||||
DTB
|
||||
// Device tree loaded from dtb partition.
|
||||
DTB,
|
||||
} GblEfiDeviceTreeSource;
|
||||
|
||||
typedef struct {
|
||||
// GblDeviceTreeSource
|
||||
uint32_t source;
|
||||
// GblDeviceTreeType
|
||||
uint32_t type;
|
||||
// Values are zeroed and must not be used in case of BOOT / VENDOR_BOOT source
|
||||
uint32_t id;
|
||||
uint32_t rev;
|
||||
uint32_t custom[4];
|
||||
// Make sure GblDeviceTreeMetadata size is 8-bytes aligned. Also reserved for
|
||||
// the future cases
|
||||
uint32_t reserved;
|
||||
} GblEfiDeviceTreeMetadata;
|
||||
|
||||
typedef struct {
|
||||
@@ -57,15 +68,10 @@ typedef struct {
|
||||
|
||||
// Warning: API is UNSTABLE
|
||||
// Documentation:
|
||||
// https://cs.android.com/android/platform/superproject/main/+/main:bootable/libbootloader/gbl/docs/gbl_os_configuration_protocol.md
|
||||
// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:bootable/libbootloader/gbl/docs/gbl_os_configuration_protocol.md
|
||||
typedef struct GblEfiOsConfigurationProtocol {
|
||||
uint64_t revision;
|
||||
|
||||
// Generates fixups for the kernel command line built by GBL.
|
||||
EfiStatus (*fixup_kernel_commandline)(
|
||||
struct GblEfiOsConfigurationProtocol *self, const char *command_line,
|
||||
char *fixup, size_t *fixup_buffer_size);
|
||||
|
||||
// Generates fixups for the bootconfig built by GBL.
|
||||
EfiStatus (*fixup_bootconfig)(struct GblEfiOsConfigurationProtocol *self,
|
||||
const char *bootconfig, size_t size,
|
||||
|
||||
@@ -35,8 +35,10 @@ using EfiGuid = struct EfiGuid {
|
||||
uint8_t data4[8];
|
||||
};
|
||||
|
||||
struct EfiEventImpl;
|
||||
|
||||
using EfiHandle = void *;
|
||||
using EfiEvent = void *;
|
||||
using EfiEvent = EfiEventImpl *;
|
||||
using EfiPhysicalAddr = uint64_t;
|
||||
using EfiVirtualAddr = uint64_t;
|
||||
|
||||
|
||||
34
lib/uefi/io_stack.cpp
Normal file
34
lib/uefi/io_stack.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 "io_stack.h"
|
||||
|
||||
#include <kernel/vm.h>
|
||||
|
||||
void *get_io_stack() {
|
||||
static void *io_stack = nullptr;
|
||||
if (io_stack == nullptr) {
|
||||
auto status = vmm_alloc(vmm_get_kernel_aspace(), "uefi_io_stack",
|
||||
kIoStackSize, &io_stack, PAGE_SIZE_SHIFT, 0, 0);
|
||||
if (io_stack == nullptr) {
|
||||
printf("Failed to allocated IO stack of size %zu error %d\n",
|
||||
kIoStackSize, status);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return io_stack;
|
||||
}
|
||||
65
lib/uefi/io_stack.h
Normal file
65
lib/uefi/io_stack.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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 <stddef.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
#include "switch_stack.h"
|
||||
|
||||
constexpr size_t kIoStackSize = 1024ul * 32;
|
||||
|
||||
void* get_io_stack();
|
||||
|
||||
template <typename Arg1, EfiStatus (*Func)(Arg1)>
|
||||
EfiStatus (*switch_stack_wrapper())(Arg1 arg) {
|
||||
auto trampoline = [](Arg1 arg) -> EfiStatus {
|
||||
void* io_stack = reinterpret_cast<char*>(get_io_stack()) + kIoStackSize;
|
||||
return static_cast<EfiStatus>(call_with_stack(io_stack, Func, arg));
|
||||
};
|
||||
return trampoline;
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2, EfiStatus (*Func)(Arg1, Arg2)>
|
||||
EfiStatus (*switch_stack_wrapper())(Arg1 arg1, Arg2 arg2) {
|
||||
auto trampoline = [](Arg1 arg1, Arg2 arg2) -> EfiStatus {
|
||||
void* io_stack = reinterpret_cast<char*>(get_io_stack()) + kIoStackSize;
|
||||
return static_cast<EfiStatus>(call_with_stack(io_stack, Func, arg1, arg2));
|
||||
};
|
||||
return trampoline;
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2, typename Arg3,
|
||||
EfiStatus (*Func)(Arg1, Arg2, Arg3)>
|
||||
EfiStatus (*switch_stack_wrapper())(Arg1 arg1, Arg2 arg2, Arg3 arg3) {
|
||||
auto trampoline = [](Arg1 arg1, Arg2 arg2, Arg3 arg3) -> EfiStatus {
|
||||
void* io_stack = reinterpret_cast<char*>(get_io_stack()) + kIoStackSize;
|
||||
return static_cast<EfiStatus>(
|
||||
call_with_stack(io_stack, Func, arg1, arg2, arg3));
|
||||
};
|
||||
return trampoline;
|
||||
}
|
||||
|
||||
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4,
|
||||
EfiStatus (*Func)(Arg1, Arg2, Arg3, Arg4)>
|
||||
EfiStatus (*switch_stack_wrapper())(Arg1, Arg2, Arg3, Arg4) {
|
||||
auto trampoline = [](Arg1 arg1, Arg2 arg2, Arg3 arg3,
|
||||
Arg4 arg4) -> EfiStatus {
|
||||
void* io_stack = reinterpret_cast<char*>(get_io_stack()) + kIoStackSize;
|
||||
return static_cast<EfiStatus>(
|
||||
call_with_stack(io_stack, Func, arg1, arg2, arg3, arg4));
|
||||
};
|
||||
return trampoline;
|
||||
}
|
||||
@@ -17,17 +17,22 @@
|
||||
|
||||
#include "memory_protocols.h"
|
||||
|
||||
#include <arch/defines.h>
|
||||
#include <arch/mmu.h>
|
||||
#include <kernel/vm.h>
|
||||
#include <lib/dlmalloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <uefi/boot_service.h>
|
||||
#include <uefi/types.h>
|
||||
|
||||
static vmm_aspace_t *old_aspace = nullptr;
|
||||
// MACRO list_for_every_entry cast between int/ptr
|
||||
// NOLINTBEGIN(performance-no-int-to-ptr)
|
||||
|
||||
namespace {
|
||||
constexpr size_t kHeapSize = 256ul * 1024 * 1024;
|
||||
vmm_aspace_t *old_aspace = nullptr;
|
||||
constexpr size_t kHeapSize = 300ul * 1024 * 1024;
|
||||
|
||||
void *get_heap() {
|
||||
static auto heap = alloc_page(kHeapSize);
|
||||
@@ -44,6 +49,9 @@ mspace get_mspace() {
|
||||
static auto space = create_mspace_with_base_limit(get_heap(), kHeapSize, 1);
|
||||
return space;
|
||||
}
|
||||
|
||||
void restore_aspace() { vmm_set_active_aspace(old_aspace); }
|
||||
|
||||
} // namespace
|
||||
|
||||
vmm_aspace_t *set_boot_aspace() {
|
||||
@@ -59,63 +67,54 @@ vmm_aspace_t *set_boot_aspace() {
|
||||
return aspace;
|
||||
}
|
||||
|
||||
void restore_aspace() { vmm_set_active_aspace(old_aspace); }
|
||||
|
||||
void *identity_map(void *addr, size_t size) {
|
||||
size = ROUNDUP(size, PAGE_SIZE);
|
||||
auto vaddr = reinterpret_cast<vaddr_t>(addr);
|
||||
paddr_t pa{};
|
||||
uint flags{};
|
||||
auto aspace = set_boot_aspace();
|
||||
auto err = arch_mmu_query(&aspace->arch_aspace, vaddr, &pa, &flags);
|
||||
if (err) {
|
||||
printf("Failed to query physical address for memory 0x%p\n", addr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
err = arch_mmu_unmap(&aspace->arch_aspace, vaddr, size / PAGE_SIZE);
|
||||
if (err) {
|
||||
printf("Failed to unmap virtual address 0x%lx\n", vaddr);
|
||||
return nullptr;
|
||||
}
|
||||
arch_mmu_map(&aspace->arch_aspace, pa, pa, size / PAGE_SIZE, flags);
|
||||
if (err) {
|
||||
printf("Failed to identity map physical address 0x%lx\n", pa);
|
||||
return nullptr;
|
||||
}
|
||||
printf("Identity mapped physical address 0x%lx size %zu flags 0x%x\n", pa,
|
||||
size, flags);
|
||||
|
||||
return reinterpret_cast<void *>(pa);
|
||||
}
|
||||
|
||||
void *alloc_page(size_t size, size_t align_log2) {
|
||||
size = ROUNDUP(size, PAGE_SIZE);
|
||||
auto aspace = set_boot_aspace();
|
||||
void *vptr{};
|
||||
status_t err = vmm_alloc_contiguous(aspace, "uefi_program", size, &vptr,
|
||||
align_log2, 0, 0);
|
||||
if (err) {
|
||||
printf("Failed to allocate memory for uefi program %d\n", err);
|
||||
paddr_t pa{};
|
||||
size_t allocated =
|
||||
pmm_alloc_contiguous(size / PAGE_SIZE, align_log2, &pa, nullptr);
|
||||
if (allocated != size / PAGE_SIZE) {
|
||||
printf("Failed to allocate physical memory size %zu\n", size);
|
||||
return nullptr;
|
||||
}
|
||||
return identity_map(vptr, size);
|
||||
int ret = arch_mmu_map(&aspace->arch_aspace, pa, pa, size / PAGE_SIZE, 0);
|
||||
if (ret != 0) {
|
||||
printf("Failed to arch_mmu_map(0x%lx)\n", pa);
|
||||
return nullptr;
|
||||
}
|
||||
status_t err{};
|
||||
err = vmm_reserve_space(aspace, "uefi_program", size, pa);
|
||||
if (err) {
|
||||
printf("Failed to vmm_reserve_space for uefi program %d\n", err);
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<void *>(pa);
|
||||
}
|
||||
|
||||
void *alloc_page(void *addr, size_t size, size_t align_log2) {
|
||||
if (addr == nullptr) {
|
||||
return alloc_page(size, align_log2);
|
||||
}
|
||||
auto err =
|
||||
vmm_alloc_contiguous(set_boot_aspace(), "uefi_program", size, &addr,
|
||||
align_log2, VMM_FLAG_VALLOC_SPECIFIC, 0);
|
||||
if (err) {
|
||||
auto aspace = set_boot_aspace();
|
||||
size = ROUNDUP(size, PAGE_SIZE);
|
||||
struct list_node list;
|
||||
size_t allocated =
|
||||
pmm_alloc_range(reinterpret_cast<paddr_t>(addr), size, &list);
|
||||
if (allocated != size / PAGE_SIZE) {
|
||||
printf(
|
||||
"Failed to allocate memory for uefi program @ fixed address 0x%p %d , "
|
||||
"falling back to non-fixed allocation\n",
|
||||
addr, err);
|
||||
"Failed to allocate physical memory size %zu at specified address %p "
|
||||
"fallback to regular allocation\n",
|
||||
size, addr);
|
||||
return alloc_page(size, align_log2);
|
||||
}
|
||||
return identity_map(addr, size);
|
||||
status_t err{};
|
||||
err = vmm_reserve_space(aspace, "uefi_program", size,
|
||||
reinterpret_cast<vaddr_t>(addr));
|
||||
if (err) {
|
||||
printf("Failed to vmm_reserve_space for uefi program %d\n", err);
|
||||
return nullptr;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
void *uefi_malloc(size_t size) { return mspace_malloc(get_mspace(), size); }
|
||||
@@ -141,8 +140,28 @@ EfiStatus free_pool(void *mem) {
|
||||
}
|
||||
|
||||
EfiStatus free_pages(EfiPhysicalAddr memory, size_t pages) {
|
||||
printf("%s is unsupported\n", __FUNCTION__);
|
||||
return UNSUPPORTED;
|
||||
auto pa = reinterpret_cast<void *>(
|
||||
vaddr_to_paddr(reinterpret_cast<void *>(memory)));
|
||||
if (pa != reinterpret_cast<void *>(memory)) {
|
||||
printf(
|
||||
"WARN: virtual address 0x%llx is not identity mapped, physical addr: "
|
||||
"%p\n",
|
||||
memory, pa);
|
||||
}
|
||||
status_t err =
|
||||
vmm_free_region(set_boot_aspace(), static_cast<vaddr_t>(memory));
|
||||
if (err) {
|
||||
printf("%s err:%d memory [0x%llx] pages:%zu\n", __FUNCTION__, err, memory,
|
||||
pages);
|
||||
return DEVICE_ERROR;
|
||||
}
|
||||
auto pages_freed = pmm_free_kpages(pa, pages);
|
||||
if (pages_freed != pages) {
|
||||
printf("Failed to free physical pages %p %zu, only freed %zu pages\n", pa,
|
||||
pages, pages_freed);
|
||||
return DEVICE_ERROR;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
size_t get_aspace_entry_count(vmm_aspace_t *aspace) {
|
||||
@@ -255,3 +274,5 @@ EfiStatus get_memory_map(size_t *memory_map_size,
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
// NOLINTEND(performance-no-int-to-ptr)
|
||||
@@ -13,9 +13,13 @@ MODULE_SRCS += \
|
||||
$(LOCAL_DIR)/boot_service_provider.cpp \
|
||||
$(LOCAL_DIR)/memory_protocols.cpp \
|
||||
$(LOCAL_DIR)/blockio_protocols.cpp \
|
||||
$(LOCAL_DIR)/blockio2_protocols.cpp \
|
||||
$(LOCAL_DIR)/uefi_platform.cpp \
|
||||
$(LOCAL_DIR)/runtime_service_provider.cpp \
|
||||
$(LOCAL_DIR)/switch_stack.S \
|
||||
$(LOCAL_DIR)/configuration_table.cpp \
|
||||
$(LOCAL_DIR)/events.cpp \
|
||||
$(LOCAL_DIR)/io_stack.cpp \
|
||||
|
||||
|
||||
include make/module.mk
|
||||
|
||||
@@ -17,23 +17,24 @@
|
||||
|
||||
#include <lk/asm.h>
|
||||
|
||||
// int call_with_stack_asm(void *stack, int (*fp)(), int arg1, int arg2, int arg3, int arg4);
|
||||
// int call_with_stack_asm(void *stack, int (*fp)(), int arg1, int arg2, int arg3, int arg4, int arg5);
|
||||
FUNCTION(call_with_stack_asm)
|
||||
stp fp, lr, [sp, #-16]!
|
||||
mov fp, sp
|
||||
|
||||
sub x0,x0,16
|
||||
mov x7,sp
|
||||
str x7,[x0]
|
||||
mov x8,sp
|
||||
str x8,[x0]
|
||||
mov sp,x0
|
||||
mov x6,x1
|
||||
mov x7,x1
|
||||
mov x0,x2
|
||||
mov x1,x3
|
||||
mov x2,x4
|
||||
mov x3,x5
|
||||
blr x6
|
||||
ldr x7,[sp]
|
||||
mov sp,x7
|
||||
mov x4,x6
|
||||
blr x7
|
||||
ldr x8,[sp]
|
||||
mov sp,x8
|
||||
|
||||
ldp fp, lr, [sp], 16
|
||||
ret lr
|
||||
|
||||
@@ -15,30 +15,64 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __LIB_UEFI_SWITCH_STACK_H
|
||||
#define __LIB_UEFI_SWITCH_STACK_H
|
||||
|
||||
#include <lk/compiler.h>
|
||||
#include <stddef.h>
|
||||
|
||||
__BEGIN_CDECLS
|
||||
|
||||
size_t call_with_stack_asm(void *stack, const void *function, void *param1,
|
||||
void *param2, void *param3, void *param4);
|
||||
void *param2, void *param3, void *param4,
|
||||
void *param5);
|
||||
|
||||
__END_CDECLS
|
||||
|
||||
#ifdef __cplusplus
|
||||
template <typename Function, typename P1, typename P2, typename P3, typename P4>
|
||||
template <typename Function, typename P1, typename P2, typename P3, typename P4,
|
||||
typename P5>
|
||||
size_t call_with_stack(void *stack, Function &&fp, P1 &¶m1, P2 &¶m2,
|
||||
P3 &¶m3, P4 &¶m4) {
|
||||
P3 &¶m3, P4 &¶m4, P5 &¶m5) {
|
||||
return call_with_stack_asm(
|
||||
stack, reinterpret_cast<const void *>(fp),
|
||||
reinterpret_cast<void *>(param1), reinterpret_cast<void *>(param2),
|
||||
reinterpret_cast<void *>(param3), reinterpret_cast<void *>(param4));
|
||||
reinterpret_cast<void *>(param3), reinterpret_cast<void *>(param4),
|
||||
reinterpret_cast<void *>(param5));
|
||||
}
|
||||
template <typename Function, typename P1, typename P2, typename P3, typename P4>
|
||||
size_t call_with_stack(void *stack, Function &&fp, P1 &¶m1, P2 &¶m2,
|
||||
P3 &¶m3, P4 &¶m4) {
|
||||
return call_with_stack_asm(stack, reinterpret_cast<const void *>(fp),
|
||||
reinterpret_cast<void *>(param1),
|
||||
reinterpret_cast<void *>(param2),
|
||||
reinterpret_cast<void *>(param3),
|
||||
reinterpret_cast<void *>(param4), nullptr);
|
||||
}
|
||||
|
||||
template <typename Function, typename P1, typename P2, typename P3>
|
||||
size_t call_with_stack(void *stack, Function &&fp, P1 &¶m1, P2 &¶m2,
|
||||
P3 &¶m3) {
|
||||
return call_with_stack_asm(
|
||||
stack, reinterpret_cast<const void *>(fp),
|
||||
reinterpret_cast<void *>(param1), reinterpret_cast<void *>(param2),
|
||||
reinterpret_cast<void *>(param3), nullptr, nullptr);
|
||||
}
|
||||
|
||||
template <typename Function, typename P1, typename P2>
|
||||
size_t call_with_stack(void *stack, Function &&fp, P1 &¶m1, P2 &¶m2) {
|
||||
return call_with_stack_asm(
|
||||
stack, reinterpret_cast<const void *>(fp),
|
||||
reinterpret_cast<void *>(param1), reinterpret_cast<void *>(param2), 0, 0);
|
||||
return call_with_stack_asm(stack, reinterpret_cast<const void *>(fp),
|
||||
reinterpret_cast<void *>(param1),
|
||||
reinterpret_cast<void *>(param2), nullptr, nullptr,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
template <typename Function, typename P1>
|
||||
size_t call_with_stack(void *stack, Function &&fp, P1 &¶m1) {
|
||||
return call_with_stack_asm(stack, reinterpret_cast<const void *>(fp),
|
||||
reinterpret_cast<void *>(param1), nullptr, nullptr,
|
||||
nullptr, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
28
lib/uefi/thread_utils.h
Normal file
28
lib/uefi/thread_utils.h
Normal file
@@ -0,0 +1,28 @@
|
||||
|
||||
#ifndef __LIB_UEFI_THREAD_UTILS_H_
|
||||
#define __LIB_UEFI_THREAD_UTILS_H_
|
||||
|
||||
#include <kernel/thread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
template <typename Func>
|
||||
int functor_thread_trampoline(void *f) {
|
||||
auto callable = reinterpret_cast<Func *>(f);
|
||||
int ret = (*callable)();
|
||||
delete callable;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename Func>
|
||||
thread_t *thread_create_functor(const char *name, Func f, int priority,
|
||||
size_t stack_size) {
|
||||
auto heap_functor = new decltype(f)(f);
|
||||
return thread_create(name, functor_thread_trampoline<Func>, heap_functor,
|
||||
priority, stack_size);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -52,7 +52,8 @@ constexpr auto EFI_SYSTEM_TABLE_SIGNATURE =
|
||||
|
||||
using EfiEntry = int (*)(void *, struct EfiSystemTable *);
|
||||
|
||||
template <typename T> void fill(T *data, size_t skip, uint8_t begin = 0) {
|
||||
template <typename T>
|
||||
void fill(T *data, size_t skip, uint8_t begin = 0) {
|
||||
auto ptr = reinterpret_cast<char *>(data);
|
||||
for (size_t i = 0; i < sizeof(T); i++) {
|
||||
if (i < skip) {
|
||||
@@ -123,13 +124,16 @@ int load_sections_and_execute(bdev_t *dev,
|
||||
reinterpret_cast<EfiConfigurationTable *>(alloc_page(PAGE_SIZE));
|
||||
memset(table.configuration_table, 0, PAGE_SIZE);
|
||||
setup_configuration_table(&table);
|
||||
platform_setup_system_table(&table);
|
||||
auto status = platform_setup_system_table(&table);
|
||||
if (status != SUCCESS) {
|
||||
printf("platform_setup_system_table failed: %lu\n", status);
|
||||
return -static_cast<int>(status);
|
||||
}
|
||||
|
||||
constexpr size_t kStackSize = 8 * 1024ul * 1024;
|
||||
auto stack = reinterpret_cast<char *>(alloc_page(kStackSize, 23));
|
||||
memset(stack, 0, kStackSize);
|
||||
printf("Calling kernel with stack [%p, %p]\n", stack,
|
||||
stack + kStackSize - 1);
|
||||
printf("Calling kernel with stack [%p, %p]\n", stack, stack + kStackSize - 1);
|
||||
return call_with_stack(stack + kStackSize, entry, image_base, &table);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,15 +57,6 @@ __WEAK EFI_STATUS efi_dt_fixup(struct EfiDtFixupProtocol *self, void *fdt,
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
// Generates fixups for the kernel command line built by GBL.
|
||||
__WEAK EfiStatus fixup_kernel_commandline(
|
||||
struct GblEfiOsConfigurationProtocol *self, const char *command_line,
|
||||
char *fixup, size_t *fixup_buffer_size) {
|
||||
printf("%s(%p, \"%s\")\n", __FUNCTION__, self, command_line);
|
||||
*fixup_buffer_size = 0;
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
// Generates fixups for the bootconfig built by GBL.
|
||||
__WEAK EfiStatus fixup_bootconfig(struct GblEfiOsConfigurationProtocol *self,
|
||||
const char *bootconfig, size_t size,
|
||||
|
||||
@@ -29,10 +29,6 @@
|
||||
EFI_STATUS efi_dt_fixup(struct EfiDtFixupProtocol *self, void *fdt,
|
||||
size_t *buffer_size, uint32_t flags);
|
||||
|
||||
EfiStatus fixup_kernel_commandline(struct GblEfiOsConfigurationProtocol *self,
|
||||
const char *command_line, char *fixup,
|
||||
size_t *fixup_buffer_size);
|
||||
|
||||
EfiStatus fixup_bootconfig(struct GblEfiOsConfigurationProtocol *self,
|
||||
const char *bootconfig, size_t size, char *fixup,
|
||||
size_t *fixup_buffer_size);
|
||||
|
||||
Reference in New Issue
Block a user