162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright © 2017-2019 Intel Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include "intel_wopcm.h" 762306a36Sopenharmony_ci#include "i915_drv.h" 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/** 1062306a36Sopenharmony_ci * DOC: WOPCM Layout 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * The layout of the WOPCM will be fixed after writing to GuC WOPCM size and 1362306a36Sopenharmony_ci * offset registers whose values are calculated and determined by HuC/GuC 1462306a36Sopenharmony_ci * firmware size and set of hardware requirements/restrictions as shown below: 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * :: 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * +=========> +====================+ <== WOPCM Top 1962306a36Sopenharmony_ci * ^ | HW contexts RSVD | 2062306a36Sopenharmony_ci * | +===> +====================+ <== GuC WOPCM Top 2162306a36Sopenharmony_ci * | ^ | | 2262306a36Sopenharmony_ci * | | | | 2362306a36Sopenharmony_ci * | | | | 2462306a36Sopenharmony_ci * | GuC | | 2562306a36Sopenharmony_ci * | WOPCM | | 2662306a36Sopenharmony_ci * | Size +--------------------+ 2762306a36Sopenharmony_ci * WOPCM | | GuC FW RSVD | 2862306a36Sopenharmony_ci * | | +--------------------+ 2962306a36Sopenharmony_ci * | | | GuC Stack RSVD | 3062306a36Sopenharmony_ci * | | +------------------- + 3162306a36Sopenharmony_ci * | v | GuC WOPCM RSVD | 3262306a36Sopenharmony_ci * | +===> +====================+ <== GuC WOPCM base 3362306a36Sopenharmony_ci * | | WOPCM RSVD | 3462306a36Sopenharmony_ci * | +------------------- + <== HuC Firmware Top 3562306a36Sopenharmony_ci * v | HuC FW | 3662306a36Sopenharmony_ci * +=========> +====================+ <== WOPCM Base 3762306a36Sopenharmony_ci * 3862306a36Sopenharmony_ci * GuC accessible WOPCM starts at GuC WOPCM base and ends at GuC WOPCM top. 3962306a36Sopenharmony_ci * The top part of the WOPCM is reserved for hardware contexts (e.g. RC6 4062306a36Sopenharmony_ci * context). 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Default WOPCM size is 2MB from Gen11, 1MB on previous platforms */ 4462306a36Sopenharmony_ci#define GEN11_WOPCM_SIZE SZ_2M 4562306a36Sopenharmony_ci#define GEN9_WOPCM_SIZE SZ_1M 4662306a36Sopenharmony_ci#define MAX_WOPCM_SIZE SZ_8M 4762306a36Sopenharmony_ci/* 16KB WOPCM (RSVD WOPCM) is reserved from HuC firmware top. */ 4862306a36Sopenharmony_ci#define WOPCM_RESERVED_SIZE SZ_16K 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* 16KB reserved at the beginning of GuC WOPCM. */ 5162306a36Sopenharmony_ci#define GUC_WOPCM_RESERVED SZ_16K 5262306a36Sopenharmony_ci/* 8KB from GUC_WOPCM_RESERVED is reserved for GuC stack. */ 5362306a36Sopenharmony_ci#define GUC_WOPCM_STACK_RESERVED SZ_8K 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* GuC WOPCM Offset value needs to be aligned to 16KB. */ 5662306a36Sopenharmony_ci#define GUC_WOPCM_OFFSET_ALIGNMENT (1UL << GUC_WOPCM_OFFSET_SHIFT) 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 24KB at the end of WOPCM is reserved for RC6 CTX on BXT. */ 5962306a36Sopenharmony_ci#define BXT_WOPCM_RC6_CTX_RESERVED (SZ_16K + SZ_8K) 6062306a36Sopenharmony_ci/* 36KB WOPCM reserved at the end of WOPCM on ICL. */ 6162306a36Sopenharmony_ci#define ICL_WOPCM_HW_CTX_RESERVED (SZ_32K + SZ_4K) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* 128KB from GUC_WOPCM_RESERVED is reserved for FW on Gen9. */ 6462306a36Sopenharmony_ci#define GEN9_GUC_FW_RESERVED SZ_128K 6562306a36Sopenharmony_ci#define GEN9_GUC_WOPCM_OFFSET (GUC_WOPCM_RESERVED + GEN9_GUC_FW_RESERVED) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic inline struct intel_gt *wopcm_to_gt(struct intel_wopcm *wopcm) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci return container_of(wopcm, struct intel_gt, wopcm); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/** 7362306a36Sopenharmony_ci * intel_wopcm_init_early() - Early initialization of the WOPCM. 7462306a36Sopenharmony_ci * @wopcm: pointer to intel_wopcm. 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * Setup the size of WOPCM which will be used by later on WOPCM partitioning. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_civoid intel_wopcm_init_early(struct intel_wopcm *wopcm) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci struct intel_gt *gt = wopcm_to_gt(wopcm); 8162306a36Sopenharmony_ci struct drm_i915_private *i915 = gt->i915; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (!HAS_GT_UC(i915)) 8462306a36Sopenharmony_ci return; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (GRAPHICS_VER(i915) >= 11) 8762306a36Sopenharmony_ci wopcm->size = GEN11_WOPCM_SIZE; 8862306a36Sopenharmony_ci else 8962306a36Sopenharmony_ci wopcm->size = GEN9_WOPCM_SIZE; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci drm_dbg(&i915->drm, "WOPCM: %uK\n", wopcm->size / 1024); 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_cistatic u32 context_reserved_size(struct drm_i915_private *i915) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci if (IS_GEN9_LP(i915)) 9762306a36Sopenharmony_ci return BXT_WOPCM_RC6_CTX_RESERVED; 9862306a36Sopenharmony_ci else if (GRAPHICS_VER(i915) >= 11) 9962306a36Sopenharmony_ci return ICL_WOPCM_HW_CTX_RESERVED; 10062306a36Sopenharmony_ci else 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic bool gen9_check_dword_gap(struct drm_i915_private *i915, 10562306a36Sopenharmony_ci u32 guc_wopcm_base, u32 guc_wopcm_size) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci u32 offset; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci /* 11062306a36Sopenharmony_ci * GuC WOPCM size shall be at least a dword larger than the offset from 11162306a36Sopenharmony_ci * WOPCM base (GuC WOPCM offset from WOPCM base + GEN9_GUC_WOPCM_OFFSET) 11262306a36Sopenharmony_ci * due to hardware limitation on Gen9. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci offset = guc_wopcm_base + GEN9_GUC_WOPCM_OFFSET; 11562306a36Sopenharmony_ci if (offset > guc_wopcm_size || 11662306a36Sopenharmony_ci (guc_wopcm_size - offset) < sizeof(u32)) { 11762306a36Sopenharmony_ci drm_err(&i915->drm, 11862306a36Sopenharmony_ci "WOPCM: invalid GuC region size: %uK < %uK\n", 11962306a36Sopenharmony_ci guc_wopcm_size / SZ_1K, 12062306a36Sopenharmony_ci (u32)(offset + sizeof(u32)) / SZ_1K); 12162306a36Sopenharmony_ci return false; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return true; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic bool gen9_check_huc_fw_fits(struct drm_i915_private *i915, 12862306a36Sopenharmony_ci u32 guc_wopcm_size, u32 huc_fw_size) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci /* 13162306a36Sopenharmony_ci * On Gen9, hardware requires the total available GuC WOPCM 13262306a36Sopenharmony_ci * size to be larger than or equal to HuC firmware size. Otherwise, 13362306a36Sopenharmony_ci * firmware uploading would fail. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_ci if (huc_fw_size > guc_wopcm_size - GUC_WOPCM_RESERVED) { 13662306a36Sopenharmony_ci drm_err(&i915->drm, "WOPCM: no space for %s: %uK < %uK\n", 13762306a36Sopenharmony_ci intel_uc_fw_type_repr(INTEL_UC_FW_TYPE_HUC), 13862306a36Sopenharmony_ci (guc_wopcm_size - GUC_WOPCM_RESERVED) / SZ_1K, 13962306a36Sopenharmony_ci huc_fw_size / 1024); 14062306a36Sopenharmony_ci return false; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return true; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic bool check_hw_restrictions(struct drm_i915_private *i915, 14762306a36Sopenharmony_ci u32 guc_wopcm_base, u32 guc_wopcm_size, 14862306a36Sopenharmony_ci u32 huc_fw_size) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci if (GRAPHICS_VER(i915) == 9 && !gen9_check_dword_gap(i915, guc_wopcm_base, 15162306a36Sopenharmony_ci guc_wopcm_size)) 15262306a36Sopenharmony_ci return false; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (GRAPHICS_VER(i915) == 9 && 15562306a36Sopenharmony_ci !gen9_check_huc_fw_fits(i915, guc_wopcm_size, huc_fw_size)) 15662306a36Sopenharmony_ci return false; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return true; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic bool __check_layout(struct intel_gt *gt, u32 wopcm_size, 16262306a36Sopenharmony_ci u32 guc_wopcm_base, u32 guc_wopcm_size, 16362306a36Sopenharmony_ci u32 guc_fw_size, u32 huc_fw_size) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci struct drm_i915_private *i915 = gt->i915; 16662306a36Sopenharmony_ci const u32 ctx_rsvd = context_reserved_size(i915); 16762306a36Sopenharmony_ci u32 size; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci size = wopcm_size - ctx_rsvd; 17062306a36Sopenharmony_ci if (unlikely(range_overflows(guc_wopcm_base, guc_wopcm_size, size))) { 17162306a36Sopenharmony_ci drm_err(&i915->drm, 17262306a36Sopenharmony_ci "WOPCM: invalid GuC region layout: %uK + %uK > %uK\n", 17362306a36Sopenharmony_ci guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K, 17462306a36Sopenharmony_ci size / SZ_1K); 17562306a36Sopenharmony_ci return false; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci size = guc_fw_size + GUC_WOPCM_RESERVED + GUC_WOPCM_STACK_RESERVED; 17962306a36Sopenharmony_ci if (unlikely(guc_wopcm_size < size)) { 18062306a36Sopenharmony_ci drm_err(&i915->drm, "WOPCM: no space for %s: %uK < %uK\n", 18162306a36Sopenharmony_ci intel_uc_fw_type_repr(INTEL_UC_FW_TYPE_GUC), 18262306a36Sopenharmony_ci guc_wopcm_size / SZ_1K, size / SZ_1K); 18362306a36Sopenharmony_ci return false; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (intel_uc_supports_huc(>->uc)) { 18762306a36Sopenharmony_ci size = huc_fw_size + WOPCM_RESERVED_SIZE; 18862306a36Sopenharmony_ci if (unlikely(guc_wopcm_base < size)) { 18962306a36Sopenharmony_ci drm_err(&i915->drm, "WOPCM: no space for %s: %uK < %uK\n", 19062306a36Sopenharmony_ci intel_uc_fw_type_repr(INTEL_UC_FW_TYPE_HUC), 19162306a36Sopenharmony_ci guc_wopcm_base / SZ_1K, size / SZ_1K); 19262306a36Sopenharmony_ci return false; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci return check_hw_restrictions(i915, guc_wopcm_base, guc_wopcm_size, 19762306a36Sopenharmony_ci huc_fw_size); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic bool __wopcm_regs_locked(struct intel_uncore *uncore, 20162306a36Sopenharmony_ci u32 *guc_wopcm_base, u32 *guc_wopcm_size) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci u32 reg_base = intel_uncore_read(uncore, DMA_GUC_WOPCM_OFFSET); 20462306a36Sopenharmony_ci u32 reg_size = intel_uncore_read(uncore, GUC_WOPCM_SIZE); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (!(reg_size & GUC_WOPCM_SIZE_LOCKED) || 20762306a36Sopenharmony_ci !(reg_base & GUC_WOPCM_OFFSET_VALID)) 20862306a36Sopenharmony_ci return false; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci *guc_wopcm_base = reg_base & GUC_WOPCM_OFFSET_MASK; 21162306a36Sopenharmony_ci *guc_wopcm_size = reg_size & GUC_WOPCM_SIZE_MASK; 21262306a36Sopenharmony_ci return true; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic bool __wopcm_regs_writable(struct intel_uncore *uncore) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci if (!HAS_GUC_DEPRIVILEGE(uncore->i915)) 21862306a36Sopenharmony_ci return true; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci return intel_uncore_read(uncore, GUC_SHIM_CONTROL2) & GUC_IS_PRIVILEGED; 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci/** 22462306a36Sopenharmony_ci * intel_wopcm_init() - Initialize the WOPCM structure. 22562306a36Sopenharmony_ci * @wopcm: pointer to intel_wopcm. 22662306a36Sopenharmony_ci * 22762306a36Sopenharmony_ci * This function will partition WOPCM space based on GuC and HuC firmware sizes 22862306a36Sopenharmony_ci * and will allocate max remaining for use by GuC. This function will also 22962306a36Sopenharmony_ci * enforce platform dependent hardware restrictions on GuC WOPCM offset and 23062306a36Sopenharmony_ci * size. It will fail the WOPCM init if any of these checks fail, so that the 23162306a36Sopenharmony_ci * following WOPCM registers setup and GuC firmware uploading would be aborted. 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_civoid intel_wopcm_init(struct intel_wopcm *wopcm) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct intel_gt *gt = wopcm_to_gt(wopcm); 23662306a36Sopenharmony_ci struct drm_i915_private *i915 = gt->i915; 23762306a36Sopenharmony_ci u32 guc_fw_size = intel_uc_fw_get_upload_size(>->uc.guc.fw); 23862306a36Sopenharmony_ci u32 huc_fw_size = intel_uc_fw_get_upload_size(>->uc.huc.fw); 23962306a36Sopenharmony_ci u32 ctx_rsvd = context_reserved_size(i915); 24062306a36Sopenharmony_ci u32 wopcm_size = wopcm->size; 24162306a36Sopenharmony_ci u32 guc_wopcm_base; 24262306a36Sopenharmony_ci u32 guc_wopcm_size; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (!guc_fw_size) 24562306a36Sopenharmony_ci return; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci GEM_BUG_ON(!wopcm_size); 24862306a36Sopenharmony_ci GEM_BUG_ON(wopcm->guc.base); 24962306a36Sopenharmony_ci GEM_BUG_ON(wopcm->guc.size); 25062306a36Sopenharmony_ci GEM_BUG_ON(guc_fw_size >= wopcm_size); 25162306a36Sopenharmony_ci GEM_BUG_ON(huc_fw_size >= wopcm_size); 25262306a36Sopenharmony_ci GEM_BUG_ON(ctx_rsvd + WOPCM_RESERVED_SIZE >= wopcm_size); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (i915_inject_probe_failure(i915)) 25562306a36Sopenharmony_ci return; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (__wopcm_regs_locked(gt->uncore, &guc_wopcm_base, &guc_wopcm_size)) { 25862306a36Sopenharmony_ci drm_dbg(&i915->drm, "GuC WOPCM is already locked [%uK, %uK)\n", 25962306a36Sopenharmony_ci guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K); 26062306a36Sopenharmony_ci /* 26162306a36Sopenharmony_ci * Note that to keep things simple (i.e. avoid different 26262306a36Sopenharmony_ci * defines per platform) our WOPCM math doesn't always use the 26362306a36Sopenharmony_ci * actual WOPCM size, but a value that is less or equal to it. 26462306a36Sopenharmony_ci * This is perfectly fine when i915 programs the registers, but 26562306a36Sopenharmony_ci * on platforms with GuC deprivilege the registers are not 26662306a36Sopenharmony_ci * writable from i915 and are instead pre-programmed by the 26762306a36Sopenharmony_ci * bios/IFWI, so there might be a mismatch of sizes. 26862306a36Sopenharmony_ci * Instead of handling the size difference, we trust that the 26962306a36Sopenharmony_ci * programmed values make sense and disable the relevant check 27062306a36Sopenharmony_ci * by using the maximum possible WOPCM size in the verification 27162306a36Sopenharmony_ci * math. In the extremely unlikely case that the registers 27262306a36Sopenharmony_ci * were pre-programmed with an invalid value, we will still 27362306a36Sopenharmony_ci * gracefully fail later during the GuC/HuC dma. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci if (!__wopcm_regs_writable(gt->uncore)) 27662306a36Sopenharmony_ci wopcm_size = MAX_WOPCM_SIZE; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci goto check; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* 28262306a36Sopenharmony_ci * On platforms with a media GT, the WOPCM is partitioned between the 28362306a36Sopenharmony_ci * two GTs, so we would have to take that into account when doing the 28462306a36Sopenharmony_ci * math below. There is also a new section reserved for the GSC context 28562306a36Sopenharmony_ci * that would have to be factored in. However, all platforms with a 28662306a36Sopenharmony_ci * media GT also have GuC depriv enabled, so the WOPCM regs are 28762306a36Sopenharmony_ci * pre-locked and therefore we don't have to do the math ourselves. 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ci if (unlikely(i915->media_gt)) { 29062306a36Sopenharmony_ci drm_err(&i915->drm, "Unlocked WOPCM regs with media GT\n"); 29162306a36Sopenharmony_ci return; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* 29562306a36Sopenharmony_ci * Aligned value of guc_wopcm_base will determine available WOPCM space 29662306a36Sopenharmony_ci * for HuC firmware and mandatory reserved area. 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ci guc_wopcm_base = huc_fw_size + WOPCM_RESERVED_SIZE; 29962306a36Sopenharmony_ci guc_wopcm_base = ALIGN(guc_wopcm_base, GUC_WOPCM_OFFSET_ALIGNMENT); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci /* 30262306a36Sopenharmony_ci * Need to clamp guc_wopcm_base now to make sure the following math is 30362306a36Sopenharmony_ci * correct. Formal check of whole WOPCM layout will be done below. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_ci guc_wopcm_base = min(guc_wopcm_base, wopcm_size - ctx_rsvd); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* Aligned remainings of usable WOPCM space can be assigned to GuC. */ 30862306a36Sopenharmony_ci guc_wopcm_size = wopcm_size - ctx_rsvd - guc_wopcm_base; 30962306a36Sopenharmony_ci guc_wopcm_size &= GUC_WOPCM_SIZE_MASK; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci drm_dbg(&i915->drm, "Calculated GuC WOPCM [%uK, %uK)\n", 31262306a36Sopenharmony_ci guc_wopcm_base / SZ_1K, guc_wopcm_size / SZ_1K); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cicheck: 31562306a36Sopenharmony_ci if (__check_layout(gt, wopcm_size, guc_wopcm_base, guc_wopcm_size, 31662306a36Sopenharmony_ci guc_fw_size, huc_fw_size)) { 31762306a36Sopenharmony_ci wopcm->guc.base = guc_wopcm_base; 31862306a36Sopenharmony_ci wopcm->guc.size = guc_wopcm_size; 31962306a36Sopenharmony_ci GEM_BUG_ON(!wopcm->guc.base); 32062306a36Sopenharmony_ci GEM_BUG_ON(!wopcm->guc.size); 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci} 323