diff --git a/src/runtime_src/core/edge/drm/zocl/common/zocl_drv.c b/src/runtime_src/core/edge/drm/zocl/common/zocl_drv.c index 54c675a3f17..c76fc5661be 100644 --- a/src/runtime_src/core/edge/drm/zocl/common/zocl_drv.c +++ b/src/runtime_src/core/edge/drm/zocl/common/zocl_drv.c @@ -21,6 +21,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -890,6 +893,42 @@ zocl_gem_mmap(struct file *filp, struct vm_area_struct *vma) return rc; } +/* Aperture addresses come straight out of the IP_LAYOUT and DEBUG_IP_LAYOUT + * sections of a user supplied xclbin, so they cannot be trusted to describe a + * register window. Refuse anything the kernel knows to be System RAM, which + * would otherwise hand the caller a writable mapping of arbitrary memory. + * Addresses the kernel knows nothing about are still allowed, they are the + * undeclared PL windows the CUs actually live in. + * + * The whole pages the range falls in are checked, since those are what a + * mapping would hand out. + * + * @param addr: start of the register range + * @param size: size of the register range + * + * @return true if the range may be mapped to user space. + */ +bool zocl_apt_range_is_mmio(phys_addr_t addr, size_t size) +{ + phys_addr_t start = round_down(addr, PAGE_SIZE); + phys_addr_t last; + size_t len; + + if (!size || check_add_overflow(addr, (phys_addr_t)(size - 1), &last)) + return false; + + last |= (phys_addr_t)(PAGE_SIZE - 1); + len = last - start + 1; + if (!len) + return false; + + if (region_intersects(start, len, IORESOURCE_SYSTEM_RAM, + IORES_DESC_NONE) != REGION_DISJOINT) + return false; + + return !pfn_valid(PHYS_PFN(start)) && !pfn_valid(PHYS_PFN(last)); +} + /* This function map two types of kernel address to user space. * The first type is pysical registers of a hardware IP, like CUs. * The second type is GEM buffer. @@ -908,7 +947,7 @@ static int zocl_mmap(struct file *filp, struct vm_area_struct *vma) struct drm_zocl_bo *bo = NULL; unsigned long vsize; phys_addr_t phy_addr; - int apt_idx; + unsigned long apt_idx; int rc; /* A GEM buffer object has a fake mmap offset start from page offset @@ -949,17 +988,27 @@ static int zocl_mmap(struct file *filp, struct vm_area_struct *vma) * Could not map from the middle of an aperture. */ apt_idx = vma->vm_pgoff; - if (apt_idx < 0 || apt_idx >= zdev->cu_subdev.num_apts) { + if (apt_idx >= zdev->cu_subdev.num_apts) { DRM_ERROR("The offset is not in the apertures list\n"); return -EINVAL; } phy_addr = apts[apt_idx].addr; - vma->vm_pgoff = phy_addr >> PAGE_SHIFT; + if (phy_addr == EMPTY_APT_VALUE) { + DRM_ERROR("Aperture %lu is not in use\n", apt_idx); + return -EINVAL; + } vsize = vma->vm_end - vma->vm_start; - if (vsize > zdev->cu_subdev.apertures[apt_idx].size) + if (vsize > apts[apt_idx].size) return -EINVAL; + if (!zocl_apt_range_is_mmio(phy_addr, vsize)) { + DRM_ERROR("Aperture %lu at %pa is not a register window\n", + apt_idx, &phy_addr); + return -EINVAL; + } + + vma->vm_pgoff = phy_addr >> PAGE_SHIFT; vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0) vma->vm_flags |= VM_IO; diff --git a/src/runtime_src/core/edge/drm/zocl/common/zocl_xclbin.c b/src/runtime_src/core/edge/drm/zocl/common/zocl_xclbin.c index 2bc8e1e853b..d9a5740d536 100644 --- a/src/runtime_src/core/edge/drm/zocl/common/zocl_xclbin.c +++ b/src/runtime_src/core/edge/drm/zocl/common/zocl_xclbin.c @@ -269,7 +269,7 @@ update_max_apt_number(struct drm_zocl_dev *zdev) * @param slot_idx: Specific slot index * */ -static void +void zocl_clean_aperture(struct drm_zocl_dev *zdev, u32 slot_idx) { int apt_idx = 0; @@ -339,9 +339,28 @@ zocl_update_apertures(struct drm_zocl_dev *zdev, struct drm_zocl_slot *slot) /* Now update the aperture for the new xclbin */ if (slot->ip) { for (i = 0; i < slot->ip->m_count; ++i) { + size_t apt_size = CU_SIZE; + ip = &slot->ip->m_ip_data[i]; if (ip->m_base_address == EMPTY_APT_VALUE) continue; + + strncpy(kname, ip->m_name, sizeof(kname)); + kname[sizeof(kname)-1] = '\0'; + kname_p = &kname[0]; + strncpy(kname, strsep(&kname_p, ":"), sizeof(kname)); + kname[sizeof(kname)-1] = '\0'; + krnl_info = zocl_query_kernel(slot, kname); + if (krnl_info && krnl_info->range >= CU_SIZE) + apt_size = krnl_info->range; + + if (!zocl_apt_range_is_mmio(ip->m_base_address, + apt_size)) { + DRM_WARN("IP %s at 0x%llx is not a register window, no aperture\n", + kname, ip->m_base_address); + continue; + } + apt_idx = get_next_free_apt_index(zdev); if (apt_idx < 0) { DRM_ERROR("No more free apertures\n"); @@ -350,18 +369,8 @@ zocl_update_apertures(struct drm_zocl_dev *zdev, struct drm_zocl_slot *slot) apt = &zdev->cu_subdev.apertures[apt_idx]; apt->addr = ip->m_base_address; - - strncpy(kname, ip->m_name, sizeof(kname)); - kname[sizeof(kname)-1] = '\0'; - kname_p = &kname[0]; - strncpy(kname, strsep(&kname_p, ":"), sizeof(kname)); - kname[sizeof(kname)-1] = '\0'; - krnl_info = zocl_query_kernel(slot, kname); - if (krnl_info && krnl_info->range >= CU_SIZE) - apt->size = krnl_info->range; - else - apt->size = CU_SIZE; - apt->prop = ip->properties; + apt->size = apt_size; + apt->prop = ip->properties; apt->cu_idx = -1; apt->slot_idx = slot->slot_idx; } @@ -371,7 +380,23 @@ zocl_update_apertures(struct drm_zocl_dev *zdev, struct drm_zocl_slot *slot) if (slot->debug_ip) { for (i = 0; i < slot->debug_ip->m_count; ++i) { + size_t apt_size = _64KB; + dbg_ip = &slot->debug_ip->m_debug_ip_data[i]; + if (dbg_ip->m_type == AXI_MONITOR_FIFO_LITE + || dbg_ip->m_type == AXI_MONITOR_FIFO_FULL) + /* FIFO_LITE has 4KB and FIFO FULL has 8KB + * address range. Use both 8K is okay. + */ + apt_size = _8KB; + + if (!zocl_apt_range_is_mmio(dbg_ip->m_base_address, + apt_size)) { + DRM_WARN("Debug IP at 0x%llx is not a register window, no aperture\n", + dbg_ip->m_base_address); + continue; + } + apt_idx = get_next_free_apt_index(zdev); if (apt_idx < 0) { DRM_ERROR("No more free apertures\n"); @@ -380,16 +405,10 @@ zocl_update_apertures(struct drm_zocl_dev *zdev, struct drm_zocl_slot *slot) apt = &zdev->cu_subdev.apertures[apt_idx]; apt->addr = dbg_ip->m_base_address; + apt->size = apt_size; + apt->prop = 0; + apt->cu_idx = -1; apt->slot_idx = slot->slot_idx; - if (dbg_ip->m_type == AXI_MONITOR_FIFO_LITE - || dbg_ip->m_type == AXI_MONITOR_FIFO_FULL) - /* FIFO_LITE has 4KB and FIFO FULL has 8KB - * address range. Use both 8K is okay. - */ - apt->size = _8KB; - else - /* Others debug IPs have 64KB address range*/ - apt->size = _64KB; } /* Update num_apts based on current index */ update_max_apt_number(zdev); diff --git a/src/runtime_src/core/edge/drm/zocl/edge/zocl_edge_xclbin.c b/src/runtime_src/core/edge/drm/zocl/edge/zocl_edge_xclbin.c index eb82502a2b2..bbecf3053bb 100644 --- a/src/runtime_src/core/edge/drm/zocl/edge/zocl_edge_xclbin.c +++ b/src/runtime_src/core/edge/drm/zocl/edge/zocl_edge_xclbin.c @@ -407,11 +407,11 @@ zocl_xclbin_read_axlf(struct drm_zocl_dev *zdev, struct drm_zocl_axlf *axlf_obj, /* Create the CUs for this slot (after INTC refresh so CUs register correctly) */ ret = zocl_create_cu(zdev, slot); if (ret) - goto out0; + goto out_apt; ret = zocl_kds_update(zdev, slot, &axlf_obj->kds_cfg); if (ret) - goto out0; + goto out_apt; } DRM_INFO("xclbin %pUb successfully loaded to slot %d\n", @@ -419,6 +419,14 @@ zocl_xclbin_read_axlf(struct drm_zocl_dev *zdev, struct drm_zocl_axlf *axlf_obj, goto done; +out_apt: + /* Only reached once the apertures hold this xclbin, drop them so a + * failed load cannot leave a mappable address behind. The earlier + * error paths must not come here, they can still be serving the + * previous xclbin. + */ + zocl_clean_aperture(zdev, slot->slot_idx); + out0: /* The old overlay or PL may be gone, drop the stale CUs and IRQ routing. */ if (dt_overlay_dirty) { diff --git a/src/runtime_src/core/edge/drm/zocl/include/zocl_drv.h b/src/runtime_src/core/edge/drm/zocl/include/zocl_drv.h index a622767e209..be27af6b240 100644 --- a/src/runtime_src/core/edge/drm/zocl/include/zocl_drv.h +++ b/src/runtime_src/core/edge/drm/zocl/include/zocl_drv.h @@ -364,6 +364,7 @@ zocl_get_zdev(void) return NULL; return platform_get_drvdata(pdev); } +bool zocl_apt_range_is_mmio(phys_addr_t addr, size_t size); int get_apt_index_by_addr(struct drm_zocl_dev *zdev, phys_addr_t addr); int get_apt_index_by_addr_and_slot(struct drm_zocl_dev *zdev, phys_addr_t addr, int slot_idx); int get_apt_index_by_cu_idx(struct drm_zocl_dev *zdev, int cu_idx); diff --git a/src/runtime_src/core/edge/drm/zocl/include/zocl_xclbin.h b/src/runtime_src/core/edge/drm/zocl/include/zocl_xclbin.h index 6effb7bdece..9526e510945 100644 --- a/src/runtime_src/core/edge/drm/zocl/include/zocl_xclbin.h +++ b/src/runtime_src/core/edge/drm/zocl/include/zocl_xclbin.h @@ -75,6 +75,7 @@ int zocl_offsetof_sect(enum axlf_section_kind kind, void *sect, int zocl_read_sect(enum axlf_section_kind kind, void *sect, struct axlf *axlf_full, char __user *xclbin_ptr); int zocl_update_apertures(struct drm_zocl_dev *zdev, struct drm_zocl_slot *slot); +void zocl_clean_aperture(struct drm_zocl_dev *zdev, u32 slot_idx); void zocl_destroy_cu_slot(struct drm_zocl_dev *zdev, u32 slot_idx); int zocl_create_cu(struct drm_zocl_dev *zdev, struct drm_zocl_slot *slot); void zocl_slot_remove_xclbin_overlay(struct drm_zocl_dev *zdev,