| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| # HP Sure Start | ||
|
|
||
| According to the [HP Sure Start Technical Whitepaper], HP Sure Start is a chipset | ||
| and processor independent firmware intrusion detection and automatic repair system. | ||
| It is implemented in HP notebooks since 2013, and desktops since 2015. | ||
|
|
||
| This document talks about some mechanism of HP Sure Start on some machines, and | ||
| the method to bypass it. | ||
|
|
||
| ## Laptops with SMSC MEC1322 embedded controller | ||
|
|
||
| Haswell EliteBook, ZBook and ProBook 600 series use SMSC MEC1322 embedded controller. | ||
| The EC firmware implements HP Sure Start. | ||
|
|
||
| A Haswell EliteBook has two flash chips. According to the strings in the EC firmware, | ||
| the 16MiB flash chip that stores the BIOS firmware is called the *system flash*, and | ||
| the 2MiB flash chip that stores part of the system flash content is called the | ||
| *private flash*. A Haswell ProBook 600 series laptop also uses MEC1322 and has similar | ||
| EC firmware, but the HP Sure Start functions are not enabled. | ||
|
|
||
| The private flash is connected to the EC, and is not accessible by the OS. | ||
| It contains the following: | ||
|
|
||
| - HP Sure Start policy header (starting with the string "POLI") | ||
| - A copy of the Intel Flash Descriptor | ||
| - A copy of the GbE firmware | ||
| - Machine Unique Data (MUD) | ||
| - Hashes of the IFD, GbE firmware and MUD, the hash algorithm is unknown | ||
| - A copy of the bootblock, UEFI PEI stage, and microcode | ||
|
|
||
| If the IFD of the system flash does not match the hash in the private flash, for example, | ||
| modifying the IFD with ``ifdtool -u`` or ``me_cleaner -S``, the EC will recover the IFD. | ||
|
|
||
| If the content of the private flash is lost, the EC firmware will still copy the IFD, | ||
| bootblock and PEI to the private flash. However, the IFD is not protected after that. | ||
|
|
||
| HP Sure Start also verifies bootblock, PEI, and microcode without using the private flash. | ||
| EC firmware reads them from an absolute address of the system flash chip, which is | ||
| hardcoded in the EC firmware. It looks like this verification is done with a digital | ||
| signature. If the PEI volume is modified, EC firmware will recover it using the copy | ||
| in the private flash. If the private flash has no valid copies of the PEI volume, and | ||
| the PEI volume is modified, the machine will refuse to boot with the CapsLock LED blinking. | ||
|
|
||
| ## Bypassing HP Sure Start | ||
|
|
||
| First search the mainboard for the flash chips. If there are two flash chips, | ||
| the smaller one may be the private flash. | ||
|
|
||
| For Intel boards, try to modify the IFD with ``ifdtool -u``, power on and shut down | ||
| the machine, then read the flash again. If the IFD is not modified, it is likely to | ||
| be recovered from the private flash. Find the private flash and erase it, then the IFD | ||
| can be modified. | ||
|
|
||
| To bypass the bootblock and PEI verification, we can modify the IFD to make the | ||
| BIOS region not overlap with the protected region. Since the EC firmware is usually | ||
| located at the high address of the flash chip (and in the protected region), | ||
| we can leave it untouched, and do not need to extract the EC firmware to put it in | ||
| the coreboot image. | ||
|
|
||
| [HP Sure Start Technical Whitepaper]: http://h10032.www1.hp.com/ctg/Manual/c05163901 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| # Kontron mAL10 Computer-on-Modules platform | ||
|
|
||
| The Kontron [mAL10] COMe is a credit card sized Computer-on-Modules | ||
| platform based on the Intel Atom E3900 Series, Pentium and Celeron | ||
| processors. | ||
|
|
||
| ## Technology | ||
|
|
||
| ```eval_rst | ||
| +------------------+----------------------------------+ | ||
| | COMe Type | mini pin-out type 10 | | ||
| +------------------+----------------------------------+ | ||
| | SoC | Intel Atom x5-E3940 (4 core) | | ||
| +------------------+----------------------------------+ | ||
| | GPU | Intel HD Graphics 500 | | ||
| +------------------+----------------------------------+ | ||
| | Coprocessor | Intel TXE 3.0 | | ||
| +------------------+----------------------------------+ | ||
| | RAM | 8GB DDR3L | | ||
| +------------------+----------------------------------+ | ||
| | eMMC Flash | 32GB eMMC pSLC | | ||
| +------------------+----------------------------------+ | ||
| | USB3 | x2 | | ||
| +------------------+----------------------------------+ | ||
| | USB2 | x6 | | ||
| +------------------+----------------------------------+ | ||
| | SATA | x2 | | ||
| +------------------+----------------------------------+ | ||
| | LAN | Intel I210IT, I211AT | | ||
| +------------------+----------------------------------+ | ||
| | Super IO/EC | Kontron CPLD/EC | | ||
| +------------------+----------------------------------+ | ||
| | HWM | NCT7802 | | ||
| +------------------+----------------------------------+ | ||
| ``` | ||
|
|
||
| ## Building coreboot | ||
|
|
||
| The following commands will build a working image: | ||
|
|
||
| ```bash | ||
| make distclean | ||
| make defconfig KBUILD_DEFCONFIG=configs/config.kontron_mal10 | ||
| make | ||
| ``` | ||
| ## Payloads | ||
| - SeaBIOS | ||
| - Tianocore | ||
| - Linux as payload | ||
|
|
||
| ## Flashing coreboot | ||
|
|
||
| The SPI flash can be accessed internally using [flashrom]. | ||
| The following command is used to flash BIOS region. | ||
|
|
||
| ```bash | ||
| $ flashrom -p internal --ifd -i bios -w coreboot.rom --noverify-all | ||
| ``` | ||
|
|
||
| ## Hardware Monitor | ||
|
|
||
| The Nuvoton [NCT7802Y] is a hardware monitoring IC, capable of monitor critical | ||
| system parameters including power supply voltages, fan speeds, and temperatures. | ||
| The remote inputs can be connected to CPU/GPU thermal diode or any thermal diode | ||
| sensors and thermistor. | ||
|
|
||
| - 6 temperature sensors; | ||
| - 5 voltage sensors; | ||
| - 3 fan speed sensors; | ||
| - 4 sets of temperature setting points. | ||
|
|
||
| PECI is not supported by Apollo Lake Pentium/Celeron/Atom processors and the CPU | ||
| temperature value is taken from a thermal resistor (NTC) that is placed very | ||
| close to the CPU. | ||
|
|
||
| ## Known issues | ||
|
|
||
| - Works only with Tianocore "UEFIPayload" payload edk2-stable201903-1569-g3e63a91 | ||
| Booting with the "CorebootPayload" [crashes]. | ||
| - Tianocore outputs video through an external GPU only. | ||
|
|
||
| ## Untested | ||
|
|
||
| - IGD/LVDS | ||
| - SDIO | ||
|
|
||
| ## Tested and working | ||
|
|
||
| - Kontron CPLD/EC (Serial ports, I2C port) | ||
| - NCT7802 [HWM](#Hardware Monitor) | ||
| - USB2/3 | ||
| - Gigabit Ethernet ports | ||
| - eMMC | ||
| - SATA | ||
| - PCIe ports | ||
| - IGD/DP | ||
|
|
||
| ## TODO | ||
| - Onboard audio (codec IDT 92HD73C1X5, currently disabled) | ||
| - S3 suspend/resume | ||
|
|
||
| [mAL10]: https://www.kontron.com/products/iot/iot-industry-4.0/iot-ready-boards-and-modules/com-express/com-express-mini/come-mal10-e2-.html | ||
| [W25Q128FV]: https://www.winbond.com/resource-files/w25q128fv%20rev.m%2005132016%20kms.pdf | ||
| [flashrom]: https://flashrom.org/Flashrom | ||
| [NCT7802Y]: https://www.nuvoton.com/products/cloud-computing/hardware-monitors/desktop-server-series/nct7802y/?__locale=en | ||
| [crashes]: https://pastebin.com/cpCfrPCL |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| Upcoming release - coreboot 4.14 | ||
| ================================ | ||
|
|
||
| The 4.14 release is planned for May 2021. | ||
|
|
||
| Update this document with changes that should be in the release notes. | ||
|
|
||
| * Please use Markdown. | ||
| * See the past few release notes for the general format. | ||
| * The chip and board additions and removals will be updated right | ||
| before the release, so those do not need to be added. | ||
|
|
||
| Significant changes | ||
| ------------------- | ||
|
|
||
| ### Add significant changes here |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| # Not meant for actual use, but rather to build-test individual options. | ||
| # If keeping this combination of options buildable becomes too hard in | ||
| # the future, then this config can be split into several smaller chunks. | ||
| # Exercises, among other things: | ||
| # + SMMSTORE | ||
| # + OXPCIE support | ||
| # + FSP MP init | ||
| # + EM100Pro SPI console | ||
| # + Debug options | ||
| CONFIG_VENDOR_PORTWELL=y | ||
| CONFIG_CONSOLE_POST=y | ||
| # CONFIG_CONSOLE_SERIAL is not set | ||
| CONFIG_ENABLE_BUILTIN_COM1=y | ||
| CONFIG_ONBOARD_MEM_KINGSTON=y | ||
| CONFIG_USE_INTEL_FSP_MP_INIT=y | ||
| CONFIG_SOC_INTEL_COMMON_BLOCK_SMM_TCO_ENABLE=y | ||
| CONFIG_SOC_INTEL_DEBUG_CONSENT=y | ||
| CONFIG_PCIEXP_HOTPLUG=y | ||
| CONFIG_PCIEXP_HOTPLUG_PREFETCH_MEM_BELOW_4G=y | ||
| CONFIG_SOFTWARE_I2C=y | ||
| CONFIG_SMMSTORE=y | ||
| CONFIG_SPI_FLASH_NO_FAST_READ=y | ||
| CONFIG_DRIVERS_UART_OXPCIE=y | ||
| CONFIG_DRIVERS_GENESYSLOGIC_GL9755=y | ||
| CONFIG_DISPLAY_HOBS=y | ||
| CONFIG_DISPLAY_VBT=y | ||
| CONFIG_DISPLAY_FSP_ENTRY_POINTS=y | ||
| CONFIG_DISPLAY_UPD_DATA=y | ||
| CONFIG_EM100PRO_SPI_CONSOLE=y | ||
| CONFIG_DISPLAY_MTRRS=y | ||
| CONFIG_GDB_STUB=y | ||
| CONFIG_GDB_WAIT=y | ||
| CONFIG_FATAL_ASSERTS=y | ||
| CONFIG_DEBUG_CBFS=y | ||
| CONFIG_DEBUG_SMBUS=y | ||
| CONFIG_DEBUG_SMI=y | ||
| CONFIG_DEBUG_PERIODIC_SMI=y | ||
| CONFIG_DEBUG_MALLOC=y | ||
| CONFIG_DEBUG_CONSOLE_INIT=y | ||
| CONFIG_REALMODE_DEBUG=y | ||
| CONFIG_DEBUG_BOOT_STATE=y |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| CONFIG_VENDOR_SCALEWAY=y | ||
| CONFIG_BOARD_SCALEWAY_TAGADA=y | ||
| CONFIG_CBFS_SIZE=0x400000 | ||
| CONFIG_CONSOLE_POST=y | ||
| # CONFIG_DRIVERS_INTEL_WIFI is not set | ||
| # CONFIG_IQAT_ENABLE is not set | ||
| CONFIG_LEGACY_UART_MODE=y | ||
| CONFIG_USE_DENVERTON_NS_FSP_CAR=y | ||
| CONFIG_SPI_FLASH_NO_FAST_READ=y | ||
| CONFIG_PAYLOAD_ELF=y | ||
| CONFIG_PAYLOAD_FILE="UEFIPAYLOAD.fd" | ||
| CONFIG_DISPLAY_FSP_CALLS_AND_STATUS=y | ||
| CONFIG_DISPLAY_FSP_HEADER=y | ||
| CONFIG_DEBUG_CBFS=y | ||
| CONFIG_DEBUG_BOOT_STATE=y |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| lpbuild/ | ||
| lp.config* |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| depthcharge/depthcharge/ | ||
| FILO/filo/ | ||
| GRUB2/grub2/ | ||
| LinuxBoot/linuxboot/ | ||
| SeaBIOS/seabios/ | ||
| tianocore/tianocore/ | ||
| tint/tint/ | ||
| U-Boot/u-boot/ | ||
| Memtest86Plus/memtest86plus/ | ||
| iPXE/ipxe/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,3 @@ | ||
| ## SPDX-License-Identifier: GPL-2.0-only | ||
|
|
||
| if PAYLOAD_LINUXBOOT | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,3 @@ | ||
| ## SPDX-License-Identifier: GPL-2.0-only | ||
|
|
||
| config PAYLOAD_LINUXBOOT | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,3 @@ | ||
| ## SPDX-License-Identifier: GPL-2.0-only | ||
|
|
||
| project_dir=linuxboot | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,3 @@ | ||
| ## SPDX-License-Identifier: GPL-2.0-only | ||
|
|
||
| SHELL := /bin/bash | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,3 @@ | ||
| ## SPDX-License-Identifier: GPL-2.0-only | ||
|
|
||
| project_dir=$(shell pwd)/linuxboot | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,3 @@ | ||
| ## SPDX-License-Identifier: GPL-2.0-only | ||
|
|
||
| TAG-$(CONFIG_MEMTEST_MASTER)=origin/master | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,3 @@ | ||
| ## SPDX-License-Identifier: GPL-2.0-only | ||
|
|
||
| # 2019-4 tag | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,3 @@ | ||
| ## SPDX-License-Identifier: GPL-2.0-only | ||
|
|
||
| TAG-$(CONFIG_YABITS_MASTER)=origin/master | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,3 @@ | ||
| ## SPDX-License-Identifier: GPL-2.0-only | ||
|
|
||
| config PXE | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,3 @@ | ||
| ## SPDX-License-Identifier: GPL-2.0-only | ||
|
|
||
| # 2019.3 - Last commit of March 2019 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| install/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| build | ||
| libpayload |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0-only */ | ||
|
|
||
| #include <acpi/acpi.h> | ||
| #include <console/console.h> | ||
| #include <romstage_handoff.h> | ||
| #include <smbios.h> | ||
|
|
||
| /* This is filled with acpi_handoff_wakeup_s3() call early in ramstage. */ | ||
| static int acpi_slp_type = -1; | ||
|
|
||
| static void acpi_handoff_wakeup(void) | ||
| { | ||
| if (acpi_slp_type < 0) { | ||
| if (romstage_handoff_is_resume()) { | ||
| printk(BIOS_DEBUG, "S3 Resume\n"); | ||
| acpi_slp_type = ACPI_S3; | ||
| } else { | ||
| printk(BIOS_DEBUG, "Normal boot\n"); | ||
| acpi_slp_type = ACPI_S0; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| int acpi_handoff_wakeup_s3(void) | ||
| { | ||
| acpi_handoff_wakeup(); | ||
| return (acpi_slp_type == ACPI_S3); | ||
| } | ||
|
|
||
| void __weak mainboard_suspend_resume(void) | ||
| { | ||
| } | ||
|
|
||
| /* Default mapping to ACPI FADT preferred_pm_profile field. */ | ||
| uint8_t acpi_get_preferred_pm_profile(void) | ||
| { | ||
| switch (smbios_mainboard_enclosure_type()) { | ||
| case SMBIOS_ENCLOSURE_LAPTOP: | ||
| case SMBIOS_ENCLOSURE_CONVERTIBLE: | ||
| return PM_MOBILE; | ||
| case SMBIOS_ENCLOSURE_DETACHABLE: | ||
| case SMBIOS_ENCLOSURE_TABLET: | ||
| return PM_TABLET; | ||
| case SMBIOS_ENCLOSURE_DESKTOP: | ||
| return PM_DESKTOP; | ||
| case SMBIOS_ENCLOSURE_UNKNOWN: | ||
| default: | ||
| return PM_UNSPECIFIED; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,143 @@ | ||
| /* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */ | ||
|
|
||
| #include <assert.h> | ||
| #include <commonlib/bsd/cbfs_private.h> | ||
|
|
||
| /* | ||
| * A CBFS metadata cache is an in memory data structure storing CBFS file headers (= metadata). | ||
| * It is defined by its start pointer and size. It contains a sequence of variable-length | ||
| * union mcache_entry entries. There is no overall header structure for the cache. | ||
| * | ||
| * Each mcache_entry is the raw metadata for a CBFS file (including attributes) in the same form | ||
| * as stored on flash (i.e. values in big-endian), except that the CBFS magic signature in the | ||
| * first 8 bytes ('LARCHIVE') is overwritten with mcache-internal bookkeeping data. The first 4 | ||
| * bytes are a magic number (MCACHE_MAGIC_FILE) and the next 4 bytes are the absolute offset in | ||
| * bytes on the cbfs_dev_t that this metadata blob was found at. (Note that depending on the | ||
| * implementation of cbfs_dev_t, this offset may still be relative to the start of a subregion | ||
| * of the underlying storage device.) | ||
| * | ||
| * The length of an mcache_entry (i.e. length of the underlying metadata blob) is encoded in the | ||
| * metadata (entry->file.h.offset). The next mcache_entry begins at the next | ||
| * CBFS_MCACHE_ALIGNMENT boundary after that. The cache is terminated by a special 4-byte | ||
| * mcache_entry that consists only of a magic number (MCACHE_MAGIC_END or MCACHE_MAGIC_FULL). | ||
| */ | ||
|
|
||
| #define MCACHE_MAGIC_FILE 0x454c4946 /* 'FILE' */ | ||
| #define MCACHE_MAGIC_FULL 0x4c4c5546 /* 'FULL' */ | ||
| #define MCACHE_MAGIC_END 0x444e4524 /* '$END' */ | ||
|
|
||
| union mcache_entry { | ||
| union cbfs_mdata file; | ||
| struct { /* These fields exactly overlap file.h.magic */ | ||
| uint32_t magic; | ||
| uint32_t offset; | ||
| }; | ||
| }; | ||
|
|
||
| struct cbfs_mcache_build_args { | ||
| void *mcache; | ||
| void *end; | ||
| int count; | ||
| }; | ||
|
|
||
| static cb_err_t build_walker(cbfs_dev_t dev, size_t offset, const union cbfs_mdata *mdata, | ||
| size_t already_read, void *arg) | ||
| { | ||
| struct cbfs_mcache_build_args *args = arg; | ||
| union mcache_entry *entry = args->mcache; | ||
| const uint32_t data_offset = be32toh(mdata->h.offset); | ||
|
|
||
| if (args->end - args->mcache < data_offset) | ||
| return CB_CBFS_CACHE_FULL; | ||
|
|
||
| if (cbfs_copy_fill_metadata(args->mcache, mdata, already_read, dev, offset)) | ||
| return CB_CBFS_IO; | ||
|
|
||
| entry->magic = MCACHE_MAGIC_FILE; | ||
| entry->offset = offset; | ||
|
|
||
| args->mcache += ALIGN_UP(data_offset, CBFS_MCACHE_ALIGNMENT); | ||
| args->count++; | ||
|
|
||
| return CB_CBFS_NOT_FOUND; | ||
| } | ||
|
|
||
| cb_err_t cbfs_mcache_build(cbfs_dev_t dev, void *mcache, size_t size, | ||
| struct vb2_hash *metadata_hash) | ||
| { | ||
| struct cbfs_mcache_build_args args = { | ||
| .mcache = mcache, | ||
| .end = mcache + ALIGN_DOWN(size, CBFS_MCACHE_ALIGNMENT) | ||
| - sizeof(uint32_t), /* leave space for terminating magic */ | ||
| .count = 0, | ||
| }; | ||
|
|
||
| assert(size > sizeof(uint32_t) && IS_ALIGNED((uintptr_t)mcache, CBFS_MCACHE_ALIGNMENT)); | ||
| cb_err_t ret = cbfs_walk(dev, build_walker, &args, metadata_hash, 0); | ||
| union mcache_entry *entry = args.mcache; | ||
| if (ret == CB_CBFS_NOT_FOUND) { | ||
| ret = CB_SUCCESS; | ||
| entry->magic = MCACHE_MAGIC_END; | ||
| } else if (ret == CB_CBFS_CACHE_FULL) { | ||
| ERROR("mcache overflow, should increase CBFS_MCACHE size!\n"); | ||
| entry->magic = MCACHE_MAGIC_FULL; | ||
| } | ||
|
|
||
| LOG("mcache @%p built for %d files, used %#zx of %#zx bytes\n", mcache, | ||
| args.count, args.mcache + sizeof(entry->magic) - mcache, size); | ||
| return ret; | ||
| } | ||
|
|
||
| cb_err_t cbfs_mcache_lookup(const void *mcache, size_t mcache_size, const char *name, | ||
| union cbfs_mdata *mdata_out, size_t *data_offset_out) | ||
| { | ||
| const size_t namesize = strlen(name) + 1; /* Count trailing \0 so we can memcmp() it. */ | ||
| const void *end = mcache + mcache_size; | ||
| const void *current = mcache; | ||
|
|
||
| while (current + sizeof(uint32_t) < end) { | ||
| const union mcache_entry *entry = current; | ||
|
|
||
| if (entry->magic == MCACHE_MAGIC_END) | ||
| return CB_CBFS_NOT_FOUND; | ||
| if (entry->magic == MCACHE_MAGIC_FULL) | ||
| return CB_CBFS_CACHE_FULL; | ||
|
|
||
| assert(entry->magic == MCACHE_MAGIC_FILE); | ||
| const uint32_t data_offset = be32toh(entry->file.h.offset); | ||
| const uint32_t data_length = be32toh(entry->file.h.len); | ||
| if (namesize <= data_offset - offsetof(union cbfs_mdata, filename) && | ||
| memcmp(name, entry->file.filename, namesize) == 0) { | ||
| LOG("Found '%s' @%#x size %#x in mcache @%p\n", | ||
| name, entry->offset, data_length, current); | ||
| *data_offset_out = entry->offset + data_offset; | ||
| memcpy(mdata_out, &entry->file, data_offset); | ||
| return CB_SUCCESS; | ||
| } | ||
|
|
||
| current += ALIGN_UP(data_offset, CBFS_MCACHE_ALIGNMENT); | ||
| } | ||
|
|
||
| ERROR("CBFS mcache overflow!\n"); | ||
| return CB_ERR; | ||
| } | ||
|
|
||
| size_t cbfs_mcache_real_size(const void *mcache, size_t mcache_size) | ||
| { | ||
| const void *end = mcache + mcache_size; | ||
| const void *current = mcache; | ||
|
|
||
| while (current + sizeof(uint32_t) < end) { | ||
| const union mcache_entry *entry = current; | ||
|
|
||
| if (entry->magic == MCACHE_MAGIC_FULL || entry->magic == MCACHE_MAGIC_END) { | ||
| current += sizeof(entry->magic); | ||
| break; | ||
| } | ||
|
|
||
| assert(entry->magic == MCACHE_MAGIC_FILE); | ||
| current += ALIGN_UP(be32toh(entry->file.h.offset), CBFS_MCACHE_ALIGNMENT); | ||
| } | ||
|
|
||
| return current - mcache; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,161 @@ | ||
| /* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */ | ||
|
|
||
| #include <commonlib/bsd/cbfs_private.h> | ||
| #include <assert.h> | ||
|
|
||
| static cb_err_t read_next_header(cbfs_dev_t dev, size_t *offset, struct cbfs_file *buffer) | ||
| { | ||
| const size_t devsize = cbfs_dev_size(dev); | ||
| DEBUG("Looking for next file @%#zx...\n", *offset); | ||
| *offset = ALIGN_UP(*offset, CBFS_ALIGNMENT); | ||
| while (*offset + sizeof(*buffer) < devsize) { | ||
| if (cbfs_dev_read(dev, buffer, *offset, sizeof(*buffer)) != sizeof(*buffer)) | ||
| return CB_CBFS_IO; | ||
|
|
||
| if (memcmp(buffer->magic, CBFS_FILE_MAGIC, sizeof(buffer->magic)) == 0) | ||
| return CB_SUCCESS; | ||
|
|
||
| *offset += CBFS_ALIGNMENT; | ||
| } | ||
|
|
||
| DEBUG("End of CBFS reached\n"); | ||
| return CB_CBFS_NOT_FOUND; | ||
| } | ||
|
|
||
| cb_err_t cbfs_walk(cbfs_dev_t dev, cb_err_t (*walker)(cbfs_dev_t dev, size_t offset, | ||
| const union cbfs_mdata *mdata, | ||
| size_t already_read, void *arg), | ||
| void *arg, struct vb2_hash *metadata_hash, enum cbfs_walk_flags flags) | ||
| { | ||
| const bool do_hash = CBFS_ENABLE_HASHING && metadata_hash; | ||
| struct vb2_digest_context dc; | ||
| vb2_error_t vbrv; | ||
|
|
||
| assert(CBFS_ENABLE_HASHING || (!metadata_hash && !(flags & CBFS_WALK_WRITEBACK_HASH))); | ||
| if (do_hash && (vbrv = vb2_digest_init(&dc, metadata_hash->algo))) { | ||
| ERROR("Metadata hash digest (%d) init error: %#x\n", metadata_hash->algo, vbrv); | ||
| return CB_ERR_ARG; | ||
| } | ||
|
|
||
| size_t offset = 0; | ||
| cb_err_t ret_header; | ||
| cb_err_t ret_walker = CB_CBFS_NOT_FOUND; | ||
| union cbfs_mdata mdata; | ||
| while ((ret_header = read_next_header(dev, &offset, &mdata.h)) == CB_SUCCESS) { | ||
| const uint32_t attr_offset = be32toh(mdata.h.attributes_offset); | ||
| const uint32_t data_offset = be32toh(mdata.h.offset); | ||
| const uint32_t data_length = be32toh(mdata.h.len); | ||
| const uint32_t type = be32toh(mdata.h.type); | ||
| const bool empty = (type == CBFS_TYPE_DELETED || type == CBFS_TYPE_DELETED2); | ||
|
|
||
| DEBUG("Found CBFS header @%#zx (type %d, attr +%#x, data +%#x, length %#x)\n", | ||
| offset, type, attr_offset, data_offset, data_length); | ||
| if (data_offset > sizeof(mdata)) { | ||
| ERROR("File metadata @%#zx too large\n", offset); | ||
| goto next_file; | ||
| } | ||
|
|
||
| if (empty && !(flags & CBFS_WALK_INCLUDE_EMPTY)) | ||
| goto next_file; | ||
|
|
||
| /* When hashing we need to read everything. Otherwise skip the attributes. | ||
| attr_offset may be 0, which means there are no attributes. */ | ||
| ssize_t todo; | ||
| if (do_hash || attr_offset == 0) | ||
| todo = data_offset - sizeof(mdata.h); | ||
| else | ||
| todo = attr_offset - sizeof(mdata.h); | ||
| if (todo <= 0 || data_offset < attr_offset) { | ||
| ERROR("Corrupt file header @%#zx\n", offset); | ||
| goto next_file; | ||
| } | ||
|
|
||
| /* Read the rest of the metadata (filename, and possibly attributes). */ | ||
| assert(todo > 0 && todo <= sizeof(mdata) - sizeof(mdata.h)); | ||
| if (cbfs_dev_read(dev, mdata.raw + sizeof(mdata.h), | ||
| offset + sizeof(mdata.h), todo) != todo) | ||
| return CB_CBFS_IO; | ||
| DEBUG("File name: '%s'\n", mdata.filename); | ||
|
|
||
| if (do_hash && !empty && vb2_digest_extend(&dc, mdata.raw, data_offset)) | ||
| return CB_ERR; | ||
|
|
||
| if (walker && ret_walker == CB_CBFS_NOT_FOUND) | ||
| ret_walker = walker(dev, offset, &mdata, sizeof(mdata.h) + todo, arg); | ||
|
|
||
| /* Return IO errors immediately. For others, finish the hash first if needed. */ | ||
| if (ret_walker == CB_CBFS_IO || (ret_walker != CB_CBFS_NOT_FOUND && !do_hash)) | ||
| return ret_walker; | ||
|
|
||
| next_file: | ||
| offset += data_offset + data_length; | ||
| } | ||
|
|
||
| if (ret_header != CB_CBFS_NOT_FOUND) | ||
| return ret_header; | ||
|
|
||
| if (do_hash) { | ||
| uint8_t real_hash[VB2_MAX_DIGEST_SIZE]; | ||
| size_t hash_size = vb2_digest_size(metadata_hash->algo); | ||
| if (vb2_digest_finalize(&dc, real_hash, hash_size)) | ||
| return CB_ERR; | ||
| if (flags & CBFS_WALK_WRITEBACK_HASH) | ||
| memcpy(metadata_hash->raw, real_hash, hash_size); | ||
| else if (memcmp(metadata_hash->raw, real_hash, hash_size) != 0) | ||
| return CB_CBFS_HASH_MISMATCH; | ||
| } | ||
|
|
||
| return ret_walker; | ||
| } | ||
|
|
||
| cb_err_t cbfs_copy_fill_metadata(union cbfs_mdata *dst, const union cbfs_mdata *src, | ||
| size_t already_read, cbfs_dev_t dev, size_t offset) | ||
| { | ||
| /* First, copy the stuff that cbfs_walk() already read for us. */ | ||
| memcpy(dst, src, already_read); | ||
|
|
||
| /* Then read in whatever metadata may be left (will only happen in non-hashing case). */ | ||
| const size_t todo = be32toh(src->h.offset) - already_read; | ||
| assert(todo <= sizeof(*dst) - already_read); | ||
| if (todo && cbfs_dev_read(dev, dst->raw + already_read, offset + already_read, | ||
| todo) != todo) | ||
| return CB_CBFS_IO; | ||
| return CB_SUCCESS; | ||
| } | ||
|
|
||
| struct cbfs_lookup_args { | ||
| union cbfs_mdata *mdata_out; | ||
| const char *name; | ||
| size_t namesize; | ||
| size_t *data_offset_out; | ||
| }; | ||
|
|
||
| static cb_err_t lookup_walker(cbfs_dev_t dev, size_t offset, const union cbfs_mdata *mdata, | ||
| size_t already_read, void *arg) | ||
| { | ||
| struct cbfs_lookup_args *args = arg; | ||
|
|
||
| /* Check if the name we're looking for could fit, then we can safely memcmp() it. */ | ||
| if (args->namesize > already_read - offsetof(union cbfs_mdata, filename) || | ||
| memcmp(args->name, mdata->filename, args->namesize) != 0) | ||
| return CB_CBFS_NOT_FOUND; | ||
|
|
||
| LOG("Found '%s' @%#zx size %#x\n", args->name, offset, be32toh(mdata->h.len)); | ||
| if (cbfs_copy_fill_metadata(args->mdata_out, mdata, already_read, dev, offset)) | ||
| return CB_CBFS_IO; | ||
|
|
||
| *args->data_offset_out = offset + be32toh(mdata->h.offset); | ||
| return CB_SUCCESS; | ||
| } | ||
|
|
||
| cb_err_t cbfs_lookup(cbfs_dev_t dev, const char *name, union cbfs_mdata *mdata_out, | ||
| size_t *data_offset_out, struct vb2_hash *metadata_hash) | ||
| { | ||
| struct cbfs_lookup_args args = { | ||
| .mdata_out = mdata_out, | ||
| .name = name, | ||
| .namesize = strlen(name) + 1, /* Count trailing \0 so we can memcmp() it. */ | ||
| .data_offset_out = data_offset_out, | ||
| }; | ||
| return cbfs_walk(dev, lookup_walker, &args, metadata_hash, 0); | ||
| } |