/*
 * QEMU CXL Events
 *
 * Copyright (c) 2022 Intel
 *
 * This work is licensed under the terms of the GNU GPL, version 2. See the
 * COPYING file in the top-level directory.
 */

#ifndef CXL_EVENTS_H
#define CXL_EVENTS_H

#include "qemu/uuid.h"

/*
 * CXL r3.1 section 8.2.9.2.2: Get Event Records (Opcode 0100h); Table 8-52
 *
 * Define these as the bit position for the event status register for ease of
 * setting the status.
 */
typedef enum CXLEventLogType {
    CXL_EVENT_TYPE_INFO          = 0,
    CXL_EVENT_TYPE_WARN          = 1,
    CXL_EVENT_TYPE_FAIL          = 2,
    CXL_EVENT_TYPE_FATAL         = 3,
    CXL_EVENT_TYPE_DYNAMIC_CAP   = 4,
    CXL_EVENT_TYPE_MAX
} CXLEventLogType;

/*
 * Common Event Record Format
 * CXL r3.2 section 8.2.10.2.1: Event Records; Table 8-55
 */
#define CXL_EVENT_REC_FLAGS_PERMANENT_COND BIT(2)
#define CXL_EVENT_REC_FLAGS_MAINT_NEEDED   BIT(3)
#define CXL_EVENT_REC_FLAGS_PERF_DEGRADED  BIT(4)
#define CXL_EVENT_REC_FLAGS_HW_REPLACEMENT_NEEDED BIT(5)
#define CXL_EVENT_REC_FLAGS_MAINT_OP_SUBCLASS_VALID BIT(6)
#define CXL_EVENT_REC_FLAGS_LD_ID_VALID BIT(7)
#define CXL_EVENT_REC_FLAGS_HEAD_ID_VALID BIT(8)
typedef struct CXLEventRecordHdr {
    QemuUUID id;
    uint8_t length;
    uint8_t flags[3];
    uint16_t handle;
    uint16_t related_handle;
    uint64_t timestamp;
    uint8_t maint_op_class;
    uint8_t maint_op_subclass;
    uint16_t ld_id;
    uint8_t head_id;
    uint8_t reserved[0xb];
} QEMU_PACKED CXLEventRecordHdr;

#define CXL_EVENT_RECORD_DATA_LENGTH 0x50
typedef struct CXLEventRecordRaw {
    CXLEventRecordHdr hdr;
    uint8_t data[CXL_EVENT_RECORD_DATA_LENGTH];
} QEMU_PACKED CXLEventRecordRaw;
#define CXL_EVENT_RECORD_SIZE (sizeof(CXLEventRecordRaw))

/*
 * Get Event Records output payload
 * CXL r3.1 section 8.2.9.2.2; Table 8-53
 */
#define CXL_GET_EVENT_FLAG_OVERFLOW     BIT(0)
#define CXL_GET_EVENT_FLAG_MORE_RECORDS BIT(1)
typedef struct CXLGetEventPayload {
    uint8_t flags;
    uint8_t reserved1;
    uint16_t overflow_err_count;
    uint64_t first_overflow_timestamp;
    uint64_t last_overflow_timestamp;
    uint16_t record_count;
    uint8_t reserved2[0xa];
    CXLEventRecordRaw records[];
} QEMU_PACKED CXLGetEventPayload;
#define CXL_EVENT_PAYLOAD_HDR_SIZE (sizeof(CXLGetEventPayload))

/*
 * Clear Event Records input payload
 * CXL r3.1 section 8.2.9.2.3; Table 8-54
 */
typedef struct CXLClearEventPayload {
    uint8_t event_log;      /* CXLEventLogType */
    uint8_t clear_flags;
    uint8_t nr_recs;
    uint8_t reserved[3];
    uint16_t handle[];
} CXLClearEventPayload;

/*
 * Event Interrupt Policy
 *
 * CXL r3.1 section 8.2.9.2.4; Table 8-55
 */
typedef enum CXLEventIntMode {
    CXL_INT_NONE     = 0x00,
    CXL_INT_MSI_MSIX = 0x01,
    CXL_INT_FW       = 0x02,
    CXL_INT_RES      = 0x03,
} CXLEventIntMode;
#define CXL_EVENT_INT_MODE_MASK 0x3
#define CXL_EVENT_INT_SETTING(vector) \
    ((((uint8_t)vector & 0xf) << 4) | CXL_INT_MSI_MSIX)
typedef struct CXLEventInterruptPolicy {
    uint8_t info_settings;
    uint8_t warn_settings;
    uint8_t failure_settings;
    uint8_t fatal_settings;
    uint8_t dyn_cap_settings;
} QEMU_PACKED CXLEventInterruptPolicy;
/* DCD is optional but other fields are not */
#define CXL_EVENT_INT_SETTING_MIN_LEN 4

/*
 * General Media Event Record
 * CXL r3.2 Section 8.2.10.2.1.1; Table 8-57
 */
#define CXL_EVENT_GEN_MED_COMP_ID_SIZE  0x10
#define CXL_EVENT_GEN_MED_RES_SIZE      0x29
typedef struct CXLEventGenMedia {
    CXLEventRecordHdr hdr;
    uint64_t phys_addr;
    uint8_t descriptor;
    uint8_t type;
    uint8_t transaction_type;
    uint16_t validity_flags;
    uint8_t channel;
    uint8_t rank;
    uint8_t device[3];
    uint8_t component_id[CXL_EVENT_GEN_MED_COMP_ID_SIZE];
    uint8_t cme_ev_flags;
    uint8_t cme_count[3];
    uint8_t sub_type;
    uint8_t reserved[CXL_EVENT_GEN_MED_RES_SIZE];
} QEMU_PACKED CXLEventGenMedia;

/*
 * DRAM Event Record
 * CXL r3.2 Section 8.2.10.2.1.2: Table 8-58
 * All fields little endian.
 */
typedef struct CXLEventDram {
    CXLEventRecordHdr hdr;
    uint64_t phys_addr;
    uint8_t descriptor;
    uint8_t type;
    uint8_t transaction_type;
    uint16_t validity_flags;
    uint8_t channel;
    uint8_t rank;
    uint8_t nibble_mask[3];
    uint8_t bank_group;
    uint8_t bank;
    uint8_t row[3];
    uint16_t column;
    uint64_t correction_mask[4];
    uint8_t component_id[CXL_EVENT_GEN_MED_COMP_ID_SIZE];
    uint8_t sub_channel;
    uint8_t cme_ev_flags;
    uint8_t cvme_count[3];
    uint8_t sub_type;
    uint8_t reserved;
} QEMU_PACKED CXLEventDram;

/*
 * Memory Module Event Record
 * CXL r3.2 Section 8.2.10.2.1.3: Table 8-59
 * All fields little endian.
 */
typedef struct CXLEventMemoryModule {
    CXLEventRecordHdr hdr;
    uint8_t type;
    uint8_t health_status;
    uint8_t media_status;
    uint8_t additional_status;
    uint8_t life_used;
    int16_t temperature;
    uint32_t dirty_shutdown_count;
    uint32_t corrected_volatile_error_count;
    uint32_t corrected_persistent_error_count;
    uint16_t validity_flags;
    uint8_t component_id[CXL_EVENT_GEN_MED_COMP_ID_SIZE];
    uint8_t sub_type;
    uint8_t reserved[0x2a];
} QEMU_PACKED CXLEventMemoryModule;

/*
 * CXL r3.1 section Table 8-50: Dynamic Capacity Event Record
 * All fields little endian.
 */
typedef struct CXLEventDynamicCapacity {
    CXLEventRecordHdr hdr;
    uint8_t type;
    uint8_t validity_flags;
    uint16_t host_id;
    uint8_t updated_region_id;
    uint8_t flags;
    uint8_t reserved2[2];
    uint8_t dynamic_capacity_extent[0x28]; /* defined in cxl_device.h */
    uint8_t reserved[0x18];
    uint32_t extents_avail;
    uint32_t tags_avail;
} QEMU_PACKED CXLEventDynamicCapacity;

/* CXL r3.1 Table 8-50: Dynamic Capacity Event Record */
static const QemuUUID dynamic_capacity_uuid = {
    .data = UUID(0xca95afa7, 0xf183, 0x4018, 0x8c, 0x2f,
                 0x95, 0x26, 0x8e, 0x10, 0x1a, 0x2a),
};

typedef enum CXLDCEventType {
    DC_EVENT_ADD_CAPACITY = 0x0,
    DC_EVENT_RELEASE_CAPACITY = 0x1,
    DC_EVENT_FORCED_RELEASE_CAPACITY = 0x2,
    DC_EVENT_REGION_CONFIG_UPDATED = 0x3,
    DC_EVENT_ADD_CAPACITY_RSP = 0x4,
    DC_EVENT_CAPACITY_RELEASED = 0x5,
} CXLDCEventType;

/*
 * CXL r3.2 section Table 8-60: Memory Sparing Event Record
 * All fields little endian.
 */

#define CXL_MSER_FLAGS_QUERY_RESOURCES BIT(0)
#define CXL_MSER_FLAGS_HARD_SPARING BIT(1)
#define CXL_MSER_FLAGS_DEV_INITIATED BIT(2)

#define CXL_MSER_VALID_CHANNEL BIT(0)
#define CXL_MSER_VALID_RANK BIT(1)
#define CXL_MSER_VALID_NIB_MASK BIT(2)
#define CXL_MSER_VALID_BANK_GROUP BIT(3)
#define CXL_MSER_VALID_BANK BIT(4)
#define CXL_MSER_VALID_ROW BIT(5)
#define CXL_MSER_VALID_COLUMN BIT(6)
#define CXL_MSER_VALID_COMP_ID BIT(7)
#define CXL_MSER_VALID_COMP_ID_FORMAT BIT(8)
#define CXL_MSER_VALID_SUB_CHANNEL BIT(9)

typedef struct CXLEventSparing {
    CXLEventRecordHdr hdr;
    uint8_t maint_op_class;
    uint8_t maint_op_subclass;
    uint8_t flags;
    uint8_t result;
    uint16_t validity_flags;
    uint8_t reserved1[6];
    uint16_t res_avail;
    uint8_t channel;
    uint8_t rank;
    uint8_t nibble_mask[3];
    uint8_t bank_group;
    uint8_t bank;
    uint8_t row[3];
    uint16_t column;
    uint8_t component_id[CXL_EVENT_GEN_MED_COMP_ID_SIZE];
    uint8_t sub_channel;
    uint8_t reserved2[0x25];
} QEMU_PACKED CXLEventSparing;

/* CXL r3.2 Table 8-60: Memory Sparing Event Record */
static const QemuUUID sparing_uuid = {
    .data = UUID(0xe71f3a40, 0x2d29, 0x4092, 0x8a, 0x39,
                 0x4d, 0x1c, 0x96, 0x6c, 0x7c, 0x65),
};

#endif /* CXL_EVENTS_H */
