[lib][uefi] Support async blockio
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
|
||||
@@ -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",
|
||||
|
||||
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__
|
||||
@@ -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
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
|
||||
Reference in New Issue
Block a user