[TB_SECURITY_DPONLY] = "dponly",
 };
 
+static ssize_t boot_acl_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+{
+       struct tb *tb = container_of(dev, struct tb, dev);
+       uuid_t *uuids;
+       ssize_t ret;
+       int i;
+
+       uuids = kcalloc(tb->nboot_acl, sizeof(uuid_t), GFP_KERNEL);
+       if (!uuids)
+               return -ENOMEM;
+
+       if (mutex_lock_interruptible(&tb->lock)) {
+               ret = -ERESTARTSYS;
+               goto out;
+       }
+       ret = tb->cm_ops->get_boot_acl(tb, uuids, tb->nboot_acl);
+       if (ret) {
+               mutex_unlock(&tb->lock);
+               goto out;
+       }
+       mutex_unlock(&tb->lock);
+
+       for (ret = 0, i = 0; i < tb->nboot_acl; i++) {
+               if (!uuid_is_null(&uuids[i]))
+                       ret += snprintf(buf + ret, PAGE_SIZE - ret, "%pUb",
+                                       &uuids[i]);
+
+               ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s",
+                              i < tb->nboot_acl - 1 ? "," : "\n");
+       }
+
+out:
+       kfree(uuids);
+       return ret;
+}
+
+static ssize_t boot_acl_store(struct device *dev, struct device_attribute *attr,
+                             const char *buf, size_t count)
+{
+       struct tb *tb = container_of(dev, struct tb, dev);
+       char *str, *s, *uuid_str;
+       ssize_t ret = 0;
+       uuid_t *acl;
+       int i = 0;
+
+       /*
+        * Make sure the value is not bigger than tb->nboot_acl * UUID
+        * length + commas and optional "\n". Also the smallest allowable
+        * string is tb->nboot_acl * ",".
+        */
+       if (count > (UUID_STRING_LEN + 1) * tb->nboot_acl + 1)
+               return -EINVAL;
+       if (count < tb->nboot_acl - 1)
+               return -EINVAL;
+
+       str = kstrdup(buf, GFP_KERNEL);
+       if (!str)
+               return -ENOMEM;
+
+       acl = kcalloc(tb->nboot_acl, sizeof(uuid_t), GFP_KERNEL);
+       if (!acl) {
+               ret = -ENOMEM;
+               goto err_free_str;
+       }
+
+       uuid_str = strim(str);
+       while ((s = strsep(&uuid_str, ",")) != NULL && i < tb->nboot_acl) {
+               size_t len = strlen(s);
+
+               if (len) {
+                       if (len != UUID_STRING_LEN) {
+                               ret = -EINVAL;
+                               goto err_free_acl;
+                       }
+                       ret = uuid_parse(s, &acl[i]);
+                       if (ret)
+                               goto err_free_acl;
+               }
+
+               i++;
+       }
+
+       if (s || i < tb->nboot_acl) {
+               ret = -EINVAL;
+               goto err_free_acl;
+       }
+
+       if (mutex_lock_interruptible(&tb->lock)) {
+               ret = -ERESTARTSYS;
+               goto err_free_acl;
+       }
+       ret = tb->cm_ops->set_boot_acl(tb, acl, tb->nboot_acl);
+       mutex_unlock(&tb->lock);
+
+err_free_acl:
+       kfree(acl);
+err_free_str:
+       kfree(str);
+
+       return ret ?: count;
+}
+static DEVICE_ATTR_RW(boot_acl);
+
 static ssize_t security_show(struct device *dev, struct device_attribute *attr,
                             char *buf)
 {
 static DEVICE_ATTR_RO(security);
 
 static struct attribute *domain_attrs[] = {
+       &dev_attr_boot_acl.attr,
        &dev_attr_security.attr,
        NULL,
 };
 
+static umode_t domain_attr_is_visible(struct kobject *kobj,
+                                     struct attribute *attr, int n)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct tb *tb = container_of(dev, struct tb, dev);
+
+       if (attr == &dev_attr_boot_acl.attr) {
+               if (tb->nboot_acl &&
+                   tb->cm_ops->get_boot_acl &&
+                   tb->cm_ops->set_boot_acl)
+                       return attr->mode;
+               return 0;
+       }
+
+       return attr->mode;
+}
+
 static struct attribute_group domain_attr_group = {
+       .is_visible = domain_attr_is_visible,
        .attrs = domain_attrs,
 };
 
 
  * @vnd_cap: Vendor defined capability where PCIe2CIO mailbox resides
  *          (only set when @upstream_port is not %NULL)
  * @safe_mode: ICM is in safe mode
+ * @max_boot_acl: Maximum number of preboot ACL entries (%0 if not supported)
  * @is_supported: Checks if we can support ICM on this controller
  * @get_mode: Read and return the ICM firmware mode (optional)
  * @get_route: Find a route string for given switch
        struct mutex request_lock;
        struct delayed_work rescan_work;
        struct pci_dev *upstream_port;
+       size_t max_boot_acl;
        int vnd_cap;
        bool safe_mode;
        bool (*is_supported)(struct tb *tb);
        int (*get_mode)(struct tb *tb);
        int (*get_route)(struct tb *tb, u8 link, u8 depth, u64 *route);
        int (*driver_ready)(struct tb *tb,
-                           enum tb_security_level *security_level);
+                           enum tb_security_level *security_level,
+                           size_t *nboot_acl);
        void (*device_connected)(struct tb *tb,
                                 const struct icm_pkg_header *hdr);
        void (*device_disconnected)(struct tb *tb,
 }
 
 static int
-icm_fr_driver_ready(struct tb *tb, enum tb_security_level *security_level)
+icm_fr_driver_ready(struct tb *tb, enum tb_security_level *security_level,
+                   size_t *nboot_acl)
 {
        struct icm_fr_pkg_driver_ready_response reply;
        struct icm_pkg_driver_ready request = {
        return nhi_mailbox_mode(nhi);
 }
 
+static int
+icm_ar_driver_ready(struct tb *tb, enum tb_security_level *security_level,
+                   size_t *nboot_acl)
+{
+       struct icm_ar_pkg_driver_ready_response reply;
+       struct icm_pkg_driver_ready request = {
+               .hdr.code = ICM_DRIVER_READY,
+       };
+       int ret;
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, ICM_TIMEOUT);
+       if (ret)
+               return ret;
+
+       if (security_level)
+               *security_level = reply.info & ICM_AR_INFO_SLEVEL_MASK;
+       if (nboot_acl && (reply.info & ICM_AR_INFO_BOOT_ACL_SUPPORTED))
+               *nboot_acl = (reply.info & ICM_AR_INFO_BOOT_ACL_MASK) >>
+                               ICM_AR_INFO_BOOT_ACL_SHIFT;
+       return 0;
+}
+
 static int icm_ar_get_route(struct tb *tb, u8 link, u8 depth, u64 *route)
 {
        struct icm_ar_pkg_get_route_response reply;
        return 0;
 }
 
+static int icm_ar_get_boot_acl(struct tb *tb, uuid_t *uuids, size_t nuuids)
+{
+       struct icm_ar_pkg_preboot_acl_response reply;
+       struct icm_ar_pkg_preboot_acl request = {
+               .hdr = { .code = ICM_PREBOOT_ACL },
+       };
+       int ret, i;
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, ICM_TIMEOUT);
+       if (ret)
+               return ret;
+
+       if (reply.hdr.flags & ICM_FLAGS_ERROR)
+               return -EIO;
+
+       for (i = 0; i < nuuids; i++) {
+               u32 *uuid = (u32 *)&uuids[i];
+
+               uuid[0] = reply.acl[i].uuid_lo;
+               uuid[1] = reply.acl[i].uuid_hi;
+
+               if (uuid[0] == 0xffffffff && uuid[1] == 0xffffffff) {
+                       /* Map empty entries to null UUID */
+                       uuid[0] = 0;
+                       uuid[1] = 0;
+               } else {
+                       /* Upper two DWs are always one's */
+                       uuid[2] = 0xffffffff;
+                       uuid[3] = 0xffffffff;
+               }
+       }
+
+       return ret;
+}
+
+static int icm_ar_set_boot_acl(struct tb *tb, const uuid_t *uuids,
+                              size_t nuuids)
+{
+       struct icm_ar_pkg_preboot_acl_response reply;
+       struct icm_ar_pkg_preboot_acl request = {
+               .hdr = {
+                       .code = ICM_PREBOOT_ACL,
+                       .flags = ICM_FLAGS_WRITE,
+               },
+       };
+       int ret, i;
+
+       for (i = 0; i < nuuids; i++) {
+               const u32 *uuid = (const u32 *)&uuids[i];
+
+               if (uuid_is_null(&uuids[i])) {
+                       /*
+                        * Map null UUID to the empty (all one) entries
+                        * for ICM.
+                        */
+                       request.acl[i].uuid_lo = 0xffffffff;
+                       request.acl[i].uuid_hi = 0xffffffff;
+               } else {
+                       /* Two high DWs need to be set to all one */
+                       if (uuid[2] != 0xffffffff || uuid[3] != 0xffffffff)
+                               return -EINVAL;
+
+                       request.acl[i].uuid_lo = uuid[0];
+                       request.acl[i].uuid_hi = uuid[1];
+               }
+       }
+
+       memset(&reply, 0, sizeof(reply));
+       ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply),
+                         1, ICM_TIMEOUT);
+       if (ret)
+               return ret;
+
+       if (reply.hdr.flags & ICM_FLAGS_ERROR)
+               return -EIO;
+
+       return 0;
+}
+
 static void icm_handle_notification(struct work_struct *work)
 {
        struct icm_notification *n = container_of(work, typeof(*n), work);
 }
 
 static int
-__icm_driver_ready(struct tb *tb, enum tb_security_level *security_level)
+__icm_driver_ready(struct tb *tb, enum tb_security_level *security_level,
+                  size_t *nboot_acl)
 {
        struct icm *icm = tb_priv(tb);
        unsigned int retries = 50;
        int ret;
 
-       ret = icm->driver_ready(tb, security_level);
+       ret = icm->driver_ready(tb, security_level, nboot_acl);
        if (ret) {
                tb_err(tb, "failed to send driver ready to ICM\n");
                return ret;
                return 0;
        }
 
-       return __icm_driver_ready(tb, &tb->security_level);
+       ret = __icm_driver_ready(tb, &tb->security_level, &tb->nboot_acl);
+       if (ret)
+               return ret;
+
+       /*
+        * Make sure the number of supported preboot ACL matches what we
+        * expect or disable the whole feature.
+        */
+       if (tb->nboot_acl > icm->max_boot_acl)
+               tb->nboot_acl = 0;
+
+       return 0;
 }
 
 static int icm_suspend(struct tb *tb)
         * Now all existing children should be resumed, start events
         * from ICM to get updated status.
         */
-       __icm_driver_ready(tb, NULL);
+       __icm_driver_ready(tb, NULL, NULL);
 
        /*
         * We do not get notifications of devices that have been
        return nhi_mailbox_cmd(tb->nhi, NHI_MAILBOX_DISCONNECT_PCIE_PATHS, 0);
 }
 
-/* Falcon Ridge and Alpine Ridge */
+/* Falcon Ridge */
 static const struct tb_cm_ops icm_fr_ops = {
        .driver_ready = icm_driver_ready,
        .start = icm_start,
        .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths,
 };
 
+/* Alpine Ridge */
+static const struct tb_cm_ops icm_ar_ops = {
+       .driver_ready = icm_driver_ready,
+       .start = icm_start,
+       .stop = icm_stop,
+       .suspend = icm_suspend,
+       .complete = icm_complete,
+       .handle_event = icm_handle_event,
+       .get_boot_acl = icm_ar_get_boot_acl,
+       .set_boot_acl = icm_ar_set_boot_acl,
+       .approve_switch = icm_fr_approve_switch,
+       .add_switch_key = icm_fr_add_switch_key,
+       .challenge_switch_key = icm_fr_challenge_switch_key,
+       .disconnect_pcie_paths = icm_disconnect_pcie_paths,
+       .approve_xdomain_paths = icm_fr_approve_xdomain_paths,
+       .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths,
+};
+
 struct tb *icm_probe(struct tb_nhi *nhi)
 {
        struct icm *icm;
        case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_LP_NHI:
        case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_4C_NHI:
        case PCI_DEVICE_ID_INTEL_ALPINE_RIDGE_C_2C_NHI:
+               icm->max_boot_acl = ICM_AR_PREBOOT_ACL_ENTRIES;
                icm->is_supported = icm_ar_is_supported;
                icm->get_mode = icm_ar_get_mode;
                icm->get_route = icm_ar_get_route;
-               icm->driver_ready = icm_fr_driver_ready;
+               icm->driver_ready = icm_ar_driver_ready;
                icm->device_connected = icm_fr_device_connected;
                icm->device_disconnected = icm_fr_device_disconnected;
                icm->xdomain_connected = icm_fr_xdomain_connected;
                icm->xdomain_disconnected = icm_fr_xdomain_disconnected;
-               tb->cm_ops = &icm_fr_ops;
+               tb->cm_ops = &icm_ar_ops;
                break;
        }
 
 
        ICM_ADD_DEVICE_KEY = 0x6,
        ICM_GET_ROUTE = 0xa,
        ICM_APPROVE_XDOMAIN = 0x10,
+       ICM_PREBOOT_ACL = 0x18,
 };
 
 enum icm_event_code {
 #define ICM_FLAGS_NO_KEY               BIT(1)
 #define ICM_FLAGS_SLEVEL_SHIFT         3
 #define ICM_FLAGS_SLEVEL_MASK          GENMASK(4, 3)
+#define ICM_FLAGS_WRITE                        BIT(7)
 
 struct icm_pkg_driver_ready {
        struct icm_pkg_header hdr;
 };
 
-/* Falcon Ridge & Alpine Ridge common messages */
+/* Falcon Ridge only messages */
 
 struct icm_fr_pkg_driver_ready_response {
        struct icm_pkg_header hdr;
 
 #define ICM_FR_SLEVEL_MASK             0xf
 
+/* Falcon Ridge & Alpine Ridge common messages */
+
 struct icm_fr_pkg_get_topology {
        struct icm_pkg_header hdr;
 };
 
 /* Alpine Ridge only messages */
 
+struct icm_ar_pkg_driver_ready_response {
+       struct icm_pkg_header hdr;
+       u8 romver;
+       u8 ramver;
+       u16 info;
+};
+
+#define ICM_AR_INFO_SLEVEL_MASK                GENMASK(3, 0)
+#define ICM_AR_INFO_BOOT_ACL_SHIFT     7
+#define ICM_AR_INFO_BOOT_ACL_MASK      GENMASK(11, 7)
+#define ICM_AR_INFO_BOOT_ACL_SUPPORTED BIT(13)
+
 struct icm_ar_pkg_get_route {
        struct icm_pkg_header hdr;
        u16 reserved;
        u32 route_lo;
 };
 
+struct icm_ar_boot_acl_entry {
+       u32 uuid_lo;
+       u32 uuid_hi;
+};
+
+#define ICM_AR_PREBOOT_ACL_ENTRIES     16
+
+struct icm_ar_pkg_preboot_acl {
+       struct icm_pkg_header hdr;
+       struct icm_ar_boot_acl_entry acl[ICM_AR_PREBOOT_ACL_ENTRIES];
+};
+
+struct icm_ar_pkg_preboot_acl_response {
+       struct icm_pkg_header hdr;
+       struct icm_ar_boot_acl_entry acl[ICM_AR_PREBOOT_ACL_ENTRIES];
+};
+
 /* XDomain messages */
 
 struct tb_xdomain_header {