sdei

sdei的全称是software delegated exception interface。首先明确在bios 看来这就是一个apci table
这样在os中就可以解析并注册一个设备
static int __init sdei_init(void)
{
	if (sdei_present_dt() || sdei_present_acpi())
		platform_driver_register(&sdei_driver);

	return 0;
}
可以看到这里调用platform_driver_register 注册了一个总线设备,我们重点看看这个设备的probe函数
static int sdei_probe(struct platform_device *pdev)
{
	int err;
	u64 ver = 0;
	int conduit;

	conduit = sdei_get_conduit(pdev);
	if (!sdei_firmware_call)
		return 0;
#检查sdei的版本
	err = sdei_api_get_version(&ver);
	if (err == -EOPNOTSUPP)
		pr_err("advertised but not implemented in platform firmware\n");
	if (err) {
		pr_err("Failed to get SDEI version: %d\n", err);
		sdei_mark_interface_broken();
		return err;
	}

	pr_info("SDEIv%d.%d (0x%x) detected in firmware.\n",
		(int)SDEI_VERSION_MAJOR(ver), (int)SDEI_VERSION_MINOR(ver),
		(int)SDEI_VERSION_VENDOR(ver));

	if (SDEI_VERSION_MAJOR(ver) != 1) {
		pr_warn("Conflicting SDEI version detected.\n");
		sdei_mark_interface_broken();
		return -EINVAL;
	}

	err = sdei_platform_reset();
	if (err)
		return err;
#拿到entry_point
	sdei_entry_point = sdei_arch_get_entry_point(conduit);
	if (!sdei_entry_point) {
		/* Not supported due to hardware or boot configuration */
		sdei_mark_interface_broken();
		return 0;
	}
#下面分别注册pm和reboot相关的通知链
	err = cpu_pm_register_notifier(&sdei_pm_nb);
	if (err) {
		pr_warn("Failed to register CPU PM notifier...\n");
		goto error;
	}

	err = register_reboot_notifier(&sdei_reboot_nb);
	if (err) {
		pr_warn("Failed to register reboot notifier...\n");
		goto remove_cpupm;
	}

	err = cpuhp_setup_state(CPUHP_AP_ARM_SDEI_STARTING, "SDEI",
				&sdei_cpuhp_up, &sdei_cpuhp_down);
	if (err) {
		pr_warn("Failed to register CPU hotplug notifier...\n");
		goto remove_reboot;
	}

	return 0;

remove_reboot:
	unregister_reboot_notifier(&sdei_reboot_nb);

remove_cpupm:
	cpu_pm_unregister_notifier(&sdei_pm_nb);

error:
	sdei_mark_interface_broken();
	return err;
}
到这里可以看到kernel 会为sdei注册一个总线设备
在kernel中会调用sdei_event_register来注册一些事件,
int sdei_event_register(u32 event_num, sdei_event_callback *cb, void *arg)
{
	int err;
	struct sdei_event *event;

	WARN_ON(in_nmi());

	mutex_lock(&sdei_events_lock);
	do {
#首先检测事件是否已经注册,这里的时间随意数字为标识符,且不能重复注册
		if (sdei_event_find(event_num)) {
			pr_warn("Event %u already registered\n", event_num);
			err = -EBUSY;
			break;
		}
#根据event_num 和callback 注册一个sdei的event
		event = sdei_event_create(event_num, cb, arg);
		if (IS_ERR(event)) {
			err = PTR_ERR(event);
			pr_warn("Failed to create event %u: %d\n", event_num,
				err);
			break;
		}
#注册这个事件到sdei_list中
		err = _sdei_event_register(event);
		if (err) {
			sdei_event_destroy(event);
			pr_warn("Failed to register event %u: %d\n", event_num,
				err);
		}
	} while (0);
	mutex_unlock(&sdei_events_lock);

	return err;
}
_sdei_event_register代码如下:
static int _sdei_event_register(struct sdei_event *event)
{
	int err;

	lockdep_assert_held(&sdei_events_lock);

	spin_lock(&sdei_list_lock);
	event->reregister = true;
	spin_unlock(&sdei_list_lock);
#可以看到event根据type可以分为是否共享的还是私有的event
	if (event->type == SDEI_EVENT_TYPE_SHARED)
		return sdei_api_event_register(event->event_num,
					       sdei_entry_point,
					       event->registered,
					       SDEI_EVENT_REGISTER_RM_ANY, 0);

#共享或者私有都会走到这里
	err = sdei_do_cross_call(_local_event_register, event);
	if (err) {
		spin_lock(&sdei_list_lock);
		event->reregister = false;
		event->reenable = false;
		spin_unlock(&sdei_list_lock);

		sdei_do_cross_call(_local_event_unregister, event);
	}

	return err;
}
sdei_do_cross_call基本上是为每个cpu调用一遍fn
static inline int sdei_do_cross_call(void *fn, struct sdei_event * event)
{
	struct sdei_crosscall_args arg;

	CROSSCALL_INIT(arg, event);
#调用内核接口在每个cpu上fn
	on_each_cpu(fn, &arg, true);

	return arg.first_error;
}
我们重点看看fn
static void _local_event_register(void *data)
{
	int err;
	struct sdei_registered_event *reg;
	struct sdei_crosscall_args *arg = data;

	WARN_ON(preemptible());

	reg = per_cpu_ptr(arg->event->private_registered, smp_processor_id());
#这里的第二个参数就是从bios的acpi 表中获得的,调用这个函数就会陷入到atf中
	err = sdei_api_event_register(arg->event->event_num, sdei_entry_point,
				      reg, 0, 0);

	sdei_cross_call_return(arg, err);
}

static int sdei_api_event_register(u32 event_num, unsigned long entry_point,
				   void *arg, u64 flags, u64 affinity)
{
	return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_REGISTER, event_num,
			      (unsigned long)entry_point, (unsigned long)arg,
			      flags, affinity, NULL);
}
static int invoke_sdei_fn(unsigned long function_id, unsigned long arg0,
			  unsigned long arg1, unsigned long arg2,
			  unsigned long arg3, unsigned long arg4,
			  u64 *result)
{
	int err = 0;
	struct arm_smccc_res res;

	if (sdei_firmware_call) {
#最终调用sdei_fireware_call 进入到atf中
		sdei_firmware_call(function_id, arg0, arg1, arg2, arg3, arg4,
				   &res);
		if (sdei_is_err(&res))
			err = sdei_to_linux_errno(res.a0);
	} else {
		/*
		 * !sdei_firmware_call means we failed to probe or called
		 * sdei_mark_interface_broken(). -EIO is not an error returned
		 * by sdei_to_linux_errno() and is used to suppress messages
		 * from this driver.
		 */
		err = -EIO;
		res.a0 = SDEI_NOT_SUPPORTED;
	}

	if (result)
		*result = res.a0;

	return err;
}
发布了1432 篇原创文章 · 获赞 59 · 访问量 118万+

猜你喜欢

转载自blog.csdn.net/tiantao2012/article/details/88565209