162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Functions corresponding to password object type attributes under BIOS Password Object GUID for
462306a36Sopenharmony_ci * use with dell-wmi-sysman
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  Copyright (c) 2020 Dell Inc.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "dell-wmi-sysman.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cienum po_properties {IS_PASS_SET = 1, MIN_PASS_LEN, MAX_PASS_LEN};
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ciget_instance_id(po);
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic ssize_t is_enabled_show(struct kobject *kobj, struct kobj_attribute *attr,
1662306a36Sopenharmony_ci					  char *buf)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	int instance_id = get_po_instance_id(kobj);
1962306a36Sopenharmony_ci	union acpi_object *obj;
2062306a36Sopenharmony_ci	ssize_t ret;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	if (instance_id < 0)
2362306a36Sopenharmony_ci		return instance_id;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	/* need to use specific instance_id and guid combination to get right data */
2662306a36Sopenharmony_ci	obj = get_wmiobj_pointer(instance_id, DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID);
2762306a36Sopenharmony_ci	if (!obj)
2862306a36Sopenharmony_ci		return -EIO;
2962306a36Sopenharmony_ci	if (obj->package.elements[IS_PASS_SET].type != ACPI_TYPE_INTEGER) {
3062306a36Sopenharmony_ci		kfree(obj);
3162306a36Sopenharmony_ci		return -EINVAL;
3262306a36Sopenharmony_ci	}
3362306a36Sopenharmony_ci	ret = snprintf(buf, PAGE_SIZE, "%lld\n", obj->package.elements[IS_PASS_SET].integer.value);
3462306a36Sopenharmony_ci	kfree(obj);
3562306a36Sopenharmony_ci	return ret;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic struct kobj_attribute po_is_pass_set = __ATTR_RO(is_enabled);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic ssize_t current_password_store(struct kobject *kobj,
4162306a36Sopenharmony_ci				      struct kobj_attribute *attr,
4262306a36Sopenharmony_ci				      const char *buf, size_t count)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	char *target = NULL;
4562306a36Sopenharmony_ci	int length;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	length = strlen(buf);
4862306a36Sopenharmony_ci	if (buf[length-1] == '\n')
4962306a36Sopenharmony_ci		length--;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	/* firmware does verifiation of min/max password length,
5262306a36Sopenharmony_ci	 * hence only check for not exceeding MAX_BUFF here.
5362306a36Sopenharmony_ci	 */
5462306a36Sopenharmony_ci	if (length >= MAX_BUFF)
5562306a36Sopenharmony_ci		return -EINVAL;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (strcmp(kobj->name, "Admin") == 0)
5862306a36Sopenharmony_ci		target = wmi_priv.current_admin_password;
5962306a36Sopenharmony_ci	else if (strcmp(kobj->name, "System") == 0)
6062306a36Sopenharmony_ci		target = wmi_priv.current_system_password;
6162306a36Sopenharmony_ci	if (!target)
6262306a36Sopenharmony_ci		return -EIO;
6362306a36Sopenharmony_ci	memcpy(target, buf, length);
6462306a36Sopenharmony_ci	target[length] = '\0';
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return count;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic struct kobj_attribute po_current_password = __ATTR_WO(current_password);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic ssize_t new_password_store(struct kobject *kobj,
7262306a36Sopenharmony_ci				  struct kobj_attribute *attr,
7362306a36Sopenharmony_ci				  const char *buf, size_t count)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	char *p, *buf_cp;
7662306a36Sopenharmony_ci	int ret;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	buf_cp = kstrdup(buf, GFP_KERNEL);
7962306a36Sopenharmony_ci	if (!buf_cp)
8062306a36Sopenharmony_ci		return -ENOMEM;
8162306a36Sopenharmony_ci	p = memchr(buf_cp, '\n', count);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (p != NULL)
8462306a36Sopenharmony_ci		*p = '\0';
8562306a36Sopenharmony_ci	if (strlen(buf_cp) > MAX_BUFF) {
8662306a36Sopenharmony_ci		ret = -EINVAL;
8762306a36Sopenharmony_ci		goto out;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	ret = set_new_password(kobj->name, buf_cp);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ciout:
9362306a36Sopenharmony_ci	kfree(buf_cp);
9462306a36Sopenharmony_ci	return ret ? ret : count;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic struct kobj_attribute po_new_password = __ATTR_WO(new_password);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ciattribute_n_property_show(min_password_length, po);
10062306a36Sopenharmony_cistatic struct kobj_attribute po_min_pass_length = __ATTR_RO(min_password_length);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ciattribute_n_property_show(max_password_length, po);
10362306a36Sopenharmony_cistatic struct kobj_attribute po_max_pass_length = __ATTR_RO(max_password_length);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic ssize_t mechanism_show(struct kobject *kobj, struct kobj_attribute *attr,
10662306a36Sopenharmony_ci			 char *buf)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	return sprintf(buf, "password\n");
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic struct kobj_attribute po_mechanism = __ATTR_RO(mechanism);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic ssize_t role_show(struct kobject *kobj, struct kobj_attribute *attr,
11462306a36Sopenharmony_ci			 char *buf)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	if (strcmp(kobj->name, "Admin") == 0)
11762306a36Sopenharmony_ci		return sprintf(buf, "bios-admin\n");
11862306a36Sopenharmony_ci	else if (strcmp(kobj->name, "System") == 0)
11962306a36Sopenharmony_ci		return sprintf(buf, "power-on\n");
12062306a36Sopenharmony_ci	return -EIO;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic struct kobj_attribute po_role = __ATTR_RO(role);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic struct attribute *po_attrs[] = {
12662306a36Sopenharmony_ci	&po_is_pass_set.attr,
12762306a36Sopenharmony_ci	&po_min_pass_length.attr,
12862306a36Sopenharmony_ci	&po_max_pass_length.attr,
12962306a36Sopenharmony_ci	&po_current_password.attr,
13062306a36Sopenharmony_ci	&po_new_password.attr,
13162306a36Sopenharmony_ci	&po_role.attr,
13262306a36Sopenharmony_ci	&po_mechanism.attr,
13362306a36Sopenharmony_ci	NULL,
13462306a36Sopenharmony_ci};
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic const struct attribute_group po_attr_group = {
13762306a36Sopenharmony_ci	.attrs = po_attrs,
13862306a36Sopenharmony_ci};
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ciint alloc_po_data(void)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	int ret = 0;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	wmi_priv.po_instances_count = get_instance_count(DELL_WMI_BIOS_PASSOBJ_ATTRIBUTE_GUID);
14562306a36Sopenharmony_ci	wmi_priv.po_data = kcalloc(wmi_priv.po_instances_count, sizeof(struct po_data), GFP_KERNEL);
14662306a36Sopenharmony_ci	if (!wmi_priv.po_data) {
14762306a36Sopenharmony_ci		wmi_priv.po_instances_count = 0;
14862306a36Sopenharmony_ci		ret = -ENOMEM;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci	return ret;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/**
15462306a36Sopenharmony_ci * populate_po_data() - Populate all properties of an instance under password object attribute
15562306a36Sopenharmony_ci * @po_obj: ACPI object with password object data
15662306a36Sopenharmony_ci * @instance_id: The instance to enumerate
15762306a36Sopenharmony_ci * @attr_name_kobj: The parent kernel object
15862306a36Sopenharmony_ci */
15962306a36Sopenharmony_ciint populate_po_data(union acpi_object *po_obj, int instance_id, struct kobject *attr_name_kobj)
16062306a36Sopenharmony_ci{
16162306a36Sopenharmony_ci	wmi_priv.po_data[instance_id].attr_name_kobj = attr_name_kobj;
16262306a36Sopenharmony_ci	if (check_property_type(po, ATTR_NAME, ACPI_TYPE_STRING))
16362306a36Sopenharmony_ci		return -EINVAL;
16462306a36Sopenharmony_ci	strlcpy_attr(wmi_priv.po_data[instance_id].attribute_name,
16562306a36Sopenharmony_ci		     po_obj[ATTR_NAME].string.pointer);
16662306a36Sopenharmony_ci	if (check_property_type(po, MIN_PASS_LEN, ACPI_TYPE_INTEGER))
16762306a36Sopenharmony_ci		return -EINVAL;
16862306a36Sopenharmony_ci	wmi_priv.po_data[instance_id].min_password_length =
16962306a36Sopenharmony_ci		(uintptr_t)po_obj[MIN_PASS_LEN].string.pointer;
17062306a36Sopenharmony_ci	if (check_property_type(po, MAX_PASS_LEN, ACPI_TYPE_INTEGER))
17162306a36Sopenharmony_ci		return -EINVAL;
17262306a36Sopenharmony_ci	wmi_priv.po_data[instance_id].max_password_length =
17362306a36Sopenharmony_ci		(uintptr_t) po_obj[MAX_PASS_LEN].string.pointer;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	return sysfs_create_group(attr_name_kobj, &po_attr_group);
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci/**
17962306a36Sopenharmony_ci * exit_po_attributes() - Clear all attribute data
18062306a36Sopenharmony_ci *
18162306a36Sopenharmony_ci * Clears all data allocated for this group of attributes
18262306a36Sopenharmony_ci */
18362306a36Sopenharmony_civoid exit_po_attributes(void)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	int instance_id;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	for (instance_id = 0; instance_id < wmi_priv.po_instances_count; instance_id++) {
18862306a36Sopenharmony_ci		if (wmi_priv.po_data[instance_id].attr_name_kobj)
18962306a36Sopenharmony_ci			sysfs_remove_group(wmi_priv.po_data[instance_id].attr_name_kobj,
19062306a36Sopenharmony_ci								&po_attr_group);
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci	wmi_priv.po_instances_count = 0;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	kfree(wmi_priv.po_data);
19562306a36Sopenharmony_ci	wmi_priv.po_data = NULL;
19662306a36Sopenharmony_ci}
197