arm-trusted-firmware/plat/intel/soc/common/socfpga_sip_svc.c
Sieu Mun Tang c703d752cc fix(intel): fix ECC Double Bit Error handling
SError and Abort are handled in Linux (EL1) instead of
EL3. This patch adds some functionality that complements the
use cases by Linux as follows:

- Provide SMC for ECC DBE notification to EL3
- Determine type of reset needed and service the request in
  place of Linux

Signed-off-by: Abdul Halim, Muhammad Hadi Asyrafi <muhammad.hadi.asyrafi.abdul.halim@intel.com>
Signed-off-by: Sieu Mun Tang <sieu.mun.tang@intel.com>
Change-Id: I43d02c77f28004a31770be53599a5a42de412211
2022-03-09 09:14:16 +08:00

567 lines
13 KiB
C

/*
* Copyright (c) 2019-2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <common/debug.h>
#include <common/runtime_svc.h>
#include <lib/mmio.h>
#include <tools_share/uuid.h>
#include "socfpga_fcs.h"
#include "socfpga_mailbox.h"
#include "socfpga_reset_manager.h"
#include "socfpga_sip_svc.h"
/* Total buffer the driver can hold */
#define FPGA_CONFIG_BUFFER_SIZE 4
static int current_block, current_buffer;
static int read_block, max_blocks, is_partial_reconfig;
static uint32_t send_id, rcv_id;
static uint32_t bytes_per_block, blocks_submitted;
/* SiP Service UUID */
DEFINE_SVC_UUID2(intl_svc_uid,
0xa85273b0, 0xe85a, 0x4862, 0xa6, 0x2a,
0xfa, 0x88, 0x88, 0x17, 0x68, 0x81);
static uint64_t socfpga_sip_handler(uint32_t smc_fid,
uint64_t x1,
uint64_t x2,
uint64_t x3,
uint64_t x4,
void *cookie,
void *handle,
uint64_t flags)
{
ERROR("%s: unhandled SMC (0x%x)\n", __func__, smc_fid);
SMC_RET1(handle, SMC_UNK);
}
struct fpga_config_info fpga_config_buffers[FPGA_CONFIG_BUFFER_SIZE];
static int intel_fpga_sdm_write_buffer(struct fpga_config_info *buffer)
{
uint32_t args[3];
while (max_blocks > 0 && buffer->size > buffer->size_written) {
args[0] = (1<<8);
args[1] = buffer->addr + buffer->size_written;
if (buffer->size - buffer->size_written <= bytes_per_block) {
args[2] = buffer->size - buffer->size_written;
current_buffer++;
current_buffer %= FPGA_CONFIG_BUFFER_SIZE;
} else
args[2] = bytes_per_block;
buffer->size_written += args[2];
mailbox_send_cmd_async(&send_id, MBOX_RECONFIG_DATA, args,
3U, CMD_INDIRECT);
buffer->subblocks_sent++;
max_blocks--;
}
return !max_blocks;
}
static int intel_fpga_sdm_write_all(void)
{
for (int i = 0; i < FPGA_CONFIG_BUFFER_SIZE; i++)
if (intel_fpga_sdm_write_buffer(
&fpga_config_buffers[current_buffer]))
break;
return 0;
}
static uint32_t intel_mailbox_fpga_config_isdone(uint32_t query_type)
{
uint32_t ret;
if (query_type == 1)
ret = intel_mailbox_get_config_status(MBOX_CONFIG_STATUS, false);
else
ret = intel_mailbox_get_config_status(MBOX_RECONFIG_STATUS, true);
if (ret) {
if (ret == MBOX_CFGSTAT_STATE_CONFIG)
return INTEL_SIP_SMC_STATUS_BUSY;
else
return INTEL_SIP_SMC_STATUS_ERROR;
}
if (query_type != 1) {
/* full reconfiguration */
if (!is_partial_reconfig)
socfpga_bridges_enable(); /* Enable bridge */
}
return INTEL_SIP_SMC_STATUS_OK;
}
static int mark_last_buffer_xfer_completed(uint32_t *buffer_addr_completed)
{
int i;
for (i = 0; i < FPGA_CONFIG_BUFFER_SIZE; i++) {
if (fpga_config_buffers[i].block_number == current_block) {
fpga_config_buffers[i].subblocks_sent--;
if (fpga_config_buffers[i].subblocks_sent == 0
&& fpga_config_buffers[i].size <=
fpga_config_buffers[i].size_written) {
fpga_config_buffers[i].write_requested = 0;
current_block++;
*buffer_addr_completed =
fpga_config_buffers[i].addr;
return 0;
}
}
}
return -1;
}
static int intel_fpga_config_completed_write(uint32_t *completed_addr,
uint32_t *count, uint32_t *job_id)
{
uint32_t resp[5];
unsigned int resp_len = ARRAY_SIZE(resp);
int status = INTEL_SIP_SMC_STATUS_OK;
int all_completed = 1;
*count = 0;
while (*count < 3) {
status = mailbox_read_response(job_id,
resp, &resp_len);
if (status < 0) {
break;
}
max_blocks++;
if (mark_last_buffer_xfer_completed(
&completed_addr[*count]) == 0) {
*count = *count + 1;
} else {
break;
}
}
if (*count <= 0) {
if (status != MBOX_NO_RESPONSE &&
status != MBOX_TIMEOUT && resp_len != 0) {
mailbox_clear_response();
return INTEL_SIP_SMC_STATUS_ERROR;
}
*count = 0;
}
intel_fpga_sdm_write_all();
if (*count > 0)
status = INTEL_SIP_SMC_STATUS_OK;
else if (*count == 0)
status = INTEL_SIP_SMC_STATUS_BUSY;
for (int i = 0; i < FPGA_CONFIG_BUFFER_SIZE; i++) {
if (fpga_config_buffers[i].write_requested != 0) {
all_completed = 0;
break;
}
}
if (all_completed == 1)
return INTEL_SIP_SMC_STATUS_OK;
return status;
}
static int intel_fpga_config_start(uint32_t config_type)
{
uint32_t argument = 0x1;
uint32_t response[3];
int status = 0;
unsigned int size = 0;
unsigned int resp_len = ARRAY_SIZE(response);
is_partial_reconfig = config_type;
mailbox_clear_response();
mailbox_send_cmd(MBOX_JOB_ID, MBOX_CMD_CANCEL, NULL, 0U,
CMD_CASUAL, NULL, NULL);
status = mailbox_send_cmd(MBOX_JOB_ID, MBOX_RECONFIG, &argument, size,
CMD_CASUAL, response, &resp_len);
if (status < 0)
return status;
max_blocks = response[0];
bytes_per_block = response[1];
for (int i = 0; i < FPGA_CONFIG_BUFFER_SIZE; i++) {
fpga_config_buffers[i].size = 0;
fpga_config_buffers[i].size_written = 0;
fpga_config_buffers[i].addr = 0;
fpga_config_buffers[i].write_requested = 0;
fpga_config_buffers[i].block_number = 0;
fpga_config_buffers[i].subblocks_sent = 0;
}
blocks_submitted = 0;
current_block = 0;
read_block = 0;
current_buffer = 0;
/* full reconfiguration */
if (!is_partial_reconfig) {
/* Disable bridge */
socfpga_bridges_disable();
}
return 0;
}
static bool is_fpga_config_buffer_full(void)
{
for (int i = 0; i < FPGA_CONFIG_BUFFER_SIZE; i++)
if (!fpga_config_buffers[i].write_requested)
return false;
return true;
}
bool is_address_in_ddr_range(uint64_t addr, uint64_t size)
{
if (!addr && !size) {
return true;
}
if (size > (UINT64_MAX - addr))
return false;
if (addr < BL31_LIMIT)
return false;
if (addr + size > DRAM_BASE + DRAM_SIZE)
return false;
return true;
}
static uint32_t intel_fpga_config_write(uint64_t mem, uint64_t size)
{
int i;
intel_fpga_sdm_write_all();
if (!is_address_in_ddr_range(mem, size) ||
is_fpga_config_buffer_full())
return INTEL_SIP_SMC_STATUS_REJECTED;
for (i = 0; i < FPGA_CONFIG_BUFFER_SIZE; i++) {
int j = (i + current_buffer) % FPGA_CONFIG_BUFFER_SIZE;
if (!fpga_config_buffers[j].write_requested) {
fpga_config_buffers[j].addr = mem;
fpga_config_buffers[j].size = size;
fpga_config_buffers[j].size_written = 0;
fpga_config_buffers[j].write_requested = 1;
fpga_config_buffers[j].block_number =
blocks_submitted++;
fpga_config_buffers[j].subblocks_sent = 0;
break;
}
}
if (is_fpga_config_buffer_full())
return INTEL_SIP_SMC_STATUS_BUSY;
return INTEL_SIP_SMC_STATUS_OK;
}
static int is_out_of_sec_range(uint64_t reg_addr)
{
switch (reg_addr) {
case(0xF8011100): /* ECCCTRL1 */
case(0xF8011104): /* ECCCTRL2 */
case(0xF8011110): /* ERRINTEN */
case(0xF8011114): /* ERRINTENS */
case(0xF8011118): /* ERRINTENR */
case(0xF801111C): /* INTMODE */
case(0xF8011120): /* INTSTAT */
case(0xF8011124): /* DIAGINTTEST */
case(0xF801112C): /* DERRADDRA */
case(0xFFD12028): /* SDMMCGRP_CTRL */
case(0xFFD12044): /* EMAC0 */
case(0xFFD12048): /* EMAC1 */
case(0xFFD1204C): /* EMAC2 */
case(0xFFD12090): /* ECC_INT_MASK_VALUE */
case(0xFFD12094): /* ECC_INT_MASK_SET */
case(0xFFD12098): /* ECC_INT_MASK_CLEAR */
case(0xFFD1209C): /* ECC_INTSTATUS_SERR */
case(0xFFD120A0): /* ECC_INTSTATUS_DERR */
case(0xFFD120C0): /* NOC_TIMEOUT */
case(0xFFD120C4): /* NOC_IDLEREQ_SET */
case(0xFFD120C8): /* NOC_IDLEREQ_CLR */
case(0xFFD120D0): /* NOC_IDLEACK */
case(0xFFD120D4): /* NOC_IDLESTATUS */
case(0xFFD12200): /* BOOT_SCRATCH_COLD0 */
case(0xFFD12204): /* BOOT_SCRATCH_COLD1 */
case(0xFFD12220): /* BOOT_SCRATCH_COLD8 */
case(0xFFD12224): /* BOOT_SCRATCH_COLD9 */
return 0;
default:
break;
}
return -1;
}
/* Secure register access */
uint32_t intel_secure_reg_read(uint64_t reg_addr, uint32_t *retval)
{
if (is_out_of_sec_range(reg_addr))
return INTEL_SIP_SMC_STATUS_ERROR;
*retval = mmio_read_32(reg_addr);
return INTEL_SIP_SMC_STATUS_OK;
}
uint32_t intel_secure_reg_write(uint64_t reg_addr, uint32_t val,
uint32_t *retval)
{
if (is_out_of_sec_range(reg_addr))
return INTEL_SIP_SMC_STATUS_ERROR;
mmio_write_32(reg_addr, val);
return intel_secure_reg_read(reg_addr, retval);
}
uint32_t intel_secure_reg_update(uint64_t reg_addr, uint32_t mask,
uint32_t val, uint32_t *retval)
{
if (!intel_secure_reg_read(reg_addr, retval)) {
*retval &= ~mask;
*retval |= val & mask;
return intel_secure_reg_write(reg_addr, *retval, retval);
}
return INTEL_SIP_SMC_STATUS_ERROR;
}
/* Intel Remote System Update (RSU) services */
uint64_t intel_rsu_update_address;
static uint32_t intel_rsu_status(uint64_t *respbuf, unsigned int respbuf_sz)
{
if (mailbox_rsu_status((uint32_t *)respbuf, respbuf_sz) < 0)
return INTEL_SIP_SMC_RSU_ERROR;
return INTEL_SIP_SMC_STATUS_OK;
}
static uint32_t intel_rsu_update(uint64_t update_address)
{
intel_rsu_update_address = update_address;
return INTEL_SIP_SMC_STATUS_OK;
}
static uint32_t intel_rsu_notify(uint32_t execution_stage)
{
if (mailbox_hps_stage_notify(execution_stage) < 0)
return INTEL_SIP_SMC_RSU_ERROR;
return INTEL_SIP_SMC_STATUS_OK;
}
static uint32_t intel_rsu_retry_counter(uint32_t *respbuf, uint32_t respbuf_sz,
uint32_t *ret_stat)
{
if (mailbox_rsu_status((uint32_t *)respbuf, respbuf_sz) < 0)
return INTEL_SIP_SMC_RSU_ERROR;
*ret_stat = respbuf[8];
return INTEL_SIP_SMC_STATUS_OK;
}
/* Mailbox services */
static uint32_t intel_mbox_send_cmd(uint32_t cmd, uint32_t *args,
unsigned int len,
uint32_t urgent, uint32_t *response,
unsigned int resp_len, int *mbox_status,
unsigned int *len_in_resp)
{
*len_in_resp = 0;
*mbox_status = 0;
if (!is_address_in_ddr_range((uint64_t)args, sizeof(uint32_t) * len))
return INTEL_SIP_SMC_STATUS_REJECTED;
int status = mailbox_send_cmd(MBOX_JOB_ID, cmd, args, len, urgent,
response, &resp_len);
if (status < 0) {
*mbox_status = -status;
return INTEL_SIP_SMC_STATUS_ERROR;
}
*mbox_status = 0;
*len_in_resp = resp_len;
return INTEL_SIP_SMC_STATUS_OK;
}
/*
* This function is responsible for handling all SiP calls from the NS world
*/
uintptr_t sip_smc_handler(uint32_t smc_fid,
u_register_t x1,
u_register_t x2,
u_register_t x3,
u_register_t x4,
void *cookie,
void *handle,
u_register_t flags)
{
uint32_t retval = 0;
uint32_t completed_addr[3];
uint64_t rsu_respbuf[9];
int status = INTEL_SIP_SMC_STATUS_OK;
int mbox_status;
unsigned int len_in_resp;
u_register_t x5, x6;
switch (smc_fid) {
case SIP_SVC_UID:
/* Return UID to the caller */
SMC_UUID_RET(handle, intl_svc_uid);
case INTEL_SIP_SMC_FPGA_CONFIG_ISDONE:
status = intel_mailbox_fpga_config_isdone(x1);
SMC_RET4(handle, status, 0, 0, 0);
case INTEL_SIP_SMC_FPGA_CONFIG_GET_MEM:
SMC_RET3(handle, INTEL_SIP_SMC_STATUS_OK,
INTEL_SIP_SMC_FPGA_CONFIG_ADDR,
INTEL_SIP_SMC_FPGA_CONFIG_SIZE -
INTEL_SIP_SMC_FPGA_CONFIG_ADDR);
case INTEL_SIP_SMC_FPGA_CONFIG_START:
status = intel_fpga_config_start(x1);
SMC_RET4(handle, status, 0, 0, 0);
case INTEL_SIP_SMC_FPGA_CONFIG_WRITE:
status = intel_fpga_config_write(x1, x2);
SMC_RET4(handle, status, 0, 0, 0);
case INTEL_SIP_SMC_FPGA_CONFIG_COMPLETED_WRITE:
status = intel_fpga_config_completed_write(completed_addr,
&retval, &rcv_id);
switch (retval) {
case 1:
SMC_RET4(handle, INTEL_SIP_SMC_STATUS_OK,
completed_addr[0], 0, 0);
case 2:
SMC_RET4(handle, INTEL_SIP_SMC_STATUS_OK,
completed_addr[0],
completed_addr[1], 0);
case 3:
SMC_RET4(handle, INTEL_SIP_SMC_STATUS_OK,
completed_addr[0],
completed_addr[1],
completed_addr[2]);
case 0:
SMC_RET4(handle, status, 0, 0, 0);
default:
mailbox_clear_response();
SMC_RET1(handle, INTEL_SIP_SMC_STATUS_ERROR);
}
case INTEL_SIP_SMC_REG_READ:
status = intel_secure_reg_read(x1, &retval);
SMC_RET3(handle, status, retval, x1);
case INTEL_SIP_SMC_REG_WRITE:
status = intel_secure_reg_write(x1, (uint32_t)x2, &retval);
SMC_RET3(handle, status, retval, x1);
case INTEL_SIP_SMC_REG_UPDATE:
status = intel_secure_reg_update(x1, (uint32_t)x2,
(uint32_t)x3, &retval);
SMC_RET3(handle, status, retval, x1);
case INTEL_SIP_SMC_RSU_STATUS:
status = intel_rsu_status(rsu_respbuf,
ARRAY_SIZE(rsu_respbuf));
if (status) {
SMC_RET1(handle, status);
} else {
SMC_RET4(handle, rsu_respbuf[0], rsu_respbuf[1],
rsu_respbuf[2], rsu_respbuf[3]);
}
case INTEL_SIP_SMC_RSU_UPDATE:
status = intel_rsu_update(x1);
SMC_RET1(handle, status);
case INTEL_SIP_SMC_RSU_NOTIFY:
status = intel_rsu_notify(x1);
SMC_RET1(handle, status);
case INTEL_SIP_SMC_RSU_RETRY_COUNTER:
status = intel_rsu_retry_counter((uint32_t *)rsu_respbuf,
ARRAY_SIZE(rsu_respbuf), &retval);
if (status) {
SMC_RET1(handle, status);
} else {
SMC_RET2(handle, status, retval);
}
case INTEL_SIP_SMC_ECC_DBE:
status = intel_ecc_dbe_notification(x1);
SMC_RET1(handle, status);
case INTEL_SIP_SMC_MBOX_SEND_CMD:
x5 = SMC_GET_GP(handle, CTX_GPREG_X5);
x6 = SMC_GET_GP(handle, CTX_GPREG_X6);
status = intel_mbox_send_cmd(x1, (uint32_t *)x2, x3, x4,
(uint32_t *)x5, x6, &mbox_status,
&len_in_resp);
SMC_RET3(handle, status, mbox_status, len_in_resp);
default:
return socfpga_sip_handler(smc_fid, x1, x2, x3, x4,
cookie, handle, flags);
}
}
DECLARE_RT_SVC(
socfpga_sip_svc,
OEN_SIP_START,
OEN_SIP_END,
SMC_TYPE_FAST,
NULL,
sip_smc_handler
);
DECLARE_RT_SVC(
socfpga_sip_svc_std,
OEN_SIP_START,
OEN_SIP_END,
SMC_TYPE_YIELD,
NULL,
sip_smc_handler
);