diff --git a/src/boot/boot.c b/src/boot/boot.c index ffe7cfd1bb7..8436ae00980 100644 --- a/src/boot/boot.c +++ b/src/boot/boot.c @@ -2064,8 +2064,18 @@ static EFI_STATUS call_boot_windows_bitlocker(const BootEntry *entry, EFI_FILE * if (err != EFI_SUCCESS || block_io->Media->BlockSize < 512 || block_io->Media->BlockSize > 4096) continue; - char buf[4096]; - err = block_io->ReadBlocks(block_io, block_io->Media->MediaId, 0, sizeof(buf), buf); + #define BLOCK_IO_BUFFER_SIZE 4096 + _cleanup_pages_ Pages buf_pages = xmalloc_aligned_pages( + AllocateMaxAddress, + EfiLoaderData, + EFI_SIZE_TO_PAGES(BLOCK_IO_BUFFER_SIZE), + block_io->Media->IoAlign, + /* On 32-bit allocate below 4G boundary as we can't easily access anything above that. + * 64-bit platforms don't suffer this limitation, so we can allocate from anywhere. + * addr= */ UINTPTR_MAX); + char *buf = PHYSICAL_ADDRESS_TO_POINTER(buf_pages.addr); + + err = block_io->ReadBlocks(block_io, block_io->Media->MediaId, /* LBA= */ 0, BLOCK_IO_BUFFER_SIZE, buf); if (err != EFI_SUCCESS) continue; diff --git a/src/boot/part-discovery.c b/src/boot/part-discovery.c index 0c8e7fde04b..dc1aed0514b 100644 --- a/src/boot/part-discovery.c +++ b/src/boot/part-discovery.c @@ -77,52 +77,73 @@ static EFI_STATUS try_gpt( EFI_LBA *ret_backup_lba, /* May be changed even on error! */ HARDDRIVE_DEVICE_PATH *ret_hd) { - _cleanup_free_ EFI_PARTITION_ENTRY *entries = NULL; - GptHeader gpt; + EFI_PARTITION_ENTRY *entries; + _cleanup_pages_ Pages gpt_pages = {}; + _cleanup_pages_ Pages entries_pages = {}; + GptHeader *gpt; EFI_STATUS err; uint32_t crc32; size_t size; assert(block_io); + assert(block_io->Media); assert(ret_hd); + gpt_pages = xmalloc_aligned_pages( + AllocateMaxAddress, + EfiLoaderData, + EFI_SIZE_TO_PAGES(sizeof(GptHeader)), + block_io->Media->IoAlign, + /* On 32-bit allocate below 4G boundary as we can't easily access anything above that. + * 64-bit platforms don't suffer this limitation, so we can allocate from anywhere. + * addr= */ UINTPTR_MAX); + gpt = PHYSICAL_ADDRESS_TO_POINTER(gpt_pages.addr); + /* Read the GPT header */ err = block_io->ReadBlocks( block_io, block_io->Media->MediaId, lba, - sizeof(gpt), &gpt); + sizeof(*gpt), gpt); if (err != EFI_SUCCESS) return err; /* Indicate the location of backup LBA even if the rest of the header is corrupt. */ if (ret_backup_lba) - *ret_backup_lba = gpt.AlternateLBA; + *ret_backup_lba = gpt->AlternateLBA; - if (!verify_gpt(&gpt, lba)) + if (!verify_gpt(gpt, lba)) return EFI_NOT_FOUND; /* Now load the GPT entry table */ - size = ALIGN_TO((size_t) gpt.SizeOfPartitionEntry * (size_t) gpt.NumberOfPartitionEntries, 512); - entries = xmalloc(size); + size = ALIGN_TO((size_t) gpt->SizeOfPartitionEntry * (size_t) gpt->NumberOfPartitionEntries, 512); + entries_pages = xmalloc_aligned_pages( + AllocateMaxAddress, + EfiLoaderData, + EFI_SIZE_TO_PAGES(size), + block_io->Media->IoAlign, + /* On 32-bit allocate below 4G boundary as we can't easily access anything above that. + * 64-bit platforms don't suffer this limitation, so we can allocate from anywhere. + * addr= */ UINTPTR_MAX); + entries = PHYSICAL_ADDRESS_TO_POINTER(entries_pages.addr); err = block_io->ReadBlocks( block_io, block_io->Media->MediaId, - gpt.PartitionEntryLBA, + gpt->PartitionEntryLBA, size, entries); if (err != EFI_SUCCESS) return err; /* Calculate CRC of entries array, too */ err = BS->CalculateCrc32(entries, size, &crc32); - if (err != EFI_SUCCESS || crc32 != gpt.PartitionEntryArrayCRC32) + if (err != EFI_SUCCESS || crc32 != gpt->PartitionEntryArrayCRC32) return EFI_CRC_ERROR; /* Now we can finally look for xbootloader partitions. */ - for (size_t i = 0; i < gpt.NumberOfPartitionEntries; i++) { + for (size_t i = 0; i < gpt->NumberOfPartitionEntries; i++) { EFI_PARTITION_ENTRY *entry = - (EFI_PARTITION_ENTRY *) ((uint8_t *) entries + gpt.SizeOfPartitionEntry * i); + (EFI_PARTITION_ENTRY *) ((uint8_t *) entries + gpt->SizeOfPartitionEntry * i); if (!efi_guid_equal(&entry->PartitionTypeGUID, type)) continue; diff --git a/src/boot/util.c b/src/boot/util.c index 63bf21ae50f..4a4c4e93650 100644 --- a/src/boot/util.c +++ b/src/boot/util.c @@ -517,6 +517,51 @@ void *xmalloc(size_t size) { return p; } +Pages xmalloc_aligned_pages( + EFI_ALLOCATE_TYPE type, + EFI_MEMORY_TYPE memory_type, + size_t n_pages, + size_t alignment, + EFI_PHYSICAL_ADDRESS addr) { + + EFI_PHYSICAL_ADDRESS aligned = addr; + + /* Allow to pass block_io->Media->IoAlign to this function directly. + * alignment <= 1 means no alignment is required, in that case just + * allocate pages directly. + */ + if (alignment <= 1) + alignment = EFI_PAGE_SIZE; + + assert(ISPOWEROF2(alignment)); + + if (alignment <= EFI_PAGE_SIZE) { + assert_se(BS->AllocatePages(type, memory_type, n_pages, &aligned) == EFI_SUCCESS); + return (Pages) { + .addr = aligned, + .n_pages = n_pages, + }; + } + + size_t total_pages = n_pages + EFI_SIZE_TO_PAGES(alignment); + assert_se(BS->AllocatePages(type, memory_type, total_pages, &addr) == EFI_SUCCESS); + + aligned = ALIGN_TO(addr, alignment); + size_t unaligned_pages = EFI_SIZE_TO_PAGES(aligned - addr); + if (unaligned_pages > 0) + assert_se(BS->FreePages(addr, unaligned_pages) == EFI_SUCCESS); + + addr = aligned + n_pages * EFI_PAGE_SIZE; + unaligned_pages = total_pages - n_pages - unaligned_pages; + if (unaligned_pages > 0) + assert_se(BS->FreePages(addr, unaligned_pages) == EFI_SUCCESS); + + return (Pages) { + .addr = aligned, + .n_pages = n_pages, + }; +} + bool free_and_xstrdup16(char16_t **p, const char16_t *s) { char16_t *t; diff --git a/src/boot/util.h b/src/boot/util.h index 59f61c6c39e..2c8cc36ea58 100644 --- a/src/boot/util.h +++ b/src/boot/util.h @@ -96,6 +96,13 @@ static inline Pages xmalloc_pages( }; } +Pages xmalloc_aligned_pages( + EFI_ALLOCATE_TYPE type, + EFI_MEMORY_TYPE memory_type, + size_t n_pages, + size_t alignment, + EFI_PHYSICAL_ADDRESS addr); + static inline Pages xmalloc_initrd_pages(size_t n_pages) { /* The original native x86 boot protocol of the Linux kernel was not 64bit safe, hence we try to * allocate memory for the initrds below the 4G boundary on x86, since we don't know early enough