1*1cdf1eb8SJeremy Linton /* 2*1cdf1eb8SJeremy Linton * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved. 3*1cdf1eb8SJeremy Linton * 4*1cdf1eb8SJeremy Linton * SPDX-License-Identifier: BSD-3-Clause 5*1cdf1eb8SJeremy Linton */ 6*1cdf1eb8SJeremy Linton 7*1cdf1eb8SJeremy Linton #include <assert.h> 8*1cdf1eb8SJeremy Linton #include <stdint.h> 9*1cdf1eb8SJeremy Linton 10*1cdf1eb8SJeremy Linton #include <common/debug.h> 11*1cdf1eb8SJeremy Linton #include <common/runtime_svc.h> 12*1cdf1eb8SJeremy Linton #include <services/pci_svc.h> 13*1cdf1eb8SJeremy Linton #include <services/std_svc.h> 14*1cdf1eb8SJeremy Linton #include <smccc_helpers.h> 15*1cdf1eb8SJeremy Linton 16*1cdf1eb8SJeremy Linton static uint64_t validate_rw_addr_sz(uint32_t addr, uint64_t off, uint64_t sz) 17*1cdf1eb8SJeremy Linton { 18*1cdf1eb8SJeremy Linton uint32_t nseg; 19*1cdf1eb8SJeremy Linton uint32_t ret; 20*1cdf1eb8SJeremy Linton uint32_t start_end_bus; 21*1cdf1eb8SJeremy Linton 22*1cdf1eb8SJeremy Linton ret = pci_get_bus_for_seg(PCI_ADDR_SEG(addr), &start_end_bus, &nseg); 23*1cdf1eb8SJeremy Linton 24*1cdf1eb8SJeremy Linton if (ret != SMC_PCI_CALL_SUCCESS) { 25*1cdf1eb8SJeremy Linton return SMC_PCI_CALL_INVAL_PARAM; 26*1cdf1eb8SJeremy Linton } 27*1cdf1eb8SJeremy Linton switch (sz) { 28*1cdf1eb8SJeremy Linton case SMC_PCI_SZ_8BIT: 29*1cdf1eb8SJeremy Linton case SMC_PCI_SZ_16BIT: 30*1cdf1eb8SJeremy Linton case SMC_PCI_SZ_32BIT: 31*1cdf1eb8SJeremy Linton break; 32*1cdf1eb8SJeremy Linton default: 33*1cdf1eb8SJeremy Linton return SMC_PCI_CALL_INVAL_PARAM; 34*1cdf1eb8SJeremy Linton } 35*1cdf1eb8SJeremy Linton if ((off + sz) > (PCI_OFFSET_MASK + 1U)) { 36*1cdf1eb8SJeremy Linton return SMC_PCI_CALL_INVAL_PARAM; 37*1cdf1eb8SJeremy Linton } 38*1cdf1eb8SJeremy Linton return SMC_PCI_CALL_SUCCESS; 39*1cdf1eb8SJeremy Linton } 40*1cdf1eb8SJeremy Linton 41*1cdf1eb8SJeremy Linton uint64_t pci_smc_handler(uint32_t smc_fid, 42*1cdf1eb8SJeremy Linton u_register_t x1, 43*1cdf1eb8SJeremy Linton u_register_t x2, 44*1cdf1eb8SJeremy Linton u_register_t x3, 45*1cdf1eb8SJeremy Linton u_register_t x4, 46*1cdf1eb8SJeremy Linton void *cookie, 47*1cdf1eb8SJeremy Linton void *handle, 48*1cdf1eb8SJeremy Linton u_register_t flags) 49*1cdf1eb8SJeremy Linton { 50*1cdf1eb8SJeremy Linton switch (smc_fid) { 51*1cdf1eb8SJeremy Linton case SMC_PCI_VERSION: { 52*1cdf1eb8SJeremy Linton pcie_version ver; 53*1cdf1eb8SJeremy Linton 54*1cdf1eb8SJeremy Linton ver.major = 1U; 55*1cdf1eb8SJeremy Linton ver.minor = 0U; 56*1cdf1eb8SJeremy Linton SMC_RET4(handle, ver.val, 0U, 0U, 0U); 57*1cdf1eb8SJeremy Linton } 58*1cdf1eb8SJeremy Linton case SMC_PCI_FEATURES: 59*1cdf1eb8SJeremy Linton switch (x1) { 60*1cdf1eb8SJeremy Linton case SMC_PCI_VERSION: 61*1cdf1eb8SJeremy Linton case SMC_PCI_FEATURES: 62*1cdf1eb8SJeremy Linton case SMC_PCI_READ: 63*1cdf1eb8SJeremy Linton case SMC_PCI_WRITE: 64*1cdf1eb8SJeremy Linton case SMC_PCI_SEG_INFO: 65*1cdf1eb8SJeremy Linton SMC_RET1(handle, SMC_PCI_CALL_SUCCESS); 66*1cdf1eb8SJeremy Linton default: 67*1cdf1eb8SJeremy Linton SMC_RET1(handle, SMC_PCI_CALL_NOT_SUPPORTED); 68*1cdf1eb8SJeremy Linton } 69*1cdf1eb8SJeremy Linton break; 70*1cdf1eb8SJeremy Linton case SMC_PCI_READ: { 71*1cdf1eb8SJeremy Linton uint32_t ret; 72*1cdf1eb8SJeremy Linton 73*1cdf1eb8SJeremy Linton if (validate_rw_addr_sz(x1, x2, x3) != SMC_PCI_CALL_SUCCESS) { 74*1cdf1eb8SJeremy Linton SMC_RET2(handle, SMC_PCI_CALL_INVAL_PARAM, 0U); 75*1cdf1eb8SJeremy Linton } 76*1cdf1eb8SJeremy Linton if (x4 != 0U) { 77*1cdf1eb8SJeremy Linton SMC_RET2(handle, SMC_PCI_CALL_INVAL_PARAM, 0U); 78*1cdf1eb8SJeremy Linton } 79*1cdf1eb8SJeremy Linton if (pci_read_config(x1, x2, x3, &ret) != 0U) { 80*1cdf1eb8SJeremy Linton SMC_RET2(handle, SMC_PCI_CALL_INVAL_PARAM, 0U); 81*1cdf1eb8SJeremy Linton } else { 82*1cdf1eb8SJeremy Linton SMC_RET2(handle, SMC_PCI_CALL_SUCCESS, ret); 83*1cdf1eb8SJeremy Linton } 84*1cdf1eb8SJeremy Linton break; 85*1cdf1eb8SJeremy Linton } 86*1cdf1eb8SJeremy Linton case SMC_PCI_WRITE: { 87*1cdf1eb8SJeremy Linton uint32_t ret; 88*1cdf1eb8SJeremy Linton 89*1cdf1eb8SJeremy Linton if (validate_rw_addr_sz(x1, x2, x3) != SMC_PCI_CALL_SUCCESS) { 90*1cdf1eb8SJeremy Linton SMC_RET1(handle, SMC_PCI_CALL_INVAL_PARAM); 91*1cdf1eb8SJeremy Linton } 92*1cdf1eb8SJeremy Linton ret = pci_write_config(x1, x2, x3, x4); 93*1cdf1eb8SJeremy Linton SMC_RET1(handle, ret); 94*1cdf1eb8SJeremy Linton break; 95*1cdf1eb8SJeremy Linton } 96*1cdf1eb8SJeremy Linton case SMC_PCI_SEG_INFO: { 97*1cdf1eb8SJeremy Linton uint32_t nseg; 98*1cdf1eb8SJeremy Linton uint32_t ret; 99*1cdf1eb8SJeremy Linton uint32_t start_end_bus; 100*1cdf1eb8SJeremy Linton 101*1cdf1eb8SJeremy Linton if ((x2 != 0U) || (x3 != 0U) || (x4 != 0U)) { 102*1cdf1eb8SJeremy Linton SMC_RET3(handle, SMC_PCI_CALL_INVAL_PARAM, 0U, 0U); 103*1cdf1eb8SJeremy Linton } 104*1cdf1eb8SJeremy Linton ret = pci_get_bus_for_seg(x1, &start_end_bus, &nseg); 105*1cdf1eb8SJeremy Linton SMC_RET3(handle, ret, start_end_bus, nseg); 106*1cdf1eb8SJeremy Linton break; 107*1cdf1eb8SJeremy Linton } 108*1cdf1eb8SJeremy Linton default: 109*1cdf1eb8SJeremy Linton /* should be unreachable */ 110*1cdf1eb8SJeremy Linton WARN("Unimplemented PCI Service Call: 0x%x\n", smc_fid); 111*1cdf1eb8SJeremy Linton SMC_RET1(handle, SMC_PCI_CALL_NOT_SUPPORTED); 112*1cdf1eb8SJeremy Linton } 113*1cdf1eb8SJeremy Linton } 114