[lib][uefi] Support async blockio

This commit is contained in:
Kelvin Zhang
2025-07-15 15:05:18 -07:00
parent 76ce54625a
commit f7d8f58cdc
6 changed files with 241 additions and 1 deletions

View 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;
}

View 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

View File

@@ -30,6 +30,7 @@
#include <uefi/types.h>
#include "arch/defines.h"
#include "blockio2_protocols.h"
#include "blockio_protocols.h"
#include "events.h"
#include "io_stack.h"
@@ -252,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",

View 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__

View File

@@ -13,6 +13,7 @@ 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 \

28
lib/uefi/thread_utils.h Normal file
View 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