1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * PowerNV code for secure variables 4 * 5 * Copyright (C) 2019 IBM Corporation 6 * Author: Claudio Carvalho 7 * Nayna Jain 8 * 9 * APIs to access secure variables managed by OPAL. 10 */ 11 12 #define pr_fmt(fmt) "secvar: "fmt 13 14 #include <linux/types.h> 15 #include <linux/of.h> 16 #include <linux/platform_device.h> 17 #include <asm/opal.h> 18 #include <asm/secvar.h> 19 #include <asm/secure_boot.h> 20 21 static int opal_status_to_err(int rc) 22 { 23 int err; 24 25 switch (rc) { 26 case OPAL_SUCCESS: 27 err = 0; 28 break; 29 case OPAL_UNSUPPORTED: 30 err = -ENXIO; 31 break; 32 case OPAL_PARAMETER: 33 err = -EINVAL; 34 break; 35 case OPAL_RESOURCE: 36 err = -ENOSPC; 37 break; 38 case OPAL_HARDWARE: 39 err = -EIO; 40 break; 41 case OPAL_NO_MEM: 42 err = -ENOMEM; 43 break; 44 case OPAL_EMPTY: 45 err = -ENOENT; 46 break; 47 case OPAL_PARTIAL: 48 err = -EFBIG; 49 break; 50 default: 51 err = -EINVAL; 52 } 53 54 return err; 55 } 56 57 static int opal_get_variable(const char *key, u64 ksize, u8 *data, u64 *dsize) 58 { 59 int rc; 60 61 if (!key || !dsize) 62 return -EINVAL; 63 64 *dsize = cpu_to_be64(*dsize); 65 66 rc = opal_secvar_get(key, ksize, data, dsize); 67 68 *dsize = be64_to_cpu(*dsize); 69 70 return opal_status_to_err(rc); 71 } 72 73 static int opal_get_next_variable(const char *key, u64 *keylen, u64 keybufsize) 74 { 75 int rc; 76 77 if (!key || !keylen) 78 return -EINVAL; 79 80 *keylen = cpu_to_be64(*keylen); 81 82 rc = opal_secvar_get_next(key, keylen, keybufsize); 83 84 *keylen = be64_to_cpu(*keylen); 85 86 return opal_status_to_err(rc); 87 } 88 89 static int opal_set_variable(const char *key, u64 ksize, u8 *data, u64 dsize) 90 { 91 int rc; 92 93 if (!key || !data) 94 return -EINVAL; 95 96 rc = opal_secvar_enqueue_update(key, ksize, data, dsize); 97 98 return opal_status_to_err(rc); 99 } 100 101 static ssize_t opal_secvar_format(char *buf, size_t bufsize) 102 { 103 ssize_t rc = 0; 104 struct device_node *node; 105 const char *format; 106 107 node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend"); 108 if (!of_device_is_available(node)) { 109 rc = -ENODEV; 110 goto out; 111 } 112 113 rc = of_property_read_string(node, "format", &format); 114 if (rc) 115 goto out; 116 117 rc = snprintf(buf, bufsize, "%s", format); 118 119 out: 120 of_node_put(node); 121 122 return rc; 123 } 124 125 static int opal_secvar_max_size(u64 *max_size) 126 { 127 int rc; 128 struct device_node *node; 129 130 node = of_find_compatible_node(NULL, NULL, "ibm,secvar-backend"); 131 if (!node) 132 return -ENODEV; 133 134 if (!of_device_is_available(node)) { 135 rc = -ENODEV; 136 goto out; 137 } 138 139 rc = of_property_read_u64(node, "max-var-size", max_size); 140 141 out: 142 of_node_put(node); 143 return rc; 144 } 145 146 static const struct secvar_operations opal_secvar_ops = { 147 .get = opal_get_variable, 148 .get_next = opal_get_next_variable, 149 .set = opal_set_variable, 150 .format = opal_secvar_format, 151 .max_size = opal_secvar_max_size, 152 }; 153 154 static int opal_secvar_probe(struct platform_device *pdev) 155 { 156 if (!opal_check_token(OPAL_SECVAR_GET) 157 || !opal_check_token(OPAL_SECVAR_GET_NEXT) 158 || !opal_check_token(OPAL_SECVAR_ENQUEUE_UPDATE)) { 159 pr_err("OPAL doesn't support secure variables\n"); 160 return -ENODEV; 161 } 162 163 return set_secvar_ops(&opal_secvar_ops); 164 } 165 166 static const struct of_device_id opal_secvar_match[] = { 167 { .compatible = "ibm,secvar-backend",}, 168 {}, 169 }; 170 171 static struct platform_driver opal_secvar_driver = { 172 .driver = { 173 .name = "secvar", 174 .of_match_table = opal_secvar_match, 175 }, 176 }; 177 178 static int __init opal_secvar_init(void) 179 { 180 return platform_driver_probe(&opal_secvar_driver, opal_secvar_probe); 181 } 182 device_initcall(opal_secvar_init); 183