Exemplo n.º 1
0
int ocxl_config_check_afu_index(struct pci_dev *dev,
				struct ocxl_fn_config *fn, int afu_idx)
{
	u32 val;
	int rc, templ_major, templ_minor, len;

	pci_write_config_byte(dev,
			fn->dvsec_afu_info_pos + OCXL_DVSEC_AFU_INFO_AFU_IDX,
			afu_idx);
	rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_VERSION, &val);
	if (rc)
		return rc;

	/* AFU index map can have holes */
	if (!val)
		return 0;

	templ_major = EXTRACT_BITS(val, 8, 15);
	templ_minor = EXTRACT_BITS(val, 0, 7);
	dev_dbg(&dev->dev, "AFU descriptor template version %d.%d\n",
		templ_major, templ_minor);

	len = EXTRACT_BITS(val, 16, 31);
	if (len != OCXL_TEMPL_LEN) {
		dev_warn(&dev->dev,
			"Unexpected template length in AFU information (%#x)\n",
			len);
	}
	return 1;
}
Exemplo n.º 2
0
static int read_dvsec_function(struct pci_dev *dev, struct ocxl_fn_config *fn)
{
	int pos, afu_present;
	u32 val;

	pos = find_dvsec(dev, OCXL_DVSEC_FUNC_ID);
	if (!pos) {
		dev_err(&dev->dev, "Can't find function DVSEC\n");
		return -ENODEV;
	}
	fn->dvsec_function_pos = pos;

	pci_read_config_dword(dev, pos + OCXL_DVSEC_FUNC_OFF_INDEX, &val);
	afu_present = EXTRACT_BIT(val, 31);
	if (!afu_present) {
		fn->max_afu_index = -1;
		dev_dbg(&dev->dev, "Function doesn't define any AFU\n");
		goto out;
	}
	fn->max_afu_index = EXTRACT_BITS(val, 24, 29);

out:
	dev_dbg(&dev->dev, "Function DVSEC:\n");
	dev_dbg(&dev->dev, "  Max AFU index = %d\n", fn->max_afu_index);
	return 0;
}
Exemplo n.º 3
0
static int read_afu_mmio(struct pci_dev *dev, struct ocxl_fn_config *fn,
			struct ocxl_afu_config *afu)
{
	int rc;
	u32 val;

	/*
	 * Global MMIO
	 */
	rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MMIO_GLOBAL, &val);
	if (rc)
		return rc;
	afu->global_mmio_bar = EXTRACT_BITS(val, 0, 2);
	afu->global_mmio_offset = EXTRACT_BITS(val, 16, 31) << 16;

	rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MMIO_GLOBAL + 4, &val);
	if (rc)
		return rc;
	afu->global_mmio_offset += (u64) val << 32;

	rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MMIO_GLOBAL_SZ, &val);
	if (rc)
		return rc;
	afu->global_mmio_size = val;

	/*
	 * Per-process MMIO
	 */
	rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MMIO_PP, &val);
	if (rc)
		return rc;
	afu->pp_mmio_bar = EXTRACT_BITS(val, 0, 2);
	afu->pp_mmio_offset = EXTRACT_BITS(val, 16, 31) << 16;

	rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MMIO_PP + 4, &val);
	if (rc)
		return rc;
	afu->pp_mmio_offset += (u64) val << 32;

	rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MMIO_PP_SZ, &val);
	if (rc)
		return rc;
	afu->pp_mmio_stride = val;

	return 0;
}
Exemplo n.º 4
0
static int read_afu_control(struct pci_dev *dev, struct ocxl_afu_config *afu)
{
	int pos;
	u8 val8;
	u16 val16;

	pos = find_dvsec_afu_ctrl(dev, afu->idx);
	if (!pos) {
		dev_err(&dev->dev, "Can't find AFU control DVSEC for AFU %d\n",
			afu->idx);
		return -ENODEV;
	}
	afu->dvsec_afu_control_pos = pos;

	pci_read_config_byte(dev, pos + OCXL_DVSEC_AFU_CTRL_PASID_SUP, &val8);
	afu->pasid_supported_log = EXTRACT_BITS(val8, 0, 4);

	pci_read_config_word(dev, pos + OCXL_DVSEC_AFU_CTRL_ACTAG_SUP, &val16);
	afu->actag_supported = EXTRACT_BITS(val16, 0, 11);
	return 0;
}
Exemplo n.º 5
0
static void read_pasid(struct pci_dev *dev, struct ocxl_fn_config *fn)
{
	u16 val;
	int pos;

	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_PASID);
	if (!pos) {
		/*
		 * PASID capability is not mandatory, but there
		 * shouldn't be any AFU
		 */
		dev_dbg(&dev->dev, "Function doesn't require any PASID\n");
		fn->max_pasid_log = -1;
		goto out;
	}
	pci_read_config_word(dev, pos + PCI_PASID_CAP, &val);
	fn->max_pasid_log = EXTRACT_BITS(val, 8, 12);

out:
	dev_dbg(&dev->dev, "PASID capability:\n");
	dev_dbg(&dev->dev, "  Max PASID log = %d\n", fn->max_pasid_log);
}
Exemplo n.º 6
0
int ocxl_config_read_afu(struct pci_dev *dev, struct ocxl_fn_config *fn,
			struct ocxl_afu_config *afu, u8 afu_idx)
{
	int rc;
	u32 val32;

	/*
	 * First, we need to write the AFU idx for the AFU we want to
	 * access.
	 */
	WARN_ON((afu_idx & OCXL_DVSEC_AFU_IDX_MASK) != afu_idx);
	afu->idx = afu_idx;
	pci_write_config_byte(dev,
			fn->dvsec_afu_info_pos + OCXL_DVSEC_AFU_INFO_AFU_IDX,
			afu->idx);

	rc = read_afu_name(dev, fn, afu);
	if (rc)
		return rc;

	rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_AFU_VERSION, &val32);
	if (rc)
		return rc;
	afu->version_major = EXTRACT_BITS(val32, 24, 31);
	afu->version_minor = EXTRACT_BITS(val32, 16, 23);
	afu->afuc_type = EXTRACT_BITS(val32, 14, 15);
	afu->afum_type = EXTRACT_BITS(val32, 12, 13);
	afu->profile = EXTRACT_BITS(val32, 0, 7);

	rc = read_afu_mmio(dev, fn, afu);
	if (rc)
		return rc;

	rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MEM_SZ, &val32);
	if (rc)
		return rc;
	afu->log_mem_size = EXTRACT_BITS(val32, 0, 7);

	rc = read_afu_control(dev, afu);
	if (rc)
		return rc;

	dev_dbg(&dev->dev, "AFU configuration:\n");
	dev_dbg(&dev->dev, "  name = %s\n", afu->name);
	dev_dbg(&dev->dev, "  version = %d.%d\n", afu->version_major,
		afu->version_minor);
	dev_dbg(&dev->dev, "  global mmio bar = %hhu\n", afu->global_mmio_bar);
	dev_dbg(&dev->dev, "  global mmio offset = %#llx\n",
		afu->global_mmio_offset);
	dev_dbg(&dev->dev, "  global mmio size = %#x\n", afu->global_mmio_size);
	dev_dbg(&dev->dev, "  pp mmio bar = %hhu\n", afu->pp_mmio_bar);
	dev_dbg(&dev->dev, "  pp mmio offset = %#llx\n", afu->pp_mmio_offset);
	dev_dbg(&dev->dev, "  pp mmio stride = %#x\n", afu->pp_mmio_stride);
	dev_dbg(&dev->dev, "  mem size (log) = %hhu\n", afu->log_mem_size);
	dev_dbg(&dev->dev, "  pasid supported (log) = %u\n",
		afu->pasid_supported_log);
	dev_dbg(&dev->dev, "  actag supported = %u\n",
		afu->actag_supported);

	rc = validate_afu(dev, afu);
	return rc;
}
/**
 * Initialize a new decoded CFE entry using the provided encoded CFE data. Any resources held by a successfully
 * initialized instance must be freed via plcrash_async_cfe_entry_free();
 *
 * @param entry The entry instance to initialize.
 * @param cpu_type The target architecture of the CFE data, encoded as a Mach-O CPU type. Interpreting CFE data is
 * architecture-specific, and Apple has not defined encodings for all supported architectures.
 * @param encoding The CFE entry data, in the hosts' native byte order.
 *
 * @internal
 * This code supports sparse register lists for the EBP_FRAME and RBP_FRAME modes. It's unclear as to whether these
 * actually ever occur in the wild, but they are supported by Apple's unwinddump tool.
 */
apigee_plcrash_error_t apigee_plcrash_async_cfe_entry_init (apigee_plcrash_async_cfe_entry_t *entry, cpu_type_t cpu_type, uint32_t encoding) {
    apigee_plcrash_error_t ret;
    
    /* Target-neutral initialization */
    entry->cpu_type = cpu_type;
    entry->stack_adjust = 0;

    /* Perform target-specific decoding */
    if (cpu_type == CPU_TYPE_X86) {
        uint32_t mode = encoding & UNWIND_X86_MODE_MASK;
        switch (mode) {
            case UNWIND_X86_MODE_EBP_FRAME: {
                entry->type = APIGEE_PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAME_PTR;

                /* Extract the register frame offset */
                entry->stack_offset = -(EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_OFFSET) * sizeof(uint32_t));

                /* Extract the register values. They're stored as a bitfield of of 3 bit values. We support
                 * sparse entries, but terminate the loop if no further entries remain. */
                uint32_t regs = EXTRACT_BITS(encoding, UNWIND_X86_EBP_FRAME_REGISTERS);
                entry->register_count = 0;
                for (uint32_t i = 0; i < PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX; i++) {
                    /* Check for completion */
                    uint32_t remaining = regs >> (3 * i);
                    if (remaining == 0)
                        break;

                    /* Map to the correct PLCrashReporter register name */
                    uint32_t reg = remaining & 0x7;
                    ret = apigee_plcrash_async_map_register_name(reg, &entry->register_list[i], cpu_type);
                    if (ret != APIGEE_PLCRASH_ESUCCESS) {
                        PLCF_DEBUG("Failed to map register value of %" PRIx32, reg);
                        return ret;
                    }

                    /* Update the register count */
                    entry->register_count++;
                }
                
                return APIGEE_PLCRASH_ESUCCESS;
            }

            case UNWIND_X86_MODE_STACK_IMMD:
            case UNWIND_X86_MODE_STACK_IND: {
                /* These two types are identical except for the interpretation of the stack offset and adjustment values */
                if (mode == UNWIND_X86_MODE_STACK_IMMD) {
                    entry->type = APIGEE_PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAMELESS_IMMD;
                    entry->stack_offset = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE) * sizeof(uint32_t);
                } else {
                    entry->type = APIGEE_PLCRASH_ASYNC_CFE_ENTRY_TYPE_FRAMELESS_INDIRECT;
                    entry->stack_offset = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE);
                    entry->stack_adjust = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST) * sizeof(uint32_t);
                }

                /* Extract the register values */
                entry->register_count = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT);
                uint32_t encoded_regs = EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION);
                uint32_t decoded_regs[PLCRASH_ASYNC_CFE_SAVED_REGISTER_MAX];
                
                apigee_plcrash_async_cfe_register_decode(encoded_regs, entry->register_count, decoded_regs);
                
                /* Map to the correct PLCrashReporter register names */
                for (uint32_t i = 0; i < entry->register_count; i++) {
                    ret = apigee_plcrash_async_map_register_name(decoded_regs[i], &entry->register_list[i], cpu_type);
                    if (ret != APIGEE_PLCRASH_ESUCCESS) {
                        PLCF_DEBUG("Failed to map register value of %" PRIx32, entry->register_list[i]);
                        return ret;
                    }
                }

                return APIGEE_PLCRASH_ESUCCESS;
            }

            case UNWIND_X86_MODE_DWARF:
                entry->type = APIGEE_PLCRASH_ASYNC_CFE_ENTRY_TYPE_DWARF;

                /* Extract the register frame offset */
                entry->stack_offset = EXTRACT_BITS(encoding, UNWIND_X86_DWARF_SECTION_OFFSET);
                entry->register_count = 0;

                return APIGEE_PLCRASH_ESUCCESS;

            case 0:
                /* Handle a NULL encoding. This interpretation is derived from Apple's actual implementation; the correct interpretation of
                 * a 0x0 value is not defined in what documentation exists. */
                entry->type = APIGEE_PLCRASH_ASYNC_CFE_ENTRY_TYPE_NONE;
                entry->stack_offset = 0;
                entry->register_count = 0;
                return APIGEE_PLCRASH_ESUCCESS;
                
            default:
                PLCF_DEBUG("Unexpected entry mode of %" PRIx32, mode);
                return APIGEE_PLCRASH_ENOTSUP;
        }
        
        // Unreachable
        __builtin_trap();
        return APIGEE_PLCRASH_EINTERNAL;

    } else if (cpu_type == CPU_TYPE_X86_64) {