19155e234SNayna Jain // SPDX-License-Identifier: GPL-2.0
29155e234SNayna Jain /*
39155e234SNayna Jain  * PowerNV code for secure variables
49155e234SNayna Jain  *
59155e234SNayna Jain  * Copyright (C) 2019 IBM Corporation
69155e234SNayna Jain  * Author: Claudio Carvalho
79155e234SNayna Jain  *         Nayna Jain
89155e234SNayna Jain  *
99155e234SNayna Jain  * APIs to access secure variables managed by OPAL.
109155e234SNayna Jain  */
119155e234SNayna Jain 
129155e234SNayna Jain #define pr_fmt(fmt) "secvar: "fmt
139155e234SNayna Jain 
149155e234SNayna Jain #include <linux/types.h>
15*81d7cac4SRob Herring #include <linux/of.h>
169155e234SNayna Jain #include <linux/platform_device.h>
179155e234SNayna Jain #include <asm/opal.h>
189155e234SNayna Jain #include <asm/secvar.h>
199155e234SNayna Jain #include <asm/secure_boot.h>
209155e234SNayna Jain 
opal_status_to_err(int rc)219155e234SNayna Jain static int opal_status_to_err(int rc)
229155e234SNayna Jain {
239155e234SNayna Jain 	int err;
249155e234SNayna Jain 
259155e234SNayna Jain 	switch (rc) {
269155e234SNayna Jain 	case OPAL_SUCCESS:
279155e234SNayna Jain 		err = 0;
289155e234SNayna Jain 		break;
299155e234SNayna Jain 	case OPAL_UNSUPPORTED:
309155e234SNayna Jain 		err = -ENXIO;
319155e234SNayna Jain 		break;
329155e234SNayna Jain 	case OPAL_PARAMETER:
339155e234SNayna Jain 		err = -EINVAL;
349155e234SNayna Jain 		break;
359155e234SNayna Jain 	case OPAL_RESOURCE:
369155e234SNayna Jain 		err = -ENOSPC;
379155e234SNayna Jain 		break;
389155e234SNayna Jain 	case OPAL_HARDWARE:
399155e234SNayna Jain 		err = -EIO;
409155e234SNayna Jain 		break;
419155e234SNayna Jain 	case OPAL_NO_MEM:
429155e234SNayna Jain 		err = -ENOMEM;
439155e234SNayna Jain 		break;
449155e234SNayna Jain 	case OPAL_EMPTY:
459155e234SNayna Jain 		err = -ENOENT;
469155e234SNayna Jain 		break;
479155e234SNayna Jain 	case OPAL_PARTIAL:
489155e234SNayna Jain 		err = -EFBIG;
499155e234SNayna Jain 		break;
509155e234SNayna Jain 	default:
519155e234SNayna Jain 		err = -EINVAL;
529155e234SNayna Jain 	}
539155e234SNayna Jain 
549155e234SNayna Jain 	return err;
559155e234SNayna Jain }
569155e234SNayna Jain 
opal_get_variable(const char * key,u64 ksize,u8 * data,u64 * dsize)5753cea34bSMichael Ellerman static int opal_get_variable(const char *key, u64 ksize, u8 *data, u64 *dsize)
589155e234SNayna Jain {
599155e234SNayna Jain 	int rc;
609155e234SNayna Jain 
619155e234SNayna Jain 	if (!key || !dsize)
629155e234SNayna Jain 		return -EINVAL;
639155e234SNayna Jain 
649155e234SNayna Jain 	*dsize = cpu_to_be64(*dsize);
659155e234SNayna Jain 
669155e234SNayna Jain 	rc = opal_secvar_get(key, ksize, data, dsize);
679155e234SNayna Jain 
689155e234SNayna Jain 	*dsize = be64_to_cpu(*dsize);
699155e234SNayna Jain 
709155e234SNayna Jain 	return opal_status_to_err(rc);
719155e234SNayna Jain }
729155e234SNayna Jain 
opal_get_next_variable(const char * key,u64 * keylen,u64 keybufsize)7353cea34bSMichael Ellerman static int opal_get_next_variable(const char *key, u64 *keylen, u64 keybufsize)
749155e234SNayna Jain {
759155e234SNayna Jain 	int rc;
769155e234SNayna Jain 
779155e234SNayna Jain 	if (!key || !keylen)
789155e234SNayna Jain 		return -EINVAL;
799155e234SNayna Jain 
809155e234SNayna Jain 	*keylen = cpu_to_be64(*keylen);
819155e234SNayna Jain 
829155e234SNayna Jain 	rc = opal_secvar_get_next(key, keylen, keybufsize);
839155e234SNayna Jain 
849155e234SNayna Jain 	*keylen = be64_to_cpu(*keylen);
859155e234SNayna Jain 
869155e234SNayna Jain 	return opal_status_to_err(rc);
879155e234SNayna Jain }
889155e234SNayna Jain 
opal_set_variable(const char * key,u64 ksize,u8 * data,u64 dsize)8953cea34bSMichael Ellerman static int opal_set_variable(const char *key, u64 ksize, u8 *data, u64 dsize)
909155e234SNayna Jain {
919155e234SNayna Jain 	int rc;
929155e234SNayna Jain 
939155e234SNayna Jain 	if (!key || !data)
949155e234SNayna Jain 		return -EINVAL;
959155e234SNayna Jain 
969155e234SNayna Jain 	rc = opal_secvar_enqueue_update(key, ksize, data, dsize);
979155e234SNayna Jain 
989155e234SNayna Jain 	return opal_status_to_err(rc);
999155e234SNayna Jain }
1009155e234SNayna Jain 
opal_secvar_format(char * buf,size_t bufsize)101ec2f40bdSRussell Currey static ssize_t opal_secvar_format(char *buf, size_t bufsize)
102ec2f40bdSRussell Currey {
103ec2f40bdSRussell Currey 	ssize_t rc = 0;
104ec2f40bdSRussell Currey 	struct device_node *node;
105ec2f40bdSRussell Currey 	const char *format;
106ec2f40bdSRussell Currey 
107ec2f40bdSRussell Currey 	node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
108ec2f40bdSRussell Currey 	if (!of_device_is_available(node)) {
109ec2f40bdSRussell Currey 		rc = -ENODEV;
110ec2f40bdSRussell Currey 		goto out;
111ec2f40bdSRussell Currey 	}
112ec2f40bdSRussell Currey 
113ec2f40bdSRussell Currey 	rc = of_property_read_string(node, "format", &format);
114ec2f40bdSRussell Currey 	if (rc)
115ec2f40bdSRussell Currey 		goto out;
116ec2f40bdSRussell Currey 
117ec2f40bdSRussell Currey 	rc = snprintf(buf, bufsize, "%s", format);
118ec2f40bdSRussell Currey 
119ec2f40bdSRussell Currey out:
120ec2f40bdSRussell Currey 	of_node_put(node);
121ec2f40bdSRussell Currey 
122ec2f40bdSRussell Currey 	return rc;
123ec2f40bdSRussell Currey }
124ec2f40bdSRussell Currey 
opal_secvar_max_size(u64 * max_size)125e0240794SRussell Currey static int opal_secvar_max_size(u64 *max_size)
126e0240794SRussell Currey {
127e0240794SRussell Currey 	int rc;
128e0240794SRussell Currey 	struct device_node *node;
129e0240794SRussell Currey 
130e0240794SRussell Currey 	node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend");
131e0240794SRussell Currey 	if (!node)
132e0240794SRussell Currey 		return -ENODEV;
133e0240794SRussell Currey 
134e0240794SRussell Currey 	if (!of_device_is_available(node)) {
135e0240794SRussell Currey 		rc = -ENODEV;
136e0240794SRussell Currey 		goto out;
137e0240794SRussell Currey 	}
138e0240794SRussell Currey 
139e0240794SRussell Currey 	rc = of_property_read_u64(node, "max-var-size", max_size);
140e0240794SRussell Currey 
141e0240794SRussell Currey out:
142e0240794SRussell Currey 	of_node_put(node);
143e0240794SRussell Currey 	return rc;
144e0240794SRussell Currey }
145e0240794SRussell Currey 
1469155e234SNayna Jain static const struct secvar_operations opal_secvar_ops = {
1479155e234SNayna Jain 	.get = opal_get_variable,
1489155e234SNayna Jain 	.get_next = opal_get_next_variable,
1499155e234SNayna Jain 	.set = opal_set_variable,
150ec2f40bdSRussell Currey 	.format = opal_secvar_format,
151e0240794SRussell Currey 	.max_size = opal_secvar_max_size,
1529155e234SNayna Jain };
1539155e234SNayna Jain 
opal_secvar_probe(struct platform_device * pdev)1549155e234SNayna Jain static int opal_secvar_probe(struct platform_device *pdev)
1559155e234SNayna Jain {
1569155e234SNayna Jain 	if (!opal_check_token(OPAL_SECVAR_GET)
1579155e234SNayna Jain 			|| !opal_check_token(OPAL_SECVAR_GET_NEXT)
1589155e234SNayna Jain 			|| !opal_check_token(OPAL_SECVAR_ENQUEUE_UPDATE)) {
1599155e234SNayna Jain 		pr_err("OPAL doesn't support secure variables\n");
1609155e234SNayna Jain 		return -ENODEV;
1619155e234SNayna Jain 	}
1629155e234SNayna Jain 
16326149b02SRussell Currey 	return set_secvar_ops(&opal_secvar_ops);
1649155e234SNayna Jain }
1659155e234SNayna Jain 
1669155e234SNayna Jain static const struct of_device_id opal_secvar_match[] = {
1679155e234SNayna Jain 	{ .compatible = "ibm,secvar-backend",},
1689155e234SNayna Jain 	{},
1699155e234SNayna Jain };
1709155e234SNayna Jain 
1719155e234SNayna Jain static struct platform_driver opal_secvar_driver = {
1729155e234SNayna Jain 	.driver = {
1739155e234SNayna Jain 		.name = "secvar",
1749155e234SNayna Jain 		.of_match_table = opal_secvar_match,
1759155e234SNayna Jain 	},
1769155e234SNayna Jain };
1779155e234SNayna Jain 
opal_secvar_init(void)1789155e234SNayna Jain static int __init opal_secvar_init(void)
1799155e234SNayna Jain {
1809155e234SNayna Jain 	return platform_driver_probe(&opal_secvar_driver, opal_secvar_probe);
1819155e234SNayna Jain }
1829155e234SNayna Jain device_initcall(opal_secvar_init);
183