aboutsummaryrefslogtreecommitdiff
path: root/hw/fsp/fsp-sysparam.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/fsp/fsp-sysparam.c')
-rw-r--r--hw/fsp/fsp-sysparam.c454
1 files changed, 454 insertions, 0 deletions
diff --git a/hw/fsp/fsp-sysparam.c b/hw/fsp/fsp-sysparam.c
new file mode 100644
index 0000000..e9e5b16
--- /dev/null
+++ b/hw/fsp/fsp-sysparam.c
@@ -0,0 +1,454 @@
+/* Copyright 2013-2014 IBM Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <skiboot.h>
+#include <fsp.h>
+#include <opal.h>
+#include <device.h>
+#include <lock.h>
+#include <processor.h>
+#include <psi.h>
+#include <opal-msg.h>
+#include <fsp-sysparam.h>
+
+struct sysparam_comp_data {
+ uint32_t param_len;
+ uint64_t async_token;
+};
+
+struct sysparam_req {
+ sysparam_compl_t completion;
+ void *comp_data;
+ void *ubuf;
+ uint32_t ulen;
+ struct fsp_msg msg;
+ struct fsp_msg resp;
+ bool done;
+};
+
+static struct sysparam_attr {
+ const char *name;
+ uint32_t id;
+ uint32_t length;
+ uint8_t perm;
+} sysparam_attrs[] = {
+#define _R OPAL_SYSPARAM_READ
+#define _W OPAL_SYSPARAM_WRITE
+#define _RW OPAL_SYSPARAM_RW
+ {"surveillance", SYS_PARAM_SURV, 4, _RW},
+ {"hmc-management", SYS_PARAM_HMC_MANAGED, 4, _R},
+ {"cupd-policy", SYS_PARAM_FLASH_POLICY, 4, _RW},
+ {"plat-hmc-managed", SYS_PARAM_NEED_HMC, 4, _RW},
+ {"fw-license-policy", SYS_PARAM_FW_LICENSE, 4, _RW},
+ {"world-wide-port-num", SYS_PARAM_WWPN, 12, _W},
+ {"default-boot-device", SYS_PARAM_DEF_BOOT_DEV, 1, _RW},
+ {"next-boot-device", SYS_PARAM_NEXT_BOOT_DEV,1, _RW}
+#undef _R
+#undef _W
+#undef _RW
+};
+
+static int fsp_sysparam_process(struct sysparam_req *r)
+{
+ u32 param_id, len;
+ int stlen = 0;
+ u8 fstat;
+ /* Snapshot completion before we set the "done" flag */
+ sysparam_compl_t comp = r->completion;
+ void *cdata = r->comp_data;
+
+ if (r->msg.state != fsp_msg_done) {
+ prerror("FSP: Request for sysparam 0x%x got FSP failure!\n",
+ r->msg.data.words[0]);
+ stlen = -1; /* XXX Find saner error codes */
+ goto complete;
+ }
+
+ param_id = r->resp.data.words[0];
+ len = r->resp.data.words[1] & 0xffff;
+
+ /* Check params validity */
+ if (param_id != r->msg.data.words[0]) {
+ prerror("FSP: Request for sysparam 0x%x got resp. for 0x%x!\n",
+ r->msg.data.words[0], param_id);
+ stlen = -2; /* XXX Sane error codes */
+ goto complete;
+ }
+ if (len > r->ulen) {
+ prerror("FSP: Request for sysparam 0x%x truncated!\n",
+ param_id);
+ len = r->ulen;
+ }
+
+ /* Decode the request status */
+ fstat = (r->msg.resp->word1 >> 8) & 0xff;
+ switch(fstat) {
+ case 0x00: /* XXX Is that even possible ? */
+ case 0x11: /* Data in request */
+ memcpy(r->ubuf, &r->resp.data.words[2], len);
+ /* pass through */
+ case 0x12: /* Data in TCE */
+ stlen = len;
+ break;
+ default:
+ stlen = -fstat;
+ }
+ complete:
+ /* Call completion if any */
+ if (comp)
+ comp(r->msg.data.words[0], stlen, cdata);
+
+ free(r);
+
+ return stlen;
+}
+
+static void fsp_sysparam_get_complete(struct fsp_msg *msg)
+{
+ struct sysparam_req *r = container_of(msg, struct sysparam_req, msg);
+
+ /* If it's an asynchronous request, process it now */
+ if (r->completion) {
+ fsp_sysparam_process(r);
+ return;
+ }
+
+ /* Else just set the done flag */
+
+ /* Another CPU can be polling on the "done" flag without the
+ * lock held, so let's order the udpates to the structure
+ */
+ lwsync();
+ r->done = true;
+}
+
+int fsp_get_sys_param(uint32_t param_id, void *buffer, uint32_t length,
+ sysparam_compl_t async_complete, void *comp_data)
+{
+ struct sysparam_req *r;
+ uint64_t baddr, tce_token;
+ int rc;
+
+ if (!fsp_present())
+ return -ENODEV;
+ /*
+ * XXX FIXME: We currently always allocate the sysparam_req here
+ * however, we want to avoid runtime allocations as much as
+ * possible, so if this is going to be used a lot at runtime,
+ * we probably want to pre-allocate a pool of these
+ */
+ r = zalloc(sizeof(struct sysparam_req));
+ if (!r)
+ return -ENOMEM;
+ if (length > 4096)
+ return -EINVAL;
+ r->completion = async_complete;
+ r->comp_data = comp_data;
+ r->done = false;
+ r->ubuf = buffer;
+ r->ulen = length;
+ r->msg.resp = &r->resp;
+
+ /* Map always 1 page ... easier that way and none of that
+ * is performance critical
+ */
+ baddr = (uint64_t)buffer;
+ fsp_tce_map(PSI_DMA_GET_SYSPARAM, (void *)(baddr & ~0xffful), 0x1000);
+ tce_token = PSI_DMA_GET_SYSPARAM | (baddr & 0xfff);
+ fsp_fillmsg(&r->msg, FSP_CMD_QUERY_SPARM, 3,
+ param_id, length, tce_token);
+ rc = fsp_queue_msg(&r->msg, fsp_sysparam_get_complete);
+
+ /* Asynchronous operation or queueing failure, return */
+ if (rc || async_complete)
+ return rc;
+
+ /* Synchronous operation requested, spin and process */
+ while(!r->done)
+ fsp_poll();
+
+ /* Will free the request */
+ return fsp_sysparam_process(r);
+}
+
+static void fsp_opal_getparam_complete(uint32_t param_id __unused, int err_len,
+ void *data)
+{
+ struct sysparam_comp_data *comp_data = data;
+ int rc = OPAL_SUCCESS;
+
+ if (comp_data->param_len != err_len)
+ rc = OPAL_INTERNAL_ERROR;
+
+ opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL,
+ comp_data->async_token, rc);
+ free(comp_data);
+}
+
+static void fsp_opal_setparam_complete(struct fsp_msg *msg)
+{
+ struct sysparam_comp_data *comp_data = msg->user_data;
+ u8 fstat;
+ uint32_t param_id;
+ int rc = OPAL_SUCCESS;
+
+ if (msg->state != fsp_msg_done) {
+ prerror("FSP: Request for set sysparam 0x%x got FSP failure!\n",
+ msg->data.words[0]);
+ rc = OPAL_INTERNAL_ERROR;
+ goto out;
+ }
+
+ param_id = msg->resp->data.words[0];
+ if (param_id != msg->data.words[0]) {
+ prerror("FSP: Request for set sysparam 0x%x got resp. for 0x%x!"
+ "\n", msg->data.words[0], param_id);
+ rc = OPAL_INTERNAL_ERROR;
+ goto out;
+ }
+
+ fstat = (msg->resp->word1 >> 8) & 0xff;
+ switch (fstat) {
+ case 0x00:
+ rc = OPAL_SUCCESS;
+ break;
+ case 0x22:
+ prerror("%s: Response status 0x%x, invalid data\n", __func__,
+ fstat);
+ rc = OPAL_INTERNAL_ERROR;
+ break;
+ case 0x24:
+ prerror("%s: Response status 0x%x, DMA error\n", __func__,
+ fstat);
+ rc = OPAL_INTERNAL_ERROR;
+ break;
+ default:
+ rc = OPAL_INTERNAL_ERROR;
+ break;
+ }
+
+out:
+ opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL,
+ comp_data->async_token, rc);
+ free(comp_data);
+ fsp_freemsg(msg);
+}
+
+/* OPAL interface for PowerNV to read the system parameter from FSP */
+static int64_t fsp_opal_get_param(uint64_t async_token, uint32_t param_id,
+ uint64_t buffer, uint64_t length)
+{
+ struct sysparam_comp_data *comp_data;
+ int count, rc, i;
+
+ if (!fsp_present())
+ return OPAL_HARDWARE;
+
+ count = ARRAY_SIZE(sysparam_attrs);
+ for (i = 0; i < count; i++)
+ if (sysparam_attrs[i].id == param_id)
+ break;
+ if (i == count)
+ return OPAL_PARAMETER;
+
+ if (length < sysparam_attrs[i].length)
+ return OPAL_PARAMETER;
+ if (!(sysparam_attrs[i].perm & OPAL_SYSPARAM_READ))
+ return OPAL_PERMISSION;
+
+ comp_data = zalloc(sizeof(struct sysparam_comp_data));
+ if (!comp_data)
+ return OPAL_NO_MEM;
+
+ comp_data->param_len = sysparam_attrs[i].length;
+ comp_data->async_token = async_token;
+ rc = fsp_get_sys_param(param_id, (void *)buffer,
+ sysparam_attrs[i].length, fsp_opal_getparam_complete,
+ comp_data);
+ if (rc) {
+ free(comp_data);
+ prerror("%s: Error %d queuing param request\n", __func__, rc);
+ return OPAL_INTERNAL_ERROR;
+ }
+
+ return OPAL_ASYNC_COMPLETION;
+}
+
+/* OPAL interface for PowerNV to update the system parameter to FSP */
+static int64_t fsp_opal_set_param(uint64_t async_token, uint32_t param_id,
+ uint64_t buffer, uint64_t length)
+{
+ struct sysparam_comp_data *comp_data;
+ struct fsp_msg *msg;
+ uint64_t tce_token;
+ int count, rc, i;
+
+ if (!fsp_present())
+ return OPAL_HARDWARE;
+
+ count = ARRAY_SIZE(sysparam_attrs);
+ for (i = 0; i < count; i++)
+ if (sysparam_attrs[i].id == param_id)
+ break;
+ if (i == count)
+ return OPAL_PARAMETER;
+
+ if (length < sysparam_attrs[i].length)
+ return OPAL_PARAMETER;
+ if (!(sysparam_attrs[i].perm & OPAL_SYSPARAM_WRITE))
+ return OPAL_PERMISSION;
+
+ fsp_tce_map(PSI_DMA_SET_SYSPARAM, (void *)(buffer & ~0xffful), 0x1000);
+ tce_token = PSI_DMA_SET_SYSPARAM | (buffer & 0xfff);
+
+ msg = fsp_mkmsg(FSP_CMD_SET_SPARM_2, 4, param_id, length,
+ tce_token >> 32, tce_token);
+ if (!msg) {
+ prerror("%s: Failed to allocate the message\n", __func__);
+ return OPAL_INTERNAL_ERROR;
+ }
+
+ comp_data = zalloc(sizeof(struct sysparam_comp_data));
+ if (!comp_data)
+ return OPAL_NO_MEM;
+
+ comp_data->param_len = length;
+ comp_data->async_token = async_token;
+ msg->user_data = comp_data;
+
+ rc = fsp_queue_msg(msg, fsp_opal_setparam_complete);
+ if (rc) {
+ free(comp_data);
+ fsp_freemsg(msg);
+ prerror("%s: Failed to queue the message\n", __func__);
+ return OPAL_INTERNAL_ERROR;
+ }
+
+ return OPAL_ASYNC_COMPLETION;
+}
+
+static bool fsp_sysparam_msg(u32 cmd_sub_mod, struct fsp_msg *msg)
+{
+ struct fsp_msg *rsp;
+ int rc = -ENOMEM;
+
+ switch(cmd_sub_mod) {
+ case FSP_CMD_SP_SPARM_UPD_0:
+ case FSP_CMD_SP_SPARM_UPD_1:
+ printf("FSP: Got sysparam update, param ID 0x%x\n",
+ msg->data.words[0]);
+ rsp = fsp_mkmsg((cmd_sub_mod & 0xffff00) | 0x008000, 0);
+ if (rsp)
+ rc = fsp_queue_msg(rsp, fsp_freemsg);
+ if (rc) {
+ prerror("FSP: Error %d queuing sysparam reply\n", rc);
+ /* What to do here ? R/R ? */
+ fsp_freemsg(rsp);
+ }
+ return true;
+ }
+ return false;
+}
+
+static struct fsp_client fsp_sysparam_client = {
+ .message = fsp_sysparam_msg,
+};
+
+static void add_opal_sysparam_node(void)
+{
+ struct dt_node *sysparams;
+ char *names, *s;
+ uint32_t *ids, *lens;
+ uint8_t *perms;
+ unsigned int i, count, size = 0;
+
+ if (!fsp_present())
+ return;
+
+ sysparams = dt_new(opal_node, "sysparams");
+ dt_add_property_string(sysparams, "compatible", "ibm,opal-sysparams");
+
+ count = ARRAY_SIZE(sysparam_attrs);
+ for (i = 0; i < count; i++)
+ size = size + strlen(sysparam_attrs[i].name) + 1;
+
+ names = zalloc(size);
+ if (!names) {
+ prerror("%s: Failed to allocate memory for parameter names\n",
+ __func__);
+ return;
+ }
+
+ ids = zalloc(count * sizeof(*ids));
+ if (!ids) {
+ prerror("%s: Failed to allocate memory for parameter ids\n",
+ __func__);
+ goto out_free_name;
+ }
+
+ lens = zalloc(count * sizeof(*lens));
+ if (!lens) {
+ prerror("%s: Failed to allocate memory for parameter length\n",
+ __func__);
+ goto out_free_id;
+ }
+
+ perms = zalloc(count * sizeof(*perms));
+ if (!perms) {
+ prerror("%s: Failed to allocate memory for parameter length\n",
+ __func__);
+ goto out_free_len;
+ }
+
+ s = names;
+ for (i = 0; i < count; i++) {
+ strcpy(s, sysparam_attrs[i].name);
+ s = s + strlen(sysparam_attrs[i].name) + 1;
+
+ ids[i] = sysparam_attrs[i].id;
+ lens[i] = sysparam_attrs[i].length;
+ perms[i] = sysparam_attrs[i].perm;
+ }
+
+ dt_add_property(sysparams, "param-name", names, size);
+ dt_add_property(sysparams, "param-id", ids, count * sizeof(*ids));
+ dt_add_property(sysparams, "param-len", lens, count * sizeof(*lens));
+ dt_add_property(sysparams, "param-perm", perms, count * sizeof(*perms));
+
+ free(perms);
+
+out_free_len:
+ free(lens);
+out_free_id:
+ free(ids);
+out_free_name:
+ free(names);
+}
+
+void fsp_sysparam_init(void)
+{
+ if (!fsp_present())
+ return;
+
+ /* Register change notifications */
+ fsp_register_client(&fsp_sysparam_client, FSP_MCLASS_SERVICE);
+
+ /* Register OPAL interfaces */
+ opal_register(OPAL_GET_PARAM, fsp_opal_get_param, 4);
+ opal_register(OPAL_SET_PARAM, fsp_opal_set_param, 4);
+
+ /* Add device-tree nodes */
+ add_opal_sysparam_node();
+}