soc/intel/common/basecode: Implement CSE update flow

The following changes are done in this patch:
 1. Get the CSE partition info containing version of CSE RW using
    GET_BOOT_PARTITION_INFO HECI command
 2. Get the me_rw.version from the currently selected RW slot.
 3. If the versions from the above 2 locations don't match start the update
    - If CSE's current boot partition is not RO, then
        * Set the CSE's next boot partition to RO using SET_BOOT_PARTITION
          HECI command.
        * Send global reset command to reset the system.
    - Enable HMRFPO (Host ME Region Flash Protection Override) operation
      mode using HMRFPO_ENABLE HECI command
    - Erase and Copy the CBFS CSE RW to CSE RW partition
    - Set the CSE's next boot partition to RW using
      SET_BOOT_PARTITION HECI command
    - Trigger global reset
    - The system should boot with the updated CSE RW partition.

TEST=Verified basic update flows on hatch and helios.
BUG=b:111330995

Change-Id: I12f6bba3324069d65edabaccd234006b0840e700
Signed-off-by: Rizwan Qureshi <rizwan.qureshi@intel.com>
Signed-off-by: Sridhar Siricilla <sridhar.siricilla@intel.com>
Signed-off-by: V Sowmya <v.sowmya@intel.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/35403
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Furquan Shaikh <furquan@google.com>
Reviewed-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
This commit is contained in:
Rizwan Qureshi 2019-09-06 20:28:43 +05:30 committed by Patrick Georgi
parent 56642930ab
commit ec321094f6
6 changed files with 690 additions and 21 deletions

View File

@ -0,0 +1,150 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by Microsoft Visio, SVG Export Layout_after.svg Page-1 -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
width="2.39231in" height="2.05998in" viewBox="0 0 172.246 148.318" xml:space="preserve" color-interpolation-filters="sRGB"
class="st12">
<style type="text/css">
<![CDATA[
.st1 {fill:#ffffff;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
.st2 {fill:#000000;font-family:Calibri;font-size:0.333344em}
.st3 {fill:#ffc000;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
.st4 {fill:#000000;font-family:Calibri;font-size:0.499992em}
.st5 {fill:#a5a5a5;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
.st6 {fill:#a5a5a5;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
.st7 {fill:none;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
.st8 {stroke:#4bacc6;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
.st9 {font-size:1em}
.st10 {marker-end:url(#mrkr4-59);stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
.st11 {fill:#000000;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:0.22935779816514}
.st12 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
]]>
</style>
<defs id="Markers">
<g id="lend4">
<path d="M 2 1 L 0 0 L 2 -1 L 2 1 " style="stroke:none"/>
</g>
<marker id="mrkr4-59" class="st11" refX="-8.72" orient="auto" markerUnits="strokeWidth" overflow="visible">
<use xlink:href="#lend4" transform="scale(-4.36,-4.36) "/>
</marker>
</defs>
<g>
<title>Page-1</title>
<g id="shape116-1" transform="translate(38.7567,-139.932)">
<title>Rectangle.116</title>
<desc>DESC</desc>
<rect x="0" y="143.783" width="79.3701" height="4.53543" class="st1"/>
<text x="35.49" y="147.25" class="st2">DESC</text> </g>
<g id="shape117-4" transform="translate(38.7567,-130.935)">
<title>Rectangle.117</title>
<desc>CSE - RO</desc>
<rect x="0" y="139.605" width="79.3701" height="8.71293" class="st3"/>
<text x="29.35" y="145.76" class="st4">CSE - RO</text> </g>
<g id="shape118-7" transform="translate(38.5344,-4.54823)">
<title>Rectangle.118</title>
<rect x="0" y="29.8973" width="79.5923" height="118.421" class="st1"/>
</g>
<g id="shape119-9" transform="translate(41.225,-5.80807)">
<title>Rectangle.119</title>
<desc>COREBOOT_RO</desc>
<rect x="0" y="119.972" width="74.3581" height="28.3465" class="st5"/>
<text x="18.32" y="135.95" class="st4">COREBOOT_RO</text> </g>
<g id="shape120-12" transform="translate(41.225,-34.1545)">
<title>Rectangle.120</title>
<desc>RW_MISC</desc>
<rect x="0" y="143.907" width="74.3581" height="4.41113" class="st5"/>
<text x="29.12" y="147.31" class="st2">RW_MISC</text> </g>
<g id="shape121-15" transform="translate(41.225,-38.7215)">
<title>Rectangle.121</title>
<desc>FW_MAIN_B</desc>
<rect x="0" y="119.972" width="74.3581" height="28.3465" class="st6"/>
<text x="21.52" y="129.37" class="st4">FW_MAIN_B</text> </g>
<g id="shape122-18" transform="translate(41.225,-67.0679)">
<title>Rectangle.122</title>
<desc>FW_MAIN_A</desc>
<rect x="0" y="119.972" width="74.3581" height="28.3465" class="st6"/>
<text x="21.41" y="129.37" class="st4">FW_MAIN_A</text> </g>
<g id="shape123-21" transform="translate(3.88308,-0.375)">
<title>Sheet.123</title>
<desc>0x1FFFFFF</desc>
<rect x="0" y="137.688" width="41.4007" height="10.6299" class="st7"/>
<text x="8.09" y="144.8" class="st4">0x1FFFFFF</text> </g>
<g id="shape124-24" transform="translate(21.7488,-138.564)">
<title>Sheet.124</title>
<desc>0x0</desc>
<rect x="0" y="138.939" width="21.2598" height="9.37934" class="st7"/>
<text x="6.29" y="145.43" class="st4">0x0</text> </g>
<g id="shape125-27" transform="translate(41.2627,-96.0443)">
<title>Rectangle.125</title>
<desc>RW_LEGACY</desc>
<rect x="0" y="122.448" width="74.3581" height="25.8706" class="st5"/>
<text x="27.04" y="136.58" class="st2">RW_LEGACY</text> </g>
<g id="shape126-30" transform="translate(119.253,-6.75295)">
<title>Right Brace.126</title>
<path d="M-0 148.32 A9.42279 2.96575 -180 0 0 5.11 146.6 L5.11 135.46 L10.21 135.46 L5.11 135.46 L5.11 124.32 A9.42279
2.96575 -180 0 0 0 122.6" class="st8"/>
</g>
<g id="shape127-33" transform="translate(120.961,-10.2963)">
<title>Sheet.127</title>
<desc>HW WP</desc>
<rect x="0" y="137.688" width="31.1811" height="10.6299" class="st7"/>
<text x="6.16" y="144.8" class="st4">HW WP</text> </g>
<g id="shape128-36" transform="translate(119.43,-123.061)">
<title>Right Brace.128</title>
<path d="M-0 148.32 a10.4615 0.900102 -180 0 0 5.66929 -0.520272 L5.67 144.42 L11.34 144.42 L5.67 144.42 L5.67 141.03
a10.4615 0.900102 -180 0 0 -5.66929 -0.520272" class="st8"/>
</g>
<g id="shape129-39" transform="translate(126.517,-119.597)">
<title>Sheet.129</title>
<desc>SPI Controller WP via descriptor</desc>
<rect x="0" y="138.043" width="45.3543" height="10.2756" class="st7"/>
<text x="5.53" y="141.98" class="st2">SPI Controller WP via <tspan x="14.37" dy="1.2em" class="st9">descriptor</tspan></text> </g>
<g id="group130-43" transform="translate(42.8947,-77.0772)">
<title>Sheet.130</title>
<g id="shape131-44">
<title>Rectangle.423</title>
<desc>CSE-RW</desc>
<rect x="0" y="141.232" width="70.8661" height="7.08661" class="st3"/>
<text x="25.77" y="146.58" class="st4">CSE-RW</text> </g>
</g>
<g id="group132-47" transform="translate(42.8947,-48.7307)">
<title>Sheet.132</title>
<g id="shape133-48">
<title>Rectangle.423</title>
<desc>CSE-RW</desc>
<rect x="0" y="141.232" width="70.8661" height="7.08661" class="st3"/>
<text x="25.77" y="146.58" class="st4">CSE-RW</text> </g>
</g>
<g id="shape134-51" transform="translate(38.6427,-123.114)">
<title>Rectangle.134</title>
<desc>CSE-RW</desc>
<rect x="0" y="140.497" width="79.3701" height="7.82103" class="st3"/>
<text x="30.03" y="146.21" class="st4">CSE-RW</text> </g>
<g id="shape135-54" transform="translate(41.225,-52.8947)">
<title>Universal connector.473</title>
<path d="M0 148.32 L-8.38 148.32 A8.37776 8.37776 0 0 1 -16.76 139.94 L-16.76 111.25 L-16.76 81.27 A7.08661 7.08661 0
0 1 -9.67 74.19 L-9.12 74.19" class="st10"/>
</g>
<g id="shape136-60" transform="translate(41.225,-81.2411)">
<title>Universal connector.136</title>
<path d="M0 148.32 L-8.38 148.32 A8.37776 8.37776 0 0 1 -16.76 139.94 L-16.76 125.43 L-16.76 109.62 A7.08661 7.08661
0 0 1 -9.67 102.53 L-9.12 102.53" class="st10"/>
</g>
<g id="shape138-65" transform="translate(-124.557,86.8317) rotate(-90)">
<title>Sheet.138</title>
<desc>CSE RW copied during an update</desc>
<rect x="0" y="124.932" width="58.1102" height="23.3858" class="st7"/>
<text x="10.77" y="134.83" class="st4">CSE RW copied <tspan x="8.15" dy="1.2em" class="st9">during an update</tspan></text> </g>
<g id="shape139-69" transform="translate(119.43,-133.052)">
<title>Right Brace.139</title>
<path d="M0 148.32 a10.4615 0.736641 -180 0 0 5.66929 -0.425789 L5.67 145.12 L11.34 145.12 L5.67 145.12 L5.67 142.36
a10.4615 0.736641 -180 0 0 -5.66929 -0.425789" class="st8"/>
</g>
<g id="shape140-72" transform="translate(127.934,-133.061)">
<title>Sheet.140</title>
<desc>GRP0 Protected</desc>
<rect x="0" y="140.523" width="35.4331" height="7.79528" class="st7"/>
<text x="4.86" y="145.62" class="st2">GRP0 Protected</text> </g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by Microsoft Visio, SVG Export Layout_before.svg Page-1 -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
width="2.3058in" height="2.05998in" viewBox="0 0 166.017 148.318" xml:space="preserve" color-interpolation-filters="sRGB"
class="st11">
<style type="text/css">
<![CDATA[
.st1 {fill:#ffffff;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
.st2 {fill:#000000;font-family:Calibri;font-size:0.333344em}
.st3 {fill:#ffc000;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
.st4 {fill:#000000;font-family:Calibri;font-size:0.499992em}
.st5 {fill:#a5a5a5;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.24}
.st6 {fill:#a5a5a5;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
.st7 {fill:none;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
.st8 {stroke:#4bacc6;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75}
.st9 {fill:#000000;font-family:Calibri;font-size:0.416656em}
.st10 {font-size:1em}
.st11 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
]]>
</style>
<g>
<title>Page-1</title>
<g id="shape87-1" transform="translate(35.2486,-139.932)">
<title>Rectangle.178</title>
<desc>DESC</desc>
<rect x="0" y="143.783" width="79.3701" height="4.53543" class="st1"/>
<text x="35.49" y="147.25" class="st2">DESC</text> </g>
<g id="shape88-4" transform="translate(35.2486,-122.945)">
<title>Rectangle.179</title>
<desc>CSME/PMC</desc>
<rect x="0" y="131.615" width="79.3701" height="16.7031" class="st3"/>
<text x="25.8" y="141.02" class="st4">CSME/PMC</text> </g>
<g id="shape89-7" transform="translate(35.0263,-4.54823)">
<title>Rectangle.180</title>
<rect x="0" y="29.8973" width="79.5923" height="118.421" class="st1"/>
</g>
<g id="shape90-9" transform="translate(37.7169,-5.80807)">
<title>Rectangle.181</title>
<desc>COREBOOT_RO</desc>
<rect x="0" y="119.972" width="74.3581" height="28.3465" class="st5"/>
<text x="18.32" y="135.95" class="st4">COREBOOT_RO</text> </g>
<g id="shape91-12" transform="translate(37.7169,-34.1545)">
<title>Rectangle.182</title>
<desc>RW_MISC</desc>
<rect x="0" y="143.907" width="74.3581" height="4.41113" class="st5"/>
<text x="29.12" y="147.31" class="st2">RW_MISC</text> </g>
<g id="shape92-15" transform="translate(37.7169,-38.7215)">
<title>Rectangle.183</title>
<desc>FW_MAIN_B</desc>
<rect x="0" y="119.972" width="74.3581" height="28.3465" class="st6"/>
<text x="21.52" y="129.37" class="st4">FW_MAIN_B</text> </g>
<g id="shape93-18" transform="translate(37.7169,-67.0679)">
<title>Rectangle.184</title>
<desc>FW_MAIN_A</desc>
<rect x="0" y="119.972" width="74.3581" height="28.3465" class="st6"/>
<text x="21.41" y="129.37" class="st4">FW_MAIN_A</text> </g>
<g id="shape94-21" transform="translate(0.375,-0.375)">
<title>Sheet.94</title>
<desc>0x1FFFFFF</desc>
<rect x="0" y="137.688" width="41.4007" height="10.6299" class="st7"/>
<text x="8.09" y="144.8" class="st4">0x1FFFFFF</text> </g>
<g id="shape95-24" transform="translate(18.2407,-138.564)">
<title>Sheet.95</title>
<desc>0x0</desc>
<rect x="0" y="138.939" width="21.2598" height="9.37934" class="st7"/>
<text x="6.29" y="145.43" class="st4">0x0</text> </g>
<g id="shape106-27" transform="translate(37.7546,-96.0443)">
<title>Rectangle.106</title>
<desc>RW_LEGACY</desc>
<rect x="0" y="122.448" width="74.3581" height="25.8706" class="st5"/>
<text x="27.04" y="136.58" class="st2">RW_LEGACY</text> </g>
<g id="shape113-30" transform="translate(115.744,-6.75295)">
<title>Right Brace.398</title>
<path d="M-0 148.32 A9.42279 2.96575 -180 0 0 5.11 146.6 L5.11 135.46 L10.21 135.46 L5.11 135.46 L5.11 124.32 A9.42279
2.96575 -180 0 0 0 122.6" class="st8"/>
</g>
<g id="shape96-33" transform="translate(117.453,-10.2963)">
<title>Sheet.96</title>
<desc>HW WP</desc>
<rect x="0" y="137.688" width="31.1811" height="10.6299" class="st7"/>
<text x="6.16" y="144.8" class="st4">HW WP</text> </g>
<g id="shape115-36" transform="translate(116.508,-123.131)">
<title>Right Brace.115</title>
<path d="M0 148.32 A10.4615 2.27029 -180 0 0 5.67 147.01 L5.67 138.48 L11.34 138.48 L5.67 138.48 L5.67 129.95 A10.4615
2.27029 -180 0 0 0 128.63" class="st8"/>
</g>
<g id="shape97-39" transform="translate(120.288,-122.265)">
<title>Sheet.97</title>
<desc>SPI Controller WP via descriptor</desc>
<rect x="0" y="124.932" width="45.3543" height="23.3858" class="st7"/>
<text x="4.71" y="135.13" class="st9">SPI Controller WP <tspan x="8.83" dy="1.2em" class="st10">via descriptor</tspan></text> </g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,127 @@
CSE FW update mechanism for devices in field
## Introduction
CSE Firmware and PMC Firmware are critical components of Intel SoCs.
CSE and PMC cooperate by providing platform services during boot and other
power transition flows.
## Problem Statement
Currently, on Chromium OS Systems, CSE region is not updatable. So, new CSE FW
versions that are released by Intel to address important functional and security
bugs post-product launch will not be available to the end-user. Hence, the proposed
solution allows in-field CSE FW update to propagate those bug fixes
to end user platforms.
## Design Proposal
### CSE FW design Proposal:
Key Elements:
- CSE FW layout is composed of two bootable partitions (RO Recovery Partition
and RW Normal Partition).
- Boot partition selection: An API-based mechanism is used to decide from which partition
CSE will boot.
- The HECI APIs below will be supported in this CSE FW:
- HMRFPO_ENABLE: This command requests the CSE enter a mode in which writes to
the CSE region from the CSE are disabled. It also grants temporary write access
to the RW partition from the host (RO is still protected by GPR0).
- GET_PARTITION_INFO: The command retrieves information for each boot partition from CSE
like version, start/end offsets of a partition within CSE region, and boot
partition status. Also, it provides below information:
- The current boot partition which was used during this boot,
- The boot partition that will be used on the next CSE reset
- The number of boot partitions available in the CSE region
- SET_BOOT_PARTITION_INFO: This command allows the firmware to request the
CSE to boot from either its RO or RW partition at its next reset.
- DATA_CLEAR: This command requests the CSE to reset its data partition back
to manufacturing defaults
FW Layout, RW/RO Partitions:
The CSE RO partition is the first in the CSE boot order, hence it will be used
out of G3. RO partition contains minimum CSE code capable to boot platform and
execute FW update of RW partition. In addition to CSE code, the RO partition also
contains PMC FW patch and other CSE-loadable platform FW components.
RW partition contains fully operational CSE FW, PMC FW, other CSE loadable
platform FW components.
Boot partition selection:
CSE FW shall support 2 APIs to get boot partition info, and set boot partition
info to notify CSE to select the partition on the next boot.
### HOST FW Design proposal:
Key Elements:
- Build time artifacts:
CSE RW Version update binary - The FW shall pack CSE RW update blob and
corresponding version binary which contains version of the CSE RW blob.
- FW Update:
coreboot will implement the logic to compare the CSE's FW version with CBFS
CSE RW binary's version in the firmware slot (FW_MAIN_A/FW_MAIN_B) and update
the CSE RW region if there is a version mismatch. If there is no version
mismatch, firmware skips CSE FW update.
- Handling of CSE FW Downgrade:
coreboot will send DATA_CLEAR HECI command when there is a CSE FW downgrade.
This must be done to avoid data mismatch due to CSE FW downgrade. Further,
CSE will restore the data back to manufacturing defaults after data reset.
## Implementation Details
To enable CSE FW update flow the following changes are required in coreboot:
* Descriptor change may be required to accommodate CSE binary. The CSE binary is tied with
a platform. So CSE size may vary from one platform to another.
* FMAP changes may be required to accommodate CSE binary and CSE RW blob in the RW CBFS region.
Please check platform specific CSE kit for CSE binary information.
* CSE Lite SKU binary and CSE RW blob
* Makefile change to pack CSE RW binaries in the CBFS
* Implementation of update flow:
- Get CSE boot partition info using GET_BOOT_PARTITION_INFO HECI command.
- Get the cbfs_me_rw.version from the currently selected RW slot.
- If the version from the above 2 locations don't match, then start CSE FW update.
- If CSE is not booting from RO, then
- Set the CSE's next boot partition to RO using SET_BOOT_PARTITION_INFO
HECI command.
- Send GLOBAL_RESET HECI command to reset the system.
- If RW update is a CSE FW downgrade, then coreboot has to send
DATA_CLEAR command to clear run time data of CSE.
- Enable HMRFPO Mode (Host ME Region Flash Protection Override) by
sending HMRFPO_ENABLE HECI command to CSE.
- Erase and Copy the CBFS CSE RW to CSE RW partition
- Set CSE's next boot partition to RW.
- Trigger Global Reset which resets both CSE and Host.
Then system should boot with the updated CSE.
* The resulting flash layout is shown below:
![Flash Layout](./Layout_before.svg) ![FlashLayout](./Layout_after.svg)
- Typical boot flow
- Vboot selects the RW FW (FW_MAIN_A or FW_MAIN_B) to boot.
- coreboot skips CSE FW update flow if boot mode is recovery.
- If CSE RW blob is not locatable in the CBFS, then RW Firmware skips update flow
and sends SET_BOOT_PARTITION_INFO command to switch CSE to boot from RW
and issues Global Reset if CSE is already not booting from RW partition.
- The RW firmware will compare the CSE RW version with CSE RW blob in the slot.
- If there is a mismatch, then firmware will carry out update flow as explained before.

View File

@ -10,3 +10,4 @@ This section contains documentation about coreboot on specific Intel SOCs.
- [MP Initialization](mp_init/mp_init.md) - [MP Initialization](mp_init/mp_init.md)
- [Firmware Interface Table](fit.md) - [Firmware Interface Table](fit.md)
- [Apollolake](apollolake/index.md) - [Apollolake](apollolake/index.md)
- [CSE FW Update](cse_fw_update/cse_fw_update_model.md)

View File

@ -19,3 +19,15 @@ config SOC_INTEL_CSE_LITE_SKU
depends on CHROMEOS depends on CHROMEOS
help help
Enables CSE Lite SKU Enables CSE Lite SKU
config SOC_INTEL_CSE_FMAP_NAME
string "Name of CSE Region in FMAP"
default "SI_ME"
help
Name of CSE region in FMAP
config SOC_INTEL_CSE_RW_CBFS_NAME
string "CBFS entry name for CSE RW blob"
default "me_rw"
help
CBFS entry name for Intel CSE CBFS RW blob

View File

@ -1,15 +1,30 @@
/* SPDX-License-Identifier: GPL-2.0-only */ /* SPDX-License-Identifier: GPL-2.0-only */
#include <bootstate.h> #include <bootstate.h>
#include <console/console.h> #include <console/console.h>
#include <soc/intel/common/reset.h> #include <boot_device.h>
#include <cbfs.h>
#include <commonlib/cbfs.h>
#include <commonlib/region.h>
#include <fmap.h>
#include <intelblocks/cse.h> #include <intelblocks/cse.h>
#include <lib.h>
#include <security/vboot/vboot_common.h> #include <security/vboot/vboot_common.h>
#include <security/vboot/misc.h> #include <security/vboot/misc.h>
#include <vb2_api.h> #include <vb2_api.h>
#include <soc/intel/common/reset.h>
/* CSE RW version size reserved in the CSE CBFS RW binary */
#define CSE_RW_VERSION_SZ 16
/* Converts bp index to boot partition string */ /* Converts bp index to boot partition string */
#define GET_BP_STR(bp_index) (bp_index ? "RW" : "RO") #define GET_BP_STR(bp_index) (bp_index ? "RW" : "RO")
/* CSE RW boot partition signature */
#define CSE_RW_SIGNATURE 0x000055aa
/* CSE RW boot partition signature size */
#define CSE_RW_SIGN_SIZE sizeof(uint32_t)
/* /*
* CSE Firmware supports 3 boot partitions. For CSE Lite SKU, only 2 boot partitions are * CSE Firmware supports 3 boot partitions. For CSE Lite SKU, only 2 boot partitions are
* used and 3rd boot partition is set to BP_STATUS_PARTITION_NOT_PRESENT. * used and 3rd boot partition is set to BP_STATUS_PARTITION_NOT_PRESENT.
@ -22,10 +37,10 @@
/* CSE Lite SKU's valid bootable partition identifiers */ /* CSE Lite SKU's valid bootable partition identifiers */
enum boot_partition_id { enum boot_partition_id {
/* RO(BP1) contains recovery/minimal boot FW */ /* RO(BP1) contains recovery/minimal boot firmware */
RO = 0, RO = 0,
/* RW(BP2) contains fully functional CSE Firmware */ /* RW(BP2) contains fully functional CSE firmware */
RW = 1 RW = 1
}; };
@ -162,6 +177,49 @@ static const struct cse_bp_entry *cse_get_bp_entry(enum boot_partition_id bp,
return &cse_bp_info->bp_entries[bp]; return &cse_bp_info->bp_entries[bp];
} }
static void cse_get_bp_entry_range(const struct cse_bp_info *cse_bp_info,
enum boot_partition_id bp, uint32_t *start_offset, uint32_t *end_offset)
{
const struct cse_bp_entry *cse_bp;
cse_bp = cse_get_bp_entry(bp, cse_bp_info);
if (start_offset)
*start_offset = cse_bp->start_offset;
if (end_offset)
*end_offset = cse_bp->end_offset;
}
static const struct fw_version *cse_get_bp_entry_version(enum boot_partition_id bp,
const struct cse_bp_info *bp_info)
{
const struct cse_bp_entry *cse_bp;
cse_bp = cse_get_bp_entry(bp, bp_info);
return &cse_bp->fw_ver;
}
static const struct fw_version *cse_get_rw_version(const struct cse_bp_info *cse_bp_info)
{
return cse_get_bp_entry_version(RW, cse_bp_info);
}
static bool cse_is_rw_bp_status_valid(const struct cse_bp_info *cse_bp_info)
{
const struct cse_bp_entry *rw_bp;
rw_bp = cse_get_bp_entry(RW, cse_bp_info);
if (rw_bp->status == BP_STATUS_PARTITION_NOT_PRESENT ||
rw_bp->status == BP_STATUS_GENERAL_FAILURE) {
printk(BIOS_ERR, "cse_lite: RW BP (status:%u) is not valid\n", rw_bp->status);
return false;
}
return true;
}
static void cse_print_boot_partition_info(const struct cse_bp_info *cse_bp_info) static void cse_print_boot_partition_info(const struct cse_bp_info *cse_bp_info)
{ {
const struct cse_bp_entry *cse_bp; const struct cse_bp_entry *cse_bp;
@ -196,6 +254,9 @@ static void cse_print_boot_partition_info(const struct cse_bp_info *cse_bp_info)
* - When CSE boots from RW partition (COM: Normal and CWS: Normal) * - When CSE boots from RW partition (COM: Normal and CWS: Normal)
* - When CSE boots from RO partition (COM: Soft Temp Disable and CWS: Normal) * - When CSE boots from RO partition (COM: Soft Temp Disable and CWS: Normal)
* - After HMRFPO_ENABLE command is issued to CSE (COM: SECOVER_MEI_MSG and CWS: Normal) * - After HMRFPO_ENABLE command is issued to CSE (COM: SECOVER_MEI_MSG and CWS: Normal)
* The prerequisite check should be handled in cse_get_bp_info() and
* cse_set_next_boot_partition() since the CSE's current operation mode is changed between these
* cmd handler calls.
*/ */
static bool cse_is_bp_cmd_info_possible(void) static bool cse_is_bp_cmd_info_possible(void)
{ {
@ -294,40 +355,259 @@ static bool cse_set_next_boot_partition(enum boot_partition_id bp)
return true; return true;
} }
static bool cse_boot_to_rw(const struct cse_bp_info *cse_bp_info) /* Set the CSE's next boot partition and issues system reset */
static bool cse_set_and_boot_from_next_bp(enum boot_partition_id bp)
{ {
if (cse_get_current_bp(cse_bp_info) == RW) if (!cse_set_next_boot_partition(bp))
return true;
if (!cse_set_next_boot_partition(RW))
return false; return false;
do_global_reset(); do_global_reset();
die("cse_lite: Failed to reset system\n"); die("cse_lite: Failed to reset the system\n");
/* Control never reaches here */ /* Control never reaches here */
return false; return false;
} }
static bool cse_is_rw_status_valid(const struct cse_bp_info *cse_bp_info) static bool cse_boot_to_rw(const struct cse_bp_info *cse_bp_info)
{ {
const struct cse_bp_entry *rw_bp; if (cse_get_current_bp(cse_bp_info) == RW)
return true;
/* RW(BP2) alone represents RW partition */ return cse_set_and_boot_from_next_bp(RW);
rw_bp = cse_get_bp_entry(RW, cse_bp_info); }
if (rw_bp->status == BP_STATUS_PARTITION_NOT_PRESENT || static bool cse_boot_to_ro(const struct cse_bp_info *cse_bp_info)
rw_bp->status == BP_STATUS_GENERAL_FAILURE) { {
printk(BIOS_ERR, "cse_lite: RW BP (status:%u) is not valid\n", rw_bp->status); if (cse_get_current_bp(cse_bp_info) == RO)
return true;
return cse_set_and_boot_from_next_bp(RO);
}
static bool cse_get_rw_rdev(struct region_device *rdev)
{
if (fmap_locate_area_as_rdev_rw(CONFIG_SOC_INTEL_CSE_FMAP_NAME, rdev) < 0) {
printk(BIOS_ERR, "cse_lite: Failed to locate %s in FMAP\n",
CONFIG_SOC_INTEL_CSE_FMAP_NAME);
return false;
}
return true;
}
static bool cse_get_cbfs_rdev(struct region_device *source_rdev)
{
struct cbfsf file_desc;
if (cbfs_boot_locate(&file_desc, CONFIG_SOC_INTEL_CSE_RW_CBFS_NAME, NULL) < 0)
return false;
cbfs_file_data(source_rdev, &file_desc);
return true;
}
static bool cse_is_rw_bp_sign_valid(const struct region_device *target_rdev)
{
uint32_t cse_bp_sign;
if (rdev_readat(target_rdev, &cse_bp_sign, 0, CSE_RW_SIGN_SIZE) != CSE_RW_SIGN_SIZE) {
printk(BIOS_ERR, "cse_lite: Failed to read RW boot partition signature\n");
return false;
}
return cse_bp_sign == CSE_RW_SIGNATURE;
}
static bool cse_get_target_rdev(const struct cse_bp_info *cse_bp_info,
struct region_device *target_rdev)
{
struct region_device cse_region_rdev;
size_t size;
uint32_t start_offset;
uint32_t end_offset;
if (!cse_get_rw_rdev(&cse_region_rdev))
return false;
cse_get_bp_entry_range(cse_bp_info, RW, &start_offset, &end_offset);
size = end_offset + 1 - start_offset;
if (rdev_chain(target_rdev, &cse_region_rdev, start_offset, size))
return false;
printk(BIOS_DEBUG, "cse_lite: CSE RW partition: offset = 0x%x, size = 0x%x\n",
(uint32_t)start_offset, (uint32_t) size);
return true;
}
static bool cse_get_cbfs_rw_version(const struct region_device *source_rdev,
void *cse_cbfs_rw_ver)
{
if (rdev_readat(source_rdev, (void *) cse_cbfs_rw_ver, 0, sizeof(struct fw_version))
!= sizeof(struct fw_version)) {
printk(BIOS_ERR, "cse_lite: Failed to read CSE CBFW RW version\n");
return false; return false;
} }
return true; return true;
} }
static bool cse_is_rw_info_valid(struct cse_bp_info *cse_bp_info) /*
* Compare versions of CSE CBFS RW and CSE RW partition
* If ver_cmp_status = 0, no update is required
* If ver_cmp_status < 0, coreboot downgrades CSE RW region
* If ver_cmp_status > 0, coreboot upgrades CSE RW region
*/
static int cse_check_version_mismatch(const struct cse_bp_info *cse_bp_info,
const struct region_device *source_rdev)
{ {
return cse_is_rw_status_valid(cse_bp_info); struct fw_version cse_cbfs_rw_ver;
const struct fw_version *cse_rw_ver;
if (!cse_get_cbfs_rw_version(source_rdev, &cse_cbfs_rw_ver))
return false;
printk(BIOS_DEBUG, "cse_lite: CSE CBFS RW version : %d.%d.%d.%d\n",
cse_cbfs_rw_ver.major,
cse_cbfs_rw_ver.minor,
cse_cbfs_rw_ver.hotfix,
cse_cbfs_rw_ver.build);
cse_rw_ver = cse_get_rw_version(cse_bp_info);
if (cse_cbfs_rw_ver.major != cse_rw_ver->major)
return cse_cbfs_rw_ver.major - cse_rw_ver->major;
else if (cse_cbfs_rw_ver.minor != cse_rw_ver->minor)
return cse_cbfs_rw_ver.minor - cse_rw_ver->minor;
else if (cse_cbfs_rw_ver.hotfix != cse_rw_ver->hotfix)
return cse_cbfs_rw_ver.hotfix - cse_rw_ver->hotfix;
else
return cse_cbfs_rw_ver.build - cse_rw_ver->build;
}
static bool cse_erase_rw_region(const struct region_device *target_rdev)
{
if (rdev_eraseat(target_rdev, 0, region_device_sz(target_rdev)) < 0) {
printk(BIOS_ERR, "cse_lite: CSE RW partition could not be erased\n");
return false;
}
return true;
}
static bool cse_copy_rw(const struct region_device *target_rdev, const void *buf, size_t offset,
size_t size)
{
if (rdev_writeat(target_rdev, buf, offset, size) < 0) {
printk(BIOS_ERR, "cse_lite: Failed to update CSE firmware\n");
return false;
}
return true;
}
static bool cse_is_rw_version_latest(const struct cse_bp_info *cse_bp_info,
const struct region_device *source_rdev)
{
return !cse_check_version_mismatch(cse_bp_info, source_rdev);
}
static bool cse_is_update_required(const struct cse_bp_info *cse_bp_info,
const struct region_device *source_rdev, struct region_device *target_rdev)
{
return (!cse_is_rw_bp_sign_valid(target_rdev) ||
!cse_is_rw_version_latest(cse_bp_info, source_rdev));
}
static bool cse_write_rw_region(const struct region_device *target_rdev,
const struct region_device *source_rdev)
{
void *cse_cbfs_rw = rdev_mmap(source_rdev, CSE_RW_VERSION_SZ,
region_device_sz(source_rdev) - CSE_RW_VERSION_SZ);
/* Points to CSE CBFS RW image after boot partition signature */
uint8_t *cse_cbfs_rw_wo_sign = (uint8_t *)cse_cbfs_rw + CSE_RW_SIGN_SIZE;
/* Size of CSE CBFS RW image without boot partition signature */
uint32_t cse_cbfs_rw_wo_sign_sz = region_device_sz(source_rdev) -
(CSE_RW_VERSION_SZ + CSE_RW_SIGN_SIZE);
/* Update except CSE RW signature */
if (!cse_copy_rw(target_rdev, cse_cbfs_rw_wo_sign, CSE_RW_SIGN_SIZE,
cse_cbfs_rw_wo_sign_sz))
goto exit_rw_update;
/* Update CSE RW signature to indicate update is complete */
if (!cse_copy_rw(target_rdev, (void *)cse_cbfs_rw, 0, CSE_RW_SIGN_SIZE))
goto exit_rw_update;
rdev_munmap(source_rdev, cse_cbfs_rw_wo_sign);
return true;
exit_rw_update:
rdev_munmap(source_rdev, cse_cbfs_rw_wo_sign);
return false;
}
static bool cse_update_rw(const struct cse_bp_info *cse_bp_info,
const struct region_device *source_rdev, struct region_device *target_rdev)
{
if (!cse_erase_rw_region(target_rdev))
return false;
if (!cse_write_rw_region(target_rdev, source_rdev))
return false;
printk(BIOS_INFO, "cse_lite: CSE RW Update Successful\n");
return true;
}
static bool cse_prep_for_rw_update(const struct cse_bp_info *cse_bp_info)
{
/*
* To set CSE's operation mode to HMRFPO mode:
* 1. Ensure CSE to boot from RO(BP1)
* 2. Send HMRFPO_ENABLE command to CSE
*/
if (!cse_boot_to_ro(cse_bp_info))
return false;
return cse_hmrfpo_enable();
}
static uint8_t cse_trigger_fw_update(const struct cse_bp_info *cse_bp_info,
const struct region_device *source_rdev, struct region_device *target_rdev)
{
if (!cse_prep_for_rw_update(cse_bp_info))
return CSE_LITE_SKU_COMMUNICATION_ERROR;
if (!cse_update_rw(cse_bp_info, source_rdev, target_rdev))
return CSE_LITE_SKU_FW_UPDATE_ERROR;
return 0;
}
static uint8_t cse_fw_update(const struct cse_bp_info *cse_bp_info,
const struct region_device *source_rdev)
{
struct region_device target_rdev;
if (!cse_get_target_rdev(cse_bp_info, &target_rdev)) {
printk(BIOS_ERR, "cse_lite: Failed to get CSE RW Partition\n");
return CSE_LITE_SKU_RW_ACCESS_ERROR;
}
if (cse_is_update_required(cse_bp_info, source_rdev, &target_rdev)) {
printk(BIOS_DEBUG, "cse_lite: CSE RW update is initiated\n");
return cse_trigger_fw_update(cse_bp_info, source_rdev, &target_rdev);
}
if (!cse_is_rw_bp_status_valid(cse_bp_info))
return CSE_LITE_SKU_RW_JUMP_ERROR;
return 0;
} }
void cse_fw_sync(void *unused) void cse_fw_sync(void *unused)
@ -350,9 +630,13 @@ void cse_fw_sync(void *unused)
cse_trigger_recovery(CSE_LITE_SKU_COMMUNICATION_ERROR); cse_trigger_recovery(CSE_LITE_SKU_COMMUNICATION_ERROR);
} }
if (!cse_is_rw_info_valid(&cse_bp_info.bp_info)) { /* If RW blob is present in CBFS, then trigger CSE firmware update */
printk(BIOS_ERR, "cse_lite: CSE RW partition is not valid\n"); uint8_t rv;
cse_trigger_recovery(CSE_LITE_SKU_RW_JUMP_ERROR); struct region_device source_rdev;
if (cse_get_cbfs_rdev(&source_rdev)) {
rv = cse_fw_update(&cse_bp_info.bp_info, &source_rdev);
if (rv)
cse_trigger_recovery(rv);
} }
if (!cse_boot_to_rw(&cse_bp_info.bp_info)) { if (!cse_boot_to_rw(&cse_bp_info.bp_info)) {