/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include #include #include // Where the coreboot image is expected to be located on the SD card // Only certain locations are allowed - check the AM335x technical reference // manual for more details. #define COREBOOT_IMAGE_OFFSET (128 * KiB) #define SD_BLOCK_SIZE 512 static struct am335x_mmc_host sd_host; static struct storage_media media; static size_t partial_block_read(uint8_t *dest, uint64_t block, uint32_t offset, uint32_t count) { static uint8_t overflow_block[SD_BLOCK_SIZE]; uint64_t blocks_read = storage_block_read(&media, block, 1, &overflow_block); if (blocks_read != 1) { printk(BIOS_ERR, "Expected to read 1 block but read: %llu\n", blocks_read); return 0; } assert((offset + count) <= SD_BLOCK_SIZE); int dest_index = 0; for (int overflow_index = offset; overflow_index < (offset + count); overflow_index++) dest[dest_index++] = overflow_block[overflow_index]; return count; } // This supports reads from a SD card that aren't necessarily aligned to the // sd block size static ssize_t sd_readat(const struct region_device *rdev, void *dest, size_t offset, size_t count) { uint8_t *buffer = (uint8_t *)dest; uint64_t block_start = offset / SD_BLOCK_SIZE; uint64_t block_end = (offset + count) / SD_BLOCK_SIZE; uint64_t blocks = block_end - block_start + 1; // Read the last first, which might not be aligned on a SD block uint32_t first_block_offset = offset % SD_BLOCK_SIZE; size_t first_block_to_read = MIN(SD_BLOCK_SIZE - first_block_offset, count); size_t bytes_read = partial_block_read(buffer, block_start, first_block_offset, first_block_to_read); if (blocks == 1) return bytes_read; buffer += bytes_read; if (blocks > 2) { // Read all the "whole" blocks between the start and end blocks uint64_t to_read = blocks - 2; uint64_t blocks_read = storage_block_read(&media, block_start + 1, to_read, (void *)buffer); if (blocks_read != to_read) { printk(BIOS_ERR, "Expecting to read %llu blocks but only read %llu\n", to_read, blocks_read); return blocks_read * SD_BLOCK_SIZE; } buffer += to_read * SD_BLOCK_SIZE; bytes_read += to_read * SD_BLOCK_SIZE; } // Read the last block, which might not be aligned on a SD block bytes_read += partial_block_read(buffer, block_end, 0, count - bytes_read); return bytes_read; } static const struct region_device_ops am335x_sd_ops = { .mmap = mmap_helper_rdev_mmap, .munmap = mmap_helper_rdev_munmap, .readat = sd_readat, }; extern struct omap_image_headers headers; static struct mmap_helper_region_device sd_mdev = MMAP_HELPER_DEV_INIT( &am335x_sd_ops, COREBOOT_IMAGE_OFFSET + sizeof(headers), CONFIG_ROM_SIZE, &cbfs_cache); static bool init_done = false; void boot_device_init(void) { if (init_done) return; sd_host.sd_clock_hz = 96000000; sd_host.reg = (void *)MMCHS0_BASE; am335x_mmc_init_storage(&sd_host); storage_setup_media(&media, &sd_host.sd_mmc_ctrlr); storage_display_setup(&media); init_done = true; } const struct region_device *boot_device_ro(void) { return &sd_mdev.rdev; }