1e2e1558aSJessica Hamilton /*
2bc8cfa3aSFredrik Holmqvist * Copyright 2016-2020 Haiku, Inc. All rights reserved.
3e2e1558aSJessica Hamilton * Distributed under the terms of the MIT License.
4e2e1558aSJessica Hamilton */
5e2e1558aSJessica Hamilton
6e2e1558aSJessica Hamilton
72da1cb75SJessica Hamilton #include <boot/partitions.h>
8e2e1558aSJessica Hamilton #include <boot/platform.h>
9e2e1558aSJessica Hamilton #include <boot/stage2.h>
106b4cbec0SJessica Hamilton
1162f80a2aSJessica Hamilton #include "Header.h"
1262f80a2aSJessica Hamilton
132da1cb75SJessica Hamilton #include "efi_platform.h"
14485b5cf8SAlexander von Gluck IV #include <efi/protocol/block-io.h>
15485b5cf8SAlexander von Gluck IV
1662f80a2aSJessica Hamilton #include "gpt.h"
1762f80a2aSJessica Hamilton #include "gpt_known_guids.h"
1862f80a2aSJessica Hamilton
1942e718f0SJessica Hamilton
20b0f530baSAlexander von Gluck IV //#define TRACE_DEVICES
21b0f530baSAlexander von Gluck IV #ifdef TRACE_DEVICES
22b0f530baSAlexander von Gluck IV # define TRACE(x...) dprintf("efi/devices: " x)
23b0f530baSAlexander von Gluck IV #else
24b0f530baSAlexander von Gluck IV # define TRACE(x...) ;
25b0f530baSAlexander von Gluck IV #endif
26b0f530baSAlexander von Gluck IV
27b0f530baSAlexander von Gluck IV
28485b5cf8SAlexander von Gluck IV static efi_guid BlockIoGUID = EFI_BLOCK_IO_PROTOCOL_GUID;
2942e718f0SJessica Hamilton
3042e718f0SJessica Hamilton
312da1cb75SJessica Hamilton class EfiDevice : public Node
322da1cb75SJessica Hamilton {
332da1cb75SJessica Hamilton public:
34bc8cfa3aSFredrik Holmqvist EfiDevice(efi_block_io_protocol *blockIo);
352da1cb75SJessica Hamilton virtual ~EfiDevice();
362da1cb75SJessica Hamilton
372da1cb75SJessica Hamilton virtual ssize_t ReadAt(void *cookie, off_t pos, void *buffer,
382da1cb75SJessica Hamilton size_t bufferSize);
WriteAt(void * cookie,off_t pos,const void * buffer,size_t bufferSize)392da1cb75SJessica Hamilton virtual ssize_t WriteAt(void *cookie, off_t pos, const void *buffer,
402da1cb75SJessica Hamilton size_t bufferSize) { return B_UNSUPPORTED; }
Size() const411d830665SFredrik Holmqvist virtual off_t Size() const {
421d830665SFredrik Holmqvist return (fBlockIo->Media->LastBlock + 1) * BlockSize(); }
432da1cb75SJessica Hamilton
BlockSize() const441d830665SFredrik Holmqvist uint32 BlockSize() const { return fBlockIo->Media->BlockSize; }
452da1cb75SJessica Hamilton private:
46485b5cf8SAlexander von Gluck IV efi_block_io_protocol* fBlockIo;
472da1cb75SJessica Hamilton };
482da1cb75SJessica Hamilton
492da1cb75SJessica Hamilton
EfiDevice(efi_block_io_protocol * blockIo)50bc8cfa3aSFredrik Holmqvist EfiDevice::EfiDevice(efi_block_io_protocol *blockIo)
512da1cb75SJessica Hamilton :
52bc8cfa3aSFredrik Holmqvist fBlockIo(blockIo)
532da1cb75SJessica Hamilton {
542da1cb75SJessica Hamilton }
552da1cb75SJessica Hamilton
562da1cb75SJessica Hamilton
~EfiDevice()572da1cb75SJessica Hamilton EfiDevice::~EfiDevice()
582da1cb75SJessica Hamilton {
592da1cb75SJessica Hamilton }
602da1cb75SJessica Hamilton
612da1cb75SJessica Hamilton
622da1cb75SJessica Hamilton ssize_t
ReadAt(void * cookie,off_t pos,void * buffer,size_t bufferSize)632da1cb75SJessica Hamilton EfiDevice::ReadAt(void *cookie, off_t pos, void *buffer, size_t bufferSize)
642da1cb75SJessica Hamilton {
65b0f530baSAlexander von Gluck IV TRACE("%s called. pos: %" B_PRIdOFF ", %p, %" B_PRIuSIZE "\n", __func__,
66b0f530baSAlexander von Gluck IV pos, buffer, bufferSize);
67b0f530baSAlexander von Gluck IV
68bc8cfa3aSFredrik Holmqvist off_t offset = pos % BlockSize();
691d830665SFredrik Holmqvist pos /= BlockSize();
702da1cb75SJessica Hamilton
713cd3b702SAlexander von Gluck IV uint32 numBlocks = (offset + bufferSize + BlockSize() - 1) / BlockSize();
723cd3b702SAlexander von Gluck IV
733cd3b702SAlexander von Gluck IV // TODO: We really should implement memalign and align all requests to
743cd3b702SAlexander von Gluck IV // fBlockIo->Media->IoAlign. This static alignment is large enough though
753cd3b702SAlexander von Gluck IV // to catch most required alignments.
763cd3b702SAlexander von Gluck IV char readBuffer[numBlocks * BlockSize()]
773cd3b702SAlexander von Gluck IV __attribute__((aligned(2048)));
782da1cb75SJessica Hamilton
791d830665SFredrik Holmqvist if (fBlockIo->ReadBlocks(fBlockIo, fBlockIo->Media->MediaId,
803cd3b702SAlexander von Gluck IV pos, sizeof(readBuffer), readBuffer) != EFI_SUCCESS) {
813cd3b702SAlexander von Gluck IV dprintf("%s: blockIo error reading from device!\n", __func__);
822da1cb75SJessica Hamilton return B_ERROR;
833cd3b702SAlexander von Gluck IV }
842da1cb75SJessica Hamilton
852da1cb75SJessica Hamilton memcpy(buffer, readBuffer + offset, bufferSize);
862da1cb75SJessica Hamilton
872da1cb75SJessica Hamilton return bufferSize;
882da1cb75SJessica Hamilton }
892da1cb75SJessica Hamilton
902da1cb75SJessica Hamilton
912da1cb75SJessica Hamilton static off_t
get_next_check_sum_offset(int32 index,off_t maxSize)922da1cb75SJessica Hamilton get_next_check_sum_offset(int32 index, off_t maxSize)
932da1cb75SJessica Hamilton {
94b0f530baSAlexander von Gluck IV TRACE("%s: called\n", __func__);
95b0f530baSAlexander von Gluck IV
962da1cb75SJessica Hamilton if (index < 2)
972da1cb75SJessica Hamilton return index * 512;
982da1cb75SJessica Hamilton
992da1cb75SJessica Hamilton if (index < 4)
1002da1cb75SJessica Hamilton return (maxSize >> 10) + index * 2048;
1012da1cb75SJessica Hamilton
1021d830665SFredrik Holmqvist return ((system_time() + index) % (maxSize >> 9)) * 512;
1032da1cb75SJessica Hamilton }
1042da1cb75SJessica Hamilton
1052da1cb75SJessica Hamilton
1062da1cb75SJessica Hamilton static uint32
compute_check_sum(Node * device,off_t offset)1072da1cb75SJessica Hamilton compute_check_sum(Node *device, off_t offset)
1082da1cb75SJessica Hamilton {
109b0f530baSAlexander von Gluck IV TRACE("%s: called\n", __func__);
110b0f530baSAlexander von Gluck IV
1112da1cb75SJessica Hamilton char buffer[512];
1122da1cb75SJessica Hamilton ssize_t bytesRead = device->ReadAt(NULL, offset, buffer, sizeof(buffer));
1132da1cb75SJessica Hamilton if (bytesRead < B_OK)
1142da1cb75SJessica Hamilton return 0;
1152da1cb75SJessica Hamilton
1162da1cb75SJessica Hamilton if (bytesRead < (ssize_t)sizeof(buffer))
1172da1cb75SJessica Hamilton memset(buffer + bytesRead, 0, sizeof(buffer) - bytesRead);
1182da1cb75SJessica Hamilton
1192da1cb75SJessica Hamilton uint32 *array = (uint32*)buffer;
1202da1cb75SJessica Hamilton uint32 sum = 0;
1212da1cb75SJessica Hamilton
122bc8cfa3aSFredrik Holmqvist for (uint32 i = 0; i < (bytesRead + sizeof(uint32) - 1) / sizeof(uint32); i++)
1232da1cb75SJessica Hamilton sum += array[i];
1242da1cb75SJessica Hamilton
1252da1cb75SJessica Hamilton return sum;
1262da1cb75SJessica Hamilton }
1272da1cb75SJessica Hamilton
128e2e1558aSJessica Hamilton
12962f80a2aSJessica Hamilton static bool
device_contains_partition(EfiDevice * device,boot::Partition * partition)13062f80a2aSJessica Hamilton device_contains_partition(EfiDevice *device, boot::Partition *partition)
13162f80a2aSJessica Hamilton {
13262f80a2aSJessica Hamilton EFI::Header *header = (EFI::Header*)partition->content_cookie;
13362f80a2aSJessica Hamilton if (header != NULL && header->InitCheck() == B_OK) {
13462f80a2aSJessica Hamilton // check if device is GPT, and contains partition entry
13562f80a2aSJessica Hamilton uint32 blockSize = device->BlockSize();
13662f80a2aSJessica Hamilton gpt_table_header *deviceHeader =
13762f80a2aSJessica Hamilton (gpt_table_header*)malloc(blockSize);
13862f80a2aSJessica Hamilton ssize_t bytesRead = device->ReadAt(NULL, blockSize, deviceHeader,
13962f80a2aSJessica Hamilton blockSize);
140e82b4578SDavid Karoly if (bytesRead != (ssize_t)blockSize)
14162f80a2aSJessica Hamilton return false;
14262f80a2aSJessica Hamilton
14362f80a2aSJessica Hamilton if (memcmp(deviceHeader, &header->TableHeader(),
14462f80a2aSJessica Hamilton sizeof(gpt_table_header)) != 0)
14562f80a2aSJessica Hamilton return false;
14662f80a2aSJessica Hamilton
14762f80a2aSJessica Hamilton // partition->cookie == int partition entry index
14862f80a2aSJessica Hamilton uint32 index = (uint32)(addr_t)partition->cookie;
14962f80a2aSJessica Hamilton uint32 size = sizeof(gpt_partition_entry) * (index + 1);
15062f80a2aSJessica Hamilton gpt_partition_entry *entries = (gpt_partition_entry*)malloc(size);
15162f80a2aSJessica Hamilton bytesRead = device->ReadAt(NULL,
15262f80a2aSJessica Hamilton deviceHeader->entries_block * blockSize, entries, size);
153e82b4578SDavid Karoly if (bytesRead != (ssize_t)size)
15462f80a2aSJessica Hamilton return false;
15562f80a2aSJessica Hamilton
15662f80a2aSJessica Hamilton if (memcmp(&entries[index], &header->EntryAt(index),
15762f80a2aSJessica Hamilton sizeof(gpt_partition_entry)) != 0)
15862f80a2aSJessica Hamilton return false;
15962f80a2aSJessica Hamilton
16062f80a2aSJessica Hamilton for (size_t i = 0; i < sizeof(kTypeMap) / sizeof(struct type_map); ++i)
16162f80a2aSJessica Hamilton if (strcmp(kTypeMap[i].type, BFS_NAME) == 0)
16262f80a2aSJessica Hamilton if (kTypeMap[i].guid == header->EntryAt(index).partition_type)
16362f80a2aSJessica Hamilton return true;
16462f80a2aSJessica Hamilton
16562f80a2aSJessica Hamilton // Our partition has an EFI header, but we couldn't find one, so bail
16662f80a2aSJessica Hamilton return false;
16762f80a2aSJessica Hamilton }
16862f80a2aSJessica Hamilton
16962f80a2aSJessica Hamilton if ((partition->offset + partition->size) <= device->Size())
17062f80a2aSJessica Hamilton return true;
17162f80a2aSJessica Hamilton
17262f80a2aSJessica Hamilton return false;
17362f80a2aSJessica Hamilton }
17462f80a2aSJessica Hamilton
17562f80a2aSJessica Hamilton
176e2e1558aSJessica Hamilton status_t
platform_add_boot_device(struct stage2_args * args,NodeList * devicesList)17742e718f0SJessica Hamilton platform_add_boot_device(struct stage2_args *args, NodeList *devicesList)
17842e718f0SJessica Hamilton {
179b0f530baSAlexander von Gluck IV TRACE("%s: called\n", __func__);
180b0f530baSAlexander von Gluck IV
181bc8cfa3aSFredrik Holmqvist efi_block_io_protocol *blockIo;
182bc8cfa3aSFredrik Holmqvist size_t memSize = 0;
18342e718f0SJessica Hamilton
184bc8cfa3aSFredrik Holmqvist // Read to zero sized buffer to get memory needed for handles
185bc8cfa3aSFredrik Holmqvist if (kBootServices->LocateHandle(ByProtocol, &BlockIoGUID, 0, &memSize, 0)
186bc8cfa3aSFredrik Holmqvist != EFI_BUFFER_TOO_SMALL)
187bc8cfa3aSFredrik Holmqvist panic("Cannot read size of block device handles!");
18842e718f0SJessica Hamilton
189bc8cfa3aSFredrik Holmqvist uint32 noOfHandles = memSize / sizeof(efi_handle);
190bc8cfa3aSFredrik Holmqvist
191bc8cfa3aSFredrik Holmqvist efi_handle handles[noOfHandles];
192bc8cfa3aSFredrik Holmqvist if (kBootServices->LocateHandle(ByProtocol, &BlockIoGUID, 0, &memSize,
193bc8cfa3aSFredrik Holmqvist handles) != EFI_SUCCESS)
194bc8cfa3aSFredrik Holmqvist panic("Failed to locate block devices!");
195bc8cfa3aSFredrik Holmqvist
196bc8cfa3aSFredrik Holmqvist // All block devices has one for the disk and one per partition
197bc8cfa3aSFredrik Holmqvist // There is a special case for a device with one fixed partition
198bc8cfa3aSFredrik Holmqvist // But we probably do not care about booting on that kind of device
199bc8cfa3aSFredrik Holmqvist // So find all disk block devices and let Haiku do partition scan
200bc8cfa3aSFredrik Holmqvist for (uint32 n = 0; n < noOfHandles; n++) {
201bc8cfa3aSFredrik Holmqvist if (kBootServices->HandleProtocol(handles[n], &BlockIoGUID,
202bc8cfa3aSFredrik Holmqvist (void**)&blockIo) != EFI_SUCCESS)
203bc8cfa3aSFredrik Holmqvist panic("Cannot get block device handle!");
204bc8cfa3aSFredrik Holmqvist
205b0f530baSAlexander von Gluck IV TRACE("%s: %p: present: %s, logical: %s, removeable: %s, "
206*6e28efbbSDavid Karoly "blocksize: %" PRIu32 ", lastblock: %" PRIu64 "\n",
207b0f530baSAlexander von Gluck IV __func__, blockIo,
208b0f530baSAlexander von Gluck IV blockIo->Media->MediaPresent ? "true" : "false",
209b0f530baSAlexander von Gluck IV blockIo->Media->LogicalPartition ? "true" : "false",
210b0f530baSAlexander von Gluck IV blockIo->Media->RemovableMedia ? "true" : "false",
211b0f530baSAlexander von Gluck IV blockIo->Media->BlockSize, blockIo->Media->LastBlock);
212b0f530baSAlexander von Gluck IV
213bc8cfa3aSFredrik Holmqvist if (!blockIo->Media->MediaPresent || blockIo->Media->LogicalPartition)
214bc8cfa3aSFredrik Holmqvist continue;
215bc8cfa3aSFredrik Holmqvist
216a704a197SAlexander von Gluck IV // The qemu flash device with a 256K block sizes sometime show up
217a704a197SAlexander von Gluck IV // in edk2. If flash is unconfigured, bad things happen on arm.
218a704a197SAlexander von Gluck IV // edk2 bug: https://bugzilla.tianocore.org/show_bug.cgi?id=2856
219a704a197SAlexander von Gluck IV // We're not ready for flash devices in efi, so skip anything odd.
220a704a197SAlexander von Gluck IV if (blockIo->Media->BlockSize > 8192)
221a704a197SAlexander von Gluck IV continue;
222a704a197SAlexander von Gluck IV
223bc8cfa3aSFredrik Holmqvist EfiDevice *device = new(std::nothrow)EfiDevice(blockIo);
224bc8cfa3aSFredrik Holmqvist if (device == NULL)
225bc8cfa3aSFredrik Holmqvist panic("Can't allocate memory for block devices!");
226bc8cfa3aSFredrik Holmqvist devicesList->Insert(device);
22742e718f0SJessica Hamilton }
228bc8cfa3aSFredrik Holmqvist return devicesList->Count() > 0 ? B_OK : B_ENTRY_NOT_FOUND;
22942e718f0SJessica Hamilton }
23042e718f0SJessica Hamilton
23162f80a2aSJessica Hamilton
23242e718f0SJessica Hamilton status_t
platform_add_block_devices(struct stage2_args * args,NodeList * devicesList)233e2e1558aSJessica Hamilton platform_add_block_devices(struct stage2_args *args, NodeList *devicesList)
234e2e1558aSJessica Hamilton {
235b0f530baSAlexander von Gluck IV TRACE("%s: called\n", __func__);
236b0f530baSAlexander von Gluck IV
237bc8cfa3aSFredrik Holmqvist //TODO: Currently we add all in platform_add_boot_device
2382123a7ccSJessica Hamilton return devicesList->Count() > 0 ? B_OK : B_ENTRY_NOT_FOUND;
239e2e1558aSJessica Hamilton }
240e2e1558aSJessica Hamilton
24162f80a2aSJessica Hamilton
242e2e1558aSJessica Hamilton status_t
platform_get_boot_partitions(struct stage2_args * args,Node * bootDevice,NodeList * partitions,NodeList * bootPartitions)24362f80a2aSJessica Hamilton platform_get_boot_partitions(struct stage2_args *args, Node *bootDevice,
24462f80a2aSJessica Hamilton NodeList *partitions, NodeList *bootPartitions)
245e2e1558aSJessica Hamilton {
24662f80a2aSJessica Hamilton NodeIterator iterator = partitions->GetIterator();
24762f80a2aSJessica Hamilton boot::Partition *partition = NULL;
24862f80a2aSJessica Hamilton while ((partition = (boot::Partition*)iterator.Next()) != NULL) {
24962f80a2aSJessica Hamilton if (device_contains_partition((EfiDevice*)bootDevice, partition)) {
25062f80a2aSJessica Hamilton bootPartitions->Insert(partition);
25162f80a2aSJessica Hamilton }
25262f80a2aSJessica Hamilton }
25362f80a2aSJessica Hamilton
25462f80a2aSJessica Hamilton return bootPartitions->Count() > 0 ? B_OK : B_ENTRY_NOT_FOUND;
255e2e1558aSJessica Hamilton }
256e2e1558aSJessica Hamilton
257e2e1558aSJessica Hamilton
258e2e1558aSJessica Hamilton status_t
platform_register_boot_device(Node * device)259e2e1558aSJessica Hamilton platform_register_boot_device(Node *device)
260e2e1558aSJessica Hamilton {
261b0f530baSAlexander von Gluck IV TRACE("%s: called\n", __func__);
262b0f530baSAlexander von Gluck IV
2632da1cb75SJessica Hamilton disk_identifier identifier;
2642da1cb75SJessica Hamilton
2652da1cb75SJessica Hamilton identifier.bus_type = UNKNOWN_BUS;
2662da1cb75SJessica Hamilton identifier.device_type = UNKNOWN_DEVICE;
2672da1cb75SJessica Hamilton identifier.device.unknown.size = device->Size();
2682da1cb75SJessica Hamilton
2692da1cb75SJessica Hamilton for (uint32 i = 0; i < NUM_DISK_CHECK_SUMS; ++i) {
2702da1cb75SJessica Hamilton off_t offset = get_next_check_sum_offset(i, device->Size());
2712da1cb75SJessica Hamilton identifier.device.unknown.check_sums[i].offset = offset;
272bc8cfa3aSFredrik Holmqvist identifier.device.unknown.check_sums[i].sum = compute_check_sum(device,
273bc8cfa3aSFredrik Holmqvist offset);
2742da1cb75SJessica Hamilton }
2752da1cb75SJessica Hamilton
276552e0ac0SFredrik Holmqvist // ...HARD_DISK, as we pick partition and have checksum (no need to use _CD)
277552e0ac0SFredrik Holmqvist gBootVolume.SetInt32(BOOT_METHOD, BOOT_METHOD_HARD_DISK);
2782da1cb75SJessica Hamilton gBootVolume.SetData(BOOT_VOLUME_DISK_IDENTIFIER, B_RAW_TYPE,
2792da1cb75SJessica Hamilton &identifier, sizeof(disk_identifier));
2802da1cb75SJessica Hamilton
2812da1cb75SJessica Hamilton return B_OK;
282e2e1558aSJessica Hamilton }
283e1b41d44SAndreas Faerber
284e1b41d44SAndreas Faerber
285e1b41d44SAndreas Faerber void
platform_cleanup_devices()286e1b41d44SAndreas Faerber platform_cleanup_devices()
287e1b41d44SAndreas Faerber {
288e1b41d44SAndreas Faerber }
289