Files
lk/lib/uefi/blockio_protocols.cpp
Kelvin Zhang e8cea74e9b [lib][uefi] Implement events API in UEFI
LK already has events APIs. To support events protocols in UEFI spec,
I created a wrapper object for LK events. This wrapper object keeps
additional data that are necessary to support UEFI events spec:

* Notification function, or callbacks
* Argument for the notification function
* Type of this UEFI event (which specifies when the callback should be
  called)

The events API is essential for supporting async I/O in UEFI. Intended
use case looks like:

* UEFI app creates an event object, attatch callback to this event
* UEFI app calls LK for asynchronous read/write on a block device, with
  the newly created events object
* LK executes IO operation in background, after IO finishes, signal the
  specified event object
* UEFI's callback gets executed
2025-07-16 14:00:00 -07:00

116 lines
3.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 "blockio_protocols.h"
#include <kernel/vm.h>
#include <lib/bio.h>
#include <string.h>
#include <uefi/protocols/block_io_protocol.h>
#include <uefi/types.h>
#include "io_stack.h"
#include "memory_protocols.h"
#include "switch_stack.h"
namespace {
EfiStatus read_blocks(EfiBlockIoProtocol *self, uint32_t media_id, uint64_t lba,
size_t buffer_size, void *buffer) {
auto interface = reinterpret_cast<EfiBlockIoInterface *>(self);
auto dev = reinterpret_cast<bdev_t *>(interface->dev);
if (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,
buffer_size / dev->block_size);
if (bytes_read != buffer_size) {
printf("Failed to read %ld bytes from %s\n", buffer_size, dev->name);
return DEVICE_ERROR;
}
return SUCCESS;
}
EfiStatus write_blocks(EfiBlockIoProtocol *self, uint32_t media_id,
uint64_t lba, size_t buffer_size, const void *buffer) {
printf("%s is called\n", __FUNCTION__);
return SUCCESS;
}
EfiStatus flush_blocks(EfiBlockIoProtocol *self) {
printf("%s is called\n", __FUNCTION__);
return SUCCESS;
}
EfiStatus reset(EfiBlockIoProtocol *self, bool extended_verification) {
printf("%s is called\n", __FUNCTION__);
return UNSUPPORTED;
}
} // namespace
EfiStatus open_block_device(EfiHandle handle, void **intf) {
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;
interface->protocol.reset = reset;
interface->protocol.read_blocks = read_blocks;
interface->protocol.write_blocks = write_blocks;
interface->protocol.flush_blocks = flush_blocks;
interface->protocol.media = &interface->media;
interface->media.block_size = dev->block_size;
interface->media.io_align = interface->media.block_size;
interface->media.last_block = dev->block_count - 1;
interface->io_stack = reinterpret_cast<char *>(io_stack) + kIoStackSize;
*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;
}