brintos

brintos / linux-shallow public Read only

0
0
Text · 9.6 KiB · 3d3191d Raw
359 lines · c
1// SPDX-License-Identifier: MIT2/*3 * Copyright © 2022 Intel Corporation4 */5 6#include <linux/types.h>7 8#include "gt/intel_gt.h"9#include "gt/intel_gt_print.h"10#include "intel_gsc_fw.h"11#include "intel_gsc_proxy.h"12#include "intel_gsc_uc.h"13#include "i915_drv.h"14#include "i915_reg.h"15 16static void gsc_work(struct work_struct *work)17{18	struct intel_gsc_uc *gsc = container_of(work, typeof(*gsc), work);19	struct intel_gt *gt = gsc_uc_to_gt(gsc);20	intel_wakeref_t wakeref;21	u32 actions;22	int ret;23 24	wakeref = intel_runtime_pm_get(gt->uncore->rpm);25 26	spin_lock_irq(gt->irq_lock);27	actions = gsc->gsc_work_actions;28	gsc->gsc_work_actions = 0;29	spin_unlock_irq(gt->irq_lock);30 31	if (actions & GSC_ACTION_FW_LOAD) {32		ret = intel_gsc_uc_fw_upload(gsc);33		if (!ret)34			/* setup proxy on a new load */35			actions |= GSC_ACTION_SW_PROXY;36		else if (ret != -EEXIST)37			goto out_put;38 39		/*40		 * The HuC auth can be done both before or after the proxy init;41		 * if done after, a proxy request will be issued and must be42		 * serviced before the authentication can complete.43		 * Since this worker also handles proxy requests, we can't44		 * perform an action that requires the proxy from within it and45		 * then stall waiting for it, because we'd be blocking the46		 * service path. Therefore, it is easier for us to load HuC47		 * first and do proxy later. The GSC will ack the HuC auth and48		 * then send the HuC proxy request as part of the proxy init49		 * flow.50		 * Note that we can only do the GSC auth if the GuC auth was51		 * successful.52		 */53		if (intel_uc_uses_huc(&gt->uc) &&54		    intel_huc_is_authenticated(&gt->uc.huc, INTEL_HUC_AUTH_BY_GUC))55			intel_huc_auth(&gt->uc.huc, INTEL_HUC_AUTH_BY_GSC);56	}57 58	if (actions & GSC_ACTION_SW_PROXY) {59		if (!intel_gsc_uc_fw_init_done(gsc)) {60			gt_err(gt, "Proxy request received with GSC not loaded!\n");61			goto out_put;62		}63 64		ret = intel_gsc_proxy_request_handler(gsc);65		if (ret) {66			if (actions & GSC_ACTION_FW_LOAD) {67				/*68				 * A proxy failure right after firmware load means the proxy-init69				 * step has failed so mark GSC as not usable after this70				 */71				gt_err(gt, "GSC proxy handler failed to init\n");72				intel_uc_fw_change_status(&gsc->fw, INTEL_UC_FIRMWARE_LOAD_FAIL);73			}74			goto out_put;75		}76 77		/* mark the GSC FW init as done the first time we run this */78		if (actions & GSC_ACTION_FW_LOAD) {79			/*80			 * If there is a proxy establishment error, the GSC might still81			 * complete the request handling cleanly, so we need to check the82			 * status register to check if the proxy init was actually successful83			 */84			if (intel_gsc_uc_fw_proxy_init_done(gsc, false)) {85				gt_dbg(gt, "GSC Proxy initialized\n");86				intel_uc_fw_change_status(&gsc->fw, INTEL_UC_FIRMWARE_RUNNING);87			} else {88				gt_err(gt, "GSC status reports proxy init not complete\n");89				intel_uc_fw_change_status(&gsc->fw, INTEL_UC_FIRMWARE_LOAD_FAIL);90			}91		}92	}93 94out_put:95	intel_runtime_pm_put(gt->uncore->rpm, wakeref);96}97 98static bool gsc_engine_supported(struct intel_gt *gt)99{100	intel_engine_mask_t mask;101 102	/*103	 * We reach here from i915_driver_early_probe for the primary GT before104	 * its engine mask is set, so we use the device info engine mask for it.105	 * For other GTs we expect the GT-specific mask to be set before we106	 * call this function.107	 */108	GEM_BUG_ON(!gt_is_root(gt) && !gt->info.engine_mask);109 110	if (gt_is_root(gt))111		mask = INTEL_INFO(gt->i915)->platform_engine_mask;112	else113		mask = gt->info.engine_mask;114 115	return __HAS_ENGINE(mask, GSC0);116}117 118void intel_gsc_uc_init_early(struct intel_gsc_uc *gsc)119{120	struct intel_gt *gt = gsc_uc_to_gt(gsc);121 122	/*123	 * GSC FW needs to be copied to a dedicated memory allocations for124	 * loading (see gsc->local), so we don't need to GGTT map the FW image125	 * itself into GGTT.126	 */127	intel_uc_fw_init_early(&gsc->fw, INTEL_UC_FW_TYPE_GSC, false);128	INIT_WORK(&gsc->work, gsc_work);129 130	/* we can arrive here from i915_driver_early_probe for primary131	 * GT with it being not fully setup hence check device info's132	 * engine mask133	 */134	if (!gsc_engine_supported(gt)) {135		intel_uc_fw_change_status(&gsc->fw, INTEL_UC_FIRMWARE_NOT_SUPPORTED);136		return;137	}138 139	gsc->wq = alloc_ordered_workqueue("i915_gsc", 0);140	if (!gsc->wq) {141		gt_err(gt, "failed to allocate WQ for GSC, disabling FW\n");142		intel_uc_fw_change_status(&gsc->fw, INTEL_UC_FIRMWARE_NOT_SUPPORTED);143	}144}145 146static int gsc_allocate_and_map_vma(struct intel_gsc_uc *gsc, u32 size)147{148	struct intel_gt *gt = gsc_uc_to_gt(gsc);149	struct drm_i915_gem_object *obj;150	struct i915_vma *vma;151	void __iomem *vaddr;152	int ret = 0;153 154	/*155	 * The GSC FW doesn't immediately suspend after becoming idle, so there156	 * is a chance that it could still be awake after we successfully157	 * return from the  pci suspend function, even if there are no pending158	 * operations.159	 * The FW might therefore try to access memory for its suspend operation160	 * after the kernel has completed the HW suspend flow; this can cause161	 * issues if the FW is mapped in normal RAM memory, as some of the162	 * involved HW units might've already lost power.163	 * The driver must therefore avoid this situation and the recommended164	 * way to do so is to use stolen memory for the GSC memory allocation,165	 * because stolen memory takes a different path in HW and it is166	 * guaranteed to always work as long as the GPU itself is awake (which167	 * it must be if the GSC is awake).168	 */169	obj = i915_gem_object_create_stolen(gt->i915, size);170	if (IS_ERR(obj))171		return PTR_ERR(obj);172 173	vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, 0);174	if (IS_ERR(vma)) {175		ret = PTR_ERR(vma);176		goto err;177	}178 179	vaddr = i915_vma_pin_iomap(vma);180	i915_vma_unpin(vma);181	if (IS_ERR(vaddr)) {182		ret = PTR_ERR(vaddr);183		goto err;184	}185 186	i915_vma_make_unshrinkable(vma);187 188	gsc->local = vma;189	gsc->local_vaddr = vaddr;190 191	return 0;192 193err:194	i915_gem_object_put(obj);195	return ret;196}197 198static void gsc_unmap_and_free_vma(struct intel_gsc_uc *gsc)199{200	struct i915_vma *vma = fetch_and_zero(&gsc->local);201 202	if (!vma)203		return;204 205	gsc->local_vaddr = NULL;206	i915_vma_unpin_iomap(vma);207	i915_gem_object_put(vma->obj);208}209 210int intel_gsc_uc_init(struct intel_gsc_uc *gsc)211{212	static struct lock_class_key gsc_lock;213	struct intel_gt *gt = gsc_uc_to_gt(gsc);214	struct intel_engine_cs *engine = gt->engine[GSC0];215	struct intel_context *ce;216	int err;217 218	err = intel_uc_fw_init(&gsc->fw);219	if (err)220		goto out;221 222	err = gsc_allocate_and_map_vma(gsc, SZ_4M);223	if (err)224		goto out_fw;225 226	ce = intel_engine_create_pinned_context(engine, engine->gt->vm, SZ_4K,227						I915_GEM_HWS_GSC_ADDR,228						&gsc_lock, "gsc_context");229	if (IS_ERR(ce)) {230		gt_err(gt, "failed to create GSC CS ctx for FW communication\n");231		err =  PTR_ERR(ce);232		goto out_vma;233	}234 235	gsc->ce = ce;236 237	/* if we fail to init proxy we still want to load GSC for PM */238	intel_gsc_proxy_init(gsc);239 240	intel_uc_fw_change_status(&gsc->fw, INTEL_UC_FIRMWARE_LOADABLE);241 242	return 0;243 244out_vma:245	gsc_unmap_and_free_vma(gsc);246out_fw:247	intel_uc_fw_fini(&gsc->fw);248out:249	gt_probe_error(gt, "GSC init failed %pe\n", ERR_PTR(err));250	return err;251}252 253void intel_gsc_uc_fini(struct intel_gsc_uc *gsc)254{255	if (!intel_uc_fw_is_loadable(&gsc->fw))256		return;257 258	flush_work(&gsc->work);259	if (gsc->wq) {260		destroy_workqueue(gsc->wq);261		gsc->wq = NULL;262	}263 264	intel_gsc_proxy_fini(gsc);265 266	if (gsc->ce)267		intel_engine_destroy_pinned_context(fetch_and_zero(&gsc->ce));268 269	gsc_unmap_and_free_vma(gsc);270 271	intel_uc_fw_fini(&gsc->fw);272}273 274void intel_gsc_uc_flush_work(struct intel_gsc_uc *gsc)275{276	if (!intel_uc_fw_is_loadable(&gsc->fw))277		return;278 279	flush_work(&gsc->work);280}281 282void intel_gsc_uc_resume(struct intel_gsc_uc *gsc)283{284	if (!intel_uc_fw_is_loadable(&gsc->fw))285		return;286 287	/*288	 * we only want to start the GSC worker from here in the actual resume289	 * flow and not during driver load. This is because GSC load is slow and290	 * therefore we want to make sure that the default state init completes291	 * first to not slow down the init thread. A separate call to292	 * intel_gsc_uc_load_start will ensure that the GSC is loaded during293	 * driver load.294	 */295	if (!gsc_uc_to_gt(gsc)->engine[GSC0]->default_state)296		return;297 298	intel_gsc_uc_load_start(gsc);299}300 301void intel_gsc_uc_load_start(struct intel_gsc_uc *gsc)302{303	struct intel_gt *gt = gsc_uc_to_gt(gsc);304 305	if (!intel_uc_fw_is_loadable(&gsc->fw) || intel_uc_fw_is_in_error(&gsc->fw))306		return;307 308	if (intel_gsc_uc_fw_init_done(gsc))309		return;310 311	spin_lock_irq(gt->irq_lock);312	gsc->gsc_work_actions |= GSC_ACTION_FW_LOAD;313	spin_unlock_irq(gt->irq_lock);314 315	queue_work(gsc->wq, &gsc->work);316}317 318void intel_gsc_uc_load_status(struct intel_gsc_uc *gsc, struct drm_printer *p)319{320	struct intel_gt *gt = gsc_uc_to_gt(gsc);321	struct intel_uncore *uncore = gt->uncore;322	intel_wakeref_t wakeref;323 324	if (!intel_gsc_uc_is_supported(gsc)) {325		drm_printf(p, "GSC not supported\n");326		return;327	}328 329	if (!intel_gsc_uc_is_wanted(gsc)) {330		drm_printf(p, "GSC disabled\n");331		return;332	}333 334	drm_printf(p, "GSC firmware: %s\n", gsc->fw.file_selected.path);335	if (gsc->fw.file_selected.path != gsc->fw.file_wanted.path)336		drm_printf(p, "GSC firmware wanted: %s\n", gsc->fw.file_wanted.path);337	drm_printf(p, "\tstatus: %s\n", intel_uc_fw_status_repr(gsc->fw.status));338 339	drm_printf(p, "Release: %u.%u.%u.%u\n",340		   gsc->release.major, gsc->release.minor,341		   gsc->release.patch, gsc->release.build);342 343	drm_printf(p, "Compatibility Version: %u.%u [min expected %u.%u]\n",344		   gsc->fw.file_selected.ver.major, gsc->fw.file_selected.ver.minor,345		   gsc->fw.file_wanted.ver.major, gsc->fw.file_wanted.ver.minor);346 347	drm_printf(p, "SVN: %u\n", gsc->security_version);348 349	with_intel_runtime_pm(uncore->rpm, wakeref) {350		u32 i;351 352		for (i = 1; i <= 6; i++) {353			u32 status = intel_uncore_read(uncore,354						       HECI_FWSTS(MTL_GSC_HECI1_BASE, i));355			drm_printf(p, "HECI1 FWSTST%u = 0x%08x\n", i, status);356		}357	}358}359