struct ib_uverbs_attr __user *uattr_ptr)
 {
        const struct uverbs_attr_spec *spec;
+       const struct uverbs_attr_spec *val_spec;
        struct uverbs_attr *e;
        const struct uverbs_object_spec *object;
        struct uverbs_obj_attr *o_attr;
        struct uverbs_attr *elements = attr_bundle_h->attrs;
 
-       if (uattr->reserved)
-               return -EINVAL;
-
        if (attr_id >= attr_spec_bucket->num_attrs) {
                if (uattr->flags & UVERBS_ATTR_F_MANDATORY)
                        return -EINVAL;
                return -EINVAL;
 
        spec = &attr_spec_bucket->attrs[attr_id];
+       val_spec = spec;
        e = &elements[attr_id];
        e->uattr = uattr_ptr;
 
        switch (spec->type) {
+       case UVERBS_ATTR_TYPE_ENUM_IN:
+               if (uattr->attr_data.enum_data.elem_id >= spec->enum_def.num_elems)
+                       return -EOPNOTSUPP;
+
+               if (uattr->attr_data.enum_data.reserved)
+                       return -EINVAL;
+
+               val_spec = &spec->enum_def.ids[uattr->attr_data.enum_data.elem_id];
+
+               /* Currently we only support PTR_IN based enums */
+               if (val_spec->type != UVERBS_ATTR_TYPE_PTR_IN)
+                       return -EOPNOTSUPP;
+
+               e->ptr_attr.enum_id = uattr->attr_data.enum_data.elem_id;
+       /* fall through */
        case UVERBS_ATTR_TYPE_PTR_IN:
                /* Ensure that any data provided by userspace beyond the known
                 * struct is zero. Userspace that knows how to use some future
                 * longer struct will fail here if used with an old kernel and
                 * non-zero content, making ABI compat/discovery simpler.
                 */
-               if (uattr->len > spec->ptr.len &&
-                   spec->flags & UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO &&
-                   !uverbs_is_attr_cleared(uattr, spec->ptr.len))
+               if (uattr->len > val_spec->ptr.len &&
+                   val_spec->flags & UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO &&
+                   !uverbs_is_attr_cleared(uattr, val_spec->ptr.len))
                        return -EOPNOTSUPP;
 
        /* fall through */
        case UVERBS_ATTR_TYPE_PTR_OUT:
-               if (uattr->len < spec->ptr.min_len ||
-                   (!(spec->flags & UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO) &&
-                    uattr->len > spec->ptr.len))
+               if (uattr->len < val_spec->ptr.min_len ||
+                   (!(val_spec->flags & UVERBS_ATTR_SPEC_F_MIN_SZ_OR_ZERO) &&
+                    uattr->len > val_spec->ptr.len))
+                       return -EINVAL;
+
+               if (spec->type != UVERBS_ATTR_TYPE_ENUM_IN &&
+                   uattr->attr_data.reserved)
                        return -EINVAL;
 
                e->ptr_attr.data = uattr->data;
                        return -EINVAL;
        /* fall through */
        case UVERBS_ATTR_TYPE_FD:
+               if (uattr->attr_data.reserved)
+                       return -EINVAL;
+
                if (uattr->len != 0 || !ucontext || uattr->data > INT_MAX)
                        return -EINVAL;
 
 
        UVERBS_ATTR_TYPE_PTR_OUT,
        UVERBS_ATTR_TYPE_IDR,
        UVERBS_ATTR_TYPE_FD,
+       UVERBS_ATTR_TYPE_ENUM_IN,
 };
 
 enum uverbs_obj_access {
                        u16                     obj_type;
                        u8                      access;
                } obj;
+               struct {
+                       enum uverbs_attr_type           type;
+                       /* Combination of bits from enum UVERBS_ATTR_SPEC_F_XXXX */
+                       u8                              flags;
+                       u8                              num_elems;
+                       /*
+                        * The enum attribute can select one of the attributes
+                        * contained in the ids array. Currently only PTR_IN
+                        * attributes are supported in the ids array.
+                        */
+                       const struct uverbs_attr_spec   *ids;
+               } enum_def;
        };
 };
 
        UVERBS_ATTR(_id, UVERBS_ATTR_TYPE_PTR_OUT, ptr, _len, ##__VA_ARGS__)
 #define UVERBS_ATTR_PTR_OUT(_id, _type, ...)                           \
        UVERBS_ATTR_PTR_OUT_SZ(_id, _type, ##__VA_ARGS__)
+#define UVERBS_ATTR_ENUM_IN(_id, _enum_arr, ...)                       \
+       UVERBS_ATTR(_id, UVERBS_ATTR_TYPE_ENUM_IN, enum_def,            \
+                   .ids = (_enum_arr),                                 \
+                   .num_elems = ARRAY_SIZE(_enum_arr), ##__VA_ARGS__)
 
 /*
  * In new compiler, UVERBS_ATTR_IDR (and FD) could be simplified by declaring
 #define DECLARE_UVERBS_ATTR_SPEC(_name, ...)                           \
        const struct uverbs_attr_def _name = __VA_ARGS__
 
+#define DECLARE_UVERBS_ENUM(_name, ...)                                        \
+       const struct uverbs_enum_spec _name = {                         \
+               .len = ARRAY_SIZE(((struct uverbs_attr_spec[]){__VA_ARGS__})),\
+               .ids = {__VA_ARGS__},                                   \
+       }
 #define _UVERBS_METHOD_ATTRS_SZ(...)                                   \
        (sizeof((const struct uverbs_attr_def * const []){__VA_ARGS__}) /\
         sizeof(const struct uverbs_attr_def *))
        u16             len;
        /* Combination of bits from enum UVERBS_ATTR_F_XXXX */
        u16             flags;
+       u8              enum_id;
 };
 
 struct uverbs_obj_attr {
        return &attrs_bundle->hash[idx_bucket].attrs[idx & ~UVERBS_ID_NS_MASK];
 }
 
+static inline int uverbs_attr_get_enum_id(const struct uverbs_attr_bundle *attrs_bundle,
+                                         u16 idx)
+{
+       const struct uverbs_attr *attr = uverbs_attr_get(attrs_bundle, idx);
+
+       if (IS_ERR(attr))
+               return PTR_ERR(attr);
+
+       return attr->ptr_attr.enum_id;
+}
+
 static inline int uverbs_copy_to(const struct uverbs_attr_bundle *attrs_bundle,
                                 size_t idx, const void *from, size_t size)
 {
 
        __u16 attr_id;          /* command specific type attribute */
        __u16 len;              /* only for pointers */
        __u16 flags;            /* combination of UVERBS_ATTR_F_XXXX */
-       __u16 reserved;
+       union {
+               struct {
+                       __u8 elem_id;
+                       __u8 reserved;
+               } enum_data;
+               __u16 reserved;
+       } attr_data;
        __aligned_u64 data;     /* ptr to command, inline data or idr/fd */
 };