162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright 2019 Advanced Micro Devices, Inc.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
562306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
662306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
762306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
862306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
962306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
1262306a36Sopenharmony_ci * all copies or substantial portions of the Software.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1562306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1662306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1762306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
1862306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1962306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2062306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "amdgpu.h"
2562306a36Sopenharmony_ci#include "umc_v6_7.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int amdgpu_umc_convert_error_address(struct amdgpu_device *adev,
2862306a36Sopenharmony_ci				    struct ras_err_data *err_data, uint64_t err_addr,
2962306a36Sopenharmony_ci				    uint32_t ch_inst, uint32_t umc_inst)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	switch (adev->ip_versions[UMC_HWIP][0]) {
3262306a36Sopenharmony_ci	case IP_VERSION(6, 7, 0):
3362306a36Sopenharmony_ci		umc_v6_7_convert_error_address(adev,
3462306a36Sopenharmony_ci				err_data, err_addr, ch_inst, umc_inst);
3562306a36Sopenharmony_ci		break;
3662306a36Sopenharmony_ci	default:
3762306a36Sopenharmony_ci		dev_warn(adev->dev,
3862306a36Sopenharmony_ci			 "UMC address to Physical address translation is not supported\n");
3962306a36Sopenharmony_ci		return AMDGPU_RAS_FAIL;
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return AMDGPU_RAS_SUCCESS;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ciint amdgpu_umc_page_retirement_mca(struct amdgpu_device *adev,
4662306a36Sopenharmony_ci			uint64_t err_addr, uint32_t ch_inst, uint32_t umc_inst)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct ras_err_data err_data = {0, 0, 0, NULL};
4962306a36Sopenharmony_ci	int ret = AMDGPU_RAS_FAIL;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	err_data.err_addr =
5262306a36Sopenharmony_ci		kcalloc(adev->umc.max_ras_err_cnt_per_query,
5362306a36Sopenharmony_ci			sizeof(struct eeprom_table_record), GFP_KERNEL);
5462306a36Sopenharmony_ci	if (!err_data.err_addr) {
5562306a36Sopenharmony_ci		dev_warn(adev->dev,
5662306a36Sopenharmony_ci			"Failed to alloc memory for umc error record in MCA notifier!\n");
5762306a36Sopenharmony_ci		return AMDGPU_RAS_FAIL;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/*
6162306a36Sopenharmony_ci	 * Translate UMC channel address to Physical address
6262306a36Sopenharmony_ci	 */
6362306a36Sopenharmony_ci	ret = amdgpu_umc_convert_error_address(adev, &err_data, err_addr,
6462306a36Sopenharmony_ci					ch_inst, umc_inst);
6562306a36Sopenharmony_ci	if (ret)
6662306a36Sopenharmony_ci		goto out;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (amdgpu_bad_page_threshold != 0) {
6962306a36Sopenharmony_ci		amdgpu_ras_add_bad_pages(adev, err_data.err_addr,
7062306a36Sopenharmony_ci						err_data.err_addr_cnt);
7162306a36Sopenharmony_ci		amdgpu_ras_save_bad_pages(adev, NULL);
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ciout:
7562306a36Sopenharmony_ci	kfree(err_data.err_addr);
7662306a36Sopenharmony_ci	return ret;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic int amdgpu_umc_do_page_retirement(struct amdgpu_device *adev,
8062306a36Sopenharmony_ci		void *ras_error_status,
8162306a36Sopenharmony_ci		struct amdgpu_iv_entry *entry,
8262306a36Sopenharmony_ci		bool reset)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct ras_err_data *err_data = (struct ras_err_data *)ras_error_status;
8562306a36Sopenharmony_ci	struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
8662306a36Sopenharmony_ci	int ret = 0;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	kgd2kfd_set_sram_ecc_flag(adev->kfd.dev);
8962306a36Sopenharmony_ci	ret = amdgpu_dpm_get_ecc_info(adev, (void *)&(con->umc_ecc));
9062306a36Sopenharmony_ci	if (ret == -EOPNOTSUPP) {
9162306a36Sopenharmony_ci		if (adev->umc.ras && adev->umc.ras->ras_block.hw_ops &&
9262306a36Sopenharmony_ci		    adev->umc.ras->ras_block.hw_ops->query_ras_error_count)
9362306a36Sopenharmony_ci		    adev->umc.ras->ras_block.hw_ops->query_ras_error_count(adev, ras_error_status);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci		if (adev->umc.ras && adev->umc.ras->ras_block.hw_ops &&
9662306a36Sopenharmony_ci		    adev->umc.ras->ras_block.hw_ops->query_ras_error_address &&
9762306a36Sopenharmony_ci		    adev->umc.max_ras_err_cnt_per_query) {
9862306a36Sopenharmony_ci			err_data->err_addr =
9962306a36Sopenharmony_ci				kcalloc(adev->umc.max_ras_err_cnt_per_query,
10062306a36Sopenharmony_ci					sizeof(struct eeprom_table_record), GFP_KERNEL);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci			/* still call query_ras_error_address to clear error status
10362306a36Sopenharmony_ci			 * even NOMEM error is encountered
10462306a36Sopenharmony_ci			 */
10562306a36Sopenharmony_ci			if(!err_data->err_addr)
10662306a36Sopenharmony_ci				dev_warn(adev->dev, "Failed to alloc memory for "
10762306a36Sopenharmony_ci						"umc error address record!\n");
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci			/* umc query_ras_error_address is also responsible for clearing
11062306a36Sopenharmony_ci			 * error status
11162306a36Sopenharmony_ci			 */
11262306a36Sopenharmony_ci			adev->umc.ras->ras_block.hw_ops->query_ras_error_address(adev, ras_error_status);
11362306a36Sopenharmony_ci		}
11462306a36Sopenharmony_ci	} else if (!ret) {
11562306a36Sopenharmony_ci		if (adev->umc.ras &&
11662306a36Sopenharmony_ci		    adev->umc.ras->ecc_info_query_ras_error_count)
11762306a36Sopenharmony_ci		    adev->umc.ras->ecc_info_query_ras_error_count(adev, ras_error_status);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		if (adev->umc.ras &&
12062306a36Sopenharmony_ci		    adev->umc.ras->ecc_info_query_ras_error_address &&
12162306a36Sopenharmony_ci		    adev->umc.max_ras_err_cnt_per_query) {
12262306a36Sopenharmony_ci			err_data->err_addr =
12362306a36Sopenharmony_ci				kcalloc(adev->umc.max_ras_err_cnt_per_query,
12462306a36Sopenharmony_ci					sizeof(struct eeprom_table_record), GFP_KERNEL);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci			/* still call query_ras_error_address to clear error status
12762306a36Sopenharmony_ci			 * even NOMEM error is encountered
12862306a36Sopenharmony_ci			 */
12962306a36Sopenharmony_ci			if(!err_data->err_addr)
13062306a36Sopenharmony_ci				dev_warn(adev->dev, "Failed to alloc memory for "
13162306a36Sopenharmony_ci						"umc error address record!\n");
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci			/* umc query_ras_error_address is also responsible for clearing
13462306a36Sopenharmony_ci			 * error status
13562306a36Sopenharmony_ci			 */
13662306a36Sopenharmony_ci			adev->umc.ras->ecc_info_query_ras_error_address(adev, ras_error_status);
13762306a36Sopenharmony_ci		}
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/* only uncorrectable error needs gpu reset */
14162306a36Sopenharmony_ci	if (err_data->ue_count) {
14262306a36Sopenharmony_ci		dev_info(adev->dev, "%ld uncorrectable hardware errors "
14362306a36Sopenharmony_ci				"detected in UMC block\n",
14462306a36Sopenharmony_ci				err_data->ue_count);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		if ((amdgpu_bad_page_threshold != 0) &&
14762306a36Sopenharmony_ci			err_data->err_addr_cnt) {
14862306a36Sopenharmony_ci			amdgpu_ras_add_bad_pages(adev, err_data->err_addr,
14962306a36Sopenharmony_ci						err_data->err_addr_cnt);
15062306a36Sopenharmony_ci			amdgpu_ras_save_bad_pages(adev, &(err_data->ue_count));
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci			amdgpu_dpm_send_hbm_bad_pages_num(adev, con->eeprom_control.ras_num_recs);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci			if (con->update_channel_flag == true) {
15562306a36Sopenharmony_ci				amdgpu_dpm_send_hbm_bad_channel_flag(adev, con->eeprom_control.bad_channel_bitmap);
15662306a36Sopenharmony_ci				con->update_channel_flag = false;
15762306a36Sopenharmony_ci			}
15862306a36Sopenharmony_ci		}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		if (reset)
16162306a36Sopenharmony_ci			amdgpu_ras_reset_gpu(adev);
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	kfree(err_data->err_addr);
16562306a36Sopenharmony_ci	return AMDGPU_RAS_SUCCESS;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ciint amdgpu_umc_poison_handler(struct amdgpu_device *adev, bool reset)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	int ret = AMDGPU_RAS_SUCCESS;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (adev->gmc.xgmi.connected_to_cpu ||
17362306a36Sopenharmony_ci		adev->gmc.is_app_apu) {
17462306a36Sopenharmony_ci		if (reset) {
17562306a36Sopenharmony_ci			/* MCA poison handler is only responsible for GPU reset,
17662306a36Sopenharmony_ci			 * let MCA notifier do page retirement.
17762306a36Sopenharmony_ci			 */
17862306a36Sopenharmony_ci			kgd2kfd_set_sram_ecc_flag(adev->kfd.dev);
17962306a36Sopenharmony_ci			amdgpu_ras_reset_gpu(adev);
18062306a36Sopenharmony_ci		}
18162306a36Sopenharmony_ci		return ret;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (!amdgpu_sriov_vf(adev)) {
18562306a36Sopenharmony_ci		struct ras_err_data err_data = {0, 0, 0, NULL};
18662306a36Sopenharmony_ci		struct ras_common_if head = {
18762306a36Sopenharmony_ci			.block = AMDGPU_RAS_BLOCK__UMC,
18862306a36Sopenharmony_ci		};
18962306a36Sopenharmony_ci		struct ras_manager *obj = amdgpu_ras_find_obj(adev, &head);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci		ret = amdgpu_umc_do_page_retirement(adev, &err_data, NULL, reset);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci		if (ret == AMDGPU_RAS_SUCCESS && obj) {
19462306a36Sopenharmony_ci			obj->err_data.ue_count += err_data.ue_count;
19562306a36Sopenharmony_ci			obj->err_data.ce_count += err_data.ce_count;
19662306a36Sopenharmony_ci		}
19762306a36Sopenharmony_ci	} else {
19862306a36Sopenharmony_ci		if (adev->virt.ops && adev->virt.ops->ras_poison_handler)
19962306a36Sopenharmony_ci			adev->virt.ops->ras_poison_handler(adev);
20062306a36Sopenharmony_ci		else
20162306a36Sopenharmony_ci			dev_warn(adev->dev,
20262306a36Sopenharmony_ci				"No ras_poison_handler interface in SRIOV!\n");
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return ret;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ciint amdgpu_umc_process_ras_data_cb(struct amdgpu_device *adev,
20962306a36Sopenharmony_ci		void *ras_error_status,
21062306a36Sopenharmony_ci		struct amdgpu_iv_entry *entry)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	return amdgpu_umc_do_page_retirement(adev, ras_error_status, entry, true);
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ciint amdgpu_umc_ras_sw_init(struct amdgpu_device *adev)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	int err;
21862306a36Sopenharmony_ci	struct amdgpu_umc_ras *ras;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (!adev->umc.ras)
22162306a36Sopenharmony_ci		return 0;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	ras = adev->umc.ras;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	err = amdgpu_ras_register_ras_block(adev, &ras->ras_block);
22662306a36Sopenharmony_ci	if (err) {
22762306a36Sopenharmony_ci		dev_err(adev->dev, "Failed to register umc ras block!\n");
22862306a36Sopenharmony_ci		return err;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	strcpy(adev->umc.ras->ras_block.ras_comm.name, "umc");
23262306a36Sopenharmony_ci	ras->ras_block.ras_comm.block = AMDGPU_RAS_BLOCK__UMC;
23362306a36Sopenharmony_ci	ras->ras_block.ras_comm.type = AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE;
23462306a36Sopenharmony_ci	adev->umc.ras_if = &ras->ras_block.ras_comm;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	if (!ras->ras_block.ras_late_init)
23762306a36Sopenharmony_ci		ras->ras_block.ras_late_init = amdgpu_umc_ras_late_init;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (!ras->ras_block.ras_cb)
24062306a36Sopenharmony_ci		ras->ras_block.ras_cb = amdgpu_umc_process_ras_data_cb;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	return 0;
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ciint amdgpu_umc_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	int r;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	r = amdgpu_ras_block_late_init(adev, ras_block);
25062306a36Sopenharmony_ci	if (r)
25162306a36Sopenharmony_ci		return r;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	if (amdgpu_ras_is_supported(adev, ras_block->block)) {
25462306a36Sopenharmony_ci		r = amdgpu_irq_get(adev, &adev->gmc.ecc_irq, 0);
25562306a36Sopenharmony_ci		if (r)
25662306a36Sopenharmony_ci			goto late_fini;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* ras init of specific umc version */
26062306a36Sopenharmony_ci	if (adev->umc.ras &&
26162306a36Sopenharmony_ci	    adev->umc.ras->err_cnt_init)
26262306a36Sopenharmony_ci		adev->umc.ras->err_cnt_init(adev);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	return 0;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cilate_fini:
26762306a36Sopenharmony_ci	amdgpu_ras_block_late_fini(adev, ras_block);
26862306a36Sopenharmony_ci	return r;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ciint amdgpu_umc_process_ecc_irq(struct amdgpu_device *adev,
27262306a36Sopenharmony_ci		struct amdgpu_irq_src *source,
27362306a36Sopenharmony_ci		struct amdgpu_iv_entry *entry)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct ras_common_if *ras_if = adev->umc.ras_if;
27662306a36Sopenharmony_ci	struct ras_dispatch_if ih_data = {
27762306a36Sopenharmony_ci		.entry = entry,
27862306a36Sopenharmony_ci	};
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (!ras_if)
28162306a36Sopenharmony_ci		return 0;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	ih_data.head = *ras_if;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	amdgpu_ras_interrupt_dispatch(adev, &ih_data);
28662306a36Sopenharmony_ci	return 0;
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_civoid amdgpu_umc_fill_error_record(struct ras_err_data *err_data,
29062306a36Sopenharmony_ci		uint64_t err_addr,
29162306a36Sopenharmony_ci		uint64_t retired_page,
29262306a36Sopenharmony_ci		uint32_t channel_index,
29362306a36Sopenharmony_ci		uint32_t umc_inst)
29462306a36Sopenharmony_ci{
29562306a36Sopenharmony_ci	struct eeprom_table_record *err_rec =
29662306a36Sopenharmony_ci		&err_data->err_addr[err_data->err_addr_cnt];
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	err_rec->address = err_addr;
29962306a36Sopenharmony_ci	/* page frame address is saved */
30062306a36Sopenharmony_ci	err_rec->retired_page = retired_page >> AMDGPU_GPU_PAGE_SHIFT;
30162306a36Sopenharmony_ci	err_rec->ts = (uint64_t)ktime_get_real_seconds();
30262306a36Sopenharmony_ci	err_rec->err_type = AMDGPU_RAS_EEPROM_ERR_NON_RECOVERABLE;
30362306a36Sopenharmony_ci	err_rec->cu = 0;
30462306a36Sopenharmony_ci	err_rec->mem_channel = channel_index;
30562306a36Sopenharmony_ci	err_rec->mcumc_id = umc_inst;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	err_data->err_addr_cnt++;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ciint amdgpu_umc_loop_channels(struct amdgpu_device *adev,
31162306a36Sopenharmony_ci			umc_func func, void *data)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	uint32_t node_inst       = 0;
31462306a36Sopenharmony_ci	uint32_t umc_inst        = 0;
31562306a36Sopenharmony_ci	uint32_t ch_inst         = 0;
31662306a36Sopenharmony_ci	int ret = 0;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	if (adev->umc.node_inst_num) {
31962306a36Sopenharmony_ci		LOOP_UMC_EACH_NODE_INST_AND_CH(node_inst, umc_inst, ch_inst) {
32062306a36Sopenharmony_ci			ret = func(adev, node_inst, umc_inst, ch_inst, data);
32162306a36Sopenharmony_ci			if (ret) {
32262306a36Sopenharmony_ci				dev_err(adev->dev, "Node %d umc %d ch %d func returns %d\n",
32362306a36Sopenharmony_ci					node_inst, umc_inst, ch_inst, ret);
32462306a36Sopenharmony_ci				return ret;
32562306a36Sopenharmony_ci			}
32662306a36Sopenharmony_ci		}
32762306a36Sopenharmony_ci	} else {
32862306a36Sopenharmony_ci		LOOP_UMC_INST_AND_CH(umc_inst, ch_inst) {
32962306a36Sopenharmony_ci			ret = func(adev, 0, umc_inst, ch_inst, data);
33062306a36Sopenharmony_ci			if (ret) {
33162306a36Sopenharmony_ci				dev_err(adev->dev, "Umc %d ch %d func returns %d\n",
33262306a36Sopenharmony_ci					umc_inst, ch_inst, ret);
33362306a36Sopenharmony_ci				return ret;
33462306a36Sopenharmony_ci			}
33562306a36Sopenharmony_ci		}
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	return 0;
33962306a36Sopenharmony_ci}
340